feat(KB-082): complete Step 5 — Display failure prominently in TaskDetailModal

This commit is contained in:
gsxdsm
2026-03-29 21:43:25 -07:00
parent 9dfb851e70
commit 7a4c5e803a
4 changed files with 51 additions and 58 deletions

View File

@@ -17,11 +17,11 @@ const COLUMN_COLOR_MAP: Record<Column, string> = {
const ACTIVE_STATUSES = new Set(["planning", "researching", "executing", "finalizing", "merging", "specifying"]);
type SortField = "id" | "title" | "status" | "column" | "createdAt" | "updatedAt";
type SortField = "id" | "title" | "status" | "column";
type SortDirection = "asc" | "desc";
// Column visibility types
const ALL_LIST_COLUMNS = ["id", "title", "status", "column", "createdAt", "updatedAt", "dependencies", "progress"] as const;
const ALL_LIST_COLUMNS = ["id", "title", "status", "column", "dependencies", "progress"] as const;
type ListColumn = typeof ALL_LIST_COLUMNS[number];
interface ListViewProps {
@@ -36,11 +36,6 @@ interface ListViewProps {
onNewTask?: () => void;
}
function formatDate(iso: string): string {
const date = new Date(iso);
return date.toLocaleDateString() + " " + date.toLocaleTimeString([], { hour: "2-digit", minute: "2-digit" });
}
function getStepProgress(steps: TaskStep[]): string {
if (steps.length === 0) return "-";
const done = steps.filter((s) => s.status === "done").length;
@@ -64,7 +59,7 @@ export function ListView({
onCancelCreate,
onCreateTask,
}: ListViewProps) {
const [sortField, setSortField] = useState<SortField>("createdAt");
const [sortField, setSortField] = useState<SortField>("id");
const [sortDirection, setSortDirection] = useState<SortDirection>("desc");
const [filter, setFilter] = useState("");
const [draggingTaskId, setDraggingTaskId] = useState<string | null>(null);
@@ -173,8 +168,6 @@ export function ListView({
title: "Title",
status: "Status",
column: "Column",
createdAt: "Created",
updatedAt: "Updated",
dependencies: "Dependencies",
progress: "Progress",
};
@@ -232,12 +225,6 @@ export function ListView({
case "column":
comparison = a.column.localeCompare(b.column);
break;
case "createdAt":
comparison = a.createdAt.localeCompare(b.createdAt);
break;
case "updatedAt":
comparison = a.updatedAt.localeCompare(b.updatedAt);
break;
}
return sortDirection === "asc" ? comparison : -comparison;
});
@@ -499,16 +486,6 @@ export function ListView({
Column {getSortIcon("column")}
</th>
)}
{visibleColumns.has("createdAt") && (
<th className="list-header-cell" onClick={() => handleSort("createdAt")}>
Created {getSortIcon("createdAt")}
</th>
)}
{visibleColumns.has("updatedAt") && (
<th className="list-header-cell" onClick={() => handleSort("updatedAt")}>
Updated {getSortIcon("updatedAt")}
</th>
)}
{visibleColumns.has("dependencies") && (
<th className="list-header-cell">Dependencies</th>
)}
@@ -608,12 +585,6 @@ export function ListView({
</span>
</td>
)}
{visibleColumns.has("createdAt") && (
<td className="list-cell list-cell-date">{formatDate(task.createdAt)}</td>
)}
{visibleColumns.has("updatedAt") && (
<td className="list-cell list-cell-date">{formatDate(task.updatedAt)}</td>
)}
{visibleColumns.has("dependencies") && (
<td className="list-cell list-cell-deps">
{task.dependencies && task.dependencies.length > 0 ? (

View File

@@ -407,6 +407,15 @@ export function TaskDetailModal({
Created {new Date(task.createdAt).toLocaleDateString()} · Updated{" "}
{new Date(task.updatedAt).toLocaleDateString()}
</div>
{task.status === "failed" && task.error && (
<div className="detail-error-alert">
<span className="detail-error-icon"></span>
<div className="detail-error-content">
<div className="detail-error-title">Task Failed</div>
<div className="detail-error-message">{task.error}</div>
</div>
</div>
)}
<div className="detail-tabs">
<button
className={`detail-tab${activeTab === "definition" ? " detail-tab-active" : ""}`}

View File

@@ -377,8 +377,8 @@ describe("ListView", () => {
renderListView({ tasks });
const depCells = screen.getAllByRole("cell");
// Find the cell that should contain deps (7th column)
const depCell = depCells[6];
// Find the cell that should contain deps (5th column, index 4)
const depCell = depCells[4];
expect(depCell.textContent).toBe("-");
});
@@ -537,28 +537,6 @@ describe("ListView", () => {
});
});
it("formats dates correctly", () => {
const tasks = [
createMockTask({
id: "KB-001",
createdAt: "2024-03-15T10:30:00Z",
updatedAt: "2024-03-16T14:45:00Z",
}),
];
renderListView({ tasks });
// Check that dates are formatted and displayed
const cells = screen.getAllByRole("cell");
// Created and Updated are columns 5 and 6 (0-indexed: 4 and 5)
const createdCell = cells[4];
const updatedCell = cells[5];
// Should contain formatted dates with time
expect(createdCell.textContent).toMatch(/\d{1,2}\/\d{1,2}\/\d{4}/);
expect(updatedCell.textContent).toMatch(/\d{1,2}\/\d{1,2}\/\d{4}/);
});
it("truncates long descriptions in title cell", () => {
const longDescription = "A".repeat(100);
const tasks = [createMockTask({ id: "KB-001", title: undefined, description: longDescription })];
@@ -871,8 +849,6 @@ describe("ListView Column Visibility", () => {
expect(screen.getByText("Title")).toBeDefined();
expect(screen.getByText("Status")).toBeDefined();
expect(screen.getByText("Column")).toBeDefined();
expect(screen.getByText("Created")).toBeDefined();
expect(screen.getByText("Updated")).toBeDefined();
expect(screen.getByText("Dependencies")).toBeDefined();
expect(screen.getByText("Progress")).toBeDefined();
});

View File

@@ -1104,6 +1104,43 @@ body {
margin-bottom: 16px;
}
/* Error alert in task detail modal */
.detail-error-alert {
display: flex;
align-items: flex-start;
gap: 12px;
margin: 12px 0 16px;
padding: 12px 14px;
background: rgba(218, 54, 51, 0.1);
border: 1px solid rgba(218, 54, 51, 0.3);
border-radius: var(--radius);
}
.detail-error-icon {
flex-shrink: 0;
font-size: 18px;
line-height: 1;
}
.detail-error-content {
flex: 1;
min-width: 0;
}
.detail-error-title {
font-size: 13px;
font-weight: 600;
color: #da3633;
margin-bottom: 4px;
}
.detail-error-message {
font-size: 12px;
color: var(--text-muted);
line-height: 1.5;
word-break: break-word;
}
.detail-section {
margin-top: 16px;
}