diff --git a/README.md b/README.md index 8a55b5cc..e0a687fe 100644 --- a/README.md +++ b/README.md @@ -1,10 +1,24 @@ # Help Desk -

+

Laravel v8.x Livewire v2.x Filament v2.x PHP 8.0 +
+ + GitHub tag + + + License + + + issues - help-desk + +
+ + view - Documentation +

Help Desk is a Laravel based project, that let you manage your support tickets and communicate with your customers, with diff --git a/app/Http/Livewire/Kanban.php b/app/Http/Livewire/Kanban.php new file mode 100644 index 00000000..92cac6cf --- /dev/null +++ b/app/Http/Livewire/Kanban.php @@ -0,0 +1,148 @@ +withCount('comments'); + if (has_all_permissions(auth()->user(), 'view-own-tickets') && !has_all_permissions(auth()->user(), 'view-all-tickets')) { + $query->where(function ($query) { + $query->where('owner_id', auth()->user()->id) + ->orWhere('responsible_id', auth()->user()->id); + }); + } + return $query->get() + ->map(function (Ticket $ticket) { + $priority = config('system.priorities.' . $ticket->priority); + $type = config('system.types.' . $ticket->type); + return [ + 'id' => $ticket->id, + 'title' => new HtmlString(' +
+
+
+ +
+
+ +
+ ' . Str::limit($ticket->title, 15) . ' +
+
+ ' . Str::limit(htmlspecialchars(strip_tags($ticket->content))) . ' +
+
+
+ '. + ($ticket->responsible ? ' + ' . $ticket->responsible->name . ' + ' . $ticket->responsible->name . ' + ' : '' . __('Not assigned yet!') . '') + .' +
+
+ ' . $ticket->comments_count . ' + +
+
+
+ '), + 'status' => $ticket->status, + ]; + }); + } + + /** + * Customizing kanban board styles + * + * @return string[] + */ + protected function styles(): array + { + return [ + 'wrapper' => 'w-full h-full flex space-x-4 overflow-x-auto', + 'kanbanWrapper' => 'h-full flex-1', + 'kanban' => 'border border-gray-150 flex flex-col h-full rounded', + 'kanbanHeader' => 'px-3 py-3 font-bold text-xs w-full border-b border-gray-150', + 'kanbanFooter' => '', + 'kanbanRecords' => 'space-y-4 p-3 flex-1 overflow-y-auto w-64', + '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', + 'recordContent' => 'w-full', + ]; + } + + /** + * Event launched when the record status is changed + * + * @param $recordId + * @param $statusId + * @param $fromOrderedIds + * @param $toOrderedIds + * @return void + */ + public function onStatusChanged($recordId, $statusId, $fromOrderedIds, $toOrderedIds): void + { + $ticket = Ticket::find($recordId); + 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')) { + $before = __(config('system.statuses.' . $ticket->status . '.title')) ?? '-'; + $ticket->status = $statusId; + $ticket->save(); + Notification::make() + ->success() + ->title(__('Status updated')) + ->body(__('The ticket status has been successfully updated')) + ->send(); + TicketUpdatedJob::dispatch($ticket, __('Status'), $before, __(config('system.statuses.' . $ticket->status . '.title') ?? '-')); + } else { + Notification::make() + ->success() + ->title(__('Oops!')) + ->body(__("You don't have permissions to change this ticket status")) + ->send(); + } + } + + /** + * Event launched when the record is clicked + * + * @param $recordId + * @return void + */ + public function onRecordClick($recordId): void + { + $ticket = Ticket::find($recordId); + $url = route('tickets.details', ['ticket' => $ticket, 'slug' => Str::slug($ticket->title)]); + $this->dispatchBrowserEvent('open-ticket', ['url' => $url]); + } +} diff --git a/app/Http/Livewire/TicketDetails/Priority.php b/app/Http/Livewire/TicketDetails/Priority.php index 9715dab5..3857c158 100644 --- a/app/Http/Livewire/TicketDetails/Priority.php +++ b/app/Http/Livewire/TicketDetails/Priority.php @@ -66,7 +66,7 @@ public function update(): void public function save(): void { $data = $this->form->getState(); - $before = config('system.priorities.' . $this->ticket->priority . '.title') ?? '-'; + $before = __(config('system.priorities.' . $this->ticket->priority . '.title')) ?? '-'; $this->ticket->priority = $data['priority']; $this->ticket->save(); Notification::make() @@ -79,6 +79,6 @@ public function save(): void ]); $this->updating = false; $this->emit('ticketSaved'); - TicketUpdatedJob::dispatch($this->ticket, __('Priority'), $before, (config('system.priorities.' . $this->ticket->priority . '.title') ?? '-')); + TicketUpdatedJob::dispatch($this->ticket, __('Priority'), $before, __(config('system.priorities.' . $this->ticket->priority . '.title') ?? '-')); } } diff --git a/app/Http/Livewire/TicketDetails/Status.php b/app/Http/Livewire/TicketDetails/Status.php index 6ad5b9c2..f411896a 100644 --- a/app/Http/Livewire/TicketDetails/Status.php +++ b/app/Http/Livewire/TicketDetails/Status.php @@ -65,7 +65,7 @@ public function update(): void public function save(): void { $data = $this->form->getState(); - $before = config('system.statuses.' . $this->ticket->status . '.title') ?? '-'; + $before = __(config('system.statuses.' . $this->ticket->status . '.title')) ?? '-'; $this->ticket->status = $data['status']; $this->ticket->save(); Notification::make() @@ -78,6 +78,6 @@ public function save(): void ]); $this->updating = false; $this->emit('ticketSaved'); - TicketUpdatedJob::dispatch($this->ticket, __('Status'), $before, (config('system.statuses.' . $this->ticket->status . '.title') ?? '-')); + TicketUpdatedJob::dispatch($this->ticket, __('Status'), $before, __(config('system.statuses.' . $this->ticket->status . '.title') ?? '-')); } } diff --git a/app/Http/Livewire/TicketDetails/Type.php b/app/Http/Livewire/TicketDetails/Type.php index e7e453b9..689e4782 100644 --- a/app/Http/Livewire/TicketDetails/Type.php +++ b/app/Http/Livewire/TicketDetails/Type.php @@ -65,7 +65,7 @@ public function update(): void public function save(): void { $data = $this->form->getState(); - $before = config('system.types.' . $this->ticket->type . '.title') ?? '-'; + $before = __(config('system.types.' . $this->ticket->type . '.title')) ?? '-'; $this->ticket->type = $data['type']; $this->ticket->save(); Notification::make() @@ -78,6 +78,6 @@ public function save(): void ]); $this->updating = false; $this->emit('ticketSaved'); - TicketUpdatedJob::dispatch($this->ticket, __('Type'), $before, (config('system.types.' . $this->ticket->type . '.title') ?? '-')); + TicketUpdatedJob::dispatch($this->ticket, __('Type'), $before, __(config('system.types.' . $this->ticket->type . '.title') ?? '-')); } } diff --git a/app/View/Components/MainMenu.php b/app/View/Components/MainMenu.php index 0e28b2a4..7c35769e 100644 --- a/app/View/Components/MainMenu.php +++ b/app/View/Components/MainMenu.php @@ -40,6 +40,12 @@ public function __construct() 'always_shown' => false, 'show_notification_indicator' => false ], + 'kanban' => [ + 'title' => 'Kanban Board', + 'icon' => 'fa-clipboard-check', + 'always_shown' => false, + 'show_notification_indicator' => false + ], 'administration' => [ 'title' => 'Administration', 'icon' => 'fa-cogs', diff --git a/app/helpers.php b/app/helpers.php index 493b08e1..c28392ff 100644 --- a/app/helpers.php +++ b/app/helpers.php @@ -98,6 +98,27 @@ function statuses_list(): array } } +if (!function_exists('statuses_list_for_kanban')) { + /** + * Return statuses list as an array for kanban board + * + * @return array + */ + function statuses_list_for_kanban(): array + { + $statuses = []; + foreach (config('system.statuses') as $key => $value) { + $statuses[] = [ + 'id' => $key, + 'title' => __($value['title']), + 'text-color' => $value['text-color'], + 'bg-color' => $value['bg-color'], + ]; + } + return $statuses; + } +} + if (!function_exists('priorities_list')) { /** * Return priorities list as an array of KEY (priority id) => VALUE (priority title) diff --git a/composer.json b/composer.json index a926d7d2..29ae0de8 100644 --- a/composer.json +++ b/composer.json @@ -1,14 +1,15 @@ { "name": "laravel/laravel", "type": "project", - "description": "The Laravel Framework.", - "keywords": ["framework", "laravel"], + "description": "Help Desk open source project.", + "keywords": ["helpdesk", "laravel", "tailwindcss", "filament-forms", "filament-notifications"], "license": "MIT", "require": { "php": "^8.0.2", "filament/forms": "^2.15", "filament/notifications": "^2.15", "guzzlehttp/guzzle": "^7.2", + "invaders-xx/filament-kanban-board": "^0.2.6", "laravel/framework": "^9.19", "laravel/sanctum": "^3.0", "laravel/tinker": "^2.7", diff --git a/composer.lock b/composer.lock index 58d6054a..c758419a 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "ce59978fb4fd9bcad9576151196a79e0", + "content-hash": "baed92234ce3f3ea6223795f8e4f729f", "packages": [ { "name": "akaunting/laravel-money", @@ -758,16 +758,16 @@ }, { "name": "filament/filament", - "version": "v2.16.2", + "version": "v2.16.5", "source": { "type": "git", "url": "https://github.com/filamentphp/admin.git", - "reference": "91a73f1a583a964ed7c657f5d8627b6eb10d7903" + "reference": "94320edcd20fbd30db5b93c0ba43326d83953bf3" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/filamentphp/admin/zipball/91a73f1a583a964ed7c657f5d8627b6eb10d7903", - "reference": "91a73f1a583a964ed7c657f5d8627b6eb10d7903", + "url": "https://api.github.com/repos/filamentphp/admin/zipball/94320edcd20fbd30db5b93c0ba43326d83953bf3", + "reference": "94320edcd20fbd30db5b93c0ba43326d83953bf3", "shasum": "" }, "require": { @@ -817,20 +817,20 @@ "issues": "https://github.com/filamentphp/filament/issues", "source": "https://github.com/filamentphp/filament" }, - "time": "2022-09-12T15:57:50+00:00" + "time": "2022-09-16T14:59:12+00:00" }, { "name": "filament/forms", - "version": "v2.16.2", + "version": "v2.16.5", "source": { "type": "git", "url": "https://github.com/filamentphp/forms.git", - "reference": "0de49bf91025c25df22ee596f2df624a4a7a5039" + "reference": "a9c3f8a0f5c6cefa1190f77ee48ddda98ba95649" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/filamentphp/forms/zipball/0de49bf91025c25df22ee596f2df624a4a7a5039", - "reference": "0de49bf91025c25df22ee596f2df624a4a7a5039", + "url": "https://api.github.com/repos/filamentphp/forms/zipball/a9c3f8a0f5c6cefa1190f77ee48ddda98ba95649", + "reference": "a9c3f8a0f5c6cefa1190f77ee48ddda98ba95649", "shasum": "" }, "require": { @@ -875,20 +875,20 @@ "issues": "https://github.com/filamentphp/filament/issues", "source": "https://github.com/filamentphp/filament" }, - "time": "2022-09-12T15:57:51+00:00" + "time": "2022-09-16T14:59:17+00:00" }, { "name": "filament/notifications", - "version": "v2.16.2", + "version": "v2.16.5", "source": { "type": "git", "url": "https://github.com/filamentphp/notifications.git", - "reference": "3c704738306123337acebe0ba990c6fd84b4842c" + "reference": "8bc1cbfb4aede05113d499677be870d71b87ddf1" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/filamentphp/notifications/zipball/3c704738306123337acebe0ba990c6fd84b4842c", - "reference": "3c704738306123337acebe0ba990c6fd84b4842c", + "url": "https://api.github.com/repos/filamentphp/notifications/zipball/8bc1cbfb4aede05113d499677be870d71b87ddf1", + "reference": "8bc1cbfb4aede05113d499677be870d71b87ddf1", "shasum": "" }, "require": { @@ -925,20 +925,20 @@ "issues": "https://github.com/filamentphp/filament/issues", "source": "https://github.com/filamentphp/filament" }, - "time": "2022-09-12T15:57:43+00:00" + "time": "2022-09-16T10:23:15+00:00" }, { "name": "filament/support", - "version": "v2.16.2", + "version": "v2.16.5", "source": { "type": "git", "url": "https://github.com/filamentphp/support.git", - "reference": "65d67c72ab370ba193d203f09975a033f627de09" + "reference": "d4cb0588141371ed8a49aa472ca5c8ea3b732e96" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/filamentphp/support/zipball/65d67c72ab370ba193d203f09975a033f627de09", - "reference": "65d67c72ab370ba193d203f09975a033f627de09", + "url": "https://api.github.com/repos/filamentphp/support/zipball/d4cb0588141371ed8a49aa472ca5c8ea3b732e96", + "reference": "d4cb0588141371ed8a49aa472ca5c8ea3b732e96", "shasum": "" }, "require": { @@ -975,20 +975,20 @@ "issues": "https://github.com/filamentphp/filament/issues", "source": "https://github.com/filamentphp/filament" }, - "time": "2022-09-12T15:57:43+00:00" + "time": "2022-09-16T10:23:06+00:00" }, { "name": "filament/tables", - "version": "v2.16.2", + "version": "v2.16.5", "source": { "type": "git", "url": "https://github.com/filamentphp/tables.git", - "reference": "b5854e190dd8ba9c463b6c5a8c26e70978e28e73" + "reference": "def2615dc4a37dacd736ec3b1aa4041ac01e5e62" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/filamentphp/tables/zipball/b5854e190dd8ba9c463b6c5a8c26e70978e28e73", - "reference": "b5854e190dd8ba9c463b6c5a8c26e70978e28e73", + "url": "https://api.github.com/repos/filamentphp/tables/zipball/def2615dc4a37dacd736ec3b1aa4041ac01e5e62", + "reference": "def2615dc4a37dacd736ec3b1aa4041ac01e5e62", "shasum": "" }, "require": { @@ -1031,7 +1031,7 @@ "issues": "https://github.com/filamentphp/filament/issues", "source": "https://github.com/filamentphp/filament" }, - "time": "2022-09-12T15:57:42+00:00" + "time": "2022-09-16T10:23:07+00:00" }, { "name": "fruitcake/php-cors", @@ -1497,6 +1497,80 @@ ], "time": "2022-08-28T14:45:39+00:00" }, + { + "name": "invaders-xx/filament-kanban-board", + "version": "0.2.6", + "source": { + "type": "git", + "url": "https://github.com/invaders-xx/filament-kanban-board.git", + "reference": "942a4da9b142340730c5448a113ed1647e8a4a55" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/invaders-xx/filament-kanban-board/zipball/942a4da9b142340730c5448a113ed1647e8a4a55", + "reference": "942a4da9b142340730c5448a113ed1647e8a4a55", + "shasum": "" + }, + "require": { + "filament/filament": "^2.0", + "illuminate/contracts": "^8.74|^9.0", + "php": "^8.0", + "spatie/laravel-package-tools": "^1.9.2" + }, + "require-dev": { + "friendsofphp/php-cs-fixer": "^3.8", + "nunomaduro/collision": "^6.0", + "nunomaduro/larastan": "^2.0.1", + "orchestra/testbench": "^7.0", + "pestphp/pest": "^1.21", + "pestphp/pest-plugin-laravel": "^1.1", + "phpstan/extension-installer": "^1.1", + "phpstan/phpstan-deprecation-rules": "^1.0", + "phpstan/phpstan-phpunit": "^1.0", + "phpunit/phpunit": "^9.5", + "spatie/laravel-ray": "^1.26" + }, + "type": "library", + "extra": { + "laravel": { + "providers": [ + "InvadersXX\\FilamentKanbanBoard\\FilamentKanbanBoardServiceProvider" + ], + "aliases": { + "FilamentKanbanBoard": "InvadersXX\\FilamentKanbanBoard\\Facades\\FilamentKanbanBoard" + } + } + }, + "autoload": { + "psr-4": { + "InvadersXX\\FilamentKanbanBoard\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "David Vincent", + "email": "envahisseur@gmail.com", + "role": "Developer" + } + ], + "description": "Add a Kanban page to filament", + "homepage": "https://github.com/invaders-xx/filament-kanban-board", + "keywords": [ + "filament", + "filament-kanban-board", + "invaders-xx", + "laravel" + ], + "support": { + "issues": "https://github.com/invaders-xx/filament-kanban-board/issues", + "source": "https://github.com/invaders-xx/filament-kanban-board/tree/0.2.6" + }, + "time": "2022-07-17T08:00:58+00:00" + }, { "name": "laravel/framework", "version": "v9.30.1", @@ -2067,16 +2141,16 @@ }, { "name": "league/flysystem", - "version": "3.3.0", + "version": "3.4.0", "source": { "type": "git", "url": "https://github.com/thephpleague/flysystem.git", - "reference": "d8295793b3e2f91aa39e1feb2d5bfce772891ae2" + "reference": "5972d2a966e236674286e3a2281139ce74e4415d" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/thephpleague/flysystem/zipball/d8295793b3e2f91aa39e1feb2d5bfce772891ae2", - "reference": "d8295793b3e2f91aa39e1feb2d5bfce772891ae2", + "url": "https://api.github.com/repos/thephpleague/flysystem/zipball/5972d2a966e236674286e3a2281139ce74e4415d", + "reference": "5972d2a966e236674286e3a2281139ce74e4415d", "shasum": "" }, "require": { @@ -2138,7 +2212,7 @@ ], "support": { "issues": "https://github.com/thephpleague/flysystem/issues", - "source": "https://github.com/thephpleague/flysystem/tree/3.3.0" + "source": "https://github.com/thephpleague/flysystem/tree/3.4.0" }, "funding": [ { @@ -2154,7 +2228,7 @@ "type": "tidelift" } ], - "time": "2022-09-09T11:11:42+00:00" + "time": "2022-09-16T20:57:23+00:00" }, { "name": "league/mime-type-detection", @@ -3622,16 +3696,16 @@ }, { "name": "ramsey/uuid", - "version": "4.5.0", + "version": "4.5.1", "source": { "type": "git", "url": "https://github.com/ramsey/uuid.git", - "reference": "ef842484ba57f163c6d465ab744bfecb872a11d4" + "reference": "a161a26d917604dc6d3aa25100fddf2556e9f35d" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/ramsey/uuid/zipball/ef842484ba57f163c6d465ab744bfecb872a11d4", - "reference": "ef842484ba57f163c6d465ab744bfecb872a11d4", + "url": "https://api.github.com/repos/ramsey/uuid/zipball/a161a26d917604dc6d3aa25100fddf2556e9f35d", + "reference": "a161a26d917604dc6d3aa25100fddf2556e9f35d", "shasum": "" }, "require": { @@ -3700,7 +3774,7 @@ ], "support": { "issues": "https://github.com/ramsey/uuid/issues", - "source": "https://github.com/ramsey/uuid/tree/4.5.0" + "source": "https://github.com/ramsey/uuid/tree/4.5.1" }, "funding": [ { @@ -3712,7 +3786,7 @@ "type": "tidelift" } ], - "time": "2022-09-15T01:44:53+00:00" + "time": "2022-09-16T03:22:46+00:00" }, { "name": "ryangjchandler/blade-capture-directive", @@ -8705,27 +8779,27 @@ }, { "name": "spatie/laravel-ignition", - "version": "1.4.1", + "version": "1.5.0", "source": { "type": "git", "url": "https://github.com/spatie/laravel-ignition.git", - "reference": "29deea5d9cf921590184be6956e657c4f4566440" + "reference": "192962f4d84526f6868c512530c00633e3165749" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/spatie/laravel-ignition/zipball/29deea5d9cf921590184be6956e657c4f4566440", - "reference": "29deea5d9cf921590184be6956e657c4f4566440", + "url": "https://api.github.com/repos/spatie/laravel-ignition/zipball/192962f4d84526f6868c512530c00633e3165749", + "reference": "192962f4d84526f6868c512530c00633e3165749", "shasum": "" }, "require": { "ext-curl": "*", "ext-json": "*", "ext-mbstring": "*", - "illuminate/support": "^8.77|^9.0", + "illuminate/support": "^8.77|^9.27", "monolog/monolog": "^2.3", "php": "^8.0", "spatie/flare-client-php": "^1.0.1", - "spatie/ignition": "^1.2.4", + "spatie/ignition": "^1.4.1", "symfony/console": "^5.0|^6.0", "symfony/var-dumper": "^5.0|^6.0" }, @@ -8791,7 +8865,7 @@ "type": "github" } ], - "time": "2022-09-01T11:31:14+00:00" + "time": "2022-09-16T13:45:54+00:00" }, { "name": "theseer/tokenizer", @@ -8853,5 +8927,5 @@ "php": "^8.0.2" }, "platform-dev": [], - "plugin-api-version": "2.1.0" + "plugin-api-version": "2.2.0" } diff --git a/config/filament-kanban-board.php b/config/filament-kanban-board.php new file mode 100644 index 00000000..bee73cf4 --- /dev/null +++ b/config/filament-kanban-board.php @@ -0,0 +1,4 @@ + 'bg-red-50', 'bg-color' => 'text-red-500', 'permissions' => [ - 'pages' => ['analytics', 'tickets', 'administration'], + 'pages' => ['analytics', 'tickets', 'kanban', 'administration'], 'functions' => [ 'view-all-projects', 'update-all-projects', 'delete-all-projects', 'create-projects', 'view-all-tickets', 'update-all-tickets', 'delete-all-tickets', 'create-tickets', 'assign-tickets', 'change-status-tickets' @@ -201,7 +201,7 @@ 'text-color' => 'bg-gray-50', 'bg-color' => 'text-gray-500', 'permissions' => [ - 'pages' => ['analytics', 'tickets'], + 'pages' => ['analytics', 'tickets', 'kanban'], 'functions' => [ 'view-own-projects', 'view-own-tickets', 'update-own-tickets', 'delete-own-tickets', 'create-tickets', 'assign-tickets', 'change-status-tickets' @@ -213,7 +213,7 @@ 'text-color' => 'bg-blue-50', 'bg-color' => 'text-blue-500', 'permissions' => [ - 'pages' => ['analytics', 'tickets'], + 'pages' => ['analytics', 'tickets', 'kanban'], 'functions' => [ 'view-own-projects', 'view-own-tickets', 'update-own-tickets', 'delete-own-tickets', 'create-tickets' diff --git a/lang/fr.json b/lang/fr.json index 67d2b19b..a3bec0cf 100644 --- a/lang/fr.json +++ b/lang/fr.json @@ -210,5 +210,9 @@ "Employee": "Employé", "Customer": "Client", "Overview": "Vue d'ensemble", - "Administration": "Administration" + "Administration": "Administration", + "Below is the Kanban Board for tickets configured on :app": "Ci-dessous le tableau Kanban pour les tickets configurés sur :app", + "Kanban Board": "Tableau Kanban", + "Oops!": "Oups !", + "You don't have permissions to change this ticket status": "Vous n'êtes pas autorisé à modifier le statut de ce ticket" } diff --git a/public/docs/index.html b/public/docs/index.html index 53265217..34fda145 100644 --- a/public/docs/index.html +++ b/public/docs/index.html @@ -91,6 +91,7 @@