Changes
Co-authored-by: renee-png <262607627+renee-png@users.noreply.github.com>
This commit is contained in:
co-authored by
renee-png
parent
dd992975c9
commit
c0b978dc79
@@ -48,16 +48,27 @@ interface ArchiveRow {
|
||||
created_at: string;
|
||||
}
|
||||
|
||||
const PAGE_SIZE = 200;
|
||||
|
||||
function ArchivePage() {
|
||||
const { isAdmin } = useAuth();
|
||||
const [categories, setCategories] = useState<Category[]>([]);
|
||||
const [activeId, setActiveId] = useState<string>("");
|
||||
const [rows, setRows] = useState<ArchiveRow[]>([]);
|
||||
const [loading, setLoading] = useState(true);
|
||||
const [loadingMore, setLoadingMore] = useState(false);
|
||||
const [search, setSearch] = useState("");
|
||||
const [debouncedSearch, setDebouncedSearch] = useState("");
|
||||
const [totalCount, setTotalCount] = useState(0);
|
||||
const [uploadOpen, setUploadOpen] = useState(false);
|
||||
const [newCatOpen, setNewCatOpen] = useState(false);
|
||||
|
||||
// Debounce search input (300ms)
|
||||
useEffect(() => {
|
||||
const t = setTimeout(() => setDebouncedSearch(search.trim()), 300);
|
||||
return () => clearTimeout(t);
|
||||
}, [search]);
|
||||
|
||||
const loadCategories = async () => {
|
||||
const { data, error } = await supabase
|
||||
.from("archive_categories")
|
||||
@@ -74,31 +85,59 @@ function ArchivePage() {
|
||||
}
|
||||
};
|
||||
|
||||
const loadRows = async (categoryId: string) => {
|
||||
const buildQuery = (categoryId: string, searchTerm: string) => {
|
||||
let q = supabase
|
||||
.from("archive_records")
|
||||
.select("*", { count: "exact" })
|
||||
.eq("category_id", categoryId);
|
||||
if (searchTerm) {
|
||||
// Cast jsonb to text and search case-insensitively
|
||||
q = q.ilike("data::text", `%${searchTerm}%`);
|
||||
}
|
||||
return q.order("created_at", { ascending: false });
|
||||
};
|
||||
|
||||
const loadRows = async (categoryId: string, searchTerm: string) => {
|
||||
if (!categoryId) return;
|
||||
setLoading(true);
|
||||
const { data, error } = await supabase
|
||||
.from("archive_records")
|
||||
.select("*")
|
||||
.eq("category_id", categoryId)
|
||||
.order("created_at", { ascending: false })
|
||||
.limit(1000);
|
||||
const { data, error, count } = await buildQuery(categoryId, searchTerm).range(
|
||||
0,
|
||||
PAGE_SIZE - 1,
|
||||
);
|
||||
if (error) {
|
||||
toast.error(error.message);
|
||||
setLoading(false);
|
||||
return;
|
||||
}
|
||||
setRows((data ?? []) as ArchiveRow[]);
|
||||
setTotalCount(count ?? 0);
|
||||
setLoading(false);
|
||||
};
|
||||
|
||||
const loadMore = async () => {
|
||||
if (!activeId || loadingMore) return;
|
||||
setLoadingMore(true);
|
||||
const { data, error } = await buildQuery(activeId, debouncedSearch).range(
|
||||
rows.length,
|
||||
rows.length + PAGE_SIZE - 1,
|
||||
);
|
||||
if (error) {
|
||||
toast.error(error.message);
|
||||
setLoadingMore(false);
|
||||
return;
|
||||
}
|
||||
setRows((prev) => [...prev, ...((data ?? []) as ArchiveRow[])]);
|
||||
setLoadingMore(false);
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
loadCategories();
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
if (activeId) loadRows(activeId);
|
||||
}, [activeId]);
|
||||
if (activeId) loadRows(activeId, debouncedSearch);
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [activeId, debouncedSearch]);
|
||||
|
||||
const activeCategory = categories.find((c) => c.id === activeId);
|
||||
|
||||
@@ -113,12 +152,6 @@ function ArchivePage() {
|
||||
return Array.from(set);
|
||||
}, [rows]);
|
||||
|
||||
const filteredRows = useMemo(() => {
|
||||
if (!search.trim()) return rows;
|
||||
const s = search.toLowerCase();
|
||||
return rows.filter((r) => JSON.stringify(r.data).toLowerCase().includes(s));
|
||||
}, [rows, search]);
|
||||
|
||||
const deleteRow = async (id: string) => {
|
||||
if (!confirm("Delete this record?")) return;
|
||||
const { error } = await supabase.from("archive_records").delete().eq("id", id);
|
||||
@@ -127,18 +160,20 @@ function ArchivePage() {
|
||||
return;
|
||||
}
|
||||
setRows((prev) => prev.filter((r) => r.id !== id));
|
||||
setTotalCount((c) => Math.max(0, c - 1));
|
||||
toast.success("Record deleted");
|
||||
};
|
||||
|
||||
const deleteAllInCategory = async () => {
|
||||
if (!activeCategory) return;
|
||||
if (!confirm(`Delete ALL ${rows.length} records in "${activeCategory.label}"? This cannot be undone.`)) return;
|
||||
if (!confirm(`Delete ALL ${totalCount} records in "${activeCategory.label}"? This cannot be undone.`)) return;
|
||||
const { error } = await supabase.from("archive_records").delete().eq("category_id", activeCategory.id);
|
||||
if (error) {
|
||||
toast.error(error.message);
|
||||
return;
|
||||
}
|
||||
setRows([]);
|
||||
setTotalCount(0);
|
||||
toast.success("All records deleted");
|
||||
};
|
||||
|
||||
|
||||
Reference in New Issue
Block a user