Enhance channel management with apply report feature
ci / build-backend (push) Successful in 1m37s
ci / build-frontend (push) Successful in 57s
ci / tests (push) Successful in 1m59s
ci / sonar (push) Successful in 4m37s

Added functionality to display a consolidated apply report for warnings after applying channel templates, improving user experience by reducing clutter from individual warning notifications. Updated the ChannelDetail component to manage the new report state and integrated the ApplyReportDialog for displaying the report. Refactored the Problems component to utilize a new PlanningWarnings component for better organization of warning messages. Localization strings were updated to include new report-related texts in both English and Russian.
This commit is contained in:
Leonid Pershin
2026-07-29 08:15:45 +03:00
parent 3601404383
commit 46c0169dea
9 changed files with 184 additions and 69 deletions
@@ -0,0 +1,51 @@
import { useMemo } from 'react'
import { useTranslation } from 'react-i18next'
import type { PlanningWarningDto } from '@/shared/api/types'
/** Сколько строк одного вида показываем целиком: дальше это уже не чтение, а простыня. */
const DETAILS_LIMIT = 20
/**
* Предупреждения планировщика, сгруппированные по виду: десять однотипных строк — это одна
* проблема, и читать их надо вместе, а не по одной.
*/
export function PlanningWarnings({ warnings }: Readonly<{ warnings: PlanningWarningDto[] }>) {
const { t } = useTranslation()
// Ключ строки — «вид + порядковый номер внутри вида»: у предупреждения нет своего id, а текст
// повторяется (одна и та же причина на разных слотах), и позиция здесь — единственное, что его
// отличает. Считаем ключ при группировке, чтобы список не пересобирался на каждый рендер.
const grouped = useMemo(() => {
const map = new Map<string, { key: string; text: string }[]>()
for (const warning of warnings) {
const list = map.get(warning.kind) ?? []
list.push({ key: `${warning.kind}#${list.length}`, text: warning.details })
map.set(warning.kind, list)
}
return [...map.entries()]
}, [warnings])
if (grouped.length === 0)
return <p className="text-sm text-muted-foreground">{t('admin.channels.noProblems')}</p>
return (
<ul className="flex flex-col gap-2 text-xs">
{grouped.map(([kind, details]) => (
<li key={kind} className="flex flex-col gap-0.5">
<span className="font-medium text-amber-500">
{t(`admin.channels.warnings.${kind}`)} · {details.length}
</span>
{details.slice(0, DETAILS_LIMIT).map((detail) => (
<span key={detail.key} className="text-muted-foreground">
{detail.text}
</span>
))}
{details.length > DETAILS_LIMIT && (
<span className="text-muted-foreground">
{t('admin.channels.andMore', { count: details.length - DETAILS_LIMIT })}
</span>
)}
</li>
))}
</ul>
)
}