Co nowego: - Kolumny kolejki operatora: dwie nowe (ID, e-mail) obok istniejących, oraz możliwość zmiany kolejności widocznych kolumn strzałkami ↑/↓ w picker „Kolumny” — nie tylko włączanie/wyłączanie. Kolejność zapamiętywana jest per operator tak samo jak dotąd widoczność. - Wybór sprzętu klienta (Snipe-IT): druga lista dozwolonych kategorii obok istniejącej listy podkategorii — pozwala objąć od razu wszystkie podkategorie danej kategorii jednym zaznaczeniem. - Pulpit klienta i widok zgłoszenia operatora pamiętają teraz aktywną zakładkę, więc „Wróć do listy” wraca do tej samej, a nie zawsze do domyślnej. Poprawki: - Paginacja (kolejka operatora, pulpit klienta) używała domyślnego, szarego stylu Laravela reagującego na motyw systemu/przeglądarki, a nie przełącznik jasny/ciemny w aplikacji — stąd ciemne przyciski nawet w trybie jasnym. Podmieniony na własny widok zgodny z kolorami aplikacji (w tym własne tło/border każdego przycisku i wyśrodkowanie na telefonie). - Kolorystyka boksu z informacją o logowaniu nie zmienia już odcienia między trybem jasnym i ciemnym (wcześniej pochodziła z --color-accent) — teraz stałe, ciemne tło w obu trybach, więc kolory tekstu ustawione przez admina (np. biały) zostają czytelne niezależnie od motywu. Poszerzona karta logowania (380px → 480px). - Lista zgłoszeń klienta: etykiety priorytetu/statusu nie zawijają się już do osobnej linii przy długim temacie na wąskich ekranach — zostają przypięte do prawej, a temat zawija się we własnej kolumnie. - Liczniki czasu pracy (resumeTimer/stopTimer/...) nie dotykają już updated_at — samo otwarcie zgłoszenia nie liczy się jako aktualizacja. Kolejka operatora i pulpit klienta domyślnie sortują po dacie utworzenia z tego samego powodu. Zaktualizowana dokumentacja: README, CLAUDE.md, ARCHITECTURE.md, CHANGELOG.md, wiki/admin, wiki/client, wiki/operator. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
615 lines
34 KiB
Markdown
615 lines
34 KiB
Markdown
# Changelog
|
||
|
||
All notable changes to this project are documented in this file. Format loosely
|
||
follows [Keep a Changelog](https://keepachangelog.com/en/1.1.0/).
|
||
|
||
## [1.5.1] - 2026-08-06
|
||
|
||
### Added
|
||
|
||
- **Operator queue columns**: two new optional columns, ID (raw DB id) and
|
||
e-mail, alongside the existing set; visible columns can now also be
|
||
**reordered** with ↑/↓ arrows next to each entry in the "Kolumny" picker,
|
||
not just shown/hidden — order is remembered per operator the same way
|
||
visibility already was (`users.operator_queue_columns`).
|
||
- **SnipeIT client asset picker**: a second, category-level allow-list
|
||
(`snipeit_client_asset_category_ids`) alongside the existing subcategory
|
||
one — lets an admin cover every subcategory of a category in one click
|
||
instead of ticking each one individually. The two lists are additive.
|
||
- Client dashboard and operator ticket-detail page now remember which tab
|
||
(Bieżące/Archiwum, or whichever queue tab) was active, so "Wróć do listy"
|
||
returns to it instead of always resetting to the default.
|
||
|
||
### Fixed
|
||
|
||
- **Pagination controls** (operator queue, client dashboard) no longer
|
||
render as Laravel's stock gray Tailwind styling, which only reacted to the
|
||
browser/OS's `prefers-color-scheme` and stayed dark even when the app's own
|
||
light/dark toggle was set to light. They're now a themed override
|
||
(`resources/views/vendor/livewire/tailwind.blade.php` — pagination here
|
||
actually renders through Livewire's own pagination view, not Laravel's
|
||
default) styled with the app's own light/dark CSS variables, with visible
|
||
per-button background/border and a centered layout on mobile.
|
||
- **Login notice box** background/border no longer shift hue between light
|
||
and dark mode (previously derived from `--color-accent`, which differs per
|
||
theme) — it's now one fixed dark color in both themes, so admin-picked
|
||
text colors (e.g. white) stay legible regardless of the viewer's theme.
|
||
Login card widened (380px → 480px).
|
||
- **Client dashboard ticket list**: priority/status badges no longer wrap
|
||
onto their own left-aligned line below a long ticket subject on narrow
|
||
screens — they stay pinned to the right while the subject text wraps
|
||
within its own column instead.
|
||
- Timer bookkeeping (`resumeTimer()`/`stopTimer()`/etc.) no longer touches
|
||
`updated_at` — merely opening a ticket (or the background timer
|
||
starting/stopping) no longer counted as an update, which used to drown out
|
||
genuinely stale tickets in any list sorted by that column. The operator
|
||
queue and client dashboard both now default to sorting by creation date
|
||
instead for the same reason.
|
||
- Admin panel and SnipeIT integration config test coverage extended for the
|
||
new category-level allow-list.
|
||
|
||
## [1.5.0] - 2026-08-05
|
||
|
||
### Added
|
||
|
||
- **Admin log viewer** (Admin > Logi) — browse `storage/logs/*.log` from the
|
||
admin panel without shell access to the container: a file picker (size +
|
||
last-modified, most recent first), level/free-text/entry-count filters, and
|
||
an optional 5s auto-refresh. Read-only, admin-only; reads only the tail of
|
||
large files to keep it fast.
|
||
- **"Bez kategorii" filter** in the operator queue's category dropdown —
|
||
isolates tickets with neither a category nor subcategory assigned (e.g. an
|
||
IMAP mailbox routed to nothing in particular), previously only reachable by
|
||
scanning the unfiltered list.
|
||
|
||
### Changed
|
||
|
||
- **Hesk import tool** (`scripts/hesk-import/`) no longer auto-creates client
|
||
accounts for unrecognized requester e-mails — the servicedesk user base is
|
||
now treated as authoritative, so a Hesk ticket whose requester has no
|
||
matching account is skipped instead (reported at the end, with the list of
|
||
skipped e-mails). Hesk staff replies/notes and ticket ownership are now
|
||
linked to real operator/admin accounts when the staff e-mail matches one.
|
||
Two new backfill flags cover tickets imported before these existed:
|
||
`--assign-operators` (sets `assignee_id` from Hesk's ticket owner, never
|
||
overwriting a manual reassignment) and `--fix-closed-dates` (corrects a
|
||
closed ticket's date to Hesk's own `closedat` column instead of the
|
||
drifting `lastchange`, and adds the missing "Zamknięte" history entry).
|
||
Every newly imported ticket also records its source Hesk id
|
||
(`tickets.hesk_ticket_id`, unique) as a second, DB-level guard against
|
||
duplicate imports on top of the existing state file. See
|
||
`scripts/hesk-import/README.md` for details.
|
||
- Internal database cleanup: removed three columns confirmed unused against
|
||
live data (`users.remember_token`, `users.email_verified_at`,
|
||
`email_templates.trigger_label`); moved custom field values, AI
|
||
triage/summary state, and the linked Snipe-IT asset off the `tickets` row
|
||
into three dedicated one-to-one tables (`ticket_field_values`,
|
||
`ticket_ai_summaries`, `ticket_snipeit_assets`) — no visible behavior
|
||
change, but custom field values are now efficiently queryable instead of
|
||
living only in a JSON blob, and the `tickets` row itself is narrower; added
|
||
missing reverse indexes on 4 pivot tables (`team_subcategory`, `role_user`,
|
||
`team_user`, `custom_field_subcategory`); `tickets.source` and
|
||
`ticket_messages.source` now validate against a known set of values
|
||
instead of silently accepting any string.
|
||
|
||
### Fixed
|
||
|
||
- The operator statistics dashboard's category/subcategory breakdown (and
|
||
its category filter) only counted tickets routed through a subcategory —
|
||
a ticket routed to a whole category with no subcategory (e.g. via an IMAP
|
||
mailbox routed to "całą kategorię") was silently excluded from those
|
||
charts and from filtering by that category. Now counted correctly, with
|
||
bare-category tickets shown as their own "(bez podkategorii)" row in the
|
||
subcategory breakdown.
|
||
- A JavaScript error (and stray background timers left running) could occur
|
||
when navigating away from a page with an active countdown/timer widget —
|
||
ticket/queue auto-refresh, the theme switcher, file-attachment
|
||
drag-and-drop. Most consequential in the operator ticket time tracker,
|
||
where it could throw console errors and momentarily break other UI
|
||
elements (e.g. dropdown menus) after leaving a ticket with the timer
|
||
running.
|
||
|
||
## [1.4.0] - 2026-08-05
|
||
|
||
### Added
|
||
|
||
- **Active Directory support for LDAP auth** (Admin > Integracje > "LDAP /
|
||
Active Directory") — a "Typ katalogu" dropdown switches between LLDAP/
|
||
OpenLDAP (the original, still the default) and Active Directory. AD uses a
|
||
different schema (no inetOrgPerson/posixAccount, a binary `objectGUID`
|
||
instead of `entryUUID`) and login attribute (`sAMAccountName`, not `uid`);
|
||
both are now auto-detected from the directory type instead of requiring
|
||
LLDAP's schema everywhere.
|
||
- **Operator client search** (Operator > Klienci) — a dedicated search page
|
||
(name or e-mail, any account, not just role=client) showing each match's
|
||
role badges and ticket count, linking straight into the queue pre-filtered
|
||
to that customer.
|
||
- **Paginated ticket lists** — the operator queue (50/page) and client
|
||
dashboard (20/page, current/archive tracked as separate pages so switching
|
||
tabs doesn't lose your place) no longer render every matching ticket at
|
||
once; sorting still happens over the full filtered result first.
|
||
- **Command-palette global search (Ctrl+K / Cmd+K)** — searches tickets from
|
||
anywhere in the app, scoped to what the searching user can actually see
|
||
(operators/admins search everything visible to them, clients only their
|
||
own). Supports Gmail-style operators — `od:` (reporter), `temat:`
|
||
(subject only), `treść:`/`tresc:` (message content only), `numer:`/`nr:`
|
||
(ticket number), combinable and AND'd together (`od:kacper
|
||
temat:drukarka`) — plain text with no operator still searches everything
|
||
as before. A "Szukaj" button in the operator/admin sidebars opens the same
|
||
dialog for anyone who doesn't know the shortcut.
|
||
- **Recently viewed tickets** (operator sidebar) — the last 6 tickets an
|
||
operator actually opened, most-recent first, re-bumped (not duplicated) on
|
||
a repeat visit.
|
||
- **Navbar redesign: panel switcher.** The old dropdown-only role switcher
|
||
(in the profile menu) and the plain "Panel Klienta/Operatora/Administratora"
|
||
text label are replaced by a single segmented control centered in the top
|
||
bar — Klient / Operator / Administrator — showing only the areas the
|
||
logged-in user actually holds, highlighting the current one, and sliding a
|
||
preview to whichever option is hovered before you click. Adapts to a
|
||
full-width row below the icons on narrow screens instead of overlapping
|
||
them.
|
||
- **Richer profile dropdown** — now shows the account's name, e-mail, and
|
||
role badges above the existing Powiadomienia/Wyloguj się links.
|
||
- **Per-page browser tab titles** — every page sets its own `<title>`
|
||
(e.g. the ticket subject, the selected queue, the active admin tab)
|
||
instead of every tab just showing the company name, always anchored with
|
||
the company name as a suffix so it's still identifiable once the browser
|
||
truncates a long tab title.
|
||
- **Hesk 3.x historical import** (`scripts/hesk-import/`) — a one-time,
|
||
read-only migration of tickets (with full reply/note history) from an old
|
||
Hesk helpdesk database, filtered by e-mail domain. Dry-run by default,
|
||
resumable, auto-maps categories (exact name match) and teams (when
|
||
unambiguous). See `scripts/hesk-import/README.md`.
|
||
|
||
### Fixed
|
||
|
||
- Pagination controls ("« Poprzednia" / "Następna »") were showing the raw
|
||
translation keys `pagination.previous`/`pagination.next` instead of
|
||
actual text — the app's `APP_LOCALE=pl` had no matching `lang/pl/`
|
||
translation file and no English fallback (`APP_FALLBACK_LOCALE` is also
|
||
`pl`), so Laravel had nothing to resolve those strings to.
|
||
|
||
## [1.3.0] - 2026-07-27
|
||
|
||
### Added
|
||
|
||
- **Snipe-IT asset inventory integration** (Admin > Integracje), optional and
|
||
off by default — connects to a Snipe-IT instance by API address + personal
|
||
API token (plus a "Nie sprawdzaj SSL" toggle for self-signed instances) and
|
||
surfaces three independently switchable capabilities:
|
||
- **Klient może wybrać sprzęt, którego dotyczy zgłoszenie** — while
|
||
creating a ticket, a client sees the devices checked out to them in
|
||
Snipe-IT (matched by e-mail) and can pick the one the ticket is about.
|
||
Scoped to admin-selected subcategories via a multi-select picker that
|
||
only appears once this is turned on — same "nothing shows until
|
||
explicitly opted in" convention as BookStack's shelf allow-lists.
|
||
- **Operator może zobaczyć sprzęt zgłaszającego w widoku zgłoszenia** — the
|
||
same per-requester asset list, shown in a sidebar card on the ticket
|
||
view, with a "Powiąż" button per item.
|
||
- **Zezwól operatorowi na przeszukiwanie całego inwentarza** — a search
|
||
box + button in the same sidebar (not a separate page) letting an
|
||
operator link any asset in Snipe-IT, not just the requester's own — for
|
||
shared equipment like printers.
|
||
- A linked asset shows live status/category/current assignment (fetched
|
||
fresh from Snipe-IT, not just the cached label) with an "Odepnij" button
|
||
that stays available to the operator regardless of the two toggles above
|
||
— clearing an existing link is a correction, not new Snipe-IT access.
|
||
Every asset is displayed as "numer środka - numer seryjny - producent
|
||
model" plus its Snipe-IT category, joining whichever of those pieces are
|
||
actually present.
|
||
- **AI summary: manual regenerate + regenerate on new message.** A
|
||
"Wygeneruj teraz" button now sits on the operator's "Podsumowanie AI" card
|
||
for an immediate, on-demand refresh. Separately, a new admin toggle
|
||
("Regeneruj podsumowanie od razu po każdej nowej wiadomości", off by
|
||
default) re-runs the summary right after any reply/note lands on a ticket,
|
||
instead of only ever picking it up on the next scheduled
|
||
`ai:run-ticket-automation` sweep. The transcript sent to the model now also
|
||
includes the ticket's own opening body text (previously only the reply
|
||
thread), fixing summaries silently missing the original request on long
|
||
tickets whose first message had scrolled out of the transcript window.
|
||
|
||
### Fixed
|
||
|
||
- The status dropdown in the operator ticket view could keep showing the
|
||
pre-change status after sending a reply via a status-changing quick action
|
||
(e.g. "Wyślij i oznacz jako rozwiązane") until the next full page load — a
|
||
Livewire/Alpine-morph quirk for `<select>` elements bound via `wire:change`
|
||
rather than `wire:model`. Fixed by keying the element to the status value
|
||
so the DOM node is force-replaced instead of morphed.
|
||
|
||
## [1.2.2] - 2026-07-24
|
||
|
||
### Added
|
||
|
||
- Subcategories can now be reordered within their category — up/down arrow
|
||
buttons next to "Edytuj" in Admin > Kategorie, right beside each
|
||
subcategory's edit/delete buttons. The order set there is used everywhere a
|
||
subcategory list is shown (subcategory pickers, admin listings, etc.), not
|
||
just the admin panel itself.
|
||
|
||
## [1.2.1] - 2026-07-24
|
||
|
||
### Added
|
||
|
||
- **Generic AI integration** (Admin > Integracje > "Integracja AI"), optional
|
||
and off by default — an OpenAI-compatible `/chat/completions` client (works
|
||
against Groq, OpenAI itself, or a self-hosted Ollama instance) configured by
|
||
base URL, optional API key, model name, and an SSL-verification toggle for
|
||
self-signed local endpoints. Not tied to any one feature — it's the shared
|
||
foundation for the two AI-driven features below, and for anything else that
|
||
wants an LLM call in the future.
|
||
- **BookStack automatic content tagging (AI)** — a "Otaguj nową treść"/"Otaguj
|
||
wszystko ponownie (force)" button pair in the BookStack card, plus
|
||
`php artisan bookstack:tag-content` (`--dry-run`/`--force`/`--limit=N`) for
|
||
the command line. Uses the AI integration above to classify every
|
||
book/chapter/page's title+content against the current list of helpdesk
|
||
subcategories and tags matching ones by name — idempotent by default
|
||
(skips already-tagged content), so re-running after adding a few pages is
|
||
cheap. This is what gives the BookStack "search by tags" option below
|
||
something to actually match against.
|
||
- **BookStack search refinement** — "Przeszukuj" is now three independent
|
||
checkboxes (Książki / Strony / Rozdziały) instead of a single dropdown with
|
||
no chapter option, plus a new "Szukaj po" setting: słowa kluczowe w nazwie /
|
||
tagi / oba. Tag matching uses the bare subcategory name (e.g. "Drukarki i
|
||
skanery"), matching what the auto-tagging feature above writes.
|
||
- **AI-driven ticket triage + summary** (Admin > Integracje >
|
||
"Automatyzacja AI dla zgłoszeń", runs via a new scheduled
|
||
`ai:run-ticket-automation`) — five independent toggles: assign a
|
||
category/subcategory when a ticket has neither, pick a subcategory when it
|
||
only has a category, recheck and possibly correct an already-categorized
|
||
ticket, rewrite an unclear subject, and set a priority based on content.
|
||
Runs once per ticket in the background (never synchronously at submission,
|
||
so it adds no latency for a client), and every applied change leaves a
|
||
specific line plus a "Automatyzacja: klasyfikacja AI" entry in the ticket's
|
||
history, same convention as the existing SLA automation rules. Separately,
|
||
an AI-generated summary + suggested next action for **every** ticket, shown
|
||
only to operators in a new "Podsumowanie AI" sidebar card, refreshed
|
||
whenever the thread gets a new message — its system prompt is admin-editable
|
||
as a plain-text field with a "Resetuj" button back to the shipped default.
|
||
- Operators can now reassign a ticket to **any** team, not just one they
|
||
belong to (previously the dropdown only ever offered the operator's own
|
||
teams).
|
||
- The auto-refresh countdown badges (ticket view, operator queue) are now
|
||
**clickable** — fetch immediately and reset the countdown, instead of only
|
||
ever refreshing on their own fixed schedule.
|
||
- **All 7 "cyclical" intervals** in the app are now configurable from
|
||
Admin > Konfiguracja instead of fixed in code: the 3 browser auto-refresh
|
||
countdowns (ticket view, operator queue), the notification bell's poll,
|
||
and the 4 background scheduled commands (SLA breach check, SLA automation
|
||
rules, IMAP fetch, AI ticket automation). Defaults match the previous
|
||
hardcoded values, so nothing changes until an admin edits them.
|
||
|
||
### Fixed
|
||
|
||
- An operator viewing a ticket that gets deleted by someone else, or whose
|
||
team changes to one outside the operator's own scope (including via their
|
||
own reassignment above), is now redirected back to the operator queue
|
||
instead of hitting an error mid-session.
|
||
|
||
## [1.2.0] - 2026-07-23
|
||
|
||
### Added
|
||
|
||
- **E-mail intake (IMAP)**, optional and off by default — clients can create a
|
||
ticket or reply to an existing one just by sending/replying to an e-mail.
|
||
Configure any number of mailboxes in the new **Admin > Poczta** page (which
|
||
now hosts SMTP alongside IMAP, replacing the old "E-MAIL" tab), each with
|
||
its own host/port/encryption/credentials/folder and routed to either a
|
||
specific subcategory (routes to that subcategory's team, same as a web
|
||
ticket) or a whole category with no subcategory (a new `tickets.category_id`
|
||
column covers this case — previously a ticket's category only ever came
|
||
through a subcategory).
|
||
- A reply is matched back to its ticket via the number/checksum already
|
||
present in every notification e-mail's subject — works with either the
|
||
plain sequential number or the obfuscated checksum, whichever numbering
|
||
mode is active, no changes to outbound templates needed.
|
||
- Automatic replies (autoresponders, "out of office", bounces/mailer-daemon)
|
||
are detected via headers and common EN/PL subject phrasing and rejected
|
||
instead of creating a ticket; a per-mailbox sender blocklist covers the
|
||
rest. The "tylko użytkownicy z LDAP" restriction is enforced for e-mail
|
||
exactly like the guest web form.
|
||
- A "Pobierz teraz" button per mailbox fetches immediately, outside the
|
||
5-minute schedule — useful for testing a freshly-configured mailbox or
|
||
diagnosing why a specific e-mail didn't turn into a ticket.
|
||
- Every connection attempt and per-message decision (accepted/rejected/
|
||
matched to which ticket) is logged to a dedicated `storage/logs/imap-*.log`
|
||
file, independent of the app's own log level.
|
||
- Tickets and individual messages that came in by e-mail show a small
|
||
mail-icon badge in the operator queue and ticket view, distinguishing them
|
||
from ones created/replied to on the web.
|
||
- **Operator queue**: a "select all" checkbox in the table header
|
||
selects/deselects every ticket currently visible under the active
|
||
filter/tab in one click, instead of clicking each row's checkbox.
|
||
|
||
### Changed
|
||
|
||
- Admin's old **"E-MAIL"** tab is now **"Poczta"** and also lists/manages the
|
||
IMAP mailboxes above — the two halves of "reply by e-mail" (send/receive)
|
||
now live together instead of SMTP being off on its own.
|
||
|
||
### Fixed
|
||
|
||
- **Scheduled-command notifications were silently going nowhere.**
|
||
`AppServiceProvider`'s Settings-based config override (SMTP/LDAP/session/
|
||
timezone) used to skip itself for *any* console command, not just
|
||
`migrate` — meaning `tickets:check-sla-breaches` and `automation:run-rules`
|
||
(and now `emails:fetch-imap`) always sent their e-mails through whatever
|
||
`.env`'s `MAIL_MAILER` happened to be (`log`, i.e. nowhere) instead of the
|
||
admin-configured SMTP server, with no visible error. Now scoped to just the
|
||
`migrate` command family, so every scheduled command gets the same live
|
||
config a web request would.
|
||
- Visiting a ticket that no longer exists (most commonly: it was deleted
|
||
while the viewer had it open, and a later background refresh hit the same
|
||
URL) no longer shows Laravel's default 404 page — redirects back to the
|
||
operator queue or client dashboard instead.
|
||
- This host had no crontab entry at all for `php artisan schedule:run` —
|
||
meaning SLA breach checks and automation rules had never actually run on
|
||
their own, only ever on request. Documented and configured (see
|
||
[CLAUDE.md](CLAUDE.md)).
|
||
|
||
## [1.1.4] - 2026-07-23
|
||
|
||
### Added
|
||
|
||
- **Configurable ticket numbering** (Admin > Konfiguracja > Ogólne) — an
|
||
admin-set prefix (default `#`) and a minimum zero-padded length for the
|
||
ticket number.
|
||
- **"Ukryj kolejność zgłoszeń"** — an opt-in mode that displays a stable,
|
||
HMAC-derived checksum instead of the sequential ticket number, so the
|
||
number shown gives no indication of ticket volume or creation order. Every
|
||
ticket gets its checksum assigned once, on creation, guaranteed unique.
|
||
When this mode is on, ticket URLs switch to the same checksum too (custom
|
||
`Ticket::getRouteKey()`/`resolveRouteBinding()`), so a link and the number
|
||
on the page it points to always match — and a URL built under the other
|
||
mode stops resolving. The REST API is unaffected; it's pinned to `id`
|
||
regardless of this setting. Search (queue/dashboard) now also matches
|
||
against the checksum. A live preview against a real ticket from the
|
||
database shows exactly how the number will look before saving.
|
||
|
||
### Changed
|
||
|
||
- **Attachments**: dropped the inline image thumbnail preview in the message
|
||
thread — every attachment (images included) now shows as just its
|
||
filename, opening in a new tab on click, consistent with how non-image
|
||
attachments already worked.
|
||
|
||
## [1.1.3] - 2026-07-22
|
||
|
||
### Added
|
||
|
||
- **Triggers** (Admin > Wyzwalacze) — event-driven business rules that fire
|
||
immediately on a ticket lifecycle event (created, any field updated, status/
|
||
priority/assignee/team/category changed, new public reply). AND-combined
|
||
conditions gate a sequence of ordered actions (set status/priority/team/
|
||
assignee, or send an e-mail). Ships with its own dedicated, freely
|
||
add/edit/delete-able trigger e-mail templates — kept separate from the
|
||
fixed, per-event system templates, which stay exactly as fixed as before.
|
||
Complements the time-based SLA automation rules rather than replacing them,
|
||
guarded against runaway loops (a depth limit plus a same-value no-op check).
|
||
- **Ticket watching** — operators can star/"Obserwuj" any ticket to follow it
|
||
regardless of assignment or team.
|
||
- **Real-time notification bell** — the bell now updates the instant a
|
||
notification is created (broadcast on a new private per-user channel),
|
||
with the existing 30s poll kept as a fallback for a dropped websocket.
|
||
Optionally also raises a native in-tab browser push notification.
|
||
- **Per-user notification preferences** (`/settings/notifications`) — each
|
||
operator/admin chooses, per event category (new ticket, ticket update,
|
||
escalation), which scope of tickets (mine, unassigned, watched, all)
|
||
notifies them via the bell and whether that also sends an e-mail, plus an
|
||
opt-in toggle for the browser push notifications above.
|
||
- **Admin > Integracje** — new tab hosting LDAP/AD and BookStack
|
||
configuration, split out of Konfiguracja so that tab is just general
|
||
system settings (attachments, session, timezone).
|
||
- Operator queue: three more optional columns (off by default, toggle via
|
||
"Kolumny") — Podkategoria, Zespół, Utworzono.
|
||
|
||
### Changed
|
||
|
||
- The "Obserwuj" button on the operator ticket view moved next to the
|
||
auto-refresh countdown badge, both now grouped on the right.
|
||
- `/settings/notifications`: added a "← Wróć" link back to the operator/admin
|
||
area, the browser-push card now spans the full page width, and the
|
||
preferences table sits in a bordered card like the rest of the app.
|
||
- Trigger conditions on Podkategoria/Zgłaszający now show a name dropdown
|
||
instead of a raw ID field.
|
||
- The admin panel's active tab and the operator queue's active view now
|
||
persist across a plain page refresh (bound to the URL query string), so
|
||
reloading no longer bounces back to the first tab.
|
||
|
||
## [1.1.2] - 2026-07-22
|
||
|
||
### Added
|
||
|
||
- **Real-time updates (Laravel Reverb)** — the operator ticket queue now
|
||
updates live: new tickets appear, status/priority/team/assignee changes
|
||
and new replies re-sort/refresh the affected row, and closed/deleted/
|
||
reassigned-away tickets disappear, all without a manual refresh. The
|
||
ticket message thread is now "live chat": a reply from either side
|
||
appears for the other party instantly. Clients also see status/priority/
|
||
team/assignee changes and new history entries on their own ticket live.
|
||
Backed by two private channels (`operator.queue`, `ticket.{id}`) and two
|
||
broadcast events (`TicketQueueChanged`, `TicketMessagePosted`), sent
|
||
synchronously (no queue worker needed, consistent with how e-mail
|
||
notifications already work in this app).
|
||
- **Periodic fallback refresh** — since a websocket connection can drop
|
||
silently (backgrounded tab, network blip), the operator queue and both
|
||
ticket-detail views also poll themselves every 30–60 seconds regardless
|
||
of broadcasting, with a small visible countdown badge so it's clear the
|
||
page is still refreshing on its own.
|
||
- **SLA automation rules** (Admin > Automatyzacja SLA) — configurable rules
|
||
that act on a ticket after N minutes of customer silence (optionally
|
||
scoped to a priority/category/team): change priority, status, team, or
|
||
assignee. Reuses the same `TicketService` setters a manual operator
|
||
action would, so automated changes get the same history entry,
|
||
notification, and (now) live broadcast as a human doing it. A new
|
||
scheduled command (`automation:run-rules`, every 15 minutes) evaluates
|
||
all enabled rules; each rule only fires once per ticket until a fresh
|
||
reply or a close/reopen resets it.
|
||
- **New notification: ticket landed in your team** — every operator on a
|
||
team whose subcategories match a newly created ticket now gets notified
|
||
(enabled by default; toggle like any other trigger in Admin > Szablony
|
||
e-mail).
|
||
- **BookStack knowledge-base sidebar on the client's own ticket view** —
|
||
previously only shown to operators; clients now see the same
|
||
category/subcategory-matched suggestions on their ticket page that they
|
||
saw while creating it.
|
||
- **Operator ticket view / client ticket view now show the assigned
|
||
operator and team** in the "Status i priorytet" card (client side).
|
||
- **Stats dashboard**: new breakdowns — tickets by subcategory, CSAT
|
||
average by team and by operator, top 10 clients by ticket volume (+ one
|
||
"Goście" bucket for guest submissions), and a client × subcategory
|
||
cross-tab (top 10 clients × top 5 subcategories, rest folded into
|
||
"Inne"). The whole dashboard is now organized into labeled sections
|
||
(Podsumowanie / Rozkład zgłoszeń / Obciążenie / Klienci / Ocena obsługi /
|
||
Trend) instead of one flat wall of cards.
|
||
|
||
### Changed
|
||
|
||
- BookStack suggestions (ticket-creation wizards, guest landing page,
|
||
operator/client ticket views) now load in a beat after the page's first
|
||
paint (`wire:init`) instead of blocking the initial render on BookStack's
|
||
API response.
|
||
- The notification bell shows **unread notifications only** — reading one
|
||
(by clicking it or "mark all as read") now removes it from the list
|
||
instead of just dimming it.
|
||
- The client ticket-view page is now the same width as the operator's
|
||
(1180px, up from 920px) — the main message-thread column is unaffected.
|
||
- Mobile: the theme/notifications/profile-menu dropdowns in the top nav
|
||
now expand to the full screen width below 640px instead of a fixed
|
||
narrow width that could overflow off-screen.
|
||
|
||
### Fixed
|
||
|
||
- Two bugs that silently disabled all real-time updates from the moment
|
||
Reverb was first wired in: (1) the Echo client never sent a CSRF token
|
||
when authorizing private channels, so every subscription attempt was
|
||
rejected by the app's own CSRF middleware; (2) the Echo setup script
|
||
(loaded as a deferred ES module) raced against Livewire's own
|
||
synchronously-loaded bootstrap script and could miss the `livewire:init`
|
||
event entirely, silently skipping the operator-queue subscription.
|
||
- The `mariadb` container's healthcheck was failing (`Access denied ...
|
||
using password: NO`) because its auto-generated credentials file had
|
||
world-writable permissions (an artifact of this stack's NFS-backed bind
|
||
mount) and MariaDB's client refuses to read credentials from a file
|
||
anyone can modify — the healthcheck now passes.
|
||
- Several pre-existing test-suite gaps found while working in this area:
|
||
`User::roles` (a virtual attribute backed by the `roles`/`role_user`
|
||
pivot, not a plain column) needs the `roles` table seeded before
|
||
assigning a role by key — now seeded automatically for every test in
|
||
`Tests\TestCase`. A handful of other tests were missing
|
||
`NotificationSetting`/`ReplyQuickAction` seed data, asserting a stale set
|
||
of default-enabled notification triggers, or using the wrong Notification
|
||
Fake assertion for a real (non-guest) recipient.
|
||
|
||
## [1.1.0] - 2026-07-22
|
||
|
||
### Added
|
||
|
||
- **In-app notifications** — a bell in the top bar backed by Laravel's
|
||
database notification channel, alongside existing e-mail notifications
|
||
(same per-trigger toggle drives both; ticket links now correctly point into
|
||
the recipient's own area instead of always linking to the client view).
|
||
- **Drag-and-drop attachments** on every upload form (ticket creation, replies,
|
||
internal notes), plus inline image thumbnails in the message thread instead
|
||
of a plain download link.
|
||
- **Customer satisfaction (CSAT)** rating — clients rate a closed ticket 1–5
|
||
stars with an optional comment; shown read-only to operators, surfaced as a
|
||
KPI on the stats dashboard, and linked from the "ticket closed" e-mail.
|
||
- **Saved queue views** — operators can save/apply/delete named filter+sort+
|
||
column presets in the ticket queue and mark one as their default.
|
||
- **Full-text search** — MySQL/MariaDB `FULLTEXT` search (portable `LIKE`
|
||
fallback on sqlite) across ticket subject/body and reply message bodies, now
|
||
also available on the client's own ticket list (previously operator-only,
|
||
and previously subject/number/name/email only).
|
||
- **Stats CSV export** — exports the currently filtered ticket set from the
|
||
operator stats dashboard.
|
||
- **BookStack knowledge-base integration**, optional and off by default —
|
||
suggests relevant articles by category/subcategory while creating a ticket,
|
||
and in a separate sidebar on an existing ticket for operators (with a
|
||
copy-link button). Configurable from Admin > Konfiguracja: connection + API
|
||
token (encrypted), optional SSL-verification bypass for self-signed
|
||
instances, page/book search-type filter, and two independent per-shelf
|
||
allow-lists (nothing is ever searched until specific shelves are opted in,
|
||
separately for ticket-creation suggestions vs. the operator sidebar) with a
|
||
manual refresh button for the shelf list.
|
||
|
||
### Changed
|
||
|
||
- Closed tickets are no longer shown in the "Moje zgłoszenia" / "Nieprzypisane"
|
||
/ per-team queue tabs — they now only ever appear under "Zamknięte", matching
|
||
how the "Otwarte" tab already worked.
|
||
- The operator ticket-view sidebar is ~50% wider (to fit the BookStack
|
||
suggestions panel); the page itself grew to match, so the ticket
|
||
content/thread column keeps its previous width.
|
||
- The "Resetuj" work-timer button sits below the "Zgłoszenie zamknięte —
|
||
zliczanie wstrzymane" notice instead of beside it.
|
||
|
||
### Fixed
|
||
|
||
- `TicketService::setStatus()` now checks a status's `stage` (via
|
||
`Status::stageFor()`) rather than the literal key `'closed'` to decide
|
||
whether to fire the "ticket closed" notification/stop the timer — correct
|
||
even if an admin renames or replaces which key maps to the closed stage.
|
||
|
||
## [1.0.2] - 2026-07-22
|
||
|
||
- Fixed `.gitea/workflows/build.yml`: registry login was failing with
|
||
`unauthorized` because Gitea's auto-injected `secrets.GITHUB_TOKEN` isn't
|
||
granted push access to its own container registry on this instance. Now
|
||
uses a dedicated `REGISTRY_TOKEN` secret (a Gitea access token with
|
||
`write:package`/`read:package` scope), and the workflow fails fast with a
|
||
clear `::error::` message before attempting login if that secret isn't
|
||
configured, instead of surfacing Docker's opaque `unauthorized` error.
|
||
- Fixed: opening or manually resuming a **closed** ticket no longer starts its
|
||
work timer, and closing a ticket (via the status dropdown, a reply "quick
|
||
action" transition, the REST API, or merge) now checkpoints and stops any
|
||
running timer. Time tracking only ever accrues while a ticket is open.
|
||
- Fixed: the login form accepted an empty username/password, submitting them
|
||
straight to the auth provider. The form fields are now `required` (blocks
|
||
submission client-side) and `Login::submit()` also rejects blank/
|
||
whitespace-only credentials server-side before attempting authentication,
|
||
showing "Podaj nazwę użytkownika i hasło." instead.
|
||
- Fixed: closing a ticket sent two separate notification e-mails
|
||
(`status_changed` and `ticket_closed`) for the same event. Closing now only
|
||
fires `ticket_closed`; every other status transition still fires
|
||
`status_changed` as before.
|
||
|
||
## [1.0.1] - 2026-07-22
|
||
|
||
Documentation and deployment/CI overhaul — no application behavior changes.
|
||
|
||
- Added project docs: `TESTING.md`, `CONTRIBUTING.md`, `ARCHITECTURE.md`,
|
||
`SECURITY.md`, `CLAUDE.md`; removed the unmaintained stock Laravel
|
||
`src/README.md`.
|
||
- Added `.gitea/workflows/build.yml`: Gitea Actions now builds and pushes the
|
||
`servicedesk` image to the Gitea container registry whenever `Dockerfile`
|
||
changes on `main`.
|
||
- `compose.yaml` now pulls `image: gitea.kzbikowski.pl/kzbkowski/servicedesk:${IMAGE_TAG:-latest}`
|
||
instead of building locally; `mariadb` pinned to `mariadb:12.3`; added a
|
||
`mariadb` healthcheck and `depends_on: condition: service_healthy` for
|
||
`servicedesk`.
|
||
- Added tracked templates `compose.yaml.example` and `.env.example` (root) for
|
||
the previously-untracked `compose.yaml`/`.env`.
|
||
- Documented the new pull-based deploy flow and one-time registry login in
|
||
`install.md`.
|
||
|
||
## [1.0.0] - 2026-07-21
|
||
|
||
Initial release.
|
||
|
||
- Ticketing core: categories/subcategories with per-subcategory custom fields,
|
||
statuses, priorities, teams, attachments, message threads (public replies +
|
||
internal notes), history log, merge/delete.
|
||
- SLA rules per priority with a scheduled breach check (`tickets:check-sla-breaches`,
|
||
every 15 minutes).
|
||
- Response templates, quick actions, and per-event HTML email templates/
|
||
notification toggles.
|
||
- Operator statistics dashboard (`/operator/stats`) with KPI tiles and breakdowns.
|
||
- Branding/config panel: company identity, LDAP connection + user sync, SMTP,
|
||
attachment limits, session lifetime, timezone.
|
||
- LDAP authentication (LdapRecord) with local-account fallback.
|
||
- REST API (`/api/v1/...`) via Sanctum, ability-scoped (`tickets:read`,
|
||
`tickets:write`, `dictionaries:read`, `users:read`), with Swagger docs at
|
||
`/admin/api-docs`.
|
||
- Installable PWA manifest/icons for the client-facing area.
|