fix(locker): revert to onClick and add error feedback to slot picker
- Revert onPointerDown back to onClick for item selection - Add error state to display server action failures to user - Add pending state to disable buttons during submission - Update onPick to return ActionResult for error handling
This commit is contained in:
parent
bf6b29690f
commit
fc27f11ab8
2 changed files with 33 additions and 21 deletions
|
|
@ -62,7 +62,7 @@ export function WardrobePanel({ userName, loadouts, equippedLoadout, items }: Wa
|
|||
}
|
||||
|
||||
async function handlePick(slotName: string, assetId: number | null) {
|
||||
if (!equippedLoadout) return;
|
||||
if (!equippedLoadout) return { success: false, error: "No loadout equipped." };
|
||||
setBusy(true);
|
||||
const res = await setLoadoutSlot(equippedLoadout.id, slotName, assetId);
|
||||
setBusy(false);
|
||||
|
|
@ -70,6 +70,7 @@ export function WardrobePanel({ userName, loadouts, equippedLoadout, items }: Wa
|
|||
setPickerSlot(null);
|
||||
router.refresh();
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
const pickerLayout = pickerSlot ? getWardrobeSlot(pickerSlot) : null;
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
"use client";
|
||||
|
||||
import { useMemo } from "react";
|
||||
import { useMemo, useState } from "react";
|
||||
import type { Asset } from "@/payload-types";
|
||||
import { Button } from "@/components/ui/button";
|
||||
import {
|
||||
|
|
@ -22,7 +22,7 @@ interface WardrobeSlotPickerProps {
|
|||
items: LockerGridItem[];
|
||||
currentValue: number | Asset | null;
|
||||
busy: boolean;
|
||||
onPick: (assetId: number | null) => void;
|
||||
onPick: (assetId: number | null) => Promise<{ success: boolean; error?: string }>;
|
||||
}
|
||||
|
||||
function resolveCurrentId(value: number | Asset | null): number | null {
|
||||
|
|
@ -40,6 +40,9 @@ export function WardrobeSlotPicker({
|
|||
busy,
|
||||
onPick,
|
||||
}: WardrobeSlotPickerProps) {
|
||||
const [error, setError] = useState<string | null>(null);
|
||||
const [pending, setPending] = useState(false);
|
||||
|
||||
const options = useMemo(() => {
|
||||
const types = new Set(slot.assetTypes);
|
||||
const seen = new Set<number>();
|
||||
|
|
@ -53,6 +56,21 @@ export function WardrobeSlotPicker({
|
|||
|
||||
const currentId = resolveCurrentId(currentValue);
|
||||
|
||||
async function handlePick(assetId: number | null) {
|
||||
setError(null);
|
||||
setPending(true);
|
||||
try {
|
||||
const res = await onPick(assetId);
|
||||
if (!res.success) {
|
||||
setError(res.error ?? "Failed to equip item.");
|
||||
}
|
||||
} catch (e) {
|
||||
setError(e instanceof Error ? e.message : "An unexpected error occurred.");
|
||||
} finally {
|
||||
setPending(false);
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
<Dialog open={open} onOpenChange={onOpenChange}>
|
||||
<DialogContent className="max-h-[85vh] overflow-y-auto">
|
||||
|
|
@ -63,6 +81,12 @@ export function WardrobeSlotPicker({
|
|||
</DialogDescription>
|
||||
</DialogHeader>
|
||||
|
||||
{error && (
|
||||
<div className="rounded-md border border-red-500/50 bg-red-500/10 px-3 py-2 text-sm text-red-400">
|
||||
{error}
|
||||
</div>
|
||||
)}
|
||||
|
||||
<div className="flex flex-col gap-1.5">
|
||||
{options.length === 0 && (
|
||||
<p className="text-sm text-muted-foreground py-2">
|
||||
|
|
@ -75,11 +99,8 @@ export function WardrobeSlotPicker({
|
|||
<button
|
||||
key={entry.assetId}
|
||||
type="button"
|
||||
disabled={busy}
|
||||
onPointerDown={(e) => {
|
||||
e.preventDefault();
|
||||
onPick(entry.assetId);
|
||||
}}
|
||||
disabled={busy || pending}
|
||||
onClick={() => handlePick(entry.assetId)}
|
||||
className={cn(
|
||||
"flex items-center gap-3 rounded-md border px-3 py-2 text-left text-sm transition-colors",
|
||||
selected
|
||||
|
|
@ -103,23 +124,13 @@ export function WardrobeSlotPicker({
|
|||
<DialogFooter>
|
||||
<Button
|
||||
variant="outline"
|
||||
disabled={busy}
|
||||
onPointerDown={(e) => {
|
||||
e.preventDefault();
|
||||
onOpenChange(false);
|
||||
}}
|
||||
disabled={busy || pending}
|
||||
onClick={() => onOpenChange(false)}
|
||||
>
|
||||
Cancel
|
||||
</Button>
|
||||
{currentId != null && (
|
||||
<Button
|
||||
variant="ghost"
|
||||
disabled={busy}
|
||||
onPointerDown={(e) => {
|
||||
e.preventDefault();
|
||||
onPick(null);
|
||||
}}
|
||||
>
|
||||
<Button variant="ghost" disabled={busy || pending} onClick={() => handlePick(null)}>
|
||||
Clear Slot
|
||||
</Button>
|
||||
)}
|
||||
|
|
|
|||
Loading…
Reference in a new issue