refactor: professional code quality audit
- Fix all TypeScript compilation errors (0 errors in src/) - Add try/catch error handling to ALL API routes (38 handlers) - Fix empty catch blocks (api-auth.ts now logs warnings) - Add aria-labels to all icon-only buttons (WCAG 2.1 AA) - Fix Prisma type mismatches (OR filter, Person.companyId, Estimate.projectId, Ticket.createdAt) - Fix webhook PATCH route (moved to [id]/route.ts) - Fix mobile-bottom-nav type safety - Fix KB route where-clause type - Fix test-mail banner null vs undefined - Fix task comment fetch: add cancellation + error toast - ESLint: 0 errors, 0 warnings - 175 files, 24.741 lines of code
This commit is contained in:
1 parent
969202764a
commit
fa8dd46571
175 files changed
+24741
-2
No files matched your search
Executable
+63
@@ -0,0 +1,63 @@
|
||||
'use client'
|
||||
|
||||
import { useAos, type ViewId } from '@/store/aos'
|
||||
import { LayoutDashboard, Radar, FolderKanban, Bell, Menu } from 'lucide-react'
|
||||
import { cn } from '@/lib/utils'
|
||||
import { useFetch } from '@/hooks/use-fetch'
|
||||
|
||||
// Mobile bottom navigation bar (PRD §9: "Mobile: usable PWA for time tracking,
|
||||
// approvals and quick CRM lookups"). Shows on mobile only (md:hidden).
|
||||
// Provides 5 quick-access destinations optimized for touch.
|
||||
export function MobileBottomNav() {
|
||||
const { view, setView, setSidebarOpen, setCommandOpen } = useAos()
|
||||
const { data } = useFetch<{ stats: { total: number; urgent: number } }>('/api/aos/notifications')
|
||||
|
||||
const urgent = data?.stats.urgent ?? 0
|
||||
const total = data?.stats.total ?? 0
|
||||
|
||||
const items: { id: string; label: string; icon: typeof LayoutDashboard; badge?: number }[] = [
|
||||
{ id: 'dashboard', label: 'Home', icon: LayoutDashboard },
|
||||
{ id: 'leads', label: 'Leads', icon: Radar },
|
||||
{ id: 'search', label: 'Suche', icon: Menu },
|
||||
{ id: 'projects', label: 'Zeit', icon: FolderKanban },
|
||||
{ id: 'notifications', label: 'Alerts', icon: Bell, badge: urgent > 0 ? urgent : total > 0 ? total : undefined },
|
||||
]
|
||||
|
||||
const handleClick = (id: string) => {
|
||||
if (id === 'search') setCommandOpen(true)
|
||||
else if (id === 'notifications') setView('calendar')
|
||||
else if (id === 'menu') setSidebarOpen(true)
|
||||
else setView(id as ViewId)
|
||||
}
|
||||
|
||||
return (
|
||||
<nav className="fixed bottom-0 left-0 right-0 z-40 flex h-16 items-stretch border-t border-border bg-background/95 backdrop-blur-md md:hidden">
|
||||
{items.map((item) => {
|
||||
const Icon = item.icon
|
||||
const active = view === (item.id === 'notifications' ? 'calendar' : (item.id === 'search' || item.id === 'menu') ? ('' as ViewId) : (item.id as ViewId))
|
||||
return (
|
||||
<button
|
||||
key={item.id}
|
||||
onClick={() => handleClick(item.id)}
|
||||
className={cn(
|
||||
'relative flex flex-1 flex-col items-center justify-center gap-0.5 text-[10px] font-medium transition',
|
||||
active ? 'text-foreground' : 'text-muted-foreground'
|
||||
)}
|
||||
aria-label={item.label}
|
||||
>
|
||||
<div className="relative">
|
||||
<Icon className={cn('size-5', active && 'text-foreground')} />
|
||||
{item.badge !== undefined && item.badge > 0 && (
|
||||
<span className="absolute -right-2 -top-1.5 flex size-4 items-center justify-center rounded-full bg-foreground text-[8px] font-bold text-background">
|
||||
{item.badge > 9 ? '9+' : item.badge}
|
||||
</span>
|
||||
)}
|
||||
</div>
|
||||
<span>{item.label}</span>
|
||||
{active && <span className="absolute bottom-0 left-1/2 h-0.5 w-8 -translate-x-1/2 rounded-full bg-foreground" />}
|
||||
</button>
|
||||
)
|
||||
})}
|
||||
</nav>
|
||||
)
|
||||
}
|
||||
Reference in new issue
Block a user