Files
EpicNext-Cms/src/app/admin/rooms/show/[id]/page.tsx
T
openhands dc8fb8a6ed feat: speed up admin clone import and enable Cache Components
- Clone import: defer FurnitureData.json writes and append all entries in a
  single batched write instead of one read-modify-write per item, removing
  the main serialization bottleneck for large batches.
- Clone import: raise SSE batch concurrency cap from 5 to 10 and bump the
  clone client/route default from 2 to 6.
- Add a flush hook to runSseBatch so callers can batch deferred work before
  batch_complete is emitted, and surface flush errors as an error event.
- Enable Next.js Cache Components (instant: false opt-out) and silence the
  related build warnings in next.config.ts.
- Switch isomorphic-dompurify to dompurify and refresh dependencies.
2026-08-05 11:10:16 +02:00

148 lines
4.6 KiB
TypeScript

import { DoorOpen, Eye, Globe, Lock, Pencil, Sofa, Users } from "lucide-react";
import Link from "next/link";
import { redirect } from "next/navigation";
import { AdminBreadcrumb } from "@/components/admin/admin-breadcrumb";
import { Badge } from "@/components/ui/badge";
import { Button } from "@/components/ui/button";
import { Card, CardContent } from "@/components/ui/card";
import { canAccess, getAdminContext, PERMS } from "@/lib/permissions";
import { loadRoom } from "../../_lib/load-room";
// TODO: Cache Components adoption. Refactor this route so this opt-out can be removed.
// See: https://nextjs.org/docs/app/guides/migrating-to-cache-components
export const instant = false;
const stateVariants: Record<
string,
"default" | "secondary" | "destructive" | "outline"
> = {
open: "default",
locked: "secondary",
password: "destructive",
invisible: "outline",
};
export default async function RoomShowPage({
params,
}: {
params: Promise<{ locale: string; id: string }>;
}) {
const { id } = await params;
const { session, permissions } = await getAdminContext();
if (!canAccess(permissions, PERMS.ROOMS_VIEW, session.user.rank))
redirect("/admin");
const room = await loadRoom(id);
const canEdit = canAccess(permissions, PERMS.ROOMS_EDIT, session.user.rank);
return (
<div className="space-y-6">
<AdminBreadcrumb
items={[{ label: "Rooms", href: "/admin/rooms" }, { label: room.name }]}
/>
{/* Hero */}
<div className="rounded-xl border bg-gradient-to-r from-card to-muted/30 p-6">
<div className="flex items-start justify-between gap-4">
<div className="space-y-2">
<div className="flex items-center gap-2">
<DoorOpen className="h-6 w-6 text-muted-foreground" />
<h1 className="text-2xl font-bold">{room.name}</h1>
<Badge variant="secondary" className="text-xs">
#{room.id}
</Badge>
<Badge variant={stateVariants[room.state] || "outline"}>
{room.state}
</Badge>
</div>
{room.description && (
<p className="text-muted-foreground">{room.description}</p>
)}
<div className="flex items-center gap-4 text-sm">
<span className="flex items-center gap-1.5">
<Users className="h-4 w-4 text-muted-foreground" />
<span className="font-medium">{room.users}</span> /{" "}
{room.usersMax} users
</span>
{room.owner ? (
<Link
href={`/admin/users/show/${room.owner.id}`}
className="text-primary hover:underline"
>
Owner: {room.owner.username}
</Link>
) : (
<span>Owner: {room.ownerName}</span>
)}
</div>
</div>
<div className="flex gap-2">
<Button variant="outline" asChild>
<Link href={`/admin/rooms/${room.id}/furni`}>
<Sofa className="mr-2 h-4 w-4" /> Furniture
</Link>
</Button>
{canEdit && (
<Button asChild>
<Link href={`/admin/rooms/edit/${room.id}`}>
<Pencil className="mr-2 h-4 w-4" /> Edit
</Link>
</Button>
)}
</div>
</div>
</div>
{/* Info grid */}
<div className="grid gap-4 sm:grid-cols-2 lg:grid-cols-4">
<Card>
<CardContent className="pt-6">
<div className="flex items-center gap-3">
<Users className="h-5 w-5 text-muted-foreground" />
<div>
<p className="text-2xl font-bold">
{room.users} / {room.usersMax}
</p>
<p className="text-xs text-muted-foreground">Current Users</p>
</div>
</div>
</CardContent>
</Card>
<Card>
<CardContent className="pt-6">
<div className="flex items-center gap-3">
<Lock className="h-5 w-5 text-muted-foreground" />
<div>
<p className="text-2xl font-bold capitalize">{room.state}</p>
<p className="text-xs text-muted-foreground">Access State</p>
</div>
</div>
</CardContent>
</Card>
<Card>
<CardContent className="pt-6">
<div className="flex items-center gap-3">
<Eye className="h-5 w-5 text-muted-foreground" />
<div>
<p className="text-2xl font-bold">{room.score}</p>
<p className="text-xs text-muted-foreground">Score</p>
</div>
</div>
</CardContent>
</Card>
<Card>
<CardContent className="pt-6">
<div className="flex items-center gap-3">
<Globe className="h-5 w-5 text-muted-foreground" />
<div>
<p className="text-2xl font-bold truncate">{room.category}</p>
<p className="text-xs text-muted-foreground">Category</p>
</div>
</div>
</CardContent>
</Card>
</div>
</div>
);
}