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)}
+
+
+ }
+ />
+ ))}
+
+
+ ))
+ )}
);
}