feat(course): reset student course progress (#671)
* feat(course): add reset student course progress action Give course admins and tutors a destructive reset-progress flow on the student people detail page. The backend clears learner progress for one (courseId, memberId) pair in a single transaction while keeping the groupmember enrolled, and logs an audit summary. The dashboard shows a confirmation dialog with impact counts from analytics before deleting. Co-authored-by: Rotimi Best <rotimi-best@users.noreply.github.com> * fix(i18n): add localized reset progress strings for all locales Co-authored-by: Rotimi Best <rotimi-best@users.noreply.github.com> * fix(course): scope reset progress to course data only Stop deleting org-level community questions and answers during course progress reset. Fix course newsfeed deletion order so comments on a student's posts are removed before the posts themselves. Restrict reset to student members, move analytics loading to the person detail layout, and rename impact copy to course newsfeed only. Co-authored-by: Rotimi Best <rotimi-best@users.noreply.github.com> * fix(course): address Greptile review on reset progress Make progressImpact opt-in via includeProgressImpact query param so analytics requests avoid extra DB work unless the reset dialog needs it. Count only student-authored course newsfeed posts and comments in the impact summary to avoid double-counting replies on the student's posts. Co-authored-by: Rotimi Best <rotimi-best@users.noreply.github.com> --------- Co-authored-by: Cursor Agent <cursoragent@cursor.com> Co-authored-by: Rotimi Best <rotimi-best@users.noreply.github.com>
This commit is contained in:
co-authored by
Rotimi Best
Cursor Agent
parent
e43d8a2444
commit
a695c97eda
@@ -468,3 +468,12 @@ export const ZCourseUserAnalyticsParam = z.object({
|
||||
userId: z.string().uuid()
|
||||
});
|
||||
export type TCourseUserAnalyticsParam = z.infer<typeof ZCourseUserAnalyticsParam>;
|
||||
|
||||
export const ZCourseUserAnalyticsQuery = z.object({
|
||||
includeProgressImpact: z
|
||||
.enum(['true', 'false'])
|
||||
.optional()
|
||||
.default('false')
|
||||
.transform((value) => value === 'true')
|
||||
});
|
||||
export type TCourseUserAnalyticsQuery = z.infer<typeof ZCourseUserAnalyticsQuery>;
|
||||
|
||||
@@ -26,3 +26,9 @@ export const ZUpdateCourseMember = z.object({
|
||||
email: z.email().optional()
|
||||
});
|
||||
export type TUpdateCourseMember = z.infer<typeof ZUpdateCourseMember>;
|
||||
|
||||
export const ZResetCourseMemberProgressParam = z.object({
|
||||
courseId: z.string().uuid(),
|
||||
memberId: z.string().uuid()
|
||||
});
|
||||
export type TResetCourseMemberProgressParam = z.infer<typeof ZResetCourseMemberProgressParam>;
|
||||
|
||||
Reference in New Issue
Block a user