fix(ui): повысить приоритет правил и исправить мобильные строки
This commit is contained in:
1 parent
c7e1f75e2e
commit
d5edfe5ef6
5 files changed
+137
-113
No files matched your search
+102
-111
@@ -722,7 +722,7 @@ function AlertRuleRow({ rule, onToggle, onDelete, onEdit, interactionsDisabled,i
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={cn(
|
<div className={cn(
|
||||||
"flex items-center gap-3 px-4 py-3 hover:bg-muted/20 transition-colors group",
|
"flex flex-wrap items-center gap-3 px-4 py-3 hover:bg-muted/20 transition-colors group",
|
||||||
!rule.enabled && "opacity-55",
|
!rule.enabled && "opacity-55",
|
||||||
)}>
|
)}>
|
||||||
{/* toggle */}
|
{/* toggle */}
|
||||||
@@ -737,10 +737,10 @@ function AlertRuleRow({ rule, onToggle, onDelete, onEdit, interactionsDisabled,i
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* name + target */}
|
{/* name + target */}
|
||||||
<div className="flex-1 min-w-0">
|
<div className="order-first min-w-0 basis-full sm:order-none sm:basis-auto sm:flex-1">
|
||||||
{isLive&&rule.type==="bgp-prefix"?<p role="status" className="text-xs text-warning">Отслеживание появления и отзыва префиксов пока недоступно. Это правило не проверяется.</p>:null}
|
{isLive&&rule.type==="bgp-prefix"?<p role="status" className="text-xs text-warning">Отслеживание появления и отзыва префиксов пока недоступно. Это правило не проверяется.</p>:null}
|
||||||
<p className="text-sm font-medium truncate">{rule.name}</p>
|
<p className="text-sm font-medium break-words sm:truncate">{rule.name}</p>
|
||||||
<p className="text-[11px] text-muted-foreground truncate">
|
<p className="text-[11px] text-muted-foreground break-words sm:truncate">
|
||||||
{TYPE_META[rule.type].label} ·{" "}
|
{TYPE_META[rule.type].label} ·{" "}
|
||||||
<span className="font-mono">
|
<span className="font-mono">
|
||||||
{rule.targets.length > 1 ? `${rule.targets.length} объектов` : rule.target}
|
{rule.targets.length > 1 ? `${rule.targets.length} объектов` : rule.target}
|
||||||
@@ -774,7 +774,7 @@ function AlertRuleRow({ rule, onToggle, onDelete, onEdit, interactionsDisabled,i
|
|||||||
|
|
||||||
{/* last fired */}
|
{/* last fired */}
|
||||||
<span className={cn(
|
<span className={cn(
|
||||||
"text-[11px] shrink-0 w-[80px] text-right",
|
"text-[11px] shrink-0 sm:w-[80px] text-right",
|
||||||
rule.lastFired ? "text-muted-foreground" : "text-muted-foreground/40",
|
rule.lastFired ? "text-muted-foreground" : "text-muted-foreground/40",
|
||||||
)}>
|
)}>
|
||||||
{rule.lastFired ?? "—"}
|
{rule.lastFired ?? "—"}
|
||||||
@@ -786,7 +786,7 @@ function AlertRuleRow({ rule, onToggle, onDelete, onEdit, interactionsDisabled,i
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* actions (visible on hover) */}
|
{/* actions (visible on hover) */}
|
||||||
<div className="flex gap-1 opacity-100 lg:opacity-0 lg:group-hover:opacity-100 focus-within:opacity-100 transition-opacity shrink-0">
|
<div className="ml-auto flex gap-1 opacity-100 lg:opacity-0 lg:group-hover:opacity-100 focus-within:opacity-100 transition-opacity shrink-0">
|
||||||
{onEdit && (
|
{onEdit && (
|
||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
@@ -2530,42 +2530,7 @@ function AlertsContent() {
|
|||||||
{config.outcome?<p role="status" className="text-xs">{config.outcome}</p>:null}
|
{config.outcome?<p role="status" className="text-xs">{config.outcome}</p>:null}
|
||||||
{config.busy?<p role="status" className="text-xs">Ожидаем подтверждение правил и групп…</p>:null}
|
{config.busy?<p role="status" className="text-xs">Ожидаем подтверждение правил и групп…</p>:null}
|
||||||
{config.unknown&&!config.busy?<div className="space-y-2"><p role="status" className="text-xs">Результат сохранения правил неизвестен. Запрос мог выполниться; проверьте правила и группы в БД перед повтором. Обновление списка не разрешает повтор автоматически.</p><Button type="button" size="sm" variant="outline" className="h-auto whitespace-normal" disabled={!config.enabled||operationBusy} onClick={config.review}>Я проверил правила — разрешить новый запрос</Button></div>:null}
|
{config.unknown&&!config.busy?<div className="space-y-2"><p role="status" className="text-xs">Результат сохранения правил неизвестен. Запрос мог выполниться; проверьте правила и группы в БД перед повтором. Обновление списка не разрешает повтор автоматически.</p><Button type="button" size="sm" variant="outline" className="h-auto whitespace-normal" disabled={!config.enabled||operationBusy} onClick={config.review}>Я проверил правила — разрешить новый запрос</Button></div>:null}
|
||||||
{isLive?<AlertsTargetsStatus catalogs={catalogs}/>:null}
|
|
||||||
{isLive?<AlertsOutboxPanel base={backendUrl} available={backendStatus===true} operationsBusy={operationBusy} beginOperation={beginOperation} endOperation={endOperation}/>:null}
|
|
||||||
{known?<>
|
{known?<>
|
||||||
<Collapsible open={hintsExpanded} onOpenChange={setHintsExpanded} className="rounded-lg border border-dashed border-border bg-muted/15">
|
|
||||||
<CollapsibleTrigger className="flex w-full items-center gap-2 px-3 py-2 text-left text-xs font-medium text-foreground hover:bg-muted/40 rounded-lg">
|
|
||||||
<ChevronDownIcon className={cn("size-4 shrink-0 transition-transform", hintsExpanded && "rotate-180")} />
|
|
||||||
Подсказки: формат целей и «восстановился»
|
|
||||||
</CollapsibleTrigger>
|
|
||||||
<CollapsibleContent className="px-3 pb-3 text-[11px] text-muted-foreground leading-relaxed space-y-2 border-t border-border/60 pt-2">
|
|
||||||
<p>
|
|
||||||
Движок читает последние сэмплы из PostgreSQL (uptime ресурсы, REST-пинг, пробы, GRE/BGP). Условие
|
|
||||||
срабатывает только если цепочка состояний совпадает с текстом правила.
|
|
||||||
</p>
|
|
||||||
<ul className="list-disc pl-4 space-y-1.5">
|
|
||||||
<li>
|
|
||||||
<span className="font-medium text-foreground">RTT / потери</span> — в объектах укажите ключ пробы
|
|
||||||
как в мониторинге: <code className="rounded bg-muted px-1 font-mono">имя пробы → target</code>.
|
|
||||||
</li>
|
|
||||||
<li>
|
|
||||||
<span className="font-medium text-foreground">Восстановился</span> — нужна история: после падения
|
|
||||||
в последних сэмплах виден возврат в online (REST и метрики ресурса объединяются). Один текущий
|
|
||||||
online без прошлого offline часто не даёт hit.
|
|
||||||
</li>
|
|
||||||
<li>
|
|
||||||
<span className="font-medium text-foreground">Перешёл в offline</span> — только на ребре (предыдущий
|
|
||||||
сэмпл был online). Пока сервер остаётся offline, повторные уведомления не шлются.
|
|
||||||
</li>
|
|
||||||
<li>
|
|
||||||
<span className="font-medium text-foreground">Стабильность / cooldown</span> — задержка перед
|
|
||||||
Telegram и пауза между повторными отправками. В разделе «Сбор данных» в деталях прогона «Оповещения»
|
|
||||||
есть таблица по каждому правилу (hit, стабильность, cooldown, Telegram, причина блока).
|
|
||||||
</li>
|
|
||||||
</ul>
|
|
||||||
</CollapsibleContent>
|
|
||||||
</Collapsible>
|
|
||||||
|
|
||||||
{/* ── summary chip bar ── */}
|
{/* ── summary chip bar ── */}
|
||||||
<div className="flex items-center gap-2 flex-wrap">
|
<div className="flex items-center gap-2 flex-wrap">
|
||||||
|
|
||||||
@@ -2576,7 +2541,7 @@ function AlertsContent() {
|
|||||||
{ key: "warning", label: `Предупреждений: ${warning}`, cls: SEVERITY_META.warning.chip },
|
{ key: "warning", label: `Предупреждений: ${warning}`, cls: SEVERITY_META.warning.chip },
|
||||||
{ key: "info", label: `Информационных: ${info}`, cls: SEVERITY_META.info.chip },
|
{ key: "info", label: `Информационных: ${info}`, cls: SEVERITY_META.info.chip },
|
||||||
] as { key: SeverityFilter; label: string; cls: string }[]).map(({ key, label, cls }) => (
|
] as { key: SeverityFilter; label: string; cls: string }[]).map(({ key, label, cls }) => (
|
||||||
<button key={key} onClick={() => handleChip(key)}
|
<button type="button" aria-pressed={chipActive(key)} key={key} onClick={() => handleChip(key)}
|
||||||
className={cn(
|
className={cn(
|
||||||
"text-xs px-3 py-1.5 rounded-full border font-medium transition-colors",
|
"text-xs px-3 py-1.5 rounded-full border font-medium transition-colors",
|
||||||
chipActive(key)
|
chipActive(key)
|
||||||
@@ -2590,7 +2555,7 @@ function AlertsContent() {
|
|||||||
<div className="w-px h-4 bg-border mx-1" />
|
<div className="w-px h-4 bg-border mx-1" />
|
||||||
|
|
||||||
{/* active count chip */}
|
{/* active count chip */}
|
||||||
<button onClick={() => setOnlyActive(v => !v)}
|
<button type="button" aria-pressed={onlyActive} onClick={() => setOnlyActive(v => !v)}
|
||||||
className={cn(
|
className={cn(
|
||||||
"text-xs px-3 py-1.5 rounded-full border font-medium transition-colors",
|
"text-xs px-3 py-1.5 rounded-full border font-medium transition-colors",
|
||||||
onlyActive
|
onlyActive
|
||||||
@@ -2612,6 +2577,66 @@ function AlertsContent() {
|
|||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
{/* ── main layout ── */}
|
||||||
|
<div className="grid grid-cols-1 xl:grid-cols-[minmax(0,1fr)_300px] gap-5 items-start">
|
||||||
|
|
||||||
|
{/* ── rules card ── */}
|
||||||
|
<OpsPanel
|
||||||
|
title={
|
||||||
|
<span className="flex items-center gap-2">
|
||||||
|
Правила оповещения
|
||||||
|
{rulesSaveBusy && <LoaderCircleIcon className="size-3.5 animate-spin text-muted-foreground" aria-hidden />}
|
||||||
|
</span>
|
||||||
|
}
|
||||||
|
contentClassName="px-0 pb-0 pt-2"
|
||||||
|
>
|
||||||
|
<div className="px-4 pb-3"><Input aria-label="Поиск правил" placeholder="Поиск по имени или объекту…" value={search} onChange={e => setSearch(e.target.value)} autoComplete="off" name="alert-rules-search" /></div>
|
||||||
|
{filteredRules.length === 0 ? (
|
||||||
|
<div className="flex flex-col items-center justify-center py-12 text-muted-foreground/40">
|
||||||
|
<BellOffIcon className="size-8 mb-2 opacity-40" />
|
||||||
|
<p className="text-sm">Правила не найдены</p>
|
||||||
|
</div>
|
||||||
|
) : (
|
||||||
|
<div className="divide-y divide-border/60">
|
||||||
|
{filteredRules.map(r => (
|
||||||
|
<AlertRuleRow
|
||||||
|
key={r.id}
|
||||||
|
rule={r}
|
||||||
|
onToggle={handleToggle}
|
||||||
|
onDelete={id=>setPendingDelete({kind:"rule",id})}
|
||||||
|
onEdit={openRuleSheetEdit}
|
||||||
|
interactionsDisabled={writesDisabled}
|
||||||
|
isLive={isLive}
|
||||||
|
/>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{/* footer */}
|
||||||
|
<div className="px-4 py-2.5 border-t flex flex-wrap items-center justify-between gap-2 text-[11px] text-muted-foreground">
|
||||||
|
<span>
|
||||||
|
{filteredRules.length} из {total} правил
|
||||||
|
{onlyActive && " · только активные"}
|
||||||
|
{sevFilter !== "all" && ` · ${SEVERITY_META[sevFilter].label.toLowerCase()}`}
|
||||||
|
</span>
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
disabled={writesDisabled}
|
||||||
|
onClick={openRuleSheetAdd}
|
||||||
|
className="flex items-center gap-1 hover:text-foreground transition-colors">
|
||||||
|
<PlusIcon className="size-3" />Добавить правило
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</OpsPanel>
|
||||||
|
|
||||||
|
{/* ── right sidebar ── */}
|
||||||
|
<div className="flex flex-col gap-5">
|
||||||
|
<AlertsTelegramForm initial={tg} tokenConfigured={tokenConfigured} base={backendUrl} live={isLive} available={backendStatus===true&&!operationBusy}
|
||||||
|
onBegin={beginOperation} onEnd={endOperation}
|
||||||
|
onSaved={(value,configured)=>{setTg(value);setTokenConfigured(configured)}}/>
|
||||||
|
<HistoryCard entries={history} />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
{/* ── presets + groups (свёрнуто по умолчанию) ── */}
|
{/* ── presets + groups (свёрнуто по умолчанию) ── */}
|
||||||
<Collapsible open={extrasExpanded} onOpenChange={setExtrasExpanded} className="rounded-lg border border-border bg-card shadow-sm">
|
<Collapsible open={extrasExpanded} onOpenChange={setExtrasExpanded} className="rounded-lg border border-border bg-card shadow-sm">
|
||||||
<CollapsibleTrigger
|
<CollapsibleTrigger
|
||||||
@@ -2706,76 +2731,42 @@ function AlertsContent() {
|
|||||||
</CollapsibleContent>
|
</CollapsibleContent>
|
||||||
</Collapsible>
|
</Collapsible>
|
||||||
|
|
||||||
{/* ── main layout ── */}
|
<Collapsible open={hintsExpanded} onOpenChange={setHintsExpanded} className="rounded-lg border border-dashed border-border bg-muted/15">
|
||||||
<div className="grid grid-cols-1 xl:grid-cols-[1fr_300px] gap-5 items-start">
|
<CollapsibleTrigger className="flex w-full items-center gap-2 px-3 py-2 text-left text-xs font-medium text-foreground hover:bg-muted/40 rounded-lg">
|
||||||
|
<ChevronDownIcon className={cn("size-4 shrink-0 transition-transform", hintsExpanded && "rotate-180")} />
|
||||||
|
Подсказки: формат целей и «восстановился»
|
||||||
|
</CollapsibleTrigger>
|
||||||
|
<CollapsibleContent className="px-3 pb-3 text-[11px] text-muted-foreground leading-relaxed space-y-2 border-t border-border/60 pt-2">
|
||||||
|
<p>
|
||||||
|
Движок читает последние сэмплы из PostgreSQL (uptime ресурсы, REST-пинг, пробы, GRE/BGP). Условие
|
||||||
|
срабатывает только если цепочка состояний совпадает с текстом правила.
|
||||||
|
</p>
|
||||||
|
<ul className="list-disc pl-4 space-y-1.5">
|
||||||
|
<li>
|
||||||
|
<span className="font-medium text-foreground">RTT / потери</span> — в объектах укажите ключ пробы
|
||||||
|
как в мониторинге: <code className="rounded bg-muted px-1 font-mono">имя пробы → target</code>.
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<span className="font-medium text-foreground">Восстановился</span> — нужна история: после падения
|
||||||
|
в последних сэмплах виден возврат в online (REST и метрики ресурса объединяются). Один текущий
|
||||||
|
online без прошлого offline часто не даёт hit.
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<span className="font-medium text-foreground">Перешёл в offline</span> — только на ребре (предыдущий
|
||||||
|
сэмпл был online). Пока сервер остаётся offline, повторные уведомления не шлются.
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<span className="font-medium text-foreground">Стабильность / cooldown</span> — задержка перед
|
||||||
|
Telegram и пауза между повторными отправками. В разделе «Сбор данных» в деталях прогона «Оповещения»
|
||||||
|
есть таблица по каждому правилу (hit, стабильность, cooldown, Telegram, причина блока).
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</CollapsibleContent>
|
||||||
|
</Collapsible>
|
||||||
|
|
||||||
{/* ── rules card ── */}
|
|
||||||
<OpsPanel
|
|
||||||
title={
|
|
||||||
<span className="flex items-center gap-2">
|
|
||||||
Правила оповещения
|
|
||||||
{rulesSaveBusy && <LoaderCircleIcon className="size-3.5 animate-spin text-muted-foreground" aria-hidden />}
|
|
||||||
</span>
|
|
||||||
}
|
|
||||||
headerRight={
|
|
||||||
<Input
|
|
||||||
placeholder="Поиск…"
|
|
||||||
value={search}
|
|
||||||
onChange={e => setSearch(e.target.value)}
|
|
||||||
autoComplete="off"
|
|
||||||
name="alert-rules-search"
|
|
||||||
className="h-7 text-xs max-w-[200px]"
|
|
||||||
/>
|
|
||||||
}
|
|
||||||
contentClassName="px-0 pb-0 pt-2"
|
|
||||||
>
|
|
||||||
{filteredRules.length === 0 ? (
|
|
||||||
<div className="flex flex-col items-center justify-center py-12 text-muted-foreground/40">
|
|
||||||
<BellOffIcon className="size-8 mb-2 opacity-40" />
|
|
||||||
<p className="text-sm">Правила не найдены</p>
|
|
||||||
</div>
|
|
||||||
) : (
|
|
||||||
<div className="divide-y divide-border/60">
|
|
||||||
{filteredRules.map(r => (
|
|
||||||
<AlertRuleRow
|
|
||||||
key={r.id}
|
|
||||||
rule={r}
|
|
||||||
onToggle={handleToggle}
|
|
||||||
onDelete={id=>setPendingDelete({kind:"rule",id})}
|
|
||||||
onEdit={openRuleSheetEdit}
|
|
||||||
interactionsDisabled={writesDisabled}
|
|
||||||
isLive={isLive}
|
|
||||||
/>
|
|
||||||
))}
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
|
|
||||||
{/* footer */}
|
|
||||||
<div className="px-4 py-2.5 border-t flex items-center justify-between text-[11px] text-muted-foreground">
|
|
||||||
<span>
|
|
||||||
{filteredRules.length} из {total} правил
|
|
||||||
{onlyActive && " · только активные"}
|
|
||||||
{sevFilter !== "all" && ` · ${SEVERITY_META[sevFilter].label.toLowerCase()}`}
|
|
||||||
</span>
|
|
||||||
<button
|
|
||||||
type="button"
|
|
||||||
disabled={writesDisabled}
|
|
||||||
onClick={openRuleSheetAdd}
|
|
||||||
className="flex items-center gap-1 hover:text-foreground transition-colors">
|
|
||||||
<PlusIcon className="size-3" />Добавить правило
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
</OpsPanel>
|
|
||||||
|
|
||||||
{/* ── right sidebar ── */}
|
|
||||||
<div className="flex flex-col gap-5">
|
|
||||||
<AlertsTelegramForm initial={tg} tokenConfigured={tokenConfigured} base={backendUrl} live={isLive} available={backendStatus===true&&!operationBusy}
|
|
||||||
onBegin={beginOperation} onEnd={endOperation}
|
|
||||||
onSaved={(value,configured)=>{setTg(value);setTokenConfigured(configured)}}/>
|
|
||||||
<HistoryCard entries={history} />
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</>:null}
|
</>:null}
|
||||||
|
{isLive?<AlertsTargetsStatus catalogs={catalogs}/>:null}
|
||||||
|
{isLive?<AlertsOutboxPanel base={backendUrl} available={backendStatus===true} operationsBusy={operationBusy} beginOperation={beginOperation} endOperation={endOperation}/>:null}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|||||||
+1
-1
@@ -4,7 +4,7 @@
|
|||||||
|
|
||||||
## Актуальная точка продолжения — 2026-10-03
|
## Актуальная точка продолжения — 2026-10-03
|
||||||
|
|
||||||
Этап 11Б в работе; последний проверенный блок 11Б.4.4.3.10; System DB принят локально с внешними ограничениями. Следующий блок 11Б.4.4.3.11: компактность основной Alerts страницы и профильная приёмка ПК/320px; processing recovery проектировать отдельно с доказуемой остановкой всех отправителей. Затем Releases/probe scheduling и приёмка 11Б/12. Внешняя PG/Telegram приёмка обязательна перед выпуском.
|
Этап 11Б в работе; последний проверенный блок 11Б.4.4.3.11; основной Alerts экран принят локально на ПК/320px. Следующий блок 11Б.5: проверить достоверность release manifest, безопасные ссылки и локальное/неизвестное состояние Releases, затем probe scheduling и приёмка 11Б/12. Processing recovery остаётся отдельной задачей с доказуемой остановкой всех отправителей; внешняя PG/Telegram приёмка обязательна перед выпуском.
|
||||||
|
|
||||||
Реальные pg_dump/pg_restore, PG rollback и остановка UDP worker требуют внешней приёмки. Датированные продолжения ниже — история; точный следующий шаг также указан в начале [журнала](ux-ui-progress.md).
|
Реальные pg_dump/pg_restore, PG rollback и остановка UDP worker требуют внешней приёмки. Датированные продолжения ниже — история; точный следующий шаг также указан в начале [журнала](ux-ui-progress.md).
|
||||||
|
|
||||||
|
|||||||
@@ -28,7 +28,7 @@
|
|||||||
| 9 | VPN, Firewall и Filters | Завершён в границах локальной приёмки 2026-09-30; блоки 9.1–9.64, ограничения в 9.64 |
|
| 9 | VPN, Firewall и Filters | Завершён в границах локальной приёмки 2026-09-30; блоки 9.1–9.64, ограничения в 9.64 |
|
||||||
| 10 | Маршрутизация, диагностика, карта и терминал | Завершён в границах локальной приёмки 2026-10-01; блоки 10.1–10.71, внешние ограничения в 10.71 |
|
| 10 | Маршрутизация, диагностика, карта и терминал | Завершён в границах локальной приёмки 2026-10-01; блоки 10.1–10.71, внешние ограничения в 10.71 |
|
||||||
| 11А | Пользователи, контейнеры, сертификаты и резервные копии | Завершён в границах локальной приёмки 2026-10-01; 11А.1–11А.33, внешние ограничения в 11А.33 |
|
| 11А | Пользователи, контейнеры, сертификаты и резервные копии | Завершён в границах локальной приёмки 2026-10-01; 11А.1–11А.33, внешние ограничения в 11А.33 |
|
||||||
| 11Б | Оповещения, сбор данных, интеграции, релизы и настройки | В работе: 11Б.1–11Б.4.4.3.10; scheduler и GeoIP приняты локально; NetFlow принят локально; Evolution принят локально; далее общие Settings |
|
| 11Б | Оповещения, сбор данных, интеграции, релизы и настройки | В работе: 11Б.1–11Б.4.4.3.11; scheduler и GeoIP приняты локально; NetFlow принят локально; Evolution принят локально; далее общие Settings |
|
||||||
| 12 | Итоговая доступность, адаптивность, производительность и регрессия | Не начат |
|
| 12 | Итоговая доступность, адаптивность, производительность и регрессия | Не начат |
|
||||||
|
|
||||||
|
|
||||||
@@ -36,6 +36,14 @@
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## Блок 11Б.4.4.3.11 — приоритет правил и мобильная компоновка (2026-10-03)
|
||||||
|
|
||||||
|
- Правила и фильтры расположены перед каталогами, очередью, пресетами и подсказками. Независимые readers каталогов/очереди остаются смонтированы и доступны даже без подтверждённых основных данных. Формы, checkpoints, общий lock и API не изменены.
|
||||||
|
- Поиск получил имя и отдельную строку, фильтры — aria-pressed/type=button. Название и объект правила на телефоне занимают отдельную строку; метки и действия переносятся, desktop columns допускают сжатие. Frame/base-nova primitives не изменялись.
|
||||||
|
- 35 browser сценариев чтения/прав/каталогов/правил/Telegram/очереди прошли; после исправления сжатого названия повторно 12 layout/prefix/rules. Typecheck/lint прошли; ReUI Frame checklist применён. 320px screenshot просмотрен: название, объект, действия видимы.
|
||||||
|
- Первый запуск остановился по timeout dev server из-за повреждённых generated .next/dev/types; проверенный локальный cache удалён, повторные проверки прошли. Production build предыдущего блока не выдаётся за сборку этих изменений. Реальные PG/Telegram и processing recovery не проверены.
|
||||||
|
- Следующий блок 11Б.5: проверить достоверность release manifest, безопасные ссылки и локальное/неизвестное состояние Releases, затем probe scheduling и приёмка 11Б/12. Processing recovery остаётся отдельной задачей с доказуемой остановкой всех отправителей; внешняя PG/Telegram приёмка обязательна перед выпуском.
|
||||||
|
|
||||||
## Блок 11Б.4.4.3.10 — честная доступность BGP-prefix и сборка (2026-10-03)
|
## Блок 11Б.4.4.3.10 — честная доступность BGP-prefix и сборка (2026-10-03)
|
||||||
|
|
||||||
- Backend evaluate-rule для bgp-prefix возвращает null; live UI теперь прямо сообщает, что сохранённое правило не проверяется. Выбор этого типа для нового правила недоступен, выключенное legacy правило нельзя включить через switch. Existing enabled запись можно отключить; редактирование/смена типа/удаление сохраняются. Mock демонстрация и backend policies не менялись.
|
- Backend evaluate-rule для bgp-prefix возвращает null; live UI теперь прямо сообщает, что сохранённое правило не проверяется. Выбор этого типа для нового правила недоступен, выключенное legacy правило нельзя включить через switch. Existing enabled запись можно отключить; редактирование/смена типа/удаление сохраняются. Mock демонстрация и backend policies не менялись.
|
||||||
|
|||||||
@@ -320,3 +320,7 @@ Picker keyboard semantics/one parent scroll/initial objects accepted locally; br
|
|||||||
### Продолжение 2026-10-03 — 11Б.4.4.3.10
|
### Продолжение 2026-10-03 — 11Б.4.4.3.10
|
||||||
|
|
||||||
Live BGP-prefix честно помечен unsupported, legacy записи не удалены; browser12/typecheck/lint и production frontend/contracts/backend build прошли. Финальный Alerts/backend sidebar профиль прошёл. Основной Alerts экран/processing recovery/внешняя PG и Telegram приёмка остаются. Следующий блок 11Б.4.4.3.11: компактность основной Alerts страницы и профильная приёмка ПК/320px; processing recovery проектировать отдельно с доказуемой остановкой всех отправителей. Затем Releases/probe scheduling и приёмка 11Б/12. Внешняя PG/Telegram приёмка обязательна перед выпуском.
|
Live BGP-prefix честно помечен unsupported, legacy записи не удалены; browser12/typecheck/lint и production frontend/contracts/backend build прошли. Финальный Alerts/backend sidebar профиль прошёл. Основной Alerts экран/processing recovery/внешняя PG и Telegram приёмка остаются. Следующий блок 11Б.4.4.3.11: компактность основной Alerts страницы и профильная приёмка ПК/320px; processing recovery проектировать отдельно с доказуемой остановкой всех отправителей. Затем Releases/probe scheduling и приёмка 11Б/12. Внешняя PG/Telegram приёмка обязательна перед выпуском.
|
||||||
|
|
||||||
|
### Продолжение 2026-10-03 — 11Б.4.4.3.11
|
||||||
|
|
||||||
|
Приоритет правил, доступные фильтры/поиск и мобильная строка приняты локально: browser35 + повтор12, typecheck/lint/ReUI audit, screenshot320. Readers/формы/guards сохранены. Следующий блок 11Б.5: проверить достоверность release manifest, безопасные ссылки и локальное/неизвестное состояние Releases, затем probe scheduling и приёмка 11Б/12. Processing recovery остаётся отдельной задачей с доказуемой остановкой всех отправителей; внешняя PG/Telegram приёмка обязательна перед выпуском.
|
||||||
@@ -0,0 +1,21 @@
|
|||||||
|
import {test,expect} from "@playwright/test"
|
||||||
|
import {alertsWithRule,emptyOutbox} from "./fixtures/alerts"
|
||||||
|
|
||||||
|
for(const width of [320,1440])test("Alerts rules priority, accessible filters and no overflow/"+width,async({page})=>{
|
||||||
|
await page.setViewportSize({width,height:900})
|
||||||
|
await page.route("**/health",r=>r.fulfill({json:{status:"ok"}}))
|
||||||
|
await page.route("**/api/**",r=>{const path=new URL(r.request().url()).pathname;return r.fulfill({json:path==="/api/auth/config"?{required:false}:path==="/api/alerts"?alertsWithRule:path==="/api/alerts/outbox"?emptyOutbox:path==="/api/filters/gre-tunnels"?{tunnels:[],failures:[]}:[]})})
|
||||||
|
await page.goto("/alerts")
|
||||||
|
const search=page.getByRole("textbox",{name:"Поиск правил",exact:true})
|
||||||
|
await expect(search).toBeVisible()
|
||||||
|
const critical=page.getByRole("button",{name:"Критических: 1",exact:true})
|
||||||
|
await critical.focus();await page.keyboard.press("Space");await expect(critical).toHaveAttribute("aria-pressed","true")
|
||||||
|
await search.fill("missing fixture");await expect(page.getByText("Правила не найдены",{exact:true})).toBeVisible()
|
||||||
|
await search.fill("Fixture rule");await expect(page.getByRole("button",{name:"Изменить правило Fixture rule"})).toBeVisible()
|
||||||
|
const name=page.getByText("Fixture rule",{exact:true});await expect(name).toBeVisible();expect((await name.boundingBox())!.width).toBeGreaterThan(100)
|
||||||
|
const position=await search.boundingBox();const catalogs=await page.getByText("Каталоги целей",{exact:true}).boundingBox();const queue=await page.getByRole("region",{name:"Очередь Telegram"}).boundingBox()
|
||||||
|
expect(position!.y).toBeLessThan(catalogs!.y);expect(position!.y).toBeLessThan(queue!.y)
|
||||||
|
await search.scrollIntoViewIfNeeded();await expect(search).toBeInViewport()
|
||||||
|
expect(await page.evaluate(()=>document.documentElement.scrollWidth<=window.innerWidth)).toBe(true)
|
||||||
|
await page.screenshot({path:"test-results/alerts-layout-"+width+".png",fullPage:true})
|
||||||
|
})
|
||||||
Reference in new issue
Block a user