SPB Git

spb/findata

Public

web: bars deep-link auto-load, curated GB precision

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Simon-Pierre Boucher committed (Nov 1, 2026) parent 9540256 Browse files

2 changed files +18 −2

modified apps/web/src/app/bars/page.tsx +17 −1
@@ -1,6 +1,6 @@
1 1 "use client";
2 2 import Link from "next/link";
3 −import { useEffect, useMemo, useState, type FormEvent } from "react";
3 +import { useEffect, useMemo, useRef, useState, type FormEvent } from "react";
4 4 import { Download, Play } from "lucide-react";
5 5 import { api, isApiError, type Bar, type BarsOut } from "@/lib/api";
6 6 import { useQueryParam } from "@/lib/hooks";
@@ -15,6 +15,8 @@ const MAX_SPAN: Record<string, number> = { "1m": 31, "5m": 186, "1d": 3660 };
15 15 export default function BarsPage() {
16 16 const qSymbol = useQueryParam("symbol");
17 17 const qInterval = useQueryParam("interval");
18 + const qStart = useQueryParam("start");
19 + const qEnd = useQueryParam("end");
18 20 const [symbol, setSymbol] = useState("AAPL");
19 21 const [interval, setInterval_] = useState<"1m" | "5m" | "1d">("1m");
20 22 const [start, setStart] = useState(daysAgo(7));
@@ -34,6 +36,20 @@ export default function BarsPage() {
34 36 useEffect(() => {
35 37 if (qInterval === "1m" || qInterval === "5m" || qInterval === "1d") setInterval_(qInterval);
36 38 }, [qInterval]);
39 + useEffect(() => {
40 + if (qStart && /^\d{4}-\d{2}-\d{2}$/.test(qStart)) setStart(qStart);
41 + if (qEnd && /^\d{4}-\d{2}-\d{2}$/.test(qEnd)) setEnd(qEnd);
42 + }, [qStart, qEnd]);
43 + // deep link (/bars/?symbol=…&start=…&end=…) loads automatically once the params are applied
44 + const autoRan = useRef(false);
45 + useEffect(() => {
46 + if (autoRan.current || !qSymbol || !qStart || !qEnd) return;
47 + if (symbol === qSymbol.toUpperCase() && start === qStart && end === qEnd) {
48 + autoRan.current = true;
49 + void run();
50 + }
51 + // eslint-disable-next-line react-hooks/exhaustive-deps
52 + }, [symbol, start, end, qSymbol, qStart, qEnd]);
37 53
38 54 const spanDays = useMemo(() => Math.round((new Date(end).getTime() - new Date(start).getTime()) / 86_400_000), [start, end]);
39 55 const tooLarge = spanDays > (MAX_SPAN[interval] ?? 31);
modified apps/web/src/app/page.tsx +1 −1
@@ -75,7 +75,7 @@ export default function OverviewPage() {
75 75 <KpiTile
76 76 label="Lake free"
77 77 value={lake ? fmtGB(lake.free_gb, 0) : "—"}
78 − sub={lake ? `curated ${fmtGB(lake.curated_gb, 0)} / budget ${lake.soft_limit_gb} GB · min free ${lake.hard_min_free_gb} GB` : undefined}
78 + sub={lake ? `curated ${fmtGB(lake.curated_gb, 2)} / budget ${lake.soft_limit_gb} GB · min free ${lake.hard_min_free_gb} GB` : undefined}
79 79 tone={freeTone}
80 80 href={isOwner ? "/storage/" : undefined}
81 81 />
82 82
Menu

Theme