From 573b0c4bc710a1f7759181aa4dac6c2faf0b82e1 Mon Sep 17 00:00:00 2001 From: kongkx Date: Thu, 10 Sep 2026 20:04:02 +0800 Subject: [PATCH] feat(notification): show device monitoring stats --- src/api/modules/devices.ts | 12 +++++++- src/api/types.ts | 28 ++++++++++++------ src/routes/_authed/devices.tsx | 54 ++++++++++++++++++++++++++-------- 3 files changed, 71 insertions(+), 23 deletions(-) diff --git a/src/api/modules/devices.ts b/src/api/modules/devices.ts index 44fcf5d..4779faf 100644 --- a/src/api/modules/devices.ts +++ b/src/api/modules/devices.ts @@ -1,6 +1,16 @@ import { api } from '@/api/client' -import type { ListParams, NotificationDevice, PaginatedResponse } from '@/api/types' +import type { + ApiResponse, + ListParams, + NotificationDevice, + NotificationDeviceStats, + PaginatedResponse, +} from '@/api/types' export function fetchDevices(params: ListParams) { return api.get>('/api/admin/v1/notification/devices', params) } + +export function fetchDeviceStats() { + return api.get>('/api/admin/v1/notification/devices/stats') +} diff --git a/src/api/types.ts b/src/api/types.ts index a7d814a..531e000 100644 --- a/src/api/types.ts +++ b/src/api/types.ts @@ -389,15 +389,25 @@ export interface LocaleRow { } export interface NotificationDevice { - id: number | string - device_id?: string - user_uid?: string | null - platform?: string - model?: string | null - is_online?: boolean - active_sessions_count?: number - last_seen_at?: string | null - created_at?: string + kind: 'device' + id: string + device_client_id: string + platform: 'ios' | 'android' | 'web' | 'desktop' + push_provider: string | null + app_key: string | null + device_model: string | null + is_online: boolean + has_push_token: boolean + active_sessions_count: number + last_seen_at: string | null + created_at: string + updated_at: string +} + +export interface NotificationDeviceStats { + total_devices: number + online_devices: number + platform_stats: Partial> } export type ReportStatus = 'open' | 'resolved' | 'dismissed' diff --git a/src/routes/_authed/devices.tsx b/src/routes/_authed/devices.tsx index e9f98ab..b38f454 100644 --- a/src/routes/_authed/devices.tsx +++ b/src/routes/_authed/devices.tsx @@ -1,13 +1,15 @@ import { createFileRoute } from '@tanstack/react-router' +import { useQuery } from '@tanstack/react-query' import { PERM, requirePermission } from '@/auth/permissions' import { useState } from 'react' import type { ColumnDef } from '@tanstack/react-table' import type { NotificationDevice } from '@/api/types' -import { fetchDevices } from '@/api/modules/devices' +import { fetchDevices, fetchDeviceStats } from '@/api/modules/devices' import { useListPage } from '@/lib/list-page' import { DataTable } from '@/components/data-table/data-table' import { PageHeader } from '@/components/page-header' import { Badge } from '@/components/ui/badge' +import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card' import { Select, SelectContent, @@ -26,9 +28,9 @@ const columns: ColumnDef[] = [ header: '设备', cell: ({ row }) => (
-
{row.original.model ?? row.original.device_id ?? '—'}
+
{row.original.device_model ?? row.original.device_client_id}
- {String(row.original.device_id ?? row.original.id)} + {row.original.device_client_id}
), @@ -37,10 +39,8 @@ const columns: ColumnDef[] = [ header: '平台', cell: ({ row }) => {row.original.platform ?? '—'}, }, - { - header: '用户', - cell: ({ row }) => {row.original.user_uid ?? '未绑定'}, - }, + { header: '应用', cell: ({ row }) => row.original.app_key ?? '—' }, + { header: '推送', cell: ({ row }) => (row.original.has_push_token ? row.original.push_provider ?? '已配置' : '未配置') }, { header: '在线', cell: ({ row }) => @@ -63,22 +63,35 @@ const columns: ColumnDef[] = [ function DevicesPage() { const [platform, setPlatform] = useState('all') + const [online, setOnline] = useState('all') + const stats = useQuery({ queryKey: ['device-stats'], queryFn: fetchDeviceStats }) const list = useListPage( 'devices', fetchDevices, - platform === 'all' ? {} : { platform }, + { + ...(platform === 'all' ? {} : { platform }), + ...(online === 'all' ? {} : { is_online: online === 'online' }), + }, ) + const stat = stats.data?.data + return (
+ - } +
} /> +
+ + + + +
) } + +function DeviceStatCard({ title, value }: { title: string; value?: number }) { + return ( + + {title} +
{value ?? '—'}
+
+ ) +}