diff --git a/client/public/add-category.html b/client/public/add-category.html
new file mode 100644
index 0000000..7c005c3
--- /dev/null
+++ b/client/public/add-category.html
@@ -0,0 +1,60 @@
+
+
+
+
+ Add Room Category
+
+
+ Add Room Category (Admin)
+ Make sure you are logged in to /admin/login first.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/client/public/uploads/1-1768295870059-239826802.jpg b/client/public/uploads/1-1768295870059-239826802.jpg
new file mode 100644
index 0000000..5d25430
Binary files /dev/null and b/client/public/uploads/1-1768295870059-239826802.jpg differ
diff --git a/client/public/uploads/2-1768295870063-802829438.jpg b/client/public/uploads/2-1768295870063-802829438.jpg
new file mode 100644
index 0000000..7f8a469
Binary files /dev/null and b/client/public/uploads/2-1768295870063-802829438.jpg differ
diff --git a/client/public/uploads/3-1768295870064-579147982.webp b/client/public/uploads/3-1768295870064-579147982.webp
new file mode 100644
index 0000000..ebcea41
Binary files /dev/null and b/client/public/uploads/3-1768295870064-579147982.webp differ
diff --git a/client/public/uploads/4-1768295870068-442995448.jpg b/client/public/uploads/4-1768295870068-442995448.jpg
new file mode 100644
index 0000000..1b339db
Binary files /dev/null and b/client/public/uploads/4-1768295870068-442995448.jpg differ
diff --git a/client/public/uploads/5-1768295870072-844168095.png b/client/public/uploads/5-1768295870072-844168095.png
new file mode 100644
index 0000000..782ac88
Binary files /dev/null and b/client/public/uploads/5-1768295870072-844168095.png differ
diff --git a/client/public/uploads/888-1768295870105-385689950.jpg b/client/public/uploads/888-1768295870105-385689950.jpg
new file mode 100644
index 0000000..f3fa8ce
Binary files /dev/null and b/client/public/uploads/888-1768295870105-385689950.jpg differ
diff --git a/client/public/uploads/ai-accounting-1768295870109-673067819.jpg b/client/public/uploads/ai-accounting-1768295870109-673067819.jpg
new file mode 100644
index 0000000..0eac489
Binary files /dev/null and b/client/public/uploads/ai-accounting-1768295870109-673067819.jpg differ
diff --git a/client/public/uploads/optimized-3-1768295870064-579147982.webp b/client/public/uploads/optimized-3-1768295870064-579147982.webp
new file mode 100644
index 0000000..7839da2
Binary files /dev/null and b/client/public/uploads/optimized-3-1768295870064-579147982.webp differ
diff --git a/client/src/components/admin/AdminLayout.js b/client/src/components/admin/AdminLayout.js
index c8619cb..79aa9c8 100644
--- a/client/src/components/admin/AdminLayout.js
+++ b/client/src/components/admin/AdminLayout.js
@@ -49,6 +49,10 @@ const menuItems = [
{ title: 'Settings', path: '/admin/settings', icon: },
];
+// ✅ hide from sidebar only (keep code/routes for future)
+const hiddenMenuTitles = new Set(['Guests', 'Blog']);
+const adminMenuItems = menuItems.filter(item => !hiddenMenuTitles.has(item.title));
+
const AdminLayout = () => {
const theme = useTheme();
const isMobile = useMediaQuery(theme.breakpoints.down('md'));
@@ -102,7 +106,7 @@ const AdminLayout = () => {
- {menuItems.map((item) => (
+ {adminMenuItems.map((item) => (
{
};
export default AdminLayout;
-
diff --git a/client/src/pages/About.js b/client/src/pages/About.js
index 28c688e..bca8458 100644
--- a/client/src/pages/About.js
+++ b/client/src/pages/About.js
@@ -20,7 +20,25 @@ import {
import { useTranslation } from 'react-i18next';
import { motion } from 'framer-motion';
import { Helmet } from 'react-helmet-async';
-import staticData from '../utils/staticData';
+import api from '../utils/api'; // ✅ بدل staticData
+
+const API_BASE = process.env.REACT_APP_API_URL || 'http://localhost:5080';
+const toMediaUrl = (url) => {
+ if (!url) return url;
+ if (/^https?:\/\//i.test(url)) {
+ if (url.includes('localhost:3060/uploads/')) return url.replace('http://localhost:3060', API_BASE);
+ return url;
+ }
+ if (url.startsWith('/uploads/')) return `${API_BASE}${url}`;
+ return url; // /images/... stays on client
+};
+
+const unwrapContent = (res) => {
+ return res?.data?.data?.content || res?.data?.data || res?.data || null;
+};
+
+// ✅ remove HTML tags مثل ..
+const stripHtml = (html = '') => String(html).replace(/<[^>]*>/g, '').trim();
const About = () => {
const { t, i18n } = useTranslation();
@@ -29,20 +47,27 @@ const About = () => {
const [loading, setLoading] = useState(true);
const currentLanguage = i18n.language;
- // Fetch about page content from static data
+ // ✅ Fetch about page content from API بدل static data
useEffect(() => {
+ let mounted = true;
+
const fetchContent = async () => {
try {
- const aboutContent = await staticData.getAboutContent();
- setContent(aboutContent);
+ const res = await api.get('/api/content/about');
+ const aboutContent = unwrapContent(res);
+ if (mounted) setContent(aboutContent);
} catch (error) {
console.error('Error loading about page content:', error);
} finally {
- setLoading(false);
+ if (mounted) setLoading(false);
}
};
fetchContent();
+
+ return () => {
+ mounted = false;
+ };
}, []);
const values = [
@@ -68,7 +93,6 @@ const About = () => {
}
];
-
const milestones = [
{
year: '1985',
@@ -111,25 +135,40 @@ const About = () => {
);
}
- // Use translations for non-English languages, static data for English
+ // Use translations for non-English languages, API content for English
const useTranslations = currentLanguage !== 'en';
-
- // Fallback content - prioritize translations for non-English
- const heroTitle = useTranslations ? t('about.heroTitle') : (content?.hero?.title || t('about.heroTitle'));
- const heroSubtitle = useTranslations ? t('about.heroSubtitle') : (content?.hero?.subtitle || t('about.heroSubtitle'));
- const heroDescription = useTranslations ? '' : (content?.hero?.description || '');
- const heroImage = content?.hero?.backgroundImage || '/images/about-hero.jpg';
+
+ // ✅ stripHtml only for content coming from dashboard (English)
+ const heroTitle = useTranslations ? t('about.heroTitle') : stripHtml(content?.hero?.title || t('about.heroTitle'));
+ const heroSubtitle = useTranslations ? t('about.heroSubtitle') : stripHtml(content?.hero?.subtitle || t('about.heroSubtitle'));
+ const heroDescription = useTranslations ? '' : stripHtml(content?.hero?.description || '');
+ const heroImage = toMediaUrl(content?.hero?.backgroundImage) || '/images/about-hero.jpg';
// Get sections from content - use translations for non-English
const heritageSectionStatic = content?.sections?.find(s => s.sectionId === 'heritage') || {};
+
const heritageSection = useTranslations ? {
title: t('about.heritageTitle'),
content: t('about.heritageContent'),
- image: heritageSectionStatic.image || '/images/about.jpg'
- } : heritageSectionStatic;
+ image: toMediaUrl(heritageSectionStatic.image) || '/images/about.jpg'
+ } : {
+ ...heritageSectionStatic,
+ title: stripHtml(heritageSectionStatic.title || ''),
+ subtitle: stripHtml(heritageSectionStatic.subtitle || ''),
+ content: stripHtml(heritageSectionStatic.content || ''),
+ image: toMediaUrl(heritageSectionStatic.image) || '/images/about.jpg'
+ };
+
const missionSection = content?.sections?.find(s => s.sectionId === 'mission') || {};
const visionSection = content?.sections?.find(s => s.sectionId === 'vision') || {};
- const valuesSection = content?.sections?.find(s => s.sectionId === 'values') || {};
+ const valuesSectionStatic = content?.sections?.find(s => s.sectionId === 'values') || {};
+
+ const valuesSection = useTranslations ? valuesSectionStatic : {
+ ...valuesSectionStatic,
+ title: stripHtml(valuesSectionStatic.title || ''),
+ content: stripHtml(valuesSectionStatic.content || ''),
+ // items (إذا موجودة) نتركها كما هي بدون تغيير
+ };
return (
<>
@@ -162,7 +201,6 @@ const About = () => {
component="h1"
sx={{
mb: 3,
- // Use theme heading font
fontSize: { xs: '2.5rem', md: '4rem' },
}}
>
@@ -225,7 +263,7 @@ const About = () => {
sx={{
lineHeight: 1.8,
fontSize: '1.1rem',
- whiteSpace: 'pre-line' // Preserve line breaks
+ whiteSpace: 'pre-line'
}}
>
{heritageSection.content}
@@ -332,107 +370,105 @@ const About = () => {
- {false && (
-
-
-
- Our Journey
-
-
- {/* Timeline line */}
-
-
- {milestones.map((milestone, index) => (
+ {false && (
+
-
-
-
-
- {milestone.year}
-
-
- {milestone.title}
-
-
- {milestone.description}
-
-
-
+ Our Journey
+
- {/* Timeline dot */}
+
-
+ {milestones.map((milestone, index) => (
+
+
+
+
+
+ {milestone.year}
+
+
+ {milestone.title}
+
+
+ {milestone.description}
+
+
+
+
+
+
+
+
+
+ ))}
- ))}
-
-
-
-)}
-
+
+ )}
>
);
};
-export default About;
\ No newline at end of file
+export default About;
diff --git a/client/src/pages/Booking.js b/client/src/pages/Booking.js
index 20beaa7..4a74586 100644
--- a/client/src/pages/Booking.js
+++ b/client/src/pages/Booking.js
@@ -1,30 +1,184 @@
-import React from 'react';
-import { Container, Typography, Box } from '@mui/material';
+import React, { useEffect, useState } from 'react';
+import {
+ Container, Typography, Box, TextField, Button,
+ MenuItem, Alert, CircularProgress
+} from '@mui/material';
+import api from '../utils/api';
const Booking = () => {
+ const [rooms, setRooms] = useState([]);
+ const [loadingRooms, setLoadingRooms] = useState(true);
+ const [submitting, setSubmitting] = useState(false);
+ const [message, setMessage] = useState(null);
+
+ const [form, setForm] = useState({
+ firstName: '',
+ lastName: '',
+ email: '',
+ phone: '',
+ roomId: '',
+ checkInDate: '',
+ checkOutDate: '',
+ adults: 1,
+ children: 0,
+ specialRequests: ''
+ });
+
+ useEffect(() => {
+ const loadRooms = async () => {
+ try {
+ const res = await api.get('/api/rooms?limit=100');
+ setRooms(res?.data?.data?.rooms || []);
+ } catch (e) {
+ setMessage({ type: 'error', text: 'Failed to load rooms' });
+ } finally {
+ setLoadingRooms(false);
+ }
+ };
+ loadRooms();
+ }, []);
+
+ const handleChange = (key) => (e) => {
+ setForm(prev => ({ ...prev, [key]: e.target.value }));
+ };
+
+ const handleSubmit = async (e) => {
+ e.preventDefault();
+ setSubmitting(true);
+ setMessage(null);
+
+ try {
+ const payload = {
+ guestInfo: {
+ firstName: form.firstName,
+ lastName: form.lastName,
+ email: form.email,
+ phone: form.phone
+ },
+ roomId: form.roomId,
+ checkInDate: form.checkInDate,
+ checkOutDate: form.checkOutDate,
+ numberOfGuests: {
+ adults: Number(form.adults),
+ children: Number(form.children || 0)
+ },
+ specialRequests: form.specialRequests
+ };
+
+ const res = await api.post('/api/bookings/request', payload);
+ const bookingNumber = res?.data?.data?.booking?.bookingNumber;
+
+ setMessage({
+ type: 'success',
+ text: bookingNumber
+ ? `Booking request submitted! Booking #: ${bookingNumber}`
+ : 'Booking request submitted successfully!'
+ });
+
+ // reset minimal
+ setForm(prev => ({ ...prev, specialRequests: '' }));
+ } catch (error) {
+ setMessage({
+ type: 'error',
+ text: error.response?.data?.message || 'Failed to submit booking request'
+ });
+ } finally {
+ setSubmitting(false);
+ }
+ };
+
return (
-
-
+
+
Book Your Stay
-
- Reserve your room at our luxury hotel
+
+ Submit a booking request (Pay at hotel)
-
-
- Booking form coming soon...
-
- {/* Add booking form here */}
-
+
+ {message && (
+ setMessage(null)}>
+ {message.text}
+
+ )}
+
+ {loadingRooms ? (
+
+
+
+ ) : (
+
+
+
+
+
+
+
+ {rooms.map(r => (
+
+ ))}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ )}
);
};
-export default Booking;
\ No newline at end of file
+export default Booking;
diff --git a/client/src/pages/CategoryGallery.js b/client/src/pages/CategoryGallery.js
index 48cfa60..c2fbd7a 100644
--- a/client/src/pages/CategoryGallery.js
+++ b/client/src/pages/CategoryGallery.js
@@ -1,4 +1,4 @@
-import React, { useState, useEffect } from 'react';
+import React, { useState, useEffect, useMemo } from 'react';
import {
Container,
Typography,
@@ -19,7 +19,7 @@ import {
import { useParams, Link } from 'react-router-dom';
import { motion, AnimatePresence } from 'framer-motion';
import { Helmet } from 'react-helmet-async';
-import staticData from '../utils/staticData';
+import api from '../utils/api';
import CloseIcon from '@mui/icons-material/Close';
import ArrowBackIosIcon from '@mui/icons-material/ArrowBackIos';
import ArrowForwardIosIcon from '@mui/icons-material/ArrowForwardIos';
@@ -31,35 +31,90 @@ import 'swiper/css/navigation';
import 'swiper/css/thumbs';
import 'swiper/css/free-mode';
+const API_BASE = process.env.REACT_APP_API_URL || 'http://localhost:5080';
+
+// يحوّل روابط uploads لتكون من الـ CMS بدل الـ client
+const toMediaUrl = (url) => {
+ if (!url) return url;
+
+ // إذا رابط كامل http/https
+ if (/^https?:\/\//i.test(url)) {
+ // Fix: لو كان بالغلط على 3060/uploads
+ if (url.includes('localhost:3060/uploads/')) {
+ return url.replace('http://localhost:3060', API_BASE);
+ }
+ return url;
+ }
+
+ // إذا /uploads/... خليه من API_BASE
+ if (url.startsWith('/uploads/')) return `${API_BASE}${url}`;
+
+ // باقي الروابط مثل /images/... خليها عادي
+ return url;
+};
+
const CategoryGallery = () => {
const { categorySlug } = useParams();
const [category, setCategory] = useState(null);
const [loading, setLoading] = useState(true);
const [error, setError] = useState(null);
+
const [lightboxOpen, setLightboxOpen] = useState(false);
const [lightboxIndex, setLightboxIndex] = useState(0);
const [thumbsSwiper, setThumbsSwiper] = useState(null);
+
const theme = useTheme();
const isMobile = useMediaQuery(theme.breakpoints.down('sm'));
useEffect(() => {
+ let mounted = true;
+
const fetchCategory = async () => {
try {
- const category = await staticData.getRoomCategory(categorySlug);
- setCategory(category);
+ setLoading(true);
+ setError(null);
+
+ const res = await api.get(`/api/room-categories/${categorySlug}`);
+ const cat = res?.data?.data?.category || null;
+
+ if (mounted) setCategory(cat);
} catch (err) {
console.error('Error loading category:', err);
- setError('Failed to load room category. Please try again later.');
+ if (mounted) setError('Category not found');
} finally {
- setLoading(false);
+ if (mounted) setLoading(false);
}
};
- if (categorySlug) {
- fetchCategory();
- }
+ if (categorySlug) fetchCategory();
+
+ return () => {
+ mounted = false;
+ };
}, [categorySlug]);
+ const rooms = category?.rooms || [];
+
+ // ✅ Gallery images:
+ // إذا الكاتيجوري عندها صور استخدميها، إذا لا، استخدمي صور الغرف التابعة
+ const images = useMemo(() => {
+ const catImages = category?.images || [];
+ if (catImages.length > 0) return catImages;
+
+ // fallback: اجمع صور الغرف
+ const roomImages = rooms
+ .flatMap((r) => r?.images || [])
+ .filter((img) => img?.url);
+
+ // إزالة التكرار حسب url
+ const seen = new Set();
+ return roomImages.filter((img) => {
+ if (seen.has(img.url)) return false;
+ seen.add(img.url);
+ return true;
+ });
+ }, [category, rooms]);
+
const handleImageClick = (index) => {
setLightboxIndex(index);
setLightboxOpen(true);
@@ -70,15 +125,11 @@ const CategoryGallery = () => {
};
const handlePrevious = () => {
- setLightboxIndex((prev) =>
- prev > 0 ? prev - 1 : (category.images.length - 1)
- );
+ setLightboxIndex((prev) => (prev > 0 ? prev - 1 : images.length - 1));
};
const handleNext = () => {
- setLightboxIndex((prev) =>
- prev < category.images.length - 1 ? prev + 1 : 0
- );
+ setLightboxIndex((prev) => (prev < images.length - 1 ? prev + 1 : 0));
};
if (loading) {
@@ -102,9 +153,6 @@ const CategoryGallery = () => {
);
}
- const images = category.images || [];
- const rooms = category.rooms || [];
-
return (
<>
@@ -117,7 +165,7 @@ const CategoryGallery = () => {
sx={{
minHeight: '40vh',
background: category.primaryImage
- ? `linear-gradient(rgba(0,0,0,0.5), rgba(0,0,0,0.5)), url(${category.primaryImage}) center/cover`
+ ? `linear-gradient(rgba(0,0,0,0.5), rgba(0,0,0,0.5)), url(${toMediaUrl(category.primaryImage)}) center/cover`
: 'linear-gradient(rgba(0,0,0,0.5), rgba(0,0,0,0.5)), url("/images/hero.jpg") center/cover',
display: 'flex',
alignItems: 'center',
@@ -135,30 +183,22 @@ const CategoryGallery = () => {
{category.name}
{category.description}
+
{category.priceRange && category.priceRange.min > 0 && (
From ${category.priceRange.min}
- {category.priceRange.max > category.priceRange.min &&
- ` - $${category.priceRange.max}`}
+ {category.priceRange.max > category.priceRange.min ? ` - $${category.priceRange.max}` : ''}
/night
)}
@@ -191,7 +231,7 @@ const CategoryGallery = () => {
handleImageClick(index)}
sx={{
@@ -200,9 +240,7 @@ const CategoryGallery = () => {
objectFit: 'cover',
cursor: 'pointer',
transition: 'transform 0.3s',
- '&:hover': {
- transform: 'scale(1.02)',
- },
+ '&:hover': { transform: 'scale(1.02)' },
}}
/>
@@ -225,7 +263,7 @@ const CategoryGallery = () => {
handleImageClick(index)}
sx={{
@@ -236,9 +274,7 @@ const CategoryGallery = () => {
cursor: 'pointer',
border: '2px solid transparent',
transition: 'border-color 0.3s',
- '&:hover': {
- borderColor: 'primary.main',
- },
+ '&:hover': { borderColor: 'primary.main' },
}}
/>
@@ -260,6 +296,7 @@ const CategoryGallery = () => {
Available Rooms ({rooms.length})
+
{rooms.map((room) => (
@@ -269,45 +306,44 @@ const CategoryGallery = () => {
display: 'flex',
flexDirection: 'column',
transition: 'transform 0.3s, box-shadow 0.3s',
- '&:hover': {
- transform: 'translateY(-4px)',
- boxShadow: 4,
- },
+ '&:hover': { transform: 'translateY(-4px)', boxShadow: 4 },
}}
>
0
- ? room.images.find(img => img.isPrimary)?.url || room.images[0].url
- : '/images/room-default.jpg'
+ toMediaUrl(
+ room.images?.find((img) => img.isPrimary)?.url ||
+ room.images?.[0]?.url
+ ) || '/images/room-default.jpg'
}
alt={room.name}
sx={{ objectFit: 'cover' }}
+ onError={(e) => { e.currentTarget.src = '/images/room-default.jpg'; }}
/>
+
{room.name}
+
{room.shortDescription}
+
Room {room.roomNumber} | {room.size} m² | Max {room.maxOccupancy} guests
+
{room.amenities && room.amenities.slice(0, 3).map((amenity, idx) => (
-
+
))}
+
@@ -317,6 +353,7 @@ const CategoryGallery = () => {
per night
+
-
+
{
>
{category.name}
-
+
{category.shortDescription || category.description}
{/* Features */}
- {category.features && category.features.length > 0 && (
+ {Array.isArray(category.features) && category.features.length > 0 && (
{category.features.slice(0, 4).map((feature, idx) => (
{
/>
))}
{category.features.length > 4 && (
-
+
)}
)}
{/* Stats */}
-
+
{category.roomCount > 0 && (
{category.roomCount} {category.roomCount === 1 ? 'Room' : 'Rooms'} Available
)}
+
+ {category.priceRange?.min > 0 && (
+
+ From ${category.priceRange.min}/night
+
+ )}
-
+
{
bookings.map((booking) => (
{booking.bookingNumber}
- {booking.guest?.name || 'N/A'}
+ {`${booking.guest?.firstName || ''} ${booking.guest?.lastName || ''}`.trim() || booking.guest?.email || 'N/A'}
{new Date(booking.checkInDate).toLocaleDateString()}
{new Date(booking.checkOutDate).toLocaleDateString()}
{booking.room?.name || 'N/A'}
diff --git a/client/src/pages/admin/RoomManagement.js b/client/src/pages/admin/RoomManagement.js
index 90a7b64..9492358 100644
--- a/client/src/pages/admin/RoomManagement.js
+++ b/client/src/pages/admin/RoomManagement.js
@@ -53,6 +53,7 @@ const ROOM_TYPES = ['Standard', 'Deluxe', 'Suite', 'Executive Suite', 'President
const RoomManagement = () => {
const [rooms, setRooms] = useState([]);
+ const [categories, setCategories] = useState([]);
const [loading, setLoading] = useState(true);
const [message, setMessage] = useState(null);
const [dialogOpen, setDialogOpen] = useState(false);
@@ -62,12 +63,14 @@ const RoomManagement = () => {
useEffect(() => {
fetchRooms();
+ fetchCategories();
+ // eslint-disable-next-line react-hooks/exhaustive-deps
}, []);
const fetchRooms = async () => {
try {
const response = await api.get('/api/rooms?limit=100');
- setRooms(response.data.data.rooms);
+ setRooms(response?.data?.data?.rooms || []);
} catch (error) {
console.error('Error fetching rooms:', error);
setMessage({ type: 'error', text: 'Failed to load rooms' });
@@ -76,11 +79,29 @@ const RoomManagement = () => {
}
};
+ const fetchCategories = async () => {
+ try {
+ // Prefer admin/all (includes inactive too)
+ const res = await api.get('/api/room-categories/admin/all');
+ setCategories(res?.data?.data?.categories || []);
+ } catch (e) {
+ // Fallback to public endpoint
+ try {
+ const res = await api.get('/api/room-categories');
+ setCategories(res?.data?.data?.categories || []);
+ } catch (err) {
+ console.error('Error fetching categories:', err);
+ setMessage({ type: 'error', text: 'Failed to load room categories' });
+ }
+ }
+ };
+
const handleOpenDialog = (room = null) => {
if (room) {
// Edit mode
setCurrentRoom({
...room,
+ category: room?.category?._id || room?.category || '', // handle populated or raw id
images: room.images || [],
amenities: room.amenities || [],
});
@@ -89,6 +110,7 @@ const RoomManagement = () => {
setCurrentRoom({
name: '',
type: 'Deluxe',
+ category: categories?.[0]?._id || '', // ✅ important
roomNumber: '',
floor: 1,
size: 0,
@@ -134,6 +156,10 @@ const RoomManagement = () => {
} else {
newImages[index] = { url: value, alt: '', isPrimary: index === 0 };
}
+
+ // Ensure first image stays primary if user adds images
+ if (index === 0) newImages[0].isPrimary = true;
+
handleChange('images', newImages);
};
@@ -143,6 +169,8 @@ const RoomManagement = () => {
const removeImage = (index) => {
const newImages = currentRoom.images.filter((_, idx) => idx !== index);
+ // Ensure primary image exists if list not empty
+ if (newImages.length > 0) newImages[0].isPrimary = true;
handleChange('images', newImages);
};
@@ -150,24 +178,29 @@ const RoomManagement = () => {
setSaving(true);
setMessage(null);
+ // ✅ required for website grouping
+ if (!currentRoom?.category) {
+ setSaving(false);
+ setMessage({ type: 'error', text: 'Please select a category for this room.' });
+ return;
+ }
+
try {
if (currentRoom._id) {
- // Update existing room
await api.put(`/api/rooms/${currentRoom._id}`, currentRoom);
setMessage({ type: 'success', text: 'Room updated successfully!' });
} else {
- // Create new room
await api.post('/api/rooms', currentRoom);
setMessage({ type: 'success', text: 'Room created successfully!' });
}
-
+
handleCloseDialog();
fetchRooms();
} catch (error) {
console.error('Error saving room:', error);
- setMessage({
- type: 'error',
- text: error.response?.data?.message || 'Failed to save room'
+ setMessage({
+ type: 'error',
+ text: error.response?.data?.message || 'Failed to save room'
});
} finally {
setSaving(false);
@@ -185,9 +218,9 @@ const RoomManagement = () => {
fetchRooms();
} catch (error) {
console.error('Error deleting room:', error);
- setMessage({
- type: 'error',
- text: error.response?.data?.message || 'Failed to delete room'
+ setMessage({
+ type: 'error',
+ text: error.response?.data?.message || 'Failed to delete room'
});
} finally {
setConfirmDialog({ open: false, roomId: null });
@@ -224,8 +257,8 @@ const RoomManagement = () => {
{message && (
- setMessage(null)}
>
@@ -316,8 +349,8 @@ const RoomManagement = () => {
{/* Add/Edit Room Dialog */}
-