Skip to content

Compare changes

Choose two branches to see what one has that the other does not, then open a pull request for it.

Open a pull request

1 commit

3 files+83−200/3 viewed
+15−18
392392 </SidebarLink>
393393 </div>
394394
395− {ws && (
396− <SidebarGroup title="Workspace">
397− <SidebarLink to={`/${ws.slug}`} end icon={<LayoutDashboard size={15} />}>
398− Overview
399− </SidebarLink>
400− <SidebarLink to={`/${ws.slug}/-/usage`} icon={<BarChart3 size={15} />}>
401− Usage
402− </SidebarLink>
403− {/* Everything else about the workspace lives in its settings, and only there. */}
404− <SidebarLink
405− to={ws.role === "owner" ? `/${ws.slug}/-/settings` : `/${ws.slug}/-/people`}
406− icon={<Settings size={15} />}
407− >
408− Settings
409− </SidebarLink>
410− </SidebarGroup>
411− )}
412−
413395 <SidebarGroup
414396 title="Repositories"
415397 action={
506488 </div>
507489 </div>
508490
491+ {/* The workspace's own things sit at the bottom, by its usage and the account. */}
509492 <div className="space-y-2 p-2">
493+ {ws && (
494+ <div className="space-y-px">
495+ <SidebarLink to={`/${ws.slug}/-/usage`} icon={<BarChart3 size={15} />}>
496+ Usage
497+ </SidebarLink>
498+ {/* Everything else about the workspace lives in its settings, and only there. */}
499+ <SidebarLink
500+ to={ws.role === "owner" ? `/${ws.slug}/-/settings` : `/${ws.slug}/-/people`}
501+ icon={<Settings size={15} />}
502+ >
503+ Settings
504+ </SidebarLink>
505+ </div>
506+ )}
510507 {ws && <UsageCard slug={ws.slug} shell={shell} />}
511508 <AccountMenu user={user} />
512509 </div>
+61−1
1−import { ArrowRight, Hand, Plus } from "lucide-react";
1+import { ArrowRight, Hand, Plus, Settings, Users } from "lucide-react";
22 import { type ReactNode, useEffect } from "react";
33 import { Link, data, useRevalidator, useRouteLoaderData } from "react-router";
44
141141 type Step = { done: boolean; title: string; about: string; to: string | null; action: string };
142142
143143 /**
144+ * The workspace being looked at, from mission control: what it holds and
145+ * what is moving in it, and the way to its people and settings. The rest of
146+ * the page stays the viewer's own, across every workspace.
147+ */
148+function WorkspaceCard({ slug, role, repos, active }: { slug: string; role: string; repos: Repo[]; active: Active[] }) {
149+ const mine = active.filter((item) => item.repo.namespace.toLowerCase() === slug);
150+ const stats = [
151+ { value: repos.filter((repo) => repo.namespace.toLowerCase() === slug).length, label: "repositories" },
152+ { value: mine.length, label: "pull requests open" },
153+ { value: mine.filter((item) => item.lifecycle?.stage === "needs_you").length, label: "need you" },
154+ ];
155+ return (
156+ <div className="rounded-xl border border-line bg-surface p-5">
157+ <div className="flex items-center gap-3">
158+ <Avatar name={slug} size={32} square />
159+ <div className="min-w-0">
160+ <Link to={`/${slug}`} className="block truncate font-medium hover:underline">
161+ {slug}
162+ </Link>
163+ <p className="text-xs text-muted">Workspace · {role}</p>
164+ </div>
165+ </div>
166+ <dl className="mt-4 grid grid-cols-3 gap-2">
167+ {stats.map((stat) => (
168+ <div key={stat.label} className="rounded-lg bg-bg/60 px-2.5 py-2 ring-1 ring-line">
169+ <dt className="sr-only">{stat.label}</dt>
170+ <dd className="text-lg font-semibold tabular-nums tracking-tight">{stat.value}</dd>
171+ <dd className="text-[0.6875rem] leading-tight text-muted">{stat.label}</dd>
172+ </div>
173+ ))}
174+ </dl>
175+ <div className="mt-4 flex flex-wrap gap-x-4 gap-y-1 text-sm">
176+ <Link to={`/${slug}/-/people`} className="inline-flex items-center gap-1.5 text-muted hover:text-fg">
177+ <Users size={13} />
178+ Members
179+ </Link>
180+ <Link
181+ to={role === "owner" ? `/${slug}/-/settings` : `/${slug}/-/people`}
182+ className="inline-flex items-center gap-1.5 text-muted hover:text-fg"
183+ >
184+ <Settings size={13} />
185+ Settings
186+ </Link>
187+ <Link to={`/${slug}`} className="inline-flex items-center gap-1 text-accent hover:underline">
188+ Its page <ArrowRight size={13} />
189+ </Link>
190+ </div>
191+ </div>
192+ );
193+}
194+
195+/**
144196 * The first things to do, ticked off as they are done, until an outcome has
145197 * been handed to agents. Shown above everything else on mission control.
146198 */
343395 </div>
344396
345397 <aside className="space-y-4">
398+ {shell?.workspace && (
399+ <WorkspaceCard
400+ slug={shell.workspace.slug.toLowerCase()}
401+ role={shell.workspace.role}
402+ repos={repos}
403+ active={active}
404+ />
405+ )}
346406 <div className="rounded-xl border border-line bg-surface p-5">
347407 <h2 className="font-medium">Connect an agent</h2>
348408 <p className="mt-1.5 text-sm text-muted">
+7−1
300300 w.id === workflow?.id ? "bg-raised text-fg" : "text-muted hover:bg-raised/60 hover:text-fg"
301301 }`}
302302 >
303− <span className="min-w-0 truncate">{w.name}</span>
303+ <span className="min-w-0 truncate">
304+ {w.name}
305+ {/* Two workflows of one name are told apart by their files. */}
306+ {workflows.some((other) => other.id !== w.id && other.name === w.name) && (
307+ <span className="block truncate font-mono text-[0.6875rem] text-faint">{w.path}</span>
308+ )}
309+ </span>
304310 {w.error && <AlertTriangle size={12} className="shrink-0 text-danger" aria-label="Its file has a problem" />}
305311 {w.state === "disabled" && <span className="ml-auto shrink-0 text-xs text-faint">off</span>}
306312 </Link>