From 5c37db51158afb9af804a54d73d9ca9f12c370fe Mon Sep 17 00:00:00 2001 From: YouXam Date: Wed, 27 Aug 2025 21:50:08 +0800 Subject: [PATCH] improve ui --- client/components/HistoricalPeriods.tsx | 172 +++++++++++++++++++++--- client/components/LoginPage.tsx | 2 +- 2 files changed, 151 insertions(+), 23 deletions(-) diff --git a/client/components/HistoricalPeriods.tsx b/client/components/HistoricalPeriods.tsx index b8aa859..cca87c7 100644 --- a/client/components/HistoricalPeriods.tsx +++ b/client/components/HistoricalPeriods.tsx @@ -40,12 +40,66 @@ interface HistoricalPeriodsProps { userId: string; } +interface PeriodOption { + period: Period; + totalCost: number | null; // null means still loading +} + export function HistoricalPeriods({ periods, apiKey, userId }: HistoricalPeriodsProps) { const [selectedPeriod, setSelectedPeriod] = useState(null); const [summary, setSummary] = useState(null); const [isLoading, setIsLoading] = useState(false); const [error, setError] = useState(''); + const [periodOptions, setPeriodOptions] = useState([]); + const [isSelectOpen, setIsSelectOpen] = useState(false); + // Fetch total costs for all periods + useEffect(() => { + const fetchAllPeriodCosts = async () => { + if (periods.length === 0) return; + + // Initialize with loading state (totalCost: null) + const initialOptions: PeriodOption[] = periods.map(period => ({ + period, + totalCost: null + })); + setPeriodOptions(initialOptions); + + if (!selectedPeriod && initialOptions.length > 0) { + setSelectedPeriod(initialOptions[0].period); + } + + // Fetch costs individually to update progressively + const updatedOptions = [...initialOptions]; + + for (let i = 0; i < periods.length; i++) { + const period = periods[i]; + try { + const response = await fetch(`/api/periods/${period.index}/summary`, { + headers: { 'X-API-Key': apiKey }, + }); + + if (response.ok) { + const data = await response.json(); + updatedOptions[i] = { + period, + totalCost: data.totals?.totalCost || 0 + }; + } else { + updatedOptions[i] = { period, totalCost: 0 }; + } + } catch { + updatedOptions[i] = { period, totalCost: 0 }; + } + + // Update state after each fetch for progressive loading + setPeriodOptions([...updatedOptions]); + } + }; + + fetchAllPeriodCosts(); + }, [periods, apiKey]); + useEffect(() => { if (periods.length > 0 && !selectedPeriod) { setSelectedPeriod(periods[0] || null); // Select the most recent historical period @@ -83,8 +137,8 @@ export function HistoricalPeriods({ periods, apiKey, userId }: HistoricalPeriods } }; - const formatDate = (dateString: string | null) => { - if (!dateString) return 'Unknown'; + const formatDate = (dateString: string | null, isFirstPeriod: boolean = false) => { + if (!dateString) return isFirstPeriod ? 'Beginning' : 'Unknown'; const date = new Date(dateString); return date.toLocaleString('zh-CN', { timeZone: 'Asia/Shanghai', @@ -97,8 +151,8 @@ export function HistoricalPeriods({ periods, apiKey, userId }: HistoricalPeriods }); }; - const formatDateRange = (startAt: string | null, endAt: string | null) => { - return `${formatDate(startAt)} → ${formatDate(endAt)}`; + const formatDateRange = (startAt: string | null, endAt: string | null, isFirstPeriod: boolean = false) => { + return `${formatDate(startAt, isFirstPeriod)} → ${formatDate(endAt)}`; }; const formatCurrency = (amount: number) => { @@ -130,25 +184,99 @@ export function HistoricalPeriods({ periods, apiKey, userId }: HistoricalPeriods
{/* Period Selector */}
-