Skip to content

Commit 694ee38

Browse files
Added compatibility with parent tasks in student dashboard.
1 parent 12c90d8 commit 694ee38

9 files changed

Lines changed: 345 additions & 128 deletions

File tree

src/actions/intermediateTasks.js

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -141,10 +141,15 @@ export const deleteIntermediateTask = (id, parentTaskId = null) => {
141141
/**
142142
* Mark an intermediate task as done (for students)
143143
*/
144-
export const markIntermediateTaskAsDone = (id) => {
144+
export const markIntermediateTaskAsDone = (id, parentTaskId) => {
145145
return async (dispatch) => {
146146
try {
147+
// First, fetch the current task data
148+
const currentTask = await httpService.get(ENDPOINTS.INTERMEDIATE_TASK_BY_ID(id));
149+
150+
// Update with the completed status while preserving all required fields
147151
const response = await httpService.put(ENDPOINTS.INTERMEDIATE_TASK_BY_ID(id), {
152+
...currentTask.data,
148153
status: 'completed'
149154
});
150155
toast.success('Sub-task marked as done');

src/components/EductionPortal/IntermediateTasks/IntermediateTaskForm.jsx

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,11 @@ const IntermediateTaskForm = ({ task, onSubmit, onCancel }) => {
6666
status: formData.status,
6767
};
6868

69+
// Only set logged_hours to 0 when creating a new task (not editing)
70+
if (!task) {
71+
submitData.loggedHours = 0;
72+
}
73+
6974
// Only include dueDate if it's set
7075
if (formData.dueDate) {
7176
submitData.dueDate = new Date(formData.dueDate).toISOString();
@@ -145,7 +150,7 @@ const IntermediateTaskForm = ({ task, onSubmit, onCancel }) => {
145150
>
146151
<option value="pending">Pending</option>
147152
<option value="in_progress">In Progress</option>
148-
<option value="completed">Completed</option>
153+
{task && <option value="completed">Completed</option>}
149154
</Input>
150155
</FormGroup>
151156
</ModalBody>

src/components/EductionPortal/StudentDashboard/StudentDashboard.jsx

Lines changed: 26 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,31 @@ const StudentDashboard = () => {
3030
dispatch(fetchStudentTasks());
3131
}, [dispatch]);
3232

33+
// Fetch intermediate tasks for all parent tasks
34+
useEffect(() => {
35+
const fetchAllIntermediateTasks = async () => {
36+
if (tasks && tasks.length > 0) {
37+
const intermediateTasksData = {};
38+
39+
// Fetch intermediate tasks for each parent task
40+
for (const task of tasks) {
41+
try {
42+
const subTasks = await dispatch(fetchIntermediateTasks(task.id));
43+
if (subTasks && subTasks.length > 0) {
44+
intermediateTasksData[task.id] = subTasks;
45+
}
46+
} catch (error) {
47+
console.error(`Error fetching intermediate tasks for task ${task.id}:`, error);
48+
}
49+
}
50+
51+
setIntermediateTasks(intermediateTasksData);
52+
}
53+
};
54+
55+
fetchAllIntermediateTasks();
56+
}, [tasks, dispatch]);
57+
3358
// Calculate summary data when tasks change
3459
useEffect(() => {
3560
if (tasks && tasks.length > 0) {
@@ -91,16 +116,7 @@ const StudentDashboard = () => {
91116
const toggleIntermediateTasks = async taskId => {
92117
const isExpanded = expandedTasks[taskId];
93118

94-
if (!isExpanded && !intermediateTasks[taskId]) {
95-
// Fetch intermediate tasks when expanding
96-
try {
97-
const tasks = await dispatch(fetchIntermediateTasks(taskId));
98-
setIntermediateTasks(prev => ({ ...prev, [taskId]: tasks || [] }));
99-
} catch (error) {
100-
console.error('Error fetching intermediate tasks:', error);
101-
}
102-
}
103-
119+
// Just toggle the expanded state (tasks are already loaded)
104120
setExpandedTasks(prev => ({
105121
...prev,
106122
[taskId]: !isExpanded,

src/components/EductionPortal/StudentDashboard/TaskCard.jsx

Lines changed: 66 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import React from 'react';
22
import styles from './TaskCard.module.css';
33
import { useTaskLogic } from './useTaskLogic';
4+
import { canMarkIntermediateTaskAsDone, getMarkIntermediateAsDoneTooltip } from './taskUtils';
45

56
const TaskCard = ({
67
task,
@@ -94,7 +95,9 @@ const TaskCard = ({
9495
</svg>
9596
</button>
9697

97-
{task.is_completed ? (
98+
{task.is_completed ||
99+
(intermediateTasks.length > 0 &&
100+
intermediateTasks.every(t => t.status === 'completed')) ? (
98101
<button className={styles.completedButton} disabled>
99102
<svg
100103
width="16"
@@ -159,52 +162,72 @@ const TaskCard = ({
159162
{intermediateTasks.length === 0 ? (
160163
<p className={styles.noIntermediateTasks}>No sub-tasks available</p>
161164
) : (
162-
intermediateTasks.map(subTask => (
163-
<div key={subTask._id || subTask.id} className={styles.intermediateTaskItem}>
164-
<div className={styles.intermediateTaskContent}>
165-
<h4 className={styles.intermediateTaskTitle}>{subTask.title}</h4>
166-
{subTask.description && (
167-
<p className={styles.intermediateTaskDescription}>
168-
{subTask.description}
169-
</p>
170-
)}
171-
<div className={styles.intermediateTaskMeta}>
172-
<span className={styles.intermediateTaskHours}>
173-
{subTask.expected_hours || 0}h
174-
</span>
175-
{subTask.due_date && (
176-
<span className={styles.intermediateTaskDueDate}>
177-
Due: {new Date(subTask.due_date).toLocaleDateString()}
178-
</span>
165+
intermediateTasks.map(subTask => {
166+
const subTaskProgress = subTask.status === 'completed' ? 100 : 0;
167+
const canMarkIntermediateDone = canMarkIntermediateTaskAsDone(subTask);
168+
const intermediateTooltip = getMarkIntermediateAsDoneTooltip(subTask);
169+
170+
return (
171+
<div key={subTask._id || subTask.id} className={styles.intermediateTaskItem}>
172+
<div className={styles.intermediateTaskContent}>
173+
<h4 className={styles.intermediateTaskTitle}>{subTask.title}</h4>
174+
{subTask.description && (
175+
<p className={styles.intermediateTaskDescription}>
176+
{subTask.description}
177+
</p>
179178
)}
180-
<span
181-
className={`${styles.intermediateTaskStatus} ${
182-
styles[`status${subTask.status}`]
179+
{/* Progress Bar for Sub-task */}
180+
<div className={styles.subTaskProgressSection}>
181+
<div className={styles.subTaskProgressBar}>
182+
<div
183+
className={styles.subTaskProgressFill}
184+
style={{ width: `${subTaskProgress}%` }}
185+
/>
186+
</div>
187+
<span className={styles.subTaskProgressText}>{subTaskProgress}%</span>
188+
</div>
189+
<div className={styles.intermediateTaskMeta}>
190+
<span className={styles.intermediateTaskHours}>
191+
{subTask.logged_hours || 0} / {subTask.expected_hours || 0}h
192+
</span>
193+
{subTask.due_date && (
194+
<span className={styles.intermediateTaskDueDate}>
195+
Due: {new Date(subTask.due_date).toLocaleDateString()}
196+
</span>
197+
)}
198+
<span
199+
className={`${styles.intermediateTaskStatus} ${
200+
styles[`status${subTask.status}`]
201+
}`}
202+
>
203+
{subTask.status || 'pending'}
204+
</span>
205+
</div>
206+
</div>
207+
{subTask.status !== 'completed' && (
208+
<button
209+
className={`${styles.markIntermediateDoneButton} ${
210+
!canMarkIntermediateDone ? styles.disabled : ''
183211
}`}
212+
onClick={() => handleMarkIntermediateAsDone(subTask._id || subTask.id)}
213+
disabled={!canMarkIntermediateDone}
214+
title={intermediateTooltip}
184215
>
185-
{subTask.status || 'pending'}
186-
</span>
187-
</div>
216+
<svg
217+
width="16"
218+
height="16"
219+
viewBox="0 0 24 24"
220+
fill="none"
221+
stroke="currentColor"
222+
strokeWidth="2"
223+
>
224+
<polyline points="20,6 9,17 4,12" />
225+
</svg>
226+
</button>
227+
)}
188228
</div>
189-
{subTask.status !== 'completed' && (
190-
<button
191-
className={styles.markIntermediateDoneButton}
192-
onClick={() => handleMarkIntermediateAsDone(subTask._id || subTask.id)}
193-
>
194-
<svg
195-
width="16"
196-
height="16"
197-
viewBox="0 0 24 24"
198-
fill="none"
199-
stroke="currentColor"
200-
strokeWidth="2"
201-
>
202-
<polyline points="20,6 9,17 4,12" />
203-
</svg>
204-
</button>
205-
)}
206-
</div>
207-
))
229+
);
230+
})
208231
)}
209232
</div>
210233
)}

src/components/EductionPortal/StudentDashboard/TaskCard.module.css

Lines changed: 45 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -262,6 +262,36 @@
262262
line-height: 1.4;
263263
}
264264

265+
.subTaskProgressSection {
266+
display: flex;
267+
align-items: center;
268+
gap: 0.5rem;
269+
margin-bottom: 0.5rem;
270+
}
271+
272+
.subTaskProgressBar {
273+
flex: 1;
274+
height: 6px;
275+
background-color: #e5e7eb;
276+
border-radius: 3px;
277+
overflow: hidden;
278+
}
279+
280+
.subTaskProgressFill {
281+
height: 100%;
282+
background-color: #10b981;
283+
border-radius: 3px;
284+
transition: width 0.3s ease;
285+
}
286+
287+
.subTaskProgressText {
288+
font-size: 0.75rem;
289+
font-weight: 600;
290+
color: #6b7280;
291+
min-width: 35px;
292+
text-align: right;
293+
}
294+
265295
.intermediateTaskMeta {
266296
display: flex;
267297
flex-wrap: wrap;
@@ -308,12 +338,26 @@
308338
flex-shrink: 0;
309339
}
310340

311-
.markIntermediateDoneButton:hover {
341+
.markIntermediateDoneButton:hover:not(.disabled) {
312342
background-color: #10b981;
313343
border-color: #10b981;
314344
color: #ffffff;
315345
}
316346

347+
.markIntermediateDoneButton.disabled {
348+
background-color: #f3f4f6;
349+
border-color: #e5e7eb;
350+
color: #d1d5db;
351+
cursor: not-allowed;
352+
opacity: 0.6;
353+
}
354+
355+
.markIntermediateDoneButton.disabled:hover {
356+
background-color: #f3f4f6;
357+
border-color: #e5e7eb;
358+
color: #d1d5db;
359+
}
360+
317361
/* Responsive Design */
318362
@media (max-width: 768px) {
319363
.taskCard {

0 commit comments

Comments
 (0)