@@ -8,8 +8,10 @@ import { Button } from "@/components/ui/button";
88import { Card , CardContent } from "@/components/ui/card" ;
99import { useToast } from "@/components/ui/toast-provider" ;
1010import { HABIT_PET_DATA_UPDATED_EVENT } from "@/lib/app-events" ;
11+ import { getAvatarCustomization } from "@/lib/avatar-customization-storage" ;
12+ import type { AvatarCustomization } from "@/lib/avatar-customization-storage" ;
1113import { DEFAULT_GRAY_COLOR } from "@/lib/character/presets" ;
12- import { getRoomBackground } from "@/lib/room-backgrounds" ;
14+ import { getRoomBackground , normalizeRoomBackgroundId } from "@/lib/room-backgrounds" ;
1315import {
1416 equipShopItem ,
1517 getShopInventory ,
@@ -23,6 +25,8 @@ import {
2325} from "@/lib/shop-catalog" ;
2426import { cn } from "@/lib/utils" ;
2527
28+ import { ShopItemPreviewModal } from "@/components/shop/shop-item-preview-modal" ;
29+
2630const SHOP_LAYER_ORDER : ShopLayerId [ ] = [
2731 "head" ,
2832 "torso" ,
@@ -39,6 +43,7 @@ function ShopItemCard({
3943 isPending,
4044 onPurchase,
4145 onEquipToggle,
46+ onPreview,
4247} : {
4348 item : ShopItemRecord ;
4449 owned : boolean ;
@@ -47,6 +52,7 @@ function ShopItemCard({
4752 isPending : boolean ;
4853 onPurchase : ( ) => void ;
4954 onEquipToggle : ( ) => void ;
55+ onPreview : ( ) => void ;
5056} ) {
5157 const room = item . type === "room" ? getRoomBackground ( item . id ) : null ;
5258
@@ -101,16 +107,29 @@ function ShopItemCard({
101107 </ div >
102108 </ div >
103109
104- < Button
105- className = "mt-auto h-auto min-h-9 w-full whitespace-normal px-2 py-2 text-center text-[10px] leading-snug"
106- variant = { owned && equipped ? "default" : "outline" }
107- disabled = {
108- isPending || ( ! owned && ! canAfford ) || ( owned && item . type === "room" && equipped )
109- }
110- onClick = { owned ? onEquipToggle : onPurchase }
111- >
112- { actionLabel }
113- </ Button >
110+ < div className = "mt-auto grid grid-cols-2 gap-2" >
111+ < Button
112+ type = "button"
113+ className = "h-auto min-h-9 whitespace-normal px-2 py-2 text-[10px] leading-snug"
114+ variant = "outline"
115+ disabled = { isPending }
116+ onClick = { onPreview }
117+ >
118+ Preview
119+ </ Button >
120+ < Button
121+ className = "h-auto min-h-9 whitespace-normal px-2 py-2 text-[10px] leading-snug"
122+ variant = { owned && equipped ? "default" : "outline" }
123+ disabled = {
124+ isPending ||
125+ ( ! owned && ! canAfford ) ||
126+ ( owned && item . type === "room" && equipped )
127+ }
128+ onClick = { owned ? onEquipToggle : onPurchase }
129+ >
130+ { actionLabel }
131+ </ Button >
132+ </ div >
114133 </ CardContent >
115134 </ Card >
116135 ) ;
@@ -125,16 +144,27 @@ export function ShopContent() {
125144 const [ coins , setCoins ] = useState < number | null > ( null ) ;
126145 const [ error , setError ] = useState < string | null > ( null ) ;
127146 const [ pendingItemId , setPendingItemId ] = useState < string | null > ( null ) ;
147+ const [ baseCustomization , setBaseCustomization ] =
148+ useState < AvatarCustomization | null > ( null ) ;
149+ const [ previewItem , setPreviewItem ] = useState < ShopItemRecord | null > ( null ) ;
128150
129151 const refreshShop = useCallback ( async ( ) => {
130152 try {
131153 setError ( null ) ;
132- const inventory = await getShopInventory ( ) ;
154+ const [ inventory , customization ] = await Promise . all ( [
155+ getShopInventory ( ) ,
156+ getAvatarCustomization ( ) ,
157+ ] ) ;
133158 setItems ( inventory . items ) ;
134159 setOwnedItemIds ( inventory . ownedItemIds ) ;
135160 setEquippedItems ( inventory . equippedItems ) ;
136161 setEquippedRoomBackground ( inventory . equippedRoomBackground ) ;
137162 setCoins ( inventory . coins ) ;
163+ setBaseCustomization ( {
164+ ...customization ,
165+ equippedItems : inventory . equippedItems ,
166+ roomBackground : normalizeRoomBackgroundId ( inventory . equippedRoomBackground ) ,
167+ } ) ;
138168 } catch ( refreshError ) {
139169 setError (
140170 refreshError instanceof Error
@@ -214,6 +244,12 @@ export function ShopContent() {
214244
215245 return (
216246 < >
247+ < ShopItemPreviewModal
248+ item = { previewItem }
249+ baseCustomization = { baseCustomization }
250+ onClose = { ( ) => setPreviewItem ( null ) }
251+ />
252+
217253 < div className = "mb-5 flex items-center justify-between gap-3 border-2 border-border bg-muted/40 px-4 py-3 shadow-[var(--retro-shadow-sm)]" >
218254 < span className = "text-[10px] uppercase tracking-wider text-muted-foreground" >
219255 Your balance
@@ -253,6 +289,7 @@ export function ShopContent() {
253289 isPending = { pendingItemId === item . id }
254290 onPurchase = { ( ) => void handlePurchase ( item . id ) }
255291 onEquipToggle = { ( ) => void handleEquipToggle ( item ) }
292+ onPreview = { ( ) => setPreviewItem ( item ) }
256293 />
257294 ) ) }
258295 </ div >
@@ -275,6 +312,7 @@ export function ShopContent() {
275312 isPending = { pendingItemId === item . id }
276313 onPurchase = { ( ) => void handlePurchase ( item . id ) }
277314 onEquipToggle = { ( ) => void handleEquipToggle ( item ) }
315+ onPreview = { ( ) => setPreviewItem ( item ) }
278316 />
279317 ) ) }
280318 </ div >
0 commit comments