"use client"; export const dynamic = "force-dynamic"; import Link from "next/link"; import { useMemo, useState } from "react"; import { useAuth } from "@/auth/clerk"; import { useQueryClient } from "@tanstack/react-query"; import { ApiError } from "@/api/mutator"; import type { SkillPackRead } from "@/api/generated/model"; import { getListSkillPacksApiV1SkillsPacksGetQueryKey, type listSkillPacksApiV1SkillsPacksGetResponse, useDeleteSkillPackApiV1SkillsPacksPackIdDelete, useListSkillPacksApiV1SkillsPacksGet, useSyncSkillPackApiV1SkillsPacksPackIdSyncPost, } from "@/api/generated/skills/skills"; import { SkillPacksTable } from "@/components/skills/SkillPacksTable"; import { DashboardPageLayout } from "@/components/templates/DashboardPageLayout"; import { buttonVariants } from "@/components/ui/button"; import { ConfirmActionDialog } from "@/components/ui/confirm-action-dialog"; import { useOrganizationMembership } from "@/lib/use-organization-membership"; import { useUrlSorting } from "@/lib/use-url-sorting"; const PACKS_SORTABLE_COLUMNS = ["name", "source_url", "skill_count", "updated_at"]; export default function SkillsPacksPage() { const queryClient = useQueryClient(); const { isSignedIn } = useAuth(); const { isAdmin } = useOrganizationMembership(isSignedIn); const [deleteTarget, setDeleteTarget] = useState(null); const [syncingPackIds, setSyncingPackIds] = useState>(new Set()); const { sorting, onSortingChange } = useUrlSorting({ allowedColumnIds: PACKS_SORTABLE_COLUMNS, defaultSorting: [{ id: "name", desc: false }], paramPrefix: "skill_packs", }); const packsQuery = useListSkillPacksApiV1SkillsPacksGet< listSkillPacksApiV1SkillsPacksGetResponse, ApiError >({ query: { enabled: Boolean(isSignedIn && isAdmin), refetchOnMount: "always", refetchInterval: 15_000, }, }); const packsQueryKey = getListSkillPacksApiV1SkillsPacksGetQueryKey(); const packs = useMemo( () => (packsQuery.data?.status === 200 ? packsQuery.data.data : []), [packsQuery.data], ); const deleteMutation = useDeleteSkillPackApiV1SkillsPacksPackIdDelete( { mutation: { onSuccess: async () => { setDeleteTarget(null); await queryClient.invalidateQueries({ queryKey: packsQueryKey, }); }, }, }, queryClient, ); const syncMutation = useSyncSkillPackApiV1SkillsPacksPackIdSyncPost( { mutation: { onSuccess: async () => { await queryClient.invalidateQueries({ queryKey: packsQueryKey, }); }, }, }, queryClient, ); const handleDelete = () => { if (!deleteTarget) return; deleteMutation.mutate({ packId: deleteTarget.id }); }; const handleSyncPack = async (pack: SkillPackRead) => { setSyncingPackIds((previous) => { const next = new Set(previous); next.add(pack.id); return next; }); try { await syncMutation.mutateAsync({ packId: pack.id, }); } finally { setSyncingPackIds((previous) => { const next = new Set(previous); next.delete(pack.id); return next; }); } }; return ( <> Add pack ) : null } isAdmin={isAdmin} adminOnlyMessage="Only organization owners and admins can manage skill packs." stickyHeader >
`/skills/packs/${pack.id}/edit`} canSync syncingPackIds={syncingPackIds} onSync={(pack) => { void handleSyncPack(pack); }} onDelete={setDeleteTarget} emptyState={{ title: "No packs yet", description: "Add your first skill URL pack to get started.", actionHref: "/skills/packs/new", actionLabel: "Add your first pack", }} />
{packsQuery.error ? (

{packsQuery.error.message}

) : null} {deleteMutation.error ? (

{deleteMutation.error.message}

) : null} {syncMutation.error ? (

{syncMutation.error.message}

) : null}
{ if (!open) setDeleteTarget(null); }} ariaLabel="Delete skill pack" title="Delete skill pack" description={ <> This will remove {deleteTarget?.name} from your pack list. This action cannot be undone. } errorMessage={deleteMutation.error?.message} onConfirm={handleDelete} isConfirming={deleteMutation.isPending} /> ); }