Skip to content

Commit 01b64fd

Browse files
authored
Merge pull request #59 from devaslanphp/dev
Chat system
2 parents e83f4e4 + 51232b9 commit 01b64fd

12 files changed

Lines changed: 252 additions & 30 deletions

File tree

app/Http/Livewire/Chat.php

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
<?php
2+
3+
namespace App\Http\Livewire;
4+
5+
use App\Models\Chat as Model;
6+
use App\Models\Ticket;
7+
use Filament\Forms\Components\RichEditor;
8+
use Filament\Forms\Concerns\InteractsWithForms;
9+
use Filament\Forms\Contracts\HasForms;
10+
use Livewire\Component;
11+
12+
class Chat extends Component implements HasForms
13+
{
14+
use InteractsWithForms;
15+
16+
public Ticket $ticket;
17+
18+
public function mount(): void
19+
{
20+
$this->form->fill();
21+
}
22+
23+
public function render()
24+
{
25+
$messages = Model::where('ticket_id', $this->ticket->id)->get();
26+
return view('livewire.chat', compact('messages'));
27+
}
28+
29+
/**
30+
* Form schema definition
31+
*
32+
* @return array
33+
*/
34+
protected function getFormSchema(): array
35+
{
36+
return [
37+
RichEditor::make('message')
38+
->label(__('Type a message..'))
39+
->disableLabel()
40+
->placeholder(__('Type a message..'))
41+
->required()
42+
->fileAttachmentsDisk(config('filesystems.default'))
43+
->fileAttachmentsDirectory('chat')
44+
->fileAttachmentsVisibility('private'),
45+
];
46+
}
47+
48+
/**
49+
* Send a message
50+
*
51+
* @return void
52+
*/
53+
public function send(): void
54+
{
55+
$data = $this->form->getState();
56+
Model::create([
57+
'message' => $data['message'],
58+
'user_id' => auth()->user()->id,
59+
'ticket_id' => $this->ticket->id
60+
]);
61+
$this->form->fill();
62+
}
63+
}

app/Http/Livewire/TicketDetails.php

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ public function mount(): void
1818
{
1919
$this->menu = [
2020
'Comments',
21-
'Activities',
21+
'Chat',
2222
];
2323
$this->activeMenu = $this->menu[0];
2424
}
@@ -37,6 +37,7 @@ public function render()
3737
public function selectMenu($item)
3838
{
3939
$this->activeMenu = $item;
40+
$this->dispatchBrowserEvent('initMagnificPopupOnTicketComments');
4041
}
4142

4243
/**

app/Models/Chat.php

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
<?php
2+
3+
namespace App\Models;
4+
5+
use Illuminate\Database\Eloquent\Builder;
6+
use Illuminate\Database\Eloquent\Factories\HasFactory;
7+
use Illuminate\Database\Eloquent\Model;
8+
use Illuminate\Database\Eloquent\Relations\BelongsTo;
9+
use Illuminate\Database\Eloquent\SoftDeletes;
10+
11+
class Chat extends Model
12+
{
13+
use HasFactory, SoftDeletes;
14+
15+
protected $fillable = [
16+
'message',
17+
'ticket_id',
18+
'user_id'
19+
];
20+
21+
protected static function boot()
22+
{
23+
parent::boot();
24+
static::addGlobalScope('order', function (Builder $builder) {
25+
$builder->orderBy('created_at', 'desc');
26+
});
27+
}
28+
29+
public function ticket(): BelongsTo
30+
{
31+
return $this->belongsTo(Ticket::class);
32+
}
33+
34+
public function user(): BelongsTo
35+
{
36+
return $this->belongsTo(User::class);
37+
}
38+
}

app/Models/Ticket.php

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
use Illuminate\Database\Eloquent\Model;
1111
use Illuminate\Database\Eloquent\Relations\BelongsTo;
1212
use Illuminate\Database\Eloquent\Relations\HasMany;
13+
use Illuminate\Database\Eloquent\Relations\HasOne;
1314
use Illuminate\Database\Eloquent\SoftDeletes;
1415

1516
class Ticket extends Model implements HasLogsActivity
@@ -79,4 +80,10 @@ public function activityLogLink(): string
7980
{
8081
return route('tickets.number', $this->ticket_number);
8182
}
83+
84+
public function chat(): HasOne
85+
{
86+
return $this->hasOne(Chat::class);
87+
}
88+
8289
}
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
<?php
2+
3+
use Illuminate\Database\Migrations\Migration;
4+
use Illuminate\Database\Schema\Blueprint;
5+
use Illuminate\Support\Facades\Schema;
6+
7+
return new class extends Migration {
8+
/**
9+
* Run the migrations.
10+
*
11+
* @return void
12+
*/
13+
public function up()
14+
{
15+
Schema::create('chats', function (Blueprint $table) {
16+
$table->id();
17+
$table->longText('message');
18+
$table->foreignId('ticket_id')->constrained('tickets');
19+
$table->foreignId('user_id')->constrained('users');
20+
$table->softDeletes();
21+
$table->timestamps();
22+
});
23+
}
24+
25+
/**
26+
* Reverse the migrations.
27+
*
28+
* @return void
29+
*/
30+
public function down()
31+
{
32+
Schema::dropIfExists('chats');
33+
}
34+
};

lang/fr.json

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -277,5 +277,11 @@
277277
"Activity logs": "Journaux d'activité",
278278
"Here you can see all activity logs of :app": "Ici vous pouvez voir tous les journaux d'activité de :app",
279279
"Below is the list of activity logs of :app": "Ci-dessous les journaux d'activité de :app",
280-
"Search for activity logs": "Rechercher les journaux d'activité"
280+
"Search for activity logs": "Rechercher les journaux d'activité",
281+
"Chat": "Chat",
282+
"Chat section": "Section de chat",
283+
"Use the section below to chat with the speakers of this ticket": "Utilisez la section ci-dessous pour échanger avec les intervenants de ce ticket",
284+
"Type a message..": "Tapez un message..",
285+
"Send": "Envoyer",
286+
"No messages yet!": "Aucun message pour le moment !"
281287
}

public/docs/index.html

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,7 @@
5454
<!-- Logo -->
5555
<a class="logo ml-md-3" href="index.html" title="Help Desk">
5656
<img src="/docs/assets/images/logo.png" alt="Help Desk" width="120" /> </a>
57-
<span class="text-2 ml-2">v1.2.4</span>
57+
<span class="text-2 ml-2">v1.3.0</span>
5858
<!-- Logo End -->
5959

6060
<!-- Navbar Toggler -->
@@ -91,6 +91,7 @@
9191
<li class="nav-item"><a class="nav-link" href="#idocs_faq">FAQ</a></li>
9292
<li class="nav-item"><a class="nav-link" href="#idocs_changelog">Changelog</a>
9393
<ul class="nav flex-column">
94+
<li class="nav-item"><a class="nav-link" href="#v1-3-0">v1.3.0</a></li>
9495
<li class="nav-item"><a class="nav-link" href="#v1-2-4">v1.2.4</a></li>
9596
<li class="nav-item"><a class="nav-link" href="#v1-2-3">v1.2.3</a></li>
9697
<li class="nav-item"><a class="nav-link" href="#v1-2-2">v1.2.2</a></li>
@@ -124,7 +125,7 @@ <h2>Help Desk</h2>
124125
<div class="row">
125126
<div class="col-sm-6 col-lg-4">
126127
<ul class="list-unstyled">
127-
<li><strong>Version:</strong> 1.2.4</li>
128+
<li><strong>Version:</strong> 1.3.0</li>
128129
<li><strong>Author:</strong> <a href="mailto:eloufirhatim@gmail.com"
129130
target="_blank">heloufir</a>
130131
</li>
@@ -321,6 +322,10 @@ <h5 class="mb-0"> <a href="#" class="collapsed" data-toggle="collapse" data-targ
321322
<section id="idocs_changelog">
322323
<h2>Changelog</h2>
323324
<p class="text-4">See what's new added, changed, fixed, improved or updated in the latest versions. </p>
325+
<h3 id="v1-3-0">Version 1.3.0 <small class="text-muted">(19 September, 2022)</small></h3>
326+
<ul>
327+
<li>Chat system: Integrate a simple chat system into ticket details</li>
328+
</ul>
324329
<h3 id="v1-2-4">Version 1.2.4 <small class="text-muted">(19 September, 2022)</small></h3>
325330
<ul>
326331
<li>Activity logs: integration of <code>spatie/laravel-activitylog</code></li>

resources/css/app.scss

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -75,3 +75,13 @@ table {
7575
}
7676
}
7777
}
78+
79+
#chat {
80+
height: 500px;
81+
82+
#chat-container {
83+
img {
84+
max-width: 50%;
85+
}
86+
}
87+
}

resources/js/app.js

Lines changed: 26 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -25,29 +25,33 @@ window.Alpine = Alpine
2525

2626
Alpine.start()
2727

28-
// Open image as magnific popup
29-
if ($('.magnificpopup-container').length) {
30-
$('.magnificpopup-container').magnificPopup({
31-
type: 'image',
32-
delegate: 'img',
33-
gallery: {
34-
enabled: true
35-
},
36-
callbacks: {
37-
elementParse: function (qw) {
38-
qw.src = qw.el.attr('src');
28+
// Open image as magnific popup (Ticket comments)
29+
window.initMagnificPopupOnTicketComments = function () {
30+
if ($('.magnificpopup-container').length) {
31+
$('.magnificpopup-container').magnificPopup({
32+
type: 'image',
33+
delegate: 'img',
34+
gallery: {
35+
enabled: true
36+
},
37+
callbacks: {
38+
elementParse: function (qw) {
39+
qw.src = qw.el.attr('src');
40+
}
41+
},
42+
image: {
43+
titleSrc: function (item) {
44+
let title = '';
45+
if (item.el.closest('figure').children('figcaption'))
46+
title = item.el.closest('figure').children('figcaption').text();
47+
return title;
48+
}
3949
}
40-
},
41-
image: {
42-
titleSrc: function (item) {
43-
let title = '';
44-
if (item.el.closest('figure').children('figcaption'))
45-
title = item.el.closest('figure').children('figcaption').text();
46-
return title;
47-
}
48-
}
49-
});
50-
}
50+
});
51+
}
52+
};
53+
54+
(() => window.initMagnificPopupOnTicketComments())();
5155

5256
// Copy text to clipboard
5357
window.unsecuredCopyToClipboard = function (text) {
Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
<div class="w-full flex flex-col justify-start items-start gap-5">
2+
<div class="w-full flex md:flex-row flex-col justify-between items-start gap-2">
3+
<div class="flex flex-col justify-center items-start gap-0">
4+
<span class="lg:text-xl md:text-lg text-lg font-medium text-gray-700">
5+
@lang('Chat section')
6+
</span>
7+
<span class="lg:text-lg md:text-sm text-xs font-light text-gray-500">
8+
@lang('Use the section below to chat with the speakers of this ticket')
9+
</span>
10+
</div>
11+
</div>
12+
<div class="w-full flex flex-col gap-5">
13+
<div id="chat" class="relative w-full flex flex-col justify-end items-end gap-3 rounded-lg border border-gray-300 bg-gray-50" wire:poll>
14+
@if($messages->count())
15+
<div id="chat-container" class="absolute top-0 right-0 left-0 bottom-0 w-full h-full flex flex-col-reverse justify-start items-start gap-8 p-5 overflow-y-auto">
16+
@foreach($messages as $message)
17+
<div class="w-full flex flex-col justify-center gap-1 {{ $message->user_id === auth()->user()->id ? 'items-end' : 'items-start' }}">
18+
<span class="text-xs text-gray-500 font-medium px-2">{{ $message->user->name }}</span>
19+
<div class="w-auto max-w-3xl prose text-left rounded-2xl p-4 shadow {{ $message->user_id === auth()->user()->id ? 'bg-white' : 'bg-primary-100' }}">
20+
{!! $message->message !!}
21+
</div>
22+
<span class="text-xs text-gray-500 font-light px-2">{{ $message->created_at->diffForHumans() }}</span>
23+
</div>
24+
@endforeach
25+
</div>
26+
@else
27+
<div class="absolute top-0 right-0 left-0 bottom-0 w-full h-full flex flex-col justify-center items-center">
28+
<img src="{{ asset('images/comments-empty.jpeg') }}" alt="No comments" class="w-14 opacity-50"/>
29+
<span class="text-lg text-neutral-400 font-light">
30+
@lang('No messages yet!')
31+
</span>
32+
</div>
33+
@endif
34+
</div>
35+
<form wire:submit.prevent="send" class="w-full relative flex flex-col justify-start items-start gap-2">
36+
<div class="w-full">
37+
{{ $this->form }}
38+
</div>
39+
<div class="flex flex-row items-center gap-1">
40+
<button type="submit"
41+
class="py-2 px-3 rounded-lg shadow hover:shadow-lg bg-primary-700 hover:bg-primary-800 text-white text-base">
42+
<div class="flex items-center gap-2">
43+
<i class="fa fa-send-o"></i>
44+
@lang('Send')
45+
</div>
46+
</button>
47+
</div>
48+
</form>
49+
</div>
50+
</div>

0 commit comments

Comments
 (0)