From ca9474ac839e2f234309314662e4dca6176a0606 Mon Sep 17 00:00:00 2001 From: Denozordec Date: Wed, 23 Sep 2026 14:48:13 +0700 Subject: [PATCH] =?UTF-8?q?fix(ui):=20=D0=B0=D0=B4=D0=B0=D0=BF=D1=82=D0=B8?= =?UTF-8?q?=D1=80=D0=BE=D0=B2=D0=B0=D1=82=D1=8C=20=D1=84=D0=BE=D1=80=D0=BC?= =?UTF-8?q?=D1=8B=20=D1=81=D0=B5=D1=80=D0=B2=D0=B5=D1=80=D0=BE=D0=B2=20?= =?UTF-8?q?=D0=B8=20=D0=BF=D1=80=D0=BE=D0=BA=D1=80=D1=83=D1=82=D0=BA=D1=83?= =?UTF-8?q?=20=D1=80=D0=B5=D1=81=D1=83=D1=80=D1=81=D0=BE=D0=B2?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/(main)/servers/page.tsx | 7 +++-- app/(main)/uptime/page.tsx | 6 +--- .../data-grids/uptime-resources-data-grid.tsx | 10 +++++-- docs/ux-ui-progress.md | 7 +++++ docs/ux-ui-stage-7.md | 2 +- tests/e2e/servers.spec.ts | 24 +++++++++++++++ tests/e2e/uptime-links.spec.ts | 29 +++++++++++++++++++ 7 files changed, 74 insertions(+), 11 deletions(-) diff --git a/app/(main)/servers/page.tsx b/app/(main)/servers/page.tsx index 9a5938e8..9fee3600 100644 --- a/app/(main)/servers/page.tsx +++ b/app/(main)/servers/page.tsx @@ -720,7 +720,7 @@ function ServersPageContent() { {/* ══ Sheet: Add / Edit Server ══════════════════════════════════════════ */} - + {sheetMode === "edit" ? "Редактировать сервер" : "Новый сервер"} MikroTik RouterOS · Web API (REST) @@ -731,7 +731,7 @@ function ServersPageContent() { {!detailsReady && !detailsLoading && editingId && } } -
+
@@ -772,6 +772,7 @@ function ServersPageContent() { set("type", v)} options={[ @@ -945,7 +946,7 @@ function ServersPageContent() {
- + }>Отмена {sheetStep > 1 && ( diff --git a/app/(main)/uptime/page.tsx b/app/(main)/uptime/page.tsx index bfcf2b81..2eb4572d 100644 --- a/app/(main)/uptime/page.tsx +++ b/app/(main)/uptime/page.tsx @@ -1030,11 +1030,7 @@ function ResourcesTab({ resources, serversList, liveApi }: { resources: ServerRe {/* ── Table ─────────────────────────────────────────────────────────── */} } search={resSearch} onSearchChange={setResSearch} searchPlaceholder="Поиск по имени, площадке…" diff --git a/components/data-grids/uptime-resources-data-grid.tsx b/components/data-grids/uptime-resources-data-grid.tsx index 243d604d..d5a53f15 100644 --- a/components/data-grids/uptime-resources-data-grid.tsx +++ b/components/data-grids/uptime-resources-data-grid.tsx @@ -1,6 +1,6 @@ "use client" -import { dataGridFeatures, type DataGridFeatures } from "@/components/reui/data-grid" +import { DataGridContainer, DataGridScrollArea, DataGridTable, dataGridFeatures, type DataGridFeatures } from "@/components/reui/data-grid" import { useMemo } from "react" import { @@ -377,7 +377,13 @@ function UptimeResourcesDataGrid({ rows }: UptimeResourcesDataGridProps) { "[&:has([data-resource-crit=true])]:bg-red-500/3", ), }} - /> + > + + + + + + ) } diff --git a/docs/ux-ui-progress.md b/docs/ux-ui-progress.md index 2d685ded..56ba4f53 100644 --- a/docs/ux-ui-progress.md +++ b/docs/ux-ui-progress.md @@ -29,6 +29,13 @@ | 11Б | Оповещения, сбор данных, интеграции, релизы и настройки | Не начат | | 12 | Итоговая доступность, адаптивность, производительность и регрессия | Не начат | +## Приёмка 7.5В — формы серверов и таблица Resources (2026-09-23) + +- Форма сервера на телефоне занимает полную ширину; fieldset получил min-w-0, выбор типа узла и кнопки подвала допускают перенос. На 320 px прежде выходили за экран поля, страны и последний шаг. Проверены четыре шага редактирования, выбор Home Router, добавленный WAN, раскрытые дополнительные поля и Escape на 320/390/1440 px. Существующие проверки создания и ошибок сохранения также прошли. +- Resources: фильтры типов переносятся внутри панели. Таблица обёрнута установленным ReUI DataGridScrollArea с orientation=horizontal внутри DataGridContainer: правые колонки доступны при прокрутке, общая страница не расширяется. Общая оболочка DataGridShell, vendored ReUI и тема не изменены. +- Проверки: 55 профильных E2E серверов/мониторинга прошли; затем ещё 2 уточнённые проверки Resources на 320/390 px подтвердили доступ к значению последней колонки, поиск, пустой результат и восстановление строки. Скриншоты WAN, дополнительных полей и прокрученной таблицы просмотрены. Production build и TypeScript прошли: 34 статические страницы. Lint изменённых файлов чистый, кроме исходных 1 ошибки и 6 предупреждений uptime. ReUI API и audit checklist проверены. +- Границы: HTTP-моки, без реального RouterOS/JWT; сортировка в адаптивном тесте проверена на одной строке, порядок нескольких строк этим тестом не доказывается. Сквозной сценарий и смена backend/mode при незавершённой операции остаются следующими пунктами 7.5. Этап 7 целиком ещё не завершён. На контрольной точке осталось 23% пятичасовой и 72% недельной квоты. Локальный коммит без push. + ## Приёмка 7.5Б — адаптивность мониторинга и форм (2026-09-23) - Формы Ping/Speed используют всю ширину телефона; min-w-0 у fieldset устраняет выход полей за границы панели. Параметры Speed идут вертикально также на ПК: ширины боковой панели недостаточно для трёх колонок без перекрытия Direction и длительности. diff --git a/docs/ux-ui-stage-7.md b/docs/ux-ui-stage-7.md index b5267c55..7d87f8a1 100644 --- a/docs/ux-ui-stage-7.md +++ b/docs/ux-ui-stage-7.md @@ -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 после прокрутки, сквозные переходы, смена backend/mode при незавершённой операции и фиксация ограничений. Формы Ping/Speed и вкладки проверены на 320/390/1440 px; устранены переполнение форм, перекрытие параметров Speed и сдвиг страницы при выборе вкладки. URL-контекст вкладки, Ping и Resources выполнен. Учесть границы HTTP-моков, экспорт серверов и ограничения сетевых операций из журнала; этап 7 целиком пока не завершён. +Следующий исполняемый шаг: 7.5 — сквозные переходы дашборд → объект → действие → возврат, смена backend/mode при незавершённой операции и фиксация ограничений. Формы серверов проверены на 320/390/1440 px, Resources после прокрутки — на 320/390 px; исправлены переполнение формы и обрезка фильтров/таблицы. Формы Ping/Speed и вкладки проверены на 320/390/1440 px; устранены переполнение форм, перекрытие параметров Speed и сдвиг страницы при выборе вкладки. URL-контекст вкладки, Ping и Resources выполнен. Учесть границы HTTP-моков, экспорт серверов и ограничения сетевых операций из журнала; этап 7 целиком пока не завершён. ## Решение по синхронизации URL (7.1) diff --git a/tests/e2e/servers.spec.ts b/tests/e2e/servers.spec.ts index 104644f6..86435ab1 100644 --- a/tests/e2e/servers.spec.ts +++ b/tests/e2e/servers.spec.ts @@ -509,3 +509,27 @@ test("права серверов: отзыв перед сохранением await expect(page.getByRole("button", { name: "Сохранить", exact: true })).toBeDisabled() expect(writes).toBe(0) }) + +for (const width of [320, 390, 1440]) { + test(`приёмка формы сервера ${width}px: все шаги и отмена`, async ({ page }) => { + await page.setViewportSize({ width, height: 844 }) + await setup(page) + await edit(page) + const dialog = page.getByRole('dialog') + await dialog.getByRole("button", { name: "Home Router", exact: true }).click() + for (let step = 1; step <= 4; step++) { + if (step === 2) await dialog.getByRole("button", { name: "Добавить WAN-аплинк", exact: true }).click() + if (step === 4) await dialog.getByRole("button", { name: "Дополнительно", exact: true }).last().click() + 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/server-form-${width}-${step}.png`, animations: 'disabled' }) + if (step < 4) await dialog.getByRole('button', { name: 'Далее', exact: true }).click() + } + await expect(dialog.getByRole('button', { name: 'Сохранить', exact: true })).toBeInViewport() + await page.keyboard.press('Escape') + await expect(dialog).toBeHidden() + }) +} diff --git a/tests/e2e/uptime-links.spec.ts b/tests/e2e/uptime-links.spec.ts index 251662aa..bafb80d5 100644 --- a/tests/e2e/uptime-links.spec.ts +++ b/tests/e2e/uptime-links.spec.ts @@ -612,3 +612,32 @@ for (const width of [320, 390, 1440]) { await page.screenshot({ path: `test-results/speed-${width}.png`, animations: "disabled" }) }) } + +for (const width of [320, 390]) { + test(`Resources ${width}px: таблица после прокрутки и поиск`, async ({ page }) => { + await page.setViewportSize({ width, height: 844 }) + await resourceSetup(page) + const search = page.getByPlaceholder('Поиск по имени, площадке…') + await search.scrollIntoViewIfNeeded() + await search.fill('Источник') + await expect(page.locator('tr[data-row-id]')).toHaveCount(1) + const cpu = page.getByRole('button', { name: 'CPU', exact: true }) + await cpu.click() + await expect(cpu).toBeInViewport() + const viewport = page.locator('[data-slot="data-grid"] [data-slot="scroll-area-viewport"]') + await viewport.evaluate(el => { el.scrollLeft = el.scrollWidth }) + await expect(page.getByRole("columnheader").last()).toBeInViewport() + const lastValue = page.locator("tr[data-row-id] td").last().locator("span").last() + await lastValue.scrollIntoViewIfNeeded() + await expect(lastValue).toBeInViewport() + const home = page.getByRole("button", { name: "Home Router", exact: true }) + const bounds = await home.boundingBox() + expect(bounds!.x + bounds!.width).toBeLessThanOrEqual(width) + await page.screenshot({ path: `test-results/resources-table-${width}.png`, animations: 'disabled' }) + await search.fill('нет-совпадений') + await expect(page.getByText('Ничего не найдено', { exact: true })).toBeVisible() + await search.clear() + await expect(page.locator('tr[data-row-id]')).toHaveCount(1) + expect(await page.evaluate(() => document.documentElement.scrollWidth <= innerWidth)).toBe(true) + }) +}