Commit Graph

390 Commits

Author SHA1 Message Date
fs
2a4ccc8f1a chore(qa): clear stale PHPStan baseline entry and PHPUnit deprecations
Two pre-existing findings surfaced during the export-feature test run.
Both are trivial housekeeping, not related to the feature:

- phpstan-baseline.neon: drop the stale ignore for
  RequestInput::wantsJson — the method is now called from the
  helpdesk security-level endpoint and the new export flavor helper,
  so PHPStan no longer matched the pattern and failed with
  `ignore.unmatched` (non-ignorable).

- tests/Service/Auth/SsoUserLinkServiceTest.php: replace the deprecated
  isType('array') / isType('string') assertions with the
  PHPUnit 13-compatible isArray() / isString() equivalents.

Gates after cleanup: PHPUnit 1880 OK (0 deprecations), PHPStan 0 errors.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-21 22:01:12 +02:00
fs
5ba086adee refactor(admin/users): migrate CSV export to core primitive
Replace the inline export implementation with the new generic primitive
(core/Service/Export + helpers/export + app-list-export.js). No more
hand-rolled fputcsv loop, header setup, or escape closure duplicated
here — one implementation, tested once, reused everywhere.

- export().php: uses exportRequireGetRequest, exportCapLimit,
  exportResolveFlavor, ExportColumn[], exportSendCsv. Phone/mobile/
  short_dial keep allowSignedNumeric=true so +49 numbers render
  untouched.
- export(none).phtml: deleted — headers + fputcsv now live in the
  core helper.
- index(default).phtml: inline <details class="dropdown"> "Export CSV"
  replaced by the reusable app-list-export-dropdown partial, exposing
  CSV and Excel flavors. exportUrl added to pageConfig.
- app-users-list.js: hand-rolled export-button binding removed in
  favor of initListExport({ gridConfig, exportUrl }).
- admin-users-index.js: forwards config.exportUrl into
  initUsersListPage.

Net effect: ~100 lines of duplicated export plumbing removed from the
users page; users gain the Excel flavor for free.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-21 21:57:31 +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
c3de7d4238 feat(js): enforce global lifecycle hard-cuts and helpdesk list adapter 2026-04-20 23:01:54 +02:00
fs
c163393cc4 refactor(js): migrate core list pages to list module factory 2026-04-20 22:54:19 +02:00
fs
5219bbaff2 refactor(js): migrate audit and addressbook list pages to list factory 2026-04-20 22:45:54 +02:00
fs
7c47e818f2 feat(js): harden global HTTP and module import contracts 2026-04-20 22:41:07 +02:00
fs
f189ef9df6 feat(js): add app-http contracts and migrate helpdesk runtime layer 2026-04-20 22:31:13 +02:00
fs
ec2f03e0cf helpdesk: remove unused UpdateRepository::findById 2026-04-20 19:58:51 +02:00
fs
5982e749d7 css: consolidate core stylesheet entrypoint and add contract check 2026-04-20 19:55:49 +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
320f0a5a00 feat(helpdesk): polish handover wizard and list page UI
- Simplify wizard step indicator markup (divs instead of ol/li)
- Narrow wizard content width, compact spacing
- Use app-action-success button style on list titlebar
- Add handover-form-heading class for consistent section spacing
- Remove unused wrapper divs and redundant CSS

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-15 21:29:43 +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
2600e62a73 fix(helpdesk): tighten debitor lookup permission and use Router::json
Require ABILITY_HANDOVERS_CREATE instead of generic ABILITY_ACCESS.
Replace manual header + echo with Router::json() for consistency.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-15 21:29:31 +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
639ff161b8 fix(helpdesk): add handover permissions to admin role seed in init.sql
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-15 20:47:06 +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
57b7920098 fix(flash): make flash messages one-shot per request 2026-04-15 18:45:45 +02:00
fs
63be2def76 docs: add UI/UX component guidelines and improve breadcrumb/tab-panel styling
Add "Komponenten in Formularen" section to boundaries-ui.md with rules
for core button classes, margin resets, checkbox sizing, sticky positioning,
icon-only buttons, responsive grids, and CSS variables. Add matching
quick-reference bullets to CLAUDE.md UI Patterns section.

Fix breadcrumb wrapping: nowrap + overflow hidden + flex-shrink for
single-line behavior. Add data-tab-panel-wide opt-out for full-width
tab panels (default 70ch).

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-15 18:44:29 +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
20f582f459 refactor(helpdesk): remove redundant readonly fields from software product form
Code and BC description are already shown in the aside — no need to
duplicate them as readonly inputs in the form.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-14 22:26:18 +02:00
fs
14ba29c468 fix(helpdesk): align software products aside to core detail page standard
Use badge-list wrapper, <p><small> pattern, <hr> separators, and dt()
for date formatting — matching the departments edit page convention.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-14 22:24:09 +02:00
fs
06c7651be7 fix(helpdesk): fix Session class not found in software products form
Use fully qualified \MintyPHP\Session in _form.phtml and remove
duplicate CSRF token call from edit template (already in form partial).

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-14 22:23:14 +02:00
fs
49078f1853 fix(scheduled-jobs): handle null last_run_status in jobs list data endpoint
Jobs that have never run have last_run_status=NULL which caused a crash
when calling ->value and ->badgeVariant() on null. Use explicit null
checks instead of direct property access.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-14 22:21:42 +02:00
fs
2424f24d27 fix(helpdesk): add software-products.manage permission to admin role seed
The new permission was missing from the admin role seed in init.sql and
needed an idempotent update script for existing installations.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-14 22:18:13 +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
a736566071 chore: upgrade PHPStan to 2.x and add unused-public dead code detection
- Upgrade phpstan/phpstan 1.12 → 2.1
- Add tomasvotruba/unused-public 2.2 for automatic dead code detection
- Generate baseline (486 existing findings) to keep CI green
- Remove stale @phpstan-ignore annotation in login page (ternary.alwaysFalse)
- New code is fully checked against PHPStan 2 + unused-public rules

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-14 09:02:43 +02:00
fs
94b746ddf8 refactor: remove 13 dead code artifacts across core/
Dead methods (9):
- Command::bootstrapModuleApp() — unused by all subclasses
- SearchConfig::providerCoverageGaps() — diagnostic method, never called
- RequestInput::queryArray() — no callers
- LocaleResolver::getAvailableLocales() / getDefaultLocale() — unused getters
- EmailVerificationService::getExpiryMinutes() — constant wrapper, never called
- AuthService::loginAndRedirect() — convenience wrapper, never called
- DepartmentService::listForUserAssignments() — no callers
- UserTenantContextService::getAvailableDepartmentsByTenant() — no callers

Dead functions (3):
- navActivePublicPages(), appNormalizeStringList(), appNormalizePositiveIntList()

Dead constant (1):
- PermissionService::API_DOCS_VIEW — duplicated via other paths

Orphaned file (1):
- bin/cli-bootstrap.php — never included anywhere

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-14 08:48:29 +02:00
fs
b1a907b79c feat(modules): harden module platform with event constants, validation checks, and CLI improvements
- Add ModuleEvents constants class with all 8 event strings; update dispatch call sites
- ValidateCommand: check AuthorizationPolicy container registration (check 20)
- ValidateCommand: warn on unknown event_listeners keys (check 21)
- make:module --simple flag for minimal manifests
- module:deactivate reverse-dependency warning
- 12 new PHPUnit tests covering all improvements

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-14 08:23:04 +02:00
fs
143d887a5c fix: patch remaining lib/ references missed in core/ rename
CliAppBootstrap and ModuleCliRuntime still required from the old
lib/ path, which would break all CLI commands. Also fixes stale
lib/ references in docs, test assertion messages, and a PHPDoc
comment.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-14 07:54:02 +02:00
fs
0e86925464 refactor: rename lib/ to core/ for clearer core-module separation
Rename the top-level lib/ directory to core/ so the project root
immediately communicates which code is core platform and which lives
in modules/. PHP namespaces (MintyPHP\*) are unchanged — only the
Composer PSR-4 path mapping moves from lib/ to core/.

Module-internal lib/ directories (modules/*/lib/) are untouched.

Updated across the full stack:
- composer.json PSR-4 mapping
- bootstrap entry points (web/index.php, bin/*, tests/bootstrap.php)
- tooling configs (phpstan.neon, phpunit.xml, php-cs-fixer)
- 26 architecture contract tests
- enforcement-policy, guard-catalog, quality-gates
- all documentation (CLAUDE.md, README, 25 docs/, .agents/skills/)
- bin/qa-extended.sh search paths
- .claude/settings.local.json permission paths

Workflow: .agents/runs/CORE-LIB-RENAME-001/ (Analyst → Planner →
Executor → Code Review (4 findings fixed) → Security Review →
Acceptance Test → Finalizer)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-13 23:20:42 +02:00
fs
7c10fadcb9 docs: fix guard drift in reviewer prompts and add missing README feature
reviewer-code.md was missing 4 of 17 assigned guards (LAYERS, META,
MODULE, TAXONOMY); reviewer-security.md was missing GR-SEC-010. README
did not list the Scheduled Jobs feature.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-13 22:31:04 +02:00
fs
99f8b55d49 refactor: consolidate JS toggle components, grid query parsers, and add tests
JS components: migrate app-custom-field-options-toggle, app-color-default-toggle,
and app-settings-telemetry to shared conditional-controls factory/utility,
removing duplicated syncControlState logic and init boilerplate.

grid.php: extract gridQueryCsvInput() to DRY up 3 CSV parsers, simplify
multi_csv handler via gridNormalizeLabelList, delegate order type to gridQueryEnum.

Tests: add 23 SearchQueryNormalizer tests (LIKE escaping, wildcards, Unicode)
and 7 additional Crypto edge-case tests (empty input, missing fields, special
chars, binary content, truncation).

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-13 22:22:15 +02:00
fs
7496903544 chore(i18n): remove 180 orphaned translation keys
Audited all 1441 keys against 12 translation pipelines (direct t() calls,
flash messages, enum labelToken(), filter schemas, module manifests, theme
configs, etc.). Removed 180 keys unused by any pipeline from both language
files (1441 → 1261 keys, parity maintained).

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-13 21:57:45 +02:00
fs
31ecb40235 refactor: clean up web assets and consolidate PHP helpers
- Remove 2079 unused bootstrap-icons SVGs + min.css (~9.3 MB saved, app uses font approach only)
- Remove unused editorjs.umd.js (only .mjs is imported)
- Extract shared syncControlState into app-conditional-controls.js, deduplicate SSO/LDAP toggles
- Add settingToBool() helper to DRY up repeated bool-parsing pattern across 6 call sites
- Merge single-function auth.php into app.php, delete auth.php
- Extract 9 branding functions from app.php into dedicated branding.php (app.php 709→528 lines)
- Remove empty web/img/ directory

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-13 21:31:59 +02:00
fs
5f156661fd docs: fix guard count drift in CLAUDE.md, README, and workflow
Update security guard references from 9 to 10 (GR-SEC-010 was missing),
code reviewer guards from 13 to 17 (new GR-CORE-LAYERS, GR-CORE-MODULE,
GR-CORE-META, GR-UI-TAXONOMY), and total guard count from 22 to 27.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-13 20:50:24 +02:00
fs
98afac24b1 test: add 92 tests for 12 untested gateway and service classes
Cover Settings gateways (Session, LoginPersistence, SystemAudit, Smtp,
FrontendTelemetry, Metadata, App), Auth gateways (ExternalIdentity,
Tenant), DirectorySettings, UserSettings, and HotkeyService.
Gateway test coverage rises from 25% to 52%, overall service/gateway
coverage from 58.6% to 70.7%.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-13 20:46:20 +02:00
fs
ea0b31ba67 chore(guards): map all 85 architecture tests to guards, add 4 new guards
Add GR-CORE-LAYERS (layer isolation), GR-CORE-MODULE (module boundaries),
GR-CORE-META (agent/codex system integrity), GR-UI-TAXONOMY (status
taxonomy consistency). Extend existing guards GR-SEC-008, GR-UI-DETAIL,
GR-UI-LIST, GR-UI-A11Y, GR-UI-REUSE, GR-TEST-002, GR-SEC-002 with
their previously unmapped architecture tests. Coverage goes from 23/85
mapped tests (27%) to 85/85 (100%).

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-13 20:39:51 +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
a17c2c7cea refactor(audit): decouple API audit classes from core via interfaces
Core code (lib/) no longer imports any MintyPHP\Module\Audit\* classes directly.
New ApiAuditServiceInterface and ApiSystemAuditReporterInterface follow the
existing pattern (interface + null implementation in core, concrete binding
from module registrar). DoctorRunner and helpers/app.php now resolve through
DI interfaces, ensuring the audit module remains fully optional.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-13 19:19:17 +02:00
fs
4967095bb8 fix: resolve all quality gate failures — i18n key, typography tokens, PHPStan errors, style
- Add missing i18n key "LDAP attribute map could not be encoded" (de+en)
- Replace raw font-weight/line-height with design tokens in app-topbar.css
- Fix import ordering in SchedulerRunService.php (php-cs-fixer)
- Fix PHPStan mock chain errors in DebitorDetailServiceTest and TenantSsoServiceTest (PHPUnit 12 API)
- Simplify redundant comparison in NotificationService dedupe window
- Add PHPStan type hint for mock callback array in SchedulerJobFailedNotificationListenerTest
- Fix undefined variable and add flow-analysis suppression in login().php
- Increase dev PHP memory_limit to 1G for PHPStan (prod stays 256M)

All 6 quality gates now pass: QG-001 (1618 tests), QG-002 (0 errors),
QG-003 (6630 assertions), QG-006 (0 fixable), QG-008, QG-009.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-13 19:06:12 +02:00