charity event contact updated
This commit is contained in:
parent
93ffc13af5
commit
00e031a009
@ -73,6 +73,7 @@ VITE_APP_NAME="${APP_NAME}"
|
|||||||
# Notification Reminder Settings
|
# Notification Reminder Settings
|
||||||
WORKER_NO_RESPONSE_REMINDER_DAYS=14
|
WORKER_NO_RESPONSE_REMINDER_DAYS=14
|
||||||
REVIEW_ELIGIBILITY_MONTHS=3
|
REVIEW_ELIGIBILITY_MONTHS=3
|
||||||
|
REVIEW_ELIGIBILITY_MINUTES=10
|
||||||
REVIEW_EDIT_PERIOD_DAYS=7
|
REVIEW_EDIT_PERIOD_DAYS=7
|
||||||
EMPLOYER_HIRE_CONFIRM_REMINDER_DAYS=3
|
EMPLOYER_HIRE_CONFIRM_REMINDER_DAYS=3
|
||||||
WORKER_JOIN_CONFIRM_REMINDER_DAYS=3
|
WORKER_JOIN_CONFIRM_REMINDER_DAYS=3
|
||||||
|
|||||||
@ -374,7 +374,9 @@ private function checkWorkerNoResponse()
|
|||||||
private function checkReviewReminders()
|
private function checkReviewReminders()
|
||||||
{
|
{
|
||||||
$eligibilityMonths = (int) config('reminders.review_eligibility_months', 3);
|
$eligibilityMonths = (int) config('reminders.review_eligibility_months', 3);
|
||||||
$level = "{$eligibilityMonths}_months";
|
$eligibilityMinutes = config('reminders.review_eligibility_minutes');
|
||||||
|
$isMinutes = ($eligibilityMinutes !== null && $eligibilityMinutes > 0);
|
||||||
|
$level = $isMinutes ? "{$eligibilityMinutes}_minutes" : "{$eligibilityMonths}_months";
|
||||||
|
|
||||||
// 1. Standard Job Applications with status 'hired'
|
// 1. Standard Job Applications with status 'hired'
|
||||||
$applications = \App\Models\JobApplication::where('status', 'hired')
|
$applications = \App\Models\JobApplication::where('status', 'hired')
|
||||||
@ -390,9 +392,16 @@ private function checkReviewReminders()
|
|||||||
if (!$employer || !$worker) continue;
|
if (!$employer || !$worker) continue;
|
||||||
|
|
||||||
$joiningDate = Carbon::parse($app->joining_confirmed_at);
|
$joiningDate = Carbon::parse($app->joining_confirmed_at);
|
||||||
$months = $joiningDate->startOfDay()->diffInMonths(now()->startOfDay(), false);
|
|
||||||
|
|
||||||
if ($months >= $eligibilityMonths) {
|
$eligible = false;
|
||||||
|
if ($isMinutes) {
|
||||||
|
$eligible = $joiningDate->diffInMinutes(now(), false) >= $eligibilityMinutes;
|
||||||
|
} else {
|
||||||
|
$months = $joiningDate->startOfDay()->diffInMonths(now()->startOfDay(), false);
|
||||||
|
$eligible = $months >= $eligibilityMonths;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($eligible) {
|
||||||
// Remind worker to review employer
|
// Remind worker to review employer
|
||||||
if (!$this->alreadySent($worker, 'review_employer_reminder', $app, $level)) {
|
if (!$this->alreadySent($worker, 'review_employer_reminder', $app, $level)) {
|
||||||
$this->info("Sending review reminder to worker {$worker->name} to review employer {$employer->name}.");
|
$this->info("Sending review reminder to worker {$worker->name} to review employer {$employer->name}.");
|
||||||
@ -428,9 +437,16 @@ private function checkReviewReminders()
|
|||||||
if (!$worker || !$employer) continue;
|
if (!$worker || !$employer) continue;
|
||||||
|
|
||||||
$joiningDate = $offer->work_date ? Carbon::parse($offer->work_date) : $offer->updated_at;
|
$joiningDate = $offer->work_date ? Carbon::parse($offer->work_date) : $offer->updated_at;
|
||||||
$months = $joiningDate->startOfDay()->diffInMonths(now()->startOfDay(), false);
|
|
||||||
|
|
||||||
if ($months >= $eligibilityMonths) {
|
$eligible = false;
|
||||||
|
if ($isMinutes) {
|
||||||
|
$eligible = $joiningDate->diffInMinutes(now(), false) >= $eligibilityMinutes;
|
||||||
|
} else {
|
||||||
|
$months = $joiningDate->startOfDay()->diffInMonths(now()->startOfDay(), false);
|
||||||
|
$eligible = $months >= $eligibilityMonths;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($eligible) {
|
||||||
// Remind worker to review employer
|
// Remind worker to review employer
|
||||||
if (!$this->alreadySent($worker, 'review_employer_reminder', $offer, $level)) {
|
if (!$this->alreadySent($worker, 'review_employer_reminder', $offer, $level)) {
|
||||||
$this->info("Sending review reminder to worker {$worker->name} to review employer {$employer->name} (direct offer).");
|
$this->info("Sending review reminder to worker {$worker->name} to review employer {$employer->name} (direct offer).");
|
||||||
|
|||||||
@ -108,15 +108,26 @@ public function getMyAnnouncements(Request $request)
|
|||||||
|
|
||||||
$announcements = $query->skip($offset)->take($perPage)->get()
|
$announcements = $query->skip($offset)->take($perPage)->get()
|
||||||
->map(function ($announcement) {
|
->map(function ($announcement) {
|
||||||
|
$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 [
|
return [
|
||||||
'id' => $announcement->id,
|
'id' => $announcement->id,
|
||||||
'title' => $announcement->title,
|
'title' => $announcement->title,
|
||||||
'body' => $announcement->body,
|
'body' => $content,
|
||||||
'type' => $announcement->type,
|
'type' => $announcement->type,
|
||||||
'status' => $announcement->status ?? 'pending',
|
'status' => $announcement->status ?? 'pending',
|
||||||
'remarks' => $announcement->remarks,
|
'remarks' => $announcement->remarks,
|
||||||
'created_at' => $announcement->created_at->toISOString(),
|
'created_at' => $announcement->created_at->toISOString(),
|
||||||
'time_ago' => $announcement->created_at->diffForHumans(),
|
'time_ago' => $announcement->created_at->diffForHumans(),
|
||||||
|
'charity_details' => $charityDetails,
|
||||||
];
|
];
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -155,12 +166,33 @@ public function createAnnouncement(Request $request)
|
|||||||
/** @var User $employer */
|
/** @var User $employer */
|
||||||
$employer = $request->attributes->get('employer');
|
$employer = $request->attributes->get('employer');
|
||||||
|
|
||||||
$validator = Validator::make($request->all(), [
|
$isCharity = $request->type === 'charity' ||
|
||||||
|
$request->has('event_date') ||
|
||||||
|
$request->has('location_details') ||
|
||||||
|
$request->has('provided_items') ||
|
||||||
|
$request->has('contact_person_name');
|
||||||
|
|
||||||
|
$rules = [
|
||||||
'title' => 'required|string|max:255',
|
'title' => 'required|string|max:255',
|
||||||
'body' => 'required_without:content|string|max:5000',
|
'body' => 'required_without:content|string|max:5000',
|
||||||
'content' => 'required_without:body|string|max:5000',
|
'content' => 'required_without:body|string|max:5000',
|
||||||
'type' => 'nullable|string|in:info,warning,success',
|
'type' => 'nullable|string|in:info,warning,success,charity',
|
||||||
]);
|
];
|
||||||
|
|
||||||
|
if ($isCharity) {
|
||||||
|
$rules['event_date'] = 'required|string';
|
||||||
|
$rules['event_time'] = 'required_without_all:start_time,end_time|nullable|string';
|
||||||
|
$rules['start_time'] = 'required_without:event_time|nullable|string';
|
||||||
|
$rules['end_time'] = 'required_without:event_time|nullable|string';
|
||||||
|
$rules['provided_items'] = 'required|string';
|
||||||
|
$rules['location_details'] = 'required|string';
|
||||||
|
$rules['location_pin'] = 'required|string|url';
|
||||||
|
$rules['contact_person_name'] = 'required|string|max:255';
|
||||||
|
$rules['contact_number'] = 'required|string|regex:/^\d{7,15}$/';
|
||||||
|
$rules['country_code'] = 'required|string|regex:/^\+\d{1,4}$/';
|
||||||
|
}
|
||||||
|
|
||||||
|
$validator = Validator::make($request->all(), $rules);
|
||||||
|
|
||||||
if ($validator->fails()) {
|
if ($validator->fails()) {
|
||||||
return response()->json([
|
return response()->json([
|
||||||
@ -173,26 +205,44 @@ public function createAnnouncement(Request $request)
|
|||||||
try {
|
try {
|
||||||
$bodyText = $request->body ?? $request->content;
|
$bodyText = $request->body ?? $request->content;
|
||||||
|
|
||||||
// Append extra event details if they are provided
|
if ($isCharity) {
|
||||||
$extras = [];
|
$eventTime = $request->event_time;
|
||||||
if ($request->event_date) $extras[] = "Date: " . $request->event_date;
|
if (empty($eventTime)) {
|
||||||
if ($request->event_time) $extras[] = "Time: " . $request->event_time;
|
$eventTime = $request->start_time . ' - ' . $request->end_time;
|
||||||
if ($request->location_details) $extras[] = "Location: " . $request->location_details;
|
}
|
||||||
if ($request->location_pin) $extras[] = "Map Pin: " . $request->location_pin;
|
|
||||||
if ($request->provided_items) $extras[] = "Provided: " . $request->provided_items;
|
|
||||||
|
|
||||||
if (!empty($extras)) {
|
$bodyText = json_encode([
|
||||||
$bodyText .= "\n\n" . implode("\n", $extras);
|
'type' => 'Charity',
|
||||||
|
'provided_items' => $request->provided_items,
|
||||||
|
'event_date' => $request->event_date,
|
||||||
|
'event_time' => $eventTime,
|
||||||
|
'location_details' => $request->location_details,
|
||||||
|
'location_pin' => $request->location_pin,
|
||||||
|
'content' => $request->body ?? $request->content,
|
||||||
|
'contact_person_name' => $request->contact_person_name,
|
||||||
|
'contact_number' => $request->contact_number,
|
||||||
|
'country_code' => $request->country_code,
|
||||||
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
$announcement = Announcement::create([
|
$announcement = Announcement::create([
|
||||||
'title' => $request->title,
|
'title' => $request->title,
|
||||||
'body' => $bodyText,
|
'body' => $bodyText,
|
||||||
'type' => $request->type ?? 'info',
|
'type' => $request->type ?? ($isCharity ? 'charity' : 'info'),
|
||||||
'employer_id' => $employer->id,
|
'employer_id' => $employer->id,
|
||||||
'status' => 'pending',
|
'status' => 'pending',
|
||||||
]);
|
]);
|
||||||
|
|
||||||
|
$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 response()->json([
|
return response()->json([
|
||||||
'success' => true,
|
'success' => true,
|
||||||
'message' => 'Announcement posted successfully.',
|
'message' => 'Announcement posted successfully.',
|
||||||
@ -200,12 +250,13 @@ public function createAnnouncement(Request $request)
|
|||||||
'announcement' => [
|
'announcement' => [
|
||||||
'id' => $announcement->id,
|
'id' => $announcement->id,
|
||||||
'title' => $announcement->title,
|
'title' => $announcement->title,
|
||||||
'body' => $announcement->body,
|
'body' => $content,
|
||||||
'type' => $announcement->type,
|
'type' => $announcement->type,
|
||||||
'status' => $announcement->status ?? 'pending',
|
'status' => $announcement->status ?? 'pending',
|
||||||
'remarks' => $announcement->remarks,
|
'remarks' => $announcement->remarks,
|
||||||
'created_at' => $announcement->created_at->toISOString(),
|
'created_at' => $announcement->created_at->toISOString(),
|
||||||
'time_ago' => $announcement->created_at->diffForHumans(),
|
'time_ago' => $announcement->created_at->diffForHumans(),
|
||||||
|
'charity_details' => $charityDetails,
|
||||||
]
|
]
|
||||||
]
|
]
|
||||||
], 201);
|
], 201);
|
||||||
|
|||||||
@ -400,12 +400,7 @@ public function getDashboard(Request $request)
|
|||||||
// Recent Announcements / Events
|
// Recent Announcements / Events
|
||||||
$dbAnnouncements = \App\Models\Announcement::where('status', 'approved')->latest()->limit(2)->get();
|
$dbAnnouncements = \App\Models\Announcement::where('status', 'approved')->latest()->limit(2)->get();
|
||||||
$recentAnnouncements = $dbAnnouncements->map(function ($ann) {
|
$recentAnnouncements = $dbAnnouncements->map(function ($ann) {
|
||||||
$body = $ann->body;
|
$charityDetails = null;
|
||||||
$eventDate = null;
|
|
||||||
$eventTime = null;
|
|
||||||
$locationDetails = null;
|
|
||||||
$locationPin = null;
|
|
||||||
$providedItems = null;
|
|
||||||
|
|
||||||
if (strpos($ann->body, '{"type":"Charity"') === 0) {
|
if (strpos($ann->body, '{"type":"Charity"') === 0) {
|
||||||
$decoded = json_decode($ann->body, true);
|
$decoded = json_decode($ann->body, true);
|
||||||
@ -416,6 +411,7 @@ public function getDashboard(Request $request)
|
|||||||
$locationDetails = $decoded['location_details'] ?? null;
|
$locationDetails = $decoded['location_details'] ?? null;
|
||||||
$locationPin = $decoded['location_pin'] ?? null;
|
$locationPin = $decoded['location_pin'] ?? null;
|
||||||
$providedItems = $decoded['provided_items'] ?? null;
|
$providedItems = $decoded['provided_items'] ?? null;
|
||||||
|
$charityDetails = $decoded;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
$body = $ann->body;
|
$body = $ann->body;
|
||||||
@ -438,6 +434,7 @@ public function getDashboard(Request $request)
|
|||||||
'provided_items' => $providedItems,
|
'provided_items' => $providedItems,
|
||||||
'created_at' => $ann->created_at->toISOString(),
|
'created_at' => $ann->created_at->toISOString(),
|
||||||
'time_ago' => $ann->created_at->diffForHumans(),
|
'time_ago' => $ann->created_at->diffForHumans(),
|
||||||
|
'charity_details' => $charityDetails,
|
||||||
];
|
];
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@ -71,6 +71,16 @@ public function addReview(Request $request)
|
|||||||
], 403);
|
], 403);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Verify if completed configured period from joining date
|
||||||
|
$eligibilityMinutes = config('reminders.review_eligibility_minutes');
|
||||||
|
if ($eligibilityMinutes !== null && $eligibilityMinutes > 0) {
|
||||||
|
if ($joiningDate->diffInMinutes(now(), false) < $eligibilityMinutes) {
|
||||||
|
return response()->json([
|
||||||
|
'success' => false,
|
||||||
|
'message' => "You can write a review only after {$eligibilityMinutes} minutes from the joining date."
|
||||||
|
], 403);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
$eligibilityMonths = (int) config('reminders.review_eligibility_months', 3);
|
$eligibilityMonths = (int) config('reminders.review_eligibility_months', 3);
|
||||||
if ($joiningDate->startOfDay()->diffInMonths(now()->startOfDay(), false) < $eligibilityMonths) {
|
if ($joiningDate->startOfDay()->diffInMonths(now()->startOfDay(), false) < $eligibilityMonths) {
|
||||||
return response()->json([
|
return response()->json([
|
||||||
@ -78,6 +88,7 @@ public function addReview(Request $request)
|
|||||||
'message' => "You can write a review only after {$eligibilityMonths} months from the joining date."
|
'message' => "You can write a review only after {$eligibilityMonths} months from the joining date."
|
||||||
], 403);
|
], 403);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Check if employer has already reviewed this worker
|
// Check if employer has already reviewed this worker
|
||||||
$existingReview = Review::where('employer_id', $employer->id)
|
$existingReview = Review::where('employer_id', $employer->id)
|
||||||
|
|||||||
@ -90,7 +90,16 @@ public function addReview(Request $request)
|
|||||||
], 403);
|
], 403);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Verify if completed configured months from joining date
|
// Verify if completed configured period from joining date
|
||||||
|
$eligibilityMinutes = config('reminders.review_eligibility_minutes');
|
||||||
|
if ($eligibilityMinutes !== null && $eligibilityMinutes > 0) {
|
||||||
|
if ($joiningDate->diffInMinutes(now(), false) < $eligibilityMinutes) {
|
||||||
|
return response()->json([
|
||||||
|
'success' => false,
|
||||||
|
'message' => "You can write a review only after {$eligibilityMinutes} minutes from the joining date."
|
||||||
|
], 403);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
$eligibilityMonths = (int) config('reminders.review_eligibility_months', 3);
|
$eligibilityMonths = (int) config('reminders.review_eligibility_months', 3);
|
||||||
if ($joiningDate->startOfDay()->diffInMonths(now()->startOfDay(), false) < $eligibilityMonths) {
|
if ($joiningDate->startOfDay()->diffInMonths(now()->startOfDay(), false) < $eligibilityMonths) {
|
||||||
return response()->json([
|
return response()->json([
|
||||||
@ -98,6 +107,7 @@ public function addReview(Request $request)
|
|||||||
'message' => "You can write a review only after {$eligibilityMonths} months from the joining date."
|
'message' => "You can write a review only after {$eligibilityMonths} months from the joining date."
|
||||||
], 403);
|
], 403);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// 2. Prevent duplicate reviews for the same job/employer combination
|
// 2. Prevent duplicate reviews for the same job/employer combination
|
||||||
$existing = EmployerReview::where('worker_id', $worker->id)
|
$existing = EmployerReview::where('worker_id', $worker->id)
|
||||||
|
|||||||
@ -3,6 +3,7 @@
|
|||||||
return [
|
return [
|
||||||
'worker_no_response_reminder_days' => env('WORKER_NO_RESPONSE_REMINDER_DAYS', 14),
|
'worker_no_response_reminder_days' => env('WORKER_NO_RESPONSE_REMINDER_DAYS', 14),
|
||||||
'review_eligibility_months' => env('REVIEW_ELIGIBILITY_MONTHS', 3),
|
'review_eligibility_months' => env('REVIEW_ELIGIBILITY_MONTHS', 3),
|
||||||
|
'review_eligibility_minutes' => env('REVIEW_ELIGIBILITY_MINUTES'),
|
||||||
'review_edit_period_days' => env('REVIEW_EDIT_PERIOD_DAYS', 7),
|
'review_edit_period_days' => env('REVIEW_EDIT_PERIOD_DAYS', 7),
|
||||||
'employer_hire_confirm_reminder_days' => env('EMPLOYER_HIRE_CONFIRM_REMINDER_DAYS', '3,7'),
|
'employer_hire_confirm_reminder_days' => env('EMPLOYER_HIRE_CONFIRM_REMINDER_DAYS', '3,7'),
|
||||||
'worker_join_confirm_reminder_days' => env('WORKER_JOIN_CONFIRM_REMINDER_DAYS', '3,7,1'),
|
'worker_join_confirm_reminder_days' => env('WORKER_JOIN_CONFIRM_REMINDER_DAYS', '3,7,1'),
|
||||||
|
|||||||
@ -36,6 +36,7 @@
|
|||||||
<!-- Reminder Configuration for Tests -->
|
<!-- Reminder Configuration for Tests -->
|
||||||
<env name="WORKER_NO_RESPONSE_REMINDER_DAYS" value="14"/>
|
<env name="WORKER_NO_RESPONSE_REMINDER_DAYS" value="14"/>
|
||||||
<env name="REVIEW_ELIGIBILITY_MONTHS" value="3"/>
|
<env name="REVIEW_ELIGIBILITY_MONTHS" value="3"/>
|
||||||
|
<env name="REVIEW_ELIGIBILITY_MINUTES" value="0"/>
|
||||||
<env name="REVIEW_EDIT_PERIOD_DAYS" value="7"/>
|
<env name="REVIEW_EDIT_PERIOD_DAYS" value="7"/>
|
||||||
<env name="EMPLOYER_HIRE_CONFIRM_REMINDER_DAYS" value="3,7"/>
|
<env name="EMPLOYER_HIRE_CONFIRM_REMINDER_DAYS" value="3,7"/>
|
||||||
<env name="WORKER_JOIN_CONFIRM_REMINDER_DAYS" value="3,7,1"/>
|
<env name="WORKER_JOIN_CONFIRM_REMINDER_DAYS" value="3,7,1"/>
|
||||||
|
|||||||
@ -4730,7 +4730,10 @@
|
|||||||
"event_date",
|
"event_date",
|
||||||
"event_time",
|
"event_time",
|
||||||
"location_details",
|
"location_details",
|
||||||
"location_pin"
|
"location_pin",
|
||||||
|
"contact_person_name",
|
||||||
|
"contact_number",
|
||||||
|
"country_code"
|
||||||
],
|
],
|
||||||
"properties": {
|
"properties": {
|
||||||
"title": {
|
"title": {
|
||||||
@ -4762,6 +4765,18 @@
|
|||||||
"type": "string",
|
"type": "string",
|
||||||
"format": "uri",
|
"format": "uri",
|
||||||
"example": "https://maps.app.goo.gl/xyz"
|
"example": "https://maps.app.goo.gl/xyz"
|
||||||
|
},
|
||||||
|
"contact_person_name": {
|
||||||
|
"type": "string",
|
||||||
|
"example": "Jane Doe"
|
||||||
|
},
|
||||||
|
"contact_number": {
|
||||||
|
"type": "string",
|
||||||
|
"example": "551234567"
|
||||||
|
},
|
||||||
|
"country_code": {
|
||||||
|
"type": "string",
|
||||||
|
"example": "+971"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -347,4 +347,90 @@ public function test_admin_can_reject_announcement_with_remarks()
|
|||||||
'remarks' => 'Inappropriate content or location details.',
|
'remarks' => 'Inappropriate content or location details.',
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Test employer can post and get charity announcements with contact details.
|
||||||
|
*/
|
||||||
|
public function test_employer_can_post_and_get_charity_announcement_with_contact_details()
|
||||||
|
{
|
||||||
|
$response = $this->withHeaders([
|
||||||
|
'Authorization' => 'Bearer ' . $this->employerToken,
|
||||||
|
])->postJson('/api/employers/announcements', [
|
||||||
|
'title' => 'Free Dental Checkup Camp',
|
||||||
|
'content' => 'Emirates Charity is providing free professional dental screening, cleanings, and educational packages for domestic helpers.',
|
||||||
|
'provided_items' => 'Free Dental screening, cleanings, and wellness kits',
|
||||||
|
'event_date' => '2026-06-15',
|
||||||
|
'event_time' => '9:00 AM - 4:00 PM',
|
||||||
|
'location_details' => 'Al Quoz Community Hall, Dubai',
|
||||||
|
'location_pin' => 'https://maps.app.goo.gl/xyz',
|
||||||
|
'contact_person_name' => 'Jane Doe',
|
||||||
|
'contact_number' => '551234567',
|
||||||
|
'country_code' => '+971',
|
||||||
|
]);
|
||||||
|
|
||||||
|
$response->assertStatus(201)
|
||||||
|
->assertJson([
|
||||||
|
'success' => true,
|
||||||
|
'message' => 'Announcement posted successfully.',
|
||||||
|
])
|
||||||
|
->assertJsonStructure([
|
||||||
|
'data' => [
|
||||||
|
'announcement' => [
|
||||||
|
'id',
|
||||||
|
'title',
|
||||||
|
'body',
|
||||||
|
'type',
|
||||||
|
'status',
|
||||||
|
'charity_details' => [
|
||||||
|
'type',
|
||||||
|
'provided_items',
|
||||||
|
'event_date',
|
||||||
|
'event_time',
|
||||||
|
'location_details',
|
||||||
|
'location_pin',
|
||||||
|
'content',
|
||||||
|
'contact_person_name',
|
||||||
|
'contact_number',
|
||||||
|
'country_code',
|
||||||
|
]
|
||||||
|
]
|
||||||
|
]
|
||||||
|
]);
|
||||||
|
|
||||||
|
$announcementId = $response->json('data.announcement.id');
|
||||||
|
|
||||||
|
// Approve it so it shows up in global get endpoint
|
||||||
|
Announcement::find($announcementId)->update(['status' => 'approved']);
|
||||||
|
|
||||||
|
// Test GET /api/employers/announcements
|
||||||
|
$getResponse = $this->withHeaders([
|
||||||
|
'Authorization' => 'Bearer ' . $this->employerToken,
|
||||||
|
])->getJson('/api/employers/announcements');
|
||||||
|
|
||||||
|
$getResponse->assertStatus(200);
|
||||||
|
$announcements = $getResponse->json('data.announcements');
|
||||||
|
$postedAnn = collect($announcements)->firstWhere('id', $announcementId);
|
||||||
|
|
||||||
|
$this->assertNotNull($postedAnn);
|
||||||
|
$this->assertEquals('Free Dental Checkup Camp', $postedAnn['title']);
|
||||||
|
$this->assertEquals('Jane Doe', $postedAnn['charity_details']['contact_person_name']);
|
||||||
|
$this->assertEquals('551234567', $postedAnn['charity_details']['contact_number']);
|
||||||
|
$this->assertEquals('+971', $postedAnn['charity_details']['country_code']);
|
||||||
|
|
||||||
|
// Test GET /api/employers/my-announcements
|
||||||
|
$getMyResponse = $this->withHeaders([
|
||||||
|
'Authorization' => 'Bearer ' . $this->employerToken,
|
||||||
|
])->getJson('/api/employers/my-announcements');
|
||||||
|
|
||||||
|
$getMyResponse->assertStatus(200);
|
||||||
|
$myAnnouncements = $getMyResponse->json('data.announcements');
|
||||||
|
$myPostedAnn = collect($myAnnouncements)->firstWhere('id', $announcementId);
|
||||||
|
|
||||||
|
$this->assertNotNull($myPostedAnn);
|
||||||
|
$this->assertEquals('Free Dental Checkup Camp', $myPostedAnn['title']);
|
||||||
|
$this->assertEquals('Jane Doe', $myPostedAnn['charity_details']['contact_person_name']);
|
||||||
|
$this->assertEquals('551234567', $myPostedAnn['charity_details']['contact_number']);
|
||||||
|
$this->assertEquals('+971', $myPostedAnn['charity_details']['country_code']);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|||||||
@ -366,4 +366,119 @@ public function test_employer_review_controller_eligibility_and_edit_window()
|
|||||||
$response->assertJsonPath('success', false);
|
$response->assertJsonPath('success', false);
|
||||||
$response->assertJsonPath('message', 'The 7-day edit window for this review has expired.');
|
$response->assertJsonPath('message', 'The 7-day edit window for this review has expired.');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function test_minute_based_review_eligibility()
|
||||||
|
{
|
||||||
|
config(['reminders.review_eligibility_minutes' => 10]);
|
||||||
|
|
||||||
|
$worker = $this->createTestWorker();
|
||||||
|
$employer = $this->createTestEmployer();
|
||||||
|
|
||||||
|
$job = JobPost::create([
|
||||||
|
'employer_id' => $employer->id,
|
||||||
|
'title' => 'Nanny',
|
||||||
|
'location' => 'Dubai',
|
||||||
|
'salary' => 2500,
|
||||||
|
'workers_needed' => 1,
|
||||||
|
'job_type' => 'Full Time',
|
||||||
|
'start_date' => now()->subDays(5)->toDateString(),
|
||||||
|
'description' => 'Test job',
|
||||||
|
'status' => 'active',
|
||||||
|
]);
|
||||||
|
|
||||||
|
// JobApplication with joining_confirmed_at exactly 9 minutes ago
|
||||||
|
$app = JobApplication::create([
|
||||||
|
'job_id' => $job->id,
|
||||||
|
'worker_id' => $worker->id,
|
||||||
|
'status' => 'hired',
|
||||||
|
'joining_confirmed_at' => now()->subMinutes(9),
|
||||||
|
]);
|
||||||
|
|
||||||
|
// 1. Worker review submission fails at 9 minutes
|
||||||
|
$response = $this->postJson('/api/workers/reviews', [
|
||||||
|
'employer_id' => $employer->id,
|
||||||
|
'job_id' => $job->id,
|
||||||
|
'rating' => 5,
|
||||||
|
'comment' => 'Great employer!',
|
||||||
|
], [
|
||||||
|
'Authorization' => 'Bearer worker_api_token'
|
||||||
|
]);
|
||||||
|
$response->assertStatus(403);
|
||||||
|
$response->assertJsonPath('message', 'You can write a review only after 10 minutes from the joining date.');
|
||||||
|
|
||||||
|
// 2. Employer review submission fails at 9 minutes
|
||||||
|
$response = $this->postJson('/api/employers/reviews', [
|
||||||
|
'worker_id' => $worker->id,
|
||||||
|
'rating' => 5,
|
||||||
|
'comment' => 'Great worker!',
|
||||||
|
], [
|
||||||
|
'Authorization' => 'Bearer employer_api_token'
|
||||||
|
]);
|
||||||
|
$response->assertStatus(403);
|
||||||
|
$response->assertJsonPath('message', 'You can write a review only after 10 minutes from the joining date.');
|
||||||
|
|
||||||
|
// 3. Scheduler does NOT send reminder at 9 minutes
|
||||||
|
Notification::fake();
|
||||||
|
Artisan::call('app:send-reminders');
|
||||||
|
Notification::assertNothingSent();
|
||||||
|
|
||||||
|
// 4. Update joining date to 10 minutes ago
|
||||||
|
$app->joining_confirmed_at = now()->subMinutes(10);
|
||||||
|
$app->save();
|
||||||
|
|
||||||
|
// 5. Worker review submission succeeds at 10 minutes
|
||||||
|
$response = $this->postJson('/api/workers/reviews', [
|
||||||
|
'employer_id' => $employer->id,
|
||||||
|
'job_id' => $job->id,
|
||||||
|
'rating' => 5,
|
||||||
|
'comment' => 'Great employer!',
|
||||||
|
], [
|
||||||
|
'Authorization' => 'Bearer worker_api_token'
|
||||||
|
]);
|
||||||
|
$response->assertStatus(201);
|
||||||
|
|
||||||
|
// 6. Employer review submission succeeds at 10 minutes
|
||||||
|
$response = $this->postJson('/api/employers/reviews', [
|
||||||
|
'worker_id' => $worker->id,
|
||||||
|
'rating' => 5,
|
||||||
|
'comment' => 'Great worker!',
|
||||||
|
], [
|
||||||
|
'Authorization' => 'Bearer employer_api_token'
|
||||||
|
]);
|
||||||
|
$response->assertStatus(201);
|
||||||
|
|
||||||
|
// 7. Scheduler sends reminders when updated to 10 minutes ago
|
||||||
|
$worker2 = $this->createTestWorker([
|
||||||
|
'email' => 'worker2@example.com',
|
||||||
|
'phone' => '971500000002',
|
||||||
|
'api_token' => 'worker2_api_token'
|
||||||
|
]);
|
||||||
|
$app2 = JobApplication::create([
|
||||||
|
'job_id' => $job->id,
|
||||||
|
'worker_id' => $worker2->id,
|
||||||
|
'status' => 'hired',
|
||||||
|
'joining_confirmed_at' => now()->subMinutes(10),
|
||||||
|
]);
|
||||||
|
|
||||||
|
Notification::fake();
|
||||||
|
Artisan::call('app:send-reminders');
|
||||||
|
|
||||||
|
Notification::assertSentTo(
|
||||||
|
$worker2,
|
||||||
|
WorkerReviewReminderNotification::class,
|
||||||
|
function ($notification) use ($app2) {
|
||||||
|
return $notification->applicationId === $app2->id &&
|
||||||
|
$notification->reviewLevel === '10_minutes';
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
Notification::assertSentTo(
|
||||||
|
$employer,
|
||||||
|
EmployerReviewReminderNotification::class,
|
||||||
|
function ($notification) use ($app2) {
|
||||||
|
return $notification->applicationId === $app2->id &&
|
||||||
|
$notification->reviewLevel === '10_minutes';
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user