refactor(nexus): add BuilderTabStrip to ProjectDetail (phase 11)
Integrates the Phase 11 Builder tab strip into ProjectDetail without disturbing any existing Paperclip sub-route behavior. The approach: 1. When location.pathname resolves to a Builder tab (overview / issues / agents / gates / costs / activity / org), render the BuilderTabStrip + the matching new tab component (OverviewTab / ProjectIssuesList / AgentsTab / etc.) and hide the legacy PageTabBar + legacy overview/list handlers. 2. When the pathname resolves to a legacy tab (configuration / budget / workspaces / plugin:*), the existing Tabs/PageTabBar and dispatch render exactly as before — backwards compat preserved. This is Option 1 from the Phase 11 plan: union the old and new tab logic, silent pass-through for configuration/budget, no surface in the new strip. OverviewTab currently receives null for every data slice (progress/milestones/originChat/activity24h) because the shared Project type + projectsApi don't carry those fields yet; the tab renders explicit em-dash / "No milestones defined" placeholders. See the Phase 11 report for the backend gap list. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
e43d5ec220
commit
1e85565765
1 changed files with 67 additions and 2 deletions
|
|
@ -24,6 +24,13 @@ import { ExecutionWorkspaceCloseDialog } from "../components/ExecutionWorkspaceC
|
|||
import { IssuesList } from "../components/IssuesList";
|
||||
import { PageSkeleton } from "../components/PageSkeleton";
|
||||
import { PageTabBar } from "../components/PageTabBar";
|
||||
import { BuilderTabStrip, resolveBuilderTab } from "../components/projects/BuilderTabStrip";
|
||||
import { OverviewTab } from "../components/projects/tabs/OverviewTab";
|
||||
import { AgentsTab } from "../components/projects/tabs/AgentsTab";
|
||||
import { GatesTab } from "../components/projects/tabs/GatesTab";
|
||||
import { CostsTab } from "../components/projects/tabs/CostsTab";
|
||||
import { ActivityTab } from "../components/projects/tabs/ActivityTab";
|
||||
import { OrgTab } from "../components/projects/tabs/OrgTab";
|
||||
import { buildProjectWorkspaceSummaries } from "../lib/project-workspaces-tab";
|
||||
import { projectRouteRef, projectWorkspaceUrl } from "../lib/utils";
|
||||
import { timeAgo } from "../lib/timeAgo";
|
||||
|
|
@ -847,6 +854,63 @@ export function ProjectDetail() {
|
|||
itemClassName="inline-flex"
|
||||
/>
|
||||
|
||||
{/*
|
||||
Phase 11 Builder tab strip. Renders iff the current URL resolves
|
||||
to one of the new Builder tabs (overview/issues/agents/gates/
|
||||
costs/activity/org). Legacy sub-routes (configuration/budget/
|
||||
workspaces/plugin) fall through to the existing PageTabBar
|
||||
below for backwards compat — see plan §Preserving existing
|
||||
behavior.
|
||||
*/}
|
||||
{(() => {
|
||||
const builderTab = resolveBuilderTab(location.pathname, canonicalProjectRef);
|
||||
if (!builderTab) return null;
|
||||
// Phase 11: no reliable per-project agent count source yet. Show
|
||||
// the full (7-tab) strip by default; BuilderTabStrip hides ORG
|
||||
// when hasMultipleAgents=false. Leaving this true preserves
|
||||
// access to the Org placeholder tab until the data is wired.
|
||||
const hasMultipleAgents = true;
|
||||
return (
|
||||
<div data-testid="project-detail-builder-chrome" className="space-y-6">
|
||||
<BuilderTabStrip
|
||||
projectRef={canonicalProjectRef}
|
||||
activeTab={builderTab}
|
||||
hasMultipleAgents={hasMultipleAgents}
|
||||
/>
|
||||
{builderTab === "overview" && (
|
||||
<OverviewTab
|
||||
project={{ name: project.name, status: project.status }}
|
||||
progress={null}
|
||||
activeAgentsCount={0}
|
||||
milestones={null}
|
||||
originChat={null}
|
||||
activity24h={{
|
||||
commits: null,
|
||||
issuesClosed: null,
|
||||
gatesAwaiting: null,
|
||||
costBurnedCents: null,
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
{builderTab === "issues" && project?.id && resolvedCompanyId && (
|
||||
<ProjectIssuesList projectId={project.id} companyId={resolvedCompanyId} />
|
||||
)}
|
||||
{builderTab === "agents" && <AgentsTab projectId={project.id} />}
|
||||
{builderTab === "gates" && <GatesTab projectId={project.id} />}
|
||||
{builderTab === "costs" && <CostsTab projectId={project.id} />}
|
||||
{builderTab === "activity" && <ActivityTab projectId={project.id} />}
|
||||
{builderTab === "org" && <OrgTab projectId={project.id} />}
|
||||
</div>
|
||||
);
|
||||
})()}
|
||||
|
||||
{/*
|
||||
Legacy Paperclip chrome — rendered only when the current tab is
|
||||
NOT a Builder tab (configuration / budget / workspaces / plugin).
|
||||
This preserves existing behavior for every pre-Phase-11
|
||||
sub-route while the Builder strip owns the new tabs.
|
||||
*/}
|
||||
{resolveBuilderTab(location.pathname, canonicalProjectRef) === null && (
|
||||
<Tabs value={activeTab ?? "list"} onValueChange={(value) => handleTabChange(value as ProjectTab)}>
|
||||
<PageTabBar
|
||||
items={[
|
||||
|
|
@ -865,8 +929,9 @@ export function ProjectDetail() {
|
|||
onValueChange={(value) => handleTabChange(value as ProjectTab)}
|
||||
/>
|
||||
</Tabs>
|
||||
)}
|
||||
|
||||
{activeTab === "overview" && (
|
||||
{activeTab === "overview" && resolveBuilderTab(location.pathname, canonicalProjectRef) === null && (
|
||||
<OverviewContent
|
||||
project={project}
|
||||
onUpdate={(data) => updateProject.mutate(data)}
|
||||
|
|
@ -877,7 +942,7 @@ export function ProjectDetail() {
|
|||
/>
|
||||
)}
|
||||
|
||||
{activeTab === "list" && project?.id && resolvedCompanyId && (
|
||||
{activeTab === "list" && project?.id && resolvedCompanyId && resolveBuilderTab(location.pathname, canonicalProjectRef) === null && (
|
||||
<ProjectIssuesList projectId={project.id} companyId={resolvedCompanyId} />
|
||||
)}
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue