diff --git a/packages/client/src/pages/admin/queue.chart.vue b/packages/client/src/pages/admin/queue.chart.vue
index 136fb63bb..be63830bd 100644
--- a/packages/client/src/pages/admin/queue.chart.vue
+++ b/packages/client/src/pages/admin/queue.chart.vue
@@ -26,62 +26,40 @@
 </div>
 </template>
 
-<script lang="ts">
-import { defineComponent, markRaw, onMounted, onUnmounted, ref } from 'vue';
+<script lang="ts" setup>
+import { onMounted, onUnmounted, ref } from 'vue';
 import number from '@/filters/number';
 import MkQueueChart from '@/components/queue-chart.vue';
 import * as os from '@/os';
 
-export default defineComponent({
-	components: {
-		MkQueueChart
-	},
+const activeSincePrevTick = ref(0);
+const active = ref(0);
+const waiting = ref(0);
+const delayed = ref(0);
+const jobs = ref([]);
 
-	props: {
-		domain: {
-			type: String,
-			required: true,
-		},
-		connection: {
-			required: true,
-		},
-	},
+const props = defineProps<{
+	domain: string,
+	connection: any,
+}>();
 
-	setup(props) {
-		const activeSincePrevTick = ref(0);
-		const active = ref(0);
-		const waiting = ref(0);
-		const delayed = ref(0);
-		const jobs = ref([]);
+onMounted(() => {
+	os.api(props.domain === 'inbox' ? 'admin/queue/inbox-delayed' : props.domain === 'deliver' ? 'admin/queue/deliver-delayed' : null, {}).then(result => {
+		jobs.value = result;
+	});
 
-		onMounted(() => {
-			os.api(props.domain === 'inbox' ? 'admin/queue/inbox-delayed' : props.domain === 'deliver' ? 'admin/queue/deliver-delayed' : null, {}).then(result => {
-				jobs.value = result;
-			});
+	const onStats = (stats) => {
+		activeSincePrevTick.value = stats[props.domain].activeSincePrevTick;
+		active.value = stats[props.domain].active;
+		waiting.value = stats[props.domain].waiting;
+		delayed.value = stats[props.domain].delayed;
+	};
 
-			const onStats = (stats) => {
-				activeSincePrevTick.value = stats[props.domain].activeSincePrevTick;
-				active.value = stats[props.domain].active;
-				waiting.value = stats[props.domain].waiting;
-				delayed.value = stats[props.domain].delayed;
-			};
+	props.connection.on('stats', onStats);
 
-			props.connection.on('stats', onStats);
-
-			onUnmounted(() => {
-				props.connection.off('stats', onStats);
-			});
-		});
-
-		return {
-			jobs,
-			activeSincePrevTick,
-			active,
-			waiting,
-			delayed,
-			number,
-		};
-	},
+	onUnmounted(() => {
+		props.connection.off('stats', onStats);
+	});
 });
 </script>
 
diff --git a/packages/client/src/pages/admin/queue.vue b/packages/client/src/pages/admin/queue.vue
index 35fd618c8..e05098082 100644
--- a/packages/client/src/pages/admin/queue.vue
+++ b/packages/client/src/pages/admin/queue.vue
@@ -6,71 +6,60 @@
 	<XQueue :connection="connection" domain="deliver">
 		<template #title>Out</template>
 	</XQueue>
-	<MkButton danger @click="clear()"><i class="fas fa-trash-alt"></i> {{ $ts.clearQueue }}</MkButton>
+	<MkButton danger @click="clear()"><i class="fas fa-trash-alt"></i> {{ i18n.ts.clearQueue }}</MkButton>
 </MkSpacer>
 </template>
 
-<script lang="ts">
-import { defineComponent, markRaw } from 'vue';
+<script lang="ts" setup>
+import { markRaw, onMounted, onBeforeUnmount, nextTick } from 'vue';
 import MkButton from '@/components/ui/button.vue';
 import XQueue from './queue.chart.vue';
 import * as os from '@/os';
 import { stream } from '@/stream';
 import * as symbols from '@/symbols';
 import * as config from '@/config';
+import { i18n } from '@/i18n';
 
-export default defineComponent({
-	components: {
-		MkButton,
-		XQueue,
-	},
+const connection = markRaw(stream.useChannel('queueStats'))
 
-	emits: ['info'],
+function clear() {
+	os.confirm({
+		type: 'warning',
+		title: i18n.ts.clearQueueConfirmTitle,
+		text: i18n.ts.clearQueueConfirmText,
+	}).then(({ canceled }) => {
+		if (canceled) return;
 
-	data() {
-		return {
-			[symbols.PAGE_INFO]: {
-				title: this.$ts.jobQueue,
-				icon: 'fas fa-clipboard-list',
-				bg: 'var(--bg)',
-				actions: [{
-					asFullButton: true,
-					icon: 'fas fa-up-right-from-square',
-					text: this.$ts.dashboard,
-					handler: () => {
-						window.open(config.url + '/queue', '_blank');
-					},
-				}],
-			},
-			connection: markRaw(stream.useChannel('queueStats')),
-		}
-	},
+		os.apiWithDialog('admin/queue/clear');
+	});
+}
 
-	mounted() {
-		this.$nextTick(() => {
-			this.connection.send('requestLog', {
-				id: Math.random().toString().substr(2, 8),
-				length: 200
-			});
+onMounted(() => {
+	nextTick(() => {
+		connection.send('requestLog', {
+			id: Math.random().toString().substr(2, 8),
+			length: 200
 		});
-	},
+	});
+})
 
-	beforeUnmount() {
-		this.connection.dispose();
-	},
+onBeforeUnmount(() => {
+	connection.dispose();
+});
 
-	methods: {
-		clear() {
-			os.confirm({
-				type: 'warning',
-				title: this.$ts.clearQueueConfirmTitle,
-				text: this.$ts.clearQueueConfirmText,
-			}).then(({ canceled }) => {
-				if (canceled) return;
-
-				os.apiWithDialog('admin/queue/clear', {});
-			});
-		}
+defineExpose({
+	[symbols.PAGE_INFO]: {
+		title: i18n.ts.jobQueue,
+		icon: 'fas fa-clipboard-list',
+		bg: 'var(--bg)',
+		actions: [{
+			asFullButton: true,
+			icon: 'fas fa-up-right-from-square',
+			text: i18n.ts.dashboard,
+			handler: () => {
+				window.open(config.url + '/queue', '_blank');
+			},
+		}],
 	}
 });
 </script>