Updated the BumpersPanel, ChannelsPanel, CollectionsPanel, GroupsPanel, ClipGroupPanel, and ShowsPanel components to replace direct input fields with a CreateNameDialog for creating new items. This change enhances user experience by centralizing the creation process and improving UI consistency. Localization strings were also updated to reflect new dialog titles and labels in both English and Russian.
124 lines
4.5 KiB
TypeScript
124 lines
4.5 KiB
TypeScript
import { useMutation } from '@tanstack/react-query'
|
|
import { useState } from 'react'
|
|
import { useTranslation } from 'react-i18next'
|
|
import {
|
|
AUDIENCE_UNSET,
|
|
SHOW_AUDIENCES,
|
|
type ShowAudience,
|
|
type ShowKind,
|
|
} from '@/shared/api/types'
|
|
import { useApiError } from '@/shared/lib/use-api-error'
|
|
import { Button } from '@/shared/ui/button'
|
|
import { Dialog, DialogContent, DialogFooter, DialogHeader, DialogTitle } from '@/shared/ui/dialog'
|
|
import { Input } from '@/shared/ui/input'
|
|
import { Label } from '@/shared/ui/label'
|
|
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from '@/shared/ui/select'
|
|
import { createShow } from './api'
|
|
|
|
/**
|
|
* Заведение шоу. Отдельным окном, а не строкой над таблицей: поля создания стояли вплотную к полям
|
|
* фильтра, и в них постоянно начинали искать.
|
|
*/
|
|
export function CreateShowDialog({
|
|
onClose,
|
|
onCreated,
|
|
}: Readonly<{ onClose: () => void; onCreated: () => void }>) {
|
|
const { t } = useTranslation()
|
|
const onError = useApiError()
|
|
|
|
const [name, setName] = useState('')
|
|
const [originalName, setOriginalName] = useState('')
|
|
const [kind, setKind] = useState<ShowKind>('Series')
|
|
// Новое шоу заводится без рейтинга: проставят метаданные либо админ руками.
|
|
const [audience, setAudience] = useState<ShowAudience | null>(null)
|
|
|
|
const createMutation = useMutation({
|
|
mutationFn: () =>
|
|
createShow({
|
|
name: name.trim(),
|
|
kind,
|
|
originalName: originalName.trim() || undefined,
|
|
audience,
|
|
}),
|
|
onSuccess: () => {
|
|
onCreated()
|
|
onClose()
|
|
},
|
|
onError,
|
|
})
|
|
|
|
const submit = () => {
|
|
if (name.trim() && !createMutation.isPending) createMutation.mutate()
|
|
}
|
|
|
|
return (
|
|
<Dialog open onOpenChange={(open) => !open && onClose()}>
|
|
<DialogContent className="max-w-lg">
|
|
<DialogHeader>
|
|
<DialogTitle>{t('admin.shows.createTitle')}</DialogTitle>
|
|
</DialogHeader>
|
|
|
|
<div className="flex flex-col gap-3">
|
|
<div className="flex flex-col gap-1.5">
|
|
<Label>{t('admin.shows.name')}</Label>
|
|
<Input
|
|
autoFocus
|
|
value={name}
|
|
onChange={(e) => setName(e.target.value)}
|
|
// Enter в единственном обязательном поле — самый быстрый путь: форма короткая.
|
|
onKeyDown={(e) => e.key === 'Enter' && submit()}
|
|
/>
|
|
</div>
|
|
|
|
<div className="flex flex-col gap-1.5">
|
|
<Label>{t('admin.shows.originalName')}</Label>
|
|
<Input value={originalName} onChange={(e) => setOriginalName(e.target.value)} />
|
|
</div>
|
|
|
|
<div className="flex flex-col gap-1.5">
|
|
<Label>{t('admin.shows.kind')}</Label>
|
|
<Select value={kind} onValueChange={(v) => setKind(v as ShowKind)}>
|
|
<SelectTrigger>
|
|
<SelectValue />
|
|
</SelectTrigger>
|
|
<SelectContent>
|
|
<SelectItem value="Series">{t('admin.shows.kinds.Series')}</SelectItem>
|
|
<SelectItem value="Single">{t('admin.shows.kinds.Single')}</SelectItem>
|
|
</SelectContent>
|
|
</Select>
|
|
</div>
|
|
|
|
<div className="flex flex-col gap-1.5">
|
|
<Label>{t('admin.shows.audience')}</Label>
|
|
<Select
|
|
value={audience ?? AUDIENCE_UNSET}
|
|
onValueChange={(v) => setAudience(v === AUDIENCE_UNSET ? null : (v as ShowAudience))}
|
|
>
|
|
<SelectTrigger className="whitespace-nowrap">
|
|
<SelectValue />
|
|
</SelectTrigger>
|
|
<SelectContent>
|
|
<SelectItem value={AUDIENCE_UNSET}>{t('admin.shows.audienceUnset')}</SelectItem>
|
|
{SHOW_AUDIENCES.map((value) => (
|
|
<SelectItem key={value} value={value}>
|
|
{t(`admin.shows.audiences.${value}`)}
|
|
</SelectItem>
|
|
))}
|
|
</SelectContent>
|
|
</Select>
|
|
</div>
|
|
</div>
|
|
|
|
<DialogFooter>
|
|
<Button size="sm" variant="outline" onClick={onClose}>
|
|
{t('common.cancel')}
|
|
</Button>
|
|
<Button size="sm" disabled={!name.trim() || createMutation.isPending} onClick={submit}>
|
|
{t('common.create')}
|
|
</Button>
|
|
</DialogFooter>
|
|
</DialogContent>
|
|
</Dialog>
|
|
)
|
|
}
|