Skip to content

Commit ec70d76

Browse files
authored
Merge pull request #2806 from AkshataKatwal16/release-1.15.0
Issue feat: Refactor logic for course compltetion
2 parents d1e21aa + ce8a88a commit ec70d76

1 file changed

Lines changed: 93 additions & 157 deletions

File tree

mfes/youthNet/src/components/ManagerDashboard/CourseCompletion.tsx

Lines changed: 93 additions & 157 deletions
Original file line numberDiff line numberDiff line change
@@ -9,23 +9,27 @@ import {
99
} from 'recharts';
1010
import { useTheme } from '@mui/material/styles';
1111
import { useTranslation } from 'next-i18next';
12+
1213
interface ChartDataItem {
1314
name: string;
1415
value: number;
1516
color: string;
1617
}
18+
1719
interface CourseStatus {
1820
userId: string;
1921
courseId: string;
2022
status: 'completed' | 'inprogress';
2123
}
24+
2225
interface CourseCompletionProps {
2326
mandatoryCourses: CourseStatus[];
2427
nonMandatoryCourses: CourseStatus[];
2528
userIds?: string[];
2629
mandatoryCourseIds?: string[];
2730
optionalCourseIds?: string[];
2831
}
32+
2933
const CourseCompletion: React.FC<CourseCompletionProps> = ({
3034
mandatoryCourses,
3135
nonMandatoryCourses,
@@ -34,137 +38,98 @@ const CourseCompletion: React.FC<CourseCompletionProps> = ({
3438
optionalCourseIds,
3539
}) => {
3640
const { t } = useTranslation();
37-
console.log('userIds=========>', userIds);
38-
console.log('mandatoryCourseIds=========>', mandatoryCourseIds);
39-
console.log('optionalCourseIds=========>', optionalCourseIds);
40-
console.log('nonMandatoryCoursesCourseCompletion', nonMandatoryCourses);
4141
const theme = useTheme();
4242
const isMobile = useMediaQuery(theme.breakpoints.down('sm'));
43-
const prepareMandatoryData = (): ChartDataItem[] => {
43+
44+
// ✅ FINAL LOGIC WITH INPROGRESS SUPPORT
45+
const calculateUserCompletion = (
46+
courses: CourseStatus[],
47+
courseIds?: string[]
48+
): ChartDataItem[] => {
4449
let completed = 0;
4550
let inProgress = 0;
4651

47-
// Check if we have the required props for validation
48-
if (userIds && mandatoryCourseIds && userIds.length > 0 && mandatoryCourseIds.length > 0) {
49-
// Group courses by courseId to check completion status per course
50-
const coursesByCourseId = new Map<string, Map<string, string>>();
51-
52-
mandatoryCourses.forEach((course) => {
53-
if (!coursesByCourseId.has(course.courseId)) {
54-
coursesByCourseId.set(course.courseId, new Map());
55-
}
56-
const userStatusMap = coursesByCourseId.get(course.courseId);
57-
if (userStatusMap) {
58-
userStatusMap.set(course.userId, course.status);
59-
}
60-
});
61-
62-
// Check each mandatory course
63-
mandatoryCourseIds.forEach((courseId) => {
64-
const userStatusMap = coursesByCourseId.get(courseId);
65-
// Check if all users have completed this course
66-
// A user must have an entry with status 'completed' to be considered completed
67-
const allUsersCompleted = userIds.every((userId) => {
68-
const status = userStatusMap?.get(userId);
69-
return status === 'completed';
70-
});
52+
if (userIds && courseIds && userIds.length > 0 && courseIds.length > 0) {
53+
// user → completed courses
54+
const completedMap = new Map<string, Set<string>>();
55+
// user → any activity (completed OR inprogress)
56+
const activityMap = new Map<string, boolean>();
7157

72-
if (allUsersCompleted) {
73-
completed++;
74-
} else {
75-
// If not all users completed, count as inProgress
76-
// This includes: some users in progress, some not started, or mix of both
77-
inProgress++;
58+
courses.forEach((course) => {
59+
// track activity
60+
if (!activityMap.has(course.userId)) {
61+
activityMap.set(course.userId, false);
7862
}
79-
});
80-
} else {
81-
// Fallback to original logic if props are not available
82-
completed = mandatoryCourses.filter(course => course.status === 'completed').length;
83-
inProgress = mandatoryCourses.filter(course => course.status === 'inprogress').length;
84-
}
85-
86-
return [
87-
{
88-
name: t('COMPLETED'),
89-
value: completed,
90-
color: '#4CAF50',
91-
},
92-
{
93-
name: t('IN_PROGRESS'),
94-
value: inProgress,
95-
color: '#FFC107',
96-
},
97-
];
98-
};
99-
const prepareNonMandatoryData = (): ChartDataItem[] => {
100-
let completed = 0;
101-
let inProgress = 0;
10263

103-
// Check if we have the required props for validation
104-
if (userIds && optionalCourseIds && userIds.length > 0 && optionalCourseIds.length > 0) {
105-
// Group courses by courseId to check completion status per course
106-
const coursesByCourseId = new Map<string, Map<string, string>>();
107-
108-
nonMandatoryCourses.forEach((course) => {
109-
if (!coursesByCourseId.has(course.courseId)) {
110-
coursesByCourseId.set(course.courseId, new Map());
64+
if (course.status === 'completed' || course.status === 'inprogress') {
65+
activityMap.set(course.userId, true);
11166
}
112-
const userStatusMap = coursesByCourseId.get(course.courseId);
113-
if (userStatusMap) {
114-
userStatusMap.set(course.userId, course.status);
67+
68+
// track completed
69+
if (course.status === 'completed') {
70+
if (!completedMap.has(course.userId)) {
71+
completedMap.set(course.userId, new Set());
72+
}
73+
completedMap.get(course.userId)?.add(course.courseId);
11574
}
11675
});
11776

118-
// Check each optional course
119-
optionalCourseIds.forEach((courseId) => {
120-
const userStatusMap = coursesByCourseId.get(courseId);
121-
// Check if all users have completed this course
122-
// A user must have an entry with status 'completed' to be considered completed
123-
const allUsersCompleted = userIds.every((userId) => {
124-
const status = userStatusMap?.get(userId);
125-
return status === 'completed';
126-
});
77+
userIds.forEach((userId) => {
78+
const completedCourses = completedMap.get(userId) || new Set();
79+
const hasActivity = activityMap.get(userId) || false;
80+
81+
const completedCount = courseIds.filter((courseId) =>
82+
completedCourses.has(courseId)
83+
).length;
12784

128-
if (allUsersCompleted) {
85+
if (completedCount === courseIds.length) {
86+
// ✅ all completed
12987
completed++;
130-
} else {
131-
// If not all users completed, count as inProgress
132-
// This includes: some users in progress, some not started, or mix of both
88+
} else if (hasActivity) {
89+
// ⏳ has at least one completed OR inprogress
13390
inProgress++;
13491
}
92+
// ❌ no activity → ignore
13593
});
94+
13695
} else {
137-
// Fallback to original logic if props are not available
138-
completed = nonMandatoryCourses.filter(course => course.status === 'completed').length;
139-
inProgress = nonMandatoryCourses.filter(course => course.status === 'inprogress').length;
96+
// ✅ FALLBACK LOGIC
97+
completed = courses.filter(c => c.status === 'completed').length;
98+
inProgress = courses.filter(c => c.status === 'inprogress').length;
14099
}
141100

142101
return [
143-
{
144-
name: t('COMPLETED'),
145-
value: completed,
146-
color: '#4CAF50',
147-
},
148-
{
149-
name: t('IN_PROGRESS'),
150-
value: inProgress,
151-
color: '#FFC107',
152-
},
102+
{ name: t('COMPLETED'), value: completed, color: '#4CAF50' },
103+
{ name: t('IN_PROGRESS'), value: inProgress, color: '#FFC107' },
153104
];
154105
};
106+
107+
const prepareMandatoryData = () =>
108+
calculateUserCompletion(mandatoryCourses, mandatoryCourseIds);
109+
110+
const prepareNonMandatoryData = () =>
111+
calculateUserCompletion(nonMandatoryCourses, optionalCourseIds);
112+
155113
const renderDonutChart = (data: ChartDataItem[], title: string) => {
156114
const backgroundData = [{ value: 100 }];
157-
// Responsive radius values: smaller for mobile, original for desktop
158115
const innerRadius = isMobile ? 38 : 48;
159116
const outerRadius = isMobile ? 55 : 68;
160-
117+
161118
return (
162-
<Box sx={{ flex: 1, display: 'flex', flexDirection: 'column', minWidth: 0, maxWidth: '100%' }}>
163-
<Typography variant="body2" fontWeight={500} color="text.secondary" gutterBottom sx={{ mb: { xs: 1, sm: 2 }, fontSize: { xs: '0.875rem', sm: '0.875rem' }, flexShrink: 0 }}>
119+
<Box sx={{ flex: 1, display: 'flex', flexDirection: 'column', minWidth: 0 }}>
120+
<Typography variant="body2" fontWeight={500} color="text.secondary" gutterBottom>
164121
{title}
165122
</Typography>
166-
<Box sx={{ display: 'flex', flexDirection: { xs: 'column', sm: 'row', lg: 'row' }, alignItems: 'center', gap: { xs: 1.5, sm: 2, lg: 1.5 }, width: '100%', minWidth: 0, flex: 1 }}>
167-
<Box sx={{ position: 'relative', height: { xs: 120, sm: 150, lg: 130, xl: 140 }, width: { xs: 120, sm: 150, lg: 130, xl: 140 }, flexShrink: 0 }}>
123+
124+
<Box
125+
sx={{
126+
display: 'flex',
127+
flexDirection: { xs: 'column', sm: 'row' },
128+
alignItems: 'center',
129+
gap: 2,
130+
}}
131+
>
132+
<Box sx={{ position: 'relative', height: 140, width: 140 }}>
168133
<ResponsiveContainer width="100%" height="100%">
169134
<PieChart>
170135
<Pie
@@ -174,8 +139,6 @@ const CourseCompletion: React.FC<CourseCompletionProps> = ({
174139
innerRadius={innerRadius}
175140
outerRadius={outerRadius}
176141
dataKey="value"
177-
startAngle={0}
178-
endAngle={360}
179142
fill="#E0E0E0"
180143
/>
181144
<Pie
@@ -189,54 +152,26 @@ const CourseCompletion: React.FC<CourseCompletionProps> = ({
189152
endAngle={-270}
190153
>
191154
{data.map((entry, index) => (
192-
<Cell key={`cell-${index}`} fill={entry.color} />
155+
<Cell key={index} fill={entry.color} />
193156
))}
194157
</Pie>
195158
<Tooltip />
196159
</PieChart>
197160
</ResponsiveContainer>
198-
<Box
199-
sx={{
200-
position: 'absolute',
201-
top: '50%',
202-
left: '50%',
203-
transform: 'translate(-50%, -50%)',
204-
textAlign: 'center',
205-
width: { xs: '60px', sm: '80px' },
206-
}}
207-
>
208-
{/* <Typography variant="caption" color="text.secondary" sx={{ fontSize: '9px', textTransform: 'uppercase', lineHeight: 1.2 }}>
209-
NO. OF EMPLOYEES
210-
</Typography> */}
211-
</Box>
212161
</Box>
213-
<Stack spacing={1} sx={{ flex: 1, minWidth: 0 }}>
162+
163+
<Stack spacing={1}>
214164
{data.map((item, index) => (
215-
<Stack
216-
key={index}
217-
direction="row"
218-
alignItems="center"
219-
spacing={1}
220-
sx={{ minWidth: 0 }}
221-
>
165+
<Stack key={index} direction="row" alignItems="center" spacing={1}>
222166
<Box
223167
sx={{
224168
width: 10,
225169
height: 10,
226170
borderRadius: '50%',
227171
backgroundColor: item.color,
228-
flexShrink: 0,
229172
}}
230173
/>
231-
<Typography
232-
variant="body2"
233-
sx={{
234-
color: theme.palette.primary.main,
235-
fontSize: { xs: '12px', sm: '13px', lg: '12px', xl: '13px' },
236-
minWidth: 0,
237-
wordBreak: 'break-word'
238-
}}
239-
>
174+
<Typography variant="body2" color="primary">
240175
{item.name} : {item.value}
241176
</Typography>
242177
</Stack>
@@ -246,32 +181,33 @@ const CourseCompletion: React.FC<CourseCompletionProps> = ({
246181
</Box>
247182
);
248183
};
184+
249185
return (
250-
<Paper elevation={0} sx={{ p: { xs: 1.5, sm: 2 }, border: '1px solid #E0E0E0', borderRadius: 2, height: '100%', display: 'flex', flexDirection: 'column', overflowX: 'hidden', overflowY: 'visible' }}>
251-
<Typography variant="subtitle1" fontWeight={600} gutterBottom sx={{ fontSize: { xs: '1rem', sm: '1.125rem' }, flexShrink: 0 }}>
186+
<Paper
187+
elevation={0}
188+
sx={{
189+
p: 2,
190+
border: '1px solid #E0E0E0',
191+
borderRadius: 2,
192+
height: '100%',
193+
}}
194+
>
195+
<Typography variant="subtitle1" fontWeight={600}>
252196
{t('COURSE_COMPLETION')}
253197
</Typography>
254-
<Stack
255-
direction={{ xs: 'column', sm: 'column', lg: 'row', xl: 'row' }}
256-
spacing={{ xs: 2, sm: 2, lg: 2, xl: 2.5 }}
257-
sx={{
258-
mt: 2,
259-
flex: 1,
260-
minHeight: 0,
261-
width: '100%',
262-
alignItems: { xs: 'stretch', sm: 'stretch', lg: 'flex-start', xl: 'flex-start' },
263-
'& > *': {
264-
flex: { lg: '1 1 auto', xl: '1 1 auto' },
265-
minWidth: 0,
266-
maxWidth: { lg: 'calc(50% - 4px)', xl: 'calc(50% - 5px)' }
267-
}
268-
}}
269-
>
270-
{renderDonutChart(prepareMandatoryData(), t('MANDATORY_COURSES'))}
271-
{renderDonutChart(prepareNonMandatoryData(), t('NON_MANDATORY_COURSES'))}
198+
199+
<Stack direction={{ xs: 'column', lg: 'row' }} spacing={2} sx={{ mt: 2 }}>
200+
{renderDonutChart(
201+
prepareMandatoryData(),
202+
t('MANDATORY_COURSES')
203+
)}
204+
{renderDonutChart(
205+
prepareNonMandatoryData(),
206+
t('NON_MANDATORY_COURSES')
207+
)}
272208
</Stack>
273209
</Paper>
274210
);
275211
};
276-
export default CourseCompletion;
277212

213+
export default CourseCompletion;

0 commit comments

Comments
 (0)