1 line
19 KiB
JSON
1 line
19 KiB
JSON
{"file":"index-Cv2HQkhy.js","mappings":"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","names":["_unref","_ssrInterpolate","_ssrRenderList","_ssrRenderStyle","_ssrRenderAttr","_ssrRenderClass"],"sources":["../../../../pages/index.vue"],"sourcesContent":["<template>\n <div>\n <!-- Page Header -->\n <div class=\"mb-8\">\n <h1 class=\"text-2xl font-bold text-white\">{{ t('dashboard.title') }}</h1>\n <p class=\"text-slate-400 mt-1\">{{ t('dashboard.subtitle') }}</p>\n </div>\n\n <!-- Loading State -->\n <div v-if=\"loading\" class=\"flex items-center justify-center py-20\">\n <div class=\"text-slate-400 flex items-center gap-3\">\n <svg class=\"w-5 h-5 animate-spin\" fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\">\n <path stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" d=\"M4 4v5h.582m15.356 2A8.001 8.001 0 004.582 9m0 0H9m11 11v-5h-.581m0 0a8.003 8.003 0 01-15.357-2m15.357 2H15\" />\n </svg>\n {{ t('dashboard.loading') }}\n </div>\n </div>\n\n <!-- Error State -->\n <div v-else-if=\"error\" class=\"bg-rose-500/10 border border-rose-500/30 rounded-xl p-6 mb-8\">\n <p class=\"text-rose-400\">{{ t('dashboard.error') }}</p>\n </div>\n\n <!-- Dashboard Content -->\n <template v-else>\n <!-- Stat Tiles Grid -->\n <div class=\"grid grid-cols-1 sm:grid-cols-2 xl:grid-cols-4 gap-6 mb-8\">\n <!-- Active Users -->\n <div class=\"bg-slate-800 rounded-xl border border-slate-700 p-6 hover:border-indigo-500/50 transition group\">\n <div class=\"flex items-center justify-between mb-4\">\n <div class=\"p-2 rounded-lg bg-indigo-500/10 text-indigo-400 group-hover:bg-indigo-500/20 transition\">\n <svg class=\"w-6 h-6\" fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\">\n <path stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" d=\"M12 4.354a4 4 0 110 5.292M15 21H3v-1a6 6 0 0112 0v1zm0 0h6v-1a6 6 0 00-9-5.197m13.5-9a2.25 2.25 0 11-4.5 0 2.25 2.25 0 014.5 0z\" />\n </svg>\n </div>\n <span v-if=\"stats.new_users_this_month > 0\" class=\"text-xs font-medium text-emerald-400 bg-emerald-400/10 px-2 py-1 rounded-full\">+{{ stats.new_users_this_month }}</span>\n </div>\n <h3 class=\"text-sm font-medium text-slate-400 uppercase tracking-wider\">{{ t('dashboard.active_users') }}</h3>\n <p class=\"text-3xl font-bold text-white mt-2\">{{ formatNumber(stats.active_users) }}</p>\n <p class=\"text-xs text-slate-500 mt-1\">{{ formatNumber(stats.new_users_this_month) }} {{ t('dashboard.new_this_month') }}</p>\n </div>\n\n <!-- Total Vehicles -->\n <div class=\"bg-slate-800 rounded-xl border border-slate-700 p-6 hover:border-cyan-500/50 transition group\">\n <div class=\"flex items-center justify-between mb-4\">\n <div class=\"p-2 rounded-lg bg-cyan-500/10 text-cyan-400 group-hover:bg-cyan-500/20 transition\">\n <svg class=\"w-6 h-6\" fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\">\n <path stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" d=\"M13.5 21v-7.5a.75.75 0 01.75-.75h3a.75.75 0 01.75.75V21m-4.5 0H2.36m11.14 0H18m0 0h3.64m-1.39 0V9.349m-16.5 11.65V9.35m0 0a3.001 3.001 0 003.75-.615A2.993 2.993 0 009.75 9.75c.896 0 1.7-.393 2.25-1.016a2.993 2.993 0 002.25 1.016c.896 0 1.7-.393 2.25-1.016a3.001 3.001 0 003.75.614m-16.5 0a3.004 3.004 0 01-.621-4.72L4.318 3.44A1.5 1.5 0 015.378 3h13.243a1.5 1.5 0 011.06.44l1.19 1.189a3 3 0 01-.621 4.72m-13.5 8.65h3.75a.75.75 0 00.75-.75V13.5a.75.75 0 00-.75-.75H6.75a.75.75 0 00-.75.75v3.75c0 .415.336.75.75.75z\" />\n </svg>\n </div>\n </div>\n <h3 class=\"text-sm font-medium text-slate-400 uppercase tracking-wider\">{{ t('dashboard.total_vehicles') }}</h3>\n <p class=\"text-3xl font-bold text-white mt-2\">{{ formatNumber(stats.total_vehicles) }}</p>\n </div>\n\n <!-- Organizations -->\n <div class=\"bg-slate-800 rounded-xl border border-slate-700 p-6 hover:border-emerald-500/50 transition group\">\n <div class=\"flex items-center justify-between mb-4\">\n <div class=\"p-2 rounded-lg bg-emerald-500/10 text-emerald-400 group-hover:bg-emerald-500/20 transition\">\n <svg class=\"w-6 h-6\" fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\">\n <path stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" d=\"M2.25 21h19.5m-18-18v18m10.5-18v18m6-13.5V21M6.75 6.75h.75m-.75 3h.75m-.75 3h.75m3-6h.75m-.75 3h.75m-.75 3h.75M6.75 21v-3.375c0-.621.504-1.125 1.125-1.125h2.25c.621 0 1.125.504 1.125 1.125V21M3 3h12m-.75 4.5H21m-3.75 3.75h.008v.008h-.008v-.008zm0 3h.008v.008h-.008v-.008zm0 3h.008v.008h-.008v-.008z\" />\n </svg>\n </div>\n </div>\n <h3 class=\"text-sm font-medium text-slate-400 uppercase tracking-wider\">{{ t('dashboard.organizations') }}</h3>\n <p class=\"text-3xl font-bold text-white mt-2\">{{ formatNumber(stats.active_organizations_count) }}</p>\n <p class=\"text-xs text-slate-500 mt-1\">{{ formatNumber(stats.total_memberships) }} {{ t('dashboard.memberships') }}</p>\n </div>\n\n <!-- New Users (This Month) -->\n <div class=\"bg-slate-800 rounded-xl border border-slate-700 p-6 hover:border-violet-500/50 transition group\">\n <div class=\"flex items-center justify-between mb-4\">\n <div class=\"p-2 rounded-lg bg-violet-500/10 text-violet-400 group-hover:bg-violet-500/20 transition\">\n <svg class=\"w-6 h-6\" fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\">\n <path stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" d=\"M18 18.72a9.094 9.094 0 003.741-.479 3 3 0 00-4.682-2.72m.94 3.198l.001.031c0 .225-.012.447-.037.666A11.944 11.944 0 0112 21c-2.17 0-4.207-.576-5.963-1.584A6.062 6.062 0 016 18.719m12 0a5.971 5.971 0 00-.941-3.197m0 0A5.995 5.995 0 0012 12.75a5.995 5.995 0 00-5.058 2.772m0 0a3 3 0 00-4.681 2.72 8.986 8.986 0 003.74.477m.94-3.197a5.971 5.971 0 00-.94 3.197M15 6.75a3 3 0 11-6 0 3 3 0 016 0zm6 3a2.25 2.25 0 11-4.5 0 2.25 2.25 0 014.5 0zm-13.5 0a2.25 2.25 0 11-4.5 0 2.25 2.25 0 014.5 0z\" />\n </svg>\n </div>\n </div>\n <h3 class=\"text-sm font-medium text-slate-400 uppercase tracking-wider\">{{ t('dashboard.new_users') }}</h3>\n <p class=\"text-3xl font-bold text-white mt-2\">{{ formatNumber(stats.new_users_this_month) }}</p>\n <p class=\"text-xs text-slate-500 mt-1\">{{ formatNumber(stats.new_users_today) }} {{ t('dashboard.new_today') }}</p>\n </div>\n </div>\n\n <!-- Secondary Stats Row -->\n <div class=\"grid grid-cols-1 lg:grid-cols-3 gap-6 mb-8\">\n <!-- Users by Role -->\n <div class=\"bg-slate-800 rounded-xl border border-slate-700 p-6\">\n <h3 class=\"text-sm font-medium text-slate-400 uppercase tracking-wider mb-4\">{{ t('dashboard.users_by_role') }}</h3>\n <div class=\"space-y-3\">\n <div v-for=\"(count, role) in stats.users_by_role\" :key=\"role\" class=\"flex items-center justify-between\">\n <span class=\"text-sm text-slate-300\">{{ role }}</span>\n <span class=\"text-sm font-semibold text-white\">{{ count }}</span>\n </div>\n </div>\n </div>\n\n <!-- Users by Plan -->\n <div class=\"bg-slate-800 rounded-xl border border-slate-700 p-6\">\n <h3 class=\"text-sm font-medium text-slate-400 uppercase tracking-wider mb-4\">{{ t('dashboard.users_by_plan') }}</h3>\n <div class=\"space-y-3\">\n <div v-for=\"(count, plan) in stats.users_by_plan\" :key=\"plan\" class=\"flex items-center justify-between\">\n <span class=\"text-sm text-slate-300\">{{ plan }}</span>\n <span class=\"text-sm font-semibold text-white\">{{ count }}</span>\n </div>\n </div>\n </div>\n\n <!-- Users by Language -->\n <div class=\"bg-slate-800 rounded-xl border border-slate-700 p-6\">\n <h3 class=\"text-sm font-medium text-slate-400 uppercase tracking-wider mb-4\">{{ t('dashboard.users_by_language') }}</h3>\n <div class=\"space-y-3\">\n <div v-for=\"(count, lang) in stats.users_by_language\" :key=\"lang\" class=\"flex items-center justify-between\">\n <span class=\"text-sm text-slate-300\">{{ lang }}</span>\n <span class=\"text-sm font-semibold text-white\">{{ count }}</span>\n </div>\n </div>\n </div>\n </div>\n\n <!-- Registration Trend -->\n <div class=\"bg-slate-800 rounded-xl border border-slate-700 p-6 mb-8\">\n <h3 class=\"text-sm font-medium text-slate-400 uppercase tracking-wider mb-4\">{{ t('dashboard.registration_trend') }}</h3>\n <div class=\"flex items-end gap-1 h-32\">\n <div\n v-for=\"(day, idx) in registrationTrend\"\n :key=\"idx\"\n class=\"flex-1 bg-indigo-500/60 hover:bg-indigo-400/80 rounded-t transition-all cursor-pointer relative group\"\n :style=\"{ height: day.height + '%' }\"\n :title=\"day.date + ': ' + day.count + ' users'\"\n >\n <div class=\"absolute -top-8 left-1/2 -translate-x-1/2 bg-slate-700 text-xs text-white px-2 py-1 rounded opacity-0 group-hover:opacity-100 transition whitespace-nowrap\">\n {{ day.date }}: {{ day.count }}\n </div>\n </div>\n </div>\n </div>\n\n <!-- Quick Actions & Recent Activity -->\n <div class=\"grid grid-cols-1 lg:grid-cols-2 gap-6\">\n <!-- Quick Actions -->\n <div class=\"bg-slate-800 rounded-xl border border-slate-700 p-6\">\n <h2 class=\"text-lg font-semibold text-white mb-4\">{{ t('dashboard.quick_actions') }}</h2>\n <div class=\"space-y-3\">\n <button class=\"w-full flex items-center gap-3 px-4 py-3 bg-slate-700/50 hover:bg-slate-700 rounded-lg text-sm text-slate-300 hover:text-white transition text-left\">\n <svg class=\"w-5 h-5 text-indigo-400 flex-shrink-0\" fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\">\n <path stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" d=\"M12 4.354a4 4 0 110 5.292M15 21H3v-1a6 6 0 0112 0v1zm0 0h6v-1a6 6 0 00-9-5.197m13.5-9a2.25 2.25 0 11-4.5 0 2.25 2.25 0 014.5 0z\" />\n </svg>\n {{ t('dashboard.invite_admin') }}\n </button>\n <button class=\"w-full flex items-center gap-3 px-4 py-3 bg-slate-700/50 hover:bg-slate-700 rounded-lg text-sm text-slate-300 hover:text-white transition text-left\">\n <svg class=\"w-5 h-5 text-cyan-400 flex-shrink-0\" fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\">\n <path stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" d=\"M13.5 21v-7.5a.75.75 0 01.75-.75h3a.75.75 0 01.75.75V21m-4.5 0H2.36m11.14 0H18m0 0h3.64m-1.39 0V9.349m-16.5 11.65V9.35m0 0a3.001 3.001 0 003.75-.615A2.993 2.993 0 009.75 9.75c.896 0 1.7-.393 2.25-1.016a2.993 2.993 0 002.25 1.016c.896 0 1.7-.393 2.25-1.016a3.001 3.001 0 003.75.614m-16.5 0a3.004 3.004 0 01-.621-4.72L4.318 3.44A1.5 1.5 0 015.378 3h13.243a1.5 1.5 0 011.06.44l1.19 1.189a3 3 0 01-.621 4.72m-13.5 8.65h3.75a.75.75 0 00.75-.75V13.5a.75.75 0 00-.75-.75H6.75a.75.75 0 00-.75.75v3.75c0 .415.336.75.75.75z\" />\n </svg>\n {{ t('dashboard.add_vehicle') }}\n </button>\n <button class=\"w-full flex items-center gap-3 px-4 py-3 bg-slate-700/50 hover:bg-slate-700 rounded-lg text-sm text-slate-300 hover:text-white transition text-left\">\n <svg class=\"w-5 h-5 text-emerald-400 flex-shrink-0\" fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\">\n <path stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" d=\"M9 12h3.75M9 15h3.75M9 18h3.75m3 .75H18a2.25 2.25 0 002.25-2.25V6.108c0-1.135-.845-2.098-1.976-2.192a48.424 48.424 0 00-1.123-.08m-5.801 0c-.065.21-.1.433-.1.664 0 .414.336.75.75.75h4.5a.75.75 0 00.75-.75 2.25 2.25 0 00-.1-.664m-5.8 0A2.251 2.251 0 0113.5 2.25H15a2.25 2.25 0 012.15 1.586m-5.8 0c-.376.023-.75.05-1.124.08C9.095 4.01 8.25 4.973 8.25 6.108V8.25m0 0H4.875c-.621 0-1.125.504-1.125 1.125v11.25c0 .621.504 1.125 1.125 1.125h9.75c.621 0 1.125-.504 1.125-1.125V9.375c0-.621-.504-1.125-1.125-1.125H8.25zM6.75 12h.008v.008H6.75V12zm0 3h.008v.008H6.75V15zm0 3h.008v.008H6.75V18z\" />\n </svg>\n {{ t('dashboard.view_reports') }}\n </button>\n </div>\n </div>\n\n <!-- Recent Activity -->\n <div class=\"bg-slate-800 rounded-xl border border-slate-700 p-6\">\n <h2 class=\"text-lg font-semibold text-white mb-4\">{{ t('dashboard.recent_activity') }}</h2>\n <div class=\"space-y-4\">\n <div v-for=\"(activity, index) in recentActivities\" :key=\"index\" class=\"flex items-start gap-3\">\n <div class=\"w-2 h-2 mt-2 rounded-full flex-shrink-0\" :class=\"activity.dotColor\" />\n <div class=\"flex-1 min-w-0\">\n <p class=\"text-sm text-slate-300\">{{ activity.text }}</p>\n <p class=\"text-xs text-slate-500 mt-0.5\">{{ activity.time }}</p>\n </div>\n </div>\n </div>\n </div>\n </div>\n </template>\n </div>\n</template>\n\n<script setup lang=\"ts\">\ndefinePageMeta({\n middleware: 'auth',\n})\n\ninterface DashboardStats {\n total_users: number\n active_users: number\n deleted_users: number\n banned_users: number\n new_users_today: number\n new_users_this_week: number\n new_users_this_month: number\n users_by_role: Record<string, number>\n users_by_plan: Record<string, number>\n users_by_language: Record<string, number>\n users_with_person: number\n users_without_person: number\n total_vehicles: number\n active_organizations_count: number\n total_memberships: number\n registration_trend: { date: string; count: number }[]\n}\n\nconst { t } = useI18n()\n\nconst loading = ref(true)\nconst error = ref(false)\nconst stats = ref<DashboardStats>({\n total_users: 0,\n active_users: 0,\n deleted_users: 0,\n banned_users: 0,\n new_users_today: 0,\n new_users_this_week: 0,\n new_users_this_month: 0,\n users_by_role: {},\n users_by_plan: {},\n users_by_language: {},\n users_with_person: 0,\n users_without_person: 0,\n total_vehicles: 0,\n active_organizations_count: 0,\n total_memberships: 0,\n registration_trend: [],\n})\n\nconst registrationTrend = computed(() => {\n const trend = stats.value.registration_trend\n if (!trend.length) return []\n const maxCount = Math.max(...trend.map(d => d.count))\n return trend.map(d => ({\n date: d.date,\n count: d.count,\n height: maxCount > 0 ? (d.count / maxCount) * 100 : 0,\n }))\n})\n\nfunction formatNumber(num: number): string {\n return new Intl.NumberFormat().format(num)\n}\n\nconst recentActivities = [\n { text: 'New user registered: john.doe@example.com', time: '2 minutes ago', dotColor: 'bg-emerald-400' },\n { text: 'Vehicle #V-8842 added to catalog', time: '15 minutes ago', dotColor: 'bg-cyan-400' },\n { text: 'Payment received: €120.00 from Garage XYZ', time: '1 hour ago', dotColor: 'bg-emerald-400' },\n { text: 'System error: DVLA API timeout on request #4421', time: '2 hours ago', dotColor: 'bg-rose-400' },\n { text: 'Permission updated for user: superadmin@sf.hu', time: '3 hours ago', dotColor: 'bg-indigo-400' },\n]\n\nonMounted(async () => {\n try {\n // Read the access_token from cookie for authenticated API calls\n const tokenCookie = useCookie('access_token')\n const token = tokenCookie.value\n const data = await $fetch('/api/v1/admin/users/stats', {\n headers: token ? { Authorization: `Bearer ${token}` } : undefined,\n })\n stats.value = data as DashboardStats\n } catch (e) {\n console.error('Failed to load dashboard stats:', e)\n error.value = true\n } finally {\n loading.value = false\n }\n})\n</script>\n"],"version":3} |