// 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)}
);
})}
)}
);
}