Skip to content

Commit 6bae550

Browse files
committed
create task service layer to convert the api resutls
1 parent 12ffa58 commit 6bae550

File tree

3 files changed

+290
-40
lines changed

3 files changed

+290
-40
lines changed

src/frontend_react/src/pages/HomePage.tsx

Lines changed: 207 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -1,68 +1,235 @@
1-
import React from 'react';
2-
import { useParams, useNavigate } from 'react-router-dom';
1+
import React, { useEffect, useState, useCallback } from 'react';
2+
import { useNavigate } from 'react-router-dom';
33
import {
44
Button,
55
Text,
6-
Card,
7-
CardHeader
6+
Spinner,
7+
Toast,
8+
ToastTitle,
9+
ToastBody,
10+
useToastController,
11+
Toaster
812
} from '@fluentui/react-components';
913
import {
1014
Add20Regular,
11-
ArrowLeft24Regular,
12-
ErrorCircle24Regular
15+
CheckmarkCircle20Regular,
16+
ErrorCircle20Regular
1317
} from '@fluentui/react-icons';
14-
import PlanView from '../components/PlanView';
1518
import '../styles/PlanPage.css';
1619
import CoralShellColumn from '../coral/components/Layout/CoralShellColumn';
1720
import CoralShellRow from '../coral/components/Layout/CoralShellRow';
1821
import Content from '../coral/components/Content/Content';
1922
import PanelLeft from '../coral/components/Panels/PanelLeft';
2023
import PanelLeftToolbar from '../coral/components/Panels/PanelLeftToolbar';
2124
import TaskList from '../components/content/TaskList';
25+
import { useGetPlans } from '../hooks';
26+
import { PlanWithSteps, PlanStatus } from '../models';
27+
import { Task } from '../models/taskList';
28+
import { apiService } from '../api/apiService';
29+
import { TaskService } from '../services';
2230

2331
/**
24-
* Page component for displaying a specific plan
25-
* Accessible via the route /plan/{plan_id}
32+
* HomePage component - displays task lists and provides navigation
33+
* Accessible via the route "/"
2634
*/
2735
const HomePage: React.FC = () => {
28-
const { planId } = useParams<{ planId: string }>();
2936
const navigate = useNavigate();
37+
const { dispatchToast } = useToastController('toast');
3038

31-
// Handle back navigation
32-
const handleBackClick = () => {
33-
navigate(-1);
34-
};
39+
// State for task lists
40+
const [inProgressTasks, setInProgressTasks] = useState<Task[]>([]);
41+
const [completedTasks, setCompletedTasks] = useState<Task[]>([]);
3542

36-
// Show error if no planId is provided
43+
// API hook for fetching plans
44+
const {
45+
data: plans,
46+
loading: plansLoading,
47+
error: plansError,
48+
execute: fetchPlans, } = useGetPlans();
3749

50+
/**
51+
* Load plans data and update task lists
52+
*/
53+
const loadPlansData = useCallback(async (showToast = false) => {
54+
try {
55+
await fetchPlans();
56+
if (showToast) {
57+
dispatchToast(
58+
<Toast>
59+
<ToastTitle>
60+
<CheckmarkCircle20Regular />
61+
Tasks refreshed successfully
62+
</ToastTitle>
63+
</Toast>,
64+
{ intent: 'success' }
65+
);
66+
}
67+
} catch (error) {
68+
console.error('Failed to load plans:', error);
69+
if (showToast) {
70+
dispatchToast(
71+
<Toast>
72+
<ToastTitle>
73+
<ErrorCircle20Regular />
74+
Failed to refresh tasks
75+
</ToastTitle>
76+
<ToastBody>
77+
{error instanceof Error ? error.message : 'An unknown error occurred'}
78+
</ToastBody>
79+
</Toast>,
80+
{ intent: 'error' }
81+
);
82+
}
83+
}
84+
}, [fetchPlans, dispatchToast]);
85+
86+
/**
87+
* Handle new task creation - placeholder for future implementation
88+
*/
89+
const handleNewTask = useCallback((taskName: string) => {
90+
console.log('Creating new task:', taskName);
91+
// TODO: Implement task creation functionality
92+
// This would typically involve:
93+
// 1. Opening a modal or navigation to task creation form
94+
// 2. Calling apiService.submitInputTask() with user input
95+
// 3. Refreshing the task lists after creation
96+
97+
dispatchToast(
98+
<Toast>
99+
<ToastTitle>New Task</ToastTitle>
100+
<ToastBody>Task creation functionality coming soon</ToastBody>
101+
</Toast>,
102+
{ intent: 'info' }
103+
);
104+
}, [dispatchToast]);
105+
106+
/**
107+
* Handle task selection - navigate to task details
108+
*/
109+
const handleTaskSelect = useCallback((taskId: string) => {
110+
console.log('Selected task ID:', taskId);
111+
112+
// Find the plan by session_id to get the plan_id
113+
const selectedPlan = plans?.find(plan => plan.session_id === taskId);
114+
if (selectedPlan) {
115+
navigate(`/plan/${selectedPlan.id}`);
116+
} else {
117+
dispatchToast(
118+
<Toast>
119+
<ToastTitle>
120+
<ErrorCircle20Regular />
121+
Task not found
122+
</ToastTitle>
123+
<ToastBody>Unable to locate the selected task</ToastBody>
124+
</Toast>,
125+
{ intent: 'error' }
126+
);
127+
}
128+
}, [plans, navigate, dispatchToast]); // Transform plans data when it changes
129+
useEffect(() => {
130+
if (plans) {
131+
const { inProgress, completed } = TaskService.transformPlansToTasks(plans);
132+
setInProgressTasks(inProgress);
133+
setCompletedTasks(completed);
134+
}
135+
}, [plans]);
136+
137+
// Initial data load
138+
useEffect(() => {
139+
loadPlansData();
140+
}, [loadPlansData]);
141+
142+
// Handle API errors
143+
useEffect(() => {
144+
if (plansError) {
145+
dispatchToast(
146+
<Toast>
147+
<ToastTitle>
148+
<ErrorCircle20Regular />
149+
Failed to load tasks
150+
</ToastTitle>
151+
<ToastBody>
152+
{plansError.message}
153+
</ToastBody>
154+
</Toast>,
155+
{ intent: 'error' }
156+
);
157+
}
158+
}, [plansError, dispatchToast]);
38159

39160
return (
40-
<CoralShellColumn>
41-
<CoralShellRow>
42-
<div style={{ flexShrink: 0, display: "flex", overflow: "hidden" }}>
43-
<PanelLeft
44-
panelWidth={280}
45-
panelResize={true}>
46-
<PanelLeftToolbar panelTitle="" panelIcon={null}>
47-
<Button
48-
icon={<Add20Regular />}
49-
onClick={() => handleNewTask("New task")}
50-
>
51-
New task
52-
</Button>
53-
</PanelLeftToolbar>
54-
<TaskList
55-
inProgressTasks={inProgressTasks}
56-
completedTasks={completedTasks}
57-
onTaskSelect={handleTaskSelect}
58-
/>
59-
</PanelLeft>
60-
<Content>
161+
<>
162+
<Toaster toasterId="toast" />
163+
<CoralShellColumn>
164+
<CoralShellRow>
165+
<div style={{ flexShrink: 0, display: "flex", overflow: "hidden" }}>
166+
<PanelLeft
167+
panelWidth={280}
168+
panelResize={true}>
169+
<PanelLeftToolbar panelTitle="Tasks" panelIcon={null}>
170+
<Button
171+
icon={<Add20Regular />}
172+
onClick={() => handleNewTask("New task")}
173+
disabled={plansLoading}
174+
>
175+
New task
176+
</Button>
177+
</PanelLeftToolbar>
178+
{plansLoading && (!inProgressTasks.length && !completedTasks.length) ? (
179+
<div style={{ padding: '20px', textAlign: 'center' }}>
180+
<Spinner size="medium" label="Loading tasks..." />
181+
</div>
182+
) : (
183+
<TaskList
184+
inProgressTasks={inProgressTasks}
185+
completedTasks={completedTasks}
186+
onTaskSelect={handleTaskSelect}
187+
/>
188+
)}
189+
</PanelLeft>
190+
<Content>
191+
<div style={{ padding: '20px' }}>
192+
<Text size={600} weight="semibold">
193+
Welcome to MACAE
194+
</Text>
195+
<Text as="p" style={{ marginTop: '10px' }}>
196+
Select a task from the sidebar to view its details, or create a new task to get started.
197+
</Text>
198+
199+
{/* Task statistics */}
200+
<div style={{ marginTop: '20px', display: 'flex', gap: '20px' }}>
201+
<div>
202+
<Text size={400} weight="semibold">
203+
{inProgressTasks.length}
204+
</Text>
205+
<Text as="p" size={200}>
206+
In Progress
207+
</Text>
208+
</div>
209+
<div>
210+
<Text size={400} weight="semibold">
211+
{completedTasks.length}
212+
</Text>
213+
<Text as="p" size={200}>
214+
Completed
215+
</Text>
216+
</div>
217+
</div>
61218

62-
</Content>
63-
</div>
64-
</CoralShellRow>
65-
</CoralShellColumn>
219+
{/* Refresh button */}
220+
<Button
221+
style={{ marginTop: '20px' }}
222+
disabled={plansLoading}
223+
onClick={() => loadPlansData(true)}
224+
>
225+
{plansLoading ? <Spinner size="tiny" /> : 'Refresh Tasks'}
226+
</Button>
227+
</div>
228+
</Content>
229+
</div>
230+
</CoralShellRow>
231+
</CoralShellColumn>
232+
</>
66233
);
67234
};
68235

Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
1+
import { PlanWithSteps, PlanStatus } from '../models';
2+
import { Task } from '../models/taskList';
3+
import { apiService } from '../api/apiService';
4+
5+
/**
6+
* TaskService - Service for handling task-related operations and transformations
7+
*/
8+
export class TaskService {
9+
/**
10+
* Transform PlanWithSteps data into Task arrays for TaskList component
11+
* @param plansData Array of PlanWithSteps to transform
12+
* @returns Object containing inProgress and completed task arrays
13+
*/
14+
static transformPlansToTasks(plansData: PlanWithSteps[]): { inProgress: Task[], completed: Task[] } {
15+
if (!plansData || plansData.length === 0) {
16+
return { inProgress: [], completed: [] };
17+
}
18+
19+
const inProgress: Task[] = [];
20+
const completed: Task[] = [];
21+
22+
plansData.forEach((plan) => {
23+
const task: Task = {
24+
id: plan.session_id,
25+
name: plan.initial_goal,
26+
status: apiService.isPlanComplete(plan) ? 'completed' : 'inprogress',
27+
date: new Date(plan.timestamp).toLocaleDateString('en-US', {
28+
month: 'short',
29+
day: 'numeric',
30+
hour: '2-digit',
31+
minute: '2-digit'
32+
})
33+
};
34+
35+
// Categorize based on plan status and completion
36+
if (plan.overall_status === PlanStatus.COMPLETED || apiService.isPlanComplete(plan)) {
37+
completed.push(task);
38+
} else {
39+
inProgress.push(task);
40+
}
41+
});
42+
43+
return { inProgress, completed };
44+
}
45+
46+
/**
47+
* Get task statistics from task arrays
48+
* @param inProgressTasks Array of in-progress tasks
49+
* @param completedTasks Array of completed tasks
50+
* @returns Object containing task count statistics
51+
*/
52+
static getTaskStatistics(inProgressTasks: Task[], completedTasks: Task[]) {
53+
return {
54+
inProgressCount: inProgressTasks.length,
55+
completedCount: completedTasks.length,
56+
totalCount: inProgressTasks.length + completedTasks.length
57+
};
58+
}
59+
60+
/**
61+
* Find a task by ID in either task array
62+
* @param taskId The task ID to search for
63+
* @param inProgressTasks Array of in-progress tasks
64+
* @param completedTasks Array of completed tasks
65+
* @returns The found task or undefined
66+
*/
67+
static findTaskById(taskId: string, inProgressTasks: Task[], completedTasks: Task[]): Task | undefined {
68+
return [...inProgressTasks, ...completedTasks].find(task => task.id === taskId);
69+
}
70+
71+
/**
72+
* Filter tasks by status
73+
* @param tasks Array of tasks to filter
74+
* @param status Status to filter by
75+
* @returns Filtered array of tasks
76+
*/
77+
static filterTasksByStatus(tasks: Task[], status: 'inprogress' | 'completed'): Task[] {
78+
return tasks.filter(task => task.status === status);
79+
}
80+
}
81+
82+
export default TaskService;
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
export { default as TaskService } from './TaskService';

0 commit comments

Comments
 (0)