- 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>
202 lines
11 KiB
Markdown
202 lines
11 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.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.
|