- 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
179 lines
4.9 KiB
TypeScript
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
|
|
}
|
|
/>
|
|
)
|
|
}
|