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

@ -2,14 +2,12 @@
import { useState, useEffect } from 'react';
import { motion } from 'framer-motion';
import { useRouter } from 'next/navigation';
import { useTranslation } from 'react-i18next';
import {
DollarSign,
TrendingUp,
Calendar,
Building,
Loader2,
ArrowLeft,
Star,
} from 'lucide-react';
import toast, { Toaster } from 'react-hot-toast';
@ -34,7 +32,7 @@ const StatCard = ({ title, value, icon: Icon, color, subtitle, isNA }) => (
);
export default function OwnerAccountBookPage() {
const router = useRouter();
const { t, i18n } = useTranslation();
const [user, setUser] = useState(null);
const [isLoading, setIsLoading] = useState(true);
const [stats, setStats] = useState({
@ -53,11 +51,9 @@ export default function OwnerAccountBookPage() {
useEffect(() => {
if (AuthService.isGuest()) {
router.push('/auth/choose-role');
return;
}
if (!AuthService.isOwner()) {
router.push('/');
return;
}
@ -93,18 +89,18 @@ export default function OwnerAccountBookPage() {
});
}
} catch {
toast.error('تعذر تحميل إحصائيات الحساب');
toast.error(t('accountBook.loadError'));
} finally {
setIsLoading(false);
}
}
fetchData();
}, [router]);
}, []);
const formatCurrency = (amount) => {
const num = Number(amount);
if (isNaN(num)) return 'N/A';
return num.toLocaleString() + ' ل.س';
return num.toLocaleString() + ' ' + t('currency-syp-suffix');
};
const formatNumber = (val) => {
@ -126,32 +122,23 @@ export default function OwnerAccountBookPage() {
<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('accountBook.loading')}</p>
</div>
</div>
);
}
return (
<div className="min-h-screen bg-gray-50 py-8" dir="rtl">
<div className="min-h-screen bg-gray-50 py-8" dir={i18n.language === 'ar' ? 'rtl' : 'ltr'}>
<Toaster position="top-center" reverseOrder={false} />
<div className="container mx-auto px-4 max-w-7xl">
<motion.div
initial={{ opacity: 0, y: -20 }}
animate={{ opacity: 1, y: 0 }}
className="flex flex-col md:flex-row justify-between items-start md:items-center mb-8 gap-4"
className="mb-8"
>
<div className="flex items-center gap-4">
<button
onClick={() => router.back()}
className="p-2 hover:bg-gray-200 rounded-xl transition-colors"
>
<ArrowLeft className="w-5 h-5 text-gray-600" />
</button>
<div>
<h1 className="text-3xl font-bold text-gray-900">دفتر الحسابات</h1>
</div>
</div>
<h1 className="text-3xl font-bold text-gray-900">{t('accountBook')}</h1>
<p className="text-gray-600">{t('financialRecords')}</p>
</motion.div>
<motion.div
@ -159,24 +146,24 @@ export default function OwnerAccountBookPage() {
animate={{ opacity: 1, y: 0 }}
className="mb-8"
>
<h2 className="text-xl font-bold text-gray-900 mb-4">نظرة مالية للمالك</h2>
<h2 className="text-xl font-bold text-gray-900 mb-4">{t('accountBook.ownerFinancialOverview')}</h2>
<div className="grid grid-cols-1 sm:grid-cols-3 gap-6">
<StatCard
title="الإيرادات"
title={t('accountBook.revenue')}
value={formatCurrency(stats.financialRevenue)}
icon={TrendingUp}
color="bg-emerald-500"
isNA={isNA(stats.financialRevenue)}
/>
<StatCard
title="العمولة"
title={t('accountBook.commission')}
value={formatCurrency(stats.financialCommission)}
icon={DollarSign}
color="bg-orange-500"
isNA={isNA(stats.financialCommission)}
/>
<StatCard
title="الرصيد المتبقي"
title={t('accountBook.remainingBalance')}
value={formatCurrency(stats.financialBalance)}
icon={DollarSign}
color="bg-blue-500"
@ -191,38 +178,38 @@ export default function OwnerAccountBookPage() {
transition={{ delay: 0.1 }}
className="mb-8"
>
<h2 className="text-xl font-bold text-gray-900 mb-4">إحصائيات العقارات المباشرة</h2>
<h2 className="text-xl font-bold text-gray-900 mb-4">{t('accountBook.directPropertyStats')}</h2>
<div className="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-5 gap-6">
<StatCard
title="الإيرادات"
title={t('accountBook.revenue')}
value={formatCurrency(stats.directRevenue)}
icon={TrendingUp}
color="bg-emerald-500"
isNA={isNA(stats.directRevenue)}
/>
<StatCard
title="العمولة"
title={t('accountBook.commission')}
value={formatCurrency(stats.directCommission)}
icon={DollarSign}
color="bg-orange-500"
isNA={isNA(stats.directCommission)}
/>
<StatCard
title="الرصيد المتبقي"
title={t('accountBook.remainingBalance')}
value={formatCurrency(stats.directBalance)}
icon={DollarSign}
color="bg-blue-500"
isNA={isNA(stats.directBalance)}
/>
<StatCard
title="التقييم العام"
title={t('accountBook.overallRating')}
value={formatRating(stats.directRating)}
icon={Star}
color="bg-purple-500"
isNA={isNA(stats.directRating)}
/>
<StatCard
title="مرات التأجيل"
title={t('accountBook.postponements')}
value={formatNumber(stats.directPostponements)}
icon={Calendar}
color="bg-red-500"

View File

@ -4,6 +4,7 @@ import { useState, useEffect } from 'react';
import { motion, AnimatePresence } from 'framer-motion';
import { useRouter } from 'next/navigation';
import Link from 'next/link';
import { useTranslation } from 'react-i18next';
import {
Calendar,
Home,
@ -37,6 +38,7 @@ import AuthService from '../../services/AuthService';
import Image from 'next/image';
const OwnerBookingCalendar = ({ property, onDateSelect, selectedDates }) => {
const { t } = useTranslation();
const [currentMonth, setCurrentMonth] = useState(new Date());
const [hoverDate, setHoverDate] = useState(null);
@ -53,8 +55,13 @@ const OwnerBookingCalendar = ({ property, onDateSelect, selectedDates }) => {
).getDay();
const monthNames = [
'يناير', 'فبراير', 'مارس', 'إبريل', 'مايو', 'يونيو',
'يوليو', 'أغسطس', 'سبتمبر', 'أكتوبر', 'نوفمبر', 'ديسمبر'
t('january'), t('february'), t('march'), t('april'), t('may'), t('june'),
t('july'), t('august'), t('september'), t('october'), t('november'), t('december')
];
const dayNames = [
t('dayFull.sunday'), t('dayFull.monday'), t('dayFull.tuesday'), t('dayFull.wednesday'),
t('dayFull.thursday'), t('dayFull.friday'), t('dayFull.saturday')
];
const isDateBooked = (date) => {
@ -134,7 +141,6 @@ const OwnerBookingCalendar = ({ property, onDateSelect, selectedDates }) => {
return (
<div className="bg-white rounded-2xl shadow-sm border border-gray-200 p-6">
{/* رأس التقويم */}
<div className="flex items-center justify-between mb-6">
<button
onClick={() => setCurrentMonth(new Date(currentMonth.getFullYear(), currentMonth.getMonth() - 1, 1))}
@ -156,15 +162,10 @@ const OwnerBookingCalendar = ({ property, onDateSelect, selectedDates }) => {
</button>
</div>
{/* أيام الأسبوع */}
<div className="grid grid-cols-7 gap-1 mb-3 text-center text-sm font-medium text-gray-500">
<div>أحد</div>
<div>إثنين</div>
<div>ثلاثاء</div>
<div>أربعاء</div>
<div>خميس</div>
<div>جمعة</div>
<div>سبت</div>
{dayNames.map((name, i) => (
<div key={i}>{name}</div>
))}
</div>
<div className="grid grid-cols-7 gap-1">
@ -174,19 +175,19 @@ const OwnerBookingCalendar = ({ property, onDateSelect, selectedDates }) => {
<div className="flex flex-wrap gap-4 mt-6 pt-4 border-t border-gray-200 text-xs">
<div className="flex items-center gap-2">
<div className="w-3 h-3 bg-red-100 rounded" />
<span className="text-gray-600">محجوز</span>
<span className="text-gray-600">{t('booked')}</span>
</div>
<div className="flex items-center gap-2">
<div className="w-3 h-3 bg-amber-500 rounded" />
<span className="text-gray-600">محدد</span>
<span className="text-gray-600">{t('selected')}</span>
</div>
<div className="flex items-center gap-2">
<div className="w-3 h-3 bg-amber-100 rounded" />
<span className="text-gray-600">ضمن الفترة</span>
<span className="text-gray-600">{t('inRange')}</span>
</div>
<div className="flex items-center gap-2">
<div className="w-3 h-3 border-2 border-amber-500 rounded" />
<span className="text-gray-600">اليوم</span>
<span className="text-gray-600">{t('today')}</span>
</div>
</div>
</div>
@ -194,16 +195,18 @@ const OwnerBookingCalendar = ({ property, onDateSelect, selectedDates }) => {
};
const BookingCard = ({ booking, onViewDetails, onContact }) => {
const { t } = useTranslation();
const formatCurrency = (amount) => {
return amount?.toLocaleString() + ' ل.س';
return amount?.toLocaleString() + ' ' + t('syp');
};
const getStatusBadge = (status) => {
const statusConfig = {
pending: { label: 'قيد الانتظار', color: 'bg-yellow-100 text-yellow-800', icon: Clock },
confirmed: { label: 'مؤكد', color: 'bg-green-100 text-green-800', icon: CheckCircle },
cancelled: { label: 'ملغي', color: 'bg-red-100 text-red-800', icon: XCircle },
completed: { label: 'منتهي', color: 'bg-gray-100 text-gray-800', icon: CheckCircle }
pending: { label: t('pending'), color: 'bg-yellow-100 text-yellow-800', icon: Clock },
confirmed: { label: t('confirmed'), color: 'bg-green-100 text-green-800', icon: CheckCircle },
cancelled: { label: t('cancelled'), color: 'bg-red-100 text-red-800', icon: XCircle },
completed: { label: t('completed'), color: 'bg-gray-100 text-gray-800', icon: CheckCircle }
};
const config = statusConfig[status] || statusConfig.pending;
@ -237,7 +240,7 @@ const BookingCard = ({ booking, onViewDetails, onContact }) => {
</div>
<div className="text-left">
<div className="text-lg font-bold text-amber-600">{formatCurrency(booking.totalAmount)}</div>
<div className="text-xs text-gray-500">إجمالي المبلغ</div>
<div className="text-xs text-gray-500">{t('totalAmount')}</div>
</div>
</div>
@ -261,18 +264,18 @@ const BookingCard = ({ booking, onViewDetails, onContact }) => {
<div className="grid grid-cols-3 gap-3 mb-4 text-center">
<div className="bg-gray-50 p-2 rounded-lg">
<Calendar className="w-4 h-4 text-amber-500 mx-auto mb-1" />
<div className="text-xs text-gray-500">من</div>
<div className="text-xs text-gray-500">{t('from')}</div>
<div className="text-sm font-medium">{booking.startDate}</div>
</div>
<div className="bg-gray-50 p-2 rounded-lg">
<Calendar className="w-4 h-4 text-amber-500 mx-auto mb-1" />
<div className="text-xs text-gray-500">إلى</div>
<div className="text-xs text-gray-500">{t('to')}</div>
<div className="text-sm font-medium">{booking.endDate}</div>
</div>
<div className="bg-gray-50 p-2 rounded-lg">
<Clock className="w-4 h-4 text-amber-500 mx-auto mb-1" />
<div className="text-xs text-gray-500">المدة</div>
<div className="text-sm font-medium">{booking.days} يوم</div>
<div className="text-xs text-gray-500">{t('duration')}</div>
<div className="text-sm font-medium">{booking.days} {t('days')}</div>
</div>
</div>
@ -282,15 +285,8 @@ const BookingCard = ({ booking, onViewDetails, onContact }) => {
className="flex-1 bg-gray-100 text-gray-700 py-2 rounded-xl text-sm font-medium hover:bg-gray-200 transition-colors flex items-center justify-center gap-2"
>
<Eye className="w-4 h-4" />
التفاصيل
{t('details')}
</button>
{/* <button
onClick={() => onContact(booking)}
className="flex-1 bg-amber-500 text-white py-2 rounded-xl text-sm font-medium hover:bg-amber-600 transition-colors flex items-center justify-center gap-2"
>
<MessageCircle className="w-4 h-4" />
تواصل
</button> */}
</div>
</div>
</motion.div>
@ -298,10 +294,11 @@ const BookingCard = ({ booking, onViewDetails, onContact }) => {
};
const BookingDetailsModal = ({ booking, isOpen, onClose }) => {
const { t } = useTranslation();
if (!isOpen || !booking) return null;
const formatCurrency = (amount) => {
return amount?.toLocaleString() + ' ل.س';
return amount?.toLocaleString() + ' ' + t('syp');
};
return (
@ -321,80 +318,80 @@ const BookingDetailsModal = ({ booking, isOpen, onClose }) => {
>
<div className="sticky top-0 bg-gradient-to-r from-amber-500 to-amber-600 p-6 text-white">
<div className="flex justify-between items-center">
<h2 className="text-xl font-bold">تفاصيل الحجز</h2>
<h2 className="text-xl font-bold">{t('bookingDetails')}</h2>
<button onClick={onClose} className="p-1 hover:bg-white/20 rounded-full">
<XCircle className="w-6 h-6" />
</button>
</div>
<p className="text-amber-100 text-sm mt-1">رقم الحجز: #{booking.id}</p>
<p className="text-amber-100 text-sm mt-1">{t('bookingId', { id: booking.id })}</p>
</div>
<div className="p-6 space-y-6">
<div className="bg-gray-50 p-4 rounded-xl">
<h3 className="font-bold text-gray-900 mb-3">معلومات العقار</h3>
<h3 className="font-bold text-gray-900 mb-3">{t('propertyInfo')}</h3>
<div className="space-y-2">
<p><span className="text-gray-500">العقار:</span> {booking.propertyTitle}</p>
<p><span className="text-gray-500">الموقع:</span> {booking.location}</p>
<p><span className="text-gray-500">{t('property')}:</span> {booking.propertyTitle}</p>
<p><span className="text-gray-500">{t('location')}:</span> {booking.location}</p>
{booking.propertyDetails && (
<div className="flex gap-3 mt-2">
<span className="text-sm bg-white px-2 py-1 rounded-lg">{booking.propertyDetails.bedrooms} غرف</span>
<span className="text-sm bg-white px-2 py-1 rounded-lg">{booking.propertyDetails.bathrooms} حمامات</span>
<span className="text-sm bg-white px-2 py-1 rounded-lg">{booking.propertyDetails.area} م²</span>
<span className="text-sm bg-white px-2 py-1 rounded-lg">{booking.propertyDetails.bedrooms} {t('rooms')}</span>
<span className="text-sm bg-white px-2 py-1 rounded-lg">{booking.propertyDetails.bathrooms} {t('bathrooms')}</span>
<span className="text-sm bg-white px-2 py-1 rounded-lg">{booking.propertyDetails.area} {t('sqm')}</span>
</div>
)}
</div>
</div>
<div className="bg-gray-50 p-4 rounded-xl">
<h3 className="font-bold text-gray-900 mb-3">معلومات المستأجر</h3>
<h3 className="font-bold text-gray-900 mb-3">{t('tenantInfo')}</h3>
<div className="space-y-2">
<p><span className="text-gray-500">الاسم:</span> {booking.tenantName}</p>
<p><span className="text-gray-500">البريد الإلكتروني:</span> {booking.tenantEmail}</p>
<p><span className="text-gray-500">رقم الهاتف:</span> {booking.tenantPhone}</p>
<p><span className="text-gray-500">{t('name')}</span> {booking.tenantName}</p>
<p><span className="text-gray-500">{t('email')}</span> {booking.tenantEmail}</p>
<p><span className="text-gray-500">{t('phone')}</span> {booking.tenantPhone}</p>
</div>
</div>
<div className="bg-gray-50 p-4 rounded-xl">
<h3 className="font-bold text-gray-900 mb-3">تفاصيل الحجز</h3>
<h3 className="font-bold text-gray-900 mb-3">{t('bookingDetails')}</h3>
<div className="grid grid-cols-2 gap-4">
<div>
<p className="text-gray-500">تاريخ البداية</p>
<p className="text-gray-500">{t('startDate')}</p>
<p className="font-medium">{booking.startDate}</p>
</div>
<div>
<p className="text-gray-500">تاريخ النهاية</p>
<p className="text-gray-500">{t('endDate')}</p>
<p className="font-medium">{booking.endDate}</p>
</div>
<div>
<p className="text-gray-500">عدد الأيام</p>
<p className="font-medium">{booking.days} يوم</p>
<p className="text-gray-500">{t('durationDays', { days: booking.days })}</p>
<p className="font-medium">{booking.days} {t('days')}</p>
</div>
<div>
<p className="text-gray-500">حالة الحجز</p>
<p className="font-medium">{booking.status === 'pending' ? 'قيد الانتظار' :
booking.status === 'confirmed' ? 'مؤكد' :
booking.status === 'cancelled' ? 'ملغي' : 'منتهي'}</p>
<p className="text-gray-500">{t('bookingStatus')}</p>
<p className="font-medium">{booking.status === 'pending' ? t('pending') :
booking.status === 'confirmed' ? t('confirmed') :
booking.status === 'cancelled' ? t('cancelled') : t('completed')}</p>
</div>
</div>
</div>
<div className="bg-amber-50 p-4 rounded-xl">
<h3 className="font-bold text-amber-700 mb-3">المعلومات المالية</h3>
<h3 className="font-bold text-amber-700 mb-3">{t('financialInfo')}</h3>
<div className="space-y-2">
<div className="flex justify-between">
<span className="text-gray-600">السعر اليومي</span>
<span className="text-gray-600">{t('dailyPrice')}</span>
<span className="font-medium">{formatCurrency(booking.dailyPrice)}</span>
</div>
<div className="flex justify-between">
<span className="text-gray-600">المدة ({booking.days} أيام)</span>
<span className="text-gray-600">{t('durationDays', { days: booking.days })}</span>
<span className="font-medium">{formatCurrency(booking.dailyPrice * booking.days)}</span>
</div>
<div className="flex justify-between">
<span className="text-gray-600">سلفة الضمان</span>
<span className="text-gray-600">{t('securityDeposit')}</span>
<span className="font-medium">{formatCurrency(booking.securityDeposit || 0)}</span>
</div>
<div className="flex justify-between pt-2 border-t border-amber-200 font-bold">
<span className="text-gray-900">الإجمالي</span>
<span className="text-gray-900">{t('total')}</span>
<span className="text-amber-600 text-lg">{formatCurrency(booking.totalAmount)}</span>
</div>
</div>
@ -402,7 +399,7 @@ const BookingDetailsModal = ({ booking, isOpen, onClose }) => {
{booking.notes && (
<div className="bg-gray-50 p-4 rounded-xl">
<h3 className="font-bold text-gray-900 mb-2">ملاحظات</h3>
<h3 className="font-bold text-gray-900 mb-2">{t('notes')}</h3>
<p className="text-gray-600">{booking.notes}</p>
</div>
)}
@ -413,6 +410,7 @@ const BookingDetailsModal = ({ booking, isOpen, onClose }) => {
};
export default function OwnerBookingsPage() {
const { t, i18n } = useTranslation();
const router = useRouter();
const [user, setUser] = useState(null);
const [bookings, setBookings] = useState([]);
@ -449,10 +447,10 @@ export default function OwnerBookingsPage() {
{
id: 'BK001',
propertyId: 1,
propertyTitle: 'فيلا فاخرة في المزة',
location: 'دمشق، المزة',
propertyTitle: t('ownerBookings.mockB1Title'),
location: t('ownerBookings.mockB1Location'),
propertyDetails: { bedrooms: 5, bathrooms: 4, area: 450 },
tenantName: 'أحمد محمد',
tenantName: t('ownerBookings.mockB1Tenant'),
tenantEmail: 'ahmed@example.com',
tenantPhone: '0933111222',
startDate: '2024-03-10',
@ -463,15 +461,15 @@ export default function OwnerBookingsPage() {
securityDeposit: 500000,
status: 'confirmed',
createdAt: '2024-02-25',
notes: 'طلب الحجز من خلال الموقع'
notes: t('ownerBookings.mockB1Notes')
},
{
id: 'BK002',
propertyId: 2,
propertyTitle: 'شقة حديثة في الشهباء',
location: 'حلب، الشهباء',
propertyTitle: t('ownerBookings.mockB2Title'),
location: t('ownerBookings.mockB2Location'),
propertyDetails: { bedrooms: 3, bathrooms: 2, area: 180 },
tenantName: 'سارة أحمد',
tenantName: t('ownerBookings.mockB2Tenant'),
tenantEmail: 'sara@example.com',
tenantPhone: '0945123789',
startDate: '2024-03-05',
@ -482,15 +480,15 @@ export default function OwnerBookingsPage() {
securityDeposit: 250000,
status: 'pending',
createdAt: '2024-02-24',
notes: 'تحتاج إلى تأكيد'
notes: t('ownerBookings.mockB2Notes')
},
{
id: 'BK003',
propertyId: 3,
propertyTitle: 'بيت عائلي في بابا عمرو',
location: 'حمص، بابا عمرو',
propertyTitle: t('ownerBookings.mockB3Title'),
location: t('ownerBookings.mockB3Location'),
propertyDetails: { bedrooms: 4, bathrooms: 3, area: 300 },
tenantName: 'محمد الحلبي',
tenantName: t('ownerBookings.mockB3Tenant'),
tenantEmail: 'mohammed@example.com',
tenantPhone: '0956123456',
startDate: '2024-02-20',
@ -501,7 +499,7 @@ export default function OwnerBookingsPage() {
securityDeposit: 500000,
status: 'completed',
createdAt: '2024-02-15',
notes: 'تم إنهاء الإيجار بنجاح'
notes: t('ownerBookings.mockB3Notes')
}
];
setBookings(mockBookings);
@ -518,7 +516,7 @@ export default function OwnerBookingsPage() {
};
const handleContact = (booking) => {
toast.success(`جاري فتح محادثة مع ${booking.tenantName}`, {
toast.success(t('openingChat', { name: booking.tenantName }), {
icon: '💬',
style: { background: '#dcfce7', color: '#166534' }
});
@ -531,7 +529,7 @@ export default function OwnerBookingsPage() {
setBookings(updatedBookings);
setFilteredBookings(updatedBookings);
localStorage.setItem('ownerBookings', JSON.stringify(updatedBookings));
toast.success(`تم تحديث حالة الحجز بنجاح`);
toast.success(t('statusUpdated'));
};
const statusCounts = {
@ -547,14 +545,14 @@ export default function OwnerBookingsPage() {
<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('loading')}</p>
</div>
</div>
);
}
return (
<div className="min-h-screen bg-gray-50 py-8">
<div className="min-h-screen bg-gray-50 py-8" dir={i18n.language === 'ar' ? 'rtl' : 'ltr'}>
<Toaster position="top-center" reverseOrder={false} />
<BookingDetailsModal
@ -570,8 +568,8 @@ export default function OwnerBookingsPage() {
className="flex flex-col md:flex-row justify-between items-start md:items-center mb-8 gap-4"
>
<div>
<h1 className="text-3xl font-bold text-gray-900 mb-2">حجوزاتي</h1>
<p className="text-gray-600">مرحباً {user?.name}، لديك {bookings.length} حجز</p>
<h1 className="text-3xl font-bold text-gray-900 mb-2">{t('myBookings')}</h1>
<p className="text-gray-600">{t('welcomeBookings', { name: user?.name, count: bookings.length })}</p>
</div>
<div className="flex gap-3">
@ -580,12 +578,8 @@ export default function OwnerBookingsPage() {
className="px-4 py-2 bg-white border border-gray-300 rounded-xl text-gray-700 hover:bg-gray-50 transition-colors flex items-center gap-2"
>
<Calendar className="w-5 h-5" />
{showCalendar ? 'إخفاء التقويم' : 'عرض التقويم'}
{showCalendar ? t('hideCalendar') : t('showCalendar')}
</button>
{/* <button className="px-4 py-2 bg-green-600 text-white rounded-xl hover:bg-green-700 transition-colors flex items-center gap-2">
<Download className="w-5 h-5" />
تصدير التقرير
</button> */}
</div>
</motion.div>
@ -598,7 +592,7 @@ export default function OwnerBookingsPage() {
onClick={() => setFilterStatus('all')}
>
<div className="text-2xl font-bold text-gray-900">{statusCounts.all}</div>
<div className="text-sm text-gray-600">جميع الحجوزات</div>
<div className="text-sm text-gray-600">{t('allBookings')}</div>
</motion.div>
<motion.div
initial={{ opacity: 0, y: 20 }}
@ -610,7 +604,7 @@ export default function OwnerBookingsPage() {
onClick={() => setFilterStatus('pending')}
>
<div className="text-2xl font-bold text-yellow-600">{statusCounts.pending}</div>
<div className="text-sm text-gray-600">قيد الانتظار</div>
<div className="text-sm text-gray-600">{t('pending')}</div>
</motion.div>
<motion.div
initial={{ opacity: 0, y: 20 }}
@ -622,7 +616,7 @@ export default function OwnerBookingsPage() {
onClick={() => setFilterStatus('confirmed')}
>
<div className="text-2xl font-bold text-green-600">{statusCounts.confirmed}</div>
<div className="text-sm text-gray-600">مؤكدة</div>
<div className="text-sm text-gray-600">{t('confirmed')}</div>
</motion.div>
<motion.div
initial={{ opacity: 0, y: 20 }}
@ -634,7 +628,7 @@ export default function OwnerBookingsPage() {
onClick={() => setFilterStatus('completed')}
>
<div className="text-2xl font-bold text-gray-600">{statusCounts.completed}</div>
<div className="text-sm text-gray-600">منتهية</div>
<div className="text-sm text-gray-600">{t('completed')}</div>
</motion.div>
<motion.div
initial={{ opacity: 0, y: 20 }}
@ -646,7 +640,7 @@ export default function OwnerBookingsPage() {
onClick={() => setFilterStatus('cancelled')}
>
<div className="text-2xl font-bold text-red-600">{statusCounts.cancelled}</div>
<div className="text-sm text-gray-600">ملغية</div>
<div className="text-sm text-gray-600">{t('cancelled')}</div>
</motion.div>
</div>
@ -655,7 +649,7 @@ export default function OwnerBookingsPage() {
<Search className="absolute left-3 top-1/2 transform -translate-y-1/2 w-5 h-5 text-gray-400" />
<input
type="text"
placeholder="ابحث باسم العقار أو المستأجر.."
placeholder={t('searchBookings')}
value={searchTerm}
onChange={(e) => setSearchTerm(e.target.value)}
className="w-full pl-10 pr-4 py-3 border border-gray-300 rounded-xl focus:outline-none focus:ring-2 focus:ring-amber-500"
@ -667,21 +661,21 @@ export default function OwnerBookingsPage() {
value={dateRange.start}
onChange={(e) => setDateRange({...dateRange, start: e.target.value})}
className="px-4 py-3 border border-gray-300 rounded-xl focus:outline-none focus:ring-2 focus:ring-amber-500"
placeholder="من تاريخ"
placeholder={t('filterDateFrom')}
/>
<input
type="date"
value={dateRange.end}
onChange={(e) => setDateRange({...dateRange, end: e.target.value})}
className="px-4 py-3 border border-gray-300 rounded-xl focus:outline-none focus:ring-2 focus:ring-amber-500"
placeholder="إلى تاريخ"
placeholder={t('filterDateTo')}
/>
{(dateRange.start || dateRange.end) && (
<button
onClick={() => setDateRange({ start: '', end: '' })}
className="px-4 py-3 bg-gray-100 text-gray-700 rounded-xl hover:bg-gray-200 transition-colors"
>
مسح
{t('clear')}
</button>
)}
</div>
@ -708,16 +702,16 @@ export default function OwnerBookingsPage() {
<div className="w-24 h-24 bg-amber-100 rounded-full flex items-center justify-center mx-auto mb-4">
<Calendar className="w-12 h-12 text-amber-600" />
</div>
<h3 className="text-xl font-bold text-gray-900 mb-2">لا توجد حجوزات</h3>
<h3 className="text-xl font-bold text-gray-900 mb-2">{t('noBookings')}</h3>
<p className="text-gray-600 mb-4">
{filterStatus !== 'all' ? 'لا توجد حجوزات في هذه الفئة' : 'لم يتم استلام أي حجوزات بعد'}
{filterStatus !== 'all' ? t('noBookingsInCategory') : t('noBookingsReceived')}
</p>
{filterStatus !== 'all' && (
<button
onClick={() => setFilterStatus('all')}
className="inline-flex items-center gap-2 bg-amber-500 text-white px-6 py-3 rounded-xl font-medium hover:bg-amber-600"
>
عرض جميع الحجوزات
{t('viewAllBookings')}
</button>
)}
</motion.div>
@ -736,4 +730,4 @@ export default function OwnerBookingsPage() {
</div>
</div>
);
}
}

View File

@ -4,6 +4,7 @@ import { useState, useEffect } from 'react';
import { motion, AnimatePresence } from 'framer-motion';
import { useRouter } from 'next/navigation';
import Link from 'next/link';
import { useTranslation } from 'react-i18next';
import {
Calendar,
ChevronLeft,
@ -39,13 +40,14 @@ import toast, { Toaster } from 'react-hot-toast';
import AuthService from '../../services/AuthService';
const MonthlyCalendar = ({ properties, selectedPropertyId, onDateClick, onPropertySelect }) => {
const { t } = useTranslation();
const [currentMonth, setCurrentMonth] = useState(new Date());
const [selectedDate, setSelectedDate] = useState(null);
const [viewType, setViewType] = useState('grid');
const monthNames = [
'يناير', 'فبراير', 'مارس', 'إبريل', 'مايو', 'يونيو',
'يوليو', 'أغسطس', 'سبتمبر', 'أكتوبر', 'نوفمبر', 'ديسمبر'
t('calendar.month1'), t('calendar.month2'), t('calendar.month3'), t('calendar.month4'), t('calendar.month5'), t('calendar.month6'),
t('calendar.month7'), t('calendar.month8'), t('calendar.month9'), t('calendar.month10'), t('calendar.month11'), t('calendar.month12')
];
const daysInMonth = new Date(
@ -76,17 +78,17 @@ const MonthlyCalendar = ({ properties, selectedPropertyId, onDateClick, onProper
const totalProperties = properties.length;
const bookedCount = properties.filter(p => isDateBookedForProperty(date, p)).length;
if (bookedCount === 0) return { status: 'all_available', label: 'جميع العقارات متاحة', color: 'bg-green-100 text-green-800' };
if (bookedCount === totalProperties) return { status: 'all_booked', label: 'جميع العقارات محجوزة', color: 'bg-red-100 text-red-800' };
return { status: 'partial', label: `${bookedCount}/${totalProperties} محجوز`, color: 'bg-yellow-100 text-yellow-800' };
if (bookedCount === 0) return { status: 'all_available', label: t('calendar.allAvailable'), color: 'bg-green-100 text-green-800' };
if (bookedCount === totalProperties) return { status: 'all_booked', label: t('calendar.allBooked'), color: 'bg-red-100 text-red-800' };
return { status: 'partial', label: t('calendar.bookedCount', { count: bookedCount, total: totalProperties }), color: 'bg-yellow-100 text-yellow-800' };
} else {
const property = properties.find(p => p.id === selectedPropertyId);
if (!property) return { status: 'no_property', label: 'غير متاح', color: 'bg-gray-100 text-gray-500' };
if (!property) return { status: 'no_property', label: t('calendar.notAvailable'), color: 'bg-gray-100 text-gray-500' };
const isBooked = isDateBookedForProperty(date, property);
return {
status: isBooked ? 'booked' : 'available',
label: isBooked ? 'محجوز' : 'متاح',
label: isBooked ? t('calendar.booked') : t('calendar.available'),
color: isBooked ? 'bg-red-100 text-red-800' : 'bg-green-100 text-green-800'
};
}
@ -171,7 +173,7 @@ const MonthlyCalendar = ({ properties, selectedPropertyId, onDateClick, onProper
onClick={() => setCurrentMonth(new Date())}
className="px-4 py-2 bg-amber-500 text-white rounded-xl text-sm hover:bg-amber-600 transition-colors"
>
اليوم
{t('calendar.today')}
</button>
<div className="flex border border-gray-200 rounded-xl overflow-hidden">
<button
@ -192,7 +194,7 @@ const MonthlyCalendar = ({ properties, selectedPropertyId, onDateClick, onProper
</div>
<div className="grid grid-cols-7 gap-1 p-4 bg-gray-50 border-b border-gray-200">
{['أحد', 'إثنين', 'ثلاثاء', 'أربعاء', 'خميس', 'جمعة', 'سبت'].map((day, index) => (
{[t('calendar.day1'), t('calendar.day2'), t('calendar.day3'), t('calendar.day4'), t('calendar.day5'), t('calendar.day6'), t('calendar.day7')].map((day, index) => (
<div key={index} className="text-center text-sm font-medium text-gray-600 py-2">
{day}
</div>
@ -209,23 +211,23 @@ const MonthlyCalendar = ({ properties, selectedPropertyId, onDateClick, onProper
<div className="flex flex-wrap gap-4 justify-center text-xs">
<div className="flex items-center gap-2">
<div className="w-3 h-3 bg-green-100 rounded" />
<span className="text-gray-600">متاح</span>
<span className="text-gray-600">{t('calendar.available')}</span>
</div>
<div className="flex items-center gap-2">
<div className="w-3 h-3 bg-red-100 rounded" />
<span className="text-gray-600">محجوز</span>
<span className="text-gray-600">{t('calendar.booked')}</span>
</div>
<div className="flex items-center gap-2">
<div className="w-3 h-3 bg-yellow-100 rounded" />
<span className="text-gray-600">محجوز جزئياً</span>
<span className="text-gray-600">{t('calendar.partiallyBooked')}</span>
</div>
<div className="flex items-center gap-2">
<div className="w-3 h-3 ring-2 ring-amber-500 rounded" />
<span className="text-gray-600">اليوم</span>
<span className="text-gray-600">{t('calendar.today')}</span>
</div>
<div className="flex items-center gap-2">
<div className="w-3 h-3 ring-2 ring-blue-500 rounded" />
<span className="text-gray-600">محدد</span>
<span className="text-gray-600">{t('calendar.selected')}</span>
</div>
</div>
</div>
@ -234,8 +236,9 @@ const MonthlyCalendar = ({ properties, selectedPropertyId, onDateClick, onProper
};
const PropertyCalendarList = ({ properties, selectedDate, onPropertyClick }) => {
const { t } = useTranslation();
const formatCurrency = (amount) => {
return amount?.toLocaleString() + ' ل.س';
return amount?.toLocaleString() + ' ' + t('calendar.syp');
};
const isDateBooked = (property, date) => {
@ -264,8 +267,8 @@ const PropertyCalendarList = ({ properties, selectedDate, onPropertyClick }) =>
return (
<div className="bg-white rounded-2xl shadow-sm border border-gray-200 p-12 text-center">
<CalendarDays className="w-16 h-16 text-gray-300 mx-auto mb-4" />
<h3 className="text-lg font-bold text-gray-700 mb-2">اختر تاريخاً</h3>
<p className="text-gray-500">اضغط على أي يوم في التقويم لعرض حالة العقارات في ذلك التاريخ</p>
<h3 className="text-lg font-bold text-gray-700 mb-2">{t('calendar.selectDate')}</h3>
<p className="text-gray-500">{t('calendar.clickDayHint')}</p>
</div>
);
}
@ -282,7 +285,7 @@ const PropertyCalendarList = ({ properties, selectedDate, onPropertyClick }) =>
<div className="p-4 md:p-6 border-b border-gray-200 bg-gradient-to-r from-amber-50 to-amber-100">
<h3 className="text-lg font-bold text-gray-900 flex items-center gap-2">
<CalendarDays className="w-5 h-5 text-amber-500" />
حالة العقارات في تاريخ: {formattedDate}
{t('calendar.statusForDate')} {formattedDate}
</h3>
</div>
@ -306,7 +309,7 @@ const PropertyCalendarList = ({ properties, selectedDate, onPropertyClick }) =>
<span className={`px-2 py-1 rounded-lg text-xs font-medium ${
isBooked ? 'bg-red-100 text-red-800' : 'bg-green-100 text-green-800'
}`}>
{isBooked ? 'محجوز' : 'متاح'}
{isBooked ? t('calendar.booked') : t('calendar.available')}
</span>
</div>
<div className="flex items-center gap-1 text-gray-500 text-sm mb-2">
@ -316,26 +319,26 @@ const PropertyCalendarList = ({ properties, selectedDate, onPropertyClick }) =>
<div className="flex flex-wrap gap-3 text-sm text-gray-600">
<div className="flex items-center gap-1">
<Bed className="w-4 h-4" />
<span>{property.bedrooms} غرف</span>
<span>{property.bedrooms} {t('calendar.rooms')}</span>
</div>
<div className="flex items-center gap-1">
<Bath className="w-4 h-4" />
<span>{property.bathrooms} حمامات</span>
<span>{property.bathrooms} {t('calendar.bathrooms')}</span>
</div>
<div className="flex items-center gap-1">
<Square className="w-4 h-4" />
<span>{property.area} م²</span>
<span>{property.area} {t('calendar.sqm')}</span>
</div>
</div>
</div>
<div className="text-right">
<div className="text-lg font-bold text-amber-600">{formatCurrency(property.price)}</div>
<div className="text-xs text-gray-500">/يوم</div>
<div className="text-xs text-gray-500">{t('calendar.perDay')}</div>
{isBooked && booking && (
<div className="mt-2 text-xs text-gray-500">
<div>مستأجر: {booking.tenantName || 'غير معروف'}</div>
<div>من: {booking.startDate} إلى {booking.endDate}</div>
<div>{t('calendar.tenant')}: {booking.tenantName || t('calendar.unknown')}</div>
<div>{t('calendar.from')} {booking.startDate} {t('calendar.to')} {booking.endDate}</div>
</div>
)}
</div>
@ -349,10 +352,11 @@ const PropertyCalendarList = ({ properties, selectedDate, onPropertyClick }) =>
};
const PropertyDetailsModal = ({ property, isOpen, onClose }) => {
const { t } = useTranslation();
if (!isOpen || !property) return null;
const formatCurrency = (amount) => {
return amount?.toLocaleString() + ' ل.س';
return amount?.toLocaleString() + ' ' + t('calendar.syp');
};
return (
@ -385,7 +389,7 @@ const PropertyDetailsModal = ({ property, isOpen, onClose }) => {
<div className="p-6 space-y-6">
{property.images && property.images.length > 0 && (
<div>
<h3 className="font-bold text-gray-900 mb-3">صور العقار</h3>
<h3 className="font-bold text-gray-900 mb-3">{t('calendar.propertyImages')}</h3>
<div className="grid grid-cols-2 gap-3">
{property.images.slice(0, 4).map((image, index) => (
<div key={index} className="relative h-32 rounded-lg overflow-hidden bg-gray-100">
@ -400,28 +404,28 @@ const PropertyDetailsModal = ({ property, isOpen, onClose }) => {
<div className="bg-gray-50 p-3 rounded-xl text-center">
<Bed className="w-5 h-5 text-amber-500 mx-auto mb-1" />
<div className="text-sm font-bold">{property.bedrooms}</div>
<div className="text-xs text-gray-500">غرف نوم</div>
<div className="text-xs text-gray-500">{t('calendar.bedrooms')}</div>
</div>
<div className="bg-gray-50 p-3 rounded-xl text-center">
<Bath className="w-5 h-5 text-amber-500 mx-auto mb-1" />
<div className="text-sm font-bold">{property.bathrooms}</div>
<div className="text-xs text-gray-500">حمامات</div>
<div className="text-xs text-gray-500">{t('calendar.bathrooms')}</div>
</div>
<div className="bg-gray-50 p-3 rounded-xl text-center">
<Square className="w-5 h-5 text-amber-500 mx-auto mb-1" />
<div className="text-sm font-bold">{property.area}</div>
<div className="text-xs text-gray-500">م²</div>
<div className="text-xs text-gray-500">{t('calendar.sqm')}</div>
</div>
<div className="bg-gray-50 p-3 rounded-xl text-center">
<DollarSign className="w-5 h-5 text-amber-500 mx-auto mb-1" />
<div className="text-sm font-bold">{formatCurrency(property.price)}</div>
<div className="text-xs text-gray-500">/يوم</div>
<div className="text-xs text-gray-500">{t('calendar.perDay')}</div>
</div>
</div>
{property.features && property.features.length > 0 && (
<div>
<h3 className="font-bold text-gray-900 mb-3">المميزات</h3>
<h3 className="font-bold text-gray-900 mb-3">{t('calendar.features')}</h3>
<div className="flex flex-wrap gap-2">
{property.features.map((feature, index) => (
<span key={index} className="px-2 py-1 bg-gray-100 text-gray-700 rounded-lg text-xs">
@ -436,14 +440,14 @@ const PropertyDetailsModal = ({ property, isOpen, onClose }) => {
<div>
<h3 className="font-bold text-gray-900 mb-3 flex items-center gap-2">
<Clock className="w-4 h-4 text-amber-500" />
الحجوزات القادمة
{t('calendar.upcomingBookings')}
</h3>
<div className="space-y-2">
{property.bookings.slice(0, 3).map((booking, index) => (
<div key={index} className="bg-gray-50 p-3 rounded-lg flex justify-between items-center">
<div>
<p className="font-medium text-gray-900">{booking.startDate} - {booking.endDate}</p>
<p className="text-xs text-gray-500">مستأجر: {booking.tenantName || 'غير معروف'}</p>
<p className="text-xs text-gray-500">{t('calendar.tenant')}: {booking.tenantName || t('calendar.unknown')}</p>
</div>
<span className="text-sm font-bold text-amber-600">{formatCurrency(booking.totalAmount)}</span>
</div>
@ -458,13 +462,13 @@ const PropertyDetailsModal = ({ property, isOpen, onClose }) => {
href={`/owner/properties/edit?id=${property.id}`}
className="flex-1 bg-amber-500 text-white py-3 rounded-xl text-center font-medium hover:bg-amber-600 transition-colors"
>
تعديل العقار
{t('calendar.editProperty')}
</Link>
<button
onClick={() => window.location.href = `/owner/bookings?property=${property.id}`}
className="flex-1 bg-gray-100 text-gray-700 py-3 rounded-xl text-center font-medium hover:bg-gray-200 transition-colors"
>
عرض الحجوزات
{t('calendar.viewBookings')}
</button>
</div>
</motion.div>
@ -473,6 +477,7 @@ const PropertyDetailsModal = ({ property, isOpen, onClose }) => {
};
export default function OwnerCalendarPage() {
const { t, i18n } = useTranslation();
const router = useRouter();
const [user, setUser] = useState(null);
const [properties, setProperties] = useState([]);
@ -509,44 +514,44 @@ export default function OwnerCalendarPage() {
const mockProperties = [
{
id: 1,
title: 'فيلا فاخرة في المزة',
location: 'دمشق، المزة',
title: t('calendar.mockVillaTitle'),
location: t('calendar.mockVillaLocation'),
bedrooms: 5,
bathrooms: 4,
area: 450,
price: 500000,
features: ['مسبح', 'حديقة خاصة', 'موقف سيارات', 'أمن 24/7'],
features: [t('swimmingPool'), t('privateGarden'), t('parking'), t('propertyService.security247')],
images: ['/villa1.jpg'],
status: 'available',
bookings: [
{ startDate: '2024-03-10', endDate: '2024-03-15', totalAmount: 2500000, tenantName: 'أحمد محمد' },
{ startDate: '2024-03-20', endDate: '2024-03-25', totalAmount: 2500000, tenantName: 'سارة أحمد' }
{ startDate: '2024-03-10', endDate: '2024-03-15', totalAmount: 2500000, tenantName: t('calendar.mockTenant1') },
{ startDate: '2024-03-20', endDate: '2024-03-25', totalAmount: 2500000, tenantName: t('calendar.mockTenant2') }
]
},
{
id: 2,
title: 'شقة حديثة في الشهباء',
location: 'حلب، الشهباء',
title: t('calendar.mockApartmentTitle'),
location: t('calendar.mockApartmentLocation'),
bedrooms: 3,
bathrooms: 2,
area: 180,
price: 250000,
features: ['مطبخ مجهز', 'بلكونة', 'موقف سيارات', 'مصعد'],
features: [t('equippedKitchen'), t('balcony'), t('parking'), t('propertyService.elevator')],
images: ['/apartment1.jpg'],
status: 'available',
bookings: [
{ startDate: '2024-03-05', endDate: '2024-03-08', totalAmount: 750000, tenantName: 'محمد علي' }
{ startDate: '2024-03-05', endDate: '2024-03-08', totalAmount: 750000, tenantName: t('calendar.mockTenant3') }
]
},
{
id: 3,
title: 'بيت عائلي في بابا عمرو',
location: 'حمص، بابا عمرو',
title: t('calendar.mockHouseTitle'),
location: t('calendar.mockHouseLocation'),
bedrooms: 4,
bathrooms: 3,
area: 300,
price: 350000,
features: ['حديقة كبيرة', 'موقف سيارات', 'مدفأة', 'كراج'],
features: [t('largeGarden'), t('parking'), t('fireplace'), t('garage')],
images: ['/house1.jpg'],
status: 'booked',
bookings: []
@ -586,14 +591,14 @@ export default function OwnerCalendarPage() {
<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('calendar.loading')}</p>
</div>
</div>
);
}
return (
<div className="min-h-screen bg-gray-50 py-8">
<div className="min-h-screen bg-gray-50 py-8" dir={i18n.language === 'ar' ? 'rtl' : 'ltr'}>
<Toaster position="top-center" reverseOrder={false} />
<PropertyDetailsModal
@ -609,8 +614,8 @@ export default function OwnerCalendarPage() {
className="flex flex-col md:flex-row justify-between items-start md:items-center mb-8 gap-4"
>
<div>
<h1 className="text-3xl font-bold text-gray-900 mb-2">تقويم العقارات</h1>
<p className="text-gray-600">مرحباً {user?.name}، تتبع حالة عقاراتك عبر التقويم</p>
<h1 className="text-3xl font-bold text-gray-900 mb-2">{t('calendar.pageTitle')}</h1>
<p className="text-gray-600">{t('calendar.welcomeMessage', { name: user?.name })}</p>
</div>
<div className="flex gap-3">
@ -619,7 +624,7 @@ export default function OwnerCalendarPage() {
className="px-4 py-2 bg-white border border-gray-300 rounded-xl text-gray-700 hover:bg-gray-50 transition-colors flex items-center gap-2"
>
<Filter className="w-5 h-5" />
فلترة العقارات
{t('calendar.filterProperties')}
<ChevronDown className={`w-4 h-4 transition-transform ${showFilters ? 'rotate-180' : ''}`} />
</button>
{/* <button className="px-4 py-2 bg-green-600 text-white rounded-xl hover:bg-green-700 transition-colors flex items-center gap-2">
@ -638,7 +643,7 @@ export default function OwnerCalendarPage() {
>
<Building className="w-6 h-6 text-amber-500 mx-auto mb-2" />
<div className="text-2xl font-bold text-gray-900">{calendarStats.totalProperties}</div>
<div className="text-sm text-gray-600">إجمالي العقارات</div>
<div className="text-sm text-gray-600">{t('calendar.totalProperties')}</div>
</motion.div>
<motion.div
initial={{ opacity: 0, y: 20 }}
@ -648,7 +653,7 @@ export default function OwnerCalendarPage() {
>
<CheckCircle className="w-6 h-6 text-green-500 mx-auto mb-2" />
<div className="text-2xl font-bold text-green-600">{calendarStats.availableToday}</div>
<div className="text-sm text-gray-600">متاح اليوم</div>
<div className="text-sm text-gray-600">{t('calendar.availableToday')}</div>
</motion.div>
<motion.div
initial={{ opacity: 0, y: 20 }}
@ -658,7 +663,7 @@ export default function OwnerCalendarPage() {
>
<XCircle className="w-6 h-6 text-red-500 mx-auto mb-2" />
<div className="text-2xl font-bold text-red-600">{calendarStats.bookedToday}</div>
<div className="text-sm text-gray-600">محجوز اليوم</div>
<div className="text-sm text-gray-600">{t('calendar.bookedToday')}</div>
</motion.div>
<motion.div
initial={{ opacity: 0, y: 20 }}
@ -668,7 +673,7 @@ export default function OwnerCalendarPage() {
>
<CalendarDays className="w-6 h-6 text-blue-500 mx-auto mb-2" />
<div className="text-2xl font-bold text-blue-600">{calendarStats.upcomingBookings}</div>
<div className="text-sm text-gray-600">حجوزات قادمة</div>
<div className="text-sm text-gray-600">{t('calendar.upcomingBookings')}</div>
</motion.div>
</div>
@ -682,13 +687,13 @@ export default function OwnerCalendarPage() {
>
<div className="bg-white rounded-2xl shadow-sm border border-gray-200 p-4">
<div className="flex flex-wrap gap-3 items-center">
<label className="text-sm font-medium text-gray-700">اختر عقاراً:</label>
<label className="text-sm font-medium text-gray-700">{t('calendar.selectProperty')}</label>
<select
value={selectedPropertyId}
onChange={(e) => setSelectedPropertyId(e.target.value)}
className="px-4 py-2 border border-gray-300 rounded-xl focus:outline-none focus:ring-2 focus:ring-amber-500"
>
<option value="all">جميع العقارات</option>
<option value="all">{t('calendar.allProperties')}</option>
{properties.map((property) => (
<option key={property.id} value={property.id}>{property.title}</option>
))}
@ -697,7 +702,7 @@ export default function OwnerCalendarPage() {
onClick={() => setSelectedPropertyId('all')}
className="px-3 py-2 bg-gray-100 text-gray-700 rounded-lg hover:bg-gray-200 transition-colors"
>
إعادة تعيين
{t('calendar.reset')}
</button>
</div>
</div>
@ -729,7 +734,7 @@ export default function OwnerCalendarPage() {
className="mt-6 text-center text-sm text-gray-500"
>
<AlertCircle className="w-4 h-4 inline ml-1" />
اضغط على أي عقار لعرض التفاصيل الكاملة
{t('calendar.clickPropertyHint')}
</motion.div>
)}
</div>

View File

@ -298,9 +298,11 @@ import { useRouter } from 'next/navigation';
import { Download, Loader2 } from 'lucide-react';
import toast, { Toaster } from 'react-hot-toast';
import * as XLSX from 'xlsx';
import { useTranslation } from 'react-i18next';
import AuthService from '@/app/services/AuthService';
export default function OwnerProfitsPage() {
const { t, i18n } = useTranslation();
const router = useRouter();
const [user, setUser] = useState(null);
const [isLoading, setIsLoading] = useState(true);
@ -404,29 +406,29 @@ export default function OwnerProfitsPage() {
const handleExportReport = () => {
try {
const exportData = tableData.map((row) => ({
'العقار': row.property,
'رقم الحجز': row.bookingNumber,
'من تاريخ': row.fromDate,
'حتى تاريخ': row.toDate,
'العروض المستلم': row.amountReceived,
'عمولة المنصة': row.platformCommission,
'ربح المنصة (5%)': row.platformProfit,
'المستحق للمالك': row.ownerDue,
'تم التحويل للمالك': row.transferredToOwner,
'رقم وصل التحويل': row.transferReceipt,
[t('profits.colProperty')]: row.property,
[t('profits.colBookingNumber')]: row.bookingNumber,
[t('profits.colFromDate')]: row.fromDate,
[t('profits.colToDate')]: row.toDate,
[t('profits.colAmountReceived')]: row.amountReceived,
[t('profits.colPlatformCommission')]: row.platformCommission,
[t('profits.colPlatformProfit')]: row.platformProfit,
[t('profits.colOwnerDue')]: row.ownerDue,
[t('profits.colTransferred')]: row.transferredToOwner,
[t('profits.colTransferReceipt')]: row.transferReceipt,
}));
exportData.push({
'العقار': 'الإجمالي العام',
'رقم الحجز': '',
'من تاريخ': '',
'حتى تاريخ': '',
'العروض المستلم': totals.totalAmountReceived,
'عمولة المنصة': totals.totalCommission,
'ربح المنصة (5%)': totals.totalPlatformProfit,
'المستحق للمالك': totals.totalOwnerDue,
'تم التحويل للمالك': totals.totalTransferred,
'رقم وصل التحويل': '—',
[t('profits.colProperty')]: t('profits.totalGeneral'),
[t('profits.colBookingNumber')]: '',
[t('profits.colFromDate')]: '',
[t('profits.colToDate')]: '',
[t('profits.colAmountReceived')]: totals.totalAmountReceived,
[t('profits.colPlatformCommission')]: totals.totalCommission,
[t('profits.colPlatformProfit')]: totals.totalPlatformProfit,
[t('profits.colOwnerDue')]: totals.totalOwnerDue,
[t('profits.colTransferred')]: totals.totalTransferred,
[t('profits.colTransferReceipt')]: '—',
});
const worksheet = XLSX.utils.json_to_sheet(exportData);
@ -445,14 +447,14 @@ export default function OwnerProfitsPage() {
worksheet['!cols'] = colWidths;
const workbook = XLSX.utils.book_new();
XLSX.utils.book_append_sheet(workbook, worksheet, 'أرباح المالك');
XLSX.utils.book_append_sheet(workbook, worksheet, t('profits.sheetName'));
XLSX.writeFile(workbook, `تقرير_الأرباح_${new Date().toISOString().slice(0,19).replace(/:/g, '-')}.xlsx`);
XLSX.writeFile(workbook, `${t('profits.fileName')}${new Date().toISOString().slice(0,19).replace(/:/g, '-')}.xlsx`);
toast.success('تم تصدير التقرير بنجاح!');
toast.success(t('profits.exportSuccess'));
} catch (error) {
console.error('خطأ في التصدير:', error);
toast.error('حدث خطأ أثناء تصدير التقرير');
console.error(t('profits.exportErrorLog'), error);
toast.error(t('profits.exportError'));
}
};
@ -461,14 +463,14 @@ export default function OwnerProfitsPage() {
<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('profits.loading')}</p>
</div>
</div>
);
}
return (
<div className="min-h-screen bg-gray-50 py-8" dir="rtl">
<div className="min-h-screen bg-gray-50 py-8" dir={i18n.language === 'ar' ? 'rtl' : 'ltr'}>
<Toaster position="top-center" reverseOrder={false} />
<div className="container mx-auto px-4 max-w-7xl">
<motion.div
@ -477,9 +479,9 @@ export default function OwnerProfitsPage() {
className="flex flex-col md:flex-row justify-between items-start md:items-center mb-8 gap-4"
>
<div>
<h1 className="text-3xl font-bold text-gray-900 mb-2">أرباح المالك</h1>
<h1 className="text-3xl font-bold text-gray-900 mb-2">{t('profits.pageTitle')}</h1>
<p className="text-gray-600">
مرحباً {user?.name}
{t('profits.greeting')} {user?.name}
</p>
</div>
<button
@ -487,7 +489,7 @@ export default function OwnerProfitsPage() {
className="px-5 py-2.5 bg-amber-500 text-white rounded-xl font-medium hover:bg-amber-600 transition-colors flex items-center gap-2 shadow-sm"
>
<Download className="w-5 h-5" />
تصدير التقرير
{t('profits.exportReport')}
</button>
</motion.div>
@ -500,18 +502,18 @@ export default function OwnerProfitsPage() {
<table className="min-w-full divide-y divide-gray-200 text-sm">
<thead className="bg-gray-800 text-gray-100">
<tr>
<th className="px-4 py-4 text-center text-xs font-semibold uppercase tracking-wider">العقار</th>
<th className="px-4 py-4 text-center text-xs font-semibold uppercase tracking-wider">رقم الحجز</th>
<th className="px-4 py-4 text-center text-xs font-semibold uppercase tracking-wider">من تاريخ</th>
<th className="px-4 py-4 text-center text-xs font-semibold uppercase tracking-wider">حتى تاريخ</th>
<th className="px-4 py-4 text-center text-xs font-semibold uppercase tracking-wider">العروض المستلم</th>
<th className="px-4 py-4 text-center text-xs font-semibold uppercase tracking-wider">عمولة المنصة</th>
<th className="px-4 py-4 text-center text-xs font-semibold uppercase tracking-wider">{t('profits.colProperty')}</th>
<th className="px-4 py-4 text-center text-xs font-semibold uppercase tracking-wider">{t('profits.colBookingNumber')}</th>
<th className="px-4 py-4 text-center text-xs font-semibold uppercase tracking-wider">{t('profits.colFromDate')}</th>
<th className="px-4 py-4 text-center text-xs font-semibold uppercase tracking-wider">{t('profits.colToDate')}</th>
<th className="px-4 py-4 text-center text-xs font-semibold uppercase tracking-wider">{t('profits.colAmountReceived')}</th>
<th className="px-4 py-4 text-center text-xs font-semibold uppercase tracking-wider">{t('profits.colPlatformCommission')}</th>
<th className="px-4 py-4 text-center text-xs font-semibold uppercase tracking-wider bg-amber-50 text-amber-800">
ربح المنصة <span className="font-normal text-[11px] block">(5% من العربون)</span>
{t('profits.colPlatformProfit')} <span className="font-normal text-[11px] block">{t('profits.colPlatformProfitSub')}</span>
</th>
<th className="px-4 py-4 text-center text-xs font-semibold uppercase tracking-wider">المستحق للمالك</th>
<th className="px-4 py-4 text-center text-xs font-semibold uppercase tracking-wider">تم التحويل للمالك</th>
<th className="px-4 py-4 text-center text-xs font-semibold uppercase tracking-wider">رقم وصل التحويل</th>
<th className="px-4 py-4 text-center text-xs font-semibold uppercase tracking-wider">{t('profits.colOwnerDue')}</th>
<th className="px-4 py-4 text-center text-xs font-semibold uppercase tracking-wider">{t('profits.colTransferred')}</th>
<th className="px-4 py-4 text-center text-xs font-semibold uppercase tracking-wider">{t('profits.colTransferReceipt')}</th>
</tr>
</thead>
<tbody className="bg-white divide-y divide-gray-100">
@ -558,7 +560,7 @@ export default function OwnerProfitsPage() {
<tfoot className="bg-gray-100 border-t-2 border-gray-300">
<tr>
<td colSpan="4" className="px-4 py-4 text-right font-bold text-gray-800">
الإجمالي العام
{t('profits.totalGeneral')}
</td>
<td className="px-4 py-4 text-center font-bold font-mono text-gray-800">
{totals.totalAmountReceived}
@ -582,8 +584,8 @@ export default function OwnerProfitsPage() {
</div>
<div className="bg-gray-50 px-6 py-3 text-xs text-gray-500 border-t border-gray-200">
<span className="inline-flex items-center gap-1"></span> ملاحظة:
<strong> ربح المنصة </strong> يُحتسب تلقائياً بنسبة <strong className="text-amber-600">5%</strong> من قيمة «العروض المستلم».
<span className="inline-flex items-center gap-1"></span> {t('profits.note')}
<strong> {t('profits.colPlatformProfit')} </strong>{t('profits.noteMiddle')}<strong className="text-amber-600">5%</strong>{t('profits.noteEnd', { colName: t('profits.colAmountReceived') })}
</div>
</motion.div>
</div>

View File

@ -6,6 +6,7 @@ import { useRouter, useSearchParams } from 'next/navigation';
import Link from 'next/link';
import Image from 'next/image';
import 'leaflet/dist/leaflet.css';
import { useTranslation } from 'react-i18next';
import {
ArrowLeft,
MapPin,
@ -74,6 +75,7 @@ export default function AddPropertyPage() {
const router = useRouter();
const searchParams = useSearchParams();
const purpose = searchParams.get('purpose') || 'rent';
const { t } = useTranslation();
const [step, setStep] = useState(1);
const totalSteps = purpose === 'sale' ? 4 : 4;
@ -171,15 +173,15 @@ export default function AddPropertyPage() {
const fileInputRef = useRef(null);
const propertyTypes = [
{ id: 'apartment', label: 'شقة', icon: Building },
{ id: 'villa', label: 'فيلا', icon: Home },
{ id: 'sweet', label: 'سويت', icon: Sofa },
{ id: 'room', label: 'غرفة ضمن شقة (سكن مشترك)', icon: DoorOpen },
{ id: 'studio', label: 'استوديو', icon: Sofa },
{ id: 'office', label: 'مكتب', icon: Building },
{ id: 'farms', label: 'مزرعة', icon: Trees },
{ id: 'shop', label: 'متجر', icon: Warehouse },
{ id: 'warehouse', label: 'مستودع', icon: Warehouse },
{ id: 'apartment', label: t('buildingType.apartment'), icon: Building },
{ id: 'villa', label: t('buildingType.villa'), icon: Home },
{ id: 'sweet', label: t('buildingType.sweet'), icon: Sofa },
{ id: 'room', label: t('buildingType.room'), icon: DoorOpen },
{ id: 'studio', label: t('buildingType.studio'), icon: Sofa },
{ id: 'office', label: t('buildingType.office'), icon: Building },
{ id: 'farms', label: t('buildingType.farms'), icon: Trees },
{ id: 'shop', label: t('buildingType.shop'), icon: Warehouse },
{ id: 'warehouse', label: t('buildingType.warehouse'), icon: Warehouse },
];
const serviceList = [
@ -199,9 +201,9 @@ export default function AddPropertyPage() {
];
const offerTypes = [
{ id: 'daily', label: 'إيجار يومي', icon: Clock },
{ id: 'monthly', label: 'إيجار شهري', icon: Calendar },
{ id: 'both', label: 'إيجار يومي وشهري', icon: Calendar },
{ id: 'daily', label: t('dailyRent'), icon: Clock },
{ id: 'monthly', label: t('monthlyRent'), icon: Calendar },
{ id: 'both', label: t('addProperty.dailyAndMonthly'), icon: Calendar },
].filter(Boolean);
useEffect(() => {
@ -218,7 +220,7 @@ export default function AddPropertyPage() {
const handleSearch = async () => {
if (!searchQuery) return;
toast.loading('جاري البحث...', { id: 'search' });
toast.loading(t('toast.searching'), { id: 'search' });
try {
const response = await fetch(
@ -245,23 +247,23 @@ export default function AddPropertyPage() {
address: addressData.display_name || result.display_name
});
toast.success('تم العثور على الموقع', { id: 'search' });
toast.success(t('toast.locationFound'), { id: 'search' });
} else {
toast.error('لم يتم العثور على العنوان', { id: 'search' });
toast.error(t('toast.locationNotFound'), { id: 'search' });
}
} catch (error) {
console.error('خطأ في البحث:', error);
toast.error('حدث خطأ في البحث', { id: 'search' });
console.error(t('search.error'), error);
toast.error(t('search.error'), { id: 'search' });
}
};
const handleGeolocation = () => {
if (!navigator.geolocation) {
toast.error('المتصفح لا يدعم تحديد الموقع');
if (!navigator.geolocation) {
toast.error(t('toast.geolocationNotSupported'));
return;
}
toast.loading('جاري تحديد موقعك...', { id: 'geolocation' });
toast.loading(t('toast.geolocationLoading'), { id: 'geolocation' });
navigator.geolocation.getCurrentPosition(
async (position) => {
@ -269,30 +271,30 @@ export default function AddPropertyPage() {
setMapCenter([latitude, longitude]);
setMapZoom(18);
try {
const response = await fetch(
`https://nominatim.openstreetmap.org/reverse?format=json&lat=${latitude}&lon=${longitude}&accept-language=ar`
);
const data = await response.json();
setSelectedLocation({
lat: latitude,
lng: longitude,
address: data.display_name || 'موقعك الحالي'
});
toast.success('تم تحديد موقعك', { id: 'geolocation' });
} catch (error) {
setSelectedLocation({
lat: latitude,
lng: longitude,
address: 'موقعك الحالي'
});
toast.success('تم تحديد موقعك', { id: 'geolocation' });
}
try {
const response = await fetch(
`https://nominatim.openstreetmap.org/reverse?format=json&lat=${latitude}&lon=${longitude}&accept-language=ar`
);
const data = await response.json();
setSelectedLocation({
lat: latitude,
lng: longitude,
address: data.display_name || t('addProperty.currentLocation')
});
toast.success(t('toast.geolocationSuccess'), { id: 'geolocation' });
} catch (error) {
setSelectedLocation({
lat: latitude,
lng: longitude,
address: t('addProperty.currentLocation')
});
toast.success(t('toast.geolocationSuccess'), { id: 'geolocation' });
}
},
(error) => {
toast.error('فشل في تحديد الموقع', { id: 'geolocation' });
toast.error(t('toast.geolocationFailed'), { id: 'geolocation' });
}
);
};
@ -301,32 +303,32 @@ const handleMapClick = async (coords) => {
try {
const [lat, lng] = coords;
toast.loading('جاري تحديد الموقع...', { id: 'location' });
toast.loading(t('addProperty.locationSelecting'), { id: 'location' });
const response = await fetch(
`https://nominatim.openstreetmap.org/reverse?format=json&lat=${lat}&lon=${lng}&accept-language=ar`
);
const data = await response.json();
setSelectedLocation({
lat: lat,
lng: lng,
address: data.display_name || 'موقع محدد'
});
setMapZoom(18);
toast.success('تم تحديد الموقع بنجاح!', { id: 'location' });
const response = await fetch(
`https://nominatim.openstreetmap.org/reverse?format=json&lat=${lat}&lon=${lng}&accept-language=ar`
);
const data = await response.json();
setSelectedLocation({
lat: lat,
lng: lng,
address: data.display_name || t('addProperty.defaultAddress')
});
setMapZoom(18);
toast.success(t('toast.locationSelectedSuccess'), { id: 'location' });
} catch (error) {
console.error('خطأ في تحديد الموقع:', error);
console.error(t('addProperty.locationError'), error);
const [lat, lng] = coords;
setSelectedLocation({
lat: lat,
lng: lng,
address: 'موقع محدد'
address: t('addProperty.defaultAddress')
});
setMapZoom(18);
toast.success('تم تحديد الموقع', { id: 'location' });
toast.success(t('toast.locationConfirmed'), { id: 'location' });
}
};
const handleMarkerDragEnd = async (lat, lng) => {
@ -338,13 +340,13 @@ const handleMarkerDragEnd = async (lat, lng) => {
setSelectedLocation({
lat,
lng,
address: data.display_name || 'موقع محدد'
address: data.display_name || t('addProperty.defaultAddress')
});
} catch (error) {
setSelectedLocation({
lat,
lng,
address: 'موقع محدد'
address: t('addProperty.defaultAddress')
});
}
};
@ -357,7 +359,7 @@ const handleMarkerDragEnd = async (lat, lng) => {
address: selectedLocation.address
});
toast.success('تم تأكيد الموقع بنجاح');
toast.success(t('toast.locationConfirmed'));
}
};
@ -370,25 +372,25 @@ const handleMarkerDragEnd = async (lat, lng) => {
address: ''
});
setMapZoom(15);
toast.info('تم إلغاء تحديد الموقع');
toast.info(t('toast.clearLocation'));
};
const handleImageUpload = async (files) => {
const newImages = Array.from(files);
if (formData.images.length + newImages.length > 5) {
toast.error('يمكنك رفع 5 صور كحد أقصى');
toast.error(t('toast.maxImages'));
return;
}
for (const file of newImages) {
if (!file.type.startsWith('image/')) {
toast.error('الرجاء اختيار صور صالحة فقط');
toast.error(t('toast.imageRequired'));
continue;
}
if (file.size > 5 * 1024 * 1024) {
toast.error('حجم الصورة يجب أن يكون أقل من 5 ميجابايت');
toast.error(t('toast.imageSize'));
continue;
}
@ -410,7 +412,7 @@ const handleMarkerDragEnd = async (lat, lng) => {
setUploadedImagePaths(prev => [...prev, path]);
} catch (err) {
console.error('[AddProperty] Image upload failed:', err);
toast.error('فشل رفع الصورة: ' + file.name);
toast.error(`${t('toast.imageUploadFailed')}: ${file.name}`);
}
}
};
@ -519,48 +521,48 @@ const handleMarkerDragEnd = async (lat, lng) => {
switch(step) {
case 1:
if (!formData.propertyType) {
newErrors.propertyType = 'نوع العقار مطلوب';
newErrors.propertyType = t('addProperty.propertyTypeRequired');
}
break;
case 2:
if (!formData.bedrooms) {
newErrors.bedrooms = 'عدد الغرف مطلوب';
newErrors.bedrooms = t('addProperty.bedroomsRequired');
}
if (!formData.bathrooms) {
newErrors.bathrooms = 'عدد الحمامات مطلوب';
newErrors.bathrooms = t('addProperty.bathroomsRequired');
}
if (!formData.livingRooms) {
newErrors.livingRooms = 'عدد الصالونات مطلوب';
newErrors.livingRooms = t('addProperty.livingRoomsRequired');
}
break;
case 3:
if (purpose === 'sale') {
if (!formData.salePrice) newErrors.salePrice = 'سعر البيع مطلوب';
if (!formData.salePrice) newErrors.salePrice = t('addProperty.salePriceRequired');
} else {
if (!formData.offerType) {
newErrors.offerType = 'الرجاء اختيار نوع العرض';
newErrors.offerType = t('addProperty.offerTypeRequired');
} else if (formData.offerType === 'daily' && !formData.dailyPrice) {
newErrors.dailyPrice = 'السعر اليومي مطلوب';
newErrors.dailyPrice = t('addProperty.dailyPriceRequired');
} else if (formData.offerType === 'monthly' && !formData.monthlyPrice) {
newErrors.monthlyPrice = 'السعر الشهري مطلوب';
newErrors.monthlyPrice = t('addProperty.monthlyPriceRequired');
} else if (formData.offerType === 'both') {
if (!formData.dailyPrice) newErrors.dailyPrice = 'السعر اليومي مطلوب';
if (!formData.monthlyPrice) newErrors.monthlyPrice = 'السعر الشهري مطلوب';
if (!formData.dailyPrice) newErrors.dailyPrice = t('addProperty.dailyPriceRequired');
if (!formData.monthlyPrice) newErrors.monthlyPrice = t('addProperty.monthlyPriceRequired');
}
}
break;
case 4:
if (!formData.lat || !formData.lng) {
newErrors.location = 'الرجاء تحديد موقع العقار على الخريطة';
newErrors.location = t('addProperty.locationRequired');
}
if (formData.images.length < 2) {
newErrors.images = 'يجب رفع صورتين على الأقل';
newErrors.images = t('addProperty.minImagesRequired');
}
if (formData.images.length > 5) {
newErrors.images = 'يمكن رفع 5 صور كحد أقصى';
newErrors.images = t('addProperty.maxImagesError');
}
break;
}
@ -670,7 +672,7 @@ const handleMarkerDragEnd = async (lat, lng) => {
currencyId: selectedCurrencyId,
};
const res = await addSaleProperty(payload);
toast.success('تم إضافة عقار للبيع بنجاح!');
toast.success(t('toast.salePropertySuccess'));
} else {
const rentTypeMap = { daily: RentType.DAILY, monthly: RentType.MONTHLY, both: RentType.MONTHLY };
const payload = {
@ -685,14 +687,14 @@ const handleMarkerDragEnd = async (lat, lng) => {
allowedPaymentPeriod: formData.allowedPaymentPeriod || '',
};
const res = await addRentProperty(payload);
toast.success('تم إضافة عقار للإيجار بنجاح!');
toast.success(t('toast.rentPropertySuccess'));
}
setTimeout(() => {
router.push('/owner/properties');
}, 1500);
} catch (err) {
console.error('[AddProperty] API error:', err);
toast.error(err.message || 'فشل في إضافة العقار');
toast.error(err.message || t('toast.propertyAddFailed'));
} finally {
setIsLoading(false);
}
@ -713,15 +715,15 @@ const handleMarkerDragEnd = async (lat, lng) => {
<div className="container mx-auto px-4 max-w-4xl">
<div className="mb-8">
<div className="flex items-center justify-between mb-4">
<Link
href="/owner/properties"
className="flex items-center gap-2 text-gray-600 hover:text-amber-600 transition-colors group"
>
<ArrowLeft className="w-4 h-4 group-hover:-translate-x-1 transition-transform" />
<span>العودة للعقارات</span>
</Link>
<Link
href="/owner/properties"
className="flex items-center gap-2 text-gray-600 hover:text-amber-600 transition-colors group"
>
<ArrowLeft className="w-4 h-4 group-hover:-translate-x-1 transition-transform" />
<span>{t('addProperty.backToProperties')}</span>
</Link>
<div className="flex items-center gap-2">
<span className="text-sm font-medium text-amber-600">خطوة {step} من {totalSteps}</span>
<span className="text-sm font-medium text-amber-600">{t('addProperty.stepCounter', { step, total: totalSteps })}</span>
</div>
</div>
@ -738,10 +740,10 @@ const handleMarkerDragEnd = async (lat, lng) => {
</div>
<div className="flex justify-between mt-2 text-xs text-gray-500">
<span>معلومات العقار</span>
<span>التفاصيل والخدمات</span>
<span>{purpose === 'sale' ? 'سعر البيع' : 'السعر'}</span>
<span>الموقع والصور</span>
<span>{t('addProperty.stepInfo')}</span>
<span>{t('addProperty.stepDetails')}</span>
<span>{purpose === 'sale' ? t('addProperty.stepSalePrice') : t('addProperty.stepRentPrice')}</span>
<span>{t('addProperty.stepLocation')}</span>
</div>
</div>
@ -759,14 +761,14 @@ const handleMarkerDragEnd = async (lat, lng) => {
<div className="w-20 h-20 bg-amber-100 rounded-2xl flex items-center justify-center mx-auto mb-4">
<Home className="w-10 h-10 text-amber-600" />
</div>
<h2 className="text-2xl font-bold text-gray-900 mb-2">معلومات العقار</h2>
<p className="text-gray-600">اختر نوع العقار والحالة</p>
<h2 className="text-2xl font-bold text-gray-900 mb-2">{t('addProperty.stepInfo')}</h2>
<p className="text-gray-600">{t('addProperty.propertyInfoSubtitle')}</p>
</div>
<div>
<label className="block text-sm font-medium text-gray-700 mb-3">
نوع العقار <span className="text-red-500">*</span>
</label>
<label className="block text-sm font-medium text-gray-700 mb-3">
{t('addProperty.propertyTypeLabel')} <span className="text-red-500">*</span>
</label>
<div className="grid grid-cols-2 md:grid-cols-4 gap-3">
{propertyTypes.map((type) => {
const Icon = type.icon;
@ -794,7 +796,7 @@ const handleMarkerDragEnd = async (lat, lng) => {
<div>
<label className="block text-sm font-medium text-gray-700 mb-3">
حالة العقار
{t('addProperty.furnishedStatus')}
</label>
<div className="flex gap-4">
<label className="flex items-center gap-2 p-3 border rounded-xl cursor-pointer hover:bg-gray-50 flex-1">
@ -805,7 +807,7 @@ const handleMarkerDragEnd = async (lat, lng) => {
onChange={() => setFormData({...formData, furnished: true})}
className="w-4 h-4 text-amber-500"
/>
<span className="text-gray-700">مفروش</span>
<span className="text-gray-700">{t('addProperty.furnished')}</span>
</label>
<label className="flex items-center gap-2 p-3 border rounded-xl cursor-pointer hover:bg-gray-50 flex-1">
<input
@ -815,21 +817,21 @@ const handleMarkerDragEnd = async (lat, lng) => {
onChange={() => setFormData({...formData, furnished: false})}
className="w-4 h-4 text-amber-500"
/>
<span className="text-gray-700">غير مفروش</span>
<span className="text-gray-700">{t('addProperty.unfurnished')}</span>
</label>
</div>
</div>
<div>
<label className="block text-sm font-medium text-gray-700 mb-2">
وصف إضافي (اختياري)
{t('addProperty.additionalDescription')}
</label>
<textarea
value={formData.description}
onChange={(e) => setFormData({...formData, description: e.target.value})}
rows="4"
className="w-full px-4 py-3 border border-gray-300 rounded-xl focus:outline-none focus:ring-2 focus:ring-amber-500"
placeholder="أضف وصفاً إضافياً للعقار..."
placeholder={t('addProperty.descriptionPlaceholder')}
/>
</div>
</motion.div>
@ -841,14 +843,14 @@ const handleMarkerDragEnd = async (lat, lng) => {
<div className="w-20 h-20 bg-amber-100 rounded-2xl flex items-center justify-center mx-auto mb-4">
<Layers className="w-10 h-10 text-amber-600" />
</div>
<h2 className="text-2xl font-bold text-gray-900 mb-2">تفاصيل العقار</h2>
<p className="text-gray-600">أدخل التفاصيل والخدمات المتاحة</p>
<h2 className="text-2xl font-bold text-gray-900 mb-2">{t('addProperty.propertyDetailsTitle')}</h2>
<p className="text-gray-600">{t('addProperty.propertyDetailsSubtitle')}</p>
</div>
<div className="grid grid-cols-1 md:grid-cols-4 gap-6">
<div>
<label className="block text-sm font-medium text-gray-700 mb-2">
المساحة (م²)
{t('addProperty.space')}
</label>
<div className="relative">
<Square className="absolute right-3 top-1/2 transform -translate-y-1/2 w-4 h-4 text-gray-400" />
@ -858,14 +860,14 @@ const handleMarkerDragEnd = async (lat, lng) => {
value={formData.space}
onChange={(e) => setFormData({...formData, space: e.target.value})}
className="w-full pr-10 pl-3 py-3 border border-gray-300 rounded-xl focus:outline-none focus:ring-2 focus:ring-amber-500"
placeholder="مثال: 120"
placeholder={t('addProperty.spacePlaceholder')}
/>
</div>
</div>
<div>
<label className="block text-sm font-medium text-gray-700 mb-2">
عدد الغرف <span className="text-red-500">*</span>
{t('addProperty.bedrooms')} <span className="text-red-500">*</span>
</label>
<div className="flex items-center gap-2">
<button
@ -893,7 +895,7 @@ const handleMarkerDragEnd = async (lat, lng) => {
<div>
<label className="block text-sm font-medium text-gray-700 mb-2">
عدد الحمامات <span className="text-red-500">*</span>
{t('addProperty.bathrooms')} <span className="text-red-500">*</span>
</label>
<div className="flex items-center gap-2">
<button
@ -921,7 +923,7 @@ const handleMarkerDragEnd = async (lat, lng) => {
<div>
<label className="block text-sm font-medium text-gray-700 mb-2">
عدد الصالونات <span className="text-red-500">*</span>
{t('addProperty.livingRooms')} <span className="text-red-500">*</span>
</label>
<div className="flex items-center gap-2">
<button
@ -950,41 +952,41 @@ const handleMarkerDragEnd = async (lat, lng) => {
<div className="grid grid-cols-1 md:grid-cols-3 gap-4">
<div>
<label className="block text-sm font-medium text-gray-700 mb-2">رقم الطابق</label>
<label className="block text-sm font-medium text-gray-700 mb-2">{t('addProperty.floorNumber')}</label>
<input
type="number"
value={formData.floorNumber}
onChange={(e) => setFormData({...formData, floorNumber: e.target.value})}
className="w-full px-4 py-3 border border-gray-300 rounded-xl focus:outline-none focus:ring-2 focus:ring-amber-500"
placeholder="مثال: 3"
placeholder={t('addProperty.floorPlaceholder')}
/>
</div>
<div>
<label className="block text-sm font-medium text-gray-700 mb-2">عدد الصالونات</label>
<label className="block text-sm font-medium text-gray-700 mb-2">{t('addProperty.salons')}</label>
<input
type="number"
min="0"
value={formData.salons}
onChange={(e) => setFormData({...formData, salons: e.target.value})}
className="w-full px-4 py-3 border border-gray-300 rounded-xl focus:outline-none focus:ring-2 focus:ring-amber-500"
placeholder="مثال: 1"
placeholder={t('addProperty.salonsPlaceholder')}
/>
</div>
<div>
<label className="block text-sm font-medium text-gray-700 mb-2">عدد الشرفات</label>
<label className="block text-sm font-medium text-gray-700 mb-2">{t('addProperty.balconies')}</label>
<input
type="number"
min="0"
value={formData.balconies}
onChange={(e) => setFormData({...formData, balconies: e.target.value})}
className="w-full px-4 py-3 border border-gray-300 rounded-xl focus:outline-none focus:ring-2 focus:ring-amber-500"
placeholder="مثال: 1"
placeholder={t('addProperty.balconiesPlaceholder')}
/>
</div>
</div>
<div>
<h3 className="text-lg font-bold text-gray-900 mb-4">الخدمات المتوفرة <span className="text-red-500">*</span></h3>
<h3 className="text-lg font-bold text-gray-900 mb-4">{t('addProperty.servicesLabel')} <span className="text-red-500">*</span></h3>
<div className="space-y-3">
{serviceList.map((service) => {
const Icon = service.icon;
@ -1010,7 +1012,7 @@ const handleMarkerDragEnd = async (lat, lng) => {
value={formData.serviceDetails[service.id] || ''}
onChange={(e) => updateServiceDetail(service.id, e.target.value)}
className="w-full px-3 py-2 border border-gray-200 rounded-lg text-sm focus:outline-none focus:ring-2 focus:ring-amber-500"
placeholder="تفاصيل الخدمة (مثال: في جميع الغرف)"
placeholder={t('addProperty.serviceDetailPlaceholder')}
/>
</div>
)}
@ -1023,11 +1025,11 @@ const handleMarkerDragEnd = async (lat, lng) => {
<div className="bg-white rounded-xl p-6 border border-gray-200 mt-6">
<h3 className="text-lg font-bold text-gray-800 mb-4 flex items-center gap-2">
<MapPin className="w-5 h-5 text-amber-600" />
القرب من الخدمات
{t('addProperty.nearbyServicesTitle')}
</h3>
<div className="grid grid-cols-1 md:grid-cols-2 gap-4">
<div>
<label className="block text-sm font-medium text-gray-700 mb-2">المسافة إلى أقرب مدرسة (كم)</label>
<label className="block text-sm font-medium text-gray-700 mb-2">{t('addProperty.nearbySchoolLabel')}</label>
<input
type="number"
min="0"
@ -1035,11 +1037,11 @@ const handleMarkerDragEnd = async (lat, lng) => {
value={formData.nearbySchool}
onChange={(e) => setFormData({...formData, nearbySchool: e.target.value})}
className="w-full px-4 py-3 border border-gray-300 rounded-xl focus:outline-none focus:ring-2 focus:ring-amber-500"
placeholder="مثال: 0.5"
placeholder={t('addProperty.nearbyPlaceholder')}
/>
</div>
<div>
<label className="block text-sm font-medium text-gray-700 mb-2">المسافة إلى أقرب مستشفى (كم)</label>
<label className="block text-sm font-medium text-gray-700 mb-2">{t('addProperty.nearbyHospitalLabel')}</label>
<input
type="number"
min="0"
@ -1047,11 +1049,11 @@ const handleMarkerDragEnd = async (lat, lng) => {
value={formData.nearbyHospital}
onChange={(e) => setFormData({...formData, nearbyHospital: e.target.value})}
className="w-full px-4 py-3 border border-gray-300 rounded-xl focus:outline-none focus:ring-2 focus:ring-amber-500"
placeholder="مثال: 1.0"
placeholder={t('addProperty.nearbyPlaceholder')}
/>
</div>
<div>
<label className="block text-sm font-medium text-gray-700 mb-2">المسافة إلى أقرب مطعم (كم)</label>
<label className="block text-sm font-medium text-gray-700 mb-2">{t('addProperty.nearbyRestaurantLabel')}</label>
<input
type="number"
min="0"
@ -1059,11 +1061,11 @@ const handleMarkerDragEnd = async (lat, lng) => {
value={formData.nearbyRestaurant}
onChange={(e) => setFormData({...formData, nearbyRestaurant: e.target.value})}
className="w-full px-4 py-3 border border-gray-300 rounded-xl focus:outline-none focus:ring-2 focus:ring-amber-500"
placeholder="مثال: 0.3"
placeholder={t('addProperty.nearbyPlaceholder')}
/>
</div>
<div>
<label className="block text-sm font-medium text-gray-700 mb-2">المسافة إلى أقرب جامعة (كم)</label>
<label className="block text-sm font-medium text-gray-700 mb-2">{t('addProperty.nearbyUniversityLabel')}</label>
<input
type="number"
min="0"
@ -1071,11 +1073,11 @@ const handleMarkerDragEnd = async (lat, lng) => {
value={formData.nearbyUniversity}
onChange={(e) => setFormData({...formData, nearbyUniversity: e.target.value})}
className="w-full px-4 py-3 border border-gray-300 rounded-xl focus:outline-none focus:ring-2 focus:ring-amber-500"
placeholder="مثال: 2.0"
placeholder={t('addProperty.nearbyPlaceholder')}
/>
</div>
<div>
<label className="block text-sm font-medium text-gray-700 mb-2">المسافة إلى أقرب حديقة (كم)</label>
<label className="block text-sm font-medium text-gray-700 mb-2">{t('addProperty.nearbyParkLabel')}</label>
<input
type="number"
min="0"
@ -1083,11 +1085,11 @@ const handleMarkerDragEnd = async (lat, lng) => {
value={formData.nearbyPark}
onChange={(e) => setFormData({...formData, nearbyPark: e.target.value})}
className="w-full px-4 py-3 border border-gray-300 rounded-xl focus:outline-none focus:ring-2 focus:ring-amber-500"
placeholder="مثال: 0.5"
placeholder={t('addProperty.nearbyPlaceholder')}
/>
</div>
<div>
<label className="block text-sm font-medium text-gray-700 mb-2">المسافة إلى أقرب مول (كم)</label>
<label className="block text-sm font-medium text-gray-700 mb-2">{t('addProperty.nearbyMallLabel')}</label>
<input
type="number"
min="0"
@ -1095,7 +1097,7 @@ const handleMarkerDragEnd = async (lat, lng) => {
value={formData.nearbyMall}
onChange={(e) => setFormData({...formData, nearbyMall: e.target.value})}
className="w-full px-4 py-3 border border-gray-300 rounded-xl focus:outline-none focus:ring-2 focus:ring-amber-500"
placeholder="مثال: 1.5"
placeholder={t('addProperty.nearbyPlaceholder')}
/>
</div>
</div>
@ -1103,7 +1105,7 @@ const handleMarkerDragEnd = async (lat, lng) => {
{purpose === 'rent' && (
<div>
<h3 className="text-lg font-bold text-gray-900 mb-4">شروط استخدام العقار</h3>
<h3 className="text-lg font-bold text-gray-900 mb-4">{t('addProperty.termsTitle')}</h3>
<div className="grid grid-cols-2 md:grid-cols-3 gap-3">
{termsList.map((term) => {
const Icon = term.icon;
@ -1137,14 +1139,14 @@ const handleMarkerDragEnd = async (lat, lng) => {
{/* Custom Terms */}
<div className="mt-4 p-4 border border-dashed border-gray-300 rounded-xl">
<p className="text-sm font-medium text-gray-700 mb-2">إضافة شرط مخصص</p>
<p className="text-sm font-medium text-gray-700 mb-2">{t('addProperty.customTermsTitle')}</p>
<div className="flex gap-2">
<input
type="text"
value={customTermInput}
onChange={(e) => setCustomTermInput(e.target.value)}
onKeyDown={(e) => { if (e.key === 'Enter') { e.preventDefault(); addCustomTerm(); } }}
placeholder="اكتب شرطاً مخصصاً..."
placeholder={t('addProperty.customTermPlaceholder')}
className="flex-1 px-3 py-2 border border-gray-300 rounded-lg text-sm focus:ring-2 focus:ring-amber-500 focus:border-transparent outline-none"
/>
<button
@ -1153,7 +1155,7 @@ const handleMarkerDragEnd = async (lat, lng) => {
disabled={!customTermInput.trim()}
className="px-4 py-2 bg-amber-500 text-white rounded-lg text-sm font-medium hover:bg-amber-600 disabled:bg-gray-300 disabled:cursor-not-allowed transition-colors"
>
إضافة
{t('addProperty.add')}
</button>
</div>
{customTerms.length > 0 && (
@ -1187,13 +1189,13 @@ const handleMarkerDragEnd = async (lat, lng) => {
<div className="w-20 h-20 bg-amber-100 rounded-2xl flex items-center justify-center mx-auto mb-4">
<DollarSign className="w-10 h-10 text-amber-600" />
</div>
<h2 className="text-2xl font-bold text-gray-900 mb-2">سعر البيع</h2>
<p className="text-gray-600">حدد سعر البيع والعملة</p>
<h2 className="text-2xl font-bold text-gray-900 mb-2">{t('addProperty.salePriceTitle')}</h2>
<p className="text-gray-600">{t('addProperty.salePriceSubtitle')}</p>
</div>
<div>
<label className="block text-sm font-medium text-gray-700 mb-2">
سعر البيع (ل.س) <span className="text-red-500">*</span>
{t('addProperty.salePriceLabel')} <span className="text-red-500">*</span>
</label>
<div className="relative">
<span className="absolute right-3 top-1/2 transform -translate-y-1/2 w-5 h-5 text-gray-400 font-bold text-sm flex items-center justify-center">{currencySymbol}</span>
@ -1211,7 +1213,7 @@ const handleMarkerDragEnd = async (lat, lng) => {
className={`w-full pr-12 pl-4 py-3 border rounded-xl focus:outline-none focus:ring-2 focus:ring-amber-500 ${
errors.salePrice ? 'border-red-500' : 'border-gray-300'
}`}
placeholder="مثال: 50000000"
placeholder={t('addProperty.salePricePlaceholder')}
/>
</div>
{errors.salePrice && <p className="text-red-500 text-sm mt-1">{errors.salePrice}</p>}
@ -1219,16 +1221,16 @@ const handleMarkerDragEnd = async (lat, lng) => {
<div>
<label className="block text-sm font-medium text-gray-700 mb-2">
العملة <span className="text-red-500">*</span>
{t('addProperty.currencyLabel')} <span className="text-red-500">*</span>
</label>
<select
value={selectedCurrencyId}
onChange={(e) => setSelectedCurrencyId(parseInt(e.target.value))}
className="w-full px-4 py-3 border border-gray-300 rounded-xl focus:outline-none focus:ring-2 focus:ring-amber-500"
>
<option value={Currency.USD}>دولار امريكي $</option>
<option value={Currency.SYP}>عملة سورية SP</option>
</select>
<select
value={selectedCurrencyId}
onChange={(e) => setSelectedCurrencyId(parseInt(e.target.value))}
className="w-full px-4 py-3 border border-gray-300 rounded-xl focus:outline-none focus:ring-2 focus:ring-amber-500"
>
<option value={Currency.USD}>{t('addProperty.usdOption')}</option>
<option value={Currency.SYP}>{t('addProperty.sypOption')}</option>
</select>
</div>
</motion.div>
)}
@ -1239,13 +1241,13 @@ const handleMarkerDragEnd = async (lat, lng) => {
<div className="w-20 h-20 bg-amber-100 rounded-2xl flex items-center justify-center mx-auto mb-4">
<DollarSign className="w-10 h-10 text-amber-600" />
</div>
<h2 className="text-2xl font-bold text-gray-900 mb-2">نوع العرض والسعر</h2>
<p className="text-gray-600">اختر نوع العرض وحدد السعر المناسب</p>
<h2 className="text-2xl font-bold text-gray-900 mb-2">{t('addProperty.offerAndPriceTitle')}</h2>
<p className="text-gray-600">{t('addProperty.offerAndPriceSubtitle')}</p>
</div>
<div>
<label className="block text-sm font-medium text-gray-700 mb-3">
نوع العرض <span className="text-red-500">*</span>
{t('addProperty.offerTypeLabel')} <span className="text-red-500">*</span>
</label>
<div className="grid grid-cols-2 md:grid-cols-4 gap-3">
{offerTypes.map((type) => {
@ -1272,15 +1274,15 @@ const handleMarkerDragEnd = async (lat, lng) => {
{/* Currency dropdown */}
<div>
<label className="block text-sm font-medium text-gray-700 mb-2">
العملة <span className="text-red-500">*</span>
{t('addProperty.currencyLabel')} <span className="text-red-500">*</span>
</label>
<select
value={selectedCurrencyId}
onChange={(e) => setSelectedCurrencyId(parseInt(e.target.value))}
className="w-full px-4 py-3 border border-gray-300 rounded-xl focus:outline-none focus:ring-2 focus:ring-amber-500"
>
<option value={Currency.USD}>دولار امريكي $</option>
<option value={Currency.SYP}>عملة سورية SP</option>
<option value={Currency.USD}>{t('addProperty.usdOption')}</option>
<option value={Currency.SYP}>{t('addProperty.sypOption')}</option>
</select>
</div>
@ -1292,7 +1294,7 @@ const handleMarkerDragEnd = async (lat, lng) => {
{!formData.offerType && (
<div className="border-2 border-dashed border-gray-200 rounded-2xl p-8 text-center">
<DollarSign className="w-8 h-8 text-gray-300 mx-auto mb-3" />
<p className="text-gray-400 font-medium">اختر نوع العرض اولا</p>
<p className="text-gray-400 font-medium">{t('addProperty.selectOfferTypeFirst')}</p>
</div>
)}
@ -1307,7 +1309,7 @@ const handleMarkerDragEnd = async (lat, lng) => {
>
<div>
<label className="block text-sm font-medium text-gray-700 mb-2">
سعر الايجار اليومي ({currencySymbol}) <span className="text-red-500">*</span>
{t('addProperty.dailyPriceLabel')} ({currencySymbol}) <span className="text-red-500">*</span>
</label>
<div className="relative">
<span className="absolute right-3 top-1/2 transform -translate-y-1/2 w-5 h-5 text-gray-400 font-bold text-sm flex items-center justify-center">{currencySymbol}</span>
@ -1325,7 +1327,7 @@ const handleMarkerDragEnd = async (lat, lng) => {
className={`w-full pr-12 pl-4 py-3 border rounded-xl focus:outline-none focus:ring-2 focus:ring-amber-500 ${
errors.dailyPrice ? 'border-red-500' : 'border-gray-300'
}`}
placeholder="مثال: 50000"
placeholder={t('addProperty.dailyPricePlaceholder')}
/>
</div>
{errors.dailyPrice && (
@ -1334,7 +1336,7 @@ const handleMarkerDragEnd = async (lat, lng) => {
</div>
<div>
<label className="block text-sm font-medium text-gray-700 mb-2">
مبلغ التأمين ({currencySymbol})
{t('addProperty.depositLabel')} ({currencySymbol})
</label>
<div className="relative">
<span className="absolute right-3 top-1/2 transform -translate-y-1/2 w-5 h-5 text-gray-400 font-bold text-sm flex items-center justify-center">{currencySymbol}</span>
@ -1350,7 +1352,7 @@ const handleMarkerDragEnd = async (lat, lng) => {
}}
onKeyDown={(e) => { if (['-', 'e', 'E', '+'].includes(e.key)) e.preventDefault() }}
className="w-full pr-12 pl-4 py-3 border border-gray-300 rounded-xl focus:outline-none focus:ring-2 focus:ring-amber-500"
placeholder="مثال: 500000"
placeholder={t('addProperty.depositPlaceholder')}
/>
</div>
</div>
@ -1367,7 +1369,7 @@ const handleMarkerDragEnd = async (lat, lng) => {
>
<div>
<label className="block text-sm font-medium text-gray-700 mb-2">
سعر الايجار الشهري ({currencySymbol}) <span className="text-red-500">*</span>
{t('addProperty.monthlyPriceLabel')} ({currencySymbol}) <span className="text-red-500">*</span>
</label>
<div className="relative">
<span className="absolute right-3 top-1/2 transform -translate-y-1/2 w-5 h-5 text-gray-400 font-bold text-sm flex items-center justify-center">{currencySymbol}</span>
@ -1385,7 +1387,7 @@ const handleMarkerDragEnd = async (lat, lng) => {
className={`w-full pr-12 pl-4 py-3 border rounded-xl focus:outline-none focus:ring-2 focus:ring-amber-500 ${
errors.monthlyPrice ? 'border-red-500' : 'border-gray-300'
}`}
placeholder="مثال: 1000000"
placeholder={t('addProperty.monthlyPricePlaceholder')}
/>
</div>
{errors.monthlyPrice && (
@ -1394,7 +1396,7 @@ const handleMarkerDragEnd = async (lat, lng) => {
</div>
<div>
<label className="block text-sm font-medium text-gray-700 mb-2">
مبلغ التأمين ({currencySymbol})
{t('addProperty.depositLabel')} ({currencySymbol})
</label>
<div className="relative">
<span className="absolute right-3 top-1/2 transform -translate-y-1/2 w-5 h-5 text-gray-400 font-bold text-sm flex items-center justify-center">{currencySymbol}</span>
@ -1410,7 +1412,7 @@ const handleMarkerDragEnd = async (lat, lng) => {
}}
onKeyDown={(e) => { if (['-', 'e', 'E', '+'].includes(e.key)) e.preventDefault() }}
className="w-full pr-12 pl-4 py-3 border border-gray-300 rounded-xl focus:outline-none focus:ring-2 focus:ring-amber-500"
placeholder="مثال: 500000"
placeholder={t('addProperty.depositPlaceholder')}
/>
</div>
</div>
@ -1427,7 +1429,7 @@ const handleMarkerDragEnd = async (lat, lng) => {
>
<div>
<label className="block text-sm font-medium text-gray-700 mb-2">
سعر الايجار اليومي ({currencySymbol}) <span className="text-red-500">*</span>
{t('addProperty.dailyPriceLabel')} ({currencySymbol}) <span className="text-red-500">*</span>
</label>
<div className="relative">
<span className="absolute right-3 top-1/2 transform -translate-y-1/2 w-5 h-5 text-gray-400 font-bold text-sm flex items-center justify-center">{currencySymbol}</span>
@ -1445,7 +1447,7 @@ const handleMarkerDragEnd = async (lat, lng) => {
className={`w-full pr-12 pl-4 py-3 border rounded-xl focus:outline-none focus:ring-2 focus:ring-amber-500 ${
errors.dailyPrice ? 'border-red-500' : 'border-gray-300'
}`}
placeholder="مثال: 50000"
placeholder={t('addProperty.dailyPricePlaceholder')}
/>
</div>
{errors.dailyPrice && (
@ -1454,7 +1456,7 @@ const handleMarkerDragEnd = async (lat, lng) => {
</div>
<div>
<label className="block text-sm font-medium text-gray-700 mb-2">
سعر الايجار الشهري ({currencySymbol}) <span className="text-red-500">*</span>
{t('addProperty.monthlyPriceLabel')} ({currencySymbol}) <span className="text-red-500">*</span>
</label>
<div className="relative">
<span className="absolute right-3 top-1/2 transform -translate-y-1/2 w-5 h-5 text-gray-400 font-bold text-sm flex items-center justify-center">{currencySymbol}</span>
@ -1472,7 +1474,7 @@ const handleMarkerDragEnd = async (lat, lng) => {
className={`w-full pr-12 pl-4 py-3 border rounded-xl focus:outline-none focus:ring-2 focus:ring-amber-500 ${
errors.monthlyPrice ? 'border-red-500' : 'border-gray-300'
}`}
placeholder="مثال: 1000000"
placeholder={t('addProperty.monthlyPricePlaceholder')}
/>
</div>
{errors.monthlyPrice && (
@ -1481,7 +1483,7 @@ const handleMarkerDragEnd = async (lat, lng) => {
</div>
<div>
<label className="block text-sm font-medium text-gray-700 mb-2">
مبلغ التأمين ({currencySymbol})
{t('addProperty.depositLabel')} ({currencySymbol})
</label>
<div className="relative">
<span className="absolute right-3 top-1/2 transform -translate-y-1/2 w-5 h-5 text-gray-400 font-bold text-sm flex items-center justify-center">{currencySymbol}</span>
@ -1497,7 +1499,7 @@ const handleMarkerDragEnd = async (lat, lng) => {
}}
onKeyDown={(e) => { if (['-', 'e', 'E', '+'].includes(e.key)) e.preventDefault() }}
className="w-full pr-12 pl-4 py-3 border border-gray-300 rounded-xl focus:outline-none focus:ring-2 focus:ring-amber-500"
placeholder="مثال: 500000"
placeholder={t('addProperty.depositPlaceholder')}
/>
</div>
</div>
@ -1513,12 +1515,12 @@ const handleMarkerDragEnd = async (lat, lng) => {
<div className="w-20 h-20 bg-amber-100 rounded-2xl flex items-center justify-center mx-auto mb-4">
<MapPin className="w-10 h-10 text-amber-600" />
</div>
<h2 className="text-2xl font-bold text-gray-900 mb-2">الموقع والصور</h2>
<p className="text-gray-600">حدد موقع العقار وأضف الصور</p>
<h2 className="text-2xl font-bold text-gray-900 mb-2">{t('addProperty.locationAndImagesTitle')}</h2>
<p className="text-gray-600">{t('addProperty.locationAndImagesSubtitle')}</p>
</div>
<div>
<h3 className="text-lg font-bold text-gray-900 mb-4">حدد موقع العقار على الخريطة</h3>
<h3 className="text-lg font-bold text-gray-900 mb-4">{t('addProperty.selectLocationTitle')}</h3>
<div className="flex gap-2 mb-4">
<div className="flex-1 relative">
@ -1527,7 +1529,7 @@ const handleMarkerDragEnd = async (lat, lng) => {
value={searchQuery}
onChange={(e) => setSearchQuery(e.target.value)}
onKeyPress={(e) => e.key === 'Enter' && handleSearch()}
placeholder="ابحث عن عنوان..."
placeholder={t('addProperty.searchAddress')}
className="w-full px-4 py-3 border border-gray-300 rounded-xl focus:outline-none focus:ring-2 focus:ring-amber-500 pr-12"
/>
<Search className="absolute left-4 top-1/2 transform -translate-y-1/2 w-5 h-5 text-gray-400" />
@ -1536,16 +1538,16 @@ const handleMarkerDragEnd = async (lat, lng) => {
onClick={handleSearch}
className="px-6 py-3 bg-amber-500 text-white rounded-xl hover:bg-amber-600 transition-colors"
>
بحث
{t('addProperty.searchButton')}
</button>
</div>
<div className="bg-amber-50 border border-amber-200 rounded-xl p-4 mb-4 text-sm text-amber-800 leading-relaxed">
<Search className="w-4 h-4 inline ml-1" />
ابحث أولاً ثم اضغط على النتيجة لتحريك الخريطة و تثبيت العلامة .
{t('addProperty.searchHint')}
<br />
<MapPin className="w-4 h-4 inline ml-1" />
اضغط على الخريطة لتحديد موقع العقار
{t('addProperty.clickMapHint')}
</div>
<div className="relative w-full h-96 rounded-xl overflow-hidden border-2 border-gray-200 mb-4">
@ -1564,7 +1566,7 @@ const handleMarkerDragEnd = async (lat, lng) => {
className="w-full mb-4 bg-green-500 text-white py-3 rounded-xl font-medium hover:bg-green-600 transition-colors flex items-center justify-center gap-2"
>
<CheckCircle className="w-5 h-5" />
تأكيد هذا الموقع
{t('addProperty.confirmLocation')}
</button>
)}
@ -1572,7 +1574,7 @@ const handleMarkerDragEnd = async (lat, lng) => {
<div className="bg-green-50 border border-green-200 rounded-xl p-4 mb-4">
<div className="flex items-center gap-2">
<CheckCircle className="w-5 h-5 text-green-600" />
<span className="text-green-800 font-medium">تم تأكيد الموقع:</span>
<span className="text-green-800 font-medium">{t('addProperty.locationConfirmed')}:</span>
</div>
<p className="text-green-700 text-sm mt-2 line-clamp-2">{formData.address}</p>
</div>
@ -1584,7 +1586,7 @@ const handleMarkerDragEnd = async (lat, lng) => {
</div>
<div>
<h3 className="text-lg font-bold text-gray-900 mb-4">صور العقار</h3>
<h3 className="text-lg font-bold text-gray-900 mb-4">{t('addProperty.imagesTitle')}</h3>
<div
onClick={() => fileInputRef.current?.click()}
@ -1602,16 +1604,16 @@ const handleMarkerDragEnd = async (lat, lng) => {
/>
<Upload className="w-12 h-12 text-gray-400 mx-auto mb-3" />
<p className="text-gray-600 font-medium">اضغط لرفع الصور</p>
<p className="text-gray-600 font-medium">{t('addProperty.uploadImages')}</p>
<p className="text-xs text-gray-500 mt-2">
JPEG, PNG, JPG حتى 5MB 800x600 بكسل 2-5 صور
{t('addProperty.imageFormatHint')}
</p>
</div>
<p className="text-sm text-gray-500 mt-2 mb-4 text-center">
يرجى رفع عدة صور واضحة
{t('addProperty.uploadMultipleHintLine1')}
<br />
(اضغط على الرفع أكثر من مرة)
{t('addProperty.uploadMultipleHintLine2')}
</p>
{errors.images && (
@ -1649,8 +1651,7 @@ const handleMarkerDragEnd = async (lat, lng) => {
<div className="border-t border-gray-200 pt-6 mt-8">
<div className="bg-gray-50 rounded-2xl p-5 border border-gray-200">
<p className="text-sm text-gray-700 leading-relaxed mb-4">
أنا صاحب العقار أقر أنني مالك العقار أو مخول قانونياً بعرضه للإيجار أو البيع،
وأن جميع المعلومات المدخلة صحيحة، وأتحمل كامل المسؤولية عن أي معلومات غير صحيحة.
{t('addProperty.ownerAgreement')}
</p>
<label className="flex items-start gap-3 cursor-pointer">
<input
@ -1660,9 +1661,9 @@ const handleMarkerDragEnd = async (lat, lng) => {
className="w-5 h-5 mt-0.5 text-amber-500 rounded shrink-0"
/>
<span className="text-sm text-gray-600">
أوافق على{' '}
{t('addProperty.agreeToText')}{' '}
<Link href="/terms" target="_blank" className="text-amber-600 underline hover:text-amber-700">
شروط الاستخدام
{t('addProperty.termsOfUseText')}
</Link>
</span>
</label>
@ -1680,7 +1681,7 @@ const handleMarkerDragEnd = async (lat, lng) => {
className="flex-1 py-3 px-4 bg-gray-100 text-gray-700 rounded-xl font-medium hover:bg-gray-200 transition-colors flex items-center justify-center gap-2"
>
<ChevronRight className="w-5 h-5" />
السابق
{t('addProperty.previous')}
</button>
)}
@ -1691,7 +1692,7 @@ const handleMarkerDragEnd = async (lat, lng) => {
step === 1 ? 'w-full' : ''
}`}
>
التالي
{t('addProperty.next')}
<ChevronLeft className="w-5 h-5" />
</button>
) : (
@ -1703,12 +1704,12 @@ const handleMarkerDragEnd = async (lat, lng) => {
{isLoading ? (
<>
<Loader2 className="w-5 h-5 animate-spin" />
جاري الحفظ...
{t('addProperty.saving')}
</>
) : (
<>
<Save className="w-5 h-5" />
حفظ العقار
{t('addProperty.saveProperty')}
</>
)}
</button>

File diff suppressed because it is too large Load Diff

View File

@ -643,12 +643,12 @@
'use client';
import { useState, useEffect, useCallback } from 'react';
import { motion } from 'framer-motion';
import { useRouter } from 'next/navigation';
import { useTranslation } from 'react-i18next';
import {
Calendar,
Clock,
@ -660,8 +660,6 @@ import {
MapPin,
DollarSign,
Home,
ArrowLeft,
RefreshCw,
Flag,
} from 'lucide-react';
import toast, { Toaster } from 'react-hot-toast';
@ -680,34 +678,37 @@ const STATUS_MAP = [
'cancelled',
];
const STATUS_I18N_KEYS = {
pending: 'bookingStatus.pending',
ownerConfirmed: 'bookingStatus.ownerConfirmed',
depositPaid: 'bookingStatus.depositPaid',
depositConfirmed: 'bookingStatus.depositConfirmed',
completed: 'bookingStatus.completed',
cancelled: 'bookingStatus.cancelled',
};
const STATUS_UI = {
pending: {
label: 'قيد الانتظار',
color: 'bg-yellow-100 text-yellow-800',
icon: Clock,
},
ownerConfirmed: {
label: 'مؤكد',
color: 'bg-green-100 text-green-800',
icon: CheckCircle,
},
depositPaid: {
label: 'تم دفع السلفة',
color: 'bg-indigo-100 text-indigo-800',
icon: DollarSign,
},
depositConfirmed: {
label: 'مؤكد نهائياً',
color: 'bg-green-100 text-green-800',
icon: CheckCircle,
},
completed: {
label: 'منتهي',
color: 'bg-blue-100 text-blue-800',
icon: CheckCircle,
},
cancelled: {
label: 'ملغي',
color: 'bg-gray-100 text-gray-800',
icon: XCircle,
},
@ -715,7 +716,7 @@ const STATUS_UI = {
const getStatusKey = (code) => STATUS_MAP[Number(code)] || 'pending';
const sLabel = (code) => STATUS_UI[getStatusKey(code)]?.label ?? String(code);
const sLabel = (code, t) => t(STATUS_I18N_KEYS[getStatusKey(code)]) || String(code);
const sColor = (code) =>
STATUS_UI[getStatusKey(code)]?.color ?? 'bg-gray-100 text-gray-700';
@ -723,6 +724,7 @@ const sColor = (code) =>
const sIcon = (code) => STATUS_UI[getStatusKey(code)]?.icon ?? Clock;
function StatusBadge({ code }) {
const { t } = useTranslation();
const Icon = sIcon(code);
return (
@ -731,7 +733,7 @@ function StatusBadge({ code }) {
code
)}`}
>
<Icon className="w-3 h-3" /> {sLabel(code)}
<Icon className="w-3 h-3" /> {sLabel(code, t)}
</span>
);
}
@ -839,10 +841,10 @@ async function reportReservation(reservationId, message) {
const rid = Number(reservationId);
if (!Number.isInteger(rid)) {
throw new Error('رقم الحجز غير صالح');
throw new Error(t('invalidReservationId'));
}
if (!Number.isInteger(reporter)) {
throw new Error('تعذر تحديد المستخدم الحالي');
throw new Error(t('couldNotIdentifyUser'));
}
const token = getAuthToken();
@ -860,7 +862,7 @@ async function reportReservation(reservationId, message) {
});
if (!res.ok) {
let errorMessage = 'فشل إرسال البلاغ';
let errorMessage = t('reportFailed');
try {
const data = await res.json();
errorMessage = data?.message || data?.title || errorMessage;
@ -889,6 +891,7 @@ function OwnerCard({
actionLoadingId,
reportingId,
}) {
const { t } = useTranslation();
const p = r._prop;
const imgs = pImgs(p);
const img = imgs.length > 0 ? buildImageUrl(imgs[0]) : null;
@ -926,21 +929,21 @@ function OwnerCard({
<div className="text-lg font-bold text-amber-600">
{r.totalPrice?.toLocaleString() ?? '—'}
</div>
<div className="text-xs text-gray-500">السعر الإجمالي</div>
<div className="text-xs text-gray-500">{t('totalPrice')}</div>
</div>
</div>
{(pBeds(p) || pBaths(p)) && (
<div className="flex gap-3 mb-3 text-sm text-gray-600">
{pBeds(p) > 0 && <span>{pBeds(p)} غرف</span>}
{pBaths(p) > 0 && <span>{pBaths(p)} حمامات</span>}
{pBeds(p) > 0 && <span>{pBeds(p)} {t('reservationsBedrooms')}</span>}
{pBaths(p) > 0 && <span>{pBaths(p)} {t('reservationsBathrooms')}</span>}
</div>
)}
<div className="grid grid-cols-2 gap-3 mb-4 text-center">
<div className="bg-gray-50 p-2 rounded-lg">
<Calendar className="w-4 h-4 text-amber-500 mx-auto mb-1" />
<div className="text-xs text-gray-500">من</div>
<div className="text-xs text-gray-500">{t('from')}</div>
<div className="text-sm font-medium">
{r.startDate
? new Date(r.startDate).toLocaleDateString('ar')
@ -950,7 +953,7 @@ function OwnerCard({
<div className="bg-gray-50 p-2 rounded-lg">
<Calendar className="w-4 h-4 text-amber-500 mx-auto mb-1" />
<div className="text-xs text-gray-500">إلى</div>
<div className="text-xs text-gray-500">{t('to')}</div>
<div className="text-sm font-medium">
{r.endDate ? new Date(r.endDate).toLocaleDateString('ar') : '—'}
</div>
@ -968,7 +971,7 @@ function OwnerCard({
className="flex-1 bg-gray-100 text-gray-700 py-2 rounded-xl text-sm font-medium hover:bg-gray-200 transition-colors flex items-center justify-center gap-2"
>
<Eye className="w-4 h-4" />
التفاصيل
{t('details')}
</button>
{isPending && (
@ -984,7 +987,7 @@ function OwnerCard({
) : (
<CheckCircle className="w-4 h-4" />
)}
قبول
{t('accept')}
</button>
<button
@ -998,7 +1001,7 @@ function OwnerCard({
) : (
<XCircle className="w-4 h-4" />
)}
رفض
{t('reject')}
</button>
</>
)}
@ -1019,7 +1022,7 @@ function OwnerCard({
) : (
<Flag className="w-4 h-4" />
)}
{isReporting ? 'جاري الإبلاغ...' : 'إبلاغ'}
{isReporting ? t('reporting') : t('report')}
</button>
</div>
</motion.div>
@ -1027,6 +1030,7 @@ function OwnerCard({
}
function DetailsModal({ r, isOpen, onClose, onReport, reportingId }) {
const { t } = useTranslation();
if (!isOpen || !r) return null;
const p = r._prop;
@ -1049,7 +1053,7 @@ function DetailsModal({ r, isOpen, onClose, onReport, reportingId }) {
>
<div className="sticky top-0 bg-gradient-to-r from-amber-500 to-amber-600 p-6 text-white">
<div className="flex justify-between items-center">
<h2 className="text-xl font-bold">طلب حجز #{r.id}</h2>
<h2 className="text-xl font-bold">{t('reservationRequest', { id: r.id })}</h2>
<button
type="button"
@ -1066,11 +1070,11 @@ function DetailsModal({ r, isOpen, onClose, onReport, reportingId }) {
<div className="bg-gray-50 p-4 rounded-xl">
<h3 className="font-bold text-gray-900 mb-3 flex items-center gap-2">
<Home className="w-5 h-5 text-amber-500" />
معلومات العقار
{t('propertyInfo')}
</h3>
<p>
<span className="text-gray-500">العنوان:</span>{' '}
<span className="text-gray-500">{t('address')}</span>{' '}
{pAddr(p) || '—'}
</p>
@ -1078,13 +1082,13 @@ function DetailsModal({ r, isOpen, onClose, onReport, reportingId }) {
<div className="flex gap-3 mt-2">
{pBeds(p) > 0 && (
<span className="text-sm bg-white px-2 py-1 rounded-lg">
{pBeds(p)} غرف
{pBeds(p)} {t('reservationsBedrooms')}
</span>
)}
{pBaths(p) > 0 && (
<span className="text-sm bg-white px-2 py-1 rounded-lg">
{pBaths(p)} حمامات
{pBaths(p)} {t('reservationsBathrooms')}
</span>
)}
</div>
@ -1095,12 +1099,12 @@ function DetailsModal({ r, isOpen, onClose, onReport, reportingId }) {
<div className="bg-gray-50 p-4 rounded-xl">
<h3 className="font-bold text-gray-900 mb-3 flex items-center gap-2">
<Calendar className="w-5 h-5 text-amber-500" />
تفاصيل الحجز
{t('reservationDetails')}
</h3>
<div className="grid grid-cols-2 gap-4">
<div>
<p className="text-gray-500">تاريخ البداية</p>
<p className="text-gray-500">{t('startDate')}</p>
<p className="font-medium">
{r.startDate
? new Date(r.startDate).toLocaleDateString('ar')
@ -1109,19 +1113,19 @@ function DetailsModal({ r, isOpen, onClose, onReport, reportingId }) {
</div>
<div>
<p className="text-gray-500">تاريخ النهاية</p>
<p className="text-gray-500">{t('endDate')}</p>
<p className="font-medium">
{r.endDate ? new Date(r.endDate).toLocaleDateString('ar') : '—'}
</p>
</div>
<div>
<p className="text-gray-500">الحالة</p>
<p className="text-gray-500">{t('statusLabel')}</p>
<StatusBadge code={r.status} />
</div>
<div>
<p className="text-gray-500">تاريخ الإنشاء</p>
<p className="text-gray-500">{t('createdAt')}</p>
<p className="font-medium">
{r.createdAt
? new Date(r.createdAt).toLocaleDateString('ar')
@ -1134,11 +1138,11 @@ function DetailsModal({ r, isOpen, onClose, onReport, reportingId }) {
<div className="bg-amber-50 p-4 rounded-xl">
<h3 className="font-bold text-amber-700 mb-3 flex items-center gap-2">
<DollarSign className="w-5 h-5" />
المعلومات المالية
{t('financialInfo')}
</h3>
<div className="flex justify-between font-bold">
<span className="text-gray-900">الإجمالي</span>
<span className="text-gray-900">{t('total')}</span>
<span className="text-amber-600 text-lg">
{r.totalPrice?.toLocaleString() ?? '—'}
</span>
@ -1151,6 +1155,7 @@ function DetailsModal({ r, isOpen, onClose, onReport, reportingId }) {
}
function ReportDialog({ isOpen, reservation, onClose, onSubmit, submitting }) {
const { t } = useTranslation();
const [message, setMessage] = useState('');
useEffect(() => {
@ -1177,8 +1182,8 @@ function ReportDialog({ isOpen, reservation, onClose, onSubmit, submitting }) {
<div className="bg-gradient-to-r from-red-500 to-red-600 p-6 text-white">
<div className="flex items-center justify-between">
<div>
<h2 className="text-xl font-bold">الإبلاغ عن الحجز</h2>
<p className="text-red-100 text-sm mt-1">رقم الحجز: #{reservation.id}</p>
<h2 className="text-xl font-bold">{t('reportReservation')}</h2>
<p className="text-red-100 text-sm mt-1">{t('reservationId', { id: reservation.id })}</p>
</div>
<button onClick={onClose} className="rounded-full p-1 hover:bg-white/20">
<XCircle className="h-6 w-6" />
@ -1188,13 +1193,13 @@ function ReportDialog({ isOpen, reservation, onClose, onSubmit, submitting }) {
<div className="p-6">
<p className="text-gray-700 mb-4 leading-7">
اخبر فريق الدعم بما حدث التفاصيل الواضحة تساعدنا على مراجعة هذا الحجز بشكل اسرع
{t('reportDescription')}
</p>
<textarea
value={message}
onChange={(e) => setMessage(e.target.value)}
placeholder="اكتب تفاصيل البلاغ هنا..."
placeholder={t('reportPlaceholder')}
rows={5}
className="w-full resize-none rounded-xl border border-gray-300 p-3 text-sm focus:outline-none focus:ring-2 focus:ring-red-500"
/>
@ -1208,14 +1213,14 @@ function ReportDialog({ isOpen, reservation, onClose, onSubmit, submitting }) {
}`}
>
{submitting ? <Loader2 className="h-4 w-4 animate-spin" /> : <Flag className="h-4 w-4" />}
{submitting ? 'جاري الإرسال...' : 'إرسال البلاغ'}
{submitting ? t('submitting') : t('submitReport')}
</button>
<button
onClick={onClose}
disabled={submitting}
className="rounded-xl bg-gray-200 px-4 py-2.5 text-sm font-medium text-gray-700 hover:bg-gray-300 transition-colors disabled:cursor-not-allowed"
>
إلغاء
{t('cancel')}
</button>
</div>
</div>
@ -1225,6 +1230,7 @@ function ReportDialog({ isOpen, reservation, onClose, onSubmit, submitting }) {
}
export default function OwnerReservationRequestsPage() {
const { t, i18n } = useTranslation();
const router = useRouter();
const [reservations, setReservations] = useState([]);
@ -1270,7 +1276,7 @@ export default function OwnerReservationRequestsPage() {
setFiltered(enriched);
} catch (err) {
console.error(err);
toast.error('فشل تحميل طلبات الحجز');
toast.error(t('loadFailedReservations'));
setReservations([]);
setFiltered([]);
} finally {
@ -1321,18 +1327,18 @@ export default function OwnerReservationRequestsPage() {
throw new Error(errorText || `HTTP ${res.status}`);
}
toast.success('تم قبول الحجز بنجاح');
toast.success(t('acceptSuccess'));
await loadReservations();
} catch (err) {
console.error(err);
toast.error('فشل قبول الحجز');
toast.error(t('acceptFailed'));
} finally {
setActionLoadingId(null);
}
};
const handleReject = async (r) => {
if (!confirm('هل أنت متأكد من رفض هذا الحجز؟')) return;
if (!confirm(t('rejectConfirm'))) return;
try {
setActionLoadingId(r.id);
@ -1348,11 +1354,11 @@ export default function OwnerReservationRequestsPage() {
throw new Error(errorText || `HTTP ${res.status}`);
}
toast.success('تم رفض الحجز');
toast.success(t('rejectSuccess'));
await loadReservations();
} catch (err) {
console.error(err);
toast.error('فشل رفض الحجز');
toast.error(t('rejectFailed'));
} finally {
setActionLoadingId(null);
}
@ -1372,11 +1378,11 @@ export default function OwnerReservationRequestsPage() {
setReportingId(reportDialog.reservation.id);
try {
await reportReservation(reportDialog.reservation.id, message.trim() || null);
toast.success('تم إرسال البلاغ بنجاح');
toast.success(t('reportSuccess'));
closeReportDialog();
} catch (err) {
console.error(err);
toast.error(err?.message || 'فشل إرسال البلاغ');
toast.error(err?.message || t('reportFailed'));
} finally {
setReportingId(null);
}
@ -1405,7 +1411,7 @@ export default function OwnerReservationRequestsPage() {
}
return (
<div className="min-h-screen bg-gray-50 py-8" dir="rtl">
<div className="min-h-screen bg-gray-50 py-8" dir={i18n.language === 'ar' ? 'rtl' : 'ltr'}>
<Toaster position="top-center" reverseOrder={false} />
<DetailsModal
@ -1429,30 +1435,11 @@ export default function OwnerReservationRequestsPage() {
animate={{ opacity: 1, y: 0 }}
className="mb-8"
>
<button
type="button"
onClick={() => router.back()}
className="flex items-center gap-2 text-gray-600 hover:text-amber-600 mb-4"
>
<ArrowLeft className="w-5 h-5" />
الرجوع
</button>
<div className="flex items-center justify-between mb-2">
<div>
<h1 className="text-3xl font-bold text-gray-900">
طلبات الحجز
</h1>
<p className="text-gray-600">لديك {reservations.length} طلب</p>
</div>
<button
type="button"
onClick={loadReservations}
className="p-2 bg-white shadow rounded-xl hover:shadow-md transition-all"
>
<RefreshCw className="w-5 h-5 text-gray-600" />
</button>
<div className="mb-4">
<h1 className="text-3xl font-bold text-gray-900">
{t('reservationsTitle')}
</h1>
<p className="text-gray-600">{t('reservationsCount', { count: reservations.length })}</p>
</div>
</motion.div>
@ -1471,7 +1458,7 @@ export default function OwnerReservationRequestsPage() {
>
<div className="text-2xl font-bold text-amber-600">{c}</div>
<div className="text-sm text-gray-600">
{s === 'all' ? 'الكل' : STATUS_UI[s]?.label || s}
{s === 'all' ? t('filterAll') : t(STATUS_I18N_KEYS[s]) || s}
</div>
</motion.div>
))}
@ -1482,7 +1469,7 @@ export default function OwnerReservationRequestsPage() {
<input
type="text"
placeholder="ابحث بعنوان العقار أو رقم الحجز..."
placeholder={t('searchPlaceholderReservations')}
value={searchTerm}
onChange={(e) => setSearchTerm(e.target.value)}
className="w-full pl-10 pr-4 py-3 border border-gray-300 rounded-xl focus:outline-none focus:ring-2 focus:ring-amber-500"
@ -1493,10 +1480,10 @@ export default function OwnerReservationRequestsPage() {
<div className="bg-white rounded-2xl p-12 text-center border-2 border-dashed border-gray-300">
<Calendar className="w-12 h-12 text-amber-600 mx-auto mb-4" />
<h3 className="text-xl font-bold text-gray-900 mb-2">
لا توجد طلبات
{t('noReservations')}
</h3>
<p className="text-gray-600">
لم يتم استلام أي طلبات حجز حتى الآن
{t('noReservationsHint')}
</p>
</div>
) : (
@ -1518,4 +1505,4 @@ export default function OwnerReservationRequestsPage() {
</div>
</div>
);
}
}