Files
Epicnabbo-Catalogus-Updated…/src/app/admin/rooms/rooms-table.tsx
T
openhands 7a41775c7e fix: disable route prefetching app-wide to avoid spurious requests
Wrap next/link in a shared Link component that ships prefetch=false by
default, so no route is ever prefetched (viewport or hover) anymore, and
drop the DNS prefetch hint. Removes hidden background requests that were
the source of intermittent issues.
2026-08-29 19:27:13 +02:00

141 lines
3.3 KiB
TypeScript

"use client";
import { Eye, Pencil, Sofa, Wifi } from "lucide-react";
import { DataTable } from "@/components/admin/data-table";
import Link from "@/components/link";
import { Badge } from "@/components/ui/badge";
import { Button } from "@/components/ui/button";
import type { DataTableColumn, PaginatedResult } from "@/types/common";
interface RoomRow {
id: number;
name: string;
ownerName: string;
ownerId: number;
state: string;
users: number;
usersMax: number;
description: string;
}
const stateVariants: Record<
string,
"default" | "secondary" | "destructive" | "outline"
> = {
open: "default",
locked: "secondary",
password: "destructive",
invisible: "outline",
};
function getColumns(canEdit: boolean): DataTableColumn<RoomRow>[] {
return [
{ key: "id", label: "ID", sortable: true },
{
key: "name",
label: "Room",
sortable: true,
render: (_, row) => (
<Link href={`/admin/rooms/show/${row.id}`} className="hover:opacity-80">
<div className="min-w-0">
<span className="font-medium block">{row.name}</span>
{row.description && (
<span className="text-xs text-muted-foreground truncate block max-w-[250px]">
{row.description}
</span>
)}
</div>
</Link>
),
},
{
key: "ownerName",
label: "Owner",
render: (_, row) => (
<Link
href={`/admin/users/show/${row.ownerId}`}
className="text-sm text-primary hover:underline"
>
{row.ownerName}
</Link>
),
},
{
key: "state",
label: "State",
sortable: true,
filterKey: "filter_state",
filterOptions: [
{ label: "Open", value: "open" },
{ label: "Locked", value: "locked" },
{ label: "Password", value: "password" },
{ label: "Invisible", value: "invisible" },
],
render: (value) => (
<Badge variant={stateVariants[String(value)] || "outline"}>
{String(value)}
</Badge>
),
},
{
key: "users",
label: "Users",
sortable: true,
render: (_, row) => (
<span className="flex items-center gap-1.5 text-sm">
{row.users > 0 && (
<Wifi className="h-3 w-3 text-[var(--admin-success)]" />
)}
<span
className={row.users > 0 ? "font-medium" : "text-muted-foreground"}
>
{row.users}
</span>
<span className="text-muted-foreground">/ {row.usersMax}</span>
</span>
),
},
{
key: "actions",
label: "",
render: (_, row) => (
<div className="flex items-center gap-1">
<Button variant="ghost" size="icon" asChild>
<Link href={`/admin/rooms/show/${row.id}`}>
<Eye className="h-4 w-4" />
</Link>
</Button>
<Button variant="ghost" size="icon" asChild>
<Link href={`/admin/rooms/${row.id}/furni`}>
<Sofa className="h-4 w-4" />
</Link>
</Button>
{canEdit && (
<Button variant="ghost" size="icon" asChild>
<Link href={`/admin/rooms/edit/${row.id}`}>
<Pencil className="h-4 w-4" />
</Link>
</Button>
)}
</div>
),
},
];
}
interface RoomsTableProps {
data: PaginatedResult<RoomRow>;
canEdit: boolean;
}
export function RoomsTable({ data, canEdit }: RoomsTableProps) {
return (
<DataTable
data={data}
columns={getColumns(canEdit)}
searchPlaceholder="Search by room name or owner..."
exportUrl={canEdit ? "/api/admin/export?type=rooms" : undefined}
/>
);
}