diff --git a/app/owner/reservations/page.js b/app/owner/reservations/page.js
index b8a4697..43fd68a 100644
--- a/app/owner/reservations/page.js
+++ b/app/owner/reservations/page.js
@@ -1,104 +1,521 @@
+// 'use client';
+
+// import { useState, useEffect, useCallback } from 'react';
+// import { motion } from 'framer-motion';
+// import { useRouter } from 'next/navigation';
+// import {
+// Calendar, Clock, CheckCircle, XCircle, Eye, Loader2, Search,
+// MapPin, DollarSign, Home, ArrowLeft, User, RefreshCw, Mail, Phone,
+// } from 'lucide-react';
+// import toast, { Toaster } from 'react-hot-toast';
+// import AuthService from '../../services/AuthService';
+// import { getRentProperty } from '../../utils/api';
+
+// const API_BASE = process.env.NEXT_PUBLIC_API_URL || 'https://45.93.137.91.nip.io/api';
+
+// const STATUS_MAP = ['pending','ownerConfirmed','depositPaid','depositConfirmed','completed','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 },
+// };
+
+// const sLabel = c => STATUS_UI[STATUS_MAP[c]]?.label ?? String(c);
+// const sColor = c => STATUS_UI[STATUS_MAP[c]]?.color ?? 'bg-gray-100 text-gray-700';
+// const sIcon = c => STATUS_UI[STATUS_MAP[c]]?.icon ?? Clock;
+
+// function StatusBadge({ code }) {
+// const Icon = sIcon(code);
+// return {sLabel(code)} ;
+// }
+
+// async function enrich(r) {
+// if (!r.propertyId) return r;
+// try {
+// const prop = await getRentProperty(r.propertyId);
+// r._prop = prop?.propertyInformation ?? prop ?? null;
+// } catch { /* skip */ }
+// return r;
+// }
+
+// const pAddr = p => p?.address ?? '';
+// const pImgs = p => Array.isArray(p?.images) ? p.images : [];
+// const pBeds = p => p?.numberOfBedRooms ?? 0;
+// const pBaths = p => p?.numberOfBathRooms ?? 0;
+// const API = (token, method, path, body) => fetch(`${API_BASE}${path}`, {
+// method: method || 'GET',
+// headers: { 'Content-Type': 'application/json', ...(token && { Authorization: `Bearer ${token}` }) },
+// ...(body && { body: JSON.stringify(body) }),
+// });
+
+// function OwnerCard({ r, onViewDetails, onConfirm, onReject }) {
+// const p = r._prop;
+// const imgs = pImgs(p);
+// const img = imgs.length > 0 ? `${API_BASE}${imgs[0]}` : null;
+// const addr = pAddr(p);
+// const isPending = r.status === 0; // Pending
+
+// return (
+//
+//
+// {img &&
}
+//
+//
+//
+// {addr && {addr}
}
+//
+//
+//
{r.totalPrice?.toLocaleString()??'—'}
+//
السعر الإجمالي
+//
+//
+// {(pBeds(p)||pBaths(p)) &&
{pBeds(p)>0&&{pBeds(p)} غرف }{pBaths(p)>0&&{pBaths(p)} حمامات }
}
+//
+//
+//
من
+//
{new Date(r.startDate).toLocaleDateString('ar')}
+//
+//
+//
إلى
+//
{new Date(r.endDate).toLocaleDateString('ar')}
+//
+//
+//
+// onViewDetails(r)}
+// 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">
+// التفاصيل
+//
+// {isPending && <>
+// onConfirm(r)}
+// className="flex-1 bg-green-500 text-white py-2 rounded-xl text-sm font-medium hover:bg-green-600 transition-colors flex items-center justify-center gap-2">
+// قبول
+//
+// onReject(r)}
+// className="flex-1 bg-red-500 text-white py-2 rounded-xl text-sm font-medium hover:bg-red-600 transition-colors flex items-center justify-center gap-2">
+// رفض
+//
+// >}
+//
+//
+//
+// );
+// }
+
+// function DetailsModal({ r, isOpen, onClose }) {
+// if (!isOpen || !r) return null;
+// const p = r._prop;
+// return (
+//
+// e.stopPropagation()}>
+//
+//
+//
طلب حجز #{r.id}
+//
+//
+//
+//
+// {p &&
+//
معلومات العقار
+//
العنوان: {pAddr(p)||'—'}
+// {(pBeds(p)||pBaths(p)) &&
+// {pBeds(p)>0&&{pBeds(p)} غرف }
+// {pBaths(p)>0&&{pBaths(p)} حمامات }
+//
}
+//
}
+//
+//
تفاصيل الحجز
+//
+//
تاريخ البداية
{new Date(r.startDate).toLocaleDateString('ar')}
+//
تاريخ النهاية
{new Date(r.endDate).toLocaleDateString('ar')}
+//
+//
تاريخ الإنشاء
{new Date(r.createdAt).toLocaleDateString('ar')}
+//
+//
+//
+//
المعلومات المالية
+//
الإجمالي {r.totalPrice?.toLocaleString()??'—'}
+//
+//
+//
+//
+// );
+// }
+
+// export default function OwnerReservationRequestsPage() {
+// const router = useRouter();
+// const [reservations, setReservations] = useState([]);
+// const [filtered, setFiltered] = useState([]);
+// const [loading, setLoading] = useState(true);
+// const [selected, setSelected] = useState(null);
+// const [filterStatus, setFilterStatus] = useState('all');
+// const [searchTerm, setSearchTerm] = useState('');
+
+// useEffect(() => {
+// if (!AuthService.getUser() || !AuthService.isOwner()) { router.push('/auth/choose-role'); return; }
+// loadReservations();
+// }, [router]);
+
+// const loadReservations = useCallback(async () => {
+// try {
+// const token = AuthService.getToken();
+// const res = await fetch(`${API_BASE}/Reservations/GetOwnerResevationRequests`, {
+// headers: { Authorization: `Bearer ${token}` },
+// });
+// if (!res.ok) throw new Error(`HTTP ${res.status}`);
+// const json = await res.json();
+// let list = json.data || json || [];
+// if (!Array.isArray(list)) list = [];
+// const enriched = await Promise.all(list.map(enrich));
+// setReservations(enriched);
+// setFiltered(enriched);
+// } catch (err) {
+// console.error(err);
+// toast.error('فشل تحميل طلبات الحجز');
+// }
+// setLoading(false);
+// }, []);
+
+// useEffect(() => {
+// let r = reservations;
+// if (filterStatus !== 'all') r = r.filter(x => STATUS_MAP[x.status] === filterStatus);
+// if (searchTerm) {
+// const q = searchTerm.toLowerCase();
+// r = r.filter(x => pAddr(x._prop).toLowerCase().includes(q) || String(x.id).includes(q));
+// }
+// setFiltered(r);
+// }, [reservations, filterStatus, searchTerm]);
+
+// const handleConfirm = async (r) => {
+// try {
+// const res = await API(AuthService.getToken(), 'PUT', `/Reservations/owner-confirm/${r.id}`);
+// if (!res.ok) throw new Error(`HTTP ${res.status}`);
+// toast.success('تم قبول الحجز بنجاح');
+// await loadReservations();
+// } catch (err) { console.error(err); toast.error('فشل قبول الحجز'); }
+// };
+
+// const handleReject = async (r) => {
+// if (!confirm('هل أنت متأكد من رفض هذا الحجز؟')) return;
+// try {
+// const res = await API(AuthService.getToken(), 'PUT', `/Reservations/reject/${r.id}`);
+// if (!res.ok) throw new Error(`HTTP ${res.status}`);
+// toast.success('تم رفض الحجز');
+// await loadReservations();
+// } catch (err) { console.error(err); toast.error('فشل رفض الحجز'); }
+// };
+
+// const allStatuses = [...new Set(reservations.map(r => STATUS_MAP[r.status]))];
+// const counts = { all: reservations.length, ...Object.fromEntries(allStatuses.map(s => [s, reservations.filter(r => STATUS_MAP[r.status] === s).length])) };
+
+// if (loading) return
;
+
+// return (
+//
+//
+//
setSelected(null)} />
+//
+//
+// router.back()} className="flex items-center gap-2 text-gray-600 hover:text-amber-600 mb-4"> الرجوع
+//
+//
+//
طلبات الحجز
+//
لديك {reservations.length} طلب
+//
+//
+//
+//
+//
+// {Object.entries(counts).map(([s, c]) => (
+//
setFilterStatus(s)}>
+// {c}
+// {s==='all'?'الكل':(STATUS_UI[s]?.label||s)}
+//
+// ))}
+//
+//
+//
+// 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"/>
+//
+// {filtered.length === 0 ? (
+//
+//
+//
لا توجد طلبات
+//
لم يتم استلام أي طلبات حجز حتى الآن
+//
+// ) : (
+//
+// {filtered.map(r => )}
+//
+// )}
+//
+//
+// );
+// }
+
+
'use client';
import { useState, useEffect, useCallback } from 'react';
import { motion } from 'framer-motion';
import { useRouter } from 'next/navigation';
import {
- Calendar, Clock, CheckCircle, XCircle, Eye, Loader2, Search,
- MapPin, DollarSign, Home, ArrowLeft, User, RefreshCw, Mail, Phone,
+ Calendar,
+ Clock,
+ CheckCircle,
+ XCircle,
+ Eye,
+ Loader2,
+ Search,
+ MapPin,
+ DollarSign,
+ Home,
+ ArrowLeft,
+ RefreshCw,
} from 'lucide-react';
import toast, { Toaster } from 'react-hot-toast';
import AuthService from '../../services/AuthService';
import { getRentProperty } from '../../utils/api';
-const API_BASE = process.env.NEXT_PUBLIC_API_URL || 'https://45.93.137.91.nip.io/api';
+const API_BASE =
+ process.env.NEXT_PUBLIC_API_URL || 'https://45.93.137.91.nip.io/api';
+
+const STATUS_MAP = [
+ 'pending',
+ 'ownerConfirmed',
+ 'depositPaid',
+ 'depositConfirmed',
+ 'completed',
+ 'cancelled',
+];
-const STATUS_MAP = ['pending','ownerConfirmed','depositPaid','depositConfirmed','completed','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 },
+ 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,
+ },
};
-const sLabel = c => STATUS_UI[STATUS_MAP[c]]?.label ?? String(c);
-const sColor = c => STATUS_UI[STATUS_MAP[c]]?.color ?? 'bg-gray-100 text-gray-700';
-const sIcon = c => STATUS_UI[STATUS_MAP[c]]?.icon ?? Clock;
+const getStatusKey = (code) => STATUS_MAP[Number(code)] || 'pending';
+
+const sLabel = (code) => STATUS_UI[getStatusKey(code)]?.label ?? String(code);
+
+const sColor = (code) =>
+ STATUS_UI[getStatusKey(code)]?.color ?? 'bg-gray-100 text-gray-700';
+
+const sIcon = (code) => STATUS_UI[getStatusKey(code)]?.icon ?? Clock;
function StatusBadge({ code }) {
const Icon = sIcon(code);
- return {sLabel(code)} ;
+
+ return (
+
+ {sLabel(code)}
+
+ );
}
async function enrich(r) {
- if (!r.propertyId) return r;
+ if (!r?.propertyId) return r;
+
try {
const prop = await getRentProperty(r.propertyId);
- r._prop = prop?.propertyInformation ?? prop ?? null;
- } catch { /* skip */ }
- return r;
+
+ return {
+ ...r,
+ _prop: prop?.propertyInformation ?? prop ?? null,
+ };
+ } catch {
+ return {
+ ...r,
+ _prop: null,
+ };
+ }
}
-const pAddr = p => p?.address ?? '';
-const pImgs = p => Array.isArray(p?.images) ? p.images : [];
-const pBeds = p => p?.numberOfBedRooms ?? 0;
-const pBaths = p => p?.numberOfBathRooms ?? 0;
-const API = (token, method, path, body) => fetch(`${API_BASE}${path}`, {
- method: method || 'GET',
- headers: { 'Content-Type': 'application/json', ...(token && { Authorization: `Bearer ${token}` }) },
- ...(body && { body: JSON.stringify(body) }),
-});
+const pAddr = (p) => p?.address ?? '';
+const pImgs = (p) => (Array.isArray(p?.images) ? p.images : []);
+const pBeds = (p) => p?.numberOfBedRooms ?? 0;
+const pBaths = (p) => p?.numberOfBathRooms ?? 0;
-function OwnerCard({ r, onViewDetails, onConfirm, onReject }) {
+const API = (token, method, path, body) => {
+ return fetch(`${API_BASE}${path}`, {
+ method: method || 'GET',
+ headers: {
+ 'Content-Type': 'application/json',
+ ...(token && { Authorization: `Bearer ${token}` }),
+ },
+ ...(body && { body: JSON.stringify(body) }),
+ });
+};
+
+function buildImageUrl(path) {
+ if (!path) return null;
+
+ if (path.startsWith('http://') || path.startsWith('https://')) {
+ return path;
+ }
+
+ return `${API_BASE}${path}`;
+}
+
+function OwnerCard({
+ r,
+ onViewDetails,
+ onConfirm,
+ onReject,
+ actionLoadingId,
+}) {
const p = r._prop;
const imgs = pImgs(p);
- const img = imgs.length > 0 ? `${API_BASE}${imgs[0]}` : null;
+ const img = imgs.length > 0 ? buildImageUrl(imgs[0]) : null;
const addr = pAddr(p);
- const isPending = r.status === 0; // Pending
+ const isPending = Number(r.status) === 0;
+ const isActionLoading = actionLoadingId === r.id;
return (
-
+
- {img &&
}
+ {img && (
+
+
+
+ )}
+
-
- {addr && {addr}
}
+
+
+ {addr && (
+
+
+ {addr}
+
+ )}
+
-
{r.totalPrice?.toLocaleString()??'—'}
+
+ {r.totalPrice?.toLocaleString() ?? '—'}
+
السعر الإجمالي
- {(pBeds(p)||pBaths(p)) &&
{pBeds(p)>0&&{pBeds(p)} غرف }{pBaths(p)>0&&{pBaths(p)} حمامات }
}
+
+ {(pBeds(p) || pBaths(p)) && (
+
+ {pBeds(p) > 0 && {pBeds(p)} غرف }
+ {pBaths(p) > 0 && {pBaths(p)} حمامات }
+
+ )}
+
-
من
-
{new Date(r.startDate).toLocaleDateString('ar')}
+
+
من
+
+ {r.startDate
+ ? new Date(r.startDate).toLocaleDateString('ar')
+ : '—'}
+
+
-
إلى
-
{new Date(r.endDate).toLocaleDateString('ar')}
+
+
إلى
+
+ {r.endDate ? new Date(r.endDate).toLocaleDateString('ar') : '—'}
+
-
-
onViewDetails(r)}
- 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">
- التفاصيل
+
+
+ onViewDetails(r)}
+ 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"
+ >
+
+ التفاصيل
- {isPending && <>
- onConfirm(r)}
- className="flex-1 bg-green-500 text-white py-2 rounded-xl text-sm font-medium hover:bg-green-600 transition-colors flex items-center justify-center gap-2">
- قبول
-
- onReject(r)}
- className="flex-1 bg-red-500 text-white py-2 rounded-xl text-sm font-medium hover:bg-red-600 transition-colors flex items-center justify-center gap-2">
- رفض
-
- >}
+
+ {isPending && (
+ <>
+ onConfirm(r)}
+ className="flex-1 bg-green-500 text-white py-2 rounded-xl text-sm font-medium hover:bg-green-600 transition-colors flex items-center justify-center gap-2 disabled:opacity-60 disabled:cursor-not-allowed"
+ >
+ {isActionLoading ? (
+
+ ) : (
+
+ )}
+ قبول
+
+
+ onReject(r)}
+ className="flex-1 bg-red-500 text-white py-2 rounded-xl text-sm font-medium hover:bg-red-600 transition-colors flex items-center justify-center gap-2 disabled:opacity-60 disabled:cursor-not-allowed"
+ >
+ {isActionLoading ? (
+
+ ) : (
+
+ )}
+ رفض
+
+ >
+ )}
@@ -107,39 +524,120 @@ function OwnerCard({ r, onViewDetails, onConfirm, onReject }) {
function DetailsModal({ r, isOpen, onClose }) {
if (!isOpen || !r) return null;
+
const p = r._prop;
+
return (
-
- e.stopPropagation()}>
+
+ e.stopPropagation()}
+ >
طلب حجز #{r.id}
-
+
+
+
+
+
- {p &&
-
معلومات العقار
-
العنوان: {pAddr(p)||'—'}
- {(pBeds(p)||pBaths(p)) &&
- {pBeds(p)>0&&{pBeds(p)} غرف }
- {pBaths(p)>0&&{pBaths(p)} حمامات }
-
}
-
}
+ {p && (
+
+
+
+ معلومات العقار
+
+
+
+ العنوان: {' '}
+ {pAddr(p) || '—'}
+
+
+ {(pBeds(p) || pBaths(p)) && (
+
+ {pBeds(p) > 0 && (
+
+ {pBeds(p)} غرف
+
+ )}
+
+ {pBaths(p) > 0 && (
+
+ {pBaths(p)} حمامات
+
+ )}
+
+ )}
+
+ )}
+
-
تفاصيل الحجز
+
+
+ تفاصيل الحجز
+
+
-
تاريخ البداية
{new Date(r.startDate).toLocaleDateString('ar')}
-
تاريخ النهاية
{new Date(r.endDate).toLocaleDateString('ar')}
-
-
تاريخ الإنشاء
{new Date(r.createdAt).toLocaleDateString('ar')}
+
+
تاريخ البداية
+
+ {r.startDate
+ ? new Date(r.startDate).toLocaleDateString('ar')
+ : '—'}
+
+
+
+
+
تاريخ النهاية
+
+ {r.endDate ? new Date(r.endDate).toLocaleDateString('ar') : '—'}
+
+
+
+
+
+
+
تاريخ الإنشاء
+
+ {r.createdAt
+ ? new Date(r.createdAt).toLocaleDateString('ar')
+ : '—'}
+
+
+
-
المعلومات المالية
-
الإجمالي {r.totalPrice?.toLocaleString()??'—'}
+
+
+ المعلومات المالية
+
+
+
+ الإجمالي
+
+ {r.totalPrice?.toLocaleString() ?? '—'}
+
+
@@ -149,114 +647,259 @@ function DetailsModal({ r, isOpen, onClose }) {
export default function OwnerReservationRequestsPage() {
const router = useRouter();
+
const [reservations, setReservations] = useState([]);
const [filtered, setFiltered] = useState([]);
const [loading, setLoading] = useState(true);
const [selected, setSelected] = useState(null);
const [filterStatus, setFilterStatus] = useState('all');
const [searchTerm, setSearchTerm] = useState('');
-
- useEffect(() => {
- if (!AuthService.getUser() || !AuthService.isOwner()) { router.push('/auth/choose-role'); return; }
- loadReservations();
- }, [router]);
+ const [actionLoadingId, setActionLoadingId] = useState(null);
const loadReservations = useCallback(async () => {
try {
+ setLoading(true);
+
const token = AuthService.getToken();
- const res = await fetch(`${API_BASE}/Reservations/GetOwnerResevationRequests`, {
- headers: { Authorization: `Bearer ${token}` },
- });
- if (!res.ok) throw new Error(`HTTP ${res.status}`);
+
+ const res = await fetch(
+ `${API_BASE}/Reservations/GetOwnerResevationRequests`,
+ {
+ method: 'GET',
+ headers: {
+ Authorization: `Bearer ${token}`,
+ 'Content-Type': 'application/json',
+ },
+ }
+ );
+
+ if (!res.ok) {
+ const errorText = await res.text();
+ throw new Error(errorText || `HTTP ${res.status}`);
+ }
+
const json = await res.json();
+
let list = json.data || json || [];
if (!Array.isArray(list)) list = [];
+
const enriched = await Promise.all(list.map(enrich));
+
setReservations(enriched);
setFiltered(enriched);
} catch (err) {
console.error(err);
toast.error('فشل تحميل طلبات الحجز');
+ setReservations([]);
+ setFiltered([]);
+ } finally {
+ setLoading(false);
}
- setLoading(false);
}, []);
useEffect(() => {
- let r = reservations;
- if (filterStatus !== 'all') r = r.filter(x => STATUS_MAP[x.status] === filterStatus);
- if (searchTerm) {
- const q = searchTerm.toLowerCase();
- r = r.filter(x => pAddr(x._prop).toLowerCase().includes(q) || String(x.id).includes(q));
+ if (!AuthService.getUser() || !AuthService.isOwner()) {
+ router.push('/auth/choose-role');
+ return;
}
- setFiltered(r);
+
+ loadReservations();
+ }, [router, loadReservations]);
+
+ useEffect(() => {
+ let result = reservations;
+
+ if (filterStatus !== 'all') {
+ result = result.filter((x) => getStatusKey(x.status) === filterStatus);
+ }
+
+ if (searchTerm.trim()) {
+ const q = searchTerm.trim().toLowerCase();
+
+ result = result.filter(
+ (x) =>
+ pAddr(x._prop).toLowerCase().includes(q) || String(x.id).includes(q)
+ );
+ }
+
+ setFiltered(result);
}, [reservations, filterStatus, searchTerm]);
const handleConfirm = async (r) => {
try {
- const res = await API(AuthService.getToken(), 'PUT', `/Reservations/owner-confirm/${r.id}`);
- if (!res.ok) throw new Error(`HTTP ${res.status}`);
+ setActionLoadingId(r.id);
+
+ const res = await API(
+ AuthService.getToken(),
+ 'PUT',
+ `/Reservations/OwnerConfirmReservation/owner-confirm/${r.id}`
+ );
+
+ if (!res.ok) {
+ const errorText = await res.text();
+ throw new Error(errorText || `HTTP ${res.status}`);
+ }
+
toast.success('تم قبول الحجز بنجاح');
await loadReservations();
- } catch (err) { console.error(err); toast.error('فشل قبول الحجز'); }
+ } catch (err) {
+ console.error(err);
+ toast.error('فشل قبول الحجز');
+ } finally {
+ setActionLoadingId(null);
+ }
};
const handleReject = async (r) => {
if (!confirm('هل أنت متأكد من رفض هذا الحجز؟')) return;
+
try {
- const res = await API(AuthService.getToken(), 'PUT', `/Reservations/reject/${r.id}`);
- if (!res.ok) throw new Error(`HTTP ${res.status}`);
+ setActionLoadingId(r.id);
+
+ const res = await API(
+ AuthService.getToken(),
+ 'POST',
+ `/Reservations/ChangeReservationStatus?id=${r.id}&newStatus=5`
+ );
+
+ if (!res.ok) {
+ const errorText = await res.text();
+ throw new Error(errorText || `HTTP ${res.status}`);
+ }
+
toast.success('تم رفض الحجز');
await loadReservations();
- } catch (err) { console.error(err); toast.error('فشل رفض الحجز'); }
+ } catch (err) {
+ console.error(err);
+ toast.error('فشل رفض الحجز');
+ } finally {
+ setActionLoadingId(null);
+ }
};
- const allStatuses = [...new Set(reservations.map(r => STATUS_MAP[r.status]))];
- const counts = { all: reservations.length, ...Object.fromEntries(allStatuses.map(s => [s, reservations.filter(r => STATUS_MAP[r.status] === s).length])) };
+ const allStatuses = [
+ ...new Set(reservations.map((r) => getStatusKey(r.status)).filter(Boolean)),
+ ];
- if (loading) return
;
+ const counts = {
+ all: reservations.length,
+ ...Object.fromEntries(
+ allStatuses.map((s) => [
+ s,
+ reservations.filter((r) => getStatusKey(r.status) === s).length,
+ ])
+ ),
+ };
+
+ if (loading) {
+ return (
+
+
+
+ );
+ }
return (
-
setSelected(null)} />
+
+ setSelected(null)}
+ />
+
-
- router.back()} className="flex items-center gap-2 text-gray-600 hover:text-amber-600 mb-4"> الرجوع
+
+ router.back()}
+ className="flex items-center gap-2 text-gray-600 hover:text-amber-600 mb-4"
+ >
+
+ الرجوع
+
+
-
طلبات الحجز
+
+ طلبات الحجز
+
لديك {reservations.length} طلب
-
+
+
+
+
+
{Object.entries(counts).map(([s, c]) => (
-
setFilterStatus(s)}>
+ setFilterStatus(s)}
+ >
{c}
- {s==='all'?'الكل':(STATUS_UI[s]?.label||s)}
+
+ {s === 'all' ? 'الكل' : STATUS_UI[s]?.label || s}
+
))}
+
-
- 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"/>
+
+
+ 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"
+ />
+
{filtered.length === 0 ? (
-
-
لا توجد طلبات
-
لم يتم استلام أي طلبات حجز حتى الآن
+
+
+ لا توجد طلبات
+
+
+ لم يتم استلام أي طلبات حجز حتى الآن
+
) : (
- {filtered.map(r => )}
+ {filtered.map((r) => (
+
+ ))}
)}
);
-}
+}
\ No newline at end of file
diff --git a/app/reservations/page.js b/app/reservations/page.js
index 29c1074..5c8eb52 100644
--- a/app/reservations/page.js
+++ b/app/reservations/page.js
@@ -223,4 +223,4 @@ export default function UserReservationsPage() {
);
-}
+}
\ No newline at end of file