mohan #12

Merged
mohanmd merged 4 commits from mohan into master 2026-06-23 13:43:57 +00:00
4 changed files with 416 additions and 7 deletions
Showing only changes of commit dcf8481c5f - Show all commits

View File

@ -11,12 +11,89 @@
class EmployerAnnouncementController extends Controller class EmployerAnnouncementController extends Controller
{ {
/** /**
* Get all announcements posted by this employer. * Get all approved announcements for employers.
* *
* @param \Illuminate\Http\Request $request * @param \Illuminate\Http\Request $request
* @return \Illuminate\Http\JsonResponse * @return \Illuminate\Http\JsonResponse
*/ */
public function getAnnouncements(Request $request) public function getAnnouncements(Request $request)
{
try {
$page = (int)$request->input('page', 1);
$perPage = (int)$request->input('per_page', 15);
$query = Announcement::with(['employer.employerProfile', 'sponsor'])->where('status', 'approved')->latest();
$total = $query->count();
$offset = ($page - 1) * $perPage;
$announcements = $query->skip($offset)->take($perPage)->get()
->map(function ($announcement) {
$postedBy = 'System';
$organization = 'Migrant Support';
if ($announcement->sponsor_id) {
$postedBy = $announcement->sponsor->full_name;
$organization = $announcement->sponsor->organization_name;
} elseif ($announcement->employer_id) {
$postedBy = $announcement->employer->name;
$organization = $announcement->employer->employerProfile->company_name ?? 'Employer';
}
// Decode charity details if they exist in json
$charityDetails = null;
$content = $announcement->body;
if (strpos($announcement->body, '{"type":"Charity"') === 0) {
$decoded = json_decode($announcement->body, true);
if ($decoded) {
$charityDetails = $decoded;
$content = $decoded['content'] ?? $announcement->body;
}
}
return [
'id' => $announcement->id,
'title' => $announcement->title,
'body' => $content,
'type' => $announcement->type,
'employer_name' => $postedBy,
'company_name' => $organization,
'created_at' => $announcement->created_at->toISOString(),
'time_ago' => $announcement->created_at->diffForHumans(),
'charity_details' => $charityDetails,
];
});
return response()->json([
'success' => true,
'data' => [
'announcements' => $announcements,
'pagination' => [
'total' => $total,
'per_page' => $perPage,
'current_page' => $page,
'last_page' => max(1, (int)ceil($total / $perPage)),
]
]
], 200);
} catch (\Exception $e) {
logger()->error('Mobile Employer Get All Announcements Failure: ' . $e->getMessage());
return response()->json([
'success' => false,
'message' => 'An error occurred while fetching announcements.',
'error' => app()->environment('local') ? $e->getMessage() : 'Internal Server Error'
], 500);
}
}
/**
* Get all announcements posted by this employer.
*
* @param \Illuminate\Http\Request $request
* @return \Illuminate\Http\JsonResponse
*/
public function getMyAnnouncements(Request $request)
{ {
/** @var User $employer */ /** @var User $employer */
$employer = $request->attributes->get('employer'); $employer = $request->attributes->get('employer');
@ -57,7 +134,7 @@ public function getAnnouncements(Request $request)
], 200); ], 200);
} catch (\Exception $e) { } catch (\Exception $e) {
logger()->error('Mobile Employer Get Announcements Failure: ' . $e->getMessage()); logger()->error('Mobile Employer Get My Announcements Failure: ' . $e->getMessage());
return response()->json([ return response()->json([
'success' => false, 'success' => false,

View File

@ -3527,7 +3527,91 @@
], ],
"responses": { "responses": {
"200": { "200": {
"description": "List of posted charity events retrieved successfully." "description": "List of posted charity events retrieved successfully.",
"content": {
"application/json": {
"schema": {
"type": "object",
"properties": {
"success": {
"type": "boolean",
"example": true
},
"data": {
"type": "object",
"properties": {
"announcements": {
"type": "array",
"items": {
"type": "object",
"properties": {
"id": {
"type": "integer",
"example": 1
},
"title": {
"type": "string",
"example": "Holiday Notice"
},
"body": {
"type": "string",
"example": "Office will be closed tomorrow."
},
"type": {
"type": "string",
"example": "info"
},
"employer_name": {
"type": "string",
"example": "Employer One"
},
"company_name": {
"type": "string",
"example": "Elite Services"
},
"created_at": {
"type": "string",
"format": "date-time",
"example": "2026-06-23T10:13:17.000000Z"
},
"time_ago": {
"type": "string",
"example": "2 hours ago"
},
"charity_details": {
"type": "object",
"nullable": true
}
}
}
},
"pagination": {
"type": "object",
"properties": {
"total": {
"type": "integer",
"example": 1
},
"per_page": {
"type": "integer",
"example": 15
},
"current_page": {
"type": "integer",
"example": 1
},
"last_page": {
"type": "integer",
"example": 1
}
}
}
}
}
}
}
}
}
} }
} }
}, },
@ -3628,7 +3712,85 @@
"description": "Retrieves stats including contacted workers count, total hired workers, saved candidates, current plan details, and recent announcements or events.", "description": "Retrieves stats including contacted workers count, total hired workers, saved candidates, current plan details, and recent announcements or events.",
"responses": { "responses": {
"200": { "200": {
"description": "Employer dashboard stats retrieved successfully." "description": "Employer dashboard stats retrieved successfully.",
"content": {
"application/json": {
"schema": {
"type": "object",
"properties": {
"success": {
"type": "boolean",
"example": true
},
"data": {
"type": "object",
"properties": {
"stats": {
"type": "object",
"properties": {
"contacted_workers_count": {
"type": "integer",
"example": 0
},
"total_hired_workers": {
"type": "integer",
"example": 0
},
"saved_candidates": {
"type": "integer",
"example": 0
},
"total_workers": {
"type": "integer",
"example": 15
}
}
},
"current_plan": {
"type": "object",
"properties": {
"plan_id": {
"type": "string",
"example": "premium"
},
"name": {
"type": "string",
"example": "Premium Pass"
},
"status": {
"type": "string",
"example": "active"
},
"starts_at": {
"type": "string",
"format": "date",
"example": "2026-06-23"
},
"expires_at": {
"type": "string",
"format": "date",
"example": "2026-07-23"
}
}
},
"recent_announcements": {
"type": "array",
"items": {
"type": "object"
}
},
"recent_events": {
"type": "array",
"items": {
"type": "object"
}
}
}
}
}
}
}
}
} }
} }
} }
@ -5515,6 +5677,122 @@
} }
} }
} }
},
"/employers/my-announcements": {
"get": {
"tags": [
"Employer/CharityEvents"
],
"summary": "Get My Posted Announcements/Charity Events (Employer)",
"description": "Retrieves the list of announcements and charity events posted specifically by the logged-in employer.",
"parameters": [
{
"name": "page",
"in": "query",
"required": false,
"description": "Page number.",
"schema": {
"type": "integer",
"default": 1
}
},
{
"name": "per_page",
"in": "query",
"required": false,
"description": "Number of items per page.",
"schema": {
"type": "integer",
"default": 15
}
}
],
"responses": {
"200": {
"description": "List of my posted announcements retrieved successfully.",
"content": {
"application/json": {
"schema": {
"type": "object",
"properties": {
"success": {
"type": "boolean",
"example": true
},
"data": {
"type": "object",
"properties": {
"announcements": {
"type": "array",
"items": {
"type": "object",
"properties": {
"id": {
"type": "integer",
"example": 1
},
"title": {
"type": "string",
"example": "Holiday Notice"
},
"body": {
"type": "string",
"example": "Office will be closed tomorrow."
},
"type": {
"type": "string",
"example": "info"
},
"status": {
"type": "string",
"example": "pending"
},
"remarks": {
"type": "string",
"nullable": true
},
"created_at": {
"type": "string",
"format": "date-time",
"example": "2026-06-23T10:13:17.000000Z"
},
"time_ago": {
"type": "string",
"example": "2 hours ago"
}
}
}
},
"pagination": {
"type": "object",
"properties": {
"total": {
"type": "integer",
"example": 1
},
"per_page": {
"type": "integer",
"example": 15
},
"current_page": {
"type": "integer",
"example": 1
},
"last_page": {
"type": "integer",
"example": 1
}
}
}
}
}
}
}
}
}
}
}
}
} }
}, },
"components": { "components": {

View File

@ -119,6 +119,7 @@
// Announcement Management // Announcement Management
Route::get('/employers/announcements', [EmployerAnnouncementController::class, 'getAnnouncements']); Route::get('/employers/announcements', [EmployerAnnouncementController::class, 'getAnnouncements']);
Route::get('/employers/my-announcements', [EmployerAnnouncementController::class, 'getMyAnnouncements']);
Route::post('/employers/announcements', [EmployerAnnouncementController::class, 'createAnnouncement']); Route::post('/employers/announcements', [EmployerAnnouncementController::class, 'createAnnouncement']);
Route::delete('/employers/announcements/{id}', [EmployerAnnouncementController::class, 'deleteAnnouncement']); Route::delete('/employers/announcements/{id}', [EmployerAnnouncementController::class, 'deleteAnnouncement']);

View File

@ -103,9 +103,9 @@ public function test_worker_can_get_announcements()
} }
/** /**
* Test employer can get only their posted announcements. * Test employer can get only their posted announcements via my-announcements.
*/ */
public function test_employer_can_get_posted_announcements() public function test_employer_can_get_my_announcements()
{ {
// Announcement by this employer // Announcement by this employer
Announcement::create([ Announcement::create([
@ -131,13 +131,66 @@ public function test_employer_can_get_posted_announcements()
$response = $this->withHeaders([ $response = $this->withHeaders([
'Authorization' => 'Bearer ' . $this->employerToken, 'Authorization' => 'Bearer ' . $this->employerToken,
])->getJson('/api/employers/announcements'); ])->getJson('/api/employers/my-announcements');
$response->assertStatus(200); $response->assertStatus(200);
$this->assertCount(1, $response->json('data.announcements')); $this->assertCount(1, $response->json('data.announcements'));
$this->assertEquals('Employer Ann', $response->json('data.announcements.0.title')); $this->assertEquals('Employer Ann', $response->json('data.announcements.0.title'));
} }
/**
* Test employer can get all approved announcements.
*/
public function test_employer_can_get_all_approved_announcements()
{
// Approved announcement by this employer
Announcement::create([
'title' => 'Approved Ann 1',
'body' => 'Message 1',
'type' => 'info',
'employer_id' => $this->employer->id,
'status' => 'approved',
]);
// Pending announcement by this employer
Announcement::create([
'title' => 'Pending Ann 1',
'body' => 'Message 2',
'type' => 'info',
'employer_id' => $this->employer->id,
'status' => 'pending',
]);
// Approved announcement by another sponsor
$sponsor = \App\Models\Sponsor::create([
'full_name' => 'Sponsor Name',
'email' => 'sponsor@example.com',
'mobile' => '+971501112233',
'organization_name' => 'Charity Org',
'address' => 'Dubai',
'role' => 'sponsor',
'password' => bcrypt('password'),
]);
Announcement::create([
'title' => 'Approved Ann 2',
'body' => 'Message 3',
'type' => 'info',
'sponsor_id' => $sponsor->id,
'status' => 'approved',
]);
$response = $this->withHeaders([
'Authorization' => 'Bearer ' . $this->employerToken,
])->getJson('/api/employers/announcements');
$response->assertStatus(200);
$this->assertCount(2, $response->json('data.announcements'));
$titles = collect($response->json('data.announcements'))->pluck('title');
$this->assertTrue($titles->contains('Approved Ann 1'));
$this->assertTrue($titles->contains('Approved Ann 2'));
$this->assertFalse($titles->contains('Pending Ann 1'));
}
/** /**
* Test employer can create a new announcement. * Test employer can create a new announcement.
*/ */