TypeScript · 4848 bytes Raw Blame History
1 'use client';
2
3 import { useState } from 'react';
4 import { X, Star, MapPin } from 'lucide-react';
5 import { Restaurant, CreateRatingData } from '@/lib/api';
6
7 interface RestaurantPanelProps {
8 restaurant: Restaurant;
9 onClose: () => void;
10 onAddRating: (data: CreateRatingData) => Promise<void>;
11 }
12
13 export default function RestaurantPanel({ restaurant, onClose, onAddRating }: RestaurantPanelProps) {
14 const [showRatingForm, setShowRatingForm] = useState(false);
15 const [rating, setRating] = useState(5);
16 const [review, setReview] = useState('');
17 const [isSubmitting, setIsSubmitting] = useState(false);
18
19 const handleSubmit = async (e: React.FormEvent) => {
20 e.preventDefault();
21 if (!review.trim()) return;
22
23 setIsSubmitting(true);
24 try {
25 await onAddRating({ rating, review });
26 setShowRatingForm(false);
27 setRating(5);
28 setReview('');
29 } finally {
30 setIsSubmitting(false);
31 }
32 };
33
34 return (
35 <div className="absolute bottom-0 left-0 right-0 bg-white p-6 shadow-xl rounded-t-xl max-h-96 overflow-y-auto z-[1000] slide-up">
36 <div className="flex justify-between items-start mb-4">
37 <div className="flex-1">
38 <h2 className="text-xl font-bold text-gray-900">{restaurant.name}</h2>
39 <p className="text-gray-700 flex items-center mt-1">
40 <MapPin className="w-4 h-4 mr-1" />
41 {restaurant.address}
42 </p>
43 </div>
44 <button
45 onClick={onClose}
46 className="text-gray-500 hover:text-gray-700 p-1"
47 aria-label="Close panel"
48 >
49 <X className="w-6 h-6" />
50 </button>
51 </div>
52
53 <div className="flex items-center space-x-4 mb-4">
54 <div className="flex items-center">
55 {restaurant.average_rating ? (
56 <>
57 <Star className="w-5 h-5 text-yellow-500 fill-current" />
58 <span className="ml-1 font-semibold text-gray-900">
59 {restaurant.average_rating.toFixed(1)}
60 </span>
61 </>
62 ) : (
63 <span className="text-gray-700">No ratings yet</span>
64 )}
65 </div>
66 <span className="text-gray-700">
67 {restaurant.total_ratings} {restaurant.total_ratings === 1 ? 'rating' : 'ratings'}
68 </span>
69 {restaurant.distance && (
70 <span className="text-gray-700">
71 {restaurant.distance < 1 ? `${Math.round(restaurant.distance * 1000)}m` : `${restaurant.distance.toFixed(1)}km`} away
72 </span>
73 )}
74 </div>
75
76 {!showRatingForm ? (
77 <button
78 onClick={() => setShowRatingForm(true)}
79 className="w-full bg-amber-600 text-white py-2 px-4 rounded-lg hover:bg-amber-700 transition font-medium"
80 >
81 Rate the Toast! 🍞
82 </button>
83 ) : (
84 <form onSubmit={handleSubmit} className="space-y-4">
85 <div>
86 <label className="block text-sm font-medium mb-2">Rating</label>
87 <div className="flex space-x-2">
88 {[1, 2, 3, 4, 5].map((value) => (
89 <button
90 key={value}
91 type="button"
92 onClick={() => setRating(value)}
93 className={`p-2 ${rating >= value ? 'text-yellow-500' : 'text-gray-300'}`}
94 >
95 <Star className="w-8 h-8 fill-current" />
96 </button>
97 ))}
98 </div>
99 </div>
100
101 <div>
102 <label className="block text-sm font-medium mb-2">
103 Review (must be about toast!)
104 </label>
105 <textarea
106 value={review}
107 onChange={(e) => setReview(e.target.value)}
108 className="w-full p-3 border rounded-lg focus:ring-2 focus:ring-amber-500 focus:border-amber-500"
109 rows={3}
110 placeholder="How was the toast? Crispy? Buttery? Perfect golden brown?"
111 required
112 />
113 </div>
114
115 <div className="flex space-x-2">
116 <button
117 type="submit"
118 disabled={isSubmitting || !review.trim()}
119 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"
120 >
121 {isSubmitting ? 'Submitting...' : 'Submit Rating'}
122 </button>
123 <button
124 type="button"
125 onClick={() => {
126 setShowRatingForm(false);
127 setRating(5);
128 setReview('');
129 }}
130 className="flex-1 bg-gray-200 text-gray-700 py-2 px-4 rounded-lg hover:bg-gray-300 transition font-medium"
131 >
132 Cancel
133 </button>
134 </div>
135 </form>
136 )}
137 </div>
138 );
139 }