diff --git a/frontend/packages/swarm-ui/src/App.tsx b/frontend/packages/swarm-ui/src/App.tsx index 854f5169..65d47afe 100644 --- a/frontend/packages/swarm-ui/src/App.tsx +++ b/frontend/packages/swarm-ui/src/App.tsx @@ -6,7 +6,6 @@ import { AgentsPage } from './pages/AgentsPage.js'; import { ComponentsPage } from './pages/ComponentsPage.js'; import { JobsPage } from './pages/JobsPage.js'; import { HivesPage } from './pages/HivesPage.js'; -import { IssueReportPage } from './pages/IssueReportPage.js'; import { Panel } from './ui/panel/Panel.js'; function NotFound() { @@ -24,7 +23,6 @@ export function App() { - diff --git a/frontend/packages/swarm-ui/src/pages/IssueReportPage.css b/frontend/packages/swarm-ui/src/pages/IssueReportPage.css deleted file mode 100644 index 1419727d..00000000 --- a/frontend/packages/swarm-ui/src/pages/IssueReportPage.css +++ /dev/null @@ -1,43 +0,0 @@ -/* — the repo-picker + hide-blocked toggle sit in one - row (mirrors CreateAgentForm's row/wrap pattern: flex-wrap so narrow - viewports stack instead of overflowing), the label chips get their - own row below since the set is open-ended and shouldn't fight the - controls row for width. Sort buttons live inside the table's own - `` cells (Table.tsx's `header` now accepts real markup for - exactly this) so they inherit the table's header styling for free — - only the pointer cursor + no-underline reset is this page's to own. */ -.issue-report-controls { - display: flex; - flex-wrap: wrap; - align-items: flex-end; - gap: 1.25em; - margin-bottom: 1em; -} -.issue-report-toggle { - display: flex; - align-items: center; - gap: 0.4em; - cursor: pointer; -} -.issue-report-labels { - display: flex; - flex-wrap: wrap; - gap: 0.6em; - margin-bottom: 1em; -} -.issue-report-label-chip { - display: flex; - align-items: center; - gap: 0.35em; - cursor: pointer; - color: var(--muted); -} -.issue-report-sort-btn { - background: none; - border: none; - padding: 0; - margin: 0; - font: inherit; - color: inherit; - cursor: pointer; -} diff --git a/frontend/packages/swarm-ui/src/pages/IssueReportPage.tsx b/frontend/packages/swarm-ui/src/pages/IssueReportPage.tsx deleted file mode 100644 index dc7a352c..00000000 --- a/frontend/packages/swarm-ui/src/pages/IssueReportPage.tsx +++ /dev/null @@ -1,318 +0,0 @@ -// — mara's custom issue report: browse open issues -// across every repo that has one, sortable/filterable her own way, with -// "hide blocked (open dependency)" as the headline filter and a rank -// column for how many other open issues depend on each one. Backed by -// swarm-controller's `GET /api/repos` (dropdown source, only repos with -// an open issue), `GET /api/issue-report` (default — every repo -// combined) and `GET /api/repos/{org}/{repo}/issue-report` (once a -// specific repo is picked) — `blocked` and `depended_on_by_count` both -// arrive pre-resolved per row, so nothing here does its own -// dependency-graph walk. -// -// Sorting and the label/hide-blocked filters are client-side over -// whatever's currently loaded — only the repo selection re-fetches. -// Default sort is `depended_on_by_count` descending: mara's own framing -// ("i would also like to rank them by how many issues depend on them") -// reads as the report's headline ordering, not just one more column. -// -// No `RefreshIntervalPicker`/auto-poll like the roster pages — this -// reads as a "generate a report" action rather than a live status view -// (mara: a "generating report" spinner sub-5s is fine, doesn't need to -// be instant), so it fetches once per repo-selection change instead of -// polling. -import { useEffect, useMemo, useState } from 'preact/hooks'; -import { ApiErrorPanel } from '@hive/shared/api-error-panel.js'; -import { readApiError, type ProblemDetails } from '@hive/shared/api-error.js'; -import { Badge } from '@hive/shared/badge.js'; -import { Panel } from '../ui/panel/Panel.js'; -import { SelectField } from '../ui/select-field/SelectField.js'; -import { Table, type TableColumn } from '../ui/table/Table.js'; -import './IssueReportPage.css'; - -interface IssueReportRow { - repo: string; - number: number; - title: string; - labels: string[]; - // `string[]`, not a single `assignee` — forge's own `assignee` field is - // the legacy single-value one; `assignees` is the real multi-assignee - // list, and this repo actually uses multiple (caught against an - // earlier draft of this row shape that had it singular). - assignees: string[]; - html_url: string | null; - blocked: boolean; - depended_on_by_count: number; -} - -type SortKey = 'repo' | 'number' | 'title' | 'assignees' | 'blocked' | 'depended_on_by_count'; -type SortDir = 'asc' | 'desc'; - -// Sentinel ` setHideBlocked((e.target as HTMLInputElement).checked)} - /> - hide blocked (open dependency) - - - {allLabels.length ? ( -
- {allLabels.map((l) => ( - - ))} -
- ) : null} - {error ? : null} - {!error && loading ?

generating report…

: null} - {!loading && rows ? ( - `${r.repo}#${r.number}`} - emptyMessage="no issues match the current filters" - /> - ) : null} - - ); -} diff --git a/frontend/packages/swarm-ui/src/shell/Shell.tsx b/frontend/packages/swarm-ui/src/shell/Shell.tsx index aeef0055..8eb9d6cd 100644 --- a/frontend/packages/swarm-ui/src/shell/Shell.tsx +++ b/frontend/packages/swarm-ui/src/shell/Shell.tsx @@ -48,7 +48,6 @@ const NAV_ITEMS: { href: string; label: string; accent: string }[] = [ { href: '/', label: 'hives', accent: 'var(--purple)' }, { href: '/agents', label: 'agents', accent: 'var(--green)' }, { href: '/jobs', label: 'jobs', accent: 'var(--pink)' }, - { href: '/issues', label: 'issues', accent: 'var(--yellow)' }, { href: '/components', label: 'components', accent: 'var(--blue)' }, ]; diff --git a/frontend/packages/swarm-ui/src/ui/table/Table.tsx b/frontend/packages/swarm-ui/src/ui/table/Table.tsx index 8c14b423..23c1739a 100644 --- a/frontend/packages/swarm-ui/src/ui/table/Table.tsx +++ b/frontend/packages/swarm-ui/src/ui/table/Table.tsx @@ -14,23 +14,8 @@ import './Table.css'; export interface TableColumn { key: string; - // `ComponentChildren`, not `string` — a plain string still satisfies - // this and renders identically, but a caller that wants a clickable - // sortable header (the issue-report page's own column-header buttons) - // can pass real markup instead of forking a second table primitive. - header: ComponentChildren; + header: string; render: (row: T) => ComponentChildren; - /** - * ARIA sort state for this column's ` {columns.map((c) => ( - + ))}
` — `'ascending'` / - * `'descending'` while this is the active sort column, `'none'` while - * sortable but not active, omitted entirely for a non-sortable column - * (no `aria-sort` attribute at all, the correct value for a column - * that can never be the active sort). A screen reader announces which - * column/direction is active from this attribute; the ▲/▼ glyph a - * sortable header renders is `aria-hidden` and carries no information - * on its own. - */ - ariaSort?: 'ascending' | 'descending' | 'none'; } export function Table({ @@ -55,9 +40,7 @@ export function Table({
- {c.header} - {c.header}