updates
This commit is contained in:
@@ -6,8 +6,29 @@ import pageContentService, { PageContent } from '../services/pageContentService'
|
||||
import serviceService, { Service } from '../../hotel_services/services/serviceService';
|
||||
import Loading from '../../../shared/components/Loading';
|
||||
import { useFormatCurrency } from '../../payments/hooks/useFormatCurrency';
|
||||
import { useTheme } from '../../../shared/contexts/ThemeContext';
|
||||
import { getThemeBackgroundClasses, getThemeHeroBackgroundClasses, getThemeTextClasses, getThemeCardClasses, getThemeInputClasses } from '../../../shared/utils/themeUtils';
|
||||
|
||||
// Helper function to get icon component from icon name (handles both PascalCase and lowercase)
|
||||
const getIconComponent = (iconName?: string, fallback: any = Award) => {
|
||||
if (!iconName) return fallback;
|
||||
|
||||
// Try direct match first (for PascalCase names)
|
||||
if ((LucideIcons as any)[iconName]) {
|
||||
return (LucideIcons as any)[iconName];
|
||||
}
|
||||
|
||||
// Convert to PascalCase (capitalize first letter)
|
||||
const pascalCaseName = iconName.charAt(0).toUpperCase() + iconName.slice(1).toLowerCase();
|
||||
if ((LucideIcons as any)[pascalCaseName]) {
|
||||
return (LucideIcons as any)[pascalCaseName];
|
||||
}
|
||||
|
||||
return fallback;
|
||||
};
|
||||
|
||||
const ServicesPage: React.FC = () => {
|
||||
const { theme } = useTheme();
|
||||
const [pageContent, setPageContent] = useState<PageContent | null>(null);
|
||||
const [hotelServices, setHotelServices] = useState<Service[]>([]);
|
||||
const [loading, setLoading] = useState(true);
|
||||
@@ -156,10 +177,16 @@ const ServicesPage: React.FC = () => {
|
||||
return <Loading />;
|
||||
}
|
||||
|
||||
const bgClasses = getThemeBackgroundClasses(theme.theme_layout_mode);
|
||||
const heroBgClasses = getThemeHeroBackgroundClasses(theme.theme_layout_mode);
|
||||
const textClasses = getThemeTextClasses(theme.theme_layout_mode);
|
||||
const cardClasses = getThemeCardClasses(theme.theme_layout_mode);
|
||||
const inputClasses = getThemeInputClasses(theme.theme_layout_mode);
|
||||
|
||||
return (
|
||||
<div className="min-h-screen bg-gradient-to-b from-[#0f0f0f] via-[#1a1a1a] to-[#0f0f0f] w-full" style={{ width: '100vw', position: 'relative', left: '50%', right: '50%', marginLeft: '-50vw', marginRight: '-50vw', marginTop: '-1.5rem', marginBottom: '-1.5rem' }}>
|
||||
<div className={`min-h-screen ${bgClasses} w-full`} style={{ width: '100vw', position: 'relative', left: '50%', right: '50%', marginLeft: '-50vw', marginRight: '-50vw', marginTop: '-1.5rem', marginBottom: '-1.5rem' }}>
|
||||
{/* Hero Section */}
|
||||
<div className="w-full bg-gradient-to-br from-[#1a1a1a] via-[#0f0f0f] to-[#1a1a1a] border-b border-[var(--luxury-gold)]/10 pt-6 sm:pt-7 md:pt-8 overflow-hidden relative">
|
||||
<div className={`w-full ${heroBgClasses} pt-6 sm:pt-7 md:pt-8 overflow-hidden relative`}>
|
||||
{/* Background Effects */}
|
||||
<div className="absolute inset-0 opacity-10">
|
||||
<div className="absolute top-10 left-10 w-32 sm:w-48 h-32 sm:h-48 bg-[var(--luxury-gold)] rounded-full blur-3xl"></div>
|
||||
@@ -172,18 +199,20 @@ const ServicesPage: React.FC = () => {
|
||||
<div className="flex justify-center mb-2 sm:mb-3 md:mb-4">
|
||||
<div className="relative group">
|
||||
<div className="absolute inset-0 bg-gradient-to-br from-[var(--luxury-gold)] via-[var(--luxury-gold-light)] to-[var(--luxury-gold-dark)] rounded-xl blur-lg opacity-40 group-hover:opacity-60 transition-opacity duration-500"></div>
|
||||
<div className="relative p-2 sm:p-2.5 md:p-3 bg-gradient-to-br from-[#1a1a1a] to-[#0a0a0a] rounded-lg border-2 border-[var(--luxury-gold)]/40 backdrop-blur-sm shadow-xl shadow-[var(--luxury-gold)]/20 group-hover:border-[var(--luxury-gold)]/60 transition-all duration-300">
|
||||
<div className={`relative p-2 sm:p-2.5 md:p-3 ${cardClasses} rounded-lg border-2 border-[var(--luxury-gold)]/40 backdrop-blur-sm shadow-xl shadow-[var(--luxury-gold)]/20 group-hover:border-[var(--luxury-gold)]/60 transition-all duration-300`}>
|
||||
<Award className="w-5 h-5 sm:w-6 sm:h-6 md:w-7 md:h-7 text-[var(--luxury-gold)] drop-shadow-lg" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<h1 className="text-2xl xs:text-3xl sm:text-4xl md:text-5xl font-serif font-semibold mb-2 sm:mb-3 tracking-tight leading-tight px-2">
|
||||
<span className="bg-gradient-to-r from-white via-[var(--luxury-gold)] to-white bg-clip-text text-transparent">
|
||||
<span className={`${theme.theme_layout_mode === 'light'
|
||||
? 'bg-gradient-to-r from-gray-900 via-[var(--luxury-gold)] to-gray-900'
|
||||
: 'bg-gradient-to-r from-white via-[var(--luxury-gold)] to-white'} bg-clip-text text-transparent`}>
|
||||
{pageContent?.luxury_services_section_title || 'Our Services'}
|
||||
</span>
|
||||
</h1>
|
||||
<div className="w-12 sm:w-16 md:w-20 h-0.5 bg-gradient-to-r from-transparent via-[var(--luxury-gold)] to-transparent mx-auto mb-2 sm:mb-3"></div>
|
||||
<p className="text-sm sm:text-base md:text-lg text-gray-300 font-light leading-relaxed max-w-xl mx-auto tracking-wide px-2 sm:px-4">
|
||||
<p className={`text-sm sm:text-base md:text-lg ${textClasses.secondary} font-light leading-relaxed max-w-xl mx-auto tracking-wide px-2 sm:px-4`}>
|
||||
{pageContent?.luxury_services_section_subtitle || 'Discover our premium services designed to enhance your stay'}
|
||||
</p>
|
||||
<div className="mt-4 flex items-center justify-center gap-2 text-[var(--luxury-gold)]/60">
|
||||
@@ -211,7 +240,7 @@ const ServicesPage: React.FC = () => {
|
||||
placeholder="Search services..."
|
||||
value={searchTerm}
|
||||
onChange={(e) => setSearchTerm(e.target.value)}
|
||||
className="w-full pl-14 pr-5 py-4 bg-gradient-to-br from-[#1a1a1a] to-[#0f0f0f] border border-[var(--luxury-gold)]/20 rounded-xl text-white placeholder-gray-500 focus:outline-none focus:ring-2 focus:ring-[var(--luxury-gold)]/50 focus:border-[var(--luxury-gold)]/50 transition-all duration-300 backdrop-blur-sm font-light"
|
||||
className={`w-full pl-14 pr-5 py-4 ${inputClasses} border border-[var(--luxury-gold)]/20 rounded-xl placeholder-gray-500 focus:outline-none focus:ring-2 focus:ring-[var(--luxury-gold)]/50 focus:border-[var(--luxury-gold)]/50 transition-all duration-300 backdrop-blur-sm font-light`}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
@@ -221,13 +250,13 @@ const ServicesPage: React.FC = () => {
|
||||
{/* Categories Filter - Top Center */}
|
||||
{allCategories.length > 0 && (
|
||||
<div className="mb-12 flex justify-center">
|
||||
<div className="inline-flex flex-wrap items-center gap-3 bg-gradient-to-br from-[#1a1a1a] via-[#0f0f0f] to-[#0a0a0a] rounded-2xl border-2 border-[var(--luxury-gold)]/20 p-4 backdrop-blur-xl shadow-2xl">
|
||||
<div className={`inline-flex flex-wrap items-center gap-3 ${cardClasses} rounded-2xl border-2 border-[var(--luxury-gold)]/20 p-4 backdrop-blur-xl shadow-2xl`}>
|
||||
<button
|
||||
onClick={() => setSelectedCategory(null)}
|
||||
className={`group relative px-6 py-3 rounded-xl text-sm font-medium transition-all duration-300 overflow-hidden ${
|
||||
selectedCategory === null
|
||||
? 'bg-gradient-to-r from-[var(--luxury-gold)] to-[var(--luxury-gold-dark)] text-[#0f0f0f] shadow-lg shadow-[var(--luxury-gold)]/40'
|
||||
: 'bg-gradient-to-br from-[#0f0f0f] to-[#0a0a0a] text-gray-300 border-2 border-[var(--luxury-gold)]/20 hover:border-[var(--luxury-gold)]/50 hover:text-[var(--luxury-gold)]'
|
||||
: `${cardClasses} ${textClasses.secondary} border-2 border-[var(--luxury-gold)]/20 hover:border-[var(--luxury-gold)]/50 hover:text-[var(--luxury-gold)] ${theme.theme_layout_mode === 'light' ? 'hover:bg-gray-100' : 'hover:bg-[#1a1a1a]'}`
|
||||
}`}
|
||||
>
|
||||
<span className="relative z-10 flex items-center gap-2">
|
||||
@@ -242,7 +271,7 @@ const ServicesPage: React.FC = () => {
|
||||
className={`group relative px-6 py-3 rounded-xl text-sm font-medium transition-all duration-300 overflow-hidden ${
|
||||
selectedCategory === category
|
||||
? 'bg-gradient-to-r from-[var(--luxury-gold)] to-[var(--luxury-gold-dark)] text-[#0f0f0f] shadow-lg shadow-[var(--luxury-gold)]/40'
|
||||
: 'bg-gradient-to-br from-[#0f0f0f] to-[#0a0a0a] text-gray-300 border-2 border-[var(--luxury-gold)]/20 hover:border-[var(--luxury-gold)]/50 hover:text-[var(--luxury-gold)]'
|
||||
: `${cardClasses} ${textClasses.secondary} border-2 border-[var(--luxury-gold)]/20 hover:border-[var(--luxury-gold)]/50 hover:text-[var(--luxury-gold)] ${theme.theme_layout_mode === 'light' ? 'hover:bg-gray-100' : 'hover:bg-[#1a1a1a]'}`
|
||||
}`}
|
||||
>
|
||||
<span className="relative z-10 flex items-center gap-2">
|
||||
@@ -259,7 +288,7 @@ const ServicesPage: React.FC = () => {
|
||||
<div className="flex flex-col items-center">
|
||||
{/* Results Count */}
|
||||
{!loading && filteredServices.length > 0 && (
|
||||
<div className="mb-8 text-gray-400 font-light text-sm text-center">
|
||||
<div className={`mb-8 ${textClasses.muted} font-light text-sm text-center`}>
|
||||
Showing {filteredServices.length} of {allServices.length} services
|
||||
</div>
|
||||
)}
|
||||
@@ -270,8 +299,8 @@ const ServicesPage: React.FC = () => {
|
||||
<div className="inline-flex items-center justify-center w-20 h-20 rounded-full bg-[var(--luxury-gold)]/10 mb-6">
|
||||
<Award className="w-10 h-10 text-[var(--luxury-gold)]" />
|
||||
</div>
|
||||
<p className="text-gray-400 text-xl font-light">No services found</p>
|
||||
<p className="text-gray-500 text-sm mt-2">Try adjusting your search or filters</p>
|
||||
<p className={`${textClasses.muted} text-xl font-light`}>No services found</p>
|
||||
<p className={`${textClasses.muted} text-sm mt-2`}>Try adjusting your search or filters</p>
|
||||
</div>
|
||||
) : (
|
||||
<>
|
||||
@@ -288,7 +317,7 @@ const ServicesPage: React.FC = () => {
|
||||
<Link
|
||||
key={service.id}
|
||||
to={`/services/${serviceSlug}`}
|
||||
className="group relative bg-gradient-to-br from-[#1a1a1a] via-[#0f0f0f] to-[#0a0a0a] rounded-3xl border-2 border-[var(--luxury-gold)]/20 overflow-hidden hover:border-[var(--luxury-gold)]/60 transition-all duration-700 hover:shadow-2xl hover:shadow-[var(--luxury-gold)]/30 hover:-translate-y-3 block"
|
||||
className={`group relative ${cardClasses} rounded-3xl border-2 border-[var(--luxury-gold)]/20 overflow-hidden hover:border-[var(--luxury-gold)]/60 transition-all duration-700 hover:shadow-2xl hover:shadow-[var(--luxury-gold)]/30 hover:-translate-y-3 block`}
|
||||
style={{ animationDelay: `${index * 50}ms` }}
|
||||
>
|
||||
{/* Premium Glow Effects */}
|
||||
@@ -320,7 +349,7 @@ const ServicesPage: React.FC = () => {
|
||||
<div className="absolute top-0 right-0 w-32 h-32 bg-gradient-to-br from-[var(--luxury-gold)]/20 to-transparent rounded-bl-full opacity-0 group-hover:opacity-100 transition-opacity duration-500"></div>
|
||||
</div>
|
||||
) : (
|
||||
<div className="h-48 sm:h-56 bg-gradient-to-br from-[#1a1a1a] to-[#0f0f0f] flex items-center justify-center p-8">
|
||||
<div className={`h-48 sm:h-56 ${cardClasses} flex items-center justify-center p-8`}>
|
||||
{service.icon && (LucideIcons as any)[service.icon] ? (
|
||||
<div className="relative">
|
||||
<div className="absolute inset-0 bg-[var(--luxury-gold)]/20 rounded-full blur-2xl"></div>
|
||||
@@ -331,7 +360,12 @@ const ServicesPage: React.FC = () => {
|
||||
) : (
|
||||
<div className="relative">
|
||||
<div className="absolute inset-0 bg-[var(--luxury-gold)]/20 rounded-full blur-2xl"></div>
|
||||
<Award className="w-16 h-16 sm:w-20 sm:h-20 text-[var(--luxury-gold)] relative z-10 drop-shadow-lg" />
|
||||
{React.createElement(
|
||||
getIconComponent(pageContent?.services_fallback_icon, Award),
|
||||
{
|
||||
className: 'w-16 h-16 sm:w-20 sm:h-20 text-[var(--luxury-gold)] relative z-10 drop-shadow-lg'
|
||||
}
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
@@ -345,11 +379,11 @@ const ServicesPage: React.FC = () => {
|
||||
</span>
|
||||
</div>
|
||||
)}
|
||||
<h2 className="text-2xl sm:text-3xl font-serif font-bold text-white mb-4 group-hover:text-[var(--luxury-gold)] transition-colors duration-500 line-clamp-2 leading-tight tracking-tight">
|
||||
<h2 className={`text-2xl sm:text-3xl font-serif font-bold ${textClasses.primary} mb-4 group-hover:text-[var(--luxury-gold)] transition-colors duration-500 line-clamp-2 leading-tight tracking-tight`}>
|
||||
{service.title}
|
||||
</h2>
|
||||
{service.description && (
|
||||
<p className="text-gray-300 text-base mb-6 line-clamp-3 font-light leading-relaxed">
|
||||
<p className={`${textClasses.secondary} text-base mb-6 line-clamp-3 font-light leading-relaxed`}>
|
||||
{service.description}
|
||||
</p>
|
||||
)}
|
||||
@@ -360,7 +394,7 @@ const ServicesPage: React.FC = () => {
|
||||
{formatCurrency(service.price)}
|
||||
</span>
|
||||
{service.unit && (
|
||||
<span className="text-sm text-gray-400 font-light">
|
||||
<span className={`text-sm ${textClasses.muted} font-light`}>
|
||||
/ {service.unit}
|
||||
</span>
|
||||
)}
|
||||
|
||||
Reference in New Issue
Block a user