Skip to content

Chat voting: saved votes, new panel and immediate action (#400) - #401

Merged
smb merged 11 commits into
mainfrom
400-chat-voting-saved
Oct 11, 2026
Merged

smb merged 11 commits into
mainfrom
400-chat-voting-saved

Conversation

@smb

@smb smb commented Oct 10, 2026 •

Copy link
Copy Markdown
Contributor

Second slice of the chat voting rework (epic #397), after #399.

What changes

  • Saved votes: migration 0047 adds chat_vote_templates (revision, legacy_alias, last_used_at, unique shortcut per channel). Non-empty legacy label settings move once into templates; !vote yesno/scale/01/12 resolve their migrated template via legacy_alias. The settings editor drops the five label fields.
  • Rules: template CRUD for every member role with revision check (409) and a cap of 100 per channel; invalid drafts are stored but not startable, and templateStartProblem gives one reason for the panel ▶, the immediate-action ▶ and !vote <shortcut>; unknown single tokens reply instead of starting.
  • Panel: fixed live block (status, question, end time, End) above ListDetail; list with Saved | Recent, one 20 px status row (n/100, lock reason), flat rows, order fixed per session; inspector with question, answers, quick templates (undo toast), free-text switch card, duration segment with a compact custom row, !vote shortcut and a fixed footer (save status, Start, subtle Delete). Below 1280 px the inspector is an overlay that opens only on click.
  • Immediate action: one card in the standard 192 px height with status, End and a scrolling template list. The stream manager host is unchanged.
  • Autosave: serialized per entry, flushed before start, entry switch and leaving; 409 reloads the entry with a toast.
  • The worker no longer reads or writes preset and legacy_written; dropping those columns follows in a later PR, after this one runs in production.

Deploy order

Production applies migrations by hand before the worker deploy. 0047 only adds a table and reads settings, so it is safe while the #398 worker runs. 0046 must already be applied. Do not edit chat voting settings between applying 0047 and deploying the worker: label settings are copied once at migration time.

Verification

  • pnpm run check green (unit, worker, 118 e2e); module-panels-stability.spec.ts green with --repeat-each=10.
  • Panel and card screenshots reviewed at 1440, 1100 and 760 px (German locale) before opening this PR.

Closes #400

- 📊 Database:
  - Migration 0047 adds chat_vote_templates (revision, legacy_alias, last_used_at, shortcut index)
  - Non-empty legacy label settings move once into templates; aliases resolve via legacy_alias
  - LATEST_SCHEMA_MIGRATION bumped to 0047
- ✨ Features:
  - Template CRUD with revision check (409), cap of 100 per channel, same member guard as start
  - templateStartProblem gives one reason for panel, immediate action and !vote <shortcut>
  - !vote <shortcut> starts a template; unknown single tokens reply instead of starting
  - Immediate-action card with running status, End and a scrolling template list in the standard 192px card
- 🎨 Style:
  - Panel: fixed live block above ListDetail, Saved | Recent list with one status row, inspector with fixed footer
  - Quick templates with undo toast, free-text switch card, duration segment with compact custom row, !vote prefix
- 🛠 Improvements:
  - Serialized autosave flushed before start, switch and leave; 409 reloads with a toast
  - Settings editor drops the five label fields
  - The worker no longer reads or writes preset and legacy_written
- 🧪 Tests:
  - Template migration, routes, repository, service and parser coverage
  - Panel, inspector and immediate-action component tests
  - Stability e2e restored and extended (custom duration, five free-text rows, overnight time, card height)

Refs #400
@smb smb added enhancement New feature or request modul:voting Chat-Voting (Schnellmodus) labels Oct 10, 2026
@smb smb self-assigned this Oct 10, 2026
smb added 10 commits October 10, 2026 20:21
- 🐛 Defects:
  - PATCH acknowledges via UPDATE … RETURNING so a concurrent write is never taken as our own revision
  - Autosave recomputes dirty state after each acknowledgment; flush awaits pending saves and drains later edits
  - Start keeps the latest acknowledged revision when recording usage
  - Unload saves use a cached CSRF token and keepalive without a fresh request
  - Migration 0047 keeps one-answer yes/no, 0/1, 1/2 and scale drafts; only options need two labels
  - Custom duration can be selected from a preset; mode is tracked separately
  - Immediate-action Start/End no longer depend on canManage; the server guard decides
  - Live block keeps one reserved height across loading, empty, error and running on mobile
  - Running markers and Recent refresh after card, chat or external starts and closes
- 🧪 Tests:
  - Regression test per defect (repository, panel, card, migration, stability e2e)

Refs #400
- 🐛 Defects:
  - Server-reported shortcut conflicts are kept until the shortcut changes, ending the save loop
  - Recovered drafts carry their base revision and snapshot; a newer remote edit leads to conflict recovery
  - Invalid CSRF tokens are refetched once for active mutations
  - Usage bookkeeping failures no longer turn a completed start into an error
  - Live-state and usage responses are sequenced so stale replies cannot roll the UI back
  - Immediate-action card keeps its loaded order after starts
  - Restored drafts select the matching duration segment
  - Usage refresh success clears the template error state
- 🔒 Security:
  - Template create, update and delete write guarded audit records with actor and before/after
- 🧪 Tests:
  - Regression test per defect

Refs #400
- 🔄 Refactors:
  - Remove the autosave hook (debounce, queue, unload saves, draft recovery, blank reuse) and the quick-template undo toast
  - Inspector keeps one local draft with the dashboard SaveBar: Save, Discard, status, 409 reload, delete in the destructive slot
  - New votes stay local until the first Save; Start saves a dirty draft first and starts only on success
  - Discard confirmation when switching, closing or leaving with unsaved changes
- 🐛 Defects:
  - PATCH acknowledges via UPDATE … RETURNING inside the guarded audit batch
  - Polling applies responses by sequence without starving slow connections
  - Immediate-action refresh merges configuration and membership in session order
  - Running notice keeps a fixed-height slot
- 🧪 Tests:
  - Explicit-save flow, RETURNING under concurrency, slow polling, card refresh, notice slot

Refs #400
# Conflicts:
#	src/modules/chat_voting/panel/index.tsx
#	src/modules/chat_voting/panel/service.ts
#	src/modules/chat_voting/routes.ts
#	src/worker/config.ts
#	tests/components/chat-voting-panel.test.tsx
#	tests/unit/chat-voting-routes.test.ts
#	tests/unit/sql-contract.test.ts
#	tests/worker/healthz.test.ts
- 🎨 Style:
  - Delete moves into the inspector header menu; Start gets its own fixed row above the standard SaveBar
- 🐛 Defects:
  - The implicitly opened desktop template keeps its guarded draft across responsive changes
  - A second save of a new vote no longer remounts the inspector and drops pending edits
  - Template saves and deletes go through runModuleQueryWrite so stale reads cannot overwrite them
  - Escape closes the desktop Recent inspector (SubInspector)
- 🧪 Tests:
  - Footer visibility at 1440, 1100, 760 and 390 px; one regression test per defect

Refs #400
- 🎨 Style:
  - At inspector widths up to 480px the save status gets its own row inside the 64px bar
  - Header actions menu sits right next to the close button
- 🐛 Defects:
  - A pending delete can no longer discard another template's draft
  - Reselecting a template after a remote save adopts its newer revision
  - A delayed Start only merges lastUsedAt and keeps newer configuration
  - Aborted loads no longer raise load-error notifications
- 🧪 Tests:
  - Regression test per defect; status fits at 390px with a 20 % margin

Refs #400
- 🐛 Defects:
  - Save and reload completions from a replaced inspector are ignored, so B's edits can never be written into A
  - Template usage timestamps update monotonically in the database and in the immediate-action cache
- 🧪 Tests:
  - Delayed reload while editing another template; out-of-order starts keep the newest usage time
  - New-draft save test waits for the Save button instead of asserting synchronously

Refs #400
# Conflicts:
#	src/dashboard/ui/ListToolbar.tsx
#	src/modules/chat_voting/panel/index.tsx
- 🐛 Defects:
  - A delayed conflict reload no longer overwrites edits made after Discard; duplicate reloads are ignored
  - A manual retry error clears on the next successful fetch even when the data is unchanged (useManualQueryFailure)
- 🧪 Tests:
  - Delayed reload after Discard and edits, duplicate reload clicks, recovery after identical refreshes

Refs #400
- 🐛 Defects:
  - Conflict Reload uses refetchModuleQueryData so cache and inspector hold the same revision; reselecting no longer rolls back
- 🧪 Tests:
  - Reload followed by reselection keeps the newest revision

Refs #400
@sonarqubecloud

Copy link
Copy Markdown

@smb
smb merged commit 4bbf7a5 into main Oct 11, 2026
7 checks passed
@smb
smb deleted the 400-chat-voting-saved branch October 11, 2026 07:52
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

enhancement New feature or request modul:voting Chat-Voting (Schnellmodus)

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Chat voting: saved votes, new panel and immediate action

1 participant