225 lines
7.7 KiB
PHP
225 lines
7.7 KiB
PHP
<?php
|
|
|
|
namespace Modules\Portal\Http\Controllers\Customer;
|
|
|
|
use App\Business;
|
|
use Illuminate\Http\Request;
|
|
use Illuminate\Support\Facades\Schema;
|
|
use Modules\Communication\Entities\ChatMessage;
|
|
use Modules\Communication\Entities\ChatRoom;
|
|
use Modules\Communication\Events\MessageSent;
|
|
use Modules\Communication\Services\ChatRoomService;
|
|
use Modules\Communication\Services\PresenceService;
|
|
use Modules\Portal\Http\Controllers\BasePortalController;
|
|
use Modules\Portal\Services\CustomerSpecialistService;
|
|
|
|
class CommunicationController extends BasePortalController
|
|
{
|
|
public function __construct(
|
|
protected CustomerSpecialistService $specialistService,
|
|
protected ChatRoomService $chatRoomService,
|
|
protected PresenceService $presenceService
|
|
) {}
|
|
|
|
public function index()
|
|
{
|
|
$this->ensureCrmSubscription();
|
|
$business_id = $this->businessId();
|
|
$contact_id = $this->contactId();
|
|
$specialists = $this->specialistsWithPresence($business_id, $contact_id);
|
|
$onlineSpecialists = $specialists->where('is_online', true)->values();
|
|
$business = Business::with(['locations' => fn ($q) => $q->limit(1)])->findOrFail($business_id);
|
|
$chatEnabled = $this->chatEnabled();
|
|
$presenceEnabled = $this->presenceEnabled();
|
|
$support_phone = optional($business->locations->first())->mobile;
|
|
|
|
return view('portal::customer.communications.index', compact(
|
|
'specialists', 'onlineSpecialists', 'business', 'chatEnabled', 'presenceEnabled', 'support_phone'
|
|
));
|
|
}
|
|
|
|
public function chat(int $specialist_id)
|
|
{
|
|
$this->ensureCrmSubscription();
|
|
if (! $this->chatEnabled()) {
|
|
abort(404);
|
|
}
|
|
|
|
$business_id = $this->businessId();
|
|
$contact_id = $this->contactId();
|
|
$specialist = $this->specialistService->assertSpecialistForContact($business_id, $contact_id, $specialist_id);
|
|
$room = $this->chatRoomService->findOrCreateDirectRoom($business_id, auth()->user(), $specialist->id);
|
|
if ($this->presenceEnabled()) {
|
|
$this->presenceService->heartbeat($business_id, auth()->id());
|
|
}
|
|
$is_online = $this->presenceEnabled()
|
|
&& $this->presenceService->isOnline($business_id, $specialist->id);
|
|
|
|
return view('portal::customer.communications.chat', [
|
|
'specialist' => [
|
|
'id' => $specialist->id,
|
|
'name' => trim($specialist->surname.' '.$specialist->first_name.' '.$specialist->last_name),
|
|
'mobile' => $specialist->contact_no,
|
|
'email' => $specialist->email,
|
|
'is_online' => $is_online,
|
|
],
|
|
'room_id' => $room->id,
|
|
'presenceEnabled' => $this->presenceEnabled(),
|
|
]);
|
|
}
|
|
|
|
public function presence()
|
|
{
|
|
$this->ensureCrmSubscription();
|
|
$business_id = $this->businessId();
|
|
$contact_id = $this->contactId();
|
|
|
|
return response()->json($this->specialistsWithPresence($business_id, $contact_id));
|
|
}
|
|
|
|
public function specialistPresence(int $specialist_id)
|
|
{
|
|
$this->ensureCrmSubscription();
|
|
$business_id = $this->businessId();
|
|
$contact_id = $this->contactId();
|
|
$specialist = $this->specialistService->assertSpecialistForContact($business_id, $contact_id, $specialist_id);
|
|
if ($this->presenceEnabled()) {
|
|
$this->presenceService->heartbeat($business_id, auth()->id());
|
|
}
|
|
|
|
return response()->json([
|
|
'id' => $specialist->id,
|
|
'is_online' => $this->presenceEnabled()
|
|
&& $this->presenceService->isOnline($business_id, $specialist->id),
|
|
]);
|
|
}
|
|
|
|
public function heartbeat()
|
|
{
|
|
$this->ensureCrmSubscription();
|
|
if ($this->presenceEnabled()) {
|
|
$this->presenceService->heartbeat($this->businessId(), auth()->id());
|
|
}
|
|
|
|
return response()->json(['success' => true]);
|
|
}
|
|
|
|
public function messages(Request $request, int $room_id)
|
|
{
|
|
$this->ensureCrmSubscription();
|
|
$room = $this->assertCustomerRoom($room_id);
|
|
|
|
$messages = ChatMessage::where('chat_room_id', $room->id)
|
|
->with('user:id,surname,first_name,last_name')
|
|
->orderBy('created_at')
|
|
->get()
|
|
->map(fn ($m) => [
|
|
'id' => $m->id,
|
|
'body' => $m->body,
|
|
'user_id' => $m->user_id,
|
|
'created_at' => $m->created_at?->format('Y-m-d H:i'),
|
|
'is_mine' => (int) $m->user_id === (int) auth()->id(),
|
|
]);
|
|
|
|
return response()->json($messages);
|
|
}
|
|
|
|
public function sendMessage(Request $request, int $room_id)
|
|
{
|
|
$this->ensureCrmSubscription();
|
|
$validated = $request->validate([
|
|
'body' => 'required|string|max:5000',
|
|
]);
|
|
|
|
$room = $this->assertCustomerRoom($room_id);
|
|
$business_id = $this->businessId();
|
|
|
|
$message = ChatMessage::create([
|
|
'chat_room_id' => $room->id,
|
|
'user_id' => auth()->id(),
|
|
'body' => $validated['body'],
|
|
'type' => 'text',
|
|
]);
|
|
$room->update(['last_message_at' => now()]);
|
|
$room->members()->updateExistingPivot(auth()->id(), ['last_read_at' => now()]);
|
|
|
|
try {
|
|
event(new MessageSent($message->load('user:id,surname,first_name,last_name,username,email'), $business_id));
|
|
} catch (\Throwable $e) {
|
|
report($e);
|
|
}
|
|
|
|
return response()->json([
|
|
'id' => $message->id,
|
|
'body' => $message->body,
|
|
'user_id' => $message->user_id,
|
|
'created_at' => $message->created_at?->format('Y-m-d H:i'),
|
|
'is_mine' => true,
|
|
]);
|
|
}
|
|
|
|
public function markRead(int $room_id)
|
|
{
|
|
$this->ensureCrmSubscription();
|
|
$room = $this->assertCustomerRoom($room_id);
|
|
$room->members()->updateExistingPivot(auth()->id(), ['last_read_at' => now()]);
|
|
|
|
return response()->json(['success' => true]);
|
|
}
|
|
|
|
private function chatEnabled(): bool
|
|
{
|
|
return Schema::hasTable('chat_rooms')
|
|
&& Schema::hasTable('chat_messages')
|
|
&& class_exists(ChatRoomService::class);
|
|
}
|
|
|
|
private function presenceEnabled(): bool
|
|
{
|
|
return $this->chatEnabled() && class_exists(PresenceService::class);
|
|
}
|
|
|
|
private function specialistsWithPresence(int $business_id, int $contact_id)
|
|
{
|
|
$specialists = $this->specialistService->specialistsForContact($business_id, $contact_id);
|
|
|
|
if (! $this->presenceEnabled()) {
|
|
return $specialists->map(fn ($s) => array_merge($s, ['is_online' => false]));
|
|
}
|
|
|
|
$this->presenceService->heartbeat($business_id, auth()->id());
|
|
$onlineIds = $this->presenceService->onlineUserIds(
|
|
$business_id,
|
|
$specialists->pluck('id')->all()
|
|
);
|
|
|
|
return $specialists
|
|
->map(function ($s) use ($onlineIds) {
|
|
$s['is_online'] = in_array($s['id'], $onlineIds, true);
|
|
|
|
return $s;
|
|
})
|
|
->sortByDesc('is_online')
|
|
->values();
|
|
}
|
|
|
|
private function assertCustomerRoom(int $room_id): ChatRoom
|
|
{
|
|
if (! $this->chatEnabled()) {
|
|
abort(404);
|
|
}
|
|
|
|
$business_id = $this->businessId();
|
|
$contact_id = $this->contactId();
|
|
$room = ChatRoom::where('business_id', $business_id)->findOrFail($room_id);
|
|
$this->chatRoomService->assertRoomMember($room, auth()->user());
|
|
|
|
$otherMember = $room->members()->where('users.id', '!=', auth()->id())->first();
|
|
if ($otherMember) {
|
|
$this->specialistService->assertSpecialistForContact($business_id, $contact_id, $otherMember->id);
|
|
}
|
|
|
|
return $room;
|
|
}
|
|
}
|