379 lines
15 KiB
PHP
379 lines
15 KiB
PHP
<?php
|
|
|
|
namespace App\Http\Controllers\Api;
|
|
|
|
use App\Http\Controllers\Controller;
|
|
use App\Models\Conversation;
|
|
use App\Models\Message;
|
|
use App\Models\Worker;
|
|
use App\Models\User;
|
|
use App\Models\JobOffer;
|
|
use Illuminate\Http\Request;
|
|
use Illuminate\Support\Facades\DB;
|
|
use Illuminate\Support\Facades\Validator;
|
|
|
|
class EmployerMessageController extends Controller
|
|
{
|
|
public static function processWorkerResponse($conv, $worker, $text)
|
|
{
|
|
$replyText = strtolower(trim($text));
|
|
|
|
if ($replyText === 'yes' || $replyText === 'no') {
|
|
// Find the last message sent by the employer in this conversation (excluding the current worker message)
|
|
$lastEmployerMessage = Message::where('conversation_id', $conv->id)
|
|
->where('sender_type', 'employer')
|
|
->latest()
|
|
->first();
|
|
|
|
if ($lastEmployerMessage) {
|
|
$questionText = strtolower($lastEmployerMessage->text);
|
|
$isLookingJobQuestion = str_contains($questionText, 'looking job') ||
|
|
str_contains($questionText, 'looking for a job') ||
|
|
str_contains($questionText, 'looking for job') ||
|
|
str_contains($questionText, 'are you looking');
|
|
|
|
if ($isLookingJobQuestion) {
|
|
if ($replyText === 'yes') {
|
|
// S6 Outcome: Update status as Hired
|
|
$worker->update([
|
|
'status' => 'Hired',
|
|
'availability' => 'Hired'
|
|
]);
|
|
|
|
// Accept existing direct offer or application
|
|
$offer = JobOffer::where('employer_id', $conv->employer_id)
|
|
->where('worker_id', $worker->id)
|
|
->first();
|
|
|
|
if ($offer) {
|
|
$offer->update(['status' => 'accepted']);
|
|
} else {
|
|
JobOffer::create([
|
|
'employer_id' => $conv->employer_id,
|
|
'worker_id' => $worker->id,
|
|
'work_date' => now()->format('Y-m-d'),
|
|
'location' => 'Dubai',
|
|
'salary' => $worker->salary ?: 2000,
|
|
'notes' => 'Hired via chat agreement.',
|
|
'status' => 'accepted',
|
|
]);
|
|
}
|
|
} else if ($replyText === 'no') {
|
|
// S5 Outcome: Update status as Hidden (Auto-Hidden from search)
|
|
$worker->update([
|
|
'status' => 'hidden',
|
|
'availability' => 'Not Available'
|
|
]);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Get all conversations for the authorized employer.
|
|
*
|
|
* @param \Illuminate\Http\Request $request
|
|
* @return \Illuminate\Http\JsonResponse
|
|
*/
|
|
public function getConversations(Request $request)
|
|
{
|
|
/** @var User $employer */
|
|
$employer = $request->attributes->get('employer');
|
|
|
|
try {
|
|
$dbConversations = Conversation::where('employer_id', $employer->id)
|
|
->with(['worker', 'messages'])
|
|
->latest('updated_at')
|
|
->get();
|
|
|
|
$conversations = $dbConversations->map(function ($conv) {
|
|
$lastMsg = $conv->messages->last();
|
|
|
|
// Calculate unread count (messages sent by worker that are not read by employer)
|
|
$unreadCount = $conv->messages->filter(function ($msg) {
|
|
return $msg->sender_type === 'worker' && is_null($msg->read_at);
|
|
})->count();
|
|
|
|
return [
|
|
'id' => $conv->id,
|
|
'worker_id' => $conv->worker_id,
|
|
'worker_name' => $conv->worker->name ?? 'Candidate',
|
|
'nationality' => $conv->worker->nationality ?? 'Unknown',
|
|
'worker_status' => strtolower($conv->worker->status ?? 'active'),
|
|
'salary' => ($conv->worker->salary ?? 2000) . ' AED',
|
|
'last_message' => $lastMsg->text ?? 'No messages yet.',
|
|
'unread_count' => $unreadCount,
|
|
'sent_at' => $lastMsg ? $lastMsg->created_at->diffForHumans() : 'Just now',
|
|
'updated_at' => $conv->updated_at->toISOString(),
|
|
];
|
|
});
|
|
|
|
return response()->json([
|
|
'success' => true,
|
|
'data' => [
|
|
'conversations' => $conversations
|
|
]
|
|
], 200);
|
|
|
|
} catch (\Exception $e) {
|
|
logger()->error('Mobile Employer Get Conversations Failure: ' . $e->getMessage());
|
|
|
|
return response()->json([
|
|
'success' => false,
|
|
'message' => 'An error occurred while fetching conversations.',
|
|
'error' => app()->environment('local') ? $e->getMessage() : 'Internal Server Error'
|
|
], 500);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Get messages in a single conversation and mark unread incoming messages as read.
|
|
*
|
|
* @param \Illuminate\Http\Request $request
|
|
* @param int $id
|
|
* @return \Illuminate\Http\JsonResponse
|
|
*/
|
|
public function getMessages(Request $request, $id)
|
|
{
|
|
/** @var User $employer */
|
|
$employer = $request->attributes->get('employer');
|
|
|
|
try {
|
|
$conv = Conversation::where('employer_id', $employer->id)
|
|
->where('id', $id)
|
|
->with(['worker', 'messages'])
|
|
->first();
|
|
|
|
if (!$conv) {
|
|
return response()->json([
|
|
'success' => false,
|
|
'message' => 'Conversation not found.'
|
|
], 404);
|
|
}
|
|
|
|
// Mark incoming worker messages in this conversation as read
|
|
Message::where('conversation_id', $conv->id)
|
|
->where('sender_type', 'worker')
|
|
->whereNull('read_at')
|
|
->update(['read_at' => now()]);
|
|
|
|
$messages = $conv->messages->map(function ($msg) {
|
|
return [
|
|
'id' => $msg->id,
|
|
'sender_type' => $msg->sender_type, // employer or worker
|
|
'text' => $msg->text,
|
|
'time' => $msg->created_at->format('g:i A') . ($msg->created_at->isToday() ? ' Today' : ' ' . $msg->created_at->format('M d')),
|
|
'read_at' => $msg->read_at ? $msg->read_at->toISOString() : null,
|
|
'created_at' => $msg->created_at->toISOString(),
|
|
'attachment_url' => $msg->attachment_path ? asset('storage/' . $msg->attachment_path) : null,
|
|
'attachment_type' => $msg->attachment_type,
|
|
];
|
|
});
|
|
|
|
$conversationDetail = [
|
|
'id' => $conv->id,
|
|
'worker_name' => $conv->worker->name ?? 'Candidate',
|
|
'nationality' => $conv->worker->nationality ?? 'Unknown',
|
|
'worker_status' => strtolower($conv->worker->status ?? 'active'),
|
|
'salary' => ($conv->worker->salary ?? 2000) . ' AED',
|
|
];
|
|
|
|
return response()->json([
|
|
'success' => true,
|
|
'data' => [
|
|
'conversation' => $conversationDetail,
|
|
'messages' => $messages
|
|
]
|
|
], 200);
|
|
|
|
} catch (\Exception $e) {
|
|
logger()->error('Mobile Employer Get Messages Failure: ' . $e->getMessage());
|
|
|
|
return response()->json([
|
|
'success' => false,
|
|
'message' => 'An error occurred while fetching messages.',
|
|
'error' => app()->environment('local') ? $e->getMessage() : 'Internal Server Error'
|
|
], 500);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Send a reply message from the employer to the worker.
|
|
*
|
|
* @param \Illuminate\Http\Request $request
|
|
* @param int $id
|
|
* @return \Illuminate\Http\JsonResponse
|
|
*/
|
|
public function sendMessage(Request $request, $id)
|
|
{
|
|
/** @var User $employer */
|
|
$employer = $request->attributes->get('employer');
|
|
|
|
$validator = Validator::make($request->all(), [
|
|
'text' => 'required_without:file|string|max:1000|nullable',
|
|
'file' => 'nullable|file|mimes:jpg,jpeg,png,pdf,mp3,wav,m4a,ogg,webm,mp4,aac,3gp,amr|max:10240', // 10MB limit
|
|
]);
|
|
|
|
if ($validator->fails()) {
|
|
return response()->json([
|
|
'success' => false,
|
|
'message' => 'Validation error.',
|
|
'errors' => $validator->errors()
|
|
], 422);
|
|
}
|
|
|
|
try {
|
|
$conv = Conversation::where('employer_id', $employer->id)
|
|
->where('id', $id)
|
|
->first();
|
|
|
|
if (!$conv) {
|
|
return response()->json([
|
|
'success' => false,
|
|
'message' => 'Conversation not found.'
|
|
], 404);
|
|
}
|
|
|
|
$attachmentPath = null;
|
|
$attachmentType = null;
|
|
|
|
if ($request->hasFile('file')) {
|
|
$file = $request->file('file');
|
|
$attachmentPath = $file->store('chat_attachments', 'public');
|
|
|
|
$mime = $file->getMimeType();
|
|
if (str_starts_with($mime, 'image/')) {
|
|
$attachmentType = 'image';
|
|
} elseif (str_starts_with($mime, 'audio/')) {
|
|
$attachmentType = 'voice';
|
|
} else {
|
|
$attachmentType = 'document';
|
|
}
|
|
}
|
|
|
|
$message = null;
|
|
DB::transaction(function () use ($conv, $employer, $request, $attachmentPath, $attachmentType, &$message) {
|
|
$message = Message::create([
|
|
'conversation_id' => $conv->id,
|
|
'sender_type' => 'employer',
|
|
'sender_id' => $employer->id,
|
|
'text' => $request->text,
|
|
'attachment_path' => $attachmentPath,
|
|
'attachment_type' => $attachmentType,
|
|
]);
|
|
|
|
// Touch conversation updated_at for sorting
|
|
$conv->touch();
|
|
});
|
|
|
|
// Dispatch push notification to worker
|
|
$worker = $conv->worker;
|
|
if ($worker && $worker->fcm_token) {
|
|
\App\Services\FCMService::sendPushNotification(
|
|
$worker->fcm_token,
|
|
"New Message from " . ($employer->name ?? "Employer"),
|
|
$request->text ?: "Sent an attachment",
|
|
[
|
|
'conversation_id' => $conv->id,
|
|
'sender_type' => 'employer',
|
|
'sender_id' => $employer->id,
|
|
]
|
|
);
|
|
}
|
|
|
|
return response()->json([
|
|
'success' => true,
|
|
'message' => 'Message sent successfully.',
|
|
'data' => [
|
|
'message' => [
|
|
'id' => $message->id,
|
|
'sender_type' => $message->sender_type,
|
|
'text' => $message->text,
|
|
'time' => $message->created_at->format('g:i A') . ' Today',
|
|
'created_at' => $message->created_at->toISOString(),
|
|
'attachment_url' => $message->attachment_path ? asset('storage/' . $message->attachment_path) : null,
|
|
'attachment_type' => $message->attachment_type,
|
|
]
|
|
]
|
|
], 201);
|
|
|
|
} catch (\Exception $e) {
|
|
logger()->error('Mobile Employer Send Message Failure: ' . $e->getMessage());
|
|
|
|
return response()->json([
|
|
'success' => false,
|
|
'message' => 'An error occurred while sending the message.',
|
|
'error' => app()->environment('local') ? $e->getMessage() : 'Internal Server Error'
|
|
], 500);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Start a conversation with a specific worker.
|
|
*
|
|
* @param \Illuminate\Http\Request $request
|
|
* @return \Illuminate\Http\JsonResponse
|
|
*/
|
|
public function startConversation(Request $request)
|
|
{
|
|
/** @var User $employer */
|
|
$employer = $request->attributes->get('employer');
|
|
|
|
$validator = Validator::make($request->all(), [
|
|
'worker_id' => 'required|exists:workers,id',
|
|
]);
|
|
|
|
if ($validator->fails()) {
|
|
return response()->json([
|
|
'success' => false,
|
|
'message' => 'Validation error.',
|
|
'errors' => $validator->errors()
|
|
], 422);
|
|
}
|
|
|
|
try {
|
|
// Find or create conversation
|
|
$conv = Conversation::where('employer_id', $employer->id)
|
|
->where('worker_id', $request->worker_id)
|
|
->first();
|
|
|
|
if (!$conv) {
|
|
if (!User::canContactWorker($employer->id, $request->worker_id)) {
|
|
$activeSub = \App\Models\Subscription::where('user_id', $employer->id)->where('status', 'active')->first();
|
|
$planId = $activeSub ? $activeSub->plan_id : 'basic';
|
|
$plan = \App\Models\Plan::find($planId);
|
|
$maxContacts = $plan ? $plan->max_contact_people : 10;
|
|
|
|
return response()->json([
|
|
'success' => false,
|
|
'message' => 'You have reached your contacted workers limit of ' . $maxContacts . ' under your active plan. Please upgrade or renew your subscription to contact more workers.'
|
|
], 403);
|
|
}
|
|
|
|
$conv = Conversation::create([
|
|
'employer_id' => $employer->id,
|
|
'worker_id' => $request->worker_id,
|
|
]);
|
|
}
|
|
|
|
return response()->json([
|
|
'success' => true,
|
|
'message' => 'Conversation started successfully.',
|
|
'data' => [
|
|
'conversation_id' => $conv->id
|
|
]
|
|
], 200);
|
|
|
|
} catch (\Exception $e) {
|
|
logger()->error('Mobile Employer Start Conversation Failure: ' . $e->getMessage());
|
|
|
|
return response()->json([
|
|
'success' => false,
|
|
'message' => 'An error occurred while starting the conversation.',
|
|
'error' => app()->environment('local') ? $e->getMessage() : 'Internal Server Error'
|
|
], 500);
|
|
}
|
|
}
|
|
}
|