377 lines
23 KiB
JavaScript
377 lines
23 KiB
JavaScript
import React, { useState } from 'react';
|
|
import { useForm, Head, Link } from '@inertiajs/react';
|
|
import {
|
|
CheckCircle,
|
|
Camera,
|
|
Eye,
|
|
EyeOff,
|
|
X,
|
|
Loader2,
|
|
FileText,
|
|
AlertCircle,
|
|
CreditCard,
|
|
ShieldCheck,
|
|
Lock,
|
|
User,
|
|
Mail,
|
|
Phone as PhoneIcon,
|
|
ArrowRight,
|
|
ArrowLeft,
|
|
Check,
|
|
Globe,
|
|
ChevronLeft,
|
|
ChevronRight,
|
|
Zap,
|
|
Shield,
|
|
Trophy
|
|
} from 'lucide-react';
|
|
|
|
export default function EmployerRegister() {
|
|
const [step, setStep] = useState(1);
|
|
const { data, setData, post, processing, errors } = useForm({
|
|
name: '',
|
|
email: '',
|
|
phone: '',
|
|
password: '',
|
|
password_confirmation: '',
|
|
emirates_id_front: null,
|
|
emirates_id_back: null,
|
|
selected_plan: 'premium',
|
|
card_number: '',
|
|
expiry: '',
|
|
cvc: '',
|
|
source: 'mobile',
|
|
});
|
|
|
|
const [showPassword, setShowPassword] = useState(false);
|
|
const [frontFileName, setFrontFileName] = useState(null);
|
|
const [backFileName, setBackFileName] = useState(null);
|
|
|
|
const plans = [
|
|
{ id: 'basic', name: 'Basic Search', price: '99', icon: Zap, color: 'text-blue-500 bg-blue-50', features: ['Browse 500+ workers', '10 Shortlists'] },
|
|
{ id: 'premium', name: 'Premium Pass', price: '199', icon: Shield, color: 'text-[#185FA5] bg-blue-50', popular: true, features: ['Unlimited lists', 'Direct Messaging'] },
|
|
{ id: 'vip', name: 'VIP Concierge', price: '499', icon: Trophy, color: 'text-amber-500 bg-amber-50', features: ['Assigned Manager', 'Full Warranty'] },
|
|
];
|
|
|
|
const nextStep = () => setStep(prev => prev + 1);
|
|
const prevStep = () => setStep(prev => prev - 1);
|
|
|
|
const handleFileChange = (e, field) => {
|
|
const file = e.target.files[0];
|
|
if (file) {
|
|
setData(field, file);
|
|
const sizeMb = (file.size / (1024 * 1024)).toFixed(1);
|
|
if (field === 'emirates_id_front') {
|
|
setFrontFileName(`${file.name} (${sizeMb}MB)`);
|
|
} else {
|
|
setBackFileName(`${file.name} (${sizeMb}MB)`);
|
|
}
|
|
}
|
|
};
|
|
|
|
const removeFile = (field) => {
|
|
setData(field, null);
|
|
if (field === 'emirates_id_front') {
|
|
setFrontFileName(null);
|
|
} else {
|
|
setBackFileName(null);
|
|
}
|
|
};
|
|
|
|
const handleSubmit = (e) => {
|
|
e.preventDefault();
|
|
post('/employer/register');
|
|
};
|
|
|
|
return (
|
|
<div className="min-h-screen bg-white font-sans text-slate-900 flex flex-col max-w-md mx-auto relative overflow-hidden">
|
|
<Head title="Enrollment" />
|
|
|
|
{/* Minimal Header */}
|
|
<div className="px-6 pt-12 pb-4 flex items-center justify-between sticky top-0 bg-white/80 backdrop-blur-xl z-50">
|
|
<button onClick={() => step > 1 ? prevStep() : window.history.back()} className="p-3 bg-slate-50 rounded-2xl text-slate-400 border border-slate-100 active:scale-95 transition-all">
|
|
<ChevronLeft className="w-6 h-6" />
|
|
</button>
|
|
<div className="flex items-center space-x-1.5 px-4 py-2 bg-slate-50 rounded-full border border-slate-100">
|
|
<div className="w-1.5 h-1.5 bg-[#185FA5] rounded-full animate-pulse" />
|
|
<span className="text-[10px] font-bold uppercase tracking-widest text-slate-400">Step {step} / 4</span>
|
|
</div>
|
|
</div>
|
|
|
|
<div className="flex-1 px-8 pt-8 space-y-8 overflow-y-auto no-scrollbar pb-32">
|
|
{/* Header Text */}
|
|
<div className="space-y-2">
|
|
<h1 className="text-3xl font-black tracking-tight text-slate-900 leading-none">
|
|
{step === 1 && "Create Account"}
|
|
{step === 2 && "Identity Vetting"}
|
|
{step === 3 && "Choose Plan"}
|
|
{step === 4 && "Checkout"}
|
|
</h1>
|
|
<p className="text-sm font-medium text-slate-400">
|
|
{step === 1 && "Start your professional hiring journey."}
|
|
{step === 2 && "Verified ID is mandatory for compliance."}
|
|
{step === 3 && "Select a package that fits your needs."}
|
|
{step === 4 && "Secure payment via encrypted gateway."}
|
|
</p>
|
|
</div>
|
|
|
|
<form onSubmit={handleSubmit} className="space-y-6">
|
|
{/* Step 1: Account Info */}
|
|
{step === 1 && (
|
|
<div className="space-y-5 animate-in fade-in slide-in-from-bottom-4 duration-500">
|
|
<div className="space-y-2">
|
|
<label className="text-[11px] font-bold text-slate-400 uppercase tracking-widest ml-2">Full Name</label>
|
|
<input
|
|
type="text"
|
|
value={data.name}
|
|
onChange={(e) => setData('name', e.target.value)}
|
|
placeholder="Enter your full name"
|
|
className="w-full px-6 py-4 bg-slate-50 border border-slate-100 rounded-[24px] text-sm font-medium focus:bg-white focus:border-[#185FA5] focus:ring-4 focus:ring-blue-500/10 transition-all outline-none"
|
|
required
|
|
/>
|
|
</div>
|
|
<div className="space-y-2">
|
|
<label className="text-[11px] font-bold text-slate-400 uppercase tracking-widest ml-2">Email Address</label>
|
|
<input
|
|
type="email"
|
|
value={data.email}
|
|
onChange={(e) => setData('email', e.target.value)}
|
|
placeholder="your@email.com"
|
|
className="w-full px-6 py-4 bg-slate-50 border border-slate-100 rounded-[24px] text-sm font-medium focus:bg-white focus:border-[#185FA5] focus:ring-4 focus:ring-blue-500/10 transition-all outline-none"
|
|
required
|
|
/>
|
|
</div>
|
|
<div className="space-y-2">
|
|
<label className="text-[11px] font-bold text-slate-400 uppercase tracking-widest ml-2">Phone Number</label>
|
|
<input
|
|
type="text"
|
|
value={data.phone}
|
|
onChange={(e) => setData('phone', e.target.value)}
|
|
placeholder="+971 50 000 0000"
|
|
className="w-full px-6 py-4 bg-slate-50 border border-slate-100 rounded-[24px] text-sm font-medium focus:bg-white focus:border-[#185FA5] focus:ring-4 focus:ring-blue-500/10 transition-all outline-none"
|
|
required
|
|
/>
|
|
</div>
|
|
<div className="space-y-2">
|
|
<label className="text-[11px] font-bold text-slate-400 uppercase tracking-widest ml-2">Password</label>
|
|
<div className="relative">
|
|
<input
|
|
type={showPassword ? 'text' : 'password'}
|
|
value={data.password}
|
|
onChange={(e) => setData('password', e.target.value)}
|
|
placeholder="Min. 8 characters"
|
|
className="w-full px-6 py-4 bg-slate-50 border border-slate-100 rounded-[24px] text-sm font-medium focus:bg-white focus:border-[#185FA5] focus:ring-4 focus:ring-blue-500/10 transition-all outline-none"
|
|
required
|
|
/>
|
|
<button
|
|
type="button"
|
|
onClick={() => setShowPassword(!showPassword)}
|
|
className="absolute right-6 top-1/2 -translate-y-1/2 text-slate-300 hover:text-slate-500"
|
|
>
|
|
{showPassword ? <EyeOff className="w-5 h-5" /> : <Eye className="w-5 h-5" />}
|
|
</button>
|
|
</div>
|
|
</div>
|
|
<button
|
|
type="button"
|
|
onClick={nextStep}
|
|
className="w-full py-5 bg-[#185FA5] text-white rounded-[24px] font-bold text-sm uppercase tracking-widest shadow-xl shadow-blue-500/20 flex items-center justify-center space-x-3 active:scale-[0.98] transition-all mt-4"
|
|
>
|
|
<span>Continue</span>
|
|
<ArrowRight className="w-5 h-5" />
|
|
</button>
|
|
</div>
|
|
)}
|
|
|
|
{/* Step 2: Emirates ID */}
|
|
{step === 2 && (
|
|
<div className="space-y-6 animate-in fade-in slide-in-from-bottom-4 duration-500">
|
|
<div className="bg-emerald-50 p-6 rounded-[32px] flex items-center space-x-4 border border-emerald-100">
|
|
<ShieldCheck className="w-8 h-8 text-emerald-500 shrink-0" />
|
|
<p className="text-[11px] font-bold text-emerald-800 leading-relaxed uppercase tracking-wider">
|
|
Your ID is encrypted and handled securely as per UAE data protection laws.
|
|
</p>
|
|
</div>
|
|
|
|
<div className="space-y-4">
|
|
{[
|
|
{ field: 'emirates_id_front', label: 'EID Front Side', file: frontFileName },
|
|
{ field: 'emirates_id_back', label: 'EID Back Side', file: backFileName }
|
|
].map((slot) => (
|
|
<div key={slot.field} className="space-y-2">
|
|
<label className="text-[11px] font-bold text-slate-400 uppercase tracking-widest ml-2">{slot.label}</label>
|
|
{slot.file ? (
|
|
<div className="relative h-40 bg-slate-50 rounded-[32px] border-2 border-[#185FA5] flex flex-col items-center justify-center p-6 shadow-xl shadow-blue-500/5">
|
|
<FileText className="w-10 h-10 text-[#185FA5] mb-2" />
|
|
<span className="text-[10px] font-bold text-slate-900 uppercase truncate max-w-full px-4">{slot.file}</span>
|
|
<button
|
|
type="button"
|
|
onClick={() => removeFile(slot.field)}
|
|
className="absolute -top-2 -right-2 bg-rose-500 text-white p-3 rounded-full shadow-lg active:scale-90 transition-transform"
|
|
>
|
|
<X className="w-4 h-4" strokeWidth={3} />
|
|
</button>
|
|
</div>
|
|
) : (
|
|
<label className="relative h-40 bg-slate-50 rounded-[32px] border-2 border-dashed border-slate-200 flex flex-col items-center justify-center p-8 cursor-pointer hover:border-[#185FA5] transition-all group">
|
|
<div className="w-14 h-14 bg-white rounded-2xl flex items-center justify-center shadow-sm mb-3 group-hover:scale-110 transition-transform">
|
|
<Camera className="w-6 h-6 text-slate-300 group-hover:text-[#185FA5]" />
|
|
</div>
|
|
<span className="text-[10px] font-bold text-slate-400 uppercase tracking-widest group-hover:text-[#185FA5]">Tap to Upload</span>
|
|
<input
|
|
type="file"
|
|
className="hidden"
|
|
onChange={(e) => handleFileChange(e, slot.field)}
|
|
/>
|
|
</label>
|
|
)}
|
|
</div>
|
|
))}
|
|
</div>
|
|
|
|
<button
|
|
type="button"
|
|
onClick={nextStep}
|
|
disabled={!frontFileName || !backFileName}
|
|
className="w-full py-5 bg-[#185FA5] text-white rounded-[24px] font-bold text-sm uppercase tracking-widest shadow-xl shadow-blue-500/20 flex items-center justify-center space-x-3 active:scale-[0.98] transition-all disabled:opacity-50 mt-4"
|
|
>
|
|
<span>Verify Identity</span>
|
|
<ShieldCheck className="w-5 h-5" />
|
|
</button>
|
|
</div>
|
|
)}
|
|
|
|
{/* Step 3: Plans */}
|
|
{step === 3 && (
|
|
<div className="space-y-4 animate-in fade-in slide-in-from-bottom-4 duration-500 pb-10">
|
|
{plans.map((p) => (
|
|
<button
|
|
key={p.id}
|
|
type="button"
|
|
onClick={() => setData('selected_plan', p.id)}
|
|
className={`w-full p-6 rounded-[32px] border-2 text-left transition-all relative overflow-hidden flex items-center justify-between ${
|
|
data.selected_plan === p.id
|
|
? 'bg-blue-50/50 border-[#185FA5] shadow-lg shadow-blue-500/5'
|
|
: 'bg-white border-slate-50 hover:border-slate-100'
|
|
}`}
|
|
>
|
|
<div className="flex items-center space-x-4">
|
|
<div className={`w-12 h-12 rounded-2xl flex items-center justify-center ${p.color}`}>
|
|
<p.icon className="w-6 h-6" />
|
|
</div>
|
|
<div className="space-y-0.5">
|
|
<div className="flex items-center space-x-2">
|
|
<h4 className="text-sm font-bold text-slate-900 uppercase tracking-tight">{p.name}</h4>
|
|
{p.popular && <span className="bg-emerald-500 text-white px-2 py-0.5 rounded-full text-[8px] font-bold uppercase tracking-widest">Best</span>}
|
|
</div>
|
|
<div className="text-[10px] font-medium text-slate-400">{p.features[0]}</div>
|
|
</div>
|
|
</div>
|
|
<div className="text-right">
|
|
<div className="text-xl font-black text-[#185FA5]">{p.price} <span className="text-[10px] font-bold">AED</span></div>
|
|
</div>
|
|
</button>
|
|
))}
|
|
<button
|
|
type="button"
|
|
onClick={nextStep}
|
|
className="w-full py-5 bg-[#185FA5] text-white rounded-[24px] font-bold text-sm uppercase tracking-widest shadow-xl shadow-blue-500/20 flex items-center justify-center space-x-3 active:scale-[0.98] transition-all mt-4"
|
|
>
|
|
<span>Continue to Payment</span>
|
|
<ArrowRight className="w-5 h-5" />
|
|
</button>
|
|
</div>
|
|
)}
|
|
|
|
{/* Step 4: Payment */}
|
|
{step === 4 && (
|
|
<div className="space-y-6 animate-in fade-in slide-in-from-bottom-4 duration-500">
|
|
{/* Card Visual */}
|
|
<div className="bg-[#141B34] rounded-[32px] p-8 text-white relative overflow-hidden shadow-2xl">
|
|
<div className="absolute top-0 right-0 w-32 h-32 bg-[#185FA5]/20 rounded-full blur-3xl -mr-16 -mt-16" />
|
|
<div className="flex justify-between items-start mb-10 relative z-10">
|
|
<div className="w-12 h-8 bg-amber-400 rounded-lg" />
|
|
<CreditCard className="w-8 h-8 text-white/20" />
|
|
</div>
|
|
<div className="space-y-4 relative z-10">
|
|
<div className="text-lg font-bold tracking-[0.2em] h-8 flex items-center">
|
|
{data.card_number || '•••• •••• •••• ••••'}
|
|
</div>
|
|
<div className="flex justify-between border-t border-white/10 pt-4">
|
|
<div className="space-y-0.5 text-left">
|
|
<div className="text-[9px] font-bold uppercase opacity-40 tracking-widest">Card Holder</div>
|
|
<div className="text-[11px] font-bold uppercase tracking-widest truncate max-w-[150px]">{data.name || 'Your Name'}</div>
|
|
</div>
|
|
<div className="text-right space-y-0.5">
|
|
<div className="text-[9px] font-bold uppercase opacity-40 tracking-widest">Expiry</div>
|
|
<div className="text-[11px] font-bold uppercase tracking-widest">{data.expiry || 'MM/YY'}</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div className="space-y-4">
|
|
<input
|
|
type="text"
|
|
value={data.card_number}
|
|
onChange={(e) => setData('card_number', e.target.value.replace(/\W/gi, '').replace(/(.{4})/g, '$1 ').trim())}
|
|
placeholder="Card Number"
|
|
className="w-full px-6 py-4 bg-slate-50 border border-slate-100 rounded-[24px] text-sm font-medium focus:bg-white focus:border-[#185FA5] transition-all outline-none"
|
|
required
|
|
/>
|
|
<div className="grid grid-cols-2 gap-4">
|
|
<input
|
|
type="text"
|
|
value={data.expiry}
|
|
onChange={(e) => setData('expiry', e.target.value.replace(/^(\d{2})/, '$1/'))}
|
|
placeholder="MM/YY"
|
|
className="w-full px-6 py-4 bg-slate-50 border border-slate-100 rounded-[24px] text-sm font-medium focus:bg-white focus:border-[#185FA5] transition-all outline-none text-center"
|
|
required
|
|
/>
|
|
<input
|
|
type="password"
|
|
value={data.cvc}
|
|
onChange={(e) => setData('cvc', e.target.value)}
|
|
placeholder="CVC"
|
|
className="w-full px-6 py-4 bg-slate-50 border border-slate-100 rounded-[24px] text-sm font-medium focus:bg-white focus:border-[#185FA5] transition-all outline-none text-center"
|
|
required
|
|
/>
|
|
</div>
|
|
</div>
|
|
|
|
<div className="p-6 bg-slate-50 rounded-[32px] flex items-center justify-between border border-slate-100">
|
|
<div className="space-y-0.5">
|
|
<div className="text-[10px] font-bold text-slate-400 uppercase tracking-widest">Total to Pay</div>
|
|
<div className="text-xl font-black text-slate-900">{plans.find(p => p.id === data.selected_plan)?.price} AED</div>
|
|
</div>
|
|
<div className="p-3 bg-emerald-50 rounded-2xl">
|
|
<Lock className="w-6 h-6 text-emerald-500" />
|
|
</div>
|
|
</div>
|
|
|
|
<button
|
|
type="submit"
|
|
disabled={processing}
|
|
className="w-full py-5 bg-emerald-500 text-white rounded-[24px] font-bold text-sm uppercase tracking-widest shadow-xl shadow-emerald-500/20 flex items-center justify-center space-x-3 active:scale-[0.98] transition-all disabled:opacity-70 mt-4"
|
|
>
|
|
{processing ? <Loader2 className="w-6 h-6 animate-spin" /> : (
|
|
<>
|
|
<span>Confirm & Pay</span>
|
|
<ChevronRight className="w-5 h-5" />
|
|
</>
|
|
)}
|
|
</button>
|
|
</div>
|
|
)}
|
|
</form>
|
|
|
|
<div className="text-center pt-8 pb-10">
|
|
<p className="text-xs font-bold text-slate-400 uppercase tracking-widest">
|
|
Already joined? <Link href="/mobile/employer/login" className="text-[#185FA5]">Sign In</Link>
|
|
</p>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
);
|
|
}
|