Skip to content

Commit c165840

Browse files
authored
Merge pull request #9 from devaslanphp/dev
Kanban Board
2 parents cd6a131 + 8ac4b2e commit c165840

17 files changed

Lines changed: 357 additions & 59 deletions

File tree

app/Http/Livewire/Kanban.php

Lines changed: 148 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,148 @@
1+
<?php
2+
3+
namespace App\Http\Livewire;
4+
5+
use App\Jobs\TicketUpdatedJob;
6+
use App\Models\Ticket;
7+
use Filament\Notifications\Notification;
8+
use Illuminate\Support\Collection;
9+
use Illuminate\Support\HtmlString;
10+
use Illuminate\Support\Str;
11+
use InvadersXX\FilamentKanbanBoard\Pages\FilamentKanbanBoard;
12+
13+
class Kanban extends FilamentKanbanBoard
14+
{
15+
protected static ?string $title = '';
16+
public bool $sortable = true;
17+
public bool $sortableBetweenStatuses = true;
18+
public bool $recordClickEnabled = true;
19+
20+
/**
21+
* Statuses list Definition
22+
*
23+
* @return Collection
24+
*/
25+
protected function statuses(): Collection
26+
{
27+
return collect(statuses_list_for_kanban());
28+
}
29+
30+
/**
31+
* Records list definitino
32+
*
33+
* @return Collection
34+
*/
35+
protected function records(): Collection
36+
{
37+
$query = Ticket::query();
38+
$query->withCount('comments');
39+
if (has_all_permissions(auth()->user(), 'view-own-tickets') && !has_all_permissions(auth()->user(), 'view-all-tickets')) {
40+
$query->where(function ($query) {
41+
$query->where('owner_id', auth()->user()->id)
42+
->orWhere('responsible_id', auth()->user()->id);
43+
});
44+
}
45+
return $query->get()
46+
->map(function (Ticket $ticket) {
47+
$priority = config('system.priorities.' . $ticket->priority);
48+
$type = config('system.types.' . $ticket->type);
49+
return [
50+
'id' => $ticket->id,
51+
'title' => new HtmlString('
52+
<div class="w-full flex flex-col space-y-3">
53+
<div class="w-full flex items-center gap-2">
54+
<div title="' . $type['title'] . '" class="text-xs rounded-full w-6 h-6 flex items-center justify-center text-center ' . $type['text-color'] . ' ' . $type['bg-color'] . '">
55+
<i class="fa ' . $type['icon'] . '"></i>
56+
</div>
57+
<div title="' . $priority['title'] . '" class="text-xs rounded-full w-6 h-6 flex items-center justify-center text-center ' . $priority['text-color'] . ' ' . $priority['bg-color'] . '">
58+
<i class="fa ' . $priority['icon'] . '"></i>
59+
</div>
60+
<span class="text-sm font-normal" title="' . $ticket->title . '">' . Str::limit($ticket->title, 15) . '</span>
61+
</div>
62+
<div class="w-full text-xs font-light">
63+
' . Str::limit(htmlspecialchars(strip_tags($ticket->content))) . '
64+
</div>
65+
<div class="w-full flex items-center space-x-4">
66+
<div class="flex items-center gap-1">
67+
'.
68+
($ticket->responsible ? '
69+
<img src="' . $ticket->responsible->avatar_url . '" alt="' . $ticket->responsible->name . '" class="rounded-full shadow" style="width: 20px; height: 20px;" />
70+
<span class="font-light text-xs">' . $ticket->responsible->name . '</span>
71+
' : '<span class="text-xs font-normal text-gray-400">' . __('Not assigned yet!') . '</span>')
72+
.'
73+
</div>
74+
<div class="flex items-center gap-1 text-xs text-gray-500">
75+
' . $ticket->comments_count . '
76+
<i class="fa fa-comment-o"></i>
77+
</div>
78+
</div>
79+
</div>
80+
'),
81+
'status' => $ticket->status,
82+
];
83+
});
84+
}
85+
86+
/**
87+
* Customizing kanban board styles
88+
*
89+
* @return string[]
90+
*/
91+
protected function styles(): array
92+
{
93+
return [
94+
'wrapper' => 'w-full h-full flex space-x-4 overflow-x-auto',
95+
'kanbanWrapper' => 'h-full flex-1',
96+
'kanban' => 'border border-gray-150 flex flex-col h-full rounded',
97+
'kanbanHeader' => 'px-3 py-3 font-bold text-xs w-full border-b border-gray-150',
98+
'kanbanFooter' => '',
99+
'kanbanRecords' => 'space-y-4 p-3 flex-1 overflow-y-auto w-64',
100+
'record' => 'bg-white dark:bg-gray-800 p-4 border border-gray-150 rounded cursor-pointer w-62 hover:bg-gray-50 hover:shadow-lg',
101+
'recordContent' => 'w-full',
102+
];
103+
}
104+
105+
/**
106+
* Event launched when the record status is changed
107+
*
108+
* @param $recordId
109+
* @param $statusId
110+
* @param $fromOrderedIds
111+
* @param $toOrderedIds
112+
* @return void
113+
*/
114+
public function onStatusChanged($recordId, $statusId, $fromOrderedIds, $toOrderedIds): void
115+
{
116+
$ticket = Ticket::find($recordId);
117+
if ((has_all_permissions(auth()->user(), 'update-all-tickets') || (has_all_permissions(auth()->user(), 'update-own-tickets') && ($ticket->owner_id === auth()->user() || $ticket->responsible_id === auth()->user()->id))) && has_all_permissions(auth()->user(), 'change-status-tickets')) {
118+
$before = __(config('system.statuses.' . $ticket->status . '.title')) ?? '-';
119+
$ticket->status = $statusId;
120+
$ticket->save();
121+
Notification::make()
122+
->success()
123+
->title(__('Status updated'))
124+
->body(__('The ticket status has been successfully updated'))
125+
->send();
126+
TicketUpdatedJob::dispatch($ticket, __('Status'), $before, __(config('system.statuses.' . $ticket->status . '.title') ?? '-'));
127+
} else {
128+
Notification::make()
129+
->success()
130+
->title(__('Oops!'))
131+
->body(__("You don't have permissions to change this ticket status"))
132+
->send();
133+
}
134+
}
135+
136+
/**
137+
* Event launched when the record is clicked
138+
*
139+
* @param $recordId
140+
* @return void
141+
*/
142+
public function onRecordClick($recordId): void
143+
{
144+
$ticket = Ticket::find($recordId);
145+
$url = route('tickets.details', ['ticket' => $ticket, 'slug' => Str::slug($ticket->title)]);
146+
$this->dispatchBrowserEvent('open-ticket', ['url' => $url]);
147+
}
148+
}

app/Http/Livewire/TicketDetails/Priority.php

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -66,7 +66,7 @@ public function update(): void
6666
public function save(): void
6767
{
6868
$data = $this->form->getState();
69-
$before = config('system.priorities.' . $this->ticket->priority . '.title') ?? '-';
69+
$before = __(config('system.priorities.' . $this->ticket->priority . '.title')) ?? '-';
7070
$this->ticket->priority = $data['priority'];
7171
$this->ticket->save();
7272
Notification::make()
@@ -79,6 +79,6 @@ public function save(): void
7979
]);
8080
$this->updating = false;
8181
$this->emit('ticketSaved');
82-
TicketUpdatedJob::dispatch($this->ticket, __('Priority'), $before, (config('system.priorities.' . $this->ticket->priority . '.title') ?? '-'));
82+
TicketUpdatedJob::dispatch($this->ticket, __('Priority'), $before, __(config('system.priorities.' . $this->ticket->priority . '.title') ?? '-'));
8383
}
8484
}

app/Http/Livewire/TicketDetails/Status.php

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -65,7 +65,7 @@ public function update(): void
6565
public function save(): void
6666
{
6767
$data = $this->form->getState();
68-
$before = config('system.statuses.' . $this->ticket->status . '.title') ?? '-';
68+
$before = __(config('system.statuses.' . $this->ticket->status . '.title')) ?? '-';
6969
$this->ticket->status = $data['status'];
7070
$this->ticket->save();
7171
Notification::make()
@@ -78,6 +78,6 @@ public function save(): void
7878
]);
7979
$this->updating = false;
8080
$this->emit('ticketSaved');
81-
TicketUpdatedJob::dispatch($this->ticket, __('Status'), $before, (config('system.statuses.' . $this->ticket->status . '.title') ?? '-'));
81+
TicketUpdatedJob::dispatch($this->ticket, __('Status'), $before, __(config('system.statuses.' . $this->ticket->status . '.title') ?? '-'));
8282
}
8383
}

app/Http/Livewire/TicketDetails/Type.php

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -65,7 +65,7 @@ public function update(): void
6565
public function save(): void
6666
{
6767
$data = $this->form->getState();
68-
$before = config('system.types.' . $this->ticket->type . '.title') ?? '-';
68+
$before = __(config('system.types.' . $this->ticket->type . '.title')) ?? '-';
6969
$this->ticket->type = $data['type'];
7070
$this->ticket->save();
7171
Notification::make()
@@ -78,6 +78,6 @@ public function save(): void
7878
]);
7979
$this->updating = false;
8080
$this->emit('ticketSaved');
81-
TicketUpdatedJob::dispatch($this->ticket, __('Type'), $before, (config('system.types.' . $this->ticket->type . '.title') ?? '-'));
81+
TicketUpdatedJob::dispatch($this->ticket, __('Type'), $before, __(config('system.types.' . $this->ticket->type . '.title') ?? '-'));
8282
}
8383
}

app/View/Components/MainMenu.php

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,12 @@ public function __construct()
4040
'always_shown' => false,
4141
'show_notification_indicator' => false
4242
],
43+
'kanban' => [
44+
'title' => 'Kanban Board',
45+
'icon' => 'fa-clipboard-check',
46+
'always_shown' => false,
47+
'show_notification_indicator' => false
48+
],
4349
'administration' => [
4450
'title' => 'Administration',
4551
'icon' => 'fa-cogs',

app/helpers.php

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -98,6 +98,27 @@ function statuses_list(): array
9898
}
9999
}
100100

101+
if (!function_exists('statuses_list_for_kanban')) {
102+
/**
103+
* Return statuses list as an array for kanban board
104+
*
105+
* @return array
106+
*/
107+
function statuses_list_for_kanban(): array
108+
{
109+
$statuses = [];
110+
foreach (config('system.statuses') as $key => $value) {
111+
$statuses[] = [
112+
'id' => $key,
113+
'title' => __($value['title']),
114+
'text-color' => $value['text-color'],
115+
'bg-color' => $value['bg-color'],
116+
];
117+
}
118+
return $statuses;
119+
}
120+
}
121+
101122
if (!function_exists('priorities_list')) {
102123
/**
103124
* Return priorities list as an array of KEY (priority id) => VALUE (priority title)

composer.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
"filament/forms": "^2.15",
1010
"filament/notifications": "^2.15",
1111
"guzzlehttp/guzzle": "^7.2",
12+
"invaders-xx/filament-kanban-board": "^0.2.6",
1213
"laravel/framework": "^9.19",
1314
"laravel/sanctum": "^3.0",
1415
"laravel/tinker": "^2.7",

0 commit comments

Comments
 (0)