fix(ui): исправить адаптивность форм и вкладок мониторинга
This commit is contained in:
1 parent
a9ed459481
commit
9cd15528c9
4 files changed
+61
-8
No files matched your search
@@ -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">
|
||||
|
||||
@@ -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А — неработающее действие экспорта серверов
|
||||
|
||||
- Удалена кнопка «Экспорт» серверов без обработчика и её неиспользуемый импорт. Полноценный экспорт не реализован: формат, поля и назначение требуют отдельного контракта. Пользователь больше не видит действие, которое ничего не делает.
|
||||
|
||||
@@ -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)
|
||||
|
||||
|
||||
@@ -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" })
|
||||
})
|
||||
}
|
||||
Reference in new issue
Block a user