- select multiple, no placeholder option (was causing two empty entries)
- value stored as JSON array string e.g. ["1","3"]
- on render decode JSON back to array for selected check
- both create and edit actions read field as array
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Adds 'user-select' to FIELD_TYPES so it appears in the schema editor
dropdown as 'Benutzerauswahl' / 'User selection'. Preview renders a
placeholder select. Translations added to both i18n files.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
New schema field type 'user-select' renders a dropdown of active tenant
users. Action files (create + edit) load tenant users when the schema
contains at least one user-select field and pass them to _form.phtml.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Select had name="domain_no" but JS was looking for input[name="domain_no"].
Moved name to a hidden input #wizard-domain-no; select syncs into it on
change; manual text input syncs into it on input event.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
When the domain API returns an empty list for a selected customer, the
domain select is replaced with two text inputs: domain name (required)
and URL (optional). JS syncs the manual values into the existing hidden
domain_no/domain_url fields so the server-side validation and save logic
is unchanged.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Using resolver 127.0.0.11 (Docker internal DNS) with set $php_upstream
forces nginx to re-resolve the php hostname dynamically instead of caching
the IP at startup — eliminates 502 after php container is recreated.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
QG-009 was failing on GitHub Actions runner because ~/.codex does not
exist on ubuntu-latest and CODEX_HOME is not set. The check is a
local-developer tool only — CI runners do not have Codex installed.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
css-contract-check.sh expects @layer pages { (block syntax),
not @layer pages; (statement syntax) — regex captures up to
the first whitespace or { so the semicolon was included in the
detected layer name.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Required by css-contract-check.sh — all files under pages/ must declare
their layer near the top of the file.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Same fix as qa-required.yaml — css-contract-check.sh calls rg on
the host runner, not inside Docker.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
CSS/docs/JS contract checks call rg directly on the host runner,
not inside Docker, so ripgrep must be present on the ubuntu runner.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Add .gitea/workflows/deploy.yaml: QA → build prod image → SCP to server → docker load + compose up + module:sync
- Fix Dockerfile prod stage: copy module web assets directly from modules/*/web/ instead of resolving symlinks (symlinks are excluded via .dockerignore and break on Windows/CI)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Edit page: managers see 'Delete handover' button in aside with confirm dialog
- Delete handler: POST action=delete → deleteByIds → redirect to list
- Flash scope set to null (global) so toast shows reliably after redirect
regardless of locale prefix or query string in URL
- i18n: added Delete handover / Delete this handover? / Handover deleted (de+en)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Tabs: replace open/closed with active (draft+in_progress+under_review)
and done (completed+archived) — records now appear in the correct tab
Create wizard assign mode: redirect to list with flash message after
assigning instead of opening edit page
i18n: add In progress / Done tab labels and wizard strings (de + en)
Nikita Soldatov note: user does not exist in DB — needs to be created
via admin user management
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
HandoverServiceTest: 30 new cases covering:
- assign(): happy path, draft→in_progress transition, permission check,
zero assignedTo, not found, email sent, due date passed to repo
- submitForReview(): assignee and manager paths, non-assignee denied,
wrong status (under_review/completed), revision created, email sent
- resendNotification(): happy path, permission denied, no assignee
- statusLabel/Variant for under_review
HandoverNotificationServiceTest: 9 new cases covering:
- notifyAssigned: sends correct template+vars, skips on empty/null email,
fallback dash for empty debitor and due_date
- notifyReviewRequested: sends to manager, skips on no email,
fallback dash for empty assignee name
48 tests total, all passing.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
MintyPHP DB returns rows as $row[$table][$field], so tenant users
come back as $u['u']['id'] not $u['id']. Added fallback for both.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>