major ui updates

This commit is contained in:
Mark Gregory
2026-06-12 15:11:51 +01:00
parent 3c9b39d83b
commit 7bc8f20ed5
154 changed files with 3796 additions and 4117 deletions
@@ -6,11 +6,10 @@ 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
{
/**
* Run the migrations.
*
* @return void
*/
public function up(): void
{
@@ -24,5 +23,4 @@ return new class extends Migration {
}
});
}
};
@@ -6,8 +6,8 @@ use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration {
/** @return void */
return new class extends Migration
{
public function up(): void
{
Schema::create('helpdesk_tickets', static function (Blueprint $table): void {
@@ -38,5 +38,4 @@ return new class extends Migration {
$table->index('last_activity_at');
});
}
};
@@ -6,8 +6,8 @@ use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration {
/** @return void */
return new class extends Migration
{
public function up(): void
{
Schema::create('helpdesk_git_repositories', static function (Blueprint $table): void {
@@ -75,5 +75,4 @@ return new class extends Migration {
$table->index(['status', 'created_at']);
});
}
};
@@ -6,8 +6,8 @@ use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration {
/** @return void */
return new class extends Migration
{
public function up(): void
{
Schema::create('helpdesk_comments', static function (Blueprint $table): void {
@@ -37,5 +37,4 @@ return new class extends Migration {
$table->index(['ticket_id', 'comment_id', 'created_at']);
});
}
};
@@ -1,131 +0,0 @@
<?php
declare(strict_types=1);
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Schema;
return new class extends Migration {
/** @return void */
public function up(): void
{
$this->addIndex('helpdesk_tickets', ['company_id', 'status_id'], 'hdesk_tickets_company_status_idx');
$this->addIndex('helpdesk_tickets', ['company_id', 'priority_id'], 'hdesk_tickets_company_priority_idx');
$this->addIndex('helpdesk_tickets', ['company_id', 'category_id'], 'hdesk_tickets_company_category_idx');
$this->addIndex('helpdesk_tickets', ['company_id', 'team_id'], 'hdesk_tickets_company_team_idx');
$this->addIndex('helpdesk_tickets', ['status_id', 'created_at'], 'hdesk_tickets_status_created_idx');
$this->addIndex('helpdesk_tickets', ['due_at'], 'hdesk_tickets_due_at_idx');
$this->addIndex('helpdesk_tickets', ['archived_at'], 'hdesk_tickets_archived_at_idx');
$this->addIndex('helpdesk_comments', ['parent_id', 'created_at'], 'hdesk_comments_parent_created_idx');
$this->addIndex('helpdesk_comments', ['user_id', 'created_at'], 'hdesk_comments_user_created_idx');
$this->addIndex('helpdesk_attachments', ['ticket_id'], 'hdesk_attachments_ticket_idx');
$this->addIndex('helpdesk_attachments', ['comment_id'], 'hdesk_attachments_comment_idx');
$this->addIndex('helpdesk_attachments', ['user_id'], 'hdesk_attachments_user_idx');
if (Schema::hasTable('helpdesk_projects')) {
$this->addIndex('helpdesk_projects', ['company_id', 'archived_at'], 'hdesk_projects_company_archived_idx');
}
if (Schema::hasTable('helpdesk_teams')) {
$this->addIndex('helpdesk_teams', ['company_id', 'archived_at'], 'hdesk_teams_company_archived_idx');
}
if (Schema::hasTable('helpdesk_category_user')) {
$this->addIndex('helpdesk_category_user', ['category_id', 'user_id'], 'hdesk_category_user_category_user_idx');
$this->addIndex('helpdesk_category_user', ['user_id', 'category_id'], 'hdesk_category_user_user_category_idx');
}
}
/** @return void */
public function down(): void
{
foreach ([
'helpdesk_tickets' => [
'hdesk_tickets_company_status_idx',
'hdesk_tickets_company_priority_idx',
'hdesk_tickets_company_category_idx',
'hdesk_tickets_company_team_idx',
'hdesk_tickets_status_created_idx',
'hdesk_tickets_due_at_idx',
'hdesk_tickets_archived_at_idx',
],
'helpdesk_comments' => [
'hdesk_comments_parent_created_idx',
'hdesk_comments_user_created_idx',
],
'helpdesk_attachments' => [
'hdesk_attachments_ticket_idx',
'hdesk_attachments_comment_idx',
'hdesk_attachments_user_idx',
],
'helpdesk_projects' => [
'hdesk_projects_company_archived_idx',
],
'helpdesk_teams' => [
'hdesk_teams_company_archived_idx',
],
'helpdesk_category_user' => [
'hdesk_category_user_category_user_idx',
'hdesk_category_user_user_category_idx',
],
] as $table => $indexes) {
if (! Schema::hasTable($table)) {
continue;
}
Schema::table($table, function (Blueprint $tableBlueprint) use ($table, $indexes): void {
foreach ($indexes as $index) {
if ($this->indexExists($table, $index)) {
$tableBlueprint->dropIndex($index);
}
}
});
}
}
/**
* Add an index only when the table exists and the named index does not exist.
*
* @param string $table
* @param array<int, string> $columns
* @param string $index
* @return void
*/
private function addIndex(string $table, array $columns, string $index): void
{
if (! Schema::hasTable($table) || $this->indexExists($table, $index)) {
return;
}
Schema::table($table, static function (Blueprint $tableBlueprint) use ($columns, $index): void {
$tableBlueprint->index($columns, $index);
});
}
/**
* Determine whether an index exists for the current connection.
*
* @param string $table
* @param string $index
* @return bool
*/
private function indexExists(string $table, string $index): bool
{
if (method_exists(Schema::getFacadeRoot(), 'hasIndex')) {
return Schema::hasIndex($table, $index);
}
if (DB::getDriverName() === 'pgsql') {
return DB::selectOne(
'select 1 from pg_indexes where schemaname = current_schema() and tablename = ? and indexname = ? limit 1',
[$table, $index]
) !== null;
}
return false;
}
};
@@ -0,0 +1,39 @@
<?php
declare(strict_types=1);
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
/**
* Run the migrations.
*/
public function up(): void
{
if (! Schema::hasTable('helpdesk_kanban_boards')) {
Schema::create('helpdesk_kanban_boards', function (Blueprint $table): void {
$table->id();
$table->string('name');
$table->string('slug')->unique();
$table->text('description')->nullable();
$table->boolean('is_default')->default(false);
$table->unsignedInteger('sort_order')->default(0);
$table->timestamps();
$table->softDeletes();
$table->index(['is_default', 'sort_order']);
});
}
}
/**
* Reverse the migrations.
*/
public function down(): void
{
Schema::dropIfExists('helpdesk_kanban_boards');
}
};
@@ -0,0 +1,31 @@
<?php
declare(strict_types=1);
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
public function up(): void
{
if (! Schema::hasTable('helpdesk_kanban_board_statuses')) {
Schema::create('helpdesk_kanban_board_statuses', function (Blueprint $table): void {
$table->id();
$table->foreignId('board_id')->constrained('helpdesk_kanban_boards')->cascadeOnDelete();
$table->foreignId('status_id')->constrained('helpdesk_statuses')->cascadeOnDelete();
$table->unsignedInteger('sort_order')->default(0);
$table->timestamps();
$table->unique(['board_id', 'status_id']);
$table->index(['board_id', 'sort_order']);
});
}
}
public function down(): void
{
Schema::dropIfExists('helpdesk_kanban_board_statuses');
}
};
@@ -5,8 +5,10 @@ declare(strict_types=1);
namespace Modules\Helpdesk\Database\Seeders;
use Illuminate\Database\Seeder;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Str;
use Modules\Helpdesk\Models\Category;
use Modules\Helpdesk\Models\KanbanBoard;
use Modules\Helpdesk\Models\Priority;
use Modules\Helpdesk\Models\Project;
use Modules\Helpdesk\Models\Status;
@@ -26,7 +28,19 @@ final class HelpdeskDatabaseSeeder extends Seeder
$this->seedPriorities();
$this->seedCategories();
$this->seedTeamsAndProjects();
$this->seedKanbanBoards();
$this->seedWorkflowTemplates();
DB::table('helpdesk_kanban_boards')->updateOrInsert(
['slug' => 'default'],
[
'name' => 'Default Helpdesk Board',
'description' => 'Standard support workflow board.',
'is_default' => true,
'sort_order' => 1,
'created_at' => now(),
'updated_at' => now(),
]
);
}
/**
@@ -115,6 +129,38 @@ final class HelpdeskDatabaseSeeder extends Seeder
);
}
/**
* Seed the default configurable Kanban board.
*
* @return void
*/
private function seedKanbanBoards(): void
{
$board = KanbanBoard::query()->updateOrCreate(
['slug' => 'standard-support'],
[
'name' => 'Standard Support',
'slug' => 'standard-support',
'description' => 'Default Helpdesk workflow board covering new work through closure.',
'is_default' => true,
'sort_order' => 1,
]
);
$statuses = Status::query()
->whereIn('slug', ['new', 'open', 'in-progress', 'stalled', 'on-hold', 'closed'])
->orderBy('sort_order')
->get();
$sync = [];
foreach ($statuses as $index => $status) {
$sync[$status->id] = ['sort_order' => ($index + 1) * 10];
}
$board->statuses()->syncWithoutDetaching($sync);
}
/**
* Seed default professional workflow email templates.
*
@@ -134,4 +180,7 @@ final class HelpdeskDatabaseSeeder extends Seeder
);
}
}
}