Introduced a new endpoint for bulk filling collection posters, allowing for automatic assignment of posters to collections without existing images. Updated the DeleteShowCommand to include an option for cutting shows from future airings, enhancing the deletion process. Refactored related components and API calls to support these features, ensuring a seamless user experience. Additionally, updated localization strings to reflect the new functionalities and adjusted tests to verify correct behavior.
97 lines
3.6 KiB
TypeScript
97 lines
3.6 KiB
TypeScript
import { useState } from 'react'
|
|
import { useTranslation } from 'react-i18next'
|
|
import type { ShowSummaryDto } from '@/shared/api/types'
|
|
import { Button } from '@/shared/ui/button'
|
|
import {
|
|
Dialog,
|
|
DialogContent,
|
|
DialogDescription,
|
|
DialogFooter,
|
|
DialogHeader,
|
|
DialogTitle,
|
|
} from '@/shared/ui/dialog'
|
|
|
|
/**
|
|
* Подтверждение удаления шоу с выбором, что делать с его файлами.
|
|
*
|
|
* Галочка нужна потому, что медиа переживает шоу: серия ссылается на ассет по значению, внешнего
|
|
* ключа между ними нет, и после удаления файлы остались бы в библиотеке ничьими, занимая место.
|
|
* Спросить об этом здесь дешевле, чем потом искать осиротевшие гигабайты в списке медиа.
|
|
*/
|
|
export function DeleteShowDialog({
|
|
show,
|
|
pending,
|
|
onConfirm,
|
|
onClose,
|
|
}: Readonly<{
|
|
show: ShowSummaryDto
|
|
pending: boolean
|
|
onConfirm: (options: { withMedia: boolean; withSchedule: boolean }) => void
|
|
onClose: () => void
|
|
}>) {
|
|
const { t } = useTranslation()
|
|
const [withMedia, setWithMedia] = useState(false)
|
|
const [withSchedule, setWithSchedule] = useState(false)
|
|
const hasMedia = show.episodeCount > 0
|
|
|
|
return (
|
|
<Dialog open onOpenChange={(open) => !open && onClose()}>
|
|
<DialogContent>
|
|
<DialogHeader>
|
|
<DialogTitle>{t('admin.shows.deleteTitle')}</DialogTitle>
|
|
<DialogDescription>{t('admin.shows.deleteHint', { name: show.name })}</DialogDescription>
|
|
</DialogHeader>
|
|
|
|
{/* Занятость эфиром — самая частая причина отказа, и лечится она не всегда пересборкой:
|
|
идущую запись она не трогает, а выключенный канал вещать и не начнёт. Поэтому здесь
|
|
не только объяснение, но и способ довести удаление до конца. */}
|
|
<label className="flex items-start gap-2 text-sm">
|
|
<input
|
|
type="checkbox"
|
|
className="mt-1"
|
|
checked={withSchedule}
|
|
onChange={(e) => setWithSchedule(e.target.checked)}
|
|
/>
|
|
<span>
|
|
{t('admin.shows.deleteWithSchedule')}
|
|
<span className="block text-xs text-muted-foreground">
|
|
{t('admin.shows.deleteWithScheduleHint')}
|
|
</span>
|
|
</span>
|
|
</label>
|
|
|
|
{hasMedia && (
|
|
<label className="flex items-start gap-2 text-sm">
|
|
<input
|
|
type="checkbox"
|
|
className="mt-1"
|
|
checked={withMedia}
|
|
onChange={(e) => setWithMedia(e.target.checked)}
|
|
/>
|
|
<span>
|
|
{t('admin.shows.deleteWithMedia', { count: show.episodeCount })}
|
|
<span className="block text-xs text-muted-foreground">
|
|
{t('admin.shows.deleteWithMediaHint')}
|
|
</span>
|
|
</span>
|
|
</label>
|
|
)}
|
|
|
|
<DialogFooter>
|
|
<Button size="sm" variant="outline" onClick={onClose}>
|
|
{t('common.cancel')}
|
|
</Button>
|
|
<Button
|
|
size="sm"
|
|
variant="destructive"
|
|
disabled={pending}
|
|
onClick={() => onConfirm({ withMedia, withSchedule })}
|
|
>
|
|
{t('common.delete')}
|
|
</Button>
|
|
</DialogFooter>
|
|
</DialogContent>
|
|
</Dialog>
|
|
)
|
|
}
|