feat: multi-user support, goals editing, shared food library
Some checks failed
Security Checks / dependency-audit (push) Has been cancelled
Security Checks / secret-scanning (push) Has been cancelled
Security Checks / dockerfile-lint (push) Has been cancelled

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:
Yusuf Suleman
2026-03-29 14:44:46 -05:00
parent 9e13984b05
commit 810502ab9d
8 changed files with 359 additions and 169 deletions

View File

@@ -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

View File

@@ -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}>