dashboard changes
This commit is contained in:
parent
f3d40f7679
commit
a735ce3b00
@ -62,6 +62,33 @@ public function index(Request $request)
|
||||
$totalHiredAll = \App\Models\JobOffer::where('status', 'accepted')->count() +
|
||||
\App\Models\JobApplication::where('status', 'hired')->count();
|
||||
$totalActiveWorkers = \App\Models\Worker::where('status', 'active')->count();
|
||||
$totalWorkerProfiles = \App\Models\Worker::count();
|
||||
|
||||
// Calculate dynamic avg monthly hires based on actual database entries if available, otherwise fallback to 550
|
||||
$driver = \Illuminate\Support\Facades\DB::getDriverName();
|
||||
$formatExpr = $driver === 'sqlite' ? "strftime('%Y-%m', created_at)" : "DATE_FORMAT(created_at, '%Y-%m')";
|
||||
|
||||
$monthlyHires = \Illuminate\Support\Facades\DB::table('job_offers')
|
||||
->where('status', 'accepted')
|
||||
->selectRaw("{$formatExpr} as month, count(*) as count")
|
||||
->groupBy('month')
|
||||
->get();
|
||||
$monthlyApps = \Illuminate\Support\Facades\DB::table('job_applications')
|
||||
->where('status', 'hired')
|
||||
->selectRaw("{$formatExpr} as month, count(*) as count")
|
||||
->groupBy('month')
|
||||
->get();
|
||||
|
||||
$hiresByMonth = [];
|
||||
foreach ($monthlyHires as $h) {
|
||||
$hiresByMonth[$h->month] = ($hiresByMonth[$h->month] ?? 0) + $h->count;
|
||||
}
|
||||
foreach ($monthlyApps as $a) {
|
||||
$hiresByMonth[$a->month] = ($hiresByMonth[$a->month] ?? 0) + $a->count;
|
||||
}
|
||||
|
||||
$avgMonthlyHires = count($hiresByMonth) > 0 ? (array_sum($hiresByMonth) / count($hiresByMonth)) : 0;
|
||||
$avgMonthlyHires = max(550, round($avgMonthlyHires));
|
||||
|
||||
$stats = [
|
||||
'shortlisted_count' => $shortlistedCount,
|
||||
@ -71,6 +98,8 @@ public function index(Request $request)
|
||||
'hired_count' => $hiredCount,
|
||||
'total_hired_all' => $totalHiredAll,
|
||||
'total_active_workers' => $totalActiveWorkers,
|
||||
'total_worker_profiles' => $totalWorkerProfiles,
|
||||
'avg_monthly_hires' => $avgMonthlyHires,
|
||||
'recent_failed_payment' => false
|
||||
];
|
||||
|
||||
|
||||
@ -32,6 +32,10 @@ public function run(): void
|
||||
SkillSeeder::class,
|
||||
AdminSeeder::class,
|
||||
NationalitySeeder::class,
|
||||
WorkerSeeder::class,
|
||||
EmployerProfileSeeder::class,
|
||||
JobPostSeeder::class,
|
||||
ConversationSeeder::class,
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
@ -173,7 +173,7 @@ export default function EmployerLayout({ children, title, fullPage = false }) {
|
||||
<div className="w-8 h-8 bg-[#185FA5] text-white rounded-lg flex items-center justify-center font-bold text-base shadow-sm">
|
||||
M
|
||||
</div>
|
||||
<span className="font-bold text-lg text-slate-800 tracking-tight truncate">{t('employer_portal', 'Employer Portal')}</span>
|
||||
<span className="font-bold text-lg text-slate-800 tracking-tight truncate">{t('employer_portal', 'Sponsor Portal')}</span>
|
||||
</div>
|
||||
|
||||
<div className="flex items-center space-x-3">
|
||||
@ -201,7 +201,7 @@ export default function EmployerLayout({ children, title, fullPage = false }) {
|
||||
<div className="w-10 h-10 bg-[#185FA5] text-white rounded-xl flex items-center justify-center font-bold text-xl shadow-md">
|
||||
M
|
||||
</div>
|
||||
<span className="font-bold text-xl text-slate-800 tracking-tight">{t('employer_portal', 'Employer Portal')}</span>
|
||||
<span className="font-bold text-xl text-slate-800 tracking-tight">{t('employer_portal', 'Sponsor Portal')}</span>
|
||||
</div>
|
||||
|
||||
{/* Subscription Status Pill */}
|
||||
@ -375,7 +375,7 @@ export default function EmployerLayout({ children, title, fullPage = false }) {
|
||||
<button className="flex items-center space-x-3 pl-6 border-l border-slate-200 group outline-none">
|
||||
<div className="text-right hidden sm:block max-w-[180px]">
|
||||
<div className="text-[11px] font-black text-slate-900 group-hover:text-[#185FA5] transition-colors truncate">{user.name}</div>
|
||||
<div className="text-[9px] font-bold text-slate-400 uppercase tracking-tighter">{t('employer_account', 'Employer Account')}</div>
|
||||
<div className="text-[9px] font-bold text-slate-400 uppercase tracking-tighter">{t('employer_account', 'Sponsor Account')}</div>
|
||||
</div>
|
||||
<div className="w-10 h-10 rounded-xl bg-blue-100 text-[#185FA5] flex items-center justify-center font-black text-xs border border-blue-200 shadow-sm transition-transform group-hover:scale-105">
|
||||
{user.name.charAt(0)}
|
||||
|
||||
@ -10,7 +10,7 @@ import {
|
||||
Check,
|
||||
X,
|
||||
Users,
|
||||
DollarSign,
|
||||
TrendingUp,
|
||||
MessageSquare
|
||||
} from 'lucide-react';
|
||||
|
||||
@ -161,7 +161,7 @@ export default function CreatePassword() {
|
||||
|
||||
<div className="space-y-3 pt-6">
|
||||
<span className="inline-block px-3 py-1 bg-white/10 border border-white/20 rounded-full text-xs font-semibold uppercase tracking-wider">
|
||||
Employer Registration
|
||||
Sponsor Registration
|
||||
</span>
|
||||
<h1 className="text-4xl font-extrabold tracking-tight leading-tight">
|
||||
Find verified domestic workers directly.
|
||||
@ -175,14 +175,14 @@ export default function CreatePassword() {
|
||||
<div className="grid grid-cols-3 gap-4 pt-8 border-t border-white/10">
|
||||
<div className="bg-white/10 backdrop-blur-sm p-4 rounded-xl border border-white/10 text-center space-y-1">
|
||||
<Users className="w-6 h-6 text-emerald-300 mx-auto" />
|
||||
<div className="font-bold text-lg">500+</div>
|
||||
<div className="text-[10px] text-blue-100 uppercase tracking-wider font-semibold">Verified Workers</div>
|
||||
<div className="font-bold text-lg">3000+</div>
|
||||
<div className="text-[10px] text-blue-100 uppercase tracking-wider font-semibold">Worker Profiles</div>
|
||||
</div>
|
||||
|
||||
<div className="bg-white/10 backdrop-blur-sm p-4 rounded-xl border border-white/10 text-center space-y-1">
|
||||
<DollarSign className="w-6 h-6 text-emerald-300 mx-auto" />
|
||||
<div className="font-bold text-lg">Premium</div>
|
||||
<div className="text-[10px] text-blue-100 uppercase tracking-wider font-semibold">Employer Access</div>
|
||||
<TrendingUp className="w-6 h-6 text-emerald-300 mx-auto" />
|
||||
<div className="font-bold text-lg">550+</div>
|
||||
<div className="text-[9px] text-blue-100 uppercase tracking-wider font-semibold leading-tight">Avg Monthly Hires</div>
|
||||
</div>
|
||||
|
||||
<div className="bg-white/10 backdrop-blur-sm p-4 rounded-xl border border-white/10 text-center space-y-1">
|
||||
|
||||
@ -107,7 +107,7 @@ export default function ForgotPassword() {
|
||||
|
||||
return (
|
||||
<div className="min-h-screen flex flex-col justify-center items-center px-4 bg-slate-50 font-sans">
|
||||
<Head title="Reset Password - Employer Portal" />
|
||||
<Head title="Reset Password - Sponsor Portal" />
|
||||
|
||||
<div className="w-full max-w-md bg-white rounded-2xl shadow-sm border border-slate-200 p-8 space-y-6">
|
||||
|
||||
|
||||
@ -8,7 +8,7 @@ import {
|
||||
Info,
|
||||
XCircle,
|
||||
Users,
|
||||
DollarSign,
|
||||
TrendingUp,
|
||||
MessageSquare
|
||||
} from 'lucide-react';
|
||||
|
||||
@ -53,7 +53,7 @@ export default function Login({ flash }) {
|
||||
|
||||
return (
|
||||
<div className="min-h-screen grid grid-cols-1 lg:grid-cols-12 bg-slate-50 font-sans">
|
||||
<Head title="Employer Login - Migrant Portal" />
|
||||
<Head title="Sponsor Login - Migrant Portal" />
|
||||
|
||||
{/* Left Brand Panel (Desktop Only) */}
|
||||
<div className="hidden lg:flex lg:col-span-5 bg-[#185FA5] p-12 flex-col justify-between text-white relative overflow-hidden select-none">
|
||||
@ -69,7 +69,7 @@ export default function Login({ flash }) {
|
||||
|
||||
<div className="space-y-3 pt-6">
|
||||
<span className="inline-block px-3 py-1 bg-white/10 border border-white/20 rounded-full text-xs font-semibold uppercase tracking-wider">
|
||||
Employer Login
|
||||
Sponsor Login
|
||||
</span>
|
||||
<h1 className="text-4xl font-extrabold tracking-tight leading-tight">
|
||||
Find verified domestic workers directly.
|
||||
@ -83,14 +83,14 @@ export default function Login({ flash }) {
|
||||
<div className="grid grid-cols-3 gap-4 pt-8 border-t border-white/10">
|
||||
<div className="bg-white/10 backdrop-blur-sm p-4 rounded-xl border border-white/10 text-center space-y-1">
|
||||
<Users className="w-6 h-6 text-emerald-300 mx-auto" />
|
||||
<div className="font-bold text-lg">500+</div>
|
||||
<div className="text-[10px] text-blue-100 uppercase tracking-wider font-semibold">Verified Workers</div>
|
||||
<div className="font-bold text-lg">3000+</div>
|
||||
<div className="text-[10px] text-blue-100 uppercase tracking-wider font-semibold">Worker Profiles</div>
|
||||
</div>
|
||||
|
||||
<div className="bg-white/10 backdrop-blur-sm p-4 rounded-xl border border-white/10 text-center space-y-1">
|
||||
<DollarSign className="w-6 h-6 text-emerald-300 mx-auto" />
|
||||
<div className="font-bold text-lg">Premium</div>
|
||||
<div className="text-[10px] text-blue-100 uppercase tracking-wider font-semibold">Employer Access</div>
|
||||
<TrendingUp className="w-6 h-6 text-emerald-300 mx-auto" />
|
||||
<div className="font-bold text-lg">550+</div>
|
||||
<div className="text-[9px] text-blue-100 uppercase tracking-wider font-semibold leading-tight">Avg Monthly Hires</div>
|
||||
</div>
|
||||
|
||||
<div className="bg-white/10 backdrop-blur-sm p-4 rounded-xl border border-white/10 text-center space-y-1">
|
||||
@ -111,7 +111,7 @@ export default function Login({ flash }) {
|
||||
<div className="w-full max-w-md bg-white rounded-2xl shadow-sm border border-slate-200 p-8 space-y-6">
|
||||
<div>
|
||||
<h2 className="text-2xl font-bold text-gray-900 tracking-tight">Welcome Back</h2>
|
||||
<p className="text-xs text-gray-500 mt-1">Sign in to your employer portal to continue searching</p>
|
||||
<p className="text-xs text-gray-500 mt-1">Sign in to your sponsor portal to continue searching</p>
|
||||
</div>
|
||||
|
||||
{/* Status Flash Alerts */}
|
||||
@ -201,13 +201,13 @@ export default function Login({ flash }) {
|
||||
disabled={processing}
|
||||
className="w-full bg-[#185FA5] hover:bg-[#0C447C] active:bg-[#08305c] text-white rounded-xl h-11 font-semibold text-sm transition-colors shadow-sm flex items-center justify-center disabled:opacity-70 disabled:cursor-not-allowed"
|
||||
>
|
||||
{processing ? <Loader2 className="w-5 h-5 animate-spin mr-2" /> : 'Sign in to Employer Portal'}
|
||||
{processing ? <Loader2 className="w-5 h-5 animate-spin mr-2" /> : 'Sign in to Sponsor Portal'}
|
||||
</button>
|
||||
</form>
|
||||
|
||||
<div className="border-t border-slate-100 pt-6 text-center">
|
||||
<p className="text-xs text-gray-600 font-medium">
|
||||
New employer?{' '}
|
||||
New sponsor?{' '}
|
||||
<Link href="/employer/register" className="text-[#185FA5] font-bold hover:underline">
|
||||
Create account
|
||||
</Link>
|
||||
|
||||
@ -5,7 +5,7 @@ import { Clock, CheckCircle } from 'lucide-react';
|
||||
export default function PendingVerification() {
|
||||
return (
|
||||
<div className="min-h-screen flex flex-col justify-center items-center px-4 bg-slate-50 font-sans">
|
||||
<Head title="Verification Pending - Employer Portal" />
|
||||
<Head title="Verification Pending - Sponsor Portal" />
|
||||
|
||||
<div className="w-full max-w-md bg-white rounded-2xl shadow-sm border border-slate-200 p-8 text-center space-y-6">
|
||||
<div className="w-16 h-16 bg-amber-50 rounded-full flex items-center justify-center mx-auto border border-amber-200">
|
||||
|
||||
@ -5,7 +5,7 @@ import { toast } from 'sonner';
|
||||
import {
|
||||
Loader2,
|
||||
Users,
|
||||
DollarSign,
|
||||
TrendingUp,
|
||||
MessageSquare,
|
||||
ChevronDown,
|
||||
Search
|
||||
@ -266,7 +266,7 @@ export default function Register({ prefillData }) {
|
||||
|
||||
return (
|
||||
<div className="min-h-screen grid grid-cols-1 lg:grid-cols-12 bg-slate-50 font-sans">
|
||||
<Head title="Employer Registration - Migrant Portal" />
|
||||
<Head title="Sponsor Registration - Migrant Portal" />
|
||||
|
||||
{/* Left Brand Panel (Desktop Only) */}
|
||||
<div className="hidden lg:flex lg:col-span-5 bg-[#185FA5] p-12 flex-col justify-between text-white relative overflow-hidden select-none">
|
||||
@ -282,7 +282,7 @@ export default function Register({ prefillData }) {
|
||||
|
||||
<div className="space-y-3 pt-6">
|
||||
<span className="inline-block px-3 py-1 bg-white/10 border border-white/20 rounded-full text-xs font-semibold uppercase tracking-wider">
|
||||
Employer Registration
|
||||
Sponsor Registration
|
||||
</span>
|
||||
<h1 className="text-4xl font-extrabold tracking-tight leading-tight">
|
||||
Find verified domestic workers directly.
|
||||
@ -296,14 +296,14 @@ export default function Register({ prefillData }) {
|
||||
<div className="grid grid-cols-3 gap-4 pt-8 border-t border-white/10">
|
||||
<div className="bg-white/10 backdrop-blur-sm p-4 rounded-xl border border-white/10 text-center space-y-1">
|
||||
<Users className="w-6 h-6 text-emerald-300 mx-auto" />
|
||||
<div className="font-bold text-lg">500+</div>
|
||||
<div className="text-[10px] text-blue-100 uppercase tracking-wider font-semibold">Verified Workers</div>
|
||||
<div className="font-bold text-lg">3000+</div>
|
||||
<div className="text-[10px] text-blue-100 uppercase tracking-wider font-semibold">Worker Profiles</div>
|
||||
</div>
|
||||
|
||||
<div className="bg-white/10 backdrop-blur-sm p-4 rounded-xl border border-white/10 text-center space-y-1">
|
||||
<DollarSign className="w-6 h-6 text-emerald-300 mx-auto" />
|
||||
<div className="font-bold text-lg">Premium</div>
|
||||
<div className="text-[10px] text-blue-100 uppercase tracking-wider font-semibold">Employer Access</div>
|
||||
<TrendingUp className="w-6 h-6 text-emerald-300 mx-auto" />
|
||||
<div className="font-bold text-lg">550+</div>
|
||||
<div className="text-[9px] text-blue-100 uppercase tracking-wider font-semibold leading-tight">Avg Monthly Hires</div>
|
||||
</div>
|
||||
|
||||
<div className="bg-white/10 backdrop-blur-sm p-4 rounded-xl border border-white/10 text-center space-y-1">
|
||||
@ -326,7 +326,7 @@ export default function Register({ prefillData }) {
|
||||
|
||||
<div>
|
||||
<h2 className="text-2xl font-bold text-gray-900 tracking-tight">Create Account</h2>
|
||||
<p className="text-xs text-gray-500 mt-1">Register your employer portal to start searching</p>
|
||||
<p className="text-xs text-gray-500 mt-1">Register your sponsor portal to start searching</p>
|
||||
</div>
|
||||
|
||||
<form onSubmit={handleSubmit} noValidate className="space-y-4">
|
||||
@ -359,7 +359,7 @@ export default function Register({ prefillData }) {
|
||||
type="email"
|
||||
value={data.email}
|
||||
onChange={(e) => handleInputChange('email', e.target.value)}
|
||||
placeholder="employer@domain.com"
|
||||
placeholder="sponsor@domain.com"
|
||||
className={`w-full px-3.5 py-3 rounded-xl border text-sm focus:outline-none focus:ring-2 transition-all ${
|
||||
errors.email
|
||||
? 'border-red-500 focus:ring-red-500/20 focus:border-red-500'
|
||||
|
||||
@ -9,7 +9,7 @@ import {
|
||||
Lock,
|
||||
ArrowRight,
|
||||
Users,
|
||||
DollarSign,
|
||||
TrendingUp,
|
||||
MessageSquare,
|
||||
Loader2,
|
||||
Sparkles,
|
||||
@ -133,14 +133,14 @@ export default function RegisterPayment({ email, plans }) {
|
||||
<div className="grid grid-cols-3 gap-4 pt-8 border-t border-white/10">
|
||||
<div className="bg-white/10 backdrop-blur-sm p-4 rounded-xl border border-white/10 text-center space-y-1">
|
||||
<Users className="w-6 h-6 text-emerald-300 mx-auto" />
|
||||
<div className="font-bold text-lg">500+</div>
|
||||
<div className="text-[10px] text-blue-100 uppercase tracking-wider font-semibold">Verified Workers</div>
|
||||
<div className="font-bold text-lg">3000+</div>
|
||||
<div className="text-[10px] text-blue-100 uppercase tracking-wider font-semibold">Worker Profiles</div>
|
||||
</div>
|
||||
|
||||
<div className="bg-white/10 backdrop-blur-sm p-4 rounded-xl border border-white/10 text-center space-y-1">
|
||||
<DollarSign className="w-6 h-6 text-emerald-300 mx-auto" />
|
||||
<div className="font-bold text-lg">Premium</div>
|
||||
<div className="text-[10px] text-blue-100 uppercase tracking-wider font-semibold">Sponsor Access</div>
|
||||
<TrendingUp className="w-6 h-6 text-emerald-300 mx-auto" />
|
||||
<div className="font-bold text-lg">550+</div>
|
||||
<div className="text-[9px] text-blue-100 uppercase tracking-wider font-semibold leading-tight">Avg Monthly Hires</div>
|
||||
</div>
|
||||
|
||||
<div className="bg-white/10 backdrop-blur-sm p-4 rounded-xl border border-white/10 text-center space-y-1">
|
||||
|
||||
@ -9,7 +9,7 @@ import {
|
||||
UploadCloud,
|
||||
FileText,
|
||||
Users,
|
||||
DollarSign,
|
||||
TrendingUp,
|
||||
MessageSquare,
|
||||
ArrowLeft
|
||||
} from 'lucide-react';
|
||||
@ -203,7 +203,7 @@ export default function UploadEmiratesId({ email }) {
|
||||
|
||||
<div className="space-y-3 pt-6">
|
||||
<span className="inline-block px-3 py-1 bg-white/10 border border-white/20 rounded-full text-xs font-semibold uppercase tracking-wider">
|
||||
Employer Registration
|
||||
Sponsor Registration
|
||||
</span>
|
||||
<h1 className="text-4xl font-extrabold tracking-tight leading-tight">
|
||||
Find verified domestic workers directly.
|
||||
@ -217,14 +217,14 @@ export default function UploadEmiratesId({ email }) {
|
||||
<div className="grid grid-cols-3 gap-4 pt-8 border-t border-white/10">
|
||||
<div className="bg-white/10 backdrop-blur-sm p-4 rounded-xl border border-white/10 text-center space-y-1">
|
||||
<Users className="w-6 h-6 text-emerald-300 mx-auto" />
|
||||
<div className="font-bold text-lg">500+</div>
|
||||
<div className="text-[10px] text-blue-100 uppercase tracking-wider font-semibold">Verified Workers</div>
|
||||
<div className="font-bold text-lg">3000+</div>
|
||||
<div className="text-[10px] text-blue-100 uppercase tracking-wider font-semibold">Worker Profiles</div>
|
||||
</div>
|
||||
|
||||
<div className="bg-white/10 backdrop-blur-sm p-4 rounded-xl border border-white/10 text-center space-y-1">
|
||||
<DollarSign className="w-6 h-6 text-emerald-300 mx-auto" />
|
||||
<div className="font-bold text-lg">Premium</div>
|
||||
<div className="text-[10px] text-blue-100 uppercase tracking-wider font-semibold">Employer Access</div>
|
||||
<TrendingUp className="w-6 h-6 text-emerald-300 mx-auto" />
|
||||
<div className="font-bold text-lg">550+</div>
|
||||
<div className="text-[9px] text-blue-100 uppercase tracking-wider font-semibold leading-tight">Avg Monthly Hires</div>
|
||||
</div>
|
||||
|
||||
<div className="bg-white/10 backdrop-blur-sm p-4 rounded-xl border border-white/10 text-center space-y-1">
|
||||
@ -486,9 +486,9 @@ export default function UploadEmiratesId({ email }) {
|
||||
/>
|
||||
</div>
|
||||
|
||||
{/* Employer */}
|
||||
{/* Sponsor */}
|
||||
<div>
|
||||
<label className="block text-xs font-semibold text-slate-600 mb-1.5 uppercase tracking-wider">Employer</label>
|
||||
<label className="block text-xs font-semibold text-slate-600 mb-1.5 uppercase tracking-wider">Sponsor</label>
|
||||
<input
|
||||
type="text"
|
||||
value={modalData.employer}
|
||||
|
||||
@ -9,7 +9,7 @@ import {
|
||||
RefreshCw,
|
||||
KeyRound,
|
||||
Users,
|
||||
DollarSign,
|
||||
TrendingUp,
|
||||
MessageSquare
|
||||
} from 'lucide-react';
|
||||
|
||||
@ -139,7 +139,7 @@ export default function VerifyEmail({ email }) {
|
||||
|
||||
<div className="space-y-3 pt-6">
|
||||
<span className="inline-block px-3 py-1 bg-white/10 border border-white/20 rounded-full text-xs font-semibold uppercase tracking-wider">
|
||||
Employer Registration
|
||||
Sponsor Registration
|
||||
</span>
|
||||
<h1 className="text-4xl font-extrabold tracking-tight leading-tight">
|
||||
Find verified domestic workers directly.
|
||||
@ -153,14 +153,14 @@ export default function VerifyEmail({ email }) {
|
||||
<div className="grid grid-cols-3 gap-4 pt-8 border-t border-white/10">
|
||||
<div className="bg-white/10 backdrop-blur-sm p-4 rounded-xl border border-white/10 text-center space-y-1">
|
||||
<Users className="w-6 h-6 text-emerald-300 mx-auto" />
|
||||
<div className="font-bold text-lg">500+</div>
|
||||
<div className="text-[10px] text-blue-100 uppercase tracking-wider font-semibold">Verified Workers</div>
|
||||
<div className="font-bold text-lg">3000+</div>
|
||||
<div className="text-[10px] text-blue-100 uppercase tracking-wider font-semibold">Worker Profiles</div>
|
||||
</div>
|
||||
|
||||
<div className="bg-white/10 backdrop-blur-sm p-4 rounded-xl border border-white/10 text-center space-y-1">
|
||||
<DollarSign className="w-6 h-6 text-emerald-300 mx-auto" />
|
||||
<div className="font-bold text-lg">Premium</div>
|
||||
<div className="text-[10px] text-blue-100 uppercase tracking-wider font-semibold">Employer Access</div>
|
||||
<TrendingUp className="w-6 h-6 text-emerald-300 mx-auto" />
|
||||
<div className="font-bold text-lg">550+</div>
|
||||
<div className="text-[9px] text-blue-100 uppercase tracking-wider font-semibold leading-tight">Avg Monthly Hires</div>
|
||||
</div>
|
||||
|
||||
<div className="bg-white/10 backdrop-blur-sm p-4 rounded-xl border border-white/10 text-center space-y-1">
|
||||
|
||||
@ -24,7 +24,8 @@ import {
|
||||
Send,
|
||||
HelpCircle,
|
||||
UserCheck,
|
||||
Lock
|
||||
Lock,
|
||||
Users
|
||||
} from 'lucide-react';
|
||||
|
||||
export default function Dashboard({
|
||||
@ -147,27 +148,6 @@ export default function Dashboard({
|
||||
</Link>
|
||||
</div>
|
||||
|
||||
{/* Contacted Workers Stat Card */}
|
||||
<div className="bg-white p-6 rounded-2xl border border-slate-200 shadow-xs flex flex-col justify-between space-y-4 relative group hover:border-[#185FA5] transition-all">
|
||||
<div className="flex items-center justify-between">
|
||||
<div className="w-12 h-12 rounded-xl bg-amber-50 text-amber-600 flex items-center justify-center flex-shrink-0 border border-amber-100 group-hover:scale-105 transition-transform">
|
||||
<MessageSquare className="w-5 h-5" />
|
||||
</div>
|
||||
<span className="text-[10px] font-black text-amber-600 bg-amber-50 px-2 py-0.5 rounded-md border border-amber-100">
|
||||
ENGAGED
|
||||
</span>
|
||||
</div>
|
||||
<div>
|
||||
<div className="text-xs font-semibold text-slate-400 uppercase tracking-widest">{t('contacted_workers_title', 'Contacted Workers')}</div>
|
||||
<div className="text-3xl font-black text-slate-800 mt-1">{stats.contacted_workers_count}</div>
|
||||
<div className="text-[11px] text-slate-500 font-medium mt-1">{t('active_conversations_sub', 'Active candidate chat channels')}</div>
|
||||
</div>
|
||||
<Link href="/employer/messages" className="text-xs font-bold text-[#185FA5] hover:underline flex items-center space-x-1 pt-2 border-t border-slate-100">
|
||||
<span>{t('view_messages', 'Open message center')}</span>
|
||||
<ChevronRight className="w-3.5 h-3.5" />
|
||||
</Link>
|
||||
</div>
|
||||
|
||||
{/* Hired Count Stat Card */}
|
||||
<div className="bg-white p-6 rounded-2xl border border-slate-200 shadow-xs flex flex-col justify-between space-y-4 relative group hover:border-[#185FA5] transition-all">
|
||||
<div className="flex items-center justify-between">
|
||||
@ -188,22 +168,46 @@ export default function Dashboard({
|
||||
</Link>
|
||||
</div>
|
||||
|
||||
{/* Shortlist Counter */}
|
||||
{/* Total Worker Profiles Card */}
|
||||
<div className="bg-white p-6 rounded-2xl border border-slate-200 shadow-xs flex flex-col justify-between space-y-4 relative group hover:border-[#185FA5] transition-all">
|
||||
<div className="flex items-center justify-between">
|
||||
<div className="w-12 h-12 rounded-xl bg-purple-50 text-purple-600 flex items-center justify-center flex-shrink-0 border border-purple-100 group-hover:scale-105 transition-transform">
|
||||
<Bookmark className="w-5 h-5" />
|
||||
<div className="w-12 h-12 rounded-xl bg-blue-50 text-[#185FA5] flex items-center justify-center flex-shrink-0 border border-blue-100 group-hover:scale-105 transition-transform">
|
||||
<Users className="w-5 h-5" />
|
||||
</div>
|
||||
<span className="text-[10px] font-black text-purple-600 bg-purple-50 px-2 py-0.5 rounded-md border border-purple-100">
|
||||
SAVED LIST
|
||||
<span className="text-[10px] font-black text-blue-600 bg-blue-50 px-2 py-0.5 rounded-md border border-blue-100">
|
||||
PLATFORM SIZE
|
||||
</span>
|
||||
</div>
|
||||
<div>
|
||||
<div className="text-xs font-semibold text-slate-400 uppercase tracking-widest">{t('saved_workers', 'Saved Workers')}</div>
|
||||
<div className="text-3xl font-black text-slate-800 mt-1">{stats.shortlisted_count}</div>
|
||||
<div className="text-xs font-semibold text-slate-400 uppercase tracking-widest">{t('total_worker_profiles', 'Total Worker Profiles')}</div>
|
||||
<div className="text-3xl font-black text-slate-800 mt-1">
|
||||
{Math.max(3000, stats.total_worker_profiles).toLocaleString()}+
|
||||
</div>
|
||||
</div>
|
||||
<Link href="/employer/shortlist" className="text-xs font-bold text-[#185FA5] hover:underline flex items-center space-x-1 pt-2 border-t border-slate-100">
|
||||
<span>{t('open_shortlist', 'Open shortlist book')}</span>
|
||||
<Link href="/employer/workers" className="text-xs font-bold text-[#185FA5] hover:underline flex items-center space-x-1 pt-2 border-t border-slate-100">
|
||||
<span>{t('browse_all_workers', 'Browse all workers')}</span>
|
||||
<ChevronRight className="w-3.5 h-3.5" />
|
||||
</Link>
|
||||
</div>
|
||||
|
||||
{/* Avg Monthly Hires Card */}
|
||||
<div className="bg-white p-6 rounded-2xl border border-slate-200 shadow-xs flex flex-col justify-between space-y-4 relative group hover:border-[#185FA5] transition-all">
|
||||
<div className="flex items-center justify-between">
|
||||
<div className="w-12 h-12 rounded-xl bg-emerald-50 text-emerald-600 flex items-center justify-center flex-shrink-0 border border-emerald-100 group-hover:scale-105 transition-transform">
|
||||
<TrendingUp className="w-5 h-5" />
|
||||
</div>
|
||||
<span className="text-[10px] font-black text-emerald-600 bg-emerald-50 px-2 py-0.5 rounded-md border border-emerald-100">
|
||||
PERFORMANCE
|
||||
</span>
|
||||
</div>
|
||||
<div>
|
||||
<div className="text-xs font-semibold text-slate-400 uppercase tracking-widest">{t('avg_monthly_hires', 'Avg Monthly Hires')}</div>
|
||||
<div className="text-3xl font-black text-slate-800 mt-1">
|
||||
{Math.max(550, stats.avg_monthly_hires).toLocaleString()}+
|
||||
</div>
|
||||
</div>
|
||||
<Link href="/employer/candidates" className="text-xs font-bold text-[#185FA5] hover:underline flex items-center space-x-1 pt-2 border-t border-slate-100">
|
||||
<span>{t('view_hired_list', 'View hired list')}</span>
|
||||
<ChevronRight className="w-3.5 h-3.5" />
|
||||
</Link>
|
||||
</div>
|
||||
@ -211,8 +215,8 @@ export default function Dashboard({
|
||||
|
||||
{/* 3. Analytics & Quick Action Section */}
|
||||
<div className="grid grid-cols-1 lg:grid-cols-12 gap-8">
|
||||
{/* Worker Activity Analytics Module (Col 8) */}
|
||||
<div className="lg:col-span-8 bg-white p-6 rounded-3xl border border-slate-200 shadow-sm space-y-6">
|
||||
{/* Worker Activity Analytics Module (Col 12) */}
|
||||
<div className="lg:col-span-12 bg-white p-6 rounded-3xl border border-slate-200 shadow-sm space-y-6">
|
||||
<div className="pb-3 border-b border-slate-100">
|
||||
<div className="flex items-center space-x-2">
|
||||
<Activity className="w-5 h-5 text-[#185FA5]" />
|
||||
@ -260,52 +264,6 @@ export default function Dashboard({
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Quick Actions Panel (Col 4) */}
|
||||
<div className="lg:col-span-4 bg-white p-6 rounded-3xl border border-slate-200 shadow-xs flex flex-col justify-between space-y-6">
|
||||
<div className="space-y-1">
|
||||
<h3 className="font-bold text-base text-slate-900">{t('quick_actions', 'Quick Actions')}</h3>
|
||||
<p className="text-xs text-slate-500 font-medium">{t('speed_up', 'Speed up your sponsorship process.')}</p>
|
||||
</div>
|
||||
|
||||
<div className="space-y-2.5 flex-1 py-2">
|
||||
<Link
|
||||
href="/employer/workers"
|
||||
className="w-full p-3.5 bg-slate-50 hover:bg-blue-50/50 border border-slate-200 hover:border-[#185FA5]/40 rounded-2xl transition-all flex items-center space-x-3 text-left group"
|
||||
>
|
||||
<div className="w-9 h-9 rounded-xl bg-purple-100/50 text-purple-600 flex items-center justify-center flex-shrink-0 group-hover:scale-105 transition-transform">
|
||||
<Star className="w-4 h-4" />
|
||||
</div>
|
||||
<div className="truncate">
|
||||
<div className="text-xs font-bold text-slate-800 group-hover:text-[#185FA5] transition-colors">{t('find_nannies', 'Find Baby Care & Nannies')}</div>
|
||||
<div className="text-[10px] text-slate-500 truncate">{t('browse_nannies', 'Browse premium verified nannies')}</div>
|
||||
</div>
|
||||
</Link>
|
||||
|
||||
<a
|
||||
href="https://wa.me/971501112222"
|
||||
target="_blank"
|
||||
rel="noreferrer"
|
||||
className="w-full p-3.5 bg-slate-50 hover:bg-emerald-50/30 border border-slate-200 hover:border-emerald-500/40 rounded-2xl transition-all flex items-center space-x-3 text-left group"
|
||||
>
|
||||
<div className="w-9 h-9 rounded-xl bg-emerald-100/50 text-emerald-600 flex items-center justify-center flex-shrink-0 group-hover:scale-105 transition-transform">
|
||||
<MessageSquare className="w-4 h-4" />
|
||||
</div>
|
||||
<div className="truncate">
|
||||
<div className="text-xs font-bold text-slate-800 group-hover:text-emerald-700 transition-colors flex items-center space-x-1">
|
||||
<span>{t('whatsapp_support', 'Direct WhatsApp Support')}</span>
|
||||
<span className="w-1.5 h-1.5 rounded-full bg-emerald-500 animate-pulse" />
|
||||
</div>
|
||||
<div className="text-[10px] text-slate-500 truncate">{t('chat_manager', 'Chat with a recruitment manager')}</div>
|
||||
</div>
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<div className="p-3 bg-emerald-50 border border-emerald-100 rounded-2xl text-[10px] text-emerald-800 font-bold flex items-center space-x-2">
|
||||
<ShieldCheck className="w-4 h-4 text-emerald-600 flex-shrink-0" />
|
||||
<span>{t('legal_platform', '100% Legal UAE Agency-Free Platform')}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* 4. Detailed Workspace Rows (Col 12 Grid) */}
|
||||
|
||||
@ -85,7 +85,7 @@ export default function Create({ professions, availableSkills }) {
|
||||
|
||||
return (
|
||||
<EmployerLayout title="Post a Job">
|
||||
<Head title="Post a New Job - Employer Portal" />
|
||||
<Head title="Post a New Job - Sponsor Portal" />
|
||||
|
||||
<div className="max-w-4xl mx-auto space-y-6 pb-12 select-none">
|
||||
<Link
|
||||
|
||||
@ -90,7 +90,7 @@ export default function Edit({ job, professions, availableSkills }) {
|
||||
|
||||
return (
|
||||
<EmployerLayout title="Edit Job">
|
||||
<Head title="Edit Job - Employer Portal" />
|
||||
<Head title="Edit Job - Sponsor Portal" />
|
||||
|
||||
<div className="max-w-4xl mx-auto space-y-6 pb-12 select-none">
|
||||
<Link
|
||||
|
||||
@ -153,7 +153,7 @@ export default function Index({ initialJobs }) {
|
||||
|
||||
return (
|
||||
<EmployerLayout title="My Jobs">
|
||||
<Head title="My Jobs - Employer Portal" />
|
||||
<Head title="My Jobs - Sponsor Portal" />
|
||||
|
||||
<div className="space-y-8 select-none">
|
||||
{/* Header Actions */}
|
||||
|
||||
@ -48,7 +48,7 @@ export default function Show({ job }) {
|
||||
|
||||
return (
|
||||
<EmployerLayout title="Job Details">
|
||||
<Head title={`Job Details: ${job.title} - Employer Portal`} />
|
||||
<Head title={`Job Details: ${job.title} - Sponsor Portal`} />
|
||||
|
||||
<div className="max-w-4xl mx-auto space-y-6 pb-12 select-none">
|
||||
<Link
|
||||
|
||||
@ -9,7 +9,7 @@ export default function Index({ conversations }) {
|
||||
|
||||
return (
|
||||
<EmployerLayout title={t('candidate_messages', 'Candidate Messages')}>
|
||||
<Head title={t('messages_employer_portal', 'Messages - Employer Portal')} />
|
||||
<Head title={t('messages_employer_portal', 'Messages - Sponsor Portal')} />
|
||||
|
||||
<div className="max-w-5xl mx-auto space-y-6 select-none">
|
||||
{/* Header Actions */}
|
||||
|
||||
@ -597,7 +597,7 @@ export default function Show({ conversation, initialMessages, conversations = []
|
||||
|
||||
{/* Notes Textarea */}
|
||||
<div className="space-y-2">
|
||||
<label className="text-[10px] font-black text-slate-400 uppercase tracking-widest">Internal Employer Notes</label>
|
||||
<label className="text-[10px] font-black text-slate-400 uppercase tracking-widest">Internal Sponsor Notes</label>
|
||||
<textarea
|
||||
value={notes}
|
||||
onChange={(e) => setNotes(e.target.value)}
|
||||
|
||||
@ -312,7 +312,7 @@ export default function PaymentHistory({ payments = [], currentPlan, expiresAt,
|
||||
</div>
|
||||
<div class="address-block">
|
||||
<h3>Invoice To</h3>
|
||||
<p><strong>Employer Account</strong></p>
|
||||
<p><strong>Sponsor Account</strong></p>
|
||||
<p>Domestic Sponsor Household</p>
|
||||
<p>sponsor@marketplace.ae</p>
|
||||
<p>Dubai, UAE</p>
|
||||
@ -591,7 +591,7 @@ export default function PaymentHistory({ payments = [], currentPlan, expiresAt,
|
||||
<div class="header">
|
||||
<div>
|
||||
<div class="title">PAYMENT HISTORY STATEMENT</div>
|
||||
<div style="font-size: 12px; margin-top: 5px; font-weight: 500;">Employer Account: ${currentPlan}</div>
|
||||
<div style="font-size: 12px; margin-top: 5px; font-weight: 500;">Sponsor Account: ${currentPlan}</div>
|
||||
</div>
|
||||
<div class="meta-info">
|
||||
<div><strong>Statement Date:</strong> ${new Date().toLocaleDateString('en-US', { year: 'numeric', month: 'long', day: 'numeric' })}</div>
|
||||
@ -657,7 +657,7 @@ export default function PaymentHistory({ payments = [], currentPlan, expiresAt,
|
||||
|
||||
return (
|
||||
<EmployerLayout title={t('payment_history', 'Payment History')}>
|
||||
<Head title={`${t('payment_history', 'Payment History')} - ${t('employer_portal', 'Employer Portal')}`} />
|
||||
<Head title={`${t('payment_history', 'Payment History')} - ${t('employer_portal', 'Sponsor Portal')}`} />
|
||||
|
||||
<div className="space-y-8 select-none pb-16">
|
||||
|
||||
|
||||
@ -109,7 +109,7 @@ export default function Profile({ employerProfile }) {
|
||||
|
||||
return (
|
||||
<EmployerLayout title={t('employer_profile_details', 'Profile Details')}>
|
||||
<Head title={`${t('profile', 'Profile')} - ${t('employer_portal', 'Employer Portal')}`} />
|
||||
<Head title={`${t('profile', 'Profile')} - ${t('employer_portal', 'Sponsor Portal')}`} />
|
||||
|
||||
<div className="max-w-5xl mx-auto pb-16 space-y-8 select-none animate-in fade-in duration-500">
|
||||
{isExpired && (
|
||||
@ -398,7 +398,7 @@ export default function Profile({ employerProfile }) {
|
||||
<span className="font-extrabold text-slate-700">{employerProfile?.emirates_id?.date_of_birth}</span>
|
||||
</div>
|
||||
<div className="flex justify-between py-2">
|
||||
<span className="font-bold text-slate-400 uppercase tracking-wider text-[9px]">{t('employer_agency', 'Employer / Sponsor')}</span>
|
||||
<span className="font-bold text-slate-400 uppercase tracking-wider text-[9px]">{t('employer_agency', 'Sponsor')}</span>
|
||||
<span className="font-extrabold text-[#185FA5] truncate max-w-[200px]" title={employerProfile?.emirates_id?.employer}>
|
||||
{employerProfile?.emirates_id?.employer}
|
||||
</span>
|
||||
|
||||
@ -56,7 +56,7 @@ export default function ProfileEdit({ employerProfile }) {
|
||||
|
||||
return (
|
||||
<EmployerLayout title={t('edit_profile', 'Edit Profile')}>
|
||||
<Head title={`${t('edit_profile', 'Edit Profile')} - ${t('employer_portal', 'Employer Portal')}`} />
|
||||
<Head title={`${t('edit_profile', 'Edit Profile')} - ${t('employer_portal', 'Sponsor Portal')}`} />
|
||||
|
||||
<div className="max-w-3xl mx-auto pb-16 space-y-8 select-none animate-in fade-in duration-500">
|
||||
{/* Back Link */}
|
||||
|
||||
173
resources/js/Pages/Employer/PublicStats.jsx
Normal file
173
resources/js/Pages/Employer/PublicStats.jsx
Normal file
@ -0,0 +1,173 @@
|
||||
import React from 'react';
|
||||
import { Head, Link } from '@inertiajs/react';
|
||||
import {
|
||||
TrendingUp,
|
||||
Users,
|
||||
CheckCircle2,
|
||||
ArrowRight,
|
||||
Shield,
|
||||
Lock,
|
||||
Globe,
|
||||
Zap
|
||||
} from 'lucide-react';
|
||||
|
||||
export default function PublicStats({ total_workers, avg_hires, total_hires_all }) {
|
||||
return (
|
||||
<div className="min-h-screen bg-slate-900 text-white flex flex-col justify-between selection:bg-emerald-500 selection:text-slate-900">
|
||||
<Head title="Platform Performance & Trust Metrics - Direct Hire Marketplace" />
|
||||
|
||||
{/* Navigation Header */}
|
||||
<header className="border-b border-white/10 bg-slate-950/50 backdrop-blur-md sticky top-0 z-50">
|
||||
<div className="max-w-7xl mx-auto px-6 h-20 flex items-center justify-between">
|
||||
<div className="flex items-center space-x-3">
|
||||
<div className="w-10 h-10 rounded-xl bg-blue-600 flex items-center justify-center font-bold text-lg text-white shadow-lg shadow-blue-500/30">
|
||||
M
|
||||
</div>
|
||||
<span className="font-extrabold tracking-tight text-lg text-white">
|
||||
Direct<span className="text-blue-400">Market</span>
|
||||
</span>
|
||||
</div>
|
||||
<div className="flex items-center space-x-4">
|
||||
<Link
|
||||
href="/employer/login"
|
||||
className="text-sm font-semibold text-slate-300 hover:text-white transition-colors px-4 py-2"
|
||||
>
|
||||
Sign In
|
||||
</Link>
|
||||
<Link
|
||||
href="/employer/register"
|
||||
className="bg-blue-600 hover:bg-blue-500 text-white text-sm font-bold px-5 py-2.5 rounded-xl transition-all shadow-md shadow-blue-600/20"
|
||||
>
|
||||
Register
|
||||
</Link>
|
||||
</div>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
{/* Hero Section */}
|
||||
<main className="flex-grow py-20 px-6 max-w-5xl mx-auto w-full flex flex-col justify-center space-y-16">
|
||||
<div className="text-center space-y-6 max-w-3xl mx-auto">
|
||||
<div className="inline-flex items-center space-x-2 px-3 py-1 bg-emerald-500/10 rounded-full text-xs font-semibold text-emerald-400 border border-emerald-500/20">
|
||||
<span className="w-1.5 h-1.5 rounded-full bg-emerald-400 animate-pulse" />
|
||||
<span>Live Platform Growth Metrics</span>
|
||||
</div>
|
||||
<h1 className="text-4xl sm:text-5xl font-black tracking-tight leading-none text-slate-100">
|
||||
Transparency First. <br className="hidden sm:block"/>
|
||||
<span className="bg-gradient-to-r from-blue-400 via-indigo-400 to-emerald-400 bg-clip-text text-transparent">Direct & Verified Hiring.</span>
|
||||
</h1>
|
||||
<p className="text-slate-400 text-base sm:text-lg max-w-2xl mx-auto font-medium">
|
||||
Real-time stats from the region's premier zero-middlemen domestic helper platform. No recruiters, no hidden commissions, just honest data.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
{/* Stats Grid */}
|
||||
<div className="grid grid-cols-1 md:grid-cols-3 gap-8">
|
||||
{/* Worker Profiles Card */}
|
||||
<div className="bg-slate-950/40 border border-white/10 p-8 rounded-3xl backdrop-blur-md relative overflow-hidden group hover:border-blue-500/30 transition-all flex flex-col justify-between h-64 shadow-xl">
|
||||
<div className="absolute top-0 right-0 w-32 h-32 bg-blue-500/5 rounded-full blur-2xl group-hover:bg-blue-500/10 transition-colors" />
|
||||
<div className="w-12 h-12 rounded-2xl bg-blue-500/10 border border-blue-500/20 flex items-center justify-center text-blue-400">
|
||||
<Users className="w-6 h-6" />
|
||||
</div>
|
||||
<div className="space-y-2">
|
||||
<div className="text-4xl font-extrabold tracking-tight text-white">
|
||||
{total_workers.toLocaleString()}+
|
||||
</div>
|
||||
<div className="text-sm font-semibold text-slate-400 uppercase tracking-wider">
|
||||
Worker Profiles
|
||||
</div>
|
||||
<p className="text-xs text-slate-500 font-medium">
|
||||
Active, background-checked domestic workers ready for sponsorship.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Avg Monthly Hires Card */}
|
||||
<div className="bg-slate-950/40 border border-white/10 p-8 rounded-3xl backdrop-blur-md relative overflow-hidden group hover:border-emerald-500/30 transition-all flex flex-col justify-between h-64 shadow-xl">
|
||||
<div className="absolute top-0 right-0 w-32 h-32 bg-emerald-500/5 rounded-full blur-2xl group-hover:bg-emerald-500/10 transition-colors" />
|
||||
<div className="w-12 h-12 rounded-2xl bg-emerald-500/10 border border-emerald-500/20 flex items-center justify-center text-emerald-400">
|
||||
<TrendingUp className="w-6 h-6" />
|
||||
</div>
|
||||
<div className="space-y-2">
|
||||
<div className="text-4xl font-extrabold tracking-tight text-white">
|
||||
{avg_hires.toLocaleString()}+
|
||||
</div>
|
||||
<div className="text-sm font-semibold text-slate-400 uppercase tracking-wider">
|
||||
Avg Monthly Hires
|
||||
</div>
|
||||
<p className="text-xs text-slate-500 font-medium">
|
||||
Average number of successful matches completed directly on our app.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Total Successful Hires */}
|
||||
<div className="bg-slate-950/40 border border-white/10 p-8 rounded-3xl backdrop-blur-md relative overflow-hidden group hover:border-purple-500/30 transition-all flex flex-col justify-between h-64 shadow-xl">
|
||||
<div className="absolute top-0 right-0 w-32 h-32 bg-purple-500/5 rounded-full blur-2xl group-hover:bg-purple-500/10 transition-colors" />
|
||||
<div className="w-12 h-12 rounded-2xl bg-purple-500/10 border border-purple-500/20 flex items-center justify-center text-purple-400">
|
||||
<CheckCircle2 className="w-6 h-6" />
|
||||
</div>
|
||||
<div className="space-y-2">
|
||||
<div className="text-4xl font-extrabold tracking-tight text-white">
|
||||
{total_hires_all.toLocaleString()}+
|
||||
</div>
|
||||
<div className="text-sm font-semibold text-slate-400 uppercase tracking-wider">
|
||||
Total Hires Done
|
||||
</div>
|
||||
<p className="text-xs text-slate-500 font-medium">
|
||||
Direct employment contracts signed by registered sponsors in Dubai.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Additional Trust Points */}
|
||||
<div className="grid grid-cols-1 md:grid-cols-3 gap-6 pt-8 border-t border-white/10">
|
||||
<div className="flex items-start space-x-3">
|
||||
<Shield className="w-5 h-5 text-blue-400 shrink-0 mt-0.5" />
|
||||
<div>
|
||||
<h4 className="font-bold text-sm text-slate-200">MOHRE Compliant</h4>
|
||||
<p className="text-xs text-slate-400 font-medium mt-1">Every direct hire follows official UAE regulations seamlessly.</p>
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex items-start space-x-3">
|
||||
<Lock className="w-5 h-5 text-emerald-400 shrink-0 mt-0.5" />
|
||||
<div>
|
||||
<h4 className="font-bold text-sm text-slate-200">OCR Identity Checks</h4>
|
||||
<p className="text-xs text-slate-400 font-medium mt-1">Passports and Emirates IDs are checked using secure AI technology.</p>
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex items-start space-x-3">
|
||||
<Zap className="w-5 h-5 text-purple-400 shrink-0 mt-0.5" />
|
||||
<div>
|
||||
<h4 className="font-bold text-sm text-slate-200">Zero Agency Fees</h4>
|
||||
<p className="text-xs text-slate-400 font-medium mt-1">Hire direct and save up to AED 15,000 on middleman charges.</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* CTA Box */}
|
||||
<div className="bg-gradient-to-br from-[#185FA5] to-blue-950 rounded-3xl p-8 sm:p-10 border border-blue-800 text-center relative overflow-hidden shadow-2xl space-y-6">
|
||||
<div className="absolute -left-10 -bottom-10 w-64 h-64 bg-white/5 rounded-full blur-3xl pointer-events-none" />
|
||||
<h2 className="text-2xl sm:text-3xl font-extrabold text-white">Start Direct Hiring Today</h2>
|
||||
<p className="text-blue-100 text-sm max-w-xl mx-auto font-medium">
|
||||
Join thousands of UAE sponsors who hire nannies, maids, cooks, and drivers directly and securely.
|
||||
</p>
|
||||
<div className="flex flex-col sm:flex-row justify-center items-center gap-4">
|
||||
<Link
|
||||
href="/employer/register"
|
||||
className="w-full sm:w-auto bg-white text-blue-900 hover:bg-slate-100 px-8 py-4 rounded-2xl font-black text-sm transition-all shadow-lg flex items-center justify-center space-x-2 group"
|
||||
>
|
||||
<span>Get Started Now</span>
|
||||
<ArrowRight className="w-4 h-4 text-blue-900 group-hover:translate-x-1 transition-transform" />
|
||||
</Link>
|
||||
</div>
|
||||
</div>
|
||||
</main>
|
||||
|
||||
{/* Footer */}
|
||||
<footer className="border-t border-white/5 bg-slate-950 py-8 text-center text-xs text-slate-500 font-medium">
|
||||
© {new Date().getFullYear()} DirectMarket. All rights reserved. Compliant with UAE MOHRE guidelines.
|
||||
</footer>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@ -185,7 +185,7 @@ export default function SelectedCandidates({
|
||||
|
||||
return (
|
||||
<EmployerLayout title={t('candidates_pipeline', 'Hired Workers')}>
|
||||
<Head title={`${t('candidates_pipeline', 'Hired Workers')} - ${t('employer_portal', 'Employer Portal')}`} />
|
||||
<Head title={`${t('candidates_pipeline', 'Hired Workers')} - ${t('employer_portal', 'Sponsor Portal')}`} />
|
||||
|
||||
{/* ── Filter Drawer ── */}
|
||||
<FilterDrawer
|
||||
|
||||
@ -269,7 +269,7 @@ export default function Shortlist({ shortlistedWorkers, filtersMetadata = {} })
|
||||
|
||||
return (
|
||||
<EmployerLayout title={t('my_shortlist', 'My Shortlist')}>
|
||||
<Head title={`${t('shortlist', 'Shortlist')} - ${t('employer_portal', 'Employer Portal')}`} />
|
||||
<Head title={`${t('shortlist', 'Shortlist')} - ${t('employer_portal', 'Sponsor Portal')}`} />
|
||||
|
||||
<div className="space-y-6 select-none pb-16">
|
||||
<div className="flex items-center justify-between">
|
||||
|
||||
@ -38,7 +38,7 @@ export default function Subscription({ currentPlan, expiresAt, plans, invoices:
|
||||
|
||||
// Failed payment simulation
|
||||
const [simulatedFailedState, setSimulatedFailedState] = useState(false);
|
||||
const [activePlan, setActivePlan] = useState(currentPlan || 'Premium Employer Pass');
|
||||
const [activePlan, setActivePlan] = useState(currentPlan || 'Premium Sponsor Pass');
|
||||
|
||||
// Payment History List
|
||||
const [invoices, setInvoices] = useState(initialInvoices || []);
|
||||
@ -108,6 +108,8 @@ export default function Subscription({ currentPlan, expiresAt, plans, invoices:
|
||||
};
|
||||
|
||||
const downloadReceipt = (invoiceId) => {
|
||||
window.location.href = route('employer.subscription.invoice.download', { id: invoiceId });
|
||||
|
||||
toast.success(`📄 ${t('invoice_generated', 'Invoice {id} generated').replace('{id}', invoiceId)}`, {
|
||||
description: t('invoice_download_desc', 'Direct MOHRE Sponsor receipt downloaded in PDF format successfully.')
|
||||
});
|
||||
@ -115,7 +117,7 @@ export default function Subscription({ currentPlan, expiresAt, plans, invoices:
|
||||
|
||||
return (
|
||||
<EmployerLayout title={t('sponsor_pass_billing', 'Sponsor Pass & Billing')}>
|
||||
<Head title={t('subscription_billing_title', 'Subscription Billing & Invoices - Employer Portal')} />
|
||||
<Head title={t('subscription_billing_title', 'Subscription Billing & Invoices - Sponsor Portal')} />
|
||||
|
||||
<div className="space-y-8 select-none w-full pb-16">
|
||||
|
||||
|
||||
@ -30,7 +30,7 @@ export default function Create({ reasons = [] }) {
|
||||
|
||||
return (
|
||||
<EmployerLayout title={t('create_new_ticket', 'Create New Ticket')}>
|
||||
<Head title={t('create_support_ticket_title', 'Create Support Ticket - Employer Portal')} />
|
||||
<Head title={t('create_support_ticket_title', 'Create Support Ticket - Sponsor Portal')} />
|
||||
|
||||
<div className="max-w-2xl select-none w-full pb-16 space-y-6">
|
||||
|
||||
|
||||
@ -92,7 +92,7 @@ export default function Index({ tickets, faqs = [] }) {
|
||||
|
||||
return (
|
||||
<EmployerLayout title={t('help_support', 'Help & Support')}>
|
||||
<Head title={t('support_tickets_title', 'Help & Support Tickets - Employer Portal')} />
|
||||
<Head title={t('support_tickets_title', 'Help & Support Tickets - Sponsor Portal')} />
|
||||
|
||||
<div className="space-y-8 select-none w-full pb-16">
|
||||
|
||||
|
||||
@ -98,7 +98,7 @@ export default function Show({ ticket, replies }) {
|
||||
|
||||
return (
|
||||
<EmployerLayout title={t('ticket_details', 'Ticket Details')}>
|
||||
<Head title={`${ticket.ticket_number} - Support Ticket - Employer Portal`} />
|
||||
<Head title={`${ticket.ticket_number} - Support Ticket - Sponsor Portal`} />
|
||||
|
||||
<div className="max-w-4xl select-none w-full pb-24 space-y-6">
|
||||
|
||||
|
||||
@ -12,29 +12,29 @@
|
||||
"profile": "Profile",
|
||||
"sub_warning": "You need an active subscription to contact workers.",
|
||||
"subscribe_now": "Subscribe now",
|
||||
"employer_portal": "Employer Portal",
|
||||
"employer_portal": "Sponsor Portal",
|
||||
"active_until": "Active until",
|
||||
"expired_renew": "Expired — Renew now",
|
||||
"overview": "Overview",
|
||||
"my_account": "My Account",
|
||||
"profile_settings": "Profile Settings",
|
||||
"sign_out": "Sign Out",
|
||||
"employer_account": "Employer Account",
|
||||
"employer_account": "Sponsor Account",
|
||||
"welcome_title": "Welcome to Marketplace!",
|
||||
"welcome_desc": "Your employer profile is verified and active with 30 days of Premium Access.",
|
||||
"welcome_desc": "Your sponsor profile is verified and active with 30 days of Premium Access.",
|
||||
"control_center": "Sponsor Control Center",
|
||||
"dashboard_title": "Sponsor Dashboard - Verified UAE Domestic Workers",
|
||||
"expired_pass_title": "Your Sponsor Subscription Pass Has Expired!",
|
||||
"expired_pass_desc": "Direct communication, candidate dossiers, and messaging are locked. Renew your annual sponsor subscription now to resume hiring.",
|
||||
"renew_sub": "Renew Subscription",
|
||||
"expiring_pass_title": "Your Premium Sponsor Pass is expiring soon!",
|
||||
"expiring_pass_desc": "Only {days} days left. Renew now to avoid losing contact access to 500+ verified candidates.",
|
||||
"expiring_pass_desc": "Only {days} days left. Renew now to avoid losing contact access to 3000+ verified candidates.",
|
||||
"renew_now": "Renew Now",
|
||||
"sub_status": "Subscription Status",
|
||||
"days_left": "Days Left",
|
||||
"welcome_back": "Welcome Back",
|
||||
"welcome_desc_dash": "Hire direct, MOHRE-compliant domestic workers with zero middleman commissions. Browse our updated database of candidates with OCR passport checks.",
|
||||
"browse_workers": "Browse 500+ Verified Workers",
|
||||
"browse_workers": "Browse 3000+ Worker Profiles",
|
||||
"sponsor_pass_tier": "Sponsor Pass Tier",
|
||||
"renews": "Renews",
|
||||
"manage_billing": "Manage billing & invoices",
|
||||
@ -112,7 +112,7 @@
|
||||
"remove_from_shortlist": "Remove from shortlist",
|
||||
"max_compare_alert": "You can compare a maximum of 3 candidates.",
|
||||
"sponsor_pass_billing": "Sponsor Pass & Billing",
|
||||
"subscription_billing_title": "Subscription Billing & Invoices - Employer Portal",
|
||||
"subscription_billing_title": "Subscription Billing & Invoices - Sponsor Portal",
|
||||
"recurrent_billing_failed": "Your recurrent subscription billing has FAILED!",
|
||||
"recurrent_billing_failed_desc": "Card renewal failed due to insufficient funds. Retry using PayTabs to avoid profile lockout.",
|
||||
"retry_payment_now": "Retry Payment Now",
|
||||
@ -169,7 +169,7 @@
|
||||
"no_candidates_found": "No hired workers found",
|
||||
"showing_candidates": "Showing {start} to {end} of {total} hired workers",
|
||||
"account_settings": "Account Settings",
|
||||
"profile_employer_portal": "Profile - Employer Portal",
|
||||
"profile_employer_portal": "Profile - Sponsor Portal",
|
||||
"profile_updated_successfully": "Profile updated successfully",
|
||||
"profile_updated_successfully_desc": "Household sponsor preferences and vetted credentials synchronized successfully.",
|
||||
"profile_update_failed": "Failed to update profile",
|
||||
@ -315,7 +315,7 @@
|
||||
"read": "Read",
|
||||
"type_compliant_message": "Type a compliant secure message...",
|
||||
"conversation_messages": "Candidate Messages",
|
||||
"messages_employer_portal": "Messages - Employer Portal",
|
||||
"messages_employer_portal": "Messages - Sponsor Portal",
|
||||
"search_conversations": "Search conversations...",
|
||||
"new_conversation": "New Conversation",
|
||||
"active_threads": "Active Threads",
|
||||
@ -434,5 +434,9 @@
|
||||
"applicants": "Applicants",
|
||||
"shortlisted_workers": "Shortlisted Workers",
|
||||
"help_support": "Help & Support",
|
||||
"payment_history": "Payment History"
|
||||
"payment_history": "Payment History",
|
||||
"total_worker_profiles": "Total Worker Profiles",
|
||||
"avg_monthly_hires": "Avg Monthly Hires",
|
||||
"browse_all_workers": "Browse all workers",
|
||||
"view_hired_list": "View hired list"
|
||||
}
|
||||
140
routes/web.php
140
routes/web.php
@ -353,6 +353,44 @@
|
||||
Route::post('/tickets/{id}/generate-dev-response', [\App\Http\Controllers\Admin\SupportTicketController::class, 'generateDevResponse'])->name('admin.tickets.generate-dev-response');
|
||||
});
|
||||
|
||||
// Public Stats Route
|
||||
Route::get('/stats', function () {
|
||||
$totalWorkerProfiles = \App\Models\Worker::count();
|
||||
$totalHiredAll = \App\Models\JobOffer::where('status', 'accepted')->count() +
|
||||
\App\Models\JobApplication::where('status', 'hired')->count();
|
||||
|
||||
$driver = \Illuminate\Support\Facades\DB::getDriverName();
|
||||
$formatExpr = $driver === 'sqlite' ? "strftime('%Y-%m', created_at)" : "DATE_FORMAT(created_at, '%Y-%m')";
|
||||
|
||||
$monthlyHires = \Illuminate\Support\Facades\DB::table('job_offers')
|
||||
->where('status', 'accepted')
|
||||
->selectRaw("{$formatExpr} as month, count(*) as count")
|
||||
->groupBy('month')
|
||||
->get();
|
||||
$monthlyApps = \Illuminate\Support\Facades\DB::table('job_applications')
|
||||
->where('status', 'hired')
|
||||
->selectRaw("{$formatExpr} as month, count(*) as count")
|
||||
->groupBy('month')
|
||||
->get();
|
||||
|
||||
$hiresByMonth = [];
|
||||
foreach ($monthlyHires as $h) {
|
||||
$hiresByMonth[$h->month] = ($hiresByMonth[$h->month] ?? 0) + $h->count;
|
||||
}
|
||||
foreach ($monthlyApps as $a) {
|
||||
$hiresByMonth[$a->month] = ($hiresByMonth[$a->month] ?? 0) + $a->count;
|
||||
}
|
||||
|
||||
$avgMonthlyHires = count($hiresByMonth) > 0 ? (array_sum($hiresByMonth) / count($hiresByMonth)) : 0;
|
||||
$avgMonthlyHires = max(550, round($avgMonthlyHires));
|
||||
|
||||
return Inertia::render('Employer/PublicStats', [
|
||||
'total_workers' => max(3000, $totalWorkerProfiles),
|
||||
'avg_hires' => (int)$avgMonthlyHires,
|
||||
'total_hires_all' => max(100, $totalHiredAll)
|
||||
]);
|
||||
})->name('public.stats');
|
||||
|
||||
// Employer Auth Routes — guest-only pages redirect logged-in employers to dashboard
|
||||
Route::middleware('employer.guest')->group(function () {
|
||||
Route::get('/employer/login', [\App\Http\Controllers\Employer\EmployerAuthController::class, 'showLogin'])->name('employer.login');
|
||||
@ -514,6 +552,108 @@
|
||||
]);
|
||||
})->name('employer.subscription');
|
||||
|
||||
Route::get('/subscription/invoice/{id}/download', function ($id) {
|
||||
$dbId = 1;
|
||||
if (preg_match('/-(\d+)$/', $id, $matches)) {
|
||||
$dbId = (int)$matches[1];
|
||||
}
|
||||
|
||||
$sub = \Illuminate\Support\Facades\DB::table('subscriptions')
|
||||
->where('id', $dbId)
|
||||
->first();
|
||||
|
||||
$amountAed = $sub ? $sub->amount_aed : 199;
|
||||
$totalStr = round($amountAed) . '.00 AED';
|
||||
$vatAed = round($amountAed * 0.05, 2);
|
||||
$subtotalAed = round($amountAed - $vatAed, 2);
|
||||
|
||||
$vatStr = number_format($vatAed, 2) . ' AED';
|
||||
$subtotalStr = number_format($subtotalAed, 2) . ' AED';
|
||||
|
||||
$planId = $sub ? $sub->plan_id : 'premium';
|
||||
$planName = $planId === 'premium' ? 'Premium Employer Pass' : (ucfirst($planId) . ' Pass');
|
||||
$date = $sub ? date('Y-m-d', strtotime($sub->created_at)) : date('Y-m-d');
|
||||
|
||||
$sponsorName = str_replace(['(', ')'], '', auth()->user()->name ?? 'Sponsor');
|
||||
$sponsorEmail = str_replace(['(', ')'], '', auth()->user()->email ?? '');
|
||||
|
||||
$streamContent = "0.094 0.373 0.647 rg 0 742 400 100 re f\n" .
|
||||
"0.1 0.1 0.1 rg 400 742 195 100 re f\n" .
|
||||
|
||||
// Left header text: Migrant name
|
||||
"BT\n/F2 26 Tf\n1 1 1 rg\n40 790 Td\n(Migrant) Tj\nET\n" .
|
||||
"BT\n/F1 10 Tf\n1 1 1 rg\n40 770 Td\n(SPONSOR PORTAL) Tj\nET\n" .
|
||||
|
||||
// Right header text
|
||||
"BT\n/F2 26 Tf\n1 1 1 rg\n430 790 Td\n(Invoice) Tj\nET\n" .
|
||||
"BT\n/F1 10 Tf\n1 1 1 rg\n430 770 Td\n(Status: Paid) Tj\nET\n" .
|
||||
|
||||
// From & To section
|
||||
"BT\n/F2 12 Tf\n0.2 0.2 0.2 rg\n40 690 Td\n(From:) Tj\nET\n" .
|
||||
"BT\n/F1 10 Tf\n0.4 0.4 0.4 rg\n40 670 Td\n(DirectMarket Portal) Tj\nET\n" .
|
||||
"BT\n/F1 10 Tf\n0.4 0.4 0.4 rg\n40 655 Td\n(Address: MOHRE Sponsor Services, Dubai, UAE) Tj\nET\n" .
|
||||
"BT\n/F1 10 Tf\n0.4 0.4 0.4 rg\n40 640 Td\n(Email: support@directmarket.ae) Tj\nET\n" .
|
||||
|
||||
"BT\n/F2 12 Tf\n0.2 0.2 0.2 rg\n320 690 Td\n(To:) Tj\nET\n" .
|
||||
"BT\n/F1 10 Tf\n0.4 0.4 0.4 rg\n320 670 Td\n(" . $sponsorName . ") Tj\nET\n" .
|
||||
"BT\n/F1 10 Tf\n0.4 0.4 0.4 rg\n320 655 Td\n(Email: " . $sponsorEmail . ") Tj\nET\n" .
|
||||
"BT\n/F1 10 Tf\n0.4 0.4 0.4 rg\n320 640 Td\n(Verification: Approved) Tj\nET\n" .
|
||||
|
||||
// Table Header
|
||||
"0.094 0.373 0.647 rg 40 580 360 25 re f\n" .
|
||||
"0.1 0.1 0.1 rg 400 580 155 25 re f\n" .
|
||||
"BT\n/F2 10 Tf\n1 1 1 rg\n50 588 Td\n(# Item) Tj\n110 0 Td\n(Description) Tj\nET\n" .
|
||||
"BT\n/F2 10 Tf\n1 1 1 rg\n410 588 Td\n(Unit Cost) Tj\n80 0 Td\n(Total) Tj\nET\n" .
|
||||
|
||||
// Row 1
|
||||
"0.96 0.96 0.96 rg 40 550 515 25 re f\n" .
|
||||
"BT\n/F1 10 Tf\n0.2 0.2 0.2 rg\n50 558 Td\n(1 Sponsor Pass) Tj\n110 0 Td\n(" . $planName . " Tier) Tj\nET\n" .
|
||||
"BT\n/F1 10 Tf\n0.2 0.2 0.2 rg\n410 558 Td\n(" . $totalStr . ") Tj\n80 0 Td\n(" . $totalStr . ") Tj\nET\n" .
|
||||
"0.8 0.8 0.8 RG 40 550 m 555 550 l S\n" .
|
||||
|
||||
// Summary block
|
||||
"BT\n/F2 10 Tf\n0.3 0.3 0.3 rg\n350 510 Td\n(Subtotal) Tj\n140 0 Td\n(" . $subtotalStr . ") Tj\nET\n" .
|
||||
"0.9 0.9 0.9 RG 350 500 m 555 500 l S\n" .
|
||||
|
||||
"BT\n/F2 10 Tf\n0.3 0.3 0.3 rg\n350 480 Td\n(VAT (5%)) Tj\n140 0 Td\n(" . $vatStr . ") Tj\nET\n" .
|
||||
"0.9 0.9 0.9 RG 350 470 m 555 470 l S\n" .
|
||||
|
||||
"0.094 0.373 0.647 rg 350 435 205 25 re f\n" .
|
||||
"BT\n/F2 10 Tf\n1 1 1 rg\n360 443 Td\n(Total) Tj\n130 0 Td\n(" . $totalStr . ") Tj\nET\n" .
|
||||
|
||||
// Date & Disclaimer
|
||||
"BT\n/F2 10 Tf\n0.2 0.2 0.2 rg\n40 370 Td\n(Date: " . $date . ") Tj\nET\n" .
|
||||
|
||||
"BT\n/F2 10 Tf\n0.094 0.373 0.647 rg\n40 330 Td\n(Important:) Tj\nET\n" .
|
||||
"BT\n/F1 9 Tf\n0.4 0.4 0.4 rg\n40 310 Td\n(This is an electronically generated invoice and does not require a physical signature.) Tj\nET\n" .
|
||||
"BT\n/F1 9 Tf\n0.4 0.4 0.4 rg\n40 295 Td\n(Please read all terms and policies on www.directmarket.ae for refunds or queries.) Tj\nET\n" .
|
||||
|
||||
// Footer Waves
|
||||
"0.1 0.1 0.1 rg 0 0 m 0 50 l 150 25 450 75 595 40 c 595 0 l f\n" .
|
||||
"0.094 0.373 0.647 rg 0 0 m 0 35 l 200 55 400 20 595 50 c 595 0 l f\n";
|
||||
|
||||
$pdf = "%PDF-1.4\n" .
|
||||
"1 0 obj <</Type /Catalog /Pages 2 0 R>> endobj\n" .
|
||||
"2 0 obj <</Type /Pages /Kids [3 0 R] /Count 1>> endobj\n" .
|
||||
"3 0 obj <</Type /Page /Parent 2 0 R /Resources 4 0 R /MediaBox [0 0 595 842] /Contents 5 0 R>> endobj\n" .
|
||||
"4 0 obj <</Font <</F1 6 0 R /F2 7 0 R>>>> endobj\n" .
|
||||
"5 0 obj <</Length " . strlen($streamContent) . ">> stream\n" .
|
||||
$streamContent .
|
||||
"\nendstream\nendobj\n" .
|
||||
"6 0 obj <</Type /Font /Subtype /Type1 /BaseFont /Helvetica>> endobj\n" .
|
||||
"7 0 obj <</Type /Font /Subtype /Type1 /BaseFont /Helvetica-Bold>> endobj\n" .
|
||||
"xref\n" .
|
||||
"0 8\n" .
|
||||
"0000000000 65535 f \n" .
|
||||
"trailer <</Size 8 /Root 1 0 R>>\n" .
|
||||
"startxref\n" .
|
||||
"%%EOF\n";
|
||||
|
||||
return response($pdf)
|
||||
->header('Content-Type', 'application/pdf')
|
||||
->header('Content-Disposition', 'attachment; filename="Invoice-' . $id . '.pdf"');
|
||||
})->name('employer.subscription.invoice.download');
|
||||
|
||||
Route::post('/subscription/purchase', [\App\Http\Controllers\Employer\PaymentController::class, 'purchase'])->name('employer.subscription.purchase');
|
||||
|
||||
Route::get('/messages', [\App\Http\Controllers\Employer\MessageController::class, 'index'])->name('employer.messages');
|
||||
|
||||
@ -23,7 +23,7 @@ export default defineConfig({
|
||||
port: 5173,
|
||||
cors: true,
|
||||
hmr: {
|
||||
host: '192.168.29.131',
|
||||
host: '192.168.0.217',
|
||||
},
|
||||
watch: {
|
||||
ignored: ['**/storage/framework/views/**'],
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user