8d650fefae
CI / build-and-push (push) Successful in 28s
Address 18 issues across high/medium/low impact tiers identified in a full interface review. Key changes: Models page decomposed into tabs, confirmation dialogs for irreversible actions (deploy/open-source/acquire), chart Y-axes made visible, hash router extended for Market tab persistence, collapsible sidebar, keyboard navigation shortcuts (g+key chords), notification bulk actions, achievement progress bars, and ARIA label improvements. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
54 lines
1.6 KiB
TypeScript
54 lines
1.6 KiB
TypeScript
import { useEffect, useState } from 'react';
|
|
import { useGameStore, type ActivePage } from '@/store';
|
|
|
|
const VALID_PAGES = new Set<ActivePage>([
|
|
'dashboard', 'infrastructure', 'research', 'models',
|
|
'market', 'talent', 'data', 'competitors',
|
|
'finance', 'achievements', 'leaderboard', 'settings',
|
|
]);
|
|
|
|
function parseHash(): { page: ActivePage | null; subPath: string | null } {
|
|
const raw = window.location.hash.slice(1);
|
|
const [page, subPath] = raw.split('/') as [string, string | undefined];
|
|
return {
|
|
page: VALID_PAGES.has(page as ActivePage) ? (page as ActivePage) : null,
|
|
subPath: subPath || null,
|
|
};
|
|
}
|
|
|
|
export function useHashRouter() {
|
|
const activePage = useGameStore((s) => s.activePage);
|
|
const setActivePage = useGameStore((s) => s.setActivePage);
|
|
const [subPath, setSubPath] = useState<string | null>(() => parseHash().subPath);
|
|
|
|
useEffect(() => {
|
|
const { page, subPath: sub } = parseHash();
|
|
if (page && page !== activePage) {
|
|
setActivePage(page);
|
|
}
|
|
setSubPath(sub);
|
|
}, []);
|
|
|
|
useEffect(() => {
|
|
const current = window.location.hash.slice(1);
|
|
const expected = subPath ? `${activePage}/${subPath}` : activePage;
|
|
if (current !== expected) {
|
|
window.history.pushState(null, '', `#${expected}`);
|
|
}
|
|
}, [activePage, subPath]);
|
|
|
|
useEffect(() => {
|
|
const handler = () => {
|
|
const { page, subPath: sub } = parseHash();
|
|
if (page) {
|
|
setActivePage(page);
|
|
}
|
|
setSubPath(sub);
|
|
};
|
|
window.addEventListener('hashchange', handler);
|
|
return () => window.removeEventListener('hashchange', handler);
|
|
}, [setActivePage]);
|
|
|
|
return { subPath, setSubPath };
|
|
}
|