worker api changes hiring, worker/employers api list
This commit is contained in:
parent
52b47cc2ac
commit
e475a7558b
@ -90,30 +90,6 @@ private function formatWorker(Worker $w)
|
|||||||
]
|
]
|
||||||
];
|
];
|
||||||
})->toArray(),
|
})->toArray(),
|
||||||
'documents' => $w->documents->map(function ($doc) {
|
|
||||||
$ocrData = $doc->ocr_data;
|
|
||||||
if ($doc->type === 'visa' && is_array($ocrData)) {
|
|
||||||
unset($ocrData['file_number']);
|
|
||||||
unset($ocrData['name']);
|
|
||||||
unset($ocrData['passport_number']);
|
|
||||||
unset($ocrData['accompanied_by']);
|
|
||||||
unset($ocrData['valid_until']);
|
|
||||||
unset($ocrData['sponsor']);
|
|
||||||
}
|
|
||||||
return [
|
|
||||||
'id' => $doc->id,
|
|
||||||
'worker_id' => $doc->worker_id,
|
|
||||||
'type' => $doc->type,
|
|
||||||
'number' => $doc->number,
|
|
||||||
'issue_date' => $doc->issue_date,
|
|
||||||
'expiry_date' => $doc->expiry_date,
|
|
||||||
'ocr_accuracy' => $doc->ocr_accuracy,
|
|
||||||
'file_path' => $doc->file_path ? url($doc->file_path) : null,
|
|
||||||
'ocr_data' => $ocrData,
|
|
||||||
'created_at' => $doc->created_at?->toISOString(),
|
|
||||||
'updated_at' => $doc->updated_at?->toISOString(),
|
|
||||||
];
|
|
||||||
})->toArray(),
|
|
||||||
'category' => [
|
'category' => [
|
||||||
'id' => 7,
|
'id' => 7,
|
||||||
'name' => 'General Helper',
|
'name' => 'General Helper',
|
||||||
@ -440,10 +416,11 @@ public function getCandidates(Request $request)
|
|||||||
if (!$w) return null;
|
if (!$w) return null;
|
||||||
|
|
||||||
$status = 'Reviewing';
|
$status = 'Reviewing';
|
||||||
if ($app->status === 'hired') $status = 'Hired';
|
$dbStatusLower = strtolower($app->status ?? '');
|
||||||
elseif ($app->status === 'rejected') $status = 'Rejected';
|
if ($dbStatusLower === 'hired') $status = 'Hired';
|
||||||
elseif ($app->status === 'shortlisted' || $app->status === 'offer_sent') $status = 'Offer Sent';
|
elseif ($dbStatusLower === 'rejected') $status = 'Rejected';
|
||||||
elseif ($app->status === 'applied') $status = 'Reviewing';
|
elseif ($dbStatusLower === 'shortlisted' || $dbStatusLower === 'offer_sent' || $dbStatusLower === 'offer sent') $status = 'Offer Sent';
|
||||||
|
elseif ($dbStatusLower === 'applied') $status = 'Reviewing';
|
||||||
else $status = ucfirst($app->status);
|
else $status = ucfirst($app->status);
|
||||||
|
|
||||||
$formattedWorker = $this->formatWorker($w);
|
$formattedWorker = $this->formatWorker($w);
|
||||||
@ -465,9 +442,10 @@ public function getCandidates(Request $request)
|
|||||||
if (!$w) return null;
|
if (!$w) return null;
|
||||||
|
|
||||||
$status = 'Offer Sent';
|
$status = 'Offer Sent';
|
||||||
if ($offer->status === 'accepted') $status = 'Hired';
|
$dbOfferStatusLower = strtolower($offer->status ?? '');
|
||||||
elseif ($offer->status === 'rejected') $status = 'Rejected';
|
if ($dbOfferStatusLower === 'accepted' || $dbOfferStatusLower === 'hired') $status = 'Hired';
|
||||||
elseif ($offer->status === 'pending') $status = 'Offer Sent';
|
elseif ($dbOfferStatusLower === 'rejected') $status = 'Rejected';
|
||||||
|
elseif ($dbOfferStatusLower === 'pending' || $dbOfferStatusLower === 'offer sent' || $dbOfferStatusLower === 'offer_sent') $status = 'Offer Sent';
|
||||||
|
|
||||||
$formattedWorker = $this->formatWorker($w);
|
$formattedWorker = $this->formatWorker($w);
|
||||||
|
|
||||||
@ -480,12 +458,21 @@ public function getCandidates(Request $request)
|
|||||||
]);
|
]);
|
||||||
})->filter()->values()->toArray();
|
})->filter()->values()->toArray();
|
||||||
|
|
||||||
// Merge and sort
|
// Merge and sort (filtering out duplicates for direct offers where already hired via application)
|
||||||
$mergedCandidates = array_merge($selectedWorkers, $directWorkers);
|
$hiredAppWorkerIds = collect($selectedWorkers)
|
||||||
|
->filter(fn($w) => strtolower($w['status'] ?? '') === 'hired')
|
||||||
|
->pluck('worker_id')
|
||||||
|
->toArray();
|
||||||
|
|
||||||
|
$filteredDirectWorkers = array_filter($directWorkers, function($dw) use ($hiredAppWorkerIds) {
|
||||||
|
return $dw && !in_array($dw['worker_id'], $hiredAppWorkerIds);
|
||||||
|
});
|
||||||
|
|
||||||
|
$mergedCandidates = array_merge($selectedWorkers, $filteredDirectWorkers);
|
||||||
|
|
||||||
// Only display Hired candidates in the candidates pipeline (exclude active states)
|
// Only display Hired candidates in the candidates pipeline (exclude active states)
|
||||||
$mergedCandidates = array_values(array_filter($mergedCandidates, function ($c) {
|
$mergedCandidates = array_values(array_filter($mergedCandidates, function ($c) {
|
||||||
return $c && $c['status'] === 'Hired';
|
return $c && strtolower($c['status'] ?? '') === 'hired';
|
||||||
}));
|
}));
|
||||||
|
|
||||||
// Apply filters: gender, skills, languages, nationality, availability, preferred_location, job_type, live_in_out, in_country, visa_status
|
// Apply filters: gender, skills, languages, nationality, availability, preferred_location, job_type, live_in_out, in_country, visa_status
|
||||||
@ -810,6 +797,20 @@ public function hireCandidate(Request $request, $id = null)
|
|||||||
$worker = $application->worker;
|
$worker = $application->worker;
|
||||||
}
|
}
|
||||||
$updatedApplication = $application;
|
$updatedApplication = $application;
|
||||||
|
// Create/update the JobOffer record to satisfy the Worker Employer List API
|
||||||
|
JobOffer::updateOrCreate(
|
||||||
|
[
|
||||||
|
'employer_id' => $employerId,
|
||||||
|
'worker_id' => $application->worker_id,
|
||||||
|
],
|
||||||
|
[
|
||||||
|
'work_date' => $application->jobPost->start_date ?? now(),
|
||||||
|
'location' => $application->jobPost->location ?? 'Dubai',
|
||||||
|
'salary' => $application->jobPost->salary ?? 0,
|
||||||
|
'status' => 'accepted',
|
||||||
|
'notes' => $application->notes ?? 'Hired through Job Application',
|
||||||
|
]
|
||||||
|
);
|
||||||
} else {
|
} else {
|
||||||
// Try targeting by Worker ID directly
|
// Try targeting by Worker ID directly
|
||||||
$worker = Worker::find($targetId);
|
$worker = Worker::find($targetId);
|
||||||
@ -841,6 +842,20 @@ public function hireCandidate(Request $request, $id = null)
|
|||||||
if ($application) {
|
if ($application) {
|
||||||
$application->update(['status' => 'hired']);
|
$application->update(['status' => 'hired']);
|
||||||
$updatedApplication = $application;
|
$updatedApplication = $application;
|
||||||
|
// Create/update the JobOffer record to satisfy the Worker Employer List API
|
||||||
|
JobOffer::updateOrCreate(
|
||||||
|
[
|
||||||
|
'employer_id' => $employerId,
|
||||||
|
'worker_id' => $application->worker_id,
|
||||||
|
],
|
||||||
|
[
|
||||||
|
'work_date' => $application->jobPost->start_date ?? now(),
|
||||||
|
'location' => $application->jobPost->location ?? 'Dubai',
|
||||||
|
'salary' => $application->jobPost->salary ?? 0,
|
||||||
|
'status' => 'accepted',
|
||||||
|
'notes' => $application->notes ?? 'Hired through Job Application',
|
||||||
|
]
|
||||||
|
);
|
||||||
} else {
|
} else {
|
||||||
// Create a new direct job offer and accept it
|
// Create a new direct job offer and accept it
|
||||||
$updatedOffer = JobOffer::create([
|
$updatedOffer = JobOffer::create([
|
||||||
@ -1049,30 +1064,6 @@ public function getWorkerDetail(Request $request, $id)
|
|||||||
]
|
]
|
||||||
];
|
];
|
||||||
})->toArray(),
|
})->toArray(),
|
||||||
'documents' => $w->documents->map(function ($doc) {
|
|
||||||
$ocrData = $doc->ocr_data;
|
|
||||||
if ($doc->type === 'visa' && is_array($ocrData)) {
|
|
||||||
unset($ocrData['file_number']);
|
|
||||||
unset($ocrData['name']);
|
|
||||||
unset($ocrData['passport_number']);
|
|
||||||
unset($ocrData['accompanied_by']);
|
|
||||||
unset($ocrData['valid_until']);
|
|
||||||
unset($ocrData['sponsor']);
|
|
||||||
}
|
|
||||||
return [
|
|
||||||
'id' => $doc->id,
|
|
||||||
'worker_id' => $doc->worker_id,
|
|
||||||
'type' => $doc->type,
|
|
||||||
'number' => $doc->number,
|
|
||||||
'issue_date' => $doc->issue_date,
|
|
||||||
'expiry_date' => $doc->expiry_date,
|
|
||||||
'ocr_accuracy' => $doc->ocr_accuracy,
|
|
||||||
'file_path' => $doc->file_path ? url($doc->file_path) : null,
|
|
||||||
'ocr_data' => $ocrData,
|
|
||||||
'created_at' => $doc->created_at?->toISOString(),
|
|
||||||
'updated_at' => $doc->updated_at?->toISOString(),
|
|
||||||
];
|
|
||||||
})->toArray(),
|
|
||||||
];
|
];
|
||||||
|
|
||||||
return response()->json([
|
return response()->json([
|
||||||
|
|||||||
@ -643,6 +643,20 @@ public function employerUpdateApplicationStatus(Request $request, $id)
|
|||||||
if ($application->worker) {
|
if ($application->worker) {
|
||||||
$application->worker->update(['status' => 'Hired']);
|
$application->worker->update(['status' => 'Hired']);
|
||||||
}
|
}
|
||||||
|
// Create/update the JobOffer record to satisfy the Worker Employer List API
|
||||||
|
\App\Models\JobOffer::updateOrCreate(
|
||||||
|
[
|
||||||
|
'employer_id' => $employer->id,
|
||||||
|
'worker_id' => $application->worker_id,
|
||||||
|
],
|
||||||
|
[
|
||||||
|
'work_date' => $application->jobPost->start_date ?? now(),
|
||||||
|
'location' => $application->jobPost->location ?? 'Dubai',
|
||||||
|
'salary' => $application->jobPost->salary ?? 0,
|
||||||
|
'status' => 'accepted',
|
||||||
|
'notes' => $application->notes ?? 'Hired through Job Application',
|
||||||
|
]
|
||||||
|
);
|
||||||
} elseif ($dbStatus === 'rejected') {
|
} elseif ($dbStatus === 'rejected') {
|
||||||
if ($application->worker) {
|
if ($application->worker) {
|
||||||
$application->worker->update(['status' => 'active']);
|
$application->worker->update(['status' => 'active']);
|
||||||
|
|||||||
@ -4,6 +4,7 @@
|
|||||||
|
|
||||||
use App\Http\Controllers\Controller;
|
use App\Http\Controllers\Controller;
|
||||||
use App\Models\JobOffer;
|
use App\Models\JobOffer;
|
||||||
|
use App\Models\JobApplication;
|
||||||
use App\Models\Worker;
|
use App\Models\Worker;
|
||||||
use App\Models\WorkerDocument;
|
use App\Models\WorkerDocument;
|
||||||
use App\Models\ProfileView;
|
use App\Models\ProfileView;
|
||||||
@ -587,13 +588,29 @@ public function getDashboard(Request $request)
|
|||||||
|
|
||||||
// 3. Currently working sponsor/employer
|
// 3. Currently working sponsor/employer
|
||||||
$currentSponsor = null;
|
$currentSponsor = null;
|
||||||
|
$emp = null;
|
||||||
|
$hiredAt = null;
|
||||||
|
|
||||||
$acceptedOffer = JobOffer::where('worker_id', $worker->id)
|
$acceptedOffer = JobOffer::where('worker_id', $worker->id)
|
||||||
->where('status', 'accepted')
|
->whereIn(DB::raw('LOWER(status)'), ['accepted', 'hired'])
|
||||||
->with(['employer.employerProfile', 'employer.employerReviews.worker'])
|
->with(['employer.employerProfile', 'employer.employerReviews.worker'])
|
||||||
->first();
|
->first();
|
||||||
|
|
||||||
if ($acceptedOffer && $acceptedOffer->employer) {
|
if ($acceptedOffer) {
|
||||||
$emp = $acceptedOffer->employer;
|
$emp = $acceptedOffer->employer;
|
||||||
|
$hiredAt = $acceptedOffer->updated_at;
|
||||||
|
} else {
|
||||||
|
$hiredApplication = \App\Models\JobApplication::where('worker_id', $worker->id)
|
||||||
|
->whereIn(DB::raw('LOWER(status)'), ['hired'])
|
||||||
|
->with(['jobPost.employer.employerProfile', 'jobPost.employer.employerReviews.worker'])
|
||||||
|
->first();
|
||||||
|
if ($hiredApplication && $hiredApplication->jobPost && $hiredApplication->jobPost->employer) {
|
||||||
|
$emp = $hiredApplication->jobPost->employer;
|
||||||
|
$hiredAt = $hiredApplication->updated_at;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($emp) {
|
||||||
$empProfile = $emp->employerProfile;
|
$empProfile = $emp->employerProfile;
|
||||||
$currentSponsor = [
|
$currentSponsor = [
|
||||||
'id' => $emp->id,
|
'id' => $emp->id,
|
||||||
@ -602,9 +619,9 @@ public function getDashboard(Request $request)
|
|||||||
'nationality' => $empProfile->nationality ?? 'UAE',
|
'nationality' => $empProfile->nationality ?? 'UAE',
|
||||||
'city' => $empProfile->city ?? 'Dubai',
|
'city' => $empProfile->city ?? 'Dubai',
|
||||||
'email' => $emp->email,
|
'email' => $emp->email,
|
||||||
'phone' => $emp->phone,
|
'phone' => $empProfile->phone ?? $emp->phone,
|
||||||
'hired_at' => $acceptedOffer->updated_at->toIso8601String(),
|
'hired_at' => $hiredAt ? $hiredAt->toIso8601String() : null,
|
||||||
'hired_at_formatted' => $acceptedOffer->updated_at->format('M d, Y'),
|
'hired_at_formatted' => $hiredAt ? $hiredAt->format('M d, Y') : null,
|
||||||
'rating' => $emp->rating,
|
'rating' => $emp->rating,
|
||||||
'reviews_count' => $emp->reviews_count,
|
'reviews_count' => $emp->reviews_count,
|
||||||
'reviews' => $emp->employerReviews,
|
'reviews' => $emp->employerReviews,
|
||||||
@ -687,45 +704,22 @@ public function getEmployers(Request $request)
|
|||||||
$worker = $request->attributes->get('worker');
|
$worker = $request->attributes->get('worker');
|
||||||
|
|
||||||
try {
|
try {
|
||||||
$query = JobOffer::where('worker_id', $worker->id)
|
// Fetch JobOffers
|
||||||
->with(['employer.employerProfile', 'employer.employerReviews.worker']);
|
$offers = JobOffer::where('worker_id', $worker->id)
|
||||||
|
->with(['employer.employerProfile', 'employer.employerReviews.worker'])
|
||||||
|
->get();
|
||||||
|
|
||||||
// Filtering by status
|
// Fetch hired JobApplications
|
||||||
if ($request->filled('status')) {
|
$applications = JobApplication::where('worker_id', $worker->id)
|
||||||
$statusInput = strtolower($request->status);
|
->where('status', 'hired')
|
||||||
if ($statusInput === 'active') {
|
->with(['jobPost.employer.employerProfile', 'jobPost.employer.employerReviews.worker'])
|
||||||
$query->where('status', 'accepted');
|
->get();
|
||||||
} else {
|
|
||||||
$query->where('status', $statusInput);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Sorting
|
// Map JobOffers
|
||||||
$sortBy = $request->input('sort_by', 'joining_date');
|
$mappedOffers = $offers->map(function ($offer) {
|
||||||
$sortOrder = strtolower($request->input('sort_order', 'desc')) === 'asc' ? 'asc' : 'desc';
|
|
||||||
|
|
||||||
$sortColumn = 'work_date';
|
|
||||||
if ($sortBy === 'salary') {
|
|
||||||
$sortColumn = 'salary';
|
|
||||||
} elseif ($sortBy === 'status') {
|
|
||||||
$sortColumn = 'status';
|
|
||||||
} elseif ($sortBy === 'created_at') {
|
|
||||||
$sortColumn = 'created_at';
|
|
||||||
}
|
|
||||||
|
|
||||||
// Always display the current active employer (status = accepted) at the top of the list,
|
|
||||||
// followed by other employers ordered by the chosen sort column.
|
|
||||||
$query->orderByRaw("CASE WHEN status = 'accepted' THEN 0 ELSE 1 END ASC")
|
|
||||||
->orderBy($sortColumn, $sortOrder);
|
|
||||||
|
|
||||||
$perPage = (int) $request->input('per_page', 15);
|
|
||||||
$paginator = $query->paginate($perPage);
|
|
||||||
|
|
||||||
$employers = collect($paginator->items())->map(function ($offer) {
|
|
||||||
$emp = $offer->employer;
|
$emp = $offer->employer;
|
||||||
$empProfile = $emp ? $emp->employerProfile : null;
|
$empProfile = $emp ? $emp->employerProfile : null;
|
||||||
|
|
||||||
// Map 'accepted' -> 'Active' (and others to Title Case for nice presentation)
|
|
||||||
$statusFormatted = $offer->status;
|
$statusFormatted = $offer->status;
|
||||||
if (strtolower($offer->status) === 'accepted') {
|
if (strtolower($offer->status) === 'accepted') {
|
||||||
$statusFormatted = 'Active';
|
$statusFormatted = 'Active';
|
||||||
@ -736,10 +730,13 @@ public function getEmployers(Request $request)
|
|||||||
return [
|
return [
|
||||||
'offer_id' => $offer->id,
|
'offer_id' => $offer->id,
|
||||||
'joining_date' => $offer->work_date ? $offer->work_date->format('Y-m-d') : null,
|
'joining_date' => $offer->work_date ? $offer->work_date->format('Y-m-d') : null,
|
||||||
|
'joining_date_raw' => $offer->work_date,
|
||||||
'location' => $offer->location,
|
'location' => $offer->location,
|
||||||
'salary' => (int) $offer->salary,
|
'salary' => (int) $offer->salary,
|
||||||
'status' => $statusFormatted,
|
'status' => $statusFormatted,
|
||||||
|
'status_raw' => $offer->status,
|
||||||
'notes' => $offer->notes,
|
'notes' => $offer->notes,
|
||||||
|
'created_at' => $offer->created_at,
|
||||||
'employer' => $emp ? [
|
'employer' => $emp ? [
|
||||||
'id' => $emp->id,
|
'id' => $emp->id,
|
||||||
'name' => $emp->name,
|
'name' => $emp->name,
|
||||||
@ -755,15 +752,120 @@ public function getEmployers(Request $request)
|
|||||||
];
|
];
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Map JobApplications
|
||||||
|
$mappedApps = $applications->map(function ($app) {
|
||||||
|
$job = $app->jobPost;
|
||||||
|
$emp = $job ? $job->employer : null;
|
||||||
|
$empProfile = $emp ? $emp->employerProfile : null;
|
||||||
|
|
||||||
|
return [
|
||||||
|
'offer_id' => $app->id,
|
||||||
|
'joining_date' => $app->updated_at ? $app->updated_at->format('Y-m-d') : null,
|
||||||
|
'joining_date_raw' => $app->updated_at,
|
||||||
|
'location' => $job->location ?? null,
|
||||||
|
'salary' => $job ? (int) $job->salary : 0,
|
||||||
|
'status' => 'Active',
|
||||||
|
'status_raw' => 'accepted',
|
||||||
|
'notes' => $app->notes,
|
||||||
|
'created_at' => $app->created_at,
|
||||||
|
'employer' => $emp ? [
|
||||||
|
'id' => $emp->id,
|
||||||
|
'name' => $emp->name,
|
||||||
|
'email' => $emp->email,
|
||||||
|
'phone' => $emp->phone,
|
||||||
|
'company_name' => $empProfile->company_name ?? 'Private Sponsor',
|
||||||
|
'city' => $empProfile->city ?? null,
|
||||||
|
'nationality' => $empProfile->nationality ?? null,
|
||||||
|
'rating' => $emp->rating,
|
||||||
|
'reviews_count' => $emp->reviews_count,
|
||||||
|
'reviews' => $emp->employerReviews,
|
||||||
|
] : null
|
||||||
|
];
|
||||||
|
});
|
||||||
|
|
||||||
|
// Merge and de-duplicate by employer id
|
||||||
|
$merged = $mappedOffers->concat($mappedApps)->unique(function ($item) {
|
||||||
|
return $item['employer']['id'] ?? null;
|
||||||
|
})->values();
|
||||||
|
|
||||||
|
// Filtering by status
|
||||||
|
if ($request->filled('status')) {
|
||||||
|
$statusInput = strtolower($request->status);
|
||||||
|
$merged = $merged->filter(function ($item) use ($statusInput) {
|
||||||
|
$rawStatus = strtolower($item['status_raw'] ?? '');
|
||||||
|
if ($statusInput === 'active') {
|
||||||
|
return $rawStatus === 'accepted' || $rawStatus === 'hired';
|
||||||
|
}
|
||||||
|
return $rawStatus === $statusInput;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Sorting
|
||||||
|
$sortBy = $request->input('sort_by', 'joining_date');
|
||||||
|
$sortOrder = strtolower($request->input('sort_order', 'desc')) === 'asc' ? 'asc' : 'desc';
|
||||||
|
|
||||||
|
$merged = $merged->sort(function ($a, $b) use ($sortBy, $sortOrder) {
|
||||||
|
// Priority: status = 'Active' (accepted/hired) comes first
|
||||||
|
$aActive = $a['status'] === 'Active' ? 0 : 1;
|
||||||
|
$bActive = $b['status'] === 'Active' ? 0 : 1;
|
||||||
|
|
||||||
|
if ($aActive !== $bActive) {
|
||||||
|
return $aActive <=> $bActive;
|
||||||
|
}
|
||||||
|
|
||||||
|
$valA = null;
|
||||||
|
$valB = null;
|
||||||
|
|
||||||
|
if ($sortBy === 'salary') {
|
||||||
|
$valA = $a['salary'];
|
||||||
|
$valB = $b['salary'];
|
||||||
|
} elseif ($sortBy === 'status') {
|
||||||
|
$valA = $a['status'];
|
||||||
|
$valB = $b['status'];
|
||||||
|
} elseif ($sortBy === 'created_at') {
|
||||||
|
$valA = $a['created_at'];
|
||||||
|
$valB = $b['created_at'];
|
||||||
|
} else {
|
||||||
|
$valA = $a['joining_date_raw'];
|
||||||
|
$valB = $b['joining_date_raw'];
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($valA == $valB) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($sortOrder === 'asc') {
|
||||||
|
return $valA > $valB ? 1 : -1;
|
||||||
|
} else {
|
||||||
|
return $valA < $valB ? 1 : -1;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// Pagination
|
||||||
|
$total = $merged->count();
|
||||||
|
$perPage = (int) $request->input('per_page', 15);
|
||||||
|
$currentPage = (int) $request->input('page', 1);
|
||||||
|
$lastPage = max(1, (int) ceil($total / $perPage));
|
||||||
|
|
||||||
|
$pageItems = $merged->slice(($currentPage - 1) * $perPage, $perPage)->values();
|
||||||
|
|
||||||
|
// Clean up temporary sorting/filtering keys
|
||||||
|
$employers = $pageItems->map(function ($item) {
|
||||||
|
unset($item['joining_date_raw']);
|
||||||
|
unset($item['status_raw']);
|
||||||
|
unset($item['created_at']);
|
||||||
|
return $item;
|
||||||
|
});
|
||||||
|
|
||||||
return response()->json([
|
return response()->json([
|
||||||
'success' => true,
|
'success' => true,
|
||||||
'data' => [
|
'data' => [
|
||||||
'employers' => $employers,
|
'employers' => $employers,
|
||||||
'pagination' => [
|
'pagination' => [
|
||||||
'total' => $paginator->total(),
|
'total' => $total,
|
||||||
'per_page' => $paginator->perPage(),
|
'per_page' => $perPage,
|
||||||
'current_page' => $paginator->currentPage(),
|
'current_page' => $currentPage,
|
||||||
'last_page' => $paginator->lastPage(),
|
'last_page' => $lastPage,
|
||||||
]
|
]
|
||||||
]
|
]
|
||||||
], 200);
|
], 200);
|
||||||
@ -976,7 +1078,22 @@ public function getEmployerProfile(Request $request, $id)
|
|||||||
->where('worker_id', $worker->id)
|
->where('worker_id', $worker->id)
|
||||||
->exists();
|
->exists();
|
||||||
|
|
||||||
if ($hasOffer || $hasApplication || $hasConversation) {
|
// If the worker has 'Hired' status (case-insensitive) and is hired by this employer
|
||||||
|
$isHiredByThisEmployer = false;
|
||||||
|
if (strtolower($worker->status ?? '') === 'hired') {
|
||||||
|
$isHiredByThisEmployer = \App\Models\JobOffer::where('employer_id', $employer->id)
|
||||||
|
->where('worker_id', $worker->id)
|
||||||
|
->whereIn(DB::raw('LOWER(status)'), ['accepted', 'hired'])
|
||||||
|
->exists() ||
|
||||||
|
\App\Models\JobApplication::where('worker_id', $worker->id)
|
||||||
|
->whereHas('jobPost', function ($query) use ($employer) {
|
||||||
|
$query->where('employer_id', $employer->id);
|
||||||
|
})
|
||||||
|
->where(DB::raw('LOWER(status)'), 'hired')
|
||||||
|
->exists();
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($hasOffer || $hasApplication || $hasConversation || $isHiredByThisEmployer) {
|
||||||
$hasAccess = true;
|
$hasAccess = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -55,10 +55,11 @@ public function index(Request $request)
|
|||||||
|
|
||||||
// Map DB status to UI friendly status
|
// Map DB status to UI friendly status
|
||||||
$status = 'Reviewing';
|
$status = 'Reviewing';
|
||||||
if ($app->status === 'hired') $status = 'Hired';
|
$dbStatusLower = strtolower($app->status ?? '');
|
||||||
elseif ($app->status === 'rejected') $status = 'Rejected';
|
if ($dbStatusLower === 'hired') $status = 'Hired';
|
||||||
elseif ($app->status === 'shortlisted' || $app->status === 'offer_sent') $status = 'Offer Sent';
|
elseif ($dbStatusLower === 'rejected') $status = 'Rejected';
|
||||||
elseif ($app->status === 'applied') $status = 'Reviewing';
|
elseif ($dbStatusLower === 'shortlisted' || $dbStatusLower === 'offer_sent' || $dbStatusLower === 'offer sent') $status = 'Offer Sent';
|
||||||
|
elseif ($dbStatusLower === 'applied') $status = 'Reviewing';
|
||||||
else $status = ucfirst($app->status);
|
else $status = ucfirst($app->status);
|
||||||
|
|
||||||
return [
|
return [
|
||||||
@ -83,15 +84,27 @@ public function index(Request $request)
|
|||||||
->with('worker')
|
->with('worker')
|
||||||
->get();
|
->get();
|
||||||
|
|
||||||
$directWorkers = $directOffers->map(function ($offer) {
|
// Get worker IDs of all hired job applications for this employer to prevent duplication
|
||||||
|
$hiredAppWorkerIds = collect($applications)
|
||||||
|
->filter(fn($app) => strtolower($app->status ?? '') === 'hired')
|
||||||
|
->pluck('worker_id')
|
||||||
|
->toArray();
|
||||||
|
|
||||||
|
$directWorkers = $directOffers->map(function ($offer) use ($hiredAppWorkerIds) {
|
||||||
$w = $offer->worker;
|
$w = $offer->worker;
|
||||||
if (!$w) return null;
|
if (!$w) return null;
|
||||||
|
|
||||||
|
// Avoid duplicate hired entries if worker was hired via standard application
|
||||||
|
$dbOfferStatusLower = strtolower($offer->status ?? '');
|
||||||
|
if (($dbOfferStatusLower === 'accepted' || $dbOfferStatusLower === 'hired') && in_array($w->id, $hiredAppWorkerIds)) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
// Map DB offer status to UI friendly status
|
// Map DB offer status to UI friendly status
|
||||||
$status = 'Offer Sent';
|
$status = 'Offer Sent';
|
||||||
if ($offer->status === 'accepted') $status = 'Hired';
|
if ($dbOfferStatusLower === 'accepted' || $dbOfferStatusLower === 'hired') $status = 'Hired';
|
||||||
elseif ($offer->status === 'rejected') $status = 'Rejected';
|
elseif ($dbOfferStatusLower === 'rejected') $status = 'Rejected';
|
||||||
elseif ($offer->status === 'pending') $status = 'Offer Sent';
|
elseif ($dbOfferStatusLower === 'pending' || $dbOfferStatusLower === 'offer sent' || $dbOfferStatusLower === 'offer_sent') $status = 'Offer Sent';
|
||||||
|
|
||||||
return [
|
return [
|
||||||
'id' => 'offer_' . $offer->id, // Unique string key prefix to prevent clashes
|
'id' => 'offer_' . $offer->id, // Unique string key prefix to prevent clashes
|
||||||
@ -115,7 +128,7 @@ public function index(Request $request)
|
|||||||
|
|
||||||
// Only display Hired candidates in the candidates pipeline
|
// Only display Hired candidates in the candidates pipeline
|
||||||
$mergedCandidates = array_values(array_filter($mergedCandidates, function ($w) {
|
$mergedCandidates = array_values(array_filter($mergedCandidates, function ($w) {
|
||||||
return $w && $w['status'] === 'Hired';
|
return $w && strtolower($w['status'] ?? '') === 'hired';
|
||||||
}));
|
}));
|
||||||
|
|
||||||
// Apply filters: preferred_location, job_type, live_in_out, nationality, in_country, visa_status
|
// Apply filters: preferred_location, job_type, live_in_out, nationality, in_country, visa_status
|
||||||
@ -411,6 +424,20 @@ public function updateStatus(Request $request, $id)
|
|||||||
if ($app->worker) {
|
if ($app->worker) {
|
||||||
$app->worker->update(['status' => 'Hired']);
|
$app->worker->update(['status' => 'Hired']);
|
||||||
}
|
}
|
||||||
|
// Create/update the JobOffer record to satisfy the Worker Employer List API
|
||||||
|
JobOffer::updateOrCreate(
|
||||||
|
[
|
||||||
|
'employer_id' => $user->id,
|
||||||
|
'worker_id' => $app->worker_id,
|
||||||
|
],
|
||||||
|
[
|
||||||
|
'work_date' => $app->jobPost->start_date ?? now(),
|
||||||
|
'location' => $app->jobPost->location ?? 'Dubai',
|
||||||
|
'salary' => $app->jobPost->salary ?? 0,
|
||||||
|
'status' => 'accepted',
|
||||||
|
'notes' => $app->notes ?? 'Hired through Job Application',
|
||||||
|
]
|
||||||
|
);
|
||||||
} elseif ($dbStatus === 'rejected') {
|
} elseif ($dbStatus === 'rejected') {
|
||||||
if ($app->worker) {
|
if ($app->worker) {
|
||||||
$app->worker->update(['status' => 'active']);
|
$app->worker->update(['status' => 'active']);
|
||||||
|
|||||||
@ -78,7 +78,7 @@ export default function SelectedCandidates({
|
|||||||
|
|
||||||
// Apply Client-Side Filters
|
// Apply Client-Side Filters
|
||||||
const filteredWorkers = useMemo(() => {
|
const filteredWorkers = useMemo(() => {
|
||||||
let list = (selectedWorkers || []).filter(w => w.status !== 'Searching');
|
let list = (workers || []).filter(w => w.status !== 'Searching');
|
||||||
|
|
||||||
if (searchTerm.trim() !== '') {
|
if (searchTerm.trim() !== '') {
|
||||||
const query = searchTerm.toLowerCase();
|
const query = searchTerm.toLowerCase();
|
||||||
@ -134,7 +134,7 @@ export default function SelectedCandidates({
|
|||||||
}
|
}
|
||||||
|
|
||||||
return list;
|
return list;
|
||||||
}, [selectedWorkers, searchTerm, filterProfession, filterLocation, filterJobType, filterAccommodation, filterNationalities, filterGender, filterInCountry, filterVisaType, filterSkills, filterLanguages, maxSalary]);
|
}, [workers, searchTerm, filterProfession, filterLocation, filterJobType, filterAccommodation, filterNationalities, filterGender, filterInCountry, filterVisaType, filterSkills, filterLanguages, maxSalary]);
|
||||||
|
|
||||||
const resetFilters = () => {
|
const resetFilters = () => {
|
||||||
setFilterProfession('All Professions');
|
setFilterProfession('All Professions');
|
||||||
|
|||||||
@ -541,11 +541,8 @@ public function test_worker_list_api_has_full_details_and_pagination()
|
|||||||
$this->assertCount(1, $matchingWorker['skills']);
|
$this->assertCount(1, $matchingWorker['skills']);
|
||||||
$this->assertEquals('Ironing', $matchingWorker['skills'][0]['name']);
|
$this->assertEquals('Ironing', $matchingWorker['skills'][0]['name']);
|
||||||
|
|
||||||
// Check documents object array
|
// Check documents object array is not present
|
||||||
$this->assertIsArray($matchingWorker['documents']);
|
$this->assertArrayNotHasKey('documents', $matchingWorker);
|
||||||
$this->assertCount(2, $matchingWorker['documents']);
|
|
||||||
$this->assertEquals('passport', $matchingWorker['documents'][0]['type']);
|
|
||||||
$this->assertEquals('visa', $matchingWorker['documents'][1]['type']);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public function test_worker_detail_api_has_full_details_without_religion_and_cleaned_ocr_data()
|
public function test_worker_detail_api_has_full_details_without_religion_and_cleaned_ocr_data()
|
||||||
@ -618,16 +615,7 @@ public function test_worker_detail_api_has_full_details_without_religion_and_cle
|
|||||||
// Assert religion is removed
|
// Assert religion is removed
|
||||||
$this->assertArrayNotHasKey('religion', $wData);
|
$this->assertArrayNotHasKey('religion', $wData);
|
||||||
|
|
||||||
// Check documents object array
|
// Check documents object array is not present
|
||||||
$this->assertIsArray($wData['documents']);
|
$this->assertArrayNotHasKey('documents', $wData);
|
||||||
$this->assertCount(2, $wData['documents']);
|
|
||||||
|
|
||||||
// Assert visa ocr_data is cleaned up
|
|
||||||
$visaDoc = collect($wData['documents'])->firstWhere('type', 'visa');
|
|
||||||
$this->assertNotNull($visaDoc);
|
|
||||||
$this->assertArrayNotHasKey('sponsor', $visaDoc['ocr_data']);
|
|
||||||
$this->assertArrayNotHasKey('valid_until', $visaDoc['ocr_data']);
|
|
||||||
$this->assertEquals('some sponsor name', $visaDoc['ocr_data']['sponsor_name']);
|
|
||||||
$this->assertEquals('2024-02-15', $visaDoc['ocr_data']['expiry_date']);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -404,4 +404,196 @@ public function test_worker_can_view_employer_profile_without_permissions_masks_
|
|||||||
$response->assertJsonPath('data.employer.email', null); // Masked/null since no connection exists
|
$response->assertJsonPath('data.employer.email', null); // Masked/null since no connection exists
|
||||||
$response->assertJsonPath('data.employer.phone', null);
|
$response->assertJsonPath('data.employer.phone', null);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function test_worker_hired_via_job_application_appears_in_employers_list()
|
||||||
|
{
|
||||||
|
// 1. Create a worker
|
||||||
|
$worker = Worker::create([
|
||||||
|
'name' => 'Hired Worker',
|
||||||
|
'email' => 'hiredworker@example.com',
|
||||||
|
'phone' => '+971501234567',
|
||||||
|
'language' => 'HI',
|
||||||
|
'password' => bcrypt('password'),
|
||||||
|
'nationality' => 'Indian',
|
||||||
|
'age' => 25,
|
||||||
|
'salary' => 1500,
|
||||||
|
'availability' => 'Immediate',
|
||||||
|
'experience' => 'Not Specified',
|
||||||
|
'religion' => 'Not Specified',
|
||||||
|
'bio' => 'Test',
|
||||||
|
'verified' => true,
|
||||||
|
'status' => 'active',
|
||||||
|
'api_token' => 'worker-hired-token',
|
||||||
|
]);
|
||||||
|
|
||||||
|
WorkerDocument::create([
|
||||||
|
'worker_id' => $worker->id,
|
||||||
|
'type' => 'passport',
|
||||||
|
'number' => '123456',
|
||||||
|
'file_path' => 'passports/test.pdf',
|
||||||
|
]);
|
||||||
|
|
||||||
|
// 2. Create employer with active subscription
|
||||||
|
$employer = User::create([
|
||||||
|
'name' => 'Premium Employer',
|
||||||
|
'email' => 'premium_emp@example.com',
|
||||||
|
'password' => bcrypt('password'),
|
||||||
|
'role' => 'employer',
|
||||||
|
'subscription_status' => 'active',
|
||||||
|
'api_token' => 'premium_employer_token',
|
||||||
|
]);
|
||||||
|
|
||||||
|
EmployerProfile::create([
|
||||||
|
'user_id' => $employer->id,
|
||||||
|
'company_name' => 'Premium Corp Ltd',
|
||||||
|
'nationality' => 'UAE',
|
||||||
|
'city' => 'Abu Dhabi',
|
||||||
|
]);
|
||||||
|
|
||||||
|
\App\Models\Subscription::create([
|
||||||
|
'user_id' => $employer->id,
|
||||||
|
'plan_id' => 'premium',
|
||||||
|
'amount_aed' => 100.00,
|
||||||
|
'starts_at' => now(),
|
||||||
|
'expires_at' => now()->addDays(30),
|
||||||
|
'status' => 'active',
|
||||||
|
]);
|
||||||
|
|
||||||
|
// 3. Create a JobPost
|
||||||
|
$job = \App\Models\JobPost::create([
|
||||||
|
'employer_id' => $employer->id,
|
||||||
|
'title' => 'Housekeeper Needed',
|
||||||
|
'workers_needed' => 1,
|
||||||
|
'location' => 'Abu Dhabi',
|
||||||
|
'salary' => 2000,
|
||||||
|
'job_type' => 'Full Time',
|
||||||
|
'start_date' => now()->addDays(5),
|
||||||
|
'description' => 'Test job',
|
||||||
|
'requirements' => 'None',
|
||||||
|
'status' => 'active',
|
||||||
|
]);
|
||||||
|
|
||||||
|
// 4. Create JobApplication
|
||||||
|
$application = \App\Models\JobApplication::create([
|
||||||
|
'job_id' => $job->id,
|
||||||
|
'worker_id' => $worker->id,
|
||||||
|
'status' => 'applied',
|
||||||
|
'notes' => 'Looking forward to work',
|
||||||
|
]);
|
||||||
|
|
||||||
|
// Verify employer list is empty for the worker initially
|
||||||
|
$response = $this->withHeaders([
|
||||||
|
'Authorization' => 'Bearer worker-hired-token',
|
||||||
|
])->getJson('/api/workers/employers');
|
||||||
|
|
||||||
|
$response->assertStatus(200);
|
||||||
|
$response->assertJsonCount(0, 'data.employers');
|
||||||
|
|
||||||
|
// 5. Employer hires candidate via API
|
||||||
|
$response = $this->postJson("/api/employers/applications/{$application->id}/status", [
|
||||||
|
'status' => 'hired',
|
||||||
|
'notes' => 'Hired and approved'
|
||||||
|
], [
|
||||||
|
'Authorization' => 'Bearer premium_employer_token'
|
||||||
|
]);
|
||||||
|
|
||||||
|
$response->assertStatus(200);
|
||||||
|
|
||||||
|
// Verify that the JobOffer with accepted status has been created
|
||||||
|
$this->assertDatabaseHas('job_offers', [
|
||||||
|
'employer_id' => $employer->id,
|
||||||
|
'worker_id' => $worker->id,
|
||||||
|
'status' => 'accepted',
|
||||||
|
]);
|
||||||
|
|
||||||
|
// 6. Hit the worker employers API and assert that the employer is now listed
|
||||||
|
$response = $this->withHeaders([
|
||||||
|
'Authorization' => 'Bearer worker-hired-token',
|
||||||
|
])->getJson('/api/workers/employers');
|
||||||
|
|
||||||
|
$response->assertStatus(200);
|
||||||
|
$response->assertJsonCount(1, 'data.employers');
|
||||||
|
$response->assertJsonPath('data.employers.0.employer.name', 'Premium Employer');
|
||||||
|
$response->assertJsonPath('data.employers.0.employer.company_name', 'Premium Corp Ltd');
|
||||||
|
$response->assertJsonPath('data.employers.0.status', 'Active');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function test_worker_hired_via_job_application_appears_without_job_offer()
|
||||||
|
{
|
||||||
|
// 1. Create a worker
|
||||||
|
$worker = Worker::create([
|
||||||
|
'name' => 'Hired Worker Legacy',
|
||||||
|
'email' => 'legacyworker@example.com',
|
||||||
|
'phone' => '+971501234560',
|
||||||
|
'language' => 'HI',
|
||||||
|
'password' => bcrypt('password'),
|
||||||
|
'nationality' => 'Indian',
|
||||||
|
'age' => 25,
|
||||||
|
'salary' => 1500,
|
||||||
|
'availability' => 'Immediate',
|
||||||
|
'experience' => 'Not Specified',
|
||||||
|
'religion' => 'Not Specified',
|
||||||
|
'bio' => 'Test',
|
||||||
|
'verified' => true,
|
||||||
|
'status' => 'active',
|
||||||
|
'api_token' => 'worker-legacy-token',
|
||||||
|
]);
|
||||||
|
|
||||||
|
WorkerDocument::create([
|
||||||
|
'worker_id' => $worker->id,
|
||||||
|
'type' => 'passport',
|
||||||
|
'number' => '123456',
|
||||||
|
'file_path' => 'passports/test.pdf',
|
||||||
|
]);
|
||||||
|
|
||||||
|
// 2. Create employer
|
||||||
|
$employer = User::create([
|
||||||
|
'name' => 'Legacy Employer',
|
||||||
|
'email' => 'legacy_emp@example.com',
|
||||||
|
'password' => bcrypt('password'),
|
||||||
|
'role' => 'employer',
|
||||||
|
'subscription_status' => 'active',
|
||||||
|
'api_token' => 'legacy_employer_token',
|
||||||
|
]);
|
||||||
|
|
||||||
|
EmployerProfile::create([
|
||||||
|
'user_id' => $employer->id,
|
||||||
|
'company_name' => 'Legacy Corp Ltd',
|
||||||
|
'nationality' => 'UAE',
|
||||||
|
'city' => 'Dubai',
|
||||||
|
]);
|
||||||
|
|
||||||
|
// 3. Create a JobPost
|
||||||
|
$job = \App\Models\JobPost::create([
|
||||||
|
'employer_id' => $employer->id,
|
||||||
|
'title' => 'Housekeeper Needed Legacy',
|
||||||
|
'workers_needed' => 1,
|
||||||
|
'location' => 'Dubai',
|
||||||
|
'salary' => 2000,
|
||||||
|
'job_type' => 'Full Time',
|
||||||
|
'start_date' => now()->addDays(5),
|
||||||
|
'description' => 'Test job',
|
||||||
|
'requirements' => 'None',
|
||||||
|
'status' => 'active',
|
||||||
|
]);
|
||||||
|
|
||||||
|
// 4. Create JobApplication with status 'hired' directly, and NO JobOffer
|
||||||
|
$application = \App\Models\JobApplication::create([
|
||||||
|
'job_id' => $job->id,
|
||||||
|
'worker_id' => $worker->id,
|
||||||
|
'status' => 'hired',
|
||||||
|
'notes' => 'Hired directly in DB previously',
|
||||||
|
]);
|
||||||
|
|
||||||
|
// 5. Hit the worker employers API and assert that the employer is listed
|
||||||
|
$response = $this->withHeaders([
|
||||||
|
'Authorization' => 'Bearer worker-legacy-token',
|
||||||
|
])->getJson('/api/workers/employers');
|
||||||
|
|
||||||
|
$response->assertStatus(200);
|
||||||
|
$response->assertJsonCount(1, 'data.employers');
|
||||||
|
$response->assertJsonPath('data.employers.0.employer.name', 'Legacy Employer');
|
||||||
|
$response->assertJsonPath('data.employers.0.employer.company_name', 'Legacy Corp Ltd');
|
||||||
|
$response->assertJsonPath('data.employers.0.status', 'Active');
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -507,4 +507,101 @@ public function test_job_hiring_workflow_enhancements()
|
|||||||
'id' => $app->id
|
'id' => $app->id
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function test_hired_worker_visibility_and_profile_access()
|
||||||
|
{
|
||||||
|
// Create EmployerProfile with a phone number
|
||||||
|
\App\Models\EmployerProfile::create([
|
||||||
|
'user_id' => $this->employer->id,
|
||||||
|
'company_name' => 'Premium Sponsoring Group',
|
||||||
|
'phone' => '971501234567',
|
||||||
|
'city' => 'Dubai',
|
||||||
|
'nationality' => 'UAE',
|
||||||
|
]);
|
||||||
|
|
||||||
|
// 1. Create a job post
|
||||||
|
$job = JobPost::create([
|
||||||
|
'employer_id' => $this->employer->id,
|
||||||
|
'title' => 'Special Hired Job',
|
||||||
|
'location' => 'Dubai Jumeirah',
|
||||||
|
'salary' => 4500,
|
||||||
|
'workers_needed' => 1,
|
||||||
|
'job_type' => 'Full Time',
|
||||||
|
'start_date' => now()->addDays(5)->format('Y-m-d'),
|
||||||
|
'description' => 'A special job details description',
|
||||||
|
'status' => 'active',
|
||||||
|
]);
|
||||||
|
|
||||||
|
// 2. Worker applies for the job
|
||||||
|
$response = $this->postJson("/api/workers/jobs/{$job->id}/apply", [], [
|
||||||
|
'Authorization' => 'Bearer worker_api_token'
|
||||||
|
]);
|
||||||
|
$response->assertStatus(201);
|
||||||
|
$app = JobApplication::first();
|
||||||
|
|
||||||
|
// 3. Employer hires the worker by setting status to 'hired' (lowercase, as in the database)
|
||||||
|
$response = $this->postJson("/api/employers/applications/{$app->id}/status", [
|
||||||
|
'status' => 'hired',
|
||||||
|
'notes' => 'Hired through job flow'
|
||||||
|
], [
|
||||||
|
'Authorization' => 'Bearer employer_api_token'
|
||||||
|
]);
|
||||||
|
$response->assertStatus(200);
|
||||||
|
|
||||||
|
// Verify status updates
|
||||||
|
$this->assertDatabaseHas('job_applications', [
|
||||||
|
'id' => $app->id,
|
||||||
|
'status' => 'hired',
|
||||||
|
]);
|
||||||
|
$this->assertDatabaseHas('workers', [
|
||||||
|
'id' => $this->worker->id,
|
||||||
|
'status' => 'Hired',
|
||||||
|
]);
|
||||||
|
|
||||||
|
// 4. Verify candidate is visible in Hired list (web: CandidateController::index)
|
||||||
|
$response = $this->actingAs($this->employer)
|
||||||
|
->withSession(['user' => $this->employer])
|
||||||
|
->get('/employer/candidates');
|
||||||
|
$response->assertStatus(200);
|
||||||
|
// Extract the Inertia prop 'selectedWorkers' to check
|
||||||
|
$inertiaData = $response->original->getData();
|
||||||
|
$selectedWorkers = $inertiaData['page']['props']['selectedWorkers'] ?? [];
|
||||||
|
$hiredList = array_values(array_filter($selectedWorkers, function ($w) {
|
||||||
|
return $w['worker_id'] === $this->worker->id;
|
||||||
|
}));
|
||||||
|
$this->assertCount(1, $hiredList);
|
||||||
|
$this->assertEquals('Hired', $hiredList[0]['status']);
|
||||||
|
|
||||||
|
// 5. Verify candidate is visible in Hired list (API: EmployerWorkerController::getCandidates)
|
||||||
|
$response = $this->getJson('/api/employers/candidates', [
|
||||||
|
'Authorization' => 'Bearer employer_api_token'
|
||||||
|
]);
|
||||||
|
$response->assertStatus(200);
|
||||||
|
$response->assertJsonPath('success', true);
|
||||||
|
// Find the worker in candidates array
|
||||||
|
$candidates = $response->json('data.candidates');
|
||||||
|
$hiredApiList = array_values(array_filter($candidates, function ($c) {
|
||||||
|
return $c['worker_id'] === $this->worker->id;
|
||||||
|
}));
|
||||||
|
$this->assertCount(1, $hiredApiList);
|
||||||
|
$this->assertEquals('Hired', $hiredApiList[0]['status']);
|
||||||
|
|
||||||
|
// 6. Verify hired worker can view employer profile details bypassing subscription limits (WorkerProfileController::getEmployerProfile)
|
||||||
|
$response = $this->getJson("/api/workers/employers/{$this->employer->id}", [
|
||||||
|
'Authorization' => 'Bearer worker_api_token'
|
||||||
|
]);
|
||||||
|
$response->assertStatus(200);
|
||||||
|
$response->assertJsonPath('success', true);
|
||||||
|
$response->assertJsonPath('data.employer.email', 'employer@example.com');
|
||||||
|
$response->assertJsonPath('data.employer.phone', '971501234567'); // should be exposed/visible because worker is hired
|
||||||
|
|
||||||
|
// 7. Verify hired worker dashboard shows correct currently working sponsor
|
||||||
|
$response = $this->getJson('/api/workers/dashboard', [
|
||||||
|
'Authorization' => 'Bearer worker_api_token'
|
||||||
|
]);
|
||||||
|
$response->assertStatus(200);
|
||||||
|
$response->assertJsonPath('success', true);
|
||||||
|
$response->assertJsonPath('data.current_employer.id', $this->employer->id);
|
||||||
|
$response->assertJsonPath('data.currently_working_sponsor.id', $this->employer->id);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user