## MODIFIED Requirements ### Requirement: Справочники из настроек хаба Приложение SHALL брать перевод меток видов работ (`kind_labels`) и типов связей Jira (`link_types`) из `GET /settings` и набор закрытых статусов Jira (`closed_statuses`) из `GET /settings` и не содержать собственных копий этих словарей. Если нужного словаря в настройках нет, страница SHALL сообщать, какого словаря не хватает, вместо пустой доски или списка без подписей; исключение — `closed_statuses`: без него ни одна задача не считается закрытой, сообщения нет. #### Scenario: Новый тип связи в настройках - **WHEN** в `link_types` настроек добавлен тип и в зеркале есть связь этого типа - **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 быть значок «Открыть карточку», открывающий `/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 — в полную яркость; критический путь и ящик «Проблемы» те же, что без приглушения ### Requirement: Список задач Страница `/app/issues` SHALL показывать задачи зеркала из `GET /issues` таблицей: ключ (открывает карточку задачи окном поверх списка), тема, тип, статус Jira, исполнитель, спринт, срок (`planned_end`, иначе `duedate`), эпик (имя эпика, иначе ключ), вид работы, признак «сосед»; статуса по факту в списке нет. Фильтры SHALL соответствовать параметрам `GET /issues` (спринт, исполнитель, вид работы, статус Jira, метка, эпик, только соседи, поиск по ключу или теме, скрыть закрытые), храниться в query-строке адреса и применяться сервером; варианты выпадающих списков SHALL браться из снимка `GET /board`; значение фильтра из адреса, которого нет среди вариантов, SHALL показываться в списке с пометкой «(нет на доске)». Флажок «скрыть закрытые» SHALL быть включён по умолчанию (в адресе ничего), снятый — `closed=1` в адресе; включённый — запрос с `hide_closed=1`, кроме случая, когда в фильтре «Статус Jira» выбран статус из `closed_statuses`. Параметр `fact_status` в адресе игнорируется. Задачи 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 (нет на доске)» выбранным, таблица — по этому фильтру #### Scenario: Карточка поверх списка - **WHEN** список отфильтрован по спринту, прокручен и догружен «Показать ещё», открыта карточка задачи и окно закрыто - **THEN** адрес списка с тем же фильтром, в таблице те же строки, прокрутка та же #### Scenario: Закрытые скрыты по умолчанию - **WHEN** открыт `/app/issues`, в зеркале задачи в статусах «В работе», «Готово», «Снято» - **THEN** флажок «скрыть закрытые» включён, запрос `GET /issues` с `hide_closed=1`, в таблице только «В работе»; после снятия флажка адрес содержит `closed=1`, в таблице все три #### Scenario: Выбран закрытый статус - **WHEN** флажок «скрыть закрытые» включён и в «Статус Jira» выбрано «Готово» - **THEN** запрос `GET /issues?status=Готово` без `hide_closed`, в таблице задачи в статусе «Готово» ### Requirement: Карточка задачи Адрес `/app/issues/{key}` SHALL открывать карточку задачи из `GET /issues/{key}` модальным окном поверх страницы, с которой её открыли (список «Задачи» или доска); страница под окном SHALL оставаться смонтированной и сохранять фильтры, загруженные строки, прокрутку и раскладку. Перезагрузка страницы с открытым окном SHALL сохранять страницу под окном. Открытый напрямую адрес (новая вкладка, закладка) SHALL показывать окно поверх списка задач без фильтров. Окно SHALL закрываться кнопкой ✕, клавишей Esc и щелчком по фону (нажатие и отпускание — на фоне; выделение текста, отпущенное за краем окна, окно не закрывает) и возвращать на страницу, с которой его открыли, даже после переходов по связям внутри окна; «назад» браузера после закрытия MUST NOT открывать карточку снова. Переход по связанной задаче SHALL менять содержимое окна и адрес, «назад» браузера — возвращать к прежней карточке. Пока окно открыто, фокус SHALL оставаться в окне, страница под ним не прокручивается. Содержимое окна: - шапка: ключ, статус Jira, «Открыть в Jira», ✕, тема; строка «исполнитель · срок · спринт · эпик» без пустых частей, срок — `planned_end`, иначе `duedate`, с пометкой «просрочено», если он раньше сегодняшней даты и статус Jira не входит в `closed_statuses`; эпик — темой (`epic_summary`), иначе ключом; - «Связи» по группам в порядке: «Зависит от» (входящие связи логических типов `блокирует начало` и `блокирует завершение` из `link_types`, с меткой «начало» / «завершение»), «Блокирует» (исходящие тех же типов, с теми же метками), «Связана с» (тип `связанные` в обе стороны), «Прочие» (тип Jira вне `link_types`: имя типа и направление «→» / «←»); в строке — ключ (открывает связанную задачу в окне), тема и статус Jira, а если задачи нет в зеркале — ключ и «нет в зеркале»; пустые группы не показываются, без связей — «связей нет»; - «Описание» — текст, отрендеренный по требованию «Разметка Jira в карточке», с кнопкой «исходник» / «текст» в заголовке раздела; длиннее 8 строк — свёрнуто с кнопкой «развернуть» / «свернуть» в обоих режимах; без описания раздела нет; - «Комментарии (N)» — новые сверху, автор, время, кнопка «исходник» / «текст» у каждого комментария, текст, отрендеренный по требованию «Разметка Jira в карточке»; кнопка «Скрыть» / «Показать» сворачивает раздел, выбор SHALL запоминаться в браузере для всех карточек; без комментариев — «комментариев нет»; - «Служебные заметки» (требование «Служебные заметки»); - «Прочие поля» — непустые из: тип, приоритет, автор, метки, плановое начало, обновлена в Jira, сосед по связям; - «Команды моста (N)» — 10 последних команд по задаче (id, пакет, тип, статус, время завершения, ошибка), раздел свёрнут при открытии; если команд не было — текст «команд по задаче не было». На ширине от 900 px «Связи», «Описание», «Комментарии» — в левой колонке, заметки, прочие поля и команды — в правой; уже — одна колонка: заметки, связи, описание, комментарии, прочие поля, команды. Если задачи нет в зеркале (404), окно SHALL сообщать об этом с кнопкой «Закрыть»; хаб не отвечает — сообщение и «Повторить», ранее загруженные данные остаются. #### Scenario: Связанная задача - **WHEN** у AURORA-1620 исходящая связь `ASUPR_Блокер_Начала` на AURORA-1621 «Расчёт свода» в статусе «В работе» - **THEN** в карточке в группе «Блокирует» строка «начало · AURORA-1621 · Расчёт свода · В работе»; щелчок по ключу меняет содержимое окна на AURORA-1621, адрес — `/app/issues/AURORA-1621`; «назад» браузера возвращает AURORA-1620 #### Scenario: Входящий блокер - **WHEN** у AURORA-1620 входящая связь `ASUPR_Блокер_Завершения` от AURORA-1500 - **THEN** строка с AURORA-1500 и меткой «завершение» — в группе «Зависит от» #### Scenario: Задача без команд - **WHEN** по AURORA-1620 не было команд - **THEN** в разделе «Команды моста» — текст «команд по задаче не было», таблицы нет #### Scenario: Закрытие после переходов - **WHEN** со списка «Задачи» открыта AURORA-1620, в окне — переход по связи на AURORA-1621, нажата Esc - **THEN** окно закрыто, адрес — адрес списка с прежними фильтрами; «назад» браузера не открывает карточку #### Scenario: Прямой адрес - **WHEN** в новой вкладке открыт `/app/issues/AURORA-1620`, затем нажат ✕ - **THEN** под окном был список задач без фильтров; после закрытия адрес `/app/issues`, «назад» не открывает карточку #### Scenario: Скрытые комментарии - **WHEN** в карточке нажато «Скрыть» у комментариев, затем страница перезагружена и открыта другая карточка - **THEN** комментарии в ней скрыты, видна кнопка «Показать» и число комментариев #### Scenario: Просрочка закрытой задачи - **WHEN** у AURORA-1620 срок 2026-10-01, сегодня 2026-10-17, статус Jira «Готово» - **THEN** в строке под темой «срок 01.10» без пометки «просрочено»; при статусе «В работе» — с пометкой ### Requirement: Служебные заметки Карточка задачи SHALL показывать служебные заметки в режиме чтения: «Вид работы», «Сделано», «Осталось», «Заметка владельца» (пустые — «—») и время и автор последнего изменения; кнопка «Править» SHALL открывать форму: «вид работы» — выбор из значений `kind_labels` настроек (и текущего значения, если его там нет, и пустого), «сделано», «осталось», «заметка владельца» — многострочный текст; «Отмена» SHALL возвращать режим чтения, при несохранённых правках — после подтверждения. Сохранение SHALL отправлять `PUT /issues/{key}/note` только с изменёнными полями и `updated_by: "web"`; очищенное поле отправляется как `null`. После успеха SHALL показываться режим чтения с сохранённой заметкой; список задач под окном SHALL показывать новый «вид работы» без «Перечитать», доска под окном SHALL перечитать снимок (`GET /board`) без потери раскладки и прокрутки; при 422 — текст ошибки у формы без потери введённого. Закрытие окна, переход по связи и уход в другой раздел с несохранёнными правками SHALL требовать подтверждения. Статуса по факту в заметках приложения нет: форма его не показывает и не отправляет, сохранённое в хабе значение не меняется. Заметки пишутся только в хаб, в Jira приложение ничего не отправляет. #### Scenario: Только изменённые поля - **WHEN** в заметке нажато «Править», изменено только «осталось» и нажато «Сохранить» - **THEN** тело `PUT /issues/{key}/note` — `{rest_text: "…", updated_by: "web"}`; карточка снова в режиме чтения с новым «Осталось» #### Scenario: Уход с несохранёнными правками - **WHEN** в форме заметки есть несохранённая правка и нажата Esc - **THEN** приложение спрашивает подтверждение; при отказе окно открыто, форма с введённым текстом #### Scenario: Заметка видна на доске - **WHEN** карточка AURORA-9002 открыта с доски, вид работы изменён на «тестирование» и сохранён - **THEN** доска под окном остаётся на месте и перезапрашивается (`GET /board` отдаёт `work_kind` «тестирование»); окно закрывается без потери раскладки и прокрутки доски