"use client"; import { useCallback, useEffect, useState } from "react"; import { TintedSpriteIcon } from "@/components/character/tinted-sprite-icon"; import { Badge } from "@/components/ui/badge"; import { Button } from "@/components/ui/button"; import { Card, CardContent, CardDescription, CardHeader, CardTitle, } from "@/components/ui/card"; import { useToast } from "@/components/ui/toast-provider"; import { HABIT_PET_DATA_UPDATED_EVENT } from "@/lib/app-events"; import { DEFAULT_GRAY_COLOR } from "@/lib/character/presets"; import { equipShopItem, getShopInventory, purchaseShopItem, unequipShopItem, } from "@/lib/shop-storage"; import { getLayerLabel, type ShopItemRecord, type ShopLayerId, } from "@/lib/shop-catalog"; const SHOP_LAYER_ORDER: ShopLayerId[] = [ "head", "torso", "pants", "shoes", "eyes", ]; export function ShopContent() { const { toast } = useToast(); const [items, setItems] = useState([]); const [ownedItemIds, setOwnedItemIds] = useState([]); const [equippedItems, setEquippedItems] = useState([]); const [coins, setCoins] = useState(null); const [error, setError] = useState(null); const [pendingItemId, setPendingItemId] = useState(null); const refreshShop = useCallback(async () => { try { setError(null); const inventory = await getShopInventory(); setItems(inventory.items); setOwnedItemIds(inventory.ownedItemIds); setEquippedItems(inventory.equippedItems); setCoins(inventory.coins); } catch (refreshError) { setError( refreshError instanceof Error ? refreshError.message : "Could not load the shop.", ); setCoins(0); } }, []); useEffect(() => { void refreshShop(); window.addEventListener(HABIT_PET_DATA_UPDATED_EVENT, refreshShop); return () => { window.removeEventListener(HABIT_PET_DATA_UPDATED_EVENT, refreshShop); }; }, [refreshShop]); const handlePurchase = async (itemId: string) => { setPendingItemId(itemId); try { const item = await purchaseShopItem(itemId); toast(`Purchased ${item.name}.`, "success"); await refreshShop(); } catch (purchaseError) { toast( purchaseError instanceof Error ? purchaseError.message : "Purchase failed.", "error", ); } finally { setPendingItemId(null); } }; const handleEquipToggle = async (item: ShopItemRecord) => { setPendingItemId(item.id); try { const isEquipped = equippedItems.includes(item.id); if (isEquipped) { await unequipShopItem(item.id); toast(`Unequipped ${item.name}.`, "default"); } else { await equipShopItem(item.id); toast(`Equipped ${item.name}.`, "success"); } await refreshShop(); } catch (equipError) { toast( equipError instanceof Error ? equipError.message : "Could not equip item.", "error", ); } finally { setPendingItemId(null); } }; const groupedItems = SHOP_LAYER_ORDER.map((layerId) => ({ layerId, label: getLayerLabel(layerId), items: items.filter((item) => item.type === layerId), })).filter((group) => group.items.length > 0); return ( <>
Your balance {coins === null ? "..." : `${coins} points`}
{error ?

{error}

: null} {!error && coins !== null && groupedItems.length === 0 ? (

Nothing in the shop right now. Keep completing habits and daily check-ins to earn points for when new styles arrive.

) : null}
{groupedItems.map((group) => (

{group.label}

{group.items.map((item) => { const owned = ownedItemIds.includes(item.id); const equipped = equippedItems.includes(item.id); const canAfford = coins !== null && coins >= item.price; const isPending = pendingItemId === item.id; return (
{item.name} {item.id}
{item.price} pts
{!owned ? ( ) : ( )}
); })}
))}
); }