@php $tabGroups = [ 'Zgłoszenia' => [ ['key' => 'categories', 'label' => 'Kategorie', 'icon' => 'category'], ['key' => 'fields', 'label' => 'Pola dodatkowe', 'icon' => 'list_alt'], ['key' => 'statuses', 'label' => 'Statusy', 'icon' => 'flag'], ['key' => 'priorities', 'label' => 'Priorytety i SLA', 'icon' => 'priority_high'], ['key' => 'reply-quick-actions', 'label' => 'Szybkie akcje odpowiedzi', 'icon' => 'bolt'], ['key' => 'response-templates', 'label' => 'Szablony odpowiedzi', 'icon' => 'chat'], ['key' => 'automation-rules', 'label' => 'Automatyzacja SLA', 'icon' => 'bolt'], ['key' => 'triggers', 'label' => 'Wyzwalacze', 'icon' => 'rule'], ], 'Zespół' => [ ['key' => 'users', 'label' => 'Użytkownicy', 'icon' => 'group'], ['key' => 'teams', 'label' => 'Zespoły', 'icon' => 'groups'], ['key' => 'user-fields', 'label' => 'Pola dodatkowe', 'icon' => 'badge'], ], 'Ustawienia' => [ ['key' => 'templates', 'label' => 'Szablony e-mail', 'icon' => 'mail'], ['key' => 'email', 'label' => 'Poczta', 'icon' => 'forward_to_inbox'], ['key' => 'branding', 'label' => 'Wygląd i branding', 'icon' => 'palette'], ['key' => 'config', 'label' => 'Konfiguracja', 'icon' => 'settings'], ['key' => 'integrations', 'label' => 'Integracje', 'icon' => 'hub'], ['key' => 'api-keys', 'label' => 'Klucze API', 'icon' => 'vpn_key'], ['key' => 'about', 'label' => 'O aplikacji', 'icon' => 'info'], ], ]; @endphp
@foreach ($tabGroups as $groupLabel => $items)
{{ $groupLabel }}
@foreach ($items as $item) @endforeach
@endforeach
{{-- ================= CATEGORIES ================= --}} @if ($tab === 'categories')

Kategorie i podkategorie

@foreach ($this->categories as $cat)
category @if ($editingCategoryId === $cat->id)
@else

{{ $cat->name }}

@if ($cat->description)

{{ $cat->description }}

@endif
@endif
@foreach ($cat->subcategories as $i => $sub) @endforeach
Podkategoria
folder{{ $sub->name }}
@if ($sub->description)
{{ $sub->description }}
@endif
Pola: {{ $sub->customFields->pluck('label')->implode(', ') ?: '—' }}
@endforeach
@endif {{-- ================= FIELDS ================= --}} @if ($tab === 'fields')

Pola dodatkowe

@if ($this->customFields->isNotEmpty())
@foreach ($this->customFields as $f) @endforeach
EtykietaTypWymagane
{{ $f->label }} {{ $f->typeLabel() }} {{ $f->required ? 'Tak' : 'Nie' }}
@else

Brak pól dodatkowych. Dodaj pierwsze pole używając przycisku wyżej.

@endif @endif {{-- ================= USERS ================= --}} @if ($tab === 'users')

Użytkownicy

Logowanie odbywa się przez LDAP/AD. Zaproszenie tworzy tu tylko rolę i przypisanie — po pierwszym logowaniu tożsamość LDAP zwiąże się automatycznie z tym adresem e-mail. „Synchronizuj z LDAP” pobiera od razu wartości pól dodatkowych (zakładka Pola dodatkowe) dla wszystkich istniejących użytkowników.

@if ($ldapSyncResult)
{{ $ldapSyncResult }}
@endif
@foreach ($this->users as $u) @endforeach
Imię i nazwiskoE-mailRolaZespoły
{{ $u->name }} {{ $u->email }}
@forelse ($u->teams as $tm) {{ $tm->name }} @empty — @endforelse
@if ($u->id !== auth()->id()) @endif
@endif {{-- ================= TEAMS ================= --}} @if ($tab === 'teams')

Zespoły

@foreach ($this->teams as $tm)
{{ $tm->name }}
Członkowie: {{ $tm->members->pluck('name')->implode(', ') ?: '—' }}
Podkategorie: {{ $tm->subcategories->map(fn($s) => $s->category->name.' / '.$s->name)->implode(', ') ?: '—' }}
@endforeach
@endif {{-- ================= USER FIELDS ================= --}} @if ($tab === 'user-fields')

Pola dodatkowe (użytkownicy)

Te pola pojawiają się przy edycji użytkownika (nie przy zapraszaniu nowego). Jeśli podasz atrybut LDAP, wartość pobierze się automatycznie przy logowaniu oraz przyciskiem „Synchronizuj z LDAP” w zakładce Użytkownicy.

@if ($this->userFields->isNotEmpty())
@foreach ($this->userFields as $f) @endforeach
NazwaOpisTypAtrybut LDAP
{{ $f->label }} {{ $f->description ?: '—' }} {{ $f->typeLabel() }} {{ $f->ldap_attribute ?: '—' }}
@else

Brak pól dodatkowych. Dodaj pierwsze pole używając przycisku wyżej.

@endif @endif {{-- ================= REPLY QUICK ACTIONS ================= --}} @if ($tab === 'reply-quick-actions')

Szybkie akcje odpowiedzi

Te opcje pojawiają się w menu obok przycisku „Wyślij odpowiedź” w panelu operatora.

@if ($this->replyQuickActions->isNotEmpty())
@foreach ($this->replyQuickActions as $qa) @endforeach
Nazwa przyciskuZmienia status na
{{ $qa->label }} @if ($qa->status_key) {{ $qa->statusLabel() }} @else Nie zmieniaj @endif
@else

Brak szybkich akcji. Dodaj pierwszą używając przycisku wyżej.

@endif @endif {{-- ================= RESPONSE TEMPLATES ================= --}} @if ($tab === 'response-templates')

Szablony odpowiedzi

Te szablony operator może wstawić do treści odpowiedzi z listy „Wstaw szablon odpowiedzi…” w widoku zgłoszenia.

@if ($this->responseTemplates->isNotEmpty())
@foreach ($this->responseTemplates as $rt)
{{ $rt->label }}

{{ \Illuminate\Support\Str::limit($rt->body, 200) }}

@endforeach
@else

Brak szablonów odpowiedzi. Dodaj pierwszy używając przycisku wyżej.

@endif @endif {{-- ================= AUTOMATION RULES ================= --}} @if ($tab === 'automation-rules')

Automatyzacja SLA

Co 15 minut sprawdzane jest, czy zgłoszenie milczy (brak odpowiedzi klienta) dłużej niż próg reguły — jeśli tak (i pasuje do opcjonalnego zawężenia), wykonywana jest wybrana akcja. Reguła nie powtarza się dla tego samego zgłoszenia, dopóki klient znów nie napisze albo zgłoszenie nie zostanie zamknięte i otwarte ponownie.

@if ($this->automationRules->isNotEmpty())
@foreach ($this->automationRules as $rule)
{{ $rule->label }} Brak odpowiedzi klienta ≥ {{ $rule->condition_minutes }} min. → {{ match($rule->action_type) { 'change_priority' => 'zmień priorytet', 'change_status' => 'zmień status', 'change_team' => 'zmień zespół', 'change_assignee' => 'zmień przypisanie', default => $rule->action_type } }}
@endforeach
@else

Brak reguł automatyzacji. Dodaj pierwszą używając przycisku wyżej.

@endif @endif {{-- ================= TRIGGERS ================= --}} @if ($tab === 'triggers') @endif {{-- ================= STATUSES ================= --}} @if ($tab === 'statuses')

Statusy

Nowy, Otwarty i Zamknięty to 3 stałe grupy (nie da się ich usunąć). Pozostałe statusy to tylko dodatkowe warianty grupy „Otwarty” — możesz je dowolnie edytować, usuwać i dodawać nowe.

@foreach ($this->statuses as $i => $s) @endforeach
GrupaNazwaKolorPodgląd
{{ match ($s->stage) { 'new' => 'Nowy', 'closed' => 'Zamknięty', default => 'Otwarty' } }} {{ $s->label }}
@unless ($s->locked) @endunless
@error('newStatusKey') {{ $message }} @enderror @endif {{-- ================= PRIORITIES ================= --}} @if ($tab === 'priorities')

Priorytety i SLA

Edytuj nazwy, kolory i czasy SLA (w minutach), dodawaj i usuwaj wartości — obowiązuje we wszystkich panelach. Czas rozwiązania równy 0 oznacza brak SLA dla danego priorytetu — zgłoszenie nigdy nie zostanie oznaczone jako przeterminowane.

@foreach ($this->priorities as $i => $p) @endforeach
PriorytetKolorPodglądCzas odpowiedzi (min)Czas rozwiązania (min)
{{ $p->label }}
@error('newPriorityKey') {{ $message }} @enderror @endif {{-- ================= EMAIL TEMPLATES ================= --}} @if ($tab === 'templates')

Powiadomienia e-mail

Każde zdarzenie ma stały, przypisany na stałe szablon — możesz go dowolnie edytować, ale nie zmienić na inny. Wyłącz przełącznik, żeby dana wiadomość nigdy nie była wysyłana.

@foreach ($this->notificationSettings as $ns) @endforeach
ZdarzenieOdbiorcaWysyłane
{{ $ns->trigger_label }} {{ $ns->recipient === 'operator' ? 'Operator' : 'Zgłaszający' }} enabled) wire:click="toggleNotificationEnabled({{ $ns->id }})">
@endif {{-- ================= E-MAIL ================= --}} @if ($tab === 'email')

Wygląd wiadomości e-mail

Każde powiadomienie wysyłane jest w stałym „pudełku” (nazwa firmy, ramka, treść zdarzenia) — tu edytujesz tylko jego stopkę. Po prawej — podgląd na żywo na przykładowym zgłoszeniu.

Wstawiana w pudełku w miejscu {stopka}; dopisywana do każdego wysyłanego powiadomienia.
Podgląd przykładowego e-maila
@endif {{-- ================= BRANDING ================= --}} @if ($tab === 'branding')

Wygląd i branding

Motyw jasny / ciemny / auto ustawia się przełącznikiem w nagłówku — dotyczy całej aplikacji.

Zastępuje domyślny fioletowy kolor przycisków, linków i etykiet w całej aplikacji.

Widoczne na ekranie logowania. Bez własnego pliku używany jest domyślny znak.

@if (\App\Support\Settings::get('company_logo_path')) @endif

Ikona karty przeglądarki. Bez własnego pliku używany jest domyślny znak.

@if ($faviconError) {{ $faviconError }} @endif
@if (\App\Support\Settings::get('favicon_path')) @endif

Wyświetlany nad formularzem logowania, nad polami „Nazwa użytkownika” / „Hasło”. Obsługuje wiele akapitów, pogrubienia, listy itd.

@foreach (\App\Support\Settings::loginNoticeTypes() as $key => $label) @endforeach
Podgląd
@endif {{-- ================= CONFIG ================= --}} @if ($tab === 'config')

Konfiguracja

Zgłoszenia

ID z bazy: {{ $this->ticketNumberPreview['id'] }} → podgląd numeru: {{ $this->ticketNumberPreview['formatted'] }}

Załączniki

@if ($systemConfig['allowAttachments'])
@endif

Sesja i strefa czasowa

Aktualny czas:

Częstotliwość odświeżania i harmonogramu

Odświeżanie w przeglądarce

Sekundy między automatycznymi odświeżeniami w przeglądarce (niezależnie od odświeżenia na żądanie po kliknięciu w licznik).

Zadania w tle

Minuty między uruchomieniami zadań w tle. Zmiana obowiązuje od najbliższego uruchomienia harmonogramu (co minutę), bez potrzeby restartu.

@endif {{-- ================= INTEGRATIONS ================= --}} @if ($tab === 'integrations')

Integracje

LDAP / Active Directory

@if ($ldapConfig['enabled'])
@if ($ldapTestResult === 'ok')
check_circlePołączenie OK
@elseif ($ldapTestResult === 'error')
errorBłąd połączenia
@endif
@else @endif

Baza wiedzy BookStack

Po włączeniu, podczas tworzenia zgłoszenia klientom i operatorom podpowiadane będą pasujące artykuły z BookStack na podstawie wybranej kategorii/podkategorii. @if ($bookstackConfig['enabled'])

Token API generuje się w BookStack: Profil → Ustawienia API. Użytkownik/rola właściciela tokenu musi mieć uprawnienie „Access System API”.

@if (empty($bookstackConfig['searchTypes']))

Nic nie zaznaczono — przy zapisie zostaną użyte wszystkie typy.

@endif

„Tagi” dopasowują artykuły oznaczone w BookStack tagiem o nazwie zgodnej z kategorią/podkategorią zgłoszenia (np. tag „Drukarki” dla podkategorii „Drukarki”).

@if (count($this->bookstackShelves))
@foreach ($this->bookstackShelves as $shelf) @endforeach
@else

Brak półek do wyświetlenia — sprawdź, czy połączenie działa (przycisk „Testuj połączenie” niżej), albo zapisz konfigurację, żeby odświeżyć listę.

@endif

Tylko książki/strony z zaznaczonych półek mogą pojawić się jako podpowiedzi podczas tworzenia zgłoszenia (klient, operator, formularz gościa). Jeśli żadna półka nie jest zaznaczona, podpowiedzi się nie pojawią.

@if (count($this->bookstackShelves))
@foreach ($this->bookstackShelves as $shelf) @endforeach
@else

Brak półek do wyświetlenia — sprawdź, czy połączenie działa (przycisk „Testuj połączenie” niżej), albo zapisz konfigurację, żeby odświeżyć listę.

@endif

Niezależna lista — kontroluje, co widzi operator w bocznym panelu po otwarciu istniejącego zgłoszenia (link do artykułu lub przycisk kopiowania linku). Jeśli żadna półka nie jest zaznaczona, panel się nie pokaże.

Bez zaznaczenia podpowiedzi widoczne są tylko przy tworzeniu zgłoszenia przez zalogowanego klienta lub operatora. Wyłącz tylko jeśli instancja BookStack korzysta z certyfikatu self-signed / z prywatnego CA.

Używa integracji AI (skonfigurowanej w karcie „Integracja AI” obok) do otagowania książek/stron/rozdziałów nazwami pasujących podkategorii helpdesku — bez tego wyszukiwanie „po tagach” nic nie znajdzie. „Otaguj nową treść” pomija już otagowane pozycje; „Otaguj wszystko ponownie” klasyfikuje od nowa całą wiki (dłużej, więcej zapytań do AI).

@if ($bookstackTagResult !== null)
Przeskanowano {{ $bookstackTagResult['scanned'] }}, otagowano {{ $bookstackTagResult['tagged'] }}, pominięto {{ $bookstackTagResult['skipped'] }}, nieudanych paczek {{ $bookstackTagResult['failed_batches'] }}.
@elseif ($bookstackTagError)
error{{ $bookstackTagError }}
@endif
@if ($bookstackTestResult === 'ok')
check_circlePołączenie OK
@elseif ($bookstackTestResult === 'error')
errorBłąd połączenia{{ $bookstackTestMessage ? ': '.$bookstackTestMessage : '' }}
@endif
@else @endif

Snipe-IT (ewidencja sprzętu)

Pokazuje sprzęt przypisany do zgłaszającego przy tworzeniu i przeglądaniu zgłoszenia oraz pozwala powiązać zgłoszenie z konkretnym urządzeniem z ewidencji Snipe-IT. @if ($snipeitConfig['enabled'])

Osobisty token API generuje się w Snipe-IT: profil użytkownika → „Create New Token”.

Zaznacz tylko, jeśli instancja Snipe-IT korzysta z certyfikatu self-signed / z prywatnego CA.

Przy tworzeniu zgłoszenia klient zobaczy listę swojego sprzętu z Snipe-IT (dopasowanego po adresie e-mail) i będzie mógł je powiązać ze zgłoszeniem.

@if ($snipeitConfig['clientCanSelectAsset'])

Wybór sprzętu pojawi się klientowi tylko przy tworzeniu zgłoszenia w zaznaczonych tu podkategoriach. Jeśli nic nie jest zaznaczone, opcja nie pojawi się w żadnej podkategorii.

@endif

Obie funkcje pojawiają się w bocznym panelu widoku zgłoszenia operatora — przeszukiwanie inwentarza jako pole wyszukiwania z przyciskiem „Szukaj”, nie osobna podstrona. Odpięcie już powiązanego urządzenia jest zawsze dostępne dla operatora, niezależnie od tych dwóch ustawień.

@if ($snipeitTestResult === 'ok')
check_circlePołączenie OK
@elseif ($snipeitTestResult === 'error')
errorBłąd połączenia{{ $snipeitTestMessage ? ': '.$snipeitTestMessage : '' }}
@endif
@else @endif

Integracja AI

Ogólne połączenie z dostawcą modelu językowego (API kompatybilne z OpenAI — Groq, OpenAI, lokalny Ollama itp.), wykorzystywane m.in. do automatycznego tagowania treści w BookStack. @if ($aiConfig['enabled'])

Zostaw puste dla lokalnych instancji bez autoryzacji (np. Ollama).

Wyłącz tylko jeśli instancja (np. lokalny Ollama) korzysta z certyfikatu self-signed / z prywatnego CA.
@if ($aiTestResult === 'ok')
check_circlePołączenie OK
@elseif ($aiTestResult === 'error')
errorBłąd połączenia{{ $aiTestMessage ? ': '.$aiTestMessage : '' }}
@endif
@else @endif

Automatyzacja AI dla zgłoszeń

Wymaga włączonej integracji AI (karta obok). Zgłoszenia są przetwarzane w tle, cyklicznie co kilka minut — nie spowalnia to tworzenia zgłoszenia przez klienta.

Każde zgłoszenie jest sprawdzane tylko raz — zastosowane zmiany trafiają do historii zgłoszenia z adnotacją „Automatyzacja: klasyfikacja AI”.

Widoczne wyłącznie w panelu operatora, w bocznym panelu zgłoszenia. Domyślnie odświeżane cyklicznie (co kilka minut, wraz z pozostałą automatyzacją AI powyżej).

Zamiast czekać na najbliższy cykl automatyzacji — dotyczy odpowiedzi operatora, klienta i notatek wewnętrznych.

Model musi zwrócić obiekt JSON z kluczami "summary" i "suggested_action" — nie zmieniaj tego wymogu, chyba że wiadomo, że nowy dostawca/model obsłuży to inaczej.

@endif {{-- ================= ABOUT ================= --}} {{-- ================= API KEYS ================= --}} @if ($tab === 'api-keys') @endif @if ($tab === 'about')

O aplikacji

Aplikacja{{ \App\Support\Settings::get('company_name') }}
Wersja{{ config('app.version') ?: '—' }}
Kontakt wsparcia{{ config('app.author_contact') ?: '—' }}
@endif
{{-- ================= DIALOGS ================= --}} @if ($replyQuickActionFormOpen)
{{ $replyQuickActionForm['id'] ? 'Edytuj szybką akcję' : 'Nowa szybka akcja' }}
@error('replyQuickActionForm.label') {{ $message }} @enderror
@error('replyQuickActionForm.sortOrder') {{ $message }} @enderror
@endif @if ($responseTemplateFormOpen)
{{ $responseTemplateForm['id'] ? 'Edytuj szablon odpowiedzi' : 'Nowy szablon odpowiedzi' }}
@error('responseTemplateForm.label') {{ $message }} @enderror
@error('responseTemplateForm.body') {{ $message }} @enderror
@endif @if ($automationRuleFormOpen)
{{ $automationRuleForm['id'] ? 'Edytuj regułę automatyzacji' : 'Nowa reguła automatyzacji' }}
@error('automationRuleForm.label') {{ $message }} @enderror
@error('automationRuleForm.condition_minutes') {{ $message }} @enderror
@switch ($automationRuleForm['action_type']) @case ('change_priority') @break @case ('change_status') @break @case ('change_team') @break @case ('change_assignee') @break @endswitch
@error('automationRuleForm.action_value') {{ $message }} @enderror
@endif @if ($this->editingTemplate)
Edytuj szablon — {{ $this->editingTemplate->name }}

Dostępne zmienne: {numer}, {imie}, {temat}, {status}, {kategoria}, {priorytet}, {zespol}, {operator}, {link} — {link} wstawia bezpośredni link do zgłoszenia. Ta treść trafia do wspólnego szablonu-pudełka (u góry listy) w miejscu {tresc}.

@endif @if ($fieldFormOpen)
{{ $fieldForm['id'] ? 'Edytuj pole' : 'Nowe pole dodatkowe' }}
@if ($fieldForm['type'] === 'select')
@endif
@endif @if ($userFieldFormOpen)
{{ $userFieldForm['id'] ? 'Edytuj pole' : 'Nowe pole dodatkowe użytkownika' }}
@if ($userFieldForm['type'] === 'select')
@endif

Jeśli podane, wartość tego pola będzie automatycznie pobierana z LDAP dla każdego użytkownika.

@endif @if ($subcategoryEditFormOpen)
Edytuj podkategorię
Priorytet nadawany automatycznie zgłoszeniom z tej podkategorii.
@foreach ($subcategoryEditForm['fieldIds'] as $i => $fieldId) @php $field = $this->customFields->firstWhere('id', $fieldId); @endphp @continue(! $field)
@endforeach @foreach ($this->customFields as $field) @continue(in_array($field->id, $subcategoryEditForm['fieldIds'], true))
@endforeach
@endif @if ($this->viewingUser)
{{ $this->viewingUser->name }}
{{ $this->viewingUser->email }}
@forelse ($this->viewingUser->roles ?? [] as $role) {{ match($role) { 'client' => 'Klient', 'operator' => 'Operator', 'admin' => 'Administrator', default => $role } }} @empty — @endforelse
@forelse ($this->viewingUser->teams as $tm) {{ $tm->name }} @empty — @endforelse
@if ($this->userFields->isNotEmpty())
@foreach ($this->userFields as $f) @php $value = $this->viewingUser->custom_field_values[$f->id] ?? null; @endphp
{{ $f->type === 'checkbox' ? ($value ? 'Tak' : 'Nie') : ((string) $value !== '' && $value !== null ? $value : '—') }}
@endforeach
@endif
@endif @if ($userFormOpen)
{{ $userForm['id'] ? 'Edytuj użytkownika' : 'Zaproś użytkownika' }}
@error('userForm.email') {{ $message }} @enderror

Podaj hasło, aby ten użytkownik mógł logować się lokalnie, niezależnie od LDAP/AD — przydatne dla kont serwisowych lub gdy LDAP jest niedostępny.

@error('userForm.password') {{ $message }} @enderror
@error('userForm.roles') {{ $message }} @enderror
@if ($userForm['id'] && $this->userFields->isNotEmpty())
@foreach ($this->userFields as $f)
@if ($f->type === 'textarea') @elseif ($f->type === 'select') @elseif ($f->type === 'checkbox') @elseif ($f->type === 'date') @elseif ($f->type === 'number') @else @endif @if ($f->description && $f->type !== 'checkbox') {{ $f->description }} @endif
@endforeach
@endif
@endif @if ($teamFormOpen)
{{ $teamForm['id'] ? 'Edytuj zespół' : 'Nowy zespół' }}
@endif @if ($pendingDeleteType)
Potwierdź usunięcie
{{ $pendingDeleteMessage }}
@endif