TypeScript · 4909 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 } catch (error) {
30 // Error handling done in parent
31 } finally {
32 setIsSubmitting(false);
33 }
34 };
35
36 return (
37 <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">
38 <div className="flex justify-between items-start mb-4">
39 <div className="flex-1">
40 <h2 className="text-xl font-bold text-gray-900">{restaurant.name}</h2>
41 <p className="text-gray-700 flex items-center mt-1">
42 <MapPin className="w-4 h-4 mr-1" />
43 {restaurant.address}
44 </p>
45 </div>
46 <button
47 onClick={onClose}
48 className="text-gray-500 hover:text-gray-700 p-1"
49 aria-label="Close panel"
50 >
51 <X className="w-6 h-6" />
52 </button>
53 </div>
54
55 <div className="flex items-center space-x-4 mb-4">
56 <div className="flex items-center">
57 {restaurant.average_rating ? (
58 <>
59 <Star className="w-5 h-5 text-yellow-500 fill-current" />
60 <span className="ml-1 font-semibold text-gray-900">
61 {restaurant.average_rating.toFixed(1)}
62 </span>
63 </>
64 ) : (
65 <span className="text-gray-700">No ratings yet</span>
66 )}
67 </div>
68 <span className="text-gray-700">
69 {restaurant.total_ratings} {restaurant.total_ratings === 1 ? 'rating' : 'ratings'}
70 </span>
71 {restaurant.distance && (
72 <span className="text-gray-700">
73 {restaurant.distance < 1 ? `${Math.round(restaurant.distance * 1000)}m` : `${restaurant.distance.toFixed(1)}km`} away
74 </span>
75 )}
76 </div>
77
78 {!showRatingForm ? (
79 <button
80 onClick={() => setShowRatingForm(true)}
81 className="w-full bg-amber-600 text-white py-2 px-4 rounded-lg hover:bg-amber-700 transition font-medium"
82 >
83 Rate the Toast! 🍞
84 </button>
85 ) : (
86 <form onSubmit={handleSubmit} className="space-y-4">
87 <div>
88 <label className="block text-sm font-medium mb-2">Rating</label>
89 <div className="flex space-x-2">
90 {[1, 2, 3, 4, 5].map((value) => (
91 <button
92 key={value}
93 type="button"
94 onClick={() => setRating(value)}
95 className={`p-2 ${rating >= value ? 'text-yellow-500' : 'text-gray-300'}`}
96 >
97 <Star className="w-8 h-8 fill-current" />
98 </button>
99 ))}
100 </div>
101 </div>
102
103 <div>
104 <label className="block text-sm font-medium mb-2">
105 Review (must be about toast!)
106 </label>
107 <textarea
108 value={review}
109 onChange={(e) => setReview(e.target.value)}
110 className="w-full p-3 border rounded-lg focus:ring-2 focus:ring-amber-500 focus:border-amber-500"
111 rows={3}
112 placeholder="How was the toast? Crispy? Buttery? Perfect golden brown?"
113 required
114 />
115 </div>
116
117 <div className="flex space-x-2">
118 <button
119 type="submit"
120 disabled={isSubmitting || !review.trim()}
121 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"
122 >
123 {isSubmitting ? 'Submitting...' : 'Submit Rating'}
124 </button>
125 <button
126 type="button"
127 onClick={() => {
128 setShowRatingForm(false);
129 setRating(5);
130 setReview('');
131 }}
132 className="flex-1 bg-gray-200 text-gray-700 py-2 px-4 rounded-lg hover:bg-gray-300 transition font-medium"
133 >
134 Cancel
135 </button>
136 </div>
137 </form>
138 )}
139 </div>
140 );
141 }