// src/renderer/pages/DailyQuests.tsx import React, { useEffect, useMemo, useState } from 'react'; import { Alert, Box, Button, Chip, Divider, LinearProgress, Paper, Stack, Typography, } from '@mui/material'; import CoinsDisplay from '../components/CoinsDisplay'; import { FullScreenLoader } from '../components/FullScreenLoader'; import { claimDailyQuest, fetchDailyQuestsStatus, DailyQuestsStatusResponse } from '../api'; function formatHHMMSS(totalSeconds: number) { const s = Math.max(0, Math.floor(totalSeconds)); const hh = String(Math.floor(s / 3600)).padStart(2, '0'); const mm = String(Math.floor((s % 3600) / 60)).padStart(2, '0'); const ss = String(s % 60).padStart(2, '0'); return `${hh}:${mm}:${ss}`; } type Quest = { key: string; title: string; event?: string; target?: string; required: number; progress: number; reward: number; status: 'active' | 'completed' | 'claimed'; claimed_at?: string; completed_at?: string; }; type DailyQuestsStatusCompat = DailyQuestsStatusResponse & { was_online_today?: boolean; seconds_to_next?: number; next_reset_at_utc?: string; next_reset_at_local?: string; quests?: Quest[]; }; function statusChip(status: Quest['status']) { if (status === 'claimed') return ; if (status === 'completed') return ; return ; } export default function DailyQuests() { const [status, setStatus] = useState(null); const [pageLoading, setPageLoading] = useState(true); const [actionLoadingKey, setActionLoadingKey] = useState(''); const [tick, setTick] = useState(0); const [error, setError] = useState(''); const [success, setSuccess] = useState(''); const loadStatus = async () => { setError(''); try { const s = (await fetchDailyQuestsStatus()) as DailyQuestsStatusCompat; setStatus(s); } catch (e) { setError(e instanceof Error ? e.message : 'Ошибка загрузки ежедневных заданий'); } }; useEffect(() => { (async () => { setPageLoading(true); await loadStatus(); setPageLoading(false); })(); }, []); useEffect(() => { const id = setInterval(() => setTick((x) => x + 1), 1000); return () => clearInterval(id); }, []); const wasOnlineToday = status?.was_online_today ?? false; const clientSecondsLeft = useMemo(() => { if (!status) return 0; return Math.max(0, (status.seconds_to_next ?? 0) - tick); }, [status, tick]); const subtitle = useMemo(() => { if (!status) return ''; if (!wasOnlineToday) return 'Награды откроются после входа на сервер сегодня.'; return `До обновления заданий: ${formatHHMMSS(clientSecondsLeft)}`; }, [status, wasOnlineToday, clientSecondsLeft]); const quests: Quest[] = useMemo(() => (status?.quests ?? []) as Quest[], [status]); const totalRewardLeft = useMemo(() => { // сколько ещё можно забрать сегодня (completed, но не claimed) return quests .filter((q) => q.status === 'completed') .reduce((sum, q) => sum + (q.reward ?? 0), 0); }, [quests]); const handleClaim = async (questKey: string) => { setActionLoadingKey(questKey); setError(''); setSuccess(''); try { const res = await claimDailyQuest(questKey); if (res.claimed) { const added = res.coins_added ?? 0; setSuccess(`Вы получили ${added} монет!`); } else { if (res.reason === 'not_online_today') { setError('Чтобы забрать награду — зайдите на сервер сегодня хотя бы на минуту.'); } else if (res.reason === 'not_completed') { setError('Сначала выполните задание, затем заберите награду.'); } else if (res.reason === 'already_claimed') { setError('Награда уже получена.'); } else { setError(res.message || res.reason || 'Награда недоступна'); } } await loadStatus(); setTick(0); } catch (e) { setError(e instanceof Error ? e.message : 'Ошибка при получении награды'); } finally { setActionLoadingKey(''); } }; if (pageLoading) { return ( ); } return ( {/* sticky header */} {error && ( {error} )} {success && ( {success} )} {subtitle} Можно забрать сегодня: {/* content */} {!wasOnlineToday && ( Зайдите на сервер сегодня, чтобы открыть получение наград за квесты. )} {quests.length === 0 ? ( На сегодня заданий нет. ) : ( {quests.map((q) => { const req = Math.max(1, q.required ?? 1); const prog = Math.max(0, q.progress ?? 0); const pct = Math.min(100, (prog / req) * 100); const canClaim = wasOnlineToday && q.status === 'completed'; const disabled = !canClaim || actionLoadingKey === q.key; return ( {q.title} Прогресс: {Math.min(prog, req)}/{req} {statusChip(q.status)} ); })} )} ); }