diff --git a/messages/de.json b/messages/de.json index 3dfe3715f..aa4bcd595 100644 --- a/messages/de.json +++ b/messages/de.json @@ -7693,7 +7693,10 @@ "notePlaceholder": "Was erwähnenswert ist (verschlüsselt).", "saveError": "Die Episode konnte nicht gespeichert werden. Bitte erneut versuchen.", "parent": "Teil von", - "parentNone": "Nicht verknüpft" + "parentNone": "Nicht verknüpft", + "resolved": "Beendet am", + "resolvedHelp": "Leer lassen, solange die Episode noch andauert.", + "markOngoing": "Noch andauernd" }, "sheet": { "title": "{date} erfassen", @@ -7745,7 +7748,8 @@ "direction": { "above": "über deinem Üblichen", "below": "unter deinem Üblichen" - } + }, + "settled": "Während dieser Episode wich nichts Auffälliges von deinem üblichen Bereich ab." }, "insights": { "title": "Dein Krankheitsverlauf", diff --git a/messages/en.json b/messages/en.json index d789de5f6..af5e01480 100644 --- a/messages/en.json +++ b/messages/en.json @@ -7693,7 +7693,10 @@ "notePlaceholder": "Anything worth remembering (encrypted).", "saveError": "Could not save the episode. Please try again.", "parent": "Part of", - "parentNone": "Not linked" + "parentNone": "Not linked", + "resolved": "Ended on", + "resolvedHelp": "Leave empty while the episode is still ongoing.", + "markOngoing": "Still ongoing" }, "sheet": { "title": "Log {date}", @@ -7745,7 +7748,8 @@ "direction": { "above": "above your usual", "below": "below your usual" - } + }, + "settled": "Nothing notable stood out from your usual range during this episode." }, "insights": { "title": "Your illness history", diff --git a/messages/es.json b/messages/es.json index fdd912d82..09e524701 100644 --- a/messages/es.json +++ b/messages/es.json @@ -7693,7 +7693,10 @@ "notePlaceholder": "Algo que valga la pena recordar (cifrado).", "saveError": "No se pudo guardar el episodio. Inténtalo de nuevo.", "parent": "Parte de", - "parentNone": "Sin vincular" + "parentNone": "Sin vincular", + "resolved": "Finalizó el", + "resolvedHelp": "Déjalo vacío mientras el episodio siga en curso.", + "markOngoing": "Aún en curso" }, "sheet": { "title": "Registrar {date}", @@ -7745,7 +7748,8 @@ "direction": { "above": "por encima de lo habitual", "below": "por debajo de lo habitual" - } + }, + "settled": "Durante este episodio nada destacó respecto a tu rango habitual." }, "insights": { "title": "Tu historial de enfermedades", diff --git a/messages/fr.json b/messages/fr.json index ac8b3e0c8..a37d14cea 100644 --- a/messages/fr.json +++ b/messages/fr.json @@ -7693,7 +7693,10 @@ "notePlaceholder": "Ce qui mérite d'être noté (chiffré).", "saveError": "Impossible d'enregistrer l'épisode. Veuillez réessayer.", "parent": "Fait partie de", - "parentNone": "Non lié" + "parentNone": "Non lié", + "resolved": "Terminée le", + "resolvedHelp": "Laissez vide tant que l'épisode est en cours.", + "markOngoing": "Toujours en cours" }, "sheet": { "title": "Enregistrer {date}", @@ -7745,7 +7748,8 @@ "direction": { "above": "au-dessus de ton habituel", "below": "en dessous de ton habituel" - } + }, + "settled": "Rien de notable ne s'est écarté de ta plage habituelle pendant cet épisode." }, "insights": { "title": "Ton historique de maladies", diff --git a/messages/it.json b/messages/it.json index 32d7464c8..6dbf5208e 100644 --- a/messages/it.json +++ b/messages/it.json @@ -7693,7 +7693,10 @@ "notePlaceholder": "Qualcosa da ricordare (cifrato).", "saveError": "Impossibile salvare l'episodio. Riprova.", "parent": "Parte di", - "parentNone": "Non collegato" + "parentNone": "Non collegato", + "resolved": "Conclusa il", + "resolvedHelp": "Lascia vuoto finché l'episodio è ancora in corso.", + "markOngoing": "Ancora in corso" }, "sheet": { "title": "Registra {date}", @@ -7745,7 +7748,8 @@ "direction": { "above": "sopra il tuo solito", "below": "sotto il tuo solito" - } + }, + "settled": "Durante questo episodio nulla si è discostato in modo evidente dal tuo intervallo abituale." }, "insights": { "title": "La tua storia di malattie", diff --git a/messages/pl.json b/messages/pl.json index c5e8028ff..beb3947b1 100644 --- a/messages/pl.json +++ b/messages/pl.json @@ -7693,7 +7693,10 @@ "notePlaceholder": "Cokolwiek wartego zapamiętania (zaszyfrowane).", "saveError": "Nie udało się zapisać epizodu. Spróbuj ponownie.", "parent": "Część", - "parentNone": "Niepowiązane" + "parentNone": "Niepowiązane", + "resolved": "Zakończono", + "resolvedHelp": "Pozostaw puste, dopóki epizod nadal trwa.", + "markOngoing": "Nadal trwa" }, "sheet": { "title": "Zapisz {date}", @@ -7745,7 +7748,8 @@ "direction": { "above": "powyżej Twojej normy", "below": "poniżej Twojej normy" - } + }, + "settled": "Podczas tego epizodu nic istotnego nie odbiegało od twojego zwykłego zakresu." }, "insights": { "title": "Twoja historia chorób", diff --git a/src/components/illness/illness-correlation-card.tsx b/src/components/illness/illness-correlation-card.tsx index 4edec9d0b..852b7d9d4 100644 --- a/src/components/illness/illness-correlation-card.tsx +++ b/src/components/illness/illness-correlation-card.tsx @@ -57,6 +57,25 @@ function CorrelationBody({ value }: { value: IllnessCorrelationValue }) { // the engine on thin data — never a recovery claim, never a second gap. const sleep = value.sleepContext; + // The engine can return `ok` with NO findings — a mild episode where nothing + // strayed notably from the personal band, still active with no recovery gap, + // and no red flag. Without this the card body rendered empty under its "how + // it progressed" heading, reading as if nothing was recorded at all. Show a + // calm, honest line instead (retrospective, never a fabricated finding). + const hasContent = + value.redFlags.length > 0 || + gap !== null || + value.preOnset.length > 0 || + value.nadir.length > 0; + + if (!hasContent) { + return ( +

+ {t("illness.correlation.settled")} +

+ ); + } + return (
{value.redFlags.length > 0 ? ( diff --git a/src/components/illness/new-episode-sheet.tsx b/src/components/illness/new-episode-sheet.tsx index 0334548b3..9b14f8cbd 100644 --- a/src/components/illness/new-episode-sheet.tsx +++ b/src/components/illness/new-episode-sheet.tsx @@ -83,6 +83,10 @@ export function NewEpisodeSheet({ const [type, setType] = useState("INFECTION"); const [lifecycle, setLifecycle] = useState("ACUTE"); const [onset, setOnset] = useState(today); + // Resolved/end date — "" means the episode is still ongoing (open-ended). + // Backdating both onset and this end date lets a whole past episode be + // recorded after the fact. + const [resolved, setResolved] = useState(""); const [parentId, setParentId] = useState(NONE); const [note, setNote] = useState(""); @@ -96,6 +100,9 @@ export function NewEpisodeSheet({ setType(editEpisode?.type ?? "INFECTION"); setLifecycle(editEpisode?.lifecycle ?? "ACUTE"); setOnset(editEpisode ? editEpisode.onsetAt.slice(0, 10) : today); + setResolved( + editEpisode?.resolvedAt ? editEpisode.resolvedAt.slice(0, 10) : "", + ); setParentId(editEpisode?.parentConditionId ?? NONE); setNote(editEpisode?.note ?? ""); } else if (!open && wasOpen) { @@ -104,9 +111,17 @@ export function NewEpisodeSheet({ const showParent = PARENTABLE.includes(lifecycle); + // The end date can never precede the start; the field is min-bounded to the + // onset, and this guard belts-and-braces the same invariant the server holds. + const endBeforeStart = resolved !== "" && resolved < onset; + async function handleSave() { - if (label.trim() === "") return; + if (label.trim() === "" || endBeforeStart) return; const onsetAt = new Date(`${onset}T12:00:00`).toISOString(); + // "" ⇒ ongoing (null clears any stored end). Anchor to local noon so the + // UTC instant lands on the intended calendar day regardless of offset. + const resolvedAt = + resolved === "" ? null : new Date(`${resolved}T12:00:00`).toISOString(); const parentConditionId = showParent && parentId !== NONE ? parentId : null; try { if (isEdit && editEpisode) { @@ -117,6 +132,7 @@ export function NewEpisodeSheet({ type, lifecycle, onsetAt, + resolvedAt, parentConditionId, note: note.trim() === "" ? null : note, }, @@ -126,9 +142,8 @@ export function NewEpisodeSheet({ label: label.trim(), type, lifecycle, - // Anchor to local noon so the UTC instant lands on the intended - // calendar day regardless of the viewer's offset. onsetAt, + resolvedAt, parentConditionId, note: note.trim() === "" ? null : note, }); @@ -154,7 +169,7 @@ export function NewEpisodeSheet({ @@ -240,6 +255,33 @@ export function NewEpisodeSheet({ />
+
+ +
+ + {resolved !== "" ? ( + + ) : null} +
+

+ {t("illness.new.resolvedHelp")} +

+
+