Enhance EntryTraceDialog component by refactoring data display logic into dedicated summary functions for improved readability and maintainability. Update GridTab to streamline checkbox state management with a new toggle function. Refactor RulesCard to simplify window removal logic. Adjust CollectionsPanel, GenresPanel, GroupsPanel, RolesPanel, ShowsPanel, and UsersPanel to import sorting utilities from a centralized location, enhancing code organization. Update ThemeProvider to utilize a shared theme context for better consistency across the application.
This commit is contained in:
@@ -1,6 +1,7 @@
|
||||
import { useQuery } from '@tanstack/react-query'
|
||||
import { qk } from '@/shared/api/query-keys'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import type { EntryTraceDto } from '@/shared/api/types'
|
||||
import {
|
||||
Dialog,
|
||||
DialogContent,
|
||||
@@ -42,52 +43,11 @@ export function EntryTraceDialog({
|
||||
|
||||
{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.traceLayer')}>{layerSummary(data, t)}</Row>
|
||||
<Row label={t('admin.channels.traceSlot')}>{slotSummary(data, t)}</Row>
|
||||
<Row label={t('admin.channels.traceGroup')}>{groupSummary(data)}</Row>
|
||||
<Row label={t('admin.channels.traceCollection')}>{data.collectionName}</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.traceStrategy')}>{strategySummary(data, t)}</Row>
|
||||
<Row label={t('admin.channels.traceJunction')}>{data.junctionName}</Row>
|
||||
</dl>
|
||||
)}
|
||||
@@ -96,6 +56,49 @@ export function EntryTraceDialog({
|
||||
)
|
||||
}
|
||||
|
||||
type Translate = ReturnType<typeof useTranslation>['t']
|
||||
|
||||
/** Склейка непустых частей строки трейса; пусто — значит строка не заполнена (покажем «—»). */
|
||||
const joinParts = (parts: (string | null | undefined)[]) => parts.filter(Boolean).join(' · ') || null
|
||||
|
||||
function layerSummary(data: EntryTraceDto, t: Translate) {
|
||||
if (!data.layerName) return null
|
||||
const priority =
|
||||
data.layerPriority !== null ? ` (${t('admin.channels.priority')} ${data.layerPriority})` : ''
|
||||
return `${data.layerName}${priority}`
|
||||
}
|
||||
|
||||
function slotSummary(data: EntryTraceDto, t: Translate) {
|
||||
if (!data.slotTitle) return null
|
||||
return joinParts([
|
||||
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,
|
||||
])
|
||||
}
|
||||
|
||||
function groupSummary(data: EntryTraceDto) {
|
||||
if (!data.groupName) return null
|
||||
const count = data.groupItemCount !== null ? ` (${data.groupItemCount})` : ''
|
||||
return `${data.groupName}${count}`
|
||||
}
|
||||
|
||||
function strategySummary(data: EntryTraceDto, t: Translate) {
|
||||
if (!data.strategy) return null
|
||||
return joinParts([
|
||||
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,
|
||||
])
|
||||
}
|
||||
|
||||
function Row({ label, children }: { label: string; children: React.ReactNode }) {
|
||||
return (
|
||||
<>
|
||||
|
||||
@@ -55,6 +55,9 @@ export function GridTab({
|
||||
const [copyTargets, setCopyTargets] = useState<number[]>([])
|
||||
const [copyFromChannel, setCopyFromChannel] = useState('')
|
||||
|
||||
const toggleCopyTarget = (day: number, checked: boolean) =>
|
||||
setCopyTargets((current) => (checked ? [...current, day] : current.filter((d) => d !== day)))
|
||||
|
||||
const { data: channels } = useQuery({ queryKey: qk.channels.all, queryFn: listChannels })
|
||||
|
||||
const addLayerMutation = useMutation({
|
||||
@@ -318,13 +321,7 @@ export function GridTab({
|
||||
<input
|
||||
type="checkbox"
|
||||
checked={copyTargets.includes(day)}
|
||||
onChange={(e) =>
|
||||
setCopyTargets((current) =>
|
||||
e.target.checked
|
||||
? [...current, day]
|
||||
: current.filter((d) => d !== day),
|
||||
)
|
||||
}
|
||||
onChange={(e) => toggleCopyTarget(day, e.target.checked)}
|
||||
/>
|
||||
{t(`admin.channels.weekdays.${day}`)}
|
||||
</label>
|
||||
|
||||
@@ -87,6 +87,9 @@ export function RulesCard({
|
||||
onError,
|
||||
})
|
||||
|
||||
const removeWindow = (key: string) =>
|
||||
setWindows((current) => current.filter((row) => row.key !== key))
|
||||
|
||||
const patchWindow = (key: string, part: Partial<AudienceWindow>) =>
|
||||
setWindows((current) =>
|
||||
current.map((row) =>
|
||||
@@ -156,7 +159,7 @@ export function RulesCard({
|
||||
<Button
|
||||
size="sm"
|
||||
variant="ghost"
|
||||
onClick={() => setWindows((c) => c.filter((row) => row.key !== key))}
|
||||
onClick={() => removeWindow(key)}
|
||||
>
|
||||
<Trash2 className="h-4 w-4" />
|
||||
</Button>
|
||||
|
||||
Reference in New Issue
Block a user