18 KiB
MODIFIED Requirements
Requirement: Вход по токену
Приложение SHALL работать с API хаба того же хоста под клиентским токеном: хранить его в localStorage под ключом
jira-hub-token (и в памяти, если хранилище закрыто) и передавать в заголовке Authorization: Bearer каждого запроса.
Если токена нет или любой запрос ответил 401, приложение SHALL показывать экран входа вместо страниц: поле токена с
подсказкой uv run jira-hub tokens, без панелей страниц и без фоновых запросов, которые могли бы изменить поле ввода.
Введённый токен SHALL проверяться запросом GET /settings и сохраняться только при успехе. Действие «Сменить токен»
SHALL очищать токен и показывать экран входа. Ответ 401 SHALL выводить на экран входа, только если запрос ушёл с текущим токеном: запоздавший 401 на
запрос с прежним токеном после входа с новым не учитывается. Если проверка токена получила ответ 4xx, кроме 401,
экран входа SHALL показывать текст ошибки хаба, а не «хаб не отвечает».
Scenario: Первый вход
- WHEN приложение открыто впервые, токена нет
- THEN показан экран входа; после ввода верного токена открывается запрошенная страница, токен сохранён
Scenario: Устаревший токен
- WHEN сохранённый токен не подходит и запрос ответил 401
- THEN показан экран входа с пояснением, что токен не подошёл; панели доски и задач не видны
Scenario: Токен от прежней доски
- WHEN в
localStorageестьjira-hub-token, сохранённый страницейlinks.html - THEN приложение входит с ним без запроса токена
Scenario: Запоздавший 401 от прежнего токена
- WHEN запрос ушёл с прежним токеном, пользователь вошёл с новым, и после этого запрос ответил 401
- THEN приложение остаётся на странице, новый токен сохранён
Scenario: Хаб отверг проверку токена
- WHEN проверка
GET /settingsответила 400 сdetail - THEN экран входа показывает текст
detail, а не «хаб не отвечает»
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 быть значок «Открыть карточку»,
ведущий на /app/issues/{key}; ключ карточки по-прежнему открывает задачу в Jira. Действия «Сменить токен» в меню
доски нет — оно в верхней панели приложения. Ещё два отличия от links.html (R-10, R-18): двойной щелчок по уголку
карточки меряет высоту по содержимому при новой ширине, а не при прежней; указатель при перетаскивании и растяжке
захватывается на нажатом элементе, а не на карточке, поэтому двойной щелчок по уголку сбрасывает размер. Загрузка файла 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 доска показана с той же прокруткой
Requirement: Источник данных доски
Доска SHALL хранить явный источник данных — хаб или файл. Пока показан снимок из файла (CSV или JSON-снимок), ответы
хаба SHALL его не заменять; «Перечитать» SHALL возвращать источник «хаб» и загружать GET /board заново без обращения к
Jira. Импорт JSON в режиме хаба SHALL применять раскладку, дорожки и фильтры и перечитывать хаб. «Обновить из Jira»
SHALL проверять агента (GET /agent; офлайн — предупреждение, пакет всё равно ставится), ставить
POST /refresh {full: true, author: "viewer"}, опрашивать GET /batches/{id} каждые 3 с одним опросом до
терминального статуса пакета и затем перечитывать GET /board. Кнопка «Обновить из Jira» SHALL быть недоступна,
пока запрос /refresh в пути или пакет выполняется. Незавершённый пакет refresh с автором viewer SHALL
подхватываться при каждой успешной загрузке из хаба; пакеты viewer с другими командами не подхватываются. Опрос пакета SHALL прекращаться при ответе 404 и после 5 ошибок
«хаб не отвечает» подряд, с сообщением и снова доступной кнопкой. Шапка доски SHALL показывать
источник данных (данные хаба на время последнего полного прогона; «из файла») и одно текущее событие (загрузка; пакет
выполняется / выполнен / ошибка с текстом; хаб не отвечает — прежние данные, а при снимке из файла — «на доске снимок из файла»; агент офлайн — пакет поставлен; ошибка
разбора файла — «файл не разобран: <причина>»); новое событие заменяет предыдущее целиком. Пока пакет выполняется,
«Перечитать» и загрузка файла SHALL не стирать событие пакета.
Scenario: Поздний ответ хаба не затирает файл
- WHEN запрос
GET /boardв пути, и в это время загружен CSV - THEN после ответа хаба на доске по-прежнему данные CSV с пометкой «из файла»
Scenario: Нет второго полного прогона
- WHEN нажата «Обновить из Jira» и, пока пакет выполняется, доска перерисована (фильтр, «Перечитать»)
- THEN кнопка остаётся недоступной, в хабе один пакет, его опрашивает один опрос
Scenario: Пакет пропал
- WHEN опрос пакета получил 404
- THEN опрос остановлен, показано сообщение, кнопка «Обновить из Jira» снова доступна
Scenario: Пакет после «Перечитать»
- WHEN пакет
viewerпоставлен с другой вкладки и не завершён, нажато «Перечитать» - THEN доска показывает «пакет #N выполняется» и по завершении перечитывает данные
Scenario: «Перечитать» во время пакета
- WHEN пакет
viewerвыполняется и нажато «Перечитать» - THEN шапка по-прежнему показывает «пакет #N выполняется»
Scenario: Хаб недоступен при снимке из файла
- WHEN хаб не отвечает и загружен JSON-снимок доски, либо на доске снимок из CSV и хаб не ответил на перечитывание снимка после завершения пакета
- THEN шапка показывает «хаб не отвечает — на доске снимок из файла», а не «показаны прежние данные»
Scenario: Чужой пакет не подхватывается
- WHEN незавершённый пакет автора
viewerсостоит из командensure_fields - THEN доска его не опрашивает, кнопка «Обновить из Jira» доступна
Requirement: Список задач
Страница /app/issues SHALL показывать задачи зеркала из GET /issues таблицей: ключ (ссылка на карточку), тема,
тип, статус Jira, исполнитель, спринт, срок (planned_end, иначе duedate), эпик (имя эпика, иначе ключ), вид работы,
статус по факту, признак «сосед». Фильтры SHALL соответствовать параметрам GET /issues (спринт, исполнитель, вид
работы, статус по факту, статус Jira, метка, эпик, только соседи, поиск по ключу или теме), храниться в query-строке
адреса и применяться сервером; варианты выпадающих списков SHALL браться из снимка GET /board; значение фильтра из адреса, которого нет среди
вариантов, SHALL показываться в списке с пометкой «(нет на доске)». Задачи SHALL
загружаться порциями по 50 строк; «Показать ещё» SHALL быть доступна, пока последняя порция полная.
Scenario: Фильтр в адресе
- WHEN выбран спринт «AWPCA_Спринт 2» и исполнитель
- THEN адрес содержит
sprint=иassignee=, таблица — результатGET /issuesс этими параметрами; открытие этого адреса в новой вкладке показывает ту же выборку
Scenario: Конец списка
- WHEN вторая порция вернула 17 задач
- THEN в таблице 67 задач, «Показать ещё» не показана
Scenario: Фильтр по закрытому спринту
- WHEN открыт адрес
/app/issues?sprint=AWPCA_Спринт 0, а в снимке доски такого спринта нет - THEN выпадающий список «Спринт» показывает «AWPCA_Спринт 0 (нет на доске)» выбранным, таблица — по этому фильтру
Requirement: Карточка задачи
Страница /app/issues/{key} SHALL показывать задачу из GET /issues/{key}: поля зеркала, ссылку на задачу в Jira,
описание как текст с сохранёнными переносами строк (без разбора разметки Jira), исходящие и входящие связи с подписью
типа (логический тип из link_types, иначе имя типа Jira) и ссылками на карточки связанных задач, 10 последних команд
по задаче (id, пакет, тип, статус, время завершения, ошибка), а если команд не было — текст «команд по задаче не было». Если задачи нет в зеркале (404), страница SHALL сообщать
об этом со ссылкой на список.
Scenario: Связанная задача
- WHEN у AURORA-1620 исходящая связь
ASUPR_Блокер_Началана AURORA-1621 - THEN в карточке «блокирует начало → AURORA-1621», ключ ведёт на
/app/issues/AURORA-1621
Scenario: Задача без команд
- WHEN по AURORA-1620 не было команд
- THEN в разделе «Последние команды» — текст «команд по задаче не было», таблицы нет
Requirement: Сборка и разработка
Приложение SHALL быть npm-проектом в web/ (React, TypeScript, Vite) с точными версиями зависимостей и
package-lock.json. make web SHALL устанавливать зависимости (npm ci) и собирать приложение в
src/jira_hub/web/app/; каталог сборки не хранится в git. Шрифты и прочие ресурсы
страницы 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 страница не запрашивает ресурсов вне хаба, текст набран IBM Plex Sans