Skip to content

Commit 4164c60

Browse files
authored
Merge pull request #34 from OzPol/OzPol-7.24
Oz pol 7.24
2 parents c8bcf2e + f563f12 commit 4164c60

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

48 files changed

+1636
-655
lines changed

app/layout.tsx

+1-3
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,4 @@ export default function RootLayout({
2020
<body className={inter.className}>{children}</body>
2121
</html>
2222
);
23-
}
24-
25-
23+
}

components/AvailabilityCalendar.tsx

+17-8
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,30 @@
1-
// ./components/AvailabilityCalendar.tsx
2-
3-
import React, { useState } from 'react';
1+
import React, { useEffect, useState } from 'react';
42
import Calendar, { CalendarProps } from 'react-calendar';
53
import 'react-calendar/dist/Calendar.css';
64

7-
const AvailabilityCalendar = ({ availableDates }: { availableDates: Date[] }) => {
5+
interface AvailabilityCalendarProps {
6+
availableDates: Date[];
7+
onDateChange?: (date: Date) => void;
8+
isProvider?: boolean;
9+
}
10+
11+
const AvailabilityCalendar: React.FC<AvailabilityCalendarProps> = ({ availableDates, onDateChange, isProvider }) => {
812
const [selectedDate, setSelectedDate] = useState<Date | null>(null);
913

14+
useEffect(() => {
15+
if (availableDates.length > 0) {
16+
setSelectedDate(availableDates[0]);
17+
}
18+
}, [availableDates]);
19+
1020
const isAvailable = (date: Date) => {
1121
return availableDates.some(availableDate => availableDate.toDateString() === date.toDateString());
1222
};
1323

14-
const handleDateChange: CalendarProps['onChange'] = (date, event) => {
15-
if (Array.isArray(date)) {
16-
setSelectedDate(date[0]);
17-
} else {
24+
const handleDateChange: CalendarProps['onChange'] = (date) => {
25+
if (date instanceof Date) {
1826
setSelectedDate(date);
27+
if (onDateChange) onDateChange(date);
1928
}
2029
};
2130

components/BookingForm.tsx

+144-46
Original file line numberDiff line numberDiff line change
@@ -1,60 +1,158 @@
1-
// components/BookingForm.tsx
1+
// ./components/BookingForm.tsx
22

3-
'use client';
4-
5-
import { zodResolver } from '@hookform/resolvers/zod';
6-
import { useForm } from 'react-hook-form';
3+
import React, { useState, useEffect } from 'react';
4+
import { databases } from '../lib/appwrite.config';
5+
import { BookingFormProps } from '../types/appwrite.type';
76
import { useRouter } from 'next/router';
8-
import { z } from 'zod';
9-
import { getBookingSchema } from '../lib/validation';
10-
import CustomFormField, { FormFieldType } from './CustomFormField';
11-
import SubmitButton from './SubmitButton';
12-
import { createBooking } from '../lib/booking.actions';
137

14-
const BookingForm = () => {
8+
const BookingForm: React.FC<BookingFormProps> = ({ providerId, serviceId, selectedDate, onSubmit }) => {
9+
const [address, setAddress] = useState('');
10+
const [city, setCity] = useState('');
11+
const [state, setState] = useState('');
12+
const [zipcode, setZipcode] = useState('');
13+
const [coupon, setCoupon] = useState('');
14+
const [discount, setDiscount] = useState(0);
1515
const router = useRouter();
16-
const form = useForm<z.infer<ReturnType<typeof getBookingSchema>>>({
17-
resolver: zodResolver(getBookingSchema('create')),
18-
defaultValues: {
19-
service: '',
20-
date: new Date(),
21-
time: '',
22-
},
23-
});
24-
25-
const onSubmit = async (values: z.infer<ReturnType<typeof getBookingSchema>>) => {
16+
const consumerId = JSON.parse(localStorage.getItem('appwriteSession') || '{}').userId;
17+
18+
useEffect(() => {
19+
const fetchServiceDetails = async () => {
20+
try {
21+
const service = await databases.getDocument(
22+
process.env.DATABASE_ID!,
23+
process.env.SERVICE_COLLECTION_ID!,
24+
serviceId
25+
);
26+
setDiscount(service.price);
27+
} catch (error) {
28+
console.error('Error fetching service details:', error);
29+
}
30+
};
31+
32+
fetchServiceDetails();
33+
}, [serviceId]);
34+
35+
const handleCouponApply = async () => {
36+
if (coupon === '100OFF') {
37+
setDiscount(100);
38+
} else {
39+
setDiscount(0);
40+
}
41+
};
42+
43+
const handleFormSubmit = async (e: React.FormEvent) => {
44+
e.preventDefault();
45+
if (!selectedDate) {
46+
alert("Please select a date for booking.");
47+
return;
48+
}
49+
50+
const formData = {
51+
date: selectedDate.toISOString(),
52+
consumerId,
53+
providerId,
54+
serviceId,
55+
status: 'pending',
56+
address,
57+
city,
58+
state,
59+
zipcode,
60+
discount
61+
};
62+
2663
try {
27-
await createBooking(values);
28-
router.push('/customerProfile'); // Redirect after booking
64+
const response = await fetch('/api/bookings/create', {
65+
method: 'POST',
66+
headers: {
67+
'Content-Type': 'application/json',
68+
},
69+
body: JSON.stringify(formData),
70+
});
71+
72+
if (response.ok) {
73+
const responseData = await response.json();
74+
router.push(`/payment-confirmation?bookingId=${responseData.bookingId}`);
75+
} else {
76+
console.error('Failed to create booking.');
77+
}
2978
} catch (error) {
3079
console.error('Error creating booking:', error);
3180
}
3281
};
3382

3483
return (
35-
<form onSubmit={form.handleSubmit(onSubmit)} className="space-y-6">
36-
<CustomFormField
37-
fieldType={FormFieldType.INPUT}
38-
control={form.control}
39-
name="service"
40-
label="Service"
41-
placeholder="Service"
42-
/>
43-
<CustomFormField
44-
fieldType={FormFieldType.DATE_PICKER}
45-
control={form.control}
46-
name="date"
47-
label="Date"
48-
dateFormat="MM/dd/yyyy"
49-
/>
50-
<CustomFormField
51-
fieldType={FormFieldType.INPUT}
52-
control={form.control}
53-
name="time"
54-
label="Time"
55-
placeholder="Time"
56-
/>
57-
<SubmitButton isLoading={false}>Create Booking</SubmitButton>
84+
<form onSubmit={handleFormSubmit} className="mt-4">
85+
<div>
86+
<label className="block text-sm font-medium text-gray-700">Date</label>
87+
<input
88+
type="text"
89+
value={selectedDate.toDateString()}
90+
readOnly
91+
className="mt-1 block w-1/2 sm:w-1/3 lg:w-1/4 border border-gray-300 rounded-md shadow-sm p-2"
92+
/>
93+
</div>
94+
<div className="mt-2">
95+
<label className="block text-sm font-medium text-gray-700">Address</label>
96+
<input
97+
type="text"
98+
value={address}
99+
onChange={(e) => setAddress(e.target.value)}
100+
className="mt-1 block w-1/2 sm:w-1/3 lg:w-1/4 border border-gray-300 rounded-md shadow-sm p-2"
101+
required
102+
/>
103+
</div>
104+
<div className="mt-2">
105+
<label className="block text-sm font-medium text-gray-700">City</label>
106+
<input
107+
type="text"
108+
value={city}
109+
onChange={(e) => setCity(e.target.value)}
110+
className="mt-1 block w-1/2 sm:w-1/3 lg:w-1/4 border border-gray-300 rounded-md shadow-sm p-2"
111+
required
112+
/>
113+
</div>
114+
<div className="mt-2">
115+
<label className="block text-sm font-medium text-gray-700">State</label>
116+
<input
117+
type="text"
118+
value={state}
119+
onChange={(e) => setState(e.target.value)}
120+
className="mt-1 block w-1/2 sm:w-1/3 lg:w-1/4 border border-gray-300 rounded-md shadow-sm p-2"
121+
required
122+
/>
123+
</div>
124+
<div className="mt-2">
125+
<label className="block text-sm font-medium text-gray-700">Zipcode</label>
126+
<input
127+
type="text"
128+
value={zipcode}
129+
onChange={(e) => setZipcode(e.target.value)}
130+
className="mt-1 block w-1/2 sm:w-1/3 lg:w-1/4 border border-gray-300 rounded-md shadow-sm p-2"
131+
required
132+
/>
133+
</div>
134+
<div className="mt-2">
135+
<label className="block text-sm font-medium text-gray-700">Coupon Code</label>
136+
<input
137+
type="text"
138+
value={coupon}
139+
onChange={(e) => setCoupon(e.target.value)}
140+
className="mt-1 block w-1/2 sm:w-1/3 lg:w-1/4 border border-gray-300 rounded-md shadow-sm p-2"
141+
/>
142+
<button
143+
type="button"
144+
onClick={handleCouponApply}
145+
className="ml-2 bg-green-500 text-white py-1 px-2 rounded"
146+
>
147+
Apply
148+
</button>
149+
</div>
150+
<button
151+
type="submit"
152+
className="mt-4 bg-blue-500 text-white py-2 px-4 rounded"
153+
>
154+
Book Now
155+
</button>
58156
</form>
59157
);
60158
};

components/CustomerAccountDetails.tsx

+1-1
Original file line numberDiff line numberDiff line change
@@ -125,4 +125,4 @@ const CustomerAccountDetails: React.FC = () => {
125125
);
126126
};
127127

128-
export default CustomerAccountDetails;
128+
export default CustomerAccountDetails;

components/CustomerContent.tsx

+19
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
// components/CustomerContent.tsx
2+
import Link from 'next/link';
3+
4+
const CustomerContent = () => {
5+
return (
6+
<div className="flex flex-col items-center space-y-4 p-8 bg-white bg-opacity-80 rounded-md shadow-lg md:w-3/2">
7+
<h1 className="text-4xl font-bold mb-2">Looking for inspirations?!</h1>
8+
<h2 className="text-2xl mb-6">Check out our latest services and offers tailored just for you.</h2>
9+
<Link href="/popular-services">
10+
<span className="w-full bg-blue-500 text-white py-3 px-6 rounded-lg text-center hover:bg-blue-600">
11+
View Popular Services in Your Area
12+
</span>
13+
</Link>
14+
{/* Add more customer-specific content or components here */}
15+
</div>
16+
);
17+
};
18+
19+
export default CustomerContent;

components/CustomerSearchServices.tsx

+10-2
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,12 @@ import { Service } from '../types/appwrite.type';
66
import { fetchAllServices } from './DataServiceConsumer';
77
import { FaSearch } from 'react-icons/fa';
88

9-
const CustomerSearchServices: React.FC = () => {
9+
// Define the prop type for CustomerSearchServices
10+
interface CustomerSearchServicesProps {
11+
onServiceClick: React.Dispatch<React.SetStateAction<Service | null>>;
12+
}
13+
14+
const CustomerSearchServices: React.FC<CustomerSearchServicesProps> = ({ onServiceClick }) => {
1015
const [services, setServices] = useState<Service[]>([]);
1116
const [filteredServices, setFilteredServices] = useState<Service[]>([]);
1217
const [selectedService, setSelectedService] = useState<Service | null>(null);
@@ -225,7 +230,10 @@ const CustomerSearchServices: React.FC = () => {
225230
providerIcon={'/assets/DefaultProviderProfile.jpeg'}
226231
rating={parseFloat(calculateAverageRating(service.ratings).toFixed(1))}
227232
imageUrl={service.imageUrl}
228-
onClick={() => setSelectedService(service)} // Set the selected service on click
233+
onClick={() => {
234+
setSelectedService(service); // Set the selected service on click
235+
onServiceClick(service); // Notify the parent component of the selected service
236+
}}
229237
onProviderClick={() => setSelectedProvider(service.providerId)} // Set the selected provider on click
230238
/>
231239
))}

0 commit comments

Comments
 (0)