"use client"; import { useEffect, useState } from "react"; import Link from "next/link"; import { useParams, useSearchParams } from "next/navigation"; import { useQuery, useQueryClient } from "@tanstack/react-query"; import { Icon } from "@iconify/react"; import { useTranslations } from "next-intl"; import { toast } from "sonner"; import { fetchContentList, fetchSchema, fetchLocales, getPerPage, deleteEntry } from "@/lib/api"; import { Button } from "@/components/ui/button"; import { CodeBlock } from "@/components/CodeBlock"; import { SearchBar } from "@/components/SearchBar"; import { PaginationLinks } from "@/components/PaginationLinks"; import { ContentLocaleSwitcher } from "@/components/ContentLocaleSwitcher"; import { Breadcrumbs } from "@/components/Breadcrumbs"; import { AlertDialog, AlertDialogAction, AlertDialogCancel, AlertDialogContent, AlertDialogDescription, AlertDialogFooter, AlertDialogHeader, AlertDialogTitle, } from "@/components/ui/alert-dialog"; import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow, } from "@/components/ui/table"; import { Skeleton } from "@/components/ui/skeleton"; import { CollapsibleSection } from "@/components/ui/collapsible"; import { TypeDependencyGraph } from "@/components/TypeDependencyGraph"; export default function ContentListPage() { const t = useTranslations("ContentListPage"); const tSchema = useTranslations("SchemaAndPreviewBar"); const params = useParams(); const searchParams = useSearchParams(); const queryClient = useQueryClient(); const collection = typeof params.collection === "string" ? params.collection : ""; const [pendingDelete, setPendingDelete] = useState(null); const [deleting, setDeleting] = useState(false); const page = Math.max(1, parseInt(searchParams.get("_page") ?? "1", 10) || 1); const sort = searchParams.get("_sort") ?? undefined; const order = (searchParams.get("_order") ?? "asc") as "asc" | "desc"; const q = searchParams.get("_q") ?? undefined; const locale = searchParams.get("_locale") ?? undefined; const perPage = getPerPage(); const { data: schema } = useQuery({ queryKey: ["schema", collection], queryFn: () => fetchSchema(collection), enabled: !!collection, }); const { data: localesData } = useQuery({ queryKey: ["locales"], queryFn: fetchLocales, }); const locales = localesData?.locales ?? []; const defaultLocale = localesData?.default ?? null; const listParams = { _page: page, _per_page: perPage, _status: "all" as const, ...(sort ? { _sort: sort, _order: order } : {}), ...(q?.trim() ? { _q: q.trim() } : {}), ...(locale ? { _locale: locale } : {}), }; const { data, isLoading, error } = useQuery({ queryKey: ["content", collection, listParams], queryFn: () => fetchContentList(collection, listParams), enabled: !!collection, }); if (!collection) { return (
Missing collection name.
); } const items = data?.items ?? []; const total = data?.total ?? 0; const totalPages = data?.total_pages ?? 1; const localeQ = locale ? `_locale=${locale}` : ""; const baseQuery = new URLSearchParams(); if (locale) baseQuery.set("_locale", locale); if (q?.trim()) baseQuery.set("_q", q.trim()); const sortQuery = (field: string, order: "asc" | "desc") => { const p = new URLSearchParams(baseQuery); p.set("_sort", field); p.set("_order", order); return p.toString(); }; const isSortSlug = sort === "_slug" || !sort; const nextSlugOrder = isSortSlug && order === "asc" ? "desc" : "asc"; const tBread = useTranslations("Breadcrumbs"); useEffect(() => { document.title = collection ? `${collection} — RustyCMS Admin` : "RustyCMS Admin"; return () => { document.title = "RustyCMS Admin"; }; }, [collection]); const handleDoDelete = async () => { if (!pendingDelete) return; setDeleting(true); try { await deleteEntry(collection, pendingDelete, locale ? { _locale: locale } : {}); await queryClient.invalidateQueries({ queryKey: ["content", collection] }); setPendingDelete(null); toast.success(t("deleted")); } catch (e) { toast.error(e instanceof Error ? e.message : t("errorDeleting")); } finally { setDeleting(false); } }; return (

{collection}

{schema && ( {tSchema("sectionSchema")} } defaultOpen={false} className="mb-4" contentClassName="max-h-[60vh] overflow-auto" > )} {t("typeDependencies")} } defaultOpen={false} className="mb-4" > {isLoading && (
_slug {t("colStatus")} {t("colActions")} {[1, 2, 3, 4, 5].map((i) => ( ))}
)} {error && (

{error instanceof Error ? error.message : String(error)}

)} {!isLoading && !error && items.length === 0 && (

{t("noEntriesCreate")}

)} {!isLoading && !error && items.length > 0 && ( <>
_slug {isSortSlug && ( )} {t("colStatus")} {t("colActions")} {items.map((entry: Record) => { const slug = entry._slug as string | undefined; if (slug == null) return null; const isDraft = entry._status === "draft"; const editHref = `/content/${collection}/${encodeURIComponent(slug)}${localeQ ? `?${localeQ}` : ""}`; return ( {slug} {isDraft ? t("draft") : t("published")}
); })}
!o && setPendingDelete(null)}> {t("confirmDelete", { slug: pendingDelete ?? "" })} {t("confirmDeleteDescription")} {t("cancel")} {deleting ? t("deleting") : t("yesDelete")} )}
); }