Спека: редизайн веб-приложения по дизайн-системе AURA (#65)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
parent
57bf1faf7a
commit
fa20059ecd
2
openspec/changes/redesign/.openspec.yaml
Normal file
2
openspec/changes/redesign/.openspec.yaml
Normal file
@ -0,0 +1,2 @@
|
||||
schema: spec-driven
|
||||
created: 2026-10-21
|
||||
189
openspec/changes/redesign/design.md
Normal file
189
openspec/changes/redesign/design.md
Normal file
@ -0,0 +1,189 @@
|
||||
## Context
|
||||
|
||||
`main` 57bf1fa. Материалы дизайн-системы (вне репозитория, `~/projects/jira-bridge/design/`):
|
||||
- `DESIGN-SYSTEM-RULES.md`, `STYLE-INVENTORY.md`, `REACT-COMPONENT-CATALOG.md` — правила и инвентарь AURA;
|
||||
- `AURA-xlsx-tool-handoff-2026-09-29.zip` — исходники AURA: `design-system/tokens.json` (канон токенов),
|
||||
`src/styles/tokens.css` (сгенерированные CSS-переменные), `src/styles.css`, `design-system/foundations.md`;
|
||||
- `Overview.svg`, `Универсалки-2…7.svg` — экспорт Figma «Универсалки»: основы (цветовые роли, типографика, геометрия),
|
||||
загрузка файлов, составные компоненты (плитки выбора, сообщения, панель действия), макеты, контейнер контента,
|
||||
базовые компоненты (кнопки, табы, шаги, тема, навигация приложений).
|
||||
|
||||
Что важно из системы:
|
||||
- **Цвета (светлая):** канва `#f8f8f6`, поверхность `#fdfdfc`, приглушённый фон `#fafaf8`, текст `#17171a`, вторичный
|
||||
`#818190`, плейсхолдер `#5c6260`, граница `#e5e5e0`, сильная граница `#cfcfc8`, действие `#568d32` (hover/active
|
||||
`#477729`), выбранное — фон `#f4faf5` и граница `#568d32`, ошибка `#b42318` / фон `#fff1f0`, предупреждение `#d89614`
|
||||
/ фон `#fffbe6` / граница `#ffe58f`, фокус `rgb(42 122 70 / 35%)`. **Тёмная:** канва `#181818`, поверхность `#1f1f1f`,
|
||||
приглушённый `#272727`, текст `#f2f4f2`, вторичный `#d6dcd7`, плейсхолдер `#aab2ac`, границы `rgb(232 238 233 / 10%)` и
|
||||
`/ 17%`, действие `#69a93d` (hover `#83c657`), текст на действии `#112016`, ошибка `#e26c67` / фон `#402725`,
|
||||
предупреждение `#e8c17a` / фон `#382f20` / граница `#8a6d1d`, фокус `rgb(116 184 255 / 55%)`.
|
||||
- **Типографика:** заголовок раздела Bold 20/28, текст Regular 14/20, подпись Semibold 14/20, мелкий текст 12/16,
|
||||
крупный 16/24.
|
||||
- **Геометрия:** шаг 4 px; контрол 32 px (крупный 40), радиус контрола 10; кнопки — «таблетки» (радиус 999);
|
||||
панели — радиус 16, отступ 16–24; окно/канва — радиус 20–24; флажок 16×16 радиус 4; контейнер контента 1440 px;
|
||||
переходы 180 ms ease, нажатие 100 ms.
|
||||
- **Компоненты из Figma:** кнопки (основная зелёная, вторичная контурная, опасная, с загрузкой), сегментный
|
||||
переключатель на сером треке с белой активной «таблеткой» и зелёным текстом («Навигация приложений»), переключатель
|
||||
темы (солнце / луна), сообщения успеха и ошибки (иконка в круге, заголовок, пояснение), плитки выбора, плашки
|
||||
файлов, шаги процесса.
|
||||
|
||||
Приложение сейчас (`web/src`): глобальные стили `styles/{theme,base,controls}.css` (переменные `--bg`, `--fg`,
|
||||
`--muted`, `--line`, `--card`, `--panel`, `--soft`, `--accent` синий `#1f6f8b`, палитра видов работ `--k-*`, приоритетов
|
||||
`--p-*`, стрелок `--edge*`, `--guide`), стили страниц `board/board.css` (165 строк, в нём же вход, список, карточка),
|
||||
`issues/issues.css`, `projects/projects.css`, `shell/shell.css`. Шрифт IBM Plex Sans/Mono из `@fontsource`. Иконки —
|
||||
символы текста (⋯ ⇄ ⚑ ▥ ✕ ▼). Обновление из Jira, ход пакета и тема живут в `board/useBoardSource.ts` и
|
||||
`board/components/Toolbar.tsx`; тема — `shell/theme.ts` (ключ `graph-theme`: `""` системная, `light`, `dark`).
|
||||
|
||||
## Goals / Non-Goals
|
||||
|
||||
**Goals:** приложение выглядит как AURA в обеих темах; каждый экран собран из одних компонентов и токенов; общие
|
||||
действия (обновление из Jira, тема, токен) — в оболочке; фильтры доски и списка сгруппированы и не перегружают экран;
|
||||
поведение, адреса, ключи `localStorage` и логика доски — прежние.
|
||||
|
||||
**Non-Goals:** см. proposal.md.
|
||||
|
||||
## Decisions
|
||||
|
||||
1. **Без Ant Design.** Приложение на своих компонентах; тянуть antd ради внешнего вида — тяжёлая зависимость и
|
||||
переписывание всех контролов. Токены AURA переносятся в `web/src/styles/tokens.css` вручную с пометкой источника
|
||||
(`tokens.json` 29.09.2026); скрипта синхронизации нет.
|
||||
2. **Токены — два слоя.** Семантические имена AURA (`--color-bg-canvas`, `--color-text-secondary`,
|
||||
`--color-action-primary`, `--space-*`, `--radius-*`, `--font-size-*`, `--component-control-height` …) для светлой и
|
||||
тёмной темы плюс токены приложения, которых в AURA нет: палитра видов работ `--k-*`, приоритеты `--p-*`, стрелки
|
||||
`--edge`, `--edge-strong`, `--guide`, дорожки доски. Старые имена (`--bg`, `--fg`, `--muted`, `--line`, `--card`,
|
||||
`--panel`, `--soft`, `--accent`, `--accent-fg`) на время перехода — псевдонимы семантических; после переноса всех
|
||||
страниц псевдонимы удаляются. `--edge-strong` и `--accent` становятся зелёными; `--guide` (передачи, критический
|
||||
путь) остаётся оранжевым — его роль — выделение поверх зелёного. Палитра `--k-*` подбирается заново под тёплую
|
||||
канву, оттенки остаются различимыми между собой и с зелёным действием.
|
||||
3. **Цвета только из токенов.** Вне `tokens.css` в CSS нет литералов цвета (`#…`, `rgb(…)`, `hsl(…)`, именованных,
|
||||
кроме `transparent`, `currentColor`, `inherit`); проверка — тест Vitest, который читает все `*.css` в `web/src`.
|
||||
Так новая страница не может «забыть» тему.
|
||||
4. **Шрифт — Mulish** (`@fontsource/mulish` 5.3.0, начертания 400/600/700, подмножества с кириллицей): ближайшая
|
||||
свободная гарнитура к Proxima Nova по пропорциям; Montserrat шире и съедает место в плотных таблицах. Моноширинный —
|
||||
IBM Plex Mono (ключи задач, код, исходник разметки), как сейчас. IBM Plex Sans удаляется из зависимостей.
|
||||
5. **Иконки — свои SVG** в `web/src/ui/icons.tsx` (линейные 16/20 px, обводка 1.5, `currentColor`), набор под
|
||||
нужды приложения: обновить, загрузка/файл, меню (⋯), закрыть, шеврон, поиск, фильтр, солнце, луна, внешняя ссылка,
|
||||
предупреждение, галочка, ошибка, передачи, критический путь, по датам, соседи, связи, сетка, сброс, легенда. Без
|
||||
новой зависимости; текстовые символы в интерфейсе заменяются иконками.
|
||||
6. **Компоненты `web/src/ui/`:** `Button` (варианты `primary`, `secondary`, `danger`, `ghost`; размеры 32 и 40;
|
||||
«таблетка»; состояние загрузки — крутилка вместо иконки, кнопка недоступна), `IconButton` (32×32, радиус 10, имя —
|
||||
`aria-label`), `Segmented` (трек `--color-bg-subtle`/серый, активный сегмент — поверхность и зелёный текст; ссылки
|
||||
или кнопки), `ThemeSwitch` (солнце/луна), `Field` (подпись Semibold 14/20 над контролом), `TextInput`, `SearchInput`
|
||||
(иконка поиска, очистка), `Select` (родной `<select>`, 32 px, радиус 10, своя стрелка), `Checkbox` (16×16, радиус 4,
|
||||
зелёный при отметке), `Badge` (статус, сосед, счётчик), `Chip` (снимаемая плашка фильтра: «Спринт: X ✕»), `Notice`
|
||||
(успех / ошибка / предупреждение / сведения: иконка, текст, необязательное действие), `Panel` (поверхность, граница,
|
||||
радиус 16), `PageHeader` (заголовок 20/28, подзаголовок 14/20 вторичным, место для действий справа), `Dialog`
|
||||
(родной `<dialog>`, радиус 20, тень канвы), `Menu` (выпадающее меню на основе нынешнего `Dropdown`), `Popover`.
|
||||
Компоненты держат только вид и доступность; логика страниц остаётся в страницах.
|
||||
7. **Состояние «предупреждение» разрешено.** В AURA его нет в контролах, но токены `warning` в системе есть, а хабу
|
||||
нужно отличать «агент офлайн», «хаб не отвечает — прежние данные» от ошибок. Используется только в `Notice`, `Badge`
|
||||
и тексте состояния; у полей ввода состояния `warning` нет.
|
||||
8. **Контейнер.** Страницы «Задачи», «не найдено», вход — контейнер до 1440 px по центру с боковым отступом 24 px
|
||||
(16 px уже 640 px). Доска — во всю ширину окна с отступом 24 px: графу нужна ширина. Верхняя панель — во всю ширину,
|
||||
её содержимое выровнено по тем же отступам.
|
||||
9. **Верхняя панель (оболочка)**, высота 64 px, поверхность, нижняя граница; слева направо: название «Хаб Jira»
|
||||
(Bold 16/24), выбор проекта (`Select`), разделы — `Segmented` из ссылок «Доска связей» / «Задачи» (активный —
|
||||
`aria-current="page"`), растяжка, блок данных Jira, `ThemeSwitch`, меню `IconButton ⋯` с пунктом «Сменить токен».
|
||||
Уже 1100 px блок данных переносится второй строкой панели; уже 640 px — название скрыто, разделы на всю ширину.
|
||||
10. **Блок данных Jira:** точка и подпись агента («агент на связи» зелёной точкой / «агент офлайн» — предупреждение;
|
||||
`GET /agent` при старте и раз в 60 с, подсказка — время последнего выхода на связь), текст «данные на ДД.ММ ЧЧ:ММ»
|
||||
(`last_sync.finished_at` из снимка доски текущего проекта; «полного обновления не было»), кнопка `primary`
|
||||
«Обновить из Jira» с иконкой; во время пакета — состояние загрузки и текст хода «пакет #N в очереди / выполняется,
|
||||
~N с»; итог (выполнен / не выполнен: текст / отменён / потерян) — текстом с цветом статуса до следующего события
|
||||
или 10 с для «выполнен». Тексты событий — нынешние `statusText` для пакетных событий.
|
||||
11. **Обновление из Jira переезжает в оболочку** (`web/src/shell/refresh.ts` — хук и контекст): постановка
|
||||
`POST /refresh {full: true, author: "viewer"}` с проверкой `GET /agent`, один опрос `GET /batches/{id}` раз в 3 с,
|
||||
остановка на терминальном статусе, 4xx, 404 и после 5 ошибок «хаб не отвечает» подряд — правила прежние (M-2…M-4
|
||||
#20). Подхват незавершённого пакета `viewer` с первой командой `refresh` — при входе в приложение и после каждой
|
||||
успешной загрузки снимка доски (подписка на кэш запросов), кроме перечитывания после своего пакета. По завершении
|
||||
пакета инвалидируются снимки всех досок (`["board"]`), списки задач (`["issues"]`), карточки (`["issue"]`) и
|
||||
проекты (`["projects"]`, счётчики задач). Доска, показывающая файл, файл не заменяет (M-1). «Новый проект» отдаёт
|
||||
`batch_id` созданного пакета прямо в оболочку — опрос начинается сразу, без подхвата.
|
||||
12. **Доска после переезда** (`useBoardSource`): источник (хаб / файл), `reload`, `loadFile`, события снимка
|
||||
(загрузка, хаб не отвечает, файл не разобран, JSON применён, ошибка) — как были; пакетных событий нет. Строка
|
||||
состояния доски показывает только свои события, пакетные — в оболочке.
|
||||
13. **Шапка доски** (липкая, как сейчас): `PageHeader` «Доска связей», подзаголовок — счётчики («42 задачи из 120 ·
|
||||
+5 соседей · 60 связей из 80 · скрыто без связей: 3»), источник — `Badge` («хаб» не показывается; «из файла:
|
||||
имя» с кнопкой ✕ = «Перечитать»), событие доски — текстом под заголовком; справа — `Button secondary` «Проблемы»
|
||||
со счётчиком (`Badge` ошибки, если > 0) и меню ⋯: «Перечитать», «Загрузить файл…», «Экспорт JSON», «Таблица
|
||||
связей» (переключатель), «Привязка к сетке» (переключатель), разделитель, «Сбросить раскладку» (с прежним
|
||||
подтверждением). Темы в меню доски нет.
|
||||
14. **Панель управления доски** — `Panel` в одну строку (перенос по ширине): «Спринты» и «Исполнители» —
|
||||
мультиселекты (нынешний `MultiSelect`, кнопка в виде `Select` с подписью внутри), разделитель, группа «Показать»:
|
||||
переключатели «Соседи по связям», «Только передачи», «Скрыть без связей»; группа «Режим»: «Критический путь», «По
|
||||
датам». Переключатель — кнопка-«таблетка» с иконкой, включённый — стиль «выбрано» (фон `--color-bg-selected-green`,
|
||||
зелёная граница и текст), `aria-pressed`. Подсказки (`title`) — нынешние.
|
||||
15. **Легенда** — под панелью, сворачивается кнопкой «Легенда» (`aria-expanded`); свёрнута — видно число скрытых
|
||||
видов работ и типов связей («скрыто: 2»), если они есть. Состояние свёртки — `localStorage` ключ
|
||||
`jira-hub-legend` (`"0"` свёрнута; по умолчанию развёрнута). Поведение пунктов (щелчок скрывает вид / тип) —
|
||||
прежнее.
|
||||
16. **Карточки на доске:** радиус 12, граница `--color-border-default`, поверхность; ключ — моно, цвет действия;
|
||||
статус — `Badge`; размеры, ручки перетаскивания, растяжка и измерение высоты — прежние (меняются только цвета,
|
||||
радиусы и шрифт; высота по содержимому считается тем же слоем измерения, поэтому смена шрифта не ломает раскладку,
|
||||
а сохранённые размеры карточек остаются). Дорожки — чередование через `--color-bg-subtle`.
|
||||
17. **Список задач.** `PageHeader` «Задачи», подзаголовок «показано N» (и «есть ещё», если следующая порция есть).
|
||||
Под ним `Panel` фильтров: строка 1 — `SearchInput` «Ключ или тема» (широкий), `Segmented` «Открытые | Все»
|
||||
(замена флажка «скрыть закрытые», адрес тот же: «Все» — `closed=1`), переключатель-«таблетка» «Только соседи»
|
||||
(`neighbor=1`), кнопка «Фильтры» со счётчиком выбранных из шести списков; строка 2 (при выбранных) — `Chip`
|
||||
«Спринт: X ✕» на каждый выбранный список и «Сбросить» (снимает шесть списков, поиск и переключатели не трогает);
|
||||
панель «Фильтры» раскрывается под строкой сеткой `Field`+`Select` по 3 в ряд (спринт, исполнитель, вид работы,
|
||||
статус Jira, метка, эпик). Раскрыта ли панель — в `sessionStorage` (`jira-hub-issue-filters`). Подписи и варианты
|
||||
списков, «(нет на доске)» — как сейчас.
|
||||
18. **Таблица задач** — в `Panel` без внутреннего отступа, шапка липкая; колонки: Ключ (моно, ссылка; рядом `Badge`
|
||||
«сосед»), Тема, Статус Jira (`Badge`), Исполнитель, Срок (просрочено — цветом ошибки, по правилу карточки), Спринт,
|
||||
Эпик, Вид работы, Тип. Строка 44 px, при наведении — приглушённый фон; щелчок по строке (кроме ссылок и выделения
|
||||
текста) открывает карточку тем же переходом, что ключ. «Показать ещё» — `Button secondary` по центру под таблицей.
|
||||
Пусто — `Notice` сведений «По этим фильтрам задач нет».
|
||||
19. **Карточка задачи** — `Dialog` 1000 px, радиус 20; шапка: ключ (моно), `Badge` статуса, справа `Button secondary`
|
||||
«Открыть в Jira» с иконкой внешней ссылки и `IconButton` «Закрыть»; тема — 20/28 Bold; строка фактов — 14/20
|
||||
вторичным, «просрочено» — `Badge` ошибки. Разделы — заголовки 16/24 Semibold (без верхнего регистра); правая
|
||||
колонка — `Panel` приглушённого фона: заметки, прочие поля, команды. Форма заметок — `Field`/`Select`/`textarea`
|
||||
системы, «Сохранить» `primary`, «Отмена» `secondary`. Содержание, порядок и поведение разделов — прежние.
|
||||
20. **Вход** — `Panel` 480 px по центру канвы: заголовок 20/28, пояснение, поле 40 px, `Button primary` «Войти»
|
||||
(40 px); ошибка — `Notice` ошибки. **«Новый проект»** — `Dialog` 480 px с `Field`-полями, «Создать» `primary`,
|
||||
«Отмена» `secondary`, ошибка хаба — `Notice` у кнопок. **«Не найдено»**, **ошибка отрисовки**, **проект не
|
||||
найден**, **нет проектов** — `Notice` в контейнере с действием.
|
||||
21. **Тема** — `ThemeSwitch` в оболочке, два положения. Ключ `graph-theme` прежний: без сохранённого выбора (или `""`)
|
||||
— тема системы, переключатель показывает её; щелчок сохраняет `light` или `dark`. Пункта «Тема: системная» больше
|
||||
нет: вернуть «как в системе» — убрать выбор нельзя, это осознанное упрощение (вторичный сценарий).
|
||||
22. **Фокус и доступность:** видимое кольцо фокуса 2 px `--color-focus-ring` со смещением 2 px у всех интерактивных
|
||||
элементов; цвет не единственный признак состояния (включённые переключатели — `aria-pressed` и иконка-галочка или
|
||||
заливка; ошибки — иконка); `prefers-reduced-motion` отключает переходы.
|
||||
23. **Проверка вида.** Скриншоты Playwright на тестовом хабе (`web/scripts/test-hub.sh`) 1920×1080 и 390×844 в
|
||||
светлой и тёмной темах: доска, задачи с раскрытыми фильтрами, карточка, вход, «Новый проект» — прикладываются к PR
|
||||
(в репозиторий не попадают, снимаются скриптом `web/e2e/screens.spec.ts` по флагу `SCREENS=1`). Пиксельных
|
||||
эталонов в наборе тестов нет: вид будет меняться по замечаниям владельца.
|
||||
24. **Тесты.** Логика доски и golden-эталоны не меняются. Тесты, привязанные к разметке (`Toolbar`, `BoardPage`,
|
||||
`IssuesPage`, `LoginScreen`, `projects`, e2e), переписываются под новые роли и подписи; тесты опроса пакета
|
||||
переезжают из `useBoardSource.test.tsx` в тест хука оболочки. Новые: компоненты `ui/` (роли, `aria-*`, состояния),
|
||||
тест «цвета только из токенов», тест переключателя темы.
|
||||
|
||||
## Risks / Trade-offs
|
||||
|
||||
- **Шрифт меняет ширину текста** → сохранённые высоты карточек на доске могут обрезать текст. Смягчение: высота «по
|
||||
содержимому» пересчитывается двойным щелчком по уголку, как сейчас; «Сбросить раскладку» доступна.
|
||||
- **Переезд обновления в оболочку** затрагивает отработанные правила опроса (M-1…M-5 #20). Смягчение: перенос кода и
|
||||
тестов почти без изменений, меняется только владелец состояния и набор инвалидируемых запросов.
|
||||
- **Большой дифф стилей** → регрессии вида на редких состояниях (ящик «Проблемы», режим «по датам», экспорт).
|
||||
Смягчение: скриншоты всех экранов в PR, владелец смотрит живое приложение.
|
||||
|
||||
## Decisions made without discussion
|
||||
|
||||
Владелец 21.10.2026 отдал редизайн и UX на усмотрение агента («сделай редизайн и проработай UX сам, потом будем
|
||||
отталкиваться»); его входные данные — только: «и то и то» (родство с AURA и исправление неудобств), примеры
|
||||
неудобств (обновление из Jira только с доски, перегрузка фильтрами, «падинги не те»), сценарий (раз в спринт граф,
|
||||
затем поиск и перепроверка задач), «фильтры все нужны». Всё ниже — решения агента.
|
||||
|
||||
| # | Дата | Решение | Обоснование | Цена ошибки |
|
||||
|---|---|---|---|---|
|
||||
| 1 | 21.10.2026 | Без Ant Design, токены вручную (решение 1) | antd — замена всех контролов и тяжёлая зависимость ради вида | низкая: вид тот же, antd можно добавить позже |
|
||||
| 2 | 21.10.2026 | Mulish вместо Proxima Nova (решение 4) | Proxima Nova платная; Mulish ближе по пропорциям, с кириллицей | низкая: замена шрифта — одна строка токена и пакет |
|
||||
| 3 | 21.10.2026 | Свои SVG-иконки без библиотеки (решение 5) | ~20 иконок, зависимость не нужна | низкая |
|
||||
| 4 | 21.10.2026 | Предупреждение разрешено вне контролов (решение 7) | хабу нужно отличать «офлайн» и «прежние данные» от ошибок | низкая |
|
||||
| 5 | 21.10.2026 | Доска во всю ширину, прочее — контейнер 1440 (решение 8) | графу нужна ширина, таблице и карточке — ограничение строки | низкая |
|
||||
| 6 | 21.10.2026 | Обновление из Jira и ход пакета — в оболочке, инвалидация всех данных (решения 10–12) | пример неудобства владельца; после обновления устаревают и список, и карточка | средняя: правила опроса переносятся, регрессии ловят перенесённые тесты |
|
||||
| 7 | 21.10.2026 | Редкие действия доски — в меню ⋯, режимы — группами «Показать» / «Режим» (решения 13–14) | «перегрузка фильтрами»; доска нужна раз в спринт, режимы — основное, файл/экспорт — редко | низкая: перестановка кнопок |
|
||||
| 8 | 21.10.2026 | Легенда сворачивается, состояние в `localStorage` (решение 15) | легенда занимает строку, нужна для чтения цветов — свёрнута по выбору | низкая |
|
||||
| 9 | 21.10.2026 | Список: поиск и «Открытые / Все» на виду, шесть списков — в панели «Фильтры» с плашками (решение 17) | частые действия на виду, редкие — по запросу; выбранное видно плашками | низкая |
|
||||
| 10 | 21.10.2026 | Щелчок по строке таблицы открывает карточку (решение 18) | основной сценарий списка — открыть и перепроверить задачу | низкая |
|
||||
| 11 | 21.10.2026 | Тема — два положения, системная только без сохранённого выбора (решение 21) | переключатель AURA двухпозиционный | низкая: вернуть третье положение — пункт меню |
|
||||
| 12 | 21.10.2026 | Проверка вида скриншотами в PR, без пиксельных эталонов (решение 23) | вид будет меняться по замечаниям, эталоны мешали бы | низкая |
|
||||
40
openspec/changes/redesign/proposal.md
Normal file
40
openspec/changes/redesign/proposal.md
Normal file
@ -0,0 +1,40 @@
|
||||
## Why
|
||||
|
||||
Веб-приложение хаба выросло из перенесённой доски `links.html` и выглядит и ведёт себя как набор страниц, собранных в
|
||||
разное время: свои цвета и отступы у каждой страницы, «Обновить из Jira» и тема — только в шапке доски, фильтры списка
|
||||
задач — сплошная строка из шести выпадающих списков, двух флажков и поиска. Владелец (21.10.2026, jira-hub#65) хочет,
|
||||
чтобы хаб выглядел как родственное приложение AURA (дизайн-система из handoff AURA-xlsx-tool 29.09.2026 и Figma
|
||||
«Универсалки», материалы — `~/projects/jira-bridge/design/`), и чтобы интерфейс стал логичнее.
|
||||
|
||||
Сценарий владельца: раз в спринт — обзор графа связей на доске; в остальное время — поиск и перепроверка задач в
|
||||
списке и карточке. Все фильтры нужны, но упакованы должны быть удобнее. Редизайн и UX владелец отдал на усмотрение
|
||||
агента; дальше — доработки от результата.
|
||||
|
||||
## What Changes
|
||||
|
||||
- **Дизайн-система в приложении:** токены AURA (цвета светлой и тёмной темы, типографика, шаг 4 px, радиусы, высоты
|
||||
контролов, фокус) — `web/src/styles/tokens.css`; набор базовых компонентов `web/src/ui/` (кнопки-«таблетки»,
|
||||
иконки, сегментный переключатель, переключатель темы, поле, выпадающий список, флажок, бейдж, сообщение, панель,
|
||||
окно, меню); все страницы собраны из них. Шрифт — Mulish (свободная замена Proxima Nova, с кириллицей), моноширинный
|
||||
— IBM Plex Mono, как был.
|
||||
- **Верхняя панель приложения:** название, проект, разделы сегментным переключателем, блок данных Jira (время
|
||||
последнего полного обновления, агент на связи / офлайн, «Обновить из Jira» и ход пакета), переключатель темы, меню
|
||||
с «Сменить токен». Обновление из Jira и тема доступны с любой страницы.
|
||||
- **Обновление из Jira — уровень приложения:** пакет `refresh` ставит и опрашивает оболочка; по завершении
|
||||
перечитываются снимки доски, список задач и открытая карточка. Доска сохраняет только своё: источник (хаб / файл),
|
||||
«Перечитать», загрузку файла и события снимка.
|
||||
- **Доска:** шапка страницы (название, счётчики, источник, «Проблемы», меню), панель управления (спринты, исполнители,
|
||||
режимы показа группами), сворачиваемая легенда; редкие действия — в меню. Поведение доски (модель, выборка,
|
||||
критический путь, раскладка, ключи `localStorage`) не меняется.
|
||||
- **Список задач:** поиск и «Открытые / Все» на виду, остальные фильтры — в раскрывающейся панели «Фильтры (N)»,
|
||||
выбранные — снимаемыми плашками; таблица в панели, строка открывает карточку, «сосед» — плашкой у ключа. Адрес и
|
||||
запросы к хабу — как были.
|
||||
- **Карточка задачи, вход, «Новый проект», «не найдено», ошибки** — в стиле системы, содержание и поведение прежние.
|
||||
|
||||
## Non-Goals
|
||||
|
||||
- Ant Design и генерация токенов из `tokens.json` скриптом (`design-system:sync`): токены переносятся вручную.
|
||||
- Proxima Nova (платный шрифт) и любые ресурсы вне сборки.
|
||||
- Новые функции: правки в Jira из интерфейса (`viewer-edit`), страницы очереди, агента и настроек.
|
||||
- Изменения API хаба, MCP, агента; изменения логики доски (golden-тесты не трогаются).
|
||||
- Мобильная вёрстка доски сверх нынешней (узкий экран — только без поломок).
|
||||
320
openspec/changes/redesign/specs/web-app/spec.md
Normal file
320
openspec/changes/redesign/specs/web-app/spec.md
Normal file
@ -0,0 +1,320 @@
|
||||
## ADDED Requirements
|
||||
|
||||
### Requirement: Дизайн-система
|
||||
Приложение SHALL следовать дизайн-системе AURA (handoff AURA-xlsx-tool 29.09.2026, Figma «Универсалки») в светлой и
|
||||
тёмной темах. Токены — цвета по семантическим ролям для обеих тем, типографика, отступы с шагом 4 px, радиусы, высоты
|
||||
контролов, фокус, движение — SHALL быть определены в `web/src/styles/tokens.css` (вместе с токенами приложения: палитра
|
||||
видов работ, приоритеты, стрелки и дорожки доски); прочие файлы стилей MUST NOT содержать литералов цвета и SHALL
|
||||
брать цвета из токенов. Экраны SHALL собираться из компонентов `web/src/ui/` (кнопки, кнопка-иконка, сегментный
|
||||
переключатель, переключатель темы, поле с подписью, поле ввода и поиска, выпадающий список, флажок, бейдж, снимаемая
|
||||
плашка, сообщение, панель, шапка страницы, окно, меню). Геометрия: контролы 32 px (крупные 40 px) с радиусом 10 px,
|
||||
кнопки — «таблетки», панели — радиус 16 px, окна — радиус 20 px, флажок 16×16 px; текст Regular 14/20, подписи
|
||||
Semibold 14/20, заголовок страницы Bold 20/28. Страницы, кроме доски, SHALL занимать контейнер до 1440 px по центру с
|
||||
боковым отступом 24 px (16 px на экранах уже 640 px); доска — всю ширину окна с отступом 24 px. Иконки — SVG из
|
||||
сборки, не символы текста. Каждый интерактивный элемент SHALL иметь видимое кольцо фокуса; цвет MUST NOT быть
|
||||
единственным признаком состояния.
|
||||
|
||||
#### Scenario: Цвет вне токенов
|
||||
- **WHEN** в любом `*.css` в `web/src`, кроме `styles/tokens.css`, появился литерал цвета
|
||||
- **THEN** тесты фронта падают с именем файла и литералом
|
||||
|
||||
#### Scenario: Тёмная тема
|
||||
- **WHEN** выбрана тёмная тема
|
||||
- **THEN** канва `#181818`, поверхности `#1f1f1f`, основной текст `#f2f4f2`, основное действие `#69a93d` на всех
|
||||
страницах, в окнах и меню
|
||||
|
||||
### Requirement: Обновление из Jira
|
||||
Действие «Обновить из Jira» SHALL быть в верхней панели приложения и доступно с любой страницы. Оно SHALL проверять
|
||||
агента (`GET /agent`; офлайн — предупреждение, пакет всё равно ставится), ставить `POST /refresh {full: true, author:
|
||||
"viewer"}` и опрашивать `GET /batches/{id}` каждые 3 с одним опросом до терминального статуса пакета. Кнопка SHALL быть
|
||||
недоступна (с признаком загрузки), пока запрос `/refresh` в пути или пакет выполняется. Незавершённый пакет автора
|
||||
`viewer`, первая команда которого — `refresh`, SHALL подхватываться при входе в приложение и после каждой успешной
|
||||
загрузки снимка доски, кроме перечитывания снимка после завершения собственного пакета; пакеты `viewer` с другими
|
||||
командами не подхватываются. Опрос SHALL прекращаться при ответе 4xx (404 — «пакета нет в хабе») и после 5 ошибок
|
||||
«хаб не отвечает» подряд, с сообщением и снова доступной кнопкой. По завершении пакета приложение SHALL перечитывать
|
||||
снимки досок, списки задач, открытую карточку и список проектов; доска, показывающая снимок из файла, его не заменяет.
|
||||
Пакет, поставленный формой «Новый проект», SHALL опрашиваться так же сразу после создания.
|
||||
|
||||
Верхняя панель SHALL показывать блок данных Jira: состояние агента («агент на связи» / «агент офлайн», по `GET /agent`
|
||||
при входе и раз в 60 с), время последнего полного обновления («данные на ДД.ММ ЧЧ:ММ» по `last_sync` снимка доски
|
||||
текущего проекта, «полного обновления не было», если его нет) и одно текущее событие пакета: в очереди / выполняется (с
|
||||
оставшимся временем, с пометкой «агент офлайн» и «хаб не отвечает, повтор»), выполнен, не выполнен с текстом ошибки,
|
||||
отменён, опрос остановлен с причиной, хаб не ответил на постановку; новое событие заменяет предыдущее, «выполнен»
|
||||
исчезает через 10 с.
|
||||
|
||||
#### Scenario: Обновление со списка задач
|
||||
- **WHEN** открыт `/app/issues`, нажато «Обновить из Jira», пакет выполнен
|
||||
- **THEN** в хабе один пакет `refresh` автора `viewer`; список задач перезапрошен; верхняя панель показывала «пакет #N
|
||||
выполняется», затем «пакет #N выполнен»
|
||||
|
||||
#### Scenario: Один прогон на всё приложение
|
||||
- **WHEN** нажата «Обновить из Jira» и, пока пакет выполняется, выполнен переход между разделами и «Перечитать» на доске
|
||||
- **THEN** кнопка остаётся недоступной, в хабе один пакет, его опрашивает один опрос
|
||||
|
||||
#### Scenario: Опрос получил 404
|
||||
- **WHEN** опрос пакета получил 404
|
||||
- **THEN** опрос остановлен, верхняя панель показывает «пакета нет в хабе», кнопка «Обновить из Jira» снова доступна
|
||||
|
||||
#### Scenario: Пакет с другой вкладки
|
||||
- **WHEN** пакет `viewer` с командой `refresh` поставлен с другой вкладки и не завершён, приложение открыто
|
||||
- **THEN** верхняя панель показывает «пакет #N выполняется» и по завершении данные перечитываются
|
||||
|
||||
#### Scenario: Пакет с другими командами
|
||||
- **WHEN** незавершённый пакет автора `viewer` состоит из команд `ensure_fields`
|
||||
- **THEN** приложение его не опрашивает, кнопка «Обновить из Jira» доступна
|
||||
|
||||
#### Scenario: Агент офлайн
|
||||
- **WHEN** `GET /agent` отвечает `online: false` и нажато «Обновить из Jira»
|
||||
- **THEN** пакет поставлен, событие — «агент офлайн — пакет выполнится, когда он выйдет на связь; пакет #N в очереди…»
|
||||
|
||||
### Requirement: Тема
|
||||
Переключатель темы SHALL быть в верхней панели приложения: два положения — светлая и тёмная. Выбор SHALL храниться в
|
||||
`localStorage` под ключом `graph-theme` (`light` / `dark`), как у прежней доски; без сохранённого выбора или со
|
||||
значением `""` тема SHALL следовать настройке системы, и переключатель показывает её. Тема применяется до первой
|
||||
отрисовки, без мигания.
|
||||
|
||||
#### Scenario: Выбор темы сохраняется
|
||||
- **WHEN** в системе светлая тема, переключатель переведён в «тёмная», страница перезагружена
|
||||
- **THEN** приложение в тёмной теме, в `localStorage` `graph-theme` = `dark`
|
||||
|
||||
## MODIFIED Requirements
|
||||
|
||||
### Requirement: Оболочка и адреса
|
||||
Приложение SHALL открываться под префиксом `/app/` и иметь страницы `/app/board` (доска связей), `/app/issues` (список
|
||||
задач) и `/app/issues/{key}` (карточка задачи — окно поверх страницы, требование «Карточка задачи»); `/app` и `/app/` SHALL вести на `/app/board`, неизвестный путь —
|
||||
на страницу «не найдено» со ссылками на разделы. Верхняя панель SHALL содержать название, выпадающий список проектов
|
||||
(«<метка> — <название>», текущий отмечен, в конце «+ Новый проект…»; требование «Проект в приложении»), разделы
|
||||
«Доска связей» и «Задачи» сегментным переключателем (текущий отмечен `aria-current="page"`), блок данных Jira с
|
||||
действием «Обновить из Jira» (требование «Обновление из Jira»), переключатель темы (требование «Тема») и меню с
|
||||
действием «Сменить токен». На узких экранах панель SHALL переноситься на две строки без горизонтальной прокрутки.
|
||||
Неожиданная ошибка отрисовки страницы SHALL показываться сообщением с кнопкой «Перезагрузить», не пустым экраном.
|
||||
|
||||
#### Scenario: Переход между разделами
|
||||
- **WHEN** на доске выбран раздел «Задачи»
|
||||
- **THEN** адрес — `/app/issues` с текущим проектом, показан список задач; «назад» возвращает на доску
|
||||
|
||||
#### Scenario: Сменить токен
|
||||
- **WHEN** в меню верхней панели выбрано «Сменить токен»
|
||||
- **THEN** токен очищен, показан экран входа
|
||||
|
||||
### Requirement: Доска связей
|
||||
Страница `/app/board` SHALL воспроизводить поведение доски связей хаба (`links.html` ревизии 3931a88) без изменений,
|
||||
кроме перечисленных в этом требовании и в требовании «Источник данных доски»: построение модели из `GET /board`, CSV
|
||||
фильтра Jira и JSON доски (`arm-ka-links/1`); дорожки по эпикам со свёрткой и перестановкой, служебные дорожки внизу;
|
||||
карточки с перетаскиванием, растяжкой, размером по содержимому и привязкой к сетке; стрелки связей с выравниванием;
|
||||
фильтры по спринтам и исполнителям; легенда со скрытием видов работ и типов связей; «соседи по связям», «только
|
||||
передачи», «скрыть без связей», «критический путь», «по датам» с колонками дат и их растяжкой; ящик «Проблемы»; таблица
|
||||
связей; экспорт и импорт JSON; «Сбросить раскладку». Раскладка, дорожки и фильтры SHALL храниться в
|
||||
`localStorage` под теми же ключами `graph-<имя>…`, что у `links.html`, с той же миграцией старых ключей.
|
||||
Результат построения модели, видимой выборки, критического пути и проблем SHALL совпадать с результатом `links.html`
|
||||
на одних и тех же данных и фильтрах.
|
||||
|
||||
Шапка доски (закреплена при прокрутке) SHALL содержать заголовок «Доска связей», счётчики (задачи из всех, соседи по
|
||||
связям, связи из всех, скрытые без связей), пометку источника, если на доске снимок из файла (с действием вернуться к
|
||||
данным хаба), событие доски, кнопку «Проблемы» со счётчиком и меню с действиями «Перечитать», «Загрузить файл…»,
|
||||
«Экспорт JSON», «Таблица связей», «Привязка к сетке» и «Сбросить раскладку» (с подтверждением). Под шапкой — панель
|
||||
управления: мультиселекты «Спринты» и «Исполнители», переключатели группы «Показать» («Соседи по связям», «Только
|
||||
передачи», «Скрыть без связей») и группы «Режим» («Критический путь», «По датам»), каждый с `aria-pressed` и прежней
|
||||
подсказкой. Под панелью — легенда, которая сворачивается кнопкой «Легенда»; свёрнутая показывает число скрытых видов
|
||||
работ и типов связей; состояние свёртки хранится в `localStorage` под ключом `jira-hub-legend`. «Обновить из Jira»,
|
||||
тема и «Сменить токен» — в верхней панели приложения, на доске их нет.
|
||||
|
||||
У карточки задачи (не у заглушки) SHALL быть значок «Открыть карточку»,
|
||||
открывающий `/app/issues/{key}` окном поверх доски; ключ карточки по-прежнему открывает задачу в Jira. Пока окно открыто
|
||||
и после его закрытия доска SHALL сохранять прокрутку, раскладку, фильтры и строку состояния. Ещё два отличия от `links.html` (R-10, R-18): двойной щелчок по уголку
|
||||
карточки меряет высоту по содержимому при новой ширине, а не при прежней; указатель при перетаскивании и растяжке
|
||||
захватывается на нажатом элементе, а не на карточке, поэтому двойной щелчок по уголку сбрасывает размер. Загрузка файла CSV/JSON доступна только после входа, с экрана входа её нет.
|
||||
Карточка задачи, статус Jira которой входит в `closed_statuses`, SHALL быть приглушена (полупрозрачна), под
|
||||
указателем и с фокусом клавиатуры внутри карточки — в полную яркость; стрелка связи, у которой хотя бы один конец — такая задача, SHALL быть приглушена так же.
|
||||
Приглушение — только вид: модель, видимая выборка, критический путь, проблемы, раскладка и экспорт JSON от него не
|
||||
зависят; переключателя нет; правило одно для данных хаба, CSV и JSON.
|
||||
При возврате на `/app/board` с другой страницы приложения в той же вкладке доска SHALL восстанавливать прежнюю
|
||||
прокрутку; после перезагрузки страницы доска открывается сверху.
|
||||
|
||||
#### Scenario: Раскладка переживает переход
|
||||
- **WHEN** раскладка карточек сохранена страницей `links.html`, открыта `/app/board` с теми же данными
|
||||
- **THEN** карточки стоят на тех же местах, дорожки в том же порядке и свёртке, фильтры те же
|
||||
|
||||
#### Scenario: Совпадение со старой доской
|
||||
- **WHEN** на одной фикстуре `GET /board` и одном наборе фильтров построены модель, видимая выборка, критический путь
|
||||
и проблемы старой страницей и приложением
|
||||
- **THEN** результаты совпадают
|
||||
|
||||
#### Scenario: Открыть карточку
|
||||
- **WHEN** на карточке AURORA-1620 нажат значок «Открыть карточку»
|
||||
- **THEN** адрес `/app/issues/AURORA-1620`, карточка задачи открыта окном, под ним доска
|
||||
|
||||
#### Scenario: Возврат с карточки
|
||||
- **WHEN** доска прокручена вниз, нажат значок «Открыть карточку», затем окно закрыто
|
||||
- **THEN** адрес `/app/board`, доска показана с той же прокруткой и раскладкой, без повторной загрузки `GET /board`
|
||||
|
||||
#### Scenario: Возврат из другого раздела
|
||||
- **WHEN** доска прокручена вниз, выбран раздел «Задачи», затем раздел «Доска связей»
|
||||
- **THEN** доска показана с той же прокруткой
|
||||
|
||||
#### Scenario: Закрытые на доске приглушены
|
||||
- **WHEN** на доске AURORA-1620 «В работе», AURORA-1621 «Готово», AURORA-1622 «Снято», связь AURORA-1620 → AURORA-1621
|
||||
- **THEN** карточки AURORA-1621 и AURORA-1622 и стрелка AURORA-1620 → AURORA-1621 приглушены, AURORA-1620 — нет;
|
||||
под указателем AURORA-1621 — в полную яркость; критический путь и ящик «Проблемы» те же, что без приглушения
|
||||
|
||||
#### Scenario: Свёрнутая легенда
|
||||
- **WHEN** в легенде скрыт вид работы «тестирование», легенда свёрнута, страница перезагружена
|
||||
- **THEN** легенда свёрнута, рядом с кнопкой «Легенда» — «скрыто: 1»; карточек вида «тестирование» на доске нет
|
||||
|
||||
### Requirement: Источник данных доски
|
||||
Доска SHALL хранить явный источник данных — хаб или файл. Пока показан снимок из файла (CSV или JSON-снимок), ответы
|
||||
хаба SHALL его не заменять; «Перечитать» SHALL возвращать источник «хаб» и загружать `GET /board` заново без обращения к
|
||||
Jira. Импорт JSON в режиме хаба SHALL применять раскладку, дорожки и фильтры и перечитывать хаб. Шапка доски SHALL
|
||||
показывать источник данных (пометка «из файла» с именем файла; данные хаба пометки не имеют) и одно текущее событие
|
||||
доски (загрузка; хаб не отвечает — прежние данные, а при снимке из файла — «на доске снимок из файла»; ошибка разбора
|
||||
файла — «файл не разобран: <причина>»; раскладка и фильтры из JSON применены; ошибка); новое событие заменяет
|
||||
предыдущее целиком. События пакета обновления из Jira показывает верхняя панель приложения (требование «Обновление из
|
||||
Jira»), не шапка доски.
|
||||
|
||||
#### Scenario: Поздний ответ хаба не затирает файл
|
||||
- **WHEN** запрос `GET /board` в пути, и в это время загружен CSV
|
||||
- **THEN** после ответа хаба на доске по-прежнему данные CSV с пометкой «из файла»
|
||||
|
||||
#### Scenario: Пакет завершился при снимке из файла
|
||||
- **WHEN** на доске снимок из CSV и завершился пакет обновления из Jira
|
||||
- **THEN** на доске по-прежнему данные CSV с пометкой «из файла»; после «Перечитать» — данные хаба
|
||||
|
||||
#### Scenario: Хаб недоступен при снимке из файла
|
||||
- **WHEN** хаб не отвечает и загружен JSON-снимок доски
|
||||
- **THEN** шапка показывает «хаб не отвечает — на доске снимок из файла», а не «показаны прежние данные»
|
||||
|
||||
|
||||
#### Scenario: Нет второго полного прогона
|
||||
- **WHEN** нажата «Обновить из Jira» и, пока пакет выполняется, доска перерисована (фильтр, «Перечитать»)
|
||||
- **THEN** кнопка «Обновить из Jira» в верхней панели остаётся недоступной, в хабе один пакет, его опрашивает один опрос
|
||||
|
||||
#### Scenario: Пакет пропал
|
||||
- **WHEN** опрос пакета получил 404, на доске данные хаба
|
||||
- **THEN** доска показывает прежние данные без своего события об ошибке; сообщение о пропавшем пакете — в верхней панели
|
||||
|
||||
#### Scenario: Пакет после «Перечитать»
|
||||
- **WHEN** пакет `viewer` поставлен с другой вкладки и не завершён, на доске нажато «Перечитать»
|
||||
- **THEN** верхняя панель показывает «пакет #N выполняется», по завершении доска перечитывает данные
|
||||
|
||||
#### Scenario: «Перечитать» во время пакета
|
||||
- **WHEN** пакет `viewer` выполняется и на доске нажато «Перечитать»
|
||||
- **THEN** верхняя панель по-прежнему показывает «пакет #N выполняется», шапка доски — без события пакета
|
||||
|
||||
#### Scenario: Чужой пакет не подхватывается
|
||||
- **WHEN** незавершённый пакет автора `viewer` состоит из команд `ensure_fields`, открыта доска
|
||||
- **THEN** доска и верхняя панель его не опрашивают, кнопка «Обновить из Jira» доступна
|
||||
|
||||
### Requirement: Список задач
|
||||
Страница `/app/issues` SHALL показывать задачи текущего проекта из `GET /issues?project=<текущий>` таблицей: ключ
|
||||
(открывает карточку задачи окном поверх списка) с пометкой «сосед» (относительно проекта), тема, статус Jira,
|
||||
исполнитель, срок (`planned_end`, иначе `duedate`; просроченный срок незакрытой задачи выделен), спринт, эпик (имя
|
||||
эпика, иначе ключ), вид работы, тип; статуса по факту в списке нет. Щелчок по строке (кроме ссылок и выделения текста)
|
||||
SHALL открывать карточку задачи так же, как ключ. Шапка страницы SHALL показывать число показанных задач.
|
||||
|
||||
Фильтры SHALL соответствовать параметрам `GET /issues` (спринт, исполнитель, вид работы, статус Jira, метка, эпик,
|
||||
только соседи, поиск по ключу или теме, скрыть закрытые), храниться в query-строке адреса вместе с `project` и
|
||||
применяться сервером. На виду SHALL быть поиск, переключатель «Открытые | Все», переключатель «Только соседи» и
|
||||
кнопка «Фильтры» с числом выбранных выпадающих списков; шесть выпадающих списков (спринт, исполнитель, вид работы,
|
||||
статус Jira, метка, эпик) — в панели, которая раскрывается этой кнопкой; каждый выбранный список SHALL показываться
|
||||
снимаемой плашкой «<подпись>: <значение>» и под строкой фильтров должно быть действие «Сбросить», снимающее все шесть
|
||||
списков. Варианты выпадающих списков SHALL браться из снимка `GET /board?project=<текущий>`; значение фильтра из
|
||||
адреса, которого нет среди вариантов, SHALL показываться в списке с пометкой «(нет на доске)». Положение «Открытые»
|
||||
SHALL быть по умолчанию (в адресе ничего), «Все» — `closed=1` в адресе; «Открытые» — запрос с `hide_closed=1`,
|
||||
кроме случая, когда в фильтре «Статус Jira» выбран статус из `closed_statuses`. Параметр `fact_status` в адресе
|
||||
игнорируется. Задачи SHALL загружаться порциями по 50 строк; «Показать ещё» SHALL быть доступна, пока последняя
|
||||
порция полная.
|
||||
|
||||
#### Scenario: Фильтр в адресе
|
||||
- **WHEN** выбран спринт «AWPCA_Спринт 2» и исполнитель
|
||||
- **THEN** адрес содержит `project=`, `sprint=` и `assignee=`, таблица — результат `GET /issues` с этими параметрами; открытие этого
|
||||
адреса в новой вкладке показывает ту же выборку, кнопка «Фильтры» показывает 2, видны плашки спринта и исполнителя
|
||||
|
||||
#### Scenario: Снять фильтр плашкой
|
||||
- **WHEN** выбраны спринт и исполнитель, нажат ✕ на плашке спринта
|
||||
- **THEN** в адресе нет `sprint=`, `assignee=` остался, таблица перезапрошена
|
||||
|
||||
#### Scenario: Конец списка
|
||||
- **WHEN** вторая порция вернула 17 задач
|
||||
- **THEN** в таблице 67 задач, «Показать ещё» не показана
|
||||
|
||||
#### Scenario: Фильтр по закрытому спринту
|
||||
- **WHEN** открыт адрес `/app/issues?project=AWPCA&sprint=AWPCA_Спринт 0`, а в снимке доски такого спринта нет
|
||||
- **THEN** выпадающий список «Спринт» показывает «AWPCA_Спринт 0 (нет на доске)» выбранным, таблица — по этому фильтру
|
||||
|
||||
#### Scenario: Карточка поверх списка
|
||||
- **WHEN** список отфильтрован по спринту, прокручен и догружен «Показать ещё», открыта карточка задачи и окно закрыто
|
||||
- **THEN** адрес списка с тем же фильтром, в таблице те же строки, прокрутка та же
|
||||
|
||||
#### Scenario: Щелчок по строке
|
||||
- **WHEN** в таблице щёлкнута ячейка темы строки AURORA-1620
|
||||
- **THEN** адрес `/app/issues/AURORA-1620`, карточка открыта окном поверх списка
|
||||
|
||||
#### Scenario: Закрытые скрыты по умолчанию
|
||||
- **WHEN** открыт `/app/issues`, в зеркале задачи в статусах «В работе», «Готово», «Снято»
|
||||
- **THEN** выбрано «Открытые», запрос `GET /issues` с `hide_closed=1`, в таблице только «В работе»; после выбора «Все»
|
||||
адрес содержит `closed=1`, в таблице все три
|
||||
|
||||
#### Scenario: Выбран закрытый статус
|
||||
- **WHEN** выбрано «Открытые» и в «Статус Jira» выбрано «Готово»
|
||||
- **THEN** запрос `GET /issues?status=Готово` без `hide_closed`, в таблице задачи в статусе «Готово»
|
||||
|
||||
### Requirement: Проект в приложении
|
||||
Доска и список задач SHALL показывать один проект — текущий. Текущий проект SHALL задаваться параметром `project` в
|
||||
адресе `/app/board` и `/app/issues`; без параметра — последний выбранный в этом браузере (`localStorage`), если он есть
|
||||
в `GET /projects`, иначе первый из `GET /projects`; выбранный проект записывается в адрес без новой записи истории.
|
||||
Проекта из адреса нет в `GET /projects` — страница SHALL показывать «проект <метка> не найден» и выпадающий список
|
||||
проектов вместо данных. Проектов нет — доска и список SHALL показывать приглашение создать проект («+ Новый проект…»).
|
||||
Переходы «Доска связей» / «Задачи» SHALL сохранять текущий проект. Карточка задачи (`/app/issues/{key}`) от проекта не зависит.
|
||||
|
||||
Доска SHALL загружать `GET /board?project=<текущий>`; «Обновить из Jira» — `POST /refresh {full: true}`, обновляет все
|
||||
проекты. Раскладка, дорожки и фильтры доски в `localStorage` SHALL храниться отдельно для каждого проекта;
|
||||
у проекта `AWPCA` — на прежних ключах (раскладка, сделанная до проектов, сохраняется). Фильтр спринтов SHALL
|
||||
показываться, только если хотя бы у одной своей задачи снимка (`neighbor = false`) есть спринт; иначе — «колонки
|
||||
«Спринт» нет — показаны все задачи».
|
||||
|
||||
Форма «Новый проект» (пункт «+ Новый проект…» выпадающего списка) SHALL открываться окном поверх страницы: метка,
|
||||
название, номер доски Jira (необязательно; подсказка «scrum или kanban определится сам»). «Создать» — `POST /projects`
|
||||
с автором `viewer`; ошибка хаба 409/422 — текст `detail` рядом с кнопкой, окно остаётся открытым; успех — окно
|
||||
закрывается, текущим становится новый проект, верхняя панель показывает ход пакета обновления из ответа (`batch_id`).
|
||||
|
||||
#### Scenario: Первый вход после выкатки
|
||||
- **WHEN** открыт `/app/board` без `project`, в `localStorage` выбора нет, проекты `AWPCA` и `STOCKS_DRRK`
|
||||
- **THEN** адрес — `/app/board?project=AWPCA`, доска — `GET /board?project=AWPCA` с прежней раскладкой карточек
|
||||
|
||||
#### Scenario: Переключение проекта
|
||||
- **WHEN** на доске `AWPCA` в выпадающем списке выбран `STOCKS_DRRK`, затем открыт раздел «Задачи»
|
||||
- **THEN** доска — `GET /board?project=STOCKS_DRRK` со своей раскладкой; адрес «Задач» — `/app/issues?project=STOCKS_DRRK`
|
||||
|
||||
#### Scenario: Неизвестный проект в адресе
|
||||
- **WHEN** открыт `/app/issues?project=NOPE`
|
||||
- **THEN** «проект NOPE не найден» и список проектов; запроса `GET /issues` нет
|
||||
|
||||
#### Scenario: Создание проекта из формы
|
||||
- **WHEN** в форме введены `STOCKS_DRRK`, «Запасы ДРРК», доска пусто; нажато «Создать»
|
||||
- **THEN** `POST /projects {label: "STOCKS_DRRK", name: "Запасы ДРРК", author: "viewer"}`; текущий проект — `STOCKS_DRRK`;
|
||||
верхняя панель показывает «пакет #N выполняется»
|
||||
|
||||
#### Scenario: Метка занята
|
||||
- **WHEN** в форме введена метка `awpca`, хаб ответил 409
|
||||
- **THEN** окно открыто, рядом с «Создать» — текст ошибки хаба
|
||||
|
||||
#### Scenario: Канбан-проект
|
||||
- **WHEN** текущий проект — `STOCKS_DRRK`, ни у одной своей задачи снимка нет спринта (у соседа из `AWPCA` спринт есть)
|
||||
- **THEN** вместо фильтра спринтов — «колонки «Спринт» нет — показаны все задачи»
|
||||
|
||||
### Requirement: Сборка и разработка
|
||||
Приложение SHALL быть npm-проектом в `web/` (React, TypeScript, Vite) с точными версиями зависимостей и
|
||||
`package-lock.json`. `make web` SHALL устанавливать зависимости (`npm ci`) и собирать приложение в
|
||||
`src/jira_hub/web/app/`; каталог сборки не хранится в git. Шрифты (Mulish — текст интерфейса, IBM Plex Mono — ключи
|
||||
задач и исходники), иконки и прочие ресурсы страницы SHALL входить в сборку: приложение не обращается к внешним
|
||||
адресам. `make check` SHALL запускать `uv run pytest`, проверку типов и линтер фронта и тесты фронта; зелёным считается
|
||||
только весь набор. `npm run dev` SHALL поднимать сервер разработки с прокси маршрутов API на хаб из `JIRA_HUB_URL` (по
|
||||
умолчанию тестовый `http://127.0.0.1:8791`).
|
||||
|
||||
#### Scenario: Сборка с нуля
|
||||
- **WHEN** в чистом checkout выполнен `make web`
|
||||
- **THEN** в `src/jira_hub/web/app/` есть `index.html` и `assets/`; `git status` не показывает файлов сборки
|
||||
|
||||
#### Scenario: Без интернета
|
||||
- **WHEN** приложение открыто на машине без доступа в интернет
|
||||
- **THEN** страница не запрашивает ресурсов вне хаба, текст набран Mulish, ключи задач — IBM Plex Mono
|
||||
Loading…
x
Reference in New Issue
Block a user