fix: replace stale program references (#749)
This commit is contained in:
@@ -26,7 +26,7 @@ export const cohortMemberMiddleware = async (c: Context, next: Next) => {
|
||||
return c.json(
|
||||
{
|
||||
success: false,
|
||||
error: 'Program ID is required',
|
||||
error: 'Cohort ID is required',
|
||||
code: 'COHORT_ID_REQUIRED'
|
||||
},
|
||||
400
|
||||
|
||||
@@ -82,7 +82,7 @@ export const cohortNewsfeedCommentAuthorOrTeamMiddleware = async (c: Context, ne
|
||||
return c.json(
|
||||
{
|
||||
success: false,
|
||||
error: 'Only the author or program team members can perform this action',
|
||||
error: 'Only the author or cohort team members can perform this action',
|
||||
code: ErrorCodes.ORG_TEAM_NOT_AUTHORIZED
|
||||
},
|
||||
403
|
||||
|
||||
@@ -6,7 +6,7 @@ import { ROLE } from '@cio/utils/constants';
|
||||
|
||||
/**
|
||||
* Middleware to check if the authenticated user is a cohort teacher or org admin.
|
||||
* Mirrors the course-team-member pattern for program routes.
|
||||
* Mirrors the course-team-member pattern for cohort routes.
|
||||
*/
|
||||
export const cohortTeamMemberMiddleware = async (c: Context, next: Next) => {
|
||||
try {
|
||||
@@ -27,7 +27,7 @@ export const cohortTeamMemberMiddleware = async (c: Context, next: Next) => {
|
||||
return c.json(
|
||||
{
|
||||
success: false,
|
||||
error: 'Program ID is required',
|
||||
error: 'Cohort ID is required',
|
||||
code: 'COHORT_ID_REQUIRED'
|
||||
},
|
||||
400
|
||||
@@ -56,7 +56,7 @@ export const cohortTeamMemberMiddleware = async (c: Context, next: Next) => {
|
||||
return c.json(
|
||||
{
|
||||
success: false,
|
||||
error: 'Failed to verify program team membership',
|
||||
error: 'Failed to verify cohort team membership',
|
||||
code: 'COHORT_TEAM_MEMBER_CHECK_FAILED'
|
||||
},
|
||||
500
|
||||
|
||||
@@ -82,29 +82,29 @@ export const cohortRouter = new Hono()
|
||||
|
||||
/**
|
||||
* GET /cohort/enrolled
|
||||
* Get programs the current user is enrolled in
|
||||
* Get cohorts the current user is enrolled in
|
||||
*/
|
||||
.get('/enrolled', authMiddleware, async (c) => {
|
||||
try {
|
||||
const user = c.get('user')!;
|
||||
const programs = await getEnrolledCohorts(user.id);
|
||||
return c.json({ success: true, data: programs }, 200);
|
||||
const cohorts = await getEnrolledCohorts(user.id);
|
||||
return c.json({ success: true, data: cohorts }, 200);
|
||||
} catch (error) {
|
||||
return handleError(c, error, 'Failed to get enrolled cohorts');
|
||||
}
|
||||
})
|
||||
|
||||
// ── Programs ──────────────────────────────────────────────────────────────
|
||||
// ── Cohorts ───────────────────────────────────────────────────────────────
|
||||
|
||||
/**
|
||||
* GET /cohort?organizationId=...
|
||||
* List all programs for an org
|
||||
* List all cohorts for an org
|
||||
*/
|
||||
.get('/', authMiddleware, zValidator('query', ZOrgQuery), async (c) => {
|
||||
try {
|
||||
const { organizationId } = c.req.valid('query');
|
||||
const programs = await listOrgCohorts(organizationId);
|
||||
return c.json({ success: true, data: programs }, 200);
|
||||
const cohorts = await listOrgCohorts(organizationId);
|
||||
return c.json({ success: true, data: cohorts }, 200);
|
||||
} catch (error) {
|
||||
return handleError(c, error, 'Failed to list cohorts');
|
||||
}
|
||||
@@ -112,7 +112,7 @@ export const cohortRouter = new Hono()
|
||||
|
||||
/**
|
||||
* POST /cohort
|
||||
* Create a new program
|
||||
* Create a new cohort
|
||||
*/
|
||||
.post(
|
||||
'/',
|
||||
@@ -255,7 +255,7 @@ export const cohortRouter = new Hono()
|
||||
const member = await removeCohortMemberService(cohortId, memberId);
|
||||
return c.json({ success: true, data: member }, 200);
|
||||
} catch (error) {
|
||||
return handleError(c, error, 'Failed to remove program member');
|
||||
return handleError(c, error, 'Failed to remove cohort member');
|
||||
}
|
||||
}
|
||||
)
|
||||
@@ -267,7 +267,7 @@ export const cohortRouter = new Hono()
|
||||
* Invite new students to a cohort by CSV emails. Issues org invites
|
||||
* pre-tagged with this cohort so accept auto-enrolls them.
|
||||
* Gated by cohort-team-member middleware — Cohort ADMINs/TUTORs can
|
||||
* invite into their own program without org-admin rights.
|
||||
* invite into their own cohort without org-admin rights.
|
||||
*/
|
||||
.post(
|
||||
'/:cohortId/invite',
|
||||
@@ -283,7 +283,7 @@ export const cohortRouter = new Hono()
|
||||
const result = await inviteStudentsToCohort(cohortId, data, user.id);
|
||||
return c.json({ success: true, data: result }, 201);
|
||||
} catch (error) {
|
||||
return handleError(c, error, 'Failed to invite students to program');
|
||||
return handleError(c, error, 'Failed to invite students to cohort');
|
||||
}
|
||||
}
|
||||
)
|
||||
@@ -305,7 +305,7 @@ export const cohortRouter = new Hono()
|
||||
const result = await assignExistingStudentsToCohort(cohortId, data);
|
||||
return c.json({ success: true, data: result }, 200);
|
||||
} catch (error) {
|
||||
return handleError(c, error, 'Failed to assign students to program');
|
||||
return handleError(c, error, 'Failed to assign students to cohort');
|
||||
}
|
||||
}
|
||||
)
|
||||
@@ -344,7 +344,7 @@ export const cohortRouter = new Hono()
|
||||
const result = await addCourseToCohortService(cohortId, data);
|
||||
return c.json({ success: true, data: result }, 201);
|
||||
} catch (error) {
|
||||
return handleError(c, error, 'Failed to add course to program');
|
||||
return handleError(c, error, 'Failed to add course to cohort');
|
||||
}
|
||||
}
|
||||
)
|
||||
@@ -364,7 +364,7 @@ export const cohortRouter = new Hono()
|
||||
const result = await removeCourseFromCohortService(cohortId, courseId);
|
||||
return c.json({ success: true, data: result }, 200);
|
||||
} catch (error) {
|
||||
return handleError(c, error, 'Failed to remove course from program');
|
||||
return handleError(c, error, 'Failed to remove course from cohort');
|
||||
}
|
||||
}
|
||||
)
|
||||
@@ -547,7 +547,7 @@ export const cohortRouter = new Hono()
|
||||
|
||||
/**
|
||||
* GET /cohort/my/goals
|
||||
* Goal assignments for the current authenticated user across all their programs.
|
||||
* Goal assignments for the current authenticated user across all their cohorts.
|
||||
* Used by the LMS "Your Goals" widget.
|
||||
*/
|
||||
.get('/my/goals', authMiddleware, async (c) => {
|
||||
|
||||
@@ -105,11 +105,11 @@ async function enrollCohortStudentsInGroups(
|
||||
return groupMemberRows.length;
|
||||
}
|
||||
|
||||
// ─── Program CRUD ────────────────────────────────────────────────────────────
|
||||
// ─── Cohort CRUD ─────────────────────────────────────────────────────────────
|
||||
|
||||
export async function createCohort(organizationId: string, profileId: string, data: TCreateCohort) {
|
||||
try {
|
||||
const description = data.description?.trim() || `A program for ${data.name}`;
|
||||
const description = data.description?.trim() || `A cohort for ${data.name}`;
|
||||
|
||||
return createCohortWithCreatorMembership(
|
||||
{
|
||||
@@ -195,7 +195,7 @@ export async function deleteCohort(cohortId: string) {
|
||||
}
|
||||
}
|
||||
|
||||
// ─── Program Members ─────────────────────────────────────────────────────────
|
||||
// ─── Cohort Members ──────────────────────────────────────────────────────────
|
||||
|
||||
export async function listCohortMembers(cohortId: string) {
|
||||
try {
|
||||
@@ -323,7 +323,7 @@ export async function removeCohortMemberService(_cohortId: string, memberId: str
|
||||
} catch (error) {
|
||||
if (error instanceof AppError) throw error;
|
||||
throw new AppError(
|
||||
error instanceof Error ? error.message : 'Failed to remove program member',
|
||||
error instanceof Error ? error.message : 'Failed to remove cohort member',
|
||||
ErrorCodes.INTERNAL_ERROR,
|
||||
500
|
||||
);
|
||||
@@ -360,7 +360,7 @@ export async function getEnrolledCohorts(profileId: string) {
|
||||
}
|
||||
}
|
||||
|
||||
// ─── Program Courses ─────────────────────────────────────────────────────────
|
||||
// ─── Cohort Courses ──────────────────────────────────────────────────────────
|
||||
|
||||
export async function listCohortCourses(cohortId: string, profileId: string) {
|
||||
try {
|
||||
@@ -411,7 +411,7 @@ export async function addCourseToCohortService(cohortId: string, data: TAddCours
|
||||
} catch (error) {
|
||||
if (error instanceof AppError) throw error;
|
||||
throw new AppError(
|
||||
error instanceof Error ? error.message : 'Failed to add course to program',
|
||||
error instanceof Error ? error.message : 'Failed to add course to cohort',
|
||||
ErrorCodes.INTERNAL_ERROR,
|
||||
500
|
||||
);
|
||||
@@ -422,20 +422,20 @@ export async function removeCourseFromCohortService(cohortId: string, courseId:
|
||||
try {
|
||||
const deleted = await removeCourseFromCohort(cohortId, courseId);
|
||||
if (!deleted) {
|
||||
throw new AppError('Course not found in program', ErrorCodes.NOT_FOUND, 404);
|
||||
throw new AppError('Course not found in cohort', ErrorCodes.NOT_FOUND, 404);
|
||||
}
|
||||
return deleted;
|
||||
} catch (error) {
|
||||
if (error instanceof AppError) throw error;
|
||||
throw new AppError(
|
||||
error instanceof Error ? error.message : 'Failed to remove course from program',
|
||||
error instanceof Error ? error.message : 'Failed to remove course from cohort',
|
||||
ErrorCodes.INTERNAL_ERROR,
|
||||
500
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// ─── Program Newsfeed ────────────────────────────────────────────────────────
|
||||
// ─── Cohort Newsfeed ─────────────────────────────────────────────────────────
|
||||
|
||||
export async function listCohortNewsfeed(cohortId: string, options: { cursor?: string; limit: number }) {
|
||||
try {
|
||||
@@ -524,7 +524,7 @@ export async function deleteCohortNewsfeedService(feedId: string) {
|
||||
}
|
||||
}
|
||||
|
||||
// ─── Program Newsfeed Comments ────────────────────────────────────────────────
|
||||
// ─── Cohort Newsfeed Comments ────────────────────────────────────────────────
|
||||
|
||||
export async function listCohortNewsfeedComments(feedId: string) {
|
||||
try {
|
||||
|
||||
@@ -221,7 +221,7 @@ function decideStatus(args: {
|
||||
*
|
||||
* Pulls all student cohort-members, computes a per-learner status, and upserts
|
||||
* `program_goal_assignment`. Designed to be safe to run repeatedly (idempotent)
|
||||
* and cheap enough for daily cron over hundreds of programs.
|
||||
* and cheap enough for daily cron over hundreds of cohorts.
|
||||
*/
|
||||
export async function evaluateGoal(goalId: string): Promise<{ evaluated: number }> {
|
||||
const goal = await getCohortGoalById(goalId);
|
||||
@@ -316,7 +316,7 @@ export async function evaluateGoal(goalId: string): Promise<{ evaluated: number
|
||||
}
|
||||
|
||||
/**
|
||||
* Re-evaluate every active goal in a cohort. Used after material program
|
||||
* Re-evaluate every active goal in a cohort. Used after material cohort
|
||||
* changes (member added, course added) or by cron sweeps.
|
||||
*/
|
||||
export async function evaluateCohortGoals(cohortId: string): Promise<{ evaluated: number }> {
|
||||
@@ -450,7 +450,7 @@ export async function getMyGoals(profileId: string) {
|
||||
|
||||
/**
|
||||
* Cross-cohort goal roll-up for the org owner. Lists every active goal in the
|
||||
* org with its per-status counts so the dashboard can group by program.
|
||||
* org with its per-status counts so the dashboard can group by cohort.
|
||||
*/
|
||||
export async function getOrgGoalsOverview(organizationId: string) {
|
||||
const goals = await getCohortGoalsByOrg(organizationId);
|
||||
|
||||
@@ -4,11 +4,11 @@ import type { TInviteStudentsToCohort, TAssignExistingStudentsToCohort } from '@
|
||||
import { assignAudienceToCourses, importAudienceMembers } from '@api/services/organization/audience';
|
||||
|
||||
/**
|
||||
* Invite students to a single program by email CSV.
|
||||
* Invite students to a single cohort by email CSV.
|
||||
*
|
||||
* Thin wrapper that hardcodes `cohortIds = [cohortId]` and delegates to
|
||||
* `importAudienceMembers`. Lets a Cohort ADMIN/TUTOR (or org admin) issue org
|
||||
* invites scoped to *their own* program, without granting org-wide invite rights.
|
||||
* invites scoped to *their own* cohort, without granting org-wide invite rights.
|
||||
*/
|
||||
export async function inviteStudentsToCohort(
|
||||
cohortId: string,
|
||||
@@ -35,8 +35,8 @@ export async function inviteStudentsToCohort(
|
||||
|
||||
/**
|
||||
* Assign existing student profiles (already members of the org audience) to a
|
||||
* single program. Same scoping logic as `inviteStudentsToCohort` — restricted
|
||||
* to one program at a time so the caller's program-admin permission is enough.
|
||||
* single cohort. Same scoping logic as `inviteStudentsToCohort` — restricted
|
||||
* to one cohort at a time so the caller's cohort-admin permission is enough.
|
||||
*/
|
||||
export async function assignExistingStudentsToCohort(cohortId: string, data: TAssignExistingStudentsToCohort) {
|
||||
const cohort = await getCohortById(cohortId);
|
||||
|
||||
Reference in New Issue
Block a user