feat(timeline): draw person-defined symptoms as value lines

A custom symptom could feed pattern discovery and the Coach but could not
be looked at over time. The timeline now offers every symptom the record
has logged, hidden ones included, beside the measurements and lab markers,
and draws a chosen one as a value line.

`values` takes `symptom:<definitionId>`, checked for its shape and capped
like a lab marker; an id the record does not hold draws nothing. A day's
value is its highest intensity on the record's local day; a point is the
mean of the bucket's day values over the days logged, with the number of
those days, and a day without an entry is not a zero. The line sits on a
fixed 0 to 10 scale. Symptoms are read under the illness lane's gate, the
module and sharing domain the symptom routes use. An `all` window reaches
back to a chosen symptom's first entry.

The response carries `symptomSeries` and `availableSymptoms`; the OpenAPI
contract is regenerated. The page words a point's count as days and the
value out of ten. The date-key schema moves to its own module so the
symptom schemas can share it.
This commit is contained in:
Marc-André Bombeck committed 2026-11-07 00:38:53 +01:00
1 parent be7a65bdb9
commit 74e691cecd
39 files changed
+1871 -132

No files matched your search

+11 -1
View File
@@ -13944,7 +13944,17 @@
},
"legendLabValue": "Laborwert, wie gemessen",
"legendLabOutside": "Außerhalb des Referenzbereichs",
"legendLabRange": "Referenzbereich"
"legendLabRange": "Referenzbereich",
"legendSymptom": "Symptomlinie: höchste Stärke jedes erfassten Tages (0 bis 10), gemittelt über die erfassten Tage",
"symptoms": {
"menuTitle": "Symptome",
"full": "Bis zu {max} Symptome gleichzeitig. Entferne eines, um ein anderes hinzuzufügen.",
"hidden": "ausgeblendet",
"unnamed": "Symptom",
"daysOne": "an 1 Tag",
"daysFew": "Schnitt aus {count} Tagen",
"daysOther": "Schnitt aus {count} Tagen"
}
},
"lifeEvents": {
"title": "Lebensereignis",
+11 -1
View File
@@ -13944,7 +13944,17 @@
},
"legendLabValue": "Lab value, as measured",
"legendLabOutside": "Outside the reference range",
"legendLabRange": "Reference range"
"legendLabRange": "Reference range",
"legendSymptom": "Symptom line: each logged day's highest intensity (0 to 10), averaged over the logged days",
"symptoms": {
"menuTitle": "Symptoms",
"full": "Up to {max} symptoms at once. Remove one to add another.",
"hidden": "hidden",
"unnamed": "Symptom",
"daysOne": "on 1 day",
"daysFew": "mean of {count} days",
"daysOther": "mean of {count} days"
}
},
"lifeEvents": {
"title": "Life event",
+11 -1
View File
@@ -13944,7 +13944,17 @@
},
"legendLabValue": "Valor de laboratorio, tal como se midió",
"legendLabOutside": "Fuera del intervalo de referencia",
"legendLabRange": "Intervalo de referencia"
"legendLabRange": "Intervalo de referencia",
"legendSymptom": "Línea de síntoma: la intensidad más alta de cada día registrado (0 a 10), promediada sobre los días registrados",
"symptoms": {
"menuTitle": "Síntomas",
"full": "Hasta {max} síntomas a la vez. Quita uno para añadir otro.",
"hidden": "oculto",
"unnamed": "Síntoma",
"daysOne": "en 1 día",
"daysFew": "media de {count} días",
"daysOther": "media de {count} días"
}
},
"lifeEvents": {
"title": "Acontecimiento vital",
+11 -1
View File
@@ -13944,7 +13944,17 @@
},
"legendLabValue": "Valeur de laboratoire, telle que mesurée",
"legendLabOutside": "Hors de l’intervalle de référence",
"legendLabRange": "Intervalle de référence"
"legendLabRange": "Intervalle de référence",
"legendSymptom": "Ligne de symptôme : intensité la plus forte de chaque jour noté (0 à 10), en moyenne sur les jours notés",
"symptoms": {
"menuTitle": "Symptômes",
"full": "Jusqu’à {max} symptômes à la fois. Retirez-en un pour en ajouter un autre.",
"hidden": "masqué",
"unnamed": "Symptôme",
"daysOne": "sur 1 jour",
"daysFew": "moyenne sur {count} jours",
"daysOther": "moyenne sur {count} jours"
}
},
"lifeEvents": {
"title": "Événement de vie",
+11 -1
View File
@@ -13944,7 +13944,17 @@
},
"legendLabValue": "Valore di laboratorio, così come misurato",
"legendLabOutside": "Fuori dall’intervallo di riferimento",
"legendLabRange": "Intervallo di riferimento"
"legendLabRange": "Intervallo di riferimento",
"legendSymptom": "Linea del sintomo: l’intensità più alta di ogni giorno registrato (da 0 a 10), in media sui giorni registrati",
"symptoms": {
"menuTitle": "Sintomi",
"full": "Fino a {max} sintomi alla volta. Rimuovine uno per aggiungerne un altro.",
"hidden": "nascosto",
"unnamed": "Sintomo",
"daysOne": "in 1 giorno",
"daysFew": "media di {count} giorni",
"daysOther": "media di {count} giorni"
}
},
"lifeEvents": {
"title": "Evento di vita",
+11 -1
View File
@@ -13944,7 +13944,17 @@
},
"legendLabValue": "측정된 그대로의 검사 수치",
"legendLabOutside": "참고 범위 밖",
"legendLabRange": "참고 범위"
"legendLabRange": "참고 범위",
"legendSymptom": "증상 선: 기록한 날마다의 가장 높은 강도(0~10)를 기록한 날 기준으로 평균한 값",
"symptoms": {
"menuTitle": "증상",
"full": "증상은 한 번에 최대 {max}개까지 표시할 수 있습니다. 다른 증상을 추가하려면 하나를 제거하세요.",
"hidden": "숨김",
"unnamed": "증상",
"daysOne": "1일",
"daysFew": "{count}일 평균",
"daysOther": "{count}일 평균"
}
},
"lifeEvents": {
"title": "삶의 사건",
+11 -1
View File
@@ -13944,7 +13944,17 @@
},
"legendLabValue": "Labwaarde, zoals gemeten",
"legendLabOutside": "Buiten het referentiebereik",
"legendLabRange": "Referentiebereik"
"legendLabRange": "Referentiebereik",
"legendSymptom": "Symptoomlijn: de hoogste intensiteit van elke vastgelegde dag (0 tot 10), gemiddeld over de vastgelegde dagen",
"symptoms": {
"menuTitle": "Symptomen",
"full": "Maximaal {max} symptomen tegelijk. Verwijder er een om een ander toe te voegen.",
"hidden": "verborgen",
"unnamed": "Symptoom",
"daysOne": "op 1 dag",
"daysFew": "gemiddelde van {count} dagen",
"daysOther": "gemiddelde van {count} dagen"
}
},
"lifeEvents": {
"title": "Levensgebeurtenis",
+11 -1
View File
@@ -13944,7 +13944,17 @@
},
"legendLabValue": "Wynik badania, tak jak zmierzono",
"legendLabOutside": "Poza zakresem referencyjnym",
"legendLabRange": "Zakres referencyjny"
"legendLabRange": "Zakres referencyjny",
"legendSymptom": "Linia objawu: najwyższe natężenie każdego zapisanego dnia (od 0 do 10), uśrednione po zapisanych dniach",
"symptoms": {
"menuTitle": "Objawy",
"full": "Naraz można pokazać do {max} objawów. Usuń jeden, aby dodać inny.",
"hidden": "ukryty",
"unnamed": "Objaw",
"daysOne": "w 1 dniu",
"daysFew": "średnia z {count} dni",
"daysOther": "średnia z {count} dni"
}
},
"lifeEvents": {
"title": "Wydarzenie życiowe",
+11 -1
View File
@@ -13944,7 +13944,17 @@
},
"legendLabValue": "Valor de exame, como foi medido",
"legendLabOutside": "Fora da faixa de referência",
"legendLabRange": "Faixa de referência"
"legendLabRange": "Faixa de referência",
"legendSymptom": "Linha do sintoma: a maior intensidade de cada dia registrado (de 0 a 10), em média sobre os dias registrados",
"symptoms": {
"menuTitle": "Sintomas",
"full": "Até {max} sintomas ao mesmo tempo. Remova um para adicionar outro.",
"hidden": "oculto",
"unnamed": "Sintoma",
"daysOne": "em 1 dia",
"daysFew": "média de {count} dias",
"daysOther": "média de {count} dias"
}
},
"lifeEvents": {
"title": "Evento de vida",
+11 -1
View File
@@ -13944,7 +13944,17 @@
},
"legendLabValue": "Результат анализа, как измерено",
"legendLabOutside": "Вне референсного диапазона",
"legendLabRange": "Референсный диапазон"
"legendLabRange": "Референсный диапазон",
"legendSymptom": "Линия симптома: наибольшая сила за каждый отмеченный день (от 0 до 10), усреднённая по отмеченным дням",
"symptoms": {
"menuTitle": "Симптомы",
"full": "До {max} симптомов одновременно. Уберите один, чтобы добавить другой.",
"hidden": "скрыт",
"unnamed": "Симптом",
"daysOne": "за 1 день",
"daysFew": "среднее за {count} дня",
"daysOther": "среднее за {count} дней"
}
},
"lifeEvents": {
"title": "Событие жизни",