WFDASH-WEBAPP/src/lib/use-theme-id.ts
SJCoetz 7f924ccc45 refactor: switch hardcoded UI colors to semantic theme tokens
Overhaul the app's color system by replacing all hardcoded Tailwind color classes with semantic theme variables to ensure consistent theming across all components, pages, and tests.

Additional changes:
- Add assigned date support for order item status updates: add `assignedDate` field to the order item status PATCH API, implement server-side normalization/validation, and add corresponding tests
- Refactor purchase order PDF workflows to use the consolidated `purchase_orders.documents` field instead of the legacy separate `purchase_order_pdfs` collection
- Fix quote number parsing and formatting to support both dashed (Q-YYYY-MM-NNN) and dashless (QYYYYMMNNN) formats
- Rename `driver_pool` item status to `collection_pool` across the codebase
- Add null handling for order item quantities and pricing to display "—" for missing values
- Improve filter tab styling: make mobile tabs scrollable, prevent text wrapping, hide scrollbar
- Add new utility scripts, API routes, migrations, and polish small UI/UX fixes including tsconfig updates and theme hook improvements
2026-07-07 17:22:03 +02:00

52 lines
1.9 KiB
TypeScript

"use client";
import { useCallback, useEffect, useState } from "react";
import { DEFAULT_THEME_ID, getThemeById } from "@/lib/themes";
const STORAGE_KEY = "wfdash-theme";
/**
* useThemeId — reads / writes the active visual theme id.
*
* The theme id is applied to <html> via the `data-theme` attribute; the
* matching tokens live in src/styles/themes.css.
*
* On the first render the value is `DEFAULT_THEME_ID` — the bootstrap
* script in app/layout.tsx applies the persisted value before hydration,
* then a tiny effect in this hook syncs the React state to whatever the
* DOM already says. This is the only safe way to avoid a hydration
* mismatch without rendering nothing on the first frame.
*/
export function useThemeId() {
const [themeId, setThemeIdState] = useState<string>(DEFAULT_THEME_ID);
useEffect(() => {
// Pull whatever the bootstrap script already set on <html> so the
// first React render matches the DOM. Intentionally a setState-in-effect:
// we need to read the DOM after hydration, not before.
const fromDom = document.documentElement.getAttribute("data-theme");
if (fromDom && fromDom !== themeId) {
// eslint-disable-next-line react-hooks/set-state-in-effect
setThemeIdState(fromDom);
}
}, []); // eslint-disable-line react-hooks/exhaustive-deps
const setThemeId = useCallback((id: string) => {
const t = getThemeById(id);
setThemeIdState(t.id);
try {
localStorage.setItem(STORAGE_KEY, t.id);
} catch {
/* ignore */
}
if (typeof document !== "undefined") {
const root = document.documentElement;
root.setAttribute("data-theme", t.id);
root.style.setProperty("--radius", t.radius);
root.style.setProperty("--font-heading", t.fontHeading);
root.style.setProperty("--font-body", t.fontBody);
}
}, []);
return { themeId, setThemeId };
}