@@ -22,20 +22,32 @@ interface Props {
2222const GOOGLE_MAPS_LIBRARIES_ARRAY : Libraries = [ "places" ] ; //Add more to this array if you need to import more libraries from the API
2323const CHERRY_RED = "#A03145" ;
2424
25- export const BookingInteractiveArea = ( { initialViewMode = ViewMode . CALENDAR } : Props ) => {
25+ export const BookingInteractiveArea = ( {
26+ initialViewMode = ViewMode . CALENDAR ,
27+ } : Props ) => {
2628 const [ viewMode , setViewMode ] = useState < ViewMode > ( initialViewMode ) ;
2729 const [ showBookingModal , setShowBookingModal ] = useState < boolean > ( false ) ;
28- // eventually this loading state will be replacted with a tanstack mutation loading state
29- const [ loading , setLoading ] = useState < boolean > ( false ) ;
3030 const [ currentDate , setCurrentDate ] = useState < Date > ( new Date ( ) ) ;
3131 const [ isDayView , setIsDayView ] = useState < boolean > ( false ) ;
32- const [ validationAddressGood , setValidationAddressGood ] = useState < boolean > ( false ) ;
32+ const [ validationAddressGood , setValidationAddressGood ] =
33+ useState < boolean > ( false ) ;
3334
3435 const {
3536 data : bookings ,
3637 isLoading : isLoadingBookings ,
3738 isError : isErrorBookings ,
3839 } = api . bookings . getAll . useQuery ( ) ;
40+ const createBookingMutation = api . trip . create . useMutation ( {
41+ onSuccess : ( ) => {
42+ notify . success ( "Booking successfully created" ) ;
43+ form . reset ( ) ;
44+ setShowBookingModal ( false ) ;
45+ setValidationAddressGood ( false ) ;
46+ } ,
47+ onError : ( error ) => {
48+ notify . error ( error . message || "Failed to create a booking" ) ;
49+ } ,
50+ } ) ;
3951
4052 //Define a variable that react will reassign its value on runtime
4153 //Starts off as null but will equal (through inputElement.current) an HTML input element when assigned at runtime
@@ -64,12 +76,18 @@ export const BookingInteractiveArea = ({ initialViewMode = ViewMode.CALENDAR }:
6476
6577 validate : {
6678 title : ( value ) => ( value . trim ( ) . length > 0 ? null : "Title is required" ) ,
67- residentName : ( value ) => ( value . trim ( ) . length > 0 ? null : "Resident name is required" ) ,
68- contactInfo : ( value ) => ( value . trim ( ) . length > 0 ? null : "Contact info is required" ) ,
69- startTime : ( value ) => ( value . trim ( ) . length > 0 ? null : "Date and time is required" ) ,
70- endTime : ( value ) => ( value . trim ( ) . length > 0 ? null : "Date and time is required" ) ,
71- purpose : ( value ) => ( value . trim ( ) . length > 0 ? null : "Purpose is required" ) ,
72- pickupAddress : ( value ) => ( value . trim ( ) . length > 0 ? null : "Pickup address is required" ) ,
79+ residentName : ( value ) =>
80+ value . trim ( ) . length > 0 ? null : "Resident name is required" ,
81+ contactInfo : ( value ) =>
82+ value . trim ( ) . length > 0 ? null : "Contact info is required" ,
83+ startTime : ( value ) =>
84+ value . trim ( ) . length > 0 ? null : "Date and time is required" ,
85+ endTime : ( value ) =>
86+ value . trim ( ) . length > 0 ? null : "Date and time is required" ,
87+ purpose : ( value ) =>
88+ value . trim ( ) . length > 0 ? null : "Purpose is required" ,
89+ pickupAddress : ( value ) =>
90+ value . trim ( ) . length > 0 ? null : "Pickup address is required" ,
7391 destinationAddress : ( value ) =>
7492 value . trim ( ) . length > 0 ? null : "Destination address is required" ,
7593 } ,
@@ -79,14 +97,21 @@ export const BookingInteractiveArea = ({ initialViewMode = ViewMode.CALENDAR }:
7997 // biome-ignore lint: inputElement.current does change and needs to be changed in order for useEffect() to run
8098 useEffect ( ( ) => {
8199 //Do not run the code within the useEffect if the api or mantine form hasn't fully loaded
82- if ( ! isLoaded || google . maps . places === null || inputElement . current === null ) {
100+ if (
101+ ! isLoaded ||
102+ google . maps . places === null ||
103+ inputElement . current === null
104+ ) {
83105 return ;
84106 }
85107
86108 //Make the google auto complete element and attach it to inputElement (requires an HTML input element to mount to)
87- const googleAutoCompleteElement = new google . maps . places . Autocomplete ( inputElement . current , {
88- types : [ "address" ] ,
89- } ) ;
109+ const googleAutoCompleteElement = new google . maps . places . Autocomplete (
110+ inputElement . current ,
111+ {
112+ types : [ "address" ] ,
113+ }
114+ ) ;
90115
91116 //Places bounds on what locations google will suggest
92117 googleAutoCompleteElement . setComponentRestrictions ( {
@@ -122,54 +147,34 @@ export const BookingInteractiveArea = ({ initialViewMode = ViewMode.CALENDAR }:
122147 } ;
123148 } , [ inputElement . current ] ) ;
124149
125- const handleConfirm = async ( ) => {
126- setLoading ( true ) ;
150+ const handleConfirm = ( ) => {
127151 const validation = form . validate ( ) ;
128152 const hasErrors = Object . keys ( validation . errors ) . length > 0 ;
129153
130154 if ( hasErrors ) {
131155 notify . error ( "Please fix the errors in the form before submitting" ) ;
132- setLoading ( false ) ;
133156 return ;
134157 }
135158
136- const values = form . values ;
137- console . log ( "submit" , values ) ;
138-
139- // enter an actual api call here like a tanstack mutation
140- // when calling the backend to verify form and update the db, must manually pass the destination address
141- // do NOT use the mantine field form as it will not contain the destination address
142- // use instead -> [inputElement.current?.value || ""]
143- // see the below code for a sample usage of calling the backend to verify destination address
144- /*
145- //Define backend endpoint (if you need to directly call this api - probably won't though, put at the top of the file, under useStates)
146- const validateDestinationAddressAPI = api.form.validateDestinationAddress.useMutation();
147-
148- if (validationAddressGood) {
149- //Call the backend
150- const result = await validateDestinationAddressAPI.mutateAsync({
151- regionCode: "ca",
152- destinationAddress: [inputElement.current?.value || ""],
153- });
154-
155- //Evaluate the result from the back-end
156- if (result === null) {
157- //Manually adds an error field to the mantine form
158- form.setFieldError("destinationAddress", "Destination address is too vague");
159- } else{
160- // use -> result passed from funct call as address that goes into db
161- // update db with result var
162- console.log("Backend is happy with input");
163- }
164- }
165- */
159+ if ( ! validationAddressGood ) {
160+ form . setFieldError (
161+ "destinationAddress" ,
162+ "Please select a valid address from the dropdown"
163+ ) ;
164+ return ;
165+ }
166166
167- setTimeout ( ( ) => {
168- setLoading ( false ) ;
169- setShowBookingModal ( false ) ;
170- notify . success ( "Booking successfully created" ) ;
171- form . reset ( ) ;
172- } , 2000 ) ;
167+ createBookingMutation . mutate ( {
168+ title : form . values . title ,
169+ residentName : form . values . residentName ,
170+ contactInfo : form . values . contactInfo ,
171+ additionalInfo : form . values . additionalInfo ,
172+ pickupAddress : form . values . pickupAddress ,
173+ destinationAddress : inputElement . current ?. value || "" ,
174+ startTime : new Date ( form . values . startTime ) . toISOString ( ) ,
175+ endTime : new Date ( form . values . endTime ) . toISOString ( ) ,
176+ purpose : form . values . purpose ,
177+ } ) ;
173178 } ;
174179
175180 //If the script hasn't loaded yet, don't render anything until it does
@@ -228,7 +233,7 @@ export const BookingInteractiveArea = ({ initialViewMode = ViewMode.CALENDAR }:
228233 size = "xl"
229234 showDefaultFooter
230235 confirmText = "Confirm Booking"
231- loading = { loading }
236+ loading = { createBookingMutation . isPending }
232237 >
233238 < AgencyForm form = { form } destinationAddressRef = { inputElement } />
234239 </ Modal >
0 commit comments