diff --git a/frontend/src/components/ExperimentCalendar.jsx b/frontend/src/components/ExperimentCalendar.jsx
index 8352e35..2d53ae7 100644
--- a/frontend/src/components/ExperimentCalendar.jsx
+++ b/frontend/src/components/ExperimentCalendar.jsx
@@ -53,29 +53,25 @@ function DayPanel({ date, animals, allStatuses, template, experimentId, selected
const fieldLabel = fieldOptions.find((f) => f.value === selectedField)?.label;
- // Bar chart: session stats across ALL days, for animals with at least one valid field value
+ // Bar chart: this day's field value per animal (numeric only, filtered to animals present that day)
const barData = useMemo(() => {
if (!selectedField) return [];
const isBuiltin = BUILTIN_KEYS_SET.has(selectedField);
- return animals
+ return animalsWithData
.map((animal) => {
- const statuses = allStatuses.filter((s) => s.animal_id === animal.id);
- const total = statuses.length;
- if (total === 0) return null;
- const success = statuses.filter((s) => {
- const val = isBuiltin ? s[selectedField] : s.custom_fields?.[selectedField];
- return val !== null && val !== undefined && String(val).trim() !== '';
- }).length;
- if (success === 0) return null; // skip animals with no valid field data
+ 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),
- total,
- success,
- failure: total - success,
+ value: num,
};
})
.filter(Boolean);
- }, [animals, allStatuses, selectedField]);
+ }, [animalsWithData, statusByAnimal, selectedField]);
return (
@@ -124,20 +120,16 @@ function DayPanel({ date, animals, allStatuses, template, experimentId, selected
{/* Bar chart: overall per-subject completeness */}
{barData.length > 0 && (
-
- Session statistics — {fieldLabel ?? 'selected field'}
+
+ {fieldLabel ?? 'Field'} — this session
-
All sessions across the experiment
-
-
-
-
-
-
+
+ [v, fieldLabel ?? 'Value']} />
+
diff --git a/frontend/tests/ExperimentCalendar.test.jsx b/frontend/tests/ExperimentCalendar.test.jsx
index a17dfb9..7aefa27 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 session stats bar chart when animals have valid field data', async () => {
+ it('renders bar chart with that day\'s numeric field values', async () => {
const dateStr = todayStr();
const statuses = [
makeStatus('a1000000-0000-0000-0000-000000000001', dateStr, { 'ww000000-0000-0000-0000-000000000099': '325' }),
@@ -258,14 +258,13 @@ describe('bar chart inside day modal', () => {
);
fireEvent.click(screen.getByTestId(`day-${dateStr}`));
await waitFor(() => {
- const dialog = screen.getByRole('dialog');
- expect(within(dialog).getByTestId('bar-chart')).toBeInTheDocument();
+ expect(within(screen.getByRole('dialog')).getByTestId('bar-chart')).toBeInTheDocument();
});
});
- it('excludes animals with no valid field data from bar chart', async () => {
+ it('omits animals with empty or non-numeric field value from bar chart', async () => {
const dateStr = todayStr();
- // Rat A has weight data; Rat B has only empty custom_fields — should be excluded from chart
+ // Rat A has numeric weight; Rat B has empty weight — Rat B excluded from chart but shown in day list
const statuses = [
makeStatus('a1000000-0000-0000-0000-000000000001', dateStr, { 'ww000000-0000-0000-0000-000000000099': '325' }),
makeStatus('a2000000-0000-0000-0000-000000000002', dateStr, {}),
@@ -275,7 +274,19 @@ describe('bar chart inside day modal', () => {
);
fireEvent.click(screen.getByTestId(`day-${dateStr}`));
await waitFor(() => screen.getByRole('dialog'));
- // Both animals are in the day view (both have records), but bar chart only has Rat A
expect(screen.getByTestId('bar-chart')).toBeInTheDocument();
});
+
+ it('hides bar chart when no animals have a numeric value for the selected field that day', async () => {
+ const dateStr = todayStr();
+ // vitals is text — no bar chart
+ const statuses = [makeStatus('a1000000-0000-0000-0000-000000000001', dateStr, {}, 'HR 72')];
+ 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();
+ });
});