diff --git a/Modules/Helpdesk/Livewire/SettingsManager.php b/Modules/Helpdesk/Livewire/SettingsManager.php index cd80dbc..746f16d 100644 --- a/Modules/Helpdesk/Livewire/SettingsManager.php +++ b/Modules/Helpdesk/Livewire/SettingsManager.php @@ -12,6 +12,7 @@ use Illuminate\Support\Facades\Schema; use Illuminate\Support\Str; use Illuminate\Validation\ValidationException; use Livewire\Component; +use Livewire\WithPagination; use Modules\Helpdesk\Models\AutomationRule; use Modules\Helpdesk\Models\CannedResponse; use Modules\Helpdesk\Models\Category; @@ -30,10 +31,26 @@ use Modules\Helpdesk\Support\Color; final class SettingsManager extends Component { + use WithPagination; + public string $section = 'statuses'; public bool $teamModalOpen = false; + public bool $statusModalOpen = false; + public bool $priorityModalOpen = false; + public bool $categoryModalOpen = false; + public bool $projectModalOpen = false; + public bool $boardModalOpen = false; + public bool $repositoryModalOpen = false; + public bool $workflowModalOpen = false; + public bool $cannedModalOpen = false; + public bool $slaModalOpen = false; + public bool $automationModalOpen = false; + public bool $checklistModalOpen = false; + public bool $tagModalOpen = false; + public bool $productModalOpen = false; + /** @var array */ public array $statusForm = ['id' => null, 'name' => '', 'slug' => '', 'color' => 'gray', 'sort_order' => 0, 'is_closed' => false, 'is_default' => false]; @@ -141,6 +158,7 @@ final class SettingsManager extends Component $this->persistSettingModel(Status::class, $this->statusForm['id'], $data); $this->resetStatusForm(); + $this->statusModalOpen = false; session()->flash('success', 'Status saved.'); } @@ -149,6 +167,7 @@ final class SettingsManager extends Component $this->statusForm = Status::query()->findOrFail($id)->only(['id', 'name', 'slug', 'color', 'sort_order', 'is_closed', 'is_default']); $this->statusForm['color'] = Color::normalise($this->statusForm['color'] ?? 'gray'); $this->section = 'statuses'; + $this->statusModalOpen = true; } public function deleteStatus(int $id): void @@ -157,6 +176,21 @@ final class SettingsManager extends Component session()->flash('success', 'Status deleted.'); } + public function openCreateStatusModal(): void + { + $this->resetValidation(); + $this->resetStatusForm(); + $this->section = 'statuses'; + $this->statusModalOpen = true; + } + + public function closeStatusModal(): void + { + $this->resetValidation(); + $this->resetStatusForm(); + $this->statusModalOpen = false; + } + public function resetStatusForm(): void { $this->statusForm = ['id' => null, 'name' => '', 'slug' => '', 'color' => 'gray', 'sort_order' => 0, 'is_closed' => false, 'is_default' => false]; @@ -180,6 +214,7 @@ final class SettingsManager extends Component } $this->persistSettingModel(Priority::class, $this->priorityForm['id'], $data); $this->resetPriorityForm(); + $this->priorityModalOpen = false; session()->flash('success', 'Priority saved.'); } @@ -188,6 +223,7 @@ final class SettingsManager extends Component $this->priorityForm = Priority::query()->findOrFail($id)->only(['id', 'name', 'slug', 'color', 'sort_order', 'is_default']); $this->priorityForm['color'] = Color::normalise($this->priorityForm['color'] ?? 'gray'); $this->section = 'priorities'; + $this->priorityModalOpen = true; } public function deletePriority(int $id): void @@ -196,6 +232,21 @@ final class SettingsManager extends Component session()->flash('success', 'Priority deleted.'); } + public function openCreatePriorityModal(): void + { + $this->resetValidation(); + $this->resetPriorityForm(); + $this->section = 'priorities'; + $this->priorityModalOpen = true; + } + + public function closePriorityModal(): void + { + $this->resetValidation(); + $this->resetPriorityForm(); + $this->priorityModalOpen = false; + } + public function resetPriorityForm(): void { $this->priorityForm = ['id' => null, 'name' => '', 'slug' => '', 'color' => 'gray', 'sort_order' => 0, 'is_default' => false]; @@ -221,6 +272,7 @@ final class SettingsManager extends Component } $this->persistSettingModel(Category::class, $this->categoryForm['id'], $data); $this->resetCategoryForm(); + $this->categoryModalOpen = false; session()->flash('success', 'Category saved.'); } @@ -229,6 +281,7 @@ final class SettingsManager extends Component $this->categoryForm = Category::query()->findOrFail($id)->only(['id', 'name', 'slug', 'color', 'description', 'is_default']); $this->categoryForm['color'] = Color::normalise($this->categoryForm['color'] ?? 'blue'); $this->section = 'categories'; + $this->categoryModalOpen = true; } public function deleteCategory(int $id): void @@ -237,6 +290,21 @@ final class SettingsManager extends Component session()->flash('success', 'Category deleted.'); } + public function openCreateCategoryModal(): void + { + $this->resetValidation(); + $this->resetCategoryForm(); + $this->section = 'categories'; + $this->categoryModalOpen = true; + } + + public function closeCategoryModal(): void + { + $this->resetValidation(); + $this->resetCategoryForm(); + $this->categoryModalOpen = false; + } + public function resetCategoryForm(): void { $this->categoryForm = ['id' => null, 'name' => '', 'slug' => '', 'color' => 'blue', 'description' => '', 'is_default' => false]; @@ -401,6 +469,7 @@ final class SettingsManager extends Component } $this->persistSettingModel(Project::class, $this->projectForm['id'], $data); $this->resetProjectForm(); + $this->projectModalOpen = false; session()->flash('success', 'Project saved.'); } @@ -409,6 +478,7 @@ final class SettingsManager extends Component $this->projectForm = Project::query()->findOrFail($id)->only(['id', 'team_id', 'title', 'slug', 'color', 'description', 'is_default', 'archived_at']); $this->projectForm['color'] = Color::normalise($this->projectForm['color'] ?? 'blue'); $this->section = 'projects'; + $this->projectModalOpen = true; } public function deleteProject(int $id): void @@ -417,6 +487,21 @@ final class SettingsManager extends Component session()->flash('success', 'Project deleted.'); } + public function openCreateProjectModal(): void + { + $this->resetValidation(); + $this->resetProjectForm(); + $this->section = 'projects'; + $this->projectModalOpen = true; + } + + public function closeProjectModal(): void + { + $this->resetValidation(); + $this->resetProjectForm(); + $this->projectModalOpen = false; + } + public function resetProjectForm(): void { $this->projectForm = ['id' => null, 'team_id' => null, 'title' => '', 'slug' => '', 'color' => 'blue', 'description' => '', 'is_default' => false, 'archived_at' => null]; @@ -456,6 +541,7 @@ final class SettingsManager extends Component $board->statuses()->sync($sync); $this->resetBoardForm(); + $this->boardModalOpen = false; $this->section = 'boards'; session()->flash('success', 'Kanban board saved.'); } @@ -476,6 +562,7 @@ final class SettingsManager extends Component ]; $this->section = 'boards'; + $this->boardModalOpen = true; } public function deleteBoard(int $id): void @@ -484,6 +571,21 @@ final class SettingsManager extends Component session()->flash('success', 'Kanban board deleted.'); } + public function openCreateBoardModal(): void + { + $this->resetValidation(); + $this->resetBoardForm(); + $this->section = 'boards'; + $this->boardModalOpen = true; + } + + public function closeBoardModal(): void + { + $this->resetValidation(); + $this->resetBoardForm(); + $this->boardModalOpen = false; + } + public function resetBoardForm(): void { $this->boardForm = ['id' => null, 'name' => '', 'slug' => '', 'description' => '', 'owner_id' => null, 'is_default' => false, 'sort_order' => 0, 'status_ids' => []]; @@ -506,6 +608,7 @@ final class SettingsManager extends Component $this->persistSettingModel(GitRepository::class, $this->repositoryForm['id'], $data); $this->resetRepositoryForm(); + $this->repositoryModalOpen = false; session()->flash('success', 'Git repository saved.'); } @@ -513,6 +616,7 @@ final class SettingsManager extends Component { $this->repositoryForm = GitRepository::query()->findOrFail($id)->only(['id', 'project_id', 'name', 'provider', 'remote_url', 'default_branch', 'webhook_secret']); $this->section = 'git'; + $this->repositoryModalOpen = true; } public function deleteRepository(int $id): void @@ -521,6 +625,21 @@ final class SettingsManager extends Component session()->flash('success', 'Git repository deleted.'); } + public function openCreateRepositoryModal(): void + { + $this->resetValidation(); + $this->resetRepositoryForm(); + $this->section = 'git'; + $this->repositoryModalOpen = true; + } + + public function closeRepositoryModal(): void + { + $this->resetValidation(); + $this->resetRepositoryForm(); + $this->repositoryModalOpen = false; + } + public function resetRepositoryForm(): void { $this->repositoryForm = ['id' => null, 'project_id' => null, 'name' => '', 'provider' => 'git', 'remote_url' => '', 'default_branch' => 'main', 'webhook_secret' => '']; @@ -544,6 +663,7 @@ final class SettingsManager extends Component $this->persistSettingModel(WorkflowTemplate::class, $this->workflowForm['id'], $data); $this->resetWorkflowTemplateForm(); + $this->workflowModalOpen = false; session()->flash('success', 'Workflow message saved.'); } @@ -551,6 +671,7 @@ final class SettingsManager extends Component { $this->workflowForm = WorkflowTemplate::query()->findOrFail($id)->only(['id', 'name', 'event', 'subject', 'html_body', 'text_body', 'to_role', 'cc', 'bcc', 'is_active']); $this->section = 'workflow'; + $this->workflowModalOpen = true; } public function deleteWorkflowTemplate(int $id): void @@ -559,6 +680,21 @@ final class SettingsManager extends Component session()->flash('success', 'Workflow message deleted.'); } + public function openCreateWorkflowTemplateModal(): void + { + $this->resetValidation(); + $this->resetWorkflowTemplateForm(); + $this->section = 'workflow'; + $this->workflowModalOpen = true; + } + + public function closeWorkflowTemplateModal(): void + { + $this->resetValidation(); + $this->resetWorkflowTemplateForm(); + $this->workflowModalOpen = false; + } + public function resetWorkflowTemplateForm(): void { $this->workflowForm = ['id' => null, 'name' => '', 'event' => 'ticket.created', 'subject' => '', 'html_body' => '', 'text_body' => '', 'to_role' => 'requester', 'cc' => '', 'bcc' => '', 'is_active' => true]; @@ -582,6 +718,7 @@ final class SettingsManager extends Component $data['slug'] = $this->slug($data['slug'] ?: $data['name']); $this->persistSettingModel(CannedResponse::class, $this->cannedForm['id'], $data); $this->resetCannedResponseForm(); + $this->cannedModalOpen = false; session()->flash('success', 'Canned response saved.'); } @@ -589,6 +726,7 @@ final class SettingsManager extends Component { $this->cannedForm = CannedResponse::query()->findOrFail($id)->only(['id', 'category_id', 'team_id', 'name', 'slug', 'visibility', 'subject', 'body', 'is_active', 'sort_order']); $this->section = 'canned'; + $this->cannedModalOpen = true; } public function deleteCannedResponse(int $id): void @@ -597,6 +735,21 @@ final class SettingsManager extends Component session()->flash('success', 'Canned response deleted.'); } + public function openCreateCannedResponseModal(): void + { + $this->resetValidation(); + $this->resetCannedResponseForm(); + $this->section = 'canned'; + $this->cannedModalOpen = true; + } + + public function closeCannedResponseModal(): void + { + $this->resetValidation(); + $this->resetCannedResponseForm(); + $this->cannedModalOpen = false; + } + public function resetCannedResponseForm(): void { $this->cannedForm = ['id' => null, 'category_id' => null, 'team_id' => null, 'name' => '', 'slug' => '', 'visibility' => 'agents', 'subject' => '', 'body' => '', 'is_active' => true, 'sort_order' => 0]; @@ -623,6 +776,7 @@ final class SettingsManager extends Component } $this->persistSettingModel(SlaPolicy::class, $this->slaForm['id'], $data); $this->resetSlaPolicyForm(); + $this->slaModalOpen = false; session()->flash('success', 'SLA policy saved.'); } @@ -630,6 +784,7 @@ final class SettingsManager extends Component { $this->slaForm = SlaPolicy::query()->findOrFail($id)->only(['id', 'priority_id', 'category_id', 'name', 'slug', 'first_response_minutes', 'resolution_minutes', 'business_hours_only', 'is_default', 'is_active']); $this->section = 'sla'; + $this->slaModalOpen = true; } public function deleteSlaPolicy(int $id): void @@ -638,6 +793,21 @@ final class SettingsManager extends Component session()->flash('success', 'SLA policy deleted.'); } + public function openCreateSlaPolicyModal(): void + { + $this->resetValidation(); + $this->resetSlaPolicyForm(); + $this->section = 'sla'; + $this->slaModalOpen = true; + } + + public function closeSlaPolicyModal(): void + { + $this->resetValidation(); + $this->resetSlaPolicyForm(); + $this->slaModalOpen = false; + } + public function resetSlaPolicyForm(): void { $this->slaForm = ['id' => null, 'priority_id' => null, 'category_id' => null, 'name' => '', 'slug' => '', 'first_response_minutes' => 240, 'resolution_minutes' => 2880, 'business_hours_only' => false, 'is_default' => false, 'is_active' => true]; @@ -666,6 +836,7 @@ final class SettingsManager extends Component $data['slug'] = $this->slug($data['slug'] ?: $data['name']); $this->persistSettingModel(AutomationRule::class, $this->automationForm['id'], $data); $this->resetAutomationRuleForm(); + $this->automationModalOpen = false; session()->flash('success', 'Automation rule saved.'); } @@ -673,6 +844,7 @@ final class SettingsManager extends Component { $this->automationForm = AutomationRule::query()->findOrFail($id)->only(['id', 'name', 'slug', 'match_sender', 'match_subject', 'match_mailbox', 'match_category_id', 'set_priority_id', 'set_category_id', 'set_team_id', 'set_project_id', 'set_status_id', 'set_agent_id', 'is_active', 'sort_order']); $this->section = 'automation'; + $this->automationModalOpen = true; } public function deleteAutomationRule(int $id): void @@ -681,6 +853,21 @@ final class SettingsManager extends Component session()->flash('success', 'Automation rule deleted.'); } + public function openCreateAutomationRuleModal(): void + { + $this->resetValidation(); + $this->resetAutomationRuleForm(); + $this->section = 'automation'; + $this->automationModalOpen = true; + } + + public function closeAutomationRuleModal(): void + { + $this->resetValidation(); + $this->resetAutomationRuleForm(); + $this->automationModalOpen = false; + } + public function resetAutomationRuleForm(): void { $this->automationForm = ['id' => null, 'name' => '', 'slug' => '', 'match_sender' => '', 'match_subject' => '', 'match_mailbox' => '', 'match_category_id' => null, 'set_priority_id' => null, 'set_category_id' => null, 'set_team_id' => null, 'set_project_id' => null, 'set_status_id' => null, 'set_agent_id' => null, 'is_active' => true, 'sort_order' => 0]; @@ -702,6 +889,7 @@ final class SettingsManager extends Component $data['slug'] = $this->slug($data['slug'] ?: $data['name']); $this->persistSettingModel(Product::class, $this->productForm['id'], $data); $this->resetProductForm(); + $this->productModalOpen = false; session()->flash('success', 'Product/service saved.'); } @@ -709,6 +897,7 @@ final class SettingsManager extends Component { $this->productForm = Product::query()->findOrFail($id)->only(['id', 'name', 'slug', 'type', 'version', 'release_channel', 'description', 'is_active']); $this->section = 'products'; + $this->productModalOpen = true; } public function deleteProduct(int $id): void @@ -717,6 +906,21 @@ final class SettingsManager extends Component session()->flash('success', 'Product/service deleted.'); } + public function openCreateProductModal(): void + { + $this->resetValidation(); + $this->resetProductForm(); + $this->section = 'products'; + $this->productModalOpen = true; + } + + public function closeProductModal(): void + { + $this->resetValidation(); + $this->resetProductForm(); + $this->productModalOpen = false; + } + public function resetProductForm(): void { $this->productForm = ['id' => null, 'name' => '', 'slug' => '', 'type' => 'service', 'version' => '', 'release_channel' => '', 'description' => '', 'is_active' => true]; @@ -734,6 +938,7 @@ final class SettingsManager extends Component $data['slug'] = $this->slug($data['slug'] ?: $data['name']); $this->persistSettingModel(Tag::class, $this->tagForm['id'], $data); $this->resetTagForm(); + $this->tagModalOpen = false; session()->flash('success', 'Tag saved.'); } @@ -742,6 +947,7 @@ final class SettingsManager extends Component $this->tagForm = Tag::query()->findOrFail($id)->only(['id', 'name', 'slug', 'color']); $this->tagForm['color'] = Color::normalise($this->tagForm['color'] ?? 'gray'); $this->section = 'tags'; + $this->tagModalOpen = true; } public function deleteTag(int $id): void @@ -750,6 +956,21 @@ final class SettingsManager extends Component session()->flash('success', 'Tag deleted.'); } + public function openCreateTagModal(): void + { + $this->resetValidation(); + $this->resetTagForm(); + $this->section = 'tags'; + $this->tagModalOpen = true; + } + + public function closeTagModal(): void + { + $this->resetValidation(); + $this->resetTagForm(); + $this->tagModalOpen = false; + } + public function resetTagForm(): void { $this->tagForm = ['id' => null, 'name' => '', 'slug' => '', 'color' => 'gray']; @@ -773,6 +994,7 @@ final class SettingsManager extends Component $data['items'] = collect(preg_split('/\r\n|\r|\n/', $itemsText) ?: [])->map(fn ($line): string => trim($line))->filter()->values()->all(); $this->persistSettingModel(ChecklistTemplate::class, $this->checklistTemplateForm['id'], $data); $this->resetChecklistTemplateForm(); + $this->checklistModalOpen = false; session()->flash('success', 'Checklist template saved.'); } @@ -783,6 +1005,7 @@ final class SettingsManager extends Component 'items_text' => collect($template->items ?? [])->map(fn ($item): string => is_array($item) ? (string) ($item['title'] ?? '') : (string) $item)->implode("\n"), ]; $this->section = 'checklists'; + $this->checklistModalOpen = true; } public function deleteChecklistTemplate(int $id): void @@ -791,6 +1014,21 @@ final class SettingsManager extends Component session()->flash('success', 'Checklist template deleted.'); } + public function openCreateChecklistTemplateModal(): void + { + $this->resetValidation(); + $this->resetChecklistTemplateForm(); + $this->section = 'checklists'; + $this->checklistModalOpen = true; + } + + public function closeChecklistTemplateModal(): void + { + $this->resetValidation(); + $this->resetChecklistTemplateForm(); + $this->checklistModalOpen = false; + } + public function resetChecklistTemplateForm(): void { $this->checklistTemplateForm = ['id' => null, 'category_id' => null, 'project_id' => null, 'name' => '', 'slug' => '', 'items_text' => '', 'is_active' => true]; @@ -810,15 +1048,45 @@ final class SettingsManager extends Component 'boardOwners' => User::query()->where(static function ($query): void { $query->where('helpdesk_agent', true)->orWhere('helpdesk_admin', true); })->orderBy('name')->get(['id', 'name', 'email']), - 'boards' => KanbanBoard::query()->with(['statuses', 'owner:id,name,email'])->withCount('tickets')->orderBy('sort_order')->orderBy('name')->get(), - 'repositories' => GitRepository::query()->with(['project'])->withCount(['commits', 'webhookLogs'])->orderBy('name')->get(), - 'workflowTemplates' => WorkflowTemplate::query()->orderBy('event')->orderBy('name')->get(), - 'cannedResponses' => CannedResponse::query()->with(['category', 'team'])->orderBy('sort_order')->orderBy('name')->get(), - 'slaPolicies' => SlaPolicy::query()->with(['priority', 'category'])->orderByDesc('is_default')->orderBy('name')->get(), - 'automationRules' => AutomationRule::query()->orderBy('sort_order')->orderBy('name')->get(), - 'products' => Product::query()->withCount('tickets')->orderBy('name')->get(), - 'tags' => Tag::query()->orderBy('name')->get(), - 'checklistTemplates' => ChecklistTemplate::query()->orderBy('name')->get(), + 'boards' => KanbanBoard::query() + ->with(['statuses', 'owner:id,name,email']) + ->withCount('tickets') + ->orderBy('sort_order') + ->orderBy('name') + ->paginate(10, ['*'], 'boardsPage'), + 'repositories' => GitRepository::query() + ->with(['project']) + ->withCount(['commits', 'webhookLogs']) + ->orderBy('name') + ->paginate(10, ['*'], 'gitPage'), + 'workflowTemplates' => WorkflowTemplate::query() + ->orderBy('event') + ->orderBy('name') + ->paginate(10, ['*'], 'workflowPage'), + 'cannedResponses' => CannedResponse::query() + ->with(['category', 'team']) + ->orderBy('sort_order') + ->orderBy('name') + ->paginate(10, ['*'], 'cannedPage'), + 'slaPolicies' => SlaPolicy::query() + ->with(['priority', 'category']) + ->orderByDesc('is_default') + ->orderBy('name') + ->paginate(10, ['*'], 'slaPage'), + 'automationRules' => AutomationRule::query() + ->orderBy('sort_order') + ->orderBy('name') + ->paginate(10, ['*'], 'rulesPage'), + 'products' => Product::query() + ->withCount('tickets') + ->orderBy('name') + ->paginate(10, ['*'], 'productsPage'), + 'tags' => Tag::query() + ->orderBy('name') + ->paginate(10, ['*'], 'tagsPage'), + 'checklistTemplates' => ChecklistTemplate::query() + ->orderBy('name') + ->paginate(10, ['*'], 'checksPage'), 'colourOptions' => Color::options(), ])->layout('layouts.app'); } @@ -830,7 +1098,7 @@ final class SettingsManager extends Component ->when($this->hasDefaultColumn('helpdesk_statuses'), fn ($query) => $query->orderByDesc('is_default')) ->orderBy('sort_order') ->orderBy('name') - ->get(); + ->paginate(10, ['*'], 'statusesPage'); } private function orderedPriorities(): mixed @@ -840,7 +1108,7 @@ final class SettingsManager extends Component ->when($this->hasDefaultColumn('helpdesk_priorities'), fn ($query) => $query->orderByDesc('is_default')) ->orderBy('sort_order') ->orderBy('name') - ->get(); + ->paginate(10, ['*'], 'prioritiesPage'); } private function orderedCategories(): mixed @@ -849,7 +1117,7 @@ final class SettingsManager extends Component ->withCount('tickets') ->when($this->hasDefaultColumn('helpdesk_categories'), fn ($query) => $query->orderByDesc('is_default')) ->orderBy('name') - ->get(); + ->paginate(10, ['*'], 'categoriesPage'); } private function orderedTeams(): mixed @@ -859,7 +1127,7 @@ final class SettingsManager extends Component ->withCount(['projects', 'tickets', 'users']) ->when($this->hasDefaultColumn('helpdesk_teams'), fn ($query) => $query->orderByDesc('is_default')) ->orderBy('name') - ->get(); + ->paginate(10, ['*'], 'teamsPage'); } private function orderedProjects(): mixed @@ -869,7 +1137,7 @@ final class SettingsManager extends Component ->withCount('tickets') ->when($this->hasDefaultColumn('helpdesk_projects'), fn ($query) => $query->orderByDesc('is_default')) ->orderBy('title') - ->get(); + ->paginate(10, ['*'], 'projectsPage'); } /** diff --git a/Modules/Helpdesk/Resources/assets/css/app.css b/Modules/Helpdesk/Resources/assets/css/app.css index c7c675e..ef97daf 100644 --- a/Modules/Helpdesk/Resources/assets/css/app.css +++ b/Modules/Helpdesk/Resources/assets/css/app.css @@ -982,3 +982,38 @@ span.flatpickr-weekday { box-shadow: 0 0 0 1000px #09090b inset !important; transition: background-color 9999s ease-in-out 0s; } + + +/* Helpdesk settings modal presentation */ +.hd-settings-modal { + text-align: left; +} + +.hd-settings-modal form { + width: 100%; + text-align: left; +} + +.hd-settings-modal form > * { + min-width: 0; +} + +.hd-settings-modal input:not([type="checkbox"]):not([type="radio"]):not([type="color"]), +.hd-settings-modal select, +.hd-settings-modal textarea { + width: 100%; +} + +.hd-settings-modal textarea { + line-height: 1.5; +} + +.hd-settings-modal label { + text-align: left; +} + +@media (min-width: 768px) { + .hd-settings-modal { + padding-inline: 0.25rem; + } +} diff --git a/Modules/Helpdesk/Tests/Feature/SettingsModalStructureTest.php b/Modules/Helpdesk/Tests/Feature/SettingsModalStructureTest.php new file mode 100644 index 0000000..50ca9e9 --- /dev/null +++ b/Modules/Helpdesk/Tests/Feature/SettingsModalStructureTest.php @@ -0,0 +1,27 @@ +hasProperty($property), $property); + } + } +} diff --git a/resources/themes/backend/views/components/helpdesk/settings-pagination.blade.php b/resources/themes/backend/views/components/helpdesk/settings-pagination.blade.php new file mode 100644 index 0000000..46cb784 --- /dev/null +++ b/resources/themes/backend/views/components/helpdesk/settings-pagination.blade.php @@ -0,0 +1,28 @@ +@props(['paginator']) + +@if ($paginator instanceof \Illuminate\Contracts\Pagination\Paginator) +
+
+ @if (method_exists($paginator, 'total')) + Showing + {{ $paginator->firstItem() ?? 0 }} + – + {{ $paginator->lastItem() ?? 0 }} + of + {{ $paginator->total() }} + @else + Page {{ $paginator->currentPage() }} + @endif +
+ + @if ($paginator->hasPages()) +
+ {{ $paginator->onEachSide(1)->links() }} +
+ @else +
+ Page 1 +
+ @endif +
+@endif diff --git a/resources/themes/backend/views/modules/helpdesk/livewire/settings-manager.blade.php b/resources/themes/backend/views/modules/helpdesk/livewire/settings-manager.blade.php index 449c211..9ac3f91 100644 --- a/resources/themes/backend/views/modules/helpdesk/livewire/settings-manager.blade.php +++ b/resources/themes/backend/views/modules/helpdesk/livewire/settings-manager.blade.php @@ -3,20 +3,20 @@ * Keep the settings view safe during rolling module upgrades or Livewire navigate * hydration where the compiled Blade view may be newer than the loaded component. */ - $statuses = collect($statuses ?? []); - $priorities = collect($priorities ?? []); - $categories = collect($categories ?? []); - $teams = collect($teams ?? []); - $projects = collect($projects ?? []); - $boards = collect($boards ?? []); - $repositories = collect($repositories ?? []); - $workflowTemplates = collect($workflowTemplates ?? []); - $cannedResponses = collect($cannedResponses ?? []); - $slaPolicies = collect($slaPolicies ?? []); - $automationRules = collect($automationRules ?? []); - $checklistTemplates = collect($checklistTemplates ?? []); - $tags = collect($tags ?? []); - $products = collect($products ?? []); + $statuses = $statuses ?? collect(); + $priorities = $priorities ?? collect(); + $categories = $categories ?? collect(); + $teams = $teams ?? collect(); + $projects = $projects ?? collect(); + $boards = $boards ?? collect(); + $repositories = $repositories ?? collect(); + $workflowTemplates = $workflowTemplates ?? collect(); + $cannedResponses = $cannedResponses ?? collect(); + $slaPolicies = $slaPolicies ?? collect(); + $automationRules = $automationRules ?? collect(); + $checklistTemplates = $checklistTemplates ?? collect(); + $tags = $tags ?? collect(); + $products = $products ?? collect(); @endphp
@@ -43,20 +43,20 @@
@foreach ([ - 'statuses' => ['label' => 'Statuses', 'count' => $statuses->count()], - 'priorities' => ['label' => 'Priorities', 'count' => $priorities->count()], - 'categories' => ['label' => 'Categories', 'count' => $categories->count()], - 'teams' => ['label' => 'Teams', 'count' => $teams->count()], - 'projects' => ['label' => 'Projects', 'count' => $projects->count()], - 'boards' => ['label' => 'Boards', 'count' => $boards->count()], - 'git' => ['label' => 'Git', 'count' => $repositories->count()], - 'workflow' => ['label' => 'Workflow', 'count' => $workflowTemplates->count()], - 'canned' => ['label' => 'Canned', 'count' => $cannedResponses->count()], - 'sla' => ['label' => 'SLA', 'count' => $slaPolicies->count()], - 'automation' => ['label' => 'Rules', 'count' => $automationRules->count()], - 'checklists' => ['label' => 'Checks', 'count' => $checklistTemplates->count()], - 'tags' => ['label' => 'Tags', 'count' => $tags->count()], - 'products' => ['label' => 'Products', 'count' => $products->count()], + 'statuses' => ['label' => 'Statuses', 'count' => method_exists($statuses, 'total') ? $statuses->total() : $statuses->count()], + 'priorities' => ['label' => 'Priorities', 'count' => method_exists($priorities, 'total') ? $priorities->total() : $priorities->count()], + 'categories' => ['label' => 'Categories', 'count' => method_exists($categories, 'total') ? $categories->total() : $categories->count()], + 'teams' => ['label' => 'Teams', 'count' => method_exists($teams, 'total') ? $teams->total() : $teams->count()], + 'projects' => ['label' => 'Projects', 'count' => method_exists($projects, 'total') ? $projects->total() : $projects->count()], + 'boards' => ['label' => 'Boards', 'count' => method_exists($boards, 'total') ? $boards->total() : $boards->count()], + 'git' => ['label' => 'Git', 'count' => method_exists($repositories, 'total') ? $repositories->total() : $repositories->count()], + 'workflow' => ['label' => 'Workflow', 'count' => method_exists($workflowTemplates, 'total') ? $workflowTemplates->total() : $workflowTemplates->count()], + 'canned' => ['label' => 'Canned', 'count' => method_exists($cannedResponses, 'total') ? $cannedResponses->total() : $cannedResponses->count()], + 'sla' => ['label' => 'SLA', 'count' => method_exists($slaPolicies, 'total') ? $slaPolicies->total() : $slaPolicies->count()], + 'automation' => ['label' => 'Rules', 'count' => method_exists($automationRules, 'total') ? $automationRules->total() : $automationRules->count()], + 'checklists' => ['label' => 'Checks', 'count' => method_exists($checklistTemplates, 'total') ? $checklistTemplates->total() : $checklistTemplates->count()], + 'tags' => ['label' => 'Tags', 'count' => method_exists($tags, 'total') ? $tags->total() : $tags->count()], + 'products' => ['label' => 'Products', 'count' => method_exists($products, 'total') ? $products->total() : $products->count()], ] as $key => $item) @php $sectionPalette = match ($key) { @@ -103,12 +103,34 @@ @if ($section === 'statuses') - -
-
+ + + + Create status + + + + +
+ {{ filled($statusForm['id'] ?? null) ? 'Edit status' : 'Create status' }} + Create or edit this status without leaving the settings list. +
+ +
-
+
@@ -124,41 +146,52 @@ class="rounded border-zinc-300 bg-zinc-950 text-blue-600 focus:ring-blue-500"> Default status for new tickets - @error('teamForm.name') -
{{ $message }}
- @enderror - @error('teamForm.slug') -
{{ $message }}
- @enderror - @error('teamForm.color') -
{{ $message }}
- @enderror - @error('teamForm.user_ids.*') -
{{ $message }}
- @enderror
Save status - Reset + Cancel
- + - + + @endif @if ($section === 'priorities') - -
-
+ + + + Create priority + + + + +
+ {{ filled($priorityForm['id'] ?? null) ? 'Edit priority' : 'Create priority' }} + Create or edit this priority without leaving the settings list. +
+ +
-
+
@@ -174,27 +207,48 @@ Save priority - Reset + Cancel
- +
- + + @endif @if ($section === 'categories') - -
-
-
-
+ + + + Create category + + + + +
+ {{ filled($categoryForm['id'] ?? null) ? 'Edit category' : 'Create category' }} + Create or edit this category without leaving the settings list. +
+ +
-
+
@@ -210,14 +264,15 @@ Save category - Reset + Cancel
- +
- + + @endif @if ($section === 'teams') @@ -241,7 +296,7 @@
@@ -307,19 +362,6 @@ Default team for new tickets - @error('teamForm.name') -
{{ $message }}
- @enderror - @error('teamForm.slug') -
{{ $message }}
- @enderror - @error('teamForm.color') -
{{ $message }}
- @enderror - @error('teamForm.user_ids.*') -
{{ $message }}
- @enderror -
Cancel @@ -331,14 +373,37 @@
- + + @endif @if ($section === 'projects') - -
+ + + + Create project + + + + +
+ {{ filled($projectForm['id'] ?? null) ? 'Edit project' : 'Create project' }} + Create or edit this project without leaving the settings list. +
+
@@ -368,22 +433,45 @@ Save project - Reset + Cancel
-
+ -
+ + @endif @if ($section === 'boards') - -
+ + + + Create board + + + + +
+ {{ filled($boardForm['id'] ?? null) ? 'Edit board' : 'Create board' }} + Create or edit this board without leaving the settings list. +
+ @@ -420,12 +508,12 @@ Save board - Reset + Cancel
-
+
@@ -485,14 +573,37 @@
-
+ + @endif @if ($section === 'git') - -
+ + + + Create repository + + + + +
+ {{ filled($repositoryForm['id'] ?? null) ? 'Edit repository' : 'Create repository' }} + Create or edit this repository without leaving the settings list. +
+ @foreach ($projects as $project) @@ -512,22 +623,45 @@ Save repository - Reset + Cancel
-
+ -
+ + @endif @if ($section === 'workflow') - -
+ + + + Create workflow message + + + + +
+ {{ filled($workflowForm['id'] ?? null) ? 'Edit workflow message' : 'Create workflow message' }} + Create or edit this workflow message without leaving the settings list. +
+ @@ -564,12 +698,12 @@ Save workflow message - Reset + Cancel
- +
@@ -616,15 +750,38 @@
- + + @endif @if ($section === 'canned') - -
+ + + + Create canned response + + + + +
+ {{ filled($cannedForm['id'] ?? null) ? 'Edit canned response' : 'Create canned response' }} + Create or edit this canned response without leaving the settings list. +
+ @@ -654,12 +811,12 @@ Save canned response - Reset + Cancel
-
+
@forelse ($cannedResponses as $response)
@@ -692,14 +849,37 @@ No canned responses yet.
@endforelse
-
+ + @endif @if ($section === 'sla') - -
+ + + + Create SLA policy + + + + +
+ {{ filled($slaForm['id'] ?? null) ? 'Edit SLA policy' : 'Create SLA policy' }} + Create or edit this SLA policy without leaving the settings list. +
+ @@ -731,12 +911,12 @@ Save SLA policy - Reset + Cancel
- +
@@ -778,14 +958,37 @@
- + + @endif @if ($section === 'automation') - -
+ + + + Create automation rule + + + + +
+ {{ filled($automationForm['id'] ?? null) ? 'Edit automation rule' : 'Create automation rule' }} + Create or edit this automation rule without leaving the settings list. +
+ Save automation rule - Reset + Cancel -
+
@forelse ($automationRules as $rule)
@@ -858,14 +1061,37 @@ No automation rules yet.
@endforelse
-
+ + @endif @if ($section === 'checklists') - -
+ + + + Create checklist + + + + +
+ {{ filled($checklistTemplateForm['id'] ?? null) ? 'Edit checklist template' : 'Create checklist' }} + Create or edit this checklist template without leaving the settings list. +
+ @@ -891,12 +1117,12 @@ Save checklist - Reset + Cancel -
+
@forelse ($checklistTemplates as $template)
@@ -918,14 +1144,37 @@ No checklist templates yet.
@endforelse
-
+ + @endif @if ($section === 'tags') - -
+ + + + Create tag + + + + +
+ {{ filled($tagForm['id'] ?? null) ? 'Edit tag' : 'Create tag' }} + Create or edit this tag without leaving the settings list. +
+
@@ -937,12 +1186,12 @@ Save tag - Reset + Cancel -
+
@forelse ($tags as $tag)
@endforelse
- + + @endif @if ($section === 'products') - -
+ + + + Create product + + + + +
+ {{ filled($productForm['id'] ?? null) ? 'Edit product/service' : 'Create product' }} + Create or edit this product/service without leaving the settings list. +
+ @@ -985,12 +1257,12 @@ Save product/service - Reset + Cancel
- +
@forelse ($products as $product)
@@ -1013,7 +1285,8 @@ No products or services yet.
@endforelse
-
+ + @endif