Добавлен демо-режим: Можно открыть приложение с параметром ?demo в URL В демо-режиме эмулируется Telegram WebApp SDK Добавлена заглушка для демо-пользователя Исправлены типы и интерфейсы: Добавлен тип SafeUser для безопасной работы с mongoose документами Исправлены пропсы компонентов Обновлены типы для покупок в магазине Улучшена обработка ошибок: Добавлены информативные сообщения об ошибках Реализована корректная обработка сетевых ошибок Добавлены toast-уведомления с подробной информацией Улучшен UI: Добавлены спиннеры загрузки Улучшен внешний вид карточек товаров Добавлены информативные сообщения о состоянии
141 lines
4.4 KiB
TypeScript
141 lines
4.4 KiB
TypeScript
'use client';
|
||
|
||
import React, { useState, useEffect } from 'react';
|
||
import { Box, VStack, Spinner, Center, useToast } from '@chakra-ui/react';
|
||
import { UserProfile } from './UserProfile';
|
||
import { Shop } from './Shop';
|
||
import { TransferBalance } from './TransferBalance';
|
||
import { auth, getProfile, getShopItems, purchaseItem, transferBalance } from '../utils/api';
|
||
import { IUser } from '../../backend/models/User';
|
||
import { IShopItem } from '../../backend/models/ShopItem';
|
||
import { isDemoMode, getDemoWebApp } from '../utils/demo';
|
||
|
||
type SafeUser = Omit<IUser, keyof Document>;
|
||
|
||
export default function MainApp() {
|
||
const [isLoading, setIsLoading] = useState(true);
|
||
const [user, setUser] = useState<SafeUser | null>(null);
|
||
const [shopItems, setShopItems] = useState<IShopItem[]>([]);
|
||
const toast = useToast();
|
||
|
||
const initApp = async () => {
|
||
try {
|
||
setIsLoading(true);
|
||
|
||
let webApp;
|
||
if (isDemoMode()) {
|
||
webApp = getDemoWebApp();
|
||
} else {
|
||
const WebApp = (await import('@twa-dev/sdk')).default;
|
||
webApp = WebApp;
|
||
}
|
||
|
||
// Авторизация пользователя
|
||
const { user: telegramUser } = webApp.initDataUnsafe;
|
||
const authResponse = await auth(telegramUser.id.toString(), telegramUser.username || 'anonymous');
|
||
|
||
// Получение данных пользователя и магазина
|
||
const [profileData, shopData] = await Promise.all([
|
||
getProfile(),
|
||
getShopItems()
|
||
]);
|
||
|
||
setUser(profileData as SafeUser);
|
||
setShopItems(shopData);
|
||
} catch (error: any) {
|
||
console.error('Initialization error:', error);
|
||
toast({
|
||
title: 'Ошибка инициализации',
|
||
description: error.message || 'Произошла ошибка при загрузке приложения',
|
||
status: 'error',
|
||
duration: 5000,
|
||
isClosable: true,
|
||
});
|
||
} finally {
|
||
setIsLoading(false);
|
||
}
|
||
};
|
||
|
||
useEffect(() => {
|
||
initApp();
|
||
}, []);
|
||
|
||
if (isLoading) {
|
||
return (
|
||
<Center h="100vh">
|
||
<Spinner size="xl" />
|
||
</Center>
|
||
);
|
||
}
|
||
|
||
if (!user) {
|
||
return (
|
||
<Center h="100vh">
|
||
<Box p={4} textAlign="center">
|
||
Пожалуйста, авторизуйтесь через Telegram или используйте демо-режим
|
||
</Box>
|
||
</Center>
|
||
);
|
||
}
|
||
|
||
return (
|
||
<VStack spacing={6} p={4}>
|
||
<UserProfile
|
||
username={user.username}
|
||
level={user.level}
|
||
experience={user.experience}
|
||
balance={user.balance}
|
||
achievements={user.achievements}
|
||
/>
|
||
<Shop
|
||
items={shopItems}
|
||
userBalance={user.balance}
|
||
onPurchase={async (item: IShopItem) => {
|
||
try {
|
||
const response = await purchaseItem(item._id.toString());
|
||
setUser(response.user as SafeUser);
|
||
toast({
|
||
title: 'Успешная покупка',
|
||
description: `Вы приобрели ${item.name}`,
|
||
status: 'success',
|
||
duration: 3000,
|
||
isClosable: true,
|
||
});
|
||
} catch (error: any) {
|
||
toast({
|
||
title: 'Ошибка покупки',
|
||
description: error.message || 'Произошла ошибка при покупке',
|
||
status: 'error',
|
||
duration: 3000,
|
||
isClosable: true,
|
||
});
|
||
}
|
||
}}
|
||
/>
|
||
<TransferBalance
|
||
userBalance={user.balance}
|
||
onTransfer={async (username, amount) => {
|
||
try {
|
||
const response = await transferBalance(username, amount);
|
||
setUser(prev => ({ ...prev!, balance: response.balance } as SafeUser));
|
||
toast({
|
||
title: 'Успешный перевод',
|
||
description: `Вы перевели ${amount} монет пользователю ${username}`,
|
||
status: 'success',
|
||
duration: 3000,
|
||
isClosable: true,
|
||
});
|
||
} catch (error: any) {
|
||
toast({
|
||
title: 'Ошибка перевода',
|
||
description: error.message || 'Произошла ошибка при переводе',
|
||
status: 'error',
|
||
duration: 3000,
|
||
isClosable: true,
|
||
});
|
||
}
|
||
}}
|
||
/>
|
||
</VStack>
|
||
);
|
||
}
|