# Design: UI Foundation ## Icon Library: Lucide Svelte ### Installation ```bash npm install lucide-svelte ``` ### Usage Pattern ```svelte ``` ### Icon Mapping for Navigation | Nav Item | Lucide Icon | |-----------------|------------------| | Dashboard | `LayoutDashboard`| | Team Members | `Users` | | Projects | `Folder` | | Allocations | `Calendar` | | Actuals | `CheckCircle` | | Forecast | `TrendingUp` | | Utilization | `BarChart3` | | Costs | `DollarSign` | | Variance | `AlertTriangle` | | Settings | `Settings` | | Master Data | `Database` | --- ## Types ### `src/lib/types/layout.ts` ```typescript export type SidebarState = 'expanded' | 'collapsed' | 'hidden'; export interface NavItem { label: string; href: string; icon: string; // Lucide icon name badge?: string | number; // Optional notification badge } export interface NavSection { title: string; items: NavItem[]; roles?: string[]; // If set, only visible to these roles } export type Theme = 'light' | 'dark'; ``` --- ## Stores ### `src/lib/stores/layout.ts` ```typescript import { writable } from 'svelte/store'; import { browser } from '$app/environment'; import type { SidebarState, Theme } from '$lib/types/layout'; const DEFAULT_SIDEBAR_STATE: SidebarState = 'expanded'; const DEFAULT_THEME: Theme = 'light'; function createLayoutStore() { // Initialize from localStorage or defaults const getInitialState = (): SidebarState => { if (!browser) return DEFAULT_SIDEBAR_STATE; const stored = localStorage.getItem('headroom_sidebar_state'); return (stored as SidebarState) || DEFAULT_SIDEBAR_STATE; }; const getInitialTheme = (): Theme => { if (!browser) return DEFAULT_THEME; const stored = localStorage.getItem('headroom_theme'); if (stored) return stored as Theme; // Respect system preference on first visit if (window.matchMedia('(prefers-color-scheme: dark)').matches) { return 'dark'; } return DEFAULT_THEME; }; const sidebarState = writable(getInitialState()); const theme = writable(getInitialTheme()); // Apply theme to document if (browser) { theme.subscribe((value) => { document.documentElement.setAttribute('data-theme', value); localStorage.setItem('headroom_theme', value); }); sidebarState.subscribe((value) => { localStorage.setItem('headroom_sidebar_state', value); }); } return { sidebarState: { subscribe: sidebarState.subscribe }, theme: { subscribe: theme.subscribe }, toggleSidebar: () => { sidebarState.update((current) => { if (current === 'expanded') return 'collapsed'; if (current === 'collapsed') return 'hidden'; return 'expanded'; }); }, setSidebarState: (state: SidebarState) => sidebarState.set(state), toggleTheme: () => { theme.update((current) => (current === 'light' ? 'dark' : 'light')); }, setTheme: (newTheme: Theme) => theme.set(newTheme), }; } export const layoutStore = createLayoutStore(); ``` ### `src/lib/stores/period.ts` ```typescript import { writable, derived } from 'svelte/store'; import { browser } from '$app/environment'; function createPeriodStore() { const getCurrentMonth = (): string => { const now = new Date(); return `${now.getFullYear()}-${String(now.getMonth() + 1).padStart(2, '0')}`; }; const getInitialPeriod = (): string => { if (!browser) return getCurrentMonth(); const stored = localStorage.getItem('headroom_selected_period'); return stored || getCurrentMonth(); }; const selectedPeriod = writable(getInitialPeriod()); if (browser) { selectedPeriod.subscribe((value) => { localStorage.setItem('headroom_selected_period', value); }); } // Derived values for convenience const selectedMonth = derived(selectedPeriod, ($period) => { const [year, month] = $period.split('-').map(Number); return { year, month }; }); const selectedDate = derived(selectedPeriod, ($period) => { const [year, month] = $period.split('-').map(Number); return new Date(year, month - 1, 1); }); return { selectedPeriod: { subscribe: selectedPeriod.subscribe }, selectedMonth, selectedDate, setPeriod: (period: string) => selectedPeriod.set(period), previousMonth: () => { selectedPeriod.update((current) => { const [year, month] = current.split('-').map(Number); const date = new Date(year, month - 2, 1); return `${date.getFullYear()}-${String(date.getMonth() + 1).padStart(2, '0')}`; }); }, nextMonth: () => { selectedPeriod.update((current) => { const [year, month] = current.split('-').map(Number); const date = new Date(year, month, 1); return `${date.getFullYear()}-${String(date.getMonth() + 1).padStart(2, '0')}`; }); }, currentMonth: () => selectedPeriod.set(getCurrentMonth()), }; } export const periodStore = createPeriodStore(); ``` --- ## Navigation Configuration ### `src/lib/config/navigation.ts` ```typescript import type { NavSection } from '$lib/types/layout'; export const navigationSections: NavSection[] = [ { title: 'PLANNING', items: [ { label: 'Dashboard', href: '/dashboard', icon: 'LayoutDashboard' }, { label: 'Team Members', href: '/team-members', icon: 'Users' }, { label: 'Projects', href: '/projects', icon: 'Folder' }, { label: 'Allocations', href: '/allocations', icon: 'Calendar' }, { label: 'Actuals', href: '/actuals', icon: 'CheckCircle' }, ], }, { title: 'REPORTS', items: [ { label: 'Forecast', href: '/reports/forecast', icon: 'TrendingUp' }, { label: 'Utilization', href: '/reports/utilization', icon: 'BarChart3' }, { label: 'Costs', href: '/reports/costs', icon: 'DollarSign' }, { label: 'Variance', href: '/reports/variance', icon: 'AlertTriangle' }, { label: 'Allocation Matrix', href: '/reports/allocation', icon: 'Grid3X3' }, ], }, { title: 'ADMIN', roles: ['superuser'], items: [ { label: 'Settings', href: '/settings', icon: 'Settings' }, { label: 'Master Data', href: '/master-data', icon: 'Database' }, ], }, ]; ``` --- ## Theme CSS ### Update `src/app.css` ```css @import 'tailwindcss'; @import 'daisyui'; /* Theme variables - optional customization */ :root { --sidebar-width-expanded: 240px; --sidebar-width-collapsed: 64px; --topbar-height: 56px; } /* Ensure theme attribute works */ [data-theme='light'] { color-scheme: light; } [data-theme='dark'] { color-scheme: dark; } ``` --- ## File Structure ``` src/lib/ ├── types/ │ └── layout.ts # NEW ├── stores/ │ ├── layout.ts # NEW │ ├── period.ts # NEW │ └── auth.ts # EXISTS ├── config/ │ └── navigation.ts # NEW └── ... ```