v1.0.0
This commit is contained in:
1
src/database/.gitignore
vendored
Normal file
1
src/database/.gitignore
vendored
Normal file
@@ -0,0 +1 @@
|
||||
*.sqlite*
|
||||
22
src/database/factories/ApiClientFactory.php
Normal file
22
src/database/factories/ApiClientFactory.php
Normal file
@@ -0,0 +1,22 @@
|
||||
<?php
|
||||
|
||||
namespace Database\Factories;
|
||||
|
||||
use App\Models\ApiClient;
|
||||
use Illuminate\Database\Eloquent\Factories\Factory;
|
||||
|
||||
/**
|
||||
* @extends Factory<ApiClient>
|
||||
*/
|
||||
class ApiClientFactory extends Factory
|
||||
{
|
||||
protected $model = ApiClient::class;
|
||||
|
||||
public function definition(): array
|
||||
{
|
||||
return [
|
||||
'name' => fake()->company().' Integration',
|
||||
'description' => fake()->sentence(),
|
||||
];
|
||||
}
|
||||
}
|
||||
46
src/database/factories/UserFactory.php
Normal file
46
src/database/factories/UserFactory.php
Normal file
@@ -0,0 +1,46 @@
|
||||
<?php
|
||||
|
||||
namespace Database\Factories;
|
||||
|
||||
use App\Models\User;
|
||||
use Illuminate\Database\Eloquent\Factories\Factory;
|
||||
use Illuminate\Support\Facades\Hash;
|
||||
use Illuminate\Support\Str;
|
||||
|
||||
/**
|
||||
* @extends Factory<User>
|
||||
*/
|
||||
class UserFactory extends Factory
|
||||
{
|
||||
/**
|
||||
* The current password being used by the factory.
|
||||
*/
|
||||
protected static ?string $password;
|
||||
|
||||
/**
|
||||
* Define the model's default state.
|
||||
*
|
||||
* @return array<string, mixed>
|
||||
*/
|
||||
public function definition(): array
|
||||
{
|
||||
return [
|
||||
'name' => fake()->name(),
|
||||
'email' => fake()->unique()->safeEmail(),
|
||||
'email_verified_at' => now(),
|
||||
'password' => static::$password ??= Hash::make('password'),
|
||||
'roles' => ['client'],
|
||||
'remember_token' => Str::random(10),
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Indicate that the model's email address should be unverified.
|
||||
*/
|
||||
public function unverified(): static
|
||||
{
|
||||
return $this->state(fn (array $attributes) => [
|
||||
'email_verified_at' => null,
|
||||
]);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,45 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
return new class extends Migration
|
||||
{
|
||||
public function up(): void
|
||||
{
|
||||
Schema::create('users', function (Blueprint $table) {
|
||||
$table->id();
|
||||
$table->string('name');
|
||||
$table->string('email')->unique();
|
||||
$table->timestamp('email_verified_at')->nullable();
|
||||
$table->string('password')->nullable();
|
||||
$table->string('guid')->nullable()->unique();
|
||||
$table->string('domain')->nullable();
|
||||
$table->rememberToken();
|
||||
$table->timestamps();
|
||||
});
|
||||
|
||||
Schema::create('password_reset_tokens', function (Blueprint $table) {
|
||||
$table->string('email')->primary();
|
||||
$table->string('token');
|
||||
$table->timestamp('created_at')->nullable();
|
||||
});
|
||||
|
||||
Schema::create('sessions', function (Blueprint $table) {
|
||||
$table->string('id')->primary();
|
||||
$table->foreignId('user_id')->nullable()->index();
|
||||
$table->string('ip_address', 45)->nullable();
|
||||
$table->text('user_agent')->nullable();
|
||||
$table->longText('payload');
|
||||
$table->integer('last_activity')->index();
|
||||
});
|
||||
}
|
||||
|
||||
public function down(): void
|
||||
{
|
||||
Schema::dropIfExists('sessions');
|
||||
Schema::dropIfExists('password_reset_tokens');
|
||||
Schema::dropIfExists('users');
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,29 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
return new class extends Migration
|
||||
{
|
||||
public function up(): void
|
||||
{
|
||||
Schema::create('cache', function (Blueprint $table) {
|
||||
$table->string('key')->primary();
|
||||
$table->mediumText('value');
|
||||
$table->bigInteger('expiration')->index();
|
||||
});
|
||||
|
||||
Schema::create('cache_locks', function (Blueprint $table) {
|
||||
$table->string('key')->primary();
|
||||
$table->string('owner');
|
||||
$table->bigInteger('expiration')->index();
|
||||
});
|
||||
}
|
||||
|
||||
public function down(): void
|
||||
{
|
||||
Schema::dropIfExists('cache');
|
||||
Schema::dropIfExists('cache_locks');
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,53 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
return new class extends Migration
|
||||
{
|
||||
public function up(): void
|
||||
{
|
||||
Schema::create('jobs', function (Blueprint $table) {
|
||||
$table->id();
|
||||
$table->string('queue')->index();
|
||||
$table->longText('payload');
|
||||
$table->unsignedSmallInteger('attempts');
|
||||
$table->unsignedInteger('reserved_at')->nullable();
|
||||
$table->unsignedInteger('available_at');
|
||||
$table->unsignedInteger('created_at');
|
||||
});
|
||||
|
||||
Schema::create('job_batches', function (Blueprint $table) {
|
||||
$table->string('id')->primary();
|
||||
$table->string('name');
|
||||
$table->integer('total_jobs');
|
||||
$table->integer('pending_jobs');
|
||||
$table->integer('failed_jobs');
|
||||
$table->longText('failed_job_ids');
|
||||
$table->mediumText('options')->nullable();
|
||||
$table->integer('cancelled_at')->nullable();
|
||||
$table->integer('created_at');
|
||||
$table->integer('finished_at')->nullable();
|
||||
});
|
||||
|
||||
Schema::create('failed_jobs', function (Blueprint $table) {
|
||||
$table->id();
|
||||
$table->string('uuid')->unique();
|
||||
$table->string('connection');
|
||||
$table->string('queue');
|
||||
$table->longText('payload');
|
||||
$table->longText('exception');
|
||||
$table->timestamp('failed_at')->useCurrent();
|
||||
|
||||
$table->index(['connection', 'queue', 'failed_at']);
|
||||
});
|
||||
}
|
||||
|
||||
public function down(): void
|
||||
{
|
||||
Schema::dropIfExists('failed_jobs');
|
||||
Schema::dropIfExists('job_batches');
|
||||
Schema::dropIfExists('jobs');
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,30 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
return new class extends Migration
|
||||
{
|
||||
public function up(): void
|
||||
{
|
||||
Schema::create('roles', function (Blueprint $table) {
|
||||
$table->id();
|
||||
$table->string('key')->unique();
|
||||
$table->string('label');
|
||||
$table->timestamps();
|
||||
});
|
||||
|
||||
Schema::create('role_user', function (Blueprint $table) {
|
||||
$table->foreignId('role_id')->constrained()->cascadeOnDelete();
|
||||
$table->foreignId('user_id')->constrained()->cascadeOnDelete();
|
||||
$table->primary(['role_id', 'user_id']);
|
||||
});
|
||||
}
|
||||
|
||||
public function down(): void
|
||||
{
|
||||
Schema::dropIfExists('role_user');
|
||||
Schema::dropIfExists('roles');
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,29 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
return new class extends Migration
|
||||
{
|
||||
public function up(): void
|
||||
{
|
||||
Schema::create('teams', function (Blueprint $table) {
|
||||
$table->id();
|
||||
$table->string('name');
|
||||
$table->timestamps();
|
||||
});
|
||||
|
||||
Schema::create('team_user', function (Blueprint $table) {
|
||||
$table->foreignId('team_id')->constrained()->cascadeOnDelete();
|
||||
$table->foreignId('user_id')->constrained()->cascadeOnDelete();
|
||||
$table->primary(['team_id', 'user_id']);
|
||||
});
|
||||
}
|
||||
|
||||
public function down(): void
|
||||
{
|
||||
Schema::dropIfExists('team_user');
|
||||
Schema::dropIfExists('teams');
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,40 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
return new class extends Migration
|
||||
{
|
||||
public function up(): void
|
||||
{
|
||||
Schema::create('categories', function (Blueprint $table) {
|
||||
$table->id();
|
||||
$table->string('name');
|
||||
$table->text('description')->nullable();
|
||||
$table->timestamps();
|
||||
});
|
||||
|
||||
Schema::create('subcategories', function (Blueprint $table) {
|
||||
$table->id();
|
||||
$table->foreignId('category_id')->constrained()->cascadeOnDelete();
|
||||
$table->string('name');
|
||||
$table->text('description')->nullable();
|
||||
$table->string('default_priority_key')->nullable();
|
||||
$table->timestamps();
|
||||
});
|
||||
|
||||
Schema::create('team_subcategory', function (Blueprint $table) {
|
||||
$table->foreignId('team_id')->constrained()->cascadeOnDelete();
|
||||
$table->foreignId('subcategory_id')->constrained()->cascadeOnDelete();
|
||||
$table->primary(['team_id', 'subcategory_id']);
|
||||
});
|
||||
}
|
||||
|
||||
public function down(): void
|
||||
{
|
||||
Schema::dropIfExists('team_subcategory');
|
||||
Schema::dropIfExists('subcategories');
|
||||
Schema::dropIfExists('categories');
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,34 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
return new class extends Migration
|
||||
{
|
||||
public function up(): void
|
||||
{
|
||||
Schema::create('custom_fields', function (Blueprint $table) {
|
||||
$table->id();
|
||||
$table->string('label');
|
||||
$table->enum('type', ['text', 'textarea', 'select', 'checkbox', 'date', 'number']);
|
||||
$table->boolean('required')->default(false);
|
||||
$table->json('options')->nullable();
|
||||
$table->unsignedInteger('sort_order')->default(0);
|
||||
$table->timestamps();
|
||||
});
|
||||
|
||||
Schema::create('custom_field_subcategory', function (Blueprint $table) {
|
||||
$table->foreignId('custom_field_id')->constrained()->cascadeOnDelete();
|
||||
$table->foreignId('subcategory_id')->constrained()->cascadeOnDelete();
|
||||
$table->unsignedInteger('position')->default(0);
|
||||
$table->primary(['custom_field_id', 'subcategory_id']);
|
||||
});
|
||||
}
|
||||
|
||||
public function down(): void
|
||||
{
|
||||
Schema::dropIfExists('custom_field_subcategory');
|
||||
Schema::dropIfExists('custom_fields');
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,38 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
return new class extends Migration
|
||||
{
|
||||
public function up(): void
|
||||
{
|
||||
Schema::create('user_fields', function (Blueprint $table) {
|
||||
$table->id();
|
||||
$table->string('label');
|
||||
$table->text('description')->nullable();
|
||||
$table->enum('type', ['text', 'textarea', 'select', 'checkbox', 'date', 'number']);
|
||||
$table->json('options')->nullable();
|
||||
$table->string('ldap_attribute')->nullable();
|
||||
$table->unsignedInteger('sort_order')->default(0);
|
||||
$table->timestamps();
|
||||
});
|
||||
|
||||
Schema::create('user_field_values', function (Blueprint $table) {
|
||||
$table->id();
|
||||
$table->foreignId('user_id')->constrained()->cascadeOnDelete();
|
||||
$table->foreignId('user_field_id')->constrained()->cascadeOnDelete();
|
||||
$table->text('value')->nullable();
|
||||
$table->timestamps();
|
||||
|
||||
$table->unique(['user_id', 'user_field_id']);
|
||||
});
|
||||
}
|
||||
|
||||
public function down(): void
|
||||
{
|
||||
Schema::dropIfExists('user_field_values');
|
||||
Schema::dropIfExists('user_fields');
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,46 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
return new class extends Migration
|
||||
{
|
||||
public function up(): void
|
||||
{
|
||||
Schema::create('statuses', function (Blueprint $table) {
|
||||
$table->string('key')->primary();
|
||||
$table->string('label');
|
||||
$table->string('color');
|
||||
$table->string('stage')->default('open');
|
||||
$table->boolean('locked')->default(false);
|
||||
$table->unsignedInteger('sort_order')->default(0);
|
||||
$table->timestamps();
|
||||
});
|
||||
|
||||
Schema::create('priorities', function (Blueprint $table) {
|
||||
$table->string('key')->primary();
|
||||
$table->string('label');
|
||||
$table->string('color');
|
||||
$table->unsignedInteger('sort_order')->default(0);
|
||||
$table->timestamps();
|
||||
});
|
||||
|
||||
Schema::create('sla_rules', function (Blueprint $table) {
|
||||
$table->id();
|
||||
$table->string('priority_key');
|
||||
$table->unsignedInteger('response_mins');
|
||||
$table->unsignedInteger('resolution_mins');
|
||||
$table->timestamps();
|
||||
|
||||
$table->foreign('priority_key')->references('key')->on('priorities')->cascadeOnDelete();
|
||||
});
|
||||
}
|
||||
|
||||
public function down(): void
|
||||
{
|
||||
Schema::dropIfExists('sla_rules');
|
||||
Schema::dropIfExists('priorities');
|
||||
Schema::dropIfExists('statuses');
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,41 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
return new class extends Migration
|
||||
{
|
||||
public function up(): void
|
||||
{
|
||||
Schema::create('email_templates', function (Blueprint $table) {
|
||||
$table->id();
|
||||
$table->string('key')->unique();
|
||||
$table->string('name');
|
||||
$table->string('trigger_label');
|
||||
$table->string('subject');
|
||||
$table->text('body');
|
||||
$table->timestamps();
|
||||
});
|
||||
|
||||
Schema::create('response_templates', function (Blueprint $table) {
|
||||
$table->id();
|
||||
$table->string('label');
|
||||
$table->text('body');
|
||||
$table->timestamps();
|
||||
});
|
||||
|
||||
Schema::create('settings', function (Blueprint $table) {
|
||||
$table->string('key')->primary();
|
||||
$table->text('value')->nullable();
|
||||
$table->timestamps();
|
||||
});
|
||||
}
|
||||
|
||||
public function down(): void
|
||||
{
|
||||
Schema::dropIfExists('settings');
|
||||
Schema::dropIfExists('response_templates');
|
||||
Schema::dropIfExists('email_templates');
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,26 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
return new class extends Migration
|
||||
{
|
||||
public function up(): void
|
||||
{
|
||||
Schema::create('notification_settings', function (Blueprint $table) {
|
||||
$table->id();
|
||||
$table->string('trigger_key')->unique();
|
||||
$table->string('trigger_label');
|
||||
$table->boolean('enabled')->default(true);
|
||||
$table->string('recipient')->default('client');
|
||||
$table->foreignId('email_template_id')->nullable()->constrained('email_templates')->nullOnDelete();
|
||||
$table->timestamps();
|
||||
});
|
||||
}
|
||||
|
||||
public function down(): void
|
||||
{
|
||||
Schema::dropIfExists('notification_settings');
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,28 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
return new class extends Migration
|
||||
{
|
||||
/**
|
||||
* status_key is a soft reference (like tickets.status_key) — no FK, so
|
||||
* deleting a status in Admin never blocks or cascades into these rows.
|
||||
*/
|
||||
public function up(): void
|
||||
{
|
||||
Schema::create('reply_quick_actions', function (Blueprint $table) {
|
||||
$table->id();
|
||||
$table->string('label');
|
||||
$table->string('status_key')->nullable();
|
||||
$table->unsignedInteger('sort_order')->default(0);
|
||||
$table->timestamps();
|
||||
});
|
||||
}
|
||||
|
||||
public function down(): void
|
||||
{
|
||||
Schema::dropIfExists('reply_quick_actions');
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,25 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
return new class extends Migration
|
||||
{
|
||||
public function up(): void
|
||||
{
|
||||
Schema::create('api_clients', function (Blueprint $table) {
|
||||
$table->id();
|
||||
$table->string('name');
|
||||
$table->text('description')->nullable();
|
||||
$table->foreignId('created_by')->nullable()->constrained('users')->nullOnDelete();
|
||||
$table->timestamp('revoked_at')->nullable();
|
||||
$table->timestamps();
|
||||
});
|
||||
}
|
||||
|
||||
public function down(): void
|
||||
{
|
||||
Schema::dropIfExists('api_clients');
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,39 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
return new class extends Migration
|
||||
{
|
||||
public function up(): void
|
||||
{
|
||||
Schema::create('tickets', function (Blueprint $table) {
|
||||
$table->id();
|
||||
$table->string('number')->unique();
|
||||
$table->foreignId('customer_id')->nullable()->constrained('users')->nullOnDelete();
|
||||
$table->string('email');
|
||||
$table->string('name');
|
||||
$table->foreignId('subcategory_id')->nullable()->constrained('subcategories')->nullOnDelete();
|
||||
$table->string('subject');
|
||||
$table->text('body');
|
||||
$table->string('status_key');
|
||||
$table->string('priority_key');
|
||||
$table->foreignId('team_id')->nullable()->constrained('teams')->nullOnDelete();
|
||||
$table->foreignId('assignee_id')->nullable()->constrained('users')->nullOnDelete();
|
||||
$table->foreignId('api_client_id')->nullable()->constrained('api_clients')->nullOnDelete();
|
||||
$table->json('custom_fields')->nullable();
|
||||
$table->timestamp('sla_notified_at')->nullable();
|
||||
$table->unsignedInteger('time_spent_seconds')->default(0);
|
||||
$table->timestamp('timer_started_at')->nullable();
|
||||
$table->timestamps();
|
||||
|
||||
$table->index(['status_key', 'priority_key']);
|
||||
});
|
||||
}
|
||||
|
||||
public function down(): void
|
||||
{
|
||||
Schema::dropIfExists('tickets');
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,57 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
return new class extends Migration
|
||||
{
|
||||
public function up(): void
|
||||
{
|
||||
Schema::create('ticket_messages', function (Blueprint $table) {
|
||||
$table->id();
|
||||
$table->foreignId('ticket_id')->constrained()->cascadeOnDelete();
|
||||
$table->string('author_name');
|
||||
$table->boolean('internal')->default(false);
|
||||
$table->text('body');
|
||||
$table->boolean('edited')->default(false);
|
||||
$table->foreignId('api_client_id')->nullable()->constrained('api_clients')->nullOnDelete();
|
||||
$table->timestamps();
|
||||
});
|
||||
|
||||
// Who wrote a message and in what role — a message with no row here
|
||||
// is a system-generated entry (no author, no role).
|
||||
Schema::create('ticket_message_authors', function (Blueprint $table) {
|
||||
$table->id();
|
||||
$table->foreignId('ticket_message_id')->constrained()->cascadeOnDelete();
|
||||
$table->foreignId('user_id')->nullable()->constrained('users')->nullOnDelete();
|
||||
$table->foreignId('role_id')->nullable()->constrained('roles')->nullOnDelete();
|
||||
});
|
||||
|
||||
Schema::create('ticket_attachments', function (Blueprint $table) {
|
||||
$table->id();
|
||||
$table->foreignId('ticket_id')->constrained()->cascadeOnDelete();
|
||||
$table->foreignId('message_id')->nullable()->constrained('ticket_messages')->nullOnDelete();
|
||||
$table->string('original_name');
|
||||
$table->string('path');
|
||||
$table->unsignedBigInteger('size')->default(0);
|
||||
$table->string('mime')->nullable();
|
||||
$table->timestamps();
|
||||
});
|
||||
|
||||
Schema::create('ticket_histories', function (Blueprint $table) {
|
||||
$table->id();
|
||||
$table->foreignId('ticket_id')->constrained()->cascadeOnDelete();
|
||||
$table->string('text');
|
||||
$table->timestamp('created_at')->nullable();
|
||||
});
|
||||
}
|
||||
|
||||
public function down(): void
|
||||
{
|
||||
Schema::dropIfExists('ticket_histories');
|
||||
Schema::dropIfExists('ticket_attachments');
|
||||
Schema::dropIfExists('ticket_message_authors');
|
||||
Schema::dropIfExists('ticket_messages');
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,27 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
return new class extends Migration
|
||||
{
|
||||
public function up(): void
|
||||
{
|
||||
Schema::create('personal_access_tokens', function (Blueprint $table) {
|
||||
$table->id();
|
||||
$table->morphs('tokenable');
|
||||
$table->text('name');
|
||||
$table->string('token', 64)->unique();
|
||||
$table->text('abilities')->nullable();
|
||||
$table->timestamp('last_used_at')->nullable();
|
||||
$table->timestamp('expires_at')->nullable()->index();
|
||||
$table->timestamps();
|
||||
});
|
||||
}
|
||||
|
||||
public function down(): void
|
||||
{
|
||||
Schema::dropIfExists('personal_access_tokens');
|
||||
}
|
||||
};
|
||||
445
src/database/seeders/DatabaseSeeder.php
Normal file
445
src/database/seeders/DatabaseSeeder.php
Normal file
@@ -0,0 +1,445 @@
|
||||
<?php
|
||||
|
||||
namespace Database\Seeders;
|
||||
|
||||
use App\Models\Category;
|
||||
use App\Models\CustomField;
|
||||
use App\Models\EmailTemplate;
|
||||
use App\Models\NotificationSetting;
|
||||
use App\Models\Priority;
|
||||
use App\Models\ReplyQuickAction;
|
||||
use App\Models\ResponseTemplate;
|
||||
use App\Models\Role;
|
||||
use App\Models\SlaRule;
|
||||
use App\Models\Status;
|
||||
use App\Models\Subcategory;
|
||||
use App\Models\Team;
|
||||
use App\Models\User;
|
||||
use App\Models\UserField;
|
||||
use App\Support\Settings;
|
||||
use Illuminate\Database\Seeder;
|
||||
|
||||
class DatabaseSeeder extends Seeder
|
||||
{
|
||||
/** @var array<string, Subcategory> */
|
||||
protected array $subs = [];
|
||||
|
||||
/** @var array<string, CustomField> */
|
||||
protected array $fields = [];
|
||||
|
||||
/**
|
||||
* Seeds the application with the same reference data currently
|
||||
* configured in production (categories, custom fields, statuses,
|
||||
* priorities/SLA, teams, templates, quick actions, settings) — no ticket
|
||||
* data, and only a single local admin account instead of real users.
|
||||
*/
|
||||
public function run(): void
|
||||
{
|
||||
$this->seedRoles();
|
||||
$this->seedStatusesAndPriorities();
|
||||
$this->seedCategories();
|
||||
$this->seedCustomFields();
|
||||
$this->seedTeams();
|
||||
$this->seedUserFields();
|
||||
$this->seedReplyQuickActions();
|
||||
$this->seedResponseTemplates();
|
||||
$this->seedEmailTemplatesAndNotifications();
|
||||
$this->seedSettings();
|
||||
$this->seedAdminUser();
|
||||
}
|
||||
|
||||
protected function seedRoles(): void
|
||||
{
|
||||
foreach ([
|
||||
['key' => 'client', 'label' => 'Klient'],
|
||||
['key' => 'operator', 'label' => 'Operator'],
|
||||
['key' => 'admin', 'label' => 'Administrator'],
|
||||
] as $role) {
|
||||
Role::query()->create($role);
|
||||
}
|
||||
}
|
||||
|
||||
protected function seedStatusesAndPriorities(): void
|
||||
{
|
||||
foreach ([
|
||||
['key' => 'new', 'label' => 'Nowe', 'color' => '#e90a0f', 'stage' => 'new', 'locked' => true, 'sort_order' => 1],
|
||||
['key' => 'open', 'label' => 'Otwarty', 'color' => '#b5abfc', 'stage' => 'open', 'locked' => true, 'sort_order' => 2],
|
||||
['key' => 'in_progress', 'label' => 'W trakcie', 'color' => '#2fd744', 'stage' => 'open', 'locked' => false, 'sort_order' => 3],
|
||||
['key' => 'waiting_customer', 'label' => 'Oczekuje na klienta', 'color' => '#82ccd0', 'stage' => 'open', 'locked' => false, 'sort_order' => 4],
|
||||
['key' => 'waiting_operator', 'label' => 'Oczekuje na operatora', 'color' => '#b5abfc', 'stage' => 'open', 'locked' => false, 'sort_order' => 5],
|
||||
['key' => 'on_hold', 'label' => 'Wstrzymany', 'color' => '#b5abfc', 'stage' => 'open', 'locked' => false, 'sort_order' => 6],
|
||||
['key' => 'closed', 'label' => 'Zamknięte', 'color' => '#75798c', 'stage' => 'closed', 'locked' => true, 'sort_order' => 7],
|
||||
] as $status) {
|
||||
Status::query()->create($status);
|
||||
}
|
||||
|
||||
foreach ([
|
||||
['key' => 'critical', 'label' => 'Krytyczny', 'color' => '#e60000', 'sort_order' => 1],
|
||||
['key' => 'high', 'label' => 'Wysoki', 'color' => '#0008f0', 'sort_order' => 2],
|
||||
['key' => 'medium', 'label' => 'Średni', 'color' => '#fd08f4', 'sort_order' => 3],
|
||||
['key' => 'low', 'label' => 'Niski', 'color' => '#1be748', 'sort_order' => 4],
|
||||
['key' => 'none', 'label' => 'Brak', 'color' => '#9184d9', 'sort_order' => 5],
|
||||
] as $priority) {
|
||||
Priority::query()->create($priority);
|
||||
}
|
||||
|
||||
foreach ([
|
||||
['critical', 30, 240],
|
||||
['high', 60, 480],
|
||||
['medium', 240, 1440],
|
||||
['low', 480, 4320],
|
||||
['none', 0, 0],
|
||||
] as [$priority, $response, $resolution]) {
|
||||
SlaRule::query()->create([
|
||||
'priority_key' => $priority,
|
||||
'response_mins' => $response,
|
||||
'resolution_mins' => $resolution,
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
protected function seedCategories(): void
|
||||
{
|
||||
$data = [
|
||||
'IT-Pomoc' => [
|
||||
'description' => 'Pomoc techniczna z sprzętem lub oprogramowaniem.',
|
||||
'subs' => [
|
||||
'aktualizacje-systemu' => ['Aktualizacje systemu', 'Aktualizacja systemu lub konkretnej aplikacji.'],
|
||||
'backup' => ['Backup i odzyskiwanie danych', 'Tworzenie kopii zapasowych i przywracanie utraconych danych.'],
|
||||
'drukarki' => ['Drukarki i skanery', 'Problemy z drukarkami, skanerami i urządzeniami wielofunkcyjnymi.'],
|
||||
'incydent' => ['Incydent bezpieczeństwa', 'Zgłaszanie podejrzanych zdarzeń i naruszeń bezpieczeństwa IT.'],
|
||||
'it-inne' => ['Inne', 'Pozostałe sprawy związane z IT, niepasujące do innych kategorii.'],
|
||||
'internet' => ['Internet', 'Problemy z dostępem do internetu i połączeniem sieciowym.'],
|
||||
'konta' => ['Konta i dostępy', 'Zakładanie, modyfikacja i blokowanie kont oraz uprawnień w systemach.'],
|
||||
'oprogramowanie' => ['Oprogramowanie', 'Instalacja, aktualizacja i błędy aplikacji oraz oprogramowania.'],
|
||||
'poczta' => ['Poczta', 'Problemy ze skrzynką pocztową, wysyłką i odbiorem wiadomości e-mail.'],
|
||||
'pulpit-zdalny' => ['Pulpit zdalny', 'Dostęp i problemy z pulpitem zdalnym oraz zdalnym połączeniem do komputera.'],
|
||||
'reset-hasla' => ['Reset hasła', 'Resetowanie zapomnianych lub zablokowanych haseł do systemów.'],
|
||||
'siec-wewnetrzna' => ['Sieć wewnętrzna', 'Problemy z siecią lokalną i infrastrukturą sieciową w firmie.'],
|
||||
'sprzet' => ['Sprzęt komputerowy', 'Awarie i wsparcie dla komputerów, laptopów i akcesoriów.'],
|
||||
'telefon' => ['Telefon', 'Problemy z telefonią stacjonarną i komórkową.'],
|
||||
'vpn' => ['VPN', 'Problemy z połączeniem VPN i dostępem zdalnym do zasobów firmy.'],
|
||||
],
|
||||
],
|
||||
'Zamówienia' => [
|
||||
'description' => 'Zamawianie sprzętu, oprogramowania, licencji i materiałów biurowych.',
|
||||
'subs' => [
|
||||
'zam-sprzet' => ['Zamówienie sprzętu IT', 'Wnioski o zakup nowego sprzętu komputerowego i akcesoriów.'],
|
||||
'zam-oprogramowanie' => ['Zamówienie oprogramowania/licencji', 'Wnioski o zakup oprogramowania i licencji.'],
|
||||
'zam-materialy' => ['Zamówienie materiałów biurowych', 'Zamawianie artykułów i materiałów biurowych.'],
|
||||
'zam-usluga' => ['Zamówienie usługi zewnętrznej', 'Zlecanie usług realizowanych przez firmy zewnętrzne.'],
|
||||
],
|
||||
],
|
||||
'Administracja' => [
|
||||
'description' => 'Sprawy administracyjne, kadrowe i biurowe.',
|
||||
'subs' => [
|
||||
'wnioski-dokumenty' => ['Wnioski i dokumenty', 'Składanie wniosków i obieg dokumentów administracyjnych.'],
|
||||
'sprawy-kadrowe' => ['Sprawy kadrowe', 'Sprawy związane z zatrudnieniem, urlopami i dokumentacją pracowniczą.'],
|
||||
'obsluga-biura' => ['Obsługa biura', 'Sprawy związane z bieżącą obsługą i funkcjonowaniem biura.'],
|
||||
'dostep-budynek' => ['Dostęp do budynku / karty', 'Wydawanie i zarządzanie kartami dostępu do budynku.'],
|
||||
],
|
||||
],
|
||||
'Delegacje' => [
|
||||
'description' => 'Wnioski i rozliczenia związane z podróżami służbowymi.',
|
||||
'subs' => [
|
||||
'del-krajowa' => ['Wniosek o delegację krajową', 'Zgłaszanie wyjazdów służbowych na terenie kraju.'],
|
||||
'del-zagraniczna' => ['Wniosek o delegację zagraniczną', 'Zgłaszanie wyjazdów służbowych za granicę.'],
|
||||
'del-rozliczenie' => ['Rozliczenie kosztów delegacji', 'Rozliczanie kosztów poniesionych podczas delegacji.'],
|
||||
'del-zaliczka' => ['Zaliczka na delegację', 'Wnioski o zaliczkę na poczet wydatków związanych z delegacją.'],
|
||||
],
|
||||
],
|
||||
];
|
||||
|
||||
foreach ($data as $name => $cat) {
|
||||
$category = Category::query()->create(['name' => $name, 'description' => $cat['description']]);
|
||||
|
||||
foreach ($cat['subs'] as $slug => [$subName, $description]) {
|
||||
$this->subs[$slug] = $category->subcategories()->create([
|
||||
'name' => $subName,
|
||||
'description' => $description,
|
||||
]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected function seedCustomFields(): void
|
||||
{
|
||||
$fields = [
|
||||
'teamviewer' => ['Teamviewer-ID', 'text', null, ['aktualizacje-systemu', 'drukarki', 'oprogramowanie', 'pulpit-zdalny', 'siec-wewnetrzna', 'sprzet', 'vpn']],
|
||||
'nazwa-systemu' => ['Nazwa systemu/aplikacji', 'text', null, ['aktualizacje-systemu', 'konta']],
|
||||
'lokalizacja-biura' => ['Lokalizacja / numer biura', 'text', null, ['internet', 'siec-wewnetrzna']],
|
||||
'lokalizacja-danych' => ['Lokalizacja danych do odzyskania', 'text', null, ['backup']],
|
||||
'data-utraty' => ['Data utraty danych', 'date', null, ['backup']],
|
||||
'model-urzadzenia' => ['Model urządzenia', 'text', null, ['drukarki']],
|
||||
'typ-incydentu' => ['Typ incydentu', 'select', ['Phishing', 'Złośliwe oprogramowanie', 'Wyciek danych', 'Nieautoryzowany dostęp', 'Inne'], ['incydent']],
|
||||
'dane-wrazliwe' => ['Dane wrażliwe ujawnione', 'checkbox', null, ['incydent']],
|
||||
'opis-sprawy' => ['Opis sprawy', 'textarea', null, ['it-inne']],
|
||||
'rodzaj-problemu-net' => ['Rodzaj problemu', 'select', ['Brak połączenia', 'Wolne łącze', 'Przerywane połączenie', 'Inne'], ['internet']],
|
||||
'rodzaj-dostepu' => ['Rodzaj dostępu', 'select', ['Nowe konto', 'Zmiana uprawnień', 'Zablokowanie konta', 'Inne'], ['konta']],
|
||||
'nazwa-aplikacji' => ['Nazwa aplikacji', 'text', null, ['oprogramowanie']],
|
||||
'adres-email' => ['Adres e-mail', 'text', null, ['poczta']],
|
||||
'rodzaj-problemu-mail' => ['Rodzaj problemu', 'select', ['Nie odbiera', 'Nie wysyła', 'Spam/Phishing', 'Skrzynka pełna', 'Inne'], ['poczta']],
|
||||
'adres-stacji-zdalnej' => ['Adres/nazwa stacji zdalnej', 'text', null, ['pulpit-zdalny']],
|
||||
'nazwa-systemu-konta' => ['Nazwa systemu/konta', 'text', null, ['reset-hasla']],
|
||||
'numer-inwentarzowy' => ['Numer inwentarzowy sprzętu', 'text', null, ['sprzet']],
|
||||
'numer-telefonu' => ['Numer telefonu służbowego', 'text', null, ['telefon']],
|
||||
'model-telefonu' => ['Model telefonu', 'text', null, ['telefon']],
|
||||
'lokalizacja-pracy-zdalnej' => ['Lokalizacja pracy zdalnej', 'text', null, ['vpn']],
|
||||
'kwota' => ['Szacowana kwota (PLN)', 'number', null, ['zam-sprzet', 'zam-oprogramowanie', 'zam-materialy', 'zam-usluga']],
|
||||
'centrum-kosztow' => ['Numer centrum kosztów', 'text', null, ['zam-sprzet', 'zam-oprogramowanie', 'zam-materialy', 'zam-usluga']],
|
||||
'zgoda-przelozonego' => ['Zgoda przełożonego uzyskana', 'checkbox', null, ['zam-sprzet', 'zam-oprogramowanie', 'zam-materialy', 'zam-usluga']],
|
||||
'rodzaj-wniosku' => ['Rodzaj wniosku', 'text', null, ['wnioski-dokumenty', 'sprawy-kadrowe', 'obsluga-biura', 'dostep-budynek']],
|
||||
'termin-realizacji' => ['Termin realizacji', 'date', null, ['wnioski-dokumenty', 'sprawy-kadrowe', 'obsluga-biura', 'dostep-budynek']],
|
||||
'miejsce-delegacji' => ['Miejsce delegacji', 'text', null, ['del-krajowa', 'del-zagraniczna', 'del-rozliczenie', 'del-zaliczka']],
|
||||
'powod-delegacji' => ['Powód delegacji', 'text', null, ['del-krajowa', 'del-zagraniczna', 'del-rozliczenie', 'del-zaliczka']],
|
||||
'data-poczatku-delegacji' => ['Data początku delegacji', 'date', null, ['del-krajowa', 'del-zagraniczna', 'del-rozliczenie', 'del-zaliczka']],
|
||||
'data-konca-delegacji' => ['Data końca delegacji', 'date', null, ['del-krajowa', 'del-zagraniczna', 'del-rozliczenie', 'del-zaliczka']],
|
||||
'srodki-transportu' => ['Środki transportu', 'select', ['Samochód', 'Pociąg', 'Samolot', 'Autobus', 'Inne'], ['del-krajowa', 'del-zagraniczna', 'del-rozliczenie', 'del-zaliczka']],
|
||||
'hotel' => ['Hotel', 'select', [
|
||||
'Brak', 'Dowolny', 'Inny wskazany', 'Antares', 'Countryard (Gdynia)', 'Countryard by Mariot Szczecin',
|
||||
'Dom Muzyka', 'Focus Szczecin', 'Moxy Szczecin City', 'My Story', 'Qubus Hotel Gdańsk',
|
||||
'Radisson Blu Hotel Szczecin', 'Różany Gaj', 'Vulcan', 'Millenium (Świnoujście)', 'Grand Focus (Szczecin)',
|
||||
], ['del-krajowa', 'del-zagraniczna', 'del-rozliczenie', 'del-zaliczka']],
|
||||
];
|
||||
|
||||
foreach ($fields as $slug => [$label, $type, $options, $subSlugs]) {
|
||||
$field = CustomField::query()->create([
|
||||
'label' => $label,
|
||||
'type' => $type,
|
||||
'required' => false,
|
||||
'options' => $options,
|
||||
'sort_order' => count($this->fields),
|
||||
]);
|
||||
|
||||
$this->fields[$slug] = $field;
|
||||
|
||||
foreach ($subSlugs as $position => $subSlug) {
|
||||
$field->subcategories()->attach($this->subs[$subSlug]->id, ['position' => $position]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected function seedTeams(): void
|
||||
{
|
||||
$teams = [
|
||||
'IT' => array_keys(array_filter($this->subs, fn ($_, $slug) => in_array($slug, [
|
||||
'aktualizacje-systemu', 'backup', 'drukarki', 'incydent', 'it-inne', 'internet', 'konta',
|
||||
'oprogramowanie', 'poczta', 'pulpit-zdalny', 'reset-hasla', 'siec-wewnetrzna', 'sprzet', 'telefon', 'vpn',
|
||||
], true), ARRAY_FILTER_USE_BOTH)),
|
||||
'Administracja' => ['wnioski-dokumenty', 'sprawy-kadrowe', 'obsluga-biura', 'dostep-budynek', 'del-krajowa', 'del-zagraniczna', 'del-rozliczenie', 'del-zaliczka'],
|
||||
'Zakupy' => ['zam-sprzet', 'zam-oprogramowanie', 'zam-materialy', 'zam-usluga'],
|
||||
];
|
||||
|
||||
foreach ($teams as $name => $subSlugs) {
|
||||
$team = Team::query()->create(['name' => $name]);
|
||||
$team->subcategories()->attach(array_map(fn ($slug) => $this->subs[$slug]->id, $subSlugs));
|
||||
}
|
||||
}
|
||||
|
||||
protected function seedUserFields(): void
|
||||
{
|
||||
foreach ([
|
||||
['label' => 'Stanowisko', 'ldap_attribute' => 'title', 'sort_order' => 2],
|
||||
['label' => 'Dział', 'ldap_attribute' => 'department', 'sort_order' => 3],
|
||||
['label' => 'Firma', 'ldap_attribute' => 'company', 'sort_order' => 4],
|
||||
['label' => 'Mob', 'ldap_attribute' => 'mobile', 'sort_order' => 5],
|
||||
['label' => 'Stacjonarny', 'ldap_attribute' => 'telephone', 'sort_order' => 6],
|
||||
['label' => 'Biuro', 'ldap_attribute' => 'phisical_delivery_office_name', 'sort_order' => 7],
|
||||
] as $field) {
|
||||
UserField::query()->create([
|
||||
'label' => $field['label'],
|
||||
'type' => 'text',
|
||||
'ldap_attribute' => $field['ldap_attribute'],
|
||||
'sort_order' => $field['sort_order'],
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
protected function seedReplyQuickActions(): void
|
||||
{
|
||||
foreach ([
|
||||
['label' => 'Wyślij i „Oczekuje na klienta”', 'status_key' => 'waiting_customer', 'sort_order' => 1],
|
||||
['label' => 'Wyślij i zamknij', 'status_key' => 'closed', 'sort_order' => 3],
|
||||
] as $action) {
|
||||
ReplyQuickAction::query()->create($action);
|
||||
}
|
||||
}
|
||||
|
||||
protected function seedResponseTemplates(): void
|
||||
{
|
||||
foreach ([
|
||||
['label' => 'Prośba o więcej informacji', 'body' => 'Dziękujemy za zgłoszenie. Czy mógłby Pan/Pani podać więcej szczegółów oraz zrzut ekranu problemu?'],
|
||||
['label' => 'Restart usuwa problem', 'body' => 'Prosimy o zrestartowanie urządzenia/aplikacji i sprawdzenie, czy problem nadal występuje.'],
|
||||
['label' => 'Zgłoszenie rozwiązane', 'body' => 'Zgłoszenie zostało rozwiązane. Dziękujemy za cierpliwość — w razie dalszych pytań prosimy o kontakt.'],
|
||||
] as $template) {
|
||||
ResponseTemplate::query()->create($template);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Email template bodies are rendered as raw HTML (see
|
||||
* Settings::renderEmailLayout() / TicketNotification), so every body
|
||||
* here is proper markup — paragraphs instead of bare "\n\n", and the
|
||||
* {link} placeholder wrapped in a real <a> tag instead of being dropped
|
||||
* in as plain text.
|
||||
*/
|
||||
protected function seedEmailTemplatesAndNotifications(): void
|
||||
{
|
||||
$footer = '<p>Pozdrawiamy,<br>Zespół Wsparcia</p>';
|
||||
$link = '<p>Podgląd zgłoszenia: <a href="{link}" rel="noopener noreferrer" target="_blank">Kliknij tu</a></p>';
|
||||
|
||||
$templates = [
|
||||
'tpl-new' => [
|
||||
'name' => 'Nowe zgłoszenie przyjęte', 'trigger_label' => 'Zgłoszenie utworzone',
|
||||
'subject' => 'Otrzymaliśmy Twoje zgłoszenie #{numer}',
|
||||
'body' => '<p>Cześć {imie},</p><p>Otrzymaliśmy Twoje zgłoszenie „{temat}”. Nasz zespół zajmie się nim najszybciej jak to możliwe.</p>'.$link.$footer,
|
||||
],
|
||||
'tpl-status' => [
|
||||
'name' => 'Zmiana statusu', 'trigger_label' => 'Status zgłoszenia zmieniony',
|
||||
'subject' => 'Aktualizacja zgłoszenia #{numer}',
|
||||
'body' => '<p>Cześć {imie},</p><p>Status Twojego zgłoszenia „{temat}” zmienił się na: {status}.</p>'.$link.$footer,
|
||||
],
|
||||
'tpl-category' => [
|
||||
'name' => 'Zmiana kategorii', 'trigger_label' => 'Kategoria zgłoszenia zmieniona',
|
||||
'subject' => 'Zmieniono kategorię zgłoszenia #{numer}',
|
||||
'body' => '<p>Cześć {imie},</p><p>Kategoria Twojego zgłoszenia „{temat}” została zmieniona na: {kategoria}.</p>'.$link.$footer,
|
||||
],
|
||||
'tpl-assignee' => [
|
||||
'name' => 'Zmiana przypisanego operatora', 'trigger_label' => 'Przypisany operator zmieniony',
|
||||
'subject' => 'Zmieniono osobę obsługującą zgłoszenie #{numer}',
|
||||
'body' => '<p>Cześć {imie},</p><p>Twoim zgłoszeniem „{temat}” zajmie się teraz: {operator}.</p>'.$link.$footer,
|
||||
],
|
||||
'tpl-priority' => [
|
||||
'name' => 'Zmiana priorytetu', 'trigger_label' => 'Priorytet zgłoszenia zmieniony',
|
||||
'subject' => 'Zmieniono priorytet zgłoszenia #{numer}',
|
||||
'body' => '<p>Cześć {imie},</p><p>Priorytet Twojego zgłoszenia „{temat}” zmienił się na: {priorytet}.</p>'.$link.$footer,
|
||||
],
|
||||
'tpl-team' => [
|
||||
'name' => 'Zmiana zespołu', 'trigger_label' => 'Zespół obsługujący zmieniony',
|
||||
'subject' => 'Zmieniono zespół obsługujący zgłoszenie #{numer}',
|
||||
'body' => '<p>Cześć {imie},</p><p>Twoim zgłoszeniem „{temat}” zajmuje się teraz zespół: {zespol}.</p>'.$link.$footer,
|
||||
],
|
||||
'tpl-closed' => [
|
||||
'name' => 'Zgłoszenie zamknięte', 'trigger_label' => 'Status = Zamknięte',
|
||||
'subject' => 'Zgłoszenie #{numer} zostało zamknięte',
|
||||
'body' => '<p>Cześć {imie},</p><p>Twoje zgłoszenie „{temat}” zostało zamknięte. Jeśli temat nie został rozwiązany, odpowiedz na tego maila lub zgłoś sprawę ponownie.</p>'.$link.$footer,
|
||||
],
|
||||
'tpl-reply' => [
|
||||
'name' => 'Nowa odpowiedź operatora', 'trigger_label' => 'Operator odpowiedział',
|
||||
'subject' => 'Nowa odpowiedź w zgłoszeniu #{numer}',
|
||||
'body' => '<p>Cześć {imie},</p><p>Otrzymałeś/aś nową odpowiedź w zgłoszeniu „{temat}”.</p>'.$link.$footer,
|
||||
],
|
||||
'tpl-sla-breach' => [
|
||||
'name' => 'Przekroczenie SLA', 'trigger_label' => 'SLA przekroczone — operator',
|
||||
'subject' => 'Przekroczono SLA zgłoszenia #{numer}',
|
||||
'body' => '<p>Cześć {operator},</p><p>Zgłoszenie „{temat}” (#{numer}) przekroczyło ustalony czas rozwiązania SLA.</p>'.$link.$footer,
|
||||
],
|
||||
];
|
||||
|
||||
$ids = [];
|
||||
|
||||
foreach ($templates as $key => $tpl) {
|
||||
$ids[$key] = EmailTemplate::query()->create([
|
||||
'key' => $key,
|
||||
'name' => $tpl['name'],
|
||||
'trigger_label' => $tpl['trigger_label'],
|
||||
'subject' => $tpl['subject'],
|
||||
'body' => $tpl['body'],
|
||||
])->id;
|
||||
}
|
||||
|
||||
foreach ([
|
||||
['trigger_key' => 'ticket_created', 'trigger_label' => 'Nowe zgłoszenie utworzone', 'enabled' => true, 'recipient' => 'client', 'template' => 'tpl-new'],
|
||||
['trigger_key' => 'status_changed', 'trigger_label' => 'Zmiana statusu zgłoszenia', 'enabled' => true, 'recipient' => 'client', 'template' => 'tpl-status'],
|
||||
['trigger_key' => 'category_changed', 'trigger_label' => 'Zmiana kategorii zgłoszenia', 'enabled' => false, 'recipient' => 'client', 'template' => 'tpl-category'],
|
||||
['trigger_key' => 'assignee_changed', 'trigger_label' => 'Zmiana przypisanego operatora', 'enabled' => false, 'recipient' => 'client', 'template' => 'tpl-assignee'],
|
||||
['trigger_key' => 'priority_changed', 'trigger_label' => 'Zmiana priorytetu', 'enabled' => false, 'recipient' => 'client', 'template' => 'tpl-priority'],
|
||||
['trigger_key' => 'team_changed', 'trigger_label' => 'Zmiana zespołu obsługującego', 'enabled' => false, 'recipient' => 'client', 'template' => 'tpl-team'],
|
||||
['trigger_key' => 'ticket_closed', 'trigger_label' => 'Zgłoszenie zamknięte', 'enabled' => true, 'recipient' => 'client', 'template' => 'tpl-closed'],
|
||||
['trigger_key' => 'operator_replied', 'trigger_label' => 'Nowa odpowiedź operatora', 'enabled' => true, 'recipient' => 'client', 'template' => 'tpl-reply'],
|
||||
['trigger_key' => 'sla_breached', 'trigger_label' => 'Przekroczono SLA (powiadom operatora)', 'enabled' => false, 'recipient' => 'operator', 'template' => 'tpl-sla-breach'],
|
||||
] as $setting) {
|
||||
NotificationSetting::query()->create([
|
||||
'trigger_key' => $setting['trigger_key'],
|
||||
'trigger_label' => $setting['trigger_label'],
|
||||
'enabled' => $setting['enabled'],
|
||||
'recipient' => $setting['recipient'],
|
||||
'email_template_id' => $ids[$setting['template']],
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Carries over the real branding/behavior configuration as-is; SMTP and
|
||||
* LDAP connection details are seeded with placeholder example values
|
||||
* instead of production secrets/hosts — an admin fills in the real ones
|
||||
* from the Admin > Konfiguracja panel.
|
||||
*/
|
||||
protected function seedSettings(): void
|
||||
{
|
||||
$settings = [
|
||||
'company_name' => 'Servicedesk',
|
||||
'company_logo_path' => null,
|
||||
'accent_color' => '#2c55e2',
|
||||
'timezone' => 'Europe/Warsaw',
|
||||
'default_status' => 'new',
|
||||
'auto_assign_by_category' => '1',
|
||||
'allow_attachments' => '1',
|
||||
'attachment_max_size_kb' => '10240',
|
||||
'attachment_max_count' => '5',
|
||||
'attachment_max_total_size_kb' => '20480',
|
||||
'attachment_allowed_types' => 'jpg,jpeg,png,pdf,doc,docx,xls,xlsx,zip,txt',
|
||||
'session_lifetime_minutes' => '120',
|
||||
|
||||
'login_notice_type' => 'info',
|
||||
'login_notice_html' => '<p class="ql-align-center"><strong style="color: rgb(230, 0, 0);">Informacja o logowaniu</strong></p><p class="ql-align-center"><span style="color: rgb(255, 255, 255);">Aby uzyskać dostęp do serwisu, użyj swoich standardowych danych służbowych:</span></p><hr><p class="ql-align-center"><strong style="color: rgb(255, 255, 255);">Nazwa użytkownika</strong><span style="color: rgb(255, 255, 255);">: (np. jkowalski)</span></p><p class="ql-align-center"><strong style="color: rgb(255, 255, 255);">Hasło</strong><span style="color: rgb(255, 255, 255);">: Takie samo, jak do komputera</span></p>',
|
||||
|
||||
'email_footer' => '<p>Ta wiadomość została wygenerowana automatycznie przez system {firma} — prosimy na nią nie odpowiadać.</p>',
|
||||
'email_layout_html' => '<div style="max-width:560px;margin:0 auto;padding:32px;background:#ffffff;border:1px solid #e5e5ea;border-radius:12px;font-family:Arial,Helvetica,sans-serif;color:#23252d"><div style="font-size:12px;font-weight:600;text-transform:uppercase;letter-spacing:0.05em;color:#8a8a93;margin-bottom:18px">{firma}</div><div style="font-size:14px;line-height:1.6">{tresc}</div><div style="border-top:1px solid #e5e5ea;margin:24px 0 16px"></div><div style="font-size:12px;line-height:1.5;color:#8a8a93">{stopka}</div></div>',
|
||||
|
||||
'restrict_user_creation_to_ldap' => '0',
|
||||
'restrict_tickets_to_ldap' => '1',
|
||||
|
||||
// LDAP — example connection details, not production credentials.
|
||||
'ldap_enabled' => '1',
|
||||
'ldap_host' => 'ldap.example.com',
|
||||
'ldap_port' => '389',
|
||||
'ldap_base_dn' => 'dc=example,dc=com',
|
||||
'ldap_bind_dn' => 'cn=admin,dc=example,dc=com',
|
||||
'ldap_bind_password' => 'changeme-ldap-password',
|
||||
'ldap_use_ssl' => '0',
|
||||
'ldap_user_filter' => '(uid={0})',
|
||||
'ldap_auto_provision_guests' => '1',
|
||||
|
||||
// SMTP — example connection details, not production credentials.
|
||||
'mail_smtp_enabled' => '1',
|
||||
'mail_smtp_host' => 'smtp.example.com',
|
||||
'mail_smtp_port' => '587',
|
||||
'mail_smtp_encryption' => 'tls',
|
||||
'mail_smtp_username' => 'noreply@example.com',
|
||||
'mail_smtp_password' => 'changeme-smtp-password',
|
||||
'mail_from_address' => 'noreply@example.com',
|
||||
'mail_from_name' => 'Servicedesk',
|
||||
];
|
||||
|
||||
foreach ($settings as $key => $value) {
|
||||
Settings::set($key, $value);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The only account this seeder creates: a local (non-LDAP) admin login
|
||||
* for initial/emergency access. The login form's LDAP lookup falls back
|
||||
* to matching by e-mail + local password (see Livewire\Auth\Login), so
|
||||
* "admin" doubles as both username and e-mail here.
|
||||
*/
|
||||
protected function seedAdminUser(): void
|
||||
{
|
||||
User::query()->create([
|
||||
'name' => 'Administrator',
|
||||
'email' => 'admin@example.com',
|
||||
'password' => 'admin',
|
||||
'roles' => ['admin'],
|
||||
]);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user