diff --git a/ui/src/App.tsx b/ui/src/App.tsx index 4eeb7200..7319f24a 100644 --- a/ui/src/App.tsx +++ b/ui/src/App.tsx @@ -42,6 +42,7 @@ export function App() { } /> } /> } /> + } /> } /> } /> } /> diff --git a/ui/src/pages/Issues.tsx b/ui/src/pages/Issues.tsx index 4ff8c82b..c9340466 100644 --- a/ui/src/pages/Issues.tsx +++ b/ui/src/pages/Issues.tsx @@ -27,17 +27,18 @@ function statusLabel(status: string): string { return status.replace(/_/g, " ").replace(/\b\w/g, (c) => c.toUpperCase()); } -type TabFilter = "all" | "active" | "backlog" | "done"; +type TabFilter = "all" | "active" | "backlog" | "done" | "recent"; const issueTabItems = [ { value: "all", label: "All Issues" }, { value: "active", label: "Active" }, { value: "backlog", label: "Backlog" }, { value: "done", label: "Done" }, + { value: "recent", label: "Recent" }, ] as const; function parseIssueTab(value: string | null): TabFilter { - if (value === "all" || value === "active" || value === "backlog" || value === "done") return value; + if (value === "all" || value === "active" || value === "backlog" || value === "done" || value === "recent") return value; return "active"; } @@ -113,6 +114,9 @@ export function Issues() { } const filtered = filterIssues(issues ?? [], tab); + const recentSorted = tab === "recent" + ? [...filtered].sort((a, b) => new Date(b.updatedAt).getTime() - new Date(a.updatedAt).getTime()) + : null; const grouped = groupBy(filtered, (i) => i.status); const orderedGroups = statusOrder .filter((s) => grouped[s]?.length) @@ -146,70 +150,118 @@ export function Issues() { /> )} - {orderedGroups.map(({ status, items }) => ( -
-
- - - {statusLabel(status)} - - {items.length} - -
-
- {items.map((issue) => ( - navigate(`/issues/${issue.id}`)} - leading={ - // eslint-disable-next-line jsx-a11y/click-events-have-key-events, jsx-a11y/no-static-element-interactions -
e.stopPropagation()}> - updateIssue.mutate({ id: issue.id, data: { priority: p } })} - /> - updateIssue.mutate({ id: issue.id, data: { status: s } })} - /> -
- } - trailing={ -
- {liveIssueIds.has(issue.id) && ( - - - - - - Live + {recentSorted ? ( +
+ {recentSorted.map((issue) => ( + navigate(`/issues/${issue.id}`)} + leading={ + // eslint-disable-next-line jsx-a11y/click-events-have-key-events, jsx-a11y/no-static-element-interactions +
e.stopPropagation()}> + updateIssue.mutate({ id: issue.id, data: { priority: p } })} + /> + updateIssue.mutate({ id: issue.id, data: { status: s } })} + /> +
+ } + trailing={ +
+ {liveIssueIds.has(issue.id) && ( + + + + - )} - {issue.assigneeAgentId && (() => { - const name = agentName(issue.assigneeAgentId); - return name - ? - : {issue.assigneeAgentId.slice(0, 8)}; - })()} - - {formatDate(issue.createdAt)} + Live -
- } - /> - ))} -
+ )} + {issue.assigneeAgentId && (() => { + const name = agentName(issue.assigneeAgentId); + return name + ? + : {issue.assigneeAgentId.slice(0, 8)}; + })()} + + {formatDate(issue.updatedAt)} + +
+ } + /> + ))}
- ))} + ) : ( + orderedGroups.map(({ status, items }) => ( +
+
+ + + {statusLabel(status)} + + {items.length} + +
+
+ {items.map((issue) => ( + navigate(`/issues/${issue.id}`)} + leading={ + // eslint-disable-next-line jsx-a11y/click-events-have-key-events, jsx-a11y/no-static-element-interactions +
e.stopPropagation()}> + updateIssue.mutate({ id: issue.id, data: { priority: p } })} + /> + updateIssue.mutate({ id: issue.id, data: { status: s } })} + /> +
+ } + trailing={ +
+ {liveIssueIds.has(issue.id) && ( + + + + + + Live + + )} + {issue.assigneeAgentId && (() => { + const name = agentName(issue.assigneeAgentId); + return name + ? + : {issue.assigneeAgentId.slice(0, 8)}; + })()} + + {formatDate(issue.createdAt)} + +
+ } + /> + ))} +
+
+ )) + )}
); }