Enhance channel and settings functionalities: introduce viewer settings in ChannelEndpoints, update SiteSettings to include channel number toggling, and refactor related data structures. Implement new endpoints for validating templates and diffing scheduling changes, improving overall user experience and configuration management.
This commit is contained in:
@@ -0,0 +1,104 @@
|
||||
import { useQuery } from '@tanstack/react-query'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import {
|
||||
Dialog,
|
||||
DialogContent,
|
||||
DialogHeader,
|
||||
DialogTitle,
|
||||
} from '@/shared/ui/dialog'
|
||||
import { getEntryTrace } from '../api'
|
||||
import { formatChannelTime } from '../lib/format'
|
||||
|
||||
/**
|
||||
* «Почему это здесь» (см. 6.5): цепочка происхождения записи. Трейс пишется в момент генерации —
|
||||
* восстановить его потом нельзя, поэтому у старых записей часть строк будет пустой.
|
||||
*/
|
||||
export function EntryTraceDialog({
|
||||
entryId,
|
||||
utcOffsetMinutes,
|
||||
onClose,
|
||||
}: {
|
||||
entryId: string
|
||||
utcOffsetMinutes: number
|
||||
onClose: () => void
|
||||
}) {
|
||||
const { t } = useTranslation()
|
||||
const { data } = useQuery({
|
||||
queryKey: ['admin', 'entries', entryId, 'trace'],
|
||||
queryFn: () => getEntryTrace(entryId),
|
||||
})
|
||||
|
||||
return (
|
||||
<Dialog open onOpenChange={(open) => !open && onClose()}>
|
||||
<DialogContent>
|
||||
<DialogHeader>
|
||||
<DialogTitle>
|
||||
{data
|
||||
? `${data.showName ?? '—'} · ${formatChannelTime(data.startsAtUtc, utcOffsetMinutes)}`
|
||||
: t('common.loading')}
|
||||
</DialogTitle>
|
||||
</DialogHeader>
|
||||
|
||||
{data && (
|
||||
<dl className="grid grid-cols-[110px_1fr] gap-x-3 gap-y-1.5 text-sm">
|
||||
<Row label={t('admin.channels.traceLayer')}>
|
||||
{data.layerName
|
||||
? `${data.layerName}${data.layerPriority !== null ? ` (${t('admin.channels.priority')} ${data.layerPriority})` : ''}`
|
||||
: null}
|
||||
</Row>
|
||||
<Row label={t('admin.channels.traceSlot')}>
|
||||
{data.slotTitle
|
||||
? [
|
||||
data.slotTitle,
|
||||
data.slotWeekday === null
|
||||
? t('admin.channels.everyDay')
|
||||
: t(`admin.channels.weekdays.${data.slotWeekday}`),
|
||||
data.slotTargetStart?.slice(0, 5),
|
||||
data.slotDurationMinutes ? `${data.slotDurationMinutes} мин` : null,
|
||||
data.driftMinutes !== 0
|
||||
? t('admin.channels.traceDrift', { minutes: data.driftMinutes })
|
||||
: null,
|
||||
data.snapped ? t('admin.channels.traceSnapped') : null,
|
||||
]
|
||||
.filter(Boolean)
|
||||
.join(' · ')
|
||||
: null}
|
||||
</Row>
|
||||
<Row label={t('admin.channels.traceGroup')}>
|
||||
{data.groupName
|
||||
? `${data.groupName}${data.groupItemCount !== null ? ` (${data.groupItemCount})` : ''}`
|
||||
: null}
|
||||
</Row>
|
||||
<Row label={t('admin.channels.traceStrategy')}>
|
||||
{data.strategy
|
||||
? [
|
||||
t(`admin.channels.strategies.${data.strategy}`),
|
||||
data.cooldownDays
|
||||
? t('admin.channels.traceCooldown', { days: data.cooldownDays })
|
||||
: null,
|
||||
data.candidatesAfterCooldown !== null
|
||||
? t('admin.channels.traceCandidates', {
|
||||
count: data.candidatesAfterCooldown,
|
||||
})
|
||||
: null,
|
||||
]
|
||||
.filter(Boolean)
|
||||
.join(' · ')
|
||||
: null}
|
||||
</Row>
|
||||
<Row label={t('admin.channels.traceJunction')}>{data.junctionName}</Row>
|
||||
</dl>
|
||||
)}
|
||||
</DialogContent>
|
||||
</Dialog>
|
||||
)
|
||||
}
|
||||
|
||||
function Row({ label, children }: { label: string; children: React.ReactNode }) {
|
||||
return (
|
||||
<>
|
||||
<dt className="text-muted-foreground">{label}</dt>
|
||||
<dd>{children || '—'}</dd>
|
||||
</>
|
||||
)
|
||||
}
|
||||
Reference in New Issue
Block a user