diff --git a/package.json b/package.json index 62bbbbf..4a05e5c 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "homepage", - "version": "1.11.0", + "version": "1.11.1", "private": true, "type": "module", "scripts": { diff --git a/src/app.css b/src/app.css index 793b3a0..9df09e5 100644 --- a/src/app.css +++ b/src/app.css @@ -272,6 +272,16 @@ font-family: Helvetica, Arial, "Noto Sans", sans-serif; } +input[type="number"]::-webkit-inner-spin-button, +input[type="number"]::-webkit-outer-spin-button { + -webkit-appearance: none; + margin: 0; +} +input[type="number"] { + -moz-appearance: textfield; + appearance: textfield; +} + body { margin: 0; padding: 0; diff --git a/src/lib/components/fitness/FoodSearch.svelte b/src/lib/components/fitness/FoodSearch.svelte index bc9d3ba..a4e54bf 100644 --- a/src/lib/components/fitness/FoodSearch.svelte +++ b/src/lib/components/fitness/FoodSearch.svelte @@ -357,13 +357,18 @@
+ {#if query} + + {/if} {#if browser} + +
@@ -293,8 +297,33 @@ } .profile-fields { display: flex; - gap: 0.75rem; + gap: 0.5rem; align-items: flex-end; + justify-content: flex-end; + flex-wrap: wrap; + } + .sex-pills { + display: flex; + gap: 0.4rem; + } + .sex-pill { + display: flex; + align-items: center; + gap: 0.3rem; + padding: 0.35rem 0.65rem; + border: 1px solid var(--color-border); + border-radius: 20px; + background: var(--color-bg-tertiary); + color: var(--color-text-secondary); + font-size: 0.8rem; + font-weight: 500; + cursor: pointer; + transition: all 150ms; + } + .sex-pill.active { + background: var(--color-primary); + border-color: var(--color-primary); + color: var(--color-text-on-primary); } .profile-save-btn { padding: 0.4rem 0.75rem; @@ -315,11 +344,11 @@ } .form-group { - flex: 1; display: flex; flex-direction: column; gap: 0.2rem; margin-bottom: 0.4rem; + flex-shrink: 0; } .form-group label { font-size: 0.7rem; diff --git a/src/routes/fitness/[nutrition=fitnessNutrition]/+page.svelte b/src/routes/fitness/[nutrition=fitnessNutrition]/+page.svelte index 9b09fa5..6672568 100644 --- a/src/routes/fitness/[nutrition=fitnessNutrition]/+page.svelte +++ b/src/routes/fitness/[nutrition=fitnessNutrition]/+page.svelte @@ -468,6 +468,7 @@ // --- Inline add food --- let addingMeal = $state(null); + let inlineTab = $state('search'); // 'search' | 'meals' // --- FAB modal (route-based via ?add param) --- const showFabModal = $derived($page.url.searchParams.has('add')); @@ -588,12 +589,57 @@ function startAdd(meal) { addingMeal = meal; + inlineTab = 'search'; + loadCustomMeals(); } function cancelAdd() { addingMeal = null; } + async function inlineLogCustomMeal(meal) { + if (!addingMeal) return; + try { + const totals = {}; + const nutrientKeys = ['calories', 'protein', 'fat', 'saturatedFat', 'carbs', 'fiber', 'sugars', + 'calcium', 'iron', 'magnesium', 'phosphorus', 'potassium', 'sodium', 'zinc', + 'vitaminA', 'vitaminC', 'vitaminD', 'vitaminE', 'vitaminK', + 'thiamin', 'riboflavin', 'niacin', 'vitaminB6', 'vitaminB12', 'folate', 'cholesterol', + 'isoleucine', 'leucine', 'lysine', 'methionine', 'phenylalanine', 'threonine', + 'tryptophan', 'valine', 'histidine', 'alanine', 'arginine', 'asparticAcid', + 'cysteine', 'glutamicAcid', 'glycine', 'proline', 'serine', 'tyrosine']; + for (const k of nutrientKeys) totals[k] = 0; + let totalGrams = 0; + for (const ing of meal.ingredients) { + const r = ing.amountGrams / 100; + totalGrams += ing.amountGrams; + for (const k of nutrientKeys) totals[k] += (ing.per100g?.[k] ?? 0) * r; + } + const per100g = {}; + const scale = totalGrams > 0 ? 100 / totalGrams : 0; + for (const k of nutrientKeys) per100g[k] = totals[k] * scale; + + await fetch('/api/fitness/food-log', { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify({ + date: currentDate, + mealType: addingMeal, + name: meal.name, + source: 'custom', + sourceId: meal._id, + amountGrams: totalGrams, + per100g, + }) + }); + await goto(`/fitness/${s.nutrition}?date=${currentDate}`, { replaceState: true, noScroll: true }); + cancelAdd(); + toast.success(isEn ? `Logged "${meal.name}"` : `"${meal.name}" eingetragen`); + } catch { + toast.error(isEn ? 'Failed to log meal' : 'Fehler beim Eintragen'); + } + } + async function inlineLogFood(food) { try { const res = await fetch('/api/fitness/food-log', { @@ -776,14 +822,21 @@ - 0} + + {/if} + {#if calorieOverflow > 0} {/if} {fmtCal(Math.abs(calorieBalance))} @@ -1241,7 +1294,41 @@ {#if addingMeal === meal}
- +
+
+ + +
+ +
+ + {#if inlineTab === 'search'} + + {:else} +
+ {#if customMeals.length === 0} +

{t('no_custom_meals', lang)}

+ {/if} + {#each customMeals as cm} +
+
+ {cm.name} + {cm.ingredients.length} {t('ingredients', lang)} · {fmtCal(mealTotalCal(cm))} kcal +
+ +
+ {/each} + + + {isEn ? 'Manage meals' : 'Mahlzeiten verwalten'} + +
+ {/if}
{:else}