From b193afc56947ddf370f874c72d31123cd3e28881 Mon Sep 17 00:00:00 2001 From: Andrew Boni Signori Date: Tue, 10 Mar 2026 08:20:54 -0700 Subject: [PATCH 1/8] History for Ministry, Institution, and Student --- .../backend/apps/api/src/app.aest.module.ts | 2 + .../form-submission.aest.controller.ts | 17 ++ .../form-submission.controller.service.ts | 58 +++-- ...form-submission.institutions.controller.ts | 45 +++- .../form-submission.students.controller.ts | 24 +- .../models/form-submission.dto.ts | 5 + .../form-submission.service.ts | 32 ++- .../FormSubmissionPendingAppealsTable.vue | 22 +- .../FormSubmissionPendingFormsTable.vue | 2 +- .../FormSubmissionHistory.vue | 208 ++++++++++++++++++ .../src/constants/routes/RouteConstants.ts | 6 + sources/packages/web/src/router/AESTRoutes.ts | 77 +++++++ .../web/src/router/InstitutionRoutes.ts | 45 ++++ .../web/src/services/FormSubmissionService.ts | 10 +- .../src/services/http/FormSubmissionApi.ts | 25 ++- .../services/http/dto/FormSubmission.dto.ts | 5 + sources/packages/web/src/types/AppRoutes.ts | 2 + .../src/views/aest/student/StudentDetails.vue | 8 + .../student/StudentFormSubmissionHistory.vue | 43 ++++ .../student/InstitutionStudentDetails.vue | 8 + .../student/StudentFormSubmissionHistory.vue | 41 ++++ .../student/StudentFormSubmissionView.vue | 6 +- .../form-submissions/StudentFormsHistory.vue | 176 +-------------- 23 files changed, 612 insertions(+), 255 deletions(-) create mode 100644 sources/packages/web/src/components/form-submissions/FormSubmissionHistory.vue create mode 100644 sources/packages/web/src/views/aest/student/StudentFormSubmissionHistory.vue create mode 100644 sources/packages/web/src/views/institution/student/StudentFormSubmissionHistory.vue diff --git a/sources/packages/backend/apps/api/src/app.aest.module.ts b/sources/packages/backend/apps/api/src/app.aest.module.ts index 050c6e5a5f..b34c2630cb 100644 --- a/sources/packages/backend/apps/api/src/app.aest.module.ts +++ b/sources/packages/backend/apps/api/src/app.aest.module.ts @@ -110,6 +110,7 @@ import { DisbursementScheduleAESTController, FormSubmissionAESTController, MSFAANumberAESTController, + FormSubmissionControllerService, } from "./route-controllers"; import { AuthModule } from "./auth/auth.module"; import { @@ -248,6 +249,7 @@ import { ECertIntegrationModule } from "@sims/integrations/esdc-integration"; StudentAppealActionsProcessor, StudentAppealCreateAssessmentAction, StudentAppealUpdateModifiedIndependentAction, + FormSubmissionControllerService, // Form validators. ConfigurationContextValidator, PendingConcurrencyValidator, diff --git a/sources/packages/backend/apps/api/src/route-controllers/form-submission/form-submission.aest.controller.ts b/sources/packages/backend/apps/api/src/route-controllers/form-submission/form-submission.aest.controller.ts index 8e9fcff6f5..d0c6908aaa 100644 --- a/sources/packages/backend/apps/api/src/route-controllers/form-submission/form-submission.aest.controller.ts +++ b/sources/packages/backend/apps/api/src/route-controllers/form-submission/form-submission.aest.controller.ts @@ -38,6 +38,7 @@ import { FormSubmissionItemDecisionAPIInDTO, FormSubmissionMinistryAPIOutDTO, FormSubmissionPendingSummaryAPIOutDTO, + FormSubmissionsAPIOutDTO, } from "./models/form-submission.dto"; import { getUserFullName } from "../../utilities"; import { FormSubmissionDecisionStatus } from "@sims/sims-db"; @@ -46,6 +47,7 @@ import { FormSubmissionPendingPaginationOptionsAPIInDTO, PaginatedResultsAPIOutDTO, } from "../models/pagination.dto"; +import { FormSubmissionControllerService } from "./form-submission.controller.service"; /** * Roles allowed to update the form submission item decision @@ -64,6 +66,7 @@ export class FormSubmissionAESTController extends BaseController { constructor( private readonly formSubmissionApprovalService: FormSubmissionApprovalService, private readonly formSubmissionService: FormSubmissionService, + private readonly formSubmissionControllerService: FormSubmissionControllerService, ) { super(); } @@ -95,6 +98,20 @@ export class FormSubmissionAESTController extends BaseController { }; } + @Get("student/:studentId") + async getFormSubmissionHistory( + @Param("studentId", ParseIntPipe) studentId: number, + ): Promise { + const submissions = + await this.formSubmissionControllerService.getFormSubmissions(studentId, { + includeBasicDecisionDetails: false, + keepPendingDecisionsWhilePendingFormSubmission: false, + }); + return { + submissions, + }; + } + /** * Get the details of a form submission, including the individual form items and their details. * @param formSubmissionId ID of the form submission to retrieve the details for. diff --git a/sources/packages/backend/apps/api/src/route-controllers/form-submission/form-submission.controller.service.ts b/sources/packages/backend/apps/api/src/route-controllers/form-submission/form-submission.controller.service.ts index 1bcbefa6ef..84cf6d95bd 100644 --- a/sources/packages/backend/apps/api/src/route-controllers/form-submission/form-submission.controller.service.ts +++ b/sources/packages/backend/apps/api/src/route-controllers/form-submission/form-submission.controller.service.ts @@ -1,6 +1,7 @@ import { Injectable, NotFoundException } from "@nestjs/common"; import { FormSubmissionService } from "../../services"; import { + FormSubmission, FormSubmissionDecisionStatus, FormSubmissionItem, FormSubmissionStatus, @@ -25,24 +26,43 @@ export class FormSubmissionControllerService { * - `applicationId`: optional ID of the application, used to validate the access to the form submission * @returns form submission details. */ - async getFormSubmission( - formSubmissionId: number, + async getFormSubmissions( studentId: number, options?: { + formSubmissionId?: number; + locationIds?: number[]; includeBasicDecisionDetails?: boolean; - applicationId?: number; + keepPendingDecisionsWhilePendingFormSubmission?: boolean; }, - ): Promise { - const submission = await this.formSubmissionService.getFormSubmissionById( - formSubmissionId, + ): Promise { + const submissions = await this.formSubmissionService.getFormSubmissions( studentId, - { applicationId: options?.applicationId }, + { + formSubmissionId: options?.formSubmissionId, + locationIds: options?.locationIds, + }, ); - if (!submission) { + if (options?.formSubmissionId && !submissions?.length) { throw new NotFoundException( - `Form submission with ID ${formSubmissionId} not found.`, + `Form submission with ID ${options?.formSubmissionId} not found.`, ); } + return submissions.map((submission) => + this.mapSubmissionsToAPIOutDTO(submission, { + includeBasicDecisionDetails: options?.includeBasicDecisionDetails, + keepPendingDecisionsWhilePendingFormSubmission: + options?.keepPendingDecisionsWhilePendingFormSubmission, + }), + ); + } + + private mapSubmissionsToAPIOutDTO( + submission: FormSubmission, + options?: { + includeBasicDecisionDetails?: boolean; + keepPendingDecisionsWhilePendingFormSubmission?: boolean; + }, + ): FormSubmissionAPIOutDTO { return { id: submission.id, formCategory: submission.formCategory, @@ -50,6 +70,7 @@ export class FormSubmissionControllerService { applicationId: submission.application?.id, applicationNumber: submission.application?.applicationNumber, submittedDate: submission.submittedDate, + assessedDate: submission.assessedDate, submissionItems: submission.formSubmissionItems.map((item) => ({ id: item.id, formType: item.dynamicFormConfiguration.formType, @@ -60,7 +81,8 @@ export class FormSubmissionControllerService { currentDecision: this.mapCurrentDecision( submission.submissionStatus, item, - !!options?.includeBasicDecisionDetails, + options?.includeBasicDecisionDetails ?? false, + options?.keepPendingDecisionsWhilePendingFormSubmission ?? true, ), })), }; @@ -80,15 +102,19 @@ export class FormSubmissionControllerService { submissionStatus: FormSubmissionStatus, submissionItem: FormSubmissionItem, includeBasicDecisionDetails: boolean, + keepPendingDecisionsWhilePendingFormSubmission: boolean, ): FormSubmissionItemDecisionAPIOutDTO { - if (submissionStatus === FormSubmissionStatus.Pending) { - // For pending submissions, the decision details should not be returned. - return { decisionStatus: FormSubmissionDecisionStatus.Pending }; - } + let decisionStatus = + keepPendingDecisionsWhilePendingFormSubmission && + submissionStatus === FormSubmissionStatus.Pending + ? FormSubmissionDecisionStatus.Pending + : submissionItem.currentDecision?.decisionStatus; + // Default to Pending if no decision exists. + decisionStatus = decisionStatus ?? FormSubmissionDecisionStatus.Pending; return { - decisionStatus: submissionItem.currentDecision.decisionStatus, + decisionStatus: decisionStatus, decisionNoteDescription: includeBasicDecisionDetails - ? submissionItem.currentDecision.decisionNote.description + ? submissionItem.currentDecision?.decisionNote?.description : undefined, }; } diff --git a/sources/packages/backend/apps/api/src/route-controllers/form-submission/form-submission.institutions.controller.ts b/sources/packages/backend/apps/api/src/route-controllers/form-submission/form-submission.institutions.controller.ts index f7d601c768..86cdb4a53b 100644 --- a/sources/packages/backend/apps/api/src/route-controllers/form-submission/form-submission.institutions.controller.ts +++ b/sources/packages/backend/apps/api/src/route-controllers/form-submission/form-submission.institutions.controller.ts @@ -1,15 +1,19 @@ import { Controller, Get, Param, ParseIntPipe } from "@nestjs/common"; -import { AuthorizedParties } from "../../auth"; +import { AuthorizedParties, IInstitutionUserToken } from "../../auth"; import { AllowAuthorizedParty, HasStudentDataAccess, IsBCPublicInstitution, + UserToken, } from "../../auth/decorators"; import { ApiNotFoundResponse, ApiTags } from "@nestjs/swagger"; import BaseController from "../BaseController"; import { ClientTypeBaseRoute } from "../../types"; import { FormSubmissionControllerService } from "./form-submission.controller.service"; -import { FormSubmissionAPIOutDTO } from "./models/form-submission.dto"; +import { + FormSubmissionAPIOutDTO, + FormSubmissionsAPIOutDTO, +} from "./models/form-submission.dto"; @AllowAuthorizedParty(AuthorizedParties.institution) @IsBCPublicInstitution() @@ -22,6 +26,22 @@ export class FormSubmissionInstitutionsController extends BaseController { super(); } + @Get("student/:studentId") + async getFormSubmissionHistory( + @Param("studentId", ParseIntPipe) studentId: number, + @UserToken() userToken: IInstitutionUserToken, + ): Promise { + const submissions = + await this.formSubmissionControllerService.getFormSubmissions(studentId, { + includeBasicDecisionDetails: true, + keepPendingDecisionsWhilePendingFormSubmission: false, + locationIds: userToken.authorizations.getLocationsIds(), + }); + return { + submissions, + }; + } + /** * Get the details of a form submission, including the individual form items and their details. * Please note currently the institution can only access form submissions related to their students @@ -34,19 +54,20 @@ export class FormSubmissionInstitutionsController extends BaseController { * @returns form submission details including individual form items and their details. */ @ApiNotFoundResponse({ description: "Form submission not found." }) - @HasStudentDataAccess("studentId", "applicationId") - @Get( - "student/:studentId/application/:applicationId/form-submission/:formSubmissionId", - ) + @HasStudentDataAccess("studentId") + @Get("student/:studentId/form-submission/:formSubmissionId") async getFormSubmission( @Param("studentId", ParseIntPipe) studentId: number, - @Param("applicationId", ParseIntPipe) applicationId: number, @Param("formSubmissionId", ParseIntPipe) formSubmissionId: number, + @UserToken() userToken: IInstitutionUserToken, ): Promise { - return this.formSubmissionControllerService.getFormSubmission( - formSubmissionId, - studentId, - { includeBasicDecisionDetails: true, applicationId }, - ); + const [submission] = + await this.formSubmissionControllerService.getFormSubmissions(studentId, { + formSubmissionId, + includeBasicDecisionDetails: true, + keepPendingDecisionsWhilePendingFormSubmission: true, + locationIds: userToken.authorizations.getLocationsIds(), + }); + return submission; } } diff --git a/sources/packages/backend/apps/api/src/route-controllers/form-submission/form-submission.students.controller.ts b/sources/packages/backend/apps/api/src/route-controllers/form-submission/form-submission.students.controller.ts index b15272e464..7f6f90a5c3 100644 --- a/sources/packages/backend/apps/api/src/route-controllers/form-submission/form-submission.students.controller.ts +++ b/sources/packages/backend/apps/api/src/route-controllers/form-submission/form-submission.students.controller.ts @@ -34,6 +34,7 @@ import { FormSubmissionAPIInDTO, FormSubmissionAPIOutDTO, FormSubmissionConfigurationsAPIOutDTO, + FormSubmissionsAPIOutDTO, FormSupplementaryDataAPIInDTO, FormSupplementaryDataAPIOutDTO, } from "./models/form-submission.dto"; @@ -100,6 +101,19 @@ export class FormSubmissionStudentsController extends BaseController { }; } + @Get() + async getFormSubmissionHistory( + @UserToken() userToken: StudentUserToken, + ): Promise { + const submissions = + await this.formSubmissionControllerService.getFormSubmissions( + userToken.studentId, + ); + return { + submissions, + }; + } + /** * Get the details of a form submission, including the individual form items and their details. * @param formSubmissionId ID of the form submission to retrieve the details for. @@ -111,10 +125,12 @@ export class FormSubmissionStudentsController extends BaseController { @Param("formSubmissionId", ParseIntPipe) formSubmissionId: number, @UserToken() userToken: StudentUserToken, ): Promise { - return this.formSubmissionControllerService.getFormSubmission( - formSubmissionId, - userToken.studentId, - ); + const [submission] = + await this.formSubmissionControllerService.getFormSubmissions( + userToken.studentId, + { formSubmissionId }, + ); + return submission; } /** diff --git a/sources/packages/backend/apps/api/src/route-controllers/form-submission/models/form-submission.dto.ts b/sources/packages/backend/apps/api/src/route-controllers/form-submission/models/form-submission.dto.ts index aff0b149c5..47c20452bd 100644 --- a/sources/packages/backend/apps/api/src/route-controllers/form-submission/models/form-submission.dto.ts +++ b/sources/packages/backend/apps/api/src/route-controllers/form-submission/models/form-submission.dto.ts @@ -65,6 +65,7 @@ abstract class FormSubmissionBaseAPIOutDTO { applicationId?: number; applicationNumber?: string; submittedDate: Date; + assessedDate?: Date; } /** @@ -117,6 +118,10 @@ export class FormSubmissionAPIOutDTO extends FormSubmissionBaseAPIOutDTO { submissionItems: FormSubmissionItemAPIOutDTO[]; } +export class FormSubmissionsAPIOutDTO { + submissions: FormSubmissionAPIOutDTO[]; +} + /** * Individual form items that will be part of a form submission with one to many forms * for the Ministry, including the decision details. diff --git a/sources/packages/backend/apps/api/src/services/form-submission/form-submission.service.ts b/sources/packages/backend/apps/api/src/services/form-submission/form-submission.service.ts index 5a0d3025f4..871a1ff062 100644 --- a/sources/packages/backend/apps/api/src/services/form-submission/form-submission.service.ts +++ b/sources/packages/backend/apps/api/src/services/form-submission/form-submission.service.ts @@ -1,5 +1,5 @@ import { Injectable } from "@nestjs/common"; -import { Brackets, In, Repository } from "typeorm"; +import { Brackets, In, IsNull, Repository } from "typeorm"; import { FormCategory, FormSubmission, @@ -158,27 +158,19 @@ export class FormSubmissionService { }; } - /** - * Gets a form submission by its ID. - * @param formSubmissionId The ID of the form submission to retrieve. - * @param studentId student ID for authorization. - * @param options method options. - * - `applicationId`: optional application ID to ensure the form submission - * belongs to the application related to the institution's student. - * @returns The form submission if found, otherwise null. - */ - async getFormSubmissionById( - formSubmissionId: number, + async getFormSubmissions( studentId: number, options?: { - applicationId?: number; + formSubmissionId?: number; + locationIds?: number[]; }, - ): Promise { - return this.formSubmissionRepo.findOne({ + ): Promise { + return this.formSubmissionRepo.find({ select: { id: true, submissionStatus: true, submittedDate: true, + assessedDate: true, formCategory: true, application: { id: true, @@ -212,11 +204,15 @@ export class FormSubmissionService { }, }, where: { - id: formSubmissionId, + id: options?.formSubmissionId, student: { id: studentId }, - application: { id: options?.applicationId }, + application: { + location: options?.locationIds + ? [{ id: In(options.locationIds) }, { id: IsNull() }] + : undefined, + }, }, - order: { formSubmissionItems: { id: "ASC" } }, + order: { submittedDate: "DESC", formSubmissionItems: { id: "ASC" } }, }); } } diff --git a/sources/packages/web/src/components/aest/students/FormSubmissionPendingAppealsTable.vue b/sources/packages/web/src/components/aest/students/FormSubmissionPendingAppealsTable.vue index e25bdfb46a..e6887b5a38 100644 --- a/sources/packages/web/src/components/aest/students/FormSubmissionPendingAppealsTable.vue +++ b/sources/packages/web/src/components/aest/students/FormSubmissionPendingAppealsTable.vue @@ -125,18 +125,18 @@ const currentPagination: PaginationOptions = { const goToAppealsApproval = ( pendingAppeal: FormSubmissionPendingSummaryAPIOutDTO, ) => { - if (pendingAppeal.applicationId) { - router.push({ - name: AESTRoutesConst.ASSESSMENTS_SUMMARY, - params: { - applicationId: pendingAppeal.applicationId, - studentId: pendingAppeal.studentId, - }, - }); - return; - } + // if (pendingAppeal.applicationId) { + // router.push({ + // name: AESTRoutesConst.ASSESSMENTS_SUMMARY, + // params: { + // applicationId: pendingAppeal.applicationId, + // studentId: pendingAppeal.studentId, + // }, + // }); + // return; + // } router.push({ - name: AESTRoutesConst.STUDENT_FORM_SUBMISSION_APPROVAL, + name: AESTRoutesConst.FORM_SUBMISSION_APPROVAL_FROM_PENDING_APPEALS, params: { formSubmissionId: pendingAppeal.formSubmissionId, }, diff --git a/sources/packages/web/src/components/aest/students/FormSubmissionPendingFormsTable.vue b/sources/packages/web/src/components/aest/students/FormSubmissionPendingFormsTable.vue index 13e0818c9f..10aa195486 100644 --- a/sources/packages/web/src/components/aest/students/FormSubmissionPendingFormsTable.vue +++ b/sources/packages/web/src/components/aest/students/FormSubmissionPendingFormsTable.vue @@ -101,7 +101,7 @@ const goToFormSubmission = ( formSubmission: FormSubmissionPendingSummaryAPIOutDTO, ) => { router.push({ - name: AESTRoutesConst.STUDENT_FORM_SUBMISSION_APPROVAL, + name: AESTRoutesConst.STUDENT_FORM_SUBMISSION_PENDING_FORMS, params: { formSubmissionId: formSubmission.formSubmissionId }, }); }; diff --git a/sources/packages/web/src/components/form-submissions/FormSubmissionHistory.vue b/sources/packages/web/src/components/form-submissions/FormSubmissionHistory.vue new file mode 100644 index 0000000000..f77fca79e7 --- /dev/null +++ b/sources/packages/web/src/components/form-submissions/FormSubmissionHistory.vue @@ -0,0 +1,208 @@ + + diff --git a/sources/packages/web/src/constants/routes/RouteConstants.ts b/sources/packages/web/src/constants/routes/RouteConstants.ts index 46dbb9286d..b533dca3f2 100644 --- a/sources/packages/web/src/constants/routes/RouteConstants.ts +++ b/sources/packages/web/src/constants/routes/RouteConstants.ts @@ -102,6 +102,8 @@ export const InstitutionRoutesConst = { STUDENT_PROFILE: Symbol(), STUDENT_DETAILS: Symbol(), STUDENT_APPLICATIONS: Symbol(), + STUDENT_FORM_SUBMISSION_HISTORY: Symbol(), + STUDENT_FORM_SUBMISSION_VIEW_FROM_HISTORY: Symbol(), STUDENT_APPLICATION_DETAILS: Symbol(), STUDENT_RESTRICTIONS: Symbol(), STUDENT_FILE_UPLOADS: Symbol(), @@ -167,6 +169,9 @@ export const AESTRoutesConst = { STUDENT_APPLICATION_APPEAL_REQUESTS_APPROVAL: Symbol(), STUDENT_APPLICATION_APPEAL_REQUESTS_APPROVAL_VERSION: Symbol(), STUDENT_APPLICATION_FORM_SUBMISSION_APPROVAL: Symbol(), + FORM_SUBMISSION_APPROVAL_FROM_HISTORY: Symbol(), + FORM_SUBMISSION_APPROVAL_FROM_PENDING_APPEALS: Symbol(), + FORM_SUBMISSION_APPROVAL_FROM_PENDING_FORMS: Symbol(), STUDENT_APPLICATION_FORM_SUBMISSION_APPROVAL_VERSION: Symbol(), STUDENT_APPLICATION_OFFERING_CHANGE_REQUEST: Symbol(), STUDENT_APPLICATION_OFFERING_CHANGE_REQUEST_VERSION: Symbol(), @@ -184,6 +189,7 @@ export const AESTRoutesConst = { LEGACY_STUDENT_APPEALS: Symbol(), STUDENT_FORM_SUBMISSION_PENDING_FORMS: Symbol(), STUDENT_FORM_SUBMISSION_PENDING_APPEALS: Symbol(), + STUDENT_FORM_SUBMISSION_HISTORY: Symbol(), APPLICATION_CHANGE_REQUESTS_PENDING: Symbol(), STUDENT_ACCOUNT_APPLICATIONS: Symbol(), STUDENT_ACCOUNT_APPLICATIONS_APPROVAL: Symbol(), diff --git a/sources/packages/web/src/router/AESTRoutes.ts b/sources/packages/web/src/router/AESTRoutes.ts index 541d677bae..425f7a8fbd 100644 --- a/sources/packages/web/src/router/AESTRoutes.ts +++ b/sources/packages/web/src/router/AESTRoutes.ts @@ -69,6 +69,7 @@ import { AESTRoutesApplicationVersionsDetails } from "@/router/AESTRoutesApplica import ViewPendingOfferings from "@/views/aest/institution/ViewPendingOfferings.vue"; import ViewPendingPrograms from "@/views/aest/institution/ViewPendingPrograms.vue"; import StudentFormSubmissionApproval from "@/views/aest/student/StudentFormSubmissionApproval.vue"; +import StudentFormSubmissionHistory from "@/views/aest/student/StudentFormSubmissionHistory.vue"; import StudentFormSubmissionPendingForms from "@/views/aest/student/StudentFormSubmissionPendingForms.vue"; import StudentFormSubmissionPendingAppeals from "@/views/aest/student/StudentFormSubmissionPendingAppeals.vue"; @@ -140,6 +141,17 @@ export const aestRoutes: Array = [ clientType: ClientIdType.AEST, }, }, + { + path: AppRoutes.FormSubmissionHistory, + name: AESTRoutesConst.STUDENT_FORM_SUBMISSION_HISTORY, + component: StudentFormSubmissionHistory, + props: (route) => ({ + studentId: Number.parseInt(route.params.studentId as string), + }), + meta: { + clientType: ClientIdType.AEST, + }, + }, { path: AppRoutes.Restrictions, name: AESTRoutesConst.STUDENT_RESTRICTION, @@ -379,6 +391,71 @@ export const aestRoutes: Array = [ ...AESTRoutesApplicationVersionsDetails, ], }, + { + path: AppRoutes.StudentFormSubmissionApproval, + name: AESTRoutesConst.FORM_SUBMISSION_APPROVAL_FROM_HISTORY, + props: (route) => ({ + formSubmissionId: Number.parseInt( + route.params.formSubmissionId as string, + ), + readOnly: false, + backTarget: route.query?.studentId + ? { + name: "Student details", + to: { + name: AESTRoutesConst.STUDENT_FORM_SUBMISSION_HISTORY, + params: { + studentId: route.query.studentId, + }, + }, + } + : undefined, + }), + component: StudentFormSubmissionApproval, + meta: { + clientType: ClientIdType.AEST, + }, + }, + { + path: AppRoutes.StudentFormSubmissionApproval, + name: AESTRoutesConst.FORM_SUBMISSION_APPROVAL_FROM_PENDING_APPEALS, + props: (route) => ({ + formSubmissionId: Number.parseInt( + route.params.formSubmissionId as string, + ), + readOnly: false, + backTarget: { + name: "Appeals", + to: { + name: AESTRoutesConst.STUDENT_FORM_SUBMISSION_PENDING_APPEALS, + }, + }, + }), + component: StudentFormSubmissionApproval, + meta: { + clientType: ClientIdType.AEST, + }, + }, + { + path: AppRoutes.StudentFormSubmissionApproval, + name: AESTRoutesConst.FORM_SUBMISSION_APPROVAL_FROM_PENDING_FORMS, + props: (route) => ({ + formSubmissionId: Number.parseInt( + route.params.formSubmissionId as string, + ), + readOnly: false, + backTarget: { + name: "Forms", + to: { + name: AESTRoutesConst.STUDENT_FORM_SUBMISSION_PENDING_FORMS, + }, + }, + }), + component: StudentFormSubmissionApproval, + meta: { + clientType: ClientIdType.AEST, + }, + }, { path: AppRoutes.SearchStudents, name: AESTRoutesConst.SEARCH_STUDENTS, diff --git a/sources/packages/web/src/router/InstitutionRoutes.ts b/sources/packages/web/src/router/InstitutionRoutes.ts index 84a5e45ea4..e059202fa2 100644 --- a/sources/packages/web/src/router/InstitutionRoutes.ts +++ b/sources/packages/web/src/router/InstitutionRoutes.ts @@ -62,6 +62,7 @@ import InProgress from "@/views/institution/locations/request-a-change/request-a import Completed from "@/views/institution/locations/request-a-change/request-a-change/Completed.vue"; import Reports from "@/views/institution/Reports.vue"; import FormSubmissionView from "@/views/institution/student/StudentFormSubmissionView.vue"; +import StudentFormSubmissionHistory from "@/views/institution/student/StudentFormSubmissionHistory.vue"; export const institutionRoutes: Array = [ { @@ -686,6 +687,22 @@ export const institutionRoutes: Array = [ ], }, }, + { + path: AppRoutes.FormSubmissionHistory, + name: InstitutionRoutesConst.STUDENT_FORM_SUBMISSION_HISTORY, + component: StudentFormSubmissionHistory, + props: (route) => ({ + studentId: Number.parseInt(route.params.studentId as string), + }), + meta: { + clientType: ClientIdType.Institution, + institutionUserTypes: [ + InstitutionUserTypes.admin, + InstitutionUserTypes.user, + InstitutionUserTypes.readOnlyUser, + ], + }, + }, { path: AppRoutes.Restrictions, name: InstitutionRoutesConst.STUDENT_RESTRICTIONS, @@ -827,6 +844,34 @@ export const institutionRoutes: Array = [ }, ], }, + { + path: AppRoutes.InstitutionStudentFormSubmissionView, + name: InstitutionRoutesConst.STUDENT_FORM_SUBMISSION_VIEW_FROM_HISTORY, + component: FormSubmissionView, + props: (route) => ({ + studentId: Number.parseInt(route.params.studentId as string), + formSubmissionId: Number.parseInt( + route.params.formSubmissionId as string, + ), + backTarget: { + name: "Student details", + to: { + name: InstitutionRoutesConst.STUDENT_FORM_SUBMISSION_HISTORY, + params: { + studentId: route.params.studentId, + }, + }, + }, + }), + meta: { + clientType: ClientIdType.Institution, + institutionUserTypes: [ + InstitutionUserTypes.admin, + InstitutionUserTypes.user, + InstitutionUserTypes.readOnlyUser, + ], + }, + }, ], beforeEnter: (to, _from, next) => { AuthService.shared diff --git a/sources/packages/web/src/services/FormSubmissionService.ts b/sources/packages/web/src/services/FormSubmissionService.ts index 3722af8a28..b4de1e0a38 100644 --- a/sources/packages/web/src/services/FormSubmissionService.ts +++ b/sources/packages/web/src/services/FormSubmissionService.ts @@ -8,9 +8,9 @@ import { FormSubmissionMinistryAPIOutDTO, FormSubmissionCompletionAPIInDTO, FormSubmissionAPIOutDTO, - FormSubmissionItemAPIOutDTO, FormSubmissionPendingSummaryAPIOutDTO, PaginatedResultsAPIOutDTO, + FormSubmissionsAPIOutDTO, } from "@/services/http/dto"; import { FormCategory, PaginationOptions } from "@/types"; @@ -31,10 +31,10 @@ export class FormSubmissionService { } // TODO: To be implemented. - async getFormSubmissionSummary(): Promise<{ - submissions: FormSubmissionItemAPIOutDTO[]; - }> { - return ApiClient.FormSubmissionApi.getFormSubmissionSummary(); + async getFormSubmissionHistory( + studentId?: number, + ): Promise { + return ApiClient.FormSubmissionApi.getFormSubmissionHistory(studentId); } /** diff --git a/sources/packages/web/src/services/http/FormSubmissionApi.ts b/sources/packages/web/src/services/http/FormSubmissionApi.ts index 5497736ab1..78cec8f744 100644 --- a/sources/packages/web/src/services/http/FormSubmissionApi.ts +++ b/sources/packages/web/src/services/http/FormSubmissionApi.ts @@ -10,6 +10,7 @@ import { FormSubmissionPendingSummaryAPIOutDTO, PaginatedResultsAPIOutDTO, FormSubmissionAPIOutDTO, + FormSubmissionsAPIOutDTO, } from "@/services/http/dto"; import { FormCategory, @@ -106,13 +107,14 @@ export class FormSubmissionApi extends HttpBaseClient { return this.getCall(this.addClientRoot("form-submission/forms")); } - // TODO: To be implemented. - async getFormSubmissionSummary(): Promise<{ - submissions: FormSubmissionAPIOutDTO[]; - }> { - return { - submissions: MOCKED_SUBMISSIONS, - }; + async getFormSubmissionHistory( + studentId?: number, + ): Promise { + let url = "form-submission"; + if (studentId) { + url += `/student/${studentId}`; + } + return this.getCall(this.addClientRoot(url)); } /** @@ -132,12 +134,13 @@ export class FormSubmissionApi extends HttpBaseClient { */ async getFormSubmission( formSubmissionId: number, - options?: { studentId?: number; applicationId?: number; itemId?: number }, + options?: { studentId?: number; itemId?: number }, ): Promise { let url = `form-submission/${formSubmissionId}`; - if (options?.studentId && options?.applicationId) { - // Used for institutions to validate the access to the student and application data related to the form submission. - url = `form-submission/student/${options.studentId}/application/${options.applicationId}/${url}`; + if (options?.studentId) { + // Used for institutions to validate the access to the student data. + // Used by the Ministry to request a specific student. + url = `form-submission/student/${options.studentId}/${url}`; } if (options?.itemId) { // Used by the Ministry to filter the form submission details for a specific form item during the approval process. diff --git a/sources/packages/web/src/services/http/dto/FormSubmission.dto.ts b/sources/packages/web/src/services/http/dto/FormSubmission.dto.ts index 35b9fd9c19..f3b94b245b 100644 --- a/sources/packages/web/src/services/http/dto/FormSubmission.dto.ts +++ b/sources/packages/web/src/services/http/dto/FormSubmission.dto.ts @@ -40,6 +40,7 @@ interface FormSubmissionBaseAPIOutDTO { applicationId?: number; applicationNumber?: string; submittedDate: Date; + assessedDate?: Date; } /** @@ -64,6 +65,10 @@ export interface FormSubmissionAPIOutDTO extends FormSubmissionBaseAPIOutDTO { submissionItems: FormSubmissionItemAPIOutDTO[]; } +export class FormSubmissionsAPIOutDTO { + submissions: FormSubmissionAPIOutDTO[]; +} + /** * Current decision associated with a form submission item. */ diff --git a/sources/packages/web/src/types/AppRoutes.ts b/sources/packages/web/src/types/AppRoutes.ts index bfcb7b31da..8eef4a626e 100644 --- a/sources/packages/web/src/types/AppRoutes.ts +++ b/sources/packages/web/src/types/AppRoutes.ts @@ -103,6 +103,7 @@ export enum AppRoutes { ActiveApplicationScholasticStandingView = "active-application/location/:locationId/scholastic-standing/:scholasticStandingId", OfferingsUpload = "offerings-upload", WithdrawalUpload = "withdrawal-upload", + InstitutionStudentFormSubmissionView = "student/:studentId/forms/submission/:formSubmissionId", // AEST AESTRoot = "/ministry", @@ -125,6 +126,7 @@ export enum AppRoutes { LegacyStudentAppeals = "legacy-appeals", FormSubmissionPendingForms = "pending-forms", FormSubmissionPendingAppeals = "pending-appeals", + FormSubmissionHistory = "form-submissions", ApplicationChangeRequests = "application-change-requests", StudentAccountApplications = "student-account-applications", StudentAccountApplicationsApproval = "student-account-applications/:studentAccountApplicationId/approval", diff --git a/sources/packages/web/src/views/aest/student/StudentDetails.vue b/sources/packages/web/src/views/aest/student/StudentDetails.vue index 7a293e85c2..f598d8b0ef 100644 --- a/sources/packages/web/src/views/aest/student/StudentDetails.vue +++ b/sources/packages/web/src/views/aest/student/StudentDetails.vue @@ -64,6 +64,14 @@ export default defineComponent({ params: { studentId: props.studentId }, }), }, + { + label: "Forms", + icon: "fa:fas fa-inbox", + command: () => ({ + name: AESTRoutesConst.STUDENT_FORM_SUBMISSION_HISTORY, + params: { studentId: props.studentId }, + }), + }, { label: "Restrictions", icon: "fa:far fa-times-circle", diff --git a/sources/packages/web/src/views/aest/student/StudentFormSubmissionHistory.vue b/sources/packages/web/src/views/aest/student/StudentFormSubmissionHistory.vue new file mode 100644 index 0000000000..cd327fe899 --- /dev/null +++ b/sources/packages/web/src/views/aest/student/StudentFormSubmissionHistory.vue @@ -0,0 +1,43 @@ + + diff --git a/sources/packages/web/src/views/institution/student/InstitutionStudentDetails.vue b/sources/packages/web/src/views/institution/student/InstitutionStudentDetails.vue index 6586bf6aff..fcb7da0454 100644 --- a/sources/packages/web/src/views/institution/student/InstitutionStudentDetails.vue +++ b/sources/packages/web/src/views/institution/student/InstitutionStudentDetails.vue @@ -55,6 +55,14 @@ export default defineComponent({ params: { studentId: props.studentId }, }), }, + { + label: "Forms", + icon: "fa:fas fa-inbox", + command: () => ({ + name: InstitutionRoutesConst.STUDENT_FORM_SUBMISSION_HISTORY, + params: { studentId: props.studentId }, + }), + }, { label: "Restrictions", icon: "fa:far fa-times-circle", diff --git a/sources/packages/web/src/views/institution/student/StudentFormSubmissionHistory.vue b/sources/packages/web/src/views/institution/student/StudentFormSubmissionHistory.vue new file mode 100644 index 0000000000..60ab7da92b --- /dev/null +++ b/sources/packages/web/src/views/institution/student/StudentFormSubmissionHistory.vue @@ -0,0 +1,41 @@ + + diff --git a/sources/packages/web/src/views/institution/student/StudentFormSubmissionView.vue b/sources/packages/web/src/views/institution/student/StudentFormSubmissionView.vue index 4bd000e843..1b8f74804b 100644 --- a/sources/packages/web/src/views/institution/student/StudentFormSubmissionView.vue +++ b/sources/packages/web/src/views/institution/student/StudentFormSubmissionView.vue @@ -76,10 +76,6 @@ export default defineComponent({ type: Number, required: true, }, - applicationId: { - type: Number, - required: true, - }, formSubmissionId: { type: Number, required: true, @@ -110,7 +106,7 @@ export default defineComponent({ const submission = (await FormSubmissionService.shared.getFormSubmission( props.formSubmissionId, - { studentId: props.studentId, applicationId: props.applicationId }, + { studentId: props.studentId }, )) as FormSubmissionAPIOutDTO; formSubmission.value = submission; diff --git a/sources/packages/web/src/views/student/form-submissions/StudentFormsHistory.vue b/sources/packages/web/src/views/student/form-submissions/StudentFormsHistory.vue index 3dd4407c35..bbbf64cc3e 100644 --- a/sources/packages/web/src/views/student/form-submissions/StudentFormsHistory.vue +++ b/sources/packages/web/src/views/student/form-submissions/StudentFormsHistory.vue @@ -1,176 +1,19 @@