| 1 | import { useLang } from "../context/LanguageContext"; |
| 2 | |
| 3 | interface BarbershopDetailProps { |
| 4 | shop: { |
| 5 | id: number; |
| 6 | name: { [key: string]: string }; |
| 7 | address: { [key: string]: string }; |
| 8 | rating: number; |
| 9 | reviewCount: number; |
| 10 | priceLevel: number; |
| 11 | distance: { [key: string]: string }; |
| 12 | openNow: boolean; |
| 13 | phone: string; |
| 14 | types: string[]; |
| 15 | imageUrl?: string; |
| 16 | }; |
| 17 | onClose: () => void; |
| 18 | onDirections: () => void; |
| 19 | } |
| 20 | |
| 21 | export default function BarbershopDetail({ |
| 22 | shop, |
| 23 | onClose, |
| 24 | onDirections, |
| 25 | }: BarbershopDetailProps) { |
| 26 | const { lang } = useLang(); |
| 27 | const isRTL = lang === "fa"; |
| 28 | const dir = isRTL ? "rtl" : "ltr"; |
| 29 | |
| 30 | const getPriceLevelSymbol = (level: number) => { |
| 31 | return "$".repeat(level); |
| 32 | }; |
| 33 | |
| 34 | const getTranslation = (key: string) => { |
| 35 | const translations: { [key: string]: { [lang: string]: string } } = { |
| 36 | close: { fa: "بستن", en: "Close", sv: "Stäng" }, |
| 37 | call: { fa: "تماس", en: "Call", sv: "Ring" }, |
| 38 | directions: { fa: "مسیریابی", en: "Directions", sv: "Vägbeskrivning" }, |
| 39 | openNow: { fa: "باز است", en: "Open now", sv: "Öppet nu" }, |
| 40 | closed: { fa: "بسته است", en: "Closed", sv: "Stängt" }, |
| 41 | reviews: { fa: "نظرات", en: "reviews", sv: "recensioner" }, |
| 42 | priceLevel: { fa: "سطح قیمت", en: "Price Level", sv: "Prisnivå" }, |
| 43 | distanceAway: { fa: "فاصله", en: "away", sv: "bort" }, |
| 44 | about: { fa: "درباره", en: "About", sv: "Om" }, |
| 45 | services: { fa: "خدمات", en: "Services", sv: "Tjänster" }, |
| 46 | contact: { fa: "تماس", en: "Contact", sv: "Kontakt" }, |
| 47 | phoneNumber: { |
| 48 | fa: "شماره تلفن", |
| 49 | en: "Phone Number", |
| 50 | sv: "Telefonnummer", |
| 51 | }, |
| 52 | address: { fa: "آدرس", en: "Address", sv: "Adress" }, |
| 53 | openingHours: { fa: "ساعات کاری", en: "Opening Hours", sv: "Öppettider" }, |
| 54 | monday: { fa: "دوشنبه", en: "Monday", sv: "Måndag" }, |
| 55 | tuesday: { fa: "سهشنبه", en: "Tuesday", sv: "Tisdag" }, |
| 56 | wednesday: { fa: "چهارشنبه", en: "Wednesday", sv: "Onsdag" }, |
| 57 | thursday: { fa: "پنجشنبه", en: "Thursday", sv: "Torsdag" }, |
| 58 | friday: { fa: "جمعه", en: "Friday", sv: "Fredag" }, |
| 59 | saturday: { fa: "شنبه", en: "Saturday", sv: "Lördag" }, |
| 60 | sunday: { fa: "یکشنبه", en: "Sunday", sv: "Söndag" }, |
| 61 | }; |
| 62 | return translations[key]?.[lang] || key; |
| 63 | }; |
| 64 | |
| 65 | // Mock opening hours data |
| 66 | const openingHours = [ |
| 67 | { day: getTranslation("monday"), hours: "09:00 - 18:00" }, |
| 68 | { day: getTranslation("tuesday"), hours: "09:00 - 18:00" }, |
| 69 | { day: getTranslation("wednesday"), hours: "09:00 - 18:00" }, |
| 70 | { day: getTranslation("thursday"), hours: "09:00 - 19:00" }, |
| 71 | { day: getTranslation("friday"), hours: "09:00 - 19:00" }, |
| 72 | { day: getTranslation("saturday"), hours: "10:00 - 16:00" }, |
| 73 | { day: getTranslation("sunday"), hours: getTranslation("closed") }, |
| 74 | ]; |
| 75 | |
| 76 | return ( |
| 77 | <div |
| 78 | className="fixed inset-0 bg-black bg-opacity-50 flex items-center justify-center z-50 p-4 animate-fadeIn" |
| 79 | onClick={onClose} |
| 80 | > |
| 81 | <div |
| 82 | dir={dir} |
| 83 | className="bg-white rounded-2xl max-w-2xl w-full max-h-[90vh] overflow-y-auto shadow-2xl animate-slideUp" |
| 84 | onClick={(e) => e.stopPropagation()} |
| 85 | style={{ scrollBehavior: "smooth" }} |
| 86 | > |
| 87 | {/* Header Image */} |
| 88 | <div className="relative w-full h-64 bg-gradient-to-br from-blue-400 to-blue-600 flex items-center justify-center"> |
| 89 | <div className="text-white text-8xl">💈</div> |
| 90 | <button |
| 91 | onClick={onClose} |
| 92 | className="absolute top-4 right-4 bg-white bg-opacity-90 hover:bg-opacity-100 rounded-full p-2 transition-all shadow-lg" |
| 93 | aria-label={getTranslation("close")} |
| 94 | > |
| 95 | <svg |
| 96 | className="w-6 h-6 text-gray-800" |
| 97 | viewBox="0 0 24 24" |
| 98 | fill="none" |
| 99 | stroke="currentColor" |
| 100 | strokeWidth="2" |
| 101 | strokeLinecap="round" |
| 102 | strokeLinejoin="round" |
| 103 | > |
| 104 | <line x1="18" y1="6" x2="6" y2="18" /> |
| 105 | <line x1="6" y1="6" x2="18" y2="18" /> |
| 106 | </svg> |
| 107 | </button> |
| 108 | </div> |
| 109 | |
| 110 | {/* Content */} |
| 111 | <div className="p-6"> |
| 112 | {/* Title and Status */} |
| 113 | <div className="mb-4"> |
| 114 | <h2 className="text-3xl font-bold text-gray-900 mb-2"> |
| 115 | {shop.name[lang]} |
| 116 | </h2> |
| 117 | <div className="flex items-center gap-2 flex-wrap"> |
| 118 | <span className="text-sm text-gray-500"> |
| 119 | {shop.types.join(" • ")} |
| 120 | </span> |
| 121 | <span className="text-sm text-gray-400">•</span> |
| 122 | <span |
| 123 | className={`text-sm font-semibold ${ |
| 124 | shop.openNow ? "text-green-600" : "text-red-600" |
| 125 | }`} |
| 126 | > |
| 127 | {shop.openNow |
| 128 | ? getTranslation("openNow") |
| 129 | : getTranslation("closed")} |
| 130 | </span> |
| 131 | </div> |
| 132 | </div> |
| 133 | |
| 134 | {/* Rating and Info */} |
| 135 | <div className="flex items-center gap-4 mb-6 pb-6 border-b"> |
| 136 | <div className="flex items-center gap-1"> |
| 137 | <svg |
| 138 | className="w-5 h-5 fill-yellow-400 text-yellow-400" |
| 139 | viewBox="0 0 24 24" |
| 140 | > |
| 141 | <path d="M12 2l3.09 6.26L22 9.27l-5 4.87 1.18 6.88L12 17.77l-6.18 3.25L7 14.14 2 9.27l6.91-1.01L12 2z" /> |
| 142 | </svg> |
| 143 | <span className="font-bold text-lg text-gray-900"> |
| 144 | {shop.rating.toFixed(1)} |
| 145 | </span> |
| 146 | </div> |
| 147 | <span className="text-sm text-gray-600"> |
| 148 | ({shop.reviewCount} {getTranslation("reviews")}) |
| 149 | </span> |
| 150 | <span className="text-sm text-gray-400">•</span> |
| 151 | <span className="text-sm font-semibold text-gray-700"> |
| 152 | {getPriceLevelSymbol(shop.priceLevel)} |
| 153 | </span> |
| 154 | <span className="text-sm text-gray-400">•</span> |
| 155 | <span className="text-sm text-gray-600"> |
| 156 | {shop.distance[lang]} {getTranslation("distanceAway")} |
| 157 | </span> |
| 158 | </div> |
| 159 | |
| 160 | {/* Action Buttons */} |
| 161 | <div className="grid grid-cols-2 gap-3 mb-6"> |
| 162 | <button |
| 163 | onClick={onDirections} |
| 164 | className="flex items-center justify-center gap-2 bg-blue-600 hover:bg-blue-700 text-white px-6 py-3 rounded-lg font-semibold transition-colors" |
| 165 | > |
| 166 | <svg |
| 167 | className="w-5 h-5" |
| 168 | viewBox="0 0 24 24" |
| 169 | fill="none" |
| 170 | stroke="currentColor" |
| 171 | strokeWidth="2" |
| 172 | strokeLinecap="round" |
| 173 | strokeLinejoin="round" |
| 174 | > |
| 175 | <polygon points="3 11 22 2 13 21 11 13 3 11" /> |
| 176 | </svg> |
| 177 | {getTranslation("directions")} |
| 178 | </button> |
| 179 | <a |
| 180 | href={`tel:${shop.phone}`} |
| 181 | className="flex items-center justify-center gap-2 bg-gray-100 hover:bg-gray-200 text-gray-700 px-6 py-3 rounded-lg font-semibold transition-colors" |
| 182 | onClick={(e) => e.stopPropagation()} |
| 183 | > |
| 184 | <svg |
| 185 | className="w-5 h-5" |
| 186 | viewBox="0 0 24 24" |
| 187 | fill="none" |
| 188 | stroke="currentColor" |
| 189 | strokeWidth="2" |
| 190 | strokeLinecap="round" |
| 191 | strokeLinejoin="round" |
| 192 | > |
| 193 | <path d="M22 16.92v3a2 2 0 0 1-2.18 2 19.79 19.79 0 0 1-8.63-3.07 19.5 19.5 0 0 1-6-6 19.79 19.79 0 0 1-3.07-8.67A2 2 0 0 1 4.11 2h3a2 2 0 0 1 2 1.72 12.84 12.84 0 0 0 .7 2.81 2 2 0 0 1-.45 2.11L8.09 9.91a16 16 0 0 0 6 6l1.27-1.27a2 2 0 0 1 2.11-.45 12.84 12.84 0 0 0 2.81.7A2 2 0 0 1 22 16.92z" /> |
| 194 | </svg> |
| 195 | {getTranslation("call")} |
| 196 | </a> |
| 197 | </div> |
| 198 | |
| 199 | {/* Contact Information */} |
| 200 | <div className="mb-6"> |
| 201 | <h3 className="text-lg font-bold text-gray-900 mb-3"> |
| 202 | {getTranslation("contact")} |
| 203 | </h3> |
| 204 | |
| 205 | {/* Address */} |
| 206 | <div className="flex items-start gap-3 mb-3"> |
| 207 | <svg |
| 208 | className="w-5 h-5 text-gray-500 mt-0.5 flex-shrink-0" |
| 209 | viewBox="0 0 24 24" |
| 210 | fill="none" |
| 211 | stroke="currentColor" |
| 212 | strokeWidth="2" |
| 213 | strokeLinecap="round" |
| 214 | strokeLinejoin="round" |
| 215 | > |
| 216 | <path d="M21 10c0 7-9 13-9 13s-9-6-9-13a9 9 0 0 1 18 0z" /> |
| 217 | <circle cx="12" cy="10" r="3" /> |
| 218 | </svg> |
| 219 | <div> |
| 220 | <p className="text-sm font-semibold text-gray-700 mb-1"> |
| 221 | {getTranslation("address")} |
| 222 | </p> |
| 223 | <p className="text-sm text-gray-600">{shop.address[lang]}</p> |
| 224 | </div> |
| 225 | </div> |
| 226 | |
| 227 | {/* Phone */} |
| 228 | <div className="flex items-start gap-3"> |
| 229 | <svg |
| 230 | className="w-5 h-5 text-gray-500 mt-0.5 flex-shrink-0" |
| 231 | viewBox="0 0 24 24" |
| 232 | fill="none" |
| 233 | stroke="currentColor" |
| 234 | strokeWidth="2" |
| 235 | strokeLinecap="round" |
| 236 | strokeLinejoin="round" |
| 237 | > |
| 238 | <path d="M22 16.92v3a2 2 0 0 1-2.18 2 19.79 19.79 0 0 1-8.63-3.07 19.5 19.5 0 0 1-6-6 19.79 19.79 0 0 1-3.07-8.67A2 2 0 0 1 4.11 2h3a2 2 0 0 1 2 1.72 12.84 12.84 0 0 0 .7 2.81 2 2 0 0 1-.45 2.11L8.09 9.91a16 16 0 0 0 6 6l1.27-1.27a2 2 0 0 1 2.11-.45 12.84 12.84 0 0 0 2.81.7A2 2 0 0 1 22 16.92z" /> |
| 239 | </svg> |
| 240 | <div> |
| 241 | <p className="text-sm font-semibold text-gray-700 mb-1"> |
| 242 | {getTranslation("phoneNumber")} |
| 243 | </p> |
| 244 | <a |
| 245 | href={`tel:${shop.phone}`} |
| 246 | className="text-sm text-blue-600 hover:underline" |
| 247 | onClick={(e) => e.stopPropagation()} |
| 248 | > |
| 249 | {shop.phone} |
| 250 | </a> |
| 251 | </div> |
| 252 | </div> |
| 253 | </div> |
| 254 | |
| 255 | {/* Opening Hours */} |
| 256 | <div className="mb-6"> |
| 257 | <h3 className="text-lg font-bold text-gray-900 mb-3"> |
| 258 | {getTranslation("openingHours")} |
| 259 | </h3> |
| 260 | <div className="space-y-2"> |
| 261 | {openingHours.map((item, index) => ( |
| 262 | <div key={index} className="flex justify-between text-sm"> |
| 263 | <span className="text-gray-600">{item.day}</span> |
| 264 | <span className="font-medium text-gray-900"> |
| 265 | {item.hours} |
| 266 | </span> |
| 267 | </div> |
| 268 | ))} |
| 269 | </div> |
| 270 | </div> |
| 271 | |
| 272 | {/* Services */} |
| 273 | <div className="pb-2"> |
| 274 | <h3 className="text-lg font-bold text-gray-900 mb-3"> |
| 275 | {getTranslation("services")} |
| 276 | </h3> |
| 277 | <div className="flex flex-wrap gap-2"> |
| 278 | {shop.types.map((type, index) => ( |
| 279 | <span |
| 280 | key={index} |
| 281 | className="bg-blue-100 text-blue-800 px-3 py-1.5 rounded-full text-sm font-medium hover:bg-blue-200 transition-colors" |
| 282 | > |
| 283 | {type} |
| 284 | </span> |
| 285 | ))} |
| 286 | </div> |
| 287 | </div> |
| 288 | </div> |
| 289 | </div> |
| 290 | </div> |
| 291 | ); |
| 292 | } |