@@ -13,18 +13,57 @@ import { Input } from "~/_components/ui/input";
1313import { DeleteTripButton } from "~/app/trips/_components/DeleteTripButton" ;
1414import { Label } from "~/_components/ui/label" ;
1515import { api } from "~/trpc/react" ;
16+ import { DatePicker } from "~/_components/ui/datepicker" ;
17+ import { Controller , useForm } from "react-hook-form" ;
18+ import * as yup from "yup" ;
19+ import { yupResolver } from "@hookform/resolvers/yup" ;
20+
21+ type FormData = {
22+ title : string ;
23+ destination : string ;
24+ startDate : Date ;
25+ endDate : Date ;
26+ } ;
1627
1728function TripDetailsCard ( { trip } : { trip : Trip } ) {
1829 const [ isEditing , setIsEditing ] = useState ( false ) ;
19- const [ formData , setFormData ] = useState < Partial < Trip > > ( trip ) ;
2030 const utils = api . useUtils ( ) ;
2131
22- const handleChange = (
23- e : React . ChangeEvent < HTMLInputElement | HTMLTextAreaElement > ,
24- ) => {
25- const { name, value } = e . target ;
26- setFormData ( ( prev ) => ( { ...prev , [ name ] : value } ) ) ;
27- } ;
32+ const validationSchema = yup . object ( {
33+ title : yup . string ( ) . required ( "Title is required" ) ,
34+ destination : yup . string ( ) . required ( "Destination is required" ) ,
35+ startDate : yup . date ( ) . required ( "Start Date is required" ) ,
36+ endDate : yup
37+ . date ( )
38+ . required ( "End Date is required" )
39+ . min ( yup . ref ( "startDate" ) , "End Date cannot be before Start Date" )
40+ . test (
41+ "max-trip-length" ,
42+ "Trip cannot be longer than 30 days" ,
43+ function ( value ) {
44+ const { startDate } = this . parent as { startDate ?: Date } ;
45+ if ( ! value || ! startDate ) return true ;
46+
47+ const diff =
48+ ( value . getTime ( ) - startDate . getTime ( ) ) / ( 1000 * 60 * 60 * 24 ) ;
49+ return diff <= 30 ;
50+ } ,
51+ ) ,
52+ } ) ;
53+
54+ const {
55+ control,
56+ handleSubmit,
57+ formState : { errors } ,
58+ } = useForm < FormData > ( {
59+ defaultValues : {
60+ title : trip . title ,
61+ destination : trip . destination ,
62+ startDate : trip . startDate ,
63+ endDate : trip . endDate ,
64+ } ,
65+ resolver : yupResolver ( validationSchema ) ,
66+ } ) ;
2867
2968 const updateTripDetails = api . trip . update . useMutation ( {
3069 onSuccess : async ( ) => {
@@ -35,8 +74,7 @@ function TripDetailsCard({ trip }: { trip: Trip }) {
3574 } ,
3675 } ) ;
3776
38- const handleSubmit = async ( e : React . FormEvent ) => {
39- e . preventDefault ( ) ;
77+ const onSubmit = async ( formData : FormData ) => {
4078 if ( ! trip ?. id ) return ;
4179
4280 updateTripDetails . mutate ( {
@@ -55,55 +93,61 @@ function TripDetailsCard({ trip }: { trip: Trip }) {
5593 < Card className = "w-full rounded-lg border bg-white text-black shadow-lg dark:border-gray-700 dark:bg-gray-800" >
5694 < CardContent >
5795 { isEditing ? (
58- < form onSubmit = { handleSubmit } className = "space-y-4" >
96+ < form onSubmit = { handleSubmit ( onSubmit ) } className = "space-y-4" >
5997 < div >
6098 < Label htmlFor = "title" > Title</ Label >
61- < Input
62- id = "title"
63- name = "title"
64- value = { formData . title ?? "" }
65- onChange = { handleChange }
66- />
99+ < Input id = "title" { ...control . register ( "title" ) } />
100+ </ div >
101+ < div >
102+ < Label htmlFor = "destination" > Destination</ Label >
103+ < Input id = "destination" { ...control . register ( "destination" ) } />
67104 </ div >
68105
69106 < div >
70107 < Label htmlFor = "destination" > Destination</ Label >
71- < Input
72- id = "destination"
73- name = "destination"
74- value = { formData . destination ?? "" }
75- onChange = { handleChange }
76- />
108+ < Input id = "destination" { ...control . register ( "destination" ) } />
77109 </ div >
78110
79111 < div className = "flex gap-4" >
80112 < div >
81113 < Label htmlFor = "startDate" > Start Date</ Label >
82- < Input
83- id = "startDate"
114+ < Controller
115+ control = { control }
84116 name = "startDate"
85- type = "date"
86- value = {
87- formData . startDate
88- ? new Date ( formData . startDate ) . toISOString ( ) . split ( "T" ) [ 0 ]
89- : ""
90- }
91- onChange = { handleChange }
117+ render = { ( { field } ) => (
118+ < DatePicker
119+ value = { field . value }
120+ onChange = { ( date ) => {
121+ field . onChange ( date ?. toISOString ( ) ?? "" ) ;
122+ } }
123+ />
124+ ) }
92125 />
126+ { errors . startDate && (
127+ < p className = "text-sm text-red-500" >
128+ { errors . startDate . message }
129+ </ p >
130+ ) }
93131 </ div >
94132 < div >
95133 < Label htmlFor = "endDate" > End Date</ Label >
96- < Input
97- id = "endDate"
134+ < Controller
135+ control = { control }
98136 name = "endDate"
99- type = "date"
100- value = {
101- formData . endDate
102- ? new Date ( formData . endDate ) . toISOString ( ) . split ( "T" ) [ 0 ]
103- : ""
104- }
105- onChange = { handleChange }
137+ render = { ( { field } ) => (
138+ < DatePicker
139+ value = { field . value }
140+ onChange = { ( date ) => {
141+ field . onChange ( date ?. toISOString ( ) ?? "" ) ;
142+ } }
143+ />
144+ ) }
106145 />
146+ { errors . endDate && (
147+ < p className = "text-sm text-red-500" >
148+ { errors . endDate . message }
149+ </ p >
150+ ) }
107151 </ div >
108152 </ div >
109153
0 commit comments