update
This commit is contained in:
@@ -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>
|
||||
|
||||
@@ -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'
|
||||
},
|
||||
]
|
||||
},
|
||||
{
|
||||
|
||||
@@ -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,
|
||||
|
||||
Reference in New Issue
Block a user