Files
TeleWave/frontend/src/features/admin/maintenance/MaintenancePanel.tsx
T

133 lines
4.6 KiB
TypeScript

import { useMutation, useQuery, useQueryClient } from '@tanstack/react-query'
import { useState } from 'react'
import { useTranslation } from 'react-i18next'
import { AlertTriangle } from 'lucide-react'
import { HttpError } from '@/shared/api/client'
import { Button } from '@/shared/ui/button'
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/shared/ui/card'
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from '@/shared/ui/select'
import { toast } from '@/shared/ui/toast-store'
import { listShows } from '@/features/admin/shows/api'
import { clearAllMedia, deleteAllShows, deleteShowMedia } from './api'
export function MaintenancePanel() {
const { t } = useTranslation()
const queryClient = useQueryClient()
const [showId, setShowId] = useState('')
const { data: shows } = useQuery({ queryKey: ['admin', 'shows'], queryFn: () => listShows() })
const onError = (error: unknown) =>
toast.error(error instanceof HttpError ? error.detail : t('common.error'))
// Затрагиваются медиа/шоу/каналы — сбрасываем все связанные кэши.
const invalidateAll = () => {
for (const key of [['admin', 'media'], ['admin', 'shows'], ['admin', 'channels']])
void queryClient.invalidateQueries({ queryKey: key })
}
const clearMedia = useMutation({
mutationFn: clearAllMedia,
onSuccess: (count) => {
toast.success(t('admin.maintenance.doneCount', { count }))
invalidateAll()
},
onError,
})
const clearShows = useMutation({
mutationFn: deleteAllShows,
onSuccess: (count) => {
toast.success(t('admin.maintenance.doneCount', { count }))
invalidateAll()
},
onError,
})
const clearShowMedia = useMutation({
mutationFn: () => deleteShowMedia(showId),
onSuccess: (count) => {
setShowId('')
toast.success(t('admin.maintenance.doneCount', { count }))
invalidateAll()
},
onError,
})
const confirmed = (message: string, run: () => void) => {
if (window.confirm(message)) run()
}
return (
<div className="flex flex-col gap-4">
<div className="flex items-center gap-2">
<AlertTriangle className="h-5 w-5 text-red-500" />
<h2 className="crt-glow text-xl font-semibold">{t('admin.maintenance.title')}</h2>
</div>
<p className="text-sm text-muted-foreground">{t('admin.maintenance.warning')}</p>
<Card>
<CardHeader>
<CardTitle>{t('admin.maintenance.clearMedia')}</CardTitle>
<CardDescription>{t('admin.maintenance.clearMediaHint')}</CardDescription>
</CardHeader>
<CardContent>
<Button
variant="destructive"
disabled={clearMedia.isPending}
onClick={() => confirmed(t('admin.maintenance.confirmClearMedia'), () => clearMedia.mutate())}
>
{t('admin.maintenance.clearMedia')}
</Button>
</CardContent>
</Card>
<Card>
<CardHeader>
<CardTitle>{t('admin.maintenance.clearShowMedia')}</CardTitle>
<CardDescription>{t('admin.maintenance.clearShowMediaHint')}</CardDescription>
</CardHeader>
<CardContent className="flex flex-wrap items-center gap-2">
<Select value={showId} onValueChange={setShowId}>
<SelectTrigger className="max-w-xs">
<SelectValue placeholder={t('admin.maintenance.pickShow')} />
</SelectTrigger>
<SelectContent>
{shows?.map((s) => (
<SelectItem key={s.id} value={s.id}>
{s.name}
</SelectItem>
))}
</SelectContent>
</Select>
<Button
variant="destructive"
disabled={!showId || clearShowMedia.isPending}
onClick={() =>
confirmed(t('admin.maintenance.confirmClearShowMedia'), () => clearShowMedia.mutate())
}
>
{t('admin.maintenance.clearShowMedia')}
</Button>
</CardContent>
</Card>
<Card>
<CardHeader>
<CardTitle>{t('admin.maintenance.deleteShows')}</CardTitle>
<CardDescription>{t('admin.maintenance.deleteShowsHint')}</CardDescription>
</CardHeader>
<CardContent>
<Button
variant="destructive"
disabled={clearShows.isPending}
onClick={() => confirmed(t('admin.maintenance.confirmDeleteShows'), () => clearShows.mutate())}
>
{t('admin.maintenance.deleteShows')}
</Button>
</CardContent>
</Card>
</div>
)
}