134 lines
4.3 KiB
TypeScript
134 lines
4.3 KiB
TypeScript
"use client";
|
|
|
|
import { useState } from "react";
|
|
import { useRouter } from "next/navigation";
|
|
import type { Loadout } from "@/payload-types";
|
|
import { Button } from "@/components/ui/button";
|
|
import {
|
|
Select,
|
|
SelectContent,
|
|
SelectItem,
|
|
SelectTrigger,
|
|
SelectValue,
|
|
} from "@/components/ui/select";
|
|
import { setLoadoutSlot, equipLoadout } from "@/app/(frontend)/locker/actions";
|
|
import {
|
|
getLoadoutSlot,
|
|
getWardrobeSlot,
|
|
type LoadoutSlots,
|
|
type LockerGridItem,
|
|
} from "@/lib/locker";
|
|
import { Wardrobe } from "./Wardrobe";
|
|
import { WardrobeSlotPicker } from "./WardrobeSlotPicker";
|
|
import { cn } from "@/lib/utils";
|
|
|
|
interface LockerCharacterPanelProps {
|
|
loadouts: Loadout[];
|
|
items: LockerGridItem[];
|
|
}
|
|
|
|
export function LockerCharacterPanel({ loadouts, items }: LockerCharacterPanelProps) {
|
|
const router = useRouter();
|
|
const equippedLoadout = loadouts.find((loadout) => loadout.equipped) ?? null;
|
|
const [pickerSlot, setPickerSlot] = useState<string | null>(null);
|
|
const [busy, setBusy] = useState(false);
|
|
const slots = (equippedLoadout?.slots ?? {}) as LoadoutSlots;
|
|
const pickerLayout = pickerSlot ? getWardrobeSlot(pickerSlot) : null;
|
|
const pickerConfig = pickerSlot ? getLoadoutSlot(pickerSlot) : null;
|
|
|
|
async function changeLoadout(id: string) {
|
|
setBusy(true);
|
|
const result = await equipLoadout(Number(id));
|
|
setBusy(false);
|
|
if (result.success) router.refresh();
|
|
}
|
|
|
|
async function pickSlot(assetId: number | null) {
|
|
if (!equippedLoadout || !pickerSlot) return { success: false, error: "No loadout equipped." };
|
|
setBusy(true);
|
|
const result = await setLoadoutSlot(equippedLoadout.id, pickerSlot, assetId);
|
|
setBusy(false);
|
|
if (result.success) {
|
|
setPickerSlot(null);
|
|
router.refresh();
|
|
}
|
|
return result;
|
|
}
|
|
|
|
return (
|
|
<section
|
|
className="flex min-w-0 flex-col border border-border/70 bg-card/35"
|
|
aria-label="Character equipment"
|
|
>
|
|
<div className="flex items-center justify-between gap-3 border-b border-border/60 bg-muted/20 px-3 py-2">
|
|
<div>
|
|
<p className="font-mono text-[10px] uppercase tracking-[0.2em] text-primary/80">
|
|
CHARACTER
|
|
</p>
|
|
<p className="text-xs text-muted-foreground/70">EQUIPMENT STATUS</p>
|
|
</div>
|
|
<span
|
|
className={cn(
|
|
"size-2 rounded-full",
|
|
equippedLoadout ? "bg-emerald-400" : "bg-muted-foreground/40",
|
|
)}
|
|
aria-label={equippedLoadout ? "Loadout equipped" : "No loadout equipped"}
|
|
/>
|
|
</div>
|
|
|
|
<div className="border-b border-border/50 p-2">
|
|
<Select
|
|
value={equippedLoadout ? String(equippedLoadout.id) : ""}
|
|
onValueChange={changeLoadout}
|
|
disabled={busy || loadouts.length === 0}
|
|
>
|
|
<SelectTrigger className="h-8 rounded-sm border-border/60 bg-background/40 text-xs uppercase tracking-wide">
|
|
<SelectValue placeholder="SELECT LOADOUT" />
|
|
</SelectTrigger>
|
|
<SelectContent>
|
|
{loadouts.map((loadout) => (
|
|
<SelectItem key={loadout.id} value={String(loadout.id)}>
|
|
{loadout.name}
|
|
</SelectItem>
|
|
))}
|
|
</SelectContent>
|
|
</Select>
|
|
</div>
|
|
|
|
<div className="p-2">
|
|
<Wardrobe
|
|
loadout={equippedLoadout ?? ({ name: "Empty", slots: {} } as unknown as Loadout)}
|
|
items={items}
|
|
onSlotClick={setPickerSlot}
|
|
/>
|
|
</div>
|
|
|
|
<div className="flex items-center justify-between border-t border-border/50 px-3 py-2 font-mono text-[10px] uppercase tracking-wide text-muted-foreground/60">
|
|
<span>Click a slot to equip</span>
|
|
<Button
|
|
variant="ghost"
|
|
size="sm"
|
|
className="h-6 rounded-sm px-2 text-[10px]"
|
|
onClick={() => setPickerSlot("primary")}
|
|
disabled={!equippedLoadout || busy}
|
|
>
|
|
EDIT
|
|
</Button>
|
|
</div>
|
|
|
|
{pickerLayout && pickerConfig && (
|
|
<WardrobeSlotPicker
|
|
open={!!pickerSlot}
|
|
onOpenChange={(open) => {
|
|
if (!open) setPickerSlot(null);
|
|
}}
|
|
slot={pickerConfig}
|
|
items={items}
|
|
currentValue={slots[pickerSlot as keyof LoadoutSlots] ?? null}
|
|
busy={busy}
|
|
onPick={pickSlot}
|
|
/>
|
|
)}
|
|
</section>
|
|
);
|
|
}
|