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();
});
});