diff --git a/web/src/issues/IssueCard.test.tsx b/web/src/issues/IssueCard.test.tsx
index f80c656..5718ab2 100644
--- a/web/src/issues/IssueCard.test.tsx
+++ b/web/src/issues/IssueCard.test.tsx
@@ -46,12 +46,12 @@ describe("карточка задачи", () => {
expect(await screen.findByRole("heading", { level: 2, name: "Тема AURORA-9001" })).toBeInTheDocument();
const head = screen.getByTestId("issue-head");
expect(within(head).getByText("AURORA-9001")).toBeInTheDocument();
- expect(within(head).getByText("В работе", { selector: ".badge.status" })).toBeInTheDocument();
+ expect(within(head).getByText("В работе", { selector: ".ui-badge" })).toBeInTheDocument();
expect(head.querySelector(".badge.fact")).toBeNull(); // статуса по факту в карточке нет (hide-closed)
expect(within(head).queryByText(/факт:/)).toBeNull();
expect(within(head).getByTestId("issue-facts")).toHaveTextContent("Иванов Иван Иванович · срок 10.01 просрочено · AWPCA_Спринт 2 · эпик: Тестовый эпик");
- expect(within(head).getByText("просрочено")).toHaveClass("overdue");
- expect(screen.getByRole("link", { name: "Открыть в Jira ↗" })).toHaveAttribute("href", "https://sdlc.go.rshbank.ru/jira/browse/AURORA-9001");
+ expect(within(head).getByText("просрочено")).toHaveClass("ui-badge--error");
+ expect(screen.getByRole("link", { name: "Открыть в Jira" })).toHaveAttribute("href", "https://sdlc.go.rshbank.ru/jira/browse/AURORA-9001");
});
it("связи по группам с темой и статусом; задача вне зеркала; прочий тип с направлением", async () => {
diff --git a/web/src/issues/IssueCard.tsx b/web/src/issues/IssueCard.tsx
index 14757b7..40c8942 100644
--- a/web/src/issues/IssueCard.tsx
+++ b/web/src/issues/IssueCard.tsx
@@ -8,6 +8,7 @@ import { fmtDate, fmtStamp } from "../board/model/util.ts";
import { groupLinks, isOverdue, todayISO } from "./card.ts";
import { useCommentsHidden } from "./commentsPref.ts";
import { IssueLink } from "./IssueLink.tsx";
+import { Badge, Button, IconButton, IconClose, IconExternal, Notice, Panel } from "../ui/index.ts";
import { Notes } from "./Notes.tsx";
import { dueOf } from "./options.ts";
import { WikiText } from "./wiki/WikiText.tsx";
@@ -19,17 +20,23 @@ export function IssueCard({ issueKey: key, onClose }: { issueKey: string; onClos
const issue = useIssue(key);
const settings = useSettings();
const closed = useClosedStatuses();
- const close = ;
- if (issue.isPending) return
;
+ const close = } onClick={onClose} />;
+ if (issue.isPending) {
+ return {key}{close}
загрузка {key}…
;
+ }
if (issue.error instanceof ApiError && issue.error.status === 404) {
- return Задачи {key} нет в зеркале хаба.
;
+ return (
+
+ Закрыть}>Задачи {key} нет в зеркале хаба.
+
+ );
}
if (issue.isError && !issue.data) {
return (
-
-
{issue.error.message}{" "}
-
-
+
+ >}>
+ {issue.error.message}
+
);
}
@@ -37,7 +44,7 @@ export function IssueCard({ issueKey: key, onClose }: { issueKey: string; onClos
const due = dueOf(i);
const facts = [
i.assignee &&
{i.assignee},
- due &&
срок {fmtDate(due)}{isOverdue(due, i.status, closed, todayISO()) && <> просрочено>},
+ due &&
срок {fmtDate(due)}{isOverdue(due, i.status, closed, todayISO()) && <> просрочено>},
i.sprint &&
{i.sprint},
i.epic_key &&
эпик: {i.epic_summary ?? i.epic_key},
].filter(Boolean);
@@ -46,16 +53,15 @@ export function IssueCard({ issueKey: key, onClose }: { issueKey: string; onClos
{i.summary || i.key}
{facts.length > 0 && {facts.flatMap((f, n) => (n ? [" · ", f] : [f]))}
}
{issue.isError && (
- {issue.error.message}{" "}
-
+ void issue.refetch()}>Повторить}>{issue.error.message}
)}
@@ -64,11 +70,11 @@ export function IssueCard({ issueKey: key, onClose }: { issueKey: string; onClos
{i.description && }
-
+
);
@@ -90,7 +96,7 @@ function Links({ issue, linkTypes }: { issue: IssueDetail; linkTypes: Record{" "}
{l.summary === null && l.status === null
? нет в зеркале
- : <>{l.summary}{l.status && <>{" "}{l.status}>}>}
+ : <>{l.summary}{l.status && <>{" "}{l.status}>}>}
))}
@@ -102,7 +108,7 @@ function Links({ issue, linkTypes }: { issue: IssueDetail; linkTypes: Record void }) {
- return ;
+ return ;
}
function Description({ text, projectKey }: { text: string; projectKey?: string }) {
@@ -134,7 +140,7 @@ function Description({ text, projectKey }: { text: string; projectKey?: string }
{source
? } className={cls}>{text}
: } className={cls}>
}
- {(long || open) && }
+ {(long || open) && }
);
}
@@ -143,8 +149,8 @@ function Comment({ c, projectKey }: { c: IssueComment; projectKey?: string }) {
const [source, setSource] = useState(false);
return (
-
-
{c.author ?? "—"}{c.created ? `, ${fmtStamp(c.created)}` : ""}
+
+ {c.author ?? "—"}{c.created ? `, ${fmtStamp(c.created)}` : ""}
setSource(!source)} />
{source
@@ -160,7 +166,7 @@ function Comments({ items, projectKey }: { items: IssueComment[]; projectKey?: s
Комментарии ({items.length})
- {items.length > 0 && }
+ {items.length > 0 && }
{!items.length && комментариев нет
}
{items.length > 0 && !hidden && (
@@ -194,13 +200,13 @@ function Commands({ issue: i }: { issue: IssueDetail }) {
Команды моста ({i.commands.length})
-
+
| Команда | Пакет | Тип | Статус | Завершена | Ошибка |
{i.commands.map((c) => (
| #{c.id} | #{c.batch_id} | {c.type} | {c.status} |
{c.finished_at ? fmtStamp(c.finished_at) : ""} | {c.error} |
))}
-
+
);
diff --git a/web/src/issues/IssueModal.tsx b/web/src/issues/IssueModal.tsx
index a4f7134..f7ac8b6 100644
--- a/web/src/issues/IssueModal.tsx
+++ b/web/src/issues/IssueModal.tsx
@@ -1,27 +1,25 @@
import { useEffect, useRef } from "react";
+import { Dialog } from "../ui/index.ts";
import { ErrorBoundary } from "../shell/ErrorBoundary.tsx";
import { IssueCard } from "./IssueCard.tsx";
-// имя окна — заголовок карточки; пока его нет (загрузка, 404, ошибка), запасное имя — ключ задачи
+// имя окна — ключ задачи; заголовок карточки (h2) подписывает окно через aria-labelledby
// окно карточки (design 13):