1061 lines
47 KiB
PHP
1061 lines
47 KiB
PHP
<?php
|
|
|
|
namespace App\Http\Controllers\Api;
|
|
|
|
use App\Http\Controllers\Controller;
|
|
use App\Models\Worker;
|
|
use Illuminate\Http\Request;
|
|
use Illuminate\Support\Facades\Cache;
|
|
use Illuminate\Support\Facades\DB;
|
|
use Illuminate\Support\Facades\Hash;
|
|
use Illuminate\Support\Facades\Mail;
|
|
use Illuminate\Support\Facades\Validator;
|
|
use Illuminate\Support\Str;
|
|
|
|
class WorkerAuthController extends Controller
|
|
{
|
|
/**
|
|
* Get system configuration and supported languages for worker onboarding.
|
|
* Aligns with Step 2 "Select Language (HI / SW / TL / TA)".
|
|
*/
|
|
public function config()
|
|
{
|
|
return response()->json([
|
|
'success' => true,
|
|
'languages' => [
|
|
['code' => 'HI', 'name' => 'Hindi (हिन्दी)'],
|
|
['code' => 'SW', 'name' => 'Swahili (Kiswahili)'],
|
|
['code' => 'TL', 'name' => 'Tagalog (Wikang Tagalog)'],
|
|
['code' => 'TA', 'name' => 'Tamil (தமிழ்)'],
|
|
]
|
|
], 200);
|
|
}
|
|
|
|
/**
|
|
* Get all master skills for worker registration/onboarding.
|
|
*/
|
|
public function skills()
|
|
{
|
|
$skills = \App\Models\Skill::all()->map(function ($skill) {
|
|
return [
|
|
'id' => $skill->id,
|
|
'name' => $skill->name,
|
|
'image_url' => $skill->image_path ? asset('storage/' . $skill->image_path) : null,
|
|
];
|
|
});
|
|
|
|
return response()->json([
|
|
'success' => true,
|
|
'skills' => $skills
|
|
], 200);
|
|
}
|
|
|
|
/**
|
|
* Step 1: Send OTP to worker's mobile number or email.
|
|
* Accepts: phone OR email (one is required).
|
|
*/
|
|
public function sendOtp(Request $request)
|
|
{
|
|
$validator = Validator::make($request->all(), [
|
|
'phone' => 'required_without:email|nullable|string|max:50|unique:workers,phone',
|
|
'email' => 'required_without:phone|nullable|email|max:255|unique:workers,email',
|
|
], [
|
|
'phone.required_without' => 'Please provide a mobile number or email address.',
|
|
'phone.unique' => 'This mobile number is already registered.',
|
|
'email.required_without' => 'Please provide a mobile number or email address.',
|
|
'email.unique' => 'This email address is already registered.',
|
|
]);
|
|
|
|
if ($validator->fails()) {
|
|
return response()->json([
|
|
'success' => false,
|
|
'message' => 'Validation error.',
|
|
'errors' => $validator->errors()
|
|
], 422);
|
|
}
|
|
|
|
// Determine identifier (phone takes priority)
|
|
$identifier = $request->phone ?? $request->email;
|
|
$isPhone = !empty($request->phone);
|
|
|
|
// Generate OTP (always 111111 in local/dev, random in production)
|
|
$otp = app()->environment('production') ? strval(rand(100000, 999999)) : '111111';
|
|
|
|
// Cache OTP for 10 minutes
|
|
Cache::put('worker_otp_' . $identifier, [
|
|
'code' => $otp,
|
|
'expires_at' => now()->addMinutes(10)->timestamp,
|
|
], 600);
|
|
|
|
// Send OTP via email if email provided (phone SMS would be handled by SMS gateway)
|
|
if (!$isPhone && $request->email) {
|
|
try {
|
|
Mail::raw("Your Migrant Worker verification code is: {$otp}\n\nThis code expires in 10 minutes.", function ($m) use ($request, $otp) {
|
|
$m->to($request->email)
|
|
->subject('Your Migrant Worker Verification Code');
|
|
});
|
|
} catch (\Exception $mailEx) {
|
|
logger()->error('Worker OTP Mail Failure: ' . $mailEx->getMessage());
|
|
}
|
|
}
|
|
|
|
return response()->json([
|
|
'success' => true,
|
|
'message' => $isPhone
|
|
? 'OTP sent to your mobile number.'
|
|
: 'OTP sent to your email address.',
|
|
'identifier' => $identifier,
|
|
'otp' => app()->environment('production') ? null : $otp, // Return OTP in response in non-prod for testing convenience
|
|
], 200);
|
|
}
|
|
|
|
/**
|
|
* Step 2: Verify OTP sent to mobile/email.
|
|
* Accepts: phone OR email, otp.
|
|
*/
|
|
public function verifyOtp(Request $request)
|
|
{
|
|
$validator = Validator::make($request->all(), [
|
|
'phone' => 'required_without:email|nullable|string|max:50',
|
|
'email' => 'required_without:phone|nullable|email|max:255',
|
|
'otp' => 'required|string|size:6',
|
|
]);
|
|
|
|
if ($validator->fails()) {
|
|
return response()->json([
|
|
'success' => false,
|
|
'errors' => $validator->errors()
|
|
], 422);
|
|
}
|
|
|
|
$identifier = $request->phone ?? $request->email;
|
|
$cachedData = Cache::get('worker_otp_' . $identifier);
|
|
|
|
if (!$cachedData || !isset($cachedData['code']) || !isset($cachedData['expires_at'])) {
|
|
return response()->json([
|
|
'success' => false,
|
|
'message' => 'Invalid or expired verification code.'
|
|
], 400);
|
|
}
|
|
|
|
// Handle any dirty/legacy cache objects gracefully
|
|
if (is_object($cachedData['expires_at']) || $cachedData['expires_at'] instanceof \__PHP_Incomplete_Class) {
|
|
Cache::forget('worker_otp_' . $identifier);
|
|
return response()->json([
|
|
'success' => false,
|
|
'message' => 'Session expired. Please request a new verification code.'
|
|
], 400);
|
|
}
|
|
|
|
if ($cachedData['code'] !== $request->otp || now()->timestamp > $cachedData['expires_at']) {
|
|
return response()->json([
|
|
'success' => false,
|
|
'message' => 'Invalid or expired verification code.'
|
|
], 400);
|
|
}
|
|
|
|
// Mark OTP as verified in cache (used as gate for profile setup step)
|
|
Cache::put('worker_otp_verified_' . $identifier, true, 3600); // 1 hour grace
|
|
Cache::forget('worker_otp_' . $identifier);
|
|
|
|
return response()->json([
|
|
'success' => true,
|
|
'message' => 'Mobile number verified successfully. Please complete your profile.',
|
|
'identifier' => $identifier,
|
|
], 200);
|
|
}
|
|
|
|
/**
|
|
* Step 3: Complete basic profile setup after OTP verification.
|
|
* Accepts: phone OR email, name, nationality, skills (array of skill IDs), language.
|
|
*/
|
|
public function setupProfile(Request $request)
|
|
{
|
|
$validator = Validator::make($request->all(), [
|
|
'phone' => 'required_without:email|nullable|string|max:50',
|
|
'email' => 'required_without:phone|nullable|email|max:255',
|
|
'name' => 'required|string|max:255',
|
|
'nationality' => 'required|string|max:100',
|
|
'language' => 'nullable|string',
|
|
'gender' => 'nullable|string|in:male,female,other',
|
|
'live_in_out' => 'nullable|string|in:live_in,live_out',
|
|
'preferred_location' => 'nullable|string|max:255',
|
|
'skills' => 'nullable|array',
|
|
'skills.*' => 'integer|exists:skills,id',
|
|
'fcm_token' => 'nullable|string|max:255',
|
|
]);
|
|
|
|
if ($validator->fails()) {
|
|
return response()->json([
|
|
'success' => false,
|
|
'errors' => $validator->errors()
|
|
], 422);
|
|
}
|
|
|
|
$identifier = $request->phone ?? $request->email;
|
|
|
|
// Ensure OTP was verified in this session
|
|
if (!Cache::get('worker_otp_verified_' . $identifier)) {
|
|
return response()->json([
|
|
'success' => false,
|
|
'message' => 'OTP verification is required before profile setup.'
|
|
], 403);
|
|
}
|
|
|
|
// Check if already registered
|
|
if ($request->phone && Worker::where('phone', $request->phone)->exists()) {
|
|
return response()->json([
|
|
'success' => false,
|
|
'message' => 'This mobile number is already registered.'
|
|
], 409);
|
|
}
|
|
if ($request->email && Worker::where('email', $request->email)->exists()) {
|
|
return response()->json([
|
|
'success' => false,
|
|
'message' => 'This email address is already registered.'
|
|
], 409);
|
|
}
|
|
|
|
try {
|
|
// Build email for workers who registered by phone
|
|
$email = $request->email;
|
|
if (!$email && $request->phone) {
|
|
$phoneClean = preg_replace('/[^0-9]/', '', $request->phone);
|
|
$email = "worker.{$phoneClean}@migrant.ae";
|
|
if (Worker::where('email', $email)->exists()) {
|
|
$email = "worker.{$phoneClean}." . rand(10, 99) . "@migrant.ae";
|
|
}
|
|
}
|
|
|
|
$apiToken = Str::random(80);
|
|
|
|
$worker = DB::transaction(function () use ($request, $email, $apiToken) {
|
|
$worker = Worker::create([
|
|
'name' => $request->name,
|
|
'email' => $email,
|
|
'phone' => $request->phone ?? null,
|
|
'language' => $request->language ?? 'HI', // Default to Hindi if not selected
|
|
'password' => Hash::make(Str::random(16)), // No password at this stage
|
|
'nationality' => $request->nationality,
|
|
'gender' => $request->gender,
|
|
'live_in_out' => $request->live_in_out,
|
|
'preferred_location' => $request->preferred_location,
|
|
'age' => 25, // Default — updated later in full profile
|
|
'salary' => 1500, // Default AED — updated later
|
|
'availability' => 'Immediate',
|
|
'experience' => 'Not Specified',
|
|
'religion' => 'Not Specified',
|
|
'bio' => 'New worker on Migrant platform.',
|
|
'verified' => false,
|
|
'status' => 'active',
|
|
'api_token' => $apiToken,
|
|
'fcm_token' => $request->fcm_token,
|
|
]);
|
|
|
|
// Sync skills if provided
|
|
if (!empty($request->skills)) {
|
|
$worker->skills()->sync($request->skills);
|
|
}
|
|
|
|
return $worker;
|
|
});
|
|
|
|
if ($request->filled('fcm_token')) {
|
|
\App\Services\FCMService::sendPushNotification(
|
|
$request->fcm_token,
|
|
'Welcome to Migrant',
|
|
'Registration complete! Welcome to Migrant.'
|
|
);
|
|
}
|
|
|
|
// Clear the OTP verification gate
|
|
Cache::forget('worker_otp_verified_' . $identifier);
|
|
|
|
$worker->load(['skills']);
|
|
|
|
return response()->json([
|
|
'success' => true,
|
|
'message' => 'Registration complete! Welcome to Migrant.',
|
|
'data' => [
|
|
'worker' => $worker,
|
|
'token' => $apiToken,
|
|
]
|
|
], 201);
|
|
|
|
} catch (\Exception $e) {
|
|
logger()->error('Worker Profile Setup Failure: ' . $e->getMessage());
|
|
return response()->json([
|
|
'success' => false,
|
|
'message' => 'Registration failed. Please try again.',
|
|
'error' => app()->environment('local', 'testing') ? $e->getMessage() : 'Internal Server Error'
|
|
], 500);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Unified registration — supports backward compatibility and multipart file registration.
|
|
*/
|
|
public function register(Request $request)
|
|
{
|
|
$validator = Validator::make($request->all(), [
|
|
'name' => 'required|string|max:255',
|
|
'phone' => 'required|string|max:50|unique:workers,phone',
|
|
'salary' => 'required|numeric|min:0',
|
|
'password' => 'required|string|min:6',
|
|
'skills' => 'nullable',
|
|
'language' => 'nullable|string',
|
|
'nationality' => 'nullable|string|max:100',
|
|
'age' => 'nullable|integer',
|
|
'experience' => 'nullable|string|max:100',
|
|
'in_country' => 'nullable',
|
|
'visa_status' => 'nullable|string|max:100',
|
|
'preferred_job_type' => 'nullable|string|max:100',
|
|
'gender' => 'nullable|string|in:male,female,other',
|
|
'live_in_out' => 'nullable|string|in:live_in,live_out',
|
|
'preferred_location' => 'nullable|string|max:255',
|
|
'fcm_token' => 'nullable|string|max:255',
|
|
]);
|
|
|
|
if ($validator->fails()) {
|
|
return response()->json([
|
|
'success' => false,
|
|
'message' => 'Validation error.',
|
|
'errors' => $validator->errors()
|
|
], 422);
|
|
}
|
|
|
|
try {
|
|
$phoneClean = preg_replace('/[^0-9]/', '', $request->phone);
|
|
$email = "worker.{$phoneClean}@migrant.ae";
|
|
if (Worker::where('email', $email)->exists()) {
|
|
$email = "worker.{$phoneClean}." . rand(10, 99) . "@migrant.ae";
|
|
}
|
|
|
|
$skillsArray = [];
|
|
if ($request->has('skills')) {
|
|
$skillsInput = $request->skills;
|
|
if (is_array($skillsInput)) {
|
|
$skillsArray = $skillsInput;
|
|
} elseif (is_string($skillsInput)) {
|
|
$decoded = json_decode($skillsInput, true);
|
|
$skillsArray = (json_last_error() === JSON_ERROR_NONE && is_array($decoded))
|
|
? $decoded
|
|
: array_filter(array_map('trim', explode(',', $skillsInput)));
|
|
}
|
|
}
|
|
|
|
$inCountry = filter_var($request->input('in_country', true), FILTER_VALIDATE_BOOLEAN);
|
|
$apiToken = Str::random(80);
|
|
|
|
$result = DB::transaction(function () use ($request, $email, $skillsArray, $apiToken, $inCountry) {
|
|
$worker = Worker::create([
|
|
'name' => $request->name,
|
|
'email' => $email,
|
|
'phone' => $request->phone,
|
|
'language' => $request->language ?? 'HI',
|
|
'password' => Hash::make($request->password),
|
|
'nationality' => $request->nationality ?? 'Not Specified',
|
|
'gender' => $request->gender,
|
|
'live_in_out' => $request->live_in_out,
|
|
'preferred_location' => $request->preferred_location,
|
|
'age' => $request->age ?? 25,
|
|
'salary' => $request->salary,
|
|
'availability' => 'Immediate',
|
|
'experience' => $request->experience ?? 'Not Specified',
|
|
'religion' => 'Not Specified',
|
|
'bio' => 'Hardworking and reliable General Helper available for immediate hire.',
|
|
'verified' => false,
|
|
'status' => 'active',
|
|
'api_token' => $apiToken,
|
|
'in_country' => $inCountry,
|
|
'visa_status' => $inCountry ? $request->visa_status : null,
|
|
'preferred_job_type' => $request->preferred_job_type,
|
|
'fcm_token' => $request->fcm_token,
|
|
]);
|
|
|
|
if (!empty($skillsArray)) {
|
|
$validSkillIds = DB::table('skills')->whereIn('id', $skillsArray)->pluck('id')->toArray();
|
|
if (!empty($validSkillIds)) {
|
|
$worker->skills()->sync($validSkillIds);
|
|
}
|
|
}
|
|
|
|
return $worker;
|
|
});
|
|
|
|
if ($request->filled('fcm_token')) {
|
|
\App\Services\FCMService::sendPushNotification(
|
|
$request->fcm_token,
|
|
'Welcome to Migrant',
|
|
'Worker registered successfully. Please upload your passport and visa documents.'
|
|
);
|
|
}
|
|
|
|
$result->load(['skills', 'documents']);
|
|
|
|
return response()->json([
|
|
'success' => true,
|
|
'message' => 'Worker registered successfully. Please upload your passport and visa documents.',
|
|
'data' => ['worker' => $result, 'token' => $apiToken]
|
|
], 201);
|
|
|
|
} catch (\Exception $e) {
|
|
logger()->error('Worker Registration Failure: ' . $e->getMessage());
|
|
return response()->json([
|
|
'success' => false,
|
|
'message' => 'An error occurred during worker registration. Please try again.',
|
|
'error' => app()->environment('local') ? $e->getMessage() : 'Internal Server Error'
|
|
], 500);
|
|
}
|
|
}
|
|
|
|
// -------------------------------------------------------------------------
|
|
// Document Upload Endpoints (protected — require Bearer token)
|
|
// -------------------------------------------------------------------------
|
|
|
|
/**
|
|
* Upload & OCR-process the worker's passport document.
|
|
* POST /workers/register/passport (auth.worker)
|
|
*
|
|
* Calls the dedicated Passport OCR API (http://192.168.0.252:5000/passport).
|
|
* Creates or updates the worker's passport document record.
|
|
* Optionally syncs name, nationality, and age extracted from OCR onto the worker profile.
|
|
*/
|
|
public function registerPassport(Request $request)
|
|
{
|
|
$validator = Validator::make($request->all(), [
|
|
'passport_file' => 'required|file|mimes:jpeg,png,jpg,pdf|max:10240',
|
|
'sync_profile' => 'nullable|in:true,false,1,0,TRUE,FALSE',
|
|
]);
|
|
|
|
if ($validator->fails()) {
|
|
return response()->json([
|
|
'success' => false,
|
|
'message' => 'Validation error.',
|
|
'errors' => $validator->errors()
|
|
], 422);
|
|
}
|
|
|
|
/** @var \App\Models\Worker $worker */
|
|
$worker = $request->attributes->get('worker');
|
|
|
|
try {
|
|
$file = $request->file('passport_file');
|
|
$ocrRes = \App\Services\OcrDocumentService::extractPassportData($file);
|
|
|
|
if (empty($ocrRes['success'])) {
|
|
return response()->json([
|
|
'success' => false,
|
|
'message' => 'Passport OCR extraction failed. Please upload a clear image and try again.',
|
|
'errors' => [
|
|
'passport_file' => ['Passport OCR API could not read the document. Please try again.']
|
|
]
|
|
], 422);
|
|
}
|
|
|
|
// Build document data from OCR
|
|
$number = $ocrRes['document_number'] ?? ('P' . rand(1000000, 9999999));
|
|
$expiryDate = $ocrRes['expiry_date'] ?? now()->addYears(8)->toDateString();
|
|
$issueDate = !empty($ocrRes['expiry_date'])
|
|
? date('Y-m-d', strtotime($ocrRes['expiry_date'] . ' -5 years'))
|
|
: now()->subYears(3)->toDateString();
|
|
|
|
// Upsert passport document (update if already exists, create if not)
|
|
$worker->documents()->updateOrCreate(
|
|
['type' => 'passport'],
|
|
[
|
|
'number' => $number,
|
|
'issue_date' => $issueDate,
|
|
'expiry_date' => $expiryDate,
|
|
'ocr_accuracy' => 98.5,
|
|
'file_path' => null,
|
|
]
|
|
);
|
|
|
|
// Optionally sync OCR-extracted profile fields
|
|
$profileUpdates = ['verified' => true];
|
|
$syncProfile = filter_var($request->input('sync_profile', true), FILTER_VALIDATE_BOOLEAN);
|
|
if ($syncProfile) {
|
|
if (!empty($ocrRes['name'])) {
|
|
$profileUpdates['name'] = $ocrRes['name'];
|
|
}
|
|
|
|
if (!empty($ocrRes['nationality'])) {
|
|
$codeUpper = strtoupper($ocrRes['nationality']);
|
|
$matchedNationality = \App\Models\Nationality::where('iso3', $codeUpper)
|
|
->orWhere('code', $codeUpper)
|
|
->first();
|
|
$profileUpdates['nationality'] = $matchedNationality ? $matchedNationality->name : $ocrRes['nationality'];
|
|
}
|
|
|
|
if (!empty($ocrRes['date_of_birth'])) {
|
|
try {
|
|
$dob = new \DateTime($ocrRes['date_of_birth']);
|
|
$profileUpdates['age'] = (new \DateTime())->diff($dob)->y;
|
|
} catch (\Exception $dateEx) {
|
|
logger()->error('Passport DOB parse error: ' . $dateEx->getMessage());
|
|
}
|
|
}
|
|
}
|
|
|
|
if (!empty($profileUpdates)) {
|
|
$worker->update($profileUpdates);
|
|
}
|
|
|
|
$worker->load(['skills', 'documents']);
|
|
|
|
$passport = $worker->documents->where('type', 'passport')->first();
|
|
$passportData = null;
|
|
if ($passport) {
|
|
$passportData = array_merge($passport->toArray(), [
|
|
'document_number' => $ocrRes['document_number'],
|
|
'expiry_date' => $ocrRes['expiry_date'],
|
|
'date_of_birth' => $ocrRes['date_of_birth'] ?? null,
|
|
'nationality' => $ocrRes['nationality'] ?? null,
|
|
'name' => $ocrRes['name'] ?? null,
|
|
]);
|
|
}
|
|
|
|
|
|
return response()->json([
|
|
'success' => true,
|
|
'message' => 'Passport uploaded and processed successfully.',
|
|
'data' => [
|
|
'worker' => $worker,
|
|
'passport' => $passportData,
|
|
]
|
|
], 200);
|
|
|
|
} catch (\Exception $e) {
|
|
logger()->error('Worker Passport Upload Failure: ' . $e->getMessage());
|
|
return response()->json([
|
|
'success' => false,
|
|
'message' => 'An error occurred while processing the passport. Please try again.',
|
|
'error' => app()->environment('local') ? $e->getMessage() : 'Internal Server Error'
|
|
], 500);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Upload & OCR-process the worker's visa document.
|
|
* POST /workers/register/visa (auth.worker)
|
|
*
|
|
* Calls the dedicated Visa OCR API (http://192.168.0.252:5000/visa).
|
|
* Creates or updates the worker's visa document record.
|
|
*/
|
|
public function registerVisa(Request $request)
|
|
{
|
|
$validator = Validator::make($request->all(), [
|
|
'visa_file' => 'required|file|mimes:jpeg,png,jpg,pdf|max:10240',
|
|
]);
|
|
|
|
if ($validator->fails()) {
|
|
return response()->json([
|
|
'success' => false,
|
|
'message' => 'Validation error.',
|
|
'errors' => $validator->errors()
|
|
], 422);
|
|
}
|
|
|
|
/** @var \App\Models\Worker $worker */
|
|
$worker = $request->attributes->get('worker');
|
|
|
|
try {
|
|
$file = $request->file('visa_file');
|
|
$ocrRes = \App\Services\OcrDocumentService::extractVisaData($file);
|
|
|
|
$number = $ocrRes['document_number'] ?? ('V' . rand(1000000, 9999999));
|
|
$expiryDate = $ocrRes['expiry_date'] ?? now()->addYears(2)->toDateString();
|
|
$issueDate = $ocrRes['issue_date'] ?? date('Y-m-d', strtotime($expiryDate . ' -2 years'));
|
|
$accuracy = $ocrRes['ocr_accuracy'] ?? ($ocrRes['success'] ? 98.5 : 0.0);
|
|
|
|
// Upsert visa document
|
|
$worker->documents()->updateOrCreate(
|
|
['type' => 'visa'],
|
|
[
|
|
'number' => $number,
|
|
'issue_date' => $issueDate,
|
|
'expiry_date' => $expiryDate,
|
|
'ocr_accuracy' => $accuracy,
|
|
'file_path' => null,
|
|
]
|
|
);
|
|
|
|
$worker->update(['verified' => true]);
|
|
|
|
$worker->load(['skills', 'documents']);
|
|
|
|
$visa = $worker->documents->where('type', 'visa')->first();
|
|
$visaData = null;
|
|
if ($visa) {
|
|
$visaData = array_merge($visa->toArray(), [
|
|
'document_number' => $ocrRes['document_number'] ?? null,
|
|
'expiry_date' => $ocrRes['expiry_date'] ?? null,
|
|
'issue_date' => $ocrRes['issue_date'] ?? null,
|
|
'visa_type' => $ocrRes['visa_type'] ?? null,
|
|
]);
|
|
}
|
|
|
|
|
|
return response()->json([
|
|
'success' => true,
|
|
'message' => 'Visa uploaded and processed successfully.',
|
|
'data' => [
|
|
'worker' => $worker,
|
|
'visa' => $visaData,
|
|
]
|
|
], 200);
|
|
|
|
} catch (\Exception $e) {
|
|
logger()->error('Worker Visa Upload Failure: ' . $e->getMessage());
|
|
return response()->json([
|
|
'success' => false,
|
|
'message' => 'An error occurred while processing the visa. Please try again.',
|
|
'error' => app()->environment('local') ? $e->getMessage() : 'Internal Server Error'
|
|
], 500);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Authenticate a worker and return a secure Bearer token.
|
|
*/
|
|
public function login(Request $request)
|
|
{
|
|
$validator = Validator::make($request->all(), [
|
|
'phone' => 'required_without:email|nullable|string',
|
|
'email' => 'required_without:phone|nullable|email',
|
|
'password' => 'required|string',
|
|
'fcm_token' => 'nullable|string|max:255',
|
|
]);
|
|
|
|
if ($validator->fails()) {
|
|
return response()->json([
|
|
'success' => false,
|
|
'message' => 'Validation error.',
|
|
'errors' => $validator->errors()
|
|
], 422);
|
|
}
|
|
|
|
try {
|
|
$worker = $request->phone
|
|
? Worker::where('phone', $request->phone)->first()
|
|
: Worker::where('email', $request->email)->first();
|
|
|
|
if (!$worker || !Hash::check($request->password, $worker->password)) {
|
|
return response()->json([
|
|
'success' => false,
|
|
'message' => 'Invalid credentials.'
|
|
], 401);
|
|
}
|
|
|
|
$apiToken = Str::random(80);
|
|
$updateData = ['api_token' => $apiToken];
|
|
if ($request->has('fcm_token')) {
|
|
$updateData['fcm_token'] = $request->fcm_token;
|
|
}
|
|
$worker->update($updateData);
|
|
|
|
if ($request->filled('fcm_token')) {
|
|
\App\Services\FCMService::sendPushNotification(
|
|
$request->fcm_token,
|
|
'Successful Login',
|
|
'Worker logged in successfully.'
|
|
);
|
|
}
|
|
|
|
return response()->json([
|
|
'success' => true,
|
|
'message' => 'Worker logged in successfully.',
|
|
'data' => [
|
|
'worker' => $worker->load(['skills', 'documents']),
|
|
'token' => $apiToken
|
|
]
|
|
], 200);
|
|
|
|
} catch (\Exception $e) {
|
|
logger()->error('Worker Login Failure: ' . $e->getMessage());
|
|
return response()->json([
|
|
'success' => false,
|
|
'message' => 'An error occurred during login. Please try again.',
|
|
'error' => app()->environment('local') ? $e->getMessage() : 'Internal Server Error'
|
|
], 500);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Get list of nationalities translated to requested language.
|
|
* Supports en, hindi (hi), sawahi (sw), taglo (tl), tamil (ta).
|
|
*/
|
|
public function nationalities(Request $request)
|
|
{
|
|
$lang = strtolower($request->input('lang') ?? $request->header('Accept-Language') ?? 'en');
|
|
if (str_contains($lang, ',')) {
|
|
$lang = explode(',', $lang)[0];
|
|
}
|
|
if (str_contains($lang, '-')) {
|
|
$lang = explode('-', $lang)[0];
|
|
}
|
|
|
|
$langMap = [
|
|
'hindi' => 'hi',
|
|
'sawahi' => 'sw',
|
|
'swahili' => 'sw',
|
|
'taglo' => 'tl',
|
|
'tagalog' => 'tl',
|
|
'tamil' => 'ta',
|
|
];
|
|
|
|
if (isset($langMap[$lang])) {
|
|
$lang = $langMap[$lang];
|
|
}
|
|
|
|
if (!in_array($lang, ['en', 'hi', 'sw', 'tl', 'ta'])) {
|
|
$lang = 'en';
|
|
}
|
|
|
|
$rawNationalities = \App\Models\Nationality::all();
|
|
|
|
$list = [];
|
|
foreach ($rawNationalities as $item) {
|
|
$list[] = [
|
|
'code' => $item->code,
|
|
'name' => $item->{$lang} ?? $item->name,
|
|
];
|
|
}
|
|
|
|
$search = strtolower($request->input('search') ?? $request->input('q') ?? '');
|
|
if ($search !== '') {
|
|
$list = array_filter($list, function ($item) use ($search) {
|
|
return stripos($item['name'], $search) !== false || stripos($item['code'], $search) !== false;
|
|
});
|
|
$list = array_values($list);
|
|
}
|
|
|
|
// Apply Pagination
|
|
$page = (int) $request->input('page', 1);
|
|
$perPage = (int) $request->input('per_page', 500);
|
|
if ($perPage === 15) {
|
|
$perPage = 500;
|
|
}
|
|
$total = count($list);
|
|
$offset = ($page - 1) * $perPage;
|
|
$paginatedList = array_slice($list, $offset, $perPage);
|
|
|
|
return response()->json([
|
|
'success' => true,
|
|
'data' => [
|
|
'nationalities' => $paginatedList,
|
|
'pagination' => [
|
|
'total' => $total,
|
|
'per_page' => $perPage,
|
|
'current_page' => $page,
|
|
'last_page' => max(1, (int) ceil($total / $perPage)),
|
|
]
|
|
],
|
|
], 200);
|
|
}
|
|
|
|
/**
|
|
* Get list of preferred locations translated to requested language.
|
|
* Supports en, hindi (hi), sawahi (sw), taglo (tl), tamil (ta).
|
|
*/
|
|
public function preferredLocations(Request $request)
|
|
{
|
|
$lang = strtolower($request->input('lang') ?? $request->header('Accept-Language') ?? 'en');
|
|
if (str_contains($lang, ',')) {
|
|
$lang = explode(',', $lang)[0];
|
|
}
|
|
if (str_contains($lang, '-')) {
|
|
$lang = explode('-', $lang)[0];
|
|
}
|
|
|
|
$langMap = [
|
|
'hindi' => 'hi',
|
|
'sawahi' => 'sw',
|
|
'swahili' => 'sw',
|
|
'taglo' => 'tl',
|
|
'tagalog' => 'tl',
|
|
'tamil' => 'ta',
|
|
];
|
|
|
|
if (isset($langMap[$lang])) {
|
|
$lang = $langMap[$lang];
|
|
}
|
|
|
|
if (!in_array($lang, ['en', 'hi', 'sw', 'tl', 'ta'])) {
|
|
$lang = 'en';
|
|
}
|
|
|
|
$rawLocations = [
|
|
['code' => 'dubai', 'name' => 'Dubai', 'hi' => 'दुबई', 'sw' => 'Dubai', 'tl' => 'Dubai', 'ta' => 'துபாய்'],
|
|
['code' => 'abu dhabi', 'name' => 'Abu Dhabi', 'hi' => 'अबू धाबी', 'sw' => 'Abu Dhabi', 'tl' => 'Abu Dhabi', 'ta' => 'அபுதாபி'],
|
|
['code' => 'oman', 'name' => 'Oman', 'hi' => 'ओमान', 'sw' => 'Omani', 'tl' => 'Oman', 'ta' => 'ஓமன்']
|
|
];
|
|
|
|
$list = [];
|
|
foreach ($rawLocations as $item) {
|
|
$list[] = [
|
|
'code' => $item['code'],
|
|
'name' => $item[$lang] ?? $item['name'],
|
|
];
|
|
}
|
|
|
|
$search = strtolower($request->input('search') ?? $request->input('q') ?? '');
|
|
if ($search !== '') {
|
|
$list = array_filter($list, function ($item) use ($search) {
|
|
return stripos($item['name'], $search) !== false || stripos($item['code'], $search) !== false;
|
|
});
|
|
$list = array_values($list);
|
|
}
|
|
|
|
// Apply Pagination
|
|
$page = (int) $request->input('page', 1);
|
|
$perPage = (int) $request->input('per_page', 15);
|
|
$total = count($list);
|
|
$offset = ($page - 1) * $perPage;
|
|
$paginatedList = array_slice($list, $offset, $perPage);
|
|
|
|
return response()->json([
|
|
'success' => true,
|
|
'data' => [
|
|
'preferred_locations' => $paginatedList,
|
|
'pagination' => [
|
|
'total' => $total,
|
|
'per_page' => $perPage,
|
|
'current_page' => $page,
|
|
'last_page' => max(1, (int) ceil($total / $perPage)),
|
|
]
|
|
],
|
|
], 200);
|
|
}
|
|
|
|
/**
|
|
* Get list of areas for a specific preferred location translated to requested language.
|
|
* Supports en, hindi (hi), sawahi (sw), taglo (tl), tamil (ta).
|
|
*/
|
|
public function preferredLocationAreas(Request $request, $location = null)
|
|
{
|
|
// Get location from path or query parameter
|
|
$loc = strtolower(trim($location ?? $request->input('location') ?? $request->input('city') ?? ''));
|
|
|
|
// Handle URL-friendly formats like abu-dhabi, abu_dhabi, abu dhabi
|
|
$loc = str_replace(['_', '-'], ' ', $loc);
|
|
|
|
if (empty($loc)) {
|
|
return response()->json([
|
|
'success' => false,
|
|
'message' => 'Please provide a valid location (e.g., dubai, abu dhabi, oman).'
|
|
], 400);
|
|
}
|
|
|
|
$lang = strtolower($request->input('lang') ?? $request->header('Accept-Language') ?? 'en');
|
|
if (str_contains($lang, ',')) {
|
|
$lang = explode(',', $lang)[0];
|
|
}
|
|
if (str_contains($lang, '-')) {
|
|
$lang = explode('-', $lang)[0];
|
|
}
|
|
|
|
$langMap = [
|
|
'hindi' => 'hi',
|
|
'sawahi' => 'sw',
|
|
'swahili' => 'sw',
|
|
'taglo' => 'tl',
|
|
'tagalog' => 'tl',
|
|
'tamil' => 'ta',
|
|
];
|
|
|
|
if (isset($langMap[$lang])) {
|
|
$lang = $langMap[$lang];
|
|
}
|
|
|
|
if (!in_array($lang, ['en', 'hi', 'sw', 'tl', 'ta'])) {
|
|
$lang = 'en';
|
|
}
|
|
|
|
$areasMapping = [
|
|
'dubai' => [
|
|
['code' => 'dubai', 'name' => 'Dubai', 'hi' => 'दुबई', 'sw' => 'Dubai', 'tl' => 'Dubai', 'ta' => 'துபாய்'],
|
|
['code' => 'marina', 'name' => 'Dubai Marina', 'hi' => 'दुबई मरीना', 'sw' => 'Dubai Marina', 'tl' => 'Dubai Marina', 'ta' => 'துபாய் மெரினா'],
|
|
['code' => 'barsha', 'name' => 'Al Barsha', 'hi' => 'अल बरशा', 'sw' => 'Al Barsha', 'tl' => 'Al Barsha', 'ta' => 'அல் பர்ஷா'],
|
|
['code' => 'nahda', 'name' => 'Al Nahda', 'hi' => 'अल नहदा', 'sw' => 'Al Nahda', 'tl' => 'Al Nahda', 'ta' => 'அல் நஹ்தா'],
|
|
['code' => 'jumeirah', 'name' => 'Jumeirah', 'hi' => 'जुमेराह', 'sw' => 'Jumeirah', 'tl' => 'Jumeirah', 'ta' => 'ஜுமேரா'],
|
|
['code' => 'deira', 'name' => 'Deira', 'hi' => 'देइरा', 'sw' => 'Deira', 'tl' => 'Deira', 'ta' => 'தேரா'],
|
|
['code' => 'downtown', 'name' => 'Downtown Dubai', 'hi' => 'डाउनटाउन दुबई', 'sw' => 'Downtown Dubai', 'tl' => 'Downtown Dubai', 'ta' => 'டவுன்டவுன் துபாய்'],
|
|
['code' => 'silicon', 'name' => 'Silicon Oasis', 'hi' => 'सिलिकॉन ओएसिस', 'sw' => 'Silicon Oasis', 'tl' => 'Silicon Oasis', 'ta' => 'சிலிக்கான் ஓசிஸ்'],
|
|
['code' => 'sports', 'name' => 'Sports City', 'hi' => 'स्पोर्ट्स सिटी', 'sw' => 'Sports City', 'tl' => 'Sports City', 'ta' => 'ஸ்போர்ட்ஸ் சிட்டி'],
|
|
['code' => 'motor', 'name' => 'Motor City', 'hi' => 'मोटर सिटी', 'sw' => 'Motor City', 'tl' => 'Motor City', 'ta' => 'மோட்டார் சிட்டி'],
|
|
['code' => 'jlt', 'name' => 'JLT', 'hi' => 'JLT', 'sw' => 'JLT', 'tl' => 'JLT', 'ta' => 'ஜே.எல்.டி'],
|
|
['code' => 'jbr', 'name' => 'JBR', 'hi' => 'JBR', 'sw' => 'JBR', 'tl' => 'JBR', 'ta' => 'ஜே.பி.ஆர்'],
|
|
['code' => 'meydan', 'name' => 'Meydan', 'hi' => 'मेदान', 'sw' => 'Meydan', 'tl' => 'Meydan', 'ta' => 'மெய்தான்'],
|
|
['code' => 'ranches', 'name' => 'Arabian Ranches', 'hi' => 'अरेबियन रैंचेस', 'sw' => 'Arabian Ranches', 'tl' => 'Arabian Ranches', 'ta' => 'அரேபியன் ரான்சஸ்'],
|
|
['code' => 'bay', 'name' => 'Business Bay', 'hi' => 'बिजनेस बे', 'sw' => 'Business Bay', 'tl' => 'Business Bay', 'ta' => 'பிசினஸ் பே'],
|
|
['code' => 'mirdif', 'name' => 'Mirdif', 'hi' => 'मिर्डिफ', 'sw' => 'Mirdif', 'tl' => 'Mirdif', 'ta' => 'மிர்திஃப்'],
|
|
['code' => 'quoz', 'name' => 'Al Quoz', 'hi' => 'अल क्वोज़', 'sw' => 'Al Quoz', 'tl' => 'Al Quoz', 'ta' => 'அல் கூஸ்'],
|
|
],
|
|
'abu dhabi' => [
|
|
['code' => 'abu dhabi', 'name' => 'Abu Dhabi City', 'hi' => 'अबू धाबी शहर', 'sw' => 'Abu Dhabi City', 'tl' => 'Abu Dhabi City', 'ta' => 'அபுதாபி நகரம்'],
|
|
['code' => 'yas', 'name' => 'Yas Island', 'hi' => 'यास द्वीप', 'sw' => 'Yas Island', 'tl' => 'Yas Island', 'ta' => 'யாஸ் தீவு'],
|
|
['code' => 'khalifa', 'name' => 'Khalifa City', 'hi' => 'खलीफा शहर', 'sw' => 'Khalifa City', 'tl' => 'Khalifa City', 'ta' => 'கலீஃபா நகரம்'],
|
|
['code' => 'reem', 'name' => 'Reem Island', 'hi' => 'रीम द्वीप', 'sw' => 'Reem Island', 'tl' => 'Reem Island', 'ta' => 'ரீம் தீவு'],
|
|
['code' => 'saadiyat', 'name' => 'Saadiyat Island', 'hi' => 'सादियात द्वीप', 'sw' => 'Saadiyat Island', 'tl' => 'Saadiyat Island', 'ta' => 'சாதியாத் தீவு'],
|
|
['code' => 'raha', 'name' => 'Al Raha', 'hi' => 'अल राहा', 'sw' => 'Al Raha', 'tl' => 'Al Raha', 'ta' => 'அல் ராஹா'],
|
|
['code' => 'mussafah', 'name' => 'Mussafah', 'hi' => 'मुसफ्फह', 'sw' => 'Mussafah', 'tl' => 'Mussafah', 'ta' => 'முஸாஃபா'],
|
|
['code' => 'zahiyah', 'name' => 'Al Zahiyah', 'hi' => 'अल ज़ाहिया', 'sw' => 'Al Zahiyah', 'tl' => 'Al Zahiyah', 'ta' => 'அல் ஜாஹியா'],
|
|
['code' => 'karamah', 'name' => 'Al Karamah', 'hi' => 'अल करमा', 'sw' => 'Al Karamah', 'tl' => 'Al Karamah', 'ta' => 'அல் கராமா'],
|
|
],
|
|
'oman' => [
|
|
['code' => 'oman', 'name' => 'Oman', 'hi' => 'ओमान', 'sw' => 'Omani', 'tl' => 'Oman', 'ta' => 'ஓமன்'],
|
|
['code' => 'muscat', 'name' => 'Muscat', 'hi' => 'मस्कट', 'sw' => 'Muscat', 'tl' => 'Muscat', 'ta' => 'மस्कट'],
|
|
['code' => 'salalah', 'name' => 'Salalah', 'hi' => 'सलालाह', 'sw' => 'Salalah', 'tl' => 'Salalah', 'ta' => 'சலாலா'],
|
|
['code' => 'sohar', 'name' => 'Sohar', 'hi' => 'सोहर', 'sw' => 'Sohar', 'tl' => 'Sohar', 'ta' => 'சோஹார்'],
|
|
['code' => 'nizwa', 'name' => 'Nizwa', 'hi' => 'निज़वा', 'sw' => 'Nizwa', 'tl' => 'Nizwa', 'ta' => 'நிஸ்வா'],
|
|
['code' => 'sur', 'name' => 'Sur', 'hi' => 'सुर', 'sw' => 'Sur', 'tl' => 'Sur', 'ta' => 'சூர்'],
|
|
['code' => 'ibri', 'name' => 'Ibri', 'hi' => 'इबरी', 'sw' => 'Ibri', 'tl' => 'Ibri', 'ta' => 'இப்ரி'],
|
|
['code' => 'rustaq', 'name' => 'Rustaq', 'hi' => 'रुस्तक़', 'sw' => 'Rustaq', 'tl' => 'Rustaq', 'ta' => 'रुस्तक़'],
|
|
]
|
|
];
|
|
|
|
if (!isset($areasMapping[$loc])) {
|
|
return response()->json([
|
|
'success' => false,
|
|
'message' => 'Areas for the specified location were not found. Supported locations: dubai, abu dhabi, oman.'
|
|
], 404);
|
|
}
|
|
|
|
$rawAreas = $areasMapping[$loc];
|
|
$list = [];
|
|
foreach ($rawAreas as $item) {
|
|
$list[] = [
|
|
'code' => $item['code'],
|
|
'name' => $item[$lang] ?? $item['name'],
|
|
];
|
|
}
|
|
|
|
$search = strtolower($request->input('search') ?? $request->input('q') ?? '');
|
|
if ($search !== '') {
|
|
$list = array_filter($list, function ($item) use ($search) {
|
|
return stripos($item['name'], $search) !== false || stripos($item['code'], $search) !== false;
|
|
});
|
|
$list = array_values($list);
|
|
}
|
|
|
|
// Apply Pagination
|
|
$page = (int) $request->input('page', 1);
|
|
$perPage = (int) $request->input('per_page', 15);
|
|
$total = count($list);
|
|
$offset = ($page - 1) * $perPage;
|
|
$paginatedList = array_slice($list, $offset, $perPage);
|
|
|
|
return response()->json([
|
|
'success' => true,
|
|
'data' => [
|
|
'location' => $loc,
|
|
'areas' => $paginatedList,
|
|
'pagination' => [
|
|
'total' => $total,
|
|
'per_page' => $perPage,
|
|
'current_page' => $page,
|
|
'last_page' => max(1, (int) ceil($total / $perPage)),
|
|
]
|
|
],
|
|
], 200);
|
|
}
|
|
|
|
/**
|
|
* Get list of languages translated to requested language.
|
|
* Supports en, hindi (hi), sawahi (sw), taglo (tl), tamil (ta).
|
|
*/
|
|
public function languages(Request $request)
|
|
{
|
|
$lang = strtolower($request->input('lang') ?? $request->header('Accept-Language') ?? 'en');
|
|
if (str_contains($lang, ',')) {
|
|
$lang = explode(',', $lang)[0];
|
|
}
|
|
if (str_contains($lang, '-')) {
|
|
$lang = explode('-', $lang)[0];
|
|
}
|
|
|
|
$langMap = [
|
|
'hindi' => 'hi',
|
|
'sawahi' => 'sw',
|
|
'swahili' => 'sw',
|
|
'taglo' => 'tl',
|
|
'tagalog' => 'tl',
|
|
'tamil' => 'ta',
|
|
];
|
|
|
|
if (isset($langMap[$lang])) {
|
|
$lang = $langMap[$lang];
|
|
}
|
|
|
|
if (!in_array($lang, ['en', 'hi', 'sw', 'tl', 'ta'])) {
|
|
$lang = 'en';
|
|
}
|
|
|
|
$rawLanguages = [
|
|
['code' => 'en', 'name' => 'English', 'hi' => 'अंग्रेज़ी', 'sw' => 'Kiingereza', 'tl' => 'Ingles', 'ta' => 'ஆங்கிலம்'],
|
|
['code' => 'ar', 'name' => 'Arabic', 'hi' => 'अरबी', 'sw' => 'Kiarabu', 'tl' => 'Arabe', 'ta' => 'அரபு'],
|
|
['code' => 'hi', 'name' => 'Hindi', 'hi' => 'हिन्दी', 'sw' => 'Kihindi', 'tl' => 'Hindi', 'ta' => 'இந்தி'],
|
|
['code' => 'tl', 'name' => 'Tagalog', 'hi' => 'तागालोग', 'sw' => 'Kitagalog', 'tl' => 'Tagalog', 'ta' => 'தகலாக்'],
|
|
['code' => 'sw', 'name' => 'Swahili', 'hi' => 'स्वाहिली', 'sw' => 'Kiswahili', 'tl' => 'Swahili', 'ta' => 'சுவாஹிலி'],
|
|
['code' => 'ta', 'name' => 'Tamil', 'hi' => 'तमिल', 'sw' => 'Kitamil', 'tl' => 'Tamil', 'ta' => 'தமிழ்'],
|
|
['code' => 'ur', 'name' => 'Urdu', 'hi' => 'उर्दू', 'sw' => 'Kiurdu', 'tl' => 'Urdu', 'ta' => 'உருது'],
|
|
['code' => 'bn', 'name' => 'Bengali', 'hi' => 'बंगाली', 'sw' => 'Kibengali', 'tl' => 'Bengali', 'ta' => 'பெங்காலி'],
|
|
['code' => 'ml', 'name' => 'Malayalam', 'hi' => 'मलयालम', 'sw' => 'Kimalayalam', 'tl' => 'Malayalam', 'ta' => 'மலையாளம்'],
|
|
['code' => 'ne', 'name' => 'Nepali', 'hi' => 'नेपाली', 'sw' => 'Kinepali', 'tl' => 'Nepali', 'ta' => 'நேபாளி'],
|
|
['code' => 'si', 'name' => 'Sinhala', 'hi' => 'सिंहली', 'sw' => 'Kisinhala', 'tl' => 'Sinhala', 'ta' => 'சிங்களம்'],
|
|
['code' => 'te', 'name' => 'Telugu', 'hi' => 'तेलुगु', 'sw' => 'Kitelugu', 'tl' => 'Telugu', 'ta' => 'தெலுங்கு'],
|
|
['code' => 'pa', 'name' => 'Punjabi', 'hi' => 'पंजाबी', 'sw' => 'Kipunjabi', 'tl' => 'Punjabi', 'ta' => 'பஞ்சாபி'],
|
|
['code' => 'ps', 'name' => 'Pashto', 'hi' => 'पश्तो', 'sw' => 'Kipashto', 'tl' => 'Pashto', 'ta' => 'பஷ்தூ'],
|
|
['code' => 'kn', 'name' => 'Kannada', 'hi' => 'कन्नड़', 'sw' => 'Kikannada', 'tl' => 'Kannada', 'ta' => 'கன்னடம்'],
|
|
['code' => 'mr', 'name' => 'Marathi', 'hi' => 'मराठी', 'sw' => 'Kimarathi', 'tl' => 'Marathi', 'ta' => 'மராத்தி'],
|
|
['code' => 'gu', 'name' => 'Gujarati', 'hi' => 'गुजराती', 'sw' => 'Kigujarati', 'tl' => 'Gujarati', 'ta' => 'குஜராत्ति'],
|
|
['code' => 'fr', 'name' => 'French', 'hi' => 'फ़्रांसीसी', 'sw' => 'Kifaransa', 'tl' => 'Pranses', 'ta' => 'பிரெஞ்சு'],
|
|
['code' => 'es', 'name' => 'Spanish', 'hi' => 'स्पैनिश', 'sw' => 'Kihispania', 'tl' => 'Espanyol', 'ta' => 'ஸ்பானிஷ்'],
|
|
['code' => 'zh', 'name' => 'Chinese', 'hi' => 'चीनी', 'sw' => 'Kichina', 'tl' => 'Intsik', 'ta' => 'சீனம்'],
|
|
['code' => 'ru', 'name' => 'Russian', 'hi' => 'रूसी', 'sw' => 'Kirusi', 'tl' => 'Ruso', 'ta' => 'ரஷ்யன்'],
|
|
['code' => 'id', 'name' => 'Indonesian', 'hi' => 'इंडोनेशियाई', 'sw' => 'Kiindonesia', 'tl' => 'Indonesian', 'ta' => 'இந்தோனேசியன்'],
|
|
];
|
|
|
|
$list = [];
|
|
foreach ($rawLanguages as $item) {
|
|
$list[] = [
|
|
'code' => $item['code'],
|
|
'name' => $item[$lang] ?? $item['name'],
|
|
];
|
|
}
|
|
|
|
$search = strtolower($request->input('search') ?? $request->input('q') ?? '');
|
|
if ($search !== '') {
|
|
$list = array_filter($list, function ($item) use ($search) {
|
|
return stripos($item['name'], $search) !== false || stripos($item['code'], $search) !== false;
|
|
});
|
|
$list = array_values($list);
|
|
}
|
|
|
|
// Apply Pagination
|
|
$page = (int) $request->input('page', 1);
|
|
$perPage = (int) $request->input('per_page', 500);
|
|
if ($perPage === 15) {
|
|
$perPage = 500;
|
|
}
|
|
$total = count($list);
|
|
$offset = ($page - 1) * $perPage;
|
|
$paginatedList = array_slice($list, $offset, $perPage);
|
|
|
|
return response()->json([
|
|
'success' => true,
|
|
'data' => [
|
|
'languages' => $paginatedList,
|
|
'pagination' => [
|
|
'total' => $total,
|
|
'per_page' => $perPage,
|
|
'current_page' => $page,
|
|
'last_page' => max(1, (int) ceil($total / $perPage)),
|
|
]
|
|
],
|
|
], 200);
|
|
}
|
|
}
|