From 551df89f8d4a566dd05f4116aa4314699df91148 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=88=98=E4=BF=8A=E9=91=AB?= <673638712@qq.com> Date: Mon, 1 Jun 2026 14:41:56 +0800 Subject: [PATCH] =?UTF-8?q?feat(monitoring):=20=E8=B0=83=E6=95=B4=E7=AD=9B?= =?UTF-8?q?=E9=80=89=E4=B8=8E=E8=B0=83=E7=94=A8=E6=96=B9=E5=AF=86=E9=92=A5?= =?UTF-8?q?=E6=B1=87=E6=80=BB=E4=BA=A4=E4=BA=92?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../monitoring/accountOverviewState.ts | 1 + .../hooks/useMonitoringData.test.ts | 13 +++++++++ .../monitoring/hooks/useMonitoringData.ts | 25 ++++++----------- src/i18n/locales/en.json | 1 + src/i18n/locales/ru.json | 1 + src/i18n/locales/zh-CN.json | 1 + src/i18n/locales/zh-TW.json | 1 + src/pages/MonitoringCenterPage.module.scss | 2 +- src/pages/MonitoringCenterPage.tsx | 28 ++++++++++++++----- 9 files changed, 49 insertions(+), 24 deletions(-) diff --git a/src/features/monitoring/accountOverviewState.ts b/src/features/monitoring/accountOverviewState.ts index 1873b1a85..748218ed9 100644 --- a/src/features/monitoring/accountOverviewState.ts +++ b/src/features/monitoring/accountOverviewState.ts @@ -42,6 +42,7 @@ export type MonitoringPageSizes = { const MONITORING_TIME_RANGE_VALUES: readonly MonitoringTimeRange[] = [ 'today', + 'yesterday', '7d', '14d', '30d', diff --git a/src/features/monitoring/hooks/useMonitoringData.test.ts b/src/features/monitoring/hooks/useMonitoringData.test.ts index a108cb2fb..3026c18e1 100644 --- a/src/features/monitoring/hooks/useMonitoringData.test.ts +++ b/src/features/monitoring/hooks/useMonitoringData.test.ts @@ -6,6 +6,7 @@ import { buildMonitoringFilterFacetsFromSummary, buildRangeFilteredRows, buildMonitoringAuthMetaMap, + getRangeBounds, type MonitoringEventRow, } from './useMonitoringData'; import { sha256Hex } from '@/utils/apiKeyHash'; @@ -58,6 +59,18 @@ const createMonitoringEventRow = ( searchText: overrides.searchText ?? 'amount myth resend', }); +describe('getRangeBounds', () => { + it('returns the previous local day for yesterday', () => { + const nowMs = new Date(2026, 4, 9, 12, 34, 56, 789).getTime(); + const bounds = getRangeBounds('yesterday', nowMs); + + expect(bounds).toEqual({ + startMs: new Date(2026, 4, 8, 0, 0, 0, 0).getTime(), + endMs: new Date(2026, 4, 9, 0, 0, 0, 0).getTime(), + }); + }); +}); + describe('buildAccountRows', () => { it('keeps raw auth indices for account-level auth file linking', () => { const rows = buildAccountRows([ diff --git a/src/features/monitoring/hooks/useMonitoringData.ts b/src/features/monitoring/hooks/useMonitoringData.ts index d92639227..4feadee05 100644 --- a/src/features/monitoring/hooks/useMonitoringData.ts +++ b/src/features/monitoring/hooks/useMonitoringData.ts @@ -68,6 +68,8 @@ export const getRangeBounds = ( switch (range) { case 'today': return { startMs: todayStart, endMs: nowMs }; + case 'yesterday': + return { startMs: todayStart - 24 * 60 * 60 * 1000, endMs: todayStart }; case '7d': return { startMs: todayStart - 6 * 24 * 60 * 60 * 1000, endMs: nowMs }; case '14d': @@ -85,6 +87,7 @@ const shouldUseHourlyTimeline = ( customRange?: MonitoringCustomTimeRange | null ) => range === 'today' || + range === 'yesterday' || (range === 'custom' && isValidCustomTimeRange(customRange) && buildLocalDayKey(customRange.startMs) === buildLocalDayKey(customRange.endMs)); @@ -291,7 +294,7 @@ type MonitoringAuthMeta = { updatedAt: string; }; -export type MonitoringTimeRange = 'today' | '7d' | '14d' | '30d' | 'all' | 'custom'; +export type MonitoringTimeRange = 'today' | 'yesterday' | '7d' | '14d' | '30d' | 'all' | 'custom'; export type MonitoringCustomTimeRange = { startMs: number; @@ -1860,9 +1863,7 @@ const buildEventRows = ( Number(detail.__streamFailureCountToEvent) || 0, 0 ); - const serverStreamRecentPatternToEvent = Array.isArray( - detail.__streamRecentPatternToEvent - ) + const serverStreamRecentPatternToEvent = Array.isArray(detail.__streamRecentPatternToEvent) ? detail.__streamRecentPatternToEvent.map((value) => value === true).slice(-10) : undefined; const dayKey = buildLocalDayKey(timestampMs); @@ -1915,10 +1916,8 @@ const buildEventRows = ( totalCost, serverStreamKey: serverStreamTotalCalls > 0 ? serverStreamKey || undefined : undefined, serverStreamTotalCalls: serverStreamTotalCalls > 0 ? serverStreamTotalCalls : undefined, - serverStreamSuccessCalls: - serverStreamTotalCalls > 0 ? serverStreamSuccessCalls : undefined, - serverStreamFailureCalls: - serverStreamTotalCalls > 0 ? serverStreamFailureCalls : undefined, + serverStreamSuccessCalls: serverStreamTotalCalls > 0 ? serverStreamSuccessCalls : undefined, + serverStreamFailureCalls: serverStreamTotalCalls > 0 ? serverStreamFailureCalls : undefined, serverStreamRecentPattern, serverStreamRequestCount: serverStreamRequestCount > 0 ? serverStreamRequestCount : undefined, @@ -2006,9 +2005,7 @@ const buildAccountRowsFromPageItems = ( const latencySum = readPageNumber(item, 'latency_sum_ms', 'latencySumMs'); const rawRecentPattern = item.recent_pattern ?? item.recentPattern; const recentPattern = Array.isArray(rawRecentPattern) - ? rawRecentPattern - .map((value: unknown) => value === true) - .filter((_, index) => index < 10) + ? rawRecentPattern.map((value: unknown) => value === true).filter((_, index) => index < 10) : []; return { id: account, @@ -2132,11 +2129,7 @@ const buildRealtimeRowsFromPageItems = ( success_count: item.failed === true ? 0 : 1, failure_count: item.failed === true ? 1 : 0, __streamKey: readString(item.stream_key ?? item.streamKey), - __streamTotalRequests: readPageNumber( - item, - 'stream_total_requests', - 'streamTotalRequests' - ), + __streamTotalRequests: readPageNumber(item, 'stream_total_requests', 'streamTotalRequests'), __streamSuccessCount: readPageNumber(item, 'stream_success_count', 'streamSuccessCount'), __streamFailureCount: readPageNumber(item, 'stream_failure_count', 'streamFailureCount'), __streamRecentPattern: streamRecentPattern, diff --git a/src/i18n/locales/en.json b/src/i18n/locales/en.json index 1c80560d5..e9de08357 100644 --- a/src/i18n/locales/en.json +++ b/src/i18n/locales/en.json @@ -1165,6 +1165,7 @@ "realtime_console_eyebrow": "Realtime Runtime Monitor", "console_subtitle": "Track call volume, success and failure, token structure, and spend by account within the selected time range, with a live table that behaves more like a request log console.", "range_today": "Today", + "range_yesterday": "Yesterday", "range_7d": "7d", "range_14d": "14d", "range_30d": "30d", diff --git a/src/i18n/locales/ru.json b/src/i18n/locales/ru.json index 0075f10f3..91ec32150 100644 --- a/src/i18n/locales/ru.json +++ b/src/i18n/locales/ru.json @@ -1160,6 +1160,7 @@ "realtime_console_eyebrow": "Realtime Runtime Monitor", "console_subtitle": "Отслеживайте объём вызовов, успехи и ошибки, структуру token и расходы по аккаунтам в выбранном диапазоне времени, с живой таблицей в стиле request log.", "range_today": "Сегодня", + "range_yesterday": "Вчера", "range_7d": "7д", "range_14d": "14д", "range_30d": "30д", diff --git a/src/i18n/locales/zh-CN.json b/src/i18n/locales/zh-CN.json index 5af5f5465..c00f60d0e 100644 --- a/src/i18n/locales/zh-CN.json +++ b/src/i18n/locales/zh-CN.json @@ -1165,6 +1165,7 @@ "realtime_console_eyebrow": "实时运行监控", "console_subtitle": "按账号和时间范围追踪调用规模、成功失败、Token 结构和花费,并提供接近请求日志的实时监控表。", "range_today": "今天", + "range_yesterday": "昨天", "range_7d": "7 天", "range_14d": "14 天", "range_30d": "30 天", diff --git a/src/i18n/locales/zh-TW.json b/src/i18n/locales/zh-TW.json index 971cb7a05..cf73cc1a3 100644 --- a/src/i18n/locales/zh-TW.json +++ b/src/i18n/locales/zh-TW.json @@ -1189,6 +1189,7 @@ "realtime_console_eyebrow": "即時運行監控", "console_subtitle": "按帳號與時間範圍追蹤呼叫規模、成功失敗、Token 結構與花費,並提供接近請求日誌的即時監控表。", "range_today": "今天", + "range_yesterday": "昨天", "range_7d": "7 天", "range_14d": "14 天", "range_30d": "30 天", diff --git a/src/pages/MonitoringCenterPage.module.scss b/src/pages/MonitoringCenterPage.module.scss index e830eab3a..46180a164 100644 --- a/src/pages/MonitoringCenterPage.module.scss +++ b/src/pages/MonitoringCenterPage.module.scss @@ -740,7 +740,7 @@ .filterGrid { display: grid; - grid-template-columns: repeat(5, minmax(0, 1fr)); + grid-template-columns: repeat(6, minmax(0, 1fr)); align-items: start; gap: 12px; diff --git a/src/pages/MonitoringCenterPage.tsx b/src/pages/MonitoringCenterPage.tsx index a9f08f070..d2630484e 100644 --- a/src/pages/MonitoringCenterPage.tsx +++ b/src/pages/MonitoringCenterPage.tsx @@ -8,6 +8,7 @@ import { useRef, useState, type ChangeEvent, + type MouseEvent, type ReactNode, } from 'react'; import { Link } from 'react-router-dom'; @@ -120,6 +121,7 @@ import styles from './MonitoringCenterPage.module.scss'; const TIME_RANGE_OPTIONS: Array<{ value: MonitoringTimeRange; labelKey: string }> = [ { value: 'today', labelKey: 'monitoring.range_today' }, + { value: 'yesterday', labelKey: 'monitoring.range_yesterday' }, { value: '7d', labelKey: 'monitoring.range_7d' }, { value: '14d', labelKey: 'monitoring.range_14d' }, { value: '30d', labelKey: 'monitoring.range_30d' }, @@ -2284,8 +2286,7 @@ export function MonitoringCenterPage() { ); const syncPriceModels = useMemo( - () => - buildModelPriceCandidateModels(filterFacets.models, filteredRows, modelPrices), + () => buildModelPriceCandidateModels(filterFacets.models, filteredRows, modelPrices), [filterFacets.models, filteredRows, modelPrices] ); @@ -2808,6 +2809,17 @@ export function MonitoringCenterPage() { setCustomDraftEndInput(event.target.value); }, []); + const openDateTimePicker = useCallback((event: MouseEvent) => { + const input = event.currentTarget; + input.focus(); + try { + input.showPicker?.(); + } catch { + // Some browsers only allow showPicker during trusted user gestures. + // Keeping focus preserves the native fallback behavior. + } + }, []); + const applyCustomTimeRange = useCallback(() => { if (customDraftTimeRangeError) return; setCustomStartInput(customDraftStartInput); @@ -3860,11 +3872,6 @@ export function MonitoringCenterPage() { extra={
-
- - {t('monitoring.api_key_summary_keys_count', { count: apiKeyTotalCount })} - -