feat(transcripts): delete, bulk-clean, row polish, live search (LC-440) #439

Merged
longjacksonle merged 1 commit from feat/transcripts-list-delete into main 2026-06-22 02:44:42 +02:00

Improvements to the Call transcripts list (/transcripts) (ref data/ref-transcripts.png). Main gap was no way to delete a transcript. Closes LC-440 (sub-tasks LC-441..LC-445).

Delete (LC-441)

Per-row trash button, hover/focus-revealed; hx-confirm ("Delete this transcript? This can't be undone."); DELETE /transcripts/{id} removes the row via htmx (outerHTML swap) + an out-of-band toast; failures surface via the htmx error path. db::transcripts::delete removes the row + its segments in one transaction (pragma-independent).
Authz - tightened, not loosened: the existing read gate require_access AND the destructive rule (the user who started it, or a site admin). A member who can merely view a shared transcript cannot delete it. Tested: starter can delete; a DM member who isn't the starter (and isn't admin) gets 403; an outsider gets 403.

Bulk clean (LC-442)

A "Delete empty" button (DELETE /transcripts) removes the viewer's deletable 0-line transcripts and re-renders the list + a count toast. A hide-empty toggle filters 0-line rows. Remaining 0-line rows are visually de-emphasized so they don't compete.

Row polish (LC-443)

Title + a colour-coded type badge (Direct call = accent, Voice channel = success) as the primary line; timestamp + line count secondary. The whole row is a clickable stretched-link card with a hover state; the delete button sits above the link via z-index so the targets never conflict; chevron kept.

Empty state (LC-444)

Centered icon + message for: no transcripts yet, no search match, and nothing after the hide-empty filter.

Search (LC-445)

The box now filters live (hx-get debounced, swaps only #lc-transcript-list) and composes with the hide-empty toggle via one shared form + hx-push-url. Search matches the resolved title (incl. the DM peer name) and the date in addition to transcript content. index() returns the list-body partial for htmx requests; a no-JS GET submit still works.

New i18n (en + es). just check, just test, just test-saas pass; Tailwind rebuilt. (Dev server is SSO-gated, so a quick in-browser pass on the PR build is worthwhile - delete + confirm, delete-empty, the hide-empty toggle, and live search.)

🤖 Generated with Claude Code

Improvements to the Call transcripts list (/transcripts) (ref data/ref-transcripts.png). Main gap was no way to delete a transcript. Closes LC-440 (sub-tasks LC-441..LC-445). ## Delete (LC-441) Per-row trash button, hover/focus-revealed; `hx-confirm` ("Delete this transcript? This can't be undone."); `DELETE /transcripts/{id}` removes the row via htmx (outerHTML swap) + an out-of-band toast; failures surface via the htmx error path. `db::transcripts::delete` removes the row + its segments in one transaction (pragma-independent). Authz - tightened, not loosened: the existing read gate `require_access` AND the destructive rule (the user who started it, or a site admin). A member who can merely view a shared transcript cannot delete it. Tested: starter can delete; a DM member who isn't the starter (and isn't admin) gets 403; an outsider gets 403. ## Bulk clean (LC-442) A "Delete empty" button (`DELETE /transcripts`) removes the viewer's deletable 0-line transcripts and re-renders the list + a count toast. A hide-empty toggle filters 0-line rows. Remaining 0-line rows are visually de-emphasized so they don't compete. ## Row polish (LC-443) Title + a colour-coded type badge (Direct call = accent, Voice channel = success) as the primary line; timestamp + line count secondary. The whole row is a clickable stretched-link card with a hover state; the delete button sits above the link via z-index so the targets never conflict; chevron kept. ## Empty state (LC-444) Centered icon + message for: no transcripts yet, no search match, and nothing after the hide-empty filter. ## Search (LC-445) The box now filters live (hx-get debounced, swaps only `#lc-transcript-list`) and composes with the hide-empty toggle via one shared form + `hx-push-url`. Search matches the resolved title (incl. the DM peer name) and the date in addition to transcript content. `index()` returns the list-body partial for htmx requests; a no-JS GET submit still works. New i18n (en + es). `just check`, `just test`, `just test-saas` pass; Tailwind rebuilt. (Dev server is SSO-gated, so a quick in-browser pass on the PR build is worthwhile - delete + confirm, delete-empty, the hide-empty toggle, and live search.) 🤖 Generated with [Claude Code](https://claude.com/claude-code)
feat(transcripts): delete + bulk-clean empty + row polish + live search (LC-440)
All checks were successful
check-secrets / Nosey parker (push) Successful in 7s
check-secrets / Kingfisher (push) Successful in 8s
check-secrets / TruffleHog (push) Successful in 9s
check-secrets / Nosey parker (pull_request) Successful in 4s
check-secrets / Kingfisher (pull_request) Successful in 7s
check-secrets / TruffleHog (pull_request) Successful in 8s
Check / clippy + fmt + tests (pull_request) Successful in 3m20s
Create release / Create release from merged PR (pull_request) Has been skipped
c4e382d1e1
Adds the missing delete on /transcripts and tidies the list. Askama + HTMX + Tailwind, server-rendered.

- Delete a transcript (LC-441): a per-row trash button (hover/focus-revealed) with an hx-confirm; DELETE /transcripts/{id} removes the row via htmx (outerHTML swap) and pops an out-of-band toast; errors surface as an htmx error. db::transcripts::delete removes the row + its segments in one tx. Authz tightens the read gate: require_access AND (the user started it OR is a site admin) - a member who can merely view a shared record can't nuke it.
- Bulk clean (LC-442): a "Delete empty" button (DELETE /transcripts) removes the viewer's deletable 0-line transcripts and re-renders the list + a count toast; a hide-empty toggle filters 0-line rows; remaining 0-line rows are visually de-emphasized.
- Row polish (LC-443): title + a colour-coded type badge (Direct call = accent, Voice channel = success) as the primary line, timestamp + line count secondary. The whole row is a clickable stretched-link card with a hover state; the delete button sits above the link (z-index) so the click targets never conflict; the chevron stays.
- Empty state (LC-444): a centered icon + message for no transcripts / no search match / nothing after the hide-empty filter.
- Search (LC-445): the box now filters live (hx-get debounced, swaps just #lc-transcript-list) and composes with the hide-empty toggle via one shared form + hx-push-url. Search now matches the resolved title (incl. DM peer name) and date in addition to transcript content. index() returns the list-body partial for htmx requests; a no-JS GET submit still works.

New i18n (en + es). New tests: starter_can_delete / member_who_is_not_starter_cannot_delete / outsider_cannot_delete. just check, just test, just test-saas pass; Tailwind rebuilt.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
longjacksonle deleted branch feat/transcripts-list-delete 2026-06-22 02:44:42 +02:00
Sign in to join this conversation.
No reviewers
No labels
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set

Reference
psa-systems/lets-chat!439
No description provided.