updates
This commit is contained in:
Binary file not shown.
@@ -1846,6 +1846,180 @@ async def upload_company_favicon(
|
|||||||
logger.error(f"Error uploading favicon: {e}", exc_info=True)
|
logger.error(f"Error uploading favicon: {e}", exc_info=True)
|
||||||
raise HTTPException(status_code=500, detail=str(e))
|
raise HTTPException(status_code=500, detail=str(e))
|
||||||
|
|
||||||
|
class UpdateThemeSettingsRequest(BaseModel):
|
||||||
|
theme_primary_color: Optional[str] = None
|
||||||
|
theme_primary_light: Optional[str] = None
|
||||||
|
theme_primary_dark: Optional[str] = None
|
||||||
|
theme_primary_accent: Optional[str] = None
|
||||||
|
|
||||||
|
@router.get("/theme")
|
||||||
|
async def get_theme_settings(
|
||||||
|
db: Session = Depends(get_db)
|
||||||
|
):
|
||||||
|
"""Get current theme color settings"""
|
||||||
|
try:
|
||||||
|
setting_keys = [
|
||||||
|
"theme_primary_color",
|
||||||
|
"theme_primary_light",
|
||||||
|
"theme_primary_dark",
|
||||||
|
"theme_primary_accent",
|
||||||
|
]
|
||||||
|
|
||||||
|
settings_dict = {}
|
||||||
|
for key in setting_keys:
|
||||||
|
setting = db.query(SystemSettings).filter(
|
||||||
|
SystemSettings.key == key
|
||||||
|
).first()
|
||||||
|
if setting:
|
||||||
|
settings_dict[key] = setting.value
|
||||||
|
else:
|
||||||
|
settings_dict[key] = None
|
||||||
|
|
||||||
|
# Get updated_at and updated_by from any theme setting
|
||||||
|
theme_setting = db.query(SystemSettings).filter(
|
||||||
|
SystemSettings.key == "theme_primary_color"
|
||||||
|
).first()
|
||||||
|
|
||||||
|
updated_at = None
|
||||||
|
updated_by = None
|
||||||
|
if theme_setting:
|
||||||
|
updated_at = theme_setting.updated_at.isoformat() if theme_setting.updated_at else None
|
||||||
|
updated_by = theme_setting.updated_by.full_name if theme_setting.updated_by else None
|
||||||
|
|
||||||
|
return {
|
||||||
|
"status": "success",
|
||||||
|
"data": {
|
||||||
|
"theme_primary_color": settings_dict.get("theme_primary_color", "#d4af37"),
|
||||||
|
"theme_primary_light": settings_dict.get("theme_primary_light", "#f5d76e"),
|
||||||
|
"theme_primary_dark": settings_dict.get("theme_primary_dark", "#c9a227"),
|
||||||
|
"theme_primary_accent": settings_dict.get("theme_primary_accent", "#e8c547"),
|
||||||
|
"updated_at": updated_at,
|
||||||
|
"updated_by": updated_by,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
except Exception as e:
|
||||||
|
logger.error(f"Error getting theme settings: {e}", exc_info=True)
|
||||||
|
raise HTTPException(status_code=500, detail=str(e))
|
||||||
|
|
||||||
|
@router.put("/theme")
|
||||||
|
async def update_theme_settings(
|
||||||
|
request_data: UpdateThemeSettingsRequest,
|
||||||
|
request: Request,
|
||||||
|
current_user: User = Depends(authorize_roles("admin")),
|
||||||
|
db: Session = Depends(get_db)
|
||||||
|
):
|
||||||
|
"""Update theme color settings (admin only)"""
|
||||||
|
import re
|
||||||
|
|
||||||
|
client_ip = request.client.host if request.client else None
|
||||||
|
user_agent = request.headers.get('User-Agent')
|
||||||
|
request_id = getattr(request.state, 'request_id', None)
|
||||||
|
|
||||||
|
try:
|
||||||
|
# Validate hex color format
|
||||||
|
hex_color_pattern = re.compile(r'^#([A-Fa-f0-9]{6}|[A-Fa-f0-9]{3})$')
|
||||||
|
|
||||||
|
db_settings = {}
|
||||||
|
|
||||||
|
if request_data.theme_primary_color is not None:
|
||||||
|
if not hex_color_pattern.match(request_data.theme_primary_color):
|
||||||
|
raise HTTPException(
|
||||||
|
status_code=400,
|
||||||
|
detail="Invalid theme_primary_color format. Must be a valid hex color (e.g., #d4af37)"
|
||||||
|
)
|
||||||
|
db_settings["theme_primary_color"] = request_data.theme_primary_color
|
||||||
|
|
||||||
|
if request_data.theme_primary_light is not None:
|
||||||
|
if not hex_color_pattern.match(request_data.theme_primary_light):
|
||||||
|
raise HTTPException(
|
||||||
|
status_code=400,
|
||||||
|
detail="Invalid theme_primary_light format. Must be a valid hex color (e.g., #f5d76e)"
|
||||||
|
)
|
||||||
|
db_settings["theme_primary_light"] = request_data.theme_primary_light
|
||||||
|
|
||||||
|
if request_data.theme_primary_dark is not None:
|
||||||
|
if not hex_color_pattern.match(request_data.theme_primary_dark):
|
||||||
|
raise HTTPException(
|
||||||
|
status_code=400,
|
||||||
|
detail="Invalid theme_primary_dark format. Must be a valid hex color (e.g., #c9a227)"
|
||||||
|
)
|
||||||
|
db_settings["theme_primary_dark"] = request_data.theme_primary_dark
|
||||||
|
|
||||||
|
if request_data.theme_primary_accent is not None:
|
||||||
|
if not hex_color_pattern.match(request_data.theme_primary_accent):
|
||||||
|
raise HTTPException(
|
||||||
|
status_code=400,
|
||||||
|
detail="Invalid theme_primary_accent format. Must be a valid hex color (e.g., #e8c547)"
|
||||||
|
)
|
||||||
|
db_settings["theme_primary_accent"] = request_data.theme_primary_accent
|
||||||
|
|
||||||
|
# Update or create settings
|
||||||
|
for key, value in db_settings.items():
|
||||||
|
setting = db.query(SystemSettings).filter(
|
||||||
|
SystemSettings.key == key
|
||||||
|
).first()
|
||||||
|
|
||||||
|
if setting:
|
||||||
|
setting.value = value
|
||||||
|
setting.updated_at = datetime.utcnow()
|
||||||
|
setting.updated_by_id = current_user.id
|
||||||
|
else:
|
||||||
|
setting = SystemSettings(
|
||||||
|
key=key,
|
||||||
|
value=value,
|
||||||
|
updated_by_id=current_user.id
|
||||||
|
)
|
||||||
|
db.add(setting)
|
||||||
|
|
||||||
|
db.commit()
|
||||||
|
|
||||||
|
# Get updated settings
|
||||||
|
updated_settings = {}
|
||||||
|
for key in ["theme_primary_color", "theme_primary_light", "theme_primary_dark", "theme_primary_accent"]:
|
||||||
|
setting = db.query(SystemSettings).filter(
|
||||||
|
SystemSettings.key == key
|
||||||
|
).first()
|
||||||
|
if setting:
|
||||||
|
updated_settings[key] = setting.value
|
||||||
|
else:
|
||||||
|
# Return defaults if not set
|
||||||
|
defaults = {
|
||||||
|
"theme_primary_color": "#d4af37",
|
||||||
|
"theme_primary_light": "#f5d76e",
|
||||||
|
"theme_primary_dark": "#c9a227",
|
||||||
|
"theme_primary_accent": "#e8c547",
|
||||||
|
}
|
||||||
|
updated_settings[key] = defaults.get(key)
|
||||||
|
|
||||||
|
theme_setting = db.query(SystemSettings).filter(
|
||||||
|
SystemSettings.key == "theme_primary_color"
|
||||||
|
).first()
|
||||||
|
|
||||||
|
updated_at = None
|
||||||
|
updated_by = None
|
||||||
|
if theme_setting:
|
||||||
|
updated_at = theme_setting.updated_at.isoformat() if theme_setting.updated_at else None
|
||||||
|
updated_by = theme_setting.updated_by.full_name if theme_setting.updated_by else None
|
||||||
|
|
||||||
|
return {
|
||||||
|
"status": "success",
|
||||||
|
"message": "Theme settings updated successfully",
|
||||||
|
"data": {
|
||||||
|
"theme_primary_color": updated_settings.get("theme_primary_color", "#d4af37"),
|
||||||
|
"theme_primary_light": updated_settings.get("theme_primary_light", "#f5d76e"),
|
||||||
|
"theme_primary_dark": updated_settings.get("theme_primary_dark", "#c9a227"),
|
||||||
|
"theme_primary_accent": updated_settings.get("theme_primary_accent", "#e8c547"),
|
||||||
|
"updated_at": updated_at,
|
||||||
|
"updated_by": updated_by,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
except HTTPException:
|
||||||
|
raise
|
||||||
|
except Exception as e:
|
||||||
|
db.rollback()
|
||||||
|
logger.error(f"Error updating theme settings: {e}", exc_info=True)
|
||||||
|
raise HTTPException(status_code=500, detail=str(e))
|
||||||
|
|
||||||
@router.get("/recaptcha")
|
@router.get("/recaptcha")
|
||||||
async def get_recaptcha_settings(
|
async def get_recaptcha_settings(
|
||||||
db: Session = Depends(get_db)
|
db: Session = Depends(get_db)
|
||||||
|
|||||||
@@ -12,6 +12,7 @@ import { LoadingProvider, useNavigationLoading, useLoading } from './shared/cont
|
|||||||
import { CookieConsentProvider } from './shared/contexts/CookieConsentContext';
|
import { CookieConsentProvider } from './shared/contexts/CookieConsentContext';
|
||||||
import { CurrencyProvider } from './features/payments/contexts/CurrencyContext';
|
import { CurrencyProvider } from './features/payments/contexts/CurrencyContext';
|
||||||
import { CompanySettingsProvider } from './shared/contexts/CompanySettingsContext';
|
import { CompanySettingsProvider } from './shared/contexts/CompanySettingsContext';
|
||||||
|
import { ThemeProvider } from './shared/contexts/ThemeContext';
|
||||||
import { AuthModalProvider } from './features/auth/contexts/AuthModalContext';
|
import { AuthModalProvider } from './features/auth/contexts/AuthModalContext';
|
||||||
import { AntibotProvider } from './features/auth/contexts/AntibotContext';
|
import { AntibotProvider } from './features/auth/contexts/AntibotContext';
|
||||||
import { RoomProvider } from './features/rooms/contexts/RoomContext';
|
import { RoomProvider } from './features/rooms/contexts/RoomContext';
|
||||||
@@ -91,7 +92,7 @@ const GroupBookingManagementPage = lazy(() => import('./pages/admin/GroupBooking
|
|||||||
const AdminBookingManagementPage = lazy(() => import('./pages/admin/BookingManagementPage'));
|
const AdminBookingManagementPage = lazy(() => import('./pages/admin/BookingManagementPage'));
|
||||||
const PageContentDashboardPage = lazy(() => import('./pages/admin/PageContentDashboard'));
|
const PageContentDashboardPage = lazy(() => import('./pages/admin/PageContentDashboard'));
|
||||||
const AnalyticsDashboardPage = lazy(() => import('./pages/admin/AnalyticsDashboardPage'));
|
const AnalyticsDashboardPage = lazy(() => import('./pages/admin/AnalyticsDashboardPage'));
|
||||||
const BusinessDashboardPage = lazy(() => import('./pages/admin/BusinessDashboardPage'));
|
const PromotionsManagementPage = lazy(() => import('./pages/admin/PromotionsManagementPage'));
|
||||||
const SettingsPage = lazy(() => import('./pages/admin/SettingsPage'));
|
const SettingsPage = lazy(() => import('./pages/admin/SettingsPage'));
|
||||||
const TaskManagementPage = lazy(() => import('./pages/admin/TaskManagementPage'));
|
const TaskManagementPage = lazy(() => import('./pages/admin/TaskManagementPage'));
|
||||||
const WorkflowManagementPage = lazy(() => import('./pages/admin/WorkflowManagementPage'));
|
const WorkflowManagementPage = lazy(() => import('./pages/admin/WorkflowManagementPage'));
|
||||||
@@ -249,6 +250,7 @@ function App() {
|
|||||||
<LoadingProvider>
|
<LoadingProvider>
|
||||||
<CookieConsentProvider>
|
<CookieConsentProvider>
|
||||||
<CurrencyProvider>
|
<CurrencyProvider>
|
||||||
|
<ThemeProvider>
|
||||||
<CompanySettingsProvider>
|
<CompanySettingsProvider>
|
||||||
<AntibotProvider>
|
<AntibotProvider>
|
||||||
<AuthModalProvider>
|
<AuthModalProvider>
|
||||||
@@ -600,8 +602,8 @@ function App() {
|
|||||||
element={<UserManagementPage />}
|
element={<UserManagementPage />}
|
||||||
/>
|
/>
|
||||||
<Route
|
<Route
|
||||||
path="business"
|
path="promotions"
|
||||||
element={<BusinessDashboardPage />}
|
element={<PromotionsManagementPage />}
|
||||||
/>
|
/>
|
||||||
<Route
|
<Route
|
||||||
path="reception"
|
path="reception"
|
||||||
@@ -974,6 +976,7 @@ function App() {
|
|||||||
</AuthModalProvider>
|
</AuthModalProvider>
|
||||||
</AntibotProvider>
|
</AntibotProvider>
|
||||||
</CompanySettingsProvider>
|
</CompanySettingsProvider>
|
||||||
|
</ThemeProvider>
|
||||||
</CurrencyProvider>
|
</CurrencyProvider>
|
||||||
</CookieConsentProvider>
|
</CookieConsentProvider>
|
||||||
</LoadingProvider>
|
</LoadingProvider>
|
||||||
|
|||||||
@@ -93,7 +93,7 @@ const ForgotPasswordModal: React.FC = () => {
|
|||||||
<div className="absolute inset-0 bg-black/60 backdrop-blur-sm" />
|
<div className="absolute inset-0 bg-black/60 backdrop-blur-sm" />
|
||||||
|
|
||||||
{/* Modal */}
|
{/* Modal */}
|
||||||
<div className="relative w-full max-w-md max-h-[95vh] overflow-y-auto bg-gradient-to-br from-gray-50 via-white to-gray-50 rounded-lg shadow-2xl border border-[#d4af37]/20">
|
<div className="relative w-full max-w-md max-h-[95vh] overflow-y-auto bg-gradient-to-br from-gray-50 via-white to-gray-50 rounded-lg shadow-2xl border border-[var(--luxury-gold)]/20">
|
||||||
{/* Close button */}
|
{/* Close button */}
|
||||||
<button
|
<button
|
||||||
onClick={closeModal}
|
onClick={closeModal}
|
||||||
@@ -117,13 +117,13 @@ const ForgotPasswordModal: React.FC = () => {
|
|||||||
style={{ filter: 'drop-shadow(0 4px 6px rgba(0,0,0,0.1))' }}
|
style={{ filter: 'drop-shadow(0 4px 6px rgba(0,0,0,0.1))' }}
|
||||||
/>
|
/>
|
||||||
) : (
|
) : (
|
||||||
<div className="relative p-2.5 sm:p-3 bg-gradient-to-br from-[#d4af37] to-[#c9a227] rounded-full shadow-lg shadow-[#d4af37]/30">
|
<div className="relative p-2.5 sm:p-3 bg-gradient-to-br from-[var(--luxury-gold)] to-[var(--luxury-gold-dark)] rounded-full shadow-lg shadow-[var(--luxury-gold)]/30">
|
||||||
<Mail className="w-6 h-6 sm:w-8 sm:h-8 text-[#0f0f0f] relative z-10" />
|
<Mail className="w-6 h-6 sm:w-8 sm:h-8 text-[#0f0f0f] relative z-10" />
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
{settings.company_tagline && (
|
{settings.company_tagline && (
|
||||||
<p className="text-[10px] sm:text-xs text-[#d4af37] uppercase tracking-[1.5px] sm:tracking-[2px] mb-1 sm:mb-2 font-light">
|
<p className="text-[10px] sm:text-xs text-[var(--luxury-gold)] uppercase tracking-[1.5px] sm:tracking-[2px] mb-1 sm:mb-2 font-light">
|
||||||
{settings.company_tagline}
|
{settings.company_tagline}
|
||||||
</p>
|
</p>
|
||||||
)}
|
)}
|
||||||
@@ -150,7 +150,7 @@ const ForgotPasswordModal: React.FC = () => {
|
|||||||
<p className="text-xs sm:text-sm text-gray-600">
|
<p className="text-xs sm:text-sm text-gray-600">
|
||||||
We have sent a password reset link to
|
We have sent a password reset link to
|
||||||
</p>
|
</p>
|
||||||
<p className="text-xs sm:text-sm font-medium text-[#d4af37] break-all">
|
<p className="text-xs sm:text-sm font-medium text-[var(--luxury-gold)] break-all">
|
||||||
{submittedEmail}
|
{submittedEmail}
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
@@ -172,7 +172,7 @@ const ForgotPasswordModal: React.FC = () => {
|
|||||||
setIsSuccess(false);
|
setIsSuccess(false);
|
||||||
clearError();
|
clearError();
|
||||||
}}
|
}}
|
||||||
className="w-full flex items-center justify-center py-2.5 sm:py-3 px-4 border border-gray-300 rounded-lg text-xs sm:text-sm font-medium text-gray-700 bg-white hover:bg-gray-50 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-[#d4af37] transition-colors"
|
className="w-full flex items-center justify-center py-2.5 sm:py-3 px-4 border border-gray-300 rounded-lg text-xs sm:text-sm font-medium text-gray-700 bg-white hover:bg-gray-50 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-[var(--luxury-gold)] transition-colors"
|
||||||
>
|
>
|
||||||
<Mail className="-ml-1 mr-2 h-4 w-4 sm:h-5 sm:w-5" />
|
<Mail className="-ml-1 mr-2 h-4 w-4 sm:h-5 sm:w-5" />
|
||||||
Resend Email
|
Resend Email
|
||||||
@@ -180,7 +180,7 @@ const ForgotPasswordModal: React.FC = () => {
|
|||||||
|
|
||||||
<button
|
<button
|
||||||
onClick={() => openModal('login')}
|
onClick={() => openModal('login')}
|
||||||
className="w-full flex items-center justify-center py-2.5 sm:py-3 px-4 border border-transparent rounded-lg text-xs sm:text-sm font-medium text-white bg-gradient-to-r from-[#d4af37] to-[#c9a227] hover:from-[#f5d76e] hover:to-[#d4af37] focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-[#d4af37] transition-colors"
|
className="w-full flex items-center justify-center py-2.5 sm:py-3 px-4 border border-transparent rounded-lg text-xs sm:text-sm font-medium text-white bg-gradient-to-r from-[var(--luxury-gold)] to-[var(--luxury-gold-dark)] hover:from-[var(--luxury-gold-light)] hover:to-[var(--luxury-gold)] focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-[var(--luxury-gold)] transition-colors"
|
||||||
>
|
>
|
||||||
<ArrowLeft className="-ml-1 mr-2 h-4 w-4 sm:h-5 sm:w-5" />
|
<ArrowLeft className="-ml-1 mr-2 h-4 w-4 sm:h-5 sm:w-5" />
|
||||||
Back to Login
|
Back to Login
|
||||||
@@ -220,7 +220,7 @@ const ForgotPasswordModal: React.FC = () => {
|
|||||||
className={`block w-full pl-9 sm:pl-10 pr-3 py-2.5 sm:py-3 border rounded-lg focus:outline-none focus:ring-2 transition-colors text-sm sm:text-base luxury-input ${
|
className={`block w-full pl-9 sm:pl-10 pr-3 py-2.5 sm:py-3 border rounded-lg focus:outline-none focus:ring-2 transition-colors text-sm sm:text-base luxury-input ${
|
||||||
errors.email
|
errors.email
|
||||||
? 'border-red-300 focus:ring-red-500'
|
? 'border-red-300 focus:ring-red-500'
|
||||||
: 'border-gray-300 focus:ring-[#d4af37]'
|
: 'border-gray-300 focus:ring-[var(--luxury-gold)]'
|
||||||
}`}
|
}`}
|
||||||
placeholder="email@example.com"
|
placeholder="email@example.com"
|
||||||
/>
|
/>
|
||||||
@@ -235,7 +235,7 @@ const ForgotPasswordModal: React.FC = () => {
|
|||||||
<button
|
<button
|
||||||
type="submit"
|
type="submit"
|
||||||
disabled={isLoading}
|
disabled={isLoading}
|
||||||
className="w-full flex items-center justify-center py-2.5 sm:py-3 px-4 border border-transparent rounded-lg shadow-sm text-xs sm:text-sm font-medium text-white bg-gradient-to-r from-[#d4af37] to-[#c9a227] hover:from-[#f5d76e] hover:to-[#d4af37] focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-[#d4af37] disabled:opacity-50 disabled:cursor-not-allowed transition-colors"
|
className="w-full flex items-center justify-center py-2.5 sm:py-3 px-4 border border-transparent rounded-lg shadow-sm text-xs sm:text-sm font-medium text-white bg-gradient-to-r from-[var(--luxury-gold)] to-[var(--luxury-gold-dark)] hover:from-[var(--luxury-gold-light)] hover:to-[var(--luxury-gold)] focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-[var(--luxury-gold)] disabled:opacity-50 disabled:cursor-not-allowed transition-colors"
|
||||||
>
|
>
|
||||||
{isLoading ? (
|
{isLoading ? (
|
||||||
<>
|
<>
|
||||||
@@ -254,7 +254,7 @@ const ForgotPasswordModal: React.FC = () => {
|
|||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
onClick={() => openModal('login')}
|
onClick={() => openModal('login')}
|
||||||
className="inline-flex items-center text-xs sm:text-sm font-medium text-[#d4af37] hover:text-[#c9a227] transition-colors"
|
className="inline-flex items-center text-xs sm:text-sm font-medium text-[var(--luxury-gold)] hover:text-[var(--luxury-gold-dark)] transition-colors"
|
||||||
>
|
>
|
||||||
<ArrowLeft className="mr-1 h-3.5 w-3.5 sm:h-4 sm:w-4" />
|
<ArrowLeft className="mr-1 h-3.5 w-3.5 sm:h-4 sm:w-4" />
|
||||||
Back to Login
|
Back to Login
|
||||||
@@ -269,7 +269,7 @@ const ForgotPasswordModal: React.FC = () => {
|
|||||||
Don't have an account?{' '}
|
Don't have an account?{' '}
|
||||||
<button
|
<button
|
||||||
onClick={() => openModal('register')}
|
onClick={() => openModal('register')}
|
||||||
className="font-medium text-[#d4af37] hover:text-[#c9a227] transition-colors"
|
className="font-medium text-[var(--luxury-gold)] hover:text-[var(--luxury-gold-dark)] transition-colors"
|
||||||
>
|
>
|
||||||
Register now
|
Register now
|
||||||
</button>
|
</button>
|
||||||
@@ -286,7 +286,7 @@ const ForgotPasswordModal: React.FC = () => {
|
|||||||
If you're having trouble resetting your password, please contact our support team via email{' '}
|
If you're having trouble resetting your password, please contact our support team via email{' '}
|
||||||
<a
|
<a
|
||||||
href={`mailto:${supportEmail}`}
|
href={`mailto:${supportEmail}`}
|
||||||
className="text-[#d4af37] hover:underline break-all"
|
className="text-[var(--luxury-gold)] hover:underline break-all"
|
||||||
>
|
>
|
||||||
{supportEmail}
|
{supportEmail}
|
||||||
</a>
|
</a>
|
||||||
@@ -295,7 +295,7 @@ const ForgotPasswordModal: React.FC = () => {
|
|||||||
{' '}or hotline{' '}
|
{' '}or hotline{' '}
|
||||||
<a
|
<a
|
||||||
href={`tel:${supportPhone.replace(/\s+/g, '').replace(/[()]/g, '')}`}
|
href={`tel:${supportPhone.replace(/\s+/g, '').replace(/[()]/g, '')}`}
|
||||||
className="text-[#d4af37] hover:underline"
|
className="text-[var(--luxury-gold)] hover:underline"
|
||||||
>
|
>
|
||||||
{supportPhone}
|
{supportPhone}
|
||||||
</a>
|
</a>
|
||||||
|
|||||||
@@ -376,7 +376,7 @@ const LoginModal: React.FC = () => {
|
|||||||
<div className="absolute inset-0 bg-black/60 backdrop-blur-sm" />
|
<div className="absolute inset-0 bg-black/60 backdrop-blur-sm" />
|
||||||
|
|
||||||
{/* Modal */}
|
{/* Modal */}
|
||||||
<div className="relative w-full max-w-md max-h-[95vh] overflow-y-auto bg-gradient-to-br from-gray-50 via-white to-gray-50 rounded-lg shadow-2xl border border-[#d4af37]/20">
|
<div className="relative w-full max-w-md max-h-[95vh] overflow-y-auto bg-gradient-to-br from-gray-50 via-white to-gray-50 rounded-lg shadow-2xl border border-[var(--luxury-gold)]/20">
|
||||||
{/* Close button */}
|
{/* Close button */}
|
||||||
<button
|
<button
|
||||||
onClick={closeModal}
|
onClick={closeModal}
|
||||||
@@ -400,13 +400,13 @@ const LoginModal: React.FC = () => {
|
|||||||
style={{ filter: 'drop-shadow(0 4px 6px rgba(0,0,0,0.1))' }}
|
style={{ filter: 'drop-shadow(0 4px 6px rgba(0,0,0,0.1))' }}
|
||||||
/>
|
/>
|
||||||
) : (
|
) : (
|
||||||
<div className="relative p-2.5 sm:p-3 bg-gradient-to-br from-[#d4af37] to-[#c9a227] rounded-full shadow-lg shadow-[#d4af37]/30">
|
<div className="relative p-2.5 sm:p-3 bg-gradient-to-br from-[var(--luxury-gold)] to-[var(--luxury-gold-dark)] rounded-full shadow-lg shadow-[var(--luxury-gold)]/30">
|
||||||
<Shield className="w-6 h-6 sm:w-8 sm:h-8 text-[#0f0f0f] relative z-10" />
|
<Shield className="w-6 h-6 sm:w-8 sm:h-8 text-[#0f0f0f] relative z-10" />
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
{settings.company_tagline && (
|
{settings.company_tagline && (
|
||||||
<p className="text-[10px] sm:text-xs text-[#d4af37] uppercase tracking-[1.5px] sm:tracking-[2px] mb-1 sm:mb-2 font-light">
|
<p className="text-[10px] sm:text-xs text-[var(--luxury-gold)] uppercase tracking-[1.5px] sm:tracking-[2px] mb-1 sm:mb-2 font-light">
|
||||||
{settings.company_tagline}
|
{settings.company_tagline}
|
||||||
</p>
|
</p>
|
||||||
)}
|
)}
|
||||||
@@ -555,7 +555,7 @@ const LoginModal: React.FC = () => {
|
|||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
onClick={() => setShowPassword(!showPassword)}
|
onClick={() => setShowPassword(!showPassword)}
|
||||||
className="absolute inset-y-0 right-0 pr-3 flex items-center transition-colors hover:text-[#d4af37]"
|
className="absolute inset-y-0 right-0 pr-3 flex items-center transition-colors hover:text-[var(--luxury-gold)]"
|
||||||
>
|
>
|
||||||
{showPassword ? (
|
{showPassword ? (
|
||||||
<EyeOff className="h-4 w-4 sm:h-5 sm:w-5 text-gray-400" />
|
<EyeOff className="h-4 w-4 sm:h-5 sm:w-5 text-gray-400" />
|
||||||
@@ -577,7 +577,7 @@ const LoginModal: React.FC = () => {
|
|||||||
{...register('rememberMe')}
|
{...register('rememberMe')}
|
||||||
id="rememberMe"
|
id="rememberMe"
|
||||||
type="checkbox"
|
type="checkbox"
|
||||||
className="h-4 w-4 text-[#d4af37] focus:ring-[#d4af37]/50 border-gray-300 rounded-sm cursor-pointer accent-[#d4af37]"
|
className="h-4 w-4 text-[var(--luxury-gold)] focus:ring-[var(--luxury-gold)]/50 border-gray-300 rounded-sm cursor-pointer accent-[var(--luxury-gold)]"
|
||||||
/>
|
/>
|
||||||
<label htmlFor="rememberMe" className="ml-2 block text-xs sm:text-sm text-gray-700 cursor-pointer font-light tracking-wide">
|
<label htmlFor="rememberMe" className="ml-2 block text-xs sm:text-sm text-gray-700 cursor-pointer font-light tracking-wide">
|
||||||
Remember me
|
Remember me
|
||||||
@@ -587,7 +587,7 @@ const LoginModal: React.FC = () => {
|
|||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
onClick={() => openModal('forgot-password')}
|
onClick={() => openModal('forgot-password')}
|
||||||
className="text-xs sm:text-sm font-medium text-[#d4af37] hover:text-[#c9a227] transition-colors tracking-wide"
|
className="text-xs sm:text-sm font-medium text-[var(--luxury-gold)] hover:text-[var(--luxury-gold-dark)] transition-colors tracking-wide"
|
||||||
>
|
>
|
||||||
Forgot password?
|
Forgot password?
|
||||||
</button>
|
</button>
|
||||||
@@ -633,7 +633,7 @@ const LoginModal: React.FC = () => {
|
|||||||
Don't have an account?{' '}
|
Don't have an account?{' '}
|
||||||
<button
|
<button
|
||||||
onClick={() => openModal('register')}
|
onClick={() => openModal('register')}
|
||||||
className="font-medium text-[#d4af37] hover:text-[#c9a227] transition-colors"
|
className="font-medium text-[var(--luxury-gold)] hover:text-[var(--luxury-gold-dark)] transition-colors"
|
||||||
>
|
>
|
||||||
Register now
|
Register now
|
||||||
</button>
|
</button>
|
||||||
|
|||||||
@@ -15,11 +15,11 @@ import HoneypotField from '../../../shared/components/HoneypotField';
|
|||||||
const PasswordRequirement: React.FC<{ met: boolean; text: string }> = ({ met, text }) => (
|
const PasswordRequirement: React.FC<{ met: boolean; text: string }> = ({ met, text }) => (
|
||||||
<div className="flex items-center gap-1.5 sm:gap-2 text-[10px] sm:text-xs font-light">
|
<div className="flex items-center gap-1.5 sm:gap-2 text-[10px] sm:text-xs font-light">
|
||||||
{met ? (
|
{met ? (
|
||||||
<CheckCircle2 className="h-3.5 w-3.5 sm:h-4 sm:w-4 text-[#d4af37] flex-shrink-0" />
|
<CheckCircle2 className="h-3.5 w-3.5 sm:h-4 sm:w-4 text-[var(--luxury-gold)] flex-shrink-0" />
|
||||||
) : (
|
) : (
|
||||||
<XCircle className="h-3.5 w-3.5 sm:h-4 sm:w-4 text-gray-300 flex-shrink-0" />
|
<XCircle className="h-3.5 w-3.5 sm:h-4 sm:w-4 text-gray-300 flex-shrink-0" />
|
||||||
)}
|
)}
|
||||||
<span className={met ? 'text-[#c9a227] font-medium' : 'text-gray-500'}>
|
<span className={met ? 'text-[var(--luxury-gold-dark)] font-medium' : 'text-gray-500'}>
|
||||||
{text}
|
{text}
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
@@ -165,7 +165,7 @@ const RegisterModal: React.FC = () => {
|
|||||||
<div className="absolute inset-0 bg-black/60 backdrop-blur-sm" />
|
<div className="absolute inset-0 bg-black/60 backdrop-blur-sm" />
|
||||||
|
|
||||||
{/* Modal */}
|
{/* Modal */}
|
||||||
<div className="relative w-full max-w-md max-h-[95vh] overflow-y-auto bg-gradient-to-br from-gray-50 via-white to-gray-50 rounded-lg shadow-2xl border border-[#d4af37]/20">
|
<div className="relative w-full max-w-md max-h-[95vh] overflow-y-auto bg-gradient-to-br from-gray-50 via-white to-gray-50 rounded-lg shadow-2xl border border-[var(--luxury-gold)]/20">
|
||||||
{/* Close button */}
|
{/* Close button */}
|
||||||
<button
|
<button
|
||||||
onClick={closeModal}
|
onClick={closeModal}
|
||||||
@@ -189,13 +189,13 @@ const RegisterModal: React.FC = () => {
|
|||||||
style={{ filter: 'drop-shadow(0 4px 6px rgba(0,0,0,0.1))' }}
|
style={{ filter: 'drop-shadow(0 4px 6px rgba(0,0,0,0.1))' }}
|
||||||
/>
|
/>
|
||||||
) : (
|
) : (
|
||||||
<div className="relative p-2.5 sm:p-3 bg-gradient-to-br from-[#d4af37] to-[#c9a227] rounded-full shadow-lg shadow-[#d4af37]/30">
|
<div className="relative p-2.5 sm:p-3 bg-gradient-to-br from-[var(--luxury-gold)] to-[var(--luxury-gold-dark)] rounded-full shadow-lg shadow-[var(--luxury-gold)]/30">
|
||||||
<UserPlus className="w-6 h-6 sm:w-8 sm:h-8 text-[#0f0f0f] relative z-10" />
|
<UserPlus className="w-6 h-6 sm:w-8 sm:h-8 text-[#0f0f0f] relative z-10" />
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
{settings.company_tagline && (
|
{settings.company_tagline && (
|
||||||
<p className="text-[10px] sm:text-xs text-[#d4af37] uppercase tracking-[1.5px] sm:tracking-[2px] mb-1 sm:mb-2 font-light">
|
<p className="text-[10px] sm:text-xs text-[var(--luxury-gold)] uppercase tracking-[1.5px] sm:tracking-[2px] mb-1 sm:mb-2 font-light">
|
||||||
{settings.company_tagline}
|
{settings.company_tagline}
|
||||||
</p>
|
</p>
|
||||||
)}
|
)}
|
||||||
@@ -322,7 +322,7 @@ const RegisterModal: React.FC = () => {
|
|||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
onClick={() => setShowPassword(!showPassword)}
|
onClick={() => setShowPassword(!showPassword)}
|
||||||
className="absolute inset-y-0 right-0 pr-3 flex items-center transition-colors hover:text-[#d4af37]"
|
className="absolute inset-y-0 right-0 pr-3 flex items-center transition-colors hover:text-[var(--luxury-gold)]"
|
||||||
>
|
>
|
||||||
{showPassword ? (
|
{showPassword ? (
|
||||||
<EyeOff className="h-4 w-4 sm:h-5 sm:w-5 text-gray-400" />
|
<EyeOff className="h-4 w-4 sm:h-5 sm:w-5 text-gray-400" />
|
||||||
@@ -344,7 +344,7 @@ const RegisterModal: React.FC = () => {
|
|||||||
<div
|
<div
|
||||||
className={`h-full transition-all duration-300 ${
|
className={`h-full transition-all duration-300 ${
|
||||||
passwordStrength.strength >= 4
|
passwordStrength.strength >= 4
|
||||||
? 'bg-[#d4af37]'
|
? 'bg-[var(--luxury-gold)]'
|
||||||
: passwordStrength.strength >= 3
|
: passwordStrength.strength >= 3
|
||||||
? 'bg-yellow-500'
|
? 'bg-yellow-500'
|
||||||
: 'bg-red-500'
|
: 'bg-red-500'
|
||||||
@@ -389,7 +389,7 @@ const RegisterModal: React.FC = () => {
|
|||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
onClick={() => setShowConfirmPassword(!showConfirmPassword)}
|
onClick={() => setShowConfirmPassword(!showConfirmPassword)}
|
||||||
className="absolute inset-y-0 right-0 pr-3 flex items-center transition-colors hover:text-[#d4af37]"
|
className="absolute inset-y-0 right-0 pr-3 flex items-center transition-colors hover:text-[var(--luxury-gold)]"
|
||||||
>
|
>
|
||||||
{showConfirmPassword ? (
|
{showConfirmPassword ? (
|
||||||
<EyeOff className="h-4 w-4 sm:h-5 sm:w-5 text-gray-400" />
|
<EyeOff className="h-4 w-4 sm:h-5 sm:w-5 text-gray-400" />
|
||||||
@@ -443,7 +443,7 @@ const RegisterModal: React.FC = () => {
|
|||||||
Already have an account?{' '}
|
Already have an account?{' '}
|
||||||
<button
|
<button
|
||||||
onClick={() => openModal('login')}
|
onClick={() => openModal('login')}
|
||||||
className="font-medium text-[#d4af37] hover:text-[#c9a227] transition-colors"
|
className="font-medium text-[var(--luxury-gold)] hover:text-[var(--luxury-gold-dark)] transition-colors"
|
||||||
>
|
>
|
||||||
Login now
|
Login now
|
||||||
</button>
|
</button>
|
||||||
|
|||||||
@@ -132,7 +132,7 @@ const ResetPasswordModal: React.FC<ResetPasswordModalProps> = ({ token }) => {
|
|||||||
<div className="absolute inset-0 bg-black/60 backdrop-blur-sm" />
|
<div className="absolute inset-0 bg-black/60 backdrop-blur-sm" />
|
||||||
|
|
||||||
{/* Modal */}
|
{/* Modal */}
|
||||||
<div className="relative w-full max-w-md max-h-[95vh] overflow-y-auto bg-gradient-to-br from-gray-50 via-white to-gray-50 rounded-lg shadow-2xl border border-[#d4af37]/20">
|
<div className="relative w-full max-w-md max-h-[95vh] overflow-y-auto bg-gradient-to-br from-gray-50 via-white to-gray-50 rounded-lg shadow-2xl border border-[var(--luxury-gold)]/20">
|
||||||
{/* Close button */}
|
{/* Close button */}
|
||||||
{!isSuccess && (
|
{!isSuccess && (
|
||||||
<button
|
<button
|
||||||
@@ -158,13 +158,13 @@ const ResetPasswordModal: React.FC<ResetPasswordModalProps> = ({ token }) => {
|
|||||||
style={{ filter: 'drop-shadow(0 4px 6px rgba(0,0,0,0.1))' }}
|
style={{ filter: 'drop-shadow(0 4px 6px rgba(0,0,0,0.1))' }}
|
||||||
/>
|
/>
|
||||||
) : (
|
) : (
|
||||||
<div className="relative p-2.5 sm:p-3 bg-gradient-to-br from-[#d4af37] to-[#c9a227] rounded-full shadow-lg shadow-[#d4af37]/30">
|
<div className="relative p-2.5 sm:p-3 bg-gradient-to-br from-[var(--luxury-gold)] to-[var(--luxury-gold-dark)] rounded-full shadow-lg shadow-[var(--luxury-gold)]/30">
|
||||||
<KeyRound className="w-6 h-6 sm:w-8 sm:h-8 text-[#0f0f0f] relative z-10" />
|
<KeyRound className="w-6 h-6 sm:w-8 sm:h-8 text-[#0f0f0f] relative z-10" />
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
{settings.company_tagline && (
|
{settings.company_tagline && (
|
||||||
<p className="text-[10px] sm:text-xs text-[#d4af37] uppercase tracking-[1.5px] sm:tracking-[2px] mb-1 sm:mb-2 font-light">
|
<p className="text-[10px] sm:text-xs text-[var(--luxury-gold)] uppercase tracking-[1.5px] sm:tracking-[2px] mb-1 sm:mb-2 font-light">
|
||||||
{settings.company_tagline}
|
{settings.company_tagline}
|
||||||
</p>
|
</p>
|
||||||
)}
|
)}
|
||||||
@@ -203,7 +203,7 @@ const ResetPasswordModal: React.FC<ResetPasswordModalProps> = ({ token }) => {
|
|||||||
Redirecting to login...
|
Redirecting to login...
|
||||||
</p>
|
</p>
|
||||||
<div className="mt-2 flex justify-center">
|
<div className="mt-2 flex justify-center">
|
||||||
<Loader2 className="animate-spin h-4 w-4 sm:h-5 sm:w-5 text-[#d4af37]" />
|
<Loader2 className="animate-spin h-4 w-4 sm:h-5 sm:w-5 text-[var(--luxury-gold)]" />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@@ -212,7 +212,7 @@ const ResetPasswordModal: React.FC<ResetPasswordModalProps> = ({ token }) => {
|
|||||||
closeModal();
|
closeModal();
|
||||||
openModal('login');
|
openModal('login');
|
||||||
}}
|
}}
|
||||||
className="inline-flex items-center justify-center w-full py-2.5 sm:py-3 px-4 border border-transparent rounded-lg text-xs sm:text-sm font-medium text-white bg-gradient-to-r from-[#d4af37] to-[#c9a227] hover:from-[#f5d76e] hover:to-[#d4af37] focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-[#d4af37] transition-colors"
|
className="inline-flex items-center justify-center w-full py-2.5 sm:py-3 px-4 border border-transparent rounded-lg text-xs sm:text-sm font-medium text-white bg-gradient-to-r from-[var(--luxury-gold)] to-[var(--luxury-gold-dark)] hover:from-[var(--luxury-gold-light)] hover:to-[var(--luxury-gold)] focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-[var(--luxury-gold)] transition-colors"
|
||||||
>
|
>
|
||||||
<KeyRound className="-ml-1 mr-2 h-4 w-4 sm:h-5 sm:w-5" />
|
<KeyRound className="-ml-1 mr-2 h-4 w-4 sm:h-5 sm:w-5" />
|
||||||
Login Now
|
Login Now
|
||||||
@@ -267,7 +267,7 @@ const ResetPasswordModal: React.FC<ResetPasswordModalProps> = ({ token }) => {
|
|||||||
className={`block w-full pl-9 sm:pl-10 pr-9 sm:pr-10 py-2.5 sm:py-3 border rounded-lg focus:outline-none focus:ring-2 transition-colors text-sm sm:text-base luxury-input ${
|
className={`block w-full pl-9 sm:pl-10 pr-9 sm:pr-10 py-2.5 sm:py-3 border rounded-lg focus:outline-none focus:ring-2 transition-colors text-sm sm:text-base luxury-input ${
|
||||||
errors.password
|
errors.password
|
||||||
? 'border-red-300 focus:ring-red-500'
|
? 'border-red-300 focus:ring-red-500'
|
||||||
: 'border-gray-300 focus:ring-[#d4af37]'
|
: 'border-gray-300 focus:ring-[var(--luxury-gold)]'
|
||||||
}`}
|
}`}
|
||||||
placeholder="••••••••"
|
placeholder="••••••••"
|
||||||
/>
|
/>
|
||||||
@@ -330,7 +330,7 @@ const ResetPasswordModal: React.FC<ResetPasswordModalProps> = ({ token }) => {
|
|||||||
className={`block w-full pl-9 sm:pl-10 pr-9 sm:pr-10 py-2.5 sm:py-3 border rounded-lg focus:outline-none focus:ring-2 transition-colors text-sm sm:text-base luxury-input ${
|
className={`block w-full pl-9 sm:pl-10 pr-9 sm:pr-10 py-2.5 sm:py-3 border rounded-lg focus:outline-none focus:ring-2 transition-colors text-sm sm:text-base luxury-input ${
|
||||||
errors.confirmPassword
|
errors.confirmPassword
|
||||||
? 'border-red-300 focus:ring-red-500'
|
? 'border-red-300 focus:ring-red-500'
|
||||||
: 'border-gray-300 focus:ring-[#d4af37]'
|
: 'border-gray-300 focus:ring-[var(--luxury-gold)]'
|
||||||
}`}
|
}`}
|
||||||
placeholder="••••••••"
|
placeholder="••••••••"
|
||||||
/>
|
/>
|
||||||
@@ -356,7 +356,7 @@ const ResetPasswordModal: React.FC<ResetPasswordModalProps> = ({ token }) => {
|
|||||||
<button
|
<button
|
||||||
type="submit"
|
type="submit"
|
||||||
disabled={isLoading}
|
disabled={isLoading}
|
||||||
className="w-full flex items-center justify-center py-2.5 sm:py-3 px-4 border border-transparent rounded-lg shadow-sm text-xs sm:text-sm font-medium text-white bg-gradient-to-r from-[#d4af37] to-[#c9a227] hover:from-[#f5d76e] hover:to-[#d4af37] focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-[#d4af37] disabled:opacity-50 disabled:cursor-not-allowed transition-colors"
|
className="w-full flex items-center justify-center py-2.5 sm:py-3 px-4 border border-transparent rounded-lg shadow-sm text-xs sm:text-sm font-medium text-white bg-gradient-to-r from-[var(--luxury-gold)] to-[var(--luxury-gold-dark)] hover:from-[var(--luxury-gold-light)] hover:to-[var(--luxury-gold)] focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-[var(--luxury-gold)] disabled:opacity-50 disabled:cursor-not-allowed transition-colors"
|
||||||
>
|
>
|
||||||
{isLoading ? (
|
{isLoading ? (
|
||||||
<>
|
<>
|
||||||
@@ -375,7 +375,7 @@ const ResetPasswordModal: React.FC<ResetPasswordModalProps> = ({ token }) => {
|
|||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
onClick={() => openModal('login')}
|
onClick={() => openModal('login')}
|
||||||
className="text-xs sm:text-sm font-medium text-[#d4af37] hover:text-[#c9a227] transition-colors"
|
className="text-xs sm:text-sm font-medium text-[var(--luxury-gold)] hover:text-[var(--luxury-gold-dark)] transition-colors"
|
||||||
>
|
>
|
||||||
Back to Login
|
Back to Login
|
||||||
</button>
|
</button>
|
||||||
|
|||||||
@@ -345,7 +345,7 @@ const HousekeepingLoginPage: React.FC = () => {
|
|||||||
<div className="min-h-screen bg-gradient-to-br from-gray-50 via-white to-gray-50 flex items-center justify-center p-4">
|
<div className="min-h-screen bg-gradient-to-br from-gray-50 via-white to-gray-50 flex items-center justify-center p-4">
|
||||||
<div className="w-full max-w-md">
|
<div className="w-full max-w-md">
|
||||||
<div className="bg-white rounded-2xl shadow-2xl border border-gray-200/50 overflow-hidden">
|
<div className="bg-white rounded-2xl shadow-2xl border border-gray-200/50 overflow-hidden">
|
||||||
<div className="bg-gradient-to-r from-[#d4af37] via-[#c9a227] to-[#d4af37] p-6 sm:p-8 text-center">
|
<div className="bg-gradient-to-r from-[var(--luxury-gold)] via-[var(--luxury-gold-dark)] to-[var(--luxury-gold)] p-6 sm:p-8 text-center">
|
||||||
<div className="flex justify-center mb-4">
|
<div className="flex justify-center mb-4">
|
||||||
{settings.company_logo_url ? (
|
{settings.company_logo_url ? (
|
||||||
<img
|
<img
|
||||||
@@ -389,7 +389,7 @@ const HousekeepingLoginPage: React.FC = () => {
|
|||||||
autoComplete="one-time-code"
|
autoComplete="one-time-code"
|
||||||
maxLength={8}
|
maxLength={8}
|
||||||
className={`w-full pl-10 pr-4 py-3 border rounded-lg text-center tracking-widest ${
|
className={`w-full pl-10 pr-4 py-3 border rounded-lg text-center tracking-widest ${
|
||||||
mfaErrors.mfaToken ? 'border-red-300 focus:ring-red-500' : 'border-gray-300 focus:ring-[#d4af37]'
|
mfaErrors.mfaToken ? 'border-red-300 focus:ring-red-500' : 'border-gray-300 focus:ring-[var(--luxury-gold)]'
|
||||||
}`}
|
}`}
|
||||||
placeholder="000000"
|
placeholder="000000"
|
||||||
/>
|
/>
|
||||||
@@ -402,7 +402,7 @@ const HousekeepingLoginPage: React.FC = () => {
|
|||||||
<button
|
<button
|
||||||
type="submit"
|
type="submit"
|
||||||
disabled={isLoading}
|
disabled={isLoading}
|
||||||
className="w-full bg-gradient-to-r from-[#d4af37] to-[#c9a227] text-white py-3 px-4 rounded-lg font-semibold hover:from-[#f5d76e] hover:to-[#d4af37] transition-all duration-200 disabled:opacity-50 disabled:cursor-not-allowed flex items-center justify-center"
|
className="w-full bg-gradient-to-r from-[var(--luxury-gold)] to-[var(--luxury-gold-dark)] text-white py-3 px-4 rounded-lg font-semibold hover:from-[var(--luxury-gold-light)] hover:to-[var(--luxury-gold)] transition-all duration-200 disabled:opacity-50 disabled:cursor-not-allowed flex items-center justify-center"
|
||||||
>
|
>
|
||||||
{isLoading ? (
|
{isLoading ? (
|
||||||
<>
|
<>
|
||||||
@@ -461,7 +461,7 @@ const HousekeepingLoginPage: React.FC = () => {
|
|||||||
type="email"
|
type="email"
|
||||||
autoComplete="email"
|
autoComplete="email"
|
||||||
className={`w-full pl-10 pr-4 py-3 border rounded-lg ${
|
className={`w-full pl-10 pr-4 py-3 border rounded-lg ${
|
||||||
errors.email ? 'border-red-300 focus:ring-red-500' : 'border-gray-300 focus:ring-[#d4af37]'
|
errors.email ? 'border-red-300 focus:ring-red-500' : 'border-gray-300 focus:ring-[var(--luxury-gold)]'
|
||||||
}`}
|
}`}
|
||||||
placeholder="email@example.com"
|
placeholder="email@example.com"
|
||||||
/>
|
/>
|
||||||
@@ -485,7 +485,7 @@ const HousekeepingLoginPage: React.FC = () => {
|
|||||||
type={showPassword ? 'text' : 'password'}
|
type={showPassword ? 'text' : 'password'}
|
||||||
autoComplete="current-password"
|
autoComplete="current-password"
|
||||||
className={`w-full pl-10 pr-10 py-3 border rounded-lg ${
|
className={`w-full pl-10 pr-10 py-3 border rounded-lg ${
|
||||||
errors.password ? 'border-red-300 focus:ring-red-500' : 'border-gray-300 focus:ring-[#d4af37]'
|
errors.password ? 'border-red-300 focus:ring-red-500' : 'border-gray-300 focus:ring-[var(--luxury-gold)]'
|
||||||
}`}
|
}`}
|
||||||
placeholder="••••••••"
|
placeholder="••••••••"
|
||||||
/>
|
/>
|
||||||
@@ -511,7 +511,7 @@ const HousekeepingLoginPage: React.FC = () => {
|
|||||||
{...register('rememberMe')}
|
{...register('rememberMe')}
|
||||||
id="rememberMe"
|
id="rememberMe"
|
||||||
type="checkbox"
|
type="checkbox"
|
||||||
className="h-4 w-4 text-[#d4af37] focus:ring-[#d4af37] border-gray-300 rounded"
|
className="h-4 w-4 text-[var(--luxury-gold)] focus:ring-[var(--luxury-gold)] border-gray-300 rounded"
|
||||||
/>
|
/>
|
||||||
<label htmlFor="rememberMe" className="ml-2 block text-sm text-gray-700">
|
<label htmlFor="rememberMe" className="ml-2 block text-sm text-gray-700">
|
||||||
Remember me
|
Remember me
|
||||||
@@ -535,7 +535,7 @@ const HousekeepingLoginPage: React.FC = () => {
|
|||||||
<button
|
<button
|
||||||
type="submit"
|
type="submit"
|
||||||
disabled={isLoading}
|
disabled={isLoading}
|
||||||
className="w-full bg-gradient-to-r from-[#d4af37] to-[#c9a227] text-white py-3 px-4 rounded-lg font-semibold hover:from-[#f5d76e] hover:to-[#d4af37] transition-all duration-200 disabled:opacity-50 disabled:cursor-not-allowed flex items-center justify-center"
|
className="w-full bg-gradient-to-r from-[var(--luxury-gold)] to-[var(--luxury-gold-dark)] text-white py-3 px-4 rounded-lg font-semibold hover:from-[var(--luxury-gold-light)] hover:to-[var(--luxury-gold)] transition-all duration-200 disabled:opacity-50 disabled:cursor-not-allowed flex items-center justify-center"
|
||||||
>
|
>
|
||||||
{isLoading ? (
|
{isLoading ? (
|
||||||
<>
|
<>
|
||||||
|
|||||||
@@ -44,19 +44,19 @@ const InvoiceInfoModal: React.FC<InvoiceInfoModalProps> = ({
|
|||||||
className="fixed inset-0 bg-black/90 backdrop-blur-sm"
|
className="fixed inset-0 bg-black/90 backdrop-blur-sm"
|
||||||
onClick={onClose}
|
onClick={onClose}
|
||||||
/>
|
/>
|
||||||
<div className="relative w-full max-w-lg max-h-[95vh] bg-gradient-to-br from-[#0a0a0a] via-[#1a1a1a] to-[#0a0a0a] rounded-xl sm:rounded-2xl border border-[#d4af37]/30 shadow-2xl shadow-[#d4af37]/20 overflow-hidden flex flex-col">
|
<div className="relative w-full max-w-lg max-h-[95vh] bg-gradient-to-br from-[#0a0a0a] via-[#1a1a1a] to-[#0a0a0a] rounded-xl sm:rounded-2xl border border-[var(--luxury-gold)]/30 shadow-2xl shadow-[var(--luxury-gold)]/20 overflow-hidden flex flex-col">
|
||||||
{/* Header */}
|
{/* Header */}
|
||||||
<div className="px-3 sm:px-4 md:px-6 py-3 sm:py-4 border-b border-[#d4af37]/20 bg-gradient-to-r from-[#1a1a1a] to-[#0a0a0a] flex-shrink-0">
|
<div className="px-3 sm:px-4 md:px-6 py-3 sm:py-4 border-b border-[var(--luxury-gold)]/20 bg-gradient-to-r from-[#1a1a1a] to-[#0a0a0a] flex-shrink-0">
|
||||||
<div className="flex items-center justify-between gap-2">
|
<div className="flex items-center justify-between gap-2">
|
||||||
<div className="flex items-center gap-2 min-w-0 flex-1">
|
<div className="flex items-center gap-2 min-w-0 flex-1">
|
||||||
<Building2 className="w-4 h-4 sm:w-5 sm:h-5 text-[#d4af37] flex-shrink-0" />
|
<Building2 className="w-4 h-4 sm:w-5 sm:h-5 text-[var(--luxury-gold)] flex-shrink-0" />
|
||||||
<h2 className="text-base sm:text-lg md:text-xl font-serif font-bold text-white tracking-tight truncate">
|
<h2 className="text-base sm:text-lg md:text-xl font-serif font-bold text-white tracking-tight truncate">
|
||||||
Invoice Information
|
Invoice Information
|
||||||
</h2>
|
</h2>
|
||||||
</div>
|
</div>
|
||||||
<button
|
<button
|
||||||
onClick={onClose}
|
onClick={onClose}
|
||||||
className="p-1.5 sm:p-2 hover:bg-[#d4af37]/10 rounded-lg transition-colors text-gray-400 hover:text-white flex-shrink-0"
|
className="p-1.5 sm:p-2 hover:bg-[var(--luxury-gold)]/10 rounded-lg transition-colors text-gray-400 hover:text-white flex-shrink-0"
|
||||||
>
|
>
|
||||||
<X className="w-4 h-4 sm:w-5 sm:h-5" />
|
<X className="w-4 h-4 sm:w-5 sm:h-5" />
|
||||||
</button>
|
</button>
|
||||||
@@ -73,7 +73,7 @@ const InvoiceInfoModal: React.FC<InvoiceInfoModalProps> = ({
|
|||||||
<input
|
<input
|
||||||
{...register('company_name')}
|
{...register('company_name')}
|
||||||
type="text"
|
type="text"
|
||||||
className="w-full px-3 py-2 bg-[#0a0a0a] border border-[#d4af37]/20 rounded-lg text-white text-sm focus:ring-2 focus:ring-[#d4af37]/50 focus:border-[#d4af37]"
|
className="w-full px-3 py-2 bg-[#0a0a0a] border border-[var(--luxury-gold)]/20 rounded-lg text-white text-sm focus:ring-2 focus:ring-[var(--luxury-gold)]/50 focus:border-[var(--luxury-gold)]"
|
||||||
placeholder="Company Name"
|
placeholder="Company Name"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
@@ -85,7 +85,7 @@ const InvoiceInfoModal: React.FC<InvoiceInfoModalProps> = ({
|
|||||||
<textarea
|
<textarea
|
||||||
{...register('company_address')}
|
{...register('company_address')}
|
||||||
rows={3}
|
rows={3}
|
||||||
className="w-full px-3 py-2 bg-[#0a0a0a] border border-[#d4af37]/20 rounded-lg text-white text-sm focus:ring-2 focus:ring-[#d4af37]/50 focus:border-[#d4af37] resize-none"
|
className="w-full px-3 py-2 bg-[#0a0a0a] border border-[var(--luxury-gold)]/20 rounded-lg text-white text-sm focus:ring-2 focus:ring-[var(--luxury-gold)]/50 focus:border-[var(--luxury-gold)] resize-none"
|
||||||
placeholder="Company Address"
|
placeholder="Company Address"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
@@ -98,7 +98,7 @@ const InvoiceInfoModal: React.FC<InvoiceInfoModalProps> = ({
|
|||||||
<input
|
<input
|
||||||
{...register('company_tax_id')}
|
{...register('company_tax_id')}
|
||||||
type="text"
|
type="text"
|
||||||
className="w-full px-3 py-2 bg-[#0a0a0a] border border-[#d4af37]/20 rounded-lg text-white text-sm focus:ring-2 focus:ring-[#d4af37]/50 focus:border-[#d4af37]"
|
className="w-full px-3 py-2 bg-[#0a0a0a] border border-[var(--luxury-gold)]/20 rounded-lg text-white text-sm focus:ring-2 focus:ring-[var(--luxury-gold)]/50 focus:border-[var(--luxury-gold)]"
|
||||||
placeholder="Tax ID"
|
placeholder="Tax ID"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
@@ -110,7 +110,7 @@ const InvoiceInfoModal: React.FC<InvoiceInfoModalProps> = ({
|
|||||||
<input
|
<input
|
||||||
{...register('customer_tax_id')}
|
{...register('customer_tax_id')}
|
||||||
type="text"
|
type="text"
|
||||||
className="w-full px-3 py-2 bg-[#0a0a0a] border border-[#d4af37]/20 rounded-lg text-white text-sm focus:ring-2 focus:ring-[#d4af37]/50 focus:border-[#d4af37]"
|
className="w-full px-3 py-2 bg-[#0a0a0a] border border-[var(--luxury-gold)]/20 rounded-lg text-white text-sm focus:ring-2 focus:ring-[var(--luxury-gold)]/50 focus:border-[var(--luxury-gold)]"
|
||||||
placeholder="Tax ID"
|
placeholder="Tax ID"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
@@ -118,7 +118,7 @@ const InvoiceInfoModal: React.FC<InvoiceInfoModalProps> = ({
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Footer */}
|
{/* Footer */}
|
||||||
<div className="mt-4 sm:mt-6 flex items-center justify-end gap-2 sm:gap-3 pt-3 sm:pt-4 border-t border-[#d4af37]/20 flex-shrink-0">
|
<div className="mt-4 sm:mt-6 flex items-center justify-end gap-2 sm:gap-3 pt-3 sm:pt-4 border-t border-[var(--luxury-gold)]/20 flex-shrink-0">
|
||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
onClick={onClose}
|
onClick={onClose}
|
||||||
@@ -128,7 +128,7 @@ const InvoiceInfoModal: React.FC<InvoiceInfoModalProps> = ({
|
|||||||
</button>
|
</button>
|
||||||
<button
|
<button
|
||||||
type="submit"
|
type="submit"
|
||||||
className="px-4 sm:px-6 py-2 bg-[#d4af37] text-black font-semibold rounded-lg hover:bg-[#c9a227] transition-all flex items-center gap-1.5 sm:gap-2 text-sm sm:text-base min-h-[44px]"
|
className="px-4 sm:px-6 py-2 bg-[var(--luxury-gold)] text-black font-semibold rounded-lg hover:bg-[var(--luxury-gold-dark)] transition-all flex items-center gap-1.5 sm:gap-2 text-sm sm:text-base min-h-[44px]"
|
||||||
>
|
>
|
||||||
<Save className="w-3.5 h-3.5 sm:w-4 sm:h-4" />
|
<Save className="w-3.5 h-3.5 sm:w-4 sm:h-4" />
|
||||||
<span>Save</span>
|
<span>Save</span>
|
||||||
|
|||||||
@@ -514,9 +514,9 @@ const LuxuryBookingModal: React.FC<LuxuryBookingModalProps> = ({
|
|||||||
className="fixed inset-0 bg-black/80 backdrop-blur-sm"
|
className="fixed inset-0 bg-black/80 backdrop-blur-sm"
|
||||||
onClick={onClose}
|
onClick={onClose}
|
||||||
/>
|
/>
|
||||||
<div className="relative w-full max-w-4xl max-h-[95vh] bg-gradient-to-br from-[#0a0a0a] via-[#1a1a1a] to-[#0a0a0a] rounded-xl sm:rounded-2xl border border-[#d4af37]/30 shadow-2xl shadow-[#d4af37]/20 overflow-hidden flex flex-col">
|
<div className="relative w-full max-w-4xl max-h-[95vh] bg-gradient-to-br from-[#0a0a0a] via-[#1a1a1a] to-[#0a0a0a] rounded-xl sm:rounded-2xl border border-[var(--luxury-gold)]/30 shadow-2xl shadow-[var(--luxury-gold)]/20 overflow-hidden flex flex-col">
|
||||||
{/* Header */}
|
{/* Header */}
|
||||||
<div className="relative px-3 sm:px-4 md:px-6 py-3 sm:py-4 border-b border-[#d4af37]/20 bg-gradient-to-r from-[#1a1a1a] to-[#0a0a0a] flex-shrink-0">
|
<div className="relative px-3 sm:px-4 md:px-6 py-3 sm:py-4 border-b border-[var(--luxury-gold)]/20 bg-gradient-to-r from-[#1a1a1a] to-[#0a0a0a] flex-shrink-0">
|
||||||
<div className="flex items-center justify-between gap-2">
|
<div className="flex items-center justify-between gap-2">
|
||||||
<div className="min-w-0 flex-1">
|
<div className="min-w-0 flex-1">
|
||||||
<h2 className="text-base sm:text-lg md:text-xl lg:text-2xl font-serif font-bold text-white tracking-tight truncate">
|
<h2 className="text-base sm:text-lg md:text-xl lg:text-2xl font-serif font-bold text-white tracking-tight truncate">
|
||||||
@@ -528,7 +528,7 @@ const LuxuryBookingModal: React.FC<LuxuryBookingModalProps> = ({
|
|||||||
</div>
|
</div>
|
||||||
<button
|
<button
|
||||||
onClick={onClose}
|
onClick={onClose}
|
||||||
className="p-1.5 sm:p-2 hover:bg-[#d4af37]/10 rounded-lg transition-colors text-gray-400 hover:text-white flex-shrink-0"
|
className="p-1.5 sm:p-2 hover:bg-[var(--luxury-gold)]/10 rounded-lg transition-colors text-gray-400 hover:text-white flex-shrink-0"
|
||||||
>
|
>
|
||||||
<X className="w-4 h-4 sm:w-5 sm:h-5" />
|
<X className="w-4 h-4 sm:w-5 sm:h-5" />
|
||||||
</button>
|
</button>
|
||||||
@@ -545,7 +545,7 @@ const LuxuryBookingModal: React.FC<LuxuryBookingModalProps> = ({
|
|||||||
<div
|
<div
|
||||||
className={`flex items-center justify-center w-6 h-6 sm:w-7 sm:h-7 md:w-8 md:h-8 rounded-full border-2 transition-all flex-shrink-0 ${
|
className={`flex items-center justify-center w-6 h-6 sm:w-7 sm:h-7 md:w-8 md:h-8 rounded-full border-2 transition-all flex-shrink-0 ${
|
||||||
isActive
|
isActive
|
||||||
? 'bg-[#d4af37] border-[#d4af37] text-black'
|
? 'bg-[var(--luxury-gold)] border-[var(--luxury-gold)] text-black'
|
||||||
: isCompleted
|
: isCompleted
|
||||||
? 'bg-green-500/20 border-green-500 text-green-400'
|
? 'bg-green-500/20 border-green-500 text-green-400'
|
||||||
: 'bg-transparent border-gray-600 text-gray-500'
|
: 'bg-transparent border-gray-600 text-gray-500'
|
||||||
@@ -561,7 +561,7 @@ const LuxuryBookingModal: React.FC<LuxuryBookingModalProps> = ({
|
|||||||
</div>
|
</div>
|
||||||
<span
|
<span
|
||||||
className={`ml-1 sm:ml-2 text-[10px] sm:text-xs font-medium hidden md:block truncate ${
|
className={`ml-1 sm:ml-2 text-[10px] sm:text-xs font-medium hidden md:block truncate ${
|
||||||
isActive ? 'text-[#d4af37]' : isCompleted ? 'text-green-400' : 'text-gray-500'
|
isActive ? 'text-[var(--luxury-gold)]' : isCompleted ? 'text-green-400' : 'text-gray-500'
|
||||||
}`}
|
}`}
|
||||||
>
|
>
|
||||||
{step.label}
|
{step.label}
|
||||||
@@ -585,7 +585,7 @@ const LuxuryBookingModal: React.FC<LuxuryBookingModalProps> = ({
|
|||||||
<div className="h-full overflow-y-auto">
|
<div className="h-full overflow-y-auto">
|
||||||
{loading ? (
|
{loading ? (
|
||||||
<div className="flex items-center justify-center py-12">
|
<div className="flex items-center justify-center py-12">
|
||||||
<Loader2 className="w-8 h-8 animate-spin text-[#d4af37]" />
|
<Loader2 className="w-8 h-8 animate-spin text-[var(--luxury-gold)]" />
|
||||||
</div>
|
</div>
|
||||||
) : (
|
) : (
|
||||||
<form onSubmit={handleSubmit(onSubmit)} className="space-y-6 relative">
|
<form onSubmit={handleSubmit(onSubmit)} className="space-y-6 relative">
|
||||||
@@ -602,13 +602,13 @@ const LuxuryBookingModal: React.FC<LuxuryBookingModalProps> = ({
|
|||||||
<div className="space-y-4">
|
<div className="space-y-4">
|
||||||
<div>
|
<div>
|
||||||
<h3 className="text-lg font-serif font-semibold text-white mb-4 flex items-center gap-2">
|
<h3 className="text-lg font-serif font-semibold text-white mb-4 flex items-center gap-2">
|
||||||
<Calendar className="w-5 h-5 text-[#d4af37]" />
|
<Calendar className="w-5 h-5 text-[var(--luxury-gold)]" />
|
||||||
Select Dates
|
Select Dates
|
||||||
</h3>
|
</h3>
|
||||||
<div className="grid grid-cols-1 sm:grid-cols-2 gap-4">
|
<div className="grid grid-cols-1 sm:grid-cols-2 gap-4">
|
||||||
<div>
|
<div>
|
||||||
<label className="block text-xs font-medium text-gray-300 mb-2">
|
<label className="block text-xs font-medium text-gray-300 mb-2">
|
||||||
Check-in Date <span className="text-[#d4af37]">*</span>
|
Check-in Date <span className="text-[var(--luxury-gold)]">*</span>
|
||||||
</label>
|
</label>
|
||||||
<Controller
|
<Controller
|
||||||
control={control}
|
control={control}
|
||||||
@@ -631,7 +631,7 @@ const LuxuryBookingModal: React.FC<LuxuryBookingModalProps> = ({
|
|||||||
}
|
}
|
||||||
field.onChange(date);
|
field.onChange(date);
|
||||||
}}
|
}}
|
||||||
className="w-full px-3 py-2 bg-[#0a0a0a] border border-[#d4af37]/20 rounded-lg text-white text-sm focus:ring-2 focus:ring-[#d4af37]/50 focus:border-[#d4af37]"
|
className="w-full px-3 py-2 bg-[#0a0a0a] border border-[var(--luxury-gold)]/20 rounded-lg text-white text-sm focus:ring-2 focus:ring-[var(--luxury-gold)]/50 focus:border-[var(--luxury-gold)]"
|
||||||
wrapperClassName="w-full"
|
wrapperClassName="w-full"
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
@@ -642,7 +642,7 @@ const LuxuryBookingModal: React.FC<LuxuryBookingModalProps> = ({
|
|||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<label className="block text-xs font-medium text-gray-300 mb-2">
|
<label className="block text-xs font-medium text-gray-300 mb-2">
|
||||||
Check-out Date <span className="text-[#d4af37]">*</span>
|
Check-out Date <span className="text-[var(--luxury-gold)]">*</span>
|
||||||
</label>
|
</label>
|
||||||
<Controller
|
<Controller
|
||||||
control={control}
|
control={control}
|
||||||
@@ -668,7 +668,7 @@ const LuxuryBookingModal: React.FC<LuxuryBookingModalProps> = ({
|
|||||||
}
|
}
|
||||||
field.onChange(date);
|
field.onChange(date);
|
||||||
}}
|
}}
|
||||||
className="w-full px-3 py-2 bg-[#0a0a0a] border border-[#d4af37]/20 rounded-lg text-white text-sm focus:ring-2 focus:ring-[#d4af37]/50 focus:border-[#d4af37]"
|
className="w-full px-3 py-2 bg-[#0a0a0a] border border-[var(--luxury-gold)]/20 rounded-lg text-white text-sm focus:ring-2 focus:ring-[var(--luxury-gold)]/50 focus:border-[var(--luxury-gold)]"
|
||||||
wrapperClassName="w-full"
|
wrapperClassName="w-full"
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
@@ -679,10 +679,10 @@ const LuxuryBookingModal: React.FC<LuxuryBookingModalProps> = ({
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{checkInDate && checkOutDate && (
|
{checkInDate && checkOutDate && (
|
||||||
<div className="mt-4 p-3 bg-[#d4af37]/10 border border-[#d4af37]/20 rounded-lg">
|
<div className="mt-4 p-3 bg-[var(--luxury-gold)]/10 border border-[var(--luxury-gold)]/20 rounded-lg">
|
||||||
<p className="text-sm text-gray-300">
|
<p className="text-sm text-gray-300">
|
||||||
<span className="text-[#d4af37] font-semibold">{numberOfNights}</span> night{numberOfNights !== 1 ? 's' : ''} •
|
<span className="text-[var(--luxury-gold)] font-semibold">{numberOfNights}</span> night{numberOfNights !== 1 ? 's' : ''} •
|
||||||
<span className="text-[#d4af37] font-semibold ml-1">{formatCurrency(roomTotal)}</span>
|
<span className="text-[var(--luxury-gold)] font-semibold ml-1">{formatCurrency(roomTotal)}</span>
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
@@ -695,18 +695,18 @@ const LuxuryBookingModal: React.FC<LuxuryBookingModalProps> = ({
|
|||||||
<div className="space-y-4">
|
<div className="space-y-4">
|
||||||
<div>
|
<div>
|
||||||
<h3 className="text-lg font-serif font-semibold text-white mb-4 flex items-center gap-2">
|
<h3 className="text-lg font-serif font-semibold text-white mb-4 flex items-center gap-2">
|
||||||
<Users className="w-5 h-5 text-[#d4af37]" />
|
<Users className="w-5 h-5 text-[var(--luxury-gold)]" />
|
||||||
Guest Information
|
Guest Information
|
||||||
</h3>
|
</h3>
|
||||||
<div className="space-y-4">
|
<div className="space-y-4">
|
||||||
<div>
|
<div>
|
||||||
<label className="block text-xs font-medium text-gray-300 mb-2">
|
<label className="block text-xs font-medium text-gray-300 mb-2">
|
||||||
Full Name <span className="text-[#d4af37]">*</span>
|
Full Name <span className="text-[var(--luxury-gold)]">*</span>
|
||||||
</label>
|
</label>
|
||||||
<input
|
<input
|
||||||
{...register('fullName')}
|
{...register('fullName')}
|
||||||
type="text"
|
type="text"
|
||||||
className="w-full px-3 py-2 bg-[#0a0a0a] border border-[#d4af37]/20 rounded-lg text-white text-sm focus:ring-2 focus:ring-[#d4af37]/50 focus:border-[#d4af37]"
|
className="w-full px-3 py-2 bg-[#0a0a0a] border border-[var(--luxury-gold)]/20 rounded-lg text-white text-sm focus:ring-2 focus:ring-[var(--luxury-gold)]/50 focus:border-[var(--luxury-gold)]"
|
||||||
placeholder="John Doe"
|
placeholder="John Doe"
|
||||||
/>
|
/>
|
||||||
{errors.fullName && (
|
{errors.fullName && (
|
||||||
@@ -716,12 +716,12 @@ const LuxuryBookingModal: React.FC<LuxuryBookingModalProps> = ({
|
|||||||
<div className="grid grid-cols-1 sm:grid-cols-2 gap-4">
|
<div className="grid grid-cols-1 sm:grid-cols-2 gap-4">
|
||||||
<div>
|
<div>
|
||||||
<label className="block text-xs font-medium text-gray-300 mb-2">
|
<label className="block text-xs font-medium text-gray-300 mb-2">
|
||||||
Email <span className="text-[#d4af37]">*</span>
|
Email <span className="text-[var(--luxury-gold)]">*</span>
|
||||||
</label>
|
</label>
|
||||||
<input
|
<input
|
||||||
{...register('email')}
|
{...register('email')}
|
||||||
type="email"
|
type="email"
|
||||||
className="w-full px-3 py-2 bg-[#0a0a0a] border border-[#d4af37]/20 rounded-lg text-white text-sm focus:ring-2 focus:ring-[#d4af37]/50 focus:border-[#d4af37]"
|
className="w-full px-3 py-2 bg-[#0a0a0a] border border-[var(--luxury-gold)]/20 rounded-lg text-white text-sm focus:ring-2 focus:ring-[var(--luxury-gold)]/50 focus:border-[var(--luxury-gold)]"
|
||||||
placeholder="email@example.com"
|
placeholder="email@example.com"
|
||||||
/>
|
/>
|
||||||
{errors.email && (
|
{errors.email && (
|
||||||
@@ -730,12 +730,12 @@ const LuxuryBookingModal: React.FC<LuxuryBookingModalProps> = ({
|
|||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<label className="block text-xs font-medium text-gray-300 mb-2">
|
<label className="block text-xs font-medium text-gray-300 mb-2">
|
||||||
Phone <span className="text-[#d4af37]">*</span>
|
Phone <span className="text-[var(--luxury-gold)]">*</span>
|
||||||
</label>
|
</label>
|
||||||
<input
|
<input
|
||||||
{...register('phone')}
|
{...register('phone')}
|
||||||
type="tel"
|
type="tel"
|
||||||
className="w-full px-3 py-2 bg-[#0a0a0a] border border-[#d4af37]/20 rounded-lg text-white text-sm focus:ring-2 focus:ring-[#d4af37]/50 focus:border-[#d4af37]"
|
className="w-full px-3 py-2 bg-[#0a0a0a] border border-[var(--luxury-gold)]/20 rounded-lg text-white text-sm focus:ring-2 focus:ring-[var(--luxury-gold)]/50 focus:border-[var(--luxury-gold)]"
|
||||||
placeholder="0123456789"
|
placeholder="0123456789"
|
||||||
/>
|
/>
|
||||||
{errors.phone && (
|
{errors.phone && (
|
||||||
@@ -745,14 +745,14 @@ const LuxuryBookingModal: React.FC<LuxuryBookingModalProps> = ({
|
|||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<label className="block text-xs font-medium text-gray-300 mb-2">
|
<label className="block text-xs font-medium text-gray-300 mb-2">
|
||||||
Number of Guests <span className="text-[#d4af37]">*</span>
|
Number of Guests <span className="text-[var(--luxury-gold)]">*</span>
|
||||||
</label>
|
</label>
|
||||||
<input
|
<input
|
||||||
{...register('guestCount')}
|
{...register('guestCount')}
|
||||||
type="number"
|
type="number"
|
||||||
min="1"
|
min="1"
|
||||||
max={room?.capacity || room?.room_type?.capacity || 10}
|
max={room?.capacity || room?.room_type?.capacity || 10}
|
||||||
className="w-full px-3 py-2 bg-[#0a0a0a] border border-[#d4af37]/20 rounded-lg text-white text-sm focus:ring-2 focus:ring-[#d4af37]/50 focus:border-[#d4af37]"
|
className="w-full px-3 py-2 bg-[#0a0a0a] border border-[var(--luxury-gold)]/20 rounded-lg text-white text-sm focus:ring-2 focus:ring-[var(--luxury-gold)]/50 focus:border-[var(--luxury-gold)]"
|
||||||
/>
|
/>
|
||||||
{errors.guestCount && (
|
{errors.guestCount && (
|
||||||
<p className="text-xs text-red-400 mt-1">{errors.guestCount.message}</p>
|
<p className="text-xs text-red-400 mt-1">{errors.guestCount.message}</p>
|
||||||
@@ -765,7 +765,7 @@ const LuxuryBookingModal: React.FC<LuxuryBookingModalProps> = ({
|
|||||||
<textarea
|
<textarea
|
||||||
{...register('notes')}
|
{...register('notes')}
|
||||||
rows={2}
|
rows={2}
|
||||||
className="w-full px-3 py-2 bg-[#0a0a0a] border border-[#d4af37]/20 rounded-lg text-white text-sm focus:ring-2 focus:ring-[#d4af37]/50 focus:border-[#d4af37] resize-none"
|
className="w-full px-3 py-2 bg-[#0a0a0a] border border-[var(--luxury-gold)]/20 rounded-lg text-white text-sm focus:ring-2 focus:ring-[var(--luxury-gold)]/50 focus:border-[var(--luxury-gold)] resize-none"
|
||||||
placeholder="Any special requests..."
|
placeholder="Any special requests..."
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
@@ -779,7 +779,7 @@ const LuxuryBookingModal: React.FC<LuxuryBookingModalProps> = ({
|
|||||||
<div className="space-y-4">
|
<div className="space-y-4">
|
||||||
<div>
|
<div>
|
||||||
<h3 className="text-lg font-serif font-semibold text-white mb-4 flex items-center gap-2">
|
<h3 className="text-lg font-serif font-semibold text-white mb-4 flex items-center gap-2">
|
||||||
<Sparkles className="w-5 h-5 text-[#d4af37]" />
|
<Sparkles className="w-5 h-5 text-[var(--luxury-gold)]" />
|
||||||
Additional Services
|
Additional Services
|
||||||
</h3>
|
</h3>
|
||||||
{services.length > 0 ? (
|
{services.length > 0 ? (
|
||||||
@@ -790,7 +790,7 @@ const LuxuryBookingModal: React.FC<LuxuryBookingModalProps> = ({
|
|||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
key={service.id}
|
key={service.id}
|
||||||
className="p-3 bg-[#0a0a0a] border border-[#d4af37]/20 rounded-lg"
|
className="p-3 bg-[#0a0a0a] border border-[var(--luxury-gold)]/20 rounded-lg"
|
||||||
>
|
>
|
||||||
<div className="flex items-center justify-between">
|
<div className="flex items-center justify-between">
|
||||||
<div className="flex-1">
|
<div className="flex-1">
|
||||||
@@ -815,7 +815,7 @@ const LuxuryBookingModal: React.FC<LuxuryBookingModalProps> = ({
|
|||||||
}
|
}
|
||||||
}}
|
}}
|
||||||
disabled={quantity === 0}
|
disabled={quantity === 0}
|
||||||
className="w-7 h-7 flex items-center justify-center bg-[#1a1a1a] border border-[#d4af37]/20 rounded text-[#d4af37] hover:bg-[#d4af37]/10 disabled:opacity-50"
|
className="w-7 h-7 flex items-center justify-center bg-[#1a1a1a] border border-[var(--luxury-gold)]/20 rounded text-[var(--luxury-gold)] hover:bg-[var(--luxury-gold)]/10 disabled:opacity-50"
|
||||||
>
|
>
|
||||||
<Minus className="w-3.5 h-3.5" />
|
<Minus className="w-3.5 h-3.5" />
|
||||||
</button>
|
</button>
|
||||||
@@ -833,13 +833,13 @@ const LuxuryBookingModal: React.FC<LuxuryBookingModalProps> = ({
|
|||||||
));
|
));
|
||||||
}
|
}
|
||||||
}}
|
}}
|
||||||
className="w-7 h-7 flex items-center justify-center bg-[#1a1a1a] border border-[#d4af37]/20 rounded text-[#d4af37] hover:bg-[#d4af37]/10"
|
className="w-7 h-7 flex items-center justify-center bg-[#1a1a1a] border border-[var(--luxury-gold)]/20 rounded text-[var(--luxury-gold)] hover:bg-[var(--luxury-gold)]/10"
|
||||||
>
|
>
|
||||||
<Plus className="w-3.5 h-3.5" />
|
<Plus className="w-3.5 h-3.5" />
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
{quantity > 0 && (
|
{quantity > 0 && (
|
||||||
<span className="text-sm font-semibold text-[#d4af37] w-20 text-right">
|
<span className="text-sm font-semibold text-[var(--luxury-gold)] w-20 text-right">
|
||||||
{formatCurrency(service.price * quantity)}
|
{formatCurrency(service.price * quantity)}
|
||||||
</span>
|
</span>
|
||||||
)}
|
)}
|
||||||
@@ -854,7 +854,7 @@ const LuxuryBookingModal: React.FC<LuxuryBookingModalProps> = ({
|
|||||||
)}
|
)}
|
||||||
|
|
||||||
{/* Promotion Code */}
|
{/* Promotion Code */}
|
||||||
<div className="mt-6 pt-6 border-t border-[#d4af37]/20">
|
<div className="mt-6 pt-6 border-t border-[var(--luxury-gold)]/20">
|
||||||
<h4 className="text-sm font-semibold text-white mb-3">Promotion Code</h4>
|
<h4 className="text-sm font-semibold text-white mb-3">Promotion Code</h4>
|
||||||
{!selectedPromotion ? (
|
{!selectedPromotion ? (
|
||||||
<div className="flex gap-2">
|
<div className="flex gap-2">
|
||||||
@@ -866,14 +866,14 @@ const LuxuryBookingModal: React.FC<LuxuryBookingModalProps> = ({
|
|||||||
setPromotionError(null);
|
setPromotionError(null);
|
||||||
}}
|
}}
|
||||||
placeholder="Enter code"
|
placeholder="Enter code"
|
||||||
className="flex-1 px-3 py-2 bg-[#0a0a0a] border border-[#d4af37]/20 rounded-lg text-white text-sm focus:ring-2 focus:ring-[#d4af37]/50 focus:border-[#d4af37]"
|
className="flex-1 px-3 py-2 bg-[#0a0a0a] border border-[var(--luxury-gold)]/20 rounded-lg text-white text-sm focus:ring-2 focus:ring-[var(--luxury-gold)]/50 focus:border-[var(--luxury-gold)]"
|
||||||
disabled={validatingPromotion || subtotal === 0}
|
disabled={validatingPromotion || subtotal === 0}
|
||||||
/>
|
/>
|
||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
onClick={handleValidatePromotion}
|
onClick={handleValidatePromotion}
|
||||||
disabled={validatingPromotion || !promotionCode.trim() || subtotal === 0}
|
disabled={validatingPromotion || !promotionCode.trim() || subtotal === 0}
|
||||||
className="px-4 py-2 bg-[#d4af37] text-black font-semibold rounded-lg hover:bg-[#c9a227] disabled:opacity-50 disabled:cursor-not-allowed text-sm"
|
className="px-4 py-2 bg-[var(--luxury-gold)] text-black font-semibold rounded-lg hover:bg-[var(--luxury-gold-dark)] disabled:opacity-50 disabled:cursor-not-allowed text-sm"
|
||||||
>
|
>
|
||||||
{validatingPromotion ? <Loader2 className="w-4 h-4 animate-spin" /> : 'Apply'}
|
{validatingPromotion ? <Loader2 className="w-4 h-4 animate-spin" /> : 'Apply'}
|
||||||
</button>
|
</button>
|
||||||
@@ -897,9 +897,9 @@ const LuxuryBookingModal: React.FC<LuxuryBookingModalProps> = ({
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Referral Code */}
|
{/* Referral Code */}
|
||||||
<div className="mt-6 pt-6 border-t border-[#d4af37]/20">
|
<div className="mt-6 pt-6 border-t border-[var(--luxury-gold)]/20">
|
||||||
<div className="flex items-center gap-2 mb-3">
|
<div className="flex items-center gap-2 mb-3">
|
||||||
<Users className="w-4 h-4 text-[#d4af37]" />
|
<Users className="w-4 h-4 text-[var(--luxury-gold)]" />
|
||||||
<h4 className="text-sm font-semibold text-white">Referral Code (Optional)</h4>
|
<h4 className="text-sm font-semibold text-white">Referral Code (Optional)</h4>
|
||||||
</div>
|
</div>
|
||||||
<div className="space-y-2">
|
<div className="space-y-2">
|
||||||
@@ -911,7 +911,7 @@ const LuxuryBookingModal: React.FC<LuxuryBookingModalProps> = ({
|
|||||||
setReferralError(null);
|
setReferralError(null);
|
||||||
}}
|
}}
|
||||||
placeholder="Enter referral code from a friend"
|
placeholder="Enter referral code from a friend"
|
||||||
className="w-full px-3 py-2 bg-[#0a0a0a] border border-[#d4af37]/20 rounded-lg text-white text-sm focus:ring-2 focus:ring-[#d4af37]/50 focus:border-[#d4af37]"
|
className="w-full px-3 py-2 bg-[#0a0a0a] border border-[var(--luxury-gold)]/20 rounded-lg text-white text-sm focus:ring-2 focus:ring-[var(--luxury-gold)]/50 focus:border-[var(--luxury-gold)]"
|
||||||
/>
|
/>
|
||||||
{referralCode && (
|
{referralCode && (
|
||||||
<p className="text-xs text-gray-400">
|
<p className="text-xs text-gray-400">
|
||||||
@@ -930,51 +930,51 @@ const LuxuryBookingModal: React.FC<LuxuryBookingModalProps> = ({
|
|||||||
<div className="space-y-4">
|
<div className="space-y-4">
|
||||||
<div>
|
<div>
|
||||||
<h3 className="text-lg font-serif font-semibold text-white mb-4 flex items-center gap-2">
|
<h3 className="text-lg font-serif font-semibold text-white mb-4 flex items-center gap-2">
|
||||||
<CreditCard className="w-5 h-5 text-[#d4af37]" />
|
<CreditCard className="w-5 h-5 text-[var(--luxury-gold)]" />
|
||||||
Payment Method
|
Payment Method
|
||||||
</h3>
|
</h3>
|
||||||
<div className="space-y-3">
|
<div className="space-y-3">
|
||||||
<label className="flex items-start p-4 bg-[#0a0a0a] border-2 border-[#d4af37]/20 rounded-lg cursor-pointer hover:border-[#d4af37]/40 transition-all">
|
<label className="flex items-start p-4 bg-[#0a0a0a] border-2 border-[var(--luxury-gold)]/20 rounded-lg cursor-pointer hover:border-[var(--luxury-gold)]/40 transition-all">
|
||||||
<input
|
<input
|
||||||
{...register('paymentMethod')}
|
{...register('paymentMethod')}
|
||||||
type="radio"
|
type="radio"
|
||||||
value="cash"
|
value="cash"
|
||||||
className="mt-1 mr-3 w-4 h-4 text-[#d4af37] border-[#d4af37]/30 focus:ring-[#d4af37]/50"
|
className="mt-1 mr-3 w-4 h-4 text-[var(--luxury-gold)] border-[var(--luxury-gold)]/30 focus:ring-[var(--luxury-gold)]/50"
|
||||||
/>
|
/>
|
||||||
<div className="flex-1">
|
<div className="flex-1">
|
||||||
<div className="flex items-center gap-2 mb-1">
|
<div className="flex items-center gap-2 mb-1">
|
||||||
<CreditCard className="w-4 h-4 text-[#d4af37]" />
|
<CreditCard className="w-4 h-4 text-[var(--luxury-gold)]" />
|
||||||
<span className="font-semibold text-white text-sm">Pay on Arrival</span>
|
<span className="font-semibold text-white text-sm">Pay on Arrival</span>
|
||||||
<span className="text-xs bg-orange-500/20 text-orange-300 px-2 py-0.5 rounded">20% deposit</span>
|
<span className="text-xs bg-orange-500/20 text-orange-300 px-2 py-0.5 rounded">20% deposit</span>
|
||||||
</div>
|
</div>
|
||||||
<p className="text-xs text-gray-400">Pay 20% deposit now, remaining on arrival</p>
|
<p className="text-xs text-gray-400">Pay 20% deposit now, remaining on arrival</p>
|
||||||
</div>
|
</div>
|
||||||
</label>
|
</label>
|
||||||
<label className="flex items-start p-4 bg-[#0a0a0a] border-2 border-[#d4af37]/20 rounded-lg cursor-pointer hover:border-[#d4af37]/40 transition-all">
|
<label className="flex items-start p-4 bg-[#0a0a0a] border-2 border-[var(--luxury-gold)]/20 rounded-lg cursor-pointer hover:border-[var(--luxury-gold)]/40 transition-all">
|
||||||
<input
|
<input
|
||||||
{...register('paymentMethod')}
|
{...register('paymentMethod')}
|
||||||
type="radio"
|
type="radio"
|
||||||
value="stripe"
|
value="stripe"
|
||||||
className="mt-1 mr-3 w-4 h-4 text-[#d4af37] border-[#d4af37]/30 focus:ring-[#d4af37]/50"
|
className="mt-1 mr-3 w-4 h-4 text-[var(--luxury-gold)] border-[var(--luxury-gold)]/30 focus:ring-[var(--luxury-gold)]/50"
|
||||||
/>
|
/>
|
||||||
<div className="flex-1">
|
<div className="flex-1">
|
||||||
<div className="flex items-center gap-2 mb-1">
|
<div className="flex items-center gap-2 mb-1">
|
||||||
<CreditCard className="w-4 h-4 text-[#d4af37]" />
|
<CreditCard className="w-4 h-4 text-[var(--luxury-gold)]" />
|
||||||
<span className="font-semibold text-white text-sm">Credit/Debit Card</span>
|
<span className="font-semibold text-white text-sm">Credit/Debit Card</span>
|
||||||
</div>
|
</div>
|
||||||
<p className="text-xs text-gray-400">Secure payment via Stripe</p>
|
<p className="text-xs text-gray-400">Secure payment via Stripe</p>
|
||||||
</div>
|
</div>
|
||||||
</label>
|
</label>
|
||||||
<label className="flex items-start p-4 bg-[#0a0a0a] border-2 border-[#d4af37]/20 rounded-lg cursor-pointer hover:border-[#d4af37]/40 transition-all">
|
<label className="flex items-start p-4 bg-[#0a0a0a] border-2 border-[var(--luxury-gold)]/20 rounded-lg cursor-pointer hover:border-[var(--luxury-gold)]/40 transition-all">
|
||||||
<input
|
<input
|
||||||
{...register('paymentMethod')}
|
{...register('paymentMethod')}
|
||||||
type="radio"
|
type="radio"
|
||||||
value="paypal"
|
value="paypal"
|
||||||
className="mt-1 mr-3 w-4 h-4 text-[#d4af37] border-[#d4af37]/30 focus:ring-[#d4af37]/50"
|
className="mt-1 mr-3 w-4 h-4 text-[var(--luxury-gold)] border-[var(--luxury-gold)]/30 focus:ring-[var(--luxury-gold)]/50"
|
||||||
/>
|
/>
|
||||||
<div className="flex-1">
|
<div className="flex-1">
|
||||||
<div className="flex items-center gap-2 mb-1">
|
<div className="flex items-center gap-2 mb-1">
|
||||||
<CreditCard className="w-4 h-4 text-[#d4af37]" />
|
<CreditCard className="w-4 h-4 text-[var(--luxury-gold)]" />
|
||||||
<span className="font-semibold text-white text-sm">PayPal</span>
|
<span className="font-semibold text-white text-sm">PayPal</span>
|
||||||
</div>
|
</div>
|
||||||
<p className="text-xs text-gray-400">Pay securely with PayPal</p>
|
<p className="text-xs text-gray-400">Pay securely with PayPal</p>
|
||||||
@@ -987,7 +987,7 @@ const LuxuryBookingModal: React.FC<LuxuryBookingModalProps> = ({
|
|||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
onClick={() => setShowInvoiceModal(true)}
|
onClick={() => setShowInvoiceModal(true)}
|
||||||
className="flex items-center gap-2 px-4 py-2 text-sm text-[#d4af37] hover:text-[#f5d76e] transition-colors"
|
className="flex items-center gap-2 px-4 py-2 text-sm text-[var(--luxury-gold)] hover:text-[var(--luxury-gold-light)] transition-colors"
|
||||||
>
|
>
|
||||||
<Receipt className="w-4 h-4" />
|
<Receipt className="w-4 h-4" />
|
||||||
Add Invoice Information (Optional)
|
Add Invoice Information (Optional)
|
||||||
@@ -995,7 +995,7 @@ const LuxuryBookingModal: React.FC<LuxuryBookingModalProps> = ({
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Summary */}
|
{/* Summary */}
|
||||||
<div className="mt-6 p-4 bg-[#0a0a0a] border border-[#d4af37]/20 rounded-lg">
|
<div className="mt-6 p-4 bg-[#0a0a0a] border border-[var(--luxury-gold)]/20 rounded-lg">
|
||||||
<div className="space-y-2 text-sm">
|
<div className="space-y-2 text-sm">
|
||||||
<div className="flex justify-between text-gray-300">
|
<div className="flex justify-between text-gray-300">
|
||||||
<span>Room ({numberOfNights} nights)</span>
|
<span>Room ({numberOfNights} nights)</span>
|
||||||
@@ -1013,7 +1013,7 @@ const LuxuryBookingModal: React.FC<LuxuryBookingModalProps> = ({
|
|||||||
<span>-{formatCurrency(promotionDiscount)}</span>
|
<span>-{formatCurrency(promotionDiscount)}</span>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
<div className="flex justify-between text-lg font-bold text-[#d4af37] pt-2 border-t border-[#d4af37]/20">
|
<div className="flex justify-between text-lg font-bold text-[var(--luxury-gold)] pt-2 border-t border-[var(--luxury-gold)]/20">
|
||||||
<span>Total</span>
|
<span>Total</span>
|
||||||
<span>{formatCurrency(totalPrice)}</span>
|
<span>{formatCurrency(totalPrice)}</span>
|
||||||
</div>
|
</div>
|
||||||
@@ -1057,7 +1057,7 @@ const LuxuryBookingModal: React.FC<LuxuryBookingModalProps> = ({
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Footer */}
|
{/* Footer */}
|
||||||
<div className="px-3 sm:px-4 md:px-6 py-3 sm:py-4 border-t border-[#d4af37]/20 bg-gradient-to-r from-[#1a1a1a] to-[#0a0a0a] flex-shrink-0">
|
<div className="px-3 sm:px-4 md:px-6 py-3 sm:py-4 border-t border-[var(--luxury-gold)]/20 bg-gradient-to-r from-[#1a1a1a] to-[#0a0a0a] flex-shrink-0">
|
||||||
<div className="flex items-center justify-between gap-2 sm:gap-3">
|
<div className="flex items-center justify-between gap-2 sm:gap-3">
|
||||||
<button
|
<button
|
||||||
onClick={currentStep === 'dates' ? onClose : handleBack}
|
onClick={currentStep === 'dates' ? onClose : handleBack}
|
||||||
@@ -1071,7 +1071,7 @@ const LuxuryBookingModal: React.FC<LuxuryBookingModalProps> = ({
|
|||||||
<button
|
<button
|
||||||
onClick={handleNext}
|
onClick={handleNext}
|
||||||
disabled={!canGoToNextStep()}
|
disabled={!canGoToNextStep()}
|
||||||
className="px-4 sm:px-6 py-2 bg-[#d4af37] text-black font-semibold rounded-lg hover:bg-[#c9a227] disabled:opacity-50 disabled:cursor-not-allowed transition-all flex items-center gap-1.5 sm:gap-2 text-xs sm:text-sm sm:text-base min-h-[44px]"
|
className="px-4 sm:px-6 py-2 bg-[var(--luxury-gold)] text-black font-semibold rounded-lg hover:bg-[var(--luxury-gold-dark)] disabled:opacity-50 disabled:cursor-not-allowed transition-all flex items-center gap-1.5 sm:gap-2 text-xs sm:text-sm sm:text-base min-h-[44px]"
|
||||||
>
|
>
|
||||||
<span>Next</span>
|
<span>Next</span>
|
||||||
<ArrowRight className="w-3.5 h-3.5 sm:w-4 sm:h-4" />
|
<ArrowRight className="w-3.5 h-3.5 sm:w-4 sm:h-4" />
|
||||||
@@ -1102,7 +1102,7 @@ const LuxuryBookingModal: React.FC<LuxuryBookingModalProps> = ({
|
|||||||
})();
|
})();
|
||||||
}}
|
}}
|
||||||
disabled={submitting || !paymentMethodForm}
|
disabled={submitting || !paymentMethodForm}
|
||||||
className="px-4 sm:px-6 py-2 bg-[#d4af37] text-black font-semibold rounded-lg hover:bg-[#c9a227] disabled:opacity-50 disabled:cursor-not-allowed transition-all flex items-center gap-1.5 sm:gap-2 text-xs sm:text-sm sm:text-base min-h-[44px]"
|
className="px-4 sm:px-6 py-2 bg-[var(--luxury-gold)] text-black font-semibold rounded-lg hover:bg-[var(--luxury-gold-dark)] disabled:opacity-50 disabled:cursor-not-allowed transition-all flex items-center gap-1.5 sm:gap-2 text-xs sm:text-sm sm:text-base min-h-[44px]"
|
||||||
>
|
>
|
||||||
{submitting ? (
|
{submitting ? (
|
||||||
<>
|
<>
|
||||||
|
|||||||
@@ -223,11 +223,11 @@ const PartnersCarousel: React.FC<PartnersCarouselProps> = ({
|
|||||||
disabled={currentIndex === 0}
|
disabled={currentIndex === 0}
|
||||||
className="absolute left-0 top-1/2 -translate-y-1/2 z-10
|
className="absolute left-0 top-1/2 -translate-y-1/2 z-10
|
||||||
w-10 h-10 sm:w-12 sm:h-12
|
w-10 h-10 sm:w-12 sm:h-12
|
||||||
bg-gradient-to-r from-[#d4af37] to-[#c9a227]
|
bg-gradient-to-r from-[var(--luxury-gold)] to-[var(--luxury-gold-dark)]
|
||||||
text-white rounded-full
|
text-white rounded-full
|
||||||
flex items-center justify-center
|
flex items-center justify-center
|
||||||
shadow-lg shadow-[#d4af37]/40
|
shadow-lg shadow-[var(--luxury-gold)]/40
|
||||||
hover:shadow-xl hover:shadow-[#d4af37]/50
|
hover:shadow-xl hover:shadow-[var(--luxury-gold)]/50
|
||||||
hover:scale-110
|
hover:scale-110
|
||||||
active:scale-95
|
active:scale-95
|
||||||
transition-all duration-300
|
transition-all duration-300
|
||||||
@@ -243,11 +243,11 @@ const PartnersCarousel: React.FC<PartnersCarouselProps> = ({
|
|||||||
disabled={currentIndex >= maxIndex}
|
disabled={currentIndex >= maxIndex}
|
||||||
className="absolute right-0 top-1/2 -translate-y-1/2 z-10
|
className="absolute right-0 top-1/2 -translate-y-1/2 z-10
|
||||||
w-10 h-10 sm:w-12 sm:h-12
|
w-10 h-10 sm:w-12 sm:h-12
|
||||||
bg-gradient-to-r from-[#d4af37] to-[#c9a227]
|
bg-gradient-to-r from-[var(--luxury-gold)] to-[var(--luxury-gold-dark)]
|
||||||
text-white rounded-full
|
text-white rounded-full
|
||||||
flex items-center justify-center
|
flex items-center justify-center
|
||||||
shadow-lg shadow-[#d4af37]/40
|
shadow-lg shadow-[var(--luxury-gold)]/40
|
||||||
hover:shadow-xl hover:shadow-[#d4af37]/50
|
hover:shadow-xl hover:shadow-[var(--luxury-gold)]/50
|
||||||
hover:scale-110
|
hover:scale-110
|
||||||
active:scale-95
|
active:scale-95
|
||||||
transition-all duration-300
|
transition-all duration-300
|
||||||
@@ -270,8 +270,8 @@ const PartnersCarousel: React.FC<PartnersCarouselProps> = ({
|
|||||||
className={`transition-all duration-300 rounded-full
|
className={`transition-all duration-300 rounded-full
|
||||||
${
|
${
|
||||||
index === currentIndex
|
index === currentIndex
|
||||||
? 'w-8 h-2 sm:w-10 sm:h-2.5 bg-gradient-to-r from-[#d4af37] to-[#c9a227] shadow-lg shadow-[#d4af37]/40'
|
? 'w-8 h-2 sm:w-10 sm:h-2.5 bg-gradient-to-r from-[var(--luxury-gold)] to-[var(--luxury-gold-dark)] shadow-lg shadow-[var(--luxury-gold)]/40'
|
||||||
: 'w-2 h-2 sm:w-2.5 sm:h-2.5 bg-gray-300 hover:bg-[#d4af37]/50'
|
: 'w-2 h-2 sm:w-2.5 sm:h-2.5 bg-gray-300 hover:bg-[var(--luxury-gold)]/50'
|
||||||
}
|
}
|
||||||
`}
|
`}
|
||||||
aria-label={`Go to partners slide ${index + 1}`}
|
aria-label={`Go to partners slide ${index + 1}`}
|
||||||
|
|||||||
@@ -178,7 +178,7 @@ const AboutPage: React.FC = () => {
|
|||||||
className="w-full h-full object-cover scale-105 transition-transform duration-[20s] ease-out hover:scale-100"
|
className="w-full h-full object-cover scale-105 transition-transform duration-[20s] ease-out hover:scale-100"
|
||||||
/>
|
/>
|
||||||
<div className="absolute inset-0 bg-gradient-to-b from-black/70 via-black/50 to-black/70"></div>
|
<div className="absolute inset-0 bg-gradient-to-b from-black/70 via-black/50 to-black/70"></div>
|
||||||
<div className="absolute inset-0 bg-gradient-to-r from-[#d4af37]/10 via-transparent to-[#d4af37]/10"></div>
|
<div className="absolute inset-0 bg-gradient-to-r from-[var(--luxury-gold)]/10 via-transparent to-[var(--luxury-gold)]/10"></div>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
{!pageContent?.about_hero_image && (
|
{!pageContent?.about_hero_image && (
|
||||||
@@ -192,8 +192,8 @@ const AboutPage: React.FC = () => {
|
|||||||
{!pageContent?.about_hero_image && (
|
{!pageContent?.about_hero_image && (
|
||||||
<div className="flex justify-center mb-8">
|
<div className="flex justify-center mb-8">
|
||||||
<div className="relative">
|
<div className="relative">
|
||||||
<div className="absolute inset-0 bg-gradient-to-br from-[#d4af37] via-[#f5d76e] to-[#d4af37] rounded-full blur-3xl opacity-40 animate-pulse"></div>
|
<div className="absolute inset-0 bg-gradient-to-br from-[var(--luxury-gold)] via-[var(--luxury-gold-light)] to-[var(--luxury-gold)] rounded-full blur-3xl opacity-40 animate-pulse"></div>
|
||||||
<div className="relative bg-gradient-to-br from-[#d4af37] to-[#c9a227] p-6 rounded-2xl shadow-2xl shadow-[#d4af37]/30">
|
<div className="relative bg-gradient-to-br from-[var(--luxury-gold)] to-[var(--luxury-gold-dark)] p-6 rounded-2xl shadow-2xl shadow-[var(--luxury-gold)]/30">
|
||||||
<Hotel className="w-12 h-12 md:w-16 md:h-16 text-white drop-shadow-lg" />
|
<Hotel className="w-12 h-12 md:w-16 md:h-16 text-white drop-shadow-lg" />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -201,11 +201,11 @@ const AboutPage: React.FC = () => {
|
|||||||
)}
|
)}
|
||||||
<div className="mb-6">
|
<div className="mb-6">
|
||||||
<div className="inline-block mb-4">
|
<div className="inline-block mb-4">
|
||||||
<div className="h-px w-20 bg-gradient-to-r from-transparent via-[#d4af37] to-transparent mx-auto"></div>
|
<div className="h-px w-20 bg-gradient-to-r from-transparent via-[var(--luxury-gold)] to-transparent mx-auto"></div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<h1 className="text-5xl md:text-6xl lg:text-7xl font-serif font-light mb-6 tracking-[0.02em] leading-tight">
|
<h1 className="text-5xl md:text-6xl lg:text-7xl font-serif font-light mb-6 tracking-[0.02em] leading-tight">
|
||||||
<span className="bg-gradient-to-b from-white via-[#f5d76e] to-[#d4af37] bg-clip-text text-transparent drop-shadow-2xl">
|
<span className="bg-gradient-to-b from-white via-[var(--luxury-gold-light)] to-[var(--luxury-gold)] bg-clip-text text-transparent drop-shadow-2xl">
|
||||||
{pageContent?.title || 'About Luxury Hotel'}
|
{pageContent?.title || 'About Luxury Hotel'}
|
||||||
</span>
|
</span>
|
||||||
</h1>
|
</h1>
|
||||||
@@ -214,7 +214,7 @@ const AboutPage: React.FC = () => {
|
|||||||
</p>
|
</p>
|
||||||
<div className="mt-8">
|
<div className="mt-8">
|
||||||
<div className="inline-block">
|
<div className="inline-block">
|
||||||
<div className="h-px w-20 bg-gradient-to-r from-transparent via-[#d4af37] to-transparent mx-auto"></div>
|
<div className="h-px w-20 bg-gradient-to-r from-transparent via-[var(--luxury-gold)] to-transparent mx-auto"></div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -223,20 +223,20 @@ const AboutPage: React.FC = () => {
|
|||||||
|
|
||||||
{}
|
{}
|
||||||
<section className="py-20 md:py-28 bg-white relative overflow-hidden">
|
<section className="py-20 md:py-28 bg-white relative overflow-hidden">
|
||||||
<div className="absolute top-0 left-0 w-full h-px bg-gradient-to-r from-transparent via-[#d4af37]/30 to-transparent"></div>
|
<div className="absolute top-0 left-0 w-full h-px bg-gradient-to-r from-transparent via-[var(--luxury-gold)]/30 to-transparent"></div>
|
||||||
<div className="container mx-auto px-4 sm:px-6 lg:px-8">
|
<div className="container mx-auto px-4 sm:px-6 lg:px-8">
|
||||||
<div className="max-w-5xl mx-auto">
|
<div className="max-w-5xl mx-auto">
|
||||||
<div className="text-center mb-16">
|
<div className="text-center mb-16">
|
||||||
<div className="inline-block mb-4">
|
<div className="inline-block mb-4">
|
||||||
<span className="text-sm font-semibold text-[#d4af37] tracking-[0.2em] uppercase">Our Heritage</span>
|
<span className="text-sm font-semibold text-[var(--luxury-gold)] tracking-[0.2em] uppercase">Our Heritage</span>
|
||||||
</div>
|
</div>
|
||||||
<h2 className="text-4xl md:text-5xl lg:text-6xl font-serif font-light text-gray-900 mb-6 tracking-tight">
|
<h2 className="text-4xl md:text-5xl lg:text-6xl font-serif font-light text-gray-900 mb-6 tracking-tight">
|
||||||
Our Story
|
Our Story
|
||||||
</h2>
|
</h2>
|
||||||
<div className="flex items-center justify-center gap-4 mb-6">
|
<div className="flex items-center justify-center gap-4 mb-6">
|
||||||
<div className="h-px w-12 bg-gradient-to-r from-transparent to-[#d4af37]"></div>
|
<div className="h-px w-12 bg-gradient-to-r from-transparent to-[var(--luxury-gold)]"></div>
|
||||||
<div className="w-2 h-2 bg-[#d4af37] rounded-full"></div>
|
<div className="w-2 h-2 bg-[var(--luxury-gold)] rounded-full"></div>
|
||||||
<div className="h-px w-12 bg-gradient-to-l from-transparent to-[#d4af37]"></div>
|
<div className="h-px w-12 bg-gradient-to-l from-transparent to-[var(--luxury-gold)]"></div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="prose prose-lg md:prose-xl max-w-none text-gray-700 leading-relaxed space-y-8">
|
<div className="prose prose-lg md:prose-xl max-w-none text-gray-700 leading-relaxed space-y-8">
|
||||||
@@ -247,7 +247,7 @@ const AboutPage: React.FC = () => {
|
|||||||
/>
|
/>
|
||||||
) : (
|
) : (
|
||||||
<>
|
<>
|
||||||
<p className="text-lg md:text-xl leading-relaxed font-light tracking-wide first-letter:text-5xl first-letter:font-serif first-letter:text-[#d4af37] first-letter:float-left first-letter:mr-2 first-letter:leading-none">
|
<p className="text-lg md:text-xl leading-relaxed font-light tracking-wide first-letter:text-5xl first-letter:font-serif first-letter:text-[var(--luxury-gold)] first-letter:float-left first-letter:mr-2 first-letter:leading-none">
|
||||||
Welcome to Luxury Hotel, where timeless elegance meets modern sophistication.
|
Welcome to Luxury Hotel, where timeless elegance meets modern sophistication.
|
||||||
Since our founding, we have been dedicated to providing exceptional hospitality
|
Since our founding, we have been dedicated to providing exceptional hospitality
|
||||||
and creating unforgettable memories for our guests.
|
and creating unforgettable memories for our guests.
|
||||||
@@ -267,7 +267,7 @@ const AboutPage: React.FC = () => {
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="absolute bottom-0 left-0 w-full h-px bg-gradient-to-r from-transparent via-[#d4af37]/30 to-transparent"></div>
|
<div className="absolute bottom-0 left-0 w-full h-px bg-gradient-to-r from-transparent via-[var(--luxury-gold)]/30 to-transparent"></div>
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
{}
|
{}
|
||||||
@@ -278,33 +278,33 @@ const AboutPage: React.FC = () => {
|
|||||||
<div className="max-w-7xl mx-auto">
|
<div className="max-w-7xl mx-auto">
|
||||||
<div className="text-center mb-16">
|
<div className="text-center mb-16">
|
||||||
<div className="inline-block mb-4">
|
<div className="inline-block mb-4">
|
||||||
<span className="text-sm font-semibold text-[#d4af37] tracking-[0.2em] uppercase">Core Principles</span>
|
<span className="text-sm font-semibold text-[var(--luxury-gold)] tracking-[0.2em] uppercase">Core Principles</span>
|
||||||
</div>
|
</div>
|
||||||
<h2 className="text-4xl md:text-5xl lg:text-6xl font-serif font-light text-gray-900 mb-6 tracking-tight">
|
<h2 className="text-4xl md:text-5xl lg:text-6xl font-serif font-light text-gray-900 mb-6 tracking-tight">
|
||||||
Our Values
|
Our Values
|
||||||
</h2>
|
</h2>
|
||||||
<div className="flex items-center justify-center gap-4 mb-6">
|
<div className="flex items-center justify-center gap-4 mb-6">
|
||||||
<div className="h-px w-12 bg-gradient-to-r from-transparent to-[#d4af37]"></div>
|
<div className="h-px w-12 bg-gradient-to-r from-transparent to-[var(--luxury-gold)]"></div>
|
||||||
<div className="w-2 h-2 bg-[#d4af37] rounded-full"></div>
|
<div className="w-2 h-2 bg-[var(--luxury-gold)] rounded-full"></div>
|
||||||
<div className="h-px w-12 bg-gradient-to-l from-transparent to-[#d4af37]"></div>
|
<div className="h-px w-12 bg-gradient-to-l from-transparent to-[var(--luxury-gold)]"></div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-6 lg:gap-8">
|
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-6 lg:gap-8">
|
||||||
{values.map((value, index) => (
|
{values.map((value, index) => (
|
||||||
<div
|
<div
|
||||||
key={value.title}
|
key={value.title}
|
||||||
className="group relative bg-white/80 backdrop-blur-sm p-8 rounded-2xl shadow-lg hover:shadow-2xl transition-all duration-500 border border-gray-100 hover:border-[#d4af37]/30 hover:-translate-y-2"
|
className="group relative bg-white/80 backdrop-blur-sm p-8 rounded-2xl shadow-lg hover:shadow-2xl transition-all duration-500 border border-gray-100 hover:border-[var(--luxury-gold)]/30 hover:-translate-y-2"
|
||||||
style={{ animationDelay: `${index * 0.1}s` }}
|
style={{ animationDelay: `${index * 0.1}s` }}
|
||||||
>
|
>
|
||||||
<div className="absolute top-0 right-0 w-32 h-32 bg-gradient-to-br from-[#d4af37]/5 to-transparent rounded-bl-full opacity-0 group-hover:opacity-100 transition-opacity duration-500"></div>
|
<div className="absolute top-0 right-0 w-32 h-32 bg-gradient-to-br from-[var(--luxury-gold)]/5 to-transparent rounded-bl-full opacity-0 group-hover:opacity-100 transition-opacity duration-500"></div>
|
||||||
<div className="relative">
|
<div className="relative">
|
||||||
<div className="w-16 h-16 bg-gradient-to-br from-[#d4af37] via-[#f5d76e] to-[#d4af37] rounded-xl flex items-center justify-center mb-6 shadow-lg shadow-[#d4af37]/20 group-hover:scale-110 group-hover:rotate-3 transition-transform duration-500">
|
<div className="w-16 h-16 bg-gradient-to-br from-[var(--luxury-gold)] via-[var(--luxury-gold-light)] to-[var(--luxury-gold)] rounded-xl flex items-center justify-center mb-6 shadow-lg shadow-[var(--luxury-gold)]/20 group-hover:scale-110 group-hover:rotate-3 transition-transform duration-500">
|
||||||
{(() => {
|
{(() => {
|
||||||
const ValueIcon = getIconComponent(value.icon);
|
const ValueIcon = getIconComponent(value.icon);
|
||||||
return <ValueIcon className="w-8 h-8 text-white drop-shadow-md" />;
|
return <ValueIcon className="w-8 h-8 text-white drop-shadow-md" />;
|
||||||
})()}
|
})()}
|
||||||
</div>
|
</div>
|
||||||
<h3 className="text-xl md:text-2xl font-serif font-semibold text-gray-900 mb-3 group-hover:text-[#d4af37] transition-colors duration-300">
|
<h3 className="text-xl md:text-2xl font-serif font-semibold text-gray-900 mb-3 group-hover:text-[var(--luxury-gold)] transition-colors duration-300">
|
||||||
{value.title}
|
{value.title}
|
||||||
</h3>
|
</h3>
|
||||||
<p className="text-gray-600 leading-relaxed font-light text-sm md:text-base">
|
<p className="text-gray-600 leading-relaxed font-light text-sm md:text-base">
|
||||||
@@ -327,15 +327,15 @@ const AboutPage: React.FC = () => {
|
|||||||
<div className="max-w-7xl mx-auto">
|
<div className="max-w-7xl mx-auto">
|
||||||
<div className="text-center mb-16">
|
<div className="text-center mb-16">
|
||||||
<div className="inline-block mb-4">
|
<div className="inline-block mb-4">
|
||||||
<span className="text-sm font-semibold text-[#d4af37] tracking-[0.2em] uppercase">Excellence Defined</span>
|
<span className="text-sm font-semibold text-[var(--luxury-gold)] tracking-[0.2em] uppercase">Excellence Defined</span>
|
||||||
</div>
|
</div>
|
||||||
<h2 className="text-4xl md:text-5xl lg:text-6xl font-serif font-light text-gray-900 mb-6 tracking-tight">
|
<h2 className="text-4xl md:text-5xl lg:text-6xl font-serif font-light text-gray-900 mb-6 tracking-tight">
|
||||||
Why Choose Us
|
Why Choose Us
|
||||||
</h2>
|
</h2>
|
||||||
<div className="flex items-center justify-center gap-4 mb-6">
|
<div className="flex items-center justify-center gap-4 mb-6">
|
||||||
<div className="h-px w-12 bg-gradient-to-r from-transparent to-[#d4af37]"></div>
|
<div className="h-px w-12 bg-gradient-to-r from-transparent to-[var(--luxury-gold)]"></div>
|
||||||
<div className="w-2 h-2 bg-[#d4af37] rounded-full"></div>
|
<div className="w-2 h-2 bg-[var(--luxury-gold)] rounded-full"></div>
|
||||||
<div className="h-px w-12 bg-gradient-to-l from-transparent to-[#d4af37]"></div>
|
<div className="h-px w-12 bg-gradient-to-l from-transparent to-[var(--luxury-gold)]"></div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="grid grid-cols-1 md:grid-cols-3 gap-8 lg:gap-12">
|
<div className="grid grid-cols-1 md:grid-cols-3 gap-8 lg:gap-12">
|
||||||
@@ -347,12 +347,12 @@ const AboutPage: React.FC = () => {
|
|||||||
className="group text-center p-8 relative"
|
className="group text-center p-8 relative"
|
||||||
style={{ animationDelay: `${index * 0.1}s` }}
|
style={{ animationDelay: `${index * 0.1}s` }}
|
||||||
>
|
>
|
||||||
<div className="absolute inset-0 bg-gradient-to-br from-[#d4af37]/5 to-transparent rounded-3xl opacity-0 group-hover:opacity-100 transition-opacity duration-500"></div>
|
<div className="absolute inset-0 bg-gradient-to-br from-[var(--luxury-gold)]/5 to-transparent rounded-3xl opacity-0 group-hover:opacity-100 transition-opacity duration-500"></div>
|
||||||
<div className="relative">
|
<div className="relative">
|
||||||
<div className="w-20 h-20 bg-gradient-to-br from-[#d4af37] via-[#f5d76e] to-[#d4af37] rounded-2xl flex items-center justify-center mx-auto mb-6 shadow-xl shadow-[#d4af37]/30 group-hover:scale-110 group-hover:shadow-2xl group-hover:shadow-[#d4af37]/40 transition-all duration-500">
|
<div className="w-20 h-20 bg-gradient-to-br from-[var(--luxury-gold)] via-[var(--luxury-gold-light)] to-[var(--luxury-gold)] rounded-2xl flex items-center justify-center mx-auto mb-6 shadow-xl shadow-[var(--luxury-gold)]/30 group-hover:scale-110 group-hover:shadow-2xl group-hover:shadow-[var(--luxury-gold)]/40 transition-all duration-500">
|
||||||
<FeatureIcon className="w-10 h-10 text-white drop-shadow-lg" />
|
<FeatureIcon className="w-10 h-10 text-white drop-shadow-lg" />
|
||||||
</div>
|
</div>
|
||||||
<h3 className="text-xl md:text-2xl font-serif font-semibold text-gray-900 mb-4 group-hover:text-[#d4af37] transition-colors duration-300">
|
<h3 className="text-xl md:text-2xl font-serif font-semibold text-gray-900 mb-4 group-hover:text-[var(--luxury-gold)] transition-colors duration-300">
|
||||||
{feature.title}
|
{feature.title}
|
||||||
</h3>
|
</h3>
|
||||||
<p className="text-gray-600 leading-relaxed font-light text-sm md:text-base">
|
<p className="text-gray-600 leading-relaxed font-light text-sm md:text-base">
|
||||||
@@ -377,11 +377,11 @@ const AboutPage: React.FC = () => {
|
|||||||
<div className="container mx-auto px-4 sm:px-6 lg:px-8 relative z-10">
|
<div className="container mx-auto px-4 sm:px-6 lg:px-8 relative z-10">
|
||||||
<div className="max-w-7xl mx-auto grid grid-cols-1 md:grid-cols-2 gap-8 lg:gap-12">
|
<div className="max-w-7xl mx-auto grid grid-cols-1 md:grid-cols-2 gap-8 lg:gap-12">
|
||||||
{pageContent.mission && (
|
{pageContent.mission && (
|
||||||
<div className="group relative bg-white/95 backdrop-blur-sm p-10 md:p-12 rounded-2xl shadow-2xl border border-[#d4af37]/20 hover:border-[#d4af37]/40 transition-all duration-500 hover:shadow-[#d4af37]/20 hover:-translate-y-1">
|
<div className="group relative bg-white/95 backdrop-blur-sm p-10 md:p-12 rounded-2xl shadow-2xl border border-[var(--luxury-gold)]/20 hover:border-[var(--luxury-gold)]/40 transition-all duration-500 hover:shadow-[var(--luxury-gold)]/20 hover:-translate-y-1">
|
||||||
<div className="absolute top-0 right-0 w-40 h-40 bg-gradient-to-br from-[#d4af37]/10 to-transparent rounded-bl-full opacity-0 group-hover:opacity-100 transition-opacity duration-500"></div>
|
<div className="absolute top-0 right-0 w-40 h-40 bg-gradient-to-br from-[var(--luxury-gold)]/10 to-transparent rounded-bl-full opacity-0 group-hover:opacity-100 transition-opacity duration-500"></div>
|
||||||
<div className="relative">
|
<div className="relative">
|
||||||
<div className="flex items-center gap-3 mb-6">
|
<div className="flex items-center gap-3 mb-6">
|
||||||
<div className="w-1 h-12 bg-gradient-to-b from-[#d4af37] to-[#f5d76e] rounded-full"></div>
|
<div className="w-1 h-12 bg-gradient-to-b from-[var(--luxury-gold)] to-[var(--luxury-gold-light)] rounded-full"></div>
|
||||||
<h2 className="text-3xl md:text-4xl font-serif font-light text-gray-900">Our Mission</h2>
|
<h2 className="text-3xl md:text-4xl font-serif font-light text-gray-900">Our Mission</h2>
|
||||||
</div>
|
</div>
|
||||||
<p className="text-gray-700 leading-relaxed text-base md:text-lg font-light tracking-wide">{pageContent.mission}</p>
|
<p className="text-gray-700 leading-relaxed text-base md:text-lg font-light tracking-wide">{pageContent.mission}</p>
|
||||||
@@ -389,11 +389,11 @@ const AboutPage: React.FC = () => {
|
|||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
{pageContent.vision && (
|
{pageContent.vision && (
|
||||||
<div className="group relative bg-white/95 backdrop-blur-sm p-10 md:p-12 rounded-2xl shadow-2xl border border-[#d4af37]/20 hover:border-[#d4af37]/40 transition-all duration-500 hover:shadow-[#d4af37]/20 hover:-translate-y-1">
|
<div className="group relative bg-white/95 backdrop-blur-sm p-10 md:p-12 rounded-2xl shadow-2xl border border-[var(--luxury-gold)]/20 hover:border-[var(--luxury-gold)]/40 transition-all duration-500 hover:shadow-[var(--luxury-gold)]/20 hover:-translate-y-1">
|
||||||
<div className="absolute top-0 right-0 w-40 h-40 bg-gradient-to-br from-[#d4af37]/10 to-transparent rounded-bl-full opacity-0 group-hover:opacity-100 transition-opacity duration-500"></div>
|
<div className="absolute top-0 right-0 w-40 h-40 bg-gradient-to-br from-[var(--luxury-gold)]/10 to-transparent rounded-bl-full opacity-0 group-hover:opacity-100 transition-opacity duration-500"></div>
|
||||||
<div className="relative">
|
<div className="relative">
|
||||||
<div className="flex items-center gap-3 mb-6">
|
<div className="flex items-center gap-3 mb-6">
|
||||||
<div className="w-1 h-12 bg-gradient-to-b from-[#d4af37] to-[#f5d76e] rounded-full"></div>
|
<div className="w-1 h-12 bg-gradient-to-b from-[var(--luxury-gold)] to-[var(--luxury-gold-light)] rounded-full"></div>
|
||||||
<h2 className="text-3xl md:text-4xl font-serif font-light text-gray-900">Our Vision</h2>
|
<h2 className="text-3xl md:text-4xl font-serif font-light text-gray-900">Our Vision</h2>
|
||||||
</div>
|
</div>
|
||||||
<p className="text-gray-700 leading-relaxed text-base md:text-lg font-light tracking-wide">{pageContent.vision}</p>
|
<p className="text-gray-700 leading-relaxed text-base md:text-lg font-light tracking-wide">{pageContent.vision}</p>
|
||||||
@@ -412,24 +412,24 @@ const AboutPage: React.FC = () => {
|
|||||||
<div className="max-w-7xl mx-auto">
|
<div className="max-w-7xl mx-auto">
|
||||||
<div className="text-center mb-16">
|
<div className="text-center mb-16">
|
||||||
<div className="inline-block mb-4">
|
<div className="inline-block mb-4">
|
||||||
<span className="text-sm font-semibold text-[#d4af37] tracking-[0.2em] uppercase">Meet The Experts</span>
|
<span className="text-sm font-semibold text-[var(--luxury-gold)] tracking-[0.2em] uppercase">Meet The Experts</span>
|
||||||
</div>
|
</div>
|
||||||
<h2 className="text-4xl md:text-5xl lg:text-6xl font-serif font-light text-gray-900 mb-6 tracking-tight">
|
<h2 className="text-4xl md:text-5xl lg:text-6xl font-serif font-light text-gray-900 mb-6 tracking-tight">
|
||||||
Our Team
|
Our Team
|
||||||
</h2>
|
</h2>
|
||||||
<div className="flex items-center justify-center gap-4 mb-6">
|
<div className="flex items-center justify-center gap-4 mb-6">
|
||||||
<div className="h-px w-12 bg-gradient-to-r from-transparent to-[#d4af37]"></div>
|
<div className="h-px w-12 bg-gradient-to-r from-transparent to-[var(--luxury-gold)]"></div>
|
||||||
<div className="w-2 h-2 bg-[#d4af37] rounded-full"></div>
|
<div className="w-2 h-2 bg-[var(--luxury-gold)] rounded-full"></div>
|
||||||
<div className="h-px w-12 bg-gradient-to-l from-transparent to-[#d4af37]"></div>
|
<div className="h-px w-12 bg-gradient-to-l from-transparent to-[var(--luxury-gold)]"></div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-8 lg:gap-10">
|
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-8 lg:gap-10">
|
||||||
{team.map((member: any, index: number) => (
|
{team.map((member: any, index: number) => (
|
||||||
<div
|
<div
|
||||||
key={index}
|
key={index}
|
||||||
className="group relative bg-white rounded-2xl shadow-xl overflow-hidden hover:shadow-2xl transition-all duration-500 border border-gray-100 hover:border-[#d4af37]/30 hover:-translate-y-2"
|
className="group relative bg-white rounded-2xl shadow-xl overflow-hidden hover:shadow-2xl transition-all duration-500 border border-gray-100 hover:border-[var(--luxury-gold)]/30 hover:-translate-y-2"
|
||||||
>
|
>
|
||||||
<div className="absolute inset-0 bg-gradient-to-br from-[#d4af37]/5 to-transparent opacity-0 group-hover:opacity-100 transition-opacity duration-500 z-10"></div>
|
<div className="absolute inset-0 bg-gradient-to-br from-[var(--luxury-gold)]/5 to-transparent opacity-0 group-hover:opacity-100 transition-opacity duration-500 z-10"></div>
|
||||||
{member.image && (
|
{member.image && (
|
||||||
<div className="relative overflow-hidden h-72">
|
<div className="relative overflow-hidden h-72">
|
||||||
<img
|
<img
|
||||||
@@ -441,8 +441,8 @@ const AboutPage: React.FC = () => {
|
|||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
<div className="p-8 relative z-10">
|
<div className="p-8 relative z-10">
|
||||||
<h3 className="text-2xl font-serif font-semibold text-gray-900 mb-2 group-hover:text-[#d4af37] transition-colors duration-300">{member.name}</h3>
|
<h3 className="text-2xl font-serif font-semibold text-gray-900 mb-2 group-hover:text-[var(--luxury-gold)] transition-colors duration-300">{member.name}</h3>
|
||||||
<p className="text-[#d4af37] font-medium mb-4 text-sm tracking-wide uppercase">{member.role}</p>
|
<p className="text-[var(--luxury-gold)] font-medium mb-4 text-sm tracking-wide uppercase">{member.role}</p>
|
||||||
{member.bio && <p className="text-gray-600 text-sm mb-6 leading-relaxed font-light">{member.bio}</p>}
|
{member.bio && <p className="text-gray-600 text-sm mb-6 leading-relaxed font-light">{member.bio}</p>}
|
||||||
{member.social_links && (
|
{member.social_links && (
|
||||||
<div className="flex gap-4 pt-4 border-t border-gray-100">
|
<div className="flex gap-4 pt-4 border-t border-gray-100">
|
||||||
@@ -451,7 +451,7 @@ const AboutPage: React.FC = () => {
|
|||||||
href={member.social_links.linkedin}
|
href={member.social_links.linkedin}
|
||||||
target="_blank"
|
target="_blank"
|
||||||
rel="noopener noreferrer"
|
rel="noopener noreferrer"
|
||||||
className="w-10 h-10 rounded-full bg-gray-100 flex items-center justify-center text-gray-600 hover:bg-[#d4af37] hover:text-white transition-all duration-300 group-hover:scale-110"
|
className="w-10 h-10 rounded-full bg-gray-100 flex items-center justify-center text-gray-600 hover:bg-[var(--luxury-gold)] hover:text-white transition-all duration-300 group-hover:scale-110"
|
||||||
>
|
>
|
||||||
<Linkedin className="w-5 h-5" />
|
<Linkedin className="w-5 h-5" />
|
||||||
</a>
|
</a>
|
||||||
@@ -461,7 +461,7 @@ const AboutPage: React.FC = () => {
|
|||||||
href={member.social_links.twitter}
|
href={member.social_links.twitter}
|
||||||
target="_blank"
|
target="_blank"
|
||||||
rel="noopener noreferrer"
|
rel="noopener noreferrer"
|
||||||
className="w-10 h-10 rounded-full bg-gray-100 flex items-center justify-center text-gray-600 hover:bg-[#d4af37] hover:text-white transition-all duration-300 group-hover:scale-110"
|
className="w-10 h-10 rounded-full bg-gray-100 flex items-center justify-center text-gray-600 hover:bg-[var(--luxury-gold)] hover:text-white transition-all duration-300 group-hover:scale-110"
|
||||||
>
|
>
|
||||||
<Twitter className="w-5 h-5" />
|
<Twitter className="w-5 h-5" />
|
||||||
</a>
|
</a>
|
||||||
@@ -485,30 +485,30 @@ const AboutPage: React.FC = () => {
|
|||||||
<div className="max-w-6xl mx-auto">
|
<div className="max-w-6xl mx-auto">
|
||||||
<div className="text-center mb-16">
|
<div className="text-center mb-16">
|
||||||
<div className="inline-block mb-4">
|
<div className="inline-block mb-4">
|
||||||
<span className="text-sm font-semibold text-[#d4af37] tracking-[0.2em] uppercase">Our Journey</span>
|
<span className="text-sm font-semibold text-[var(--luxury-gold)] tracking-[0.2em] uppercase">Our Journey</span>
|
||||||
</div>
|
</div>
|
||||||
<h2 className="text-4xl md:text-5xl lg:text-6xl font-serif font-light text-gray-900 mb-6 tracking-tight">
|
<h2 className="text-4xl md:text-5xl lg:text-6xl font-serif font-light text-gray-900 mb-6 tracking-tight">
|
||||||
Our History
|
Our History
|
||||||
</h2>
|
</h2>
|
||||||
<div className="flex items-center justify-center gap-4 mb-6">
|
<div className="flex items-center justify-center gap-4 mb-6">
|
||||||
<div className="h-px w-12 bg-gradient-to-r from-transparent to-[#d4af37]"></div>
|
<div className="h-px w-12 bg-gradient-to-r from-transparent to-[var(--luxury-gold)]"></div>
|
||||||
<div className="w-2 h-2 bg-[#d4af37] rounded-full"></div>
|
<div className="w-2 h-2 bg-[var(--luxury-gold)] rounded-full"></div>
|
||||||
<div className="h-px w-12 bg-gradient-to-l from-transparent to-[#d4af37]"></div>
|
<div className="h-px w-12 bg-gradient-to-l from-transparent to-[var(--luxury-gold)]"></div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="relative">
|
<div className="relative">
|
||||||
<div className="absolute left-8 md:left-1/2 transform md:-translate-x-1/2 w-1 h-full bg-gradient-to-b from-[#d4af37] via-[#f5d76e] to-[#d4af37] shadow-lg"></div>
|
<div className="absolute left-8 md:left-1/2 transform md:-translate-x-1/2 w-1 h-full bg-gradient-to-b from-[var(--luxury-gold)] via-[var(--luxury-gold-light)] to-[var(--luxury-gold)] shadow-lg"></div>
|
||||||
<div className="space-y-12 md:space-y-16">
|
<div className="space-y-12 md:space-y-16">
|
||||||
{timeline.map((event: any, index: number) => (
|
{timeline.map((event: any, index: number) => (
|
||||||
<div key={index} className={`relative flex items-center ${index % 2 === 0 ? 'md:flex-row' : 'md:flex-row-reverse'}`}>
|
<div key={index} className={`relative flex items-center ${index % 2 === 0 ? 'md:flex-row' : 'md:flex-row-reverse'}`}>
|
||||||
<div className="absolute left-6 md:left-1/2 transform md:-translate-x-1/2 w-6 h-6 bg-gradient-to-br from-[#d4af37] to-[#c9a227] rounded-full border-4 border-white shadow-xl z-10 group-hover:scale-125 transition-transform duration-300"></div>
|
<div className="absolute left-6 md:left-1/2 transform md:-translate-x-1/2 w-6 h-6 bg-gradient-to-br from-[var(--luxury-gold)] to-[var(--luxury-gold-dark)] rounded-full border-4 border-white shadow-xl z-10 group-hover:scale-125 transition-transform duration-300"></div>
|
||||||
<div className={`ml-20 md:ml-0 md:w-5/12 ${index % 2 === 0 ? 'md:mr-auto md:pr-8' : 'md:ml-auto md:pl-8'}`}>
|
<div className={`ml-20 md:ml-0 md:w-5/12 ${index % 2 === 0 ? 'md:mr-auto md:pr-8' : 'md:ml-auto md:pl-8'}`}>
|
||||||
<div className="group bg-white/90 backdrop-blur-sm p-8 rounded-2xl shadow-xl hover:shadow-2xl transition-all duration-500 border border-gray-100 hover:border-[#d4af37]/30">
|
<div className="group bg-white/90 backdrop-blur-sm p-8 rounded-2xl shadow-xl hover:shadow-2xl transition-all duration-500 border border-gray-100 hover:border-[var(--luxury-gold)]/30">
|
||||||
<div className="flex items-center gap-3 mb-4">
|
<div className="flex items-center gap-3 mb-4">
|
||||||
<div className="text-[#d4af37] font-bold text-2xl md:text-3xl font-serif">{event.year}</div>
|
<div className="text-[var(--luxury-gold)] font-bold text-2xl md:text-3xl font-serif">{event.year}</div>
|
||||||
<div className="h-px flex-1 bg-gradient-to-r from-[#d4af37] to-transparent"></div>
|
<div className="h-px flex-1 bg-gradient-to-r from-[var(--luxury-gold)] to-transparent"></div>
|
||||||
</div>
|
</div>
|
||||||
<h3 className="text-2xl md:text-3xl font-serif font-semibold text-gray-900 mb-3 group-hover:text-[#d4af37] transition-colors duration-300">{event.title}</h3>
|
<h3 className="text-2xl md:text-3xl font-serif font-semibold text-gray-900 mb-3 group-hover:text-[var(--luxury-gold)] transition-colors duration-300">{event.title}</h3>
|
||||||
<p className="text-gray-600 leading-relaxed font-light mb-4">{event.description}</p>
|
<p className="text-gray-600 leading-relaxed font-light mb-4">{event.description}</p>
|
||||||
{event.image && (
|
{event.image && (
|
||||||
<div className="mt-6 overflow-hidden rounded-xl">
|
<div className="mt-6 overflow-hidden rounded-xl">
|
||||||
@@ -538,15 +538,15 @@ const AboutPage: React.FC = () => {
|
|||||||
<div className="max-w-7xl mx-auto">
|
<div className="max-w-7xl mx-auto">
|
||||||
<div className="text-center mb-16">
|
<div className="text-center mb-16">
|
||||||
<div className="inline-block mb-4">
|
<div className="inline-block mb-4">
|
||||||
<span className="text-sm font-semibold text-[#d4af37] tracking-[0.2em] uppercase">Recognition</span>
|
<span className="text-sm font-semibold text-[var(--luxury-gold)] tracking-[0.2em] uppercase">Recognition</span>
|
||||||
</div>
|
</div>
|
||||||
<h2 className="text-4xl md:text-5xl lg:text-6xl font-serif font-light text-gray-900 mb-6 tracking-tight">
|
<h2 className="text-4xl md:text-5xl lg:text-6xl font-serif font-light text-gray-900 mb-6 tracking-tight">
|
||||||
Achievements & Awards
|
Achievements & Awards
|
||||||
</h2>
|
</h2>
|
||||||
<div className="flex items-center justify-center gap-4 mb-6">
|
<div className="flex items-center justify-center gap-4 mb-6">
|
||||||
<div className="h-px w-12 bg-gradient-to-r from-transparent to-[#d4af37]"></div>
|
<div className="h-px w-12 bg-gradient-to-r from-transparent to-[var(--luxury-gold)]"></div>
|
||||||
<div className="w-2 h-2 bg-[#d4af37] rounded-full"></div>
|
<div className="w-2 h-2 bg-[var(--luxury-gold)] rounded-full"></div>
|
||||||
<div className="h-px w-12 bg-gradient-to-l from-transparent to-[#d4af37]"></div>
|
<div className="h-px w-12 bg-gradient-to-l from-transparent to-[var(--luxury-gold)]"></div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-8 lg:gap-10">
|
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-8 lg:gap-10">
|
||||||
@@ -555,19 +555,19 @@ const AboutPage: React.FC = () => {
|
|||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
key={index}
|
key={index}
|
||||||
className="group relative bg-gradient-to-br from-white to-slate-50 p-8 rounded-2xl shadow-xl hover:shadow-2xl transition-all duration-500 border border-gray-100 hover:border-[#d4af37]/40 hover:-translate-y-2"
|
className="group relative bg-gradient-to-br from-white to-slate-50 p-8 rounded-2xl shadow-xl hover:shadow-2xl transition-all duration-500 border border-gray-100 hover:border-[var(--luxury-gold)]/40 hover:-translate-y-2"
|
||||||
>
|
>
|
||||||
<div className="absolute top-0 right-0 w-32 h-32 bg-gradient-to-br from-[#d4af37]/10 to-transparent rounded-bl-full opacity-0 group-hover:opacity-100 transition-opacity duration-500"></div>
|
<div className="absolute top-0 right-0 w-32 h-32 bg-gradient-to-br from-[var(--luxury-gold)]/10 to-transparent rounded-bl-full opacity-0 group-hover:opacity-100 transition-opacity duration-500"></div>
|
||||||
<div className="relative">
|
<div className="relative">
|
||||||
<div className="flex items-center gap-4 mb-6">
|
<div className="flex items-center gap-4 mb-6">
|
||||||
<div className="w-16 h-16 bg-gradient-to-br from-[#d4af37] via-[#f5d76e] to-[#d4af37] rounded-xl flex items-center justify-center shadow-lg shadow-[#d4af37]/20 group-hover:scale-110 group-hover:rotate-3 transition-transform duration-500">
|
<div className="w-16 h-16 bg-gradient-to-br from-[var(--luxury-gold)] via-[var(--luxury-gold-light)] to-[var(--luxury-gold)] rounded-xl flex items-center justify-center shadow-lg shadow-[var(--luxury-gold)]/20 group-hover:scale-110 group-hover:rotate-3 transition-transform duration-500">
|
||||||
<AchievementIcon className="w-8 h-8 text-white drop-shadow-md" />
|
<AchievementIcon className="w-8 h-8 text-white drop-shadow-md" />
|
||||||
</div>
|
</div>
|
||||||
{achievement.year && (
|
{achievement.year && (
|
||||||
<div className="text-[#d4af37] font-bold text-2xl font-serif">{achievement.year}</div>
|
<div className="text-[var(--luxury-gold)] font-bold text-2xl font-serif">{achievement.year}</div>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
<h3 className="text-xl md:text-2xl font-serif font-semibold text-gray-900 mb-3 group-hover:text-[#d4af37] transition-colors duration-300">{achievement.title}</h3>
|
<h3 className="text-xl md:text-2xl font-serif font-semibold text-gray-900 mb-3 group-hover:text-[var(--luxury-gold)] transition-colors duration-300">{achievement.title}</h3>
|
||||||
<p className="text-gray-600 text-sm md:text-base leading-relaxed font-light mb-4">{achievement.description}</p>
|
<p className="text-gray-600 text-sm md:text-base leading-relaxed font-light mb-4">{achievement.description}</p>
|
||||||
{achievement.image && (
|
{achievement.image && (
|
||||||
<div className="mt-6 overflow-hidden rounded-xl">
|
<div className="mt-6 overflow-hidden rounded-xl">
|
||||||
@@ -595,15 +595,15 @@ const AboutPage: React.FC = () => {
|
|||||||
<div className="max-w-6xl mx-auto">
|
<div className="max-w-6xl mx-auto">
|
||||||
<div className="text-center mb-16">
|
<div className="text-center mb-16">
|
||||||
<div className="inline-block mb-4">
|
<div className="inline-block mb-4">
|
||||||
<span className="text-sm font-semibold text-[#d4af37] tracking-[0.2em] uppercase">Connect With Us</span>
|
<span className="text-sm font-semibold text-[var(--luxury-gold)] tracking-[0.2em] uppercase">Connect With Us</span>
|
||||||
</div>
|
</div>
|
||||||
<h2 className="text-4xl md:text-5xl lg:text-6xl font-serif font-light text-gray-900 mb-6 tracking-tight">
|
<h2 className="text-4xl md:text-5xl lg:text-6xl font-serif font-light text-gray-900 mb-6 tracking-tight">
|
||||||
Get In Touch
|
Get In Touch
|
||||||
</h2>
|
</h2>
|
||||||
<div className="flex items-center justify-center gap-4 mb-6">
|
<div className="flex items-center justify-center gap-4 mb-6">
|
||||||
<div className="h-px w-12 bg-gradient-to-r from-transparent to-[#d4af37]"></div>
|
<div className="h-px w-12 bg-gradient-to-r from-transparent to-[var(--luxury-gold)]"></div>
|
||||||
<div className="w-2 h-2 bg-[#d4af37] rounded-full"></div>
|
<div className="w-2 h-2 bg-[var(--luxury-gold)] rounded-full"></div>
|
||||||
<div className="h-px w-12 bg-gradient-to-l from-transparent to-[#d4af37]"></div>
|
<div className="h-px w-12 bg-gradient-to-l from-transparent to-[var(--luxury-gold)]"></div>
|
||||||
</div>
|
</div>
|
||||||
<p className="text-gray-600 mt-6 text-lg font-light max-w-2xl mx-auto">
|
<p className="text-gray-600 mt-6 text-lg font-light max-w-2xl mx-auto">
|
||||||
We'd love to hear from you. Contact us for reservations or inquiries.
|
We'd love to hear from you. Contact us for reservations or inquiries.
|
||||||
@@ -612,11 +612,11 @@ const AboutPage: React.FC = () => {
|
|||||||
{(displayAddress || displayPhone || displayEmail) && (
|
{(displayAddress || displayPhone || displayEmail) && (
|
||||||
<div className="grid grid-cols-1 md:grid-cols-3 gap-8 lg:gap-10 mb-16">
|
<div className="grid grid-cols-1 md:grid-cols-3 gap-8 lg:gap-10 mb-16">
|
||||||
{displayAddress && (
|
{displayAddress && (
|
||||||
<div className="group text-center p-8 bg-white/80 backdrop-blur-sm rounded-2xl shadow-xl hover:shadow-2xl transition-all duration-500 border border-gray-100 hover:border-[#d4af37]/40 hover:-translate-y-2">
|
<div className="group text-center p-8 bg-white/80 backdrop-blur-sm rounded-2xl shadow-xl hover:shadow-2xl transition-all duration-500 border border-gray-100 hover:border-[var(--luxury-gold)]/40 hover:-translate-y-2">
|
||||||
<div className="w-16 h-16 bg-gradient-to-br from-[#d4af37] via-[#f5d76e] to-[#d4af37] rounded-2xl flex items-center justify-center mx-auto mb-6 shadow-lg shadow-[#d4af37]/20 group-hover:scale-110 group-hover:rotate-3 transition-transform duration-500">
|
<div className="w-16 h-16 bg-gradient-to-br from-[var(--luxury-gold)] via-[var(--luxury-gold-light)] to-[var(--luxury-gold)] rounded-2xl flex items-center justify-center mx-auto mb-6 shadow-lg shadow-[var(--luxury-gold)]/20 group-hover:scale-110 group-hover:rotate-3 transition-transform duration-500">
|
||||||
<MapPin className="w-8 h-8 text-white drop-shadow-md" />
|
<MapPin className="w-8 h-8 text-white drop-shadow-md" />
|
||||||
</div>
|
</div>
|
||||||
<h3 className="text-xl font-serif font-semibold text-gray-900 mb-4 group-hover:text-[#d4af37] transition-colors duration-300">
|
<h3 className="text-xl font-serif font-semibold text-gray-900 mb-4 group-hover:text-[var(--luxury-gold)] transition-colors duration-300">
|
||||||
Address
|
Address
|
||||||
</h3>
|
</h3>
|
||||||
<p className="text-gray-600 leading-relaxed font-light">
|
<p className="text-gray-600 leading-relaxed font-light">
|
||||||
@@ -631,30 +631,30 @@ const AboutPage: React.FC = () => {
|
|||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
{displayPhone && (
|
{displayPhone && (
|
||||||
<div className="group text-center p-8 bg-white/80 backdrop-blur-sm rounded-2xl shadow-xl hover:shadow-2xl transition-all duration-500 border border-gray-100 hover:border-[#d4af37]/40 hover:-translate-y-2" style={{ animationDelay: '0.1s' }}>
|
<div className="group text-center p-8 bg-white/80 backdrop-blur-sm rounded-2xl shadow-xl hover:shadow-2xl transition-all duration-500 border border-gray-100 hover:border-[var(--luxury-gold)]/40 hover:-translate-y-2" style={{ animationDelay: '0.1s' }}>
|
||||||
<div className="w-16 h-16 bg-gradient-to-br from-[#d4af37] via-[#f5d76e] to-[#d4af37] rounded-2xl flex items-center justify-center mx-auto mb-6 shadow-lg shadow-[#d4af37]/20 group-hover:scale-110 group-hover:rotate-3 transition-transform duration-500">
|
<div className="w-16 h-16 bg-gradient-to-br from-[var(--luxury-gold)] via-[var(--luxury-gold-light)] to-[var(--luxury-gold)] rounded-2xl flex items-center justify-center mx-auto mb-6 shadow-lg shadow-[var(--luxury-gold)]/20 group-hover:scale-110 group-hover:rotate-3 transition-transform duration-500">
|
||||||
<Phone className="w-8 h-8 text-white drop-shadow-md" />
|
<Phone className="w-8 h-8 text-white drop-shadow-md" />
|
||||||
</div>
|
</div>
|
||||||
<h3 className="text-xl font-serif font-semibold text-gray-900 mb-4 group-hover:text-[#d4af37] transition-colors duration-300">
|
<h3 className="text-xl font-serif font-semibold text-gray-900 mb-4 group-hover:text-[var(--luxury-gold)] transition-colors duration-300">
|
||||||
Phone
|
Phone
|
||||||
</h3>
|
</h3>
|
||||||
<p className="text-gray-600 font-light">
|
<p className="text-gray-600 font-light">
|
||||||
<a href={`tel:${displayPhone.replace(/\s+/g, '').replace(/[()]/g, '')}`} className="hover:text-[#d4af37] transition-colors duration-300">
|
<a href={`tel:${displayPhone.replace(/\s+/g, '').replace(/[()]/g, '')}`} className="hover:text-[var(--luxury-gold)] transition-colors duration-300">
|
||||||
{displayPhone}
|
{displayPhone}
|
||||||
</a>
|
</a>
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
{displayEmail && (
|
{displayEmail && (
|
||||||
<div className="group text-center p-8 bg-white/80 backdrop-blur-sm rounded-2xl shadow-xl hover:shadow-2xl transition-all duration-500 border border-gray-100 hover:border-[#d4af37]/40 hover:-translate-y-2" style={{ animationDelay: '0.2s' }}>
|
<div className="group text-center p-8 bg-white/80 backdrop-blur-sm rounded-2xl shadow-xl hover:shadow-2xl transition-all duration-500 border border-gray-100 hover:border-[var(--luxury-gold)]/40 hover:-translate-y-2" style={{ animationDelay: '0.2s' }}>
|
||||||
<div className="w-16 h-16 bg-gradient-to-br from-[#d4af37] via-[#f5d76e] to-[#d4af37] rounded-2xl flex items-center justify-center mx-auto mb-6 shadow-lg shadow-[#d4af37]/20 group-hover:scale-110 group-hover:rotate-3 transition-transform duration-500">
|
<div className="w-16 h-16 bg-gradient-to-br from-[var(--luxury-gold)] via-[var(--luxury-gold-light)] to-[var(--luxury-gold)] rounded-2xl flex items-center justify-center mx-auto mb-6 shadow-lg shadow-[var(--luxury-gold)]/20 group-hover:scale-110 group-hover:rotate-3 transition-transform duration-500">
|
||||||
<Mail className="w-8 h-8 text-white drop-shadow-md" />
|
<Mail className="w-8 h-8 text-white drop-shadow-md" />
|
||||||
</div>
|
</div>
|
||||||
<h3 className="text-xl font-serif font-semibold text-gray-900 mb-4 group-hover:text-[#d4af37] transition-colors duration-300">
|
<h3 className="text-xl font-serif font-semibold text-gray-900 mb-4 group-hover:text-[var(--luxury-gold)] transition-colors duration-300">
|
||||||
Email
|
Email
|
||||||
</h3>
|
</h3>
|
||||||
<p className="text-gray-600 font-light">
|
<p className="text-gray-600 font-light">
|
||||||
<a href={`mailto:${displayEmail}`} className="hover:text-[#d4af37] transition-colors duration-300">
|
<a href={`mailto:${displayEmail}`} className="hover:text-[var(--luxury-gold)] transition-colors duration-300">
|
||||||
{displayEmail}
|
{displayEmail}
|
||||||
</a>
|
</a>
|
||||||
</p>
|
</p>
|
||||||
@@ -665,11 +665,11 @@ const AboutPage: React.FC = () => {
|
|||||||
<div className="text-center">
|
<div className="text-center">
|
||||||
<Link
|
<Link
|
||||||
to="/rooms"
|
to="/rooms"
|
||||||
className="group inline-flex items-center space-x-3 px-10 py-4 bg-gradient-to-r from-[#d4af37] via-[#f5d76e] to-[#d4af37] text-white rounded-xl hover:shadow-2xl hover:shadow-[#d4af37]/40 transition-all duration-500 font-medium text-lg tracking-wide relative overflow-hidden"
|
className="group inline-flex items-center space-x-3 px-10 py-4 bg-gradient-to-r from-[var(--luxury-gold)] via-[var(--luxury-gold-light)] to-[var(--luxury-gold)] text-white rounded-xl hover:shadow-2xl hover:shadow-[var(--luxury-gold)]/40 transition-all duration-500 font-medium text-lg tracking-wide relative overflow-hidden"
|
||||||
>
|
>
|
||||||
<span className="relative z-10">Explore Our Rooms</span>
|
<span className="relative z-10">Explore Our Rooms</span>
|
||||||
<Hotel className="w-5 h-5 relative z-10 group-hover:translate-x-1 transition-transform duration-300" />
|
<Hotel className="w-5 h-5 relative z-10 group-hover:translate-x-1 transition-transform duration-300" />
|
||||||
<div className="absolute inset-0 bg-gradient-to-r from-[#f5d76e] to-[#d4af37] opacity-0 group-hover:opacity-100 transition-opacity duration-500"></div>
|
<div className="absolute inset-0 bg-gradient-to-r from-[var(--luxury-gold-light)] to-[var(--luxury-gold)] opacity-0 group-hover:opacity-100 transition-opacity duration-500"></div>
|
||||||
</Link>
|
</Link>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -94,12 +94,12 @@ const AccessibilityPage: React.FC = () => {
|
|||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<div className="text-center">
|
<div className="text-center">
|
||||||
<Accessibility className="w-16 h-16 text-[#d4af37]/50 mx-auto mb-4" />
|
<Accessibility className="w-16 h-16 text-[var(--luxury-gold)]/50 mx-auto mb-4" />
|
||||||
<h1 className="text-2xl font-elegant font-bold text-white mb-2">Accessibility</h1>
|
<h1 className="text-2xl font-elegant font-bold text-white mb-2">Accessibility</h1>
|
||||||
<p className="text-gray-400">This page is currently unavailable.</p>
|
<p className="text-gray-400">This page is currently unavailable.</p>
|
||||||
<Link
|
<Link
|
||||||
to="/"
|
to="/"
|
||||||
className="inline-flex items-center gap-2 text-[#d4af37]/80 hover:text-[#d4af37] mt-6 transition-all duration-300"
|
className="inline-flex items-center gap-2 text-[var(--luxury-gold)]/80 hover:text-[var(--luxury-gold)] mt-6 transition-all duration-300"
|
||||||
>
|
>
|
||||||
<ArrowLeft className="w-4 h-4" />
|
<ArrowLeft className="w-4 h-4" />
|
||||||
<span>Back to Home</span>
|
<span>Back to Home</span>
|
||||||
@@ -123,17 +123,17 @@ const AccessibilityPage: React.FC = () => {
|
|||||||
<div className="w-full px-3 sm:px-4 md:px-6 lg:px-8 py-8 sm:py-12 max-w-5xl mx-auto">
|
<div className="w-full px-3 sm:px-4 md:px-6 lg:px-8 py-8 sm:py-12 max-w-5xl mx-auto">
|
||||||
<Link
|
<Link
|
||||||
to="/"
|
to="/"
|
||||||
className="inline-flex items-center gap-2 text-[#d4af37]/80 hover:text-[#d4af37] mb-6 transition-all duration-300 group font-light tracking-wide text-xs sm:text-sm"
|
className="inline-flex items-center gap-2 text-[var(--luxury-gold)]/80 hover:text-[var(--luxury-gold)] mb-6 transition-all duration-300 group font-light tracking-wide text-xs sm:text-sm"
|
||||||
>
|
>
|
||||||
<ArrowLeft className="w-4 h-4 group-hover:-translate-x-1 transition-transform" />
|
<ArrowLeft className="w-4 h-4 group-hover:-translate-x-1 transition-transform" />
|
||||||
<span>Back to Home</span>
|
<span>Back to Home</span>
|
||||||
</Link>
|
</Link>
|
||||||
|
|
||||||
<div className="mb-8 sm:mb-12 text-center">
|
<div className="mb-8 sm:mb-12 text-center">
|
||||||
<div className="inline-flex items-center justify-center w-16 h-16 sm:w-20 sm:h-20 mb-4 sm:mb-6 bg-gradient-to-br from-[#d4af37]/20 to-[#c9a227]/10 rounded-full border border-[#d4af37]/30">
|
<div className="inline-flex items-center justify-center w-16 h-16 sm:w-20 sm:h-20 mb-4 sm:mb-6 bg-gradient-to-br from-[var(--luxury-gold)]/20 to-[var(--luxury-gold-dark)]/10 rounded-full border border-[var(--luxury-gold)]/30">
|
||||||
<Accessibility className="w-8 h-8 sm:w-10 sm:h-10 text-[#d4af37]" />
|
<Accessibility className="w-8 h-8 sm:w-10 sm:h-10 text-[var(--luxury-gold)]" />
|
||||||
</div>
|
</div>
|
||||||
<h1 className="text-3xl sm:text-4xl lg:text-5xl font-elegant font-bold text-white mb-3 sm:mb-4 tracking-tight leading-tight bg-gradient-to-r from-white via-[#d4af37] to-white bg-clip-text text-transparent">
|
<h1 className="text-3xl sm:text-4xl lg:text-5xl font-elegant font-bold text-white mb-3 sm:mb-4 tracking-tight leading-tight bg-gradient-to-r from-white via-[var(--luxury-gold)] to-white bg-clip-text text-transparent">
|
||||||
{pageContent.title || 'Accessibility'}
|
{pageContent.title || 'Accessibility'}
|
||||||
</h1>
|
</h1>
|
||||||
{pageContent.subtitle && (
|
{pageContent.subtitle && (
|
||||||
@@ -143,18 +143,18 @@ const AccessibilityPage: React.FC = () => {
|
|||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="bg-gradient-to-br from-[#1a1a1a] to-[#0a0a0a] rounded-xl border border-[#d4af37]/20 backdrop-blur-xl shadow-2xl shadow-[#d4af37]/5 p-6 sm:p-8 lg:p-12">
|
<div className="bg-gradient-to-br from-[#1a1a1a] to-[#0a0a0a] rounded-xl border border-[var(--luxury-gold)]/20 backdrop-blur-xl shadow-2xl shadow-[var(--luxury-gold)]/5 p-6 sm:p-8 lg:p-12">
|
||||||
<div
|
<div
|
||||||
className="prose prose-invert prose-lg max-w-none text-gray-300
|
className="prose prose-invert prose-lg max-w-none text-gray-300
|
||||||
prose-headings:text-white prose-headings:font-elegant prose-headings:font-semibold
|
prose-headings:text-white prose-headings:font-elegant prose-headings:font-semibold
|
||||||
prose-h2:text-2xl prose-h2:mt-8 prose-h2:mb-4 prose-h2:border-b prose-h2:border-[#d4af37]/20 prose-h2:pb-2
|
prose-h2:text-2xl prose-h2:mt-8 prose-h2:mb-4 prose-h2:border-b prose-h2:border-[var(--luxury-gold)]/20 prose-h2:pb-2
|
||||||
prose-p:text-gray-300 prose-p:font-light prose-p:leading-relaxed prose-p:mb-4
|
prose-p:text-gray-300 prose-p:font-light prose-p:leading-relaxed prose-p:mb-4
|
||||||
prose-ul:text-gray-300 prose-ul:font-light prose-ul:my-4
|
prose-ul:text-gray-300 prose-ul:font-light prose-ul:my-4
|
||||||
prose-li:text-gray-300 prose-li:mb-2 prose-li:ml-4
|
prose-li:text-gray-300 prose-li:mb-2 prose-li:ml-4
|
||||||
prose-strong:text-[#d4af37] prose-strong:font-medium
|
prose-strong:text-[var(--luxury-gold)] prose-strong:font-medium
|
||||||
prose-a:text-[#d4af37] prose-a:no-underline hover:prose-a:underline
|
prose-a:text-[var(--luxury-gold)] prose-a:no-underline hover:prose-a:underline
|
||||||
[&_*]:text-gray-300 [&_h1]:text-white [&_h2]:text-white [&_h3]:text-white [&_h4]:text-white [&_h5]:text-white [&_h6]:text-white
|
[&_*]:text-gray-300 [&_h1]:text-white [&_h2]:text-white [&_h3]:text-white [&_h4]:text-white [&_h5]:text-white [&_h6]:text-white
|
||||||
[&_strong]:text-[#d4af37] [&_b]:text-[#d4af37] [&_a]:text-[#d4af37]"
|
[&_strong]:text-[var(--luxury-gold)] [&_b]:text-[var(--luxury-gold)] [&_a]:text-[var(--luxury-gold)]"
|
||||||
style={{ color: '#d1d5db' }}
|
style={{ color: '#d1d5db' }}
|
||||||
dangerouslySetInnerHTML={createSanitizedHtml(
|
dangerouslySetInnerHTML={createSanitizedHtml(
|
||||||
pageContent.content || pageContent.description || '<p style="color: #d1d5db;">No content available.</p>'
|
pageContent.content || pageContent.description || '<p style="color: #d1d5db;">No content available.</p>'
|
||||||
@@ -166,7 +166,7 @@ const AccessibilityPage: React.FC = () => {
|
|||||||
<div className="mt-8 text-center">
|
<div className="mt-8 text-center">
|
||||||
<p className="text-sm text-gray-400 font-light">
|
<p className="text-sm text-gray-400 font-light">
|
||||||
For accessibility inquiries, contact us at{' '}
|
For accessibility inquiries, contact us at{' '}
|
||||||
<a href={`mailto:${settings.company_email}`} className="text-[#d4af37] hover:underline">
|
<a href={`mailto:${settings.company_email}`} className="text-[var(--luxury-gold)] hover:underline">
|
||||||
{settings.company_email}
|
{settings.company_email}
|
||||||
</a>
|
</a>
|
||||||
</p>
|
</p>
|
||||||
|
|||||||
@@ -111,7 +111,7 @@ const BlogDetailPage: React.FC = () => {
|
|||||||
<div className="min-h-screen bg-gradient-to-b from-[#0f0f0f] via-[#1a1a1a] to-black text-white flex items-center justify-center">
|
<div className="min-h-screen bg-gradient-to-b from-[#0f0f0f] via-[#1a1a1a] to-black text-white flex items-center justify-center">
|
||||||
<div className="text-center">
|
<div className="text-center">
|
||||||
<h1 className="text-2xl font-bold mb-4">Post not found</h1>
|
<h1 className="text-2xl font-bold mb-4">Post not found</h1>
|
||||||
<Link to="/blog" className="text-[#d4af37] hover:underline">
|
<Link to="/blog" className="text-[var(--luxury-gold)] hover:underline">
|
||||||
Back to Blog
|
Back to Blog
|
||||||
</Link>
|
</Link>
|
||||||
</div>
|
</div>
|
||||||
@@ -140,7 +140,7 @@ const BlogDetailPage: React.FC = () => {
|
|||||||
{/* Back Button */}
|
{/* Back Button */}
|
||||||
<Link
|
<Link
|
||||||
to="/blog"
|
to="/blog"
|
||||||
className="inline-flex items-center gap-2 text-gray-400 hover:text-[#d4af37] transition-colors mb-8"
|
className="inline-flex items-center gap-2 text-gray-400 hover:text-[var(--luxury-gold)] transition-colors mb-8"
|
||||||
>
|
>
|
||||||
<ArrowLeft className="w-4 h-4" />
|
<ArrowLeft className="w-4 h-4" />
|
||||||
<span>Back to Blog</span>
|
<span>Back to Blog</span>
|
||||||
@@ -154,7 +154,7 @@ const BlogDetailPage: React.FC = () => {
|
|||||||
<Link
|
<Link
|
||||||
key={tag}
|
key={tag}
|
||||||
to={`/blog?tag=${encodeURIComponent(tag)}`}
|
to={`/blog?tag=${encodeURIComponent(tag)}`}
|
||||||
className="inline-flex items-center gap-1 px-3 py-1 bg-[#d4af37]/10 text-[#d4af37] rounded-full text-sm font-medium hover:bg-[#d4af37]/20 transition-colors"
|
className="inline-flex items-center gap-1 px-3 py-1 bg-[var(--luxury-gold)]/10 text-[var(--luxury-gold)] rounded-full text-sm font-medium hover:bg-[var(--luxury-gold)]/20 transition-colors"
|
||||||
>
|
>
|
||||||
<Tag className="w-3 h-3" />
|
<Tag className="w-3 h-3" />
|
||||||
{tag}
|
{tag}
|
||||||
@@ -173,7 +173,7 @@ const BlogDetailPage: React.FC = () => {
|
|||||||
</p>
|
</p>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
<div className="flex flex-wrap items-center gap-6 text-sm text-gray-400 pb-6 border-b border-[#d4af37]/20">
|
<div className="flex flex-wrap items-center gap-6 text-sm text-gray-400 pb-6 border-b border-[var(--luxury-gold)]/20">
|
||||||
{post.published_at && (
|
{post.published_at && (
|
||||||
<div className="flex items-center gap-2">
|
<div className="flex items-center gap-2">
|
||||||
<Calendar className="w-4 h-4" />
|
<Calendar className="w-4 h-4" />
|
||||||
@@ -192,7 +192,7 @@ const BlogDetailPage: React.FC = () => {
|
|||||||
</div>
|
</div>
|
||||||
<button
|
<button
|
||||||
onClick={handleShare}
|
onClick={handleShare}
|
||||||
className="flex items-center gap-2 text-[#d4af37] hover:text-[#f5d76e] transition-colors"
|
className="flex items-center gap-2 text-[var(--luxury-gold)] hover:text-[var(--luxury-gold-light)] transition-colors"
|
||||||
>
|
>
|
||||||
<Share2 className="w-4 h-4" />
|
<Share2 className="w-4 h-4" />
|
||||||
<span>Share</span>
|
<span>Share</span>
|
||||||
@@ -203,17 +203,17 @@ const BlogDetailPage: React.FC = () => {
|
|||||||
{/* Article Content */}
|
{/* Article Content */}
|
||||||
<article className="prose prose-invert prose-lg max-w-none mb-12
|
<article className="prose prose-invert prose-lg max-w-none mb-12
|
||||||
prose-headings:text-white prose-headings:font-elegant prose-headings:font-semibold
|
prose-headings:text-white prose-headings:font-elegant prose-headings:font-semibold
|
||||||
prose-h2:text-3xl prose-h2:mt-12 prose-h2:mb-6 prose-h2:border-b prose-h2:border-[#d4af37]/20 prose-h2:pb-3
|
prose-h2:text-3xl prose-h2:mt-12 prose-h2:mb-6 prose-h2:border-b prose-h2:border-[var(--luxury-gold)]/20 prose-h2:pb-3
|
||||||
prose-h3:text-2xl prose-h3:mt-8 prose-h3:mb-4
|
prose-h3:text-2xl prose-h3:mt-8 prose-h3:mb-4
|
||||||
prose-p:text-gray-300 prose-p:font-light prose-p:leading-relaxed prose-p:mb-6
|
prose-p:text-gray-300 prose-p:font-light prose-p:leading-relaxed prose-p:mb-6
|
||||||
prose-ul:text-gray-300 prose-ul:font-light prose-ul:my-6
|
prose-ul:text-gray-300 prose-ul:font-light prose-ul:my-6
|
||||||
prose-ol:text-gray-300 prose-ol:font-light prose-ol:my-6
|
prose-ol:text-gray-300 prose-ol:font-light prose-ol:my-6
|
||||||
prose-li:text-gray-300 prose-li:mb-2
|
prose-li:text-gray-300 prose-li:mb-2
|
||||||
prose-strong:text-[#d4af37] prose-strong:font-medium
|
prose-strong:text-[var(--luxury-gold)] prose-strong:font-medium
|
||||||
prose-a:text-[#d4af37] prose-a:no-underline hover:prose-a:underline
|
prose-a:text-[var(--luxury-gold)] prose-a:no-underline hover:prose-a:underline
|
||||||
prose-img:rounded-xl prose-img:shadow-2xl
|
prose-img:rounded-xl prose-img:shadow-2xl
|
||||||
[&_*]:text-gray-300 [&_h1]:text-white [&_h2]:text-white [&_h3]:text-white [&_h4]:text-white
|
[&_*]:text-gray-300 [&_h1]:text-white [&_h2]:text-white [&_h3]:text-white [&_h4]:text-white
|
||||||
[&_strong]:text-[#d4af37] [&_b]:text-[#d4af37] [&_a]:text-[#d4af37]"
|
[&_strong]:text-[var(--luxury-gold)] [&_b]:text-[var(--luxury-gold)] [&_a]:text-[var(--luxury-gold)]"
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
dangerouslySetInnerHTML={createSanitizedHtml(
|
dangerouslySetInnerHTML={createSanitizedHtml(
|
||||||
@@ -231,7 +231,7 @@ const BlogDetailPage: React.FC = () => {
|
|||||||
<div key={index}>
|
<div key={index}>
|
||||||
{/* Hero Section */}
|
{/* Hero Section */}
|
||||||
{section.type === 'hero' && (
|
{section.type === 'hero' && (
|
||||||
<div className="relative rounded-3xl overflow-hidden border-2 border-[#d4af37]/20 shadow-2xl">
|
<div className="relative rounded-3xl overflow-hidden border-2 border-[var(--luxury-gold)]/20 shadow-2xl">
|
||||||
{section.image && (
|
{section.image && (
|
||||||
<div className="absolute inset-0">
|
<div className="absolute inset-0">
|
||||||
<img src={section.image} alt={section.title} className="w-full h-full object-cover" />
|
<img src={section.image} alt={section.title} className="w-full h-full object-cover" />
|
||||||
@@ -255,7 +255,7 @@ const BlogDetailPage: React.FC = () => {
|
|||||||
|
|
||||||
{/* Text Section */}
|
{/* Text Section */}
|
||||||
{section.type === 'text' && (
|
{section.type === 'text' && (
|
||||||
<div className={`bg-gradient-to-br from-[#1a1a1a] to-[#0f0f0f] rounded-3xl border-2 border-[#d4af37]/20 p-8 md:p-12 shadow-xl ${
|
<div className={`bg-gradient-to-br from-[#1a1a1a] to-[#0f0f0f] rounded-3xl border-2 border-[var(--luxury-gold)]/20 p-8 md:p-12 shadow-xl ${
|
||||||
section.alignment === 'center' ? 'text-center' : section.alignment === 'right' ? 'text-right' : 'text-left'
|
section.alignment === 'center' ? 'text-center' : section.alignment === 'right' ? 'text-right' : 'text-left'
|
||||||
}`}>
|
}`}>
|
||||||
{section.title && (
|
{section.title && (
|
||||||
@@ -274,10 +274,10 @@ const BlogDetailPage: React.FC = () => {
|
|||||||
|
|
||||||
{/* Image Section */}
|
{/* Image Section */}
|
||||||
{section.type === 'image' && section.image && (
|
{section.type === 'image' && section.image && (
|
||||||
<div className="rounded-3xl overflow-hidden border-2 border-[#d4af37]/20 shadow-2xl">
|
<div className="rounded-3xl overflow-hidden border-2 border-[var(--luxury-gold)]/20 shadow-2xl">
|
||||||
<img src={section.image} alt={section.title || 'Blog image'} className="w-full h-auto" />
|
<img src={section.image} alt={section.title || 'Blog image'} className="w-full h-auto" />
|
||||||
{section.title && (
|
{section.title && (
|
||||||
<div className="bg-gradient-to-br from-[#1a1a1a] to-[#0f0f0f] px-6 py-4 border-t border-[#d4af37]/20">
|
<div className="bg-gradient-to-br from-[#1a1a1a] to-[#0f0f0f] px-6 py-4 border-t border-[var(--luxury-gold)]/20">
|
||||||
<p className="text-gray-400 text-sm font-light italic text-center">{section.title}</p>
|
<p className="text-gray-400 text-sm font-light italic text-center">{section.title}</p>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
@@ -288,7 +288,7 @@ const BlogDetailPage: React.FC = () => {
|
|||||||
{section.type === 'gallery' && section.images && section.images.length > 0 && (
|
{section.type === 'gallery' && section.images && section.images.length > 0 && (
|
||||||
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6">
|
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6">
|
||||||
{section.images.map((img, imgIndex) => (
|
{section.images.map((img, imgIndex) => (
|
||||||
<div key={imgIndex} className="rounded-2xl overflow-hidden border-2 border-[#d4af37]/20 shadow-xl group hover:border-[#d4af37]/50 transition-all">
|
<div key={imgIndex} className="rounded-2xl overflow-hidden border-2 border-[var(--luxury-gold)]/20 shadow-xl group hover:border-[var(--luxury-gold)]/50 transition-all">
|
||||||
<img src={img} alt={`Gallery image ${imgIndex + 1}`} className="w-full h-64 object-cover group-hover:scale-110 transition-transform duration-500" />
|
<img src={img} alt={`Gallery image ${imgIndex + 1}`} className="w-full h-64 object-cover group-hover:scale-110 transition-transform duration-500" />
|
||||||
</div>
|
</div>
|
||||||
))}
|
))}
|
||||||
@@ -297,15 +297,15 @@ const BlogDetailPage: React.FC = () => {
|
|||||||
|
|
||||||
{/* Quote Section */}
|
{/* Quote Section */}
|
||||||
{section.type === 'quote' && (
|
{section.type === 'quote' && (
|
||||||
<div className="relative bg-gradient-to-br from-[#1a1a1a] to-[#0f0f0f] rounded-3xl border-2 border-[#d4af37]/30 p-8 md:p-12 shadow-2xl">
|
<div className="relative bg-gradient-to-br from-[#1a1a1a] to-[#0f0f0f] rounded-3xl border-2 border-[var(--luxury-gold)]/30 p-8 md:p-12 shadow-2xl">
|
||||||
<div className="absolute top-6 left-6 text-6xl text-[#d4af37]/20 font-serif">"</div>
|
<div className="absolute top-6 left-6 text-6xl text-[var(--luxury-gold)]/20 font-serif">"</div>
|
||||||
{section.quote && (
|
{section.quote && (
|
||||||
<blockquote className="text-2xl md:text-3xl font-serif font-light text-white italic mb-6 relative z-10 pl-8">
|
<blockquote className="text-2xl md:text-3xl font-serif font-light text-white italic mb-6 relative z-10 pl-8">
|
||||||
{section.quote}
|
{section.quote}
|
||||||
</blockquote>
|
</blockquote>
|
||||||
)}
|
)}
|
||||||
{section.author && (
|
{section.author && (
|
||||||
<cite className="text-[#d4af37] text-lg font-medium not-italic block text-right">
|
<cite className="text-[var(--luxury-gold)] text-lg font-medium not-italic block text-right">
|
||||||
— {section.author}
|
— {section.author}
|
||||||
</cite>
|
</cite>
|
||||||
)}
|
)}
|
||||||
@@ -316,7 +316,7 @@ const BlogDetailPage: React.FC = () => {
|
|||||||
{section.type === 'features' && section.features && (
|
{section.type === 'features' && section.features && (
|
||||||
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6">
|
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6">
|
||||||
{section.features.map((feature, featIndex) => (
|
{section.features.map((feature, featIndex) => (
|
||||||
<div key={featIndex} className="bg-gradient-to-br from-[#1a1a1a] to-[#0f0f0f] rounded-2xl border-2 border-[#d4af37]/20 p-6 shadow-xl hover:border-[#d4af37]/50 transition-all">
|
<div key={featIndex} className="bg-gradient-to-br from-[#1a1a1a] to-[#0f0f0f] rounded-2xl border-2 border-[var(--luxury-gold)]/20 p-6 shadow-xl hover:border-[var(--luxury-gold)]/50 transition-all">
|
||||||
{feature.icon && (
|
{feature.icon && (
|
||||||
<div className="text-4xl mb-4">{feature.icon}</div>
|
<div className="text-4xl mb-4">{feature.icon}</div>
|
||||||
)}
|
)}
|
||||||
@@ -329,7 +329,7 @@ const BlogDetailPage: React.FC = () => {
|
|||||||
|
|
||||||
{/* CTA Section */}
|
{/* CTA Section */}
|
||||||
{section.type === 'cta' && (
|
{section.type === 'cta' && (
|
||||||
<div className="relative bg-gradient-to-br from-[#d4af37]/10 via-[#c9a227]/5 to-[#d4af37]/10 rounded-3xl border-2 border-[#d4af37]/30 p-8 md:p-12 text-center shadow-2xl">
|
<div className="relative bg-gradient-to-br from-[var(--luxury-gold)]/10 via-[var(--luxury-gold-dark)]/5 to-[var(--luxury-gold)]/10 rounded-3xl border-2 border-[var(--luxury-gold)]/30 p-8 md:p-12 text-center shadow-2xl">
|
||||||
{section.title && (
|
{section.title && (
|
||||||
<h3 className="text-3xl md:text-4xl font-serif font-bold text-white mb-4">
|
<h3 className="text-3xl md:text-4xl font-serif font-bold text-white mb-4">
|
||||||
{section.title}
|
{section.title}
|
||||||
@@ -343,7 +343,7 @@ const BlogDetailPage: React.FC = () => {
|
|||||||
{section.cta_text && section.cta_link && (
|
{section.cta_text && section.cta_link && (
|
||||||
<a
|
<a
|
||||||
href={section.cta_link}
|
href={section.cta_link}
|
||||||
className="inline-flex items-center gap-3 px-8 py-4 bg-gradient-to-r from-[#d4af37] to-[#c9a227] text-[#0f0f0f] font-semibold rounded-xl hover:from-[#f5d76e] hover:to-[#d4af37] transition-all shadow-lg hover:shadow-xl hover:scale-105"
|
className="inline-flex items-center gap-3 px-8 py-4 bg-gradient-to-r from-[var(--luxury-gold)] to-[var(--luxury-gold-dark)] text-[#0f0f0f] font-semibold rounded-xl hover:from-[var(--luxury-gold-light)] hover:to-[var(--luxury-gold)] transition-all shadow-lg hover:shadow-xl hover:scale-105"
|
||||||
>
|
>
|
||||||
{section.cta_text}
|
{section.cta_text}
|
||||||
<ArrowRight className="w-5 h-5" />
|
<ArrowRight className="w-5 h-5" />
|
||||||
@@ -354,7 +354,7 @@ const BlogDetailPage: React.FC = () => {
|
|||||||
|
|
||||||
{/* Video Section */}
|
{/* Video Section */}
|
||||||
{section.type === 'video' && section.video_url && (
|
{section.type === 'video' && section.video_url && (
|
||||||
<div className="rounded-3xl overflow-hidden border-2 border-[#d4af37]/20 shadow-2xl bg-black">
|
<div className="rounded-3xl overflow-hidden border-2 border-[var(--luxury-gold)]/20 shadow-2xl bg-black">
|
||||||
<div className="aspect-video">
|
<div className="aspect-video">
|
||||||
<iframe
|
<iframe
|
||||||
src={section.video_url.replace('watch?v=', 'embed/').replace('vimeo.com/', 'player.vimeo.com/video/')}
|
src={section.video_url.replace('watch?v=', 'embed/').replace('vimeo.com/', 'player.vimeo.com/video/')}
|
||||||
@@ -372,14 +372,14 @@ const BlogDetailPage: React.FC = () => {
|
|||||||
|
|
||||||
{/* Related Posts */}
|
{/* Related Posts */}
|
||||||
{relatedPosts.length > 0 && (
|
{relatedPosts.length > 0 && (
|
||||||
<div className="mt-16 pt-12 border-t border-[#d4af37]/20">
|
<div className="mt-16 pt-12 border-t border-[var(--luxury-gold)]/20">
|
||||||
<h2 className="text-2xl sm:text-3xl font-bold text-white mb-8">Related Posts</h2>
|
<h2 className="text-2xl sm:text-3xl font-bold text-white mb-8">Related Posts</h2>
|
||||||
<div className="grid grid-cols-1 md:grid-cols-3 gap-6">
|
<div className="grid grid-cols-1 md:grid-cols-3 gap-6">
|
||||||
{relatedPosts.map((relatedPost) => (
|
{relatedPosts.map((relatedPost) => (
|
||||||
<Link
|
<Link
|
||||||
key={relatedPost.id}
|
key={relatedPost.id}
|
||||||
to={`/blog/${relatedPost.slug}`}
|
to={`/blog/${relatedPost.slug}`}
|
||||||
className="group bg-gradient-to-br from-[#1a1a1a] to-[#0f0f0f] rounded-xl border border-[#d4af37]/20 overflow-hidden hover:border-[#d4af37]/50 transition-all duration-300"
|
className="group bg-gradient-to-br from-[#1a1a1a] to-[#0f0f0f] rounded-xl border border-[var(--luxury-gold)]/20 overflow-hidden hover:border-[var(--luxury-gold)]/50 transition-all duration-300"
|
||||||
>
|
>
|
||||||
{relatedPost.featured_image && (
|
{relatedPost.featured_image && (
|
||||||
<div className="relative h-40 overflow-hidden">
|
<div className="relative h-40 overflow-hidden">
|
||||||
@@ -391,7 +391,7 @@ const BlogDetailPage: React.FC = () => {
|
|||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
<div className="p-4">
|
<div className="p-4">
|
||||||
<h3 className="text-lg font-bold text-white mb-2 group-hover:text-[#d4af37] transition-colors line-clamp-2">
|
<h3 className="text-lg font-bold text-white mb-2 group-hover:text-[var(--luxury-gold)] transition-colors line-clamp-2">
|
||||||
{relatedPost.title}
|
{relatedPost.title}
|
||||||
</h3>
|
</h3>
|
||||||
{relatedPost.excerpt && (
|
{relatedPost.excerpt && (
|
||||||
|
|||||||
@@ -70,41 +70,41 @@ const BlogPage: React.FC = () => {
|
|||||||
return (
|
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 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' }}>
|
||||||
{/* Hero Section */}
|
{/* Hero Section */}
|
||||||
<div className="w-full bg-gradient-to-br from-[#1a1a1a] via-[#0f0f0f] to-[#1a1a1a] border-b border-[#d4af37]/10 pt-6 sm:pt-7 md:pt-8 overflow-hidden relative">
|
<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">
|
||||||
{/* Background Effects */}
|
{/* Background Effects */}
|
||||||
<div className="absolute inset-0 opacity-10">
|
<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-[#d4af37] rounded-full blur-3xl"></div>
|
<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>
|
||||||
<div className="absolute bottom-10 right-10 w-40 sm:w-64 h-40 sm:h-64 bg-[#c9a227] rounded-full blur-3xl"></div>
|
<div className="absolute bottom-10 right-10 w-40 sm:w-64 h-40 sm:h-64 bg-[var(--luxury-gold-dark)] rounded-full blur-3xl"></div>
|
||||||
</div>
|
</div>
|
||||||
<div className="absolute top-0 left-0 w-full h-px bg-gradient-to-r from-transparent via-[#d4af37]/50 to-transparent"></div>
|
<div className="absolute top-0 left-0 w-full h-px bg-gradient-to-r from-transparent via-[var(--luxury-gold)]/50 to-transparent"></div>
|
||||||
|
|
||||||
<div className="w-full max-w-[1920px] mx-auto px-3 sm:px-4 md:px-6 lg:px-8 xl:px-12 2xl:px-16 3xl:px-20 py-4 sm:py-5 md:py-6 relative z-10">
|
<div className="w-full max-w-[1920px] mx-auto px-3 sm:px-4 md:px-6 lg:px-8 xl:px-12 2xl:px-16 3xl:px-20 py-4 sm:py-5 md:py-6 relative z-10">
|
||||||
<div className="max-w-2xl mx-auto text-center px-2">
|
<div className="max-w-2xl mx-auto text-center px-2">
|
||||||
<div className="flex justify-center mb-2 sm:mb-3 md:mb-4">
|
<div className="flex justify-center mb-2 sm:mb-3 md:mb-4">
|
||||||
<div className="relative group">
|
<div className="relative group">
|
||||||
<div className="absolute inset-0 bg-gradient-to-br from-[#d4af37] via-[#f5d76e] to-[#c9a227] rounded-xl blur-lg opacity-40 group-hover:opacity-60 transition-opacity duration-500"></div>
|
<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-[#d4af37]/40 backdrop-blur-sm shadow-xl shadow-[#d4af37]/20 group-hover:border-[#d4af37]/60 transition-all duration-300">
|
<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">
|
||||||
<BookOpen className="w-5 h-5 sm:w-6 sm:h-6 md:w-7 md:h-7 text-[#d4af37] drop-shadow-lg" />
|
<BookOpen 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>
|
</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">
|
<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-[#d4af37] to-white bg-clip-text text-transparent">
|
<span className="bg-gradient-to-r from-white via-[var(--luxury-gold)] to-white bg-clip-text text-transparent">
|
||||||
Our Blog
|
Our Blog
|
||||||
</span>
|
</span>
|
||||||
</h1>
|
</h1>
|
||||||
<div className="w-12 sm:w-16 md:w-20 h-0.5 bg-gradient-to-r from-transparent via-[#d4af37] to-transparent mx-auto mb-2 sm:mb-3"></div>
|
<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 text-gray-300 font-light leading-relaxed max-w-xl mx-auto tracking-wide px-2 sm:px-4">
|
||||||
Discover stories, insights, and updates from our luxury hotel
|
Discover stories, insights, and updates from our luxury hotel
|
||||||
</p>
|
</p>
|
||||||
<div className="mt-4 flex items-center justify-center gap-2 text-[#d4af37]/60">
|
<div className="mt-4 flex items-center justify-center gap-2 text-[var(--luxury-gold)]/60">
|
||||||
<Sparkles className="w-4 h-4 animate-pulse" />
|
<Sparkles className="w-4 h-4 animate-pulse" />
|
||||||
<span className="text-xs sm:text-sm font-light tracking-wider uppercase">Premium Content</span>
|
<span className="text-xs sm:text-sm font-light tracking-wider uppercase">Premium Content</span>
|
||||||
<Sparkles className="w-4 h-4 animate-pulse" style={{ animationDelay: '0.5s' }} />
|
<Sparkles className="w-4 h-4 animate-pulse" style={{ animationDelay: '0.5s' }} />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="absolute bottom-0 left-0 w-full h-px bg-gradient-to-r from-transparent via-[#d4af37]/30 to-transparent"></div>
|
<div className="absolute bottom-0 left-0 w-full h-px bg-gradient-to-r from-transparent via-[var(--luxury-gold)]/30 to-transparent"></div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Main Content - Full Width */}
|
{/* Main Content - Full Width */}
|
||||||
@@ -114,9 +114,9 @@ const BlogPage: React.FC = () => {
|
|||||||
<div className="mb-12 sm:mb-16">
|
<div className="mb-12 sm:mb-16">
|
||||||
<div className="flex flex-col lg:flex-row gap-6 mb-8">
|
<div className="flex flex-col lg:flex-row gap-6 mb-8">
|
||||||
<div className="flex-1 relative group">
|
<div className="flex-1 relative group">
|
||||||
<div className="absolute inset-0 bg-gradient-to-r from-[#d4af37]/10 to-transparent rounded-xl blur-xl opacity-0 group-hover:opacity-100 transition-opacity duration-500"></div>
|
<div className="absolute inset-0 bg-gradient-to-r from-[var(--luxury-gold)]/10 to-transparent rounded-xl blur-xl opacity-0 group-hover:opacity-100 transition-opacity duration-500"></div>
|
||||||
<div className="relative">
|
<div className="relative">
|
||||||
<Search className="absolute left-5 top-1/2 transform -translate-y-1/2 text-[#d4af37] w-5 h-5 z-10" />
|
<Search className="absolute left-5 top-1/2 transform -translate-y-1/2 text-[var(--luxury-gold)] w-5 h-5 z-10" />
|
||||||
<input
|
<input
|
||||||
type="text"
|
type="text"
|
||||||
placeholder="Search blog posts..."
|
placeholder="Search blog posts..."
|
||||||
@@ -125,7 +125,7 @@ const BlogPage: React.FC = () => {
|
|||||||
setSearchTerm(e.target.value);
|
setSearchTerm(e.target.value);
|
||||||
setCurrentPage(1);
|
setCurrentPage(1);
|
||||||
}}
|
}}
|
||||||
className="w-full pl-14 pr-5 py-4 bg-gradient-to-br from-[#1a1a1a] to-[#0f0f0f] border border-[#d4af37]/20 rounded-xl text-white placeholder-gray-500 focus:outline-none focus:ring-2 focus:ring-[#d4af37]/50 focus:border-[#d4af37]/50 transition-all duration-300 backdrop-blur-sm font-light"
|
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"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -147,8 +147,8 @@ const BlogPage: React.FC = () => {
|
|||||||
{/* Blog Posts Grid - Luxury Design */}
|
{/* Blog Posts Grid - Luxury Design */}
|
||||||
{posts.length === 0 ? (
|
{posts.length === 0 ? (
|
||||||
<div className="text-center py-20">
|
<div className="text-center py-20">
|
||||||
<div className="inline-flex items-center justify-center w-20 h-20 rounded-full bg-[#d4af37]/10 mb-6">
|
<div className="inline-flex items-center justify-center w-20 h-20 rounded-full bg-[var(--luxury-gold)]/10 mb-6">
|
||||||
<BookOpen className="w-10 h-10 text-[#d4af37]" />
|
<BookOpen className="w-10 h-10 text-[var(--luxury-gold)]" />
|
||||||
</div>
|
</div>
|
||||||
<p className="text-gray-400 text-xl font-light">No blog posts found</p>
|
<p className="text-gray-400 text-xl font-light">No blog posts found</p>
|
||||||
<p className="text-gray-500 text-sm mt-2">Try adjusting your search or filters</p>
|
<p className="text-gray-500 text-sm mt-2">Try adjusting your search or filters</p>
|
||||||
@@ -160,12 +160,12 @@ const BlogPage: React.FC = () => {
|
|||||||
<Link
|
<Link
|
||||||
key={post.id}
|
key={post.id}
|
||||||
to={`/blog/${post.slug}`}
|
to={`/blog/${post.slug}`}
|
||||||
className="group relative bg-gradient-to-br from-[#1a1a1a] via-[#0f0f0f] to-[#0a0a0a] rounded-3xl border-2 border-[#d4af37]/20 overflow-hidden hover:border-[#d4af37]/60 transition-all duration-700 hover:shadow-2xl hover:shadow-[#d4af37]/30 hover:-translate-y-3"
|
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"
|
||||||
style={{ animationDelay: `${index * 50}ms` }}
|
style={{ animationDelay: `${index * 50}ms` }}
|
||||||
>
|
>
|
||||||
{/* Premium Glow Effects */}
|
{/* Premium Glow Effects */}
|
||||||
<div className="absolute inset-0 bg-gradient-to-br from-[#d4af37]/0 via-[#d4af37]/0 to-[#d4af37]/0 group-hover:from-[#d4af37]/10 group-hover:via-[#d4af37]/5 group-hover:to-[#d4af37]/10 transition-all duration-700 rounded-3xl blur-2xl opacity-0 group-hover:opacity-100"></div>
|
<div className="absolute inset-0 bg-gradient-to-br from-[var(--luxury-gold)]/0 via-[var(--luxury-gold)]/0 to-[var(--luxury-gold)]/0 group-hover:from-[var(--luxury-gold)]/10 group-hover:via-[var(--luxury-gold)]/5 group-hover:to-[var(--luxury-gold)]/10 transition-all duration-700 rounded-3xl blur-2xl opacity-0 group-hover:opacity-100"></div>
|
||||||
<div className="absolute top-0 left-0 w-full h-1 bg-gradient-to-r from-transparent via-[#d4af37]/50 to-transparent opacity-0 group-hover:opacity-100 transition-opacity duration-500"></div>
|
<div className="absolute top-0 left-0 w-full h-1 bg-gradient-to-r from-transparent via-[var(--luxury-gold)]/50 to-transparent opacity-0 group-hover:opacity-100 transition-opacity duration-500"></div>
|
||||||
|
|
||||||
{/* Content */}
|
{/* Content */}
|
||||||
<div className="relative z-10">
|
<div className="relative z-10">
|
||||||
@@ -179,15 +179,15 @@ const BlogPage: React.FC = () => {
|
|||||||
/>
|
/>
|
||||||
{/* Premium Badge Overlay */}
|
{/* Premium Badge Overlay */}
|
||||||
<div className="absolute top-6 left-6 z-20">
|
<div className="absolute top-6 left-6 z-20">
|
||||||
<div className="bg-gradient-to-br from-[#d4af37]/20 to-[#c9a227]/10 backdrop-blur-md rounded-2xl px-4 py-2 border border-[#d4af37]/40 shadow-xl">
|
<div className="bg-gradient-to-br from-[var(--luxury-gold)]/20 to-[var(--luxury-gold-dark)]/10 backdrop-blur-md rounded-2xl px-4 py-2 border border-[var(--luxury-gold)]/40 shadow-xl">
|
||||||
<div className="flex items-center gap-2 text-[#d4af37]">
|
<div className="flex items-center gap-2 text-[var(--luxury-gold)]">
|
||||||
<Eye className="w-4 h-4" />
|
<Eye className="w-4 h-4" />
|
||||||
<span className="text-sm font-semibold">{post.views_count}</span>
|
<span className="text-sm font-semibold">{post.views_count}</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{/* Luxury Corner Accent */}
|
{/* Luxury Corner Accent */}
|
||||||
<div className="absolute top-0 right-0 w-32 h-32 bg-gradient-to-br from-[#d4af37]/20 to-transparent rounded-bl-full opacity-0 group-hover:opacity-100 transition-opacity duration-500"></div>
|
<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>
|
||||||
)}
|
)}
|
||||||
<div className="p-8">
|
<div className="p-8">
|
||||||
@@ -196,7 +196,7 @@ const BlogPage: React.FC = () => {
|
|||||||
{post.tags.slice(0, 2).map((tag) => (
|
{post.tags.slice(0, 2).map((tag) => (
|
||||||
<span
|
<span
|
||||||
key={tag}
|
key={tag}
|
||||||
className="inline-flex items-center gap-1.5 px-4 py-1.5 bg-gradient-to-br from-[#d4af37]/15 to-[#d4af37]/5 text-[#d4af37] rounded-full text-xs font-semibold border border-[#d4af37]/30 backdrop-blur-sm shadow-lg"
|
className="inline-flex items-center gap-1.5 px-4 py-1.5 bg-gradient-to-br from-[var(--luxury-gold)]/15 to-[var(--luxury-gold)]/5 text-[var(--luxury-gold)] rounded-full text-xs font-semibold border border-[var(--luxury-gold)]/30 backdrop-blur-sm shadow-lg"
|
||||||
>
|
>
|
||||||
<Tag className="w-3 h-3" />
|
<Tag className="w-3 h-3" />
|
||||||
{tag}
|
{tag}
|
||||||
@@ -204,7 +204,7 @@ const BlogPage: React.FC = () => {
|
|||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
<h2 className="text-2xl sm:text-3xl font-serif font-bold text-white mb-4 group-hover:text-[#d4af37] transition-colors duration-500 line-clamp-2 leading-tight tracking-tight">
|
<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">
|
||||||
{post.title}
|
{post.title}
|
||||||
</h2>
|
</h2>
|
||||||
{post.excerpt && (
|
{post.excerpt && (
|
||||||
@@ -212,28 +212,28 @@ const BlogPage: React.FC = () => {
|
|||||||
{post.excerpt}
|
{post.excerpt}
|
||||||
</p>
|
</p>
|
||||||
)}
|
)}
|
||||||
<div className="flex items-center justify-between text-sm text-gray-400 pt-6 border-t border-[#d4af37]/20 mb-6">
|
<div className="flex items-center justify-between text-sm text-gray-400 pt-6 border-t border-[var(--luxury-gold)]/20 mb-6">
|
||||||
<div className="flex items-center gap-4">
|
<div className="flex items-center gap-4">
|
||||||
{post.published_at && (
|
{post.published_at && (
|
||||||
<div className="flex items-center gap-2">
|
<div className="flex items-center gap-2">
|
||||||
<Calendar className="w-4 h-4 text-[#d4af37]" />
|
<Calendar className="w-4 h-4 text-[var(--luxury-gold)]" />
|
||||||
<span className="font-light">{formatDate(post.published_at)}</span>
|
<span className="font-light">{formatDate(post.published_at)}</span>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
{post.author_name && (
|
{post.author_name && (
|
||||||
<div className="flex items-center gap-2">
|
<div className="flex items-center gap-2">
|
||||||
<User className="w-4 h-4 text-[#d4af37]" />
|
<User className="w-4 h-4 text-[var(--luxury-gold)]" />
|
||||||
<span className="font-light">{post.author_name}</span>
|
<span className="font-light">{post.author_name}</span>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
<div className="flex items-center justify-between pt-4 border-t border-[#d4af37]/10">
|
<div className="flex items-center justify-between pt-4 border-t border-[var(--luxury-gold)]/10">
|
||||||
<div className="flex items-center gap-3 text-[#d4af37] group-hover:gap-4 transition-all duration-300">
|
<div className="flex items-center gap-3 text-[var(--luxury-gold)] group-hover:gap-4 transition-all duration-300">
|
||||||
<span className="text-sm font-semibold tracking-wide uppercase">Read Article</span>
|
<span className="text-sm font-semibold tracking-wide uppercase">Read Article</span>
|
||||||
<ArrowRight className="w-5 h-5 group-hover:translate-x-2 transition-transform duration-300" />
|
<ArrowRight className="w-5 h-5 group-hover:translate-x-2 transition-transform duration-300" />
|
||||||
</div>
|
</div>
|
||||||
<div className="w-12 h-0.5 bg-gradient-to-r from-[#d4af37] to-transparent group-hover:w-20 transition-all duration-500"></div>
|
<div className="w-12 h-0.5 bg-gradient-to-r from-[var(--luxury-gold)] to-transparent group-hover:w-20 transition-all duration-500"></div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -259,9 +259,9 @@ const BlogPage: React.FC = () => {
|
|||||||
<div className="lg:col-span-3">
|
<div className="lg:col-span-3">
|
||||||
{allTags.length > 0 && (
|
{allTags.length > 0 && (
|
||||||
<div className="sticky top-8">
|
<div className="sticky top-8">
|
||||||
<div className="bg-gradient-to-br from-[#1a1a1a] via-[#0f0f0f] to-[#0a0a0a] rounded-3xl border-2 border-[#d4af37]/20 p-8 backdrop-blur-xl shadow-2xl">
|
<div className="bg-gradient-to-br from-[#1a1a1a] via-[#0f0f0f] to-[#0a0a0a] rounded-3xl border-2 border-[var(--luxury-gold)]/20 p-8 backdrop-blur-xl shadow-2xl">
|
||||||
<div className="flex items-center gap-3 mb-6 pb-6 border-b border-[#d4af37]/20">
|
<div className="flex items-center gap-3 mb-6 pb-6 border-b border-[var(--luxury-gold)]/20">
|
||||||
<div className="w-1 h-8 bg-gradient-to-b from-[#d4af37] to-[#c9a227] rounded-full"></div>
|
<div className="w-1 h-8 bg-gradient-to-b from-[var(--luxury-gold)] to-[var(--luxury-gold-dark)] rounded-full"></div>
|
||||||
<h3 className="text-xl font-serif font-bold text-white">Filter by Tags</h3>
|
<h3 className="text-xl font-serif font-bold text-white">Filter by Tags</h3>
|
||||||
</div>
|
</div>
|
||||||
<div className="space-y-3">
|
<div className="space-y-3">
|
||||||
@@ -272,8 +272,8 @@ const BlogPage: React.FC = () => {
|
|||||||
}}
|
}}
|
||||||
className={`group relative w-full text-left px-5 py-4 rounded-2xl text-sm font-medium transition-all duration-300 overflow-hidden ${
|
className={`group relative w-full text-left px-5 py-4 rounded-2xl text-sm font-medium transition-all duration-300 overflow-hidden ${
|
||||||
selectedTag === null
|
selectedTag === null
|
||||||
? 'bg-gradient-to-r from-[#d4af37] to-[#c9a227] text-[#0f0f0f] shadow-lg shadow-[#d4af37]/40'
|
? '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-[#d4af37]/20 hover:border-[#d4af37]/50 hover:text-[#d4af37] hover:bg-[#1a1a1a]'
|
: '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)] hover:bg-[#1a1a1a]'
|
||||||
}`}
|
}`}
|
||||||
>
|
>
|
||||||
<span className="relative z-10 flex items-center gap-2">
|
<span className="relative z-10 flex items-center gap-2">
|
||||||
@@ -293,8 +293,8 @@ const BlogPage: React.FC = () => {
|
|||||||
}}
|
}}
|
||||||
className={`group relative w-full text-left px-5 py-4 rounded-2xl text-sm font-medium transition-all duration-300 overflow-hidden ${
|
className={`group relative w-full text-left px-5 py-4 rounded-2xl text-sm font-medium transition-all duration-300 overflow-hidden ${
|
||||||
selectedTag === tag
|
selectedTag === tag
|
||||||
? 'bg-gradient-to-r from-[#d4af37] to-[#c9a227] text-[#0f0f0f] shadow-lg shadow-[#d4af37]/40'
|
? '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-[#d4af37]/20 hover:border-[#d4af37]/50 hover:text-[#d4af37] hover:bg-[#1a1a1a]'
|
: '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)] hover:bg-[#1a1a1a]'
|
||||||
}`}
|
}`}
|
||||||
>
|
>
|
||||||
<span className="relative z-10 flex items-center gap-2">
|
<span className="relative z-10 flex items-center gap-2">
|
||||||
|
|||||||
@@ -94,12 +94,12 @@ const CancellationPolicyPage: React.FC = () => {
|
|||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<div className="text-center">
|
<div className="text-center">
|
||||||
<XCircle className="w-16 h-16 text-[#d4af37]/50 mx-auto mb-4" />
|
<XCircle className="w-16 h-16 text-[var(--luxury-gold)]/50 mx-auto mb-4" />
|
||||||
<h1 className="text-2xl font-elegant font-bold text-white mb-2">Cancellation Policy</h1>
|
<h1 className="text-2xl font-elegant font-bold text-white mb-2">Cancellation Policy</h1>
|
||||||
<p className="text-gray-400">This page is currently unavailable.</p>
|
<p className="text-gray-400">This page is currently unavailable.</p>
|
||||||
<Link
|
<Link
|
||||||
to="/"
|
to="/"
|
||||||
className="inline-flex items-center gap-2 text-[#d4af37]/80 hover:text-[#d4af37] mt-6 transition-all duration-300"
|
className="inline-flex items-center gap-2 text-[var(--luxury-gold)]/80 hover:text-[var(--luxury-gold)] mt-6 transition-all duration-300"
|
||||||
>
|
>
|
||||||
<ArrowLeft className="w-4 h-4" />
|
<ArrowLeft className="w-4 h-4" />
|
||||||
<span>Back to Home</span>
|
<span>Back to Home</span>
|
||||||
@@ -123,17 +123,17 @@ const CancellationPolicyPage: React.FC = () => {
|
|||||||
<div className="w-full px-3 sm:px-4 md:px-6 lg:px-8 py-8 sm:py-12 max-w-5xl mx-auto">
|
<div className="w-full px-3 sm:px-4 md:px-6 lg:px-8 py-8 sm:py-12 max-w-5xl mx-auto">
|
||||||
<Link
|
<Link
|
||||||
to="/"
|
to="/"
|
||||||
className="inline-flex items-center gap-2 text-[#d4af37]/80 hover:text-[#d4af37] mb-6 transition-all duration-300 group font-light tracking-wide text-xs sm:text-sm"
|
className="inline-flex items-center gap-2 text-[var(--luxury-gold)]/80 hover:text-[var(--luxury-gold)] mb-6 transition-all duration-300 group font-light tracking-wide text-xs sm:text-sm"
|
||||||
>
|
>
|
||||||
<ArrowLeft className="w-4 h-4 group-hover:-translate-x-1 transition-transform" />
|
<ArrowLeft className="w-4 h-4 group-hover:-translate-x-1 transition-transform" />
|
||||||
<span>Back to Home</span>
|
<span>Back to Home</span>
|
||||||
</Link>
|
</Link>
|
||||||
|
|
||||||
<div className="mb-8 sm:mb-12 text-center">
|
<div className="mb-8 sm:mb-12 text-center">
|
||||||
<div className="inline-flex items-center justify-center w-16 h-16 sm:w-20 sm:h-20 mb-4 sm:mb-6 bg-gradient-to-br from-[#d4af37]/20 to-[#c9a227]/10 rounded-full border border-[#d4af37]/30">
|
<div className="inline-flex items-center justify-center w-16 h-16 sm:w-20 sm:h-20 mb-4 sm:mb-6 bg-gradient-to-br from-[var(--luxury-gold)]/20 to-[var(--luxury-gold-dark)]/10 rounded-full border border-[var(--luxury-gold)]/30">
|
||||||
<XCircle className="w-8 h-8 sm:w-10 sm:h-10 text-[#d4af37]" />
|
<XCircle className="w-8 h-8 sm:w-10 sm:h-10 text-[var(--luxury-gold)]" />
|
||||||
</div>
|
</div>
|
||||||
<h1 className="text-3xl sm:text-4xl lg:text-5xl font-elegant font-bold text-white mb-3 sm:mb-4 tracking-tight leading-tight bg-gradient-to-r from-white via-[#d4af37] to-white bg-clip-text text-transparent">
|
<h1 className="text-3xl sm:text-4xl lg:text-5xl font-elegant font-bold text-white mb-3 sm:mb-4 tracking-tight leading-tight bg-gradient-to-r from-white via-[var(--luxury-gold)] to-white bg-clip-text text-transparent">
|
||||||
{pageContent.title || 'Cancellation Policy'}
|
{pageContent.title || 'Cancellation Policy'}
|
||||||
</h1>
|
</h1>
|
||||||
{pageContent.subtitle && (
|
{pageContent.subtitle && (
|
||||||
@@ -143,18 +143,18 @@ const CancellationPolicyPage: React.FC = () => {
|
|||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="bg-gradient-to-br from-[#1a1a1a] to-[#0a0a0a] rounded-xl border border-[#d4af37]/20 backdrop-blur-xl shadow-2xl shadow-[#d4af37]/5 p-6 sm:p-8 lg:p-12">
|
<div className="bg-gradient-to-br from-[#1a1a1a] to-[#0a0a0a] rounded-xl border border-[var(--luxury-gold)]/20 backdrop-blur-xl shadow-2xl shadow-[var(--luxury-gold)]/5 p-6 sm:p-8 lg:p-12">
|
||||||
<div
|
<div
|
||||||
className="prose prose-invert prose-lg max-w-none text-gray-300
|
className="prose prose-invert prose-lg max-w-none text-gray-300
|
||||||
prose-headings:text-white prose-headings:font-elegant prose-headings:font-semibold
|
prose-headings:text-white prose-headings:font-elegant prose-headings:font-semibold
|
||||||
prose-h2:text-2xl prose-h2:mt-8 prose-h2:mb-4 prose-h2:border-b prose-h2:border-[#d4af37]/20 prose-h2:pb-2
|
prose-h2:text-2xl prose-h2:mt-8 prose-h2:mb-4 prose-h2:border-b prose-h2:border-[var(--luxury-gold)]/20 prose-h2:pb-2
|
||||||
prose-p:text-gray-300 prose-p:font-light prose-p:leading-relaxed prose-p:mb-4
|
prose-p:text-gray-300 prose-p:font-light prose-p:leading-relaxed prose-p:mb-4
|
||||||
prose-ul:text-gray-300 prose-ul:font-light prose-ul:my-4
|
prose-ul:text-gray-300 prose-ul:font-light prose-ul:my-4
|
||||||
prose-li:text-gray-300 prose-li:mb-2 prose-li:ml-4
|
prose-li:text-gray-300 prose-li:mb-2 prose-li:ml-4
|
||||||
prose-strong:text-[#d4af37] prose-strong:font-medium
|
prose-strong:text-[var(--luxury-gold)] prose-strong:font-medium
|
||||||
prose-a:text-[#d4af37] prose-a:no-underline hover:prose-a:underline
|
prose-a:text-[var(--luxury-gold)] prose-a:no-underline hover:prose-a:underline
|
||||||
[&_*]:text-gray-300 [&_h1]:text-white [&_h2]:text-white [&_h3]:text-white [&_h4]:text-white [&_h5]:text-white [&_h6]:text-white
|
[&_*]:text-gray-300 [&_h1]:text-white [&_h2]:text-white [&_h3]:text-white [&_h4]:text-white [&_h5]:text-white [&_h6]:text-white
|
||||||
[&_strong]:text-[#d4af37] [&_b]:text-[#d4af37] [&_a]:text-[#d4af37]"
|
[&_strong]:text-[var(--luxury-gold)] [&_b]:text-[var(--luxury-gold)] [&_a]:text-[var(--luxury-gold)]"
|
||||||
style={{ color: '#d1d5db' }}
|
style={{ color: '#d1d5db' }}
|
||||||
dangerouslySetInnerHTML={createSanitizedHtml(
|
dangerouslySetInnerHTML={createSanitizedHtml(
|
||||||
pageContent.content || pageContent.description || '<p style="color: #d1d5db;">No content available.</p>'
|
pageContent.content || pageContent.description || '<p style="color: #d1d5db;">No content available.</p>'
|
||||||
@@ -166,7 +166,7 @@ const CancellationPolicyPage: React.FC = () => {
|
|||||||
<div className="mt-8 text-center">
|
<div className="mt-8 text-center">
|
||||||
<p className="text-sm text-gray-400 font-light">
|
<p className="text-sm text-gray-400 font-light">
|
||||||
For questions about cancellations, contact us at{' '}
|
For questions about cancellations, contact us at{' '}
|
||||||
<a href={`mailto:${settings.company_email}`} className="text-[#d4af37] hover:underline">
|
<a href={`mailto:${settings.company_email}`} className="text-[var(--luxury-gold)] hover:underline">
|
||||||
{settings.company_email}
|
{settings.company_email}
|
||||||
</a>
|
</a>
|
||||||
</p>
|
</p>
|
||||||
|
|||||||
@@ -174,36 +174,36 @@ const ContactPage: React.FC = () => {
|
|||||||
return (
|
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 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="w-full bg-gradient-to-br from-[#1a1a1a] via-[#0f0f0f] to-[#1a1a1a] border-b border-[#d4af37]/10 pt-6 sm:pt-7 md:pt-8 overflow-hidden relative">
|
<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="absolute inset-0 opacity-10">
|
<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-[#d4af37] rounded-full blur-3xl"></div>
|
<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>
|
||||||
<div className="absolute bottom-10 right-10 w-40 sm:w-64 h-40 sm:h-64 bg-[#c9a227] rounded-full blur-3xl"></div>
|
<div className="absolute bottom-10 right-10 w-40 sm:w-64 h-40 sm:h-64 bg-[var(--luxury-gold-dark)] rounded-full blur-3xl"></div>
|
||||||
</div>
|
</div>
|
||||||
<div className="absolute top-0 left-0 w-full h-px bg-gradient-to-r from-transparent via-[#d4af37]/50 to-transparent"></div>
|
<div className="absolute top-0 left-0 w-full h-px bg-gradient-to-r from-transparent via-[var(--luxury-gold)]/50 to-transparent"></div>
|
||||||
|
|
||||||
<div className="w-full max-w-[1920px] mx-auto px-3 sm:px-4 md:px-6 lg:px-8 xl:px-12 2xl:px-16 3xl:px-20 py-4 sm:py-5 md:py-6 relative z-10">
|
<div className="w-full max-w-[1920px] mx-auto px-3 sm:px-4 md:px-6 lg:px-8 xl:px-12 2xl:px-16 3xl:px-20 py-4 sm:py-5 md:py-6 relative z-10">
|
||||||
<div className="max-w-2xl mx-auto text-center px-2">
|
<div className="max-w-2xl mx-auto text-center px-2">
|
||||||
<div className="flex justify-center mb-2 sm:mb-3 md:mb-4">
|
<div className="flex justify-center mb-2 sm:mb-3 md:mb-4">
|
||||||
<div className="relative group">
|
<div className="relative group">
|
||||||
<div className="absolute inset-0 bg-gradient-to-br from-[#d4af37] via-[#f5d76e] to-[#c9a227] rounded-xl blur-lg opacity-40 group-hover:opacity-60 transition-opacity duration-500"></div>
|
<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-[#d4af37]/40 backdrop-blur-sm shadow-xl shadow-[#d4af37]/20 group-hover:border-[#d4af37]/60 transition-all duration-300">
|
<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">
|
||||||
<Mail className="w-5 h-5 sm:w-6 sm:h-6 md:w-7 md:h-7 text-[#d4af37] drop-shadow-lg" />
|
<Mail 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>
|
</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">
|
<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-[#d4af37] to-white bg-clip-text text-transparent">
|
<span className="bg-gradient-to-r from-white via-[var(--luxury-gold)] to-white bg-clip-text text-transparent">
|
||||||
{pageContent?.title || 'Contact Us'}
|
{pageContent?.title || 'Contact Us'}
|
||||||
</span>
|
</span>
|
||||||
</h1>
|
</h1>
|
||||||
<div className="w-12 sm:w-16 md:w-20 h-0.5 bg-gradient-to-r from-transparent via-[#d4af37] to-transparent mx-auto mb-2 sm:mb-3"></div>
|
<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 text-gray-300 font-light leading-relaxed max-w-xl mx-auto tracking-wide px-2 sm:px-4">
|
||||||
{pageContent?.subtitle || pageContent?.description || "Experience the pinnacle of hospitality. We're here to make your stay extraordinary."}
|
{pageContent?.subtitle || pageContent?.description || "Experience the pinnacle of hospitality. We're here to make your stay extraordinary."}
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="absolute bottom-0 left-0 w-full h-px bg-gradient-to-r from-transparent via-[#d4af37]/30 to-transparent"></div>
|
<div className="absolute bottom-0 left-0 w-full h-px bg-gradient-to-r from-transparent via-[var(--luxury-gold)]/30 to-transparent"></div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{}
|
{}
|
||||||
@@ -213,15 +213,15 @@ const ContactPage: React.FC = () => {
|
|||||||
{}
|
{}
|
||||||
<div className="lg:col-span-4">
|
<div className="lg:col-span-4">
|
||||||
<div className="bg-gradient-to-br from-[#1a1a1a] via-[#0f0f0f] to-[#0a0a0a]
|
<div className="bg-gradient-to-br from-[#1a1a1a] via-[#0f0f0f] to-[#0a0a0a]
|
||||||
rounded-xl sm:rounded-2xl border-2 border-[#d4af37]/30 p-5 sm:p-6 md:p-8 lg:p-10
|
rounded-xl sm:rounded-2xl border-2 border-[var(--luxury-gold)]/30 p-5 sm:p-6 md:p-8 lg:p-10
|
||||||
shadow-2xl shadow-[#d4af37]/10 backdrop-blur-xl
|
shadow-2xl shadow-[var(--luxury-gold)]/10 backdrop-blur-xl
|
||||||
relative overflow-hidden h-full group hover:border-[#d4af37]/50 transition-all duration-500">
|
relative overflow-hidden h-full group hover:border-[var(--luxury-gold)]/50 transition-all duration-500">
|
||||||
{}
|
{}
|
||||||
<div className="absolute inset-0 bg-gradient-to-br from-[#d4af37]/5 via-transparent to-transparent opacity-0 group-hover:opacity-100 transition-opacity duration-500"></div>
|
<div className="absolute inset-0 bg-gradient-to-br from-[var(--luxury-gold)]/5 via-transparent to-transparent opacity-0 group-hover:opacity-100 transition-opacity duration-500"></div>
|
||||||
|
|
||||||
<div className="relative z-10">
|
<div className="relative z-10">
|
||||||
<div className="flex items-center gap-2 sm:gap-3 mb-6 sm:mb-7 md:mb-8">
|
<div className="flex items-center gap-2 sm:gap-3 mb-6 sm:mb-7 md:mb-8">
|
||||||
<div className="w-0.5 sm:w-1 h-6 sm:h-8 bg-gradient-to-b from-[#d4af37] to-[#c9a227] rounded-full"></div>
|
<div className="w-0.5 sm:w-1 h-6 sm:h-8 bg-gradient-to-b from-[var(--luxury-gold)] to-[var(--luxury-gold-dark)] rounded-full"></div>
|
||||||
<h2 className="text-xl sm:text-2xl md:text-3xl font-serif font-semibold
|
<h2 className="text-xl sm:text-2xl md:text-3xl font-serif font-semibold
|
||||||
text-white tracking-tight">
|
text-white tracking-tight">
|
||||||
Get in Touch
|
Get in Touch
|
||||||
@@ -230,35 +230,35 @@ const ContactPage: React.FC = () => {
|
|||||||
|
|
||||||
<div className="space-y-5 sm:space-y-6 md:space-y-7">
|
<div className="space-y-5 sm:space-y-6 md:space-y-7">
|
||||||
<div className="flex items-start gap-3 sm:gap-4 md:gap-5 group/item hover:translate-x-1 transition-transform duration-300">
|
<div className="flex items-start gap-3 sm:gap-4 md:gap-5 group/item hover:translate-x-1 transition-transform duration-300">
|
||||||
<div className="p-2.5 sm:p-3 md:p-4 bg-gradient-to-br from-[#d4af37]/20 to-[#d4af37]/10 rounded-lg sm:rounded-xl border border-[#d4af37]/40 flex-shrink-0 group-hover/item:bg-gradient-to-br group-hover/item:from-[#d4af37]/30 group-hover/item:to-[#d4af37]/20 group-hover/item:border-[#d4af37]/60 transition-all duration-300 shadow-lg shadow-[#d4af37]/10">
|
<div className="p-2.5 sm:p-3 md:p-4 bg-gradient-to-br from-[var(--luxury-gold)]/20 to-[var(--luxury-gold)]/10 rounded-lg sm:rounded-xl border border-[var(--luxury-gold)]/40 flex-shrink-0 group-hover/item:bg-gradient-to-br group-hover/item:from-[var(--luxury-gold)]/30 group-hover/item:to-[var(--luxury-gold)]/20 group-hover/item:border-[var(--luxury-gold)]/60 transition-all duration-300 shadow-lg shadow-[var(--luxury-gold)]/10">
|
||||||
<Mail className="w-5 h-5 sm:w-6 sm:h-6 text-[#d4af37] drop-shadow-lg" />
|
<Mail className="w-5 h-5 sm:w-6 sm:h-6 text-[var(--luxury-gold)] drop-shadow-lg" />
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<h3 className="text-sm sm:text-base font-medium text-[#d4af37] mb-1 sm:mb-2 tracking-wide">Email</h3>
|
<h3 className="text-sm sm:text-base font-medium text-[var(--luxury-gold)] mb-1 sm:mb-2 tracking-wide">Email</h3>
|
||||||
<a href={`mailto:${displayEmail}`} className="text-gray-300 font-light text-xs sm:text-sm leading-relaxed hover:text-[#d4af37] transition-colors">
|
<a href={`mailto:${displayEmail}`} className="text-gray-300 font-light text-xs sm:text-sm leading-relaxed hover:text-[var(--luxury-gold)] transition-colors">
|
||||||
{displayEmail}
|
{displayEmail}
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="flex items-start gap-3 sm:gap-4 md:gap-5 group/item hover:translate-x-1 transition-transform duration-300">
|
<div className="flex items-start gap-3 sm:gap-4 md:gap-5 group/item hover:translate-x-1 transition-transform duration-300">
|
||||||
<div className="p-2.5 sm:p-3 md:p-4 bg-gradient-to-br from-[#d4af37]/20 to-[#d4af37]/10 rounded-lg sm:rounded-xl border border-[#d4af37]/40 flex-shrink-0 group-hover/item:bg-gradient-to-br group-hover/item:from-[#d4af37]/30 group-hover/item:to-[#d4af37]/20 group-hover/item:border-[#d4af37]/60 transition-all duration-300 shadow-lg shadow-[#d4af37]/10">
|
<div className="p-2.5 sm:p-3 md:p-4 bg-gradient-to-br from-[var(--luxury-gold)]/20 to-[var(--luxury-gold)]/10 rounded-lg sm:rounded-xl border border-[var(--luxury-gold)]/40 flex-shrink-0 group-hover/item:bg-gradient-to-br group-hover/item:from-[var(--luxury-gold)]/30 group-hover/item:to-[var(--luxury-gold)]/20 group-hover/item:border-[var(--luxury-gold)]/60 transition-all duration-300 shadow-lg shadow-[var(--luxury-gold)]/10">
|
||||||
<Phone className="w-5 h-5 sm:w-6 sm:h-6 text-[#d4af37] drop-shadow-lg" />
|
<Phone className="w-5 h-5 sm:w-6 sm:h-6 text-[var(--luxury-gold)] drop-shadow-lg" />
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<h3 className="text-sm sm:text-base font-medium text-[#d4af37] mb-1 sm:mb-2 tracking-wide">Phone</h3>
|
<h3 className="text-sm sm:text-base font-medium text-[var(--luxury-gold)] mb-1 sm:mb-2 tracking-wide">Phone</h3>
|
||||||
<a href={`tel:${displayPhone.replace(/\s+/g, '').replace(/[()]/g, '')}`} className="text-gray-300 font-light text-xs sm:text-sm leading-relaxed hover:text-[#d4af37] transition-colors">
|
<a href={`tel:${displayPhone.replace(/\s+/g, '').replace(/[()]/g, '')}`} className="text-gray-300 font-light text-xs sm:text-sm leading-relaxed hover:text-[var(--luxury-gold)] transition-colors">
|
||||||
{displayPhone}
|
{displayPhone}
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="flex items-start gap-3 sm:gap-4 md:gap-5 group/item hover:translate-x-1 transition-transform duration-300">
|
<div className="flex items-start gap-3 sm:gap-4 md:gap-5 group/item hover:translate-x-1 transition-transform duration-300">
|
||||||
<div className="p-2.5 sm:p-3 md:p-4 bg-gradient-to-br from-[#d4af37]/20 to-[#d4af37]/10 rounded-lg sm:rounded-xl border border-[#d4af37]/40 flex-shrink-0 group-hover/item:bg-gradient-to-br group-hover/item:from-[#d4af37]/30 group-hover/item:to-[#d4af37]/20 group-hover/item:border-[#d4af37]/60 transition-all duration-300 shadow-lg shadow-[#d4af37]/10">
|
<div className="p-2.5 sm:p-3 md:p-4 bg-gradient-to-br from-[var(--luxury-gold)]/20 to-[var(--luxury-gold)]/10 rounded-lg sm:rounded-xl border border-[var(--luxury-gold)]/40 flex-shrink-0 group-hover/item:bg-gradient-to-br group-hover/item:from-[var(--luxury-gold)]/30 group-hover/item:to-[var(--luxury-gold)]/20 group-hover/item:border-[var(--luxury-gold)]/60 transition-all duration-300 shadow-lg shadow-[var(--luxury-gold)]/10">
|
||||||
<MapPin className="w-5 h-5 sm:w-6 sm:h-6 text-[#d4af37] drop-shadow-lg" />
|
<MapPin className="w-5 h-5 sm:w-6 sm:h-6 text-[var(--luxury-gold)] drop-shadow-lg" />
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<h3 className="text-sm sm:text-base font-medium text-[#d4af37] mb-1 sm:mb-2 tracking-wide">Location</h3>
|
<h3 className="text-sm sm:text-base font-medium text-[var(--luxury-gold)] mb-1 sm:mb-2 tracking-wide">Location</h3>
|
||||||
<p className="text-gray-300 font-light text-xs sm:text-sm leading-relaxed whitespace-pre-line">
|
<p className="text-gray-300 font-light text-xs sm:text-sm leading-relaxed whitespace-pre-line">
|
||||||
{displayAddress}
|
{displayAddress}
|
||||||
</p>
|
</p>
|
||||||
@@ -268,11 +268,11 @@ const ContactPage: React.FC = () => {
|
|||||||
|
|
||||||
{}
|
{}
|
||||||
{pageContent?.map_url && (
|
{pageContent?.map_url && (
|
||||||
<div className="mt-6 sm:mt-7 md:mt-8 pt-6 sm:pt-7 md:pt-8 border-t border-[#d4af37]/30">
|
<div className="mt-6 sm:mt-7 md:mt-8 pt-6 sm:pt-7 md:pt-8 border-t border-[var(--luxury-gold)]/30">
|
||||||
<h3 className="text-sm sm:text-base font-medium text-[#d4af37] mb-3 sm:mb-4 tracking-wide">
|
<h3 className="text-sm sm:text-base font-medium text-[var(--luxury-gold)] mb-3 sm:mb-4 tracking-wide">
|
||||||
Find Us
|
Find Us
|
||||||
</h3>
|
</h3>
|
||||||
<div className="relative rounded-lg overflow-hidden border-2 border-[#d4af37]/30 shadow-lg shadow-[#d4af37]/10 group hover:border-[#d4af37]/50 transition-all duration-300">
|
<div className="relative rounded-lg overflow-hidden border-2 border-[var(--luxury-gold)]/30 shadow-lg shadow-[var(--luxury-gold)]/10 group hover:border-[var(--luxury-gold)]/50 transition-all duration-300">
|
||||||
<iframe
|
<iframe
|
||||||
src={pageContent.map_url}
|
src={pageContent.map_url}
|
||||||
width="100%"
|
width="100%"
|
||||||
@@ -288,7 +288,7 @@ const ContactPage: React.FC = () => {
|
|||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
<div className="mt-5 sm:mt-6 md:mt-7 pt-5 sm:pt-6 md:pt-7 border-t border-[#d4af37]/30">
|
<div className="mt-5 sm:mt-6 md:mt-7 pt-5 sm:pt-6 md:pt-7 border-t border-[var(--luxury-gold)]/30">
|
||||||
<p className="text-gray-400 font-light text-xs sm:text-sm leading-relaxed tracking-wide">
|
<p className="text-gray-400 font-light text-xs sm:text-sm leading-relaxed tracking-wide">
|
||||||
{pageContent?.content || "Our team is here to help you with any questions about your stay, bookings, or special requests. We're committed to exceeding your expectations."}
|
{pageContent?.content || "Our team is here to help you with any questions about your stay, bookings, or special requests. We're committed to exceeding your expectations."}
|
||||||
</p>
|
</p>
|
||||||
@@ -300,17 +300,17 @@ const ContactPage: React.FC = () => {
|
|||||||
{}
|
{}
|
||||||
<div className="lg:col-span-8">
|
<div className="lg:col-span-8">
|
||||||
<div className="bg-gradient-to-br from-[#1a1a1a] via-[#0f0f0f] to-[#0a0a0a]
|
<div className="bg-gradient-to-br from-[#1a1a1a] via-[#0f0f0f] to-[#0a0a0a]
|
||||||
rounded-xl sm:rounded-2xl border-2 border-[#d4af37]/30 p-5 sm:p-6 md:p-8 lg:p-10
|
rounded-xl sm:rounded-2xl border-2 border-[var(--luxury-gold)]/30 p-5 sm:p-6 md:p-8 lg:p-10
|
||||||
shadow-2xl shadow-[#d4af37]/10 backdrop-blur-xl
|
shadow-2xl shadow-[var(--luxury-gold)]/10 backdrop-blur-xl
|
||||||
relative overflow-hidden">
|
relative overflow-hidden">
|
||||||
{}
|
{}
|
||||||
<div className="absolute inset-0 opacity-5">
|
<div className="absolute inset-0 opacity-5">
|
||||||
<div className="absolute top-0 right-0 w-48 sm:w-64 md:w-96 h-48 sm:h-64 md:h-96 bg-[#d4af37] rounded-full blur-3xl"></div>
|
<div className="absolute top-0 right-0 w-48 sm:w-64 md:w-96 h-48 sm:h-64 md:h-96 bg-[var(--luxury-gold)] rounded-full blur-3xl"></div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="relative z-10">
|
<div className="relative z-10">
|
||||||
<div className="flex items-center gap-2 sm:gap-3 mb-6 sm:mb-7 md:mb-8">
|
<div className="flex items-center gap-2 sm:gap-3 mb-6 sm:mb-7 md:mb-8">
|
||||||
<div className="w-0.5 sm:w-1 h-6 sm:h-8 bg-gradient-to-b from-[#d4af37] to-[#c9a227] rounded-full"></div>
|
<div className="w-0.5 sm:w-1 h-6 sm:h-8 bg-gradient-to-b from-[var(--luxury-gold)] to-[var(--luxury-gold-dark)] rounded-full"></div>
|
||||||
<h2 className="text-xl sm:text-2xl md:text-3xl font-serif font-semibold
|
<h2 className="text-xl sm:text-2xl md:text-3xl font-serif font-semibold
|
||||||
text-white tracking-tight">
|
text-white tracking-tight">
|
||||||
Send Us a Message
|
Send Us a Message
|
||||||
@@ -330,8 +330,8 @@ const ContactPage: React.FC = () => {
|
|||||||
<div>
|
<div>
|
||||||
<label htmlFor="name" className="block text-xs sm:text-sm font-medium text-gray-300 mb-2 sm:mb-3 tracking-wide">
|
<label htmlFor="name" className="block text-xs sm:text-sm font-medium text-gray-300 mb-2 sm:mb-3 tracking-wide">
|
||||||
<span className="flex items-center gap-1.5 sm:gap-2">
|
<span className="flex items-center gap-1.5 sm:gap-2">
|
||||||
<User className="w-3.5 h-3.5 sm:w-4 sm:h-4 text-[#d4af37] drop-shadow-lg" />
|
<User className="w-3.5 h-3.5 sm:w-4 sm:h-4 text-[var(--luxury-gold)] drop-shadow-lg" />
|
||||||
Full Name <span className="text-[#d4af37] font-semibold">*</span>
|
Full Name <span className="text-[var(--luxury-gold)] font-semibold">*</span>
|
||||||
</span>
|
</span>
|
||||||
</label>
|
</label>
|
||||||
<input
|
<input
|
||||||
@@ -342,9 +342,9 @@ const ContactPage: React.FC = () => {
|
|||||||
onChange={handleChange}
|
onChange={handleChange}
|
||||||
className={`w-full px-4 sm:px-5 py-3 sm:py-4 bg-gradient-to-br from-[#0f0f0f] to-[#0a0a0a] border-2 rounded-lg
|
className={`w-full px-4 sm:px-5 py-3 sm:py-4 bg-gradient-to-br from-[#0f0f0f] to-[#0a0a0a] border-2 rounded-lg
|
||||||
text-white text-sm sm:text-base placeholder-gray-500/60
|
text-white text-sm sm:text-base placeholder-gray-500/60
|
||||||
focus:outline-none focus:ring-2 focus:ring-[#d4af37]/50 focus:border-[#d4af37] focus:shadow-lg focus:shadow-[#d4af37]/20
|
focus:outline-none focus:ring-2 focus:ring-[var(--luxury-gold)]/50 focus:border-[var(--luxury-gold)] focus:shadow-lg focus:shadow-[var(--luxury-gold)]/20
|
||||||
transition-all duration-300 hover:border-[#d4af37]/40
|
transition-all duration-300 hover:border-[var(--luxury-gold)]/40
|
||||||
${errors.name ? 'border-red-500/50 focus:border-red-500 focus:ring-red-500/50' : 'border-[#d4af37]/30'}`}
|
${errors.name ? 'border-red-500/50 focus:border-red-500 focus:ring-red-500/50' : 'border-[var(--luxury-gold)]/30'}`}
|
||||||
placeholder="Enter your full name"
|
placeholder="Enter your full name"
|
||||||
/>
|
/>
|
||||||
{errors.name && (
|
{errors.name && (
|
||||||
@@ -358,8 +358,8 @@ const ContactPage: React.FC = () => {
|
|||||||
<div>
|
<div>
|
||||||
<label htmlFor="email" className="block text-xs sm:text-sm font-medium text-gray-300 mb-2 sm:mb-3 tracking-wide">
|
<label htmlFor="email" className="block text-xs sm:text-sm font-medium text-gray-300 mb-2 sm:mb-3 tracking-wide">
|
||||||
<span className="flex items-center gap-1.5 sm:gap-2">
|
<span className="flex items-center gap-1.5 sm:gap-2">
|
||||||
<Mail className="w-3.5 h-3.5 sm:w-4 sm:h-4 text-[#d4af37] drop-shadow-lg" />
|
<Mail className="w-3.5 h-3.5 sm:w-4 sm:h-4 text-[var(--luxury-gold)] drop-shadow-lg" />
|
||||||
Email <span className="text-[#d4af37] font-semibold">*</span>
|
Email <span className="text-[var(--luxury-gold)] font-semibold">*</span>
|
||||||
</span>
|
</span>
|
||||||
</label>
|
</label>
|
||||||
<input
|
<input
|
||||||
@@ -370,9 +370,9 @@ const ContactPage: React.FC = () => {
|
|||||||
onChange={handleChange}
|
onChange={handleChange}
|
||||||
className={`w-full px-4 sm:px-5 py-3 sm:py-4 bg-gradient-to-br from-[#0f0f0f] to-[#0a0a0a] border-2 rounded-lg
|
className={`w-full px-4 sm:px-5 py-3 sm:py-4 bg-gradient-to-br from-[#0f0f0f] to-[#0a0a0a] border-2 rounded-lg
|
||||||
text-white text-sm sm:text-base placeholder-gray-500/60
|
text-white text-sm sm:text-base placeholder-gray-500/60
|
||||||
focus:outline-none focus:ring-2 focus:ring-[#d4af37]/50 focus:border-[#d4af37] focus:shadow-lg focus:shadow-[#d4af37]/20
|
focus:outline-none focus:ring-2 focus:ring-[var(--luxury-gold)]/50 focus:border-[var(--luxury-gold)] focus:shadow-lg focus:shadow-[var(--luxury-gold)]/20
|
||||||
transition-all duration-300 hover:border-[#d4af37]/40
|
transition-all duration-300 hover:border-[var(--luxury-gold)]/40
|
||||||
${errors.email ? 'border-red-500/50 focus:border-red-500 focus:ring-red-500/50' : 'border-[#d4af37]/30'}`}
|
${errors.email ? 'border-red-500/50 focus:border-red-500 focus:ring-red-500/50' : 'border-[var(--luxury-gold)]/30'}`}
|
||||||
placeholder="your.email@example.com"
|
placeholder="your.email@example.com"
|
||||||
/>
|
/>
|
||||||
{errors.email && (
|
{errors.email && (
|
||||||
@@ -384,7 +384,7 @@ const ContactPage: React.FC = () => {
|
|||||||
<div>
|
<div>
|
||||||
<label htmlFor="phone" className="block text-xs sm:text-sm font-medium text-gray-300 mb-2 sm:mb-3 tracking-wide">
|
<label htmlFor="phone" className="block text-xs sm:text-sm font-medium text-gray-300 mb-2 sm:mb-3 tracking-wide">
|
||||||
<span className="flex items-center gap-1.5 sm:gap-2">
|
<span className="flex items-center gap-1.5 sm:gap-2">
|
||||||
<Phone className="w-3.5 h-3.5 sm:w-4 sm:h-4 text-[#d4af37] drop-shadow-lg" />
|
<Phone className="w-3.5 h-3.5 sm:w-4 sm:h-4 text-[var(--luxury-gold)] drop-shadow-lg" />
|
||||||
Phone <span className="text-gray-500 text-xs">(Optional)</span>
|
Phone <span className="text-gray-500 text-xs">(Optional)</span>
|
||||||
</span>
|
</span>
|
||||||
</label>
|
</label>
|
||||||
@@ -394,10 +394,10 @@ const ContactPage: React.FC = () => {
|
|||||||
name="phone"
|
name="phone"
|
||||||
value={formData.phone}
|
value={formData.phone}
|
||||||
onChange={handleChange}
|
onChange={handleChange}
|
||||||
className="w-full px-4 sm:px-5 py-3 sm:py-4 bg-gradient-to-br from-[#0f0f0f] to-[#0a0a0a] border-2 border-[#d4af37]/30 rounded-lg
|
className="w-full px-4 sm:px-5 py-3 sm:py-4 bg-gradient-to-br from-[#0f0f0f] to-[#0a0a0a] border-2 border-[var(--luxury-gold)]/30 rounded-lg
|
||||||
text-white text-sm sm:text-base placeholder-gray-500/60
|
text-white text-sm sm:text-base placeholder-gray-500/60
|
||||||
focus:outline-none focus:ring-2 focus:ring-[#d4af37]/50 focus:border-[#d4af37] focus:shadow-lg focus:shadow-[#d4af37]/20
|
focus:outline-none focus:ring-2 focus:ring-[var(--luxury-gold)]/50 focus:border-[var(--luxury-gold)] focus:shadow-lg focus:shadow-[var(--luxury-gold)]/20
|
||||||
transition-all duration-300 hover:border-[#d4af37]/40"
|
transition-all duration-300 hover:border-[var(--luxury-gold)]/40"
|
||||||
placeholder="+1 (555) 123-4567"
|
placeholder="+1 (555) 123-4567"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
@@ -407,8 +407,8 @@ const ContactPage: React.FC = () => {
|
|||||||
<div>
|
<div>
|
||||||
<label htmlFor="subject" className="block text-xs sm:text-sm font-medium text-gray-300 mb-2 sm:mb-3 tracking-wide">
|
<label htmlFor="subject" className="block text-xs sm:text-sm font-medium text-gray-300 mb-2 sm:mb-3 tracking-wide">
|
||||||
<span className="flex items-center gap-1.5 sm:gap-2">
|
<span className="flex items-center gap-1.5 sm:gap-2">
|
||||||
<MessageSquare className="w-3.5 h-3.5 sm:w-4 sm:h-4 text-[#d4af37] drop-shadow-lg" />
|
<MessageSquare className="w-3.5 h-3.5 sm:w-4 sm:h-4 text-[var(--luxury-gold)] drop-shadow-lg" />
|
||||||
Subject <span className="text-[#d4af37] font-semibold">*</span>
|
Subject <span className="text-[var(--luxury-gold)] font-semibold">*</span>
|
||||||
</span>
|
</span>
|
||||||
</label>
|
</label>
|
||||||
<input
|
<input
|
||||||
@@ -419,9 +419,9 @@ const ContactPage: React.FC = () => {
|
|||||||
onChange={handleChange}
|
onChange={handleChange}
|
||||||
className={`w-full px-4 sm:px-5 py-3 sm:py-4 bg-gradient-to-br from-[#0f0f0f] to-[#0a0a0a] border-2 rounded-lg
|
className={`w-full px-4 sm:px-5 py-3 sm:py-4 bg-gradient-to-br from-[#0f0f0f] to-[#0a0a0a] border-2 rounded-lg
|
||||||
text-white text-sm sm:text-base placeholder-gray-500/60
|
text-white text-sm sm:text-base placeholder-gray-500/60
|
||||||
focus:outline-none focus:ring-2 focus:ring-[#d4af37]/50 focus:border-[#d4af37] focus:shadow-lg focus:shadow-[#d4af37]/20
|
focus:outline-none focus:ring-2 focus:ring-[var(--luxury-gold)]/50 focus:border-[var(--luxury-gold)] focus:shadow-lg focus:shadow-[var(--luxury-gold)]/20
|
||||||
transition-all duration-300 hover:border-[#d4af37]/40
|
transition-all duration-300 hover:border-[var(--luxury-gold)]/40
|
||||||
${errors.subject ? 'border-red-500/50 focus:border-red-500 focus:ring-red-500/50' : 'border-[#d4af37]/30'}`}
|
${errors.subject ? 'border-red-500/50 focus:border-red-500 focus:ring-red-500/50' : 'border-[var(--luxury-gold)]/30'}`}
|
||||||
placeholder="What is this regarding?"
|
placeholder="What is this regarding?"
|
||||||
/>
|
/>
|
||||||
{errors.subject && (
|
{errors.subject && (
|
||||||
@@ -433,8 +433,8 @@ const ContactPage: React.FC = () => {
|
|||||||
<div>
|
<div>
|
||||||
<label htmlFor="message" className="block text-xs sm:text-sm font-medium text-gray-300 mb-2 sm:mb-3 tracking-wide">
|
<label htmlFor="message" className="block text-xs sm:text-sm font-medium text-gray-300 mb-2 sm:mb-3 tracking-wide">
|
||||||
<span className="flex items-center gap-1.5 sm:gap-2">
|
<span className="flex items-center gap-1.5 sm:gap-2">
|
||||||
<MessageSquare className="w-3.5 h-3.5 sm:w-4 sm:h-4 text-[#d4af37] drop-shadow-lg" />
|
<MessageSquare className="w-3.5 h-3.5 sm:w-4 sm:h-4 text-[var(--luxury-gold)] drop-shadow-lg" />
|
||||||
Message <span className="text-[#d4af37] font-semibold">*</span>
|
Message <span className="text-[var(--luxury-gold)] font-semibold">*</span>
|
||||||
</span>
|
</span>
|
||||||
</label>
|
</label>
|
||||||
<textarea
|
<textarea
|
||||||
@@ -445,9 +445,9 @@ const ContactPage: React.FC = () => {
|
|||||||
rows={6}
|
rows={6}
|
||||||
className={`w-full px-4 sm:px-5 py-3 sm:py-4 bg-gradient-to-br from-[#0f0f0f] to-[#0a0a0a] border-2 rounded-lg
|
className={`w-full px-4 sm:px-5 py-3 sm:py-4 bg-gradient-to-br from-[#0f0f0f] to-[#0a0a0a] border-2 rounded-lg
|
||||||
text-white text-sm sm:text-base placeholder-gray-500/60 resize-none
|
text-white text-sm sm:text-base placeholder-gray-500/60 resize-none
|
||||||
focus:outline-none focus:ring-2 focus:ring-[#d4af37]/50 focus:border-[#d4af37] focus:shadow-lg focus:shadow-[#d4af37]/20
|
focus:outline-none focus:ring-2 focus:ring-[var(--luxury-gold)]/50 focus:border-[var(--luxury-gold)] focus:shadow-lg focus:shadow-[var(--luxury-gold)]/20
|
||||||
transition-all duration-300 hover:border-[#d4af37]/40
|
transition-all duration-300 hover:border-[var(--luxury-gold)]/40
|
||||||
${errors.message ? 'border-red-500/50 focus:border-red-500 focus:ring-red-500/50' : 'border-[#d4af37]/30'}`}
|
${errors.message ? 'border-red-500/50 focus:border-red-500 focus:ring-red-500/50' : 'border-[var(--luxury-gold)]/30'}`}
|
||||||
placeholder="Tell us more about your inquiry..."
|
placeholder="Tell us more about your inquiry..."
|
||||||
/>
|
/>
|
||||||
{errors.message && (
|
{errors.message && (
|
||||||
@@ -475,14 +475,14 @@ const ContactPage: React.FC = () => {
|
|||||||
type="submit"
|
type="submit"
|
||||||
disabled={loading}
|
disabled={loading}
|
||||||
className="group w-full sm:w-auto inline-flex items-center justify-center gap-2 sm:gap-3
|
className="group w-full sm:w-auto inline-flex items-center justify-center gap-2 sm:gap-3
|
||||||
bg-gradient-to-r from-[#d4af37] via-[#f5d76e] to-[#d4af37]
|
bg-gradient-to-r from-[var(--luxury-gold)] via-[var(--luxury-gold-light)] to-[var(--luxury-gold)]
|
||||||
text-[#0f0f0f] font-semibold
|
text-[#0f0f0f] font-semibold
|
||||||
active:scale-[0.98]
|
active:scale-[0.98]
|
||||||
disabled:opacity-50 disabled:cursor-not-allowed
|
disabled:opacity-50 disabled:cursor-not-allowed
|
||||||
transition-all duration-500
|
transition-all duration-500
|
||||||
tracking-wide text-sm sm:text-base
|
tracking-wide text-sm sm:text-base
|
||||||
px-6 sm:px-8 md:px-10 py-3 sm:py-3.5 md:py-4 rounded-lg
|
px-6 sm:px-8 md:px-10 py-3 sm:py-3.5 md:py-4 rounded-lg
|
||||||
shadow-2xl shadow-[#d4af37]/40 hover:shadow-[#d4af37]/60 hover:scale-[1.02]
|
shadow-2xl shadow-[var(--luxury-gold)]/40 hover:shadow-[var(--luxury-gold)]/60 hover:scale-[1.02]
|
||||||
relative overflow-hidden
|
relative overflow-hidden
|
||||||
touch-manipulation min-h-[44px] sm:min-h-[48px] md:min-h-[52px]"
|
touch-manipulation min-h-[44px] sm:min-h-[48px] md:min-h-[52px]"
|
||||||
>
|
>
|
||||||
|
|||||||
@@ -94,12 +94,12 @@ const FAQPage: React.FC = () => {
|
|||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<div className="text-center">
|
<div className="text-center">
|
||||||
<HelpCircle className="w-16 h-16 text-[#d4af37]/50 mx-auto mb-4" />
|
<HelpCircle className="w-16 h-16 text-[var(--luxury-gold)]/50 mx-auto mb-4" />
|
||||||
<h1 className="text-2xl font-elegant font-bold text-white mb-2">Frequently Asked Questions</h1>
|
<h1 className="text-2xl font-elegant font-bold text-white mb-2">Frequently Asked Questions</h1>
|
||||||
<p className="text-gray-400">This page is currently unavailable.</p>
|
<p className="text-gray-400">This page is currently unavailable.</p>
|
||||||
<Link
|
<Link
|
||||||
to="/"
|
to="/"
|
||||||
className="inline-flex items-center gap-2 text-[#d4af37]/80 hover:text-[#d4af37] mt-6 transition-all duration-300"
|
className="inline-flex items-center gap-2 text-[var(--luxury-gold)]/80 hover:text-[var(--luxury-gold)] mt-6 transition-all duration-300"
|
||||||
>
|
>
|
||||||
<ArrowLeft className="w-4 h-4" />
|
<ArrowLeft className="w-4 h-4" />
|
||||||
<span>Back to Home</span>
|
<span>Back to Home</span>
|
||||||
@@ -123,17 +123,17 @@ const FAQPage: React.FC = () => {
|
|||||||
<div className="w-full px-3 sm:px-4 md:px-6 lg:px-8 py-8 sm:py-12 max-w-5xl mx-auto">
|
<div className="w-full px-3 sm:px-4 md:px-6 lg:px-8 py-8 sm:py-12 max-w-5xl mx-auto">
|
||||||
<Link
|
<Link
|
||||||
to="/"
|
to="/"
|
||||||
className="inline-flex items-center gap-2 text-[#d4af37]/80 hover:text-[#d4af37] mb-6 transition-all duration-300 group font-light tracking-wide text-xs sm:text-sm"
|
className="inline-flex items-center gap-2 text-[var(--luxury-gold)]/80 hover:text-[var(--luxury-gold)] mb-6 transition-all duration-300 group font-light tracking-wide text-xs sm:text-sm"
|
||||||
>
|
>
|
||||||
<ArrowLeft className="w-4 h-4 group-hover:-translate-x-1 transition-transform" />
|
<ArrowLeft className="w-4 h-4 group-hover:-translate-x-1 transition-transform" />
|
||||||
<span>Back to Home</span>
|
<span>Back to Home</span>
|
||||||
</Link>
|
</Link>
|
||||||
|
|
||||||
<div className="mb-8 sm:mb-12 text-center">
|
<div className="mb-8 sm:mb-12 text-center">
|
||||||
<div className="inline-flex items-center justify-center w-16 h-16 sm:w-20 sm:h-20 mb-4 sm:mb-6 bg-gradient-to-br from-[#d4af37]/20 to-[#c9a227]/10 rounded-full border border-[#d4af37]/30">
|
<div className="inline-flex items-center justify-center w-16 h-16 sm:w-20 sm:h-20 mb-4 sm:mb-6 bg-gradient-to-br from-[var(--luxury-gold)]/20 to-[var(--luxury-gold-dark)]/10 rounded-full border border-[var(--luxury-gold)]/30">
|
||||||
<HelpCircle className="w-8 h-8 sm:w-10 sm:h-10 text-[#d4af37]" />
|
<HelpCircle className="w-8 h-8 sm:w-10 sm:h-10 text-[var(--luxury-gold)]" />
|
||||||
</div>
|
</div>
|
||||||
<h1 className="text-3xl sm:text-4xl lg:text-5xl font-elegant font-bold text-white mb-3 sm:mb-4 tracking-tight leading-tight bg-gradient-to-r from-white via-[#d4af37] to-white bg-clip-text text-transparent">
|
<h1 className="text-3xl sm:text-4xl lg:text-5xl font-elegant font-bold text-white mb-3 sm:mb-4 tracking-tight leading-tight bg-gradient-to-r from-white via-[var(--luxury-gold)] to-white bg-clip-text text-transparent">
|
||||||
{pageContent.title || 'Frequently Asked Questions'}
|
{pageContent.title || 'Frequently Asked Questions'}
|
||||||
</h1>
|
</h1>
|
||||||
{pageContent.subtitle && (
|
{pageContent.subtitle && (
|
||||||
@@ -143,18 +143,18 @@ const FAQPage: React.FC = () => {
|
|||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="bg-gradient-to-br from-[#1a1a1a] to-[#0a0a0a] rounded-xl border border-[#d4af37]/20 backdrop-blur-xl shadow-2xl shadow-[#d4af37]/5 p-6 sm:p-8 lg:p-12">
|
<div className="bg-gradient-to-br from-[#1a1a1a] to-[#0a0a0a] rounded-xl border border-[var(--luxury-gold)]/20 backdrop-blur-xl shadow-2xl shadow-[var(--luxury-gold)]/5 p-6 sm:p-8 lg:p-12">
|
||||||
<div
|
<div
|
||||||
className="prose prose-invert prose-lg max-w-none text-gray-300
|
className="prose prose-invert prose-lg max-w-none text-gray-300
|
||||||
prose-headings:text-white prose-headings:font-elegant prose-headings:font-semibold
|
prose-headings:text-white prose-headings:font-elegant prose-headings:font-semibold
|
||||||
prose-h2:text-2xl prose-h2:mt-8 prose-h2:mb-4 prose-h2:border-b prose-h2:border-[#d4af37]/20 prose-h2:pb-2
|
prose-h2:text-2xl prose-h2:mt-8 prose-h2:mb-4 prose-h2:border-b prose-h2:border-[var(--luxury-gold)]/20 prose-h2:pb-2
|
||||||
prose-p:text-gray-300 prose-p:font-light prose-p:leading-relaxed prose-p:mb-4
|
prose-p:text-gray-300 prose-p:font-light prose-p:leading-relaxed prose-p:mb-4
|
||||||
prose-ul:text-gray-300 prose-ul:font-light prose-ul:my-4
|
prose-ul:text-gray-300 prose-ul:font-light prose-ul:my-4
|
||||||
prose-li:text-gray-300 prose-li:mb-2 prose-li:ml-4
|
prose-li:text-gray-300 prose-li:mb-2 prose-li:ml-4
|
||||||
prose-strong:text-[#d4af37] prose-strong:font-medium
|
prose-strong:text-[var(--luxury-gold)] prose-strong:font-medium
|
||||||
prose-a:text-[#d4af37] prose-a:no-underline hover:prose-a:underline
|
prose-a:text-[var(--luxury-gold)] prose-a:no-underline hover:prose-a:underline
|
||||||
[&_*]:text-gray-300 [&_h1]:text-white [&_h2]:text-white [&_h3]:text-white [&_h4]:text-white [&_h5]:text-white [&_h6]:text-white
|
[&_*]:text-gray-300 [&_h1]:text-white [&_h2]:text-white [&_h3]:text-white [&_h4]:text-white [&_h5]:text-white [&_h6]:text-white
|
||||||
[&_strong]:text-[#d4af37] [&_b]:text-[#d4af37] [&_a]:text-[#d4af37]"
|
[&_strong]:text-[var(--luxury-gold)] [&_b]:text-[var(--luxury-gold)] [&_a]:text-[var(--luxury-gold)]"
|
||||||
style={{ color: '#d1d5db' }}
|
style={{ color: '#d1d5db' }}
|
||||||
dangerouslySetInnerHTML={createSanitizedHtml(
|
dangerouslySetInnerHTML={createSanitizedHtml(
|
||||||
pageContent.content || pageContent.description || '<p style="color: #d1d5db;">No content available.</p>'
|
pageContent.content || pageContent.description || '<p style="color: #d1d5db;">No content available.</p>'
|
||||||
@@ -166,7 +166,7 @@ const FAQPage: React.FC = () => {
|
|||||||
<div className="mt-8 text-center">
|
<div className="mt-8 text-center">
|
||||||
<p className="text-sm text-gray-400 font-light">
|
<p className="text-sm text-gray-400 font-light">
|
||||||
Still have questions? Contact us at{' '}
|
Still have questions? Contact us at{' '}
|
||||||
<a href={`mailto:${settings.company_email}`} className="text-[#d4af37] hover:underline">
|
<a href={`mailto:${settings.company_email}`} className="text-[var(--luxury-gold)] hover:underline">
|
||||||
{settings.company_email}
|
{settings.company_email}
|
||||||
</a>
|
</a>
|
||||||
</p>
|
</p>
|
||||||
|
|||||||
@@ -708,7 +708,7 @@ const HomePage: React.FC = () => {
|
|||||||
|
|
||||||
<div className="min-h-screen bg-gradient-to-br from-gray-50 via-white to-gray-50/30 relative overflow-hidden">
|
<div className="min-h-screen bg-gradient-to-br from-gray-50 via-white to-gray-50/30 relative overflow-hidden">
|
||||||
{}
|
{}
|
||||||
<div className="fixed inset-0 opacity-[0.015] bg-[radial-gradient(circle_at_1px_1px,#d4af37_1px,transparent_0)] bg-[length:60px_60px] pointer-events-none"></div>
|
<div className="fixed inset-0 opacity-[0.015] bg-[radial-gradient(circle_at_1px_1px,var(--luxury-gold)_1px,transparent_0)] bg-[length:60px_60px] pointer-events-none"></div>
|
||||||
<div className="relative z-10">
|
<div className="relative z-10">
|
||||||
|
|
||||||
{}
|
{}
|
||||||
@@ -717,7 +717,7 @@ const HomePage: React.FC = () => {
|
|||||||
{}
|
{}
|
||||||
<div className="text-center animate-fade-in mb-6 md:mb-8">
|
<div className="text-center animate-fade-in mb-6 md:mb-8">
|
||||||
<div className="inline-block mb-3">
|
<div className="inline-block mb-3">
|
||||||
<div className="h-0.5 w-16 bg-gradient-to-r from-[#d4af37] to-[#f5d76e] mx-auto rounded-full"></div>
|
<div className="h-0.5 w-16 bg-gradient-to-r from-[var(--luxury-gold)] to-[var(--luxury-gold-light)] mx-auto rounded-full"></div>
|
||||||
</div>
|
</div>
|
||||||
<h2 className="text-2xl sm:text-3xl md:text-4xl font-serif font-bold text-gray-900 tracking-tight mb-3 md:mb-4">
|
<h2 className="text-2xl sm:text-3xl md:text-4xl font-serif font-bold text-gray-900 tracking-tight mb-3 md:mb-4">
|
||||||
{pageContent?.rooms_section_title || pageContent?.hero_title || (apiError ? '' : 'Featured & Newest Rooms')}
|
{pageContent?.rooms_section_title || pageContent?.hero_title || (apiError ? '' : 'Featured & Newest Rooms')}
|
||||||
@@ -730,9 +730,9 @@ const HomePage: React.FC = () => {
|
|||||||
<div className="mt-6 md:mt-8 flex justify-center">
|
<div className="mt-6 md:mt-8 flex justify-center">
|
||||||
<Link
|
<Link
|
||||||
to={pageContent?.rooms_section_button_link || '/rooms'}
|
to={pageContent?.rooms_section_button_link || '/rooms'}
|
||||||
className="group relative inline-flex items-center gap-2 px-6 py-2.5 md:px-8 md:py-3 bg-gradient-to-r from-[#d4af37] to-[#c9a227] text-[#0f0f0f] rounded-lg font-semibold tracking-wide text-sm md:text-base shadow-md shadow-[#d4af37]/20 hover:shadow-lg hover:shadow-[#d4af37]/30 hover:-translate-y-0.5 transition-all duration-300 overflow-hidden"
|
className="group relative inline-flex items-center gap-2 px-6 py-2.5 md:px-8 md:py-3 bg-gradient-to-r from-[var(--luxury-gold)] to-[var(--luxury-gold-dark)] text-[#0f0f0f] rounded-lg font-semibold tracking-wide text-sm md:text-base shadow-md shadow-[var(--luxury-gold)]/20 hover:shadow-lg hover:shadow-[var(--luxury-gold)]/30 hover:-translate-y-0.5 transition-all duration-300 overflow-hidden"
|
||||||
>
|
>
|
||||||
<span className="absolute inset-0 bg-gradient-to-r from-[#f5d76e] to-[#d4af37] opacity-0 group-hover:opacity-100 transition-opacity duration-300"></span>
|
<span className="absolute inset-0 bg-gradient-to-r from-[var(--luxury-gold-light)] to-[var(--luxury-gold)] opacity-0 group-hover:opacity-100 transition-opacity duration-300"></span>
|
||||||
<span className="relative z-10">{pageContent?.rooms_section_button_text || 'View All Rooms'}</span>
|
<span className="relative z-10">{pageContent?.rooms_section_button_text || 'View All Rooms'}</span>
|
||||||
<ArrowRight className="w-4 h-4 md:w-5 md:h-5 relative z-10 group-hover:translate-x-1 transition-transform duration-300" />
|
<ArrowRight className="w-4 h-4 md:w-5 md:h-5 relative z-10 group-hover:translate-x-1 transition-transform duration-300" />
|
||||||
</Link>
|
</Link>
|
||||||
@@ -810,18 +810,18 @@ const HomePage: React.FC = () => {
|
|||||||
// Only show section if we have features from API, or if pageContent was loaded but is empty (not if API failed)
|
// Only show section if we have features from API, or if pageContent was loaded but is empty (not if API failed)
|
||||||
return validFeatures.length > 0 && (
|
return validFeatures.length > 0 && (
|
||||||
<section className="container mx-auto px-4 sm:px-6 lg:px-8 py-12 md:py-16">
|
<section className="container mx-auto px-4 sm:px-6 lg:px-8 py-12 md:py-16">
|
||||||
<div className="relative bg-white rounded-xl md:rounded-2xl shadow-xl shadow-[#d4af37]/5 p-6 md:p-8 lg:p-10 animate-fade-in overflow-hidden border border-gray-100/50">
|
<div className="relative bg-white rounded-xl md:rounded-2xl shadow-xl shadow-[var(--luxury-gold)]/5 p-6 md:p-8 lg:p-10 animate-fade-in overflow-hidden border border-gray-100/50">
|
||||||
{}
|
{}
|
||||||
<div className="absolute top-0 left-0 right-0 h-1 bg-gradient-to-r from-[#d4af37] via-[#f5d76e] to-[#d4af37]"></div>
|
<div className="absolute top-0 left-0 right-0 h-1 bg-gradient-to-r from-[var(--luxury-gold)] via-[var(--luxury-gold-light)] to-[var(--luxury-gold)]"></div>
|
||||||
<div className="absolute bottom-0 left-0 right-0 h-1 bg-gradient-to-r from-[#d4af37] via-[#f5d76e] to-[#d4af37]"></div>
|
<div className="absolute bottom-0 left-0 right-0 h-1 bg-gradient-to-r from-[var(--luxury-gold)] via-[var(--luxury-gold-light)] to-[var(--luxury-gold)]"></div>
|
||||||
|
|
||||||
{}
|
{}
|
||||||
<div className="absolute inset-0 opacity-[0.015] bg-[radial-gradient(circle_at_1px_1px,#d4af37_1px,transparent_0)] bg-[length:40px_40px]"></div>
|
<div className="absolute inset-0 opacity-[0.015] bg-[radial-gradient(circle_at_1px_1px,var(--luxury-gold)_1px,transparent_0)] bg-[length:40px_40px]"></div>
|
||||||
|
|
||||||
{(pageContent?.features_section_title || pageContent?.features_section_subtitle) && (
|
{(pageContent?.features_section_title || pageContent?.features_section_subtitle) && (
|
||||||
<div className="text-center mb-8 md:mb-10 animate-fade-in">
|
<div className="text-center mb-8 md:mb-10 animate-fade-in">
|
||||||
<div className="inline-block mb-3">
|
<div className="inline-block mb-3">
|
||||||
<div className="h-0.5 w-16 md:w-20 bg-gradient-to-r from-[#d4af37] to-[#f5d76e] mx-auto rounded-full"></div>
|
<div className="h-0.5 w-16 md:w-20 bg-gradient-to-r from-[var(--luxury-gold)] to-[var(--luxury-gold-light)] mx-auto rounded-full"></div>
|
||||||
</div>
|
</div>
|
||||||
{pageContent.features_section_title && (
|
{pageContent.features_section_title && (
|
||||||
<h2 className="text-2xl sm:text-3xl md:text-4xl font-serif font-bold text-gray-900 tracking-tight mb-3 md:mb-4 px-4">
|
<h2 className="text-2xl sm:text-3xl md:text-4xl font-serif font-bold text-gray-900 tracking-tight mb-3 md:mb-4 px-4">
|
||||||
@@ -841,20 +841,20 @@ const HomePage: React.FC = () => {
|
|||||||
validFeatures.map((feature: any, index: number) => (
|
validFeatures.map((feature: any, index: number) => (
|
||||||
<div key={`feature-${index}-${feature.title || index}`} className="text-center group relative">
|
<div key={`feature-${index}-${feature.title || index}`} className="text-center group relative">
|
||||||
{feature.image ? (
|
{feature.image ? (
|
||||||
<div className="w-16 h-16 md:w-20 md:h-20 mx-auto mb-4 md:mb-5 rounded-lg overflow-hidden shadow-lg shadow-[#d4af37]/15 group-hover:scale-110 group-hover:shadow-xl group-hover:shadow-[#d4af37]/25 transition-all duration-300 border border-[#d4af37]/20">
|
<div className="w-16 h-16 md:w-20 md:h-20 mx-auto mb-4 md:mb-5 rounded-lg overflow-hidden shadow-lg shadow-[var(--luxury-gold)]/15 group-hover:scale-110 group-hover:shadow-xl group-hover:shadow-[var(--luxury-gold)]/25 transition-all duration-300 border border-[var(--luxury-gold)]/20">
|
||||||
<img src={feature.image} alt={feature.title || 'Feature'} className="w-full h-full object-cover" />
|
<img src={feature.image} alt={feature.title || 'Feature'} className="w-full h-full object-cover" />
|
||||||
</div>
|
</div>
|
||||||
) : (
|
) : (
|
||||||
<div
|
<div
|
||||||
className="w-16 h-16 md:w-18 md:h-18 bg-gradient-to-br from-[#d4af37]/15 via-[#f5d76e]/10 to-[#d4af37]/15
|
className="w-16 h-16 md:w-18 md:h-18 bg-gradient-to-br from-[var(--luxury-gold)]/15 via-[var(--luxury-gold-light)]/10 to-[var(--luxury-gold)]/15
|
||||||
rounded-lg flex items-center justify-center mx-auto mb-4 md:mb-5
|
rounded-lg flex items-center justify-center mx-auto mb-4 md:mb-5
|
||||||
shadow-lg shadow-[#d4af37]/15 border border-[#d4af37]/25
|
shadow-lg shadow-[var(--luxury-gold)]/15 border border-[var(--luxury-gold)]/25
|
||||||
group-hover:scale-110 group-hover:shadow-xl group-hover:shadow-[#d4af37]/30 group-hover:border-[#d4af37]/40
|
group-hover:scale-110 group-hover:shadow-xl group-hover:shadow-[var(--luxury-gold)]/30 group-hover:border-[var(--luxury-gold)]/40
|
||||||
transition-all duration-300 backdrop-blur-sm"
|
transition-all duration-300 backdrop-blur-sm"
|
||||||
>
|
>
|
||||||
{feature.icon && (LucideIcons as any)[feature.icon] ? (
|
{feature.icon && (LucideIcons as any)[feature.icon] ? (
|
||||||
React.createElement((LucideIcons as any)[feature.icon], {
|
React.createElement((LucideIcons as any)[feature.icon], {
|
||||||
className: 'w-7 h-7 md:w-8 md:h-8 text-[#d4af37] drop-shadow-md'
|
className: 'w-7 h-7 md:w-8 md:h-8 text-[var(--luxury-gold)] drop-shadow-md'
|
||||||
})
|
})
|
||||||
) : (
|
) : (
|
||||||
<span className="text-3xl">✨</span>
|
<span className="text-3xl">✨</span>
|
||||||
@@ -864,7 +864,7 @@ const HomePage: React.FC = () => {
|
|||||||
{feature.title && (
|
{feature.title && (
|
||||||
<h3
|
<h3
|
||||||
className="text-lg md:text-xl font-serif font-semibold mb-2 md:mb-3
|
className="text-lg md:text-xl font-serif font-semibold mb-2 md:mb-3
|
||||||
text-gray-900 group-hover:text-[#d4af37] transition-colors duration-300 tracking-tight leading-tight"
|
text-gray-900 group-hover:text-[var(--luxury-gold)] transition-colors duration-300 tracking-tight leading-tight"
|
||||||
>
|
>
|
||||||
{feature.title}
|
{feature.title}
|
||||||
</h3>
|
</h3>
|
||||||
@@ -888,7 +888,7 @@ const HomePage: React.FC = () => {
|
|||||||
<section className="container mx-auto px-4 sm:px-6 lg:px-8 py-12 md:py-16 bg-gradient-to-b from-white via-gray-50/20 to-white">
|
<section className="container mx-auto px-4 sm:px-6 lg:px-8 py-12 md:py-16 bg-gradient-to-b from-white via-gray-50/20 to-white">
|
||||||
<div className="text-center mb-8 md:mb-10 animate-fade-in">
|
<div className="text-center mb-8 md:mb-10 animate-fade-in">
|
||||||
<div className="inline-block mb-3">
|
<div className="inline-block mb-3">
|
||||||
<div className="h-0.5 w-16 md:w-20 bg-gradient-to-r from-[#d4af37] to-[#f5d76e] mx-auto rounded-full"></div>
|
<div className="h-0.5 w-16 md:w-20 bg-gradient-to-r from-[var(--luxury-gold)] to-[var(--luxury-gold-light)] mx-auto rounded-full"></div>
|
||||||
</div>
|
</div>
|
||||||
<h2 className="text-2xl sm:text-3xl md:text-4xl font-serif font-bold text-gray-900 tracking-tight mb-3 md:mb-4 px-4">
|
<h2 className="text-2xl sm:text-3xl md:text-4xl font-serif font-bold text-gray-900 tracking-tight mb-3 md:mb-4 px-4">
|
||||||
{pageContent.luxury_section_title || 'Experience Unparalleled Luxury'}
|
{pageContent.luxury_section_title || 'Experience Unparalleled Luxury'}
|
||||||
@@ -901,7 +901,7 @@ const HomePage: React.FC = () => {
|
|||||||
</div>
|
</div>
|
||||||
{pageContent.luxury_section_image && (
|
{pageContent.luxury_section_image && (
|
||||||
<div className="mb-8 md:mb-10 animate-fade-in px-4">
|
<div className="mb-8 md:mb-10 animate-fade-in px-4">
|
||||||
<div className="relative rounded-xl md:rounded-2xl overflow-hidden shadow-xl shadow-[#d4af37]/10 group">
|
<div className="relative rounded-xl md:rounded-2xl overflow-hidden shadow-xl shadow-[var(--luxury-gold)]/10 group">
|
||||||
<img
|
<img
|
||||||
src={pageContent.luxury_section_image}
|
src={pageContent.luxury_section_image}
|
||||||
alt="Luxury Experience"
|
alt="Luxury Experience"
|
||||||
@@ -914,18 +914,18 @@ const HomePage: React.FC = () => {
|
|||||||
{pageContent.luxury_features && pageContent.luxury_features.length > 0 && (
|
{pageContent.luxury_features && pageContent.luxury_features.length > 0 && (
|
||||||
<div className="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 gap-5 md:gap-6 px-4">
|
<div className="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 gap-5 md:gap-6 px-4">
|
||||||
{pageContent.luxury_features.map((feature, index) => (
|
{pageContent.luxury_features.map((feature, index) => (
|
||||||
<div key={index} className="relative bg-white rounded-lg md:rounded-xl p-5 md:p-6 group hover:shadow-xl hover:shadow-[#d4af37]/10 transition-all duration-300 animate-fade-in border border-gray-100/50 hover:border-[#d4af37]/25 hover:-translate-y-1" style={{ animationDelay: `${index * 0.1}s` }}>
|
<div key={index} className="relative bg-white rounded-lg md:rounded-xl p-5 md:p-6 group hover:shadow-xl hover:shadow-[var(--luxury-gold)]/10 transition-all duration-300 animate-fade-in border border-gray-100/50 hover:border-[var(--luxury-gold)]/25 hover:-translate-y-1" style={{ animationDelay: `${index * 0.1}s` }}>
|
||||||
<div className="absolute top-0 left-0 w-full h-0.5 bg-gradient-to-r from-[#d4af37] to-[#f5d76e] opacity-0 group-hover:opacity-100 transition-opacity duration-300 rounded-t-lg md:rounded-t-xl"></div>
|
<div className="absolute top-0 left-0 w-full h-0.5 bg-gradient-to-r from-[var(--luxury-gold)] to-[var(--luxury-gold-light)] opacity-0 group-hover:opacity-100 transition-opacity duration-300 rounded-t-lg md:rounded-t-xl"></div>
|
||||||
<div className="w-14 h-14 md:w-16 md:h-16 bg-gradient-to-br from-[#d4af37]/15 via-[#f5d76e]/10 to-[#d4af37]/15 rounded-lg flex items-center justify-center mb-4 md:mb-5 mx-auto group-hover:scale-110 group-hover:shadow-lg group-hover:shadow-[#d4af37]/20 transition-all duration-300 border border-[#d4af37]/25 group-hover:border-[#d4af37]/40">
|
<div className="w-14 h-14 md:w-16 md:h-16 bg-gradient-to-br from-[var(--luxury-gold)]/15 via-[var(--luxury-gold-light)]/10 to-[var(--luxury-gold)]/15 rounded-lg flex items-center justify-center mb-4 md:mb-5 mx-auto group-hover:scale-110 group-hover:shadow-lg group-hover:shadow-[var(--luxury-gold)]/20 transition-all duration-300 border border-[var(--luxury-gold)]/25 group-hover:border-[var(--luxury-gold)]/40">
|
||||||
{feature.icon && (LucideIcons as any)[feature.icon] ? (
|
{feature.icon && (LucideIcons as any)[feature.icon] ? (
|
||||||
React.createElement((LucideIcons as any)[feature.icon], {
|
React.createElement((LucideIcons as any)[feature.icon], {
|
||||||
className: 'w-7 h-7 md:w-8 md:h-8 text-[#d4af37] drop-shadow-md'
|
className: 'w-7 h-7 md:w-8 md:h-8 text-[var(--luxury-gold)] drop-shadow-md'
|
||||||
})
|
})
|
||||||
) : (
|
) : (
|
||||||
<span className="text-2xl md:text-3xl">✨</span>
|
<span className="text-2xl md:text-3xl">✨</span>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
<h3 className="text-lg md:text-xl font-serif font-semibold mb-2 md:mb-3 text-gray-900 group-hover:text-[#d4af37] transition-colors duration-300 text-center tracking-tight leading-tight">
|
<h3 className="text-lg md:text-xl font-serif font-semibold mb-2 md:mb-3 text-gray-900 group-hover:text-[var(--luxury-gold)] transition-colors duration-300 text-center tracking-tight leading-tight">
|
||||||
{feature.title}
|
{feature.title}
|
||||||
</h3>
|
</h3>
|
||||||
<p className="text-sm md:text-base text-gray-600 leading-relaxed font-light text-center tracking-wide">
|
<p className="text-sm md:text-base text-gray-600 leading-relaxed font-light text-center tracking-wide">
|
||||||
@@ -943,7 +943,7 @@ const HomePage: React.FC = () => {
|
|||||||
<section className="container mx-auto px-4 sm:px-6 lg:px-8 py-12 md:py-16">
|
<section className="container mx-auto px-4 sm:px-6 lg:px-8 py-12 md:py-16">
|
||||||
<div className="text-center mb-8 md:mb-10 animate-fade-in">
|
<div className="text-center mb-8 md:mb-10 animate-fade-in">
|
||||||
<div className="inline-block mb-3">
|
<div className="inline-block mb-3">
|
||||||
<div className="h-0.5 w-16 md:w-20 bg-gradient-to-r from-[#d4af37] to-[#f5d76e] mx-auto rounded-full"></div>
|
<div className="h-0.5 w-16 md:w-20 bg-gradient-to-r from-[var(--luxury-gold)] to-[var(--luxury-gold-light)] mx-auto rounded-full"></div>
|
||||||
</div>
|
</div>
|
||||||
<h2 className="text-2xl sm:text-3xl md:text-4xl font-serif font-bold text-gray-900 tracking-tight mb-3 md:mb-4 px-4">
|
<h2 className="text-2xl sm:text-3xl md:text-4xl font-serif font-bold text-gray-900 tracking-tight mb-3 md:mb-4 px-4">
|
||||||
{pageContent.luxury_gallery_section_title || 'Luxury Gallery'}
|
{pageContent.luxury_gallery_section_title || 'Luxury Gallery'}
|
||||||
@@ -968,7 +968,7 @@ const HomePage: React.FC = () => {
|
|||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
key={`luxury-gallery-${index}-${image}`}
|
key={`luxury-gallery-${index}-${image}`}
|
||||||
className="relative group overflow-hidden rounded-lg md:rounded-xl aspect-square animate-fade-in shadow-md shadow-gray-900/5 hover:shadow-xl hover:shadow-[#d4af37]/15 transition-all duration-300 cursor-pointer"
|
className="relative group overflow-hidden rounded-lg md:rounded-xl aspect-square animate-fade-in shadow-md shadow-gray-900/5 hover:shadow-xl hover:shadow-[var(--luxury-gold)]/15 transition-all duration-300 cursor-pointer"
|
||||||
style={{ animationDelay: `${index * 0.05}s` }}
|
style={{ animationDelay: `${index * 0.05}s` }}
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
const normalizedImages = (pageContent.luxury_gallery || [])
|
const normalizedImages = (pageContent.luxury_gallery || [])
|
||||||
@@ -998,7 +998,7 @@ const HomePage: React.FC = () => {
|
|||||||
<ZoomIn className="w-8 h-8 md:w-10 md:h-10 text-white drop-shadow-2xl" />
|
<ZoomIn className="w-8 h-8 md:w-10 md:h-10 text-white drop-shadow-2xl" />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="absolute inset-0 border-2 border-transparent group-hover:border-[#d4af37]/60 transition-colors duration-300 rounded-lg md:rounded-xl"></div>
|
<div className="absolute inset-0 border-2 border-transparent group-hover:border-[var(--luxury-gold)]/60 transition-colors duration-300 rounded-lg md:rounded-xl"></div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
})}
|
})}
|
||||||
@@ -1011,7 +1011,7 @@ const HomePage: React.FC = () => {
|
|||||||
<section className="container mx-auto px-4 sm:px-6 lg:px-8 py-12 md:py-16 bg-gradient-to-b from-white via-gray-50/20 to-white">
|
<section className="container mx-auto px-4 sm:px-6 lg:px-8 py-12 md:py-16 bg-gradient-to-b from-white via-gray-50/20 to-white">
|
||||||
<div className="text-center mb-8 md:mb-10 animate-fade-in">
|
<div className="text-center mb-8 md:mb-10 animate-fade-in">
|
||||||
<div className="inline-block mb-3">
|
<div className="inline-block mb-3">
|
||||||
<div className="h-0.5 w-16 md:w-20 bg-gradient-to-r from-[#d4af37] to-[#f5d76e] mx-auto rounded-full"></div>
|
<div className="h-0.5 w-16 md:w-20 bg-gradient-to-r from-[var(--luxury-gold)] to-[var(--luxury-gold-light)] mx-auto rounded-full"></div>
|
||||||
</div>
|
</div>
|
||||||
<h2 className="text-2xl sm:text-3xl md:text-4xl font-serif font-bold text-gray-900 tracking-tight mb-3 md:mb-4 px-4">
|
<h2 className="text-2xl sm:text-3xl md:text-4xl font-serif font-bold text-gray-900 tracking-tight mb-3 md:mb-4 px-4">
|
||||||
{pageContent.luxury_testimonials_section_title || 'Guest Experiences'}
|
{pageContent.luxury_testimonials_section_title || 'Guest Experiences'}
|
||||||
@@ -1024,16 +1024,16 @@ const HomePage: React.FC = () => {
|
|||||||
</div>
|
</div>
|
||||||
<div className="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 gap-5 md:gap-6 px-4">
|
<div className="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 gap-5 md:gap-6 px-4">
|
||||||
{pageContent.luxury_testimonials.map((testimonial, index) => (
|
{pageContent.luxury_testimonials.map((testimonial, index) => (
|
||||||
<div key={index} className="relative bg-white rounded-lg md:rounded-xl p-5 md:p-6 shadow-lg shadow-gray-900/5 hover:shadow-xl hover:shadow-[#d4af37]/8 transition-all duration-300 animate-fade-in border border-gray-100/50 hover:border-[#d4af37]/25 hover:-translate-y-1" style={{ animationDelay: `${index * 0.1}s` }}>
|
<div key={index} className="relative bg-white rounded-lg md:rounded-xl p-5 md:p-6 shadow-lg shadow-gray-900/5 hover:shadow-xl hover:shadow-[var(--luxury-gold)]/8 transition-all duration-300 animate-fade-in border border-gray-100/50 hover:border-[var(--luxury-gold)]/25 hover:-translate-y-1" style={{ animationDelay: `${index * 0.1}s` }}>
|
||||||
<div className="absolute top-0 left-0 w-full h-0.5 bg-gradient-to-r from-[#d4af37] to-[#f5d76e] opacity-0 hover:opacity-100 transition-opacity duration-300 rounded-t-lg md:rounded-t-xl"></div>
|
<div className="absolute top-0 left-0 w-full h-0.5 bg-gradient-to-r from-[var(--luxury-gold)] to-[var(--luxury-gold-light)] opacity-0 hover:opacity-100 transition-opacity duration-300 rounded-t-lg md:rounded-t-xl"></div>
|
||||||
<div className="flex items-center mb-4 md:mb-5">
|
<div className="flex items-center mb-4 md:mb-5">
|
||||||
{testimonial.image ? (
|
{testimonial.image ? (
|
||||||
<div className="relative">
|
<div className="relative">
|
||||||
<img src={testimonial.image} alt={testimonial.name} className="w-12 h-12 md:w-14 md:h-14 rounded-full object-cover mr-3 md:mr-4 border-2 border-[#d4af37]/20 shadow-md" />
|
<img src={testimonial.image} alt={testimonial.name} className="w-12 h-12 md:w-14 md:h-14 rounded-full object-cover mr-3 md:mr-4 border-2 border-[var(--luxury-gold)]/20 shadow-md" />
|
||||||
<div className="absolute inset-0 rounded-full border border-[#d4af37]/40"></div>
|
<div className="absolute inset-0 rounded-full border border-[var(--luxury-gold)]/40"></div>
|
||||||
</div>
|
</div>
|
||||||
) : (
|
) : (
|
||||||
<div className="w-12 h-12 md:w-14 md:h-14 rounded-full bg-gradient-to-br from-[#d4af37] to-[#f5d76e] flex items-center justify-center text-white font-bold text-base md:text-lg mr-3 md:mr-4 shadow-md border-2 border-white">
|
<div className="w-12 h-12 md:w-14 md:h-14 rounded-full bg-gradient-to-br from-[var(--luxury-gold)] to-[var(--luxury-gold-light)] flex items-center justify-center text-white font-bold text-base md:text-lg mr-3 md:mr-4 shadow-md border-2 border-white">
|
||||||
{testimonial.name.charAt(0).toUpperCase()}
|
{testimonial.name.charAt(0).toUpperCase()}
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
@@ -1045,7 +1045,7 @@ const HomePage: React.FC = () => {
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="relative">
|
<div className="relative">
|
||||||
<div className="absolute -top-1 -left-1 text-4xl md:text-5xl text-[#d4af37]/8 font-serif">"</div>
|
<div className="absolute -top-1 -left-1 text-4xl md:text-5xl text-[var(--luxury-gold)]/8 font-serif">"</div>
|
||||||
<p className="text-sm md:text-base text-gray-700 leading-relaxed italic relative z-10 font-light">{testimonial.quote}</p>
|
<p className="text-sm md:text-base text-gray-700 leading-relaxed italic relative z-10 font-light">{testimonial.quote}</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -1057,15 +1057,15 @@ const HomePage: React.FC = () => {
|
|||||||
{}
|
{}
|
||||||
{(pageContent?.sections_enabled?.stats !== false) && pageContent?.stats && Array.isArray(pageContent.stats) && pageContent.stats.length > 0 && (
|
{(pageContent?.sections_enabled?.stats !== false) && pageContent?.stats && Array.isArray(pageContent.stats) && pageContent.stats.length > 0 && (
|
||||||
<section className="container mx-auto px-4 sm:px-6 lg:px-8 py-12 md:py-16">
|
<section className="container mx-auto px-4 sm:px-6 lg:px-8 py-12 md:py-16">
|
||||||
<div className="relative bg-gradient-to-br from-slate-900 via-slate-800 to-slate-900 rounded-xl md:rounded-2xl p-6 md:p-8 lg:p-10 shadow-xl shadow-black/30 animate-fade-in overflow-hidden border border-[#d4af37]/15">
|
<div className="relative bg-gradient-to-br from-slate-900 via-slate-800 to-slate-900 rounded-xl md:rounded-2xl p-6 md:p-8 lg:p-10 shadow-xl shadow-black/30 animate-fade-in overflow-hidden border border-[var(--luxury-gold)]/15">
|
||||||
{}
|
{}
|
||||||
<div className="absolute top-0 left-0 right-0 h-0.5 bg-gradient-to-r from-[#d4af37] via-[#f5d76e] to-[#d4af37]"></div>
|
<div className="absolute top-0 left-0 right-0 h-0.5 bg-gradient-to-r from-[var(--luxury-gold)] via-[var(--luxury-gold-light)] to-[var(--luxury-gold)]"></div>
|
||||||
<div className="absolute inset-0 opacity-8 bg-[radial-gradient(circle_at_1px_1px,#d4af37_1px,transparent_0)] bg-[length:40px_40px]"></div>
|
<div className="absolute inset-0 opacity-8 bg-[radial-gradient(circle_at_1px_1px,var(--luxury-gold)_1px,transparent_0)] bg-[length:40px_40px]"></div>
|
||||||
|
|
||||||
{(pageContent?.stats_section_title || pageContent?.stats_section_subtitle) && (
|
{(pageContent?.stats_section_title || pageContent?.stats_section_subtitle) && (
|
||||||
<div className="text-center mb-8 md:mb-10 animate-fade-in relative z-10">
|
<div className="text-center mb-8 md:mb-10 animate-fade-in relative z-10">
|
||||||
<div className="inline-block mb-3">
|
<div className="inline-block mb-3">
|
||||||
<div className="h-0.5 w-16 md:w-20 bg-gradient-to-r from-[#d4af37] to-[#f5d76e] mx-auto rounded-full"></div>
|
<div className="h-0.5 w-16 md:w-20 bg-gradient-to-r from-[var(--luxury-gold)] to-[var(--luxury-gold-light)] mx-auto rounded-full"></div>
|
||||||
</div>
|
</div>
|
||||||
{pageContent.stats_section_title && (
|
{pageContent.stats_section_title && (
|
||||||
<h2 className="text-2xl sm:text-3xl md:text-4xl font-serif font-bold text-white tracking-tight mb-3 md:mb-4 px-4">
|
<h2 className="text-2xl sm:text-3xl md:text-4xl font-serif font-bold text-white tracking-tight mb-3 md:mb-4 px-4">
|
||||||
@@ -1087,7 +1087,7 @@ const HomePage: React.FC = () => {
|
|||||||
<div className="mb-3 md:mb-4 group-hover:scale-110 transition-transform duration-300 flex items-center justify-center">
|
<div className="mb-3 md:mb-4 group-hover:scale-110 transition-transform duration-300 flex items-center justify-center">
|
||||||
{stat.icon && (LucideIcons as any)[stat.icon] ? (
|
{stat.icon && (LucideIcons as any)[stat.icon] ? (
|
||||||
React.createElement((LucideIcons as any)[stat.icon], {
|
React.createElement((LucideIcons as any)[stat.icon], {
|
||||||
className: 'w-8 h-8 md:w-10 md:h-10 text-[#d4af37] drop-shadow-md'
|
className: 'w-8 h-8 md:w-10 md:h-10 text-[var(--luxury-gold)] drop-shadow-md'
|
||||||
})
|
})
|
||||||
) : (
|
) : (
|
||||||
<span className="text-3xl md:text-4xl">{stat.icon}</span>
|
<span className="text-3xl md:text-4xl">{stat.icon}</span>
|
||||||
@@ -1095,7 +1095,7 @@ const HomePage: React.FC = () => {
|
|||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
{stat?.number && (
|
{stat?.number && (
|
||||||
<div className="text-2xl sm:text-3xl md:text-4xl lg:text-5xl font-bold text-[#d4af37] mb-1 md:mb-2 font-serif tracking-tight">
|
<div className="text-2xl sm:text-3xl md:text-4xl lg:text-5xl font-bold text-[var(--luxury-gold)] mb-1 md:mb-2 font-serif tracking-tight">
|
||||||
{stat.number}
|
{stat.number}
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
@@ -1116,7 +1116,7 @@ const HomePage: React.FC = () => {
|
|||||||
<section className="container mx-auto px-4 sm:px-6 lg:px-8 py-12 md:py-16">
|
<section className="container mx-auto px-4 sm:px-6 lg:px-8 py-12 md:py-16">
|
||||||
<div className="text-center mb-8 md:mb-10 animate-fade-in">
|
<div className="text-center mb-8 md:mb-10 animate-fade-in">
|
||||||
<div className="inline-block mb-3">
|
<div className="inline-block mb-3">
|
||||||
<div className="h-0.5 w-16 md:w-20 bg-gradient-to-r from-[#d4af37] to-[#f5d76e] mx-auto rounded-full"></div>
|
<div className="h-0.5 w-16 md:w-20 bg-gradient-to-r from-[var(--luxury-gold)] to-[var(--luxury-gold-light)] mx-auto rounded-full"></div>
|
||||||
</div>
|
</div>
|
||||||
<h2 className="text-2xl sm:text-3xl md:text-4xl font-serif font-bold text-gray-900 tracking-tight mb-3 md:mb-4 px-4">
|
<h2 className="text-2xl sm:text-3xl md:text-4xl font-serif font-bold text-gray-900 tracking-tight mb-3 md:mb-4 px-4">
|
||||||
{pageContent.amenities_section_title || 'Luxury Amenities'}
|
{pageContent.amenities_section_title || 'Luxury Amenities'}
|
||||||
@@ -1127,24 +1127,24 @@ const HomePage: React.FC = () => {
|
|||||||
</div>
|
</div>
|
||||||
<div className="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 gap-5 md:gap-6 px-4">
|
<div className="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 gap-5 md:gap-6 px-4">
|
||||||
{pageContent.amenities.map((amenity, index) => (
|
{pageContent.amenities.map((amenity, index) => (
|
||||||
<div key={index} className="relative bg-white rounded-lg md:rounded-xl p-5 md:p-6 group hover:shadow-xl hover:shadow-[#d4af37]/10 transition-all duration-300 animate-fade-in border border-gray-100/50 hover:border-[#d4af37]/25 hover:-translate-y-1" style={{ animationDelay: `${index * 0.1}s` }}>
|
<div key={index} className="relative bg-white rounded-lg md:rounded-xl p-5 md:p-6 group hover:shadow-xl hover:shadow-[var(--luxury-gold)]/10 transition-all duration-300 animate-fade-in border border-gray-100/50 hover:border-[var(--luxury-gold)]/25 hover:-translate-y-1" style={{ animationDelay: `${index * 0.1}s` }}>
|
||||||
<div className="absolute top-0 left-0 w-full h-0.5 bg-gradient-to-r from-[#d4af37] to-[#f5d76e] opacity-0 group-hover:opacity-100 transition-opacity duration-300 rounded-t-lg md:rounded-t-xl"></div>
|
<div className="absolute top-0 left-0 w-full h-0.5 bg-gradient-to-r from-[var(--luxury-gold)] to-[var(--luxury-gold-light)] opacity-0 group-hover:opacity-100 transition-opacity duration-300 rounded-t-lg md:rounded-t-xl"></div>
|
||||||
{amenity.image ? (
|
{amenity.image ? (
|
||||||
<div className="w-full h-40 md:h-48 mb-4 md:mb-5 rounded-lg md:rounded-xl overflow-hidden shadow-lg group-hover:scale-105 transition-transform duration-300 border border-gray-100 group-hover:border-[#d4af37]/25">
|
<div className="w-full h-40 md:h-48 mb-4 md:mb-5 rounded-lg md:rounded-xl overflow-hidden shadow-lg group-hover:scale-105 transition-transform duration-300 border border-gray-100 group-hover:border-[var(--luxury-gold)]/25">
|
||||||
<img src={amenity.image} alt={amenity.title} className="w-full h-full object-cover" />
|
<img src={amenity.image} alt={amenity.title} className="w-full h-full object-cover" />
|
||||||
</div>
|
</div>
|
||||||
) : (
|
) : (
|
||||||
<div className="w-14 h-14 md:w-16 md:h-16 bg-gradient-to-br from-[#d4af37]/15 via-[#f5d76e]/10 to-[#d4af37]/15 rounded-lg flex items-center justify-center mb-4 md:mb-5 mx-auto group-hover:scale-110 group-hover:shadow-lg group-hover:shadow-[#d4af37]/20 transition-all duration-300 border border-[#d4af37]/25 group-hover:border-[#d4af37]/40">
|
<div className="w-14 h-14 md:w-16 md:h-16 bg-gradient-to-br from-[var(--luxury-gold)]/15 via-[var(--luxury-gold-light)]/10 to-[var(--luxury-gold)]/15 rounded-lg flex items-center justify-center mb-4 md:mb-5 mx-auto group-hover:scale-110 group-hover:shadow-lg group-hover:shadow-[var(--luxury-gold)]/20 transition-all duration-300 border border-[var(--luxury-gold)]/25 group-hover:border-[var(--luxury-gold)]/40">
|
||||||
{amenity.icon && (LucideIcons as any)[amenity.icon] ? (
|
{amenity.icon && (LucideIcons as any)[amenity.icon] ? (
|
||||||
React.createElement((LucideIcons as any)[amenity.icon], {
|
React.createElement((LucideIcons as any)[amenity.icon], {
|
||||||
className: 'w-7 h-7 md:w-8 md:h-8 text-[#d4af37] drop-shadow-md'
|
className: 'w-7 h-7 md:w-8 md:h-8 text-[var(--luxury-gold)] drop-shadow-md'
|
||||||
})
|
})
|
||||||
) : (
|
) : (
|
||||||
<span className="text-2xl md:text-3xl">✨</span>
|
<span className="text-2xl md:text-3xl">✨</span>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
<h3 className="text-lg md:text-xl font-serif font-semibold mb-2 md:mb-3 text-gray-900 group-hover:text-[#d4af37] transition-colors duration-300 tracking-tight">
|
<h3 className="text-lg md:text-xl font-serif font-semibold mb-2 md:mb-3 text-gray-900 group-hover:text-[var(--luxury-gold)] transition-colors duration-300 tracking-tight">
|
||||||
{amenity.title}
|
{amenity.title}
|
||||||
</h3>
|
</h3>
|
||||||
<p className="text-sm md:text-base text-gray-600 leading-relaxed font-light tracking-wide">
|
<p className="text-sm md:text-base text-gray-600 leading-relaxed font-light tracking-wide">
|
||||||
@@ -1161,10 +1161,10 @@ const HomePage: React.FC = () => {
|
|||||||
{}
|
{}
|
||||||
{(pageContent?.sections_enabled?.about_preview !== false) && (pageContent?.about_preview_title || pageContent?.about_preview_content) && (
|
{(pageContent?.sections_enabled?.about_preview !== false) && (pageContent?.about_preview_title || pageContent?.about_preview_content) && (
|
||||||
<section className="container mx-auto px-4 sm:px-6 lg:px-8 py-12 md:py-16">
|
<section className="container mx-auto px-4 sm:px-6 lg:px-8 py-12 md:py-16">
|
||||||
<div className="relative bg-white rounded-xl md:rounded-2xl shadow-xl shadow-[#d4af37]/5 overflow-hidden animate-fade-in border border-gray-100/50">
|
<div className="relative bg-white rounded-xl md:rounded-2xl shadow-xl shadow-[var(--luxury-gold)]/5 overflow-hidden animate-fade-in border border-gray-100/50">
|
||||||
{}
|
{}
|
||||||
<div className="absolute top-0 left-0 right-0 h-1 bg-gradient-to-r from-[#d4af37] via-[#f5d76e] to-[#d4af37]"></div>
|
<div className="absolute top-0 left-0 right-0 h-1 bg-gradient-to-r from-[var(--luxury-gold)] via-[var(--luxury-gold-light)] to-[var(--luxury-gold)]"></div>
|
||||||
<div className="absolute bottom-0 left-0 right-0 h-1 bg-gradient-to-r from-[#d4af37] via-[#f5d76e] to-[#d4af37]"></div>
|
<div className="absolute bottom-0 left-0 right-0 h-1 bg-gradient-to-r from-[var(--luxury-gold)] via-[var(--luxury-gold-light)] to-[var(--luxury-gold)]"></div>
|
||||||
|
|
||||||
<div className="grid grid-cols-1 lg:grid-cols-2 gap-0">
|
<div className="grid grid-cols-1 lg:grid-cols-2 gap-0">
|
||||||
{pageContent.about_preview_image && (
|
{pageContent.about_preview_image && (
|
||||||
@@ -1179,7 +1179,7 @@ const HomePage: React.FC = () => {
|
|||||||
)}
|
)}
|
||||||
<div className="p-6 md:p-8 lg:p-10 xl:p-12 flex flex-col justify-center bg-gradient-to-br from-white to-gray-50/20">
|
<div className="p-6 md:p-8 lg:p-10 xl:p-12 flex flex-col justify-center bg-gradient-to-br from-white to-gray-50/20">
|
||||||
<div className="inline-block mb-3 md:mb-4">
|
<div className="inline-block mb-3 md:mb-4">
|
||||||
<div className="h-0.5 w-12 md:w-16 bg-gradient-to-r from-[#d4af37] to-[#f5d76e] rounded-full"></div>
|
<div className="h-0.5 w-12 md:w-16 bg-gradient-to-r from-[var(--luxury-gold)] to-[var(--luxury-gold-light)] rounded-full"></div>
|
||||||
</div>
|
</div>
|
||||||
<h2 className="text-2xl sm:text-3xl md:text-4xl font-serif font-bold text-gray-900 mb-3 md:mb-4 tracking-tight leading-tight">
|
<h2 className="text-2xl sm:text-3xl md:text-4xl font-serif font-bold text-gray-900 mb-3 md:mb-4 tracking-tight leading-tight">
|
||||||
{pageContent.about_preview_title || 'About Our Hotel'}
|
{pageContent.about_preview_title || 'About Our Hotel'}
|
||||||
@@ -1196,9 +1196,9 @@ const HomePage: React.FC = () => {
|
|||||||
)}
|
)}
|
||||||
<Link
|
<Link
|
||||||
to="/about"
|
to="/about"
|
||||||
className="group relative inline-flex items-center gap-2 px-6 py-2.5 md:px-8 md:py-3 bg-gradient-to-r from-[#d4af37] to-[#c9a227] text-[#0f0f0f] rounded-lg font-semibold tracking-wide text-sm md:text-base shadow-md shadow-[#d4af37]/20 hover:shadow-lg hover:shadow-[#d4af37]/30 hover:-translate-y-0.5 transition-all duration-300 overflow-hidden w-fit"
|
className="group relative inline-flex items-center gap-2 px-6 py-2.5 md:px-8 md:py-3 bg-gradient-to-r from-[var(--luxury-gold)] to-[var(--luxury-gold-dark)] text-[#0f0f0f] rounded-lg font-semibold tracking-wide text-sm md:text-base shadow-md shadow-[var(--luxury-gold)]/20 hover:shadow-lg hover:shadow-[var(--luxury-gold)]/30 hover:-translate-y-0.5 transition-all duration-300 overflow-hidden w-fit"
|
||||||
>
|
>
|
||||||
<span className="absolute inset-0 bg-gradient-to-r from-[#f5d76e] to-[#d4af37] opacity-0 group-hover:opacity-100 transition-opacity duration-300"></span>
|
<span className="absolute inset-0 bg-gradient-to-r from-[var(--luxury-gold-light)] to-[var(--luxury-gold)] opacity-0 group-hover:opacity-100 transition-opacity duration-300"></span>
|
||||||
<span className="relative z-10">Learn More</span>
|
<span className="relative z-10">Learn More</span>
|
||||||
<ArrowRight className="w-4 h-4 md:w-5 md:h-5 relative z-10 group-hover:translate-x-1 transition-transform duration-300" />
|
<ArrowRight className="w-4 h-4 md:w-5 md:h-5 relative z-10 group-hover:translate-x-1 transition-transform duration-300" />
|
||||||
</Link>
|
</Link>
|
||||||
@@ -1213,7 +1213,7 @@ const HomePage: React.FC = () => {
|
|||||||
<section className="container mx-auto px-4 sm:px-6 lg:px-8 py-12 md:py-16">
|
<section className="container mx-auto px-4 sm:px-6 lg:px-8 py-12 md:py-16">
|
||||||
<div className="text-center mb-8 md:mb-10 animate-fade-in">
|
<div className="text-center mb-8 md:mb-10 animate-fade-in">
|
||||||
<div className="inline-block mb-3">
|
<div className="inline-block mb-3">
|
||||||
<div className="h-0.5 w-16 md:w-20 bg-gradient-to-r from-[#d4af37] to-[#f5d76e] mx-auto rounded-full"></div>
|
<div className="h-0.5 w-16 md:w-20 bg-gradient-to-r from-[var(--luxury-gold)] to-[var(--luxury-gold-light)] mx-auto rounded-full"></div>
|
||||||
</div>
|
</div>
|
||||||
<h2 className="text-2xl sm:text-3xl md:text-4xl font-serif font-bold text-gray-900 tracking-tight mb-3 md:mb-4 px-4">
|
<h2 className="text-2xl sm:text-3xl md:text-4xl font-serif font-bold text-gray-900 tracking-tight mb-3 md:mb-4 px-4">
|
||||||
{pageContent?.luxury_services_section_title || 'Luxury Services'}
|
{pageContent?.luxury_services_section_title || 'Luxury Services'}
|
||||||
@@ -1231,27 +1231,27 @@ const HomePage: React.FC = () => {
|
|||||||
<Link
|
<Link
|
||||||
key={service.id}
|
key={service.id}
|
||||||
to={`/services/${serviceSlug}`}
|
to={`/services/${serviceSlug}`}
|
||||||
className="relative bg-white rounded-lg md:rounded-xl p-5 md:p-6 group hover:shadow-xl hover:shadow-[#d4af37]/10 transition-all duration-300 animate-fade-in border border-gray-100/50 hover:border-[#d4af37]/25 hover:-translate-y-1 block"
|
className="relative bg-white rounded-lg md:rounded-xl p-5 md:p-6 group hover:shadow-xl hover:shadow-[var(--luxury-gold)]/10 transition-all duration-300 animate-fade-in border border-gray-100/50 hover:border-[var(--luxury-gold)]/25 hover:-translate-y-1 block"
|
||||||
style={{ animationDelay: `${index * 0.1}s` }}
|
style={{ animationDelay: `${index * 0.1}s` }}
|
||||||
>
|
>
|
||||||
<div className="absolute top-0 left-0 w-full h-0.5 bg-gradient-to-r from-[#d4af37] to-[#f5d76e] opacity-0 group-hover:opacity-100 transition-opacity duration-300 rounded-t-lg md:rounded-t-xl"></div>
|
<div className="absolute top-0 left-0 w-full h-0.5 bg-gradient-to-r from-[var(--luxury-gold)] to-[var(--luxury-gold-light)] opacity-0 group-hover:opacity-100 transition-opacity duration-300 rounded-t-lg md:rounded-t-xl"></div>
|
||||||
{service.image ? (
|
{service.image ? (
|
||||||
<div className="w-full h-40 md:h-48 mb-4 md:mb-5 rounded-lg md:rounded-xl overflow-hidden shadow-lg group-hover:scale-105 transition-transform duration-300 border border-gray-100 group-hover:border-[#d4af37]/25">
|
<div className="w-full h-40 md:h-48 mb-4 md:mb-5 rounded-lg md:rounded-xl overflow-hidden shadow-lg group-hover:scale-105 transition-transform duration-300 border border-gray-100 group-hover:border-[var(--luxury-gold)]/25">
|
||||||
<img src={service.image} alt={service.name} className="w-full h-full object-cover" />
|
<img src={service.image} alt={service.name} className="w-full h-full object-cover" />
|
||||||
</div>
|
</div>
|
||||||
) : (
|
) : (
|
||||||
<div className="w-14 h-14 md:w-16 md:h-16 bg-gradient-to-br from-[#d4af37]/15 via-[#f5d76e]/10 to-[#d4af37]/15 rounded-lg flex items-center justify-center mb-4 md:mb-5 mx-auto group-hover:scale-110 group-hover:shadow-lg group-hover:shadow-[#d4af37]/20 transition-all duration-300 border border-[#d4af37]/25 group-hover:border-[#d4af37]/40">
|
<div className="w-14 h-14 md:w-16 md:h-16 bg-gradient-to-br from-[var(--luxury-gold)]/15 via-[var(--luxury-gold-light)]/10 to-[var(--luxury-gold)]/15 rounded-lg flex items-center justify-center mb-4 md:mb-5 mx-auto group-hover:scale-110 group-hover:shadow-lg group-hover:shadow-[var(--luxury-gold)]/20 transition-all duration-300 border border-[var(--luxury-gold)]/25 group-hover:border-[var(--luxury-gold)]/40">
|
||||||
<span className="text-2xl md:text-3xl">✨</span>
|
<span className="text-2xl md:text-3xl">✨</span>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
<h3 className="text-lg md:text-xl font-serif font-semibold mb-2 md:mb-3 text-gray-900 group-hover:text-[#d4af37] transition-colors duration-300 tracking-tight">
|
<h3 className="text-lg md:text-xl font-serif font-semibold mb-2 md:mb-3 text-gray-900 group-hover:text-[var(--luxury-gold)] transition-colors duration-300 tracking-tight">
|
||||||
{service.name}
|
{service.name}
|
||||||
</h3>
|
</h3>
|
||||||
<p className="text-sm md:text-base text-gray-600 leading-relaxed font-light tracking-wide">
|
<p className="text-sm md:text-base text-gray-600 leading-relaxed font-light tracking-wide">
|
||||||
{service.description || 'Premium service for your comfort'}
|
{service.description || 'Premium service for your comfort'}
|
||||||
</p>
|
</p>
|
||||||
{service.price && (
|
{service.price && (
|
||||||
<div className="mt-3 text-sm font-semibold text-[#d4af37]">
|
<div className="mt-3 text-sm font-semibold text-[var(--luxury-gold)]">
|
||||||
Starting from {formatCurrency(service.price)}
|
Starting from {formatCurrency(service.price)}
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
@@ -1262,7 +1262,7 @@ const HomePage: React.FC = () => {
|
|||||||
<div className="text-center mt-8 md:mt-10">
|
<div className="text-center mt-8 md:mt-10">
|
||||||
<Link
|
<Link
|
||||||
to={pageContent?.services_section_button_link || '/services'}
|
to={pageContent?.services_section_button_link || '/services'}
|
||||||
className="inline-flex items-center gap-2 px-6 py-3 bg-gradient-to-r from-[#d4af37] to-[#c9a227] text-[#0f0f0f] rounded-lg font-medium hover:from-[#f5d76e] hover:to-[#d4af37] transition-all duration-300 shadow-lg shadow-[#d4af37]/30 hover:shadow-xl hover:shadow-[#d4af37]/40 hover:-translate-y-0.5"
|
className="inline-flex items-center gap-2 px-6 py-3 bg-gradient-to-r from-[var(--luxury-gold)] to-[var(--luxury-gold-dark)] text-[#0f0f0f] rounded-lg font-medium hover:from-[var(--luxury-gold-light)] hover:to-[var(--luxury-gold)] transition-all duration-300 shadow-lg shadow-[var(--luxury-gold)]/30 hover:shadow-xl hover:shadow-[var(--luxury-gold)]/40 hover:-translate-y-0.5"
|
||||||
>
|
>
|
||||||
<span>{pageContent?.services_section_button_text || 'View All Services'}</span>
|
<span>{pageContent?.services_section_button_text || 'View All Services'}</span>
|
||||||
<ArrowRight className="w-5 h-5" />
|
<ArrowRight className="w-5 h-5" />
|
||||||
@@ -1276,7 +1276,7 @@ const HomePage: React.FC = () => {
|
|||||||
<section className="container mx-auto px-4 sm:px-6 lg:px-8 py-12 md:py-16 bg-gradient-to-b from-white via-gray-50/20 to-white">
|
<section className="container mx-auto px-4 sm:px-6 lg:px-8 py-12 md:py-16 bg-gradient-to-b from-white via-gray-50/20 to-white">
|
||||||
<div className="text-center mb-8 md:mb-10 animate-fade-in">
|
<div className="text-center mb-8 md:mb-10 animate-fade-in">
|
||||||
<div className="inline-block mb-3">
|
<div className="inline-block mb-3">
|
||||||
<div className="h-0.5 w-16 md:w-20 bg-gradient-to-r from-[#d4af37] to-[#f5d76e] mx-auto rounded-full"></div>
|
<div className="h-0.5 w-16 md:w-20 bg-gradient-to-r from-[var(--luxury-gold)] to-[var(--luxury-gold-light)] mx-auto rounded-full"></div>
|
||||||
</div>
|
</div>
|
||||||
<h2 className="text-2xl sm:text-3xl md:text-4xl font-serif font-bold text-gray-900 tracking-tight mb-3 md:mb-4 px-4">
|
<h2 className="text-2xl sm:text-3xl md:text-4xl font-serif font-bold text-gray-900 tracking-tight mb-3 md:mb-4 px-4">
|
||||||
{pageContent.luxury_experiences_section_title || 'Unique Experiences'}
|
{pageContent.luxury_experiences_section_title || 'Unique Experiences'}
|
||||||
@@ -1289,24 +1289,24 @@ const HomePage: React.FC = () => {
|
|||||||
</div>
|
</div>
|
||||||
<div className="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 gap-5 md:gap-6 px-4">
|
<div className="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 gap-5 md:gap-6 px-4">
|
||||||
{pageContent.luxury_experiences.map((experience: any, index: number) => (
|
{pageContent.luxury_experiences.map((experience: any, index: number) => (
|
||||||
<div key={index} className="relative bg-white rounded-lg md:rounded-xl p-5 md:p-6 group hover:shadow-xl hover:shadow-[#d4af37]/10 transition-all duration-300 animate-fade-in border border-gray-100/50 hover:border-[#d4af37]/25 hover:-translate-y-1" style={{ animationDelay: `${index * 0.1}s` }}>
|
<div key={index} className="relative bg-white rounded-lg md:rounded-xl p-5 md:p-6 group hover:shadow-xl hover:shadow-[var(--luxury-gold)]/10 transition-all duration-300 animate-fade-in border border-gray-100/50 hover:border-[var(--luxury-gold)]/25 hover:-translate-y-1" style={{ animationDelay: `${index * 0.1}s` }}>
|
||||||
<div className="absolute top-0 left-0 w-full h-0.5 bg-gradient-to-r from-[#d4af37] to-[#f5d76e] opacity-0 group-hover:opacity-100 transition-opacity duration-300 rounded-t-lg md:rounded-t-xl"></div>
|
<div className="absolute top-0 left-0 w-full h-0.5 bg-gradient-to-r from-[var(--luxury-gold)] to-[var(--luxury-gold-light)] opacity-0 group-hover:opacity-100 transition-opacity duration-300 rounded-t-lg md:rounded-t-xl"></div>
|
||||||
{experience.image ? (
|
{experience.image ? (
|
||||||
<div className="w-full h-40 md:h-48 mb-4 md:mb-5 rounded-lg md:rounded-xl overflow-hidden shadow-lg group-hover:scale-105 transition-transform duration-300 border border-gray-100 group-hover:border-[#d4af37]/25">
|
<div className="w-full h-40 md:h-48 mb-4 md:mb-5 rounded-lg md:rounded-xl overflow-hidden shadow-lg group-hover:scale-105 transition-transform duration-300 border border-gray-100 group-hover:border-[var(--luxury-gold)]/25">
|
||||||
<img src={experience.image} alt={experience.title} className="w-full h-full object-cover" />
|
<img src={experience.image} alt={experience.title} className="w-full h-full object-cover" />
|
||||||
</div>
|
</div>
|
||||||
) : (
|
) : (
|
||||||
<div className="w-14 h-14 md:w-16 md:h-16 bg-gradient-to-br from-[#d4af37]/15 via-[#f5d76e]/10 to-[#d4af37]/15 rounded-lg flex items-center justify-center mb-4 md:mb-5 mx-auto group-hover:scale-110 group-hover:shadow-lg group-hover:shadow-[#d4af37]/20 transition-all duration-300 border border-[#d4af37]/25 group-hover:border-[#d4af37]/40">
|
<div className="w-14 h-14 md:w-16 md:h-16 bg-gradient-to-br from-[var(--luxury-gold)]/15 via-[var(--luxury-gold-light)]/10 to-[var(--luxury-gold)]/15 rounded-lg flex items-center justify-center mb-4 md:mb-5 mx-auto group-hover:scale-110 group-hover:shadow-lg group-hover:shadow-[var(--luxury-gold)]/20 transition-all duration-300 border border-[var(--luxury-gold)]/25 group-hover:border-[var(--luxury-gold)]/40">
|
||||||
{experience.icon && (LucideIcons as any)[experience.icon] ? (
|
{experience.icon && (LucideIcons as any)[experience.icon] ? (
|
||||||
React.createElement((LucideIcons as any)[experience.icon], {
|
React.createElement((LucideIcons as any)[experience.icon], {
|
||||||
className: 'w-7 h-7 md:w-8 md:h-8 text-[#d4af37] drop-shadow-md'
|
className: 'w-7 h-7 md:w-8 md:h-8 text-[var(--luxury-gold)] drop-shadow-md'
|
||||||
})
|
})
|
||||||
) : (
|
) : (
|
||||||
<span className="text-2xl md:text-3xl">✨</span>
|
<span className="text-2xl md:text-3xl">✨</span>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
<h3 className="text-lg md:text-xl font-serif font-semibold mb-2 md:mb-3 text-gray-900 group-hover:text-[#d4af37] transition-colors duration-300 tracking-tight">
|
<h3 className="text-lg md:text-xl font-serif font-semibold mb-2 md:mb-3 text-gray-900 group-hover:text-[var(--luxury-gold)] transition-colors duration-300 tracking-tight">
|
||||||
{experience.title}
|
{experience.title}
|
||||||
</h3>
|
</h3>
|
||||||
<p className="text-sm md:text-base text-gray-600 leading-relaxed font-light tracking-wide">
|
<p className="text-sm md:text-base text-gray-600 leading-relaxed font-light tracking-wide">
|
||||||
@@ -1323,7 +1323,7 @@ const HomePage: React.FC = () => {
|
|||||||
<section className="container mx-auto px-4 sm:px-6 lg:px-8 py-12 md:py-16">
|
<section className="container mx-auto px-4 sm:px-6 lg:px-8 py-12 md:py-16">
|
||||||
<div className="text-center mb-8 md:mb-10 animate-fade-in">
|
<div className="text-center mb-8 md:mb-10 animate-fade-in">
|
||||||
<div className="inline-block mb-3">
|
<div className="inline-block mb-3">
|
||||||
<div className="h-0.5 w-16 md:w-20 bg-gradient-to-r from-[#d4af37] to-[#f5d76e] mx-auto rounded-full"></div>
|
<div className="h-0.5 w-16 md:w-20 bg-gradient-to-r from-[var(--luxury-gold)] to-[var(--luxury-gold-light)] mx-auto rounded-full"></div>
|
||||||
</div>
|
</div>
|
||||||
<h2 className="text-2xl sm:text-3xl md:text-4xl font-serif font-bold text-gray-900 tracking-tight mb-3 md:mb-4 px-4">
|
<h2 className="text-2xl sm:text-3xl md:text-4xl font-serif font-bold text-gray-900 tracking-tight mb-3 md:mb-4 px-4">
|
||||||
{pageContent.awards_section_title || 'Awards & Recognition'}
|
{pageContent.awards_section_title || 'Awards & Recognition'}
|
||||||
@@ -1336,17 +1336,17 @@ const HomePage: React.FC = () => {
|
|||||||
</div>
|
</div>
|
||||||
<div className="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-4 gap-5 md:gap-6 px-4">
|
<div className="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-4 gap-5 md:gap-6 px-4">
|
||||||
{pageContent.awards.map((award: any, index: number) => (
|
{pageContent.awards.map((award: any, index: number) => (
|
||||||
<div key={index} className="relative bg-white rounded-lg md:rounded-xl p-5 md:p-6 group hover:shadow-xl hover:shadow-[#d4af37]/10 transition-all duration-300 animate-fade-in border border-gray-100/50 hover:border-[#d4af37]/25 hover:-translate-y-1 text-center" style={{ animationDelay: `${index * 0.1}s` }}>
|
<div key={index} className="relative bg-white rounded-lg md:rounded-xl p-5 md:p-6 group hover:shadow-xl hover:shadow-[var(--luxury-gold)]/10 transition-all duration-300 animate-fade-in border border-gray-100/50 hover:border-[var(--luxury-gold)]/25 hover:-translate-y-1 text-center" style={{ animationDelay: `${index * 0.1}s` }}>
|
||||||
<div className="absolute top-0 left-0 w-full h-0.5 bg-gradient-to-r from-[#d4af37] to-[#f5d76e] opacity-0 group-hover:opacity-100 transition-opacity duration-300 rounded-t-lg md:rounded-t-xl"></div>
|
<div className="absolute top-0 left-0 w-full h-0.5 bg-gradient-to-r from-[var(--luxury-gold)] to-[var(--luxury-gold-light)] opacity-0 group-hover:opacity-100 transition-opacity duration-300 rounded-t-lg md:rounded-t-xl"></div>
|
||||||
{award.image ? (
|
{award.image ? (
|
||||||
<div className="w-20 h-20 md:w-24 md:h-24 mx-auto mb-4 md:mb-5 rounded-lg overflow-hidden shadow-lg">
|
<div className="w-20 h-20 md:w-24 md:h-24 mx-auto mb-4 md:mb-5 rounded-lg overflow-hidden shadow-lg">
|
||||||
<img src={award.image} alt={award.title} className="w-full h-full object-cover" />
|
<img src={award.image} alt={award.title} className="w-full h-full object-cover" />
|
||||||
</div>
|
</div>
|
||||||
) : (
|
) : (
|
||||||
<div className="w-16 h-16 md:w-20 md:h-20 bg-gradient-to-br from-[#d4af37]/15 via-[#f5d76e]/10 to-[#d4af37]/15 rounded-lg flex items-center justify-center mx-auto mb-4 md:mb-5 group-hover:scale-110 group-hover:shadow-lg group-hover:shadow-[#d4af37]/20 transition-all duration-300 border border-[#d4af37]/25 group-hover:border-[#d4af37]/40">
|
<div className="w-16 h-16 md:w-20 md:h-20 bg-gradient-to-br from-[var(--luxury-gold)]/15 via-[var(--luxury-gold-light)]/10 to-[var(--luxury-gold)]/15 rounded-lg flex items-center justify-center mx-auto mb-4 md:mb-5 group-hover:scale-110 group-hover:shadow-lg group-hover:shadow-[var(--luxury-gold)]/20 transition-all duration-300 border border-[var(--luxury-gold)]/25 group-hover:border-[var(--luxury-gold)]/40">
|
||||||
{award.icon && (LucideIcons as any)[award.icon] ? (
|
{award.icon && (LucideIcons as any)[award.icon] ? (
|
||||||
React.createElement((LucideIcons as any)[award.icon], {
|
React.createElement((LucideIcons as any)[award.icon], {
|
||||||
className: 'w-8 h-8 md:w-10 md:h-10 text-[#d4af37] drop-shadow-md'
|
className: 'w-8 h-8 md:w-10 md:h-10 text-[var(--luxury-gold)] drop-shadow-md'
|
||||||
})
|
})
|
||||||
) : (
|
) : (
|
||||||
<span className="text-3xl">🏆</span>
|
<span className="text-3xl">🏆</span>
|
||||||
@@ -1354,9 +1354,9 @@ const HomePage: React.FC = () => {
|
|||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
{award.year && (
|
{award.year && (
|
||||||
<div className="text-xs md:text-sm text-[#d4af37] font-semibold mb-2">{award.year}</div>
|
<div className="text-xs md:text-sm text-[var(--luxury-gold)] font-semibold mb-2">{award.year}</div>
|
||||||
)}
|
)}
|
||||||
<h3 className="text-base md:text-lg font-serif font-semibold mb-2 text-gray-900 group-hover:text-[#d4af37] transition-colors duration-300 tracking-tight">
|
<h3 className="text-base md:text-lg font-serif font-semibold mb-2 text-gray-900 group-hover:text-[var(--luxury-gold)] transition-colors duration-300 tracking-tight">
|
||||||
{award.title}
|
{award.title}
|
||||||
</h3>
|
</h3>
|
||||||
{award.description && (
|
{award.description && (
|
||||||
@@ -1373,7 +1373,7 @@ const HomePage: React.FC = () => {
|
|||||||
{}
|
{}
|
||||||
{(pageContent?.cta_title || pageContent?.cta_subtitle) && (
|
{(pageContent?.cta_title || pageContent?.cta_subtitle) && (
|
||||||
<section className="container mx-auto px-4 sm:px-6 lg:px-8 py-12 md:py-16">
|
<section className="container mx-auto px-4 sm:px-6 lg:px-8 py-12 md:py-16">
|
||||||
<div className="relative bg-gradient-to-br from-slate-900 via-slate-800 to-slate-900 rounded-xl md:rounded-2xl p-8 md:p-12 lg:p-16 shadow-xl shadow-black/30 animate-fade-in overflow-hidden border border-[#d4af37]/15">
|
<div className="relative bg-gradient-to-br from-slate-900 via-slate-800 to-slate-900 rounded-xl md:rounded-2xl p-8 md:p-12 lg:p-16 shadow-xl shadow-black/30 animate-fade-in overflow-hidden border border-[var(--luxury-gold)]/15">
|
||||||
{pageContent.cta_image && (
|
{pageContent.cta_image && (
|
||||||
<div className="absolute inset-0 opacity-20">
|
<div className="absolute inset-0 opacity-20">
|
||||||
<img src={pageContent.cta_image} alt="CTA Background" className="w-full h-full object-cover" />
|
<img src={pageContent.cta_image} alt="CTA Background" className="w-full h-full object-cover" />
|
||||||
@@ -1382,7 +1382,7 @@ const HomePage: React.FC = () => {
|
|||||||
<div className="absolute inset-0 bg-gradient-to-r from-black/60 via-black/40 to-black/60"></div>
|
<div className="absolute inset-0 bg-gradient-to-r from-black/60 via-black/40 to-black/60"></div>
|
||||||
<div className="relative z-10 text-center">
|
<div className="relative z-10 text-center">
|
||||||
<div className="inline-block mb-4">
|
<div className="inline-block mb-4">
|
||||||
<div className="h-0.5 w-16 md:w-20 bg-gradient-to-r from-[#d4af37] to-[#f5d76e] mx-auto rounded-full"></div>
|
<div className="h-0.5 w-16 md:w-20 bg-gradient-to-r from-[var(--luxury-gold)] to-[var(--luxury-gold-light)] mx-auto rounded-full"></div>
|
||||||
</div>
|
</div>
|
||||||
<h2 className="text-2xl sm:text-3xl md:text-4xl lg:text-5xl font-serif font-bold text-white mb-4 md:mb-6 tracking-tight">
|
<h2 className="text-2xl sm:text-3xl md:text-4xl lg:text-5xl font-serif font-bold text-white mb-4 md:mb-6 tracking-tight">
|
||||||
{pageContent.cta_title}
|
{pageContent.cta_title}
|
||||||
@@ -1395,9 +1395,9 @@ const HomePage: React.FC = () => {
|
|||||||
{pageContent.cta_button_text && pageContent.cta_button_link && (
|
{pageContent.cta_button_text && pageContent.cta_button_link && (
|
||||||
<Link
|
<Link
|
||||||
to={pageContent.cta_button_link}
|
to={pageContent.cta_button_link}
|
||||||
className="group relative inline-flex items-center gap-2 px-8 py-3 md:px-10 md:py-4 bg-gradient-to-r from-[#d4af37] to-[#c9a227] text-[#0f0f0f] rounded-lg font-semibold tracking-wide text-base md:text-lg shadow-lg shadow-[#d4af37]/30 hover:shadow-xl hover:shadow-[#d4af37]/40 hover:-translate-y-1 transition-all duration-300 overflow-hidden"
|
className="group relative inline-flex items-center gap-2 px-8 py-3 md:px-10 md:py-4 bg-gradient-to-r from-[var(--luxury-gold)] to-[var(--luxury-gold-dark)] text-[#0f0f0f] rounded-lg font-semibold tracking-wide text-base md:text-lg shadow-lg shadow-[var(--luxury-gold)]/30 hover:shadow-xl hover:shadow-[var(--luxury-gold)]/40 hover:-translate-y-1 transition-all duration-300 overflow-hidden"
|
||||||
>
|
>
|
||||||
<span className="absolute inset-0 bg-gradient-to-r from-[#f5d76e] to-[#d4af37] opacity-0 group-hover:opacity-100 transition-opacity duration-300"></span>
|
<span className="absolute inset-0 bg-gradient-to-r from-[var(--luxury-gold-light)] to-[var(--luxury-gold)] opacity-0 group-hover:opacity-100 transition-opacity duration-300"></span>
|
||||||
<span className="relative z-10">{pageContent.cta_button_text}</span>
|
<span className="relative z-10">{pageContent.cta_button_text}</span>
|
||||||
<ArrowRight className="w-5 h-5 md:w-6 md:h-6 relative z-10 group-hover:translate-x-1 transition-transform duration-300" />
|
<ArrowRight className="w-5 h-5 md:w-6 md:h-6 relative z-10 group-hover:translate-x-1 transition-transform duration-300" />
|
||||||
</Link>
|
</Link>
|
||||||
@@ -1413,7 +1413,7 @@ const HomePage: React.FC = () => {
|
|||||||
<div className="container mx-auto px-4 sm:px-6 lg:px-8">
|
<div className="container mx-auto px-4 sm:px-6 lg:px-8">
|
||||||
<div className="text-center mb-8 md:mb-10 animate-fade-in">
|
<div className="text-center mb-8 md:mb-10 animate-fade-in">
|
||||||
<div className="inline-block mb-3">
|
<div className="inline-block mb-3">
|
||||||
<div className="h-0.5 w-16 md:w-20 bg-gradient-to-r from-[#d4af37] to-[#f5d76e] mx-auto rounded-full"></div>
|
<div className="h-0.5 w-16 md:w-20 bg-gradient-to-r from-[var(--luxury-gold)] to-[var(--luxury-gold-light)] mx-auto rounded-full"></div>
|
||||||
</div>
|
</div>
|
||||||
<h2 className="text-2xl sm:text-3xl md:text-4xl font-serif font-bold text-gray-900 tracking-tight mb-3 md:mb-4 px-4">
|
<h2 className="text-2xl sm:text-3xl md:text-4xl font-serif font-bold text-gray-900 tracking-tight mb-3 md:mb-4 px-4">
|
||||||
{pageContent.partners_section_title || 'Our Partners'}
|
{pageContent.partners_section_title || 'Our Partners'}
|
||||||
@@ -1439,7 +1439,7 @@ const HomePage: React.FC = () => {
|
|||||||
<section className="container mx-auto px-4 sm:px-6 lg:px-8 py-12 md:py-16 bg-gradient-to-b from-white via-gray-50/20 to-white">
|
<section className="container mx-auto px-4 sm:px-6 lg:px-8 py-12 md:py-16 bg-gradient-to-b from-white via-gray-50/20 to-white">
|
||||||
<div className="text-center mb-8 md:mb-10 animate-fade-in">
|
<div className="text-center mb-8 md:mb-10 animate-fade-in">
|
||||||
<div className="inline-block mb-3">
|
<div className="inline-block mb-3">
|
||||||
<div className="h-0.5 w-16 md:w-20 bg-gradient-to-r from-[#d4af37] to-[#f5d76e] mx-auto rounded-full"></div>
|
<div className="h-0.5 w-16 md:w-20 bg-gradient-to-r from-[var(--luxury-gold)] to-[var(--luxury-gold-light)] mx-auto rounded-full"></div>
|
||||||
</div>
|
</div>
|
||||||
{pageContent.promotions_section_title && (
|
{pageContent.promotions_section_title && (
|
||||||
<h2 className="text-2xl sm:text-3xl md:text-4xl font-serif font-bold text-gray-900 tracking-tight mb-3 md:mb-4 px-4">
|
<h2 className="text-2xl sm:text-3xl md:text-4xl font-serif font-bold text-gray-900 tracking-tight mb-3 md:mb-4 px-4">
|
||||||
@@ -1524,7 +1524,7 @@ const HomePage: React.FC = () => {
|
|||||||
<div className="p-6">
|
<div className="p-6">
|
||||||
<div className="flex items-start justify-between mb-2">
|
<div className="flex items-start justify-between mb-2">
|
||||||
<h3 className={`text-xl font-bold mb-2 transition-colors ${
|
<h3 className={`text-xl font-bold mb-2 transition-colors ${
|
||||||
isValid ? 'text-gray-900 group-hover:text-[#d4af37]' : 'text-gray-500'
|
isValid ? 'text-gray-900 group-hover:text-[var(--luxury-gold)]' : 'text-gray-500'
|
||||||
}`}>
|
}`}>
|
||||||
{promo.title}
|
{promo.title}
|
||||||
</h3>
|
</h3>
|
||||||
@@ -1561,7 +1561,7 @@ const HomePage: React.FC = () => {
|
|||||||
}}
|
}}
|
||||||
className={`inline-flex items-center gap-2 px-6 py-2 rounded-lg font-semibold transition-all duration-300 ${
|
className={`inline-flex items-center gap-2 px-6 py-2 rounded-lg font-semibold transition-all duration-300 ${
|
||||||
isValid
|
isValid
|
||||||
? 'bg-gradient-to-r from-[#d4af37] to-[#c9a227] text-[#0f0f0f] hover:from-[#f5d76e] hover:to-[#d4af37] cursor-pointer group-hover:shadow-lg'
|
? 'bg-gradient-to-r from-[var(--luxury-gold)] to-[var(--luxury-gold-dark)] text-[#0f0f0f] hover:from-[var(--luxury-gold-light)] hover:to-[var(--luxury-gold)] cursor-pointer group-hover:shadow-lg'
|
||||||
: 'bg-gray-300 text-gray-500 cursor-not-allowed'
|
: 'bg-gray-300 text-gray-500 cursor-not-allowed'
|
||||||
}`}
|
}`}
|
||||||
role="button"
|
role="button"
|
||||||
@@ -1587,7 +1587,7 @@ const HomePage: React.FC = () => {
|
|||||||
<section className="container mx-auto px-4 sm:px-6 lg:px-8 py-12 md:py-16 bg-gradient-to-b from-white via-gray-50/20 to-white">
|
<section className="container mx-auto px-4 sm:px-6 lg:px-8 py-12 md:py-16 bg-gradient-to-b from-white via-gray-50/20 to-white">
|
||||||
<div className="text-center mb-8 md:mb-10 animate-fade-in">
|
<div className="text-center mb-8 md:mb-10 animate-fade-in">
|
||||||
<div className="inline-block mb-3">
|
<div className="inline-block mb-3">
|
||||||
<div className="h-0.5 w-16 md:w-20 bg-gradient-to-r from-[#d4af37] to-[#f5d76e] mx-auto rounded-full"></div>
|
<div className="h-0.5 w-16 md:w-20 bg-gradient-to-r from-[var(--luxury-gold)] to-[var(--luxury-gold-light)] mx-auto rounded-full"></div>
|
||||||
</div>
|
</div>
|
||||||
{pageContent.blog_section_title && (
|
{pageContent.blog_section_title && (
|
||||||
<h2 className="text-2xl sm:text-3xl md:text-4xl font-serif font-bold text-gray-900 tracking-tight mb-3 md:mb-4 px-4">
|
<h2 className="text-2xl sm:text-3xl md:text-4xl font-serif font-bold text-gray-900 tracking-tight mb-3 md:mb-4 px-4">
|
||||||
@@ -1636,13 +1636,13 @@ const HomePage: React.FC = () => {
|
|||||||
{new Date(post.published_at).toLocaleDateString()}
|
{new Date(post.published_at).toLocaleDateString()}
|
||||||
</p>
|
</p>
|
||||||
)}
|
)}
|
||||||
<h3 className="text-xl font-bold text-gray-900 mb-2 group-hover:text-[#d4af37] transition-colors">
|
<h3 className="text-xl font-bold text-gray-900 mb-2 group-hover:text-[var(--luxury-gold)] transition-colors">
|
||||||
{post.title}
|
{post.title}
|
||||||
</h3>
|
</h3>
|
||||||
{post.excerpt && (
|
{post.excerpt && (
|
||||||
<p className="text-gray-600 mb-4 line-clamp-3">{post.excerpt}</p>
|
<p className="text-gray-600 mb-4 line-clamp-3">{post.excerpt}</p>
|
||||||
)}
|
)}
|
||||||
<div className="flex items-center gap-2 text-[#d4af37] font-semibold group-hover:gap-3 transition-all">
|
<div className="flex items-center gap-2 text-[var(--luxury-gold)] font-semibold group-hover:gap-3 transition-all">
|
||||||
Read More
|
Read More
|
||||||
<ArrowRight className="w-4 h-4" />
|
<ArrowRight className="w-4 h-4" />
|
||||||
</div>
|
</div>
|
||||||
@@ -1655,7 +1655,7 @@ const HomePage: React.FC = () => {
|
|||||||
<div className="text-center mt-8 md:mt-10">
|
<div className="text-center mt-8 md:mt-10">
|
||||||
<Link
|
<Link
|
||||||
to="/blog"
|
to="/blog"
|
||||||
className="inline-flex items-center gap-2 px-8 py-3 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 hover:-translate-y-0.5"
|
className="inline-flex items-center gap-2 px-8 py-3 bg-gradient-to-r from-[var(--luxury-gold)] to-[var(--luxury-gold-dark)] text-[#0f0f0f] rounded-lg font-semibold hover:from-[var(--luxury-gold-light)] hover:to-[var(--luxury-gold)] transition-all duration-300 shadow-lg shadow-[var(--luxury-gold)]/30 hover:shadow-xl hover:shadow-[var(--luxury-gold)]/40 hover:-translate-y-0.5"
|
||||||
>
|
>
|
||||||
View All Posts
|
View All Posts
|
||||||
<ArrowRight className="w-5 h-5" />
|
<ArrowRight className="w-5 h-5" />
|
||||||
@@ -1680,7 +1680,7 @@ const HomePage: React.FC = () => {
|
|||||||
e.stopPropagation();
|
e.stopPropagation();
|
||||||
setLightboxOpen(false);
|
setLightboxOpen(false);
|
||||||
}}
|
}}
|
||||||
className="absolute top-4 right-4 md:top-6 md:right-6 z-50 p-3 bg-black/50 hover:bg-black/70 rounded-full text-white hover:text-[#d4af37] transition-all duration-300 backdrop-blur-sm border border-white/10 hover:border-[#d4af37]/50"
|
className="absolute top-4 right-4 md:top-6 md:right-6 z-50 p-3 bg-black/50 hover:bg-black/70 rounded-full text-white hover:text-[var(--luxury-gold)] transition-all duration-300 backdrop-blur-sm border border-white/10 hover:border-[var(--luxury-gold)]/50"
|
||||||
aria-label="Close"
|
aria-label="Close"
|
||||||
>
|
>
|
||||||
<X className="w-6 h-6 md:w-7 md:h-7" />
|
<X className="w-6 h-6 md:w-7 md:h-7" />
|
||||||
@@ -1693,7 +1693,7 @@ const HomePage: React.FC = () => {
|
|||||||
e.stopPropagation();
|
e.stopPropagation();
|
||||||
setLightboxIndex((prev) => (prev === 0 ? lightboxImages.length - 1 : prev - 1));
|
setLightboxIndex((prev) => (prev === 0 ? lightboxImages.length - 1 : prev - 1));
|
||||||
}}
|
}}
|
||||||
className="absolute left-4 md:left-6 z-50 p-3 bg-black/50 hover:bg-black/70 rounded-full text-white hover:text-[#d4af37] transition-all duration-300 backdrop-blur-sm border border-white/10 hover:border-[#d4af37]/50"
|
className="absolute left-4 md:left-6 z-50 p-3 bg-black/50 hover:bg-black/70 rounded-full text-white hover:text-[var(--luxury-gold)] transition-all duration-300 backdrop-blur-sm border border-white/10 hover:border-[var(--luxury-gold)]/50"
|
||||||
aria-label="Previous image"
|
aria-label="Previous image"
|
||||||
>
|
>
|
||||||
<ChevronLeft className="w-6 h-6 md:w-7 md:h-7" />
|
<ChevronLeft className="w-6 h-6 md:w-7 md:h-7" />
|
||||||
@@ -1707,7 +1707,7 @@ const HomePage: React.FC = () => {
|
|||||||
e.stopPropagation();
|
e.stopPropagation();
|
||||||
setLightboxIndex((prev) => (prev === lightboxImages.length - 1 ? 0 : prev + 1));
|
setLightboxIndex((prev) => (prev === lightboxImages.length - 1 ? 0 : prev + 1));
|
||||||
}}
|
}}
|
||||||
className="absolute right-4 md:right-6 z-50 p-3 bg-black/50 hover:bg-black/70 rounded-full text-white hover:text-[#d4af37] transition-all duration-300 backdrop-blur-sm border border-white/10 hover:border-[#d4af37]/50"
|
className="absolute right-4 md:right-6 z-50 p-3 bg-black/50 hover:bg-black/70 rounded-full text-white hover:text-[var(--luxury-gold)] transition-all duration-300 backdrop-blur-sm border border-white/10 hover:border-[var(--luxury-gold)]/50"
|
||||||
aria-label="Next image"
|
aria-label="Next image"
|
||||||
>
|
>
|
||||||
<ChevronRight className="w-6 h-6 md:w-7 md:h-7" />
|
<ChevronRight className="w-6 h-6 md:w-7 md:h-7" />
|
||||||
@@ -1748,7 +1748,7 @@ const HomePage: React.FC = () => {
|
|||||||
}}
|
}}
|
||||||
className={`flex-shrink-0 w-16 h-16 md:w-20 md:h-20 rounded-lg overflow-hidden border-2 transition-all duration-300 ${
|
className={`flex-shrink-0 w-16 h-16 md:w-20 md:h-20 rounded-lg overflow-hidden border-2 transition-all duration-300 ${
|
||||||
idx === lightboxIndex
|
idx === lightboxIndex
|
||||||
? 'border-[#d4af37] shadow-lg shadow-[#d4af37]/50 scale-110'
|
? 'border-[var(--luxury-gold)] shadow-lg shadow-[var(--luxury-gold)]/50 scale-110'
|
||||||
: 'border-white/30 hover:border-white/60 opacity-70 hover:opacity-100'
|
: 'border-white/30 hover:border-white/60 opacity-70 hover:opacity-100'
|
||||||
}`}
|
}`}
|
||||||
>
|
>
|
||||||
|
|||||||
@@ -99,12 +99,12 @@ const PrivacyPolicyPage: React.FC = () => {
|
|||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<div className="text-center">
|
<div className="text-center">
|
||||||
<Shield className="w-16 h-16 text-[#d4af37]/50 mx-auto mb-4" />
|
<Shield className="w-16 h-16 text-[var(--luxury-gold)]/50 mx-auto mb-4" />
|
||||||
<h1 className="text-2xl font-elegant font-bold text-white mb-2">Privacy Policy</h1>
|
<h1 className="text-2xl font-elegant font-bold text-white mb-2">Privacy Policy</h1>
|
||||||
<p className="text-gray-400">This page is currently unavailable.</p>
|
<p className="text-gray-400">This page is currently unavailable.</p>
|
||||||
<Link
|
<Link
|
||||||
to="/"
|
to="/"
|
||||||
className="inline-flex items-center gap-2 text-[#d4af37]/80 hover:text-[#d4af37] mt-6 transition-all duration-300"
|
className="inline-flex items-center gap-2 text-[var(--luxury-gold)]/80 hover:text-[var(--luxury-gold)] mt-6 transition-all duration-300"
|
||||||
>
|
>
|
||||||
<ArrowLeft className="w-4 h-4" />
|
<ArrowLeft className="w-4 h-4" />
|
||||||
<span>Back to Home</span>
|
<span>Back to Home</span>
|
||||||
@@ -129,7 +129,7 @@ const PrivacyPolicyPage: React.FC = () => {
|
|||||||
{/* Back Link */}
|
{/* Back Link */}
|
||||||
<Link
|
<Link
|
||||||
to="/"
|
to="/"
|
||||||
className="inline-flex items-center gap-2 text-[#d4af37]/80 hover:text-[#d4af37] mb-6 transition-all duration-300 group font-light tracking-wide text-xs sm:text-sm"
|
className="inline-flex items-center gap-2 text-[var(--luxury-gold)]/80 hover:text-[var(--luxury-gold)] mb-6 transition-all duration-300 group font-light tracking-wide text-xs sm:text-sm"
|
||||||
>
|
>
|
||||||
<ArrowLeft className="w-4 h-4 group-hover:-translate-x-1 transition-transform" />
|
<ArrowLeft className="w-4 h-4 group-hover:-translate-x-1 transition-transform" />
|
||||||
<span>Back to Home</span>
|
<span>Back to Home</span>
|
||||||
@@ -137,10 +137,10 @@ const PrivacyPolicyPage: React.FC = () => {
|
|||||||
|
|
||||||
{/* Header */}
|
{/* Header */}
|
||||||
<div className="mb-8 sm:mb-12 text-center">
|
<div className="mb-8 sm:mb-12 text-center">
|
||||||
<div className="inline-flex items-center justify-center w-16 h-16 sm:w-20 sm:h-20 mb-4 sm:mb-6 bg-gradient-to-br from-[#d4af37]/20 to-[#c9a227]/10 rounded-full border border-[#d4af37]/30">
|
<div className="inline-flex items-center justify-center w-16 h-16 sm:w-20 sm:h-20 mb-4 sm:mb-6 bg-gradient-to-br from-[var(--luxury-gold)]/20 to-[var(--luxury-gold-dark)]/10 rounded-full border border-[var(--luxury-gold)]/30">
|
||||||
<Shield className="w-8 h-8 sm:w-10 sm:h-10 text-[#d4af37]" />
|
<Shield className="w-8 h-8 sm:w-10 sm:h-10 text-[var(--luxury-gold)]" />
|
||||||
</div>
|
</div>
|
||||||
<h1 className="text-3xl sm:text-4xl lg:text-5xl font-elegant font-bold text-white mb-3 sm:mb-4 tracking-tight leading-tight bg-gradient-to-r from-white via-[#d4af37] to-white bg-clip-text text-transparent">
|
<h1 className="text-3xl sm:text-4xl lg:text-5xl font-elegant font-bold text-white mb-3 sm:mb-4 tracking-tight leading-tight bg-gradient-to-r from-white via-[var(--luxury-gold)] to-white bg-clip-text text-transparent">
|
||||||
{pageContent.title || 'Privacy Policy'}
|
{pageContent.title || 'Privacy Policy'}
|
||||||
</h1>
|
</h1>
|
||||||
{pageContent.subtitle && (
|
{pageContent.subtitle && (
|
||||||
@@ -151,18 +151,18 @@ const PrivacyPolicyPage: React.FC = () => {
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Content */}
|
{/* Content */}
|
||||||
<div className="bg-gradient-to-br from-[#1a1a1a] to-[#0a0a0a] rounded-xl border border-[#d4af37]/20 backdrop-blur-xl shadow-2xl shadow-[#d4af37]/5 p-6 sm:p-8 lg:p-12">
|
<div className="bg-gradient-to-br from-[#1a1a1a] to-[#0a0a0a] rounded-xl border border-[var(--luxury-gold)]/20 backdrop-blur-xl shadow-2xl shadow-[var(--luxury-gold)]/5 p-6 sm:p-8 lg:p-12">
|
||||||
<div
|
<div
|
||||||
className="prose prose-invert prose-lg max-w-none text-gray-300
|
className="prose prose-invert prose-lg max-w-none text-gray-300
|
||||||
prose-headings:text-white prose-headings:font-elegant prose-headings:font-semibold
|
prose-headings:text-white prose-headings:font-elegant prose-headings:font-semibold
|
||||||
prose-h2:text-2xl prose-h2:mt-8 prose-h2:mb-4 prose-h2:border-b prose-h2:border-[#d4af37]/20 prose-h2:pb-2
|
prose-h2:text-2xl prose-h2:mt-8 prose-h2:mb-4 prose-h2:border-b prose-h2:border-[var(--luxury-gold)]/20 prose-h2:pb-2
|
||||||
prose-p:text-gray-300 prose-p:font-light prose-p:leading-relaxed prose-p:mb-4
|
prose-p:text-gray-300 prose-p:font-light prose-p:leading-relaxed prose-p:mb-4
|
||||||
prose-ul:text-gray-300 prose-ul:font-light prose-ul:my-4
|
prose-ul:text-gray-300 prose-ul:font-light prose-ul:my-4
|
||||||
prose-li:text-gray-300 prose-li:mb-2 prose-li:ml-4
|
prose-li:text-gray-300 prose-li:mb-2 prose-li:ml-4
|
||||||
prose-strong:text-[#d4af37] prose-strong:font-medium
|
prose-strong:text-[var(--luxury-gold)] prose-strong:font-medium
|
||||||
prose-a:text-[#d4af37] prose-a:no-underline hover:prose-a:underline
|
prose-a:text-[var(--luxury-gold)] prose-a:no-underline hover:prose-a:underline
|
||||||
[&_*]:text-gray-300 [&_h1]:text-white [&_h2]:text-white [&_h3]:text-white [&_h4]:text-white [&_h5]:text-white [&_h6]:text-white
|
[&_*]:text-gray-300 [&_h1]:text-white [&_h2]:text-white [&_h3]:text-white [&_h4]:text-white [&_h5]:text-white [&_h6]:text-white
|
||||||
[&_strong]:text-[#d4af37] [&_b]:text-[#d4af37] [&_a]:text-[#d4af37]"
|
[&_strong]:text-[var(--luxury-gold)] [&_b]:text-[var(--luxury-gold)] [&_a]:text-[var(--luxury-gold)]"
|
||||||
style={{ color: '#d1d5db' }}
|
style={{ color: '#d1d5db' }}
|
||||||
dangerouslySetInnerHTML={createSanitizedHtml(
|
dangerouslySetInnerHTML={createSanitizedHtml(
|
||||||
pageContent.content || pageContent.description || '<p style="color: #d1d5db;">No content available.</p>'
|
pageContent.content || pageContent.description || '<p style="color: #d1d5db;">No content available.</p>'
|
||||||
@@ -176,7 +176,7 @@ const PrivacyPolicyPage: React.FC = () => {
|
|||||||
<div className="text-center">
|
<div className="text-center">
|
||||||
<p className="text-sm text-gray-400 font-light">
|
<p className="text-sm text-gray-400 font-light">
|
||||||
For questions about this policy, contact us at{' '}
|
For questions about this policy, contact us at{' '}
|
||||||
<a href={`mailto:${settings.company_email}`} className="text-[#d4af37] hover:underline">
|
<a href={`mailto:${settings.company_email}`} className="text-[var(--luxury-gold)] hover:underline">
|
||||||
{settings.company_email}
|
{settings.company_email}
|
||||||
</a>
|
</a>
|
||||||
</p>
|
</p>
|
||||||
@@ -185,7 +185,7 @@ const PrivacyPolicyPage: React.FC = () => {
|
|||||||
<div className="text-center">
|
<div className="text-center">
|
||||||
<Link
|
<Link
|
||||||
to="/gdpr"
|
to="/gdpr"
|
||||||
className="inline-flex items-center gap-2 text-sm text-[#d4af37] hover:text-[#f5d76e] transition-colors font-light"
|
className="inline-flex items-center gap-2 text-sm text-[var(--luxury-gold)] hover:text-[var(--luxury-gold-light)] transition-colors font-light"
|
||||||
>
|
>
|
||||||
<Shield className="w-4 h-4" />
|
<Shield className="w-4 h-4" />
|
||||||
<span>Manage Your Data Privacy (GDPR)</span>
|
<span>Manage Your Data Privacy (GDPR)</span>
|
||||||
|
|||||||
@@ -99,12 +99,12 @@ const RefundsPolicyPage: React.FC = () => {
|
|||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<div className="text-center">
|
<div className="text-center">
|
||||||
<RefreshCw className="w-16 h-16 text-[#d4af37]/50 mx-auto mb-4" />
|
<RefreshCw className="w-16 h-16 text-[var(--luxury-gold)]/50 mx-auto mb-4" />
|
||||||
<h1 className="text-2xl font-elegant font-bold text-white mb-2">Refunds Policy</h1>
|
<h1 className="text-2xl font-elegant font-bold text-white mb-2">Refunds Policy</h1>
|
||||||
<p className="text-gray-400">This page is currently unavailable.</p>
|
<p className="text-gray-400">This page is currently unavailable.</p>
|
||||||
<Link
|
<Link
|
||||||
to="/"
|
to="/"
|
||||||
className="inline-flex items-center gap-2 text-[#d4af37]/80 hover:text-[#d4af37] mt-6 transition-all duration-300"
|
className="inline-flex items-center gap-2 text-[var(--luxury-gold)]/80 hover:text-[var(--luxury-gold)] mt-6 transition-all duration-300"
|
||||||
>
|
>
|
||||||
<ArrowLeft className="w-4 h-4" />
|
<ArrowLeft className="w-4 h-4" />
|
||||||
<span>Back to Home</span>
|
<span>Back to Home</span>
|
||||||
@@ -129,7 +129,7 @@ const RefundsPolicyPage: React.FC = () => {
|
|||||||
{/* Back Link */}
|
{/* Back Link */}
|
||||||
<Link
|
<Link
|
||||||
to="/"
|
to="/"
|
||||||
className="inline-flex items-center gap-2 text-[#d4af37]/80 hover:text-[#d4af37] mb-6 transition-all duration-300 group font-light tracking-wide text-xs sm:text-sm"
|
className="inline-flex items-center gap-2 text-[var(--luxury-gold)]/80 hover:text-[var(--luxury-gold)] mb-6 transition-all duration-300 group font-light tracking-wide text-xs sm:text-sm"
|
||||||
>
|
>
|
||||||
<ArrowLeft className="w-4 h-4 group-hover:-translate-x-1 transition-transform" />
|
<ArrowLeft className="w-4 h-4 group-hover:-translate-x-1 transition-transform" />
|
||||||
<span>Back to Home</span>
|
<span>Back to Home</span>
|
||||||
@@ -137,10 +137,10 @@ const RefundsPolicyPage: React.FC = () => {
|
|||||||
|
|
||||||
{/* Header */}
|
{/* Header */}
|
||||||
<div className="mb-8 sm:mb-12 text-center">
|
<div className="mb-8 sm:mb-12 text-center">
|
||||||
<div className="inline-flex items-center justify-center w-16 h-16 sm:w-20 sm:h-20 mb-4 sm:mb-6 bg-gradient-to-br from-[#d4af37]/20 to-[#c9a227]/10 rounded-full border border-[#d4af37]/30">
|
<div className="inline-flex items-center justify-center w-16 h-16 sm:w-20 sm:h-20 mb-4 sm:mb-6 bg-gradient-to-br from-[var(--luxury-gold)]/20 to-[var(--luxury-gold-dark)]/10 rounded-full border border-[var(--luxury-gold)]/30">
|
||||||
<RefreshCw className="w-8 h-8 sm:w-10 sm:h-10 text-[#d4af37]" />
|
<RefreshCw className="w-8 h-8 sm:w-10 sm:h-10 text-[var(--luxury-gold)]" />
|
||||||
</div>
|
</div>
|
||||||
<h1 className="text-3xl sm:text-4xl lg:text-5xl font-elegant font-bold text-white mb-3 sm:mb-4 tracking-tight leading-tight bg-gradient-to-r from-white via-[#d4af37] to-white bg-clip-text text-transparent">
|
<h1 className="text-3xl sm:text-4xl lg:text-5xl font-elegant font-bold text-white mb-3 sm:mb-4 tracking-tight leading-tight bg-gradient-to-r from-white via-[var(--luxury-gold)] to-white bg-clip-text text-transparent">
|
||||||
{pageContent.title || 'Refunds Policy'}
|
{pageContent.title || 'Refunds Policy'}
|
||||||
</h1>
|
</h1>
|
||||||
{pageContent.subtitle && (
|
{pageContent.subtitle && (
|
||||||
@@ -151,18 +151,18 @@ const RefundsPolicyPage: React.FC = () => {
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Content */}
|
{/* Content */}
|
||||||
<div className="bg-gradient-to-br from-[#1a1a1a] to-[#0a0a0a] rounded-xl border border-[#d4af37]/20 backdrop-blur-xl shadow-2xl shadow-[#d4af37]/5 p-6 sm:p-8 lg:p-12">
|
<div className="bg-gradient-to-br from-[#1a1a1a] to-[#0a0a0a] rounded-xl border border-[var(--luxury-gold)]/20 backdrop-blur-xl shadow-2xl shadow-[var(--luxury-gold)]/5 p-6 sm:p-8 lg:p-12">
|
||||||
<div
|
<div
|
||||||
className="prose prose-invert prose-lg max-w-none text-gray-300
|
className="prose prose-invert prose-lg max-w-none text-gray-300
|
||||||
prose-headings:text-white prose-headings:font-elegant prose-headings:font-semibold
|
prose-headings:text-white prose-headings:font-elegant prose-headings:font-semibold
|
||||||
prose-h2:text-2xl prose-h2:mt-8 prose-h2:mb-4 prose-h2:border-b prose-h2:border-[#d4af37]/20 prose-h2:pb-2
|
prose-h2:text-2xl prose-h2:mt-8 prose-h2:mb-4 prose-h2:border-b prose-h2:border-[var(--luxury-gold)]/20 prose-h2:pb-2
|
||||||
prose-p:text-gray-300 prose-p:font-light prose-p:leading-relaxed prose-p:mb-4
|
prose-p:text-gray-300 prose-p:font-light prose-p:leading-relaxed prose-p:mb-4
|
||||||
prose-ul:text-gray-300 prose-ul:font-light prose-ul:my-4
|
prose-ul:text-gray-300 prose-ul:font-light prose-ul:my-4
|
||||||
prose-li:text-gray-300 prose-li:mb-2 prose-li:ml-4
|
prose-li:text-gray-300 prose-li:mb-2 prose-li:ml-4
|
||||||
prose-strong:text-[#d4af37] prose-strong:font-medium
|
prose-strong:text-[var(--luxury-gold)] prose-strong:font-medium
|
||||||
prose-a:text-[#d4af37] prose-a:no-underline hover:prose-a:underline
|
prose-a:text-[var(--luxury-gold)] prose-a:no-underline hover:prose-a:underline
|
||||||
[&_*]:text-gray-300 [&_h1]:text-white [&_h2]:text-white [&_h3]:text-white [&_h4]:text-white [&_h5]:text-white [&_h6]:text-white
|
[&_*]:text-gray-300 [&_h1]:text-white [&_h2]:text-white [&_h3]:text-white [&_h4]:text-white [&_h5]:text-white [&_h6]:text-white
|
||||||
[&_strong]:text-[#d4af37] [&_b]:text-[#d4af37] [&_a]:text-[#d4af37]"
|
[&_strong]:text-[var(--luxury-gold)] [&_b]:text-[var(--luxury-gold)] [&_a]:text-[var(--luxury-gold)]"
|
||||||
style={{ color: '#d1d5db' }}
|
style={{ color: '#d1d5db' }}
|
||||||
dangerouslySetInnerHTML={createSanitizedHtml(
|
dangerouslySetInnerHTML={createSanitizedHtml(
|
||||||
pageContent.content || pageContent.description || '<p style="color: #d1d5db;">No content available.</p>'
|
pageContent.content || pageContent.description || '<p style="color: #d1d5db;">No content available.</p>'
|
||||||
@@ -175,7 +175,7 @@ const RefundsPolicyPage: React.FC = () => {
|
|||||||
<div className="mt-8 text-center">
|
<div className="mt-8 text-center">
|
||||||
<p className="text-sm text-gray-400 font-light">
|
<p className="text-sm text-gray-400 font-light">
|
||||||
For refund inquiries, contact us at{' '}
|
For refund inquiries, contact us at{' '}
|
||||||
<a href={`mailto:${settings.company_email}`} className="text-[#d4af37] hover:underline">
|
<a href={`mailto:${settings.company_email}`} className="text-[var(--luxury-gold)] hover:underline">
|
||||||
{settings.company_email}
|
{settings.company_email}
|
||||||
</a>
|
</a>
|
||||||
</p>
|
</p>
|
||||||
|
|||||||
@@ -291,7 +291,7 @@ const ServiceDetailPage: React.FC = () => {
|
|||||||
<div className="min-h-screen bg-gradient-to-b from-[#0f0f0f] via-[#1a1a1a] to-black text-white flex items-center justify-center">
|
<div className="min-h-screen bg-gradient-to-b from-[#0f0f0f] via-[#1a1a1a] to-black text-white flex items-center justify-center">
|
||||||
<div className="text-center">
|
<div className="text-center">
|
||||||
<h1 className="text-2xl font-bold mb-4">Service not found</h1>
|
<h1 className="text-2xl font-bold mb-4">Service not found</h1>
|
||||||
<Link to="/services" className="text-[#d4af37] hover:underline">
|
<Link to="/services" className="text-[var(--luxury-gold)] hover:underline">
|
||||||
Back to Services
|
Back to Services
|
||||||
</Link>
|
</Link>
|
||||||
</div>
|
</div>
|
||||||
@@ -316,8 +316,8 @@ const ServiceDetailPage: React.FC = () => {
|
|||||||
|
|
||||||
{/* Luxury Overlay Pattern */}
|
{/* Luxury Overlay Pattern */}
|
||||||
<div className="absolute inset-0 opacity-10">
|
<div className="absolute inset-0 opacity-10">
|
||||||
<div className="absolute top-0 left-0 w-96 h-96 bg-[#d4af37] rounded-full blur-3xl"></div>
|
<div className="absolute top-0 left-0 w-96 h-96 bg-[var(--luxury-gold)] rounded-full blur-3xl"></div>
|
||||||
<div className="absolute bottom-0 right-0 w-96 h-96 bg-[#c9a227] rounded-full blur-3xl"></div>
|
<div className="absolute bottom-0 right-0 w-96 h-96 bg-[var(--luxury-gold-dark)] rounded-full blur-3xl"></div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Hero Content Overlay */}
|
{/* Hero Content Overlay */}
|
||||||
@@ -326,12 +326,12 @@ const ServiceDetailPage: React.FC = () => {
|
|||||||
<div className="max-w-5xl mx-auto">
|
<div className="max-w-5xl mx-auto">
|
||||||
{service.category && (
|
{service.category && (
|
||||||
<div className="flex flex-wrap gap-3 mb-6">
|
<div className="flex flex-wrap gap-3 mb-6">
|
||||||
<span className="inline-flex items-center gap-2 px-5 py-2.5 bg-gradient-to-br from-[#d4af37]/20 to-[#c9a227]/10 backdrop-blur-md rounded-xl text-[#d4af37] text-sm font-semibold border border-[#d4af37]/40 shadow-xl">
|
<span className="inline-flex items-center gap-2 px-5 py-2.5 bg-gradient-to-br from-[var(--luxury-gold)]/20 to-[var(--luxury-gold-dark)]/10 backdrop-blur-md rounded-xl text-[var(--luxury-gold)] text-sm font-semibold border border-[var(--luxury-gold)]/40 shadow-xl">
|
||||||
<Tag className="w-4 h-4" />
|
<Tag className="w-4 h-4" />
|
||||||
{service.category}
|
{service.category}
|
||||||
</span>
|
</span>
|
||||||
{service.type === 'luxury' && (
|
{service.type === 'luxury' && (
|
||||||
<span className="inline-flex items-center gap-2 px-5 py-2.5 bg-gradient-to-br from-[#d4af37]/30 to-[#c9a227]/20 backdrop-blur-md rounded-xl text-[#0f0f0f] text-sm font-bold border border-[#d4af37]/50 shadow-xl">
|
<span className="inline-flex items-center gap-2 px-5 py-2.5 bg-gradient-to-br from-[var(--luxury-gold)]/30 to-[var(--luxury-gold-dark)]/20 backdrop-blur-md rounded-xl text-[#0f0f0f] text-sm font-bold border border-[var(--luxury-gold)]/50 shadow-xl">
|
||||||
<Star className="w-4 h-4 fill-[#0f0f0f]" />
|
<Star className="w-4 h-4 fill-[#0f0f0f]" />
|
||||||
Premium
|
Premium
|
||||||
</span>
|
</span>
|
||||||
@@ -348,7 +348,7 @@ const ServiceDetailPage: React.FC = () => {
|
|||||||
)}
|
)}
|
||||||
{service.price !== undefined && (
|
{service.price !== undefined && (
|
||||||
<div className="flex items-baseline gap-3 mb-8">
|
<div className="flex items-baseline gap-3 mb-8">
|
||||||
<span className="text-4xl sm:text-5xl lg:text-6xl font-serif font-bold bg-gradient-to-r from-[#d4af37] via-[#f5d76e] to-[#d4af37] bg-clip-text text-transparent drop-shadow-lg">
|
<span className="text-4xl sm:text-5xl lg:text-6xl font-serif font-bold bg-gradient-to-r from-[var(--luxury-gold)] via-[var(--luxury-gold-light)] to-[var(--luxury-gold)] bg-clip-text text-transparent drop-shadow-lg">
|
||||||
{formatCurrency(service.price)}
|
{formatCurrency(service.price)}
|
||||||
</span>
|
</span>
|
||||||
{service.unit && (
|
{service.unit && (
|
||||||
@@ -363,7 +363,7 @@ const ServiceDetailPage: React.FC = () => {
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Decorative Bottom Border */}
|
{/* Decorative Bottom Border */}
|
||||||
<div className="absolute bottom-0 left-0 w-full h-1 bg-gradient-to-r from-transparent via-[#d4af37]/50 to-transparent"></div>
|
<div className="absolute bottom-0 left-0 w-full h-1 bg-gradient-to-r from-transparent via-[var(--luxury-gold)]/50 to-transparent"></div>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
@@ -375,7 +375,7 @@ const ServiceDetailPage: React.FC = () => {
|
|||||||
{!service.image && (
|
{!service.image && (
|
||||||
<Link
|
<Link
|
||||||
to="/services"
|
to="/services"
|
||||||
className="inline-flex items-center gap-3 px-6 py-3 bg-gradient-to-br from-[#1a1a1a] to-[#0f0f0f] border-2 border-[#d4af37]/20 rounded-xl text-gray-300 hover:text-[#d4af37] hover:border-[#d4af37]/50 transition-all duration-300 mb-12 group"
|
className="inline-flex items-center gap-3 px-6 py-3 bg-gradient-to-br from-[#1a1a1a] to-[#0f0f0f] border-2 border-[var(--luxury-gold)]/20 rounded-xl text-gray-300 hover:text-[var(--luxury-gold)] hover:border-[var(--luxury-gold)]/50 transition-all duration-300 mb-12 group"
|
||||||
>
|
>
|
||||||
<ArrowLeft className="w-5 h-5 group-hover:-translate-x-1 transition-transform" />
|
<ArrowLeft className="w-5 h-5 group-hover:-translate-x-1 transition-transform" />
|
||||||
<span className="font-medium">Back to Services</span>
|
<span className="font-medium">Back to Services</span>
|
||||||
@@ -389,13 +389,13 @@ const ServiceDetailPage: React.FC = () => {
|
|||||||
<div className="flex flex-wrap gap-3 mb-6">
|
<div className="flex flex-wrap gap-3 mb-6">
|
||||||
<Link
|
<Link
|
||||||
to={`/services?category=${encodeURIComponent(service.category)}`}
|
to={`/services?category=${encodeURIComponent(service.category)}`}
|
||||||
className="inline-flex items-center gap-2 px-5 py-2.5 bg-gradient-to-br from-[#d4af37]/15 to-[#d4af37]/5 text-[#d4af37] rounded-xl text-sm font-semibold border border-[#d4af37]/30 hover:border-[#d4af37]/50 transition-all"
|
className="inline-flex items-center gap-2 px-5 py-2.5 bg-gradient-to-br from-[var(--luxury-gold)]/15 to-[var(--luxury-gold)]/5 text-[var(--luxury-gold)] rounded-xl text-sm font-semibold border border-[var(--luxury-gold)]/30 hover:border-[var(--luxury-gold)]/50 transition-all"
|
||||||
>
|
>
|
||||||
<Tag className="w-4 h-4" />
|
<Tag className="w-4 h-4" />
|
||||||
{service.category}
|
{service.category}
|
||||||
</Link>
|
</Link>
|
||||||
{service.type === 'luxury' && (
|
{service.type === 'luxury' && (
|
||||||
<span className="inline-flex items-center gap-2 px-5 py-2.5 bg-gradient-to-br from-[#d4af37]/20 to-[#c9a227]/10 text-[#d4af37] rounded-xl text-sm font-bold border border-[#d4af37]/40">
|
<span className="inline-flex items-center gap-2 px-5 py-2.5 bg-gradient-to-br from-[var(--luxury-gold)]/20 to-[var(--luxury-gold-dark)]/10 text-[var(--luxury-gold)] rounded-xl text-sm font-bold border border-[var(--luxury-gold)]/40">
|
||||||
<Star className="w-4 h-4" />
|
<Star className="w-4 h-4" />
|
||||||
Premium
|
Premium
|
||||||
</span>
|
</span>
|
||||||
@@ -406,9 +406,9 @@ const ServiceDetailPage: React.FC = () => {
|
|||||||
<div className="flex items-start gap-6 mb-6">
|
<div className="flex items-start gap-6 mb-6">
|
||||||
{service.icon && (LucideIcons as any)[service.icon] && (
|
{service.icon && (LucideIcons as any)[service.icon] && (
|
||||||
<div className="relative flex-shrink-0">
|
<div className="relative flex-shrink-0">
|
||||||
<div className="absolute inset-0 bg-[#d4af37]/20 rounded-full blur-3xl"></div>
|
<div className="absolute inset-0 bg-[var(--luxury-gold)]/20 rounded-full blur-3xl"></div>
|
||||||
{React.createElement((LucideIcons as any)[service.icon], {
|
{React.createElement((LucideIcons as any)[service.icon], {
|
||||||
className: 'w-20 h-20 sm:w-24 sm:h-24 text-[#d4af37] relative z-10 drop-shadow-2xl'
|
className: 'w-20 h-20 sm:w-24 sm:h-24 text-[var(--luxury-gold)] relative z-10 drop-shadow-2xl'
|
||||||
})}
|
})}
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
@@ -418,7 +418,7 @@ const ServiceDetailPage: React.FC = () => {
|
|||||||
</h1>
|
</h1>
|
||||||
{service.price !== undefined && (
|
{service.price !== undefined && (
|
||||||
<div className="flex items-baseline gap-3 mb-6">
|
<div className="flex items-baseline gap-3 mb-6">
|
||||||
<span className="text-4xl sm:text-5xl font-serif font-bold bg-gradient-to-r from-[#d4af37] to-[#f5d76e] bg-clip-text text-transparent">
|
<span className="text-4xl sm:text-5xl font-serif font-bold bg-gradient-to-r from-[var(--luxury-gold)] to-[var(--luxury-gold-light)] bg-clip-text text-transparent">
|
||||||
{formatCurrency(service.price)}
|
{formatCurrency(service.price)}
|
||||||
</span>
|
</span>
|
||||||
{service.unit && (
|
{service.unit && (
|
||||||
@@ -437,10 +437,10 @@ const ServiceDetailPage: React.FC = () => {
|
|||||||
</p>
|
</p>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
<div className="flex flex-wrap items-center gap-6 pt-8 border-t border-[#d4af37]/20">
|
<div className="flex flex-wrap items-center gap-6 pt-8 border-t border-[var(--luxury-gold)]/20">
|
||||||
<button
|
<button
|
||||||
onClick={handleShare}
|
onClick={handleShare}
|
||||||
className="flex items-center gap-3 px-6 py-3 bg-gradient-to-br from-[#1a1a1a] to-[#0f0f0f] border-2 border-[#d4af37]/20 rounded-xl text-[#d4af37] hover:border-[#d4af37]/50 hover:bg-[#d4af37]/5 transition-all duration-300 group"
|
className="flex items-center gap-3 px-6 py-3 bg-gradient-to-br from-[#1a1a1a] to-[#0f0f0f] border-2 border-[var(--luxury-gold)]/20 rounded-xl text-[var(--luxury-gold)] hover:border-[var(--luxury-gold)]/50 hover:bg-[var(--luxury-gold)]/5 transition-all duration-300 group"
|
||||||
>
|
>
|
||||||
<Share2 className="w-5 h-5 group-hover:scale-110 transition-transform" />
|
<Share2 className="w-5 h-5 group-hover:scale-110 transition-transform" />
|
||||||
<span className="font-medium">Share</span>
|
<span className="font-medium">Share</span>
|
||||||
@@ -454,7 +454,7 @@ const ServiceDetailPage: React.FC = () => {
|
|||||||
<div className="flex justify-end mb-12">
|
<div className="flex justify-end mb-12">
|
||||||
<button
|
<button
|
||||||
onClick={handleShare}
|
onClick={handleShare}
|
||||||
className="flex items-center gap-3 px-6 py-3 bg-gradient-to-br from-[#1a1a1a] to-[#0f0f0f] border-2 border-[#d4af37]/20 rounded-xl text-[#d4af37] hover:border-[#d4af37]/50 hover:bg-[#d4af37]/5 transition-all duration-300 group"
|
className="flex items-center gap-3 px-6 py-3 bg-gradient-to-br from-[#1a1a1a] to-[#0f0f0f] border-2 border-[var(--luxury-gold)]/20 rounded-xl text-[var(--luxury-gold)] hover:border-[var(--luxury-gold)]/50 hover:bg-[var(--luxury-gold)]/5 transition-all duration-300 group"
|
||||||
>
|
>
|
||||||
<Share2 className="w-5 h-5 group-hover:scale-110 transition-transform" />
|
<Share2 className="w-5 h-5 group-hover:scale-110 transition-transform" />
|
||||||
<span className="font-medium">Share</span>
|
<span className="font-medium">Share</span>
|
||||||
@@ -466,23 +466,23 @@ const ServiceDetailPage: React.FC = () => {
|
|||||||
{service.content && (
|
{service.content && (
|
||||||
<article className="prose prose-invert prose-xl max-w-none mb-16
|
<article className="prose prose-invert prose-xl max-w-none mb-16
|
||||||
prose-headings:text-white prose-headings:font-serif prose-headings:font-bold
|
prose-headings:text-white prose-headings:font-serif prose-headings:font-bold
|
||||||
prose-h2:text-4xl prose-h2:mt-16 prose-h2:mb-8 prose-h2:border-b-2 prose-h2:border-[#d4af37]/30 prose-h2:pb-4 prose-h2:pt-2
|
prose-h2:text-4xl prose-h2:mt-16 prose-h2:mb-8 prose-h2:border-b-2 prose-h2:border-[var(--luxury-gold)]/30 prose-h2:pb-4 prose-h2:pt-2
|
||||||
prose-h3:text-3xl prose-h3:mt-12 prose-h3:mb-6 prose-h3:text-[#d4af37]
|
prose-h3:text-3xl prose-h3:mt-12 prose-h3:mb-6 prose-h3:text-[var(--luxury-gold)]
|
||||||
prose-h4:text-2xl prose-h4:mt-10 prose-h4:mb-5
|
prose-h4:text-2xl prose-h4:mt-10 prose-h4:mb-5
|
||||||
prose-p:text-gray-300 prose-p:font-light prose-p:leading-relaxed prose-p:mb-8 prose-p:text-lg
|
prose-p:text-gray-300 prose-p:font-light prose-p:leading-relaxed prose-p:mb-8 prose-p:text-lg
|
||||||
prose-ul:text-gray-300 prose-ul:font-light prose-ul:my-8 prose-ul:text-lg
|
prose-ul:text-gray-300 prose-ul:font-light prose-ul:my-8 prose-ul:text-lg
|
||||||
prose-ol:text-gray-300 prose-ol:font-light prose-ol:my-8 prose-ol:text-lg
|
prose-ol:text-gray-300 prose-ol:font-light prose-ol:my-8 prose-ol:text-lg
|
||||||
prose-li:text-gray-300 prose-li:mb-4 prose-li:leading-relaxed
|
prose-li:text-gray-300 prose-li:mb-4 prose-li:leading-relaxed
|
||||||
prose-strong:text-[#d4af37] prose-strong:font-semibold
|
prose-strong:text-[var(--luxury-gold)] prose-strong:font-semibold
|
||||||
prose-a:text-[#d4af37] prose-a:no-underline hover:prose-a:underline prose-a:font-medium
|
prose-a:text-[var(--luxury-gold)] prose-a:no-underline hover:prose-a:underline prose-a:font-medium
|
||||||
prose-img:rounded-2xl prose-img:shadow-2xl prose-img:border-2 prose-img:border-[#d4af37]/20 prose-img:my-12
|
prose-img:rounded-2xl prose-img:shadow-2xl prose-img:border-2 prose-img:border-[var(--luxury-gold)]/20 prose-img:my-12
|
||||||
prose-blockquote:border-l-4 prose-blockquote:border-[#d4af37] prose-blockquote:pl-6 prose-blockquote:italic prose-blockquote:text-gray-300
|
prose-blockquote:border-l-4 prose-blockquote:border-[var(--luxury-gold)] prose-blockquote:pl-6 prose-blockquote:italic prose-blockquote:text-gray-300
|
||||||
[&_*]:text-gray-300 [&_h1]:text-white [&_h2]:text-white [&_h3]:text-[#d4af37] [&_h4]:text-white
|
[&_*]:text-gray-300 [&_h1]:text-white [&_h2]:text-white [&_h3]:text-[var(--luxury-gold)] [&_h4]:text-white
|
||||||
[&_strong]:text-[#d4af37] [&_b]:text-[#d4af37] [&_a]:text-[#d4af37]
|
[&_strong]:text-[var(--luxury-gold)] [&_b]:text-[var(--luxury-gold)] [&_a]:text-[var(--luxury-gold)]
|
||||||
[&_ul]:space-y-3 [&_ol]:space-y-3"
|
[&_ul]:space-y-3 [&_ol]:space-y-3"
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
className="bg-gradient-to-br from-[#1a1a1a]/50 to-[#0f0f0f]/50 rounded-3xl border-2 border-[#d4af37]/10 p-8 md:p-12 lg:p-16 backdrop-blur-sm"
|
className="bg-gradient-to-br from-[#1a1a1a]/50 to-[#0f0f0f]/50 rounded-3xl border-2 border-[var(--luxury-gold)]/10 p-8 md:p-12 lg:p-16 backdrop-blur-sm"
|
||||||
dangerouslySetInnerHTML={createSanitizedHtml(
|
dangerouslySetInnerHTML={createSanitizedHtml(
|
||||||
service.content || '<p>No content available.</p>'
|
service.content || '<p>No content available.</p>'
|
||||||
)}
|
)}
|
||||||
@@ -499,7 +499,7 @@ const ServiceDetailPage: React.FC = () => {
|
|||||||
<div key={index}>
|
<div key={index}>
|
||||||
{/* Hero Section - Enhanced */}
|
{/* Hero Section - Enhanced */}
|
||||||
{section.type === 'hero' && (
|
{section.type === 'hero' && (
|
||||||
<div className="relative rounded-3xl overflow-hidden border-2 border-[#d4af37]/30 shadow-2xl group">
|
<div className="relative rounded-3xl overflow-hidden border-2 border-[var(--luxury-gold)]/30 shadow-2xl group">
|
||||||
{section.image && (
|
{section.image && (
|
||||||
<div className="absolute inset-0">
|
<div className="absolute inset-0">
|
||||||
<img src={section.image} alt={section.title} className="w-full h-full object-cover group-hover:scale-105 transition-transform duration-1000" />
|
<img src={section.image} alt={section.title} className="w-full h-full object-cover group-hover:scale-105 transition-transform duration-1000" />
|
||||||
@@ -508,8 +508,8 @@ const ServiceDetailPage: React.FC = () => {
|
|||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
<div className="absolute inset-0 opacity-10">
|
<div className="absolute inset-0 opacity-10">
|
||||||
<div className="absolute top-0 left-0 w-96 h-96 bg-[#d4af37] rounded-full blur-3xl"></div>
|
<div className="absolute top-0 left-0 w-96 h-96 bg-[var(--luxury-gold)] rounded-full blur-3xl"></div>
|
||||||
<div className="absolute bottom-0 right-0 w-96 h-96 bg-[#c9a227] rounded-full blur-3xl"></div>
|
<div className="absolute bottom-0 right-0 w-96 h-96 bg-[var(--luxury-gold-dark)] rounded-full blur-3xl"></div>
|
||||||
</div>
|
</div>
|
||||||
<div className="relative px-8 py-20 md:px-16 md:py-32 text-center">
|
<div className="relative px-8 py-20 md:px-16 md:py-32 text-center">
|
||||||
{section.title && (
|
{section.title && (
|
||||||
@@ -523,13 +523,13 @@ const ServiceDetailPage: React.FC = () => {
|
|||||||
</p>
|
</p>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
<div className="absolute bottom-0 left-0 w-full h-1 bg-gradient-to-r from-transparent via-[#d4af37]/50 to-transparent"></div>
|
<div className="absolute bottom-0 left-0 w-full h-1 bg-gradient-to-r from-transparent via-[var(--luxury-gold)]/50 to-transparent"></div>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{/* Text Section */}
|
{/* Text Section */}
|
||||||
{section.type === 'text' && (
|
{section.type === 'text' && (
|
||||||
<div className={`bg-gradient-to-br from-[#1a1a1a] to-[#0f0f0f] rounded-3xl border-2 border-[#d4af37]/20 p-8 md:p-12 shadow-xl ${
|
<div className={`bg-gradient-to-br from-[#1a1a1a] to-[#0f0f0f] rounded-3xl border-2 border-[var(--luxury-gold)]/20 p-8 md:p-12 shadow-xl ${
|
||||||
section.alignment === 'center' ? 'text-center' : section.alignment === 'right' ? 'text-right' : 'text-left'
|
section.alignment === 'center' ? 'text-center' : section.alignment === 'right' ? 'text-right' : 'text-left'
|
||||||
}`}>
|
}`}>
|
||||||
{section.title && (
|
{section.title && (
|
||||||
@@ -548,10 +548,10 @@ const ServiceDetailPage: React.FC = () => {
|
|||||||
|
|
||||||
{/* Image Section */}
|
{/* Image Section */}
|
||||||
{section.type === 'image' && section.image && (
|
{section.type === 'image' && section.image && (
|
||||||
<div className="rounded-3xl overflow-hidden border-2 border-[#d4af37]/20 shadow-2xl">
|
<div className="rounded-3xl overflow-hidden border-2 border-[var(--luxury-gold)]/20 shadow-2xl">
|
||||||
<img src={section.image} alt={section.title || 'Service image'} className="w-full h-auto" />
|
<img src={section.image} alt={section.title || 'Service image'} className="w-full h-auto" />
|
||||||
{section.title && (
|
{section.title && (
|
||||||
<div className="bg-gradient-to-br from-[#1a1a1a] to-[#0f0f0f] px-6 py-4 border-t border-[#d4af37]/20">
|
<div className="bg-gradient-to-br from-[#1a1a1a] to-[#0f0f0f] px-6 py-4 border-t border-[var(--luxury-gold)]/20">
|
||||||
<p className="text-gray-400 text-sm font-light italic text-center">{section.title}</p>
|
<p className="text-gray-400 text-sm font-light italic text-center">{section.title}</p>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
@@ -562,7 +562,7 @@ const ServiceDetailPage: React.FC = () => {
|
|||||||
{section.type === 'gallery' && section.images && section.images.length > 0 && (
|
{section.type === 'gallery' && section.images && section.images.length > 0 && (
|
||||||
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6">
|
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6">
|
||||||
{section.images.map((img, imgIndex) => (
|
{section.images.map((img, imgIndex) => (
|
||||||
<div key={imgIndex} className="rounded-2xl overflow-hidden border-2 border-[#d4af37]/20 shadow-xl group hover:border-[#d4af37]/50 transition-all">
|
<div key={imgIndex} className="rounded-2xl overflow-hidden border-2 border-[var(--luxury-gold)]/20 shadow-xl group hover:border-[var(--luxury-gold)]/50 transition-all">
|
||||||
<img src={img} alt={`Gallery image ${imgIndex + 1}`} className="w-full h-64 object-cover group-hover:scale-110 transition-transform duration-500" />
|
<img src={img} alt={`Gallery image ${imgIndex + 1}`} className="w-full h-64 object-cover group-hover:scale-110 transition-transform duration-500" />
|
||||||
</div>
|
</div>
|
||||||
))}
|
))}
|
||||||
@@ -571,15 +571,15 @@ const ServiceDetailPage: React.FC = () => {
|
|||||||
|
|
||||||
{/* Quote Section */}
|
{/* Quote Section */}
|
||||||
{section.type === 'quote' && (
|
{section.type === 'quote' && (
|
||||||
<div className="relative bg-gradient-to-br from-[#1a1a1a] to-[#0f0f0f] rounded-3xl border-2 border-[#d4af37]/30 p-8 md:p-12 shadow-2xl">
|
<div className="relative bg-gradient-to-br from-[#1a1a1a] to-[#0f0f0f] rounded-3xl border-2 border-[var(--luxury-gold)]/30 p-8 md:p-12 shadow-2xl">
|
||||||
<div className="absolute top-6 left-6 text-6xl text-[#d4af37]/20 font-serif">"</div>
|
<div className="absolute top-6 left-6 text-6xl text-[var(--luxury-gold)]/20 font-serif">"</div>
|
||||||
{section.quote && (
|
{section.quote && (
|
||||||
<blockquote className="text-2xl md:text-3xl font-serif font-light text-white italic mb-6 relative z-10 pl-8">
|
<blockquote className="text-2xl md:text-3xl font-serif font-light text-white italic mb-6 relative z-10 pl-8">
|
||||||
{section.quote}
|
{section.quote}
|
||||||
</blockquote>
|
</blockquote>
|
||||||
)}
|
)}
|
||||||
{section.author && (
|
{section.author && (
|
||||||
<cite className="text-[#d4af37] text-lg font-medium not-italic block text-right">
|
<cite className="text-[var(--luxury-gold)] text-lg font-medium not-italic block text-right">
|
||||||
— {section.author}
|
— {section.author}
|
||||||
</cite>
|
</cite>
|
||||||
)}
|
)}
|
||||||
@@ -590,7 +590,7 @@ const ServiceDetailPage: React.FC = () => {
|
|||||||
{section.type === 'features' && section.features && (
|
{section.type === 'features' && section.features && (
|
||||||
<div>
|
<div>
|
||||||
{section.title && (
|
{section.title && (
|
||||||
<h3 className="text-4xl md:text-5xl font-serif font-bold text-white mb-12 text-center border-b-2 border-[#d4af37]/30 pb-6">
|
<h3 className="text-4xl md:text-5xl font-serif font-bold text-white mb-12 text-center border-b-2 border-[var(--luxury-gold)]/30 pb-6">
|
||||||
{section.title}
|
{section.title}
|
||||||
</h3>
|
</h3>
|
||||||
)}
|
)}
|
||||||
@@ -600,18 +600,18 @@ const ServiceDetailPage: React.FC = () => {
|
|||||||
? (LucideIcons as any)[feature.icon]
|
? (LucideIcons as any)[feature.icon]
|
||||||
: null;
|
: null;
|
||||||
return (
|
return (
|
||||||
<div key={featIndex} className="group relative bg-gradient-to-br from-[#1a1a1a] via-[#0f0f0f] to-[#0a0a0a] rounded-3xl border-2 border-[#d4af37]/20 p-8 shadow-2xl hover:border-[#d4af37]/60 hover:shadow-[#d4af37]/20 transition-all duration-500 hover:-translate-y-2">
|
<div key={featIndex} className="group relative bg-gradient-to-br from-[#1a1a1a] via-[#0f0f0f] to-[#0a0a0a] rounded-3xl border-2 border-[var(--luxury-gold)]/20 p-8 shadow-2xl hover:border-[var(--luxury-gold)]/60 hover:shadow-[var(--luxury-gold)]/20 transition-all duration-500 hover:-translate-y-2">
|
||||||
<div className="absolute inset-0 bg-gradient-to-br from-[#d4af37]/0 to-[#d4af37]/0 group-hover:from-[#d4af37]/5 group-hover:to-transparent rounded-3xl transition-all duration-500"></div>
|
<div className="absolute inset-0 bg-gradient-to-br from-[var(--luxury-gold)]/0 to-[var(--luxury-gold)]/0 group-hover:from-[var(--luxury-gold)]/5 group-hover:to-transparent rounded-3xl transition-all duration-500"></div>
|
||||||
<div className="relative z-10">
|
<div className="relative z-10">
|
||||||
{IconComponent && (
|
{IconComponent && (
|
||||||
<div className="mb-6 relative">
|
<div className="mb-6 relative">
|
||||||
<div className="absolute inset-0 bg-[#d4af37]/20 rounded-full blur-2xl group-hover:blur-3xl transition-all"></div>
|
<div className="absolute inset-0 bg-[var(--luxury-gold)]/20 rounded-full blur-2xl group-hover:blur-3xl transition-all"></div>
|
||||||
{React.createElement(IconComponent, {
|
{React.createElement(IconComponent, {
|
||||||
className: 'w-12 h-12 text-[#d4af37] relative z-10 group-hover:scale-110 transition-transform duration-300'
|
className: 'w-12 h-12 text-[var(--luxury-gold)] relative z-10 group-hover:scale-110 transition-transform duration-300'
|
||||||
})}
|
})}
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
<h4 className="text-2xl font-bold text-white mb-4 group-hover:text-[#d4af37] transition-colors">{feature.title}</h4>
|
<h4 className="text-2xl font-bold text-white mb-4 group-hover:text-[var(--luxury-gold)] transition-colors">{feature.title}</h4>
|
||||||
<p className="text-gray-300 font-light leading-relaxed text-base">{feature.description}</p>
|
<p className="text-gray-300 font-light leading-relaxed text-base">{feature.description}</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -623,7 +623,7 @@ const ServiceDetailPage: React.FC = () => {
|
|||||||
|
|
||||||
{/* CTA Section */}
|
{/* CTA Section */}
|
||||||
{section.type === 'cta' && (
|
{section.type === 'cta' && (
|
||||||
<div className="relative bg-gradient-to-br from-[#d4af37]/10 via-[#c9a227]/5 to-[#d4af37]/10 rounded-3xl border-2 border-[#d4af37]/30 p-8 md:p-12 text-center shadow-2xl">
|
<div className="relative bg-gradient-to-br from-[var(--luxury-gold)]/10 via-[var(--luxury-gold-dark)]/5 to-[var(--luxury-gold)]/10 rounded-3xl border-2 border-[var(--luxury-gold)]/30 p-8 md:p-12 text-center shadow-2xl">
|
||||||
{section.title && (
|
{section.title && (
|
||||||
<h3 className="text-3xl md:text-4xl font-serif font-bold text-white mb-4">
|
<h3 className="text-3xl md:text-4xl font-serif font-bold text-white mb-4">
|
||||||
{section.title}
|
{section.title}
|
||||||
@@ -637,7 +637,7 @@ const ServiceDetailPage: React.FC = () => {
|
|||||||
{section.cta_text && section.cta_link && (
|
{section.cta_text && section.cta_link && (
|
||||||
<a
|
<a
|
||||||
href={section.cta_link}
|
href={section.cta_link}
|
||||||
className="inline-flex items-center gap-3 px-8 py-4 bg-gradient-to-r from-[#d4af37] to-[#c9a227] text-[#0f0f0f] font-semibold rounded-xl hover:from-[#f5d76e] hover:to-[#d4af37] transition-all shadow-lg hover:shadow-xl hover:scale-105"
|
className="inline-flex items-center gap-3 px-8 py-4 bg-gradient-to-r from-[var(--luxury-gold)] to-[var(--luxury-gold-dark)] text-[#0f0f0f] font-semibold rounded-xl hover:from-[var(--luxury-gold-light)] hover:to-[var(--luxury-gold)] transition-all shadow-lg hover:shadow-xl hover:scale-105"
|
||||||
>
|
>
|
||||||
{section.cta_text}
|
{section.cta_text}
|
||||||
</a>
|
</a>
|
||||||
@@ -647,7 +647,7 @@ const ServiceDetailPage: React.FC = () => {
|
|||||||
|
|
||||||
{/* Video Section */}
|
{/* Video Section */}
|
||||||
{section.type === 'video' && section.video_url && (
|
{section.type === 'video' && section.video_url && (
|
||||||
<div className="rounded-3xl overflow-hidden border-2 border-[#d4af37]/20 shadow-2xl bg-black">
|
<div className="rounded-3xl overflow-hidden border-2 border-[var(--luxury-gold)]/20 shadow-2xl bg-black">
|
||||||
<div className="aspect-video">
|
<div className="aspect-video">
|
||||||
<iframe
|
<iframe
|
||||||
src={section.video_url.replace('watch?v=', 'embed/').replace('vimeo.com/', 'player.vimeo.com/video/')}
|
src={section.video_url.replace('watch?v=', 'embed/').replace('vimeo.com/', 'player.vimeo.com/video/')}
|
||||||
@@ -665,14 +665,14 @@ const ServiceDetailPage: React.FC = () => {
|
|||||||
|
|
||||||
{/* Related Services */}
|
{/* Related Services */}
|
||||||
{relatedServices.length > 0 && (
|
{relatedServices.length > 0 && (
|
||||||
<div className="mt-16 pt-12 border-t border-[#d4af37]/20">
|
<div className="mt-16 pt-12 border-t border-[var(--luxury-gold)]/20">
|
||||||
<h2 className="text-2xl sm:text-3xl font-bold text-white mb-8">Related Services</h2>
|
<h2 className="text-2xl sm:text-3xl font-bold text-white mb-8">Related Services</h2>
|
||||||
<div className="grid grid-cols-1 md:grid-cols-3 gap-6">
|
<div className="grid grid-cols-1 md:grid-cols-3 gap-6">
|
||||||
{relatedServices.map((relatedService) => (
|
{relatedServices.map((relatedService) => (
|
||||||
<Link
|
<Link
|
||||||
key={relatedService.id}
|
key={relatedService.id}
|
||||||
to={`/services/${relatedService.slug}`}
|
to={`/services/${relatedService.slug}`}
|
||||||
className="group bg-gradient-to-br from-[#1a1a1a] to-[#0f0f0f] rounded-xl border border-[#d4af37]/20 overflow-hidden hover:border-[#d4af37]/50 transition-all duration-300"
|
className="group bg-gradient-to-br from-[#1a1a1a] to-[#0f0f0f] rounded-xl border border-[var(--luxury-gold)]/20 overflow-hidden hover:border-[var(--luxury-gold)]/50 transition-all duration-300"
|
||||||
>
|
>
|
||||||
{relatedService.image && (
|
{relatedService.image && (
|
||||||
<div className="relative h-40 overflow-hidden">
|
<div className="relative h-40 overflow-hidden">
|
||||||
@@ -684,7 +684,7 @@ const ServiceDetailPage: React.FC = () => {
|
|||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
<div className="p-4">
|
<div className="p-4">
|
||||||
<h3 className="text-lg font-bold text-white mb-2 group-hover:text-[#d4af37] transition-colors line-clamp-2">
|
<h3 className="text-lg font-bold text-white mb-2 group-hover:text-[var(--luxury-gold)] transition-colors line-clamp-2">
|
||||||
{relatedService.title}
|
{relatedService.title}
|
||||||
</h3>
|
</h3>
|
||||||
{relatedService.description && (
|
{relatedService.description && (
|
||||||
|
|||||||
@@ -159,41 +159,41 @@ const ServicesPage: React.FC = () => {
|
|||||||
return (
|
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 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' }}>
|
||||||
{/* Hero Section */}
|
{/* Hero Section */}
|
||||||
<div className="w-full bg-gradient-to-br from-[#1a1a1a] via-[#0f0f0f] to-[#1a1a1a] border-b border-[#d4af37]/10 pt-6 sm:pt-7 md:pt-8 overflow-hidden relative">
|
<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">
|
||||||
{/* Background Effects */}
|
{/* Background Effects */}
|
||||||
<div className="absolute inset-0 opacity-10">
|
<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-[#d4af37] rounded-full blur-3xl"></div>
|
<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>
|
||||||
<div className="absolute bottom-10 right-10 w-40 sm:w-64 h-40 sm:h-64 bg-[#c9a227] rounded-full blur-3xl"></div>
|
<div className="absolute bottom-10 right-10 w-40 sm:w-64 h-40 sm:h-64 bg-[var(--luxury-gold-dark)] rounded-full blur-3xl"></div>
|
||||||
</div>
|
</div>
|
||||||
<div className="absolute top-0 left-0 w-full h-px bg-gradient-to-r from-transparent via-[#d4af37]/50 to-transparent"></div>
|
<div className="absolute top-0 left-0 w-full h-px bg-gradient-to-r from-transparent via-[var(--luxury-gold)]/50 to-transparent"></div>
|
||||||
|
|
||||||
<div className="w-full max-w-[1920px] mx-auto px-3 sm:px-4 md:px-6 lg:px-8 xl:px-12 2xl:px-16 3xl:px-20 py-4 sm:py-5 md:py-6 relative z-10">
|
<div className="w-full max-w-[1920px] mx-auto px-3 sm:px-4 md:px-6 lg:px-8 xl:px-12 2xl:px-16 3xl:px-20 py-4 sm:py-5 md:py-6 relative z-10">
|
||||||
<div className="max-w-2xl mx-auto text-center px-2">
|
<div className="max-w-2xl mx-auto text-center px-2">
|
||||||
<div className="flex justify-center mb-2 sm:mb-3 md:mb-4">
|
<div className="flex justify-center mb-2 sm:mb-3 md:mb-4">
|
||||||
<div className="relative group">
|
<div className="relative group">
|
||||||
<div className="absolute inset-0 bg-gradient-to-br from-[#d4af37] via-[#f5d76e] to-[#c9a227] rounded-xl blur-lg opacity-40 group-hover:opacity-60 transition-opacity duration-500"></div>
|
<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-[#d4af37]/40 backdrop-blur-sm shadow-xl shadow-[#d4af37]/20 group-hover:border-[#d4af37]/60 transition-all duration-300">
|
<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">
|
||||||
<Award className="w-5 h-5 sm:w-6 sm:h-6 md:w-7 md:h-7 text-[#d4af37] drop-shadow-lg" />
|
<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>
|
</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">
|
<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-[#d4af37] to-white bg-clip-text text-transparent">
|
<span className="bg-gradient-to-r from-white via-[var(--luxury-gold)] to-white bg-clip-text text-transparent">
|
||||||
{pageContent?.luxury_services_section_title || 'Our Services'}
|
{pageContent?.luxury_services_section_title || 'Our Services'}
|
||||||
</span>
|
</span>
|
||||||
</h1>
|
</h1>
|
||||||
<div className="w-12 sm:w-16 md:w-20 h-0.5 bg-gradient-to-r from-transparent via-[#d4af37] to-transparent mx-auto mb-2 sm:mb-3"></div>
|
<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 text-gray-300 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'}
|
{pageContent?.luxury_services_section_subtitle || 'Discover our premium services designed to enhance your stay'}
|
||||||
</p>
|
</p>
|
||||||
<div className="mt-4 flex items-center justify-center gap-2 text-[#d4af37]/60">
|
<div className="mt-4 flex items-center justify-center gap-2 text-[var(--luxury-gold)]/60">
|
||||||
<Sparkles className="w-4 h-4 animate-pulse" />
|
<Sparkles className="w-4 h-4 animate-pulse" />
|
||||||
<span className="text-xs sm:text-sm font-light tracking-wider uppercase">Premium Services</span>
|
<span className="text-xs sm:text-sm font-light tracking-wider uppercase">Premium Services</span>
|
||||||
<Sparkles className="w-4 h-4 animate-pulse" style={{ animationDelay: '0.5s' }} />
|
<Sparkles className="w-4 h-4 animate-pulse" style={{ animationDelay: '0.5s' }} />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="absolute bottom-0 left-0 w-full h-px bg-gradient-to-r from-transparent via-[#d4af37]/30 to-transparent"></div>
|
<div className="absolute bottom-0 left-0 w-full h-px bg-gradient-to-r from-transparent via-[var(--luxury-gold)]/30 to-transparent"></div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Main Content - Full Width */}
|
{/* Main Content - Full Width */}
|
||||||
@@ -203,15 +203,15 @@ const ServicesPage: React.FC = () => {
|
|||||||
<div className="mb-12 sm:mb-16">
|
<div className="mb-12 sm:mb-16">
|
||||||
<div className="flex flex-col lg:flex-row gap-6 mb-8">
|
<div className="flex flex-col lg:flex-row gap-6 mb-8">
|
||||||
<div className="flex-1 relative group">
|
<div className="flex-1 relative group">
|
||||||
<div className="absolute inset-0 bg-gradient-to-r from-[#d4af37]/10 to-transparent rounded-xl blur-xl opacity-0 group-hover:opacity-100 transition-opacity duration-500"></div>
|
<div className="absolute inset-0 bg-gradient-to-r from-[var(--luxury-gold)]/10 to-transparent rounded-xl blur-xl opacity-0 group-hover:opacity-100 transition-opacity duration-500"></div>
|
||||||
<div className="relative">
|
<div className="relative">
|
||||||
<Search className="absolute left-5 top-1/2 transform -translate-y-1/2 text-[#d4af37] w-5 h-5 z-10" />
|
<Search className="absolute left-5 top-1/2 transform -translate-y-1/2 text-[var(--luxury-gold)] w-5 h-5 z-10" />
|
||||||
<input
|
<input
|
||||||
type="text"
|
type="text"
|
||||||
placeholder="Search services..."
|
placeholder="Search services..."
|
||||||
value={searchTerm}
|
value={searchTerm}
|
||||||
onChange={(e) => setSearchTerm(e.target.value)}
|
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-[#d4af37]/20 rounded-xl text-white placeholder-gray-500 focus:outline-none focus:ring-2 focus:ring-[#d4af37]/50 focus:border-[#d4af37]/50 transition-all duration-300 backdrop-blur-sm font-light"
|
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"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -221,13 +221,13 @@ const ServicesPage: React.FC = () => {
|
|||||||
{/* Categories Filter - Top Center */}
|
{/* Categories Filter - Top Center */}
|
||||||
{allCategories.length > 0 && (
|
{allCategories.length > 0 && (
|
||||||
<div className="mb-12 flex justify-center">
|
<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-[#d4af37]/20 p-4 backdrop-blur-xl shadow-2xl">
|
<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">
|
||||||
<button
|
<button
|
||||||
onClick={() => setSelectedCategory(null)}
|
onClick={() => setSelectedCategory(null)}
|
||||||
className={`group relative px-6 py-3 rounded-xl text-sm font-medium transition-all duration-300 overflow-hidden ${
|
className={`group relative px-6 py-3 rounded-xl text-sm font-medium transition-all duration-300 overflow-hidden ${
|
||||||
selectedCategory === null
|
selectedCategory === null
|
||||||
? 'bg-gradient-to-r from-[#d4af37] to-[#c9a227] text-[#0f0f0f] shadow-lg shadow-[#d4af37]/40'
|
? '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-[#d4af37]/20 hover:border-[#d4af37]/50 hover:text-[#d4af37]'
|
: '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)]'
|
||||||
}`}
|
}`}
|
||||||
>
|
>
|
||||||
<span className="relative z-10 flex items-center gap-2">
|
<span className="relative z-10 flex items-center gap-2">
|
||||||
@@ -241,8 +241,8 @@ const ServicesPage: React.FC = () => {
|
|||||||
onClick={() => setSelectedCategory(category)}
|
onClick={() => setSelectedCategory(category)}
|
||||||
className={`group relative px-6 py-3 rounded-xl text-sm font-medium transition-all duration-300 overflow-hidden ${
|
className={`group relative px-6 py-3 rounded-xl text-sm font-medium transition-all duration-300 overflow-hidden ${
|
||||||
selectedCategory === category
|
selectedCategory === category
|
||||||
? 'bg-gradient-to-r from-[#d4af37] to-[#c9a227] text-[#0f0f0f] shadow-lg shadow-[#d4af37]/40'
|
? '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-[#d4af37]/20 hover:border-[#d4af37]/50 hover:text-[#d4af37]'
|
: '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)]'
|
||||||
}`}
|
}`}
|
||||||
>
|
>
|
||||||
<span className="relative z-10 flex items-center gap-2">
|
<span className="relative z-10 flex items-center gap-2">
|
||||||
@@ -267,8 +267,8 @@ const ServicesPage: React.FC = () => {
|
|||||||
{/* Services Grid - Luxury Design - Centered */}
|
{/* Services Grid - Luxury Design - Centered */}
|
||||||
{filteredServices.length === 0 ? (
|
{filteredServices.length === 0 ? (
|
||||||
<div className="text-center py-20">
|
<div className="text-center py-20">
|
||||||
<div className="inline-flex items-center justify-center w-20 h-20 rounded-full bg-[#d4af37]/10 mb-6">
|
<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-[#d4af37]" />
|
<Award className="w-10 h-10 text-[var(--luxury-gold)]" />
|
||||||
</div>
|
</div>
|
||||||
<p className="text-gray-400 text-xl font-light">No services found</p>
|
<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="text-gray-500 text-sm mt-2">Try adjusting your search or filters</p>
|
||||||
@@ -288,12 +288,12 @@ const ServicesPage: React.FC = () => {
|
|||||||
<Link
|
<Link
|
||||||
key={service.id}
|
key={service.id}
|
||||||
to={`/services/${serviceSlug}`}
|
to={`/services/${serviceSlug}`}
|
||||||
className="group relative bg-gradient-to-br from-[#1a1a1a] via-[#0f0f0f] to-[#0a0a0a] rounded-3xl border-2 border-[#d4af37]/20 overflow-hidden hover:border-[#d4af37]/60 transition-all duration-700 hover:shadow-2xl hover:shadow-[#d4af37]/30 hover:-translate-y-3 block"
|
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"
|
||||||
style={{ animationDelay: `${index * 50}ms` }}
|
style={{ animationDelay: `${index * 50}ms` }}
|
||||||
>
|
>
|
||||||
{/* Premium Glow Effects */}
|
{/* Premium Glow Effects */}
|
||||||
<div className="absolute inset-0 bg-gradient-to-br from-[#d4af37]/0 via-[#d4af37]/0 to-[#d4af37]/0 group-hover:from-[#d4af37]/10 group-hover:via-[#d4af37]/5 group-hover:to-[#d4af37]/10 transition-all duration-700 rounded-3xl blur-2xl opacity-0 group-hover:opacity-100"></div>
|
<div className="absolute inset-0 bg-gradient-to-br from-[var(--luxury-gold)]/0 via-[var(--luxury-gold)]/0 to-[var(--luxury-gold)]/0 group-hover:from-[var(--luxury-gold)]/10 group-hover:via-[var(--luxury-gold)]/5 group-hover:to-[var(--luxury-gold)]/10 transition-all duration-700 rounded-3xl blur-2xl opacity-0 group-hover:opacity-100"></div>
|
||||||
<div className="absolute top-0 left-0 w-full h-1 bg-gradient-to-r from-transparent via-[#d4af37]/50 to-transparent opacity-0 group-hover:opacity-100 transition-opacity duration-500"></div>
|
<div className="absolute top-0 left-0 w-full h-1 bg-gradient-to-r from-transparent via-[var(--luxury-gold)]/50 to-transparent opacity-0 group-hover:opacity-100 transition-opacity duration-500"></div>
|
||||||
|
|
||||||
{/* Content */}
|
{/* Content */}
|
||||||
<div className="relative z-10">
|
<div className="relative z-10">
|
||||||
@@ -308,8 +308,8 @@ const ServicesPage: React.FC = () => {
|
|||||||
{/* Premium Badge Overlay */}
|
{/* Premium Badge Overlay */}
|
||||||
{service.type === 'luxury' && (
|
{service.type === 'luxury' && (
|
||||||
<div className="absolute top-6 left-6 z-20">
|
<div className="absolute top-6 left-6 z-20">
|
||||||
<div className="bg-gradient-to-br from-[#d4af37]/20 to-[#c9a227]/10 backdrop-blur-md rounded-2xl px-4 py-2 border border-[#d4af37]/40 shadow-xl">
|
<div className="bg-gradient-to-br from-[var(--luxury-gold)]/20 to-[var(--luxury-gold-dark)]/10 backdrop-blur-md rounded-2xl px-4 py-2 border border-[var(--luxury-gold)]/40 shadow-xl">
|
||||||
<div className="flex items-center gap-2 text-[#d4af37]">
|
<div className="flex items-center gap-2 text-[var(--luxury-gold)]">
|
||||||
<Star className="w-4 h-4" />
|
<Star className="w-4 h-4" />
|
||||||
<span className="text-sm font-semibold">Premium</span>
|
<span className="text-sm font-semibold">Premium</span>
|
||||||
</div>
|
</div>
|
||||||
@@ -317,21 +317,21 @@ const ServicesPage: React.FC = () => {
|
|||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
{/* Luxury Corner Accent */}
|
{/* Luxury Corner Accent */}
|
||||||
<div className="absolute top-0 right-0 w-32 h-32 bg-gradient-to-br from-[#d4af37]/20 to-transparent rounded-bl-full opacity-0 group-hover:opacity-100 transition-opacity duration-500"></div>
|
<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>
|
||||||
) : (
|
) : (
|
||||||
<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 bg-gradient-to-br from-[#1a1a1a] to-[#0f0f0f] flex items-center justify-center p-8">
|
||||||
{service.icon && (LucideIcons as any)[service.icon] ? (
|
{service.icon && (LucideIcons as any)[service.icon] ? (
|
||||||
<div className="relative">
|
<div className="relative">
|
||||||
<div className="absolute inset-0 bg-[#d4af37]/20 rounded-full blur-2xl"></div>
|
<div className="absolute inset-0 bg-[var(--luxury-gold)]/20 rounded-full blur-2xl"></div>
|
||||||
{React.createElement((LucideIcons as any)[service.icon], {
|
{React.createElement((LucideIcons as any)[service.icon], {
|
||||||
className: 'w-16 h-16 sm:w-20 sm:h-20 text-[#d4af37] relative z-10 drop-shadow-lg'
|
className: 'w-16 h-16 sm:w-20 sm:h-20 text-[var(--luxury-gold)] relative z-10 drop-shadow-lg'
|
||||||
})}
|
})}
|
||||||
</div>
|
</div>
|
||||||
) : (
|
) : (
|
||||||
<div className="relative">
|
<div className="relative">
|
||||||
<div className="absolute inset-0 bg-[#d4af37]/20 rounded-full blur-2xl"></div>
|
<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-[#d4af37] relative z-10 drop-shadow-lg" />
|
<Award className="w-16 h-16 sm:w-20 sm:h-20 text-[var(--luxury-gold)] relative z-10 drop-shadow-lg" />
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
@@ -339,13 +339,13 @@ const ServicesPage: React.FC = () => {
|
|||||||
<div className="p-8">
|
<div className="p-8">
|
||||||
{service.category && (
|
{service.category && (
|
||||||
<div className="flex flex-wrap gap-2 mb-5">
|
<div className="flex flex-wrap gap-2 mb-5">
|
||||||
<span className="inline-flex items-center gap-1.5 px-4 py-1.5 bg-gradient-to-br from-[#d4af37]/15 to-[#d4af37]/5 text-[#d4af37] rounded-full text-xs font-semibold border border-[#d4af37]/30 backdrop-blur-sm shadow-lg">
|
<span className="inline-flex items-center gap-1.5 px-4 py-1.5 bg-gradient-to-br from-[var(--luxury-gold)]/15 to-[var(--luxury-gold)]/5 text-[var(--luxury-gold)] rounded-full text-xs font-semibold border border-[var(--luxury-gold)]/30 backdrop-blur-sm shadow-lg">
|
||||||
<Tag className="w-3 h-3" />
|
<Tag className="w-3 h-3" />
|
||||||
{service.category}
|
{service.category}
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
<h2 className="text-2xl sm:text-3xl font-serif font-bold text-white mb-4 group-hover:text-[#d4af37] transition-colors duration-500 line-clamp-2 leading-tight tracking-tight">
|
<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">
|
||||||
{service.title}
|
{service.title}
|
||||||
</h2>
|
</h2>
|
||||||
{service.description && (
|
{service.description && (
|
||||||
@@ -354,9 +354,9 @@ const ServicesPage: React.FC = () => {
|
|||||||
</p>
|
</p>
|
||||||
)}
|
)}
|
||||||
{service.price !== undefined && (
|
{service.price !== undefined && (
|
||||||
<div className="mb-6 pb-6 border-b border-[#d4af37]/20">
|
<div className="mb-6 pb-6 border-b border-[var(--luxury-gold)]/20">
|
||||||
<div className="flex items-baseline gap-2">
|
<div className="flex items-baseline gap-2">
|
||||||
<span className="text-2xl sm:text-3xl font-serif font-semibold bg-gradient-to-r from-[#d4af37] to-[#f5d76e] bg-clip-text text-transparent">
|
<span className="text-2xl sm:text-3xl font-serif font-semibold bg-gradient-to-r from-[var(--luxury-gold)] to-[var(--luxury-gold-light)] bg-clip-text text-transparent">
|
||||||
{formatCurrency(service.price)}
|
{formatCurrency(service.price)}
|
||||||
</span>
|
</span>
|
||||||
{service.unit && (
|
{service.unit && (
|
||||||
@@ -367,12 +367,12 @@ const ServicesPage: React.FC = () => {
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
<div className="flex items-center justify-between pt-4 border-t border-[#d4af37]/10">
|
<div className="flex items-center justify-between pt-4 border-t border-[var(--luxury-gold)]/10">
|
||||||
<div className="flex items-center gap-3 text-[#d4af37] group-hover:gap-4 transition-all duration-300">
|
<div className="flex items-center gap-3 text-[var(--luxury-gold)] group-hover:gap-4 transition-all duration-300">
|
||||||
<span className="text-sm font-semibold tracking-wide uppercase">Learn More</span>
|
<span className="text-sm font-semibold tracking-wide uppercase">Learn More</span>
|
||||||
<ArrowRight className="w-5 h-5 group-hover:translate-x-2 transition-transform duration-300" />
|
<ArrowRight className="w-5 h-5 group-hover:translate-x-2 transition-transform duration-300" />
|
||||||
</div>
|
</div>
|
||||||
<div className="w-12 h-0.5 bg-gradient-to-r from-[#d4af37] to-transparent group-hover:w-20 transition-all duration-500"></div>
|
<div className="w-12 h-0.5 bg-gradient-to-r from-[var(--luxury-gold)] to-transparent group-hover:w-20 transition-all duration-500"></div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -99,12 +99,12 @@ const TermsPage: React.FC = () => {
|
|||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<div className="text-center">
|
<div className="text-center">
|
||||||
<Scale className="w-16 h-16 text-[#d4af37]/50 mx-auto mb-4" />
|
<Scale className="w-16 h-16 text-[var(--luxury-gold)]/50 mx-auto mb-4" />
|
||||||
<h1 className="text-2xl font-elegant font-bold text-white mb-2">Terms & Conditions</h1>
|
<h1 className="text-2xl font-elegant font-bold text-white mb-2">Terms & Conditions</h1>
|
||||||
<p className="text-gray-400">This page is currently unavailable.</p>
|
<p className="text-gray-400">This page is currently unavailable.</p>
|
||||||
<Link
|
<Link
|
||||||
to="/"
|
to="/"
|
||||||
className="inline-flex items-center gap-2 text-[#d4af37]/80 hover:text-[#d4af37] mt-6 transition-all duration-300"
|
className="inline-flex items-center gap-2 text-[var(--luxury-gold)]/80 hover:text-[var(--luxury-gold)] mt-6 transition-all duration-300"
|
||||||
>
|
>
|
||||||
<ArrowLeft className="w-4 h-4" />
|
<ArrowLeft className="w-4 h-4" />
|
||||||
<span>Back to Home</span>
|
<span>Back to Home</span>
|
||||||
@@ -129,7 +129,7 @@ const TermsPage: React.FC = () => {
|
|||||||
{/* Back Link */}
|
{/* Back Link */}
|
||||||
<Link
|
<Link
|
||||||
to="/"
|
to="/"
|
||||||
className="inline-flex items-center gap-2 text-[#d4af37]/80 hover:text-[#d4af37] mb-6 transition-all duration-300 group font-light tracking-wide text-xs sm:text-sm"
|
className="inline-flex items-center gap-2 text-[var(--luxury-gold)]/80 hover:text-[var(--luxury-gold)] mb-6 transition-all duration-300 group font-light tracking-wide text-xs sm:text-sm"
|
||||||
>
|
>
|
||||||
<ArrowLeft className="w-4 h-4 group-hover:-translate-x-1 transition-transform" />
|
<ArrowLeft className="w-4 h-4 group-hover:-translate-x-1 transition-transform" />
|
||||||
<span>Back to Home</span>
|
<span>Back to Home</span>
|
||||||
@@ -137,10 +137,10 @@ const TermsPage: React.FC = () => {
|
|||||||
|
|
||||||
{/* Header */}
|
{/* Header */}
|
||||||
<div className="mb-8 sm:mb-12 text-center">
|
<div className="mb-8 sm:mb-12 text-center">
|
||||||
<div className="inline-flex items-center justify-center w-16 h-16 sm:w-20 sm:h-20 mb-4 sm:mb-6 bg-gradient-to-br from-[#d4af37]/20 to-[#c9a227]/10 rounded-full border border-[#d4af37]/30">
|
<div className="inline-flex items-center justify-center w-16 h-16 sm:w-20 sm:h-20 mb-4 sm:mb-6 bg-gradient-to-br from-[var(--luxury-gold)]/20 to-[var(--luxury-gold-dark)]/10 rounded-full border border-[var(--luxury-gold)]/30">
|
||||||
<Scale className="w-8 h-8 sm:w-10 sm:h-10 text-[#d4af37]" />
|
<Scale className="w-8 h-8 sm:w-10 sm:h-10 text-[var(--luxury-gold)]" />
|
||||||
</div>
|
</div>
|
||||||
<h1 className="text-3xl sm:text-4xl lg:text-5xl font-elegant font-bold text-white mb-3 sm:mb-4 tracking-tight leading-tight bg-gradient-to-r from-white via-[#d4af37] to-white bg-clip-text text-transparent">
|
<h1 className="text-3xl sm:text-4xl lg:text-5xl font-elegant font-bold text-white mb-3 sm:mb-4 tracking-tight leading-tight bg-gradient-to-r from-white via-[var(--luxury-gold)] to-white bg-clip-text text-transparent">
|
||||||
{pageContent.title || 'Terms & Conditions'}
|
{pageContent.title || 'Terms & Conditions'}
|
||||||
</h1>
|
</h1>
|
||||||
{pageContent.subtitle && (
|
{pageContent.subtitle && (
|
||||||
@@ -151,18 +151,18 @@ const TermsPage: React.FC = () => {
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Content */}
|
{/* Content */}
|
||||||
<div className="bg-gradient-to-br from-[#1a1a1a] to-[#0a0a0a] rounded-xl border border-[#d4af37]/20 backdrop-blur-xl shadow-2xl shadow-[#d4af37]/5 p-6 sm:p-8 lg:p-12">
|
<div className="bg-gradient-to-br from-[#1a1a1a] to-[#0a0a0a] rounded-xl border border-[var(--luxury-gold)]/20 backdrop-blur-xl shadow-2xl shadow-[var(--luxury-gold)]/5 p-6 sm:p-8 lg:p-12">
|
||||||
<div
|
<div
|
||||||
className="prose prose-invert prose-lg max-w-none text-gray-300
|
className="prose prose-invert prose-lg max-w-none text-gray-300
|
||||||
prose-headings:text-white prose-headings:font-elegant prose-headings:font-semibold
|
prose-headings:text-white prose-headings:font-elegant prose-headings:font-semibold
|
||||||
prose-h2:text-2xl prose-h2:mt-8 prose-h2:mb-4 prose-h2:border-b prose-h2:border-[#d4af37]/20 prose-h2:pb-2
|
prose-h2:text-2xl prose-h2:mt-8 prose-h2:mb-4 prose-h2:border-b prose-h2:border-[var(--luxury-gold)]/20 prose-h2:pb-2
|
||||||
prose-p:text-gray-300 prose-p:font-light prose-p:leading-relaxed prose-p:mb-4
|
prose-p:text-gray-300 prose-p:font-light prose-p:leading-relaxed prose-p:mb-4
|
||||||
prose-ul:text-gray-300 prose-ul:font-light prose-ul:my-4
|
prose-ul:text-gray-300 prose-ul:font-light prose-ul:my-4
|
||||||
prose-li:text-gray-300 prose-li:mb-2 prose-li:ml-4
|
prose-li:text-gray-300 prose-li:mb-2 prose-li:ml-4
|
||||||
prose-strong:text-[#d4af37] prose-strong:font-medium
|
prose-strong:text-[var(--luxury-gold)] prose-strong:font-medium
|
||||||
prose-a:text-[#d4af37] prose-a:no-underline hover:prose-a:underline
|
prose-a:text-[var(--luxury-gold)] prose-a:no-underline hover:prose-a:underline
|
||||||
[&_*]:text-gray-300 [&_h1]:text-white [&_h2]:text-white [&_h3]:text-white [&_h4]:text-white [&_h5]:text-white [&_h6]:text-white
|
[&_*]:text-gray-300 [&_h1]:text-white [&_h2]:text-white [&_h3]:text-white [&_h4]:text-white [&_h5]:text-white [&_h6]:text-white
|
||||||
[&_strong]:text-[#d4af37] [&_b]:text-[#d4af37] [&_a]:text-[#d4af37]"
|
[&_strong]:text-[var(--luxury-gold)] [&_b]:text-[var(--luxury-gold)] [&_a]:text-[var(--luxury-gold)]"
|
||||||
style={{ color: '#d1d5db' }}
|
style={{ color: '#d1d5db' }}
|
||||||
dangerouslySetInnerHTML={createSanitizedHtml(
|
dangerouslySetInnerHTML={createSanitizedHtml(
|
||||||
pageContent.content || pageContent.description || '<p style="color: #d1d5db;">No content available.</p>'
|
pageContent.content || pageContent.description || '<p style="color: #d1d5db;">No content available.</p>'
|
||||||
@@ -175,7 +175,7 @@ const TermsPage: React.FC = () => {
|
|||||||
<div className="mt-8 text-center">
|
<div className="mt-8 text-center">
|
||||||
<p className="text-sm text-gray-400 font-light">
|
<p className="text-sm text-gray-400 font-light">
|
||||||
For questions about these terms, contact us at{' '}
|
For questions about these terms, contact us at{' '}
|
||||||
<a href={`mailto:${settings.company_email}`} className="text-[#d4af37] hover:underline">
|
<a href={`mailto:${settings.company_email}`} className="text-[var(--luxury-gold)] hover:underline">
|
||||||
{settings.company_email}
|
{settings.company_email}
|
||||||
</a>
|
</a>
|
||||||
</p>
|
</p>
|
||||||
|
|||||||
@@ -368,10 +368,10 @@ const ChatWidget: React.FC<ChatWidgetProps> = ({ onClose }) => {
|
|||||||
{!isOpen ? (
|
{!isOpen ? (
|
||||||
<button
|
<button
|
||||||
onClick={handleOpen}
|
onClick={handleOpen}
|
||||||
className="bg-gradient-to-r from-[#d4af37] to-[#c9a227] text-slate-900 p-5 rounded-full shadow-2xl shadow-[#d4af37]/40 hover:from-[#c9a227] hover:to-[#d4af37] transition-all duration-300 hover:scale-110 flex items-center justify-center group relative"
|
className="bg-gradient-to-r from-[var(--luxury-gold)] to-[var(--luxury-gold-dark)] text-slate-900 p-5 rounded-full shadow-2xl shadow-[var(--luxury-gold)]/40 hover:from-[var(--luxury-gold-dark)] hover:to-[var(--luxury-gold)] transition-all duration-300 hover:scale-110 flex items-center justify-center group relative"
|
||||||
aria-label="Open chat"
|
aria-label="Open chat"
|
||||||
>
|
>
|
||||||
<div className="absolute inset-0 bg-gradient-to-r from-[#d4af37] to-[#c9a227] rounded-full blur-xl opacity-50 group-hover:opacity-70 transition-opacity"></div>
|
<div className="absolute inset-0 bg-gradient-to-r from-[var(--luxury-gold)] to-[var(--luxury-gold-dark)] rounded-full blur-xl opacity-50 group-hover:opacity-70 transition-opacity"></div>
|
||||||
<MessageCircle className="w-6 h-6 group-hover:scale-110 transition-transform relative z-10" strokeWidth={2.5} />
|
<MessageCircle className="w-6 h-6 group-hover:scale-110 transition-transform relative z-10" strokeWidth={2.5} />
|
||||||
{chat && chat.status === 'pending' && (
|
{chat && chat.status === 'pending' && (
|
||||||
<span className="absolute -top-1 -right-1 w-4 h-4 bg-red-500 rounded-full animate-pulse border-2 border-white shadow-lg z-10"></span>
|
<span className="absolute -top-1 -right-1 w-4 h-4 bg-red-500 rounded-full animate-pulse border-2 border-white shadow-lg z-10"></span>
|
||||||
@@ -379,14 +379,14 @@ const ChatWidget: React.FC<ChatWidgetProps> = ({ onClose }) => {
|
|||||||
</button>
|
</button>
|
||||||
) : (
|
) : (
|
||||||
<div
|
<div
|
||||||
className={`luxury-glass rounded-2xl shadow-2xl border border-[#d4af37]/30 flex flex-col transition-all duration-300 ${
|
className={`luxury-glass rounded-2xl shadow-2xl border border-[var(--luxury-gold)]/30 flex flex-col transition-all duration-300 ${
|
||||||
isMinimized ? 'w-80 h-16' : 'w-96 h-[600px]'
|
isMinimized ? 'w-80 h-16' : 'w-96 h-[600px]'
|
||||||
}`}
|
}`}
|
||||||
style={{ transformOrigin: 'bottom right' }}
|
style={{ transformOrigin: 'bottom right' }}
|
||||||
>
|
>
|
||||||
{}
|
{}
|
||||||
<div className="bg-gradient-to-r from-[#d4af37] to-[#c9a227] text-slate-900 p-4 rounded-t-2xl flex items-center justify-between relative overflow-hidden">
|
<div className="bg-gradient-to-r from-[var(--luxury-gold)] to-[var(--luxury-gold-dark)] text-slate-900 p-4 rounded-t-2xl flex items-center justify-between relative overflow-hidden">
|
||||||
<div className="absolute inset-0 bg-gradient-to-r from-[#d4af37]/20 to-[#c9a227]/20"></div>
|
<div className="absolute inset-0 bg-gradient-to-r from-[var(--luxury-gold)]/20 to-[var(--luxury-gold-dark)]/20"></div>
|
||||||
<div className="absolute top-0 left-0 right-0 h-0.5 bg-gradient-to-r from-transparent via-slate-900/20 to-transparent"></div>
|
<div className="absolute top-0 left-0 right-0 h-0.5 bg-gradient-to-r from-transparent via-slate-900/20 to-transparent"></div>
|
||||||
<div className="flex items-center gap-3 relative z-10">
|
<div className="flex items-center gap-3 relative z-10">
|
||||||
<div className="bg-slate-900/10 p-2 rounded-lg backdrop-blur-sm">
|
<div className="bg-slate-900/10 p-2 rounded-lg backdrop-blur-sm">
|
||||||
@@ -453,9 +453,9 @@ const ChatWidget: React.FC<ChatWidgetProps> = ({ onClose }) => {
|
|||||||
{!isWithinBusinessHours ? (
|
{!isWithinBusinessHours ? (
|
||||||
<div className="flex-1 overflow-y-auto p-4 bg-gradient-to-b from-slate-50/50 to-white">
|
<div className="flex-1 overflow-y-auto p-4 bg-gradient-to-b from-slate-50/50 to-white">
|
||||||
<div className="max-w-md mx-auto">
|
<div className="max-w-md mx-auto">
|
||||||
<div className="mb-6 p-4 bg-gradient-to-r from-[#d4af37]/10 to-[#c9a227]/10 rounded-xl border border-[#d4af37]/20">
|
<div className="mb-6 p-4 bg-gradient-to-r from-[var(--luxury-gold)]/10 to-[var(--luxury-gold-dark)]/10 rounded-xl border border-[var(--luxury-gold)]/20">
|
||||||
<div className="flex items-start gap-3">
|
<div className="flex items-start gap-3">
|
||||||
<Clock className="w-5 h-5 text-[#d4af37] mt-0.5 flex-shrink-0" />
|
<Clock className="w-5 h-5 text-[var(--luxury-gold)] mt-0.5 flex-shrink-0" />
|
||||||
<div>
|
<div>
|
||||||
<p className="text-sm font-semibold text-slate-900 mb-1">Chat Hours</p>
|
<p className="text-sm font-semibold text-slate-900 mb-1">Chat Hours</p>
|
||||||
<p className="text-xs text-slate-600 font-light">
|
<p className="text-xs text-slate-600 font-light">
|
||||||
@@ -474,7 +474,7 @@ const ChatWidget: React.FC<ChatWidgetProps> = ({ onClose }) => {
|
|||||||
type="email"
|
type="email"
|
||||||
value={inquiryEmail}
|
value={inquiryEmail}
|
||||||
onChange={(e) => setInquiryEmail(e.target.value)}
|
onChange={(e) => setInquiryEmail(e.target.value)}
|
||||||
className="w-full px-4 py-2.5 border-2 border-slate-200 rounded-xl focus:outline-none focus:ring-2 focus:ring-[#d4af37]/30 focus:border-[#d4af37] transition-all duration-200 font-light"
|
className="w-full px-4 py-2.5 border-2 border-slate-200 rounded-xl focus:outline-none focus:ring-2 focus:ring-[var(--luxury-gold)]/30 focus:border-[var(--luxury-gold)] transition-all duration-200 font-light"
|
||||||
placeholder="your.email@example.com"
|
placeholder="your.email@example.com"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
@@ -486,14 +486,14 @@ const ChatWidget: React.FC<ChatWidgetProps> = ({ onClose }) => {
|
|||||||
value={inquiry}
|
value={inquiry}
|
||||||
onChange={(e) => setInquiry(e.target.value)}
|
onChange={(e) => setInquiry(e.target.value)}
|
||||||
rows={6}
|
rows={6}
|
||||||
className="w-full px-4 py-2.5 border-2 border-slate-200 rounded-xl focus:outline-none focus:ring-2 focus:ring-[#d4af37]/30 focus:border-[#d4af37] transition-all duration-200 font-light resize-none"
|
className="w-full px-4 py-2.5 border-2 border-slate-200 rounded-xl focus:outline-none focus:ring-2 focus:ring-[var(--luxury-gold)]/30 focus:border-[var(--luxury-gold)] transition-all duration-200 font-light resize-none"
|
||||||
placeholder="Please describe your inquiry or question..."
|
placeholder="Please describe your inquiry or question..."
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<button
|
<button
|
||||||
onClick={handleSubmitInquiry}
|
onClick={handleSubmitInquiry}
|
||||||
disabled={submittingInquiry || !inquiryEmail.trim() || !inquiry.trim()}
|
disabled={submittingInquiry || !inquiryEmail.trim() || !inquiry.trim()}
|
||||||
className="w-full bg-gradient-to-r from-[#d4af37] to-[#c9a227] text-slate-900 px-4 py-3 rounded-xl hover:from-[#c9a227] hover:to-[#d4af37] disabled:bg-gray-300 disabled:text-gray-500 disabled:cursor-not-allowed transition-all duration-300 font-semibold shadow-lg shadow-[#d4af37]/30 hover:shadow-xl disabled:shadow-none"
|
className="w-full bg-gradient-to-r from-[var(--luxury-gold)] to-[var(--luxury-gold-dark)] text-slate-900 px-4 py-3 rounded-xl hover:from-[var(--luxury-gold-dark)] hover:to-[var(--luxury-gold)] disabled:bg-gray-300 disabled:text-gray-500 disabled:cursor-not-allowed transition-all duration-300 font-semibold shadow-lg shadow-[var(--luxury-gold)]/30 hover:shadow-xl disabled:shadow-none"
|
||||||
>
|
>
|
||||||
{submittingInquiry ? 'Sending...' : 'Send Inquiry'}
|
{submittingInquiry ? 'Sending...' : 'Send Inquiry'}
|
||||||
</button>
|
</button>
|
||||||
@@ -521,8 +521,8 @@ const ChatWidget: React.FC<ChatWidgetProps> = ({ onClose }) => {
|
|||||||
setVisitorInfo({ ...visitorInfo, name: e.target.value });
|
setVisitorInfo({ ...visitorInfo, name: e.target.value });
|
||||||
if (formErrors.name) setFormErrors({ ...formErrors, name: '' });
|
if (formErrors.name) setFormErrors({ ...formErrors, name: '' });
|
||||||
}}
|
}}
|
||||||
className={`w-full px-4 py-2.5 border-2 rounded-xl focus:outline-none focus:ring-2 focus:ring-[#d4af37]/30 transition-all duration-200 font-light ${
|
className={`w-full px-4 py-2.5 border-2 rounded-xl focus:outline-none focus:ring-2 focus:ring-[var(--luxury-gold)]/30 transition-all duration-200 font-light ${
|
||||||
formErrors.name ? 'border-red-500 focus:border-red-500' : 'border-slate-200 focus:border-[#d4af37]'
|
formErrors.name ? 'border-red-500 focus:border-red-500' : 'border-slate-200 focus:border-[var(--luxury-gold)]'
|
||||||
}`}
|
}`}
|
||||||
placeholder="Your full name"
|
placeholder="Your full name"
|
||||||
/>
|
/>
|
||||||
@@ -541,8 +541,8 @@ const ChatWidget: React.FC<ChatWidgetProps> = ({ onClose }) => {
|
|||||||
setVisitorInfo({ ...visitorInfo, email: e.target.value });
|
setVisitorInfo({ ...visitorInfo, email: e.target.value });
|
||||||
if (formErrors.email) setFormErrors({ ...formErrors, email: '' });
|
if (formErrors.email) setFormErrors({ ...formErrors, email: '' });
|
||||||
}}
|
}}
|
||||||
className={`w-full px-4 py-2.5 border-2 rounded-xl focus:outline-none focus:ring-2 focus:ring-[#d4af37]/30 transition-all duration-200 font-light ${
|
className={`w-full px-4 py-2.5 border-2 rounded-xl focus:outline-none focus:ring-2 focus:ring-[var(--luxury-gold)]/30 transition-all duration-200 font-light ${
|
||||||
formErrors.email ? 'border-red-500 focus:border-red-500' : 'border-slate-200 focus:border-[#d4af37]'
|
formErrors.email ? 'border-red-500 focus:border-red-500' : 'border-slate-200 focus:border-[var(--luxury-gold)]'
|
||||||
}`}
|
}`}
|
||||||
placeholder="your.email@example.com"
|
placeholder="your.email@example.com"
|
||||||
/>
|
/>
|
||||||
@@ -561,8 +561,8 @@ const ChatWidget: React.FC<ChatWidgetProps> = ({ onClose }) => {
|
|||||||
setVisitorInfo({ ...visitorInfo, phone: e.target.value });
|
setVisitorInfo({ ...visitorInfo, phone: e.target.value });
|
||||||
if (formErrors.phone) setFormErrors({ ...formErrors, phone: '' });
|
if (formErrors.phone) setFormErrors({ ...formErrors, phone: '' });
|
||||||
}}
|
}}
|
||||||
className={`w-full px-4 py-2.5 border-2 rounded-xl focus:outline-none focus:ring-2 focus:ring-[#d4af37]/30 transition-all duration-200 font-light ${
|
className={`w-full px-4 py-2.5 border-2 rounded-xl focus:outline-none focus:ring-2 focus:ring-[var(--luxury-gold)]/30 transition-all duration-200 font-light ${
|
||||||
formErrors.phone ? 'border-red-500 focus:border-red-500' : 'border-slate-200 focus:border-[#d4af37]'
|
formErrors.phone ? 'border-red-500 focus:border-red-500' : 'border-slate-200 focus:border-[var(--luxury-gold)]'
|
||||||
}`}
|
}`}
|
||||||
placeholder="+1 (555) 123-4567"
|
placeholder="+1 (555) 123-4567"
|
||||||
/>
|
/>
|
||||||
@@ -573,7 +573,7 @@ const ChatWidget: React.FC<ChatWidgetProps> = ({ onClose }) => {
|
|||||||
<button
|
<button
|
||||||
onClick={createChat}
|
onClick={createChat}
|
||||||
disabled={loading}
|
disabled={loading}
|
||||||
className="w-full bg-gradient-to-r from-[#d4af37] to-[#c9a227] text-slate-900 px-4 py-3 rounded-xl hover:from-[#c9a227] hover:to-[#d4af37] disabled:bg-gray-300 disabled:text-gray-500 disabled:cursor-not-allowed transition-all duration-300 font-semibold shadow-lg shadow-[#d4af37]/30 hover:shadow-xl disabled:shadow-none"
|
className="w-full bg-gradient-to-r from-[var(--luxury-gold)] to-[var(--luxury-gold-dark)] text-slate-900 px-4 py-3 rounded-xl hover:from-[var(--luxury-gold-dark)] hover:to-[var(--luxury-gold)] disabled:bg-gray-300 disabled:text-gray-500 disabled:cursor-not-allowed transition-all duration-300 font-semibold shadow-lg shadow-[var(--luxury-gold)]/30 hover:shadow-xl disabled:shadow-none"
|
||||||
>
|
>
|
||||||
{loading ? 'Starting chat...' : 'Start Chat'}
|
{loading ? 'Starting chat...' : 'Start Chat'}
|
||||||
</button>
|
</button>
|
||||||
@@ -617,7 +617,7 @@ const ChatWidget: React.FC<ChatWidgetProps> = ({ onClose }) => {
|
|||||||
}
|
}
|
||||||
}}
|
}}
|
||||||
disabled={loading}
|
disabled={loading}
|
||||||
className="bg-gradient-to-r from-[#d4af37] to-[#c9a227] text-slate-900 px-8 py-3 rounded-xl hover:from-[#c9a227] hover:to-[#d4af37] transition-all duration-300 font-semibold shadow-lg shadow-[#d4af37]/30 hover:shadow-xl disabled:opacity-50 disabled:cursor-not-allowed"
|
className="bg-gradient-to-r from-[var(--luxury-gold)] to-[var(--luxury-gold-dark)] text-slate-900 px-8 py-3 rounded-xl hover:from-[var(--luxury-gold-dark)] hover:to-[var(--luxury-gold)] transition-all duration-300 font-semibold shadow-lg shadow-[var(--luxury-gold)]/30 hover:shadow-xl disabled:opacity-50 disabled:cursor-not-allowed"
|
||||||
>
|
>
|
||||||
{loading ? 'Starting...' : 'Start New Chat'}
|
{loading ? 'Starting...' : 'Start New Chat'}
|
||||||
</button>
|
</button>
|
||||||
@@ -637,12 +637,12 @@ const ChatWidget: React.FC<ChatWidgetProps> = ({ onClose }) => {
|
|||||||
<div
|
<div
|
||||||
className={`max-w-[80%] rounded-xl p-4 shadow-lg ${
|
className={`max-w-[80%] rounded-xl p-4 shadow-lg ${
|
||||||
message.sender_type === 'visitor'
|
message.sender_type === 'visitor'
|
||||||
? 'bg-gradient-to-br from-[#d4af37] to-[#c9a227] text-slate-900 border border-[#d4af37]/30'
|
? 'bg-gradient-to-br from-[var(--luxury-gold)] to-[var(--luxury-gold-dark)] text-slate-900 border border-[var(--luxury-gold)]/30'
|
||||||
: 'bg-white text-slate-800 border border-slate-200/60 shadow-sm'
|
: 'bg-white text-slate-800 border border-slate-200/60 shadow-sm'
|
||||||
}`}
|
}`}
|
||||||
>
|
>
|
||||||
{message.sender_type === 'staff' && (
|
{message.sender_type === 'staff' && (
|
||||||
<div className="text-xs font-semibold mb-1.5 text-[#d4af37] tracking-wide">
|
<div className="text-xs font-semibold mb-1.5 text-[var(--luxury-gold)] tracking-wide">
|
||||||
{message.sender_name || 'Staff'}
|
{message.sender_name || 'Staff'}
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
@@ -671,7 +671,7 @@ const ChatWidget: React.FC<ChatWidgetProps> = ({ onClose }) => {
|
|||||||
|
|
||||||
{}
|
{}
|
||||||
{!showVisitorForm && chat && chat.status !== 'closed' && (
|
{!showVisitorForm && chat && chat.status !== 'closed' && (
|
||||||
<div className="p-4 border-t border-[#d4af37]/20 bg-white/90 backdrop-blur-sm rounded-b-2xl">
|
<div className="p-4 border-t border-[var(--luxury-gold)]/20 bg-white/90 backdrop-blur-sm rounded-b-2xl">
|
||||||
<div className="flex gap-3">
|
<div className="flex gap-3">
|
||||||
<input
|
<input
|
||||||
type="text"
|
type="text"
|
||||||
@@ -679,12 +679,12 @@ const ChatWidget: React.FC<ChatWidgetProps> = ({ onClose }) => {
|
|||||||
onChange={(e) => setNewMessage(e.target.value)}
|
onChange={(e) => setNewMessage(e.target.value)}
|
||||||
onKeyPress={handleKeyPress}
|
onKeyPress={handleKeyPress}
|
||||||
placeholder="Type your message..."
|
placeholder="Type your message..."
|
||||||
className="flex-1 px-4 py-3 border-2 border-slate-200 rounded-xl focus:outline-none focus:ring-2 focus:ring-[#d4af37]/30 focus:border-[#d4af37] transition-all duration-200 font-light"
|
className="flex-1 px-4 py-3 border-2 border-slate-200 rounded-xl focus:outline-none focus:ring-2 focus:ring-[var(--luxury-gold)]/30 focus:border-[var(--luxury-gold)] transition-all duration-200 font-light"
|
||||||
/>
|
/>
|
||||||
<button
|
<button
|
||||||
onClick={handleSend}
|
onClick={handleSend}
|
||||||
disabled={!newMessage.trim()}
|
disabled={!newMessage.trim()}
|
||||||
className="bg-gradient-to-r from-[#d4af37] to-[#c9a227] text-slate-900 px-5 py-3 rounded-xl hover:from-[#c9a227] hover:to-[#d4af37] disabled:bg-gray-300 disabled:text-gray-500 disabled:cursor-not-allowed transition-all duration-300 flex items-center gap-2 font-semibold shadow-lg shadow-[#d4af37]/30 hover:shadow-xl disabled:shadow-none"
|
className="bg-gradient-to-r from-[var(--luxury-gold)] to-[var(--luxury-gold-dark)] text-slate-900 px-5 py-3 rounded-xl hover:from-[var(--luxury-gold-dark)] hover:to-[var(--luxury-gold)] disabled:bg-gray-300 disabled:text-gray-500 disabled:cursor-not-allowed transition-all duration-300 flex items-center gap-2 font-semibold shadow-lg shadow-[var(--luxury-gold)]/30 hover:shadow-xl disabled:shadow-none"
|
||||||
>
|
>
|
||||||
<Send className="w-4 h-4" />
|
<Send className="w-4 h-4" />
|
||||||
</button>
|
</button>
|
||||||
|
|||||||
@@ -112,16 +112,16 @@ const BoricaPaymentModal: React.FC<BoricaPaymentModalProps> = ({
|
|||||||
className="fixed inset-0 bg-black/90 backdrop-blur-sm"
|
className="fixed inset-0 bg-black/90 backdrop-blur-sm"
|
||||||
onClick={onClose}
|
onClick={onClose}
|
||||||
/>
|
/>
|
||||||
<div className="relative w-full max-w-md max-h-[95vh] bg-gradient-to-br from-[#0a0a0a] via-[#1a1a1a] to-[#0a0a0a] rounded-xl sm:rounded-2xl border border-[#d4af37]/30 shadow-2xl shadow-[#d4af37]/20 overflow-hidden flex flex-col">
|
<div className="relative w-full max-w-md max-h-[95vh] bg-gradient-to-br from-[#0a0a0a] via-[#1a1a1a] to-[#0a0a0a] rounded-xl sm:rounded-2xl border border-[var(--luxury-gold)]/30 shadow-2xl shadow-[var(--luxury-gold)]/20 overflow-hidden flex flex-col">
|
||||||
{/* Header */}
|
{/* Header */}
|
||||||
<div className="px-3 sm:px-4 md:px-6 py-3 sm:py-4 border-b border-[#d4af37]/20 bg-gradient-to-r from-[#1a1a1a] to-[#0a0a0a] flex-shrink-0">
|
<div className="px-3 sm:px-4 md:px-6 py-3 sm:py-4 border-b border-[var(--luxury-gold)]/20 bg-gradient-to-r from-[#1a1a1a] to-[#0a0a0a] flex-shrink-0">
|
||||||
<div className="flex items-center justify-between gap-2">
|
<div className="flex items-center justify-between gap-2">
|
||||||
<h2 className="text-base sm:text-lg md:text-xl font-serif font-bold text-white tracking-tight truncate flex-1">
|
<h2 className="text-base sm:text-lg md:text-xl font-serif font-bold text-white tracking-tight truncate flex-1">
|
||||||
Borica Payment
|
Borica Payment
|
||||||
</h2>
|
</h2>
|
||||||
<button
|
<button
|
||||||
onClick={onClose}
|
onClick={onClose}
|
||||||
className="p-1.5 sm:p-2 hover:bg-[#d4af37]/10 rounded-lg transition-colors text-gray-400 hover:text-white flex-shrink-0"
|
className="p-1.5 sm:p-2 hover:bg-[var(--luxury-gold)]/10 rounded-lg transition-colors text-gray-400 hover:text-white flex-shrink-0"
|
||||||
>
|
>
|
||||||
<X className="w-4 h-4 sm:w-5 sm:h-5" />
|
<X className="w-4 h-4 sm:w-5 sm:h-5" />
|
||||||
</button>
|
</button>
|
||||||
@@ -132,7 +132,7 @@ const BoricaPaymentModal: React.FC<BoricaPaymentModalProps> = ({
|
|||||||
<div className="flex-1 overflow-y-auto p-3 sm:p-4 md:p-6">
|
<div className="flex-1 overflow-y-auto p-3 sm:p-4 md:p-6">
|
||||||
{loading ? (
|
{loading ? (
|
||||||
<div className="flex flex-col items-center justify-center py-8 sm:py-12">
|
<div className="flex flex-col items-center justify-center py-8 sm:py-12">
|
||||||
<Loader2 className="w-10 h-10 sm:w-12 sm:h-12 animate-spin text-[#d4af37] mb-3 sm:mb-4" />
|
<Loader2 className="w-10 h-10 sm:w-12 sm:h-12 animate-spin text-[var(--luxury-gold)] mb-3 sm:mb-4" />
|
||||||
<p className="text-xs sm:text-sm text-gray-400">Initializing Borica payment...</p>
|
<p className="text-xs sm:text-sm text-gray-400">Initializing Borica payment...</p>
|
||||||
</div>
|
</div>
|
||||||
) : error ? (
|
) : error ? (
|
||||||
@@ -153,14 +153,14 @@ const BoricaPaymentModal: React.FC<BoricaPaymentModalProps> = ({
|
|||||||
<div className="space-y-4 sm:space-y-6">
|
<div className="space-y-4 sm:space-y-6">
|
||||||
<div className="text-center">
|
<div className="text-center">
|
||||||
<div className="mb-3 sm:mb-4 flex justify-center">
|
<div className="mb-3 sm:mb-4 flex justify-center">
|
||||||
<CreditCard className="w-12 h-12 sm:w-16 sm:h-16 text-[#d4af37]" />
|
<CreditCard className="w-12 h-12 sm:w-16 sm:h-16 text-[var(--luxury-gold)]" />
|
||||||
</div>
|
</div>
|
||||||
<h3 className="text-base sm:text-lg font-serif font-semibold text-white mb-1.5 sm:mb-2">
|
<h3 className="text-base sm:text-lg font-serif font-semibold text-white mb-1.5 sm:mb-2">
|
||||||
Complete Payment with Borica
|
Complete Payment with Borica
|
||||||
</h3>
|
</h3>
|
||||||
<p className="text-xs sm:text-sm text-gray-400 mb-3 sm:mb-4 leading-relaxed px-2">
|
<p className="text-xs sm:text-sm text-gray-400 mb-3 sm:mb-4 leading-relaxed px-2">
|
||||||
You will be redirected to Borica payment gateway to securely complete your payment of{' '}
|
You will be redirected to Borica payment gateway to securely complete your payment of{' '}
|
||||||
<span className="font-semibold text-[#d4af37]">
|
<span className="font-semibold text-[var(--luxury-gold)]">
|
||||||
{new Intl.NumberFormat('en-US', {
|
{new Intl.NumberFormat('en-US', {
|
||||||
style: 'currency',
|
style: 'currency',
|
||||||
currency: currency,
|
currency: currency,
|
||||||
@@ -169,14 +169,14 @@ const BoricaPaymentModal: React.FC<BoricaPaymentModalProps> = ({
|
|||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="bg-[#1a1a1a]/50 border border-[#d4af37]/20 rounded-lg p-3 sm:p-4 space-y-2 sm:space-y-3">
|
<div className="bg-[#1a1a1a]/50 border border-[var(--luxury-gold)]/20 rounded-lg p-3 sm:p-4 space-y-2 sm:space-y-3">
|
||||||
<div className="flex justify-between items-center text-xs sm:text-sm">
|
<div className="flex justify-between items-center text-xs sm:text-sm">
|
||||||
<span className="text-gray-400">Order ID:</span>
|
<span className="text-gray-400">Order ID:</span>
|
||||||
<span className="text-white font-mono">{paymentRequest.order_id}</span>
|
<span className="text-white font-mono">{paymentRequest.order_id}</span>
|
||||||
</div>
|
</div>
|
||||||
<div className="flex justify-between items-center text-xs sm:text-sm">
|
<div className="flex justify-between items-center text-xs sm:text-sm">
|
||||||
<span className="text-gray-400">Amount:</span>
|
<span className="text-gray-400">Amount:</span>
|
||||||
<span className="text-[#d4af37] font-semibold">
|
<span className="text-[var(--luxury-gold)] font-semibold">
|
||||||
{new Intl.NumberFormat('en-US', {
|
{new Intl.NumberFormat('en-US', {
|
||||||
style: 'currency',
|
style: 'currency',
|
||||||
currency: currency,
|
currency: currency,
|
||||||
@@ -202,7 +202,7 @@ const BoricaPaymentModal: React.FC<BoricaPaymentModalProps> = ({
|
|||||||
|
|
||||||
{/* Footer */}
|
{/* Footer */}
|
||||||
{!loading && !error && paymentRequest && (
|
{!loading && !error && paymentRequest && (
|
||||||
<div className="px-3 sm:px-4 md:px-6 py-3 sm:py-4 border-t border-[#d4af37]/20 bg-gradient-to-r from-[#1a1a1a] to-[#0a0a0a] flex-shrink-0 flex gap-2 sm:gap-3">
|
<div className="px-3 sm:px-4 md:px-6 py-3 sm:py-4 border-t border-[var(--luxury-gold)]/20 bg-gradient-to-r from-[#1a1a1a] to-[#0a0a0a] flex-shrink-0 flex gap-2 sm:gap-3">
|
||||||
<button
|
<button
|
||||||
onClick={onClose}
|
onClick={onClose}
|
||||||
className="flex-1 px-3 sm:px-4 py-2 sm:py-2.5 text-xs sm:text-sm font-medium text-gray-300 bg-[#1a1a1a] border border-gray-700 rounded-lg hover:bg-[#2a2a2a] hover:border-gray-600 transition-colors"
|
className="flex-1 px-3 sm:px-4 py-2 sm:py-2.5 text-xs sm:text-sm font-medium text-gray-300 bg-[#1a1a1a] border border-gray-700 rounded-lg hover:bg-[#2a2a2a] hover:border-gray-600 transition-colors"
|
||||||
@@ -211,7 +211,7 @@ const BoricaPaymentModal: React.FC<BoricaPaymentModalProps> = ({
|
|||||||
</button>
|
</button>
|
||||||
<button
|
<button
|
||||||
onClick={handleSubmitPayment}
|
onClick={handleSubmitPayment}
|
||||||
className="flex-1 px-3 sm:px-4 py-2 sm:py-2.5 text-xs sm:text-sm font-medium text-white bg-gradient-to-r from-[#d4af37] to-[#b8941f] rounded-lg hover:from-[#e5c048] hover:to-[#c9a428] transition-all shadow-lg shadow-[#d4af37]/20 flex items-center justify-center gap-2"
|
className="flex-1 px-3 sm:px-4 py-2 sm:py-2.5 text-xs sm:text-sm font-medium text-white bg-gradient-to-r from-[var(--luxury-gold)] to-[#b8941f] rounded-lg hover:from-[#e5c048] hover:to-[#c9a428] transition-all shadow-lg shadow-[var(--luxury-gold)]/20 flex items-center justify-center gap-2"
|
||||||
>
|
>
|
||||||
<CreditCard className="w-4 h-4" />
|
<CreditCard className="w-4 h-4" />
|
||||||
Proceed to Payment
|
Proceed to Payment
|
||||||
|
|||||||
@@ -41,16 +41,16 @@ const CashPaymentModal: React.FC<CashPaymentModalProps> = ({
|
|||||||
className="fixed inset-0 bg-black/90 backdrop-blur-sm"
|
className="fixed inset-0 bg-black/90 backdrop-blur-sm"
|
||||||
onClick={onClose}
|
onClick={onClose}
|
||||||
/>
|
/>
|
||||||
<div className="relative w-full max-w-md max-h-[95vh] bg-gradient-to-br from-[#0a0a0a] via-[#1a1a1a] to-[#0a0a0a] rounded-xl sm:rounded-2xl border border-[#d4af37]/30 shadow-2xl shadow-[#d4af37]/20 overflow-hidden flex flex-col">
|
<div className="relative w-full max-w-md max-h-[95vh] bg-gradient-to-br from-[#0a0a0a] via-[#1a1a1a] to-[#0a0a0a] rounded-xl sm:rounded-2xl border border-[var(--luxury-gold)]/30 shadow-2xl shadow-[var(--luxury-gold)]/20 overflow-hidden flex flex-col">
|
||||||
{/* Header */}
|
{/* Header */}
|
||||||
<div className="px-3 sm:px-4 md:px-6 py-3 sm:py-4 border-b border-[#d4af37]/20 bg-gradient-to-r from-[#1a1a1a] to-[#0a0a0a] flex-shrink-0">
|
<div className="px-3 sm:px-4 md:px-6 py-3 sm:py-4 border-b border-[var(--luxury-gold)]/20 bg-gradient-to-r from-[#1a1a1a] to-[#0a0a0a] flex-shrink-0">
|
||||||
<div className="flex items-center justify-between gap-2">
|
<div className="flex items-center justify-between gap-2">
|
||||||
<h2 className="text-base sm:text-lg md:text-xl font-serif font-bold text-white tracking-tight truncate flex-1">
|
<h2 className="text-base sm:text-lg md:text-xl font-serif font-bold text-white tracking-tight truncate flex-1">
|
||||||
Booking Created
|
Booking Created
|
||||||
</h2>
|
</h2>
|
||||||
<button
|
<button
|
||||||
onClick={onClose}
|
onClick={onClose}
|
||||||
className="p-1.5 sm:p-2 hover:bg-[#d4af37]/10 rounded-lg transition-colors text-gray-400 hover:text-white flex-shrink-0"
|
className="p-1.5 sm:p-2 hover:bg-[var(--luxury-gold)]/10 rounded-lg transition-colors text-gray-400 hover:text-white flex-shrink-0"
|
||||||
>
|
>
|
||||||
<X className="w-4 h-4 sm:w-5 sm:h-5" />
|
<X className="w-4 h-4 sm:w-5 sm:h-5" />
|
||||||
</button>
|
</button>
|
||||||
@@ -72,16 +72,16 @@ const CashPaymentModal: React.FC<CashPaymentModalProps> = ({
|
|||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="bg-[#0a0a0a] border border-[#d4af37]/20 rounded-lg p-3 sm:p-4 space-y-2 sm:space-y-3">
|
<div className="bg-[#0a0a0a] border border-[var(--luxury-gold)]/20 rounded-lg p-3 sm:p-4 space-y-2 sm:space-y-3">
|
||||||
<div className="flex items-center justify-between text-xs sm:text-sm">
|
<div className="flex items-center justify-between text-xs sm:text-sm">
|
||||||
<span className="text-gray-400">Total Amount</span>
|
<span className="text-gray-400">Total Amount</span>
|
||||||
<span className="text-white font-semibold">{formatCurrency(amount)}</span>
|
<span className="text-white font-semibold">{formatCurrency(amount)}</span>
|
||||||
</div>
|
</div>
|
||||||
<div className="flex items-center justify-between text-xs sm:text-sm">
|
<div className="flex items-center justify-between text-xs sm:text-sm">
|
||||||
<span className="text-gray-400 break-words pr-2">Deposit Required (20%)</span>
|
<span className="text-gray-400 break-words pr-2">Deposit Required (20%)</span>
|
||||||
<span className="text-[#d4af37] font-bold flex-shrink-0">{formatCurrency(depositAmount)}</span>
|
<span className="text-[var(--luxury-gold)] font-bold flex-shrink-0">{formatCurrency(depositAmount)}</span>
|
||||||
</div>
|
</div>
|
||||||
<div className="pt-2 sm:pt-3 border-t border-[#d4af37]/20">
|
<div className="pt-2 sm:pt-3 border-t border-[var(--luxury-gold)]/20">
|
||||||
<p className="text-xs text-gray-400 leading-relaxed">
|
<p className="text-xs text-gray-400 leading-relaxed">
|
||||||
Pay the remaining balance on arrival at the hotel.
|
Pay the remaining balance on arrival at the hotel.
|
||||||
</p>
|
</p>
|
||||||
@@ -91,7 +91,7 @@ const CashPaymentModal: React.FC<CashPaymentModalProps> = ({
|
|||||||
<div className="flex flex-col gap-2 sm:gap-3 pt-2">
|
<div className="flex flex-col gap-2 sm:gap-3 pt-2">
|
||||||
<button
|
<button
|
||||||
onClick={handlePayDeposit}
|
onClick={handlePayDeposit}
|
||||||
className="w-full bg-[#d4af37] text-black font-semibold py-2.5 sm:py-3 px-4 sm:px-6 rounded-lg hover:bg-[#c9a227] transition-all flex items-center justify-center gap-2 text-sm sm:text-base min-h-[44px]"
|
className="w-full bg-[var(--luxury-gold)] text-black font-semibold py-2.5 sm:py-3 px-4 sm:px-6 rounded-lg hover:bg-[var(--luxury-gold-dark)] transition-all flex items-center justify-center gap-2 text-sm sm:text-base min-h-[44px]"
|
||||||
>
|
>
|
||||||
<CreditCard className="w-4 h-4 sm:w-5 sm:h-5" />
|
<CreditCard className="w-4 h-4 sm:w-5 sm:h-5" />
|
||||||
<span>Pay Deposit Now</span>
|
<span>Pay Deposit Now</span>
|
||||||
|
|||||||
@@ -177,9 +177,9 @@ const DepositPaymentModal: React.FC<DepositPaymentModalProps> = ({
|
|||||||
className="fixed inset-0 bg-black/90 backdrop-blur-sm"
|
className="fixed inset-0 bg-black/90 backdrop-blur-sm"
|
||||||
onClick={onClose}
|
onClick={onClose}
|
||||||
/>
|
/>
|
||||||
<div className="relative w-full max-w-3xl max-h-[95vh] bg-gradient-to-br from-[#0a0a0a] via-[#1a1a1a] to-[#0a0a0a] rounded-xl sm:rounded-2xl border border-[#d4af37]/30 shadow-2xl shadow-[#d4af37]/20 overflow-hidden flex flex-col">
|
<div className="relative w-full max-w-3xl max-h-[95vh] bg-gradient-to-br from-[#0a0a0a] via-[#1a1a1a] to-[#0a0a0a] rounded-xl sm:rounded-2xl border border-[var(--luxury-gold)]/30 shadow-2xl shadow-[var(--luxury-gold)]/20 overflow-hidden flex flex-col">
|
||||||
{/* Header */}
|
{/* Header */}
|
||||||
<div className="px-3 sm:px-4 md:px-6 py-3 sm:py-4 border-b border-[#d4af37]/20 bg-gradient-to-r from-[#1a1a1a] to-[#0a0a0a] flex-shrink-0">
|
<div className="px-3 sm:px-4 md:px-6 py-3 sm:py-4 border-b border-[var(--luxury-gold)]/20 bg-gradient-to-r from-[#1a1a1a] to-[#0a0a0a] flex-shrink-0">
|
||||||
<div className="flex items-center justify-between gap-2">
|
<div className="flex items-center justify-between gap-2">
|
||||||
<div className="min-w-0 flex-1">
|
<div className="min-w-0 flex-1">
|
||||||
<h2 className="text-base sm:text-lg md:text-xl font-serif font-bold text-white tracking-tight truncate">
|
<h2 className="text-base sm:text-lg md:text-xl font-serif font-bold text-white tracking-tight truncate">
|
||||||
@@ -204,7 +204,7 @@ const DepositPaymentModal: React.FC<DepositPaymentModalProps> = ({
|
|||||||
)}
|
)}
|
||||||
<button
|
<button
|
||||||
onClick={onClose}
|
onClick={onClose}
|
||||||
className="p-1.5 sm:p-2 hover:bg-[#d4af37]/10 rounded-lg transition-colors text-gray-400 hover:text-white flex-shrink-0"
|
className="p-1.5 sm:p-2 hover:bg-[var(--luxury-gold)]/10 rounded-lg transition-colors text-gray-400 hover:text-white flex-shrink-0"
|
||||||
>
|
>
|
||||||
<X className="w-4 h-4 sm:w-5 sm:h-5" />
|
<X className="w-4 h-4 sm:w-5 sm:h-5" />
|
||||||
</button>
|
</button>
|
||||||
@@ -216,7 +216,7 @@ const DepositPaymentModal: React.FC<DepositPaymentModalProps> = ({
|
|||||||
<div className="flex-1 overflow-y-auto px-3 sm:px-4 md:px-6 py-4 sm:py-6">
|
<div className="flex-1 overflow-y-auto px-3 sm:px-4 md:px-6 py-4 sm:py-6">
|
||||||
{loading ? (
|
{loading ? (
|
||||||
<div className="flex flex-col items-center justify-center py-8 sm:py-12">
|
<div className="flex flex-col items-center justify-center py-8 sm:py-12">
|
||||||
<Loader2 className="w-10 h-10 sm:w-12 sm:h-12 animate-spin text-[#d4af37] mb-3 sm:mb-4" />
|
<Loader2 className="w-10 h-10 sm:w-12 sm:h-12 animate-spin text-[var(--luxury-gold)] mb-3 sm:mb-4" />
|
||||||
<p className="text-xs sm:text-sm text-gray-400">Loading payment information...</p>
|
<p className="text-xs sm:text-sm text-gray-400">Loading payment information...</p>
|
||||||
</div>
|
</div>
|
||||||
) : error || !booking || !depositPayment ? (
|
) : error || !booking || !depositPayment ? (
|
||||||
@@ -227,7 +227,7 @@ const DepositPaymentModal: React.FC<DepositPaymentModalProps> = ({
|
|||||||
</p>
|
</p>
|
||||||
<button
|
<button
|
||||||
onClick={onClose}
|
onClick={onClose}
|
||||||
className="px-4 py-2 sm:py-2.5 bg-[#d4af37] text-black font-semibold rounded-lg hover:bg-[#c9a227] transition-all text-sm sm:text-base min-h-[44px]"
|
className="px-4 py-2 sm:py-2.5 bg-[var(--luxury-gold)] text-black font-semibold rounded-lg hover:bg-[var(--luxury-gold-dark)] transition-all text-sm sm:text-base min-h-[44px]"
|
||||||
>
|
>
|
||||||
Close
|
Close
|
||||||
</button>
|
</button>
|
||||||
@@ -260,17 +260,17 @@ const DepositPaymentModal: React.FC<DepositPaymentModalProps> = ({
|
|||||||
|
|
||||||
{/* Payment Required Message */}
|
{/* Payment Required Message */}
|
||||||
{!isDepositPaid && (
|
{!isDepositPaid && (
|
||||||
<div className="bg-[#d4af37]/10 border border-[#d4af37]/30 rounded-lg p-3 sm:p-4">
|
<div className="bg-[var(--luxury-gold)]/10 border border-[var(--luxury-gold)]/30 rounded-lg p-3 sm:p-4">
|
||||||
<div className="flex items-start gap-2 sm:gap-3">
|
<div className="flex items-start gap-2 sm:gap-3">
|
||||||
<div className="w-10 h-10 sm:w-12 sm:h-12 bg-[#d4af37]/20 rounded-full flex items-center justify-center border border-[#d4af37]/30 flex-shrink-0">
|
<div className="w-10 h-10 sm:w-12 sm:h-12 bg-[var(--luxury-gold)]/20 rounded-full flex items-center justify-center border border-[var(--luxury-gold)]/30 flex-shrink-0">
|
||||||
<CreditCard className="w-5 h-5 sm:w-6 sm:h-6 md:w-7 md:h-7 text-[#d4af37]" />
|
<CreditCard className="w-5 h-5 sm:w-6 sm:h-6 md:w-7 md:h-7 text-[var(--luxury-gold)]" />
|
||||||
</div>
|
</div>
|
||||||
<div className="flex-1 min-w-0">
|
<div className="flex-1 min-w-0">
|
||||||
<h3 className="text-base sm:text-lg font-serif font-semibold text-[#d4af37] mb-1">
|
<h3 className="text-base sm:text-lg font-serif font-semibold text-[var(--luxury-gold)] mb-1">
|
||||||
Deposit Payment Required
|
Deposit Payment Required
|
||||||
</h3>
|
</h3>
|
||||||
<p className="text-xs sm:text-sm text-gray-300 leading-relaxed">
|
<p className="text-xs sm:text-sm text-gray-300 leading-relaxed">
|
||||||
Please pay <strong className="text-[#d4af37]">20% deposit</strong> to confirm your booking. Pay the remaining balance on arrival at the hotel.
|
Please pay <strong className="text-[var(--luxury-gold)]">20% deposit</strong> to confirm your booking. Pay the remaining balance on arrival at the hotel.
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -278,14 +278,14 @@ const DepositPaymentModal: React.FC<DepositPaymentModalProps> = ({
|
|||||||
)}
|
)}
|
||||||
|
|
||||||
{/* Payment Information */}
|
{/* Payment Information */}
|
||||||
<div className="bg-[#0a0a0a] border border-[#d4af37]/20 rounded-lg p-3 sm:p-4">
|
<div className="bg-[#0a0a0a] border border-[var(--luxury-gold)]/20 rounded-lg p-3 sm:p-4">
|
||||||
<h3 className="text-xs sm:text-sm font-semibold text-[#d4af37] mb-2 sm:mb-3">Payment Information</h3>
|
<h3 className="text-xs sm:text-sm font-semibold text-[var(--luxury-gold)] mb-2 sm:mb-3">Payment Information</h3>
|
||||||
<div className="space-y-1.5 sm:space-y-2 text-xs sm:text-sm">
|
<div className="space-y-1.5 sm:space-y-2 text-xs sm:text-sm">
|
||||||
<div className="flex justify-between text-gray-300">
|
<div className="flex justify-between text-gray-300">
|
||||||
<span>Total Room Price</span>
|
<span>Total Room Price</span>
|
||||||
<span>{formatCurrency(booking.total_price)}</span>
|
<span>{formatCurrency(booking.total_price)}</span>
|
||||||
</div>
|
</div>
|
||||||
<div className="flex justify-between text-base sm:text-lg font-bold text-[#d4af37] pt-2 border-t border-[#d4af37]/20">
|
<div className="flex justify-between text-base sm:text-lg font-bold text-[var(--luxury-gold)] pt-2 border-t border-[var(--luxury-gold)]/20">
|
||||||
<span className="break-words pr-2">Deposit Amount (20%)</span>
|
<span className="break-words pr-2">Deposit Amount (20%)</span>
|
||||||
<span className="flex-shrink-0">{formatCurrency(depositAmount)}</span>
|
<span className="flex-shrink-0">{formatCurrency(depositAmount)}</span>
|
||||||
</div>
|
</div>
|
||||||
@@ -298,18 +298,18 @@ const DepositPaymentModal: React.FC<DepositPaymentModalProps> = ({
|
|||||||
|
|
||||||
{/* Payment Method Selection */}
|
{/* Payment Method Selection */}
|
||||||
{!isDepositPaid && !selectedPaymentMethod && (
|
{!isDepositPaid && !selectedPaymentMethod && (
|
||||||
<div className="bg-[#0a0a0a] border border-[#d4af37]/20 rounded-lg p-3 sm:p-4">
|
<div className="bg-[#0a0a0a] border border-[var(--luxury-gold)]/20 rounded-lg p-3 sm:p-4">
|
||||||
<h3 className="text-xs sm:text-sm font-semibold text-[#d4af37] mb-2 sm:mb-3">Choose Payment Method</h3>
|
<h3 className="text-xs sm:text-sm font-semibold text-[var(--luxury-gold)] mb-2 sm:mb-3">Choose Payment Method</h3>
|
||||||
<div className="grid grid-cols-1 sm:grid-cols-2 gap-2 sm:gap-3">
|
<div className="grid grid-cols-1 sm:grid-cols-2 gap-2 sm:gap-3">
|
||||||
<button
|
<button
|
||||||
onClick={() => handlePaymentMethodSelect('stripe')}
|
onClick={() => handlePaymentMethodSelect('stripe')}
|
||||||
className="p-3 sm:p-4 bg-[#1a1a1a] border-2 border-gray-600/30 rounded-lg hover:border-[#d4af37]/50 transition-all text-left group min-h-[80px] sm:min-h-[100px]"
|
className="p-3 sm:p-4 bg-[#1a1a1a] border-2 border-gray-600/30 rounded-lg hover:border-[var(--luxury-gold)]/50 transition-all text-left group min-h-[80px] sm:min-h-[100px]"
|
||||||
>
|
>
|
||||||
<div className="flex items-center justify-between mb-1.5 sm:mb-2">
|
<div className="flex items-center justify-between mb-1.5 sm:mb-2">
|
||||||
<div className="w-8 h-8 sm:w-10 sm:h-10 bg-indigo-500/20 rounded-lg flex items-center justify-center border border-indigo-500/30 flex-shrink-0">
|
<div className="w-8 h-8 sm:w-10 sm:h-10 bg-indigo-500/20 rounded-lg flex items-center justify-center border border-indigo-500/30 flex-shrink-0">
|
||||||
<CreditCard className="w-4 h-4 sm:w-5 sm:h-5 text-indigo-400 group-hover:text-[#d4af37] transition-colors" />
|
<CreditCard className="w-4 h-4 sm:w-5 sm:h-5 text-indigo-400 group-hover:text-[var(--luxury-gold)] transition-colors" />
|
||||||
</div>
|
</div>
|
||||||
<span className="text-xs font-semibold text-indigo-400 group-hover:text-[#d4af37] transition-colors">
|
<span className="text-xs font-semibold text-indigo-400 group-hover:text-[var(--luxury-gold)] transition-colors">
|
||||||
Card Payment
|
Card Payment
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
@@ -320,19 +320,19 @@ const DepositPaymentModal: React.FC<DepositPaymentModalProps> = ({
|
|||||||
|
|
||||||
<button
|
<button
|
||||||
onClick={() => handlePaymentMethodSelect('paypal')}
|
onClick={() => handlePaymentMethodSelect('paypal')}
|
||||||
className="p-3 sm:p-4 bg-[#1a1a1a] border-2 border-gray-600/30 rounded-lg hover:border-[#d4af37]/50 transition-all text-left group min-h-[80px] sm:min-h-[100px]"
|
className="p-3 sm:p-4 bg-[#1a1a1a] border-2 border-gray-600/30 rounded-lg hover:border-[var(--luxury-gold)]/50 transition-all text-left group min-h-[80px] sm:min-h-[100px]"
|
||||||
>
|
>
|
||||||
<div className="flex items-center justify-between mb-1.5 sm:mb-2">
|
<div className="flex items-center justify-between mb-1.5 sm:mb-2">
|
||||||
<div className="w-8 h-8 sm:w-10 sm:h-10 bg-blue-500/20 rounded-lg flex items-center justify-center border border-blue-500/30 flex-shrink-0">
|
<div className="w-8 h-8 sm:w-10 sm:h-10 bg-blue-500/20 rounded-lg flex items-center justify-center border border-blue-500/30 flex-shrink-0">
|
||||||
<svg
|
<svg
|
||||||
className="w-4 h-4 sm:w-5 sm:h-5 text-blue-400 group-hover:text-[#d4af37] transition-colors"
|
className="w-4 h-4 sm:w-5 sm:h-5 text-blue-400 group-hover:text-[var(--luxury-gold)] transition-colors"
|
||||||
viewBox="0 0 24 24"
|
viewBox="0 0 24 24"
|
||||||
fill="currentColor"
|
fill="currentColor"
|
||||||
>
|
>
|
||||||
<path d="M7.076 21.337H2.47a.641.641 0 0 1-.633-.74L4.944.901C5.026.382 5.474 0 5.998 0h7.46c2.57 0 4.578.543 5.69 1.81 1.01 1.15 1.304 2.42 1.012 4.287-.023.143-.047.288-.077.437-.983 5.05-4.349 6.797-8.647 6.797h-2.19c-.524 0-.968.382-1.05.9l-1.12 7.203zm14.146-14.42a.477.477 0 0 0-.414-.24h-3.84c-.48 0-.856.355-.932.826-.075.47-.232 1.21-.232 1.21s-.156-.74-.232-1.21a.957.957 0 0 0-.932-.826H5.342a.957.957 0 0 0-.932.826c-.076.47-.232 1.21-.232 1.21s-.156-.74-.232-1.21a.957.957 0 0 0-.932-.826H.477a.477.477 0 0 0-.414.24c-.11.19-.14.426-.08.643.06.217.2.4.388.51l.04.02c.19.11.426.14.643.08.217-.06.4-.2.51-.388l.01-.02c.11-.19.14-.426.08-.643a.955.955 0 0 0-.388-.51l-.01-.01a.955.955 0 0 0-.51-.388.955.955 0 0 0-.643.08l-.01.01a.955.955 0 0 0-.388.51c-.06.217-.03.453.08.643l.01.02c.11.188.293.328.51.388.217.06.453.03.643-.08l.01-.02c.188-.11.328-.293.388-.51.06-.217.03-.453-.08-.643l-.01-.01z"/>
|
<path d="M7.076 21.337H2.47a.641.641 0 0 1-.633-.74L4.944.901C5.026.382 5.474 0 5.998 0h7.46c2.57 0 4.578.543 5.69 1.81 1.01 1.15 1.304 2.42 1.012 4.287-.023.143-.047.288-.077.437-.983 5.05-4.349 6.797-8.647 6.797h-2.19c-.524 0-.968.382-1.05.9l-1.12 7.203zm14.146-14.42a.477.477 0 0 0-.414-.24h-3.84c-.48 0-.856.355-.932.826-.075.47-.232 1.21-.232 1.21s-.156-.74-.232-1.21a.957.957 0 0 0-.932-.826H5.342a.957.957 0 0 0-.932.826c-.076.47-.232 1.21-.232 1.21s-.156-.74-.232-1.21a.957.957 0 0 0-.932-.826H.477a.477.477 0 0 0-.414.24c-.11.19-.14.426-.08.643.06.217.2.4.388.51l.04.02c.19.11.426.14.643.08.217-.06.4-.2.51-.388l.01-.02c.11-.19.14-.426.08-.643a.955.955 0 0 0-.388-.51l-.01-.01a.955.955 0 0 0-.51-.388.955.955 0 0 0-.643.08l-.01.01a.955.955 0 0 0-.388.51c-.06.217-.03.453.08.643l.01.02c.11.188.293.328.51.388.217.06.453.03.643-.08l.01-.02c.188-.11.328-.293.388-.51.06-.217.03-.453-.08-.643l-.01-.01z"/>
|
||||||
</svg>
|
</svg>
|
||||||
</div>
|
</div>
|
||||||
<span className="text-xs font-semibold text-blue-400 group-hover:text-[#d4af37] transition-colors">
|
<span className="text-xs font-semibold text-blue-400 group-hover:text-[var(--luxury-gold)] transition-colors">
|
||||||
PayPal
|
PayPal
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
@@ -346,10 +346,10 @@ const DepositPaymentModal: React.FC<DepositPaymentModalProps> = ({
|
|||||||
|
|
||||||
{/* Change Payment Method */}
|
{/* Change Payment Method */}
|
||||||
{!isDepositPaid && selectedPaymentMethod && (
|
{!isDepositPaid && selectedPaymentMethod && (
|
||||||
<div className="bg-[#0a0a0a] border border-[#d4af37]/20 rounded-lg p-3 sm:p-4">
|
<div className="bg-[#0a0a0a] border border-[var(--luxury-gold)]/20 rounded-lg p-3 sm:p-4">
|
||||||
<div className="flex items-center justify-between gap-2 mb-2 sm:mb-3">
|
<div className="flex items-center justify-between gap-2 mb-2 sm:mb-3">
|
||||||
<div className="min-w-0 flex-1">
|
<div className="min-w-0 flex-1">
|
||||||
<h3 className="text-xs sm:text-sm font-semibold text-[#d4af37]">
|
<h3 className="text-xs sm:text-sm font-semibold text-[var(--luxury-gold)]">
|
||||||
{selectedPaymentMethod === 'stripe' ? 'Card Payment' : 'PayPal Payment'}
|
{selectedPaymentMethod === 'stripe' ? 'Card Payment' : 'PayPal Payment'}
|
||||||
</h3>
|
</h3>
|
||||||
<p className="text-xs text-gray-400 mt-0.5 sm:mt-1">
|
<p className="text-xs text-gray-400 mt-0.5 sm:mt-1">
|
||||||
@@ -361,7 +361,7 @@ const DepositPaymentModal: React.FC<DepositPaymentModalProps> = ({
|
|||||||
setSelectedPaymentMethod(null);
|
setSelectedPaymentMethod(null);
|
||||||
setShowPaymentModal(false);
|
setShowPaymentModal(false);
|
||||||
}}
|
}}
|
||||||
className="text-xs text-gray-400 hover:text-[#d4af37] underline transition-colors flex-shrink-0 whitespace-nowrap"
|
className="text-xs text-gray-400 hover:text-[var(--luxury-gold)] underline transition-colors flex-shrink-0 whitespace-nowrap"
|
||||||
>
|
>
|
||||||
Change method
|
Change method
|
||||||
</button>
|
</button>
|
||||||
|
|||||||
@@ -89,16 +89,16 @@ const PayPalPaymentModal: React.FC<PayPalPaymentModalProps> = ({
|
|||||||
className="fixed inset-0 bg-black/90 backdrop-blur-sm"
|
className="fixed inset-0 bg-black/90 backdrop-blur-sm"
|
||||||
onClick={onClose}
|
onClick={onClose}
|
||||||
/>
|
/>
|
||||||
<div className="relative w-full max-w-md max-h-[95vh] bg-gradient-to-br from-[#0a0a0a] via-[#1a1a1a] to-[#0a0a0a] rounded-xl sm:rounded-2xl border border-[#d4af37]/30 shadow-2xl shadow-[#d4af37]/20 overflow-hidden flex flex-col">
|
<div className="relative w-full max-w-md max-h-[95vh] bg-gradient-to-br from-[#0a0a0a] via-[#1a1a1a] to-[#0a0a0a] rounded-xl sm:rounded-2xl border border-[var(--luxury-gold)]/30 shadow-2xl shadow-[var(--luxury-gold)]/20 overflow-hidden flex flex-col">
|
||||||
{/* Header */}
|
{/* Header */}
|
||||||
<div className="px-3 sm:px-4 md:px-6 py-3 sm:py-4 border-b border-[#d4af37]/20 bg-gradient-to-r from-[#1a1a1a] to-[#0a0a0a] flex-shrink-0">
|
<div className="px-3 sm:px-4 md:px-6 py-3 sm:py-4 border-b border-[var(--luxury-gold)]/20 bg-gradient-to-r from-[#1a1a1a] to-[#0a0a0a] flex-shrink-0">
|
||||||
<div className="flex items-center justify-between gap-2">
|
<div className="flex items-center justify-between gap-2">
|
||||||
<h2 className="text-base sm:text-lg md:text-xl font-serif font-bold text-white tracking-tight truncate flex-1">
|
<h2 className="text-base sm:text-lg md:text-xl font-serif font-bold text-white tracking-tight truncate flex-1">
|
||||||
PayPal Payment
|
PayPal Payment
|
||||||
</h2>
|
</h2>
|
||||||
<button
|
<button
|
||||||
onClick={onClose}
|
onClick={onClose}
|
||||||
className="p-1.5 sm:p-2 hover:bg-[#d4af37]/10 rounded-lg transition-colors text-gray-400 hover:text-white flex-shrink-0"
|
className="p-1.5 sm:p-2 hover:bg-[var(--luxury-gold)]/10 rounded-lg transition-colors text-gray-400 hover:text-white flex-shrink-0"
|
||||||
>
|
>
|
||||||
<X className="w-4 h-4 sm:w-5 sm:h-5" />
|
<X className="w-4 h-4 sm:w-5 sm:h-5" />
|
||||||
</button>
|
</button>
|
||||||
@@ -109,7 +109,7 @@ const PayPalPaymentModal: React.FC<PayPalPaymentModalProps> = ({
|
|||||||
<div className="flex-1 overflow-y-auto p-3 sm:p-4 md:p-6">
|
<div className="flex-1 overflow-y-auto p-3 sm:p-4 md:p-6">
|
||||||
{loading ? (
|
{loading ? (
|
||||||
<div className="flex flex-col items-center justify-center py-8 sm:py-12">
|
<div className="flex flex-col items-center justify-center py-8 sm:py-12">
|
||||||
<Loader2 className="w-10 h-10 sm:w-12 sm:h-12 animate-spin text-[#d4af37] mb-3 sm:mb-4" />
|
<Loader2 className="w-10 h-10 sm:w-12 sm:h-12 animate-spin text-[var(--luxury-gold)] mb-3 sm:mb-4" />
|
||||||
<p className="text-xs sm:text-sm text-gray-400">Initializing PayPal payment...</p>
|
<p className="text-xs sm:text-sm text-gray-400">Initializing PayPal payment...</p>
|
||||||
</div>
|
</div>
|
||||||
) : error ? (
|
) : error ? (
|
||||||
@@ -147,7 +147,7 @@ const PayPalPaymentModal: React.FC<PayPalPaymentModalProps> = ({
|
|||||||
</h3>
|
</h3>
|
||||||
<p className="text-xs sm:text-sm text-gray-400 mb-3 sm:mb-4 leading-relaxed px-2">
|
<p className="text-xs sm:text-sm text-gray-400 mb-3 sm:mb-4 leading-relaxed px-2">
|
||||||
You will be redirected to PayPal to securely complete your payment of{' '}
|
You will be redirected to PayPal to securely complete your payment of{' '}
|
||||||
<span className="font-semibold text-[#d4af37]">
|
<span className="font-semibold text-[var(--luxury-gold)]">
|
||||||
{new Intl.NumberFormat('en-US', {
|
{new Intl.NumberFormat('en-US', {
|
||||||
style: 'currency',
|
style: 'currency',
|
||||||
currency: currency,
|
currency: currency,
|
||||||
@@ -178,7 +178,7 @@ const PayPalPaymentModal: React.FC<PayPalPaymentModalProps> = ({
|
|||||||
</div>
|
</div>
|
||||||
) : (
|
) : (
|
||||||
<div className="flex flex-col items-center justify-center py-8 sm:py-12">
|
<div className="flex flex-col items-center justify-center py-8 sm:py-12">
|
||||||
<Loader2 className="w-10 h-10 sm:w-12 sm:h-12 animate-spin text-[#d4af37] mb-3 sm:mb-4" />
|
<Loader2 className="w-10 h-10 sm:w-12 sm:h-12 animate-spin text-[var(--luxury-gold)] mb-3 sm:mb-4" />
|
||||||
<p className="text-xs sm:text-sm text-gray-400">Loading PayPal...</p>
|
<p className="text-xs sm:text-sm text-gray-400">Loading PayPal...</p>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|||||||
@@ -83,10 +83,10 @@ const PayPalPaymentWrapper: React.FC<PayPalPaymentWrapperProps> = ({
|
|||||||
if (loading) {
|
if (loading) {
|
||||||
return (
|
return (
|
||||||
<div className="flex items-center justify-center p-8">
|
<div className="flex items-center justify-center p-8">
|
||||||
<div className="w-16 h-16 bg-gradient-to-br from-[#d4af37]/20 to-[#f5d76e]/20
|
<div className="w-16 h-16 bg-gradient-to-br from-[var(--luxury-gold)]/20 to-[var(--luxury-gold-light)]/20
|
||||||
rounded-full flex items-center justify-center
|
rounded-full flex items-center justify-center
|
||||||
border border-[#d4af37]/30 shadow-lg shadow-[#d4af37]/20">
|
border border-[var(--luxury-gold)]/30 shadow-lg shadow-[var(--luxury-gold)]/20">
|
||||||
<Loader2 className="w-8 h-8 animate-spin text-[#d4af37]" />
|
<Loader2 className="w-8 h-8 animate-spin text-[var(--luxury-gold)]" />
|
||||||
</div>
|
</div>
|
||||||
<span className="ml-4 text-gray-300 font-light tracking-wide">
|
<span className="ml-4 text-gray-300 font-light tracking-wide">
|
||||||
Initializing PayPal payment...
|
Initializing PayPal payment...
|
||||||
@@ -117,10 +117,10 @@ const PayPalPaymentWrapper: React.FC<PayPalPaymentWrapperProps> = ({
|
|||||||
if (!approvalUrl) {
|
if (!approvalUrl) {
|
||||||
return (
|
return (
|
||||||
<div className="flex items-center justify-center p-8">
|
<div className="flex items-center justify-center p-8">
|
||||||
<div className="w-16 h-16 bg-gradient-to-br from-[#d4af37]/20 to-[#f5d76e]/20
|
<div className="w-16 h-16 bg-gradient-to-br from-[var(--luxury-gold)]/20 to-[var(--luxury-gold-light)]/20
|
||||||
rounded-full flex items-center justify-center
|
rounded-full flex items-center justify-center
|
||||||
border border-[#d4af37]/30 shadow-lg shadow-[#d4af37]/20">
|
border border-[var(--luxury-gold)]/30 shadow-lg shadow-[var(--luxury-gold)]/20">
|
||||||
<Loader2 className="w-8 h-8 animate-spin text-[#d4af37]" />
|
<Loader2 className="w-8 h-8 animate-spin text-[var(--luxury-gold)]" />
|
||||||
</div>
|
</div>
|
||||||
<span className="ml-4 text-gray-300 font-light tracking-wide">
|
<span className="ml-4 text-gray-300 font-light tracking-wide">
|
||||||
Loading PayPal...
|
Loading PayPal...
|
||||||
@@ -144,12 +144,12 @@ const PayPalPaymentWrapper: React.FC<PayPalPaymentWrapperProps> = ({
|
|||||||
/>
|
/>
|
||||||
</svg>
|
</svg>
|
||||||
</div>
|
</div>
|
||||||
<h3 className="text-xl font-serif font-semibold text-[#d4af37] mb-3 tracking-wide">
|
<h3 className="text-xl font-serif font-semibold text-[var(--luxury-gold)] mb-3 tracking-wide">
|
||||||
Complete Payment with PayPal
|
Complete Payment with PayPal
|
||||||
</h3>
|
</h3>
|
||||||
<p className="text-gray-300/80 font-light text-lg mb-8 tracking-wide">
|
<p className="text-gray-300/80 font-light text-lg mb-8 tracking-wide">
|
||||||
You will be redirected to PayPal to securely complete your payment of{' '}
|
You will be redirected to PayPal to securely complete your payment of{' '}
|
||||||
<span className="font-semibold text-[#d4af37]">
|
<span className="font-semibold text-[var(--luxury-gold)]">
|
||||||
{new Intl.NumberFormat('en-US', {
|
{new Intl.NumberFormat('en-US', {
|
||||||
style: 'currency',
|
style: 'currency',
|
||||||
currency: currency,
|
currency: currency,
|
||||||
|
|||||||
@@ -75,7 +75,7 @@ const StripePaymentForm: React.FC<StripePaymentFormProps> = ({
|
|||||||
if (!stripe || !elements) {
|
if (!stripe || !elements) {
|
||||||
return (
|
return (
|
||||||
<div className="flex items-center justify-center p-8">
|
<div className="flex items-center justify-center p-8">
|
||||||
<Loader2 className="w-8 h-8 animate-spin text-[#d4af37]" />
|
<Loader2 className="w-8 h-8 animate-spin text-[var(--luxury-gold)]" />
|
||||||
<span className="ml-2 text-gray-300">Loading payment form...</span>
|
<span className="ml-2 text-gray-300">Loading payment form...</span>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
@@ -88,12 +88,12 @@ const StripePaymentForm: React.FC<StripePaymentFormProps> = ({
|
|||||||
autoComplete="off"
|
autoComplete="off"
|
||||||
// SECURITY: Prevent form caching and autocomplete for payment forms
|
// SECURITY: Prevent form caching and autocomplete for payment forms
|
||||||
>
|
>
|
||||||
<div className="mb-4 p-4 bg-[#d4af37]/10 border border-[#d4af37]/20 rounded-lg">
|
<div className="mb-4 p-4 bg-[var(--luxury-gold)]/10 border border-[var(--luxury-gold)]/20 rounded-lg">
|
||||||
<div className="flex justify-between items-center">
|
<div className="flex justify-between items-center">
|
||||||
<span className="text-sm font-medium text-gray-300">
|
<span className="text-sm font-medium text-gray-300">
|
||||||
Amount to Pay
|
Amount to Pay
|
||||||
</span>
|
</span>
|
||||||
<span className="text-xl font-bold text-[#d4af37]">
|
<span className="text-xl font-bold text-[var(--luxury-gold)]">
|
||||||
${amount.toFixed(2)}
|
${amount.toFixed(2)}
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
@@ -141,8 +141,8 @@ const StripePaymentForm: React.FC<StripePaymentFormProps> = ({
|
|||||||
<button
|
<button
|
||||||
type="submit"
|
type="submit"
|
||||||
disabled={isProcessing || !stripe || !elements}
|
disabled={isProcessing || !stripe || !elements}
|
||||||
className="w-full bg-[#d4af37] text-black py-3 px-4 rounded-lg
|
className="w-full bg-[var(--luxury-gold)] text-black py-3 px-4 rounded-lg
|
||||||
hover:bg-[#c9a227] transition-colors font-semibold
|
hover:bg-[var(--luxury-gold-dark)] transition-colors font-semibold
|
||||||
disabled:bg-gray-600 disabled:cursor-not-allowed disabled:text-gray-400
|
disabled:bg-gray-600 disabled:cursor-not-allowed disabled:text-gray-400
|
||||||
flex items-center justify-center gap-2"
|
flex items-center justify-center gap-2"
|
||||||
>
|
>
|
||||||
|
|||||||
@@ -123,16 +123,16 @@ const StripePaymentModal: React.FC<StripePaymentModalProps> = ({
|
|||||||
className="fixed inset-0 bg-black/90 backdrop-blur-sm"
|
className="fixed inset-0 bg-black/90 backdrop-blur-sm"
|
||||||
onClick={onClose}
|
onClick={onClose}
|
||||||
/>
|
/>
|
||||||
<div className="relative w-full max-w-md max-h-[95vh] bg-gradient-to-br from-[#0a0a0a] via-[#1a1a1a] to-[#0a0a0a] rounded-xl sm:rounded-2xl border border-[#d4af37]/30 shadow-2xl shadow-[#d4af37]/20 overflow-hidden flex flex-col">
|
<div className="relative w-full max-w-md max-h-[95vh] bg-gradient-to-br from-[#0a0a0a] via-[#1a1a1a] to-[#0a0a0a] rounded-xl sm:rounded-2xl border border-[var(--luxury-gold)]/30 shadow-2xl shadow-[var(--luxury-gold)]/20 overflow-hidden flex flex-col">
|
||||||
{/* Header */}
|
{/* Header */}
|
||||||
<div className="px-3 sm:px-4 md:px-6 py-3 sm:py-4 border-b border-[#d4af37]/20 bg-gradient-to-r from-[#1a1a1a] to-[#0a0a0a] flex-shrink-0">
|
<div className="px-3 sm:px-4 md:px-6 py-3 sm:py-4 border-b border-[var(--luxury-gold)]/20 bg-gradient-to-r from-[#1a1a1a] to-[#0a0a0a] flex-shrink-0">
|
||||||
<div className="flex items-center justify-between gap-2">
|
<div className="flex items-center justify-between gap-2">
|
||||||
<h2 className="text-base sm:text-lg md:text-xl font-serif font-bold text-white tracking-tight truncate flex-1">
|
<h2 className="text-base sm:text-lg md:text-xl font-serif font-bold text-white tracking-tight truncate flex-1">
|
||||||
Complete Payment
|
Complete Payment
|
||||||
</h2>
|
</h2>
|
||||||
<button
|
<button
|
||||||
onClick={onClose}
|
onClick={onClose}
|
||||||
className="p-1.5 sm:p-2 hover:bg-[#d4af37]/10 rounded-lg transition-colors text-gray-400 hover:text-white flex-shrink-0"
|
className="p-1.5 sm:p-2 hover:bg-[var(--luxury-gold)]/10 rounded-lg transition-colors text-gray-400 hover:text-white flex-shrink-0"
|
||||||
>
|
>
|
||||||
<X className="w-4 h-4 sm:w-5 sm:h-5" />
|
<X className="w-4 h-4 sm:w-5 sm:h-5" />
|
||||||
</button>
|
</button>
|
||||||
@@ -143,7 +143,7 @@ const StripePaymentModal: React.FC<StripePaymentModalProps> = ({
|
|||||||
<div className="flex-1 overflow-y-auto p-3 sm:p-4 md:p-6">
|
<div className="flex-1 overflow-y-auto p-3 sm:p-4 md:p-6">
|
||||||
{loading ? (
|
{loading ? (
|
||||||
<div className="flex flex-col items-center justify-center py-8 sm:py-12">
|
<div className="flex flex-col items-center justify-center py-8 sm:py-12">
|
||||||
<Loader2 className="w-10 h-10 sm:w-12 sm:h-12 animate-spin text-[#d4af37] mb-3 sm:mb-4" />
|
<Loader2 className="w-10 h-10 sm:w-12 sm:h-12 animate-spin text-[var(--luxury-gold)] mb-3 sm:mb-4" />
|
||||||
<p className="text-xs sm:text-sm text-gray-400">Initializing payment...</p>
|
<p className="text-xs sm:text-sm text-gray-400">Initializing payment...</p>
|
||||||
</div>
|
</div>
|
||||||
) : error ? (
|
) : error ? (
|
||||||
@@ -171,7 +171,7 @@ const StripePaymentModal: React.FC<StripePaymentModalProps> = ({
|
|||||||
</Elements>
|
</Elements>
|
||||||
) : (
|
) : (
|
||||||
<div className="flex flex-col items-center justify-center py-8 sm:py-12">
|
<div className="flex flex-col items-center justify-center py-8 sm:py-12">
|
||||||
<Loader2 className="w-10 h-10 sm:w-12 sm:h-12 animate-spin text-[#d4af37] mb-3 sm:mb-4" />
|
<Loader2 className="w-10 h-10 sm:w-12 sm:h-12 animate-spin text-[var(--luxury-gold)] mb-3 sm:mb-4" />
|
||||||
<p className="text-xs sm:text-sm text-gray-400">Loading payment form...</p>
|
<p className="text-xs sm:text-sm text-gray-400">Loading payment form...</p>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|||||||
@@ -143,7 +143,7 @@ const BannerCarousel: React.FC<BannerCarouselProps> = ({
|
|||||||
<div className="relative w-full flex flex-col items-center">
|
<div className="relative w-full flex flex-col items-center">
|
||||||
{}
|
{}
|
||||||
<div
|
<div
|
||||||
className="w-0 h-0.5 bg-gradient-to-r from-transparent via-[#d4af37] to-transparent mb-4 sm:mb-6 opacity-90
|
className="w-0 h-0.5 bg-gradient-to-r from-transparent via-[var(--luxury-gold)] to-transparent mb-4 sm:mb-6 opacity-90
|
||||||
animate-lineExpand"
|
animate-lineExpand"
|
||||||
style={{
|
style={{
|
||||||
animation: 'lineExpand 1.2s cubic-bezier(0.4, 0, 0.2, 1) 0.3s forwards',
|
animation: 'lineExpand 1.2s cubic-bezier(0.4, 0, 0.2, 1) 0.3s forwards',
|
||||||
@@ -168,7 +168,7 @@ const BannerCarousel: React.FC<BannerCarouselProps> = ({
|
|||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<span
|
<span
|
||||||
className="bg-gradient-to-b from-white via-white via-[#f5d76e] to-[#d4af37] bg-clip-text text-transparent
|
className="bg-gradient-to-b from-white via-white via-[var(--luxury-gold-light)] to-[var(--luxury-gold)] bg-clip-text text-transparent
|
||||||
animate-gradientShift"
|
animate-gradientShift"
|
||||||
style={{
|
style={{
|
||||||
backgroundSize: '200% 200%',
|
backgroundSize: '200% 200%',
|
||||||
@@ -201,7 +201,7 @@ const BannerCarousel: React.FC<BannerCarouselProps> = ({
|
|||||||
|
|
||||||
{}
|
{}
|
||||||
<div
|
<div
|
||||||
className="w-0 h-0.5 bg-gradient-to-r from-transparent via-[#d4af37] to-transparent mt-3 sm:mt-4 opacity-90
|
className="w-0 h-0.5 bg-gradient-to-r from-transparent via-[var(--luxury-gold)] to-transparent mt-3 sm:mt-4 opacity-90
|
||||||
animate-lineExpand"
|
animate-lineExpand"
|
||||||
style={{
|
style={{
|
||||||
animation: 'lineExpand 1.2s cubic-bezier(0.4, 0, 0.2, 1) 0.9s forwards',
|
animation: 'lineExpand 1.2s cubic-bezier(0.4, 0, 0.2, 1) 0.9s forwards',
|
||||||
@@ -229,7 +229,7 @@ const BannerCarousel: React.FC<BannerCarouselProps> = ({
|
|||||||
{[...Array(3)].map((_, i) => (
|
{[...Array(3)].map((_, i) => (
|
||||||
<div
|
<div
|
||||||
key={i}
|
key={i}
|
||||||
className="absolute w-1 h-1 bg-[#d4af37] rounded-full opacity-40"
|
className="absolute w-1 h-1 bg-[var(--luxury-gold)] rounded-full opacity-40"
|
||||||
style={{
|
style={{
|
||||||
left: `${20 + i * 30}%`,
|
left: `${20 + i * 30}%`,
|
||||||
top: `${30 + i * 20}%`,
|
top: `${30 + i * 20}%`,
|
||||||
@@ -314,7 +314,7 @@ const BannerCarousel: React.FC<BannerCarouselProps> = ({
|
|||||||
transition-all duration-300 cursor-pointer
|
transition-all duration-300 cursor-pointer
|
||||||
${
|
${
|
||||||
index === currentIndex
|
index === currentIndex
|
||||||
? 'bg-gradient-to-r from-[#d4af37] to-[#f5d76e] w-8 sm:w-10 shadow-lg shadow-[#d4af37]/50'
|
? 'bg-gradient-to-r from-[var(--luxury-gold)] to-[var(--luxury-gold-light)] w-8 sm:w-10 shadow-lg shadow-[var(--luxury-gold)]/50'
|
||||||
: 'bg-white/40 hover:bg-white/70 w-2 sm:h-2.5 hover:scale-125'
|
: 'bg-white/40 hover:bg-white/70 w-2 sm:h-2.5 hover:scale-125'
|
||||||
}`}
|
}`}
|
||||||
aria-label={`Go to banner ${index + 1}`}
|
aria-label={`Go to banner ${index + 1}`}
|
||||||
|
|||||||
@@ -77,9 +77,9 @@ const Pagination: React.FC<PaginationProps> = ({
|
|||||||
<button
|
<button
|
||||||
onClick={() => handlePageChange(currentPage - 1)}
|
onClick={() => handlePageChange(currentPage - 1)}
|
||||||
disabled={currentPage === 1}
|
disabled={currentPage === 1}
|
||||||
className="px-4 py-2 border border-[#d4af37]/30
|
className="px-4 py-2 border border-[var(--luxury-gold)]/30
|
||||||
rounded-lg bg-[#0a0a0a] text-gray-300
|
rounded-lg bg-[#0a0a0a] text-gray-300
|
||||||
hover:bg-[#1a1a1a] hover:border-[#d4af37] hover:text-[#d4af37]
|
hover:bg-[#1a1a1a] hover:border-[var(--luxury-gold)] hover:text-[var(--luxury-gold)]
|
||||||
disabled:opacity-50 disabled:cursor-not-allowed
|
disabled:opacity-50 disabled:cursor-not-allowed
|
||||||
transition-all duration-300 font-medium tracking-wide"
|
transition-all duration-300 font-medium tracking-wide"
|
||||||
aria-label="Previous page"
|
aria-label="Previous page"
|
||||||
@@ -110,8 +110,8 @@ const Pagination: React.FC<PaginationProps> = ({
|
|||||||
className={`px-5 py-2 rounded-lg transition-all duration-300
|
className={`px-5 py-2 rounded-lg transition-all duration-300
|
||||||
font-medium tracking-wide ${
|
font-medium tracking-wide ${
|
||||||
isActive
|
isActive
|
||||||
? 'bg-gradient-to-r from-[#d4af37] to-[#c9a227] text-[#0f0f0f] shadow-lg shadow-[#d4af37]/30'
|
? 'bg-gradient-to-r from-[var(--luxury-gold)] to-[var(--luxury-gold-dark)] text-[#0f0f0f] shadow-lg shadow-[var(--luxury-gold)]/30'
|
||||||
: 'border border-[#d4af37]/30 bg-[#0a0a0a] text-gray-300 hover:bg-[#1a1a1a] hover:border-[#d4af37] hover:text-[#d4af37]'
|
: 'border border-[var(--luxury-gold)]/30 bg-[#0a0a0a] text-gray-300 hover:bg-[#1a1a1a] hover:border-[var(--luxury-gold)] hover:text-[var(--luxury-gold)]'
|
||||||
}`}
|
}`}
|
||||||
aria-label={`Page ${pageNum}`}
|
aria-label={`Page ${pageNum}`}
|
||||||
aria-current={isActive ? 'page' : undefined}
|
aria-current={isActive ? 'page' : undefined}
|
||||||
@@ -125,9 +125,9 @@ const Pagination: React.FC<PaginationProps> = ({
|
|||||||
<button
|
<button
|
||||||
onClick={() => handlePageChange(currentPage + 1)}
|
onClick={() => handlePageChange(currentPage + 1)}
|
||||||
disabled={currentPage === totalPages}
|
disabled={currentPage === totalPages}
|
||||||
className="px-4 py-2 border border-[#d4af37]/30
|
className="px-4 py-2 border border-[var(--luxury-gold)]/30
|
||||||
rounded-lg bg-[#0a0a0a] text-gray-300
|
rounded-lg bg-[#0a0a0a] text-gray-300
|
||||||
hover:bg-[#1a1a1a] hover:border-[#d4af37] hover:text-[#d4af37]
|
hover:bg-[#1a1a1a] hover:border-[var(--luxury-gold)] hover:text-[var(--luxury-gold)]
|
||||||
disabled:opacity-50 disabled:cursor-not-allowed
|
disabled:opacity-50 disabled:cursor-not-allowed
|
||||||
transition-all duration-300 font-medium tracking-wide"
|
transition-all duration-300 font-medium tracking-wide"
|
||||||
aria-label="Next page"
|
aria-label="Next page"
|
||||||
|
|||||||
@@ -71,7 +71,7 @@ const RatingStars: React.FC<RatingStarsProps> = ({
|
|||||||
<Star
|
<Star
|
||||||
className={`${sizeClasses[size]} ${
|
className={`${sizeClasses[size]} ${
|
||||||
isFilled
|
isFilled
|
||||||
? 'text-[#d4af37] fill-[#d4af37]'
|
? 'text-[var(--luxury-gold)] fill-[var(--luxury-gold)]'
|
||||||
: 'text-gray-500'
|
: 'text-gray-500'
|
||||||
}`}
|
}`}
|
||||||
/>
|
/>
|
||||||
|
|||||||
@@ -184,13 +184,13 @@ const ReviewSection: React.FC<ReviewSectionProps> = ({
|
|||||||
return (
|
return (
|
||||||
<div className="space-y-4">
|
<div className="space-y-4">
|
||||||
{}
|
{}
|
||||||
<div className="bg-gradient-to-br from-[#1a1a1a] to-[#0a0a0a] rounded-lg border border-[#d4af37]/20 p-3 sm:p-4 backdrop-blur-xl shadow-lg shadow-[#d4af37]/5">
|
<div className="bg-gradient-to-br from-[#1a1a1a] to-[#0a0a0a] rounded-lg border border-[var(--luxury-gold)]/20 p-3 sm:p-4 backdrop-blur-xl shadow-lg shadow-[var(--luxury-gold)]/5">
|
||||||
<h3 className="text-sm sm:text-base font-serif font-semibold text-white mb-3 tracking-wide">
|
<h3 className="text-sm sm:text-base font-serif font-semibold text-white mb-3 tracking-wide">
|
||||||
Customer Reviews
|
Customer Reviews
|
||||||
</h3>
|
</h3>
|
||||||
<div className="flex items-center gap-4">
|
<div className="flex items-center gap-4">
|
||||||
<div className="text-center">
|
<div className="text-center">
|
||||||
<div className="text-2xl sm:text-3xl font-serif font-bold bg-gradient-to-r from-[#d4af37] to-[#f5d76e] bg-clip-text text-transparent">
|
<div className="text-2xl sm:text-3xl font-serif font-bold bg-gradient-to-r from-[var(--luxury-gold)] to-[var(--luxury-gold-light)] bg-clip-text text-transparent">
|
||||||
{totalReviews > 0 && averageRating > 0
|
{totalReviews > 0 && averageRating > 0
|
||||||
? averageRating.toFixed(1)
|
? averageRating.toFixed(1)
|
||||||
: totalReviews === 0
|
: totalReviews === 0
|
||||||
@@ -214,7 +214,7 @@ const ReviewSection: React.FC<ReviewSectionProps> = ({
|
|||||||
|
|
||||||
{}
|
{}
|
||||||
{isAuthenticated ? (
|
{isAuthenticated ? (
|
||||||
<div className="bg-gradient-to-br from-[#1a1a1a] to-[#0a0a0a] rounded-lg border border-[#d4af37]/20 p-3 sm:p-4 backdrop-blur-xl shadow-lg shadow-[#d4af37]/5">
|
<div className="bg-gradient-to-br from-[#1a1a1a] to-[#0a0a0a] rounded-lg border border-[var(--luxury-gold)]/20 p-3 sm:p-4 backdrop-blur-xl shadow-lg shadow-[var(--luxury-gold)]/5">
|
||||||
<h4 className="text-xs sm:text-sm font-serif font-semibold text-white mb-3 tracking-wide">
|
<h4 className="text-xs sm:text-sm font-serif font-semibold text-white mb-3 tracking-wide">
|
||||||
Write Your Review
|
Write Your Review
|
||||||
</h4>
|
</h4>
|
||||||
@@ -263,9 +263,9 @@ const ReviewSection: React.FC<ReviewSectionProps> = ({
|
|||||||
id="comment"
|
id="comment"
|
||||||
rows={3}
|
rows={3}
|
||||||
className="w-full px-2.5 py-1.5 bg-[#0a0a0a] border
|
className="w-full px-2.5 py-1.5 bg-[#0a0a0a] border
|
||||||
border-[#d4af37]/20 rounded-lg text-white placeholder-gray-500 text-xs sm:text-sm
|
border-[var(--luxury-gold)]/20 rounded-lg text-white placeholder-gray-500 text-xs sm:text-sm
|
||||||
focus:ring-2 focus:ring-[#d4af37]/50
|
focus:ring-2 focus:ring-[var(--luxury-gold)]/50
|
||||||
focus:border-[#d4af37] transition-all duration-300
|
focus:border-[var(--luxury-gold)] transition-all duration-300
|
||||||
font-light tracking-wide resize-none"
|
font-light tracking-wide resize-none"
|
||||||
placeholder="Share your experience..."
|
placeholder="Share your experience..."
|
||||||
/>
|
/>
|
||||||
@@ -292,27 +292,27 @@ const ReviewSection: React.FC<ReviewSectionProps> = ({
|
|||||||
<button
|
<button
|
||||||
type="submit"
|
type="submit"
|
||||||
disabled={submitting}
|
disabled={submitting}
|
||||||
className="px-4 py-2 bg-gradient-to-r from-[#d4af37] to-[#c9a227]
|
className="px-4 py-2 bg-gradient-to-r from-[var(--luxury-gold)] to-[var(--luxury-gold-dark)]
|
||||||
text-[#0f0f0f] rounded-sm hover:from-[#f5d76e] hover:to-[#d4af37]
|
text-[#0f0f0f] rounded-sm hover:from-[var(--luxury-gold-light)] hover:to-[var(--luxury-gold)]
|
||||||
disabled:bg-gray-800 disabled:text-gray-500
|
disabled:bg-gray-800 disabled:text-gray-500
|
||||||
disabled:cursor-not-allowed
|
disabled:cursor-not-allowed
|
||||||
transition-all duration-300 font-medium text-xs sm:text-sm
|
transition-all duration-300 font-medium text-xs sm:text-sm
|
||||||
shadow-sm shadow-[#d4af37]/30 tracking-wide"
|
shadow-sm shadow-[var(--luxury-gold)]/30 tracking-wide"
|
||||||
>
|
>
|
||||||
{submitting ? 'Submitting...' : 'Submit Review'}
|
{submitting ? 'Submitting...' : 'Submit Review'}
|
||||||
</button>
|
</button>
|
||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
) : (
|
) : (
|
||||||
<div className="bg-gradient-to-br from-[#d4af37]/10 to-[#c9a227]/5 border border-[#d4af37]/30
|
<div className="bg-gradient-to-br from-[var(--luxury-gold)]/10 to-[var(--luxury-gold-dark)]/5 border border-[var(--luxury-gold)]/30
|
||||||
rounded-lg p-3 sm:p-4 text-center backdrop-blur-sm"
|
rounded-lg p-3 sm:p-4 text-center backdrop-blur-sm"
|
||||||
>
|
>
|
||||||
<p className="text-[#d4af37] text-xs sm:text-sm font-light">
|
<p className="text-[var(--luxury-gold)] text-xs sm:text-sm font-light">
|
||||||
Please{' '}
|
Please{' '}
|
||||||
<button
|
<button
|
||||||
onClick={() => openModal('login')}
|
onClick={() => openModal('login')}
|
||||||
className="font-semibold underline
|
className="font-semibold underline
|
||||||
hover:text-[#f5d76e] transition-colors"
|
hover:text-[var(--luxury-gold-light)] transition-colors"
|
||||||
>
|
>
|
||||||
login
|
login
|
||||||
</button>{' '}
|
</button>{' '}
|
||||||
@@ -332,7 +332,7 @@ const ReviewSection: React.FC<ReviewSectionProps> = ({
|
|||||||
{Array.from({ length: 3 }).map((_, index) => (
|
{Array.from({ length: 3 }).map((_, index) => (
|
||||||
<div
|
<div
|
||||||
key={index}
|
key={index}
|
||||||
className="bg-gradient-to-br from-[#1a1a1a] to-[#0a0a0a] rounded-lg border border-[#d4af37]/20 p-3
|
className="bg-gradient-to-br from-[#1a1a1a] to-[#0a0a0a] rounded-lg border border-[var(--luxury-gold)]/20 p-3
|
||||||
animate-pulse"
|
animate-pulse"
|
||||||
>
|
>
|
||||||
<div className="h-3 bg-gray-700
|
<div className="h-3 bg-gray-700
|
||||||
@@ -345,7 +345,7 @@ const ReviewSection: React.FC<ReviewSectionProps> = ({
|
|||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
) : reviews.length === 0 ? (
|
) : reviews.length === 0 ? (
|
||||||
<div className="text-center py-6 sm:py-8 bg-gradient-to-br from-[#1a1a1a] to-[#0a0a0a] rounded-lg border border-[#d4af37]/20 p-4"
|
<div className="text-center py-6 sm:py-8 bg-gradient-to-br from-[#1a1a1a] to-[#0a0a0a] rounded-lg border border-[var(--luxury-gold)]/20 p-4"
|
||||||
>
|
>
|
||||||
<p className="text-gray-300 text-sm sm:text-base font-light">
|
<p className="text-gray-300 text-sm sm:text-base font-light">
|
||||||
No reviews yet
|
No reviews yet
|
||||||
@@ -359,8 +359,8 @@ const ReviewSection: React.FC<ReviewSectionProps> = ({
|
|||||||
{reviews.map((review) => (
|
{reviews.map((review) => (
|
||||||
<div
|
<div
|
||||||
key={review.id}
|
key={review.id}
|
||||||
className="bg-gradient-to-br from-[#1a1a1a] to-[#0a0a0a] rounded-lg border border-[#d4af37]/20
|
className="bg-gradient-to-br from-[#1a1a1a] to-[#0a0a0a] rounded-lg border border-[var(--luxury-gold)]/20
|
||||||
p-3 sm:p-4 backdrop-blur-xl shadow-sm shadow-[#d4af37]/5"
|
p-3 sm:p-4 backdrop-blur-xl shadow-sm shadow-[var(--luxury-gold)]/5"
|
||||||
>
|
>
|
||||||
<div className="flex items-start
|
<div className="flex items-start
|
||||||
justify-between mb-2"
|
justify-between mb-2"
|
||||||
|
|||||||
@@ -75,8 +75,8 @@ const RoomCard: React.FC<RoomCardProps> = ({ room, compact = false }) => {
|
|||||||
className={`luxury-card overflow-hidden group h-full flex flex-col
|
className={`luxury-card overflow-hidden group h-full flex flex-col
|
||||||
border-t-2 transition-all duration-300
|
border-t-2 transition-all duration-300
|
||||||
${room.featured
|
${room.featured
|
||||||
? 'border-[#d4af37] shadow-lg shadow-[#d4af37]/20 hover:border-[#d4af37] hover:shadow-luxury-gold'
|
? 'border-[var(--luxury-gold)] shadow-lg shadow-[var(--luxury-gold)]/20 hover:border-[var(--luxury-gold)] hover:shadow-luxury-gold'
|
||||||
: 'border-transparent hover:border-[#d4af37] hover:shadow-luxury-gold'
|
: 'border-transparent hover:border-[var(--luxury-gold)] hover:shadow-luxury-gold'
|
||||||
}`}
|
}`}
|
||||||
>
|
>
|
||||||
{}
|
{}
|
||||||
@@ -112,10 +112,10 @@ const RoomCard: React.FC<RoomCardProps> = ({ room, compact = false }) => {
|
|||||||
{room.featured && (
|
{room.featured && (
|
||||||
<div
|
<div
|
||||||
className="absolute top-3 left-3 z-20
|
className="absolute top-3 left-3 z-20
|
||||||
bg-gradient-to-r from-[#d4af37] to-[#c9a227]
|
bg-gradient-to-r from-[var(--luxury-gold)] to-[var(--luxury-gold-dark)]
|
||||||
text-[#0f0f0f] px-3 py-1.5
|
text-[#0f0f0f] px-3 py-1.5
|
||||||
rounded-sm text-xs font-medium tracking-wide
|
rounded-sm text-xs font-medium tracking-wide
|
||||||
shadow-lg shadow-[#d4af37]/30 backdrop-blur-sm
|
shadow-lg shadow-[var(--luxury-gold)]/30 backdrop-blur-sm
|
||||||
flex items-center gap-1.5"
|
flex items-center gap-1.5"
|
||||||
>
|
>
|
||||||
<Crown className="w-4 h-4 fill-[#0f0f0f] text-[#0f0f0f]" />
|
<Crown className="w-4 h-4 fill-[#0f0f0f] text-[#0f0f0f]" />
|
||||||
@@ -153,7 +153,7 @@ const RoomCard: React.FC<RoomCardProps> = ({ room, compact = false }) => {
|
|||||||
{room.featured && (
|
{room.featured && (
|
||||||
<Crown
|
<Crown
|
||||||
className={`${compact ? 'w-4 h-4' : 'w-5 h-5'}
|
className={`${compact ? 'w-4 h-4' : 'w-5 h-5'}
|
||||||
text-[#d4af37] fill-[#d4af37]
|
text-[var(--luxury-gold)] fill-[var(--luxury-gold)]
|
||||||
drop-shadow-lg animate-pulse`}
|
drop-shadow-lg animate-pulse`}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
@@ -165,7 +165,7 @@ const RoomCard: React.FC<RoomCardProps> = ({ room, compact = false }) => {
|
|||||||
className={`flex items-center text-gray-600 font-light tracking-wide
|
className={`flex items-center text-gray-600 font-light tracking-wide
|
||||||
${compact ? 'text-xs mb-1.5' : 'text-xs sm:text-sm mb-2'}`}
|
${compact ? 'text-xs mb-1.5' : 'text-xs sm:text-sm mb-2'}`}
|
||||||
>
|
>
|
||||||
<MapPin className={`${compact ? 'w-3 h-3' : 'w-4 h-4'} mr-1.5 text-[#d4af37]`} />
|
<MapPin className={`${compact ? 'w-3 h-3' : 'w-4 h-4'} mr-1.5 text-[var(--luxury-gold)]`} />
|
||||||
<span>
|
<span>
|
||||||
Room {room.room_number} - Floor {room.floor}
|
Room {room.room_number} - Floor {room.floor}
|
||||||
</span>
|
</span>
|
||||||
@@ -182,7 +182,7 @@ const RoomCard: React.FC<RoomCardProps> = ({ room, compact = false }) => {
|
|||||||
{}
|
{}
|
||||||
<div className={`flex items-center justify-between ${compact ? 'mb-1.5' : 'mb-3'}`}>
|
<div className={`flex items-center justify-between ${compact ? 'mb-1.5' : 'mb-3'}`}>
|
||||||
<div className="flex items-center text-gray-700">
|
<div className="flex items-center text-gray-700">
|
||||||
<Users className={`${compact ? 'w-3 h-3' : 'w-4 h-4'} mr-1.5 text-[#d4af37]`} />
|
<Users className={`${compact ? 'w-3 h-3' : 'w-4 h-4'} mr-1.5 text-[var(--luxury-gold)]`} />
|
||||||
<span className={`font-light tracking-wide ${compact ? 'text-xs' : 'text-sm'}`}>
|
<span className={`font-light tracking-wide ${compact ? 'text-xs' : 'text-sm'}`}>
|
||||||
{room.capacity || roomType.capacity} guests
|
{room.capacity || roomType.capacity} guests
|
||||||
</span>
|
</span>
|
||||||
@@ -191,7 +191,7 @@ const RoomCard: React.FC<RoomCardProps> = ({ room, compact = false }) => {
|
|||||||
{room.average_rating != null && (
|
{room.average_rating != null && (
|
||||||
<div className="flex items-center">
|
<div className="flex items-center">
|
||||||
<Star
|
<Star
|
||||||
className={`${compact ? 'w-3 h-3' : 'w-4 h-4'} text-[#d4af37] mr-1`}
|
className={`${compact ? 'w-3 h-3' : 'w-4 h-4'} text-[var(--luxury-gold)] mr-1`}
|
||||||
fill="#d4af37"
|
fill="#d4af37"
|
||||||
/>
|
/>
|
||||||
<span className={`font-semibold text-gray-900 ${compact ? 'text-xs' : 'text-sm'}`}>
|
<span className={`font-semibold text-gray-900 ${compact ? 'text-xs' : 'text-sm'}`}>
|
||||||
@@ -213,13 +213,13 @@ const RoomCard: React.FC<RoomCardProps> = ({ room, compact = false }) => {
|
|||||||
<div
|
<div
|
||||||
key={index}
|
key={index}
|
||||||
className="flex items-center gap-1
|
className="flex items-center gap-1
|
||||||
text-gray-700 text-xs bg-[#d4af37]/10
|
text-gray-700 text-xs bg-[var(--luxury-gold)]/10
|
||||||
border border-[#d4af37]/20
|
border border-[var(--luxury-gold)]/20
|
||||||
px-2 sm:px-2.5 py-1 sm:py-1.5 rounded-sm
|
px-2 sm:px-2.5 py-1 sm:py-1.5 rounded-sm
|
||||||
font-light tracking-wide"
|
font-light tracking-wide"
|
||||||
title={amenity}
|
title={amenity}
|
||||||
>
|
>
|
||||||
<span className="text-[#d4af37]">
|
<span className="text-[var(--luxury-gold)]">
|
||||||
{amenityIcons[amenity.toLowerCase()] ||
|
{amenityIcons[amenity.toLowerCase()] ||
|
||||||
<span>•</span>}
|
<span>•</span>}
|
||||||
</span>
|
</span>
|
||||||
|
|||||||
@@ -4,8 +4,8 @@ const RoomCardSkeleton: React.FC = () => {
|
|||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
className="bg-gradient-to-br from-[#1a1a1a] to-[#0a0a0a]
|
className="bg-gradient-to-br from-[#1a1a1a] to-[#0a0a0a]
|
||||||
rounded-xl border border-[#d4af37]/20
|
rounded-xl border border-[var(--luxury-gold)]/20
|
||||||
overflow-hidden animate-pulse shadow-lg shadow-[#d4af37]/5"
|
overflow-hidden animate-pulse shadow-lg shadow-[var(--luxury-gold)]/5"
|
||||||
>
|
>
|
||||||
{}
|
{}
|
||||||
<div className="h-40 sm:h-44 md:h-48 lg:h-52 bg-gradient-to-br from-gray-800 to-gray-900" />
|
<div className="h-40 sm:h-44 md:h-48 lg:h-52 bg-gradient-to-br from-gray-800 to-gray-900" />
|
||||||
@@ -40,7 +40,7 @@ const RoomCardSkeleton: React.FC = () => {
|
|||||||
{}
|
{}
|
||||||
<div
|
<div
|
||||||
className="flex items-center justify-between
|
className="flex items-center justify-between
|
||||||
pt-3 border-t border-[#d4af37]/20"
|
pt-3 border-t border-[var(--luxury-gold)]/20"
|
||||||
>
|
>
|
||||||
<div>
|
<div>
|
||||||
<div className="h-3 bg-gray-800 rounded w-12 mb-1" />
|
<div className="h-3 bg-gray-800 rounded w-12 mb-1" />
|
||||||
|
|||||||
@@ -150,11 +150,11 @@ const RoomCarousel: React.FC<RoomCarouselProps> = ({
|
|||||||
disabled={isAnimating || currentIndex === 0}
|
disabled={isAnimating || currentIndex === 0}
|
||||||
className="absolute left-0 top-1/2 -translate-y-1/2 -translate-x-2 sm:-translate-x-4
|
className="absolute left-0 top-1/2 -translate-y-1/2 -translate-x-2 sm:-translate-x-4
|
||||||
z-10 w-8 h-8 sm:w-9 sm:h-9
|
z-10 w-8 h-8 sm:w-9 sm:h-9
|
||||||
bg-gradient-to-r from-[#d4af37] to-[#c9a227]
|
bg-gradient-to-r from-[var(--luxury-gold)] to-[var(--luxury-gold-dark)]
|
||||||
text-white rounded-full
|
text-white rounded-full
|
||||||
flex items-center justify-center
|
flex items-center justify-center
|
||||||
shadow-lg shadow-[#d4af37]/40
|
shadow-lg shadow-[var(--luxury-gold)]/40
|
||||||
hover:shadow-xl hover:shadow-[#d4af37]/50
|
hover:shadow-xl hover:shadow-[var(--luxury-gold)]/50
|
||||||
hover:scale-110
|
hover:scale-110
|
||||||
active:scale-95
|
active:scale-95
|
||||||
transition-all duration-300
|
transition-all duration-300
|
||||||
@@ -170,11 +170,11 @@ const RoomCarousel: React.FC<RoomCarouselProps> = ({
|
|||||||
disabled={isAnimating || currentIndex >= rooms.length - 1}
|
disabled={isAnimating || currentIndex >= rooms.length - 1}
|
||||||
className="absolute right-0 top-1/2 -translate-y-1/2 translate-x-2 sm:translate-x-4
|
className="absolute right-0 top-1/2 -translate-y-1/2 translate-x-2 sm:translate-x-4
|
||||||
z-10 w-8 h-8 sm:w-9 sm:h-9
|
z-10 w-8 h-8 sm:w-9 sm:h-9
|
||||||
bg-gradient-to-r from-[#d4af37] to-[#c9a227]
|
bg-gradient-to-r from-[var(--luxury-gold)] to-[var(--luxury-gold-dark)]
|
||||||
text-white rounded-full
|
text-white rounded-full
|
||||||
flex items-center justify-center
|
flex items-center justify-center
|
||||||
shadow-lg shadow-[#d4af37]/40
|
shadow-lg shadow-[var(--luxury-gold)]/40
|
||||||
hover:shadow-xl hover:shadow-[#d4af37]/50
|
hover:shadow-xl hover:shadow-[var(--luxury-gold)]/50
|
||||||
hover:scale-110
|
hover:scale-110
|
||||||
active:scale-95
|
active:scale-95
|
||||||
transition-all duration-300
|
transition-all duration-300
|
||||||
@@ -198,8 +198,8 @@ const RoomCarousel: React.FC<RoomCarouselProps> = ({
|
|||||||
className={`transition-all duration-300 rounded-full
|
className={`transition-all duration-300 rounded-full
|
||||||
${
|
${
|
||||||
index === currentIndex
|
index === currentIndex
|
||||||
? 'w-6 h-1.5 sm:w-7 sm:h-2 bg-gradient-to-r from-[#d4af37] to-[#c9a227] shadow-lg shadow-[#d4af37]/40'
|
? 'w-6 h-1.5 sm:w-7 sm:h-2 bg-gradient-to-r from-[var(--luxury-gold)] to-[var(--luxury-gold-dark)] shadow-lg shadow-[var(--luxury-gold)]/40'
|
||||||
: 'w-1.5 h-1.5 sm:w-2 sm:h-2 bg-gray-300 hover:bg-[#d4af37]/50'
|
: 'w-1.5 h-1.5 sm:w-2 sm:h-2 bg-gray-300 hover:bg-[var(--luxury-gold)]/50'
|
||||||
}
|
}
|
||||||
disabled:cursor-not-allowed
|
disabled:cursor-not-allowed
|
||||||
`}
|
`}
|
||||||
|
|||||||
@@ -250,14 +250,14 @@ const RoomFilter: React.FC<RoomFilterProps> = ({ onFilterChange }) => {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="bg-gradient-to-br from-[#1a1a1a] to-[#0a0a0a]
|
<div className="bg-gradient-to-br from-[#1a1a1a] to-[#0a0a0a]
|
||||||
rounded-xl border border-[#d4af37]/30
|
rounded-xl border border-[var(--luxury-gold)]/30
|
||||||
backdrop-blur-xl shadow-2xl shadow-[#d4af37]/10
|
backdrop-blur-xl shadow-2xl shadow-[var(--luxury-gold)]/10
|
||||||
p-4 sm:p-5 md:p-6"
|
p-4 sm:p-5 md:p-6"
|
||||||
>
|
>
|
||||||
<div className="flex items-center gap-2 sm:gap-3 mb-4 sm:mb-5 md:mb-6">
|
<div className="flex items-center gap-2 sm:gap-3 mb-4 sm:mb-5 md:mb-6">
|
||||||
<div className="p-1.5 sm:p-2 bg-[#d4af37]/10 rounded-lg
|
<div className="p-1.5 sm:p-2 bg-[var(--luxury-gold)]/10 rounded-lg
|
||||||
border border-[#d4af37]/30">
|
border border-[var(--luxury-gold)]/30">
|
||||||
<svg className="w-4 h-4 sm:w-5 sm:h-5 text-[#d4af37]" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
<svg className="w-4 h-4 sm:w-5 sm:h-5 text-[var(--luxury-gold)]" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||||
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M3 4a1 1 0 011-1h16a1 1 0 011 1v2.586a1 1 0 01-.293.707l-6.414 6.414a1 1 0 00-.293.707V17l-4 4v-6.586a1 1 0 00-.293-.707L3.293 7.293A1 1 0 013 6.586V4z" />
|
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M3 4a1 1 0 011-1h16a1 1 0 011 1v2.586a1 1 0 01-.293.707l-6.414 6.414a1 1 0 00-.293.707V17l-4 4v-6.586a1 1 0 00-.293-.707L3.293 7.293A1 1 0 013 6.586V4z" />
|
||||||
</svg>
|
</svg>
|
||||||
</div>
|
</div>
|
||||||
@@ -282,12 +282,12 @@ const RoomFilter: React.FC<RoomFilterProps> = ({ onFilterChange }) => {
|
|||||||
value={filters.type || ''}
|
value={filters.type || ''}
|
||||||
onChange={handleInputChange}
|
onChange={handleInputChange}
|
||||||
className="w-full px-4 py-3.5 bg-[#1a1a1a] border-2
|
className="w-full px-4 py-3.5 bg-[#1a1a1a] border-2
|
||||||
border-[#d4af37]/30 rounded-lg text-white text-base
|
border-[var(--luxury-gold)]/30 rounded-lg text-white text-base
|
||||||
placeholder-gray-400 focus:ring-2
|
placeholder-gray-400 focus:ring-2
|
||||||
focus:ring-[#d4af37]/60 focus:border-[#d4af37]
|
focus:ring-[var(--luxury-gold)]/60 focus:border-[var(--luxury-gold)]
|
||||||
transition-all duration-300 font-normal tracking-wide
|
transition-all duration-300 font-normal tracking-wide
|
||||||
appearance-none cursor-pointer
|
appearance-none cursor-pointer
|
||||||
hover:border-[#d4af37]/50"
|
hover:border-[var(--luxury-gold)]/50"
|
||||||
>
|
>
|
||||||
<option value="" className="bg-[#1a1a1a] text-white">All Room Types</option>
|
<option value="" className="bg-[#1a1a1a] text-white">All Room Types</option>
|
||||||
<option value="Standard Room" className="bg-[#1a1a1a] text-white">Standard Room</option>
|
<option value="Standard Room" className="bg-[#1a1a1a] text-white">Standard Room</option>
|
||||||
@@ -297,7 +297,7 @@ const RoomFilter: React.FC<RoomFilterProps> = ({ onFilterChange }) => {
|
|||||||
<option value="Twin Room" className="bg-[#1a1a1a] text-white">Twin Room</option>
|
<option value="Twin Room" className="bg-[#1a1a1a] text-white">Twin Room</option>
|
||||||
</select>
|
</select>
|
||||||
<div className="absolute right-3 top-1/2 -translate-y-1/2 pointer-events-none">
|
<div className="absolute right-3 top-1/2 -translate-y-1/2 pointer-events-none">
|
||||||
<svg className="w-5 h-5 text-[#d4af37]" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
<svg className="w-5 h-5 text-[var(--luxury-gold)]" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||||
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M19 9l-7 7-7-7" />
|
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M19 9l-7 7-7-7" />
|
||||||
</svg>
|
</svg>
|
||||||
</div>
|
</div>
|
||||||
@@ -311,7 +311,7 @@ const RoomFilter: React.FC<RoomFilterProps> = ({ onFilterChange }) => {
|
|||||||
htmlFor="from"
|
htmlFor="from"
|
||||||
className="block text-sm font-medium text-gray-200 mb-2 tracking-wide flex items-center gap-2"
|
className="block text-sm font-medium text-gray-200 mb-2 tracking-wide flex items-center gap-2"
|
||||||
>
|
>
|
||||||
<Calendar className="w-4 h-4 text-[#d4af37]" />
|
<Calendar className="w-4 h-4 text-[var(--luxury-gold)]" />
|
||||||
Check-in Date
|
Check-in Date
|
||||||
</label>
|
</label>
|
||||||
<div className="relative">
|
<div className="relative">
|
||||||
@@ -331,11 +331,11 @@ const RoomFilter: React.FC<RoomFilterProps> = ({ onFilterChange }) => {
|
|||||||
dateFormat="dd/MM/yyyy"
|
dateFormat="dd/MM/yyyy"
|
||||||
placeholderText="Select check-in"
|
placeholderText="Select check-in"
|
||||||
className="w-full px-4 py-3.5 pl-11 bg-[#1a1a1a] border-2
|
className="w-full px-4 py-3.5 pl-11 bg-[#1a1a1a] border-2
|
||||||
border-[#d4af37]/30 rounded-lg text-white text-base
|
border-[var(--luxury-gold)]/30 rounded-lg text-white text-base
|
||||||
placeholder-gray-400 focus:ring-2
|
placeholder-gray-400 focus:ring-2
|
||||||
focus:ring-[#d4af37]/60 focus:border-[#d4af37]
|
focus:ring-[var(--luxury-gold)]/60 focus:border-[var(--luxury-gold)]
|
||||||
transition-all duration-300 font-normal tracking-wide
|
transition-all duration-300 font-normal tracking-wide
|
||||||
hover:border-[#d4af37]/50"
|
hover:border-[var(--luxury-gold)]/50"
|
||||||
wrapperClassName="w-full"
|
wrapperClassName="w-full"
|
||||||
/>
|
/>
|
||||||
{checkInDate && (
|
{checkInDate && (
|
||||||
@@ -349,13 +349,13 @@ const RoomFilter: React.FC<RoomFilterProps> = ({ onFilterChange }) => {
|
|||||||
}
|
}
|
||||||
}}
|
}}
|
||||||
className="absolute right-3 top-1/2 -translate-y-1/2
|
className="absolute right-3 top-1/2 -translate-y-1/2
|
||||||
text-gray-400 hover:text-[#d4af37] transition-colors"
|
text-gray-400 hover:text-[var(--luxury-gold)] transition-colors"
|
||||||
>
|
>
|
||||||
<X className="w-4 h-4" />
|
<X className="w-4 h-4" />
|
||||||
</button>
|
</button>
|
||||||
)}
|
)}
|
||||||
<Calendar className="absolute left-3 top-1/2 -translate-y-1/2
|
<Calendar className="absolute left-3 top-1/2 -translate-y-1/2
|
||||||
w-5 h-5 text-[#d4af37] pointer-events-none" />
|
w-5 h-5 text-[var(--luxury-gold)] pointer-events-none" />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@@ -364,7 +364,7 @@ const RoomFilter: React.FC<RoomFilterProps> = ({ onFilterChange }) => {
|
|||||||
htmlFor="to"
|
htmlFor="to"
|
||||||
className="block text-sm font-medium text-gray-200 mb-2 tracking-wide flex items-center gap-2"
|
className="block text-sm font-medium text-gray-200 mb-2 tracking-wide flex items-center gap-2"
|
||||||
>
|
>
|
||||||
<Calendar className="w-4 h-4 text-[#d4af37]" />
|
<Calendar className="w-4 h-4 text-[var(--luxury-gold)]" />
|
||||||
Check-out Date
|
Check-out Date
|
||||||
</label>
|
</label>
|
||||||
<div className="relative">
|
<div className="relative">
|
||||||
@@ -383,11 +383,11 @@ const RoomFilter: React.FC<RoomFilterProps> = ({ onFilterChange }) => {
|
|||||||
dateFormat="dd/MM/yyyy"
|
dateFormat="dd/MM/yyyy"
|
||||||
placeholderText={checkInDate ? "Select check-out" : "Select check-in first"}
|
placeholderText={checkInDate ? "Select check-out" : "Select check-in first"}
|
||||||
className="w-full px-4 py-3.5 pl-11 bg-[#1a1a1a] border-2
|
className="w-full px-4 py-3.5 pl-11 bg-[#1a1a1a] border-2
|
||||||
border-[#d4af37]/30 rounded-lg text-white text-base
|
border-[var(--luxury-gold)]/30 rounded-lg text-white text-base
|
||||||
placeholder-gray-400 focus:ring-2
|
placeholder-gray-400 focus:ring-2
|
||||||
focus:ring-[#d4af37]/60 focus:border-[#d4af37]
|
focus:ring-[var(--luxury-gold)]/60 focus:border-[var(--luxury-gold)]
|
||||||
transition-all duration-300 font-normal tracking-wide
|
transition-all duration-300 font-normal tracking-wide
|
||||||
hover:border-[#d4af37]/50
|
hover:border-[var(--luxury-gold)]/50
|
||||||
disabled:opacity-50 disabled:cursor-not-allowed"
|
disabled:opacity-50 disabled:cursor-not-allowed"
|
||||||
wrapperClassName="w-full"
|
wrapperClassName="w-full"
|
||||||
/>
|
/>
|
||||||
@@ -399,13 +399,13 @@ const RoomFilter: React.FC<RoomFilterProps> = ({ onFilterChange }) => {
|
|||||||
setCheckOutDate(null);
|
setCheckOutDate(null);
|
||||||
}}
|
}}
|
||||||
className="absolute right-3 top-1/2 -translate-y-1/2
|
className="absolute right-3 top-1/2 -translate-y-1/2
|
||||||
text-gray-400 hover:text-[#d4af37] transition-colors"
|
text-gray-400 hover:text-[var(--luxury-gold)] transition-colors"
|
||||||
>
|
>
|
||||||
<X className="w-4 h-4" />
|
<X className="w-4 h-4" />
|
||||||
</button>
|
</button>
|
||||||
)}
|
)}
|
||||||
<Calendar className="absolute left-3 top-1/2 -translate-y-1/2
|
<Calendar className="absolute left-3 top-1/2 -translate-y-1/2
|
||||||
w-5 h-5 text-[#d4af37] pointer-events-none" />
|
w-5 h-5 text-[var(--luxury-gold)] pointer-events-none" />
|
||||||
</div>
|
</div>
|
||||||
{checkInDate && !checkOutDate && (
|
{checkInDate && !checkOutDate && (
|
||||||
<p className="mt-1.5 text-xs text-gray-400 font-light">
|
<p className="mt-1.5 text-xs text-gray-400 font-light">
|
||||||
@@ -418,7 +418,7 @@ const RoomFilter: React.FC<RoomFilterProps> = ({ onFilterChange }) => {
|
|||||||
{}
|
{}
|
||||||
<div>
|
<div>
|
||||||
<label className="block text-sm font-medium text-gray-200 mb-3 tracking-wide flex items-center gap-2">
|
<label className="block text-sm font-medium text-gray-200 mb-3 tracking-wide flex items-center gap-2">
|
||||||
<DollarSign className="w-4 h-4 text-[#d4af37]" />
|
<DollarSign className="w-4 h-4 text-[var(--luxury-gold)]" />
|
||||||
Price Range
|
Price Range
|
||||||
</label>
|
</label>
|
||||||
<div className="grid grid-cols-2 gap-4">
|
<div className="grid grid-cols-2 gap-4">
|
||||||
@@ -432,7 +432,7 @@ const RoomFilter: React.FC<RoomFilterProps> = ({ onFilterChange }) => {
|
|||||||
</label>
|
</label>
|
||||||
<div className="relative">
|
<div className="relative">
|
||||||
<DollarSign className="absolute left-3 top-1/2 -translate-y-1/2
|
<DollarSign className="absolute left-3 top-1/2 -translate-y-1/2
|
||||||
w-4 h-4 text-[#d4af37] pointer-events-none" />
|
w-4 h-4 text-[var(--luxury-gold)] pointer-events-none" />
|
||||||
<input
|
<input
|
||||||
type="text"
|
type="text"
|
||||||
id="minPrice"
|
id="minPrice"
|
||||||
@@ -447,11 +447,11 @@ const RoomFilter: React.FC<RoomFilterProps> = ({ onFilterChange }) => {
|
|||||||
inputMode="numeric"
|
inputMode="numeric"
|
||||||
pattern="[0-9.]*"
|
pattern="[0-9.]*"
|
||||||
className="w-full px-4 py-3.5 pl-10 bg-[#1a1a1a] border-2
|
className="w-full px-4 py-3.5 pl-10 bg-[#1a1a1a] border-2
|
||||||
border-[#d4af37]/30 rounded-lg text-white text-base
|
border-[var(--luxury-gold)]/30 rounded-lg text-white text-base
|
||||||
placeholder-gray-400 focus:ring-2
|
placeholder-gray-400 focus:ring-2
|
||||||
focus:ring-[#d4af37]/60 focus:border-[#d4af37]
|
focus:ring-[var(--luxury-gold)]/60 focus:border-[var(--luxury-gold)]
|
||||||
transition-all duration-300 font-normal tracking-wide
|
transition-all duration-300 font-normal tracking-wide
|
||||||
hover:border-[#d4af37]/50"
|
hover:border-[var(--luxury-gold)]/50"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -465,7 +465,7 @@ const RoomFilter: React.FC<RoomFilterProps> = ({ onFilterChange }) => {
|
|||||||
</label>
|
</label>
|
||||||
<div className="relative">
|
<div className="relative">
|
||||||
<DollarSign className="absolute left-3 top-1/2 -translate-y-1/2
|
<DollarSign className="absolute left-3 top-1/2 -translate-y-1/2
|
||||||
w-4 h-4 text-[#d4af37] pointer-events-none" />
|
w-4 h-4 text-[var(--luxury-gold)] pointer-events-none" />
|
||||||
<input
|
<input
|
||||||
type="text"
|
type="text"
|
||||||
id="maxPrice"
|
id="maxPrice"
|
||||||
@@ -480,11 +480,11 @@ const RoomFilter: React.FC<RoomFilterProps> = ({ onFilterChange }) => {
|
|||||||
inputMode="numeric"
|
inputMode="numeric"
|
||||||
pattern="[0-9.]*"
|
pattern="[0-9.]*"
|
||||||
className="w-full px-4 py-3.5 pl-10 bg-[#1a1a1a] border-2
|
className="w-full px-4 py-3.5 pl-10 bg-[#1a1a1a] border-2
|
||||||
border-[#d4af37]/30 rounded-lg text-white text-base
|
border-[var(--luxury-gold)]/30 rounded-lg text-white text-base
|
||||||
placeholder-gray-400 focus:ring-2
|
placeholder-gray-400 focus:ring-2
|
||||||
focus:ring-[#d4af37]/60 focus:border-[#d4af37]
|
focus:ring-[var(--luxury-gold)]/60 focus:border-[var(--luxury-gold)]
|
||||||
transition-all duration-300 font-normal tracking-wide
|
transition-all duration-300 font-normal tracking-wide
|
||||||
hover:border-[#d4af37]/50"
|
hover:border-[var(--luxury-gold)]/50"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -498,12 +498,12 @@ const RoomFilter: React.FC<RoomFilterProps> = ({ onFilterChange }) => {
|
|||||||
className="block text-sm font-medium
|
className="block text-sm font-medium
|
||||||
text-gray-200 mb-2 tracking-wide flex items-center gap-2"
|
text-gray-200 mb-2 tracking-wide flex items-center gap-2"
|
||||||
>
|
>
|
||||||
<Users className="w-4 h-4 text-[#d4af37]" />
|
<Users className="w-4 h-4 text-[var(--luxury-gold)]" />
|
||||||
Number of Guests
|
Number of Guests
|
||||||
</label>
|
</label>
|
||||||
<div className="relative">
|
<div className="relative">
|
||||||
<Users className="absolute left-3 top-1/2 -translate-y-1/2
|
<Users className="absolute left-3 top-1/2 -translate-y-1/2
|
||||||
w-5 h-5 text-[#d4af37] pointer-events-none" />
|
w-5 h-5 text-[var(--luxury-gold)] pointer-events-none" />
|
||||||
<input
|
<input
|
||||||
type="number"
|
type="number"
|
||||||
id="capacity"
|
id="capacity"
|
||||||
@@ -514,11 +514,11 @@ const RoomFilter: React.FC<RoomFilterProps> = ({ onFilterChange }) => {
|
|||||||
min="1"
|
min="1"
|
||||||
max="10"
|
max="10"
|
||||||
className="w-full px-4 py-3.5 pl-11 bg-[#1a1a1a] border-2
|
className="w-full px-4 py-3.5 pl-11 bg-[#1a1a1a] border-2
|
||||||
border-[#d4af37]/30 rounded-lg text-white text-base
|
border-[var(--luxury-gold)]/30 rounded-lg text-white text-base
|
||||||
placeholder-gray-400 focus:ring-2
|
placeholder-gray-400 focus:ring-2
|
||||||
focus:ring-[#d4af37]/60 focus:border-[#d4af37]
|
focus:ring-[var(--luxury-gold)]/60 focus:border-[var(--luxury-gold)]
|
||||||
transition-all duration-300 font-normal tracking-wide
|
transition-all duration-300 font-normal tracking-wide
|
||||||
hover:border-[#d4af37]/50
|
hover:border-[var(--luxury-gold)]/50
|
||||||
[appearance:textfield] [&::-webkit-outer-spin-button]:appearance-none
|
[appearance:textfield] [&::-webkit-outer-spin-button]:appearance-none
|
||||||
[&::-webkit-inner-spin-button]:appearance-none"
|
[&::-webkit-inner-spin-button]:appearance-none"
|
||||||
/>
|
/>
|
||||||
@@ -532,29 +532,29 @@ const RoomFilter: React.FC<RoomFilterProps> = ({ onFilterChange }) => {
|
|||||||
</label>
|
</label>
|
||||||
{availableAmenities.length === 0 ? (
|
{availableAmenities.length === 0 ? (
|
||||||
<div className="text-sm text-gray-400 font-light bg-[#1a1a1a]/50
|
<div className="text-sm text-gray-400 font-light bg-[#1a1a1a]/50
|
||||||
border border-[#d4af37]/20 rounded-lg px-4 py-3">
|
border border-[var(--luxury-gold)]/20 rounded-lg px-4 py-3">
|
||||||
Loading amenities...
|
Loading amenities...
|
||||||
</div>
|
</div>
|
||||||
) : (
|
) : (
|
||||||
<div className="bg-[#1a1a1a]/50 border border-[#d4af37]/20 rounded-lg p-3
|
<div className="bg-[#1a1a1a]/50 border border-[var(--luxury-gold)]/20 rounded-lg p-3
|
||||||
max-h-48 overflow-y-auto custom-scrollbar space-y-2">
|
max-h-48 overflow-y-auto custom-scrollbar space-y-2">
|
||||||
{availableAmenities.map((amenity) => (
|
{availableAmenities.map((amenity) => (
|
||||||
<label
|
<label
|
||||||
key={amenity}
|
key={amenity}
|
||||||
className="flex items-center gap-3 text-sm w-full font-normal tracking-wide
|
className="flex items-center gap-3 text-sm w-full font-normal tracking-wide
|
||||||
hover:text-[#d4af37] transition-colors cursor-pointer
|
hover:text-[var(--luxury-gold)] transition-colors cursor-pointer
|
||||||
text-gray-200 group"
|
text-gray-200 group"
|
||||||
>
|
>
|
||||||
<input
|
<input
|
||||||
type="checkbox"
|
type="checkbox"
|
||||||
checked={selectedAmenities.includes(amenity)}
|
checked={selectedAmenities.includes(amenity)}
|
||||||
onChange={() => toggleAmenity(amenity)}
|
onChange={() => toggleAmenity(amenity)}
|
||||||
className="h-5 w-5 accent-[#d4af37] cursor-pointer
|
className="h-5 w-5 accent-[var(--luxury-gold)] cursor-pointer
|
||||||
border-2 border-[#d4af37]/30 rounded
|
border-2 border-[var(--luxury-gold)]/30 rounded
|
||||||
checked:bg-[#d4af37] checked:border-[#d4af37]
|
checked:bg-[var(--luxury-gold)] checked:border-[var(--luxury-gold)]
|
||||||
group-hover:border-[#d4af37] transition-all"
|
group-hover:border-[var(--luxury-gold)] transition-all"
|
||||||
/>
|
/>
|
||||||
<span className="flex-1 group-hover:text-[#d4af37] transition-colors">
|
<span className="flex-1 group-hover:text-[var(--luxury-gold)] transition-colors">
|
||||||
{amenity}
|
{amenity}
|
||||||
</span>
|
</span>
|
||||||
</label>
|
</label>
|
||||||
@@ -562,7 +562,7 @@ const RoomFilter: React.FC<RoomFilterProps> = ({ onFilterChange }) => {
|
|||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
{selectedAmenities.length > 0 && (
|
{selectedAmenities.length > 0 && (
|
||||||
<p className="mt-2 text-xs text-[#d4af37] font-light">
|
<p className="mt-2 text-xs text-[var(--luxury-gold)] font-light">
|
||||||
{selectedAmenities.length} amenit{selectedAmenities.length === 1 ? 'y' : 'ies'} selected
|
{selectedAmenities.length} amenit{selectedAmenities.length === 1 ? 'y' : 'ies'} selected
|
||||||
</p>
|
</p>
|
||||||
)}
|
)}
|
||||||
@@ -572,10 +572,10 @@ const RoomFilter: React.FC<RoomFilterProps> = ({ onFilterChange }) => {
|
|||||||
<div className="flex flex-col sm:flex-row gap-2 sm:gap-3 pt-2">
|
<div className="flex flex-col sm:flex-row gap-2 sm:gap-3 pt-2">
|
||||||
<button
|
<button
|
||||||
type="submit"
|
type="submit"
|
||||||
className="flex-1 bg-gradient-to-r from-[#d4af37] to-[#c9a227]
|
className="flex-1 bg-gradient-to-r from-[var(--luxury-gold)] to-[var(--luxury-gold-dark)]
|
||||||
text-[#0f0f0f] py-2.5 sm:py-3 px-4 rounded-sm font-medium tracking-wide text-sm sm:text-base
|
text-[#0f0f0f] py-2.5 sm:py-3 px-4 rounded-sm font-medium tracking-wide text-sm sm:text-base
|
||||||
hover:from-[#f5d76e] hover:to-[#d4af37] active:scale-95
|
hover:from-[var(--luxury-gold-light)] hover:to-[var(--luxury-gold)] active:scale-95
|
||||||
transition-all duration-300 shadow-lg shadow-[#d4af37]/30
|
transition-all duration-300 shadow-lg shadow-[var(--luxury-gold)]/30
|
||||||
relative overflow-hidden group touch-manipulation min-h-[44px]"
|
relative overflow-hidden group touch-manipulation min-h-[44px]"
|
||||||
>
|
>
|
||||||
<span className="relative z-10">Apply</span>
|
<span className="relative z-10">Apply</span>
|
||||||
@@ -585,8 +585,8 @@ const RoomFilter: React.FC<RoomFilterProps> = ({ onFilterChange }) => {
|
|||||||
type="button"
|
type="button"
|
||||||
onClick={handleReset}
|
onClick={handleReset}
|
||||||
className="flex-1 bg-[#0a0a0a] backdrop-blur-sm text-gray-300
|
className="flex-1 bg-[#0a0a0a] backdrop-blur-sm text-gray-300
|
||||||
py-2.5 sm:py-3 px-4 rounded-sm border border-[#d4af37]/30
|
py-2.5 sm:py-3 px-4 rounded-sm border border-[var(--luxury-gold)]/30
|
||||||
hover:bg-[#1a1a1a] hover:border-[#d4af37] hover:text-[#d4af37] active:scale-95
|
hover:bg-[#1a1a1a] hover:border-[var(--luxury-gold)] hover:text-[var(--luxury-gold)] active:scale-95
|
||||||
transition-all font-medium tracking-wide text-sm sm:text-base
|
transition-all font-medium tracking-wide text-sm sm:text-base
|
||||||
touch-manipulation min-h-[44px]"
|
touch-manipulation min-h-[44px]"
|
||||||
>
|
>
|
||||||
|
|||||||
@@ -103,15 +103,15 @@ const SearchRoomForm: React.FC<SearchRoomFormProps> = ({
|
|||||||
const isOverlay = className.includes('overlay');
|
const isOverlay = className.includes('overlay');
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={`w-full ${isOverlay ? 'bg-transparent shadow-none border-none p-0' : 'luxury-glass rounded-sm shadow-2xl p-6 border border-[#d4af37]/20'} ${className}`}>
|
<div className={`w-full ${isOverlay ? 'bg-transparent shadow-none border-none p-0' : 'luxury-glass rounded-sm shadow-2xl p-6 border border-[var(--luxury-gold)]/20'} ${className}`}>
|
||||||
{}
|
{}
|
||||||
{(!isOverlay || !isMobile) && (
|
{(!isOverlay || !isMobile) && (
|
||||||
<div className={`flex items-center justify-center gap-1.5 sm:gap-2 md:gap-3 ${isOverlay ? 'mb-1 sm:mb-2 md:mb-3 lg:mb-4 xl:mb-6' : 'mb-6'}`}>
|
<div className={`flex items-center justify-center gap-1.5 sm:gap-2 md:gap-3 ${isOverlay ? 'mb-1 sm:mb-2 md:mb-3 lg:mb-4 xl:mb-6' : 'mb-6'}`}>
|
||||||
<div className={`${isOverlay ? 'w-0.5 sm:w-0.5 md:w-1' : 'w-1'} ${isOverlay ? 'h-3 sm:h-4 md:h-6 lg:h-8' : 'h-8'} bg-gradient-to-b from-[#d4af37] to-[#c9a227]`}></div>
|
<div className={`${isOverlay ? 'w-0.5 sm:w-0.5 md:w-1' : 'w-1'} ${isOverlay ? 'h-3 sm:h-4 md:h-6 lg:h-8' : 'h-8'} bg-gradient-to-b from-[var(--luxury-gold)] to-[var(--luxury-gold-dark)]`}></div>
|
||||||
<h3 className={`${isOverlay ? 'text-xs sm:text-sm md:text-base lg:text-lg xl:text-xl' : 'text-2xl'} font-serif font-semibold text-gray-900 tracking-tight`}>
|
<h3 className={`${isOverlay ? 'text-xs sm:text-sm md:text-base lg:text-lg xl:text-xl' : 'text-2xl'} font-serif font-semibold text-gray-900 tracking-tight`}>
|
||||||
{isOverlay && isMobile ? 'Find Rooms' : 'Find Available Rooms'}
|
{isOverlay && isMobile ? 'Find Rooms' : 'Find Available Rooms'}
|
||||||
</h3>
|
</h3>
|
||||||
<div className={`${isOverlay ? 'w-0.5 sm:w-0.5 md:w-1' : 'w-1'} ${isOverlay ? 'h-3 sm:h-4 md:h-6 lg:h-8' : 'h-8'} bg-gradient-to-b from-[#d4af37] to-[#c9a227]`}></div>
|
<div className={`${isOverlay ? 'w-0.5 sm:w-0.5 md:w-1' : 'w-1'} ${isOverlay ? 'h-3 sm:h-4 md:h-6 lg:h-8' : 'h-8'} bg-gradient-to-b from-[var(--luxury-gold)] to-[var(--luxury-gold-dark)]`}></div>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
|
|||||||
@@ -212,6 +212,26 @@ export interface UpdateRecaptchaSettingsRequest {
|
|||||||
recaptcha_enabled?: boolean;
|
recaptcha_enabled?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface ThemeSettingsResponse {
|
||||||
|
status: string;
|
||||||
|
data: {
|
||||||
|
theme_primary_color: string;
|
||||||
|
theme_primary_light: string;
|
||||||
|
theme_primary_dark: string;
|
||||||
|
theme_primary_accent: string;
|
||||||
|
updated_at?: string | null;
|
||||||
|
updated_by?: string | null;
|
||||||
|
};
|
||||||
|
message?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface UpdateThemeSettingsRequest {
|
||||||
|
theme_primary_color?: string;
|
||||||
|
theme_primary_light?: string;
|
||||||
|
theme_primary_dark?: string;
|
||||||
|
theme_primary_accent?: string;
|
||||||
|
}
|
||||||
|
|
||||||
export interface VerifyRecaptchaRequest {
|
export interface VerifyRecaptchaRequest {
|
||||||
token: string;
|
token: string;
|
||||||
}
|
}
|
||||||
@@ -438,6 +458,25 @@ const recaptchaService = {
|
|||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
export default systemSettingsService;
|
const themeService = {
|
||||||
export { recaptchaService };
|
getThemeSettings: async (): Promise<ThemeSettingsResponse> => {
|
||||||
|
const response = await apiClient.get<ThemeSettingsResponse>(
|
||||||
|
'/api/admin/system-settings/theme'
|
||||||
|
);
|
||||||
|
return response.data;
|
||||||
|
},
|
||||||
|
|
||||||
|
updateThemeSettings: async (
|
||||||
|
settings: UpdateThemeSettingsRequest
|
||||||
|
): Promise<ThemeSettingsResponse> => {
|
||||||
|
const response = await apiClient.put<ThemeSettingsResponse>(
|
||||||
|
'/api/admin/system-settings/theme',
|
||||||
|
settings
|
||||||
|
);
|
||||||
|
return response.data;
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
export default systemSettingsService;
|
||||||
|
export { recaptchaService, themeService };
|
||||||
|
|
||||||
|
|||||||
@@ -42,14 +42,14 @@ const HousekeepingLayout: React.FC = () => {
|
|||||||
return (
|
return (
|
||||||
<div className="min-h-screen bg-gradient-to-br from-gray-50 via-white to-gray-50 overflow-x-hidden">
|
<div className="min-h-screen bg-gradient-to-br from-gray-50 via-white to-gray-50 overflow-x-hidden">
|
||||||
{/* Luxury Top Navigation Bar */}
|
{/* Luxury Top Navigation Bar */}
|
||||||
<header className="sticky top-0 z-50 bg-white/95 backdrop-blur-xl border-b border-[#d4af37]/20 shadow-sm">
|
<header className="sticky top-0 z-50 bg-white/95 backdrop-blur-xl border-b border-[var(--luxury-gold)]/20 shadow-sm">
|
||||||
<div className="w-full max-w-7xl mx-auto px-3 sm:px-4 md:px-6 lg:px-8">
|
<div className="w-full max-w-7xl mx-auto px-3 sm:px-4 md:px-6 lg:px-8">
|
||||||
<div className="flex items-center justify-between h-14 sm:h-16">
|
<div className="flex items-center justify-between h-14 sm:h-16">
|
||||||
{/* Logo/Brand */}
|
{/* Logo/Brand */}
|
||||||
<div className="flex items-center space-x-2 sm:space-x-3 min-w-0 flex-shrink-0">
|
<div className="flex items-center space-x-2 sm:space-x-3 min-w-0 flex-shrink-0">
|
||||||
<div className="relative flex-shrink-0">
|
<div className="relative flex-shrink-0">
|
||||||
<div className="absolute inset-0 bg-gradient-to-r from-[#d4af37] to-[#c9a227] rounded-lg blur-sm opacity-50"></div>
|
<div className="absolute inset-0 bg-gradient-to-r from-[var(--luxury-gold)] to-[var(--luxury-gold-dark)] rounded-lg blur-sm opacity-50"></div>
|
||||||
<div className="relative bg-gradient-to-r from-[#d4af37] to-[#c9a227] p-1.5 sm:p-2 rounded-lg">
|
<div className="relative bg-gradient-to-r from-[var(--luxury-gold)] to-[var(--luxury-gold-dark)] p-1.5 sm:p-2 rounded-lg">
|
||||||
<Sparkles className="w-4 h-4 sm:w-5 sm:h-5 md:w-6 md:h-6 text-white" />
|
<Sparkles className="w-4 h-4 sm:w-5 sm:h-5 md:w-6 md:h-6 text-white" />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -70,7 +70,7 @@ const HousekeepingLayout: React.FC = () => {
|
|||||||
to={item.path}
|
to={item.path}
|
||||||
className={`flex items-center space-x-1 sm:space-x-2 px-2 sm:px-3 py-1.5 sm:py-2 rounded-lg text-xs sm:text-sm font-medium transition-all duration-200 ${
|
className={`flex items-center space-x-1 sm:space-x-2 px-2 sm:px-3 py-1.5 sm:py-2 rounded-lg text-xs sm:text-sm font-medium transition-all duration-200 ${
|
||||||
isActive(item.path)
|
isActive(item.path)
|
||||||
? 'bg-gradient-to-r from-[#d4af37]/10 to-[#c9a227]/10 text-[#d4af37] border border-[#d4af37]/30'
|
? 'bg-gradient-to-r from-[var(--luxury-gold)]/10 to-[var(--luxury-gold-dark)]/10 text-[var(--luxury-gold)] border border-[var(--luxury-gold)]/30'
|
||||||
: 'text-gray-600 hover:text-gray-900 hover:bg-gray-100'
|
: 'text-gray-600 hover:text-gray-900 hover:bg-gray-100'
|
||||||
}`}
|
}`}
|
||||||
>
|
>
|
||||||
@@ -87,7 +87,7 @@ const HousekeepingLayout: React.FC = () => {
|
|||||||
<InAppNotificationBell />
|
<InAppNotificationBell />
|
||||||
|
|
||||||
<div className="hidden lg:flex items-center space-x-2 px-3 py-1.5 rounded-lg bg-gradient-to-r from-gray-50 to-gray-100/50 border border-gray-200/50">
|
<div className="hidden lg:flex items-center space-x-2 px-3 py-1.5 rounded-lg bg-gradient-to-r from-gray-50 to-gray-100/50 border border-gray-200/50">
|
||||||
<div className="w-7 h-7 rounded-full bg-gradient-to-br from-[#d4af37] to-[#c9a227] flex items-center justify-center flex-shrink-0">
|
<div className="w-7 h-7 rounded-full bg-gradient-to-br from-[var(--luxury-gold)] to-[var(--luxury-gold-dark)] flex items-center justify-center flex-shrink-0">
|
||||||
<User className="w-3.5 h-3.5 text-white" />
|
<User className="w-3.5 h-3.5 text-white" />
|
||||||
</div>
|
</div>
|
||||||
<div className="text-left min-w-0">
|
<div className="text-left min-w-0">
|
||||||
|
|||||||
@@ -417,7 +417,7 @@ const ProfilePage: React.FC = () => {
|
|||||||
{}
|
{}
|
||||||
<div className="mb-6 sm:mb-8 animate-fade-in">
|
<div className="mb-6 sm:mb-8 animate-fade-in">
|
||||||
<div className="flex items-center gap-2 sm:gap-3 mb-2 sm:mb-3">
|
<div className="flex items-center gap-2 sm:gap-3 mb-2 sm:mb-3">
|
||||||
<div className="h-1 w-12 sm:w-16 md:w-20 bg-gradient-to-r from-[#d4af37] via-amber-400 to-[#d4af37] rounded-full"></div>
|
<div className="h-1 w-12 sm:w-16 md:w-20 bg-gradient-to-r from-[var(--luxury-gold)] via-amber-400 to-[var(--luxury-gold)] rounded-full"></div>
|
||||||
<h1 className="text-2xl sm:text-3xl lg:text-4xl font-serif font-bold bg-gradient-to-r from-gray-900 via-amber-900/90 to-gray-900 bg-clip-text text-transparent tracking-tight">
|
<h1 className="text-2xl sm:text-3xl lg:text-4xl font-serif font-bold bg-gradient-to-r from-gray-900 via-amber-900/90 to-gray-900 bg-clip-text text-transparent tracking-tight">
|
||||||
Accountant Profile & Settings
|
Accountant Profile & Settings
|
||||||
</h1>
|
</h1>
|
||||||
@@ -428,61 +428,61 @@ const ProfilePage: React.FC = () => {
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
{}
|
{}
|
||||||
<div className="mb-4 sm:mb-6 border-b border-[#d4af37]/20 overflow-x-auto bg-white/50 backdrop-blur-sm rounded-t-lg sm:rounded-t-xl px-4 sm:px-6">
|
<div className="mb-4 sm:mb-6 border-b border-[var(--luxury-gold)]/20 overflow-x-auto bg-white/50 backdrop-blur-sm rounded-t-lg sm:rounded-t-xl px-4 sm:px-6">
|
||||||
<div className="flex space-x-4 sm:space-x-8 min-w-max">
|
<div className="flex space-x-4 sm:space-x-8 min-w-max">
|
||||||
<button
|
<button
|
||||||
onClick={() => setActiveTab('profile')}
|
onClick={() => setActiveTab('profile')}
|
||||||
className={`py-3 sm:py-4 px-2 sm:px-1 border-b-2 font-semibold text-xs sm:text-sm transition-all duration-300 whitespace-nowrap ${
|
className={`py-3 sm:py-4 px-2 sm:px-1 border-b-2 font-semibold text-xs sm:text-sm transition-all duration-300 whitespace-nowrap ${
|
||||||
activeTab === 'profile'
|
activeTab === 'profile'
|
||||||
? 'border-[#d4af37] text-[#d4af37] shadow-sm'
|
? 'border-[var(--luxury-gold)] text-[var(--luxury-gold)] shadow-sm'
|
||||||
: 'border-transparent text-gray-500 hover:text-[#d4af37] hover:border-[#d4af37]/30'
|
: 'border-transparent text-gray-500 hover:text-[var(--luxury-gold)] hover:border-[var(--luxury-gold)]/30'
|
||||||
}`}
|
}`}
|
||||||
>
|
>
|
||||||
<User className={`w-4 h-4 sm:w-5 sm:h-5 inline mr-2 ${activeTab === 'profile' ? 'text-[#d4af37]' : ''}`} />
|
<User className={`w-4 h-4 sm:w-5 sm:h-5 inline mr-2 ${activeTab === 'profile' ? 'text-[var(--luxury-gold)]' : ''}`} />
|
||||||
Profile Information
|
Profile Information
|
||||||
</button>
|
</button>
|
||||||
<button
|
<button
|
||||||
onClick={() => setActiveTab('password')}
|
onClick={() => setActiveTab('password')}
|
||||||
className={`py-3 sm:py-4 px-2 sm:px-1 border-b-2 font-semibold text-xs sm:text-sm transition-all duration-300 whitespace-nowrap ${
|
className={`py-3 sm:py-4 px-2 sm:px-1 border-b-2 font-semibold text-xs sm:text-sm transition-all duration-300 whitespace-nowrap ${
|
||||||
activeTab === 'password'
|
activeTab === 'password'
|
||||||
? 'border-[#d4af37] text-[#d4af37] shadow-sm'
|
? 'border-[var(--luxury-gold)] text-[var(--luxury-gold)] shadow-sm'
|
||||||
: 'border-transparent text-gray-500 hover:text-[#d4af37] hover:border-[#d4af37]/30'
|
: 'border-transparent text-gray-500 hover:text-[var(--luxury-gold)] hover:border-[var(--luxury-gold)]/30'
|
||||||
}`}
|
}`}
|
||||||
>
|
>
|
||||||
<KeyRound className={`w-4 h-4 sm:w-5 sm:h-5 inline mr-2 ${activeTab === 'password' ? 'text-[#d4af37]' : ''}`} />
|
<KeyRound className={`w-4 h-4 sm:w-5 sm:h-5 inline mr-2 ${activeTab === 'password' ? 'text-[var(--luxury-gold)]' : ''}`} />
|
||||||
Change Password
|
Change Password
|
||||||
</button>
|
</button>
|
||||||
<button
|
<button
|
||||||
onClick={() => setActiveTab('mfa')}
|
onClick={() => setActiveTab('mfa')}
|
||||||
className={`py-3 sm:py-4 px-2 sm:px-1 border-b-2 font-semibold text-xs sm:text-sm transition-all duration-300 whitespace-nowrap ${
|
className={`py-3 sm:py-4 px-2 sm:px-1 border-b-2 font-semibold text-xs sm:text-sm transition-all duration-300 whitespace-nowrap ${
|
||||||
activeTab === 'mfa'
|
activeTab === 'mfa'
|
||||||
? 'border-[#d4af37] text-[#d4af37] shadow-sm'
|
? 'border-[var(--luxury-gold)] text-[var(--luxury-gold)] shadow-sm'
|
||||||
: 'border-transparent text-gray-500 hover:text-[#d4af37] hover:border-[#d4af37]/30'
|
: 'border-transparent text-gray-500 hover:text-[var(--luxury-gold)] hover:border-[var(--luxury-gold)]/30'
|
||||||
}`}
|
}`}
|
||||||
>
|
>
|
||||||
<Shield className={`w-4 h-4 sm:w-5 sm:h-5 inline mr-2 ${activeTab === 'mfa' ? 'text-[#d4af37]' : ''}`} />
|
<Shield className={`w-4 h-4 sm:w-5 sm:h-5 inline mr-2 ${activeTab === 'mfa' ? 'text-[var(--luxury-gold)]' : ''}`} />
|
||||||
Two-Factor Authentication
|
Two-Factor Authentication
|
||||||
</button>
|
</button>
|
||||||
<button
|
<button
|
||||||
onClick={() => setActiveTab('sessions')}
|
onClick={() => setActiveTab('sessions')}
|
||||||
className={`py-3 sm:py-4 px-2 sm:px-1 border-b-2 font-semibold text-xs sm:text-sm transition-all duration-300 whitespace-nowrap ${
|
className={`py-3 sm:py-4 px-2 sm:px-1 border-b-2 font-semibold text-xs sm:text-sm transition-all duration-300 whitespace-nowrap ${
|
||||||
activeTab === 'sessions'
|
activeTab === 'sessions'
|
||||||
? 'border-[#d4af37] text-[#d4af37] shadow-sm'
|
? 'border-[var(--luxury-gold)] text-[var(--luxury-gold)] shadow-sm'
|
||||||
: 'border-transparent text-gray-500 hover:text-[#d4af37] hover:border-[#d4af37]/30'
|
: 'border-transparent text-gray-500 hover:text-[var(--luxury-gold)] hover:border-[var(--luxury-gold)]/30'
|
||||||
}`}
|
}`}
|
||||||
>
|
>
|
||||||
<Monitor className={`w-4 h-4 sm:w-5 sm:h-5 inline mr-2 ${activeTab === 'sessions' ? 'text-[#d4af37]' : ''}`} />
|
<Monitor className={`w-4 h-4 sm:w-5 sm:h-5 inline mr-2 ${activeTab === 'sessions' ? 'text-[var(--luxury-gold)]' : ''}`} />
|
||||||
Active Sessions
|
Active Sessions
|
||||||
</button>
|
</button>
|
||||||
<button
|
<button
|
||||||
onClick={() => setActiveTab('gdpr')}
|
onClick={() => setActiveTab('gdpr')}
|
||||||
className={`py-3 sm:py-4 px-2 sm:px-1 border-b-2 font-semibold text-xs sm:text-sm transition-all duration-300 whitespace-nowrap ${
|
className={`py-3 sm:py-4 px-2 sm:px-1 border-b-2 font-semibold text-xs sm:text-sm transition-all duration-300 whitespace-nowrap ${
|
||||||
activeTab === 'gdpr'
|
activeTab === 'gdpr'
|
||||||
? 'border-[#d4af37] text-[#d4af37] shadow-sm'
|
? 'border-[var(--luxury-gold)] text-[var(--luxury-gold)] shadow-sm'
|
||||||
: 'border-transparent text-gray-500 hover:text-[#d4af37] hover:border-[#d4af37]/30'
|
: 'border-transparent text-gray-500 hover:text-[var(--luxury-gold)] hover:border-[var(--luxury-gold)]/30'
|
||||||
}`}
|
}`}
|
||||||
>
|
>
|
||||||
<Database className={`w-4 h-4 sm:w-5 sm:h-5 inline mr-2 ${activeTab === 'gdpr' ? 'text-[#d4af37]' : ''}`} />
|
<Database className={`w-4 h-4 sm:w-5 sm:h-5 inline mr-2 ${activeTab === 'gdpr' ? 'text-[var(--luxury-gold)]' : ''}`} />
|
||||||
Data Privacy
|
Data Privacy
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
@@ -490,7 +490,7 @@ const ProfilePage: React.FC = () => {
|
|||||||
|
|
||||||
{}
|
{}
|
||||||
{activeTab === 'profile' && (
|
{activeTab === 'profile' && (
|
||||||
<div className="luxury-glass rounded-sm p-4 sm:p-6 lg:p-8 border border-[#d4af37]/20 shadow-2xl animate-slide-up">
|
<div className="luxury-glass rounded-sm p-4 sm:p-6 lg:p-8 border border-[var(--luxury-gold)]/20 shadow-2xl animate-slide-up">
|
||||||
<form onSubmit={handleSubmitProfile(onSubmitProfile)} className="space-y-5 sm:space-y-6">
|
<form onSubmit={handleSubmitProfile(onSubmitProfile)} className="space-y-5 sm:space-y-6">
|
||||||
{}
|
{}
|
||||||
<div className="flex flex-col sm:flex-row items-center sm:items-start gap-4 sm:gap-6 pb-5 sm:pb-6 border-b border-gray-200">
|
<div className="flex flex-col sm:flex-row items-center sm:items-start gap-4 sm:gap-6 pb-5 sm:pb-6 border-b border-gray-200">
|
||||||
@@ -499,20 +499,20 @@ const ProfilePage: React.FC = () => {
|
|||||||
<img
|
<img
|
||||||
src={avatarPreview || normalizeImageUrl(userInfo?.avatar)}
|
src={avatarPreview || normalizeImageUrl(userInfo?.avatar)}
|
||||||
alt="Profile"
|
alt="Profile"
|
||||||
className="w-20 h-20 sm:w-24 sm:h-24 rounded-full object-cover ring-4 ring-[#d4af37]/20 shadow-lg"
|
className="w-20 h-20 sm:w-24 sm:h-24 rounded-full object-cover ring-4 ring-[var(--luxury-gold)]/20 shadow-lg"
|
||||||
onError={() => {
|
onError={() => {
|
||||||
// If image fails to load, show default avatar
|
// If image fails to load, show default avatar
|
||||||
setAvatarError(true);
|
setAvatarError(true);
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
) : (
|
) : (
|
||||||
<div className="w-20 h-20 sm:w-24 sm:h-24 rounded-full bg-gradient-to-br from-[#d4af37] to-[#c9a227] flex items-center justify-center ring-4 ring-[#d4af37]/20 shadow-lg">
|
<div className="w-20 h-20 sm:w-24 sm:h-24 rounded-full bg-gradient-to-br from-[var(--luxury-gold)] to-[var(--luxury-gold-dark)] flex items-center justify-center ring-4 ring-[var(--luxury-gold)]/20 shadow-lg">
|
||||||
<User className="w-10 h-10 sm:w-12 sm:h-12 text-white" />
|
<User className="w-10 h-10 sm:w-12 sm:h-12 text-white" />
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
<label
|
<label
|
||||||
htmlFor="avatar-upload"
|
htmlFor="avatar-upload"
|
||||||
className="absolute bottom-0 right-0 p-2 bg-gradient-to-br from-[#d4af37] to-[#c9a227] rounded-full cursor-pointer hover:from-[#f5d76e] hover:to-[#d4af37] transition-all duration-300 shadow-lg hover:shadow-xl hover:shadow-[#d4af37]/40 hover:-translate-y-0.5 active:translate-y-0"
|
className="absolute bottom-0 right-0 p-2 bg-gradient-to-br from-[var(--luxury-gold)] to-[var(--luxury-gold-dark)] rounded-full cursor-pointer hover:from-[var(--luxury-gold-light)] hover:to-[var(--luxury-gold)] transition-all duration-300 shadow-lg hover:shadow-xl hover:shadow-[var(--luxury-gold)]/40 hover:-translate-y-0.5 active:translate-y-0"
|
||||||
>
|
>
|
||||||
<Camera className="w-3.5 h-3.5 sm:w-4 sm:h-4 text-white" />
|
<Camera className="w-3.5 h-3.5 sm:w-4 sm:h-4 text-white" />
|
||||||
<input
|
<input
|
||||||
@@ -528,7 +528,7 @@ const ProfilePage: React.FC = () => {
|
|||||||
|
|
||||||
<div>
|
<div>
|
||||||
<label className="block text-sm sm:text-base font-medium text-gray-700 mb-2">
|
<label className="block text-sm sm:text-base font-medium text-gray-700 mb-2">
|
||||||
<User className="w-4 h-4 sm:w-5 sm:h-5 inline mr-2 text-[#d4af37]" />
|
<User className="w-4 h-4 sm:w-5 sm:h-5 inline mr-2 text-[var(--luxury-gold)]" />
|
||||||
Full Name
|
Full Name
|
||||||
<span className="text-red-500 ml-1">*</span>
|
<span className="text-red-500 ml-1">*</span>
|
||||||
</label>
|
</label>
|
||||||
@@ -551,7 +551,7 @@ const ProfilePage: React.FC = () => {
|
|||||||
{}
|
{}
|
||||||
<div>
|
<div>
|
||||||
<label className="block text-sm sm:text-base font-medium text-gray-700 mb-2">
|
<label className="block text-sm sm:text-base font-medium text-gray-700 mb-2">
|
||||||
<Mail className="w-4 h-4 sm:w-5 sm:h-5 inline mr-2 text-[#d4af37]" />
|
<Mail className="w-4 h-4 sm:w-5 sm:h-5 inline mr-2 text-[var(--luxury-gold)]" />
|
||||||
Email Address
|
Email Address
|
||||||
<span className="text-red-500 ml-1">*</span>
|
<span className="text-red-500 ml-1">*</span>
|
||||||
</label>
|
</label>
|
||||||
@@ -574,7 +574,7 @@ const ProfilePage: React.FC = () => {
|
|||||||
{}
|
{}
|
||||||
<div>
|
<div>
|
||||||
<label className="block text-sm sm:text-base font-medium text-gray-700 mb-2">
|
<label className="block text-sm sm:text-base font-medium text-gray-700 mb-2">
|
||||||
<Phone className="w-4 h-4 sm:w-5 sm:h-5 inline mr-2 text-[#d4af37]" />
|
<Phone className="w-4 h-4 sm:w-5 sm:h-5 inline mr-2 text-[var(--luxury-gold)]" />
|
||||||
Phone Number
|
Phone Number
|
||||||
<span className="text-red-500 ml-1">*</span>
|
<span className="text-red-500 ml-1">*</span>
|
||||||
</label>
|
</label>
|
||||||
@@ -611,7 +611,7 @@ const ProfilePage: React.FC = () => {
|
|||||||
|
|
||||||
{}
|
{}
|
||||||
{activeTab === 'password' && (
|
{activeTab === 'password' && (
|
||||||
<div className="luxury-glass rounded-sm p-4 sm:p-6 lg:p-8 border border-[#d4af37]/20 shadow-2xl animate-slide-up">
|
<div className="luxury-glass rounded-sm p-4 sm:p-6 lg:p-8 border border-[var(--luxury-gold)]/20 shadow-2xl animate-slide-up">
|
||||||
<form onSubmit={handleSubmitPassword(onSubmitPassword)} className="space-y-5 sm:space-y-6">
|
<form onSubmit={handleSubmitPassword(onSubmitPassword)} className="space-y-5 sm:space-y-6">
|
||||||
{}
|
{}
|
||||||
<div className="bg-gradient-to-r from-blue-50 to-indigo-50 border border-blue-200/60 rounded-sm p-4 sm:p-5">
|
<div className="bg-gradient-to-r from-blue-50 to-indigo-50 border border-blue-200/60 rounded-sm p-4 sm:p-5">
|
||||||
@@ -640,7 +640,7 @@ const ProfilePage: React.FC = () => {
|
|||||||
{}
|
{}
|
||||||
<div>
|
<div>
|
||||||
<label className="block text-sm sm:text-base font-medium text-gray-700 mb-2">
|
<label className="block text-sm sm:text-base font-medium text-gray-700 mb-2">
|
||||||
<Lock className="w-4 h-4 sm:w-5 sm:h-5 inline mr-2 text-[#d4af37]" />
|
<Lock className="w-4 h-4 sm:w-5 sm:h-5 inline mr-2 text-[var(--luxury-gold)]" />
|
||||||
Current Password
|
Current Password
|
||||||
<span className="text-red-500 ml-1">*</span>
|
<span className="text-red-500 ml-1">*</span>
|
||||||
</label>
|
</label>
|
||||||
@@ -656,7 +656,7 @@ const ProfilePage: React.FC = () => {
|
|||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
onClick={() => setShowPassword({ ...showPassword, current: !showPassword.current })}
|
onClick={() => setShowPassword({ ...showPassword, current: !showPassword.current })}
|
||||||
className="absolute inset-y-0 right-0 pr-3 flex items-center text-gray-400 hover:text-[#d4af37] transition-colors"
|
className="absolute inset-y-0 right-0 pr-3 flex items-center text-gray-400 hover:text-[var(--luxury-gold)] transition-colors"
|
||||||
>
|
>
|
||||||
{showPassword.current ? (
|
{showPassword.current ? (
|
||||||
<EyeOff className="w-4 h-4 sm:w-5 sm:h-5" />
|
<EyeOff className="w-4 h-4 sm:w-5 sm:h-5" />
|
||||||
@@ -676,7 +676,7 @@ const ProfilePage: React.FC = () => {
|
|||||||
{}
|
{}
|
||||||
<div>
|
<div>
|
||||||
<label className="block text-sm sm:text-base font-medium text-gray-700 mb-2">
|
<label className="block text-sm sm:text-base font-medium text-gray-700 mb-2">
|
||||||
<Lock className="w-4 h-4 sm:w-5 sm:h-5 inline mr-2 text-[#d4af37]" />
|
<Lock className="w-4 h-4 sm:w-5 sm:h-5 inline mr-2 text-[var(--luxury-gold)]" />
|
||||||
New Password
|
New Password
|
||||||
<span className="text-red-500 ml-1">*</span>
|
<span className="text-red-500 ml-1">*</span>
|
||||||
</label>
|
</label>
|
||||||
@@ -692,7 +692,7 @@ const ProfilePage: React.FC = () => {
|
|||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
onClick={() => setShowPassword({ ...showPassword, new: !showPassword.new })}
|
onClick={() => setShowPassword({ ...showPassword, new: !showPassword.new })}
|
||||||
className="absolute inset-y-0 right-0 pr-3 flex items-center text-gray-400 hover:text-[#d4af37] transition-colors"
|
className="absolute inset-y-0 right-0 pr-3 flex items-center text-gray-400 hover:text-[var(--luxury-gold)] transition-colors"
|
||||||
>
|
>
|
||||||
{showPassword.new ? (
|
{showPassword.new ? (
|
||||||
<EyeOff className="w-4 h-4 sm:w-5 sm:h-5" />
|
<EyeOff className="w-4 h-4 sm:w-5 sm:h-5" />
|
||||||
@@ -712,7 +712,7 @@ const ProfilePage: React.FC = () => {
|
|||||||
{}
|
{}
|
||||||
<div>
|
<div>
|
||||||
<label className="block text-sm sm:text-base font-medium text-gray-700 mb-2">
|
<label className="block text-sm sm:text-base font-medium text-gray-700 mb-2">
|
||||||
<Lock className="w-4 h-4 sm:w-5 sm:h-5 inline mr-2 text-[#d4af37]" />
|
<Lock className="w-4 h-4 sm:w-5 sm:h-5 inline mr-2 text-[var(--luxury-gold)]" />
|
||||||
Confirm New Password
|
Confirm New Password
|
||||||
<span className="text-red-500 ml-1">*</span>
|
<span className="text-red-500 ml-1">*</span>
|
||||||
</label>
|
</label>
|
||||||
@@ -728,7 +728,7 @@ const ProfilePage: React.FC = () => {
|
|||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
onClick={() => setShowPassword({ ...showPassword, confirm: !showPassword.confirm })}
|
onClick={() => setShowPassword({ ...showPassword, confirm: !showPassword.confirm })}
|
||||||
className="absolute inset-y-0 right-0 pr-3 flex items-center text-gray-400 hover:text-[#d4af37] transition-colors"
|
className="absolute inset-y-0 right-0 pr-3 flex items-center text-gray-400 hover:text-[var(--luxury-gold)] transition-colors"
|
||||||
>
|
>
|
||||||
{showPassword.confirm ? (
|
{showPassword.confirm ? (
|
||||||
<EyeOff className="w-4 h-4 sm:w-5 sm:h-5" />
|
<EyeOff className="w-4 h-4 sm:w-5 sm:h-5" />
|
||||||
@@ -762,11 +762,11 @@ const ProfilePage: React.FC = () => {
|
|||||||
|
|
||||||
{}
|
{}
|
||||||
{activeTab === 'mfa' && (
|
{activeTab === 'mfa' && (
|
||||||
<div className="luxury-glass rounded-sm p-4 sm:p-6 lg:p-8 border border-[#d4af37]/20 shadow-2xl animate-slide-up space-y-5 sm:space-y-6">
|
<div className="luxury-glass rounded-sm p-4 sm:p-6 lg:p-8 border border-[var(--luxury-gold)]/20 shadow-2xl animate-slide-up space-y-5 sm:space-y-6">
|
||||||
{}
|
{}
|
||||||
<div>
|
<div>
|
||||||
<h2 className="text-xl sm:text-2xl font-serif font-semibold text-gray-900 mb-2 flex items-center gap-2">
|
<h2 className="text-xl sm:text-2xl font-serif font-semibold text-gray-900 mb-2 flex items-center gap-2">
|
||||||
<Shield className="w-5 h-5 sm:w-6 sm:h-6 text-[#d4af37]" />
|
<Shield className="w-5 h-5 sm:w-6 sm:h-6 text-[var(--luxury-gold)]" />
|
||||||
Two-Factor Authentication
|
Two-Factor Authentication
|
||||||
</h2>
|
</h2>
|
||||||
<p className="text-xs sm:text-sm text-gray-600 font-light leading-relaxed">
|
<p className="text-xs sm:text-sm text-gray-600 font-light leading-relaxed">
|
||||||
@@ -838,7 +838,7 @@ const ProfilePage: React.FC = () => {
|
|||||||
{}
|
{}
|
||||||
<div className="bg-white border border-gray-200 rounded-sm p-4 sm:p-5">
|
<div className="bg-white border border-gray-200 rounded-sm p-4 sm:p-5">
|
||||||
<h3 className="text-sm sm:text-base font-semibold text-gray-900 mb-2 flex items-center gap-2">
|
<h3 className="text-sm sm:text-base font-semibold text-gray-900 mb-2 flex items-center gap-2">
|
||||||
<RefreshCw className="w-4 h-4 sm:w-5 sm:h-5 text-[#d4af37]" />
|
<RefreshCw className="w-4 h-4 sm:w-5 sm:h-5 text-[var(--luxury-gold)]" />
|
||||||
Backup Codes
|
Backup Codes
|
||||||
</h3>
|
</h3>
|
||||||
<p className="text-xs sm:text-sm text-gray-600 font-light mb-4 leading-relaxed">
|
<p className="text-xs sm:text-sm text-gray-600 font-light mb-4 leading-relaxed">
|
||||||
@@ -984,7 +984,7 @@ const ProfilePage: React.FC = () => {
|
|||||||
{}
|
{}
|
||||||
<div className="bg-white border border-gray-200 rounded-sm p-4 sm:p-5">
|
<div className="bg-white border border-gray-200 rounded-sm p-4 sm:p-5">
|
||||||
<h3 className="font-semibold text-gray-900 mb-2 text-sm sm:text-base flex items-center gap-2">
|
<h3 className="font-semibold text-gray-900 mb-2 text-sm sm:text-base flex items-center gap-2">
|
||||||
<KeyRound className="w-4 h-4 sm:w-5 sm:h-5 text-[#d4af37]" />
|
<KeyRound className="w-4 h-4 sm:w-5 sm:h-5 text-[var(--luxury-gold)]" />
|
||||||
Step 2: Verify Setup
|
Step 2: Verify Setup
|
||||||
</h3>
|
</h3>
|
||||||
<p className="text-xs sm:text-sm text-gray-600 font-light mb-4 leading-relaxed">
|
<p className="text-xs sm:text-sm text-gray-600 font-light mb-4 leading-relaxed">
|
||||||
@@ -1110,10 +1110,10 @@ const SessionsTab: React.FC = () => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const getDeviceIcon = (userAgent?: string) => {
|
const getDeviceIcon = (userAgent?: string) => {
|
||||||
if (!userAgent) return <Monitor className="w-5 h-5 text-[#d4af37]" />;
|
if (!userAgent) return <Monitor className="w-5 h-5 text-[var(--luxury-gold)]" />;
|
||||||
if (userAgent.includes('Mobile')) return <Smartphone className="w-5 h-5 text-blue-500" />;
|
if (userAgent.includes('Mobile')) return <Smartphone className="w-5 h-5 text-blue-500" />;
|
||||||
if (userAgent.includes('Tablet')) return <Tablet className="w-5 h-5 text-purple-500" />;
|
if (userAgent.includes('Tablet')) return <Tablet className="w-5 h-5 text-purple-500" />;
|
||||||
return <Monitor className="w-5 h-5 text-[#d4af37]" />;
|
return <Monitor className="w-5 h-5 text-[var(--luxury-gold)]" />;
|
||||||
};
|
};
|
||||||
|
|
||||||
const getDeviceName = (userAgent?: string) => {
|
const getDeviceName = (userAgent?: string) => {
|
||||||
@@ -1181,17 +1181,17 @@ const SessionsTab: React.FC = () => {
|
|||||||
|
|
||||||
if (loading) {
|
if (loading) {
|
||||||
return (
|
return (
|
||||||
<div className="luxury-glass rounded-sm p-4 sm:p-6 lg:p-8 border border-[#d4af37]/20 shadow-2xl">
|
<div className="luxury-glass rounded-sm p-4 sm:p-6 lg:p-8 border border-[var(--luxury-gold)]/20 shadow-2xl">
|
||||||
<Loading text="Loading sessions..." />
|
<Loading text="Loading sessions..." />
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="luxury-glass rounded-sm p-4 sm:p-6 lg:p-8 border border-[#d4af37]/20 shadow-2xl animate-slide-up space-y-5 sm:space-y-6">
|
<div className="luxury-glass rounded-sm p-4 sm:p-6 lg:p-8 border border-[var(--luxury-gold)]/20 shadow-2xl animate-slide-up space-y-5 sm:space-y-6">
|
||||||
<div>
|
<div>
|
||||||
<h2 className="text-xl sm:text-2xl font-serif font-semibold text-gray-900 mb-2 flex items-center gap-2">
|
<h2 className="text-xl sm:text-2xl font-serif font-semibold text-gray-900 mb-2 flex items-center gap-2">
|
||||||
<Monitor className="w-5 h-5 sm:w-6 sm:h-6 text-[#d4af37]" />
|
<Monitor className="w-5 h-5 sm:w-6 sm:h-6 text-[var(--luxury-gold)]" />
|
||||||
Active Sessions
|
Active Sessions
|
||||||
</h2>
|
</h2>
|
||||||
<p className="text-xs sm:text-sm text-gray-600 font-light">
|
<p className="text-xs sm:text-sm text-gray-600 font-light">
|
||||||
@@ -1220,11 +1220,11 @@ const SessionsTab: React.FC = () => {
|
|||||||
{sessions.map((session) => (
|
{sessions.map((session) => (
|
||||||
<div
|
<div
|
||||||
key={session.id}
|
key={session.id}
|
||||||
className="bg-gradient-to-r from-slate-50 to-white border border-[#d4af37]/20 rounded-sm p-4 sm:p-5 hover:shadow-lg hover:border-[#d4af37]/40 transition-all duration-300"
|
className="bg-gradient-to-r from-slate-50 to-white border border-[var(--luxury-gold)]/20 rounded-sm p-4 sm:p-5 hover:shadow-lg hover:border-[var(--luxury-gold)]/40 transition-all duration-300"
|
||||||
>
|
>
|
||||||
<div className="flex items-start justify-between gap-4">
|
<div className="flex items-start justify-between gap-4">
|
||||||
<div className="flex items-start gap-4 flex-1">
|
<div className="flex items-start gap-4 flex-1">
|
||||||
<div className="p-2 sm:p-3 bg-gradient-to-br from-[#d4af37]/10 to-[#c9a227]/10 rounded-sm flex-shrink-0">
|
<div className="p-2 sm:p-3 bg-gradient-to-br from-[var(--luxury-gold)]/10 to-[var(--luxury-gold-dark)]/10 rounded-sm flex-shrink-0">
|
||||||
{getDeviceIcon(session.user_agent)}
|
{getDeviceIcon(session.user_agent)}
|
||||||
</div>
|
</div>
|
||||||
<div className="flex-1 min-w-0">
|
<div className="flex-1 min-w-0">
|
||||||
@@ -1354,17 +1354,17 @@ const GDPRTab: React.FC = () => {
|
|||||||
|
|
||||||
if (loading) {
|
if (loading) {
|
||||||
return (
|
return (
|
||||||
<div className="luxury-glass rounded-sm p-4 sm:p-6 lg:p-8 border border-[#d4af37]/20 shadow-2xl">
|
<div className="luxury-glass rounded-sm p-4 sm:p-6 lg:p-8 border border-[var(--luxury-gold)]/20 shadow-2xl">
|
||||||
<Loading text="Loading privacy data..." />
|
<Loading text="Loading privacy data..." />
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="luxury-glass rounded-sm p-4 sm:p-6 lg:p-8 border border-[#d4af37]/20 shadow-2xl animate-slide-up space-y-5 sm:space-y-6">
|
<div className="luxury-glass rounded-sm p-4 sm:p-6 lg:p-8 border border-[var(--luxury-gold)]/20 shadow-2xl animate-slide-up space-y-5 sm:space-y-6">
|
||||||
<div>
|
<div>
|
||||||
<h2 className="text-xl sm:text-2xl font-serif font-semibold text-gray-900 mb-2 flex items-center gap-2">
|
<h2 className="text-xl sm:text-2xl font-serif font-semibold text-gray-900 mb-2 flex items-center gap-2">
|
||||||
<Database className="w-5 h-5 sm:w-6 sm:h-6 text-[#d4af37]" />
|
<Database className="w-5 h-5 sm:w-6 sm:h-6 text-[var(--luxury-gold)]" />
|
||||||
Data Privacy & GDPR Rights
|
Data Privacy & GDPR Rights
|
||||||
</h2>
|
</h2>
|
||||||
<p className="text-xs sm:text-sm text-gray-600 font-light">
|
<p className="text-xs sm:text-sm text-gray-600 font-light">
|
||||||
@@ -1431,14 +1431,14 @@ const GDPRTab: React.FC = () => {
|
|||||||
{requests.length > 0 && (
|
{requests.length > 0 && (
|
||||||
<div className="border-t border-gray-200 pt-5 sm:pt-6">
|
<div className="border-t border-gray-200 pt-5 sm:pt-6">
|
||||||
<h3 className="text-base sm:text-lg font-semibold text-gray-900 mb-4 flex items-center gap-2">
|
<h3 className="text-base sm:text-lg font-semibold text-gray-900 mb-4 flex items-center gap-2">
|
||||||
<FileText className="w-5 h-5 text-[#d4af37]" />
|
<FileText className="w-5 h-5 text-[var(--luxury-gold)]" />
|
||||||
Request History
|
Request History
|
||||||
</h3>
|
</h3>
|
||||||
<div className="space-y-3 sm:space-y-4">
|
<div className="space-y-3 sm:space-y-4">
|
||||||
{requests.map((request) => (
|
{requests.map((request) => (
|
||||||
<div
|
<div
|
||||||
key={request.id}
|
key={request.id}
|
||||||
className="bg-gradient-to-r from-slate-50 to-white border border-[#d4af37]/20 rounded-sm p-4 sm:p-5 hover:shadow-md hover:border-[#d4af37]/40 transition-all duration-300"
|
className="bg-gradient-to-r from-slate-50 to-white border border-[var(--luxury-gold)]/20 rounded-sm p-4 sm:p-5 hover:shadow-md hover:border-[var(--luxury-gold)]/40 transition-all duration-300"
|
||||||
>
|
>
|
||||||
<div className="flex flex-col sm:flex-row items-start sm:items-center justify-between gap-3 sm:gap-4">
|
<div className="flex flex-col sm:flex-row items-start sm:items-center justify-between gap-3 sm:gap-4">
|
||||||
<div className="flex-1 min-w-0">
|
<div className="flex-1 min-w-0">
|
||||||
|
|||||||
@@ -1334,10 +1334,10 @@ const AdvancedRoomManagementPage: React.FC = () => {
|
|||||||
{/* Room Modal */}
|
{/* Room Modal */}
|
||||||
{showRoomModal && (
|
{showRoomModal && (
|
||||||
<div className="fixed inset-0 bg-black/80 backdrop-blur-sm flex items-center justify-center z-[100] p-4">
|
<div className="fixed inset-0 bg-black/80 backdrop-blur-sm flex items-center justify-center z-[100] p-4">
|
||||||
<div className="bg-gradient-to-br from-[#1a1a1a] via-[#0f0f0f] to-[#1a1a1a] rounded-xl border border-[#d4af37]/30 backdrop-blur-xl shadow-2xl shadow-[#d4af37]/20 p-8 w-full max-w-4xl max-h-[90vh] overflow-y-auto">
|
<div className="bg-gradient-to-br from-[#1a1a1a] via-[#0f0f0f] to-[#1a1a1a] rounded-xl border border-[var(--luxury-gold)]/30 backdrop-blur-xl shadow-2xl shadow-[var(--luxury-gold)]/20 p-8 w-full max-w-4xl max-h-[90vh] overflow-y-auto">
|
||||||
<div className="flex justify-between items-center mb-6 pb-6 border-b border-[#d4af37]/20">
|
<div className="flex justify-between items-center mb-6 pb-6 border-b border-[var(--luxury-gold)]/20">
|
||||||
<div>
|
<div>
|
||||||
<h2 className="text-xl sm:text-2xl md:text-2xl font-serif font-semibold bg-gradient-to-r from-white via-[#d4af37] to-white bg-clip-text text-transparent tracking-wide">
|
<h2 className="text-xl sm:text-2xl md:text-2xl font-serif font-semibold bg-gradient-to-r from-white via-[var(--luxury-gold)] to-white bg-clip-text text-transparent tracking-wide">
|
||||||
{editingRoom ? 'Update Room' : 'Add New Room'}
|
{editingRoom ? 'Update Room' : 'Add New Room'}
|
||||||
</h2>
|
</h2>
|
||||||
<p className="text-gray-400 text-sm mt-1 font-light">
|
<p className="text-gray-400 text-sm mt-1 font-light">
|
||||||
@@ -1346,16 +1346,16 @@ const AdvancedRoomManagementPage: React.FC = () => {
|
|||||||
</div>
|
</div>
|
||||||
<button
|
<button
|
||||||
onClick={() => setShowRoomModal(false)}
|
onClick={() => setShowRoomModal(false)}
|
||||||
className="p-2 hover:bg-[#d4af37]/10 rounded-lg transition-colors border border-[#d4af37]/20 hover:border-[#d4af37]/40"
|
className="p-2 hover:bg-[var(--luxury-gold)]/10 rounded-lg transition-colors border border-[var(--luxury-gold)]/20 hover:border-[var(--luxury-gold)]/40"
|
||||||
>
|
>
|
||||||
<X className="w-6 h-6 text-[#d4af37] hover:text-white transition-colors" />
|
<X className="w-6 h-6 text-[var(--luxury-gold)] hover:text-white transition-colors" />
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<form onSubmit={handleRoomSubmit} className="space-y-6">
|
<form onSubmit={handleRoomSubmit} className="space-y-6">
|
||||||
<div className="space-y-4">
|
<div className="space-y-4">
|
||||||
<h3 className="text-lg font-serif font-semibold text-[#d4af37] mb-4 flex items-center gap-2">
|
<h3 className="text-lg font-serif font-semibold text-[var(--luxury-gold)] mb-4 flex items-center gap-2">
|
||||||
<div className="w-1 h-6 bg-gradient-to-b from-[#d4af37] to-[#c9a227]" />
|
<div className="w-1 h-6 bg-gradient-to-b from-[var(--luxury-gold)] to-[var(--luxury-gold-dark)]" />
|
||||||
Basic Information
|
Basic Information
|
||||||
</h3>
|
</h3>
|
||||||
|
|
||||||
@@ -1368,7 +1368,7 @@ const AdvancedRoomManagementPage: React.FC = () => {
|
|||||||
type="text"
|
type="text"
|
||||||
value={roomFormData.room_number}
|
value={roomFormData.room_number}
|
||||||
onChange={(e) => setRoomFormData({ ...roomFormData, room_number: e.target.value })}
|
onChange={(e) => setRoomFormData({ ...roomFormData, room_number: e.target.value })}
|
||||||
className="w-full px-4 py-3 bg-[#0a0a0a] border border-[#d4af37]/20 rounded-lg text-white placeholder-gray-500 focus:ring-2 focus:ring-[#d4af37]/50 focus:border-[#d4af37] transition-all duration-300"
|
className="w-full px-4 py-3 bg-[#0a0a0a] border border-[var(--luxury-gold)]/20 rounded-lg text-white placeholder-gray-500 focus:ring-2 focus:ring-[var(--luxury-gold)]/50 focus:border-[var(--luxury-gold)] transition-all duration-300"
|
||||||
placeholder="e.g., 1001"
|
placeholder="e.g., 1001"
|
||||||
required
|
required
|
||||||
/>
|
/>
|
||||||
@@ -1381,7 +1381,7 @@ const AdvancedRoomManagementPage: React.FC = () => {
|
|||||||
type="number"
|
type="number"
|
||||||
value={roomFormData.floor}
|
value={roomFormData.floor}
|
||||||
onChange={(e) => setRoomFormData({ ...roomFormData, floor: parseInt(e.target.value) })}
|
onChange={(e) => setRoomFormData({ ...roomFormData, floor: parseInt(e.target.value) })}
|
||||||
className="w-full px-4 py-3 bg-[#0a0a0a] border border-[#d4af37]/20 rounded-lg text-white placeholder-gray-500 focus:ring-2 focus:ring-[#d4af37]/50 focus:border-[#d4af37] transition-all duration-300"
|
className="w-full px-4 py-3 bg-[#0a0a0a] border border-[var(--luxury-gold)]/20 rounded-lg text-white placeholder-gray-500 focus:ring-2 focus:ring-[var(--luxury-gold)]/50 focus:border-[var(--luxury-gold)] transition-all duration-300"
|
||||||
required
|
required
|
||||||
min="1"
|
min="1"
|
||||||
/>
|
/>
|
||||||
@@ -1405,7 +1405,7 @@ const AdvancedRoomManagementPage: React.FC = () => {
|
|||||||
amenities: inheritedAmenities // Inherit amenities from room type
|
amenities: inheritedAmenities // Inherit amenities from room type
|
||||||
});
|
});
|
||||||
}}
|
}}
|
||||||
className="w-full px-4 py-3 bg-[#0a0a0a] border border-[#d4af37]/20 rounded-lg text-white focus:ring-2 focus:ring-[#d4af37]/50 focus:border-[#d4af37] transition-all duration-300"
|
className="w-full px-4 py-3 bg-[#0a0a0a] border border-[var(--luxury-gold)]/20 rounded-lg text-white focus:ring-2 focus:ring-[var(--luxury-gold)]/50 focus:border-[var(--luxury-gold)] transition-all duration-300"
|
||||||
required
|
required
|
||||||
>
|
>
|
||||||
{roomTypes.length > 0 ? (
|
{roomTypes.length > 0 ? (
|
||||||
@@ -1430,7 +1430,7 @@ const AdvancedRoomManagementPage: React.FC = () => {
|
|||||||
<select
|
<select
|
||||||
value={roomFormData.status}
|
value={roomFormData.status}
|
||||||
onChange={(e) => setRoomFormData({ ...roomFormData, status: e.target.value as 'available' | 'occupied' | 'maintenance' })}
|
onChange={(e) => setRoomFormData({ ...roomFormData, status: e.target.value as 'available' | 'occupied' | 'maintenance' })}
|
||||||
className="w-full px-4 py-3 bg-[#0a0a0a] border border-[#d4af37]/20 rounded-lg text-white focus:ring-2 focus:ring-[#d4af37]/50 focus:border-[#d4af37] transition-all duration-300"
|
className="w-full px-4 py-3 bg-[#0a0a0a] border border-[var(--luxury-gold)]/20 rounded-lg text-white focus:ring-2 focus:ring-[var(--luxury-gold)]/50 focus:border-[var(--luxury-gold)] transition-all duration-300"
|
||||||
required
|
required
|
||||||
>
|
>
|
||||||
<option value="available" className="bg-[#1a1a1a]">Available</option>
|
<option value="available" className="bg-[#1a1a1a]">Available</option>
|
||||||
@@ -1439,13 +1439,13 @@ const AdvancedRoomManagementPage: React.FC = () => {
|
|||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="flex items-center gap-3 p-4 bg-[#0a0a0a] rounded-lg border border-[#d4af37]/20">
|
<div className="flex items-center gap-3 p-4 bg-[#0a0a0a] rounded-lg border border-[var(--luxury-gold)]/20">
|
||||||
<input
|
<input
|
||||||
type="checkbox"
|
type="checkbox"
|
||||||
id="featured"
|
id="featured"
|
||||||
checked={roomFormData.featured}
|
checked={roomFormData.featured}
|
||||||
onChange={(e) => setRoomFormData({ ...roomFormData, featured: e.target.checked })}
|
onChange={(e) => setRoomFormData({ ...roomFormData, featured: e.target.checked })}
|
||||||
className="w-5 h-5 text-[#d4af37] bg-[#1a1a1a] border-[#d4af37]/30 rounded focus:ring-[#d4af37]/50 focus:ring-2 cursor-pointer transition-all"
|
className="w-5 h-5 text-[var(--luxury-gold)] bg-[#1a1a1a] border-[var(--luxury-gold)]/30 rounded focus:ring-[var(--luxury-gold)]/50 focus:ring-2 cursor-pointer transition-all"
|
||||||
/>
|
/>
|
||||||
<label htmlFor="featured" className="text-sm text-gray-300 cursor-pointer font-medium">
|
<label htmlFor="featured" className="text-sm text-gray-300 cursor-pointer font-medium">
|
||||||
Featured Room
|
Featured Room
|
||||||
@@ -1462,7 +1462,7 @@ const AdvancedRoomManagementPage: React.FC = () => {
|
|||||||
min="0"
|
min="0"
|
||||||
value={roomFormData.price}
|
value={roomFormData.price}
|
||||||
onChange={(e) => setRoomFormData({ ...roomFormData, price: e.target.value })}
|
onChange={(e) => setRoomFormData({ ...roomFormData, price: e.target.value })}
|
||||||
className="w-full px-4 py-3 bg-[#0a0a0a] border border-[#d4af37]/20 rounded-lg text-white placeholder-gray-500 focus:ring-2 focus:ring-[#d4af37]/50 focus:border-[#d4af37] transition-all duration-300"
|
className="w-full px-4 py-3 bg-[#0a0a0a] border border-[var(--luxury-gold)]/20 rounded-lg text-white placeholder-gray-500 focus:ring-2 focus:ring-[var(--luxury-gold)]/50 focus:border-[var(--luxury-gold)] transition-all duration-300"
|
||||||
placeholder="e.g., 150.00"
|
placeholder="e.g., 150.00"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
@@ -1475,7 +1475,7 @@ const AdvancedRoomManagementPage: React.FC = () => {
|
|||||||
value={roomFormData.description}
|
value={roomFormData.description}
|
||||||
onChange={(e) => setRoomFormData({ ...roomFormData, description: e.target.value })}
|
onChange={(e) => setRoomFormData({ ...roomFormData, description: e.target.value })}
|
||||||
rows={4}
|
rows={4}
|
||||||
className="w-full px-4 py-3 bg-[#0a0a0a] border border-[#d4af37]/20 rounded-lg text-white placeholder-gray-500 focus:ring-2 focus:ring-[#d4af37]/50 focus:border-[#d4af37] transition-all duration-300 resize-none"
|
className="w-full px-4 py-3 bg-[#0a0a0a] border border-[var(--luxury-gold)]/20 rounded-lg text-white placeholder-gray-500 focus:ring-2 focus:ring-[var(--luxury-gold)]/50 focus:border-[var(--luxury-gold)] transition-all duration-300 resize-none"
|
||||||
placeholder="Enter the actual description for this specific room..."
|
placeholder="Enter the actual description for this specific room..."
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
@@ -1490,7 +1490,7 @@ const AdvancedRoomManagementPage: React.FC = () => {
|
|||||||
min="1"
|
min="1"
|
||||||
value={roomFormData.capacity}
|
value={roomFormData.capacity}
|
||||||
onChange={(e) => setRoomFormData({ ...roomFormData, capacity: e.target.value })}
|
onChange={(e) => setRoomFormData({ ...roomFormData, capacity: e.target.value })}
|
||||||
className="w-full px-4 py-3 bg-[#0a0a0a] border border-[#d4af37]/20 rounded-lg text-white placeholder-gray-500 focus:ring-2 focus:ring-[#d4af37]/50 focus:border-[#d4af37] transition-all duration-300"
|
className="w-full px-4 py-3 bg-[#0a0a0a] border border-[var(--luxury-gold)]/20 rounded-lg text-white placeholder-gray-500 focus:ring-2 focus:ring-[var(--luxury-gold)]/50 focus:border-[var(--luxury-gold)] transition-all duration-300"
|
||||||
placeholder="e.g., 4"
|
placeholder="e.g., 4"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
@@ -1503,7 +1503,7 @@ const AdvancedRoomManagementPage: React.FC = () => {
|
|||||||
type="text"
|
type="text"
|
||||||
value={roomFormData.room_size}
|
value={roomFormData.room_size}
|
||||||
onChange={(e) => setRoomFormData({ ...roomFormData, room_size: e.target.value })}
|
onChange={(e) => setRoomFormData({ ...roomFormData, room_size: e.target.value })}
|
||||||
className="w-full px-4 py-3 bg-[#0a0a0a] border border-[#d4af37]/20 rounded-lg text-white placeholder-gray-500 focus:ring-2 focus:ring-[#d4af37]/50 focus:border-[#d4af37] transition-all duration-300"
|
className="w-full px-4 py-3 bg-[#0a0a0a] border border-[var(--luxury-gold)]/20 rounded-lg text-white placeholder-gray-500 focus:ring-2 focus:ring-[var(--luxury-gold)]/50 focus:border-[var(--luxury-gold)] transition-all duration-300"
|
||||||
placeholder="e.g., 1 Room, 50 sqm"
|
placeholder="e.g., 1 Room, 50 sqm"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
@@ -1517,19 +1517,19 @@ const AdvancedRoomManagementPage: React.FC = () => {
|
|||||||
type="text"
|
type="text"
|
||||||
value={roomFormData.view}
|
value={roomFormData.view}
|
||||||
onChange={(e) => setRoomFormData({ ...roomFormData, view: e.target.value })}
|
onChange={(e) => setRoomFormData({ ...roomFormData, view: e.target.value })}
|
||||||
className="w-full px-4 py-3 bg-[#0a0a0a] border border-[#d4af37]/20 rounded-lg text-white placeholder-gray-500 focus:ring-2 focus:ring-[#d4af37]/50 focus:border-[#d4af37] transition-all duration-300"
|
className="w-full px-4 py-3 bg-[#0a0a0a] border border-[var(--luxury-gold)]/20 rounded-lg text-white placeholder-gray-500 focus:ring-2 focus:ring-[var(--luxury-gold)]/50 focus:border-[var(--luxury-gold)] transition-all duration-300"
|
||||||
placeholder="e.g., City View, Ocean View, Mountain View"
|
placeholder="e.g., City View, Ocean View, Mountain View"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="space-y-4">
|
<div className="space-y-4">
|
||||||
<h3 className="text-lg font-serif font-semibold text-[#d4af37] mb-4 flex items-center gap-2">
|
<h3 className="text-lg font-serif font-semibold text-[var(--luxury-gold)] mb-4 flex items-center gap-2">
|
||||||
<div className="w-1 h-6 bg-gradient-to-b from-[#d4af37] to-[#c9a227]" />
|
<div className="w-1 h-6 bg-gradient-to-b from-[var(--luxury-gold)] to-[var(--luxury-gold-dark)]" />
|
||||||
Amenities & Features
|
Amenities & Features
|
||||||
</h3>
|
</h3>
|
||||||
|
|
||||||
<div className="border border-[#d4af37]/20 rounded-lg p-4 bg-[#0a0a0a]/50 backdrop-blur-sm">
|
<div className="border border-[var(--luxury-gold)]/20 rounded-lg p-4 bg-[#0a0a0a]/50 backdrop-blur-sm">
|
||||||
{(() => {
|
{(() => {
|
||||||
const selectedRoomType = roomTypesList.find(rt => rt.id === roomFormData.room_type_id);
|
const selectedRoomType = roomTypesList.find(rt => rt.id === roomFormData.room_type_id);
|
||||||
const inheritedAmenities = selectedRoomType ? getAmenitiesArray(selectedRoomType.amenities) : [];
|
const inheritedAmenities = selectedRoomType ? getAmenitiesArray(selectedRoomType.amenities) : [];
|
||||||
@@ -1549,15 +1549,15 @@ const AdvancedRoomManagementPage: React.FC = () => {
|
|||||||
<>
|
<>
|
||||||
<div className="mb-3">
|
<div className="mb-3">
|
||||||
<p className="text-xs text-gray-400 italic flex items-center gap-2">
|
<p className="text-xs text-gray-400 italic flex items-center gap-2">
|
||||||
<Check className="w-3 h-3 text-[#d4af37]" />
|
<Check className="w-3 h-3 text-[var(--luxury-gold)]" />
|
||||||
Inherited from room type: <strong className="text-[#d4af37]">{selectedRoomType?.name}</strong>
|
Inherited from room type: <strong className="text-[var(--luxury-gold)]">{selectedRoomType?.name}</strong>
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
<div className="flex flex-wrap gap-2">
|
<div className="flex flex-wrap gap-2">
|
||||||
{inheritedAmenities.map((amenity) => (
|
{inheritedAmenities.map((amenity) => (
|
||||||
<span
|
<span
|
||||||
key={amenity}
|
key={amenity}
|
||||||
className="px-3 py-1.5 bg-gradient-to-r from-[#d4af37]/20 to-[#c9a227]/20 border border-[#d4af37]/30 rounded-lg text-sm text-[#d4af37] font-medium"
|
className="px-3 py-1.5 bg-gradient-to-r from-[var(--luxury-gold)]/20 to-[var(--luxury-gold-dark)]/20 border border-[var(--luxury-gold)]/30 rounded-lg text-sm text-[var(--luxury-gold)] font-medium"
|
||||||
>
|
>
|
||||||
{amenity}
|
{amenity}
|
||||||
</span>
|
</span>
|
||||||
@@ -1572,17 +1572,17 @@ const AdvancedRoomManagementPage: React.FC = () => {
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="flex gap-4 pt-4 border-t border-[#d4af37]/20">
|
<div className="flex gap-4 pt-4 border-t border-[var(--luxury-gold)]/20">
|
||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
onClick={() => setShowRoomModal(false)}
|
onClick={() => setShowRoomModal(false)}
|
||||||
className="flex-1 px-6 py-3 border border-[#d4af37]/30 text-gray-300 rounded-lg hover:bg-[#d4af37]/10 hover:border-[#d4af37] transition-all duration-300 font-medium"
|
className="flex-1 px-6 py-3 border border-[var(--luxury-gold)]/30 text-gray-300 rounded-lg hover:bg-[var(--luxury-gold)]/10 hover:border-[var(--luxury-gold)] transition-all duration-300 font-medium"
|
||||||
>
|
>
|
||||||
Cancel
|
Cancel
|
||||||
</button>
|
</button>
|
||||||
<button
|
<button
|
||||||
type="submit"
|
type="submit"
|
||||||
className="flex-1 px-6 py-3 bg-gradient-to-r from-[#d4af37] to-[#c9a227] text-[#0f0f0f] rounded-lg hover:from-[#f5d76e] hover:to-[#d4af37] transition-all duration-300 font-semibold shadow-lg shadow-[#d4af37]/30 hover:shadow-xl hover:shadow-[#d4af37]/40"
|
className="flex-1 px-6 py-3 bg-gradient-to-r from-[var(--luxury-gold)] to-[var(--luxury-gold-dark)] text-[#0f0f0f] rounded-lg hover:from-[var(--luxury-gold-light)] hover:to-[var(--luxury-gold)] transition-all duration-300 font-semibold shadow-lg shadow-[var(--luxury-gold)]/30 hover:shadow-xl hover:shadow-[var(--luxury-gold)]/40"
|
||||||
>
|
>
|
||||||
{editingRoom ? 'Update Room' : 'Add Room'}
|
{editingRoom ? 'Update Room' : 'Add Room'}
|
||||||
</button>
|
</button>
|
||||||
@@ -1590,9 +1590,9 @@ const AdvancedRoomManagementPage: React.FC = () => {
|
|||||||
</form>
|
</form>
|
||||||
|
|
||||||
{editingRoom && (
|
{editingRoom && (
|
||||||
<div className="mt-8 pt-8 border-t border-[#d4af37]/20">
|
<div className="mt-8 pt-8 border-t border-[var(--luxury-gold)]/20">
|
||||||
<h3 className="text-lg font-serif font-semibold text-[#d4af37] mb-6 flex items-center gap-2">
|
<h3 className="text-lg font-serif font-semibold text-[var(--luxury-gold)] mb-6 flex items-center gap-2">
|
||||||
<div className="w-1 h-6 bg-gradient-to-b from-[#d4af37] to-[#c9a227]" />
|
<div className="w-1 h-6 bg-gradient-to-b from-[var(--luxury-gold)] to-[var(--luxury-gold-dark)]" />
|
||||||
<ImageIcon className="w-5 h-5" />
|
<ImageIcon className="w-5 h-5" />
|
||||||
Room Images
|
Room Images
|
||||||
</h3>
|
</h3>
|
||||||
@@ -1655,8 +1655,8 @@ const AdvancedRoomManagementPage: React.FC = () => {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<div key={index} className="relative group">
|
<div key={index} className="relative group">
|
||||||
<div className="overflow-hidden rounded-lg border border-[#d4af37]/20
|
<div className="overflow-hidden rounded-lg border border-[var(--luxury-gold)]/20
|
||||||
hover:border-[#d4af37] transition-all duration-300">
|
hover:border-[var(--luxury-gold)] transition-all duration-300">
|
||||||
<img
|
<img
|
||||||
src={imageUrl}
|
src={imageUrl}
|
||||||
alt={`Room Image ${index + 1}`}
|
alt={`Room Image ${index + 1}`}
|
||||||
@@ -1669,7 +1669,7 @@ const AdvancedRoomManagementPage: React.FC = () => {
|
|||||||
</div>
|
</div>
|
||||||
{isRoomImage && (
|
{isRoomImage && (
|
||||||
<>
|
<>
|
||||||
<div className="absolute top-2 left-2 bg-[#d4af37]/90 backdrop-blur-sm
|
<div className="absolute top-2 left-2 bg-[var(--luxury-gold)]/90 backdrop-blur-sm
|
||||||
text-[#0f0f0f] px-2 py-1 rounded text-xs font-semibold
|
text-[#0f0f0f] px-2 py-1 rounded text-xs font-semibold
|
||||||
opacity-0 group-hover:opacity-100 transition-opacity duration-300">
|
opacity-0 group-hover:opacity-100 transition-opacity duration-300">
|
||||||
Room Image
|
Room Image
|
||||||
@@ -1692,7 +1692,7 @@ const AdvancedRoomManagementPage: React.FC = () => {
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
) : (
|
) : (
|
||||||
<div className="mb-6 p-8 bg-[#0a0a0a]/50 rounded-lg border border-[#d4af37]/10 text-center">
|
<div className="mb-6 p-8 bg-[#0a0a0a]/50 rounded-lg border border-[var(--luxury-gold)]/10 text-center">
|
||||||
<ImageIcon className="w-12 h-12 text-gray-600 mx-auto mb-3" />
|
<ImageIcon className="w-12 h-12 text-gray-600 mx-auto mb-3" />
|
||||||
<p className="text-sm text-gray-400 font-light">
|
<p className="text-sm text-gray-400 font-light">
|
||||||
No images uploaded yet. Upload images below to display them here.
|
No images uploaded yet. Upload images below to display them here.
|
||||||
@@ -1717,10 +1717,10 @@ const AdvancedRoomManagementPage: React.FC = () => {
|
|||||||
className="w-full text-sm text-gray-400
|
className="w-full text-sm text-gray-400
|
||||||
file:mr-4 file:py-3 file:px-6 file:rounded-lg file:border-0
|
file:mr-4 file:py-3 file:px-6 file:rounded-lg file:border-0
|
||||||
file:text-sm file:font-semibold
|
file:text-sm file:font-semibold
|
||||||
file:bg-gradient-to-r file:from-[#d4af37]/20 file:to-[#c9a227]/20
|
file:bg-gradient-to-r file:from-[var(--luxury-gold)]/20 file:to-[var(--luxury-gold-dark)]/20
|
||||||
file:text-[#d4af37] file:border file:border-[#d4af37]/30
|
file:text-[var(--luxury-gold)] file:border file:border-[var(--luxury-gold)]/30
|
||||||
hover:file:from-[#d4af37]/30 hover:file:to-[#c9a227]/30
|
hover:file:from-[var(--luxury-gold)]/30 hover:file:to-[var(--luxury-gold-dark)]/30
|
||||||
hover:file:border-[#d4af37] file:cursor-pointer
|
hover:file:border-[var(--luxury-gold)] file:cursor-pointer
|
||||||
transition-all duration-300 bg-[#0a0a0a] rounded-lg"
|
transition-all duration-300 bg-[#0a0a0a] rounded-lg"
|
||||||
/>
|
/>
|
||||||
<p className="text-xs text-gray-500 mt-1 italic">
|
<p className="text-xs text-gray-500 mt-1 italic">
|
||||||
@@ -1742,7 +1742,7 @@ const AdvancedRoomManagementPage: React.FC = () => {
|
|||||||
type="button"
|
type="button"
|
||||||
onClick={handleUploadImages}
|
onClick={handleUploadImages}
|
||||||
disabled={uploadingImages || selectedFiles.length === 0}
|
disabled={uploadingImages || selectedFiles.length === 0}
|
||||||
className="px-6 py-2 bg-gradient-to-r from-[#d4af37] to-[#c9a227] text-[#0f0f0f] rounded-lg hover:from-[#f5d76e] hover:to-[#d4af37] transition-all duration-300 font-semibold shadow-lg shadow-[#d4af37]/30 hover:shadow-xl hover:shadow-[#d4af37]/40 disabled:opacity-50 disabled:cursor-not-allowed"
|
className="px-6 py-2 bg-gradient-to-r from-[var(--luxury-gold)] to-[var(--luxury-gold-dark)] text-[#0f0f0f] rounded-lg hover:from-[var(--luxury-gold-light)] hover:to-[var(--luxury-gold)] transition-all duration-300 font-semibold shadow-lg shadow-[var(--luxury-gold)]/30 hover:shadow-xl hover:shadow-[var(--luxury-gold)]/40 disabled:opacity-50 disabled:cursor-not-allowed"
|
||||||
>
|
>
|
||||||
{uploadingImages ? 'Uploading...' : 'Upload Images'}
|
{uploadingImages ? 'Uploading...' : 'Upload Images'}
|
||||||
</button>
|
</button>
|
||||||
@@ -1752,7 +1752,7 @@ const AdvancedRoomManagementPage: React.FC = () => {
|
|||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
<div className="mt-8 pt-6 border-t border-[#d4af37]/20 flex justify-end">
|
<div className="mt-8 pt-6 border-t border-[var(--luxury-gold)]/20 flex justify-end">
|
||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
import React, { useState, useEffect } from 'react';
|
import React, { useState, useEffect } from 'react';
|
||||||
|
import { useNavigate } from 'react-router-dom';
|
||||||
import {
|
import {
|
||||||
BarChart3,
|
BarChart3,
|
||||||
FileText,
|
FileText,
|
||||||
@@ -26,7 +27,8 @@ import {
|
|||||||
CreditCard,
|
CreditCard,
|
||||||
Building2,
|
Building2,
|
||||||
Target,
|
Target,
|
||||||
Award
|
Award,
|
||||||
|
ChevronRight
|
||||||
} from 'lucide-react';
|
} from 'lucide-react';
|
||||||
import { toast } from 'react-toastify';
|
import { toast } from 'react-toastify';
|
||||||
import Loading from '../../shared/components/Loading';
|
import Loading from '../../shared/components/Loading';
|
||||||
@@ -36,7 +38,6 @@ import Pagination from '../../shared/components/Pagination';
|
|||||||
import CurrencyIcon from '../../shared/components/CurrencyIcon';
|
import CurrencyIcon from '../../shared/components/CurrencyIcon';
|
||||||
import { useAsync } from '../../shared/hooks/useAsync';
|
import { useAsync } from '../../shared/hooks/useAsync';
|
||||||
import reportService, { ReportData } from '../../features/analytics/services/reportService';
|
import reportService, { ReportData } from '../../features/analytics/services/reportService';
|
||||||
import reviewService, { Review } from '../../features/reviews/services/reviewService';
|
|
||||||
import { auditService, AuditLog, AuditLogFilters } from '../../features/analytics/services/auditService';
|
import { auditService, AuditLog, AuditLogFilters } from '../../features/analytics/services/auditService';
|
||||||
import { formatDate } from '../../shared/utils/format';
|
import { formatDate } from '../../shared/utils/format';
|
||||||
import { useFormatCurrency } from '../../features/payments/hooks/useFormatCurrency';
|
import { useFormatCurrency } from '../../features/payments/hooks/useFormatCurrency';
|
||||||
@@ -62,10 +63,11 @@ import { SimpleBarChart, SimpleLineChart, SimplePieChart, KPICard } from '../../
|
|||||||
import { exportData } from '../../shared/utils/exportUtils';
|
import { exportData } from '../../shared/utils/exportUtils';
|
||||||
import CustomReportBuilder from '../../features/analytics/components/CustomReportBuilder';
|
import CustomReportBuilder from '../../features/analytics/components/CustomReportBuilder';
|
||||||
|
|
||||||
type AnalyticsTab = 'overview' | 'reports' | 'revenue' | 'operational' | 'guest' | 'financial' | 'audit-logs' | 'reviews';
|
type AnalyticsTab = 'overview' | 'reports' | 'revenue' | 'operational' | 'guest' | 'financial' | 'audit-logs';
|
||||||
|
|
||||||
const AnalyticsDashboardPage: React.FC = () => {
|
const AnalyticsDashboardPage: React.FC = () => {
|
||||||
const { formatCurrency } = useFormatCurrency();
|
const { formatCurrency } = useFormatCurrency();
|
||||||
|
const navigate = useNavigate();
|
||||||
const [activeTab, setActiveTab] = useState<AnalyticsTab>('overview');
|
const [activeTab, setActiveTab] = useState<AnalyticsTab>('overview');
|
||||||
const [showReportBuilder, setShowReportBuilder] = useState(false);
|
const [showReportBuilder, setShowReportBuilder] = useState(false);
|
||||||
const [analyticsDateRange, setAnalyticsDateRange] = useState({
|
const [analyticsDateRange, setAnalyticsDateRange] = useState({
|
||||||
@@ -96,16 +98,6 @@ const AnalyticsDashboardPage: React.FC = () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
const [reviews, setReviews] = useState<Review[]>([]);
|
|
||||||
const [reviewsLoading, setReviewsLoading] = useState(true);
|
|
||||||
const [reviewsFilters, setReviewsFilters] = useState({
|
|
||||||
status: '',
|
|
||||||
});
|
|
||||||
const [reviewsCurrentPage, setReviewsCurrentPage] = useState(1);
|
|
||||||
const [reviewsTotalPages, setReviewsTotalPages] = useState(1);
|
|
||||||
const [reviewsTotalItems, setReviewsTotalItems] = useState(0);
|
|
||||||
const reviewsPerPage = 5;
|
|
||||||
|
|
||||||
// Advanced Analytics Data
|
// Advanced Analytics Data
|
||||||
const { data: comprehensiveData, loading: comprehensiveLoading, execute: fetchComprehensive } = useAsync<ComprehensiveAnalyticsData>(
|
const { data: comprehensiveData, loading: comprehensiveLoading, execute: fetchComprehensive } = useAsync<ComprehensiveAnalyticsData>(
|
||||||
() => analyticsService.getComprehensiveAnalytics({
|
() => analyticsService.getComprehensiveAnalytics({
|
||||||
@@ -214,8 +206,6 @@ const AnalyticsDashboardPage: React.FC = () => {
|
|||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (activeTab === 'audit-logs') {
|
if (activeTab === 'audit-logs') {
|
||||||
fetchLogs();
|
fetchLogs();
|
||||||
} else if (activeTab === 'reviews') {
|
|
||||||
fetchReviews();
|
|
||||||
} else if (activeTab === 'overview') {
|
} else if (activeTab === 'overview') {
|
||||||
fetchComprehensive();
|
fetchComprehensive();
|
||||||
} else if (activeTab === 'revenue') {
|
} else if (activeTab === 'revenue') {
|
||||||
@@ -227,17 +217,12 @@ const AnalyticsDashboardPage: React.FC = () => {
|
|||||||
} else if (activeTab === 'financial') {
|
} else if (activeTab === 'financial') {
|
||||||
Promise.all([fetchProfitLoss(), fetchPaymentMethods(), fetchRefunds()]);
|
Promise.all([fetchProfitLoss(), fetchPaymentMethods(), fetchRefunds()]);
|
||||||
}
|
}
|
||||||
}, [activeTab, auditFilters, currentPage, reviewsFilters, reviewsCurrentPage, analyticsDateRange]);
|
}, [activeTab, auditFilters, currentPage, analyticsDateRange]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
setAuditFilters(prev => ({ ...prev, page: currentPage }));
|
setAuditFilters(prev => ({ ...prev, page: currentPage }));
|
||||||
}, [currentPage]);
|
}, [currentPage]);
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
if (activeTab === 'reviews') {
|
|
||||||
setReviewsCurrentPage(1);
|
|
||||||
}
|
|
||||||
}, [reviewsFilters, activeTab]);
|
|
||||||
|
|
||||||
const fetchLogs = async () => {
|
const fetchLogs = async () => {
|
||||||
try {
|
try {
|
||||||
@@ -400,76 +385,6 @@ const AnalyticsDashboardPage: React.FC = () => {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
const fetchReviews = async () => {
|
|
||||||
try {
|
|
||||||
setReviewsLoading(true);
|
|
||||||
const response = await reviewService.getReviews({
|
|
||||||
...reviewsFilters,
|
|
||||||
page: reviewsCurrentPage,
|
|
||||||
limit: reviewsPerPage,
|
|
||||||
});
|
|
||||||
setReviews(response.data.reviews);
|
|
||||||
if (response.data.pagination) {
|
|
||||||
setReviewsTotalPages(response.data.pagination.totalPages);
|
|
||||||
setReviewsTotalItems(response.data.pagination.total);
|
|
||||||
}
|
|
||||||
} catch (error: any) {
|
|
||||||
toast.error(error.response?.data?.message || 'Unable to load reviews list');
|
|
||||||
} finally {
|
|
||||||
setReviewsLoading(false);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const handleApproveReview = async (id: number) => {
|
|
||||||
try {
|
|
||||||
await reviewService.approveReview(id);
|
|
||||||
toast.success('Review approved successfully');
|
|
||||||
fetchReviews();
|
|
||||||
} catch (error: any) {
|
|
||||||
toast.error(error.response?.data?.message || 'Unable to approve review');
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const handleRejectReview = async (id: number) => {
|
|
||||||
if (!window.confirm('Are you sure you want to reject this review?')) return;
|
|
||||||
|
|
||||||
try {
|
|
||||||
await reviewService.rejectReview(id);
|
|
||||||
toast.success('Review rejected successfully');
|
|
||||||
fetchReviews();
|
|
||||||
} catch (error: any) {
|
|
||||||
toast.error(error.response?.data?.message || 'Unable to reject review');
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const getReviewStatusBadge = (status: string) => {
|
|
||||||
const badges: Record<string, { bg: string; text: string; label: string; border: string }> = {
|
|
||||||
pending: { bg: 'bg-gradient-to-r from-amber-50 to-yellow-50', text: 'text-amber-800', label: 'Pending', border: 'border-amber-200' },
|
|
||||||
approved: { bg: 'bg-gradient-to-r from-emerald-50 to-green-50', text: 'text-emerald-800', label: 'Approved', border: 'border-emerald-200' },
|
|
||||||
rejected: { bg: 'bg-gradient-to-r from-rose-50 to-red-50', text: 'text-rose-800', label: 'Rejected', border: 'border-rose-200' },
|
|
||||||
};
|
|
||||||
const badge = badges[status] || badges.pending;
|
|
||||||
return (
|
|
||||||
<span className={`px-3 py-1.5 rounded-full text-xs font-semibold border shadow-sm ${badge.bg} ${badge.text} ${badge.border}`}>
|
|
||||||
{badge.label}
|
|
||||||
</span>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
const renderStars = (rating: number) => {
|
|
||||||
return (
|
|
||||||
<div className="flex items-center gap-0.5">
|
|
||||||
{[1, 2, 3, 4, 5].map((star) => (
|
|
||||||
<Star
|
|
||||||
key={star}
|
|
||||||
className={`w-4 h-4 ${star <= rating ? 'text-amber-400 fill-amber-400' : 'text-gray-300'}`}
|
|
||||||
/>
|
|
||||||
))}
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
const tabs = [
|
const tabs = [
|
||||||
{ id: 'overview' as AnalyticsTab, label: 'Overview', icon: BarChart3 },
|
{ id: 'overview' as AnalyticsTab, label: 'Overview', icon: BarChart3 },
|
||||||
{ id: 'revenue' as AnalyticsTab, label: 'Revenue', icon: DollarSign },
|
{ id: 'revenue' as AnalyticsTab, label: 'Revenue', icon: DollarSign },
|
||||||
@@ -478,7 +393,6 @@ const AnalyticsDashboardPage: React.FC = () => {
|
|||||||
{ id: 'financial' as AnalyticsTab, label: 'Financial', icon: CreditCard },
|
{ id: 'financial' as AnalyticsTab, label: 'Financial', icon: CreditCard },
|
||||||
{ id: 'reports' as AnalyticsTab, label: 'Reports', icon: FileText },
|
{ id: 'reports' as AnalyticsTab, label: 'Reports', icon: FileText },
|
||||||
{ id: 'audit-logs' as AnalyticsTab, label: 'Audit Logs', icon: ClipboardList },
|
{ id: 'audit-logs' as AnalyticsTab, label: 'Audit Logs', icon: ClipboardList },
|
||||||
{ id: 'reviews' as AnalyticsTab, label: 'Reviews', icon: Star },
|
|
||||||
];
|
];
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@@ -668,7 +582,7 @@ const AnalyticsDashboardPage: React.FC = () => {
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Quick Access Cards */}
|
{/* Quick Access Cards */}
|
||||||
<div className="grid grid-cols-1 md:grid-cols-3 gap-6 lg:gap-8">
|
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-6 lg:gap-8">
|
||||||
<div
|
<div
|
||||||
onClick={() => setActiveTab('revenue')}
|
onClick={() => setActiveTab('revenue')}
|
||||||
className="group relative bg-white/90 backdrop-blur-xl rounded-2xl shadow-xl border border-green-100/50 p-8 cursor-pointer transition-all duration-300 hover:shadow-2xl hover:scale-105 hover:border-green-300/60 overflow-hidden"
|
className="group relative bg-white/90 backdrop-blur-xl rounded-2xl shadow-xl border border-green-100/50 p-8 cursor-pointer transition-all duration-300 hover:shadow-2xl hover:scale-105 hover:border-green-300/60 overflow-hidden"
|
||||||
@@ -731,6 +645,34 @@ const AnalyticsDashboardPage: React.FC = () => {
|
|||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div
|
||||||
|
onClick={() => navigate('/admin/reviews')}
|
||||||
|
className="group relative bg-white/90 backdrop-blur-xl rounded-2xl shadow-xl border border-amber-100/50 p-8 cursor-pointer transition-all duration-300 hover:shadow-2xl hover:scale-105 hover:border-amber-300/60 overflow-hidden"
|
||||||
|
>
|
||||||
|
<div className="absolute top-0 right-0 w-32 h-32 bg-gradient-to-br from-amber-400/10 to-transparent rounded-bl-full"></div>
|
||||||
|
<div className="relative space-y-5">
|
||||||
|
<div className="flex items-center gap-4">
|
||||||
|
<div className="p-3.5 rounded-xl bg-gradient-to-br from-amber-500 to-amber-600 shadow-lg">
|
||||||
|
<Star className="w-6 h-6 text-white" />
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<h3 className="font-bold text-gray-900 text-xl mb-1">Review Management</h3>
|
||||||
|
<div className="h-1 w-12 bg-gradient-to-r from-amber-500 to-amber-600 rounded-full"></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<p className="text-gray-600 text-sm leading-relaxed">
|
||||||
|
Approve and manage customer reviews and ratings
|
||||||
|
</p>
|
||||||
|
<div className="pt-5 border-t border-gray-100">
|
||||||
|
<div className="flex items-center justify-between text-sm">
|
||||||
|
<span className="text-gray-500 font-medium">Manage Reviews</span>
|
||||||
|
<ChevronRight className="w-5 h-5 text-amber-600 group-hover:translate-x-1 transition-transform" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div className="absolute inset-0 bg-gradient-to-r from-transparent via-amber-50/30 to-transparent opacity-0 group-hover:opacity-100 transition-opacity pointer-events-none"></div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</>
|
</>
|
||||||
) : (
|
) : (
|
||||||
@@ -1275,169 +1217,6 @@ const AnalyticsDashboardPage: React.FC = () => {
|
|||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{}
|
|
||||||
{activeTab === 'reviews' && (
|
|
||||||
<div className="space-y-8">
|
|
||||||
{}
|
|
||||||
<div className="bg-white/90 backdrop-blur-xl rounded-2xl shadow-xl border border-gray-200/50 p-8">
|
|
||||||
<div className="space-y-3">
|
|
||||||
<div className="flex items-center gap-3">
|
|
||||||
<div className="p-2.5 rounded-xl bg-gradient-to-br from-amber-500/10 to-yellow-500/10 border border-amber-200/40">
|
|
||||||
<Star className="w-6 h-6 text-amber-600 fill-amber-600" />
|
|
||||||
</div>
|
|
||||||
<h2 className="text-3xl font-extrabold text-gray-900">Review Management</h2>
|
|
||||||
</div>
|
|
||||||
<p className="text-gray-600 text-base max-w-2xl leading-relaxed">
|
|
||||||
Approve and manage customer reviews and ratings
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{}
|
|
||||||
<div className="relative bg-white/90 backdrop-blur-xl rounded-2xl shadow-xl border border-gray-200/50 p-8">
|
|
||||||
<div className="flex items-center gap-3 mb-6">
|
|
||||||
<div className="p-2 rounded-xl bg-gradient-to-br from-amber-500/10 to-amber-600/10 border border-amber-200/40">
|
|
||||||
<Filter className="w-5 h-5 text-amber-600" />
|
|
||||||
</div>
|
|
||||||
<h3 className="text-xl font-bold text-gray-900">Filters</h3>
|
|
||||||
</div>
|
|
||||||
<div className="grid grid-cols-1 md:grid-cols-2 gap-6">
|
|
||||||
<div className="space-y-2">
|
|
||||||
<label className="block text-sm font-semibold text-gray-900">Status</label>
|
|
||||||
<select
|
|
||||||
value={reviewsFilters.status}
|
|
||||||
onChange={(e) => setReviewsFilters({ ...reviewsFilters, status: e.target.value })}
|
|
||||||
className="w-full px-4 py-3 bg-white border-2 border-gray-200 rounded-xl focus:border-amber-400 focus:ring-4 focus:ring-amber-100 transition-all duration-200 text-gray-700 font-medium shadow-sm hover:shadow-md cursor-pointer"
|
|
||||||
>
|
|
||||||
<option value="">All statuses</option>
|
|
||||||
<option value="pending">Pending</option>
|
|
||||||
<option value="approved">Approved</option>
|
|
||||||
<option value="rejected">Rejected</option>
|
|
||||||
</select>
|
|
||||||
</div>
|
|
||||||
<div className="flex items-end">
|
|
||||||
<div className="w-full px-4 py-3 bg-gradient-to-r from-gray-50 to-white border-2 border-gray-200 rounded-xl">
|
|
||||||
<span className="text-sm font-semibold text-gray-700">
|
|
||||||
{reviewsTotalItems} review{reviewsTotalItems !== 1 ? 's' : ''}
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{}
|
|
||||||
{reviewsLoading && reviews.length === 0 ? (
|
|
||||||
<Loading fullScreen text="Loading reviews..." />
|
|
||||||
) : reviews.length === 0 ? (
|
|
||||||
<div className="relative bg-white/90 backdrop-blur-xl rounded-2xl shadow-xl border border-gray-200/50 p-8">
|
|
||||||
<EmptyState
|
|
||||||
title="No Reviews Found"
|
|
||||||
description="No reviews match your current filters."
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
) : (
|
|
||||||
<>
|
|
||||||
<div className="relative bg-white/90 backdrop-blur-xl rounded-2xl shadow-xl border border-gray-200/50 overflow-hidden">
|
|
||||||
<div className="overflow-x-auto">
|
|
||||||
<table className="min-w-full divide-y divide-gray-200">
|
|
||||||
<thead className="bg-gradient-to-r from-gray-50 to-gray-100">
|
|
||||||
<tr>
|
|
||||||
<th className="px-8 py-5 text-left text-xs font-bold text-gray-700 uppercase tracking-wider border-b border-gray-200">User</th>
|
|
||||||
<th className="px-8 py-5 text-left text-xs font-bold text-gray-700 uppercase tracking-wider border-b border-gray-200">Room</th>
|
|
||||||
<th className="px-8 py-5 text-left text-xs font-bold text-gray-700 uppercase tracking-wider border-b border-gray-200">Rating</th>
|
|
||||||
<th className="px-8 py-5 text-left text-xs font-bold text-gray-700 uppercase tracking-wider border-b border-gray-200">Comment</th>
|
|
||||||
<th className="px-8 py-5 text-left text-xs font-bold text-gray-700 uppercase tracking-wider border-b border-gray-200">Created Date</th>
|
|
||||||
<th className="px-8 py-5 text-left text-xs font-bold text-gray-700 uppercase tracking-wider border-b border-gray-200">Status</th>
|
|
||||||
<th className="px-8 py-5 text-right text-xs font-bold text-gray-700 uppercase tracking-wider border-b border-gray-200">Actions</th>
|
|
||||||
</tr>
|
|
||||||
</thead>
|
|
||||||
<tbody className="bg-white divide-y divide-gray-200">
|
|
||||||
{reviews.map((review) => (
|
|
||||||
<tr key={review.id} className="hover:bg-gray-50 transition-colors">
|
|
||||||
<td className="px-8 py-5 whitespace-nowrap">
|
|
||||||
<div className="text-sm font-semibold text-gray-900">{review.user?.name}</div>
|
|
||||||
{review.user?.email && (
|
|
||||||
<div className="text-xs text-gray-500">{review.user.email}</div>
|
|
||||||
)}
|
|
||||||
</td>
|
|
||||||
<td className="px-8 py-5 whitespace-nowrap">
|
|
||||||
<div className="text-sm font-medium text-gray-900">
|
|
||||||
Room {review.room?.room_number}
|
|
||||||
</div>
|
|
||||||
{review.room?.room_type?.name && (
|
|
||||||
<div className="text-xs text-gray-500">{review.room.room_type.name}</div>
|
|
||||||
)}
|
|
||||||
</td>
|
|
||||||
<td className="px-8 py-5 whitespace-nowrap">
|
|
||||||
<div className="flex items-center gap-2">
|
|
||||||
{renderStars(review.rating)}
|
|
||||||
<span className="text-sm font-semibold text-gray-700">({review.rating})</span>
|
|
||||||
</div>
|
|
||||||
</td>
|
|
||||||
<td className="px-8 py-5">
|
|
||||||
<div className="text-sm text-gray-900 max-w-xs">{review.comment}</div>
|
|
||||||
</td>
|
|
||||||
<td className="px-8 py-5 whitespace-nowrap">
|
|
||||||
<div className="text-sm text-gray-600">
|
|
||||||
{new Date(review.created_at).toLocaleDateString('en-US', {
|
|
||||||
year: 'numeric',
|
|
||||||
month: 'short',
|
|
||||||
day: 'numeric'
|
|
||||||
})}
|
|
||||||
</div>
|
|
||||||
</td>
|
|
||||||
<td className="px-8 py-5 whitespace-nowrap">
|
|
||||||
{getReviewStatusBadge(review.status)}
|
|
||||||
</td>
|
|
||||||
<td className="px-8 py-5 whitespace-nowrap text-right text-sm font-medium">
|
|
||||||
{review.status === 'pending' && (
|
|
||||||
<div className="flex items-center justify-end gap-3">
|
|
||||||
<button
|
|
||||||
onClick={() => handleApproveReview(review.id)}
|
|
||||||
className="px-4 py-2 bg-gradient-to-r from-emerald-500 to-emerald-600 text-white rounded-lg hover:from-emerald-600 hover:to-emerald-700 transition-all duration-200 shadow-lg hover:shadow-xl font-semibold flex items-center gap-2"
|
|
||||||
title="Approve"
|
|
||||||
>
|
|
||||||
<CheckCircle className="w-4 h-4" />
|
|
||||||
Approve
|
|
||||||
</button>
|
|
||||||
<button
|
|
||||||
onClick={() => handleRejectReview(review.id)}
|
|
||||||
className="px-4 py-2 bg-gradient-to-r from-rose-500 to-rose-600 text-white rounded-lg hover:from-rose-600 hover:to-rose-700 transition-all duration-200 shadow-lg hover:shadow-xl font-semibold flex items-center gap-2"
|
|
||||||
title="Reject"
|
|
||||||
>
|
|
||||||
<XCircle className="w-4 h-4" />
|
|
||||||
Reject
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
{review.status !== 'pending' && (
|
|
||||||
<span className="text-gray-400 text-xs">No actions available</span>
|
|
||||||
)}
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
))}
|
|
||||||
</tbody>
|
|
||||||
</table>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{}
|
|
||||||
{reviewsTotalPages > 1 && (
|
|
||||||
<div className="flex justify-center">
|
|
||||||
<Pagination
|
|
||||||
currentPage={reviewsCurrentPage}
|
|
||||||
totalPages={reviewsTotalPages}
|
|
||||||
onPageChange={setReviewsCurrentPage}
|
|
||||||
totalItems={reviewsTotalItems}
|
|
||||||
itemsPerPage={reviewsPerPage}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
</>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
|
|
||||||
{}
|
{}
|
||||||
{showDetails && selectedLog && (
|
{showDetails && selectedLog && (
|
||||||
<div className="fixed inset-0 bg-black/60 backdrop-blur-sm flex items-center justify-center z-50 p-4">
|
<div className="fixed inset-0 bg-black/60 backdrop-blur-sm flex items-center justify-center z-50 p-4">
|
||||||
|
|||||||
@@ -1,472 +0,0 @@
|
|||||||
import React, { useState, useEffect, useRef } from 'react';
|
|
||||||
import {
|
|
||||||
FileText,
|
|
||||||
Search,
|
|
||||||
Filter,
|
|
||||||
Eye,
|
|
||||||
Activity,
|
|
||||||
AlertCircle,
|
|
||||||
CheckCircle,
|
|
||||||
XCircle,
|
|
||||||
Info
|
|
||||||
} from 'lucide-react';
|
|
||||||
import { toast } from 'react-toastify';
|
|
||||||
import Loading from '../../shared/components/Loading';
|
|
||||||
import EmptyState from '../../shared/components/EmptyState';
|
|
||||||
import Pagination from '../../shared/components/Pagination';
|
|
||||||
import { auditService, AuditLog, AuditLogFilters } from '../../features/analytics/services/auditService';
|
|
||||||
import { formatDate } from '../../shared/utils/format';
|
|
||||||
import { logger } from '../../shared/utils/logger';
|
|
||||||
|
|
||||||
const AuditLogsPage: React.FC = () => {
|
|
||||||
const [logs, setLogs] = useState<AuditLog[]>([]);
|
|
||||||
const [loading, setLoading] = useState(true);
|
|
||||||
const [currentPage, setCurrentPage] = useState(1);
|
|
||||||
const [totalPages, setTotalPages] = useState(1);
|
|
||||||
const [totalItems, setTotalItems] = useState(0);
|
|
||||||
const [selectedLog, setSelectedLog] = useState<AuditLog | null>(null);
|
|
||||||
const [showDetails, setShowDetails] = useState(false);
|
|
||||||
const itemsPerPage = 20;
|
|
||||||
const abortControllerRef = useRef<AbortController | null>(null);
|
|
||||||
|
|
||||||
const [filters, setFilters] = useState<AuditLogFilters>({
|
|
||||||
page: 1,
|
|
||||||
limit: itemsPerPage,
|
|
||||||
});
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
// Cancel previous request if exists
|
|
||||||
if (abortControllerRef.current) {
|
|
||||||
abortControllerRef.current.abort();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Create new abort controller
|
|
||||||
abortControllerRef.current = new AbortController();
|
|
||||||
|
|
||||||
fetchLogs();
|
|
||||||
|
|
||||||
// Cleanup: abort request on unmount
|
|
||||||
return () => {
|
|
||||||
if (abortControllerRef.current) {
|
|
||||||
abortControllerRef.current.abort();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}, [filters, currentPage]);
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
setFilters(prev => ({ ...prev, page: currentPage }));
|
|
||||||
}, [currentPage]);
|
|
||||||
|
|
||||||
const fetchLogs = async () => {
|
|
||||||
try {
|
|
||||||
setLoading(true);
|
|
||||||
const response = await auditService.getAuditLogs({
|
|
||||||
...filters,
|
|
||||||
page: currentPage,
|
|
||||||
limit: itemsPerPage,
|
|
||||||
});
|
|
||||||
setLogs(response.data.logs);
|
|
||||||
if (response.data.pagination) {
|
|
||||||
setTotalPages(response.data.pagination.totalPages);
|
|
||||||
setTotalItems(response.data.pagination.total);
|
|
||||||
}
|
|
||||||
} catch (error: any) {
|
|
||||||
// Handle AbortError silently
|
|
||||||
if (error.name === 'AbortError') {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
// Clear data when API connection fails
|
|
||||||
setLogs([]);
|
|
||||||
setTotalPages(1);
|
|
||||||
setTotalItems(0);
|
|
||||||
logger.error('Error fetching audit logs', error);
|
|
||||||
toast.error(error.response?.data?.message || 'Unable to load audit logs');
|
|
||||||
} finally {
|
|
||||||
setLoading(false);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const handleFilterChange = (key: keyof AuditLogFilters, value: any) => {
|
|
||||||
setFilters(prev => ({ ...prev, [key]: value }));
|
|
||||||
setCurrentPage(1);
|
|
||||||
};
|
|
||||||
|
|
||||||
const handleSearch = (searchTerm: string) => {
|
|
||||||
handleFilterChange('search', searchTerm || undefined);
|
|
||||||
};
|
|
||||||
|
|
||||||
const handleViewDetails = (log: AuditLog) => {
|
|
||||||
setSelectedLog(log);
|
|
||||||
setShowDetails(true);
|
|
||||||
};
|
|
||||||
|
|
||||||
const getStatusIcon = (status: string) => {
|
|
||||||
switch (status) {
|
|
||||||
case 'success':
|
|
||||||
return <CheckCircle className="w-5 h-5 text-green-500" />;
|
|
||||||
case 'failed':
|
|
||||||
return <XCircle className="w-5 h-5 text-red-500" />;
|
|
||||||
case 'error':
|
|
||||||
return <AlertCircle className="w-5 h-5 text-orange-500" />;
|
|
||||||
default:
|
|
||||||
return <Info className="w-5 h-5 text-gray-500" />;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const getStatusBadge = (status: string) => {
|
|
||||||
const baseClasses = "px-2 py-1 rounded-full text-xs font-medium";
|
|
||||||
switch (status) {
|
|
||||||
case 'success':
|
|
||||||
return `${baseClasses} bg-green-100 text-green-800`;
|
|
||||||
case 'failed':
|
|
||||||
return `${baseClasses} bg-red-100 text-red-800`;
|
|
||||||
case 'error':
|
|
||||||
return `${baseClasses} bg-orange-100 text-orange-800`;
|
|
||||||
default:
|
|
||||||
return `${baseClasses} bg-gray-100 text-gray-800`;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
if (loading && logs.length === 0) {
|
|
||||||
return <Loading fullScreen text="Loading audit logs..." />;
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div className="container mx-auto px-4 py-8">
|
|
||||||
<div className="mb-8">
|
|
||||||
<h1 className="text-3xl font-bold text-gray-900 mb-2">Audit Logs</h1>
|
|
||||||
<p className="text-gray-600">View all system activity and actions</p>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{}
|
|
||||||
<div className="mb-6 bg-white p-4 rounded-lg shadow-md">
|
|
||||||
<div className="flex items-center space-x-2 mb-4">
|
|
||||||
<Filter className="w-5 h-5 text-gray-500" />
|
|
||||||
<h3 className="text-lg font-semibold text-gray-900">Filters</h3>
|
|
||||||
</div>
|
|
||||||
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-4">
|
|
||||||
<div>
|
|
||||||
<label className="block text-sm font-medium text-gray-700 mb-1">
|
|
||||||
Search
|
|
||||||
</label>
|
|
||||||
<div className="relative">
|
|
||||||
<Search className="absolute left-3 top-1/2 transform -translate-y-1/2 w-5 h-5 text-gray-400" />
|
|
||||||
<input
|
|
||||||
type="text"
|
|
||||||
placeholder="Search actions, types..."
|
|
||||||
value={filters.search || ''}
|
|
||||||
onChange={(e) => handleSearch(e.target.value)}
|
|
||||||
className="w-full pl-10 pr-4 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-[#d4af37] focus:border-[#d4af37]"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div>
|
|
||||||
<label className="block text-sm font-medium text-gray-700 mb-1">
|
|
||||||
Action
|
|
||||||
</label>
|
|
||||||
<input
|
|
||||||
type="text"
|
|
||||||
placeholder="Filter by action"
|
|
||||||
value={filters.action || ''}
|
|
||||||
onChange={(e) => handleFilterChange('action', e.target.value || undefined)}
|
|
||||||
className="w-full px-4 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-[#d4af37] focus:border-[#d4af37]"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
<div>
|
|
||||||
<label className="block text-sm font-medium text-gray-700 mb-1">
|
|
||||||
Resource Type
|
|
||||||
</label>
|
|
||||||
<input
|
|
||||||
type="text"
|
|
||||||
placeholder="Filter by type"
|
|
||||||
value={filters.resource_type || ''}
|
|
||||||
onChange={(e) => handleFilterChange('resource_type', e.target.value || undefined)}
|
|
||||||
className="w-full px-4 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-[#d4af37] focus:border-[#d4af37]"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
<div>
|
|
||||||
<label className="block text-sm font-medium text-gray-700 mb-1">
|
|
||||||
Status
|
|
||||||
</label>
|
|
||||||
<select
|
|
||||||
value={filters.status || ''}
|
|
||||||
onChange={(e) => handleFilterChange('status', e.target.value || undefined)}
|
|
||||||
className="w-full px-4 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-[#d4af37] focus:border-[#d4af37]"
|
|
||||||
>
|
|
||||||
<option value="">All</option>
|
|
||||||
<option value="success">Success</option>
|
|
||||||
<option value="failed">Failed</option>
|
|
||||||
<option value="error">Error</option>
|
|
||||||
</select>
|
|
||||||
</div>
|
|
||||||
<div>
|
|
||||||
<label className="block text-sm font-medium text-gray-700 mb-1">
|
|
||||||
Start Date
|
|
||||||
</label>
|
|
||||||
<input
|
|
||||||
type="date"
|
|
||||||
value={filters.start_date || ''}
|
|
||||||
onChange={(e) => handleFilterChange('start_date', e.target.value || undefined)}
|
|
||||||
className="w-full px-4 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-[#d4af37] focus:border-[#d4af37]"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
<div>
|
|
||||||
<label className="block text-sm font-medium text-gray-700 mb-1">
|
|
||||||
End Date
|
|
||||||
</label>
|
|
||||||
<input
|
|
||||||
type="date"
|
|
||||||
value={filters.end_date || ''}
|
|
||||||
onChange={(e) => handleFilterChange('end_date', e.target.value || undefined)}
|
|
||||||
className="w-full px-4 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-[#d4af37] focus:border-[#d4af37]"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{}
|
|
||||||
<div className="grid grid-cols-1 md:grid-cols-4 gap-4 mb-6">
|
|
||||||
<div className="bg-white p-4 rounded-lg shadow-md">
|
|
||||||
<div className="flex items-center justify-between">
|
|
||||||
<div>
|
|
||||||
<p className="text-sm text-gray-600">Total Logs</p>
|
|
||||||
<p className="text-2xl font-bold text-gray-900">{totalItems}</p>
|
|
||||||
</div>
|
|
||||||
<FileText className="w-8 h-8 text-[#d4af37]" />
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div className="bg-white p-4 rounded-lg shadow-md">
|
|
||||||
<div className="flex items-center justify-between">
|
|
||||||
<div>
|
|
||||||
<p className="text-sm text-gray-600">Current Page</p>
|
|
||||||
<p className="text-2xl font-bold text-gray-900">{currentPage}</p>
|
|
||||||
</div>
|
|
||||||
<Activity className="w-8 h-8 text-blue-500" />
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{}
|
|
||||||
{logs.length === 0 ? (
|
|
||||||
<EmptyState
|
|
||||||
title="No Audit Logs Found"
|
|
||||||
description="No audit logs match your current filters."
|
|
||||||
/>
|
|
||||||
) : (
|
|
||||||
<div className="bg-white rounded-lg shadow-md overflow-hidden">
|
|
||||||
<div className="overflow-x-auto">
|
|
||||||
<table className="min-w-full divide-y divide-gray-200">
|
|
||||||
<thead className="bg-gray-50">
|
|
||||||
<tr>
|
|
||||||
<th className="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
|
|
||||||
ID
|
|
||||||
</th>
|
|
||||||
<th className="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
|
|
||||||
Action
|
|
||||||
</th>
|
|
||||||
<th className="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
|
|
||||||
Resource
|
|
||||||
</th>
|
|
||||||
<th className="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
|
|
||||||
User
|
|
||||||
</th>
|
|
||||||
<th className="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
|
|
||||||
Status
|
|
||||||
</th>
|
|
||||||
<th className="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
|
|
||||||
IP Address
|
|
||||||
</th>
|
|
||||||
<th className="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
|
|
||||||
Date
|
|
||||||
</th>
|
|
||||||
<th className="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
|
|
||||||
Actions
|
|
||||||
</th>
|
|
||||||
</tr>
|
|
||||||
</thead>
|
|
||||||
<tbody className="bg-white divide-y divide-gray-200">
|
|
||||||
{logs.map((log) => (
|
|
||||||
<tr key={log.id} className="hover:bg-gray-50">
|
|
||||||
<td className="px-6 py-4 whitespace-nowrap text-sm text-gray-900">
|
|
||||||
#{log.id}
|
|
||||||
</td>
|
|
||||||
<td className="px-6 py-4 whitespace-nowrap">
|
|
||||||
<div className="text-sm font-medium text-gray-900">{log.action}</div>
|
|
||||||
</td>
|
|
||||||
<td className="px-6 py-4 whitespace-nowrap">
|
|
||||||
<div className="text-sm text-gray-900">{log.resource_type}</div>
|
|
||||||
{log.resource_id && (
|
|
||||||
<div className="text-xs text-gray-500">ID: {log.resource_id}</div>
|
|
||||||
)}
|
|
||||||
</td>
|
|
||||||
<td className="px-6 py-4 whitespace-nowrap">
|
|
||||||
{log.user ? (
|
|
||||||
<div>
|
|
||||||
<div className="text-sm font-medium text-gray-900">{log.user.full_name}</div>
|
|
||||||
<div className="text-xs text-gray-500">{log.user.email}</div>
|
|
||||||
</div>
|
|
||||||
) : (
|
|
||||||
<span className="text-sm text-gray-400">System</span>
|
|
||||||
)}
|
|
||||||
</td>
|
|
||||||
<td className="px-6 py-4 whitespace-nowrap">
|
|
||||||
<div className="flex items-center space-x-2">
|
|
||||||
{getStatusIcon(log.status)}
|
|
||||||
<span className={getStatusBadge(log.status)}>
|
|
||||||
{log.status}
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
</td>
|
|
||||||
<td className="px-6 py-4 whitespace-nowrap text-sm text-gray-500">
|
|
||||||
{log.ip_address || '-'}
|
|
||||||
</td>
|
|
||||||
<td className="px-6 py-4 whitespace-nowrap text-sm text-gray-500">
|
|
||||||
{formatDate(log.created_at)}
|
|
||||||
</td>
|
|
||||||
<td className="px-6 py-4 whitespace-nowrap text-sm font-medium">
|
|
||||||
<button
|
|
||||||
onClick={() => handleViewDetails(log)}
|
|
||||||
className="text-[#d4af37] hover:text-[#c9a227] flex items-center space-x-1"
|
|
||||||
>
|
|
||||||
<Eye className="w-4 h-4" />
|
|
||||||
<span>View</span>
|
|
||||||
</button>
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
))}
|
|
||||||
</tbody>
|
|
||||||
</table>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
|
|
||||||
{}
|
|
||||||
{totalPages > 1 && (
|
|
||||||
<div className="mt-6">
|
|
||||||
<Pagination
|
|
||||||
currentPage={currentPage}
|
|
||||||
totalPages={totalPages}
|
|
||||||
onPageChange={setCurrentPage}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
|
|
||||||
{}
|
|
||||||
{showDetails && selectedLog && (
|
|
||||||
<div className="fixed inset-0 bg-black bg-opacity-50 z-50 overflow-y-auto p-4">
|
|
||||||
<div className="min-h-full flex items-start justify-center py-8">
|
|
||||||
<div className="bg-white rounded-lg max-w-3xl w-full my-8 max-h-[calc(100vh-4rem)] overflow-y-auto">
|
|
||||||
<div className="p-6">
|
|
||||||
<div className="flex justify-between items-center mb-4">
|
|
||||||
<h2 className="text-2xl font-bold text-gray-900">Audit Log Details</h2>
|
|
||||||
<button
|
|
||||||
onClick={() => setShowDetails(false)}
|
|
||||||
className="text-gray-400 hover:text-gray-600"
|
|
||||||
>
|
|
||||||
<XCircle className="w-6 h-6" />
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className="space-y-4">
|
|
||||||
<div className="grid grid-cols-2 gap-4">
|
|
||||||
<div>
|
|
||||||
<label className="block text-sm font-medium text-gray-700">ID</label>
|
|
||||||
<p className="mt-1 text-sm text-gray-900">#{selectedLog.id}</p>
|
|
||||||
</div>
|
|
||||||
<div>
|
|
||||||
<label className="block text-sm font-medium text-gray-700">Status</label>
|
|
||||||
<div className="mt-1 flex items-center space-x-2">
|
|
||||||
{getStatusIcon(selectedLog.status)}
|
|
||||||
<span className={getStatusBadge(selectedLog.status)}>
|
|
||||||
{selectedLog.status}
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div>
|
|
||||||
<label className="block text-sm font-medium text-gray-700">Action</label>
|
|
||||||
<p className="mt-1 text-sm text-gray-900">{selectedLog.action}</p>
|
|
||||||
</div>
|
|
||||||
<div>
|
|
||||||
<label className="block text-sm font-medium text-gray-700">Resource Type</label>
|
|
||||||
<p className="mt-1 text-sm text-gray-900">{selectedLog.resource_type}</p>
|
|
||||||
</div>
|
|
||||||
{selectedLog.resource_id && (
|
|
||||||
<div>
|
|
||||||
<label className="block text-sm font-medium text-gray-700">Resource ID</label>
|
|
||||||
<p className="mt-1 text-sm text-gray-900">{selectedLog.resource_id}</p>
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
<div>
|
|
||||||
<label className="block text-sm font-medium text-gray-700">Date</label>
|
|
||||||
<p className="mt-1 text-sm text-gray-900">{formatDate(selectedLog.created_at)}</p>
|
|
||||||
</div>
|
|
||||||
{selectedLog.user && (
|
|
||||||
<>
|
|
||||||
<div>
|
|
||||||
<label className="block text-sm font-medium text-gray-700">User</label>
|
|
||||||
<p className="mt-1 text-sm text-gray-900">{selectedLog.user.full_name}</p>
|
|
||||||
<p className="text-xs text-gray-500">{selectedLog.user.email}</p>
|
|
||||||
</div>
|
|
||||||
<div>
|
|
||||||
<label className="block text-sm font-medium text-gray-700">User ID</label>
|
|
||||||
<p className="mt-1 text-sm text-gray-900">{selectedLog.user_id}</p>
|
|
||||||
</div>
|
|
||||||
</>
|
|
||||||
)}
|
|
||||||
{selectedLog.ip_address && (
|
|
||||||
<div>
|
|
||||||
<label className="block text-sm font-medium text-gray-700">IP Address</label>
|
|
||||||
<p className="mt-1 text-sm text-gray-900">{selectedLog.ip_address}</p>
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
{selectedLog.request_id && (
|
|
||||||
<div>
|
|
||||||
<label className="block text-sm font-medium text-gray-700">Request ID</label>
|
|
||||||
<p className="mt-1 text-sm text-gray-900 font-mono text-xs">{selectedLog.request_id}</p>
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{selectedLog.user_agent && (
|
|
||||||
<div>
|
|
||||||
<label className="block text-sm font-medium text-gray-700">User Agent</label>
|
|
||||||
<p className="mt-1 text-sm text-gray-900 break-all">{selectedLog.user_agent}</p>
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
|
|
||||||
{selectedLog.error_message && (
|
|
||||||
<div>
|
|
||||||
<label className="block text-sm font-medium text-red-700">Error Message</label>
|
|
||||||
<p className="mt-1 text-sm text-red-900 bg-red-50 p-3 rounded">{selectedLog.error_message}</p>
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
|
|
||||||
{selectedLog.details && (
|
|
||||||
<div>
|
|
||||||
<label className="block text-sm font-medium text-gray-700">Details</label>
|
|
||||||
<pre className="mt-1 text-sm text-gray-900 bg-gray-50 p-3 rounded overflow-x-auto">
|
|
||||||
{JSON.stringify(selectedLog.details, null, 2)}
|
|
||||||
</pre>
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className="mt-6 flex justify-end">
|
|
||||||
<button
|
|
||||||
onClick={() => setShowDetails(false)}
|
|
||||||
className="px-4 py-2 bg-gray-200 text-gray-800 rounded-lg hover:bg-gray-300 transition-colors"
|
|
||||||
>
|
|
||||||
Close
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
export default AuditLogsPage;
|
|
||||||
|
|
||||||
@@ -255,7 +255,7 @@ const BannerManagementPage: React.FC = () => {
|
|||||||
resetForm();
|
resetForm();
|
||||||
setShowModal(true);
|
setShowModal(true);
|
||||||
}}
|
}}
|
||||||
className="flex items-center space-x-2 px-4 py-2 bg-[#d4af37] text-white rounded-lg hover:bg-[#c9a227] transition-colors"
|
className="flex items-center space-x-2 px-4 py-2 bg-[var(--luxury-gold)] text-white rounded-lg hover:bg-[var(--luxury-gold-dark)] transition-colors"
|
||||||
>
|
>
|
||||||
<Plus className="w-5 h-5" />
|
<Plus className="w-5 h-5" />
|
||||||
<span>Add Banner</span>
|
<span>Add Banner</span>
|
||||||
@@ -276,7 +276,7 @@ const BannerManagementPage: React.FC = () => {
|
|||||||
placeholder="Search by title..."
|
placeholder="Search by title..."
|
||||||
value={filters.search}
|
value={filters.search}
|
||||||
onChange={(e) => setFilters({ ...filters, search: e.target.value })}
|
onChange={(e) => setFilters({ ...filters, search: e.target.value })}
|
||||||
className="w-full pl-10 pr-4 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-[#d4af37] focus:border-[#d4af37]"
|
className="w-full pl-10 pr-4 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-[var(--luxury-gold)] focus:border-[var(--luxury-gold)]"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -287,7 +287,7 @@ const BannerManagementPage: React.FC = () => {
|
|||||||
<select
|
<select
|
||||||
value={filters.position}
|
value={filters.position}
|
||||||
onChange={(e) => setFilters({ ...filters, position: e.target.value })}
|
onChange={(e) => setFilters({ ...filters, position: e.target.value })}
|
||||||
className="w-full px-4 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-[#d4af37] focus:border-[#d4af37]"
|
className="w-full px-4 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-[var(--luxury-gold)] focus:border-[var(--luxury-gold)]"
|
||||||
>
|
>
|
||||||
<option value="">All Positions</option>
|
<option value="">All Positions</option>
|
||||||
<option value="home">Home</option>
|
<option value="home">Home</option>
|
||||||
@@ -384,7 +384,7 @@ const BannerManagementPage: React.FC = () => {
|
|||||||
<div className="flex space-x-2">
|
<div className="flex space-x-2">
|
||||||
<button
|
<button
|
||||||
onClick={() => handleEdit(banner)}
|
onClick={() => handleEdit(banner)}
|
||||||
className="text-[#d4af37] hover:text-[#c9a227]"
|
className="text-[var(--luxury-gold)] hover:text-[var(--luxury-gold-dark)]"
|
||||||
>
|
>
|
||||||
<Edit className="w-5 h-5" />
|
<Edit className="w-5 h-5" />
|
||||||
</button>
|
</button>
|
||||||
|
|||||||
@@ -426,7 +426,7 @@ const BlogManagementPage: React.FC = () => {
|
|||||||
type="button"
|
type="button"
|
||||||
onClick={() => fileInputRef.current?.click()}
|
onClick={() => fileInputRef.current?.click()}
|
||||||
disabled={isUploading}
|
disabled={isUploading}
|
||||||
className="w-full flex items-center justify-center gap-2 px-4 py-2 bg-gradient-to-r from-[#d4af37] to-[#c9a227] text-white rounded-lg hover:from-[#f5d76e] hover:to-[#d4af37] transition-all disabled:opacity-50 disabled:cursor-not-allowed"
|
className="w-full flex items-center justify-center gap-2 px-4 py-2 bg-gradient-to-r from-[var(--luxury-gold)] to-[var(--luxury-gold-dark)] text-white rounded-lg hover:from-[var(--luxury-gold-light)] hover:to-[var(--luxury-gold)] transition-all disabled:opacity-50 disabled:cursor-not-allowed"
|
||||||
>
|
>
|
||||||
{isUploading ? (
|
{isUploading ? (
|
||||||
<>
|
<>
|
||||||
@@ -468,7 +468,7 @@ const BlogManagementPage: React.FC = () => {
|
|||||||
</button>
|
</button>
|
||||||
<button
|
<button
|
||||||
onClick={() => handleOpenModal()}
|
onClick={() => handleOpenModal()}
|
||||||
className="flex items-center gap-2 px-4 py-2 bg-gradient-to-r from-[#d4af37] to-[#c9a227] text-white rounded-lg hover:from-[#f5d76e] hover:to-[#d4af37] transition-all shadow-lg"
|
className="flex items-center gap-2 px-4 py-2 bg-gradient-to-r from-[var(--luxury-gold)] to-[var(--luxury-gold-dark)] text-white rounded-lg hover:from-[var(--luxury-gold-light)] hover:to-[var(--luxury-gold)] transition-all shadow-lg"
|
||||||
>
|
>
|
||||||
<Plus className="w-5 h-5" />
|
<Plus className="w-5 h-5" />
|
||||||
<span>New Post</span>
|
<span>New Post</span>
|
||||||
@@ -488,7 +488,7 @@ const BlogManagementPage: React.FC = () => {
|
|||||||
placeholder="Search posts..."
|
placeholder="Search posts..."
|
||||||
value={filters.search}
|
value={filters.search}
|
||||||
onChange={(e) => setFilters({ ...filters, search: e.target.value })}
|
onChange={(e) => setFilters({ ...filters, search: e.target.value })}
|
||||||
className="w-full pl-10 pr-4 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-[#d4af37] focus:border-transparent"
|
className="w-full pl-10 pr-4 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-[var(--luxury-gold)] focus:border-transparent"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -501,7 +501,7 @@ const BlogManagementPage: React.FC = () => {
|
|||||||
published: value === 'all' ? undefined : value === 'published',
|
published: value === 'all' ? undefined : value === 'published',
|
||||||
});
|
});
|
||||||
}}
|
}}
|
||||||
className="px-4 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-[#d4af37] focus:border-transparent"
|
className="px-4 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-[var(--luxury-gold)] focus:border-transparent"
|
||||||
>
|
>
|
||||||
<option value="all">All Posts</option>
|
<option value="all">All Posts</option>
|
||||||
<option value="published">Published</option>
|
<option value="published">Published</option>
|
||||||
@@ -561,7 +561,7 @@ const BlogManagementPage: React.FC = () => {
|
|||||||
<div className="flex items-center gap-2">
|
<div className="flex items-center gap-2">
|
||||||
<button
|
<button
|
||||||
onClick={() => handleOpenModal(post)}
|
onClick={() => handleOpenModal(post)}
|
||||||
className="text-[#d4af37] hover:text-[#c9a227]"
|
className="text-[var(--luxury-gold)] hover:text-[var(--luxury-gold-dark)]"
|
||||||
>
|
>
|
||||||
<Edit className="w-4 h-4" />
|
<Edit className="w-4 h-4" />
|
||||||
</button>
|
</button>
|
||||||
@@ -646,7 +646,7 @@ const BlogManagementPage: React.FC = () => {
|
|||||||
value={formData.slug}
|
value={formData.slug}
|
||||||
onChange={(e) => setFormData({ ...formData, slug: e.target.value })}
|
onChange={(e) => setFormData({ ...formData, slug: e.target.value })}
|
||||||
placeholder="Auto-generated from title"
|
placeholder="Auto-generated from title"
|
||||||
className="w-full px-4 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-[#d4af37] focus:border-transparent bg-gray-50"
|
className="w-full px-4 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-[var(--luxury-gold)] focus:border-transparent bg-gray-50"
|
||||||
/>
|
/>
|
||||||
<p className="mt-1 text-xs text-gray-500">Leave empty to auto-generate, or customize the URL slug</p>
|
<p className="mt-1 text-xs text-gray-500">Leave empty to auto-generate, or customize the URL slug</p>
|
||||||
</div>
|
</div>
|
||||||
@@ -657,7 +657,7 @@ const BlogManagementPage: React.FC = () => {
|
|||||||
value={formData.excerpt}
|
value={formData.excerpt}
|
||||||
onChange={(e) => setFormData({ ...formData, excerpt: e.target.value })}
|
onChange={(e) => setFormData({ ...formData, excerpt: e.target.value })}
|
||||||
rows={3}
|
rows={3}
|
||||||
className="w-full px-4 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-[#d4af37] focus:border-transparent"
|
className="w-full px-4 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-[var(--luxury-gold)] focus:border-transparent"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@@ -668,7 +668,7 @@ const BlogManagementPage: React.FC = () => {
|
|||||||
value={formData.content}
|
value={formData.content}
|
||||||
onChange={(e) => setFormData({ ...formData, content: e.target.value })}
|
onChange={(e) => setFormData({ ...formData, content: e.target.value })}
|
||||||
rows={10}
|
rows={10}
|
||||||
className="w-full px-4 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-[#d4af37] focus:border-transparent"
|
className="w-full px-4 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-[var(--luxury-gold)] focus:border-transparent"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@@ -682,7 +682,7 @@ const BlogManagementPage: React.FC = () => {
|
|||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
onClick={() => setShowSectionBuilder(!showSectionBuilder)}
|
onClick={() => setShowSectionBuilder(!showSectionBuilder)}
|
||||||
className="flex items-center gap-2 px-4 py-2 bg-gradient-to-r from-[#d4af37] to-[#c9a227] text-white rounded-lg hover:from-[#f5d76e] hover:to-[#d4af37] transition-all shadow-lg"
|
className="flex items-center gap-2 px-4 py-2 bg-gradient-to-r from-[var(--luxury-gold)] to-[var(--luxury-gold-dark)] text-white rounded-lg hover:from-[var(--luxury-gold-light)] hover:to-[var(--luxury-gold)] transition-all shadow-lg"
|
||||||
>
|
>
|
||||||
<Sparkles className="w-4 h-4" />
|
<Sparkles className="w-4 h-4" />
|
||||||
<span>{showSectionBuilder ? 'Hide' : 'Show'} Section Builder</span>
|
<span>{showSectionBuilder ? 'Hide' : 'Show'} Section Builder</span>
|
||||||
@@ -707,9 +707,9 @@ const BlogManagementPage: React.FC = () => {
|
|||||||
key={type}
|
key={type}
|
||||||
type="button"
|
type="button"
|
||||||
onClick={() => addSection(type)}
|
onClick={() => addSection(type)}
|
||||||
className="flex flex-col items-center gap-2 p-4 bg-gradient-to-br from-gray-50 to-white border-2 border-gray-200 rounded-xl hover:border-[#d4af37] hover:shadow-lg transition-all group"
|
className="flex flex-col items-center gap-2 p-4 bg-gradient-to-br from-gray-50 to-white border-2 border-gray-200 rounded-xl hover:border-[var(--luxury-gold)] hover:shadow-lg transition-all group"
|
||||||
>
|
>
|
||||||
<Icon className="w-6 h-6 text-gray-600 group-hover:text-[#d4af37] transition-colors" />
|
<Icon className="w-6 h-6 text-gray-600 group-hover:text-[var(--luxury-gold)] transition-colors" />
|
||||||
<span className="text-sm font-medium text-gray-700">{label}</span>
|
<span className="text-sm font-medium text-gray-700">{label}</span>
|
||||||
</button>
|
</button>
|
||||||
))}
|
))}
|
||||||
@@ -726,7 +726,7 @@ const BlogManagementPage: React.FC = () => {
|
|||||||
<div className="flex items-center justify-between mb-4">
|
<div className="flex items-center justify-between mb-4">
|
||||||
<div className="flex items-center gap-3">
|
<div className="flex items-center gap-3">
|
||||||
<GripVertical className="w-5 h-5 text-gray-400" />
|
<GripVertical className="w-5 h-5 text-gray-400" />
|
||||||
<span className="px-3 py-1 bg-[#d4af37]/10 text-[#d4af37] rounded-full text-sm font-semibold uppercase">
|
<span className="px-3 py-1 bg-[var(--luxury-gold)]/10 text-[var(--luxury-gold)] rounded-full text-sm font-semibold uppercase">
|
||||||
{section.type}
|
{section.type}
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
@@ -735,7 +735,7 @@ const BlogManagementPage: React.FC = () => {
|
|||||||
type="button"
|
type="button"
|
||||||
onClick={() => moveSection(index, 'up')}
|
onClick={() => moveSection(index, 'up')}
|
||||||
disabled={index === 0}
|
disabled={index === 0}
|
||||||
className="p-2 text-gray-400 hover:text-[#d4af37] disabled:opacity-30 disabled:cursor-not-allowed"
|
className="p-2 text-gray-400 hover:text-[var(--luxury-gold)] disabled:opacity-30 disabled:cursor-not-allowed"
|
||||||
>
|
>
|
||||||
<MoveUp className="w-4 h-4" />
|
<MoveUp className="w-4 h-4" />
|
||||||
</button>
|
</button>
|
||||||
@@ -743,7 +743,7 @@ const BlogManagementPage: React.FC = () => {
|
|||||||
type="button"
|
type="button"
|
||||||
onClick={() => moveSection(index, 'down')}
|
onClick={() => moveSection(index, 'down')}
|
||||||
disabled={index === (formData.sections?.length || 0) - 1}
|
disabled={index === (formData.sections?.length || 0) - 1}
|
||||||
className="p-2 text-gray-400 hover:text-[#d4af37] disabled:opacity-30 disabled:cursor-not-allowed"
|
className="p-2 text-gray-400 hover:text-[var(--luxury-gold)] disabled:opacity-30 disabled:cursor-not-allowed"
|
||||||
>
|
>
|
||||||
<MoveDown className="w-4 h-4" />
|
<MoveDown className="w-4 h-4" />
|
||||||
</button>
|
</button>
|
||||||
@@ -767,7 +767,7 @@ const BlogManagementPage: React.FC = () => {
|
|||||||
type="text"
|
type="text"
|
||||||
value={section.title || ''}
|
value={section.title || ''}
|
||||||
onChange={(e) => updateSection(index, { title: e.target.value })}
|
onChange={(e) => updateSection(index, { title: e.target.value })}
|
||||||
className="w-full px-4 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-[#d4af37]"
|
className="w-full px-4 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-[var(--luxury-gold)]"
|
||||||
placeholder="Hero Title"
|
placeholder="Hero Title"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
@@ -777,7 +777,7 @@ const BlogManagementPage: React.FC = () => {
|
|||||||
value={section.content || ''}
|
value={section.content || ''}
|
||||||
onChange={(e) => updateSection(index, { content: e.target.value })}
|
onChange={(e) => updateSection(index, { content: e.target.value })}
|
||||||
rows={3}
|
rows={3}
|
||||||
className="w-full px-4 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-[#d4af37]"
|
className="w-full px-4 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-[var(--luxury-gold)]"
|
||||||
placeholder="Hero content"
|
placeholder="Hero content"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
@@ -795,7 +795,7 @@ const BlogManagementPage: React.FC = () => {
|
|||||||
type="url"
|
type="url"
|
||||||
value={section.image || ''}
|
value={section.image || ''}
|
||||||
onChange={(e) => updateSection(index, { image: e.target.value })}
|
onChange={(e) => updateSection(index, { image: e.target.value })}
|
||||||
className="w-full px-4 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-[#d4af37] text-sm"
|
className="w-full px-4 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-[var(--luxury-gold)] text-sm"
|
||||||
placeholder="https://..."
|
placeholder="https://..."
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
@@ -811,7 +811,7 @@ const BlogManagementPage: React.FC = () => {
|
|||||||
type="text"
|
type="text"
|
||||||
value={section.title || ''}
|
value={section.title || ''}
|
||||||
onChange={(e) => updateSection(index, { title: e.target.value })}
|
onChange={(e) => updateSection(index, { title: e.target.value })}
|
||||||
className="w-full px-4 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-[#d4af37]"
|
className="w-full px-4 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-[var(--luxury-gold)]"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
@@ -820,7 +820,7 @@ const BlogManagementPage: React.FC = () => {
|
|||||||
value={section.content || ''}
|
value={section.content || ''}
|
||||||
onChange={(e) => updateSection(index, { content: e.target.value })}
|
onChange={(e) => updateSection(index, { content: e.target.value })}
|
||||||
rows={6}
|
rows={6}
|
||||||
className="w-full px-4 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-[#d4af37]"
|
className="w-full px-4 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-[var(--luxury-gold)]"
|
||||||
placeholder="Enter your text content (HTML supported). You can use <img src='URL' /> to add images."
|
placeholder="Enter your text content (HTML supported). You can use <img src='URL' /> to add images."
|
||||||
/>
|
/>
|
||||||
<div className="mt-2">
|
<div className="mt-2">
|
||||||
@@ -847,7 +847,7 @@ const BlogManagementPage: React.FC = () => {
|
|||||||
<select
|
<select
|
||||||
value={section.alignment || 'left'}
|
value={section.alignment || 'left'}
|
||||||
onChange={(e) => updateSection(index, { alignment: e.target.value as 'left' | 'center' | 'right' })}
|
onChange={(e) => updateSection(index, { alignment: e.target.value as 'left' | 'center' | 'right' })}
|
||||||
className="w-full px-4 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-[#d4af37]"
|
className="w-full px-4 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-[var(--luxury-gold)]"
|
||||||
>
|
>
|
||||||
<option value="left">Left</option>
|
<option value="left">Left</option>
|
||||||
<option value="center">Center</option>
|
<option value="center">Center</option>
|
||||||
@@ -873,7 +873,7 @@ const BlogManagementPage: React.FC = () => {
|
|||||||
type="url"
|
type="url"
|
||||||
value={section.image || ''}
|
value={section.image || ''}
|
||||||
onChange={(e) => updateSection(index, { image: e.target.value })}
|
onChange={(e) => updateSection(index, { image: e.target.value })}
|
||||||
className="w-full px-4 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-[#d4af37] text-sm"
|
className="w-full px-4 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-[var(--luxury-gold)] text-sm"
|
||||||
placeholder="https://..."
|
placeholder="https://..."
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
@@ -884,7 +884,7 @@ const BlogManagementPage: React.FC = () => {
|
|||||||
type="text"
|
type="text"
|
||||||
value={section.title || ''}
|
value={section.title || ''}
|
||||||
onChange={(e) => updateSection(index, { title: e.target.value })}
|
onChange={(e) => updateSection(index, { title: e.target.value })}
|
||||||
className="w-full px-4 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-[#d4af37]"
|
className="w-full px-4 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-[var(--luxury-gold)]"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</>
|
</>
|
||||||
@@ -915,7 +915,7 @@ const BlogManagementPage: React.FC = () => {
|
|||||||
updatedImages[imgIndex] = e.target.value;
|
updatedImages[imgIndex] = e.target.value;
|
||||||
updateSection(index, { images: updatedImages });
|
updateSection(index, { images: updatedImages });
|
||||||
}}
|
}}
|
||||||
className="w-full px-3 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-[#d4af37] text-sm mb-2"
|
className="w-full px-3 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-[var(--luxury-gold)] text-sm mb-2"
|
||||||
placeholder="Image URL"
|
placeholder="Image URL"
|
||||||
/>
|
/>
|
||||||
<div className="flex gap-2">
|
<div className="flex gap-2">
|
||||||
@@ -950,7 +950,7 @@ const BlogManagementPage: React.FC = () => {
|
|||||||
value={(section.images || []).join('\n')}
|
value={(section.images || []).join('\n')}
|
||||||
onChange={(e) => updateSection(index, { images: e.target.value.split('\n').filter(url => url.trim()) })}
|
onChange={(e) => updateSection(index, { images: e.target.value.split('\n').filter(url => url.trim()) })}
|
||||||
rows={4}
|
rows={4}
|
||||||
className="w-full px-4 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-[#d4af37] text-sm"
|
className="w-full px-4 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-[var(--luxury-gold)] text-sm"
|
||||||
placeholder="https://image1.com/... https://image2.com/..."
|
placeholder="https://image1.com/... https://image2.com/..."
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
@@ -965,7 +965,7 @@ const BlogManagementPage: React.FC = () => {
|
|||||||
value={section.quote || ''}
|
value={section.quote || ''}
|
||||||
onChange={(e) => updateSection(index, { quote: e.target.value })}
|
onChange={(e) => updateSection(index, { quote: e.target.value })}
|
||||||
rows={4}
|
rows={4}
|
||||||
className="w-full px-4 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-[#d4af37]"
|
className="w-full px-4 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-[var(--luxury-gold)]"
|
||||||
placeholder="Enter the quote"
|
placeholder="Enter the quote"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
@@ -975,7 +975,7 @@ const BlogManagementPage: React.FC = () => {
|
|||||||
type="text"
|
type="text"
|
||||||
value={section.author || ''}
|
value={section.author || ''}
|
||||||
onChange={(e) => updateSection(index, { author: e.target.value })}
|
onChange={(e) => updateSection(index, { author: e.target.value })}
|
||||||
className="w-full px-4 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-[#d4af37]"
|
className="w-full px-4 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-[var(--luxury-gold)]"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</>
|
</>
|
||||||
@@ -1012,7 +1012,7 @@ const BlogManagementPage: React.FC = () => {
|
|||||||
updateSection(index, { features: updatedFeatures });
|
updateSection(index, { features: updatedFeatures });
|
||||||
}}
|
}}
|
||||||
placeholder="Feature title"
|
placeholder="Feature title"
|
||||||
className="w-full px-3 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-[#d4af37] text-sm"
|
className="w-full px-3 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-[var(--luxury-gold)] text-sm"
|
||||||
/>
|
/>
|
||||||
<textarea
|
<textarea
|
||||||
value={feature.description || ''}
|
value={feature.description || ''}
|
||||||
@@ -1023,7 +1023,7 @@ const BlogManagementPage: React.FC = () => {
|
|||||||
}}
|
}}
|
||||||
placeholder="Feature description"
|
placeholder="Feature description"
|
||||||
rows={2}
|
rows={2}
|
||||||
className="w-full px-3 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-[#d4af37] text-sm"
|
className="w-full px-3 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-[var(--luxury-gold)] text-sm"
|
||||||
/>
|
/>
|
||||||
<input
|
<input
|
||||||
type="text"
|
type="text"
|
||||||
@@ -1034,7 +1034,7 @@ const BlogManagementPage: React.FC = () => {
|
|||||||
updateSection(index, { features: updatedFeatures });
|
updateSection(index, { features: updatedFeatures });
|
||||||
}}
|
}}
|
||||||
placeholder="Icon URL (optional)"
|
placeholder="Icon URL (optional)"
|
||||||
className="w-full px-3 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-[#d4af37] text-sm"
|
className="w-full px-3 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-[var(--luxury-gold)] text-sm"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -1062,7 +1062,7 @@ const BlogManagementPage: React.FC = () => {
|
|||||||
type="text"
|
type="text"
|
||||||
value={section.title || ''}
|
value={section.title || ''}
|
||||||
onChange={(e) => updateSection(index, { title: e.target.value })}
|
onChange={(e) => updateSection(index, { title: e.target.value })}
|
||||||
className="w-full px-4 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-[#d4af37]"
|
className="w-full px-4 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-[var(--luxury-gold)]"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
@@ -1071,7 +1071,7 @@ const BlogManagementPage: React.FC = () => {
|
|||||||
value={section.content || ''}
|
value={section.content || ''}
|
||||||
onChange={(e) => updateSection(index, { content: e.target.value })}
|
onChange={(e) => updateSection(index, { content: e.target.value })}
|
||||||
rows={3}
|
rows={3}
|
||||||
className="w-full px-4 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-[#d4af37]"
|
className="w-full px-4 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-[var(--luxury-gold)]"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div className="grid grid-cols-2 gap-4">
|
<div className="grid grid-cols-2 gap-4">
|
||||||
@@ -1081,7 +1081,7 @@ const BlogManagementPage: React.FC = () => {
|
|||||||
type="text"
|
type="text"
|
||||||
value={section.cta_text || ''}
|
value={section.cta_text || ''}
|
||||||
onChange={(e) => updateSection(index, { cta_text: e.target.value })}
|
onChange={(e) => updateSection(index, { cta_text: e.target.value })}
|
||||||
className="w-full px-4 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-[#d4af37]"
|
className="w-full px-4 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-[var(--luxury-gold)]"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
@@ -1090,7 +1090,7 @@ const BlogManagementPage: React.FC = () => {
|
|||||||
type="url"
|
type="url"
|
||||||
value={section.cta_link || ''}
|
value={section.cta_link || ''}
|
||||||
onChange={(e) => updateSection(index, { cta_link: e.target.value })}
|
onChange={(e) => updateSection(index, { cta_link: e.target.value })}
|
||||||
className="w-full px-4 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-[#d4af37]"
|
className="w-full px-4 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-[var(--luxury-gold)]"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -1104,7 +1104,7 @@ const BlogManagementPage: React.FC = () => {
|
|||||||
type="url"
|
type="url"
|
||||||
value={section.video_url || ''}
|
value={section.video_url || ''}
|
||||||
onChange={(e) => updateSection(index, { video_url: e.target.value })}
|
onChange={(e) => updateSection(index, { video_url: e.target.value })}
|
||||||
className="w-full px-4 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-[#d4af37]"
|
className="w-full px-4 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-[var(--luxury-gold)]"
|
||||||
placeholder="https://youtube.com/watch?v=..."
|
placeholder="https://youtube.com/watch?v=..."
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
@@ -1125,7 +1125,7 @@ const BlogManagementPage: React.FC = () => {
|
|||||||
value={formData.featured_image}
|
value={formData.featured_image}
|
||||||
onChange={(e) => setFormData({ ...formData, featured_image: e.target.value })}
|
onChange={(e) => setFormData({ ...formData, featured_image: e.target.value })}
|
||||||
placeholder="https://images.unsplash.com/..."
|
placeholder="https://images.unsplash.com/..."
|
||||||
className="w-full px-4 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-[#d4af37] focus:border-transparent"
|
className="w-full px-4 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-[var(--luxury-gold)] focus:border-transparent"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@@ -1143,7 +1143,7 @@ const BlogManagementPage: React.FC = () => {
|
|||||||
}
|
}
|
||||||
}}
|
}}
|
||||||
placeholder="Add a tag and press Enter"
|
placeholder="Add a tag and press Enter"
|
||||||
className="flex-1 px-4 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-[#d4af37] focus:border-transparent"
|
className="flex-1 px-4 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-[var(--luxury-gold)] focus:border-transparent"
|
||||||
/>
|
/>
|
||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
@@ -1157,13 +1157,13 @@ const BlogManagementPage: React.FC = () => {
|
|||||||
{formData.tags?.map((tag) => (
|
{formData.tags?.map((tag) => (
|
||||||
<span
|
<span
|
||||||
key={tag}
|
key={tag}
|
||||||
className="inline-flex items-center gap-1 px-3 py-1 bg-[#d4af37]/10 text-[#d4af37] rounded-full text-sm"
|
className="inline-flex items-center gap-1 px-3 py-1 bg-[var(--luxury-gold)]/10 text-[var(--luxury-gold)] rounded-full text-sm"
|
||||||
>
|
>
|
||||||
{tag}
|
{tag}
|
||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
onClick={() => handleRemoveTag(tag)}
|
onClick={() => handleRemoveTag(tag)}
|
||||||
className="hover:text-[#c9a227]"
|
className="hover:text-[var(--luxury-gold-dark)]"
|
||||||
>
|
>
|
||||||
<X className="w-3 h-3" />
|
<X className="w-3 h-3" />
|
||||||
</button>
|
</button>
|
||||||
@@ -1179,7 +1179,7 @@ const BlogManagementPage: React.FC = () => {
|
|||||||
type="text"
|
type="text"
|
||||||
value={formData.meta_title}
|
value={formData.meta_title}
|
||||||
onChange={(e) => setFormData({ ...formData, meta_title: e.target.value })}
|
onChange={(e) => setFormData({ ...formData, meta_title: e.target.value })}
|
||||||
className="w-full px-4 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-[#d4af37] focus:border-transparent"
|
className="w-full px-4 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-[var(--luxury-gold)] focus:border-transparent"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
@@ -1188,7 +1188,7 @@ const BlogManagementPage: React.FC = () => {
|
|||||||
type="text"
|
type="text"
|
||||||
value={formData.meta_keywords}
|
value={formData.meta_keywords}
|
||||||
onChange={(e) => setFormData({ ...formData, meta_keywords: e.target.value })}
|
onChange={(e) => setFormData({ ...formData, meta_keywords: e.target.value })}
|
||||||
className="w-full px-4 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-[#d4af37] focus:border-transparent"
|
className="w-full px-4 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-[var(--luxury-gold)] focus:border-transparent"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -1199,7 +1199,7 @@ const BlogManagementPage: React.FC = () => {
|
|||||||
value={formData.meta_description}
|
value={formData.meta_description}
|
||||||
onChange={(e) => setFormData({ ...formData, meta_description: e.target.value })}
|
onChange={(e) => setFormData({ ...formData, meta_description: e.target.value })}
|
||||||
rows={3}
|
rows={3}
|
||||||
className="w-full px-4 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-[#d4af37] focus:border-transparent"
|
className="w-full px-4 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-[var(--luxury-gold)] focus:border-transparent"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@@ -1209,7 +1209,7 @@ const BlogManagementPage: React.FC = () => {
|
|||||||
type="checkbox"
|
type="checkbox"
|
||||||
checked={formData.is_published}
|
checked={formData.is_published}
|
||||||
onChange={(e) => setFormData({ ...formData, is_published: e.target.checked })}
|
onChange={(e) => setFormData({ ...formData, is_published: e.target.checked })}
|
||||||
className="w-4 h-4 text-[#d4af37] border-gray-300 rounded focus:ring-[#d4af37]"
|
className="w-4 h-4 text-[var(--luxury-gold)] border-gray-300 rounded focus:ring-[var(--luxury-gold)]"
|
||||||
/>
|
/>
|
||||||
<span className="text-sm font-medium text-gray-700">Publish</span>
|
<span className="text-sm font-medium text-gray-700">Publish</span>
|
||||||
</label>
|
</label>
|
||||||
@@ -1220,7 +1220,7 @@ const BlogManagementPage: React.FC = () => {
|
|||||||
type="datetime-local"
|
type="datetime-local"
|
||||||
value={formData.published_at ? new Date(formData.published_at).toISOString().slice(0, 16) : ''}
|
value={formData.published_at ? new Date(formData.published_at).toISOString().slice(0, 16) : ''}
|
||||||
onChange={(e) => setFormData({ ...formData, published_at: e.target.value ? new Date(e.target.value).toISOString() : undefined })}
|
onChange={(e) => setFormData({ ...formData, published_at: e.target.value ? new Date(e.target.value).toISOString() : undefined })}
|
||||||
className="px-4 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-[#d4af37] focus:border-transparent"
|
className="px-4 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-[var(--luxury-gold)] focus:border-transparent"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@@ -162,7 +162,7 @@ const ComplaintManagementPage: React.FC = () => {
|
|||||||
<select
|
<select
|
||||||
value={filters.status || ''}
|
value={filters.status || ''}
|
||||||
onChange={(e) => setFilters({ ...filters, status: e.target.value || undefined, page: 1 })}
|
onChange={(e) => setFilters({ ...filters, status: e.target.value || undefined, page: 1 })}
|
||||||
className="w-full px-3 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-[#d4af37] focus:border-transparent"
|
className="w-full px-3 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-[var(--luxury-gold)] focus:border-transparent"
|
||||||
>
|
>
|
||||||
<option value="">All Statuses</option>
|
<option value="">All Statuses</option>
|
||||||
<option value="open">Open</option>
|
<option value="open">Open</option>
|
||||||
@@ -177,7 +177,7 @@ const ComplaintManagementPage: React.FC = () => {
|
|||||||
<select
|
<select
|
||||||
value={filters.priority || ''}
|
value={filters.priority || ''}
|
||||||
onChange={(e) => setFilters({ ...filters, priority: e.target.value || undefined, page: 1 })}
|
onChange={(e) => setFilters({ ...filters, priority: e.target.value || undefined, page: 1 })}
|
||||||
className="w-full px-3 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-[#d4af37] focus:border-transparent"
|
className="w-full px-3 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-[var(--luxury-gold)] focus:border-transparent"
|
||||||
>
|
>
|
||||||
<option value="">All Priorities</option>
|
<option value="">All Priorities</option>
|
||||||
<option value="low">Low</option>
|
<option value="low">Low</option>
|
||||||
@@ -191,7 +191,7 @@ const ComplaintManagementPage: React.FC = () => {
|
|||||||
<select
|
<select
|
||||||
value={filters.category || ''}
|
value={filters.category || ''}
|
||||||
onChange={(e) => setFilters({ ...filters, category: e.target.value || undefined, page: 1 })}
|
onChange={(e) => setFilters({ ...filters, category: e.target.value || undefined, page: 1 })}
|
||||||
className="w-full px-3 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-[#d4af37] focus:border-transparent"
|
className="w-full px-3 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-[var(--luxury-gold)] focus:border-transparent"
|
||||||
>
|
>
|
||||||
<option value="">All Categories</option>
|
<option value="">All Categories</option>
|
||||||
<option value="room_quality">Room Quality</option>
|
<option value="room_quality">Room Quality</option>
|
||||||
@@ -261,7 +261,7 @@ const ComplaintManagementPage: React.FC = () => {
|
|||||||
<td className="px-6 py-4 whitespace-nowrap text-sm font-medium">
|
<td className="px-6 py-4 whitespace-nowrap text-sm font-medium">
|
||||||
<button
|
<button
|
||||||
onClick={() => handleViewDetails(complaint.id)}
|
onClick={() => handleViewDetails(complaint.id)}
|
||||||
className="text-[#d4af37] hover:text-[#c9a227] mr-3"
|
className="text-[var(--luxury-gold)] hover:text-[var(--luxury-gold-dark)] mr-3"
|
||||||
>
|
>
|
||||||
<Eye className="w-5 h-5" />
|
<Eye className="w-5 h-5" />
|
||||||
</button>
|
</button>
|
||||||
@@ -415,7 +415,7 @@ const ResolveComplaintModal: React.FC<{
|
|||||||
value={resolution}
|
value={resolution}
|
||||||
onChange={(e) => setResolution(e.target.value)}
|
onChange={(e) => setResolution(e.target.value)}
|
||||||
rows={4}
|
rows={4}
|
||||||
className="w-full px-3 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-[#d4af37] focus:border-transparent"
|
className="w-full px-3 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-[var(--luxury-gold)] focus:border-transparent"
|
||||||
placeholder="Describe how the complaint was resolved..."
|
placeholder="Describe how the complaint was resolved..."
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
@@ -426,7 +426,7 @@ const ResolveComplaintModal: React.FC<{
|
|||||||
<select
|
<select
|
||||||
value={rating || ''}
|
value={rating || ''}
|
||||||
onChange={(e) => setRating(e.target.value ? parseInt(e.target.value) : undefined)}
|
onChange={(e) => setRating(e.target.value ? parseInt(e.target.value) : undefined)}
|
||||||
className="w-full px-3 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-[#d4af37] focus:border-transparent"
|
className="w-full px-3 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-[var(--luxury-gold)] focus:border-transparent"
|
||||||
>
|
>
|
||||||
<option value="">Not rated</option>
|
<option value="">Not rated</option>
|
||||||
<option value="1">1 - Very Dissatisfied</option>
|
<option value="1">1 - Very Dissatisfied</option>
|
||||||
@@ -444,7 +444,7 @@ const ResolveComplaintModal: React.FC<{
|
|||||||
value={feedback}
|
value={feedback}
|
||||||
onChange={(e) => setFeedback(e.target.value)}
|
onChange={(e) => setFeedback(e.target.value)}
|
||||||
rows={3}
|
rows={3}
|
||||||
className="w-full px-3 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-[#d4af37] focus:border-transparent"
|
className="w-full px-3 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-[var(--luxury-gold)] focus:border-transparent"
|
||||||
placeholder="Guest feedback on resolution..."
|
placeholder="Guest feedback on resolution..."
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -98,7 +98,7 @@ const ComplianceReportingPage: React.FC = () => {
|
|||||||
<button
|
<button
|
||||||
onClick={handleExport}
|
onClick={handleExport}
|
||||||
disabled={generating}
|
disabled={generating}
|
||||||
className="flex items-center space-x-2 px-4 py-2 bg-[#d4af37] text-white rounded-lg hover:bg-[#c9a227] transition-colors disabled:opacity-50 disabled:cursor-not-allowed"
|
className="flex items-center space-x-2 px-4 py-2 bg-[var(--luxury-gold)] text-white rounded-lg hover:bg-[var(--luxury-gold-dark)] transition-colors disabled:opacity-50 disabled:cursor-not-allowed"
|
||||||
>
|
>
|
||||||
<Download className="w-5 h-5" />
|
<Download className="w-5 h-5" />
|
||||||
<span>{generating ? 'Generating...' : 'Export CSV'}</span>
|
<span>{generating ? 'Generating...' : 'Export CSV'}</span>
|
||||||
@@ -114,7 +114,7 @@ const ComplianceReportingPage: React.FC = () => {
|
|||||||
type="date"
|
type="date"
|
||||||
value={dateRange.start_date}
|
value={dateRange.start_date}
|
||||||
onChange={(e) => setDateRange({ ...dateRange, start_date: e.target.value })}
|
onChange={(e) => setDateRange({ ...dateRange, start_date: e.target.value })}
|
||||||
className="w-full px-3 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-[#d4af37] focus:border-transparent"
|
className="w-full px-3 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-[var(--luxury-gold)] focus:border-transparent"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
@@ -123,13 +123,13 @@ const ComplianceReportingPage: React.FC = () => {
|
|||||||
type="date"
|
type="date"
|
||||||
value={dateRange.end_date}
|
value={dateRange.end_date}
|
||||||
onChange={(e) => setDateRange({ ...dateRange, end_date: e.target.value })}
|
onChange={(e) => setDateRange({ ...dateRange, end_date: e.target.value })}
|
||||||
className="w-full px-3 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-[#d4af37] focus:border-transparent"
|
className="w-full px-3 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-[var(--luxury-gold)] focus:border-transparent"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div className="flex items-end">
|
<div className="flex items-end">
|
||||||
<button
|
<button
|
||||||
onClick={fetchComplianceData}
|
onClick={fetchComplianceData}
|
||||||
className="w-full px-4 py-2 bg-[#d4af37] text-white rounded-lg hover:bg-[#c9a227] transition-colors"
|
className="w-full px-4 py-2 bg-[var(--luxury-gold)] text-white rounded-lg hover:bg-[var(--luxury-gold-dark)] transition-colors"
|
||||||
>
|
>
|
||||||
Refresh Report
|
Refresh Report
|
||||||
</button>
|
</button>
|
||||||
|
|||||||
@@ -117,8 +117,8 @@ const CookieSettingsPage: React.FC = () => {
|
|||||||
<div className="flex flex-col sm:flex-row items-start sm:items-center justify-between gap-6 pb-6 border-b border-gray-200/60">
|
<div className="flex flex-col sm:flex-row items-start sm:items-center justify-between gap-6 pb-6 border-b border-gray-200/60">
|
||||||
<div className="space-y-3">
|
<div className="space-y-3">
|
||||||
<div className="flex items-center gap-3">
|
<div className="flex items-center gap-3">
|
||||||
<div className="p-2.5 rounded-lg bg-gradient-to-br from-[#d4af37]/10 to-[#d4af37]/5 border border-[#d4af37]/20 shadow-sm">
|
<div className="p-2.5 rounded-lg bg-gradient-to-br from-[var(--luxury-gold)]/10 to-[var(--luxury-gold)]/5 border border-[var(--luxury-gold)]/20 shadow-sm">
|
||||||
<Shield className="w-6 h-6 text-[#d4af37]" />
|
<Shield className="w-6 h-6 text-[var(--luxury-gold)]" />
|
||||||
</div>
|
</div>
|
||||||
<h1 className="enterprise-section-title">Cookie & Privacy Controls</h1>
|
<h1 className="enterprise-section-title">Cookie & Privacy Controls</h1>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -78,8 +78,8 @@ const CurrencySettingsPage: React.FC = () => {
|
|||||||
<div className="flex flex-col sm:flex-row items-start sm:items-center justify-between gap-6 pb-6 border-b border-gray-200/60">
|
<div className="flex flex-col sm:flex-row items-start sm:items-center justify-between gap-6 pb-6 border-b border-gray-200/60">
|
||||||
<div className="space-y-3">
|
<div className="space-y-3">
|
||||||
<div className="flex items-center gap-3">
|
<div className="flex items-center gap-3">
|
||||||
<div className="p-2.5 rounded-lg bg-gradient-to-br from-[#d4af37]/10 to-[#d4af37]/5 border border-[#d4af37]/20 shadow-sm">
|
<div className="p-2.5 rounded-lg bg-gradient-to-br from-[var(--luxury-gold)]/10 to-[var(--luxury-gold)]/5 border border-[var(--luxury-gold)]/20 shadow-sm">
|
||||||
<DollarSign className="w-6 h-6 text-[#d4af37]" />
|
<DollarSign className="w-6 h-6 text-[var(--luxury-gold)]" />
|
||||||
</div>
|
</div>
|
||||||
<h1 className="enterprise-section-title">Platform Currency Settings</h1>
|
<h1 className="enterprise-section-title">Platform Currency Settings</h1>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -124,7 +124,7 @@ const FinancialAuditTrailPage: React.FC = () => {
|
|||||||
</div>
|
</div>
|
||||||
<button
|
<button
|
||||||
onClick={handleExport}
|
onClick={handleExport}
|
||||||
className="flex items-center space-x-2 px-4 py-2 bg-[#d4af37] text-white rounded-lg hover:bg-[#c9a227] transition-colors"
|
className="flex items-center space-x-2 px-4 py-2 bg-[var(--luxury-gold)] text-white rounded-lg hover:bg-[var(--luxury-gold-dark)] transition-colors"
|
||||||
>
|
>
|
||||||
<Download className="w-5 h-5" />
|
<Download className="w-5 h-5" />
|
||||||
<span>Export CSV</span>
|
<span>Export CSV</span>
|
||||||
@@ -139,7 +139,7 @@ const FinancialAuditTrailPage: React.FC = () => {
|
|||||||
<select
|
<select
|
||||||
value={filters.action_type || ''}
|
value={filters.action_type || ''}
|
||||||
onChange={(e) => setFilters({ ...filters, action_type: e.target.value || undefined, page: 1 })}
|
onChange={(e) => setFilters({ ...filters, action_type: e.target.value || undefined, page: 1 })}
|
||||||
className="w-full px-3 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-[#d4af37] focus:border-transparent"
|
className="w-full px-3 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-[var(--luxury-gold)] focus:border-transparent"
|
||||||
>
|
>
|
||||||
<option value="">All Actions</option>
|
<option value="">All Actions</option>
|
||||||
<option value="payment_created">Payment Created</option>
|
<option value="payment_created">Payment Created</option>
|
||||||
@@ -159,7 +159,7 @@ const FinancialAuditTrailPage: React.FC = () => {
|
|||||||
type="number"
|
type="number"
|
||||||
value={filters.payment_id || ''}
|
value={filters.payment_id || ''}
|
||||||
onChange={(e) => setFilters({ ...filters, payment_id: e.target.value ? parseInt(e.target.value) : undefined, page: 1 })}
|
onChange={(e) => setFilters({ ...filters, payment_id: e.target.value ? parseInt(e.target.value) : undefined, page: 1 })}
|
||||||
className="w-full px-3 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-[#d4af37] focus:border-transparent"
|
className="w-full px-3 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-[var(--luxury-gold)] focus:border-transparent"
|
||||||
placeholder="Filter by payment ID"
|
placeholder="Filter by payment ID"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
@@ -169,7 +169,7 @@ const FinancialAuditTrailPage: React.FC = () => {
|
|||||||
type="number"
|
type="number"
|
||||||
value={filters.booking_id || ''}
|
value={filters.booking_id || ''}
|
||||||
onChange={(e) => setFilters({ ...filters, booking_id: e.target.value ? parseInt(e.target.value) : undefined, page: 1 })}
|
onChange={(e) => setFilters({ ...filters, booking_id: e.target.value ? parseInt(e.target.value) : undefined, page: 1 })}
|
||||||
className="w-full px-3 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-[#d4af37] focus:border-transparent"
|
className="w-full px-3 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-[var(--luxury-gold)] focus:border-transparent"
|
||||||
placeholder="Filter by booking ID"
|
placeholder="Filter by booking ID"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
@@ -230,7 +230,7 @@ const FinancialAuditTrailPage: React.FC = () => {
|
|||||||
<td className="px-6 py-4 whitespace-nowrap text-sm font-medium">
|
<td className="px-6 py-4 whitespace-nowrap text-sm font-medium">
|
||||||
<button
|
<button
|
||||||
onClick={() => handleViewDetails(record.id)}
|
onClick={() => handleViewDetails(record.id)}
|
||||||
className="text-[#d4af37] hover:text-[#c9a227]"
|
className="text-[var(--luxury-gold)] hover:text-[var(--luxury-gold-dark)]"
|
||||||
>
|
>
|
||||||
<Eye className="w-5 h-5" />
|
<Eye className="w-5 h-5" />
|
||||||
</button>
|
</button>
|
||||||
|
|||||||
@@ -359,4 +359,3 @@ const InvoiceManagementPage: React.FC = () => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
export default InvoiceManagementPage;
|
export default InvoiceManagementPage;
|
||||||
|
|
||||||
|
|||||||
@@ -417,7 +417,7 @@ const ProfilePage: React.FC = () => {
|
|||||||
{}
|
{}
|
||||||
<div className="mb-6 sm:mb-8 animate-fade-in">
|
<div className="mb-6 sm:mb-8 animate-fade-in">
|
||||||
<div className="flex items-center gap-2 sm:gap-3 mb-2 sm:mb-3">
|
<div className="flex items-center gap-2 sm:gap-3 mb-2 sm:mb-3">
|
||||||
<div className="h-1 w-12 sm:w-16 md:w-20 bg-gradient-to-r from-[#d4af37] via-amber-400 to-[#d4af37] rounded-full"></div>
|
<div className="h-1 w-12 sm:w-16 md:w-20 bg-gradient-to-r from-[var(--luxury-gold)] via-amber-400 to-[var(--luxury-gold)] rounded-full"></div>
|
||||||
<h1 className="text-2xl sm:text-3xl lg:text-4xl font-serif font-bold bg-gradient-to-r from-gray-900 via-amber-900/90 to-gray-900 bg-clip-text text-transparent tracking-tight">
|
<h1 className="text-2xl sm:text-3xl lg:text-4xl font-serif font-bold bg-gradient-to-r from-gray-900 via-amber-900/90 to-gray-900 bg-clip-text text-transparent tracking-tight">
|
||||||
Admin Profile & Settings
|
Admin Profile & Settings
|
||||||
</h1>
|
</h1>
|
||||||
@@ -428,61 +428,61 @@ const ProfilePage: React.FC = () => {
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
{}
|
{}
|
||||||
<div className="mb-4 sm:mb-6 border-b border-[#d4af37]/20 overflow-x-auto bg-white/50 backdrop-blur-sm rounded-t-lg sm:rounded-t-xl px-4 sm:px-6">
|
<div className="mb-4 sm:mb-6 border-b border-[var(--luxury-gold)]/20 overflow-x-auto bg-white/50 backdrop-blur-sm rounded-t-lg sm:rounded-t-xl px-4 sm:px-6">
|
||||||
<div className="flex space-x-4 sm:space-x-8 min-w-max">
|
<div className="flex space-x-4 sm:space-x-8 min-w-max">
|
||||||
<button
|
<button
|
||||||
onClick={() => setActiveTab('profile')}
|
onClick={() => setActiveTab('profile')}
|
||||||
className={`py-3 sm:py-4 px-2 sm:px-1 border-b-2 font-semibold text-xs sm:text-sm transition-all duration-300 whitespace-nowrap ${
|
className={`py-3 sm:py-4 px-2 sm:px-1 border-b-2 font-semibold text-xs sm:text-sm transition-all duration-300 whitespace-nowrap ${
|
||||||
activeTab === 'profile'
|
activeTab === 'profile'
|
||||||
? 'border-[#d4af37] text-[#d4af37] shadow-sm'
|
? 'border-[var(--luxury-gold)] text-[var(--luxury-gold)] shadow-sm'
|
||||||
: 'border-transparent text-gray-500 hover:text-[#d4af37] hover:border-[#d4af37]/30'
|
: 'border-transparent text-gray-500 hover:text-[var(--luxury-gold)] hover:border-[var(--luxury-gold)]/30'
|
||||||
}`}
|
}`}
|
||||||
>
|
>
|
||||||
<User className={`w-4 h-4 sm:w-5 sm:h-5 inline mr-2 ${activeTab === 'profile' ? 'text-[#d4af37]' : ''}`} />
|
<User className={`w-4 h-4 sm:w-5 sm:h-5 inline mr-2 ${activeTab === 'profile' ? 'text-[var(--luxury-gold)]' : ''}`} />
|
||||||
Profile Information
|
Profile Information
|
||||||
</button>
|
</button>
|
||||||
<button
|
<button
|
||||||
onClick={() => setActiveTab('password')}
|
onClick={() => setActiveTab('password')}
|
||||||
className={`py-3 sm:py-4 px-2 sm:px-1 border-b-2 font-semibold text-xs sm:text-sm transition-all duration-300 whitespace-nowrap ${
|
className={`py-3 sm:py-4 px-2 sm:px-1 border-b-2 font-semibold text-xs sm:text-sm transition-all duration-300 whitespace-nowrap ${
|
||||||
activeTab === 'password'
|
activeTab === 'password'
|
||||||
? 'border-[#d4af37] text-[#d4af37] shadow-sm'
|
? 'border-[var(--luxury-gold)] text-[var(--luxury-gold)] shadow-sm'
|
||||||
: 'border-transparent text-gray-500 hover:text-[#d4af37] hover:border-[#d4af37]/30'
|
: 'border-transparent text-gray-500 hover:text-[var(--luxury-gold)] hover:border-[var(--luxury-gold)]/30'
|
||||||
}`}
|
}`}
|
||||||
>
|
>
|
||||||
<KeyRound className={`w-4 h-4 sm:w-5 sm:h-5 inline mr-2 ${activeTab === 'password' ? 'text-[#d4af37]' : ''}`} />
|
<KeyRound className={`w-4 h-4 sm:w-5 sm:h-5 inline mr-2 ${activeTab === 'password' ? 'text-[var(--luxury-gold)]' : ''}`} />
|
||||||
Change Password
|
Change Password
|
||||||
</button>
|
</button>
|
||||||
<button
|
<button
|
||||||
onClick={() => setActiveTab('mfa')}
|
onClick={() => setActiveTab('mfa')}
|
||||||
className={`py-3 sm:py-4 px-2 sm:px-1 border-b-2 font-semibold text-xs sm:text-sm transition-all duration-300 whitespace-nowrap ${
|
className={`py-3 sm:py-4 px-2 sm:px-1 border-b-2 font-semibold text-xs sm:text-sm transition-all duration-300 whitespace-nowrap ${
|
||||||
activeTab === 'mfa'
|
activeTab === 'mfa'
|
||||||
? 'border-[#d4af37] text-[#d4af37] shadow-sm'
|
? 'border-[var(--luxury-gold)] text-[var(--luxury-gold)] shadow-sm'
|
||||||
: 'border-transparent text-gray-500 hover:text-[#d4af37] hover:border-[#d4af37]/30'
|
: 'border-transparent text-gray-500 hover:text-[var(--luxury-gold)] hover:border-[var(--luxury-gold)]/30'
|
||||||
}`}
|
}`}
|
||||||
>
|
>
|
||||||
<Shield className={`w-4 h-4 sm:w-5 sm:h-5 inline mr-2 ${activeTab === 'mfa' ? 'text-[#d4af37]' : ''}`} />
|
<Shield className={`w-4 h-4 sm:w-5 sm:h-5 inline mr-2 ${activeTab === 'mfa' ? 'text-[var(--luxury-gold)]' : ''}`} />
|
||||||
Two-Factor Authentication
|
Two-Factor Authentication
|
||||||
</button>
|
</button>
|
||||||
<button
|
<button
|
||||||
onClick={() => setActiveTab('sessions')}
|
onClick={() => setActiveTab('sessions')}
|
||||||
className={`py-3 sm:py-4 px-2 sm:px-1 border-b-2 font-semibold text-xs sm:text-sm transition-all duration-300 whitespace-nowrap ${
|
className={`py-3 sm:py-4 px-2 sm:px-1 border-b-2 font-semibold text-xs sm:text-sm transition-all duration-300 whitespace-nowrap ${
|
||||||
activeTab === 'sessions'
|
activeTab === 'sessions'
|
||||||
? 'border-[#d4af37] text-[#d4af37] shadow-sm'
|
? 'border-[var(--luxury-gold)] text-[var(--luxury-gold)] shadow-sm'
|
||||||
: 'border-transparent text-gray-500 hover:text-[#d4af37] hover:border-[#d4af37]/30'
|
: 'border-transparent text-gray-500 hover:text-[var(--luxury-gold)] hover:border-[var(--luxury-gold)]/30'
|
||||||
}`}
|
}`}
|
||||||
>
|
>
|
||||||
<Monitor className={`w-4 h-4 sm:w-5 sm:h-5 inline mr-2 ${activeTab === 'sessions' ? 'text-[#d4af37]' : ''}`} />
|
<Monitor className={`w-4 h-4 sm:w-5 sm:h-5 inline mr-2 ${activeTab === 'sessions' ? 'text-[var(--luxury-gold)]' : ''}`} />
|
||||||
Active Sessions
|
Active Sessions
|
||||||
</button>
|
</button>
|
||||||
<button
|
<button
|
||||||
onClick={() => setActiveTab('gdpr')}
|
onClick={() => setActiveTab('gdpr')}
|
||||||
className={`py-3 sm:py-4 px-2 sm:px-1 border-b-2 font-semibold text-xs sm:text-sm transition-all duration-300 whitespace-nowrap ${
|
className={`py-3 sm:py-4 px-2 sm:px-1 border-b-2 font-semibold text-xs sm:text-sm transition-all duration-300 whitespace-nowrap ${
|
||||||
activeTab === 'gdpr'
|
activeTab === 'gdpr'
|
||||||
? 'border-[#d4af37] text-[#d4af37] shadow-sm'
|
? 'border-[var(--luxury-gold)] text-[var(--luxury-gold)] shadow-sm'
|
||||||
: 'border-transparent text-gray-500 hover:text-[#d4af37] hover:border-[#d4af37]/30'
|
: 'border-transparent text-gray-500 hover:text-[var(--luxury-gold)] hover:border-[var(--luxury-gold)]/30'
|
||||||
}`}
|
}`}
|
||||||
>
|
>
|
||||||
<Database className={`w-4 h-4 sm:w-5 sm:h-5 inline mr-2 ${activeTab === 'gdpr' ? 'text-[#d4af37]' : ''}`} />
|
<Database className={`w-4 h-4 sm:w-5 sm:h-5 inline mr-2 ${activeTab === 'gdpr' ? 'text-[var(--luxury-gold)]' : ''}`} />
|
||||||
Data Privacy
|
Data Privacy
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
@@ -490,7 +490,7 @@ const ProfilePage: React.FC = () => {
|
|||||||
|
|
||||||
{}
|
{}
|
||||||
{activeTab === 'profile' && (
|
{activeTab === 'profile' && (
|
||||||
<div className="luxury-glass rounded-sm p-4 sm:p-6 lg:p-8 border border-[#d4af37]/20 shadow-2xl animate-slide-up">
|
<div className="luxury-glass rounded-sm p-4 sm:p-6 lg:p-8 border border-[var(--luxury-gold)]/20 shadow-2xl animate-slide-up">
|
||||||
<form onSubmit={handleSubmitProfile(onSubmitProfile)} className="space-y-5 sm:space-y-6">
|
<form onSubmit={handleSubmitProfile(onSubmitProfile)} className="space-y-5 sm:space-y-6">
|
||||||
{}
|
{}
|
||||||
<div className="flex flex-col sm:flex-row items-center sm:items-start gap-4 sm:gap-6 pb-5 sm:pb-6 border-b border-gray-200">
|
<div className="flex flex-col sm:flex-row items-center sm:items-start gap-4 sm:gap-6 pb-5 sm:pb-6 border-b border-gray-200">
|
||||||
@@ -499,20 +499,20 @@ const ProfilePage: React.FC = () => {
|
|||||||
<img
|
<img
|
||||||
src={avatarPreview || normalizeImageUrl(userInfo?.avatar)}
|
src={avatarPreview || normalizeImageUrl(userInfo?.avatar)}
|
||||||
alt="Profile"
|
alt="Profile"
|
||||||
className="w-20 h-20 sm:w-24 sm:h-24 rounded-full object-cover ring-4 ring-[#d4af37]/20 shadow-lg"
|
className="w-20 h-20 sm:w-24 sm:h-24 rounded-full object-cover ring-4 ring-[var(--luxury-gold)]/20 shadow-lg"
|
||||||
onError={() => {
|
onError={() => {
|
||||||
// If image fails to load, show default avatar
|
// If image fails to load, show default avatar
|
||||||
setAvatarError(true);
|
setAvatarError(true);
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
) : (
|
) : (
|
||||||
<div className="w-20 h-20 sm:w-24 sm:h-24 rounded-full bg-gradient-to-br from-[#d4af37] to-[#c9a227] flex items-center justify-center ring-4 ring-[#d4af37]/20 shadow-lg">
|
<div className="w-20 h-20 sm:w-24 sm:h-24 rounded-full bg-gradient-to-br from-[var(--luxury-gold)] to-[var(--luxury-gold-dark)] flex items-center justify-center ring-4 ring-[var(--luxury-gold)]/20 shadow-lg">
|
||||||
<User className="w-10 h-10 sm:w-12 sm:h-12 text-white" />
|
<User className="w-10 h-10 sm:w-12 sm:h-12 text-white" />
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
<label
|
<label
|
||||||
htmlFor="avatar-upload"
|
htmlFor="avatar-upload"
|
||||||
className="absolute bottom-0 right-0 p-2 bg-gradient-to-br from-[#d4af37] to-[#c9a227] rounded-full cursor-pointer hover:from-[#f5d76e] hover:to-[#d4af37] transition-all duration-300 shadow-lg hover:shadow-xl hover:shadow-[#d4af37]/40 hover:-translate-y-0.5 active:translate-y-0"
|
className="absolute bottom-0 right-0 p-2 bg-gradient-to-br from-[var(--luxury-gold)] to-[var(--luxury-gold-dark)] rounded-full cursor-pointer hover:from-[var(--luxury-gold-light)] hover:to-[var(--luxury-gold)] transition-all duration-300 shadow-lg hover:shadow-xl hover:shadow-[var(--luxury-gold)]/40 hover:-translate-y-0.5 active:translate-y-0"
|
||||||
>
|
>
|
||||||
<Camera className="w-3.5 h-3.5 sm:w-4 sm:h-4 text-white" />
|
<Camera className="w-3.5 h-3.5 sm:w-4 sm:h-4 text-white" />
|
||||||
<input
|
<input
|
||||||
@@ -528,7 +528,7 @@ const ProfilePage: React.FC = () => {
|
|||||||
|
|
||||||
<div>
|
<div>
|
||||||
<label className="block text-sm sm:text-base font-medium text-gray-700 mb-2">
|
<label className="block text-sm sm:text-base font-medium text-gray-700 mb-2">
|
||||||
<User className="w-4 h-4 sm:w-5 sm:h-5 inline mr-2 text-[#d4af37]" />
|
<User className="w-4 h-4 sm:w-5 sm:h-5 inline mr-2 text-[var(--luxury-gold)]" />
|
||||||
Full Name
|
Full Name
|
||||||
<span className="text-red-500 ml-1">*</span>
|
<span className="text-red-500 ml-1">*</span>
|
||||||
</label>
|
</label>
|
||||||
@@ -551,7 +551,7 @@ const ProfilePage: React.FC = () => {
|
|||||||
{}
|
{}
|
||||||
<div>
|
<div>
|
||||||
<label className="block text-sm sm:text-base font-medium text-gray-700 mb-2">
|
<label className="block text-sm sm:text-base font-medium text-gray-700 mb-2">
|
||||||
<Mail className="w-4 h-4 sm:w-5 sm:h-5 inline mr-2 text-[#d4af37]" />
|
<Mail className="w-4 h-4 sm:w-5 sm:h-5 inline mr-2 text-[var(--luxury-gold)]" />
|
||||||
Email Address
|
Email Address
|
||||||
<span className="text-red-500 ml-1">*</span>
|
<span className="text-red-500 ml-1">*</span>
|
||||||
</label>
|
</label>
|
||||||
@@ -574,7 +574,7 @@ const ProfilePage: React.FC = () => {
|
|||||||
{}
|
{}
|
||||||
<div>
|
<div>
|
||||||
<label className="block text-sm sm:text-base font-medium text-gray-700 mb-2">
|
<label className="block text-sm sm:text-base font-medium text-gray-700 mb-2">
|
||||||
<Phone className="w-4 h-4 sm:w-5 sm:h-5 inline mr-2 text-[#d4af37]" />
|
<Phone className="w-4 h-4 sm:w-5 sm:h-5 inline mr-2 text-[var(--luxury-gold)]" />
|
||||||
Phone Number
|
Phone Number
|
||||||
<span className="text-red-500 ml-1">*</span>
|
<span className="text-red-500 ml-1">*</span>
|
||||||
</label>
|
</label>
|
||||||
@@ -611,7 +611,7 @@ const ProfilePage: React.FC = () => {
|
|||||||
|
|
||||||
{}
|
{}
|
||||||
{activeTab === 'password' && (
|
{activeTab === 'password' && (
|
||||||
<div className="luxury-glass rounded-sm p-4 sm:p-6 lg:p-8 border border-[#d4af37]/20 shadow-2xl animate-slide-up">
|
<div className="luxury-glass rounded-sm p-4 sm:p-6 lg:p-8 border border-[var(--luxury-gold)]/20 shadow-2xl animate-slide-up">
|
||||||
<form onSubmit={handleSubmitPassword(onSubmitPassword)} className="space-y-5 sm:space-y-6">
|
<form onSubmit={handleSubmitPassword(onSubmitPassword)} className="space-y-5 sm:space-y-6">
|
||||||
{}
|
{}
|
||||||
<div className="bg-gradient-to-r from-blue-50 to-indigo-50 border border-blue-200/60 rounded-sm p-4 sm:p-5">
|
<div className="bg-gradient-to-r from-blue-50 to-indigo-50 border border-blue-200/60 rounded-sm p-4 sm:p-5">
|
||||||
@@ -640,7 +640,7 @@ const ProfilePage: React.FC = () => {
|
|||||||
{}
|
{}
|
||||||
<div>
|
<div>
|
||||||
<label className="block text-sm sm:text-base font-medium text-gray-700 mb-2">
|
<label className="block text-sm sm:text-base font-medium text-gray-700 mb-2">
|
||||||
<Lock className="w-4 h-4 sm:w-5 sm:h-5 inline mr-2 text-[#d4af37]" />
|
<Lock className="w-4 h-4 sm:w-5 sm:h-5 inline mr-2 text-[var(--luxury-gold)]" />
|
||||||
Current Password
|
Current Password
|
||||||
<span className="text-red-500 ml-1">*</span>
|
<span className="text-red-500 ml-1">*</span>
|
||||||
</label>
|
</label>
|
||||||
@@ -656,7 +656,7 @@ const ProfilePage: React.FC = () => {
|
|||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
onClick={() => setShowPassword({ ...showPassword, current: !showPassword.current })}
|
onClick={() => setShowPassword({ ...showPassword, current: !showPassword.current })}
|
||||||
className="absolute inset-y-0 right-0 pr-3 flex items-center text-gray-400 hover:text-[#d4af37] transition-colors"
|
className="absolute inset-y-0 right-0 pr-3 flex items-center text-gray-400 hover:text-[var(--luxury-gold)] transition-colors"
|
||||||
>
|
>
|
||||||
{showPassword.current ? (
|
{showPassword.current ? (
|
||||||
<EyeOff className="w-4 h-4 sm:w-5 sm:h-5" />
|
<EyeOff className="w-4 h-4 sm:w-5 sm:h-5" />
|
||||||
@@ -676,7 +676,7 @@ const ProfilePage: React.FC = () => {
|
|||||||
{}
|
{}
|
||||||
<div>
|
<div>
|
||||||
<label className="block text-sm sm:text-base font-medium text-gray-700 mb-2">
|
<label className="block text-sm sm:text-base font-medium text-gray-700 mb-2">
|
||||||
<Lock className="w-4 h-4 sm:w-5 sm:h-5 inline mr-2 text-[#d4af37]" />
|
<Lock className="w-4 h-4 sm:w-5 sm:h-5 inline mr-2 text-[var(--luxury-gold)]" />
|
||||||
New Password
|
New Password
|
||||||
<span className="text-red-500 ml-1">*</span>
|
<span className="text-red-500 ml-1">*</span>
|
||||||
</label>
|
</label>
|
||||||
@@ -692,7 +692,7 @@ const ProfilePage: React.FC = () => {
|
|||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
onClick={() => setShowPassword({ ...showPassword, new: !showPassword.new })}
|
onClick={() => setShowPassword({ ...showPassword, new: !showPassword.new })}
|
||||||
className="absolute inset-y-0 right-0 pr-3 flex items-center text-gray-400 hover:text-[#d4af37] transition-colors"
|
className="absolute inset-y-0 right-0 pr-3 flex items-center text-gray-400 hover:text-[var(--luxury-gold)] transition-colors"
|
||||||
>
|
>
|
||||||
{showPassword.new ? (
|
{showPassword.new ? (
|
||||||
<EyeOff className="w-4 h-4 sm:w-5 sm:h-5" />
|
<EyeOff className="w-4 h-4 sm:w-5 sm:h-5" />
|
||||||
@@ -712,7 +712,7 @@ const ProfilePage: React.FC = () => {
|
|||||||
{}
|
{}
|
||||||
<div>
|
<div>
|
||||||
<label className="block text-sm sm:text-base font-medium text-gray-700 mb-2">
|
<label className="block text-sm sm:text-base font-medium text-gray-700 mb-2">
|
||||||
<Lock className="w-4 h-4 sm:w-5 sm:h-5 inline mr-2 text-[#d4af37]" />
|
<Lock className="w-4 h-4 sm:w-5 sm:h-5 inline mr-2 text-[var(--luxury-gold)]" />
|
||||||
Confirm New Password
|
Confirm New Password
|
||||||
<span className="text-red-500 ml-1">*</span>
|
<span className="text-red-500 ml-1">*</span>
|
||||||
</label>
|
</label>
|
||||||
@@ -728,7 +728,7 @@ const ProfilePage: React.FC = () => {
|
|||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
onClick={() => setShowPassword({ ...showPassword, confirm: !showPassword.confirm })}
|
onClick={() => setShowPassword({ ...showPassword, confirm: !showPassword.confirm })}
|
||||||
className="absolute inset-y-0 right-0 pr-3 flex items-center text-gray-400 hover:text-[#d4af37] transition-colors"
|
className="absolute inset-y-0 right-0 pr-3 flex items-center text-gray-400 hover:text-[var(--luxury-gold)] transition-colors"
|
||||||
>
|
>
|
||||||
{showPassword.confirm ? (
|
{showPassword.confirm ? (
|
||||||
<EyeOff className="w-4 h-4 sm:w-5 sm:h-5" />
|
<EyeOff className="w-4 h-4 sm:w-5 sm:h-5" />
|
||||||
@@ -762,11 +762,11 @@ const ProfilePage: React.FC = () => {
|
|||||||
|
|
||||||
{}
|
{}
|
||||||
{activeTab === 'mfa' && (
|
{activeTab === 'mfa' && (
|
||||||
<div className="luxury-glass rounded-sm p-4 sm:p-6 lg:p-8 border border-[#d4af37]/20 shadow-2xl animate-slide-up space-y-5 sm:space-y-6">
|
<div className="luxury-glass rounded-sm p-4 sm:p-6 lg:p-8 border border-[var(--luxury-gold)]/20 shadow-2xl animate-slide-up space-y-5 sm:space-y-6">
|
||||||
{}
|
{}
|
||||||
<div>
|
<div>
|
||||||
<h2 className="text-xl sm:text-2xl font-serif font-semibold text-gray-900 mb-2 flex items-center gap-2">
|
<h2 className="text-xl sm:text-2xl font-serif font-semibold text-gray-900 mb-2 flex items-center gap-2">
|
||||||
<Shield className="w-5 h-5 sm:w-6 sm:h-6 text-[#d4af37]" />
|
<Shield className="w-5 h-5 sm:w-6 sm:h-6 text-[var(--luxury-gold)]" />
|
||||||
Two-Factor Authentication
|
Two-Factor Authentication
|
||||||
</h2>
|
</h2>
|
||||||
<p className="text-xs sm:text-sm text-gray-600 font-light leading-relaxed">
|
<p className="text-xs sm:text-sm text-gray-600 font-light leading-relaxed">
|
||||||
@@ -838,7 +838,7 @@ const ProfilePage: React.FC = () => {
|
|||||||
{}
|
{}
|
||||||
<div className="bg-white border border-gray-200 rounded-sm p-4 sm:p-5">
|
<div className="bg-white border border-gray-200 rounded-sm p-4 sm:p-5">
|
||||||
<h3 className="text-sm sm:text-base font-semibold text-gray-900 mb-2 flex items-center gap-2">
|
<h3 className="text-sm sm:text-base font-semibold text-gray-900 mb-2 flex items-center gap-2">
|
||||||
<RefreshCw className="w-4 h-4 sm:w-5 sm:h-5 text-[#d4af37]" />
|
<RefreshCw className="w-4 h-4 sm:w-5 sm:h-5 text-[var(--luxury-gold)]" />
|
||||||
Backup Codes
|
Backup Codes
|
||||||
</h3>
|
</h3>
|
||||||
<p className="text-xs sm:text-sm text-gray-600 font-light mb-4 leading-relaxed">
|
<p className="text-xs sm:text-sm text-gray-600 font-light mb-4 leading-relaxed">
|
||||||
@@ -984,7 +984,7 @@ const ProfilePage: React.FC = () => {
|
|||||||
{}
|
{}
|
||||||
<div className="bg-white border border-gray-200 rounded-sm p-4 sm:p-5">
|
<div className="bg-white border border-gray-200 rounded-sm p-4 sm:p-5">
|
||||||
<h3 className="font-semibold text-gray-900 mb-2 text-sm sm:text-base flex items-center gap-2">
|
<h3 className="font-semibold text-gray-900 mb-2 text-sm sm:text-base flex items-center gap-2">
|
||||||
<KeyRound className="w-4 h-4 sm:w-5 sm:h-5 text-[#d4af37]" />
|
<KeyRound className="w-4 h-4 sm:w-5 sm:h-5 text-[var(--luxury-gold)]" />
|
||||||
Step 2: Verify Setup
|
Step 2: Verify Setup
|
||||||
</h3>
|
</h3>
|
||||||
<p className="text-xs sm:text-sm text-gray-600 font-light mb-4 leading-relaxed">
|
<p className="text-xs sm:text-sm text-gray-600 font-light mb-4 leading-relaxed">
|
||||||
@@ -1110,10 +1110,10 @@ const SessionsTab: React.FC = () => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const getDeviceIcon = (userAgent?: string) => {
|
const getDeviceIcon = (userAgent?: string) => {
|
||||||
if (!userAgent) return <Monitor className="w-5 h-5 text-[#d4af37]" />;
|
if (!userAgent) return <Monitor className="w-5 h-5 text-[var(--luxury-gold)]" />;
|
||||||
if (userAgent.includes('Mobile')) return <Smartphone className="w-5 h-5 text-blue-500" />;
|
if (userAgent.includes('Mobile')) return <Smartphone className="w-5 h-5 text-blue-500" />;
|
||||||
if (userAgent.includes('Tablet')) return <Tablet className="w-5 h-5 text-purple-500" />;
|
if (userAgent.includes('Tablet')) return <Tablet className="w-5 h-5 text-purple-500" />;
|
||||||
return <Monitor className="w-5 h-5 text-[#d4af37]" />;
|
return <Monitor className="w-5 h-5 text-[var(--luxury-gold)]" />;
|
||||||
};
|
};
|
||||||
|
|
||||||
const getDeviceName = (userAgent?: string) => {
|
const getDeviceName = (userAgent?: string) => {
|
||||||
@@ -1181,17 +1181,17 @@ const SessionsTab: React.FC = () => {
|
|||||||
|
|
||||||
if (loading) {
|
if (loading) {
|
||||||
return (
|
return (
|
||||||
<div className="luxury-glass rounded-sm p-4 sm:p-6 lg:p-8 border border-[#d4af37]/20 shadow-2xl">
|
<div className="luxury-glass rounded-sm p-4 sm:p-6 lg:p-8 border border-[var(--luxury-gold)]/20 shadow-2xl">
|
||||||
<Loading text="Loading sessions..." />
|
<Loading text="Loading sessions..." />
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="luxury-glass rounded-sm p-4 sm:p-6 lg:p-8 border border-[#d4af37]/20 shadow-2xl animate-slide-up space-y-5 sm:space-y-6">
|
<div className="luxury-glass rounded-sm p-4 sm:p-6 lg:p-8 border border-[var(--luxury-gold)]/20 shadow-2xl animate-slide-up space-y-5 sm:space-y-6">
|
||||||
<div>
|
<div>
|
||||||
<h2 className="text-xl sm:text-2xl font-serif font-semibold text-gray-900 mb-2 flex items-center gap-2">
|
<h2 className="text-xl sm:text-2xl font-serif font-semibold text-gray-900 mb-2 flex items-center gap-2">
|
||||||
<Monitor className="w-5 h-5 sm:w-6 sm:h-6 text-[#d4af37]" />
|
<Monitor className="w-5 h-5 sm:w-6 sm:h-6 text-[var(--luxury-gold)]" />
|
||||||
Active Sessions
|
Active Sessions
|
||||||
</h2>
|
</h2>
|
||||||
<p className="text-xs sm:text-sm text-gray-600 font-light">
|
<p className="text-xs sm:text-sm text-gray-600 font-light">
|
||||||
@@ -1220,11 +1220,11 @@ const SessionsTab: React.FC = () => {
|
|||||||
{sessions.map((session) => (
|
{sessions.map((session) => (
|
||||||
<div
|
<div
|
||||||
key={session.id}
|
key={session.id}
|
||||||
className="bg-gradient-to-r from-slate-50 to-white border border-[#d4af37]/20 rounded-sm p-4 sm:p-5 hover:shadow-lg hover:border-[#d4af37]/40 transition-all duration-300"
|
className="bg-gradient-to-r from-slate-50 to-white border border-[var(--luxury-gold)]/20 rounded-sm p-4 sm:p-5 hover:shadow-lg hover:border-[var(--luxury-gold)]/40 transition-all duration-300"
|
||||||
>
|
>
|
||||||
<div className="flex items-start justify-between gap-4">
|
<div className="flex items-start justify-between gap-4">
|
||||||
<div className="flex items-start gap-4 flex-1">
|
<div className="flex items-start gap-4 flex-1">
|
||||||
<div className="p-2 sm:p-3 bg-gradient-to-br from-[#d4af37]/10 to-[#c9a227]/10 rounded-sm flex-shrink-0">
|
<div className="p-2 sm:p-3 bg-gradient-to-br from-[var(--luxury-gold)]/10 to-[var(--luxury-gold-dark)]/10 rounded-sm flex-shrink-0">
|
||||||
{getDeviceIcon(session.user_agent)}
|
{getDeviceIcon(session.user_agent)}
|
||||||
</div>
|
</div>
|
||||||
<div className="flex-1 min-w-0">
|
<div className="flex-1 min-w-0">
|
||||||
@@ -1354,17 +1354,17 @@ const GDPRTab: React.FC = () => {
|
|||||||
|
|
||||||
if (loading) {
|
if (loading) {
|
||||||
return (
|
return (
|
||||||
<div className="luxury-glass rounded-sm p-4 sm:p-6 lg:p-8 border border-[#d4af37]/20 shadow-2xl">
|
<div className="luxury-glass rounded-sm p-4 sm:p-6 lg:p-8 border border-[var(--luxury-gold)]/20 shadow-2xl">
|
||||||
<Loading text="Loading privacy data..." />
|
<Loading text="Loading privacy data..." />
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="luxury-glass rounded-sm p-4 sm:p-6 lg:p-8 border border-[#d4af37]/20 shadow-2xl animate-slide-up space-y-5 sm:space-y-6">
|
<div className="luxury-glass rounded-sm p-4 sm:p-6 lg:p-8 border border-[var(--luxury-gold)]/20 shadow-2xl animate-slide-up space-y-5 sm:space-y-6">
|
||||||
<div>
|
<div>
|
||||||
<h2 className="text-xl sm:text-2xl font-serif font-semibold text-gray-900 mb-2 flex items-center gap-2">
|
<h2 className="text-xl sm:text-2xl font-serif font-semibold text-gray-900 mb-2 flex items-center gap-2">
|
||||||
<Database className="w-5 h-5 sm:w-6 sm:h-6 text-[#d4af37]" />
|
<Database className="w-5 h-5 sm:w-6 sm:h-6 text-[var(--luxury-gold)]" />
|
||||||
Data Privacy & GDPR Rights
|
Data Privacy & GDPR Rights
|
||||||
</h2>
|
</h2>
|
||||||
<p className="text-xs sm:text-sm text-gray-600 font-light">
|
<p className="text-xs sm:text-sm text-gray-600 font-light">
|
||||||
@@ -1431,14 +1431,14 @@ const GDPRTab: React.FC = () => {
|
|||||||
{requests.length > 0 && (
|
{requests.length > 0 && (
|
||||||
<div className="border-t border-gray-200 pt-5 sm:pt-6">
|
<div className="border-t border-gray-200 pt-5 sm:pt-6">
|
||||||
<h3 className="text-base sm:text-lg font-semibold text-gray-900 mb-4 flex items-center gap-2">
|
<h3 className="text-base sm:text-lg font-semibold text-gray-900 mb-4 flex items-center gap-2">
|
||||||
<FileText className="w-5 h-5 text-[#d4af37]" />
|
<FileText className="w-5 h-5 text-[var(--luxury-gold)]" />
|
||||||
Request History
|
Request History
|
||||||
</h3>
|
</h3>
|
||||||
<div className="space-y-3 sm:space-y-4">
|
<div className="space-y-3 sm:space-y-4">
|
||||||
{requests.map((request) => (
|
{requests.map((request) => (
|
||||||
<div
|
<div
|
||||||
key={request.id}
|
key={request.id}
|
||||||
className="bg-gradient-to-r from-slate-50 to-white border border-[#d4af37]/20 rounded-sm p-4 sm:p-5 hover:shadow-md hover:border-[#d4af37]/40 transition-all duration-300"
|
className="bg-gradient-to-r from-slate-50 to-white border border-[var(--luxury-gold)]/20 rounded-sm p-4 sm:p-5 hover:shadow-md hover:border-[var(--luxury-gold)]/40 transition-all duration-300"
|
||||||
>
|
>
|
||||||
<div className="flex flex-col sm:flex-row items-start sm:items-center justify-between gap-3 sm:gap-4">
|
<div className="flex flex-col sm:flex-row items-start sm:items-center justify-between gap-3 sm:gap-4">
|
||||||
<div className="flex-1 min-w-0">
|
<div className="flex-1 min-w-0">
|
||||||
|
|||||||
859
Frontend/src/pages/admin/PromotionsManagementPage.tsx
Normal file
859
Frontend/src/pages/admin/PromotionsManagementPage.tsx
Normal file
@@ -0,0 +1,859 @@
|
|||||||
|
import React, { useEffect, useState } from 'react';
|
||||||
|
import {
|
||||||
|
Tag,
|
||||||
|
Search,
|
||||||
|
Plus,
|
||||||
|
Edit,
|
||||||
|
Trash2,
|
||||||
|
Filter,
|
||||||
|
X,
|
||||||
|
} from 'lucide-react';
|
||||||
|
import { toast } from 'react-toastify';
|
||||||
|
import Loading from '../../shared/components/Loading';
|
||||||
|
import Pagination from '../../shared/components/Pagination';
|
||||||
|
import { useFormatCurrency } from '../../features/payments/hooks/useFormatCurrency';
|
||||||
|
import { useCurrency } from '../../features/payments/contexts/CurrencyContext';
|
||||||
|
import promotionService, { Promotion } from '../../features/loyalty/services/promotionService';
|
||||||
|
import { getRoomTypes } from '../../features/rooms/services/roomService';
|
||||||
|
|
||||||
|
interface RoomType {
|
||||||
|
id: number;
|
||||||
|
name: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
const PromotionsManagementPage: React.FC = () => {
|
||||||
|
const { formatCurrency } = useFormatCurrency();
|
||||||
|
const { currency } = useCurrency();
|
||||||
|
|
||||||
|
const [promotions, setPromotions] = useState<Promotion[]>([]);
|
||||||
|
const [promotionsLoading, setPromotionsLoading] = useState(true);
|
||||||
|
const [showPromotionModal, setShowPromotionModal] = useState(false);
|
||||||
|
const [editingPromotion, setEditingPromotion] = useState<Promotion | null>(null);
|
||||||
|
const [promotionFilters, setPromotionFilters] = useState({
|
||||||
|
search: '',
|
||||||
|
status: '',
|
||||||
|
type: '',
|
||||||
|
});
|
||||||
|
const [promotionsCurrentPage, setPromotionsCurrentPage] = useState(1);
|
||||||
|
const [promotionsTotalPages, setPromotionsTotalPages] = useState(1);
|
||||||
|
const [promotionsTotalItems, setPromotionsTotalItems] = useState(0);
|
||||||
|
const promotionsPerPage = 5;
|
||||||
|
|
||||||
|
const [promotionFormData, setPromotionFormData] = useState({
|
||||||
|
code: '',
|
||||||
|
name: '',
|
||||||
|
description: '',
|
||||||
|
discount_type: 'percentage' as 'percentage' | 'fixed',
|
||||||
|
discount_value: 0,
|
||||||
|
min_booking_amount: 0,
|
||||||
|
max_discount_amount: 0,
|
||||||
|
min_stay_days: 0,
|
||||||
|
max_stay_days: 0,
|
||||||
|
advance_booking_days: 0,
|
||||||
|
max_advance_booking_days: 0,
|
||||||
|
allowed_check_in_days: [] as number[],
|
||||||
|
allowed_check_out_days: [] as number[],
|
||||||
|
allowed_room_type_ids: [] as number[],
|
||||||
|
excluded_room_type_ids: [] as number[],
|
||||||
|
min_guests: 0,
|
||||||
|
max_guests: 0,
|
||||||
|
first_time_customer_only: false,
|
||||||
|
repeat_customer_only: false,
|
||||||
|
blackout_dates: [] as string[],
|
||||||
|
start_date: '',
|
||||||
|
end_date: '',
|
||||||
|
usage_limit: 0,
|
||||||
|
status: 'active' as 'active' | 'inactive' | 'expired',
|
||||||
|
});
|
||||||
|
|
||||||
|
const [roomTypes, setRoomTypes] = useState<RoomType[]>([]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
fetchPromotions();
|
||||||
|
}, [promotionFilters, promotionsCurrentPage]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
fetchRoomTypes();
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
setPromotionsCurrentPage(1);
|
||||||
|
}, [promotionFilters]);
|
||||||
|
|
||||||
|
const fetchRoomTypes = async () => {
|
||||||
|
try {
|
||||||
|
const response = await getRoomTypes();
|
||||||
|
if (response.success && response.data.room_types) {
|
||||||
|
setRoomTypes(response.data.room_types);
|
||||||
|
}
|
||||||
|
} catch (error: any) {
|
||||||
|
console.error('Failed to fetch room types:', error);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const fetchPromotions = async () => {
|
||||||
|
try {
|
||||||
|
setPromotionsLoading(true);
|
||||||
|
const response = await promotionService.getPromotions({
|
||||||
|
...promotionFilters,
|
||||||
|
page: promotionsCurrentPage,
|
||||||
|
limit: promotionsPerPage,
|
||||||
|
});
|
||||||
|
setPromotions(response.data.promotions);
|
||||||
|
if (response.data.pagination) {
|
||||||
|
setPromotionsTotalPages(response.data.pagination.totalPages);
|
||||||
|
setPromotionsTotalItems(response.data.pagination.total);
|
||||||
|
}
|
||||||
|
} catch (error: any) {
|
||||||
|
toast.error(error.response?.data?.message || 'Unable to load promotions list');
|
||||||
|
} finally {
|
||||||
|
setPromotionsLoading(false);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const handlePromotionSubmit = async (e: React.FormEvent) => {
|
||||||
|
e.preventDefault();
|
||||||
|
try {
|
||||||
|
const submitData: any = {
|
||||||
|
...promotionFormData,
|
||||||
|
min_stay_days: promotionFormData.min_stay_days || undefined,
|
||||||
|
max_stay_days: promotionFormData.max_stay_days || undefined,
|
||||||
|
advance_booking_days: promotionFormData.advance_booking_days || undefined,
|
||||||
|
max_advance_booking_days: promotionFormData.max_advance_booking_days || undefined,
|
||||||
|
min_guests: promotionFormData.min_guests || undefined,
|
||||||
|
max_guests: promotionFormData.max_guests || undefined,
|
||||||
|
allowed_check_in_days: promotionFormData.allowed_check_in_days?.length ? promotionFormData.allowed_check_in_days : undefined,
|
||||||
|
allowed_check_out_days: promotionFormData.allowed_check_out_days?.length ? promotionFormData.allowed_check_out_days : undefined,
|
||||||
|
allowed_room_type_ids: promotionFormData.allowed_room_type_ids?.length ? promotionFormData.allowed_room_type_ids : undefined,
|
||||||
|
excluded_room_type_ids: promotionFormData.excluded_room_type_ids?.length ? promotionFormData.excluded_room_type_ids : undefined,
|
||||||
|
blackout_dates: promotionFormData.blackout_dates?.length ? promotionFormData.blackout_dates : undefined,
|
||||||
|
min_booking_amount: promotionFormData.min_booking_amount || undefined,
|
||||||
|
max_discount_amount: promotionFormData.max_discount_amount || undefined,
|
||||||
|
usage_limit: promotionFormData.usage_limit || undefined,
|
||||||
|
};
|
||||||
|
|
||||||
|
if (editingPromotion) {
|
||||||
|
await promotionService.updatePromotion(editingPromotion.id, submitData);
|
||||||
|
toast.success('Promotion updated successfully');
|
||||||
|
} else {
|
||||||
|
await promotionService.createPromotion(submitData);
|
||||||
|
toast.success('Promotion added successfully');
|
||||||
|
}
|
||||||
|
setShowPromotionModal(false);
|
||||||
|
resetPromotionForm();
|
||||||
|
fetchPromotions();
|
||||||
|
} catch (error: any) {
|
||||||
|
toast.error(error.response?.data?.message || 'An error occurred');
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleEditPromotion = (promotion: Promotion) => {
|
||||||
|
setEditingPromotion(promotion);
|
||||||
|
setPromotionFormData({
|
||||||
|
code: promotion.code,
|
||||||
|
name: promotion.name,
|
||||||
|
description: promotion.description || '',
|
||||||
|
discount_type: promotion.discount_type,
|
||||||
|
discount_value: promotion.discount_value,
|
||||||
|
min_booking_amount: promotion.min_booking_amount || 0,
|
||||||
|
max_discount_amount: promotion.max_discount_amount || 0,
|
||||||
|
min_stay_days: promotion.min_stay_days || 0,
|
||||||
|
max_stay_days: promotion.max_stay_days || 0,
|
||||||
|
advance_booking_days: promotion.advance_booking_days || 0,
|
||||||
|
max_advance_booking_days: promotion.max_advance_booking_days || 0,
|
||||||
|
allowed_check_in_days: promotion.allowed_check_in_days || [],
|
||||||
|
allowed_check_out_days: promotion.allowed_check_out_days || [],
|
||||||
|
allowed_room_type_ids: promotion.allowed_room_type_ids || [],
|
||||||
|
excluded_room_type_ids: promotion.excluded_room_type_ids || [],
|
||||||
|
min_guests: promotion.min_guests || 0,
|
||||||
|
max_guests: promotion.max_guests || 0,
|
||||||
|
first_time_customer_only: promotion.first_time_customer_only || false,
|
||||||
|
repeat_customer_only: promotion.repeat_customer_only || false,
|
||||||
|
blackout_dates: promotion.blackout_dates || [],
|
||||||
|
start_date: promotion.start_date?.split('T')[0] || '',
|
||||||
|
end_date: promotion.end_date?.split('T')[0] || '',
|
||||||
|
usage_limit: promotion.usage_limit || 0,
|
||||||
|
status: promotion.status,
|
||||||
|
});
|
||||||
|
setShowPromotionModal(true);
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleDeletePromotion = async (id: number) => {
|
||||||
|
if (!window.confirm('Are you sure you want to delete this promotion?')) return;
|
||||||
|
|
||||||
|
try {
|
||||||
|
await promotionService.deletePromotion(id);
|
||||||
|
toast.success('Promotion deleted successfully');
|
||||||
|
fetchPromotions();
|
||||||
|
} catch (error: any) {
|
||||||
|
toast.error(error.response?.data?.message || 'Unable to delete promotion');
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const resetPromotionForm = () => {
|
||||||
|
setEditingPromotion(null);
|
||||||
|
setPromotionFormData({
|
||||||
|
code: '',
|
||||||
|
name: '',
|
||||||
|
description: '',
|
||||||
|
discount_type: 'percentage',
|
||||||
|
discount_value: 0,
|
||||||
|
min_booking_amount: 0,
|
||||||
|
max_discount_amount: 0,
|
||||||
|
min_stay_days: 0,
|
||||||
|
max_stay_days: 0,
|
||||||
|
advance_booking_days: 0,
|
||||||
|
max_advance_booking_days: 0,
|
||||||
|
allowed_check_in_days: [],
|
||||||
|
allowed_check_out_days: [],
|
||||||
|
allowed_room_type_ids: [],
|
||||||
|
excluded_room_type_ids: [],
|
||||||
|
min_guests: 0,
|
||||||
|
max_guests: 0,
|
||||||
|
first_time_customer_only: false,
|
||||||
|
repeat_customer_only: false,
|
||||||
|
blackout_dates: [],
|
||||||
|
start_date: '',
|
||||||
|
end_date: '',
|
||||||
|
usage_limit: 0,
|
||||||
|
status: 'active',
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
const getPromotionStatusBadge = (status: string) => {
|
||||||
|
const badges: Record<string, { bg: string; text: string; label: string; border: string }> = {
|
||||||
|
active: { bg: 'bg-gradient-to-r from-emerald-50 to-green-50', text: 'text-emerald-800', label: 'Active', border: 'border-emerald-200' },
|
||||||
|
inactive: { bg: 'bg-gradient-to-r from-slate-50 to-gray-50', text: 'text-slate-700', label: 'Inactive', border: 'border-slate-200' },
|
||||||
|
expired: { bg: 'bg-gradient-to-r from-rose-50 to-red-50', text: 'text-rose-800', label: 'Expired', border: 'border-rose-200' },
|
||||||
|
};
|
||||||
|
const badge = badges[status] || badges.active;
|
||||||
|
return (
|
||||||
|
<span className={`px-4 py-1.5 rounded-full text-xs font-semibold border shadow-sm ${badge.bg} ${badge.text} ${badge.border}`}>
|
||||||
|
{badge.label}
|
||||||
|
</span>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="min-h-screen bg-gradient-to-br from-slate-50 via-white to-slate-50 -m-6 p-8">
|
||||||
|
<div className="space-y-8">
|
||||||
|
{}
|
||||||
|
<div className="bg-white/90 backdrop-blur-xl rounded-2xl shadow-xl border border-gray-200/50 p-8">
|
||||||
|
<div className="flex flex-col md:flex-row md:items-center md:justify-between gap-6">
|
||||||
|
<div className="space-y-3">
|
||||||
|
<div className="flex items-center gap-3">
|
||||||
|
<div className="p-2.5 rounded-xl bg-gradient-to-br from-purple-500/10 to-pink-500/10 border border-purple-200/40">
|
||||||
|
<Tag className="w-6 h-6 text-purple-600" />
|
||||||
|
</div>
|
||||||
|
<h2 className="text-3xl font-extrabold text-gray-900">Promotion Management</h2>
|
||||||
|
</div>
|
||||||
|
<p className="text-gray-600 text-base max-w-2xl leading-relaxed">
|
||||||
|
Manage discount codes and promotional campaigns
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
<button
|
||||||
|
onClick={() => {
|
||||||
|
resetPromotionForm();
|
||||||
|
setShowPromotionModal(true);
|
||||||
|
}}
|
||||||
|
className="group relative px-8 py-4 bg-gradient-to-r from-purple-500 via-purple-500 to-pink-600 text-white font-semibold rounded-xl shadow-xl shadow-purple-500/30 hover:shadow-2xl hover:shadow-purple-500/40 transition-all duration-300 hover:scale-105 overflow-hidden"
|
||||||
|
>
|
||||||
|
<div className="absolute inset-0 bg-gradient-to-r from-transparent via-white/20 to-transparent translate-x-[-100%] group-hover:translate-x-[100%] transition-transform duration-1000"></div>
|
||||||
|
<div className="relative flex items-center gap-3">
|
||||||
|
<Plus className="w-5 h-5" />
|
||||||
|
Add Promotion
|
||||||
|
</div>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{}
|
||||||
|
<div className="relative bg-white/90 backdrop-blur-xl rounded-2xl shadow-xl border border-gray-200/50 p-8">
|
||||||
|
<div className="flex items-center gap-3 mb-6">
|
||||||
|
<div className="p-2 rounded-xl bg-gradient-to-br from-amber-500/10 to-amber-600/10 border border-amber-200/40">
|
||||||
|
<Filter className="w-5 h-5 text-amber-600" />
|
||||||
|
</div>
|
||||||
|
<h3 className="text-xl font-bold text-gray-900">Filters</h3>
|
||||||
|
</div>
|
||||||
|
<div className="grid grid-cols-1 md:grid-cols-4 gap-6">
|
||||||
|
<div className="relative group">
|
||||||
|
<Search className="absolute left-4 top-1/2 transform -translate-y-1/2 text-gray-400 w-5 h-5 group-focus-within:text-purple-500 transition-colors" />
|
||||||
|
<input
|
||||||
|
type="text"
|
||||||
|
placeholder="Search by code or name..."
|
||||||
|
value={promotionFilters.search}
|
||||||
|
onChange={(e) => setPromotionFilters({ ...promotionFilters, search: e.target.value })}
|
||||||
|
className="w-full pl-12 pr-4 py-3.5 bg-white border-2 border-gray-200 rounded-xl focus:border-purple-400 focus:ring-4 focus:ring-purple-100 transition-all duration-200 text-gray-700 placeholder-gray-400 font-medium shadow-sm hover:shadow-md"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<select
|
||||||
|
value={promotionFilters.type}
|
||||||
|
onChange={(e) => setPromotionFilters({ ...promotionFilters, type: e.target.value })}
|
||||||
|
className="px-4 py-3.5 bg-white border-2 border-gray-200 rounded-xl focus:border-purple-400 focus:ring-4 focus:ring-purple-100 transition-all duration-200 text-gray-700 font-medium shadow-sm hover:shadow-md cursor-pointer"
|
||||||
|
>
|
||||||
|
<option value="">All Types</option>
|
||||||
|
<option value="percentage">Percentage</option>
|
||||||
|
<option value="fixed">Fixed Amount</option>
|
||||||
|
</select>
|
||||||
|
<select
|
||||||
|
value={promotionFilters.status}
|
||||||
|
onChange={(e) => setPromotionFilters({ ...promotionFilters, status: e.target.value })}
|
||||||
|
className="px-4 py-3.5 bg-white border-2 border-gray-200 rounded-xl focus:border-purple-400 focus:ring-4 focus:ring-purple-100 transition-all duration-200 text-gray-700 font-medium shadow-sm hover:shadow-md cursor-pointer"
|
||||||
|
>
|
||||||
|
<option value="">All Statuses</option>
|
||||||
|
<option value="active">Active</option>
|
||||||
|
<option value="inactive">Inactive</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{}
|
||||||
|
{promotionsLoading && promotions.length === 0 ? (
|
||||||
|
<Loading fullScreen text="Loading promotions..." />
|
||||||
|
) : (
|
||||||
|
<div className="relative bg-white/90 backdrop-blur-xl rounded-2xl shadow-xl border border-gray-200/50 overflow-hidden">
|
||||||
|
<div className="overflow-x-auto">
|
||||||
|
<table className="w-full">
|
||||||
|
<thead className="bg-gradient-to-r from-gray-50 to-gray-100">
|
||||||
|
<tr>
|
||||||
|
<th className="px-8 py-5 text-left text-xs font-bold text-gray-700 uppercase tracking-wider border-b border-gray-200">Code</th>
|
||||||
|
<th className="px-8 py-5 text-left text-xs font-bold text-gray-700 uppercase tracking-wider border-b border-gray-200">Program Name</th>
|
||||||
|
<th className="px-8 py-5 text-left text-xs font-bold text-gray-700 uppercase tracking-wider border-b border-gray-200">Value</th>
|
||||||
|
<th className="px-8 py-5 text-left text-xs font-bold text-gray-700 uppercase tracking-wider border-b border-gray-200">Period</th>
|
||||||
|
<th className="px-8 py-5 text-left text-xs font-bold text-gray-700 uppercase tracking-wider border-b border-gray-200">Used</th>
|
||||||
|
<th className="px-8 py-5 text-left text-xs font-bold text-gray-700 uppercase tracking-wider border-b border-gray-200">Status</th>
|
||||||
|
<th className="px-8 py-5 text-right text-xs font-bold text-gray-700 uppercase tracking-wider border-b border-gray-200">Actions</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody className="bg-white divide-y divide-gray-100">
|
||||||
|
{promotions.map((promotion) => (
|
||||||
|
<tr key={promotion.id} className="hover:bg-gray-50 transition-colors">
|
||||||
|
<td className="px-8 py-5 whitespace-nowrap">
|
||||||
|
<div className="flex items-center gap-3">
|
||||||
|
<div className="p-2 bg-gradient-to-br from-purple-100 to-purple-200 rounded-lg">
|
||||||
|
<Tag className="w-4 h-4 text-purple-600" />
|
||||||
|
</div>
|
||||||
|
<span className="text-sm font-mono font-bold bg-gradient-to-r from-purple-600 to-purple-700 bg-clip-text text-transparent">{promotion.code}</span>
|
||||||
|
</div>
|
||||||
|
</td>
|
||||||
|
<td className="px-8 py-5">
|
||||||
|
<div className="text-sm font-semibold text-gray-900">{promotion.name}</div>
|
||||||
|
<div className="text-xs text-gray-500 mt-0.5">{promotion.description}</div>
|
||||||
|
</td>
|
||||||
|
<td className="px-8 py-5 whitespace-nowrap">
|
||||||
|
<div className="text-sm font-bold bg-gradient-to-r from-emerald-600 to-emerald-700 bg-clip-text text-transparent">
|
||||||
|
{promotion.discount_type === 'percentage'
|
||||||
|
? `${promotion.discount_value}%`
|
||||||
|
: formatCurrency(promotion.discount_value)}
|
||||||
|
</div>
|
||||||
|
</td>
|
||||||
|
<td className="px-8 py-5 whitespace-nowrap">
|
||||||
|
<div className="text-xs text-gray-600">
|
||||||
|
{promotion.start_date ? new Date(promotion.start_date).toLocaleDateString('en-US', { month: 'short', day: 'numeric' }) : 'N/A'}
|
||||||
|
<span className="text-gray-400 mx-1">→</span>
|
||||||
|
{promotion.end_date ? new Date(promotion.end_date).toLocaleDateString('en-US', { month: 'short', day: 'numeric' }) : 'N/A'}
|
||||||
|
</div>
|
||||||
|
</td>
|
||||||
|
<td className="px-8 py-5 whitespace-nowrap">
|
||||||
|
<div className="text-sm font-medium text-gray-700">
|
||||||
|
<span className="text-purple-600 font-semibold">{promotion.used_count || 0}</span>
|
||||||
|
<span className="text-gray-400 mx-1">/</span>
|
||||||
|
<span className="text-gray-600">{promotion.usage_limit || '∞'}</span>
|
||||||
|
</div>
|
||||||
|
</td>
|
||||||
|
<td className="px-8 py-5 whitespace-nowrap">
|
||||||
|
{getPromotionStatusBadge(promotion.status)}
|
||||||
|
</td>
|
||||||
|
<td className="px-8 py-5 whitespace-nowrap text-right">
|
||||||
|
<div className="flex items-center justify-end gap-2">
|
||||||
|
<button
|
||||||
|
onClick={() => handleEditPromotion(promotion)}
|
||||||
|
className="p-2 rounded-lg text-blue-600 hover:text-blue-700 hover:bg-blue-50 transition-all duration-200 shadow-sm hover:shadow-md border border-blue-200 hover:border-blue-300"
|
||||||
|
title="Edit"
|
||||||
|
>
|
||||||
|
<Edit className="w-5 h-5" />
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
onClick={() => handleDeletePromotion(promotion.id)}
|
||||||
|
className="p-2 rounded-lg text-rose-600 hover:text-rose-700 hover:bg-rose-50 transition-all duration-200 shadow-sm hover:shadow-md border border-rose-200 hover:border-rose-300"
|
||||||
|
title="Delete"
|
||||||
|
>
|
||||||
|
<Trash2 className="w-5 h-5" />
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
))}
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
<Pagination
|
||||||
|
currentPage={promotionsCurrentPage}
|
||||||
|
totalPages={promotionsTotalPages}
|
||||||
|
onPageChange={setPromotionsCurrentPage}
|
||||||
|
totalItems={promotionsTotalItems}
|
||||||
|
itemsPerPage={promotionsPerPage}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{}
|
||||||
|
{showPromotionModal && (
|
||||||
|
<div className="fixed inset-0 bg-black/70 backdrop-blur-md z-50 overflow-y-auto p-3 sm:p-4">
|
||||||
|
<div className="min-h-full flex items-center justify-center py-4">
|
||||||
|
<div className="bg-white rounded-2xl sm:rounded-3xl shadow-2xl max-w-5xl w-full my-4 flex flex-col border border-gray-200" style={{ maxHeight: 'calc(100vh - 2rem)' }}>
|
||||||
|
<div className="bg-gradient-to-r from-slate-900 via-slate-800 to-slate-900 px-4 sm:px-6 md:px-8 py-4 sm:py-5 md:py-6 border-b border-slate-700 flex-shrink-0">
|
||||||
|
<div className="flex justify-between items-center">
|
||||||
|
<div>
|
||||||
|
<h2 className="text-xl sm:text-2xl md:text-3xl font-bold text-purple-100 mb-1">
|
||||||
|
{editingPromotion ? 'Update Promotion' : 'Add New Promotion'}
|
||||||
|
</h2>
|
||||||
|
<p className="text-purple-200/80 text-xs sm:text-sm font-light">
|
||||||
|
{editingPromotion ? 'Modify promotion details' : 'Create a new promotion program'}
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
<button
|
||||||
|
onClick={() => setShowPromotionModal(false)}
|
||||||
|
className="w-9 h-9 sm:w-10 sm:h-10 flex items-center justify-center rounded-xl text-purple-100 hover:text-white hover:bg-slate-700/50 transition-all duration-200 border border-slate-600 hover:border-purple-400"
|
||||||
|
>
|
||||||
|
<X className="w-5 h-5 sm:w-6 sm:h-6" />
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="flex-1 overflow-y-auto min-h-0">
|
||||||
|
<form onSubmit={handlePromotionSubmit} className="p-4 sm:p-6 md:p-8 space-y-4 sm:space-y-5 md:space-y-6">
|
||||||
|
<div className="grid grid-cols-2 gap-6">
|
||||||
|
<div>
|
||||||
|
<label className="block text-xs font-semibold text-gray-600 uppercase tracking-wider mb-2">
|
||||||
|
Code <span className="text-red-500">*</span>
|
||||||
|
</label>
|
||||||
|
<input
|
||||||
|
type="text"
|
||||||
|
value={promotionFormData.code}
|
||||||
|
onChange={(e) => setPromotionFormData({ ...promotionFormData, code: e.target.value.toUpperCase() })}
|
||||||
|
className="w-full px-4 py-3 bg-white border-2 border-gray-200 rounded-xl focus:border-purple-400 focus:ring-4 focus:ring-purple-100 transition-all duration-200 text-gray-700 font-medium shadow-sm font-mono"
|
||||||
|
placeholder="e.g: SUMMER2024"
|
||||||
|
required
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<label className="block text-xs font-semibold text-gray-600 uppercase tracking-wider mb-2">
|
||||||
|
Program Name <span className="text-red-500">*</span>
|
||||||
|
</label>
|
||||||
|
<input
|
||||||
|
type="text"
|
||||||
|
value={promotionFormData.name}
|
||||||
|
onChange={(e) => setPromotionFormData({ ...promotionFormData, name: e.target.value })}
|
||||||
|
className="w-full px-4 py-3 bg-white border-2 border-gray-200 rounded-xl focus:border-purple-400 focus:ring-4 focus:ring-purple-100 transition-all duration-200 text-gray-700 font-medium shadow-sm"
|
||||||
|
placeholder="e.g: Summer Sale"
|
||||||
|
required
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div>
|
||||||
|
<label className="block text-xs font-semibold text-gray-600 uppercase tracking-wider mb-2">
|
||||||
|
Description
|
||||||
|
</label>
|
||||||
|
<textarea
|
||||||
|
value={promotionFormData.description}
|
||||||
|
onChange={(e) => setPromotionFormData({ ...promotionFormData, description: e.target.value })}
|
||||||
|
className="w-full px-4 py-3 bg-white border-2 border-gray-200 rounded-xl focus:border-purple-400 focus:ring-4 focus:ring-purple-100 transition-all duration-200 text-gray-700 font-medium shadow-sm"
|
||||||
|
rows={3}
|
||||||
|
placeholder="Detailed description of the program..."
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="grid grid-cols-2 gap-6">
|
||||||
|
<div>
|
||||||
|
<label className="block text-xs font-semibold text-gray-600 uppercase tracking-wider mb-2">
|
||||||
|
Discount Type <span className="text-red-500">*</span>
|
||||||
|
</label>
|
||||||
|
<select
|
||||||
|
value={promotionFormData.discount_type}
|
||||||
|
onChange={(e) => setPromotionFormData({ ...promotionFormData, discount_type: e.target.value as 'percentage' | 'fixed' })}
|
||||||
|
className="w-full px-4 py-3 bg-white border-2 border-gray-200 rounded-xl focus:border-purple-400 focus:ring-4 focus:ring-purple-100 transition-all duration-200 text-gray-700 font-medium shadow-sm cursor-pointer"
|
||||||
|
>
|
||||||
|
<option value="percentage">Percentage (%)</option>
|
||||||
|
<option value="fixed">Fixed Amount ({currency})</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<label className="block text-xs font-semibold text-gray-600 uppercase tracking-wider mb-2">
|
||||||
|
Discount Value <span className="text-red-500">*</span>
|
||||||
|
</label>
|
||||||
|
<input
|
||||||
|
type="number"
|
||||||
|
value={promotionFormData.discount_value}
|
||||||
|
onChange={(e) => setPromotionFormData({ ...promotionFormData, discount_value: parseFloat(e.target.value) })}
|
||||||
|
className="w-full px-4 py-3 bg-white border-2 border-gray-200 rounded-xl focus:border-purple-400 focus:ring-4 focus:ring-purple-100 transition-all duration-200 text-gray-700 font-medium shadow-sm"
|
||||||
|
min="0"
|
||||||
|
max={promotionFormData.discount_type === 'percentage' ? 100 : undefined}
|
||||||
|
required
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="grid grid-cols-2 gap-6">
|
||||||
|
<div>
|
||||||
|
<label className="block text-xs font-semibold text-gray-600 uppercase tracking-wider mb-2">
|
||||||
|
Minimum Order Value ({currency})
|
||||||
|
</label>
|
||||||
|
<input
|
||||||
|
type="number"
|
||||||
|
value={promotionFormData.min_booking_amount}
|
||||||
|
onChange={(e) => setPromotionFormData({ ...promotionFormData, min_booking_amount: parseFloat(e.target.value) })}
|
||||||
|
className="w-full px-4 py-3 bg-white border-2 border-gray-200 rounded-xl focus:border-purple-400 focus:ring-4 focus:ring-purple-100 transition-all duration-200 text-gray-700 font-medium shadow-sm"
|
||||||
|
min="0"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<label className="block text-xs font-semibold text-gray-600 uppercase tracking-wider mb-2">
|
||||||
|
Maximum Discount ({currency})
|
||||||
|
</label>
|
||||||
|
<input
|
||||||
|
type="number"
|
||||||
|
value={promotionFormData.max_discount_amount}
|
||||||
|
onChange={(e) => setPromotionFormData({ ...promotionFormData, max_discount_amount: parseFloat(e.target.value) })}
|
||||||
|
className="w-full px-4 py-3 bg-white border-2 border-gray-200 rounded-xl focus:border-purple-400 focus:ring-4 focus:ring-purple-100 transition-all duration-200 text-gray-700 font-medium shadow-sm"
|
||||||
|
min="0"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Enterprise Booking Conditions Section */}
|
||||||
|
<div className="border-t-2 border-purple-200 pt-6 mt-6">
|
||||||
|
<h3 className="text-lg font-bold text-gray-900 mb-4 flex items-center gap-2">
|
||||||
|
<div className="h-1 w-8 bg-gradient-to-r from-purple-400 to-purple-600 rounded-full"></div>
|
||||||
|
Enterprise Booking Conditions
|
||||||
|
</h3>
|
||||||
|
<p className="text-sm text-gray-600 mb-6">Configure advanced conditions for when this promotion applies</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="grid grid-cols-2 gap-6">
|
||||||
|
<div>
|
||||||
|
<label className="block text-xs font-semibold text-gray-600 uppercase tracking-wider mb-2">
|
||||||
|
Minimum Stay (nights)
|
||||||
|
</label>
|
||||||
|
<input
|
||||||
|
type="number"
|
||||||
|
value={promotionFormData.min_stay_days || ''}
|
||||||
|
onChange={(e) => setPromotionFormData({ ...promotionFormData, min_stay_days: parseInt(e.target.value) || 0 })}
|
||||||
|
className="w-full px-4 py-3 bg-white border-2 border-gray-200 rounded-xl focus:border-purple-400 focus:ring-4 focus:ring-purple-100 transition-all duration-200 text-gray-700 font-medium shadow-sm"
|
||||||
|
min="0"
|
||||||
|
placeholder="0 = no minimum"
|
||||||
|
/>
|
||||||
|
<p className="text-xs text-gray-500 mt-1">Minimum number of nights required for booking</p>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<label className="block text-xs font-semibold text-gray-600 uppercase tracking-wider mb-2">
|
||||||
|
Advance Booking (days)
|
||||||
|
</label>
|
||||||
|
<input
|
||||||
|
type="number"
|
||||||
|
value={promotionFormData.advance_booking_days || ''}
|
||||||
|
onChange={(e) => setPromotionFormData({ ...promotionFormData, advance_booking_days: parseInt(e.target.value) || 0 })}
|
||||||
|
className="w-full px-4 py-3 bg-white border-2 border-gray-200 rounded-xl focus:border-purple-400 focus:ring-4 focus:ring-purple-100 transition-all duration-200 text-gray-700 font-medium shadow-sm"
|
||||||
|
min="0"
|
||||||
|
placeholder="0 = no requirement"
|
||||||
|
/>
|
||||||
|
<p className="text-xs text-gray-500 mt-1">Minimum days in advance the booking must be made</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="grid grid-cols-2 gap-6">
|
||||||
|
<div>
|
||||||
|
<label className="block text-xs font-semibold text-gray-600 uppercase tracking-wider mb-2">
|
||||||
|
Maximum Stay (nights)
|
||||||
|
</label>
|
||||||
|
<input
|
||||||
|
type="number"
|
||||||
|
value={promotionFormData.max_stay_days || ''}
|
||||||
|
onChange={(e) => setPromotionFormData({ ...promotionFormData, max_stay_days: parseInt(e.target.value) || 0 })}
|
||||||
|
className="w-full px-4 py-3 bg-white border-2 border-gray-200 rounded-xl focus:border-purple-400 focus:ring-4 focus:ring-purple-100 transition-all duration-200 text-gray-700 font-medium shadow-sm"
|
||||||
|
min="0"
|
||||||
|
placeholder="0 = no maximum"
|
||||||
|
/>
|
||||||
|
<p className="text-xs text-gray-500 mt-1">Maximum number of nights allowed for booking</p>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<label className="block text-xs font-semibold text-gray-600 uppercase tracking-wider mb-2">
|
||||||
|
Max Advance Booking (days)
|
||||||
|
</label>
|
||||||
|
<input
|
||||||
|
type="number"
|
||||||
|
value={promotionFormData.max_advance_booking_days || ''}
|
||||||
|
onChange={(e) => setPromotionFormData({ ...promotionFormData, max_advance_booking_days: parseInt(e.target.value) || 0 })}
|
||||||
|
className="w-full px-4 py-3 bg-white border-2 border-gray-200 rounded-xl focus:border-purple-400 focus:ring-4 focus:ring-purple-100 transition-all duration-200 text-gray-700 font-medium shadow-sm"
|
||||||
|
min="0"
|
||||||
|
placeholder="0 = no maximum"
|
||||||
|
/>
|
||||||
|
<p className="text-xs text-gray-500 mt-1">Maximum days in advance the booking can be made</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="grid grid-cols-2 gap-6">
|
||||||
|
<div>
|
||||||
|
<label className="block text-xs font-semibold text-gray-600 uppercase tracking-wider mb-2">
|
||||||
|
Allowed Check-in Days
|
||||||
|
</label>
|
||||||
|
<div className="grid grid-cols-7 gap-2">
|
||||||
|
{['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun'].map((day, index) => (
|
||||||
|
<label key={day} className="flex items-center gap-2 cursor-pointer">
|
||||||
|
<input
|
||||||
|
type="checkbox"
|
||||||
|
checked={promotionFormData.allowed_check_in_days?.includes(index) || false}
|
||||||
|
onChange={(e) => {
|
||||||
|
const current = promotionFormData.allowed_check_in_days || [];
|
||||||
|
if (e.target.checked) {
|
||||||
|
setPromotionFormData({ ...promotionFormData, allowed_check_in_days: [...current, index] });
|
||||||
|
} else {
|
||||||
|
setPromotionFormData({ ...promotionFormData, allowed_check_in_days: current.filter(d => d !== index) });
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
className="w-4 h-4 text-purple-600 border-gray-300 rounded focus:ring-purple-500"
|
||||||
|
/>
|
||||||
|
<span className="text-xs text-gray-700">{day}</span>
|
||||||
|
</label>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
<p className="text-xs text-gray-500 mt-1">Leave empty to allow all days</p>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<label className="block text-xs font-semibold text-gray-600 uppercase tracking-wider mb-2">
|
||||||
|
Allowed Check-out Days
|
||||||
|
</label>
|
||||||
|
<div className="grid grid-cols-7 gap-2">
|
||||||
|
{['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun'].map((day, index) => (
|
||||||
|
<label key={day} className="flex items-center gap-2 cursor-pointer">
|
||||||
|
<input
|
||||||
|
type="checkbox"
|
||||||
|
checked={promotionFormData.allowed_check_out_days?.includes(index) || false}
|
||||||
|
onChange={(e) => {
|
||||||
|
const current = promotionFormData.allowed_check_out_days || [];
|
||||||
|
if (e.target.checked) {
|
||||||
|
setPromotionFormData({ ...promotionFormData, allowed_check_out_days: [...current, index] });
|
||||||
|
} else {
|
||||||
|
setPromotionFormData({ ...promotionFormData, allowed_check_out_days: current.filter(d => d !== index) });
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
className="w-4 h-4 text-purple-600 border-gray-300 rounded focus:ring-purple-500"
|
||||||
|
/>
|
||||||
|
<span className="text-xs text-gray-700">{day}</span>
|
||||||
|
</label>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
<p className="text-xs text-gray-500 mt-1">Leave empty to allow all days</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="grid grid-cols-2 gap-6">
|
||||||
|
<div>
|
||||||
|
<label className="block text-xs font-semibold text-gray-600 uppercase tracking-wider mb-2">
|
||||||
|
Allowed Room Types
|
||||||
|
</label>
|
||||||
|
<select
|
||||||
|
multiple
|
||||||
|
value={promotionFormData.allowed_room_type_ids?.map(String) || []}
|
||||||
|
onChange={(e) => {
|
||||||
|
const selected = Array.from(e.target.selectedOptions, option => parseInt(option.value));
|
||||||
|
setPromotionFormData({ ...promotionFormData, allowed_room_type_ids: selected });
|
||||||
|
}}
|
||||||
|
className="w-full px-4 py-3 bg-white border-2 border-gray-200 rounded-xl focus:border-purple-400 focus:ring-4 focus:ring-purple-100 transition-all duration-200 text-gray-700 font-medium shadow-sm"
|
||||||
|
size={4}
|
||||||
|
>
|
||||||
|
{roomTypes.map(rt => (
|
||||||
|
<option key={rt.id} value={rt.id}>{rt.name}</option>
|
||||||
|
))}
|
||||||
|
</select>
|
||||||
|
<p className="text-xs text-gray-500 mt-1">Hold Ctrl/Cmd to select multiple. Leave empty to allow all room types.</p>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<label className="block text-xs font-semibold text-gray-600 uppercase tracking-wider mb-2">
|
||||||
|
Excluded Room Types
|
||||||
|
</label>
|
||||||
|
<select
|
||||||
|
multiple
|
||||||
|
value={promotionFormData.excluded_room_type_ids?.map(String) || []}
|
||||||
|
onChange={(e) => {
|
||||||
|
const selected = Array.from(e.target.selectedOptions, option => parseInt(option.value));
|
||||||
|
setPromotionFormData({ ...promotionFormData, excluded_room_type_ids: selected });
|
||||||
|
}}
|
||||||
|
className="w-full px-4 py-3 bg-white border-2 border-gray-200 rounded-xl focus:border-purple-400 focus:ring-4 focus:ring-purple-100 transition-all duration-200 text-gray-700 font-medium shadow-sm"
|
||||||
|
size={4}
|
||||||
|
>
|
||||||
|
{roomTypes.map(rt => (
|
||||||
|
<option key={rt.id} value={rt.id}>{rt.name}</option>
|
||||||
|
))}
|
||||||
|
</select>
|
||||||
|
<p className="text-xs text-gray-500 mt-1">Hold Ctrl/Cmd to select multiple. These room types cannot use this promotion.</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="grid grid-cols-2 gap-6">
|
||||||
|
<div>
|
||||||
|
<label className="block text-xs font-semibold text-gray-600 uppercase tracking-wider mb-2">
|
||||||
|
Minimum Guests
|
||||||
|
</label>
|
||||||
|
<input
|
||||||
|
type="number"
|
||||||
|
value={promotionFormData.min_guests || ''}
|
||||||
|
onChange={(e) => setPromotionFormData({ ...promotionFormData, min_guests: parseInt(e.target.value) || 0 })}
|
||||||
|
className="w-full px-4 py-3 bg-white border-2 border-gray-200 rounded-xl focus:border-purple-400 focus:ring-4 focus:ring-purple-100 transition-all duration-200 text-gray-700 font-medium shadow-sm"
|
||||||
|
min="1"
|
||||||
|
placeholder="0 = no minimum"
|
||||||
|
/>
|
||||||
|
<p className="text-xs text-gray-500 mt-1">Minimum number of guests required</p>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<label className="block text-xs font-semibold text-gray-600 uppercase tracking-wider mb-2">
|
||||||
|
Maximum Guests
|
||||||
|
</label>
|
||||||
|
<input
|
||||||
|
type="number"
|
||||||
|
value={promotionFormData.max_guests || ''}
|
||||||
|
onChange={(e) => setPromotionFormData({ ...promotionFormData, max_guests: parseInt(e.target.value) || 0 })}
|
||||||
|
className="w-full px-4 py-3 bg-white border-2 border-gray-200 rounded-xl focus:border-purple-400 focus:ring-4 focus:ring-purple-100 transition-all duration-200 text-gray-700 font-medium shadow-sm"
|
||||||
|
min="1"
|
||||||
|
placeholder="0 = no maximum"
|
||||||
|
/>
|
||||||
|
<p className="text-xs text-gray-500 mt-1">Maximum number of guests allowed</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="grid grid-cols-2 gap-6">
|
||||||
|
<div>
|
||||||
|
<label className="flex items-center gap-3 cursor-pointer">
|
||||||
|
<input
|
||||||
|
type="checkbox"
|
||||||
|
checked={promotionFormData.first_time_customer_only || false}
|
||||||
|
onChange={(e) => setPromotionFormData({ ...promotionFormData, first_time_customer_only: e.target.checked, repeat_customer_only: e.target.checked ? false : promotionFormData.repeat_customer_only })}
|
||||||
|
className="w-5 h-5 text-purple-600 border-gray-300 rounded focus:ring-purple-500"
|
||||||
|
/>
|
||||||
|
<span className="text-sm font-semibold text-gray-700">First-Time Customer Only</span>
|
||||||
|
</label>
|
||||||
|
<p className="text-xs text-gray-500 mt-1 ml-8">Only available to first-time customers</p>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<label className="flex items-center gap-3 cursor-pointer">
|
||||||
|
<input
|
||||||
|
type="checkbox"
|
||||||
|
checked={promotionFormData.repeat_customer_only || false}
|
||||||
|
onChange={(e) => setPromotionFormData({ ...promotionFormData, repeat_customer_only: e.target.checked, first_time_customer_only: e.target.checked ? false : promotionFormData.first_time_customer_only })}
|
||||||
|
className="w-5 h-5 text-purple-600 border-gray-300 rounded focus:ring-purple-500"
|
||||||
|
/>
|
||||||
|
<span className="text-sm font-semibold text-gray-700">Repeat Customer Only</span>
|
||||||
|
</label>
|
||||||
|
<p className="text-xs text-gray-500 mt-1 ml-8">Only available to returning customers</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div>
|
||||||
|
<label className="block text-xs font-semibold text-gray-600 uppercase tracking-wider mb-2">
|
||||||
|
Blackout Dates
|
||||||
|
</label>
|
||||||
|
<textarea
|
||||||
|
value={promotionFormData.blackout_dates?.join('\n') || ''}
|
||||||
|
onChange={(e) => {
|
||||||
|
const dates = e.target.value.split('\n').filter(d => d.trim()).map(d => d.trim());
|
||||||
|
setPromotionFormData({ ...promotionFormData, blackout_dates: dates });
|
||||||
|
}}
|
||||||
|
className="w-full px-4 py-3 bg-white border-2 border-gray-200 rounded-xl focus:border-purple-400 focus:ring-4 focus:ring-purple-100 transition-all duration-200 text-gray-700 font-medium shadow-sm"
|
||||||
|
rows={3}
|
||||||
|
placeholder="Enter dates (one per line) in YYYY-MM-DD format Example: 2024-12-25 2024-12-31 2025-01-01"
|
||||||
|
/>
|
||||||
|
<p className="text-xs text-gray-500 mt-1">Dates when promotion doesn't apply. One date per line (YYYY-MM-DD format).</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Dates & Status Section */}
|
||||||
|
<div className="border-t-2 border-purple-200 pt-6 mt-6">
|
||||||
|
<h3 className="text-lg font-bold text-gray-900 mb-4 flex items-center gap-2">
|
||||||
|
<div className="h-1 w-8 bg-gradient-to-r from-purple-400 to-purple-600 rounded-full"></div>
|
||||||
|
Promotion Period & Status
|
||||||
|
</h3>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="grid grid-cols-2 gap-6">
|
||||||
|
<div>
|
||||||
|
<label className="block text-xs font-semibold text-gray-600 uppercase tracking-wider mb-2">
|
||||||
|
Start Date <span className="text-red-500">*</span>
|
||||||
|
</label>
|
||||||
|
<input
|
||||||
|
type="date"
|
||||||
|
value={promotionFormData.start_date}
|
||||||
|
onChange={(e) => setPromotionFormData({ ...promotionFormData, start_date: e.target.value })}
|
||||||
|
className="w-full px-4 py-3 bg-white border-2 border-gray-200 rounded-xl focus:border-purple-400 focus:ring-4 focus:ring-purple-100 transition-all duration-200 text-gray-700 font-medium shadow-sm"
|
||||||
|
required
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<label className="block text-xs font-semibold text-gray-600 uppercase tracking-wider mb-2">
|
||||||
|
End Date <span className="text-red-500">*</span>
|
||||||
|
</label>
|
||||||
|
<input
|
||||||
|
type="date"
|
||||||
|
value={promotionFormData.end_date}
|
||||||
|
onChange={(e) => setPromotionFormData({ ...promotionFormData, end_date: e.target.value })}
|
||||||
|
className="w-full px-4 py-3 bg-white border-2 border-gray-200 rounded-xl focus:border-purple-400 focus:ring-4 focus:ring-purple-100 transition-all duration-200 text-gray-700 font-medium shadow-sm"
|
||||||
|
required
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="grid grid-cols-2 gap-6">
|
||||||
|
<div>
|
||||||
|
<label className="block text-xs font-semibold text-gray-600 uppercase tracking-wider mb-2">
|
||||||
|
Usage Limit (0 = unlimited)
|
||||||
|
</label>
|
||||||
|
<input
|
||||||
|
type="number"
|
||||||
|
value={promotionFormData.usage_limit}
|
||||||
|
onChange={(e) => setPromotionFormData({ ...promotionFormData, usage_limit: parseInt(e.target.value) })}
|
||||||
|
className="w-full px-4 py-3 bg-white border-2 border-gray-200 rounded-xl focus:border-purple-400 focus:ring-4 focus:ring-purple-100 transition-all duration-200 text-gray-700 font-medium shadow-sm"
|
||||||
|
min="0"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<label className="block text-xs font-semibold text-gray-600 uppercase tracking-wider mb-2">
|
||||||
|
Status
|
||||||
|
</label>
|
||||||
|
<select
|
||||||
|
value={promotionFormData.status}
|
||||||
|
onChange={(e) => setPromotionFormData({ ...promotionFormData, status: e.target.value as 'active' | 'inactive' })}
|
||||||
|
className="w-full px-4 py-3 bg-white border-2 border-gray-200 rounded-xl focus:border-purple-400 focus:ring-4 focus:ring-purple-100 transition-all duration-200 text-gray-700 font-medium shadow-sm cursor-pointer"
|
||||||
|
>
|
||||||
|
<option value="active">Active</option>
|
||||||
|
<option value="inactive">Inactive</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="sticky bottom-0 bg-white border-t border-gray-200 mt-8 -mx-4 sm:-mx-6 md:-mx-8 px-4 sm:px-6 md:px-8 py-4 flex justify-end gap-3">
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
onClick={() => setShowPromotionModal(false)}
|
||||||
|
className="px-8 py-3 border-2 border-gray-300 rounded-xl text-gray-700 font-semibold hover:bg-gray-50 transition-all duration-200 shadow-sm hover:shadow-md"
|
||||||
|
>
|
||||||
|
Cancel
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
type="submit"
|
||||||
|
className="px-8 py-3 bg-gradient-to-r from-purple-500 to-purple-600 text-white rounded-xl font-semibold hover:from-purple-600 hover:to-purple-700 transition-all duration-200 shadow-lg hover:shadow-xl"
|
||||||
|
>
|
||||||
|
{editingPromotion ? 'Update' : 'Create'}
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default PromotionsManagementPage;
|
||||||
|
|
||||||
@@ -1,4 +1,5 @@
|
|||||||
import React, { useState, useEffect, useCallback } from 'react';
|
import React, { useState, useEffect, useCallback } from 'react';
|
||||||
|
import { useNavigate } from 'react-router-dom';
|
||||||
import {
|
import {
|
||||||
LogIn,
|
LogIn,
|
||||||
LogOut,
|
LogOut,
|
||||||
@@ -1293,12 +1294,20 @@ const ReceptionDashboardPage: React.FC = () => {
|
|||||||
<div className="p-2.5 rounded-xl bg-gradient-to-br from-blue-500/10 to-indigo-500/10 border border-blue-200/40">
|
<div className="p-2.5 rounded-xl bg-gradient-to-br from-blue-500/10 to-indigo-500/10 border border-blue-200/40">
|
||||||
<Calendar className="w-6 h-6 text-blue-600" />
|
<Calendar className="w-6 h-6 text-blue-600" />
|
||||||
</div>
|
</div>
|
||||||
<h2 className="text-xl sm:text-2xl md:text-2xl font-extrabold text-gray-900">Bookings Management</h2>
|
<h2 className="text-xl sm:text-2xl md:text-2xl font-extrabold text-gray-900">Reception Bookings</h2>
|
||||||
</div>
|
</div>
|
||||||
<p className="text-gray-600 text-base max-w-2xl leading-relaxed">
|
<p className="text-gray-600 text-base max-w-2xl leading-relaxed">
|
||||||
Manage and track all hotel bookings with precision
|
Quick view of bookings relevant to reception operations (confirmed, checked-in, checked-out)
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
|
<div className="flex gap-3">
|
||||||
|
<button
|
||||||
|
onClick={() => navigate('/admin/bookings')}
|
||||||
|
className="flex items-center justify-center gap-2 px-6 py-3 bg-gradient-to-r from-blue-500 to-blue-600 text-white rounded-xl font-semibold hover:from-blue-600 hover:to-blue-700 transition-all duration-200 shadow-lg hover:shadow-xl whitespace-nowrap w-full sm:w-auto"
|
||||||
|
>
|
||||||
|
<Eye className="w-5 h-5" />
|
||||||
|
View All Bookings
|
||||||
|
</button>
|
||||||
<button
|
<button
|
||||||
onClick={() => setShowCreateBookingModal(true)}
|
onClick={() => setShowCreateBookingModal(true)}
|
||||||
className="flex items-center justify-center gap-2 px-6 py-3 bg-gradient-to-r from-amber-500 to-amber-600 text-white rounded-xl font-semibold hover:from-amber-600 hover:to-amber-700 transition-all duration-200 shadow-lg hover:shadow-xl whitespace-nowrap w-full sm:w-auto"
|
className="flex items-center justify-center gap-2 px-6 py-3 bg-gradient-to-r from-amber-500 to-amber-600 text-white rounded-xl font-semibold hover:from-amber-600 hover:to-amber-700 transition-all duration-200 shadow-lg hover:shadow-xl whitespace-nowrap w-full sm:w-auto"
|
||||||
@@ -1308,6 +1317,7 @@ const ReceptionDashboardPage: React.FC = () => {
|
|||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
{}
|
{}
|
||||||
<div className="bg-white/90 backdrop-blur-xl rounded-2xl shadow-xl border border-gray-200/50 p-6">
|
<div className="bg-white/90 backdrop-blur-xl rounded-2xl shadow-xl border border-gray-200/50 p-6">
|
||||||
|
|||||||
@@ -1,379 +0,0 @@
|
|||||||
import React, { useState } from 'react';
|
|
||||||
import {
|
|
||||||
Calendar,
|
|
||||||
Users,
|
|
||||||
Hotel,
|
|
||||||
TrendingUp,
|
|
||||||
Download,
|
|
||||||
Filter,
|
|
||||||
BarChart3,
|
|
||||||
PieChart
|
|
||||||
} from 'lucide-react';
|
|
||||||
import { toast } from 'react-toastify';
|
|
||||||
import Loading from '../../shared/components/Loading';
|
|
||||||
import EmptyState from '../../shared/components/EmptyState';
|
|
||||||
import CurrencyIcon from '../../shared/components/CurrencyIcon';
|
|
||||||
import { useAsync } from '../../shared/hooks/useAsync';
|
|
||||||
import reportService, { ReportData } from '../../features/analytics/services/reportService';
|
|
||||||
import { formatDate } from '../../shared/utils/format';
|
|
||||||
import { useFormatCurrency } from '../../features/payments/hooks/useFormatCurrency';
|
|
||||||
|
|
||||||
const ReportsPage: React.FC = () => {
|
|
||||||
const { formatCurrency } = useFormatCurrency();
|
|
||||||
const [dateRange, setDateRange] = useState({
|
|
||||||
from: '',
|
|
||||||
to: '',
|
|
||||||
});
|
|
||||||
const [reportType, setReportType] = useState<'daily' | 'weekly' | 'monthly' | 'yearly' | ''>('');
|
|
||||||
|
|
||||||
const fetchReports = async (): Promise<ReportData> => {
|
|
||||||
const params: any = {};
|
|
||||||
if (dateRange.from) params.from = dateRange.from;
|
|
||||||
if (dateRange.to) params.to = dateRange.to;
|
|
||||||
if (reportType) params.type = reportType;
|
|
||||||
|
|
||||||
const response = await reportService.getReports(params);
|
|
||||||
return response.data;
|
|
||||||
};
|
|
||||||
|
|
||||||
const {
|
|
||||||
data: reportData,
|
|
||||||
loading,
|
|
||||||
error,
|
|
||||||
execute: refetchReports
|
|
||||||
} = useAsync<ReportData>(fetchReports, {
|
|
||||||
immediate: true,
|
|
||||||
onError: (error: any) => {
|
|
||||||
toast.error(error.message || 'Unable to load reports');
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
const handleExport = async () => {
|
|
||||||
try {
|
|
||||||
const params: any = {};
|
|
||||||
if (dateRange.from) params.from = dateRange.from;
|
|
||||||
if (dateRange.to) params.to = dateRange.to;
|
|
||||||
if (reportType) params.type = reportType;
|
|
||||||
|
|
||||||
const blob = await reportService.exportReport(params);
|
|
||||||
const url = window.URL.createObjectURL(blob);
|
|
||||||
const a = document.createElement('a');
|
|
||||||
a.href = url;
|
|
||||||
a.download = `report-${new Date().toISOString().split('T')[0]}.csv`;
|
|
||||||
document.body.appendChild(a);
|
|
||||||
a.click();
|
|
||||||
window.URL.revokeObjectURL(url);
|
|
||||||
document.body.removeChild(a);
|
|
||||||
toast.success('Report exported successfully');
|
|
||||||
} catch (error: any) {
|
|
||||||
toast.error(error.message || 'Failed to export report');
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const handleFilter = () => {
|
|
||||||
refetchReports();
|
|
||||||
};
|
|
||||||
|
|
||||||
if (loading && !reportData) {
|
|
||||||
return <Loading fullScreen text="Loading reports..." />;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (error && !reportData) {
|
|
||||||
return (
|
|
||||||
<div className="container mx-auto px-4 py-8">
|
|
||||||
<EmptyState
|
|
||||||
title="Unable to Load Reports"
|
|
||||||
description={error.message || 'Something went wrong. Please try again.'}
|
|
||||||
action={{
|
|
||||||
label: 'Retry',
|
|
||||||
onClick: refetchReports
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div className="container mx-auto px-4 py-8">
|
|
||||||
<div className="mb-8 flex justify-between items-center">
|
|
||||||
<div>
|
|
||||||
<h1 className="text-3xl font-bold text-gray-900 mb-2">Reports & Analytics</h1>
|
|
||||||
<p className="text-gray-600">View comprehensive reports and statistics</p>
|
|
||||||
</div>
|
|
||||||
<button
|
|
||||||
onClick={handleExport}
|
|
||||||
className="flex items-center space-x-2 px-4 py-2 bg-[#d4af37] text-white rounded-lg hover:bg-[#c9a227] transition-colors"
|
|
||||||
>
|
|
||||||
<Download className="w-5 h-5" />
|
|
||||||
<span>Export CSV</span>
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{}
|
|
||||||
<div className="mb-6 bg-white p-4 rounded-lg shadow-md">
|
|
||||||
<div className="flex items-center space-x-2 mb-4">
|
|
||||||
<Filter className="w-5 h-5 text-gray-500" />
|
|
||||||
<h3 className="text-lg font-semibold text-gray-900">Filters</h3>
|
|
||||||
</div>
|
|
||||||
<div className="grid grid-cols-1 md:grid-cols-4 gap-4">
|
|
||||||
<div>
|
|
||||||
<label className="block text-sm font-medium text-gray-700 mb-1">
|
|
||||||
From Date
|
|
||||||
</label>
|
|
||||||
<input
|
|
||||||
type="date"
|
|
||||||
value={dateRange.from}
|
|
||||||
onChange={(e) => setDateRange({ ...dateRange, from: e.target.value })}
|
|
||||||
className="w-full px-4 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-[#d4af37] focus:border-[#d4af37]"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
<div>
|
|
||||||
<label className="block text-sm font-medium text-gray-700 mb-1">
|
|
||||||
To Date
|
|
||||||
</label>
|
|
||||||
<input
|
|
||||||
type="date"
|
|
||||||
value={dateRange.to}
|
|
||||||
onChange={(e) => setDateRange({ ...dateRange, to: e.target.value })}
|
|
||||||
className="w-full px-4 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-[#d4af37] focus:border-[#d4af37]"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
<div>
|
|
||||||
<label className="block text-sm font-medium text-gray-700 mb-1">
|
|
||||||
Report Type
|
|
||||||
</label>
|
|
||||||
<select
|
|
||||||
value={reportType}
|
|
||||||
onChange={(e) => setReportType(e.target.value as any)}
|
|
||||||
className="w-full px-4 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-[#d4af37] focus:border-[#d4af37]"
|
|
||||||
>
|
|
||||||
<option value="">All</option>
|
|
||||||
<option value="daily">Daily</option>
|
|
||||||
<option value="weekly">Weekly</option>
|
|
||||||
<option value="monthly">Monthly</option>
|
|
||||||
<option value="yearly">Yearly</option>
|
|
||||||
</select>
|
|
||||||
</div>
|
|
||||||
<div className="flex items-end">
|
|
||||||
<button
|
|
||||||
onClick={handleFilter}
|
|
||||||
className="w-full px-4 py-2 bg-[#d4af37] text-white rounded-lg hover:bg-[#c9a227] transition-colors"
|
|
||||||
>
|
|
||||||
Apply Filters
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{reportData && (
|
|
||||||
<>
|
|
||||||
{}
|
|
||||||
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-6 mb-8">
|
|
||||||
<div className="bg-white p-6 rounded-lg shadow-md border-l-4 border-blue-500">
|
|
||||||
<div className="flex items-center justify-between mb-4">
|
|
||||||
<div className="p-3 bg-blue-100 rounded-lg">
|
|
||||||
<Calendar className="w-6 h-6 text-blue-600" />
|
|
||||||
</div>
|
|
||||||
<TrendingUp className="w-5 h-5 text-green-500" />
|
|
||||||
</div>
|
|
||||||
<h3 className="text-gray-500 text-sm font-medium mb-1">
|
|
||||||
Total Bookings
|
|
||||||
</h3>
|
|
||||||
<p className="text-3xl font-bold text-gray-800">
|
|
||||||
{reportData.total_bookings || 0}
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className="bg-white p-6 rounded-lg shadow-md border-l-4 border-green-500">
|
|
||||||
<div className="flex items-center justify-between mb-4">
|
|
||||||
<div className="p-3 bg-green-100 rounded-lg">
|
|
||||||
<CurrencyIcon className="text-green-600" size={24} />
|
|
||||||
</div>
|
|
||||||
<TrendingUp className="w-5 h-5 text-green-500" />
|
|
||||||
</div>
|
|
||||||
<h3 className="text-gray-500 text-sm font-medium mb-1">
|
|
||||||
Total Revenue
|
|
||||||
</h3>
|
|
||||||
<p className="text-3xl font-bold text-gray-800">
|
|
||||||
{formatCurrency(reportData.total_revenue || 0)}
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className="bg-white p-6 rounded-lg shadow-md border-l-4 border-purple-500">
|
|
||||||
<div className="flex items-center justify-between mb-4">
|
|
||||||
<div className="p-3 bg-purple-100 rounded-lg">
|
|
||||||
<Users className="w-6 h-6 text-purple-600" />
|
|
||||||
</div>
|
|
||||||
<TrendingUp className="w-5 h-5 text-green-500" />
|
|
||||||
</div>
|
|
||||||
<h3 className="text-gray-500 text-sm font-medium mb-1">
|
|
||||||
Total Customers
|
|
||||||
</h3>
|
|
||||||
<p className="text-3xl font-bold text-gray-800">
|
|
||||||
{reportData.total_customers || 0}
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className="bg-white p-6 rounded-lg shadow-md border-l-4 border-orange-500">
|
|
||||||
<div className="flex items-center justify-between mb-4">
|
|
||||||
<div className="p-3 bg-orange-100 rounded-lg">
|
|
||||||
<Hotel className="w-6 h-6 text-orange-600" />
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<h3 className="text-gray-500 text-sm font-medium mb-1">
|
|
||||||
Available Rooms
|
|
||||||
</h3>
|
|
||||||
<p className="text-3xl font-bold text-gray-800">
|
|
||||||
{reportData.available_rooms || 0}
|
|
||||||
</p>
|
|
||||||
<p className="text-sm text-gray-500 mt-1">
|
|
||||||
{reportData.occupied_rooms || 0} occupied
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{}
|
|
||||||
{reportData.bookings_by_status && (
|
|
||||||
<div className="bg-white p-6 rounded-lg shadow-md mb-8">
|
|
||||||
<div className="flex items-center space-x-2 mb-6">
|
|
||||||
<PieChart className="w-5 h-5 text-gray-500" />
|
|
||||||
<h2 className="text-xl font-bold text-gray-900">Bookings by Status</h2>
|
|
||||||
</div>
|
|
||||||
<div className="grid grid-cols-2 md:grid-cols-5 gap-4">
|
|
||||||
{Object.entries(reportData.bookings_by_status).map(([status, count]) => (
|
|
||||||
<div key={status} className="text-center p-4 bg-gray-50 rounded-lg">
|
|
||||||
<p className="text-2xl font-bold text-gray-800">{count}</p>
|
|
||||||
<p className="text-sm text-gray-600 capitalize mt-1">{status.replace('_', ' ')}</p>
|
|
||||||
</div>
|
|
||||||
))}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
|
|
||||||
{}
|
|
||||||
{reportData.revenue_by_date && reportData.revenue_by_date.length > 0 && (
|
|
||||||
<div className="bg-white p-6 rounded-lg shadow-md mb-8">
|
|
||||||
<div className="flex items-center space-x-2 mb-6">
|
|
||||||
<BarChart3 className="w-5 h-5 text-gray-500" />
|
|
||||||
<h2 className="text-xl font-bold text-gray-900">Revenue by Date</h2>
|
|
||||||
</div>
|
|
||||||
<div className="overflow-x-auto">
|
|
||||||
<table className="w-full">
|
|
||||||
<thead className="bg-gray-50">
|
|
||||||
<tr>
|
|
||||||
<th className="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase">
|
|
||||||
Date
|
|
||||||
</th>
|
|
||||||
<th className="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase">
|
|
||||||
Bookings
|
|
||||||
</th>
|
|
||||||
<th className="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase">
|
|
||||||
Revenue
|
|
||||||
</th>
|
|
||||||
</tr>
|
|
||||||
</thead>
|
|
||||||
<tbody className="bg-white divide-y divide-gray-200">
|
|
||||||
{reportData.revenue_by_date.map((item, index) => (
|
|
||||||
<tr key={index} className="hover:bg-gray-50">
|
|
||||||
<td className="px-6 py-4 whitespace-nowrap text-sm text-gray-900">
|
|
||||||
{formatDate(new Date(item.date), 'short')}
|
|
||||||
</td>
|
|
||||||
<td className="px-6 py-4 whitespace-nowrap text-sm text-gray-500">
|
|
||||||
{item.bookings}
|
|
||||||
</td>
|
|
||||||
<td className="px-6 py-4 whitespace-nowrap text-sm font-medium text-gray-900">
|
|
||||||
{formatCurrency(item.revenue)}
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
))}
|
|
||||||
</tbody>
|
|
||||||
</table>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
|
|
||||||
{}
|
|
||||||
{reportData.top_rooms && reportData.top_rooms.length > 0 && (
|
|
||||||
<div className="bg-white p-6 rounded-lg shadow-md mb-8">
|
|
||||||
<h2 className="text-xl font-bold text-gray-900 mb-6">Top Performing Rooms</h2>
|
|
||||||
<div className="overflow-x-auto">
|
|
||||||
<table className="w-full">
|
|
||||||
<thead className="bg-gray-50">
|
|
||||||
<tr>
|
|
||||||
<th className="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase">
|
|
||||||
Room Number
|
|
||||||
</th>
|
|
||||||
<th className="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase">
|
|
||||||
Bookings
|
|
||||||
</th>
|
|
||||||
<th className="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase">
|
|
||||||
Revenue
|
|
||||||
</th>
|
|
||||||
</tr>
|
|
||||||
</thead>
|
|
||||||
<tbody className="bg-white divide-y divide-gray-200">
|
|
||||||
{reportData.top_rooms.map((room) => (
|
|
||||||
<tr key={room.room_id} className="hover:bg-gray-50">
|
|
||||||
<td className="px-6 py-4 whitespace-nowrap text-sm font-medium text-gray-900">
|
|
||||||
{room.room_number}
|
|
||||||
</td>
|
|
||||||
<td className="px-6 py-4 whitespace-nowrap text-sm text-gray-500">
|
|
||||||
{room.bookings}
|
|
||||||
</td>
|
|
||||||
<td className="px-6 py-4 whitespace-nowrap text-sm font-medium text-gray-900">
|
|
||||||
{formatCurrency(room.revenue)}
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
))}
|
|
||||||
</tbody>
|
|
||||||
</table>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
|
|
||||||
{}
|
|
||||||
{reportData.service_usage && reportData.service_usage.length > 0 && (
|
|
||||||
<div className="bg-white p-6 rounded-lg shadow-md">
|
|
||||||
<h2 className="text-xl font-bold text-gray-900 mb-6">Service Usage</h2>
|
|
||||||
<div className="overflow-x-auto">
|
|
||||||
<table className="w-full">
|
|
||||||
<thead className="bg-gray-50">
|
|
||||||
<tr>
|
|
||||||
<th className="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase">
|
|
||||||
Service Name
|
|
||||||
</th>
|
|
||||||
<th className="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase">
|
|
||||||
Usage Count
|
|
||||||
</th>
|
|
||||||
<th className="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase">
|
|
||||||
Total Revenue
|
|
||||||
</th>
|
|
||||||
</tr>
|
|
||||||
</thead>
|
|
||||||
<tbody className="bg-white divide-y divide-gray-200">
|
|
||||||
{reportData.service_usage.map((service) => (
|
|
||||||
<tr key={service.service_id} className="hover:bg-gray-50">
|
|
||||||
<td className="px-6 py-4 whitespace-nowrap text-sm font-medium text-gray-900">
|
|
||||||
{service.service_name}
|
|
||||||
</td>
|
|
||||||
<td className="px-6 py-4 whitespace-nowrap text-sm text-gray-500">
|
|
||||||
{service.usage_count}
|
|
||||||
</td>
|
|
||||||
<td className="px-6 py-4 whitespace-nowrap text-sm font-medium text-gray-900">
|
|
||||||
{formatCurrency(service.total_revenue)}
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
))}
|
|
||||||
</tbody>
|
|
||||||
</table>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
</>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
export default ReportsPage;
|
|
||||||
|
|
||||||
@@ -22,7 +22,8 @@ import {
|
|||||||
MessageCircle,
|
MessageCircle,
|
||||||
Clock,
|
Clock,
|
||||||
X,
|
X,
|
||||||
CheckCircle2
|
CheckCircle2,
|
||||||
|
Palette
|
||||||
} from 'lucide-react';
|
} from 'lucide-react';
|
||||||
import { toast } from 'react-toastify';
|
import { toast } from 'react-toastify';
|
||||||
import adminPrivacyService, {
|
import adminPrivacyService, {
|
||||||
@@ -42,12 +43,13 @@ import systemSettingsService, {
|
|||||||
CompanySettingsResponse,
|
CompanySettingsResponse,
|
||||||
UpdateCompanySettingsRequest,
|
UpdateCompanySettingsRequest,
|
||||||
} from '../../features/system/services/systemSettingsService';
|
} from '../../features/system/services/systemSettingsService';
|
||||||
|
import { themeService, ThemeSettingsResponse, UpdateThemeSettingsRequest } from '../../features/system/services/systemSettingsService';
|
||||||
import { recaptchaService, RecaptchaSettingsAdminResponse, UpdateRecaptchaSettingsRequest } from '../../features/system/services/systemSettingsService';
|
import { recaptchaService, RecaptchaSettingsAdminResponse, UpdateRecaptchaSettingsRequest } from '../../features/system/services/systemSettingsService';
|
||||||
import { useCurrency } from '../../features/payments/contexts/CurrencyContext';
|
import { useCurrency } from '../../features/payments/contexts/CurrencyContext';
|
||||||
import Loading from '../../shared/components/Loading';
|
import Loading from '../../shared/components/Loading';
|
||||||
import { getCurrencySymbol } from '../../shared/utils/format';
|
import { getCurrencySymbol } from '../../shared/utils/format';
|
||||||
|
|
||||||
type SettingsTab = 'general' | 'cookie' | 'currency' | 'payment' | 'smtp' | 'company' | 'recaptcha';
|
type SettingsTab = 'general' | 'cookie' | 'currency' | 'payment' | 'smtp' | 'company' | 'recaptcha' | 'theme';
|
||||||
|
|
||||||
const SettingsPage: React.FC = () => {
|
const SettingsPage: React.FC = () => {
|
||||||
const { currency, supportedCurrencies, refreshCurrency } = useCurrency();
|
const { currency, supportedCurrencies, refreshCurrency } = useCurrency();
|
||||||
@@ -147,6 +149,15 @@ const SettingsPage: React.FC = () => {
|
|||||||
});
|
});
|
||||||
const [showRecaptchaSecret, setShowRecaptchaSecret] = useState(false);
|
const [showRecaptchaSecret, setShowRecaptchaSecret] = useState(false);
|
||||||
|
|
||||||
|
// Theme settings state
|
||||||
|
const [themeSettings, setThemeSettings] = useState<ThemeSettingsResponse['data'] | null>(null);
|
||||||
|
const [themeFormData, setThemeFormData] = useState<UpdateThemeSettingsRequest>({
|
||||||
|
theme_primary_color: '#d4af37',
|
||||||
|
theme_primary_light: '#f5d76e',
|
||||||
|
theme_primary_dark: '#c9a227',
|
||||||
|
theme_primary_accent: '#e8c547',
|
||||||
|
});
|
||||||
|
|
||||||
const [loading, setLoading] = useState(true);
|
const [loading, setLoading] = useState(true);
|
||||||
const [saving, setSaving] = useState(false);
|
const [saving, setSaving] = useState(false);
|
||||||
|
|
||||||
@@ -200,6 +211,9 @@ const SettingsPage: React.FC = () => {
|
|||||||
if (activeTab === 'recaptcha') {
|
if (activeTab === 'recaptcha') {
|
||||||
loadRecaptchaSettings();
|
loadRecaptchaSettings();
|
||||||
}
|
}
|
||||||
|
if (activeTab === 'theme') {
|
||||||
|
loadThemeSettings();
|
||||||
|
}
|
||||||
}, [activeTab]);
|
}, [activeTab]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
@@ -781,6 +795,47 @@ const SettingsPage: React.FC = () => {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const loadThemeSettings = async () => {
|
||||||
|
try {
|
||||||
|
const themeRes = await themeService.getThemeSettings();
|
||||||
|
setThemeSettings(themeRes.data);
|
||||||
|
setThemeFormData({
|
||||||
|
theme_primary_color: themeRes.data.theme_primary_color || '#d4af37',
|
||||||
|
theme_primary_light: themeRes.data.theme_primary_light || '#f5d76e',
|
||||||
|
theme_primary_dark: themeRes.data.theme_primary_dark || '#c9a227',
|
||||||
|
theme_primary_accent: themeRes.data.theme_primary_accent || '#e8c547',
|
||||||
|
});
|
||||||
|
} catch (error: any) {
|
||||||
|
toast.error(
|
||||||
|
error.response?.data?.detail ||
|
||||||
|
error.response?.data?.message ||
|
||||||
|
'Failed to load theme settings'
|
||||||
|
);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleSaveTheme = async () => {
|
||||||
|
try {
|
||||||
|
setSaving(true);
|
||||||
|
await themeService.updateThemeSettings(themeFormData);
|
||||||
|
toast.success('Theme settings updated successfully');
|
||||||
|
await loadThemeSettings();
|
||||||
|
|
||||||
|
// Trigger theme refresh in the app
|
||||||
|
if (typeof window !== 'undefined') {
|
||||||
|
window.dispatchEvent(new CustomEvent('refreshTheme'));
|
||||||
|
}
|
||||||
|
} catch (error: any) {
|
||||||
|
toast.error(
|
||||||
|
error.response?.data?.detail ||
|
||||||
|
error.response?.data?.message ||
|
||||||
|
'Failed to save theme settings'
|
||||||
|
);
|
||||||
|
} finally {
|
||||||
|
setSaving(false);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
if (loading) {
|
if (loading) {
|
||||||
return <Loading fullScreen={false} text="Loading settings..." />;
|
return <Loading fullScreen={false} text="Loading settings..." />;
|
||||||
}
|
}
|
||||||
@@ -792,6 +847,7 @@ const SettingsPage: React.FC = () => {
|
|||||||
{ id: 'payment' as SettingsTab, label: 'Payment', icon: CreditCard },
|
{ id: 'payment' as SettingsTab, label: 'Payment', icon: CreditCard },
|
||||||
{ id: 'smtp' as SettingsTab, label: 'Email Server', icon: Mail },
|
{ id: 'smtp' as SettingsTab, label: 'Email Server', icon: Mail },
|
||||||
{ id: 'company' as SettingsTab, label: 'Company Info', icon: Building2 },
|
{ id: 'company' as SettingsTab, label: 'Company Info', icon: Building2 },
|
||||||
|
{ id: 'theme' as SettingsTab, label: 'Theme Colors', icon: Palette },
|
||||||
{ id: 'recaptcha' as SettingsTab, label: 'reCAPTCHA', icon: Shield },
|
{ id: 'recaptcha' as SettingsTab, label: 'reCAPTCHA', icon: Shield },
|
||||||
];
|
];
|
||||||
|
|
||||||
@@ -2909,6 +2965,192 @@ const SettingsPage: React.FC = () => {
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
|
{activeTab === 'theme' && (
|
||||||
|
<div className="space-y-8">
|
||||||
|
<div className="flex items-center justify-between">
|
||||||
|
<div>
|
||||||
|
<h2 className="text-2xl font-bold text-gray-900 flex items-center gap-3">
|
||||||
|
<Palette className="w-6 h-6 text-amber-600" />
|
||||||
|
Theme Colors
|
||||||
|
</h2>
|
||||||
|
<p className="text-gray-600 mt-2">
|
||||||
|
Customize the primary color scheme of your frontend pages
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="bg-white rounded-xl shadow-lg border border-gray-200 p-8">
|
||||||
|
<div className="space-y-6">
|
||||||
|
<div className="grid grid-cols-1 md:grid-cols-2 gap-6">
|
||||||
|
<div className="space-y-2">
|
||||||
|
<label className="flex items-center gap-2 text-sm font-bold text-gray-900 tracking-wide">
|
||||||
|
<Palette className="w-4 h-4 text-gray-600" />
|
||||||
|
Primary Color
|
||||||
|
</label>
|
||||||
|
<div className="flex gap-3">
|
||||||
|
<input
|
||||||
|
type="color"
|
||||||
|
value={themeFormData.theme_primary_color || '#d4af37'}
|
||||||
|
onChange={(e) =>
|
||||||
|
setThemeFormData({
|
||||||
|
...themeFormData,
|
||||||
|
theme_primary_color: e.target.value,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
className="w-20 h-12 rounded-lg border-2 border-gray-300 cursor-pointer"
|
||||||
|
/>
|
||||||
|
<input
|
||||||
|
type="text"
|
||||||
|
value={themeFormData.theme_primary_color || '#d4af37'}
|
||||||
|
onChange={(e) =>
|
||||||
|
setThemeFormData({
|
||||||
|
...themeFormData,
|
||||||
|
theme_primary_color: e.target.value,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
placeholder="#d4af37"
|
||||||
|
className="flex-1 px-4 py-3.5 bg-white border border-gray-300 rounded-xl shadow-sm focus:ring-2 focus:ring-amber-500/50 focus:border-amber-500 transition-all duration-200 text-sm font-mono"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<p className="text-xs text-gray-500">
|
||||||
|
Main brand color used for buttons, links, and highlights
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="space-y-2">
|
||||||
|
<label className="flex items-center gap-2 text-sm font-bold text-gray-900 tracking-wide">
|
||||||
|
<Palette className="w-4 h-4 text-gray-600" />
|
||||||
|
Primary Light
|
||||||
|
</label>
|
||||||
|
<div className="flex gap-3">
|
||||||
|
<input
|
||||||
|
type="color"
|
||||||
|
value={themeFormData.theme_primary_light || '#f5d76e'}
|
||||||
|
onChange={(e) =>
|
||||||
|
setThemeFormData({
|
||||||
|
...themeFormData,
|
||||||
|
theme_primary_light: e.target.value,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
className="w-20 h-12 rounded-lg border-2 border-gray-300 cursor-pointer"
|
||||||
|
/>
|
||||||
|
<input
|
||||||
|
type="text"
|
||||||
|
value={themeFormData.theme_primary_light || '#f5d76e'}
|
||||||
|
onChange={(e) =>
|
||||||
|
setThemeFormData({
|
||||||
|
...themeFormData,
|
||||||
|
theme_primary_light: e.target.value,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
placeholder="#f5d76e"
|
||||||
|
className="flex-1 px-4 py-3.5 bg-white border border-gray-300 rounded-xl shadow-sm focus:ring-2 focus:ring-amber-500/50 focus:border-amber-500 transition-all duration-200 text-sm font-mono"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<p className="text-xs text-gray-500">
|
||||||
|
Lighter variant for hover states and gradients
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="space-y-2">
|
||||||
|
<label className="flex items-center gap-2 text-sm font-bold text-gray-900 tracking-wide">
|
||||||
|
<Palette className="w-4 h-4 text-gray-600" />
|
||||||
|
Primary Dark
|
||||||
|
</label>
|
||||||
|
<div className="flex gap-3">
|
||||||
|
<input
|
||||||
|
type="color"
|
||||||
|
value={themeFormData.theme_primary_dark || '#c9a227'}
|
||||||
|
onChange={(e) =>
|
||||||
|
setThemeFormData({
|
||||||
|
...themeFormData,
|
||||||
|
theme_primary_dark: e.target.value,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
className="w-20 h-12 rounded-lg border-2 border-gray-300 cursor-pointer"
|
||||||
|
/>
|
||||||
|
<input
|
||||||
|
type="text"
|
||||||
|
value={themeFormData.theme_primary_dark || '#c9a227'}
|
||||||
|
onChange={(e) =>
|
||||||
|
setThemeFormData({
|
||||||
|
...themeFormData,
|
||||||
|
theme_primary_dark: e.target.value,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
placeholder="#c9a227"
|
||||||
|
className="flex-1 px-4 py-3.5 bg-white border border-gray-300 rounded-xl shadow-sm focus:ring-2 focus:ring-amber-500/50 focus:border-amber-500 transition-all duration-200 text-sm font-mono"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<p className="text-xs text-gray-500">
|
||||||
|
Darker variant for borders and shadows
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="space-y-2">
|
||||||
|
<label className="flex items-center gap-2 text-sm font-bold text-gray-900 tracking-wide">
|
||||||
|
<Palette className="w-4 h-4 text-gray-600" />
|
||||||
|
Primary Accent
|
||||||
|
</label>
|
||||||
|
<div className="flex gap-3">
|
||||||
|
<input
|
||||||
|
type="color"
|
||||||
|
value={themeFormData.theme_primary_accent || '#e8c547'}
|
||||||
|
onChange={(e) =>
|
||||||
|
setThemeFormData({
|
||||||
|
...themeFormData,
|
||||||
|
theme_primary_accent: e.target.value,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
className="w-20 h-12 rounded-lg border-2 border-gray-300 cursor-pointer"
|
||||||
|
/>
|
||||||
|
<input
|
||||||
|
type="text"
|
||||||
|
value={themeFormData.theme_primary_accent || '#e8c547'}
|
||||||
|
onChange={(e) =>
|
||||||
|
setThemeFormData({
|
||||||
|
...themeFormData,
|
||||||
|
theme_primary_accent: e.target.value,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
placeholder="#e8c547"
|
||||||
|
className="flex-1 px-4 py-3.5 bg-white border border-gray-300 rounded-xl shadow-sm focus:ring-2 focus:ring-amber-500/50 focus:border-amber-500 transition-all duration-200 text-sm font-mono"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<p className="text-xs text-gray-500">
|
||||||
|
Accent color for special highlights and effects
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="bg-blue-50 border border-blue-200 rounded-lg p-4">
|
||||||
|
<div className="flex items-start gap-3">
|
||||||
|
<Info className="w-5 h-5 text-blue-600 flex-shrink-0 mt-0.5" />
|
||||||
|
<div className="text-sm text-blue-800">
|
||||||
|
<p className="font-semibold mb-1">About Theme Colors</p>
|
||||||
|
<p className="text-xs">
|
||||||
|
Changes to theme colors will be applied immediately across all frontend pages.
|
||||||
|
Use hex color codes (e.g., #d4af37) for best results. The colors will replace the default gold/yellow theme throughout the application.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="flex justify-end pt-4 border-t border-gray-200">
|
||||||
|
<button
|
||||||
|
onClick={handleSaveTheme}
|
||||||
|
disabled={saving}
|
||||||
|
className="flex items-center gap-2 px-6 py-3 bg-gradient-to-r from-amber-500 to-amber-600 text-white rounded-xl font-semibold hover:from-amber-600 hover:to-amber-700 transition-all duration-200 shadow-lg hover:shadow-xl disabled:opacity-50 disabled:cursor-not-allowed"
|
||||||
|
>
|
||||||
|
<Save className="w-5 h-5" />
|
||||||
|
{saving ? 'Saving...' : 'Save Theme Settings'}
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -92,7 +92,7 @@ const BoricaReturnPage: React.FC = () => {
|
|||||||
<div className="bg-gradient-to-br from-gray-900/40 to-gray-800/20
|
<div className="bg-gradient-to-br from-gray-900/40 to-gray-800/20
|
||||||
border border-gray-700/50 rounded-xl p-6 sm:p-10 w-full max-w-2xl mx-auto text-center
|
border border-gray-700/50 rounded-xl p-6 sm:p-10 w-full max-w-2xl mx-auto text-center
|
||||||
backdrop-blur-xl shadow-2xl shadow-black/20">
|
backdrop-blur-xl shadow-2xl shadow-black/20">
|
||||||
<Loader2 className="w-12 h-12 sm:w-16 sm:h-16 animate-spin text-[#d4af37] mx-auto mb-4" />
|
<Loader2 className="w-12 h-12 sm:w-16 sm:h-16 animate-spin text-[var(--luxury-gold)] mx-auto mb-4" />
|
||||||
<h1 className="text-xl sm:text-2xl font-serif font-semibold text-white mb-2">
|
<h1 className="text-xl sm:text-2xl font-serif font-semibold text-white mb-2">
|
||||||
Processing Payment...
|
Processing Payment...
|
||||||
</h1>
|
</h1>
|
||||||
@@ -124,11 +124,11 @@ const BoricaReturnPage: React.FC = () => {
|
|||||||
</p>
|
</p>
|
||||||
<button
|
<button
|
||||||
onClick={() => navigate(`/bookings/${bookingId}`)}
|
onClick={() => navigate(`/bookings/${bookingId}`)}
|
||||||
className="bg-gradient-to-r from-[#d4af37] to-[#c9a227]
|
className="bg-gradient-to-r from-[var(--luxury-gold)] to-[var(--luxury-gold-dark)]
|
||||||
text-[#0f0f0f] px-6 py-2 sm:px-8 sm:py-3 rounded-sm
|
text-[#0f0f0f] px-6 py-2 sm:px-8 sm:py-3 rounded-sm
|
||||||
hover:from-[#f5d76e] hover:to-[#d4af37]
|
hover:from-[var(--luxury-gold-light)] hover:to-[var(--luxury-gold)]
|
||||||
transition-all duration-300 font-medium tracking-wide
|
transition-all duration-300 font-medium tracking-wide
|
||||||
shadow-lg shadow-[#d4af37]/30 text-sm sm:text-base w-full sm:w-auto"
|
shadow-lg shadow-[var(--luxury-gold)]/30 text-sm sm:text-base w-full sm:w-auto"
|
||||||
>
|
>
|
||||||
View Booking
|
View Booking
|
||||||
</button>
|
</button>
|
||||||
@@ -165,11 +165,11 @@ const BoricaReturnPage: React.FC = () => {
|
|||||||
</button>
|
</button>
|
||||||
<button
|
<button
|
||||||
onClick={() => navigate('/')}
|
onClick={() => navigate('/')}
|
||||||
className="bg-gradient-to-r from-[#d4af37] to-[#c9a227]
|
className="bg-gradient-to-r from-[var(--luxury-gold)] to-[var(--luxury-gold-dark)]
|
||||||
text-[#0f0f0f] px-6 py-2 sm:px-8 sm:py-3 rounded-sm
|
text-[#0f0f0f] px-6 py-2 sm:px-8 sm:py-3 rounded-sm
|
||||||
hover:from-[#f5d76e] hover:to-[#d4af37]
|
hover:from-[var(--luxury-gold-light)] hover:to-[var(--luxury-gold)]
|
||||||
transition-all duration-300 font-medium tracking-wide
|
transition-all duration-300 font-medium tracking-wide
|
||||||
shadow-lg shadow-[#d4af37]/30 text-sm sm:text-base w-full sm:w-auto"
|
shadow-lg shadow-[var(--luxury-gold)]/30 text-sm sm:text-base w-full sm:w-auto"
|
||||||
>
|
>
|
||||||
Go Home
|
Go Home
|
||||||
</button>
|
</button>
|
||||||
|
|||||||
@@ -130,7 +130,7 @@ const ComplaintPage: React.FC = () => {
|
|||||||
</div>
|
</div>
|
||||||
<button
|
<button
|
||||||
onClick={() => setShowCreateModal(true)}
|
onClick={() => setShowCreateModal(true)}
|
||||||
className="flex items-center space-x-2 px-4 py-2 bg-[#d4af37] text-white rounded-lg hover:bg-[#c9a227] transition-colors"
|
className="flex items-center space-x-2 px-4 py-2 bg-[var(--luxury-gold)] text-white rounded-lg hover:bg-[var(--luxury-gold-dark)] transition-colors"
|
||||||
>
|
>
|
||||||
<Plus className="w-5 h-5" />
|
<Plus className="w-5 h-5" />
|
||||||
<span>Submit Complaint</span>
|
<span>Submit Complaint</span>
|
||||||
@@ -180,7 +180,7 @@ const ComplaintPage: React.FC = () => {
|
|||||||
<td className="px-6 py-4 whitespace-nowrap text-sm font-medium">
|
<td className="px-6 py-4 whitespace-nowrap text-sm font-medium">
|
||||||
<button
|
<button
|
||||||
onClick={() => handleViewDetails(complaint.id)}
|
onClick={() => handleViewDetails(complaint.id)}
|
||||||
className="text-[#d4af37] hover:text-[#c9a227]"
|
className="text-[var(--luxury-gold)] hover:text-[var(--luxury-gold-dark)]"
|
||||||
>
|
>
|
||||||
<Eye className="w-5 h-5" />
|
<Eye className="w-5 h-5" />
|
||||||
</button>
|
</button>
|
||||||
@@ -277,7 +277,7 @@ const CreateComplaintModal: React.FC<{
|
|||||||
<select
|
<select
|
||||||
value={formData.booking_id}
|
value={formData.booking_id}
|
||||||
onChange={(e) => setFormData({ ...formData, booking_id: e.target.value })}
|
onChange={(e) => setFormData({ ...formData, booking_id: e.target.value })}
|
||||||
className="w-full px-3 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-[#d4af37] focus:border-transparent"
|
className="w-full px-3 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-[var(--luxury-gold)] focus:border-transparent"
|
||||||
>
|
>
|
||||||
<option value="">Select a booking</option>
|
<option value="">Select a booking</option>
|
||||||
{bookings.map((booking) => (
|
{bookings.map((booking) => (
|
||||||
@@ -295,7 +295,7 @@ const CreateComplaintModal: React.FC<{
|
|||||||
value={formData.category}
|
value={formData.category}
|
||||||
onChange={(e) => setFormData({ ...formData, category: e.target.value })}
|
onChange={(e) => setFormData({ ...formData, category: e.target.value })}
|
||||||
required
|
required
|
||||||
className="w-full px-3 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-[#d4af37] focus:border-transparent"
|
className="w-full px-3 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-[var(--luxury-gold)] focus:border-transparent"
|
||||||
>
|
>
|
||||||
<option value="">Select category</option>
|
<option value="">Select category</option>
|
||||||
<option value="room_quality">Room Quality</option>
|
<option value="room_quality">Room Quality</option>
|
||||||
@@ -315,7 +315,7 @@ const CreateComplaintModal: React.FC<{
|
|||||||
<select
|
<select
|
||||||
value={formData.priority}
|
value={formData.priority}
|
||||||
onChange={(e) => setFormData({ ...formData, priority: e.target.value })}
|
onChange={(e) => setFormData({ ...formData, priority: e.target.value })}
|
||||||
className="w-full px-3 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-[#d4af37] focus:border-transparent"
|
className="w-full px-3 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-[var(--luxury-gold)] focus:border-transparent"
|
||||||
>
|
>
|
||||||
<option value="low">Low</option>
|
<option value="low">Low</option>
|
||||||
<option value="medium">Medium</option>
|
<option value="medium">Medium</option>
|
||||||
@@ -332,7 +332,7 @@ const CreateComplaintModal: React.FC<{
|
|||||||
value={formData.title}
|
value={formData.title}
|
||||||
onChange={(e) => setFormData({ ...formData, title: e.target.value })}
|
onChange={(e) => setFormData({ ...formData, title: e.target.value })}
|
||||||
required
|
required
|
||||||
className="w-full px-3 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-[#d4af37] focus:border-transparent"
|
className="w-full px-3 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-[var(--luxury-gold)] focus:border-transparent"
|
||||||
placeholder="Brief description of the issue"
|
placeholder="Brief description of the issue"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
@@ -345,7 +345,7 @@ const CreateComplaintModal: React.FC<{
|
|||||||
onChange={(e) => setFormData({ ...formData, description: e.target.value })}
|
onChange={(e) => setFormData({ ...formData, description: e.target.value })}
|
||||||
required
|
required
|
||||||
rows={6}
|
rows={6}
|
||||||
className="w-full px-3 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-[#d4af37] focus:border-transparent"
|
className="w-full px-3 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-[var(--luxury-gold)] focus:border-transparent"
|
||||||
placeholder="Please provide detailed information about your complaint..."
|
placeholder="Please provide detailed information about your complaint..."
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
@@ -361,7 +361,7 @@ const CreateComplaintModal: React.FC<{
|
|||||||
<button
|
<button
|
||||||
type="submit"
|
type="submit"
|
||||||
disabled={submitting}
|
disabled={submitting}
|
||||||
className="px-4 py-2 bg-[#d4af37] text-white rounded-lg hover:bg-[#c9a227] disabled:opacity-50 disabled:cursor-not-allowed"
|
className="px-4 py-2 bg-[var(--luxury-gold)] text-white rounded-lg hover:bg-[var(--luxury-gold-dark)] disabled:opacity-50 disabled:cursor-not-allowed"
|
||||||
>
|
>
|
||||||
{submitting ? 'Submitting...' : 'Submit Complaint'}
|
{submitting ? 'Submitting...' : 'Submit Complaint'}
|
||||||
</button>
|
</button>
|
||||||
|
|||||||
@@ -231,11 +231,11 @@ const FullPaymentPage: React.FC = () => {
|
|||||||
<Link
|
<Link
|
||||||
to="/bookings"
|
to="/bookings"
|
||||||
className="inline-flex items-center gap-2
|
className="inline-flex items-center gap-2
|
||||||
bg-gradient-to-r from-[#d4af37] to-[#c9a227]
|
bg-gradient-to-r from-[var(--luxury-gold)] to-[var(--luxury-gold-dark)]
|
||||||
text-[#0f0f0f] px-6 py-3 rounded-sm
|
text-[#0f0f0f] px-6 py-3 rounded-sm
|
||||||
hover:from-[#f5d76e] hover:to-[#d4af37]
|
hover:from-[var(--luxury-gold-light)] hover:to-[var(--luxury-gold)]
|
||||||
transition-all duration-300 font-medium
|
transition-all duration-300 font-medium
|
||||||
tracking-wide shadow-lg shadow-[#d4af37]/30"
|
tracking-wide shadow-lg shadow-[var(--luxury-gold)]/30"
|
||||||
>
|
>
|
||||||
<ArrowLeft className="w-4 h-4" />
|
<ArrowLeft className="w-4 h-4" />
|
||||||
Back to booking list
|
Back to booking list
|
||||||
@@ -270,7 +270,7 @@ const FullPaymentPage: React.FC = () => {
|
|||||||
<Link
|
<Link
|
||||||
to={`/bookings/${bookingId}`}
|
to={`/bookings/${bookingId}`}
|
||||||
className="inline-flex items-center gap-2
|
className="inline-flex items-center gap-2
|
||||||
text-[#d4af37]/80 hover:text-[#d4af37]
|
text-[var(--luxury-gold)]/80 hover:text-[var(--luxury-gold)]
|
||||||
mb-6 transition-all duration-300
|
mb-6 transition-all duration-300
|
||||||
group font-light tracking-wide"
|
group font-light tracking-wide"
|
||||||
>
|
>
|
||||||
@@ -307,19 +307,19 @@ const FullPaymentPage: React.FC = () => {
|
|||||||
{}
|
{}
|
||||||
{!isPaymentCompleted && (
|
{!isPaymentCompleted && (
|
||||||
<div
|
<div
|
||||||
className="bg-gradient-to-br from-[#d4af37]/10 to-[#c9a227]/5
|
className="bg-gradient-to-br from-[var(--luxury-gold)]/10 to-[var(--luxury-gold-dark)]/5
|
||||||
border-2 border-[#d4af37]/30 rounded-xl p-6 mb-6
|
border-2 border-[var(--luxury-gold)]/30 rounded-xl p-6 mb-6
|
||||||
backdrop-blur-xl shadow-2xl shadow-[#d4af37]/10"
|
backdrop-blur-xl shadow-2xl shadow-[var(--luxury-gold)]/10"
|
||||||
>
|
>
|
||||||
<div className="flex items-center gap-4">
|
<div className="flex items-center gap-4">
|
||||||
<div
|
<div
|
||||||
className="w-16 h-16 bg-[#d4af37]/20 rounded-full
|
className="w-16 h-16 bg-[var(--luxury-gold)]/20 rounded-full
|
||||||
flex items-center justify-center border border-[#d4af37]/30"
|
flex items-center justify-center border border-[var(--luxury-gold)]/30"
|
||||||
>
|
>
|
||||||
<CreditCard className="w-10 h-10 text-[#d4af37]" />
|
<CreditCard className="w-10 h-10 text-[var(--luxury-gold)]" />
|
||||||
</div>
|
</div>
|
||||||
<div className="flex-1">
|
<div className="flex-1">
|
||||||
<h1 className="text-2xl font-serif font-bold text-[#d4af37] mb-1 tracking-wide">
|
<h1 className="text-2xl font-serif font-bold text-[var(--luxury-gold)] mb-1 tracking-wide">
|
||||||
Complete Payment
|
Complete Payment
|
||||||
</h1>
|
</h1>
|
||||||
<p className="text-gray-300 font-light tracking-wide">
|
<p className="text-gray-300 font-light tracking-wide">
|
||||||
@@ -336,11 +336,11 @@ const FullPaymentPage: React.FC = () => {
|
|||||||
{}
|
{}
|
||||||
<div
|
<div
|
||||||
className="bg-gradient-to-br from-[#1a1a1a] to-[#0a0a0a]
|
className="bg-gradient-to-br from-[#1a1a1a] to-[#0a0a0a]
|
||||||
rounded-xl border border-[#d4af37]/20
|
rounded-xl border border-[var(--luxury-gold)]/20
|
||||||
backdrop-blur-xl shadow-2xl shadow-[#d4af37]/5 p-6"
|
backdrop-blur-xl shadow-2xl shadow-[var(--luxury-gold)]/5 p-6"
|
||||||
>
|
>
|
||||||
<h2 className="text-xl font-serif font-semibold text-[#d4af37] mb-4 flex items-center gap-2">
|
<h2 className="text-xl font-serif font-semibold text-[var(--luxury-gold)] mb-4 flex items-center gap-2">
|
||||||
<div className="w-1 h-6 bg-gradient-to-b from-[#d4af37] to-[#c9a227]" />
|
<div className="w-1 h-6 bg-gradient-to-b from-[var(--luxury-gold)] to-[var(--luxury-gold-dark)]" />
|
||||||
Payment Information
|
Payment Information
|
||||||
</h2>
|
</h2>
|
||||||
|
|
||||||
@@ -353,8 +353,8 @@ const FullPaymentPage: React.FC = () => {
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div
|
<div
|
||||||
className="flex justify-between border-t border-[#d4af37]/20 pt-3
|
className="flex justify-between border-t border-[var(--luxury-gold)]/20 pt-3
|
||||||
text-[#d4af37]"
|
text-[var(--luxury-gold)]"
|
||||||
>
|
>
|
||||||
<span className="font-medium">
|
<span className="font-medium">
|
||||||
Amount to Pay
|
Amount to Pay
|
||||||
@@ -386,11 +386,11 @@ const FullPaymentPage: React.FC = () => {
|
|||||||
{!isPaymentCompleted && booking && stripePayment && (
|
{!isPaymentCompleted && booking && stripePayment && (
|
||||||
<div
|
<div
|
||||||
className="bg-gradient-to-br from-[#1a1a1a] to-[#0a0a0a]
|
className="bg-gradient-to-br from-[#1a1a1a] to-[#0a0a0a]
|
||||||
rounded-xl border border-[#d4af37]/20
|
rounded-xl border border-[var(--luxury-gold)]/20
|
||||||
backdrop-blur-xl shadow-2xl shadow-[#d4af37]/5 p-6"
|
backdrop-blur-xl shadow-2xl shadow-[var(--luxury-gold)]/5 p-6"
|
||||||
>
|
>
|
||||||
<h2 className="text-xl font-serif font-semibold text-[#d4af37] mb-4 flex items-center gap-2">
|
<h2 className="text-xl font-serif font-semibold text-[var(--luxury-gold)] mb-4 flex items-center gap-2">
|
||||||
<div className="w-1 h-6 bg-gradient-to-b from-[#d4af37] to-[#c9a227]" />
|
<div className="w-1 h-6 bg-gradient-to-b from-[var(--luxury-gold)] to-[var(--luxury-gold-dark)]" />
|
||||||
<CreditCard className="w-5 h-5" />
|
<CreditCard className="w-5 h-5" />
|
||||||
Card Payment
|
Card Payment
|
||||||
</h2>
|
</h2>
|
||||||
@@ -406,11 +406,11 @@ const FullPaymentPage: React.FC = () => {
|
|||||||
</p>
|
</p>
|
||||||
<button
|
<button
|
||||||
onClick={() => navigate(`/bookings/${booking.id}`)}
|
onClick={() => navigate(`/bookings/${booking.id}`)}
|
||||||
className="bg-gradient-to-r from-[#d4af37] to-[#c9a227]
|
className="bg-gradient-to-r from-[var(--luxury-gold)] to-[var(--luxury-gold-dark)]
|
||||||
text-[#0f0f0f] px-6 py-3 rounded-sm
|
text-[#0f0f0f] px-6 py-3 rounded-sm
|
||||||
hover:from-[#f5d76e] hover:to-[#d4af37]
|
hover:from-[var(--luxury-gold-light)] hover:to-[var(--luxury-gold)]
|
||||||
transition-all duration-300 font-medium
|
transition-all duration-300 font-medium
|
||||||
tracking-wide shadow-lg shadow-[#d4af37]/30"
|
tracking-wide shadow-lg shadow-[var(--luxury-gold)]/30"
|
||||||
>
|
>
|
||||||
View Booking
|
View Booking
|
||||||
</button>
|
</button>
|
||||||
@@ -440,12 +440,12 @@ const FullPaymentPage: React.FC = () => {
|
|||||||
<div className="lg:col-span-1">
|
<div className="lg:col-span-1">
|
||||||
<div
|
<div
|
||||||
className="bg-gradient-to-br from-[#1a1a1a] to-[#0a0a0a]
|
className="bg-gradient-to-br from-[#1a1a1a] to-[#0a0a0a]
|
||||||
rounded-xl border border-[#d4af37]/20
|
rounded-xl border border-[var(--luxury-gold)]/20
|
||||||
backdrop-blur-xl shadow-2xl shadow-[#d4af37]/5 p-6
|
backdrop-blur-xl shadow-2xl shadow-[var(--luxury-gold)]/5 p-6
|
||||||
sticky top-6"
|
sticky top-6"
|
||||||
>
|
>
|
||||||
<h3 className="text-lg font-serif font-semibold text-[#d4af37] mb-4 flex items-center gap-2">
|
<h3 className="text-lg font-serif font-semibold text-[var(--luxury-gold)] mb-4 flex items-center gap-2">
|
||||||
<div className="w-1 h-5 bg-gradient-to-b from-[#d4af37] to-[#c9a227]" />
|
<div className="w-1 h-5 bg-gradient-to-b from-[var(--luxury-gold)] to-[var(--luxury-gold-dark)]" />
|
||||||
Booking Summary
|
Booking Summary
|
||||||
</h3>
|
</h3>
|
||||||
|
|
||||||
@@ -476,9 +476,9 @@ const FullPaymentPage: React.FC = () => {
|
|||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="pt-3 border-t border-[#d4af37]/20">
|
<div className="pt-3 border-t border-[var(--luxury-gold)]/20">
|
||||||
<span className="text-gray-400 font-light">Total Amount</span>
|
<span className="text-gray-400 font-light">Total Amount</span>
|
||||||
<p className="text-xl font-bold text-[#d4af37]">
|
<p className="text-xl font-bold text-[var(--luxury-gold)]">
|
||||||
{formatPrice(booking.total_price)}
|
{formatPrice(booking.total_price)}
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
@@ -486,7 +486,7 @@ const FullPaymentPage: React.FC = () => {
|
|||||||
|
|
||||||
{/* PCI DSS Compliance Trust Badge */}
|
{/* PCI DSS Compliance Trust Badge */}
|
||||||
{!isPaymentCompleted && (
|
{!isPaymentCompleted && (
|
||||||
<div className="mt-6 pt-6 border-t border-[#d4af37]/20">
|
<div className="mt-6 pt-6 border-t border-[var(--luxury-gold)]/20">
|
||||||
<div className="bg-gradient-to-r from-blue-900/20 to-indigo-900/20 border border-blue-500/30 rounded-lg p-4">
|
<div className="bg-gradient-to-r from-blue-900/20 to-indigo-900/20 border border-blue-500/30 rounded-lg p-4">
|
||||||
<div className="flex items-start gap-3">
|
<div className="flex items-start gap-3">
|
||||||
<div className="flex-shrink-0">
|
<div className="flex-shrink-0">
|
||||||
|
|||||||
@@ -195,7 +195,7 @@ const GuestRequestsPage: React.FC = () => {
|
|||||||
}
|
}
|
||||||
setShowCreateModal(true);
|
setShowCreateModal(true);
|
||||||
}}
|
}}
|
||||||
className="flex items-center space-x-2 px-6 py-3 bg-gradient-to-r from-[#d4af37] to-[#c9a227] text-white rounded-lg hover:from-[#f5d76e] hover:to-[#d4af37] transition-all duration-300 shadow-lg hover:shadow-xl font-medium"
|
className="flex items-center space-x-2 px-6 py-3 bg-gradient-to-r from-[var(--luxury-gold)] to-[var(--luxury-gold-dark)] text-white rounded-lg hover:from-[var(--luxury-gold-light)] hover:to-[var(--luxury-gold)] transition-all duration-300 shadow-lg hover:shadow-xl font-medium"
|
||||||
>
|
>
|
||||||
<Plus className="w-5 h-5" />
|
<Plus className="w-5 h-5" />
|
||||||
<span>New Request</span>
|
<span>New Request</span>
|
||||||
@@ -209,7 +209,7 @@ const GuestRequestsPage: React.FC = () => {
|
|||||||
<select
|
<select
|
||||||
value={filterStatus}
|
value={filterStatus}
|
||||||
onChange={(e) => setFilterStatus(e.target.value)}
|
onChange={(e) => setFilterStatus(e.target.value)}
|
||||||
className="border border-gray-300 rounded-lg px-4 py-2 text-sm focus:outline-none focus:ring-2 focus:ring-[#d4af37]"
|
className="border border-gray-300 rounded-lg px-4 py-2 text-sm focus:outline-none focus:ring-2 focus:ring-[var(--luxury-gold)]"
|
||||||
>
|
>
|
||||||
<option value="">All Statuses</option>
|
<option value="">All Statuses</option>
|
||||||
<option value="pending">Pending</option>
|
<option value="pending">Pending</option>
|
||||||
@@ -344,7 +344,7 @@ const GuestRequestsPage: React.FC = () => {
|
|||||||
});
|
});
|
||||||
setSelectedBooking(booking);
|
setSelectedBooking(booking);
|
||||||
}}
|
}}
|
||||||
className="w-full border border-gray-300 rounded-lg px-3 py-2 text-sm focus:outline-none focus:ring-2 focus:ring-[#d4af37]"
|
className="w-full border border-gray-300 rounded-lg px-3 py-2 text-sm focus:outline-none focus:ring-2 focus:ring-[var(--luxury-gold)]"
|
||||||
required
|
required
|
||||||
>
|
>
|
||||||
<option value="">Select Booking</option>
|
<option value="">Select Booking</option>
|
||||||
@@ -363,7 +363,7 @@ const GuestRequestsPage: React.FC = () => {
|
|||||||
<select
|
<select
|
||||||
value={requestForm.request_type}
|
value={requestForm.request_type}
|
||||||
onChange={(e) => setRequestForm({ ...requestForm, request_type: e.target.value })}
|
onChange={(e) => setRequestForm({ ...requestForm, request_type: e.target.value })}
|
||||||
className="w-full border border-gray-300 rounded-lg px-3 py-2 text-sm focus:outline-none focus:ring-2 focus:ring-[#d4af37]"
|
className="w-full border border-gray-300 rounded-lg px-3 py-2 text-sm focus:outline-none focus:ring-2 focus:ring-[var(--luxury-gold)]"
|
||||||
required
|
required
|
||||||
>
|
>
|
||||||
{requestTypes.map((type) => (
|
{requestTypes.map((type) => (
|
||||||
@@ -383,7 +383,7 @@ const GuestRequestsPage: React.FC = () => {
|
|||||||
value={requestForm.title}
|
value={requestForm.title}
|
||||||
onChange={(e) => setRequestForm({ ...requestForm, title: e.target.value })}
|
onChange={(e) => setRequestForm({ ...requestForm, title: e.target.value })}
|
||||||
placeholder="Brief description of your request"
|
placeholder="Brief description of your request"
|
||||||
className="w-full border border-gray-300 rounded-lg px-3 py-2 text-sm focus:outline-none focus:ring-2 focus:ring-[#d4af37]"
|
className="w-full border border-gray-300 rounded-lg px-3 py-2 text-sm focus:outline-none focus:ring-2 focus:ring-[var(--luxury-gold)]"
|
||||||
required
|
required
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
@@ -397,7 +397,7 @@ const GuestRequestsPage: React.FC = () => {
|
|||||||
onChange={(e) => setRequestForm({ ...requestForm, description: e.target.value })}
|
onChange={(e) => setRequestForm({ ...requestForm, description: e.target.value })}
|
||||||
placeholder="Additional details about your request"
|
placeholder="Additional details about your request"
|
||||||
rows={4}
|
rows={4}
|
||||||
className="w-full border border-gray-300 rounded-lg px-3 py-2 text-sm focus:outline-none focus:ring-2 focus:ring-[#d4af37]"
|
className="w-full border border-gray-300 rounded-lg px-3 py-2 text-sm focus:outline-none focus:ring-2 focus:ring-[var(--luxury-gold)]"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@@ -408,7 +408,7 @@ const GuestRequestsPage: React.FC = () => {
|
|||||||
<select
|
<select
|
||||||
value={requestForm.priority}
|
value={requestForm.priority}
|
||||||
onChange={(e) => setRequestForm({ ...requestForm, priority: e.target.value })}
|
onChange={(e) => setRequestForm({ ...requestForm, priority: e.target.value })}
|
||||||
className="w-full border border-gray-300 rounded-lg px-3 py-2 text-sm focus:outline-none focus:ring-2 focus:ring-[#d4af37]"
|
className="w-full border border-gray-300 rounded-lg px-3 py-2 text-sm focus:outline-none focus:ring-2 focus:ring-[var(--luxury-gold)]"
|
||||||
>
|
>
|
||||||
<option value="low">Low</option>
|
<option value="low">Low</option>
|
||||||
<option value="normal">Normal</option>
|
<option value="normal">Normal</option>
|
||||||
@@ -426,7 +426,7 @@ const GuestRequestsPage: React.FC = () => {
|
|||||||
onChange={(e) => setRequestForm({ ...requestForm, guest_notes: e.target.value })}
|
onChange={(e) => setRequestForm({ ...requestForm, guest_notes: e.target.value })}
|
||||||
placeholder="Any special instructions or preferences"
|
placeholder="Any special instructions or preferences"
|
||||||
rows={3}
|
rows={3}
|
||||||
className="w-full border border-gray-300 rounded-lg px-3 py-2 text-sm focus:outline-none focus:ring-2 focus:ring-[#d4af37]"
|
className="w-full border border-gray-300 rounded-lg px-3 py-2 text-sm focus:outline-none focus:ring-2 focus:ring-[var(--luxury-gold)]"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@@ -439,7 +439,7 @@ const GuestRequestsPage: React.FC = () => {
|
|||||||
</button>
|
</button>
|
||||||
<button
|
<button
|
||||||
onClick={handleCreateRequest}
|
onClick={handleCreateRequest}
|
||||||
className="px-4 py-2 text-sm text-white bg-gradient-to-r from-[#d4af37] to-[#c9a227] rounded-lg hover:from-[#f5d76e] hover:to-[#d4af37] transition-colors font-medium"
|
className="px-4 py-2 text-sm text-white bg-gradient-to-r from-[var(--luxury-gold)] to-[var(--luxury-gold-dark)] rounded-lg hover:from-[var(--luxury-gold-light)] hover:to-[var(--luxury-gold)] transition-colors font-medium"
|
||||||
>
|
>
|
||||||
Submit Request
|
Submit Request
|
||||||
</button>
|
</button>
|
||||||
|
|||||||
@@ -91,11 +91,11 @@ const PayPalCancelPage: React.FC = () => {
|
|||||||
{bookingId && (
|
{bookingId && (
|
||||||
<button
|
<button
|
||||||
onClick={() => navigate(`/bookings/${bookingId}`)}
|
onClick={() => navigate(`/bookings/${bookingId}`)}
|
||||||
className="flex-1 bg-gradient-to-r from-[#d4af37] to-[#c9a227]
|
className="flex-1 bg-gradient-to-r from-[var(--luxury-gold)] to-[var(--luxury-gold-dark)]
|
||||||
text-[#0f0f0f] px-4 py-2 sm:px-6 sm:py-3 rounded-sm
|
text-[#0f0f0f] px-4 py-2 sm:px-6 sm:py-3 rounded-sm
|
||||||
hover:from-[#f5d76e] hover:to-[#d4af37]
|
hover:from-[var(--luxury-gold-light)] hover:to-[var(--luxury-gold)]
|
||||||
transition-all duration-300 font-medium tracking-wide
|
transition-all duration-300 font-medium tracking-wide
|
||||||
shadow-lg shadow-[#d4af37]/30 flex items-center justify-center gap-2
|
shadow-lg shadow-[var(--luxury-gold)]/30 flex items-center justify-center gap-2
|
||||||
disabled:opacity-50 disabled:cursor-not-allowed text-sm sm:text-base"
|
disabled:opacity-50 disabled:cursor-not-allowed text-sm sm:text-base"
|
||||||
disabled={cancelling}
|
disabled={cancelling}
|
||||||
>
|
>
|
||||||
@@ -107,7 +107,7 @@ const PayPalCancelPage: React.FC = () => {
|
|||||||
onClick={() => navigate('/bookings')}
|
onClick={() => navigate('/bookings')}
|
||||||
className="flex-1 bg-gradient-to-br from-gray-800/40 to-gray-700/20
|
className="flex-1 bg-gradient-to-br from-gray-800/40 to-gray-700/20
|
||||||
border border-gray-600/30 text-gray-300 px-4 py-2 sm:px-6 sm:py-3 rounded-sm
|
border border-gray-600/30 text-gray-300 px-4 py-2 sm:px-6 sm:py-3 rounded-sm
|
||||||
hover:border-[#d4af37]/50 hover:text-[#d4af37]
|
hover:border-[var(--luxury-gold)]/50 hover:text-[var(--luxury-gold)]
|
||||||
transition-all duration-300 font-light tracking-wide
|
transition-all duration-300 font-light tracking-wide
|
||||||
backdrop-blur-sm text-sm sm:text-base"
|
backdrop-blur-sm text-sm sm:text-base"
|
||||||
>
|
>
|
||||||
|
|||||||
@@ -82,10 +82,10 @@ const PayPalReturnPage: React.FC = () => {
|
|||||||
<div className="min-h-screen bg-gradient-to-b from-[#0f0f0f] via-[#1a1a1a] to-[#0f0f0f]
|
<div className="min-h-screen bg-gradient-to-b from-[#0f0f0f] via-[#1a1a1a] to-[#0f0f0f]
|
||||||
flex items-center justify-center py-8 sm:py-12 px-4 sm:px-6 lg:px-8">
|
flex items-center justify-center py-8 sm:py-12 px-4 sm:px-6 lg:px-8">
|
||||||
<div className="text-center w-full max-w-2xl mx-auto">
|
<div className="text-center w-full max-w-2xl mx-auto">
|
||||||
<div className="w-16 h-16 sm:w-20 sm:h-20 bg-gradient-to-br from-[#d4af37]/20 to-[#f5d76e]/20
|
<div className="w-16 h-16 sm:w-20 sm:h-20 bg-gradient-to-br from-[var(--luxury-gold)]/20 to-[var(--luxury-gold-light)]/20
|
||||||
rounded-full flex items-center justify-center mx-auto mb-4 sm:mb-6
|
rounded-full flex items-center justify-center mx-auto mb-4 sm:mb-6
|
||||||
border border-[#d4af37]/30 shadow-lg shadow-[#d4af37]/20">
|
border border-[var(--luxury-gold)]/30 shadow-lg shadow-[var(--luxury-gold)]/20">
|
||||||
<Loader2 className="w-10 h-10 sm:w-12 sm:h-12 text-[#d4af37] animate-spin" />
|
<Loader2 className="w-10 h-10 sm:w-12 sm:h-12 text-[var(--luxury-gold)] animate-spin" />
|
||||||
</div>
|
</div>
|
||||||
<p className="text-gray-300/80 font-light text-base sm:text-lg tracking-wide">
|
<p className="text-gray-300/80 font-light text-base sm:text-lg tracking-wide">
|
||||||
Processing your payment...
|
Processing your payment...
|
||||||
@@ -115,11 +115,11 @@ const PayPalReturnPage: React.FC = () => {
|
|||||||
</p>
|
</p>
|
||||||
<button
|
<button
|
||||||
onClick={() => navigate(`/bookings/${bookingId}`)}
|
onClick={() => navigate(`/bookings/${bookingId}`)}
|
||||||
className="bg-gradient-to-r from-[#d4af37] to-[#c9a227]
|
className="bg-gradient-to-r from-[var(--luxury-gold)] to-[var(--luxury-gold-dark)]
|
||||||
text-[#0f0f0f] px-6 py-2 sm:px-8 sm:py-3 rounded-sm
|
text-[#0f0f0f] px-6 py-2 sm:px-8 sm:py-3 rounded-sm
|
||||||
hover:from-[#f5d76e] hover:to-[#d4af37]
|
hover:from-[var(--luxury-gold-light)] hover:to-[var(--luxury-gold)]
|
||||||
transition-all duration-300 font-medium tracking-wide
|
transition-all duration-300 font-medium tracking-wide
|
||||||
shadow-lg shadow-[#d4af37]/30 text-sm sm:text-base w-full sm:w-auto"
|
shadow-lg shadow-[var(--luxury-gold)]/30 text-sm sm:text-base w-full sm:w-auto"
|
||||||
>
|
>
|
||||||
View Booking
|
View Booking
|
||||||
</button>
|
</button>
|
||||||
@@ -149,11 +149,11 @@ const PayPalReturnPage: React.FC = () => {
|
|||||||
{bookingId && (
|
{bookingId && (
|
||||||
<button
|
<button
|
||||||
onClick={() => navigate(`/bookings/${bookingId}`)}
|
onClick={() => navigate(`/bookings/${bookingId}`)}
|
||||||
className="flex-1 bg-gradient-to-r from-[#d4af37] to-[#c9a227]
|
className="flex-1 bg-gradient-to-r from-[var(--luxury-gold)] to-[var(--luxury-gold-dark)]
|
||||||
text-[#0f0f0f] px-4 py-2 sm:px-6 sm:py-3 rounded-sm
|
text-[#0f0f0f] px-4 py-2 sm:px-6 sm:py-3 rounded-sm
|
||||||
hover:from-[#f5d76e] hover:to-[#d4af37]
|
hover:from-[var(--luxury-gold-light)] hover:to-[var(--luxury-gold)]
|
||||||
transition-all duration-300 font-medium tracking-wide
|
transition-all duration-300 font-medium tracking-wide
|
||||||
shadow-lg shadow-[#d4af37]/30 text-sm sm:text-base"
|
shadow-lg shadow-[var(--luxury-gold)]/30 text-sm sm:text-base"
|
||||||
>
|
>
|
||||||
View Booking
|
View Booking
|
||||||
</button>
|
</button>
|
||||||
@@ -162,7 +162,7 @@ const PayPalReturnPage: React.FC = () => {
|
|||||||
onClick={() => navigate('/bookings')}
|
onClick={() => navigate('/bookings')}
|
||||||
className="flex-1 bg-gradient-to-br from-gray-800/40 to-gray-700/20
|
className="flex-1 bg-gradient-to-br from-gray-800/40 to-gray-700/20
|
||||||
border border-gray-600/30 text-gray-300 px-4 py-2 sm:px-6 sm:py-3 rounded-sm
|
border border-gray-600/30 text-gray-300 px-4 py-2 sm:px-6 sm:py-3 rounded-sm
|
||||||
hover:border-[#d4af37]/50 hover:text-[#d4af37]
|
hover:border-[var(--luxury-gold)]/50 hover:text-[var(--luxury-gold)]
|
||||||
transition-all duration-300 font-light tracking-wide
|
transition-all duration-300 font-light tracking-wide
|
||||||
backdrop-blur-sm text-sm sm:text-base"
|
backdrop-blur-sm text-sm sm:text-base"
|
||||||
>
|
>
|
||||||
|
|||||||
@@ -417,7 +417,7 @@ const ProfilePage: React.FC = () => {
|
|||||||
{}
|
{}
|
||||||
<div className="mb-6 sm:mb-8 animate-fade-in">
|
<div className="mb-6 sm:mb-8 animate-fade-in">
|
||||||
<div className="flex items-center gap-2 sm:gap-3 mb-2 sm:mb-3">
|
<div className="flex items-center gap-2 sm:gap-3 mb-2 sm:mb-3">
|
||||||
<div className="h-1 w-12 sm:w-16 md:w-20 bg-gradient-to-r from-[#d4af37] via-amber-400 to-[#d4af37] rounded-full"></div>
|
<div className="h-1 w-12 sm:w-16 md:w-20 bg-gradient-to-r from-[var(--luxury-gold)] via-amber-400 to-[var(--luxury-gold)] rounded-full"></div>
|
||||||
<h1 className="text-2xl sm:text-3xl lg:text-4xl font-serif font-bold bg-gradient-to-r from-gray-900 via-amber-900/90 to-gray-900 bg-clip-text text-transparent tracking-tight">
|
<h1 className="text-2xl sm:text-3xl lg:text-4xl font-serif font-bold bg-gradient-to-r from-gray-900 via-amber-900/90 to-gray-900 bg-clip-text text-transparent tracking-tight">
|
||||||
Profile & Settings
|
Profile & Settings
|
||||||
</h1>
|
</h1>
|
||||||
@@ -428,61 +428,61 @@ const ProfilePage: React.FC = () => {
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
{}
|
{}
|
||||||
<div className="mb-4 sm:mb-6 border-b border-[#d4af37]/20 overflow-x-auto bg-white/50 backdrop-blur-sm rounded-t-lg sm:rounded-t-xl px-4 sm:px-6">
|
<div className="mb-4 sm:mb-6 border-b border-[var(--luxury-gold)]/20 overflow-x-auto bg-white/50 backdrop-blur-sm rounded-t-lg sm:rounded-t-xl px-4 sm:px-6">
|
||||||
<div className="flex space-x-4 sm:space-x-8 min-w-max">
|
<div className="flex space-x-4 sm:space-x-8 min-w-max">
|
||||||
<button
|
<button
|
||||||
onClick={() => setActiveTab('profile')}
|
onClick={() => setActiveTab('profile')}
|
||||||
className={`py-3 sm:py-4 px-2 sm:px-1 border-b-2 font-semibold text-xs sm:text-sm transition-all duration-300 whitespace-nowrap ${
|
className={`py-3 sm:py-4 px-2 sm:px-1 border-b-2 font-semibold text-xs sm:text-sm transition-all duration-300 whitespace-nowrap ${
|
||||||
activeTab === 'profile'
|
activeTab === 'profile'
|
||||||
? 'border-[#d4af37] text-[#d4af37] shadow-sm'
|
? 'border-[var(--luxury-gold)] text-[var(--luxury-gold)] shadow-sm'
|
||||||
: 'border-transparent text-gray-500 hover:text-[#d4af37] hover:border-[#d4af37]/30'
|
: 'border-transparent text-gray-500 hover:text-[var(--luxury-gold)] hover:border-[var(--luxury-gold)]/30'
|
||||||
}`}
|
}`}
|
||||||
>
|
>
|
||||||
<User className={`w-4 h-4 sm:w-5 sm:h-5 inline mr-2 ${activeTab === 'profile' ? 'text-[#d4af37]' : ''}`} />
|
<User className={`w-4 h-4 sm:w-5 sm:h-5 inline mr-2 ${activeTab === 'profile' ? 'text-[var(--luxury-gold)]' : ''}`} />
|
||||||
Profile Information
|
Profile Information
|
||||||
</button>
|
</button>
|
||||||
<button
|
<button
|
||||||
onClick={() => setActiveTab('password')}
|
onClick={() => setActiveTab('password')}
|
||||||
className={`py-3 sm:py-4 px-2 sm:px-1 border-b-2 font-semibold text-xs sm:text-sm transition-all duration-300 whitespace-nowrap ${
|
className={`py-3 sm:py-4 px-2 sm:px-1 border-b-2 font-semibold text-xs sm:text-sm transition-all duration-300 whitespace-nowrap ${
|
||||||
activeTab === 'password'
|
activeTab === 'password'
|
||||||
? 'border-[#d4af37] text-[#d4af37] shadow-sm'
|
? 'border-[var(--luxury-gold)] text-[var(--luxury-gold)] shadow-sm'
|
||||||
: 'border-transparent text-gray-500 hover:text-[#d4af37] hover:border-[#d4af37]/30'
|
: 'border-transparent text-gray-500 hover:text-[var(--luxury-gold)] hover:border-[var(--luxury-gold)]/30'
|
||||||
}`}
|
}`}
|
||||||
>
|
>
|
||||||
<KeyRound className={`w-4 h-4 sm:w-5 sm:h-5 inline mr-2 ${activeTab === 'password' ? 'text-[#d4af37]' : ''}`} />
|
<KeyRound className={`w-4 h-4 sm:w-5 sm:h-5 inline mr-2 ${activeTab === 'password' ? 'text-[var(--luxury-gold)]' : ''}`} />
|
||||||
Change Password
|
Change Password
|
||||||
</button>
|
</button>
|
||||||
<button
|
<button
|
||||||
onClick={() => setActiveTab('mfa')}
|
onClick={() => setActiveTab('mfa')}
|
||||||
className={`py-3 sm:py-4 px-2 sm:px-1 border-b-2 font-semibold text-xs sm:text-sm transition-all duration-300 whitespace-nowrap ${
|
className={`py-3 sm:py-4 px-2 sm:px-1 border-b-2 font-semibold text-xs sm:text-sm transition-all duration-300 whitespace-nowrap ${
|
||||||
activeTab === 'mfa'
|
activeTab === 'mfa'
|
||||||
? 'border-[#d4af37] text-[#d4af37] shadow-sm'
|
? 'border-[var(--luxury-gold)] text-[var(--luxury-gold)] shadow-sm'
|
||||||
: 'border-transparent text-gray-500 hover:text-[#d4af37] hover:border-[#d4af37]/30'
|
: 'border-transparent text-gray-500 hover:text-[var(--luxury-gold)] hover:border-[var(--luxury-gold)]/30'
|
||||||
}`}
|
}`}
|
||||||
>
|
>
|
||||||
<Shield className={`w-4 h-4 sm:w-5 sm:h-5 inline mr-2 ${activeTab === 'mfa' ? 'text-[#d4af37]' : ''}`} />
|
<Shield className={`w-4 h-4 sm:w-5 sm:h-5 inline mr-2 ${activeTab === 'mfa' ? 'text-[var(--luxury-gold)]' : ''}`} />
|
||||||
Two-Factor Authentication
|
Two-Factor Authentication
|
||||||
</button>
|
</button>
|
||||||
<button
|
<button
|
||||||
onClick={() => setActiveTab('sessions')}
|
onClick={() => setActiveTab('sessions')}
|
||||||
className={`py-3 sm:py-4 px-2 sm:px-1 border-b-2 font-semibold text-xs sm:text-sm transition-all duration-300 whitespace-nowrap ${
|
className={`py-3 sm:py-4 px-2 sm:px-1 border-b-2 font-semibold text-xs sm:text-sm transition-all duration-300 whitespace-nowrap ${
|
||||||
activeTab === 'sessions'
|
activeTab === 'sessions'
|
||||||
? 'border-[#d4af37] text-[#d4af37] shadow-sm'
|
? 'border-[var(--luxury-gold)] text-[var(--luxury-gold)] shadow-sm'
|
||||||
: 'border-transparent text-gray-500 hover:text-[#d4af37] hover:border-[#d4af37]/30'
|
: 'border-transparent text-gray-500 hover:text-[var(--luxury-gold)] hover:border-[var(--luxury-gold)]/30'
|
||||||
}`}
|
}`}
|
||||||
>
|
>
|
||||||
<Monitor className={`w-4 h-4 sm:w-5 sm:h-5 inline mr-2 ${activeTab === 'sessions' ? 'text-[#d4af37]' : ''}`} />
|
<Monitor className={`w-4 h-4 sm:w-5 sm:h-5 inline mr-2 ${activeTab === 'sessions' ? 'text-[var(--luxury-gold)]' : ''}`} />
|
||||||
Active Sessions
|
Active Sessions
|
||||||
</button>
|
</button>
|
||||||
<button
|
<button
|
||||||
onClick={() => setActiveTab('gdpr')}
|
onClick={() => setActiveTab('gdpr')}
|
||||||
className={`py-3 sm:py-4 px-2 sm:px-1 border-b-2 font-semibold text-xs sm:text-sm transition-all duration-300 whitespace-nowrap ${
|
className={`py-3 sm:py-4 px-2 sm:px-1 border-b-2 font-semibold text-xs sm:text-sm transition-all duration-300 whitespace-nowrap ${
|
||||||
activeTab === 'gdpr'
|
activeTab === 'gdpr'
|
||||||
? 'border-[#d4af37] text-[#d4af37] shadow-sm'
|
? 'border-[var(--luxury-gold)] text-[var(--luxury-gold)] shadow-sm'
|
||||||
: 'border-transparent text-gray-500 hover:text-[#d4af37] hover:border-[#d4af37]/30'
|
: 'border-transparent text-gray-500 hover:text-[var(--luxury-gold)] hover:border-[var(--luxury-gold)]/30'
|
||||||
}`}
|
}`}
|
||||||
>
|
>
|
||||||
<Database className={`w-4 h-4 sm:w-5 sm:h-5 inline mr-2 ${activeTab === 'gdpr' ? 'text-[#d4af37]' : ''}`} />
|
<Database className={`w-4 h-4 sm:w-5 sm:h-5 inline mr-2 ${activeTab === 'gdpr' ? 'text-[var(--luxury-gold)]' : ''}`} />
|
||||||
Data Privacy
|
Data Privacy
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
@@ -490,7 +490,7 @@ const ProfilePage: React.FC = () => {
|
|||||||
|
|
||||||
{}
|
{}
|
||||||
{activeTab === 'profile' && (
|
{activeTab === 'profile' && (
|
||||||
<div className="luxury-glass rounded-sm p-4 sm:p-6 lg:p-8 border border-[#d4af37]/20 shadow-2xl animate-slide-up">
|
<div className="luxury-glass rounded-sm p-4 sm:p-6 lg:p-8 border border-[var(--luxury-gold)]/20 shadow-2xl animate-slide-up">
|
||||||
<form onSubmit={handleSubmitProfile(onSubmitProfile)} className="space-y-5 sm:space-y-6">
|
<form onSubmit={handleSubmitProfile(onSubmitProfile)} className="space-y-5 sm:space-y-6">
|
||||||
{}
|
{}
|
||||||
<div className="flex flex-col sm:flex-row items-center sm:items-start gap-4 sm:gap-6 pb-5 sm:pb-6 border-b border-gray-200">
|
<div className="flex flex-col sm:flex-row items-center sm:items-start gap-4 sm:gap-6 pb-5 sm:pb-6 border-b border-gray-200">
|
||||||
@@ -499,20 +499,20 @@ const ProfilePage: React.FC = () => {
|
|||||||
<img
|
<img
|
||||||
src={avatarPreview || normalizeImageUrl(userInfo?.avatar)}
|
src={avatarPreview || normalizeImageUrl(userInfo?.avatar)}
|
||||||
alt="Profile"
|
alt="Profile"
|
||||||
className="w-20 h-20 sm:w-24 sm:h-24 rounded-full object-cover ring-4 ring-[#d4af37]/20 shadow-lg"
|
className="w-20 h-20 sm:w-24 sm:h-24 rounded-full object-cover ring-4 ring-[var(--luxury-gold)]/20 shadow-lg"
|
||||||
onError={() => {
|
onError={() => {
|
||||||
// If image fails to load, show default avatar
|
// If image fails to load, show default avatar
|
||||||
setAvatarError(true);
|
setAvatarError(true);
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
) : (
|
) : (
|
||||||
<div className="w-20 h-20 sm:w-24 sm:h-24 rounded-full bg-gradient-to-br from-[#d4af37] to-[#c9a227] flex items-center justify-center ring-4 ring-[#d4af37]/20 shadow-lg">
|
<div className="w-20 h-20 sm:w-24 sm:h-24 rounded-full bg-gradient-to-br from-[var(--luxury-gold)] to-[var(--luxury-gold-dark)] flex items-center justify-center ring-4 ring-[var(--luxury-gold)]/20 shadow-lg">
|
||||||
<User className="w-10 h-10 sm:w-12 sm:h-12 text-white" />
|
<User className="w-10 h-10 sm:w-12 sm:h-12 text-white" />
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
<label
|
<label
|
||||||
htmlFor="avatar-upload"
|
htmlFor="avatar-upload"
|
||||||
className="absolute bottom-0 right-0 p-2 bg-gradient-to-br from-[#d4af37] to-[#c9a227] rounded-full cursor-pointer hover:from-[#f5d76e] hover:to-[#d4af37] transition-all duration-300 shadow-lg hover:shadow-xl hover:shadow-[#d4af37]/40 hover:-translate-y-0.5 active:translate-y-0"
|
className="absolute bottom-0 right-0 p-2 bg-gradient-to-br from-[var(--luxury-gold)] to-[var(--luxury-gold-dark)] rounded-full cursor-pointer hover:from-[var(--luxury-gold-light)] hover:to-[var(--luxury-gold)] transition-all duration-300 shadow-lg hover:shadow-xl hover:shadow-[var(--luxury-gold)]/40 hover:-translate-y-0.5 active:translate-y-0"
|
||||||
>
|
>
|
||||||
<Camera className="w-3.5 h-3.5 sm:w-4 sm:h-4 text-white" />
|
<Camera className="w-3.5 h-3.5 sm:w-4 sm:h-4 text-white" />
|
||||||
<input
|
<input
|
||||||
@@ -528,7 +528,7 @@ const ProfilePage: React.FC = () => {
|
|||||||
|
|
||||||
<div>
|
<div>
|
||||||
<label className="block text-sm sm:text-base font-medium text-gray-700 mb-2">
|
<label className="block text-sm sm:text-base font-medium text-gray-700 mb-2">
|
||||||
<User className="w-4 h-4 sm:w-5 sm:h-5 inline mr-2 text-[#d4af37]" />
|
<User className="w-4 h-4 sm:w-5 sm:h-5 inline mr-2 text-[var(--luxury-gold)]" />
|
||||||
Full Name
|
Full Name
|
||||||
<span className="text-red-500 ml-1">*</span>
|
<span className="text-red-500 ml-1">*</span>
|
||||||
</label>
|
</label>
|
||||||
@@ -551,7 +551,7 @@ const ProfilePage: React.FC = () => {
|
|||||||
{}
|
{}
|
||||||
<div>
|
<div>
|
||||||
<label className="block text-sm sm:text-base font-medium text-gray-700 mb-2">
|
<label className="block text-sm sm:text-base font-medium text-gray-700 mb-2">
|
||||||
<Mail className="w-4 h-4 sm:w-5 sm:h-5 inline mr-2 text-[#d4af37]" />
|
<Mail className="w-4 h-4 sm:w-5 sm:h-5 inline mr-2 text-[var(--luxury-gold)]" />
|
||||||
Email Address
|
Email Address
|
||||||
<span className="text-red-500 ml-1">*</span>
|
<span className="text-red-500 ml-1">*</span>
|
||||||
</label>
|
</label>
|
||||||
@@ -574,7 +574,7 @@ const ProfilePage: React.FC = () => {
|
|||||||
{}
|
{}
|
||||||
<div>
|
<div>
|
||||||
<label className="block text-sm sm:text-base font-medium text-gray-700 mb-2">
|
<label className="block text-sm sm:text-base font-medium text-gray-700 mb-2">
|
||||||
<Phone className="w-4 h-4 sm:w-5 sm:h-5 inline mr-2 text-[#d4af37]" />
|
<Phone className="w-4 h-4 sm:w-5 sm:h-5 inline mr-2 text-[var(--luxury-gold)]" />
|
||||||
Phone Number
|
Phone Number
|
||||||
<span className="text-red-500 ml-1">*</span>
|
<span className="text-red-500 ml-1">*</span>
|
||||||
</label>
|
</label>
|
||||||
@@ -611,7 +611,7 @@ const ProfilePage: React.FC = () => {
|
|||||||
|
|
||||||
{}
|
{}
|
||||||
{activeTab === 'password' && (
|
{activeTab === 'password' && (
|
||||||
<div className="luxury-glass rounded-sm p-4 sm:p-6 lg:p-8 border border-[#d4af37]/20 shadow-2xl animate-slide-up">
|
<div className="luxury-glass rounded-sm p-4 sm:p-6 lg:p-8 border border-[var(--luxury-gold)]/20 shadow-2xl animate-slide-up">
|
||||||
<form onSubmit={handleSubmitPassword(onSubmitPassword)} className="space-y-5 sm:space-y-6">
|
<form onSubmit={handleSubmitPassword(onSubmitPassword)} className="space-y-5 sm:space-y-6">
|
||||||
{}
|
{}
|
||||||
<div className="bg-gradient-to-r from-blue-50 to-indigo-50 border border-blue-200/60 rounded-sm p-4 sm:p-5">
|
<div className="bg-gradient-to-r from-blue-50 to-indigo-50 border border-blue-200/60 rounded-sm p-4 sm:p-5">
|
||||||
@@ -640,7 +640,7 @@ const ProfilePage: React.FC = () => {
|
|||||||
{}
|
{}
|
||||||
<div>
|
<div>
|
||||||
<label className="block text-sm sm:text-base font-medium text-gray-700 mb-2">
|
<label className="block text-sm sm:text-base font-medium text-gray-700 mb-2">
|
||||||
<Lock className="w-4 h-4 sm:w-5 sm:h-5 inline mr-2 text-[#d4af37]" />
|
<Lock className="w-4 h-4 sm:w-5 sm:h-5 inline mr-2 text-[var(--luxury-gold)]" />
|
||||||
Current Password
|
Current Password
|
||||||
<span className="text-red-500 ml-1">*</span>
|
<span className="text-red-500 ml-1">*</span>
|
||||||
</label>
|
</label>
|
||||||
@@ -656,7 +656,7 @@ const ProfilePage: React.FC = () => {
|
|||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
onClick={() => setShowPassword({ ...showPassword, current: !showPassword.current })}
|
onClick={() => setShowPassword({ ...showPassword, current: !showPassword.current })}
|
||||||
className="absolute inset-y-0 right-0 pr-3 flex items-center text-gray-400 hover:text-[#d4af37] transition-colors"
|
className="absolute inset-y-0 right-0 pr-3 flex items-center text-gray-400 hover:text-[var(--luxury-gold)] transition-colors"
|
||||||
>
|
>
|
||||||
{showPassword.current ? (
|
{showPassword.current ? (
|
||||||
<EyeOff className="w-4 h-4 sm:w-5 sm:h-5" />
|
<EyeOff className="w-4 h-4 sm:w-5 sm:h-5" />
|
||||||
@@ -676,7 +676,7 @@ const ProfilePage: React.FC = () => {
|
|||||||
{}
|
{}
|
||||||
<div>
|
<div>
|
||||||
<label className="block text-sm sm:text-base font-medium text-gray-700 mb-2">
|
<label className="block text-sm sm:text-base font-medium text-gray-700 mb-2">
|
||||||
<Lock className="w-4 h-4 sm:w-5 sm:h-5 inline mr-2 text-[#d4af37]" />
|
<Lock className="w-4 h-4 sm:w-5 sm:h-5 inline mr-2 text-[var(--luxury-gold)]" />
|
||||||
New Password
|
New Password
|
||||||
<span className="text-red-500 ml-1">*</span>
|
<span className="text-red-500 ml-1">*</span>
|
||||||
</label>
|
</label>
|
||||||
@@ -692,7 +692,7 @@ const ProfilePage: React.FC = () => {
|
|||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
onClick={() => setShowPassword({ ...showPassword, new: !showPassword.new })}
|
onClick={() => setShowPassword({ ...showPassword, new: !showPassword.new })}
|
||||||
className="absolute inset-y-0 right-0 pr-3 flex items-center text-gray-400 hover:text-[#d4af37] transition-colors"
|
className="absolute inset-y-0 right-0 pr-3 flex items-center text-gray-400 hover:text-[var(--luxury-gold)] transition-colors"
|
||||||
>
|
>
|
||||||
{showPassword.new ? (
|
{showPassword.new ? (
|
||||||
<EyeOff className="w-4 h-4 sm:w-5 sm:h-5" />
|
<EyeOff className="w-4 h-4 sm:w-5 sm:h-5" />
|
||||||
@@ -712,7 +712,7 @@ const ProfilePage: React.FC = () => {
|
|||||||
{}
|
{}
|
||||||
<div>
|
<div>
|
||||||
<label className="block text-sm sm:text-base font-medium text-gray-700 mb-2">
|
<label className="block text-sm sm:text-base font-medium text-gray-700 mb-2">
|
||||||
<Lock className="w-4 h-4 sm:w-5 sm:h-5 inline mr-2 text-[#d4af37]" />
|
<Lock className="w-4 h-4 sm:w-5 sm:h-5 inline mr-2 text-[var(--luxury-gold)]" />
|
||||||
Confirm New Password
|
Confirm New Password
|
||||||
<span className="text-red-500 ml-1">*</span>
|
<span className="text-red-500 ml-1">*</span>
|
||||||
</label>
|
</label>
|
||||||
@@ -728,7 +728,7 @@ const ProfilePage: React.FC = () => {
|
|||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
onClick={() => setShowPassword({ ...showPassword, confirm: !showPassword.confirm })}
|
onClick={() => setShowPassword({ ...showPassword, confirm: !showPassword.confirm })}
|
||||||
className="absolute inset-y-0 right-0 pr-3 flex items-center text-gray-400 hover:text-[#d4af37] transition-colors"
|
className="absolute inset-y-0 right-0 pr-3 flex items-center text-gray-400 hover:text-[var(--luxury-gold)] transition-colors"
|
||||||
>
|
>
|
||||||
{showPassword.confirm ? (
|
{showPassword.confirm ? (
|
||||||
<EyeOff className="w-4 h-4 sm:w-5 sm:h-5" />
|
<EyeOff className="w-4 h-4 sm:w-5 sm:h-5" />
|
||||||
@@ -762,11 +762,11 @@ const ProfilePage: React.FC = () => {
|
|||||||
|
|
||||||
{}
|
{}
|
||||||
{activeTab === 'mfa' && (
|
{activeTab === 'mfa' && (
|
||||||
<div className="luxury-glass rounded-sm p-4 sm:p-6 lg:p-8 border border-[#d4af37]/20 shadow-2xl animate-slide-up space-y-5 sm:space-y-6">
|
<div className="luxury-glass rounded-sm p-4 sm:p-6 lg:p-8 border border-[var(--luxury-gold)]/20 shadow-2xl animate-slide-up space-y-5 sm:space-y-6">
|
||||||
{}
|
{}
|
||||||
<div>
|
<div>
|
||||||
<h2 className="text-xl sm:text-2xl font-serif font-semibold text-gray-900 mb-2 flex items-center gap-2">
|
<h2 className="text-xl sm:text-2xl font-serif font-semibold text-gray-900 mb-2 flex items-center gap-2">
|
||||||
<Shield className="w-5 h-5 sm:w-6 sm:h-6 text-[#d4af37]" />
|
<Shield className="w-5 h-5 sm:w-6 sm:h-6 text-[var(--luxury-gold)]" />
|
||||||
Two-Factor Authentication
|
Two-Factor Authentication
|
||||||
</h2>
|
</h2>
|
||||||
<p className="text-xs sm:text-sm text-gray-600 font-light leading-relaxed">
|
<p className="text-xs sm:text-sm text-gray-600 font-light leading-relaxed">
|
||||||
@@ -838,7 +838,7 @@ const ProfilePage: React.FC = () => {
|
|||||||
{}
|
{}
|
||||||
<div className="bg-white border border-gray-200 rounded-sm p-4 sm:p-5">
|
<div className="bg-white border border-gray-200 rounded-sm p-4 sm:p-5">
|
||||||
<h3 className="text-sm sm:text-base font-semibold text-gray-900 mb-2 flex items-center gap-2">
|
<h3 className="text-sm sm:text-base font-semibold text-gray-900 mb-2 flex items-center gap-2">
|
||||||
<RefreshCw className="w-4 h-4 sm:w-5 sm:h-5 text-[#d4af37]" />
|
<RefreshCw className="w-4 h-4 sm:w-5 sm:h-5 text-[var(--luxury-gold)]" />
|
||||||
Backup Codes
|
Backup Codes
|
||||||
</h3>
|
</h3>
|
||||||
<p className="text-xs sm:text-sm text-gray-600 font-light mb-4 leading-relaxed">
|
<p className="text-xs sm:text-sm text-gray-600 font-light mb-4 leading-relaxed">
|
||||||
@@ -984,7 +984,7 @@ const ProfilePage: React.FC = () => {
|
|||||||
{}
|
{}
|
||||||
<div className="bg-white border border-gray-200 rounded-sm p-4 sm:p-5">
|
<div className="bg-white border border-gray-200 rounded-sm p-4 sm:p-5">
|
||||||
<h3 className="font-semibold text-gray-900 mb-2 text-sm sm:text-base flex items-center gap-2">
|
<h3 className="font-semibold text-gray-900 mb-2 text-sm sm:text-base flex items-center gap-2">
|
||||||
<KeyRound className="w-4 h-4 sm:w-5 sm:h-5 text-[#d4af37]" />
|
<KeyRound className="w-4 h-4 sm:w-5 sm:h-5 text-[var(--luxury-gold)]" />
|
||||||
Step 2: Verify Setup
|
Step 2: Verify Setup
|
||||||
</h3>
|
</h3>
|
||||||
<p className="text-xs sm:text-sm text-gray-600 font-light mb-4 leading-relaxed">
|
<p className="text-xs sm:text-sm text-gray-600 font-light mb-4 leading-relaxed">
|
||||||
@@ -1110,10 +1110,10 @@ const SessionsTab: React.FC = () => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const getDeviceIcon = (userAgent?: string) => {
|
const getDeviceIcon = (userAgent?: string) => {
|
||||||
if (!userAgent) return <Monitor className="w-5 h-5 text-[#d4af37]" />;
|
if (!userAgent) return <Monitor className="w-5 h-5 text-[var(--luxury-gold)]" />;
|
||||||
if (userAgent.includes('Mobile')) return <Smartphone className="w-5 h-5 text-blue-500" />;
|
if (userAgent.includes('Mobile')) return <Smartphone className="w-5 h-5 text-blue-500" />;
|
||||||
if (userAgent.includes('Tablet')) return <Tablet className="w-5 h-5 text-purple-500" />;
|
if (userAgent.includes('Tablet')) return <Tablet className="w-5 h-5 text-purple-500" />;
|
||||||
return <Monitor className="w-5 h-5 text-[#d4af37]" />;
|
return <Monitor className="w-5 h-5 text-[var(--luxury-gold)]" />;
|
||||||
};
|
};
|
||||||
|
|
||||||
const getDeviceName = (userAgent?: string) => {
|
const getDeviceName = (userAgent?: string) => {
|
||||||
@@ -1181,17 +1181,17 @@ const SessionsTab: React.FC = () => {
|
|||||||
|
|
||||||
if (loading) {
|
if (loading) {
|
||||||
return (
|
return (
|
||||||
<div className="luxury-glass rounded-sm p-4 sm:p-6 lg:p-8 border border-[#d4af37]/20 shadow-2xl">
|
<div className="luxury-glass rounded-sm p-4 sm:p-6 lg:p-8 border border-[var(--luxury-gold)]/20 shadow-2xl">
|
||||||
<Loading text="Loading sessions..." />
|
<Loading text="Loading sessions..." />
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="luxury-glass rounded-sm p-4 sm:p-6 lg:p-8 border border-[#d4af37]/20 shadow-2xl animate-slide-up space-y-5 sm:space-y-6">
|
<div className="luxury-glass rounded-sm p-4 sm:p-6 lg:p-8 border border-[var(--luxury-gold)]/20 shadow-2xl animate-slide-up space-y-5 sm:space-y-6">
|
||||||
<div>
|
<div>
|
||||||
<h2 className="text-xl sm:text-2xl font-serif font-semibold text-gray-900 mb-2 flex items-center gap-2">
|
<h2 className="text-xl sm:text-2xl font-serif font-semibold text-gray-900 mb-2 flex items-center gap-2">
|
||||||
<Monitor className="w-5 h-5 sm:w-6 sm:h-6 text-[#d4af37]" />
|
<Monitor className="w-5 h-5 sm:w-6 sm:h-6 text-[var(--luxury-gold)]" />
|
||||||
Active Sessions
|
Active Sessions
|
||||||
</h2>
|
</h2>
|
||||||
<p className="text-xs sm:text-sm text-gray-600 font-light">
|
<p className="text-xs sm:text-sm text-gray-600 font-light">
|
||||||
@@ -1220,11 +1220,11 @@ const SessionsTab: React.FC = () => {
|
|||||||
{sessions.map((session) => (
|
{sessions.map((session) => (
|
||||||
<div
|
<div
|
||||||
key={session.id}
|
key={session.id}
|
||||||
className="bg-gradient-to-r from-slate-50 to-white border border-[#d4af37]/20 rounded-sm p-4 sm:p-5 hover:shadow-lg hover:border-[#d4af37]/40 transition-all duration-300"
|
className="bg-gradient-to-r from-slate-50 to-white border border-[var(--luxury-gold)]/20 rounded-sm p-4 sm:p-5 hover:shadow-lg hover:border-[var(--luxury-gold)]/40 transition-all duration-300"
|
||||||
>
|
>
|
||||||
<div className="flex items-start justify-between gap-4">
|
<div className="flex items-start justify-between gap-4">
|
||||||
<div className="flex items-start gap-4 flex-1">
|
<div className="flex items-start gap-4 flex-1">
|
||||||
<div className="p-2 sm:p-3 bg-gradient-to-br from-[#d4af37]/10 to-[#c9a227]/10 rounded-sm flex-shrink-0">
|
<div className="p-2 sm:p-3 bg-gradient-to-br from-[var(--luxury-gold)]/10 to-[var(--luxury-gold-dark)]/10 rounded-sm flex-shrink-0">
|
||||||
{getDeviceIcon(session.user_agent)}
|
{getDeviceIcon(session.user_agent)}
|
||||||
</div>
|
</div>
|
||||||
<div className="flex-1 min-w-0">
|
<div className="flex-1 min-w-0">
|
||||||
@@ -1354,17 +1354,17 @@ const GDPRTab: React.FC = () => {
|
|||||||
|
|
||||||
if (loading) {
|
if (loading) {
|
||||||
return (
|
return (
|
||||||
<div className="luxury-glass rounded-sm p-4 sm:p-6 lg:p-8 border border-[#d4af37]/20 shadow-2xl">
|
<div className="luxury-glass rounded-sm p-4 sm:p-6 lg:p-8 border border-[var(--luxury-gold)]/20 shadow-2xl">
|
||||||
<Loading text="Loading privacy data..." />
|
<Loading text="Loading privacy data..." />
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="luxury-glass rounded-sm p-4 sm:p-6 lg:p-8 border border-[#d4af37]/20 shadow-2xl animate-slide-up space-y-5 sm:space-y-6">
|
<div className="luxury-glass rounded-sm p-4 sm:p-6 lg:p-8 border border-[var(--luxury-gold)]/20 shadow-2xl animate-slide-up space-y-5 sm:space-y-6">
|
||||||
<div>
|
<div>
|
||||||
<h2 className="text-xl sm:text-2xl font-serif font-semibold text-gray-900 mb-2 flex items-center gap-2">
|
<h2 className="text-xl sm:text-2xl font-serif font-semibold text-gray-900 mb-2 flex items-center gap-2">
|
||||||
<Database className="w-5 h-5 sm:w-6 sm:h-6 text-[#d4af37]" />
|
<Database className="w-5 h-5 sm:w-6 sm:h-6 text-[var(--luxury-gold)]" />
|
||||||
Data Privacy & GDPR Rights
|
Data Privacy & GDPR Rights
|
||||||
</h2>
|
</h2>
|
||||||
<p className="text-xs sm:text-sm text-gray-600 font-light">
|
<p className="text-xs sm:text-sm text-gray-600 font-light">
|
||||||
@@ -1431,14 +1431,14 @@ const GDPRTab: React.FC = () => {
|
|||||||
{requests.length > 0 && (
|
{requests.length > 0 && (
|
||||||
<div className="border-t border-gray-200 pt-5 sm:pt-6">
|
<div className="border-t border-gray-200 pt-5 sm:pt-6">
|
||||||
<h3 className="text-base sm:text-lg font-semibold text-gray-900 mb-4 flex items-center gap-2">
|
<h3 className="text-base sm:text-lg font-semibold text-gray-900 mb-4 flex items-center gap-2">
|
||||||
<FileText className="w-5 h-5 text-[#d4af37]" />
|
<FileText className="w-5 h-5 text-[var(--luxury-gold)]" />
|
||||||
Request History
|
Request History
|
||||||
</h3>
|
</h3>
|
||||||
<div className="space-y-3 sm:space-y-4">
|
<div className="space-y-3 sm:space-y-4">
|
||||||
{requests.map((request) => (
|
{requests.map((request) => (
|
||||||
<div
|
<div
|
||||||
key={request.id}
|
key={request.id}
|
||||||
className="bg-gradient-to-r from-slate-50 to-white border border-[#d4af37]/20 rounded-sm p-4 sm:p-5 hover:shadow-md hover:border-[#d4af37]/40 transition-all duration-300"
|
className="bg-gradient-to-r from-slate-50 to-white border border-[var(--luxury-gold)]/20 rounded-sm p-4 sm:p-5 hover:shadow-md hover:border-[var(--luxury-gold)]/40 transition-all duration-300"
|
||||||
>
|
>
|
||||||
<div className="flex flex-col sm:flex-row items-start sm:items-center justify-between gap-3 sm:gap-4">
|
<div className="flex flex-col sm:flex-row items-start sm:items-center justify-between gap-3 sm:gap-4">
|
||||||
<div className="flex-1 min-w-0">
|
<div className="flex-1 min-w-0">
|
||||||
|
|||||||
@@ -184,9 +184,9 @@ const RoomDetailPage: React.FC = () => {
|
|||||||
>
|
>
|
||||||
<div className="w-full px-2 sm:px-4 md:px-6 lg:px-8 py-8 sm:py-12">
|
<div className="w-full px-2 sm:px-4 md:px-6 lg:px-8 py-8 sm:py-12">
|
||||||
<div className="animate-pulse space-y-6">
|
<div className="animate-pulse space-y-6">
|
||||||
<div className="h-[600px] bg-gradient-to-br from-[#1a1a1a] to-[#0a0a0a] rounded-xl border border-[#d4af37]/20" />
|
<div className="h-[600px] bg-gradient-to-br from-[#1a1a1a] to-[#0a0a0a] rounded-xl border border-[var(--luxury-gold)]/20" />
|
||||||
<div className="h-12 bg-gradient-to-br from-[#1a1a1a] to-[#0a0a0a] rounded-lg w-1/3 border border-[#d4af37]/10" />
|
<div className="h-12 bg-gradient-to-br from-[#1a1a1a] to-[#0a0a0a] rounded-lg w-1/3 border border-[var(--luxury-gold)]/10" />
|
||||||
<div className="h-6 bg-gradient-to-br from-[#1a1a1a] to-[#0a0a0a] rounded-lg w-2/3 border border-[#d4af37]/10" />
|
<div className="h-6 bg-gradient-to-br from-[#1a1a1a] to-[#0a0a0a] rounded-lg w-2/3 border border-[var(--luxury-gold)]/10" />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -217,10 +217,10 @@ const RoomDetailPage: React.FC = () => {
|
|||||||
<button
|
<button
|
||||||
onClick={() => navigate('/rooms')}
|
onClick={() => navigate('/rooms')}
|
||||||
className="inline-flex items-center gap-2 bg-gradient-to-r
|
className="inline-flex items-center gap-2 bg-gradient-to-r
|
||||||
from-[#d4af37] to-[#c9a227] text-[#0f0f0f]
|
from-[var(--luxury-gold)] to-[var(--luxury-gold-dark)] text-[#0f0f0f]
|
||||||
px-6 py-3 rounded-sm hover:from-[#f5d76e]
|
px-6 py-3 rounded-sm hover:from-[var(--luxury-gold-light)]
|
||||||
hover:to-[#d4af37] transition-all duration-300
|
hover:to-[var(--luxury-gold)] transition-all duration-300
|
||||||
font-medium tracking-wide shadow-lg shadow-[#d4af37]/30"
|
font-medium tracking-wide shadow-lg shadow-[var(--luxury-gold)]/30"
|
||||||
>
|
>
|
||||||
<ArrowLeft className="w-5 h-5" />
|
<ArrowLeft className="w-5 h-5" />
|
||||||
Back to Room List
|
Back to Room List
|
||||||
@@ -251,7 +251,7 @@ const RoomDetailPage: React.FC = () => {
|
|||||||
<Link
|
<Link
|
||||||
to="/rooms"
|
to="/rooms"
|
||||||
className="inline-flex items-center gap-1
|
className="inline-flex items-center gap-1
|
||||||
text-[#d4af37]/80 hover:text-[#d4af37]
|
text-[var(--luxury-gold)]/80 hover:text-[var(--luxury-gold)]
|
||||||
mb-3 transition-all duration-300
|
mb-3 transition-all duration-300
|
||||||
group font-light tracking-wide text-xs sm:text-sm"
|
group font-light tracking-wide text-xs sm:text-sm"
|
||||||
>
|
>
|
||||||
@@ -281,10 +281,10 @@ const RoomDetailPage: React.FC = () => {
|
|||||||
<div className="flex items-center gap-1.5 mb-2">
|
<div className="flex items-center gap-1.5 mb-2">
|
||||||
{room.featured && (
|
{room.featured && (
|
||||||
<div className="flex items-center gap-1
|
<div className="flex items-center gap-1
|
||||||
bg-gradient-to-r from-[#d4af37] to-[#c9a227]
|
bg-gradient-to-r from-[var(--luxury-gold)] to-[var(--luxury-gold-dark)]
|
||||||
text-[#0f0f0f] px-2 py-0.5 rounded-sm
|
text-[#0f0f0f] px-2 py-0.5 rounded-sm
|
||||||
text-[10px] sm:text-xs font-medium tracking-wide
|
text-[10px] sm:text-xs font-medium tracking-wide
|
||||||
shadow-sm shadow-[#d4af37]/30"
|
shadow-sm shadow-[var(--luxury-gold)]/30"
|
||||||
>
|
>
|
||||||
<Sparkles className="w-2.5 h-2.5" />
|
<Sparkles className="w-2.5 h-2.5" />
|
||||||
Featured
|
Featured
|
||||||
@@ -315,7 +315,7 @@ const RoomDetailPage: React.FC = () => {
|
|||||||
</p>
|
</p>
|
||||||
)}
|
)}
|
||||||
{room.status === 'occupied' && nextAvailableDate && (
|
{room.status === 'occupied' && nextAvailableDate && (
|
||||||
<p className="text-[9px] sm:text-[10px] text-[#d4af37] font-light leading-tight">
|
<p className="text-[9px] sm:text-[10px] text-[var(--luxury-gold)] font-light leading-tight">
|
||||||
Available from {nextAvailableDate.toLocaleDateString('en-US', { month: 'short', day: 'numeric' })}
|
Available from {nextAvailableDate.toLocaleDateString('en-US', { month: 'short', day: 'numeric' })}
|
||||||
</p>
|
</p>
|
||||||
)}
|
)}
|
||||||
@@ -324,7 +324,7 @@ const RoomDetailPage: React.FC = () => {
|
|||||||
|
|
||||||
<h1 className="text-xl sm:text-2xl lg:text-3xl font-serif font-semibold
|
<h1 className="text-xl sm:text-2xl lg:text-3xl font-serif font-semibold
|
||||||
text-white mb-2 tracking-tight leading-tight
|
text-white mb-2 tracking-tight leading-tight
|
||||||
bg-gradient-to-r from-white via-[#d4af37] to-white
|
bg-gradient-to-r from-white via-[var(--luxury-gold)] to-white
|
||||||
bg-clip-text text-transparent"
|
bg-clip-text text-transparent"
|
||||||
>
|
>
|
||||||
{roomType?.name}
|
{roomType?.name}
|
||||||
@@ -336,12 +336,12 @@ const RoomDetailPage: React.FC = () => {
|
|||||||
<div className="grid grid-cols-1 md:grid-cols-3 gap-2 sm:gap-3 mb-3">
|
<div className="grid grid-cols-1 md:grid-cols-3 gap-2 sm:gap-3 mb-3">
|
||||||
<div className="flex items-center gap-2
|
<div className="flex items-center gap-2
|
||||||
p-2 bg-gradient-to-br from-[#1a1a1a] to-[#0a0a0a]
|
p-2 bg-gradient-to-br from-[#1a1a1a] to-[#0a0a0a]
|
||||||
rounded-lg border border-[#d4af37]/20
|
rounded-lg border border-[var(--luxury-gold)]/20
|
||||||
hover:border-[#d4af37]/40 transition-all duration-300"
|
hover:border-[var(--luxury-gold)]/40 transition-all duration-300"
|
||||||
>
|
>
|
||||||
<div className="p-1 bg-[#d4af37]/10 rounded-lg
|
<div className="p-1 bg-[var(--luxury-gold)]/10 rounded-lg
|
||||||
border border-[#d4af37]/30">
|
border border-[var(--luxury-gold)]/30">
|
||||||
<MapPin className="w-3.5 h-3.5 text-[#d4af37]" />
|
<MapPin className="w-3.5 h-3.5 text-[var(--luxury-gold)]" />
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<p className="text-[10px] sm:text-xs text-gray-400 font-light tracking-wide mb-0.5">
|
<p className="text-[10px] sm:text-xs text-gray-400 font-light tracking-wide mb-0.5">
|
||||||
@@ -355,11 +355,11 @@ const RoomDetailPage: React.FC = () => {
|
|||||||
|
|
||||||
<div className="flex items-center gap-2
|
<div className="flex items-center gap-2
|
||||||
p-2 bg-gradient-to-br from-[#1a1a1a] to-[#0a0a0a]
|
p-2 bg-gradient-to-br from-[#1a1a1a] to-[#0a0a0a]
|
||||||
rounded-lg border border-[#d4af37]/20"
|
rounded-lg border border-[var(--luxury-gold)]/20"
|
||||||
>
|
>
|
||||||
<div className="p-1 bg-[#d4af37]/10 rounded-lg
|
<div className="p-1 bg-[var(--luxury-gold)]/10 rounded-lg
|
||||||
border border-[#d4af37]/30">
|
border border-[var(--luxury-gold)]/30">
|
||||||
<Users className="w-3.5 h-3.5 text-[#d4af37]" />
|
<Users className="w-3.5 h-3.5 text-[var(--luxury-gold)]" />
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<p className="text-[10px] sm:text-xs text-gray-400 font-light tracking-wide mb-0.5">
|
<p className="text-[10px] sm:text-xs text-gray-400 font-light tracking-wide mb-0.5">
|
||||||
@@ -374,12 +374,12 @@ const RoomDetailPage: React.FC = () => {
|
|||||||
{room.average_rating != null && (
|
{room.average_rating != null && (
|
||||||
<div className="flex items-center gap-2
|
<div className="flex items-center gap-2
|
||||||
p-2 bg-gradient-to-br from-[#1a1a1a] to-[#0a0a0a]
|
p-2 bg-gradient-to-br from-[#1a1a1a] to-[#0a0a0a]
|
||||||
rounded-lg border border-[#d4af37]/20
|
rounded-lg border border-[var(--luxury-gold)]/20
|
||||||
hover:border-[#d4af37]/40 transition-all duration-300"
|
hover:border-[var(--luxury-gold)]/40 transition-all duration-300"
|
||||||
>
|
>
|
||||||
<div className="p-1 bg-[#d4af37]/10 rounded-lg
|
<div className="p-1 bg-[var(--luxury-gold)]/10 rounded-lg
|
||||||
border border-[#d4af37]/30">
|
border border-[var(--luxury-gold)]/30">
|
||||||
<Star className="w-3.5 h-3.5 text-[#d4af37] fill-[#d4af37]" />
|
<Star className="w-3.5 h-3.5 text-[var(--luxury-gold)] fill-[var(--luxury-gold)]" />
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<p className="text-[10px] sm:text-xs text-gray-400 font-light tracking-wide mb-0.5">
|
<p className="text-[10px] sm:text-xs text-gray-400 font-light tracking-wide mb-0.5">
|
||||||
@@ -402,13 +402,13 @@ const RoomDetailPage: React.FC = () => {
|
|||||||
{}
|
{}
|
||||||
{(room?.description || roomType?.description) && (
|
{(room?.description || roomType?.description) && (
|
||||||
<div className="p-3 sm:p-4 bg-gradient-to-br from-[#1a1a1a] to-[#0a0a0a]
|
<div className="p-3 sm:p-4 bg-gradient-to-br from-[#1a1a1a] to-[#0a0a0a]
|
||||||
rounded-lg border border-[#d4af37]/20
|
rounded-lg border border-[var(--luxury-gold)]/20
|
||||||
backdrop-blur-xl shadow-lg shadow-[#d4af37]/5"
|
backdrop-blur-xl shadow-lg shadow-[var(--luxury-gold)]/5"
|
||||||
>
|
>
|
||||||
<div className="flex items-center gap-2 mb-2">
|
<div className="flex items-center gap-2 mb-2">
|
||||||
<div className="p-1 bg-[#d4af37]/10 rounded-lg
|
<div className="p-1 bg-[var(--luxury-gold)]/10 rounded-lg
|
||||||
border border-[#d4af37]/30">
|
border border-[var(--luxury-gold)]/30">
|
||||||
<Award className="w-3.5 h-3.5 text-[#d4af37]" />
|
<Award className="w-3.5 h-3.5 text-[var(--luxury-gold)]" />
|
||||||
</div>
|
</div>
|
||||||
<h2 className="text-sm sm:text-base font-serif font-semibold
|
<h2 className="text-sm sm:text-base font-serif font-semibold
|
||||||
text-white tracking-wide"
|
text-white tracking-wide"
|
||||||
@@ -426,13 +426,13 @@ const RoomDetailPage: React.FC = () => {
|
|||||||
|
|
||||||
{}
|
{}
|
||||||
<div className="p-3 sm:p-4 bg-gradient-to-br from-[#1a1a1a] to-[#0a0a0a]
|
<div className="p-3 sm:p-4 bg-gradient-to-br from-[#1a1a1a] to-[#0a0a0a]
|
||||||
rounded-lg border border-[#d4af37]/20
|
rounded-lg border border-[var(--luxury-gold)]/20
|
||||||
backdrop-blur-xl shadow-lg shadow-[#d4af37]/5"
|
backdrop-blur-xl shadow-lg shadow-[var(--luxury-gold)]/5"
|
||||||
>
|
>
|
||||||
<div className="flex items-center gap-2 mb-2">
|
<div className="flex items-center gap-2 mb-2">
|
||||||
<div className="p-1 bg-[#d4af37]/10 rounded-lg
|
<div className="p-1 bg-[var(--luxury-gold)]/10 rounded-lg
|
||||||
border border-[#d4af37]/30">
|
border border-[var(--luxury-gold)]/30">
|
||||||
<Sparkles className="w-3.5 h-3.5 text-[#d4af37]" />
|
<Sparkles className="w-3.5 h-3.5 text-[var(--luxury-gold)]" />
|
||||||
</div>
|
</div>
|
||||||
<h2 className="text-sm sm:text-base font-serif font-semibold
|
<h2 className="text-sm sm:text-base font-serif font-semibold
|
||||||
text-white tracking-wide"
|
text-white tracking-wide"
|
||||||
@@ -447,10 +447,10 @@ const RoomDetailPage: React.FC = () => {
|
|||||||
: (roomType?.amenities || [])
|
: (roomType?.amenities || [])
|
||||||
}
|
}
|
||||||
/>
|
/>
|
||||||
<div className="mt-6 pt-6 border-t border-[#d4af37]/20">
|
<div className="mt-6 pt-6 border-t border-[var(--luxury-gold)]/20">
|
||||||
<Link
|
<Link
|
||||||
to="/services"
|
to="/services"
|
||||||
className="inline-flex items-center gap-2 px-4 py-2 bg-gradient-to-r from-[#d4af37]/20 to-[#c9a227]/10 hover:from-[#d4af37]/30 hover:to-[#d4af37]/20 text-[#d4af37] rounded-lg font-medium transition-all duration-300 border border-[#d4af37]/30 hover:border-[#d4af37]/50"
|
className="inline-flex items-center gap-2 px-4 py-2 bg-gradient-to-r from-[var(--luxury-gold)]/20 to-[var(--luxury-gold-dark)]/10 hover:from-[var(--luxury-gold)]/30 hover:to-[var(--luxury-gold)]/20 text-[var(--luxury-gold)] rounded-lg font-medium transition-all duration-300 border border-[var(--luxury-gold)]/30 hover:border-[var(--luxury-gold)]/50"
|
||||||
>
|
>
|
||||||
<Award className="w-4 h-4" />
|
<Award className="w-4 h-4" />
|
||||||
<span className="text-sm">View All Services</span>
|
<span className="text-sm">View All Services</span>
|
||||||
@@ -462,20 +462,20 @@ const RoomDetailPage: React.FC = () => {
|
|||||||
{}
|
{}
|
||||||
<aside className="lg:col-span-4">
|
<aside className="lg:col-span-4">
|
||||||
<div className="bg-gradient-to-br from-[#1a1a1a] via-[#0f0f0f] to-[#1a1a1a]
|
<div className="bg-gradient-to-br from-[#1a1a1a] via-[#0f0f0f] to-[#1a1a1a]
|
||||||
rounded-lg border border-[#d4af37]/30
|
rounded-lg border border-[var(--luxury-gold)]/30
|
||||||
backdrop-blur-xl shadow-lg shadow-[#d4af37]/20
|
backdrop-blur-xl shadow-lg shadow-[var(--luxury-gold)]/20
|
||||||
p-3 sm:p-4 sticky top-4"
|
p-3 sm:p-4 sticky top-4"
|
||||||
>
|
>
|
||||||
{}
|
{}
|
||||||
<div className="mb-4 pb-4 border-b border-[#d4af37]/20">
|
<div className="mb-4 pb-4 border-b border-[var(--luxury-gold)]/20">
|
||||||
<p className="text-[10px] sm:text-xs text-gray-400 font-light tracking-wide mb-1">
|
<p className="text-[10px] sm:text-xs text-gray-400 font-light tracking-wide mb-1">
|
||||||
Starting from
|
Starting from
|
||||||
</p>
|
</p>
|
||||||
<div className="flex items-baseline gap-1.5">
|
<div className="flex items-baseline gap-1.5">
|
||||||
<CurrencyIcon className="text-[#d4af37]" size={16} />
|
<CurrencyIcon className="text-[var(--luxury-gold)]" size={16} />
|
||||||
<div>
|
<div>
|
||||||
<div className="text-2xl sm:text-3xl font-serif font-semibold
|
<div className="text-2xl sm:text-3xl font-serif font-semibold
|
||||||
bg-gradient-to-r from-[#d4af37] to-[#f5d76e]
|
bg-gradient-to-r from-[var(--luxury-gold)] to-[var(--luxury-gold-light)]
|
||||||
bg-clip-text text-transparent tracking-tight"
|
bg-clip-text text-transparent tracking-tight"
|
||||||
>
|
>
|
||||||
{formattedPrice}
|
{formattedPrice}
|
||||||
@@ -510,7 +510,7 @@ const RoomDetailPage: React.FC = () => {
|
|||||||
tracking-wide relative overflow-hidden group text-xs sm:text-sm
|
tracking-wide relative overflow-hidden group text-xs sm:text-sm
|
||||||
${
|
${
|
||||||
room.status === 'available'
|
room.status === 'available'
|
||||||
? 'bg-gradient-to-r from-[#d4af37] to-[#c9a227] text-[#0f0f0f] hover:from-[#f5d76e] hover:to-[#d4af37] shadow-sm shadow-[#d4af37]/30 hover:shadow-[#d4af37]/50'
|
? 'bg-gradient-to-r from-[var(--luxury-gold)] to-[var(--luxury-gold-dark)] text-[#0f0f0f] hover:from-[var(--luxury-gold-light)] hover:to-[var(--luxury-gold)] shadow-sm shadow-[var(--luxury-gold)]/30 hover:shadow-[var(--luxury-gold)]/50'
|
||||||
: 'bg-gray-800 text-gray-500 cursor-not-allowed border border-gray-700'
|
: 'bg-gray-800 text-gray-500 cursor-not-allowed border border-gray-700'
|
||||||
}`}
|
}`}
|
||||||
>
|
>
|
||||||
@@ -525,12 +525,12 @@ const RoomDetailPage: React.FC = () => {
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
{room.status === 'available' && (
|
{room.status === 'available' && (
|
||||||
<div className="flex items-start gap-2 p-2 bg-[#d4af37]/5
|
<div className="flex items-start gap-2 p-2 bg-[var(--luxury-gold)]/5
|
||||||
rounded-lg border border-[#d4af37]/20 mb-3"
|
rounded-lg border border-[var(--luxury-gold)]/20 mb-3"
|
||||||
>
|
>
|
||||||
<Shield className="w-3.5 h-3.5 text-[#d4af37] mt-0.5 flex-shrink-0" />
|
<Shield className="w-3.5 h-3.5 text-[var(--luxury-gold)] mt-0.5 flex-shrink-0" />
|
||||||
<p className="text-[10px] sm:text-xs text-gray-300 font-light tracking-wide leading-relaxed">
|
<p className="text-[10px] sm:text-xs text-gray-300 font-light tracking-wide leading-relaxed">
|
||||||
<strong className="text-[#d4af37]">20% deposit required</strong> to secure your booking. Pay the remaining balance on arrival at the hotel.
|
<strong className="text-[var(--luxury-gold)]">20% deposit required</strong> to secure your booking. Pay the remaining balance on arrival at the hotel.
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
@@ -540,27 +540,27 @@ const RoomDetailPage: React.FC = () => {
|
|||||||
{}
|
{}
|
||||||
<div className="space-y-2">
|
<div className="space-y-2">
|
||||||
<div className="flex items-center justify-between
|
<div className="flex items-center justify-between
|
||||||
py-1.5 border-b border-[#d4af37]/10"
|
py-1.5 border-b border-[var(--luxury-gold)]/10"
|
||||||
>
|
>
|
||||||
<span className="text-[10px] sm:text-xs text-gray-400 font-light tracking-wide">Room Type</span>
|
<span className="text-[10px] sm:text-xs text-gray-400 font-light tracking-wide">Room Type</span>
|
||||||
<strong className="text-xs sm:text-sm text-white font-light">{roomType?.name}</strong>
|
<strong className="text-xs sm:text-sm text-white font-light">{roomType?.name}</strong>
|
||||||
</div>
|
</div>
|
||||||
<div className="flex items-center justify-between
|
<div className="flex items-center justify-between
|
||||||
py-1.5 border-b border-[#d4af37]/10"
|
py-1.5 border-b border-[var(--luxury-gold)]/10"
|
||||||
>
|
>
|
||||||
<span className="text-[10px] sm:text-xs text-gray-400 font-light tracking-wide">Max Guests</span>
|
<span className="text-[10px] sm:text-xs text-gray-400 font-light tracking-wide">Max Guests</span>
|
||||||
<span className="text-xs sm:text-sm text-white font-light">{(room?.capacity || roomType?.capacity || 0)} guests</span>
|
<span className="text-xs sm:text-sm text-white font-light">{(room?.capacity || roomType?.capacity || 0)} guests</span>
|
||||||
</div>
|
</div>
|
||||||
{room?.room_size && (
|
{room?.room_size && (
|
||||||
<div className="flex items-center justify-between
|
<div className="flex items-center justify-between
|
||||||
py-1.5 border-b border-[#d4af37]/10"
|
py-1.5 border-b border-[var(--luxury-gold)]/10"
|
||||||
>
|
>
|
||||||
<span className="text-[10px] sm:text-xs text-gray-400 font-light tracking-wide">Room Size</span>
|
<span className="text-[10px] sm:text-xs text-gray-400 font-light tracking-wide">Room Size</span>
|
||||||
<span className="text-xs sm:text-sm text-white font-light">{room.room_size}</span>
|
<span className="text-xs sm:text-sm text-white font-light">{room.room_size}</span>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
{room?.view && (
|
{room?.view && (
|
||||||
<div className={`flex items-center justify-between ${room?.room_size ? 'py-1.5 border-b border-[#d4af37]/10' : 'py-1.5'}`}>
|
<div className={`flex items-center justify-between ${room?.room_size ? 'py-1.5 border-b border-[var(--luxury-gold)]/10' : 'py-1.5'}`}>
|
||||||
<span className="text-[10px] sm:text-xs text-gray-400 font-light tracking-wide">View</span>
|
<span className="text-[10px] sm:text-xs text-gray-400 font-light tracking-wide">View</span>
|
||||||
<span className="text-xs sm:text-sm text-white font-light">{room.view}</span>
|
<span className="text-xs sm:text-sm text-white font-light">{room.view}</span>
|
||||||
</div>
|
</div>
|
||||||
@@ -572,8 +572,8 @@ const RoomDetailPage: React.FC = () => {
|
|||||||
|
|
||||||
{}
|
{}
|
||||||
<div className="mb-4 p-3 sm:p-4 bg-gradient-to-br from-[#1a1a1a] to-[#0a0a0a]
|
<div className="mb-4 p-3 sm:p-4 bg-gradient-to-br from-[#1a1a1a] to-[#0a0a0a]
|
||||||
rounded-lg border border-[#d4af37]/20
|
rounded-lg border border-[var(--luxury-gold)]/20
|
||||||
backdrop-blur-xl shadow-lg shadow-[#d4af37]/5"
|
backdrop-blur-xl shadow-lg shadow-[var(--luxury-gold)]/5"
|
||||||
>
|
>
|
||||||
<ReviewSection roomId={room.id} />
|
<ReviewSection roomId={room.id} />
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -150,17 +150,17 @@ const RoomListPage: React.FC = () => {
|
|||||||
{}
|
{}
|
||||||
{/* Promotion Banner */}
|
{/* Promotion Banner */}
|
||||||
{showPromotionBanner && activePromotion && (
|
{showPromotionBanner && activePromotion && (
|
||||||
<div className="w-full bg-gradient-to-r from-[#d4af37]/20 via-[#f5d76e]/15 to-[#d4af37]/20 border-b border-[#d4af37]/30">
|
<div className="w-full bg-gradient-to-r from-[var(--luxury-gold)]/20 via-[var(--luxury-gold-light)]/15 to-[var(--luxury-gold)]/20 border-b border-[var(--luxury-gold)]/30">
|
||||||
<div className="w-full max-w-[1920px] mx-auto px-3 sm:px-4 md:px-6 lg:px-8 xl:px-12 2xl:px-16 3xl:px-20 py-4">
|
<div className="w-full max-w-[1920px] mx-auto px-3 sm:px-4 md:px-6 lg:px-8 xl:px-12 2xl:px-16 3xl:px-20 py-4">
|
||||||
<div className="flex items-center justify-between gap-4 bg-gradient-to-r from-[#1a1a1a] to-[#0f0f0f] border border-[#d4af37]/40 rounded-lg p-4 backdrop-blur-xl shadow-lg">
|
<div className="flex items-center justify-between gap-4 bg-gradient-to-r from-[#1a1a1a] to-[#0f0f0f] border border-[var(--luxury-gold)]/40 rounded-lg p-4 backdrop-blur-xl shadow-lg">
|
||||||
<div className="flex items-center gap-3 flex-1">
|
<div className="flex items-center gap-3 flex-1">
|
||||||
<div className="p-2 bg-[#d4af37]/20 rounded-lg border border-[#d4af37]/40">
|
<div className="p-2 bg-[var(--luxury-gold)]/20 rounded-lg border border-[var(--luxury-gold)]/40">
|
||||||
<Tag className="w-5 h-5 text-[#d4af37]" />
|
<Tag className="w-5 h-5 text-[var(--luxury-gold)]" />
|
||||||
</div>
|
</div>
|
||||||
<div className="flex-1">
|
<div className="flex-1">
|
||||||
<div className="flex items-center gap-2 mb-1">
|
<div className="flex items-center gap-2 mb-1">
|
||||||
<CheckCircle className="w-4 h-4 text-green-400" />
|
<CheckCircle className="w-4 h-4 text-green-400" />
|
||||||
<span className="text-sm font-semibold text-[#d4af37]">
|
<span className="text-sm font-semibold text-[var(--luxury-gold)]">
|
||||||
Active Promotion: {activePromotion.code || activePromotion.title}
|
Active Promotion: {activePromotion.code || activePromotion.title}
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
@@ -176,7 +176,7 @@ const RoomListPage: React.FC = () => {
|
|||||||
</div>
|
</div>
|
||||||
<button
|
<button
|
||||||
onClick={handleDismissPromotion}
|
onClick={handleDismissPromotion}
|
||||||
className="p-2 hover:bg-[#d4af37]/10 rounded-lg transition-colors"
|
className="p-2 hover:bg-[var(--luxury-gold)]/10 rounded-lg transition-colors"
|
||||||
aria-label="Dismiss promotion"
|
aria-label="Dismiss promotion"
|
||||||
>
|
>
|
||||||
<X className="w-5 h-5 text-gray-400 hover:text-white" />
|
<X className="w-5 h-5 text-gray-400 hover:text-white" />
|
||||||
@@ -186,19 +186,19 @@ const RoomListPage: React.FC = () => {
|
|||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
<div className="w-full bg-gradient-to-br from-[#1a1a1a] via-[#0f0f0f] to-[#1a1a1a] border-b border-[#d4af37]/10 pt-6 sm:pt-7 md:pt-8">
|
<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">
|
||||||
<div className="w-full max-w-[1920px] mx-auto px-3 sm:px-4 md:px-6 lg:px-8 xl:px-12 2xl:px-16 3xl:px-20 py-6 sm:py-7 md:py-8 lg:py-10">
|
<div className="w-full max-w-[1920px] mx-auto px-3 sm:px-4 md:px-6 lg:px-8 xl:px-12 2xl:px-16 3xl:px-20 py-6 sm:py-7 md:py-8 lg:py-10">
|
||||||
{}
|
{}
|
||||||
<Link
|
<Link
|
||||||
to="/"
|
to="/"
|
||||||
className="inline-flex items-center gap-2
|
className="inline-flex items-center gap-2
|
||||||
bg-gradient-to-r from-[#d4af37] to-[#c9a227]
|
bg-gradient-to-r from-[var(--luxury-gold)] to-[var(--luxury-gold-dark)]
|
||||||
text-[#0f0f0f] hover:from-[#f5d76e] hover:to-[#d4af37]
|
text-[#0f0f0f] hover:from-[var(--luxury-gold-light)] hover:to-[var(--luxury-gold)]
|
||||||
active:scale-95
|
active:scale-95
|
||||||
mb-4 sm:mb-5 md:mb-6 transition-all duration-300
|
mb-4 sm:mb-5 md:mb-6 transition-all duration-300
|
||||||
group font-medium tracking-wide text-sm
|
group font-medium tracking-wide text-sm
|
||||||
px-4 py-2 rounded-sm
|
px-4 py-2 rounded-sm
|
||||||
shadow-lg shadow-[#d4af37]/30 hover:shadow-xl hover:shadow-[#d4af37]/40
|
shadow-lg shadow-[var(--luxury-gold)]/30 hover:shadow-xl hover:shadow-[var(--luxury-gold)]/40
|
||||||
touch-manipulation"
|
touch-manipulation"
|
||||||
>
|
>
|
||||||
<ArrowLeft className="w-4 h-4 sm:w-4 sm:h-4 group-hover:-translate-x-1 transition-transform" />
|
<ArrowLeft className="w-4 h-4 sm:w-4 sm:h-4 group-hover:-translate-x-1 transition-transform" />
|
||||||
@@ -208,13 +208,13 @@ const RoomListPage: React.FC = () => {
|
|||||||
{}
|
{}
|
||||||
<div className="text-center max-w-3xl mx-auto px-2">
|
<div className="text-center max-w-3xl mx-auto px-2">
|
||||||
<div className="inline-flex items-center justify-center gap-2 mb-3 sm:mb-4">
|
<div className="inline-flex items-center justify-center gap-2 mb-3 sm:mb-4">
|
||||||
<div className="p-2 sm:p-2.5 bg-[#d4af37]/10 rounded-lg border border-[#d4af37]/30 backdrop-blur-sm">
|
<div className="p-2 sm:p-2.5 bg-[var(--luxury-gold)]/10 rounded-lg border border-[var(--luxury-gold)]/30 backdrop-blur-sm">
|
||||||
<Hotel className="w-5 h-5 sm:w-5 sm:h-5 text-[#d4af37]" />
|
<Hotel className="w-5 h-5 sm:w-5 sm:h-5 text-[var(--luxury-gold)]" />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<h1 className="text-2xl xs:text-3xl sm:text-4xl md:text-5xl font-serif font-semibold
|
<h1 className="text-2xl xs:text-3xl sm:text-4xl md:text-5xl font-serif font-semibold
|
||||||
text-white mb-2 sm:mb-3 tracking-tight leading-tight px-2
|
text-white mb-2 sm:mb-3 tracking-tight leading-tight px-2
|
||||||
bg-gradient-to-r from-white via-[#d4af37] to-white
|
bg-gradient-to-r from-white via-[var(--luxury-gold)] to-white
|
||||||
bg-clip-text text-transparent"
|
bg-clip-text text-transparent"
|
||||||
>
|
>
|
||||||
Our Rooms & Suites
|
Our Rooms & Suites
|
||||||
@@ -236,24 +236,24 @@ const RoomListPage: React.FC = () => {
|
|||||||
<button
|
<button
|
||||||
onClick={() => setIsFilterOpen(!isFilterOpen)}
|
onClick={() => setIsFilterOpen(!isFilterOpen)}
|
||||||
className="w-full bg-gradient-to-br from-[#1a1a1a] to-[#0a0a0a]
|
className="w-full bg-gradient-to-br from-[#1a1a1a] to-[#0a0a0a]
|
||||||
border border-[#d4af37]/30 rounded-xl p-4
|
border border-[var(--luxury-gold)]/30 rounded-xl p-4
|
||||||
backdrop-blur-xl shadow-lg shadow-[#d4af37]/10
|
backdrop-blur-xl shadow-lg shadow-[var(--luxury-gold)]/10
|
||||||
flex items-center justify-between gap-3
|
flex items-center justify-between gap-3
|
||||||
hover:border-[#d4af37]/50 hover:shadow-xl hover:shadow-[#d4af37]/20
|
hover:border-[var(--luxury-gold)]/50 hover:shadow-xl hover:shadow-[var(--luxury-gold)]/20
|
||||||
transition-all duration-300 touch-manipulation"
|
transition-all duration-300 touch-manipulation"
|
||||||
>
|
>
|
||||||
<div className="flex items-center gap-3">
|
<div className="flex items-center gap-3">
|
||||||
<div className="p-2 bg-[#d4af37]/10 rounded-lg border border-[#d4af37]/30">
|
<div className="p-2 bg-[var(--luxury-gold)]/10 rounded-lg border border-[var(--luxury-gold)]/30">
|
||||||
<Filter className="w-5 h-5 text-[#d4af37]" />
|
<Filter className="w-5 h-5 text-[var(--luxury-gold)]" />
|
||||||
</div>
|
</div>
|
||||||
<span className="text-white font-medium tracking-wide text-base">
|
<span className="text-white font-medium tracking-wide text-base">
|
||||||
Filters
|
Filters
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
{isFilterOpen ? (
|
{isFilterOpen ? (
|
||||||
<ChevronUp className="w-5 h-5 text-[#d4af37]" />
|
<ChevronUp className="w-5 h-5 text-[var(--luxury-gold)]" />
|
||||||
) : (
|
) : (
|
||||||
<ChevronDown className="w-5 h-5 text-[#d4af37]" />
|
<ChevronDown className="w-5 h-5 text-[var(--luxury-gold)]" />
|
||||||
)}
|
)}
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
@@ -308,10 +308,10 @@ const RoomListPage: React.FC = () => {
|
|||||||
<p className="text-red-300 font-light text-sm sm:text-base md:text-lg mb-4 sm:mb-5 md:mb-6 tracking-wide px-2">{error}</p>
|
<p className="text-red-300 font-light text-sm sm:text-base md:text-lg mb-4 sm:mb-5 md:mb-6 tracking-wide px-2">{error}</p>
|
||||||
<button
|
<button
|
||||||
onClick={() => window.location.reload()}
|
onClick={() => window.location.reload()}
|
||||||
className="px-5 sm:px-6 md:px-8 py-2.5 sm:py-3 bg-gradient-to-r from-[#d4af37] to-[#c9a227]
|
className="px-5 sm:px-6 md:px-8 py-2.5 sm:py-3 bg-gradient-to-r from-[var(--luxury-gold)] to-[var(--luxury-gold-dark)]
|
||||||
text-[#0f0f0f] rounded-sm font-medium tracking-wide text-sm sm:text-base
|
text-[#0f0f0f] rounded-sm font-medium tracking-wide text-sm sm:text-base
|
||||||
hover:from-[#f5d76e] hover:to-[#d4af37] active:scale-95
|
hover:from-[var(--luxury-gold-light)] hover:to-[var(--luxury-gold)] active:scale-95
|
||||||
transition-all duration-300 shadow-lg shadow-[#d4af37]/30
|
transition-all duration-300 shadow-lg shadow-[var(--luxury-gold)]/30
|
||||||
touch-manipulation min-h-[44px]"
|
touch-manipulation min-h-[44px]"
|
||||||
>
|
>
|
||||||
Try Again
|
Try Again
|
||||||
@@ -321,13 +321,13 @@ const RoomListPage: React.FC = () => {
|
|||||||
|
|
||||||
{!loading && !error && rooms.length === 0 && (
|
{!loading && !error && rooms.length === 0 && (
|
||||||
<div className="bg-gradient-to-br from-[#1a1a1a] to-[#0a0a0a]
|
<div className="bg-gradient-to-br from-[#1a1a1a] to-[#0a0a0a]
|
||||||
border border-[#d4af37]/20 rounded-xl p-8 sm:p-10 md:p-12 lg:p-16 text-center
|
border border-[var(--luxury-gold)]/20 rounded-xl p-8 sm:p-10 md:p-12 lg:p-16 text-center
|
||||||
backdrop-blur-xl shadow-2xl shadow-[#d4af37]/5"
|
backdrop-blur-xl shadow-2xl shadow-[var(--luxury-gold)]/5"
|
||||||
>
|
>
|
||||||
<div className="inline-flex items-center justify-center w-16 h-16 sm:w-20 sm:h-20 md:w-24 md:h-24
|
<div className="inline-flex items-center justify-center w-16 h-16 sm:w-20 sm:h-20 md:w-24 md:h-24
|
||||||
bg-[#d4af37]/10 rounded-2xl mb-4 sm:mb-5 md:mb-6 lg:mb-8 border border-[#d4af37]/30"
|
bg-[var(--luxury-gold)]/10 rounded-2xl mb-4 sm:mb-5 md:mb-6 lg:mb-8 border border-[var(--luxury-gold)]/30"
|
||||||
>
|
>
|
||||||
<Hotel className="w-8 h-8 sm:w-10 sm:h-10 md:w-12 md:h-12 text-[#d4af37]" />
|
<Hotel className="w-8 h-8 sm:w-10 sm:h-10 md:w-12 md:h-12 text-[var(--luxury-gold)]" />
|
||||||
</div>
|
</div>
|
||||||
<h3 className="text-lg sm:text-xl md:text-2xl font-serif font-semibold
|
<h3 className="text-lg sm:text-xl md:text-2xl font-serif font-semibold
|
||||||
text-white mb-3 sm:mb-4 tracking-wide px-2"
|
text-white mb-3 sm:mb-4 tracking-wide px-2"
|
||||||
@@ -339,10 +339,10 @@ const RoomListPage: React.FC = () => {
|
|||||||
</p>
|
</p>
|
||||||
<button
|
<button
|
||||||
onClick={() => window.location.href = '/rooms'}
|
onClick={() => window.location.href = '/rooms'}
|
||||||
className="px-5 sm:px-6 md:px-8 py-2.5 sm:py-3 bg-gradient-to-r from-[#d4af37] to-[#c9a227]
|
className="px-5 sm:px-6 md:px-8 py-2.5 sm:py-3 bg-gradient-to-r from-[var(--luxury-gold)] to-[var(--luxury-gold-dark)]
|
||||||
text-[#0f0f0f] rounded-sm font-medium tracking-wide text-sm sm:text-base
|
text-[#0f0f0f] rounded-sm font-medium tracking-wide text-sm sm:text-base
|
||||||
hover:from-[#f5d76e] hover:to-[#d4af37] active:scale-95
|
hover:from-[var(--luxury-gold-light)] hover:to-[var(--luxury-gold)] active:scale-95
|
||||||
transition-all duration-300 shadow-lg shadow-[#d4af37]/30
|
transition-all duration-300 shadow-lg shadow-[var(--luxury-gold)]/30
|
||||||
touch-manipulation min-h-[44px]"
|
touch-manipulation min-h-[44px]"
|
||||||
>
|
>
|
||||||
Clear Filters
|
Clear Filters
|
||||||
@@ -355,8 +355,8 @@ const RoomListPage: React.FC = () => {
|
|||||||
{}
|
{}
|
||||||
<div className="mb-3 sm:mb-4 md:mb-5 flex flex-col sm:flex-row items-start sm:items-center justify-between gap-2 sm:gap-3">
|
<div className="mb-3 sm:mb-4 md:mb-5 flex flex-col sm:flex-row items-start sm:items-center justify-between gap-2 sm:gap-3">
|
||||||
<p className="text-gray-400 font-light tracking-wide text-xs sm:text-sm md:text-base">
|
<p className="text-gray-400 font-light tracking-wide text-xs sm:text-sm md:text-base">
|
||||||
Showing <span className="text-[#d4af37] font-medium">{rooms.length}</span> of{' '}
|
Showing <span className="text-[var(--luxury-gold)] font-medium">{rooms.length}</span> of{' '}
|
||||||
<span className="text-[#d4af37] font-medium">{pagination.total}</span> rooms
|
<span className="text-[var(--luxury-gold)] font-medium">{pagination.total}</span> rooms
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@@ -370,7 +370,7 @@ const RoomListPage: React.FC = () => {
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
{pagination.totalPages > 1 && (
|
{pagination.totalPages > 1 && (
|
||||||
<div className="mt-4 sm:mt-5 md:mt-6 pt-3 sm:pt-4 border-t border-[#d4af37]/20">
|
<div className="mt-4 sm:mt-5 md:mt-6 pt-3 sm:pt-4 border-t border-[var(--luxury-gold)]/20">
|
||||||
<Pagination
|
<Pagination
|
||||||
currentPage={pagination.page}
|
currentPage={pagination.page}
|
||||||
totalPages={pagination.totalPages}
|
totalPages={pagination.totalPages}
|
||||||
|
|||||||
@@ -94,9 +94,9 @@ const TaskDetailModal: React.FC<TaskModalProps> = ({
|
|||||||
/>
|
/>
|
||||||
|
|
||||||
{/* Luxury Modal with enhanced styling - Compact */}
|
{/* Luxury Modal with enhanced styling - Compact */}
|
||||||
<div className="relative w-full max-w-3xl mx-auto my-2 sm:my-3 md:my-4 bg-white rounded-xl sm:rounded-2xl shadow-2xl overflow-hidden animate-scale-in border border-[#d4af37]/20 flex flex-col" style={{ maxHeight: 'calc(100vh - 1rem)' }}>
|
<div className="relative w-full max-w-3xl mx-auto my-2 sm:my-3 md:my-4 bg-white rounded-xl sm:rounded-2xl shadow-2xl overflow-hidden animate-scale-in border border-[var(--luxury-gold)]/20 flex flex-col" style={{ maxHeight: 'calc(100vh - 1rem)' }}>
|
||||||
{/* Luxury Header with enhanced gradient and effects - Compact */}
|
{/* Luxury Header with enhanced gradient and effects - Compact */}
|
||||||
<div className="relative bg-gradient-to-r from-[#d4af37] via-[#c9a227] via-[#d4af37] to-[#c9a227] p-3 sm:p-4 md:p-5 overflow-hidden flex-shrink-0">
|
<div className="relative bg-gradient-to-r from-[var(--luxury-gold)] via-[var(--luxury-gold-dark)] via-[var(--luxury-gold)] to-[var(--luxury-gold-dark)] p-3 sm:p-4 md:p-5 overflow-hidden flex-shrink-0">
|
||||||
{/* Animated background pattern */}
|
{/* Animated background pattern */}
|
||||||
<div className="absolute inset-0 opacity-10">
|
<div className="absolute inset-0 opacity-10">
|
||||||
<div className="absolute inset-0" style={{
|
<div className="absolute inset-0" style={{
|
||||||
@@ -147,9 +147,9 @@ const TaskDetailModal: React.FC<TaskModalProps> = ({
|
|||||||
>
|
>
|
||||||
{/* Task Info with luxury cards - Compact */}
|
{/* Task Info with luxury cards - Compact */}
|
||||||
<div className="grid grid-cols-1 sm:grid-cols-2 gap-3">
|
<div className="grid grid-cols-1 sm:grid-cols-2 gap-3">
|
||||||
<div className="group relative flex items-center space-x-2.5 sm:space-x-3 p-3 sm:p-3.5 bg-gradient-to-br from-gray-50 via-white to-gray-50 rounded-lg sm:rounded-xl border border-gray-200/50 hover:border-[#d4af37]/30 transition-all duration-300 hover:shadow-md overflow-hidden">
|
<div className="group relative flex items-center space-x-2.5 sm:space-x-3 p-3 sm:p-3.5 bg-gradient-to-br from-gray-50 via-white to-gray-50 rounded-lg sm:rounded-xl border border-gray-200/50 hover:border-[var(--luxury-gold)]/30 transition-all duration-300 hover:shadow-md overflow-hidden">
|
||||||
<div className="absolute inset-0 bg-gradient-to-br from-[#d4af37]/5 to-transparent opacity-0 group-hover:opacity-100 transition-opacity duration-300"></div>
|
<div className="absolute inset-0 bg-gradient-to-br from-[var(--luxury-gold)]/5 to-transparent opacity-0 group-hover:opacity-100 transition-opacity duration-300"></div>
|
||||||
<div className="relative w-9 h-9 sm:w-10 sm:h-10 rounded-lg bg-gradient-to-br from-[#d4af37] to-[#c9a227] flex items-center justify-center flex-shrink-0 shadow-md group-hover:scale-105 transition-transform duration-300">
|
<div className="relative w-9 h-9 sm:w-10 sm:h-10 rounded-lg bg-gradient-to-br from-[var(--luxury-gold)] to-[var(--luxury-gold-dark)] flex items-center justify-center flex-shrink-0 shadow-md group-hover:scale-105 transition-transform duration-300">
|
||||||
<Calendar className="w-4 h-4 sm:w-5 sm:h-5 text-white drop-shadow-md" />
|
<Calendar className="w-4 h-4 sm:w-5 sm:h-5 text-white drop-shadow-md" />
|
||||||
</div>
|
</div>
|
||||||
<div className="relative min-w-0 flex-1">
|
<div className="relative min-w-0 flex-1">
|
||||||
@@ -192,16 +192,16 @@ const TaskDetailModal: React.FC<TaskModalProps> = ({
|
|||||||
<div className="space-y-2">
|
<div className="space-y-2">
|
||||||
<div className="flex items-center justify-between gap-3">
|
<div className="flex items-center justify-between gap-3">
|
||||||
<h3 className="text-sm sm:text-base font-bold text-gray-900 flex items-center space-x-2 min-w-0">
|
<h3 className="text-sm sm:text-base font-bold text-gray-900 flex items-center space-x-2 min-w-0">
|
||||||
<div className="w-8 h-8 rounded-lg bg-gradient-to-br from-[#d4af37] to-[#c9a227] flex items-center justify-center shadow-md">
|
<div className="w-8 h-8 rounded-lg bg-gradient-to-br from-[var(--luxury-gold)] to-[var(--luxury-gold-dark)] flex items-center justify-center shadow-md">
|
||||||
<TrendingUp className="w-4 h-4 sm:w-5 sm:h-5 text-white" />
|
<TrendingUp className="w-4 h-4 sm:w-5 sm:h-5 text-white" />
|
||||||
</div>
|
</div>
|
||||||
<span className="truncate">Task Progress</span>
|
<span className="truncate">Task Progress</span>
|
||||||
</h3>
|
</h3>
|
||||||
<span className="text-xl sm:text-2xl font-bold bg-gradient-to-r from-[#d4af37] to-[#c9a227] bg-clip-text text-transparent flex-shrink-0">{progress}%</span>
|
<span className="text-xl sm:text-2xl font-bold bg-gradient-to-r from-[var(--luxury-gold)] to-[var(--luxury-gold-dark)] bg-clip-text text-transparent flex-shrink-0">{progress}%</span>
|
||||||
</div>
|
</div>
|
||||||
<div className="relative w-full h-3 sm:h-3.5 bg-gray-200/50 rounded-full overflow-hidden shadow-inner border border-gray-300/30">
|
<div className="relative w-full h-3 sm:h-3.5 bg-gray-200/50 rounded-full overflow-hidden shadow-inner border border-gray-300/30">
|
||||||
<div
|
<div
|
||||||
className="absolute inset-y-0 left-0 bg-gradient-to-r from-[#d4af37] via-[#c9a227] to-[#d4af37] rounded-full transition-all duration-700 ease-out shadow-md"
|
className="absolute inset-y-0 left-0 bg-gradient-to-r from-[var(--luxury-gold)] via-[var(--luxury-gold-dark)] to-[var(--luxury-gold)] rounded-full transition-all duration-700 ease-out shadow-md"
|
||||||
style={{ width: `${progress}%` }}
|
style={{ width: `${progress}%` }}
|
||||||
>
|
>
|
||||||
<div className="absolute inset-0 bg-gradient-to-r from-transparent via-white/30 to-transparent animate-shimmer"></div>
|
<div className="absolute inset-0 bg-gradient-to-r from-transparent via-white/30 to-transparent animate-shimmer"></div>
|
||||||
@@ -217,7 +217,7 @@ const TaskDetailModal: React.FC<TaskModalProps> = ({
|
|||||||
{task.checklist_items && task.checklist_items.length > 0 && (
|
{task.checklist_items && task.checklist_items.length > 0 && (
|
||||||
<div className="space-y-2.5">
|
<div className="space-y-2.5">
|
||||||
<h3 className="text-sm sm:text-base font-bold text-gray-900 flex items-center space-x-2">
|
<h3 className="text-sm sm:text-base font-bold text-gray-900 flex items-center space-x-2">
|
||||||
<div className="w-8 h-8 rounded-lg bg-gradient-to-br from-[#d4af37] to-[#c9a227] flex items-center justify-center shadow-md">
|
<div className="w-8 h-8 rounded-lg bg-gradient-to-br from-[var(--luxury-gold)] to-[var(--luxury-gold-dark)] flex items-center justify-center shadow-md">
|
||||||
<Award className="w-4 h-4 sm:w-5 sm:h-5 text-white" />
|
<Award className="w-4 h-4 sm:w-5 sm:h-5 text-white" />
|
||||||
</div>
|
</div>
|
||||||
<span>Checklist Items</span>
|
<span>Checklist Items</span>
|
||||||
@@ -229,11 +229,11 @@ const TaskDetailModal: React.FC<TaskModalProps> = ({
|
|||||||
className={`group relative flex items-start space-x-2.5 sm:space-x-3 p-2.5 sm:p-3 rounded-lg sm:rounded-xl border transition-all duration-300 cursor-pointer overflow-hidden ${
|
className={`group relative flex items-start space-x-2.5 sm:space-x-3 p-2.5 sm:p-3 rounded-lg sm:rounded-xl border transition-all duration-300 cursor-pointer overflow-hidden ${
|
||||||
item.completed
|
item.completed
|
||||||
? 'bg-gradient-to-br from-green-50 via-emerald-50/50 to-green-50 border-green-300/50 hover:border-green-400/70'
|
? 'bg-gradient-to-br from-green-50 via-emerald-50/50 to-green-50 border-green-300/50 hover:border-green-400/70'
|
||||||
: 'bg-white border-gray-200/50 hover:border-[#d4af37]/50 hover:shadow-md hover:bg-gradient-to-br hover:from-[#d4af37]/5 hover:to-transparent'
|
: 'bg-white border-gray-200/50 hover:border-[var(--luxury-gold)]/50 hover:shadow-md hover:bg-gradient-to-br hover:from-[var(--luxury-gold)]/5 hover:to-transparent'
|
||||||
}`}
|
}`}
|
||||||
>
|
>
|
||||||
{!item.completed && (
|
{!item.completed && (
|
||||||
<div className="absolute inset-0 bg-gradient-to-r from-[#d4af37]/0 via-[#d4af37]/5 to-[#d4af37]/0 opacity-0 group-hover:opacity-100 transition-opacity duration-300"></div>
|
<div className="absolute inset-0 bg-gradient-to-r from-[var(--luxury-gold)]/0 via-[var(--luxury-gold)]/5 to-[var(--luxury-gold)]/0 opacity-0 group-hover:opacity-100 transition-opacity duration-300"></div>
|
||||||
)}
|
)}
|
||||||
<input
|
<input
|
||||||
type="checkbox"
|
type="checkbox"
|
||||||
@@ -243,7 +243,7 @@ const TaskDetailModal: React.FC<TaskModalProps> = ({
|
|||||||
onUpdateChecklist(task, index, e.target.checked);
|
onUpdateChecklist(task, index, e.target.checked);
|
||||||
}}
|
}}
|
||||||
disabled={isUpdating || task.status === 'completed'}
|
disabled={isUpdating || task.status === 'completed'}
|
||||||
className="relative mt-0.5 h-4 w-4 sm:h-5 sm:w-5 text-[#d4af37] focus:ring-2 focus:ring-[#d4af37] focus:ring-offset-1 border-2 border-gray-300 rounded-md disabled:opacity-50 disabled:cursor-not-allowed flex-shrink-0 transition-all duration-200 checked:bg-[#d4af37] checked:border-[#d4af37]"
|
className="relative mt-0.5 h-4 w-4 sm:h-5 sm:w-5 text-[var(--luxury-gold)] focus:ring-2 focus:ring-[var(--luxury-gold)] focus:ring-offset-1 border-2 border-gray-300 rounded-md disabled:opacity-50 disabled:cursor-not-allowed flex-shrink-0 transition-all duration-200 checked:bg-[var(--luxury-gold)] checked:border-[var(--luxury-gold)]"
|
||||||
/>
|
/>
|
||||||
<div className="relative flex-1 min-w-0">
|
<div className="relative flex-1 min-w-0">
|
||||||
<span
|
<span
|
||||||
@@ -284,7 +284,7 @@ const TaskDetailModal: React.FC<TaskModalProps> = ({
|
|||||||
<div className="space-y-2.5">
|
<div className="space-y-2.5">
|
||||||
<div className="flex items-center justify-between">
|
<div className="flex items-center justify-between">
|
||||||
<h3 className="text-sm sm:text-base font-bold text-gray-900 flex items-center space-x-2">
|
<h3 className="text-sm sm:text-base font-bold text-gray-900 flex items-center space-x-2">
|
||||||
<div className="w-8 h-8 rounded-lg bg-gradient-to-br from-[#d4af37] to-[#c9a227] flex items-center justify-center shadow-md">
|
<div className="w-8 h-8 rounded-lg bg-gradient-to-br from-[var(--luxury-gold)] to-[var(--luxury-gold-dark)] flex items-center justify-center shadow-md">
|
||||||
<Camera className="w-4 h-4 sm:w-5 sm:h-5 text-white" />
|
<Camera className="w-4 h-4 sm:w-5 sm:h-5 text-white" />
|
||||||
</div>
|
</div>
|
||||||
<span>Photos</span>
|
<span>Photos</span>
|
||||||
@@ -329,7 +329,7 @@ const TaskDetailModal: React.FC<TaskModalProps> = ({
|
|||||||
<img
|
<img
|
||||||
src={fullUrl}
|
src={fullUrl}
|
||||||
alt={`Task photo ${index + 1}`}
|
alt={`Task photo ${index + 1}`}
|
||||||
className="w-full h-24 sm:h-32 object-cover rounded-lg border border-gray-200 hover:border-[#d4af37] transition-all duration-200 cursor-pointer"
|
className="w-full h-24 sm:h-32 object-cover rounded-lg border border-gray-200 hover:border-[var(--luxury-gold)] transition-all duration-200 cursor-pointer"
|
||||||
onClick={() => window.open(fullUrl, '_blank')}
|
onClick={() => window.open(fullUrl, '_blank')}
|
||||||
/>
|
/>
|
||||||
{task.id && task.status !== 'completed' && (
|
{task.id && task.status !== 'completed' && (
|
||||||
@@ -358,7 +358,7 @@ const TaskDetailModal: React.FC<TaskModalProps> = ({
|
|||||||
)}
|
)}
|
||||||
{uploadingPhoto && (
|
{uploadingPhoto && (
|
||||||
<div className="text-center py-2">
|
<div className="text-center py-2">
|
||||||
<RefreshCw className="w-4 h-4 animate-spin text-[#d4af37] mx-auto" />
|
<RefreshCw className="w-4 h-4 animate-spin text-[var(--luxury-gold)] mx-auto" />
|
||||||
<p className="text-xs text-gray-500 mt-1">Uploading photo...</p>
|
<p className="text-xs text-gray-500 mt-1">Uploading photo...</p>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
@@ -367,7 +367,7 @@ const TaskDetailModal: React.FC<TaskModalProps> = ({
|
|||||||
|
|
||||||
{/* Luxury Footer Actions - Sticky and Compact */}
|
{/* Luxury Footer Actions - Sticky and Compact */}
|
||||||
<div className="relative p-3 sm:p-4 bg-gradient-to-br from-gray-50 via-white to-gray-50 border-t border-gray-200/50 flex flex-col sm:flex-row items-stretch sm:items-center justify-between gap-2 sm:gap-3 flex-shrink-0 shadow-lg">
|
<div className="relative p-3 sm:p-4 bg-gradient-to-br from-gray-50 via-white to-gray-50 border-t border-gray-200/50 flex flex-col sm:flex-row items-stretch sm:items-center justify-between gap-2 sm:gap-3 flex-shrink-0 shadow-lg">
|
||||||
<div className="absolute inset-0 bg-gradient-to-r from-[#d4af37]/5 via-transparent to-[#d4af37]/5 opacity-50"></div>
|
<div className="absolute inset-0 bg-gradient-to-r from-[var(--luxury-gold)]/5 via-transparent to-[var(--luxury-gold)]/5 opacity-50"></div>
|
||||||
<div className="flex items-center gap-2">
|
<div className="flex items-center gap-2">
|
||||||
{task.id && task.status !== 'completed' && (
|
{task.id && task.status !== 'completed' && (
|
||||||
<button
|
<button
|
||||||
@@ -1344,12 +1344,12 @@ const HousekeepingDashboardPage: React.FC = () => {
|
|||||||
Enterprise Dashboard
|
Enterprise Dashboard
|
||||||
</h1>
|
</h1>
|
||||||
<p className="text-xs sm:text-sm md:text-base text-gray-600 font-light truncate">
|
<p className="text-xs sm:text-sm md:text-base text-gray-600 font-light truncate">
|
||||||
Welcome back, <span className="font-semibold text-[#d4af37]">{userInfo?.name || userInfo?.email || 'Housekeeping Staff'}</span>
|
Welcome back, <span className="font-semibold text-[var(--luxury-gold)]">{userInfo?.name || userInfo?.email || 'Housekeeping Staff'}</span>
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
<button
|
<button
|
||||||
onClick={() => activeTab === 'tasks' ? fetchTasks() : fetchInspections()}
|
onClick={() => activeTab === 'tasks' ? fetchTasks() : fetchInspections()}
|
||||||
className="w-full sm:w-auto flex items-center justify-center space-x-2 px-4 sm:px-6 py-2 sm:py-3 bg-gradient-to-r from-[#d4af37] to-[#c9a227] text-white rounded-lg hover:from-[#f5d76e] hover:to-[#d4af37] transition-all duration-300 shadow-lg hover:shadow-xl hover:-translate-y-0.5 text-sm sm:text-base font-medium flex-shrink-0"
|
className="w-full sm:w-auto flex items-center justify-center space-x-2 px-4 sm:px-6 py-2 sm:py-3 bg-gradient-to-r from-[var(--luxury-gold)] to-[var(--luxury-gold-dark)] text-white rounded-lg hover:from-[var(--luxury-gold-light)] hover:to-[var(--luxury-gold)] transition-all duration-300 shadow-lg hover:shadow-xl hover:-translate-y-0.5 text-sm sm:text-base font-medium flex-shrink-0"
|
||||||
>
|
>
|
||||||
<RefreshCw className="w-4 h-4 sm:w-5 sm:h-5" />
|
<RefreshCw className="w-4 h-4 sm:w-5 sm:h-5" />
|
||||||
<span>Refresh</span>
|
<span>Refresh</span>
|
||||||
@@ -1365,7 +1365,7 @@ const HousekeepingDashboardPage: React.FC = () => {
|
|||||||
onClick={() => setActiveTab('tasks')}
|
onClick={() => setActiveTab('tasks')}
|
||||||
className={`flex-1 px-4 sm:px-6 py-2.5 sm:py-3 rounded-lg sm:rounded-xl font-semibold text-sm sm:text-base transition-all duration-300 ${
|
className={`flex-1 px-4 sm:px-6 py-2.5 sm:py-3 rounded-lg sm:rounded-xl font-semibold text-sm sm:text-base transition-all duration-300 ${
|
||||||
activeTab === 'tasks'
|
activeTab === 'tasks'
|
||||||
? 'bg-gradient-to-r from-[#d4af37] to-[#c9a227] text-white shadow-lg'
|
? 'bg-gradient-to-r from-[var(--luxury-gold)] to-[var(--luxury-gold-dark)] text-white shadow-lg'
|
||||||
: 'bg-white text-gray-700 hover:bg-gray-100'
|
: 'bg-white text-gray-700 hover:bg-gray-100'
|
||||||
}`}
|
}`}
|
||||||
>
|
>
|
||||||
@@ -1378,7 +1378,7 @@ const HousekeepingDashboardPage: React.FC = () => {
|
|||||||
onClick={() => setActiveTab('inspections')}
|
onClick={() => setActiveTab('inspections')}
|
||||||
className={`flex-1 px-4 sm:px-6 py-2.5 sm:py-3 rounded-lg sm:rounded-xl font-semibold text-sm sm:text-base transition-all duration-300 ${
|
className={`flex-1 px-4 sm:px-6 py-2.5 sm:py-3 rounded-lg sm:rounded-xl font-semibold text-sm sm:text-base transition-all duration-300 ${
|
||||||
activeTab === 'inspections'
|
activeTab === 'inspections'
|
||||||
? 'bg-gradient-to-r from-[#d4af37] to-[#c9a227] text-white shadow-lg'
|
? 'bg-gradient-to-r from-[var(--luxury-gold)] to-[var(--luxury-gold-dark)] text-white shadow-lg'
|
||||||
: 'bg-white text-gray-700 hover:bg-gray-100'
|
: 'bg-white text-gray-700 hover:bg-gray-100'
|
||||||
}`}
|
}`}
|
||||||
>
|
>
|
||||||
@@ -1422,7 +1422,7 @@ const HousekeepingDashboardPage: React.FC = () => {
|
|||||||
type="date"
|
type="date"
|
||||||
value={filters.date}
|
value={filters.date}
|
||||||
onChange={(e) => setFilters({ ...filters, date: e.target.value })}
|
onChange={(e) => setFilters({ ...filters, date: e.target.value })}
|
||||||
className="w-full border border-gray-300 rounded-lg px-3 py-2 text-sm focus:outline-none focus:ring-2 focus:ring-[#d4af37]"
|
className="w-full border border-gray-300 rounded-lg px-3 py-2 text-sm focus:outline-none focus:ring-2 focus:ring-[var(--luxury-gold)]"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div className="flex-1 min-w-0">
|
<div className="flex-1 min-w-0">
|
||||||
@@ -1430,7 +1430,7 @@ const HousekeepingDashboardPage: React.FC = () => {
|
|||||||
<select
|
<select
|
||||||
value={filters.status}
|
value={filters.status}
|
||||||
onChange={(e) => setFilters({ ...filters, status: e.target.value })}
|
onChange={(e) => setFilters({ ...filters, status: e.target.value })}
|
||||||
className="w-full border border-gray-300 rounded-lg px-3 py-2 text-sm focus:outline-none focus:ring-2 focus:ring-[#d4af37]"
|
className="w-full border border-gray-300 rounded-lg px-3 py-2 text-sm focus:outline-none focus:ring-2 focus:ring-[var(--luxury-gold)]"
|
||||||
>
|
>
|
||||||
<option value="">All Statuses</option>
|
<option value="">All Statuses</option>
|
||||||
<option value="pending">Pending</option>
|
<option value="pending">Pending</option>
|
||||||
@@ -1444,7 +1444,7 @@ const HousekeepingDashboardPage: React.FC = () => {
|
|||||||
<select
|
<select
|
||||||
value={filters.task_type}
|
value={filters.task_type}
|
||||||
onChange={(e) => setFilters({ ...filters, task_type: e.target.value })}
|
onChange={(e) => setFilters({ ...filters, task_type: e.target.value })}
|
||||||
className="w-full border border-gray-300 rounded-lg px-3 py-2 text-sm focus:outline-none focus:ring-2 focus:ring-[#d4af37]"
|
className="w-full border border-gray-300 rounded-lg px-3 py-2 text-sm focus:outline-none focus:ring-2 focus:ring-[var(--luxury-gold)]"
|
||||||
>
|
>
|
||||||
<option value="">All Types</option>
|
<option value="">All Types</option>
|
||||||
<option value="checkout">Checkout</option>
|
<option value="checkout">Checkout</option>
|
||||||
@@ -1472,7 +1472,7 @@ const HousekeepingDashboardPage: React.FC = () => {
|
|||||||
<div className="enterprise-card p-4 sm:p-5 md:p-6">
|
<div className="enterprise-card p-4 sm:p-5 md:p-6">
|
||||||
<div className="flex items-center justify-between mb-4 sm:mb-5">
|
<div className="flex items-center justify-between mb-4 sm:mb-5">
|
||||||
<h2 className="text-lg sm:text-xl md:text-2xl font-serif font-bold text-gray-900 flex items-center gap-2 sm:gap-3">
|
<h2 className="text-lg sm:text-xl md:text-2xl font-serif font-bold text-gray-900 flex items-center gap-2 sm:gap-3">
|
||||||
<Target className="w-5 h-5 sm:w-6 sm:h-6 text-[#d4af37]" />
|
<Target className="w-5 h-5 sm:w-6 sm:h-6 text-[var(--luxury-gold)]" />
|
||||||
<span>Your Performance Today</span>
|
<span>Your Performance Today</span>
|
||||||
</h2>
|
</h2>
|
||||||
</div>
|
</div>
|
||||||
@@ -1511,9 +1511,9 @@ const HousekeepingDashboardPage: React.FC = () => {
|
|||||||
</p>
|
</p>
|
||||||
<p className="text-[9px] sm:text-xs text-gray-500 mt-1">vs estimated</p>
|
<p className="text-[9px] sm:text-xs text-gray-500 mt-1">vs estimated</p>
|
||||||
</div>
|
</div>
|
||||||
<div className="p-3 sm:p-4 bg-gradient-to-br from-[#d4af37]/10 to-[#c9a227]/10 rounded-lg sm:rounded-xl border border-[#d4af37]/20">
|
<div className="p-3 sm:p-4 bg-gradient-to-br from-[var(--luxury-gold)]/10 to-[var(--luxury-gold-dark)]/10 rounded-lg sm:rounded-xl border border-[var(--luxury-gold)]/20">
|
||||||
<div className="flex items-center gap-2 mb-2">
|
<div className="flex items-center gap-2 mb-2">
|
||||||
<Award className="w-4 h-4 sm:w-5 sm:h-5 text-[#d4af37]" />
|
<Award className="w-4 h-4 sm:w-5 sm:h-5 text-[var(--luxury-gold)]" />
|
||||||
<p className="text-[10px] sm:text-xs text-gray-600 font-medium">Quality</p>
|
<p className="text-[10px] sm:text-xs text-gray-600 font-medium">Quality</p>
|
||||||
</div>
|
</div>
|
||||||
<p className="text-xl sm:text-2xl font-bold text-gray-900">
|
<p className="text-xl sm:text-2xl font-bold text-gray-900">
|
||||||
@@ -1580,13 +1580,13 @@ const HousekeepingDashboardPage: React.FC = () => {
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="enterprise-card p-4 sm:p-5 md:p-6 relative overflow-hidden group hover:scale-105 transition-transform duration-300">
|
<div className="enterprise-card p-4 sm:p-5 md:p-6 relative overflow-hidden group hover:scale-105 transition-transform duration-300">
|
||||||
<div className="absolute top-0 right-0 w-24 h-24 sm:w-32 sm:h-32 bg-gradient-to-br from-[#d4af37]/20 to-[#c9a227]/20 rounded-full blur-2xl"></div>
|
<div className="absolute top-0 right-0 w-24 h-24 sm:w-32 sm:h-32 bg-gradient-to-br from-[var(--luxury-gold)]/20 to-[var(--luxury-gold-dark)]/20 rounded-full blur-2xl"></div>
|
||||||
<div className="relative">
|
<div className="relative">
|
||||||
<div className="flex items-center justify-between mb-3 sm:mb-4">
|
<div className="flex items-center justify-between mb-3 sm:mb-4">
|
||||||
<div className="w-10 h-10 sm:w-12 sm:h-12 rounded-xl bg-gradient-to-br from-[#d4af37] to-[#c9a227] flex items-center justify-center shadow-lg flex-shrink-0">
|
<div className="w-10 h-10 sm:w-12 sm:h-12 rounded-xl bg-gradient-to-br from-[var(--luxury-gold)] to-[var(--luxury-gold-dark)] flex items-center justify-center shadow-lg flex-shrink-0">
|
||||||
<Sparkles className="w-5 h-5 sm:w-6 sm:h-6 text-white" />
|
<Sparkles className="w-5 h-5 sm:w-6 sm:h-6 text-white" />
|
||||||
</div>
|
</div>
|
||||||
<span className="text-[10px] sm:text-xs font-semibold text-[#c9a227] bg-[#d4af37]/10 px-2 sm:px-3 py-0.5 sm:py-1 rounded-full whitespace-nowrap">TOTAL</span>
|
<span className="text-[10px] sm:text-xs font-semibold text-[var(--luxury-gold-dark)] bg-[var(--luxury-gold)]/10 px-2 sm:px-3 py-0.5 sm:py-1 rounded-full whitespace-nowrap">TOTAL</span>
|
||||||
</div>
|
</div>
|
||||||
<p className="text-xs sm:text-sm font-medium text-gray-600 mb-1">Total Today</p>
|
<p className="text-xs sm:text-sm font-medium text-gray-600 mb-1">Total Today</p>
|
||||||
<p className="text-2xl sm:text-3xl font-bold text-gray-900">{stats.total}</p>
|
<p className="text-2xl sm:text-3xl font-bold text-gray-900">{stats.total}</p>
|
||||||
@@ -1600,7 +1600,7 @@ const HousekeepingDashboardPage: React.FC = () => {
|
|||||||
<div className="space-y-4 sm:space-y-5">
|
<div className="space-y-4 sm:space-y-5">
|
||||||
<div className="flex flex-col sm:flex-row items-start sm:items-center justify-between gap-3 sm:gap-4">
|
<div className="flex flex-col sm:flex-row items-start sm:items-center justify-between gap-3 sm:gap-4">
|
||||||
<h2 className="text-xl sm:text-2xl font-serif font-bold text-gray-900 flex items-center gap-2">
|
<h2 className="text-xl sm:text-2xl font-serif font-bold text-gray-900 flex items-center gap-2">
|
||||||
<Eye className="w-5 h-5 sm:w-6 sm:h-6 text-[#d4af37]" />
|
<Eye className="w-5 h-5 sm:w-6 sm:h-6 text-[var(--luxury-gold)]" />
|
||||||
<span>Room Inspections</span>
|
<span>Room Inspections</span>
|
||||||
</h2>
|
</h2>
|
||||||
<button
|
<button
|
||||||
@@ -1609,7 +1609,7 @@ const HousekeepingDashboardPage: React.FC = () => {
|
|||||||
await fetchRoomsForInspection();
|
await fetchRoomsForInspection();
|
||||||
setShowCreateInspectionModal(true);
|
setShowCreateInspectionModal(true);
|
||||||
}}
|
}}
|
||||||
className="w-full sm:w-auto flex items-center justify-center space-x-2 px-4 sm:px-6 py-2 sm:py-3 bg-gradient-to-r from-[#d4af37] to-[#c9a227] text-white rounded-lg hover:from-[#f5d76e] hover:to-[#d4af37] transition-all duration-300 shadow-lg hover:shadow-xl text-sm sm:text-base font-medium"
|
className="w-full sm:w-auto flex items-center justify-center space-x-2 px-4 sm:px-6 py-2 sm:py-3 bg-gradient-to-r from-[var(--luxury-gold)] to-[var(--luxury-gold-dark)] text-white rounded-lg hover:from-[var(--luxury-gold-light)] hover:to-[var(--luxury-gold)] transition-all duration-300 shadow-lg hover:shadow-xl text-sm sm:text-base font-medium"
|
||||||
>
|
>
|
||||||
<Plus className="w-4 h-4 sm:w-5 sm:h-5" />
|
<Plus className="w-4 h-4 sm:w-5 sm:h-5" />
|
||||||
<span>New Inspection</span>
|
<span>New Inspection</span>
|
||||||
@@ -1620,8 +1620,8 @@ const HousekeepingDashboardPage: React.FC = () => {
|
|||||||
<Loading />
|
<Loading />
|
||||||
) : inspections.length === 0 ? (
|
) : inspections.length === 0 ? (
|
||||||
<div className="enterprise-card p-8 sm:p-12 text-center">
|
<div className="enterprise-card p-8 sm:p-12 text-center">
|
||||||
<div className="w-16 h-16 sm:w-20 sm:h-20 rounded-full bg-gradient-to-br from-[#d4af37]/20 to-[#c9a227]/20 flex items-center justify-center mx-auto mb-3 sm:mb-4">
|
<div className="w-16 h-16 sm:w-20 sm:h-20 rounded-full bg-gradient-to-br from-[var(--luxury-gold)]/20 to-[var(--luxury-gold-dark)]/20 flex items-center justify-center mx-auto mb-3 sm:mb-4">
|
||||||
<Eye className="w-8 h-8 sm:w-10 sm:h-10 text-[#d4af37]" />
|
<Eye className="w-8 h-8 sm:w-10 sm:h-10 text-[var(--luxury-gold)]" />
|
||||||
</div>
|
</div>
|
||||||
<h3 className="text-lg sm:text-xl font-semibold text-gray-900 mb-2">No inspections found</h3>
|
<h3 className="text-lg sm:text-xl font-semibold text-gray-900 mb-2">No inspections found</h3>
|
||||||
<p className="text-sm sm:text-base text-gray-500">You don't have any inspections assigned yet.</p>
|
<p className="text-sm sm:text-base text-gray-500">You don't have any inspections assigned yet.</p>
|
||||||
@@ -1640,7 +1640,7 @@ const HousekeepingDashboardPage: React.FC = () => {
|
|||||||
<div className="flex items-start justify-between mb-3 sm:mb-4 gap-3">
|
<div className="flex items-start justify-between mb-3 sm:mb-4 gap-3">
|
||||||
<div className="flex-1 min-w-0">
|
<div className="flex-1 min-w-0">
|
||||||
<div className="flex items-center space-x-2 sm:space-x-3 mb-2">
|
<div className="flex items-center space-x-2 sm:space-x-3 mb-2">
|
||||||
<div className="w-8 h-8 sm:w-10 sm:h-10 rounded-lg bg-gradient-to-br from-[#d4af37] to-[#c9a227] flex items-center justify-center shadow-lg flex-shrink-0">
|
<div className="w-8 h-8 sm:w-10 sm:h-10 rounded-lg bg-gradient-to-br from-[var(--luxury-gold)] to-[var(--luxury-gold-dark)] flex items-center justify-center shadow-lg flex-shrink-0">
|
||||||
<MapPin className="w-4 h-4 sm:w-5 sm:h-5 text-white" />
|
<MapPin className="w-4 h-4 sm:w-5 sm:h-5 text-white" />
|
||||||
</div>
|
</div>
|
||||||
<div className="min-w-0 flex-1">
|
<div className="min-w-0 flex-1">
|
||||||
@@ -1664,7 +1664,7 @@ const HousekeepingDashboardPage: React.FC = () => {
|
|||||||
|
|
||||||
<div className="flex items-center space-x-4 text-xs sm:text-sm text-gray-600 mb-3 sm:mb-4">
|
<div className="flex items-center space-x-4 text-xs sm:text-sm text-gray-600 mb-3 sm:mb-4">
|
||||||
<div className="flex items-center space-x-1.5">
|
<div className="flex items-center space-x-1.5">
|
||||||
<Calendar className="w-3.5 h-3.5 sm:w-4 sm:h-4 text-[#d4af37] flex-shrink-0" />
|
<Calendar className="w-3.5 h-3.5 sm:w-4 sm:h-4 text-[var(--luxury-gold)] flex-shrink-0" />
|
||||||
<span className="text-[10px] sm:text-xs truncate">{formatDate(inspection.scheduled_at)}</span>
|
<span className="text-[10px] sm:text-xs truncate">{formatDate(inspection.scheduled_at)}</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -1673,11 +1673,11 @@ const HousekeepingDashboardPage: React.FC = () => {
|
|||||||
<div className="mb-3 sm:mb-4">
|
<div className="mb-3 sm:mb-4">
|
||||||
<div className="flex items-center justify-between mb-1.5 sm:mb-2">
|
<div className="flex items-center justify-between mb-1.5 sm:mb-2">
|
||||||
<span className="text-[10px] sm:text-xs font-medium text-gray-600">Quality Score</span>
|
<span className="text-[10px] sm:text-xs font-medium text-gray-600">Quality Score</span>
|
||||||
<span className="text-xs sm:text-sm font-bold text-[#d4af37]">{inspection.overall_score.toFixed(1)}/5.0</span>
|
<span className="text-xs sm:text-sm font-bold text-[var(--luxury-gold)]">{inspection.overall_score.toFixed(1)}/5.0</span>
|
||||||
</div>
|
</div>
|
||||||
<div className="relative w-full h-1.5 sm:h-2 bg-gray-200 rounded-full overflow-hidden">
|
<div className="relative w-full h-1.5 sm:h-2 bg-gray-200 rounded-full overflow-hidden">
|
||||||
<div
|
<div
|
||||||
className="absolute inset-y-0 left-0 bg-gradient-to-r from-[#d4af37] to-[#c9a227] rounded-full transition-all duration-500"
|
className="absolute inset-y-0 left-0 bg-gradient-to-r from-[var(--luxury-gold)] to-[var(--luxury-gold-dark)] rounded-full transition-all duration-500"
|
||||||
style={{ width: `${(inspection.overall_score / 5) * 100}%` }}
|
style={{ width: `${(inspection.overall_score / 5) * 100}%` }}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
@@ -1709,7 +1709,7 @@ const HousekeepingDashboardPage: React.FC = () => {
|
|||||||
<div className="space-y-4 sm:space-y-5">
|
<div className="space-y-4 sm:space-y-5">
|
||||||
<div className="flex flex-col sm:flex-row items-start sm:items-center justify-between gap-2 sm:gap-4">
|
<div className="flex flex-col sm:flex-row items-start sm:items-center justify-between gap-2 sm:gap-4">
|
||||||
<h2 className="text-xl sm:text-2xl font-serif font-bold text-gray-900 flex items-center gap-2">
|
<h2 className="text-xl sm:text-2xl font-serif font-bold text-gray-900 flex items-center gap-2">
|
||||||
<Building2 className="w-5 h-5 sm:w-6 sm:h-6 text-[#d4af37]" />
|
<Building2 className="w-5 h-5 sm:w-6 sm:h-6 text-[var(--luxury-gold)]" />
|
||||||
<span>Today's Tasks by Floor</span>
|
<span>Today's Tasks by Floor</span>
|
||||||
</h2>
|
</h2>
|
||||||
<span className="text-xs sm:text-sm text-gray-500">{tasks.length} task{tasks.length !== 1 ? 's' : ''} across {tasksByFloor.length} floor{tasksByFloor.length !== 1 ? 's' : ''}</span>
|
<span className="text-xs sm:text-sm text-gray-500">{tasks.length} task{tasks.length !== 1 ? 's' : ''} across {tasksByFloor.length} floor{tasksByFloor.length !== 1 ? 's' : ''}</span>
|
||||||
@@ -1717,8 +1717,8 @@ const HousekeepingDashboardPage: React.FC = () => {
|
|||||||
|
|
||||||
{tasks.length === 0 ? (
|
{tasks.length === 0 ? (
|
||||||
<div className="enterprise-card p-8 sm:p-12 text-center">
|
<div className="enterprise-card p-8 sm:p-12 text-center">
|
||||||
<div className="w-16 h-16 sm:w-20 sm:h-20 rounded-full bg-gradient-to-br from-[#d4af37]/20 to-[#c9a227]/20 flex items-center justify-center mx-auto mb-3 sm:mb-4">
|
<div className="w-16 h-16 sm:w-20 sm:h-20 rounded-full bg-gradient-to-br from-[var(--luxury-gold)]/20 to-[var(--luxury-gold-dark)]/20 flex items-center justify-center mx-auto mb-3 sm:mb-4">
|
||||||
<Sparkles className="w-8 h-8 sm:w-10 sm:h-10 text-[#d4af37]" />
|
<Sparkles className="w-8 h-8 sm:w-10 sm:h-10 text-[var(--luxury-gold)]" />
|
||||||
</div>
|
</div>
|
||||||
<h3 className="text-lg sm:text-xl font-semibold text-gray-900 mb-2">No tasks assigned</h3>
|
<h3 className="text-lg sm:text-xl font-semibold text-gray-900 mb-2">No tasks assigned</h3>
|
||||||
<p className="text-sm sm:text-base text-gray-500">You don't have any housekeeping tasks assigned for today.</p>
|
<p className="text-sm sm:text-base text-gray-500">You don't have any housekeeping tasks assigned for today.</p>
|
||||||
@@ -1743,16 +1743,16 @@ const HousekeepingDashboardPage: React.FC = () => {
|
|||||||
onClick={() => setSelectedFloor(floor)}
|
onClick={() => setSelectedFloor(floor)}
|
||||||
className={`relative px-4 sm:px-6 py-2.5 sm:py-3 rounded-lg sm:rounded-xl font-semibold text-sm sm:text-base transition-all duration-300 flex items-center gap-2 sm:gap-3 whitespace-nowrap flex-shrink-0 ${
|
className={`relative px-4 sm:px-6 py-2.5 sm:py-3 rounded-lg sm:rounded-xl font-semibold text-sm sm:text-base transition-all duration-300 flex items-center gap-2 sm:gap-3 whitespace-nowrap flex-shrink-0 ${
|
||||||
isActive
|
isActive
|
||||||
? 'bg-gradient-to-r from-[#d4af37] to-[#c9a227] text-white shadow-lg scale-105'
|
? 'bg-gradient-to-r from-[var(--luxury-gold)] to-[var(--luxury-gold-dark)] text-white shadow-lg scale-105'
|
||||||
: 'bg-white text-gray-700 hover:bg-gray-100 border border-gray-200/50 hover:border-[#d4af37]/30'
|
: 'bg-white text-gray-700 hover:bg-gray-100 border border-gray-200/50 hover:border-[var(--luxury-gold)]/30'
|
||||||
}`}
|
}`}
|
||||||
>
|
>
|
||||||
<Building2 className={`w-4 h-4 sm:w-5 sm:h-5 ${isActive ? 'text-white' : 'text-[#d4af37]'}`} />
|
<Building2 className={`w-4 h-4 sm:w-5 sm:h-5 ${isActive ? 'text-white' : 'text-[var(--luxury-gold)]'}`} />
|
||||||
<span className="font-bold">{getFloorDisplayName(floor)}</span>
|
<span className="font-bold">{getFloorDisplayName(floor)}</span>
|
||||||
<span className={`px-2 py-0.5 rounded-full text-[10px] sm:text-xs font-bold ${
|
<span className={`px-2 py-0.5 rounded-full text-[10px] sm:text-xs font-bold ${
|
||||||
isActive
|
isActive
|
||||||
? 'bg-white/20 text-white'
|
? 'bg-white/20 text-white'
|
||||||
: 'bg-[#d4af37]/10 text-[#c9a227]'
|
: 'bg-[var(--luxury-gold)]/10 text-[var(--luxury-gold-dark)]'
|
||||||
}`}>
|
}`}>
|
||||||
{floorStats.total}
|
{floorStats.total}
|
||||||
</span>
|
</span>
|
||||||
@@ -1779,10 +1779,10 @@ const HousekeepingDashboardPage: React.FC = () => {
|
|||||||
{selectedFloor !== null && (
|
{selectedFloor !== null && (
|
||||||
<div className="space-y-4 sm:space-y-5">
|
<div className="space-y-4 sm:space-y-5">
|
||||||
{/* Floor Header with Stats */}
|
{/* Floor Header with Stats */}
|
||||||
<div className="enterprise-card p-4 sm:p-5 bg-gradient-to-r from-[#d4af37]/10 via-[#c9a227]/5 to-[#d4af37]/10 border border-[#d4af37]/20 rounded-xl sm:rounded-2xl">
|
<div className="enterprise-card p-4 sm:p-5 bg-gradient-to-r from-[var(--luxury-gold)]/10 via-[var(--luxury-gold-dark)]/5 to-[var(--luxury-gold)]/10 border border-[var(--luxury-gold)]/20 rounded-xl sm:rounded-2xl">
|
||||||
<div className="flex flex-col sm:flex-row items-start sm:items-center justify-between gap-3 sm:gap-4">
|
<div className="flex flex-col sm:flex-row items-start sm:items-center justify-between gap-3 sm:gap-4">
|
||||||
<div className="flex items-center gap-3 sm:gap-4">
|
<div className="flex items-center gap-3 sm:gap-4">
|
||||||
<div className="w-10 h-10 sm:w-12 sm:h-12 rounded-xl bg-gradient-to-br from-[#d4af37] to-[#c9a227] flex items-center justify-center shadow-lg">
|
<div className="w-10 h-10 sm:w-12 sm:h-12 rounded-xl bg-gradient-to-br from-[var(--luxury-gold)] to-[var(--luxury-gold-dark)] flex items-center justify-center shadow-lg">
|
||||||
<Building2 className="w-5 h-5 sm:w-6 sm:h-6 text-white" />
|
<Building2 className="w-5 h-5 sm:w-6 sm:h-6 text-white" />
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
@@ -1820,8 +1820,8 @@ const HousekeepingDashboardPage: React.FC = () => {
|
|||||||
{/* Tasks Grid */}
|
{/* Tasks Grid */}
|
||||||
{currentFloorTasks.length === 0 ? (
|
{currentFloorTasks.length === 0 ? (
|
||||||
<div className="enterprise-card p-8 sm:p-12 text-center">
|
<div className="enterprise-card p-8 sm:p-12 text-center">
|
||||||
<div className="w-16 h-16 sm:w-20 sm:h-20 rounded-full bg-gradient-to-br from-[#d4af37]/20 to-[#c9a227]/20 flex items-center justify-center mx-auto mb-3 sm:mb-4">
|
<div className="w-16 h-16 sm:w-20 sm:h-20 rounded-full bg-gradient-to-br from-[var(--luxury-gold)]/20 to-[var(--luxury-gold-dark)]/20 flex items-center justify-center mx-auto mb-3 sm:mb-4">
|
||||||
<Sparkles className="w-8 h-8 sm:w-10 sm:h-10 text-[#d4af37]" />
|
<Sparkles className="w-8 h-8 sm:w-10 sm:h-10 text-[var(--luxury-gold)]" />
|
||||||
</div>
|
</div>
|
||||||
<h3 className="text-lg sm:text-xl font-semibold text-gray-900 mb-2">No tasks on this floor</h3>
|
<h3 className="text-lg sm:text-xl font-semibold text-gray-900 mb-2">No tasks on this floor</h3>
|
||||||
<p className="text-sm sm:text-base text-gray-500">No housekeeping tasks assigned for {getFloorDisplayName(selectedFloor).toLowerCase()}.</p>
|
<p className="text-sm sm:text-base text-gray-500">No housekeeping tasks assigned for {getFloorDisplayName(selectedFloor).toLowerCase()}.</p>
|
||||||
@@ -1845,7 +1845,7 @@ const HousekeepingDashboardPage: React.FC = () => {
|
|||||||
<div className="flex items-start justify-between mb-3 sm:mb-4 gap-3">
|
<div className="flex items-start justify-between mb-3 sm:mb-4 gap-3">
|
||||||
<div className="flex-1 min-w-0">
|
<div className="flex-1 min-w-0">
|
||||||
<div className="flex items-center space-x-2 sm:space-x-3 mb-2">
|
<div className="flex items-center space-x-2 sm:space-x-3 mb-2">
|
||||||
<div className="w-8 h-8 sm:w-10 sm:h-10 rounded-lg bg-gradient-to-br from-[#d4af37] to-[#c9a227] flex items-center justify-center shadow-lg flex-shrink-0">
|
<div className="w-8 h-8 sm:w-10 sm:h-10 rounded-lg bg-gradient-to-br from-[var(--luxury-gold)] to-[var(--luxury-gold-dark)] flex items-center justify-center shadow-lg flex-shrink-0">
|
||||||
<MapPin className="w-4 h-4 sm:w-5 sm:h-5 text-white" />
|
<MapPin className="w-4 h-4 sm:w-5 sm:h-5 text-white" />
|
||||||
</div>
|
</div>
|
||||||
<div className="min-w-0 flex-1">
|
<div className="min-w-0 flex-1">
|
||||||
@@ -1863,7 +1863,7 @@ const HousekeepingDashboardPage: React.FC = () => {
|
|||||||
|
|
||||||
<div className="flex items-center space-x-4 text-xs sm:text-sm text-gray-600 mb-3 sm:mb-4">
|
<div className="flex items-center space-x-4 text-xs sm:text-sm text-gray-600 mb-3 sm:mb-4">
|
||||||
<div className="flex items-center space-x-1.5">
|
<div className="flex items-center space-x-1.5">
|
||||||
<Calendar className="w-3.5 h-3.5 sm:w-4 sm:h-4 text-[#d4af37] flex-shrink-0" />
|
<Calendar className="w-3.5 h-3.5 sm:w-4 sm:h-4 text-[var(--luxury-gold)] flex-shrink-0" />
|
||||||
<span className="text-[10px] sm:text-xs truncate">{formatDate(task.scheduled_time)}</span>
|
<span className="text-[10px] sm:text-xs truncate">{formatDate(task.scheduled_time)}</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -1872,7 +1872,7 @@ const HousekeepingDashboardPage: React.FC = () => {
|
|||||||
<div className="mb-3 sm:mb-4">
|
<div className="mb-3 sm:mb-4">
|
||||||
<div className="flex items-center justify-between mb-1.5 sm:mb-2">
|
<div className="flex items-center justify-between mb-1.5 sm:mb-2">
|
||||||
<span className="text-[10px] sm:text-xs font-medium text-gray-600">Progress</span>
|
<span className="text-[10px] sm:text-xs font-medium text-gray-600">Progress</span>
|
||||||
<span className="text-xs sm:text-sm font-bold text-[#d4af37]">{progress}%</span>
|
<span className="text-xs sm:text-sm font-bold text-[var(--luxury-gold)]">{progress}%</span>
|
||||||
</div>
|
</div>
|
||||||
<div className="relative w-full h-1.5 sm:h-2 bg-gray-200 rounded-full overflow-hidden">
|
<div className="relative w-full h-1.5 sm:h-2 bg-gray-200 rounded-full overflow-hidden">
|
||||||
<div
|
<div
|
||||||
@@ -1961,7 +1961,7 @@ const HousekeepingDashboardPage: React.FC = () => {
|
|||||||
<div className="p-6 border-b border-gray-200">
|
<div className="p-6 border-b border-gray-200">
|
||||||
<div className="flex items-center justify-between">
|
<div className="flex items-center justify-between">
|
||||||
<h2 className="text-xl font-bold text-gray-900 flex items-center gap-2">
|
<h2 className="text-xl font-bold text-gray-900 flex items-center gap-2">
|
||||||
<FileCheck className="w-5 h-5 text-[#d4af37]" />
|
<FileCheck className="w-5 h-5 text-[var(--luxury-gold)]" />
|
||||||
Create Room Inspection
|
Create Room Inspection
|
||||||
</h2>
|
</h2>
|
||||||
<button
|
<button
|
||||||
@@ -1983,7 +1983,7 @@ const HousekeepingDashboardPage: React.FC = () => {
|
|||||||
<select
|
<select
|
||||||
value={inspectionForm.room_id}
|
value={inspectionForm.room_id}
|
||||||
onChange={(e) => setInspectionForm({ ...inspectionForm, room_id: e.target.value })}
|
onChange={(e) => setInspectionForm({ ...inspectionForm, room_id: e.target.value })}
|
||||||
className="w-full border border-gray-300 rounded-lg px-3 py-2 text-sm focus:outline-none focus:ring-2 focus:ring-[#d4af37]"
|
className="w-full border border-gray-300 rounded-lg px-3 py-2 text-sm focus:outline-none focus:ring-2 focus:ring-[var(--luxury-gold)]"
|
||||||
required
|
required
|
||||||
>
|
>
|
||||||
<option value="">Select Room</option>
|
<option value="">Select Room</option>
|
||||||
@@ -2007,7 +2007,7 @@ const HousekeepingDashboardPage: React.FC = () => {
|
|||||||
checklist_items: defaultInspectionChecklist[type] || [],
|
checklist_items: defaultInspectionChecklist[type] || [],
|
||||||
});
|
});
|
||||||
}}
|
}}
|
||||||
className="w-full border border-gray-300 rounded-lg px-3 py-2 text-sm focus:outline-none focus:ring-2 focus:ring-[#d4af37]"
|
className="w-full border border-gray-300 rounded-lg px-3 py-2 text-sm focus:outline-none focus:ring-2 focus:ring-[var(--luxury-gold)]"
|
||||||
>
|
>
|
||||||
<option value="pre_checkin">Pre Check-in</option>
|
<option value="pre_checkin">Pre Check-in</option>
|
||||||
<option value="post_checkout">Post Checkout</option>
|
<option value="post_checkout">Post Checkout</option>
|
||||||
@@ -2025,7 +2025,7 @@ const HousekeepingDashboardPage: React.FC = () => {
|
|||||||
</button>
|
</button>
|
||||||
<button
|
<button
|
||||||
onClick={handleCreateInspection}
|
onClick={handleCreateInspection}
|
||||||
className="px-4 py-2 text-sm text-white bg-gradient-to-r from-[#d4af37] to-[#c9a227] rounded-lg hover:from-[#f5d76e] hover:to-[#d4af37] transition-colors font-medium"
|
className="px-4 py-2 text-sm text-white bg-gradient-to-r from-[var(--luxury-gold)] to-[var(--luxury-gold-dark)] rounded-lg hover:from-[var(--luxury-gold-light)] hover:to-[var(--luxury-gold)] transition-colors font-medium"
|
||||||
>
|
>
|
||||||
Create Inspection
|
Create Inspection
|
||||||
</button>
|
</button>
|
||||||
@@ -2039,7 +2039,7 @@ const HousekeepingDashboardPage: React.FC = () => {
|
|||||||
{isInspectionModalOpen && selectedInspection && (
|
{isInspectionModalOpen && selectedInspection && (
|
||||||
<div className="fixed inset-0 z-50 flex items-center justify-center p-4 bg-black/50 backdrop-blur-sm">
|
<div className="fixed inset-0 z-50 flex items-center justify-center p-4 bg-black/50 backdrop-blur-sm">
|
||||||
<div className="bg-white rounded-xl shadow-2xl max-w-3xl w-full max-h-[90vh] overflow-y-auto">
|
<div className="bg-white rounded-xl shadow-2xl max-w-3xl w-full max-h-[90vh] overflow-y-auto">
|
||||||
<div className="p-6 border-b border-gray-200 bg-gradient-to-r from-[#d4af37] to-[#c9a227]">
|
<div className="p-6 border-b border-gray-200 bg-gradient-to-r from-[var(--luxury-gold)] to-[var(--luxury-gold-dark)]">
|
||||||
<div className="flex items-center justify-between">
|
<div className="flex items-center justify-between">
|
||||||
<h2 className="text-xl font-bold text-white flex items-center gap-2">
|
<h2 className="text-xl font-bold text-white flex items-center gap-2">
|
||||||
<FileCheck className="w-5 h-5" />
|
<FileCheck className="w-5 h-5" />
|
||||||
@@ -2107,7 +2107,7 @@ const HousekeepingDashboardPage: React.FC = () => {
|
|||||||
{selectedInspection.overall_score && (
|
{selectedInspection.overall_score && (
|
||||||
<div>
|
<div>
|
||||||
<label className="block text-sm font-medium text-gray-700 mb-2">Overall Score</label>
|
<label className="block text-sm font-medium text-gray-700 mb-2">Overall Score</label>
|
||||||
<p className="text-2xl font-bold text-[#d4af37]">{selectedInspection.overall_score.toFixed(1)} / 5.0</p>
|
<p className="text-2xl font-bold text-[var(--luxury-gold)]">{selectedInspection.overall_score.toFixed(1)} / 5.0</p>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -379,7 +379,7 @@ const HousekeepingProfilePage: React.FC = () => {
|
|||||||
{/* Header */}
|
{/* Header */}
|
||||||
<div className="mb-6 sm:mb-8 animate-fade-in">
|
<div className="mb-6 sm:mb-8 animate-fade-in">
|
||||||
<div className="flex items-center gap-2 sm:gap-3 mb-2 sm:mb-3">
|
<div className="flex items-center gap-2 sm:gap-3 mb-2 sm:mb-3">
|
||||||
<div className="h-1 w-12 sm:w-16 md:w-20 bg-gradient-to-r from-[#d4af37] via-amber-400 to-[#d4af37] rounded-full"></div>
|
<div className="h-1 w-12 sm:w-16 md:w-20 bg-gradient-to-r from-[var(--luxury-gold)] via-amber-400 to-[var(--luxury-gold)] rounded-full"></div>
|
||||||
<h1 className="text-2xl sm:text-3xl lg:text-4xl font-serif font-bold bg-gradient-to-r from-gray-900 via-amber-900/90 to-gray-900 bg-clip-text text-transparent tracking-tight">
|
<h1 className="text-2xl sm:text-3xl lg:text-4xl font-serif font-bold bg-gradient-to-r from-gray-900 via-amber-900/90 to-gray-900 bg-clip-text text-transparent tracking-tight">
|
||||||
Housekeeping Profile
|
Housekeeping Profile
|
||||||
</h1>
|
</h1>
|
||||||
@@ -390,50 +390,50 @@ const HousekeepingProfilePage: React.FC = () => {
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Tabs */}
|
{/* Tabs */}
|
||||||
<div className="mb-4 sm:mb-6 border-b border-[#d4af37]/20 overflow-x-auto bg-white/50 backdrop-blur-sm rounded-t-lg sm:rounded-t-xl px-4 sm:px-6">
|
<div className="mb-4 sm:mb-6 border-b border-[var(--luxury-gold)]/20 overflow-x-auto bg-white/50 backdrop-blur-sm rounded-t-lg sm:rounded-t-xl px-4 sm:px-6">
|
||||||
<div className="flex space-x-4 sm:space-x-8 min-w-max">
|
<div className="flex space-x-4 sm:space-x-8 min-w-max">
|
||||||
<button
|
<button
|
||||||
onClick={() => setActiveTab('profile')}
|
onClick={() => setActiveTab('profile')}
|
||||||
className={`py-3 sm:py-4 px-2 sm:px-1 border-b-2 font-semibold text-xs sm:text-sm transition-all duration-300 whitespace-nowrap ${
|
className={`py-3 sm:py-4 px-2 sm:px-1 border-b-2 font-semibold text-xs sm:text-sm transition-all duration-300 whitespace-nowrap ${
|
||||||
activeTab === 'profile'
|
activeTab === 'profile'
|
||||||
? 'border-[#d4af37] text-[#d4af37] shadow-sm'
|
? 'border-[var(--luxury-gold)] text-[var(--luxury-gold)] shadow-sm'
|
||||||
: 'border-transparent text-gray-500 hover:text-[#d4af37] hover:border-[#d4af37]/30'
|
: 'border-transparent text-gray-500 hover:text-[var(--luxury-gold)] hover:border-[var(--luxury-gold)]/30'
|
||||||
}`}
|
}`}
|
||||||
>
|
>
|
||||||
<User className={`w-4 h-4 sm:w-5 sm:h-5 inline mr-2 ${activeTab === 'profile' ? 'text-[#d4af37]' : ''}`} />
|
<User className={`w-4 h-4 sm:w-5 sm:h-5 inline mr-2 ${activeTab === 'profile' ? 'text-[var(--luxury-gold)]' : ''}`} />
|
||||||
Profile
|
Profile
|
||||||
</button>
|
</button>
|
||||||
<button
|
<button
|
||||||
onClick={() => setActiveTab('password')}
|
onClick={() => setActiveTab('password')}
|
||||||
className={`py-3 sm:py-4 px-2 sm:px-1 border-b-2 font-semibold text-xs sm:text-sm transition-all duration-300 whitespace-nowrap ${
|
className={`py-3 sm:py-4 px-2 sm:px-1 border-b-2 font-semibold text-xs sm:text-sm transition-all duration-300 whitespace-nowrap ${
|
||||||
activeTab === 'password'
|
activeTab === 'password'
|
||||||
? 'border-[#d4af37] text-[#d4af37] shadow-sm'
|
? 'border-[var(--luxury-gold)] text-[var(--luxury-gold)] shadow-sm'
|
||||||
: 'border-transparent text-gray-500 hover:text-[#d4af37] hover:border-[#d4af37]/30'
|
: 'border-transparent text-gray-500 hover:text-[var(--luxury-gold)] hover:border-[var(--luxury-gold)]/30'
|
||||||
}`}
|
}`}
|
||||||
>
|
>
|
||||||
<KeyRound className={`w-4 h-4 sm:w-5 sm:h-5 inline mr-2 ${activeTab === 'password' ? 'text-[#d4af37]' : ''}`} />
|
<KeyRound className={`w-4 h-4 sm:w-5 sm:h-5 inline mr-2 ${activeTab === 'password' ? 'text-[var(--luxury-gold)]' : ''}`} />
|
||||||
Password
|
Password
|
||||||
</button>
|
</button>
|
||||||
<button
|
<button
|
||||||
onClick={() => setActiveTab('mfa')}
|
onClick={() => setActiveTab('mfa')}
|
||||||
className={`py-3 sm:py-4 px-2 sm:px-1 border-b-2 font-semibold text-xs sm:text-sm transition-all duration-300 whitespace-nowrap ${
|
className={`py-3 sm:py-4 px-2 sm:px-1 border-b-2 font-semibold text-xs sm:text-sm transition-all duration-300 whitespace-nowrap ${
|
||||||
activeTab === 'mfa'
|
activeTab === 'mfa'
|
||||||
? 'border-[#d4af37] text-[#d4af37] shadow-sm'
|
? 'border-[var(--luxury-gold)] text-[var(--luxury-gold)] shadow-sm'
|
||||||
: 'border-transparent text-gray-500 hover:text-[#d4af37] hover:border-[#d4af37]/30'
|
: 'border-transparent text-gray-500 hover:text-[var(--luxury-gold)] hover:border-[var(--luxury-gold)]/30'
|
||||||
}`}
|
}`}
|
||||||
>
|
>
|
||||||
<Shield className={`w-4 h-4 sm:w-5 sm:h-5 inline mr-2 ${activeTab === 'mfa' ? 'text-[#d4af37]' : ''}`} />
|
<Shield className={`w-4 h-4 sm:w-5 sm:h-5 inline mr-2 ${activeTab === 'mfa' ? 'text-[var(--luxury-gold)]' : ''}`} />
|
||||||
Two-Factor Auth
|
Two-Factor Auth
|
||||||
</button>
|
</button>
|
||||||
<button
|
<button
|
||||||
onClick={() => setActiveTab('sessions')}
|
onClick={() => setActiveTab('sessions')}
|
||||||
className={`py-3 sm:py-4 px-2 sm:px-1 border-b-2 font-semibold text-xs sm:text-sm transition-all duration-300 whitespace-nowrap ${
|
className={`py-3 sm:py-4 px-2 sm:px-1 border-b-2 font-semibold text-xs sm:text-sm transition-all duration-300 whitespace-nowrap ${
|
||||||
activeTab === 'sessions'
|
activeTab === 'sessions'
|
||||||
? 'border-[#d4af37] text-[#d4af37] shadow-sm'
|
? 'border-[var(--luxury-gold)] text-[var(--luxury-gold)] shadow-sm'
|
||||||
: 'border-transparent text-gray-500 hover:text-[#d4af37] hover:border-[#d4af37]/30'
|
: 'border-transparent text-gray-500 hover:text-[var(--luxury-gold)] hover:border-[var(--luxury-gold)]/30'
|
||||||
}`}
|
}`}
|
||||||
>
|
>
|
||||||
<Monitor className={`w-4 h-4 sm:w-5 sm:h-5 inline mr-2 ${activeTab === 'sessions' ? 'text-[#d4af37]' : ''}`} />
|
<Monitor className={`w-4 h-4 sm:w-5 sm:h-5 inline mr-2 ${activeTab === 'sessions' ? 'text-[var(--luxury-gold)]' : ''}`} />
|
||||||
Sessions
|
Sessions
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
@@ -441,7 +441,7 @@ const HousekeepingProfilePage: React.FC = () => {
|
|||||||
|
|
||||||
{/* Profile Tab */}
|
{/* Profile Tab */}
|
||||||
{activeTab === 'profile' && (
|
{activeTab === 'profile' && (
|
||||||
<div className="luxury-glass rounded-sm p-4 sm:p-6 lg:p-8 border border-[#d4af37]/20 shadow-2xl animate-slide-up">
|
<div className="luxury-glass rounded-sm p-4 sm:p-6 lg:p-8 border border-[var(--luxury-gold)]/20 shadow-2xl animate-slide-up">
|
||||||
<form onSubmit={handleSubmitProfile(onSubmitProfile)} className="space-y-5 sm:space-y-6">
|
<form onSubmit={handleSubmitProfile(onSubmitProfile)} className="space-y-5 sm:space-y-6">
|
||||||
{/* Avatar Section */}
|
{/* Avatar Section */}
|
||||||
<div className="flex flex-col sm:flex-row items-center sm:items-start gap-4 sm:gap-6 pb-5 sm:pb-6 border-b border-gray-200">
|
<div className="flex flex-col sm:flex-row items-center sm:items-start gap-4 sm:gap-6 pb-5 sm:pb-6 border-b border-gray-200">
|
||||||
@@ -450,17 +450,17 @@ const HousekeepingProfilePage: React.FC = () => {
|
|||||||
<img
|
<img
|
||||||
src={avatarPreview || normalizeImageUrl(userInfo?.avatar)}
|
src={avatarPreview || normalizeImageUrl(userInfo?.avatar)}
|
||||||
alt="Profile"
|
alt="Profile"
|
||||||
className="w-20 h-20 sm:w-24 sm:h-24 rounded-full object-cover ring-4 ring-[#d4af37]/20 shadow-lg"
|
className="w-20 h-20 sm:w-24 sm:h-24 rounded-full object-cover ring-4 ring-[var(--luxury-gold)]/20 shadow-lg"
|
||||||
onError={() => setAvatarError(true)}
|
onError={() => setAvatarError(true)}
|
||||||
/>
|
/>
|
||||||
) : (
|
) : (
|
||||||
<div className="w-20 h-20 sm:w-24 sm:h-24 rounded-full bg-gradient-to-br from-[#d4af37] to-[#c9a227] flex items-center justify-center ring-4 ring-[#d4af37]/20 shadow-lg">
|
<div className="w-20 h-20 sm:w-24 sm:h-24 rounded-full bg-gradient-to-br from-[var(--luxury-gold)] to-[var(--luxury-gold-dark)] flex items-center justify-center ring-4 ring-[var(--luxury-gold)]/20 shadow-lg">
|
||||||
<User className="w-10 h-10 sm:w-12 sm:h-12 text-white" />
|
<User className="w-10 h-10 sm:w-12 sm:h-12 text-white" />
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
<label
|
<label
|
||||||
htmlFor="avatar-upload"
|
htmlFor="avatar-upload"
|
||||||
className="absolute bottom-0 right-0 p-2 bg-gradient-to-br from-[#d4af37] to-[#c9a227] rounded-full cursor-pointer hover:from-[#f5d76e] hover:to-[#d4af37] transition-all duration-300 shadow-lg"
|
className="absolute bottom-0 right-0 p-2 bg-gradient-to-br from-[var(--luxury-gold)] to-[var(--luxury-gold-dark)] rounded-full cursor-pointer hover:from-[var(--luxury-gold-light)] hover:to-[var(--luxury-gold)] transition-all duration-300 shadow-lg"
|
||||||
>
|
>
|
||||||
<Camera className="w-3.5 h-3.5 sm:w-4 sm:h-4 text-white" />
|
<Camera className="w-3.5 h-3.5 sm:w-4 sm:h-4 text-white" />
|
||||||
<input
|
<input
|
||||||
@@ -477,7 +477,7 @@ const HousekeepingProfilePage: React.FC = () => {
|
|||||||
{/* Name Field */}
|
{/* Name Field */}
|
||||||
<div>
|
<div>
|
||||||
<label className="block text-sm sm:text-base font-medium text-gray-700 mb-2">
|
<label className="block text-sm sm:text-base font-medium text-gray-700 mb-2">
|
||||||
<User className="w-4 h-4 sm:w-5 sm:h-5 inline mr-2 text-[#d4af37]" />
|
<User className="w-4 h-4 sm:w-5 sm:h-5 inline mr-2 text-[var(--luxury-gold)]" />
|
||||||
Full Name <span className="text-red-500">*</span>
|
Full Name <span className="text-red-500">*</span>
|
||||||
</label>
|
</label>
|
||||||
<input
|
<input
|
||||||
@@ -499,7 +499,7 @@ const HousekeepingProfilePage: React.FC = () => {
|
|||||||
{/* Email Field */}
|
{/* Email Field */}
|
||||||
<div>
|
<div>
|
||||||
<label className="block text-sm sm:text-base font-medium text-gray-700 mb-2">
|
<label className="block text-sm sm:text-base font-medium text-gray-700 mb-2">
|
||||||
<Mail className="w-4 h-4 sm:w-5 sm:h-5 inline mr-2 text-[#d4af37]" />
|
<Mail className="w-4 h-4 sm:w-5 sm:h-5 inline mr-2 text-[var(--luxury-gold)]" />
|
||||||
Email Address <span className="text-red-500">*</span>
|
Email Address <span className="text-red-500">*</span>
|
||||||
</label>
|
</label>
|
||||||
<input
|
<input
|
||||||
@@ -521,7 +521,7 @@ const HousekeepingProfilePage: React.FC = () => {
|
|||||||
{/* Phone Field */}
|
{/* Phone Field */}
|
||||||
<div>
|
<div>
|
||||||
<label className="block text-sm sm:text-base font-medium text-gray-700 mb-2">
|
<label className="block text-sm sm:text-base font-medium text-gray-700 mb-2">
|
||||||
<Phone className="w-4 h-4 sm:w-5 sm:h-5 inline mr-2 text-[#d4af37]" />
|
<Phone className="w-4 h-4 sm:w-5 sm:h-5 inline mr-2 text-[var(--luxury-gold)]" />
|
||||||
Phone Number <span className="text-red-500">*</span>
|
Phone Number <span className="text-red-500">*</span>
|
||||||
</label>
|
</label>
|
||||||
<input
|
<input
|
||||||
@@ -556,7 +556,7 @@ const HousekeepingProfilePage: React.FC = () => {
|
|||||||
|
|
||||||
{/* Password Tab */}
|
{/* Password Tab */}
|
||||||
{activeTab === 'password' && (
|
{activeTab === 'password' && (
|
||||||
<div className="luxury-glass rounded-sm p-4 sm:p-6 lg:p-8 border border-[#d4af37]/20 shadow-2xl animate-slide-up">
|
<div className="luxury-glass rounded-sm p-4 sm:p-6 lg:p-8 border border-[var(--luxury-gold)]/20 shadow-2xl animate-slide-up">
|
||||||
<form onSubmit={handleSubmitPassword(onSubmitPassword)} className="space-y-5 sm:space-y-6">
|
<form onSubmit={handleSubmitPassword(onSubmitPassword)} className="space-y-5 sm:space-y-6">
|
||||||
{/* Password Requirements */}
|
{/* Password Requirements */}
|
||||||
<div className="bg-gradient-to-r from-blue-50 to-indigo-50 border border-blue-200/60 rounded-sm p-4 sm:p-5">
|
<div className="bg-gradient-to-r from-blue-50 to-indigo-50 border border-blue-200/60 rounded-sm p-4 sm:p-5">
|
||||||
@@ -579,7 +579,7 @@ const HousekeepingProfilePage: React.FC = () => {
|
|||||||
{/* Current Password */}
|
{/* Current Password */}
|
||||||
<div>
|
<div>
|
||||||
<label className="block text-sm sm:text-base font-medium text-gray-700 mb-2">
|
<label className="block text-sm sm:text-base font-medium text-gray-700 mb-2">
|
||||||
<Lock className="w-4 h-4 sm:w-5 sm:h-5 inline mr-2 text-[#d4af37]" />
|
<Lock className="w-4 h-4 sm:w-5 sm:h-5 inline mr-2 text-[var(--luxury-gold)]" />
|
||||||
Current Password <span className="text-red-500">*</span>
|
Current Password <span className="text-red-500">*</span>
|
||||||
</label>
|
</label>
|
||||||
<div className="relative">
|
<div className="relative">
|
||||||
@@ -594,7 +594,7 @@ const HousekeepingProfilePage: React.FC = () => {
|
|||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
onClick={() => setShowPassword({ ...showPassword, current: !showPassword.current })}
|
onClick={() => setShowPassword({ ...showPassword, current: !showPassword.current })}
|
||||||
className="absolute inset-y-0 right-0 pr-3 flex items-center text-gray-400 hover:text-[#d4af37] transition-colors"
|
className="absolute inset-y-0 right-0 pr-3 flex items-center text-gray-400 hover:text-[var(--luxury-gold)] transition-colors"
|
||||||
>
|
>
|
||||||
{showPassword.current ? <EyeOff className="w-4 h-4 sm:w-5 sm:h-5" /> : <Eye className="w-4 h-4 sm:w-5 sm:h-5" />}
|
{showPassword.current ? <EyeOff className="w-4 h-4 sm:w-5 sm:h-5" /> : <Eye className="w-4 h-4 sm:w-5 sm:h-5" />}
|
||||||
</button>
|
</button>
|
||||||
@@ -610,7 +610,7 @@ const HousekeepingProfilePage: React.FC = () => {
|
|||||||
{/* New Password */}
|
{/* New Password */}
|
||||||
<div>
|
<div>
|
||||||
<label className="block text-sm sm:text-base font-medium text-gray-700 mb-2">
|
<label className="block text-sm sm:text-base font-medium text-gray-700 mb-2">
|
||||||
<Lock className="w-4 h-4 sm:w-5 sm:h-5 inline mr-2 text-[#d4af37]" />
|
<Lock className="w-4 h-4 sm:w-5 sm:h-5 inline mr-2 text-[var(--luxury-gold)]" />
|
||||||
New Password <span className="text-red-500">*</span>
|
New Password <span className="text-red-500">*</span>
|
||||||
</label>
|
</label>
|
||||||
<div className="relative">
|
<div className="relative">
|
||||||
@@ -625,7 +625,7 @@ const HousekeepingProfilePage: React.FC = () => {
|
|||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
onClick={() => setShowPassword({ ...showPassword, new: !showPassword.new })}
|
onClick={() => setShowPassword({ ...showPassword, new: !showPassword.new })}
|
||||||
className="absolute inset-y-0 right-0 pr-3 flex items-center text-gray-400 hover:text-[#d4af37] transition-colors"
|
className="absolute inset-y-0 right-0 pr-3 flex items-center text-gray-400 hover:text-[var(--luxury-gold)] transition-colors"
|
||||||
>
|
>
|
||||||
{showPassword.new ? <EyeOff className="w-4 h-4 sm:w-5 sm:h-5" /> : <Eye className="w-4 h-4 sm:w-5 sm:h-5" />}
|
{showPassword.new ? <EyeOff className="w-4 h-4 sm:w-5 sm:h-5" /> : <Eye className="w-4 h-4 sm:w-5 sm:h-5" />}
|
||||||
</button>
|
</button>
|
||||||
@@ -641,7 +641,7 @@ const HousekeepingProfilePage: React.FC = () => {
|
|||||||
{/* Confirm Password */}
|
{/* Confirm Password */}
|
||||||
<div>
|
<div>
|
||||||
<label className="block text-sm sm:text-base font-medium text-gray-700 mb-2">
|
<label className="block text-sm sm:text-base font-medium text-gray-700 mb-2">
|
||||||
<Lock className="w-4 h-4 sm:w-5 sm:h-5 inline mr-2 text-[#d4af37]" />
|
<Lock className="w-4 h-4 sm:w-5 sm:h-5 inline mr-2 text-[var(--luxury-gold)]" />
|
||||||
Confirm Password <span className="text-red-500">*</span>
|
Confirm Password <span className="text-red-500">*</span>
|
||||||
</label>
|
</label>
|
||||||
<div className="relative">
|
<div className="relative">
|
||||||
@@ -656,7 +656,7 @@ const HousekeepingProfilePage: React.FC = () => {
|
|||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
onClick={() => setShowPassword({ ...showPassword, confirm: !showPassword.confirm })}
|
onClick={() => setShowPassword({ ...showPassword, confirm: !showPassword.confirm })}
|
||||||
className="absolute inset-y-0 right-0 pr-3 flex items-center text-gray-400 hover:text-[#d4af37] transition-colors"
|
className="absolute inset-y-0 right-0 pr-3 flex items-center text-gray-400 hover:text-[var(--luxury-gold)] transition-colors"
|
||||||
>
|
>
|
||||||
{showPassword.confirm ? <EyeOff className="w-4 h-4 sm:w-5 sm:h-5" /> : <Eye className="w-4 h-4 sm:w-5 sm:h-5" />}
|
{showPassword.confirm ? <EyeOff className="w-4 h-4 sm:w-5 sm:h-5" /> : <Eye className="w-4 h-4 sm:w-5 sm:h-5" />}
|
||||||
</button>
|
</button>
|
||||||
@@ -742,10 +742,10 @@ const MFATab: React.FC<MFATabProps> = ({
|
|||||||
setLoading,
|
setLoading,
|
||||||
}) => {
|
}) => {
|
||||||
return (
|
return (
|
||||||
<div className="luxury-glass rounded-sm p-4 sm:p-6 lg:p-8 border border-[#d4af37]/20 shadow-2xl animate-slide-up space-y-5 sm:space-y-6">
|
<div className="luxury-glass rounded-sm p-4 sm:p-6 lg:p-8 border border-[var(--luxury-gold)]/20 shadow-2xl animate-slide-up space-y-5 sm:space-y-6">
|
||||||
<div>
|
<div>
|
||||||
<h2 className="text-xl sm:text-2xl font-serif font-semibold text-gray-900 mb-2 flex items-center gap-2">
|
<h2 className="text-xl sm:text-2xl font-serif font-semibold text-gray-900 mb-2 flex items-center gap-2">
|
||||||
<Shield className="w-5 h-5 sm:w-6 sm:h-6 text-[#d4af37]" />
|
<Shield className="w-5 h-5 sm:w-6 sm:h-6 text-[var(--luxury-gold)]" />
|
||||||
Two-Factor Authentication
|
Two-Factor Authentication
|
||||||
</h2>
|
</h2>
|
||||||
<p className="text-xs sm:text-sm text-gray-600 font-light">
|
<p className="text-xs sm:text-sm text-gray-600 font-light">
|
||||||
@@ -894,7 +894,7 @@ const MFATab: React.FC<MFATabProps> = ({
|
|||||||
|
|
||||||
<div className="bg-white border border-gray-200 rounded-sm p-4">
|
<div className="bg-white border border-gray-200 rounded-sm p-4">
|
||||||
<h3 className="font-semibold text-gray-900 mb-2 flex items-center gap-2">
|
<h3 className="font-semibold text-gray-900 mb-2 flex items-center gap-2">
|
||||||
<KeyRound className="w-4 h-4 text-[#d4af37]" />
|
<KeyRound className="w-4 h-4 text-[var(--luxury-gold)]" />
|
||||||
Step 2: Verify Setup
|
Step 2: Verify Setup
|
||||||
</h3>
|
</h3>
|
||||||
<p className="text-sm text-gray-600 mb-4">Enter the 6-digit code from your authenticator app.</p>
|
<p className="text-sm text-gray-600 mb-4">Enter the 6-digit code from your authenticator app.</p>
|
||||||
@@ -992,10 +992,10 @@ const SessionsTab: React.FC = () => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const getDeviceIcon = (userAgent?: string) => {
|
const getDeviceIcon = (userAgent?: string) => {
|
||||||
if (!userAgent) return <Monitor className="w-5 h-5 text-[#d4af37]" />;
|
if (!userAgent) return <Monitor className="w-5 h-5 text-[var(--luxury-gold)]" />;
|
||||||
if (userAgent.includes('Mobile')) return <Smartphone className="w-5 h-5 text-blue-500" />;
|
if (userAgent.includes('Mobile')) return <Smartphone className="w-5 h-5 text-blue-500" />;
|
||||||
if (userAgent.includes('Tablet')) return <Tablet className="w-5 h-5 text-purple-500" />;
|
if (userAgent.includes('Tablet')) return <Tablet className="w-5 h-5 text-purple-500" />;
|
||||||
return <Monitor className="w-5 h-5 text-[#d4af37]" />;
|
return <Monitor className="w-5 h-5 text-[var(--luxury-gold)]" />;
|
||||||
};
|
};
|
||||||
|
|
||||||
const getDeviceName = (userAgent?: string) => {
|
const getDeviceName = (userAgent?: string) => {
|
||||||
@@ -1053,17 +1053,17 @@ const SessionsTab: React.FC = () => {
|
|||||||
|
|
||||||
if (loading) {
|
if (loading) {
|
||||||
return (
|
return (
|
||||||
<div className="luxury-glass rounded-sm p-4 sm:p-6 lg:p-8 border border-[#d4af37]/20 shadow-2xl">
|
<div className="luxury-glass rounded-sm p-4 sm:p-6 lg:p-8 border border-[var(--luxury-gold)]/20 shadow-2xl">
|
||||||
<Loading text="Loading sessions..." />
|
<Loading text="Loading sessions..." />
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="luxury-glass rounded-sm p-4 sm:p-6 lg:p-8 border border-[#d4af37]/20 shadow-2xl animate-slide-up space-y-5 sm:space-y-6">
|
<div className="luxury-glass rounded-sm p-4 sm:p-6 lg:p-8 border border-[var(--luxury-gold)]/20 shadow-2xl animate-slide-up space-y-5 sm:space-y-6">
|
||||||
<div>
|
<div>
|
||||||
<h2 className="text-xl sm:text-2xl font-serif font-semibold text-gray-900 mb-2 flex items-center gap-2">
|
<h2 className="text-xl sm:text-2xl font-serif font-semibold text-gray-900 mb-2 flex items-center gap-2">
|
||||||
<Monitor className="w-5 h-5 sm:w-6 sm:h-6 text-[#d4af37]" />
|
<Monitor className="w-5 h-5 sm:w-6 sm:h-6 text-[var(--luxury-gold)]" />
|
||||||
Active Sessions
|
Active Sessions
|
||||||
</h2>
|
</h2>
|
||||||
<p className="text-xs sm:text-sm text-gray-600 font-light">
|
<p className="text-xs sm:text-sm text-gray-600 font-light">
|
||||||
@@ -1092,11 +1092,11 @@ const SessionsTab: React.FC = () => {
|
|||||||
{sessions.map((session) => (
|
{sessions.map((session) => (
|
||||||
<div
|
<div
|
||||||
key={session.id}
|
key={session.id}
|
||||||
className="bg-gradient-to-r from-slate-50 to-white border border-[#d4af37]/20 rounded-sm p-4 sm:p-5 hover:shadow-lg transition-all"
|
className="bg-gradient-to-r from-slate-50 to-white border border-[var(--luxury-gold)]/20 rounded-sm p-4 sm:p-5 hover:shadow-lg transition-all"
|
||||||
>
|
>
|
||||||
<div className="flex items-start justify-between gap-4">
|
<div className="flex items-start justify-between gap-4">
|
||||||
<div className="flex items-start gap-4 flex-1">
|
<div className="flex items-start gap-4 flex-1">
|
||||||
<div className="p-2 sm:p-3 bg-gradient-to-br from-[#d4af37]/10 to-[#c9a227]/10 rounded-sm flex-shrink-0">
|
<div className="p-2 sm:p-3 bg-gradient-to-br from-[var(--luxury-gold)]/10 to-[var(--luxury-gold-dark)]/10 rounded-sm flex-shrink-0">
|
||||||
{getDeviceIcon(session.user_agent)}
|
{getDeviceIcon(session.user_agent)}
|
||||||
</div>
|
</div>
|
||||||
<div className="flex-1 min-w-0">
|
<div className="flex-1 min-w-0">
|
||||||
|
|||||||
@@ -75,7 +75,7 @@ const HousekeepingShiftViewPage: React.FC = () => {
|
|||||||
<div className="flex flex-col sm:flex-row justify-between items-start sm:items-center gap-4 animate-fade-in">
|
<div className="flex flex-col sm:flex-row justify-between items-start sm:items-center gap-4 animate-fade-in">
|
||||||
<div>
|
<div>
|
||||||
<div className="flex items-center gap-3 mb-2">
|
<div className="flex items-center gap-3 mb-2">
|
||||||
<div className="h-1 w-16 bg-gradient-to-r from-[#d4af37] to-[#c9a227] rounded-full"></div>
|
<div className="h-1 w-16 bg-gradient-to-r from-[var(--luxury-gold)] to-[var(--luxury-gold-dark)] rounded-full"></div>
|
||||||
<h1 className="text-4xl font-bold bg-gradient-to-r from-slate-900 via-slate-800 to-slate-900 bg-clip-text text-transparent tracking-tight">
|
<h1 className="text-4xl font-bold bg-gradient-to-r from-slate-900 via-slate-800 to-slate-900 bg-clip-text text-transparent tracking-tight">
|
||||||
My Shifts
|
My Shifts
|
||||||
</h1>
|
</h1>
|
||||||
@@ -86,7 +86,7 @@ const HousekeepingShiftViewPage: React.FC = () => {
|
|||||||
type="date"
|
type="date"
|
||||||
value={selectedDate.toISOString().split('T')[0]}
|
value={selectedDate.toISOString().split('T')[0]}
|
||||||
onChange={(e) => setSelectedDate(new Date(e.target.value))}
|
onChange={(e) => setSelectedDate(new Date(e.target.value))}
|
||||||
className="px-4 py-2 border-2 border-slate-200 rounded-xl focus:border-[#d4af37] focus:ring-4 focus:ring-yellow-100"
|
className="px-4 py-2 border-2 border-slate-200 rounded-xl focus:border-[var(--luxury-gold)] focus:ring-4 focus:ring-yellow-100"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@@ -96,9 +96,9 @@ const HousekeepingShiftViewPage: React.FC = () => {
|
|||||||
<div className="flex items-center justify-between">
|
<div className="flex items-center justify-between">
|
||||||
<div>
|
<div>
|
||||||
<p className="text-slate-600 text-sm font-medium">Upcoming Shifts</p>
|
<p className="text-slate-600 text-sm font-medium">Upcoming Shifts</p>
|
||||||
<p className="text-3xl font-bold text-[#d4af37] mt-2">{getUpcomingShifts()}</p>
|
<p className="text-3xl font-bold text-[var(--luxury-gold)] mt-2">{getUpcomingShifts()}</p>
|
||||||
</div>
|
</div>
|
||||||
<Calendar className="w-12 h-12 text-[#d4af37]" />
|
<Calendar className="w-12 h-12 text-[var(--luxury-gold)]" />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="bg-white/80 backdrop-blur-sm rounded-xl shadow-lg border border-slate-200/60 p-6">
|
<div className="bg-white/80 backdrop-blur-sm rounded-xl shadow-lg border border-slate-200/60 p-6">
|
||||||
@@ -169,7 +169,7 @@ const HousekeepingShiftViewPage: React.FC = () => {
|
|||||||
<select
|
<select
|
||||||
value={shift.status}
|
value={shift.status}
|
||||||
onChange={(e) => handleStatusUpdate(shift.id, e.target.value)}
|
onChange={(e) => handleStatusUpdate(shift.id, e.target.value)}
|
||||||
className="px-4 py-2 border-2 border-slate-200 rounded-xl focus:border-[#d4af37] cursor-pointer"
|
className="px-4 py-2 border-2 border-slate-200 rounded-xl focus:border-[var(--luxury-gold)] cursor-pointer"
|
||||||
>
|
>
|
||||||
<option value="scheduled">Scheduled</option>
|
<option value="scheduled">Scheduled</option>
|
||||||
<option value="in_progress">Start Shift</option>
|
<option value="in_progress">Start Shift</option>
|
||||||
|
|||||||
@@ -363,7 +363,7 @@ const ChatManagementPage: React.FC = () => {
|
|||||||
<div className="flex items-center justify-between">
|
<div className="flex items-center justify-between">
|
||||||
<div>
|
<div>
|
||||||
<div className="flex items-center gap-3 mb-2">
|
<div className="flex items-center gap-3 mb-2">
|
||||||
<div className="h-1 w-16 bg-gradient-to-r from-[#d4af37] to-[#c9a227] rounded-full"></div>
|
<div className="h-1 w-16 bg-gradient-to-r from-[var(--luxury-gold)] to-[var(--luxury-gold-dark)] rounded-full"></div>
|
||||||
<h1 className="text-3xl font-serif font-bold bg-gradient-to-r from-slate-900 via-slate-800 to-slate-900 bg-clip-text text-transparent tracking-tight">
|
<h1 className="text-3xl font-serif font-bold bg-gradient-to-r from-slate-900 via-slate-800 to-slate-900 bg-clip-text text-transparent tracking-tight">
|
||||||
Chat Management
|
Chat Management
|
||||||
</h1>
|
</h1>
|
||||||
@@ -372,7 +372,7 @@ const ChatManagementPage: React.FC = () => {
|
|||||||
</div>
|
</div>
|
||||||
<button
|
<button
|
||||||
onClick={fetchChats}
|
onClick={fetchChats}
|
||||||
className="px-6 py-2.5 bg-gradient-to-r from-[#d4af37] to-[#c9a227] text-slate-900 rounded-lg font-semibold hover:from-[#c9a227] hover:to-[#d4af37] transition-all duration-300 shadow-lg shadow-[#d4af37]/30 hover:shadow-xl hover:shadow-[#d4af37]/40 flex items-center gap-2"
|
className="px-6 py-2.5 bg-gradient-to-r from-[var(--luxury-gold)] to-[var(--luxury-gold-dark)] text-slate-900 rounded-lg font-semibold hover:from-[var(--luxury-gold-dark)] hover:to-[var(--luxury-gold)] transition-all duration-300 shadow-lg shadow-[var(--luxury-gold)]/30 hover:shadow-xl hover:shadow-[var(--luxury-gold)]/40 flex items-center gap-2"
|
||||||
>
|
>
|
||||||
<RefreshCw className="w-4 h-4" />
|
<RefreshCw className="w-4 h-4" />
|
||||||
Refresh
|
Refresh
|
||||||
@@ -381,9 +381,9 @@ const ChatManagementPage: React.FC = () => {
|
|||||||
|
|
||||||
<div className="grid grid-cols-1 lg:grid-cols-3 gap-6 h-[calc(100vh-200px)]">
|
<div className="grid grid-cols-1 lg:grid-cols-3 gap-6 h-[calc(100vh-200px)]">
|
||||||
{}
|
{}
|
||||||
<div className="luxury-glass rounded-xl shadow-2xl border border-[#d4af37]/20 overflow-hidden flex flex-col">
|
<div className="luxury-glass rounded-xl shadow-2xl border border-[var(--luxury-gold)]/20 overflow-hidden flex flex-col">
|
||||||
<div className="p-4 border-b border-[#d4af37]/20 bg-gradient-to-r from-[#d4af37]/10 via-[#c9a227]/10 to-[#d4af37]/10 relative">
|
<div className="p-4 border-b border-[var(--luxury-gold)]/20 bg-gradient-to-r from-[var(--luxury-gold)]/10 via-[var(--luxury-gold-dark)]/10 to-[var(--luxury-gold)]/10 relative">
|
||||||
<div className="absolute top-0 left-0 right-0 h-0.5 bg-gradient-to-r from-transparent via-[#d4af37] to-transparent"></div>
|
<div className="absolute top-0 left-0 right-0 h-0.5 bg-gradient-to-r from-transparent via-[var(--luxury-gold)] to-transparent"></div>
|
||||||
<h2 className="font-serif font-semibold text-slate-900 tracking-wide">Chats ({chats.length})</h2>
|
<h2 className="font-serif font-semibold text-slate-900 tracking-wide">Chats ({chats.length})</h2>
|
||||||
</div>
|
</div>
|
||||||
<div className="flex-1 overflow-y-auto">
|
<div className="flex-1 overflow-y-auto">
|
||||||
@@ -398,9 +398,9 @@ const ChatManagementPage: React.FC = () => {
|
|||||||
<div
|
<div
|
||||||
key={chat.id}
|
key={chat.id}
|
||||||
onClick={() => setSelectedChat(chat)}
|
onClick={() => setSelectedChat(chat)}
|
||||||
className={`p-4 cursor-pointer hover:bg-gradient-to-r hover:from-[#d4af37]/5 hover:to-[#c9a227]/5 transition-all duration-300 ${
|
className={`p-4 cursor-pointer hover:bg-gradient-to-r hover:from-[var(--luxury-gold)]/5 hover:to-[var(--luxury-gold-dark)]/5 transition-all duration-300 ${
|
||||||
selectedChat?.id === chat.id
|
selectedChat?.id === chat.id
|
||||||
? 'bg-gradient-to-r from-[#d4af37]/10 to-[#c9a227]/10 border-l-4 border-[#d4af37] shadow-sm'
|
? 'bg-gradient-to-r from-[var(--luxury-gold)]/10 to-[var(--luxury-gold-dark)]/10 border-l-4 border-[var(--luxury-gold)] shadow-sm'
|
||||||
: 'border-l-4 border-transparent'
|
: 'border-l-4 border-transparent'
|
||||||
}`}
|
}`}
|
||||||
>
|
>
|
||||||
@@ -425,7 +425,7 @@ const ChatManagementPage: React.FC = () => {
|
|||||||
e.stopPropagation();
|
e.stopPropagation();
|
||||||
handleAcceptChat(chat.id);
|
handleAcceptChat(chat.id);
|
||||||
}}
|
}}
|
||||||
className="mt-2 w-full px-3 py-1.5 bg-gradient-to-r from-[#d4af37] to-[#c9a227] text-slate-900 text-sm font-semibold rounded-lg hover:from-[#c9a227] hover:to-[#d4af37] transition-all duration-300 shadow-md shadow-[#d4af37]/30 hover:shadow-lg"
|
className="mt-2 w-full px-3 py-1.5 bg-gradient-to-r from-[var(--luxury-gold)] to-[var(--luxury-gold-dark)] text-slate-900 text-sm font-semibold rounded-lg hover:from-[var(--luxury-gold-dark)] hover:to-[var(--luxury-gold)] transition-all duration-300 shadow-md shadow-[var(--luxury-gold)]/30 hover:shadow-lg"
|
||||||
>
|
>
|
||||||
Accept Chat
|
Accept Chat
|
||||||
</button>
|
</button>
|
||||||
@@ -438,11 +438,11 @@ const ChatManagementPage: React.FC = () => {
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
{}
|
{}
|
||||||
<div className="lg:col-span-2 luxury-glass rounded-xl shadow-2xl border border-[#d4af37]/20 overflow-hidden flex flex-col">
|
<div className="lg:col-span-2 luxury-glass rounded-xl shadow-2xl border border-[var(--luxury-gold)]/20 overflow-hidden flex flex-col">
|
||||||
{selectedChat ? (
|
{selectedChat ? (
|
||||||
<>
|
<>
|
||||||
<div className="p-4 border-b border-[#d4af37]/20 bg-gradient-to-r from-[#d4af37]/10 via-[#c9a227]/10 to-[#d4af37]/10 flex items-center justify-between relative">
|
<div className="p-4 border-b border-[var(--luxury-gold)]/20 bg-gradient-to-r from-[var(--luxury-gold)]/10 via-[var(--luxury-gold-dark)]/10 to-[var(--luxury-gold)]/10 flex items-center justify-between relative">
|
||||||
<div className="absolute top-0 left-0 right-0 h-0.5 bg-gradient-to-r from-transparent via-[#d4af37] to-transparent"></div>
|
<div className="absolute top-0 left-0 right-0 h-0.5 bg-gradient-to-r from-transparent via-[var(--luxury-gold)] to-transparent"></div>
|
||||||
<div>
|
<div>
|
||||||
<h2 className="font-serif font-semibold text-slate-900 tracking-wide">
|
<h2 className="font-serif font-semibold text-slate-900 tracking-wide">
|
||||||
{selectedChat.visitor_name || 'Guest'}
|
{selectedChat.visitor_name || 'Guest'}
|
||||||
@@ -451,7 +451,7 @@ const ChatManagementPage: React.FC = () => {
|
|||||||
<p className="text-sm text-slate-600 font-light">{selectedChat.visitor_email}</p>
|
<p className="text-sm text-slate-600 font-light">{selectedChat.visitor_email}</p>
|
||||||
)}
|
)}
|
||||||
{selectedChat.staff_name && (
|
{selectedChat.staff_name && (
|
||||||
<p className="text-xs text-[#d4af37] mt-1 font-medium">
|
<p className="text-xs text-[var(--luxury-gold)] mt-1 font-medium">
|
||||||
Accepted by: {selectedChat.staff_name}
|
Accepted by: {selectedChat.staff_name}
|
||||||
</p>
|
</p>
|
||||||
)}
|
)}
|
||||||
@@ -489,12 +489,12 @@ const ChatManagementPage: React.FC = () => {
|
|||||||
<div
|
<div
|
||||||
className={`max-w-[80%] rounded-xl p-4 shadow-lg ${
|
className={`max-w-[80%] rounded-xl p-4 shadow-lg ${
|
||||||
message.sender_type === 'staff'
|
message.sender_type === 'staff'
|
||||||
? 'bg-gradient-to-br from-[#d4af37] to-[#c9a227] text-slate-900 border border-[#d4af37]/30'
|
? 'bg-gradient-to-br from-[var(--luxury-gold)] to-[var(--luxury-gold-dark)] text-slate-900 border border-[var(--luxury-gold)]/30'
|
||||||
: 'bg-white text-slate-800 border border-slate-200/60 shadow-sm'
|
: 'bg-white text-slate-800 border border-slate-200/60 shadow-sm'
|
||||||
}`}
|
}`}
|
||||||
>
|
>
|
||||||
{message.sender_type === 'visitor' && (
|
{message.sender_type === 'visitor' && (
|
||||||
<div className="text-xs font-semibold mb-1.5 text-[#d4af37] tracking-wide">
|
<div className="text-xs font-semibold mb-1.5 text-[var(--luxury-gold)] tracking-wide">
|
||||||
{message.sender_name || 'Visitor'}
|
{message.sender_name || 'Visitor'}
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
@@ -521,7 +521,7 @@ const ChatManagementPage: React.FC = () => {
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
{selectedChat.status !== 'closed' && (
|
{selectedChat.status !== 'closed' && (
|
||||||
<div className="p-4 border-t border-[#d4af37]/20 bg-white/90 backdrop-blur-sm">
|
<div className="p-4 border-t border-[var(--luxury-gold)]/20 bg-white/90 backdrop-blur-sm">
|
||||||
<div className="flex gap-3">
|
<div className="flex gap-3">
|
||||||
<input
|
<input
|
||||||
type="text"
|
type="text"
|
||||||
@@ -529,13 +529,13 @@ const ChatManagementPage: React.FC = () => {
|
|||||||
onChange={(e) => setNewMessage(e.target.value)}
|
onChange={(e) => setNewMessage(e.target.value)}
|
||||||
onKeyPress={handleKeyPress}
|
onKeyPress={handleKeyPress}
|
||||||
placeholder="Type your message..."
|
placeholder="Type your message..."
|
||||||
className="flex-1 px-4 py-3 border-2 border-slate-200 rounded-xl focus:outline-none focus:ring-2 focus:ring-[#d4af37]/30 focus:border-[#d4af37] transition-all duration-200 font-light"
|
className="flex-1 px-4 py-3 border-2 border-slate-200 rounded-xl focus:outline-none focus:ring-2 focus:ring-[var(--luxury-gold)]/30 focus:border-[var(--luxury-gold)] transition-all duration-200 font-light"
|
||||||
disabled={selectedChat.status === 'pending'}
|
disabled={selectedChat.status === 'pending'}
|
||||||
/>
|
/>
|
||||||
<button
|
<button
|
||||||
onClick={handleSendMessage}
|
onClick={handleSendMessage}
|
||||||
disabled={!newMessage.trim() || selectedChat.status === 'pending'}
|
disabled={!newMessage.trim() || selectedChat.status === 'pending'}
|
||||||
className="bg-gradient-to-r from-[#d4af37] to-[#c9a227] text-slate-900 px-5 py-3 rounded-xl hover:from-[#c9a227] hover:to-[#d4af37] disabled:bg-gray-300 disabled:text-gray-500 disabled:cursor-not-allowed transition-all duration-300 flex items-center gap-2 font-semibold shadow-lg shadow-[#d4af37]/30 hover:shadow-xl disabled:shadow-none"
|
className="bg-gradient-to-r from-[var(--luxury-gold)] to-[var(--luxury-gold-dark)] text-slate-900 px-5 py-3 rounded-xl hover:from-[var(--luxury-gold-dark)] hover:to-[var(--luxury-gold)] disabled:bg-gray-300 disabled:text-gray-500 disabled:cursor-not-allowed transition-all duration-300 flex items-center gap-2 font-semibold shadow-lg shadow-[var(--luxury-gold)]/30 hover:shadow-xl disabled:shadow-none"
|
||||||
>
|
>
|
||||||
<Send className="w-4 h-4" />
|
<Send className="w-4 h-4" />
|
||||||
</button>
|
</button>
|
||||||
|
|||||||
@@ -417,7 +417,7 @@ const ProfilePage: React.FC = () => {
|
|||||||
{}
|
{}
|
||||||
<div className="mb-6 sm:mb-8 animate-fade-in">
|
<div className="mb-6 sm:mb-8 animate-fade-in">
|
||||||
<div className="flex items-center gap-2 sm:gap-3 mb-2 sm:mb-3">
|
<div className="flex items-center gap-2 sm:gap-3 mb-2 sm:mb-3">
|
||||||
<div className="h-1 w-12 sm:w-16 md:w-20 bg-gradient-to-r from-[#d4af37] via-amber-400 to-[#d4af37] rounded-full"></div>
|
<div className="h-1 w-12 sm:w-16 md:w-20 bg-gradient-to-r from-[var(--luxury-gold)] via-amber-400 to-[var(--luxury-gold)] rounded-full"></div>
|
||||||
<h1 className="text-2xl sm:text-3xl lg:text-4xl font-serif font-bold bg-gradient-to-r from-gray-900 via-amber-900/90 to-gray-900 bg-clip-text text-transparent tracking-tight">
|
<h1 className="text-2xl sm:text-3xl lg:text-4xl font-serif font-bold bg-gradient-to-r from-gray-900 via-amber-900/90 to-gray-900 bg-clip-text text-transparent tracking-tight">
|
||||||
Staff Profile & Settings
|
Staff Profile & Settings
|
||||||
</h1>
|
</h1>
|
||||||
@@ -428,61 +428,61 @@ const ProfilePage: React.FC = () => {
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
{}
|
{}
|
||||||
<div className="mb-4 sm:mb-6 border-b border-[#d4af37]/20 overflow-x-auto bg-white/50 backdrop-blur-sm rounded-t-lg sm:rounded-t-xl px-4 sm:px-6">
|
<div className="mb-4 sm:mb-6 border-b border-[var(--luxury-gold)]/20 overflow-x-auto bg-white/50 backdrop-blur-sm rounded-t-lg sm:rounded-t-xl px-4 sm:px-6">
|
||||||
<div className="flex space-x-4 sm:space-x-8 min-w-max">
|
<div className="flex space-x-4 sm:space-x-8 min-w-max">
|
||||||
<button
|
<button
|
||||||
onClick={() => setActiveTab('profile')}
|
onClick={() => setActiveTab('profile')}
|
||||||
className={`py-3 sm:py-4 px-2 sm:px-1 border-b-2 font-semibold text-xs sm:text-sm transition-all duration-300 whitespace-nowrap ${
|
className={`py-3 sm:py-4 px-2 sm:px-1 border-b-2 font-semibold text-xs sm:text-sm transition-all duration-300 whitespace-nowrap ${
|
||||||
activeTab === 'profile'
|
activeTab === 'profile'
|
||||||
? 'border-[#d4af37] text-[#d4af37] shadow-sm'
|
? 'border-[var(--luxury-gold)] text-[var(--luxury-gold)] shadow-sm'
|
||||||
: 'border-transparent text-gray-500 hover:text-[#d4af37] hover:border-[#d4af37]/30'
|
: 'border-transparent text-gray-500 hover:text-[var(--luxury-gold)] hover:border-[var(--luxury-gold)]/30'
|
||||||
}`}
|
}`}
|
||||||
>
|
>
|
||||||
<User className={`w-4 h-4 sm:w-5 sm:h-5 inline mr-2 ${activeTab === 'profile' ? 'text-[#d4af37]' : ''}`} />
|
<User className={`w-4 h-4 sm:w-5 sm:h-5 inline mr-2 ${activeTab === 'profile' ? 'text-[var(--luxury-gold)]' : ''}`} />
|
||||||
Profile Information
|
Profile Information
|
||||||
</button>
|
</button>
|
||||||
<button
|
<button
|
||||||
onClick={() => setActiveTab('password')}
|
onClick={() => setActiveTab('password')}
|
||||||
className={`py-3 sm:py-4 px-2 sm:px-1 border-b-2 font-semibold text-xs sm:text-sm transition-all duration-300 whitespace-nowrap ${
|
className={`py-3 sm:py-4 px-2 sm:px-1 border-b-2 font-semibold text-xs sm:text-sm transition-all duration-300 whitespace-nowrap ${
|
||||||
activeTab === 'password'
|
activeTab === 'password'
|
||||||
? 'border-[#d4af37] text-[#d4af37] shadow-sm'
|
? 'border-[var(--luxury-gold)] text-[var(--luxury-gold)] shadow-sm'
|
||||||
: 'border-transparent text-gray-500 hover:text-[#d4af37] hover:border-[#d4af37]/30'
|
: 'border-transparent text-gray-500 hover:text-[var(--luxury-gold)] hover:border-[var(--luxury-gold)]/30'
|
||||||
}`}
|
}`}
|
||||||
>
|
>
|
||||||
<KeyRound className={`w-4 h-4 sm:w-5 sm:h-5 inline mr-2 ${activeTab === 'password' ? 'text-[#d4af37]' : ''}`} />
|
<KeyRound className={`w-4 h-4 sm:w-5 sm:h-5 inline mr-2 ${activeTab === 'password' ? 'text-[var(--luxury-gold)]' : ''}`} />
|
||||||
Change Password
|
Change Password
|
||||||
</button>
|
</button>
|
||||||
<button
|
<button
|
||||||
onClick={() => setActiveTab('mfa')}
|
onClick={() => setActiveTab('mfa')}
|
||||||
className={`py-3 sm:py-4 px-2 sm:px-1 border-b-2 font-semibold text-xs sm:text-sm transition-all duration-300 whitespace-nowrap ${
|
className={`py-3 sm:py-4 px-2 sm:px-1 border-b-2 font-semibold text-xs sm:text-sm transition-all duration-300 whitespace-nowrap ${
|
||||||
activeTab === 'mfa'
|
activeTab === 'mfa'
|
||||||
? 'border-[#d4af37] text-[#d4af37] shadow-sm'
|
? 'border-[var(--luxury-gold)] text-[var(--luxury-gold)] shadow-sm'
|
||||||
: 'border-transparent text-gray-500 hover:text-[#d4af37] hover:border-[#d4af37]/30'
|
: 'border-transparent text-gray-500 hover:text-[var(--luxury-gold)] hover:border-[var(--luxury-gold)]/30'
|
||||||
}`}
|
}`}
|
||||||
>
|
>
|
||||||
<Shield className={`w-4 h-4 sm:w-5 sm:h-5 inline mr-2 ${activeTab === 'mfa' ? 'text-[#d4af37]' : ''}`} />
|
<Shield className={`w-4 h-4 sm:w-5 sm:h-5 inline mr-2 ${activeTab === 'mfa' ? 'text-[var(--luxury-gold)]' : ''}`} />
|
||||||
Two-Factor Authentication
|
Two-Factor Authentication
|
||||||
</button>
|
</button>
|
||||||
<button
|
<button
|
||||||
onClick={() => setActiveTab('sessions')}
|
onClick={() => setActiveTab('sessions')}
|
||||||
className={`py-3 sm:py-4 px-2 sm:px-1 border-b-2 font-semibold text-xs sm:text-sm transition-all duration-300 whitespace-nowrap ${
|
className={`py-3 sm:py-4 px-2 sm:px-1 border-b-2 font-semibold text-xs sm:text-sm transition-all duration-300 whitespace-nowrap ${
|
||||||
activeTab === 'sessions'
|
activeTab === 'sessions'
|
||||||
? 'border-[#d4af37] text-[#d4af37] shadow-sm'
|
? 'border-[var(--luxury-gold)] text-[var(--luxury-gold)] shadow-sm'
|
||||||
: 'border-transparent text-gray-500 hover:text-[#d4af37] hover:border-[#d4af37]/30'
|
: 'border-transparent text-gray-500 hover:text-[var(--luxury-gold)] hover:border-[var(--luxury-gold)]/30'
|
||||||
}`}
|
}`}
|
||||||
>
|
>
|
||||||
<Monitor className={`w-4 h-4 sm:w-5 sm:h-5 inline mr-2 ${activeTab === 'sessions' ? 'text-[#d4af37]' : ''}`} />
|
<Monitor className={`w-4 h-4 sm:w-5 sm:h-5 inline mr-2 ${activeTab === 'sessions' ? 'text-[var(--luxury-gold)]' : ''}`} />
|
||||||
Active Sessions
|
Active Sessions
|
||||||
</button>
|
</button>
|
||||||
<button
|
<button
|
||||||
onClick={() => setActiveTab('gdpr')}
|
onClick={() => setActiveTab('gdpr')}
|
||||||
className={`py-3 sm:py-4 px-2 sm:px-1 border-b-2 font-semibold text-xs sm:text-sm transition-all duration-300 whitespace-nowrap ${
|
className={`py-3 sm:py-4 px-2 sm:px-1 border-b-2 font-semibold text-xs sm:text-sm transition-all duration-300 whitespace-nowrap ${
|
||||||
activeTab === 'gdpr'
|
activeTab === 'gdpr'
|
||||||
? 'border-[#d4af37] text-[#d4af37] shadow-sm'
|
? 'border-[var(--luxury-gold)] text-[var(--luxury-gold)] shadow-sm'
|
||||||
: 'border-transparent text-gray-500 hover:text-[#d4af37] hover:border-[#d4af37]/30'
|
: 'border-transparent text-gray-500 hover:text-[var(--luxury-gold)] hover:border-[var(--luxury-gold)]/30'
|
||||||
}`}
|
}`}
|
||||||
>
|
>
|
||||||
<Database className={`w-4 h-4 sm:w-5 sm:h-5 inline mr-2 ${activeTab === 'gdpr' ? 'text-[#d4af37]' : ''}`} />
|
<Database className={`w-4 h-4 sm:w-5 sm:h-5 inline mr-2 ${activeTab === 'gdpr' ? 'text-[var(--luxury-gold)]' : ''}`} />
|
||||||
Data Privacy
|
Data Privacy
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
@@ -490,7 +490,7 @@ const ProfilePage: React.FC = () => {
|
|||||||
|
|
||||||
{}
|
{}
|
||||||
{activeTab === 'profile' && (
|
{activeTab === 'profile' && (
|
||||||
<div className="luxury-glass rounded-sm p-4 sm:p-6 lg:p-8 border border-[#d4af37]/20 shadow-2xl animate-slide-up">
|
<div className="luxury-glass rounded-sm p-4 sm:p-6 lg:p-8 border border-[var(--luxury-gold)]/20 shadow-2xl animate-slide-up">
|
||||||
<form onSubmit={handleSubmitProfile(onSubmitProfile)} className="space-y-5 sm:space-y-6">
|
<form onSubmit={handleSubmitProfile(onSubmitProfile)} className="space-y-5 sm:space-y-6">
|
||||||
{}
|
{}
|
||||||
<div className="flex flex-col sm:flex-row items-center sm:items-start gap-4 sm:gap-6 pb-5 sm:pb-6 border-b border-gray-200">
|
<div className="flex flex-col sm:flex-row items-center sm:items-start gap-4 sm:gap-6 pb-5 sm:pb-6 border-b border-gray-200">
|
||||||
@@ -499,20 +499,20 @@ const ProfilePage: React.FC = () => {
|
|||||||
<img
|
<img
|
||||||
src={avatarPreview || normalizeImageUrl(userInfo?.avatar)}
|
src={avatarPreview || normalizeImageUrl(userInfo?.avatar)}
|
||||||
alt="Profile"
|
alt="Profile"
|
||||||
className="w-20 h-20 sm:w-24 sm:h-24 rounded-full object-cover ring-4 ring-[#d4af37]/20 shadow-lg"
|
className="w-20 h-20 sm:w-24 sm:h-24 rounded-full object-cover ring-4 ring-[var(--luxury-gold)]/20 shadow-lg"
|
||||||
onError={() => {
|
onError={() => {
|
||||||
// If image fails to load, show default avatar
|
// If image fails to load, show default avatar
|
||||||
setAvatarError(true);
|
setAvatarError(true);
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
) : (
|
) : (
|
||||||
<div className="w-20 h-20 sm:w-24 sm:h-24 rounded-full bg-gradient-to-br from-[#d4af37] to-[#c9a227] flex items-center justify-center ring-4 ring-[#d4af37]/20 shadow-lg">
|
<div className="w-20 h-20 sm:w-24 sm:h-24 rounded-full bg-gradient-to-br from-[var(--luxury-gold)] to-[var(--luxury-gold-dark)] flex items-center justify-center ring-4 ring-[var(--luxury-gold)]/20 shadow-lg">
|
||||||
<User className="w-10 h-10 sm:w-12 sm:h-12 text-white" />
|
<User className="w-10 h-10 sm:w-12 sm:h-12 text-white" />
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
<label
|
<label
|
||||||
htmlFor="avatar-upload"
|
htmlFor="avatar-upload"
|
||||||
className="absolute bottom-0 right-0 p-2 bg-gradient-to-br from-[#d4af37] to-[#c9a227] rounded-full cursor-pointer hover:from-[#f5d76e] hover:to-[#d4af37] transition-all duration-300 shadow-lg hover:shadow-xl hover:shadow-[#d4af37]/40 hover:-translate-y-0.5 active:translate-y-0"
|
className="absolute bottom-0 right-0 p-2 bg-gradient-to-br from-[var(--luxury-gold)] to-[var(--luxury-gold-dark)] rounded-full cursor-pointer hover:from-[var(--luxury-gold-light)] hover:to-[var(--luxury-gold)] transition-all duration-300 shadow-lg hover:shadow-xl hover:shadow-[var(--luxury-gold)]/40 hover:-translate-y-0.5 active:translate-y-0"
|
||||||
>
|
>
|
||||||
<Camera className="w-3.5 h-3.5 sm:w-4 sm:h-4 text-white" />
|
<Camera className="w-3.5 h-3.5 sm:w-4 sm:h-4 text-white" />
|
||||||
<input
|
<input
|
||||||
@@ -528,7 +528,7 @@ const ProfilePage: React.FC = () => {
|
|||||||
|
|
||||||
<div>
|
<div>
|
||||||
<label className="block text-sm sm:text-base font-medium text-gray-700 mb-2">
|
<label className="block text-sm sm:text-base font-medium text-gray-700 mb-2">
|
||||||
<User className="w-4 h-4 sm:w-5 sm:h-5 inline mr-2 text-[#d4af37]" />
|
<User className="w-4 h-4 sm:w-5 sm:h-5 inline mr-2 text-[var(--luxury-gold)]" />
|
||||||
Full Name
|
Full Name
|
||||||
<span className="text-red-500 ml-1">*</span>
|
<span className="text-red-500 ml-1">*</span>
|
||||||
</label>
|
</label>
|
||||||
@@ -551,7 +551,7 @@ const ProfilePage: React.FC = () => {
|
|||||||
{}
|
{}
|
||||||
<div>
|
<div>
|
||||||
<label className="block text-sm sm:text-base font-medium text-gray-700 mb-2">
|
<label className="block text-sm sm:text-base font-medium text-gray-700 mb-2">
|
||||||
<Mail className="w-4 h-4 sm:w-5 sm:h-5 inline mr-2 text-[#d4af37]" />
|
<Mail className="w-4 h-4 sm:w-5 sm:h-5 inline mr-2 text-[var(--luxury-gold)]" />
|
||||||
Email Address
|
Email Address
|
||||||
<span className="text-red-500 ml-1">*</span>
|
<span className="text-red-500 ml-1">*</span>
|
||||||
</label>
|
</label>
|
||||||
@@ -574,7 +574,7 @@ const ProfilePage: React.FC = () => {
|
|||||||
{}
|
{}
|
||||||
<div>
|
<div>
|
||||||
<label className="block text-sm sm:text-base font-medium text-gray-700 mb-2">
|
<label className="block text-sm sm:text-base font-medium text-gray-700 mb-2">
|
||||||
<Phone className="w-4 h-4 sm:w-5 sm:h-5 inline mr-2 text-[#d4af37]" />
|
<Phone className="w-4 h-4 sm:w-5 sm:h-5 inline mr-2 text-[var(--luxury-gold)]" />
|
||||||
Phone Number
|
Phone Number
|
||||||
<span className="text-red-500 ml-1">*</span>
|
<span className="text-red-500 ml-1">*</span>
|
||||||
</label>
|
</label>
|
||||||
@@ -611,7 +611,7 @@ const ProfilePage: React.FC = () => {
|
|||||||
|
|
||||||
{}
|
{}
|
||||||
{activeTab === 'password' && (
|
{activeTab === 'password' && (
|
||||||
<div className="luxury-glass rounded-sm p-4 sm:p-6 lg:p-8 border border-[#d4af37]/20 shadow-2xl animate-slide-up">
|
<div className="luxury-glass rounded-sm p-4 sm:p-6 lg:p-8 border border-[var(--luxury-gold)]/20 shadow-2xl animate-slide-up">
|
||||||
<form onSubmit={handleSubmitPassword(onSubmitPassword)} className="space-y-5 sm:space-y-6">
|
<form onSubmit={handleSubmitPassword(onSubmitPassword)} className="space-y-5 sm:space-y-6">
|
||||||
{}
|
{}
|
||||||
<div className="bg-gradient-to-r from-blue-50 to-indigo-50 border border-blue-200/60 rounded-sm p-4 sm:p-5">
|
<div className="bg-gradient-to-r from-blue-50 to-indigo-50 border border-blue-200/60 rounded-sm p-4 sm:p-5">
|
||||||
@@ -640,7 +640,7 @@ const ProfilePage: React.FC = () => {
|
|||||||
{}
|
{}
|
||||||
<div>
|
<div>
|
||||||
<label className="block text-sm sm:text-base font-medium text-gray-700 mb-2">
|
<label className="block text-sm sm:text-base font-medium text-gray-700 mb-2">
|
||||||
<Lock className="w-4 h-4 sm:w-5 sm:h-5 inline mr-2 text-[#d4af37]" />
|
<Lock className="w-4 h-4 sm:w-5 sm:h-5 inline mr-2 text-[var(--luxury-gold)]" />
|
||||||
Current Password
|
Current Password
|
||||||
<span className="text-red-500 ml-1">*</span>
|
<span className="text-red-500 ml-1">*</span>
|
||||||
</label>
|
</label>
|
||||||
@@ -656,7 +656,7 @@ const ProfilePage: React.FC = () => {
|
|||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
onClick={() => setShowPassword({ ...showPassword, current: !showPassword.current })}
|
onClick={() => setShowPassword({ ...showPassword, current: !showPassword.current })}
|
||||||
className="absolute inset-y-0 right-0 pr-3 flex items-center text-gray-400 hover:text-[#d4af37] transition-colors"
|
className="absolute inset-y-0 right-0 pr-3 flex items-center text-gray-400 hover:text-[var(--luxury-gold)] transition-colors"
|
||||||
>
|
>
|
||||||
{showPassword.current ? (
|
{showPassword.current ? (
|
||||||
<EyeOff className="w-4 h-4 sm:w-5 sm:h-5" />
|
<EyeOff className="w-4 h-4 sm:w-5 sm:h-5" />
|
||||||
@@ -676,7 +676,7 @@ const ProfilePage: React.FC = () => {
|
|||||||
{}
|
{}
|
||||||
<div>
|
<div>
|
||||||
<label className="block text-sm sm:text-base font-medium text-gray-700 mb-2">
|
<label className="block text-sm sm:text-base font-medium text-gray-700 mb-2">
|
||||||
<Lock className="w-4 h-4 sm:w-5 sm:h-5 inline mr-2 text-[#d4af37]" />
|
<Lock className="w-4 h-4 sm:w-5 sm:h-5 inline mr-2 text-[var(--luxury-gold)]" />
|
||||||
New Password
|
New Password
|
||||||
<span className="text-red-500 ml-1">*</span>
|
<span className="text-red-500 ml-1">*</span>
|
||||||
</label>
|
</label>
|
||||||
@@ -692,7 +692,7 @@ const ProfilePage: React.FC = () => {
|
|||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
onClick={() => setShowPassword({ ...showPassword, new: !showPassword.new })}
|
onClick={() => setShowPassword({ ...showPassword, new: !showPassword.new })}
|
||||||
className="absolute inset-y-0 right-0 pr-3 flex items-center text-gray-400 hover:text-[#d4af37] transition-colors"
|
className="absolute inset-y-0 right-0 pr-3 flex items-center text-gray-400 hover:text-[var(--luxury-gold)] transition-colors"
|
||||||
>
|
>
|
||||||
{showPassword.new ? (
|
{showPassword.new ? (
|
||||||
<EyeOff className="w-4 h-4 sm:w-5 sm:h-5" />
|
<EyeOff className="w-4 h-4 sm:w-5 sm:h-5" />
|
||||||
@@ -712,7 +712,7 @@ const ProfilePage: React.FC = () => {
|
|||||||
{}
|
{}
|
||||||
<div>
|
<div>
|
||||||
<label className="block text-sm sm:text-base font-medium text-gray-700 mb-2">
|
<label className="block text-sm sm:text-base font-medium text-gray-700 mb-2">
|
||||||
<Lock className="w-4 h-4 sm:w-5 sm:h-5 inline mr-2 text-[#d4af37]" />
|
<Lock className="w-4 h-4 sm:w-5 sm:h-5 inline mr-2 text-[var(--luxury-gold)]" />
|
||||||
Confirm New Password
|
Confirm New Password
|
||||||
<span className="text-red-500 ml-1">*</span>
|
<span className="text-red-500 ml-1">*</span>
|
||||||
</label>
|
</label>
|
||||||
@@ -728,7 +728,7 @@ const ProfilePage: React.FC = () => {
|
|||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
onClick={() => setShowPassword({ ...showPassword, confirm: !showPassword.confirm })}
|
onClick={() => setShowPassword({ ...showPassword, confirm: !showPassword.confirm })}
|
||||||
className="absolute inset-y-0 right-0 pr-3 flex items-center text-gray-400 hover:text-[#d4af37] transition-colors"
|
className="absolute inset-y-0 right-0 pr-3 flex items-center text-gray-400 hover:text-[var(--luxury-gold)] transition-colors"
|
||||||
>
|
>
|
||||||
{showPassword.confirm ? (
|
{showPassword.confirm ? (
|
||||||
<EyeOff className="w-4 h-4 sm:w-5 sm:h-5" />
|
<EyeOff className="w-4 h-4 sm:w-5 sm:h-5" />
|
||||||
@@ -762,11 +762,11 @@ const ProfilePage: React.FC = () => {
|
|||||||
|
|
||||||
{}
|
{}
|
||||||
{activeTab === 'mfa' && (
|
{activeTab === 'mfa' && (
|
||||||
<div className="luxury-glass rounded-sm p-4 sm:p-6 lg:p-8 border border-[#d4af37]/20 shadow-2xl animate-slide-up space-y-5 sm:space-y-6">
|
<div className="luxury-glass rounded-sm p-4 sm:p-6 lg:p-8 border border-[var(--luxury-gold)]/20 shadow-2xl animate-slide-up space-y-5 sm:space-y-6">
|
||||||
{}
|
{}
|
||||||
<div>
|
<div>
|
||||||
<h2 className="text-xl sm:text-2xl font-serif font-semibold text-gray-900 mb-2 flex items-center gap-2">
|
<h2 className="text-xl sm:text-2xl font-serif font-semibold text-gray-900 mb-2 flex items-center gap-2">
|
||||||
<Shield className="w-5 h-5 sm:w-6 sm:h-6 text-[#d4af37]" />
|
<Shield className="w-5 h-5 sm:w-6 sm:h-6 text-[var(--luxury-gold)]" />
|
||||||
Two-Factor Authentication
|
Two-Factor Authentication
|
||||||
</h2>
|
</h2>
|
||||||
<p className="text-xs sm:text-sm text-gray-600 font-light leading-relaxed">
|
<p className="text-xs sm:text-sm text-gray-600 font-light leading-relaxed">
|
||||||
@@ -838,7 +838,7 @@ const ProfilePage: React.FC = () => {
|
|||||||
{}
|
{}
|
||||||
<div className="bg-white border border-gray-200 rounded-sm p-4 sm:p-5">
|
<div className="bg-white border border-gray-200 rounded-sm p-4 sm:p-5">
|
||||||
<h3 className="text-sm sm:text-base font-semibold text-gray-900 mb-2 flex items-center gap-2">
|
<h3 className="text-sm sm:text-base font-semibold text-gray-900 mb-2 flex items-center gap-2">
|
||||||
<RefreshCw className="w-4 h-4 sm:w-5 sm:h-5 text-[#d4af37]" />
|
<RefreshCw className="w-4 h-4 sm:w-5 sm:h-5 text-[var(--luxury-gold)]" />
|
||||||
Backup Codes
|
Backup Codes
|
||||||
</h3>
|
</h3>
|
||||||
<p className="text-xs sm:text-sm text-gray-600 font-light mb-4 leading-relaxed">
|
<p className="text-xs sm:text-sm text-gray-600 font-light mb-4 leading-relaxed">
|
||||||
@@ -984,7 +984,7 @@ const ProfilePage: React.FC = () => {
|
|||||||
{}
|
{}
|
||||||
<div className="bg-white border border-gray-200 rounded-sm p-4 sm:p-5">
|
<div className="bg-white border border-gray-200 rounded-sm p-4 sm:p-5">
|
||||||
<h3 className="font-semibold text-gray-900 mb-2 text-sm sm:text-base flex items-center gap-2">
|
<h3 className="font-semibold text-gray-900 mb-2 text-sm sm:text-base flex items-center gap-2">
|
||||||
<KeyRound className="w-4 h-4 sm:w-5 sm:h-5 text-[#d4af37]" />
|
<KeyRound className="w-4 h-4 sm:w-5 sm:h-5 text-[var(--luxury-gold)]" />
|
||||||
Step 2: Verify Setup
|
Step 2: Verify Setup
|
||||||
</h3>
|
</h3>
|
||||||
<p className="text-xs sm:text-sm text-gray-600 font-light mb-4 leading-relaxed">
|
<p className="text-xs sm:text-sm text-gray-600 font-light mb-4 leading-relaxed">
|
||||||
@@ -1110,10 +1110,10 @@ const SessionsTab: React.FC = () => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const getDeviceIcon = (userAgent?: string) => {
|
const getDeviceIcon = (userAgent?: string) => {
|
||||||
if (!userAgent) return <Monitor className="w-5 h-5 text-[#d4af37]" />;
|
if (!userAgent) return <Monitor className="w-5 h-5 text-[var(--luxury-gold)]" />;
|
||||||
if (userAgent.includes('Mobile')) return <Smartphone className="w-5 h-5 text-blue-500" />;
|
if (userAgent.includes('Mobile')) return <Smartphone className="w-5 h-5 text-blue-500" />;
|
||||||
if (userAgent.includes('Tablet')) return <Tablet className="w-5 h-5 text-purple-500" />;
|
if (userAgent.includes('Tablet')) return <Tablet className="w-5 h-5 text-purple-500" />;
|
||||||
return <Monitor className="w-5 h-5 text-[#d4af37]" />;
|
return <Monitor className="w-5 h-5 text-[var(--luxury-gold)]" />;
|
||||||
};
|
};
|
||||||
|
|
||||||
const getDeviceName = (userAgent?: string) => {
|
const getDeviceName = (userAgent?: string) => {
|
||||||
@@ -1181,17 +1181,17 @@ const SessionsTab: React.FC = () => {
|
|||||||
|
|
||||||
if (loading) {
|
if (loading) {
|
||||||
return (
|
return (
|
||||||
<div className="luxury-glass rounded-sm p-4 sm:p-6 lg:p-8 border border-[#d4af37]/20 shadow-2xl">
|
<div className="luxury-glass rounded-sm p-4 sm:p-6 lg:p-8 border border-[var(--luxury-gold)]/20 shadow-2xl">
|
||||||
<Loading text="Loading sessions..." />
|
<Loading text="Loading sessions..." />
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="luxury-glass rounded-sm p-4 sm:p-6 lg:p-8 border border-[#d4af37]/20 shadow-2xl animate-slide-up space-y-5 sm:space-y-6">
|
<div className="luxury-glass rounded-sm p-4 sm:p-6 lg:p-8 border border-[var(--luxury-gold)]/20 shadow-2xl animate-slide-up space-y-5 sm:space-y-6">
|
||||||
<div>
|
<div>
|
||||||
<h2 className="text-xl sm:text-2xl font-serif font-semibold text-gray-900 mb-2 flex items-center gap-2">
|
<h2 className="text-xl sm:text-2xl font-serif font-semibold text-gray-900 mb-2 flex items-center gap-2">
|
||||||
<Monitor className="w-5 h-5 sm:w-6 sm:h-6 text-[#d4af37]" />
|
<Monitor className="w-5 h-5 sm:w-6 sm:h-6 text-[var(--luxury-gold)]" />
|
||||||
Active Sessions
|
Active Sessions
|
||||||
</h2>
|
</h2>
|
||||||
<p className="text-xs sm:text-sm text-gray-600 font-light">
|
<p className="text-xs sm:text-sm text-gray-600 font-light">
|
||||||
@@ -1220,11 +1220,11 @@ const SessionsTab: React.FC = () => {
|
|||||||
{sessions.map((session) => (
|
{sessions.map((session) => (
|
||||||
<div
|
<div
|
||||||
key={session.id}
|
key={session.id}
|
||||||
className="bg-gradient-to-r from-slate-50 to-white border border-[#d4af37]/20 rounded-sm p-4 sm:p-5 hover:shadow-lg hover:border-[#d4af37]/40 transition-all duration-300"
|
className="bg-gradient-to-r from-slate-50 to-white border border-[var(--luxury-gold)]/20 rounded-sm p-4 sm:p-5 hover:shadow-lg hover:border-[var(--luxury-gold)]/40 transition-all duration-300"
|
||||||
>
|
>
|
||||||
<div className="flex items-start justify-between gap-4">
|
<div className="flex items-start justify-between gap-4">
|
||||||
<div className="flex items-start gap-4 flex-1">
|
<div className="flex items-start gap-4 flex-1">
|
||||||
<div className="p-2 sm:p-3 bg-gradient-to-br from-[#d4af37]/10 to-[#c9a227]/10 rounded-sm flex-shrink-0">
|
<div className="p-2 sm:p-3 bg-gradient-to-br from-[var(--luxury-gold)]/10 to-[var(--luxury-gold-dark)]/10 rounded-sm flex-shrink-0">
|
||||||
{getDeviceIcon(session.user_agent)}
|
{getDeviceIcon(session.user_agent)}
|
||||||
</div>
|
</div>
|
||||||
<div className="flex-1 min-w-0">
|
<div className="flex-1 min-w-0">
|
||||||
@@ -1354,17 +1354,17 @@ const GDPRTab: React.FC = () => {
|
|||||||
|
|
||||||
if (loading) {
|
if (loading) {
|
||||||
return (
|
return (
|
||||||
<div className="luxury-glass rounded-sm p-4 sm:p-6 lg:p-8 border border-[#d4af37]/20 shadow-2xl">
|
<div className="luxury-glass rounded-sm p-4 sm:p-6 lg:p-8 border border-[var(--luxury-gold)]/20 shadow-2xl">
|
||||||
<Loading text="Loading privacy data..." />
|
<Loading text="Loading privacy data..." />
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="luxury-glass rounded-sm p-4 sm:p-6 lg:p-8 border border-[#d4af37]/20 shadow-2xl animate-slide-up space-y-5 sm:space-y-6">
|
<div className="luxury-glass rounded-sm p-4 sm:p-6 lg:p-8 border border-[var(--luxury-gold)]/20 shadow-2xl animate-slide-up space-y-5 sm:space-y-6">
|
||||||
<div>
|
<div>
|
||||||
<h2 className="text-xl sm:text-2xl font-serif font-semibold text-gray-900 mb-2 flex items-center gap-2">
|
<h2 className="text-xl sm:text-2xl font-serif font-semibold text-gray-900 mb-2 flex items-center gap-2">
|
||||||
<Database className="w-5 h-5 sm:w-6 sm:h-6 text-[#d4af37]" />
|
<Database className="w-5 h-5 sm:w-6 sm:h-6 text-[var(--luxury-gold)]" />
|
||||||
Data Privacy & GDPR Rights
|
Data Privacy & GDPR Rights
|
||||||
</h2>
|
</h2>
|
||||||
<p className="text-xs sm:text-sm text-gray-600 font-light">
|
<p className="text-xs sm:text-sm text-gray-600 font-light">
|
||||||
@@ -1431,14 +1431,14 @@ const GDPRTab: React.FC = () => {
|
|||||||
{requests.length > 0 && (
|
{requests.length > 0 && (
|
||||||
<div className="border-t border-gray-200 pt-5 sm:pt-6">
|
<div className="border-t border-gray-200 pt-5 sm:pt-6">
|
||||||
<h3 className="text-base sm:text-lg font-semibold text-gray-900 mb-4 flex items-center gap-2">
|
<h3 className="text-base sm:text-lg font-semibold text-gray-900 mb-4 flex items-center gap-2">
|
||||||
<FileText className="w-5 h-5 text-[#d4af37]" />
|
<FileText className="w-5 h-5 text-[var(--luxury-gold)]" />
|
||||||
Request History
|
Request History
|
||||||
</h3>
|
</h3>
|
||||||
<div className="space-y-3 sm:space-y-4">
|
<div className="space-y-3 sm:space-y-4">
|
||||||
{requests.map((request) => (
|
{requests.map((request) => (
|
||||||
<div
|
<div
|
||||||
key={request.id}
|
key={request.id}
|
||||||
className="bg-gradient-to-r from-slate-50 to-white border border-[#d4af37]/20 rounded-sm p-4 sm:p-5 hover:shadow-md hover:border-[#d4af37]/40 transition-all duration-300"
|
className="bg-gradient-to-r from-slate-50 to-white border border-[var(--luxury-gold)]/20 rounded-sm p-4 sm:p-5 hover:shadow-md hover:border-[var(--luxury-gold)]/40 transition-all duration-300"
|
||||||
>
|
>
|
||||||
<div className="flex flex-col sm:flex-row items-start sm:items-center justify-between gap-3 sm:gap-4">
|
<div className="flex flex-col sm:flex-row items-start sm:items-center justify-between gap-3 sm:gap-4">
|
||||||
<div className="flex-1 min-w-0">
|
<div className="flex-1 min-w-0">
|
||||||
|
|||||||
@@ -2992,10 +2992,10 @@ const ReceptionDashboardPage: React.FC = () => {
|
|||||||
{}
|
{}
|
||||||
{showRoomModal && (
|
{showRoomModal && (
|
||||||
<div className="fixed inset-0 bg-black/80 backdrop-blur-sm flex items-center justify-center z-50 p-4">
|
<div className="fixed inset-0 bg-black/80 backdrop-blur-sm flex items-center justify-center z-50 p-4">
|
||||||
<div className="bg-gradient-to-br from-[#1a1a1a] via-[#0f0f0f] to-[#1a1a1a] rounded-xl border border-[#d4af37]/30 backdrop-blur-xl shadow-2xl shadow-[#d4af37]/20 p-8 w-full max-w-4xl max-h-[90vh] overflow-y-auto">
|
<div className="bg-gradient-to-br from-[#1a1a1a] via-[#0f0f0f] to-[#1a1a1a] rounded-xl border border-[var(--luxury-gold)]/30 backdrop-blur-xl shadow-2xl shadow-[var(--luxury-gold)]/20 p-8 w-full max-w-4xl max-h-[90vh] overflow-y-auto">
|
||||||
<div className="flex justify-between items-center mb-6 pb-6 border-b border-[#d4af37]/20">
|
<div className="flex justify-between items-center mb-6 pb-6 border-b border-[var(--luxury-gold)]/20">
|
||||||
<div>
|
<div>
|
||||||
<h2 className="text-3xl font-serif font-semibold bg-gradient-to-r from-white via-[#d4af37] to-white bg-clip-text text-transparent tracking-wide">
|
<h2 className="text-3xl font-serif font-semibold bg-gradient-to-r from-white via-[var(--luxury-gold)] to-white bg-clip-text text-transparent tracking-wide">
|
||||||
{editingRoom ? 'Update Room' : 'Add New Room'}
|
{editingRoom ? 'Update Room' : 'Add New Room'}
|
||||||
</h2>
|
</h2>
|
||||||
<p className="text-gray-400 text-sm mt-1 font-light">
|
<p className="text-gray-400 text-sm mt-1 font-light">
|
||||||
@@ -3004,17 +3004,17 @@ const ReceptionDashboardPage: React.FC = () => {
|
|||||||
</div>
|
</div>
|
||||||
<button
|
<button
|
||||||
onClick={() => setShowRoomModal(false)}
|
onClick={() => setShowRoomModal(false)}
|
||||||
className="p-2 hover:bg-[#d4af37]/10 rounded-lg transition-colors border border-[#d4af37]/20 hover:border-[#d4af37]/40"
|
className="p-2 hover:bg-[var(--luxury-gold)]/10 rounded-lg transition-colors border border-[var(--luxury-gold)]/20 hover:border-[var(--luxury-gold)]/40"
|
||||||
>
|
>
|
||||||
<X className="w-6 h-6 text-[#d4af37] hover:text-white transition-colors" />
|
<X className="w-6 h-6 text-[var(--luxury-gold)] hover:text-white transition-colors" />
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<form onSubmit={handleRoomSubmit} className="space-y-6">
|
<form onSubmit={handleRoomSubmit} className="space-y-6">
|
||||||
{}
|
{}
|
||||||
<div className="space-y-4">
|
<div className="space-y-4">
|
||||||
<h3 className="text-lg font-serif font-semibold text-[#d4af37] mb-4 flex items-center gap-2">
|
<h3 className="text-lg font-serif font-semibold text-[var(--luxury-gold)] mb-4 flex items-center gap-2">
|
||||||
<div className="w-1 h-6 bg-gradient-to-b from-[#d4af37] to-[#c9a227]" />
|
<div className="w-1 h-6 bg-gradient-to-b from-[var(--luxury-gold)] to-[var(--luxury-gold-dark)]" />
|
||||||
Basic Information
|
Basic Information
|
||||||
</h3>
|
</h3>
|
||||||
|
|
||||||
@@ -3027,7 +3027,7 @@ const ReceptionDashboardPage: React.FC = () => {
|
|||||||
type="text"
|
type="text"
|
||||||
value={roomFormData.room_number}
|
value={roomFormData.room_number}
|
||||||
onChange={(e) => setRoomFormData({ ...roomFormData, room_number: e.target.value })}
|
onChange={(e) => setRoomFormData({ ...roomFormData, room_number: e.target.value })}
|
||||||
className="w-full px-4 py-3 bg-[#0a0a0a] border border-[#d4af37]/20 rounded-lg text-white placeholder-gray-500 focus:ring-2 focus:ring-[#d4af37]/50 focus:border-[#d4af37] transition-all duration-300"
|
className="w-full px-4 py-3 bg-[#0a0a0a] border border-[var(--luxury-gold)]/20 rounded-lg text-white placeholder-gray-500 focus:ring-2 focus:ring-[var(--luxury-gold)]/50 focus:border-[var(--luxury-gold)] transition-all duration-300"
|
||||||
placeholder="e.g., 1001"
|
placeholder="e.g., 1001"
|
||||||
required
|
required
|
||||||
/>
|
/>
|
||||||
@@ -3040,7 +3040,7 @@ const ReceptionDashboardPage: React.FC = () => {
|
|||||||
type="number"
|
type="number"
|
||||||
value={roomFormData.floor}
|
value={roomFormData.floor}
|
||||||
onChange={(e) => setRoomFormData({ ...roomFormData, floor: parseInt(e.target.value) })}
|
onChange={(e) => setRoomFormData({ ...roomFormData, floor: parseInt(e.target.value) })}
|
||||||
className="w-full px-4 py-3 bg-[#0a0a0a] border border-[#d4af37]/20 rounded-lg text-white placeholder-gray-500 focus:ring-2 focus:ring-[#d4af37]/50 focus:border-[#d4af37] transition-all duration-300"
|
className="w-full px-4 py-3 bg-[#0a0a0a] border border-[var(--luxury-gold)]/20 rounded-lg text-white placeholder-gray-500 focus:ring-2 focus:ring-[var(--luxury-gold)]/50 focus:border-[var(--luxury-gold)] transition-all duration-300"
|
||||||
required
|
required
|
||||||
min="1"
|
min="1"
|
||||||
/>
|
/>
|
||||||
@@ -3054,7 +3054,7 @@ const ReceptionDashboardPage: React.FC = () => {
|
|||||||
<select
|
<select
|
||||||
value={roomFormData.room_type_id}
|
value={roomFormData.room_type_id}
|
||||||
onChange={(e) => setRoomFormData({ ...roomFormData, room_type_id: parseInt(e.target.value) })}
|
onChange={(e) => setRoomFormData({ ...roomFormData, room_type_id: parseInt(e.target.value) })}
|
||||||
className="w-full px-4 py-3 bg-[#0a0a0a] border border-[#d4af37]/20 rounded-lg text-white focus:ring-2 focus:ring-[#d4af37]/50 focus:border-[#d4af37] transition-all duration-300"
|
className="w-full px-4 py-3 bg-[#0a0a0a] border border-[var(--luxury-gold)]/20 rounded-lg text-white focus:ring-2 focus:ring-[var(--luxury-gold)]/50 focus:border-[var(--luxury-gold)] transition-all duration-300"
|
||||||
required
|
required
|
||||||
>
|
>
|
||||||
{roomTypes.length > 0 ? (
|
{roomTypes.length > 0 ? (
|
||||||
@@ -3076,7 +3076,7 @@ const ReceptionDashboardPage: React.FC = () => {
|
|||||||
<select
|
<select
|
||||||
value={roomFormData.status}
|
value={roomFormData.status}
|
||||||
onChange={(e) => setRoomFormData({ ...roomFormData, status: e.target.value as any })}
|
onChange={(e) => setRoomFormData({ ...roomFormData, status: e.target.value as any })}
|
||||||
className="w-full px-4 py-3 bg-[#0a0a0a] border border-[#d4af37]/20 rounded-lg text-white focus:ring-2 focus:ring-[#d4af37]/50 focus:border-[#d4af37] transition-all duration-300"
|
className="w-full px-4 py-3 bg-[#0a0a0a] border border-[var(--luxury-gold)]/20 rounded-lg text-white focus:ring-2 focus:ring-[var(--luxury-gold)]/50 focus:border-[var(--luxury-gold)] transition-all duration-300"
|
||||||
required
|
required
|
||||||
>
|
>
|
||||||
<option value="available" className="bg-[#1a1a1a]">Available</option>
|
<option value="available" className="bg-[#1a1a1a]">Available</option>
|
||||||
@@ -3085,13 +3085,13 @@ const ReceptionDashboardPage: React.FC = () => {
|
|||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="flex items-center gap-3 p-4 bg-[#0a0a0a] rounded-lg border border-[#d4af37]/20">
|
<div className="flex items-center gap-3 p-4 bg-[#0a0a0a] rounded-lg border border-[var(--luxury-gold)]/20">
|
||||||
<input
|
<input
|
||||||
type="checkbox"
|
type="checkbox"
|
||||||
id="featured"
|
id="featured"
|
||||||
checked={roomFormData.featured}
|
checked={roomFormData.featured}
|
||||||
onChange={(e) => setRoomFormData({ ...roomFormData, featured: e.target.checked })}
|
onChange={(e) => setRoomFormData({ ...roomFormData, featured: e.target.checked })}
|
||||||
className="w-5 h-5 text-[#d4af37] bg-[#1a1a1a] border-[#d4af37]/30 rounded focus:ring-[#d4af37]/50 focus:ring-2 cursor-pointer transition-all"
|
className="w-5 h-5 text-[var(--luxury-gold)] bg-[#1a1a1a] border-[var(--luxury-gold)]/30 rounded focus:ring-[var(--luxury-gold)]/50 focus:ring-2 cursor-pointer transition-all"
|
||||||
/>
|
/>
|
||||||
<label htmlFor="featured" className="text-sm text-gray-300 cursor-pointer font-medium">
|
<label htmlFor="featured" className="text-sm text-gray-300 cursor-pointer font-medium">
|
||||||
Featured Room
|
Featured Room
|
||||||
@@ -3108,7 +3108,7 @@ const ReceptionDashboardPage: React.FC = () => {
|
|||||||
min="0"
|
min="0"
|
||||||
value={roomFormData.price}
|
value={roomFormData.price}
|
||||||
onChange={(e) => setRoomFormData({ ...roomFormData, price: e.target.value })}
|
onChange={(e) => setRoomFormData({ ...roomFormData, price: e.target.value })}
|
||||||
className="w-full px-4 py-3 bg-[#0a0a0a] border border-[#d4af37]/20 rounded-lg text-white placeholder-gray-500 focus:ring-2 focus:ring-[#d4af37]/50 focus:border-[#d4af37] transition-all duration-300"
|
className="w-full px-4 py-3 bg-[#0a0a0a] border border-[var(--luxury-gold)]/20 rounded-lg text-white placeholder-gray-500 focus:ring-2 focus:ring-[var(--luxury-gold)]/50 focus:border-[var(--luxury-gold)] transition-all duration-300"
|
||||||
placeholder="e.g., 150.00"
|
placeholder="e.g., 150.00"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
@@ -3121,7 +3121,7 @@ const ReceptionDashboardPage: React.FC = () => {
|
|||||||
value={roomFormData.description}
|
value={roomFormData.description}
|
||||||
onChange={(e) => setRoomFormData({ ...roomFormData, description: e.target.value })}
|
onChange={(e) => setRoomFormData({ ...roomFormData, description: e.target.value })}
|
||||||
rows={4}
|
rows={4}
|
||||||
className="w-full px-4 py-3 bg-[#0a0a0a] border border-[#d4af37]/20 rounded-lg text-white placeholder-gray-500 focus:ring-2 focus:ring-[#d4af37]/50 focus:border-[#d4af37] transition-all duration-300 resize-none"
|
className="w-full px-4 py-3 bg-[#0a0a0a] border border-[var(--luxury-gold)]/20 rounded-lg text-white placeholder-gray-500 focus:ring-2 focus:ring-[var(--luxury-gold)]/50 focus:border-[var(--luxury-gold)] transition-all duration-300 resize-none"
|
||||||
placeholder="Enter the actual description for this specific room..."
|
placeholder="Enter the actual description for this specific room..."
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
@@ -3136,7 +3136,7 @@ const ReceptionDashboardPage: React.FC = () => {
|
|||||||
min="1"
|
min="1"
|
||||||
value={roomFormData.capacity}
|
value={roomFormData.capacity}
|
||||||
onChange={(e) => setRoomFormData({ ...roomFormData, capacity: e.target.value })}
|
onChange={(e) => setRoomFormData({ ...roomFormData, capacity: e.target.value })}
|
||||||
className="w-full px-4 py-3 bg-[#0a0a0a] border border-[#d4af37]/20 rounded-lg text-white placeholder-gray-500 focus:ring-2 focus:ring-[#d4af37]/50 focus:border-[#d4af37] transition-all duration-300"
|
className="w-full px-4 py-3 bg-[#0a0a0a] border border-[var(--luxury-gold)]/20 rounded-lg text-white placeholder-gray-500 focus:ring-2 focus:ring-[var(--luxury-gold)]/50 focus:border-[var(--luxury-gold)] transition-all duration-300"
|
||||||
placeholder="e.g., 4"
|
placeholder="e.g., 4"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
@@ -3149,7 +3149,7 @@ const ReceptionDashboardPage: React.FC = () => {
|
|||||||
type="text"
|
type="text"
|
||||||
value={roomFormData.room_size}
|
value={roomFormData.room_size}
|
||||||
onChange={(e) => setRoomFormData({ ...roomFormData, room_size: e.target.value })}
|
onChange={(e) => setRoomFormData({ ...roomFormData, room_size: e.target.value })}
|
||||||
className="w-full px-4 py-3 bg-[#0a0a0a] border border-[#d4af37]/20 rounded-lg text-white placeholder-gray-500 focus:ring-2 focus:ring-[#d4af37]/50 focus:border-[#d4af37] transition-all duration-300"
|
className="w-full px-4 py-3 bg-[#0a0a0a] border border-[var(--luxury-gold)]/20 rounded-lg text-white placeholder-gray-500 focus:ring-2 focus:ring-[var(--luxury-gold)]/50 focus:border-[var(--luxury-gold)] transition-all duration-300"
|
||||||
placeholder="e.g., 1 Room, 50 sqm"
|
placeholder="e.g., 1 Room, 50 sqm"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
@@ -3163,7 +3163,7 @@ const ReceptionDashboardPage: React.FC = () => {
|
|||||||
type="text"
|
type="text"
|
||||||
value={roomFormData.view}
|
value={roomFormData.view}
|
||||||
onChange={(e) => setRoomFormData({ ...roomFormData, view: e.target.value })}
|
onChange={(e) => setRoomFormData({ ...roomFormData, view: e.target.value })}
|
||||||
className="w-full px-4 py-3 bg-[#0a0a0a] border border-[#d4af37]/20 rounded-lg text-white placeholder-gray-500 focus:ring-2 focus:ring-[#d4af37]/50 focus:border-[#d4af37] transition-all duration-300"
|
className="w-full px-4 py-3 bg-[#0a0a0a] border border-[var(--luxury-gold)]/20 rounded-lg text-white placeholder-gray-500 focus:ring-2 focus:ring-[var(--luxury-gold)]/50 focus:border-[var(--luxury-gold)] transition-all duration-300"
|
||||||
placeholder="e.g., City View, Ocean View, Mountain View"
|
placeholder="e.g., City View, Ocean View, Mountain View"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
@@ -3171,12 +3171,12 @@ const ReceptionDashboardPage: React.FC = () => {
|
|||||||
|
|
||||||
{}
|
{}
|
||||||
<div className="space-y-4">
|
<div className="space-y-4">
|
||||||
<h3 className="text-lg font-serif font-semibold text-[#d4af37] mb-4 flex items-center gap-2">
|
<h3 className="text-lg font-serif font-semibold text-[var(--luxury-gold)] mb-4 flex items-center gap-2">
|
||||||
<div className="w-1 h-6 bg-gradient-to-b from-[#d4af37] to-[#c9a227]" />
|
<div className="w-1 h-6 bg-gradient-to-b from-[var(--luxury-gold)] to-[var(--luxury-gold-dark)]" />
|
||||||
Amenities & Features
|
Amenities & Features
|
||||||
</h3>
|
</h3>
|
||||||
|
|
||||||
<div className="border border-[#d4af37]/20 rounded-lg p-4 max-h-80 overflow-y-auto bg-[#0a0a0a]/50 backdrop-blur-sm">
|
<div className="border border-[var(--luxury-gold)]/20 rounded-lg p-4 max-h-80 overflow-y-auto bg-[#0a0a0a]/50 backdrop-blur-sm">
|
||||||
{availableAmenities.length === 0 ? (
|
{availableAmenities.length === 0 ? (
|
||||||
<p className="text-sm text-gray-400 text-center py-4">Loading amenities...</p>
|
<p className="text-sm text-gray-400 text-center py-4">Loading amenities...</p>
|
||||||
) : (
|
) : (
|
||||||
@@ -3188,8 +3188,8 @@ const ReceptionDashboardPage: React.FC = () => {
|
|||||||
key={amenity}
|
key={amenity}
|
||||||
className={`flex items-center gap-3 p-3 rounded-lg cursor-pointer transition-all duration-300 ${
|
className={`flex items-center gap-3 p-3 rounded-lg cursor-pointer transition-all duration-300 ${
|
||||||
isSelected
|
isSelected
|
||||||
? 'bg-gradient-to-r from-[#d4af37]/20 to-[#c9a227]/20 border-2 border-[#d4af37] shadow-lg shadow-[#d4af37]/20'
|
? 'bg-gradient-to-r from-[var(--luxury-gold)]/20 to-[var(--luxury-gold-dark)]/20 border-2 border-[var(--luxury-gold)] shadow-lg shadow-[var(--luxury-gold)]/20'
|
||||||
: 'bg-[#1a1a1a]/50 border-2 border-[#333] hover:border-[#d4af37]/30 hover:bg-[#1a1a1a]'
|
: 'bg-[#1a1a1a]/50 border-2 border-[#333] hover:border-[var(--luxury-gold)]/30 hover:bg-[#1a1a1a]'
|
||||||
}`}
|
}`}
|
||||||
>
|
>
|
||||||
<input
|
<input
|
||||||
@@ -3200,14 +3200,14 @@ const ReceptionDashboardPage: React.FC = () => {
|
|||||||
/>
|
/>
|
||||||
<div className={`w-5 h-5 rounded border-2 flex items-center justify-center transition-all duration-300 ${
|
<div className={`w-5 h-5 rounded border-2 flex items-center justify-center transition-all duration-300 ${
|
||||||
isSelected
|
isSelected
|
||||||
? 'bg-[#d4af37] border-[#d4af37] shadow-lg shadow-[#d4af37]/30'
|
? 'bg-[var(--luxury-gold)] border-[var(--luxury-gold)] shadow-lg shadow-[var(--luxury-gold)]/30'
|
||||||
: 'border-gray-600 bg-transparent'
|
: 'border-gray-600 bg-transparent'
|
||||||
}`}>
|
}`}>
|
||||||
{isSelected && <Check className="w-3 h-3 text-[#0f0f0f] font-bold" />}
|
{isSelected && <Check className="w-3 h-3 text-[#0f0f0f] font-bold" />}
|
||||||
</div>
|
</div>
|
||||||
<span className={`text-sm flex-1 transition-colors ${
|
<span className={`text-sm flex-1 transition-colors ${
|
||||||
isSelected
|
isSelected
|
||||||
? 'font-semibold text-[#d4af37]'
|
? 'font-semibold text-[var(--luxury-gold)]'
|
||||||
: 'text-gray-400 hover:text-gray-300'
|
: 'text-gray-400 hover:text-gray-300'
|
||||||
}`}>
|
}`}>
|
||||||
{amenity}
|
{amenity}
|
||||||
@@ -3226,17 +3226,17 @@ const ReceptionDashboardPage: React.FC = () => {
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
{}
|
{}
|
||||||
<div className="flex gap-4 pt-4 border-t border-[#d4af37]/20">
|
<div className="flex gap-4 pt-4 border-t border-[var(--luxury-gold)]/20">
|
||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
onClick={() => setShowRoomModal(false)}
|
onClick={() => setShowRoomModal(false)}
|
||||||
className="flex-1 px-6 py-3 border border-[#d4af37]/30 text-gray-300 rounded-lg hover:bg-[#d4af37]/10 hover:border-[#d4af37] transition-all duration-300 font-medium"
|
className="flex-1 px-6 py-3 border border-[var(--luxury-gold)]/30 text-gray-300 rounded-lg hover:bg-[var(--luxury-gold)]/10 hover:border-[var(--luxury-gold)] transition-all duration-300 font-medium"
|
||||||
>
|
>
|
||||||
Cancel
|
Cancel
|
||||||
</button>
|
</button>
|
||||||
<button
|
<button
|
||||||
type="submit"
|
type="submit"
|
||||||
className="flex-1 px-6 py-3 bg-gradient-to-r from-[#d4af37] to-[#c9a227] text-[#0f0f0f] rounded-lg hover:from-[#f5d76e] hover:to-[#d4af37] transition-all duration-300 font-semibold shadow-lg shadow-[#d4af37]/30 hover:shadow-xl hover:shadow-[#d4af37]/40"
|
className="flex-1 px-6 py-3 bg-gradient-to-r from-[var(--luxury-gold)] to-[var(--luxury-gold-dark)] text-[#0f0f0f] rounded-lg hover:from-[var(--luxury-gold-light)] hover:to-[var(--luxury-gold)] transition-all duration-300 font-semibold shadow-lg shadow-[var(--luxury-gold)]/30 hover:shadow-xl hover:shadow-[var(--luxury-gold)]/40"
|
||||||
>
|
>
|
||||||
{editingRoom ? 'Update Room' : 'Add Room'}
|
{editingRoom ? 'Update Room' : 'Add Room'}
|
||||||
</button>
|
</button>
|
||||||
@@ -3245,9 +3245,9 @@ const ReceptionDashboardPage: React.FC = () => {
|
|||||||
|
|
||||||
{}
|
{}
|
||||||
{editingRoom && (
|
{editingRoom && (
|
||||||
<div className="mt-8 pt-8 border-t border-[#d4af37]/20">
|
<div className="mt-8 pt-8 border-t border-[var(--luxury-gold)]/20">
|
||||||
<h3 className="text-lg font-serif font-semibold text-[#d4af37] mb-6 flex items-center gap-2">
|
<h3 className="text-lg font-serif font-semibold text-[var(--luxury-gold)] mb-6 flex items-center gap-2">
|
||||||
<div className="w-1 h-6 bg-gradient-to-b from-[#d4af37] to-[#c9a227]" />
|
<div className="w-1 h-6 bg-gradient-to-b from-[var(--luxury-gold)] to-[var(--luxury-gold-dark)]" />
|
||||||
<ImageIcon className="w-5 h-5" />
|
<ImageIcon className="w-5 h-5" />
|
||||||
Room Images
|
Room Images
|
||||||
</h3>
|
</h3>
|
||||||
@@ -3316,8 +3316,8 @@ const ReceptionDashboardPage: React.FC = () => {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<div key={index} className="relative group">
|
<div key={index} className="relative group">
|
||||||
<div className="overflow-hidden rounded-lg border border-[#d4af37]/20
|
<div className="overflow-hidden rounded-lg border border-[var(--luxury-gold)]/20
|
||||||
hover:border-[#d4af37] transition-all duration-300">
|
hover:border-[var(--luxury-gold)] transition-all duration-300">
|
||||||
<img
|
<img
|
||||||
src={imageUrl}
|
src={imageUrl}
|
||||||
alt={`Room Image ${index + 1}`}
|
alt={`Room Image ${index + 1}`}
|
||||||
@@ -3331,7 +3331,7 @@ const ReceptionDashboardPage: React.FC = () => {
|
|||||||
</div>
|
</div>
|
||||||
{isRoomImage && (
|
{isRoomImage && (
|
||||||
<>
|
<>
|
||||||
<div className="absolute top-2 left-2 bg-[#d4af37]/90 backdrop-blur-sm
|
<div className="absolute top-2 left-2 bg-[var(--luxury-gold)]/90 backdrop-blur-sm
|
||||||
text-[#0f0f0f] px-2 py-1 rounded text-xs font-semibold
|
text-[#0f0f0f] px-2 py-1 rounded text-xs font-semibold
|
||||||
opacity-0 group-hover:opacity-100 transition-opacity duration-300">
|
opacity-0 group-hover:opacity-100 transition-opacity duration-300">
|
||||||
Room Image
|
Room Image
|
||||||
@@ -3354,7 +3354,7 @@ const ReceptionDashboardPage: React.FC = () => {
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
) : (
|
) : (
|
||||||
<div className="mb-6 p-8 bg-[#0a0a0a]/50 rounded-lg border border-[#d4af37]/10 text-center">
|
<div className="mb-6 p-8 bg-[#0a0a0a]/50 rounded-lg border border-[var(--luxury-gold)]/10 text-center">
|
||||||
<ImageIcon className="w-12 h-12 text-gray-600 mx-auto mb-3" />
|
<ImageIcon className="w-12 h-12 text-gray-600 mx-auto mb-3" />
|
||||||
<p className="text-sm text-gray-400 font-light">
|
<p className="text-sm text-gray-400 font-light">
|
||||||
No images uploaded yet. Upload images below to display them here.
|
No images uploaded yet. Upload images below to display them here.
|
||||||
@@ -3380,10 +3380,10 @@ const ReceptionDashboardPage: React.FC = () => {
|
|||||||
className="w-full text-sm text-gray-400
|
className="w-full text-sm text-gray-400
|
||||||
file:mr-4 file:py-3 file:px-6 file:rounded-lg file:border-0
|
file:mr-4 file:py-3 file:px-6 file:rounded-lg file:border-0
|
||||||
file:text-sm file:font-semibold
|
file:text-sm file:font-semibold
|
||||||
file:bg-gradient-to-r file:from-[#d4af37]/20 file:to-[#c9a227]/20
|
file:bg-gradient-to-r file:from-[var(--luxury-gold)]/20 file:to-[var(--luxury-gold-dark)]/20
|
||||||
file:text-[#d4af37] file:border file:border-[#d4af37]/30
|
file:text-[var(--luxury-gold)] file:border file:border-[var(--luxury-gold)]/30
|
||||||
hover:file:from-[#d4af37]/30 hover:file:to-[#c9a227]/30
|
hover:file:from-[var(--luxury-gold)]/30 hover:file:to-[var(--luxury-gold-dark)]/30
|
||||||
hover:file:border-[#d4af37] file:cursor-pointer
|
hover:file:border-[var(--luxury-gold)] file:cursor-pointer
|
||||||
transition-all duration-300 bg-[#0a0a0a] rounded-lg"
|
transition-all duration-300 bg-[#0a0a0a] rounded-lg"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
@@ -3397,7 +3397,7 @@ const ReceptionDashboardPage: React.FC = () => {
|
|||||||
type="button"
|
type="button"
|
||||||
onClick={handleUploadImages}
|
onClick={handleUploadImages}
|
||||||
disabled={uploadingImages}
|
disabled={uploadingImages}
|
||||||
className="px-6 py-2 bg-gradient-to-r from-[#d4af37] to-[#c9a227] text-[#0f0f0f] rounded-lg hover:from-[#f5d76e] hover:to-[#d4af37] transition-all duration-300 font-semibold shadow-lg shadow-[#d4af37]/30 hover:shadow-xl hover:shadow-[#d4af37]/40 disabled:opacity-50 disabled:cursor-not-allowed"
|
className="px-6 py-2 bg-gradient-to-r from-[var(--luxury-gold)] to-[var(--luxury-gold-dark)] text-[#0f0f0f] rounded-lg hover:from-[var(--luxury-gold-light)] hover:to-[var(--luxury-gold)] transition-all duration-300 font-semibold shadow-lg shadow-[var(--luxury-gold)]/30 hover:shadow-xl hover:shadow-[var(--luxury-gold)]/40 disabled:opacity-50 disabled:cursor-not-allowed"
|
||||||
>
|
>
|
||||||
{uploadingImages ? 'Uploading...' : 'Upload Images'}
|
{uploadingImages ? 'Uploading...' : 'Upload Images'}
|
||||||
</button>
|
</button>
|
||||||
@@ -3407,7 +3407,7 @@ const ReceptionDashboardPage: React.FC = () => {
|
|||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
<div className="mt-8 pt-6 border-t border-[#d4af37]/20 flex justify-end">
|
<div className="mt-8 pt-6 border-t border-[var(--luxury-gold)]/20 flex justify-end">
|
||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ const AdminDashboardPage = lazy(() => import('../pages/admin/DashboardPage'));
|
|||||||
const UserManagementPage = lazy(() => import('../pages/admin/UserManagementPage'));
|
const UserManagementPage = lazy(() => import('../pages/admin/UserManagementPage'));
|
||||||
const GuestProfilePage = lazy(() => import('../pages/admin/GuestProfilePage'));
|
const GuestProfilePage = lazy(() => import('../pages/admin/GuestProfilePage'));
|
||||||
const GroupBookingManagementPage = lazy(() => import('../pages/admin/GroupBookingManagementPage'));
|
const GroupBookingManagementPage = lazy(() => import('../pages/admin/GroupBookingManagementPage'));
|
||||||
const BusinessDashboardPage = lazy(() => import('../pages/admin/BusinessDashboardPage'));
|
const PromotionsManagementPage = lazy(() => import('../pages/admin/PromotionsManagementPage'));
|
||||||
const ReceptionDashboardPage = lazy(() => import('../pages/admin/ReceptionDashboardPage'));
|
const ReceptionDashboardPage = lazy(() => import('../pages/admin/ReceptionDashboardPage'));
|
||||||
const AdvancedRoomManagementPage = lazy(() => import('../pages/admin/AdvancedRoomManagementPage'));
|
const AdvancedRoomManagementPage = lazy(() => import('../pages/admin/AdvancedRoomManagementPage'));
|
||||||
const PageContentDashboardPage = lazy(() => import('../pages/admin/PageContentDashboard'));
|
const PageContentDashboardPage = lazy(() => import('../pages/admin/PageContentDashboard'));
|
||||||
@@ -39,7 +39,7 @@ const adminRoutes: RouteObject[] = [
|
|||||||
{ path: 'users', element: <UserManagementPage /> },
|
{ path: 'users', element: <UserManagementPage /> },
|
||||||
{ path: 'guest-profiles', element: <GuestProfilePage /> },
|
{ path: 'guest-profiles', element: <GuestProfilePage /> },
|
||||||
{ path: 'group-bookings', element: <GroupBookingManagementPage /> },
|
{ path: 'group-bookings', element: <GroupBookingManagementPage /> },
|
||||||
{ path: 'business', element: <BusinessDashboardPage /> },
|
{ path: 'promotions', element: <PromotionsManagementPage /> },
|
||||||
{ path: 'reception', element: <ReceptionDashboardPage /> },
|
{ path: 'reception', element: <ReceptionDashboardPage /> },
|
||||||
{ path: 'advanced-rooms', element: <AdvancedRoomManagementPage /> },
|
{ path: 'advanced-rooms', element: <AdvancedRoomManagementPage /> },
|
||||||
{ path: 'page-content', element: <PageContentDashboardPage /> },
|
{ path: 'page-content', element: <PageContentDashboardPage /> },
|
||||||
|
|||||||
@@ -57,11 +57,11 @@ const ConfirmationDialog: React.FC<ConfirmationDialogProps> = ({
|
|||||||
|
|
||||||
{}
|
{}
|
||||||
<div className="flex min-h-full items-end justify-center p-4 text-center sm:items-center sm:p-0">
|
<div className="flex min-h-full items-end justify-center p-4 text-center sm:items-center sm:p-0">
|
||||||
<div className="relative transform overflow-hidden rounded-sm bg-gradient-to-b from-white to-gray-50 text-left shadow-2xl border border-[#d4af37]/20 transition-all sm:my-8 sm:w-full sm:max-w-lg animate-fade-in">
|
<div className="relative transform overflow-hidden rounded-sm bg-gradient-to-b from-white to-gray-50 text-left shadow-2xl border border-[var(--luxury-gold)]/20 transition-all sm:my-8 sm:w-full sm:max-w-lg animate-fade-in">
|
||||||
{}
|
{}
|
||||||
<button
|
<button
|
||||||
onClick={onClose}
|
onClick={onClose}
|
||||||
className="absolute right-4 top-4 text-gray-400 hover:text-[#d4af37] focus:outline-none focus:ring-2 focus:ring-[#d4af37]/50 rounded-sm p-1 transition-colors"
|
className="absolute right-4 top-4 text-gray-400 hover:text-[var(--luxury-gold)] focus:outline-none focus:ring-2 focus:ring-[var(--luxury-gold)]/50 rounded-sm p-1 transition-colors"
|
||||||
aria-label="Close"
|
aria-label="Close"
|
||||||
>
|
>
|
||||||
<X className="w-5 h-5" />
|
<X className="w-5 h-5" />
|
||||||
@@ -74,8 +74,8 @@ const ConfirmationDialog: React.FC<ConfirmationDialogProps> = ({
|
|||||||
variant === 'danger'
|
variant === 'danger'
|
||||||
? 'bg-red-100 border-2 border-red-200'
|
? 'bg-red-100 border-2 border-red-200'
|
||||||
: variant === 'warning'
|
: variant === 'warning'
|
||||||
? 'bg-[#d4af37]/20 border-2 border-[#d4af37]/40'
|
? 'bg-[var(--luxury-gold)]/20 border-2 border-[var(--luxury-gold)]/40'
|
||||||
: 'bg-[#d4af37]/20 border-2 border-[#d4af37]/40'
|
: 'bg-[var(--luxury-gold)]/20 border-2 border-[var(--luxury-gold)]/40'
|
||||||
} sm:mx-0 sm:h-10 sm:w-10`}
|
} sm:mx-0 sm:h-10 sm:w-10`}
|
||||||
>
|
>
|
||||||
<AlertTriangle
|
<AlertTriangle
|
||||||
@@ -83,8 +83,8 @@ const ConfirmationDialog: React.FC<ConfirmationDialogProps> = ({
|
|||||||
variant === 'danger'
|
variant === 'danger'
|
||||||
? 'text-red-600'
|
? 'text-red-600'
|
||||||
: variant === 'warning'
|
: variant === 'warning'
|
||||||
? 'text-[#d4af37]'
|
? 'text-[var(--luxury-gold)]'
|
||||||
: 'text-[#d4af37]'
|
: 'text-[var(--luxury-gold)]'
|
||||||
}`}
|
}`}
|
||||||
aria-hidden="true"
|
aria-hidden="true"
|
||||||
/>
|
/>
|
||||||
@@ -111,8 +111,8 @@ const ConfirmationDialog: React.FC<ConfirmationDialogProps> = ({
|
|||||||
variant === 'danger'
|
variant === 'danger'
|
||||||
? 'bg-red-600 hover:bg-red-700 focus:ring-red-500'
|
? 'bg-red-600 hover:bg-red-700 focus:ring-red-500'
|
||||||
: variant === 'warning'
|
: variant === 'warning'
|
||||||
? 'bg-gradient-to-r from-[#d4af37] to-[#c9a227] hover:from-[#f5d76e] hover:to-[#d4af37] focus:ring-[#d4af37]'
|
? 'bg-gradient-to-r from-[var(--luxury-gold)] to-[var(--luxury-gold-dark)] hover:from-[var(--luxury-gold-light)] hover:to-[var(--luxury-gold)] focus:ring-[var(--luxury-gold)]'
|
||||||
: 'btn-luxury-primary focus:ring-[#d4af37]'
|
: 'btn-luxury-primary focus:ring-[var(--luxury-gold)]'
|
||||||
}`}
|
}`}
|
||||||
>
|
>
|
||||||
{isLoading ? (
|
{isLoading ? (
|
||||||
@@ -147,7 +147,7 @@ const ConfirmationDialog: React.FC<ConfirmationDialogProps> = ({
|
|||||||
type="button"
|
type="button"
|
||||||
onClick={onClose}
|
onClick={onClose}
|
||||||
disabled={isLoading}
|
disabled={isLoading}
|
||||||
className="mt-3 inline-flex w-full justify-center rounded-sm bg-white/80 backdrop-blur-sm px-4 py-2.5 text-sm font-medium tracking-wide text-gray-700 shadow-sm border border-gray-300 hover:bg-white hover:border-[#d4af37]/30 hover:text-[#d4af37] sm:mt-0 sm:w-auto disabled:opacity-50 disabled:cursor-not-allowed transition-all"
|
className="mt-3 inline-flex w-full justify-center rounded-sm bg-white/80 backdrop-blur-sm px-4 py-2.5 text-sm font-medium tracking-wide text-gray-700 shadow-sm border border-gray-300 hover:bg-white hover:border-[var(--luxury-gold)]/30 hover:text-[var(--luxury-gold)] sm:mt-0 sm:w-auto disabled:opacity-50 disabled:cursor-not-allowed transition-all"
|
||||||
>
|
>
|
||||||
{cancelText}
|
{cancelText}
|
||||||
</button>
|
</button>
|
||||||
|
|||||||
@@ -60,10 +60,10 @@ const CookieConsentBanner: React.FC = () => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="pointer-events-none fixed inset-x-0 bottom-0 z-40 flex justify-center px-4 pb-4 sm:px-6 sm:pb-6">
|
<div className="pointer-events-none fixed inset-x-0 bottom-0 z-[9998] flex justify-center px-4 pb-4 sm:px-6 sm:pb-6">
|
||||||
<div className="pointer-events-auto relative w-full max-w-4xl overflow-hidden rounded-2xl bg-gradient-to-r from-black/85 via-zinc-900/90 to-black/85 p-[1px] shadow-[0_24px_60px_rgba(0,0,0,0.8)]">
|
<div className="pointer-events-auto relative w-full max-w-4xl overflow-hidden rounded-2xl bg-gradient-to-r from-black/85 via-zinc-900/90 to-black/85 p-[1px] shadow-[0_24px_60px_rgba(0,0,0,0.8)]">
|
||||||
{}
|
{}
|
||||||
<div className="absolute inset-0 rounded-2xl border border-[#d4af37]/40" />
|
<div className="absolute inset-0 rounded-2xl border border-[var(--luxury-gold)]/40" />
|
||||||
|
|
||||||
{}
|
{}
|
||||||
<div className="pointer-events-none absolute -inset-8 bg-[radial-gradient(circle_at_top,_rgba(212,175,55,0.18),_transparent_55%),radial-gradient(circle_at_bottom,_rgba(0,0,0,0.8),_transparent_60%)] opacity-80" />
|
<div className="pointer-events-none absolute -inset-8 bg-[radial-gradient(circle_at_top,_rgba(212,175,55,0.18),_transparent_55%),radial-gradient(circle_at_bottom,_rgba(0,0,0,0.8),_transparent_60%)] opacity-80" />
|
||||||
@@ -71,8 +71,8 @@ const CookieConsentBanner: React.FC = () => {
|
|||||||
<div className="relative flex flex-col gap-4 bg-gradient-to-br from-zinc-950/80 via-zinc-900/90 to-black/90 px-4 py-4 sm:px-6 sm:py-5 lg:px-8 lg:py-6 sm:flex-row sm:items-start sm:justify-between">
|
<div className="relative flex flex-col gap-4 bg-gradient-to-br from-zinc-950/80 via-zinc-900/90 to-black/90 px-4 py-4 sm:px-6 sm:py-5 lg:px-8 lg:py-6 sm:flex-row sm:items-start sm:justify-between">
|
||||||
{}
|
{}
|
||||||
<div className="space-y-3 sm:max-w-xl">
|
<div className="space-y-3 sm:max-w-xl">
|
||||||
<div className="inline-flex items-center gap-2 rounded-full bg-black/60 px-3 py-1 text-[11px] font-medium uppercase tracking-[0.16em] text-[#d4af37]/90 ring-1 ring-[#d4af37]/30">
|
<div className="inline-flex items-center gap-2 rounded-full bg-black/60 px-3 py-1 text-[11px] font-medium uppercase tracking-[0.16em] text-[var(--luxury-gold)]/90 ring-1 ring-[var(--luxury-gold)]/30">
|
||||||
<span className="h-1.5 w-1.5 rounded-full bg-[#d4af37]" />
|
<span className="h-1.5 w-1.5 rounded-full bg-[var(--luxury-gold)]" />
|
||||||
Privacy Suite
|
Privacy Suite
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@@ -90,14 +90,14 @@ const CookieConsentBanner: React.FC = () => {
|
|||||||
<div className="flex flex-wrap items-center gap-3">
|
<div className="flex flex-wrap items-center gap-3">
|
||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
className="text-[11px] font-semibold uppercase tracking-[0.16em] text-[#d4af37] underline underline-offset-4 hover:text-[#f6e7b4]"
|
className="text-[11px] font-semibold uppercase tracking-[0.16em] text-[var(--luxury-gold)] underline underline-offset-4 hover:text-[#f6e7b4]"
|
||||||
onClick={() => setShowDetails((prev) => !prev)}
|
onClick={() => setShowDetails((prev) => !prev)}
|
||||||
>
|
>
|
||||||
{showDetails ? 'Hide detailed preferences' : 'Fine-tune preferences'}
|
{showDetails ? 'Hide detailed preferences' : 'Fine-tune preferences'}
|
||||||
</button>
|
</button>
|
||||||
<Link
|
<Link
|
||||||
to="/gdpr"
|
to="/gdpr"
|
||||||
className="text-[11px] font-semibold uppercase tracking-[0.16em] text-[#d4af37] underline underline-offset-4 hover:text-[#f6e7b4]"
|
className="text-[11px] font-semibold uppercase tracking-[0.16em] text-[var(--luxury-gold)] underline underline-offset-4 hover:text-[#f6e7b4]"
|
||||||
>
|
>
|
||||||
Data Privacy (GDPR)
|
Data Privacy (GDPR)
|
||||||
</Link>
|
</Link>
|
||||||
@@ -107,7 +107,7 @@ const CookieConsentBanner: React.FC = () => {
|
|||||||
<div className="mt-1.5 space-y-3 rounded-xl bg-black/40 p-3 ring-1 ring-zinc-800/80 backdrop-blur-md sm:p-4">
|
<div className="mt-1.5 space-y-3 rounded-xl bg-black/40 p-3 ring-1 ring-zinc-800/80 backdrop-blur-md sm:p-4">
|
||||||
<div className="flex flex-col gap-2 text-xs text-zinc-200 sm:text-[13px]">
|
<div className="flex flex-col gap-2 text-xs text-zinc-200 sm:text-[13px]">
|
||||||
<div className="flex items-start gap-3">
|
<div className="flex items-start gap-3">
|
||||||
<div className="mt-0.5 h-4 w-4 rounded border border-[#d4af37]/50 bg-[#d4af37]/20" />
|
<div className="mt-0.5 h-4 w-4 rounded border border-[var(--luxury-gold)]/50 bg-[var(--luxury-gold)]/20" />
|
||||||
<div>
|
<div>
|
||||||
<p className="font-semibold text-zinc-50">Strictly necessary</p>
|
<p className="font-semibold text-zinc-50">Strictly necessary</p>
|
||||||
<p className="text-[11px] text-zinc-400">
|
<p className="text-[11px] text-zinc-400">
|
||||||
@@ -121,7 +121,7 @@ const CookieConsentBanner: React.FC = () => {
|
|||||||
<input
|
<input
|
||||||
id="cookie-analytics"
|
id="cookie-analytics"
|
||||||
type="checkbox"
|
type="checkbox"
|
||||||
className="mt-0.5 h-4 w-4 rounded border-zinc-500 bg-black/40 text-[#d4af37] focus:ring-[#d4af37]"
|
className="mt-0.5 h-4 w-4 rounded border-zinc-500 bg-black/40 text-[var(--luxury-gold)] focus:ring-[var(--luxury-gold)]"
|
||||||
checked={analyticsChecked}
|
checked={analyticsChecked}
|
||||||
onChange={(e) => setAnalyticsChecked(e.target.checked)}
|
onChange={(e) => setAnalyticsChecked(e.target.checked)}
|
||||||
/>
|
/>
|
||||||
@@ -138,7 +138,7 @@ const CookieConsentBanner: React.FC = () => {
|
|||||||
<input
|
<input
|
||||||
id="cookie-marketing"
|
id="cookie-marketing"
|
||||||
type="checkbox"
|
type="checkbox"
|
||||||
className="mt-0.5 h-4 w-4 rounded border-zinc-500 bg-black/40 text-[#d4af37] focus:ring-[#d4af37]"
|
className="mt-0.5 h-4 w-4 rounded border-zinc-500 bg-black/40 text-[var(--luxury-gold)] focus:ring-[var(--luxury-gold)]"
|
||||||
checked={marketingChecked}
|
checked={marketingChecked}
|
||||||
onChange={(e) => setMarketingChecked(e.target.checked)}
|
onChange={(e) => setMarketingChecked(e.target.checked)}
|
||||||
/>
|
/>
|
||||||
@@ -155,7 +155,7 @@ const CookieConsentBanner: React.FC = () => {
|
|||||||
<input
|
<input
|
||||||
id="cookie-preferences"
|
id="cookie-preferences"
|
||||||
type="checkbox"
|
type="checkbox"
|
||||||
className="mt-0.5 h-4 w-4 rounded border-zinc-500 bg-black/40 text-[#d4af37] focus:ring-[#d4af37]"
|
className="mt-0.5 h-4 w-4 rounded border-zinc-500 bg-black/40 text-[var(--luxury-gold)] focus:ring-[var(--luxury-gold)]"
|
||||||
checked={preferencesChecked}
|
checked={preferencesChecked}
|
||||||
onChange={(e) => setPreferencesChecked(e.target.checked)}
|
onChange={(e) => setPreferencesChecked(e.target.checked)}
|
||||||
/>
|
/>
|
||||||
@@ -176,7 +176,7 @@ const CookieConsentBanner: React.FC = () => {
|
|||||||
<div className="mt-2 flex flex-col gap-2 sm:mt-0 sm:w-56">
|
<div className="mt-2 flex flex-col gap-2 sm:mt-0 sm:w-56">
|
||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
className="inline-flex w-full items-center justify-center rounded-full bg-gradient-to-r from-[#d4af37] via-[#f2cf74] to-[#d4af37] px-4 py-2.5 text-xs font-semibold uppercase tracking-[0.16em] text-black shadow-[0_10px_30px_rgba(0,0,0,0.6)] transition hover:from-[#f8e4a6] hover:via-[#ffe6a3] hover:to-[#f2cf74] focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-[#d4af37]/70"
|
className="inline-flex w-full items-center justify-center rounded-full bg-gradient-to-r from-[var(--luxury-gold)] via-[#f2cf74] to-[var(--luxury-gold)] px-4 py-2.5 text-xs font-semibold uppercase tracking-[0.16em] text-black shadow-[0_10px_30px_rgba(0,0,0,0.6)] transition hover:from-[#f8e4a6] hover:via-[#ffe6a3] hover:to-[#f2cf74] focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-[var(--luxury-gold)]/70"
|
||||||
onClick={handleAcceptAll}
|
onClick={handleAcceptAll}
|
||||||
>
|
>
|
||||||
Accept all & continue
|
Accept all & continue
|
||||||
@@ -191,7 +191,7 @@ const CookieConsentBanner: React.FC = () => {
|
|||||||
{showDetails && (
|
{showDetails && (
|
||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
className="inline-flex w-full items-center justify-center rounded-full border border-zinc-700 bg-zinc-900/80 px-4 py-2.5 text-xs font-semibold uppercase tracking-[0.16em] text-zinc-100 shadow-[0_8px_22px_rgba(0,0,0,0.6)] transition hover:border-[#d4af37]/60 hover:text-[#f5e9c6] focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-[#d4af37]/70"
|
className="inline-flex w-full items-center justify-center rounded-full border border-zinc-700 bg-zinc-900/80 px-4 py-2.5 text-xs font-semibold uppercase tracking-[0.16em] text-zinc-100 shadow-[0_8px_22px_rgba(0,0,0,0.6)] transition hover:border-[var(--luxury-gold)]/60 hover:text-[#f5e9c6] focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-[var(--luxury-gold)]/70"
|
||||||
onClick={handleSaveSelection}
|
onClick={handleSaveSelection}
|
||||||
>
|
>
|
||||||
Save my selection
|
Save my selection
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ const CookiePreferencesLink: React.FC = () => {
|
|||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
onClick={handleClick}
|
onClick={handleClick}
|
||||||
className="hover:text-[#d4af37] transition-colors cursor-pointer font-light tracking-wide text-gray-700"
|
className="hover:text-[var(--luxury-gold)] transition-colors cursor-pointer font-light tracking-wide text-gray-700"
|
||||||
>
|
>
|
||||||
Cookie Preferences
|
Cookie Preferences
|
||||||
</button>
|
</button>
|
||||||
|
|||||||
@@ -79,10 +79,10 @@ const CookiePreferencesModal: React.FC = () => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="fixed inset-0 z-50 overflow-y-auto" aria-labelledby="modal-title" role="dialog" aria-modal="true">
|
<div className="fixed inset-0 z-[10000] overflow-y-auto" aria-labelledby="modal-title" role="dialog" aria-modal="true">
|
||||||
{/* Backdrop */}
|
{/* Backdrop */}
|
||||||
<div
|
<div
|
||||||
className="fixed inset-0 bg-black/70 backdrop-blur-sm transition-opacity"
|
className="fixed inset-0 bg-black/70 backdrop-blur-sm transition-opacity z-[10000]"
|
||||||
onClick={() => setIsOpen(false)}
|
onClick={() => setIsOpen(false)}
|
||||||
aria-hidden="true"
|
aria-hidden="true"
|
||||||
/>
|
/>
|
||||||
@@ -91,7 +91,7 @@ const CookiePreferencesModal: React.FC = () => {
|
|||||||
<div className="flex min-h-full items-center justify-center p-4 text-center sm:p-0">
|
<div className="flex min-h-full items-center justify-center p-4 text-center sm:p-0">
|
||||||
<div
|
<div
|
||||||
ref={modalRef}
|
ref={modalRef}
|
||||||
className="relative transform overflow-hidden rounded-2xl bg-gradient-to-br from-zinc-950/95 via-zinc-900/95 to-black/95 text-left shadow-2xl border border-[#d4af37]/30 transition-all sm:my-8 sm:w-full sm:max-w-2xl"
|
className="relative transform overflow-hidden rounded-2xl bg-gradient-to-br from-zinc-950/95 via-zinc-900/95 to-black/95 text-left shadow-2xl border border-[var(--luxury-gold)]/30 transition-all sm:my-8 sm:w-full sm:max-w-2xl"
|
||||||
>
|
>
|
||||||
{/* Close button */}
|
{/* Close button */}
|
||||||
<button
|
<button
|
||||||
@@ -105,8 +105,8 @@ const CookiePreferencesModal: React.FC = () => {
|
|||||||
<div className="px-6 py-6 sm:px-8 sm:py-8">
|
<div className="px-6 py-6 sm:px-8 sm:py-8">
|
||||||
{/* Header */}
|
{/* Header */}
|
||||||
<div className="mb-6">
|
<div className="mb-6">
|
||||||
<div className="inline-flex items-center gap-2 rounded-full bg-black/60 px-3 py-1 text-[11px] font-medium uppercase tracking-[0.16em] text-[#d4af37]/90 ring-1 ring-[#d4af37]/30 mb-4">
|
<div className="inline-flex items-center gap-2 rounded-full bg-black/60 px-3 py-1 text-[11px] font-medium uppercase tracking-[0.16em] text-[var(--luxury-gold)]/90 ring-1 ring-[var(--luxury-gold)]/30 mb-4">
|
||||||
<span className="h-1.5 w-1.5 rounded-full bg-[#d4af37]" />
|
<span className="h-1.5 w-1.5 rounded-full bg-[var(--luxury-gold)]" />
|
||||||
Privacy Suite
|
Privacy Suite
|
||||||
</div>
|
</div>
|
||||||
<h2 className="text-2xl font-elegant font-bold text-white mb-2">
|
<h2 className="text-2xl font-elegant font-bold text-white mb-2">
|
||||||
@@ -121,8 +121,8 @@ const CookiePreferencesModal: React.FC = () => {
|
|||||||
<div className="space-y-4 mb-6">
|
<div className="space-y-4 mb-6">
|
||||||
<div className="rounded-xl bg-black/40 p-4 ring-1 ring-zinc-800/80 backdrop-blur-md">
|
<div className="rounded-xl bg-black/40 p-4 ring-1 ring-zinc-800/80 backdrop-blur-md">
|
||||||
<div className="flex items-start gap-3">
|
<div className="flex items-start gap-3">
|
||||||
<div className="mt-0.5 h-5 w-5 rounded border border-[#d4af37]/50 bg-[#d4af37]/20 flex items-center justify-center">
|
<div className="mt-0.5 h-5 w-5 rounded border border-[var(--luxury-gold)]/50 bg-[var(--luxury-gold)]/20 flex items-center justify-center">
|
||||||
<span className="text-[#d4af37] text-xs">✓</span>
|
<span className="text-[var(--luxury-gold)] text-xs">✓</span>
|
||||||
</div>
|
</div>
|
||||||
<div className="flex-1">
|
<div className="flex-1">
|
||||||
<p className="font-semibold text-zinc-50 mb-1">Strictly necessary</p>
|
<p className="font-semibold text-zinc-50 mb-1">Strictly necessary</p>
|
||||||
@@ -138,7 +138,7 @@ const CookiePreferencesModal: React.FC = () => {
|
|||||||
<input
|
<input
|
||||||
id="cookie-analytics-modal"
|
id="cookie-analytics-modal"
|
||||||
type="checkbox"
|
type="checkbox"
|
||||||
className="mt-0.5 h-5 w-5 rounded border-zinc-500 bg-black/40 text-[#d4af37] focus:ring-[#d4af37] cursor-pointer"
|
className="mt-0.5 h-5 w-5 rounded border-zinc-500 bg-black/40 text-[var(--luxury-gold)] focus:ring-[var(--luxury-gold)] cursor-pointer"
|
||||||
checked={analyticsChecked}
|
checked={analyticsChecked}
|
||||||
onChange={(e) => setAnalyticsChecked(e.target.checked)}
|
onChange={(e) => setAnalyticsChecked(e.target.checked)}
|
||||||
/>
|
/>
|
||||||
@@ -156,7 +156,7 @@ const CookiePreferencesModal: React.FC = () => {
|
|||||||
<input
|
<input
|
||||||
id="cookie-marketing-modal"
|
id="cookie-marketing-modal"
|
||||||
type="checkbox"
|
type="checkbox"
|
||||||
className="mt-0.5 h-5 w-5 rounded border-zinc-500 bg-black/40 text-[#d4af37] focus:ring-[#d4af37] cursor-pointer"
|
className="mt-0.5 h-5 w-5 rounded border-zinc-500 bg-black/40 text-[var(--luxury-gold)] focus:ring-[var(--luxury-gold)] cursor-pointer"
|
||||||
checked={marketingChecked}
|
checked={marketingChecked}
|
||||||
onChange={(e) => setMarketingChecked(e.target.checked)}
|
onChange={(e) => setMarketingChecked(e.target.checked)}
|
||||||
/>
|
/>
|
||||||
@@ -174,7 +174,7 @@ const CookiePreferencesModal: React.FC = () => {
|
|||||||
<input
|
<input
|
||||||
id="cookie-preferences-modal"
|
id="cookie-preferences-modal"
|
||||||
type="checkbox"
|
type="checkbox"
|
||||||
className="mt-0.5 h-5 w-5 rounded border-zinc-500 bg-black/40 text-[#d4af37] focus:ring-[#d4af37] cursor-pointer"
|
className="mt-0.5 h-5 w-5 rounded border-zinc-500 bg-black/40 text-[var(--luxury-gold)] focus:ring-[var(--luxury-gold)] cursor-pointer"
|
||||||
checked={preferencesChecked}
|
checked={preferencesChecked}
|
||||||
onChange={(e) => setPreferencesChecked(e.target.checked)}
|
onChange={(e) => setPreferencesChecked(e.target.checked)}
|
||||||
/>
|
/>
|
||||||
@@ -193,7 +193,7 @@ const CookiePreferencesModal: React.FC = () => {
|
|||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
onClick={handleAcceptAll}
|
onClick={handleAcceptAll}
|
||||||
className="flex-1 inline-flex items-center justify-center rounded-full bg-gradient-to-r from-[#d4af37] via-[#f2cf74] to-[#d4af37] px-6 py-3 text-sm font-semibold uppercase tracking-[0.16em] text-black shadow-lg transition hover:from-[#f8e4a6] hover:via-[#ffe6a3] hover:to-[#f2cf74] focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-[#d4af37]/70"
|
className="flex-1 inline-flex items-center justify-center rounded-full bg-gradient-to-r from-[var(--luxury-gold)] via-[#f2cf74] to-[var(--luxury-gold)] px-6 py-3 text-sm font-semibold uppercase tracking-[0.16em] text-black shadow-lg transition hover:from-[#f8e4a6] hover:via-[#ffe6a3] hover:to-[#f2cf74] focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-[var(--luxury-gold)]/70"
|
||||||
>
|
>
|
||||||
Accept all
|
Accept all
|
||||||
</button>
|
</button>
|
||||||
@@ -207,7 +207,7 @@ const CookiePreferencesModal: React.FC = () => {
|
|||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
onClick={handleSaveSelection}
|
onClick={handleSaveSelection}
|
||||||
className="flex-1 inline-flex items-center justify-center rounded-full border border-[#d4af37]/60 bg-zinc-900/80 px-6 py-3 text-sm font-semibold uppercase tracking-[0.16em] text-[#d4af37] shadow-lg transition hover:border-[#d4af37] hover:bg-zinc-800/80 hover:text-[#f5e9c6] focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-[#d4af37]/70"
|
className="flex-1 inline-flex items-center justify-center rounded-full border border-[var(--luxury-gold)]/60 bg-zinc-900/80 px-6 py-3 text-sm font-semibold uppercase tracking-[0.16em] text-[var(--luxury-gold)] shadow-lg transition hover:border-[var(--luxury-gold)] hover:bg-zinc-800/80 hover:text-[#f5e9c6] focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-[var(--luxury-gold)]/70"
|
||||||
>
|
>
|
||||||
Save selection
|
Save selection
|
||||||
</button>
|
</button>
|
||||||
|
|||||||
@@ -83,7 +83,7 @@ const CurrencySelector: React.FC<CurrencySelectorProps> = ({
|
|||||||
<select
|
<select
|
||||||
value={currency}
|
value={currency}
|
||||||
onChange={handleChange}
|
onChange={handleChange}
|
||||||
className="appearance-none bg-white border border-gray-300 rounded-lg px-4 py-2 pr-8 text-sm font-medium text-gray-900 focus:outline-none focus:ring-2 focus:ring-[#d4af37] focus:border-[#d4af37] transition-colors cursor-pointer"
|
className="appearance-none bg-white border border-gray-300 rounded-lg px-4 py-2 pr-8 text-sm font-medium text-gray-900 focus:outline-none focus:ring-2 focus:ring-[var(--luxury-gold)] focus:border-[var(--luxury-gold)] transition-colors cursor-pointer"
|
||||||
>
|
>
|
||||||
{supportedCurrencies.map((curr) => (
|
{supportedCurrencies.map((curr) => (
|
||||||
<option key={curr} value={curr}>
|
<option key={curr} value={curr}>
|
||||||
@@ -110,7 +110,7 @@ const CurrencySelector: React.FC<CurrencySelectorProps> = ({
|
|||||||
<select
|
<select
|
||||||
value={currency}
|
value={currency}
|
||||||
onChange={handleChange}
|
onChange={handleChange}
|
||||||
className="px-3 py-1.5 border border-gray-300 rounded-md text-sm font-medium text-gray-900 bg-white focus:outline-none focus:ring-2 focus:ring-[#d4af37] focus:border-[#d4af37] transition-colors cursor-pointer"
|
className="px-3 py-1.5 border border-gray-300 rounded-md text-sm font-medium text-gray-900 bg-white focus:outline-none focus:ring-2 focus:ring-[var(--luxury-gold)] focus:border-[var(--luxury-gold)] transition-colors cursor-pointer"
|
||||||
>
|
>
|
||||||
{supportedCurrencies.map((curr) => (
|
{supportedCurrencies.map((curr) => (
|
||||||
<option key={curr} value={curr}>
|
<option key={curr} value={curr}>
|
||||||
|
|||||||
@@ -194,7 +194,7 @@ const Footer: React.FC = () => {
|
|||||||
return (
|
return (
|
||||||
<footer className="relative bg-gradient-to-b from-[#0f0f0f] via-[#1a1a1a] to-black text-gray-300 overflow-hidden">
|
<footer className="relative bg-gradient-to-b from-[#0f0f0f] via-[#1a1a1a] to-black text-gray-300 overflow-hidden">
|
||||||
{/* Top Gold Accent Line */}
|
{/* Top Gold Accent Line */}
|
||||||
<div className="absolute top-0 left-0 right-0 h-[2px] bg-gradient-to-r from-transparent via-[#d4af37] to-transparent shadow-lg shadow-[#d4af37]/50"></div>
|
<div className="absolute top-0 left-0 right-0 h-[2px] bg-gradient-to-r from-transparent via-[var(--luxury-gold)] to-transparent shadow-lg shadow-[var(--luxury-gold)]/50"></div>
|
||||||
|
|
||||||
{/* Decorative Pattern Overlay */}
|
{/* Decorative Pattern Overlay */}
|
||||||
<div className="absolute inset-0 opacity-[0.03]" style={{
|
<div className="absolute inset-0 opacity-[0.03]" style={{
|
||||||
@@ -212,7 +212,7 @@ const Footer: React.FC = () => {
|
|||||||
<div className="flex items-center space-x-4 mb-6 sm:mb-8">
|
<div className="flex items-center space-x-4 mb-6 sm:mb-8">
|
||||||
{logoUrl ? (
|
{logoUrl ? (
|
||||||
<div className="relative group">
|
<div className="relative group">
|
||||||
<div className="absolute inset-0 bg-gradient-to-r from-[#d4af37]/20 to-transparent rounded-lg blur-xl opacity-0 group-hover:opacity-100 transition-opacity duration-500"></div>
|
<div className="absolute inset-0 bg-gradient-to-r from-[var(--luxury-gold)]/20 to-transparent rounded-lg blur-xl opacity-0 group-hover:opacity-100 transition-opacity duration-500"></div>
|
||||||
<img
|
<img
|
||||||
src={logoUrl}
|
src={logoUrl}
|
||||||
alt={settings.company_name}
|
alt={settings.company_name}
|
||||||
@@ -221,17 +221,17 @@ const Footer: React.FC = () => {
|
|||||||
</div>
|
</div>
|
||||||
) : (
|
) : (
|
||||||
<div className="relative group">
|
<div className="relative group">
|
||||||
<div className="p-3 bg-gradient-to-br from-[#d4af37]/10 to-[#c9a227]/5 rounded-lg border border-[#d4af37]/20 backdrop-blur-sm">
|
<div className="p-3 bg-gradient-to-br from-[var(--luxury-gold)]/10 to-[var(--luxury-gold-dark)]/5 rounded-lg border border-[var(--luxury-gold)]/20 backdrop-blur-sm">
|
||||||
<Hotel className="w-8 h-8 sm:w-10 sm:h-10 text-[#d4af37] relative z-10 drop-shadow-lg" />
|
<Hotel className="w-8 h-8 sm:w-10 sm:h-10 text-[var(--luxury-gold)] relative z-10 drop-shadow-lg" />
|
||||||
</div>
|
</div>
|
||||||
<div className="absolute inset-0 bg-[#d4af37]/20 blur-2xl opacity-50 group-hover:opacity-75 transition-opacity duration-500"></div>
|
<div className="absolute inset-0 bg-[var(--luxury-gold)]/20 blur-2xl opacity-50 group-hover:opacity-75 transition-opacity duration-500"></div>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
<div>
|
<div>
|
||||||
<h2 className="text-2xl sm:text-3xl font-display font-semibold text-white tracking-tight mb-1">
|
<h2 className="text-2xl sm:text-3xl font-display font-semibold text-white tracking-tight mb-1">
|
||||||
{settings.company_name || pageContent?.title || 'Luxury Hotel'}
|
{settings.company_name || pageContent?.title || 'Luxury Hotel'}
|
||||||
</h2>
|
</h2>
|
||||||
<p className="text-xs sm:text-sm text-[#d4af37] font-light tracking-[3px] sm:tracking-[4px] uppercase">
|
<p className="text-xs sm:text-sm text-[var(--luxury-gold)] font-light tracking-[3px] sm:tracking-[4px] uppercase">
|
||||||
{settings.company_tagline || 'Excellence Redefined'}
|
{settings.company_tagline || 'Excellence Redefined'}
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
@@ -249,10 +249,10 @@ const Footer: React.FC = () => {
|
|||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
key={index}
|
key={index}
|
||||||
className="group flex items-center space-x-2 px-3 py-2 bg-gradient-to-r from-[#d4af37]/5 to-transparent border border-[#d4af37]/10 rounded-lg hover:border-[#d4af37]/30 hover:from-[#d4af37]/10 transition-all duration-300"
|
className="group flex items-center space-x-2 px-3 py-2 bg-gradient-to-r from-[var(--luxury-gold)]/5 to-transparent border border-[var(--luxury-gold)]/10 rounded-lg hover:border-[var(--luxury-gold)]/30 hover:from-[var(--luxury-gold)]/10 transition-all duration-300"
|
||||||
>
|
>
|
||||||
<BadgeIcon className="w-4 h-4 sm:w-5 sm:h-5 text-[#d4af37] group-hover:scale-110 transition-transform duration-300" />
|
<BadgeIcon className="w-4 h-4 sm:w-5 sm:h-5 text-[var(--luxury-gold)] group-hover:scale-110 transition-transform duration-300" />
|
||||||
<span className="text-xs sm:text-sm font-medium tracking-wide text-gray-300 group-hover:text-[#d4af37] transition-colors">{badge.text}</span>
|
<span className="text-xs sm:text-sm font-medium tracking-wide text-gray-300 group-hover:text-[var(--luxury-gold)] transition-colors">{badge.text}</span>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
})}
|
})}
|
||||||
@@ -266,11 +266,11 @@ const Footer: React.FC = () => {
|
|||||||
href={pageContent.social_links.facebook}
|
href={pageContent.social_links.facebook}
|
||||||
target="_blank"
|
target="_blank"
|
||||||
rel="noopener noreferrer"
|
rel="noopener noreferrer"
|
||||||
className="group relative w-12 h-12 sm:w-14 sm:h-14 flex items-center justify-center rounded-lg bg-gradient-to-br from-gray-800/60 to-gray-900/60 backdrop-blur-sm border border-gray-700/50 hover:border-[#d4af37]/60 transition-all duration-300 hover:bg-gradient-to-br hover:from-[#d4af37]/10 hover:to-[#c9a227]/10 hover:shadow-lg hover:shadow-[#d4af37]/20 hover:-translate-y-0.5"
|
className="group relative w-12 h-12 sm:w-14 sm:h-14 flex items-center justify-center rounded-lg bg-gradient-to-br from-gray-800/60 to-gray-900/60 backdrop-blur-sm border border-gray-700/50 hover:border-[var(--luxury-gold)]/60 transition-all duration-300 hover:bg-gradient-to-br hover:from-[var(--luxury-gold)]/10 hover:to-[var(--luxury-gold-dark)]/10 hover:shadow-lg hover:shadow-[var(--luxury-gold)]/20 hover:-translate-y-0.5"
|
||||||
aria-label="Facebook"
|
aria-label="Facebook"
|
||||||
>
|
>
|
||||||
<Facebook className="w-5 h-5 sm:w-6 sm:h-6 text-gray-400 group-hover:text-[#d4af37] transition-all duration-300 group-hover:scale-110" />
|
<Facebook className="w-5 h-5 sm:w-6 sm:h-6 text-gray-400 group-hover:text-[var(--luxury-gold)] transition-all duration-300 group-hover:scale-110" />
|
||||||
<div className="absolute inset-0 rounded-lg bg-[#d4af37]/0 group-hover:bg-[#d4af37]/10 blur-xl transition-all duration-500"></div>
|
<div className="absolute inset-0 rounded-lg bg-[var(--luxury-gold)]/0 group-hover:bg-[var(--luxury-gold)]/10 blur-xl transition-all duration-500"></div>
|
||||||
</a>
|
</a>
|
||||||
)}
|
)}
|
||||||
{pageContent?.social_links?.twitter && (
|
{pageContent?.social_links?.twitter && (
|
||||||
@@ -278,11 +278,11 @@ const Footer: React.FC = () => {
|
|||||||
href={pageContent.social_links.twitter}
|
href={pageContent.social_links.twitter}
|
||||||
target="_blank"
|
target="_blank"
|
||||||
rel="noopener noreferrer"
|
rel="noopener noreferrer"
|
||||||
className="group relative w-12 h-12 sm:w-14 sm:h-14 flex items-center justify-center rounded-lg bg-gradient-to-br from-gray-800/60 to-gray-900/60 backdrop-blur-sm border border-gray-700/50 hover:border-[#d4af37]/60 transition-all duration-300 hover:bg-gradient-to-br hover:from-[#d4af37]/10 hover:to-[#c9a227]/10 hover:shadow-lg hover:shadow-[#d4af37]/20 hover:-translate-y-0.5"
|
className="group relative w-12 h-12 sm:w-14 sm:h-14 flex items-center justify-center rounded-lg bg-gradient-to-br from-gray-800/60 to-gray-900/60 backdrop-blur-sm border border-gray-700/50 hover:border-[var(--luxury-gold)]/60 transition-all duration-300 hover:bg-gradient-to-br hover:from-[var(--luxury-gold)]/10 hover:to-[var(--luxury-gold-dark)]/10 hover:shadow-lg hover:shadow-[var(--luxury-gold)]/20 hover:-translate-y-0.5"
|
||||||
aria-label="Twitter"
|
aria-label="Twitter"
|
||||||
>
|
>
|
||||||
<Twitter className="w-5 h-5 sm:w-6 sm:h-6 text-gray-400 group-hover:text-[#d4af37] transition-all duration-300 group-hover:scale-110" />
|
<Twitter className="w-5 h-5 sm:w-6 sm:h-6 text-gray-400 group-hover:text-[var(--luxury-gold)] transition-all duration-300 group-hover:scale-110" />
|
||||||
<div className="absolute inset-0 rounded-lg bg-[#d4af37]/0 group-hover:bg-[#d4af37]/10 blur-xl transition-all duration-500"></div>
|
<div className="absolute inset-0 rounded-lg bg-[var(--luxury-gold)]/0 group-hover:bg-[var(--luxury-gold)]/10 blur-xl transition-all duration-500"></div>
|
||||||
</a>
|
</a>
|
||||||
)}
|
)}
|
||||||
{pageContent?.social_links?.instagram && (
|
{pageContent?.social_links?.instagram && (
|
||||||
@@ -290,11 +290,11 @@ const Footer: React.FC = () => {
|
|||||||
href={pageContent.social_links.instagram}
|
href={pageContent.social_links.instagram}
|
||||||
target="_blank"
|
target="_blank"
|
||||||
rel="noopener noreferrer"
|
rel="noopener noreferrer"
|
||||||
className="group relative w-12 h-12 sm:w-14 sm:h-14 flex items-center justify-center rounded-lg bg-gradient-to-br from-gray-800/60 to-gray-900/60 backdrop-blur-sm border border-gray-700/50 hover:border-[#d4af37]/60 transition-all duration-300 hover:bg-gradient-to-br hover:from-[#d4af37]/10 hover:to-[#c9a227]/10 hover:shadow-lg hover:shadow-[#d4af37]/20 hover:-translate-y-0.5"
|
className="group relative w-12 h-12 sm:w-14 sm:h-14 flex items-center justify-center rounded-lg bg-gradient-to-br from-gray-800/60 to-gray-900/60 backdrop-blur-sm border border-gray-700/50 hover:border-[var(--luxury-gold)]/60 transition-all duration-300 hover:bg-gradient-to-br hover:from-[var(--luxury-gold)]/10 hover:to-[var(--luxury-gold-dark)]/10 hover:shadow-lg hover:shadow-[var(--luxury-gold)]/20 hover:-translate-y-0.5"
|
||||||
aria-label="Instagram"
|
aria-label="Instagram"
|
||||||
>
|
>
|
||||||
<Instagram className="w-5 h-5 sm:w-6 sm:h-6 text-gray-400 group-hover:text-[#d4af37] transition-all duration-300 group-hover:scale-110" />
|
<Instagram className="w-5 h-5 sm:w-6 sm:h-6 text-gray-400 group-hover:text-[var(--luxury-gold)] transition-all duration-300 group-hover:scale-110" />
|
||||||
<div className="absolute inset-0 rounded-lg bg-[#d4af37]/0 group-hover:bg-[#d4af37]/10 blur-xl transition-all duration-500"></div>
|
<div className="absolute inset-0 rounded-lg bg-[var(--luxury-gold)]/0 group-hover:bg-[var(--luxury-gold)]/10 blur-xl transition-all duration-500"></div>
|
||||||
</a>
|
</a>
|
||||||
)}
|
)}
|
||||||
{pageContent?.social_links?.linkedin && (
|
{pageContent?.social_links?.linkedin && (
|
||||||
@@ -302,11 +302,11 @@ const Footer: React.FC = () => {
|
|||||||
href={pageContent.social_links.linkedin}
|
href={pageContent.social_links.linkedin}
|
||||||
target="_blank"
|
target="_blank"
|
||||||
rel="noopener noreferrer"
|
rel="noopener noreferrer"
|
||||||
className="group relative w-12 h-12 sm:w-14 sm:h-14 flex items-center justify-center rounded-lg bg-gradient-to-br from-gray-800/60 to-gray-900/60 backdrop-blur-sm border border-gray-700/50 hover:border-[#d4af37]/60 transition-all duration-300 hover:bg-gradient-to-br hover:from-[#d4af37]/10 hover:to-[#c9a227]/10 hover:shadow-lg hover:shadow-[#d4af37]/20 hover:-translate-y-0.5"
|
className="group relative w-12 h-12 sm:w-14 sm:h-14 flex items-center justify-center rounded-lg bg-gradient-to-br from-gray-800/60 to-gray-900/60 backdrop-blur-sm border border-gray-700/50 hover:border-[var(--luxury-gold)]/60 transition-all duration-300 hover:bg-gradient-to-br hover:from-[var(--luxury-gold)]/10 hover:to-[var(--luxury-gold-dark)]/10 hover:shadow-lg hover:shadow-[var(--luxury-gold)]/20 hover:-translate-y-0.5"
|
||||||
aria-label="LinkedIn"
|
aria-label="LinkedIn"
|
||||||
>
|
>
|
||||||
<Linkedin className="w-5 h-5 sm:w-6 sm:h-6 text-gray-400 group-hover:text-[#d4af37] transition-all duration-300 group-hover:scale-110" />
|
<Linkedin className="w-5 h-5 sm:w-6 sm:h-6 text-gray-400 group-hover:text-[var(--luxury-gold)] transition-all duration-300 group-hover:scale-110" />
|
||||||
<div className="absolute inset-0 rounded-lg bg-[#d4af37]/0 group-hover:bg-[#d4af37]/10 blur-xl transition-all duration-500"></div>
|
<div className="absolute inset-0 rounded-lg bg-[var(--luxury-gold)]/0 group-hover:bg-[var(--luxury-gold)]/10 blur-xl transition-all duration-500"></div>
|
||||||
</a>
|
</a>
|
||||||
)}
|
)}
|
||||||
{pageContent?.social_links?.youtube && (
|
{pageContent?.social_links?.youtube && (
|
||||||
@@ -314,11 +314,11 @@ const Footer: React.FC = () => {
|
|||||||
href={pageContent.social_links.youtube}
|
href={pageContent.social_links.youtube}
|
||||||
target="_blank"
|
target="_blank"
|
||||||
rel="noopener noreferrer"
|
rel="noopener noreferrer"
|
||||||
className="group relative w-12 h-12 sm:w-14 sm:h-14 flex items-center justify-center rounded-lg bg-gradient-to-br from-gray-800/60 to-gray-900/60 backdrop-blur-sm border border-gray-700/50 hover:border-[#d4af37]/60 transition-all duration-300 hover:bg-gradient-to-br hover:from-[#d4af37]/10 hover:to-[#c9a227]/10 hover:shadow-lg hover:shadow-[#d4af37]/20 hover:-translate-y-0.5"
|
className="group relative w-12 h-12 sm:w-14 sm:h-14 flex items-center justify-center rounded-lg bg-gradient-to-br from-gray-800/60 to-gray-900/60 backdrop-blur-sm border border-gray-700/50 hover:border-[var(--luxury-gold)]/60 transition-all duration-300 hover:bg-gradient-to-br hover:from-[var(--luxury-gold)]/10 hover:to-[var(--luxury-gold-dark)]/10 hover:shadow-lg hover:shadow-[var(--luxury-gold)]/20 hover:-translate-y-0.5"
|
||||||
aria-label="YouTube"
|
aria-label="YouTube"
|
||||||
>
|
>
|
||||||
<Youtube className="w-5 h-5 sm:w-6 sm:h-6 text-gray-400 group-hover:text-[#d4af37] transition-all duration-300 group-hover:scale-110" />
|
<Youtube className="w-5 h-5 sm:w-6 sm:h-6 text-gray-400 group-hover:text-[var(--luxury-gold)] transition-all duration-300 group-hover:scale-110" />
|
||||||
<div className="absolute inset-0 rounded-lg bg-[#d4af37]/0 group-hover:bg-[#d4af37]/10 blur-xl transition-all duration-500"></div>
|
<div className="absolute inset-0 rounded-lg bg-[var(--luxury-gold)]/0 group-hover:bg-[var(--luxury-gold)]/10 blur-xl transition-all duration-500"></div>
|
||||||
</a>
|
</a>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
@@ -329,16 +329,16 @@ const Footer: React.FC = () => {
|
|||||||
<div className="lg:col-span-2">
|
<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">
|
<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="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>
|
<span className="absolute bottom-0 left-0 w-full h-[2px] bg-gradient-to-r from-[var(--luxury-gold)] via-[var(--luxury-gold)]/50 to-transparent"></span>
|
||||||
</h3>
|
</h3>
|
||||||
<ul className="space-y-3 sm:space-y-4">
|
<ul className="space-y-3 sm:space-y-4">
|
||||||
{quickLinks.map((link) => (
|
{quickLinks.map((link) => (
|
||||||
<li key={link.url}>
|
<li key={link.url}>
|
||||||
<Link
|
<Link
|
||||||
to={link.url}
|
to={link.url}
|
||||||
className="group flex items-center text-sm sm:text-base text-gray-400 hover:text-[#d4af37] transition-all duration-300 relative font-light tracking-wide"
|
className="group flex items-center text-sm sm:text-base text-gray-400 hover:text-[var(--luxury-gold)] transition-all duration-300 relative font-light tracking-wide"
|
||||||
>
|
>
|
||||||
<span className="absolute left-0 w-0 h-[2px] bg-gradient-to-r from-[#d4af37] to-[#c9a227] group-hover:w-8 transition-all duration-300 rounded-full"></span>
|
<span className="absolute left-0 w-0 h-[2px] bg-gradient-to-r from-[var(--luxury-gold)] to-[var(--luxury-gold-dark)] group-hover:w-8 transition-all duration-300 rounded-full"></span>
|
||||||
<span className="ml-10 group-hover:translate-x-2 transition-transform duration-300 group-hover:font-medium">{link.label}</span>
|
<span className="ml-10 group-hover:translate-x-2 transition-transform duration-300 group-hover:font-medium">{link.label}</span>
|
||||||
</Link>
|
</Link>
|
||||||
</li>
|
</li>
|
||||||
@@ -352,16 +352,16 @@ const Footer: React.FC = () => {
|
|||||||
<div className="lg:col-span-2">
|
<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">
|
<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="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>
|
<span className="absolute bottom-0 left-0 w-full h-[2px] bg-gradient-to-r from-[var(--luxury-gold)] via-[var(--luxury-gold)]/50 to-transparent"></span>
|
||||||
</h3>
|
</h3>
|
||||||
<ul className="space-y-3 sm:space-y-4">
|
<ul className="space-y-3 sm:space-y-4">
|
||||||
{supportLinks.map((link) => (
|
{supportLinks.map((link) => (
|
||||||
<li key={link.url}>
|
<li key={link.url}>
|
||||||
<Link
|
<Link
|
||||||
to={link.url}
|
to={link.url}
|
||||||
className="group flex items-center text-sm sm:text-base text-gray-400 hover:text-[#d4af37] transition-all duration-300 relative font-light tracking-wide"
|
className="group flex items-center text-sm sm:text-base text-gray-400 hover:text-[var(--luxury-gold)] transition-all duration-300 relative font-light tracking-wide"
|
||||||
>
|
>
|
||||||
<span className="absolute left-0 w-0 h-[2px] bg-gradient-to-r from-[#d4af37] to-[#c9a227] group-hover:w-8 transition-all duration-300 rounded-full"></span>
|
<span className="absolute left-0 w-0 h-[2px] bg-gradient-to-r from-[var(--luxury-gold)] to-[var(--luxury-gold-dark)] group-hover:w-8 transition-all duration-300 rounded-full"></span>
|
||||||
<span className="ml-10 group-hover:translate-x-2 transition-transform duration-300 group-hover:font-medium">{link.label}</span>
|
<span className="ml-10 group-hover:translate-x-2 transition-transform duration-300 group-hover:font-medium">{link.label}</span>
|
||||||
</Link>
|
</Link>
|
||||||
</li>
|
</li>
|
||||||
@@ -374,7 +374,7 @@ const Footer: React.FC = () => {
|
|||||||
<div className="lg:col-span-2">
|
<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">
|
<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="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>
|
<span className="absolute bottom-0 left-0 w-full h-[2px] bg-gradient-to-r from-[var(--luxury-gold)] via-[var(--luxury-gold)]/50 to-transparent"></span>
|
||||||
</h3>
|
</h3>
|
||||||
{homePageContent?.newsletter_section_subtitle && (
|
{homePageContent?.newsletter_section_subtitle && (
|
||||||
<p className="text-sm text-gray-400 mb-4 font-light leading-relaxed">
|
<p className="text-sm text-gray-400 mb-4 font-light leading-relaxed">
|
||||||
@@ -416,14 +416,14 @@ const Footer: React.FC = () => {
|
|||||||
value={newsletterEmail}
|
value={newsletterEmail}
|
||||||
onChange={(e) => setNewsletterEmail(e.target.value)}
|
onChange={(e) => setNewsletterEmail(e.target.value)}
|
||||||
placeholder={homePageContent?.newsletter_placeholder || 'Enter your email'}
|
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"
|
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-[var(--luxury-gold)] focus:ring-2 focus:ring-[var(--luxury-gold)]/20 transition-all disabled:opacity-50 disabled:cursor-not-allowed text-sm"
|
||||||
required
|
required
|
||||||
disabled={newsletterSubmitting}
|
disabled={newsletterSubmitting}
|
||||||
/>
|
/>
|
||||||
<button
|
<button
|
||||||
type="submit"
|
type="submit"
|
||||||
disabled={newsletterSubmitting || !newsletterEmail}
|
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"
|
className="w-full px-4 py-2.5 bg-gradient-to-r from-[var(--luxury-gold)] to-[var(--luxury-gold-dark)] text-[#0f0f0f] rounded-lg font-semibold hover:from-[var(--luxury-gold-light)] hover:to-[var(--luxury-gold)] transition-all duration-300 shadow-lg shadow-[var(--luxury-gold)]/30 hover:shadow-xl hover:shadow-[var(--luxury-gold)]/40 disabled:opacity-50 disabled:cursor-not-allowed text-sm"
|
||||||
>
|
>
|
||||||
{newsletterSubmitting ? 'Subscribing...' : (homePageContent?.newsletter_button_text || 'Subscribe')}
|
{newsletterSubmitting ? 'Subscribing...' : (homePageContent?.newsletter_button_text || 'Subscribe')}
|
||||||
</button>
|
</button>
|
||||||
@@ -443,17 +443,17 @@ const Footer: React.FC = () => {
|
|||||||
<div className="lg:col-span-2">
|
<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">
|
<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="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>
|
<span className="absolute bottom-0 left-0 w-full h-[2px] bg-gradient-to-r from-[var(--luxury-gold)] via-[var(--luxury-gold)]/50 to-transparent"></span>
|
||||||
</h3>
|
</h3>
|
||||||
{(displayAddress || displayPhone || displayEmail) && (
|
{(displayAddress || displayPhone || displayEmail) && (
|
||||||
<ul className="space-y-5 sm:space-y-6">
|
<ul className="space-y-5 sm:space-y-6">
|
||||||
{displayAddress && (
|
{displayAddress && (
|
||||||
<li className="flex items-start space-x-4 group">
|
<li className="flex items-start space-x-4 group">
|
||||||
<div className="relative mt-1 flex-shrink-0">
|
<div className="relative mt-1 flex-shrink-0">
|
||||||
<div className="p-2 bg-gradient-to-br from-[#d4af37]/10 to-[#c9a227]/5 rounded-lg border border-[#d4af37]/20 group-hover:border-[#d4af37]/40 transition-all duration-300">
|
<div className="p-2 bg-gradient-to-br from-[var(--luxury-gold)]/10 to-[var(--luxury-gold-dark)]/5 rounded-lg border border-[var(--luxury-gold)]/20 group-hover:border-[var(--luxury-gold)]/40 transition-all duration-300">
|
||||||
<MapPin className="w-4 h-4 sm:w-5 sm:h-5 text-[#d4af37] transition-all duration-300 group-hover:scale-110" />
|
<MapPin className="w-4 h-4 sm:w-5 sm:h-5 text-[var(--luxury-gold)] transition-all duration-300 group-hover:scale-110" />
|
||||||
</div>
|
</div>
|
||||||
<div className="absolute inset-0 bg-[#d4af37]/20 blur-xl opacity-0 group-hover:opacity-100 transition-opacity duration-500"></div>
|
<div className="absolute inset-0 bg-[var(--luxury-gold)]/20 blur-xl opacity-0 group-hover:opacity-100 transition-opacity duration-500"></div>
|
||||||
</div>
|
</div>
|
||||||
<span className="text-sm sm:text-base text-gray-400 group-hover:text-gray-300 transition-colors leading-relaxed font-light pt-1">
|
<span className="text-sm sm:text-base text-gray-400 group-hover:text-gray-300 transition-colors leading-relaxed font-light pt-1">
|
||||||
{displayAddress
|
{displayAddress
|
||||||
@@ -469,12 +469,12 @@ const Footer: React.FC = () => {
|
|||||||
{displayPhone && (
|
{displayPhone && (
|
||||||
<li className="flex items-center space-x-4 group">
|
<li className="flex items-center space-x-4 group">
|
||||||
<div className="relative flex-shrink-0">
|
<div className="relative flex-shrink-0">
|
||||||
<div className="p-2 bg-gradient-to-br from-[#d4af37]/10 to-[#c9a227]/5 rounded-lg border border-[#d4af37]/20 group-hover:border-[#d4af37]/40 transition-all duration-300">
|
<div className="p-2 bg-gradient-to-br from-[var(--luxury-gold)]/10 to-[var(--luxury-gold-dark)]/5 rounded-lg border border-[var(--luxury-gold)]/20 group-hover:border-[var(--luxury-gold)]/40 transition-all duration-300">
|
||||||
<Phone className="w-4 h-4 sm:w-5 sm:h-5 text-[#d4af37] transition-all duration-300 group-hover:scale-110" />
|
<Phone className="w-4 h-4 sm:w-5 sm:h-5 text-[var(--luxury-gold)] transition-all duration-300 group-hover:scale-110" />
|
||||||
</div>
|
</div>
|
||||||
<div className="absolute inset-0 bg-[#d4af37]/20 blur-xl opacity-0 group-hover:opacity-100 transition-opacity duration-500"></div>
|
<div className="absolute inset-0 bg-[var(--luxury-gold)]/20 blur-xl opacity-0 group-hover:opacity-100 transition-opacity duration-500"></div>
|
||||||
</div>
|
</div>
|
||||||
<a href={phoneHref} className="text-sm sm:text-base text-gray-400 group-hover:text-[#d4af37] transition-colors font-light tracking-wide">
|
<a href={phoneHref} className="text-sm sm:text-base text-gray-400 group-hover:text-[var(--luxury-gold)] transition-colors font-light tracking-wide">
|
||||||
{displayPhone}
|
{displayPhone}
|
||||||
</a>
|
</a>
|
||||||
</li>
|
</li>
|
||||||
@@ -482,12 +482,12 @@ const Footer: React.FC = () => {
|
|||||||
{displayEmail && (
|
{displayEmail && (
|
||||||
<li className="flex items-center space-x-4 group">
|
<li className="flex items-center space-x-4 group">
|
||||||
<div className="relative flex-shrink-0">
|
<div className="relative flex-shrink-0">
|
||||||
<div className="p-2 bg-gradient-to-br from-[#d4af37]/10 to-[#c9a227]/5 rounded-lg border border-[#d4af37]/20 group-hover:border-[#d4af37]/40 transition-all duration-300">
|
<div className="p-2 bg-gradient-to-br from-[var(--luxury-gold)]/10 to-[var(--luxury-gold-dark)]/5 rounded-lg border border-[var(--luxury-gold)]/20 group-hover:border-[var(--luxury-gold)]/40 transition-all duration-300">
|
||||||
<Mail className="w-4 h-4 sm:w-5 sm:h-5 text-[#d4af37] transition-all duration-300 group-hover:scale-110" />
|
<Mail className="w-4 h-4 sm:w-5 sm:h-5 text-[var(--luxury-gold)] transition-all duration-300 group-hover:scale-110" />
|
||||||
</div>
|
</div>
|
||||||
<div className="absolute inset-0 bg-[#d4af37]/20 blur-xl opacity-0 group-hover:opacity-100 transition-opacity duration-500"></div>
|
<div className="absolute inset-0 bg-[var(--luxury-gold)]/20 blur-xl opacity-0 group-hover:opacity-100 transition-opacity duration-500"></div>
|
||||||
</div>
|
</div>
|
||||||
<a href={`mailto:${displayEmail}`} className="text-sm sm:text-base text-gray-400 group-hover:text-[#d4af37] transition-colors font-light tracking-wide break-all">
|
<a href={`mailto:${displayEmail}`} className="text-sm sm:text-base text-gray-400 group-hover:text-[var(--luxury-gold)] transition-colors font-light tracking-wide break-all">
|
||||||
{displayEmail}
|
{displayEmail}
|
||||||
</a>
|
</a>
|
||||||
</li>
|
</li>
|
||||||
@@ -499,7 +499,7 @@ const Footer: React.FC = () => {
|
|||||||
<div className="mt-8 sm:mt-10 pt-6 sm:pt-8 border-t border-gray-800/50">
|
<div className="mt-8 sm:mt-10 pt-6 sm:pt-8 border-t border-gray-800/50">
|
||||||
<div className="flex items-center space-x-1 mb-3">
|
<div className="flex items-center space-x-1 mb-3">
|
||||||
{[...Array(5)].map((_, i) => (
|
{[...Array(5)].map((_, i) => (
|
||||||
<Star key={i} className="w-4 h-4 sm:w-5 sm:h-5 fill-[#d4af37] text-[#d4af37] drop-shadow-lg" />
|
<Star key={i} className="w-4 h-4 sm:w-5 sm:h-5 fill-[var(--luxury-gold)] text-[var(--luxury-gold)] drop-shadow-lg" />
|
||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
<p className="text-xs sm:text-sm text-gray-500 font-light tracking-wide">Rated 5.0 by 10,000+ guests</p>
|
<p className="text-xs sm:text-sm text-gray-500 font-light tracking-wide">Rated 5.0 by 10,000+ guests</p>
|
||||||
@@ -514,7 +514,7 @@ const Footer: React.FC = () => {
|
|||||||
</div>
|
</div>
|
||||||
<div className="relative flex justify-center">
|
<div className="relative flex justify-center">
|
||||||
<div className="bg-gradient-to-b from-[#0f0f0f] to-[#1a1a1a] px-6">
|
<div className="bg-gradient-to-b from-[#0f0f0f] to-[#1a1a1a] px-6">
|
||||||
<div className="w-24 sm:w-32 h-[2px] bg-gradient-to-r from-transparent via-[#d4af37]/50 to-transparent shadow-lg shadow-[#d4af37]/30"></div>
|
<div className="w-24 sm:w-32 h-[2px] bg-gradient-to-r from-transparent via-[var(--luxury-gold)]/50 to-transparent shadow-lg shadow-[var(--luxury-gold)]/30"></div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -530,11 +530,11 @@ const Footer: React.FC = () => {
|
|||||||
})()}
|
})()}
|
||||||
</div>
|
</div>
|
||||||
<div className="flex items-center space-x-4 sm:space-x-6 text-xs sm:text-sm text-gray-600">
|
<div className="flex items-center space-x-4 sm:space-x-6 text-xs sm:text-sm text-gray-600">
|
||||||
<Link to="/privacy" className="hover:text-[#d4af37] transition-colors cursor-pointer font-light tracking-wide">Privacy</Link>
|
<Link to="/privacy" className="hover:text-[var(--luxury-gold)] transition-colors cursor-pointer font-light tracking-wide">Privacy</Link>
|
||||||
<span className="text-gray-700">•</span>
|
<span className="text-gray-700">•</span>
|
||||||
<Link to="/terms" className="hover:text-[#d4af37] transition-colors cursor-pointer font-light tracking-wide">Terms</Link>
|
<Link to="/terms" className="hover:text-[var(--luxury-gold)] transition-colors cursor-pointer font-light tracking-wide">Terms</Link>
|
||||||
<span className="text-gray-700">•</span>
|
<span className="text-gray-700">•</span>
|
||||||
<Link to="/refunds" className="hover:text-[#d4af37] transition-colors cursor-pointer font-light tracking-wide">Refunds</Link>
|
<Link to="/refunds" className="hover:text-[var(--luxury-gold)] transition-colors cursor-pointer font-light tracking-wide">Refunds</Link>
|
||||||
<span className="text-gray-700">•</span>
|
<span className="text-gray-700">•</span>
|
||||||
<CookiePreferencesLink />
|
<CookiePreferencesLink />
|
||||||
</div>
|
</div>
|
||||||
@@ -542,7 +542,7 @@ const Footer: React.FC = () => {
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Bottom Gold Accent Line */}
|
{/* Bottom Gold Accent Line */}
|
||||||
<div className="absolute bottom-0 left-0 right-0 h-[2px] bg-gradient-to-r from-transparent via-[#d4af37] to-transparent shadow-lg shadow-[#d4af37]/50"></div>
|
<div className="absolute bottom-0 left-0 right-0 h-[2px] bg-gradient-to-r from-transparent via-[var(--luxury-gold)] to-transparent shadow-lg shadow-[var(--luxury-gold)]/50"></div>
|
||||||
|
|
||||||
{/* Chat Widget */}
|
{/* Chat Widget */}
|
||||||
<ChatWidget />
|
<ChatWidget />
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import React, { useState, useRef } from 'react';
|
import React, { useState, useRef, useEffect } from 'react';
|
||||||
import { Link } from 'react-router-dom';
|
import { Link, useLocation } from 'react-router-dom';
|
||||||
import {
|
import {
|
||||||
Hotel,
|
Hotel,
|
||||||
User,
|
User,
|
||||||
@@ -41,6 +41,7 @@ const Header: React.FC<HeaderProps> = ({
|
|||||||
}) => {
|
}) => {
|
||||||
const { settings } = useCompanySettings();
|
const { settings } = useCompanySettings();
|
||||||
const { openModal } = useAuthModal();
|
const { openModal } = useAuthModal();
|
||||||
|
const location = useLocation();
|
||||||
|
|
||||||
|
|
||||||
const displayPhone = settings.company_phone || '+1 (234) 567-890';
|
const displayPhone = settings.company_phone || '+1 (234) 567-890';
|
||||||
@@ -55,6 +56,23 @@ const Header: React.FC<HeaderProps> = ({
|
|||||||
const [isUserMenuOpen, setIsUserMenuOpen] = useState(false);
|
const [isUserMenuOpen, setIsUserMenuOpen] = useState(false);
|
||||||
const userMenuRef = useRef<HTMLDivElement>(null);
|
const userMenuRef = useRef<HTMLDivElement>(null);
|
||||||
|
|
||||||
|
// Track previous pathname to detect actual navigation
|
||||||
|
const prevPathnameRef = useRef(location.pathname);
|
||||||
|
|
||||||
|
// Close menu when navigation happens
|
||||||
|
useEffect(() => {
|
||||||
|
const prevPathname = prevPathnameRef.current;
|
||||||
|
const currentPathname = location.pathname;
|
||||||
|
|
||||||
|
// Only close menu if it's an actual navigation (pathname changed)
|
||||||
|
if (prevPathname !== currentPathname && isMobileMenuOpen) {
|
||||||
|
setIsMobileMenuOpen(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update ref for next comparison
|
||||||
|
prevPathnameRef.current = currentPathname;
|
||||||
|
}, [location.pathname, isMobileMenuOpen]);
|
||||||
|
|
||||||
|
|
||||||
useClickOutside(userMenuRef, () => {
|
useClickOutside(userMenuRef, () => {
|
||||||
if (isUserMenuOpen) {
|
if (isUserMenuOpen) {
|
||||||
@@ -84,32 +102,42 @@ const Header: React.FC<HeaderProps> = ({
|
|||||||
{!isAuthenticated ? (
|
{!isAuthenticated ? (
|
||||||
<>
|
<>
|
||||||
<button
|
<button
|
||||||
onClick={() => {
|
onClick={(e) => {
|
||||||
setIsMobileMenuOpen(false);
|
e.stopPropagation();
|
||||||
|
setTimeout(() => setIsMobileMenuOpen(false), 100);
|
||||||
openModal('login');
|
openModal('login');
|
||||||
}}
|
}}
|
||||||
|
onTouchStart={(e) => {
|
||||||
|
e.stopPropagation();
|
||||||
|
}}
|
||||||
className="flex items-center
|
className="flex items-center
|
||||||
space-x-2 px-4 py-3 text-white/90
|
space-x-2 px-4 py-3.5 text-white/95
|
||||||
hover:bg-[#d4af37]/10 hover:text-[#d4af37]
|
hover:bg-[rgba(var(--luxury-gold-rgb),0.15)] hover:text-[var(--luxury-gold)]
|
||||||
rounded-sm transition-all duration-300
|
rounded-md transition-all duration-300
|
||||||
border-l-2 border-transparent
|
border-l-2 border-transparent
|
||||||
hover:border-[#d4af37] font-light tracking-wide w-full text-left"
|
hover:border-[var(--luxury-gold)] font-light tracking-wider text-sm w-full text-left group relative mx-2 cursor-pointer"
|
||||||
|
style={{ touchAction: 'manipulation' }}
|
||||||
>
|
>
|
||||||
<LogIn className="w-4 h-4" />
|
<LogIn className="w-4 h-4" />
|
||||||
<span>Login</span>
|
<span>Login</span>
|
||||||
</button>
|
</button>
|
||||||
<button
|
<button
|
||||||
onClick={() => {
|
onClick={(e) => {
|
||||||
setIsMobileMenuOpen(false);
|
e.stopPropagation();
|
||||||
|
setTimeout(() => setIsMobileMenuOpen(false), 100);
|
||||||
openModal('register');
|
openModal('register');
|
||||||
}}
|
}}
|
||||||
|
onTouchStart={(e) => {
|
||||||
|
e.stopPropagation();
|
||||||
|
}}
|
||||||
className="flex items-center
|
className="flex items-center
|
||||||
space-x-2 px-4 py-3 bg-gradient-to-r
|
space-x-2 px-4 py-3.5 bg-gradient-to-r
|
||||||
from-[#d4af37] to-[#c9a227] text-[#0f0f0f]
|
from-[var(--luxury-gold)] to-[var(--luxury-gold-dark)] text-[#0f0f0f]
|
||||||
rounded-sm hover:from-[#f5d76e]
|
rounded-md hover:from-[var(--luxury-gold-light)]
|
||||||
hover:to-[#d4af37] transition-all
|
hover:to-[var(--luxury-gold)] transition-all
|
||||||
duration-300 font-medium tracking-wide
|
duration-300 font-medium tracking-wider text-sm
|
||||||
mt-2 w-full text-left"
|
mt-2 w-full text-left mx-2 cursor-pointer"
|
||||||
|
style={{ touchAction: 'manipulation' }}
|
||||||
>
|
>
|
||||||
<UserPlus className="w-4 h-4" />
|
<UserPlus className="w-4 h-4" />
|
||||||
<span>Register</span>
|
<span>Register</span>
|
||||||
@@ -118,21 +146,26 @@ const Header: React.FC<HeaderProps> = ({
|
|||||||
) : (
|
) : (
|
||||||
<>
|
<>
|
||||||
<div className="px-4 py-2 text-sm
|
<div className="px-4 py-2 text-sm
|
||||||
text-[#d4af37]/70 font-light tracking-wide"
|
text-text-[rgba(var(--luxury-gold-rgb),0.7)] font-light tracking-wide"
|
||||||
>
|
>
|
||||||
Hello, {userInfo?.name}
|
Hello, {userInfo?.name}
|
||||||
</div>
|
</div>
|
||||||
<Link
|
<Link
|
||||||
to="/profile"
|
to="/profile"
|
||||||
onClick={() =>
|
onClick={(e) => {
|
||||||
setIsMobileMenuOpen(false)
|
e.stopPropagation();
|
||||||
}
|
setTimeout(() => setIsMobileMenuOpen(false), 100);
|
||||||
|
}}
|
||||||
|
onTouchStart={(e) => {
|
||||||
|
e.stopPropagation();
|
||||||
|
}}
|
||||||
className="flex items-center
|
className="flex items-center
|
||||||
space-x-2 px-4 py-3 text-white/90
|
space-x-2 px-4 py-3.5 text-white/95
|
||||||
hover:bg-[#d4af37]/10 hover:text-[#d4af37]
|
hover:bg-[rgba(var(--luxury-gold-rgb),0.15)] hover:text-[var(--luxury-gold)]
|
||||||
rounded-sm transition-all duration-300
|
rounded-md transition-all duration-300
|
||||||
border-l-2 border-transparent
|
border-l-2 border-transparent
|
||||||
hover:border-[#d4af37] font-light tracking-wide"
|
hover:border-[var(--luxury-gold)] font-light tracking-wider text-sm group relative mx-2"
|
||||||
|
style={{ touchAction: 'manipulation' }}
|
||||||
>
|
>
|
||||||
<User className="w-4 h-4" />
|
<User className="w-4 h-4" />
|
||||||
<span>Profile</span>
|
<span>Profile</span>
|
||||||
@@ -141,120 +174,160 @@ const Header: React.FC<HeaderProps> = ({
|
|||||||
<>
|
<>
|
||||||
<Link
|
<Link
|
||||||
to="/dashboard"
|
to="/dashboard"
|
||||||
onClick={() =>
|
onClick={(e) => {
|
||||||
setIsMobileMenuOpen(false)
|
e.stopPropagation();
|
||||||
}
|
setTimeout(() => setIsMobileMenuOpen(false), 100);
|
||||||
|
}}
|
||||||
|
onTouchStart={(e) => {
|
||||||
|
e.stopPropagation();
|
||||||
|
}}
|
||||||
className="flex items-center
|
className="flex items-center
|
||||||
space-x-2 px-4 py-3 text-white/90
|
space-x-2 px-4 py-3 text-white/90
|
||||||
hover:bg-[#d4af37]/10 hover:text-[#d4af37]
|
hover:bg-[rgba(var(--luxury-gold-rgb),0.1)] hover:text-[var(--luxury-gold)]
|
||||||
rounded-sm transition-all duration-300
|
rounded-sm transition-all duration-300
|
||||||
border-l-2 border-transparent
|
border-l-2 border-transparent
|
||||||
hover:border-[#d4af37] font-light tracking-wide"
|
hover:border-[var(--luxury-gold)] font-light tracking-wide"
|
||||||
|
style={{ touchAction: 'manipulation' }}
|
||||||
>
|
>
|
||||||
<Calendar className="w-4 h-4" />
|
<Calendar className="w-4 h-4" />
|
||||||
<span>Dashboard</span>
|
<span>Dashboard</span>
|
||||||
</Link>
|
</Link>
|
||||||
<Link
|
<Link
|
||||||
to="/favorites"
|
to="/favorites"
|
||||||
onClick={() =>
|
onClick={(e) => {
|
||||||
setIsMobileMenuOpen(false)
|
e.stopPropagation();
|
||||||
}
|
setTimeout(() => setIsMobileMenuOpen(false), 100);
|
||||||
|
}}
|
||||||
|
onTouchStart={(e) => {
|
||||||
|
e.stopPropagation();
|
||||||
|
}}
|
||||||
className="flex items-center
|
className="flex items-center
|
||||||
space-x-2 px-4 py-3 text-white/90
|
space-x-2 px-4 py-3 text-white/90
|
||||||
hover:bg-[#d4af37]/10 hover:text-[#d4af37]
|
hover:bg-[rgba(var(--luxury-gold-rgb),0.1)] hover:text-[var(--luxury-gold)]
|
||||||
rounded-sm transition-all duration-300
|
rounded-sm transition-all duration-300
|
||||||
border-l-2 border-transparent
|
border-l-2 border-transparent
|
||||||
hover:border-[#d4af37] font-light tracking-wide"
|
hover:border-[var(--luxury-gold)] font-light tracking-wide"
|
||||||
|
style={{ touchAction: 'manipulation' }}
|
||||||
>
|
>
|
||||||
<Heart className="w-4 h-4" />
|
<Heart className="w-4 h-4" />
|
||||||
<span>Favorites</span>
|
<span>Favorites</span>
|
||||||
</Link>
|
</Link>
|
||||||
<Link
|
<Link
|
||||||
to="/bookings"
|
to="/bookings"
|
||||||
onClick={() =>
|
onClick={(e) => {
|
||||||
setIsMobileMenuOpen(false)
|
e.stopPropagation();
|
||||||
}
|
setTimeout(() => setIsMobileMenuOpen(false), 100);
|
||||||
|
}}
|
||||||
|
onTouchStart={(e) => {
|
||||||
|
e.stopPropagation();
|
||||||
|
}}
|
||||||
className="flex items-center
|
className="flex items-center
|
||||||
space-x-2 px-4 py-3 text-white/90
|
space-x-2 px-4 py-3 text-white/90
|
||||||
hover:bg-[#d4af37]/10 hover:text-[#d4af37]
|
hover:bg-[rgba(var(--luxury-gold-rgb),0.1)] hover:text-[var(--luxury-gold)]
|
||||||
rounded-sm transition-all duration-300
|
rounded-sm transition-all duration-300
|
||||||
border-l-2 border-transparent
|
border-l-2 border-transparent
|
||||||
hover:border-[#d4af37] font-light tracking-wide"
|
hover:border-[var(--luxury-gold)] font-light tracking-wide"
|
||||||
|
style={{ touchAction: 'manipulation' }}
|
||||||
>
|
>
|
||||||
<Calendar className="w-4 h-4" />
|
<Calendar className="w-4 h-4" />
|
||||||
<span>My Bookings</span>
|
<span>My Bookings</span>
|
||||||
</Link>
|
</Link>
|
||||||
<Link
|
<Link
|
||||||
to="/loyalty"
|
to="/loyalty"
|
||||||
onClick={() =>
|
onClick={(e) => {
|
||||||
setIsMobileMenuOpen(false)
|
e.stopPropagation();
|
||||||
}
|
setTimeout(() => setIsMobileMenuOpen(false), 100);
|
||||||
|
}}
|
||||||
|
onTouchStart={(e) => {
|
||||||
|
e.stopPropagation();
|
||||||
|
}}
|
||||||
className="flex items-center
|
className="flex items-center
|
||||||
space-x-2 px-4 py-3 text-white/90
|
space-x-2 px-4 py-3 text-white/90
|
||||||
hover:bg-[#d4af37]/10 hover:text-[#d4af37]
|
hover:bg-[rgba(var(--luxury-gold-rgb),0.1)] hover:text-[var(--luxury-gold)]
|
||||||
rounded-sm transition-all duration-300
|
rounded-sm transition-all duration-300
|
||||||
border-l-2 border-transparent
|
border-l-2 border-transparent
|
||||||
hover:border-[#d4af37] font-light tracking-wide"
|
hover:border-[var(--luxury-gold)] font-light tracking-wide"
|
||||||
|
style={{ touchAction: 'manipulation' }}
|
||||||
>
|
>
|
||||||
<Star className="w-4 h-4" />
|
<Star className="w-4 h-4" />
|
||||||
<span>Loyalty Program</span>
|
<span>Loyalty Program</span>
|
||||||
</Link>
|
</Link>
|
||||||
<Link
|
<Link
|
||||||
to="/group-bookings"
|
to="/group-bookings"
|
||||||
onClick={() =>
|
onClick={(e) => {
|
||||||
setIsMobileMenuOpen(false)
|
e.stopPropagation();
|
||||||
}
|
setTimeout(() => setIsMobileMenuOpen(false), 100);
|
||||||
|
}}
|
||||||
|
onTouchStart={(e) => {
|
||||||
|
e.stopPropagation();
|
||||||
|
}}
|
||||||
className="flex items-center
|
className="flex items-center
|
||||||
space-x-2 px-4 py-3 text-white/90
|
space-x-2 px-4 py-3 text-white/90
|
||||||
hover:bg-[#d4af37]/10 hover:text-[#d4af37]
|
hover:bg-[rgba(var(--luxury-gold-rgb),0.1)] hover:text-[var(--luxury-gold)]
|
||||||
rounded-sm transition-all duration-300
|
rounded-sm transition-all duration-300
|
||||||
border-l-2 border-transparent
|
border-l-2 border-transparent
|
||||||
hover:border-[#d4af37] font-light tracking-wide"
|
hover:border-[var(--luxury-gold)] font-light tracking-wide"
|
||||||
|
style={{ touchAction: 'manipulation' }}
|
||||||
>
|
>
|
||||||
<Users className="w-4 h-4" />
|
<Users className="w-4 h-4" />
|
||||||
<span>Group Bookings</span>
|
<span>Group Bookings</span>
|
||||||
</Link>
|
</Link>
|
||||||
<Link
|
<Link
|
||||||
to="/complaints"
|
to="/complaints"
|
||||||
onClick={() =>
|
onClick={(e) => {
|
||||||
setIsMobileMenuOpen(false)
|
e.stopPropagation();
|
||||||
}
|
setTimeout(() => setIsMobileMenuOpen(false), 100);
|
||||||
|
}}
|
||||||
|
onTouchStart={(e) => {
|
||||||
|
e.stopPropagation();
|
||||||
|
}}
|
||||||
className="flex items-center
|
className="flex items-center
|
||||||
space-x-2 px-4 py-3 text-white/90
|
space-x-2 px-4 py-3 text-white/90
|
||||||
hover:bg-[#d4af37]/10 hover:text-[#d4af37]
|
hover:bg-[rgba(var(--luxury-gold-rgb),0.1)] hover:text-[var(--luxury-gold)]
|
||||||
rounded-sm transition-all duration-300
|
rounded-sm transition-all duration-300
|
||||||
border-l-2 border-transparent
|
border-l-2 border-transparent
|
||||||
hover:border-[#d4af37] font-light tracking-wide"
|
hover:border-[var(--luxury-gold)] font-light tracking-wide"
|
||||||
|
style={{ touchAction: 'manipulation' }}
|
||||||
>
|
>
|
||||||
<AlertCircle className="w-4 h-4" />
|
<AlertCircle className="w-4 h-4" />
|
||||||
<span>Complaints</span>
|
<span>Complaints</span>
|
||||||
</Link>
|
</Link>
|
||||||
<Link
|
<Link
|
||||||
to="/guest-requests"
|
to="/guest-requests"
|
||||||
onClick={() =>
|
onClick={(e) => {
|
||||||
setIsMobileMenuOpen(false)
|
e.stopPropagation();
|
||||||
}
|
setTimeout(() => setIsMobileMenuOpen(false), 100);
|
||||||
|
}}
|
||||||
|
onTouchStart={(e) => {
|
||||||
|
e.stopPropagation();
|
||||||
|
}}
|
||||||
className="flex items-center
|
className="flex items-center
|
||||||
space-x-2 px-4 py-3 text-white/90
|
space-x-2 px-4 py-3 text-white/90
|
||||||
hover:bg-[#d4af37]/10 hover:text-[#d4af37]
|
hover:bg-[rgba(var(--luxury-gold-rgb),0.1)] hover:text-[var(--luxury-gold)]
|
||||||
rounded-sm transition-all duration-300
|
rounded-sm transition-all duration-300
|
||||||
border-l-2 border-transparent
|
border-l-2 border-transparent
|
||||||
hover:border-[#d4af37] font-light tracking-wide"
|
hover:border-[var(--luxury-gold)] font-light tracking-wide"
|
||||||
|
style={{ touchAction: 'manipulation' }}
|
||||||
>
|
>
|
||||||
<Bell className="w-4 h-4" />
|
<Bell className="w-4 h-4" />
|
||||||
<span>Guest Requests</span>
|
<span>Guest Requests</span>
|
||||||
</Link>
|
</Link>
|
||||||
<Link
|
<Link
|
||||||
to="/gdpr"
|
to="/gdpr"
|
||||||
onClick={() =>
|
onClick={(e) => {
|
||||||
setIsMobileMenuOpen(false)
|
e.stopPropagation();
|
||||||
}
|
setTimeout(() => setIsMobileMenuOpen(false), 100);
|
||||||
|
}}
|
||||||
|
onTouchStart={(e) => {
|
||||||
|
e.stopPropagation();
|
||||||
|
}}
|
||||||
className="flex items-center
|
className="flex items-center
|
||||||
space-x-2 px-4 py-3 text-white/90
|
space-x-2 px-4 py-3 text-white/90
|
||||||
hover:bg-[#d4af37]/10 hover:text-[#d4af37]
|
hover:bg-[rgba(var(--luxury-gold-rgb),0.1)] hover:text-[var(--luxury-gold)]
|
||||||
rounded-sm transition-all duration-300
|
rounded-sm transition-all duration-300
|
||||||
border-l-2 border-transparent
|
border-l-2 border-transparent
|
||||||
hover:border-[#d4af37] font-light tracking-wide"
|
hover:border-[var(--luxury-gold)] font-light tracking-wide"
|
||||||
|
style={{ touchAction: 'manipulation' }}
|
||||||
>
|
>
|
||||||
<Shield className="w-4 h-4" />
|
<Shield className="w-4 h-4" />
|
||||||
<span>Privacy & Data</span>
|
<span>Privacy & Data</span>
|
||||||
@@ -264,15 +337,20 @@ const Header: React.FC<HeaderProps> = ({
|
|||||||
{userInfo?.role === 'admin' && (
|
{userInfo?.role === 'admin' && (
|
||||||
<Link
|
<Link
|
||||||
to="/admin"
|
to="/admin"
|
||||||
onClick={() =>
|
onClick={(e) => {
|
||||||
setIsMobileMenuOpen(false)
|
e.stopPropagation();
|
||||||
}
|
setTimeout(() => setIsMobileMenuOpen(false), 100);
|
||||||
|
}}
|
||||||
|
onTouchStart={(e) => {
|
||||||
|
e.stopPropagation();
|
||||||
|
}}
|
||||||
className="flex items-center
|
className="flex items-center
|
||||||
space-x-2 px-4 py-3 text-white/90
|
space-x-2 px-4 py-3 text-white/90
|
||||||
hover:bg-[#d4af37]/10 hover:text-[#d4af37]
|
hover:bg-[rgba(var(--luxury-gold-rgb),0.1)] hover:text-[var(--luxury-gold)]
|
||||||
rounded-sm transition-all duration-300
|
rounded-sm transition-all duration-300
|
||||||
border-l-2 border-transparent
|
border-l-2 border-transparent
|
||||||
hover:border-[#d4af37] font-light tracking-wide"
|
hover:border-[var(--luxury-gold)] font-light tracking-wide"
|
||||||
|
style={{ touchAction: 'manipulation' }}
|
||||||
>
|
>
|
||||||
<User className="w-4 h-4" />
|
<User className="w-4 h-4" />
|
||||||
<span>Admin</span>
|
<span>Admin</span>
|
||||||
@@ -281,15 +359,20 @@ const Header: React.FC<HeaderProps> = ({
|
|||||||
{userInfo?.role === 'staff' && (
|
{userInfo?.role === 'staff' && (
|
||||||
<Link
|
<Link
|
||||||
to="/staff"
|
to="/staff"
|
||||||
onClick={() =>
|
onClick={(e) => {
|
||||||
setIsMobileMenuOpen(false)
|
e.stopPropagation();
|
||||||
}
|
setTimeout(() => setIsMobileMenuOpen(false), 100);
|
||||||
|
}}
|
||||||
|
onTouchStart={(e) => {
|
||||||
|
e.stopPropagation();
|
||||||
|
}}
|
||||||
className="flex items-center
|
className="flex items-center
|
||||||
space-x-2 px-4 py-3 text-white/90
|
space-x-2 px-4 py-3 text-white/90
|
||||||
hover:bg-[#d4af37]/10 hover:text-[#d4af37]
|
hover:bg-[rgba(var(--luxury-gold-rgb),0.1)] hover:text-[var(--luxury-gold)]
|
||||||
rounded-sm transition-all duration-300
|
rounded-sm transition-all duration-300
|
||||||
border-l-2 border-transparent
|
border-l-2 border-transparent
|
||||||
hover:border-[#d4af37] font-light tracking-wide"
|
hover:border-[var(--luxury-gold)] font-light tracking-wide"
|
||||||
|
style={{ touchAction: 'manipulation' }}
|
||||||
>
|
>
|
||||||
<User className="w-4 h-4" />
|
<User className="w-4 h-4" />
|
||||||
<span>Staff Dashboard</span>
|
<span>Staff Dashboard</span>
|
||||||
@@ -298,15 +381,20 @@ const Header: React.FC<HeaderProps> = ({
|
|||||||
{userInfo?.role === 'accountant' && (
|
{userInfo?.role === 'accountant' && (
|
||||||
<Link
|
<Link
|
||||||
to="/accountant"
|
to="/accountant"
|
||||||
onClick={() =>
|
onClick={(e) => {
|
||||||
setIsMobileMenuOpen(false)
|
e.stopPropagation();
|
||||||
}
|
setTimeout(() => setIsMobileMenuOpen(false), 100);
|
||||||
|
}}
|
||||||
|
onTouchStart={(e) => {
|
||||||
|
e.stopPropagation();
|
||||||
|
}}
|
||||||
className="flex items-center
|
className="flex items-center
|
||||||
space-x-2 px-4 py-3 text-white/90
|
space-x-2 px-4 py-3 text-white/90
|
||||||
hover:bg-[#d4af37]/10 hover:text-[#d4af37]
|
hover:bg-[rgba(var(--luxury-gold-rgb),0.1)] hover:text-[var(--luxury-gold)]
|
||||||
rounded-sm transition-all duration-300
|
rounded-sm transition-all duration-300
|
||||||
border-l-2 border-transparent
|
border-l-2 border-transparent
|
||||||
hover:border-[#d4af37] font-light tracking-wide"
|
hover:border-[var(--luxury-gold)] font-light tracking-wide"
|
||||||
|
style={{ touchAction: 'manipulation' }}
|
||||||
>
|
>
|
||||||
<User className="w-4 h-4" />
|
<User className="w-4 h-4" />
|
||||||
<span>Accountant Dashboard</span>
|
<span>Accountant Dashboard</span>
|
||||||
@@ -315,30 +403,38 @@ const Header: React.FC<HeaderProps> = ({
|
|||||||
{userInfo?.role === 'housekeeping' && (
|
{userInfo?.role === 'housekeeping' && (
|
||||||
<Link
|
<Link
|
||||||
to="/housekeeping"
|
to="/housekeeping"
|
||||||
onClick={() =>
|
onClick={(e) => {
|
||||||
setIsMobileMenuOpen(false)
|
e.stopPropagation();
|
||||||
}
|
setTimeout(() => setIsMobileMenuOpen(false), 100);
|
||||||
|
}}
|
||||||
|
onTouchStart={(e) => {
|
||||||
|
e.stopPropagation();
|
||||||
|
}}
|
||||||
className="flex items-center
|
className="flex items-center
|
||||||
space-x-2 px-4 py-3 text-white/90
|
space-x-2 px-4 py-3 text-white/90
|
||||||
hover:bg-[#d4af37]/10 hover:text-[#d4af37]
|
hover:bg-[rgba(var(--luxury-gold-rgb),0.1)] hover:text-[var(--luxury-gold)]
|
||||||
rounded-sm transition-all duration-300
|
rounded-sm transition-all duration-300
|
||||||
border-l-2 border-transparent
|
border-l-2 border-transparent
|
||||||
hover:border-[#d4af37] font-light tracking-wide"
|
hover:border-[var(--luxury-gold)] font-light tracking-wide"
|
||||||
|
style={{ touchAction: 'manipulation' }}
|
||||||
>
|
>
|
||||||
<User className="w-4 h-4" />
|
<User className="w-4 h-4" />
|
||||||
<span>Housekeeping Dashboard</span>
|
<span>Housekeeping Dashboard</span>
|
||||||
</Link>
|
</Link>
|
||||||
)}
|
)}
|
||||||
<div className="border-t border-[#d4af37]/20 my-2"></div>
|
<div className="border-t border-[rgba(var(--luxury-gold-rgb),0.2)] my-2"></div>
|
||||||
<button
|
<button
|
||||||
onClick={handleLogout}
|
onClick={(e) => {
|
||||||
|
e.stopPropagation();
|
||||||
|
handleLogout();
|
||||||
|
}}
|
||||||
className="w-full flex items-center
|
className="w-full flex items-center
|
||||||
space-x-2 px-4 py-3 text-red-400/90
|
space-x-2 px-4 py-3 text-red-400/90
|
||||||
hover:bg-red-500/10 hover:text-red-400
|
hover:bg-red-500/10 hover:text-red-400
|
||||||
rounded-sm transition-all duration-300
|
rounded-sm transition-all duration-300
|
||||||
border-l-2 border-transparent
|
border-l-2 border-transparent
|
||||||
hover:border-red-500/50 text-left
|
hover:border-red-500/50 text-left
|
||||||
font-light tracking-wide"
|
font-light tracking-wide cursor-pointer"
|
||||||
>
|
>
|
||||||
<LogOut className="w-4 h-4" />
|
<LogOut className="w-4 h-4" />
|
||||||
<span>Logout</span>
|
<span>Logout</span>
|
||||||
@@ -349,18 +445,20 @@ const Header: React.FC<HeaderProps> = ({
|
|||||||
);
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<header className="bg-gradient-to-b from-[#1a1a1a] to-[#0f0f0f] sticky top-0 z-50 border-b border-[#d4af37]/20 shadow-2xl">
|
<header
|
||||||
<div className="hidden lg:block bg-[#0a0a0a]/50 border-b border-[#d4af37]/10">
|
className="bg-gradient-to-b from-[#0f0f0f] via-[#1a1a1a] to-[#0f0f0f] sticky top-0 z-50 border-b border-[rgba(var(--luxury-gold-rgb),0.15)] shadow-[0_8px_32px_rgba(0,0,0,0.4)] backdrop-blur-sm"
|
||||||
|
>
|
||||||
|
<div className="hidden lg:block bg-[#0a0a0a]/50 border-b border-[rgba(var(--luxury-gold-rgb),0.1)]">
|
||||||
<div className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 py-2">
|
<div className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 py-2">
|
||||||
<div className="flex items-center justify-end space-x-6 text-sm">
|
<div className="flex items-center justify-end space-x-6 text-sm">
|
||||||
{displayPhone && (
|
{displayPhone && (
|
||||||
<a href={`tel:${displayPhone.replace(/\s+/g, '').replace(/[()]/g, '')}`} className="flex items-center space-x-2 text-[#d4af37] hover:text-[#f5d76e] transition-colors duration-300 font-light">
|
<a href={`tel:${displayPhone.replace(/\s+/g, '').replace(/[()]/g, '')}`} className="flex items-center space-x-2 text-[var(--luxury-gold)] hover:text-[var(--luxury-gold-light)] transition-colors duration-300 font-light">
|
||||||
<Phone className="w-3.5 h-3.5" />
|
<Phone className="w-3.5 h-3.5" />
|
||||||
<span className="tracking-wide">{displayPhone}</span>
|
<span className="tracking-wide">{displayPhone}</span>
|
||||||
</a>
|
</a>
|
||||||
)}
|
)}
|
||||||
{displayEmail && (
|
{displayEmail && (
|
||||||
<a href={`mailto:${displayEmail}`} className="flex items-center space-x-2 text-[#d4af37] hover:text-[#f5d76e] transition-colors duration-300 font-light">
|
<a href={`mailto:${displayEmail}`} className="flex items-center space-x-2 text-[var(--luxury-gold)] hover:text-[var(--luxury-gold-light)] transition-colors duration-300 font-light">
|
||||||
<Mail className="w-3.5 h-3.5" />
|
<Mail className="w-3.5 h-3.5" />
|
||||||
<span className="tracking-wide">{displayEmail}</span>
|
<span className="tracking-wide">{displayEmail}</span>
|
||||||
</a>
|
</a>
|
||||||
@@ -369,96 +467,120 @@ const Header: React.FC<HeaderProps> = ({
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 py-4">
|
<div className="w-full py-3 sm:py-4 md:py-5">
|
||||||
<div className="flex items-center justify-between">
|
<div className="flex items-center justify-between relative w-full">
|
||||||
|
{/* Logo - Left - Always visible on all screens, flush with left edge */}
|
||||||
|
<div className="flex items-center flex-shrink-0 pl-2 sm:pl-4 md:pl-6 lg:pl-8 max-w-[50%] sm:max-w-none">
|
||||||
<Link
|
<Link
|
||||||
to="/"
|
to="/"
|
||||||
className="flex items-center space-x-3
|
className="flex items-center space-x-1.5 sm:space-x-2 md:space-x-3
|
||||||
group transition-all duration-300 hover:opacity-90"
|
group transition-all duration-300 hover:opacity-90 z-10"
|
||||||
>
|
>
|
||||||
{logoUrl ? (
|
{logoUrl ? (
|
||||||
<div className="relative">
|
<div className="relative flex-shrink-0">
|
||||||
|
<div className="absolute inset-0 bg-gradient-to-br from-[var(--luxury-gold)]/20 to-transparent rounded-lg blur-xl opacity-0 group-hover:opacity-100 transition-opacity duration-500"></div>
|
||||||
<img
|
<img
|
||||||
src={logoUrl}
|
src={logoUrl}
|
||||||
alt={settings.company_name}
|
alt={settings.company_name}
|
||||||
className="h-10 w-auto object-contain drop-shadow-lg"
|
className="h-8 w-8 sm:h-10 sm:w-10 md:h-12 md:w-auto object-contain drop-shadow-[0_4px_12px_rgba(var(--luxury-gold-rgb),0.3)] relative z-10 transition-transform duration-300 group-hover:scale-105"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
) : (
|
) : (
|
||||||
<div className="relative">
|
<div className="relative flex-shrink-0">
|
||||||
<div className="absolute inset-0 bg-gradient-to-br from-[#d4af37] to-[#f5d76e] rounded-full blur-md opacity-30 group-hover:opacity-50 transition-opacity duration-300"></div>
|
<div className="absolute inset-0 bg-gradient-to-br from-[var(--luxury-gold)] to-[var(--luxury-gold-light)] rounded-full blur-xl opacity-30 group-hover:opacity-50 transition-opacity duration-500"></div>
|
||||||
<Hotel className="relative w-10 h-10 text-[#d4af37] drop-shadow-lg" />
|
<Hotel className="relative w-8 h-8 sm:w-10 sm:h-10 md:w-12 md:h-12 text-[var(--luxury-gold)] drop-shadow-[0_4px_12px_rgba(var(--luxury-gold-rgb),0.4)] transition-transform duration-300 group-hover:scale-110" />
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
<div className="flex flex-col">
|
<div className="flex flex-col min-w-0">
|
||||||
<span className="text-2xl font-display font-semibold text-white tracking-tight leading-tight">
|
<span className="text-sm sm:text-base md:text-lg lg:text-xl xl:text-2xl font-display font-semibold text-white tracking-tight leading-tight bg-gradient-to-r from-white to-white/90 bg-clip-text truncate">
|
||||||
{settings.company_name}
|
{settings.company_name}
|
||||||
</span>
|
</span>
|
||||||
<span className="text-[10px] text-[#d4af37] tracking-[0.2em] uppercase font-light">
|
<span className="text-[8px] sm:text-[9px] md:text-[10px] text-[var(--luxury-gold)] tracking-[0.25em] uppercase font-light hidden sm:block">
|
||||||
{settings.company_tagline || 'Excellence Redefined'}
|
{settings.company_tagline || 'Excellence Redefined'}
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
</Link>
|
</Link>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Navigation Links - Center */}
|
||||||
|
<div className="absolute left-1/2 transform -translate-x-1/2 hidden md:block z-20">
|
||||||
<Navbar
|
<Navbar
|
||||||
isMobileMenuOpen={isMobileMenuOpen}
|
isMobileMenuOpen={isMobileMenuOpen}
|
||||||
onMobileMenuToggle={toggleMobileMenu}
|
onMobileMenuToggle={toggleMobileMenu}
|
||||||
onLinkClick={() => setIsMobileMenuOpen(false)}
|
onLinkClick={() => setIsMobileMenuOpen(false)}
|
||||||
mobileMenuContent={mobileMenuContent}
|
mobileMenuContent={mobileMenuContent}
|
||||||
/>
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Mobile Menu Button - Right (for smaller screens) */}
|
||||||
|
<div className="flex items-center justify-end md:hidden z-20 pr-2 sm:pr-4">
|
||||||
|
<Navbar
|
||||||
|
isMobileMenuOpen={isMobileMenuOpen}
|
||||||
|
onMobileMenuToggle={toggleMobileMenu}
|
||||||
|
onLinkClick={() => setIsMobileMenuOpen(false)}
|
||||||
|
mobileMenuContent={mobileMenuContent}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Login/Register - Right */}
|
||||||
<div className="hidden md:flex items-center
|
<div className="hidden md:flex items-center
|
||||||
space-x-3"
|
justify-end space-x-4 z-10 flex-shrink-0 pr-4 sm:pr-6 lg:pr-8"
|
||||||
>
|
>
|
||||||
{!isAuthenticated ? (
|
{!isAuthenticated ? (
|
||||||
<>
|
<>
|
||||||
<button
|
<button
|
||||||
onClick={() => openModal('login')}
|
onClick={() => openModal('login')}
|
||||||
className="flex items-center space-x-2
|
className="flex items-center space-x-2
|
||||||
px-5 py-2 text-white/90
|
px-6 py-2.5 text-white/95
|
||||||
hover:text-[#d4af37] transition-all duration-300
|
hover:text-[var(--luxury-gold)] transition-all duration-300
|
||||||
font-light tracking-wide relative group"
|
font-light tracking-wider relative group overflow-hidden"
|
||||||
>
|
>
|
||||||
<LogIn className="w-4 h-4 relative z-10" />
|
<span className="absolute inset-0 border border-[rgba(var(--luxury-gold-rgb),0.2)] rounded-md opacity-0 group-hover:opacity-100 transition-all duration-300 group-hover:border-[rgba(var(--luxury-gold-rgb),0.5)]"></span>
|
||||||
<span className="relative z-10">Login</span>
|
<span className="absolute inset-0 bg-gradient-to-r from-[rgba(var(--luxury-gold-rgb),0.05)] to-transparent opacity-0 group-hover:opacity-100 transition-opacity duration-300"></span>
|
||||||
<span className="absolute inset-0 border border-[#d4af37]/30 rounded-sm opacity-0 group-hover:opacity-100 transition-opacity duration-300"></span>
|
<LogIn className="w-4 h-4 relative z-10 transition-transform duration-300 group-hover:scale-110" />
|
||||||
|
<span className="relative z-10 text-sm">Login</span>
|
||||||
</button>
|
</button>
|
||||||
<button
|
<button
|
||||||
onClick={() => openModal('register')}
|
onClick={() => openModal('register')}
|
||||||
className="flex items-center space-x-2
|
className="flex items-center space-x-2
|
||||||
px-6 py-2.5 bg-gradient-to-r from-[#d4af37] to-[#c9a227]
|
px-7 py-2.5 bg-gradient-to-r from-[var(--luxury-gold)] via-[var(--luxury-gold-light)] to-[var(--luxury-gold)]
|
||||||
text-[#0f0f0f] rounded-sm hover:from-[#f5d76e]
|
text-[#0f0f0f] rounded-md hover:from-[var(--luxury-gold-light)]
|
||||||
hover:to-[#d4af37] transition-all duration-300
|
hover:via-[var(--luxury-gold)] hover:to-[var(--luxury-gold-dark)] transition-all duration-500
|
||||||
font-medium tracking-wide shadow-lg shadow-[#d4af37]/20
|
font-medium tracking-wider shadow-[0_4px_20px_rgba(var(--luxury-gold-rgb),0.3)]
|
||||||
hover:shadow-[#d4af37]/30 relative overflow-hidden group"
|
hover:shadow-[0_6px_30px_rgba(var(--luxury-gold-rgb),0.5)] relative overflow-hidden group
|
||||||
|
transform hover:scale-105 transition-transform duration-300"
|
||||||
>
|
>
|
||||||
<span className="absolute inset-0 bg-gradient-to-r from-white/0 via-white/20 to-white/0 translate-x-[-100%] group-hover:translate-x-[100%] transition-transform duration-700"></span>
|
<span className="absolute inset-0 bg-gradient-to-r from-white/0 via-white/30 to-white/0 translate-x-[-100%] group-hover:translate-x-[100%] transition-transform duration-1000"></span>
|
||||||
<UserPlus className="w-4 h-4 relative z-10" />
|
<UserPlus className="w-4 h-4 relative z-10 transition-transform duration-300 group-hover:rotate-12" />
|
||||||
<span className="relative z-10">Register</span>
|
<span className="relative z-10 text-sm">Register</span>
|
||||||
</button>
|
</button>
|
||||||
</>
|
</>
|
||||||
) : (
|
) : (
|
||||||
<div className="flex items-center gap-3">
|
<div className="flex items-center gap-3 justify-end w-full">
|
||||||
{isAuthenticated && <InAppNotificationBell />}
|
{isAuthenticated && <InAppNotificationBell />}
|
||||||
<div className="relative" ref={userMenuRef}>
|
<div className="relative" ref={userMenuRef}>
|
||||||
<button
|
<button
|
||||||
onClick={toggleUserMenu}
|
onClick={toggleUserMenu}
|
||||||
className="flex items-center space-x-3
|
className="flex items-center space-x-3
|
||||||
px-3 py-2 rounded-sm hover:bg-white/10
|
px-4 py-2.5 rounded-md hover:bg-[rgba(var(--luxury-gold-rgb),0.1)]
|
||||||
transition-all duration-300 border border-transparent
|
transition-all duration-300 border border-transparent
|
||||||
hover:border-[#d4af37]/30"
|
hover:border-[rgba(var(--luxury-gold-rgb),0.3)] group"
|
||||||
>
|
>
|
||||||
{userInfo?.avatar ? (
|
{userInfo?.avatar ? (
|
||||||
<img
|
<img
|
||||||
src={normalizeImageUrl(userInfo.avatar)}
|
src={normalizeImageUrl(userInfo.avatar)}
|
||||||
alt={userInfo.name}
|
alt={userInfo.name}
|
||||||
className="w-9 h-9 rounded-full
|
className="w-10 h-10 rounded-full
|
||||||
object-cover ring-2 ring-[#d4af37]/50"
|
object-cover ring-2 ring-[var(--luxury-gold)]/50
|
||||||
|
transition-all duration-300 group-hover:ring-[var(--luxury-gold)]/80
|
||||||
|
group-hover:scale-105 shadow-lg"
|
||||||
/>
|
/>
|
||||||
) : (
|
) : (
|
||||||
<div className="w-9 h-9 bg-gradient-to-br from-[#d4af37] to-[#c9a227]
|
<div className="w-10 h-10 bg-gradient-to-br from-[var(--luxury-gold)] to-[var(--luxury-gold-dark)]
|
||||||
rounded-full flex items-center
|
rounded-full flex items-center
|
||||||
justify-center ring-2 ring-[#d4af37]/50 shadow-lg"
|
justify-center ring-2 ring-[var(--luxury-gold)]/50 shadow-lg
|
||||||
|
transition-all duration-300 group-hover:ring-[var(--luxury-gold)]/80
|
||||||
|
group-hover:scale-105"
|
||||||
>
|
>
|
||||||
<span className="text-[#0f0f0f]
|
<span className="text-[#0f0f0f]
|
||||||
font-semibold text-sm"
|
font-semibold text-sm"
|
||||||
@@ -468,28 +590,29 @@ const Header: React.FC<HeaderProps> = ({
|
|||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
<span className="font-light text-white/90 tracking-wide">
|
<span className="font-light text-white/95 tracking-wider text-sm">
|
||||||
{userInfo?.name}
|
{userInfo?.name}
|
||||||
</span>
|
</span>
|
||||||
</button>
|
</button>
|
||||||
|
|
||||||
{isUserMenuOpen && (
|
{isUserMenuOpen && (
|
||||||
<div className="absolute right-0 mt-2
|
<div className="absolute right-0 mt-3
|
||||||
w-52 bg-gradient-to-b from-[#1a1a1a] to-[#0f0f0f]
|
w-56 bg-gradient-to-b from-[#0f0f0f] via-[#1a1a1a] to-[#0f0f0f]
|
||||||
rounded-sm shadow-2xl py-2 border border-[#d4af37]/20
|
rounded-lg shadow-[0_8px_32px_rgba(0,0,0,0.6)] py-3 border border-[rgba(var(--luxury-gold-rgb),0.2)]
|
||||||
z-50 backdrop-blur-xl animate-fade-in"
|
z-[9999] backdrop-blur-xl animate-fade-in"
|
||||||
>
|
>
|
||||||
<Link
|
<Link
|
||||||
to="/profile"
|
to="/profile"
|
||||||
onClick={() => setIsUserMenuOpen(false)}
|
onClick={() => setIsUserMenuOpen(false)}
|
||||||
className="flex items-center space-x-3
|
className="flex items-center space-x-3
|
||||||
px-4 py-2.5 text-white/90
|
px-5 py-3 text-white/95
|
||||||
hover:bg-[#d4af37]/10 hover:text-[#d4af37]
|
hover:bg-[rgba(var(--luxury-gold-rgb),0.1)] hover:text-[var(--luxury-gold)]
|
||||||
transition-all duration-300 border-l-2 border-transparent
|
transition-all duration-300 border-l-2 border-transparent
|
||||||
hover:border-[#d4af37]"
|
hover:border-[var(--luxury-gold)] rounded-md group relative"
|
||||||
>
|
>
|
||||||
<User className="w-4 h-4" />
|
<span className="absolute left-0 top-0 bottom-0 w-0 bg-gradient-to-r from-[rgba(var(--luxury-gold-rgb),0.1)] to-transparent group-hover:w-full transition-all duration-300 rounded-md"></span>
|
||||||
<span className="font-light tracking-wide">Profile</span>
|
<User className="w-4 h-4 relative z-10 transition-transform duration-300 group-hover:scale-110" />
|
||||||
|
<span className="font-light tracking-wider text-sm relative z-10">Profile</span>
|
||||||
</Link>
|
</Link>
|
||||||
{userInfo?.role !== 'admin' && userInfo?.role !== 'staff' && userInfo?.role !== 'accountant' && userInfo?.role !== 'housekeeping' && (
|
{userInfo?.role !== 'admin' && userInfo?.role !== 'staff' && userInfo?.role !== 'accountant' && userInfo?.role !== 'housekeeping' && (
|
||||||
<>
|
<>
|
||||||
@@ -497,97 +620,105 @@ const Header: React.FC<HeaderProps> = ({
|
|||||||
to="/dashboard"
|
to="/dashboard"
|
||||||
onClick={() => setIsUserMenuOpen(false)}
|
onClick={() => setIsUserMenuOpen(false)}
|
||||||
className="flex items-center space-x-3
|
className="flex items-center space-x-3
|
||||||
px-4 py-2.5 text-white/90
|
px-5 py-3 text-white/95
|
||||||
hover:bg-[#d4af37]/10 hover:text-[#d4af37]
|
hover:bg-[rgba(var(--luxury-gold-rgb),0.1)] hover:text-[var(--luxury-gold)]
|
||||||
transition-all duration-300 border-l-2 border-transparent
|
transition-all duration-300 border-l-2 border-transparent
|
||||||
hover:border-[#d4af37]"
|
hover:border-[var(--luxury-gold)] rounded-md group relative"
|
||||||
>
|
>
|
||||||
<Calendar className="w-4 h-4" />
|
<span className="absolute left-0 top-0 bottom-0 w-0 bg-gradient-to-r from-[rgba(var(--luxury-gold-rgb),0.1)] to-transparent group-hover:w-full transition-all duration-300 rounded-md"></span>
|
||||||
<span className="font-light tracking-wide">Dashboard</span>
|
<Calendar className="w-4 h-4 relative z-10 transition-transform duration-300 group-hover:scale-110" />
|
||||||
|
<span className="font-light tracking-wider text-sm relative z-10">Dashboard</span>
|
||||||
</Link>
|
</Link>
|
||||||
<Link
|
<Link
|
||||||
to="/favorites"
|
to="/favorites"
|
||||||
onClick={() => setIsUserMenuOpen(false)}
|
onClick={() => setIsUserMenuOpen(false)}
|
||||||
className="flex items-center space-x-3
|
className="flex items-center space-x-3
|
||||||
px-4 py-2.5 text-white/90
|
px-5 py-3 text-white/95
|
||||||
hover:bg-[#d4af37]/10 hover:text-[#d4af37]
|
hover:bg-[rgba(var(--luxury-gold-rgb),0.1)] hover:text-[var(--luxury-gold)]
|
||||||
transition-all duration-300 border-l-2 border-transparent
|
transition-all duration-300 border-l-2 border-transparent
|
||||||
hover:border-[#d4af37]"
|
hover:border-[var(--luxury-gold)] rounded-md group relative"
|
||||||
>
|
>
|
||||||
<Heart className="w-4 h-4" />
|
<span className="absolute left-0 top-0 bottom-0 w-0 bg-gradient-to-r from-[rgba(var(--luxury-gold-rgb),0.1)] to-transparent group-hover:w-full transition-all duration-300 rounded-md"></span>
|
||||||
<span className="font-light tracking-wide">Favorites</span>
|
<Heart className="w-4 h-4 relative z-10 transition-transform duration-300 group-hover:scale-110" />
|
||||||
|
<span className="font-light tracking-wider text-sm relative z-10">Favorites</span>
|
||||||
</Link>
|
</Link>
|
||||||
<Link
|
<Link
|
||||||
to="/bookings"
|
to="/bookings"
|
||||||
onClick={() => setIsUserMenuOpen(false)}
|
onClick={() => setIsUserMenuOpen(false)}
|
||||||
className="flex items-center space-x-3
|
className="flex items-center space-x-3
|
||||||
px-4 py-2.5 text-white/90
|
px-5 py-3 text-white/95
|
||||||
hover:bg-[#d4af37]/10 hover:text-[#d4af37]
|
hover:bg-[rgba(var(--luxury-gold-rgb),0.1)] hover:text-[var(--luxury-gold)]
|
||||||
transition-all duration-300 border-l-2 border-transparent
|
transition-all duration-300 border-l-2 border-transparent
|
||||||
hover:border-[#d4af37]"
|
hover:border-[var(--luxury-gold)] rounded-md group relative"
|
||||||
>
|
>
|
||||||
<Calendar className="w-4 h-4" />
|
<span className="absolute left-0 top-0 bottom-0 w-0 bg-gradient-to-r from-[rgba(var(--luxury-gold-rgb),0.1)] to-transparent group-hover:w-full transition-all duration-300 rounded-md"></span>
|
||||||
<span className="font-light tracking-wide">My Bookings</span>
|
<Calendar className="w-4 h-4 relative z-10 transition-transform duration-300 group-hover:scale-110" />
|
||||||
|
<span className="font-light tracking-wider text-sm relative z-10">My Bookings</span>
|
||||||
</Link>
|
</Link>
|
||||||
<Link
|
<Link
|
||||||
to="/loyalty"
|
to="/loyalty"
|
||||||
onClick={() => setIsUserMenuOpen(false)}
|
onClick={() => setIsUserMenuOpen(false)}
|
||||||
className="flex items-center space-x-3
|
className="flex items-center space-x-3
|
||||||
px-4 py-2.5 text-white/90
|
px-5 py-3 text-white/95
|
||||||
hover:bg-[#d4af37]/10 hover:text-[#d4af37]
|
hover:bg-[rgba(var(--luxury-gold-rgb),0.1)] hover:text-[var(--luxury-gold)]
|
||||||
transition-all duration-300 border-l-2 border-transparent
|
transition-all duration-300 border-l-2 border-transparent
|
||||||
hover:border-[#d4af37]"
|
hover:border-[var(--luxury-gold)] rounded-md group relative"
|
||||||
>
|
>
|
||||||
<Star className="w-4 h-4" />
|
<span className="absolute left-0 top-0 bottom-0 w-0 bg-gradient-to-r from-[rgba(var(--luxury-gold-rgb),0.1)] to-transparent group-hover:w-full transition-all duration-300 rounded-md"></span>
|
||||||
<span className="font-light tracking-wide">Loyalty Program</span>
|
<Star className="w-4 h-4 relative z-10 transition-transform duration-300 group-hover:scale-110" />
|
||||||
|
<span className="font-light tracking-wider text-sm relative z-10">Loyalty Program</span>
|
||||||
</Link>
|
</Link>
|
||||||
<Link
|
<Link
|
||||||
to="/group-bookings"
|
to="/group-bookings"
|
||||||
onClick={() => setIsUserMenuOpen(false)}
|
onClick={() => setIsUserMenuOpen(false)}
|
||||||
className="flex items-center space-x-3
|
className="flex items-center space-x-3
|
||||||
px-4 py-2.5 text-white/90
|
px-5 py-3 text-white/95
|
||||||
hover:bg-[#d4af37]/10 hover:text-[#d4af37]
|
hover:bg-[rgba(var(--luxury-gold-rgb),0.1)] hover:text-[var(--luxury-gold)]
|
||||||
transition-all duration-300 border-l-2 border-transparent
|
transition-all duration-300 border-l-2 border-transparent
|
||||||
hover:border-[#d4af37]"
|
hover:border-[var(--luxury-gold)] rounded-md group relative"
|
||||||
>
|
>
|
||||||
<Users className="w-4 h-4" />
|
<span className="absolute left-0 top-0 bottom-0 w-0 bg-gradient-to-r from-[rgba(var(--luxury-gold-rgb),0.1)] to-transparent group-hover:w-full transition-all duration-300 rounded-md"></span>
|
||||||
<span className="font-light tracking-wide">Group Bookings</span>
|
<Users className="w-4 h-4 relative z-10 transition-transform duration-300 group-hover:scale-110" />
|
||||||
|
<span className="font-light tracking-wider text-sm relative z-10">Group Bookings</span>
|
||||||
</Link>
|
</Link>
|
||||||
<Link
|
<Link
|
||||||
to="/complaints"
|
to="/complaints"
|
||||||
onClick={() => setIsUserMenuOpen(false)}
|
onClick={() => setIsUserMenuOpen(false)}
|
||||||
className="flex items-center space-x-3
|
className="flex items-center space-x-3
|
||||||
px-4 py-2.5 text-white/90
|
px-5 py-3 text-white/95
|
||||||
hover:bg-[#d4af37]/10 hover:text-[#d4af37]
|
hover:bg-[rgba(var(--luxury-gold-rgb),0.1)] hover:text-[var(--luxury-gold)]
|
||||||
transition-all duration-300 border-l-2 border-transparent
|
transition-all duration-300 border-l-2 border-transparent
|
||||||
hover:border-[#d4af37]"
|
hover:border-[var(--luxury-gold)] rounded-md group relative"
|
||||||
>
|
>
|
||||||
<AlertCircle className="w-4 h-4" />
|
<span className="absolute left-0 top-0 bottom-0 w-0 bg-gradient-to-r from-[rgba(var(--luxury-gold-rgb),0.1)] to-transparent group-hover:w-full transition-all duration-300 rounded-md"></span>
|
||||||
<span className="font-light tracking-wide">Complaints</span>
|
<AlertCircle className="w-4 h-4 relative z-10 transition-transform duration-300 group-hover:scale-110" />
|
||||||
|
<span className="font-light tracking-wider text-sm relative z-10">Complaints</span>
|
||||||
</Link>
|
</Link>
|
||||||
<Link
|
<Link
|
||||||
to="/guest-requests"
|
to="/guest-requests"
|
||||||
onClick={() => setIsUserMenuOpen(false)}
|
onClick={() => setIsUserMenuOpen(false)}
|
||||||
className="flex items-center space-x-3
|
className="flex items-center space-x-3
|
||||||
px-4 py-2.5 text-white/90
|
px-5 py-3 text-white/95
|
||||||
hover:bg-[#d4af37]/10 hover:text-[#d4af37]
|
hover:bg-[rgba(var(--luxury-gold-rgb),0.1)] hover:text-[var(--luxury-gold)]
|
||||||
transition-all duration-300 border-l-2 border-transparent
|
transition-all duration-300 border-l-2 border-transparent
|
||||||
hover:border-[#d4af37]"
|
hover:border-[var(--luxury-gold)] rounded-md group relative"
|
||||||
>
|
>
|
||||||
<Bell className="w-4 h-4" />
|
<span className="absolute left-0 top-0 bottom-0 w-0 bg-gradient-to-r from-[rgba(var(--luxury-gold-rgb),0.1)] to-transparent group-hover:w-full transition-all duration-300 rounded-md"></span>
|
||||||
<span className="font-light tracking-wide">Guest Requests</span>
|
<Bell className="w-4 h-4 relative z-10 transition-transform duration-300 group-hover:scale-110" />
|
||||||
|
<span className="font-light tracking-wider text-sm relative z-10">Guest Requests</span>
|
||||||
</Link>
|
</Link>
|
||||||
<Link
|
<Link
|
||||||
to="/gdpr"
|
to="/gdpr"
|
||||||
onClick={() => setIsUserMenuOpen(false)}
|
onClick={() => setIsUserMenuOpen(false)}
|
||||||
className="flex items-center space-x-3
|
className="flex items-center space-x-3
|
||||||
px-4 py-2.5 text-white/90
|
px-5 py-3 text-white/95
|
||||||
hover:bg-[#d4af37]/10 hover:text-[#d4af37]
|
hover:bg-[rgba(var(--luxury-gold-rgb),0.1)] hover:text-[var(--luxury-gold)]
|
||||||
transition-all duration-300 border-l-2 border-transparent
|
transition-all duration-300 border-l-2 border-transparent
|
||||||
hover:border-[#d4af37]"
|
hover:border-[var(--luxury-gold)] rounded-md group relative"
|
||||||
>
|
>
|
||||||
<Shield className="w-4 h-4" />
|
<span className="absolute left-0 top-0 bottom-0 w-0 bg-gradient-to-r from-[rgba(var(--luxury-gold-rgb),0.1)] to-transparent group-hover:w-full transition-all duration-300 rounded-md"></span>
|
||||||
<span className="font-light tracking-wide">Privacy & Data</span>
|
<Shield className="w-4 h-4 relative z-10 transition-transform duration-300 group-hover:scale-110" />
|
||||||
|
<span className="font-light tracking-wider text-sm relative z-10">Privacy & Data</span>
|
||||||
</Link>
|
</Link>
|
||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
@@ -598,13 +729,14 @@ const Header: React.FC<HeaderProps> = ({
|
|||||||
setIsUserMenuOpen(false)
|
setIsUserMenuOpen(false)
|
||||||
}
|
}
|
||||||
className="flex items-center
|
className="flex items-center
|
||||||
space-x-3 px-4 py-2.5 text-white/90
|
space-x-3 px-5 py-3 text-white/95
|
||||||
hover:bg-[#d4af37]/10 hover:text-[#d4af37]
|
hover:bg-[rgba(var(--luxury-gold-rgb),0.1)] hover:text-[var(--luxury-gold)]
|
||||||
transition-all duration-300 border-l-2 border-transparent
|
transition-all duration-300 border-l-2 border-transparent
|
||||||
hover:border-[#d4af37]"
|
hover:border-[var(--luxury-gold)] rounded-md group relative"
|
||||||
>
|
>
|
||||||
<User className="w-4 h-4" />
|
<span className="absolute left-0 top-0 bottom-0 w-0 bg-gradient-to-r from-[rgba(var(--luxury-gold-rgb),0.1)] to-transparent group-hover:w-full transition-all duration-300 rounded-md"></span>
|
||||||
<span className="font-light tracking-wide">Admin</span>
|
<User className="w-4 h-4 relative z-10 transition-transform duration-300 group-hover:scale-110" />
|
||||||
|
<span className="font-light tracking-wider text-sm relative z-10">Admin</span>
|
||||||
</Link>
|
</Link>
|
||||||
)}
|
)}
|
||||||
{userInfo?.role === 'staff' && (
|
{userInfo?.role === 'staff' && (
|
||||||
@@ -614,13 +746,14 @@ const Header: React.FC<HeaderProps> = ({
|
|||||||
setIsUserMenuOpen(false)
|
setIsUserMenuOpen(false)
|
||||||
}
|
}
|
||||||
className="flex items-center
|
className="flex items-center
|
||||||
space-x-3 px-4 py-2.5 text-white/90
|
space-x-3 px-5 py-3 text-white/95
|
||||||
hover:bg-[#d4af37]/10 hover:text-[#d4af37]
|
hover:bg-[rgba(var(--luxury-gold-rgb),0.1)] hover:text-[var(--luxury-gold)]
|
||||||
transition-all duration-300 border-l-2 border-transparent
|
transition-all duration-300 border-l-2 border-transparent
|
||||||
hover:border-[#d4af37]"
|
hover:border-[var(--luxury-gold)] rounded-md group relative"
|
||||||
>
|
>
|
||||||
<User className="w-4 h-4" />
|
<span className="absolute left-0 top-0 bottom-0 w-0 bg-gradient-to-r from-[rgba(var(--luxury-gold-rgb),0.1)] to-transparent group-hover:w-full transition-all duration-300 rounded-md"></span>
|
||||||
<span className="font-light tracking-wide">Staff Dashboard</span>
|
<User className="w-4 h-4 relative z-10 transition-transform duration-300 group-hover:scale-110" />
|
||||||
|
<span className="font-light tracking-wider text-sm relative z-10">Staff Dashboard</span>
|
||||||
</Link>
|
</Link>
|
||||||
)}
|
)}
|
||||||
{userInfo?.role === 'accountant' && (
|
{userInfo?.role === 'accountant' && (
|
||||||
@@ -630,13 +763,14 @@ const Header: React.FC<HeaderProps> = ({
|
|||||||
setIsUserMenuOpen(false)
|
setIsUserMenuOpen(false)
|
||||||
}
|
}
|
||||||
className="flex items-center
|
className="flex items-center
|
||||||
space-x-3 px-4 py-2.5 text-white/90
|
space-x-3 px-5 py-3 text-white/95
|
||||||
hover:bg-[#d4af37]/10 hover:text-[#d4af37]
|
hover:bg-[rgba(var(--luxury-gold-rgb),0.1)] hover:text-[var(--luxury-gold)]
|
||||||
transition-all duration-300 border-l-2 border-transparent
|
transition-all duration-300 border-l-2 border-transparent
|
||||||
hover:border-[#d4af37]"
|
hover:border-[var(--luxury-gold)] rounded-md group relative"
|
||||||
>
|
>
|
||||||
<User className="w-4 h-4" />
|
<span className="absolute left-0 top-0 bottom-0 w-0 bg-gradient-to-r from-[rgba(var(--luxury-gold-rgb),0.1)] to-transparent group-hover:w-full transition-all duration-300 rounded-md"></span>
|
||||||
<span className="font-light tracking-wide">Accountant Dashboard</span>
|
<User className="w-4 h-4 relative z-10 transition-transform duration-300 group-hover:scale-110" />
|
||||||
|
<span className="font-light tracking-wider text-sm relative z-10">Accountant Dashboard</span>
|
||||||
</Link>
|
</Link>
|
||||||
)}
|
)}
|
||||||
{userInfo?.role === 'housekeeping' && (
|
{userInfo?.role === 'housekeeping' && (
|
||||||
@@ -646,26 +780,28 @@ const Header: React.FC<HeaderProps> = ({
|
|||||||
setIsUserMenuOpen(false)
|
setIsUserMenuOpen(false)
|
||||||
}
|
}
|
||||||
className="flex items-center
|
className="flex items-center
|
||||||
space-x-3 px-4 py-2.5 text-white/90
|
space-x-3 px-5 py-3 text-white/95
|
||||||
hover:bg-[#d4af37]/10 hover:text-[#d4af37]
|
hover:bg-[rgba(var(--luxury-gold-rgb),0.1)] hover:text-[var(--luxury-gold)]
|
||||||
transition-all duration-300 border-l-2 border-transparent
|
transition-all duration-300 border-l-2 border-transparent
|
||||||
hover:border-[#d4af37]"
|
hover:border-[var(--luxury-gold)] rounded-md group relative"
|
||||||
>
|
>
|
||||||
<User className="w-4 h-4" />
|
<span className="absolute left-0 top-0 bottom-0 w-0 bg-gradient-to-r from-[rgba(var(--luxury-gold-rgb),0.1)] to-transparent group-hover:w-full transition-all duration-300 rounded-md"></span>
|
||||||
<span className="font-light tracking-wide">Housekeeping Dashboard</span>
|
<User className="w-4 h-4 relative z-10 transition-transform duration-300 group-hover:scale-110" />
|
||||||
|
<span className="font-light tracking-wider text-sm relative z-10">Housekeeping Dashboard</span>
|
||||||
</Link>
|
</Link>
|
||||||
)}
|
)}
|
||||||
<div className="border-t border-[#d4af37]/20 my-1"></div>
|
<div className="border-t border-[rgba(var(--luxury-gold-rgb),0.2)] my-2"></div>
|
||||||
<button
|
<button
|
||||||
onClick={handleLogout}
|
onClick={handleLogout}
|
||||||
className="w-full flex items-center
|
className="w-full flex items-center
|
||||||
space-x-3 px-4 py-2.5 text-red-400/90
|
space-x-3 px-5 py-3 text-red-400/95
|
||||||
hover:bg-red-500/10 hover:text-red-400
|
hover:bg-red-500/10 hover:text-red-400
|
||||||
transition-all duration-300 border-l-2 border-transparent
|
transition-all duration-300 border-l-2 border-transparent
|
||||||
hover:border-red-500/50 text-left"
|
hover:border-red-500/50 text-left rounded-md group relative"
|
||||||
>
|
>
|
||||||
<LogOut className="w-4 h-4" />
|
<span className="absolute left-0 top-0 bottom-0 w-0 bg-gradient-to-r from-red-500/10 to-transparent group-hover:w-full transition-all duration-300 rounded-md"></span>
|
||||||
<span className="font-light tracking-wide">Logout</span>
|
<LogOut className="w-4 h-4 relative z-10 transition-transform duration-300 group-hover:scale-110" />
|
||||||
|
<span className="font-light tracking-wider text-sm relative z-10">Logout</span>
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import React, { useRef } from 'react';
|
import React, { useRef } from 'react';
|
||||||
|
import { createPortal } from 'react-dom';
|
||||||
import { Link } from 'react-router-dom';
|
import { Link } from 'react-router-dom';
|
||||||
import { Menu, X } from 'lucide-react';
|
import { Menu, X } from 'lucide-react';
|
||||||
import { useClickOutside } from '../hooks/useClickOutside';
|
|
||||||
|
|
||||||
interface NavbarProps {
|
interface NavbarProps {
|
||||||
isMobileMenuOpen: boolean;
|
isMobileMenuOpen: boolean;
|
||||||
@@ -28,12 +28,50 @@ const Navbar: React.FC<NavbarProps> = ({
|
|||||||
mobileMenuContent
|
mobileMenuContent
|
||||||
}) => {
|
}) => {
|
||||||
const mobileMenuContainerRef = useRef<HTMLDivElement>(null);
|
const mobileMenuContainerRef = useRef<HTMLDivElement>(null);
|
||||||
|
const mobileMenuDropdownRef = useRef<HTMLDivElement>(null);
|
||||||
|
|
||||||
useClickOutside(mobileMenuContainerRef, () => {
|
// Handle click outside for the portal-rendered dropdown
|
||||||
if (isMobileMenuOpen) {
|
// Note: We don't use useClickOutside here because the dropdown is rendered via portal
|
||||||
onMobileMenuToggle();
|
React.useEffect(() => {
|
||||||
|
if (!isMobileMenuOpen) return;
|
||||||
|
|
||||||
|
const handleClickOutside = (event: MouseEvent | TouchEvent) => {
|
||||||
|
const target = event.target as Node;
|
||||||
|
const targetElement = target as Element;
|
||||||
|
|
||||||
|
// Check if click is on a link - if so, don't close (let navigation happen)
|
||||||
|
const linkElement = targetElement.closest('a');
|
||||||
|
if (linkElement && mobileMenuDropdownRef.current?.contains(linkElement)) {
|
||||||
|
return; // Let the link handle navigation
|
||||||
}
|
}
|
||||||
});
|
|
||||||
|
// Check if click is inside the dropdown menu
|
||||||
|
if (mobileMenuDropdownRef.current?.contains(target)) {
|
||||||
|
return; // Don't close if clicking inside menu
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if click is on the menu button
|
||||||
|
if (mobileMenuContainerRef.current?.contains(target)) {
|
||||||
|
return; // Don't close if clicking the menu button
|
||||||
|
}
|
||||||
|
|
||||||
|
// Close menu if clicking outside
|
||||||
|
onMobileMenuToggle();
|
||||||
|
};
|
||||||
|
|
||||||
|
// Use a delay to avoid interfering with link clicks
|
||||||
|
// This ensures link navigation happens before the handler is active
|
||||||
|
const timeoutId = setTimeout(() => {
|
||||||
|
document.addEventListener('mousedown', handleClickOutside);
|
||||||
|
document.addEventListener('touchstart', handleClickOutside);
|
||||||
|
}, 300);
|
||||||
|
|
||||||
|
return () => {
|
||||||
|
clearTimeout(timeoutId);
|
||||||
|
document.removeEventListener('mousedown', handleClickOutside);
|
||||||
|
document.removeEventListener('touchstart', handleClickOutside);
|
||||||
|
};
|
||||||
|
}, [isMobileMenuOpen, onMobileMenuToggle]);
|
||||||
|
|
||||||
const handleLinkClick = () => {
|
const handleLinkClick = () => {
|
||||||
if (onLinkClick) {
|
if (onLinkClick) {
|
||||||
@@ -51,10 +89,10 @@ const Navbar: React.FC<NavbarProps> = ({
|
|||||||
to={link.to}
|
to={link.to}
|
||||||
onClick={handleLinkClick}
|
onClick={handleLinkClick}
|
||||||
className="px-4 py-3 text-white/90
|
className="px-4 py-3 text-white/90
|
||||||
hover:bg-[#d4af37]/10 hover:text-[#d4af37]
|
hover:bg-[var(--luxury-gold)]/10 hover:text-[var(--luxury-gold)]
|
||||||
rounded-sm transition-all duration-300
|
rounded-sm transition-all duration-300
|
||||||
border-l-2 border-transparent
|
border-l-2 border-transparent
|
||||||
hover:border-[#d4af37] font-light tracking-wide"
|
hover:border-[var(--luxury-gold)] font-light tracking-wide"
|
||||||
>
|
>
|
||||||
{link.label}
|
{link.label}
|
||||||
</Link>
|
</Link>
|
||||||
@@ -66,17 +104,24 @@ const Navbar: React.FC<NavbarProps> = ({
|
|||||||
return (
|
return (
|
||||||
<div className="relative" ref={mobileMenuContainerRef}>
|
<div className="relative" ref={mobileMenuContainerRef}>
|
||||||
{/* Desktop Navigation */}
|
{/* Desktop Navigation */}
|
||||||
<nav className="hidden md:flex items-center space-x-1">
|
<nav className="hidden md:flex items-center space-x-2">
|
||||||
{navLinks.map((link) => (
|
{navLinks.map((link) => (
|
||||||
<Link
|
<Link
|
||||||
key={link.to}
|
key={link.to}
|
||||||
to={link.to}
|
to={link.to}
|
||||||
className="text-white/90 hover:text-[#d4af37]
|
className="text-white/95 hover:text-[var(--luxury-gold)]
|
||||||
transition-all duration-300 font-light px-4 py-2
|
transition-all duration-300 font-light px-5 py-2.5
|
||||||
relative group tracking-wide"
|
relative group tracking-wider text-sm uppercase"
|
||||||
>
|
>
|
||||||
<span className="relative z-10">{link.label}</span>
|
<span className="relative z-10 transition-all duration-300 group-hover:tracking-widest">
|
||||||
<span className="absolute inset-0 bg-gradient-to-r from-[#d4af37]/0 via-[#d4af37]/10 to-[#d4af37]/0 opacity-0 group-hover:opacity-100 transition-opacity duration-300"></span>
|
{link.label}
|
||||||
|
</span>
|
||||||
|
{/* Underline effect */}
|
||||||
|
<span className="absolute bottom-0 left-0 w-0 h-[1px] bg-gradient-to-r from-[var(--luxury-gold)] to-[var(--luxury-gold-light)] group-hover:w-full transition-all duration-500"></span>
|
||||||
|
{/* Background glow on hover */}
|
||||||
|
<span className="absolute inset-0 bg-gradient-to-r from-[rgba(var(--luxury-gold-rgb),0.05)] via-[rgba(var(--luxury-gold-rgb),0.1)] to-[rgba(var(--luxury-gold-rgb),0.05)] opacity-0 group-hover:opacity-100 transition-opacity duration-300 rounded-sm"></span>
|
||||||
|
{/* Subtle border on hover */}
|
||||||
|
<span className="absolute inset-0 border border-[rgba(var(--luxury-gold-rgb),0.1)] rounded-sm opacity-0 group-hover:opacity-100 transition-opacity duration-300"></span>
|
||||||
</Link>
|
</Link>
|
||||||
))}
|
))}
|
||||||
</nav>
|
</nav>
|
||||||
@@ -84,49 +129,138 @@ const Navbar: React.FC<NavbarProps> = ({
|
|||||||
{/* Mobile Menu Button */}
|
{/* Mobile Menu Button */}
|
||||||
<button
|
<button
|
||||||
onClick={onMobileMenuToggle}
|
onClick={onMobileMenuToggle}
|
||||||
className="md:hidden p-2 rounded-sm
|
className="md:hidden p-3 rounded-lg
|
||||||
hover:bg-white/10 border border-transparent
|
bg-[rgba(var(--luxury-gold-rgb),0.1)] hover:bg-[rgba(var(--luxury-gold-rgb),0.2)]
|
||||||
hover:border-[#d4af37]/30 transition-all duration-300"
|
border border-[rgba(var(--luxury-gold-rgb),0.3)]
|
||||||
|
hover:border-[rgba(var(--luxury-gold-rgb),0.5)] transition-all duration-300
|
||||||
|
group shadow-lg"
|
||||||
|
aria-label="Toggle menu"
|
||||||
>
|
>
|
||||||
{isMobileMenuOpen ? (
|
{isMobileMenuOpen ? (
|
||||||
<X className="w-6 h-6 text-[#d4af37]" />
|
<X className="w-6 h-6 text-[var(--luxury-gold)] transition-transform duration-300 group-hover:rotate-90" />
|
||||||
) : (
|
) : (
|
||||||
<Menu className="w-6 h-6 text-white/90" />
|
<Menu className="w-6 h-6 text-[var(--luxury-gold)] group-hover:text-[var(--luxury-gold-light)] transition-colors duration-300" />
|
||||||
)}
|
)}
|
||||||
</button>
|
</button>
|
||||||
|
|
||||||
{/* Mobile Menu Dropdown - Absolute positioned */}
|
{/* Mobile Menu Backdrop and Dropdown - Rendered via Portal */}
|
||||||
{isMobileMenuOpen && (
|
{isMobileMenuOpen && typeof document !== 'undefined' && createPortal(
|
||||||
|
<>
|
||||||
|
{/* Mobile Menu Backdrop */}
|
||||||
<div
|
<div
|
||||||
className="md:hidden absolute right-0 mt-2 w-64
|
className="md:hidden fixed inset-0 bg-black/70 backdrop-blur-sm"
|
||||||
bg-gradient-to-b from-[#1a1a1a] to-[#0f0f0f]
|
style={{
|
||||||
rounded-sm shadow-2xl py-2 border border-[#d4af37]/20
|
top: '73px',
|
||||||
z-50 backdrop-blur-xl animate-fade-in max-h-[calc(100vh-120px)]
|
zIndex: 99998,
|
||||||
overflow-y-auto"
|
position: 'fixed',
|
||||||
|
pointerEvents: 'auto'
|
||||||
|
} as React.CSSProperties}
|
||||||
|
onClick={(e) => {
|
||||||
|
// Only close if clicking directly on backdrop, not on dropdown content
|
||||||
|
const target = e.target as HTMLElement;
|
||||||
|
if (target === e.currentTarget) {
|
||||||
|
onMobileMenuToggle();
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
onTouchStart={(e) => {
|
||||||
|
// Prevent backdrop from interfering with touch events on links
|
||||||
|
const target = e.target as HTMLElement;
|
||||||
|
if (mobileMenuDropdownRef.current?.contains(target)) {
|
||||||
|
e.stopPropagation();
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
|
||||||
|
{/* Mobile Menu Dropdown - Right side on mobile */}
|
||||||
|
<div
|
||||||
|
ref={mobileMenuDropdownRef}
|
||||||
|
className="md:hidden fixed top-[73px] right-0 w-80 max-w-[85vw]
|
||||||
|
bg-gradient-to-b from-[#0f0f0f] via-[#1a1a1a] to-[#0f0f0f]
|
||||||
|
shadow-[0_8px_32px_rgba(0,0,0,0.9),_-4px_0_20px_rgba(var(--luxury-gold-rgb),0.2)]
|
||||||
|
py-4 border-l-2 border-[rgba(var(--luxury-gold-rgb),0.4)]
|
||||||
|
backdrop-blur-xl animate-fade-in
|
||||||
|
overflow-y-auto h-[calc(100vh-73px)]"
|
||||||
|
style={{
|
||||||
|
zIndex: 99999,
|
||||||
|
position: 'fixed',
|
||||||
|
touchAction: 'auto',
|
||||||
|
pointerEvents: 'auto'
|
||||||
|
} as React.CSSProperties}
|
||||||
|
onClick={(e) => {
|
||||||
|
// Stop propagation to prevent backdrop from closing menu when clicking inside
|
||||||
|
e.stopPropagation();
|
||||||
|
}}
|
||||||
|
onTouchStart={(e) => {
|
||||||
|
// Stop touch events from propagating to backdrop
|
||||||
|
e.stopPropagation();
|
||||||
|
}}
|
||||||
|
onTouchEnd={(e) => {
|
||||||
|
// Stop touch end events from propagating
|
||||||
|
e.stopPropagation();
|
||||||
|
}}
|
||||||
>
|
>
|
||||||
<div className="flex flex-col space-y-1">
|
<div className="flex flex-col space-y-1 px-2">
|
||||||
{navLinks.map((link) => (
|
{navLinks.map((link) => (
|
||||||
<Link
|
<Link
|
||||||
key={link.to}
|
key={link.to}
|
||||||
to={link.to}
|
to={link.to}
|
||||||
onClick={handleLinkClick}
|
onClick={(e) => {
|
||||||
className="px-4 py-3 text-white/90
|
// Stop propagation to prevent backdrop from closing menu
|
||||||
hover:bg-[#d4af37]/10 hover:text-[#d4af37]
|
e.stopPropagation();
|
||||||
rounded-sm transition-all duration-300
|
|
||||||
|
// Close mobile menu after navigation starts
|
||||||
|
setTimeout(() => {
|
||||||
|
if (handleLinkClick) {
|
||||||
|
handleLinkClick();
|
||||||
|
}
|
||||||
|
}, 150);
|
||||||
|
}}
|
||||||
|
onTouchStart={(e) => {
|
||||||
|
// Stop touch events from propagating to backdrop
|
||||||
|
e.stopPropagation();
|
||||||
|
}}
|
||||||
|
onTouchEnd={(e) => {
|
||||||
|
// Ensure touch end doesn't trigger backdrop
|
||||||
|
e.stopPropagation();
|
||||||
|
}}
|
||||||
|
onMouseDown={(e) => {
|
||||||
|
// Ensure mouse events work
|
||||||
|
e.stopPropagation();
|
||||||
|
}}
|
||||||
|
className="px-4 py-3.5 text-white/95
|
||||||
|
hover:bg-[rgba(var(--luxury-gold-rgb),0.15)] hover:text-[var(--luxury-gold)]
|
||||||
|
rounded-md transition-all duration-300
|
||||||
border-l-2 border-transparent
|
border-l-2 border-transparent
|
||||||
hover:border-[#d4af37] font-light tracking-wide"
|
hover:border-[var(--luxury-gold)] font-light tracking-wider text-sm uppercase
|
||||||
|
group relative mx-2 cursor-pointer text-left w-full block"
|
||||||
|
style={{
|
||||||
|
touchAction: 'manipulation',
|
||||||
|
WebkitTapHighlightColor: 'transparent',
|
||||||
|
pointerEvents: 'auto'
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<span
|
||||||
|
className="relative z-10"
|
||||||
|
style={{ pointerEvents: 'none' }}
|
||||||
>
|
>
|
||||||
{link.label}
|
{link.label}
|
||||||
|
</span>
|
||||||
|
<span
|
||||||
|
className="absolute left-0 top-0 bottom-0 w-0 bg-gradient-to-r from-[rgba(var(--luxury-gold-rgb),0.15)] to-transparent group-hover:w-full transition-all duration-300 rounded-md"
|
||||||
|
style={{ pointerEvents: 'none' }}
|
||||||
|
></span>
|
||||||
</Link>
|
</Link>
|
||||||
))}
|
))}
|
||||||
{mobileMenuContent && (
|
{mobileMenuContent && (
|
||||||
<>
|
<>
|
||||||
<div className="border-t border-[#d4af37]/20 my-2"></div>
|
<div className="border-t border-[var(--luxury-gold)]/20 my-2"></div>
|
||||||
{mobileMenuContent}
|
{mobileMenuContent}
|
||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
</>,
|
||||||
|
document.body
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -14,19 +14,19 @@ const OfflineIndicator: React.FC = () => {
|
|||||||
aria-live="polite"
|
aria-live="polite"
|
||||||
>
|
>
|
||||||
<div className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 pb-4 sm:pb-6">
|
<div className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 pb-4 sm:pb-6">
|
||||||
<div className="relative overflow-hidden rounded-lg shadow-2xl border border-[#d4af37]/30 backdrop-blur-xl bg-gradient-to-r from-slate-900/95 via-slate-800/95 to-slate-900/95">
|
<div className="relative overflow-hidden rounded-lg shadow-2xl border border-[var(--luxury-gold)]/30 backdrop-blur-xl bg-gradient-to-r from-slate-900/95 via-slate-800/95 to-slate-900/95">
|
||||||
{/* Gold accent border top */}
|
{/* Gold accent border top */}
|
||||||
<div className="absolute top-0 left-0 right-0 h-0.5 bg-gradient-to-r from-transparent via-[#d4af37] to-transparent"></div>
|
<div className="absolute top-0 left-0 right-0 h-0.5 bg-gradient-to-r from-transparent via-[var(--luxury-gold)] to-transparent"></div>
|
||||||
|
|
||||||
{/* Subtle glow effect */}
|
{/* Subtle glow effect */}
|
||||||
<div className="absolute inset-0 bg-gradient-to-r from-[#d4af37]/5 via-transparent to-[#d4af37]/5 pointer-events-none"></div>
|
<div className="absolute inset-0 bg-gradient-to-r from-[var(--luxury-gold)]/5 via-transparent to-[var(--luxury-gold)]/5 pointer-events-none"></div>
|
||||||
|
|
||||||
{/* Content */}
|
{/* Content */}
|
||||||
<div className="relative px-6 py-4 sm:px-8 sm:py-5 flex items-center justify-center gap-3 sm:gap-4">
|
<div className="relative px-6 py-4 sm:px-8 sm:py-5 flex items-center justify-center gap-3 sm:gap-4">
|
||||||
{/* Icon with gold accent */}
|
{/* Icon with gold accent */}
|
||||||
<div className="relative flex-shrink-0">
|
<div className="relative flex-shrink-0">
|
||||||
<div className="absolute inset-0 bg-[#d4af37]/20 blur-xl rounded-full"></div>
|
<div className="absolute inset-0 bg-[var(--luxury-gold)]/20 blur-xl rounded-full"></div>
|
||||||
<div className="relative bg-gradient-to-br from-[#d4af37] to-[#c9a227] p-2.5 rounded-full shadow-lg shadow-[#d4af37]/30">
|
<div className="relative bg-gradient-to-br from-[var(--luxury-gold)] to-[var(--luxury-gold-dark)] p-2.5 rounded-full shadow-lg shadow-[var(--luxury-gold)]/30">
|
||||||
<WifiOff className="w-5 h-5 sm:w-6 sm:h-6 text-slate-900" strokeWidth={2.5} />
|
<WifiOff className="w-5 h-5 sm:w-6 sm:h-6 text-slate-900" strokeWidth={2.5} />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -43,9 +43,9 @@ const OfflineIndicator: React.FC = () => {
|
|||||||
|
|
||||||
{/* Decorative elements */}
|
{/* Decorative elements */}
|
||||||
<div className="hidden sm:flex absolute right-6 top-1/2 -translate-y-1/2 items-center gap-1 opacity-30">
|
<div className="hidden sm:flex absolute right-6 top-1/2 -translate-y-1/2 items-center gap-1 opacity-30">
|
||||||
<div className="w-1 h-1 rounded-full bg-[#d4af37] animate-pulse"></div>
|
<div className="w-1 h-1 rounded-full bg-[var(--luxury-gold)] animate-pulse"></div>
|
||||||
<div className="w-1 h-1 rounded-full bg-[#d4af37] animate-pulse" style={{ animationDelay: '0.2s' }}></div>
|
<div className="w-1 h-1 rounded-full bg-[var(--luxury-gold)] animate-pulse" style={{ animationDelay: '0.2s' }}></div>
|
||||||
<div className="w-1 h-1 rounded-full bg-[#d4af37] animate-pulse" style={{ animationDelay: '0.4s' }}></div>
|
<div className="w-1 h-1 rounded-full bg-[var(--luxury-gold)] animate-pulse" style={{ animationDelay: '0.4s' }}></div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -177,6 +177,11 @@ const SidebarAdmin: React.FC<SidebarAdminProps> = ({
|
|||||||
icon: FileText,
|
icon: FileText,
|
||||||
label: 'Invoices'
|
label: 'Invoices'
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
path: '/admin/promotions',
|
||||||
|
icon: Tag,
|
||||||
|
label: 'Promotions'
|
||||||
|
},
|
||||||
{
|
{
|
||||||
path: '/admin/financial-audit',
|
path: '/admin/financial-audit',
|
||||||
icon: FileCheck,
|
icon: FileCheck,
|
||||||
@@ -193,11 +198,6 @@ const SidebarAdmin: React.FC<SidebarAdminProps> = ({
|
|||||||
icon: BarChart3,
|
icon: BarChart3,
|
||||||
label: 'Reports & Analytics'
|
label: 'Reports & Analytics'
|
||||||
},
|
},
|
||||||
{
|
|
||||||
path: '/admin/business',
|
|
||||||
icon: TrendingUp,
|
|
||||||
label: 'Business Insights'
|
|
||||||
},
|
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -367,7 +367,7 @@ const SidebarAdmin: React.FC<SidebarAdminProps> = ({
|
|||||||
const isActive = (path: string) => {
|
const isActive = (path: string) => {
|
||||||
if (location.pathname === path) return true;
|
if (location.pathname === path) return true;
|
||||||
|
|
||||||
if (path === '/admin/settings' || path === '/admin/analytics' || path === '/admin/business' || path === '/admin/reception' || path === '/admin/advanced-rooms' || path === '/admin/page-content' || path === '/admin/loyalty') {
|
if (path === '/admin/settings' || path === '/admin/analytics' || path === '/admin/reception' || path === '/admin/advanced-rooms' || path === '/admin/page-content' || path === '/admin/loyalty') {
|
||||||
return location.pathname === path;
|
return location.pathname === path;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
162
Frontend/src/shared/contexts/ThemeContext.tsx
Normal file
162
Frontend/src/shared/contexts/ThemeContext.tsx
Normal file
@@ -0,0 +1,162 @@
|
|||||||
|
import React, {
|
||||||
|
createContext,
|
||||||
|
useContext,
|
||||||
|
useEffect,
|
||||||
|
useState,
|
||||||
|
ReactNode,
|
||||||
|
} from 'react';
|
||||||
|
import { themeService } from '../../features/system/services/systemSettingsService';
|
||||||
|
|
||||||
|
type ThemeColors = {
|
||||||
|
primary: string;
|
||||||
|
primaryLight: string;
|
||||||
|
primaryDark: string;
|
||||||
|
primaryAccent: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
type ThemeContextValue = {
|
||||||
|
colors: ThemeColors;
|
||||||
|
isLoading: boolean;
|
||||||
|
refreshTheme: () => Promise<void>;
|
||||||
|
updateColors: (colors: Partial<ThemeColors>) => void;
|
||||||
|
};
|
||||||
|
|
||||||
|
const defaultColors: ThemeColors = {
|
||||||
|
primary: '#d4af37',
|
||||||
|
primaryLight: '#f5d76e',
|
||||||
|
primaryDark: '#c9a227',
|
||||||
|
primaryAccent: '#e8c547',
|
||||||
|
};
|
||||||
|
|
||||||
|
const ThemeContext = createContext<ThemeContextValue | undefined>(undefined);
|
||||||
|
|
||||||
|
export const useTheme = () => {
|
||||||
|
const context = useContext(ThemeContext);
|
||||||
|
if (!context) {
|
||||||
|
throw new Error('useTheme must be used within ThemeProvider');
|
||||||
|
}
|
||||||
|
return context;
|
||||||
|
};
|
||||||
|
|
||||||
|
interface ThemeProviderProps {
|
||||||
|
children: ReactNode;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Converts hex color to RGB values
|
||||||
|
*/
|
||||||
|
const hexToRgb = (hex: string): string => {
|
||||||
|
const result = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(hex);
|
||||||
|
return result
|
||||||
|
? `${parseInt(result[1], 16)}, ${parseInt(result[2], 16)}, ${parseInt(result[3], 16)}`
|
||||||
|
: '212, 175, 55'; // Default gold RGB
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Applies theme colors as CSS variables to the document root
|
||||||
|
*/
|
||||||
|
const applyThemeColors = (colors: ThemeColors) => {
|
||||||
|
const root = document.documentElement;
|
||||||
|
|
||||||
|
// Apply CSS variables
|
||||||
|
root.style.setProperty('--luxury-gold', colors.primary);
|
||||||
|
root.style.setProperty('--luxury-gold-light', colors.primaryLight);
|
||||||
|
root.style.setProperty('--luxury-gold-dark', colors.primaryDark);
|
||||||
|
root.style.setProperty('--luxury-gold-accent', colors.primaryAccent);
|
||||||
|
|
||||||
|
// Add RGB versions for rgba() usage
|
||||||
|
root.style.setProperty('--luxury-gold-rgb', hexToRgb(colors.primary));
|
||||||
|
root.style.setProperty('--luxury-gold-light-rgb', hexToRgb(colors.primaryLight));
|
||||||
|
root.style.setProperty('--luxury-gold-dark-rgb', hexToRgb(colors.primaryDark));
|
||||||
|
root.style.setProperty('--luxury-gold-accent-rgb', hexToRgb(colors.primaryAccent));
|
||||||
|
|
||||||
|
// Also update gradient variables
|
||||||
|
root.style.setProperty(
|
||||||
|
'--gradient-gold',
|
||||||
|
`linear-gradient(135deg, ${colors.primary} 0%, ${colors.primaryLight} 100%)`
|
||||||
|
);
|
||||||
|
root.style.setProperty(
|
||||||
|
'--gradient-gold-dark',
|
||||||
|
`linear-gradient(135deg, ${colors.primaryDark} 0%, ${colors.primary} 100%)`
|
||||||
|
);
|
||||||
|
|
||||||
|
// Update shadow variables with proper opacity
|
||||||
|
const primaryRgb = hexToRgb(colors.primary);
|
||||||
|
root.style.setProperty(
|
||||||
|
'--shadow-luxury',
|
||||||
|
`0 4px 20px rgba(${primaryRgb}, 0.15)`
|
||||||
|
);
|
||||||
|
root.style.setProperty(
|
||||||
|
'--shadow-luxury-gold',
|
||||||
|
`0 8px 30px rgba(${primaryRgb}, 0.25)`
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export const ThemeProvider: React.FC<ThemeProviderProps> = ({ children }) => {
|
||||||
|
const [colors, setColors] = useState<ThemeColors>(defaultColors);
|
||||||
|
const [isLoading, setIsLoading] = useState<boolean>(true);
|
||||||
|
|
||||||
|
const loadTheme = async () => {
|
||||||
|
try {
|
||||||
|
setIsLoading(true);
|
||||||
|
const response = await themeService.getThemeSettings();
|
||||||
|
if (response.data) {
|
||||||
|
const newColors: ThemeColors = {
|
||||||
|
primary: response.data.theme_primary_color || defaultColors.primary,
|
||||||
|
primaryLight: response.data.theme_primary_light || defaultColors.primaryLight,
|
||||||
|
primaryDark: response.data.theme_primary_dark || defaultColors.primaryDark,
|
||||||
|
primaryAccent: response.data.theme_primary_accent || defaultColors.primaryAccent,
|
||||||
|
};
|
||||||
|
setColors(newColors);
|
||||||
|
applyThemeColors(newColors);
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Error loading theme settings:', error);
|
||||||
|
// Apply default colors on error
|
||||||
|
applyThemeColors(defaultColors);
|
||||||
|
setColors(defaultColors);
|
||||||
|
} finally {
|
||||||
|
setIsLoading(false);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
loadTheme();
|
||||||
|
|
||||||
|
// Listen for theme refresh events
|
||||||
|
const handleRefresh = () => {
|
||||||
|
loadTheme();
|
||||||
|
};
|
||||||
|
|
||||||
|
if (typeof window !== 'undefined') {
|
||||||
|
window.addEventListener('refreshTheme', handleRefresh);
|
||||||
|
return () => {
|
||||||
|
window.removeEventListener('refreshTheme', handleRefresh);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
const refreshTheme = async () => {
|
||||||
|
await loadTheme();
|
||||||
|
};
|
||||||
|
|
||||||
|
const updateColors = (newColors: Partial<ThemeColors>) => {
|
||||||
|
const updatedColors = { ...colors, ...newColors };
|
||||||
|
setColors(updatedColors);
|
||||||
|
applyThemeColors(updatedColors);
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<ThemeContext.Provider
|
||||||
|
value={{
|
||||||
|
colors,
|
||||||
|
isLoading,
|
||||||
|
refreshTheme,
|
||||||
|
updateColors,
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{children}
|
||||||
|
</ThemeContext.Provider>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
@@ -29,23 +29,23 @@ const NotFoundPage: React.FC = () => {
|
|||||||
style={{ filter: 'drop-shadow(0 4px 6px rgba(0,0,0,0.1))' }}
|
style={{ filter: 'drop-shadow(0 4px 6px rgba(0,0,0,0.1))' }}
|
||||||
/>
|
/>
|
||||||
) : (
|
) : (
|
||||||
<div className="relative p-4 sm:p-5 bg-gradient-to-br from-[#d4af37] to-[#c9a227] rounded-full shadow-lg shadow-[#d4af37]/30">
|
<div className="relative p-4 sm:p-5 bg-gradient-to-br from-[var(--luxury-gold)] to-[var(--luxury-gold-dark)] rounded-full shadow-lg shadow-[var(--luxury-gold)]/30">
|
||||||
<Hotel className="w-12 h-12 sm:w-16 sm:h-16 lg:w-20 lg:h-20 text-[#0f0f0f] relative z-10" />
|
<Hotel className="w-12 h-12 sm:w-16 sm:h-16 lg:w-20 lg:h-20 text-[#0f0f0f] relative z-10" />
|
||||||
<div className="absolute inset-0 bg-gradient-to-br from-[#f5d76e] to-[#d4af37] rounded-full blur-xl opacity-50"></div>
|
<div className="absolute inset-0 bg-gradient-to-br from-[var(--luxury-gold-light)] to-[var(--luxury-gold)] rounded-full blur-xl opacity-50"></div>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Tagline */}
|
{/* Tagline */}
|
||||||
{settings.company_tagline && (
|
{settings.company_tagline && (
|
||||||
<p className="text-xs sm:text-sm text-[#d4af37] uppercase tracking-[2px] sm:tracking-[3px] mb-3 sm:mb-4 font-light">
|
<p className="text-xs sm:text-sm text-[var(--luxury-gold)] uppercase tracking-[2px] sm:tracking-[3px] mb-3 sm:mb-4 font-light">
|
||||||
{settings.company_tagline}
|
{settings.company_tagline}
|
||||||
</p>
|
</p>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{/* 404 Number */}
|
{/* 404 Number */}
|
||||||
<div className="mb-6 sm:mb-8">
|
<div className="mb-6 sm:mb-8">
|
||||||
<h1 className="text-8xl sm:text-9xl lg:text-[12rem] font-display font-bold text-transparent bg-clip-text bg-gradient-to-r from-[#d4af37] via-[#f5d76e] to-[#d4af37] leading-none tracking-tight">
|
<h1 className="text-8xl sm:text-9xl lg:text-[12rem] font-display font-bold text-transparent bg-clip-text bg-gradient-to-r from-[var(--luxury-gold)] via-[var(--luxury-gold-light)] to-[var(--luxury-gold)] leading-none tracking-tight">
|
||||||
404
|
404
|
||||||
</h1>
|
</h1>
|
||||||
</div>
|
</div>
|
||||||
@@ -78,7 +78,7 @@ const NotFoundPage: React.FC = () => {
|
|||||||
<div className="flex flex-col sm:flex-row items-center justify-center gap-4">
|
<div className="flex flex-col sm:flex-row items-center justify-center gap-4">
|
||||||
<Link
|
<Link
|
||||||
to="/"
|
to="/"
|
||||||
className="inline-flex items-center justify-center gap-2 px-8 py-3.5 sm:px-10 sm:py-4 bg-gradient-to-r from-[#d4af37] to-[#c9a227] text-[#0f0f0f] font-semibold text-sm sm:text-base rounded-lg shadow-lg shadow-[#d4af37]/30 hover:from-[#f5d76e] hover:to-[#d4af37] hover:shadow-xl hover:shadow-[#d4af37]/40 transition-all duration-300 ease-out hover:-translate-y-0.5 active:translate-y-0 relative overflow-hidden group"
|
className="inline-flex items-center justify-center gap-2 px-8 py-3.5 sm:px-10 sm:py-4 bg-gradient-to-r from-[var(--luxury-gold)] to-[var(--luxury-gold-dark)] text-[#0f0f0f] font-semibold text-sm sm:text-base rounded-lg shadow-lg shadow-[var(--luxury-gold)]/30 hover:from-[var(--luxury-gold-light)] hover:to-[var(--luxury-gold)] hover:shadow-xl hover:shadow-[var(--luxury-gold)]/40 transition-all duration-300 ease-out hover:-translate-y-0.5 active:translate-y-0 relative overflow-hidden group"
|
||||||
>
|
>
|
||||||
<span className="absolute inset-0 bg-gradient-to-r from-white/0 via-white/20 to-white/0 translate-x-[-100%] group-hover:translate-x-[100%] transition-transform duration-700"></span>
|
<span className="absolute inset-0 bg-gradient-to-r from-white/0 via-white/20 to-white/0 translate-x-[-100%] group-hover:translate-x-[100%] transition-transform duration-700"></span>
|
||||||
<Home className="w-5 h-5 sm:w-6 sm:h-6 relative z-10" />
|
<Home className="w-5 h-5 sm:w-6 sm:h-6 relative z-10" />
|
||||||
@@ -88,9 +88,9 @@ const NotFoundPage: React.FC = () => {
|
|||||||
|
|
||||||
{/* Decorative Elements */}
|
{/* Decorative Elements */}
|
||||||
<div className="mt-12 sm:mt-16 flex justify-center gap-2">
|
<div className="mt-12 sm:mt-16 flex justify-center gap-2">
|
||||||
<div className="w-2 h-2 rounded-full bg-[#d4af37]/30"></div>
|
<div className="w-2 h-2 rounded-full bg-[var(--luxury-gold)]/30"></div>
|
||||||
<div className="w-2 h-2 rounded-full bg-[#d4af37]/50"></div>
|
<div className="w-2 h-2 rounded-full bg-[var(--luxury-gold)]/50"></div>
|
||||||
<div className="w-2 h-2 rounded-full bg-[#d4af37]/30"></div>
|
<div className="w-2 h-2 rounded-full bg-[var(--luxury-gold)]/30"></div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -37,11 +37,11 @@
|
|||||||
--shadow-luxury-gold: 0 8px 30px rgba(212, 175, 55, 0.25);
|
--shadow-luxury-gold: 0 8px 30px rgba(212, 175, 55, 0.25);
|
||||||
|
|
||||||
|
|
||||||
--gradient-gold: linear-gradient(135deg, #d4af37 0%, #f5d76e 100%);
|
--gradient-gold: linear-gradient(135deg, var(--luxury-gold) 0%, var(--luxury-gold-light) 100%);
|
||||||
--gradient-gold-dark: linear-gradient(135deg, #c9a227 0%, #d4af37 100%);
|
--gradient-gold-dark: linear-gradient(135deg, var(--luxury-gold-dark) 0%, var(--luxury-gold) 100%);
|
||||||
--gradient-dark: linear-gradient(180deg, #1a1a1a 0%, #0f0f0f 100%);
|
--gradient-dark: linear-gradient(180deg, #1a1a1a 0%, #0f0f0f 100%);
|
||||||
--gradient-overlay: linear-gradient(180deg, rgba(15, 15, 15, 0.8) 0%, rgba(15, 15, 15, 0.4) 100%);
|
--gradient-overlay: linear-gradient(180deg, rgba(15, 15, 15, 0.8) 0%, rgba(15, 15, 15, 0.4) 100%);
|
||||||
--gradient-gold-overlay: linear-gradient(135deg, rgba(212, 175, 55, 0.1) 0%, rgba(245, 215, 110, 0.05) 100%);
|
--gradient-gold-overlay: linear-gradient(135deg, var(--luxury-gold)1a 0%, var(--luxury-gold-light)0d 100%);
|
||||||
}
|
}
|
||||||
|
|
||||||
::-webkit-scrollbar {
|
::-webkit-scrollbar {
|
||||||
@@ -158,8 +158,12 @@ h3, h4, h5, h6 {
|
|||||||
.luxury-card {
|
.luxury-card {
|
||||||
@apply bg-white rounded-sm shadow-lg border border-gray-100;
|
@apply bg-white rounded-sm shadow-lg border border-gray-100;
|
||||||
@apply transition-all duration-300 ease-out;
|
@apply transition-all duration-300 ease-out;
|
||||||
@apply hover:shadow-xl hover:shadow-[#d4af37]/10;
|
|
||||||
@apply hover:-translate-y-1;
|
@apply hover:-translate-y-1;
|
||||||
|
box-shadow: var(--shadow-lg), 0 0 0 0 transparent;
|
||||||
|
}
|
||||||
|
|
||||||
|
.luxury-card:hover {
|
||||||
|
box-shadow: var(--shadow-xl), 0 0 20px rgba(var(--luxury-gold-rgb), 0.1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -167,35 +171,46 @@ h3, h4, h5, h6 {
|
|||||||
@apply bg-white rounded-sm shadow-lg;
|
@apply bg-white rounded-sm shadow-lg;
|
||||||
@apply relative overflow-hidden;
|
@apply relative overflow-hidden;
|
||||||
@apply transition-all duration-300 ease-out;
|
@apply transition-all duration-300 ease-out;
|
||||||
@apply hover:shadow-xl hover:shadow-[#d4af37]/20;
|
border-top: 2px solid var(--luxury-gold);
|
||||||
@apply border-t-2 border-[#d4af37];
|
}
|
||||||
|
|
||||||
|
.luxury-card-gold:hover {
|
||||||
|
box-shadow: var(--shadow-xl), 0 0 30px rgba(var(--luxury-gold-rgb), 0.2);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
.luxury-card-dark {
|
.luxury-card-dark {
|
||||||
@apply bg-gradient-to-b from-[#1a1a1a] to-[#0f0f0f] rounded-sm shadow-2xl;
|
@apply bg-gradient-to-b from-[#1a1a1a] to-[#0f0f0f] rounded-sm shadow-2xl;
|
||||||
@apply border border-[#d4af37]/20;
|
|
||||||
@apply transition-all duration-300 ease-out;
|
@apply transition-all duration-300 ease-out;
|
||||||
@apply hover:shadow-[#d4af37]/30 hover:border-[#d4af37]/40;
|
border: 1px solid rgba(var(--luxury-gold-rgb), 0.2);
|
||||||
|
}
|
||||||
|
|
||||||
|
.luxury-card-dark:hover {
|
||||||
|
box-shadow: 0 0 40px rgba(var(--luxury-gold-rgb), 0.3);
|
||||||
|
border-color: rgba(var(--luxury-gold-rgb), 0.4);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
.btn-luxury-primary {
|
.btn-luxury-primary {
|
||||||
@apply px-6 py-3 rounded-sm font-medium tracking-wide;
|
@apply px-6 py-3 rounded-sm font-medium tracking-wide;
|
||||||
@apply bg-gradient-to-r from-[#d4af37] to-[#c9a227] text-[#0f0f0f];
|
@apply text-[#0f0f0f];
|
||||||
@apply shadow-lg shadow-[#d4af37]/30;
|
|
||||||
@apply transition-all duration-300 ease-out;
|
@apply transition-all duration-300 ease-out;
|
||||||
@apply hover:from-[#f5d76e] hover:to-[#d4af37];
|
|
||||||
@apply hover:shadow-xl hover:shadow-[#d4af37]/40;
|
|
||||||
@apply hover:-translate-y-0.5;
|
@apply hover:-translate-y-0.5;
|
||||||
@apply active:translate-y-0;
|
@apply active:translate-y-0;
|
||||||
@apply disabled:opacity-50 disabled:cursor-not-allowed disabled:hover:translate-y-0;
|
@apply disabled:opacity-50 disabled:cursor-not-allowed disabled:hover:translate-y-0;
|
||||||
@apply relative overflow-hidden;
|
@apply relative overflow-hidden;
|
||||||
|
background: var(--gradient-gold-dark);
|
||||||
|
box-shadow: var(--shadow-lg), 0 0 30px rgba(var(--luxury-gold-rgb), 0.3);
|
||||||
font-family: 'Poppins', 'Inter', sans-serif;
|
font-family: 'Poppins', 'Inter', sans-serif;
|
||||||
font-weight: 500;
|
font-weight: 500;
|
||||||
letter-spacing: 0.05em;
|
letter-spacing: 0.05em;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.btn-luxury-primary:hover {
|
||||||
|
background: var(--gradient-gold);
|
||||||
|
box-shadow: var(--shadow-xl), 0 0 40px rgba(var(--luxury-gold-rgb), 0.4);
|
||||||
|
}
|
||||||
|
|
||||||
.btn-luxury-primary::before {
|
.btn-luxury-primary::before {
|
||||||
content: '';
|
content: '';
|
||||||
@apply absolute inset-0 bg-gradient-to-r from-white/0 via-white/20 to-white/0;
|
@apply absolute inset-0 bg-gradient-to-r from-white/0 via-white/20 to-white/0;
|
||||||
@@ -209,16 +224,23 @@ h3, h4, h5, h6 {
|
|||||||
|
|
||||||
.btn-luxury-secondary {
|
.btn-luxury-secondary {
|
||||||
@apply px-6 py-3 rounded-sm font-medium tracking-wide;
|
@apply px-6 py-3 rounded-sm font-medium tracking-wide;
|
||||||
@apply bg-white/10 backdrop-blur-sm text-white border border-[#d4af37]/30;
|
@apply bg-white/10 backdrop-blur-sm text-white;
|
||||||
@apply transition-all duration-300 ease-out;
|
@apply transition-all duration-300 ease-out;
|
||||||
@apply hover:bg-[#d4af37]/10 hover:border-[#d4af37] hover:text-[#d4af37];
|
|
||||||
@apply hover:-translate-y-0.5;
|
@apply hover:-translate-y-0.5;
|
||||||
@apply active:translate-y-0;
|
@apply active:translate-y-0;
|
||||||
|
border: 1px solid rgba(var(--luxury-gold-rgb), 0.3);
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-luxury-secondary:hover {
|
||||||
|
background-color: rgba(var(--luxury-gold-rgb), 0.1);
|
||||||
|
border-color: var(--luxury-gold);
|
||||||
|
color: var(--luxury-gold);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
.luxury-section-header {
|
.luxury-section-header {
|
||||||
@apply mb-8 pb-4 border-b border-[#d4af37]/20;
|
@apply mb-8 pb-4;
|
||||||
|
border-bottom: 1px solid rgba(var(--luxury-gold-rgb), 0.2);
|
||||||
}
|
}
|
||||||
|
|
||||||
.luxury-section-title {
|
.luxury-section-title {
|
||||||
@@ -254,7 +276,7 @@ h3, h4, h5, h6 {
|
|||||||
|
|
||||||
.luxury-table thead {
|
.luxury-table thead {
|
||||||
@apply bg-gradient-to-r from-gray-50 to-gray-100;
|
@apply bg-gradient-to-r from-gray-50 to-gray-100;
|
||||||
@apply border-b-2 border-[#d4af37]/30;
|
border-bottom: 2px solid rgba(var(--luxury-gold-rgb), 0.3);
|
||||||
}
|
}
|
||||||
|
|
||||||
.luxury-table th {
|
.luxury-table th {
|
||||||
@@ -270,7 +292,10 @@ h3, h4, h5, h6 {
|
|||||||
|
|
||||||
.luxury-table tbody tr {
|
.luxury-table tbody tr {
|
||||||
@apply transition-colors duration-150;
|
@apply transition-colors duration-150;
|
||||||
@apply hover:bg-[#d4af37]/5;
|
}
|
||||||
|
|
||||||
|
.luxury-table tbody tr:hover {
|
||||||
|
background-color: rgba(var(--luxury-gold-rgb), 0.05);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -278,13 +303,14 @@ h3, h4, h5, h6 {
|
|||||||
@apply inline-flex items-center px-3 py-1 rounded-sm;
|
@apply inline-flex items-center px-3 py-1 rounded-sm;
|
||||||
@apply text-xs font-medium tracking-wide;
|
@apply text-xs font-medium tracking-wide;
|
||||||
@apply transition-all duration-200;
|
@apply transition-all duration-200;
|
||||||
@apply bg-[#d4af37]/10 text-[#c9a227] border border-[#d4af37]/30;
|
color: var(--luxury-gold-dark);
|
||||||
|
background-color: rgba(var(--luxury-gold-rgb), 0.1);
|
||||||
|
border: 1px solid rgba(var(--luxury-gold-rgb), 0.3);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
.luxury-input {
|
.luxury-input {
|
||||||
@apply w-full px-4 py-3 rounded-sm border border-gray-300;
|
@apply w-full px-4 py-3 rounded-sm border border-gray-300;
|
||||||
@apply focus:ring-2 focus:ring-[#d4af37]/50 focus:border-[#d4af37];
|
|
||||||
@apply transition-all duration-200;
|
@apply transition-all duration-200;
|
||||||
@apply bg-white text-gray-900;
|
@apply bg-white text-gray-900;
|
||||||
@apply placeholder:text-gray-400;
|
@apply placeholder:text-gray-400;
|
||||||
@@ -293,6 +319,13 @@ h3, h4, h5, h6 {
|
|||||||
letter-spacing: 0.01em;
|
letter-spacing: 0.01em;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.luxury-input:focus {
|
||||||
|
outline: none;
|
||||||
|
ring: 2px;
|
||||||
|
ring-color: rgba(var(--luxury-gold-rgb), 0.5);
|
||||||
|
border-color: var(--luxury-gold);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
.luxury-gradient-bg {
|
.luxury-gradient-bg {
|
||||||
background: var(--gradient-gold);
|
background: var(--gradient-gold);
|
||||||
@@ -301,8 +334,8 @@ h3, h4, h5, h6 {
|
|||||||
|
|
||||||
.luxury-glass {
|
.luxury-glass {
|
||||||
@apply bg-white/90 backdrop-blur-xl;
|
@apply bg-white/90 backdrop-blur-xl;
|
||||||
@apply border border-[#d4af37]/20;
|
|
||||||
@apply shadow-2xl;
|
@apply shadow-2xl;
|
||||||
|
border: 1px solid rgba(var(--luxury-gold-rgb), 0.2);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -310,15 +343,18 @@ h3, h4, h5, h6 {
|
|||||||
@apply bg-white rounded-lg shadow-xl border border-gray-200/60;
|
@apply bg-white rounded-lg shadow-xl border border-gray-200/60;
|
||||||
@apply relative overflow-hidden;
|
@apply relative overflow-hidden;
|
||||||
@apply transition-all duration-300 ease-out;
|
@apply transition-all duration-300 ease-out;
|
||||||
@apply hover:shadow-2xl hover:shadow-[#d4af37]/10;
|
|
||||||
@apply hover:-translate-y-0.5;
|
@apply hover:-translate-y-0.5;
|
||||||
@apply backdrop-blur-sm;
|
@apply backdrop-blur-sm;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.enterprise-card:hover {
|
||||||
|
box-shadow: var(--shadow-2xl), 0 0 30px rgba(var(--luxury-gold-rgb), 0.1);
|
||||||
|
}
|
||||||
|
|
||||||
.enterprise-card::before {
|
.enterprise-card::before {
|
||||||
content: '';
|
content: '';
|
||||||
@apply absolute top-0 left-0 right-0 h-0.5;
|
@apply absolute top-0 left-0 right-0 h-0.5;
|
||||||
background: linear-gradient(90deg, transparent 0%, #d4af37 50%, transparent 100%);
|
background: linear-gradient(90deg, transparent 0%, var(--luxury-gold) 50%, transparent 100%);
|
||||||
opacity: 0;
|
opacity: 0;
|
||||||
transition: opacity 0.3s ease-out;
|
transition: opacity 0.3s ease-out;
|
||||||
}
|
}
|
||||||
@@ -344,15 +380,19 @@ h3, h4, h5, h6 {
|
|||||||
|
|
||||||
.btn-enterprise-primary {
|
.btn-enterprise-primary {
|
||||||
@apply px-8 py-3.5 rounded-lg font-semibold tracking-wide text-sm;
|
@apply px-8 py-3.5 rounded-lg font-semibold tracking-wide text-sm;
|
||||||
@apply bg-gradient-to-r from-[#d4af37] via-[#e8c547] to-[#d4af37];
|
@apply text-[#0f0f0f];
|
||||||
@apply text-[#0f0f0f] shadow-lg shadow-[#d4af37]/30;
|
|
||||||
@apply transition-all duration-300 ease-out;
|
@apply transition-all duration-300 ease-out;
|
||||||
@apply hover:from-[#f5d76e] hover:via-[#d4af37] hover:to-[#f5d76e];
|
|
||||||
@apply hover:shadow-xl hover:shadow-[#d4af37]/40;
|
|
||||||
@apply hover:-translate-y-0.5 hover:scale-[1.02];
|
@apply hover:-translate-y-0.5 hover:scale-[1.02];
|
||||||
@apply active:translate-y-0 active:scale-100;
|
@apply active:translate-y-0 active:scale-100;
|
||||||
@apply disabled:opacity-50 disabled:cursor-not-allowed disabled:hover:translate-y-0 disabled:hover:scale-100;
|
@apply disabled:opacity-50 disabled:cursor-not-allowed disabled:hover:translate-y-0 disabled:hover:scale-100;
|
||||||
@apply relative overflow-hidden;
|
@apply relative overflow-hidden;
|
||||||
|
background: linear-gradient(to right, var(--luxury-gold), var(--luxury-gold-accent), var(--luxury-gold));
|
||||||
|
box-shadow: var(--shadow-lg), 0 0 30px rgba(var(--luxury-gold-rgb), 0.3);
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-enterprise-primary:hover {
|
||||||
|
background: linear-gradient(to right, var(--luxury-gold-light), var(--luxury-gold), var(--luxury-gold-light));
|
||||||
|
box-shadow: var(--shadow-xl), 0 0 40px rgba(var(--luxury-gold-rgb), 0.4);
|
||||||
}
|
}
|
||||||
|
|
||||||
.btn-enterprise-primary::before {
|
.btn-enterprise-primary::before {
|
||||||
@@ -371,17 +411,21 @@ h3, h4, h5, h6 {
|
|||||||
@apply bg-white border-2 border-gray-300 text-gray-700;
|
@apply bg-white border-2 border-gray-300 text-gray-700;
|
||||||
@apply shadow-md shadow-gray-200/50;
|
@apply shadow-md shadow-gray-200/50;
|
||||||
@apply transition-all duration-300 ease-out;
|
@apply transition-all duration-300 ease-out;
|
||||||
@apply hover:bg-gray-50 hover:border-[#d4af37] hover:text-[#c9a227];
|
|
||||||
@apply hover:shadow-lg hover:shadow-[#d4af37]/20;
|
|
||||||
@apply hover:-translate-y-0.5;
|
@apply hover:-translate-y-0.5;
|
||||||
@apply active:translate-y-0;
|
@apply active:translate-y-0;
|
||||||
@apply disabled:opacity-50 disabled:cursor-not-allowed;
|
@apply disabled:opacity-50 disabled:cursor-not-allowed;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.btn-enterprise-secondary:hover {
|
||||||
|
@apply hover:bg-gray-50;
|
||||||
|
border-color: var(--luxury-gold);
|
||||||
|
color: var(--luxury-gold-dark);
|
||||||
|
box-shadow: var(--shadow-lg), 0 0 20px rgba(var(--luxury-gold-rgb), 0.2);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
.enterprise-input {
|
.enterprise-input {
|
||||||
@apply w-full px-4 py-3 rounded-lg border-2 border-gray-200;
|
@apply w-full px-4 py-3 rounded-lg border-2 border-gray-200;
|
||||||
@apply focus:ring-2 focus:ring-[#d4af37]/30 focus:border-[#d4af37];
|
|
||||||
@apply transition-all duration-200;
|
@apply transition-all duration-200;
|
||||||
@apply bg-white text-gray-900;
|
@apply bg-white text-gray-900;
|
||||||
@apply placeholder:text-gray-400;
|
@apply placeholder:text-gray-400;
|
||||||
@@ -390,7 +434,11 @@ h3, h4, h5, h6 {
|
|||||||
}
|
}
|
||||||
|
|
||||||
.enterprise-input:focus {
|
.enterprise-input:focus {
|
||||||
@apply shadow-md shadow-[#d4af37]/10;
|
outline: none;
|
||||||
|
ring: 2px;
|
||||||
|
ring-color: rgba(var(--luxury-gold-rgb), 0.3);
|
||||||
|
border-color: var(--luxury-gold);
|
||||||
|
box-shadow: var(--shadow-md), 0 0 20px rgba(var(--luxury-gold-rgb), 0.1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -399,6 +447,46 @@ h3, h4, h5, h6 {
|
|||||||
text-wrap: balance;
|
text-wrap: balance;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Theme color utilities with opacity support */
|
||||||
|
.bg-luxury-gold {
|
||||||
|
background-color: var(--luxury-gold);
|
||||||
|
}
|
||||||
|
|
||||||
|
.bg-luxury-gold\/10 {
|
||||||
|
background-color: rgba(var(--luxury-gold-rgb), 0.1);
|
||||||
|
}
|
||||||
|
|
||||||
|
.bg-luxury-gold\/20 {
|
||||||
|
background-color: rgba(var(--luxury-gold-rgb), 0.2);
|
||||||
|
}
|
||||||
|
|
||||||
|
.text-luxury-gold {
|
||||||
|
color: var(--luxury-gold);
|
||||||
|
}
|
||||||
|
|
||||||
|
.text-luxury-gold\/70 {
|
||||||
|
color: rgba(var(--luxury-gold-rgb), 0.7);
|
||||||
|
}
|
||||||
|
|
||||||
|
.border-luxury-gold {
|
||||||
|
border-color: var(--luxury-gold);
|
||||||
|
}
|
||||||
|
|
||||||
|
.border-luxury-gold\/10 {
|
||||||
|
border-color: rgba(var(--luxury-gold-rgb), 0.1);
|
||||||
|
}
|
||||||
|
|
||||||
|
.border-luxury-gold\/20 {
|
||||||
|
border-color: rgba(var(--luxury-gold-rgb), 0.2);
|
||||||
|
}
|
||||||
|
|
||||||
|
.border-luxury-gold\/30 {
|
||||||
|
border-color: rgba(var(--luxury-gold-rgb), 0.3);
|
||||||
|
}
|
||||||
|
|
||||||
|
.border-luxury-gold\/40 {
|
||||||
|
border-color: rgba(var(--luxury-gold-rgb), 0.4);
|
||||||
|
}
|
||||||
|
|
||||||
.shadow-luxury {
|
.shadow-luxury {
|
||||||
box-shadow: var(--shadow-luxury);
|
box-shadow: var(--shadow-luxury);
|
||||||
@@ -409,7 +497,7 @@ h3, h4, h5, h6 {
|
|||||||
}
|
}
|
||||||
|
|
||||||
.shadow-luxury-gold {
|
.shadow-luxury-gold {
|
||||||
box-shadow: 0 8px 30px rgba(212, 175, 55, 0.25);
|
box-shadow: var(--shadow-luxury-gold);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -523,8 +611,11 @@ h3, h4, h5, h6 {
|
|||||||
|
|
||||||
|
|
||||||
.text-gradient-luxury {
|
.text-gradient-luxury {
|
||||||
@apply bg-gradient-to-r from-[#d4af37] via-[#f5d76e] to-[#d4af37];
|
background: linear-gradient(to right, var(--luxury-gold), var(--luxury-gold-light), var(--luxury-gold));
|
||||||
@apply bg-clip-text text-transparent;
|
-webkit-background-clip: text;
|
||||||
|
background-clip: text;
|
||||||
|
-webkit-text-fill-color: transparent;
|
||||||
|
color: transparent;
|
||||||
}
|
}
|
||||||
|
|
||||||
.text-gradient-luxury-dark {
|
.text-gradient-luxury-dark {
|
||||||
@@ -581,11 +672,11 @@ img[loading="lazy"]:not([src]) {
|
|||||||
|
|
||||||
.prose.prose-invert strong,
|
.prose.prose-invert strong,
|
||||||
.prose.prose-invert b {
|
.prose.prose-invert b {
|
||||||
color: #d4af37 !important;
|
color: var(--luxury-gold) !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
.prose.prose-invert a {
|
.prose.prose-invert a {
|
||||||
color: #d4af37 !important;
|
color: var(--luxury-gold) !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Custom luxury scrollbar for modals */
|
/* Custom luxury scrollbar for modals */
|
||||||
@@ -599,11 +690,11 @@ img[loading="lazy"]:not([src]) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
.custom-scrollbar::-webkit-scrollbar-thumb {
|
.custom-scrollbar::-webkit-scrollbar-thumb {
|
||||||
background: linear-gradient(180deg, #d4af37 0%, #c9a227 100%);
|
background: linear-gradient(180deg, var(--luxury-gold) 0%, var(--luxury-gold-dark) 100%);
|
||||||
border-radius: 10px;
|
border-radius: 10px;
|
||||||
border: 2px solid #f3f4f6;
|
border: 2px solid #f3f4f6;
|
||||||
}
|
}
|
||||||
|
|
||||||
.custom-scrollbar::-webkit-scrollbar-thumb:hover {
|
.custom-scrollbar::-webkit-scrollbar-thumb:hover {
|
||||||
background: linear-gradient(180deg, #f5d76e 0%, #d4af37 100%);
|
background: linear-gradient(180deg, var(--luxury-gold-light) 0%, var(--luxury-gold) 100%);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -15,9 +15,10 @@ export default {
|
|||||||
},
|
},
|
||||||
colors: {
|
colors: {
|
||||||
luxury: {
|
luxury: {
|
||||||
gold: '#d4af37',
|
gold: 'var(--luxury-gold)',
|
||||||
'gold-light': '#f5d76e',
|
'gold-light': 'var(--luxury-gold-light)',
|
||||||
'gold-dark': '#c9a227',
|
'gold-dark': 'var(--luxury-gold-dark)',
|
||||||
|
'gold-accent': 'var(--luxury-gold-accent)',
|
||||||
black: '#0f0f0f',
|
black: '#0f0f0f',
|
||||||
'black-light': '#1a1a1a',
|
'black-light': '#1a1a1a',
|
||||||
},
|
},
|
||||||
|
|||||||
Reference in New Issue
Block a user