{t('superadmin.tenants.empty')}
) : ( @@ -130,11 +127,11 @@ export function SuperAdminTenantManager() { {tenants.map((tenant) => ({t('superadmin.tenants.usage.empty')}
- } - - return ( -{plan.description}
} -type UsageBadgeProps = { - label: string - tone: (typeof BILLING_USAGE_EVENT_CONFIG)[BillingUsageEventType]['tone'] - value: number - unit: 'count' | 'byte' - formatter: Intl.NumberFormat -} - -function UsageBadge({ label, tone, value, unit, formatter }: UsageBadgeProps) { - const toneClass = - tone === 'accent' - ? 'bg-emerald-500/10 text-emerald-200 border-emerald-500/30' - : tone === 'warning' - ? 'bg-rose-500/10 text-rose-200 border-rose-500/30' - : 'bg-fill/30 text-text-secondary border-border/40' - - return ( - - {label} - {formatUsageValue(value, unit, formatter)} - - ) -} - -function formatUsageValue(value: number, unit: 'count' | 'byte', formatter: Intl.NumberFormat): string { - if (!Number.isFinite(value)) { - return '0' - } - if (unit === 'byte') { - return formatBytes(value) - } - return formatter.format(value) -} - -function formatBytes(bytes: number): string { - if (!Number.isFinite(bytes) || bytes <= 0) { - return '0 B' - } - const units = ['B', 'KB', 'MB', 'GB', 'TB'] - let value = bytes - let unitIndex = 0 - while (value >= 1024 && unitIndex < units.length - 1) { - value /= 1024 - unitIndex += 1 - } - return `${value.toFixed(value >= 10 ? 0 : 1)} ${units[unitIndex]}` -} - function StatusBadge({ status, banned }: { status: SuperAdminTenantSummary['status']; banned: boolean }) { const { t } = useTranslation() if (banned) { diff --git a/be/apps/dashboard/src/modules/super-admin/components/TenantDetailModal.tsx b/be/apps/dashboard/src/modules/super-admin/components/TenantDetailModal.tsx new file mode 100644 index 00000000..fc286037 --- /dev/null +++ b/be/apps/dashboard/src/modules/super-admin/components/TenantDetailModal.tsx @@ -0,0 +1,151 @@ +import type { ModalComponent } from '@afilmory/ui' +import { DialogDescription, DialogHeader, DialogTitle } from '@afilmory/ui' +import { useTranslation } from 'react-i18next' + +import { Tabs, TabsContent, TabsList, TabsTrigger } from '~/components/ui/tabs' + +import { useSuperAdminTenantPhotosQuery } from '../hooks' +import type { SuperAdminTenantSummary } from '../types' +import { TenantUsageCell } from './TenantUsageCell' + +interface TenantDetailModalProps { + tenant: SuperAdminTenantSummary +} + +export const TenantDetailModal: ModalComponent{t('superadmin.tenants.modal.photos.loading')}
+ } + + if (isError) { + return{t('superadmin.tenants.modal.photos.error')}
+ } + + const photos = data?.photos ?? [] + + if (photos.length === 0) { + return{t('superadmin.tenants.modal.photos.empty')}
+ } + + return ( +| + {t('superadmin.tenants.modal.photos.table.preview')} + | ++ {t('superadmin.tenants.modal.photos.table.name')} + | ++ {t('superadmin.tenants.modal.photos.table.size')} + | ++ {t('superadmin.tenants.modal.photos.table.created')} + | +
|---|---|---|---|
|
+
+ {/* We might not have a public URL if it's not synced or private, but let's try */}
+ {photo.publicUrl && (
+
+ |
+ + {photo.storageKey.split('/').pop()} + | +{formatBytes(photo.size ?? 0)} | +{new Date(photo.createdAt).toLocaleDateString()} | +
{t('superadmin.tenants.usage.empty')}
+ } + + return ( +