Refactor ChannelEndpoints and ScheduleGenerator: consolidate endpoint logic into partial files, remove unused methods, and enhance dependency injection for scheduling. Update ChannelDetail component to streamline imports and improve UI structure.
build / backend (push) Successful in 2m17s
build / frontend (push) Successful in 52s
tests / backend-tests (push) Successful in 2m38s

This commit is contained in:
Leonid Pershin
2026-07-25 20:53:52 +03:00
parent 8484587313
commit 5bc6e144d2
24 changed files with 2546 additions and 2431 deletions
@@ -0,0 +1,158 @@
import { useMutation } from '@tanstack/react-query'
import { useState } from 'react'
import { useTranslation } from 'react-i18next'
import type { OverrideMode, OverrideRecurrence } from '@/shared/api/types'
import { Button } from '@/shared/ui/button'
import { Input } from '@/shared/ui/input'
import { Label } from '@/shared/ui/label'
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from '@/shared/ui/select'
import { createOverride } from '../api'
import { NumberField } from './fields'
export function OverrideForm({
channelId,
options,
onCreated,
onError,
}: {
channelId: string
options: { id: string; name: string }[]
onCreated: () => void
onError: (e: unknown) => void
}) {
const { t } = useTranslation()
const [mode, setMode] = useState<OverrideMode>('Exclusive')
const [recurrence, setRecurrence] = useState<OverrideRecurrence>('OneTime')
const [showId, setShowId] = useState('')
const [weight, setWeight] = useState(1)
const [start, setStart] = useState('')
const [end, setEnd] = useState('')
// Weekly: день недели (0=Вс..6=Сб) + окна времени суток «HH:MM».
const [dayOfWeek, setDayOfWeek] = useState(6)
const [startTime, setStartTime] = useState('')
const [endTime, setEndTime] = useState('')
const toMinutes = (hhmm: string) => {
const [h, m] = hhmm.split(':').map(Number)
return h * 60 + m
}
const weekly = recurrence === 'Weekly'
const create = useMutation({
mutationFn: () =>
createOverride(
channelId,
weekly
? {
mode,
recurrence,
dayOfWeek,
startMinute: toMinutes(startTime),
endMinute: toMinutes(endTime),
shows: [{ showId, weight }],
}
: {
mode,
recurrence,
startsAtUtc: new Date(start).toISOString(),
endsAtUtc: new Date(end).toISOString(),
shows: [{ showId, weight }],
},
),
onSuccess: () => {
setShowId('')
setStart('')
setEnd('')
setStartTime('')
setEndTime('')
onCreated()
},
onError,
})
const valid = weekly
? showId && startTime && endTime && toMinutes(endTime) > toMinutes(startTime)
: showId && start && end && new Date(end) > new Date(start)
return (
<div className="flex flex-wrap items-end gap-2">
<div className="flex flex-col gap-1.5">
<Label>{t('admin.channels.overrideRecurrence')}</Label>
<Select value={recurrence} onValueChange={(v) => setRecurrence(v as OverrideRecurrence)}>
<SelectTrigger className="w-36">
<SelectValue />
</SelectTrigger>
<SelectContent>
<SelectItem value="OneTime">{t('admin.channels.recurrenceOneTime')}</SelectItem>
<SelectItem value="Weekly">{t('admin.channels.recurrenceWeekly')}</SelectItem>
</SelectContent>
</Select>
</div>
<Select value={mode} onValueChange={(v) => setMode(v as OverrideMode)}>
<SelectTrigger className="w-36">
<SelectValue />
</SelectTrigger>
<SelectContent>
<SelectItem value="Exclusive">{t('admin.channels.modes.Exclusive')}</SelectItem>
<SelectItem value="Boost">{t('admin.channels.modes.Boost')}</SelectItem>
</SelectContent>
</Select>
<Select value={showId} onValueChange={setShowId}>
<SelectTrigger className="w-44">
<SelectValue placeholder={t('admin.channels.pickShow')} />
</SelectTrigger>
<SelectContent>
{options.map((o) => (
<SelectItem key={o.id} value={o.id}>
{o.name}
</SelectItem>
))}
</SelectContent>
</Select>
{mode === 'Boost' && (
<NumberField label={t('admin.channels.weight')} value={weight} onChange={setWeight} min={1} />
)}
{weekly ? (
<>
<div className="flex flex-col gap-1.5">
<Label>{t('admin.channels.weekday')}</Label>
<Select value={String(dayOfWeek)} onValueChange={(v) => setDayOfWeek(Number(v))}>
<SelectTrigger className="w-36">
<SelectValue />
</SelectTrigger>
<SelectContent>
{[1, 2, 3, 4, 5, 6, 0].map((d) => (
<SelectItem key={d} value={String(d)}>
{t(`admin.channels.weekdays.${d}`)}
</SelectItem>
))}
</SelectContent>
</Select>
</div>
<div className="flex flex-col gap-1.5">
<Label>{t('admin.channels.from')}</Label>
<Input type="time" value={startTime} onChange={(e) => setStartTime(e.target.value)} className="w-32" />
</div>
<div className="flex flex-col gap-1.5">
<Label>{t('admin.channels.to')}</Label>
<Input type="time" value={endTime} onChange={(e) => setEndTime(e.target.value)} className="w-32" />
</div>
</>
) : (
<>
<div className="flex flex-col gap-1.5">
<Label>{t('admin.channels.from')}</Label>
<Input type="datetime-local" value={start} onChange={(e) => setStart(e.target.value)} className="w-60" />
</div>
<div className="flex flex-col gap-1.5">
<Label>{t('admin.channels.to')}</Label>
<Input type="datetime-local" value={end} onChange={(e) => setEnd(e.target.value)} className="w-60" />
</div>
</>
)}
<Button size="sm" disabled={!valid || create.isPending} onClick={() => create.mutate()}>
{t('common.create')}
</Button>
</div>
)
}