added support for legacy tickets, fixed switching of modes and also style updates
This commit is contained in:
@@ -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
|
||||
|
||||
+2
-1
@@ -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 {
|
||||
|
||||
@@ -39,6 +39,7 @@ final class LegacySystemController
|
||||
public function show(LegacyTicket $ticket): View
|
||||
{
|
||||
$ticket->load('comments');
|
||||
|
||||
return view('helpdesk::legacy.show', compact('ticket'));
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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(),
|
||||
|
||||
@@ -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<string, mixed> */
|
||||
@@ -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(),
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
@@ -9,5 +9,6 @@ use Illuminate\Database\Eloquent\Model;
|
||||
final class LegacyCategory extends Model
|
||||
{
|
||||
protected $table = 'helpdesk_legacy_categories';
|
||||
|
||||
protected $guarded = [];
|
||||
}
|
||||
|
||||
@@ -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'];
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -62,7 +62,6 @@ final class Ticket extends Model
|
||||
return $this->belongsTo(User::class, 'user_id');
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Compatibility alias for the requester.
|
||||
*
|
||||
|
||||
@@ -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<string> $tables
|
||||
* @param list<string> $tables
|
||||
* @return list<string>
|
||||
*/
|
||||
private function existingTables(array $tables): array
|
||||
@@ -293,7 +292,7 @@ final class HelpdeskSyncService
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array<string, list<array<string, mixed>>> $tables
|
||||
* @param array<string, list<array<string, mixed>>> $tables
|
||||
* @return list<array<string, mixed>>
|
||||
*/
|
||||
private function exportUsers(array $tables): array
|
||||
@@ -357,7 +356,7 @@ final class HelpdeskSyncService
|
||||
}
|
||||
|
||||
/**
|
||||
* @param list<array<string, mixed>> $users
|
||||
* @param list<array<string, mixed>> $users
|
||||
* @return array<int, int>
|
||||
*/
|
||||
private function importUsers(array $users): array
|
||||
@@ -436,8 +435,8 @@ final class HelpdeskSyncService
|
||||
}
|
||||
|
||||
/**
|
||||
* @param list<array<string, mixed>> $rows
|
||||
* @param array<int, int> $userMap
|
||||
* @param list<array<string, mixed>> $rows
|
||||
* @param array<int, int> $userMap
|
||||
*/
|
||||
private function importTable(string $table, array $rows, array $userMap, bool $mirror): void
|
||||
{
|
||||
@@ -481,8 +480,8 @@ final class HelpdeskSyncService
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array<string, mixed> $row
|
||||
* @param array<int, int> $userMap
|
||||
* @param array<string, mixed> $row
|
||||
* @param array<int, int> $userMap
|
||||
* @return array<string, mixed>
|
||||
*/
|
||||
private function remapUserColumns(array $row, array $userMap): array
|
||||
@@ -503,7 +502,7 @@ final class HelpdeskSyncService
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array<string, mixed> $row
|
||||
* @param array<string, mixed> $row
|
||||
* @return array<string, mixed>
|
||||
*/
|
||||
private function compositeKey(string $table, array $row): array
|
||||
@@ -533,7 +532,7 @@ final class HelpdeskSyncService
|
||||
}
|
||||
|
||||
/**
|
||||
* @param list<array<string, mixed>> $attachments
|
||||
* @param list<array<string, mixed>> $attachments
|
||||
* @return list<array<string, mixed>>
|
||||
*/
|
||||
private function exportAttachmentFiles(array $attachments): array
|
||||
@@ -569,7 +568,7 @@ final class HelpdeskSyncService
|
||||
}
|
||||
|
||||
/**
|
||||
* @param list<array<string, mixed>> $files
|
||||
* @param list<array<string, mixed>> $files
|
||||
*/
|
||||
private function importAttachmentFiles(array $files): int
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user