Comprehensive UX audit fixes: navigation, feedback, affordances, and accessibility
CI / build-and-push (push) Successful in 28s
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>
This commit is contained in:
@@ -1,4 +1,4 @@
|
||||
import { useEffect } from 'react';
|
||||
import { useEffect, useState } from 'react';
|
||||
import { useGameStore, type ActivePage } from '@/store';
|
||||
|
||||
const VALID_PAGES = new Set<ActivePage>([
|
||||
@@ -7,32 +7,47 @@ const VALID_PAGES = new Set<ActivePage>([
|
||||
'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 hash = window.location.hash.slice(1) as ActivePage;
|
||||
if (hash && VALID_PAGES.has(hash) && hash !== activePage) {
|
||||
setActivePage(hash);
|
||||
const { page, subPath: sub } = parseHash();
|
||||
if (page && page !== activePage) {
|
||||
setActivePage(page);
|
||||
}
|
||||
setSubPath(sub);
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
const current = window.location.hash.slice(1);
|
||||
if (current !== activePage) {
|
||||
window.history.pushState(null, '', `#${activePage}`);
|
||||
const expected = subPath ? `${activePage}/${subPath}` : activePage;
|
||||
if (current !== expected) {
|
||||
window.history.pushState(null, '', `#${expected}`);
|
||||
}
|
||||
}, [activePage]);
|
||||
}, [activePage, subPath]);
|
||||
|
||||
useEffect(() => {
|
||||
const handler = () => {
|
||||
const hash = window.location.hash.slice(1) as ActivePage;
|
||||
if (hash && VALID_PAGES.has(hash)) {
|
||||
setActivePage(hash);
|
||||
const { page, subPath: sub } = parseHash();
|
||||
if (page) {
|
||||
setActivePage(page);
|
||||
}
|
||||
setSubPath(sub);
|
||||
};
|
||||
window.addEventListener('hashchange', handler);
|
||||
return () => window.removeEventListener('hashchange', handler);
|
||||
}, [setActivePage]);
|
||||
|
||||
return { subPath, setSubPath };
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user