| 1 | <template> |
| 2 | <n-form :disabled="loading" label-placement="top"> |
| 3 | <n-form-item label="Case name" required> |
| 4 | <n-input v-model:value="model.case_name" placeholder="Insert case name" /> |
| 5 | </n-form-item> |
| 6 | <n-form-item label="Case status" required> |
| 7 | <n-select v-model:value="model.case_status" :options="statusOptions" placeholder="Select case status" /> |
| 8 | </n-form-item> |
| 9 | <n-form-item label="Case description" required> |
| 10 | <n-input |
| 11 | v-model:value="model.case_description" |
| 12 | type="textarea" |
| 13 | :autosize="{ minRows: 4, maxRows: 8 }" |
| 14 | placeholder="Insert case description" |
| 15 | /> |
| 16 | </n-form-item> |
| 17 | <n-form-item label="Assigned to (optional)"> |
| 18 | <n-select |
| 19 | v-model:value="model.assigned_to" |
| 20 | :options="assignedToOptions" |
| 21 | :loading="optionsLoading" |
| 22 | clearable |
| 23 | filterable |
| 24 | placeholder="Select assignee" |
| 25 | /> |
| 26 | </n-form-item> |
| 27 | <div class="flex justify-end gap-3"> |
| 28 | <n-button :disabled="loading" @click="emit('cancel')">Cancel</n-button> |
| 29 | <n-button type="primary" :loading :disabled="!isValidForm" @click="handleSubmit">Create case</n-button> |
| 30 | </div> |
| 31 | </n-form> |
| 32 | </template> |
| 33 | |
| 34 | <script setup lang="ts"> |
| 35 | import type { CasePayload } from "@/api/endpoints/cases" |
| 36 | import type { ApiError } from "@/types/common" |
| 37 | import { NButton, NForm, NFormItem, NInput, NSelect, useMessage } from "naive-ui" |
| 38 | import { computed, onBeforeMount, ref } from "vue" |
| 39 | import Api from "@/api" |
| 40 | import { useAuthStore } from "@/stores/auth" |
| 41 | import { getApiErrorMessage } from "@/utils" |
| 42 | |
| 43 | const emit = defineEmits<{ |
| 44 | (e: "success"): void |
| 45 | (e: "cancel"): void |
| 46 | }>() |
| 47 | |
| 48 | const message = useMessage() |
| 49 | const authStore = useAuthStore() |
| 50 | const customerCode = computed(() => authStore.userCustomerCode) |
| 51 | const loading = ref(false) |
| 52 | const optionsLoading = ref(false) |
| 53 | const assignedToOptions = ref<{ label: string; value: string }[]>([]) |
| 54 | const statusOptions = [ |
| 55 | { label: "Open", value: "OPEN" }, |
| 56 | { label: "In Progress", value: "IN_PROGRESS" }, |
| 57 | { label: "Closed", value: "CLOSED" } |
| 58 | ] |
| 59 | const model = ref<CasePayload>({ |
| 60 | case_name: "", |
| 61 | case_description: "", |
| 62 | assigned_to: undefined, |
| 63 | case_status: "OPEN" |
| 64 | }) |
| 65 | |
| 66 | const isValidForm = computed( |
| 67 | () => |
| 68 | model.value.case_name.trim().length > 0 && |
| 69 | model.value.case_description.trim().length > 0 && |
| 70 | Boolean(model.value.case_status) |
| 71 | ) |
| 72 | |
| 73 | async function loadAssignedToOptions() { |
| 74 | optionsLoading.value = true |
| 75 | |
| 76 | try { |
| 77 | const response = await Api.cases.getCasesFilters() |
| 78 | assignedToOptions.value = response.data.assigned_to.map(o => ({ label: o, value: o })) |
| 79 | } catch (err) { |
| 80 | message.error(getApiErrorMessage(err as ApiError)) |
| 81 | } finally { |
| 82 | optionsLoading.value = false |
| 83 | } |
| 84 | } |
| 85 | |
| 86 | async function handleSubmit() { |
| 87 | if (!isValidForm.value || loading.value) return |
| 88 | |
| 89 | loading.value = true |
| 90 | |
| 91 | try { |
| 92 | const response = await Api.cases.createCase({ |
| 93 | case_name: model.value.case_name.trim(), |
| 94 | case_description: model.value.case_description.trim(), |
| 95 | assigned_to: model.value.assigned_to || undefined, |
| 96 | case_status: model.value.case_status, |
| 97 | customer_code: customerCode.value || undefined |
| 98 | }) |
| 99 | message.success(response.data.message || "Case created successfully") |
| 100 | emit("success") |
| 101 | } catch (err) { |
| 102 | message.error(getApiErrorMessage(err as ApiError)) |
| 103 | } finally { |
| 104 | loading.value = false |
| 105 | } |
| 106 | } |
| 107 | |
| 108 | onBeforeMount(() => { |
| 109 | loadAssignedToOptions() |
| 110 | }) |
| 111 | </script> |