feat: multi-user support, goals editing, shared food library
Multi-user: - Madiha account with per-user nav visibility - Dashboard greeting uses actual user display name - Navbar and MobileTabBar accept visibleApps prop - Madiha sees: Dashboard, Trips, Fitness, Budget, Media (no Inventory, Reader) Goals editing: - Goals page now has Edit Goals mode with inline number inputs - Saves via PUT /api/fitness/goals - Shows "No goals set" state for new users Food library: - Default view shows all shared foods (not just user's recent) - Both users see the same food database - Cleaned up duplicates: archived Eggs (kept Egg), Green Grapes (kept Grapes), duplicate Bellwether Yogurt, Latte Macchiato (kept Madiha's Caramel Latte) Add to meal buttons: - "Add to breakfast/lunch/dinner/snack" now focuses the resolve input and sets the meal type so AI logs to the correct meal
This commit is contained in:
@@ -2,6 +2,15 @@
|
||||
import { page } from '$app/state';
|
||||
import { LayoutDashboard, DollarSign, Package, Activity, MoreVertical, MapPin, BookOpen, Library, Settings } from '@lucide/svelte';
|
||||
|
||||
interface Props {
|
||||
visibleApps?: string[];
|
||||
}
|
||||
let { visibleApps = ['trips', 'fitness', 'inventory', 'budget', 'reader', 'media'] }: Props = $props();
|
||||
|
||||
function showApp(id: string): boolean {
|
||||
return visibleApps.includes(id);
|
||||
}
|
||||
|
||||
let moreOpen = $state(false);
|
||||
|
||||
function isActive(path: string): boolean {
|
||||
@@ -20,18 +29,24 @@
|
||||
<LayoutDashboard size={22} />
|
||||
Dashboard
|
||||
</a>
|
||||
<a href="/budget" class="mobile-tab" class:active={isActive('/budget')}>
|
||||
<DollarSign size={22} />
|
||||
Budget
|
||||
</a>
|
||||
<a href="/inventory" class="mobile-tab" class:active={isActive('/inventory')}>
|
||||
<Package size={22} />
|
||||
Inventory
|
||||
</a>
|
||||
<a href="/fitness" class="mobile-tab" class:active={isActive('/fitness')}>
|
||||
<Activity size={22} />
|
||||
Fitness
|
||||
</a>
|
||||
{#if showApp('budget')}
|
||||
<a href="/budget" class="mobile-tab" class:active={isActive('/budget')}>
|
||||
<DollarSign size={22} />
|
||||
Budget
|
||||
</a>
|
||||
{/if}
|
||||
{#if showApp('inventory')}
|
||||
<a href="/inventory" class="mobile-tab" class:active={isActive('/inventory')}>
|
||||
<Package size={22} />
|
||||
Inventory
|
||||
</a>
|
||||
{/if}
|
||||
{#if showApp('fitness')}
|
||||
<a href="/fitness" class="mobile-tab" class:active={isActive('/fitness')}>
|
||||
<Activity size={22} />
|
||||
Fitness
|
||||
</a>
|
||||
{/if}
|
||||
<button class="mobile-tab" class:active={moreOpen} onclick={() => moreOpen = true}>
|
||||
<MoreVertical size={22} />
|
||||
More
|
||||
@@ -45,18 +60,24 @@
|
||||
<div class="more-sheet-overlay open" onclick={(e) => { if (e.target === e.currentTarget) closeMore(); }} onkeydown={() => {}}>
|
||||
<div class="more-sheet">
|
||||
<div class="more-sheet-handle"></div>
|
||||
<a href="/trips" class="more-sheet-item" onclick={closeMore}>
|
||||
<MapPin size={20} />
|
||||
Trips
|
||||
</a>
|
||||
<a href="/reader" class="more-sheet-item" onclick={closeMore}>
|
||||
<BookOpen size={20} />
|
||||
Reader
|
||||
</a>
|
||||
<a href="/media" class="more-sheet-item" onclick={closeMore}>
|
||||
<Library size={20} />
|
||||
Media
|
||||
</a>
|
||||
{#if showApp('trips')}
|
||||
<a href="/trips" class="more-sheet-item" onclick={closeMore}>
|
||||
<MapPin size={20} />
|
||||
Trips
|
||||
</a>
|
||||
{/if}
|
||||
{#if showApp('reader')}
|
||||
<a href="/reader" class="more-sheet-item" onclick={closeMore}>
|
||||
<BookOpen size={20} />
|
||||
Reader
|
||||
</a>
|
||||
{/if}
|
||||
{#if showApp('media')}
|
||||
<a href="/media" class="more-sheet-item" onclick={closeMore}>
|
||||
<Library size={20} />
|
||||
Media
|
||||
</a>
|
||||
{/if}
|
||||
<a href="/settings" class="more-sheet-item" onclick={closeMore}>
|
||||
<Settings size={20} />
|
||||
Settings
|
||||
|
||||
@@ -5,9 +5,14 @@
|
||||
|
||||
interface Props {
|
||||
onOpenCommand?: () => void;
|
||||
visibleApps?: string[];
|
||||
}
|
||||
|
||||
let { onOpenCommand }: Props = $props();
|
||||
let { onOpenCommand, visibleApps = ['trips', 'fitness', 'inventory', 'budget', 'reader', 'media'] }: Props = $props();
|
||||
|
||||
function showApp(id: string): boolean {
|
||||
return visibleApps.includes(id);
|
||||
}
|
||||
|
||||
let tripsOpen = $state(false);
|
||||
let fitnessOpen = $state(false);
|
||||
@@ -34,30 +39,40 @@
|
||||
<div class="navbar-links">
|
||||
<a href="/" class="navbar-link" class:active={page.url.pathname === '/'}>Dashboard</a>
|
||||
|
||||
<!-- Trips dropdown -->
|
||||
<a href="/trips" class="navbar-link" class:active={isActive('/trips')}>Trips</a>
|
||||
{#if showApp('trips')}
|
||||
<a href="/trips" class="navbar-link" class:active={isActive('/trips')}>Trips</a>
|
||||
{/if}
|
||||
|
||||
<!-- Fitness dropdown -->
|
||||
<div class="nav-dropdown" role="menu">
|
||||
<button
|
||||
class="navbar-link"
|
||||
class:active={isActive('/fitness')}
|
||||
onclick={(e) => { e.stopPropagation(); fitnessOpen = !fitnessOpen; tripsOpen = false; }}
|
||||
>Fitness</button>
|
||||
{#if fitnessOpen}
|
||||
<div class="nav-dropdown-menu" onclick={(e) => e.stopPropagation()}>
|
||||
<a href="/fitness" class="nav-dropdown-item" onclick={closeDropdowns}>Dashboard</a>
|
||||
<a href="/fitness/foods" class="nav-dropdown-item" onclick={closeDropdowns}>Foods</a>
|
||||
<a href="/fitness/goals" class="nav-dropdown-item" onclick={closeDropdowns}>Goals</a>
|
||||
<a href="/fitness/templates" class="nav-dropdown-item" onclick={closeDropdowns}>Templates</a>
|
||||
</div>
|
||||
{/if}
|
||||
</div>
|
||||
{#if showApp('fitness')}
|
||||
<div class="nav-dropdown" role="menu">
|
||||
<button
|
||||
class="navbar-link"
|
||||
class:active={isActive('/fitness')}
|
||||
onclick={(e) => { e.stopPropagation(); fitnessOpen = !fitnessOpen; tripsOpen = false; }}
|
||||
>Fitness</button>
|
||||
{#if fitnessOpen}
|
||||
<div class="nav-dropdown-menu" onclick={(e) => e.stopPropagation()}>
|
||||
<a href="/fitness" class="nav-dropdown-item" onclick={closeDropdowns}>Dashboard</a>
|
||||
<a href="/fitness/foods" class="nav-dropdown-item" onclick={closeDropdowns}>Foods</a>
|
||||
<a href="/fitness/goals" class="nav-dropdown-item" onclick={closeDropdowns}>Goals</a>
|
||||
<a href="/fitness/templates" class="nav-dropdown-item" onclick={closeDropdowns}>Templates</a>
|
||||
</div>
|
||||
{/if}
|
||||
</div>
|
||||
{/if}
|
||||
|
||||
<a href="/inventory" class="navbar-link" class:active={isActive('/inventory')}>Inventory</a>
|
||||
<a href="/budget" class="navbar-link" class:active={isActive('/budget')}>Budget</a>
|
||||
<a href="/reader" class="navbar-link" class:active={isActive('/reader')}>Reader</a>
|
||||
<a href="/media" class="navbar-link" class:active={isActive('/media')}>Media</a>
|
||||
{#if showApp('inventory')}
|
||||
<a href="/inventory" class="navbar-link" class:active={isActive('/inventory')}>Inventory</a>
|
||||
{/if}
|
||||
{#if showApp('budget')}
|
||||
<a href="/budget" class="navbar-link" class:active={isActive('/budget')}>Budget</a>
|
||||
{/if}
|
||||
{#if showApp('reader')}
|
||||
<a href="/reader" class="navbar-link" class:active={isActive('/reader')}>Reader</a>
|
||||
{/if}
|
||||
{#if showApp('media')}
|
||||
<a href="/media" class="navbar-link" class:active={isActive('/media')}>Media</a>
|
||||
{/if}
|
||||
</div>
|
||||
|
||||
<button class="search-trigger" onclick={onOpenCommand}>
|
||||
|
||||
Reference in New Issue
Block a user