TypeScript · 4293 bytes Raw Blame History
1 'use client';
2
3 import { useState } from 'react';
4 import { X, Star, Loader2 } from 'lucide-react';
5 import { Restaurant, CreateRatingData } from '@/lib/api';
6
7 interface ReviewModalProps {
8 restaurant: Restaurant;
9 onClose: () => void;
10 onSubmit: (data: CreateRatingData) => Promise<void>;
11 }
12
13 export default function ReviewModal({ restaurant, onClose, onSubmit }: ReviewModalProps) {
14 const [rating, setRating] = useState(5);
15 const [review, setReview] = useState('');
16 const [isSubmitting, setIsSubmitting] = useState(false);
17
18 const handleSubmit = async (e: React.FormEvent) => {
19 e.preventDefault();
20 if (!review.trim()) return;
21
22 setIsSubmitting(true);
23 try {
24 await onSubmit({ rating, review });
25 onClose();
26 } catch {
27 // Error handling done in parent
28 } finally {
29 setIsSubmitting(false);
30 }
31 };
32
33 return (
34 <div className="fixed inset-0 bg-black bg-opacity-50 flex items-center justify-center z-[2000] p-4">
35 <div className="bg-white rounded-lg max-w-md w-full p-6 shadow-xl">
36 <div className="flex justify-between items-start mb-4">
37 <div>
38 <h2 className="text-xl font-bold text-gray-900">Rate the Toast at {restaurant.name} 🍞</h2>
39 <p className="text-sm text-gray-600 mt-1">{restaurant.address}</p>
40 </div>
41 <button
42 onClick={onClose}
43 className="text-gray-500 hover:text-gray-700 p-1"
44 aria-label="Close modal"
45 >
46 <X className="w-6 h-6" />
47 </button>
48 </div>
49
50 <form onSubmit={handleSubmit} className="space-y-4">
51 <div>
52 <label className="block text-sm font-medium mb-2">How was the toast?</label>
53 <div className="flex space-x-2 justify-center">
54 {[1, 2, 3, 4, 5].map((value) => (
55 <button
56 key={value}
57 type="button"
58 onClick={() => setRating(value)}
59 className={`p-2 transition-colors ${rating >= value ? 'text-yellow-500' : 'text-gray-300'}`}
60 >
61 <Star className="w-8 h-8 fill-current" />
62 </button>
63 ))}
64 </div>
65 <p className="text-center text-sm text-gray-600 mt-2">
66 {rating === 1 && "Terrible toast 😞"}
67 {rating === 2 && "Not great toast 😕"}
68 {rating === 3 && "Decent toast 🙂"}
69 {rating === 4 && "Good toast! 😊"}
70 {rating === 5 && "Amazing toast! 🤩"}
71 </p>
72 </div>
73
74 <div>
75 <label className="block text-sm font-medium mb-2">
76 Tell us about the toast (must mention toast!)
77 </label>
78 <textarea
79 value={review}
80 onChange={(e) => setReview(e.target.value)}
81 className="w-full p-3 border rounded-lg focus:ring-2 focus:ring-amber-500 focus:border-amber-500"
82 rows={4}
83 placeholder="How was the toast? Was it crispy? Buttery? What kind of bread? Any toppings?"
84 required
85 />
86 <p className="text-xs text-gray-500 mt-1">
87 💡 Tip: Mention the toast texture, toppings, bread type, or how it was served!
88 </p>
89 </div>
90
91 <div className="flex space-x-2">
92 <button
93 type="submit"
94 disabled={isSubmitting || !review.trim()}
95 className="flex-1 bg-amber-600 text-white py-2 px-4 rounded-lg hover:bg-amber-700 transition disabled:opacity-50 disabled:cursor-not-allowed font-medium flex items-center justify-center"
96 >
97 {isSubmitting ? (
98 <>
99 <Loader2 className="w-4 h-4 animate-spin mr-2" />
100 <span>Submitting...</span>
101 </>
102 ) : (
103 <span>Submit Toast Review</span>
104 )}
105 </button>
106 <button
107 type="button"
108 onClick={onClose}
109 className="flex-1 bg-gray-200 text-gray-700 py-2 px-4 rounded-lg hover:bg-gray-300 transition font-medium"
110 >
111 Cancel
112 </button>
113 </div>
114 </form>
115 </div>
116 </div>
117 );
118 }