diff --git a/be/apps/core/src/modules/platform/super-admin/super-admin-tenants.controller.ts b/be/apps/core/src/modules/platform/super-admin/super-admin-tenants.controller.ts index 3249b605..54e6226c 100644 --- a/be/apps/core/src/modules/platform/super-admin/super-admin-tenants.controller.ts +++ b/be/apps/core/src/modules/platform/super-admin/super-admin-tenants.controller.ts @@ -1,9 +1,12 @@ -import { Body, Controller, Get, Param, Patch } from '@afilmory/framework' +import { photoAssets } from '@afilmory/db' +import { Body, Controller, Get, Param, Patch, Query } from '@afilmory/framework' +import { DbAccessor } from 'core/database/database.provider' import { Roles } from 'core/guards/roles.decorator' import { BypassResponseTransform } from 'core/interceptors/response-transform.decorator' import { BillingPlanService } from 'core/modules/platform/billing/billing-plan.service' import { BillingUsageService } from 'core/modules/platform/billing/billing-usage.service' import { TenantService } from 'core/modules/platform/tenant/tenant.service' +import { desc, eq } from 'drizzle-orm' import type { BillingPlanId } from '../billing/billing-plan.types' import { UpdateTenantBanDto, UpdateTenantPlanDto } from './super-admin.dto' @@ -16,8 +19,27 @@ export class SuperAdminTenantController { private readonly tenantService: TenantService, private readonly billingPlanService: BillingPlanService, private readonly billingUsageService: BillingUsageService, + private readonly db: DbAccessor, ) {} + @Get('/:tenantId/photos') + async getTenantPhotos(@Param('tenantId') tenantId: string, @Query('limit') limit = '20') { + const photos = await this.db + .get() + .select() + .from(photoAssets) + .where(eq(photoAssets.tenantId, tenantId)) + .limit(Number(limit)) + .orderBy(desc(photoAssets.createdAt)) + + return { + photos: photos.map((p) => ({ + ...p, + publicUrl: p.manifest.data.thumbnailUrl, + })), + } + } + @Get('/') async listTenants() { const [tenantAggregates, plans] = await Promise.all([ diff --git a/be/apps/core/src/modules/platform/super-admin/super-admin.module.ts b/be/apps/core/src/modules/platform/super-admin/super-admin.module.ts index a74e96fa..34c096ee 100644 --- a/be/apps/core/src/modules/platform/super-admin/super-admin.module.ts +++ b/be/apps/core/src/modules/platform/super-admin/super-admin.module.ts @@ -1,4 +1,5 @@ import { Module } from '@afilmory/framework' +import { DatabaseModule } from 'core/database/database.module' import { SystemSettingModule } from 'core/modules/configuration/system-setting/system-setting.module' import { PhotoBuilderService } from 'core/modules/content/photo/builder/photo-builder.service' import { BillingModule } from 'core/modules/platform/billing/billing.module' @@ -9,7 +10,7 @@ import { SuperAdminSettingController } from './super-admin-settings.controller' import { SuperAdminTenantController } from './super-admin-tenants.controller' @Module({ - imports: [SystemSettingModule, BillingModule, TenantModule], + imports: [SystemSettingModule, BillingModule, TenantModule, DatabaseModule], controllers: [SuperAdminSettingController, SuperAdminBuilderDebugController, SuperAdminTenantController], providers: [PhotoBuilderService], }) diff --git a/be/apps/dashboard/components.json b/be/apps/dashboard/components.json index 5d46f2bd..71f5b218 100644 --- a/be/apps/dashboard/components.json +++ b/be/apps/dashboard/components.json @@ -19,6 +19,7 @@ "hooks": "~/hooks" }, "registries": { - "@animate-ui": "https://animate-ui.com/r/{name}.json" + "@animate-ui": "https://animate-ui.com/r/{name}.json", + "@coss": "https://coss.com/ui/r/{name}.json" } } diff --git a/be/apps/dashboard/package.json b/be/apps/dashboard/package.json index 548fc1a2..ecb8596c 100644 --- a/be/apps/dashboard/package.json +++ b/be/apps/dashboard/package.json @@ -19,6 +19,7 @@ "@afilmory/hooks": "workspace:*", "@afilmory/ui": "workspace:*", "@afilmory/utils": "workspace:*", + "@base-ui-components/react": "1.0.0-beta.6", "@creem_io/better-auth": "0.0.8", "@essentials/request-timeout": "1.3.0", "@headlessui/react": "2.2.9", diff --git a/be/apps/dashboard/src/components/ui/tabs.tsx b/be/apps/dashboard/src/components/ui/tabs.tsx new file mode 100644 index 00000000..80af6cb9 --- /dev/null +++ b/be/apps/dashboard/src/components/ui/tabs.tsx @@ -0,0 +1,73 @@ +'use client' + +import { clsxm as cn } from '@afilmory/utils' +import { Tabs as TabsPrimitive } from '@base-ui-components/react/tabs' + +type TabsVariant = 'default' | 'underline' + +function Tabs({ className, ...props }: TabsPrimitive.Root.Props) { + return ( + + ) +} + +function TabsList({ + variant = 'default', + className, + children, + ...props +}: TabsPrimitive.List.Props & { + variant?: TabsVariant +}) { + return ( + + {children} + + + ) +} + +function TabsTab({ className, ...props }: TabsPrimitive.Tab.Props) { + return ( + + ) +} + +function TabsPanel({ className, ...props }: TabsPrimitive.Panel.Props) { + return +} + +export { Tabs, TabsPanel as TabsContent, TabsList, TabsPanel, TabsTab, TabsTab as TabsTrigger } diff --git a/be/apps/dashboard/src/modules/super-admin/api.ts b/be/apps/dashboard/src/modules/super-admin/api.ts index f48fb899..59e7feea 100644 --- a/be/apps/dashboard/src/modules/super-admin/api.ts +++ b/be/apps/dashboard/src/modules/super-admin/api.ts @@ -8,6 +8,7 @@ import type { BuilderDebugResult, SuperAdminSettingsResponse, SuperAdminTenantListResponse, + SuperAdminTenantPhotosResponse, UpdateSuperAdminSettingsPayload, UpdateTenantBanPayload, UpdateTenantPlanPayload, @@ -177,3 +178,10 @@ export async function runBuilderDebugTest(file: File, options?: RunBuilderDebugO return camelCaseKeys(finalResult) } + +export async function fetchSuperAdminTenantPhotos(tenantId: string): Promise { + const response = await coreApi(`${SUPER_ADMIN_TENANTS_ENDPOINT}/${tenantId}/photos`, { + method: 'GET', + }) + return camelCaseKeys(response) +} diff --git a/be/apps/dashboard/src/modules/super-admin/components/SuperAdminTenantManager.tsx b/be/apps/dashboard/src/modules/super-admin/components/SuperAdminTenantManager.tsx index cfde1ff6..47938ebf 100644 --- a/be/apps/dashboard/src/modules/super-admin/components/SuperAdminTenantManager.tsx +++ b/be/apps/dashboard/src/modules/super-admin/components/SuperAdminTenantManager.tsx @@ -1,17 +1,16 @@ -import { Button, Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from '@afilmory/ui' +import { Button, Modal, Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from '@afilmory/ui' import { Spring } from '@afilmory/utils' import { RefreshCcwIcon } from 'lucide-react' import { m } from 'motion/react' -import { useMemo } from 'react' import { useTranslation } from 'react-i18next' import { toast } from 'sonner' import { LinearBorderPanel } from '~/components/common/LinearBorderPanel' -import { BILLING_USAGE_EVENT_CONFIG } from '~/modules/photos/constants' -import type { BillingUsageEventType } from '~/modules/photos/types' import { useSuperAdminTenantsQuery, useUpdateTenantBanMutation, useUpdateTenantPlanMutation } from '../hooks' import type { BillingPlanDefinition, SuperAdminTenantSummary } from '../types' +import { TenantDetailModal } from './TenantDetailModal' +import { TenantUsageCell } from './TenantUsageCell' const DATE_FORMATTER = new Intl.DateTimeFormat('zh-CN', { dateStyle: 'medium', @@ -93,25 +92,23 @@ export function SuperAdminTenantManager() { return ( - -
- -
+
+ +
+ {tenants.length === 0 ? (

{t('superadmin.tenants.empty')}

) : ( @@ -130,11 +127,11 @@ export function SuperAdminTenantManager() { {tenants.map((tenant) => ( - +
{tenant.name}
{tenant.slug}
- + - +
Modal.present(TenantDetailModal, { tenant })} + onKeyDown={(e) => { + if (e.key === 'Enter' || e.key === ' ') { + Modal.present(TenantDetailModal, { tenant }) + } + }} + role="button" + tabIndex={0} + > + +
- + - +