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>
This commit is contained in:
2026-04-15 16:44:12 +02:00
parent 364bda67a9
commit 44153f513f
11 changed files with 862 additions and 3 deletions

View File

@@ -2336,4 +2336,113 @@
}
}
/* Handover protocol schema editor */
.handover-schema-empty {
color: var(--app-muted-color);
font-style: italic;
padding: calc(var(--app-spacing) * 0.5) 0;
}
.handover-schema-list {
display: grid;
gap: calc(var(--app-spacing) * 0.5);
margin-bottom: calc(var(--app-spacing) * 0.75);
}
.handover-schema-row {
border: 1px solid var(--app-card-border-color);
border-radius: var(--app-border-radius);
padding: calc(var(--app-spacing) * 0.5);
background: var(--app-card-background-color);
}
.handover-schema-row-header {
display: flex;
align-items: flex-end;
justify-content: space-between;
gap: calc(var(--app-spacing) * 0.5);
margin-bottom: calc(var(--app-spacing) * 0.5);
}
.handover-schema-row-actions {
display: flex;
gap: calc(var(--app-spacing) * 0.25);
flex-shrink: 0;
}
.handover-schema-row-body {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(140px, 1fr));
gap: calc(var(--app-spacing) * 0.5);
align-items: end;
}
.handover-schema-field-group {
display: flex;
flex-direction: column;
gap: calc(var(--app-spacing) * 0.15);
}
.handover-schema-field-label {
font-size: 0.8125rem;
font-weight: 500;
color: var(--app-muted-color);
}
.handover-schema-field-group input[type="text"],
.handover-schema-field-group textarea,
.handover-schema-field-group select {
width: 100%;
}
.handover-schema-checkbox-label {
display: inline-flex;
align-items: center;
gap: calc(var(--app-spacing) * 0.25);
cursor: pointer;
padding-top: calc(var(--app-spacing) * 0.25);
}
.handover-schema-options {
margin-top: calc(var(--app-spacing) * 0.5);
padding-top: calc(var(--app-spacing) * 0.5);
border-top: 1px solid var(--app-card-border-color);
}
.handover-schema-options > strong {
display: block;
font-size: 0.8125rem;
margin-bottom: calc(var(--app-spacing) * 0.25);
}
.handover-schema-option-row {
display: flex;
gap: calc(var(--app-spacing) * 0.25);
align-items: center;
margin-bottom: calc(var(--app-spacing) * 0.25);
}
.handover-schema-option-row input[type="text"] {
flex: 1;
min-width: 0;
}
.handover-schema-add-button,
.handover-schema-add-option-button {
margin-top: calc(var(--app-spacing) * 0.25);
}
.handover-schema-button-move,
.handover-schema-button-remove,
.handover-schema-button-remove-option {
padding: calc(var(--app-spacing) * 0.15) calc(var(--app-spacing) * 0.35);
font-size: 0.8125rem;
cursor: pointer;
}
.handover-schema-button-remove,
.handover-schema-button-remove-option {
color: var(--app-del-color, #c62828);
}
}