mohan #26

Merged
mohanmd merged 12 commits from mohan into master 2026-07-09 10:16:34 +00:00
7 changed files with 267 additions and 62 deletions
Showing only changes of commit 905a706fe6 - Show all commits

View File

@ -243,7 +243,7 @@ public function setupProfile(Request $request)
'live_in_out' => $request->live_in_out, 'live_in_out' => $request->live_in_out,
'preferred_location' => $request->preferred_location, 'preferred_location' => $request->preferred_location,
'main_profession' => $request->main_profession, 'main_profession' => $request->main_profession,
'age' => 25, // Default — updated later in full profile 'age' => '18-25', // Default — updated later in full profile
'salary' => 1500, // Default AED — updated later 'salary' => 1500, // Default AED — updated later
'availability' => 'Immediate', 'availability' => 'Immediate',
'experience' => 'Not Specified', 'experience' => 'Not Specified',
@ -315,7 +315,7 @@ public function register(Request $request)
'skills' => 'nullable', 'skills' => 'nullable',
'language' => 'nullable|string', 'language' => 'nullable|string',
'nationality' => 'nullable|string|max:100', 'nationality' => 'nullable|string|max:100',
'age' => 'nullable|integer', 'age' => 'nullable|string|in:18-25,26-35,36-45,46-55,56-60',
'experience' => 'nullable|string|max:100', 'experience' => 'nullable|string|max:100',
'in_country' => 'nullable', 'in_country' => 'nullable',
'visa_status' => 'nullable|string', 'visa_status' => 'nullable|string',
@ -401,7 +401,7 @@ public function register(Request $request)
// Initialize fields // Initialize fields
$name = $request->name; $name = $request->name;
$nationality = $request->nationality ?? 'Not Specified'; $nationality = $request->nationality ?? 'Not Specified';
$age = $request->age ?? 25; $age = $request->age ?? '18-25';
$visaStatus = $inCountry ? $request->visa_status : null; $visaStatus = $inCountry ? $request->visa_status : null;
$passportDataInput = $request->input('passport'); $passportDataInput = $request->input('passport');
@ -430,7 +430,20 @@ public function register(Request $request)
if (!empty($passportDataInput['date_of_birth'])) { if (!empty($passportDataInput['date_of_birth'])) {
try { try {
$dob = new \DateTime($this->normaliseDateForController($passportDataInput['date_of_birth'])); $dob = new \DateTime($this->normaliseDateForController($passportDataInput['date_of_birth']));
$age = (new \DateTime())->diff($dob)->y; $ageVal = (new \DateTime())->diff($dob)->y;
if ($ageVal >= 18 && $ageVal <= 25) {
$age = '18-25';
} elseif ($ageVal >= 26 && $ageVal <= 35) {
$age = '26-35';
} elseif ($ageVal >= 36 && $ageVal <= 45) {
$age = '36-45';
} elseif ($ageVal >= 46 && $ageVal <= 55) {
$age = '46-55';
} elseif ($ageVal >= 56 && $ageVal <= 60) {
$age = '56-60';
} else {
$age = $ageVal < 18 ? '18-25' : '56-60';
}
} catch (\Exception $dateEx) { } catch (\Exception $dateEx) {
logger()->error('Passport DOB parse error in register: ' . $dateEx->getMessage()); logger()->error('Passport DOB parse error in register: ' . $dateEx->getMessage());
} }

View File

@ -70,7 +70,7 @@ public function updateProfile(Request $request)
$validator = Validator::make($request->all(), [ $validator = Validator::make($request->all(), [
'name' => 'nullable|string|max:255', 'name' => 'nullable|string|max:255',
'phone' => 'nullable|string|max:50|unique:workers,phone,' . $worker->id, 'phone' => 'nullable|string|max:50|unique:workers,phone,' . $worker->id,
'age' => 'nullable|integer|min:18|max:70', 'age' => 'nullable|string|in:18-25,26-35,36-45,46-55,56-60',
'nationality' => 'nullable|string|max:100', 'nationality' => 'nullable|string|max:100',
'language' => 'nullable|string', 'language' => 'nullable|string',
'salary' => 'nullable|numeric|min:0', 'salary' => 'nullable|numeric|min:0',

View File

@ -0,0 +1,25 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
public function up(): void
{
Schema::table('workers', function (Blueprint $table) {
$table->string('age')->nullable()->change();
});
}
/**
* Reverse the migrations.
*/
public function down(): void
{
Schema::table('workers', function (Blueprint $table) {
$table->integer('age')->nullable(false)->change();
});
}
};

View File

@ -1017,9 +1017,16 @@
"description": "Nationality of the worker." "description": "Nationality of the worker."
}, },
"age": { "age": {
"type": "integer", "type": "string",
"example": 28, "enum": [
"description": "Age of the worker." "18-25",
"26-35",
"36-45",
"46-55",
"56-60"
],
"example": "26-35",
"description": "Age range of the worker."
}, },
"experience": { "experience": {
"type": "string", "type": "string",
@ -7427,33 +7434,106 @@
"employer": { "employer": {
"type": "object", "type": "object",
"properties": { "properties": {
"id": { "type": "integer", "example": 1 }, "id": {
"name": { "type": "string", "example": "Jane Sponsor" }, "type": "integer",
"email": { "type": "string", "nullable": true, "example": "sponsor@example.com" }, "example": 1
"phone": { "type": "string", "nullable": true, "example": "+971501112222" }, },
"company_name": { "type": "string", "example": "Sponsor Co" }, "name": {
"city": { "type": "string", "nullable": true, "example": "Dubai" }, "type": "string",
"district": { "type": "string", "nullable": true, "example": "Dubai Marina" }, "example": "Jane Sponsor"
"nationality": { "type": "string", "nullable": true, "example": "Emirati" }, },
"address": { "type": "string", "nullable": true, "example": "123 Street" }, "email": {
"accommodation": { "type": "string", "nullable": true, "example": "Provided" }, "type": "string",
"language": { "type": "string", "nullable": true, "example": "English" }, "nullable": true,
"profile_photo_url": { "type": "string", "nullable": true, "example": "https://example.com/photo.jpg" }, "example": "sponsor@example.com"
"rating": { "type": "number", "format": "float", "example": 4.5 }, },
"reviews_count": { "type": "integer", "example": 12 }, "phone": {
"type": "string",
"nullable": true,
"example": "+971501112222"
},
"company_name": {
"type": "string",
"example": "Sponsor Co"
},
"city": {
"type": "string",
"nullable": true,
"example": "Dubai"
},
"district": {
"type": "string",
"nullable": true,
"example": "Dubai Marina"
},
"nationality": {
"type": "string",
"nullable": true,
"example": "Emirati"
},
"address": {
"type": "string",
"nullable": true,
"example": "123 Street"
},
"accommodation": {
"type": "string",
"nullable": true,
"example": "Provided"
},
"language": {
"type": "string",
"nullable": true,
"example": "English"
},
"profile_photo_url": {
"type": "string",
"nullable": true,
"example": "https://example.com/photo.jpg"
},
"rating": {
"type": "number",
"format": "float",
"example": 4.5
},
"reviews_count": {
"type": "integer",
"example": 12
},
"review_summary": { "review_summary": {
"type": "object", "type": "object",
"properties": { "properties": {
"total": { "type": "integer", "example": 12 }, "total": {
"average": { "type": "number", "example": 4.5 }, "type": "integer",
"example": 12
},
"average": {
"type": "number",
"example": 4.5
},
"stars": { "stars": {
"type": "object", "type": "object",
"properties": { "properties": {
"5": { "type": "integer", "example": 8 }, "5": {
"4": { "type": "integer", "example": 2 }, "type": "integer",
"3": { "type": "integer", "example": 1 }, "example": 8
"2": { "type": "integer", "example": 1 }, },
"1": { "type": "integer", "example": 0 } "4": {
"type": "integer",
"example": 2
},
"3": {
"type": "integer",
"example": 1
},
"2": {
"type": "integer",
"example": 1
},
"1": {
"type": "integer",
"example": 0
}
} }
} }
} }
@ -7463,15 +7543,45 @@
"items": { "items": {
"type": "object", "type": "object",
"properties": { "properties": {
"id": { "type": "integer", "example": 5 }, "id": {
"title": { "type": "string", "example": "Housekeeper Needed" }, "type": "integer",
"location": { "type": "string", "example": "Dubai Marina" }, "example": 5
"salary": { "type": "integer", "example": 2500 }, },
"job_type": { "type": "string", "example": "Full Time" }, "title": {
"start_date": { "type": "string", "format": "date", "example": "2026-08-01" }, "type": "string",
"description": { "type": "string", "example": "Clean house" }, "example": "Housekeeper Needed"
"requirements": { "type": "string", "nullable": true, "example": "Experience required" }, },
"posted_at": { "type": "string", "format": "date-time", "example": "2026-07-04T12:00:00Z" } "location": {
"type": "string",
"example": "Dubai Marina"
},
"salary": {
"type": "integer",
"example": 2500
},
"job_type": {
"type": "string",
"example": "Full Time"
},
"start_date": {
"type": "string",
"format": "date",
"example": "2026-08-01"
},
"description": {
"type": "string",
"example": "Clean house"
},
"requirements": {
"type": "string",
"nullable": true,
"example": "Experience required"
},
"posted_at": {
"type": "string",
"format": "date-time",
"example": "2026-07-04T12:00:00Z"
}
} }
} }
} }
@ -7485,17 +7595,42 @@
"items": { "items": {
"type": "object", "type": "object",
"properties": { "properties": {
"id": { "type": "integer", "example": 1 }, "id": {
"rating": { "type": "integer", "example": 5 }, "type": "integer",
"title": { "type": "string", "example": "Great employer" }, "example": 1
"comment": { "type": "string", "example": "Highly recommended!" }, },
"created_at": { "type": "string", "format": "date-time", "example": "2026-07-04T12:00:00Z" }, "rating": {
"type": "integer",
"example": 5
},
"title": {
"type": "string",
"example": "Great employer"
},
"comment": {
"type": "string",
"example": "Highly recommended!"
},
"created_at": {
"type": "string",
"format": "date-time",
"example": "2026-07-04T12:00:00Z"
},
"worker": { "worker": {
"type": "object", "type": "object",
"properties": { "properties": {
"id": { "type": "integer", "example": 2 }, "id": {
"name": { "type": "string", "example": "Worker Name" }, "type": "integer",
"nationality": { "type": "string", "example": "Filipino" } "example": 2
},
"name": {
"type": "string",
"example": "Worker Name"
},
"nationality": {
"type": "string",
"example": "Filipino"
}
} }
} }
} }
@ -7504,10 +7639,22 @@
"pagination": { "pagination": {
"type": "object", "type": "object",
"properties": { "properties": {
"total": { "type": "integer", "example": 1 }, "total": {
"per_page": { "type": "integer", "example": 10 }, "type": "integer",
"current_page": { "type": "integer", "example": 1 }, "example": 1
"last_page": { "type": "integer", "example": 1 } },
"per_page": {
"type": "integer",
"example": 10
},
"current_page": {
"type": "integer",
"example": 1
},
"last_page": {
"type": "integer",
"example": 1
}
} }
} }
} }
@ -9133,8 +9280,16 @@
"example": "Egypt" "example": "Egypt"
}, },
"age": { "age": {
"type": "integer", "type": "string",
"example": 28 "enum": [
"18-25",
"26-35",
"36-45",
"46-55",
"56-60"
],
"example": "26-35",
"description": "Age range of the worker."
}, },
"salary": { "salary": {
"type": "string", "type": "string",

View File

@ -238,7 +238,15 @@ export default function Shortlist({ shortlistedWorkers, filtersMetadata = {} })
} else if (sortBy === 'rating') { } else if (sortBy === 'rating') {
list.sort((a, b) => b.rating - a.rating); list.sort((a, b) => b.rating - a.rating);
} else if (sortBy === 'experience') { } else if (sortBy === 'experience') {
list.sort((a, b) => b.age - a.age); // approximate experience by age list.sort((a, b) => {
const parseAge = (val) => {
if (!val) return 0;
if (typeof val === 'number') return val;
const num = parseInt(val, 10);
return isNaN(num) ? 0 : num;
};
return parseAge(b.age) - parseAge(a.age);
});
} }
return list; return list;

View File

@ -334,7 +334,15 @@ export default function Index({ initialWorkers = [], initialShortlistedIds = [],
} else if (sortBy === 'rating') { } else if (sortBy === 'rating') {
workers.sort((a, b) => b.rating - a.rating); workers.sort((a, b) => b.rating - a.rating);
} else if (sortBy === 'experience') { } else if (sortBy === 'experience') {
workers.sort((a, b) => b.age - a.age); // approximate experience by age workers.sort((a, b) => {
const parseAge = (val) => {
if (!val) return 0;
if (typeof val === 'number') return val;
const num = parseInt(val, 10);
return isNaN(num) ? 0 : num;
};
return parseAge(b.age) - parseAge(a.age);
});
} }
return workers; return workers;

View File

@ -383,7 +383,7 @@ public function test_register_with_new_api_fields()
'passport_file' => $fakePassport, 'passport_file' => $fakePassport,
'language' => 'HI', 'language' => 'HI',
'nationality' => 'Kenyan', 'nationality' => 'Kenyan',
'age' => 30, 'age' => '26-35',
'experience' => '5 Years', 'experience' => '5 Years',
'in_country' => 'true', 'in_country' => 'true',
'visa_status' => 'Tourist Visa', 'visa_status' => 'Tourist Visa',
@ -399,6 +399,7 @@ public function test_register_with_new_api_fields()
$this->assertDatabaseHas('workers', [ $this->assertDatabaseHas('workers', [
'name' => 'John New Worker', 'name' => 'John New Worker',
'phone' => '+971501111112', 'phone' => '+971501111112',
'age' => '26-35',
'in_country' => true, 'in_country' => true,
'visa_status' => 'Tourist Visa', 'visa_status' => 'Tourist Visa',
'preferred_job_type' => 'full-time', 'preferred_job_type' => 'full-time',
@ -435,14 +436,12 @@ public function test_register_with_ocr_extraction()
$response->assertStatus(201); $response->assertStatus(201);
$workerId = $response->json('data.worker.id'); $workerId = $response->json('data.worker.id');
$expectedAge = now()->diff(new \DateTime('1990-11-07'))->y;
$this->assertDatabaseHas('workers', [ $this->assertDatabaseHas('workers', [
'id' => $workerId, 'id' => $workerId,
'name' => 'MATAR ALI KHARBASH ALSAEDI', 'name' => 'MATAR ALI KHARBASH ALSAEDI',
'phone' => '+971509999999', 'phone' => '+971509999999',
'nationality' => 'Emirati', 'nationality' => 'Emirati',
'age' => $expectedAge, 'age' => '26-35',
'verified' => true, 'verified' => true,
]); ]);
@ -497,15 +496,12 @@ public function test_register_passport_and_visa_with_direct_json_payload()
$response->assertStatus(201); $response->assertStatus(201);
$workerId = $response->json('data.worker.id'); $workerId = $response->json('data.worker.id');
$expectedAge = now()->diff(new \DateTime('1990-05-14'))->y;
$this->assertDatabaseHas('workers', [ $this->assertDatabaseHas('workers', [
'id' => $workerId, 'id' => $workerId,
'name' => 'Jane Doe', 'name' => 'Jane Doe',
'phone' => '+971508888888', 'phone' => '+971508888888',
'nationality' => 'Filipino', 'nationality' => 'Filipino',
'age' => $expectedAge, 'age' => '36-45',
'verified' => true, 'verified' => true,
'visa_status' => 'Tourist Visa', 'visa_status' => 'Tourist Visa',
]); ]);