140 lines
5.4 KiB
TypeScript
140 lines
5.4 KiB
TypeScript
import { useMutation } from '@tanstack/react-query'
|
|
import { useEffect, useState } from 'react'
|
|
import { useTranslation } from 'react-i18next'
|
|
import type { BumperFont, BumperSelection, BumperSettings, ChannelDto } from '@/shared/api/types'
|
|
import { Button } from '@/shared/ui/button'
|
|
import { Label } from '@/shared/ui/label'
|
|
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from '@/shared/ui/select'
|
|
import { toast } from '@/shared/ui/toast-store'
|
|
import { addBumperTemplate, updateChannelSettings } from '../api'
|
|
import { BumperTemplateEditor } from './BumperTemplateEditor'
|
|
import { CollapsibleCard } from './CollapsibleCard'
|
|
|
|
export function BumperCard({
|
|
channel,
|
|
onSaved,
|
|
onError,
|
|
}: {
|
|
channel: ChannelDto
|
|
onSaved: () => void
|
|
onError: (e: unknown) => void
|
|
}) {
|
|
const { t } = useTranslation()
|
|
const [bumpersEnabled, setBumpersEnabled] = useState(channel.bumpersEnabled)
|
|
const [bumper, setBumper] = useState<BumperSettings>(channel.bumper)
|
|
|
|
const setField = <K extends keyof BumperSettings>(key: K, value: BumperSettings[K]) =>
|
|
setBumper((prev) => ({ ...prev, [key]: value }))
|
|
|
|
useEffect(() => {
|
|
setBumpersEnabled(channel.bumpersEnabled)
|
|
setBumper(channel.bumper)
|
|
}, [channel])
|
|
|
|
// Общие настройки заставок сохраняются тем же эндпоинтом, что и настройки канала — остальные
|
|
// поля берём из канала без изменений (они правятся в своей карточке).
|
|
const save = useMutation({
|
|
mutationFn: () =>
|
|
updateChannelSettings(channel.id, {
|
|
name: channel.name,
|
|
isEnabled: channel.isEnabled,
|
|
bumpersEnabled,
|
|
bumper,
|
|
fillerAssetId: channel.fillerAssetId,
|
|
}),
|
|
onSuccess: () => {
|
|
toast.success(t('settings.saved'))
|
|
onSaved()
|
|
},
|
|
onError,
|
|
})
|
|
|
|
const addTemplate = useMutation({
|
|
mutationFn: () => addBumperTemplate(channel.id, ''),
|
|
onSuccess: onSaved,
|
|
onError,
|
|
})
|
|
|
|
const templates = [...channel.bumperTemplates].sort((a, b) => a.position - b.position)
|
|
|
|
return (
|
|
<CollapsibleCard title={t('admin.channels.bumpers')} contentClassName="flex flex-col gap-4">
|
|
<label className="flex items-start gap-2 text-sm">
|
|
<input
|
|
type="checkbox"
|
|
className="mt-1"
|
|
checked={bumpersEnabled}
|
|
onChange={(e) => setBumpersEnabled(e.target.checked)}
|
|
/>
|
|
<span>
|
|
{t('admin.channels.bumpersLabel')}
|
|
<span className="block text-xs text-muted-foreground">
|
|
{t('admin.channels.bumpersHint')}
|
|
</span>
|
|
</span>
|
|
</label>
|
|
|
|
{/* Общие настройки. Как часто ставить заставку — не здесь: это условие элемента стыка. */}
|
|
<div className="grid gap-4 sm:grid-cols-2">
|
|
<div className="flex flex-col gap-1.5">
|
|
<Label>{t('admin.channels.bumperSelection')}</Label>
|
|
<Select value={bumper.selection} onValueChange={(v) => setField('selection', v as BumperSelection)}>
|
|
<SelectTrigger>
|
|
<SelectValue />
|
|
</SelectTrigger>
|
|
<SelectContent>
|
|
<SelectItem value="Random">{t('admin.channels.bumperSelectionRandom')}</SelectItem>
|
|
<SelectItem value="WeightedRandom">
|
|
{t('admin.channels.bumperSelectionWeighted')}
|
|
</SelectItem>
|
|
<SelectItem value="AlwaysFirst">
|
|
{t('admin.channels.bumperSelectionAlwaysFirst')}
|
|
</SelectItem>
|
|
</SelectContent>
|
|
</Select>
|
|
</div>
|
|
<div className="flex flex-col gap-1.5">
|
|
<Label>{t('admin.channels.bumperFont')}</Label>
|
|
<Select value={bumper.font} onValueChange={(v) => setField('font', v as BumperFont)}>
|
|
<SelectTrigger>
|
|
<SelectValue />
|
|
</SelectTrigger>
|
|
<SelectContent>
|
|
<SelectItem value="Sans">{t('admin.channels.bumperFontSans')}</SelectItem>
|
|
<SelectItem value="Serif">{t('admin.channels.bumperFontSerif')}</SelectItem>
|
|
</SelectContent>
|
|
</Select>
|
|
</div>
|
|
</div>
|
|
<p className="text-xs text-muted-foreground">{t('admin.channels.bumperConditionsHint')}</p>
|
|
<div className="flex justify-end">
|
|
<Button size="sm" disabled={save.isPending} onClick={() => save.mutate()}>
|
|
{t('common.save')}
|
|
</Button>
|
|
</div>
|
|
|
|
{/* Блоки заставок */}
|
|
<div className="border-t border-border pt-4">
|
|
<p className="text-sm font-medium">{t('admin.channels.bumperTemplates')}</p>
|
|
<p className="text-xs text-muted-foreground">{t('admin.channels.bumperTemplatesHint')}</p>
|
|
</div>
|
|
<div className="flex flex-col gap-3">
|
|
{templates.map((template) => (
|
|
<BumperTemplateEditor
|
|
key={template.id}
|
|
channelId={channel.id}
|
|
template={template}
|
|
onChanged={onSaved}
|
|
onError={onError}
|
|
/>
|
|
))}
|
|
</div>
|
|
<div className="flex justify-center">
|
|
<Button size="sm" variant="outline" disabled={addTemplate.isPending} onClick={() => addTemplate.mutate()}>
|
|
{t('admin.channels.bumperAddTemplate')}
|
|
</Button>
|
|
</div>
|
|
</CollapsibleCard>
|
|
)
|
|
}
|