import { Box, Typography, Button, Grid, Card, CardMedia, CardContent, Snackbar, Alert, Dialog, DialogContent, } from '@mui/material'; import CapeCard from '../components/CapeCard'; import { Cape, fetchCapes, fetchCapesStore, purchaseCape, StoreCape, Case, CaseItem, fetchCases, fetchCase, openCase, Server, } from '../api'; import { useEffect, useState } from 'react'; import { FullScreenLoader } from '../components/FullScreenLoader'; import { getPlayerServer } from '../utils/playerOnlineCheck'; import CaseRoulette from '../components/CaseRoulette'; import CoinsDisplay from '../components/CoinsDisplay'; function getRarityByWeight( weight?: number, ): 'common' | 'rare' | 'epic' | 'legendary' { if (weight === undefined || weight === null) return 'common'; if (weight <= 5) return 'legendary'; if (weight <= 20) return 'epic'; if (weight <= 50) return 'rare'; return 'common'; } function getRarityColor(weight?: number): string { const rarity = getRarityByWeight(weight); switch (rarity) { case 'legendary': return 'rgba(255, 215, 0, 1)'; // золотой case 'epic': return 'rgba(186, 85, 211, 1)'; // фиолетовый case 'rare': return 'rgba(65, 105, 225, 1)'; // синий case 'common': default: return 'rgba(255, 255, 255, 0.25)'; // сероватый } } export default function Shop() { const [storeCapes, setStoreCapes] = useState([]); const [userCapes, setUserCapes] = useState([]); const [username, setUsername] = useState(''); const [uuid, setUuid] = useState(''); const [loading, setLoading] = useState(false); // Кейсы const [cases, setCases] = useState([]); const [casesLoading, setCasesLoading] = useState(false); // Онлайн/сервер (по аналогии с Marketplace) const [isOnline, setIsOnline] = useState(false); const [playerServer, setPlayerServer] = useState(null); const [onlineCheckLoading, setOnlineCheckLoading] = useState(true); // Рулетка const [isOpening, setIsOpening] = useState(false); const [selectedCase, setSelectedCase] = useState(null); const [rouletteOpen, setRouletteOpen] = useState(false); const [rouletteCaseItems, setRouletteCaseItems] = useState([]); const [rouletteReward, setRouletteReward] = useState(null); // Уведомления const [notification, setNotification] = useState<{ open: boolean; message: string; type: 'success' | 'error'; }>({ open: false, message: '', type: 'success', }); const VISIBLE_ITEMS = 21; // сколько элементов в линии const CENTER_INDEX = Math.floor(VISIBLE_ITEMS / 2); // Функция для загрузки плащей из магазина const loadStoreCapes = async () => { try { const capes = await fetchCapesStore(); setStoreCapes(capes); } catch (error) { console.error('Ошибка при получении плащей магазина:', error); setStoreCapes([]); } }; // Функция для загрузки плащей пользователя const loadUserCapes = async (username: string) => { try { const userCapes = await fetchCapes(username); setUserCapes(userCapes); } catch (error) { console.error('Ошибка при получении плащей пользователя:', error); setUserCapes([]); } }; const handlePurchaseCape = async (cape_id: string) => { try { await purchaseCape(username, cape_id); await loadUserCapes(username); setNotification({ open: true, message: 'Плащ успешно куплен!', type: 'success', }); } catch (error) { console.error('Ошибка при покупке плаща:', error); setNotification({ open: true, message: error instanceof Error ? error.message : 'Ошибка при покупке плаща', type: 'error', }); } }; // Загрузка кейсов const loadCases = async () => { try { setCasesLoading(true); const casesData = await fetchCases(); setCases(casesData); } catch (error) { console.error('Ошибка при получении кейсов:', error); setCases([]); } finally { setCasesLoading(false); } }; // Проверка онлайна игрока (по аналогии с Marketplace.tsx) const checkPlayerStatus = async () => { if (!username) return; try { setOnlineCheckLoading(true); const { online, server } = await getPlayerServer(username); setIsOnline(online); setPlayerServer(server || null); } catch (error) { console.error('Ошибка при проверке онлайн-статуса:', error); setIsOnline(false); setPlayerServer(null); } finally { setOnlineCheckLoading(false); } }; // Загружаем базовые данные при монтировании useEffect(() => { const savedConfig = localStorage.getItem('launcher_config'); if (savedConfig) { const config = JSON.parse(savedConfig); if (config.uuid && config.username) { setUsername(config.username); setUuid(config.uuid); setLoading(true); Promise.all([ loadStoreCapes(), loadUserCapes(config.username), loadCases(), ]) .catch((err) => console.error(err)) .finally(() => { setLoading(false); }); } } }, []); // Проверяем онлайн после того, как знаем username useEffect(() => { if (username) { checkPlayerStatus(); } }, [username]); // Фильтруем плащи, которые уже куплены пользователем const availableCapes = storeCapes.filter( (storeCape) => !userCapes.some((userCape) => userCape.cape_id === storeCape.id), ); // Генерация массива предметов для рулетки const generateRouletteItems = ( allItems: Case['items'], winningItemMaterial: string, ): Case['items'] => { if (!allItems || allItems.length === 0) return []; const result: Case['items'] = []; for (let i = 0; i < VISIBLE_ITEMS; i++) { const randomItem = allItems[Math.floor(Math.random() * allItems.length)]; result.push(randomItem); } // Принудительно ставим выигрышный предмет в центр const winningSource = allItems.find((i) => i.material === winningItemMaterial) || allItems[0]; result[CENTER_INDEX] = winningSource; return result; }; const handleOpenCase = async (caseData: Case) => { if (!username) { setNotification({ open: true, message: 'Не найдено имя игрока. Авторизуйтесь в лаунчере.', type: 'error', }); return; } if (!isOnline || !playerServer) { setNotification({ open: true, message: 'Для открытия кейсов необходимо находиться на сервере в игре.', type: 'error', }); return; } if (isOpening) return; try { setIsOpening(true); // 1. получаем полный кейс const fullCase = await fetchCase(caseData.id); const caseItems: CaseItem[] = fullCase.items || []; setSelectedCase(fullCase); // 2. открываем кейс на бэке const result = await openCase(fullCase.id, username, playerServer.id); // 3. сохраняем данные для рулетки setRouletteCaseItems(caseItems); setRouletteReward(result.reward); setRouletteOpen(true); // 4. уведомление setNotification({ open: true, message: result.message || 'Кейс открыт!', type: 'success', }); setIsOpening(false); } catch (error) { console.error('Ошибка при открытии кейса:', error); setNotification({ open: true, message: error instanceof Error ? error.message : 'Ошибка при открытии кейса', type: 'error', }); setIsOpening(false); } }; const handleCloseNotification = () => { setNotification((prev) => ({ ...prev, open: false })); }; const handleCloseRoulette = () => { setRouletteOpen(false); }; return ( {(loading || onlineCheckLoading) && ( )} {!loading && !onlineCheckLoading && ( {/* Блок кейсов */} Кейсы {!isOnline && ( Для открытия кейсов вам необходимо находиться на одном из серверов игры. Зайдите в игру и обновите страницу. )} {casesLoading ? ( ) : cases.length > 0 ? ( {cases.map((c) => ( {/* НОВАЯ КАРТОЧКА */} {/* верхний “свет” */} {c.image_url && ( {/* маленький бейдж сверху картинки */} Кейс )} {c.name} {c.description && ( {c.description} )} Цена {typeof c.items_count === 'number' && ( Предметов в кейсе: {c.items_count} )} ))} ) : ( Кейсы временно недоступны. )} {/* Блок плащей (как был) */} Доступные плащи {availableCapes.length > 0 ? ( {availableCapes.map((cape) => ( ))} ) : ( У вас уже есть все доступные плащи! )} )} {/* Компонент с анимацией рулетки */} {/* Уведомления */} {notification.message} ); }