final updates

This commit is contained in:
yotakii
2026-01-05 11:35:54 +03:00
parent 4d883c21c9
commit 54ce6ed114
12 changed files with 447 additions and 508 deletions

Binary file not shown.

Before

Width:  |  Height:  |  Size: 22 KiB

After

Width:  |  Height:  |  Size: 19 KiB

View File

@@ -30,6 +30,9 @@
"react-router-dom": "^6.19.0", "react-router-dom": "^6.19.0",
"react-scripts": "^5.0.1", "react-scripts": "^5.0.1",
"swiper": "^11.0.5" "swiper": "^11.0.5"
},
"devDependencies": {
"cross-env": "^10.1.0"
} }
}, },
"node_modules/@alloc/quick-lru": { "node_modules/@alloc/quick-lru": {
@@ -2526,6 +2529,13 @@
"integrity": "sha512-snKqtPW01tN0ui7yu9rGv69aJXr/a/Ywvl11sUjNtEcRc+ng/mQriFL0wLXMef74iHa/EkftbDzU9F8iFbH+zg==", "integrity": "sha512-snKqtPW01tN0ui7yu9rGv69aJXr/a/Ywvl11sUjNtEcRc+ng/mQriFL0wLXMef74iHa/EkftbDzU9F8iFbH+zg==",
"license": "MIT" "license": "MIT"
}, },
"node_modules/@epic-web/invariant": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/@epic-web/invariant/-/invariant-1.0.0.tgz",
"integrity": "sha512-lrTPqgvfFQtR/eY/qkIzp98OGdNJu0m5ji3q/nJI8v3SXkRKEnWiOxMmbvcSoAIzv/cGiuvRy57k4suKQSAdwA==",
"dev": true,
"license": "MIT"
},
"node_modules/@eslint-community/eslint-utils": { "node_modules/@eslint-community/eslint-utils": {
"version": "4.9.0", "version": "4.9.0",
"resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.9.0.tgz", "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.9.0.tgz",
@@ -6565,6 +6575,24 @@
"node": ">=10" "node": ">=10"
} }
}, },
"node_modules/cross-env": {
"version": "10.1.0",
"resolved": "https://registry.npmjs.org/cross-env/-/cross-env-10.1.0.tgz",
"integrity": "sha512-GsYosgnACZTADcmEyJctkJIoqAhHjttw7RsFrVoJNXbsWWqaq6Ym+7kZjq6mS45O0jij6vtiReppKQEtqWy6Dw==",
"dev": true,
"license": "MIT",
"dependencies": {
"@epic-web/invariant": "^1.0.0",
"cross-spawn": "^7.0.6"
},
"bin": {
"cross-env": "dist/bin/cross-env.js",
"cross-env-shell": "dist/bin/cross-env-shell.js"
},
"engines": {
"node": ">=20"
}
},
"node_modules/cross-spawn": { "node_modules/cross-spawn": {
"version": "7.0.6", "version": "7.0.6",
"resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz",
@@ -16978,20 +17006,6 @@
} }
} }
}, },
"node_modules/tailwindcss/node_modules/yaml": {
"version": "2.8.1",
"resolved": "https://registry.npmjs.org/yaml/-/yaml-2.8.1.tgz",
"integrity": "sha512-lcYcMxX2PO9XMGvAJkJ3OsNMw+/7FKes7/hgerGUYWIoWu5j/+YQqcZr5JnPZWzOsEBgMbSbiSTn/dv/69Mkpw==",
"license": "ISC",
"optional": true,
"peer": true,
"bin": {
"yaml": "bin.mjs"
},
"engines": {
"node": ">= 14.6"
}
},
"node_modules/tapable": { "node_modules/tapable": {
"version": "2.3.0", "version": "2.3.0",
"resolved": "https://registry.npmjs.org/tapable/-/tapable-2.3.0.tgz", "resolved": "https://registry.npmjs.org/tapable/-/tapable-2.3.0.tgz",

View File

@@ -27,7 +27,7 @@
"swiper": "^11.0.5" "swiper": "^11.0.5"
}, },
"scripts": { "scripts": {
"start": "PORT=3060 react-scripts start", "start": "cross-env PORT=3060 react-scripts start",
"build": "react-scripts build", "build": "react-scripts build",
"test": "react-scripts test", "test": "react-scripts test",
"eject": "react-scripts eject" "eject": "react-scripts eject"
@@ -50,5 +50,8 @@
"last 1 safari version" "last 1 safari version"
] ]
}, },
"proxy": "http://localhost:5080" "proxy": "http://localhost:5080",
"devDependencies": {
"cross-env": "^10.1.0"
}
} }

Binary file not shown.

Before

Width:  |  Height:  |  Size: 22 KiB

After

Width:  |  Height:  |  Size: 19 KiB

View File

@@ -2,7 +2,6 @@ import React, { useState, useEffect } from 'react';
import { import {
AppBar, AppBar,
Toolbar, Toolbar,
Typography,
Button, Button,
Box, Box,
IconButton, IconButton,
@@ -15,8 +14,6 @@ import {
ListItemText, ListItemText,
useMediaQuery, useMediaQuery,
useTheme, useTheme,
Select,
FormControl,
} from '@mui/material'; } from '@mui/material';
import { import {
Menu as MenuIcon, Menu as MenuIcon,
@@ -37,6 +34,16 @@ const Header = () => {
const [languageAnchor, setLanguageAnchor] = useState(null); const [languageAnchor, setLanguageAnchor] = useState(null);
const [scrolled, setScrolled] = useState(false); const [scrolled, setScrolled] = useState(false);
// ✅ لون غامق ثابت للهيدر الفاتح (حتى لو الثيم Dark)
const lightHeaderText = '#1a1a1a';
// ✅ Pending 17: Book Now -> WhatsApp with preset message
const WA_NUMBER = '963986105010';
const WA_TEXT = encodeURIComponent(
'For all booking inquiries and reservation confirmations, kindly contact us via WhatsApp'
);
const WA_LINK = `https://wa.me/${WA_NUMBER}?text=${WA_TEXT}`;
const navigationItems = [ const navigationItems = [
{ label: t('nav.home'), path: '/' }, { label: t('nav.home'), path: '/' },
{ label: t('nav.about'), path: '/about' }, { label: t('nav.about'), path: '/about' },
@@ -53,25 +60,24 @@ const Header = () => {
]; ];
useEffect(() => { useEffect(() => {
const handleScroll = () => { const handleScroll = () => setScrolled(window.scrollY > 50);
setScrolled(window.scrollY > 50);
};
window.addEventListener('scroll', handleScroll); window.addEventListener('scroll', handleScroll);
handleScroll();
return () => window.removeEventListener('scroll', handleScroll); return () => window.removeEventListener('scroll', handleScroll);
}, []); }, []);
const handleDrawerToggle = () => { // ✅ صفحات الخلفية عندها فاتحة (زيدي هون أي route تاني)
setMobileOpen(!mobileOpen); const lightHeaderRoutes = ['/facilities', '/contact'];
}; const forceLightHeader = lightHeaderRoutes.some(
(r) => location.pathname === r || location.pathname.startsWith(r + '/')
);
const handleLanguageClick = (event) => { const isLightHeader = scrolled || forceLightHeader;
setLanguageAnchor(event.currentTarget);
};
const handleLanguageClose = () => { const handleDrawerToggle = () => setMobileOpen((prev) => !prev);
setLanguageAnchor(null);
};
const handleLanguageClick = (event) => setLanguageAnchor(event.currentTarget);
const handleLanguageClose = () => setLanguageAnchor(null);
const handleLanguageChange = (languageCode) => { const handleLanguageChange = (languageCode) => {
i18n.changeLanguage(languageCode); i18n.changeLanguage(languageCode);
handleLanguageClose(); handleLanguageClose();
@@ -110,7 +116,7 @@ const Header = () => {
sx={{ sx={{
textAlign: 'center', textAlign: 'center',
backgroundColor: isActiveRoute(item.path) ? 'primary.main' : 'transparent', backgroundColor: isActiveRoute(item.path) ? 'primary.main' : 'transparent',
color: isActiveRoute(item.path) ? 'white' : 'text.primary', color: isActiveRoute(item.path) ? 'white' : lightHeaderText,
'&:hover': { '&:hover': {
backgroundColor: 'primary.light', backgroundColor: 'primary.light',
color: 'white', color: 'white',
@@ -122,19 +128,20 @@ const Header = () => {
</ListItem> </ListItem>
))} ))}
{/* ✅ Pending 17: Drawer Book Now -> WhatsApp */}
<ListItem disablePadding> <ListItem disablePadding>
<ListItemButton <ListItemButton
component={Link} component="a"
to="/booking" href={WA_LINK}
target="_blank"
rel="noopener noreferrer"
sx={{ sx={{
textAlign: 'center', textAlign: 'center',
backgroundColor: 'secondary.main', backgroundColor: 'secondary.main',
color: 'secondary.contrastText', color: 'secondary.contrastText',
m: 2, m: 2,
borderRadius: 2, borderRadius: 2,
'&:hover': { '&:hover': { backgroundColor: 'secondary.dark' },
backgroundColor: 'secondary.dark',
},
}} }}
> >
<ListItemText primary={t('nav.booking')} /> <ListItemText primary={t('nav.booking')} />
@@ -149,15 +156,22 @@ const Header = () => {
<AppBar <AppBar
position="fixed" position="fixed"
sx={{ sx={{
backgroundColor: scrolled ? 'rgba(255, 255, 255, 0.95)' : 'transparent', backgroundColor: isLightHeader ? 'rgba(255, 255, 255, 0.95)' : 'transparent',
color: scrolled ? 'text.primary' : 'white', color: isLightHeader ? lightHeaderText : 'white',
transition: 'all 0.3s ease-in-out', transition: 'all 0.3s ease-in-out',
backdropFilter: scrolled ? 'blur(10px)' : 'none', backdropFilter: isLightHeader ? 'blur(10px)' : 'none',
boxShadow: scrolled ? 3 : 0, boxShadow: isLightHeader ? 3 : 0,
zIndex: 1300, // Ensure header is always on top zIndex: 1300,
}} }}
> >
<Toolbar sx={{ justifyContent: 'space-between', maxWidth: 1200, width: '100%', mx: 'auto' }}> <Toolbar
sx={{
justifyContent: 'space-between',
maxWidth: 1200,
width: '100%',
mx: 'auto',
}}
>
{/* Logo */} {/* Logo */}
<motion.div <motion.div
initial={{ opacity: 0, x: -20 }} initial={{ opacity: 0, x: -20 }}
@@ -171,16 +185,14 @@ const Header = () => {
display: 'flex', display: 'flex',
alignItems: 'center', alignItems: 'center',
textDecoration: 'none', textDecoration: 'none',
'&:hover': { '&:hover': { opacity: 0.8 },
opacity: 0.8,
},
}} }}
> >
<img <img
src="/images/logo.png" src="/images/logo.png"
alt="Old Vine Hotel" alt="Old Vine Hotel"
style={{ style={{
height: scrolled ? '50px' : '60px', height: isLightHeader ? '50px' : '60px',
width: 'auto', width: 'auto',
transition: 'all 0.3s ease-in-out', transition: 'all 0.3s ease-in-out',
}} }}
@@ -202,17 +214,17 @@ const Header = () => {
component={Link} component={Link}
to={item.path} to={item.path}
sx={{ sx={{
color: scrolled color: isLightHeader
? (isActiveRoute(item.path) ? 'secondary.main' : 'text.primary') ? (isActiveRoute(item.path) ? 'secondary.main' : lightHeaderText)
: (isActiveRoute(item.path) ? 'white' : 'white'), : 'white',
fontWeight: isActiveRoute(item.path) ? 600 : 400, fontWeight: isActiveRoute(item.path) ? 600 : 400,
borderBottom: isActiveRoute(item.path) ? 2 : 0, borderBottom: isActiveRoute(item.path) ? 2 : 0,
borderColor: 'secondary.main', borderColor: 'secondary.main',
borderRadius: 0, borderRadius: 0,
opacity: isActiveRoute(item.path) ? 1 : 0.8, opacity: isActiveRoute(item.path) ? 1 : 0.85,
'&:hover': { '&:hover': {
backgroundColor: 'transparent', backgroundColor: 'transparent',
color: scrolled ? 'secondary.main' : 'white', color: isLightHeader ? 'secondary.main' : 'white',
opacity: 1, opacity: 1,
}, },
}} }}
@@ -227,21 +239,20 @@ const Header = () => {
<LanguageIcon /> <LanguageIcon />
</IconButton> </IconButton>
{/* Book Now Button */} {/* ✅ Pending 17: Book Now -> WhatsApp */}
<motion.div <motion.div
initial={{ opacity: 0, scale: 0.9 }} initial={{ opacity: 0, scale: 0.9 }}
animate={{ opacity: 1, scale: 1 }} animate={{ opacity: 1, scale: 1 }}
transition={{ duration: 0.5, delay: 0.3 }} transition={{ duration: 0.5, delay: 0.3 }}
> >
<Button <Button
component={Link} component="a"
to="/booking" href={WA_LINK}
target="_blank"
rel="noopener noreferrer"
variant="contained" variant="contained"
color="secondary" color="secondary"
sx={{ sx={{ fontWeight: 600, px: 3 }}
fontWeight: 600,
px: 3,
}}
> >
{t('nav.booking')} {t('nav.booking')}
</Button> </Button>
@@ -249,17 +260,13 @@ const Header = () => {
</Box> </Box>
)} )}
{/* Mobile Menu Button */} {/* Mobile */}
{isMobile && ( {isMobile && (
<Box sx={{ display: 'flex', alignItems: 'center', gap: 1 }}> <Box sx={{ display: 'flex', alignItems: 'center', gap: 1 }}>
<IconButton onClick={handleLanguageClick} sx={{ color: 'inherit' }}> <IconButton onClick={handleLanguageClick} sx={{ color: 'inherit' }}>
<LanguageIcon /> <LanguageIcon />
</IconButton> </IconButton>
<IconButton <IconButton color="inherit" onClick={handleDrawerToggle} sx={{ ml: 1 }}>
color="inherit"
onClick={handleDrawerToggle}
sx={{ ml: 1 }}
>
<MenuIcon /> <MenuIcon />
</IconButton> </IconButton>
</Box> </Box>
@@ -273,9 +280,7 @@ const Header = () => {
anchor="right" anchor="right"
open={mobileOpen} open={mobileOpen}
onClose={handleDrawerToggle} onClose={handleDrawerToggle}
ModalProps={{ ModalProps={{ keepMounted: true }}
keepMounted: true,
}}
sx={{ sx={{
'& .MuiDrawer-paper': { '& .MuiDrawer-paper': {
boxSizing: 'border-box', boxSizing: 'border-box',
@@ -307,7 +312,6 @@ const Header = () => {
</MenuItem> </MenuItem>
))} ))}
</Menu> </Menu>
</> </>
); );
}; };

View File

@@ -9,8 +9,8 @@ html {
} }
body { body {
font-family: 'Inter', -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen', font-family: "Montserrat", -apple-system, BlinkMacSystemFont, "Segoe UI", "Roboto",
'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue', "Oxygen", "Ubuntu", "Cantarell", "Fira Sans", "Droid Sans", "Helvetica Neue",
sans-serif; sans-serif;
-webkit-font-smoothing: antialiased; -webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale; -moz-osx-font-smoothing: grayscale;
@@ -18,6 +18,7 @@ body {
line-height: 1.6; line-height: 1.6;
} }
code { code {
font-family: source-code-pro, Menlo, Monaco, Consolas, 'Courier New', font-family: source-code-pro, Menlo, Monaco, Consolas, 'Courier New',
monospace; monospace;

View File

@@ -17,10 +17,12 @@
"exploreRooms": "استكشف الغرف" "exploreRooms": "استكشف الغرف"
}, },
"home": { "home": {
"welcomeTitle": "مرحباً بكم في فندق العريشة القديمة", "welcomeTitle": "مرحباً بكم في فندق الدالية القديمة",
"welcomeSubtitle": "حيث تلتقي الرفاهية بالتقاليد", "welcomeSubtitle": "حيث تلتقي الرفاهية بالتقاليد",
"welcomeDescription": "اكتشف تجربة ضيافة استثنائية في فندق العريشة القديمة، حيث كل تفصيل مصمم ليمنحك لحظات لا تُنسى من الراحة والأناقة.", "welcomeDescription": "اكتشف تجربة ضيافة استثنائية في فندق العريشة القديمة، حيث كل تفصيل مصمم ليمنحك لحظات لا تُنسى من الراحة والأناقة.",
"featuresTitle": "لماذا تختارنا", "featuresTitle": "لماذا تختارنا",
"feature6Title": "قاعة اجتماعات",
"feature6Description": "يوفّر بيئة فريدة لرجال الأعمال لعقد اجتماعاتهم ومؤتمراتهم.",
"feature1Title": "إقامة فاخرة", "feature1Title": "إقامة فاخرة",
"feature1Description": "غرف وأجنحة أنيقة مع وسائل راحة متميزة", "feature1Description": "غرف وأجنحة أنيقة مع وسائل راحة متميزة",
"feature2Title": "مطاعم راقية", "feature2Title": "مطاعم راقية",
@@ -97,6 +99,8 @@
"message": "الرسالة", "message": "الرسالة",
"sendMessage": "إرسال رسالة", "sendMessage": "إرسال رسالة",
"messageSent": "تم إرسال الرسالة بنجاح!", "messageSent": "تم إرسال الرسالة بنجاح!",
"whatsappOpened": "تم فتح واتساب. اضغط إرسال داخل واتساب لإرسال الرسالة.",
"requiredFields": "رجاءً املأ الحقول المطلوبة (الاسم، البريد، الرسالة).",
"directions": "احصل على الاتجاهات", "directions": "احصل على الاتجاهات",
"whatsapp": "واتساب" "whatsapp": "واتساب"
}, },

View File

@@ -32,6 +32,8 @@
"roomsTitle": "Our Rooms & Suites", "roomsTitle": "Our Rooms & Suites",
"roomsSubtitle": "Comfort and elegance in every detail", "roomsSubtitle": "Comfort and elegance in every detail",
"viewAllRooms": "View All Rooms", "viewAllRooms": "View All Rooms",
"feature6Title": "Meeting Room",
"feature6Description": "A unique setting for business meetings and conferences.",
"offersTitle": "Special Offers", "offersTitle": "Special Offers",
"offersSubtitle": "Exclusive deals for your perfect stay" "offersSubtitle": "Exclusive deals for your perfect stay"
}, },

View File

@@ -27,6 +27,8 @@
"feature2Description": "Cuisine exquise et expériences culinaires de classe mondiale", "feature2Description": "Cuisine exquise et expériences culinaires de classe mondiale",
"feature4Title": "Emplacement privilégié", "feature4Title": "Emplacement privilégié",
"feature5Title": "Belles terrasses", "feature5Title": "Belles terrasses",
"feature6Title": "Salle de réunion",
"feature6Description": "Un cadre unique pour les réunions daffaires et les conférences.",
"feature5Description": "Détendez-vous sur nos magnifiques terrasses avec des vues imprenables sur la vieille Damas", "feature5Description": "Détendez-vous sur nos magnifiques terrasses avec des vues imprenables sur la vieille Damas",
"feature4Description": "Parfaitement situé pour les affaires et les loisirs", "feature4Description": "Parfaitement situé pour les affaires et les loisirs",
"roomsTitle": "Nos chambres et suites", "roomsTitle": "Nos chambres et suites",
@@ -95,8 +97,11 @@
"hours": "Heures", "hours": "Heures",
"name": "Nom complet", "name": "Nom complet",
"message": "Message", "message": "Message",
"subject": "Sujet",
"sendMessage": "Envoyer le message", "sendMessage": "Envoyer le message",
"messageSent": "Message envoyé avec succès !", "messageSent": "Message envoyé avec succès !",
"whatsappOpened": "WhatsApp est ouvert. Appuyez sur Envoyer dans WhatsApp pour envoyer votre message.",
"requiredFields": "Veuillez remplir les champs obligatoires (nom, e-mail, message).",
"directions": "Obtenir l'itinéraire", "directions": "Obtenir l'itinéraire",
"whatsapp": "WhatsApp" "whatsapp": "WhatsApp"
}, },

View File

@@ -332,102 +332,104 @@ const About = () => {
</motion.div> </motion.div>
</Container> </Container>
</Box> </Box>
{false && (
<Container maxWidth="lg" sx={{ py: 8 }}>
<motion.div
initial={{ opacity: 0, y: 30 }}
whileInView={{ opacity: 1, y: 0 }}
transition={{ duration: 0.8 }}
viewport={{ once: true }}
>
<Typography
variant="h3"
component="h2"
textAlign="center"
sx={{ mb: 6, color: 'primary.main' }}
>
Our Journey
</Typography>
{/* Timeline Section */} <Box sx={{ position: 'relative' }}>
<Container maxWidth="lg" sx={{ py: 8 }}> {/* Timeline line */}
<motion.div <Box
initial={{ opacity: 0, y: 30 }} sx={{
whileInView={{ opacity: 1, y: 0 }} position: 'absolute',
transition={{ duration: 0.8 }} left: '50%',
viewport={{ once: true }} top: 0,
> bottom: 0,
<Typography width: 2,
variant="h3" backgroundColor: 'primary.main',
component="h2" transform: 'translateX(-50%)',
textAlign="center" display: { xs: 'none', md: 'block' },
sx={{ }}
mb: 6, />
color: 'primary.main',
}} {milestones.map((milestone, index) => (
<motion.div
key={index}
initial={{ opacity: 0, x: index % 2 === 0 ? -50 : 50 }}
whileInView={{ opacity: 1, x: 0 }}
transition={{ duration: 0.6, delay: index * 0.2 }}
viewport={{ once: true }}
> >
Our Journey
</Typography>
<Box sx={{ position: 'relative' }}>
{/* Timeline line */}
<Box <Box
sx={{ sx={{
position: 'absolute', display: 'flex',
left: '50%', alignItems: 'center',
top: 0, mb: 6,
bottom: 0, flexDirection: {
width: 2, xs: 'column',
backgroundColor: 'primary.main', md: index % 2 === 0 ? 'row' : 'row-reverse',
transform: 'translateX(-50%)', },
display: { xs: 'none', md: 'block' } textAlign: { xs: 'center', md: 'left' },
}} }}
/> >
<Box sx={{ flex: 1, px: { xs: 0, md: 4 } }}>
{milestones.map((milestone, index) => ( <Card
<motion.div
key={index}
initial={{ opacity: 0, x: index % 2 === 0 ? -50 : 50 }}
whileInView={{ opacity: 1, x: 0 }}
transition={{ duration: 0.6, delay: index * 0.2 }}
viewport={{ once: true }}
>
<Box
sx={{ sx={{
display: 'flex', p: 3,
alignItems: 'center', maxWidth: 400,
mb: 6, mx: { xs: 'auto', md: index % 2 === 0 ? 0 : 'auto' },
flexDirection: { xs: 'column', md: index % 2 === 0 ? 'row' : 'row-reverse' },
textAlign: { xs: 'center', md: 'left' }
}} }}
> >
<Box sx={{ flex: 1, px: { xs: 0, md: 4 } }}> <Typography
<Card sx={{ p: 3, maxWidth: 400, mx: { xs: 'auto', md: index % 2 === 0 ? 0 : 'auto' } }}> variant="h4"
<Typography component="h3"
variant="h4" sx={{ color: 'secondary.main', fontWeight: 700, mb: 1 }}
component="h3" >
sx={{ {milestone.year}
color: 'secondary.main', </Typography>
fontWeight: 700, <Typography variant="h6" sx={{ mb: 2, fontWeight: 600 }}>
mb: 1 {milestone.title}
}} </Typography>
> <Typography variant="body2" color="text.secondary">
{milestone.year} {milestone.description}
</Typography> </Typography>
<Typography variant="h6" sx={{ mb: 2, fontWeight: 600 }}> </Card>
{milestone.title} </Box>
</Typography>
<Typography variant="body2" color="text.secondary">
{milestone.description}
</Typography>
</Card>
</Box>
{/* Timeline dot */} {/* Timeline dot */}
<Box <Box
sx={{ sx={{
width: 20, width: 20,
height: 20, height: 20,
backgroundColor: 'secondary.main', backgroundColor: 'secondary.main',
borderRadius: '50%', borderRadius: '50%',
border: `4px solid ${theme.palette.background.paper}`, border: `4px solid ${theme.palette.background.paper}`,
boxShadow: 2, boxShadow: 2,
display: { xs: 'none', md: 'block' }, display: { xs: 'none', md: 'block' },
zIndex: 1 zIndex: 1,
}} }}
/> />
<Box sx={{ flex: 1 }} /> <Box sx={{ flex: 1 }} />
</Box> </Box>
</motion.div> </motion.div>
))} ))}
</Box> </Box>
</motion.div> </motion.div>
</Container> </Container>
)}
</> </>
); );

View File

@@ -5,18 +5,15 @@ import {
Typography, Typography,
Grid, Grid,
Card, Card,
CardContent,
TextField, TextField,
Button, Button,
IconButton, IconButton,
Alert, Alert,
useTheme,
} from '@mui/material'; } from '@mui/material';
import { import {
Phone, Phone,
Email, Email,
LocationOn, LocationOn,
AccessTime,
WhatsApp, WhatsApp,
Facebook, Facebook,
Instagram, Instagram,
@@ -26,132 +23,153 @@ import {
import { useTranslation } from 'react-i18next'; import { useTranslation } from 'react-i18next';
import { motion } from 'framer-motion'; import { motion } from 'framer-motion';
import { Helmet } from 'react-helmet-async'; import { Helmet } from 'react-helmet-async';
import { useQuery, useMutation } from 'react-query'; import { useQuery } from 'react-query';
import axios from 'axios'; import axios from 'axios';
const HOTEL_WHATSAPP = '963986105010';
const Contact = () => { const Contact = () => {
const { t } = useTranslation(); const { t, i18n } = useTranslation();
const theme = useTheme();
const lang = (i18n.resolvedLanguage || i18n.language || 'en').toLowerCase();
const isAr = lang.startsWith('ar');
const isFr = lang.startsWith('fr');
const tf = (key, en, ar, fr) => t(key, isAr ? ar : isFr ? fr : en);
const [formData, setFormData] = useState({ const [formData, setFormData] = useState({
name: '', name: '',
email: '', email: '',
phone: '', phone: '',
subject: '', subject: '',
message: '' message: '',
}); });
const [submitStatus, setSubmitStatus] = useState(null); const [submitStatus, setSubmitStatus] = useState(null);
// Fetch contact information
const { data: contactInfo } = useQuery( const { data: contactInfo } = useQuery(
'contactInfo', 'contactInfo',
() => axios.get('/api/contact/info').then(res => res.data.data), () => axios.get('/api/contact/info').then((res) => res.data.data),
{ {
staleTime: 5 * 60 * 1000, // 5 minutes staleTime: 5 * 60 * 1000,
} retry: 0,
);
// Submit contact form
const submitContactForm = useMutation(
(formData) => axios.post('/api/contact', formData),
{
onSuccess: () => {
setSubmitStatus({ type: 'success', message: t('contact.messageSent') });
setFormData({ name: '', email: '', phone: '', subject: '', message: '' });
},
onError: (error) => {
setSubmitStatus({
type: 'error',
message: error.response?.data?.message || 'Failed to send message'
});
}
} }
); );
const handleInputChange = (e) => { const handleInputChange = (e) => {
setFormData({ setFormData((prev) => ({
...formData, ...prev,
[e.target.name]: e.target.value [e.target.name]: e.target.value,
}); }));
};
const openWhatsApp = () => {
const lines = [
tf(
'contact.whatsappNewMessageHeader',
'New message from website:',
'رسالة جديدة من موقع الفندق:',
'Nouveau message du site web :'
),
`${tf('contact.whatsappLabelName', 'Name', 'الاسم', 'Nom')}: ${formData.name}`,
`${tf('contact.whatsappLabelEmail', 'Email', 'البريد الإلكتروني', 'E-mail')}: ${formData.email}`,
`${tf('contact.whatsappLabelPhone', 'Phone', 'الهاتف', 'Téléphone')}: ${formData.phone || '-'}`,
`${tf('contact.whatsappLabelSubject', 'Subject', 'الموضوع', 'Sujet')}: ${formData.subject || '-'}`,
`${tf('contact.whatsappLabelMessage', 'Message', 'الرسالة', 'Message')}: ${formData.message}`,
];
const text = encodeURIComponent(lines.join('\n'));
const url = `https://wa.me/${HOTEL_WHATSAPP}?text=${text}`;
window.open(url, '_blank', 'noopener,noreferrer');
}; };
const handleSubmit = (e) => { const handleSubmit = (e) => {
e.preventDefault(); e.preventDefault();
if (formData.name && formData.email && formData.message) {
submitContactForm.mutate(formData); if (!formData.name || !formData.email || !formData.message) {
setSubmitStatus({
type: 'error',
message: tf(
'contact.requiredFields',
'Please fill the required fields (name, email, message).',
'يرجى تعبئة الحقول المطلوبة (الاسم، البريد الإلكتروني، الرسالة).',
'Veuillez remplir les champs obligatoires (nom, e-mail, message).'
),
});
return;
} }
openWhatsApp();
setSubmitStatus({
type: 'success',
message: tf(
'contact.whatsappOpened',
'WhatsApp opened. Press Send inside WhatsApp to deliver your message.',
'تم فتح واتساب. اضغط "إرسال" داخل واتساب لإرسال رسالتك.',
'WhatsApp sest ouvert. Appuyez sur Envoyer داخل WhatsApp pour إرسال رسالتك.'
),
});
setFormData({ name: '', email: '', phone: '', subject: '', message: '' });
}; };
const contactCards = [ const contactCards = [
{ {
title: 'Phone', title: tf('contact.cardPhoneTitle', 'Phone', 'الهاتف', 'Téléphone'),
icon: <Phone sx={{ fontSize: 40 }} />, icon: <Phone sx={{ fontSize: 40 }} />,
primary: '0112241609', primary: '0112241609',
secondary: '24/7 Available', secondary: tf('contact.cardPhoneSecondary', '24/7 Available', 'متاح 24/7', 'Disponible 24/7'),
action: 'tel:0112241609' action: 'tel:0112241609',
}, },
{ {
title: 'Email', title: tf('contact.cardEmailTitle', 'Email', 'البريد الإلكتروني', 'E-mail'),
icon: <Email sx={{ fontSize: 40 }} />, icon: <Email sx={{ fontSize: 40 }} />,
primary: 'reservations@oldvinehotel.com', primary: 'reservations@oldvinehotel.com',
secondary: 'Response within 24 hours', secondary: tf(
action: 'mailto:reservations@oldvinehotel.com' 'contact.cardEmailSecondary',
'Response within 24 hours',
'الرد خلال 24 ساعة',
'Réponse sous 24 heures'
),
action: 'mailto:reservations@oldvinehotel.com',
}, },
{ {
title: 'WhatsApp', title: tf('contact.cardWhatsAppTitle', 'WhatsApp', 'واتساب', 'WhatsApp'),
icon: <WhatsApp sx={{ fontSize: 40 }} />, icon: <WhatsApp sx={{ fontSize: 40 }} />,
primary: '+963986105010', primary: '+963986105010',
secondary: 'Quick responses', secondary: tf('contact.cardWhatsAppSecondary', 'Quick responses', 'رد سريع', 'Réponses rapides'),
action: 'https://wa.me/963986105010' action: `https://wa.me/${HOTEL_WHATSAPP}`,
}, },
{ {
title: 'Address', title: tf('contact.cardAddressTitle', 'Address', 'العنوان', 'Adresse'),
icon: <LocationOn sx={{ fontSize: 40 }} />, icon: <LocationOn sx={{ fontSize: 40 }} />,
primary: contactInfo?.hotel?.address?.formatted || 'Old Damascus City', primary: contactInfo?.hotel?.address?.formatted || tf('contact.addressFallback', 'Old Vine Hotel, Damascus', 'فندق أولد فاين - دمشق', 'Old Vine Hotel, Damas'),
secondary: t('contact.directions'), secondary: t('contact.directions'),
action: 'https://maps.google.com/?q=' + encodeURIComponent(contactInfo?.hotel?.address?.formatted || 'Old Damascus City') action: 'https://www.google.com/maps/search/?api=1&query=Old%20Vine%20Hotel%20Damascus',
}
];
const departments = [
{
name: 'Reservations',
phone: contactInfo?.departments?.reservations?.phone || '+1 (555) 123-4567',
email: contactInfo?.departments?.reservations?.email || 'reservations@oldvinehotel.com',
hours: contactInfo?.departments?.reservations?.hours || '24/7'
}, },
{
name: 'Concierge',
phone: contactInfo?.departments?.concierge?.phone || '+1 (555) 123-4568',
email: contactInfo?.departments?.concierge?.email || 'concierge@oldvinehotel.com',
hours: contactInfo?.departments?.concierge?.hours || '6:00 AM - 12:00 AM'
},
{
name: 'Restaurant',
phone: contactInfo?.departments?.restaurant?.phone || '+1 (555) 123-4569',
email: contactInfo?.departments?.restaurant?.email || 'restaurant@oldvinehotel.com',
hours: contactInfo?.departments?.restaurant?.hours || '6:30 AM - 11:00 PM'
},
{
name: 'Spa & Wellness',
phone: contactInfo?.departments?.spa?.phone || '+1 (555) 123-4570',
email: contactInfo?.departments?.spa?.email || 'spa@oldvinehotel.com',
hours: contactInfo?.departments?.spa?.hours || '8:00 AM - 9:00 PM'
}
]; ];
return ( return (
<> <>
<Helmet> <Helmet>
<title>Contact Us - The Old Vine Hotel</title> <title>{tf('contact.pageTitle', 'Contact Us - The Old Vine Hotel', 'تواصل معنا - فندق أولد فاين', 'Contact - The Old Vine Hotel')}</title>
<meta name="description" content="Get in touch with The Old Vine Hotel. Contact us for reservations, inquiries, or any assistance you need during your stay." /> <meta
name="description"
content={tf(
'contact.pageDescription',
'Get in touch with The Old Vine Hotel. Contact us for reservations, inquiries, or any assistance you need during your stay.',
'تواصل مع فندق أولد فاين للحجوزات والاستفسارات وأي مساعدة تحتاجها أثناء إقامتك.',
'Contactez The Old Vine Hotel pour les réservations, questions, ou toute assistance pendant votre séjour.'
)}
/>
</Helmet> </Helmet>
{/* Hero Section */} {/* Hero Section */}
<Box <Box
sx={{ sx={{
minHeight: '60vh', minHeight: '60vh',
background: 'linear-gradient(rgba(0,0,0,0.5), rgba(0,0,0,0.5)), url("/images/gallery/hotel-gallery/31.jpg") center/cover', background:
'linear-gradient(rgba(0,0,0,0.5), rgba(0,0,0,0.5)), url("/images/gallery/hotel-gallery/31.jpg") center/cover',
display: 'flex', display: 'flex',
alignItems: 'center', alignItems: 'center',
justifyContent: 'center', justifyContent: 'center',
@@ -170,7 +188,6 @@ const Contact = () => {
component="h1" component="h1"
sx={{ sx={{
mb: 3, mb: 3,
fontFamily: 'Playfair Display',
fontSize: { xs: '2.5rem', md: '3.5rem' }, fontSize: { xs: '2.5rem', md: '3.5rem' },
}} }}
> >
@@ -223,17 +240,10 @@ const Contact = () => {
}, },
}} }}
> >
<Box <Box sx={{ display: 'flex', justifyContent: 'center', mb: 2, color: 'primary.main' }}>
sx={{
display: 'flex',
justifyContent: 'center',
mb: 2,
color: 'primary.main',
}}
>
{card.icon} {card.icon}
</Box> </Box>
<Typography variant="h6" component="h3" sx={{ mb: 2, fontWeight: 600 }}> <Typography variant="h6" sx={{ mb: 2, fontWeight: 600 }}>
{card.title} {card.title}
</Typography> </Typography>
<Typography variant="body1" sx={{ mb: 1, fontWeight: 500 }}> <Typography variant="body1" sx={{ mb: 1, fontWeight: 500 }}>
@@ -248,9 +258,8 @@ const Contact = () => {
))} ))}
</Grid> </Grid>
{/* Contact Form and Departments */} {/* Contact Form */}
<Grid container spacing={6}> <Grid container spacing={6}>
{/* Contact Form */}
<Grid item xs={12} md={12}> <Grid item xs={12} md={12}>
<motion.div <motion.div
initial={{ opacity: 0, x: -30 }} initial={{ opacity: 0, x: -30 }}
@@ -259,15 +268,8 @@ const Contact = () => {
viewport={{ once: true }} viewport={{ once: true }}
> >
<Card sx={{ p: 4 }}> <Card sx={{ p: 4 }}>
<Typography <Typography variant="h4" sx={{ mb: 3, color: 'primary.main' }}>
variant="h4" {tf('contact.sendUsMessageTitle', 'Send us a Message', 'أرسل لنا رسالة', 'Envoyez-nous un message')}
component="h2"
sx={{
mb: 3,
color: 'primary.main',
}}
>
Send us a Message
</Typography> </Typography>
{submitStatus && ( {submitStatus && (
@@ -315,7 +317,7 @@ const Contact = () => {
<Grid item xs={12} sm={6}> <Grid item xs={12} sm={6}>
<TextField <TextField
name="subject" name="subject"
label="Subject" label={tf('contact.subject', 'Subject', 'الموضوع', 'Sujet')}
fullWidth fullWidth
value={formData.subject} value={formData.subject}
onChange={handleInputChange} onChange={handleInputChange}
@@ -333,6 +335,7 @@ const Contact = () => {
onChange={handleInputChange} onChange={handleInputChange}
/> />
</Grid> </Grid>
<Grid item xs={12}> <Grid item xs={12}>
<Button <Button
type="submit" type="submit"
@@ -340,10 +343,9 @@ const Contact = () => {
color="primary" color="primary"
size="large" size="large"
startIcon={<Send />} startIcon={<Send />}
disabled={submitContactForm.isLoading}
sx={{ px: 4, py: 2 }} sx={{ px: 4, py: 2 }}
> >
{submitContactForm.isLoading ? 'Sending...' : t('contact.sendMessage')} {t('contact.sendMessage')}
</Button> </Button>
</Grid> </Grid>
</Grid> </Grid>
@@ -352,7 +354,7 @@ const Contact = () => {
</motion.div> </motion.div>
</Grid> </Grid>
{/* Social Media Section */} {/* Social Media */}
<Grid item xs={12} md={5}> <Grid item xs={12} md={5}>
<motion.div <motion.div
initial={{ opacity: 0, x: 30 }} initial={{ opacity: 0, x: 30 }}
@@ -360,17 +362,9 @@ const Contact = () => {
transition={{ duration: 0.8 }} transition={{ duration: 0.8 }}
viewport={{ once: true }} viewport={{ once: true }}
> >
{/* Social Media */}
<Card sx={{ p: 4 }}> <Card sx={{ p: 4 }}>
<Typography <Typography variant="h5" sx={{ mb: 3, color: 'primary.main' }}>
variant="h5" {tf('contact.followUs', 'Follow Us', 'تابعنا', 'Suivez-nous')}
component="h3"
sx={{
mb: 3,
color: 'primary.main',
}}
>
Follow Us
</Typography> </Typography>
<Box sx={{ display: 'flex', gap: 2 }}> <Box sx={{ display: 'flex', gap: 2 }}>
@@ -379,11 +373,7 @@ const Contact = () => {
href={contactInfo?.socialMedia?.facebook || '#'} href={contactInfo?.socialMedia?.facebook || '#'}
target="_blank" target="_blank"
rel="noopener noreferrer" rel="noopener noreferrer"
sx={{ sx={{ backgroundColor: '#1877F2', color: 'white' }}
backgroundColor: '#1877F2',
color: 'white',
'&:hover': { backgroundColor: '#166FE5' },
}}
> >
<Facebook /> <Facebook />
</IconButton> </IconButton>
@@ -392,11 +382,7 @@ const Contact = () => {
href={contactInfo?.socialMedia?.instagram || '#'} href={contactInfo?.socialMedia?.instagram || '#'}
target="_blank" target="_blank"
rel="noopener noreferrer" rel="noopener noreferrer"
sx={{ sx={{ backgroundColor: '#E4405F', color: 'white' }}
backgroundColor: '#E4405F',
color: 'white',
'&:hover': { backgroundColor: '#D62D4A' },
}}
> >
<Instagram /> <Instagram />
</IconButton> </IconButton>
@@ -405,24 +391,16 @@ const Contact = () => {
href={contactInfo?.socialMedia?.twitter || '#'} href={contactInfo?.socialMedia?.twitter || '#'}
target="_blank" target="_blank"
rel="noopener noreferrer" rel="noopener noreferrer"
sx={{ sx={{ backgroundColor: '#1DA1F2', color: 'white' }}
backgroundColor: '#1DA1F2',
color: 'white',
'&:hover': { backgroundColor: '#1A91DA' },
}}
> >
<Twitter /> <Twitter />
</IconButton> </IconButton>
<IconButton <IconButton
component="a" component="a"
href="https://wa.me/963986105010" href={`https://wa.me/${HOTEL_WHATSAPP}`}
target="_blank" target="_blank"
rel="noopener noreferrer" rel="noopener noreferrer"
sx={{ sx={{ backgroundColor: '#25D366', color: 'white' }}
backgroundColor: '#25D366',
color: 'white',
'&:hover': { backgroundColor: '#22C55E' },
}}
> >
<WhatsApp /> <WhatsApp />
</IconButton> </IconButton>
@@ -436,14 +414,14 @@ const Contact = () => {
{/* Map Section */} {/* Map Section */}
<Box sx={{ width: '100%', height: 400, bgcolor: 'grey.200' }}> <Box sx={{ width: '100%', height: 400, bgcolor: 'grey.200' }}>
<iframe <iframe
src="https://www.google.com/maps/embed?pb=!1m18!1m12!1m3!1d3310.1234567890123!2d36.2765!3d33.5138!2m3!1f0!2f0!3f0!3m2!1i1024!2i768!4f13.1!3m3!1m2!1s0x1518e6dc413cc6a7%3A0x6b9f3c2b8b8b8b8b!2sOld%20Damascus%20City!5e0!3m2!1sen!2ssy!4v1704067200000!5m2!1sen!2ssy&zoom=16" src="https://www.google.com/maps/embed?pb=!1m18!1m12!1m3!1d881.089256494776!2d36.30748342144017!3d33.51308026272531!2m3!1f0!2f0!3f0!3m2!1i1024!2i768!4f13.1!3m3!1m2!1s0x1518e728348b96bf%3A0x9a06409382325607!2sOld%20Vine%20Hotel!5e0!3m2!1$ar!2s!4v1766475597734!5m2!1$ar!2s"
width="100%" width="100%"
height="100%" height="100%"
style={{ border: 0 }} style={{ border: 0 }}
allowFullScreen="" allowFullScreen
loading="lazy" loading="lazy"
referrerPolicy="no-referrer-when-downgrade" referrerPolicy="no-referrer-when-downgrade"
title="Hotel Location" title={tf('contact.mapTitle', 'Hotel Location', 'موقع الفندق', 'Emplacement de lhôtel')}
/> />
</Box> </Box>
</> </>

View File

@@ -9,20 +9,14 @@ import {
CardContent, CardContent,
CardMedia, CardMedia,
Chip, Chip,
useTheme,
CircularProgress, CircularProgress,
} from '@mui/material'; } from '@mui/material';
import { import {
Hotel, Hotel,
Restaurant, Restaurant,
FitnessCenter,
LocationOn, LocationOn,
Star,
CheckCircle,
Spa,
Pool,
BusinessCenter,
Deck, Deck,
BusinessCenter,
} from '@mui/icons-material'; } from '@mui/icons-material';
import { Link } from 'react-router-dom'; import { Link } from 'react-router-dom';
import { useTranslation } from 'react-i18next'; import { useTranslation } from 'react-i18next';
@@ -32,22 +26,19 @@ import staticData from '../utils/staticData';
const Home = () => { const Home = () => {
const { t, i18n } = useTranslation(); const { t, i18n } = useTranslation();
const theme = useTheme();
const [content, setContent] = useState(null); const [content, setContent] = useState(null);
const [roomCategories, setRoomCategories] = useState([]); const [roomCategories, setRoomCategories] = useState([]);
const [loading, setLoading] = useState(true); const [loading, setLoading] = useState(true);
const currentLanguage = i18n.language; const currentLanguage = i18n.language;
// Fetch homepage content and room categories from static data
useEffect(() => { useEffect(() => {
const fetchContent = async () => { const fetchContent = async () => {
try { try {
const [homeContent, categories] = await Promise.all([ const [homeContent, categories] = await Promise.all([
staticData.getHomeContent(), staticData.getHomeContent(),
staticData.getRoomCategories() staticData.getRoomCategories(),
]); ]);
setContent(homeContent); setContent(homeContent);
// Get first 3 categories
setRoomCategories(categories.slice(0, 3)); setRoomCategories(categories.slice(0, 3));
} catch (error) { } catch (error) {
console.error('Error loading homepage content:', error); console.error('Error loading homepage content:', error);
@@ -80,29 +71,41 @@ const Home = () => {
title: t('home.feature5Title'), title: t('home.feature5Title'),
description: t('home.feature5Description'), description: t('home.feature5Description'),
}, },
{
icon: <BusinessCenter sx={{ fontSize: 40 }} />,
title: t('home.feature6Title', 'Meeting Room'),
description: t(
'home.feature6Description',
'A unique business-friendly setting for meetings and conferences.'
),
},
]; ];
// Map room categories from API to display format const heroImage = content?.hero?.backgroundImage || '/images/hero.jpg';
const welcomeSection = content?.sections?.find((s) => s.sectionId === 'welcome') || {};
const useTranslations = currentLanguage !== 'en';
const welcomeTitle = useTranslations
? t('home.welcomeTitle')
: (welcomeSection.title || t('home.welcomeTitle'));
const welcomeSubtitle = useTranslations
? t('home.welcomeSubtitle')
: (welcomeSection.subtitle || t('home.welcomeSubtitle'));
const welcomeContent = useTranslations
? t('home.welcomeDescription')
: (welcomeSection.content || t('home.welcomeDescription'));
const roomTypes = roomCategories.map((category) => ({ const roomTypes = roomCategories.map((category) => ({
id: category._id || category.slug, id: category._id || category.slug,
name: category.name, name: category.name,
image: category.primaryImage || '/images/room-default.jpg', image: category.primaryImage || '/images/room-default.jpg',
price: category.priceRange?.min || 0, features: category.features?.slice(0, 4) || [],
features: category.features?.slice(0, 4) || [], // Show first 4 features
slug: category.slug, slug: category.slug,
})); }));
// Special offers - using translations
const specialOffers = [
{
title: t('home.offersTitle'),
description: t('home.offersSubtitle'),
discount: '25% OFF',
validUntil: '2025-12-31',
},
];
// Show loading state
if (loading) { if (loading) {
return ( return (
<Box sx={{ display: 'flex', justifyContent: 'center', alignItems: 'center', minHeight: '100vh' }}> <Box sx={{ display: 'flex', justifyContent: 'center', alignItems: 'center', minHeight: '100vh' }}>
@@ -111,98 +114,59 @@ const Home = () => {
); );
} }
// Use translations for non-English languages, static data for English const LOGO_BOTTOM = { xs: 40, sm: 55, md: 70 };
const useTranslations = currentLanguage !== 'en';
// Fallback content - prioritize translations for non-English
const heroTitle = useTranslations ? t('hero.hotelName') : (content?.hero?.title || t('hero.hotelName'));
const heroSubtitle = useTranslations ? t('hero.subtitle') : (content?.hero?.subtitle || t('hero.subtitle'));
const heroDescription = useTranslations ? '' : (content?.hero?.description || '');
const heroImage = content?.hero?.backgroundImage || '/images/hero.jpg';
const heroCTAText = useTranslations ? t('hero.exploreRooms') : (content?.hero?.ctaText || t('hero.exploreRooms'));
const heroCTALink = content?.hero?.ctaLink || '/rooms';
// Get welcome section from content
const welcomeSection = content?.sections?.find(s => s.sectionId === 'welcome') || {};
const welcomeTitle = useTranslations ? t('home.welcomeTitle') : (welcomeSection.title || t('home.welcomeTitle'));
const welcomeSubtitle = useTranslations ? t('home.welcomeSubtitle') : (welcomeSection.subtitle || t('home.welcomeSubtitle'));
const welcomeContent = useTranslations ? t('home.welcomeDescription') : (welcomeSection.content || t('home.welcomeDescription'));
return ( return (
<> <>
<Helmet> <Helmet>
<title>{content?.seo?.title || 'The Old Vine Hotel - Luxury Accommodation & Premium Hospitality'}</title> <title>{content?.seo?.title || 'The Old Vine Hotel - Luxury Accommodation & Premium Hospitality'}</title>
<meta name="description" content={content?.seo?.description || 'Experience luxury and elegance at The Old Vine Hotel.'} /> <meta
<meta name="keywords" content={content?.seo?.keywords?.join(', ') || 'luxury hotel, premium accommodation'} /> name="description"
content={content?.seo?.description || 'Experience luxury and elegance at The Old Vine Hotel.'}
/>
<meta
name="keywords"
content={content?.seo?.keywords?.join(', ') || 'luxury hotel, premium accommodation'}
/>
</Helmet> </Helmet>
{/* Hero Section - Logo and Image Only */} {/* Hero Section - Logo and Image Only */}
<Box <Box
sx={{ sx={{
minHeight: '100vh', minHeight: '100vh',
background: `linear-gradient(rgba(0,0,0,0.35), rgba(0,0,0,0.35)), url("${heroImage}") center/cover`, background: `linear-gradient(rgba(0,0,0,0.45), rgba(0,0,0,0.45)), url("${heroImage}") center/cover`,
display: 'flex', display: 'flex',
flexDirection: 'column',
justifyContent: 'flex-end',
alignItems: 'center', alignItems: 'center',
justifyContent: 'center',
color: 'white', color: 'white',
textAlign: 'center', textAlign: 'center',
position: 'relative', position: 'relative',
pb: { xs: 25, md: 80},
}} }}
> >
<Container maxWidth="lg"> <Container maxWidth="lg" sx={{ width: '100%' }}>
<motion.div <motion.div
initial={{ opacity: 0, y: 50 }} initial={{ opacity: 0, y: 30 }}
animate={{ opacity: 1, y: 0 }} animate={{ opacity: 1, y: 0 }}
transition={{ duration: 1, ease: 'easeOut' }} transition={{ duration: 1, ease: 'easeOut' }}
> >
{/* Logo Only */} <Box sx={{ display: 'flex', justifyContent: 'center', width: '100%' }}>
<Box <Box
sx={{ component="img"
display: 'flex',
justifyContent: 'center',
mb: 4,
}}
>
<img
src="/images/logo.png" src="/images/logo.png"
alt="Old Vine Hotel" alt="Old Vine Hotel"
style={{ sx={{
height: 'auto',
width: 'auto', width: 'auto',
maxHeight: '200px', maxWidth: '90%',
maxWidth: '100%', maxHeight: { xs: 140, md: 250 },
filter: 'drop-shadow(0 10px 24px rgba(0,0,0,0.65))',
}} }}
/> />
</Box> </Box>
</motion.div> </motion.div>
</Container> </Container>
{/* Scroll Indicator */}
<Box
sx={{
position: 'absolute',
bottom: 30,
left: '50%',
transform: 'translateX(-50%)',
display: 'flex',
flexDirection: 'column',
alignItems: 'center',
animation: 'bounce 2s infinite',
}}
>
<Typography variant="body2" sx={{ mb: 1, opacity: 0.8 }}>
Scroll Down
</Typography>
<Box
sx={{
width: 2,
height: 30,
backgroundColor: 'white',
opacity: 0.6,
borderRadius: 1,
}}
/>
</Box>
</Box> </Box>
{/* Welcome Section */} {/* Welcome Section */}
@@ -214,28 +178,11 @@ const Home = () => {
viewport={{ once: true }} viewport={{ once: true }}
> >
<Box textAlign="center" sx={{ mb: 6 }}> <Box textAlign="center" sx={{ mb: 6 }}>
<Typography <Typography variant="h2" component="h2" sx={{ mb: 3, color: 'primary.main', fontWeight: 400 }}>
variant="h2"
component="h2"
sx={{
mb: 3,
// Use theme h2 font (Calistoga Regular - not bold)
color: 'primary.main',
fontWeight: 400,
}}
>
{welcomeTitle} {welcomeTitle}
</Typography> </Typography>
<Typography <Typography variant="h5" component="p" sx={{ mb: 3, color: 'text.secondary', fontWeight: 300 }}>
variant="h5"
component="p"
sx={{
mb: 3,
color: 'text.secondary',
fontWeight: 300,
}}
>
{welcomeSubtitle} {welcomeSubtitle}
</Typography> </Typography>
@@ -246,7 +193,7 @@ const Home = () => {
mx: 'auto', mx: 'auto',
lineHeight: 1.8, lineHeight: 1.8,
fontSize: '1.1rem', fontSize: '1.1rem',
whiteSpace: 'pre-line', // Preserve line breaks whiteSpace: 'pre-line',
}} }}
> >
{welcomeContent} {welcomeContent}
@@ -264,68 +211,74 @@ const Home = () => {
transition={{ duration: 0.8 }} transition={{ duration: 0.8 }}
viewport={{ once: true }} viewport={{ once: true }}
> >
<Typography <Typography variant="h3" component="h2" textAlign="center" sx={{ mb: 6, color: 'primary.main' }}>
variant="h3"
component="h2"
textAlign="center"
sx={{
mb: 6,
color: 'primary.main',
}}
>
{t('home.featuresTitle')} {t('home.featuresTitle')}
</Typography> </Typography>
<Grid container spacing={4}> <Box
sx={{
display: 'grid',
gap: 4,
gridTemplateColumns: {
xs: '1fr',
sm: 'repeat(2, 1fr)',
md: 'repeat(5, 1fr)',
},
alignItems: 'stretch',
}}
>
{features.map((feature, index) => ( {features.map((feature, index) => (
<Grid item xs={12} sm={6} md={3} key={index}> <motion.div
<motion.div key={index}
initial={{ opacity: 0, y: 20 }} initial={{ opacity: 0, y: 20 }}
whileInView={{ opacity: 1, y: 0 }} whileInView={{ opacity: 1, y: 0 }}
transition={{ duration: 0.6, delay: index * 0.1 }} transition={{ duration: 0.6, delay: index * 0.08 }}
viewport={{ once: true }} viewport={{ once: true }}
>
<Card
sx={{
height: '100%',
display: 'flex',
flexDirection: 'column',
textAlign: 'center',
p: 3,
border: 'none',
boxShadow: 3,
transition: 'all 0.3s ease',
'&:hover': { transform: 'translateY(-8px)', boxShadow: 6 },
}}
> >
<Card <Box sx={{ display: 'flex', justifyContent: 'center', mb: 2, color: 'primary.main' }}>
{feature.icon}
</Box>
<Typography
variant="h6"
component="h3"
sx={{ sx={{
height: '100%', mb: 2,
textAlign: 'center', fontWeight: 600,
p: 3, lineHeight: 1.2,
border: 'none', minHeight: '2.4em',
boxShadow: 3,
'&:hover': {
transform: 'translateY(-8px)',
boxShadow: 6,
},
transition: 'all 0.3s ease',
}} }}
> >
<Box {feature.title}
sx={{ </Typography>
display: 'flex',
justifyContent: 'center',
mb: 2,
color: 'primary.main',
}}
>
{feature.icon}
</Box>
<Typography <Typography
variant="h6" variant="body2"
component="h3" color="text.secondary"
sx={{ mb: 2, fontWeight: 600 }} sx={{
> lineHeight: 1.6,
{feature.title} minHeight: '4.8em',
</Typography> }}
>
<Typography variant="body2" color="text.secondary"> {feature.description}
{feature.description} </Typography>
</Typography> </Card>
</Card> </motion.div>
</motion.div>
</Grid>
))} ))}
</Grid> </Box>
</motion.div> </motion.div>
</Container> </Container>
</Box> </Box>
@@ -339,26 +292,11 @@ const Home = () => {
viewport={{ once: true }} viewport={{ once: true }}
> >
<Box textAlign="center" sx={{ mb: 6 }}> <Box textAlign="center" sx={{ mb: 6 }}>
<Typography <Typography variant="h3" component="h2" sx={{ mb: 3, color: 'primary.main' }}>
variant="h3"
component="h2"
sx={{
mb: 3,
color: 'primary.main',
}}
>
{t('home.roomsTitle')} {t('home.roomsTitle')}
</Typography> </Typography>
<Typography <Typography variant="h6" component="p" sx={{ mb: 4, color: 'text.secondary', fontWeight: 300 }}>
variant="h6"
component="p"
sx={{
mb: 4,
color: 'text.secondary',
fontWeight: 300,
}}
>
{t('home.roomsSubtitle')} {t('home.roomsSubtitle')}
</Typography> </Typography>
</Box> </Box>
@@ -380,18 +318,12 @@ const Home = () => {
alt={room.name} alt={room.name}
sx={{ sx={{
transition: 'transform 0.3s ease', transition: 'transform 0.3s ease',
'&:hover': { '&:hover': { transform: 'scale(1.05)' },
transform: 'scale(1.05)',
},
}} }}
/> />
<CardContent sx={{ p: 3 }}> <CardContent sx={{ p: 3 }}>
<Typography <Typography variant="h5" component="h3" sx={{ mb: 2 }}>
variant="h5"
component="h3"
sx={{ mb: 2 }}
>
{room.name} {room.name}
</Typography> </Typography>
@@ -401,12 +333,7 @@ const Home = () => {
key={idx} key={idx}
label={feature} label={feature}
size="small" size="small"
sx={{ sx={{ mr: 1, mb: 1, backgroundColor: 'primary.main', color: 'white' }}
mr: 1,
mb: 1,
backgroundColor: 'primary.main',
color: 'white',
}}
/> />
))} ))}
</Box> </Box>
@@ -441,7 +368,6 @@ const Home = () => {
</Box> </Box>
</motion.div> </motion.div>
</Container> </Container>
</> </>
); );
}; };