feat(card-attachments): ссылка на карточку task-tracker'а из панели#246
Merged
Conversation
…рикреплённой карточки
Панель прикреплённой карточки теперь показывает title как кликабельную
ссылку в task-tracker'е — открывается в новой вкладке.
Решения:
- URL — производное поле в CardAttachmentDtoMapper, не персистится в
CardSnapshot (ADR-0052 non-authoritative).
- Построение URL — задача провайдера: новый метод BuildCardWebUrl на
ITaskTrackerConnectionProvider, generic core вызывает и получает string?.
- Kaiten: короткая форма `{baseUrl}/{cardId}` (trailing slash нормализуется);
Kaiten сам редиректит на человекочитаемый URL с prefix/space.
- Батчинг: ToDtoListAsync читает connection один раз на distinct tracker,
чтобы list-endpoint остался O(providers), а не O(attachments).
Отброшено:
- Полный URL `/space/{s}/boards/card/{prefix}-{n}` — потребовал бы board
metadata (space_id, prefix), которых нет в CardSnapshot и KaitenCard, и
ломал бы провайдер-нейтральность.
- Хранить web_url в снапшоте — противоречит ADR-0052 (write-through нет,
URL меняется вместе с baseUrl коннекта).
Ограничения:
- Только Kaiten; для других провайдеров BuildCardWebUrl вернёт null и UI
покажет title без ссылки.
- Ссылка добавлена только в CardAttachmentRow; другие места UI
(board-card-browser) — вне scope.
Решения:
- Расширили TaskTrackerCardDto полем web_url (nullable) — тот же
контракт, что и у CardAttachmentDto: провайдер-нейтральное поле,
которое собирает конкретный tracker-провайдер из base_url
сохранённого коннекта. Non-authoritative, никогда не персистится
вместе с проекцией (ADR-0052).
- TaskTrackerCardDtoMapper переведён из static в DI'd sealed class
по образцу CardAttachmentDtoMapper: async, один lookup коннекта на
весь response (карточки принадлежат одному tracker), формирует
web_url через ITaskTrackerConnectionProvider.BuildCardWebUrl.
- BoardCardDetail: title карточки оборачивается в
<a target="_blank" rel="noopener noreferrer">, когда web_url задан;
список остаётся кликабельной кнопкой без вложенной ссылки, чтобы
не ломать выбор карточки.
Отброшено:
- Возвращать web_url из BoardCardBrowserService — это утечка wire-
представления в application-слой; кэш коннекта в mapper решает то
же на уровне API.
- Добавить ссылку в BoardCardRow (список): row — это <button> для
выбора; nested <a> внутри интерактивной строки конфликтует с
клавиатурной навигацией и жестами.
Ограничения:
- Формат URL — короткий (base_url/card_id). Kaiten редиректит на
человекочитаемый /space/{s}/boards/card/{prefix}-{n}, но prefix
нам недоступен без обхода OpenAPI Kaiten.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Цель
В панели прикреплённой карточки (
CardAttachmentRow) сейчас нет способа открыть саму карточку в task-tracker'е — только текстовый title, координата и меню действий. Даём кликабельную ссылку, открывающуюся в новой вкладке.Что меняется
Backend
ITaskTrackerConnectionProvider.BuildCardWebUrl(descriptor, cardId)— новый метод в generic core: провайдер решает, как строить URL, и возвращаетnull, если не умеет.KaitenTaskTrackerProvider: короткая форма{baseUrl}/{cardId}с нормализацией trailing slash. Kaiten сам редиректит на человекочитаемый/space/{s}/boards/card/{prefix}-{n}— так мы не тянем board metadata (space_id, prefix) вCardSnapshot.CardAttachmentDto.web_url: string | nullв openapi + регенерирован backend/frontend codegen.CardAttachmentDtoMapperперевёден с static на sealed class с DI (ITaskTrackerProviderRegistry,ITaskTrackerConnectionStore).ToDtoListAsyncбатчит чтение connections по distinct tracker, чтобы list-endpoint остался O(providers).web_url— производное поле в момент маппинга, не персистится вCardSnapshot(ADR-0052 non-authoritative).KaitenTaskTrackerFailureMap— выделен status→health / board-read failure маппинг изKaitenTaskTrackerProvider, чтобы файл провайдера остался в пределах maintainability budget (≤200 строк).Frontend
CardAttachmentRow.tsx: title —<a target="_blank" rel="noopener noreferrer">когдаweb_url != null, иначе как было (<span>).Явно ВНЕ scope
web_url=null, UI покажет текст).Как проверить
bash scripts/quality/verify.sh— все 19 гейтов PASS локально.{baseUrl}/{cardId}(короткая форма → Kaiten редиректит на человекочитаемую).web_url = null, title рендерится текстом без ссылки.KaitenTaskTrackerProviderCardTests.BuildCardWebUrl_*— 7 кейсов (trailing slash, пустой baseUrl/cardId, non-numeric id).IntentCardAttachmentsControllerTests.Attach_happy_pathпроверяетweb_urlв отдаваемом DTO;List_web_url_null_when_connection_missing— деградация доnullбез коннекта.CardAttachmentsList.test.tsx— рендерит title как<a>приweb_url, как<span>приnull.