This commit is contained in:
Iliyan Angelov
2025-12-05 17:43:03 +02:00
parent e1988fe37a
commit 13c91f95f4
51 changed files with 11933 additions and 289 deletions

View File

@@ -29,12 +29,17 @@ import CookiePreferencesLink from './CookiePreferencesLink';
import ChatWidget from '../../features/notifications/components/ChatWidget';
import pageContentService, { type PageContent } from '../../features/content/services/pageContentService';
import { useCompanySettings } from '../contexts/CompanySettingsContext';
import apiClient from '../services/apiClient';
const Footer: React.FC = () => {
const { settings } = useCompanySettings();
const [pageContent, setPageContent] = useState<PageContent | null>(null);
const [homePageContent, setHomePageContent] = useState<PageContent | null>(null);
const [enabledPages, setEnabledPages] = useState<Set<string>>(new Set());
const [apiError, setApiError] = useState(false);
const [newsletterEmail, setNewsletterEmail] = useState('');
const [newsletterSubmitting, setNewsletterSubmitting] = useState(false);
const [newsletterMessage, setNewsletterMessage] = useState<{ type: 'success' | 'error'; text: string } | null>(null);
useEffect(() => {
const fetchPageContent = async () => {
@@ -53,6 +58,32 @@ const Footer: React.FC = () => {
}
};
const fetchHomePageContent = async () => {
try {
const response = await pageContentService.getHomeContent();
if (response.status === 'success' && response.data?.page_content) {
let content = response.data.page_content;
// Parse sections_enabled if it's a string
if (typeof content.sections_enabled === 'string') {
try {
content.sections_enabled = JSON.parse(content.sections_enabled);
} catch (e) {
content.sections_enabled = {};
}
} else if (typeof content.sections_enabled !== 'object' || content.sections_enabled === null) {
content.sections_enabled = content.sections_enabled || {};
}
// Normalize boolean values
if (content.newsletter_enabled !== undefined) {
content.newsletter_enabled = Boolean(content.newsletter_enabled);
}
setHomePageContent(content);
}
} catch (err: any) {
console.error('Error fetching home content for newsletter:', err);
}
};
const checkEnabledPages = async () => {
const enabled = new Set<string>();
const policyPages = [
@@ -84,6 +115,7 @@ const Footer: React.FC = () => {
};
fetchPageContent();
fetchHomePageContent();
checkEnabledPages();
}, []);
@@ -174,9 +206,9 @@ const Footer: React.FC = () => {
<div className="relative container mx-auto px-4 sm:px-6 lg:px-8 py-16 sm:py-20 lg:py-24">
{/* Main Content Grid */}
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-5 gap-10 sm:gap-12 lg:gap-16 mb-16 sm:mb-20">
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-12 gap-8 sm:gap-10 lg:gap-8 mb-16 sm:mb-20">
{/* Brand Section */}
<div className="lg:col-span-2">
<div className="lg:col-span-4">
<div className="flex items-center space-x-4 mb-6 sm:mb-8">
{logoUrl ? (
<div className="relative group">
@@ -294,8 +326,8 @@ const Footer: React.FC = () => {
{/* Quick Links */}
{quickLinks.length > 0 && (
<div>
<h3 className="text-white font-elegant font-semibold text-lg sm:text-xl mb-6 sm:mb-8 relative inline-block tracking-wide">
<div className="lg:col-span-2">
<h3 className="text-white font-elegant font-semibold text-lg sm:text-xl mb-4 sm:mb-6 relative inline-block tracking-wide">
<span className="relative z-10">Quick Links</span>
<span className="absolute bottom-0 left-0 w-full h-[2px] bg-gradient-to-r from-[#d4af37] via-[#d4af37]/50 to-transparent"></span>
</h3>
@@ -317,8 +349,8 @@ const Footer: React.FC = () => {
{/* Guest Services */}
{supportLinks.length > 0 && (
<div>
<h3 className="text-white font-elegant font-semibold text-lg sm:text-xl mb-6 sm:mb-8 relative inline-block tracking-wide">
<div className="lg:col-span-2">
<h3 className="text-white font-elegant font-semibold text-lg sm:text-xl mb-4 sm:mb-6 relative inline-block tracking-wide">
<span className="relative z-10">Guest Services</span>
<span className="absolute bottom-0 left-0 w-full h-[2px] bg-gradient-to-r from-[#d4af37] via-[#d4af37]/50 to-transparent"></span>
</h3>
@@ -338,9 +370,78 @@ const Footer: React.FC = () => {
</div>
)}
{/* Contact Information */}
<div>
{/* Newsletter Subscription - Always Enabled */}
<div className="lg:col-span-2">
<h3 className="text-white font-elegant font-semibold text-lg sm:text-xl mb-6 sm:mb-8 relative inline-block tracking-wide">
<span className="relative z-10">{homePageContent?.newsletter_section_title || 'Newsletter'}</span>
<span className="absolute bottom-0 left-0 w-full h-[2px] bg-gradient-to-r from-[#d4af37] via-[#d4af37]/50 to-transparent"></span>
</h3>
{homePageContent?.newsletter_section_subtitle && (
<p className="text-sm text-gray-400 mb-4 font-light leading-relaxed">
{homePageContent.newsletter_section_subtitle}
</p>
)}
<form
className="space-y-3"
onSubmit={async (e) => {
e.preventDefault();
if (!newsletterEmail || newsletterSubmitting) return;
setNewsletterSubmitting(true);
setNewsletterMessage(null);
try {
const response = await apiClient.post('/email-campaigns/newsletter/subscribe', {
email: newsletterEmail
});
if (response.data?.status === 'success') {
setNewsletterMessage({ type: 'success', text: 'Successfully subscribed!' });
setNewsletterEmail('');
setTimeout(() => setNewsletterMessage(null), 5000);
} else {
setNewsletterMessage({ type: 'error', text: 'Failed to subscribe. Please try again.' });
}
} catch (error: any) {
console.error('Newsletter subscription error:', error);
const errorMessage = error.response?.data?.detail || error.message || 'Failed to subscribe. Please try again.';
setNewsletterMessage({ type: 'error', text: errorMessage });
} finally {
setNewsletterSubmitting(false);
}
}}
>
<input
type="email"
value={newsletterEmail}
onChange={(e) => setNewsletterEmail(e.target.value)}
placeholder={homePageContent?.newsletter_placeholder || 'Enter your email'}
className="w-full px-4 py-2.5 rounded-lg border border-gray-700 bg-gray-800/50 text-white placeholder-gray-400 focus:border-[#d4af37] focus:ring-2 focus:ring-[#d4af37]/20 transition-all disabled:opacity-50 disabled:cursor-not-allowed text-sm"
required
disabled={newsletterSubmitting}
/>
<button
type="submit"
disabled={newsletterSubmitting || !newsletterEmail}
className="w-full px-4 py-2.5 bg-gradient-to-r from-[#d4af37] to-[#c9a227] text-[#0f0f0f] rounded-lg font-semibold hover:from-[#f5d76e] hover:to-[#d4af37] transition-all duration-300 shadow-lg shadow-[#d4af37]/30 hover:shadow-xl hover:shadow-[#d4af37]/40 disabled:opacity-50 disabled:cursor-not-allowed text-sm"
>
{newsletterSubmitting ? 'Subscribing...' : (homePageContent?.newsletter_button_text || 'Subscribe')}
</button>
{newsletterMessage && (
<div className={`text-xs px-3 py-2 rounded-lg ${
newsletterMessage.type === 'success'
? 'bg-green-500/20 text-green-300 border border-green-500/30'
: 'bg-red-500/20 text-red-300 border border-red-500/30'
}`}>
{newsletterMessage.text}
</div>
)}
</form>
</div>
{/* Contact Information */}
<div className="lg:col-span-2">
<h3 className="text-white font-elegant font-semibold text-lg sm:text-xl mb-4 sm:mb-6 relative inline-block tracking-wide">
<span className="relative z-10">Contact</span>
<span className="absolute bottom-0 left-0 w-full h-[2px] bg-gradient-to-r from-[#d4af37] via-[#d4af37]/50 to-transparent"></span>
</h3>

View File

@@ -36,7 +36,9 @@ import {
Webhook,
Key,
HardDrive,
Activity
Activity,
Calendar,
Boxes
} from 'lucide-react';
import useAuthStore from '../../store/useAuthStore';
import { useResponsive } from '../../hooks';
@@ -136,6 +138,16 @@ const SidebarAdmin: React.FC<SidebarAdminProps> = ({
icon: Hotel,
label: 'Room Management'
},
{
path: '/admin/inventory',
icon: Boxes,
label: 'Inventory'
},
{
path: '/admin/shifts',
icon: Calendar,
label: 'Staff Shifts'
},
]
},
{

View File

@@ -18,7 +18,9 @@ import {
Bell,
Mail,
AlertTriangle,
TrendingUp
TrendingUp,
Package,
Calendar
} from 'lucide-react';
import useAuthStore from '../../store/useAuthStore';
import { useChatNotifications } from '../../features/notifications/contexts/ChatNotificationContext';
@@ -106,11 +108,6 @@ const SidebarStaff: React.FC<SidebarStaffProps> = ({
icon: CreditCard,
label: 'Payments'
},
{
path: '/staff/loyalty',
icon: Award,
label: 'Loyalty Program'
},
{
path: '/staff/guest-profiles',
icon: Users,
@@ -141,6 +138,16 @@ const SidebarStaff: React.FC<SidebarStaffProps> = ({
icon: Wrench,
label: 'Room Management'
},
{
path: '/staff/inventory',
icon: Package,
label: 'Inventory'
},
{
path: '/staff/shifts',
icon: Calendar,
label: 'My Shifts'
},
{
path: '/staff/chats',
icon: MessageCircle,