Skip to content

Commit

Permalink
ofmcc-5679 - service delivery document upload
Browse files Browse the repository at this point in the history
  • Loading branch information
vietle-cgi committed Aug 21, 2024
1 parent 5287aa6 commit 7b4b2ac
Show file tree
Hide file tree
Showing 7 changed files with 337 additions and 116 deletions.
7 changes: 7 additions & 0 deletions frontend/src/assets/css/main.css
Original file line number Diff line number Diff line change
Expand Up @@ -161,6 +161,13 @@ input[type='number'] {
min-height: 150px;
}

.grey-card {
border: 1px solid #0000001a;
background-color: #0f0f0f09;
border-radius: 4px;
padding: 12px;
}

.blue-background {
background-color: #003366;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,45 +2,72 @@
<v-container fluid class="pa-2 pb-0">
<template v-if="!readonly">
<AppMissingInfoError
v-if="isEmpty(currentApplication?.licences)"
v-if="isEmpty(currentApplication?.licences) || !isCCOFMissingDetailComplete()"
:to="{ name: APPLICATION_ROUTES.SERVICE_DELIVERY, hash: '#account-management', params: { applicationGuid: $route.params.applicationGuid } }">
{{ APPLICATION_ERROR_MESSAGES.LICENCE_INFO }}
</AppMissingInfoError>
<AppMissingInfoError v-else-if="!isSplitClassroomComplete()" :to="{ name: APPLICATION_ROUTES.SERVICE_DELIVERY, params: { applicationGuid: $route.params.applicationGuid } }">
{{ APPLICATION_ERROR_MESSAGES.SPLIT_CLASSROOM_INFO }}
</AppMissingInfoError>
<AppMissingInfoError
v-else-if="!currentApplication?.licenceDeclaration"
:to="{ name: APPLICATION_ROUTES.SERVICE_DELIVERY, hash: '#confirmation', params: { applicationGuid: $route.params.applicationGuid } }">
{{ APPLICATION_ERROR_MESSAGES.LICENCE_CONFIRMATION }}
</AppMissingInfoError>
<AppMissingInfoError v-else-if="!isServiceDeliveryComplete" :to="{ name: APPLICATION_ROUTES.SERVICE_DELIVERY, params: { applicationGuid: $route.params.applicationGuid } }">
{{ !allCCOFMissingDetailComplete ? APPLICATION_ERROR_MESSAGES.LICENCE_INFO : APPLICATION_ERROR_MESSAGES.SPLIT_CLASSROOM_INFO }}
</AppMissingInfoError>
</template>
<v-expansion-panels v-if="showSummary" v-model="panel" multiple>
<v-expansion-panel v-for="licence in licences" :key="licence.licenceId" :value="licence.licenceId">
<v-expansion-panel-title>
<LicenceHeader :licence="licence" />
</v-expansion-panel-title>
<v-expansion-panel-text>
<LicenceDetails :licence="licence" :readOnly="true" />
<v-card>
<LicenceDetails :licence="licence" :read-only="true" />
</v-card>
<v-card class="mt-4">
<AppDocumentUpload class="pa-4" :readonly="true" :document-label="DOCUMENT_LABELS.LICENCE" :document-type="`Licence ${licence?.licence}`" :uploaded-documents="getLicenceDocument(licence)">
<AppMissingInfoError
v-if="!readonly && !getLicenceDocument(licence)?.length"
:to="{ name: APPLICATION_ROUTES.SERVICE_DELIVERY, hash: `#${licence.licenceId}`, params: { applicationGuid: $route.params.applicationGuid } }">
{{ APPLICATION_ERROR_MESSAGES.DOCUMENT_LICENCE_UPLOAD }}
</AppMissingInfoError>
</AppDocumentUpload>
</v-card>
</v-expansion-panel-text>
</v-expansion-panel>
</v-expansion-panels>

<v-card class="mt-4">
<AppDocumentUpload
class="pa-4"
:readonly="true"
:document-label="DOCUMENT_LABELS.HEALTH_AUTHORITY_REPORT"
:document-type="DOCUMENT_TYPES.HEALTH_AUTHORITY_REPORT"
:uploaded-documents="healthAuthorityReportDocument">
<AppMissingInfoError
v-if="!readonly && !isHealthAuthorityReportUploaded()"
:to="{ name: APPLICATION_ROUTES.SERVICE_DELIVERY, hash: '#health-authority-report-upload', params: { applicationGuid: $route.params.applicationGuid } }">
{{ APPLICATION_ERROR_MESSAGES.DOCUMENT_HA_REPORT_UPLOAD }}
</AppMissingInfoError>
</AppDocumentUpload>
</v-card>
</v-container>
</template>

<script>
import { isEmpty } from 'lodash'
import { mapState } from 'pinia'
import { mapState, mapActions } from 'pinia'
import AppDocumentUpload from '@/components/ui/AppDocumentUpload.vue'
import AppMissingInfoError from '@/components/ui/AppMissingInfoError.vue'
import { useApplicationsStore } from '@/stores/applications'
import LicenceHeader from '@/components/licences/LicenceHeader.vue'
import LicenceDetails from '@/components/licences/LicenceDetails.vue'
import LicenceService from '@/services/licenceService'

Check failure on line 66 in frontend/src/components/applications/review/ServiceDeliverySummary.vue

View workflow job for this annotation

GitHub Actions / Run ESLint

'LicenceService' is defined but never used
import { APPLICATION_ERROR_MESSAGES, APPLICATION_ROUTES } from '@/utils/constants'
import { APPLICATION_ERROR_MESSAGES, APPLICATION_ROUTES, DOCUMENT_LABELS, DOCUMENT_TYPES } from '@/utils/constants'
export default {
components: { AppMissingInfoError, LicenceHeader, LicenceDetails },
components: { AppDocumentUpload, AppMissingInfoError, LicenceHeader, LicenceDetails },
props: {
readonly: {
type: Boolean,
Expand All @@ -58,34 +85,29 @@ export default {
},
computed: {
...mapState(useApplicationsStore, ['currentApplication']),
...mapState(useApplicationsStore, ['isServiceDeliveryComplete']),
allLicenceIDs() {
return this.licences?.map((licence) => licence.licenceId)
},
allCCOFMissingDetailComplete() {
return this.currentApplication?.licences.every((licence) => {
return licence.licenceDetails?.every(
(detail) =>
LicenceService.isOperationalSpacesValid(detail.operationalSpaces) &&
LicenceService.isEnrolledSpacesValid(detail.enrolledSpaces) &&
LicenceService.isWeeksInOperationValid(detail.weeksInOperation) &&
!isEmpty(detail.operationFromTime) &&
!isEmpty(detail.operationToTime) &&
!isEmpty(detail.weekDays),
)
})
},
showSummary() {
return !isEmpty(this.currentApplication?.licences) && (this.readonly || this.isServiceDeliveryComplete)
return !isEmpty(this.currentApplication?.licences) && (this.readonly || this.isLicenceDetailComplete())
},
healthAuthorityReportDocument() {
return this.currentApplication?.uploadedDocuments?.filter((document) => document.documentType?.includes(DOCUMENT_TYPES.HEALTH_AUTHORITY_REPORT))
},
},
async created() {
this.panel = this.allLicenceIDs
this.APPLICATION_ERROR_MESSAGES = APPLICATION_ERROR_MESSAGES
this.APPLICATION_ROUTES = APPLICATION_ROUTES
this.DOCUMENT_LABELS = DOCUMENT_LABELS
this.DOCUMENT_TYPES = DOCUMENT_TYPES
},
methods: {
...mapActions(useApplicationsStore, ['isCCOFMissingDetailComplete', 'isSplitClassroomComplete', 'isLicenceDetailComplete', 'isHealthAuthorityReportUploaded']),
isEmpty,
getLicenceDocument(licence) {
return this.currentApplication?.uploadedDocuments?.filter((document) => document.documentType?.includes(licence?.licence))
},
},
}
</script>
26 changes: 21 additions & 5 deletions frontend/src/components/ui/AppDocumentUpload.vue
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,12 @@
<v-container fluid class="pa-0">
<v-form ref="form" v-model="isValidForm">
<v-row>
<v-col v-if="documentType" cols="12" sm="5" class="pb-0">
<AppLabel>{{ documentType }}</AppLabel>
<v-col v-if="documentLabel || documentType" cols="12" sm="7" class="pb-0">
<AppLabel>{{ documentLabel ?? documentType }}</AppLabel>
</v-col>
<v-col cols="12" :sm="documentType ? '7' : '12'" :class="documentType ? 'd-flex flex-column align-end pr-4' : ''">
<v-col cols="12" :sm="documentType ? '5' : '12'" :class="documentType ? 'd-flex flex-column align-end pr-4' : ''">
<div v-if="!documentType">{{ SUPPORTED_DOCUMENTS_MESSAGE }}</div>
<AppButton v-if="!loading && !readonly" :disabled="disabled" id="add-new-file" :primary="false" size="large" class="addFileButton" @click="addFile">Add File</AppButton>
<AppButton v-if="showAddFileButton" id="add-new-file" :disabled="disabled" :primary="false" size="large" class="add-file-button" @click="addFile">Add File</AppButton>
</v-col>
</v-row>
<div v-if="documents.length > 0" class="mt-6">
Expand Down Expand Up @@ -64,6 +64,10 @@ export default {
required: false,
default: undefined,
},
documentLabel: {
type: String,
default: undefined,
},
documentType: {
type: String,
required: false,
Expand All @@ -85,6 +89,10 @@ export default {
type: Array,
default: () => [],
},
uploadLimit: {
type: String,
default: undefined,
},
},
emits: ['update:modelValue', 'deleteUploadedDocument', 'validateDocumentsToUpload'],
data() {
Expand All @@ -93,6 +101,14 @@ export default {
isValidForm: false,
}
},
computed: {
reachUploadLimit() {
return this.uploadLimit && this.documents?.length + this.uploadedDocuments?.length >= Number(this.uploadLimit)
},
showAddFileButton() {
return !this.loading && !this.readonly && !this.reachUploadLimit
},
},
watch: {
documents: {
handler() {
Expand Down Expand Up @@ -167,7 +183,7 @@ export default {
}
</script>
<style scoped>
.addFileButton {
.add-file-button {
font-size: 16px;
}
Expand Down
155 changes: 94 additions & 61 deletions frontend/src/stores/applications.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,58 +7,6 @@ import LicenceService from '@/services/licenceService'
import { useAppStore } from '@/stores/app'
import { APPLICATION_STATUS_CODES, DOCUMENT_TYPES, FACILITY_TYPES, YES_NO_CHOICE_CRM_MAPPING } from '@/utils/constants'

/*
Facility Details page - Helper functions
*/
function checkFacilityDetailsComplete(application) {
return application?.primaryContactId && application?.expenseAuthorityId && application?.fiscalYearEndDate
}

/*
Operating Cost page - Helper functions
*/
function checkRequiredDocsExist(application, requiredDocumentTypes) {
return requiredDocumentTypes.every((type) => application.uploadedDocuments.some((doc) => doc.documentType === type))
}

function isRentLease(application) {
return application?.facilityType === FACILITY_TYPES.RENT_LEASE
}

function checkOperatingCostsComplete(application) {
const isRequiredFinancialDocsUploaded = checkRequiredDocsExist(application, [DOCUMENT_TYPES.INCOME_STATEMENT, DOCUMENT_TYPES.BALANCE_SHEET])
const isFacilityTypeRequiredDocsUploaded = !isRentLease(application) || checkRequiredDocsExist(application, [DOCUMENT_TYPES.SUPPORTING_DOCS])
const areCostsPositive = application?.totalYearlyOperatingCosts + application?.totalYearlyFacilityCosts > 0
const isArmsLengthConfirmed = !isRentLease(application) || application?.armsLength === YES_NO_CHOICE_CRM_MAPPING.YES
return application?.facilityType && isArmsLengthConfirmed && isRequiredFinancialDocsUploaded && isFacilityTypeRequiredDocsUploaded && areCostsPositive
}

/*
Service Delivery page - Helper functions
*/
function checkServiceDeliveryComplete(application) {
const allDetailsComplete = application.licences.every((licence) => {
return licence.licenceDetails?.every(
(detail) =>
LicenceService.isOperationalSpacesValid(detail.operationalSpaces) &&
LicenceService.isEnrolledSpacesValid(detail.enrolledSpaces) &&
LicenceService.isWeeksInOperationValid(detail.weeksInOperation) &&
!isEmpty(detail.weekDays) &&
!isEmpty(detail.operationFromTime) &&
!isEmpty(detail.operationToTime) &&
LicenceService.isSplitClassRoomInfoValid(detail),
)
})
return application?.licenceDeclaration && !isEmpty(application?.licences) && allDetailsComplete
}

/*
Declare Submit page - Helper functions
*/
function checkDeclareSubmitComplete(application) {
return application?.applicationDeclaration
}

export const useApplicationsStore = defineStore('applications', {
namespaced: true,
state: () => ({
Expand All @@ -77,15 +25,11 @@ export const useApplicationsStore = defineStore('applications', {
},
actions: {
checkApplicationComplete() {
this.isFacilityDetailsComplete = checkFacilityDetailsComplete(this.currentApplication)
this.isServiceDeliveryComplete = checkServiceDeliveryComplete(this.currentApplication)
this.isOperatingCostsComplete = checkOperatingCostsComplete(this.currentApplication)
this.isStaffingComplete =
this.isThereAtLeastOneEmployee(this.currentApplication) &&
this.areEmployeeCertificatesComplete(this.currentApplication?.providerEmployees, this.currentApplication) &&
this.isUnionSectionComplete(this.currentApplication) &&
this.isCSSEASectionComplete(this.currentApplication)
this.isDeclareSubmitComplete = checkDeclareSubmitComplete(this.currentApplication)
this.isFacilityDetailsComplete = this.checkFacilityDetailsComplete()
this.isServiceDeliveryComplete = this.checkServiceDeliveryComplete()
this.isOperatingCostsComplete = this.checkOperatingCostsComplete()
this.isStaffingComplete = this.checkStaffingComplete()
this.isDeclareSubmitComplete = this.checkDeclareSubmitComplete()
},

async getApplication(applicationId) {
Expand All @@ -101,9 +45,91 @@ export const useApplicationsStore = defineStore('applications', {
}
},

/*
Facility Details page
*/
checkFacilityDetailsComplete() {
return this.currentApplication?.primaryContactId && this.currentApplication?.expenseAuthorityId && this.currentApplication?.fiscalYearEndDate
},

/*
Service Delivery page
*/
checkServiceDeliveryComplete() {
return (
this.currentApplication?.licenceDeclaration &&
!isEmpty(this.currentApplication?.licences) &&
this.isLicenceDetailComplete() &&
this.isLicenceDocumentUploaded() &&
this.isHealthAuthorityReportUploaded()
)
},

isLicenceDetailComplete() {
return this.isCCOFMissingDetailComplete() && this.isSplitClassroomComplete()
},

isCCOFMissingDetailComplete() {
return this.currentApplication?.licences.every((licence) => {
return licence.licenceDetails?.every(
(detail) =>
LicenceService.isOperationalSpacesValid(detail.operationalSpaces) &&
LicenceService.isEnrolledSpacesValid(detail.enrolledSpaces) &&
LicenceService.isWeeksInOperationValid(detail.weeksInOperation) &&
!isEmpty(detail.operationFromTime) &&
!isEmpty(detail.operationToTime) &&
!isEmpty(detail.weekDays),
)
})
},

isSplitClassroomComplete() {
return this.currentApplication?.licences.every((licence) => licence.licenceDetails?.every((detail) => LicenceService.isSplitClassRoomInfoValid(detail)))
},

isLicenceDocumentUploaded() {
return this.currentApplication.licences.every((licence) => {
const uploadedDocument = this.currentApplication?.uploadedDocuments?.filter((document) => document.documentType?.includes(licence?.licence))
return uploadedDocument?.length > 0
})
},

isHealthAuthorityReportUploaded() {
const healthAuthorityReports = this.currentApplication?.uploadedDocuments?.filter((document) => document.documentType?.includes(DOCUMENT_TYPES.HEALTH_AUTHORITY_REPORT))
return healthAuthorityReports?.length > 0
},

/*
Operating Cost page
*/
checkOperatingCostsComplete() {
const isRequiredFinancialDocsUploaded = this.checkRequiredDocsExist(this.currentApplication, [DOCUMENT_TYPES.INCOME_STATEMENT, DOCUMENT_TYPES.BALANCE_SHEET])
const isFacilityTypeRequiredDocsUploaded = !this.isRentLease(this.currentApplication) || this.checkRequiredDocsExist(this.currentApplication, [DOCUMENT_TYPES.SUPPORTING_DOCS])
const areCostsPositive = this.currentApplication?.totalYearlyOperatingCosts + this.currentApplication?.totalYearlyFacilityCosts > 0
const isArmsLengthConfirmed = !this.isRentLease(this.currentApplication) || this.currentApplication?.armsLength === YES_NO_CHOICE_CRM_MAPPING.YES
return this.currentApplication?.facilityType && isArmsLengthConfirmed && isRequiredFinancialDocsUploaded && isFacilityTypeRequiredDocsUploaded && areCostsPositive
},

checkRequiredDocsExist(application, requiredDocumentTypes) {
return requiredDocumentTypes.every((type) => application.uploadedDocuments.some((doc) => doc.documentType === type))
},

isRentLease(application) {
return application?.facilityType === FACILITY_TYPES.RENT_LEASE
},

/*
Staffing page
*/
checkStaffingComplete() {
return (
this.isThereAtLeastOneEmployee(this.currentApplication) &&
this.areEmployeeCertificatesComplete(this.currentApplication?.providerEmployees, this.currentApplication) &&
this.isUnionSectionComplete(this.currentApplication) &&
this.isCSSEASectionComplete(this.currentApplication)
)
},

isThereAtLeastOneEmployee(application) {
const totalStaff =
application?.staffingInfantECEducatorFullTime +
Expand Down Expand Up @@ -153,5 +179,12 @@ export const useApplicationsStore = defineStore('applications', {
isCSSEASectionComplete(application) {
return application?.cssea != null
},

/*
Declare Submit page
*/
checkDeclareSubmitComplete() {
return this.currentApplication?.applicationDeclaration
},
},
})
Loading

0 comments on commit 7b4b2ac

Please sign in to comment.