6
6
</div >
7
7
8
8
<div v-if =" contact" class =" flex justify-center space-y-4 w-full" >
9
- <!-- Card -->
10
9
<div class =" flex flex-col w-full" >
11
10
<div class =" h-16" ></div >
12
11
13
12
<div class =" flex flex-col space-y-2" >
14
- <!-- Avatar with upload-->
15
13
<AvatarUpload
16
14
@upload =" onUpload"
17
15
@remove =" onRemove"
45
43
</div >
46
44
47
45
<div class =" mt-12" >
48
- <form @submit.prevent =" onSubmit" class =" space-y-8" >
49
- <div class =" flex flex-wrap gap-6" >
50
- <div class =" flex-1" >
51
- <FormField v-slot =" { componentField }" name =" first_name" >
52
- <FormItem class =" flex flex-col" >
53
- <FormLabel class =" flex items-center" >
54
- {{ t('form.field.firstName') }}
55
- </FormLabel >
56
- <FormControl >
57
- <Input v-bind =" componentField" type =" text" />
58
- </FormControl >
59
- <FormMessage />
60
- </FormItem >
61
- </FormField >
62
- </div >
63
-
64
- <div class =" flex-1" >
65
- <FormField v-slot =" { componentField }" name =" last_name" class =" flex-1" >
66
- <FormItem class =" flex flex-col" >
67
- <FormLabel class =" flex items-center" >
68
- {{ t('form.field.lastName') }}
69
- </FormLabel >
70
- <FormControl >
71
- <Input v-bind =" componentField" type =" text" />
72
- </FormControl >
73
- <FormMessage />
74
- </FormItem >
75
- </FormField >
76
- </div >
77
- </div >
78
-
79
- <FormField v-slot =" { componentField }" name =" avatar_url" >
80
- <FormItem >
81
- <FormControl >
82
- <Input v-bind =" componentField" type =" hidden" />
83
- </FormControl >
84
- </FormItem >
85
- </FormField >
86
-
87
- <div class =" flex flex-wrap gap-6" >
88
- <div class =" flex-1" >
89
- <FormField v-slot =" { componentField }" name =" email" >
90
- <FormItem class =" flex flex-col" >
91
- <FormLabel class =" flex items-center" >
92
- {{ t('form.field.email') }}
93
- </FormLabel >
94
- <FormControl >
95
- <Input v-bind =" componentField" type =" email" />
96
- </FormControl >
97
- <FormMessage />
98
- </FormItem >
99
- </FormField >
100
- </div >
101
-
102
- <div class =" flex flex-col flex-1" >
103
- <div class =" flex items-end" >
104
- <FormField v-slot =" { componentField }" name =" phone_number_calling_code" >
105
- <FormItem class =" w-20" >
106
- <FormLabel class =" flex items-center whitespace-nowrap" >
107
- {{ t('form.field.phoneNumber') }}
108
- </FormLabel >
109
- <FormControl >
110
- <ComboBox
111
- v-bind =" componentField"
112
- :items =" allCountries"
113
- :placeholder =" t('form.field.select')"
114
- :buttonClass =" 'rounded-r-none border-r-0'"
115
- >
116
- <template #item =" { item } " >
117
- <div class =" flex items-center gap-2" >
118
- <div class =" w-7 h-7 flex items-center justify-center" >
119
- <span v-if =" item.emoji" >{{ item.emoji }}</span >
120
- </div >
121
- <span class =" text-sm" >{{ item.label }} ( {{ item.value }})</span >
122
- </div >
123
- </template >
124
-
125
- <template #selected =" { selected } " >
126
- <div class =" flex items-center mb-1" >
127
- <span v-if =" selected" class =" text-xl leading-none" >
128
- {{ selected.emoji }}
129
- </span >
130
- </div >
131
- </template >
132
- </ComboBox >
133
- </FormControl >
134
- <FormMessage />
135
- </FormItem >
136
- </FormField >
137
-
138
- <div class =" flex-1" >
139
- <FormField v-slot =" { componentField }" name =" phone_number" >
140
- <FormItem class =" relative" >
141
- <FormControl >
142
- <!-- Input field -->
143
- <Input
144
- type =" tel"
145
- v-bind =" componentField"
146
- class =" rounded-l-none"
147
- inputmode =" numeric"
148
- />
149
- <FormMessage class =" absolute top-full mt-1 text-sm" />
150
- </FormControl >
151
- </FormItem >
152
- </FormField >
153
- </div >
154
- </div >
155
- </div >
156
- </div >
157
-
158
- <div >
159
- <Button type =" submit" :isLoading =" formLoading" :disabled =" formLoading" >
160
- {{
161
- $t('globals.buttons.update', {
162
- name: $t('globals.terms.contact').toLowerCase()
163
- })
164
- }}
165
- </Button >
166
- </div >
167
- </form >
46
+ <ContactForm :formLoading =" formLoading" :onSubmit =" onSubmit" />
168
47
</div >
169
48
</div >
170
49
</div >
171
50
172
- <!-- Loading state -->
173
51
<Spinner v-else />
174
52
175
- <!-- Block/Unblock confirmation dialog -->
176
53
<Dialog :open =" showBlockConfirmation" @update:open =" showBlockConfirmation = $event" >
177
54
<DialogContent class =" sm:max-w-md" >
178
55
<DialogHeader >
179
- <DialogTitle >{{
180
- contact?.enabled
181
- ? t('globals.buttons.block', {
182
- name: t('globals.terms.contact')
183
- })
184
- : t('globals.buttons.unblock', {
185
- name: t('globals.terms.contact')
186
- })
187
- }}</DialogTitle >
56
+ <DialogTitle >
57
+ {{
58
+ contact?.enabled
59
+ ? t('globals.buttons.block', { name: t('globals.terms.contact') })
60
+ : t('globals.buttons.unblock', { name: t('globals.terms.contact') })
61
+ }}
62
+ </DialogTitle >
188
63
<DialogDescription >
189
64
{{ contact?.enabled ? t('contact.blockConfirm') : t('contact.unblockConfirm') }}
190
65
</DialogDescription >
@@ -215,8 +90,6 @@ import { useForm } from 'vee-validate'
215
90
import { toTypedSchema } from ' @vee-validate/zod'
216
91
import { AvatarUpload } from ' @/components/ui/avatar'
217
92
import { Button } from ' @/components/ui/button'
218
- import { FormField , FormItem , FormLabel , FormControl , FormMessage } from ' @/components/ui/form'
219
- import { Input } from ' @/components/ui/input'
220
93
import {
221
94
Dialog ,
222
95
DialogContent ,
@@ -227,44 +100,31 @@ import {
227
100
import { ShieldOffIcon , ShieldCheckIcon } from ' lucide-vue-next'
228
101
import ContactDetail from ' @/layouts/contact/ContactDetail.vue'
229
102
import api from ' @/api'
230
- import { createFormSchema } from ' ./formSchema.js'
103
+ import ContactForm from ' @/features/contact/ContactForm.vue'
104
+ import { createFormSchema } from ' @/features/contact/formSchema.js'
231
105
import { useEmitter } from ' @/composables/useEmitter'
232
106
import { EMITTER_EVENTS } from ' @/constants/emitterEvents'
233
107
import { handleHTTPError } from ' @/utils/http'
234
108
import { CustomBreadcrumb } from ' @/components/ui/breadcrumb'
235
109
import { Spinner } from ' @/components/ui/spinner'
236
- import ComboBox from ' @/components/ui/combobox/ComboBox.vue'
237
- import countries from ' @/constants/countries.js'
238
110
239
111
const { t } = useI18n ()
240
112
const emitter = useEmitter ()
241
113
const route = useRoute ()
242
114
const formLoading = ref (false )
243
115
const contact = ref (null )
244
116
const showBlockConfirmation = ref (false )
117
+
245
118
const form = useForm ({
246
119
validationSchema: toTypedSchema (createFormSchema (t))
247
120
})
248
121
249
- const allCountries = countries .map ((country ) => ({
250
- label: country .name ,
251
- value: country .calling_code ,
252
- emoji: country .emoji
253
- }))
254
-
255
122
const breadcrumbLinks = [
256
123
{ path: ' contacts' , label: t (' globals.terms.contact' , 2 ) },
257
- {
258
- path: ' ' ,
259
- label: t (' globals.messages.edit' , {
260
- name: t (' globals.terms.contact' )
261
- })
262
- }
124
+ { path: ' ' , label: t (' globals.messages.edit' , { name: t (' globals.terms.contact' ) }) }
263
125
]
264
126
265
- onMounted (() => {
266
- fetchContact ()
267
- })
127
+ onMounted (fetchContact)
268
128
269
129
async function fetchContact () {
270
130
try {
@@ -278,9 +138,8 @@ async function fetchContact() {
278
138
279
139
const getInitials = computed (() => {
280
140
if (! contact .value ) return ' '
281
- const firstName = contact .value .first_name || ' '
282
- const lastName = contact .value .last_name || ' '
283
- return ` ${ firstName .charAt (0 ).toUpperCase ()}${ lastName .charAt (0 ).toUpperCase ()} `
141
+ const { first_name = ' ' , last_name = ' ' } = contact .value
142
+ return ` ${ first_name .charAt (0 ).toUpperCase ()}${ last_name .charAt (0 ).toUpperCase ()} `
284
143
})
285
144
286
145
async function confirmToggleBlock () {
@@ -305,9 +164,7 @@ async function toggleBlock() {
305
164
const onSubmit = form .handleSubmit (async (values ) => {
306
165
try {
307
166
formLoading .value = true
308
- await api .updateContact (contact .value .id , {
309
- ... values
310
- })
167
+ await api .updateContact (contact .value .id , { ... values })
311
168
await fetchContact ()
312
169
emitToast (t (' globals.messages.updatedSuccessfully' , { name: t (' globals.terms.contact' ) }))
313
170
} catch (err) {
0 commit comments