"use client"; import { type ColumnDef } from "@tanstack/react-table"; import { formatDistanceToNow } from "date-fns"; import { Copy, ExternalLink, MoreHorizontal, Database, RefreshCw, Settings, Trash2, Shield, CheckCircle, XCircle, Clock, AlertTriangle, } from "lucide-react"; import Link from "next/link"; import { toast } from "sonner"; import { Badge } from "~/components/ui/badge"; import { Button } from "~/components/ui/button"; import { Checkbox } from "~/components/ui/checkbox"; import { DataTableColumnHeader } from "~/components/ui/data-table-column-header"; import { DropdownMenu, DropdownMenuContent, DropdownMenuItem, DropdownMenuLabel, DropdownMenuSeparator, DropdownMenuTrigger, } from "~/components/ui/dropdown-menu"; import { api } from "~/trpc/react"; // Define error type for mutations interface TRPCError { message: string; } export type Repository = { id: string; name: string; url: string; description: string | null; trustLevel: "official" | "verified" | "community"; isEnabled: boolean; isOfficial: boolean; lastSyncAt: Date | null; syncStatus: string | null; syncError: string | null; createdAt: Date; updatedAt: Date; }; const trustLevelConfig = { official: { label: "Official", className: "bg-blue-100 text-blue-800 hover:bg-blue-200", icon: Shield, description: "Official HRIStudio repository", }, verified: { label: "Verified", className: "bg-green-100 text-green-800 hover:bg-green-200", icon: Shield, description: "Verified by the community", }, community: { label: "Community", className: "bg-yellow-100 text-yellow-800 hover:bg-yellow-200", icon: Shield, description: "Community repository", }, }; const syncStatusConfig = { pending: { label: "Pending", className: "bg-gray-100 text-gray-800", icon: Clock, description: "Waiting to sync", }, syncing: { label: "Syncing", className: "bg-blue-100 text-blue-800", icon: RefreshCw, description: "Currently syncing", }, completed: { label: "Success", className: "bg-green-100 text-green-800", icon: CheckCircle, description: "Last sync completed successfully", }, failed: { label: "Failed", className: "bg-red-100 text-red-800", icon: AlertTriangle, description: "Last sync failed", }, }; function RepositoryActionsCell({ repository }: { repository: Repository }) { const utils = api.useUtils(); const syncMutation = api.admin.repositories.sync.useMutation({ onSuccess: () => { toast.success("Repository sync started"); void utils.admin.repositories.list.invalidate(); }, onError: (error: TRPCError) => { toast.error(error.message ?? "Failed to sync repository"); }, }); const deleteMutation = api.admin.repositories.delete.useMutation({ onSuccess: () => { toast.success("Repository deleted successfully"); void utils.admin.repositories.list.invalidate(); }, onError: (error: TRPCError) => { toast.error(error.message ?? "Failed to delete repository"); }, }); const handleSync = async () => { syncMutation.mutate({ id: repository.id }); }; const handleDelete = async () => { if ( window.confirm(`Are you sure you want to delete "${repository.name}"?`) ) { deleteMutation.mutate({ id: repository.id }); } }; const handleCopyId = () => { void navigator.clipboard.writeText(repository.id); toast.success("Repository ID copied to clipboard"); }; const handleCopyUrl = () => { void navigator.clipboard.writeText(repository.url); toast.success("Repository URL copied to clipboard"); }; const canDelete = !repository.isOfficial; return ( Actions Sync Repository Edit Repository View Repository Copy Repository ID Copy Repository URL {canDelete && ( <> Delete Repository )} ); } export const repositoriesColumns: ColumnDef[] = [ { id: "select", header: ({ table }) => ( table.toggleAllPageRowsSelected(!!value)} aria-label="Select all" /> ), cell: ({ row }) => ( row.toggleSelected(!!value)} aria-label="Select row" /> ), enableSorting: false, enableHiding: false, }, { accessorKey: "name", header: ({ column }) => ( ), cell: ({ row }) => { const repository = row.original; return (
{repository.name} {repository.isOfficial && ( Official )}
{repository.description && (

{repository.description}

)}
); }, }, { accessorKey: "url", header: ({ column }) => ( ), cell: ({ row }) => { const url = row.original.url; return ( {url} ); }, }, { accessorKey: "trustLevel", header: ({ column }) => ( ), cell: ({ row }) => { const trustLevel = row.original.trustLevel; const config = trustLevelConfig[trustLevel]; const TrustIcon = config.icon; return ( {config.label} ); }, filterFn: (row, id, value: string[]) => { return value.includes(row.original.trustLevel); }, }, { accessorKey: "isEnabled", header: ({ column }) => ( ), cell: ({ row }) => { const isEnabled = row.original.isEnabled; return ( {isEnabled ? ( ) : ( )} {isEnabled ? "Enabled" : "Disabled"} ); }, filterFn: (row, id, value: string[]) => { const isEnabled = row.original.isEnabled; return value.includes(isEnabled ? "enabled" : "disabled"); }, }, { accessorKey: "syncStatus", header: ({ column }) => ( ), cell: ({ row }) => { const syncStatus = row.original.syncStatus; const lastSyncAt = row.original.lastSyncAt; const syncError = row.original.syncError; if (!syncStatus) return "-"; const config = syncStatusConfig[syncStatus as keyof typeof syncStatusConfig]; if (!config) return syncStatus; const SyncIcon = config.icon; return (
{config.label} {lastSyncAt && syncStatus === "completed" && (
{formatDistanceToNow(lastSyncAt, { addSuffix: true })}
)} {syncError && syncStatus === "failed" && (
{syncError}
)}
); }, }, { accessorKey: "createdAt", header: ({ column }) => ( ), cell: ({ row }) => { const date = row.original.createdAt; return (
{formatDistanceToNow(date, { addSuffix: true })}
); }, }, { accessorKey: "updatedAt", header: ({ column }) => ( ), cell: ({ row }) => { const date = row.original.updatedAt; return (
{formatDistanceToNow(date, { addSuffix: true })}
); }, }, { id: "actions", header: "Actions", cell: ({ row }) => , enableSorting: false, enableHiding: false, }, ];