264 lines
9.5 KiB
PHP
264 lines
9.5 KiB
PHP
<?php
|
|
|
|
namespace App\Http\Controllers\Api;
|
|
|
|
use App\Http\Controllers\Controller;
|
|
use Illuminate\Http\Request;
|
|
use App\Models\User;
|
|
use App\Models\Worker;
|
|
use App\Models\Review;
|
|
use Illuminate\Support\Facades\Validator;
|
|
|
|
class EmployerReviewController extends Controller
|
|
{
|
|
/**
|
|
* Add a new review for a worker.
|
|
* POST /api/employers/reviews
|
|
*/
|
|
public function addReview(Request $request)
|
|
{
|
|
/** @var User $employer */
|
|
$employer = $request->attributes->get('employer');
|
|
|
|
$validator = Validator::make($request->all(), [
|
|
'worker_id' => 'required|exists:workers,id',
|
|
'rating' => 'required|integer|min:1|max:5',
|
|
'comment' => 'nullable|string|max:1000',
|
|
]);
|
|
|
|
if ($validator->fails()) {
|
|
return response()->json([
|
|
'success' => false,
|
|
'message' => 'Validation error.',
|
|
'errors' => $validator->errors()
|
|
], 422);
|
|
}
|
|
|
|
try {
|
|
// 1. Validate confirmed hire and completed joining, and review eligibility period
|
|
$hasConfirmedHire = false;
|
|
$joiningDate = null;
|
|
|
|
// Check standard JobApplication where the job belongs to this employer and worker matches
|
|
$app = \App\Models\JobApplication::where('worker_id', $request->worker_id)
|
|
->where('status', 'hired')
|
|
->whereNotNull('joining_confirmed_at')
|
|
->whereHas('jobPost', function ($query) use ($employer) {
|
|
$query->where('employer_id', $employer->id);
|
|
})
|
|
->first();
|
|
|
|
if ($app) {
|
|
$hasConfirmedHire = true;
|
|
$joiningDate = \Carbon\Carbon::parse($app->joining_confirmed_at);
|
|
} else {
|
|
// Check direct JobOffer
|
|
$offer = \App\Models\JobOffer::where('worker_id', $request->worker_id)
|
|
->where('employer_id', $employer->id)
|
|
->where('status', 'accepted')
|
|
->first();
|
|
|
|
if ($offer) {
|
|
$hasConfirmedHire = true;
|
|
$joiningDate = $offer->work_date ? \Carbon\Carbon::parse($offer->work_date) : $offer->updated_at;
|
|
}
|
|
}
|
|
|
|
if (!$hasConfirmedHire) {
|
|
return response()->json([
|
|
'success' => false,
|
|
'message' => 'You can only review workers with a confirmed hire and completed joining.'
|
|
], 403);
|
|
}
|
|
|
|
$eligibilityMonths = (int) config('reminders.review_eligibility_months', 3);
|
|
if ($joiningDate->startOfDay()->diffInMonths(now()->startOfDay(), false) < $eligibilityMonths) {
|
|
return response()->json([
|
|
'success' => false,
|
|
'message' => "You can write a review only after {$eligibilityMonths} months from the joining date."
|
|
], 403);
|
|
}
|
|
|
|
// Check if employer has already reviewed this worker
|
|
$existingReview = Review::where('employer_id', $employer->id)
|
|
->where('worker_id', $request->worker_id)
|
|
->first();
|
|
|
|
if ($existingReview) {
|
|
// Check if the edit window has expired
|
|
$editPeriodDays = (int) config('reminders.review_edit_period_days', 7);
|
|
if ($existingReview->created_at->addDays($editPeriodDays)->isPast()) {
|
|
return response()->json([
|
|
'success' => false,
|
|
'message' => "The {$editPeriodDays}-day edit window for this review has expired."
|
|
], 403);
|
|
}
|
|
|
|
// If they already have a review, we can update it or direct them to the edit endpoint.
|
|
// To be robust, let's update it directly and inform them it was updated (acting as an edit).
|
|
$existingReview->update([
|
|
'rating' => $request->rating,
|
|
'comment' => $request->comment,
|
|
]);
|
|
|
|
return response()->json([
|
|
'success' => true,
|
|
'message' => 'Review updated successfully (existing review updated).',
|
|
'data' => [
|
|
'review' => $existingReview
|
|
]
|
|
], 200);
|
|
}
|
|
|
|
// Create new review
|
|
$review = Review::create([
|
|
'employer_id' => $employer->id,
|
|
'worker_id' => $request->worker_id,
|
|
'rating' => $request->rating,
|
|
'comment' => $request->comment,
|
|
]);
|
|
|
|
return response()->json([
|
|
'success' => true,
|
|
'message' => 'Review added successfully.',
|
|
'data' => [
|
|
'review' => $review
|
|
]
|
|
], 201);
|
|
|
|
} catch (\Exception $e) {
|
|
logger()->error('Mobile API Add Review Failure: ' . $e->getMessage());
|
|
|
|
return response()->json([
|
|
'success' => false,
|
|
'message' => 'An error occurred while adding the review.',
|
|
'error' => app()->environment('local') ? $e->getMessage() : 'Internal Server Error'
|
|
], 500);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Edit/update an existing review.
|
|
* PUT /api/employers/reviews/{id}
|
|
*/
|
|
public function editReview(Request $request, $id)
|
|
{
|
|
/** @var User $employer */
|
|
$employer = $request->attributes->get('employer');
|
|
|
|
$validator = Validator::make($request->all(), [
|
|
'rating' => 'required|integer|min:1|max:5',
|
|
'comment' => 'nullable|string|max:1000',
|
|
]);
|
|
|
|
if ($validator->fails()) {
|
|
return response()->json([
|
|
'success' => false,
|
|
'message' => 'Validation error.',
|
|
'errors' => $validator->errors()
|
|
], 422);
|
|
}
|
|
|
|
try {
|
|
$review = Review::where('id', $id)
|
|
->where('employer_id', $employer->id)
|
|
->first();
|
|
|
|
if (!$review) {
|
|
return response()->json([
|
|
'success' => false,
|
|
'message' => 'Review not found or unauthorized.'
|
|
], 404);
|
|
}
|
|
|
|
// Check if the edit window has expired
|
|
$editPeriodDays = (int) config('reminders.review_edit_period_days', 7);
|
|
if ($review->created_at->addDays($editPeriodDays)->isPast()) {
|
|
return response()->json([
|
|
'success' => false,
|
|
'message' => "The {$editPeriodDays}-day edit window for this review has expired."
|
|
], 403);
|
|
}
|
|
|
|
$review->update([
|
|
'rating' => $request->rating,
|
|
'comment' => $request->comment,
|
|
]);
|
|
|
|
return response()->json([
|
|
'success' => true,
|
|
'message' => 'Review edited successfully.',
|
|
'data' => [
|
|
'review' => $review
|
|
]
|
|
], 200);
|
|
|
|
} catch (\Exception $e) {
|
|
logger()->error('Mobile API Edit Review Failure: ' . $e->getMessage());
|
|
|
|
return response()->json([
|
|
'success' => false,
|
|
'message' => 'An error occurred while updating the review.',
|
|
'error' => app()->environment('local') ? $e->getMessage() : 'Internal Server Error'
|
|
], 500);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Get reviews posted by this employer.
|
|
* GET /api/employers/reviews
|
|
*/
|
|
public function getReviews(Request $request)
|
|
{
|
|
/** @var User $employer */
|
|
$employer = $request->attributes->get('employer');
|
|
|
|
try {
|
|
$page = (int)$request->input('page', 1);
|
|
$perPage = (int)$request->input('per_page', 15);
|
|
|
|
$query = Review::where('employer_id', $employer->id)
|
|
->with('worker')
|
|
->latest();
|
|
|
|
$total = $query->count();
|
|
$offset = ($page - 1) * $perPage;
|
|
|
|
$reviews = $query->skip($offset)->take($perPage)->get()
|
|
->map(function ($rev) {
|
|
return [
|
|
'id' => $rev->id,
|
|
'worker_id' => $rev->worker_id,
|
|
'worker_name' => $rev->worker->name ?? 'Worker',
|
|
'rating' => $rev->rating,
|
|
'comment' => $rev->comment,
|
|
'created_at' => $rev->created_at->toISOString(),
|
|
'time_ago' => $rev->created_at->diffForHumans(),
|
|
];
|
|
});
|
|
|
|
return response()->json([
|
|
'success' => true,
|
|
'data' => [
|
|
'reviews' => $reviews,
|
|
'pagination' => [
|
|
'total' => $total,
|
|
'per_page' => $perPage,
|
|
'current_page' => $page,
|
|
'last_page' => max(1, (int)ceil($total / $perPage)),
|
|
]
|
|
]
|
|
], 200);
|
|
|
|
} catch (\Exception $e) {
|
|
logger()->error('Mobile API Get Employer Reviews Failure: ' . $e->getMessage());
|
|
|
|
return response()->json([
|
|
'success' => false,
|
|
'message' => 'An error occurred while fetching reviews.',
|
|
'error' => app()->environment('local') ? $e->getMessage() : 'Internal Server Error'
|
|
], 500);
|
|
}
|
|
}
|
|
}
|