User feedback: only the date+time matters. No override toggle, no
"overridden" badge, no UTC/local-time conversion. What you type is what
gets stored is what you see.
- Removed the `override` state, the checkbox label, the conditional show
of the input, and the "auto-stamped at" hint.
- Single always-on datetime-local input under the "Executed at" label,
disabled only while the test is `pending` (backend rejects timestamp
writes until the state machine reaches executed/reviewed_by_blue).
- `isoToInputValue` and `inputValueToIso` now strip/append the time
segment verbatim — `iso.slice(0, 16)` and `${local}:00Z`. No more
round-trip through `new Date(...).toISOString()` that pulled values
through the browser's local TZ.
- Any edit of the input is implicitly an override at submit time
(`executed_at_overridden = true` if non-empty). The flag is purely
internal bookkeeping — never surfaced in the UI per user request.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
User feedback: the tinted red border around the executed_at block was
visually heavy — same form-field treatment as Command/Output/Comment
is enough, position alone carries the priority.
Kept the label/value/override-toggle/datetime-input layout, just dropped
the border + background tint.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
User feedback: the execution timestamp is the anchor the blue team
correlates their logs against, so it should be the *first* thing in the
red form, not the last (where it lived alongside the override toggle).
Moved the executed-at block above Command/Output/Comment and wrapped it
in a tinted red sub-card (border-red/40 bg-red/5) so it reads as the
form's headline. The block now shows:
- the current `executed_at` (with an `· overridden` hint when applicable),
or a "Not yet executed" stub when the test is still pending,
- the override toggle (disabled until the test reaches executed/reviewed),
- the local datetime-local input + a small "Browser local time — server
stores UTC" hint so an operator typing 10:30 in Paris isn't surprised
to see 08:30Z in the JSON.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Reported by the user: the blue-side dropzone said "≤ 25 MB each" but
nowhere did it list the accepted extensions, and the OS file picker
showed "All files" — so an operator could spend the time picking a
`.exe` only to get a 400 back.
- New constants `EVIDENCE_ALLOWED_EXTENSIONS` + `EVIDENCE_MAX_BYTES` in
`lib/missions.ts`. Manual mirror of the backend whitelist (commented
cross-reference). One source of truth on the client.
- Dropzone now prints `Accepted: .png · .jpg · .jpeg · .pdf · .txt ·
.log · .json · .csv · .evtx · .zip · max 25 MB / file`
(testid `evidence-allowed-formats`).
- File input gains `accept=".png,.jpg,..."` so the OS picker pre-filters
to those extensions instead of "All files".
- `handleFiles` rejects drag-and-drops of unsupported extensions on the
client too (still re-checked server-side — defence in depth, not a
security boundary).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
The naive `new Date(executedAt).toISOString().slice(0, 16)` round-trip on
every keystroke silently shifted the hour by the local TZ offset (Europe
input field is local-time but we kept reformatting via UTC), so the user
could only edit the date — the time component snapped back to UTC every
render.
Fix: keep the local state in `YYYY-MM-DDTHH:MM` form (`executedAtLocal`)
and only convert to/from a UTC ISO at the boundaries — initial sync from
server and submit. Two small helpers `isoToLocalInputValue` /
`localInputValueToIso` carry the conversion explicitly.
Also tightened the useEffect on both Red and Blue zones to depend on
`test.id` instead of the whole `test` object, so polling refetches no
longer wipe an in-progress edit (the 15 s activity poll returns a fresh
object reference even when the row's contents are unchanged).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>