feat(admin): super-admin user detail dashboard

Add a read-only User Detail dashboard for super admins, reachable by
clicking a user name in the admin users list.

Backend: new `admin` module aggregating existing domain services
(no new entities). ADMIN-only endpoints under /api/v1/admin:
overview (profile, account status, wallet balance, revenue, summary
counts), wallet transactions, applications (incl. deleted/docked with
restore eligibility), build/deploy errors, tickets with conversation,
and a composite activity timeline. Adds BillingService.getRevenueSummary
and guards against a wallet get-or-create race in the overview reads.

Frontend: tabbed detail page (overview/applications/activity/errors/
tickets) with lazy per-tab queries; user names in the admin list link to
it (admin only); fa/en i18n keys and response types.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
keyhan
2026-06-17 23:52:21 +03:30
parent 87e2224d38
commit 7958d2fa72
10 changed files with 1409 additions and 3 deletions
+76
View File
@@ -793,6 +793,82 @@ const fa = {
roleUpdated: 'نقش به‌روزرسانی شد',
passwordUpdated: 'رمز عبور به‌روزرسانی شد',
passwordFailed: 'به‌روزرسانی رمز ناموفق بود',
detail: {
back: 'بازگشت',
notFound: 'کاربر پیدا نشد',
loading: 'در حال بارگذاری…',
viewDetails: 'مشاهدهٔ جزئیات',
tabOverview: 'نمای کلی',
tabApplications: 'اپلیکیشن‌ها',
tabActivity: 'تاریخچه فعالیت',
tabErrors: 'خطاها',
tabTickets: 'تیکت‌ها',
accountStatus: 'وضعیت حساب',
basicInfo: 'اطلاعات پایه',
walletBalance: 'موجودی کیف پول',
revenue: 'درآمد ایجادشده',
totalCharged: 'مجموع شارژ',
totalRefunded: 'مجموع بازگشت',
joinedAt: 'تاریخ عضویت',
namespace: 'فضای‌نام',
recentTransactions: 'آخرین تراکنش‌ها',
noTransactions: 'تراکنشی ثبت نشده.',
countsTitle: 'آمار خلاصه',
appsTotal: 'کل اپلیکیشن‌ها',
appsActive: 'اپ‌های فعال',
appsDeleted: 'اپ‌های حذف/بایگانی‌شده',
managedServices: 'سرویس‌های مدیریت‌شده',
optionalServices: 'سرویس‌های جانبی',
deploymentsCount: 'دیپلوی‌ها',
ticketsOpenCount: 'تیکت‌های باز',
txType: 'نوع',
txAmount: 'مبلغ',
txBalance: 'موجودی پس از',
txDesc: 'توضیح',
txDate: 'تاریخ',
txTypes: { charge: 'شارژ', deduction: 'کسر', refund: 'بازگشت', gateway_payment: 'پرداخت درگاه' },
appName: 'نام',
appType: 'نوع',
appStatus: 'وضعیت',
appExpires: 'انقضای سرویس',
appCreated: 'تاریخ ایجاد',
appResources: 'منابع',
activeApps: 'اپ‌های فعال',
deletedApps: 'اپ‌های حذف/بایگانی‌شده',
noApps: 'اپلیکیشنی نیست.',
expired: 'منقضی‌شده',
remainingDays: '{n} روز باقی‌مانده',
remainingHours: '{n} ساعت باقی‌مانده',
lifecycle: { active: 'فعال', suspended: 'معلق', pending_deletion: 'در انتظار حذف', docked: 'بایگانی‌شده', deleted: 'حذف‌شده' },
restore: { restorable: 'قابل بازگردانی', recoverable: 'با پرداخت قابل احیا', none: 'غیرقابل بازگردانی' },
errorApp: 'اپلیکیشن',
errorReason: 'علت خطا',
errorTime: 'زمان',
errorState: 'وضعیت',
errorResolved: 'حل‌شده',
errorOpen: 'باز',
noErrors: 'خطای Build/Deploy ثبت نشده.',
noErrorMessage: 'پیام خطایی ثبت نشده.',
viewLogs: 'مشاهدهٔ لاگ',
hideLogs: 'بستن لاگ',
buildLog: 'لاگ Build',
deployLog: 'لاگ Deploy',
noLog: 'لاگی موجود نیست.',
noActivity: 'فعالیتی ثبت نشده.',
activityTypes: { app_created: 'ساخت اپلیکیشن', app_docked: 'بایگانی اپلیکیشن', app_deleted: 'حذف اپلیکیشن', deployment: 'دیپلوی', transaction: 'تراکنش کیف پول', ticket: 'تیکت پشتیبانی' },
ticketSubject: 'موضوع',
ticketStatus: 'وضعیت',
ticketDept: 'دپارتمان',
ticketPriority: 'اولویت',
ticketMessages: '{n} پیام',
noTickets: 'تیکتی ثبت نشده.',
viewConversation: 'مشاهدهٔ گفتگو',
hideConversation: 'بستن گفتگو',
ticketStatuses: { open: 'باز', answered: 'پاسخ داده‌شده', waiting: 'در انتظار پاسخ', closed: 'بسته' },
ticketDepts: { technical: 'فنی', sales: 'فروش' },
ticketPriorities: { low: 'کم', medium: 'متوسط', high: 'زیاد' },
senderRoles: { user: 'کاربر', admin: 'مدیر', technical: 'پشتیبان فنی', sales: 'کارشناس فروش' },
},
},
pools: {
title: 'پول‌های کلاستر',