From 02642a964e87af66459c437ba502f2fdcbca8d19 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andr=C3=A9=20Bombeck?= Date: Fri, 12 Jun 2026 19:35:23 +0200 Subject: [PATCH] feat(medications): list customization moves into its own settings section MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit /settings/medications now hosts the view preference (cards or table) and the manual-order editor inline, following the dashboard and insights pattern. The page header's gear icon links there instead of opening a dialog; the quick view toggle stays next to the add button. The order editor keeps its grouped active/inactive semantics — a saved order can never interleave the groups. --- messages/de.json | 8 +- messages/en.json | 8 +- messages/es.json | 8 +- messages/fr.json | 8 +- messages/it.json | 8 +- messages/pl.json | 8 +- .../__tests__/list-view-toggle.test.tsx | 13 +- src/app/medications/page.tsx | 69 +++------ src/app/settings/[section]/page.tsx | 2 + .../medication-order-editor.test.tsx | 73 ++++----- ...dialog.tsx => medication-order-editor.tsx} | 141 ++++++++---------- .../__tests__/medications-section.test.tsx | 122 +++++++++++++++ .../__tests__/settings-shell.test.tsx | 11 ++ .../settings/medications-section.tsx | 129 ++++++++++++++++ .../settings/section-placeholder.tsx | 2 + src/components/settings/section-slugs.ts | 5 + src/components/settings/settings-shell.tsx | 6 + 17 files changed, 441 insertions(+), 180 deletions(-) rename src/components/medications/{medication-reorder-dialog.tsx => medication-order-editor.tsx} (79%) create mode 100644 src/components/settings/__tests__/medications-section.test.tsx create mode 100644 src/components/settings/medications-section.tsx diff --git a/messages/de.json b/messages/de.json index 9930facb5..3dcafb78a 100644 --- a/messages/de.json +++ b/messages/de.json @@ -1718,6 +1718,7 @@ "allowedSitesLabel": "Erlaubte Stellen", "allowedSitesHint": "Alle ausgewählt lassen für keine Einschränkung. Global ausgeschlossene Stellen werden nie angeboten.", "viewToggleLabel": "Ansicht", + "viewToggleHint": "Wähle, ob deine Medikamente als Karten oder als Tabelle angezeigt werden.", "viewCards": "Kartenansicht", "viewTable": "Tabellenansicht", "viewSaveFailed": "Ansicht konnte nicht gespeichert werden", @@ -1737,7 +1738,8 @@ "reorderTitle": "Reihenfolge anpassen", "reorderDescription": "Bestimme die Reihenfolge deiner Medikamente – sie gilt für die Karten- und die Tabellenansicht.", "reorderSaved": "Reihenfolge gespeichert", - "reorderSaveFailed": "Reihenfolge konnte nicht gespeichert werden" + "reorderSaveFailed": "Reihenfolge konnte nicht gespeichert werden", + "customize": "Medikamente anpassen" }, "charts": { "loadingLabel": "Diagramm wird geladen…", @@ -3767,6 +3769,10 @@ "insights": { "title": "Insights", "description": "Reihenfolge der Übersicht und der Navigations-Pills." + }, + "medications": { + "title": "Medikamente", + "description": "Listenansicht und Reihenfolge der Medikamente." } }, "profile": "Profil", diff --git a/messages/en.json b/messages/en.json index f5927bd8b..a9ea8f561 100644 --- a/messages/en.json +++ b/messages/en.json @@ -1718,6 +1718,7 @@ "allowedSitesLabel": "Allowed sites", "allowedSitesHint": "Leave all selected for no restriction. Sites you excluded globally are never offered.", "viewToggleLabel": "View", + "viewToggleHint": "Choose whether your medications appear as cards or as a table.", "viewCards": "Card view", "viewTable": "Table view", "viewSaveFailed": "View could not be saved", @@ -1737,7 +1738,8 @@ "reorderTitle": "Adjust order", "reorderDescription": "Set the order of your medications – it applies to both the card and the table view.", "reorderSaved": "Order saved", - "reorderSaveFailed": "Order could not be saved" + "reorderSaveFailed": "Order could not be saved", + "customize": "Customize medications" }, "charts": { "loadingLabel": "Loading chart…", @@ -3767,6 +3769,10 @@ "insights": { "title": "Insights", "description": "Overview and navigation-pill order." + }, + "medications": { + "title": "Medications", + "description": "List view and medication order." } }, "profile": "Profile", diff --git a/messages/es.json b/messages/es.json index 514b42a15..63018e39e 100644 --- a/messages/es.json +++ b/messages/es.json @@ -1718,6 +1718,7 @@ "allowedSitesLabel": "Sitios permitidos", "allowedSitesHint": "Deja todos seleccionados para no aplicar restricciones. Los sitios que excluiste globalmente nunca se ofrecen.", "viewToggleLabel": "Vista", + "viewToggleHint": "Elige si tus medicamentos se muestran como tarjetas o como tabla.", "viewCards": "Vista de tarjetas", "viewTable": "Vista de tabla", "viewSaveFailed": "No se pudo guardar la vista", @@ -1737,7 +1738,8 @@ "reorderTitle": "Ajustar orden", "reorderDescription": "Define el orden de tus medicamentos: se aplica a la vista de tarjetas y a la de tabla.", "reorderSaved": "Orden guardado", - "reorderSaveFailed": "No se pudo guardar el orden" + "reorderSaveFailed": "No se pudo guardar el orden", + "customize": "Personalizar medicamentos" }, "charts": { "loadingLabel": "Cargando la gráfica…", @@ -3767,6 +3769,10 @@ "insights": { "title": "Análisis", "description": "Orden de la vista general y de las pestañas de navegación." + }, + "medications": { + "title": "Medicamentos", + "description": "Vista de lista y orden de los medicamentos." } }, "profile": "Perfil", diff --git a/messages/fr.json b/messages/fr.json index 68b82779e..9e24dfa09 100644 --- a/messages/fr.json +++ b/messages/fr.json @@ -1718,6 +1718,7 @@ "allowedSitesLabel": "Sites autorisés", "allowedSitesHint": "Laissez tout sélectionné pour aucune restriction. Les sites exclus globalement ne sont jamais proposés.", "viewToggleLabel": "Affichage", + "viewToggleHint": "Choisis si tes médicaments s'affichent sous forme de cartes ou de tableau.", "viewCards": "Vue cartes", "viewTable": "Vue tableau", "viewSaveFailed": "L'affichage n'a pas pu être enregistré", @@ -1737,7 +1738,8 @@ "reorderTitle": "Modifier l'ordre", "reorderDescription": "Définis l'ordre de tes médicaments – il s'applique à la vue cartes et à la vue tableau.", "reorderSaved": "Ordre enregistré", - "reorderSaveFailed": "L'ordre n'a pas pu être enregistré" + "reorderSaveFailed": "L'ordre n'a pas pu être enregistré", + "customize": "Personnaliser les médicaments" }, "charts": { "loadingLabel": "Chargement du graphique…", @@ -3767,6 +3769,10 @@ "insights": { "title": "Analyses", "description": "Ordre de la vue d'ensemble et des onglets de navigation." + }, + "medications": { + "title": "Médicaments", + "description": "Vue de la liste et ordre des médicaments." } }, "profile": "Profil", diff --git a/messages/it.json b/messages/it.json index 7290f0b4e..692bf94fe 100644 --- a/messages/it.json +++ b/messages/it.json @@ -1718,6 +1718,7 @@ "allowedSitesLabel": "Siti consentiti", "allowedSitesHint": "Lascia tutti selezionati per nessuna restrizione. I siti esclusi globalmente non vengono mai proposti.", "viewToggleLabel": "Vista", + "viewToggleHint": "Scegli se i tuoi farmaci vengono mostrati come schede o come tabella.", "viewCards": "Vista a schede", "viewTable": "Vista tabella", "viewSaveFailed": "Impossibile salvare la vista", @@ -1737,7 +1738,8 @@ "reorderTitle": "Modifica ordine", "reorderDescription": "Imposta l'ordine dei tuoi farmaci: vale per la vista a schede e per la tabella.", "reorderSaved": "Ordine salvato", - "reorderSaveFailed": "Impossibile salvare l’ordine" + "reorderSaveFailed": "Impossibile salvare l’ordine", + "customize": "Personalizza i farmaci" }, "charts": { "loadingLabel": "Caricamento del grafico…", @@ -3767,6 +3769,10 @@ "insights": { "title": "Approfondimenti", "description": "Ordine della panoramica e delle schede di navigazione." + }, + "medications": { + "title": "Farmaci", + "description": "Vista elenco e ordine dei farmaci." } }, "profile": "Profilo", diff --git a/messages/pl.json b/messages/pl.json index 21e72f180..03223cb51 100644 --- a/messages/pl.json +++ b/messages/pl.json @@ -1718,6 +1718,7 @@ "allowedSitesLabel": "Dozwolone miejsca", "allowedSitesHint": "Pozostaw wszystkie zaznaczone, aby nie stosować ograniczeń. Miejsca wykluczone globalnie nigdy nie są proponowane.", "viewToggleLabel": "Widok", + "viewToggleHint": "Wybierz, czy leki mają być wyświetlane jako karty, czy jako tabela.", "viewCards": "Widok kart", "viewTable": "Widok tabeli", "viewSaveFailed": "Nie udało się zapisać widoku", @@ -1737,7 +1738,8 @@ "reorderTitle": "Zmień kolejność", "reorderDescription": "Ustal kolejność swoich leków – obowiązuje w widoku kart i w tabeli.", "reorderSaved": "Kolejność zapisana", - "reorderSaveFailed": "Nie udało się zapisać kolejności" + "reorderSaveFailed": "Nie udało się zapisać kolejności", + "customize": "Dostosuj leki" }, "charts": { "loadingLabel": "Ładowanie wykresu…", @@ -3767,6 +3769,10 @@ "insights": { "title": "Analizy", "description": "Kolejność przeglądu i pigułek nawigacyjnych." + }, + "medications": { + "title": "Leki", + "description": "Widok listy i kolejność leków." } }, "profile": "Profil", diff --git a/src/app/medications/__tests__/list-view-toggle.test.tsx b/src/app/medications/__tests__/list-view-toggle.test.tsx index 409997fb9..305737dd9 100644 --- a/src/app/medications/__tests__/list-view-toggle.test.tsx +++ b/src/app/medications/__tests__/list-view-toggle.test.tsx @@ -33,9 +33,6 @@ vi.mock("@/components/medications/wizard/MedicationWizardDialog", () => ({ vi.mock("@/components/medications/log-intake-dialog", () => ({ LogIntakeDialog: () => null, })); -vi.mock("@/components/medications/medication-reorder-dialog", () => ({ - MedicationReorderDialog: () => null, -})); import MedicationsPage from "@/app/medications/page"; import { I18nProvider } from "@/lib/i18n/context"; @@ -133,9 +130,15 @@ describe("/medications — view renders from the persisted preference", () => { ); }); - it("offers the manual-order editor from the header", () => { + it("links the header customize glyph to /settings/medications", () => { + // v1.16.10 — same entry-point logic as the dashboard and insights + // headers: the Settings2 ghost icon is a plain link to the page's + // settings section (view preference + manual order live there). const html = renderPage({ version: 1, view: "cards", order: [] }); - expect(html).toContain('aria-label="Adjust order"'); + expect(html).toContain('href="/settings/medications"'); + expect(html).toContain('aria-label="Customize medications"'); + // The in-page order dialog is gone — no dialog trigger remains. + expect(html).not.toContain('aria-label="Adjust order"'); }); }); diff --git a/src/app/medications/page.tsx b/src/app/medications/page.tsx index a0a611642..dcd1a4e54 100644 --- a/src/app/medications/page.tsx +++ b/src/app/medications/page.tsx @@ -2,6 +2,7 @@ import { useEffect, useState } from "react"; import { useQuery, useQueryClient } from "@tanstack/react-query"; +import Link from "next/link"; import { useRouter, useSearchParams } from "next/navigation"; import { useAuth } from "@/hooks/use-auth"; import { useTranslations } from "@/lib/i18n/context"; @@ -15,7 +16,6 @@ import { MedicationTableSkeleton, } from "@/components/medications/medication-table"; import { MedicationViewToggle } from "@/components/medications/medication-view-toggle"; -import { MedicationReorderDialog } from "@/components/medications/medication-reorder-dialog"; import { Button } from "@/components/ui/button"; import { Card, CardContent } from "@/components/ui/card"; import { @@ -149,16 +149,13 @@ export default function MedicationsPage() { // two paths: log an intake (incl. a backdated one) against an existing // medication, or create a new medication (the existing wizard). const [logIntakeOpen, setLogIntakeOpen] = useState(false); - // v1.16.10 — manual-order editor (shared by both views). - const [reorderOpen, setReorderOpen] = useState(false); // v1.16.10 — the persisted list presentation: cards vs table plus the // manual medication order, server-side per user // (`GET`/`PUT /api/medications/layout`). The toggle writes - // optimistically; the reorder dialog saves explicitly. - const { layout, isLayoutLoading, setView } = useMedicationListLayout( - isAuthenticated, - ); + // optimistically; the order editor lives at /settings/medications. + const { layout, isLayoutLoading, setView } = + useMedicationListLayout(isAuthenticated); useEffect(() => { if (shouldOpenFromUrl) { @@ -268,25 +265,27 @@ export default function MedicationsPage() {
{/* v1.16.10 — card ⇄ table view toggle, persisted server-side - per user; plus the manual-order editor shared by both - views. The editor opens behind the same Settings2 glyph - the dashboard and insights headers use for their - customize entry points, in the same slot (left of the - add button). It only earns its slot once there is - something to reorder. */} + per user. The Settings2 glyph next to it is the customize + shortcut to /settings/medications (view preference + the + manual-order editor), exactly like the dashboard and + insights headers link to their settings sections, in the + same slot (left of the add button) and with the same + responsive 44-px mobile tap floor. */} - {medsArray.length > 1 && ( - - )} +