main
vue 264 lines 6.12 KB
Raw
1 <template>
2 <n-spin :show="loading" class="customer-form">
3 <n-form ref="formRef" :label-width="80" :model="form" :rules>
4 <div class="flex flex-col gap-4">
5 <h4>Edit Customer</h4>
6 <div class="flex flex-wrap gap-4">
7 <div v-for="(val, key) of fieldsMeta" :key class="grow">
8 <n-form-item :label="val.label" :path="key" class="grow">
9 <n-input
10 v-model:value.trim="form[key]"
11 :placeholder="val.placeholder"
12 clearable
13 :readonly="key === 'customer_code' && lockCode"
14 :disabled="key === 'customer_code' && lockCode"
15 />
16 </n-form-item>
17 </div>
18 </div>
19 <div class="flex justify-between gap-4">
20 <div class="flex gap-4">
21 <slot name="additionalActions"></slot>
22 </div>
23 <div class="flex gap-4">
24 <n-button :disabled="loading" @click="reset()">Reset</n-button>
25 <n-button type="primary" :disabled="!isValid" :loading @click="validate()">Submit</n-button>
26 </div>
27 </div>
28 </div>
29 </n-form>
30 </n-spin>
31 </template>
32
33 <script setup lang="ts">
34 // TODO-FE: refactor
35 import type { FormInst, FormItemRule, FormRules, FormValidationError } from "naive-ui"
36 import type { Customer } from "@/types/customers.d"
37 import _get from "lodash/get"
38 import _trim from "lodash/trim"
39 import { NButton, NForm, NFormItem, NInput, NSpin, useMessage } from "naive-ui"
40 import { computed, onBeforeMount, onMounted, ref, toRefs, watch } from "vue"
41 import Api from "@/api"
42
43 const props = defineProps<{
44 customer?: Customer
45 resetOnSubmit?: boolean
46 /** lock customer_code on reset and editing (readonly) */
47 lockCode?: boolean
48 }>()
49
50 const emit = defineEmits<{
51 (e: "update:loading", value: boolean): void
52 (e: "submitted", value: Customer): void
53 (
54 e: "mounted",
55 value: {
56 reset: () => void
57 }
58 ): void
59 }>()
60
61 const { customer, resetOnSubmit, lockCode } = toRefs(props)
62
63 const loading = ref(false)
64 const message = useMessage()
65 const form = ref<Customer>(getClearForm())
66 const formRef = ref<FormInst | null>(null)
67
68 const CUSTOMER_CODE_REGEX = /^[a-z]+$/
69
70 const rules: FormRules = {
71 customer_code: {
72 required: true,
73 message: "Please input code. Code must be all lowercase and contain no spaces or special characters.",
74 trigger: ["input", "blur"],
75 validator: (_rule: FormItemRule, value: string) => {
76 if (value !== value.toLowerCase()) {
77 return new Error("Code must be all lowercase")
78 } else if (!CUSTOMER_CODE_REGEX.test(value)) {
79 return new Error("Code must not contain spaces or special characters")
80 } else {
81 return true
82 }
83 }
84 },
85 customer_name: {
86 required: true,
87 message: "Please input name",
88 trigger: ["input", "blur"]
89 },
90 contact_last_name: {
91 required: true,
92 message: "Please input last name",
93 trigger: ["input", "blur"]
94 },
95 contact_first_name: {
96 required: true,
97 message: "Please input first name",
98 trigger: ["input", "blur"]
99 }
100 }
101
102 const fieldsMeta = {
103 customer_code: {
104 label: "Code",
105 placeholder: "Unique code for the customer"
106 },
107 customer_name: {
108 label: "Name",
109 placeholder: "Name of the customer"
110 },
111 contact_last_name: {
112 label: "Last name",
113 placeholder: "Last name of the contact"
114 },
115 contact_first_name: {
116 label: "First name",
117 placeholder: "First name of the contact"
118 },
119 parent_customer_code: {
120 label: "Parent Customer Code",
121 placeholder: "Code for the parent customer"
122 },
123 phone: {
124 label: "Phone number",
125 placeholder: "Phone number"
126 },
127 address_line1: {
128 label: "First line address",
129 placeholder: "First line of the address"
130 },
131 address_line2: {
132 label: "Second line address",
133 placeholder: "Second line of the address"
134 },
135 city: {
136 label: "City",
137 placeholder: "City"
138 },
139 state: {
140 label: "State",
141 placeholder: "State"
142 },
143 postal_code: {
144 label: "Postal Code",
145 placeholder: "Postal Code"
146 },
147 country: {
148 label: "Country",
149 placeholder: "Country"
150 },
151 customer_type: {
152 label: "Type",
153 placeholder: "Type of the customer"
154 },
155 logo_file: {
156 label: "Logo",
157 placeholder: "Logo file for the customer"
158 }
159 }
160
161 const isValid = computed(() => {
162 let valid = true
163
164 for (const key in rules) {
165 const rule = rules[key] as FormRules
166
167 if (rule.required && !_trim(_get(form.value, key))) {
168 valid = false
169 }
170 }
171
172 return valid
173 })
174
175 function validate() {
176 if (!formRef.value) return
177
178 formRef.value.validate((errors?: Array<FormValidationError>) => {
179 if (!errors) {
180 submit()
181 } else {
182 message.warning("You must fill in the required fields correctly.")
183 return false
184 }
185 })
186 }
187
188 function getClearForm(customer?: Partial<Customer>) {
189 return {
190 customer_code: customer?.customer_code || "",
191 customer_name: customer?.customer_name || "",
192 contact_last_name: customer?.contact_last_name || "",
193 contact_first_name: customer?.contact_first_name || "",
194 parent_customer_code: customer?.parent_customer_code || "",
195 phone: customer?.phone || "",
196 address_line1: customer?.address_line1 || "",
197 address_line2: customer?.address_line2 || "",
198 city: customer?.city || "",
199 state: customer?.state || "",
200 postal_code: customer?.postal_code || "",
201 country: customer?.country || "",
202 customer_type: customer?.customer_type || "",
203 logo_file: customer?.logo_file || ""
204 }
205 }
206
207 function reset() {
208 let fields
209 if (lockCode.value) {
210 fields = { customer_code: customer.value?.customer_code || "" }
211 }
212 form.value = getClearForm(fields)
213 }
214
215 function submit() {
216 loading.value = true
217
218 const method = customer.value?.customer_code ? "updateCustomer" : "createCustomer"
219
220 Api.customers[method](form.value, customer.value?.customer_code)
221 .then(res => {
222 if (res.data.success) {
223 emit("submitted", res.data.customer)
224 if (resetOnSubmit.value) {
225 reset()
226 }
227 } else {
228 message.warning(res.data?.message || "An error occurred. Please try again later.")
229 }
230 })
231 .catch(err => {
232 message.error(err.response?.data?.message || "An error occurred. Please try again later.")
233 })
234 .finally(() => {
235 loading.value = false
236 })
237 }
238
239 function setForm() {
240 form.value = getClearForm(customer.value)
241 }
242
243 watch(loading, val => {
244 emit("update:loading", val)
245 })
246
247 watch(customer, val => {
248 if (val) {
249 setForm()
250 }
251 })
252
253 onBeforeMount(() => {
254 if (customer.value) {
255 setForm()
256 }
257 })
258
259 onMounted(() => {
260 emit("mounted", {
261 reset
262 })
263 })
264 </script>