From 4335b38e0202c11135f8d76fadc9f610df351745 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andr=C3=A9=20Bombeck?= Date: Sun, 9 Aug 2026 06:46:28 +0200 Subject: [PATCH 1/4] feat(nutrients): remove in-app water logging, keep the sync path Manual water entry is gone from the app: the water item on the capture picker, the dashboard quick-add menu and the hydration card's add button are removed, and the manual quick-add sheet is deleted. Water that syncs in through Apple Health still arrives and still shows on the hydration card with its daily total, history and reference line. The POST /api/nutrients/water endpoint and the batch sync path stay untouched. The nutrients page's module-enable error, which had borrowed a water-quick-add string, gets its own key across all six locales. Input-only water keys (nav.capture.water*, nutrients.hydration.quickAdd*) are removed. --- CHANGELOG.md | 4 + e2e/mobile-viewport.spec.ts | 2 +- e2e/setup/global-setup.ts | 2 +- e2e/water-capture.spec.ts | 148 ------------ messages/de.json | 17 +- messages/en.json | 17 +- messages/es.json | 17 +- messages/fr.json | 17 +- messages/it.json | 17 +- messages/pl.json | 17 +- .../success-affordance-guard.test.ts | 3 - src/app/__tests__/quick-add-labels.test.ts | 7 - src/app/insights/nutrients/page.tsx | 2 +- .../delegated-write-affordances.test.tsx | 40 +--- .../__tests__/dashboard-header.test.tsx | 28 +-- src/components/dashboard/dashboard-header.tsx | 18 +- .../dashboard/quick-entry-sheets.tsx | 21 +- .../hydration-card-display-only.test.tsx | 51 ++++ .../insights/nutrients/hydration-card.tsx | 35 +-- .../nutrients/water-quick-add-sheet.tsx | 226 ------------------ .../__tests__/capture-picker-discard.test.tsx | 83 +++---- .../layout/__tests__/capture-picker.test.tsx | 31 +-- src/components/layout/capture-picker.tsx | 54 ++--- 23 files changed, 164 insertions(+), 693 deletions(-) delete mode 100644 e2e/water-capture.spec.ts create mode 100644 src/components/insights/nutrients/__tests__/hydration-card-display-only.test.tsx delete mode 100644 src/components/insights/nutrients/water-quick-add-sheet.tsx diff --git a/CHANGELOG.md b/CHANGELOG.md index 7aecef073..27531353a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,6 +10,10 @@ - The AI consent and connector notes in Settings render as normal text now, not faint fine print, so the copy you are meant to read before turning a feature on is legible. - The respiratory-rate tile on the dashboard shows its unit in your own language instead of a fixed English label, and the personalized greeting carries its comma. +### Removed + +- Logging water by hand inside the app is gone. The water entry on the capture menu, the dashboard quick-add and the hydration card have all been removed. Water that syncs in from another app through Apple Health still arrives and still shows on the hydration card with its daily total, history and reference line. + ## [1.37.3] — 2026-08-09 ### Added diff --git a/e2e/mobile-viewport.spec.ts b/e2e/mobile-viewport.spec.ts index 1565fa86d..ac6a0a8b3 100644 --- a/e2e/mobile-viewport.spec.ts +++ b/e2e/mobile-viewport.spec.ts @@ -210,7 +210,7 @@ test.describe("mobile-viewport smoke", () => { // 1) The center capture action opens the capture picker. await page.getByTestId("bottom-nav-capture").click(); await expect(page.getByTestId("capture-picker-options")).toBeVisible(); - for (const kind of ["measurement", "medication", "mood", "water"]) { + for (const kind of ["measurement", "medication", "mood"]) { await expect(page.getByTestId(`capture-picker-${kind}`)).toBeVisible(); } // Dismiss the picker before opening the hub. diff --git a/e2e/setup/global-setup.ts b/e2e/setup/global-setup.ts index 3619bfaa7..44ac59a20 100644 --- a/e2e/setup/global-setup.ts +++ b/e2e/setup/global-setup.ts @@ -310,7 +310,7 @@ export default async function globalSetup(config: FullConfig): Promise { // out (50+ failed CI runs since v1.4.13). Specs that need the // tour can opt back in by mocking `/api/auth/me`. // Nutrients is intentionally opt-in in production. The shared fixture - // enables it because the water-capture specs exercise that real gate. + // enables it so the nutrients/hydration display specs render the card. await pool.query( `INSERT INTO users (id, username, email, password_hash, role, diff --git a/e2e/water-capture.spec.ts b/e2e/water-capture.spec.ts deleted file mode 100644 index d16fa8eb9..000000000 --- a/e2e/water-capture.spec.ts +++ /dev/null @@ -1,148 +0,0 @@ -import { expect, test } from "./setup/test"; - -import { STORAGE_STATE_PATH } from "./setup/global-setup"; -import { openMenu } from "./open-menu"; -import { - mockDashboardSnapshot, - WEIGHT_ONLY_SUMMARIES, -} from "./utils/mock-dashboard-snapshot"; - -test.describe("water capture", () => { - test.use({ storageState: STORAGE_STATE_PATH }); - - test("opens globally, waits for success, and retains failed input", async ({ - page, - }, testInfo) => { - await mockDashboardSnapshot(page, { summaries: WEIGHT_ONLY_SUMMARIES }); - await page.route(/\/api\/analytics(\?|$)/, (route) => - route.fulfill({ - status: 200, - contentType: "application/json", - body: JSON.stringify({ - data: { summaries: {}, bpInTargetPct: null, glucoseByContext: {} }, - error: null, - }), - }), - ); - await page.route("**/api/mood/analytics", (route) => - route.fulfill({ - status: 200, - contentType: "application/json", - body: JSON.stringify({ - data: { entries: [], summary: { count: 0 } }, - error: null, - }), - }), - ); - - let writeCount = 0; - let confirmSuccess: (() => void) | undefined; - const successConfirmed = new Promise((resolve) => { - confirmSuccess = resolve; - }); - await page.route("**/api/nutrients/water", async (route) => { - writeCount += 1; - if (writeCount === 1) { - await successConfirmed; - await route.fulfill({ - status: 200, - contentType: "application/json", - body: JSON.stringify({ data: { amountMl: 250 }, error: null }), - }); - return; - } - await route.fulfill({ - status: 500, - contentType: "application/json", - body: JSON.stringify({ data: null, error: "write failed" }), - }); - }); - - await page.goto("/", { waitUntil: "domcontentloaded" }); - await page.waitForLoadState("networkidle"); - - const openWater = async (toastMayOverlap = false) => { - if (testInfo.project.name === "chromium-mobile") { - const capture = page.getByTestId("bottom-nav-capture"); - if (toastMayOverlap) { - await capture.dispatchEvent("click"); - } else { - await openMenu(page, capture); - } - const option = page.getByTestId("capture-picker-water"); - await expect(option).toBeVisible(); - const box = await option.boundingBox(); - expect(box?.height ?? 0).toBeGreaterThanOrEqual(44); - if (toastMayOverlap) { - await option.dispatchEvent("click"); - } else { - await option.click(); - } - } else { - await openMenu( - page, - page.locator('[data-tour-id="dashboard-quick-add"]'), - ); - await page.getByRole("menuitem", { name: "Log water" }).click(); - } - await expect( - page.getByRole("heading", { name: "Add water" }), - ).toBeVisible(); - if (testInfo.project.name === "chromium-mobile") { - const actions = page - .getByRole("dialog") - .locator( - '[data-slot="water-quick-add-chips"] button, form button[type="submit"]', - ); - await expect(actions).toHaveCount(4); - for (const action of await actions.all()) { - await expect(action).toHaveAccessibleName(/.+/); - const actionBox = await action.boundingBox(); - expect(actionBox?.height ?? 0).toBeGreaterThanOrEqual(44); - } - } - }; - - await openWater(); - const amount = page.getByRole("spinbutton", { - name: "Custom amount (mL)", - }); - await amount.fill("250"); - const submit = page - .getByRole("dialog") - .getByRole("button", { name: "Add", exact: true }); - await amount.press("Enter"); - - await expect(submit).toBeDisabled(); - await expect(amount).toHaveValue("250"); - await expect(page.getByText("250 mL water added.")).toHaveCount(0); - - confirmSuccess?.(); - await expect(page.getByText("250 mL water added.")).toBeVisible(); - await expect(page.getByRole("heading", { name: "Add water" })).toHaveCount( - 0, - ); - expect(writeCount).toBe(1); - - await openWater(true); - const failedAmount = page.getByRole("spinbutton", { - name: "Custom amount (mL)", - }); - await expect(failedAmount).toHaveValue(""); - await failedAmount.fill("375"); - await page - .getByRole("dialog") - .getByRole("button", { name: "Add", exact: true }) - .click(); - - await expect( - page.getByText("Couldn't save that — try again."), - ).toBeVisible(); - await expect(failedAmount).toHaveValue("375"); - await expect( - page.getByRole("heading", { name: "Add water" }), - ).toBeVisible(); - await expect(page.getByText("375 mL water added.")).toHaveCount(0); - expect(writeCount).toBe(2); - }); -}); diff --git a/messages/de.json b/messages/de.json index d0f074f51..2ff4e1815 100644 --- a/messages/de.json +++ b/messages/de.json @@ -349,9 +349,7 @@ "medication": "Medikament", "medicationDescription": "Eine Dosis als genommen markieren", "mood": "Stimmung", - "moodDescription": "Stimmung und Tags erfassen", - "water": "Wasser", - "waterDescription": "Wasseraufnahme schnell hinzufügen" + "moodDescription": "Stimmung und Tags erfassen" }, "cycle": "Zyklus", "labs": "Laborwerte", @@ -455,7 +453,6 @@ "quickAddMeasurement": "Messung erfassen", "quickAddMood": "Stimmung erfassen", "quickAddMedicationIntake": "Einnahme erfassen", - "quickAddWater": "Wasser erfassen", "customizeDashboard": "Dashboard anpassen", "medicationIntakeQuickAdd": { "sheetTitle": "Medikamenteneinnahme erfassen", @@ -9654,17 +9651,11 @@ "dayInvalid": "Die Einträge trugen ein Datum, das der Server nicht akzeptieren konnte. Das kann passieren, wenn Uhrzeit oder Zeitzone auf der sendenden Seite falsch eingestellt sind — das lohnt sich zu prüfen.", "upsertFailed": "Diese Einträge haben den Server erreicht, aber ein Problem auf unserer Seite hat verhindert, dass sie gespeichert wurden. Du musst nichts weiter tun.", "unknown": "Diese Einträge sind angekommen, konnten aber nicht gespeichert werden." - } + }, + "moduleEnableError": "Das Nährstoffmodul konnte nicht aktiviert werden. Versuch es noch mal." }, "hydration": { - "referenceMeta": "Richtwert {value} {unit}/Tag · EFSA", - "quickAddTitle": "Wasser hinzufügen", - "quickAddCustomLabel": "Eigene Menge (ml)", - "quickAddCustomPlaceholder": "250", - "quickAddSubmit": "Hinzufügen", - "quickAddError": "Konnte nicht gespeichert werden — versuch's noch mal.", - "quickAddSuccess": "{amount} ml Wasser hinzugefügt.", - "editTotal": "Heutige Summe bearbeiten" + "referenceMeta": "Richtwert {value} {unit}/Tag · EFSA" }, "caffeine": { "ceilingMeta": "Sichere Obergrenze {value} {unit}/Tag · EFSA" diff --git a/messages/en.json b/messages/en.json index 75193bb2c..8d9ee2611 100644 --- a/messages/en.json +++ b/messages/en.json @@ -349,9 +349,7 @@ "medication": "Medication", "medicationDescription": "Mark a dose as taken", "mood": "Mood", - "moodDescription": "Log your mood and tags", - "water": "Water", - "waterDescription": "Quickly add your water intake" + "moodDescription": "Log your mood and tags" }, "cycle": "Cycle", "labs": "Labs", @@ -455,7 +453,6 @@ "quickAddMeasurement": "Log measurement", "quickAddMood": "Log mood", "quickAddMedicationIntake": "Log medication intake", - "quickAddWater": "Log water", "customizeDashboard": "Customize dashboard", "medicationIntakeQuickAdd": { "sheetTitle": "Log medication intake", @@ -9654,17 +9651,11 @@ "dayInvalid": "The entries carried a date the server couldn't accept. This can happen when the clock or time zone on the sending side is wrong — it's worth checking.", "upsertFailed": "These entries reached the server, but a problem on this end kept them from being saved. There's nothing you need to do.", "unknown": "These entries arrived but couldn't be stored." - } + }, + "moduleEnableError": "The nutrients module couldn't be turned on. Please try again." }, "hydration": { - "referenceMeta": "Reference intake {value} {unit}/day · EFSA", - "quickAddTitle": "Add water", - "quickAddCustomLabel": "Custom amount (mL)", - "quickAddCustomPlaceholder": "250", - "quickAddSubmit": "Add", - "quickAddError": "Couldn't save that — try again.", - "quickAddSuccess": "{amount} mL water added.", - "editTotal": "Edit today's total" + "referenceMeta": "Reference intake {value} {unit}/day · EFSA" }, "caffeine": { "ceilingMeta": "Safe-level ceiling {value} {unit}/day · EFSA" diff --git a/messages/es.json b/messages/es.json index 0ba944055..4d0782b1c 100644 --- a/messages/es.json +++ b/messages/es.json @@ -349,9 +349,7 @@ "medication": "Medicamento", "medicationDescription": "Marcar una dosis como tomada", "mood": "Estado de ánimo", - "moodDescription": "Registra tu estado de ánimo y etiquetas", - "water": "Agua", - "waterDescription": "Añade rápidamente tu consumo de agua" + "moodDescription": "Registra tu estado de ánimo y etiquetas" }, "cycle": "Ciclo", "labs": "Análisis", @@ -455,7 +453,6 @@ "quickAddMeasurement": "Registrar medición", "quickAddMood": "Registrar estado de ánimo", "quickAddMedicationIntake": "Registrar toma", - "quickAddWater": "Registrar agua", "customizeDashboard": "Personalizar panel", "medicationIntakeQuickAdd": { "sheetTitle": "Registrar toma de medicación", @@ -9654,17 +9651,11 @@ "dayInvalid": "Las entradas llevaban una fecha que el servidor no pudo aceptar. Esto puede ocurrir cuando el reloj o la zona horaria del lado que envía los datos están mal configurados; merece la pena revisarlo.", "upsertFailed": "Estas entradas llegaron al servidor, pero un problema de nuestro lado impidió guardarlas. No tienes que hacer nada.", "unknown": "Estas entradas llegaron, pero no se pudieron guardar." - } + }, + "moduleEnableError": "No se pudo activar el módulo de nutrientes. Inténtalo de nuevo." }, "hydration": { - "referenceMeta": "Ingesta de referencia {value} {unit}/día · EFSA", - "quickAddTitle": "Añadir agua", - "quickAddCustomLabel": "Cantidad personalizada (mL)", - "quickAddCustomPlaceholder": "250", - "quickAddSubmit": "Añadir", - "quickAddError": "No se pudo guardar. Inténtalo de nuevo.", - "quickAddSuccess": "Se añadieron {amount} mL de agua.", - "editTotal": "Editar el total de hoy" + "referenceMeta": "Ingesta de referencia {value} {unit}/día · EFSA" }, "caffeine": { "ceilingMeta": "Límite de seguridad {value} {unit}/día · EFSA" diff --git a/messages/fr.json b/messages/fr.json index 4f8344eb7..8ddc304d6 100644 --- a/messages/fr.json +++ b/messages/fr.json @@ -349,9 +349,7 @@ "medication": "Médicament", "medicationDescription": "Marquer une dose comme prise", "mood": "Humeur", - "moodDescription": "Enregistrez votre humeur et des étiquettes", - "water": "Eau", - "waterDescription": "Ajoutez rapidement votre consommation d’eau" + "moodDescription": "Enregistrez votre humeur et des étiquettes" }, "cycle": "Cycle", "labs": "Analyses", @@ -455,7 +453,6 @@ "quickAddMeasurement": "Saisir une mesure", "quickAddMood": "Saisir une humeur", "quickAddMedicationIntake": "Saisir une prise", - "quickAddWater": "Saisir de l’eau", "customizeDashboard": "Personnaliser le tableau de bord", "medicationIntakeQuickAdd": { "sheetTitle": "Saisir une prise de médicament", @@ -9654,17 +9651,11 @@ "dayInvalid": "Les entrées portaient une date que le serveur n'a pas pu accepter. Cela peut arriver quand l'horloge ou le fuseau horaire du côté qui envoie les données est incorrect — cela vaut la peine de vérifier.", "upsertFailed": "Ces entrées sont arrivées jusqu'au serveur, mais un problème de notre côté a empêché leur enregistrement. Tu n'as rien à faire.", "unknown": "Ces entrées sont arrivées mais n'ont pas pu être enregistrées." - } + }, + "moduleEnableError": "Le module nutriments n'a pas pu être activé. Réessaie." }, "hydration": { - "referenceMeta": "Apport de référence {value} {unit}/jour · EFSA", - "quickAddTitle": "Ajouter de l'eau", - "quickAddCustomLabel": "Quantité personnalisée (mL)", - "quickAddCustomPlaceholder": "250", - "quickAddSubmit": "Ajouter", - "quickAddError": "Échec de l'enregistrement — réessayez.", - "quickAddSuccess": "{amount} mL d’eau ajoutés.", - "editTotal": "Modifier le total du jour" + "referenceMeta": "Apport de référence {value} {unit}/jour · EFSA" }, "caffeine": { "ceilingMeta": "Plafond de sécurité {value} {unit}/jour · EFSA" diff --git a/messages/it.json b/messages/it.json index 09ab45195..b0cf8492f 100644 --- a/messages/it.json +++ b/messages/it.json @@ -349,9 +349,7 @@ "medication": "Farmaco", "medicationDescription": "Segna una dose come assunta", "mood": "Umore", - "moodDescription": "Registra umore e tag", - "water": "Acqua", - "waterDescription": "Aggiungi rapidamente l’acqua bevuta" + "moodDescription": "Registra umore e tag" }, "cycle": "Ciclo", "labs": "Esami", @@ -455,7 +453,6 @@ "quickAddMeasurement": "Registra misurazione", "quickAddMood": "Registra umore", "quickAddMedicationIntake": "Registra assunzione", - "quickAddWater": "Registra acqua", "customizeDashboard": "Personalizza dashboard", "medicationIntakeQuickAdd": { "sheetTitle": "Registra assunzione del farmaco", @@ -9654,17 +9651,11 @@ "dayInvalid": "Le voci riportavano una data che il server non ha potuto accettare. Questo può succedere quando l'orologio o il fuso orario dal lato che invia i dati non sono corretti: vale la pena controllare.", "upsertFailed": "Queste voci hanno raggiunto il server, ma un problema da questa parte ne ha impedito il salvataggio. Non devi fare nulla.", "unknown": "Queste voci sono arrivate ma non è stato possibile salvarle." - } + }, + "moduleEnableError": "Non è stato possibile attivare il modulo nutrienti. Riprova." }, "hydration": { - "referenceMeta": "Apporto di riferimento {value} {unit}/giorno · EFSA", - "quickAddTitle": "Aggiungi acqua", - "quickAddCustomLabel": "Quantità personalizzata (mL)", - "quickAddCustomPlaceholder": "250", - "quickAddSubmit": "Aggiungi", - "quickAddError": "Salvataggio non riuscito. Riprova.", - "quickAddSuccess": "Aggiunti {amount} mL di acqua.", - "editTotal": "Modifica il totale di oggi" + "referenceMeta": "Apporto di riferimento {value} {unit}/giorno · EFSA" }, "caffeine": { "ceilingMeta": "Limite di sicurezza {value} {unit}/giorno · EFSA" diff --git a/messages/pl.json b/messages/pl.json index 473e7c6fa..d8bf1893d 100644 --- a/messages/pl.json +++ b/messages/pl.json @@ -349,9 +349,7 @@ "medication": "Lek", "medicationDescription": "Oznacz dawkę jako przyjętą", "mood": "Nastrój", - "moodDescription": "Zapisz nastrój i tagi", - "water": "Woda", - "waterDescription": "Szybko dodaj ilość wypitej wody" + "moodDescription": "Zapisz nastrój i tagi" }, "cycle": "Cykl", "labs": "Wyniki badań", @@ -455,7 +453,6 @@ "quickAddMeasurement": "Zarejestruj pomiar", "quickAddMood": "Zarejestruj nastrój", "quickAddMedicationIntake": "Zarejestruj przyjęcie", - "quickAddWater": "Zapisz wodę", "customizeDashboard": "Dostosuj pulpit", "medicationIntakeQuickAdd": { "sheetTitle": "Zarejestruj przyjęcie leku", @@ -9654,17 +9651,11 @@ "dayInvalid": "Wpisy zawierały datę, której serwer nie mógł zaakceptować. Może się to zdarzyć, gdy zegar lub strefa czasowa po stronie wysyłającej są nieprawidłowe — warto to sprawdzić.", "upsertFailed": "Te wpisy dotarły do serwera, ale problem po naszej stronie uniemożliwił ich zapisanie. Nie musisz nic robić.", "unknown": "Te wpisy dotarły, ale nie udało się ich zapisać." - } + }, + "moduleEnableError": "Nie udało się włączyć modułu składników odżywczych. Spróbuj ponownie." }, "hydration": { - "referenceMeta": "Zalecane spożycie {value} {unit}/dzień · EFSA", - "quickAddTitle": "Dodaj wodę", - "quickAddCustomLabel": "Własna ilość (mL)", - "quickAddCustomPlaceholder": "250", - "quickAddSubmit": "Dodaj", - "quickAddError": "Nie udało się zapisać — spróbuj ponownie.", - "quickAddSuccess": "Dodano {amount} mL wody.", - "editTotal": "Edytuj dzisiejszą sumę" + "referenceMeta": "Zalecane spożycie {value} {unit}/dzień · EFSA" }, "caffeine": { "ceilingMeta": "Bezpieczny limit {value} {unit}/dzień · EFSA" diff --git a/src/__tests__/success-affordance-guard.test.ts b/src/__tests__/success-affordance-guard.test.ts index 3114e8a16..a7a984d9e 100644 --- a/src/__tests__/success-affordance-guard.test.ts +++ b/src/__tests__/success-affordance-guard.test.ts @@ -193,9 +193,6 @@ const PINNED_AFFORDANCES: Record< "src/components/insights/mood/mood-tag-metric-crosstab.tsx": { "text-success": 1, }, - "src/components/insights/nutrients/water-quick-add-sheet.tsx": { - "toast.success": 2, - }, "src/components/insights/personal-record-badge.tsx": { "text-success": 1 }, "src/components/insights/recommendation-feedback.tsx": { "text-success": 3 }, "src/components/labs/biomarker-form.tsx": { "toast.success": 1 }, diff --git a/src/app/__tests__/quick-add-labels.test.ts b/src/app/__tests__/quick-add-labels.test.ts index 1d24e4b70..24d04508d 100644 --- a/src/app/__tests__/quick-add-labels.test.ts +++ b/src/app/__tests__/quick-add-labels.test.ts @@ -40,7 +40,6 @@ interface Messages { * Hinzufügen, Hinzufügen, Hinzufügen" with no way to discriminate. */ quickAddMedicationIntake: string; - quickAddWater: string; }; } @@ -69,23 +68,18 @@ describe("dashboard quick-add submenu labels", () => { const measurement = messages.dashboard.quickAddMeasurement; const mood = messages.dashboard.quickAddMood; const medicationIntake = messages.dashboard.quickAddMedicationIntake; - const water = messages.dashboard.quickAddWater; const trigger = messages.common.add; // Non-empty expect(measurement.trim().length).toBeGreaterThan(0); expect(mood.trim().length).toBeGreaterThan(0); expect(medicationIntake.trim().length).toBeGreaterThan(0); - expect(water.trim().length).toBeGreaterThan(0); // Distinct from each other — the icon is decorative (aria-hidden), // so the only thing distinguishing the rows is the visible text. expect(measurement).not.toBe(mood); expect(measurement).not.toBe(medicationIntake); expect(mood).not.toBe(medicationIntake); - expect(measurement).not.toBe(water); - expect(mood).not.toBe(water); - expect(medicationIntake).not.toBe(water); // Distinct from the trigger label. The trigger sits ABOVE the menu // and announces itself first; if a menu item then repeats the same @@ -93,7 +87,6 @@ describe("dashboard quick-add submenu labels", () => { expect(measurement).not.toBe(trigger); expect(mood).not.toBe(trigger); expect(medicationIntake).not.toBe(trigger); - expect(water).not.toBe(trigger); }, ); }); diff --git a/src/app/insights/nutrients/page.tsx b/src/app/insights/nutrients/page.tsx index 0790865e5..7e8b03008 100644 --- a/src/app/insights/nutrients/page.tsx +++ b/src/app/insights/nutrients/page.tsx @@ -100,7 +100,7 @@ export default function InsightsNutrientsPage() { onSuccess: () => { void queryClient.invalidateQueries({ queryKey: queryKeys.authMe() }); }, - onError: () => toast.error(t("nutrients.hydration.quickAddError")), + onError: () => toast.error(t("nutrients.page.moduleEnableError")), }); if (!nutrientsEnabled) { diff --git a/src/components/__tests__/delegated-write-affordances.test.tsx b/src/components/__tests__/delegated-write-affordances.test.tsx index c8a14fd45..9e800346c 100644 --- a/src/components/__tests__/delegated-write-affordances.test.tsx +++ b/src/components/__tests__/delegated-write-affordances.test.tsx @@ -369,33 +369,27 @@ describe("linking a document to an illness episode", () => { }); describe("the capture picker's kinds", () => { - const ALL = ["measurement", "medication", "mood", "water"] as const; + const ALL = ["measurement", "medication", "mood"] as const; it("offers everything in the caller's own record", () => { expect( - visibleCaptureKinds({ canAdd: true, canManage: true }, true, [...ALL]), - ).toEqual(["measurement", "medication", "mood", "water"]); + visibleCaptureKinds({ canAdd: true, canManage: true }, [...ALL]), + ).toEqual(["measurement", "medication", "mood"]); }); it("offers a delegate only what the delegation admits", () => { - // A reading and a dose are admitted verbs. A mood entry and a glass of - // water are not, and the server refuses them under a switch. + // A reading and a dose are admitted verbs. A mood entry is not, and the + // server refuses it under a switch. expect( - visibleCaptureKinds({ canAdd: true, canManage: false }, true, [...ALL]), + visibleCaptureKinds({ canAdd: true, canManage: false }, [...ALL]), ).toEqual(["measurement", "medication"]); }); it("offers a read-only delegate nothing", () => { expect( - visibleCaptureKinds({ canAdd: false, canManage: false }, true, [...ALL]), + visibleCaptureKinds({ canAdd: false, canManage: false }, [...ALL]), ).toEqual([]); }); - - it("still honours the module gate for the owner", () => { - expect( - visibleCaptureKinds({ canAdd: true, canManage: true }, false, [...ALL]), - ).toEqual(["measurement", "medication", "mood"]); - }); }); /* -------------------------------------------------------------------------- */ @@ -471,13 +465,10 @@ describe("a form opened before the record answered", () => { * top of the file applies. */ it("withdraws a capture form the shrunken offer no longer holds", () => { - // The chooser offered four; the answer arrives and offers two. + // The chooser offered three; the answer arrives and offers two. expect(admittedCaptureKind("mood", ["measurement", "medication"])).toBe( null, ); - expect(admittedCaptureKind("water", ["measurement", "medication"])).toBe( - null, - ); // …and leaves an admitted one exactly where it was. expect( admittedCaptureKind("medication", ["measurement", "medication"]), @@ -488,7 +479,6 @@ describe("a form opened before the record answered", () => { it("withdraws a dashboard quick-entry sheet the delegation does not admit", () => { const DELEGATE = { canAdd: true, canManage: false }; expect(admittedQuickEntry("mood", DELEGATE)).toBe(null); - expect(admittedQuickEntry("water", DELEGATE)).toBe(null); expect(admittedQuickEntry("measurement", DELEGATE)).toBe("measurement"); expect(admittedQuickEntry("medicationIntake", DELEGATE)).toBe( "medicationIntake", @@ -497,24 +487,14 @@ describe("a form opened before the record answered", () => { it("withdraws every quick-entry sheet from a read-only delegate", () => { const READER = { canAdd: false, canManage: false }; - for (const sheet of [ - "measurement", - "mood", - "medicationIntake", - "water", - ] as const) { + for (const sheet of ["measurement", "mood", "medicationIntake"] as const) { expect(admittedQuickEntry(sheet, READER), sheet).toBe(null); } }); it("leaves the owner's own sheets alone", () => { const OWNER_CAPS = { canAdd: true, canManage: true }; - for (const sheet of [ - "measurement", - "mood", - "medicationIntake", - "water", - ] as const) { + for (const sheet of ["measurement", "mood", "medicationIntake"] as const) { expect(admittedQuickEntry(sheet, OWNER_CAPS), sheet).toBe(sheet); } expect(admittedQuickEntry(null, OWNER_CAPS)).toBe(null); diff --git a/src/components/dashboard/__tests__/dashboard-header.test.tsx b/src/components/dashboard/__tests__/dashboard-header.test.tsx index 14cd4c252..4f7ae8422 100644 --- a/src/components/dashboard/__tests__/dashboard-header.test.tsx +++ b/src/components/dashboard/__tests__/dashboard-header.test.tsx @@ -16,13 +16,6 @@ import { renderToStaticMarkup } from "react-dom/server"; import { I18nProvider } from "@/lib/i18n/context"; -const moduleGate = vi.hoisted(() => ({ nutrientsEnabled: true })); - -vi.mock("@/hooks/use-module-enabled", () => ({ - useModuleEnabled: (moduleKey: string) => - moduleKey === "nutrients" ? moduleGate.nutrientsEnabled : true, -})); - vi.mock("@/components/ui/dropdown-menu", () => ({ DropdownMenu: ({ children }: { children: React.ReactNode }) => ( <>{children} @@ -77,29 +70,18 @@ describe(" — greeting", () => { }); }); -describe(" — nutrients module gate", () => { - it("hides only the water quick-add item when nutrients are disabled", () => { - moduleGate.nutrientsEnabled = false; - +describe(" — quick-add menu", () => { + it("offers measurement, mood and medication, and no water entry", () => { const html = renderSSR( undefined} />, "en", ); - expect(html).not.toContain("Log water"); expect(html).toContain("Log measurement"); expect(html).toContain("Log mood"); expect(html).toContain("Log medication intake"); - }); - - it("keeps the water quick-add item available when nutrients are enabled", () => { - moduleGate.nutrientsEnabled = true; - - const html = renderSSR( - undefined} />, - "en", - ); - - expect(html).toContain("Log water"); + // Water logging was removed from the app; the dashboard quick-add offers + // no water entry. + expect(html).not.toContain("Log water"); }); }); diff --git a/src/components/dashboard/dashboard-header.tsx b/src/components/dashboard/dashboard-header.tsx index 868359f06..e502f9456 100644 --- a/src/components/dashboard/dashboard-header.tsx +++ b/src/components/dashboard/dashboard-header.tsx @@ -12,7 +12,7 @@ */ import { useMemo } from "react"; import Link from "next/link"; -import { Activity, GlassWater, Pill, Plus, Waves, Wrench } from "lucide-react"; +import { Activity, Pill, Plus, Waves, Wrench } from "lucide-react"; import { Button } from "@/components/ui/button"; import { DropdownMenu, @@ -24,7 +24,6 @@ import { PageHeader } from "@/components/ui/page-header"; import { useTranslations } from "@/lib/i18n/context"; import { useAuth } from "@/hooks/use-auth"; import { useMounted } from "@/hooks/use-mounted"; -import { useModuleEnabled } from "@/hooks/use-module-enabled"; import { useRecordCapabilities } from "@/hooks/use-record-capabilities"; import { getHourForTimeZone } from "@/components/dashboard/range-display"; import type { QuickEntryDialog } from "@/components/dashboard/quick-entry-sheets"; @@ -37,11 +36,10 @@ export function DashboardHeader({ const { t } = useTranslations(); const { user } = useAuth(); const mounted = useMounted(); - const nutrientsEnabled = useModuleEnabled("nutrients"); - // v1.36.x — the dashboard quick-add offers four kinds; a delegation admits - // two of them (a reading, a dose). Mood and water stay with the account - // whose record it is, and the customize shortcut points at a settings page - // sharing does not cover at all. + // v1.36.x — the dashboard quick-add offers three kinds; a delegation admits + // two of them (a reading, a dose). Mood stays with the account whose record + // it is, and the customize shortcut points at a settings page sharing does + // not cover at all. const { canAdd, canManage } = useRecordCapabilities(); // The pre-hero greeting derivation, kept hydration-safe: `user` comes @@ -166,12 +164,6 @@ export function DashboardHeader({