frontend: search, filters and load-more on Incidents; open Archive to viewers

Incidents page: text search (title, location, summary, units, vehicles,
tags, location mentions), status and type filters, and a Load more button
that pages the Firestore query 100 at a time. Filtering runs over the
loaded window, and the page says so when older incidents exist.

Archive (/calls): readable by every org member, not just admins.
GET /calls/search now takes any Firebase token scoped to the caller's org
— the Firestore rules already let members read every call in their org,
so this widens nothing. Attach/detach stays admin-only (UI and routes).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
Logan Cusano
2026-09-23 23:53:46 -04:00
co-authored by Claude Opus 5.5
parent 5f85a878fa
commit fa194e0f0a
4 changed files with 108 additions and 23 deletions
+5 -1
View File
@@ -15,6 +15,9 @@ export function useIncidents(limitCount = 100) {
const [incidents, setIncidents] = useState<IncidentRecord[]>([]);
const [loading, setLoading] = useState(true);
const [error, setError] = useState<string | null>(null);
// A full page means there may be older incidents past the limit; a short
// page means the query reached the end of the collection.
const [hasMore, setHasMore] = useState(false);
const { orgId } = useAuth();
useEffect(() => {
@@ -49,6 +52,7 @@ export function useIncidents(limitCount = 100) {
updated_at: toISO(data.updated_at),
} as IncidentRecord;
}));
setHasMore(snap.size >= limitCount);
setLoading(false);
}, (err: FirestoreError) => {
console.error("useIncidents:", err);
@@ -63,7 +67,7 @@ export function useIncidents(limitCount = 100) {
};
}, [limitCount, orgId]);
return { incidents, loading, error };
return { incidents, loading, error, hasMore };
}
export function useIncident(incidentId: string | null) {