Added translation to all site with edit style

This commit is contained in:
Rahaf
2026-07-01 15:43:58 +03:00
parent 3052209df8
commit dab5b62fb7
56 changed files with 5136 additions and 3190 deletions

View File

@ -1,6 +1,7 @@
"use client";
import { useState, useEffect, useMemo, useRef } from "react";
import { useTranslation } from "react-i18next";
import { motion, AnimatePresence } from "framer-motion";
import toast, { Toaster } from "react-hot-toast";
import Link from "next/link";
@ -149,62 +150,9 @@ function buildImageUrl(img) {
return `${apiBase}${img.startsWith("/") ? "" : "/Pictures/"}${img}`;
}
const serviceLabels = {
Electricity: "كهرباء",
Internet: "إنترنت",
Heating: "تدفئة",
Water: "ماء",
Pool: "مسبح",
PrivateGarden: "حديقة خاصة",
Parking: "موقف سيارات",
Security247: "حراسة 24 س",
CentralHeating: "تدفئة مركزية",
CentralAirConditioning: "تكييف مركزي",
EquippedKitchen: "مطبخ مجهز",
MaidsRoom: "غرفة خادمة",
Elevator: "مصعد",
Gym: "نادي رياضي",
Sauna: "ساونا",
Jacuzzi: "جاكوزي",
Balcony: "بلكونة",
Rooftop: "سطح",
Furnished: "مفروش",
AirConditioning: "تكييف",
SatelliteTV: "تلفاز",
Fireplace: "مدفأة",
StudyRoom: "غرفة دراسة",
Storage: "مستودع",
Laundry: "غرفة غسيل",
SmartHome: "منزل ذكي",
};
const termLabels = {
NoSmoking: "ممنوع التدخين",
NoAnimals: "ممنوع الحيوانات الأليفة",
NoParties: "ممنوع الحفلات",
NoAlcohol: "ممنوع الكحول",
SuitableForChildren: "مناسب للأطفال",
SuitableForFamilies: "مناسب للعائلات",
SuitableForStudents: "مناسب للطلاب",
SuitableForElderly: "مناسب لكبار السن",
OnlyFemales: "إناث فقط",
OnlyMales: "ذكور فقط",
};
const proximityLabels = {
School: "مدرسة",
Hospital: "مستشفى",
Restaurant: "مطعم",
University: "جامعة",
Park: "حديقة",
Mall: "مركز تسوق",
Supermarket: "سوبر ماركت",
Pharmacy: "صيدلية",
Mosque: "مسجد",
Bank: "بنك",
Airport: "مطار",
BusStation: "موقف باص",
};
const svcKey = (k) => "propertyService." + k.charAt(0).toLowerCase() + k.slice(1);
const termKey = (k) => "propertyTerm." + k.charAt(0).toLowerCase() + k.slice(1);
const proxKey = (k) => "proximity." + k.toLowerCase();
function mapApiDetail(item) {
if (!item) return null;
@ -231,30 +179,10 @@ function mapApiDetail(item) {
BuildingTypeKeys[info.buildingType] ??
BuildingTypeKeys[item.type] ??
"apartment";
const typeLabel =
{
apartment: "شقة",
villa: "فيلا",
house: "بيت",
sweet: "سويت",
studio: "استوديو",
office: "مكتب",
shop: "متجر",
warehouse: "مستودع",
farms: "مزرعة",
room: "غرفة ضمن شقة",
}[propType] || "عقار";
const status =
PropertyStatusKeys[info.status] ??
PropertyStatusKeys[item.status] ??
"available";
const statusLabel =
{
available: "متاح",
booked: "محجوز",
notAvailable: "غير متاح",
maintenance: "صيانة",
}[status] || "متاح";
const rawImages = Array.isArray(info.images) ? info.images : [];
const photosFallback = Array.isArray(details.photos) ? details.photos : [];
@ -312,10 +240,10 @@ function mapApiDetail(item) {
return {
id: item.id,
propertyInformationId: info.id,
title: details.description || info.address || `عقار #${item.id}`,
title: details.description || info.address || "",
description: details.description || info.description || "",
type: propType,
typeLabel,
typeLabel: propType,
price,
priceUnit,
priceDisplay: { daily: dailyPrice, monthly: monthlyPrice, sale: salePrice },
@ -324,7 +252,7 @@ function mapApiDetail(item) {
propertyCondition,
furnished,
location: {
city: extractCity(info.address) || "دمشق",
city: extractCity(info.address) || "damascus",
address: info.address || "",
lat: parseFloat(info.cordsX) || 0,
lng: parseFloat(info.cordsY) || 0,
@ -337,7 +265,7 @@ function mapApiDetail(item) {
balconies,
images,
status,
statusLabel,
statusLabel: status,
services,
terms,
proximity,
@ -355,6 +283,7 @@ function mapApiDetail(item) {
}
export default function PropertyDetailsPage() {
const { t, i18n } = useTranslation();
const params = useParams();
const router = useRouter();
const { isFavorite, addFavorite, removeFavorite } = useFavorites();
@ -483,7 +412,7 @@ export default function PropertyDetailsPage() {
setContactInfo(info);
setShowContact(true);
} catch (err) {
toast.error("فشل تحميل معلومات الاتصال");
toast.error(t("contactLoadFailed"));
}
};
@ -501,7 +430,7 @@ export default function PropertyDetailsPage() {
await addFavorite(property.id);
}
} catch (err) {
toast.error("حدث خطأ");
toast.error(t("errorOccurred"));
} finally {
setFavLoading(false);
}
@ -513,7 +442,7 @@ export default function PropertyDetailsPage() {
return;
}
if (!bookingDates.start || !bookingDates.end) {
setBookingError("يرجى تحديد تاريخ البداية والنهاية");
setBookingError(t("validationDateRequired"));
return;
}
setBookingLoading(true);
@ -525,29 +454,23 @@ export default function PropertyDetailsPage() {
bookingDates.end,
);
setBookingSuccess(true);
toast.success("تم إرسال طلب الحجز بنجاح");
toast.success(t("bookingSentSuccess"));
} catch (err) {
setBookingError(err.message || "فشل الحجز");
setBookingError(err.message || t("bookingFailed"));
} finally {
setBookingLoading(false);
}
};
const MONTHS_AR = [
"يناير",
"فبراير",
"مارس",
"أبريل",
"مايو",
"يونيو",
"يوليو",
"أغسطس",
"سبتمبر",
"أكتوبر",
"نوفمبر",
"ديسمبر",
const MONTH_KEYS = [
"month.january", "month.february", "month.march", "month.april",
"month.may", "month.june", "month.july", "month.august",
"month.september", "month.october", "month.november", "month.december",
];
const DAY_KEYS = [
"dayAbbr.sun", "dayAbbr.mon", "dayAbbr.tue", "dayAbbr.wed",
"dayAbbr.thu", "dayAbbr.fri", "dayAbbr.sat",
];
const DAYS_AR = ["ح", "ن", "ث", "ر", "خ", "ج", "س"];
const availableDatesSet = useMemo(() => {
const dates = new Set();
@ -592,7 +515,7 @@ export default function PropertyDetailsPage() {
return;
}
if (!selectedStart || !selectedEnd) {
setBookingError("يرجى تحديد تاريخ البداية والنهاية");
setBookingError(t("validationDateRequired"));
return;
}
setBookingLoading(true);
@ -603,9 +526,9 @@ export default function PropertyDetailsPage() {
const endDate = new Date(selectedEnd + "T00:00:00.000").toISOString();
await bookReservation(propInfoId, startDate, endDate);
setBookingSuccess(true);
toast.success("تم إرسال طلب الحجز بنجاح");
toast.success(t("bookingSentSuccess"));
} catch (err) {
setBookingError(err.message || "فشل الحجز");
setBookingError(err.message || t("bookingFailed"));
} finally {
setBookingLoading(false);
}
@ -636,7 +559,7 @@ export default function PropertyDetailsPage() {
<div className="min-h-screen bg-gray-50 flex items-center justify-center">
<div className="text-center">
<Loader2 className="w-12 h-12 text-amber-500 animate-spin mx-auto mb-4" />
<p className="text-gray-600">جاري تحميل العقار...</p>
<p className="text-gray-600">{t("loadingProperty")}</p>
</div>
</div>
);
@ -650,15 +573,15 @@ export default function PropertyDetailsPage() {
<Home className="w-12 h-12 text-amber-600" />
</div>
<h2 className="text-2xl font-bold text-gray-900 mb-2">
العقار غير موجود
{t("propertyNotFound")}
</h2>
<p className="text-gray-600 mb-6">لم يتم العثور على العقار المطلوب</p>
<p className="text-gray-600 mb-6">{t("propertyNotFoundDesc")}</p>
<Link
href="/properties"
className="inline-flex items-center gap-2 bg-amber-500 text-white px-6 py-3 rounded-xl font-medium hover:bg-amber-600"
>
<ArrowLeft className="w-5 h-5" />
العودة إلى العقارات
{t("backToProperties")}
</Link>
</div>
</div>
@ -679,7 +602,7 @@ export default function PropertyDetailsPage() {
: "daily";
return (
<div className="min-h-screen bg-gray-50" dir="rtl">
<div className="min-h-screen bg-gray-50" dir={i18n.language === 'ar' ? 'rtl' : 'ltr'}>
<Toaster position="top-center" reverseOrder={false} />
<div className="container mx-auto px-4 py-6">
@ -693,7 +616,7 @@ export default function PropertyDetailsPage() {
className="inline-flex items-center gap-2 text-gray-600 hover:text-amber-600 transition-colors"
>
<ArrowLeft className="w-5 h-5" />
العودة إلى العقارات
{t("backToProperties")}
</Link>
</motion.div>
@ -723,15 +646,15 @@ export default function PropertyDetailsPage() {
>
<div className="text-center">
<ImageIcon className="w-20 h-20 text-gray-500 mx-auto mb-2" />
<p className="text-gray-400 text-sm">لا توجد صور</p>
<p className="text-gray-400 text-sm">{t("noImages")}</p>
</div>
</div>
)}
{isMostRequested && (
<div className="absolute top-4 right-4 z-10">
<span className="bg-gradient-to-l from-amber-500 to-amber-600 text-white px-3 py-1.5 rounded-full text-xs font-bold shadow-lg flex items-center gap-1">
<Star className="w-3 h-3 fill-white" /> الأكثر طلباً
<span className="bg-gradient-to-l from-amber-500 to-amber-600 text-white px-3 py-1.5 rounded-full text-xs font-bold shadow-lg flex items-center gap-1">
<Star className="w-3 h-3 fill-white" /> {t("mostRequested")}
</span>
</div>
)}
@ -765,10 +688,10 @@ export default function PropertyDetailsPage() {
<div className="absolute bottom-4 right-4 flex gap-2 z-10">
<span className="bg-black/70 text-white px-3 py-1 rounded-full text-sm font-medium backdrop-blur-sm">
{property.statusLabel}
{t("propertyStatus." + property.statusLabel)}
</span>
<span className="bg-black/70 text-white px-3 py-1 rounded-full text-sm font-medium backdrop-blur-sm">
{property.typeLabel}
{t("buildingType." + property.typeLabel)}
</span>
</div>
<div className="absolute bottom-4 left-4 bg-black/70 text-white px-3 py-1 rounded-full text-xs backdrop-blur-sm z-10">
@ -808,21 +731,21 @@ export default function PropertyDetailsPage() {
<div>
<div className="flex items-center gap-1.5 mb-1 flex-wrap">
<span className="px-2 py-0.5 bg-amber-100 text-amber-800 rounded-full text-xs">
{property.typeLabel}
{t("buildingType." + property.typeLabel)}
</span>
<span
className={`px-2 py-0.5 rounded-full text-xs ${property.status === "available" ? "bg-green-100 text-green-800" : "bg-yellow-100 text-yellow-800"}`}
>
{property.statusLabel}
{t("propertyStatus." + property.statusLabel)}
</span>
{property.isRent && property.displayType && (
<span className="px-2 py-0.5 bg-blue-100 text-blue-800 rounded-full text-xs">
{(() => {
const dt = property.displayType.toLowerCase();
if (dt === "both" || dt.includes("both"))
return "يومي وشهري";
if (dt.includes("daily")) return "يومي";
if (dt.includes("monthly")) return "شهري";
return t("dailyAndMonthly");
if (dt.includes("daily")) return t("rentType.daily");
if (dt.includes("monthly")) return t("rentType.monthly");
return dt;
})()}
</span>
@ -830,16 +753,16 @@ export default function PropertyDetailsPage() {
<span
className={`px-2 py-0.5 rounded-full text-xs ${property.furnished ? "bg-purple-100 text-purple-800" : "bg-gray-100 text-gray-600"}`}
>
{property.furnished ? "مفروش" : "غير مفروش"}
{property.furnished ? t("rentCondition.furnished") : t("rentCondition.unfurnished")}
</span>
</div>
<h1 className="text-xl font-bold text-gray-900">
{property.title}
{property.title || t("propertyWithId", { id: property.id })}
</h1>
<div className="flex items-center gap-1 text-gray-500 text-xs mt-0.5">
<MapPin className="w-3 h-3" />
<span>
{property.location.address || property.location.city}
{property.location.address || t("city." + property.location.city)}
</span>
</div>
</div>
@ -863,7 +786,7 @@ export default function PropertyDetailsPage() {
<span className="text-2xl font-bold text-amber-600">
{formatCurrency(property.priceDisplay.monthly)}
</span>
<span className="text-gray-500 mr-1">ل.س / شهرياً</span>
<span className="text-gray-500 mr-1">{t("sypPerMonth")}</span>
</div>
)}
{property.priceDisplay.daily > 0 && (
@ -871,13 +794,13 @@ export default function PropertyDetailsPage() {
<span className="text-2xl font-bold text-amber-600">
{formatCurrency(property.priceDisplay.daily)}
</span>
<span className="text-gray-500 mr-1">ل.س / يومياً</span>
<span className="text-gray-500 mr-1">{t("sypPerDay")}</span>
</div>
)}
{property.deposit > 0 && (
<div className="text-sm text-gray-500">
<span className="font-medium">تأمين:</span>{" "}
{formatCurrency(property.deposit)} ل.س
<span className="font-medium">{t("depositLabel")}</span>{" "}
{formatCurrency(property.deposit)} {t("syp")}
</div>
)}
</div>
@ -886,8 +809,8 @@ export default function PropertyDetailsPage() {
<span className="text-2xl font-bold text-blue-600">
{formatCurrency(property.price)}
</span>
<span className="text-gray-500 mr-1">ل.س</span>
<span className="text-sm text-gray-400 mr-2">للبيع</span>
<span className="text-gray-500 mr-1">{t("syp")}</span>
<span className="text-sm text-gray-400 mr-2">{t("forSale")}</span>
</div>
)}
</div>
@ -900,7 +823,7 @@ export default function PropertyDetailsPage() {
<div className="font-bold text-gray-900 text-sm">
{property.bedrooms}
</div>
<div className="text-[10px] text-gray-500">غرف نوم</div>
<div className="text-[10px] text-gray-500">{t("bedrooms")}</div>
</div>
)}
{property.bathrooms > 0 && (
@ -909,7 +832,7 @@ export default function PropertyDetailsPage() {
<div className="font-bold text-gray-900 text-sm">
{property.bathrooms}
</div>
<div className="text-[10px] text-gray-500">حمامات</div>
<div className="text-[10px] text-gray-500">{t("bathrooms")}</div>
</div>
)}
{property.area > 0 && (
@ -918,7 +841,7 @@ export default function PropertyDetailsPage() {
<div className="font-bold text-gray-900 text-sm">
{property.area}
</div>
<div className="text-[10px] text-gray-500">م²</div>
<div className="text-[10px] text-gray-500">{t("sqm")}</div>
</div>
)}
{property.floor > 0 && (
@ -927,7 +850,7 @@ export default function PropertyDetailsPage() {
<div className="font-bold text-gray-900 text-sm">
{property.floor}
</div>
<div className="text-[10px] text-gray-500">طابق</div>
<div className="text-[10px] text-gray-500">{t("floor")}</div>
</div>
)}
{property.salons > 0 && (
@ -936,7 +859,7 @@ export default function PropertyDetailsPage() {
<div className="font-bold text-gray-900 text-sm">
{property.salons}
</div>
<div className="text-[10px] text-gray-500">صالونات</div>
<div className="text-[10px] text-gray-500">{t("salons")}</div>
</div>
)}
{property.balconies > 0 && (
@ -945,7 +868,7 @@ export default function PropertyDetailsPage() {
<div className="font-bold text-gray-900 text-sm">
{property.balconies}
</div>
<div className="text-[10px] text-gray-500">بلكونات</div>
<div className="text-[10px] text-gray-500">{t("balconies")}</div>
</div>
)}
{avgRating !== null && avgRating > 0 && (
@ -954,7 +877,7 @@ export default function PropertyDetailsPage() {
<div className="font-bold text-gray-900 text-sm">
{avgRating.toFixed(1)}
</div>
<div className="text-[10px] text-gray-500">التقييم</div>
<div className="text-[10px] text-gray-500">{t("rateAndReview")}</div>
</div>
)}
{property.bookedCount > 0 && (
@ -963,7 +886,7 @@ export default function PropertyDetailsPage() {
<div className="font-bold text-gray-900 text-sm">
{property.bookedCount}
</div>
<div className="text-[10px] text-gray-500">حجوزات</div>
<div className="text-[10px] text-gray-500">{t("bookingCount")}</div>
</div>
)}
</div>
@ -972,7 +895,7 @@ export default function PropertyDetailsPage() {
{property.description && (
<div className="mb-4">
<h3 className="font-bold text-gray-900 mb-1 text-sm">
الوصف
{t("description")}
</h3>
<p className="text-gray-600 text-sm leading-relaxed">
{property.description}
@ -984,17 +907,17 @@ export default function PropertyDetailsPage() {
<div className="flex flex-wrap gap-1.5 mb-4">
{property.isSmokeAllow && (
<span className="px-2 py-0.5 bg-gray-100 text-gray-700 rounded-full text-xs border flex items-center gap-1">
<Wind className="w-3 h-3" /> يسمح بالتدخين
<Wind className="w-3 h-3" /> {t("smokingAllowed")}
</span>
)}
{!property.isSmokeAllow && (
<span className="px-2 py-0.5 bg-gray-100 text-gray-700 rounded-full text-xs border flex items-center gap-1">
<Ban className="w-3 h-3" /> ممنوع التدخين
<Ban className="w-3 h-3" /> {t("propertyTerm.noSmoking")}
</span>
)}
{property.isVisitorAllow && (
<span className="px-2 py-0.5 bg-gray-100 text-gray-700 rounded-full text-xs border flex items-center gap-1">
<Users className="w-3 h-3" /> يسمح بالزوار
<Users className="w-3 h-3" /> {t("visitorsAllowed")}
</span>
)}
{property.specializedFor && (
@ -1011,7 +934,7 @@ export default function PropertyDetailsPage() {
: Object.keys(property.services).length > 0) && (
<div className="mb-4">
<h3 className="font-bold text-gray-900 mb-2 text-sm">
الخدمات
{t("services")}
</h3>
<div className="flex flex-wrap gap-1.5">
{Array.isArray(property.services)
@ -1020,7 +943,7 @@ export default function PropertyDetailsPage() {
key={i}
className="px-3 py-1 bg-green-50 text-green-700 rounded-full text-sm border border-green-200 flex items-center gap-1"
>
{serviceLabels[svc] || svc}
{t(svcKey(svc))}
</span>
))
: Object.entries(property.services).map(
@ -1037,7 +960,7 @@ export default function PropertyDetailsPage() {
key={key}
className="px-3 py-1 bg-green-50 text-green-700 rounded-full text-sm border border-green-200 flex items-center gap-1"
>
{serviceLabels[key] || key}
{t(svcKey(key))}
{detail && (
<span className="text-green-400">
· {detail}
@ -1056,7 +979,7 @@ export default function PropertyDetailsPage() {
<div className="mb-4 bg-blue-50 rounded-xl p-3">
<h3 className="font-bold text-gray-900 mb-2 text-sm flex items-center gap-2">
<Info className="w-4 h-4 text-blue-500" />
تفاصيل الغرفة
{t("roomDetails")}
</h3>
<div className="grid grid-cols-2 md:grid-cols-3 gap-2">
{(() => {
@ -1064,117 +987,117 @@ export default function PropertyDetailsPage() {
const items = [];
if (rd.areaType)
items.push({
label: "نوع المساحة",
label: t("roomDetail.areaType"),
value:
rd.areaType === "private room"
? "غرفة خاصة"
? t("roomDetail.privateRoom")
: rd.areaType === "shared room"
? "غرفة مشتركة"
? t("roomDetail.sharedRoom")
: rd.areaType,
});
if (rd.peopleAllowed)
items.push({
label: "عدد الأشخاص",
label: t("roomDetail.numberOfPeople"),
value: rd.peopleAllowed,
});
if (rd.furnitureDetails || rd.furniture)
items.push({
label: "الأثاث",
label: t("roomDetail.furniture"),
value: rd.furnitureDetails || rd.furniture,
});
if (rd.entranceType)
items.push({
label: "نوع المدخل",
label: t("roomDetail.entranceType"),
value:
rd.entranceType === "shared entrance"
? "مدخل مشترك"
? t("roomDetail.sharedEntrance")
: rd.entranceType === "independent entrance"
? "مدخل مستقل"
? t("roomDetail.independentEntrance")
: rd.entranceType,
});
if (rd.bathroomType)
items.push({
label: "الحمام",
label: t("roomDetail.bathroom"),
value:
rd.bathroomType === "room specific"
? "خاص بالغرفة"
? t("roomDetail.bathroomPrivate")
: rd.bathroomType === "shared"
? "مشترك"
? t("roomDetail.bathroomShared")
: rd.bathroomType,
});
if (rd.kitchenType)
items.push({
label: "المطبخ",
label: t("roomDetail.kitchen"),
value:
rd.kitchenType === "shared"
? "مشترك"
? t("roomDetail.kitchenShared")
: rd.kitchenType === "not available"
? "غير متوفر"
? t("roomDetail.kitchenNotAvailable")
: rd.kitchenType,
});
if (rd.homeResidentsCount ?? rd.residents)
items.push({
label: "عدد السكان",
label: t("roomDetail.residents"),
value: rd.homeResidentsCount ?? rd.residents,
});
if (rd.currentPopulationGender)
items.push({
label: "جنس السكان",
label: t("roomDetail.gender"),
value:
rd.currentPopulationGender === "men"
? "رجال"
? t("roomDetail.men")
: rd.currentPopulationGender === "women"
? "نساء"
? t("roomDetail.women")
: rd.currentPopulationGender === "family"
? "عائلة"
? t("roomDetail.family")
: rd.currentPopulationGender,
});
if (rd.dedicatedTo)
items.push({
label: "مخصص لـ",
label: t("roomDetail.dedicatedTo"),
value:
rd.dedicatedTo === "men only"
? "رجال فقط"
? t("roomDetail.menOnly")
: rd.dedicatedTo === "women only"
? "نساء فقط"
? t("roomDetail.womenOnly")
: rd.dedicatedTo === "families only"
? "عائلات فقط"
? t("roomDetail.familiesOnly")
: rd.dedicatedTo === "everyone"
? "الجميع"
? t("roomDetail.everyone")
: rd.dedicatedTo,
});
if (rd.hasRestrictedOwnerAreas !== undefined)
items.push({
label: "مناطق ممنوعة",
value: rd.hasRestrictedOwnerAreas ? "نعم" : "لا",
label: t("roomDetail.restrictedAreas"),
value: rd.hasRestrictedOwnerAreas ? t("roomDetail.yes") : t("roomDetail.no"),
});
if (rd.hasChildren !== undefined)
items.push({
label: "أطفال",
value: rd.hasChildren ? "مسموح" : "غير مسموح",
label: t("roomDetail.children"),
value: rd.hasChildren ? t("roomDetail.allowed") : t("roomDetail.notAllowed"),
});
if (rd.hasPets !== undefined)
items.push({
label: "حيوانات أليفة",
value: rd.hasPets ? "مسموح" : "غير مسموح",
label: t("roomDetail.pets"),
value: rd.hasPets ? t("roomDetail.allowed") : t("roomDetail.notAllowed"),
});
if (rd.languageDialect)
items.push({
label: "اللغة",
label: t("roomDetail.language"),
value: rd.languageDialect,
});
if (rd.visitorsAllowed !== undefined)
items.push({
label: "الزوار",
value: rd.visitorsAllowed ? "مسموح" : "ممنوع",
label: t("roomDetail.visitors"),
value: rd.visitorsAllowed ? t("roomDetail.allowed") : t("roomDetail.forbidden"),
});
if (rd.quietTimesEnabled ?? rd.quietTimes)
items.push({
label: "أوقات الهدوء",
label: t("roomDetail.quietHours"),
value:
rd.quietTimesDetails ||
rd.quietTimes ||
(rd.quietTimesEnabled ? "مفعلة" : ""),
(rd.quietTimesEnabled ? t("roomDetail.quietHoursEnabled") : ""),
});
return items.map((item, i) => (
<div
@ -1198,7 +1121,7 @@ export default function PropertyDetailsPage() {
{Object.keys(property.proximity).length > 0 && (
<div className="mb-4">
<h3 className="font-bold text-gray-900 mb-2 text-sm">
القرب من الخدمات
{t("proximityToServices")}
</h3>
<div className="grid grid-cols-2 md:grid-cols-3 gap-1.5">
{Object.entries(property.proximity).map(([key, val]) => {
@ -1239,10 +1162,10 @@ export default function PropertyDetailsPage() {
)}
<div>
<div className="text-xs font-medium text-gray-900">
{proximityLabels[key] || key}
{t(proxKey(key))}
</div>
<div className="text-[10px] text-gray-500">
{dist} {typeof dist === "number" ? "كم" : ""}
{dist} {typeof dist === "number" ? t("km") : ""}
</div>
</div>
</div>
@ -1256,7 +1179,7 @@ export default function PropertyDetailsPage() {
{Object.keys(property.terms).length > 0 && (
<div className="mb-4">
<h3 className="font-bold text-gray-900 mb-2 text-sm">
الشروط
{t("terms")}
</h3>
<div className="grid grid-cols-1 md:grid-cols-2 gap-1.5">
{Object.entries(property.terms).map(([key, val]) => {
@ -1272,7 +1195,7 @@ export default function PropertyDetailsPage() {
<Check className="w-3.5 h-3.5 text-green-500 flex-shrink-0" />
)}
<span className="text-xs text-gray-700">
{termLabels[key] || key}
{t(termKey(key))}
</span>
</div>
);
@ -1298,7 +1221,7 @@ export default function PropertyDetailsPage() {
</div>
<div className="p-3 bg-amber-50 text-center text-sm text-amber-700 flex items-center justify-center gap-2">
<Info className="w-4 h-4" />
<span>موقع تقريبي يظهر الموقع الدقيق بعد تأكيد الحجز</span>
<span>{t("approximateLocation")}</span>
</div>
<div className="p-3 border-t border-gray-100">
<a
@ -1308,7 +1231,7 @@ export default function PropertyDetailsPage() {
className="flex items-center justify-center gap-2 text-blue-600 hover:text-blue-700 font-medium text-sm"
>
<ExternalLink className="w-4 h-4" />
فتح في Google Maps
{t("openInGoogleMaps")}
</a>
</div>
</motion.div>
@ -1340,10 +1263,10 @@ export default function PropertyDetailsPage() {
<Home className="w-7 h-7 text-gray-400" />
</div>
<h4 className="font-bold text-gray-700 text-sm mb-1">
هذا عقارك
{t("thisIsYourProperty")}
</h4>
<p className="text-xs text-gray-500">
لا يمكنك حجز عقارك الخاص
{t("cannotBookOwnProperty")}
</p>
</div>
) : bookingSuccess ? (
@ -1352,10 +1275,10 @@ export default function PropertyDetailsPage() {
<Check className="w-7 h-7 text-green-600" />
</div>
<h4 className="font-bold text-green-700 text-sm mb-1">
تم إرسال طلب الحجز
{t("bookingRequestSent")}
</h4>
<p className="text-xs text-gray-500">
سيتم مراجعة طلبك من قبل المالك
{t("bookingRequestWillBeReviewed")}
</p>
</div>
) : (
@ -1368,10 +1291,10 @@ export default function PropertyDetailsPage() {
className={`p-2.5 rounded-xl text-center border-2 transition-all ${effectivePricingMode === "daily" ? "border-amber-500 bg-amber-50" : "border-gray-200 hover:border-gray-300"}`}
>
<div className="text-xs font-bold text-gray-900">
إيجار يومي
{t("dailyRentLabel")}
</div>
<div className="text-sm font-bold text-amber-600">
{formatCurrency(property.priceDisplay.daily)} ل.س
{formatCurrency(property.priceDisplay.daily)} {t("syp")}
</div>
</button>
<button
@ -1379,10 +1302,10 @@ export default function PropertyDetailsPage() {
className={`p-2.5 rounded-xl text-center border-2 transition-all ${effectivePricingMode === "monthly" ? "border-amber-500 bg-amber-50" : "border-gray-200 hover:border-gray-300"}`}
>
<div className="text-xs font-bold text-gray-900">
إيجار شهري
{t("monthlyRentLabel")}
</div>
<div className="text-sm font-bold text-amber-600">
{formatCurrency(property.priceDisplay.monthly)} ل.س
{formatCurrency(property.priceDisplay.monthly)} {t("syp")}
</div>
</button>
</div>
@ -1396,7 +1319,7 @@ export default function PropertyDetailsPage() {
<div
className={`w-1.5 h-1.5 rounded-full ${bookingStep === "entry" ? "bg-amber-500" : "bg-gray-400"}`}
/>
تحديد تاريخ البداية
{t("selectStartDate")}
</div>
<div className="text-gray-300 text-xs"></div>
<div
@ -1405,7 +1328,7 @@ export default function PropertyDetailsPage() {
<div
className={`w-1.5 h-1.5 rounded-full ${bookingStep === "exit" ? "bg-amber-500" : "bg-gray-400"}`}
/>
تحديد تاريخ النهاية
{t("selectEndDate")}
</div>
</div>
@ -1420,7 +1343,7 @@ export default function PropertyDetailsPage() {
<ChevronRight className="w-4 h-4 text-gray-600" />
</button>
<span className="text-sm font-bold text-gray-900">
{MONTHS_AR[calendarMonth]} {calendarYear}
{t(MONTH_KEYS[calendarMonth])} {calendarYear}
</span>
<button
onClick={() => navigateMonth(1)}
@ -1431,12 +1354,12 @@ export default function PropertyDetailsPage() {
</div>
<div className="grid grid-cols-7 mb-1">
{DAYS_AR.map((d, i) => (
{DAY_KEYS.map((dk, i) => (
<div
key={i}
className="text-center text-[10px] text-gray-400 font-medium py-1"
>
{d}
{t(dk)}
</div>
))}
</div>
@ -1509,7 +1432,7 @@ export default function PropertyDetailsPage() {
</button>
</div>
<div className="grid grid-cols-3 gap-2">
{MONTHS_AR.map((name, idx) => {
{MONTH_KEYS.map((mk, idx) => {
const monthStr = `${calendarYear}-${String(idx + 1).padStart(2, "0")}`;
const isSelStart =
selectedStart &&
@ -1548,7 +1471,7 @@ export default function PropertyDetailsPage() {
}}
className={`p-2.5 rounded-xl text-center text-xs font-medium transition-all border ${isSelStart || isSelEnd ? "bg-amber-500 text-white border-amber-500 shadow-sm" : inRange ? "bg-amber-100 text-amber-800 border-amber-200" : "bg-white text-gray-700 border-gray-200 hover:border-amber-300"}`}
>
{name}
{t(mk)}
</button>
);
})}
@ -1560,7 +1483,7 @@ export default function PropertyDetailsPage() {
{selectedStart && (
<div className="bg-gray-50 rounded-xl p-3 mb-3 space-y-1.5">
<div className="flex justify-between text-xs">
<span className="text-gray-500">تاريخ البداية</span>
<span className="text-gray-500">{t("startDateLabel")}</span>
<span className="font-medium text-gray-900">
{selectedStart}
</span>
@ -1569,7 +1492,7 @@ export default function PropertyDetailsPage() {
<>
<div className="flex justify-between text-xs">
<span className="text-gray-500">
تاريخ النهاية
{t("endDateLabel")}
</span>
<span className="font-medium text-gray-900">
{selectedEnd}
@ -1579,8 +1502,8 @@ export default function PropertyDetailsPage() {
<div className="flex justify-between text-xs">
<span className="text-gray-500">
{effectivePricingMode === "daily"
? "عدد الأيام"
: "عدد الأشهر"}
? t("numberOfDays")
: t("numberOfMonths")}
</span>
<span className="font-medium text-gray-900">
{effectivePricingMode === "daily"
@ -1601,7 +1524,7 @@ export default function PropertyDetailsPage() {
</span>
</div>
<div className="flex justify-between text-xs font-bold">
<span className="text-gray-700">المجموع</span>
<span className="text-gray-700">{t("total")}</span>
<span className="text-amber-600">
{formatCurrency(
effectivePricingMode === "daily"
@ -1623,14 +1546,14 @@ export default function PropertyDetailsPage() {
1) *
property.priceDisplay.monthly,
)}{" "}
ل.س
{t("syp")}
</span>
</div>
{property.deposit > 0 && (
<div className="flex justify-between text-xs">
<span className="text-gray-500">تأمين</span>
<span className="text-gray-500">{t("deposit")}</span>
<span className="font-medium text-gray-900">
{formatCurrency(property.deposit)} ل.س
{formatCurrency(property.deposit)} {t("syp")}
</span>
</div>
)}
@ -1657,7 +1580,7 @@ export default function PropertyDetailsPage() {
) : (
<Calendar className="w-4 h-4" />
)}
{bookingLoading ? "جاري الحجز..." : "تأكيد الحجز"}
{bookingLoading ? t("bookingInProgress") : t("confirmBooking")}
</button>
</>
)}
@ -1669,7 +1592,7 @@ export default function PropertyDetailsPage() {
<motion.div initial={{ opacity: 0, x: 20 }} animate={{ opacity: 1, x: 0 }} className="bg-white rounded-2xl p-5 shadow-sm border border-gray-200">
<div className="flex items-center gap-2 mb-3">
<Phone className="w-4 h-4 text-amber-500" />
<h3 className="font-bold text-gray-900">معلومات المالك</h3>
<h3 className="font-bold text-gray-900">{t("ownerInfo")}</h3>
</div>
{showContact && contactInfo ? (
@ -1690,7 +1613,7 @@ export default function PropertyDetailsPage() {
<button onClick={fetchContactInfo}
className="w-full bg-gray-800 hover:bg-gray-900 text-white py-2.5 rounded-xl font-medium text-sm transition-colors flex items-center justify-center gap-2">
<Phone className="w-4 h-4" />
عرض معلومات الاتصال
{t("showContactInfo")}
</button>
)}
</motion.div>
@ -1712,27 +1635,27 @@ export default function PropertyDetailsPage() {
<div className="w-16 h-16 bg-amber-100 rounded-full flex items-center justify-center mx-auto mb-4">
<LogIn className="w-8 h-8 text-amber-600" />
</div>
<h3 className="text-xl font-bold mb-2">تسجيل الدخول مطلوب</h3>
<h3 className="text-xl font-bold mb-2">{t("loginRequiredTitle")}</h3>
<p className="text-gray-500 mb-4">
للحجز أو إضافة المفضلة، سجل دخولك.
{t("loginRequiredDesc")}
</p>
<Link
href="/login"
className="block w-full bg-amber-500 text-white py-3 rounded-xl font-medium mb-2 hover:bg-amber-600"
>
تسجيل الدخول
{t("login")}
</Link>
<Link
href="/auth/choose-role"
className="block w-full bg-gray-100 py-3 rounded-xl font-medium hover:bg-gray-200"
>
إنشاء حساب
{t("createAccount")}
</Link>
<button
onClick={() => setShowLoginDialog(false)}
className="mt-3 text-gray-400 hover:text-gray-600"
>
إلغاء
{t("cancel")}
</button>
</div>
</div>

View File

@ -31,7 +31,7 @@ function mapProperty(item) {
const priceUnit = item.monthlyRent || item.MonthlyRent ? 'monthly' : 'daily';
const buildingType = info.buildingType ?? info.BuildingType ?? 0;
const type = { 0: 'apartment', 1: 'villa', 2: 'house' }[buildingType] || 'apartment';
const typeLabel = { 0: 'شقة', 1: 'فيلا', 2: 'بيت' }[buildingType] || 'عقار';
const typeLabel = { 0: 'apartment', 1: 'villa', 2: 'house' }[buildingType] || 'apartment';
const address = info.address || info.Address || '';
const bedrooms = info.numberOfBedRooms || info.NumberOfBedRooms || 0;
const bathrooms = info.numberOfBathRooms || info.NumberOfBathRooms || 0;