136 lines
5.6 KiB
TypeScript
136 lines
5.6 KiB
TypeScript
'use client';
|
|
|
|
import { useState } from 'react';
|
|
import { Package, Trash2 } from 'lucide-react';
|
|
import type { EquipmentInstance, EquipmentSlot } from '@/lib/game/types';
|
|
import { EQUIPMENT_TYPES, getValidSlotsForEquipmentType } from '@/lib/game/data/equipment';
|
|
import { RARITY_CSS_VAR, RARITY_GLOW_CSS_VAR } from '@/components/game/LootInventory/types';
|
|
import { CATEGORY_ICONS } from '@/components/game/LootInventory/icons';
|
|
import { ActionButton } from '@/components/ui/action-button';
|
|
import {
|
|
AlertDialog,
|
|
AlertDialogAction,
|
|
AlertDialogCancel,
|
|
AlertDialogContent,
|
|
AlertDialogDescription,
|
|
AlertDialogFooter,
|
|
AlertDialogHeader,
|
|
AlertDialogTitle,
|
|
AlertDialogTrigger,
|
|
} from '@/components/ui/alert-dialog';
|
|
|
|
interface InventoryListProps {
|
|
inventoryItems: [string, EquipmentInstance][];
|
|
equippedInstances: Record<string, string | null>;
|
|
onEquip: (instanceId: string, slot: EquipmentSlot) => boolean;
|
|
onDelete: (instanceId: string) => void;
|
|
}
|
|
|
|
export function InventoryList({ inventoryItems, equippedInstances, onEquip, onDelete }: InventoryListProps) {
|
|
const [selectedSlot, setSelectedSlot] = useState<Record<string, EquipmentSlot>>({});
|
|
|
|
if (inventoryItems.length === 0) {
|
|
return (
|
|
<div className="text-sm text-[var(--text-muted)] italic text-center py-4">
|
|
No items in inventory. Craft or find equipment to fill your slots.
|
|
</div>
|
|
);
|
|
}
|
|
|
|
return (
|
|
<div className="space-y-2">
|
|
{inventoryItems.map(([instanceId, instance]) => {
|
|
const type = EQUIPMENT_TYPES[instance.typeId];
|
|
const Icon = type ? CATEGORY_ICONS[type.category] || Package : Package;
|
|
const rarityColor = RARITY_CSS_VAR[instance.rarity] || 'var(--rarity-common)';
|
|
const rarityGlow = RARITY_GLOW_CSS_VAR[instance.rarity] || 'var(--rarity-common-glow)';
|
|
const validSlots = type ? getValidSlotsForEquipmentType(type) : [];
|
|
const chosenSlot = selectedSlot[instanceId];
|
|
const availableSlots = validSlots.filter((s) => !equippedInstances[s]);
|
|
|
|
return (
|
|
<div
|
|
key={instanceId}
|
|
className="p-3 rounded border bg-[var(--bg-sunken)] group flex items-center gap-3"
|
|
style={{
|
|
borderColor: rarityColor,
|
|
backgroundColor: rarityGlow,
|
|
}}
|
|
>
|
|
<Icon className="w-5 h-5 shrink-0" style={{ color: rarityColor }} />
|
|
<div className="flex-1 min-w-0">
|
|
<div className="text-sm font-semibold truncate" style={{ color: rarityColor }}>
|
|
{instance.name}
|
|
</div>
|
|
<div className="text-xs text-[var(--text-secondary)]">
|
|
{type?.name || instance.typeId} • {instance.usedCapacity}/{instance.totalCapacity} cap
|
|
</div>
|
|
<div className="text-xs text-[var(--text-muted)] capitalize">
|
|
{instance.rarity} • {instance.enchantments.length} enchant{instance.enchantments.length !== 1 ? 's' : ''} • {instance.quality}% quality
|
|
</div>
|
|
</div>
|
|
<div className="flex items-center gap-2 shrink-0">
|
|
{availableSlots.length > 1 ? (
|
|
<select
|
|
className="text-xs bg-[var(--bg-base)] border border-[var(--border-default)] rounded px-2 py-1 text-[var(--text-primary)]"
|
|
value={chosenSlot || ''}
|
|
onChange={(e) => setSelectedSlot((prev) => ({ ...prev, [instanceId]: e.target.value as EquipmentSlot }))}
|
|
>
|
|
<option value="">Select slot</option>
|
|
{availableSlots.map((s) => (
|
|
<option key={s} value={s}>{s}</option>
|
|
))}
|
|
</select>
|
|
) : null}
|
|
<ActionButton
|
|
variant="secondary"
|
|
size="sm"
|
|
className="text-xs"
|
|
onClick={() => {
|
|
const slot = availableSlots.length === 1 ? availableSlots[0] : chosenSlot;
|
|
if (slot) {
|
|
onEquip(instanceId, slot);
|
|
setSelectedSlot((prev) => {
|
|
const next = { ...prev };
|
|
delete next[instanceId];
|
|
return next;
|
|
});
|
|
}
|
|
}}
|
|
disabled={availableSlots.length === 0 || (availableSlots.length > 1 && !chosenSlot)}
|
|
>
|
|
Equip
|
|
</ActionButton>
|
|
<AlertDialog>
|
|
<AlertDialogTrigger asChild>
|
|
<ActionButton
|
|
variant="ghost"
|
|
size="sm"
|
|
className="h-8 w-8 p-0 text-[var(--color-danger)] hover:text-[var(--interactive-danger-hover)] hover:bg-[var(--interactive-danger)]/20"
|
|
>
|
|
<Trash2 className="w-3.5 h-3.5" />
|
|
</ActionButton>
|
|
</AlertDialogTrigger>
|
|
<AlertDialogContent>
|
|
<AlertDialogHeader>
|
|
<AlertDialogTitle>Delete {instance.name}?</AlertDialogTitle>
|
|
<AlertDialogDescription>
|
|
This action cannot be undone. The item will be permanently destroyed.
|
|
</AlertDialogDescription>
|
|
</AlertDialogHeader>
|
|
<AlertDialogFooter>
|
|
<AlertDialogCancel>Cancel</AlertDialogCancel>
|
|
<AlertDialogAction onClick={() => onDelete(instanceId)}>
|
|
Delete
|
|
</AlertDialogAction>
|
|
</AlertDialogFooter>
|
|
</AlertDialogContent>
|
|
</AlertDialog>
|
|
</div>
|
|
</div>
|
|
);
|
|
})}
|
|
</div>
|
|
);
|
|
}
|