main
vue 199 lines 4.85 KB
Raw
1 <template>
2 <n-button :size type="primary" :loading="exporting" secondary @click="showForm = true">
3 <template #icon>
4 <Icon :name="ReportIcon" />
5 </template>
6 Generate Report
7 </n-button>
8
9 <n-modal
10 v-model:show="showForm"
11 display-directive="show"
12 preset="card"
13 :style="{ maxWidth: 'min(600px, 90vw)', minHeight: 'min(240px, 90vh)', overflow: 'hidden' }"
14 title="Generate Report"
15 :bordered="false"
16 segmented
17 >
18 <n-spin :show="exporting" class="creation-report-form">
19 <n-form ref="formRef" :model="form" :rules>
20 <div class="flex flex-col gap-2">
21 <n-form-item label="Template" path="template_name">
22 <CaseReportTemplateSelect v-model:value="form.template_name" />
23 </n-form-item>
24 <n-collapse-transition :show="!!form.template_name">
25 <n-form-item label="Filename" path="file_name">
26 <n-input-group>
27 <n-input
28 v-model:value.trim="form.file_name"
29 placeholder="Please insert File Name"
30 clearable
31 />
32 <n-input-group-label v-if="reportType">.{{ reportType }}</n-input-group-label>
33 </n-input-group>
34 </n-form-item>
35 </n-collapse-transition>
36
37 <div class="mt-3 flex justify-between gap-4">
38 <n-button :disabled="exporting" @click="reset()">Reset</n-button>
39 <n-button
40 type="primary"
41 :disabled="!isValid"
42 :loading="exporting"
43 @click="validate(() => exportCases())"
44 >
45 Generate
46 </n-button>
47 </div>
48 </div>
49 </n-form>
50 </n-spin>
51 </n-modal>
52 </template>
53
54 <script setup lang="ts">
55 import type { ButtonSize, FormInst, FormRules, FormValidationError } from "naive-ui"
56 import type { CaseReportPayload } from "@/api/endpoints/incidentManagement/cases"
57 import type { DeepNullable } from "@/types/common"
58 import { saveAs } from "file-saver"
59 import {
60 NButton,
61 NCollapseTransition,
62 NForm,
63 NFormItem,
64 NInput,
65 NInputGroup,
66 NInputGroupLabel,
67 NModal,
68 NSpin,
69 useMessage
70 } from "naive-ui"
71 import { computed, ref } from "vue"
72 import Api from "@/api"
73 import Icon from "@/components/common/Icon.vue"
74 import { useSettingsStore } from "@/stores/settings"
75 import { formatDate } from "@/utils/format"
76 import CaseReportTemplateSelect from "./CaseReportTemplateSelect.vue"
77
78 const { size, caseId } = defineProps<{ size?: ButtonSize; caseId: number }>()
79
80 const ReportIcon = "carbon:report-data"
81 const dFormats = useSettingsStore().dateFormat
82 const exporting = ref(false)
83 const showForm = ref(false)
84 const message = useMessage()
85 const form = ref<DeepNullable<CaseReportPayload>>(getClearForm())
86 const formRef = ref<FormInst | null>(null)
87 const reportType = computed<"docx" | "pdf" | null>(() => {
88 const ext = form.value.template_name?.split(".").pop()?.toLowerCase() || null
89 switch (ext) {
90 case "docx":
91 return "docx"
92 case "html":
93 return "pdf"
94 default:
95 return null
96 }
97 })
98
99 const rules: FormRules = {
100 template_name: {
101 required: true,
102 message: "Please input the Report Template",
103 trigger: ["input", "blur"]
104 },
105 file_name: {
106 required: true,
107 message: "Please input the Report Filename",
108 trigger: ["input", "blur"]
109 }
110 }
111
112 const isValid = computed(() => {
113 if (!form.value.template_name) {
114 return false
115 }
116 if (!form.value.file_name) {
117 return false
118 }
119
120 return true
121 })
122
123 function validate(cb?: () => void) {
124 if (!formRef.value) return
125
126 formRef.value.validate((errors?: Array<FormValidationError>) => {
127 if (!errors) {
128 if (cb) cb()
129 } else {
130 message.warning("You must fill in the required fields correctly.")
131 return false
132 }
133 })
134 }
135
136 function getClearForm(): DeepNullable<CaseReportPayload> {
137 return {
138 case_id: caseId,
139 file_name: null,
140 template_name: null
141 }
142 }
143
144 function reset(force?: boolean) {
145 if (!exporting.value || force) {
146 resetForm()
147 formRef.value?.restoreValidation()
148 }
149 }
150
151 function resetForm() {
152 form.value = getClearForm()
153 }
154
155 function exportCases() {
156 if (!form.value.file_name || !form.value.template_name || !reportType.value) return
157
158 exporting.value = true
159
160 const extension = reportType.value === "pdf" ? "pdf" : "docx"
161 const mimeType =
162 reportType.value === "pdf"
163 ? "application/pdf"
164 : "application/vnd.openxmlformats-officedocument.wordprocessingml.document"
165
166 const fileName = form.value.file_name
167 ? `${form.value.file_name}.${extension}`
168 : `case:${caseId}_report_${formatDate(new Date(), dFormats.datetimesec)}.${extension}`
169
170 Api.incidentManagement.cases
171 .generateCaseReport(
172 {
173 case_id: caseId,
174 file_name: form.value.file_name,
175 template_name: form.value.template_name
176 },
177 reportType.value
178 )
179 .then(res => {
180 if (res.data) {
181 saveAs(
182 new Blob([res.data], {
183 type: mimeType
184 }),
185 fileName
186 )
187 reset(true)
188 } else {
189 message.warning("An error occurred. Please try again later.")
190 }
191 })
192 .catch(err => {
193 message.error(err.response?.data?.message || "An error occurred. Please try again later.")
194 })
195 .finally(() => {
196 exporting.value = false
197 })
198 }
199 </script>