| 1 | <template> |
| 2 | <n-button secondary :loading="merging" :size @click="openDialog()"> |
| 3 | <template #icon> |
| 4 | <Icon :name="MergeIcon" /> |
| 5 | </template> |
| 6 | Merge into Case |
| 7 | </n-button> |
| 8 | |
| 9 | <n-modal |
| 10 | v-model:show="showMergeBox" |
| 11 | display-directive="show" |
| 12 | preset="card" |
| 13 | :title="`Select the case you want to merge ${alerts.length > 1 ? 'them' : 'it'} with :`" |
| 14 | :style="{ maxWidth: 'min(850px, 90vw)', minHeight: 'min(540px, 90vh)', maxHeight: '80vh' }" |
| 15 | content-class="flex flex-col overflow-hidden px-2! py-0!" |
| 16 | segmented |
| 17 | > |
| 18 | <n-spin |
| 19 | :show="loadingCases" |
| 20 | class="flex grow flex-col overflow-hidden" |
| 21 | content-class="flex grow flex-col overflow-hidden" |
| 22 | > |
| 23 | <n-scrollbar class="flex grow flex-col" content-class="grow" trigger="none"> |
| 24 | <div class="flex flex-col gap-2 px-5 py-5"> |
| 25 | <template v-if="linkableCases.length"> |
| 26 | <CaseItem |
| 27 | v-for="item of linkableCases" |
| 28 | :key="item.id" |
| 29 | :case-data="item" |
| 30 | compact |
| 31 | embedded |
| 32 | :highlight="selectedCase?.id === item.id" |
| 33 | @click="toggleSelectedCase(item)" |
| 34 | /> |
| 35 | </template> |
| 36 | <template v-else> |
| 37 | <n-empty v-if="!loadingCases" description="No items found" class="h-48 justify-center" /> |
| 38 | </template> |
| 39 | </div> |
| 40 | </n-scrollbar> |
| 41 | </n-spin> |
| 42 | |
| 43 | <template #footer> |
| 44 | <div class="flex justify-end"> |
| 45 | <n-button type="success" :disabled="!selectedCase" :loading="merging" @click="linkCase()"> |
| 46 | <template #icon> |
| 47 | <Icon :name="MergeIcon" /> |
| 48 | </template> |
| 49 | Confirm Merge {{ selectedCase ? `with Case #${selectedCase.id}` : "" }} |
| 50 | </n-button> |
| 51 | </div> |
| 52 | </template> |
| 53 | </n-modal> |
| 54 | </template> |
| 55 | |
| 56 | <script setup lang="ts"> |
| 57 | import type { ButtonSize } from "naive-ui" |
| 58 | import type { Ref } from "vue" |
| 59 | import type { Alert } from "@/types/incidentManagement/alerts.d" |
| 60 | import type { Case } from "@/types/incidentManagement/cases.d" |
| 61 | import _orderBy from "lodash/orderBy" |
| 62 | import { NButton, NEmpty, NModal, NScrollbar, NSpin, useMessage } from "naive-ui" |
| 63 | import { inject, ref, watch } from "vue" |
| 64 | import Api from "@/api" |
| 65 | import Icon from "@/components/common/Icon.vue" |
| 66 | import CaseItem from "../cases/CaseItem.vue" |
| 67 | |
| 68 | const { alerts, size } = defineProps<{ alerts: Alert[]; size?: ButtonSize }>() |
| 69 | |
| 70 | const emit = defineEmits<{ |
| 71 | (e: "updated", value: Alert): void |
| 72 | (e: "merged"): void |
| 73 | }>() |
| 74 | |
| 75 | const MergeIcon = "carbon:ibm-cloud-direct-link-1-connect" |
| 76 | const message = useMessage() |
| 77 | const merging = ref(false) |
| 78 | const showMergeBox = ref(false) |
| 79 | const loadingCases = ref(false) |
| 80 | const linkableCases = inject<Ref<Case[]>>("linkable-cases", ref([])) |
| 81 | const selectedCase = ref<Case | null>(null) |
| 82 | |
| 83 | watch(showMergeBox, val => { |
| 84 | if (val && !linkableCases.value.length) { |
| 85 | getCasesList() |
| 86 | } |
| 87 | }) |
| 88 | |
| 89 | function updateAlert(updatedAlert: Alert) { |
| 90 | emit("updated", updatedAlert) |
| 91 | } |
| 92 | |
| 93 | function openDialog() { |
| 94 | showMergeBox.value = true |
| 95 | } |
| 96 | |
| 97 | function closeDialog() { |
| 98 | showMergeBox.value = false |
| 99 | } |
| 100 | |
| 101 | function toggleSelectedCase(caseEntity: Case) { |
| 102 | if (selectedCase.value?.id === caseEntity.id) { |
| 103 | selectedCase.value = null |
| 104 | } else { |
| 105 | selectedCase.value = caseEntity |
| 106 | } |
| 107 | } |
| 108 | |
| 109 | function getCasesList() { |
| 110 | loadingCases.value = true |
| 111 | |
| 112 | Api.incidentManagement.cases |
| 113 | .getCasesList(undefined, { page: 1, pageSize: 9999 }) |
| 114 | .then(res => { |
| 115 | if (res.data.success) { |
| 116 | linkableCases.value = _orderBy(res.data?.cases || [], ["id"], ["desc"]) |
| 117 | } else { |
| 118 | message.warning(res.data?.message || "An error occurred. Please try again later.") |
| 119 | } |
| 120 | }) |
| 121 | .catch(err => { |
| 122 | message.error(err.response?.data?.message || "An error occurred. Please try again later.") |
| 123 | }) |
| 124 | .finally(() => { |
| 125 | loadingCases.value = false |
| 126 | }) |
| 127 | } |
| 128 | |
| 129 | function linkCase() { |
| 130 | if (selectedCase.value?.id) { |
| 131 | merging.value = true |
| 132 | |
| 133 | Api.incidentManagement.cases |
| 134 | .multiLinkCase( |
| 135 | alerts.map(o => o.id), |
| 136 | selectedCase.value.id |
| 137 | ) |
| 138 | .then(res => { |
| 139 | if (res.data.success) { |
| 140 | closeDialog() |
| 141 | |
| 142 | for (const alert of alerts) { |
| 143 | const caseId = res.data.case_alert_links.find(o => o.alert_id === alert.id)?.case_id || 0 |
| 144 | const caseData = linkableCases.value.find(o => o.id === caseId) || null |
| 145 | updateAlert({ |
| 146 | ...alert, |
| 147 | linked_cases: [ |
| 148 | { |
| 149 | id: caseId, |
| 150 | case_name: caseData?.case_name || "", |
| 151 | case_description: caseData?.case_description || "", |
| 152 | case_creation_time: caseData?.case_creation_time || new Date(), |
| 153 | assigned_to: caseData?.assigned_to || null, |
| 154 | case_status: caseData?.case_status || null, |
| 155 | customer_code: caseData?.customer_code || null, |
| 156 | comments: caseData?.comments || [] |
| 157 | } |
| 158 | ] |
| 159 | }) |
| 160 | } |
| 161 | |
| 162 | emit("merged") |
| 163 | message.success(res.data?.message || "Case linked successfully") |
| 164 | } else { |
| 165 | message.warning(res.data?.message || "An error occurred. Please try again later.") |
| 166 | } |
| 167 | }) |
| 168 | .catch(err => { |
| 169 | message.error(err.response?.data?.message || "An error occurred. Please try again later.") |
| 170 | }) |
| 171 | .finally(() => { |
| 172 | merging.value = false |
| 173 | }) |
| 174 | } |
| 175 | } |
| 176 | </script> |