Complete super admin panel with CRUD, bookings, profile, and dev API proxy.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
2026-05-21 14:20:52 +03:30
co-authored by Cursor
parent 03f3994121
commit 4bc6c003f3
31 changed files with 1508 additions and 43 deletions
+73 -11
View File
@@ -1,20 +1,82 @@
<template>
<AdminLayout>
<h1 class="text-xl font-bold text-text">داشبورد</h1>
<p class="mt-2 text-sm text-muted">سوپر ادمین — مون‌تایم</p>
<div class="mt-6 grid gap-3 sm:grid-cols-2">
<div class="rounded-2xl border border-border bg-surface-2 p-4">
<p class="text-xs text-muted">کسب‌وکارها</p>
<p class="mt-1 text-2xl font-bold text-text">—</p>
</div>
<div class="rounded-2xl border border-border bg-surface-2 p-4">
<p class="text-xs text-muted">کاربران</p>
<p class="mt-1 text-2xl font-bold text-text">—</p>
</div>
<PageHeader title="داشبورد" subtitle="خلاصه وضعیت سیستم" />
<p v-if="error" class="mt-4 rounded-xl border border-red-500/30 bg-red-500/10 px-4 py-3 text-sm text-red-600">
{{ error }}
</p>
<div v-if="loading" class="mt-6 text-sm text-muted">در حال بارگذاری…</div>
<div v-else class="mt-6 grid gap-3 sm:grid-cols-2 lg:grid-cols-3">
<RouterLink
v-for="card in cards"
:key="card.label"
:to="card.to"
class="rounded-2xl border border-border bg-surface-2 p-4 transition hover:border-brand-500/50"
>
<p class="text-xs text-muted">{{ card.label }}</p>
<p class="mt-1 text-2xl font-bold text-text">{{ card.value }}</p>
</RouterLink>
</div>
<div v-if="auth.user" class="mt-6 rounded-2xl border border-border bg-surface-2 p-4 text-sm">
<p class="text-xs text-muted">ورود شما</p>
<p class="mt-1 font-medium" dir="ltr">{{ auth.user.mobile }}</p>
<p class="mt-1 text-muted">{{ auth.user.fullName ?? 'بدون نام' }}</p>
</div>
</AdminLayout>
</template>
<script setup lang="ts">
import { computed, onMounted, ref } from 'vue';
import { fetchBookings } from '@/api/bookings';
import { fetchCategories } from '@/api/business-categories';
import { fetchBusinesses } from '@/api/businesses';
import { fetchUsers } from '@/api/users';
import { ApiError } from '@/api/client';
import PageHeader from '@/components/ui/PageHeader.vue';
import AdminLayout from '@/layouts/AdminLayout.vue';
import { useAuthStore } from '@/stores/auth';
const auth = useAuthStore();
const loading = ref(true);
const error = ref('');
const counts = ref({
businesses: 0,
activeBusinesses: 0,
bookings: 0,
users: 0,
categories: 0,
});
const cards = computed(() => [
{ label: 'کسب‌وکارها', value: counts.value.businesses, to: '/businesses' },
{ label: 'کسب‌وکار فعال', value: counts.value.activeBusinesses, to: '/businesses' },
{ label: 'رزروها', value: counts.value.bookings, to: '/bookings' },
{ label: 'کاربران', value: counts.value.users, to: '/users' },
{ label: 'دسته‌ها', value: counts.value.categories, to: '/categories' },
]);
onMounted(async () => {
try {
const [businesses, users, categories, bookingList] = await Promise.all([
fetchBusinesses(),
fetchUsers(),
fetchCategories(),
fetchBookings().catch(() => []),
]);
counts.value = {
businesses: businesses.length,
activeBusinesses: businesses.filter((b) => b.isActive).length,
bookings: bookingList.length,
users: users.length,
categories: categories.length,
};
} catch (e) {
error.value = e instanceof ApiError ? e.message : 'خطا در بارگذاری آمار';
} finally {
loading.value = false;
}
});
</script>