diff --git a/Modules/Helpdesk/Config/config.php b/Modules/Helpdesk/Config/config.php
index 9868548..a0d0bb4 100755
--- a/Modules/Helpdesk/Config/config.php
+++ b/Modules/Helpdesk/Config/config.php
@@ -5,6 +5,7 @@ declare(strict_types=1);
return [
'route_prefix' => env('HELPDESK_ROUTE_PREFIX', 'admin/helpdesk'),
'middleware' => ['web', 'auth'],
+ 'legacy_connection' => env('HELPDESK_LEGACY_DB_CONNECTION', config('database.default')),
'disk' => env('HELPDESK_ATTACHMENTS_DISK', 'local'),
'max_upload_kb' => (int) env('HELPDESK_MAX_UPLOAD_KB', 10240),
diff --git a/Modules/Helpdesk/Console/ImportLegacyHelpdeskCommand.php b/Modules/Helpdesk/Console/ImportLegacyHelpdeskCommand.php
new file mode 100644
index 0000000..f5aba7f
--- /dev/null
+++ b/Modules/Helpdesk/Console/ImportLegacyHelpdeskCommand.php
@@ -0,0 +1,128 @@
+option('connection') ?: config('helpdesk.legacy_connection', config('database.default')));
+ $source = DB::connection($connectionName);
+ $schema = Schema::connection($connectionName);
+
+ foreach (['tickets', 'tickets_categories'] as $table) {
+ if (! $schema->hasTable($table)) {
+ $this->error("Source table {$table} was not found on connection [{$connectionName}].");
+ return self::FAILURE;
+ }
+ }
+
+ if ($this->option('fresh')) {
+ LegacyComment::query()->delete();
+ LegacyTicket::query()->delete();
+ LegacyCategory::query()->delete();
+ }
+
+ $this->importCategories($source);
+ $this->importTickets($source, $schema);
+ $this->importComments($source, $schema);
+ $this->refreshCategoryCounts();
+
+ $this->info(sprintf('Legacy import complete: %d tickets, %d categories, %d comments.', LegacyTicket::count(), LegacyCategory::count(), LegacyComment::count()));
+ return self::SUCCESS;
+ }
+
+ private function importCategories(ConnectionInterface $source): void
+ {
+ $source->table('tickets_categories')->orderBy('id')->chunk(500, function ($rows): void {
+ foreach ($rows as $row) {
+ LegacyCategory::updateOrCreate(['legacy_id' => $row->id], ['name' => (string) $row->name, 'color' => $row->color ?? null]);
+ }
+ });
+ }
+
+ private function importTickets(ConnectionInterface $source, $schema): void
+ {
+ $columns = collect($schema->getColumnListing('tickets'));
+ $lookups = [
+ 'category' => $this->lookup($source, $schema, 'tickets_categories'),
+ 'status' => $this->lookup($source, $schema, 'tickets_statuses'),
+ 'priority' => $this->lookup($source, $schema, 'tickets_priorities'),
+ 'project' => $this->lookup($source, $schema, 'projects'),
+ 'user' => $this->lookup($source, $schema, 'users'),
+ ];
+
+ $source->table('tickets')->orderBy('id')->chunk(250, function ($rows) use ($columns, $lookups): void {
+ foreach ($rows as $row) {
+ $data = (array) $row;
+ LegacyTicket::updateOrCreate(['legacy_id' => $row->id], [
+ 'reference' => $data['reference'] ?? null,
+ 'subject' => (string) ($data['subject'] ?? 'Legacy ticket #'.$row->id),
+ 'content' => $data['content'] ?? null,
+ 'html_content' => $data['htmlcontent'] ?? null,
+ 'legacy_category_id' => $data['category_id'] ?? null,
+ 'category_name' => $lookups['category'][$data['category_id'] ?? 0] ?? null,
+ 'status_name' => $lookups['status'][$data['status_id'] ?? 0] ?? null,
+ 'priority_name' => $lookups['priority'][$data['priority_id'] ?? 0] ?? null,
+ 'requester_name' => $lookups['user'][$data['user_id'] ?? 0] ?? null,
+ 'agent_name' => $lookups['user'][$data['agent_id'] ?? 0] ?? null,
+ 'project_name' => $lookups['project'][$data['project_id'] ?? 0] ?? null,
+ 'legacy_created_at' => $data['created_at'] ?? null,
+ 'legacy_updated_at' => $data['updated_at'] ?? null,
+ 'completed_at' => $data['completed_at'] ?? null,
+ 'archived_at' => $data['archived_at'] ?? null,
+ 'legacy_data' => $data,
+ ]);
+ }
+ });
+ }
+
+ private function importComments(ConnectionInterface $source, $schema): void
+ {
+ 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) {
+ $data = (array) $row;
+ LegacyComment::updateOrCreate(['legacy_id' => $row->id], [
+ 'legacy_ticket_id' => $data['ticket_id'],
+ 'author_name' => $users[$data['user_id'] ?? 0] ?? null,
+ 'content' => $data['content'] ?? null,
+ 'html_content' => $data['htmlcontent'] ?? null,
+ 'legacy_created_at' => $data['created_at'] ?? null,
+ 'legacy_updated_at' => $data['updated_at'] ?? null,
+ ]);
+ }
+ });
+ }
+
+ private function lookup(ConnectionInterface $source, $schema, string $table): array
+ {
+ 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 []; }
+ }
+
+ private function refreshCategoryCounts(): void
+ {
+ LegacyCategory::query()->each(function (LegacyCategory $category): void {
+ $category->update(['ticket_count' => LegacyTicket::query()->where('legacy_category_id', $category->legacy_id)->count()]);
+ });
+ }
+}
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
new file mode 100644
index 0000000..f25f05d
--- /dev/null
+++ b/Modules/Helpdesk/Database/Migrations/2026_09_28_100000_create_helpdesk_legacy_tables.php
@@ -0,0 +1,62 @@
+id();
+ $table->unsignedBigInteger('legacy_id')->unique();
+ $table->string('name');
+ $table->string('color', 32)->nullable();
+ $table->unsignedInteger('ticket_count')->default(0);
+ $table->timestamps();
+ });
+
+ Schema::create('helpdesk_legacy_tickets', function (Blueprint $table): void {
+ $table->id();
+ $table->unsignedBigInteger('legacy_id')->unique();
+ $table->string('reference')->nullable()->index();
+ $table->string('subject');
+ $table->longText('content')->nullable();
+ $table->longText('html_content')->nullable();
+ $table->unsignedBigInteger('legacy_category_id')->nullable()->index();
+ $table->string('category_name')->nullable();
+ $table->string('status_name')->nullable();
+ $table->string('priority_name')->nullable();
+ $table->string('requester_name')->nullable();
+ $table->string('agent_name')->nullable();
+ $table->string('project_name')->nullable();
+ $table->timestamp('legacy_created_at')->nullable()->index();
+ $table->timestamp('legacy_updated_at')->nullable();
+ $table->timestamp('completed_at')->nullable();
+ $table->timestamp('archived_at')->nullable();
+ $table->json('legacy_data')->nullable();
+ $table->timestamps();
+ });
+
+ Schema::create('helpdesk_legacy_comments', function (Blueprint $table): void {
+ $table->id();
+ $table->unsignedBigInteger('legacy_id')->unique();
+ $table->unsignedBigInteger('legacy_ticket_id')->index();
+ $table->string('author_name')->nullable();
+ $table->longText('content')->nullable();
+ $table->longText('html_content')->nullable();
+ $table->timestamp('legacy_created_at')->nullable();
+ $table->timestamp('legacy_updated_at')->nullable();
+ $table->timestamps();
+ });
+ }
+
+ public function down(): void
+ {
+ Schema::dropIfExists('helpdesk_legacy_comments');
+ Schema::dropIfExists('helpdesk_legacy_tickets');
+ Schema::dropIfExists('helpdesk_legacy_categories');
+ }
+};
diff --git a/Modules/Helpdesk/Http/Controllers/LegacySystemController.php b/Modules/Helpdesk/Http/Controllers/LegacySystemController.php
new file mode 100644
index 0000000..b3986a5
--- /dev/null
+++ b/Modules/Helpdesk/Http/Controllers/LegacySystemController.php
@@ -0,0 +1,51 @@
+query('search', ''));
+ $category = $request->integer('category');
+
+ $tickets = LegacyTicket::query()
+ ->when($search !== '', fn ($query) => $query->where(function ($q) use ($search): void {
+ $q->where('reference', 'like', "%{$search}%")
+ ->orWhere('subject', 'like', "%{$search}%")
+ ->orWhere('requester_name', 'like', "%{$search}%");
+ }))
+ ->when($category > 0, fn ($query) => $query->where('legacy_category_id', $category))
+ ->orderByDesc('legacy_created_at')
+ ->orderByDesc('legacy_id')
+ ->paginate(30)
+ ->withQueryString();
+
+ return view('helpdesk::legacy.tickets', [
+ 'tickets' => $tickets,
+ 'categories' => LegacyCategory::query()->orderBy('name')->get(),
+ 'search' => $search,
+ 'selectedCategory' => $category,
+ ]);
+ }
+
+ public function show(LegacyTicket $ticket): View
+ {
+ $ticket->load('comments');
+ return view('helpdesk::legacy.show', compact('ticket'));
+ }
+
+ public function categories(): View
+ {
+ return view('helpdesk::legacy.categories', [
+ 'categories' => LegacyCategory::query()->orderBy('name')->paginate(50),
+ ]);
+ }
+}
diff --git a/Modules/Helpdesk/Models/LegacyCategory.php b/Modules/Helpdesk/Models/LegacyCategory.php
new file mode 100644
index 0000000..103f5bd
--- /dev/null
+++ b/Modules/Helpdesk/Models/LegacyCategory.php
@@ -0,0 +1,13 @@
+ 'datetime', 'legacy_updated_at' => 'datetime'];
+}
diff --git a/Modules/Helpdesk/Models/LegacyTicket.php b/Modules/Helpdesk/Models/LegacyTicket.php
new file mode 100644
index 0000000..cdf5b7c
--- /dev/null
+++ b/Modules/Helpdesk/Models/LegacyTicket.php
@@ -0,0 +1,20 @@
+ 'array', 'legacy_created_at' => 'datetime', 'legacy_updated_at' => 'datetime', 'completed_at' => 'datetime', 'archived_at' => 'datetime'];
+
+ public function comments(): HasMany
+ {
+ return $this->hasMany(LegacyComment::class, 'legacy_ticket_id', 'legacy_id')->orderBy('legacy_created_at');
+ }
+}
diff --git a/Modules/Helpdesk/Resources/views/legacy/categories.blade.php b/Modules/Helpdesk/Resources/views/legacy/categories.blade.php
new file mode 100644
index 0000000..3c1520d
--- /dev/null
+++ b/Modules/Helpdesk/Resources/views/legacy/categories.blade.php
@@ -0,0 +1,6 @@
+ Legacy System Categories retained from the previous Helpdesk. Legacy System · Read only {{ $ticket->reference ?: 'Legacy ticket #'.$ticket->legacy_id }} Legacy System Historical tickets from the previous Helpdesk. These records are read-only.Legacy Categories
@if($categories->hasPages()) @forelse($categories as $category)Category Legacy ID Tickets @empty{{ $category->name }} {{ $category->legacy_id }} {{ $category->ticket_count }} @endforelseNo legacy categories have been imported. {{ $ticket->subject }}
Original request
Comments
Legacy Tickets
+ @forelse($tickets as $ticket)
+ Reference Subject Category Status Requester Created
+ @empty{{ $ticket->reference ?: '#'.$ticket->legacy_id }} {{ $ticket->subject }} {{ $ticket->category_name ?: '—' }} {{ $ticket->status_name ?: '—' }} {{ $ticket->requester_name ?: '—' }} {{ $ticket->legacy_created_at?->format('d M Y H:i') ?: '—' }} @endforelseNo legacy tickets have been imported.