feat(KB-082): complete Step 5 — Display failure prominently in TaskDetailModal
This commit is contained in:
@@ -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 ? (
|
||||
|
||||
@@ -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" : ""}`}
|
||||
|
||||
@@ -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();
|
||||
});
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user