mirror of
https://github.com/alkimake/paperclip.git
synced 2026-06-17 03:10:38 +09:00
fix(costs): harden company auth check, fix frozen date memo, hide empty quota rows
- add company existence check on quota-windows route to guard against sentinel and forged company IDs (was a no-op assertCompanyAccess) - fix useDateRange minuteTick memo frozen at mount; realign interval to next calendar minute boundary via setTimeout + intervalRef pattern - fix midnight timer in Costs.tsx to use stable [] dep and self-scheduling todayTimerRef to avoid StrictMode double-invoke - return null for rolling window rows with no DB data instead of rendering $0.00 / 0 tok false zeros - fix secondsToWindowLabel to handle windows >168h with actual day count instead of silently falling back to 7d - fix byProvider.get(p) non-null assertion to use ?? [] fallback
This commit is contained in:
parent
bc991a96b4
commit
db20f4f46e
5 changed files with 66 additions and 22 deletions
|
|
@ -1,4 +1,4 @@
|
|||
import { useEffect, useMemo, useState } from "react";
|
||||
import { useEffect, useMemo, useRef, useState } from "react";
|
||||
import { useQuery } from "@tanstack/react-query";
|
||||
import type { CostByProviderModel, CostWindowSpendRow, QuotaWindow } from "@paperclipai/shared";
|
||||
import { costsApi } from "../api/costs";
|
||||
|
|
@ -71,16 +71,23 @@ export function Costs() {
|
|||
setBreadcrumbs([{ label: "Costs" }]);
|
||||
}, [setBreadcrumbs]);
|
||||
|
||||
// today as state so a scheduled effect can flip it at midnight, triggering a fresh weekRange
|
||||
// today as state so the weekRange memo refreshes after midnight.
|
||||
// stable [] dep + ref avoids the StrictMode double-invoke problem of the
|
||||
// chained [today] dep pattern (which would schedule two concurrent timers).
|
||||
const [today, setToday] = useState(() => new Date().toDateString());
|
||||
const todayTimerRef = useRef<ReturnType<typeof setTimeout> | null>(null);
|
||||
useEffect(() => {
|
||||
const msUntilMidnight = () => {
|
||||
const schedule = () => {
|
||||
const now = new Date();
|
||||
return new Date(now.getFullYear(), now.getMonth(), now.getDate() + 1).getTime() - now.getTime();
|
||||
const ms = new Date(now.getFullYear(), now.getMonth(), now.getDate() + 1).getTime() - now.getTime();
|
||||
todayTimerRef.current = setTimeout(() => {
|
||||
setToday(new Date().toDateString());
|
||||
schedule();
|
||||
}, ms);
|
||||
};
|
||||
const timer = setTimeout(() => setToday(new Date().toDateString()), msUntilMidnight());
|
||||
return () => clearTimeout(timer);
|
||||
}, [today]);
|
||||
schedule();
|
||||
return () => { if (todayTimerRef.current != null) clearTimeout(todayTimerRef.current); };
|
||||
}, []);
|
||||
const weekRange = useMemo(() => currentWeekRange(), [today]);
|
||||
|
||||
// ---------- spend tab queries (no polling — cost data doesn't change in real time) ----------
|
||||
|
|
@ -247,7 +254,7 @@ export function Costs() {
|
|||
},
|
||||
...providers.map((p) => ({
|
||||
value: p,
|
||||
label: <ProviderTabLabel provider={p} rows={byProvider.get(p)!} />,
|
||||
label: <ProviderTabLabel provider={p} rows={byProvider.get(p) ?? []} />,
|
||||
})),
|
||||
];
|
||||
}, [providers, byProvider]);
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue