#5190 - Appeals and forms analysis work - Assessment History and Student View#5846
Conversation
There was a problem hiding this comment.
Pull request overview
Adds “form submission” support as the new backing model for student appeals across the stack (API + web), including student/institution views and routing, and ties form submissions into assessment history/request summaries behind a feature toggle.
Changes:
- Introduces API endpoints to retrieve form submission details for students and institutions, with shared mapping logic in a controller service.
- Updates web UI/routing to navigate from assessment history into form-submission views (student/institution/AEST), and adds “back target” navigation support in the header.
- Refactors form-submission DTO/contracts to rely on
currentDecision(instead ofdecisionStatusat the item root) and adds assessment DTO support forformSubmissionId.
Reviewed changes
Copilot reviewed 41 out of 41 changed files in this pull request and generated 10 comments.
Show a summary per file
| File | Description |
|---|---|
| sources/packages/web/src/views/student/form-submissions/StudentForms.vue | Updates student forms header title. |
| sources/packages/web/src/views/student/form-submissions/FormSubmissionView.vue | Adds status chip and supports back-target navigation (props + header). |
| sources/packages/web/src/views/institution/student/applicationDetails/InstitutionAssessmentsSummary.vue | Routes “appeal” navigation to form submissions when toggle enabled. |
| sources/packages/web/src/views/institution/student/StudentFormSubmissionView.vue | Adds institution-facing form submission view (read-only). |
| sources/packages/web/src/views/aest/student/applicationDetails/AssessmentsSummaryVersion.vue | Routes “appeal” navigation to form submissions for version flow when toggle enabled. |
| sources/packages/web/src/views/aest/student/applicationDetails/AssessmentsSummary.vue | Routes “appeal” navigation to form submissions when toggle enabled. |
| sources/packages/web/src/views/aest/student/StudentFormSubmissionApproval.vue | Adds back-target support and read-only control to approval view header/component usage. |
| sources/packages/web/src/types/index.ts | Re-exports new route navigation types. |
| sources/packages/web/src/types/contracts/FormSubmissionContracts.ts | Refactors decision typing and makes FormSubmissionItem generic over decision shape. |
| sources/packages/web/src/types/RouteNavigation.ts | Introduces BackTarget type for consistent “back” navigation. |
| sources/packages/web/src/services/http/dto/FormSubmission.dto.ts | Refactors form-submission HTTP DTOs around currentDecision and new base DTO. |
| sources/packages/web/src/services/http/dto/Assessment.dto.ts | Adds formSubmissionId to assessment history summary DTO. |
| sources/packages/web/src/services/http/FormSubmissionApi.ts | Extends getFormSubmission to support institution-student scoped endpoint. |
| sources/packages/web/src/services/FormSubmissionService.ts | Updates service types for updated form submission DTOs. |
| sources/packages/web/src/router/StudentRoutes.ts | Adds routes/props for navigating to submissions from application summary with a back target. |
| sources/packages/web/src/router/InstitutionRoutes.ts | Adds institution route to view a student’s form submission. |
| sources/packages/web/src/router/AESTRoutesApplicationVersionsDetails.ts | Adds AEST version route for form submission approval with back target. |
| sources/packages/web/src/router/AESTRoutes.ts | Moves/updates AEST form submission approval route to application-details context and adds back target. |
| sources/packages/web/src/constants/routes/RouteConstants.ts | Adds new route constants for form submission navigation. |
| sources/packages/web/src/components/students/StudentAssessmentDetails.vue | Routes assessment “appeal” navigation to form submissions when toggle enabled. |
| sources/packages/web/src/components/generic/HeaderNavigator.vue | Adds backTarget support and makes title/subtitle/routeLocation optional. |
| sources/packages/web/src/components/form-submissions/FormSubmissionApproval.vue | Updates approval UI typing and read-only gating; aligns with currentDecision. |
| sources/packages/web/src/components/common/students/assessment/History.vue | Emits formSubmissionId for appeals when toggle enabled. |
| sources/packages/web/src/assets/css/base.scss | Adds styling for a header title link class. |
| sources/packages/backend/apps/api/src/services/student-assessment/student-assessment.service.ts | Joins formSubmission in assessment history query. |
| sources/packages/backend/apps/api/src/services/index.ts | Exports new submit service and reorders form-submission service exports. |
| sources/packages/backend/apps/api/src/services/form-submission/form-submission.service.ts | Refactors into retrieval-focused service methods (repo-based). |
| sources/packages/backend/apps/api/src/services/form-submission/form-submission-submit.service.ts | Extracts submission/save logic into a dedicated service. |
| sources/packages/backend/apps/api/src/services/form-submission/form-submission-approval.service.ts | Adds application-status validation before decisions/completion. |
| sources/packages/backend/apps/api/src/services/form-submission/constants.ts | Adds new constant for invalid related application state. |
| sources/packages/backend/apps/api/src/route-controllers/index.ts | Exports new form-submission controller service and institutions controller. |
| sources/packages/backend/apps/api/src/route-controllers/form-submission/models/form-submission.dto.ts | Refactors output DTOs to include currentDecision and adds base DTO. |
| sources/packages/backend/apps/api/src/route-controllers/form-submission/form-submission.students.controller.ts | Adds student GET endpoint to retrieve a submission by id; uses submit service for POST. |
| sources/packages/backend/apps/api/src/route-controllers/form-submission/form-submission.institutions.controller.ts | Adds institution GET endpoint to retrieve a student’s submission by id. |
| sources/packages/backend/apps/api/src/route-controllers/form-submission/form-submission.controller.service.ts | Adds shared controller-layer mapping logic for student/institution retrieval. |
| sources/packages/backend/apps/api/src/route-controllers/form-submission/form-submission.aest.controller.ts | Aligns AEST response to currentDecision-based DTO shape and expands error docs. |
| sources/packages/backend/apps/api/src/route-controllers/assessment/models/assessment.dto.ts | Adds formSubmissionId and includes FormSubmissionStatus in request summary status union. |
| sources/packages/backend/apps/api/src/route-controllers/assessment/assessment.controller.service.ts | Includes pending/denied form-submission appeals in requested assessment summary; maps formSubmissionId in history. |
| sources/packages/backend/apps/api/src/app.students.module.ts | Registers FormSubmissionSubmitService and FormSubmissionControllerService. |
| sources/packages/backend/apps/api/src/app.institutions.module.ts | Registers form-submission services/controllers for institutions. |
| sources/packages/backend/apps/api/src/app.aest.module.ts | Registers FormSubmissionService for AEST module. |
Comments suppressed due to low confidence (2)
sources/packages/backend/apps/api/src/route-controllers/form-submission/form-submission.aest.controller.ts:134
previousDecisionsis filtering usingitem.currentDecision.id, butcurrentDecisioncan be undefined for pending items. This will throw at runtime whenhasApprovalAuthorizationis true but no decision exists yet. Consider guarding for missingcurrentDecision(e.g., use optional chaining or filter only whencurrentDecisionis defined).
previousDecisions: hasApprovalAuthorization
? item.decisions
.filter((decision) => decision.id !== item.currentDecision.id)
.map((decision) => ({
sources/packages/web/src/views/student/form-submissions/FormSubmissionView.vue:89
backTargetis passed toHeaderNavigator, but the footer "Back" button still callsgoBack()which always routes toSTUDENT_FORMS_HISTORY. When navigating here from the application summary route, the footer back will ignore the intendedbackTarget. Consider updatinggoBack()to useprops.backTarget?.towhen provided (same precedence asHeaderNavigator).
You can also share your feedback on Copilot code review. Take the survey.
...backend/apps/api/src/route-controllers/form-submission/form-submission.controller.service.ts
Outdated
Show resolved
Hide resolved
sources/packages/web/src/views/institution/student/StudentFormSubmissionView.vue
Outdated
Show resolved
Hide resolved
sources/packages/web/src/views/aest/student/StudentFormSubmissionApproval.vue
Show resolved
Hide resolved
sources/packages/backend/apps/api/src/services/form-submission/form-submission.service.ts
Show resolved
Hide resolved
...ackend/apps/api/src/route-controllers/form-submission/form-submission.students.controller.ts
Show resolved
Hide resolved
...nd/apps/api/src/route-controllers/form-submission/form-submission.institutions.controller.ts
Show resolved
Hide resolved
sources/packages/web/src/components/common/students/assessment/History.vue
Show resolved
Hide resolved
There was a problem hiding this comment.
No changes in this file from the previous PR. The file was only renamed.
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 43 out of 43 changed files in this pull request and generated 5 comments.
Comments suppressed due to low confidence (5)
sources/packages/backend/apps/api/src/route-controllers/form-submission/form-submission.aest.controller.ts:144
- No e2e tests exist for the AEST form-submission endpoints (e.g.,
getFormSubmission,submitItemDecision,completeFormSubmission). Add e2e specs underroute-controllers/form-submission/_tests_/e2efollowing the naming convention (e.g.,form-submission.aest.controller.getFormSubmission.e2e-spec.ts, etc.) to cover authorization and the updated decision/currentDecision behaviour.
/**
* 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.
* @param itemId optional ID of the form submission item to filter the details for.
* Useful only when a single item detail is required.
* @returns form submission details including individual form items and their details.
*/
@ApiNotFoundResponse({ description: "Form submission not found" })
@Get(":formSubmissionId")
async getFormSubmission(
@UserToken() userToken: IUserToken,
@Param("formSubmissionId", ParseIntPipe) formSubmissionId: number,
@Query("itemId", new ParseIntPipe({ optional: true })) itemId?: number,
): Promise<FormSubmissionMinistryAPIOutDTO> {
const submission =
await this.formSubmissionApprovalService.getFormSubmissionsById(
formSubmissionId,
{ itemId },
);
if (!submission) {
if (itemId) {
throw new NotFoundException(
`Form submission with ID ${formSubmissionId} and form submission item ID ${itemId} not found.`,
);
}
throw new NotFoundException(
`Form submission with ID ${formSubmissionId} not found.`,
);
}
const hasApprovalAuthorization =
this.formSubmissionApprovalService.hasApprovalAuthorization(
submission.formCategory,
userToken.roles,
);
return {
hasApprovalAuthorization,
id: submission.id,
formCategory: submission.formCategory,
status: submission.submissionStatus,
applicationId: submission.application?.id,
applicationNumber: submission.application?.applicationNumber,
submittedDate: submission.submittedDate,
submissionItems: submission.formSubmissionItems.map((item) => ({
id: item.id,
formType: item.dynamicFormConfiguration.formType,
formCategory: item.dynamicFormConfiguration.formCategory,
dynamicFormConfigurationId: item.dynamicFormConfiguration.id,
submissionData: item.submittedData,
formDefinitionName: item.dynamicFormConfiguration.formDefinitionName,
updatedAt: item.updatedAt,
currentDecision:
hasApprovalAuthorization && item.currentDecision
? {
id: item.currentDecision.id,
decisionStatus:
item.currentDecision?.decisionStatus ??
FormSubmissionDecisionStatus.Pending,
decisionDate: item.currentDecision.decisionDate,
decisionBy: getUserFullName(item.currentDecision.decisionBy),
decisionNoteDescription:
item.currentDecision.decisionNote.description,
}
: {
decisionStatus:
item.currentDecision?.decisionStatus ??
FormSubmissionDecisionStatus.Pending,
},
previousDecisions: hasApprovalAuthorization
? item.decisions
.filter((decision) => decision.id !== item.currentDecision.id)
.map((decision) => ({
id: decision.id,
decisionStatus: decision.decisionStatus,
decisionDate: decision.decisionDate,
decisionBy: getUserFullName(decision.decisionBy),
decisionNoteDescription: decision.decisionNote.description,
}))
: undefined,
})),
};
}
sources/packages/backend/apps/api/src/route-controllers/form-submission/form-submission.aest.controller.ts:134
previousDecisionsis filtered usingitem.currentDecision.ideven whenitem.currentDecisioncan be undefined (e.g., no decisions made yet). This will throw at runtime for authorized users viewing a submission without a current decision. Guard against a missingcurrentDecisionin the filter (e.g., handle the no-decision case separately or use optional chaining and a fallback).
previousDecisions: hasApprovalAuthorization
? item.decisions
.filter((decision) => decision.id !== item.currentDecision.id)
.map((decision) => ({
sources/packages/web/src/router/StudentRoutes.ts:358
- Two route records use the exact same
path(AppRoutes.StudentFormSubmissionView). Vue Router will only match one, so the other (with thebackTargetprops) will be unreachable. Consolidate into a single route record and derivebackTargetconditionally (e.g., based on a query param) or use distinct paths.
{
path: AppRoutes.StudentFormSubmissionView,
name: StudentRoutesConst.STUDENT_FORMS_SUBMISSION_VIEW,
component: FormSubmissionView,
props: (route) => ({
formSubmissionId: Number.parseInt(
route.params.formSubmissionId as string,
),
applicationId: route.query.applicationId
? Number.parseInt(route.query.applicationId as string)
: undefined,
}),
meta: {
clientType: ClientIdType.Student,
},
},
{
path: AppRoutes.StudentFormSubmissionView,
name: StudentRoutesConst.STUDENT_FORMS_SUBMISSION_VIEW_FROM_APPLICATION_SUMMARY,
component: FormSubmissionView,
props: (route) => ({
formSubmissionId: Number.parseInt(
route.params.formSubmissionId as string,
),
applicationId: route.query.applicationId
? Number.parseInt(route.query.applicationId as string)
: undefined,
backTarget: {
name: "Financial Aid Application",
to: {
name: StudentRoutesConst.STUDENT_APPLICATION_DETAILS,
params: {
id: route.query.applicationId,
},
},
},
}),
meta: {
clientType: ClientIdType.Student,
},
},
sources/packages/web/src/views/student/form-submissions/FormSubmissionView.vue:90
- The footer "Back" action always routes to
STUDENT_FORMS_HISTORY, ignoring the newbackTargetprop used by the header navigator. This will break the expected back navigation when arriving from application details. Consider updatinggoBackto preferbackTarget.towhen provided (and fall back to forms history otherwise).
sources/packages/web/src/services/http/FormSubmissionApi.ts:37 FormSubmissionStudentAPIOutDTOis still imported/used forMOCKED_SUBMISSIONS, but it no longer exists in the DTO exports. This will fail to compile, and the mocked payload shape still usesdecisionStatusinstead of the newcurrentDecisionstructure. Update/remove the mock and aligngetFormSubmissionSummarytypings with the current DTOs.
You can also share your feedback on Copilot code review. Take the survey.
sources/packages/backend/apps/api/src/services/form-submission/form-submission.service.ts
Show resolved
Hide resolved
sources/packages/backend/apps/api/src/services/form-submission/form-submission.service.ts
Outdated
Show resolved
Hide resolved
...backend/apps/api/src/route-controllers/form-submission/form-submission.controller.service.ts
Outdated
Show resolved
Hide resolved
.../packages/backend/apps/api/src/route-controllers/assessment/assessment.controller.service.ts
Show resolved
Hide resolved
|
| submittedDate: true, | ||
| }, | ||
| where: { | ||
| application: { id: applicationId }, |
There was a problem hiding this comment.
Minor. We generally use just student: { id: studentId }



Ministry
Current Assessment History
Declinedstatus used for fully declined form submissions instead ofCompleted.Assessment History Version
Institution
Student
Technical Changes
API
DTO
decisionStatuswas present twice in the DTOs to allow thecurrentDecisionproperty to be completely undefined (FormSubmissionItemAPIOutDTO). This was changed to make thecurrentDecisionas the only source for thedecisionStatusreturned from the API.Controllers and services
form-submission-approval.service.ts: Ministry focused including dependencies that support the form submission approval.form-submission-submit.service.ts: Student focused including dependencies that support the form submission by the student.form-submission.service.tsshared between Student, Institution, and potentially any shared method needed by Ministry.Outside PR Changes
Fixes from previous implementation
Back Target
Loading state improvements
Header Navigator Styles