diff --git a/backend/src/components/document.js b/backend/src/components/document.js index 29c2165f..59b6c4e8 100644 --- a/backend/src/components/document.js +++ b/backend/src/components/document.js @@ -41,10 +41,12 @@ async function getApplicationDocuments(req, res) { async function deleteUploadedDocuments(req, res) { try { - let deletedDocuments = req.body; - for (let annotationid of deletedDocuments) { - await deleteDocument(annotationid); - } + const deletedDocuments = req.body; + await Promise.all( + deletedDocuments.map(async (annotationId) => { + await deleteDocument(annotationId); + }), + ); return res.sendStatus(HttpStatus.OK); } catch (e) { log.error(e); diff --git a/backend/src/util/mapping/Mappings.js b/backend/src/util/mapping/Mappings.js index a6117c6a..b5ea8e22 100644 --- a/backend/src/util/mapping/Mappings.js +++ b/backend/src/util/mapping/Mappings.js @@ -57,6 +57,8 @@ const CCFRIFacilityMappings = [ { back: 'ccof_chargefeeccfri', front: 'hasClosureFees' }, { back: 'ccof_applicationccfriid', front: 'ccfriApplicationId' }, { back: 'ccof_unlock_rfi', front: 'unlockRfi' }, + { back: 'ccof_unlock_afs', front: 'unlockAfs' }, + { back: 'ccof_unlock_afsenable', front: 'enableAfs' }, { back: 'ccof_afs_status', front: 'afsStatus' }, ]; diff --git a/frontend/src/components/SummaryDeclaration.vue b/frontend/src/components/SummaryDeclaration.vue index aa04afb8..897c066c 100644 --- a/frontend/src/components/SummaryDeclaration.vue +++ b/frontend/src/components/SummaryDeclaration.vue @@ -172,6 +172,14 @@ @is-summary-valid="isFormComplete" /> + + + @@ -141,10 +142,25 @@ export default { await this.save(false); next(); }, + props: { + readonly: { + type: Boolean, + default: false, + }, + ccfriApplicationId: { + type: String, + default: '', + }, + facilityId: { + type: String, + default: '', + }, + }, data() { return { afs: {}, documentsToUpload: [], + uploadedDocumentsToDelete: [], isValidForm: false, processing: false, }; @@ -156,15 +172,17 @@ export default { 'formattedProgramYear', 'isApplicationSubmitted', 'programYearId', - 'uploadedDocuments', + 'applicationUploadedDocuments', ]), ...mapState(useCcfriAppStore, ['approvableFeeSchedules']), ...mapState(useNavBarStore, ['navBarList', 'nextPath', 'previousPath']), currentFacility() { - return this.navBarList?.find((el) => el.ccfriApplicationId === this.$route.params.urlGuid); + return this.facilityId + ? this.navBarList?.find((el) => el.facilityId === this.facilityId) + : this.navBarList?.find((el) => el.ccfriApplicationId === this.$route.params.urlGuid); }, filteredUploadedDocuments() { - return this.uploadedDocuments?.filter( + return this.applicationUploadedDocuments?.filter( (document) => document.documentType === DOCUMENT_TYPES.APPLICATION_AFS && document.facilityId === this.currentFacility?.facilityId, @@ -175,7 +193,7 @@ export default { }, // Note: CCFRI-3752 - AFS for change request is not in scope at this time. isReadOnly() { - return this.isLoading || (this.isApplicationSubmitted && !this.currentFacility?.unlockAfs); + return this.readonly || this.isLoading || (this.isApplicationSubmitted && !this.currentFacility?.unlockAfs); }, isSupportingDocumentsUploaded() { return this.filteredUploadedDocuments?.length + this.documentsToUpload?.length > 0; @@ -212,7 +230,9 @@ export default { ...mapActions(useSupportingDocumentUploadStore, ['saveUploadedDocuments']), isEmpty, reloadAfs() { - this.afs = this.approvableFeeSchedules?.find((item) => item.ccfriApplicationId === this.$route.params.urlGuid); + this.afs = this.ccfriApplicationId + ? this.approvableFeeSchedules?.find((item) => item.ccfriApplicationId === this.ccfriApplicationId) + : this.approvableFeeSchedules?.find((item) => item.ccfriApplicationId === this.$route.params.urlGuid); }, next() { this.$router.push(this.nextPath); @@ -233,6 +253,8 @@ export default { }; await this.updateApplicationCCFRI(this.$route.params.urlGuid, payload); await this.processDocumentsToUpload(); + await DocumentService.deleteDocuments(this.uploadedDocumentsToDelete); + await this.getApplicationUploadedDocuments(); this.setNavBarAfsComplete({ ccfriId: this.$route.params.urlGuid, complete: this.isFormComplete }); if (showMessage) { this.setSuccessAlert('Changes Successfully Saved'); @@ -248,18 +270,20 @@ export default { this.documentsToUpload = updatedDocuments; }, async processDocumentsToUpload() { - try { - const payload = cloneDeep(this.documentsToUpload); - payload.forEach((document) => { - document.ccof_applicationid = this.applicationId; - document.ccof_facility = this.currentFacility?.facilityId; - delete document.file; - }); - await DocumentService.createDocuments(payload); - await this.getApplicationUploadedDocuments(); - } catch { - this.setFailureAlert('An error occurred while saving. Please try again later.'); + const payload = cloneDeep(this.documentsToUpload); + payload.forEach((document) => { + document.ccof_applicationid = this.applicationId; + document.ccof_facility = this.currentFacility?.facilityId; + delete document.file; + }); + await DocumentService.createDocuments(payload); + }, + updateUploadedDocumentsToDelete(annotationId) { + const index = this.applicationUploadedDocuments?.findIndex((item) => item.annotationId === annotationId); + if (index > -1) { + this.applicationUploadedDocuments?.splice(index, 1); } + this.uploadedDocumentsToDelete?.push(annotationId); }, }, }; diff --git a/frontend/src/components/summary/group/AFSSummary.vue b/frontend/src/components/summary/group/AFSSummary.vue new file mode 100644 index 00000000..92b49377 --- /dev/null +++ b/frontend/src/components/summary/group/AFSSummary.vue @@ -0,0 +1,112 @@ + + + diff --git a/frontend/src/components/util/AppDocumentUpload.vue b/frontend/src/components/util/AppDocumentUpload.vue index d9767794..87113b22 100644 --- a/frontend/src/components/util/AppDocumentUpload.vue +++ b/frontend/src/components/util/AppDocumentUpload.vue @@ -4,7 +4,10 @@ {{ title }} (Required) -
{{ DOCUMENTS_REQUIREMENT_MESSAGE }}
+
+ The maximum file size is 2MB for each document. Accepted file types are jpg, jpeg, heic, png, pdf, docx, doc, xls, + and xlsx. +
@@ -79,7 +79,6 @@ import { uuid } from 'vue-uuid'; import AppButton from '@/components/guiComponents/AppButton.vue'; import alertMixin from '@/mixins/alertMixin.js'; -import { DOCUMENTS_REQUIREMENT_MESSAGE } from '@/utils/constants'; import { humanFileSize, getFileExtensionWithDot, getFileNameWithMaxNameLength } from '@/utils/file'; export default { @@ -143,8 +142,7 @@ export default { }, }, created() { - this.DOCUMENTS_REQUIREMENT_MESSAGE = DOCUMENTS_REQUIREMENT_MESSAGE; - this.MAX_FILE_SIZE = 4194304; // 4 MB + this.MAX_FILE_SIZE = 2100000; // 2.18 MB is max size since after base64 encoding it might grow upto 3 MB. this.fileExtensionAccept = ['.pdf', '.png', '.jpg', '.jpeg', '.heic', '.doc', '.docx', '.xls', '.xlsx']; this.fileFormats = 'PDF, JPEG, JPG, PNG, HEIC, DOC, DOCX, XLS, and XLSX'; this.fileRules = [ diff --git a/frontend/src/components/util/NavBar.vue b/frontend/src/components/util/NavBar.vue index 0db4106f..a430573a 100644 --- a/frontend/src/components/util/NavBar.vue +++ b/frontend/src/components/util/NavBar.vue @@ -99,6 +99,7 @@ diff --git a/frontend/src/services/documentService.js b/frontend/src/services/documentService.js index bfd7d80d..77b4a1b2 100644 --- a/frontend/src/services/documentService.js +++ b/frontend/src/services/documentService.js @@ -1,3 +1,5 @@ +import { isEmpty } from 'lodash'; + import ApiService from '@/common/apiService'; import { ApiRoutes } from '@/utils/constants'; @@ -22,6 +24,7 @@ export default { async createDocuments(payload) { try { + if (isEmpty(payload)) return; await ApiService.apiAxios.post(ApiRoutes.DOCUMENT_APPLICATION, payload); } catch (error) { console.log(`Failed to create application's documents - ${error}`); @@ -29,9 +32,10 @@ export default { } }, - async deleteDocuments(payload) { + async deleteDocuments(deletedFiles) { try { - await ApiService.apiAxios.delete(ApiRoutes.DOCUMENT_APPLICATION, payload); + if (isEmpty(deletedFiles)) return; + await ApiService.apiAxios.delete(ApiRoutes.DOCUMENT_APPLICATION, { data: deletedFiles }); } catch (error) { console.log(`Failed to delete application's documents - ${error}`); throw error; diff --git a/frontend/src/store/application.js b/frontend/src/store/application.js index 9c1464db..880ec135 100644 --- a/frontend/src/store/application.js +++ b/frontend/src/store/application.js @@ -30,7 +30,7 @@ export const useApplicationStore = defineStore('application', { ccofConfirmationEnabled: false, applicationMap: new Map(), - uploadedDocuments: [], + applicationUploadedDocuments: [], }), actions: { setApplicationId(value) { @@ -148,7 +148,7 @@ export const useApplicationStore = defineStore('application', { }, async getApplicationUploadedDocuments() { try { - this.uploadedDocuments = await DocumentService.getApplicationUploadedDocuments(this.applicationId); + this.applicationUploadedDocuments = await DocumentService.getApplicationUploadedDocuments(this.applicationId); } catch (error) { console.log(error); throw error; diff --git a/frontend/src/store/navBar.js b/frontend/src/store/navBar.js index 241ef7a5..209ae0df 100644 --- a/frontend/src/store/navBar.js +++ b/frontend/src/store/navBar.js @@ -3,10 +3,9 @@ import { defineStore } from 'pinia'; import { useAppStore } from '@/store/app.js'; import { useApplicationStore } from '@/store/application.js'; -import { useCcfriAppStore } from '@/store/ccfriApp.js'; import { useReportChangesStore } from '@/store/reportChanges.js'; import { filterFacilityListForPCF } from '@/utils/common.js'; -import { AFS_STATUSES, CHANGE_REQUEST_TYPES, PATHS } from '@/utils/constants.js'; +import { CHANGE_REQUEST_TYPES, PATHS } from '@/utils/constants.js'; function getActiveIndex(items) { let foundIndex = -1; @@ -391,15 +390,5 @@ export const useNavBarStore = defineStore('navBar', { this.navBarList = filterFacilityListForPCF(this.userProfileList, this.isRenewal, applicationStatus); } }, - checkApprovableFeeSchedulesComplete() { - const ccfriApStore = useCcfriAppStore(); - this.userProfileList?.forEach((facility) => { - const afs = ccfriApStore?.approvableFeeSchedules?.find( - (item) => item.ccfriApplicationId === facility?.ccfriApplicationId, - ); - facility.isAFSComplete = [AFS_STATUSES.ACCEPT, AFS_STATUSES.DECLINE].includes(afs?.afsStatus); - }); - this.refreshNavBarList(); - }, }, }); diff --git a/frontend/src/utils/constants.js b/frontend/src/utils/constants.js index 94f706ff..29ea3dba 100644 --- a/frontend/src/utils/constants.js +++ b/frontend/src/utils/constants.js @@ -295,9 +295,6 @@ export const PARENT_FEE_FREQUENCIES = Object.freeze({ DAILY: 100000002, }); -export const DOCUMENTS_REQUIREMENT_MESSAGE = - 'The maximum file size is 2MB for each document. Accepted file types are jpg, jpeg, heic, png, pdf, docx, doc, xls, and xlsx.'; - export const DOCUMENT_TYPES = Object.freeze({ APPLICATION_AFS: 'AFS - Supporting Documents', });