Files
vmall/apps/admin/pages/index.vue
T

108 lines
3.5 KiB
Vue

<script setup lang="ts">
const { t } = useI18n();
definePageMeta({ middleware: "auth" });
const { $api } = useNuxtApp();
const loading = ref(true);
const errorMessage = ref("");
const stats = reactive({
users: 0,
shops: 0,
orders: 0,
currencies: 0,
});
async function loadDashboard(): Promise<void> {
loading.value = true;
errorMessage.value = "";
try {
const [users, shops, orders, currencies] = await Promise.all([
$api.admin.listUsers(1),
$api.admin.listShops(),
$api.admin.listOrders(1),
$api.admin.listCurrencies(),
]);
stats.users = users.total;
stats.shops = shops.length;
stats.orders = orders.total;
stats.currencies = currencies.filter((currency) => currency.enabled).length;
} catch (error: unknown) {
errorMessage.value = error instanceof Error ? error.message : t("common.error");
} finally {
loading.value = false;
}
}
onMounted(() => {
void loadDashboard();
});
const cards = computed(() => [
{ label: "admin.totalUsers", value: stats.users, link: "/users", tone: "blue" },
{ label: "admin.totalShops", value: stats.shops, link: "/shops", tone: "green" },
{ label: "admin.totalOrders", value: stats.orders, link: "/orders", tone: "orange" },
{
label: "admin.enabledCurrencies",
value: stats.currencies,
link: "/currencies",
tone: "purple",
},
]);
</script>
<template>
<VPage :title="$t('admin.dashboardTitle')">
<template #actions>
<VBtn size="sm" :disabled="loading" @click="loadDashboard">{{ $t("admin.refresh") }}</VBtn>
</template>
<p class="text-primary mb-4 text-xs font-bold tracking-[0.08em] uppercase">
{{ $t("common.appName") }} · Admin
</p>
<p v-if="loading" class="text-muted text-sm">{{ $t("common.loading") }}</p>
<p v-else-if="errorMessage" class="text-danger my-2 text-sm" role="alert">{{ errorMessage }}</p>
<template v-else>
<div class="grid gap-4 sm:grid-cols-2 xl:grid-cols-4">
<NuxtLink
v-for="card in cards"
:key="card.link"
:to="card.link"
class="border-border bg-surface hover:border-primary flex flex-col gap-2 rounded-lg border p-5 shadow-sm transition-transform hover:-translate-y-0.5"
>
<span class="text-muted text-[13px]">{{ $t(card.label) }}</span>
<strong
class="text-[32px] leading-none"
:class="
card.tone === 'green'
? 'text-success'
: card.tone === 'orange'
? 'text-warning'
: 'text-primary'
"
>{{ card.value }}</strong
>
<span class="text-primary text-xs">{{ $t("admin.open") }} </span>
</NuxtLink>
</div>
<div class="mt-6 grid gap-4 sm:grid-cols-2 xl:grid-cols-4">
<NuxtLink
v-for="shortcut in [
{ to: '/users', title: 'admin.userManagement', label: 'nav.users' },
{ to: '/shops', title: 'admin.shopManagement', label: 'nav.shops' },
{ to: '/orders', title: 'admin.orderManagement', label: 'nav.orders' },
{ to: '/currencies', title: 'admin.currencyManagement', label: 'nav.currencies' },
]"
:key="shortcut.to"
:to="shortcut.to"
class="block no-underline hover:no-underline"
>
<VCard>
<strong class="block">{{ $t(shortcut.title) }}</strong>
<span class="text-muted text-sm">{{ $t(shortcut.label) }}</span>
</VCard>
</NuxtLink>
</div>
</template>
</VPage>
</template>