From 0fb4223bfcc246a63cc487c78a051efe398959bc Mon Sep 17 00:00:00 2001 From: Mark Gregory Date: Mon, 28 Sep 2026 15:21:32 +0100 Subject: [PATCH] added support for legacy tickets, fixed switching of modes and also style updates --- .../Console/ImportDocumentationCommand.php | 59 +-------- .../Controllers/DocumentationController.php | 2 +- .../StoreDocumentationDocumentRequest.php | 4 +- .../UpdateDocumentationDocumentRequest.php | 4 +- .../Models/DocumentationAttachment.php | 18 +-- .../Models/DocumentationCategory.php | 18 ++- .../Models/DocumentationDocument.php | 22 ++-- .../DocumentationServiceProvider.php | 9 +- .../Feature/DocumentationArchiveTest.php | 1 - .../Tests/Feature/DocumentationModuleTest.php | 3 +- .../Console/ImportLegacyHelpdeskCommand.php | 23 +++- ...8_100000_create_helpdesk_legacy_tables.php | 3 +- .../Controllers/LegacySystemController.php | 1 + .../Helpdesk/Livewire/ArchivedTicketIndex.php | 4 + .../Livewire/HelpdeskDashboardWidget.php | 5 +- Modules/Helpdesk/Livewire/SettingsManager.php | 114 ++++++++++-------- Modules/Helpdesk/Models/LegacyCategory.php | 1 + Modules/Helpdesk/Models/LegacyComment.php | 2 + Modules/Helpdesk/Models/LegacyTicket.php | 2 + Modules/Helpdesk/Models/Team.php | 1 - Modules/Helpdesk/Models/Ticket.php | 1 - .../Helpdesk/Services/HelpdeskSyncService.php | 21 ++-- .../Http/Controllers/UserController.php | 4 +- app/Livewire/Settings/Appearance.php | 1 - app/Livewire/Settings/DeleteUserForm.php | 4 +- app/Livewire/Settings/Profile.php | 6 +- app/Livewire/Settings/Security.php | 6 +- .../Settings/TwoFactor/RecoveryCodes.php | 4 +- app/Models/User.php | 4 +- deploy.php | 1 - 30 files changed, 162 insertions(+), 186 deletions(-) diff --git a/Modules/Documentation/Console/ImportDocumentationCommand.php b/Modules/Documentation/Console/ImportDocumentationCommand.php index b936c45..6e03b8d 100644 --- a/Modules/Documentation/Console/ImportDocumentationCommand.php +++ b/Modules/Documentation/Console/ImportDocumentationCommand.php @@ -5,31 +5,19 @@ declare(strict_types=1); namespace Modules\Documentation\Console; use Illuminate\Console\Command; - use Illuminate\Support\Facades\DB; - use Illuminate\Support\Str; - use Modules\Documentation\Models\DocumentationCategory; - use Modules\Documentation\Models\DocumentationDocument; - use SplFileInfo; - use Symfony\Component\Finder\Finder; - use Throwable; final class ImportDocumentationCommand extends Command - { - /** - * The console command signature. - */ - protected $signature = 'helpdesk:import-documentation {path? : Directory containing Markdown files; defaults to the bundled Sentinel documentation} @@ -39,21 +27,14 @@ final class ImportDocumentationCommand extends Command {--fresh : Remove previously imported documents in this category before importing}'; /** - * The console command description. - */ - protected $description = 'Import Markdown documentation into the Helpdesk Documentation module.'; /** - * Execute the console command. - */ - public function handle(): int - { $path = $this->resolvePath(); @@ -164,7 +145,7 @@ final class ImportDocumentationCommand extends Command $this->importFile($category, $file, $path); - ++$imported; + $imported++; $progress->advance(); @@ -198,7 +179,7 @@ final class ImportDocumentationCommand extends Command $this->components->error( - 'Documentation import failed: ' . $exception->getMessage(), + 'Documentation import failed: '.$exception->getMessage(), ); @@ -217,13 +198,9 @@ final class ImportDocumentationCommand extends Command } /** - * Resolve the requested documentation source directory. - */ - private function resolvePath(): string - { $argument = $this->argument('path'); @@ -247,17 +224,13 @@ final class ImportDocumentationCommand extends Command } /** - * Find Markdown files recursively. * * @return list - */ - private function markdownFiles(string $path): array - { $finder = Finder::create() @@ -273,7 +246,6 @@ final class ImportDocumentationCommand extends Command ->sortByName(); /** @var list $files */ - $files = iterator_to_array($finder, false); return $files; @@ -281,11 +253,8 @@ final class ImportDocumentationCommand extends Command } /** - * Import one Markdown document. - */ - private function importFile( DocumentationCategory $category, @@ -301,7 +270,6 @@ final class ImportDocumentationCommand extends Command if ($realPath === false || ! is_readable($realPath)) { throw new \RuntimeException( - sprintf('Unable to read documentation file: %s', $file->getPathname()), ); @@ -313,7 +281,6 @@ final class ImportDocumentationCommand extends Command if ($body === false) { throw new \RuntimeException( - sprintf('Unable to read documentation file: %s', $realPath), ); @@ -360,7 +327,7 @@ final class ImportDocumentationCommand extends Command 'status' => DocumentationDocument::STATUS_PUBLISHED, - 'is_featured' => strtolower($file->getBasename('.' . $file->getExtension())) === 'index', + 'is_featured' => strtolower($file->getBasename('.'.$file->getExtension())) === 'index', 'version' => '1.0.0', @@ -377,13 +344,9 @@ final class ImportDocumentationCommand extends Command } /** - * Extract the first Markdown H1 heading. - */ - private function extractTitle(string $body, SplFileInfo $file): string - { if (preg_match('/^\s*#\s+(.+?)\s*$/m', $body, $matches) === 1) { @@ -417,13 +380,9 @@ final class ImportDocumentationCommand extends Command } /** - * Extract a useful summary from the first normal Markdown paragraph. - */ - private function extractSummary(string $body): ?string - { $lines = preg_split('/\R/', $body); @@ -507,13 +466,9 @@ final class ImportDocumentationCommand extends Command } /** - * Generate a stable globally unique slug from the source path. - */ - private function makeSlug(string $relativePath, string $title): string - { $withoutExtension = preg_replace( @@ -536,7 +491,7 @@ final class ImportDocumentationCommand extends Command if ($slug === '') { - $slug = 'document-' . substr(sha1($relativePath), 0, 12); + $slug = 'document-'.substr(sha1($relativePath), 0, 12); } @@ -545,13 +500,9 @@ final class ImportDocumentationCommand extends Command } /** - * Remove a UTF-8 byte-order mark. - */ - private function stripBom(string $contents): string - { if (str_starts_with($contents, "\xEF\xBB\xBF")) { @@ -563,6 +514,4 @@ final class ImportDocumentationCommand extends Command return $contents; } - } - diff --git a/Modules/Documentation/Http/Controllers/DocumentationController.php b/Modules/Documentation/Http/Controllers/DocumentationController.php index c2f88c5..c07a234 100755 --- a/Modules/Documentation/Http/Controllers/DocumentationController.php +++ b/Modules/Documentation/Http/Controllers/DocumentationController.php @@ -31,7 +31,7 @@ class DocumentationController extends Controller } if ($request->filled('search')) { - $search = '%' . $request->string('search')->trim()->toString() . '%'; + $search = '%'.$request->string('search')->trim()->toString().'%'; $query->where(function ($builder) use ($search): void { $builder->where('title', 'like', $search) ->orWhere('summary', 'like', $search) diff --git a/Modules/Documentation/Http/Requests/StoreDocumentationDocumentRequest.php b/Modules/Documentation/Http/Requests/StoreDocumentationDocumentRequest.php index b2931ec..9c8c3a7 100755 --- a/Modules/Documentation/Http/Requests/StoreDocumentationDocumentRequest.php +++ b/Modules/Documentation/Http/Requests/StoreDocumentationDocumentRequest.php @@ -33,9 +33,9 @@ class StoreDocumentationDocumentRequest extends FormRequest 'is_featured' => ['nullable', 'boolean'], 'version' => ['required', 'string', 'max:50'], 'reference' => ['nullable', 'string', 'max:100'], - 'attachment' => ['nullable', 'file', 'max:' . config('documentation.max_upload_kb', 20480), 'mimes:' . implode(',', config('documentation.allowed_upload_mimes', []))], + 'attachment' => ['nullable', 'file', 'max:'.config('documentation.max_upload_kb', 20480), 'mimes:'.implode(',', config('documentation.allowed_upload_mimes', []))], 'attachments' => ['nullable', 'array'], - 'attachments.*' => ['file', 'max:' . config('documentation.max_upload_kb', 20480), 'mimes:' . implode(',', config('documentation.allowed_upload_mimes', []))], + 'attachments.*' => ['file', 'max:'.config('documentation.max_upload_kb', 20480), 'mimes:'.implode(',', config('documentation.allowed_upload_mimes', []))], ]; } } diff --git a/Modules/Documentation/Http/Requests/UpdateDocumentationDocumentRequest.php b/Modules/Documentation/Http/Requests/UpdateDocumentationDocumentRequest.php index 6d64014..9622e99 100755 --- a/Modules/Documentation/Http/Requests/UpdateDocumentationDocumentRequest.php +++ b/Modules/Documentation/Http/Requests/UpdateDocumentationDocumentRequest.php @@ -36,9 +36,9 @@ class UpdateDocumentationDocumentRequest extends FormRequest 'is_featured' => ['nullable', 'boolean'], 'version' => ['required', 'string', 'max:50'], 'reference' => ['nullable', 'string', 'max:100'], - 'attachment' => ['nullable', 'file', 'max:' . config('documentation.max_upload_kb', 20480), 'mimes:' . implode(',', config('documentation.allowed_upload_mimes', []))], + 'attachment' => ['nullable', 'file', 'max:'.config('documentation.max_upload_kb', 20480), 'mimes:'.implode(',', config('documentation.allowed_upload_mimes', []))], 'attachments' => ['nullable', 'array'], - 'attachments.*' => ['file', 'max:' . config('documentation.max_upload_kb', 20480), 'mimes:' . implode(',', config('documentation.allowed_upload_mimes', []))], + 'attachments.*' => ['file', 'max:'.config('documentation.max_upload_kb', 20480), 'mimes:'.implode(',', config('documentation.allowed_upload_mimes', []))], 'remove_attachment' => ['nullable', 'boolean'], ]; } diff --git a/Modules/Documentation/Models/DocumentationAttachment.php b/Modules/Documentation/Models/DocumentationAttachment.php index 9722f36..971840d 100755 --- a/Modules/Documentation/Models/DocumentationAttachment.php +++ b/Modules/Documentation/Models/DocumentationAttachment.php @@ -7,13 +7,13 @@ use Illuminate\Database\Eloquent\Model; use Illuminate\Database\Eloquent\Relations\BelongsTo; /** - * @property int $id - * @property int $documentation_document_id - * @property string $disk - * @property string $path - * @property string $original_name + * @property int $id + * @property int $documentation_document_id + * @property string $disk + * @property string $path + * @property string $original_name * @property string|null $mime_type - * @property int|null $size + * @property int|null $size */ class DocumentationAttachment extends Model { @@ -57,13 +57,13 @@ class DocumentationAttachment extends Model } if ($bytes < 1024) { - return $bytes . ' B'; + return $bytes.' B'; } if ($bytes < 1048576) { - return number_format($bytes / 1024, 1) . ' KB'; + return number_format($bytes / 1024, 1).' KB'; } - return number_format($bytes / 1048576, 1) . ' MB'; + return number_format($bytes / 1048576, 1).' MB'; } } diff --git a/Modules/Documentation/Models/DocumentationCategory.php b/Modules/Documentation/Models/DocumentationCategory.php index 0e87de1..f474cec 100755 --- a/Modules/Documentation/Models/DocumentationCategory.php +++ b/Modules/Documentation/Models/DocumentationCategory.php @@ -9,14 +9,14 @@ use Illuminate\Database\Eloquent\Relations\HasMany; use Illuminate\Support\Str; /** - * @property int $id - * @property string $name - * @property string $slug + * @property int $id + * @property string $name + * @property string $slug * @property string|null $icon - * @property string $colour + * @property string $colour * @property string|null $description - * @property int $sort_order - * @property bool $is_active + * @property int $sort_order + * @property bool $is_active */ class DocumentationCategory extends Model { @@ -66,8 +66,7 @@ class DocumentationCategory extends Model /** * Active category scope. * - * @param Builder $query - * + * @param Builder $query * @return Builder */ public function scopeActive(Builder $query): Builder @@ -78,8 +77,7 @@ class DocumentationCategory extends Model /** * Ordered category scope. * - * @param Builder $query - * + * @param Builder $query * @return Builder */ public function scopeOrdered(Builder $query): Builder diff --git a/Modules/Documentation/Models/DocumentationDocument.php b/Modules/Documentation/Models/DocumentationDocument.php index edb0f32..f5cf941 100755 --- a/Modules/Documentation/Models/DocumentationDocument.php +++ b/Modules/Documentation/Models/DocumentationDocument.php @@ -11,15 +11,15 @@ use Illuminate\Database\Eloquent\SoftDeletes; use Illuminate\Support\Str; /** - * @property int $id - * @property int|null $documentation_category_id - * @property string $title - * @property string $slug + * @property int $id + * @property int|null $documentation_category_id + * @property string $title + * @property string $slug * @property string|null $summary * @property string|null $body - * @property string $status - * @property bool $is_featured - * @property string $version + * @property string $status + * @property bool $is_featured + * @property string $version * @property string|null $reference * @property-read string $rendered_body */ @@ -101,8 +101,7 @@ class DocumentationDocument extends Model /** * Published document scope. * - * @param Builder $query - * + * @param Builder $query * @return Builder */ public function scopePublished(Builder $query): Builder @@ -113,8 +112,7 @@ class DocumentationDocument extends Model /** * Featured document scope. * - * @param Builder $query - * + * @param Builder $query * @return Builder */ public function scopeFeatured(Builder $query): Builder @@ -140,7 +138,7 @@ class DocumentationDocument extends Model ]); } - return '

' . nl2br(e($body), false) . '

'; + return '

'.nl2br(e($body), false).'

'; } /** diff --git a/Modules/Documentation/Providers/DocumentationServiceProvider.php b/Modules/Documentation/Providers/DocumentationServiceProvider.php index 8176d0b..9f92941 100755 --- a/Modules/Documentation/Providers/DocumentationServiceProvider.php +++ b/Modules/Documentation/Providers/DocumentationServiceProvider.php @@ -7,6 +7,7 @@ namespace Modules\Documentation\Providers; use Illuminate\Support\Facades\Blade; use Illuminate\Support\Facades\Config; use Illuminate\Support\ServiceProvider; +use Modules\Documentation\Console\ImportDocumentationCommand; final class DocumentationServiceProvider extends ServiceProvider { @@ -43,7 +44,7 @@ final class DocumentationServiceProvider extends ServiceProvider } $this->commands([ - \Modules\Documentation\Console\ImportDocumentationCommand::class, + ImportDocumentationCommand::class, ]); } @@ -62,12 +63,12 @@ final class DocumentationServiceProvider extends ServiceProvider $paths = [$sourcePath]; foreach (Config::get('view.paths', []) as $path) { - if (is_dir($path . '/modules/documentation')) { - array_unshift($paths, $path . '/modules/documentation'); + if (is_dir($path.'/modules/documentation')) { + array_unshift($paths, $path.'/modules/documentation'); } } $this->loadViewsFrom($paths, 'documentation'); - Blade::anonymousComponentPath($sourcePath . '/components', 'documentation'); + Blade::anonymousComponentPath($sourcePath.'/components', 'documentation'); } } diff --git a/Modules/Documentation/Tests/Feature/DocumentationArchiveTest.php b/Modules/Documentation/Tests/Feature/DocumentationArchiveTest.php index 460c3eb..c276c62 100755 --- a/Modules/Documentation/Tests/Feature/DocumentationArchiveTest.php +++ b/Modules/Documentation/Tests/Feature/DocumentationArchiveTest.php @@ -7,7 +7,6 @@ namespace Modules\Documentation\Tests\Feature; use App\Models\User; use Illuminate\Foundation\Testing\RefreshDatabase; use Illuminate\Support\Facades\Storage; -use Modules\Documentation\Models\DocumentationAttachment; use Modules\Documentation\Models\DocumentationDocument; use Tests\TestCase; diff --git a/Modules/Documentation/Tests/Feature/DocumentationModuleTest.php b/Modules/Documentation/Tests/Feature/DocumentationModuleTest.php index 66dd66c..a1b0ccd 100755 --- a/Modules/Documentation/Tests/Feature/DocumentationModuleTest.php +++ b/Modules/Documentation/Tests/Feature/DocumentationModuleTest.php @@ -17,8 +17,7 @@ class DocumentationModuleTest extends TestCase /** * Add a CSRF token for projects that keep web middleware enabled during tests. * - * @param array $payload - * + * @param array $payload * @return array */ private function csrfPayload(array $payload): array diff --git a/Modules/Helpdesk/Console/ImportLegacyHelpdeskCommand.php b/Modules/Helpdesk/Console/ImportLegacyHelpdeskCommand.php index 9555bbd..cdce59d 100644 --- a/Modules/Helpdesk/Console/ImportLegacyHelpdeskCommand.php +++ b/Modules/Helpdesk/Console/ImportLegacyHelpdeskCommand.php @@ -16,6 +16,7 @@ use Throwable; final class ImportLegacyHelpdeskCommand extends Command { protected $signature = 'helpdesk:import-legacy {--connection= : Laravel database connection containing the old tickets tables} {--fresh : Clear previously imported legacy records first}'; + protected $description = 'Import the old Helpdesk tickets and categories into the read-only Legacy System'; public function handle(): int @@ -33,6 +34,7 @@ final class ImportLegacyHelpdeskCommand extends Command foreach (['tickets', 'tickets_categories'] as $table) { if (! $schema->hasTable($table)) { $this->error("Source table {$table} was not found on connection [{$connectionName}]."); + return self::FAILURE; } } @@ -49,6 +51,7 @@ final class ImportLegacyHelpdeskCommand extends Command $this->refreshCategoryCounts(); $this->info(sprintf('Legacy import complete: %d tickets, %d categories, %d comments.', LegacyTicket::count(), LegacyCategory::count(), LegacyComment::count())); + return self::SUCCESS; } @@ -72,7 +75,7 @@ final class ImportLegacyHelpdeskCommand extends Command 'user' => $this->lookup($source, $schema, 'users'), ]; - $source->table('tickets')->orderBy('id')->chunk(250, function ($rows) use ($columns, $lookups): void { + $source->table('tickets')->orderBy('id')->chunk(250, function ($rows) use ($lookups): void { foreach ($rows as $row) { $data = (array) $row; LegacyTicket::updateOrCreate(['legacy_id' => $row->id], [ @@ -99,7 +102,9 @@ final class ImportLegacyHelpdeskCommand extends Command private function importComments(ConnectionInterface $source, $schema): void { - if (! $schema->hasTable('tickets_comments')) return; + if (! $schema->hasTable('tickets_comments')) { + return; + } $users = $this->lookup($source, $schema, 'users'); $source->table('tickets_comments')->orderBy('id')->chunk(500, function ($rows) use ($users): void { foreach ($rows as $row) { @@ -118,11 +123,19 @@ final class ImportLegacyHelpdeskCommand extends Command private function lookup(ConnectionInterface $source, $schema, string $table): array { - if (! $schema->hasTable($table)) return []; + if (! $schema->hasTable($table)) { + return []; + } $columns = $schema->getColumnListing($table); $nameColumn = in_array('name', $columns, true) ? 'name' : (in_array('username', $columns, true) ? 'username' : null); - if ($nameColumn === null) return []; - try { return $source->table($table)->pluck($nameColumn, 'id')->map(fn ($v) => (string) $v)->all(); } catch (Throwable) { return []; } + if ($nameColumn === null) { + return []; + } + try { + return $source->table($table)->pluck($nameColumn, 'id')->map(fn ($v) => (string) $v)->all(); + } catch (Throwable) { + return []; + } } private function refreshCategoryCounts(): void diff --git a/Modules/Helpdesk/Database/Migrations/2026_09_28_100000_create_helpdesk_legacy_tables.php b/Modules/Helpdesk/Database/Migrations/2026_09_28_100000_create_helpdesk_legacy_tables.php index f25f05d..62a9366 100644 --- a/Modules/Helpdesk/Database/Migrations/2026_09_28_100000_create_helpdesk_legacy_tables.php +++ b/Modules/Helpdesk/Database/Migrations/2026_09_28_100000_create_helpdesk_legacy_tables.php @@ -6,7 +6,8 @@ use Illuminate\Database\Migrations\Migration; use Illuminate\Database\Schema\Blueprint; use Illuminate\Support\Facades\Schema; -return new class extends Migration { +return new class extends Migration +{ public function up(): void { Schema::create('helpdesk_legacy_categories', function (Blueprint $table): void { diff --git a/Modules/Helpdesk/Http/Controllers/LegacySystemController.php b/Modules/Helpdesk/Http/Controllers/LegacySystemController.php index b3986a5..771dfd2 100644 --- a/Modules/Helpdesk/Http/Controllers/LegacySystemController.php +++ b/Modules/Helpdesk/Http/Controllers/LegacySystemController.php @@ -39,6 +39,7 @@ final class LegacySystemController public function show(LegacyTicket $ticket): View { $ticket->load('comments'); + return view('helpdesk::legacy.show', compact('ticket')); } diff --git a/Modules/Helpdesk/Livewire/ArchivedTicketIndex.php b/Modules/Helpdesk/Livewire/ArchivedTicketIndex.php index 04ec3d2..1367f5a 100755 --- a/Modules/Helpdesk/Livewire/ArchivedTicketIndex.php +++ b/Modules/Helpdesk/Livewire/ArchivedTicketIndex.php @@ -26,8 +26,11 @@ final class ArchivedTicketIndex extends Component public string $search = ''; public bool $deleteModalOpen = false; + public ?int $pendingDeleteTicketId = null; + public ?string $pendingDeleteTicketReference = null; + public ?string $pendingDeleteTicketSubject = null; public function updatedSearch(): void @@ -75,6 +78,7 @@ final class ArchivedTicketIndex extends Component { if ($this->pendingDeleteTicketId === null) { $this->resetDeleteState(); + return; } diff --git a/Modules/Helpdesk/Livewire/HelpdeskDashboardWidget.php b/Modules/Helpdesk/Livewire/HelpdeskDashboardWidget.php index ff25975..45dca42 100755 --- a/Modules/Helpdesk/Livewire/HelpdeskDashboardWidget.php +++ b/Modules/Helpdesk/Livewire/HelpdeskDashboardWidget.php @@ -17,7 +17,9 @@ final class HelpdeskDashboardWidget extends Component public function updatedDays(): void { - if (! in_array($this->days, [7, 14, 30], true)) $this->days = 7; + if (! in_array($this->days, [7, 14, 30], true)) { + $this->days = 7; + } } public function render(): View @@ -33,6 +35,7 @@ final class HelpdeskDashboardWidget extends Component $trendRows = (clone $base)->where('created_at', '>=', now()->subDays($this->days - 1)->startOfDay())->get(['created_at', 'completed_at']); $trend = collect($period)->map(function ($date) use ($trendRows): array { $day = $date->toDateString(); + return [ 'label' => $date->format('M j'), 'created' => $trendRows->filter(fn ($ticket) => optional($ticket->created_at)->toDateString() === $day)->count(), diff --git a/Modules/Helpdesk/Livewire/SettingsManager.php b/Modules/Helpdesk/Livewire/SettingsManager.php index a2237a0..78c9bd0 100755 --- a/Modules/Helpdesk/Livewire/SettingsManager.php +++ b/Modules/Helpdesk/Livewire/SettingsManager.php @@ -39,17 +39,29 @@ final class SettingsManager extends Component 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 */ @@ -1041,57 +1053,57 @@ final class SettingsManager extends Component return view()->file( resource_path('themes/backend/views/modules/helpdesk/livewire/settings-manager.blade.php'), [ - 'statuses' => $this->orderedStatuses(), - 'priorities' => $this->orderedPriorities(), - 'categories' => $this->orderedCategories(), - 'teams' => $this->orderedTeams(), - 'teamUsers' => User::query() - ->orderBy('name') - ->get(['id', 'name', 'email', 'helpdesk_agent', 'helpdesk_admin']), - 'projects' => $this->orderedProjects(), - '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') - ->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(), + 'statuses' => $this->orderedStatuses(), + 'priorities' => $this->orderedPriorities(), + 'categories' => $this->orderedCategories(), + 'teams' => $this->orderedTeams(), + 'teamUsers' => User::query() + ->orderBy('name') + ->get(['id', 'name', 'email', 'helpdesk_agent', 'helpdesk_admin']), + 'projects' => $this->orderedProjects(), + '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') + ->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(), ] ); } diff --git a/Modules/Helpdesk/Models/LegacyCategory.php b/Modules/Helpdesk/Models/LegacyCategory.php index 103f5bd..e20e300 100644 --- a/Modules/Helpdesk/Models/LegacyCategory.php +++ b/Modules/Helpdesk/Models/LegacyCategory.php @@ -9,5 +9,6 @@ use Illuminate\Database\Eloquent\Model; final class LegacyCategory extends Model { protected $table = 'helpdesk_legacy_categories'; + protected $guarded = []; } diff --git a/Modules/Helpdesk/Models/LegacyComment.php b/Modules/Helpdesk/Models/LegacyComment.php index dbb7df7..19878b7 100644 --- a/Modules/Helpdesk/Models/LegacyComment.php +++ b/Modules/Helpdesk/Models/LegacyComment.php @@ -9,6 +9,8 @@ use Illuminate\Database\Eloquent\Model; final class LegacyComment extends Model { protected $table = 'helpdesk_legacy_comments'; + protected $guarded = []; + protected $casts = ['legacy_created_at' => 'datetime', 'legacy_updated_at' => 'datetime']; } diff --git a/Modules/Helpdesk/Models/LegacyTicket.php b/Modules/Helpdesk/Models/LegacyTicket.php index cdf5b7c..bad47c4 100644 --- a/Modules/Helpdesk/Models/LegacyTicket.php +++ b/Modules/Helpdesk/Models/LegacyTicket.php @@ -10,7 +10,9 @@ use Illuminate\Database\Eloquent\Relations\HasMany; final class LegacyTicket extends Model { protected $table = 'helpdesk_legacy_tickets'; + protected $guarded = []; + protected $casts = ['legacy_data' => 'array', 'legacy_created_at' => 'datetime', 'legacy_updated_at' => 'datetime', 'completed_at' => 'datetime', 'archived_at' => 'datetime']; public function comments(): HasMany diff --git a/Modules/Helpdesk/Models/Team.php b/Modules/Helpdesk/Models/Team.php index c7a7712..6f30f9a 100755 --- a/Modules/Helpdesk/Models/Team.php +++ b/Modules/Helpdesk/Models/Team.php @@ -5,7 +5,6 @@ declare(strict_types=1); namespace Modules\Helpdesk\Models; use App\Models\User; - use Illuminate\Database\Eloquent\Factories\HasFactory; use Illuminate\Database\Eloquent\Model; use Illuminate\Database\Eloquent\Relations\BelongsToMany; diff --git a/Modules/Helpdesk/Models/Ticket.php b/Modules/Helpdesk/Models/Ticket.php index 6fcd183..8262ff8 100755 --- a/Modules/Helpdesk/Models/Ticket.php +++ b/Modules/Helpdesk/Models/Ticket.php @@ -62,7 +62,6 @@ final class Ticket extends Model return $this->belongsTo(User::class, 'user_id'); } - /** * Compatibility alias for the requester. * diff --git a/Modules/Helpdesk/Services/HelpdeskSyncService.php b/Modules/Helpdesk/Services/HelpdeskSyncService.php index 6cdb0e2..758c8bb 100755 --- a/Modules/Helpdesk/Services/HelpdeskSyncService.php +++ b/Modules/Helpdesk/Services/HelpdeskSyncService.php @@ -11,7 +11,6 @@ use Illuminate\Support\Facades\Schema; use Illuminate\Support\Facades\Storage; use Illuminate\Support\Str; use RuntimeException; -use Throwable; final class HelpdeskSyncService { @@ -262,7 +261,7 @@ final class HelpdeskSyncService } /** - * @param list $tables + * @param list $tables * @return list */ private function existingTables(array $tables): array @@ -293,7 +292,7 @@ final class HelpdeskSyncService } /** - * @param array>> $tables + * @param array>> $tables * @return list> */ private function exportUsers(array $tables): array @@ -357,7 +356,7 @@ final class HelpdeskSyncService } /** - * @param list> $users + * @param list> $users * @return array */ private function importUsers(array $users): array @@ -436,8 +435,8 @@ final class HelpdeskSyncService } /** - * @param list> $rows - * @param array $userMap + * @param list> $rows + * @param array $userMap */ private function importTable(string $table, array $rows, array $userMap, bool $mirror): void { @@ -481,8 +480,8 @@ final class HelpdeskSyncService } /** - * @param array $row - * @param array $userMap + * @param array $row + * @param array $userMap * @return array */ private function remapUserColumns(array $row, array $userMap): array @@ -503,7 +502,7 @@ final class HelpdeskSyncService } /** - * @param array $row + * @param array $row * @return array */ private function compositeKey(string $table, array $row): array @@ -533,7 +532,7 @@ final class HelpdeskSyncService } /** - * @param list> $attachments + * @param list> $attachments * @return list> */ private function exportAttachmentFiles(array $attachments): array @@ -569,7 +568,7 @@ final class HelpdeskSyncService } /** - * @param list> $files + * @param list> $files */ private function importAttachmentFiles(array $files): int { diff --git a/Modules/UserManagement/Http/Controllers/UserController.php b/Modules/UserManagement/Http/Controllers/UserController.php index 12c49f0..1d977b5 100755 --- a/Modules/UserManagement/Http/Controllers/UserController.php +++ b/Modules/UserManagement/Http/Controllers/UserController.php @@ -120,7 +120,7 @@ final class UserController extends Controller public function createRole(): View { return view('modules.user-management.role-form', [ - 'role' => new Role(), + 'role' => new Role, 'permissions' => Permission::query()->orderBy('name')->get(), ]); } @@ -165,7 +165,7 @@ final class UserController extends Controller public function createPermission(): View { - return view('modules.user-management.permission-form', ['permission' => new Permission()]); + return view('modules.user-management.permission-form', ['permission' => new Permission]); } public function storePermission(Request $request): RedirectResponse diff --git a/app/Livewire/Settings/Appearance.php b/app/Livewire/Settings/Appearance.php index 4a612d8..8b3f6b1 100644 --- a/app/Livewire/Settings/Appearance.php +++ b/app/Livewire/Settings/Appearance.php @@ -18,5 +18,4 @@ class Appearance extends Component resource_path('themes/backend/views/livewire/settings/appearance.blade.php') ); } - } diff --git a/app/Livewire/Settings/DeleteUserForm.php b/app/Livewire/Settings/DeleteUserForm.php index 66967a8..1121671 100644 --- a/app/Livewire/Settings/DeleteUserForm.php +++ b/app/Livewire/Settings/DeleteUserForm.php @@ -2,9 +2,9 @@ namespace App\Livewire\Settings; -use Illuminate\Contracts\View\View; use App\Concerns\PasswordValidationRules; use App\Livewire\Actions\Logout; +use Illuminate\Contracts\View\View; use Illuminate\Support\Facades\Auth; use Livewire\Component; @@ -27,11 +27,11 @@ class DeleteUserForm extends Component $this->redirect('/', navigate: true); } + public function render(): View { return view()->file( resource_path('themes/backend/views/livewire/settings/delete-user-form.blade.php') ); } - } diff --git a/app/Livewire/Settings/Profile.php b/app/Livewire/Settings/Profile.php index 5366dee..099b40d 100644 --- a/app/Livewire/Settings/Profile.php +++ b/app/Livewire/Settings/Profile.php @@ -2,13 +2,13 @@ namespace App\Livewire\Settings; -use Illuminate\Contracts\View\View; use App\Concerns\ProfileValidationRules; use Flux\Flux; use Illuminate\Contracts\Auth\MustVerifyEmail; +use Illuminate\Contracts\View\View; use Illuminate\Support\Facades\Auth; -use Livewire\Attributes\Layout; use Livewire\Attributes\Computed; +use Livewire\Attributes\Layout; use Livewire\Attributes\Title; use Livewire\Component; @@ -83,6 +83,7 @@ class Profile extends Component return ! $user instanceof MustVerifyEmail || $user->hasVerifiedEmail(); } + #[Layout('layouts.app')] public function render(): View { @@ -90,5 +91,4 @@ class Profile extends Component resource_path('themes/backend/views/livewire/settings/profile.blade.php') ); } - } diff --git a/app/Livewire/Settings/Security.php b/app/Livewire/Settings/Security.php index ffef75c..8ec864a 100644 --- a/app/Livewire/Settings/Security.php +++ b/app/Livewire/Settings/Security.php @@ -2,10 +2,10 @@ namespace App\Livewire\Settings; -use Illuminate\Contracts\View\View; use App\Concerns\PasswordValidationRules; use Exception; use Flux\Flux; +use Illuminate\Contracts\View\View; use Illuminate\Support\Facades\Auth; use Illuminate\Validation\ValidationException; use Laravel\Fortify\Actions\ConfirmTwoFactorAuthentication; @@ -14,8 +14,8 @@ use Laravel\Fortify\Actions\EnableTwoFactorAuthentication; use Laravel\Fortify\Features; use Laravel\Fortify\Fortify; use Laravel\Passkeys\Actions\DeletePasskey; -use Livewire\Attributes\Layout; use Livewire\Attributes\Computed; +use Livewire\Attributes\Layout; use Livewire\Attributes\Locked; use Livewire\Attributes\Title; use Livewire\Attributes\Validate; @@ -311,6 +311,7 @@ class Security extends Component 'buttonText' => __('Continue'), ]; } + #[Layout('layouts.app')] public function render(): View { @@ -318,5 +319,4 @@ class Security extends Component resource_path('themes/backend/views/livewire/settings/security.blade.php') ); } - } diff --git a/app/Livewire/Settings/TwoFactor/RecoveryCodes.php b/app/Livewire/Settings/TwoFactor/RecoveryCodes.php index cbafe2e..662f6e0 100644 --- a/app/Livewire/Settings/TwoFactor/RecoveryCodes.php +++ b/app/Livewire/Settings/TwoFactor/RecoveryCodes.php @@ -2,8 +2,8 @@ namespace App\Livewire\Settings\TwoFactor; -use Illuminate\Contracts\View\View; use Exception; +use Illuminate\Contracts\View\View; use Laravel\Fortify\Actions\GenerateNewRecoveryCodes; use Livewire\Attributes\Locked; use Livewire\Component; @@ -49,11 +49,11 @@ class RecoveryCodes extends Component } } } + public function render(): View { return view()->file( resource_path('themes/backend/views/livewire/settings/two-factor/recovery-codes.blade.php') ); } - } diff --git a/app/Models/User.php b/app/Models/User.php index 18e840d..320acaf 100644 --- a/app/Models/User.php +++ b/app/Models/User.php @@ -6,6 +6,7 @@ use Database\Factories\UserFactory; use Illuminate\Database\Eloquent\Attributes\Fillable; use Illuminate\Database\Eloquent\Attributes\Hidden; use Illuminate\Database\Eloquent\Factories\HasFactory; +use Illuminate\Database\Eloquent\Relations\BelongsToMany; use Illuminate\Foundation\Auth\User as Authenticatable; use Illuminate\Notifications\Notifiable; use Illuminate\Support\Carbon; @@ -14,7 +15,6 @@ use Laravel\Fortify\Contracts\PasskeyUser; use Laravel\Fortify\PasskeyAuthenticatable; use Laravel\Fortify\TwoFactorAuthenticatable; use Modules\UserManagement\Models\Role; -use Illuminate\Database\Eloquent\Relations\BelongsToMany; /** * @property int $id @@ -50,8 +50,6 @@ class User extends Authenticatable implements PasskeyUser : $initials; } - - public function gravatarUrl(int $size = 160): string { $email = strtolower(trim((string) $this->email)); diff --git a/deploy.php b/deploy.php index 6d9fa8f..f00824c 100644 --- a/deploy.php +++ b/deploy.php @@ -34,7 +34,6 @@ host('mpba') ->set('branch', 'main') ->set('deploy_path', '/var/www/helpdesk_mpba'); - /* * Install the root npm tree, then every module that owns its own lock file, * then run ONE root Vite build. Because Vite is compiling a module source file,