@@ -2,6 +2,13 @@ import { useEffect, useMemo, useState } from "react";
22import { Link } from "react-router-dom" ;
33import { useState } from "react" ;
44
5+ const Bookings = ( ) => {
6+ const [ bookings , setBookings ] = useState ( [ ] ) ;
7+ const [ search , setSearch ] = useState ( "" ) ;
8+ const [ statusFilter , setStatusFilter ] = useState ( "All" ) ;
9+ const [ loading , setLoading ] = useState ( true ) ;
10+ const [ error , setError ] = useState ( null ) ;
11+ const [ actionLoading , setActionLoading ] = useState ( { } ) ; // For individual button loading
512const StarRating = ( { rating, onRatingChange, size = "md" } ) => {
613 const [ hoverRating , setHoverRating ] = useState ( 0 ) ;
714
@@ -91,6 +98,42 @@ const StarRating = ({ rating, onRatingChange, size = "md" }) => {
9198 setLoading ( false ) ;
9299 } , [ ] ) ;
93100
101+ const handleCancel = async ( id ) => {
102+ setActionLoading ( prev => ( { ...prev , [ id ] : true } ) ) ;
103+ try {
104+ // TODO: API call to cancel booking
105+ await new Promise ( resolve => setTimeout ( resolve , 1000 ) ) ; // Simulate API call
106+ const updated = bookings . map ( ( b ) =>
107+ b . id === id ? { ...b , status : "Cancelled" } : b
108+ ) ;
109+ setBookings ( updated ) ;
110+ } catch ( error ) {
111+ console . error ( 'Cancel failed:' , error ) ;
112+ } finally {
113+ setActionLoading ( prev => ( { ...prev , [ id ] : false } ) ) ;
114+ }
115+ } ;
116+
117+ const handleReviewSubmit = async ( id ) => {
118+ if ( rating === 0 ) return alert ( "Please select a rating" ) ;
119+
120+ setActionLoading ( prev => ( { ...prev , [ `review-${ id } ` ] : true } ) ) ;
121+ try {
122+ // TODO: API call to submit review
123+ await new Promise ( resolve => setTimeout ( resolve , 1000 ) ) ; // Simulate API call
124+ const updated = bookings . map ( ( b ) =>
125+ b . id === id ? { ...b , review : { rating, comment } } : b
126+ ) ;
127+
128+ setBookings ( updated ) ;
129+ setActiveReview ( null ) ;
130+ setRating ( 0 ) ;
131+ setComment ( "" ) ;
132+ } catch ( error ) {
133+ console . error ( 'Review submit failed:' , error ) ;
134+ } finally {
135+ setActionLoading ( prev => ( { ...prev , [ `review-${ id } ` ] : false } ) ) ;
136+ }
94137 // ---------------- SAVE BOOKINGS ----------------
95138
96139 useEffect ( ( ) => {
@@ -452,6 +495,16 @@ const StarRating = ({ rating, onRatingChange, size = "md" }) => {
452495
453496 </ div >
454497
498+ { /* Actions */ }
499+ < div className = "mt-3 flex gap-4" >
500+ { booking . status === "Pending" && (
501+ < button
502+ onClick = { ( ) => handleCancel ( booking . id ) }
503+ disabled = { actionLoading [ booking . id ] }
504+ className = "text-red-600 hover:underline text-sm disabled:opacity-50 disabled:cursor-not-allowed"
505+ >
506+ < span className = { `btn-text ${ actionLoading [ booking . id ] ? 'hidden' : '' } ` } > Cancel</ span >
507+ < span className = { `btn-loader ${ actionLoading [ booking . id ] ? '' : 'hidden' } ` } > Loading...</ span >
455508 < div className = "bg-slate-50 rounded-2xl p-4" >
456509
457510 < p className = "text-xs text-slate-500" >
@@ -518,6 +571,16 @@ const StarRating = ({ rating, onRatingChange, size = "md" }) => {
518571
519572 { / * REVIEW BOX * / }
520573
574+ { /* Buttons */ }
575+ < div className = "flex gap-3" >
576+ < button
577+ onClick = { ( ) => handleReviewSubmit ( booking . id ) }
578+ disabled = { actionLoading [ `review-${ booking . id } ` ] }
579+ className = "bg-blue-600 text-white px-3 py-1 rounded text-sm disabled:opacity-50 disabled:cursor-not-allowed"
580+ >
581+ < span className = { `btn-text ${ actionLoading [ `review-${ booking . id } ` ] ? 'hidden' : '' } ` } > Submit</ span >
582+ < span className = { `btn-loader ${ actionLoading [ `review-${ booking . id } ` ] ? '' : 'hidden' } ` } > Loading...</ span >
583+ </ button >
521584 { activeReview === b . id && (
522585 < div className = "mt-6 bg-slate-50 border border-slate-200 rounded-3xl p-5" >
523586
0 commit comments