Spaces:
Running
Running
File size: 3,284 Bytes
b034029 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 | import { useEffect, useCallback } from 'react';
import { ApiError } from '@/lib/api';
import type { UsageOverviewResponse, UsageSnapshot, UsageTimeRange } from '@/lib/types';
import { USAGE_STATS_STALE_TIME_MS, useUsageStatsStore } from '@/stores';
export type UsagePayload = Partial<UsageSnapshot>;
export type UsageOverviewPayload = Omit<UsageOverviewResponse, 'usage'> & {
usage: UsagePayload;
};
export interface UseUsageDataReturn {
usage: UsageOverviewPayload | null;
loading: boolean;
error: string;
lastRefreshedAt: Date | null;
loadUsage: () => Promise<void>;
}
export interface UseUsageDataOptions {
onAuthRequired?: () => void;
range?: UsageTimeRange;
customStart?: string;
customEnd?: string;
enabled?: boolean;
}
const toRangeQuery = (value: string): UsageTimeRange => (
value === '4h' || value === '8h' || value === '12h' || value === '24h' || value === 'today' || value === '7d' || value === 'all' || value === 'custom'
? value
: 'all'
);
const toCustomDateParam = (value: string | undefined): string | undefined => {
const trimmed = value?.trim();
return trimmed && /^\d{4}-\d{2}-\d{2}$/.test(trimmed) ? trimmed : undefined;
};
export function useUsageData(options: UseUsageDataOptions = {}): UseUsageDataReturn {
const { onAuthRequired, range = 'all', customStart, customEnd, enabled = true } = options;
const usageSnapshot = useUsageStatsStore((state) => state.usage);
const loading = useUsageStatsStore((state) => state.loading);
const storeError = useUsageStatsStore((state) => state.error);
const lastRefreshedAtTs = useUsageStatsStore((state) => state.lastRefreshedAt);
const loadUsageStats = useUsageStatsStore((state) => state.loadUsageStats);
const resolvedRange = toRangeQuery(range);
const requestStart = resolvedRange === 'custom' ? toCustomDateParam(customStart) : undefined;
const requestEnd = resolvedRange === 'custom' ? toCustomDateParam(customEnd) : undefined;
const customRangeReady = resolvedRange !== 'custom' || (requestStart !== undefined && requestEnd !== undefined);
const loadUsage = useCallback(async () => {
if (!customRangeReady) return;
try {
await loadUsageStats({
force: true,
staleTimeMs: USAGE_STATS_STALE_TIME_MS,
range: resolvedRange,
start: requestStart,
end: requestEnd,
});
} catch (error) {
if (error instanceof ApiError && error.status === 401) {
onAuthRequired?.();
}
throw error;
}
}, [customRangeReady, loadUsageStats, onAuthRequired, requestEnd, requestStart, resolvedRange]);
useEffect(() => {
if (!enabled || !customRangeReady) {
return;
}
void loadUsageStats({
staleTimeMs: USAGE_STATS_STALE_TIME_MS,
range: resolvedRange,
start: requestStart,
end: requestEnd,
}).catch((error) => {
if (error instanceof ApiError && error.status === 401) {
onAuthRequired?.();
}
});
}, [customRangeReady, enabled, loadUsageStats, onAuthRequired, requestEnd, requestStart, resolvedRange]);
return {
usage: usageSnapshot as UsageOverviewPayload | null,
loading,
error: storeError || '',
lastRefreshedAt: lastRefreshedAtTs ? new Date(lastRefreshedAtTs) : null,
loadUsage,
};
}
|