diff --git a/sources/packages/backend/apps/api/src/route-controllers/application/_tests_/e2e/application.aest.controller.getApplicationDetails.e2e-spec.ts b/sources/packages/backend/apps/api/src/route-controllers/application/_tests_/e2e/application.aest.controller.getApplicationDetails.e2e-spec.ts index 2af87ca72f..c9ebba7e1a 100644 --- a/sources/packages/backend/apps/api/src/route-controllers/application/_tests_/e2e/application.aest.controller.getApplicationDetails.e2e-spec.ts +++ b/sources/packages/backend/apps/api/src/route-controllers/application/_tests_/e2e/application.aest.controller.getApplicationDetails.e2e-spec.ts @@ -5,9 +5,12 @@ import { BEARER_AUTH_TYPE, createTestingAppModule, getAESTToken, + getExpectedOfferingNameAndPeriod, } from "../../../../testHelpers"; import { createE2EDataSources, + createFakeEducationProgramOffering, + createFakeStudentAssessment, E2EDataSources, saveFakeApplication, } from "@sims/test-utils"; @@ -15,8 +18,10 @@ import { Application, ApplicationData, ApplicationStatus, + AssessmentTriggerType, EducationProgramOffering, OfferingIntensity, + ProgramInfoStatus, } from "@sims/sims-db"; import { getUserFullName } from "../../../../utilities"; import { addDays, getISODateOnlyString } from "@sims/utilities"; @@ -255,6 +260,118 @@ describe("ApplicationAESTController(e2e)-getApplicationDetails", () => { ]); }); + it("Should return PIR summary in application data when the application has PIR status completed and dynamic data is loaded.", async () => { + // Arrange + const application = await saveFakeApplication( + db.dataSource, + {}, + { + applicationStatus: ApplicationStatus.Assessment, + offeringIntensity: OfferingIntensity.fullTime, + pirStatus: ProgramInfoStatus.completed, + }, + ); + + const savedOffering = application.currentAssessment!.offering!; + const token = await getAESTToken(AESTGroups.BusinessAdministrators); + const endpoint = `/aest/application/${application.id}`; + + // Act/Assert + await request(app.getHttpServer()) + .get(endpoint) + .auth(token, BEARER_AUTH_TYPE) + .expect(HttpStatus.OK) + .expect(({ body }) => + expect(body).toMatchObject({ + data: { + pirSummary: { + programName: savedOffering.educationProgram.name, + offeringName: getExpectedOfferingNameAndPeriod(savedOffering), + }, + }, + }), + ); + }); + + it("Should not return PIR summary in application data when the application does not have PIR status completed.", async () => { + // Arrange + const application = await saveFakeApplication( + db.dataSource, + {}, + { + applicationStatus: ApplicationStatus.Assessment, + offeringIntensity: OfferingIntensity.fullTime, + pirStatus: ProgramInfoStatus.notRequired, + }, + ); + const token = await getAESTToken(AESTGroups.BusinessAdministrators); + const endpoint = `/aest/application/${application.id}`; + + // Act/Assert + await request(app.getHttpServer()) + .get(endpoint) + .auth(token, BEARER_AUTH_TYPE) + .expect(HttpStatus.OK) + .expect(({ body }) => expect(body.data.pirSummary).toBeUndefined()); + }); + + it("Should return the PIR summary from the original assessment offering even when the application has a later reassessment with a different offering.", async () => { + // Arrange + // Create an application with PIR completed (original assessment). + const application = await saveFakeApplication( + db.dataSource, + {}, + { + applicationStatus: ApplicationStatus.Assessment, + offeringIntensity: OfferingIntensity.fullTime, + pirStatus: ProgramInfoStatus.completed, + }, + ); + const originalOffering = application.currentAssessment!.offering!; + // Create a different offering for the reassessment to confirm that the + // PIR summary always reflects the original assessment's offering. + // PIR is assessed once per application (tied to the original assessment), + // so a subsequent reassessment with a new offering must not affect the PIR summary. + const differentOffering = createFakeEducationProgramOffering({ + auditUser: application.student.user, + }); + differentOffering.parentOffering = differentOffering; + const savedDifferentOffering = + await db.educationProgramOffering.save(differentOffering); + const reassessment = createFakeStudentAssessment( + { + auditUser: application.student.user, + application, + offering: savedDifferentOffering, + }, + { + initialValue: { + triggerType: AssessmentTriggerType.StudentAppeal, + }, + }, + ); + application.currentAssessment = reassessment; + await db.application.save(application); + const token = await getAESTToken(AESTGroups.BusinessAdministrators); + const endpoint = `/aest/application/${application.id}`; + + // Act/Assert + await request(app.getHttpServer()) + .get(endpoint) + .auth(token, BEARER_AUTH_TYPE) + .expect(HttpStatus.OK) + .expect(({ body }) => + expect(body).toMatchObject({ + data: { + pirSummary: { + programName: originalOffering.educationProgram.name, + offeringName: getExpectedOfferingNameAndPeriod(originalOffering), + }, + }, + }), + ); + }); + afterAll(async () => { await app?.close(); }); diff --git a/sources/packages/backend/apps/api/src/route-controllers/application/_tests_/e2e/application.institutions.controller.getApplicationDetails.e2e-spec.ts b/sources/packages/backend/apps/api/src/route-controllers/application/_tests_/e2e/application.institutions.controller.getApplicationDetails.e2e-spec.ts index be021bb73f..2e93b0e148 100644 --- a/sources/packages/backend/apps/api/src/route-controllers/application/_tests_/e2e/application.institutions.controller.getApplicationDetails.e2e-spec.ts +++ b/sources/packages/backend/apps/api/src/route-controllers/application/_tests_/e2e/application.institutions.controller.getApplicationDetails.e2e-spec.ts @@ -5,6 +5,7 @@ import { BEARER_AUTH_TYPE, createTestingAppModule, getAuthRelatedEntities, + getExpectedOfferingNameAndPeriod, getInstitutionToken, INSTITUTION_BC_PUBLIC_ERROR_MESSAGE, INSTITUTION_STUDENT_DATA_ACCESS_ERROR_MESSAGE, @@ -22,6 +23,7 @@ import { EducationProgramOffering, InstitutionLocation, OfferingIntensity, + ProgramInfoStatus, } from "@sims/sims-db"; import { addDays, getISODateOnlyString } from "@sims/utilities"; import { getUserFullName } from "../../../../utilities"; @@ -275,6 +277,68 @@ describe("ApplicationInstitutionsController(e2e)-getApplicationDetails", () => { ); }); + it("Should return PIR summary in application data when the application has PIR status completed.", async () => { + // Arrange + const savedApplication = await saveFakeApplication( + db.dataSource, + { institutionLocation: collegeFLocation }, + { + applicationStatus: ApplicationStatus.Assessment, + offeringIntensity: OfferingIntensity.fullTime, + pirStatus: ProgramInfoStatus.completed, + }, + ); + const savedOffering = savedApplication.currentAssessment!.offering!; + const student = savedApplication.student; + const endpoint = `/institutions/application/student/${student.id}/application/${savedApplication.id}`; + const institutionUserToken = await getInstitutionToken( + InstitutionTokenTypes.CollegeFUser, + ); + + // Act/Assert + await request(app.getHttpServer()) + .get(endpoint) + .auth(institutionUserToken, BEARER_AUTH_TYPE) + .expect(HttpStatus.OK) + .expect(({ body }) => + expect(body).toMatchObject({ + data: { + pirSummary: { + programName: savedOffering.educationProgram.name, + offeringName: getExpectedOfferingNameAndPeriod(savedOffering), + }, + }, + }), + ); + }); + + it("Should not return PIR summary in application data when the application does not have PIR status completed.", async () => { + // Arrange + const savedApplication = await saveFakeApplication( + db.dataSource, + { institutionLocation: collegeFLocation }, + { + applicationStatus: ApplicationStatus.Assessment, + offeringIntensity: OfferingIntensity.fullTime, + pirStatus: ProgramInfoStatus.notRequired, + }, + ); + const student = savedApplication.student; + const endpoint = `/institutions/application/student/${student.id}/application/${savedApplication.id}`; + const institutionUserToken = await getInstitutionToken( + InstitutionTokenTypes.CollegeFUser, + ); + + // Act/Assert + await request(app.getHttpServer()) + .get(endpoint) + .auth(institutionUserToken, BEARER_AUTH_TYPE) + .expect(HttpStatus.OK) + .expect(({ body }) => { + expect(body.data.pirSummary).toBeUndefined(); + }); + }); + afterAll(async () => { await app?.close(); }); diff --git a/sources/packages/backend/apps/api/src/route-controllers/application/_tests_/e2e/application.students.controller.getApplication.e2e-spec.ts b/sources/packages/backend/apps/api/src/route-controllers/application/_tests_/e2e/application.students.controller.getApplication.e2e-spec.ts index d312c9539f..cd93d42c6a 100644 --- a/sources/packages/backend/apps/api/src/route-controllers/application/_tests_/e2e/application.students.controller.getApplication.e2e-spec.ts +++ b/sources/packages/backend/apps/api/src/route-controllers/application/_tests_/e2e/application.students.controller.getApplication.e2e-spec.ts @@ -4,6 +4,7 @@ import { BEARER_AUTH_TYPE, createTestingAppModule, FakeStudentUsersTypes, + getExpectedOfferingNameAndPeriod, getStudentToken, mockJWTUserInfo, resetMockJWTUserInfo, @@ -203,6 +204,13 @@ describe("ApplicationStudentsController(e2e)-getApplication", () => { programName: "My Program", workflowName: "", programDescription: "This is my program.", + pirSummary: { + programName: + application.currentAssessment!.offering!.educationProgram.name, + offeringName: getExpectedOfferingNameAndPeriod( + application.currentAssessment!.offering!, + ), + }, }, applicationStatus: application.applicationStatus, applicationEditStatus: application.applicationEditStatus, @@ -308,6 +316,66 @@ describe("ApplicationStudentsController(e2e)-getApplication", () => { }); }); + it("Should return PIR summary in application data when the application has PIR status completed.", async () => { + // Arrange + const application = await saveFakeApplication( + db.dataSource, + { student }, + { + applicationStatus: ApplicationStatus.Assessment, + offeringIntensity: OfferingIntensity.fullTime, + pirStatus: ProgramInfoStatus.completed, + }, + ); + const savedOffering = application.currentAssessment!.offering!; + const endpoint = `/students/application/${application.id}`; + const token = await getStudentToken( + FakeStudentUsersTypes.FakeStudentUserType1, + ); + await mockJWTUserInfo(appModule, application.student.user); + + // Act/Assert + await request(app.getHttpServer()) + .get(endpoint) + .auth(token, BEARER_AUTH_TYPE) + .expect(HttpStatus.OK) + .expect(({ body }) => + expect(body).toMatchObject({ + data: { + pirSummary: { + programName: savedOffering.educationProgram.name, + offeringName: getExpectedOfferingNameAndPeriod(savedOffering), + }, + }, + }), + ); + }); + + it("Should not return PIR summary in application data when the application does not have PIR status completed.", async () => { + // Arrange + const application = await saveFakeApplication( + db.dataSource, + { student }, + { + applicationStatus: ApplicationStatus.Assessment, + offeringIntensity: OfferingIntensity.fullTime, + pirStatus: ProgramInfoStatus.notRequired, + }, + ); + const endpoint = `/students/application/${application.id}`; + const token = await getStudentToken( + FakeStudentUsersTypes.FakeStudentUserType1, + ); + await mockJWTUserInfo(appModule, application.student.user); + + // Act/Assert + await request(app.getHttpServer()) + .get(endpoint) + .auth(token, BEARER_AUTH_TYPE) + .expect(HttpStatus.OK) + .expect(({ body }) => expect(body.data.pirSummary).toBeUndefined()); + }); + afterAll(async () => { await app?.close(); }); diff --git a/sources/packages/backend/apps/api/src/route-controllers/application/application.aest.controller.ts b/sources/packages/backend/apps/api/src/route-controllers/application/application.aest.controller.ts index aaab15699c..6939d22392 100644 --- a/sources/packages/backend/apps/api/src/route-controllers/application/application.aest.controller.ts +++ b/sources/packages/backend/apps/api/src/route-controllers/application/application.aest.controller.ts @@ -100,13 +100,13 @@ export class ApplicationAESTController extends BaseController { )); const currentReadOnlyDataPromise = this.applicationControllerService.generateApplicationFormData( - application.data, + application, ); // If there is a previous application, generate its read-only data. const previousReadOnlyDataPromise = previousApplicationVersion && this.applicationControllerService.generateApplicationFormData( - previousApplicationVersion.data, + previousApplicationVersion, ); // Wait for both promises to resolve. [currentReadOnlyData, previousReadOnlyData] = await Promise.all([ diff --git a/sources/packages/backend/apps/api/src/route-controllers/application/application.controller.service.ts b/sources/packages/backend/apps/api/src/route-controllers/application/application.controller.service.ts index d123627e8e..b40894219d 100644 --- a/sources/packages/backend/apps/api/src/route-controllers/application/application.controller.service.ts +++ b/sources/packages/backend/apps/api/src/route-controllers/application/application.controller.service.ts @@ -69,7 +69,9 @@ import { ApplicationEditStatusInProgress, APPLICATION_EDIT_STATUS_IN_PROGRESS_VALUES, DynamicFormType, + ProgramInfoStatus, StudentScholasticStandingChangeType, + AssessmentTriggerType, } from "@sims/sims-db"; import { ApiProcessError } from "../../types"; import { ACTIVE_STUDENT_RESTRICTION } from "../../constants"; @@ -110,18 +112,52 @@ export class ApplicationControllerService { * Add location, program and offering labels * and reset the dropdown value for non * designated location and not approved - * programs. - * @param data application data - * @returns ApplicationFormData + * programs. When application is provided, also enriches the form data + * with the PIR outcome summary when the PIR has been completed. + * @param data application data. + * @param application application entity. When provided, the PIR outcome + * summary is added to the form data if the PIR status is completed. + * @returns ApplicationFormData. */ async generateApplicationFormData( - data: ApplicationData, + application: Application, ): Promise { const additionalFormData = {} as ApplicationFormData; - await this.processSelectedLocation(data, additionalFormData); - await this.processSelectedProgram(data, additionalFormData); - await this.processSelectedOffering(data, additionalFormData); - return { ...data, ...additionalFormData }; + await this.processSelectedLocation(application.data, additionalFormData); + await this.processSelectedProgram(application.data, additionalFormData); + await this.processSelectedOffering(application.data, additionalFormData); + this.processPIRSummary(application, additionalFormData); + return { ...application.data, ...additionalFormData }; + } + + /** + * Enriches the application form data with the PIR outcome summary when the PIR + * has been completed. The summary data is sourced from the original assessment + * offering, since PIR is assessed once per application and is tied to the + * original submission, regardless of any subsequent reassessments. + * @param application application entity loaded with the original assessment offering + * and education program data. + * @param formData form data object to be enriched with the PIR summary. + */ + private processPIRSummary( + application: Application | undefined, + formData: ApplicationFormData, + ): void { + if (application?.pirStatus !== ProgramInfoStatus.completed) { + return; + } + const originalAssessment = application.studentAssessments?.find( + (assessment) => + assessment.triggerType === AssessmentTriggerType.OriginalAssessment, + ); + if (!originalAssessment?.offering) { + return; + } + const offering = originalAssessment.offering; + formData.pirSummary = { + programName: offering.educationProgram.name, + offeringName: getOfferingNameAndPeriod(offering), + }; } /** diff --git a/sources/packages/backend/apps/api/src/route-controllers/application/application.institutions.controller.ts b/sources/packages/backend/apps/api/src/route-controllers/application/application.institutions.controller.ts index 368a57f4ff..d0d7a0baa9 100644 --- a/sources/packages/backend/apps/api/src/route-controllers/application/application.institutions.controller.ts +++ b/sources/packages/backend/apps/api/src/route-controllers/application/application.institutions.controller.ts @@ -75,7 +75,7 @@ export class ApplicationInstitutionsController extends BaseController { if (loadDynamicData) { application.data = await this.applicationControllerService.generateApplicationFormData( - application.data, + application, ); } return this.applicationControllerService.transformToApplicationDTO( diff --git a/sources/packages/backend/apps/api/src/route-controllers/application/application.students.controller.ts b/sources/packages/backend/apps/api/src/route-controllers/application/application.students.controller.ts index 7d1b8d1d50..fdc1861039 100644 --- a/sources/packages/backend/apps/api/src/route-controllers/application/application.students.controller.ts +++ b/sources/packages/backend/apps/api/src/route-controllers/application/application.students.controller.ts @@ -118,7 +118,7 @@ export class ApplicationStudentsController extends BaseController { const applicationDataPromise = this.applicationControllerService.generateApplicationFormData( - application.data, + application, ); const firstCOEPromise = this.confirmationOfEnrollmentService.getFirstDisbursementScheduleByApplication( diff --git a/sources/packages/backend/apps/api/src/route-controllers/application/models/application.dto.ts b/sources/packages/backend/apps/api/src/route-controllers/application/models/application.dto.ts index 587e8465f6..d498354459 100644 --- a/sources/packages/backend/apps/api/src/route-controllers/application/models/application.dto.ts +++ b/sources/packages/backend/apps/api/src/route-controllers/application/models/application.dto.ts @@ -102,6 +102,22 @@ export interface ApplicationFormData extends ApplicationData { id?: number; name?: string; }; + + /** + * PIR outcome summary data injected for the read-only PIR summary section + * in the application form. Only populated when pirStatus is completed. + */ + pirSummary?: { + /** + * Program name from the completed PIR in the application's original assessment. + */ + programName?: string; + /** + * Formatted offering name (including year of study and dates) from the + * completed PIR in the application's original assessment. + */ + offeringName?: string; + }; } class SupportingUserParentAPIOutDTO { diff --git a/sources/packages/backend/apps/api/src/services/application/application.service.ts b/sources/packages/backend/apps/api/src/services/application/application.service.ts index 9746bf0b12..9d29e21717 100644 --- a/sources/packages/backend/apps/api/src/services/application/application.service.ts +++ b/sources/packages/backend/apps/api/src/services/application/application.service.ts @@ -935,7 +935,9 @@ export class ApplicationService extends RecordDataModelService { * Student id/ institution id can be provided for authorization purposes. * @param applicationId application id. * @param options object that should contain: - * - `loadDynamicData` indicates if the dynamic data(JSONB) should be loaded. + * - `loadDynamicData` indicates if the dynamic data(JSONB) should be loaded. When true, + * also loads the additional offering details (name, year of study, and education program) + * from the original assessment needed to display the PIR outcome summary. * - `studentId` student id. * - `entityManager` entity manager to be used for the query. Useful when * it needs to be executed in a transaction. @@ -1015,6 +1017,25 @@ export class ApplicationService extends RecordDataModelService { lastName: true, }, }, + ...(options?.loadDynamicData + ? { + studentAssessments: { + id: true, + triggerType: true, + offering: { + id: true, + name: true, + yearOfStudy: true, + studyStartDate: true, + studyEndDate: true, + educationProgram: { + id: true, + name: true, + }, + }, + }, + } + : {}), }, relations: { applicationException: true, @@ -1026,6 +1047,9 @@ export class ApplicationService extends RecordDataModelService { precedingApplication: { currentAssessment: { offering: true, studentAppeal: true }, }, + ...(options?.loadDynamicData + ? { studentAssessments: { offering: { educationProgram: true } } } + : {}), }, where: { id: applicationId, diff --git a/sources/packages/backend/apps/api/src/testHelpers/index.ts b/sources/packages/backend/apps/api/src/testHelpers/index.ts index fd98af27f6..1a46651292 100644 --- a/sources/packages/backend/apps/api/src/testHelpers/index.ts +++ b/sources/packages/backend/apps/api/src/testHelpers/index.ts @@ -7,3 +7,4 @@ export * from "./testing-modules/testing-modules-helper"; export * from "./auth"; export * from "./fake-entities"; export * from "./program-year/program-year-helper"; +export * from "./offering/offering-utils"; diff --git a/sources/packages/backend/apps/api/src/testHelpers/offering/offering-utils.ts b/sources/packages/backend/apps/api/src/testHelpers/offering/offering-utils.ts new file mode 100644 index 0000000000..713e0f500a --- /dev/null +++ b/sources/packages/backend/apps/api/src/testHelpers/offering/offering-utils.ts @@ -0,0 +1,20 @@ +import { EducationProgramOffering } from "@sims/sims-db"; +import { getDateOnlyFormat } from "@sims/utilities/date-utils"; + +/** + * Builds the expected offering name string for test assertions, independent + * of the production utility {@link getOfferingNameAndPeriod}. + * Format: "Offering Name (MMM DD YYYY - MMM DD YYYY) - Year X" + * @param offering offering with name, studyStartDate, studyEndDate and yearOfStudy. + * @returns formatted offering name string. + */ +export function getExpectedOfferingNameAndPeriod( + offering: Pick< + EducationProgramOffering, + "name" | "studyStartDate" | "studyEndDate" | "yearOfStudy" + >, +): string { + const startDate = getDateOnlyFormat(offering.studyStartDate); + const endDate = getDateOnlyFormat(offering.studyEndDate); + return `${offering.name} (${startDate} - ${endDate}) - Year ${offering.yearOfStudy}`; +} diff --git a/sources/packages/backend/apps/api/src/utilities/eductaion-program-offering-utils.ts b/sources/packages/backend/apps/api/src/utilities/education-program-offering-utils.ts similarity index 100% rename from sources/packages/backend/apps/api/src/utilities/eductaion-program-offering-utils.ts rename to sources/packages/backend/apps/api/src/utilities/education-program-offering-utils.ts diff --git a/sources/packages/backend/apps/api/src/utilities/index.ts b/sources/packages/backend/apps/api/src/utilities/index.ts index b4dac31e42..da32d32c41 100644 --- a/sources/packages/backend/apps/api/src/utilities/index.ts +++ b/sources/packages/backend/apps/api/src/utilities/index.ts @@ -1,7 +1,7 @@ export * from "./system-configurations-constants"; export * from "./application-utils"; export * from "./auth-utils"; -export * from "./eductaion-program-offering-utils"; +export * from "./education-program-offering-utils"; export * from "./student-utils"; export * from "./credential-type-utils"; export * from "./upload-utils"; diff --git a/sources/packages/forms/src/form-definitions/sfaa2022-23.json b/sources/packages/forms/src/form-definitions/sfaa2022-23.json index 6055167bf2..4d755ff224 100644 --- a/sources/packages/forms/src/form-definitions/sfaa2022-23.json +++ b/sources/packages/forms/src/form-definitions/sfaa2022-23.json @@ -885,6 +885,63 @@ }, "lockKey": true }, + { + "key": "pirSummaryPanel", + "label": "Program information request summary", + "input": false, + "customConditional": "show = !!data.pirSummary;", + "type": "panel", + "hideLabel": true, + "tableView": false, + "collapsible": false, + "components": [ + { + "label": "HTML", + "tag": "h4", + "className": "category-header-medium-small", + "attrs": [{ "attr": "", "value": "" }], + "content": "Program information request summary", + "refreshOnChange": false, + "key": "pirSummaryHeader", + "type": "htmlelement", + "input": false, + "tableView": false + }, + { + "type": "container", + "key": "pirSummary", + "input": true, + "tableView": false, + "label": "PIR Summary", + "hideLabel": true, + "persistent": false, + "components": [ + { + "label": "Program name", + "disabled": true, + "tableView": true, + "persistent": false, + "validateWhenHidden": false, + "key": "programName", + "type": "textfield", + "input": true, + "lockKey": true + }, + { + "label": "Offering name", + "disabled": true, + "tableView": true, + "persistent": false, + "validateWhenHidden": false, + "key": "offeringName", + "type": "textfield", + "input": true, + "lockKey": true + } + ] + } + ] + }, { "key": "programStudyperiodwarning", "label": "Study period end date warning", diff --git a/sources/packages/forms/src/form-definitions/sfaa2023-24.json b/sources/packages/forms/src/form-definitions/sfaa2023-24.json index e71c804185..8cd9ddf826 100644 --- a/sources/packages/forms/src/form-definitions/sfaa2023-24.json +++ b/sources/packages/forms/src/form-definitions/sfaa2023-24.json @@ -885,6 +885,68 @@ }, "lockKey": true }, + { + "key": "pirSummaryPanel", + "label": "Program information request summary", + "input": false, + "customConditional": "show = !!data.pirSummary;", + "type": "panel", + "hideLabel": true, + "tableView": false, + "collapsible": false, + "components": [ + { + "label": "HTML", + "tag": "h4", + "className": "category-header-medium-small", + "attrs": [ + { + "attr": "", + "value": "" + } + ], + "content": "Program information request summary", + "refreshOnChange": false, + "key": "pirSummaryHeader", + "type": "htmlelement", + "input": false, + "tableView": false + }, + { + "type": "container", + "key": "pirSummary", + "input": true, + "tableView": false, + "label": "PIR Summary", + "hideLabel": true, + "persistent": false, + "components": [ + { + "label": "Program name", + "disabled": true, + "tableView": true, + "persistent": false, + "validateWhenHidden": false, + "key": "programName", + "type": "textfield", + "input": true, + "lockKey": true + }, + { + "label": "Offering name", + "disabled": true, + "tableView": true, + "persistent": false, + "validateWhenHidden": false, + "key": "offeringName", + "type": "textfield", + "input": true, + "lockKey": true + } + ] + } + ] + }, { "key": "programStudyperiodwarning", "label": "Study period end date warning", diff --git a/sources/packages/forms/src/form-definitions/sfaa2024-25.json b/sources/packages/forms/src/form-definitions/sfaa2024-25.json index 77b40f1648..706a4ae08a 100644 --- a/sources/packages/forms/src/form-definitions/sfaa2024-25.json +++ b/sources/packages/forms/src/form-definitions/sfaa2024-25.json @@ -891,6 +891,68 @@ }, "lockKey": true }, + { + "key": "pirSummaryPanel", + "label": "Program information request summary", + "input": false, + "customConditional": "show = !!data.pirSummary;", + "type": "panel", + "hideLabel": true, + "tableView": false, + "collapsible": false, + "components": [ + { + "label": "HTML", + "tag": "h4", + "className": "category-header-medium-small", + "attrs": [ + { + "attr": "", + "value": "" + } + ], + "content": "Program information request summary", + "refreshOnChange": false, + "key": "pirSummaryHeader", + "type": "htmlelement", + "input": false, + "tableView": false + }, + { + "type": "container", + "key": "pirSummary", + "input": true, + "tableView": false, + "label": "PIR Summary", + "hideLabel": true, + "persistent": false, + "components": [ + { + "label": "Program name", + "disabled": true, + "tableView": true, + "persistent": false, + "validateWhenHidden": false, + "key": "programName", + "type": "textfield", + "input": true, + "lockKey": true + }, + { + "label": "Offering name", + "disabled": true, + "tableView": true, + "persistent": false, + "validateWhenHidden": false, + "key": "offeringName", + "type": "textfield", + "input": true, + "lockKey": true + } + ] + } + ] + }, { "key": "programStudyperiodwarning", "label": "Study period end date warning", diff --git a/sources/packages/forms/src/form-definitions/sfaa2025-26-ft.json b/sources/packages/forms/src/form-definitions/sfaa2025-26-ft.json index 44fab19db9..11d48ce876 100644 --- a/sources/packages/forms/src/form-definitions/sfaa2025-26-ft.json +++ b/sources/packages/forms/src/form-definitions/sfaa2025-26-ft.json @@ -1204,6 +1204,68 @@ }, "lockKey": true }, + { + "key": "pirSummaryPanel", + "label": "Program information request summary", + "input": false, + "customConditional": "show = !!data.pirSummary;", + "type": "panel", + "hideLabel": true, + "tableView": false, + "collapsible": false, + "components": [ + { + "label": "HTML", + "tag": "h4", + "className": "category-header-medium-small", + "attrs": [ + { + "attr": "", + "value": "" + } + ], + "content": "Program information request summary", + "refreshOnChange": false, + "key": "pirSummaryHeader", + "type": "htmlelement", + "input": false, + "tableView": false + }, + { + "type": "container", + "key": "pirSummary", + "input": true, + "tableView": false, + "label": "PIR Summary", + "hideLabel": true, + "persistent": false, + "components": [ + { + "label": "Program name", + "disabled": true, + "tableView": true, + "persistent": false, + "validateWhenHidden": false, + "key": "programName", + "type": "textfield", + "input": true, + "lockKey": true + }, + { + "label": "Offering name", + "disabled": true, + "tableView": true, + "persistent": false, + "validateWhenHidden": false, + "key": "offeringName", + "type": "textfield", + "input": true, + "lockKey": true + } + ] + } + ] + }, { "title": "PIR Resubmission", "customClass": "panel-border-none", diff --git a/sources/packages/forms/src/form-definitions/sfaa2025-26-pt.json b/sources/packages/forms/src/form-definitions/sfaa2025-26-pt.json index 1aeec75e07..ef4cfbe2da 100644 --- a/sources/packages/forms/src/form-definitions/sfaa2025-26-pt.json +++ b/sources/packages/forms/src/form-definitions/sfaa2025-26-pt.json @@ -1161,6 +1161,68 @@ }, "lockKey": true }, + { + "key": "pirSummaryPanel", + "label": "Program information request summary", + "input": false, + "customConditional": "show = !!data.pirSummary;", + "type": "panel", + "hideLabel": true, + "tableView": false, + "collapsible": false, + "components": [ + { + "label": "HTML", + "tag": "h4", + "className": "category-header-medium-small", + "attrs": [ + { + "attr": "", + "value": "" + } + ], + "content": "Program information request summary", + "refreshOnChange": false, + "key": "pirSummaryHeader", + "type": "htmlelement", + "input": false, + "tableView": false + }, + { + "type": "container", + "key": "pirSummary", + "input": true, + "tableView": false, + "label": "PIR Summary", + "hideLabel": true, + "persistent": false, + "components": [ + { + "label": "Program name", + "disabled": true, + "tableView": true, + "persistent": false, + "validateWhenHidden": false, + "key": "programName", + "type": "textfield", + "input": true, + "lockKey": true + }, + { + "label": "Offering name", + "disabled": true, + "tableView": true, + "persistent": false, + "validateWhenHidden": false, + "key": "offeringName", + "type": "textfield", + "input": true, + "lockKey": true + } + ] + } + ] + }, { "title": "PIR Resubmission", "customClass": "panel-border-none", diff --git a/sources/packages/forms/src/form-definitions/sfaa2026-27-ft.json b/sources/packages/forms/src/form-definitions/sfaa2026-27-ft.json index b18077d69a..d6aff31ea7 100644 --- a/sources/packages/forms/src/form-definitions/sfaa2026-27-ft.json +++ b/sources/packages/forms/src/form-definitions/sfaa2026-27-ft.json @@ -1204,6 +1204,68 @@ }, "lockKey": true }, + { + "key": "pirSummaryPanel", + "label": "Program information request summary", + "input": false, + "customConditional": "show = !!data.pirSummary;", + "type": "panel", + "hideLabel": true, + "tableView": false, + "collapsible": false, + "components": [ + { + "label": "HTML", + "tag": "h4", + "className": "category-header-medium-small", + "attrs": [ + { + "attr": "", + "value": "" + } + ], + "content": "Program information request summary", + "refreshOnChange": false, + "key": "pirSummaryHeader", + "type": "htmlelement", + "input": false, + "tableView": false + }, + { + "type": "container", + "key": "pirSummary", + "input": true, + "tableView": false, + "label": "PIR Summary", + "hideLabel": true, + "persistent": false, + "components": [ + { + "label": "Program name", + "disabled": true, + "tableView": true, + "persistent": false, + "validateWhenHidden": false, + "key": "programName", + "type": "textfield", + "input": true, + "lockKey": true + }, + { + "label": "Offering name", + "disabled": true, + "tableView": true, + "persistent": false, + "validateWhenHidden": false, + "key": "offeringName", + "type": "textfield", + "input": true, + "lockKey": true + } + ] + } + ] + }, { "title": "PIR Resubmission", "customClass": "panel-border-none", diff --git a/sources/packages/forms/src/form-definitions/sfaa2026-27-pt.json b/sources/packages/forms/src/form-definitions/sfaa2026-27-pt.json index 1837acf081..b8a6a9a5a7 100644 --- a/sources/packages/forms/src/form-definitions/sfaa2026-27-pt.json +++ b/sources/packages/forms/src/form-definitions/sfaa2026-27-pt.json @@ -1161,6 +1161,68 @@ }, "lockKey": true }, + { + "key": "pirSummaryPanel", + "label": "Program information request summary", + "input": false, + "customConditional": "show = !!data.pirSummary;", + "type": "panel", + "hideLabel": true, + "tableView": false, + "collapsible": false, + "components": [ + { + "label": "HTML", + "tag": "h4", + "className": "category-header-medium-small", + "attrs": [ + { + "attr": "", + "value": "" + } + ], + "content": "Program information request summary", + "refreshOnChange": false, + "key": "pirSummaryHeader", + "type": "htmlelement", + "input": false, + "tableView": false + }, + { + "type": "container", + "key": "pirSummary", + "input": true, + "tableView": false, + "label": "PIR Summary", + "hideLabel": true, + "persistent": false, + "components": [ + { + "label": "Program name", + "disabled": true, + "tableView": true, + "persistent": false, + "validateWhenHidden": false, + "key": "programName", + "type": "textfield", + "input": true, + "lockKey": true + }, + { + "label": "Offering name", + "disabled": true, + "tableView": true, + "persistent": false, + "validateWhenHidden": false, + "key": "offeringName", + "type": "textfield", + "input": true, + "lockKey": true + } + ] + } + ] + }, { "title": "PIR Resubmission", "customClass": "panel-border-none",