-
Notifications
You must be signed in to change notification settings - Fork 13
#4561 - Ministry Dashboard Pending Program Queue #5688
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Conversation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Pull request overview
This PR adds a new Ministry dashboard view to display all programs in pending status, providing similar functionality to the existing pending offerings view. The implementation includes comprehensive backend support, frontend UI, and thorough e2e test coverage.
Changes:
- New backend endpoint (
GET /aest/education-program/pending) with pagination, sorting, and search capabilities for pending programs - New Vue component (
ViewPendingPrograms.vue) displaying pending programs in a data table with institution details - Updated sidebar navigation to include link to the new programs queue view
- Removed status column from pending offerings view for consistency
Reviewed changes
Copilot reviewed 17 out of 17 changed files in this pull request and generated 9 comments.
Show a summary per file
| File | Description |
|---|---|
ViewPendingPrograms.vue |
New AEST view component for displaying pending programs with search and pagination |
ViewPendingOfferings.vue |
Removed status column and StatusChipOffering component from pending offerings view |
DataTableContract.ts |
Added PendingProgramsHeaders definition and updated PendingOfferingsHeaders |
AppRoutes.ts |
Added PendingPrograms route constant |
EducationProgram.dto.ts (frontend) |
Added EducationProgramPendingAPIOutDTO interface |
EducationProgramApi.ts |
Added getPendingPrograms API client method |
EducationProgramService.ts (frontend) |
Added getPendingPrograms service method |
AESTRoutes.ts |
Added route configuration for ViewPendingPrograms component |
RouteConstants.ts |
Added PENDING_PROGRAMS route constant |
AESTHomeSideBar.vue |
Added "Programs" menu item under Institution requests |
education-program.ts (factory) |
Enhanced factory to support name and programStatus in initialValues |
education-program.service.ts |
Added getPendingPrograms method and addProgramsSort helper |
education-program.service.models.ts |
Added PendingEducationProgram model class |
pagination.dto.ts |
Added PendingProgramsPaginationOptionsAPIInDTO with sortField validation |
education-program.dto.ts (backend) |
Added EducationProgramPendingAPIOutDTO class |
education-program.aest.controller.ts |
Added getPendingPrograms endpoint handler |
education-program.aest.controller.getPendingPrograms.e2e-spec.ts |
Comprehensive e2e tests covering pagination, sorting, filtering, and inactive programs |
sources/packages/backend/apps/api/src/services/education-program/education-program.service.ts
Outdated
Show resolved
Hide resolved
...ackend/apps/api/src/route-controllers/education-program/education-program.aest.controller.ts
Outdated
Show resolved
Hide resolved
sources/packages/backend/apps/api/src/services/education-program/education-program.service.ts
Show resolved
Hide resolved
sources/packages/backend/apps/api/src/services/education-program/education-program.service.ts
Outdated
Show resolved
Hide resolved
...ackend/apps/api/src/route-controllers/education-program/education-program.aest.controller.ts
Outdated
Show resolved
Hide resolved
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
|
| const location = createFakeLocation(institution); | ||
| await db.institutionLocation.save(location); | ||
|
|
||
| const programA = createFakeEducationProgram( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Not a blocker, but the code can be simplified as follows.
const [programA, programB, programC] = Array.from({ length: 3 }).map(
(_, index) => {
return createFakeEducationProgram(
{
auditUser: savedUser,
institution,
},
{
initialValues: {
name: `Chemistry 30${index}`,
programStatus: ProgramStatus.Pending,
submittedDate: PAST_SUBMITTED_DATE,
},
},
);
},
);
await db.educationProgram.save([programC, programA, programB]);|
|
||
| it("Should return filtered pending programs when search by institution operating name is applied.", async () => { | ||
| // Arrange | ||
| const uniqueInstitutionName = "Unique Test Institution XYZ"; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
To avoid conflicts and allow the test to be executed multiple times locally, it would be preferable to use something really unique as faker.string.uuid(), as part of the name.
The full GUID can be used to execute the search partial match.
const uniqueInstitutionName = `Unique Institution - ${faker.string.uuid()}`;| </template> | ||
| </body-header> | ||
| <content-group> | ||
| <toggle-content :toggled="!programsAndCount?.count"> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
To allow the table loading behaviour, in the most recent task, we have been defining the toggle-content condition as follows.
<toggle-content :toggled="!programsAndCount?.count && !loading">And also the v-skeleton-loader.
<template #loading>
<v-skeleton-loader type="table-row@5"></v-skeleton-loader>
</template>| programsQuery: SelectQueryBuilder<EducationProgram>, | ||
| paginationOptions: PaginationOptions, | ||
| ): void { | ||
| const sortField = paginationOptions.sortField || "submittedDate"; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Minor, the submittedDate can be a const to be used at the three points below.
const defaultSortField = "submittedDate";
const sortField = paginationOptions.sortField || defaultSortField;
const sortOrder = paginationOptions.sortOrder || FieldSortOrder.ASC;
// Map the sort field to the correct column.
const sortColumnMap: Record<string, string> = {
[defaultSortField]: "programs.submittedDate",
programName: "programs.name",
institutionOperatingName: "institution.operatingName",
};
const sortColumn = sortColumnMap[sortField] || sortColumnMap[defaultSortField];
programsQuery.orderBy(sortColumn, sortOrder);| const pendingPrograms = entities.map((program, index) => ({ | ||
| id: program.id, | ||
| name: program.name, | ||
| submittedDate: program.submittedDate, | ||
| institutionId: program.institution.id, | ||
| institutionOperatingName: program.institution.operatingName, | ||
| selectedLocationId: | ||
| raw[index].selectedLocationId ?? raw[index].selectedlocationid, | ||
| })); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We created the mapFromRawAndEntities to facilitate these conversions from raw executions. Can you please check if it can be applied here?
| .andWhere( | ||
| new Brackets((qb) => { | ||
| qb.where("programs.effectiveEndDate is null").orWhere( | ||
| "programs.effectiveEndDate > CURRENT_DATE", | ||
| ); | ||
| }), | ||
| ); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is this condition required since the discussion and AC were adjusted to be only "check the is_active flag"?



Summary
Adds a new view to the Ministry portal to list all programs in pending status.
Frontend Changes
Backend Changes
E2E Tests