Skip to content

Commit f9943be

Browse files
committed
add steplist
1 parent 0af3eb5 commit f9943be

File tree

2 files changed

+393
-0
lines changed

2 files changed

+393
-0
lines changed
Lines changed: 210 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,210 @@
1+
import React from 'react';
2+
import {
3+
Card,
4+
CardHeader,
5+
Text,
6+
Button,
7+
Badge
8+
} from '@fluentui/react-components';
9+
import {
10+
CheckmarkCircle24Regular,
11+
DismissCircle24Regular,
12+
Clock24Regular
13+
} from '@fluentui/react-icons';
14+
import { Step, StepStatus, AgentType } from '../models';
15+
import '../styles/StepActionList.css';
16+
17+
interface StepActionListProps {
18+
/** Array of steps to display */
19+
steps: Step[];
20+
/** Callback function when approve button is clicked */
21+
onApprove: (stepId: string) => void;
22+
/** Callback function when reject button is clicked */
23+
onReject: (stepId: string) => void;
24+
/** Optional loading state for actions */
25+
loading?: boolean;
26+
/** Optional flag to show only steps awaiting feedback */
27+
showOnlyAwaitingFeedback?: boolean;
28+
/** Optional empty state message */
29+
emptyMessage?: string;
30+
}
31+
32+
/**
33+
* Simple component to display a list of steps with approve/reject actions
34+
*/
35+
const StepActionList: React.FC<StepActionListProps> = ({
36+
steps,
37+
onApprove,
38+
onReject,
39+
loading = false,
40+
showOnlyAwaitingFeedback = false,
41+
emptyMessage = "No steps found"
42+
}) => { /**
43+
* Get badge color based on step status
44+
*/
45+
const getStatusColor = (status: StepStatus): "success" | "brand" | "danger" | "warning" | "informative" => {
46+
switch (status) {
47+
case StepStatus.COMPLETED:
48+
case StepStatus.APPROVED:
49+
return 'success';
50+
case StepStatus.ACTION_REQUESTED:
51+
return 'brand';
52+
case StepStatus.FAILED:
53+
case StepStatus.REJECTED:
54+
return 'danger';
55+
case StepStatus.AWAITING_FEEDBACK:
56+
return 'warning';
57+
default:
58+
return 'informative';
59+
}
60+
};
61+
62+
/**
63+
* Get status text from enum
64+
*/
65+
const getStatusText = (status: StepStatus): string => {
66+
return status.replace('_', ' ');
67+
}; /**
68+
* Get agent type display name
69+
*/
70+
const getAgentName = (agent: AgentType): string => {
71+
switch (agent) {
72+
case AgentType.PLANNER:
73+
return 'Planner';
74+
case AgentType.HUMAN:
75+
return 'Human';
76+
case AgentType.HR:
77+
return 'HR';
78+
case AgentType.MARKETING:
79+
return 'Marketing';
80+
case AgentType.PROCUREMENT:
81+
return 'Procurement';
82+
case AgentType.PRODUCT:
83+
return 'Product';
84+
case AgentType.GENERIC:
85+
return 'Generic';
86+
case AgentType.TECH_SUPPORT:
87+
return 'Tech Support';
88+
case AgentType.GROUP_CHAT_MANAGER:
89+
return 'Group Chat Manager';
90+
default:
91+
return agent;
92+
}
93+
};
94+
95+
/**
96+
* Handle approve action
97+
*/
98+
const handleApprove = (stepId: string) => {
99+
if (!loading) {
100+
onApprove(stepId);
101+
}
102+
};
103+
104+
/**
105+
* Handle reject action
106+
*/
107+
const handleReject = (stepId: string) => {
108+
if (!loading) {
109+
onReject(stepId);
110+
}
111+
};
112+
113+
// Filter steps if needed
114+
const filteredSteps = showOnlyAwaitingFeedback
115+
? steps.filter(step => step.status === StepStatus.AWAITING_FEEDBACK)
116+
: steps;
117+
118+
// Show empty state if no steps
119+
if (filteredSteps.length === 0) {
120+
return (
121+
<div className="step-action-list-empty">
122+
<Card>
123+
<div className="empty-content">
124+
<Clock24Regular className="empty-icon" />
125+
<Text size={500} weight="semibold">
126+
{emptyMessage}
127+
</Text>
128+
</div>
129+
</Card>
130+
</div>
131+
);
132+
}
133+
134+
return (
135+
<div className="step-action-list-container">
136+
<div className="step-action-list">
137+
{filteredSteps.map((step) => (
138+
<Card key={step.id} className="step-action-card">
139+
<CardHeader
140+
header={
141+
<div className="step-action-header">
142+
<Text className="step-action-title" size={400} weight="semibold">
143+
Step {step.id}
144+
</Text>
145+
<div className="step-action-badges">
146+
<Badge
147+
className="status-badge"
148+
color={getStatusColor(step.status)}
149+
appearance="filled"
150+
>
151+
{getStatusText(step.status)}
152+
</Badge>
153+
<Badge
154+
className="agent-badge"
155+
appearance="tint"
156+
>
157+
{getAgentName(step.agent)}
158+
</Badge>
159+
</div>
160+
</div>
161+
}
162+
/>
163+
164+
<div className="step-action-content">
165+
<Text className="step-action-label" weight="semibold">Action:</Text>
166+
<Text className="step-action-text">{step.action}</Text>
167+
168+
{step.agent_reply && (
169+
<>
170+
<Text className="step-reply-label" weight="semibold">Agent Reply:</Text>
171+
<Text className="step-reply-text">{step.agent_reply}</Text>
172+
</>
173+
)}
174+
175+
{step.human_feedback && (
176+
<>
177+
<Text className="step-feedback-label" weight="semibold">Previous Feedback:</Text>
178+
<Text className="step-feedback-text">{step.human_feedback}</Text>
179+
</>
180+
)}
181+
182+
{step.status === StepStatus.AWAITING_FEEDBACK && (
183+
<div className="step-action-buttons">
184+
<Button
185+
appearance="primary"
186+
icon={<CheckmarkCircle24Regular />}
187+
onClick={() => handleApprove(step.id)}
188+
disabled={loading}
189+
>
190+
Approve
191+
</Button>
192+
<Button
193+
appearance="outline"
194+
icon={<DismissCircle24Regular />}
195+
onClick={() => handleReject(step.id)}
196+
disabled={loading}
197+
>
198+
Reject
199+
</Button>
200+
</div>
201+
)}
202+
</div>
203+
</Card>
204+
))}
205+
</div>
206+
</div>
207+
);
208+
};
209+
210+
export default StepActionList;
Lines changed: 183 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,183 @@
1+
/**
2+
* Example usage of the StepActionList component
3+
*
4+
* This file demonstrates how to use the StepActionList component
5+
* in different scenarios within your application.
6+
*/
7+
8+
import React, { useState } from 'react';
9+
import StepActionList from '../components/StepActionList';
10+
import { Step, StepStatus, AgentType } from '../models';
11+
import { apiService } from '../api/apiService';
12+
13+
/**
14+
* Example: Using StepActionList in a plan page
15+
*/
16+
const PlanPageExample: React.FC = () => {
17+
const [steps, setSteps] = useState<Step[]>([]);
18+
const [loading, setLoading] = useState(false);
19+
20+
// Example: Handle approve action
21+
const handleApproveStep = async (stepId: string) => {
22+
setLoading(true);
23+
try {
24+
// Call your API service to approve the step
25+
await apiService.provideStepFeedback(
26+
stepId,
27+
'plan-id', // Replace with actual plan ID
28+
'session-id', // Replace with actual session ID
29+
true // approved = true
30+
);
31+
32+
// Update the local state or refetch data
33+
// This is where you would update multiple components
34+
await refetchPlanData();
35+
} catch (error) {
36+
console.error('Failed to approve step:', error);
37+
} finally {
38+
setLoading(false);
39+
}
40+
};
41+
42+
// Example: Handle reject action
43+
const handleRejectStep = async (stepId: string) => {
44+
setLoading(true);
45+
try {
46+
// Call your API service to reject the step
47+
await apiService.provideStepFeedback(
48+
stepId,
49+
'plan-id', // Replace with actual plan ID
50+
'session-id', // Replace with actual session ID
51+
false // approved = false
52+
);
53+
54+
// Update the local state or refetch data
55+
// This is where you would update multiple components
56+
await refetchPlanData();
57+
} catch (error) {
58+
console.error('Failed to reject step:', error);
59+
} finally {
60+
setLoading(false);
61+
}
62+
};
63+
64+
// Example: Refetch plan data to update all components
65+
const refetchPlanData = async () => {
66+
// This would trigger updates to:
67+
// - Plan status
68+
// - Progress bars
69+
// - Step counts
70+
// - Other plan-related components
71+
console.log('Refetching plan data to update all components');
72+
};
73+
74+
return (
75+
<div>
76+
<h2>Plan Steps Requiring Action</h2>
77+
<StepActionList
78+
steps={steps}
79+
onApprove={handleApproveStep}
80+
onReject={handleRejectStep}
81+
loading={loading}
82+
showOnlyAwaitingFeedback={true}
83+
emptyMessage="No steps are currently awaiting your feedback"
84+
/>
85+
</div>
86+
);
87+
};
88+
89+
/**
90+
* Example: Using StepActionList for all steps (not just awaiting feedback)
91+
*/
92+
const AllStepsExample: React.FC = () => {
93+
const [steps, setSteps] = useState<Step[]>([]);
94+
95+
const handleApprove = async (stepId: string) => {
96+
// Your approval logic here
97+
console.log('Approving step:', stepId);
98+
};
99+
100+
const handleReject = async (stepId: string) => {
101+
// Your rejection logic here
102+
console.log('Rejecting step:', stepId);
103+
};
104+
105+
return (
106+
<div>
107+
<h2>All Plan Steps</h2>
108+
<StepActionList
109+
steps={steps}
110+
onApprove={handleApprove}
111+
onReject={handleReject}
112+
showOnlyAwaitingFeedback={false} // Show all steps
113+
emptyMessage="No steps found for this plan"
114+
/>
115+
</div>
116+
);
117+
};
118+
119+
/**
120+
* Example: Integration with existing plan data
121+
*/
122+
const IntegratedExample: React.FC<{ planId: string; sessionId: string }> = ({
123+
planId,
124+
sessionId
125+
}) => {
126+
const [steps, setSteps] = useState<Step[]>([]);
127+
const [loading, setLoading] = useState(false);
128+
129+
// Example: Load steps from API
130+
React.useEffect(() => {
131+
const loadSteps = async () => {
132+
try {
133+
const planSteps = await apiService.getSteps(planId);
134+
setSteps(planSteps);
135+
} catch (error) {
136+
console.error('Failed to load steps:', error);
137+
}
138+
};
139+
140+
loadSteps();
141+
}, [planId]);
142+
143+
// Integrated action handlers that update multiple components
144+
const handleStepAction = async (stepId: string, approved: boolean) => {
145+
setLoading(true);
146+
try {
147+
await apiService.provideStepFeedback(stepId, planId, sessionId, approved);
148+
149+
// Update local steps state
150+
setSteps(prevSteps =>
151+
prevSteps.map(step =>
152+
step.id === stepId
153+
? { ...step, status: approved ? StepStatus.APPROVED : StepStatus.REJECTED }
154+
: step
155+
)
156+
);
157+
158+
// Trigger updates to other components
159+
// This could be through:
160+
// - Context updates
161+
// - Parent component callbacks
162+
// - Global state management
163+
// - Event emitters
164+
165+
} catch (error) {
166+
console.error('Failed to update step:', error);
167+
} finally {
168+
setLoading(false);
169+
}
170+
};
171+
172+
return (
173+
<StepActionList
174+
steps={steps}
175+
onApprove={(stepId) => handleStepAction(stepId, true)}
176+
onReject={(stepId) => handleStepAction(stepId, false)}
177+
loading={loading}
178+
showOnlyAwaitingFeedback={true}
179+
/>
180+
);
181+
};
182+
183+
export { PlanPageExample, AllStepsExample, IntegratedExample };

0 commit comments

Comments
 (0)