Simpl-Resultat/src/hooks/useReportsPeriod.ts
le king fu 6a6a196467
All checks were successful
PR Check / rust (push) Successful in 24m38s
PR Check / frontend (push) Successful in 2m22s
PR Check / rust (pull_request) Successful in 24m56s
PR Check / frontend (pull_request) Successful in 2m31s
refactor: split useReports into per-domain hooks + URL-bookmarked period (#70)
- New useReportsPeriod hook reads/writes period via ?from=&to=&period= URL params,
  default civil year, pure resolver exported for tests
- New per-domain hooks: useHighlights, useTrends, useCompare, useCategoryZoom
  (stubs wired to useReportsPeriod, to be fleshed out in #71-#74)
- Rewire legacy useReports to consume useReportsPeriod; keep backward-compat
  state shape (period/customDateFrom/customDateTo) so /reports tabs keep working
- Mark useReports @deprecated pending removal in #76
- Tests: 7 new cases covering resolveReportsPeriod defaults, bookmarks,
  invalid inputs, preset resolution

Fixes #70

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-14 14:37:33 -04:00

119 lines
3.4 KiB
TypeScript

import { useCallback, useMemo } from "react";
import { useSearchParams } from "react-router-dom";
import type { DashboardPeriod } from "../shared/types";
import { computeDateRange } from "../utils/dateRange";
const VALID_PERIODS: readonly DashboardPeriod[] = [
"month",
"3months",
"6months",
"year",
"12months",
"all",
"custom",
];
function isValidPeriod(p: string | null): p is DashboardPeriod {
return p !== null && (VALID_PERIODS as readonly string[]).includes(p);
}
function isValidIsoDate(s: string | null): s is string {
return !!s && /^\d{4}-\d{2}-\d{2}$/.test(s);
}
function currentYearRange(today: Date = new Date()): { from: string; to: string } {
const year = today.getFullYear();
return { from: `${year}-01-01`, to: `${year}-12-31` };
}
/**
* Pure resolver used by the hook and unit tests. Exposed to keep the core
* logic hookless and testable without rendering a router.
*/
export function resolveReportsPeriod(
rawFrom: string | null,
rawTo: string | null,
rawPeriod: string | null,
today: Date = new Date(),
): { from: string; to: string; period: DashboardPeriod } {
if (isValidIsoDate(rawFrom) && isValidIsoDate(rawTo)) {
const p = isValidPeriod(rawPeriod) ? rawPeriod : "custom";
return { from: rawFrom, to: rawTo, period: p };
}
if (isValidPeriod(rawPeriod) && rawPeriod !== "custom") {
const range = computeDateRange(rawPeriod);
const { from: defaultFrom, to: defaultTo } = currentYearRange(today);
return {
from: range.dateFrom ?? defaultFrom,
to: range.dateTo ?? defaultTo,
period: rawPeriod,
};
}
const { from, to } = currentYearRange(today);
return { from, to, period: "custom" };
}
export interface UseReportsPeriodResult {
from: string;
to: string;
period: DashboardPeriod;
setPeriod: (period: DashboardPeriod) => void;
setCustomDates: (from: string, to: string) => void;
}
/**
* Reads/writes the active reporting period via the URL query string so it is
* bookmarkable and shared across the four report sub-routes.
*
* Defaults to the current civil year (Jan 1 → Dec 31).
*/
export function useReportsPeriod(): UseReportsPeriodResult {
const [searchParams, setSearchParams] = useSearchParams();
const rawPeriod = searchParams.get("period");
const rawFrom = searchParams.get("from");
const rawTo = searchParams.get("to");
const { from, to, period } = useMemo(
() => resolveReportsPeriod(rawFrom, rawTo, rawPeriod),
[rawPeriod, rawFrom, rawTo],
);
const setPeriod = useCallback(
(next: DashboardPeriod) => {
setSearchParams(
(prev) => {
const params = new URLSearchParams(prev);
if (next === "custom") {
params.set("period", "custom");
} else {
params.set("period", next);
params.delete("from");
params.delete("to");
}
return params;
},
{ replace: true },
);
},
[setSearchParams],
);
const setCustomDates = useCallback(
(nextFrom: string, nextTo: string) => {
setSearchParams(
(prev) => {
const params = new URLSearchParams(prev);
params.set("period", "custom");
params.set("from", nextFrom);
params.set("to", nextTo);
return params;
},
{ replace: true },
);
},
[setSearchParams],
);
return { from, to, period, setPeriod, setCustomDates };
}