Skip to content

Commit 0af3eb5

Browse files
committed
add plan page
1 parent 548ddfa commit 0af3eb5

File tree

4 files changed

+301
-142
lines changed

4 files changed

+301
-142
lines changed

src/frontend_react/src/App.tsx

Lines changed: 11 additions & 140 deletions
Original file line numberDiff line numberDiff line change
@@ -1,150 +1,21 @@
1-
import React, { useState } from 'react';
1+
import React from 'react';
22
import './App.css';
33
import AppProvider from './components/AppProvider';
4-
import FluentPlanViewV8 from './components/FluentPlanViewV8';
5-
import {
6-
Stack,
7-
Text,
8-
PrimaryButton,
9-
TextField,
10-
IStackStyles,
11-
ITextStyles,
12-
Spinner,
13-
SpinnerSize,
14-
Card as FluentCard,
15-
ICardStyles,
16-
Label,
17-
DefaultButton,
18-
mergeStyles
19-
} from '@fluentui/react';
20-
21-
// Define styles
22-
const containerStyles: IStackStyles = {
23-
root: {
24-
maxWidth: '1200px',
25-
margin: '0 auto',
26-
padding: '20px',
27-
}
28-
};
29-
30-
const headerStyles: IStackStyles = {
31-
root: {
32-
marginBottom: '20px'
33-
}
34-
};
35-
36-
const cardStyles: ICardStyles = {
37-
root: {
38-
marginBottom: '20px',
39-
}
40-
};
41-
42-
const titleStyles: ITextStyles = {
43-
root: {
44-
fontSize: '28px',
45-
fontWeight: 600,
46-
marginBottom: '0',
47-
}
48-
};
49-
50-
const formStyles: IStackStyles = {
51-
root: {
52-
marginTop: '20px',
53-
marginBottom: '20px'
54-
}
55-
};
56-
57-
const instructionTextStyles: ITextStyles = {
58-
root: {
59-
marginBottom: '8px'
60-
}
61-
};
62-
63-
const cardTitleStyles: ITextStyles = {
64-
root: {
65-
fontSize: '20px',
66-
fontWeight: 600,
67-
marginBottom: '16px'
68-
}
69-
};
4+
import { BrowserRouter as Router, Routes, Route, Navigate } from 'react-router-dom';
5+
import { HomePage, PlanPage } from './pages';
706

717
function App() {
72-
const [sessionId, setSessionId] = useState('');
73-
const [planId, setPlanId] = useState('');
74-
const [loading, setLoading] = useState(false);
75-
const [submitted, setSubmitted] = useState(false);
76-
77-
const handleSubmit = (e: React.FormEvent) => {
78-
e.preventDefault();
79-
if (sessionId && planId) {
80-
setLoading(true);
81-
// Simulate loading
82-
setTimeout(() => {
83-
setLoading(false);
84-
setSubmitted(true);
85-
}, 500);
86-
}
87-
};
88-
898
return (
909
<AppProvider>
91-
<Stack styles={containerStyles}>
92-
<Stack horizontal horizontalAlign="space-between" verticalAlign="center" styles={headerStyles}>
93-
<Text styles={titleStyles}>
94-
Multi-Agent Custom Automation Engine
95-
</Text>
96-
</Stack>
97-
98-
{!submitted ? (
99-
<>
100-
<FluentCard styles={cardStyles}>
101-
<Stack tokens={{ padding: 16 }}>
102-
<Text styles={cardTitleStyles}>Welcome to MACAE</Text>
103-
<Text styles={instructionTextStyles}>
104-
Enter your session ID and plan ID to view your plan and approve/reject steps.
105-
</Text>
106-
<Text styles={instructionTextStyles}>
107-
These IDs would typically be provided to you when a plan is created by the system.
108-
</Text>
109-
</Stack>
110-
</FluentCard>
111-
112-
<form onSubmit={handleSubmit}>
113-
<Stack styles={formStyles} tokens={{ childrenGap: 12 }}>
114-
<TextField
115-
label="Session ID"
116-
value={sessionId}
117-
onChange={(_, newValue) => setSessionId(newValue || '')}
118-
placeholder="Enter Session ID"
119-
required
120-
/>
121-
<TextField
122-
label="Plan ID"
123-
value={planId}
124-
onChange={(_, newValue) => setPlanId(newValue || '')}
125-
placeholder="Enter Plan ID"
126-
required
127-
/>
128-
<Stack horizontal tokens={{ childrenGap: 8 }}>
129-
<PrimaryButton
130-
type="submit"
131-
text={loading ? 'Loading...' : 'View Plan'}
132-
iconProps={{ iconName: loading ? undefined : 'Forward' }}
133-
disabled={loading || !sessionId || !planId}
134-
/>
135-
{loading && <Spinner size={SpinnerSize.small} />}
136-
</Stack>
137-
</Stack>
138-
</form>
139-
</>
140-
) : (
141-
<FluentPlanViewV8 sessionId={sessionId} planId={planId} />
142-
)}
143-
</Stack>
10+
<Router>
11+
<Routes>
12+
<Route path="/" element={<HomePage />} />
13+
<Route path="/plan/:planId" element={<PlanPage />} />
14+
<Route path="*" element={<Navigate to="/" replace />} />
15+
</Routes>
16+
</Router>
14417
</AppProvider>
14518
);
14619
}
14720

148-
export default App;
149-
150-
export default App;
21+
export default App;
Lines changed: 209 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,209 @@
1+
import React from 'react';
2+
import { useNavigate } from 'react-router-dom';
3+
import {
4+
Card,
5+
CardHeader,
6+
Text,
7+
Badge,
8+
Button
9+
} from '@fluentui/react-components';
10+
import {
11+
Eye24Regular,
12+
Calendar24Regular,
13+
CheckmarkCircle24Regular,
14+
ErrorCircle24Regular,
15+
Clock24Regular
16+
} from '@fluentui/react-icons';
17+
import { PlanWithSteps, PlanStatus } from '../models';
18+
import { apiService } from '../api/apiService';
19+
import '../styles/PlanList.css';
20+
21+
interface PlanListProps {
22+
/** Array of plans to display */
23+
plans: PlanWithSteps[];
24+
/** Optional loading state */
25+
loading?: boolean;
26+
/** Optional empty state message */
27+
emptyMessage?: string;
28+
/** Optional flag to show view button */
29+
showViewButton?: boolean;
30+
}
31+
32+
/**
33+
* Component to display a list of plans in a grid layout
34+
*/
35+
const PlanList: React.FC<PlanListProps> = ({
36+
plans,
37+
loading = false,
38+
emptyMessage = "No plans found",
39+
showViewButton = true
40+
}) => {
41+
const navigate = useNavigate();
42+
43+
/**
44+
* Get badge color based on plan status
45+
*/
46+
const getPlanStatusColor = (status: PlanStatus): string => {
47+
switch (status) {
48+
case PlanStatus.COMPLETED:
49+
return 'success';
50+
case PlanStatus.IN_PROGRESS:
51+
return 'brand';
52+
case PlanStatus.FAILED:
53+
return 'danger';
54+
default:
55+
return 'informative';
56+
}
57+
};
58+
59+
/**
60+
* Get status icon based on plan status
61+
*/
62+
const getPlanStatusIcon = (status: PlanStatus): JSX.Element => {
63+
switch (status) {
64+
case PlanStatus.COMPLETED:
65+
return <CheckmarkCircle24Regular />;
66+
case PlanStatus.FAILED:
67+
return <ErrorCircle24Regular />;
68+
case PlanStatus.IN_PROGRESS:
69+
return <Clock24Regular />;
70+
default:
71+
return <Calendar24Regular />;
72+
}
73+
};
74+
75+
/**
76+
* Format status text for display
77+
*/
78+
const getStatusText = (status: PlanStatus): string => {
79+
return status.replace('_', ' ').toLowerCase();
80+
};
81+
82+
/**
83+
* Navigate to plan details page
84+
*/
85+
const handleViewPlan = (planId: string) => {
86+
navigate(`/plan/${planId}`);
87+
};
88+
89+
/**
90+
* Format timestamp to readable date
91+
*/
92+
const formatDate = (timestamp: string): string => {
93+
return new Date(timestamp).toLocaleDateString('en-US', {
94+
year: 'numeric',
95+
month: 'short',
96+
day: 'numeric',
97+
hour: '2-digit',
98+
minute: '2-digit'
99+
});
100+
};
101+
102+
// Show empty state if no plans
103+
if (!loading && plans.length === 0) {
104+
return (
105+
<div className="plan-list-empty">
106+
<Card>
107+
<div className="empty-content">
108+
<Calendar24Regular className="empty-icon" />
109+
<Text size={500} weight="semibold">
110+
{emptyMessage}
111+
</Text>
112+
</div>
113+
</Card>
114+
</div>
115+
);
116+
}
117+
118+
return (
119+
<div className="plan-list-container">
120+
<div className="plan-list-grid">
121+
{plans.map((plan) => {
122+
const completionPercentage = apiService.getPlanCompletionPercentage(plan);
123+
const stepsAwaitingFeedback = apiService.getStepsAwaitingFeedback(plan);
124+
125+
return (
126+
<Card key={plan.id} className="plan-list-card">
127+
<CardHeader
128+
header={
129+
<div className="plan-card-header">
130+
<Text className="plan-goal" size={500} weight="semibold">
131+
{plan.initial_goal}
132+
</Text>
133+
<div className="plan-badges">
134+
<Badge
135+
className="status-badge"
136+
color={getPlanStatusColor(plan.overall_status)}
137+
appearance="filled"
138+
icon={getPlanStatusIcon(plan.overall_status)}
139+
>
140+
{getStatusText(plan.overall_status)}
141+
</Badge>
142+
{stepsAwaitingFeedback.length > 0 && (
143+
<Badge
144+
className="feedback-badge"
145+
color="warning"
146+
appearance="filled"
147+
>
148+
{stepsAwaitingFeedback.length} awaiting feedback
149+
</Badge>
150+
)}
151+
</div>
152+
</div>
153+
}
154+
/>
155+
156+
<div className="plan-card-content">
157+
{plan.summary && (
158+
<Text className="plan-summary">
159+
{plan.summary}
160+
</Text>
161+
)}
162+
163+
<div className="plan-progress">
164+
<div className="progress-info">
165+
<Text size={300}>
166+
Progress: {plan.completed} of {plan.total_steps} steps completed
167+
</Text>
168+
<Text size={300} weight="semibold">
169+
{completionPercentage}%
170+
</Text>
171+
</div>
172+
<div className="progress-bar">
173+
<div
174+
className="progress-fill"
175+
style={{ width: `${completionPercentage}%` }}
176+
/>
177+
</div>
178+
</div>
179+
180+
<div className="plan-metadata">
181+
<Text size={200} className="plan-session">
182+
Session: {plan.session_id}
183+
</Text>
184+
<Text size={200} className="plan-date">
185+
{formatDate(plan.timestamp)}
186+
</Text>
187+
</div>
188+
189+
{showViewButton && (
190+
<div className="plan-actions">
191+
<Button
192+
appearance="primary"
193+
icon={<Eye24Regular />}
194+
onClick={() => handleViewPlan(plan.id)}
195+
>
196+
View Details
197+
</Button>
198+
</div>
199+
)}
200+
</div>
201+
</Card>
202+
);
203+
})}
204+
</div>
205+
</div>
206+
);
207+
};
208+
209+
export default PlanList;

src/frontend_react/src/index.tsx

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,14 +3,15 @@ import ReactDOM from 'react-dom/client';
33
import './index.css';
44
import App from './App';
55
import reportWebVitals from './reportWebVitals';
6+
import { FluentProvider } from '@fluentui/react-components';
67

78
const root = ReactDOM.createRoot(
89
document.getElementById('root') as HTMLElement
910
);
1011
root.render(
11-
<React.StrictMode>
12+
<FluentProvider style={{ height: "100vh" }}>
1213
<App />
13-
</React.StrictMode>
14+
</FluentProvider>
1415
);
1516

1617
// If you want to start measuring performance in your app, pass a function

0 commit comments

Comments
 (0)