feat: implement saved searches#292
Conversation
- Add migration 012_create_saved_searches.sql with RLS policies - Add GET/POST /api/saved-searches route - Add PATCH/DELETE /api/saved-searches/[id] route - Add SaveSearchModal component with filter preview & alert toggle - Add SavedSearchesList component with rerun, edit, delete, alert toggle - Wire Save/Saved buttons into FilterSidebar
There was a problem hiding this comment.
Pull request overview
Implements “saved searches” for the web app by adding a new saved_searches table (with RLS) plus UI/components and API routes to create, list, update, and delete saved searches.
Changes:
- Add DB migration for
saved_searcheswith RLS policy and timestamp trigger. - Add client UI: save-search modal, saved searches list, and integrate them into
FilterSidebar. - Add Next.js API routes to CRUD saved searches via Supabase.
Reviewed changes
Copilot reviewed 6 out of 6 changed files in this pull request and generated 11 comments.
Show a summary per file
| File | Description |
|---|---|
| apps/web/migrations/012_create_saved_searches.sql | Creates saved_searches table, index, trigger, and RLS policy. |
| apps/web/components/SavedSearchesList.tsx | Client list UI to fetch, re-run, edit, toggle alerts, and delete saved searches. |
| apps/web/components/SaveSearchModal.tsx | Client modal UI to POST a new saved search with optional alerts. |
| apps/web/components/FilterSidebar.tsx | Adds “Save search” + “Saved” UI and rerun integration into filter sidebar. |
| apps/web/app/api/saved-searches/route.ts | GET/POST endpoints for saved searches with Zod validation. |
| apps/web/app/api/saved-searches/[id]/route.ts | PATCH/DELETE endpoints for individual saved searches with Zod validation. |
Comments suppressed due to low confidence (1)
apps/web/components/FilterSidebar.tsx:480
FilterSidebarhas an established test suite, but the newly added saved-search UI (Save button, Saved dropdown, and rerun behavior) is not covered. Add tests to verify opening/closing the modal, toggling the saved list, and that selecting a saved search callsrouter.replacewith the expected query params.
>
<List size={14} /> List All
</Link>
<div className="h-4 w-px bg-gray-200 dark:bg-gray-700" />
<button
onClick={clearFilters}
className="group flex items-center gap-1 text-sm text-gray-500 transition-colors hover:text-primary dark:text-gray-400 dark:hover:text-primary"
>
<X size={14} className="transition-transform group-hover:rotate-90" /> Clear all
</button>
</div>
</div>
<div className="space-y-6">
{/* Location with Autocomplete */}
<div className="relative" ref={suggestionsRef}>
<label className="mb-2 block text-sm font-medium text-gray-700 dark:text-gray-300">Location</label>
<input
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| const fetchSearches = useCallback(async () => { | ||
| setLoading(true); | ||
| try { | ||
| const res = await fetch("/api/saved-searches"); | ||
| if (res.ok) setSearches(await res.json()); | ||
| } finally { | ||
| setLoading(false); | ||
| } | ||
| }, []); | ||
|
|
||
| useEffect(() => { fetchSearches(); }, [fetchSearches]); | ||
|
|
||
| const handleDelete = async (id: string) => { | ||
| setDeletingId(id); | ||
| try { | ||
| await fetch(`/api/saved-searches/${id}`, { method: "DELETE" }); | ||
| setSearches((prev) => prev.filter((s) => s.id !== id)); | ||
| } finally { | ||
| setDeletingId(null); | ||
| } | ||
| }; | ||
|
|
||
| const handleEditSave = async (id: string) => { | ||
| if (!editName.trim()) return; | ||
| const res = await fetch(`/api/saved-searches/${id}`, { | ||
| method: "PATCH", | ||
| headers: { "Content-Type": "application/json" }, | ||
| body: JSON.stringify({ name: editName.trim() }), | ||
| }); | ||
| if (res.ok) { | ||
| const updated: SavedSearch = await res.json(); | ||
| setSearches((prev) => prev.map((s) => (s.id === id ? updated : s))); | ||
| } | ||
| setEditingId(null); | ||
| }; | ||
|
|
||
| const handleToggleAlert = async (search: SavedSearch) => { | ||
| const res = await fetch(`/api/saved-searches/${search.id}`, { | ||
| method: "PATCH", | ||
| headers: { "Content-Type": "application/json" }, | ||
| body: JSON.stringify({ alert_enabled: !search.alert_enabled }), | ||
| }); | ||
| if (res.ok) { | ||
| const updated: SavedSearch = await res.json(); | ||
| setSearches((prev) => prev.map((s) => (s.id === search.id ? updated : s))); | ||
| } | ||
| }; |
There was a problem hiding this comment.
This component implements multiple client-side mutations (DELETE + PATCH for name/alerts) and local state updates, but no tests are added. Given the existing component test suite, add tests that cover: initial fetch rendering, delete rollback/error handling, edit name flow, and alert toggle updating the UI.
There was a problem hiding this comment.
@copilot open a new pull request to apply changes based on this feedback
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
|
@copilot open a new pull request to apply changes based on the comments in this thread |
Closes #138
placeholder