diff --git a/app/Http/Controllers/TicketNumberController.php b/app/Http/Controllers/TicketNumberController.php new file mode 100644 index 0000000..ad8d3d5 --- /dev/null +++ b/app/Http/Controllers/TicketNumberController.php @@ -0,0 +1,35 @@ +whereHas('project', fn($query) => $query->where('ticket_prefix', $ticket_prefix)) + ->first(); + if ($ticket) { + return redirect()->route('tickets.details', [ + 'ticket' => $ticket, + 'slug' => Str::slug($ticket->title) + ]); + } else { + abort(404); + } + } + +} diff --git a/app/Http/Livewire/ProjectsDialog.php b/app/Http/Livewire/ProjectsDialog.php index 3af8bd2..f345237 100644 --- a/app/Http/Livewire/ProjectsDialog.php +++ b/app/Http/Livewire/ProjectsDialog.php @@ -5,6 +5,7 @@ use App\Models\Project; use App\Models\User; use App\Notifications\ProjectCreatedNotification; +use Filament\Forms\Components\Grid; use Filament\Forms\Components\RichEditor; use Filament\Forms\Components\Select; use Filament\Forms\Components\TextInput; @@ -26,6 +27,7 @@ class ProjectsDialog extends Component implements HasForms public function mount(): void { $this->form->fill([ 'name' => $this->project->name, + 'ticket_prefix' => $this->project->ticket_prefix, 'description' => $this->project->description, 'owner_id' => $this->project->owner_id ?? auth()->user()->id, ]); @@ -50,10 +52,22 @@ protected function getFormSchema(): array ->searchable() ->options(User::all()->pluck('name', 'id')), - TextInput::make('name') - ->label(__('Full name')) - ->maxLength(255) - ->required(), + Grid::make(3) + ->schema([ + TextInput::make('ticket_prefix') + ->label(__('Ticket prefix')) + ->minLength(4) + ->maxLength(4) + ->columnSpan(1) + ->helperText(__('Used to generate tickets numbers')) + ->required(), + + TextInput::make('name') + ->label(__('Full name')) + ->maxLength(255) + ->columnSpan(2) + ->required(), + ]), RichEditor::make('description') ->label(__('Description')) @@ -74,7 +88,8 @@ public function save(): void { Project::create([ 'name' => $data['name'], 'description' => $data['description'], - 'owner_id' => $data['owner_id'] + 'owner_id' => $data['owner_id'], + 'ticket_prefix' => $data['ticket_prefix'] ]); Notification::make() ->success() @@ -85,6 +100,7 @@ public function save(): void { $this->project->name = $data['name']; $this->project->description = $data['description']; $this->project->owner_id = $data['owner_id']; + $this->project->ticket_prefix = $data['ticket_prefix']; $this->project->save(); Notification::make() ->success() diff --git a/app/Http/Livewire/TicketDetails.php b/app/Http/Livewire/TicketDetails.php index 3038ea7..fa0892b 100644 --- a/app/Http/Livewire/TicketDetails.php +++ b/app/Http/Livewire/TicketDetails.php @@ -3,6 +3,7 @@ namespace App\Http\Livewire; use App\Models\Ticket; +use Filament\Notifications\Notification; use Livewire\Component; class TicketDetails extends Component @@ -47,4 +48,23 @@ public function ticketSaved(): void { $this->ticket = $this->ticket->refresh(); } + + /** + * Copy a ticket url + * + * @param Ticket $ticket + * @return void + */ + public function copyTicketUrl(Ticket $ticket): void { + Notification::make() + ->success() + ->title(__('Ticket url copied')) + ->body(__('The ticket url successfully copied to your clipboard')) + ->send(); + $this->dispatchBrowserEvent('ticketUrlCopied', [ + 'url' => route('tickets.number', [ + 'number' => $ticket->ticket_number + ]) + ]); + } } diff --git a/app/Http/Livewire/Tickets.php b/app/Http/Livewire/Tickets.php index dd95bba..261fe5b 100644 --- a/app/Http/Livewire/Tickets.php +++ b/app/Http/Livewire/Tickets.php @@ -12,7 +12,9 @@ use Filament\Forms\Components\TextInput; use Filament\Forms\Concerns\InteractsWithForms; use Filament\Forms\Contracts\HasForms; +use Filament\Notifications\Actions\Action; use Filament\Notifications\Notification; +use Illuminate\Support\Str; use Livewire\Component; class Tickets extends Component implements HasForms @@ -231,4 +233,23 @@ public function ticketDeleted() { $this->ticketSaved(); } + + /** + * Copy a ticket url + * + * @param Ticket $ticket + * @return void + */ + public function copyTicketUrl(Ticket $ticket): void { + Notification::make() + ->success() + ->title(__('Ticket url copied')) + ->body(__('The ticket url successfully copied to your clipboard')) + ->send(); + $this->dispatchBrowserEvent('ticketUrlCopied', [ + 'url' => route('tickets.number', [ + 'number' => $ticket->ticket_number + ]) + ]); + } } diff --git a/app/Http/Livewire/TicketsDialog.php b/app/Http/Livewire/TicketsDialog.php index 9d75a7f..86a163a 100644 --- a/app/Http/Livewire/TicketsDialog.php +++ b/app/Http/Livewire/TicketsDialog.php @@ -5,7 +5,6 @@ use App\Jobs\TicketCreatedJob; use App\Models\Project; use App\Models\Ticket; -use App\Models\User; use Filament\Forms\Components\Grid; use Filament\Forms\Components\RichEditor; use Filament\Forms\Components\Select; @@ -14,6 +13,7 @@ use Filament\Forms\Contracts\HasForms; use Filament\Notifications\Actions\Action; use Filament\Notifications\Notification; +use Illuminate\Support\Str; use Livewire\Component; class TicketsDialog extends Component implements HasForms @@ -109,6 +109,17 @@ public function save(): void ->success() ->title(__('Ticket created')) ->body(__('The ticket has been successfully created')) + ->actions([ + Action::make('redirect') + ->label(__('See details')) + ->color('success') + ->button() + ->close() + ->url(fn() => route('tickets.details', [ + 'ticket' => $ticket, + 'slug' => Str::slug($ticket->title) + ])) + ]) ->send(); $this->emit('ticketSaved'); TicketCreatedJob::dispatch($ticket); diff --git a/app/Models/Project.php b/app/Models/Project.php index 3157e24..8e4f03d 100644 --- a/app/Models/Project.php +++ b/app/Models/Project.php @@ -17,7 +17,8 @@ class Project extends Model protected $fillable = [ 'name', 'description', - 'owner_id' + 'owner_id', + 'ticket_prefix' ]; protected static function boot() diff --git a/app/Models/Ticket.php b/app/Models/Ticket.php index fc32c6d..5f93811 100644 --- a/app/Models/Ticket.php +++ b/app/Models/Ticket.php @@ -23,11 +23,11 @@ class Ticket extends Model 'owner_id', 'responsible_id', 'project_id', + 'number', ]; protected $appends = [ - 'status_object', - 'priority_object', + 'ticket_number' ]; protected static function boot() @@ -36,6 +36,9 @@ protected static function boot() static::addGlobalScope('order', function (Builder $builder) { $builder->orderBy('created_at', 'desc'); }); + static::creating(function (Ticket $ticket) { + $ticket->number = str_pad(Ticket::where('project_id', $ticket->project_id)->withTrashed()->count() + 1, 4, '0', STR_PAD_LEFT); + }); } public function owner(): BelongsTo @@ -53,22 +56,15 @@ public function project(): BelongsTo return $this->belongsTo(Project::class); } - public function statusObject(): Attribute + public function comments(): HasMany { - return new Attribute( - get: fn() => config('system.statuses.' . $this->status) ?? null - ); + return $this->hasMany(Comment::class); } - public function priorityObject(): Attribute + public function ticketNumber(): Attribute { return new Attribute( - get: fn() => config('system.priorities.' . $this->priority) ?? null + get: fn() => $this->project->ticket_prefix . '' . $this->number ); } - - public function comments(): HasMany - { - return $this->hasMany(Comment::class); - } } diff --git a/database/help_desk.sql b/database/help_desk.sql index e2f6bb1..7eef3af 100644 --- a/database/help_desk.sql +++ b/database/help_desk.sql @@ -13,16 +13,16 @@ VALUES (4, 'Dark Vador', 'darkvador@gmail.com', NULL, '$2y$10$4f8HPTwKhVzpAP5kas '$2y$10$MR51TVg3xzUXs308oTxp8.Pw9sjs7ijaeGYLJZsq85CdY/azYD0bG', NULL, '2022-09-11 15:31:51', '2022-09-11 15:31:55', 'employee', '82c93eba-9a33-4dbe-abac-22f11f5c1f54', NULL); -INSERT INTO `projects` (`id`, `name`, `description`, `owner_id`, `deleted_at`, `created_at`, `updated_at`) +INSERT INTO `projects` (`id`, `name`, `description`, `owner_id`, `deleted_at`, `created_at`, `updated_at`, `ticket_prefix`) VALUES (1, 'Default project', '
This is the default project to associate to any created ticket that are not related to any other projects.
', - 4, NULL, '2022-09-11 16:29:08', '2022-09-11 16:35:48'), + 4, NULL, '2022-09-11 16:29:08', '2022-09-11 16:35:48', 'DEFP'), (2, 'IDEAO', 'Project for managing tickets linked to the IDEAO project
', 4, NULL, '2022-09-11 17:09:31', - '2022-09-11 17:12:47'), + '2022-09-11 17:12:47', 'IDEA'), (3, 'Arena job', 'Project for managing tickets linked to the Arena job project
', 4, NULL, - '2022-09-11 17:13:05', '2022-09-11 17:13:17'), + '2022-09-11 17:13:05', '2022-09-11 17:13:17', 'ARJO'), (4, 'ARP', 'Project for managing tickets linked to the ARP project
', 5, NULL, '2022-09-11 17:13:25', - '2022-09-11 17:15:04'); + '2022-09-11 17:15:04', 'ARPT'); INSERT INTO `favorite_projects` (`id`, `user_id`, `project_id`, `created_at`, `updated_at`) VALUES (6, 4, 2, '2022-09-11 17:09:33', '2022-09-11 17:09:33'), @@ -31,17 +31,17 @@ VALUES (6, 4, 2, '2022-09-11 17:09:33', '2022-09-11 17:09:33'), (11, 4, 1, '2022-09-12 11:50:42', '2022-09-12 11:50:42'); INSERT INTO `tickets` (`id`, `title`, `content`, `status`, `priority`, `owner_id`, `responsible_id`, `deleted_at`, - `created_at`, `updated_at`, `project_id`, `type`) + `created_at`, `updated_at`, `project_id`, `type`, `number`) VALUES (2, 'Cannot access the platform', 'Hello,
I cannot access the platform with the credentials received by email.
Can you see what is the problem, please?
Thanks
', - 'validated', 'highest', 4, 5, NULL, '2022-09-11 18:27:55', '2022-09-12 11:48:00', 1, 'bug'), + 'validated', 'highest', 4, 5, NULL, '2022-09-11 18:27:55', '2022-09-12 11:48:00', 1, 'bug', '0001'), (3, 'Design enhancement', 'Add a logo of the company to the login page.
', 'created', 'low', 5, 4, NULL, - '2022-09-11 18:45:55', '2022-09-12 13:08:05', 2, 'improvement'), + '2022-09-11 18:45:55', '2022-09-12 13:08:05', 2, 'improvement', '0001'), (4, 'Quiz wizard', 'Add a wizard system to the quiz page
', 'created', 'normal', 4, NULL, NULL, - '2022-09-11 20:37:14', '2022-09-11 20:37:14', 2, 'improvement'), + '2022-09-11 20:37:14', '2022-09-11 20:37:14', 2, 'improvement', '0002'), (9, 'Internal error - Login page', 'We got an internal error when we visit the login page (url: /auth/login)
', 'created', 'highest', 5, 4, - NULL, '2022-09-11 20:58:37', '2022-09-12 13:08:12', 4, 'bug'); + NULL, '2022-09-11 20:58:37', '2022-09-12 13:08:12', 4, 'bug', '0001'); INSERT INTO `comments` (`id`, `owner_id`, `ticket_id`, `content`, `deleted_at`, `created_at`, `updated_at`) VALUES (1, 4, 2, 'Hello,
We are working on it, I let you know ASAP.
Best regards.
', NULL, diff --git a/database/migrations/2022_09_19_143232_add_ticket_prefix_to_projects.php b/database/migrations/2022_09_19_143232_add_ticket_prefix_to_projects.php new file mode 100644 index 0000000..440a327 --- /dev/null +++ b/database/migrations/2022_09_19_143232_add_ticket_prefix_to_projects.php @@ -0,0 +1,31 @@ +string('ticket_prefix', 4); + }); + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + Schema::table('projects', function (Blueprint $table) { + $table->dropColumn('ticket_prefix'); + }); + } +}; diff --git a/database/migrations/2022_09_19_144042_add_number_to_tickets.php b/database/migrations/2022_09_19_144042_add_number_to_tickets.php new file mode 100644 index 0000000..2ce3370 --- /dev/null +++ b/database/migrations/2022_09_19_144042_add_number_to_tickets.php @@ -0,0 +1,31 @@ +string('number'); + }); + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + Schema::table('tickets', function (Blueprint $table) { + $table->dropColumn('number'); + }); + } +}; diff --git a/lang/fr.json b/lang/fr.json index 9e82794..6d6ef25 100644 --- a/lang/fr.json +++ b/lang/fr.json @@ -267,5 +267,11 @@ "Are you sure you want to delete this type?": "Êtes-vous sûr de vouloir supprimé ce type ?", "Check the fontawesome icons here to choose your right icon": "Voir les icônes fontawesome ici pour choisir votre icône", "Selected icon:": "Icône sélectionnée", - "Icon": "Icône" + "Icon": "Icône", + "Ticket prefix": "Préfixe des tickets", + "Used to generate tickets numbers": "Utilisé pour générer les numéros des tickets", + "See details": "Voir les détails", + "Click to copy url to ticket": "Cliquez pour copier le lien vers le ticket", + "Ticket url copied": "Url du ticket copié", + "The ticket url successfully copied to your clipboard": "L'URL du ticket a été copiée avec succès dans votre presse-papiers" } diff --git a/public/docs/index.html b/public/docs/index.html index eaf0945..ecce338 100644 --- a/public/docs/index.html +++ b/public/docs/index.html @@ -54,7 +54,7 @@ - v1.2.1 + v1.2.2 @@ -91,6 +91,7 @@