feat: session file registry with drag-and-drop on day view

Adds a FileDropZone at the bottom of the day view where users can
drop all session files at once. Filenames are regex-matched to
subjects using their animal_id_string (word-boundary aware, so
"2852" won't match "28520"). Only metadata is recorded — files
are never uploaded.

Backend: new session_files table (id, daily_status_id, filename,
file_size BIGINT, file_type, last_modified, notes). Routes:
  POST /api/daily-statuses/:id/files  (batch create)
  GET  /api/daily-statuses/:id/files
  DELETE /api/session-files/:id

Frontend: FileDropZone shows per-subject match groups, unmatched
files, and a warning for subjects with no match. After saving,
registered files are displayed with delete capability.

test-files/: generate_test_files.sh creates 4 dummy files per
subject (2×bin, 2×csv) with correct naming for drag-and-drop
testing. Pre-generated for 2026-04-23 with IDs 2852/3076/3077/3078.

Tests: 19 tests covering drag state, regex matching, save payload,
partial-overlap prevention, multi-subject batching, and delete.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
Experiments DB Dev
2026-04-23 13:12:45 -04:00
parent dc46af8d31
commit daf36908ef
25 changed files with 1008 additions and 0 deletions
+2
View File
@@ -10,6 +10,7 @@ const animalsRouter = require('./routes/animals');
const dailyStatusesRouter = require('./routes/dailyStatuses');
const auditLogsRouter = require('./routes/auditLogs');
const analysesRouter = require('./routes/analyses');
const sessionFilesRouter = require('./routes/sessionFiles');
const app = express();
@@ -63,6 +64,7 @@ app.use('/api/animals', animalsRouter);
app.use('/api/daily-statuses', dailyStatusesRouter);
app.use('/api/audit-logs', auditLogsRouter);
app.use('/api', analysesRouter);
app.use('/api', sessionFilesRouter);
app.use((req, res) => {
res.status(404).json({ error: `Route ${req.method} ${req.path} not found` });