Complete stabilization pass bringing all 3 stacks to green: Frontend (apps/web/): - Fix TypeScript nullability in useSeason.ts, useTimeOfDay.ts hooks - Disable no-undef in ESLint config (TypeScript handles it; JSX misidentified) - Rename 306 story imports from @storybook/react to @storybook/react-vite - Fix conditional hook call in useMediaQuery.ts useIsTablet - Move useQuery to top of LoginPage.tsx component - Remove useless try/catch in GearFormModal.tsx - Fix stale closure in ResetPasswordPage.tsx handleChange - Make Storybook decorators (withRouter, withQueryClient, withToast, withAudio) no-ops since global StorybookDecorator already provides these — prevents nested Router / duplicate provider crashes in vitest-browser - Fix nested MemoryRouter in 3 page stories (TrackDetail, PlaylistDetail, UserProfile) - Update i18n initialization in test setup (await init before changeLanguage) - Update ~30 test assertions from English to French to match i18n translations - Update test assertions to match SUMI V3 design changes (shadow vs border) - Fix remaining story type errors (PlayerError, PlaylistBatchActions, TrackFilters, VirtualizedChatMessages) Backend (veza-backend-api/): - Fix response_test.go RespondWithAppError signature (2 args, not 3) - Fix TestErrorContractAuthEndpoints expected error codes (ErrCodeUnauthorized vs ErrCodeInvalidCredentials) - Fix TestTrackHandler_GetTrackLikes_Success missing auth middleware setup - Fix TestPlaybackAnalyticsService_GetTrackStats k-anonymity threshold (needs 5 unique users, not 1) - Replace NOW() PostgreSQL function with time.Now() parameter in marketplace service for SQLite test compatibility - Add missing AutoMigrate entries in marketplace_test.go (ProductImage, ProductPreview, ProductLicense, ProductReview) Results: - Frontend TypeCheck: 617 errors -> 0 errors - Frontend ESLint: 349 errors -> 0 errors - Frontend Vitest: 196 failing tests -> 1 skipped (3396/3397 passing) - Backend go vet: 1 error -> 0 errors - Backend tests: 5 failing -> all 13 packages passing - Rust: 150/150 tests passing (unchanged) - Storybook audit: 0 errors across 1244 stories Triage report: docs/TRIAGE_REPORT.md Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
56 lines
2.3 KiB
TypeScript
56 lines
2.3 KiB
TypeScript
import { useLocation, Link } from 'react-router-dom';
|
|
import { Home, Search, Layers, MessageSquare, User } from 'lucide-react';
|
|
import { useTranslation } from 'react-i18next';
|
|
import { cn } from '@/lib/utils';
|
|
|
|
const navItems = [
|
|
{ id: 'home', labelKey: 'nav.items.dashboard', icon: Home, path: '/dashboard' },
|
|
{ id: 'search', labelKey: 'nav.items.discover', icon: Search, path: '/discover' },
|
|
{ id: 'library', labelKey: 'nav.items.tracks', icon: Layers, path: '/library' },
|
|
{ id: 'chat', labelKey: 'nav.items.chat', icon: MessageSquare, path: '/chat' },
|
|
{ id: 'profile', labelKey: 'nav.settings', icon: User, path: '/settings' },
|
|
];
|
|
|
|
export function MobileBottomNav() {
|
|
const location = useLocation();
|
|
const { t } = useTranslation();
|
|
|
|
return (
|
|
<nav
|
|
className="fixed bottom-0 left-0 right-0 z-40 bg-[var(--sumi-glass-bg)] backdrop-blur-xl shadow-[0_-4px_8px_-4px_rgba(26,26,30,0.08)] lg:hidden"
|
|
aria-label="Mobile navigation"
|
|
>
|
|
<div className="flex items-center justify-around px-1 py-1">
|
|
{navItems.map((item) => {
|
|
const isActive = location.pathname.startsWith(item.path);
|
|
const Icon = item.icon;
|
|
|
|
return (
|
|
<Link
|
|
key={item.id}
|
|
to={item.path}
|
|
aria-current={isActive ? 'page' : undefined}
|
|
className={cn(
|
|
'relative flex flex-col items-center justify-center gap-0.5 py-2 px-3 min-w-12 min-h-12 rounded-xl transition-colors duration-[var(--sumi-duration-fast)]',
|
|
isActive
|
|
? 'text-primary'
|
|
: 'text-muted-foreground active:text-foreground',
|
|
)}
|
|
>
|
|
<Icon className={cn('h-5 w-5', isActive && 'fill-primary/20')} />
|
|
{/* text-[10px] exception: Tailwind has no scale below text-xs (12px); 10px is standard for mobile bottom nav labels */}
|
|
<span className="text-[10px] font-medium leading-none">
|
|
{t(item.labelKey)}
|
|
</span>
|
|
{isActive && (
|
|
<span className="absolute top-0.5 left-1/2 -translate-x-1/2 w-5 h-0.5 rounded-full bg-primary" />
|
|
)}
|
|
</Link>
|
|
);
|
|
})}
|
|
</div>
|
|
{/* Safe area for iPhone home indicator */}
|
|
<div className="h-[env(safe-area-inset-bottom)]" />
|
|
</nav>
|
|
);
|
|
}
|