Skip to content

Commit a65686c

Browse files
committed
API call for plan page
1 parent 32f43df commit a65686c

File tree

4 files changed

+186
-11
lines changed

4 files changed

+186
-11
lines changed

src/backend/context/cosmos_memory_kernel.py

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -231,6 +231,17 @@ async def get_plan_by_session(self, session_id: str) -> Optional[Plan]:
231231
plans = await self.query_items(query, parameters, Plan)
232232
return plans[0] if plans else None
233233

234+
async def get_plan_by_plan_id(self, plan_id: str) -> Optional[Plan]:
235+
"""Retrieve a plan associated with a session."""
236+
query = "SELECT * FROM c WHERE c.id=@id AND c.user_id=@user_id AND c.data_type=@data_type"
237+
parameters = [
238+
{"name": "@id", "value": plan_id},
239+
{"name": "@data_type", "value": "plan"},
240+
{"name": "@user_id", "value": self.user_id},
241+
]
242+
plans = await self.query_items(query, parameters, Plan)
243+
return plans[0] if plans else None
244+
234245
async def get_thread_by_session(self, session_id: str) -> Optional[Any]:
235246
"""Retrieve a plan associated with a session."""
236247
query = "SELECT * FROM c WHERE c.session_id=@session_id AND c.user_id=@user_id AND c.data_type=@data_type"

src/frontend_react/src/api/apiService.tsx

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -131,6 +131,41 @@ export class APIService {
131131
return fetcher();
132132
}
133133

134+
/**
135+
* Get a single plan by plan ID
136+
* @param planId Plan ID to fetch
137+
* @param useCache Whether to use cached data or force fresh fetch
138+
* @returns Promise with the plan and its steps
139+
*/
140+
async getPlanById(planId: string, useCache = true): Promise<PlanWithSteps> {
141+
const cacheKey = `plan_by_id_${planId}`;
142+
const params = { plan_id: planId };
143+
144+
const fetcher = async () => {
145+
const data = await apiClient.get(API_ENDPOINTS.PLANS, { params });
146+
147+
// The API returns an array, but with plan_id filter it should have only one item
148+
if (!data || data.length === 0) {
149+
throw new Error(`Plan with ID ${planId} not found`);
150+
}
151+
152+
const plan = data[0];
153+
if (useCache) {
154+
this._cache.set(cacheKey, plan, 30000); // Cache for 30 seconds
155+
}
156+
return plan;
157+
};
158+
159+
if (useCache) {
160+
const cachedPlan = this._cache.get<PlanWithSteps>(cacheKey);
161+
if (cachedPlan) return cachedPlan;
162+
163+
return this._requestTracker.trackRequest(cacheKey, fetcher);
164+
}
165+
166+
return fetcher();
167+
}
168+
134169
/**
135170
* Get a specific plan with its steps
136171
* @param sessionId Session ID

src/frontend_react/src/pages/PlanPage.tsx

Lines changed: 39 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -6,12 +6,16 @@ import {
66
Card,
77
CardHeader,
88
useToastController,
9-
Spinner
9+
Spinner,
10+
Badge
1011
} from '@fluentui/react-components';
1112
import {
1213
Add20Regular,
1314
ArrowLeft24Regular,
14-
ErrorCircle24Regular
15+
ErrorCircle24Regular,
16+
Person24Regular,
17+
CheckmarkCircle24Regular,
18+
AlertUrgent24Regular
1519
} from '@fluentui/react-icons';
1620
import '../styles/PlanPage.css';
1721
import CoralShellColumn from '../coral/components/Layout/CoralShellColumn';
@@ -21,7 +25,8 @@ import PanelLeft from '../coral/components/Panels/PanelLeft';
2125
import PanelLeftToolbar from '../coral/components/Panels/PanelLeftToolbar';
2226
import TaskList from '../components/content/TaskList';
2327
import { NewTaskService } from '../services/NewTaskService';
24-
import { PlanWithSteps, Task } from '@/models';
28+
import { PlanDataService, ProcessedPlanData } from '../services/PlanDataService';
29+
import { PlanWithSteps, Task, AgentType, Step } from '@/models';
2530
import { apiService } from '@/api';
2631
import PlanPanelLeft from '@/components/content/PlanPanelLeft';
2732

@@ -33,15 +38,40 @@ const PlanPage: React.FC = () => {
3338
const { planId } = useParams<{ planId: string }>();
3439
const navigate = useNavigate();
3540

41+
// State for plan data
42+
const [planData, setPlanData] = useState<ProcessedPlanData | null>(null);
43+
const [loading, setLoading] = useState<boolean>(true);
44+
const [error, setError] = useState<Error | null>(null);
3645

46+
/**
47+
* Fetch plan data when component mounts or planId changes
48+
*/
49+
const loadPlanData = useCallback(async () => {
50+
if (!planId) return;
3751

52+
try {
53+
setLoading(true);
54+
setError(null);
55+
56+
const data = await PlanDataService.fetchPlanData(planId);
57+
setPlanData(data);
58+
} catch (err) {
59+
console.error('Failed to load plan data:', err);
60+
setError(err instanceof Error ? err : new Error('Failed to load plan data'));
61+
} finally {
62+
setLoading(false);
63+
}
64+
}, [planId]);
65+
66+
// Load plan data on mount and when planId changes
67+
useEffect(() => {
68+
loadPlanData();
69+
}, [loadPlanData]);
3870

3971
const handleNewTaskButton = () => {
4072
// Use NewTaskService to handle navigation to homepage and reset textarea
4173
NewTaskService.handleNewTaskFromPlan(navigate);
42-
};
43-
44-
// Show error if no planId is provided
74+
}; // Show error if no planId is provided
4575
if (!planId) {
4676
return (
4777
<div style={{ padding: '20px' }}>
@@ -50,20 +80,18 @@ const PlanPage: React.FC = () => {
5080
);
5181
}
5282

53-
5483
return (
5584
<CoralShellColumn>
5685
<CoralShellRow>
57-
<PlanPanelLeft
58-
onNewTaskButton={handleNewTaskButton}
59-
/>
86+
<PlanPanelLeft onNewTaskButton={handleNewTaskButton} />
6087
<Content>
6188

6289
</Content>
63-
6490
</CoralShellRow>
6591
</CoralShellColumn>
6692
);
93+
94+
6795
};
6896

6997
export default PlanPage;
Lines changed: 101 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,101 @@
1+
import { PlanWithSteps, Step, AgentType } from '@/models';
2+
import { apiService } from '@/api';
3+
4+
/**
5+
* Interface for processed plan data
6+
*/
7+
export interface ProcessedPlanData {
8+
plan: PlanWithSteps;
9+
agents: AgentType[];
10+
steps: Step[];
11+
hasHumanClarificationRequest: boolean;
12+
}
13+
14+
/**
15+
* Service for processing and managing plan data operations
16+
*/
17+
export class PlanDataService { /**
18+
* Fetch plan details by plan ID and process the data
19+
* @param planId Plan ID to fetch
20+
* @returns Promise with processed plan data
21+
*/
22+
static async fetchPlanData(planId: string): Promise<ProcessedPlanData> {
23+
try {
24+
// Use optimized getPlanById method for better performance
25+
const plan = await apiService.getPlanById(planId);
26+
return this.processPlanData(plan);
27+
} catch (error) {
28+
console.error('Failed to fetch plan data:', error);
29+
throw error;
30+
}
31+
}
32+
33+
/**
34+
* Process plan data to extract agents, steps, and clarification status
35+
* @param plan PlanWithSteps object to process
36+
* @returns Processed plan data
37+
*/
38+
static processPlanData(plan: PlanWithSteps): ProcessedPlanData {
39+
// Extract unique agents from steps
40+
const uniqueAgents = new Set<AgentType>();
41+
plan.steps.forEach(step => {
42+
if (step.agent) {
43+
uniqueAgents.add(step.agent);
44+
}
45+
});
46+
47+
// Convert Set to Array for easier handling
48+
const agents = Array.from(uniqueAgents);
49+
50+
// Get all steps
51+
const steps = plan.steps;
52+
53+
// Check if human_clarification_request is not null
54+
const hasHumanClarificationRequest = plan.human_clarification_request != null &&
55+
plan.human_clarification_request.trim().length > 0;
56+
57+
return {
58+
plan,
59+
agents,
60+
steps,
61+
hasHumanClarificationRequest
62+
};
63+
}
64+
65+
/**
66+
* Get steps for a specific agent type
67+
* @param plan Plan with steps
68+
* @param agentType Agent type to filter by
69+
* @returns Array of steps for the specified agent
70+
*/
71+
static getStepsForAgent(plan: PlanWithSteps, agentType: AgentType): Step[] {
72+
return apiService.getStepsForAgent(plan, agentType);
73+
}
74+
75+
/**
76+
* Get steps that are awaiting human feedback
77+
* @param plan Plan with steps
78+
* @returns Array of steps awaiting feedback
79+
*/
80+
static getStepsAwaitingFeedback(plan: PlanWithSteps): Step[] {
81+
return apiService.getStepsAwaitingFeedback(plan);
82+
}
83+
84+
/**
85+
* Check if plan is complete
86+
* @param plan Plan with steps
87+
* @returns Boolean indicating if plan is complete
88+
*/
89+
static isPlanComplete(plan: PlanWithSteps): boolean {
90+
return apiService.isPlanComplete(plan);
91+
}
92+
93+
/**
94+
* Get plan completion percentage
95+
* @param plan Plan with steps
96+
* @returns Completion percentage (0-100)
97+
*/
98+
static getPlanCompletionPercentage(plan: PlanWithSteps): number {
99+
return apiService.getPlanCompletionPercentage(plan);
100+
}
101+
}

0 commit comments

Comments
 (0)