Files
EpicNext-Cms/src/app/admin/catalog/catalog-table.tsx
T
openhands 41be6835bf Add missing admin action files and navigation links
- Add 11 missing server action files: badges, bulk-users, catalog, catalog-bc, catalog-items, import-badges, import-furni, multi-account-detect, permissions, rooms, soundtracks
- Add missing admin navigation links: tickets, sounds, translations, import, radio sub-pages
- Add translation keys for all new navigation items
2026-07-11 12:01:05 +02:00

179 lines
4.9 KiB
TypeScript

'use client'
import { Pencil } from 'lucide-react'
import Link from 'next/link'
import { toggleCatalogPage } from '@/actions/catalog'
import { toggleBcPage } from '@/actions/catalog-bc'
import { DataTable } from '@/components/admin/data-table'
import { Badge } from '@/components/ui/badge'
import { Button } from '@/components/ui/button'
import { useServerAction } from '@/hooks/use-server-action'
import type { DataTableColumn, PaginatedResult } from '@/types'
interface CatalogPageRow {
id: number
caption: string
parentId: number
parentCaption: string
pageLayout: string
enabled: string
visible: string
minRank: number
orderNum: number
itemCount: number
childCount: number
}
interface CatalogTableProps {
data: PaginatedResult<CatalogPageRow>
canEdit: boolean
catalogType?: 'normal' | 'bc'
}
export function CatalogTable({ data, canEdit, catalogType = 'normal' }: CatalogTableProps) {
const { run, isPending } = useServerAction()
const isBc = catalogType === 'bc'
// Parent URLs stay in the same catalog; root takes you to the catalog index.
const detailHref = (id: number) =>
isBc ? `/admin/catalog/builder-club/${id}` : `/admin/catalog/${id}`
function handleToggle(id: number, action: 'toggleEnabled' | 'toggleVisible') {
if (isBc) {
const field = action === 'toggleEnabled' ? 'enabled' : 'visible'
run(() => toggleBcPage({ id, field }), { successMessage: 'Updated.' })
} else {
run(() => toggleCatalogPage({ id, action }), { successMessage: 'Updated.' })
}
}
const columns: DataTableColumn<CatalogPageRow>[] = [
{ key: 'id', label: 'ID', sortable: true },
{
key: 'caption',
label: 'Caption',
sortable: true,
render: (value, row) => (
<Link href={detailHref(row.id)} className="font-medium text-primary hover:underline">
{String(value)}
</Link>
),
},
{
key: 'parentId',
label: 'Parent',
sortable: true,
render: (_, row) =>
row.parentId === -1 ? (
<span className="text-xs text-muted-foreground">Root</span>
) : (
<Link href={detailHref(row.parentId)} className="text-xs hover:underline">
{row.parentCaption}
</Link>
),
},
{
key: 'pageLayout',
label: 'Layout',
render: (value) => <Badge variant="secondary">{String(value)}</Badge>,
},
{
key: 'enabled',
label: 'Enabled',
sortable: true,
filterKey: 'filter_enabled',
filterOptions: [
{ label: 'Enabled', value: '1' },
{ label: 'Disabled', value: '0' },
],
render: (value, row) =>
canEdit ? (
<button
type="button"
onClick={() => handleToggle(row.id, 'toggleEnabled')}
disabled={isPending}
className="cursor-pointer disabled:opacity-50"
>
<Badge variant={value === '1' ? 'default' : 'outline'}>
{value === '1' ? 'Yes' : 'No'}
</Badge>
</button>
) : (
<Badge variant={value === '1' ? 'default' : 'outline'}>
{value === '1' ? 'Yes' : 'No'}
</Badge>
),
},
{
key: 'visible',
label: 'Visible',
sortable: true,
filterKey: 'filter_visible',
filterOptions: [
{ label: 'Visible', value: '1' },
{ label: 'Hidden', value: '0' },
],
render: (value, row) =>
canEdit ? (
<button
type="button"
onClick={() => handleToggle(row.id, 'toggleVisible')}
disabled={isPending}
className="cursor-pointer disabled:opacity-50"
>
<Badge variant={value === '1' ? 'default' : 'outline'}>
{value === '1' ? 'Yes' : 'No'}
</Badge>
</button>
) : (
<Badge variant={value === '1' ? 'default' : 'outline'}>
{value === '1' ? 'Yes' : 'No'}
</Badge>
),
},
...(isBc
? []
: [
{
key: 'minRank' as const,
label: 'Min Rank',
sortable: true,
} satisfies DataTableColumn<CatalogPageRow>,
]),
{
key: 'itemCount',
label: 'Items',
render: (value) => <span>{String(value)}</span>,
},
{
key: 'childCount',
label: 'Children',
render: (value) => <span>{String(value)}</span>,
},
{
key: 'actions',
label: 'Actions',
render: (_, row) => (
<Button variant="ghost" size="icon" asChild>
<Link href={detailHref(row.id)}>
<Pencil className="h-4 w-4" />
</Link>
</Button>
),
},
]
return (
<DataTable
data={data}
columns={columns}
searchPlaceholder="Search by caption, layout or item..."
bulkActions={
canEdit
? [{ label: 'Delete Selected', value: 'delete', variant: 'destructive' }]
: undefined
}
/>
)
}