Skip to content

#5190 - Appeals and forms analysis work - Assessment History and Student View#5846

Open
andrewsignori-aot wants to merge 6 commits intomainfrom
feature/#5190-new-unified-data-path-for-appeals-and-forms-ministry-assessment-request-and-history
Open

#5190 - Appeals and forms analysis work - Assessment History and Student View#5846
andrewsignori-aot wants to merge 6 commits intomainfrom
feature/#5190-new-unified-data-path-for-appeals-and-forms-ministry-assessment-request-and-history

Conversation

@andrewsignori-aot
Copy link
Collaborator

@andrewsignori-aot andrewsignori-aot commented Mar 4, 2026

Ministry

Current Assessment History

  • Listing form submissions under the "Unproved changes".
  • Declined status used for fully declined form submissions instead of Completed.
image

Assessment History Version

  • Showing new appeals under the "Unproved changes".
image

Institution

  • Enable the student visualization of a submitted appeal.
  • Individual decisions statuses should not be displayed to the institution before the final decision is made and the form submission is marked as completed.
  • Institutions have access to see the final notes provided by the Ministry once the final decision in the form submission is made.
image image

Student

  • Form submission listed for the students.
image
  • Enable the student visualization of a submitted appeal.
  • Individual decisions statuses should not be displayed to the student before the final decision is made and the form submission is marked as completed.
  • While navigating from the "Financial Aid Application" (page above), the "back" allows the navigation to the source page.
image

Technical Changes

API

DTO

  • The decisionStatus was present twice in the DTOs to allow the currentDecision property to be completely undefined (FormSubmissionItemAPIOutDTO). This was changed to make the currentDecision as the only source for the decisionStatus returned from the API.
  • Ministry has specific DTOs while student and institution are sharing the same DTOs. The only different from them is the presence of a note description. Other than that, both clients also shared the decision status "emulation" that should be hidden till the final decision is given.

Controllers and services

  • The submission retrieval for student and institution is shared since they shared close to the same functionality.
  • The submission retrieval for the Ministry was kept separated because it does no seems worth to have it shared due to its different needs: item specific search, decision history, and authorization restricting data output.
  • Form submission services were split to allow its association to the specific Ministry/Student module without the need to carry all specific injected dependencies.
    • 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.ts shared between Student, Institution, and potentially any shared method needed by Ministry.

Outside PR Changes

Fixes from previous implementation

  • For pages rendered under the applications versions for the Ministry, API data change methods should prevent data changed from "versioned applications". For that sake, the decision submission and completion are now inspecting if the form submission has an associated application and not allowing the data change if the application is not the current version.

Back Target

  • Created an object to be provided to the components to allow a easy "back" redirect, also preventing properties to be added to the component only for the sake of redirect back.
  • Please see the examples along this PR and raise possible concerns. Minor improvements can be taken care.

Loading state improvements

  • Added some missing loading states.

Header Navigator Styles

  • Adjusted the styles of the header navigator.
    • Added underline on hover.
    • Changed cursor.
    • Prevent text highlight.
image

@andrewsignori-aot andrewsignori-aot self-assigned this Mar 4, 2026
@andrewsignori-aot andrewsignori-aot added Student Student Features Ministry Ministry Features SIMS-Api SIMS-Api Web portal labels Mar 4, 2026
Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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 of decisionStatus at the item root) and adds assessment DTO support for formSubmissionId.

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

  • previousDecisions is filtering using item.currentDecision.id, but currentDecision can be undefined for pending items. This will throw at runtime when hasApprovalAuthorization is true but no decision exists yet. Consider guarding for missing currentDecision (e.g., use optional chaining or filter only when currentDecision is 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

  • backTarget is passed to HeaderNavigator, but the footer "Back" button still calls goBack() which always routes to STUDENT_FORMS_HISTORY. When navigating here from the application summary route, the footer back will ignore the intended backTarget. Consider updating goBack() to use props.backTarget?.to when provided (same precedence as HeaderNavigator).

You can also share your feedback on Copilot code review. Take the survey.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No changes in this file from the previous PR. The file was only renamed.

Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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 under route-controllers/form-submission/_tests_/e2e following 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

  • previousDecisions is filtered using item.currentDecision.id even when item.currentDecision can 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 missing currentDecision in 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 the backTarget props) will be unreachable. Consolidate into a single route record and derive backTarget conditionally (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 new backTarget prop used by the header navigator. This will break the expected back navigation when arriving from application details. Consider updating goBack to prefer backTarget.to when provided (and fall back to forms history otherwise).
    sources/packages/web/src/services/http/FormSubmissionApi.ts:37
  • FormSubmissionStudentAPIOutDTO is still imported/used for MOCKED_SUBMISSIONS, but it no longer exists in the DTO exports. This will fail to compile, and the mocked payload shape still uses decisionStatus instead of the new currentDecision structure. Update/remove the mock and align getFormSubmissionSummary typings with the current DTOs.

You can also share your feedback on Copilot code review. Take the survey.

@andrewsignori-aot andrewsignori-aot marked this pull request as ready for review March 6, 2026 03:57
@sonarqubecloud
Copy link

sonarqubecloud bot commented Mar 6, 2026

@github-actions
Copy link

github-actions bot commented Mar 6, 2026

Backend Unit Tests Coverage Report

Totals Coverage
Statements: 20.26% ( 4555 / 22486 )
Methods: 9.59% ( 262 / 2733 )
Lines: 24.57% ( 3907 / 15902 )
Branches: 10.02% ( 386 / 3851 )

@github-actions
Copy link

github-actions bot commented Mar 6, 2026

E2E Workflow Workers Coverage Report

Totals Coverage
Statements: 75.23% ( 1066 / 1417 )
Methods: 78.38% ( 116 / 148 )
Lines: 78.68% ( 775 / 985 )
Branches: 61.62% ( 175 / 284 )

@github-actions
Copy link

github-actions bot commented Mar 6, 2026

E2E Queue Consumers Coverage Report

Totals Coverage
Statements: 85.68% ( 1616 / 1886 )
Methods: 85% ( 187 / 220 )
Lines: 88.64% ( 1287 / 1452 )
Branches: 66.36% ( 142 / 214 )

@github-actions
Copy link

github-actions bot commented Mar 6, 2026

E2E SIMS API Coverage Report

Totals Coverage
Statements: 75.56% ( 9450 / 12507 )
Methods: 73.76% ( 1088 / 1475 )
Lines: 80.2% ( 6858 / 8551 )
Branches: 60.62% ( 1504 / 2481 )

@dheepak-aot dheepak-aot self-requested a review March 6, 2026 21:16
submittedDate: true,
},
where: {
application: { id: applicationId },
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Minor. We generally use just student: { id: studentId }

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Ministry Ministry Features SIMS-Api SIMS-Api Student Student Features Web portal

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants