feat(graph): integrate WorkflowGraph into unified shell
- Create GraphView component with Flow/Overview tab switcher - Wire GraphView into UnifiedShell when view=graph - Connect taskId URL param to selectedId for node selection - Connect graphTab URL param for tab state persistence - ReactFlow renders in middle panel with proper layout Closes bb-ui2.20
This commit is contained in:
parent
6d87f18f20
commit
539e6e7021
3 changed files with 171 additions and 8 deletions
66
src/components/graph/graph-view.tsx
Normal file
66
src/components/graph/graph-view.tsx
Normal file
|
|
@ -0,0 +1,66 @@
|
|||
'use client';
|
||||
|
||||
import type { BeadIssue } from '../../lib/types';
|
||||
import { WorkflowGraph } from '../shared/workflow-graph';
|
||||
import type { GraphTabType } from '../../hooks/use-url-state';
|
||||
|
||||
interface GraphViewProps {
|
||||
beads: BeadIssue[];
|
||||
selectedId?: string;
|
||||
onSelect?: (id: string) => void;
|
||||
graphTab: GraphTabType;
|
||||
onGraphTabChange: (tab: GraphTabType) => void;
|
||||
hideClosed?: boolean;
|
||||
}
|
||||
|
||||
export function GraphView({
|
||||
beads,
|
||||
selectedId,
|
||||
onSelect,
|
||||
graphTab,
|
||||
onGraphTabChange,
|
||||
hideClosed = false,
|
||||
}: GraphViewProps) {
|
||||
return (
|
||||
<div className="flex flex-col h-full">
|
||||
<div className="flex items-center justify-between border-b border-white/5 px-4 py-2 bg-white/[0.02]">
|
||||
<div className="flex items-center gap-1">
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => onGraphTabChange('flow')}
|
||||
className={`rounded-lg px-3 py-1.5 text-xs font-bold uppercase tracking-wider transition-all ${
|
||||
graphTab === 'flow'
|
||||
? 'bg-sky-400/10 text-sky-200 shadow-[0_2px_8px_rgba(56,189,248,0.1)]'
|
||||
: 'text-text-muted/60 hover:text-text-body hover:bg-white/[0.04]'
|
||||
}`}
|
||||
>
|
||||
Flow
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => onGraphTabChange('overview')}
|
||||
className={`rounded-lg px-3 py-1.5 text-xs font-bold uppercase tracking-wider transition-all ${
|
||||
graphTab === 'overview'
|
||||
? 'bg-sky-400/10 text-sky-200 shadow-[0_2px_8px_rgba(56,189,248,0.1)]'
|
||||
: 'text-text-muted/60 hover:text-text-body hover:bg-white/[0.04]'
|
||||
}`}
|
||||
>
|
||||
Overview
|
||||
</button>
|
||||
</div>
|
||||
<span className="text-[10px] text-text-muted/50">
|
||||
{beads.length} beads
|
||||
</span>
|
||||
</div>
|
||||
<div className="flex-1 min-h-0">
|
||||
<WorkflowGraph
|
||||
beads={beads}
|
||||
selectedId={selectedId}
|
||||
onSelect={onSelect}
|
||||
hideClosed={graphTab === 'flow' ? hideClosed : false}
|
||||
className="h-full"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
86
src/components/shared/unified-shell.tsx
Normal file
86
src/components/shared/unified-shell.tsx
Normal file
|
|
@ -0,0 +1,86 @@
|
|||
'use client';
|
||||
|
||||
import type { BeadIssue } from '../../lib/types';
|
||||
import type { ProjectScopeOption } from '../../lib/project-scope';
|
||||
import { TopBar } from './top-bar';
|
||||
import { LeftPanel } from './left-panel';
|
||||
import { RightPanel } from './right-panel';
|
||||
import { MobileNav } from './mobile-nav';
|
||||
import { useUrlState } from '../../hooks/use-url-state';
|
||||
import { GraphView } from '../graph/graph-view';
|
||||
|
||||
export interface UnifiedShellProps {
|
||||
issues: BeadIssue[];
|
||||
projectRoot: string;
|
||||
projectScopeKey: string;
|
||||
projectScopeOptions: ProjectScopeOption[];
|
||||
projectScopeMode: 'single' | 'aggregate';
|
||||
}
|
||||
|
||||
export function UnifiedShell({
|
||||
issues,
|
||||
}: UnifiedShellProps) {
|
||||
const { view, taskId, setTaskId, graphTab, setGraphTab, panel } = useUrlState();
|
||||
|
||||
const handleGraphSelect = (id: string) => {
|
||||
setTaskId(id);
|
||||
};
|
||||
|
||||
const renderMiddleContent = () => {
|
||||
if (view === 'graph') {
|
||||
return (
|
||||
<GraphView
|
||||
beads={issues}
|
||||
selectedId={taskId ?? undefined}
|
||||
onSelect={handleGraphSelect}
|
||||
graphTab={graphTab}
|
||||
onGraphTabChange={setGraphTab}
|
||||
hideClosed={false}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="p-4" style={{ color: 'var(--color-text-secondary)' }}>
|
||||
<div className="mb-4">
|
||||
<h2 className="text-lg font-semibold" style={{ color: 'var(--color-text-primary)' }}>
|
||||
{view === 'social' && 'Social View'}
|
||||
{view === 'swarm' && 'Swarm View'}
|
||||
</h2>
|
||||
<p className="text-sm mt-2">
|
||||
{view === 'social' && 'Activity feed with blocks/unlocks coming soon'}
|
||||
{view === 'swarm' && 'Team health dashboard coming soon'}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="flex flex-col h-screen" style={{ backgroundColor: 'var(--color-bg-base)' }} data-testid="unified-shell">
|
||||
{/* TOP BAR: 3rem fixed */}
|
||||
<TopBar />
|
||||
|
||||
{/* MAIN AREA: CSS Grid [13rem | 1fr | 17rem] */}
|
||||
<div
|
||||
className="flex-1 grid overflow-hidden"
|
||||
style={{ gridTemplateColumns: '13rem 1fr 17rem' }}
|
||||
data-testid="main-area"
|
||||
>
|
||||
{/* LEFT PANEL: 13rem channel tree */}
|
||||
<LeftPanel issues={issues} />
|
||||
|
||||
{/* MIDDLE CONTENT: flex-1 */}
|
||||
<div className="overflow-y-auto" data-testid="middle-content">
|
||||
{renderMiddleContent()}
|
||||
</div>
|
||||
|
||||
{/* RIGHT PANEL: 17rem detail strip */}
|
||||
<RightPanel isOpen={panel === 'open'} />
|
||||
</div>
|
||||
|
||||
{/* MOBILE NAV: Bottom tab bar */}
|
||||
<MobileNav />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue