This commit is contained in:
Iliyan Angelov
2025-12-07 20:36:17 +02:00
parent 876af48145
commit b818d645a9
91 changed files with 3692 additions and 4501 deletions

View File

@@ -379,7 +379,7 @@ const HousekeepingProfilePage: React.FC = () => {
{/* Header */}
<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="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">
Housekeeping Profile
</h1>
@@ -390,50 +390,50 @@ const HousekeepingProfilePage: React.FC = () => {
</div>
{/* 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">
<button
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 ${
activeTab === 'profile'
? 'border-[#d4af37] text-[#d4af37] shadow-sm'
: 'border-transparent text-gray-500 hover:text-[#d4af37] hover:border-[#d4af37]/30'
? 'border-[var(--luxury-gold)] text-[var(--luxury-gold)] shadow-sm'
: '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
</button>
<button
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 ${
activeTab === 'password'
? 'border-[#d4af37] text-[#d4af37] shadow-sm'
: 'border-transparent text-gray-500 hover:text-[#d4af37] hover:border-[#d4af37]/30'
? 'border-[var(--luxury-gold)] text-[var(--luxury-gold)] shadow-sm'
: '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
</button>
<button
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 ${
activeTab === 'mfa'
? 'border-[#d4af37] text-[#d4af37] shadow-sm'
: 'border-transparent text-gray-500 hover:text-[#d4af37] hover:border-[#d4af37]/30'
? 'border-[var(--luxury-gold)] text-[var(--luxury-gold)] shadow-sm'
: '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
</button>
<button
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 ${
activeTab === 'sessions'
? 'border-[#d4af37] text-[#d4af37] shadow-sm'
: 'border-transparent text-gray-500 hover:text-[#d4af37] hover:border-[#d4af37]/30'
? 'border-[var(--luxury-gold)] text-[var(--luxury-gold)] shadow-sm'
: '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
</button>
</div>
@@ -441,7 +441,7 @@ const HousekeepingProfilePage: React.FC = () => {
{/* Profile Tab */}
{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">
{/* 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">
@@ -450,17 +450,17 @@ const HousekeepingProfilePage: React.FC = () => {
<img
src={avatarPreview || normalizeImageUrl(userInfo?.avatar)}
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)}
/>
) : (
<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" />
</div>
)}
<label
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" />
<input
@@ -477,7 +477,7 @@ const HousekeepingProfilePage: React.FC = () => {
{/* Name Field */}
<div>
<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>
</label>
<input
@@ -499,7 +499,7 @@ const HousekeepingProfilePage: React.FC = () => {
{/* Email Field */}
<div>
<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>
</label>
<input
@@ -521,7 +521,7 @@ const HousekeepingProfilePage: React.FC = () => {
{/* Phone Field */}
<div>
<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>
</label>
<input
@@ -556,7 +556,7 @@ const HousekeepingProfilePage: React.FC = () => {
{/* Password Tab */}
{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">
{/* 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">
@@ -579,7 +579,7 @@ const HousekeepingProfilePage: React.FC = () => {
{/* Current Password */}
<div>
<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>
</label>
<div className="relative">
@@ -594,7 +594,7 @@ const HousekeepingProfilePage: React.FC = () => {
<button
type="button"
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" />}
</button>
@@ -610,7 +610,7 @@ const HousekeepingProfilePage: React.FC = () => {
{/* New Password */}
<div>
<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>
</label>
<div className="relative">
@@ -625,7 +625,7 @@ const HousekeepingProfilePage: React.FC = () => {
<button
type="button"
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" />}
</button>
@@ -641,7 +641,7 @@ const HousekeepingProfilePage: React.FC = () => {
{/* Confirm Password */}
<div>
<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>
</label>
<div className="relative">
@@ -656,7 +656,7 @@ const HousekeepingProfilePage: React.FC = () => {
<button
type="button"
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" />}
</button>
@@ -742,10 +742,10 @@ const MFATab: React.FC<MFATabProps> = ({
setLoading,
}) => {
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>
<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
</h2>
<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">
<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
</h3>
<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) => {
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('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) => {
@@ -1053,17 +1053,17 @@ const SessionsTab: React.FC = () => {
if (loading) {
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..." />
</div>
);
}
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>
<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
</h2>
<p className="text-xs sm:text-sm text-gray-600 font-light">
@@ -1092,11 +1092,11 @@ const SessionsTab: React.FC = () => {
{sessions.map((session) => (
<div
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 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)}
</div>
<div className="flex-1 min-w-0">