Skip to content

Commit

Merge branch 'rm-boards'

syntaqxcommitted Parents9dc5a099b935a8Browse files
6 files+9−530/6 viewed
+1−17
1−import { Activity, BarChart3, Building2, MessagesSquare, Bell, BookMarked, BookOpen, Bookmark, Blocks, Bot, Box, Check, ChevronDown, ChevronLeft, ChevronRight, CircleDot, GripVertical, CircleUserRound, Code2, Coins, Compass, CreditCard, Fingerprint, GanttChart, Gauge, GitBranch, GitPullRequest, Globe, History, KanbanSquare, Keyboard, KeyRound, Layers, LayoutDashboard, LayoutGrid, LifeBuoy, ListTree, Lock, LogOut, Mail, MonitorCog, MonitorDown, Network, Package, PlayCircle, Plug, Plus, Rocket, Search, ServerCog, Settings, Shapes, Shield, ShieldCheck, Scale, Smile, Sparkles, SlidersHorizontal, House, Ticket, TrendingUp, UserRoundKey, Users, UsersRound, Webhook, X, ArrowLeftRight } from "lucide-react";
1+import { Activity, BarChart3, Building2, MessagesSquare, Bell, BookMarked, BookOpen, Bookmark, Blocks, Bot, Box, Check, ChevronDown, ChevronLeft, ChevronRight, CircleDot, GripVertical, CircleUserRound, Code2, Coins, Compass, CreditCard, Fingerprint, Gauge, GitBranch, GitPullRequest, Globe, History, Keyboard, KeyRound, Layers, LayoutDashboard, LayoutGrid, LifeBuoy, ListTree, Lock, LogOut, Mail, MonitorCog, MonitorDown, Network, Package, PlayCircle, Plug, Plus, Rocket, Search, ServerCog, Settings, Shapes, Shield, ShieldCheck, Scale, Smile, Sparkles, SlidersHorizontal, House, Ticket, TrendingUp, UserRoundKey, Users, UsersRound, Webhook, X, ArrowLeftRight } from "lucide-react";
22 import { Fragment, type ReactNode, useCallback, useEffect, useMemo, useRef, useState } from "react";
33 import { Link, useFetcher, useLocation, useNavigation, useRouteLoaderData, useSubmit } from "react-router";
44
263263 </SidebarMenuItem>
264264 );
265265 }
266−
267−/** Icons for what spans projects. */
268−const WORKSPACE_ICONS: Record<string, ReactNode> = {
269− board: <KanbanSquare size={15} />,
270− roadmap: <GanttChart size={15} />,
271− teams: <UsersRound size={15} />,
272− packages: <Package size={15} />,
273− fleet: <Bot size={15} />,
274−};
275266
276267 /** The Soon pages of a section, as paths, so its link is current on them. */
277268 function soonPaths(base: string, section: RoadmapItem["section"]): string[] {
12121203 {INSIGHTS.title}
12131204 </SidebarSoonLink>
12141205 )}
1215− {roadmapIn("Workspace")
1216− .filter((item) => item.key !== "insights" && item.key !== "teams" && item.key !== "fleet")
1217− .map((item) => (
1218− <SidebarSoonLink key={item.key} to={`/${slug}/-/soon/${item.key}`} icon={WORKSPACE_ICONS[item.key] ?? <Sparkles size={15} />} about={item.summary}>
1219− {item.title === "Board" ? "Boards" : item.title}
1220− </SidebarSoonLink>
1221− ))}
12221206 </NavRows>
12231207 {shared.length > 0 && (
12241208 <>
+1−1
129129 assert.equal(projectPageAt("settings/branches"), "settings");
130130 assert.equal(projectPageAt("soon/uptime", { uptime: "observability" }), "observability");
131131 assert.equal(projectPageAt("observability/logs"), "observability");
132− assert.equal(projectPageAt("soon/boards", { boards: "issues" }), "issues");
132+ assert.equal(projectPageAt("soon/playbooks", { playbooks: "agents" }), "agents");
133133 assert.equal(projectPageAt("pulse"), null);
134134 });
+2−30
1313 title: string;
1414 /**
1515 * Where it lives: a project page whose tabs it joins, or the workspace,
16− * for what spans projects (boards, the roadmap, packages, the fleet).
16+ * for what spans projects (Insights).
1717 */
1818 section: "Code" | "Issues" | "Agents" | "Deployments" | "Observability" | "Security" | "Insights" | "Workspace";
1919 /** One line, for the menu's tooltip and the page's lead. */
2727 };
2828
2929 export const ROADMAP: RoadmapItem[] = [
30− // --- Work ---------------------------------------------------------------
31− {
32− key: "board",
33− title: "Board",
34− section: "Workspace",
35− summary: "Issues and pull requests as a board, a table or a roadmap, with fields of your own.",
36− why: "One view of everything in flight, arranged the way your team thinks about it. Agents move the cards as they work, so the board is never out of date.",
37− plans: [
38− "Board, table and roadmap views of the same items, saved and shared",
39− "Custom fields: status, priority, size, iteration, dates, anything",
40− "Group and filter by owner, outcome, label, agent or state",
41− "Cards move by themselves as agents open, revise and land changes",
42− "Across projects: one board can hold work from any of the workspace's projects",
43− "Attach a board to a project, so it shows on that project too",
44− ],
45− },
46− {
47− key: "roadmap",
48− title: "Roadmap",
49− section: "Workspace",
50− summary: "Outcomes on a timeline, with how much of each has landed.",
51− why: "An outcome is what should be true when the work is done. On a roadmap you see when each is expected and how much has landed.",
52− plans: [
53− "Outcomes as bars on a timeline, from start to target",
54− "Progress from merged work, not from guesses",
55− "Slip warnings when the work left outgrows the time left",
56− ],
57− today: { label: "Outcomes", path: "plans" },
58− },
30+ // --- Workspace ----------------------------------------------------------
5931 {
6032 key: "insights",
6133 title: "Insights",
+1−1
3838 assert.equal(workspaceRedirect("/acme/-/marketplace/extensions"), "/acme/-/customize/plugins");
3939 assert.equal(workspaceRedirect("/acme/-/marketplace/extensions/mail/more"), null);
4040 assert.equal(workspaceRedirect("/acme/-/soon/insights", "?x=1"), "/acme/-/insights?x=1");
41− assert.equal(workspaceRedirect("/acme/-/soon/board"), null);
41+ assert.equal(workspaceRedirect("/acme/-/soon/uptime"), null);
4242 assert.equal(workspaceRedirect("/acme/-/people"), null);
4343 assert.equal(workspaceRedirect("/acme/web"), null);
4444 });
+1−1
3232 return { item, inProjects };
3333 }
3434
35−/** What a workspace will have across its projects: boards, the roadmap, packages, the fleet. */
35+/** What a workspace will have across its projects. */
3636 export default function WorkspaceSoon({ loaderData, params }: Route.ComponentProps) {
3737 return (
3838 <div className="mx-auto max-w-5xl px-4 py-10 sm:px-8">
+3−3
168168 // g1t made it, for ada: the card says both.
169169 assert.equal(pull.kind === "pull" && pull.author, "g1t");
170170 assert.equal(pull.kind === "pull" && pull.requestedBy, "ada");
171− const soon = await resolve("/acme/web/soon/board", s);
171+ const soon = await resolve("/acme/web/soon/environments", s);
172172 assert.equal(soon.kind, "soon");
173− assert.equal(soon.kind === "soon" && soon.title, "Board");
173+ assert.equal(soon.kind === "soon" && soon.title, "Environments");
174174 // One that does not exist falls back to the project.
175175 assert.equal((await resolve("/acme/web/issues/99", s)).kind, "project");
176176 assert.equal((await resolve("/acme/web/soon/nothing", s)).kind, "project");
179179
180180 test("nothing private ever reaches a card", async () => {
181181 const { sources: s } = sources();
182− for (const path of ["/acme/vault", "/acme/vault/issues/7", "/acme/vault/pull/8", "/acme/vault/soon/board", "/acme/vault/settings"]) {
182+ for (const path of ["/acme/vault", "/acme/vault/issues/7", "/acme/vault/pull/8", "/acme/vault/soon/environments", "/acme/vault/settings"]) {
183183 assert.deepEqual(await resolve(path, s), { kind: "brand" }, path);
184184 }
185185 // A missing repository says nothing either.