Skip to content

Commit 3d517d2

Browse files
committed
feat: Add view functionality for customer and provider bookings
- Implemented CustomerViewBookings component to display customer's bookings with options to delete. - Implemented ServiceViewBookings component to display provider's bookings with options to approve or reject. - Added API endpoints to fetch bookings for customers and providers. - Updated booking creation process to set initial status to "pending" and added approval/rejection functionality for providers.
1 parent 78ed6fa commit 3d517d2

File tree

7 files changed

+238
-27
lines changed

7 files changed

+238
-27
lines changed

components/CustomerViewBookings.tsx

+66-10
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,67 @@
1+
// components/CustomerViewBookings.tsx
2+
import React, { useEffect, useState } from 'react';
3+
import { Booking } from '../types/appwrite.type';
4+
import { databases } from '../lib/appwrite.config';
5+
import * as sdk from 'node-appwrite';
6+
17
const CustomerViewBookings = () => {
2-
return (
3-
<div>
4-
<h2 className="text-2xl font-bold mb-4">View Bookings</h2>
5-
<p>This is a placeholder for the customer bookings section.</p>
6-
</div>
7-
);
8-
};
9-
10-
export default CustomerViewBookings;
11-
8+
const [bookings, setBookings] = useState<Booking[]>([]);
9+
const userId = JSON.parse(localStorage.getItem('appwriteSession') || '{}').userId;
10+
11+
useEffect(() => {
12+
const fetchBookings = async () => {
13+
try {
14+
const response = await databases.listDocuments(
15+
process.env.DATABASE_ID!,
16+
process.env.BOOKING_COLLECTION_ID!,
17+
[sdk.Query.equal('consumerId', userId)]
18+
);
19+
20+
const bookingsData: Booking[] = response.documents.map((doc: sdk.Models.Document) => ({
21+
$id: doc.$id,
22+
$permissions: doc.$permissions,
23+
bookingId: doc.$id,
24+
$collectionId: doc.$collectionId,
25+
$databaseId: doc.$databaseId,
26+
$createdAt: doc.$createdAt,
27+
$updatedAt: doc.$updatedAt,
28+
consumerId: doc.consumerId,
29+
providerId: doc.providerId,
30+
serviceId: doc.serviceId,
31+
date: new Date(doc.date),
32+
status: doc.status,
33+
address: doc.address,
34+
city: doc.city,
35+
state: doc.state,
36+
zipcode: doc.zipcode,
37+
}));
38+
39+
setBookings(bookingsData);
40+
} catch (error) {
41+
console.error('Error fetching bookings:', error);
42+
}
43+
};
44+
45+
fetchBookings();
46+
}, [userId]);
47+
48+
return (
49+
<div>
50+
<h2 className="text-2xl font-bold mb-4">View Bookings</h2>
51+
{bookings.length > 0 ? (
52+
bookings.map((booking) => (
53+
<div key={booking.bookingId} className="mb-4 p-4 border rounded shadow">
54+
<p><strong>Service:</strong> {booking.serviceId}</p>
55+
<p><strong>Date:</strong> {new Date(booking.date).toLocaleDateString()}</p>
56+
<p><strong>Status:</strong> {booking.status}</p>
57+
<p><strong>Address:</strong> {booking.address}, {booking.city}, {booking.state}, {booking.zipcode}</p>
58+
</div>
59+
))
60+
) : (
61+
<p>No bookings found.</p>
62+
)}
63+
</div>
64+
);
65+
};
66+
67+
export default CustomerViewBookings;

components/ServiceViewBookings.tsx

+107-9
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,109 @@
1+
// components/ServiceViewBookings.tsx
2+
import React, { useEffect, useState } from 'react';
3+
import { Booking } from '../types/appwrite.type';
4+
import { databases } from '../lib/appwrite.config';
5+
import * as sdk from 'node-appwrite';
6+
17
const ServiceViewBookings = () => {
2-
return (
3-
<div>
4-
<h2 className="text-2xl font-bold mb-4">View Bookings</h2>
5-
<p>This is a placeholder for the service provider to view their bookings.</p>
6-
</div>
7-
);
8+
const [bookings, setBookings] = useState<Booking[]>([]);
9+
const userId = JSON.parse(localStorage.getItem('appwriteSession') || '{}').userId;
10+
11+
useEffect(() => {
12+
const fetchBookings = async () => {
13+
try {
14+
const response = await databases.listDocuments(
15+
process.env.DATABASE_ID!,
16+
process.env.BOOKING_COLLECTION_ID!,
17+
[sdk.Query.equal('providerId', userId)]
18+
);
19+
20+
const bookingsData: Booking[] = response.documents.map((doc: sdk.Models.Document) => ({
21+
$id: doc.$id,
22+
$permissions: doc.$permissions,
23+
bookingId: doc.$id,
24+
$collectionId: doc.$collectionId,
25+
$databaseId: doc.$databaseId,
26+
$createdAt: doc.$createdAt,
27+
$updatedAt: doc.$updatedAt,
28+
consumerId: doc.consumerId,
29+
providerId: doc.providerId,
30+
serviceId: doc.serviceId,
31+
date: new Date(doc.date),
32+
status: doc.status,
33+
address: doc.address,
34+
city: doc.city,
35+
state: doc.state,
36+
zipcode: doc.zipcode,
37+
}));
38+
39+
setBookings(bookingsData);
40+
} catch (error) {
41+
console.error('Error fetching bookings:', error);
42+
}
43+
};
44+
45+
fetchBookings();
46+
}, [userId]);
47+
48+
const handleApproval = async (bookingId: string, status: string) => {
49+
const reason = prompt('Please provide a reason for this decision:');
50+
if (!reason) {
51+
alert('A reason is required.');
52+
return;
53+
}
54+
try {
55+
const response = await fetch(`/api/bookings/update`, {
56+
method: 'POST',
57+
headers: {
58+
'Content-Type': 'application/json',
59+
},
60+
body: JSON.stringify({ bookingId, status, reason }),
61+
});
62+
63+
if (response.ok) {
64+
alert('Booking status updated.');
65+
setBookings(bookings.map(b => b.bookingId === bookingId ? { ...b, status } : b));
66+
} else {
67+
console.error('Failed to update booking.');
68+
}
69+
} catch (error) {
70+
console.error('Error updating booking:', error);
71+
}
872
};
9-
10-
export default ServiceViewBookings;
11-
73+
74+
return (
75+
<div>
76+
<h2 className="text-2xl font-bold mb-4">View Bookings</h2>
77+
{bookings.length > 0 ? (
78+
bookings.map((booking) => (
79+
<div key={booking.bookingId} className="mb-4 p-4 border rounded shadow">
80+
<p><strong>Service:</strong> {booking.serviceId}</p>
81+
<p><strong>Date:</strong> {new Date(booking.date).toLocaleDateString()}</p>
82+
<p><strong>Status:</strong> {booking.status}</p>
83+
<p><strong>Address:</strong> {booking.address}, {booking.city}, {booking.state}, {booking.zipcode}</p>
84+
{booking.status === 'pending' && (
85+
<div>
86+
<button
87+
onClick={() => handleApproval(booking.bookingId, 'approved')}
88+
className="bg-green-500 text-white py-2 px-4 rounded mr-2"
89+
>
90+
Approve
91+
</button>
92+
<button
93+
onClick={() => handleApproval(booking.bookingId, 'rejected')}
94+
className="bg-red-500 text-white py-2 px-4 rounded"
95+
>
96+
Reject
97+
</button>
98+
</div>
99+
)}
100+
</div>
101+
))
102+
) : (
103+
<p>No bookings found.</p>
104+
)}
105+
</div>
106+
);
107+
};
108+
109+
export default ServiceViewBookings;

lib/DataBookings.ts

+32
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
// lib/DataBookings.ts
2+
import { databases } from '../lib/appwrite.config';
3+
import { Booking } from '../types/appwrite.type';
4+
import * as sdk from 'node-appwrite';
5+
6+
export const fetchCustomerBookings = async (consumerId: string): Promise<Booking[]> => {
7+
try {
8+
const response = await databases.listDocuments(
9+
process.env.DATABASE_ID!,
10+
process.env.BOOKINGS_COLLECTION_ID!,
11+
[sdk.Query.equal('consumerId', consumerId)]
12+
);
13+
return response.documents as Booking[];
14+
} catch (error) {
15+
console.error('Error fetching customer bookings:', error);
16+
return [];
17+
}
18+
};
19+
20+
export const fetchProviderBookings = async (providerId: string): Promise<Booking[]> => {
21+
try {
22+
const response = await databases.listDocuments(
23+
process.env.DATABASE_ID!,
24+
process.env.BOOKINGS_COLLECTION_ID!,
25+
[sdk.Query.equal('providerId', providerId)]
26+
);
27+
return response.documents as Booking[];
28+
} catch (error) {
29+
console.error('Error fetching provider bookings:', error);
30+
return [];
31+
}
32+
};

pages/api/bookings/create.ts

+3-2
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
// Availability is an array of objects with date, startTime, and endTime
88
// It uses the appwrite sdk to update the availability in the database
99

10+
// ./pages/api/bookings/create.ts
1011
// ./pages/api/bookings/create.ts
1112

1213
import { NextApiRequest, NextApiResponse } from 'next';
@@ -15,7 +16,7 @@ import { databases } from '../../../lib/appwrite.config';
1516
export default async function handler(req: NextApiRequest, res: NextApiResponse) {
1617
if (req.method === 'POST') {
1718
try {
18-
const { date, consumerId, providerId, serviceId, status, address, city, state, zipcode } = req.body;
19+
const { date, consumerId, providerId, serviceId, address, city, state, zipcode } = req.body;
1920

2021
// Create a new booking document in Appwrite
2122
const newBooking = await databases.createDocument(
@@ -27,7 +28,7 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse)
2728
providerId,
2829
serviceId,
2930
date,
30-
status,
31+
status: 'confirmed', // Set status to 'confirmed'
3132
address,
3233
city,
3334
state,

pages/api/bookings/customer/[id].ts

+3-3
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
// This API endpoint is used to fetch all bookings of a customer by customer id
2-
// ./pages/api/bookings/customer/[id].ts
3-
2+
// pages/api/bookings/customer/[id].ts
43
import { NextApiRequest, NextApiResponse } from 'next';
54
import { databases } from '../../../../lib/appwrite.config';
5+
import * as sdk from 'node-appwrite';
66

77
export default async function handler(req: NextApiRequest, res: NextApiResponse) {
88
const { id } = req.query;
@@ -11,7 +11,7 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse)
1111
const response = await databases.listDocuments(
1212
process.env.DATABASE_ID!,
1313
process.env.BOOKINGS_COLLECTION_ID!,
14-
[`customerId=${id}`]
14+
[sdk.Query.equal('consumerId', id as string)]
1515
);
1616
res.status(200).json(response);
1717
} catch (error) {

pages/api/bookings/provider/[id].ts

+4-3
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
// This API endpoint is used to fetch all bookings of a provider by providerId
2-
// ./pages/api/bookings/provider/[id].ts
3-
2+
// pages/api/bookings/provider/[id].ts
43
import { NextApiRequest, NextApiResponse } from 'next';
54
import { databases } from '../../../../lib/appwrite.config';
5+
import * as sdk from 'node-appwrite';
66

77
export default async function handler(req: NextApiRequest, res: NextApiResponse) {
88
const { id } = req.query;
@@ -11,7 +11,7 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse)
1111
const response = await databases.listDocuments(
1212
process.env.DATABASE_ID!,
1313
process.env.BOOKINGS_COLLECTION_ID!,
14-
[`providerId=${id}`]
14+
[sdk.Query.equal('providerId', id as string)]
1515
);
1616
res.status(200).json(response);
1717
} catch (error) {
@@ -22,3 +22,4 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse)
2222
res.status(405).end(`Method ${req.method} Not Allowed`);
2323
}
2424
}
25+

pages/api/bookings/update.ts

+23
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
// pages/api/bookings/update.ts
2+
import { NextApiRequest, NextApiResponse } from 'next';
3+
import { databases } from '../../../lib/appwrite.config';
4+
5+
export default async function handler(req: NextApiRequest, res: NextApiResponse) {
6+
if (req.method === 'POST') {
7+
const { bookingId, status, reason } = req.body;
8+
try {
9+
await databases.updateDocument(
10+
process.env.DATABASE_ID!,
11+
process.env.BOOKING_COLLECTION_ID!,
12+
bookingId,
13+
{ status, reason }
14+
);
15+
res.status(200).json({ message: 'Booking status updated successfully' });
16+
} catch (error) {
17+
res.status(500).json({ error: 'Failed to update booking' });
18+
}
19+
} else {
20+
res.setHeader('Allow', ['POST']);
21+
res.status(405).end(`Method ${req.method} Not Allowed`);
22+
}
23+
}

0 commit comments

Comments
 (0)