feat(shell): scrollable status tabs, per-page tab titles, skip-to-content, Czech MUI locale
- Shared Tabs: variant=scrollable + auto scroll buttons — the 5 status-filter
tabs no longer clip unreachable at ~360px (desktop rendering unchanged).
- TitleSync: browser tab shows the active page ('Faktury · BOHA'; ambiguous
labels qualified with their section, e.g. 'Záznam – Docházka · BOHA');
index.html fallback now 'BOHA Admin'.
- Skip-to-content link (visually hidden, visible on focus) + id/tabIndex on
<main> — keyboard users skip the 248px sidebar.
- MUI csCZ locale merged into the theme (kills built-in English strings like
'No options'); Czech noOptionsText on Customer/Supplier pickers.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
42
src/admin/components/TitleSync.tsx
Normal file
42
src/admin/components/TitleSync.tsx
Normal file
@@ -0,0 +1,42 @@
|
||||
import { useEffect } from "react";
|
||||
import { useLocation } from "react-router-dom";
|
||||
import { menuSections, isItemActive } from "../ui/navData";
|
||||
|
||||
/**
|
||||
* Null-rendering helper that keeps the browser tab title in sync with the
|
||||
* active navigation item. Scans menuSections in order (first match wins) via
|
||||
* the same isItemActive logic the sidebar uses, so detail routes covered by
|
||||
* matchPrefix (e.g. /offers/123) inherit their section item's label.
|
||||
* No cleanup on unmount — the last title simply persists.
|
||||
*/
|
||||
// Labels reused across sections ("Záznam", "Moje historie", "Správa",
|
||||
// "Přehled") would produce identical tab titles — qualify those with their
|
||||
// section label so /attendance and /trips tabs stay distinguishable.
|
||||
const labelCounts = new Map<string, number>();
|
||||
for (const section of menuSections) {
|
||||
for (const item of section.items) {
|
||||
labelCounts.set(item.label, (labelCounts.get(item.label) ?? 0) + 1);
|
||||
}
|
||||
}
|
||||
|
||||
export default function TitleSync() {
|
||||
const { pathname } = useLocation();
|
||||
|
||||
useEffect(() => {
|
||||
for (const section of menuSections) {
|
||||
const item = section.items.find((candidate) =>
|
||||
isItemActive(candidate, pathname),
|
||||
);
|
||||
if (item) {
|
||||
const ambiguous = (labelCounts.get(item.label) ?? 0) > 1;
|
||||
document.title = ambiguous
|
||||
? `${item.label} – ${section.label} · BOHA`
|
||||
: `${item.label} · BOHA`;
|
||||
return;
|
||||
}
|
||||
}
|
||||
document.title = "BOHA Admin";
|
||||
}, [pathname]);
|
||||
|
||||
return null;
|
||||
}
|
||||
Reference in New Issue
Block a user