Commit Graph

56 Commits

Author SHA1 Message Date
aminovfariz
c32a0f8c31 feat(helpdesk): Übergabe assignment workflow
- New statuses: under_review
- New DB fields: assigned_to, assigned_by, assigned_at, due_date (migration 012)
- HandoverNotificationService: emails on assign and review-request
- HandoverService: assign(), submitForReview(), resendNotification()
- Assign page: manager selects employee + due date, resend notification
- Create wizard: manager can assign during creation (step 1)
- Edit page: "Submit for review" button for assignee, assign link for manager
- List page: open/closed tabs, non-managers see only their assigned handovers
- Email templates: handover_assigned + handover_review_requested (de/en)
- i18n keys added for de and en

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-01 14:35:03 +02:00
aminovfariz
cb2f429e0e feat(helpdesk): add empty Dashboard page with sidebar nav item
Adds helpdesk/dashboard route, empty page/view, Overview nav group in the
sidebar, and Dashboard i18n keys (de + en). No content yet.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-26 11:01:35 +02:00
fs
12356e2266 feat(core): generic CSV export primitive + helpdesk domains consumer
Add a reusable CSV-export building block to the core so any Grid.js list
page can gain a filter/sort-aware download with two clicks of glue.

Core primitive:
- core/Service/Export/CsvExportService: flavor-aware writer (plain CSV
  or Excel-compatible UTF-8+BOM+semicolon), with OWASP-aligned formula-
  injection escape (=, @, +, -, TAB, CR) applied to both rows *and*
  headers. Value objects for columns (ExportColumn) carry an extractor
  closure and an allowSignedNumeric flag for phone-number-shaped cells.
- core/Support/helpers/export.php: thin HTTP layer (exportSendCsv,
  exportCapLimit, exportResolveFlavor, exportRequireGetRequest) reusing
  the requestInput() contract for GR-CORE-003 consistency. Filename is
  sanitized and length-clamped; callers must still enforce auth.
- templates/partials/app-list-export-dropdown.phtml: zero-config
  <details class="dropdown"> with CSV + Excel triggers.
- web/js/core/app-list-export.js: initListExport({ gridConfig, exportUrl })
  mirrors the current grid filters + sort onto the export URL and
  navigates, preserving session cookies for download.

First consumer — Helpdesk domains:
- DomainListService extracts the shared filter/enrich/sort logic from
  domains-data so the grid endpoint and the new domains/export endpoint
  cannot drift.
- domains/export endpoint delegates to DomainListService, declares
  ExportColumns (with translated level labels), and exits via
  exportSendCsv.
- domains-data now ~20 lines, delegating to DomainListService.

Tests:
- CsvExportServiceTest (10 cases): both flavors, BOM/no-BOM, formula
  escapes incl. TAB/CR, header escape, signed-numeric allowlist,
  quoting, multiline, empty columns, generator-compatible iterable.
- ExportHelpersTest (5 cases): exportCapLimit bounds.
- DomainListServiceTest (8 cases): filter, search, "all" sentinel,
  sort, paging, enrichment, BC failure, tenant-scoped security filter.

Gates: PHPUnit green, PHPStan clean on touched files, module:sync ok.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-21 21:57:19 +02:00
fs
fb86c02427 fix(helpdesk): repair security-level persistence, enrichment, and list UX
Saving security levels appeared broken in both the domain detail view and
the domains grid. Root causes:

- Repository never unwrapped MintyPHP DB rows (nested by table name), so
  reads always returned defaults even though writes succeeded.
- Batch queries bound tenant_id last while SQL expected it first, so
  list/detail enrichment for the grid matched nothing.
- Detail page AJAX submit looked up CSRF via a non-existent data attribute
  and dropped the note field entirely.
- Endpoint used a hand-rolled CSRF check instead of Session::checkCsrfToken().

Fixes:
- DomainSecurityLevelRepository: use RepositoryArrayHelper::unwrap(List),
  swap param order in listLevelsByDomainNos / listDetailsByDomainNos,
  centralize table name in a const.
- security-level-data endpoint: canonical Session::checkCsrfToken(),
  unified JSON/PRG error path with proper HTTP status codes and flash.
- Detail page: drop broken AJAX hijack, rely on native form POST + PRG
  (GR-CORE-012).
- Grid list: refetch via gridRef.grid.forceRender() after dialog save
  instead of DOM-only mutation, so Grid.js internal state stays in sync.
- Add dedicated Note column next to the badge with ellipsis + title
  tooltip.
- Replace console.* in the dialog with showAsyncFlash for user-visible
  errors; add i18n keys (de/en) for all error messages.
- Drop unused postAction import and TENANT_ID_OTHER test constant.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-21 14:04:21 +02:00
fs
6ac685084d Feature: Security Level mit Notiz für Domains
Implementiert ein Dialog-basiertes UI zum Bearbeiten des Security Levels
mit optionaler Notiz-Funktion für Helpdesk-Domains.

Backend:
- DomainSecurityLevelRepository: CRUD für security_levels mit note-Spalte
- DomainSecurityLevelService: Business-Logik mit Batch-Enrichment
- security-level-data() Endpoint: AJAX + Full-Page POST mit CSRF
- Migration 010: note TEXT-Spalte hinzugefügt
- Bugfix: findByTenantAndDomainNo gibt null bei leerem Array zurück

Frontend (Domain-Liste):
- Badge in Grid klickbar → öffnet Modal-Dialog
- Dialog mit Level-Select + Notiz-Textarea
- AJAX-Save mit Live-Badge-Update ohne Reload
- Event-Delegation für dynamische Grid-Inhalte

Frontend (Domain-Detail):
- Notiz-Feld im Security-Level-Formular
- PRG-Pattern mit Flash-Messages

Core:
- app-http.js: DEFAULT_HEADERS auf XMLHttpRequest für CSRF-Kompatibilität

i18n:
- Keys: Priority, Note, Optional note, Security level updated

Tests:
- DomainSecurityLevelServiceTest: CRUD + Batch-Enrichment
2026-04-21 13:43:16 +02:00
fs
ec2f03e0cf helpdesk: remove unused UpdateRepository::findById 2026-04-20 19:58:51 +02:00
fs
da0abc824a feat(helpdesk): add software updates tracking with BC ticket integration
New Updates page in the Helpdesk module that fetches UPDATE/UPDATE-HF tickets
from Business Central (PBI_LV_Tickets) and allows assigning a domain and Gitea
link via a dialog. Ticket status (from BC) and assignment status (local) are
shown as separate columns with filters for both plus type and free-text search.
Assigned updates also appear on the domain detail page. Includes session-cached
BC fetch with refresh button, admin permissions, migration, and 16 unit tests.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-16 13:21:40 +02:00
fs
693d33a0c8 feat(helpdesk): add domain detail page with customer, contract, and handover sections
New domain detail page showing customer info, contract data, linked handovers,
updates section, and related domains for the same customer. Data loaded async
with skeleton loading states. Includes DomainDetailService and tests.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-16 13:21:24 +02:00
fs
e7c60468c9 feat(helpdesk): add domain linking, bulk actions, and revision polish to handovers
Adds domain selection (cascaded from debitor) to handover creation and edit,
bulk delete with confirmation dialog, and various UI improvements to the
handover wizard and list page. Includes migration for domain columns,
domain-select AJAX endpoint, and updated tests.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-16 13:21:12 +02:00
fs
03e15eaf08 feat(helpdesk): add handover revision history with timeline, diff, and restore
Every save creates an immutable revision snapshot. The aside shows a
commit-graph-style timeline (newest first) with vertical line, circle
nodes, version numbers, change type, user, and date. Clicking a past
revision renders it readonly with inline git-diff-style highlights
(changed/added/removed with tinted backgrounds). Compare mode allows
diffing any two arbitrary revisions. MANAGE users can restore old
versions, which creates a new revision preserving full history.

New: HandoverRevisionService, HandoverRevisionRepository,
migration 006, 14 PHPUnit tests, DE/EN translations.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-15 22:14:40 +02:00
fs
9a49e18e27 feat(helpdesk): redesign handover edit page sidebar
- JOIN users table in findById for created/updated-by display names
- Fix row mapping to handle merged JOIN results
- Use product name instead of debitor name in page title
- Sidebar: collapsible customer/product sections, status dropdown,
  audit partial with user attribution

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-15 21:29:36 +02:00
fs
f878d8909d feat(helpdesk): pass field values directly during handover creation
Avoids the separate updateFields call after insert by accepting fieldValues
in HandoverService::create() and validating/encoding them before the insert.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-15 21:29:27 +02:00
fs
a26c106083 feat(helpdesk): add handover protocol management
Implement handover protocols as a new entity in the helpdesk module,
allowing users to create fillable protocol records from admin-defined
software product schemas.

Key additions:
- DB migration (helpdesk_handovers table) with tenant scope
- HandoverService with status workflow (draft/in_progress/completed/archived)
- Three-tier permissions (view/create/manage)
- Two-step creation wizard (Stripe-style assistant)
- Grid.js list page with search and status filter
- Edit/detail page with aside metadata and status controls
- Reusable core autocomplete lookup component (app-lookup-field)
- Debitor lookup data endpoint for autocomplete
- Dynamic form rendering from schema snapshots
- 11 PHPUnit tests for HandoverService
- DE+EN i18n translations (48 keys each)

Also includes: PHPStan fixes (dead code removal, stale baseline cleanup),
software product edit title improvement, fieldset simplification,
and Stripe-style hover for schema preview.

Workflow: HD-HANDOVERS-001 (.agents/runs/HD-HANDOVERS-001/)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-15 20:42:41 +02:00
fs
2ec632ac8b feat(helpdesk): polish handover schema editor UI/UX
Auto-generate field keys and option values from labels via slugify,
add live preview panel with hover-highlight, insert-between-fields
dividers, and various spacing/styling improvements.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-15 18:08:12 +02:00
fs
44153f513f feat(helpdesk): add handover protocol schema editor to software products
Add a JSON-based schema editor as a new tab on the software product edit
page. Admins can define protocol fields (heading, paragraph, text,
textarea, number, date, checkbox, select) via a structured UI with
add/remove/reorder controls. The schema is stored as a versioned JSON
column on the product row.

Includes DB migration, server-side validation (type whitelist, key
uniqueness, key format, max 50 fields, select option checks), i18n
(DE+EN), CSS, and 10 PHPUnit tests.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-15 16:44:12 +02:00
fs
364bda67a9 fix(helpdesk): handle missing session in EffectiveHelpdeskSettingsService
Scheduler jobs run without an active session, causing SessionStore::all()
to fail. Wrap the call in try-catch to fall back to global config (tenant
ID 0) when no session is available.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-15 12:31:38 +02:00
fs
ef4473de64 feat(helpdesk): add Software Products page with BC contract type sync
Add a new Software-Produkte feature to the helpdesk module that syncs
contract types (Create_SaaS_License=true) from BC OData into a local
database table with a nightly scheduler job, providing a Grid.js list
page and detail/edit page for managing custom product names.

- DB migration 003: helpdesk_software_products table (code UNIQUE key)
- BcODataGateway: FS_Contract_Types entity with SaaS filter + fallback
- SoftwareProductRepository: upsert, listPaged, softDelete, updateName
- SoftwareProductSyncService: fetch → upsert → soft-delete lifecycle
- SoftwareProductSyncJobHandler: daily at 02:00 via scheduler platform
- SoftwareProductService: web UI business logic with validation
- Permission: helpdesk.software-products.manage (nav-gated)
- List page: Grid.js with Code, BC Description, Name, Status columns
- Detail page: Code/BC Description read-only, Name editable, PRG pattern
- i18n: de + en translation keys

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-14 22:16:21 +02:00
fs
d472026df4 feat(helpdesk): add Domains list page with BC OData contract type enrichment
Add a new "Domains" page to the helpdesk module that fetches domain data
from the FS_Contract_Domains OData endpoint and enriches each domain with
its contract type (PI_Header_Type) by joining with FS_Contract_Lines_Test
where Type = 'Domain'.

New files:
- domains/index().php, index(default).phtml, filter-schema.php — list page
- domains-data().php — data endpoint with PHP-side filtering/sorting
- helpdesk-domains-index.js — Grid.js via initStandardListPage()

Gateway additions:
- listDomains() — fetch all contract domains
- listDomainContractLines() — fetch domain-type contract lines for type lookup

Sidebar restructured into three collapsible groups (Lookup, Monitoring,
Administration) with per-group icons and color coding, matching the core
admin panel pattern.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-14 21:34:32 +02:00
fs
3f0db68b27 refactor: fix all 105 PHPStan 2 strict type findings across core and modules
Resolve all non-false-positive PHPStan 2 findings, reducing the baseline
from 486 to 382 entries (only unused-public false positives remain).

Categories fixed:
- Remove 39 redundant type checks (is_string, is_array, is_object, method_exists)
- Remove 12 no-op array_values() on already-sequential lists
- Simplify 13 null-coalesce on guaranteed offsets
- Remove 8 always-true instanceof checks
- Replace 12 redundant test assertions (assertTrue(true) → addToAssertionCount)
- Simplify 6 unnecessary nullsafe operators (?-> → ->)
- Fix 6 always-true !== '' comparisons
- Fix remaining: unset.offset, return.unusedType, staticMethod narrowing

53 files changed across core/, modules/, pages/, and tests/.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-14 12:54:20 +02:00
fs
4e6b586b77 refactor: enforce layer discipline — move repositories, fix constructor injection
- Move HelpdeskTenantSettingsRepository and HelpdeskTokenRepository from
  Service/ to Repository/ directory (GR-SEC-003: SQL only in repositories)
- Make AccessControl constructor require IntendedUrlService explicitly
  instead of falling back to `new IntendedUrlService()` (GR-TEST-002:
  no service instantiation outside factories)
- Update all imports and tests accordingly

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-13 19:28:07 +02:00
fs
a0cb345e04 fix(helpdesk): use strict input validation for all user-supplied customerNo in OData filters
Replaces escapeODataTrustedLiteral with escapeODataStrictUserInput for all
customerNo parameters that originate from HTTP request input. The trusted
literal escape (quote-only) remains for BC-sourced values like customerName.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-13 15:07:40 +02:00
fs
42efdb8102 feat(helpdesk): add debitor meetings tab with upcoming/past meeting display
Adds a meetings section to the debitor detail page, fetching meeting data from BC OData API with cache control support. Includes timeline UI for upcoming and past meetings.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-13 14:13:29 +02:00
fs
cacd1fb6d1 feat(helpdesk): refactor multi-tenant BC settings and risk radar improvements
Restructure helpdesk tenant settings into per-connection config with
improved token handling. Update risk radar data endpoint and UI.
Clean up ImageUploadTrait and update architecture contract tests.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-06 22:22:17 +02:00
fs
ea786f5341 feat(helpdesk): add multi-tenant BC connection settings with risk radar refinements
Introduce per-tenant override for helpdesk BC connection config.
New table helpdesk_tenant_settings stores tenant-specific credentials
with encrypted secrets (AES-256-GCM). EffectiveHelpdeskSettingsService
resolves global vs tenant config per request. Settings UI extended with
tenant override tab, toggle, and dual editing mode.

All runtime consumers (OData, SOAP, OAuth) read through the new
resolver. Token cache flushed on any config change. Default behavior
unchanged — tenants use global config until override explicitly enabled.

Also includes risk radar UI refinements: Stripe-style card redesign
with accent borders and score pills, removal of redundant KPI tiles
and search, and filtering of zero-score entries.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-06 10:29:59 +02:00
fs
97dbc7f57b fix(helpdesk): reset card margin-bottom and tighten risk level thresholds
- margin-bottom: 0 on cards to override article shell default
- Level thresholds stricter: high >= 55 (was 70), medium >= 25 (was 40)
  so fewer customers appear as 'low risk' and issues surface earlier

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-06 00:55:41 +02:00
fs
28cb60efb3 refactor(helpdesk): remove resolution time dimension from risk score
Resolution time measures the past, not current risk. The remaining
4 dimensions (open pressure 35%, trend 30%, SLA overdue 25%,
inactivity 10%) are all current and actionable. Removes resolution
aggregation, median calculation, and weight redistribution logic.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-06 00:47:58 +02:00
fs
5a4974909b feat(helpdesk): add Risk Radar dashboard with customer risk scoring
New portfolio view scoring customers 0-100 across five dimensions:
- Open pressure (30%): open + critical ticket count
- Trend (25%): net ticket flow (created - closed) in period
- SLA overdue (20%): tickets exceeding escalation targets
- Resolution time (15%): median hours to close (null = neutral)
- Inactivity (10%): age of oldest open ticket

Card grid with score badges (color-coded high/medium/low), metric
pills, driver bars. Click opens detail dialog with all dimensions
and open ticket list. Clientside search filter.

PBI_LV_Tickets with client-driven paging ($skip/$top, hardcap 5000).
Escalation definitions cached separately (30min TTL). Truncated
banner when data is capped.

New: RiskRadarService, getTicketsForRiskRadar(), 13 PHPUnit tests,
permission helpdesk.risk-radar.view, 2 routes, i18n de+en.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-06 00:24:48 +02:00
fs
2b2f41ebab fix(helpdesk): extract SOAP fault detail from GetTicketFile error responses
Show the actual BC error message instead of just 'HTTP 500' to help
diagnose authentication and parameter issues.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-05 22:55:12 +02:00
fs
c2eba3e671 fix(helpdesk): add empty ticketNo field to GetTicketFile SOAP envelope
BC expects all four parameters in the envelope even if ticketNo is
empty. The entry number alone should be sufficient to identify the file.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-05 22:54:22 +02:00
fs
792f706d9e feat(helpdesk): add ticket file download and inline image preview
New SOAP method getTicketFile() on BcSoapGateway calls BC's
GetTicketFile to retrieve file attachments as Base64, decodes in
memory and returns binary data without writing to disk.

New proxy endpoint helpdesk/ticket-file-data streams files directly
to the browser with correct MIME type. Images served inline, other
files as download attachment.

TicketCommunicationService now attaches file_entry_no and file_name
to file-type events. Frontend renders download links with bi-download
icon and inline image previews for jpg/png/gif attachments.

Works in both ticket detail and debitor detail communication feeds.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-05 22:49:01 +02:00
fs
659a6d71c5 fix(helpdesk): only resolve most recent support code on reassigned tickets
When a ticket was reassigned (multiple support codes), all codes were
mapped to the current Support_User_Name — replacing the previous
agent's name with the new one on older entries.

Now: if multiple support codes exist, only the code from the most
recent entry is mapped to the current name. Older codes remain as
abbreviations, preserving the actual history of who worked on what.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-05 22:26:25 +02:00
fs
fbe8140937 fix(helpdesk): drop redundant SOAP message entries when OData has them
When the SOAP message map is applied to OData entries, the message text
is already embedded in the OData entries. The separate SOAP message
entries then cause duplicates with different actor/timestamp metadata.

Now: when soapMessageMap was used, filter out SOAP entries with
type_variant 'message' since they are already represented in OData.
Non-message SOAP entries (status, activity) are kept.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-05 22:21:28 +02:00
fs
66be390fa9 fix(helpdesk): remove actor from dedup signature to prevent duplicate entries
SOAP entries often have an empty actor while OData entries have a code
like 'NKS'. After name resolution, one becomes 'Nikita Soldatov' and
the other stays empty — producing different signatures and causing
duplicates. The actor is not a reliable dedup field since the same
event can have different actor representations across sources.

Dedup now uses: ticket_no + timestamp_iso + type + message_hash.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-05 22:13:04 +02:00
fs
f2aa45dd2c feat(helpdesk): add trend delta indicator per agent in team dashboard
Shows a small arrow next to the ticket count in each agent card header:
↑3 (orange) = received 3 more than resolved → backlog growing
↓2 (green) = resolved 2 more than received → backlog shrinking
Hidden when delta is 0 (stable).

Delta is computed from received_in_period (tickets created in the
selected period assigned to this agent) minus resolved_in_period.
No snapshot storage needed — derived from existing ticket data.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-05 21:09:52 +02:00
fs
5c0364f687 feat(helpdesk): clickable top customers/categories filter resolution times
Clicking a customer or category in the Historical tab recalculates
Ø/Min/Max resolution times for only that filter. Click again to reset.
All computation is client-side from resolved_details already in the
response — no extra API calls. Active filter highlighted with primary
color, resolution values turn primary when filtered.

Backend now includes resolved_ticket_details per agent (customer_name,
category, resolution_hours) for client-side filtering.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-05 20:31:08 +02:00
fs
fc98f2684a fix(helpdesk): consolidation fixes from drift/performance/security review
- Fix docblock: getTicketsForTeam() uses PBI_FP_Tickets, not LV
- Process_Stage check now uses isset() guard — FP entity does not
  expose this field, prevents false positives from default 0
- Remove exception message from JSON error response (security)
- Remove dead JS functions: formatAge(), formatDate()
- Remove debug console.error and verbose error display from JS

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-05 20:25:51 +02:00
fs
4ce7a32bfc feat(helpdesk): redesign Historical tab with per-agent performance widgets
Each agent gets a card with three insight columns:
- Resolution time: Ø / Min / Max (formatted as hours or days)
- Top 3 customers by resolved ticket count (ranked list)
- Top 3 categories by resolved ticket count (ranked list)

Backend collects resolved ticket details per agent: customer names,
categories, and resolution hours. Aggregates top-3 and min/max/avg.
Replaces the old compact single-row layout with rich widget cards
matching the Current tab pattern.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-05 20:11:44 +02:00
fs
c80e317875 fix(helpdesk): fix contact name resolution when customerNo is empty
getContactsForCustomerWithMeta() returned early when customerNo OR
customerName was empty. The communication service calls it with empty
customerNo and only customerName — which always hit the early return,
preventing any contact lookup. Fix: only return early when BOTH are
empty, so the primary Company_Name query path works correctly.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-05 20:02:05 +02:00
fs
422746d141 fix(helpdesk): resolve all support actor codes instead of requiring exactly one per ticket
The previous logic only resolved support user codes to full names when
exactly one unique support code existed per ticket. Tickets with multiple
support codes (e.g. after reassignment) left all codes unresolved,
showing abbreviations like 'NKS' or 'FA' instead of full names.

Now: every support-role actor code in a ticket is mapped to that ticket's
Support_User_Name. For the debitor feed, codes are mapped on first
encounter to avoid conflicts from reassigned tickets.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-05 20:00:50 +02:00
fs
3123b8aa77 fix(helpdesk): edge-to-edge table in cards, larger avatar, sort by urgency
- Remove card padding, table goes full-width inside card border
- Section title gets own padding + subtle background as card header
- Avatar inline size increased from 1.6rem to 2rem
- Sort agents by critical tickets first (most urgent on top),
  then by open tickets. Unassigned queue stays last.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-05 19:49:40 +02:00
fs
9e94456759 feat(helpdesk): switch team query to PBI_FP_Tickets for richer data
FP entity provides Company_Contact_Name (full customer name),
Category_1_Description (readable category), and Last_Activity_By_Code.
Customer column shows name instead of number. Category shows readable
description instead of code. Debitor link only rendered when
customer_no is available (FP may not expose it).

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-05 19:47:10 +02:00
fs
d214f08c3f feat(helpdesk): add Description and Cust_Name from LV entity to team table
PBI_LV_Tickets exposes Description and Cust_Name (not Company_Contact_Name
which is FP-only). Add both to select. Table now shows 5 columns:
Ticket (link) | Customer name (link) | Description (ellipsis) |
Category | Last activity.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-05 19:44:53 +02:00
fs
cc1ae1aaca fix(helpdesk): replace description column with category in team ticket table
Description is not available on PBI_LV_Tickets. Show Category_1_Code
as its own 'Category' column instead of misusing it as description.
Remove unused ticket-desc CSS class.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-05 19:39:45 +02:00
fs
bfa3f5a154 fix(helpdesk): remove unavailable fields from LV ticket query
PBI_LV_Tickets does not expose Description or Company_Contact_Name —
BC returns HTTP 400 on unknown select fields. Revert to original LV
field set. Description falls back to Category_1_Code. Customer column
shows Customer_No when Company_Contact_Name is unavailable. Show API
error message in UI for debugging.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-05 19:38:26 +02:00
fs
72d39822bc fix(helpdesk): revert to PBI_LV_Tickets for team query, add error logging
PBI_FP_Tickets (FactPage) fails without a customer filter. Switch back
to PBI_LV_Tickets which works reliably unfiltered. Description and
Company_Contact_Name added to select — BC will return them if available,
otherwise they fall back to empty strings via existing null-coalescing.
Add console.error in JS catch to surface rendering errors.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-05 19:36:58 +02:00
fs
98ba2c024d fix(helpdesk): remove title line in team widgets, fix table columns and debitor links
- Hide section-title ::after line inside team widgets
- Fixed table-layout with consistent column widths (12/25/43/20%)
- Debitor link uses customer_no (route param), displays customer_name
- Add Customer_No to OData select for debitor link resolution
- Add customer_no to open_ticket_details backend structure

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-05 19:31:58 +02:00
fs
98f703b231 feat(helpdesk): enrich team ticket table with links, customer name, description, last activity
- Switch gateway from PBI_LV_Tickets to PBI_FP_Tickets to get
  Description and Company_Contact_Name fields
- Ticket number links to ticket detail page
- Customer column shows Company_Contact_Name with link to debitor detail
- Description column with text-overflow ellipsis
- Last activity column shows formatted date (was: age in hours)
- Backend provides last_activity ISO string and customer_name per ticket
- Table headers use i18n labels via data attributes

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-05 19:29:02 +02:00
fs
397f9fe659 feat(helpdesk): redesign team dashboard with agent cards and ticket details
Replace KPI tiles + table with visual agent cards showing workload at a
glance. Each card has the agent name, ticket count badge (warning-colored
when critical), a proportional load bar, and the actual open tickets
listed underneath (number, description, age). Critical tickets are
highlighted with warning color. Queue (unassigned) shown as dashed card
at top. Performance tab uses compact cards with success-colored bars.
Backend now returns open_ticket_details per member (sorted: critical
first, then by age descending).

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-05 19:09:15 +02:00
fs
cae66e5361 feat(helpdesk): add team workload dashboard with per-agent metrics
New standalone page (helpdesk/team) showing support agent workload
aggregated from BC tickets. KPI bar (open, unassigned, avg age, resolved),
per-agent table sorted by open tickets, period selector (30/90/180/365d).
Global OData query via getTicketsForTeam() without customer filter.
New permission helpdesk.team-workload.view with admin role assignment.
Includes 6 PHPUnit tests for buildTeamWorkloadDashboard().

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-05 18:46:54 +02:00
fs
7220aa7459 feat(helpdesk): add ticket grid filters and resolve support user names in chat
Add 3 new ticket list filters: Support User (dynamic select), Contact (dynamic
select), and Period (30/90/180/365 days). Extend categories endpoint to also
return support_users and contacts arrays from the same cached ticket data.
Add server-side filtering for all three in the data endpoint.

Resolve support user abbreviation codes to full names in the communication chat
widget. For single-ticket view, infer code-to-name from Support_User_Name when
only one support code appears. For debitor view, build map from already-fetched
entries across tickets with no additional API calls.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-05 16:37:07 +02:00