feat(search): saved searches (LC-312) #333

Merged
longjacksonle merged 1 commit from feat/lc-312-saved-searches into main 2026-06-15 04:06:54 +02:00

Summary

Save a message-search query (operators included) and re-run it in one click. No new full-page search surface: saved searches live in the existing search popover.

Changes

  • migrations/chat/0061_saved_searches.sql + db/saved_searches.rs: per-user saved_searches(user_id, query, ...), UNIQUE(user_id, query), cap 50. The query string is its own label (one-click save, no name).
  • routes/search.rs: empty-query get_search now returns the saved list (scoped to the unscoped sidebar box); POST /searches (save, cap-gated, swaps to a "Saved" confirmation) and POST /searches/delete (re-renders the list).
  • views/search.rs + templates/search/saved.html: the saved-list fragment. templates/search/results.html: a one-click Save control.
  • templates/partials/sidebar.html: focus added to the message-search hx-trigger so focusing the empty box shows saved searches.
  • templates/layout.html: delegated [data-lc-saved-query] handler fills the search input + dispatches input to re-run.
  • routes/mod.rs: register /searches + /searches/delete.
  • locales/{en,es}/misc.ftl: new strings.

Save/delete use buttons + hx-include (not nested <form>s, which are invalid inside the search form).

Testing

  • just check, just fmt, just test, just test-saas all pass.
  • db_saved_searches.rs (add/list/remove/idempotent/per-user) and routes_saved_searches.rs (save -> appears in empty-q popover -> delete; idempotent; unauth 303).
  • No LC-77 fixture change (message.html untouched).

Not operator-visible (no env/config/contract change).

## Summary Save a message-search query (operators included) and re-run it in one click. No new full-page search surface: saved searches live in the existing search popover. ## Changes - `migrations/chat/0061_saved_searches.sql` + `db/saved_searches.rs`: per-user `saved_searches(user_id, query, ...)`, UNIQUE(user_id, query), cap 50. The query string is its own label (one-click save, no name). - `routes/search.rs`: empty-query `get_search` now returns the saved list (scoped to the unscoped sidebar box); `POST /searches` (save, cap-gated, swaps to a "Saved" confirmation) and `POST /searches/delete` (re-renders the list). - `views/search.rs` + `templates/search/saved.html`: the saved-list fragment. `templates/search/results.html`: a one-click Save control. - `templates/partials/sidebar.html`: `focus` added to the message-search `hx-trigger` so focusing the empty box shows saved searches. - `templates/layout.html`: delegated `[data-lc-saved-query]` handler fills the search input + dispatches `input` to re-run. - `routes/mod.rs`: register `/searches` + `/searches/delete`. - `locales/{en,es}/misc.ftl`: new strings. Save/delete use buttons + `hx-include` (not nested `<form>`s, which are invalid inside the search form). ## Testing - `just check`, `just fmt`, `just test`, `just test-saas` all pass. - `db_saved_searches.rs` (add/list/remove/idempotent/per-user) and `routes_saved_searches.rs` (save -> appears in empty-q popover -> delete; idempotent; unauth 303). - No LC-77 fixture change (message.html untouched). Not operator-visible (no env/config/contract change).
feat(search): saved searches (LC-312)
All checks were successful
check-secrets / Kingfisher (push) Successful in 4s
check-secrets / Nosey parker (push) Successful in 5s
check-secrets / TruffleHog (push) Successful in 5s
check-secrets / Kingfisher (pull_request) Successful in 4s
check-secrets / TruffleHog (pull_request) Successful in 5s
check-secrets / Nosey parker (pull_request) Successful in 5s
Create release / Create release from merged PR (pull_request) Has been skipped
Check / clippy + fmt + tests (pull_request) Successful in 2m47s
17a32a1b4f
Save a message-search query (operators included) and re-run it in one click, without a new full-page search surface.

A "Save search" control in the results popover stores the current query; the sidebar search input now also fires on focus, and an empty query returns the user's saved searches in the popover (like an address bar showing bookmarks). Clicking a saved row fills the search box and dispatches input, so the existing /search FTS + operator + access-gating path re-runs and the popover swaps to results - no navigation. Each row can be removed.

Storage is a new chat.db saved_searches table (per user, UNIQUE(user_id, query), capped 50); the query string is also its display label, so save is one-click with no separate name. Save/delete use buttons + hx-include rather than nested <form>s (which are invalid inside the search form). The empty-query saved list is scoped to the unscoped sidebar box (room_id/enclave_id absent) so it does not surface in the room-header search.

#LC-312
#LC-313

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
longjacksonle deleted branch feat/lc-312-saved-searches 2026-06-15 04:06:54 +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!333
No description provided.