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 { IssuesList } from "../components/IssuesList";
|
||||||
import { PageSkeleton } from "../components/PageSkeleton";
|
import { PageSkeleton } from "../components/PageSkeleton";
|
||||||
import { PageTabBar } from "../components/PageTabBar";
|
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 { buildProjectWorkspaceSummaries } from "../lib/project-workspaces-tab";
|
||||||
import { projectRouteRef, projectWorkspaceUrl } from "../lib/utils";
|
import { projectRouteRef, projectWorkspaceUrl } from "../lib/utils";
|
||||||
import { timeAgo } from "../lib/timeAgo";
|
import { timeAgo } from "../lib/timeAgo";
|
||||||
|
|
@ -847,6 +854,63 @@ export function ProjectDetail() {
|
||||||
itemClassName="inline-flex"
|
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)}>
|
<Tabs value={activeTab ?? "list"} onValueChange={(value) => handleTabChange(value as ProjectTab)}>
|
||||||
<PageTabBar
|
<PageTabBar
|
||||||
items={[
|
items={[
|
||||||
|
|
@ -865,8 +929,9 @@ export function ProjectDetail() {
|
||||||
onValueChange={(value) => handleTabChange(value as ProjectTab)}
|
onValueChange={(value) => handleTabChange(value as ProjectTab)}
|
||||||
/>
|
/>
|
||||||
</Tabs>
|
</Tabs>
|
||||||
|
)}
|
||||||
|
|
||||||
{activeTab === "overview" && (
|
{activeTab === "overview" && resolveBuilderTab(location.pathname, canonicalProjectRef) === null && (
|
||||||
<OverviewContent
|
<OverviewContent
|
||||||
project={project}
|
project={project}
|
||||||
onUpdate={(data) => updateProject.mutate(data)}
|
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} />
|
<ProjectIssuesList projectId={project.id} companyId={resolvedCompanyId} />
|
||||||
)}
|
)}
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue