Skip to content

Oz pol 7.24 #34

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 26 commits into from
Aug 7, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
26 commits
Select commit Hold shift + click to select a range
3a35b40
Pulled the difference from main, and extended the appwrite schema to …
OzPol Jul 29, 2024
7e22a37
-Working on Booking Implementation. Modified the Layout.tsx files to …
OzPol Jul 30, 2024
8e89264
-img warning fixed
OzPol Jul 30, 2024
1d631b6
Image Warning Fixed
OzPol Jul 30, 2024
b68f0af
- Removed 'pages/providerProfile/[providerId].tsx' as part of an atte…
OzPol Jul 30, 2024
8f37965
- Implemented provider availability setting with multiple date select…
OzPol Jul 31, 2024
859dc09
-Compile Error fix
OzPol Jul 31, 2024
d35b28e
- Fixed Error :
OzPol Jul 31, 2024
5dc233a
-Provider View Availability
OzPol Jul 31, 2024
987facb
- Compiler Error
OzPol Jul 31, 2024
f460ec8
- Deleted the unused page
OzPol Jul 31, 2024
be075f8
Merge remote-tracking branch 'origin/main' into OzPol-7.24
OzPol Aug 6, 2024
10224c9
Merging origin/main into Ozpol-7.24
OzPol Aug 6, 2024
6472e21
-Webpack Error due to useState in Footer Fixed - Merging main in to 7.24
OzPol Aug 6, 2024
c8975e8
- Fixing Type error
OzPol Aug 6, 2024
ed423ce
- Fixing another type error encountered after conflict resolution.
OzPol Aug 6, 2024
25dba92
feat: Implement booking functionality with availability fetching and …
OzPol Aug 6, 2024
d35ebc8
Fixed BookingForm props and form rendering issues
OzPol Aug 6, 2024
0f5d497
feat: Redirect customer to confirmation page after booking and add 'B…
OzPol Aug 6, 2024
7a5fd80
- Added a Dashboard button on the header for authenticated users, to …
OzPol Aug 6, 2024
78ed6fa
feat: Enhance home page content for logged-in users
OzPol Aug 6, 2024
3d517d2
feat: Add view functionality for customer and provider bookings
OzPol Aug 6, 2024
b74884c
feat: Implemented view, edit, and delete functionality for customer b…
OzPol Aug 6, 2024
7861097
Fix: Improved footer styling and positioning
OzPol Aug 6, 2024
2c96753
-Footer fix
OzPol Aug 6, 2024
f563f12
removed red Logout on user panel
ShawnWang21 Aug 7, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 1 addition & 3 deletions app/layout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,4 @@ export default function RootLayout({
<body className={inter.className}>{children}</body>
</html>
);
}


}
25 changes: 17 additions & 8 deletions components/AvailabilityCalendar.tsx
Original file line number Diff line number Diff line change
@@ -1,21 +1,30 @@
// ./components/AvailabilityCalendar.tsx

import React, { useState } from 'react';
import React, { useEffect, useState } from 'react';
import Calendar, { CalendarProps } from 'react-calendar';
import 'react-calendar/dist/Calendar.css';

const AvailabilityCalendar = ({ availableDates }: { availableDates: Date[] }) => {
interface AvailabilityCalendarProps {
availableDates: Date[];
onDateChange?: (date: Date) => void;
isProvider?: boolean;
}

const AvailabilityCalendar: React.FC<AvailabilityCalendarProps> = ({ availableDates, onDateChange, isProvider }) => {
const [selectedDate, setSelectedDate] = useState<Date | null>(null);

useEffect(() => {
if (availableDates.length > 0) {
setSelectedDate(availableDates[0]);
}
}, [availableDates]);

const isAvailable = (date: Date) => {
return availableDates.some(availableDate => availableDate.toDateString() === date.toDateString());
};

const handleDateChange: CalendarProps['onChange'] = (date, event) => {
if (Array.isArray(date)) {
setSelectedDate(date[0]);
} else {
const handleDateChange: CalendarProps['onChange'] = (date) => {
if (date instanceof Date) {
setSelectedDate(date);
if (onDateChange) onDateChange(date);
}
};

Expand Down
190 changes: 144 additions & 46 deletions components/BookingForm.tsx
Original file line number Diff line number Diff line change
@@ -1,60 +1,158 @@
// components/BookingForm.tsx
// ./components/BookingForm.tsx

'use client';

import { zodResolver } from '@hookform/resolvers/zod';
import { useForm } from 'react-hook-form';
import React, { useState, useEffect } from 'react';
import { databases } from '../lib/appwrite.config';
import { BookingFormProps } from '../types/appwrite.type';
import { useRouter } from 'next/router';
import { z } from 'zod';
import { getBookingSchema } from '../lib/validation';
import CustomFormField, { FormFieldType } from './CustomFormField';
import SubmitButton from './SubmitButton';
import { createBooking } from '../lib/booking.actions';

const BookingForm = () => {
const BookingForm: React.FC<BookingFormProps> = ({ providerId, serviceId, selectedDate, onSubmit }) => {
const [address, setAddress] = useState('');
const [city, setCity] = useState('');
const [state, setState] = useState('');
const [zipcode, setZipcode] = useState('');
const [coupon, setCoupon] = useState('');
const [discount, setDiscount] = useState(0);
const router = useRouter();
const form = useForm<z.infer<ReturnType<typeof getBookingSchema>>>({
resolver: zodResolver(getBookingSchema('create')),
defaultValues: {
service: '',
date: new Date(),
time: '',
},
});

const onSubmit = async (values: z.infer<ReturnType<typeof getBookingSchema>>) => {
const consumerId = JSON.parse(localStorage.getItem('appwriteSession') || '{}').userId;

useEffect(() => {
const fetchServiceDetails = async () => {
try {
const service = await databases.getDocument(
process.env.DATABASE_ID!,
process.env.SERVICE_COLLECTION_ID!,
serviceId
);
setDiscount(service.price);
} catch (error) {
console.error('Error fetching service details:', error);
}
};

fetchServiceDetails();
}, [serviceId]);

const handleCouponApply = async () => {
if (coupon === '100OFF') {
setDiscount(100);
} else {
setDiscount(0);
}
};

const handleFormSubmit = async (e: React.FormEvent) => {
e.preventDefault();
if (!selectedDate) {
alert("Please select a date for booking.");
return;
}

const formData = {
date: selectedDate.toISOString(),
consumerId,
providerId,
serviceId,
status: 'pending',
address,
city,
state,
zipcode,
discount
};

try {
await createBooking(values);
router.push('/customerProfile'); // Redirect after booking
const response = await fetch('/api/bookings/create', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify(formData),
});

if (response.ok) {
const responseData = await response.json();
router.push(`/payment-confirmation?bookingId=${responseData.bookingId}`);
} else {
console.error('Failed to create booking.');
}
} catch (error) {
console.error('Error creating booking:', error);
}
};

return (
<form onSubmit={form.handleSubmit(onSubmit)} className="space-y-6">
<CustomFormField
fieldType={FormFieldType.INPUT}
control={form.control}
name="service"
label="Service"
placeholder="Service"
/>
<CustomFormField
fieldType={FormFieldType.DATE_PICKER}
control={form.control}
name="date"
label="Date"
dateFormat="MM/dd/yyyy"
/>
<CustomFormField
fieldType={FormFieldType.INPUT}
control={form.control}
name="time"
label="Time"
placeholder="Time"
/>
<SubmitButton isLoading={false}>Create Booking</SubmitButton>
<form onSubmit={handleFormSubmit} className="mt-4">
<div>
<label className="block text-sm font-medium text-gray-700">Date</label>
<input
type="text"
value={selectedDate.toDateString()}
readOnly
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"
/>
</div>
<div className="mt-2">
<label className="block text-sm font-medium text-gray-700">Address</label>
<input
type="text"
value={address}
onChange={(e) => setAddress(e.target.value)}
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"
required
/>
</div>
<div className="mt-2">
<label className="block text-sm font-medium text-gray-700">City</label>
<input
type="text"
value={city}
onChange={(e) => setCity(e.target.value)}
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"
required
/>
</div>
<div className="mt-2">
<label className="block text-sm font-medium text-gray-700">State</label>
<input
type="text"
value={state}
onChange={(e) => setState(e.target.value)}
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"
required
/>
</div>
<div className="mt-2">
<label className="block text-sm font-medium text-gray-700">Zipcode</label>
<input
type="text"
value={zipcode}
onChange={(e) => setZipcode(e.target.value)}
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"
required
/>
</div>
<div className="mt-2">
<label className="block text-sm font-medium text-gray-700">Coupon Code</label>
<input
type="text"
value={coupon}
onChange={(e) => setCoupon(e.target.value)}
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"
/>
<button
type="button"
onClick={handleCouponApply}
className="ml-2 bg-green-500 text-white py-1 px-2 rounded"
>
Apply
</button>
</div>
<button
type="submit"
className="mt-4 bg-blue-500 text-white py-2 px-4 rounded"
>
Book Now
</button>
</form>
);
};
Expand Down
2 changes: 1 addition & 1 deletion components/CustomerAccountDetails.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -125,4 +125,4 @@ const CustomerAccountDetails: React.FC = () => {
);
};

export default CustomerAccountDetails;
export default CustomerAccountDetails;
19 changes: 19 additions & 0 deletions components/CustomerContent.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
// components/CustomerContent.tsx
import Link from 'next/link';

const CustomerContent = () => {
return (
<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">
<h1 className="text-4xl font-bold mb-2">Looking for inspirations?!</h1>
<h2 className="text-2xl mb-6">Check out our latest services and offers tailored just for you.</h2>
<Link href="/popular-services">
<span className="w-full bg-blue-500 text-white py-3 px-6 rounded-lg text-center hover:bg-blue-600">
View Popular Services in Your Area
</span>
</Link>
{/* Add more customer-specific content or components here */}
</div>
);
};

export default CustomerContent;
12 changes: 10 additions & 2 deletions components/CustomerSearchServices.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,12 @@ import { Service } from '../types/appwrite.type';
import { fetchAllServices } from './DataServiceConsumer';
import { FaSearch } from 'react-icons/fa';

const CustomerSearchServices: React.FC = () => {
// Define the prop type for CustomerSearchServices
interface CustomerSearchServicesProps {
onServiceClick: React.Dispatch<React.SetStateAction<Service | null>>;
}

const CustomerSearchServices: React.FC<CustomerSearchServicesProps> = ({ onServiceClick }) => {
const [services, setServices] = useState<Service[]>([]);
const [filteredServices, setFilteredServices] = useState<Service[]>([]);
const [selectedService, setSelectedService] = useState<Service | null>(null);
Expand Down Expand Up @@ -225,7 +230,10 @@ const CustomerSearchServices: React.FC = () => {
providerIcon={'/assets/DefaultProviderProfile.jpeg'}
rating={parseFloat(calculateAverageRating(service.ratings).toFixed(1))}
imageUrl={service.imageUrl}
onClick={() => setSelectedService(service)} // Set the selected service on click
onClick={() => {
setSelectedService(service); // Set the selected service on click
onServiceClick(service); // Notify the parent component of the selected service
}}
onProviderClick={() => setSelectedProvider(service.providerId)} // Set the selected provider on click
/>
))}
Expand Down
Loading
Loading