11<script setup lang="ts">
22import { Head , usePage } from ' @inertiajs/vue3' ;
3- import { ref } from ' vue' ;
3+ import type { Form } from ' @nuxt/ui/runtime/types/form.d.ts' ;
4+ import { useForm } from ' laravel-precognition-vue-inertia' ;
5+ import { capitalize , computed , ref , useTemplateRef } from ' vue' ;
46
57const page = usePage ();
68
7- const animalTypes = ref <string []>(page .props .animalTypes );
8- const state = ref ({
9- client: {
10- name: ' ' ,
11- email: ' ' ,
12- },
13- animal: {
14- name: ' ' ,
15- type: ' ' ,
16- ageMonths: null ,
9+ const animalTypes = ref (page .props .animalTypes as string []);
10+ const timeOfDay = (page .props .timeOfDay as string []).map ((time ) => ({
11+ value: time ,
12+ label: capitalize (time ),
13+ }));
14+
15+ const form = useForm (
16+ ' post' ,
17+ route (' public.schedule-appointment' ),
18+ {
19+ client: {
20+ name: ' ' ,
21+ email: ' ' ,
22+ },
23+ animal: {
24+ name: ' ' ,
25+ type: ' ' ,
26+ ageMonths: null ,
27+ },
28+ appointment: {
29+ preferredDate: ' ' ,
30+ preferredTime: [],
31+ symptoms: ' ' ,
32+ },
1733 },
18- appointment: {
19- preferredDate: ' ' ,
20- preferredTime: [],
21- symptoms: ' ' ,
34+ {
35+ onFinish() {
36+ // Show errors after validating (using precognition) on the server
37+ showFormErrors ();
38+ },
2239 },
23- });
40+ );
41+ const formRef = useTemplateRef <Form <any >>(' formRef' );
42+ const formErrors = computed (() => Object .entries (form .errors ).map (([name , message ]) => ({ name , message })));
43+
44+ function showFormErrors() {
45+ formRef .value ?.setErrors (formErrors .value );
46+ }
47+
48+ function validateForm() {
49+ // Get touched fields from the Nuxt UI form
50+ const fields = Array .from (formRef .value ?.touchedFields as Set <string >);
51+ // Add the touched fields to the Laravel Precognition form
52+ form .touch (fields );
53+ // Validate on the server, using precognition
54+ form .validate ();
55+
56+ return formRef .value ?.getErrors () ?? []; // Don't overwrite the errors
57+ }
2458
25- function onCreateAnimalType(item : string ) {
26- console .log (' New animal type: ' + item );
59+ async function submitForm() {
60+ form .submit ({
61+ preserveScroll: true ,
62+ onError : () => {
63+ // Show errors after failed submission
64+ showFormErrors ();
65+ },
66+ });
67+ }
68+
69+ function createAnimalType(item : string ) {
2770 animalTypes .value .push (item );
28- state . value .animal .type = item ;
71+ form .animal .type = item ;
2972}
3073 </script >
3174
@@ -34,72 +77,106 @@ function onCreateAnimalType(item: string) {
3477
3578 <UApp >
3679 <UContainer class =" flex h-full flex-col items-center justify-center p-4 sm:p-6" >
37- <!-- Header -->
80+ <!-- Page header -->
3881 <UIcon name =" i-lucide-hospital" class =" mt-8 size-32" />
39- <h1 class =" mt-6 text-5xl" >{{ $ page.props.name }}</h1 >
82+ <h1 class =" mt-6 text-5xl" >{{ page.props.name }}</h1 >
4083
4184 <!-- Appointment card -->
4285 <UCard class =" mt-16 sm:p-4" >
86+ <!-- Appointment card: header-->
4387 <div class =" mt-6 mb-12 px-4 text-center sm:mt-0" >
4488 <h2 class =" text-3xl" >Schedule an appointment with us!</h2 >
4589 <p class =" mt-2" >We'll get back to you as soon as possible.</p >
4690 </div >
4791
48- <UForm :state =" state" class =" space-y-4" >
92+ <!-- Appointment card: form-->
93+ <UForm
94+ ref =" formRef"
95+ :state =" form"
96+ :validate =" validateForm"
97+ :validate-on =" ['change']"
98+ :validate-on-input-delay =" 0"
99+ :disabled =" form.processing"
100+ @submit =" submitForm"
101+ class =" space-y-4"
102+ >
103+ <!-- Client -->
49104 <p class =" mb-2 text-2xl/12" >About you</p >
50- <UFormField label =" Name" name =" name" required >
51- <UInput v-model =" state.client.name" placeholder =" Your name" class =" w-full" />
52- </UFormField >
53105
54- <UFormField label =" Email" name =" email" description =" Used to contact you about your appointment" required >
55- <UInput v-model =" state.client.email" type =" email" placeholder =" Your email" class =" w-full" />
106+ <!-- Client: name -->
107+ <UFormField label =" Name" name =" client.name" required >
108+ <UInput v-model =" form.client.name" placeholder =" Your name" class =" w-full" />
109+ </UFormField >
110+ <!-- Client: email-->
111+ <UFormField label =" Email" name =" client.email" description =" Used to contact you about your appointment" required >
112+ <UInput v-model =" form.client.email" type =" email" placeholder =" Your email" class =" w-full" />
56113 </UFormField >
57114
58115 <USeparator class =" mb-0 py-6" />
59116
117+ <!-- Pet -->
60118 <p class =" mb-2 pt-0 text-2xl/12" >About your pet</p >
61119 <div class =" flex flex-col gap-4 md:flex-row" >
62- <UFormField label =" Name" name =" name" required >
63- <UInput v-model =" state.animal.name" class =" w-full" placeholder =" Your pet's name" />
120+ <!-- Pet: name-->
121+ <UFormField label =" Name" name =" animal.name" required >
122+ <UInput v-model =" form.animal.name" class =" w-full" placeholder =" Your pet's name" />
64123 </UFormField >
65- <UFormField label =" Type" name =" type" required >
124+ <!-- Pet: type -->
125+ <UFormField label =" Type" name =" animal.type" required >
66126 <UInputMenu
67- v-model =" state .animal.type"
127+ v-model =" form .animal.type"
68128 placeholder =" Your pet type"
69129 create-item =" always"
70130 :items =" animalTypes"
71- @create =" onCreateAnimalType "
131+ @create =" createAnimalType "
72132 class =" w-full"
73133 />
74134 </UFormField >
75- <UFormField label =" Age" name =" age" hint =" in months" required >
76- <UInput v-model =" state.animal.ageMonths" class =" w-full" placeholder =" Your pet's age (in months)" />
135+ <!-- Pet: age -->
136+ <UFormField label =" Age" name =" animal.ageMonths" hint =" in months" required >
137+ <UInput v-model =" form.animal.ageMonths" class =" w-full" placeholder =" Your pet's age (in months)" />
77138 </UFormField >
78139 </div >
79140
80141 <USeparator class =" mb-0 py-6" />
81142
143+ <!-- Appointment -->
82144 <p class =" mb-2 pt-0 text-2xl/12" >About your visit</p >
83145 <div class =" flex flex-col gap-4 md:flex-row" >
84146 <div class =" space-y-2 md:w-1/3" >
85- <UFormField label =" Preferred Time" name =" preferred_date" required >
86- <UInput v-model =" state.appointment.preferredDate" type =" date" class =" w-full" />
147+ <!-- Appointment: date + time -->
148+ <UFormField label =" Preferred Time" name =" appointment.preferredDate" required >
149+ <UInput v-model =" form.appointment.preferredDate" type =" date" class =" w-full" />
150+ </UFormField >
151+ <UFormField name =" appointment.preferredTime" >
152+ <UCheckboxGroup
153+ v-model =" form.appointment.preferredTime"
154+ orientation =" horizontal"
155+ name =" appointment.preferredTime"
156+ :items =" timeOfDay"
157+ />
87158 </UFormField >
88- <UCheckboxGroup
89- v-model =" state.appointment.preferredTime"
90- orientation =" horizontal"
91- name =" preferred_time"
92- :items =" ['Morning', 'Afternoon']"
93- required
94- />
95159 </div >
96- <UFormField label =" Symptoms" name =" date" class =" w-full md:w-2/3" required >
97- <UTextarea v-model =" state.appointment.symptoms" placeholder =" Why are you visiting us?" rows =" 4" class =" w-full" />
160+ <!-- Appointment: symptoms-->
161+ <UFormField label =" Symptoms" name =" appointment.symptoms" class =" w-full md:w-2/3" required >
162+ <UTextarea v-model =" form.appointment.symptoms" placeholder =" Why are you visiting us?" :rows =" 4" class =" w-full" />
98163 </UFormField >
99164 </div >
100165
101- <div class =" mt-12 text-right" >
102- <UButton type =" submit" label =" Schedule appointment" color =" primary" size =" lg" trailing-icon =" i-lucide-paw-print" />
166+ <!-- Form: controls -->
167+ <div class =" mt-12 flex flex-col-reverse items-center justify-end gap-3 sm:flex-row" >
168+ <div v-if =" form.hasErrors" class =" text-sm text-red-400" >Please review and fix issues</div >
169+ <UButton
170+ type =" submit"
171+ :disabled =" form.hasErrors"
172+ :loading =" form.processing"
173+ color =" primary"
174+ size =" lg"
175+ icon =" i-lucide-paw-print"
176+ trailing
177+ >
178+ Schedule appointment
179+ </UButton >
103180 </div >
104181 </UForm >
105182 </UCard >
0 commit comments