From d1c9d450f26911efdbbdfb64f54c68cbcd545da7 Mon Sep 17 00:00:00 2001 From: byrsapty Date: Tue, 25 Aug 2026 14:52:09 +0300 Subject: [PATCH] =?UTF-8?q?=D0=86=D0=BD=D1=82=D0=B5=D1=80=D1=84=D0=B5?= =?UTF-8?q?=D0=B9=D1=81:=20=D0=BF=D1=80=D0=B0=D0=B2=D0=B8=D0=BB=D0=B0=20?= =?UTF-8?q?=D1=83=20=D0=B2=D0=BA=D0=BB=D0=B0=D0=B4=D1=86=D1=96,=20=D0=BA?= =?UTF-8?q?=D0=BD=D0=BE=D0=BF=D0=BA=D0=B8=20=D0=BD=D0=B5=20=D0=B2=D0=B8?= =?UTF-8?q?=D0=B4=D1=96=D0=BB=D1=8F=D1=8E=D1=82=D1=8C=D1=81=D1=8F,=20?= =?UTF-8?q?=D0=BF=D0=B0=D0=BD=D0=B5=D0=BB=D1=8C=20=D0=B7=D0=B3=D0=BE=D1=80?= =?UTF-8?q?=D1=82=D0=B0=D1=94=D1=82=D1=8C=D1=81=D1=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit «Правила» пішли з бічної панелі у вкладку поруч з «Алертами»: тригери переїхали в шаблони, а окремий пункт меню й далі пропонував заводити правила там, звідки їх прибрали. Кнопки перестали бути виділюваним текстом — у них типово user-select: auto, і в підпис можна було поставити блимаючий курсор. Бічна панель згортається у значки, стан у localStorage. Дорогою закрито дірку: вбудований шаблон нікого не сповіщав. Тригери стають правилами лише при збереженні шаблону, а вбудований нікому не належить і не зберігається — хост із icmp-basic збирав пінг і мовчав про недоступність. Плюс індекс правил з 0024 не враховував тенанта, через що другий кабінет із тим самим вбудованим шаблоном отримав би помилку унікальності. Co-Authored-By: Claude Opus 5 --- HISTORY.md | 79 +++++++++++++++++++ server/internal/store/template_triggers.go | 9 ++- server/internal/store/templates.go | 17 +++- .../0025_template_rules_per_tenant.sql | 17 ++++ web/src/components/AppShell.tsx | 54 +++++++++++-- web/src/components/ui.tsx | 33 ++++++++ web/src/index.css | 25 ++++++ web/src/pages/AlertsPage.tsx | 8 +- web/src/pages/RulesPage.tsx | 10 ++- 9 files changed, 239 insertions(+), 13 deletions(-) create mode 100644 server/migrations/0025_template_rules_per_tenant.sql diff --git a/HISTORY.md b/HISTORY.md index 9100fb3..d9dfd2b 100644 --- a/HISTORY.md +++ b/HISTORY.md @@ -2345,3 +2345,82 @@ JSON лишився запасним виходом для джерел, яки валідація невідоме джерело → 400 з поясненням тести server (з базою) і web build — зелені ``` + +## Три правки інтерфейсу + +### «Правила» пішли з бічної панелі + +Тригери переїхали в шаблони, а окремий пункт меню лишився й далі +пропонував заводити правила там, звідки їх щойно прибрали. + +Тепер це вкладка поруч з «Алертами»: `Алерти · Правила`. Розділ +лишився — разові правила на конкретний хост нікуди не діваються, — але +не займає рядок у меню як рівноправна тема. + +Вкладки маршрутні, а не станові: посилання лишається посиланням, із +середньою кнопкою, «відкрити в новій вкладці» й адресою в рядку +браузера. Тому це окремий компонент `SectionTabs`, а не той самий +`Tabs`, що перемикає вміст форми. + +Підзаголовок сторінки правил тепер прямо каже, де правити тригери +шаблонів. + +### Кнопки перестали бути текстом + +У кнопок типово `user-select: auto` — підпис лишається виділюваним +текстом, і в нього можна поставити текстовий курсор. Він блимає +всередині кнопки, ніби туди щось вводять. На дотик гірше: довге +натискання виділяє слово замість натиснути. + +Правило на елемент, а не на компонент — кнопки трапляються й поза +`
diff --git a/web/src/components/ui.tsx b/web/src/components/ui.tsx index 702dee5..4701bf9 100644 --- a/web/src/components/ui.tsx +++ b/web/src/components/ui.tsx @@ -1,5 +1,6 @@ import { useEffect, useRef, useState } from 'react' import type { ReactNode } from 'react' +import { NavLink } from 'react-router-dom' /** * Примітиви інтерфейсу. @@ -29,6 +30,38 @@ export function PageHeader({ ) } +/** + * Смуга розділів під шапкою сторінки. + * + * Не те саме, що Tabs: ті перемикають вміст форми, а ці — маршрут, і + * посилання лишається посиланням (середня кнопка, «відкрити в новій + * вкладці», адреса в рядку браузера). + * + * Потрібна там, де два екрани — це одна тема, і обидва в бічній панелі + * лише подовжували б її: алерти й правила, за якими вони виникають. + */ +export function SectionTabs({ tabs }: { tabs: { to: string; label: string }[] }) { + return ( +
+ {tabs.map((t) => ( + + `-mb-px rounded-t border-b-2 px-3 py-1.5 text-sm transition-colors ${ + isActive + ? 'border-sky-500 font-medium text-slate-100' + : 'border-transparent text-slate-500 hover:text-slate-300' + }` + } + > + {t.label} + + ))} +
+ ) +} + type ButtonKind = 'primary' | 'default' | 'danger' | 'ghost' const buttonKind: Record = { diff --git a/web/src/index.css b/web/src/index.css index b1be2b0..0368055 100644 --- a/web/src/index.css +++ b/web/src/index.css @@ -27,6 +27,31 @@ body { color: #334155 !important; } +/* --------------------------------------------------------------------- + Кнопки — це кнопки, а не текст + ------------------------------------------------------------------ */ + +/* Типово підпис кнопки лишається виділюваним текстом (user-select: auto), + і в браузері на ньому можна поставити текстовий курсор — він блимає + всередині кнопки, ніби туди щось вводять. На дотик те саме виглядає + гірше: довге натискання виділяє слово замість натиснути. + + Правило на елемент, а не на компонент: кнопки трапляються й поза +