fix(ui): исправить адаптивность форм и вкладок мониторинга

This commit is contained in:
Denozordec committed 2026-09-23 14:38:52 +07:00
1 parent a9ed459481
commit 9cd15528c9
4 files changed
+61 -8

No files matched your search

+7 -7
View File
@@ -2049,7 +2049,7 @@ function UptimePageContent({ selectedProbeId, allProbesHref }: { selectedProbeId
)}
{/* ── tab switcher ── */}
<div className="border-b bg-muted/10 px-6 flex items-center gap-1 shrink-0">
<div className="border-b bg-muted/10 px-3 sm:px-6 flex flex-wrap items-center gap-1 shrink-0">
{(["probes", "resources", "speed"] as const).map(t => (
<button
key={t}
@@ -2195,7 +2195,7 @@ function UptimePageContent({ selectedProbeId, allProbesHref }: { selectedProbeId
: <ChevronDownIcon className="size-4 text-muted-foreground shrink-0" />}
<StatusDot status={server.status} pulse={server.status === "online"} />
<Flag code={server.country} size={16} />
<span className="font-mono text-sm font-semibold">{server.name}</span>
<span className="min-w-0 flex-1 wrap-anywhere font-mono text-sm font-semibold">{server.name}</span>
<TypeChip type={server.type} />
<span className="text-xs text-muted-foreground ml-auto">{srvProbes.length} проб</span>
</button>
@@ -2739,7 +2739,7 @@ function UptimePageContent({ selectedProbeId, allProbesHref }: { selectedProbeId
}
setSpeedSheetOpen(true)
}}>
<SheetContent side="right" className="sm:max-w-[420px] flex flex-col p-0" showCloseButton={false}>
<SheetContent side="right" className="data-[side=right]:w-full sm:max-w-[420px] flex flex-col p-0" showCloseButton={false}>
<SheetHeader className="px-5 pt-5 pb-4 border-b shrink-0">
<div className="flex items-center justify-between gap-2">
<div>
@@ -2756,7 +2756,7 @@ function UptimePageContent({ selectedProbeId, allProbesHref }: { selectedProbeId
{permissionError && <ContextAlert variant="destructive" role="alert"><ContextDescription>{permissionError}</ContextDescription></ContextAlert>}
{probeSaveError && <ContextAlert variant="destructive" role="alert"><ContextDescription>{probeSaveError}</ContextDescription></ContextAlert>}
<fieldset disabled={probeSaving} aria-busy={probeSaving} className="flex-1 min-h-0 overflow-y-auto px-5 py-5 flex flex-col gap-5">
<fieldset disabled={probeSaving} aria-busy={probeSaving} className="flex-1 min-h-0 min-w-0 overflow-y-auto px-5 py-5 flex flex-col gap-5">
<FormField label="Источник">
<ServerPickerCards
options={selectableSources}
@@ -2801,7 +2801,7 @@ function UptimePageContent({ selectedProbeId, allProbesHref }: { selectedProbeId
/>
</FormField>
<div className="grid grid-cols-3 gap-3">
<div className="grid grid-cols-1 gap-3">
<FormField label="Протокол">
<SegmentedControl
value={speedDraft.protocol}
@@ -2883,7 +2883,7 @@ function UptimePageContent({ selectedProbeId, allProbesHref }: { selectedProbeId
}
setSheetOpen(true)
}}>
<SheetContent side="right" className="sm:max-w-[420px] flex flex-col p-0" showCloseButton={false}>
<SheetContent side="right" className="data-[side=right]:w-full sm:max-w-[420px] flex flex-col p-0" showCloseButton={false}>
<SheetHeader className="px-5 pt-5 pb-4 border-b shrink-0">
<div className="flex items-center justify-between gap-2">
@@ -2901,7 +2901,7 @@ function UptimePageContent({ selectedProbeId, allProbesHref }: { selectedProbeId
{permissionError && <ContextAlert variant="destructive" role="alert"><ContextDescription>{permissionError}</ContextDescription></ContextAlert>}
{probeSaveError && <ContextAlert variant="destructive" role="alert"><ContextDescription>{probeSaveError}</ContextDescription></ContextAlert>}
<fieldset disabled={probeSaving} aria-busy={probeSaving} className="flex-1 min-h-0 overflow-y-auto px-5 py-5 flex flex-col gap-5">
<fieldset disabled={probeSaving} aria-busy={probeSaving} className="flex-1 min-h-0 min-w-0 overflow-y-auto px-5 py-5 flex flex-col gap-5">
{/* from → to visual */}
<div className="flex items-center gap-2 rounded-lg bg-muted/40 border px-4 py-3">
+8
View File
@@ -29,6 +29,14 @@
| 11Б | Оповещения, сбор данных, интеграции, релизы и настройки | Не начат |
| 12 | Итоговая доступность, адаптивность, производительность и регрессия | Не начат |
## Приёмка 7.5Б — адаптивность мониторинга и форм (2026-09-23)
- Формы Ping/Speed используют всю ширину телефона; min-w-0 у fieldset устраняет выход полей за границы панели. Параметры Speed идут вертикально также на ПК: ширины боковой панели недостаточно для трёх колонок без перекрытия Direction и длительности.
- Вкладки мониторинга переносятся на новую строку. Ранее выбор «Скорость» прокручивал общий контейнер по горизонтали и обрезал левую часть экрана, хотя document.scrollWidth не превышал viewport. Добавлена отдельная проверка границ всех трёх вкладок. Длинное имя в заголовке Speed переносится внутри доступной ширины.
- 49 E2E uptime-links прошли после исправления форм; после найденного на скриншотах сдвига вкладок прошли 5 профильных проверок; после финального исправления перекрытия Speed повторно прошли 3 сценария на 320/390/1440 px. Проверены видимость сохранения/отмены, отсутствие выхода полей за экран, Escape, отсутствие перекрытия параметров Speed. Просмотрены скриншоты форм сверху/снизу, Resources и Speed. Production build с TypeScript прошёл: 34 статические страницы. Lint тестов чистый, uptime сохраняет исходные 1 ошибку react-hooks/set-state-in-effect и 6 предупреждений.
- ReUI checklist: установленные примитивы и тема не изменялись, исправлена локальная композиция страницы. Проверки используют HTTP-моки; реальный JWT/RouterOS не проверен. Resources проверен на наличие строки и ширину страницы, но взаимодействие с таблицей после прокрутки требует отдельной приёмки.
- Следующий блок 7.5: адаптивность форм серверов и таблицы Resources после прокрутки; сквозной сценарий дашборд → объект → действие → возврат; смена backend/mode при незавершённой операции. Этап 7 ещё не завершён. Квота после проверок: использовано 39% пятичасовой и 22% недельной. Локальный коммит без push.
## Приёмка 7.5А — неработающее действие экспорта серверов
- Удалена кнопка «Экспорт» серверов без обработчика и её неиспользуемый импорт. Полноценный экспорт не реализован: формат, поля и назначение требуют отдельного контракта. Пользователь больше не видит действие, которое ничего не делает.
+1 -1
View File
@@ -61,7 +61,7 @@ URL — источник сохранённого состояния. Back/Forwa
3. **7.4 — формы, детали и полный контекст мониторинга.** Восстановление вкладки/фильтров из URL, понятные единицы и подписи, возврат к списку после действия. Не дублировать сохранённые исправления этапа 1; проверить загрузку, отмену, 400/403/500, timeout и повторное нажатие.
4. **7.5 — приёмка этапа.** Сценарий «проблема → сервер/проверка → действие → возврат» с сохранением контекста; профильные E2E, права в HTTP-моках, TypeScript, lint и build. Реальные JWT/RouterOS отдельно в тестовом окружении.
Следующий исполняемый шаг: 7.5 — профильная приёмка серверов и мониторинга, сквозные переходы, адаптивность форм/Resources/Speed и фиксация ограничений. URL-контекст вкладки, Ping и Resources выполнен. Учесть границы HTTP-моков, экспорт серверов и ограничения сетевых операций из журнала; этап 7 целиком пока не завершён.
Следующий исполняемый шаг: 7.5 — адаптивность форм серверов и таблицы Resources после прокрутки, сквозные переходы, смена backend/mode при незавершённой операции и фиксация ограничений. Формы Ping/Speed и вкладки проверены на 320/390/1440 px; устранены переполнение форм, перекрытие параметров Speed и сдвиг страницы при выборе вкладки. URL-контекст вкладки, Ping и Resources выполнен. Учесть границы HTTP-моков, экспорт серверов и ограничения сетевых операций из журнала; этап 7 целиком пока не завершён.
## Решение по синхронизации URL (7.1)
+45
View File
@@ -567,3 +567,48 @@ for (const extra of ["&rtype=unknown", "&rq=a&rq=b"]) {
await expect(page.locator("tr[data-row-id]")).toHaveCount(1)
})
}
for (const width of [320, 390, 1440]) {
test(`приёмка ${width}px: Resources, Speed и формы проб`, async ({ page }) => {
await page.setViewportSize({ width, height: 844 })
await resourceSetup(page)
await expect(page.locator("tr[data-row-id]")).toHaveCount(1)
expect(await page.evaluate(() => document.documentElement.scrollWidth <= innerWidth)).toBe(true)
await page.screenshot({ path: `test-results/resources-${width}.png`, animations: "disabled" })
await page.unrouteAll({ behavior: "wait" })
await setup(page, "/uptime?probe=p1", 0, true, "mm:uptime:write")
await page.getByRole("button", { name: "Пауза", exact: true }).click()
for (const speed of [false, true]) {
if (speed) await page.getByRole("button", { name: /Скорость/ }).click()
await page.getByRole("button", { name: speed ? "Редактировать speed-пробу" : "Редактировать пробу", exact: true }).click()
const dialog = page.getByRole("dialog")
await expect(dialog).toBeVisible()
await expect(dialog.getByRole("button", { name: "Сохранить", exact: true })).toBeInViewport()
await expect(dialog.getByRole("button", { name: "Отмена", exact: true })).toBeInViewport()
await expect.poll(() => dialog.locator("button, input").evaluateAll(elements => elements.filter(el => {
const rect = el.getBoundingClientRect()
return rect.width > 0 && (rect.left < -1 || rect.right > innerWidth + 1)
}).map(el => el.textContent || el.tagName))).toEqual([])
await page.screenshot({ path: `test-results/${speed ? "speed" : "ping"}-form-${width}.png`, animations: "disabled" })
await dialog.locator("fieldset").evaluate(el => { el.scrollTop = el.scrollHeight })
if (speed) {
const direction = await dialog.getByRole("button", { name: "rx", exact: true }).boundingBox()
const duration = await dialog.locator("input").last().boundingBox()
expect(direction).not.toBeNull()
expect(duration).not.toBeNull()
expect(direction!.y + direction!.height).toBeLessThanOrEqual(duration!.y)
}
await page.screenshot({ path: `test-results/${speed ? "speed" : "ping"}-form-bottom-${width}.png`, animations: "disabled" })
await page.keyboard.press("Escape")
await expect(dialog).toBeHidden()
expect(await page.evaluate(() => document.documentElement.scrollWidth <= innerWidth)).toBe(true)
}
for (const name of [/Пинг-пробы/, /Ресурсы/, /Скорость/]) {
const bounds = await page.getByRole("button", { name }).boundingBox()
expect(bounds).not.toBeNull()
expect(bounds!.x).toBeGreaterThanOrEqual(0)
expect(bounds!.x + bounds!.width).toBeLessThanOrEqual(width)
}
await page.screenshot({ path: `test-results/speed-${width}.png`, animations: "disabled" })
})
}