Add modals

This commit is contained in:
2025-12-16 01:50:40 +07:00
parent 87ecd9756c
commit 574140e67d
11 changed files with 439 additions and 40 deletions

View File

@@ -6,9 +6,13 @@ import { Button, Card, CardContent } from '@/components/ui'
import { SpinWheel } from '@/components/SpinWheel'
import { EventBanner } from '@/components/EventBanner'
import { Loader2, Upload, X, Gamepad2, ArrowLeftRight, Check, XCircle, Clock, Send, Trophy, Users, ArrowLeft, AlertTriangle } from 'lucide-react'
import { useToast } from '@/store/toast'
import { useConfirm } from '@/store/confirm'
export function PlayPage() {
const { id } = useParams<{ id: string }>()
const toast = useToast()
const confirm = useConfirm()
const [marathon, setMarathon] = useState<Marathon | null>(null)
const [currentAssignment, setCurrentAssignment] = useState<Assignment | null>(null)
@@ -99,7 +103,7 @@ export function PlayPage() {
setGameChoiceChallenges(challenges)
} catch (error) {
console.error('Failed to load game choice challenges:', error)
alert('Не удалось загрузить челленджи для этой игры')
toast.error('Не удалось загрузить челленджи для этой игры')
} finally {
setIsLoadingChallenges(false)
}
@@ -181,7 +185,7 @@ export function PlayPage() {
return result.game
} catch (err: unknown) {
const error = err as { response?: { data?: { detail?: string } } }
alert(error.response?.data?.detail || 'Не удалось крутить')
toast.error(error.response?.data?.detail || 'Не удалось крутить')
return null
}
}
@@ -196,7 +200,7 @@ export function PlayPage() {
const handleComplete = async () => {
if (!currentAssignment) return
if (!proofFile && !proofUrl) {
alert('Пожалуйста, предоставьте доказательство (файл или ссылку)')
toast.warning('Пожалуйста, предоставьте доказательство (файл или ссылку)')
return
}
@@ -208,7 +212,7 @@ export function PlayPage() {
comment: comment || undefined,
})
alert(`Выполнено! +${result.points_earned} очков (бонус серии: +${result.streak_bonus})`)
toast.success(`Выполнено! +${result.points_earned} очков (бонус серии: +${result.streak_bonus})`)
// Reset form
setProofFile(null)
@@ -219,7 +223,7 @@ export function PlayPage() {
await loadData()
} catch (err: unknown) {
const error = err as { response?: { data?: { detail?: string } } }
alert(error.response?.data?.detail || 'Не удалось выполнить')
toast.error(error.response?.data?.detail || 'Не удалось выполнить')
} finally {
setIsCompleting(false)
}
@@ -229,18 +233,25 @@ export function PlayPage() {
if (!currentAssignment) return
const penalty = spinResult?.drop_penalty || 0
if (!confirm(`Пропустить это задание? Вы потеряете ${penalty} очков.`)) return
const confirmed = await confirm({
title: 'Пропустить задание?',
message: `Вы потеряете ${penalty} очков.`,
confirmText: 'Пропустить',
cancelText: 'Отмена',
variant: 'warning',
})
if (!confirmed) return
setIsDropping(true)
try {
const result = await wheelApi.drop(currentAssignment.id)
alert(`Пропущено. Штраф: -${result.penalty} очков`)
toast.info(`Пропущено. Штраф: -${result.penalty} очков`)
setSpinResult(null)
await loadData()
} catch (err: unknown) {
const error = err as { response?: { data?: { detail?: string } } }
alert(error.response?.data?.detail || 'Не удалось пропустить')
toast.error(error.response?.data?.detail || 'Не удалось пропустить')
} finally {
setIsDropping(false)
}
@@ -249,7 +260,7 @@ export function PlayPage() {
const handleEventComplete = async () => {
if (!eventAssignment?.assignment) return
if (!eventProofFile && !eventProofUrl) {
alert('Пожалуйста, предоставьте доказательство (файл или ссылку)')
toast.warning('Пожалуйста, предоставьте доказательство (файл или ссылку)')
return
}
@@ -261,7 +272,7 @@ export function PlayPage() {
comment: eventComment || undefined,
})
alert(`Выполнено! +${result.points_earned} очков`)
toast.success(`Выполнено! +${result.points_earned} очков`)
// Reset form
setEventProofFile(null)
@@ -271,7 +282,7 @@ export function PlayPage() {
await loadData()
} catch (err: unknown) {
const error = err as { response?: { data?: { detail?: string } } }
alert(error.response?.data?.detail || 'Не удалось выполнить')
toast.error(error.response?.data?.detail || 'Не удалось выполнить')
} finally {
setIsEventCompleting(false)
}
@@ -286,22 +297,27 @@ export function PlayPage() {
if (!id) return
const hasActiveAssignment = !!currentAssignment
const confirmMessage = hasActiveAssignment
? 'Выбрать этот челлендж? Текущее задание будет заменено без штрафа.'
: 'Выбрать этот челлендж?'
if (!confirm(confirmMessage)) return
const confirmed = await confirm({
title: 'Выбрать челлендж?',
message: hasActiveAssignment
? 'Текущее задание будет заменено без штрафа.'
: 'Вы уверены, что хотите выбрать этот челлендж?',
confirmText: 'Выбрать',
cancelText: 'Отмена',
variant: 'info',
})
if (!confirmed) return
setIsSelectingChallenge(true)
try {
const result = await eventsApi.selectGameChoiceChallenge(parseInt(id), challengeId)
alert(result.message)
toast.success(result.message)
setSelectedGameId(null)
setGameChoiceChallenges(null)
await loadData()
} catch (err: unknown) {
const error = err as { response?: { data?: { detail?: string } } }
alert(error.response?.data?.detail || 'Не удалось выбрать челлендж')
toast.error(error.response?.data?.detail || 'Не удалось выбрать челлендж')
} finally {
setIsSelectingChallenge(false)
}
@@ -310,17 +326,24 @@ export function PlayPage() {
const handleSendSwapRequest = async (participantId: number, participantName: string, theirChallenge: string) => {
if (!id) return
if (!confirm(`Отправить запрос на обмен с ${participantName}?\n\nВы предлагаете обменяться на: "${theirChallenge}"\n\n${participantName} должен будет подтвердить обмен.`)) return
const confirmed = await confirm({
title: 'Отправить запрос на обмен?',
message: `Вы предлагаете обменяться с ${participantName} на:\n"${theirChallenge}"\n\n${participantName} должен будет подтвердить обмен.`,
confirmText: 'Отправить',
cancelText: 'Отмена',
variant: 'info',
})
if (!confirmed) return
setSendingRequestTo(participantId)
try {
await eventsApi.createSwapRequest(parseInt(id), participantId)
alert('Запрос на обмен отправлен! Ожидайте подтверждения.')
toast.success('Запрос на обмен отправлен! Ожидайте подтверждения.')
await loadSwapRequests()
await loadSwapCandidates()
} catch (err: unknown) {
const error = err as { response?: { data?: { detail?: string } } }
alert(error.response?.data?.detail || 'Не удалось отправить запрос')
toast.error(error.response?.data?.detail || 'Не удалось отправить запрос')
} finally {
setSendingRequestTo(null)
}
@@ -329,16 +352,23 @@ export function PlayPage() {
const handleAcceptSwapRequest = async (requestId: number) => {
if (!id) return
if (!confirm('Принять обмен? Задания будут обменяны сразу после подтверждения.')) return
const confirmed = await confirm({
title: 'Принять обмен?',
message: 'Задания будут обменяны сразу после подтверждения.',
confirmText: 'Принять',
cancelText: 'Отмена',
variant: 'info',
})
if (!confirmed) return
setProcessingRequestId(requestId)
try {
await eventsApi.acceptSwapRequest(parseInt(id), requestId)
alert('Обмен выполнен!')
toast.success('Обмен выполнен!')
await loadData()
} catch (err: unknown) {
const error = err as { response?: { data?: { detail?: string } } }
alert(error.response?.data?.detail || 'Не удалось выполнить обмен')
toast.error(error.response?.data?.detail || 'Не удалось выполнить обмен')
} finally {
setProcessingRequestId(null)
}
@@ -353,7 +383,7 @@ export function PlayPage() {
await loadSwapRequests()
} catch (err: unknown) {
const error = err as { response?: { data?: { detail?: string } } }
alert(error.response?.data?.detail || 'Не удалось отклонить запрос')
toast.error(error.response?.data?.detail || 'Не удалось отклонить запрос')
} finally {
setProcessingRequestId(null)
}
@@ -369,7 +399,7 @@ export function PlayPage() {
await loadSwapCandidates()
} catch (err: unknown) {
const error = err as { response?: { data?: { detail?: string } } }
alert(error.response?.data?.detail || 'Не удалось отменить запрос')
toast.error(error.response?.data?.detail || 'Не удалось отменить запрос')
} finally {
setProcessingRequestId(null)
}