feat: saved searches
Lets users name and pin a search query from the SearchResults page; saved searches persist to localStorage and show as quick links in a new sidebar section, similar to Favorites. Clicking re-runs the query. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -4,6 +4,7 @@ import { AuthApi, SyncApi, AnalyticsApi } from '../api/client.js';
|
||||
import Logo from './Logo.jsx';
|
||||
import DevBanner from './DevBanner.jsx';
|
||||
import SyncStatus from './SyncStatus.jsx';
|
||||
import useSavedSearches from '../hooks/useSavedSearches.js';
|
||||
|
||||
// ── Folder definitions ────────────────────────────────────────────────────────
|
||||
|
||||
@@ -78,8 +79,8 @@ const loadFavs = () => {
|
||||
const saveFavs = (v) => localStorage.setItem(LS_FAV, JSON.stringify(v));
|
||||
|
||||
const loadSections = () => {
|
||||
try { return JSON.parse(localStorage.getItem(LS_SECTS)) ?? { fav: true, mailbox: true, smart: true }; }
|
||||
catch { return { fav: true, mailbox: true, smart: true }; }
|
||||
try { return JSON.parse(localStorage.getItem(LS_SECTS)) ?? { fav: true, mailbox: true, smart: true, saved: true }; }
|
||||
catch { return { fav: true, mailbox: true, smart: true, saved: true }; }
|
||||
};
|
||||
const saveSections = (v) => localStorage.setItem(LS_SECTS, JSON.stringify(v));
|
||||
|
||||
@@ -126,6 +127,7 @@ export default function Layout() {
|
||||
const [sections, setSections] = useState(loadSections);
|
||||
const [favSlugs, setFavSlugs] = useState(loadFavs);
|
||||
const [counts, setCounts] = useState(null);
|
||||
const { searches: savedSearches, remove: removeSavedSearch } = useSavedSearches();
|
||||
|
||||
const loc = useLocation();
|
||||
const navigate = useNavigate();
|
||||
@@ -291,6 +293,36 @@ export default function Layout() {
|
||||
</ul>
|
||||
)}
|
||||
|
||||
{/* ── Saved Searches ── */}
|
||||
{savedSearches.length > 0 && (
|
||||
<>
|
||||
<SectionHead label="Saved Searches" open={sections.saved} onToggle={() => toggleSection('saved')} collapsed={!sidebarOpen} />
|
||||
{sections.saved && (
|
||||
<ul className="folder-list">
|
||||
{savedSearches.map((s) => (
|
||||
<li key={s.id} className="saved-search-row">
|
||||
<Link
|
||||
to={`/app/search?q=${encodeURIComponent(s.query)}`}
|
||||
className={`folder-item${loc.pathname === '/app/search' && searchParams.get('q') === s.query ? ' folder-item--active' : ''}`}
|
||||
title={s.query}
|
||||
>
|
||||
<span className="folder-icon">🔎</span>
|
||||
{sidebarOpen && <span className="folder-label">{s.label}</span>}
|
||||
</Link>
|
||||
{sidebarOpen && (
|
||||
<button
|
||||
className="fav-pin fav-pin--remove"
|
||||
title="Remove saved search"
|
||||
onClick={(e) => { e.preventDefault(); removeSavedSearch(s.id); }}
|
||||
>✕</button>
|
||||
)}
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
)}
|
||||
</>
|
||||
)}
|
||||
|
||||
{/* ── Mailbox ── */}
|
||||
<SectionHead label="Mailbox" open={sections.mailbox} onToggle={() => toggleSection('mailbox')} collapsed={!sidebarOpen} />
|
||||
{sections.mailbox && (
|
||||
|
||||
Reference in New Issue
Block a user