diff --git a/Modules/Helpdesk/Config/config.php b/Modules/Helpdesk/Config/config.php index a629042b..fec8866d 100644 --- a/Modules/Helpdesk/Config/config.php +++ b/Modules/Helpdesk/Config/config.php @@ -7,7 +7,7 @@ return [ 'middleware' => ['web', 'auth'], 'disk' => env('HELPDESK_ATTACHMENTS_DISK', 'helpdesk'), 'max_upload_kb' => (int) env('HELPDESK_MAX_UPLOAD_KB', 10240), - 'allowed_mimes' => ['pdf', 'png', 'jpg', 'jpeg', 'webp', 'doc', 'docx', 'xls', 'xlsx', 'txt', 'csv', 'zip'], + 'allowed_mimes' => ['md','pdf', 'png', 'jpg', 'jpeg', 'webp', 'doc', 'docx', 'xls', 'xlsx', 'txt', 'csv', 'zip'], 'default_status_slug' => 'open', 'closed_status_slug' => 'closed', ]; diff --git a/Modules/Helpdesk/Database/Migrations/2026_04_24_000002_create_helpdesk_lookup_tables.php b/Modules/Helpdesk/Database/Migrations/2026_06_12_000010_create_helpdesk_lookup_tables.php similarity index 100% rename from Modules/Helpdesk/Database/Migrations/2026_04_24_000002_create_helpdesk_lookup_tables.php rename to Modules/Helpdesk/Database/Migrations/2026_06_12_000010_create_helpdesk_lookup_tables.php diff --git a/Modules/Helpdesk/Database/Migrations/2026_04_24_000003_create_helpdesk_tickets_table.php b/Modules/Helpdesk/Database/Migrations/2026_06_12_000020_create_helpdesk_tickets_table.php similarity index 100% rename from Modules/Helpdesk/Database/Migrations/2026_04_24_000003_create_helpdesk_tickets_table.php rename to Modules/Helpdesk/Database/Migrations/2026_06_12_000020_create_helpdesk_tickets_table.php diff --git a/Modules/Helpdesk/Database/Migrations/2026_04_24_000006_create_helpdesk_git_and_workflow_tables.php b/Modules/Helpdesk/Database/Migrations/2026_06_12_000030_create_helpdesk_git_and_workflow_tables.php similarity index 100% rename from Modules/Helpdesk/Database/Migrations/2026_04_24_000006_create_helpdesk_git_and_workflow_tables.php rename to Modules/Helpdesk/Database/Migrations/2026_06_12_000030_create_helpdesk_git_and_workflow_tables.php diff --git a/Modules/Helpdesk/Database/Migrations/2026_04_24_000004_create_helpdesk_comments_and_attachments_tables.php b/Modules/Helpdesk/Database/Migrations/2026_06_12_000040_create_helpdesk_comments_and_attachments_tables.php similarity index 100% rename from Modules/Helpdesk/Database/Migrations/2026_04_24_000004_create_helpdesk_comments_and_attachments_tables.php rename to Modules/Helpdesk/Database/Migrations/2026_06_12_000040_create_helpdesk_comments_and_attachments_tables.php diff --git a/Modules/Helpdesk/Database/Migrations/2026_04_24_000005_create_helpdesk_category_user_table.php b/Modules/Helpdesk/Database/Migrations/2026_06_12_000050_create_helpdesk_category_user_table.php similarity index 100% rename from Modules/Helpdesk/Database/Migrations/2026_04_24_000005_create_helpdesk_category_user_table.php rename to Modules/Helpdesk/Database/Migrations/2026_06_12_000050_create_helpdesk_category_user_table.php diff --git a/Modules/Helpdesk/Database/Seeders/HelpdeskDatabaseSeeder.php b/Modules/Helpdesk/Database/Seeders/HelpdeskDatabaseSeeder.php index ab50d524..a102f108 100644 --- a/Modules/Helpdesk/Database/Seeders/HelpdeskDatabaseSeeder.php +++ b/Modules/Helpdesk/Database/Seeders/HelpdeskDatabaseSeeder.php @@ -2,7 +2,7 @@ declare(strict_types=1); -namespace Modules\Helpdesk\database\seeders; +namespace Modules\Helpdesk\Database\Seeders; use Illuminate\Database\Seeder; use Illuminate\Support\Str; @@ -15,44 +15,113 @@ use Modules\Helpdesk\Models\WorkflowTemplate; final class HelpdeskDatabaseSeeder extends Seeder { - /** @return void */ + /** + * Seed the Helpdesk module lookup data and default workflow templates. + * + * @return void + */ public function run(): void + { + $this->seedStatuses(); + $this->seedPriorities(); + $this->seedCategories(); + $this->seedTeamsAndProjects(); + $this->seedWorkflowTemplates(); + } + + /** + * Seed ticket workflow statuses. + * + * @return void + */ + private function seedStatuses(): void { foreach ([ + ['name' => 'New', 'slug' => 'new', 'color' => 'sky', 'sort_order' => 5, 'is_closed' => false], ['name' => 'Open', 'slug' => 'open', 'color' => 'blue', 'sort_order' => 10, 'is_closed' => false], ['name' => 'In Progress', 'slug' => 'in-progress', 'color' => 'amber', 'sort_order' => 20, 'is_closed' => false], - ['name' => 'Waiting Customer', 'slug' => 'waiting-customer', 'color' => 'purple', 'sort_order' => 30, 'is_closed' => false], - ['name' => 'Closed', 'slug' => 'closed', 'color' => 'green', 'sort_order' => 90, 'is_closed' => true], + ['name' => 'Stalled', 'slug' => 'stalled', 'color' => 'rose', 'sort_order' => 30, 'is_closed' => false], + ['name' => 'On-Hold', 'slug' => 'on-hold', 'color' => 'violet', 'sort_order' => 40, 'is_closed' => false], + ['name' => 'Closed', 'slug' => 'closed', 'color' => 'emerald', 'sort_order' => 90, 'is_closed' => true], ] as $row) { Status::query()->updateOrCreate(['slug' => $row['slug']], $row); } + } + /** + * Seed ticket priorities. + * + * @return void + */ + private function seedPriorities(): void + { foreach ([ - ['name' => 'Low', 'slug' => 'low', 'color' => 'zinc', 'sort_order' => 10], - ['name' => 'Normal', 'slug' => 'normal', 'color' => 'blue', 'sort_order' => 20], - ['name' => 'High', 'slug' => 'high', 'color' => 'orange', 'sort_order' => 30], - ['name' => 'Critical', 'slug' => 'critical', 'color' => 'red', 'sort_order' => 40], + ['name' => 'High', 'slug' => 'high', 'color' => 'red', 'sort_order' => 10], + ['name' => 'Medium', 'slug' => 'medium', 'color' => 'amber', 'sort_order' => 20], + ['name' => 'Low', 'slug' => 'low', 'color' => 'emerald', 'sort_order' => 30], ] as $row) { Priority::query()->updateOrCreate(['slug' => $row['slug']], $row); } + } - foreach (['General Support', 'Software', 'Hardware', 'Access Request', 'Incident'] as $name) { - Category::query()->updateOrCreate(['slug' => Str::slug($name)], ['name' => $name, 'color' => 'blue']); + /** + * Seed ticket categories. + * + * @return void + */ + private function seedCategories(): void + { + foreach ([ + ['name' => 'Development', 'color' => 'indigo', 'description' => 'Software changes, enhancements and development work.'], + ['name' => 'Systems Deployment', 'color' => 'cyan', 'description' => 'Deployment, release, infrastructure and environment requests.'], + ['name' => 'Concepts', 'color' => 'purple', 'description' => 'Ideas, concepts, design discussion and future improvements.'], + ['name' => 'Bug', 'color' => 'rose', 'description' => 'Defects, regressions and incorrect behaviour.'], + ] as $row) { + Category::query()->updateOrCreate( + ['company_id' => null, 'slug' => Str::slug($row['name'])], + $row + ['company_id' => null, 'slug' => Str::slug($row['name'])] + ); } + } - $team = Team::query()->updateOrCreate(['slug' => 'support'], [ - 'name' => 'Support', - 'color' => 'indigo', - 'description' => 'Default support team for helpdesk tickets.', - ]); + /** + * Seed the default support team and a default project. + * + * @return void + */ + private function seedTeamsAndProjects(): void + { + $team = Team::query()->updateOrCreate( + ['company_id' => null, 'slug' => 'support'], + [ + 'company_id' => null, + 'name' => 'Support', + 'slug' => 'support', + 'color' => 'blue', + 'description' => 'Default support team for triage, ownership and ticket routing.', + ] + ); - Project::query()->updateOrCreate(['slug' => 'internal-helpdesk'], [ - 'team_id' => $team->id, - 'title' => 'Internal Helpdesk', - 'color' => 'blue', - 'description' => 'Default project for internal helpdesk tickets.', - ]); + Project::query()->updateOrCreate( + ['company_id' => null, 'slug' => 'support-desk'], + [ + 'company_id' => null, + 'team_id' => $team->id, + 'title' => 'Support Desk', + 'slug' => 'support-desk', + 'color' => 'indigo', + 'description' => 'Default project for operational helpdesk tickets.', + ] + ); + } + /** + * Seed default professional workflow email templates. + * + * @return void + */ + private function seedWorkflowTemplates(): void + { foreach ([ ['name' => 'Ticket raised requester notice', 'event' => 'ticket.created', 'subject' => 'Ticket {{ ticket.reference }} has been raised', 'html_body' => '

Hello {{ requester.name }},

Your ticket {{ ticket.reference }} has been raised successfully.

Subject: {{ ticket.subject }}

Priority: {{ ticket.priority }}

', 'to_role' => 'requester'], ['name' => 'Ticket updated requester notice', 'event' => 'ticket.updated', 'subject' => 'Ticket {{ ticket.reference }} has been updated', 'html_body' => '

Hello {{ requester.name }},

Your ticket {{ ticket.reference }} has been updated.

Status: {{ ticket.status }}

Priority: {{ ticket.priority }}

', 'to_role' => 'requester'], @@ -64,6 +133,5 @@ final class HelpdeskDatabaseSeeder extends Seeder $template + ['text_body' => null, 'cc' => null, 'bcc' => null, 'is_active' => true] ); } - } } diff --git a/Modules/Helpdesk/HelpdeskDatabaseSeeder.php b/Modules/Helpdesk/HelpdeskDatabaseSeeder.php new file mode 100644 index 00000000..a102f108 --- /dev/null +++ b/Modules/Helpdesk/HelpdeskDatabaseSeeder.php @@ -0,0 +1,137 @@ +seedStatuses(); + $this->seedPriorities(); + $this->seedCategories(); + $this->seedTeamsAndProjects(); + $this->seedWorkflowTemplates(); + } + + /** + * Seed ticket workflow statuses. + * + * @return void + */ + private function seedStatuses(): void + { + foreach ([ + ['name' => 'New', 'slug' => 'new', 'color' => 'sky', 'sort_order' => 5, 'is_closed' => false], + ['name' => 'Open', 'slug' => 'open', 'color' => 'blue', 'sort_order' => 10, 'is_closed' => false], + ['name' => 'In Progress', 'slug' => 'in-progress', 'color' => 'amber', 'sort_order' => 20, 'is_closed' => false], + ['name' => 'Stalled', 'slug' => 'stalled', 'color' => 'rose', 'sort_order' => 30, 'is_closed' => false], + ['name' => 'On-Hold', 'slug' => 'on-hold', 'color' => 'violet', 'sort_order' => 40, 'is_closed' => false], + ['name' => 'Closed', 'slug' => 'closed', 'color' => 'emerald', 'sort_order' => 90, 'is_closed' => true], + ] as $row) { + Status::query()->updateOrCreate(['slug' => $row['slug']], $row); + } + } + + /** + * Seed ticket priorities. + * + * @return void + */ + private function seedPriorities(): void + { + foreach ([ + ['name' => 'High', 'slug' => 'high', 'color' => 'red', 'sort_order' => 10], + ['name' => 'Medium', 'slug' => 'medium', 'color' => 'amber', 'sort_order' => 20], + ['name' => 'Low', 'slug' => 'low', 'color' => 'emerald', 'sort_order' => 30], + ] as $row) { + Priority::query()->updateOrCreate(['slug' => $row['slug']], $row); + } + } + + /** + * Seed ticket categories. + * + * @return void + */ + private function seedCategories(): void + { + foreach ([ + ['name' => 'Development', 'color' => 'indigo', 'description' => 'Software changes, enhancements and development work.'], + ['name' => 'Systems Deployment', 'color' => 'cyan', 'description' => 'Deployment, release, infrastructure and environment requests.'], + ['name' => 'Concepts', 'color' => 'purple', 'description' => 'Ideas, concepts, design discussion and future improvements.'], + ['name' => 'Bug', 'color' => 'rose', 'description' => 'Defects, regressions and incorrect behaviour.'], + ] as $row) { + Category::query()->updateOrCreate( + ['company_id' => null, 'slug' => Str::slug($row['name'])], + $row + ['company_id' => null, 'slug' => Str::slug($row['name'])] + ); + } + } + + /** + * Seed the default support team and a default project. + * + * @return void + */ + private function seedTeamsAndProjects(): void + { + $team = Team::query()->updateOrCreate( + ['company_id' => null, 'slug' => 'support'], + [ + 'company_id' => null, + 'name' => 'Support', + 'slug' => 'support', + 'color' => 'blue', + 'description' => 'Default support team for triage, ownership and ticket routing.', + ] + ); + + Project::query()->updateOrCreate( + ['company_id' => null, 'slug' => 'support-desk'], + [ + 'company_id' => null, + 'team_id' => $team->id, + 'title' => 'Support Desk', + 'slug' => 'support-desk', + 'color' => 'indigo', + 'description' => 'Default project for operational helpdesk tickets.', + ] + ); + } + + /** + * Seed default professional workflow email templates. + * + * @return void + */ + private function seedWorkflowTemplates(): void + { + foreach ([ + ['name' => 'Ticket raised requester notice', 'event' => 'ticket.created', 'subject' => 'Ticket {{ ticket.reference }} has been raised', 'html_body' => '

Hello {{ requester.name }},

Your ticket {{ ticket.reference }} has been raised successfully.

Subject: {{ ticket.subject }}

Priority: {{ ticket.priority }}

', 'to_role' => 'requester'], + ['name' => 'Ticket updated requester notice', 'event' => 'ticket.updated', 'subject' => 'Ticket {{ ticket.reference }} has been updated', 'html_body' => '

Hello {{ requester.name }},

Your ticket {{ ticket.reference }} has been updated.

Status: {{ ticket.status }}

Priority: {{ ticket.priority }}

', 'to_role' => 'requester'], + ['name' => 'Ticket response requester notice', 'event' => 'ticket.commented', 'subject' => 'New response on ticket {{ ticket.reference }}', 'html_body' => '

Hello {{ requester.name }},

A new response has been added to {{ ticket.reference }}.

Subject: {{ ticket.subject }}

', 'to_role' => 'requester'], + ['name' => 'Ticket closed requester notice', 'event' => 'ticket.closed', 'subject' => 'Ticket {{ ticket.reference }} has been closed', 'html_body' => '

Hello {{ requester.name }},

Your ticket {{ ticket.reference }} has been closed.

Thank you for using the support desk.

', 'to_role' => 'requester'], + ] as $template) { + WorkflowTemplate::query()->updateOrCreate( + ['name' => $template['name'], 'event' => $template['event']], + $template + ['text_body' => null, 'cc' => null, 'bcc' => null, 'is_active' => true] + ); + } + } +} diff --git a/Modules/Helpdesk/Livewire/TicketCreate.php b/Modules/Helpdesk/Livewire/TicketCreate.php index 7f0bcb94..e610c462 100644 --- a/Modules/Helpdesk/Livewire/TicketCreate.php +++ b/Modules/Helpdesk/Livewire/TicketCreate.php @@ -6,6 +6,8 @@ namespace Modules\Helpdesk\Livewire; use Illuminate\Contracts\View\View; use Illuminate\Support\Facades\Auth; +use Illuminate\Support\Facades\DB; +use Illuminate\Support\Facades\Schema; use Livewire\Attributes\Validate; use Livewire\Component; use Livewire\Features\SupportFileUploads\TemporaryUploadedFile; @@ -14,8 +16,6 @@ use Modules\Helpdesk\Models\Category; use Modules\Helpdesk\Models\Priority; use Modules\Helpdesk\Models\Project; use Modules\Helpdesk\Models\Team; -use Illuminate\Support\Facades\DB; -use Illuminate\Support\Facades\Schema; use Modules\Helpdesk\Services\TicketService; final class TicketCreate extends Component @@ -52,16 +52,13 @@ final class TicketCreate extends Component /** * Store a new helpdesk ticket. - * - * @param TicketService $tickets - * @return mixed */ public function save(TicketService $tickets): mixed { $this->validate(); $this->validate([ - 'attachments.*' => 'file|max:' . config('helpdesk.max_upload_kb') . '|mimes:' . implode(',', config('helpdesk.allowed_mimes')), + 'attachments.*' => 'file|max:'.config('helpdesk.max_upload_kb').'|mimes:'.implode(',', config('helpdesk.allowed_mimes')), ]); $ticket = $tickets->create(Auth::user(), [ @@ -75,12 +72,11 @@ final class TicketCreate extends Component 'due_at' => $this->due_at, ], $this->attachments); - session()->flash('success', 'Ticket ' . $ticket->reference . ' has been created.'); + session()->flash('success', 'Ticket '.$ticket->reference.' has been created.'); return redirect()->route('helpdesk.tickets.index'); } - /** @return View */ public function render(): View { return view('helpdesk::livewire.ticket-create', [ diff --git a/Modules/Helpdesk/Resources/views/components/html-editor.blade.php b/Modules/Helpdesk/Resources/views/components/html-editor.blade.php index ea9af57b..ec6df4b7 100644 --- a/Modules/Helpdesk/Resources/views/components/html-editor.blade.php +++ b/Modules/Helpdesk/Resources/views/components/html-editor.blade.php @@ -33,6 +33,10 @@ > + @if($model) + + @endif +
diff --git a/Modules/Helpdesk/Resources/views/livewire/ticket-create.blade.php b/Modules/Helpdesk/Resources/views/livewire/ticket-create.blade.php index 2c06fe46..1a2a134d 100644 --- a/Modules/Helpdesk/Resources/views/livewire/ticket-create.blade.php +++ b/Modules/Helpdesk/Resources/views/livewire/ticket-create.blade.php @@ -5,28 +5,7 @@ 'subtitle' => 'Raise a new support request with priority, project, category, team and full details.', ]) -
+ @csrf
@@ -38,30 +17,17 @@
- + @error('subject')

{{ $message }}

@enderror
@if($companies->isNotEmpty())
- @foreach ($companies as $company) - + @endforeach @error('company_id')

{{ $message }}

@enderror @@ -70,17 +36,10 @@
- @foreach ($priorities as $priority) - + @endforeach @error('priority_id')

{{ $message }}

@enderror @@ -88,16 +47,10 @@
- @foreach ($categories as $category) - + @endforeach @error('category_id')

{{ $message }}

@enderror @@ -105,16 +58,10 @@
- @foreach ($projects as $project) - + @endforeach @error('project_id')

{{ $message }}

@enderror @@ -122,16 +69,10 @@
- @foreach ($teams as $team) - + @endforeach @error('team_id')

{{ $message }}

@enderror @@ -139,63 +80,25 @@
- + @error('due_at')

{{ $message }}

@enderror
- - -
-
- - - - - - - - -
- -
-
- - - - @error('body')

{{ $message }}

@enderror +
- + @error('attachments')

{{ $message }}

@enderror @error('attachments.*')

{{ $message }}

@enderror
- Cancel - + Cancel +
diff --git a/Modules/Helpdesk/Routes/web.php b/Modules/Helpdesk/Routes/web.php index 40b20763..1d658a18 100644 --- a/Modules/Helpdesk/Routes/web.php +++ b/Modules/Helpdesk/Routes/web.php @@ -17,6 +17,6 @@ Route::get('/settings', SettingsManager::class)->name('settings'); Route::get('/kanban', KanbanBoard::class)->name('kanban'); Route::get('/tickets', TicketIndex::class)->name('tickets.index'); +Route::post('/tickets', [TicketStoreController::class, 'store'])->name('tickets.store'); Route::get('/tickets/create', TicketCreate::class)->name('tickets.create'); -Route::post('/tickets', TicketStoreController::class)->name('tickets.store'); Route::get('/tickets/{ticket}', TicketShow::class)->name('tickets.show'); diff --git a/Modules/Helpdesk/Seeders/HelpdeskDatabaseSeeder.php b/Modules/Helpdesk/Seeders/HelpdeskDatabaseSeeder.php new file mode 100644 index 00000000..a102f108 --- /dev/null +++ b/Modules/Helpdesk/Seeders/HelpdeskDatabaseSeeder.php @@ -0,0 +1,137 @@ +seedStatuses(); + $this->seedPriorities(); + $this->seedCategories(); + $this->seedTeamsAndProjects(); + $this->seedWorkflowTemplates(); + } + + /** + * Seed ticket workflow statuses. + * + * @return void + */ + private function seedStatuses(): void + { + foreach ([ + ['name' => 'New', 'slug' => 'new', 'color' => 'sky', 'sort_order' => 5, 'is_closed' => false], + ['name' => 'Open', 'slug' => 'open', 'color' => 'blue', 'sort_order' => 10, 'is_closed' => false], + ['name' => 'In Progress', 'slug' => 'in-progress', 'color' => 'amber', 'sort_order' => 20, 'is_closed' => false], + ['name' => 'Stalled', 'slug' => 'stalled', 'color' => 'rose', 'sort_order' => 30, 'is_closed' => false], + ['name' => 'On-Hold', 'slug' => 'on-hold', 'color' => 'violet', 'sort_order' => 40, 'is_closed' => false], + ['name' => 'Closed', 'slug' => 'closed', 'color' => 'emerald', 'sort_order' => 90, 'is_closed' => true], + ] as $row) { + Status::query()->updateOrCreate(['slug' => $row['slug']], $row); + } + } + + /** + * Seed ticket priorities. + * + * @return void + */ + private function seedPriorities(): void + { + foreach ([ + ['name' => 'High', 'slug' => 'high', 'color' => 'red', 'sort_order' => 10], + ['name' => 'Medium', 'slug' => 'medium', 'color' => 'amber', 'sort_order' => 20], + ['name' => 'Low', 'slug' => 'low', 'color' => 'emerald', 'sort_order' => 30], + ] as $row) { + Priority::query()->updateOrCreate(['slug' => $row['slug']], $row); + } + } + + /** + * Seed ticket categories. + * + * @return void + */ + private function seedCategories(): void + { + foreach ([ + ['name' => 'Development', 'color' => 'indigo', 'description' => 'Software changes, enhancements and development work.'], + ['name' => 'Systems Deployment', 'color' => 'cyan', 'description' => 'Deployment, release, infrastructure and environment requests.'], + ['name' => 'Concepts', 'color' => 'purple', 'description' => 'Ideas, concepts, design discussion and future improvements.'], + ['name' => 'Bug', 'color' => 'rose', 'description' => 'Defects, regressions and incorrect behaviour.'], + ] as $row) { + Category::query()->updateOrCreate( + ['company_id' => null, 'slug' => Str::slug($row['name'])], + $row + ['company_id' => null, 'slug' => Str::slug($row['name'])] + ); + } + } + + /** + * Seed the default support team and a default project. + * + * @return void + */ + private function seedTeamsAndProjects(): void + { + $team = Team::query()->updateOrCreate( + ['company_id' => null, 'slug' => 'support'], + [ + 'company_id' => null, + 'name' => 'Support', + 'slug' => 'support', + 'color' => 'blue', + 'description' => 'Default support team for triage, ownership and ticket routing.', + ] + ); + + Project::query()->updateOrCreate( + ['company_id' => null, 'slug' => 'support-desk'], + [ + 'company_id' => null, + 'team_id' => $team->id, + 'title' => 'Support Desk', + 'slug' => 'support-desk', + 'color' => 'indigo', + 'description' => 'Default project for operational helpdesk tickets.', + ] + ); + } + + /** + * Seed default professional workflow email templates. + * + * @return void + */ + private function seedWorkflowTemplates(): void + { + foreach ([ + ['name' => 'Ticket raised requester notice', 'event' => 'ticket.created', 'subject' => 'Ticket {{ ticket.reference }} has been raised', 'html_body' => '

Hello {{ requester.name }},

Your ticket {{ ticket.reference }} has been raised successfully.

Subject: {{ ticket.subject }}

Priority: {{ ticket.priority }}

', 'to_role' => 'requester'], + ['name' => 'Ticket updated requester notice', 'event' => 'ticket.updated', 'subject' => 'Ticket {{ ticket.reference }} has been updated', 'html_body' => '

Hello {{ requester.name }},

Your ticket {{ ticket.reference }} has been updated.

Status: {{ ticket.status }}

Priority: {{ ticket.priority }}

', 'to_role' => 'requester'], + ['name' => 'Ticket response requester notice', 'event' => 'ticket.commented', 'subject' => 'New response on ticket {{ ticket.reference }}', 'html_body' => '

Hello {{ requester.name }},

A new response has been added to {{ ticket.reference }}.

Subject: {{ ticket.subject }}

', 'to_role' => 'requester'], + ['name' => 'Ticket closed requester notice', 'event' => 'ticket.closed', 'subject' => 'Ticket {{ ticket.reference }} has been closed', 'html_body' => '

Hello {{ requester.name }},

Your ticket {{ ticket.reference }} has been closed.

Thank you for using the support desk.

', 'to_role' => 'requester'], + ] as $template) { + WorkflowTemplate::query()->updateOrCreate( + ['name' => $template['name'], 'event' => $template['event']], + $template + ['text_body' => null, 'cc' => null, 'bcc' => null, 'is_active' => true] + ); + } + } +} diff --git a/Modules/Helpdesk/Services/AgentAssignmentService.php b/Modules/Helpdesk/Services/AgentAssignmentService.php index 8b47e197..f8cff963 100644 --- a/Modules/Helpdesk/Services/AgentAssignmentService.php +++ b/Modules/Helpdesk/Services/AgentAssignmentService.php @@ -7,6 +7,7 @@ namespace Modules\Helpdesk\Services; use App\Models\User; use Illuminate\Database\Eloquent\Builder; use Modules\Helpdesk\Models\Category; +use Modules\Helpdesk\Models\Ticket; final class AgentAssignmentService { @@ -22,9 +23,13 @@ final class AgentAssignmentService ->where(static function (Builder $query): void { $query->where('helpdesk_agent', true)->orWhere('helpdesk_admin', true); }) - ->withCount(['helpdeskAssignedTickets as open_helpdesk_tickets_count' => static function (Builder $query): void { - $query->whereNull('completed_at'); - }]); + ->selectSub( + Ticket::query() + ->selectRaw('count(*)') + ->whereColumn('helpdesk_tickets.agent_id', 'users.id') + ->whereNull('helpdesk_tickets.completed_at'), + 'open_helpdesk_tickets_count' + ); if ($categoryId !== null) { $category = Category::query()->find($categoryId); diff --git a/Modules/Helpdesk/Services/TicketService.php b/Modules/Helpdesk/Services/TicketService.php index f59dc72b..ecd90fc2 100644 --- a/Modules/Helpdesk/Services/TicketService.php +++ b/Modules/Helpdesk/Services/TicketService.php @@ -5,6 +5,7 @@ declare(strict_types=1); namespace Modules\Helpdesk\Services; use App\Models\User; +use Illuminate\Http\UploadedFile; use Illuminate\Support\Facades\DB; use Modules\Helpdesk\Models\Comment; use Modules\Helpdesk\Models\Status; @@ -17,21 +18,32 @@ final class TicketService private readonly AgentAssignmentService $assignment, private readonly AttachmentService $attachments, private readonly WorkflowNotificationService $workflow, - ) { - } + ) {} /** * Create a ticket and store initial attachments. * - * @param User $user - * @param array $data - * @param array $files - * @return Ticket + * @param array $data + * @param array $files */ public function create(User $user, array $data, array $files = []): Ticket { return DB::transaction(function () use ($user, $data, $files): Ticket { - $status = Status::query()->where('slug', config('helpdesk.default_status_slug'))->firstOrFail(); + $status = Status::query() + ->where('slug', config('helpdesk.default_status_slug', 'open')) + ->first(); + + if (! $status) { + $status = Status::query()->create([ + 'name' => 'Open', + 'slug' => config('helpdesk.default_status_slug', 'open'), + 'colour' => '#2563eb', + 'sort_order' => 1, + 'is_default' => true, + 'is_closed' => false, + ]); + + } /** @var Ticket $ticket */ $ticket = Ticket::query()->create([ @@ -62,12 +74,7 @@ final class TicketService /** * Add a public or internal comment and optional attachments. * - * @param Ticket $ticket - * @param User $user - * @param string $body - * @param bool $internal - * @param array $files - * @return Comment + * @param array $files */ public function comment(Ticket $ticket, User $user, string $body, bool $internal = false, array $files = [], ?int $parentId = null): Comment { @@ -90,10 +97,6 @@ final class TicketService /** * Close a ticket. - * - * @param Ticket $ticket - * @param User $user - * @return Ticket */ public function close(Ticket $ticket, User $user): Ticket { diff --git a/Modules/Helpdesk/changelog.yaml b/Modules/Helpdesk/changelog.yaml old mode 100644 new mode 100755 index 47480b36..0c7ea753 --- a/Modules/Helpdesk/changelog.yaml +++ b/Modules/Helpdesk/changelog.yaml @@ -31,9 +31,14 @@ versions: "1.0.5": changed: - - Reworked ticket creation to use a normal HTTP POST route instead of the Livewire update endpoint, preventing proxy or hashed Livewire update-route 404s from blocking ticket creation. - - Added a dedicated TicketStoreController for standard ticket creation validation, attachments and redirects. - - Changed the ticket-create Blade form to use real form field names, CSRF protection and a non-Livewire rich HTML editor payload. - - Removed the module-level Livewire update route override so the Helpdesk module no longer changes the application's global Livewire endpoint. + - Added the missing POST helpdesk.tickets.store route so native form submission to /helpdesk/tickets no longer returns 404. + - Added TicketStoreController as a safe non-JavaScript fallback while keeping the Livewire ticket create flow intact. + - Updated the ticket create form with method, action, CSRF, multipart support and named fields for fallback posting. - Kept module.json version in sync with changelog.yaml. + "1.0.6": + changed: + - Rebuilt the Helpdesk database seeder with the requested Categories, Team, Priority and Status lookup values. + - Added contrasting colour assignments for seeded lookup records. + - Corrected the seeder namespace to Modules\\Helpdesk\\Database\\Seeders for PSR-4 module autoloading. + - Kept module.json version in sync with changelog.yaml. diff --git a/Modules/Helpdesk/module.json b/Modules/Helpdesk/module.json index c399ce32..ad72462d 100644 --- a/Modules/Helpdesk/module.json +++ b/Modules/Helpdesk/module.json @@ -13,5 +13,5 @@ "Modules\\Helpdesk\\Providers\\HelpdeskServiceProvider" ], "files": [], - "version": "1.0.5" + "version": "1.0.6" } diff --git a/config/filesystems.php b/config/filesystems.php index 37d8fca4..00a2b786 100644 --- a/config/filesystems.php +++ b/config/filesystems.php @@ -59,7 +59,11 @@ return [ 'throw' => false, 'report' => false, ], - + 'helpdesk' => [ + 'driver' => 'local', + 'root' => storage_path('app/helpdesk'), + 'throw' => false, + ], ], /* diff --git a/modules_statuses.json b/modules_statuses.json index 6ee0d8ba..90ff3724 100644 --- a/modules_statuses.json +++ b/modules_statuses.json @@ -17,4 +17,4 @@ "Warehouse": true, "Pricing": true, "Helpdesk": true -} \ No newline at end of file +}