Files
servicedesk/src/database/seeders/DatabaseSeeder.php
Kacper 0b06687ea1 v1.1.2
- Real-time updates (Laravel Reverb): live operator queue, live ticket
  chat/detail updates for operator and client, periodic fallback refresh
  with a visible countdown as a backstop for dropped websocket connections.
- SLA automation rules (Admin > Automatyzacja SLA): act on a ticket after
  N minutes of customer silence (change priority/status/team/assignee),
  evaluated every 15 minutes, reusing TicketService's own setters so
  automated changes get the same history/notification/broadcast a manual
  change would.
- New notification: every operator on a matching team gets notified when
  a new ticket lands in one of their subcategories.
- BookStack knowledge-base sidebar now also shown on the client's own
  ticket view (previously operator-only); suggestions everywhere now load
  in after first paint instead of blocking it.
- Client ticket view: shows assigned operator + team; page widened to
  match the operator's.
- Notification bell shows unread only; read notifications disappear
  instead of just dimming.
- Stats dashboard: sectioned layout, new breakdowns (by subcategory, CSAT
  by team/operator, top clients, client x subcategory cross-tab).
- Mobile: nav dropdowns (theme/notifications/profile) now expand full
  width instead of overflowing off-screen below 640px.
- Fixed two bugs that silently disabled all real-time updates (missing
  CSRF header on Echo's private-channel auth; a script-load-order race
  that could miss the livewire:init event) and the mariadb healthcheck
  (world-writable credentials file on this stack's NFS mount).
- Assorted test-suite fixes (roles virtual attribute needs the roles
  table seeded; a few missing seeds/wrong assertions found along the way).

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-22 20:43:05 +02:00

456 lines
27 KiB
PHP

<?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()->firstOrCreate(['key' => $role['key']], $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],
// Used to point at the now-removed "resolved" status, folded into
// "closed" by the status restructure — same target as "Wyślij i
// zamknij" today, kept as a separate quick action for continuity
// with the old hardcoded menu (see ReplyQuickActionsTest).
['label' => 'Wyślij i oznacz jako rozwiązane', 'status_key' => 'closed', 'sort_order' => 2],
['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>';
$csatLink = '<p><a href="{ocena}" rel="noopener noreferrer" target="_blank">Oceń naszą obsługę</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.$csatLink.$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,
],
'tpl-team-new-ticket' => [
'name' => 'Nowe zgłoszenie w zespole', 'trigger_label' => 'Nowe zgłoszenie w zespole — operator',
'subject' => 'Nowe zgłoszenie w Twoim zespole (#{numer})',
'body' => '<p>Cześć,</p><p>Nowe zgłoszenie „{temat}” (#{numer}, kategoria: {kategoria}) trafiło do zespołu {zespol}.</p>'.$link.$footer,
],
];
$ids = [];
foreach ($templates as $key => $tpl) {
$ids[$key] = EmailTemplate::query()->firstOrCreate(['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'],
['trigger_key' => 'ticket_created_team', 'trigger_label' => 'Nowe zgłoszenie w zespole (powiadom operatorów)', 'enabled' => true, 'recipient' => 'operator', 'template' => 'tpl-team-new-ticket'],
] as $setting) {
NotificationSetting::query()->firstOrCreate(['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'],
]);
}
}