feat(medications): label each intake history row with its logging origin

The intake history surfaced the coarse IntakeSource enum directly, so a
dose logged from the iOS app showed nothing meaningful. Map the enum to
the practical client it represents — WEB to Web, API to iOS App,
REMINDER to Reminder, IMPORT to Import — and render it as a muted
"via {origin}" caption on each intake row next to the status. No schema
or API change; the source value already rides the existing intake
response. Tighten the en/de labels that read "External API" and
"Telegram / Reminder" into the same origin vocabulary across all six
locales.
This commit is contained in:
Marc-André Bombeck committed 2026-06-01 17:31:02 +02:00
1 parent c2808a9c70
commit 8dced00188
8 files changed
+32 -18

No files matched your search

+5 -4
View File
@@ -1006,10 +1006,11 @@
"phaseResetDefaults": "Auf Defaults zurücksetzen",
"phaseSaved": "Erinnerungsphasen gespeichert",
"phaseReset": "Auf Defaults zurückgesetzt",
"sourceWeb": "Webseite",
"sourceApi": "Externe API",
"sourceReminder": "Telegram / Erinnerung",
"sourceImport": "CSV-Import",
"sourceWeb": "Web",
"sourceApi": "iOS-App",
"sourceReminder": "Erinnerung",
"sourceImport": "Import",
"intakeSourceVia": "über {origin}",
"intakeHistoryEmptyTitle": "Noch keine Einnahmen erfasst",
"intakeHistoryEmptyDescription": "Trage eine Einnahme auf der Tagesübersicht ein, um den Verlauf aufzubauen.",
"intakeHistoryEmptyAction": "Zur Tagesübersicht",
+5 -4
View File
@@ -1006,10 +1006,11 @@
"phaseResetDefaults": "Reset to defaults",
"phaseSaved": "Reminder phases saved",
"phaseReset": "Reset to defaults",
"sourceWeb": "Website",
"sourceApi": "External API",
"sourceReminder": "Telegram / Reminder",
"sourceImport": "CSV Import",
"sourceWeb": "Web",
"sourceApi": "iOS App",
"sourceReminder": "Reminder",
"sourceImport": "Import",
"intakeSourceVia": "via {origin}",
"intakeHistoryEmptyTitle": "No intakes recorded yet",
"intakeHistoryEmptyDescription": "Log a dose on the daily intake page to start building the history.",
"intakeHistoryEmptyAction": "Open daily intake",
+2 -1
View File
@@ -1007,9 +1007,10 @@
"phaseSaved": "Fases de recordatorio guardadas",
"phaseReset": "Restablecidas a valores por defecto",
"sourceWeb": "Web",
"sourceApi": "API",
"sourceApi": "App de iOS",
"sourceReminder": "Recordatorio",
"sourceImport": "Importación",
"intakeSourceVia": "vía {origin}",
"intakeHistoryEmptyTitle": "Aún no hay tomas",
"intakeHistoryEmptyDescription": "En cuanto registres una toma, aparecerá aquí con la hora y la fuente.",
"intakeHistoryEmptyAction": "Registrar toma",
+2 -1
View File
@@ -1007,9 +1007,10 @@
"phaseSaved": "Phases de rappel enregistrées",
"phaseReset": "Réinitialisées par défaut",
"sourceWeb": "Web",
"sourceApi": "API",
"sourceApi": "App iOS",
"sourceReminder": "Rappel",
"sourceImport": "Import",
"intakeSourceVia": "via {origin}",
"intakeHistoryEmptyTitle": "Aucune prise pour l’instant",
"intakeHistoryEmptyDescription": "Dès que tu enregistreras une prise, elle apparaîtra ici avec l’heure et la source.",
"intakeHistoryEmptyAction": "Enregistrer une prise",
+2 -1
View File
@@ -1007,9 +1007,10 @@
"phaseSaved": "Fasi di promemoria salvate",
"phaseReset": "Ripristinate ai valori predefiniti",
"sourceWeb": "Web",
"sourceApi": "API",
"sourceApi": "App iOS",
"sourceReminder": "Promemoria",
"sourceImport": "Importazione",
"intakeSourceVia": "tramite {origin}",
"intakeHistoryEmptyTitle": "Ancora nessuna assunzione",
"intakeHistoryEmptyDescription": "Appena registrerai un’assunzione, comparirà qui con orario e sorgente.",
"intakeHistoryEmptyAction": "Registra un’assunzione",
+2 -1
View File
@@ -1007,9 +1007,10 @@
"phaseSaved": "Fazy przypomnień zapisane",
"phaseReset": "Przywrócono wartości domyślne",
"sourceWeb": "Web",
"sourceApi": "API",
"sourceApi": "Aplikacja iOS",
"sourceReminder": "Przypomnienie",
"sourceImport": "Import",
"intakeSourceVia": "przez {origin}",
"intakeHistoryEmptyTitle": "Brak przyjęć",
"intakeHistoryEmptyDescription": "Gdy tylko zarejestrujesz przyjęcie, pojawi się tu wraz z godziną i źródłem.",
"intakeHistoryEmptyAction": "Zarejestruj przyjęcie",
@@ -143,8 +143,8 @@ describe("<IntakeHistoryListV2> — populated rows", () => {
const html = render(<IntakeHistoryListV2 medicationId="med-1" />, client);
expect(html).toContain("Taken");
expect(html).toContain("Skipped");
expect(html).toContain("Website");
expect(html).toContain("Telegram / Reminder");
expect(html).toContain("via Web");
expect(html).toContain("via Reminder");
});
it("renders the taken row with a green Check chip and the skipped row with an outline SkipForward chip", () => {
@@ -65,8 +65,11 @@ import {
* - Status chip: green Check + "Eingenommen" for taken rows,
* outline SkipForward + "Übersprungen" for skipped rows. The
* component never labels a row both at once.
* - Source badge surfaces WEB / API / REMINDER / IMPORT using the
* existing `medications.source*` keys.
* - Source badge surfaces where the intake was logged — WEB → "Web",
* API → "iOS App", REMINDER → "Reminder", IMPORT → "Import" — wrapped
* in a muted "via {origin}" caption next to the status. The labels map
* the coarse `IntakeSource` enum to the practical client per the
* existing `medications.source*` keys; there is no finer client field.
* - Empty state copy + CTA back to the daily intake page.
* - No edit-in-row, no delete buttons.
*/
@@ -377,8 +380,13 @@ export function IntakeHistoryListV2({
) : null}
</TableCell>
<TableCell>
<Badge variant="outline" className="text-xs">
{sourceLabels[event.source] ?? event.source}
<Badge
variant="outline"
className="text-muted-foreground text-xs font-normal"
>
{t("medications.intakeSourceVia", {
origin: sourceLabels[event.source] ?? event.source,
})}
</Badge>
</TableCell>
{showRowActions && (