Repository: bcgov/EDUC-CCOF
Pull request #700: Ccfri 4764 request to remove a closure
Author: login: JonahCurlCGI
Created: 2025-04-25T19:06:46Z | Merged: 2025-04-30T21:45:40Z

<!-- Provide a general summary of your changes in the Title above -->
# Description

Created an abstraction of the original NewClosureRequestDialog.vue to allow reuse in update and remove closure. Provides user with functionality to request a closure removal.

Added end point to get ChangeActionClosure details and documents.

<!-- Describe your changes in detail -->
<!-- Why is this change required? What problem does it solve? -->
<!-- If it fixes an open issue, please link to the issue here. -->

## Types of changes

<!-- What types of changes does your code introduce? Uncomment all that apply: -->

<!-- Bug fix (non-breaking change which fixes an issue) -->
New feature (non-breaking change which adds functionality)
<!-- Documentation (non-breaking change with enhancements to documentation) -->
<!-- Breaking change (fix or feature that would cause existing functionality to change) -->

## Checklist

<!-- Go over all the following points, and put an `x` in all the boxes that apply. -->
<!-- If you're unsure about any of these, don't hesitate to ask. We're here to help! -->

- [ ] I have read the [CONTRIBUTING](CONTRIBUTING.md) doc
- [ ] I have checked that unit tests pass locally with my changes
- [ ] I have added tests that prove my fix is effective or that my feature works
- [ ] I have added necessary documentation (if appropriate)

## Further comments

<!-- If this is a relatively large or complex change, kick off the discussion by explaining why you chose the solution you did and what alternatives you considered, etc... -->


Diff:
diff --git a/backend/src/components/changeRequest.js b/backend/src/components/changeRequest.js
index c9d6f67cc..560117a9a 100644
--- a/backend/src/components/changeRequest.js
+++ b/backend/src/components/changeRequest.js
@@ -2,7 +2,7 @@
 
 const log = require('./logger');
 const { MappableObjectForFront, MappableObjectForBack, getMappingString } = require('../util/mapping/MappableObject');
-const { ChangeRequestMappings, ChangeActionRequestMappings, ChangeActionClosureMappings, MtfiMappings, NewFacilityMappings } = require('../util/mapping/ChangeRequestMappings');
+const { ChangeActionClosureMappings, ChangeActionRequestMappings, ChangeRequestMappings, MtfiMappings, NewFacilityMappings } = require('../util/mapping/ChangeRequestMappings');
 const { DocumentsMappings, UserProfileBaseCCFRIMappings, UserProfileBaseFundingMappings, UserProfileECEWEMappings } = require('../util/mapping/Mappings');
 const { ChangeRequestUnlockMapping } = require('../util/mapping/ChangeRequestMappings');
 
@@ -258,9 +258,12 @@ async function createClosureChangeRequest(req, res) {
     const createChangeRequestReponse = await createRawChangeRequest(req);
     const changeActionClosure = mapChangeActionClosureObjectForBack(req.body);
     changeActionClosure['ccof_change_action@odata.bind'] = `/ccof_change_actions(${createChangeRequestReponse.changeActionId})`;
+    if (req.body.changeType === CHANGE_REQUEST_TYPES.REMOVE_A_CLOSURE) {
+      changeActionClosure['ccof_closure@odata.bind'] = `/ccof_application_ccfri_closures(${req.body.closureId})`;
+    }
     const asyncOperations = [postOperation('ccof_change_action_closures', changeActionClosure), getOperation(`ccof_change_requests(${createChangeRequestReponse.changeRequestId})?$select=ccof_name`)];
-    if (!isEmpty(req.body.documents)) {
-      req.body.documents.forEach((document) => {
+    if (req.body.changeType === CHANGE_REQUEST_TYPES.NEW_CLOSURE) {
+      req.body.documents?.forEach((document) => {
         const mappedDocument = new MappableObjectForBack(document, DocumentsMappings).toJSON();
         mappedDocument.ccof_change_action_id = createChangeRequestReponse.changeActionId;
         asyncOperations.push(postChangeActionDocument(mappedDocument));
@@ -336,6 +339,25 @@ async function getChangeRequestDocs(req, res) {
   }
 }
 
+async function getChangeActionClosure(req, res) {
+  const { changeActionClosureId } = req.params;
+  try {
+    const changeActionClosure = await getOperation(`ccof_change_action_closures(${changeActionClosureId})?$select=_ccof_change_action_value,ccof_any_details_added_on_request`);
+    const changeActionClosureForFront = new MappableObjectForFront(changeActionClosure, ChangeActionClosureMappings).toJSON();
+    changeActionClosureForFront.documents = [];
+    if (changeActionClosure?._ccof_change_action_value) {
+      const getDocumentsResponse = await getChangeActionDocument(changeActionClosure._ccof_change_action_value);
+      getDocumentsResponse?.value?.forEach((document) => {
+        changeActionClosureForFront.documents.push(new MappableObjectForFront(document, DocumentsMappings));
+      });
+    }
+    return res.status(HttpStatus.OK).json(changeActionClosureForFront);
+  } catch (e) {
+    log.error(e);
+    return res.status(HttpStatus.INTERNAL_SERVER_ERROR).json(e.data ? e.data : e?.status);
+  }
+}
+
 async function saveChangeRequestDocs(req, res) {
   try {
     const documents = req.body;
@@ -399,6 +421,7 @@ module.exports = {
   createClosureChangeRequest,
   deleteChangeRequest,
   getChangeRequestDocs,
+  getChangeActionClosure,
   saveChangeRequestDocs,
   updateChangeRequest,
   createChangeAction,
diff --git a/backend/src/routes/changeRequest.js b/backend/src/routes/changeRequest.js
index 30d6d81e3..93588c4ff 100644
--- a/backend/src/routes/changeRequest.js
+++ b/backend/src/routes/changeRequest.js
@@ -12,6 +12,7 @@ const {
   createClosureChangeRequest,
   deleteChangeRequest,
   getChangeRequestDocs,
+  getChangeActionClosure,
   saveChangeRequestDocs,
   createChangeAction,
   deleteChangeAction,
@@ -44,9 +45,9 @@ const closureChangeRequestSchema = {
   },
   changeType: {
     in: ['body'],
-    equals: {
-      options: `${CHANGE_REQUEST_TYPES.NEW_CLOSURE}`,
-      errorMessage: '[changeType] must be NEW_CLOSURE',
+    isIn: {
+      options: [[`${CHANGE_REQUEST_TYPES.NEW_CLOSURE}`, CHANGE_REQUEST_TYPES.REMOVE_A_CLOSURE]],
+      errorMessage: '[changeType] must be NEW_CLOSURE or REMOVE_A_CLOSURE',
     },
   },
   facilityId: {
@@ -57,6 +58,17 @@ const closureChangeRequestSchema = {
     in: ['body'],
     exists: { errorMessage: '[organizationId] is required' },
   },
+  closureId: {
+    in: ['body'],
+    custom: {
+      options: (value, { req }) => {
+        if (req.body.changeType === CHANGE_REQUEST_TYPES.REMOVE_A_CLOSURE && !value) {
+          throw new Error('[closureId] is required for closure removal requests');
+        }
+        return true;
+      },
+    },
+  },
 };
 
 const documentChangeRequestSchema = {
@@ -119,7 +131,7 @@ router.post(
 );
 
 /**
- * Create the change Request
+ * Create the closure change request
  */
 router.post('/closure', passport.authenticate('jwt', { session: false }), isValidBackendToken, [checkSchema(closureChangeRequestSchema)], (req, res) => {
   validationResult(req).throw();
@@ -140,6 +152,20 @@ router.get(
   },
 );
 
+/**
+ * Get Change Action Closure
+ */
+router.get(
+  '/changeActionClosure/:changeActionClosureId',
+  passport.authenticate('jwt', { session: false }),
+  isValidBackendToken,
+  [param('changeActionClosureId', 'URL param: [changeActionClosureId] is required').not().isEmpty()],
+  (req, res) => {
+    validationResult(req).throw();
+    return getChangeActionClosure(req, res);
+  },
+);
+
 /**
  * Create the change request TODO: Rename this to something better
  */
diff --git a/backend/src/util/mapping/Mappings.js b/backend/src/util/mapping/Mappings.js
index b059a88c6..8c8926d22 100644
--- a/backend/src/util/mapping/Mappings.js
+++ b/backend/src/util/mapping/Mappings.js
@@ -327,6 +327,7 @@ const OrganizationFacilityMappings = [
 ];
 
 const ClosureMappings = [
+  { back: 'ccof_application_ccfri_closureid', front: 'closureId' },
   { back: '_ccof_organizationfacility_value', front: 'organizationId' },
   { back: '_ccof_program_year_value', front: 'programYearId' },
   { back: '_ccof_facilityinfo_value', front: 'facilityId' },
@@ -338,6 +339,8 @@ const ClosureMappings = [
   { back: 'ccof_age_affected_groups', front: 'ageGroups' },
   { back: 'ccof_closure_reason', front: 'closureReason' },
   { back: 'ccof_is_full_closure', front: 'fullClosure' },
+  { back: 'ccof_paidclosure', front: 'paidClosure' },
+  { back: '_ccof_change_action_closure_value', front: 'changeActionClosureId' },
 ];
 
 const UserProfileBaseCCFRIMappings = [
diff --git a/frontend/src/components/LandingPage.vue b/frontend/src/components/LandingPage.vue
index 3eed79053..e6aed0646 100644
--- a/frontend/src/components/LandingPage.vue
+++ b/frontend/src/components/LandingPage.vue
@@ -12,8 +12,8 @@
 
     <AppAlertBanner v-if="showNotGoodStandingWarning" type="warning" class="mb-4 w-100">
       Your organization is not in good standing with BC Registries and Online Services. Being in good standing is a
-      requirement to receive CCOF Funding. Contact BC Registries and Online Services immediately to resolve.
-      Please disregard this message if you have already resolved your status.
+      requirement to receive CCOF Funding. Contact BC Registries and Online Services immediately to resolve. Please
+      disregard this message if you have already resolved your status.
     </AppAlertBanner>
 
     <v-row>
@@ -347,7 +347,6 @@ import {
   PATHS,
   pcfUrl,
   pcfUrlGuid,
-  closureUrl,
   CHANGE_REQUEST_EXTERNAL_STATUS,
   ORGANIZATION_GOOD_STANDING_STATUSES,
   ORGANIZATION_PROVIDER_TYPES,
@@ -714,9 +713,10 @@ export default {
       }
     },
     goToOrganizationClosures() {
-      this.$router.push(
-        closureUrl(this.selectedProgramYear ? this.selectedProgramYear.programYearId : this.latestProgramYearId),
-      );
+      const programYearId = this.selectedProgramYear
+        ? this.selectedProgramYear.programYearId
+        : this.latestProgramYearId;
+      this.$router.push(`${PATHS.CLOSURES}/${programYearId}`);
     },
     async getAllMessagesVuex() {
       try {
diff --git a/frontend/src/components/NewClosureRequestDialog.vue b/frontend/src/components/closure/ClosureChangeRequestDialog.vue
similarity index 69%
rename from frontend/src/components/NewClosureRequestDialog.vue
rename to frontend/src/components/closure/ClosureChangeRequestDialog.vue
index 4211697d8..3f52de5b6 100644
--- a/frontend/src/components/NewClosureRequestDialog.vue
+++ b/frontend/src/components/closure/ClosureChangeRequestDialog.vue
@@ -1,11 +1,5 @@
 <template>
-  <AppDialog
-    v-model="isDisplayed"
-    title="New Closure Request"
-    :loading="isLoading"
-    text-alignment="left"
-    @close="closeDialog"
-  >
+  <AppDialog v-model="isDisplayed" :title="title" :loading="isLoading" text-alignment="left" @close="closeDialog">
     <template #content>
       <v-form v-model="isValidForm">
         <v-container width="80%" class="text-primary pa-0">
@@ -22,13 +16,14 @@
           <v-select
             v-model="input.facilityId"
             :rules="rules.required"
+            :disabled="isDisabled"
             :items="facilityList"
             :loading="isLoading"
             item-value="facilityId"
             item-title="facilityName"
             placeholder="Select a facility"
             variant="outlined"
-            class="mt-2"
+            class="mt-2 max-width"
             @update:model-value="handleFacilityChange"
           >
             <template #item="{ props, item, index }">
@@ -70,7 +65,7 @@
               <h3 class="mt-2">Will parents pay for this closure?</h3>
             </v-col>
             <v-col cols="12" lg="3" class="pl-0">
-              <v-radio-group v-model="input.paidClosure" :rules="rules.required">
+              <v-radio-group v-model="input.paidClosure" :rules="rules.required" :disabled="isDisabled">
                 <v-row justify="start">
                   <v-col cols="6" class="pl-3 pr-0 pt-0">
                     <v-radio label="Yes" :value="1" />
@@ -96,6 +91,7 @@
                 <v-radio-group
                   v-model="input.fullClosure"
                   :rules="rules.required"
+                  :disabled="isDisabled"
                   @update:model-value="handleFullFacilityClosureChange"
                 >
                   <v-row justify="start">
@@ -110,22 +106,23 @@
               </v-col>
             </v-row>
             <div v-if="input.fullClosure === false">
-              <v-row class="ml-0">
+              <v-row class="ml-0" align="center">
                 <h3 class="pr-2">Affected Care Categorie(s)</h3>
                 <p>(select all that apply):</p>
               </v-row>
               <v-select
                 v-model.lazy="input.ageGroups"
                 :loading="isLoading"
+                :disabled="isDisabled"
                 :items="ageGroups"
                 item-title="label"
                 item-value="value"
                 label="Select affected care categories"
                 variant="outlined"
-                class="mt-2 pl-0 text-left"
+                class="mt-2"
                 multiple
                 chips
-                :rules="rulesAgeGroups"
+                :rules="rules.required"
                 clearable
               >
                 <template #prepend-item>
@@ -152,6 +149,7 @@
               <v-col cols="12" lg="4">
                 <AppDateInput
                   v-model="input.startDate"
+                  :disabled="isDisabled"
                   :min="fiscalStartAndEndDates.startDate"
                   :max="input.endDate ? input.endDate : fiscalStartAndEndDates.endDate"
                   :rules="[
@@ -168,6 +166,7 @@
               <v-col cols="12" lg="4">
                 <AppDateInput
                   v-model="input.endDate"
+                  :disabled="isDisabled"
                   :min="input.startDate ? input.startDate : fiscalStartAndEndDates.startDate"
                   :max="fiscalStartAndEndDates.endDate"
                   :rules="[
@@ -186,35 +185,55 @@
                 <h3>Reason:</h3>
               </v-col>
               <v-col cols="12" lg="9">
-                <v-text-field v-model="input.closureReason" variant="outlined" :rules="rules.required"></v-text-field>
+                <v-text-field
+                  v-model="input.closureReason"
+                  :disabled="isDisabled"
+                  variant="outlined"
+                  :rules="rules.required"
+                ></v-text-field>
               </v-col>
             </v-row>
             <h3>Request Description:</h3>
             <v-textarea
               v-model="input.description"
+              :disabled="isDisabled"
               variant="outlined"
               label="Detailed description of request"
               class="text-left mt-3"
             />
             <AppDocumentUpload
               :loading="isLoading"
+              :readonly="isDisabled"
               :document-type="DOCUMENT_TYPES.CLOSURE_REQUEST"
               title="Supporting Documents"
               :required="false"
+              :uploaded-documents="uploadedDocuments"
               @update-documents-to-upload="updateDocuments"
             />
           </v-container>
+          <v-container v-if="requestType === CHANGE_REQUEST_TYPES.REMOVE_A_CLOSURE" class="pa-0 pt-4">
+            <h3>Reason for closure removal:</h3>
+            <v-textarea
+              v-model="input.reasonForClosureRemoval"
+              variant="outlined"
+              :rules="rules.required"
+              label="Please describe the reason for removal."
+              class="text-left mt-3"
+            />
+          </v-container>
         </v-container>
       </v-form>
     </template>
     <template #button>
       <v-container width="80%">
         <v-row>
-          <v-col cols="6" md="6">
-            <AppButton :primary="false" :disabled="isLoading" @click="closeDialog">Cancel</AppButton>
+          <v-col md="6">
+            <AppButton :primary="false" @click="closeDialog">Cancel</AppButton>
           </v-col>
-          <v-col cols="6" md="6" align="right">
-            <AppButton :disabled="!isValidForm" @click="submit">Submit</AppButton>
+          <v-col md="6" align="right">
+            <AppButton :disabled="!isValidForm || isLoading" @click="submit">{{
+              requestType === CHANGE_REQUEST_TYPES.REMOVE_A_CLOSURE ? 'Remove Closure' : 'Submit'
+            }}</AppButton>
           </v-col>
         </v-row>
       </v-container>
@@ -224,6 +243,7 @@
 
 <script>
 import { mapState } from 'pinia';
+import { cloneDeep, isEmpty } from 'lodash';
 
 import AppButton from '@/components/guiComponents/AppButton.vue';
 import AppDialog from '@/components/guiComponents/AppDialog.vue';
@@ -238,23 +258,32 @@ import FacilityService from '@/services/facilityService';
 import alertMixin from '@/mixins/alertMixin';
 import { useAppStore } from '@/store/app.js';
 import { useApplicationStore } from '@/store/application.js';
-import { useAuthStore } from '@/store/auth.js';
+import { useAuthStore } from '@/store/Auth.js';
 
 export default {
-  name: 'NewClosureRequestDialog',
+  name: 'ClosureChangeRequestDialog',
   components: { AppButton, AppDateInput, AppDocumentUpload, AppDialog, AppTooltip },
   mixins: [alertMixin],
   props: {
-    show: {
-      type: Boolean,
-      default: false,
-      required: true,
+    closure: {
+      type: Object,
+      default: undefined,
     },
     programYearId: {
       type: String,
       default: '',
       required: true,
     },
+    requestType: {
+      type: [Number, null],
+      default: null,
+      required: true,
+    },
+    show: {
+      type: Boolean,
+      default: false,
+      required: true,
+    },
   },
   emits: ['close', 'submitted'],
   data() {
@@ -263,23 +292,36 @@ export default {
       isDisplayed: false,
       isLoading: false,
       input: {
-        ageGroups: [],
         documents: [],
       },
       selectedFacilityWasChanged: true,
       ageGroups: [],
+      uploadedDocuments: [],
     };
   },
   computed: {
     ...mapState(useAppStore, ['getProgramYearNameById', 'getFundingUrl']),
     ...mapState(useApplicationStore, ['fiscalStartAndEndDates', 'getFacilityListForPCFByProgramYearId']),
     ...mapState(useAuthStore, ['userInfo']),
+    title() {
+      switch (this.requestType) {
+        case CHANGE_REQUEST_TYPES.NEW_CLOSURE:
+          return 'New Closure Request';
+        case CHANGE_REQUEST_TYPES.REMOVE_A_CLOSURE:
+          return 'Remove Closure Request';
+        default:
+          return '';
+      }
+    },
+    isDisabled() {
+      return this.requestType === CHANGE_REQUEST_TYPES.REMOVE_A_CLOSURE;
+    },
+    showDocumentUpload() {
+      return this.requestType === CHANGE_REQUEST_TYPES.NEW_CLOSURE || !isEmpty(this.uploadedDocuments);
+    },
     facilityList() {
       return this.getFacilityListForPCFByProgramYearId(this.programYearId);
     },
-    rulesAgeGroups() {
-      return this.input.fullClosure === false ? rules.required : [];
-    },
     allAgeGroupsSelected() {
       return this.input.ageGroups?.length === this.ageGroups?.length;
     },
@@ -295,33 +337,48 @@ export default {
   },
   watch: {
     show: {
-      handler(value) {
+      async handler(value) {
         this.isDisplayed = value;
+        if (!value) return;
+        this.clearData();
+        this.isLoading = true;
+        if (this.requestType === CHANGE_REQUEST_TYPES.REMOVE_A_CLOSURE) {
+          this.input = await this.initInput();
+        }
+        this.isLoading = false;
       },
     },
   },
   created() {
     this.rules = rules;
     this.DOCUMENT_TYPES = DOCUMENT_TYPES;
+    this.CHANGE_REQUEST_TYPES = CHANGE_REQUEST_TYPES;
   },
   methods: {
+    async initInput() {
+      const input = cloneDeep(this.closure);
+      try {
+        this.ageGroups = await this.getLicenseCategories(this.closure.facilityId);
+        input.ageGroups = this.closure?.ageGroups?.split(',').map((value) => Number(value));
+        const changeActionClosure = await ClosureService.getChangeActionClosure(this.closure.changeActionClosureId);
+        this.uploadedDocuments = changeActionClosure?.documents ? changeActionClosure.documents : [];
+        input.description = changeActionClosure?.closureDescription ? changeActionClosure.closureDescription : '';
+        return input;
+      } catch (e) {
+        console.log(e);
+        this.setFailureAlert('An error occurred while loading. Please try again later.');
+      }
+    },
     async handleFacilityChange(facilityId) {
       this.isLoading = true;
       this.selectedFacilityWasChanged = true;
       this.facilityId = facilityId;
       this.input.ageGroups = [];
-      const ageGroups = [];
+      let ageGroups = [];
       if (facilityId && this.input.fullClosure === false) {
         try {
-          const facilityAgeGroups = await FacilityService.getLicenseCategories(facilityId);
+          ageGroups = await this.getLicenseCategories(facilityId);
           this.selectedFacilityWasChanged = false;
-          for (const ageGroup of facilityAgeGroups) {
-            ageGroups.push({
-              label: ageGroup.childCareCategory,
-              value: CLOSURE_AFFECTED_AGE_GROUPS[ageGroup.childCareCategory],
-            });
-          }
-          ageGroups.sort((a, b) => a.value - b.value);
         } catch (e) {
           console.log(e);
           this.setFailureAlert('Failed to load age categories for facility');
@@ -330,6 +387,18 @@ export default {
       this.ageGroups = ageGroups;
       this.isLoading = false;
     },
+    async getLicenseCategories(facilityId) {
+      const ageGroups = [];
+      const facilityAgeGroups = await FacilityService.getLicenseCategories(facilityId);
+      facilityAgeGroups.forEach((ageGroup) => {
+        ageGroups.push({
+          label: ageGroup.childCareCategory,
+          value: CLOSURE_AFFECTED_AGE_GROUPS[ageGroup.childCareCategory],
+        });
+      });
+      ageGroups.sort((a, b) => a.value - b.value);
+      return ageGroups;
+    },
     async handleFullFacilityClosureChange(fullClosure) {
       this.input.fullClosure = fullClosure;
       if (this.selectedFacilityWasChanged && fullClosure === false) {
@@ -365,26 +434,42 @@ export default {
       }
       return processedDocuments;
     },
+    getPayload() {
+      switch (this.requestType) {
+        case CHANGE_REQUEST_TYPES.NEW_CLOSURE:
+          return {
+            applicationId: this.applicationId,
+            programYearId: this.programYearId,
+            organizationId: this.userInfo?.organizationId,
+            facilityId: this.input.facilityId,
+            paidClosure: this.input.paidClosure,
+            fullClosure: this.input.fullClosure,
+            ageGroups: this.input.fullClosure ? null : this.input.ageGroups.join(','),
+            startDate: this.input.startDate,
+            endDate: this.input.endDate,
+            closureReason: this.input.closureReason,
+            closureDescription: this.input.description,
+            documents: this.processDocuments(this.input.documents),
+            changeType: this.requestType,
+          };
+        case CHANGE_REQUEST_TYPES.REMOVE_A_CLOSURE:
+          return {
+            applicationId: this.applicationId,
+            programYearId: this.programYearId,
+            facilityId: this.input.facilityId,
+            organizationId: this.userInfo?.organizationId,
+            closureId: this.closure.closureId,
+            changeType: this.requestType,
+          };
+        default:
+          return null;
+      }
+    },
     async submit() {
       this.isLoading = true;
-      const payload = {
-        applicationId: this.applicationId,
-        programYearId: this.programYearId,
-        organizationId: this.userInfo?.organizationId,
-        facilityId: this.input.facilityId,
-        paidClosure: this.input.paidClosure,
-        fullClosure: this.input.fullClosure,
-        ageGroups: this.input.fullClosure ? undefined : this.input.ageGroups.join(','),
-        startDate: this.input.startDate,
-        endDate: this.input.endDate,
-        closureReason: this.input.closureReason,
-        closureDescription: this.input.description,
-        documents: this.processDocuments(this.input.documents),
-        changeType: CHANGE_REQUEST_TYPES.NEW_CLOSURE,
-      };
+      const payload = this.getPayload();
       try {
         const response = await ClosureService.createClosureChangeRequest(payload);
-        this.clearInputs();
         payload.changeRequestReferenceId = response.changeRequestReferenceId;
         this.$emit('submitted', payload);
       } catch (e) {
@@ -394,9 +479,11 @@ export default {
         this.isLoading = false;
       }
     },
-    clearInputs() {
+    clearData() {
+      this.selectedFacilityWasChanged = true;
+      this.ageGroups = [];
+      this.uploadedDocuments = [];
       this.input = {
-        ageGroups: [],
         documents: [],
       };
     },
@@ -405,7 +492,7 @@ export default {
 </script>
 
 <style scoped>
-:deep(.v-select__selection) {
+:deep(.max-width .v-select__selection) {
   width: 100%;
 }
 </style>
diff --git a/frontend/src/components/util/ClosureConfirmationDialog.vue b/frontend/src/components/closure/ClosureConfirmationDialog.vue
similarity index 95%
rename from frontend/src/components/util/ClosureConfirmationDialog.vue
rename to frontend/src/components/closure/ClosureConfirmationDialog.vue
index 0fa4becb1..ba3ab3dd1 100644
--- a/frontend/src/components/util/ClosureConfirmationDialog.vue
+++ b/frontend/src/components/closure/ClosureConfirmationDialog.vue
@@ -2,7 +2,7 @@
   <AppDialog v-model="isDisplayed" title="Success" persistent max-width="40%" @close="closeDialog">
     <template #content>
       <div class="text-center">
-        <p class="pt-4 text-h6">Your message has been submitted.</p>
+        <p class="pt-4 text-h6">Your request has been submitted.</p>
         <p class="pt-4 text-h6">Reference: {{ changeRequestReferenceId }}</p>
       </div>
     </template>
diff --git a/frontend/src/components/ClosureDetailsDialog.vue b/frontend/src/components/closure/ClosureDetailsDialog.vue
similarity index 97%
rename from frontend/src/components/ClosureDetailsDialog.vue
rename to frontend/src/components/closure/ClosureDetailsDialog.vue
index b1adf159f..957cc405c 100644
--- a/frontend/src/components/ClosureDetailsDialog.vue
+++ b/frontend/src/components/closure/ClosureDetailsDialog.vue
@@ -44,8 +44,8 @@ export default {
       default: false,
     },
     closure: {
-      type: Object,
-      default: undefined,
+      type: [Object, null],
+      default: null,
     },
   },
   emits: ['close'],
diff --git a/frontend/src/components/OrganizationClosures.vue b/frontend/src/components/closure/OrganizationClosures.vue
similarity index 87%
rename from frontend/src/components/OrganizationClosures.vue
rename to frontend/src/components/closure/OrganizationClosures.vue
index 1175888b8..a7848c947 100644
--- a/frontend/src/components/OrganizationClosures.vue
+++ b/frontend/src/components/closure/OrganizationClosures.vue
@@ -8,7 +8,7 @@
       </v-col>
       <v-col cols="12" lg="6" align="right">
         <div>
-          <AppButton :loading="isLoading" size="large" @click="toggleNewClosureRequestDialog"
+          <AppButton :loading="isLoading" size="large" @click="closureRequestType = CHANGE_REQUEST_TYPES.NEW_CLOSURE"
             >Add New Closure</AppButton
           >
           <div class="text-h6 font-weight-bold my-4">
@@ -104,12 +104,14 @@
       </v-skeleton-loader>
     </v-card>
     <NavButton @previous="previous" />
-    <NewClosureRequestDialog
-      :show="showNewClosureRequestDialog"
+    <ClosureChangeRequestDialog
+      :closure="closureForRequest"
       :program-year-id="$route.params.programYearGuid"
+      :request-type="closureRequestType"
+      :show="showClosureChangeRequestDialog"
       max-width="60%"
-      @close="toggleNewClosureRequestDialog"
       @submitted="newClosureRequestSubmitted"
+      @close="closureRequestType = null"
     />
     <ClosureConfirmationDialog
       :show="showClosureConfirmationDialog"
@@ -121,7 +123,7 @@
       :show="showClosureDetailsDialog"
       max-width="60%"
       :closure="closureToView"
-      @close="setClosureToView(undefined)"
+      @close="setClosureToView(null)"
     />
   </v-container>
 </template>
@@ -129,10 +131,10 @@
 import { mapState } from 'pinia';
 
 import AppButton from '@/components/guiComponents/AppButton.vue';
-import ClosureConfirmationDialog from '@/components/util/ClosureConfirmationDialog.vue';
-import ClosureDetailsDialog from '@/components/ClosureDetailsDialog.vue';
+import ClosureChangeRequestDialog from '@/components/closure/ClosureChangeRequestDialog.vue';
+import ClosureConfirmationDialog from '@/components/closure/ClosureConfirmationDialog.vue';
+import ClosureDetailsDialog from '@/components/closure/ClosureDetailsDialog.vue';
 import NavButton from '@/components/util/NavButton.vue';
-import NewClosureRequestDialog from '@/components/NewClosureRequestDialog.vue';
 
 import alertMixin from '@/mixins/alertMixin.js';
 import { useAppStore } from '@/store/app.js';
@@ -142,6 +144,7 @@ import ClosureService from '@/services/closureService.js';
 import { formatUTCDateToShortDateString } from '@/utils/format';
 
 import {
+  CHANGE_REQUEST_TYPES,
   CLOSURE_PAYMENT_ELIGIBILITIES,
   CLOSURE_PAYMENT_ELIGIBILITY_TEXTS,
   CLOSURE_STATUS_TEXTS,
@@ -151,7 +154,7 @@ import {
 
 export default {
   name: 'OrganizationClosures',
-  components: { AppButton, ClosureConfirmationDialog, ClosureDetailsDialog, NavButton, NewClosureRequestDialog },
+  components: { AppButton, ClosureChangeRequestDialog, ClosureConfirmationDialog, ClosureDetailsDialog, NavButton },
   mixins: [alertMixin],
   data() {
     return {
@@ -175,6 +178,8 @@ export default {
       showClosureConfirmationDialog: false,
       changeRequestReferenceId: undefined,
       closureToView: undefined,
+      closureRequestType: null,
+      closureForRequest: undefined,
     };
   },
   computed: {
@@ -190,11 +195,15 @@ export default {
         );
       });
     },
+    showClosureChangeRequestDialog() {
+      return !!this.closureRequestType;
+    },
     showClosureDetailsDialog() {
       return this.closureToView != null;
     },
   },
   async created() {
+    this.CHANGE_REQUEST_TYPES = CHANGE_REQUEST_TYPES;
     await this.loadData();
   },
   methods: {
@@ -227,7 +236,8 @@ export default {
       // stub
     },
     removeClosure(closure) {
-      // stub
+      this.closureRequestType = CHANGE_REQUEST_TYPES.REMOVE_A_CLOSURE;
+      this.closureForRequest = closure;
     },
     hasPendingStatus(closure) {
       return closure.closureStatus === CLOSURE_STATUSES.PENDING;
@@ -295,21 +305,20 @@ export default {
     previous() {
       this.$router.push(PATHS.ROOT.HOME);
     },
-    toggleNewClosureRequestDialog() {
-      this.showNewClosureRequestDialog = !this.showNewClosureRequestDialog;
-    },
     toggleClosureConfirmationDialog() {
       this.showClosureConfirmationDialog = !this.showClosureConfirmationDialog;
     },
     // To prevent issues with CRM delays from sequential Post and Get requests, the closure is manually added
     // to allow the user to view the closure following the post request.
-    async newClosureRequestSubmitted(closureChangeRequest) {
-      const facility = this.getNavByFacilityId(closureChangeRequest.facilityId);
-      closureChangeRequest.facilityName = facility?.facilityName;
-      closureChangeRequest.closureStatus = CLOSURE_STATUSES.PENDING;
-      this.closures.push(closureChangeRequest);
+    newClosureRequestSubmitted(closureChangeRequest) {
+      if (this.closureRequestType === CHANGE_REQUEST_TYPES.NEW_CLOSURE) {
+        const facility = this.getNavByFacilityId(closureChangeRequest.facilityId);
+        closureChangeRequest.facilityName = facility?.facilityName;
+        closureChangeRequest.closureStatus = CLOSURE_STATUSES.PENDING;
+        this.closures.push(closureChangeRequest);
+      }
       this.changeRequestReferenceId = closureChangeRequest.changeRequestReferenceId;
-      this.toggleNewClosureRequestDialog();
+      this.closureRequestType = null;
       this.toggleClosureConfirmationDialog();
     },
   },
diff --git a/frontend/src/router.js b/frontend/src/router.js
index d44caacef..3dc0f5967 100644
--- a/frontend/src/router.js
+++ b/frontend/src/router.js
@@ -12,7 +12,6 @@ import Logout from '@/components/Logout.vue';
 import MessagesPage from '@/components/MessagesPage.vue';
 import MinistryLogin from '@/components/MinistryLogin.vue';
 import CcofNewApplicationIntermediate from '@/components/NewAppIntermediatePage.vue';
-import OrganizationClosures from '@/components/OrganizationClosures.vue';
 import NMF from '@/components/RFI/NMF.vue';
 import CCFRIRequestMoreInfo from '@/components/RFI/RFILanding.vue';
 import SessionExpired from '@/components/SessionExpired.vue';
@@ -33,6 +32,7 @@ import FacilityInformation from '@/components/ccofApplication/group/FacilityInfo
 import GroupFundAmount from '@/components/ccofApplication/group/FundAmount.vue';
 import LicenseUpload from '@/components/ccofApplication/group/LicenseUpload.vue';
 import GroupOrganizationInformation from '@/components/ccofApplication/group/OrganizationInformation.vue';
+import OrganizationClosures from '@/components/closure/OrganizationClosures.vue';
 import Unauthorized from '@/components/common/Unauthorized.vue';
 import EceweEligibility from '@/components/eceweApplication/EceweEligibility.vue';
 import EceweFacilities from '@/components/eceweApplication/EceweFacilities.vue';
@@ -54,7 +54,6 @@ import {
   PATHS,
   changeUrl,
   changeUrlGuid,
-  closureUrl,
   pcfUrl,
   pcfUrlGuid,
 } from '@/utils/constants.js';
@@ -796,20 +795,20 @@ const router = createRouter({
       },
     },
     {
-      path: '/:catchAll(.*)',
-      name: 'notfound',
-      redirect: '/',
+      path: `${PATHS.CLOSURES}/:programYearGuid`,
+      name: 'organization-closures',
+      component: OrganizationClosures,
       meta: {
+        pageTitle: PAGE_TITLES.ORGANIZATION_CLOSURES,
+        showNavBar: false,
         requiresAuth: true,
       },
     },
     {
-      path: closureUrl(),
-      name: 'organization-closures',
-      component: OrganizationClosures,
+      path: '/:catchAll(.*)',
+      name: 'notfound',
+      redirect: '/',
       meta: {
-        pageTitle: PAGE_TITLES.ORGANIZATION_CLOSURES,
-        showNavBar: false,
         requiresAuth: true,
       },
     },
diff --git a/frontend/src/services/closureService.js b/frontend/src/services/closureService.js
index ed4f96dc6..0bc7b0eec 100644
--- a/frontend/src/services/closureService.js
+++ b/frontend/src/services/closureService.js
@@ -17,6 +17,17 @@ export default {
     }
   },
 
+  async getChangeActionClosure(changeActionClosureId) {
+    try {
+      if (!changeActionClosureId) return [];
+      const response = await ApiService.apiAxios.get(`${ApiRoutes.CHANGE_ACTION_CLOSURE}/${changeActionClosureId}`);
+      return response?.data;
+    } catch (error) {
+      console.log(`Failed to get Change Action Closure - ${error}`);
+      throw error;
+    }
+  },
+
   async createClosureChangeRequest(payload) {
     try {
       if (isEmpty(payload)) return;
diff --git a/frontend/src/utils/constants.js b/frontend/src/utils/constants.js
index efdf3e7ce..795b3e76d 100644
--- a/frontend/src/utils/constants.js
+++ b/frontend/src/utils/constants.js
@@ -56,6 +56,7 @@ export const ApiRoutes = Object.freeze({
   DOCUMENT: baseRoot + '/document',
   DOCUMENT_APPLICATION: baseRoot + '/document/application',
   DOCUMENT_CHANGE_ACTION: baseRoot + '/document/change-action',
+  CHANGE_ACTION_CLOSURE: baseRoot + '/changeRequest/changeActionClosure',
   CANADA_POST: baseRoot + '/canadaPost',
 });
 
@@ -171,10 +172,6 @@ export function changeUrlGuid(
   return `${PATHS.PREFIX.CHANGE_REQUEST}/${changeType}/${changeRecGuid}${suffix}/${urlGuid}`;
 }
 
-export function closureUrl(programYearGuid = ':programYearGuid') {
-  return `${PATHS.CLOSURES}/${programYearGuid}`;
-}
-
 export const NAV_BAR_GROUPS = {
   CCOF: 'CCOF',
   CCFRI: 'CCFRI',
@@ -279,6 +276,8 @@ export const CHANGE_REQUEST_TYPES = {
   PDF_CHANGE: 100000013,
   NEW_CATEGORY: 100000014,
   NEW_CLOSURE: 100000015,
+  EDIT_EXISTING_CLOSURE: 100000016,
+  REMOVE_A_CLOSURE: 100000017,
 };
 
 export const CHANGE_REQUEST_EXTERNAL_STATUS = {


Reviews:
user: login: vietle-cgi
state: CHANGES_REQUESTED
author_association: COLLABORATOR
submitted_at: 2025-04-25T20:04:08Z
user: login: JonahCurlCGI
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-04-25T22:11:40Z
user: login: JonahCurlCGI
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-04-25T22:13:44Z
user: login: JonahCurlCGI
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-04-25T22:14:56Z
user: login: JonahCurlCGI
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-04-25T22:17:00Z
user: login: JonahCurlCGI
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-04-25T22:23:13Z
user: login: JonahCurlCGI
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-04-25T22:27:52Z
user: login: vietle-cgi
state: COMMENTED
author_association: COLLABORATOR
submitted_at: 2025-04-25T22:31:35Z
user: login: vietle-cgi
state: CHANGES_REQUESTED
author_association: COLLABORATOR
submitted_at: 2025-04-29T17:40:14Z
user: login: vietle-cgi
state: CHANGES_REQUESTED
author_association: COLLABORATOR
submitted_at: 2025-04-29T20:13:32Z
user: login: weskubo-cgi
state: CHANGES_REQUESTED
author_association: COLLABORATOR
submitted_at: 2025-04-29T20:30:11Z
user: login: vietle-cgi
state: CHANGES_REQUESTED
author_association: COLLABORATOR
submitted_at: 2025-04-29T20:55:40Z
user: login: JonahCurlCGI
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-04-30T16:21:01Z
user: login: JonahCurlCGI
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-04-30T16:21:46Z
user: login: JonahCurlCGI
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-04-30T16:22:17Z
user: login: JonahCurlCGI
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-04-30T16:32:24Z
user: login: JonahCurlCGI
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-04-30T16:37:52Z
user: login: JonahCurlCGI
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-04-30T16:46:10Z
user: login: JonahCurlCGI
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-04-30T16:48:09Z
user: login: JonahCurlCGI
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-04-30T16:48:20Z
user: login: JonahCurlCGI
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-04-30T16:48:44Z
user: login: JonahCurlCGI
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-04-30T16:51:47Z
user: login: JonahCurlCGI
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-04-30T16:58:00Z
user: login: JonahCurlCGI
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-04-30T16:59:49Z
user: login: JonahCurlCGI
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-04-30T17:00:50Z
user: login: JonahCurlCGI
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-04-30T17:03:19Z
user: login: JonahCurlCGI
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-04-30T17:11:18Z
user: login: JonahCurlCGI
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-04-30T17:11:43Z
user: login: JonahCurlCGI
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-04-30T17:12:17Z
user: login: JonahCurlCGI
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-04-30T17:12:45Z
user: login: JonahCurlCGI
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-04-30T17:19:23Z
user: login: vietle-cgi
state: CHANGES_REQUESTED
author_association: COLLABORATOR
submitted_at: 2025-04-30T20:25:02Z
user: login: JonahCurlCGI
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-04-30T20:27:54Z
user: login: JonahCurlCGI
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-04-30T21:02:38Z
user: login: weskubo-cgi
state: APPROVED
author_association: COLLABORATOR
submitted_at: 2025-04-30T21:05:35Z
user: login: vietle-cgi
state: CHANGES_REQUESTED
author_association: COLLABORATOR
submitted_at: 2025-04-30T21:18:44Z
user: login: JonahCurlCGI
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-04-30T21:26:36Z
user: login: JonahCurlCGI
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-04-30T21:28:48Z
user: login: JonahCurlCGI
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-04-30T21:28:55Z
user: login: vietle-cgi
state: APPROVED
author_association: COLLABORATOR
submitted_at: 2025-04-30T21:44:38Z

Review comments:
diff_hunk: @@ -295,33 +353,49 @@ export default {
   },
   watch: {
     show: {
-      handler(value) {
+      async handler(value) {
+        this.isLoading = true;
         this.isDisplayed = value;
+        if (value) {
+          this.clearData();
+          if (this.requestType === CHANGE_REQUEST_TYPES.REMOVE_A_CLOSURE) {
+            this.input = await this.initInput();
+          }
+        }
+        this.isLoading = false;
       },
     },
   },
   created() {
     this.rules = rules;
     this.DOCUMENT_TYPES = DOCUMENT_TYPES;
+    this.CHANGE_REQUEST_TYPES = CHANGE_REQUEST_TYPES;
   },
   methods: {
+    async initInput() {
+      const input = cloneDeep(this.closure);
path: frontend/src/components/closure/ClosureChangeRequestDialog.vue
user: login: vietle-cgi
body: let's add try catch here and setFailureAlert if there's any error
created_at: 2025-04-25T19:35:20Z
author_association: COLLABORATOR
diff_hunk: @@ -17,6 +17,16 @@ export default {
     }
   },
 
+  async getChangeActionClosure(changeActionClosureId) {
+    try {
+      if (!changeActionClosureId) return [];
+      const response = await ApiService.apiAxios.get(`${ApiRoutes.CHANGE_ACTION_CLOSURE}/${changeActionClosureId}`);
+      return response?.data;
+    } catch (error) {
+      console.log(`Failed to get Change Action Closure - ${error}`);
path: frontend/src/services/closureService.js
user: login: vietle-cgi
body: please add throw error 
created_at: 2025-04-25T19:35:45Z
author_association: COLLABORATOR
diff_hunk: @@ -295,33 +353,49 @@ export default {
   },
   watch: {
     show: {
-      handler(value) {
+      async handler(value) {
+        this.isLoading = true;
         this.isDisplayed = value;
+        if (value) {
path: frontend/src/components/closure/ClosureChangeRequestDialog.vue
user: login: vietle-cgi
body: we can use early return here.
created_at: 2025-04-25T19:39:20Z
author_association: COLLABORATOR
diff_hunk: @@ -268,18 +297,47 @@ export default {
       },
       selectedFacilityWasChanged: true,
       ageGroups: [],
+      uploadedDocuments: [],
     };
   },
   computed: {
     ...mapState(useAppStore, ['getProgramYearNameById', 'getFundingUrl']),
     ...mapState(useApplicationStore, ['fiscalStartAndEndDates', 'getFacilityListForPCFByProgramYearId']),
     ...mapState(useAuthStore, ['userInfo']),
+    title() {
+      switch (this.requestType) {
+        case CHANGE_REQUEST_TYPES.NEW_CLOSURE:
+          return 'New Closure Request';
+        case CHANGE_REQUEST_TYPES.REMOVE_A_CLOSURE:
+          return 'Remove Closure Request';
+        default:
+          return '';
+      }
+    },
+    isDisabled() {
+      return this.requestType === CHANGE_REQUEST_TYPES.REMOVE_A_CLOSURE;
+    },
+    showDocumentUpload() {
+      return this.requestType === CHANGE_REQUEST_TYPES.NEW_CLOSURE || !isEmpty(this.uploadedDocuments);
+    },
     facilityList() {
       return this.getFacilityListForPCFByProgramYearId(this.programYearId);
     },
     rulesAgeGroups() {
       return this.input.fullClosure === false ? rules.required : [];
     },
+    rulesReasonForClosureRemoval() {
+      return this.requestType !== CHANGE_REQUEST_TYPES.REMOVE_A_CLOSURE
+        ? []
path: frontend/src/components/closure/ClosureChangeRequestDialog.vue
user: login: vietle-cgi
body: can we just use rules.required here?
created_at: 2025-04-25T19:41:03Z
author_association: COLLABORATOR
diff_hunk: @@ -336,6 +339,27 @@ async function getChangeRequestDocs(req, res) {
   }
 }
 
+async function getChangeActionClosure(req, res) {
+  const { changeActionClosureId } = req.params;
+  try {
+    const changeActionClosure = await getOperation(`ccof_change_action_closures(${changeActionClosureId})?$select=_ccof_change_action_value,ccof_any_details_added_on_request`);
+    const changeActionClosureForFront = new MappableObjectForFront(changeActionClosure, ChangeActionClosureMappings).toJSON();
+    changeActionClosureForFront.documents = [];
+    if (changeActionClosure?._ccof_change_action_value) {
+      const getDocumentsResponse = await getChangeActionDocument(changeActionClosure._ccof_change_action_value);
+      if (getDocumentsResponse?.value) {
+        getDocumentsResponse.value.forEach((document) => {
+          changeActionClosureForFront.documents.push(new MappableObjectForFront(document, DocumentsMappings));
+        });
+      }
+    }
+    return res.status(HttpStatus.OK).json(changeActionClosureForFront);
+  } catch (e) {
+    log.info(e);
path: backend/src/components/changeRequest.js
user: login: vietle-cgi
body: let's change it to log.error
created_at: 2025-04-25T19:42:04Z
author_association: COLLABORATOR
diff_hunk: @@ -3,7 +3,7 @@
 const log = require('./logger');
 const { MappableObjectForFront, MappableObjectForBack, getMappingString } = require('../util/mapping/MappableObject');
 const { ChangeRequestMappings, ChangeActionRequestMappings, ChangeActionClosureMappings, MtfiMappings, NewFacilityMappings } = require('../util/mapping/ChangeRequestMappings');
-const { DocumentsMappings, UserProfileBaseCCFRIMappings, UserProfileBaseFundingMappings, UserProfileECEWEMappings } = require('../util/mapping/Mappings');
+const { DocumentsMappings, UserProfileBaseCCFRIMappings, UserProfileBaseFundingMappings, UserProfileECEWEMappings, ClosureMappings } = require('../util/mapping/Mappings');
path: backend/src/components/changeRequest.js
user: login: vietle-cgi
body: let's sort it in alphabetical order
created_at: 2025-04-25T19:45:42Z
author_association: COLLABORATOR
diff_hunk: @@ -110,23 +106,24 @@
               </v-col>
             </v-row>
             <div v-if="input.fullClosure === false">
-              <v-row class="ml-0">
+              <v-row class="ml-0" align="center">
                 <h3 class="pr-2">Affected Care Categorie(s)</h3>
                 <p>(select all that apply):</p>
               </v-row>
-              <v-select
-                v-model.lazy="input.ageGroups"
-                :loading="isLoading"
+              <v-combobox
path: frontend/src/components/closure/ClosureChangeRequestDialog.vue
user: login: vietle-cgi
body: if we use v-combobox, user can manually enter new option. maybe just use v-select?
created_at: 2025-04-25T20:00:56Z
author_association: COLLABORATOR
diff_hunk: @@ -3,7 +3,7 @@
 const log = require('./logger');
 const { MappableObjectForFront, MappableObjectForBack, getMappingString } = require('../util/mapping/MappableObject');
 const { ChangeRequestMappings, ChangeActionRequestMappings, ChangeActionClosureMappings, MtfiMappings, NewFacilityMappings } = require('../util/mapping/ChangeRequestMappings');
-const { DocumentsMappings, UserProfileBaseCCFRIMappings, UserProfileBaseFundingMappings, UserProfileECEWEMappings } = require('../util/mapping/Mappings');
+const { DocumentsMappings, UserProfileBaseCCFRIMappings, UserProfileBaseFundingMappings, UserProfileECEWEMappings, ClosureMappings } = require('../util/mapping/Mappings');
path: backend/src/components/changeRequest.js
user: login: JonahCurlCGI
body: Fixed
created_at: 2025-04-25T22:11:39Z
author_association: CONTRIBUTOR
diff_hunk: @@ -336,6 +339,27 @@ async function getChangeRequestDocs(req, res) {
   }
 }
 
+async function getChangeActionClosure(req, res) {
+  const { changeActionClosureId } = req.params;
+  try {
+    const changeActionClosure = await getOperation(`ccof_change_action_closures(${changeActionClosureId})?$select=_ccof_change_action_value,ccof_any_details_added_on_request`);
+    const changeActionClosureForFront = new MappableObjectForFront(changeActionClosure, ChangeActionClosureMappings).toJSON();
+    changeActionClosureForFront.documents = [];
+    if (changeActionClosure?._ccof_change_action_value) {
+      const getDocumentsResponse = await getChangeActionDocument(changeActionClosure._ccof_change_action_value);
+      if (getDocumentsResponse?.value) {
+        getDocumentsResponse.value.forEach((document) => {
+          changeActionClosureForFront.documents.push(new MappableObjectForFront(document, DocumentsMappings));
+        });
+      }
+    }
+    return res.status(HttpStatus.OK).json(changeActionClosureForFront);
+  } catch (e) {
+    log.info(e);
path: backend/src/components/changeRequest.js
user: login: JonahCurlCGI
body: Fixed
created_at: 2025-04-25T22:13:43Z
author_association: CONTRIBUTOR
diff_hunk: @@ -268,18 +297,47 @@ export default {
       },
       selectedFacilityWasChanged: true,
       ageGroups: [],
+      uploadedDocuments: [],
     };
   },
   computed: {
     ...mapState(useAppStore, ['getProgramYearNameById', 'getFundingUrl']),
     ...mapState(useApplicationStore, ['fiscalStartAndEndDates', 'getFacilityListForPCFByProgramYearId']),
     ...mapState(useAuthStore, ['userInfo']),
+    title() {
+      switch (this.requestType) {
+        case CHANGE_REQUEST_TYPES.NEW_CLOSURE:
+          return 'New Closure Request';
+        case CHANGE_REQUEST_TYPES.REMOVE_A_CLOSURE:
+          return 'Remove Closure Request';
+        default:
+          return '';
+      }
+    },
+    isDisabled() {
+      return this.requestType === CHANGE_REQUEST_TYPES.REMOVE_A_CLOSURE;
+    },
+    showDocumentUpload() {
+      return this.requestType === CHANGE_REQUEST_TYPES.NEW_CLOSURE || !isEmpty(this.uploadedDocuments);
+    },
     facilityList() {
       return this.getFacilityListForPCFByProgramYearId(this.programYearId);
     },
     rulesAgeGroups() {
       return this.input.fullClosure === false ? rules.required : [];
     },
+    rulesReasonForClosureRemoval() {
+      return this.requestType !== CHANGE_REQUEST_TYPES.REMOVE_A_CLOSURE
+        ? []
path: frontend/src/components/closure/ClosureChangeRequestDialog.vue
user: login: JonahCurlCGI
body: rules.required returns a specific error message different from the one in the requirements
created_at: 2025-04-25T22:14:55Z
author_association: CONTRIBUTOR
diff_hunk: @@ -17,6 +17,16 @@ export default {
     }
   },
 
+  async getChangeActionClosure(changeActionClosureId) {
+    try {
+      if (!changeActionClosureId) return [];
+      const response = await ApiService.apiAxios.get(`${ApiRoutes.CHANGE_ACTION_CLOSURE}/${changeActionClosureId}`);
+      return response?.data;
+    } catch (error) {
+      console.log(`Failed to get Change Action Closure - ${error}`);
path: frontend/src/services/closureService.js
user: login: JonahCurlCGI
body: Added
created_at: 2025-04-25T22:16:59Z
author_association: CONTRIBUTOR
diff_hunk: @@ -295,33 +353,49 @@ export default {
   },
   watch: {
     show: {
-      handler(value) {
+      async handler(value) {
+        this.isLoading = true;
         this.isDisplayed = value;
+        if (value) {
+          this.clearData();
+          if (this.requestType === CHANGE_REQUEST_TYPES.REMOVE_A_CLOSURE) {
+            this.input = await this.initInput();
+          }
+        }
+        this.isLoading = false;
       },
     },
   },
   created() {
     this.rules = rules;
     this.DOCUMENT_TYPES = DOCUMENT_TYPES;
+    this.CHANGE_REQUEST_TYPES = CHANGE_REQUEST_TYPES;
   },
   methods: {
+    async initInput() {
+      const input = cloneDeep(this.closure);
path: frontend/src/components/closure/ClosureChangeRequestDialog.vue
user: login: JonahCurlCGI
body: Added
created_at: 2025-04-25T22:23:11Z
author_association: CONTRIBUTOR
diff_hunk: @@ -295,33 +353,49 @@ export default {
   },
   watch: {
     show: {
-      handler(value) {
+      async handler(value) {
+        this.isLoading = true;
         this.isDisplayed = value;
+        if (value) {
path: frontend/src/components/closure/ClosureChangeRequestDialog.vue
user: login: JonahCurlCGI
body: Fixed
created_at: 2025-04-25T22:27:50Z
author_association: CONTRIBUTOR
diff_hunk: @@ -268,18 +297,47 @@ export default {
       },
       selectedFacilityWasChanged: true,
       ageGroups: [],
+      uploadedDocuments: [],
     };
   },
   computed: {
     ...mapState(useAppStore, ['getProgramYearNameById', 'getFundingUrl']),
     ...mapState(useApplicationStore, ['fiscalStartAndEndDates', 'getFacilityListForPCFByProgramYearId']),
     ...mapState(useAuthStore, ['userInfo']),
+    title() {
+      switch (this.requestType) {
+        case CHANGE_REQUEST_TYPES.NEW_CLOSURE:
+          return 'New Closure Request';
+        case CHANGE_REQUEST_TYPES.REMOVE_A_CLOSURE:
+          return 'Remove Closure Request';
+        default:
+          return '';
+      }
+    },
+    isDisabled() {
+      return this.requestType === CHANGE_REQUEST_TYPES.REMOVE_A_CLOSURE;
+    },
+    showDocumentUpload() {
+      return this.requestType === CHANGE_REQUEST_TYPES.NEW_CLOSURE || !isEmpty(this.uploadedDocuments);
+    },
     facilityList() {
       return this.getFacilityListForPCFByProgramYearId(this.programYearId);
     },
     rulesAgeGroups() {
       return this.input.fullClosure === false ? rules.required : [];
     },
+    rulesReasonForClosureRemoval() {
+      return this.requestType !== CHANGE_REQUEST_TYPES.REMOVE_A_CLOSURE
+        ? []
path: frontend/src/components/closure/ClosureChangeRequestDialog.vue
user: login: vietle-cgi
body: yes, but I think the error message "This field is required" is sufficient. Please check with Fei to see if she's ok with that. it make our error message more consistent
created_at: 2025-04-25T22:31:35Z
author_association: COLLABORATOR
diff_hunk: @@ -336,6 +339,27 @@ async function getChangeRequestDocs(req, res) {
   }
 }
 
+async function getChangeActionClosure(req, res) {
+  const { changeActionClosureId } = req.params;
+  try {
+    const changeActionClosure = await getOperation(`ccof_change_action_closures(${changeActionClosureId})?$select=_ccof_change_action_value,ccof_any_details_added_on_request`);
+    const changeActionClosureForFront = new MappableObjectForFront(changeActionClosure, ChangeActionClosureMappings).toJSON();
+    changeActionClosureForFront.documents = [];
+    if (changeActionClosure?._ccof_change_action_value) {
+      const getDocumentsResponse = await getChangeActionDocument(changeActionClosure._ccof_change_action_value);
+      if (getDocumentsResponse?.value) {
path: backend/src/components/changeRequest.js
user: login: vietle-cgi
body: I think we don't need this if statement. just "getDocumentsResponse?.value?.forEach"
created_at: 2025-04-29T04:30:25Z
author_association: COLLABORATOR
diff_hunk: @@ -295,33 +353,54 @@ export default {
   },
   watch: {
     show: {
-      handler(value) {
+      async handler(value) {
         this.isDisplayed = value;
+        if (!value) return;
+        this.clearData();
+        this.isLoading = true;
+        if (this.requestType === CHANGE_REQUEST_TYPES.REMOVE_A_CLOSURE) {
+          this.input = await this.initInput();
+        }
+        this.isLoading = false;
       },
     },
   },
   created() {
     this.rules = rules;
     this.DOCUMENT_TYPES = DOCUMENT_TYPES;
+    this.CHANGE_REQUEST_TYPES = CHANGE_REQUEST_TYPES;
   },
   methods: {
+    async initInput() {
+      const input = cloneDeep(this.closure);
+      try {
+        this.ageGroups = await this.getLicenseCategories(this.closure.facilityId);
+        if (this.closure.ageGroups) {
+          const closureAgeGroups = this.closure.ageGroups.split(',').map((value) => {
+            return Number(value);
+          });
+          input.ageGroups = closureAgeGroups;
+        }
+
+        const changeActionClosure = await ClosureService.getChangeActionClosure(this.closure.changeActionClosureId);
+        this.uploadedDocuments = changeActionClosure?.documents ? changeActionClosure.documents : [];
+        input.description = changeActionClosure?.closureDescription ? changeActionClosure.closureDescription : '';
+        return input;
+      } catch (e) {
+        console.log(e);
+        this.setFailureAlert('Failed to load license categories');
path: frontend/src/components/closure/ClosureChangeRequestDialog.vue
user: login: vietle-cgi
body: let's use this generic error message: "An error occurred while loading. Please try again later."
created_at: 2025-04-29T15:52:58Z
author_association: COLLABORATOR
diff_hunk: @@ -151,7 +154,7 @@ import {
 
 export default {
   name: 'OrganizationClosures',
-  components: { AppButton, ClosureConfirmationDialog, ClosureDetailsDialog, NavButton, NewClosureRequestDialog },
+  components: { AppButton, ClosureConfirmationDialog, ClosureDetailsDialog, NavButton, ClosureChangeRequestDialog },
path: frontend/src/components/closure/OrganizationClosures.vue
user: login: vietle-cgi
body: let's sort these components in alphabetical order
created_at: 2025-04-29T15:59:00Z
author_association: COLLABORATOR
diff_hunk: @@ -295,33 +353,54 @@ export default {
   },
   watch: {
     show: {
-      handler(value) {
+      async handler(value) {
         this.isDisplayed = value;
+        if (!value) return;
+        this.clearData();
+        this.isLoading = true;
+        if (this.requestType === CHANGE_REQUEST_TYPES.REMOVE_A_CLOSURE) {
+          this.input = await this.initInput();
+        }
+        this.isLoading = false;
       },
     },
   },
   created() {
     this.rules = rules;
     this.DOCUMENT_TYPES = DOCUMENT_TYPES;
+    this.CHANGE_REQUEST_TYPES = CHANGE_REQUEST_TYPES;
   },
   methods: {
+    async initInput() {
+      const input = cloneDeep(this.closure);
+      try {
+        this.ageGroups = await this.getLicenseCategories(this.closure.facilityId);
+        if (this.closure.ageGroups) {
path: frontend/src/components/closure/ClosureChangeRequestDialog.vue
user: login: vietle-cgi
body: would it work if we write it like below:
`input.ageGroups = this.closure?.ageGroups?.split(',').map((value) => Number(value));`
created_at: 2025-04-29T17:39:47Z
author_association: COLLABORATOR
diff_hunk: @@ -279,6 +276,8 @@ export const CHANGE_REQUEST_TYPES = {
   PDF_CHANGE: 100000013,
   NEW_CATEGORY: 100000014,
   NEW_CLOSURE: 100000015,
+  EDIT_EXISTING_CLOSURE: 100000016,
+  REMOVE_A_CLOSURE: 100000017,
path: frontend/src/utils/constants.js
user: login: weskubo-cgi
body: Unless there's some reason for the "A" (like a match with CRM?) can we just use REMOVE_CLOSURE? Same comment applies to backend constants.
created_at: 2025-04-29T17:48:40Z
author_association: COLLABORATOR
diff_hunk: @@ -365,26 +456,42 @@ export default {
       }
       return processedDocuments;
     },
+    getPayload() {
+      switch (this.requestType) {
+        case CHANGE_REQUEST_TYPES.NEW_CLOSURE:
+          return {
+            applicationId: this.applicationId,
+            programYearId: this.programYearId,
+            organizationId: this.userInfo?.organizationId,
+            facilityId: this.input.facilityId,
+            paidClosure: this.input.paidClosure,
+            fullClosure: this.input.fullClosure,
+            ageGroups: this.input.fullClosure ? undefined : this.input.ageGroups.join(','),
+            startDate: `${this.input.startDate}T12:00:00-07:00`,
+            endDate: `${this.input.endDate}T12:00:00-07:00`,
+            closureReason: this.input.closureReason,
+            closureDescription: this.input.description,
+            documents: this.processDocuments(this.input.documents),
+            changeType: this.requestType,
+          };
+        case CHANGE_REQUEST_TYPES.REMOVE_A_CLOSURE:
+          return {
+            applicationId: this.applicationId,
+            programYearId: this.programYearId,
+            facilityId: this.input.facilityId,
+            organizationId: this.userInfo?.organizationId,
+            closureId: this.closure.closureId,
+            changeType: this.requestType,
+          };
+        default:
+          return undefined;
path: frontend/src/components/closure/ClosureChangeRequestDialog.vue
user: login: weskubo-cgi
body: Is there logic that relies on undefined? If not I'd be more inclined to return null or an empty object depending on how it's used.
created_at: 2025-04-29T17:52:46Z
author_association: COLLABORATOR
diff_hunk: @@ -258,8 +258,11 @@ async function createClosureChangeRequest(req, res) {
     const createChangeRequestReponse = await createRawChangeRequest(req);
     const changeActionClosure = mapChangeActionClosureObjectForBack(req.body);
     changeActionClosure['ccof_change_action@odata.bind'] = `/ccof_change_actions(${createChangeRequestReponse.changeActionId})`;
+    if (req.body.changeType === CHANGE_REQUEST_TYPES.REMOVE_A_CLOSURE) {
+      changeActionClosure['ccof_closure@odata.bind'] = `/ccof_application_ccfri_closures(${req.body.closureId})`;
+    }
     const asyncOperations = [postOperation('ccof_change_action_closures', changeActionClosure), getOperation(`ccof_change_requests(${createChangeRequestReponse.changeRequestId})?$select=ccof_name`)];
-    if (!isEmpty(req.body.documents)) {
+    if (req.body.changeType === CHANGE_REQUEST_TYPES.NEW_CLOSURE && !isEmpty(req.body.documents)) {
path: backend/src/components/changeRequest.js
user: login: weskubo-cgi
body: Similar to line 350 you can skip the isEmpty check if you use optional chaining (?).
created_at: 2025-04-29T19:32:32Z
author_association: COLLABORATOR
diff_hunk: @@ -796,20 +795,20 @@ const router = createRouter({
       },
     },
     {
-      path: '/:catchAll(.*)',
-      name: 'notfound',
-      redirect: '/',
+      path: `${PATHS.CLOSURES}/:programYearGuid`,
path: frontend/src/router.js
user: login: weskubo-cgi
body: Thanks for simplifying this!
created_at: 2025-04-29T19:35:21Z
author_association: COLLABORATOR
diff_hunk: @@ -365,26 +456,42 @@ export default {
       }
       return processedDocuments;
     },
+    getPayload() {
+      switch (this.requestType) {
+        case CHANGE_REQUEST_TYPES.NEW_CLOSURE:
+          return {
+            applicationId: this.applicationId,
+            programYearId: this.programYearId,
+            organizationId: this.userInfo?.organizationId,
+            facilityId: this.input.facilityId,
+            paidClosure: this.input.paidClosure,
+            fullClosure: this.input.fullClosure,
+            ageGroups: this.input.fullClosure ? undefined : this.input.ageGroups.join(','),
+            startDate: `${this.input.startDate}T12:00:00-07:00`,
+            endDate: `${this.input.endDate}T12:00:00-07:00`,
path: frontend/src/components/closure/ClosureChangeRequestDialog.vue
user: login: weskubo-cgi
body: I thought we moved away from requiring the time component?
created_at: 2025-04-29T19:45:28Z
author_association: COLLABORATOR
diff_hunk: @@ -295,21 +309,23 @@ export default {
     previous() {
       this.$router.push(PATHS.ROOT.HOME);
     },
-    toggleNewClosureRequestDialog() {
-      this.showNewClosureRequestDialog = !this.showNewClosureRequestDialog;
+    setClosureRequestType(closureRequestType) {
+      this.closureRequestType = closureRequestType;
path: frontend/src/components/closure/OrganizationClosures.vue
user: login: weskubo-cgi
body: I'm not sure that there's any value to this setter as it doesn't do anything but assign a value.
created_at: 2025-04-29T19:51:35Z
author_association: COLLABORATOR
diff_hunk: @@ -104,12 +104,14 @@
       </v-skeleton-loader>
     </v-card>
     <NavButton @previous="previous" />
-    <NewClosureRequestDialog
-      :show="showNewClosureRequestDialog"
+    <ClosureChangeRequestDialog
+      :closure="closureForRequest"
       :program-year-id="$route.params.programYearGuid"
+      :request-type="closureRequestType"
+      :show="showClosureChangeRequestDialog"
       max-width="60%"
-      @close="toggleNewClosureRequestDialog"
       @submitted="newClosureRequestSubmitted"
+      @close="setClosureRequestType(undefined)"
path: frontend/src/components/closure/OrganizationClosures.vue
user: login: weskubo-cgi
body: Null would be a better choice.
created_at: 2025-04-29T19:55:26Z
author_association: COLLABORATOR
diff_hunk: @@ -190,11 +195,19 @@ export default {
         );
       });
     },
+    showClosureChangeRequestDialog() {
+      return [
path: frontend/src/components/closure/OrganizationClosures.vue
user: login: weskubo-cgi
body: Are there (or will there be) other closureRequestTypes or is the presence of the value enough to know to show the diloag?
created_at: 2025-04-29T19:56:40Z
author_association: COLLABORATOR
diff_hunk: @@ -295,21 +309,23 @@ export default {
     previous() {
       this.$router.push(PATHS.ROOT.HOME);
     },
-    toggleNewClosureRequestDialog() {
-      this.showNewClosureRequestDialog = !this.showNewClosureRequestDialog;
+    setClosureRequestType(closureRequestType) {
+      this.closureRequestType = closureRequestType;
     },
     toggleClosureConfirmationDialog() {
       this.showClosureConfirmationDialog = !this.showClosureConfirmationDialog;
     },
     // To prevent issues with CRM delays from sequential Post and Get requests, the closure is manually added
     // to allow the user to view the closure following the post request.
     async newClosureRequestSubmitted(closureChangeRequest) {
-      const facility = this.getNavByFacilityId(closureChangeRequest.facilityId);
-      closureChangeRequest.facilityName = facility?.facilityName;
-      closureChangeRequest.closureStatus = CLOSURE_STATUSES.PENDING;
-      this.closures.push(closureChangeRequest);
+      if (this.requestType === CHANGE_REQUEST_TYPES.NEW_CLOSURE) {
+        const facility = this.getNavByFacilityId(closureChangeRequest.facilityId);
+        closureChangeRequest.facilityName = facility?.facilityName;
+        closureChangeRequest.closureStatus = CLOSURE_STATUSES.SUBMITTED;
+        this.closures.push(closureChangeRequest);
+      }
       this.changeRequestReferenceId = closureChangeRequest.changeRequestReferenceId;
-      this.toggleNewClosureRequestDialog();
+      this.setClosureRequestType(undefined);
path: frontend/src/components/closure/OrganizationClosures.vue
user: login: weskubo-cgi
body: Null would be a better choice.
created_at: 2025-04-29T20:00:43Z
author_association: COLLABORATOR
diff_hunk: @@ -365,26 +456,42 @@ export default {
       }
       return processedDocuments;
     },
+    getPayload() {
+      switch (this.requestType) {
+        case CHANGE_REQUEST_TYPES.NEW_CLOSURE:
+          return {
+            applicationId: this.applicationId,
+            programYearId: this.programYearId,
+            organizationId: this.userInfo?.organizationId,
+            facilityId: this.input.facilityId,
+            paidClosure: this.input.paidClosure,
+            fullClosure: this.input.fullClosure,
+            ageGroups: this.input.fullClosure ? undefined : this.input.ageGroups.join(','),
path: frontend/src/components/closure/ClosureChangeRequestDialog.vue
user: login: weskubo-cgi
body: Null would be a better choice.
created_at: 2025-04-29T20:07:38Z
author_association: COLLABORATOR
diff_hunk: @@ -295,21 +309,23 @@ export default {
     previous() {
       this.$router.push(PATHS.ROOT.HOME);
     },
-    toggleNewClosureRequestDialog() {
-      this.showNewClosureRequestDialog = !this.showNewClosureRequestDialog;
+    setClosureRequestType(closureRequestType) {
+      this.closureRequestType = closureRequestType;
     },
     toggleClosureConfirmationDialog() {
       this.showClosureConfirmationDialog = !this.showClosureConfirmationDialog;
     },
     // To prevent issues with CRM delays from sequential Post and Get requests, the closure is manually added
     // to allow the user to view the closure following the post request.
     async newClosureRequestSubmitted(closureChangeRequest) {
-      const facility = this.getNavByFacilityId(closureChangeRequest.facilityId);
-      closureChangeRequest.facilityName = facility?.facilityName;
-      closureChangeRequest.closureStatus = CLOSURE_STATUSES.PENDING;
-      this.closures.push(closureChangeRequest);
+      if (this.requestType === CHANGE_REQUEST_TYPES.NEW_CLOSURE) {
+        const facility = this.getNavByFacilityId(closureChangeRequest.facilityId);
+        closureChangeRequest.facilityName = facility?.facilityName;
+        closureChangeRequest.closureStatus = CLOSURE_STATUSES.SUBMITTED;
path: frontend/src/components/closure/OrganizationClosures.vue
user: login: vietle-cgi
body: should we update this status? I don't think we have Submitted status
created_at: 2025-04-29T20:12:19Z
author_association: COLLABORATOR
diff_hunk: @@ -295,21 +309,23 @@ export default {
     previous() {
       this.$router.push(PATHS.ROOT.HOME);
     },
-    toggleNewClosureRequestDialog() {
-      this.showNewClosureRequestDialog = !this.showNewClosureRequestDialog;
+    setClosureRequestType(closureRequestType) {
+      this.closureRequestType = closureRequestType;
     },
     toggleClosureConfirmationDialog() {
       this.showClosureConfirmationDialog = !this.showClosureConfirmationDialog;
     },
     // To prevent issues with CRM delays from sequential Post and Get requests, the closure is manually added
     // to allow the user to view the closure following the post request.
     async newClosureRequestSubmitted(closureChangeRequest) {
-      const facility = this.getNavByFacilityId(closureChangeRequest.facilityId);
-      closureChangeRequest.facilityName = facility?.facilityName;
-      closureChangeRequest.closureStatus = CLOSURE_STATUSES.PENDING;
-      this.closures.push(closureChangeRequest);
+      if (this.requestType === CHANGE_REQUEST_TYPES.NEW_CLOSURE) {
path: frontend/src/components/closure/OrganizationClosures.vue
user: login: vietle-cgi
body: I can't find where you define requestType. should it be closureRequestType?
created_at: 2025-04-29T20:50:20Z
author_association: COLLABORATOR
diff_hunk: @@ -295,21 +309,23 @@ export default {
     previous() {
       this.$router.push(PATHS.ROOT.HOME);
     },
-    toggleNewClosureRequestDialog() {
-      this.showNewClosureRequestDialog = !this.showNewClosureRequestDialog;
+    setClosureRequestType(closureRequestType) {
+      this.closureRequestType = closureRequestType;
     },
     toggleClosureConfirmationDialog() {
       this.showClosureConfirmationDialog = !this.showClosureConfirmationDialog;
     },
     // To prevent issues with CRM delays from sequential Post and Get requests, the closure is manually added
     // to allow the user to view the closure following the post request.
     async newClosureRequestSubmitted(closureChangeRequest) {
path: frontend/src/components/closure/OrganizationClosures.vue
user: login: vietle-cgi
body: does this function need to be async?
created_at: 2025-04-29T20:50:51Z
author_association: COLLABORATOR
diff_hunk: @@ -336,6 +339,27 @@ async function getChangeRequestDocs(req, res) {
   }
 }
 
+async function getChangeActionClosure(req, res) {
+  const { changeActionClosureId } = req.params;
+  try {
+    const changeActionClosure = await getOperation(`ccof_change_action_closures(${changeActionClosureId})?$select=_ccof_change_action_value,ccof_any_details_added_on_request`);
+    const changeActionClosureForFront = new MappableObjectForFront(changeActionClosure, ChangeActionClosureMappings).toJSON();
+    changeActionClosureForFront.documents = [];
+    if (changeActionClosure?._ccof_change_action_value) {
+      const getDocumentsResponse = await getChangeActionDocument(changeActionClosure._ccof_change_action_value);
+      if (getDocumentsResponse?.value) {
path: backend/src/components/changeRequest.js
user: login: JonahCurlCGI
body: Done

created_at: 2025-04-30T16:21:00Z
author_association: CONTRIBUTOR
diff_hunk: @@ -258,8 +258,11 @@ async function createClosureChangeRequest(req, res) {
     const createChangeRequestReponse = await createRawChangeRequest(req);
     const changeActionClosure = mapChangeActionClosureObjectForBack(req.body);
     changeActionClosure['ccof_change_action@odata.bind'] = `/ccof_change_actions(${createChangeRequestReponse.changeActionId})`;
+    if (req.body.changeType === CHANGE_REQUEST_TYPES.REMOVE_A_CLOSURE) {
+      changeActionClosure['ccof_closure@odata.bind'] = `/ccof_application_ccfri_closures(${req.body.closureId})`;
+    }
     const asyncOperations = [postOperation('ccof_change_action_closures', changeActionClosure), getOperation(`ccof_change_requests(${createChangeRequestReponse.changeRequestId})?$select=ccof_name`)];
-    if (!isEmpty(req.body.documents)) {
+    if (req.body.changeType === CHANGE_REQUEST_TYPES.NEW_CLOSURE && !isEmpty(req.body.documents)) {
path: backend/src/components/changeRequest.js
user: login: JonahCurlCGI
body: Done
created_at: 2025-04-30T16:21:45Z
author_association: CONTRIBUTOR
diff_hunk: @@ -295,33 +353,54 @@ export default {
   },
   watch: {
     show: {
-      handler(value) {
+      async handler(value) {
         this.isDisplayed = value;
+        if (!value) return;
+        this.clearData();
+        this.isLoading = true;
+        if (this.requestType === CHANGE_REQUEST_TYPES.REMOVE_A_CLOSURE) {
+          this.input = await this.initInput();
+        }
+        this.isLoading = false;
       },
     },
   },
   created() {
     this.rules = rules;
     this.DOCUMENT_TYPES = DOCUMENT_TYPES;
+    this.CHANGE_REQUEST_TYPES = CHANGE_REQUEST_TYPES;
   },
   methods: {
+    async initInput() {
+      const input = cloneDeep(this.closure);
+      try {
+        this.ageGroups = await this.getLicenseCategories(this.closure.facilityId);
+        if (this.closure.ageGroups) {
+          const closureAgeGroups = this.closure.ageGroups.split(',').map((value) => {
+            return Number(value);
+          });
+          input.ageGroups = closureAgeGroups;
+        }
+
+        const changeActionClosure = await ClosureService.getChangeActionClosure(this.closure.changeActionClosureId);
+        this.uploadedDocuments = changeActionClosure?.documents ? changeActionClosure.documents : [];
+        input.description = changeActionClosure?.closureDescription ? changeActionClosure.closureDescription : '';
+        return input;
+      } catch (e) {
+        console.log(e);
+        this.setFailureAlert('Failed to load license categories');
path: frontend/src/components/closure/ClosureChangeRequestDialog.vue
user: login: JonahCurlCGI
body: Fixed
created_at: 2025-04-30T16:22:16Z
author_association: CONTRIBUTOR
diff_hunk: @@ -365,26 +456,42 @@ export default {
       }
       return processedDocuments;
     },
+    getPayload() {
+      switch (this.requestType) {
+        case CHANGE_REQUEST_TYPES.NEW_CLOSURE:
+          return {
+            applicationId: this.applicationId,
+            programYearId: this.programYearId,
+            organizationId: this.userInfo?.organizationId,
+            facilityId: this.input.facilityId,
+            paidClosure: this.input.paidClosure,
+            fullClosure: this.input.fullClosure,
+            ageGroups: this.input.fullClosure ? undefined : this.input.ageGroups.join(','),
path: frontend/src/components/closure/ClosureChangeRequestDialog.vue
user: login: JonahCurlCGI
body: changed
created_at: 2025-04-30T16:32:22Z
author_association: CONTRIBUTOR
diff_hunk: @@ -365,26 +456,42 @@ export default {
       }
       return processedDocuments;
     },
+    getPayload() {
+      switch (this.requestType) {
+        case CHANGE_REQUEST_TYPES.NEW_CLOSURE:
+          return {
+            applicationId: this.applicationId,
+            programYearId: this.programYearId,
+            organizationId: this.userInfo?.organizationId,
+            facilityId: this.input.facilityId,
+            paidClosure: this.input.paidClosure,
+            fullClosure: this.input.fullClosure,
+            ageGroups: this.input.fullClosure ? undefined : this.input.ageGroups.join(','),
+            startDate: `${this.input.startDate}T12:00:00-07:00`,
+            endDate: `${this.input.endDate}T12:00:00-07:00`,
path: frontend/src/components/closure/ClosureChangeRequestDialog.vue
user: login: JonahCurlCGI
body: Fixed.
created_at: 2025-04-30T16:37:50Z
author_association: CONTRIBUTOR
diff_hunk: @@ -295,33 +353,54 @@ export default {
   },
   watch: {
     show: {
-      handler(value) {
+      async handler(value) {
         this.isDisplayed = value;
+        if (!value) return;
+        this.clearData();
+        this.isLoading = true;
+        if (this.requestType === CHANGE_REQUEST_TYPES.REMOVE_A_CLOSURE) {
+          this.input = await this.initInput();
+        }
+        this.isLoading = false;
       },
     },
   },
   created() {
     this.rules = rules;
     this.DOCUMENT_TYPES = DOCUMENT_TYPES;
+    this.CHANGE_REQUEST_TYPES = CHANGE_REQUEST_TYPES;
   },
   methods: {
+    async initInput() {
+      const input = cloneDeep(this.closure);
+      try {
+        this.ageGroups = await this.getLicenseCategories(this.closure.facilityId);
+        if (this.closure.ageGroups) {
path: frontend/src/components/closure/ClosureChangeRequestDialog.vue
user: login: JonahCurlCGI
body: Yes this works
created_at: 2025-04-30T16:46:08Z
author_association: CONTRIBUTOR
diff_hunk: @@ -365,26 +456,42 @@ export default {
       }
       return processedDocuments;
     },
+    getPayload() {
+      switch (this.requestType) {
+        case CHANGE_REQUEST_TYPES.NEW_CLOSURE:
+          return {
+            applicationId: this.applicationId,
+            programYearId: this.programYearId,
+            organizationId: this.userInfo?.organizationId,
+            facilityId: this.input.facilityId,
+            paidClosure: this.input.paidClosure,
+            fullClosure: this.input.fullClosure,
+            ageGroups: this.input.fullClosure ? undefined : this.input.ageGroups.join(','),
+            startDate: `${this.input.startDate}T12:00:00-07:00`,
+            endDate: `${this.input.endDate}T12:00:00-07:00`,
+            closureReason: this.input.closureReason,
+            closureDescription: this.input.description,
+            documents: this.processDocuments(this.input.documents),
+            changeType: this.requestType,
+          };
+        case CHANGE_REQUEST_TYPES.REMOVE_A_CLOSURE:
+          return {
+            applicationId: this.applicationId,
+            programYearId: this.programYearId,
+            facilityId: this.input.facilityId,
+            organizationId: this.userInfo?.organizationId,
+            closureId: this.closure.closureId,
+            changeType: this.requestType,
+          };
+        default:
+          return undefined;
path: frontend/src/components/closure/ClosureChangeRequestDialog.vue
user: login: JonahCurlCGI
body: There isn't. Should be impossible to reach this state through the portal.
created_at: 2025-04-30T16:47:56Z
author_association: CONTRIBUTOR
diff_hunk: @@ -104,12 +104,14 @@
       </v-skeleton-loader>
     </v-card>
     <NavButton @previous="previous" />
-    <NewClosureRequestDialog
-      :show="showNewClosureRequestDialog"
+    <ClosureChangeRequestDialog
+      :closure="closureForRequest"
       :program-year-id="$route.params.programYearGuid"
+      :request-type="closureRequestType"
+      :show="showClosureChangeRequestDialog"
       max-width="60%"
-      @close="toggleNewClosureRequestDialog"
       @submitted="newClosureRequestSubmitted"
+      @close="setClosureRequestType(undefined)"
path: frontend/src/components/closure/OrganizationClosures.vue
user: login: JonahCurlCGI
body: Fixed
created_at: 2025-04-30T16:48:19Z
author_association: CONTRIBUTOR
diff_hunk: @@ -151,7 +154,7 @@ import {
 
 export default {
   name: 'OrganizationClosures',
-  components: { AppButton, ClosureConfirmationDialog, ClosureDetailsDialog, NavButton, NewClosureRequestDialog },
+  components: { AppButton, ClosureConfirmationDialog, ClosureDetailsDialog, NavButton, ClosureChangeRequestDialog },
path: frontend/src/components/closure/OrganizationClosures.vue
user: login: JonahCurlCGI
body: Fixed
created_at: 2025-04-30T16:48:43Z
author_association: CONTRIBUTOR
diff_hunk: @@ -190,11 +195,19 @@ export default {
         );
       });
     },
+    showClosureChangeRequestDialog() {
+      return [
path: frontend/src/components/closure/OrganizationClosures.vue
user: login: JonahCurlCGI
body: There are other change request types, new facility for example. I think it is safe to assume that the only ones to be used on the component are those for closures.
created_at: 2025-04-30T16:51:45Z
author_association: CONTRIBUTOR
diff_hunk: @@ -295,21 +309,23 @@ export default {
     previous() {
       this.$router.push(PATHS.ROOT.HOME);
     },
-    toggleNewClosureRequestDialog() {
-      this.showNewClosureRequestDialog = !this.showNewClosureRequestDialog;
+    setClosureRequestType(closureRequestType) {
+      this.closureRequestType = closureRequestType;
path: frontend/src/components/closure/OrganizationClosures.vue
user: login: JonahCurlCGI
body: Needed for some @click props.
created_at: 2025-04-30T16:57:58Z
author_association: CONTRIBUTOR
diff_hunk: @@ -295,21 +309,23 @@ export default {
     previous() {
       this.$router.push(PATHS.ROOT.HOME);
     },
-    toggleNewClosureRequestDialog() {
-      this.showNewClosureRequestDialog = !this.showNewClosureRequestDialog;
+    setClosureRequestType(closureRequestType) {
+      this.closureRequestType = closureRequestType;
     },
     toggleClosureConfirmationDialog() {
       this.showClosureConfirmationDialog = !this.showClosureConfirmationDialog;
     },
     // To prevent issues with CRM delays from sequential Post and Get requests, the closure is manually added
     // to allow the user to view the closure following the post request.
     async newClosureRequestSubmitted(closureChangeRequest) {
path: frontend/src/components/closure/OrganizationClosures.vue
user: login: JonahCurlCGI
body: No. Removed
created_at: 2025-04-30T16:59:48Z
author_association: CONTRIBUTOR
diff_hunk: @@ -295,21 +309,23 @@ export default {
     previous() {
       this.$router.push(PATHS.ROOT.HOME);
     },
-    toggleNewClosureRequestDialog() {
-      this.showNewClosureRequestDialog = !this.showNewClosureRequestDialog;
+    setClosureRequestType(closureRequestType) {
+      this.closureRequestType = closureRequestType;
     },
     toggleClosureConfirmationDialog() {
       this.showClosureConfirmationDialog = !this.showClosureConfirmationDialog;
     },
     // To prevent issues with CRM delays from sequential Post and Get requests, the closure is manually added
     // to allow the user to view the closure following the post request.
     async newClosureRequestSubmitted(closureChangeRequest) {
-      const facility = this.getNavByFacilityId(closureChangeRequest.facilityId);
-      closureChangeRequest.facilityName = facility?.facilityName;
-      closureChangeRequest.closureStatus = CLOSURE_STATUSES.PENDING;
-      this.closures.push(closureChangeRequest);
+      if (this.requestType === CHANGE_REQUEST_TYPES.NEW_CLOSURE) {
path: frontend/src/components/closure/OrganizationClosures.vue
user: login: JonahCurlCGI
body: Fixed
created_at: 2025-04-30T17:00:49Z
author_association: CONTRIBUTOR
diff_hunk: @@ -295,21 +309,23 @@ export default {
     previous() {
       this.$router.push(PATHS.ROOT.HOME);
     },
-    toggleNewClosureRequestDialog() {
-      this.showNewClosureRequestDialog = !this.showNewClosureRequestDialog;
+    setClosureRequestType(closureRequestType) {
+      this.closureRequestType = closureRequestType;
     },
     toggleClosureConfirmationDialog() {
       this.showClosureConfirmationDialog = !this.showClosureConfirmationDialog;
     },
     // To prevent issues with CRM delays from sequential Post and Get requests, the closure is manually added
     // to allow the user to view the closure following the post request.
     async newClosureRequestSubmitted(closureChangeRequest) {
-      const facility = this.getNavByFacilityId(closureChangeRequest.facilityId);
-      closureChangeRequest.facilityName = facility?.facilityName;
-      closureChangeRequest.closureStatus = CLOSURE_STATUSES.PENDING;
-      this.closures.push(closureChangeRequest);
+      if (this.requestType === CHANGE_REQUEST_TYPES.NEW_CLOSURE) {
+        const facility = this.getNavByFacilityId(closureChangeRequest.facilityId);
+        closureChangeRequest.facilityName = facility?.facilityName;
+        closureChangeRequest.closureStatus = CLOSURE_STATUSES.SUBMITTED;
path: frontend/src/components/closure/OrganizationClosures.vue
user: login: JonahCurlCGI
body: Changed to updated statuses
created_at: 2025-04-30T17:03:18Z
author_association: CONTRIBUTOR
diff_hunk: @@ -295,21 +309,23 @@ export default {
     previous() {
       this.$router.push(PATHS.ROOT.HOME);
     },
-    toggleNewClosureRequestDialog() {
-      this.showNewClosureRequestDialog = !this.showNewClosureRequestDialog;
+    setClosureRequestType(closureRequestType) {
+      this.closureRequestType = closureRequestType;
     },
     toggleClosureConfirmationDialog() {
       this.showClosureConfirmationDialog = !this.showClosureConfirmationDialog;
     },
     // To prevent issues with CRM delays from sequential Post and Get requests, the closure is manually added
     // to allow the user to view the closure following the post request.
     async newClosureRequestSubmitted(closureChangeRequest) {
-      const facility = this.getNavByFacilityId(closureChangeRequest.facilityId);
-      closureChangeRequest.facilityName = facility?.facilityName;
-      closureChangeRequest.closureStatus = CLOSURE_STATUSES.PENDING;
-      this.closures.push(closureChangeRequest);
+      if (this.requestType === CHANGE_REQUEST_TYPES.NEW_CLOSURE) {
+        const facility = this.getNavByFacilityId(closureChangeRequest.facilityId);
+        closureChangeRequest.facilityName = facility?.facilityName;
+        closureChangeRequest.closureStatus = CLOSURE_STATUSES.SUBMITTED;
+        this.closures.push(closureChangeRequest);
+      }
       this.changeRequestReferenceId = closureChangeRequest.changeRequestReferenceId;
-      this.toggleNewClosureRequestDialog();
+      this.setClosureRequestType(undefined);
path: frontend/src/components/closure/OrganizationClosures.vue
user: login: JonahCurlCGI
body: Fixed
created_at: 2025-04-30T17:11:17Z
author_association: CONTRIBUTOR
diff_hunk: @@ -295,21 +309,23 @@ export default {
     previous() {
       this.$router.push(PATHS.ROOT.HOME);
     },
-    toggleNewClosureRequestDialog() {
-      this.showNewClosureRequestDialog = !this.showNewClosureRequestDialog;
+    setClosureRequestType(closureRequestType) {
+      this.closureRequestType = closureRequestType;
path: frontend/src/components/closure/OrganizationClosures.vue
user: login: JonahCurlCGI
body: Fixed and working.
created_at: 2025-04-30T17:11:42Z
author_association: CONTRIBUTOR
diff_hunk: @@ -796,20 +795,20 @@ const router = createRouter({
       },
     },
     {
-      path: '/:catchAll(.*)',
-      name: 'notfound',
-      redirect: '/',
+      path: `${PATHS.CLOSURES}/:programYearGuid`,
path: frontend/src/router.js
user: login: JonahCurlCGI
body: No problem
created_at: 2025-04-30T17:12:16Z
author_association: CONTRIBUTOR
diff_hunk: @@ -279,6 +276,8 @@ export const CHANGE_REQUEST_TYPES = {
   PDF_CHANGE: 100000013,
   NEW_CATEGORY: 100000014,
   NEW_CLOSURE: 100000015,
+  EDIT_EXISTING_CLOSURE: 100000016,
+  REMOVE_A_CLOSURE: 100000017,
path: frontend/src/utils/constants.js
user: login: JonahCurlCGI
body: This is to match CRM
created_at: 2025-04-30T17:12:44Z
author_association: CONTRIBUTOR
diff_hunk: @@ -110,23 +106,24 @@
               </v-col>
             </v-row>
             <div v-if="input.fullClosure === false">
-              <v-row class="ml-0">
+              <v-row class="ml-0" align="center">
                 <h3 class="pr-2">Affected Care Categorie(s)</h3>
                 <p>(select all that apply):</p>
               </v-row>
-              <v-select
-                v-model.lazy="input.ageGroups"
-                :loading="isLoading"
+              <v-combobox
path: frontend/src/components/closure/ClosureChangeRequestDialog.vue
user: login: JonahCurlCGI
body: Fixed in earlier commit
created_at: 2025-04-30T17:19:22Z
author_association: CONTRIBUTOR
diff_hunk: @@ -129,10 +131,10 @@
 import { mapState } from 'pinia';
 
 import AppButton from '@/components/guiComponents/AppButton.vue';
-import ClosureConfirmationDialog from '@/components/util/ClosureConfirmationDialog.vue';
+import ClosureConfirmationDialog from '@/components/closure/ClosureConfirmationDialog.vue';
 import ClosureDetailsDialog from '@/components/ClosureDetailsDialog.vue';
 import NavButton from '@/components/util/NavButton.vue';
-import NewClosureRequestDialog from '@/components/NewClosureRequestDialog.vue';
+import ClosureChangeRequestDialog from '@/components/closure/ClosureChangeRequestDialog.vue';
path: frontend/src/components/closure/OrganizationClosures.vue
user: login: vietle-cgi
body: let's sort this component in alphabetical order
created_at: 2025-04-30T20:14:00Z
author_association: COLLABORATOR
diff_hunk: @@ -129,10 +131,10 @@
 import { mapState } from 'pinia';
 
 import AppButton from '@/components/guiComponents/AppButton.vue';
-import ClosureConfirmationDialog from '@/components/util/ClosureConfirmationDialog.vue';
+import ClosureConfirmationDialog from '@/components/closure/ClosureConfirmationDialog.vue';
 import ClosureDetailsDialog from '@/components/ClosureDetailsDialog.vue';
 import NavButton from '@/components/util/NavButton.vue';
-import NewClosureRequestDialog from '@/components/NewClosureRequestDialog.vue';
+import ClosureChangeRequestDialog from '@/components/closure/ClosureChangeRequestDialog.vue';
path: frontend/src/components/closure/OrganizationClosures.vue
user: login: JonahCurlCGI
body: Done
created_at: 2025-04-30T20:27:53Z
author_association: CONTRIBUTOR
diff_hunk: @@ -268,18 +297,47 @@ export default {
       },
       selectedFacilityWasChanged: true,
       ageGroups: [],
+      uploadedDocuments: [],
     };
   },
   computed: {
     ...mapState(useAppStore, ['getProgramYearNameById', 'getFundingUrl']),
     ...mapState(useApplicationStore, ['fiscalStartAndEndDates', 'getFacilityListForPCFByProgramYearId']),
     ...mapState(useAuthStore, ['userInfo']),
+    title() {
+      switch (this.requestType) {
+        case CHANGE_REQUEST_TYPES.NEW_CLOSURE:
+          return 'New Closure Request';
+        case CHANGE_REQUEST_TYPES.REMOVE_A_CLOSURE:
+          return 'Remove Closure Request';
+        default:
+          return '';
+      }
+    },
+    isDisabled() {
+      return this.requestType === CHANGE_REQUEST_TYPES.REMOVE_A_CLOSURE;
+    },
+    showDocumentUpload() {
+      return this.requestType === CHANGE_REQUEST_TYPES.NEW_CLOSURE || !isEmpty(this.uploadedDocuments);
+    },
     facilityList() {
       return this.getFacilityListForPCFByProgramYearId(this.programYearId);
     },
     rulesAgeGroups() {
       return this.input.fullClosure === false ? rules.required : [];
     },
+    rulesReasonForClosureRemoval() {
+      return this.requestType !== CHANGE_REQUEST_TYPES.REMOVE_A_CLOSURE
+        ? []
path: frontend/src/components/closure/ClosureChangeRequestDialog.vue
user: login: JonahCurlCGI
body: Confirmed with Motria, rules.required is sufficient
created_at: 2025-04-30T21:02:36Z
author_association: CONTRIBUTOR
diff_hunk: @@ -186,35 +184,55 @@
                 <h3>Reason:</h3>
               </v-col>
               <v-col cols="12" lg="9">
-                <v-text-field v-model="input.closureReason" variant="outlined" :rules="rules.required"></v-text-field>
+                <v-text-field
+                  v-model="input.closureReason"
+                  :disabled="isDisabled"
+                  variant="outlined"
+                  :rules="rules.required"
+                ></v-text-field>
               </v-col>
             </v-row>
             <h3>Request Description:</h3>
             <v-textarea
               v-model="input.description"
+              :disabled="isDisabled"
               variant="outlined"
               label="Detailed description of request"
               class="text-left mt-3"
             />
             <AppDocumentUpload
               :loading="isLoading"
+              :readonly="isDisabled"
               :document-type="DOCUMENT_TYPES.CLOSURE_REQUEST"
               title="Supporting Documents"
               :required="false"
+              :uploaded-documents="uploadedDocuments"
               @update-documents-to-upload="updateDocuments"
             />
           </v-container>
+          <v-container v-if="requestType === CHANGE_REQUEST_TYPES.REMOVE_A_CLOSURE" class="pa-0 pt-4">
+            <h3>Reason for closure removal:</h3>
+            <v-textarea
+              v-model="input.reasonForClosureRemoval"
+              variant="outlined"
+              :rules="requestType === CHANGE_REQUEST_TYPES.REMOVE_A_CLOSURE ? rules.required : []"
path: frontend/src/components/closure/ClosureChangeRequestDialog.vue
user: login: vietle-cgi
body: we don't need to check requestType again since you've already checked it in v-container
created_at: 2025-04-30T21:15:18Z
author_association: COLLABORATOR
diff_hunk: @@ -110,7 +106,7 @@
               </v-col>
             </v-row>
             <div v-if="input.fullClosure === false">
-              <v-row class="ml-0">
+              <v-row class="ml-0" align="center">
                 <h3 class="pr-2">Affected Care Categorie(s)</h3>
path: frontend/src/components/closure/ClosureChangeRequestDialog.vue
user: login: vietle-cgi
body: should this field be readonly in the "Remove Closure" request?
created_at: 2025-04-30T21:17:11Z
author_association: COLLABORATOR
diff_hunk: @@ -122,7 +118,7 @@
                 item-value="value"
                 label="Select affected care categories"
path: frontend/src/components/closure/ClosureChangeRequestDialog.vue
user: login: vietle-cgi
body: the chip layout hasn't been fixed yet. please use the fix that I sent you in the chat.
created_at: 2025-04-30T21:18:00Z
author_association: COLLABORATOR
diff_hunk: @@ -186,35 +184,55 @@
                 <h3>Reason:</h3>
               </v-col>
               <v-col cols="12" lg="9">
-                <v-text-field v-model="input.closureReason" variant="outlined" :rules="rules.required"></v-text-field>
+                <v-text-field
+                  v-model="input.closureReason"
+                  :disabled="isDisabled"
+                  variant="outlined"
+                  :rules="rules.required"
+                ></v-text-field>
               </v-col>
             </v-row>
             <h3>Request Description:</h3>
             <v-textarea
               v-model="input.description"
+              :disabled="isDisabled"
               variant="outlined"
               label="Detailed description of request"
               class="text-left mt-3"
             />
             <AppDocumentUpload
               :loading="isLoading"
+              :readonly="isDisabled"
               :document-type="DOCUMENT_TYPES.CLOSURE_REQUEST"
               title="Supporting Documents"
               :required="false"
+              :uploaded-documents="uploadedDocuments"
               @update-documents-to-upload="updateDocuments"
             />
           </v-container>
+          <v-container v-if="requestType === CHANGE_REQUEST_TYPES.REMOVE_A_CLOSURE" class="pa-0 pt-4">
+            <h3>Reason for closure removal:</h3>
+            <v-textarea
+              v-model="input.reasonForClosureRemoval"
+              variant="outlined"
+              :rules="requestType === CHANGE_REQUEST_TYPES.REMOVE_A_CLOSURE ? rules.required : []"
path: frontend/src/components/closure/ClosureChangeRequestDialog.vue
user: login: JonahCurlCGI
body: Fixed
created_at: 2025-04-30T21:26:36Z
author_association: CONTRIBUTOR
diff_hunk: @@ -110,7 +106,7 @@
               </v-col>
             </v-row>
             <div v-if="input.fullClosure === false">
-              <v-row class="ml-0">
+              <v-row class="ml-0" align="center">
                 <h3 class="pr-2">Affected Care Categorie(s)</h3>
path: frontend/src/components/closure/ClosureChangeRequestDialog.vue
user: login: JonahCurlCGI
body: fixed
created_at: 2025-04-30T21:28:48Z
author_association: CONTRIBUTOR
diff_hunk: @@ -122,7 +118,7 @@
                 item-value="value"
                 label="Select affected care categories"
path: frontend/src/components/closure/ClosureChangeRequestDialog.vue
user: login: JonahCurlCGI
body: fixed
created_at: 2025-04-30T21:28:55Z
author_association: CONTRIBUTOR

PR comments:
user: login: sonarqubecloud[bot]
created_at: 2025-04-30T21:44:21Z
body: ## [![Quality Gate Passed](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/QualityGateBadge/qg-passed-20px.png 'Quality Gate Passed')](https://sonarcloud.io/dashboard?id=bcgov_EDUC-CCOF&pullRequest=700) **Quality Gate passed**  
Issues  
![](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/passed-16px.png '') [1 New issue](https://sonarcloud.io/project/issues?id=bcgov_EDUC-CCOF&pullRequest=700&issueStatuses=OPEN,CONFIRMED&sinceLeakPeriod=true)  
![](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/accepted-16px.png '') [0 Accepted issues](https://sonarcloud.io/project/issues?id=bcgov_EDUC-CCOF&pullRequest=700&issueStatuses=ACCEPTED)

Measures  
![](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/passed-16px.png '') [0 Security Hotspots](https://sonarcloud.io/project/security_hotspots?id=bcgov_EDUC-CCOF&pullRequest=700&issueStatuses=OPEN,CONFIRMED&sinceLeakPeriod=true)  
![](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/passed-16px.png '') [0.0% Coverage on New Code](https://sonarcloud.io/component_measures?id=bcgov_EDUC-CCOF&pullRequest=700&metric=new_coverage&view=list)  
![](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/passed-16px.png '') [0.0% Duplication on New Code](https://sonarcloud.io/component_measures?id=bcgov_EDUC-CCOF&pullRequest=700&metric=new_duplicated_lines_density&view=list)  
  
[See analysis details on SonarQube Cloud](https://sonarcloud.io/dashboard?id=bcgov_EDUC-CCOF&pullRequest=700)


author_association: NONE