CampFireID/app/components/MainApp.tsx
degradin 2af270e3d8 Global fixes
Добавлен демо-режим:
Можно открыть приложение с параметром ?demo в URL
В демо-режиме эмулируется Telegram WebApp SDK
Добавлена заглушка для демо-пользователя
Исправлены типы и интерфейсы:
Добавлен тип SafeUser для безопасной работы с mongoose документами
Исправлены пропсы компонентов
Обновлены типы для покупок в магазине
Улучшена обработка ошибок:
Добавлены информативные сообщения об ошибках
Реализована корректная обработка сетевых ошибок
Добавлены toast-уведомления с подробной информацией
Улучшен UI:
Добавлены спиннеры загрузки
Улучшен внешний вид карточек товаров
Добавлены информативные сообщения о состоянии
2025-03-16 12:05:42 +03:00

141 lines
4.4 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, { 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>
);
}