feat: Implement structured lootbox results with image support and display referenced items in shop listings.
All checks were successful
Deploy to Production / test (push) Successful in 42s
All checks were successful
Deploy to Production / test (push) Successful in 42s
This commit is contained in:
@@ -20,13 +20,15 @@ import { Plus, Trash2, Package, Coins, Sparkles, GripVertical, Percent } from "l
|
||||
import { cn } from "@/lib/utils";
|
||||
|
||||
// Loot drop types
|
||||
type LootType = "ITEM" | "BALANCE" | "XP";
|
||||
type LootType = "ITEM" | "CURRENCY" | "XP";
|
||||
|
||||
interface LootDrop {
|
||||
type: LootType;
|
||||
itemId?: number;
|
||||
itemName?: string;
|
||||
amount?: number | [number, number]; // Single value or [min, max]
|
||||
amount?: number;
|
||||
minAmount?: number;
|
||||
maxAmount?: number;
|
||||
weight: number;
|
||||
}
|
||||
|
||||
@@ -37,7 +39,7 @@ interface LootTableBuilderProps {
|
||||
|
||||
const LOOT_TYPES = [
|
||||
{ value: "ITEM" as LootType, label: "Item", icon: Package, color: "text-purple-400" },
|
||||
{ value: "BALANCE" as LootType, label: "Balance", icon: Coins, color: "text-amber-400" },
|
||||
{ value: "CURRENCY" as LootType, label: "Currency", icon: Coins, color: "text-amber-400" },
|
||||
{ value: "XP" as LootType, label: "XP", icon: Sparkles, color: "text-blue-400" },
|
||||
];
|
||||
|
||||
@@ -45,10 +47,10 @@ const getDefaultDrop = (type: LootType): LootDrop => {
|
||||
switch (type) {
|
||||
case "ITEM":
|
||||
return { type: "ITEM", itemId: 0, itemName: "", weight: 10 };
|
||||
case "BALANCE":
|
||||
return { type: "BALANCE", amount: [100, 500], weight: 30 };
|
||||
case "CURRENCY":
|
||||
return { type: "CURRENCY", minAmount: 100, maxAmount: 500, weight: 30 };
|
||||
case "XP":
|
||||
return { type: "XP", amount: [50, 200], weight: 20 };
|
||||
return { type: "XP", minAmount: 50, maxAmount: 200, weight: 20 };
|
||||
}
|
||||
};
|
||||
|
||||
@@ -57,7 +59,7 @@ export function LootTableBuilder({ pool, onChange }: LootTableBuilderProps) {
|
||||
const totalWeight = pool.reduce((sum, drop) => sum + drop.weight, 0);
|
||||
|
||||
const addDrop = useCallback(() => {
|
||||
onChange([...pool, getDefaultDrop("BALANCE")]);
|
||||
onChange([...pool, getDefaultDrop("CURRENCY")]);
|
||||
}, [pool, onChange]);
|
||||
|
||||
const removeDrop = useCallback((index: number) => {
|
||||
@@ -105,7 +107,7 @@ export function LootTableBuilder({ pool, onChange }: LootTableBuilderProps) {
|
||||
|
||||
const colors: Record<LootType, string> = {
|
||||
ITEM: "bg-purple-500",
|
||||
BALANCE: "bg-amber-500",
|
||||
CURRENCY: "bg-amber-500",
|
||||
XP: "bg-blue-500",
|
||||
};
|
||||
|
||||
@@ -206,17 +208,21 @@ export function LootTableBuilder({ pool, onChange }: LootTableBuilderProps) {
|
||||
</>
|
||||
)}
|
||||
|
||||
{(drop.type === "BALANCE" || drop.type === "XP") && (
|
||||
{(drop.type === "CURRENCY" || drop.type === "XP") && (
|
||||
<>
|
||||
<div className="space-y-1.5">
|
||||
<Label className="text-xs">Min Amount</Label>
|
||||
<Input
|
||||
type="number"
|
||||
value={Array.isArray(drop.amount) ? drop.amount[0] : drop.amount || 0}
|
||||
value={drop.minAmount ?? drop.amount ?? 0}
|
||||
onChange={(e) => {
|
||||
const min = parseInt(e.target.value) || 0;
|
||||
const max = Array.isArray(drop.amount) ? drop.amount[1] : min;
|
||||
updateDrop(index, { amount: [min, Math.max(min, max)] });
|
||||
const currentMax = drop.maxAmount ?? drop.amount ?? min;
|
||||
updateDrop(index, {
|
||||
minAmount: min,
|
||||
maxAmount: Math.max(min, currentMax),
|
||||
amount: undefined // Clear amount
|
||||
});
|
||||
}}
|
||||
className="bg-background/50 h-8 text-sm"
|
||||
/>
|
||||
@@ -225,11 +231,15 @@ export function LootTableBuilder({ pool, onChange }: LootTableBuilderProps) {
|
||||
<Label className="text-xs">Max Amount</Label>
|
||||
<Input
|
||||
type="number"
|
||||
value={Array.isArray(drop.amount) ? drop.amount[1] : drop.amount || 0}
|
||||
value={drop.maxAmount ?? drop.amount ?? 0}
|
||||
onChange={(e) => {
|
||||
const max = parseInt(e.target.value) || 0;
|
||||
const min = Array.isArray(drop.amount) ? drop.amount[0] : max;
|
||||
updateDrop(index, { amount: [Math.min(min, max), max] });
|
||||
const currentMin = drop.minAmount ?? drop.amount ?? max;
|
||||
updateDrop(index, {
|
||||
minAmount: Math.min(currentMin, max),
|
||||
maxAmount: max,
|
||||
amount: undefined // Clear amount
|
||||
});
|
||||
}}
|
||||
className="bg-background/50 h-8 text-sm"
|
||||
/>
|
||||
|
||||
Reference in New Issue
Block a user