diff --git a/src/lib/api/presets.ts b/src/lib/api/presets.ts index ecfc121..f1b8367 100644 --- a/src/lib/api/presets.ts +++ b/src/lib/api/presets.ts @@ -1,11 +1,27 @@ -import { apiGet, apiDelete } from './client'; -import type { Preset } from '$lib/types/api'; +import { apiGet, apiDelete, apiPatch, apiPost } from './client'; +import type { Preset, PresetEntry } from '$lib/types/api'; export function listPresets(limit = 20, offset = 0): Promise { const params = new URLSearchParams({ limit: String(limit), offset: String(offset) }); return apiGet(`/preset?${params}`); } +export function renamePreset(id: number, name: string): Promise { + return apiPatch(`/preset/${id}`, { name }); +} + export function deletePreset(id: number): Promise { return apiDelete(`/preset/${id}`); } + +export function createPresetEntry(presetId: number, productId: number, grams: number): Promise { + return apiPost(`/preset/${presetId}/entry`, { product_id: productId, grams }); +} + +export function updatePresetEntry(presetId: number, entryId: number, grams: number): Promise { + return apiPatch(`/preset/${presetId}/entry/${entryId}`, { grams }); +} + +export function deletePresetEntry(presetId: number, entryId: number): Promise { + return apiDelete(`/preset/${presetId}/entry/${entryId}`); +} diff --git a/src/lib/components/diary/MacroSummary.svelte b/src/lib/components/diary/MacroSummary.svelte index 7fa9260..f92146c 100644 --- a/src/lib/components/diary/MacroSummary.svelte +++ b/src/lib/components/diary/MacroSummary.svelte @@ -38,7 +38,7 @@ fat_goal: number; fiber_goal: number; }>({ - calories_goal: diary.calories_goal || null, + calories_goal: null, protein_goal: diary.protein_goal, carb_goal: diary.carb_goal, fat_goal: diary.fat_goal, @@ -47,7 +47,7 @@ $effect(() => { form = { - calories_goal: diary.calories_goal || null, + calories_goal: null, // always reset to auto when diary refreshes protein_goal: diary.protein_goal, carb_goal: diary.carb_goal, fat_goal: diary.fat_goal, @@ -68,11 +68,13 @@ } -
-
+
+ +
+ -
- +
+
- {kcal(diary.calories)} + {kcal(diary.calories)} {diary.calories_goal > 0 ? `/ ${kcal(diary.calories_goal)}` : 'kcal'} @@ -113,7 +115,7 @@ + + + renameOpen = false} title="Rename meal">
Grams
e.currentTarget.select()} onkeydown={(e) => { if (e.key === 'Enter') handleAddEntry(); }} class="flex-1 bg-zinc-800 border border-zinc-700 rounded-xl px-4 py-2.5 text-center text-xl font-semibold focus:outline-none focus:border-green-500 transition-colors" />
diff --git a/src/routes/(app)/diary/[date]/edit-entry/[entry_id]/+page.svelte b/src/routes/(app)/diary/[date]/edit-entry/[entry_id]/+page.svelte index 54b0853..7199592 100644 --- a/src/routes/(app)/diary/[date]/edit-entry/[entry_id]/+page.svelte +++ b/src/routes/(app)/diary/[date]/edit-entry/[entry_id]/+page.svelte @@ -114,7 +114,8 @@
e.currentTarget.select()} onkeydown={(e) => { if (e.key === 'Enter') handleSave(); }} class="flex-1 bg-zinc-900 border border-zinc-700 rounded-xl px-4 py-3 text-center text-2xl font-semibold focus:outline-none focus:border-green-500 transition-colors" />
diff --git a/src/routes/(app)/presets/+page.svelte b/src/routes/(app)/presets/+page.svelte index ea64992..388c56b 100644 --- a/src/routes/(app)/presets/+page.svelte +++ b/src/routes/(app)/presets/+page.svelte @@ -1,20 +1,20 @@
@@ -93,56 +205,266 @@ {:else}
    {#each filteredPresets as preset (preset.id)} -
  • -
    -

    {preset.name}

    -

    - {kcal(preset.calories)} kcal · P {g(preset.protein)}g · C {g(preset.carb)}g · F {g(preset.fat)}g -

    -
    - -
    - +
  • + +
    + - - - + + +
    + + + + + + + + +
    + + + {#if expandedId === preset.id} +
    + {#if preset.entries.length === 0} +

    No entries yet

    + {:else} +
      + {#each preset.entries as entry (entry.id)} +
    • + + +
    • + {/each} +
    + {/if} + + + +
    + {/if}
  • {/each}
{/if}
+ + + renamePreset_ = null} title="Rename preset"> + + + + +
+ + + editingEntry = null} title="Edit entry"> + {#if editingEntry} +
+

{editingEntry.entry.product.name}

+
+ +
+ + e.currentTarget.select()} + class="flex-1 bg-zinc-800 border border-zinc-700 rounded-xl px-4 py-2.5 text-center text-xl font-semibold focus:outline-none focus:border-green-500 transition-colors" + /> + +
+
+ +
+ {/if} +
+ + + { addEntryPresetId = null; selectedProduct = null; }} title="Add food to preset"> + {#if addEntryPresetId !== null} + {#if selectedProduct === null} + +
+ handleProductSearch(e.currentTarget.value)} + autofocus + class="w-full bg-zinc-800 border border-zinc-700 rounded-xl px-4 py-2.5 text-sm text-zinc-100 placeholder-zinc-500 focus:outline-none focus:border-green-500 transition-colors" + /> + {#if productsQuery.isPending && productDebouncedQ} +

Searching…

+ {:else} +
    + {#each productsQuery.data ?? [] as product (product.id)} +
  • + +
  • + {/each} +
+ {/if} +
+ {:else} + +
+
+

{selectedProduct.name}

+

{kcal(selectedProduct.calories)} kcal / 100g

+
+
+ +
+ + e.currentTarget.select()} + class="flex-1 bg-zinc-800 border border-zinc-700 rounded-xl px-4 py-2.5 text-center text-xl font-semibold focus:outline-none focus:border-green-500 transition-colors" + /> + +
+
+
+ + +
+
+ {/if} + {/if} +
diff --git a/src/routes/(app)/settings/+page.svelte b/src/routes/(app)/settings/+page.svelte index a62c78f..fe08990 100644 --- a/src/routes/(app)/settings/+page.svelte +++ b/src/routes/(app)/settings/+page.svelte @@ -23,7 +23,7 @@ fat_goal: number; fiber_goal: number; }>({ - calories_goal: null, + calories_goal: null as number | null, protein_goal: 0, carb_goal: 0, fat_goal: 0, @@ -33,7 +33,7 @@ $effect(() => { if (settingsQuery.data) { form = { - calories_goal: settingsQuery.data.calories_goal || null, + calories_goal: null, // always reset to auto when settings refresh protein_goal: settingsQuery.data.protein_goal, carb_goal: settingsQuery.data.carb_goal, fat_goal: settingsQuery.data.fat_goal, @@ -76,23 +76,27 @@

Daily goals

- +
-

Calories

-

kcal · leave empty to auto-calculate

+
+

Calories

+ {#if form.calories_goal === null} + auto + {/if} +
+

kcal · clear to auto-calculate

{ const v = e.currentTarget.value; form.calories_goal = v === '' ? null : Number(v); }} - class="w-24 bg-zinc-800 border border-zinc-700 rounded-lg px-3 py-2 text-right text-sm text-zinc-100 placeholder-zinc-600 focus:outline-none focus:border-green-500 transition-colors" + class="w-24 bg-zinc-800 border border-zinc-700 rounded-lg px-3 py-2 text-right text-sm text-zinc-100 focus:outline-none focus:border-green-500 transition-colors" />