diff --git a/.github/workflows/build-n-deploy-backend-to-ocp-dev.yml b/.github/workflows/build-n-deploy-backend-to-ocp-dev.yml index a3bb038..a8ea8b5 100644 --- a/.github/workflows/build-n-deploy-backend-to-ocp-dev.yml +++ b/.github/workflows/build-n-deploy-backend-to-ocp-dev.yml @@ -34,7 +34,7 @@ env: COMMON_NAMESPACE: ${{ secrets.COMMON_NAMESPACE_NO_ENV }} TAG: "latest" MIN_REPLICAS: "1" - MAX_REPLICAS: "1" + MAX_REPLICAS: "2" MIN_CPU: "50m" MAX_CPU: "100m" MIN_MEM: "200Mi" diff --git a/.github/workflows/deploy-to.openshift-prod.yml b/.github/workflows/deploy-to.openshift-prod.yml index f4b6645..a3d155a 100644 --- a/.github/workflows/deploy-to.openshift-prod.yml +++ b/.github/workflows/deploy-to.openshift-prod.yml @@ -39,7 +39,7 @@ env: MIN_MEM: "200Mi" MAX_MEM: "250Mi" MIN_REPLICAS: "3" - MAX_REPLICAS: "3" + MAX_REPLICAS: "4" # SITE_URL should have no scheme or port. It will be prepended with https:// HOST_ROUTE: ${{ secrets.SITE_URL }} on: diff --git a/.github/workflows/deploy-to.openshift-test.yml b/.github/workflows/deploy-to.openshift-test.yml index 17f9fc9..fbdd246 100644 --- a/.github/workflows/deploy-to.openshift-test.yml +++ b/.github/workflows/deploy-to.openshift-test.yml @@ -38,8 +38,8 @@ env: MAX_CPU: "100m" MIN_MEM: "200Mi" MAX_MEM: "250Mi" - MIN_REPLICAS: "2" - MAX_REPLICAS: "2" + MIN_REPLICAS: "3" + MAX_REPLICAS: "4" # SITE_URL should have no scheme or port. It will be prepended with https:// HOST_ROUTE: ${{ secrets.SITE_URL }} on: @@ -126,7 +126,7 @@ jobs: oc process -f tools/openshift/backend.dc.yaml -p APP_NAME=${{ env.APP_NAME }} -p REPO_NAME=${{ env.REPO_NAME }} -p BRANCH=${{ env.BRANCH }} -p NAMESPACE=${{ env.OPENSHIFT_NAMESPACE }} -p TAG=${{ steps.get-latest-tag.outputs.tag }} -p MIN_REPLICAS=${{ env.MIN_REPLICAS }} -p MAX_REPLICAS=${{ env.MAX_REPLICAS }} -p MIN_CPU=${{ env.MIN_CPU }} -p MAX_CPU=${{ env.MAX_CPU }} -p MIN_MEM=${{ env.MIN_MEM }} -p MAX_MEM=${{ env.MAX_MEM }} -p HOST_ROUTE=${{ env.HOST_ROUTE }}\ | oc apply -f - - # Process and apply deployment template + # Process and apply deployment template oc process -f tools/openshift/frontend-static.dc.yaml -p APP_NAME=${{ env.APP_NAME }} -p REPO_NAME=${{ env.REPO_NAME }} -p BRANCH=${{ env.BRANCH }} -p NAMESPACE=${{ env.OPENSHIFT_NAMESPACE }} -p TAG=${{ steps.get-latest-tag.outputs.tag }} -p MIN_REPLICAS=${{ env.MIN_REPLICAS }} -p MAX_REPLICAS=${{ env.MAX_REPLICAS }} -p MIN_CPU=${{ env.MIN_CPU }} -p MAX_CPU=${{ env.MAX_CPU }} -p MIN_MEM=${{ env.MIN_MEM }} -p MAX_MEM=${{ env.MAX_MEM }} -p HOST_ROUTE=${{ env.HOST_ROUTE }} -p CA_CERT="${{ env.CA_CERT }}" -p CERTIFICATE="${{ env.CERTIFICATE }}" -p PRIVATE_KEY="${{ env.PRIVATE_KEY }}"\ | oc apply -f - diff --git a/backend/src/components/utils.js b/backend/src/components/utils.js index ea0f424..d65d976 100644 --- a/backend/src/components/utils.js +++ b/backend/src/components/utils.js @@ -1,21 +1,20 @@ const ALLOWED_FILENAMES = new Set([ - - 'districtcontacts', - 'districtmailing', - 'publicschoolcontacts', - 'independentschoolcontacts', - 'allschoolcontacts', - 'allschoolmailing', - 'authoritycontacts', - 'authoritymailing', - 'offshoreschoolrepresentatives' + "districtcontacts", + "districtmailing", + "publicschoolcontacts", + "independentschoolcontacts", + "allschoolcontacts", + "allschoolmailing", + "authoritycontacts", + "authoritymailing", + "offshoreschoolrepresentatives", // Add more allowed filepaths as needed ]); const ALLOWED_SCHOOLCATEGORYCODES = new Set([ - 'PUBLIC', - 'INDEPEND', - 'OFFSHORE' + "PUBLIC", + "INDEPEND", + "OFFSHORE", // Add more allowed filepaths as needed ]); function isAllowedSchoolCategory(category) { @@ -31,17 +30,17 @@ function createList(list, options = {}) { fieldToInclude = null, // Updated option name valueToInclude = null, // Updated option name sortFunction = null, - sortField = null + sortField = null, } = options; const filteredList = list .filter(function (item) { // Change the condition from removal to inclusion - return (!fieldToInclude || item[fieldToInclude] === valueToInclude); + return !fieldToInclude || item[fieldToInclude] === valueToInclude; }) .map(function (item) { const itemData = {}; - fields.forEach(field => { + fields.forEach((field) => { itemData[field] = item[field]; }); return itemData; @@ -58,7 +57,6 @@ function createList(list, options = {}) { }); } - return filteredList; } function removeFieldsByCriteria(inputData, criteria) { @@ -79,355 +77,434 @@ function removeFieldsByCriteria(inputData, criteria) { } function appendMailingAddressDetailsAndRemoveAddresses(data) { if (data && data.addresses && data.addresses.length > 0) { - const physicalAddress = data.addresses.find(address => address.addressTypeCode === 'PHYSICAL'); - if (physicalAddress) { - // Extract specific name-value pairs from the mailing address - const { addressLine1, addressLine2, city, postal, provinceCode, countryCode } = physicalAddress; - - // Add these name-value pairs to the original district object - data.physicalAddressLine1 = addressLine1; - data.physicalAddressLine2 = addressLine2; - data.physicalCity = city; - data.physicalPostal = postal; - data.physicalProvinceCode = provinceCode; - data.physicalCountryCode = countryCode; - - // Remove the "addresses" property - - } - const courierAddress = data.addresses.find(address => address.addressTypeCode === 'MAILING'); - if (courierAddress) { - // Extract specific name-value pairs from the mailing address - const { addressLine1, addressLine2, city, postal, provinceCode, countryCode } = courierAddress; - - // Add these name-value pairs to the original district object - data.mailingAddressLine1 = addressLine1; - data.mailingAddressLine2 = addressLine2; - data.mailingCity = city; - data.mailingPostal = postal; - data.mailingProvinceCode = provinceCode; - data.mailingCountryCode = countryCode; - - // Remove the "addresses" property - } - delete data.addresses; - delete data.contacts; - } -} -function addDistrictLabels(jsonData, districtList) { - if (jsonData.content && Array.isArray(jsonData.content)) { - jsonData.content.forEach(dataItem => { - const district = districtList.find(item => item.districtId === dataItem.districtId); - if (district) { - dataItem.districtNumber = district.districtNumber; - dataItem.districtName = district.displayName; - } - }); + const physicalAddress = data.addresses.find( + (address) => address.addressTypeCode === "PHYSICAL" + ); + if (physicalAddress) { + // Extract specific name-value pairs from the mailing address + const { + addressLine1, + addressLine2, + city, + postal, + provinceCode, + countryCode, + } = physicalAddress; + + // Add these name-value pairs to the original district object + data.physicalAddressLine1 = addressLine1; + data.physicalAddressLine2 = addressLine2; + data.physicalCity = city; + data.physicalPostal = postal; + data.physicalProvinceCode = provinceCode; + data.physicalCountryCode = countryCode; + + // Remove the "addresses" property } - return jsonData - } - - function districtNumberSort(a, b) { - // Convert the strings to numbers for comparison - const numA = parseInt(a, 10); - const numB = parseInt(b, 10); - - if (numA < numB) { - return -1; + const courierAddress = data.addresses.find( + (address) => address.addressTypeCode === "MAILING" + ); + if (courierAddress) { + // Extract specific name-value pairs from the mailing address + const { + addressLine1, + addressLine2, + city, + postal, + provinceCode, + countryCode, + } = courierAddress; + + // Add these name-value pairs to the original district object + data.mailingAddressLine1 = addressLine1; + data.mailingAddressLine2 = addressLine2; + data.mailingCity = city; + data.mailingPostal = postal; + data.mailingProvinceCode = provinceCode; + data.mailingCountryCode = countryCode; + + // Remove the "addresses" property } - if (numA > numB) { - return 1; - } - return 0; + delete data.addresses; + delete data.contacts; } - function formatGrades(grades, schoolGrades) { - const result = {}; - - // Create a set of all school grade codes from the provided grades - const gradeCodesSet = new Set(grades.map(grade => grade.schoolGradeCode)); - - // Include all school grade codes in the result object - for (const grade of grades) { - result[grade.schoolGradeCode] = "Y"; - } - - // Set the value to "N" for school grade codes not in the provided grades - for (const grade of schoolGrades) { - if (!gradeCodesSet.has(grade.schoolGradeCode)) { - result[grade.schoolGradeCode] = "N"; +} +function addDistrictLabels(jsonData, districtList) { + if (jsonData.content && Array.isArray(jsonData.content)) { + jsonData.content.forEach((dataItem) => { + const district = districtList.find( + (item) => item.districtId === dataItem.districtId + ); + if (district) { + dataItem.districtNumber = district.districtNumber; + dataItem.districtName = district.displayName; } - } - - return result; - } - - function sortJSONByDistrictNumber(districts) { - return districts.slice().sort((a, b) => { - const districtNumberA = a['District Number'] || ''; - const districtNumberB = b['District Number'] || ''; - return districtNumberA.localeCompare(districtNumberB, undefined, { numeric: true, sensitivity: 'base' }); }); } - function sortJSONBySchoolCode(schools) { - return schools.slice().sort((a, b) => { - const schoolCodeA = a.mincode || ''; - const schoolCodeB = b.mincode || ''; - return schoolCodeA.localeCompare(schoolCodeB, undefined, { numeric: true, sensitivity: 'base' }); - }); - } + return jsonData; +} - function sortByProperty(arr, propertyName, options = { numeric: true, sensitivity: 'base' }) { - return arr.slice().sort((a, b) => { - const valueA = a[propertyName] || ''; - const valueB = b[propertyName] || ''; - return valueA.localeCompare(valueB, undefined, options); - }); - } - - function rearrangeAndRelabelObjectProperties(object, propertyList) { - const reorderedObject = {}; - propertyList.forEach((propertyInfo) => { - const prop = propertyInfo.property; - const label = propertyInfo.label; - reorderedObject[label] = object.hasOwnProperty(prop) ? object[prop] : ""; - }); - return reorderedObject; - } +function districtNumberSort(a, b) { + // Convert the strings to numbers for comparison + const numA = parseInt(a, 10); + const numB = parseInt(b, 10); - function normalizeJsonObject(sourceArray, referenceArray, matchKey, condition, includeFields) { - return sourceArray.map((item) => { - const matchingItem = referenceArray.find( - (info) => info[matchKey] === item[matchKey] && (!condition || condition(info)) - ); - if (matchingItem) { - return { - ...item, - ...includeFields.reduce((result, field) => { - result[matchKey + "_" + field] = matchingItem[field]; - return result; - }, {}), - }; - } - return item; - }); + if (numA < numB) { + return -1; } - function filterRemoveByField(data, field, valuesToExclude) { - return data.filter(item => !valuesToExclude.includes(item[field])); + if (numA > numB) { + return 1; } - function filterIncludeByField(data, field, valuesToInclude) { - return data.filter(item => valuesToInclude.includes(item[field])); - } - - function filterByPubliclyAvailableCodes(jsonArray, fieldName, publicCodes) { - // Filter the array based on the condition - const filteredArray = jsonArray.filter(item => { - // Extract the field value (or use an empty string if the field is not present) - const fieldValue = item[fieldName] || ''; - - // Check if the fieldValue exactly matches any string from the stringsToRemove array - return publicCodes.includes(fieldValue); - }); - - return filteredArray; + return 0; +} +function formatGrades(grades, schoolGrades) { + const result = {}; + + // Create a set of all school grade codes from the provided grades + const gradeCodesSet = new Set(grades.map((grade) => grade.schoolGradeCode)); + + // Include all school grade codes in the result object + for (const grade of grades) { + result[grade.schoolGradeCode] = "Y"; } - function filterByField(jsonArray, fieldName, stringsToRemove) { - // Filter the array based on the condition - const filteredArray = jsonArray.filter(item => { - // Extract the field value (or use an empty string if the field is not present) - const fieldValue = item[fieldName] || ''; - - // Check if the fieldValue exactly matches any string from the stringsToRemove array - return !stringsToRemove.includes(fieldValue); - }); - - return filteredArray; + + // Set the value to "N" for school grade codes not in the provided grades + for (const grade of schoolGrades) { + if (!gradeCodesSet.has(grade.schoolGradeCode)) { + result[grade.schoolGradeCode] = "N"; + } } - function filterByOpenedAndClosedDate(data){ - const currentDate = new Date(); - - return data.filter(item => { - const closedDate = item.closedDate ? new Date(item.closedDate) : null; - const openedDate = item.openedDate ? new Date(item.openedDate) : null; - - return (closedDate === null && currentDate > openedDate) || (currentDate < closedDate && currentDate > openedDate) ; + + return result; +} + +function sortJSONByDistrictNumber(districts) { + return districts.slice().sort((a, b) => { + const districtNumberA = a["District Number"] || ""; + const districtNumberB = b["District Number"] || ""; + return districtNumberA.localeCompare(districtNumberB, undefined, { + numeric: true, + sensitivity: "base", }); - } - function filterByExpiryDate(data) { - const currentDate = new Date(); - - return data.filter(item => { - const expiryDate = item.expiryDate ? new Date(item.expiryDate) : null; - const effectiveDate = item.effectiveDate ? new Date(item.effectiveDate) : null; - - return (expiryDate === null && currentDate > effectiveDate) || (currentDate < expiryDate && currentDate > effectiveDate) ; + }); +} +function sortJSONBySchoolCode(schools) { + return schools.slice().sort((a, b) => { + const schoolCodeA = a.mincode || ""; + const schoolCodeB = b.mincode || ""; + return schoolCodeA.localeCompare(schoolCodeB, undefined, { + numeric: true, + sensitivity: "base", }); - } - function getArrayofNonPubliclyAvailableCodes(codes, field) { - if (!Array.isArray(codes)) { - throw new Error('Invalid input. Expecting an array of objects.'); - } - - // Filter out objects where "publiclyAvailable" is false - const nonPubliclyAvailableCodes = codes - .filter(item => item && item.publiclyAvailable !== true) - .map(item => item[field]); - - return nonPubliclyAvailableCodes; - } - function addFundingGroups(schools, fundingGroups) { - try { - // Process each school in the array - const schoolsWithFunding = schools.map(school => { - // Find all matching funding groups by mincode - const matchingFundingGroups = fundingGroups.filter(fundingGroup => - fundingGroup.mincode === school.mincode - ); - - const schoolWithFunding = { - ...school, - primaryK3: "", // Replace with an appropriate default value - elementary47: "", // Replace with an appropriate default value - juniorSecondary810: "", // Replace with an appropriate default value - seniorSecondary1112: "" // Replace with an appropriate default value - }; - - // Iterate through the matching funding groups - matchingFundingGroups.forEach(matchingFundingGroup => { - // Access the fundingGroupCode and fundingSubCode properties - const fundingGroupCode = matchingFundingGroup.fundingGroupCode; - const fundingSubCode = matchingFundingGroup.fundingGroupSubCode; - - // Check the fundingSubCode and update the school information - switch (fundingSubCode) { - case "01": - schoolWithFunding.primaryK3 = fundingGroupCode; - break; - case "04": - schoolWithFunding.elementary47 = fundingGroupCode; - break; - case "08": - schoolWithFunding.juniorSecondary810 = fundingGroupCode; - break; - case "11": - schoolWithFunding.seniorSecondary1112 = fundingGroupCode; - break; - default: - break; - } - }); - - return schoolWithFunding; - }); + }); +} - return schoolsWithFunding; - } catch (error) { - // Handle the error here, you can log it or perform other actions - console.error("An error occurred in addFundingGroups:", error); - // Optionally, you can rethrow the error if needed - throw error; - } +function sortByProperty( + arr, + propertyName, + options = { numeric: true, sensitivity: "base" } +) { + return arr.slice().sort((a, b) => { + const valueA = a[propertyName] || ""; + const valueB = b[propertyName] || ""; + return valueA.localeCompare(valueB, undefined, options); + }); +} + +function rearrangeAndRelabelObjectProperties(object, propertyList) { + const reorderedObject = {}; + propertyList.forEach((propertyInfo) => { + const prop = propertyInfo.property; + const label = propertyInfo.label; + reorderedObject[label] = object.hasOwnProperty(prop) ? object[prop] : ""; + }); + return reorderedObject; } - function getArrayofPubliclyAvailableCodes(codes, field) { - if (!Array.isArray(codes)) { - throw new Error('Invalid input. Expecting an array of objects.'); + +function normalizeJsonObject( + sourceArray, + referenceArray, + matchKey, + condition, + includeFields +) { + return sourceArray.map((item) => { + const matchingItem = referenceArray.find( + (info) => + info[matchKey] === item[matchKey] && (!condition || condition(info)) + ); + if (matchingItem) { + return { + ...item, + ...includeFields.reduce((result, field) => { + result[matchKey + "_" + field] = matchingItem[field]; + return result; + }, {}), + }; } - - // Filter out objects where "publiclyAvailable" is true - const publiclyAvailableCodes = codes - .filter(item => item && item.publiclyAvailable === true) - .map(item => item[field]); - - return publiclyAvailableCodes; + return item; + }); +} +function filterRemoveByField(data, field, valuesToExclude) { + return data.filter((item) => !valuesToExclude.includes(item[field])); +} +function filterIncludeByField(data, field, valuesToInclude) { + return data.filter((item) => valuesToInclude.includes(item[field])); +} + +function filterByPubliclyAvailableCodes(jsonArray, fieldName, publicCodes) { + // Filter the array based on the condition + const filteredArray = jsonArray.filter((item) => { + // Extract the field value (or use an empty string if the field is not present) + const fieldValue = item[fieldName] || ""; + + // Check if the fieldValue exactly matches any string from the stringsToRemove array + return publicCodes.includes(fieldValue); + }); + + return filteredArray; +} +function filterByField(jsonArray, fieldName, stringsToRemove) { + // Filter the array based on the condition + const filteredArray = jsonArray.filter((item) => { + // Extract the field value (or use an empty string if the field is not present) + const fieldValue = item[fieldName] || ""; + + // Check if the fieldValue exactly matches any string from the stringsToRemove array + return !stringsToRemove.includes(fieldValue); + }); + + return filteredArray; +} +function filterByOpenedAndClosedDate(data) { + const currentDate = new Date(); + + return data.filter((item) => { + const closedDate = item.closedDate ? new Date(item.closedDate) : null; + const openedDate = item.openedDate ? new Date(item.openedDate) : null; + + return ( + (closedDate === null && currentDate > openedDate) || + (currentDate < closedDate && currentDate > openedDate) + ); + }); +} +function filterByExpiryDate(data) { + const currentDate = new Date(); + + return data.filter((item) => { + const expiryDate = item.expiryDate ? new Date(item.expiryDate) : null; + const effectiveDate = item.effectiveDate + ? new Date(item.effectiveDate) + : null; + + return ( + (expiryDate === null && currentDate > effectiveDate) || + (currentDate < expiryDate && currentDate > effectiveDate) + ); + }); +} +function getArrayofNonPubliclyAvailableCodes(codes, field) { + if (!Array.isArray(codes)) { + throw new Error("Invalid input. Expecting an array of objects."); } - function createSchoolCache(schoolData, schoolGrades) { - // Preload convertedGrades with schoolGrades.schoolGradeCode and set the value to "N" + // Filter out objects where "publiclyAvailable" is false + const nonPubliclyAvailableCodes = codes + .filter((item) => item && item.publiclyAvailable !== true) + .map((item) => item[field]); - // Map over each school object - return schoolData.map((school) => { - const convertedGrades = {}; - schoolGrades.forEach((grade) => { - convertedGrades[grade.schoolGradeCode] = "N"; - }); + return nonPubliclyAvailableCodes; +} +function addFundingGroups(schools, fundingGroups) { + try { + // Process each school in the array + const schoolsWithFunding = schools.map((school) => { + // Find all matching funding groups by mincode + const matchingFundingGroups = fundingGroups.filter( + (fundingGroup) => fundingGroup.mincode === school.mincode + ); - const addressFields = { - mailing: {}, - physical: {}, + const schoolWithFunding = { + ...school, + primaryK3: "", // Replace with an appropriate default value + elementary47: "", // Replace with an appropriate default value + juniorSecondary810: "", // Replace with an appropriate default value + seniorSecondary1112: "", // Replace with an appropriate default value }; - // Loop through the grades and set the value to "Y" for each grade - school.grades.forEach((grade) => { - convertedGrades[grade.schoolGradeCode] = "Y"; + // Iterate through the matching funding groups + matchingFundingGroups.forEach((matchingFundingGroup) => { + // Access the fundingGroupCode and fundingSubCode properties + const fundingGroupCode = matchingFundingGroup.fundingGroupCode; + const fundingSubCode = matchingFundingGroup.fundingGroupSubCode; + + // Check the fundingSubCode and update the school information + switch (fundingSubCode) { + case "01": + schoolWithFunding.primaryK3 = fundingGroupCode; + break; + case "04": + schoolWithFunding.elementary47 = fundingGroupCode; + break; + case "08": + schoolWithFunding.juniorSecondary810 = fundingGroupCode; + break; + case "11": + schoolWithFunding.seniorSecondary1112 = fundingGroupCode; + break; + default: + break; + } }); - // Extract and format principal contact information if it exists - const principalContact = school.contacts.find((contact) => contact.schoolContactTypeCode === "PRINCIPAL"); - if (principalContact) { - school.firstName = principalContact.firstName; - school.lastName = principalContact.lastName; - - - } + return schoolWithFunding; + }); - // Loop through addresses and update the fields based on addressTypeCode - school.addresses.forEach((address) => { - if (address.addressTypeCode === "MAILING") { - Object.keys(address).forEach((field) => { - // Exclude the specified fields - if (![ - "createUser", - "updateUser", - "createDate", - "updateDate", - "schoolAddressId", - "schoolId", - "addressTypeCode" - ].includes(field)) { - addressFields.mailing[`mailing_${field}`] = address[field]; - } - }); - } else if (address.addressTypeCode === "PHYSICAL") { - Object.keys(address).forEach((field) => { - if (![ - "createUser", - "updateUser", - "createDate", - "updateDate", - "schoolAddressId", - "schoolId", - "addressTypeCode" - ].includes(field)) { - addressFields.mailing[`physical_${field}`] = address[field]; - } - }); - } - }); + return schoolsWithFunding; + } catch (error) { + // Handle the error here, you can log it or perform other actions + console.error("An error occurred in addFundingGroups:", error); + // Optionally, you can rethrow the error if needed + throw error; + } +} +function getArrayofPubliclyAvailableCodes(codes, field) { + if (!Array.isArray(codes)) { + throw new Error("Invalid input. Expecting an array of objects."); + } + + // Filter out objects where "publiclyAvailable" is true + const publiclyAvailableCodes = codes + .filter((item) => item && item.publiclyAvailable === true) + .map((item) => item[field]); + + return publiclyAvailableCodes; +} +function createSchoolCache(schoolData, schoolGrades) { + // Preload convertedGrades with schoolGrades.schoolGradeCode and set the value to "N" + + // Map over each school object + return schoolData.map((school) => { + const convertedGrades = {}; + schoolGrades.forEach((grade) => { + convertedGrades[grade.schoolGradeCode] = "N"; + }); + + const addressFields = { + mailing: {}, + physical: {}, + }; + + // Loop through the grades and set the value to "Y" for each grade + school.grades.forEach((grade) => { + convertedGrades[grade.schoolGradeCode] = "Y"; + }); + + // Extract and format principal contact information if it exists + const principalContact = school.contacts.find( + (contact) => contact.schoolContactTypeCode === "PRINCIPAL" + ); + if (principalContact) { + school.firstName = principalContact.firstName; + school.lastName = principalContact.lastName; + } - // Concatenate neighborhoodLearningTypeCode into a single string - const nlc = school.neighborhoodLearning.map(learning => learning.neighborhoodLearningTypeCode).join(' | '); - - // Merge the address fields and nlc into the school object - Object.assign(school, convertedGrades, addressFields.mailing, addressFields.physical, { nlc }); - - // Remove the original grades property and the updated address object - delete school.grades; - delete school.addresses; - delete school.neighborhoodLearning; - delete school.createUser; - delete school.updateUser; - delete school.updateDate; - delete school.createDate; - delete school.schoolId; - delete school.openedDate; - delete school.closedDate; - delete school.notes; - delete school.schoolMove.createUser; - delete school.schoolMove; - - // Remove the contacts property - delete school.contacts; - - return school; + // Loop through addresses and update the fields based on addressTypeCode + school.addresses.forEach((address) => { + if (address.addressTypeCode === "MAILING") { + Object.keys(address).forEach((field) => { + // Exclude the specified fields + if ( + ![ + "createUser", + "updateUser", + "createDate", + "updateDate", + "schoolAddressId", + "schoolId", + "addressTypeCode", + ].includes(field) + ) { + addressFields.mailing[`mailing_${field}`] = address[field]; + } + }); + } else if (address.addressTypeCode === "PHYSICAL") { + Object.keys(address).forEach((field) => { + if ( + ![ + "createUser", + "updateUser", + "createDate", + "updateDate", + "schoolAddressId", + "schoolId", + "addressTypeCode", + ].includes(field) + ) { + addressFields.mailing[`physical_${field}`] = address[field]; + } + }); + } }); + + // Concatenate neighborhoodLearningTypeCode into a single string + const nlc = school.neighborhoodLearning + .map((learning) => learning.neighborhoodLearningTypeCode) + .join(" | "); + + // Merge the address fields and nlc into the school object + Object.assign( + school, + convertedGrades, + addressFields.mailing, + addressFields.physical, + { nlc } + ); + + // Remove the original grades property and the updated address object + delete school.grades; + delete school.addresses; + delete school.neighborhoodLearning; + delete school.createUser; + delete school.updateUser; + delete school.updateDate; + delete school.createDate; + delete school.schoolId; + delete school.openedDate; + delete school.closedDate; + delete school.notes; + delete school.schoolMove.createUser; + delete school.schoolMove; + + // Remove the contacts property + delete school.contacts; + + return school; + }); } - module.exports = {addFundingGroups, filterByOpenedAndClosedDate, filterByPubliclyAvailableCodes, getArrayofPubliclyAvailableCodes, filterByExpiryDate, filterRemoveByField,filterIncludeByField, sortByProperty,getArrayofNonPubliclyAvailableCodes,filterByField,appendMailingAddressDetailsAndRemoveAddresses,sortJSONBySchoolCode,sortJSONByDistrictNumber,normalizeJsonObject, removeFieldsByCriteria, createList, isSafeFilePath,isAllowedSchoolCategory, addDistrictLabels, districtNumberSort, createSchoolCache, formatGrades, rearrangeAndRelabelObjectProperties}; \ No newline at end of file +module.exports = { + addFundingGroups, + filterByOpenedAndClosedDate, + filterByPubliclyAvailableCodes, + getArrayofPubliclyAvailableCodes, + filterByExpiryDate, + filterRemoveByField, + filterIncludeByField, + sortByProperty, + getArrayofNonPubliclyAvailableCodes, + filterByField, + appendMailingAddressDetailsAndRemoveAddresses, + sortJSONBySchoolCode, + sortJSONByDistrictNumber, + normalizeJsonObject, + removeFieldsByCriteria, + createList, + isSafeFilePath, + isAllowedSchoolCategory, + addDistrictLabels, + districtNumberSort, + createSchoolCache, + formatGrades, + rearrangeAndRelabelObjectProperties, +}; diff --git a/backend/src/routes/district-router.js b/backend/src/routes/district-router.js index 8df54a9..a2d9c39 100644 --- a/backend/src/routes/district-router.js +++ b/backend/src/routes/district-router.js @@ -7,8 +7,22 @@ const axios = require("axios"); const fs = require("fs"); const path = require("path"); const { checkToken } = require("../components/auth"); -const { listCache} = require("../components/cache"); -const {addFundingGroups, getArrayofPubliclyAvailableCodes,filterRemoveByField, filterByExpiryDate, getArrayofNonPubliclyAvailableCodes, filterByField,appendMailingAddressDetailsAndRemoveAddresses, rearrangeAndRelabelObjectProperties, addDistrictLabels, normalizeJsonObject, sortJSONByDistrictNumber, removeFieldsByCriteria, filterByPubliclyAvailableCodes} = require("../components/utils.js") +const { listCache } = require("../components/cache"); +const { + addFundingGroups, + getArrayofPubliclyAvailableCodes, + filterRemoveByField, + filterByExpiryDate, + getArrayofNonPubliclyAvailableCodes, + filterByField, + appendMailingAddressDetailsAndRemoveAddresses, + rearrangeAndRelabelObjectProperties, + addDistrictLabels, + normalizeJsonObject, + sortJSONByDistrictNumber, + removeFieldsByCriteria, + filterByPubliclyAvailableCodes, +} = require("../components/utils.js"); //Batch Routes router.get("/all-contacts", checkToken, getAllDistrictContacts); @@ -33,7 +47,7 @@ function addContactTypeLabels(districtDataResponse, nonPublicContactTypeCodes) { Array.isArray(updatedDistrictData.contacts) ) { updatedDistrictData.contacts.forEach((contact) => { - const matchingType = nonPublicContactTypeCodes.find( + const matchingType = nonPublicContactTypeCodes?.find( (codeObj) => codeObj.districtContactTypeCode === contact.districtContactTypeCode ); @@ -381,13 +395,13 @@ async function getDistrict(req, res) { }); const contactTypeCodes = await getDistrictCodes(req); - const schoolCategoryCodes = await listCache.get("categoryCodes") - const facilityCodes = await listCache.get("facilityCodes") - const fundingGroups = await listCache.get("fundingGroups") - const districtContactCodeTypes = await listCache.get("codesList") - const nonPublicContactTypeCodes = getNonPublicContactTypeCodes(contactTypeCodes); - - + const schoolCategoryCodes = await listCache.get("categoryCodes"); + const facilityCodes = await listCache.get("facilityCodes"); + const fundingGroups = await listCache.get("fundingGroups"); + const districtContactCodeTypes = await listCache.get("codesList"); + const nonPublicContactTypeCodes = + getNonPublicContactTypeCodes(contactTypeCodes); + const districtDataPublic = removeContacts( districtDataResponse.data, nonPublicContactTypeCodes @@ -408,28 +422,43 @@ async function getDistrict(req, res) { districtDataPublicWithLabels.contacts ); - districtSchoolsResponse.data.content = normalizeJsonObject(districtSchoolsResponse.data.content, schoolCategoryCodes, "schoolCategoryCode", null, ["label", "description"]); - districtSchoolsResponse.data.content = normalizeJsonObject(districtSchoolsResponse.data.content, facilityCodes, "faciltyTypeCode", null, ["label", "description"]); - districtSchoolsResponse.data.content = addFundingGroups(districtSchoolsResponse.data.content, fundingGroups) + districtSchoolsResponse.data.content = normalizeJsonObject( + districtSchoolsResponse.data.content, + schoolCategoryCodes, + "schoolCategoryCode", + null, + ["label", "description"] + ); + districtSchoolsResponse.data.content = normalizeJsonObject( + districtSchoolsResponse.data.content, + facilityCodes, + "faciltyTypeCode", + null, + ["label", "description"] + ); + districtSchoolsResponse.data.content = addFundingGroups( + districtSchoolsResponse.data.content, + fundingGroups + ); - const today = new Date(); - const filteredSchoolsResponse = districtSchoolsResponse.data.content.filter( - (obj) => { - // if openedDate is a valid date is less than today, keep the object - const openedDate = new Date(obj.openedDate); + const today = new Date(); + const filteredSchoolsResponse = districtSchoolsResponse.data.content.filter( + (obj) => { + // if openedDate is a valid date is less than today, keep the object + const openedDate = new Date(obj.openedDate); - // If closedDate is a valid date greater than today, keep the object - const closedDate = new Date(obj.closedDate); + // If closedDate is a valid date greater than today, keep the object + const closedDate = new Date(obj.closedDate); - // return obj IF closedDate does not exist OR is after than current date - // AND openedDate exists AND is before current date - return ( - (!obj.closedDate || closedDate > today) && - obj.openedDate && - openedDate < today - ); - } - ); + // return obj IF closedDate does not exist OR is after than current date + // AND openedDate exists AND is before current date + return ( + (!obj.closedDate || closedDate > today) && + obj.openedDate && + openedDate < today + ); + } + ); const districtJSON = { districtData: districtDataPublicWithLabels, districtSchools: filteredSchoolsResponse, diff --git a/frontend/src/assets/main.css b/frontend/src/assets/main.css index c438d8c..59a5f71 100644 --- a/frontend/src/assets/main.css +++ b/frontend/src/assets/main.css @@ -1,35 +1,39 @@ @import './base.css'; #app { - max-width: 1280px; - margin: 0 auto; - padding: 0rem; font-family: 'BCSans', 'Noto Sans', Verdana, Arial, sans-serif; font-weight: normal; } +#main { + max-width: 1280px; + padding: 0rem; +} + a { color: #2a6496; } +div.v-application__wrap { + min-height: 100vh; /* override dvh property */ +} + +.full-width { + margin-left: calc(-1*(100vw - 1024px)/2); + margin-right: calc(-1*(100vw - 1024px)/2); +} + /** Need to look at how CSS is loaded in since Vuetify defaults are overriding our custom CSS. Fixing this now could risk delaying other releases, so we will have to wait on this one - SF **/ .v-alert, .v-alert--variant-outlined { border-radius: 8px !important; } -@media (max-width: 768px) { - #app { - min-width: 1130px; /* Adjust as needed */ - padding: 0; /* Adjust as needed */ - } - - .full-width { - margin: 0 calc(-1*(100vw - 768px)/2); - padding: 0 calc((100vw - 768px)/2); - background-color: red; - } +.v-breadcrumbs-item--disabled { + opacity: 1; + font-weight: bold; } + .school-search{ margin-left:175px } @@ -38,7 +42,7 @@ Fixing this now could risk delaying other releases, so we will have to wait on t } .breadcrumbs { margin: 0 -40em; - padding-left: 40em !important; + padding-left: 40em; } /* styles button to look like links */ @@ -49,10 +53,12 @@ button.link-btn, .link-btn .v-btn__content { } .territorial-acknowledgement { - background-color: #494949; - color: #fff; - border-bottom: 3px solid #FCBA19; - border-top: 3px solid #FCBA19; + background-color: #292929; + color: #faf9f8; + font-size: 14px; + line-height: 21px; + border-bottom: 4px solid #FCBA19; + border-top: 4px solid #FCBA19; } .v-toolbar__extension { @@ -63,6 +69,40 @@ button.link-btn, .link-btn .v-btn__content { .v-btn-align-left { justify-content: normal !important; } + +@media (max-width: 1024px) { + .full-width { + margin-left: unset; + margin-right: unset; + } +} + +@media (max-width: 960px) { + #app { + /* min-width: 600px; */ + padding: 0; /* Adjust as needed */ + } + + .breadcrumbs { + margin: 0; + padding-left: 12px; + } + + h1 span.institute-name { + font-size: 1.5rem; + font-weight: bold; + } + + /* + * wraps button text as long as the class 'wrap' is added to v-btn component + * This is a workaround since a text wrap option is not available for buttons in Vuetify + */ + .wrap .v-btn__content { + white-space: pre-wrap; +} + +} + @media (hover: hover) { a:hover { background-color: #0000; @@ -82,11 +122,14 @@ button.link-btn, .link-btn .v-btn__content { #app { display: grid; - padding: 0; + padding: 0!important; + margin: 0 auto; } - .full-width { - margin: 0 calc(-1*(100vw - 1024px)/2); - padding: 0 calc((100vw - 1024px)/2); - } -} + /* .full-width { + position: relative; + width: 100vw; + left: calc(-1*(100vw - 1280px)/2); + padding: 0!important; + } */ +} \ No newline at end of file diff --git a/frontend/src/components/AuthoritySelect.vue b/frontend/src/components/AuthoritySelect.vue index 747f11d..7309103 100644 --- a/frontend/src/components/AuthoritySelect.vue +++ b/frontend/src/components/AuthoritySelect.vue @@ -19,46 +19,43 @@ function goToAuthority() { } }) } - -function downloadAuthorityMailing() { - alert('TODO - Implement authority mailing download') -} -function downloadAuthorityContacts() { - alert('TODO - Implement authority contacts download') -}