Files
servicedesk/CLAUDE.md
Kacper 90fae0a4de v1.1.0
- 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, 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 FULLTEXT, portable LIKE fallback) across ticket
  subject/body and reply message bodies, now also on the client's own ticket
  list.
- Stats CSV export for the currently filtered ticket set.
- Optional BookStack knowledge-base integration (off by default): suggests
  articles by category/subcategory while creating a ticket and in a separate
  sidebar for operators on an existing ticket (with a copy-link button).
  Configurable connection/SSL bypass/search-type filter, plus two
  independent per-shelf allow-lists so nothing is ever searched until an
  admin opts specific shelves in.
- Closed tickets no longer show in "Moje zgłoszenia"/"Nieprzypisane"/team
  queue tabs, only under "Zamknięte" (matching how "Otwarte" already worked).
- Wired up the Admin > About "Wersja" field to config('app.version')/VERSION
  in .env instead of a stale hardcoded string.
- Fixed: TicketService::setStatus() now checks a status's stage rather than
  the literal key 'closed' to decide whether to fire the "ticket closed"
  notification/stop the timer.
- Updated README/ARCHITECTURE/CHANGELOG/install/SECURITY docs and all three
  wiki/ role guides for the above; documented a root-vs-www-data file
  ownership gotcha in CLAUDE.md (running artisan commands via a plain
  `docker exec` can leave root-owned Blade cache files that later break
  recompilation for the www-data Apache process).

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-22 15:18:09 +02:00

91 lines
5.0 KiB
Markdown

# CLAUDE.md
Guidance for AI coding agents working in this repo. See [README.md](README.md)
for the feature/tech overview, [ARCHITECTURE.md](ARCHITECTURE.md) for how the app
is put together, and [install.md](install.md) for full deployment instructions.
## This is a live production system
The only real user today is the owner's own account (client+operator+admin
roles). Treat the running database as production, not a sandbox:
- Never create/modify/delete real DB records (users, tickets, settings, etc.)
without the user explicitly asking. Don't spin up throwaway test users via
`php artisan tinker` against this DB.
- For UI/CSS verification, prefer an offline static-HTML harness reproducing the
real markup+CSS, or ask for test credentials, rather than poking at prod data.
- Production is fronted by Traefik with a **private-CA TLS cert** (not publicly
trusted) — tools like `curl`/Playwright need `-k` / `ignore_https_errors` to
hit it directly.
## Container operations: use `sudo`, never build the image locally
All Docker commands against this stack need `sudo` (e.g.
`sudo docker compose exec servicedesk ...`, `sudo docker exec servicedesk-servicedesk-1 ...`).
**Never run `docker build`, `docker compose build`, or `--build`.** The
`servicedesk` image is built by CI (`.gitea/workflows/build.yml`, triggered on
`Dockerfile` changes on `main`) and pushed to the Gitea container registry;
`compose.yaml` only ever `pull`s a tag (`sudo docker compose pull && sudo docker
compose up -d`, see [install.md](install.md) 1.3/1.3a) — building locally would
just diverge from what CI produces. The app container
(`servicedesk-servicedesk-1`) mounts `./src` from the host over NFS
(`/mnt/rabbit-containers` → NFS export), so plain file edits already take effect
with no rebuild or restart:
- Blade/PHP/route edits: just edit the files — live instantly, no restart needed.
- `resources/css/app.css` / `resources/js/*` (Vite/Tailwind entrypoints) **do**
need a rebuild — `public/build/assets/` is static compiled output. Neither the
host nor the app container has Node installed; rebuild with a throwaway
container instead of touching the app image:
```bash
sudo docker run --rm -v "$(pwd)/src":/app -w /app node:22 npm run build
```
(run from the repo root; `node_modules` already exists, no install needed).
Verify via `public/build/manifest.json` picking up a new hash.
- After running artisan cache commands for diagnostics (`config:cache`,
`view:cache`), clear them again afterward (`config:clear`/`view:clear`) — this
app normally runs uncached so edits apply live; leaving a cache on silently
breaks that workflow.
- **`sudo docker exec` runs as `root`, not `www-data`.** Apache's worker
processes run as `www-data`; anything you run via a plain `docker exec`
(`php artisan test`, `tinker`, `view:cache`, etc.) runs as `root`. On this
NFS-backed mount, a file Blade compiles/caches while running as `root`
(`storage/framework/views/*.php`) can't later be overwritten by `www-data`
when a real request needs to recompile it (the source changed) — this
surfaces in production as a 500 with `touch(): Utime failed: Operation not
permitted`. If you ran `php artisan test`/`tinker`/any artisan command via
`docker exec` in a session where you also edited Blade files afterward,
finish with `sudo docker exec servicedesk-servicedesk-1 php artisan
view:clear` to flush any root-owned compiled views before ending the
session — don't wait for a report of a broken page to catch it.
## Apache `/icons/` alias trap
The stock `php:apache` image enables `mods-enabled/alias.conf`, which defines
`Alias /icons/ "/usr/share/apache2/icons/"`. Any app-level static directory at
`public/icons/` is silently shadowed by Apache's own stock icon set — requests
404 before ever reaching Laravel, with no useful log line. This project
deliberately uses `public/pwa-icons/` for PWA manifest icons for exactly this
reason — never add a `public/icons/` directory.
## Established UI conventions
- **Post-login redirect** always lands on `/client` (`User::defaultArea()` in
`app/Models/User.php`), even for accounts holding operator/admin roles too —
staff switch areas via the role switcher in the header
(`resources/views/components/profile-menu.blade.php`). Keep `/client`
first-priority in `defaultArea()` if you add new roles/areas.
- **Mobile tables**: don't rely on horizontal scroll alone for wide data tables.
The established pattern (`.table-cards-mobile` in `resources/css/app.css`,
`@media (max-width:640px)`) turns each `<tr>` into a card with `data-label`
attributes on `<td>`s. Currently applied to the operator ticket queue; apply
the same treatment to any other wide table you add or make mobile-relevant
(admin panel tables don't have it yet).
## Testing & code style
See [TESTING.md](TESTING.md) and [CONTRIBUTING.md](CONTRIBUTING.md) — run
`php artisan test` and `./vendor/bin/pint` before considering a change done.
There is no CI; local test runs are the only gate.