import { useState } from "react"; import { createFileRoute } from "@tanstack/react-router"; import { useQuery, useMutation, useQueryClient } from "@tanstack/react-query"; import { trpc } from "@/utils/trpc"; import { Input } from "@pram/ui/components/input"; import { Button } from "@pram/ui/components/button"; import { Tabs, TabsList, TabsTrigger, TabsContent } from "@pram/ui/components/tabs"; import { toast } from "sonner"; import { getErrorMessage } from "@/lib/error-utils"; import { useDebouncedValue } from "@/hooks/use-debounced-value"; import { MaterialIcon } from "@/components/ui/MaterialIcon"; export const Route = createFileRoute("/admin/featured")({ component: AdminFeatured, }); type Tab = "featured" | "packages" | "questions"; function AdminFeatured() { const [tab, setTab] = useState("featured"); const [search, debouncedSearch, setSearch] = useDebouncedValue("", 300); const [page, setPage] = useState(1); const limit = 15; const queryClient = useQueryClient(); const featured = useQuery(trpc.admin.listFeatured.queryOptions()); const fPackages = featured.data?.packages ?? []; const fQuestions = featured.data?.questions ?? []; const searchQuery = useQuery( trpc.admin.searchContent.queryOptions( { search: debouncedSearch, type: tab === "packages" ? "packages" : "questions", limit, offset: (page - 1) * limit }, { enabled: tab !== "featured" }, ), ); const togglePkgMutation = useMutation( trpc.admin.toggleFeaturedPackage.mutationOptions({ onSuccess: () => { queryClient.invalidateQueries({ queryKey: trpc.admin.listFeatured.queryKey() }); queryClient.invalidateQueries({ queryKey: trpc.admin.searchContent.queryKey() }); toast.success("Package updated"); }, onError: (e: unknown) => toast.error(getErrorMessage(e)), }), ); const toggleQMutation = useMutation( trpc.admin.toggleFeaturedQuestion.mutationOptions({ onSuccess: () => { queryClient.invalidateQueries({ queryKey: trpc.admin.listFeatured.queryKey() }); queryClient.invalidateQueries({ queryKey: trpc.admin.searchContent.queryKey() }); toast.success("Question updated"); }, onError: (e: unknown) => toast.error(getErrorMessage(e)), }), ); function handleSearch(val: string) { setSearch(val); setPage(1); } const tabs: { key: Tab; label: string }[] = [ { key: "featured", label: "Currently Featured" }, { key: "packages", label: "Browse Packages" }, { key: "questions", label: "Browse Questions" }, ]; return (

Editor's Pick

Curate featured content for the homepage.

{ setTab(v as Tab); setSearch(""); setPage(1); }} className="mb-6" > {tabs.map((t) => ( {t.label} ))}

Featured Packages

({fPackages.length})
{fPackages.length === 0 ? (

No featured packages. Browse and select from the tabs above.

) : (
{fPackages.map((pkg) => (

{pkg.title}

{pkg.examTypeId}

))}
)}

Featured Questions

({fQuestions.length})
{fQuestions.length === 0 ? (

No featured questions.

) : (
{fQuestions.map((q) => (

{q.questionText}

{q.format} · {q.examTypeId}

))}
)}
setSearch(e.target.value)} aria-label="Search packages" className="rounded-[var(--radius-lg)] border-2 border-[var(--oat-border)] bg-[var(--pure-white)] h-11" />
handleSearch(e.target.value)} className="max-w-md mb-4" />
); } function SearchResults({ query, tab, page, setPage, limit, togglePkgMutation, toggleQMutation, }: { query: ReturnType; tab: "packages" | "questions"; page: number; setPage: (fn: (p: number) => number) => void; limit: number; togglePkgMutation: { mutate: (args: { packageId: string }) => void; isPending: boolean }; toggleQMutation: { mutate: (args: { questionId: string }) => void; isPending: boolean }; }) { if (query.isLoading) { return

Searching...

; } const data = query.data as { items: Array<{ id: string; title?: string; questionText?: string; format?: string; examTypeId?: string; isFeatured?: boolean }>; total: number } | undefined; return ( <>

{query.isSuccess ? `Showing ${data?.total ?? 0} ${tab}` : ""}

{(data?.items ?? []).map((item) => (

{item.title ?? item.questionText}

{item.format ? `${item.format} · ${item.examTypeId}` : item.examTypeId} {item.isFeatured && ( Featured )}

))}
{data && data.total > limit && (
Page {page} of {Math.ceil(data.total / limit)}
)} ); }