542 lines
20 KiB
PHP
542 lines
20 KiB
PHP
<?php
|
|
|
|
namespace App\Http\Controllers\Employer;
|
|
|
|
use App\Http\Controllers\Controller;
|
|
use Illuminate\Http\Request;
|
|
use Inertia\Inertia;
|
|
use App\Models\User;
|
|
use App\Models\JobPost;
|
|
use App\Models\JobApplication;
|
|
|
|
class JobController extends Controller
|
|
{
|
|
private function resolveCurrentUser()
|
|
{
|
|
$sess = session('user');
|
|
$sessId = is_array($sess) ? ($sess['id'] ?? null) : ($sess->id ?? null);
|
|
|
|
if (!$sessId) {
|
|
$user = User::where('role', 'employer')->first();
|
|
if ($user) {
|
|
session(['user' => (object)[
|
|
'id' => $user->id,
|
|
'name' => $user->name,
|
|
'email' => $user->email,
|
|
'role' => 'employer',
|
|
'subscription_status' => $user->subscription_status ?? 'active',
|
|
]]);
|
|
return $user;
|
|
}
|
|
} else {
|
|
return User::find($sessId);
|
|
}
|
|
|
|
return null;
|
|
}
|
|
|
|
private function checkJobAccess($user)
|
|
{
|
|
if (!$user) {
|
|
return false;
|
|
}
|
|
|
|
$sub = \Illuminate\Support\Facades\DB::table('subscriptions')
|
|
->where('user_id', $user->id)
|
|
->where('status', 'active')
|
|
->latest('id')
|
|
->first();
|
|
|
|
$planId = $sub ? $sub->plan_id : 'basic';
|
|
$associatedPlan = \App\Models\Plan::find($planId);
|
|
|
|
return $associatedPlan ? (bool)$associatedPlan->enable_job_apply : ($planId !== 'basic');
|
|
}
|
|
|
|
public function index(Request $request)
|
|
{
|
|
$user = $this->resolveCurrentUser();
|
|
if (!$user) {
|
|
return redirect()->route('employer.login');
|
|
}
|
|
|
|
if (!$this->checkJobAccess($user)) {
|
|
return redirect()->route('employer.subscription')
|
|
->with('error', 'Your current plan does not support the Job Apply module. Please upgrade to a Premium plan to access this feature.');
|
|
}
|
|
|
|
$dbJobs = JobPost::where('employer_id', $user->id)
|
|
->with(['applications'])
|
|
->latest()
|
|
->get();
|
|
|
|
$jobs = $dbJobs->map(function ($job) {
|
|
return [
|
|
'id' => $job->id,
|
|
'title' => $job->title,
|
|
'location' => $job->location,
|
|
'salary' => (int) $job->salary,
|
|
'workers_needed' => $job->workers_needed,
|
|
'applied_count' => $job->applications->count(),
|
|
'hired_count' => $job->applications->where('status', 'hired')->count(),
|
|
'posted_at' => $job->created_at->format('M d, Y'),
|
|
'status' => ucfirst($job->status), // Active, Closed, Draft
|
|
];
|
|
})->toArray();
|
|
|
|
return Inertia::render('Employer/Jobs/Index', [
|
|
'initialJobs' => $jobs,
|
|
]);
|
|
}
|
|
|
|
public function show($id)
|
|
{
|
|
$user = $this->resolveCurrentUser();
|
|
if (!$user) {
|
|
return redirect()->route('employer.login');
|
|
}
|
|
|
|
if (!$this->checkJobAccess($user)) {
|
|
return redirect()->route('employer.subscription')
|
|
->with('error', 'Your current plan does not support the Job Apply module. Please upgrade to a Premium plan to access this feature.');
|
|
}
|
|
|
|
$job = JobPost::where('employer_id', $user->id)
|
|
->with(['applications.worker'])
|
|
->where('id', $id)
|
|
->firstOrFail();
|
|
|
|
$jobData = [
|
|
'id' => $job->id,
|
|
'title' => $job->title,
|
|
'location' => $job->location,
|
|
'salary' => (int) $job->salary,
|
|
'workers_needed' => $job->workers_needed,
|
|
'job_type' => $job->job_type,
|
|
'start_date' => $job->start_date ? $job->start_date->format('M d, Y') : null,
|
|
'description' => $job->description,
|
|
'requirements' => $job->requirements,
|
|
'status' => ucfirst($job->status),
|
|
'applied_count' => $job->applications->count(),
|
|
'hired_count' => $job->applications->where('status', 'hired')->count(),
|
|
'posted_at' => $job->created_at->format('M d, Y'),
|
|
];
|
|
|
|
return Inertia::render('Employer/Jobs/Show', [
|
|
'job' => $jobData,
|
|
]);
|
|
}
|
|
|
|
public function create()
|
|
{
|
|
$user = $this->resolveCurrentUser();
|
|
if (!$user) {
|
|
return redirect()->route('employer.login');
|
|
}
|
|
|
|
if (!$this->checkJobAccess($user)) {
|
|
return redirect()->route('employer.subscription')
|
|
->with('error', 'Your current plan does not support the Job Apply module. Please upgrade to a Premium plan to access this feature.');
|
|
}
|
|
|
|
$professions = \App\Models\Skill::orderBy('name')
|
|
->pluck('name')
|
|
->map(function ($name) {
|
|
return ucwords($name);
|
|
})
|
|
->unique()
|
|
->values()
|
|
->toArray();
|
|
|
|
if (empty($professions)) {
|
|
$professions = [
|
|
'Housekeeper', 'Nanny', 'Maid', 'Electrician', 'Mason', 'Plumber', 'Cleaner', 'Driver', 'Caregiver', 'Cook'
|
|
];
|
|
}
|
|
|
|
$skills = \App\Models\Skill::all()->map(function ($skill) {
|
|
return [
|
|
'id' => $skill->id,
|
|
'name' => $skill->name,
|
|
];
|
|
});
|
|
|
|
return Inertia::render('Employer/Jobs/Create', [
|
|
'professions' => $professions,
|
|
'availableSkills' => $skills,
|
|
]);
|
|
}
|
|
|
|
public function store(Request $request)
|
|
{
|
|
$user = $this->resolveCurrentUser();
|
|
if (!$user) {
|
|
return back()->withErrors(['general' => 'User session not found.']);
|
|
}
|
|
|
|
if (!$this->checkJobAccess($user)) {
|
|
return redirect()->route('employer.subscription')
|
|
->with('error', 'Your current plan does not support the Job Apply module. Please upgrade to a Premium plan to access this feature.');
|
|
}
|
|
|
|
$request->validate([
|
|
'title' => 'required|string|max:255',
|
|
'main_profession' => 'required|string|max:255',
|
|
'skills' => 'required',
|
|
'workers_needed' => 'required|integer|min:1',
|
|
'location' => 'required|string|max:255',
|
|
'salary' => 'required|numeric|min:0',
|
|
'job_type' => 'required|string|in:Full Time,Part Time,Contract',
|
|
'start_date' => 'required|date',
|
|
'description' => 'required|string|max:1000',
|
|
'requirements' => 'nullable|string',
|
|
]);
|
|
|
|
$job = JobPost::create([
|
|
'employer_id' => $user->id,
|
|
'title' => $request->title,
|
|
'main_profession' => $request->main_profession,
|
|
'workers_needed' => $request->workers_needed,
|
|
'job_type' => $request->job_type,
|
|
'location' => $request->location,
|
|
'salary' => $request->salary,
|
|
'start_date' => $request->start_date,
|
|
'description' => $request->description,
|
|
'requirements' => $request->requirements,
|
|
'status' => 'active',
|
|
]);
|
|
|
|
// Sync skills
|
|
$skillsInput = $request->skills;
|
|
$skillIds = [];
|
|
if (is_array($skillsInput)) {
|
|
foreach ($skillsInput as $skillNameOrId) {
|
|
if (is_numeric($skillNameOrId)) {
|
|
$skillIds[] = (int)$skillNameOrId;
|
|
} else {
|
|
$skill = \App\Models\Skill::firstOrCreate(['name' => trim($skillNameOrId)]);
|
|
$skillIds[] = $skill->id;
|
|
}
|
|
}
|
|
} else if (is_string($skillsInput)) {
|
|
$skillsArray = array_filter(array_map('trim', explode(',', $skillsInput)));
|
|
foreach ($skillsArray as $name) {
|
|
$skill = \App\Models\Skill::firstOrCreate(['name' => $name]);
|
|
$skillIds[] = $skill->id;
|
|
}
|
|
}
|
|
$job->skills()->sync($skillIds);
|
|
|
|
return redirect()->route('employer.jobs')->with('success', 'Job posted successfully.');
|
|
}
|
|
|
|
public function applicants($id)
|
|
{
|
|
$user = $this->resolveCurrentUser();
|
|
if (!$user) {
|
|
return redirect()->route('employer.login');
|
|
}
|
|
|
|
if (!$this->checkJobAccess($user)) {
|
|
return redirect()->route('employer.subscription')
|
|
->with('error', 'Your current plan does not support the Job Apply module. Please upgrade to a Premium plan to access this feature.');
|
|
}
|
|
|
|
$job = JobPost::where('employer_id', $user->id)->where('id', $id)->firstOrFail();
|
|
|
|
$dbApplications = JobApplication::where('job_id', $id)
|
|
->with(['worker.skills'])
|
|
->get();
|
|
|
|
$applicants = $dbApplications->map(function ($app) use ($user) {
|
|
$worker = $app->worker;
|
|
if (!$worker) return null;
|
|
$isSaved = \App\Models\Shortlist::where('employer_id', $user->id)
|
|
->where('worker_id', $worker->id)
|
|
->exists();
|
|
return [
|
|
'id' => $worker->id,
|
|
'application_id' => $app->id,
|
|
'name' => $worker->name,
|
|
'nationality' => $worker->nationality,
|
|
'salary' => (int) $worker->salary,
|
|
'experience' => ($worker->experience_years ?? 3) . ' Years',
|
|
'status' => ucfirst($app->status), // Applied, Shortlisted, Hired, Rejected, etc.
|
|
'notes' => $app->notes,
|
|
'status_history' => $app->status_history ?: [],
|
|
'match_score' => $worker->match_score ?? 90,
|
|
'is_saved' => $isSaved,
|
|
];
|
|
})->filter()->values()->toArray();
|
|
|
|
return Inertia::render('Employer/Jobs/Applicants', [
|
|
'job' => [
|
|
'id' => $job->id,
|
|
'title' => $job->title,
|
|
'salary' => (int) $job->salary,
|
|
],
|
|
'applicants' => $applicants,
|
|
]);
|
|
}
|
|
|
|
public function edit($id)
|
|
{
|
|
$user = $this->resolveCurrentUser();
|
|
if (!$user) {
|
|
return redirect()->route('employer.login');
|
|
}
|
|
|
|
if (!$this->checkJobAccess($user)) {
|
|
return redirect()->route('employer.subscription')
|
|
->with('error', 'Your current plan does not support the Job Apply module. Please upgrade to a Premium plan to access this feature.');
|
|
}
|
|
|
|
$job = JobPost::where('employer_id', $user->id)->where('id', $id)->with('skills')->firstOrFail();
|
|
|
|
// Convert start_date format to Y-m-d for date input
|
|
$jobData = $job->toArray();
|
|
if ($job->start_date) {
|
|
$jobData['start_date'] = $job->start_date->format('Y-m-d');
|
|
}
|
|
|
|
// Add skills list
|
|
$jobData['skills'] = $job->skills->map(function ($s) {
|
|
return [
|
|
'id' => $s->id,
|
|
'name' => $s->name,
|
|
];
|
|
})->toArray();
|
|
|
|
$professions = \App\Models\Skill::orderBy('name')
|
|
->pluck('name')
|
|
->map(function ($name) {
|
|
return ucwords($name);
|
|
})
|
|
->unique()
|
|
->values()
|
|
->toArray();
|
|
|
|
if (empty($professions)) {
|
|
$professions = [
|
|
'Housekeeper', 'Nanny', 'Maid', 'Electrician', 'Mason', 'Plumber', 'Cleaner', 'Driver', 'Caregiver', 'Cook'
|
|
];
|
|
}
|
|
|
|
$skills = \App\Models\Skill::all()->map(function ($skill) {
|
|
return [
|
|
'id' => $skill->id,
|
|
'name' => $skill->name,
|
|
];
|
|
});
|
|
|
|
return Inertia::render('Employer/Jobs/Edit', [
|
|
'job' => $jobData,
|
|
'professions' => $professions,
|
|
'availableSkills' => $skills,
|
|
]);
|
|
}
|
|
|
|
public function update(Request $request, $id)
|
|
{
|
|
$user = $this->resolveCurrentUser();
|
|
if (!$user) {
|
|
return back()->withErrors(['general' => 'User session not found.']);
|
|
}
|
|
|
|
if (!$this->checkJobAccess($user)) {
|
|
return redirect()->route('employer.subscription')
|
|
->with('error', 'Your current plan does not support the Job Apply module. Please upgrade to a Premium plan to access this feature.');
|
|
}
|
|
|
|
$job = JobPost::where('employer_id', $user->id)->where('id', $id)->firstOrFail();
|
|
|
|
if ($job->applications()->exists()) {
|
|
return back()->withErrors(['general' => 'This job cannot be edited because workers have already applied for it.']);
|
|
}
|
|
|
|
$request->validate([
|
|
'title' => 'required|string|max:255',
|
|
'main_profession' => 'required|string|max:255',
|
|
'skills' => 'required',
|
|
'workers_needed' => 'required|integer|min:1',
|
|
'location' => 'required|string|max:255',
|
|
'salary' => 'required|numeric|min:0',
|
|
'job_type' => 'required|string|in:Full Time,Part Time,Contract',
|
|
'start_date' => 'required|date',
|
|
'description' => 'required|string|max:1000',
|
|
'requirements' => 'nullable|string',
|
|
'status' => 'required|string|in:active,closed,draft',
|
|
]);
|
|
|
|
$job->update([
|
|
'title' => $request->title,
|
|
'main_profession' => $request->main_profession,
|
|
'workers_needed' => $request->workers_needed,
|
|
'job_type' => $request->job_type,
|
|
'location' => $request->location,
|
|
'salary' => $request->salary,
|
|
'start_date' => $request->start_date,
|
|
'description' => $request->description,
|
|
'requirements' => $request->requirements,
|
|
'status' => strtolower($request->status),
|
|
]);
|
|
|
|
// Sync skills
|
|
$skillsInput = $request->skills;
|
|
$skillIds = [];
|
|
if (is_array($skillsInput)) {
|
|
foreach ($skillsInput as $skillNameOrId) {
|
|
if (is_numeric($skillNameOrId)) {
|
|
$skillIds[] = (int)$skillNameOrId;
|
|
} else {
|
|
$skill = \App\Models\Skill::firstOrCreate(['name' => trim($skillNameOrId)]);
|
|
$skillIds[] = $skill->id;
|
|
}
|
|
}
|
|
} else if (is_string($skillsInput)) {
|
|
$skillsArray = array_filter(array_map('trim', explode(',', $skillsInput)));
|
|
foreach ($skillsArray as $name) {
|
|
$skill = \App\Models\Skill::firstOrCreate(['name' => $name]);
|
|
$skillIds[] = $skill->id;
|
|
}
|
|
}
|
|
$job->skills()->sync($skillIds);
|
|
|
|
return redirect()->route('employer.jobs')->with('success', 'Job updated successfully.');
|
|
}
|
|
|
|
public function destroy($id)
|
|
{
|
|
$user = $this->resolveCurrentUser();
|
|
if (!$user) {
|
|
return back()->withErrors(['general' => 'User session not found.']);
|
|
}
|
|
|
|
if (!$this->checkJobAccess($user)) {
|
|
return redirect()->route('employer.subscription')
|
|
->with('error', 'Your current plan does not support the Job Apply module. Please upgrade to a Premium plan to access this feature.');
|
|
}
|
|
|
|
$job = JobPost::where('employer_id', $user->id)->where('id', $id)->firstOrFail();
|
|
$job->delete();
|
|
|
|
return redirect()->route('employer.jobs')->with('success', 'Job deleted successfully.');
|
|
}
|
|
|
|
public function close($id)
|
|
{
|
|
$user = $this->resolveCurrentUser();
|
|
if (!$user) {
|
|
return back()->withErrors(['general' => 'User session not found.']);
|
|
}
|
|
|
|
if (!$this->checkJobAccess($user)) {
|
|
return redirect()->route('employer.subscription')
|
|
->with('error', 'Your current plan does not support the Job Apply module. Please upgrade to a Premium plan to access this feature.');
|
|
}
|
|
|
|
$job = JobPost::where('employer_id', $user->id)->where('id', $id)->firstOrFail();
|
|
$job->update(['status' => 'closed']);
|
|
|
|
return redirect()->route('employer.jobs.show', $id)->with('success', 'Job closed successfully.');
|
|
}
|
|
|
|
public function allApplicants(Request $request)
|
|
{
|
|
$user = $this->resolveCurrentUser();
|
|
if (!$user) {
|
|
return redirect()->route('employer.login');
|
|
}
|
|
|
|
if (!$this->checkJobAccess($user)) {
|
|
return redirect()->route('employer.subscription')
|
|
->with('error', 'Your current plan does not support the Job Apply module. Please upgrade to a Premium plan to access this feature.');
|
|
}
|
|
|
|
$jobIds = JobPost::where('employer_id', $user->id)->pluck('id');
|
|
|
|
$dbApplications = JobApplication::whereIn('job_id', $jobIds)
|
|
->with(['worker.skills', 'jobPost'])
|
|
->get();
|
|
|
|
$applicants = $dbApplications->map(function ($app) use ($user) {
|
|
$worker = $app->worker;
|
|
if (!$worker) return null;
|
|
$isSaved = \App\Models\Shortlist::where('employer_id', $user->id)
|
|
->where('worker_id', $worker->id)
|
|
->exists();
|
|
return [
|
|
'id' => $worker->id,
|
|
'application_id' => $app->id,
|
|
'name' => $worker->name,
|
|
'nationality' => $worker->nationality,
|
|
'salary' => (int) $worker->salary,
|
|
'experience' => ($worker->experience_years ?? 3) . ' Years',
|
|
'status' => ucfirst($app->status),
|
|
'notes' => $app->notes,
|
|
'status_history' => $app->status_history ?: [],
|
|
'match_score' => $worker->match_score ?? 90,
|
|
'is_saved' => $isSaved,
|
|
'job_title' => $app->jobPost->title ?? 'N/A',
|
|
'job_id' => $app->job_id,
|
|
];
|
|
})->filter()->values()->toArray();
|
|
|
|
return Inertia::render('Employer/Jobs/Applicants', [
|
|
'job' => null,
|
|
'applicants' => $applicants,
|
|
]);
|
|
}
|
|
|
|
public function shortlistedApplicants(Request $request)
|
|
{
|
|
$user = $this->resolveCurrentUser();
|
|
if (!$user) {
|
|
return redirect()->route('employer.login');
|
|
}
|
|
|
|
if (!$this->checkJobAccess($user)) {
|
|
return redirect()->route('employer.subscription')
|
|
->with('error', 'Your current plan does not support the Job Apply module. Please upgrade to a Premium plan to access this feature.');
|
|
}
|
|
|
|
$jobIds = JobPost::where('employer_id', $user->id)->pluck('id');
|
|
|
|
$dbApplications = JobApplication::whereIn('job_id', $jobIds)
|
|
->where('status', 'shortlisted')
|
|
->with(['worker.skills', 'jobPost'])
|
|
->get();
|
|
|
|
$applicants = $dbApplications->map(function ($app) use ($user) {
|
|
$worker = $app->worker;
|
|
if (!$worker) return null;
|
|
$isSaved = \App\Models\Shortlist::where('employer_id', $user->id)
|
|
->where('worker_id', $worker->id)
|
|
->exists();
|
|
return [
|
|
'id' => $worker->id,
|
|
'application_id' => $app->id,
|
|
'name' => $worker->name,
|
|
'nationality' => $worker->nationality,
|
|
'salary' => (int) $worker->salary,
|
|
'experience' => ($worker->experience_years ?? 3) . ' Years',
|
|
'status' => ucfirst($app->status),
|
|
'notes' => $app->notes,
|
|
'status_history' => $app->status_history ?: [],
|
|
'match_score' => $worker->match_score ?? 90,
|
|
'is_saved' => $isSaved,
|
|
'job_title' => $app->jobPost->title ?? 'N/A',
|
|
'job_id' => $app->job_id,
|
|
];
|
|
})->filter()->values()->toArray();
|
|
|
|
return Inertia::render('Employer/Jobs/Applicants', [
|
|
'job' => null,
|
|
'applicants' => $applicants,
|
|
'isShortlistedOnly' => true,
|
|
]);
|
|
}
|
|
}
|
|
|
|
|