Skip to content

Commit 1f69370

Browse files
layout for the session details page
1 parent 9e4890b commit 1f69370

File tree

2 files changed

+772
-302
lines changed
  • components/frontend/src/app/projects/[name]

2 files changed

+772
-302
lines changed

components/frontend/src/app/projects/[name]/rfe/[id]/page.tsx

Lines changed: 137 additions & 56 deletions
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,11 @@ import { useParams, useRouter } from "next/navigation";
66
import { Button } from "@/components/ui/button";
77
import { Card, CardContent } from "@/components/ui/card";
88
import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs";
9+
import { Accordion, AccordionContent, AccordionItem, AccordionTrigger } from "@/components/ui/accordion";
910
import { WorkflowPhase } from "@/types/agentic-session";
10-
import { ArrowLeft, Loader2 } from "lucide-react";
11+
import { ArrowLeft, Loader2, Bot } from "lucide-react";
12+
import { Badge } from "@/components/ui/badge";
13+
import { Checkbox } from "@/components/ui/checkbox";
1114
import RepoBrowser from "@/components/RepoBrowser";
1215
import type { GitHubFork } from "@/types";
1316
import { Breadcrumbs } from "@/components/breadcrumbs";
@@ -16,7 +19,8 @@ import { RfePhaseCards } from "./rfe-phase-cards";
1619
import { RfeWorkspaceCard } from "./rfe-workspace-card";
1720
import { RfeHeader } from "./rfe-header";
1821
import { RfeAgentsCard } from "./rfe-agents-card";
19-
import { useRfeWorkflow, useRfeWorkflowSessions, useDeleteRfeWorkflow, useRfeWorkflowSeeding, useSeedRfeWorkflow, useUpdateRfeWorkflow, useRepoBlob, useRepoTree, useOpenJiraIssue } from "@/services/queries";
22+
import { AVAILABLE_AGENTS } from "@/lib/agents";
23+
import { useRfeWorkflow, useRfeWorkflowSessions, useDeleteRfeWorkflow, useRfeWorkflowSeeding, useSeedRfeWorkflow, useUpdateRfeWorkflow, useRepoBlob, useRepoTree, useOpenJiraIssue, useRfeWorkflowAgents } from "@/services/queries";
2024

2125
export default function ProjectRFEDetailPage() {
2226
const params = useParams();
@@ -32,6 +36,7 @@ export default function ProjectRFEDetailPage() {
3236
const seedWorkflowMutation = useSeedRfeWorkflow();
3337
const updateWorkflowMutation = useUpdateRfeWorkflow();
3438
const { openJiraForPath } = useOpenJiraIssue(project, id);
39+
const { data: repoAgents = AVAILABLE_AGENTS, isLoading: loadingAgents } = useRfeWorkflowAgents(project, id);
3540

3641
// Extract repo info from workflow
3742
const repo = workflow?.umbrellaRepo?.url.replace(/^https?:\/\/(?:www\.)?github.com\//i, '').replace(/\.git$/i, '') || '';
@@ -255,8 +260,6 @@ export default function ProjectRFEDetailPage() {
255260
onDelete={deleteWorkflow}
256261
/>
257262

258-
259-
260263
<RfeWorkspaceCard
261264
workflow={workflow}
262265
workflowWorkspace={workflowWorkspace}
@@ -269,62 +272,140 @@ export default function ProjectRFEDetailPage() {
269272
updating={updateWorkflowMutation.isPending}
270273
/>
271274

272-
<RfeAgentsCard
273-
projectName={project}
274-
workflowId={id}
275-
selectedAgents={selectedAgents}
276-
onAgentsChange={setSelectedAgents}
277-
/>
275+
{/* Two Column Layout */}
276+
<div className="grid grid-cols-1 lg:grid-cols-2 gap-6">
277+
{/* Left Column - Tabs */}
278+
<div className="lg:col-span-1">
279+
<Tabs value={activeTab} onValueChange={setActiveTab}>
280+
<TabsList>
281+
<TabsTrigger value="overview">Overview</TabsTrigger>
282+
<TabsTrigger value="sessions">Sessions</TabsTrigger>
283+
{upstreamRepo ? <TabsTrigger value="browser">Repository</TabsTrigger> : null}
284+
</TabsList>
278285

279-
<Tabs value={activeTab} onValueChange={setActiveTab}>
280-
<TabsList>
281-
<TabsTrigger value="overview">Overview</TabsTrigger>
282-
<TabsTrigger value="sessions">Sessions</TabsTrigger>
283-
{upstreamRepo ? <TabsTrigger value="browser">Repository</TabsTrigger> : null}
284-
</TabsList>
286+
<TabsContent value="overview">
287+
<RfePhaseCards
288+
workflow={workflow}
289+
rfeSessions={rfeSessions}
290+
rfeDoc={rfeDoc}
291+
specKitDir={specKitDir}
292+
firstFeaturePath={firstFeaturePath}
293+
projectName={project}
294+
rfeId={id}
295+
workflowWorkspace={workflowWorkspace}
296+
isSeeded={isSeeded}
297+
startingPhase={startingPhase}
298+
publishingPhase={publishingPhase}
299+
selectedAgents={selectedAgents}
300+
onStartPhase={setStartingPhase}
301+
onPublishPhase={setPublishingPhase}
302+
onLoad={async () => { await load(); }}
303+
onLoadSessions={async () => { await loadSessions(); }}
304+
onError={setError}
305+
onOpenJira={openJiraForPath}
306+
/>
307+
</TabsContent>
285308

286-
<TabsContent value="overview">
287-
<RfePhaseCards
288-
workflow={workflow}
289-
rfeSessions={rfeSessions}
290-
rfeDoc={rfeDoc}
291-
specKitDir={specKitDir}
292-
firstFeaturePath={firstFeaturePath}
293-
projectName={project}
294-
rfeId={id}
295-
workflowWorkspace={workflowWorkspace}
296-
isSeeded={isSeeded}
297-
startingPhase={startingPhase}
298-
publishingPhase={publishingPhase}
299-
selectedAgents={selectedAgents}
300-
onStartPhase={setStartingPhase}
301-
onPublishPhase={setPublishingPhase}
302-
onLoad={async () => { await load(); }}
303-
onLoadSessions={async () => { await loadSessions(); }}
304-
onError={setError}
305-
onOpenJira={openJiraForPath}
306-
/>
307-
</TabsContent>
309+
<TabsContent value="sessions">
310+
<RfeSessionsTable
311+
sessions={rfeSessions}
312+
projectName={project}
313+
rfeId={id}
314+
workspacePath={workflowWorkspace}
315+
workflowId={workflow.id}
316+
/>
317+
</TabsContent>
308318

309-
<TabsContent value="sessions">
310-
<RfeSessionsTable
311-
sessions={rfeSessions}
312-
projectName={project}
313-
rfeId={id}
314-
workspacePath={workflowWorkspace}
315-
workflowId={workflow.id}
316-
/>
317-
</TabsContent>
319+
<TabsContent value="browser">
320+
<RepoBrowser
321+
projectName={project}
322+
repoUrl={selectedFork?.url || upstreamRepo}
323+
defaultRef={selectedFork?.default_branch || workflow.branchName || workflow.umbrellaRepo?.branch || "main"}
324+
/>
325+
</TabsContent>
326+
</Tabs>
327+
</div>
318328

319-
320-
<TabsContent value="browser">
321-
<RepoBrowser
322-
projectName={project}
323-
repoUrl={selectedFork?.url || upstreamRepo}
324-
defaultRef={selectedFork?.default_branch || workflow.branchName || workflow.umbrellaRepo?.branch || "main"}
325-
/>
326-
</TabsContent>
327-
</Tabs>
329+
{/* Right Column - Agents Accordion */}
330+
<div className="lg:col-span-1">
331+
<Card>
332+
<CardContent className="p-4">
333+
<Accordion type="multiple" className="w-full">
334+
<AccordionItem value="agents" className="border-none">
335+
<AccordionTrigger className="text-lg font-semibold hover:no-underline px-0">
336+
<div className="flex items-center gap-2">
337+
<Bot className="h-5 w-5" />
338+
Agents
339+
</div>
340+
</AccordionTrigger>
341+
<AccordionContent className="pt-4">
342+
{loadingAgents ? (
343+
<div className="flex items-center justify-center py-8">
344+
<Loader2 className="h-6 w-6 animate-spin text-muted-foreground" />
345+
</div>
346+
) : repoAgents.length === 0 ? (
347+
<div className="text-center py-8 text-muted-foreground">
348+
<Bot className="h-12 w-12 mx-auto mb-2 opacity-50" />
349+
<p>No agents found in repository .claude/agents directory</p>
350+
<p className="text-xs mt-1">Seed the repository to add agent definitions</p>
351+
</div>
352+
) : (
353+
<>
354+
<div className="grid grid-cols-1 gap-3">
355+
{repoAgents.map((agent) => {
356+
const isSelected = selectedAgents.includes(agent.persona);
357+
return (
358+
<div
359+
key={agent.persona}
360+
className={`p-3 rounded-lg border transition-colors ${
361+
isSelected ? 'bg-primary/5 border-primary' : 'bg-background border-border hover:border-primary/50'
362+
}`}
363+
>
364+
<label className="flex items-start gap-3 cursor-pointer">
365+
<Checkbox
366+
checked={isSelected}
367+
onCheckedChange={(checked) => {
368+
setSelectedAgents(
369+
checked
370+
? [...selectedAgents, agent.persona]
371+
: selectedAgents.filter(p => p !== agent.persona)
372+
);
373+
}}
374+
className="mt-0.5"
375+
/>
376+
<div className="flex-1 min-w-0">
377+
<div className="font-medium text-sm">{agent.name}</div>
378+
<div className="text-xs text-muted-foreground mt-0.5">{agent.role}</div>
379+
</div>
380+
</label>
381+
</div>
382+
);
383+
})}
384+
</div>
385+
{selectedAgents.length > 0 && (
386+
<div className="mt-4 pt-4 border-t">
387+
<div className="text-sm font-medium mb-2">Selected Agents ({selectedAgents.length})</div>
388+
<div className="flex flex-wrap gap-2">
389+
{selectedAgents.map(persona => {
390+
const agent = repoAgents.find(a => a.persona === persona);
391+
return agent ? (
392+
<Badge key={persona} variant="secondary">
393+
{agent.name}
394+
</Badge>
395+
) : null;
396+
})}
397+
</div>
398+
</div>
399+
)}
400+
</>
401+
)}
402+
</AccordionContent>
403+
</AccordionItem>
404+
</Accordion>
405+
</CardContent>
406+
</Card>
407+
</div>
408+
</div>
328409

329410
</div>
330411
</div>

0 commit comments

Comments
 (0)