Skip to content

fix(spa): detail-page action toast colour by level (#632) + verify-and-lock #629 / #636 + release 1.4.12#639

Merged
MartinCastroAlvarez merged 1 commit into
mainfrom
chore/release-v1.4.12
May 31, 2026
Merged

fix(spa): detail-page action toast colour by level (#632) + verify-and-lock #629 / #636 + release 1.4.12#639
MartinCastroAlvarez merged 1 commit into
mainfrom
chore/release-v1.4.12

Conversation

@MartinCastroAlvarez

Copy link
Copy Markdown
Owner

Closes #629, closes #632, closes #636. First release on the OIDC publish.yml Trusted Publisher path.

#632 — toast colour by message level (real bug fix)

The detail page's ObjectActionButton.onSuccess was unconditionally toasting green — a message_user(..., level=messages.ERROR) from a per-object action came up the same colour as a success. List page already dispatched correctly; the asymmetry was hidden behind the legacy runObjectAction adapter in @dar/data/mutations.ts, which collapsed messages[] into a single string and dropped the level.

  • ObjectActionRunResponse now carries messages?: UserMessage[].
  • A new toastMessages(toast, messages) helper next to useToast maps each level to the right method (mirrors the inline list-page logic).
  • DetailPage uses the helper when messages[] is present.

#629 — prepopulated_fields verify-and-lock (no behaviour change)

Already worked. Pulled the slugify-on-change logic out of CreatePage.handleFieldChange into a pure applyPrepopulate(...) helper, added a vitest that locks the four behaviours (single source / joined sources / manual-edit lock / empty fallback).

#636?next= open-redirect verify-and-lock (no behaviour change)

DarLoginView doesn't override the redirect helpers, so LoginView's parent allow-list check is intact today. Added a pytest that POSTs login with ?next=https://evil.example.com/ and asserts the redirect target stays same-host.

Verification

  • pnpm -r typecheck
  • pnpm lint (js + css + darkmode) ✓
  • pnpm test — 174 / 174 (up from 163; +11 new) ✓
  • poetry run pytest tests/test_login_next_redirect.py -v — 2 / 2 ✓
  • pnpm -w build

Smoke test for the new publish.yml flow

Once merged + tag + gh release create v1.4.12 fires, the new publish.yml workflow should auto-publish via OIDC Trusted Publishing (no local .env needed). The idempotency guard means we can't break anything by accident.

🤖 Generated with Claude Code

Plus lock-in tests for two audit verifications that already worked
(#629 / #636 — close-with-tests).

## #632 — toast colour by Django level

The detail page's `ObjectActionButton.onSuccess` was unconditionally
calling `toast.success(message || 'Done')` — so a
`message_user(..., level=messages.ERROR)` from a per-object action
toasted GREEN instead of red. The list page already dispatched by
level correctly; the asymmetry was hidden behind the legacy
`runObjectAction` adapter in `@dar/data/mutations.ts`, which collapsed
the `ActionRunResponse.messages[]` (each with a level) into a single
`message` string and dropped the level.

Fix:

- `ObjectActionRunResponse` now carries `messages?: UserMessage[]`
  alongside the legacy `message?: string`.
- `runObjectAction` propagates the full list.
- A new shared `toastMessages(toast, messages)` helper lives next to
  `useToast` and maps each level to the right toast method
  (`error` / `warning` → red, `info` / `debug` → blue,
  `success` / unknown → green) — mirrors what the list page already
  does inline.
- `DetailPage`'s `onSuccess` now uses the helper when `messages[]` is
  present; falls back to `toast.success(message || 'Done')` for
  back-compat with v1.4.x APIs that don't emit messages.

## #629 — prepopulated_fields verify-and-lock

The wire already carries `prepopulated_fields`; `CreatePage`
already slugifies the target from sources on each keystroke and
opts the target out of further auto-fill once the operator edits it
by hand. Pulled the logic out of `handleFieldChange` into a pure
`applyPrepopulate(...)` helper so a vitest can pin the four
behaviours (single source, joined sources, manual-edit lock, empty
fallback) without rendering the whole CreatePage.

## #636 — open-redirect protection on `?next=`

`DarLoginView` is a `LoginView` subclass that doesn't override
`get_success_url` / `form_valid` / `success_url_allowed_hosts`, so
the parent's same-host check on `?next=` is intact today. Added a
pytest that POSTs the login with `?next=https://evil.example.com/`
and asserts the redirect target is NOT off-host — locks the
property so a future override can't silently re-introduce the
open-redirect.

## Verification

- `pnpm -r typecheck` ✓
- `pnpm lint` (js + css + darkmode) ✓
- `pnpm test` — 174 / 174 (up from 163; +11 new) ✓
- `poetry run pytest tests/test_login_next_redirect.py -v` — 2 / 2 ✓
- `pnpm -w build` ✓

Closes #629, closes #632, closes #636.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@MartinCastroAlvarez MartinCastroAlvarez merged commit 00ffa77 into main May 31, 2026
5 checks passed
@MartinCastroAlvarez MartinCastroAlvarez deleted the chore/release-v1.4.12 branch May 31, 2026 10:13
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment