From e21ad16193269059f4c6f2e617ffa3fffe3e6724 Mon Sep 17 00:00:00 2001 From: Courtney Myers Date: Fri, 17 Jan 2025 15:09:18 -0500 Subject: [PATCH 01/13] Add env variable for BAP REST API version number, and use it in establishing the BAP connection --- app/server/.env.example | 1 + app/server/app/index.js | 1 + app/server/app/utilities/bap.js | 3 ++- 3 files changed, 4 insertions(+), 1 deletion(-) diff --git a/app/server/.env.example b/app/server/.env.example index 1e025a36..5e36ba60 100644 --- a/app/server/.env.example +++ b/app/server/.env.example @@ -44,6 +44,7 @@ FORMIO_BASE_URL= FORMIO_PROJECT_NAME= FORMIO_API_KEY= FORMIO_DUPLICATES_API_KEY= +BAP_REST_API_VERSION= BAP_CLIENT_ID= BAP_CLIENT_SECRET= BAP_URL= diff --git a/app/server/app/index.js b/app/server/app/index.js index 4e941af3..df208ab2 100644 --- a/app/server/app/index.js +++ b/app/server/app/index.js @@ -62,6 +62,7 @@ const requiredEnvironmentVariables = [ "FORMIO_BASE_URL", "FORMIO_PROJECT_NAME", "FORMIO_API_KEY", + "BAP_REST_API_VERSION", "S3_PUBLIC_BUCKET", "S3_PUBLIC_REGION", ]; diff --git a/app/server/app/utilities/bap.js b/app/server/app/utilities/bap.js index 6af72079..2aa95375 100644 --- a/app/server/app/utilities/bap.js +++ b/app/server/app/utilities/bap.js @@ -453,6 +453,7 @@ const { submissionPeriodOpen } = require("../config/formio"); const { SERVER_URL, + BAP_REST_API_VERSION, BAP_CLIENT_ID, BAP_CLIENT_SECRET, BAP_URL, @@ -466,7 +467,7 @@ const { */ function setupConnection(req) { const bapConnection = new jsforce.Connection({ - version: "62.0", + version: BAP_REST_API_VERSION, oauth2: { clientId: BAP_CLIENT_ID, clientSecret: BAP_CLIENT_SECRET, From 19ba72724506cb73f99cb924446395fa5e569681 Mon Sep 17 00:00:00 2001 From: Courtney Myers Date: Fri, 17 Jan 2025 15:10:29 -0500 Subject: [PATCH 02/13] Add BAP env variables to app's required env variables array, and remove not used FORMIO_DUPLICATES_API_KEY env variable from example .env file --- app/server/.env.example | 1 - app/server/app/index.js | 5 +++++ 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/app/server/.env.example b/app/server/.env.example index 5e36ba60..8ed5bd55 100644 --- a/app/server/.env.example +++ b/app/server/.env.example @@ -43,7 +43,6 @@ FORMIO_2024_CHANGE_PATH= FORMIO_BASE_URL= FORMIO_PROJECT_NAME= FORMIO_API_KEY= -FORMIO_DUPLICATES_API_KEY= BAP_REST_API_VERSION= BAP_CLIENT_ID= BAP_CLIENT_SECRET= diff --git a/app/server/app/index.js b/app/server/app/index.js index df208ab2..469ae6ea 100644 --- a/app/server/app/index.js +++ b/app/server/app/index.js @@ -63,6 +63,11 @@ const requiredEnvironmentVariables = [ "FORMIO_PROJECT_NAME", "FORMIO_API_KEY", "BAP_REST_API_VERSION", + "BAP_CLIENT_ID", + "BAP_CLIENT_SECRET", + "BAP_URL", + "BAP_USER", + "BAP_PASSWORD", "S3_PUBLIC_BUCKET", "S3_PUBLIC_REGION", ]; From c06bfec43b41ce19326f8be74178d1a6dfd7866c Mon Sep 17 00:00:00 2001 From: Courtney Myers Date: Fri, 17 Jan 2025 15:14:46 -0500 Subject: [PATCH 03/13] Update GitHub Actions workflows to include BAP REST API version env --- .github/workflows/dev.yml | 2 ++ .github/workflows/staging.yml | 2 ++ 2 files changed, 4 insertions(+) diff --git a/.github/workflows/dev.yml b/.github/workflows/dev.yml index 0a13122b..97cafcae 100644 --- a/.github/workflows/dev.yml +++ b/.github/workflows/dev.yml @@ -75,6 +75,7 @@ jobs: FORMIO_PROJECT_NAME: ${{ secrets.FORMIO_PROJECT_NAME }} FORMIO_API_KEY: ${{ secrets.FORMIO_API_KEY }} FORMIO_PKG_AUTH_TOKEN: ${{ secrets.FORMIO_PKG_AUTH_TOKEN }} + BAP_REST_API_VERSION: ${{ secrets.BAP_REST_API_VERSION }} BAP_CLIENT_ID: ${{ secrets.BAP_CLIENT_ID }} BAP_CLIENT_SECRET: ${{ secrets.BAP_CLIENT_SECRET }} BAP_URL: ${{ secrets.BAP_URL }} @@ -177,6 +178,7 @@ jobs: cf set-env $APP_NAME "FORMIO_BASE_URL" "$FORMIO_BASE_URL" > /dev/null cf set-env $APP_NAME "FORMIO_PROJECT_NAME" "$FORMIO_PROJECT_NAME" > /dev/null cf set-env $APP_NAME "FORMIO_API_KEY" "$FORMIO_API_KEY" > /dev/null + cf set-env $APP_NAME "BAP_REST_API_VERSION" "$BAP_REST_API_VERSION" > /dev/null cf set-env $APP_NAME "BAP_CLIENT_ID" "$BAP_CLIENT_ID" > /dev/null cf set-env $APP_NAME "BAP_CLIENT_SECRET" "$BAP_CLIENT_SECRET" > /dev/null cf set-env $APP_NAME "BAP_URL" "$BAP_URL" > /dev/null diff --git a/.github/workflows/staging.yml b/.github/workflows/staging.yml index 6b712ac2..22c4658c 100644 --- a/.github/workflows/staging.yml +++ b/.github/workflows/staging.yml @@ -75,6 +75,7 @@ jobs: FORMIO_PROJECT_NAME: ${{ secrets.FORMIO_PROJECT_NAME }} FORMIO_API_KEY: ${{ secrets.FORMIO_API_KEY }} FORMIO_PKG_AUTH_TOKEN: ${{ secrets.FORMIO_PKG_AUTH_TOKEN }} + BAP_REST_API_VERSION: ${{ secrets.BAP_REST_API_VERSION }} BAP_CLIENT_ID: ${{ secrets.BAP_CLIENT_ID }} BAP_CLIENT_SECRET: ${{ secrets.BAP_CLIENT_SECRET }} BAP_URL: ${{ secrets.BAP_URL }} @@ -177,6 +178,7 @@ jobs: cf set-env $APP_NAME "FORMIO_BASE_URL" "$FORMIO_BASE_URL" > /dev/null cf set-env $APP_NAME "FORMIO_PROJECT_NAME" "$FORMIO_PROJECT_NAME" > /dev/null cf set-env $APP_NAME "FORMIO_API_KEY" "$FORMIO_API_KEY" > /dev/null + cf set-env $APP_NAME "BAP_REST_API_VERSION" "$BAP_REST_API_VERSION" > /dev/null cf set-env $APP_NAME "BAP_CLIENT_ID" "$BAP_CLIENT_ID" > /dev/null cf set-env $APP_NAME "BAP_CLIENT_SECRET" "$BAP_CLIENT_SECRET" > /dev/null cf set-env $APP_NAME "BAP_URL" "$BAP_URL" > /dev/null From 42f41d2ef63e1ec34cd5e0c00504139cb3929e33 Mon Sep 17 00:00:00 2001 From: Courtney Myers Date: Fri, 17 Jan 2025 15:31:18 -0500 Subject: [PATCH 04/13] Prefix unused function parameters with an underscore --- app/server/app/routes/config.js | 2 +- app/server/app/routes/status.js | 2 +- app/server/app/utilities/formio.js | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/app/server/app/routes/config.js b/app/server/app/routes/config.js index 03c4408e..5064c34f 100644 --- a/app/server/app/routes/config.js +++ b/app/server/app/routes/config.js @@ -10,7 +10,7 @@ const router = express.Router(); router.use(ensureAuthenticated); // --- get CSB app specific configuration -router.get("/", (req, res) => { +router.get("/", (_req, res) => { // NOTE: fallback to current year if CSB_REBATE_YEAR is not set const date = new Date(); const year = date.getFullYear().toString(); diff --git a/app/server/app/routes/status.js b/app/server/app/routes/status.js index 4f5a136e..49522570 100644 --- a/app/server/app/routes/status.js +++ b/app/server/app/routes/status.js @@ -42,7 +42,7 @@ function verifySchema({ schema, substring }) { const router = express.Router(); -router.get("/app", (req, res) => { +router.get("/app", (_req, res) => { return res.json({ status: true }); }); diff --git a/app/server/app/utilities/formio.js b/app/server/app/utilities/formio.js index 3878932b..5f0e82bf 100644 --- a/app/server/app/utilities/formio.js +++ b/app/server/app/utilities/formio.js @@ -1999,7 +1999,7 @@ function updateCRFSubmission({ rebateYear, req, res }) { return res.status(errorStatus).json({ message: errorMessage }); }); }) - .catch((error) => { + .catch((_error) => { const logMessage = `User with email '${mail}' attempted to update ${rebateYear} CRF ` + `submission '${rebateId}' when the CSB CRF enrollment period was closed.`; From 415c707a5bab4eafef1e49f92cd1902e7a087364 Mon Sep 17 00:00:00 2001 From: Courtney Myers Date: Tue, 21 Jan 2025 18:03:03 -0500 Subject: [PATCH 05/13] Update queryBapFor2024PRFData to include 2024 FRF contacts record types data --- app/server/app/utilities/bap.js | 62 ++++++++++++++++++++++++++++++++- 1 file changed, 61 insertions(+), 1 deletion(-) diff --git a/app/server/app/utilities/bap.js b/app/server/app/utilities/bap.js index 2aa95375..d7496b13 100644 --- a/app/server/app/utilities/bap.js +++ b/app/server/app/utilities/bap.js @@ -250,6 +250,7 @@ const { submissionPeriodOpen } = require("../config/formio"); * Primary_Applicant__r: { * attributes: { type: "Contact", url: string } * Id: string + * RecordTypeId: string * FirstName: string * LastName: string * Title: string @@ -259,6 +260,7 @@ const { submissionPeriodOpen } = require("../config/formio"); * Alternate_Applicant__r: { * attributes: { type: "Contact", url: string } * Id: string + * RecordTypeId: string * FirstName: string * LastName: string * Title: string @@ -277,6 +279,7 @@ const { submissionPeriodOpen } = require("../config/formio"); * School_District_Contact__r: { * attributes: { type: "Contact", url: string } * Id: string + * RecordTypeId: string * FirstName: string * LastName: string * Title: string @@ -320,6 +323,7 @@ const { submissionPeriodOpen } = require("../config/formio"); * Contact__r: { * attributes: { type: "Contact", url: string } * Id: string + * RecordTypeId: string * FirstName: string * LastName: string * Title: string @@ -337,6 +341,12 @@ const { submissionPeriodOpen } = require("../config/formio"); * } * } * }[]} frf2024BusRecordsContactsQueries + * @property {{ + * attributes: { type: "RecordType", url: string } + * Id: string + * Name: string + * Description: string + * }[]} frf2024ContactsRecordTypesQuery */ /** @@ -1341,12 +1351,14 @@ async function queryBapFor2024PRFData(req, frfReviewItemId) { // Applicant_Organization__r.Id // Applicant_Organization__r.County__c // Primary_Applicant__r.Id, + // Primary_Applicant__r.RecordTypeId, // Primary_Applicant__r.FirstName, // Primary_Applicant__r.LastName, // Primary_Applicant__r.Title, // Primary_Applicant__r.Email, // Primary_Applicant__r.Phone, // Alternate_Applicant__r.Id, + // Alternate_Applicant__r.RecordTypeId, // Alternate_Applicant__r.FirstName, // Alternate_Applicant__r.LastName, // Alternate_Applicant__r.Title, @@ -1359,6 +1371,7 @@ async function queryBapFor2024PRFData(req, frfReviewItemId) { // CSB_School_District__r.BillingState, // CSB_School_District__r.BillingPostalCode, // School_District_Contact__r.Id, + // School_District_Contact__r.RecordTypeId // School_District_Contact__r.FirstName, // School_District_Contact__r.LastName, // School_District_Contact__r.Title, @@ -1393,12 +1406,14 @@ async function queryBapFor2024PRFData(req, frfReviewItemId) { "Applicant_Organization__r.Id": 1, "Applicant_Organization__r.County__c": 1, "Primary_Applicant__r.Id": 1, + "Primary_Applicant__r.RecordTypeId": 1, "Primary_Applicant__r.FirstName": 1, "Primary_Applicant__r.LastName": 1, "Primary_Applicant__r.Title": 1, "Primary_Applicant__r.Email": 1, "Primary_Applicant__r.Phone": 1, "Alternate_Applicant__r.Id": 1, + "Alternate_Applicant__r.RecordTypeId": 1, "Alternate_Applicant__r.FirstName": 1, "Alternate_Applicant__r.LastName": 1, "Alternate_Applicant__r.Title": 1, @@ -1411,6 +1426,7 @@ async function queryBapFor2024PRFData(req, frfReviewItemId) { "CSB_School_District__r.BillingState": 1, "CSB_School_District__r.BillingPostalCode": 1, "School_District_Contact__r.Id": 1, + "School_District_Contact__r.RecordTypeId": 1, "School_District_Contact__r.FirstName": 1, "School_District_Contact__r.LastName": 1, "School_District_Contact__r.Title": 1, @@ -1426,7 +1442,8 @@ async function queryBapFor2024PRFData(req, frfReviewItemId) { ) .execute(async (err, records) => ((await err) ? err : records)); - const frf2024RecordId = frf2024RecordQuery["0"].Id; + const frf2024Record = frf2024RecordQuery["0"]; + const frf2024RecordId = frf2024Record.Id; // `SELECT // Id @@ -1524,6 +1541,7 @@ async function queryBapFor2024PRFData(req, frfReviewItemId) { // Related_Line_Item__c, // Relationship_Type__c, // Contact__r.Id, + // Contant__r.RecordTypeId, // Contact__r.FirstName, // Contact__r.LastName // Contact__r.Title, @@ -1557,6 +1575,7 @@ async function queryBapFor2024PRFData(req, frfReviewItemId) { Related_Line_Item__c: 1, Relationship_Type__c: 1, "Contact__r.Id": 1, + "Contact__r.RecordTypeId": 1, "Contact__r.FirstName": 1, "Contact__r.LastName": 1, "Contact__r.Title": 1, @@ -1576,10 +1595,51 @@ async function queryBapFor2024PRFData(req, frfReviewItemId) { ) ).flat(); + const recordTypeIds = []; + + recordTypeIds.push(frf2024Record?.Primary_Applicant__r?.RecordTypeId); + recordTypeIds.push(frf2024Record?.Alternate_Applicant__r?.RecordTypeId); + recordTypeIds.push(frf2024Record?.School_District_Contact__r?.RecordTypeId); + + for (const busRecordsContactsQuery of frf2024BusRecordsContactsQueries) { + const { Contact__r } = busRecordsContactsQuery; + recordTypeIds.push(Contact__r?.RecordTypeId); + } + + const uniqueRecordTypeIds = [...new Set(recordTypeIds)].filter(Boolean); + + // `SELECT + // Id, + // Name, + // Description + // FROM + // RecordType + // WHERE + // Id IN(${uniqueRecordTypeIds.map((id) => `'${id}'`)})` + + const frf2024ContactsRecordTypesQuery = + uniqueRecordTypeIds.length === 0 + ? [] + : await bapConnection + .sobject("RecordType") + .find( + { + Id: { $in: uniqueRecordTypeIds }, + }, + { + // "*": 1, + Id: 1, // Salesforce record ID + Name: 1, + Description: 1, + }, + ) + .execute(async (err, records) => ((await err) ? err : records)); + return { frf2024RecordQuery, frf2024BusRecordsQuery, frf2024BusRecordsContactsQueries, + frf2024ContactsRecordTypesQuery, }; } From 47618be9c573baab43abc93de718bf2cae541b9b Mon Sep 17 00:00:00 2001 From: Courtney Myers Date: Wed, 22 Jan 2025 09:54:46 -0500 Subject: [PATCH 06/13] Update new 2024 PRF submission data to include contact record types --- app/server/app/utilities/formio.js | 32 ++++++++++++++++++++++++++++++ 1 file changed, 32 insertions(+) diff --git a/app/server/app/utilities/formio.js b/app/server/app/utilities/formio.js index 5f0e82bf..6dbc1d11 100644 --- a/app/server/app/utilities/formio.js +++ b/app/server/app/utilities/formio.js @@ -503,6 +503,7 @@ function fetchDataForPRFSubmission({ rebateYear, req, res }) { frf2024RecordQuery, frf2024BusRecordsQuery, frf2024BusRecordsContactsQueries, + frf2024ContactsRecordTypesQuery, } = results; const existingBusOwnerType = "Old Bus Private Fleet Owner (if changed)"; @@ -535,6 +536,7 @@ function fetchDataForPRFSubmission({ rebateYear, req, res }) { const { Id: contactId, + RecordTypeId, FirstName, LastName, Title, @@ -577,6 +579,10 @@ function fetchDataForPRFSubmission({ rebateYear, req, res }) { BillingStreet ?? "\n" ).split("\n"); + const orgContactRecordType = frf2024ContactsRecordTypesQuery.find( + (item) => item.Id === RecordTypeId, + ); + array.push({ _bap_org_frf: true, org_number: jsonOrg.org_number, @@ -588,6 +594,7 @@ function fetchDataForPRFSubmission({ rebateYear, req, res }) { _bap_org_id: orgId, _bap_org_name: orgName, _bap_org_contact_id_frf: contactId, + _bap_org_contact_recordtype: orgContactRecordType?.Name || "", _bap_org_contact_fname: FirstName, _bap_org_contact_lname: LastName, _bap_org_contact_title: Title, @@ -642,12 +649,21 @@ function fetchDataForPRFSubmission({ rebateYear, req, res }) { item.Relationship_Type__c === newBusOwnerType, ); + const existingOwnerRecordType = frf2024ContactsRecordTypesQuery.find( + (item) => item.Id === existingOwnerRecord?.Contact__r?.RecordTypeId, + ); + + const newOwnerRecordType = frf2024ContactsRecordTypesQuery.find( + (item) => item.Id === newOwnerRecord?.Contact__r?.RecordTypeId, + ); + return { bus_number: Rebate_Item_num__c, bus_existing_owner: { org_id: existingOwnerRecord?.Contact__r?.Account?.Id, org_name: existingOwnerRecord?.Contact__r?.Account?.Name, org_contact_id: existingOwnerRecord?.Contact__r?.Id, + org_contact_recordtype: existingOwnerRecordType?.Name || "", org_contact_fname: existingOwnerRecord?.Contact__r?.FirstName, org_contact_lname: existingOwnerRecord?.Contact__r?.LastName, }, @@ -668,6 +684,7 @@ function fetchDataForPRFSubmission({ rebateYear, req, res }) { org_id: newOwnerRecord?.Contact__r?.Account?.Id, org_name: newOwnerRecord?.Contact__r?.Account?.Name, org_contact_id: newOwnerRecord?.Contact__r?.Id, + org_contact_recordtype: newOwnerRecordType?.Name || "", org_contact_fname: newOwnerRecord?.Contact__r?.FirstName, org_contact_lname: newOwnerRecord?.Contact__r?.LastName, }, @@ -678,6 +695,18 @@ function fetchDataForPRFSubmission({ rebateYear, req, res }) { }; }); + const primaryContactRecordType = frf2024ContactsRecordTypesQuery.find( + (item) => item.Id === Primary_Applicant__r?.RecordTypeId, + ); + + const alternateContactRecordType = frf2024ContactsRecordTypesQuery.find( + (item) => item.Id === Alternate_Applicant__r?.RecordTypeId, + ); + + const districtContactRecordType = frf2024ContactsRecordTypesQuery.find( + (item) => item.Id === School_District_Contact__r?.RecordTypeId, + ); + return { data: { _frf_modified: frfModified, @@ -704,12 +733,14 @@ function fetchDataForPRFSubmission({ rebateYear, req, res }) { _bap_govt_bus_poc_email: GOVT_BUS_POC_EMAIL__c, _bap_alt_govt_bus_poc_email: ALT_GOVT_BUS_POC_EMAIL__c, _bap_primary_id: Primary_Applicant__r?.Id, + _bap_primary_recordtype: primaryContactRecordType?.Name || "", _bap_primary_fname: Primary_Applicant__r?.FirstName, _bap_primary_lname: Primary_Applicant__r?.LastName, _bap_primary_title: Primary_Applicant__r?.Title, _bap_primary_email: Primary_Applicant__r?.Email, _bap_primary_phone: Primary_Applicant__r?.Phone, _bap_alternate_id: Alternate_Applicant__r?.Id, + _bap_alternate_recordtype: alternateContactRecordType?.Name, _bap_alternate_fname: Alternate_Applicant__r?.FirstName, _bap_alternate_lname: Alternate_Applicant__r?.LastName, _bap_alternate_title: Alternate_Applicant__r?.Title, @@ -731,6 +762,7 @@ function fetchDataForPRFSubmission({ rebateYear, req, res }) { }, _bap_district_self_certify: Self_Certification_Category__c, _bap_district_contact_id: School_District_Contact__r?.Id, + _bap_district_contact_recordtype: districtContactRecordType?.Name || "", // prettier-ignore _bap_district_contact_fname: School_District_Contact__r?.FirstName, _bap_district_contact_lname: School_District_Contact__r?.LastName, _bap_district_contact_title: School_District_Contact__r?.Title, From f2a076b7980a3dd783a662468adf4edfcb8a84f0 Mon Sep 17 00:00:00 2001 From: Courtney Myers Date: Wed, 22 Jan 2025 17:01:08 -0500 Subject: [PATCH 07/13] Update queryBapFor2024PRFData() to get each contact's record type from the Record_Type_Name__c field, instead of getting the record type id, and then querying for the corresponding record type names --- app/server/app/utilities/bap.js | 74 ++++++------------------------ app/server/app/utilities/formio.js | 43 ++++++----------- 2 files changed, 26 insertions(+), 91 deletions(-) diff --git a/app/server/app/utilities/bap.js b/app/server/app/utilities/bap.js index d7496b13..46517e73 100644 --- a/app/server/app/utilities/bap.js +++ b/app/server/app/utilities/bap.js @@ -250,7 +250,7 @@ const { submissionPeriodOpen } = require("../config/formio"); * Primary_Applicant__r: { * attributes: { type: "Contact", url: string } * Id: string - * RecordTypeId: string + * Record_Type_Name__c: string * FirstName: string * LastName: string * Title: string @@ -260,7 +260,7 @@ const { submissionPeriodOpen } = require("../config/formio"); * Alternate_Applicant__r: { * attributes: { type: "Contact", url: string } * Id: string - * RecordTypeId: string + * Record_Type_Name__c: string * FirstName: string * LastName: string * Title: string @@ -279,7 +279,7 @@ const { submissionPeriodOpen } = require("../config/formio"); * School_District_Contact__r: { * attributes: { type: "Contact", url: string } * Id: string - * RecordTypeId: string + * Record_Type_Name__c: string * FirstName: string * LastName: string * Title: string @@ -323,7 +323,7 @@ const { submissionPeriodOpen } = require("../config/formio"); * Contact__r: { * attributes: { type: "Contact", url: string } * Id: string - * RecordTypeId: string + * Record_Type_Name__c: string * FirstName: string * LastName: string * Title: string @@ -341,12 +341,6 @@ const { submissionPeriodOpen } = require("../config/formio"); * } * } * }[]} frf2024BusRecordsContactsQueries - * @property {{ - * attributes: { type: "RecordType", url: string } - * Id: string - * Name: string - * Description: string - * }[]} frf2024ContactsRecordTypesQuery */ /** @@ -1351,14 +1345,14 @@ async function queryBapFor2024PRFData(req, frfReviewItemId) { // Applicant_Organization__r.Id // Applicant_Organization__r.County__c // Primary_Applicant__r.Id, - // Primary_Applicant__r.RecordTypeId, + // Primary_Applicant__r.Record_Type_Name__c, // Primary_Applicant__r.FirstName, // Primary_Applicant__r.LastName, // Primary_Applicant__r.Title, // Primary_Applicant__r.Email, // Primary_Applicant__r.Phone, // Alternate_Applicant__r.Id, - // Alternate_Applicant__r.RecordTypeId, + // Alternate_Applicant__r.Record_Type_Name__c, // Alternate_Applicant__r.FirstName, // Alternate_Applicant__r.LastName, // Alternate_Applicant__r.Title, @@ -1371,7 +1365,7 @@ async function queryBapFor2024PRFData(req, frfReviewItemId) { // CSB_School_District__r.BillingState, // CSB_School_District__r.BillingPostalCode, // School_District_Contact__r.Id, - // School_District_Contact__r.RecordTypeId + // School_District_Contact__r.Record_Type_Name__c // School_District_Contact__r.FirstName, // School_District_Contact__r.LastName, // School_District_Contact__r.Title, @@ -1406,14 +1400,14 @@ async function queryBapFor2024PRFData(req, frfReviewItemId) { "Applicant_Organization__r.Id": 1, "Applicant_Organization__r.County__c": 1, "Primary_Applicant__r.Id": 1, - "Primary_Applicant__r.RecordTypeId": 1, + "Primary_Applicant__r.Record_Type_Name__c": 1, "Primary_Applicant__r.FirstName": 1, "Primary_Applicant__r.LastName": 1, "Primary_Applicant__r.Title": 1, "Primary_Applicant__r.Email": 1, "Primary_Applicant__r.Phone": 1, "Alternate_Applicant__r.Id": 1, - "Alternate_Applicant__r.RecordTypeId": 1, + "Alternate_Applicant__r.Record_Type_Name__c": 1, "Alternate_Applicant__r.FirstName": 1, "Alternate_Applicant__r.LastName": 1, "Alternate_Applicant__r.Title": 1, @@ -1426,7 +1420,7 @@ async function queryBapFor2024PRFData(req, frfReviewItemId) { "CSB_School_District__r.BillingState": 1, "CSB_School_District__r.BillingPostalCode": 1, "School_District_Contact__r.Id": 1, - "School_District_Contact__r.RecordTypeId": 1, + "School_District_Contact__r.Record_Type_Name__c": 1, "School_District_Contact__r.FirstName": 1, "School_District_Contact__r.LastName": 1, "School_District_Contact__r.Title": 1, @@ -1442,8 +1436,7 @@ async function queryBapFor2024PRFData(req, frfReviewItemId) { ) .execute(async (err, records) => ((await err) ? err : records)); - const frf2024Record = frf2024RecordQuery["0"]; - const frf2024RecordId = frf2024Record.Id; + const frf2024RecordId = frf2024RecordQuery["0"].Id; // `SELECT // Id @@ -1541,7 +1534,7 @@ async function queryBapFor2024PRFData(req, frfReviewItemId) { // Related_Line_Item__c, // Relationship_Type__c, // Contact__r.Id, - // Contant__r.RecordTypeId, + // Contant__r.Record_Type_Name__c, // Contact__r.FirstName, // Contact__r.LastName // Contact__r.Title, @@ -1575,7 +1568,7 @@ async function queryBapFor2024PRFData(req, frfReviewItemId) { Related_Line_Item__c: 1, Relationship_Type__c: 1, "Contact__r.Id": 1, - "Contact__r.RecordTypeId": 1, + "Contact__r.Record_Type_Name__c": 1, "Contact__r.FirstName": 1, "Contact__r.LastName": 1, "Contact__r.Title": 1, @@ -1595,51 +1588,10 @@ async function queryBapFor2024PRFData(req, frfReviewItemId) { ) ).flat(); - const recordTypeIds = []; - - recordTypeIds.push(frf2024Record?.Primary_Applicant__r?.RecordTypeId); - recordTypeIds.push(frf2024Record?.Alternate_Applicant__r?.RecordTypeId); - recordTypeIds.push(frf2024Record?.School_District_Contact__r?.RecordTypeId); - - for (const busRecordsContactsQuery of frf2024BusRecordsContactsQueries) { - const { Contact__r } = busRecordsContactsQuery; - recordTypeIds.push(Contact__r?.RecordTypeId); - } - - const uniqueRecordTypeIds = [...new Set(recordTypeIds)].filter(Boolean); - - // `SELECT - // Id, - // Name, - // Description - // FROM - // RecordType - // WHERE - // Id IN(${uniqueRecordTypeIds.map((id) => `'${id}'`)})` - - const frf2024ContactsRecordTypesQuery = - uniqueRecordTypeIds.length === 0 - ? [] - : await bapConnection - .sobject("RecordType") - .find( - { - Id: { $in: uniqueRecordTypeIds }, - }, - { - // "*": 1, - Id: 1, // Salesforce record ID - Name: 1, - Description: 1, - }, - ) - .execute(async (err, records) => ((await err) ? err : records)); - return { frf2024RecordQuery, frf2024BusRecordsQuery, frf2024BusRecordsContactsQueries, - frf2024ContactsRecordTypesQuery, }; } diff --git a/app/server/app/utilities/formio.js b/app/server/app/utilities/formio.js index 6dbc1d11..17b59250 100644 --- a/app/server/app/utilities/formio.js +++ b/app/server/app/utilities/formio.js @@ -503,7 +503,6 @@ function fetchDataForPRFSubmission({ rebateYear, req, res }) { frf2024RecordQuery, frf2024BusRecordsQuery, frf2024BusRecordsContactsQueries, - frf2024ContactsRecordTypesQuery, } = results; const existingBusOwnerType = "Old Bus Private Fleet Owner (if changed)"; @@ -536,7 +535,7 @@ function fetchDataForPRFSubmission({ rebateYear, req, res }) { const { Id: contactId, - RecordTypeId, + Record_Type_Name__c, FirstName, LastName, Title, @@ -579,10 +578,6 @@ function fetchDataForPRFSubmission({ rebateYear, req, res }) { BillingStreet ?? "\n" ).split("\n"); - const orgContactRecordType = frf2024ContactsRecordTypesQuery.find( - (item) => item.Id === RecordTypeId, - ); - array.push({ _bap_org_frf: true, org_number: jsonOrg.org_number, @@ -594,7 +589,7 @@ function fetchDataForPRFSubmission({ rebateYear, req, res }) { _bap_org_id: orgId, _bap_org_name: orgName, _bap_org_contact_id_frf: contactId, - _bap_org_contact_recordtype: orgContactRecordType?.Name || "", + _bap_org_contact_recordtype: Record_Type_Name__c, _bap_org_contact_fname: FirstName, _bap_org_contact_lname: LastName, _bap_org_contact_title: Title, @@ -649,13 +644,13 @@ function fetchDataForPRFSubmission({ rebateYear, req, res }) { item.Relationship_Type__c === newBusOwnerType, ); - const existingOwnerRecordType = frf2024ContactsRecordTypesQuery.find( - (item) => item.Id === existingOwnerRecord?.Contact__r?.RecordTypeId, - ); + // const existingOwnerRecordType = frf2024ContactsRecordTypesQuery.find( + // (item) => item.Id === existingOwnerRecord?.Contact__r?.RecordTypeId, + // ); - const newOwnerRecordType = frf2024ContactsRecordTypesQuery.find( - (item) => item.Id === newOwnerRecord?.Contact__r?.RecordTypeId, - ); + // const newOwnerRecordType = frf2024ContactsRecordTypesQuery.find( + // (item) => item.Id === newOwnerRecord?.Contact__r?.RecordTypeId, + // ); return { bus_number: Rebate_Item_num__c, @@ -663,7 +658,7 @@ function fetchDataForPRFSubmission({ rebateYear, req, res }) { org_id: existingOwnerRecord?.Contact__r?.Account?.Id, org_name: existingOwnerRecord?.Contact__r?.Account?.Name, org_contact_id: existingOwnerRecord?.Contact__r?.Id, - org_contact_recordtype: existingOwnerRecordType?.Name || "", + org_contact_recordtype: existingOwnerRecord?.Contact__r?.Record_Type_Name__c, // prettier-ignore org_contact_fname: existingOwnerRecord?.Contact__r?.FirstName, org_contact_lname: existingOwnerRecord?.Contact__r?.LastName, }, @@ -684,7 +679,7 @@ function fetchDataForPRFSubmission({ rebateYear, req, res }) { org_id: newOwnerRecord?.Contact__r?.Account?.Id, org_name: newOwnerRecord?.Contact__r?.Account?.Name, org_contact_id: newOwnerRecord?.Contact__r?.Id, - org_contact_recordtype: newOwnerRecordType?.Name || "", + org_contact_recordtype: newOwnerRecord?.Contact__r?.Record_Type_Name__c, // prettier-ignore org_contact_fname: newOwnerRecord?.Contact__r?.FirstName, org_contact_lname: newOwnerRecord?.Contact__r?.LastName, }, @@ -695,18 +690,6 @@ function fetchDataForPRFSubmission({ rebateYear, req, res }) { }; }); - const primaryContactRecordType = frf2024ContactsRecordTypesQuery.find( - (item) => item.Id === Primary_Applicant__r?.RecordTypeId, - ); - - const alternateContactRecordType = frf2024ContactsRecordTypesQuery.find( - (item) => item.Id === Alternate_Applicant__r?.RecordTypeId, - ); - - const districtContactRecordType = frf2024ContactsRecordTypesQuery.find( - (item) => item.Id === School_District_Contact__r?.RecordTypeId, - ); - return { data: { _frf_modified: frfModified, @@ -733,14 +716,14 @@ function fetchDataForPRFSubmission({ rebateYear, req, res }) { _bap_govt_bus_poc_email: GOVT_BUS_POC_EMAIL__c, _bap_alt_govt_bus_poc_email: ALT_GOVT_BUS_POC_EMAIL__c, _bap_primary_id: Primary_Applicant__r?.Id, - _bap_primary_recordtype: primaryContactRecordType?.Name || "", + _bap_primary_recordtype: Primary_Applicant__r?.Record_Type_Name__c, _bap_primary_fname: Primary_Applicant__r?.FirstName, _bap_primary_lname: Primary_Applicant__r?.LastName, _bap_primary_title: Primary_Applicant__r?.Title, _bap_primary_email: Primary_Applicant__r?.Email, _bap_primary_phone: Primary_Applicant__r?.Phone, _bap_alternate_id: Alternate_Applicant__r?.Id, - _bap_alternate_recordtype: alternateContactRecordType?.Name, + _bap_alternate_recordtype: Alternate_Applicant__r?.Record_Type_Name__c, // prettier-ignore _bap_alternate_fname: Alternate_Applicant__r?.FirstName, _bap_alternate_lname: Alternate_Applicant__r?.LastName, _bap_alternate_title: Alternate_Applicant__r?.Title, @@ -762,7 +745,7 @@ function fetchDataForPRFSubmission({ rebateYear, req, res }) { }, _bap_district_self_certify: Self_Certification_Category__c, _bap_district_contact_id: School_District_Contact__r?.Id, - _bap_district_contact_recordtype: districtContactRecordType?.Name || "", // prettier-ignore + _bap_district_contact_recordtype: School_District_Contact__r?.Record_Type_Name__c, // prettier-ignore _bap_district_contact_fname: School_District_Contact__r?.FirstName, _bap_district_contact_lname: School_District_Contact__r?.LastName, _bap_district_contact_title: School_District_Contact__r?.Title, From d521147b8e2a116560e72c99014e541e93049af7 Mon Sep 17 00:00:00 2001 From: Courtney Myers Date: Wed, 22 Jan 2025 17:02:52 -0500 Subject: [PATCH 08/13] Remove no longer used record type variables from fetchDataForPRFSubmission() --- app/server/app/utilities/formio.js | 8 -------- 1 file changed, 8 deletions(-) diff --git a/app/server/app/utilities/formio.js b/app/server/app/utilities/formio.js index 17b59250..d07d4755 100644 --- a/app/server/app/utilities/formio.js +++ b/app/server/app/utilities/formio.js @@ -644,14 +644,6 @@ function fetchDataForPRFSubmission({ rebateYear, req, res }) { item.Relationship_Type__c === newBusOwnerType, ); - // const existingOwnerRecordType = frf2024ContactsRecordTypesQuery.find( - // (item) => item.Id === existingOwnerRecord?.Contact__r?.RecordTypeId, - // ); - - // const newOwnerRecordType = frf2024ContactsRecordTypesQuery.find( - // (item) => item.Id === newOwnerRecord?.Contact__r?.RecordTypeId, - // ); - return { bus_number: Rebate_Item_num__c, bus_existing_owner: { From afaf55b100370e50f52559222ac78299f8e897db Mon Sep 17 00:00:00 2001 From: Courtney Myers Date: Thu, 23 Jan 2025 15:21:52 -0500 Subject: [PATCH 09/13] Add server utility function to fetch a PDF of an existing form submission from Formio --- app/server/app/utilities/formio.js | 98 ++++++++++++++++++++++++++++++ 1 file changed, 98 insertions(+) diff --git a/app/server/app/utilities/formio.js b/app/server/app/utilities/formio.js index d07d4755..863d5cb1 100644 --- a/app/server/app/utilities/formio.js +++ b/app/server/app/utilities/formio.js @@ -3,6 +3,7 @@ const ObjectId = require("mongodb").ObjectId; // --- const { axiosFormio, + formioProjectUrl, formUrl, submissionPeriodOpen, formioCSBMetadata, @@ -1090,6 +1091,101 @@ function downloadS3FileMetadata({ rebateYear, req, res }) { }); } +/** + * @param {Object} param + * @param {RebateYear} param.rebateYear + * @param {express.Request} param.req + * @param {express.Response} param.res + */ +function fetchSubmissionPDF({ rebateYear, req, res }) { + const { bapComboKeys } = req; + const { mail } = req.user; + const { formType, mongoId } = req.params; + + // NOTE: included to support EPA API scan + if (mongoId === formioExampleMongoId) { + return res.json(""); + } + + /** NOTE: verifyMongoObjectId */ + if (!ObjectId.isValid(mongoId)) { + const errorStatus = 400; + const errorMessage = `MongoDB ObjectId validation error for: '${mongoId}'.`; + return res.status(errorStatus).json({ message: errorMessage }); + } + + const comboKeyFieldName = getComboKeyFieldName({ rebateYear }); + + const formioFormUrl = formUrl[rebateYear][formType]; + + if (!formioFormUrl) { + const errorStatus = 400; + const errorMessage = `Formio form URL does not exist for ${rebateYear} ${formType.toUpperCase()}.`; + return res.status(errorStatus).json({ message: errorMessage }); + } + + axiosFormio(req) + .get(`${formioFormUrl}/submission/${mongoId}`) + .then((axiosRes) => axiosRes.data) + .then((submission) => { + const projectId = submission.project; + const formId = submission.form; + const comboKey = submission.data?.[comboKeyFieldName]; + + if (!bapComboKeys.includes(comboKey)) { + const logMessage = + `User with email '${mail}' attempted to download a PDF of ` + + `${rebateYear} ${formType.toUpperCase()} submission '${mongoId}' ` + + `that they do not have access to.`; + log({ level: "warn", message: logMessage, req }); + + const errorStatus = 401; + const errorMessage = `Unauthorized.`; + return res.status(errorStatus).json({ message: errorMessage }); + } + + const headers = { + "x-allow": `GET:/project/${projectId}/form/${formId}/submission/${mongoId}/download`, + "x-expire": 3600, + }; + + axiosFormio(req) + .get(`${formioProjectUrl}/token`, { headers }) + .then((axiosRes) => axiosRes.data) + .then((json) => { + const url = `${formioProjectUrl}/form/${formId}/submission/${mongoId}/download?token=${json.key}`; + + axiosFormio(req) + .get(url, { responseType: "arraybuffer" }) + .then((axiosRes) => axiosRes.data) + .then((fileData) => { + const base64String = Buffer.from(fileData).toString("base64"); + res.attachment(`${mongoId}.pdf`); + res.type("application/pdf"); + res.send(base64String); + }) + .catch((error) => { + // NOTE: error is logged in axiosFormio response interceptor + const errorStatus = error.response?.status || 500; + const errorMessage = `Error getting Formio submission PDF.`; + return res.status(errorStatus).json({ message: errorMessage }); + }); + }) + .catch((error) => { + // NOTE: error is logged in axiosFormio response interceptor + const errorStatus = error.response?.status || 500; + const errorMessage = `Error getting Formio download token.`; + return res.status(errorStatus).json({ message: errorMessage }); + }); + }) + .catch((error) => { + // NOTE: error is logged in axiosFormio response interceptor + const errorStatus = error.response?.status || 500; + const errorMessage = `Error getting Formio ${rebateYear} ${formType.toUpperCase()} form submission '${mongoId}'.`; + return res.status(errorStatus).json({ message: errorMessage }); + }); +} + /** * @param {Object} param * @param {RebateYear} param.rebateYear @@ -2222,6 +2318,8 @@ module.exports = { uploadS3FileMetadata, downloadS3FileMetadata, // + fetchSubmissionPDF, + // fetchFRFSubmissions, createFRFSubmission, fetchFRFSubmission, From 2443f0c05aaf01794fc4ec21ac652e2e190c3df9 Mon Sep 17 00:00:00 2001 From: Courtney Myers Date: Thu, 23 Jan 2025 15:30:18 -0500 Subject: [PATCH 10/13] Add custom hook to fetch a PDF of a form submission from Formio --- app/client/src/utilities.ts | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/app/client/src/utilities.ts b/app/client/src/utilities.ts index df4324ac..c4f348f0 100644 --- a/app/client/src/utilities.ts +++ b/app/client/src/utilities.ts @@ -10,6 +10,7 @@ import { useSearchParams } from "react-router-dom"; // --- import { type RebateYear, + type FormType, type Content, type UserData, type ConfigData, @@ -187,6 +188,32 @@ export function useBapSamData() { return queryClient.getQueryData(["bap/sam"]); } +/** Custom hook to fetch a PDF of a form submission from Formio. */ +export function useSubmissionPDFQuery(options: { + rebateYear: RebateYear; + formType: FormType; + mongoId: string | undefined; +}) { + const { rebateYear, formType, mongoId } = options; + + return useQuery({ + queryKey: [`formio/${rebateYear}/${formType}-pdf`, { id: mongoId }], + queryFn: () => { + const url = `${serverUrl}/api/formio/${rebateYear}/pdf/${formType}/${mongoId}`; + return getData(url); + }, + onSuccess: (res) => { + const link = document.createElement("a"); + link.setAttribute("href", `data:application/pdf;base64,${res}`); + link.setAttribute("download", `${mongoId}.pdf`); + document.body.appendChild(link); + link.click(); + document.body.removeChild(link); + }, + enabled: false, + }); +} + /** Custom hook to fetch Change Request form submissions from Formio. */ export function useChangeRequestsQuery( rebateYear: Year, From bfb4826f8d0385d075a3a0304d4959b7508d8800 Mon Sep 17 00:00:00 2001 From: Courtney Myers Date: Thu, 23 Jan 2025 15:36:15 -0500 Subject: [PATCH 11/13] Update server app's 2022, 2023, and 2024 Formio API routes to include routes for fetching a PDF of a form submission --- app/server/app/routes/formio2022.js | 7 +++++++ app/server/app/routes/formio2023.js | 7 +++++++ app/server/app/routes/formio2024.js | 7 +++++++ 3 files changed, 21 insertions(+) diff --git a/app/server/app/routes/formio2022.js b/app/server/app/routes/formio2022.js index 2316b65c..0504c5cf 100644 --- a/app/server/app/routes/formio2022.js +++ b/app/server/app/routes/formio2022.js @@ -9,6 +9,8 @@ const { uploadS3FileMetadata, downloadS3FileMetadata, // + fetchSubmissionPDF, + // fetchFRFSubmissions, createFRFSubmission, fetchFRFSubmission, @@ -49,6 +51,11 @@ router.post( }, ); +// --- get a PDF of a 2022 form submission from Formio +router.get("/pdf/:formType/:mongoId", fetchBapComboKeys, (req, res) => { + fetchSubmissionPDF({ rebateYear, req, res }); +}); + // --- get user's 2022 FRF submissions from Formio router.get("/frf-submissions", fetchBapComboKeys, (req, res) => { fetchFRFSubmissions({ rebateYear, req, res }); diff --git a/app/server/app/routes/formio2023.js b/app/server/app/routes/formio2023.js index f2500134..06b2e184 100644 --- a/app/server/app/routes/formio2023.js +++ b/app/server/app/routes/formio2023.js @@ -11,6 +11,8 @@ const { uploadS3FileMetadata, downloadS3FileMetadata, // + fetchSubmissionPDF, + // fetchFRFSubmissions, createFRFSubmission, fetchFRFSubmission, @@ -61,6 +63,11 @@ router.post( }, ); +// --- get a PDF of a 2023 form submission from Formio +router.get("/pdf/:formType/:mongoId", fetchBapComboKeys, (req, res) => { + fetchSubmissionPDF({ rebateYear, req, res }); +}); + // --- get user's 2023 FRF submissions from Formio router.get("/frf-submissions", fetchBapComboKeys, (req, res) => { fetchFRFSubmissions({ rebateYear, req, res }); diff --git a/app/server/app/routes/formio2024.js b/app/server/app/routes/formio2024.js index 60a32f76..e61df562 100644 --- a/app/server/app/routes/formio2024.js +++ b/app/server/app/routes/formio2024.js @@ -11,6 +11,8 @@ const { uploadS3FileMetadata, downloadS3FileMetadata, // + fetchSubmissionPDF, + // fetchFRFSubmissions, createFRFSubmission, fetchFRFSubmission, @@ -61,6 +63,11 @@ router.post( }, ); +// --- get a PDF of a 2024 form submission from Formio +router.get("/pdf/:formType/:mongoId", fetchBapComboKeys, (req, res) => { + fetchSubmissionPDF({ rebateYear, req, res }); +}); + // --- get user's 2024 FRF submissions from Formio router.get("/frf-submissions", fetchBapComboKeys, (req, res) => { fetchFRFSubmissions({ rebateYear, req, res }); From 1680023493ad2d6932144fdaf9cc6dbb9924f7ca Mon Sep 17 00:00:00 2001 From: Courtney Myers Date: Thu, 23 Jan 2025 16:32:51 -0500 Subject: [PATCH 12/13] Update all forms to use the useSubmissionPDFQuery() custom hook and include a button for downloading a PDF of the submission --- app/client/src/routes/crf2022.tsx | 34 ++++++++++++++++++++++++++++++- app/client/src/routes/frf2022.tsx | 32 ++++++++++++++++++++++++++++- app/client/src/routes/frf2023.tsx | 32 ++++++++++++++++++++++++++++- app/client/src/routes/frf2024.tsx | 32 ++++++++++++++++++++++++++++- app/client/src/routes/prf2022.tsx | 34 ++++++++++++++++++++++++++++++- app/client/src/routes/prf2023.tsx | 34 ++++++++++++++++++++++++++++++- app/client/src/routes/prf2024.tsx | 34 ++++++++++++++++++++++++++++++- 7 files changed, 225 insertions(+), 7 deletions(-) diff --git a/app/client/src/routes/crf2022.tsx b/app/client/src/routes/crf2022.tsx index 10a5be3a..7bd63b93 100644 --- a/app/client/src/routes/crf2022.tsx +++ b/app/client/src/routes/crf2022.tsx @@ -19,6 +19,7 @@ import { useContentData, useConfigData, useBapSamData, + useSubmissionPDFQuery, useSubmissionsQueries, useSubmissions, submissionNeedsEdits, @@ -27,7 +28,7 @@ import { entityHasDebtSubjectToOffset, getUserInfo, } from "@/utilities"; -import { Loading } from "@/components/loading"; +import { Loading, LoadingButtonIcon } from "@/components/loading"; import { Message } from "@/components/message"; import { MarkdownContent } from "@/components/markdownContent"; import { useNotificationsActions } from "@/contexts/notifications"; @@ -40,6 +41,7 @@ function useFormioSubmissionQueryAndMutation(rebateId: string | undefined) { useEffect(() => { queryClient.resetQueries({ queryKey: ["formio/2022/crf-submission"] }); + queryClient.resetQueries({ queryKey: ["formio/2022/crf-pdf"] }); }, [queryClient]); const url = `${serverUrl}/api/formio/2022/crf-submission/${rebateId}`; @@ -129,6 +131,12 @@ function CloseOutRequestForm(props: { email: string }) { const { query, mutation } = useFormioSubmissionQueryAndMutation(rebateId); const { userAccess, formSchema, submission } = query.data ?? {}; + const pdfQuery = useSubmissionPDFQuery({ + rebateYear: "2022", + formType: "crf", + mongoId: submission?._id || "", + }); + /** * Stores when data is being posted to the server, so a loading overlay can * be rendered over the form, preventing the user from losing input data when @@ -243,6 +251,30 @@ function CloseOutRequestForm(props: { email: string }) { + {submission?._id && ( +

+ +

+ )} + {}}>
diff --git a/app/client/src/routes/frf2022.tsx b/app/client/src/routes/frf2022.tsx index a0f9c72e..cfbaa797 100644 --- a/app/client/src/routes/frf2022.tsx +++ b/app/client/src/routes/frf2022.tsx @@ -19,6 +19,7 @@ import { useContentData, useConfigData, useBapSamData, + useSubmissionPDFQuery, useSubmissionsQueries, useSubmissions, submissionNeedsEdits, @@ -27,7 +28,7 @@ import { entityHasDebtSubjectToOffset, getUserInfo, } from "@/utilities"; -import { Loading } from "@/components/loading"; +import { Loading, LoadingButtonIcon } from "@/components/loading"; import { Message } from "@/components/message"; import { MarkdownContent } from "@/components/markdownContent"; import { useDialogActions } from "@/contexts/dialog"; @@ -41,6 +42,7 @@ function useFormioSubmissionQueryAndMutation(mongoId: string | undefined) { useEffect(() => { queryClient.resetQueries({ queryKey: ["formio/2022/frf-submission"] }); + queryClient.resetQueries({ queryKey: ["formio/2022/frf-pdf"] }); }, [queryClient]); const url = `${serverUrl}/api/formio/2022/frf-submission/${mongoId}`; @@ -142,6 +144,12 @@ function FundingRequestForm(props: { email: string }) { const { query, mutation } = useFormioSubmissionQueryAndMutation(mongoId); const { userAccess, formSchema, submission } = query.data ?? {}; + const pdfQuery = useSubmissionPDFQuery({ + rebateYear: "2022", + formType: "frf", + mongoId, + }); + /** * Stores when data is being posted to the server, so a loading overlay can * be rendered over the form, preventing the user from losing input data when @@ -391,6 +399,28 @@ function FundingRequestForm(props: { email: string }) { )} +

+ +

+ {}}>
diff --git a/app/client/src/routes/frf2023.tsx b/app/client/src/routes/frf2023.tsx index 9683e325..3c475b5b 100644 --- a/app/client/src/routes/frf2023.tsx +++ b/app/client/src/routes/frf2023.tsx @@ -19,6 +19,7 @@ import { useContentData, useConfigData, useBapSamData, + useSubmissionPDFQuery, useSubmissionsQueries, useSubmissions, submissionNeedsEdits, @@ -27,7 +28,7 @@ import { entityHasDebtSubjectToOffset, getUserInfo, } from "@/utilities"; -import { Loading } from "@/components/loading"; +import { Loading, LoadingButtonIcon } from "@/components/loading"; import { Message } from "@/components/message"; import { MarkdownContent } from "@/components/markdownContent"; import { useDialogActions } from "@/contexts/dialog"; @@ -41,6 +42,7 @@ function useFormioSubmissionQueryAndMutation(mongoId: string | undefined) { useEffect(() => { queryClient.resetQueries({ queryKey: ["formio/2023/frf-submission"] }); + queryClient.resetQueries({ queryKey: ["formio/2023/frf-pdf"] }); }, [queryClient]); const url = `${serverUrl}/api/formio/2023/frf-submission/${mongoId}`; @@ -128,6 +130,12 @@ function FundingRequestForm(props: { email: string }) { const { query, mutation } = useFormioSubmissionQueryAndMutation(mongoId); const { userAccess, formSchema, submission } = query.data ?? {}; + const pdfQuery = useSubmissionPDFQuery({ + rebateYear: "2023", + formType: "frf", + mongoId, + }); + /** * Stores when data is being posted to the server, so a loading overlay can * be rendered over the form, preventing the user from losing input data when @@ -377,6 +385,28 @@ function FundingRequestForm(props: { email: string }) { )} +

+ +

+ {}}>
diff --git a/app/client/src/routes/frf2024.tsx b/app/client/src/routes/frf2024.tsx index c34d8425..dedac67c 100644 --- a/app/client/src/routes/frf2024.tsx +++ b/app/client/src/routes/frf2024.tsx @@ -19,6 +19,7 @@ import { useContentData, useConfigData, useBapSamData, + useSubmissionPDFQuery, useSubmissionsQueries, useSubmissions, submissionNeedsEdits, @@ -27,7 +28,7 @@ import { entityHasDebtSubjectToOffset, getUserInfo, } from "@/utilities"; -import { Loading } from "@/components/loading"; +import { Loading, LoadingButtonIcon } from "@/components/loading"; import { Message } from "@/components/message"; import { MarkdownContent } from "@/components/markdownContent"; import { useDialogActions } from "@/contexts/dialog"; @@ -41,6 +42,7 @@ function useFormioSubmissionQueryAndMutation(mongoId: string | undefined) { useEffect(() => { queryClient.resetQueries({ queryKey: ["formio/2024/frf-submission"] }); + queryClient.resetQueries({ queryKey: ["formio/2024/frf-pdf"] }); }, [queryClient]); const url = `${serverUrl}/api/formio/2024/frf-submission/${mongoId}`; @@ -128,6 +130,12 @@ function FundingRequestForm(props: { email: string }) { const { query, mutation } = useFormioSubmissionQueryAndMutation(mongoId); const { userAccess, formSchema, submission } = query.data ?? {}; + const pdfQuery = useSubmissionPDFQuery({ + rebateYear: "2024", + formType: "frf", + mongoId, + }); + /** * Stores when data is being posted to the server, so a loading overlay can * be rendered over the form, preventing the user from losing input data when @@ -377,6 +385,28 @@ function FundingRequestForm(props: { email: string }) { )} +

+ +

+ {}}>
diff --git a/app/client/src/routes/prf2022.tsx b/app/client/src/routes/prf2022.tsx index 05921b2c..1bda0afa 100644 --- a/app/client/src/routes/prf2022.tsx +++ b/app/client/src/routes/prf2022.tsx @@ -19,6 +19,7 @@ import { useContentData, useConfigData, useBapSamData, + useSubmissionPDFQuery, useSubmissionsQueries, useSubmissions, submissionNeedsEdits, @@ -27,7 +28,7 @@ import { entityHasDebtSubjectToOffset, getUserInfo, } from "@/utilities"; -import { Loading } from "@/components/loading"; +import { Loading, LoadingButtonIcon } from "@/components/loading"; import { Message } from "@/components/message"; import { MarkdownContent } from "@/components/markdownContent"; import { useNotificationsActions } from "@/contexts/notifications"; @@ -40,6 +41,7 @@ function useFormioSubmissionQueryAndMutation(rebateId: string | undefined) { useEffect(() => { queryClient.resetQueries({ queryKey: ["formio/2022/prf-submission"] }); + queryClient.resetQueries({ queryKey: ["formio/2022/prf-pdf"] }); }, [queryClient]); const url = `${serverUrl}/api/formio/2022/prf-submission/${rebateId}`; @@ -129,6 +131,12 @@ function PaymentRequestForm(props: { email: string }) { const { query, mutation } = useFormioSubmissionQueryAndMutation(rebateId); const { userAccess, formSchema, submission } = query.data ?? {}; + const pdfQuery = useSubmissionPDFQuery({ + rebateYear: "2022", + formType: "prf", + mongoId: submission?._id || "", + }); + /** * Stores when data is being posted to the server, so a loading overlay can * be rendered over the form, preventing the user from losing input data when @@ -264,6 +272,30 @@ function PaymentRequestForm(props: { email: string }) { + {submission?._id && ( +

+ +

+ )} + {}}>
diff --git a/app/client/src/routes/prf2023.tsx b/app/client/src/routes/prf2023.tsx index a8a34267..1ec178ec 100644 --- a/app/client/src/routes/prf2023.tsx +++ b/app/client/src/routes/prf2023.tsx @@ -19,6 +19,7 @@ import { useContentData, useConfigData, useBapSamData, + useSubmissionPDFQuery, useSubmissionsQueries, useSubmissions, submissionNeedsEdits, @@ -27,7 +28,7 @@ import { entityHasDebtSubjectToOffset, getUserInfo, } from "@/utilities"; -import { Loading } from "@/components/loading"; +import { Loading, LoadingButtonIcon } from "@/components/loading"; import { Message } from "@/components/message"; import { MarkdownContent } from "@/components/markdownContent"; import { useNotificationsActions } from "@/contexts/notifications"; @@ -40,6 +41,7 @@ function useFormioSubmissionQueryAndMutation(rebateId: string | undefined) { useEffect(() => { queryClient.resetQueries({ queryKey: ["formio/2023/prf-submission"] }); + queryClient.resetQueries({ queryKey: ["formio/2023/prf-pdf"] }); }, [queryClient]); const url = `${serverUrl}/api/formio/2023/prf-submission/${rebateId}`; @@ -129,6 +131,12 @@ function PaymentRequestForm(props: { email: string }) { const { query, mutation } = useFormioSubmissionQueryAndMutation(rebateId); const { userAccess, formSchema, submission } = query.data ?? {}; + const pdfQuery = useSubmissionPDFQuery({ + rebateYear: "2023", + formType: "prf", + mongoId: submission?._id || "", + }); + /** * Stores when data is being posted to the server, so a loading overlay can * be rendered over the form, preventing the user from losing input data when @@ -262,6 +270,30 @@ function PaymentRequestForm(props: { email: string }) { + {submission?._id && ( +

+ +

+ )} + {}}>
diff --git a/app/client/src/routes/prf2024.tsx b/app/client/src/routes/prf2024.tsx index 52e9e251..1c32bea9 100644 --- a/app/client/src/routes/prf2024.tsx +++ b/app/client/src/routes/prf2024.tsx @@ -19,6 +19,7 @@ import { useContentData, useConfigData, useBapSamData, + useSubmissionPDFQuery, useSubmissionsQueries, useSubmissions, submissionNeedsEdits, @@ -27,7 +28,7 @@ import { entityHasDebtSubjectToOffset, getUserInfo, } from "@/utilities"; -import { Loading } from "@/components/loading"; +import { Loading, LoadingButtonIcon } from "@/components/loading"; import { Message } from "@/components/message"; import { MarkdownContent } from "@/components/markdownContent"; import { useNotificationsActions } from "@/contexts/notifications"; @@ -40,6 +41,7 @@ function useFormioSubmissionQueryAndMutation(rebateId: string | undefined) { useEffect(() => { queryClient.resetQueries({ queryKey: ["formio/2024/prf-submission"] }); + queryClient.resetQueries({ queryKey: ["formio/2024/prf-pdf"] }); }, [queryClient]); const url = `${serverUrl}/api/formio/2024/prf-submission/${rebateId}`; @@ -129,6 +131,12 @@ function PaymentRequestForm(props: { email: string }) { const { query, mutation } = useFormioSubmissionQueryAndMutation(rebateId); const { userAccess, formSchema, submission } = query.data ?? {}; + const pdfQuery = useSubmissionPDFQuery({ + rebateYear: "2024", + formType: "prf", + mongoId: submission?._id || "", + }); + /** * Stores when data is being posted to the server, so a loading overlay can * be rendered over the form, preventing the user from losing input data when @@ -262,6 +270,30 @@ function PaymentRequestForm(props: { email: string }) { + {submission?._id && ( +

+ +

+ )} + {}}>
From f5abb29fc94330cd08b70473df79f2dd50d3e322 Mon Sep 17 00:00:00 2001 From: Courtney Myers Date: Thu, 23 Jan 2025 17:35:51 -0500 Subject: [PATCH 13/13] Update OpenAPI docs to include new Formio PDF submission routes --- docs/csb-openapi.json | 63 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 63 insertions(+) diff --git a/docs/csb-openapi.json b/docs/csb-openapi.json index be0eff94..d0b924eb 100644 --- a/docs/csb-openapi.json +++ b/docs/csb-openapi.json @@ -373,6 +373,27 @@ } } }, + "/api/formio/2022/pdf/{formType}/{mongoId}": { + "get": { + "summary": "Download a PDF of a 2022 form submission.", + "parameters": [ + { + "$ref": "#/components/parameters/formType" + }, + { + "$ref": "#/components/parameters/mongoId" + } + ], + "responses": { + "200": { + "description": "OK" + }, + "401": { + "$ref": "#/components/responses/Unauthorized" + } + } + } + }, "/api/formio/2022/frf-submissions": { "get": { "summary": "Get user's 2022 FRF submissions from Formio.", @@ -744,6 +765,27 @@ } } }, + "/api/formio/2023/pdf/{formType}/{mongoId}": { + "get": { + "summary": "Download a PDF of a 2023 form submission.", + "parameters": [ + { + "$ref": "#/components/parameters/formType" + }, + { + "$ref": "#/components/parameters/mongoId" + } + ], + "responses": { + "200": { + "description": "OK" + }, + "401": { + "$ref": "#/components/responses/Unauthorized" + } + } + } + }, "/api/formio/2023/frf-submissions": { "get": { "summary": "Get user's 2023 FRF submissions from Formio.", @@ -1108,6 +1150,27 @@ } } }, + "/api/formio/2024/pdf/{formType}/{mongoId}": { + "get": { + "summary": "Download a PDF of a 2024 form submission.", + "parameters": [ + { + "$ref": "#/components/parameters/formType" + }, + { + "$ref": "#/components/parameters/mongoId" + } + ], + "responses": { + "200": { + "description": "OK" + }, + "401": { + "$ref": "#/components/responses/Unauthorized" + } + } + } + }, "/api/formio/2024/frf-submissions": { "get": { "summary": "Get user's 2024 FRF submissions from Formio.",