diff --git a/app/(main)/alerts/page.tsx b/app/(main)/alerts/page.tsx index 359eb5ac..2c7fc59a 100644 --- a/app/(main)/alerts/page.tsx +++ b/app/(main)/alerts/page.tsx @@ -710,12 +710,13 @@ function SeverityBadge({ severity }: { severity: AlertSeverity }) { // ─── alert rule row ─────────────────────────────────────────────────────────── -function AlertRuleRow({ rule, onToggle, onDelete, onEdit, interactionsDisabled }: { +function AlertRuleRow({ rule, onToggle, onDelete, onEdit, interactionsDisabled,isLive }: { rule: AlertRule onToggle: (id: string, enabled: boolean) => void onDelete: (id: string) => void onEdit?: (id: string) => void interactionsDisabled?: boolean + isLive:boolean }) { const { Icon, iconClass, bg } = TYPE_META[rule.type] @@ -725,7 +726,7 @@ function AlertRuleRow({ rule, onToggle, onDelete, onEdit, interactionsDisabled } !rule.enabled && "opacity-55", )}> {/* toggle */} - onToggle(rule.id, v)} disabled={interactionsDisabled} /> + onToggle(rule.id, v)} disabled={interactionsDisabled||(isLive&&rule.type==="bgp-prefix"&&!rule.enabled)} /> {/* severity dot */} @@ -737,6 +738,7 @@ function AlertRuleRow({ rule, onToggle, onDelete, onEdit, interactionsDisabled } {/* name + target */}
+ {isLive&&rule.type==="bgp-prefix"?

Отслеживание появления и отзыва префиксов пока недоступно. Это правило не проверяется.

:null}

{rule.name}

{TYPE_META[rule.type].label} ·{" "} @@ -1722,6 +1724,8 @@ function AddRuleSheet({ {/* ── scrollable body ── */}

+ {isLive&&form.type==="bgp-prefix"?

Отслеживание появления и отзыва префиксов пока недоступно. Сохранённое правило не проверяется; можно изменить его тип или отключить в списке.

:null} + {/* name */}
@@ -1768,6 +1772,7 @@ function AddRuleSheet({ key={t} type="button" aria-pressed={active} + disabled={isLive&&t==="bgp-prefix"} onClick={() => { nameTouchedRef.current = false @@ -1801,7 +1806,7 @@ function AddRuleSheet({ {active && }
- {TYPE_EVENT_CARD_DESC[t]} + {isLive&&t==="bgp-prefix"?"Отслеживание префиксов пока недоступно":TYPE_EVENT_CARD_DESC[t]} ) @@ -2739,6 +2744,7 @@ function AlertsContent() { onDelete={id=>setPendingDelete({kind:"rule",id})} onEdit={openRuleSheetEdit} interactionsDisabled={writesDisabled} + isLive={isLive} /> ))}
diff --git a/docs/ux-ui-plan.md b/docs/ux-ui-plan.md index 2d8bf7f0..20566d5c 100644 --- a/docs/ux-ui-plan.md +++ b/docs/ux-ui-plan.md @@ -4,7 +4,7 @@ ## Актуальная точка продолжения — 2026-10-03 -Этап 11Б в работе; последний проверенный блок 11Б.4.4.3.9; System DB принят локально с внешними ограничениями. Следующий блок 11Б.4.4.3.10: явно обозначить неподдерживаемый BGP-prefix evaluator в live UI, затем production build накопленных изменений. Далее processing recovery, компактность основной Alerts страницы, Releases/probe scheduling и приёмка 11Б/12. +Этап 11Б в работе; последний проверенный блок 11Б.4.4.3.10; System DB принят локально с внешними ограничениями. Следующий блок 11Б.4.4.3.11: компактность основной Alerts страницы и профильная приёмка ПК/320px; processing recovery проектировать отдельно с доказуемой остановкой всех отправителей. Затем Releases/probe scheduling и приёмка 11Б/12. Внешняя PG/Telegram приёмка обязательна перед выпуском. Реальные pg_dump/pg_restore, PG rollback и остановка UDP worker требуют внешней приёмки. Датированные продолжения ниже — история; точный следующий шаг также указан в начале [журнала](ux-ui-progress.md). diff --git a/docs/ux-ui-progress.md b/docs/ux-ui-progress.md index 960fc76a..3e242d42 100644 --- a/docs/ux-ui-progress.md +++ b/docs/ux-ui-progress.md @@ -28,7 +28,7 @@ | 9 | VPN, Firewall и Filters | Завершён в границах локальной приёмки 2026-09-30; блоки 9.1–9.64, ограничения в 9.64 | | 10 | Маршрутизация, диагностика, карта и терминал | Завершён в границах локальной приёмки 2026-10-01; блоки 10.1–10.71, внешние ограничения в 10.71 | | 11А | Пользователи, контейнеры, сертификаты и резервные копии | Завершён в границах локальной приёмки 2026-10-01; 11А.1–11А.33, внешние ограничения в 11А.33 | -| 11Б | Оповещения, сбор данных, интеграции, релизы и настройки | В работе: 11Б.1–11Б.4.4.3.9; scheduler и GeoIP приняты локально; NetFlow принят локально; Evolution принят локально; далее общие Settings | +| 11Б | Оповещения, сбор данных, интеграции, релизы и настройки | В работе: 11Б.1–11Б.4.4.3.10; scheduler и GeoIP приняты локально; NetFlow принят локально; Evolution принят локально; далее общие Settings | | 12 | Итоговая доступность, адаптивность, производительность и регрессия | Не начат | @@ -36,6 +36,14 @@ +## Блок 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 не менялись. +- Это честное ограничение UI, не реализация мониторинга prefix UPDATE и не запрет прямого API. Backend контракт сохраняет legacy тип для совместимости; внешнему клиенту нельзя считать его поддерживаемым по одному enabled:true. +- 12 browser picker/prefix/rules, typecheck/lint прошли. Финальная production build frontend/contracts и backend build прошли; Alerts DI/HTTP/реальные JWT gates и sidebar-counts профиль повторно прошли на итоговом состоянии. ReUI Frame checklist применён к адаптации; глобальную ReUI/этап12 приёмку не объявляем. +- Полная Alerts приёмка не завершена: основная страница требует компактности, processing recovery не реализован; реальный PG isolation/CAS/rollback, migrations0008/0009 и Telegram не проверены. Все выполненные блоки сохранены локально без push; чужие изменения skill/AGENTS/config/cache не включены. +- Следующий точный шаг 11Б.4.4.3.11: компактность основной Alerts страницы и профильная приёмка ПК/320px; processing recovery проектировать отдельно с доказуемой остановкой всех отправителей. Затем Releases/probe scheduling и приёмка 11Б/12. Внешняя PG/Telegram приёмка обязательна перед выпуском. + ## Блок 11Б.4.4.3.9 — доступный выбор и одна прокрутка формы (2026-10-03) - Existing object/condition cards теперь native toggle buttons с aria-pressed в labelled group: UI не обещает listbox/option keyboard model без реализации arrow navigation. Type cards тоже сообщают выбранное состояние. Manual object input имеет имя. Список объектов доступен сразу, не требует фокуса на постороннем поле. diff --git a/docs/ux-ui-stage-11b.md b/docs/ux-ui-stage-11b.md index 03987283..8ce181c5 100644 --- a/docs/ux-ui-stage-11b.md +++ b/docs/ux-ui-stage-11b.md @@ -316,3 +316,7 @@ Backend sidebar optional counts не выдают timeout/partial/error как0, ### Продолжение 2026-10-03 — 11Б.4.4.3.9 Picker keyboard semantics/one parent scroll/initial objects accepted locally; browser21/typecheck/lint/ReUI audit passed, 320px screenshot reviewed. Общий Alerts экран не принят. Следующий блок 11Б.4.4.3.10: явно обозначить неподдерживаемый BGP-prefix evaluator в live UI, затем production build накопленных изменений. Далее processing recovery, компактность основной Alerts страницы, Releases/probe scheduling и приёмка 11Б/12. + +### Продолжение 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 приёмка обязательна перед выпуском. diff --git a/tests/e2e/alerts-prefix.spec.ts b/tests/e2e/alerts-prefix.spec.ts new file mode 100644 index 00000000..a139ea4d --- /dev/null +++ b/tests/e2e/alerts-prefix.spec.ts @@ -0,0 +1,6 @@ +import {test,expect} from "@playwright/test" +import {emptyAlerts,emptyOutbox,alertsRule} from "./fixtures/alerts" +test("Unsupported BGP prefix is visible as unavailable, existing disabled rule cannot be enabled and remains editable",async({page})=>{ + let writes=0;const rule={...alertsRule,id:"prefix",name:"Legacy prefix",type:"bgp-prefix",groupId:null,enabled:false,target:"любой префикс",targets:["любой префикс"],condition:"был отозван",conditions:["был отозван"]} + await page.route("**/health",r=>r.fulfill({json:{status:"ok"}}));await page.route("**/api/**",r=>{const path=new URL(r.request().url()).pathname;if(r.request().method()!=="GET")writes++;return r.fulfill({json:path==="/api/auth/config"?{required:false}:path==="/api/alerts"?{...emptyAlerts,rules:[rule]}:path==="/api/alerts/outbox"?emptyOutbox:path==="/api/filters/gre-tunnels"?{tunnels:[],failures:[]}:[]})});await page.goto("/alerts");await expect(page.getByText("Отслеживание появления и отзыва префиксов пока недоступно. Это правило не проверяется.")).toBeVisible();await expect(page.getByRole("switch",{name:"Включить правило Legacy prefix"})).toBeDisabled();await page.getByRole("button",{name:"Изменить правило Legacy prefix"}).click();const dialog=page.getByRole("dialog");await expect(dialog.getByRole("status")).toContainText("Сохранённое правило не проверяется");await expect(dialog.getByRole("button",{name:/^BGP-префикс/})).toBeDisabled();await dialog.getByRole("button",{name:/^Сервер Доступность/}).click();await expect(dialog.getByRole("status")).toHaveCount(0);expect(writes).toBe(0) +})