45 lines
2.0 KiB
TypeScript
45 lines
2.0 KiB
TypeScript
import type { MouseEvent } from "react";
|
||
import { Badge, Panel } from "../../../ui/index.ts";
|
||
import type { IssueRow } from "../../../api/types.ts";
|
||
import { isOverdue, todayISO } from "./card.ts";
|
||
import { IssueLink } from "./IssueLink.tsx";
|
||
import { dueOf } from "./options.ts";
|
||
|
||
// щелчок по строке открывает карточку, если это не ссылка/кнопка и не выделение текста
|
||
function rowClick(e: MouseEvent<HTMLTableRowElement>, key: string, onOpen: (key: string) => void) {
|
||
if ((e.target as HTMLElement).closest("a, button")) return;
|
||
if (window.getSelection()?.toString()) return;
|
||
onOpen(key);
|
||
}
|
||
|
||
export function IssueTable({ rows, epicNames, closed, onOpen }: {
|
||
rows: IssueRow[]; epicNames: Record<string, string>; closed: Set<string>; onOpen(key: string): void;
|
||
}) {
|
||
const today = todayISO();
|
||
return (
|
||
<Panel padding="none" className="issue-table-wrap">
|
||
<table className="issue-table">
|
||
<thead><tr><th>Ключ</th><th>Тема</th><th>Статус Jira</th><th>Исполнитель</th><th>Срок</th><th>Спринт</th><th>Эпик</th><th>Вид работы</th><th>Тип</th></tr></thead>
|
||
<tbody>
|
||
{rows.map((i) => {
|
||
const due = dueOf(i);
|
||
return (
|
||
<tr key={i.key} onClick={(e) => rowClick(e, i.key, onOpen)}>
|
||
<td className="issue-table__key"><IssueLink issueKey={i.key} />{i.neighbor && <Badge tone="neutral">сосед</Badge>}</td>
|
||
<td className="issue-table__sum">{i.summary}</td>
|
||
<td>{i.status && <Badge tone="info">{i.status}</Badge>}</td>
|
||
<td>{i.assignee}</td>
|
||
<td className={isOverdue(due, i.status, closed, today) ? "overdue" : undefined}>{due}</td>
|
||
<td>{i.sprint}</td>
|
||
<td>{i.epic_key ? epicNames[i.epic_key] ?? i.epic_key : ""}</td>
|
||
<td>{i.work_kind}</td>
|
||
<td>{i.issue_type}</td>
|
||
</tr>
|
||
);
|
||
})}
|
||
</tbody>
|
||
</table>
|
||
</Panel>
|
||
);
|
||
}
|