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,
'preferred_location' => $request->preferred_location,
'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
'availability' => 'Immediate',
'experience' => 'Not Specified',
@ -315,7 +315,7 @@ public function register(Request $request)
'skills' => 'nullable',
'language' => 'nullable|string',
'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',
'in_country' => 'nullable',
'visa_status' => 'nullable|string',
@ -401,7 +401,7 @@ public function register(Request $request)
// Initialize fields
$name = $request->name;
$nationality = $request->nationality ?? 'Not Specified';
$age = $request->age ?? 25;
$age = $request->age ?? '18-25';
$visaStatus = $inCountry ? $request->visa_status : null;
$passportDataInput = $request->input('passport');
@ -430,7 +430,20 @@ public function register(Request $request)
if (!empty($passportDataInput['date_of_birth'])) {
try {
$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) {
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(), [
'name' => 'nullable|string|max:255',
'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',
'language' => 'nullable|string',
'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."
},
"age": {
"type": "integer",
"example": 28,
"description": "Age of the worker."
"type": "string",
"enum": [
"18-25",
"26-35",
"36-45",
"46-55",
"56-60"
],
"example": "26-35",
"description": "Age range of the worker."
},
"experience": {
"type": "string",
@ -7427,33 +7434,106 @@
"employer": {
"type": "object",
"properties": {
"id": { "type": "integer", "example": 1 },
"name": { "type": "string", "example": "Jane Sponsor" },
"email": { "type": "string", "nullable": true, "example": "sponsor@example.com" },
"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 },
"id": {
"type": "integer",
"example": 1
},
"name": {
"type": "string",
"example": "Jane Sponsor"
},
"email": {
"type": "string",
"nullable": true,
"example": "sponsor@example.com"
},
"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": {
"type": "object",
"properties": {
"total": { "type": "integer", "example": 12 },
"average": { "type": "number", "example": 4.5 },
"total": {
"type": "integer",
"example": 12
},
"average": {
"type": "number",
"example": 4.5
},
"stars": {
"type": "object",
"properties": {
"5": { "type": "integer", "example": 8 },
"4": { "type": "integer", "example": 2 },
"3": { "type": "integer", "example": 1 },
"2": { "type": "integer", "example": 1 },
"1": { "type": "integer", "example": 0 }
"5": {
"type": "integer",
"example": 8
},
"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": {
"type": "object",
"properties": {
"id": { "type": "integer", "example": 5 },
"title": { "type": "string", "example": "Housekeeper Needed" },
"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" }
"id": {
"type": "integer",
"example": 5
},
"title": {
"type": "string",
"example": "Housekeeper Needed"
},
"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": {
"type": "object",
"properties": {
"id": { "type": "integer", "example": 1 },
"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" },
"id": {
"type": "integer",
"example": 1
},
"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": {
"type": "object",
"properties": {
"id": { "type": "integer", "example": 2 },
"name": { "type": "string", "example": "Worker Name" },
"nationality": { "type": "string", "example": "Filipino" }
"id": {
"type": "integer",
"example": 2
},
"name": {
"type": "string",
"example": "Worker Name"
},
"nationality": {
"type": "string",
"example": "Filipino"
}
}
}
}
@ -7504,10 +7639,22 @@
"pagination": {
"type": "object",
"properties": {
"total": { "type": "integer", "example": 1 },
"per_page": { "type": "integer", "example": 10 },
"current_page": { "type": "integer", "example": 1 },
"last_page": { "type": "integer", "example": 1 }
"total": {
"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"
},
"age": {
"type": "integer",
"example": 28
"type": "string",
"enum": [
"18-25",
"26-35",
"36-45",
"46-55",
"56-60"
],
"example": "26-35",
"description": "Age range of the worker."
},
"salary": {
"type": "string",

View File

@ -238,7 +238,15 @@ export default function Shortlist({ shortlistedWorkers, filtersMetadata = {} })
} else if (sortBy === 'rating') {
list.sort((a, b) => b.rating - a.rating);
} 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;

View File

@ -334,7 +334,15 @@ export default function Index({ initialWorkers = [], initialShortlistedIds = [],
} else if (sortBy === 'rating') {
workers.sort((a, b) => b.rating - a.rating);
} 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;

View File

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