CampFireID/app/components/Shop.tsx
degradin 22cc9ef144 SSR Fixes
Добавили динамический импорт Telegram Web App SDK с помощью import(), чтобы он загружался только на клиенте
Добавили состояние загрузки и компонент Spinner для лучшего UX
Исправили типы в компонентах:
Используем IShopItem вместо собственного интерфейса ShopItem
Создали тип SafeUser, который исключает свойства mongoose Document из типа пользователя
Добавили безопасную проверку на наличие пользователя в данных Telegram WebApp
2025-03-16 11:37:54 +03:00

109 lines
2.9 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

'use client';
import React from 'react';
import {
Box,
Grid,
Text,
Button,
Image,
VStack,
useToast,
useColorModeValue,
} from '@chakra-ui/react';
import { IShopItem } from '../../backend/models/ShopItem';
interface ShopProps {
items: IShopItem[];
userBalance: number;
onPurchase: (itemId: string) => Promise<void>;
}
export const Shop: React.FC<ShopProps> = ({ items, userBalance, onPurchase }) => {
const toast = useToast();
const bgColor = useColorModeValue('white', 'gray.800');
const borderColor = useColorModeValue('gray.200', 'gray.700');
const handlePurchase = async (item: IShopItem) => {
if (userBalance < item.price) {
toast({
title: 'Недостаточно средств',
description: 'У вас недостаточно Campfire монет для покупки этого предмета',
status: 'error',
duration: 3000,
isClosable: true,
});
return;
}
try {
await onPurchase(item.id);
toast({
title: 'Покупка успешна!',
description: `Вы приобрели ${item.name}`,
status: 'success',
duration: 3000,
isClosable: true,
});
} catch (error) {
toast({
title: 'Ошибка при покупке',
description: 'Произошла ошибка при совершении покупки',
status: 'error',
duration: 3000,
isClosable: true,
});
}
};
return (
<Box p={4}>
<Text fontSize="2xl" fontWeight="bold" mb={4}>
Магазин
</Text>
<Text mb={4}>
Ваш баланс: {userBalance} 🔥
</Text>
<Grid templateColumns={['1fr', 'repeat(2, 1fr)', 'repeat(3, 1fr)']} gap={4}>
{items.map((item) => (
<Box
key={item.id}
p={4}
borderWidth="1px"
borderRadius="lg"
borderColor={borderColor}
bg={bgColor}
>
<VStack spacing={3}>
{item.imageUrl && (
<Image
src={item.imageUrl}
alt={item.name}
boxSize="100px"
objectFit="cover"
borderRadius="md"
/>
)}
<Text fontWeight="bold">{item.name}</Text>
<Text fontSize="sm" color="gray.500">
{item.description}
</Text>
<Text color="green.500" fontWeight="bold">
{item.price} 🔥
</Text>
<Button
colorScheme="blue"
width="full"
onClick={() => handlePurchase(item)}
isDisabled={userBalance < item.price}
>
Купить
</Button>
</VStack>
</Box>
))}
</Grid>
</Box>
);
};