diff --git a/frontend/src/components/ExperimentCalendar.jsx b/frontend/src/components/ExperimentCalendar.jsx index 6797490..608acb7 100644 --- a/frontend/src/components/ExperimentCalendar.jsx +++ b/frontend/src/components/ExperimentCalendar.jsx @@ -48,32 +48,30 @@ function DayPanel({ date, animals, allStatuses, template, experimentId, selected return map; }, [allStatuses, dateStr]); - const barData = useMemo(() => { - if (!selectedField) return []; - const isBuiltin = BUILTIN_KEYS_SET.has(selectedField); - return animals - .map((animal) => { - const statuses = allStatuses.filter((s) => s.animal_id === animal.id); - const total = statuses.length; - const success = statuses.filter((s) => { - const val = isBuiltin ? s[selectedField] : s.custom_fields?.[selectedField]; - return val !== null && val !== undefined && String(val).trim() !== ''; - }).length; - return { - name: animal.animal_name || animal.animal_id_string || animal.id.slice(0, 8), - total, - success, - failure: total - success, - pct: total > 0 ? Math.round((success / total) * 100) : 0, - }; - }) - .filter((d) => d.total > 0); - }, [animals, allStatuses, selectedField]); + // Only show animals that have a status record for this day + const animalsWithData = animals.filter((a) => statusByAnimal[a.id]); const fieldLabel = fieldOptions.find((f) => f.value === selectedField)?.label; - // Only show animals that have a status record for this day - const animalsWithData = animals.filter((a) => statusByAnimal[a.id]); + // Bar chart: this day's field value per subject (numeric fields only) + const barData = useMemo(() => { + if (!selectedField) return []; + const isBuiltin = BUILTIN_KEYS_SET.has(selectedField); + return animalsWithData + .map((animal) => { + const status = statusByAnimal[animal.id]; + if (!status) return null; + const raw = isBuiltin ? status[selectedField] : status.custom_fields?.[selectedField]; + if (raw === null || raw === undefined || String(raw).trim() === '') return null; + const num = parseFloat(raw); + if (isNaN(num)) return null; + return { + name: animal.animal_name || animal.animal_id_string || animal.id.slice(0, 8), + value: num, + }; + }) + .filter(Boolean); + }, [animalsWithData, statusByAnimal, selectedField]); return (
@@ -123,28 +121,17 @@ function DayPanel({ date, animals, allStatuses, template, experimentId, selected {barData.length > 0 && (

- Overall completeness by subject - {fieldLabel && — {fieldLabel}} + {fieldLabel ?? 'Field'} by subject

- - - - - - + + [v, fieldLabel ?? 'Value']} /> + -
- {barData.map((d) => ( - - {d.name}: {d.pct}% complete - - ))} -
)}
diff --git a/frontend/tests/ExperimentCalendar.test.jsx b/frontend/tests/ExperimentCalendar.test.jsx index e1cd366..b7c462f 100644 --- a/frontend/tests/ExperimentCalendar.test.jsx +++ b/frontend/tests/ExperimentCalendar.test.jsx @@ -248,7 +248,7 @@ describe('day click modal', () => { // ── Bar chart (inside day modal) ────────────────────────────────────────────── describe('bar chart inside day modal', () => { - it('renders bar chart inside modal when animals have statuses', async () => { + it('renders bar chart for numeric field values on that day', async () => { const dateStr = todayStr(); const statuses = [ makeStatus('a1000000-0000-0000-0000-000000000001', dateStr, { 'ww000000-0000-0000-0000-000000000099': '325' }), @@ -263,21 +263,16 @@ describe('bar chart inside day modal', () => { }); }); - it('does not render bar chart in modal when no animals have overall statuses', async () => { + it('does not render bar chart when selected field is non-numeric', async () => { const dateStr = todayStr(); - // Give Rat A a status today so the day is clickable, but use a different animal not in barData - const statuses = [ - makeStatus('a1000000-0000-0000-0000-000000000001', dateStr, { 'ww000000-0000-0000-0000-000000000099': '325' }), - ]; - // Use animals list with no matching statuses for barData by filtering to empty + // vitals is a text field — not numeric, so no bar chart + const statuses = [makeStatus('a1000000-0000-0000-0000-000000000001', dateStr, {}, 'HR 72')]; render( - , - ); - // No days enabled since animals=[] means no bar chart, but day is also not enabled - // Just verify no bar chart on empty statuses/animals - render( - , + , ); + fireEvent.change(screen.getByTestId('field-select'), { target: { value: 'vitals' } }); + fireEvent.click(screen.getByTestId(`day-${dateStr}`)); + await waitFor(() => screen.getByRole('dialog')); expect(screen.queryByTestId('bar-chart')).not.toBeInTheDocument(); }); });