Files
ArchiTools/src/modules/hot-desk/components/desk-room-layout.tsx
T

148 lines
4.9 KiB
TypeScript

"use client";
import { DESKS, getDeskLabel } from "../types";
import type { DeskId, DeskReservation } from "../types";
import { getReservationForDesk } from "../services/reservation-service";
import { cn } from "@/shared/lib/utils";
interface DeskRoomLayoutProps {
selectedDate: string;
reservations: DeskReservation[];
onDeskClick: (deskId: DeskId) => void;
}
export function DeskRoomLayout({
selectedDate,
reservations,
onDeskClick,
}: DeskRoomLayoutProps) {
return (
<div className="flex flex-col items-center gap-3">
{/* Room container — styled like a top-down floor plan */}
<div className="relative w-full max-w-[340px] rounded-xl border border-border/60 bg-muted/20 p-5">
{/* Window indicator — LEFT wall (landmark for orientation) */}
<div className="absolute top-4 bottom-4 left-0 w-1.5 rounded-r-sm bg-sky-300/30 dark:bg-sky-500/20" />
<div className="absolute top-6 bottom-6 left-0 flex flex-col justify-between">
{Array.from({ length: 6 }).map((_, i) => (
<div
key={i}
className="ml-0.5 h-3 w-0.5 rounded-full bg-sky-400/25 dark:bg-sky-400/15"
/>
))}
</div>
{/* Window label */}
<div className="absolute left-1.5 top-1/2 -translate-y-1/2 -rotate-90 text-[9px] font-medium text-muted-foreground/40 tracking-widest uppercase select-none">
Fereastră
</div>
{/* Door indicator — RIGHT wall */}
<div className="absolute top-[60%] right-0 h-8 w-1.5 rounded-l-sm bg-amber-400/25 dark:bg-amber-500/15" />
<div className="absolute top-[60%] right-1.5 translate-y-1 text-[8px] text-muted-foreground/30 select-none">
Ușă
</div>
{/* Central table */}
<div className="mx-auto mt-4 mb-4 flex flex-col items-center">
{/* Top row desks */}
<div className="flex gap-3 mb-2">
{DESKS.filter((d) => d.position.startsWith("top")).map((desk) => {
const reservation = getReservationForDesk(
desk.id,
selectedDate,
reservations,
);
return (
<DeskSlot
key={desk.id}
deskId={desk.id}
label={getDeskLabel(desk.id)}
reservation={reservation}
side="top"
onClick={() => onDeskClick(desk.id)}
/>
);
})}
</div>
{/* The table surface */}
<div className="h-12 w-full max-w-[280px] rounded-md border border-border/50 bg-muted/40" />
{/* Bottom row desks */}
<div className="flex gap-3 mt-2">
{DESKS.filter((d) => d.position.startsWith("bottom")).map(
(desk) => {
const reservation = getReservationForDesk(
desk.id,
selectedDate,
reservations,
);
return (
<DeskSlot
key={desk.id}
deskId={desk.id}
label={getDeskLabel(desk.id)}
reservation={reservation}
side="bottom"
onClick={() => onDeskClick(desk.id)}
/>
);
},
)}
</div>
</div>
</div>
</div>
);
}
interface DeskSlotProps {
deskId: DeskId;
label: string;
reservation: DeskReservation | undefined;
side: "top" | "bottom";
onClick: () => void;
}
function DeskSlot({ label, reservation, side, onClick }: DeskSlotProps) {
const isBooked = !!reservation;
return (
<button
type="button"
onClick={onClick}
className={cn(
"group relative flex w-[125px] cursor-pointer flex-col items-center rounded-lg border p-3 transition-all",
side === "top" ? "rounded-b-sm" : "rounded-t-sm",
isBooked
? "border-primary/30 bg-primary/8 hover:border-primary/50 hover:bg-primary/12"
: "border-dashed border-border/60 bg-background/60 hover:border-primary/40 hover:bg-muted/50",
)}
>
{/* Chair indicator */}
<div
className={cn(
"absolute left-1/2 -translate-x-1/2 h-1.5 w-8 rounded-full transition-colors",
side === "top" ? "-top-2.5" : "-bottom-2.5",
isBooked
? "bg-primary/40"
: "bg-muted-foreground/15 group-hover:bg-muted-foreground/25",
)}
/>
{/* Desk label */}
<span className="text-[11px] font-medium text-muted-foreground">
{label}
</span>
{/* Status */}
{isBooked ? (
<span className="mt-1 text-xs font-medium text-primary truncate max-w-full">
{reservation.userName}
</span>
) : (
<span className="mt-1 text-[11px] text-muted-foreground/50">Liber</span>
)}
</button>
);
}