Issue #3: User Preferences - export format default - Add userPreferencesStore with localStorage persistence - Create Settings page with export format dropdown and theme toggle - SessionDetailPage now uses default export format from preferences Issue #5: Step Categories - database table and seed data - Migration 007: step_categories table with team scoping - Seed 10 default global categories (Citrix/VDI, AD, M365, etc.) - Full CRUD API at /api/v1/step-categories Issue #6: Step Library - database schema - Migration 008: step_library, step_ratings, step_usage_log tables - Support for decision/action/solution step types - Visibility levels: private, team, public - Rating aggregates and usage tracking Issue #7: Step Library - CRUD API endpoints - Full CRUD at /api/v1/steps with visibility filtering - Full-text search endpoint - Popular tags endpoint - Rating/review system with verified use tracking Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
76 lines
2.3 KiB
TypeScript
76 lines
2.3 KiB
TypeScript
import { Link, useLocation, useNavigate, Outlet } from 'react-router-dom'
|
|
import { useAuthStore } from '@/store/authStore'
|
|
import { ThemeToggle } from '@/components/common/ThemeToggle'
|
|
import { cn } from '@/lib/utils'
|
|
|
|
export function AppLayout() {
|
|
const location = useLocation()
|
|
const navigate = useNavigate()
|
|
const { user, logout } = useAuthStore()
|
|
|
|
const handleLogout = async () => {
|
|
await logout()
|
|
navigate('/login')
|
|
}
|
|
|
|
const navItems = [
|
|
{ path: '/trees', label: 'Trees' },
|
|
{ path: '/sessions', label: 'Sessions' },
|
|
{ path: '/settings', label: 'Settings' },
|
|
]
|
|
|
|
return (
|
|
<div className="min-h-screen bg-background">
|
|
{/* Header */}
|
|
<header className="sticky top-0 z-50 border-b border-border bg-card">
|
|
<div className="container mx-auto flex h-14 items-center justify-between px-4">
|
|
<div className="flex items-center gap-8">
|
|
<Link to="/trees" className="text-lg font-bold text-foreground">
|
|
Patherly
|
|
</Link>
|
|
<nav className="hidden items-center gap-1 sm:flex">
|
|
{navItems.map((item) => (
|
|
<Link
|
|
key={item.path}
|
|
to={item.path}
|
|
className={cn(
|
|
'rounded-md px-3 py-2 text-sm font-medium transition-colors',
|
|
location.pathname.startsWith(item.path)
|
|
? 'bg-accent text-accent-foreground'
|
|
: 'text-muted-foreground hover:bg-accent hover:text-accent-foreground'
|
|
)}
|
|
>
|
|
{item.label}
|
|
</Link>
|
|
))}
|
|
</nav>
|
|
</div>
|
|
|
|
<div className="flex items-center gap-4">
|
|
<span className="hidden text-sm text-muted-foreground sm:block">
|
|
{user?.name || user?.email}
|
|
</span>
|
|
<ThemeToggle />
|
|
<button
|
|
onClick={handleLogout}
|
|
className={cn(
|
|
'rounded-md px-3 py-1.5 text-sm font-medium',
|
|
'text-muted-foreground hover:bg-accent hover:text-accent-foreground'
|
|
)}
|
|
>
|
|
Logout
|
|
</button>
|
|
</div>
|
|
</div>
|
|
</header>
|
|
|
|
{/* Main Content */}
|
|
<main>
|
|
<Outlet />
|
|
</main>
|
|
</div>
|
|
)
|
|
}
|
|
|
|
export default AppLayout
|