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>
This commit is contained in:
@@ -2336,7 +2336,140 @@
|
||||
}
|
||||
}
|
||||
|
||||
/* Handover protocol schema editor */
|
||||
/* Handover protocol schema editor — split layout */
|
||||
.handover-schema-split {
|
||||
display: grid;
|
||||
grid-template-columns: 1fr 1fr;
|
||||
gap: var(--app-spacing);
|
||||
align-items: start;
|
||||
}
|
||||
|
||||
@media (max-width: 960px) {
|
||||
.handover-schema-split {
|
||||
grid-template-columns: 1fr;
|
||||
}
|
||||
}
|
||||
|
||||
.handover-schema-split-preview {
|
||||
border: 1px solid var(--app-card-border-color);
|
||||
border-radius: var(--app-border-radius);
|
||||
background: var(--app-card-background-color);
|
||||
position: sticky;
|
||||
top: calc(var(--app-topbar-height) + var(--app-spacing) * 5);
|
||||
}
|
||||
|
||||
.handover-schema-preview-header {
|
||||
font-weight: 600;
|
||||
padding: calc(var(--app-spacing) * 0.6) calc(var(--app-spacing) * 0.75);
|
||||
border-bottom: 1px solid var(--app-card-border-color);
|
||||
font-size: 0.875rem;
|
||||
}
|
||||
|
||||
.handover-schema-preview {
|
||||
padding: calc(var(--app-spacing) * 0.75);
|
||||
}
|
||||
|
||||
.handover-schema-preview-empty {
|
||||
color: var(--app-muted-color);
|
||||
font-style: italic;
|
||||
}
|
||||
|
||||
.handover-schema-preview-form {
|
||||
display: grid;
|
||||
gap: calc(var(--app-spacing) * 0.6);
|
||||
}
|
||||
|
||||
.handover-schema-preview-form h3 {
|
||||
margin: calc(var(--app-spacing) * 0.25) 0 0;
|
||||
font-size: 1rem;
|
||||
}
|
||||
|
||||
.handover-schema-preview-form p {
|
||||
margin: 0;
|
||||
color: var(--app-muted-color);
|
||||
}
|
||||
|
||||
.handover-schema-preview-field {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: calc(var(--app-spacing) * 0.15);
|
||||
}
|
||||
|
||||
.handover-schema-preview-field label {
|
||||
font-size: 0.8125rem;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.handover-schema-preview-field input:not([type="checkbox"]),
|
||||
.handover-schema-preview-field textarea,
|
||||
.handover-schema-preview-field select {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.handover-schema-preview-field:has(> input[type="checkbox"]) {
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
gap: 0;
|
||||
}
|
||||
|
||||
.handover-schema-preview-field > input[type="checkbox"] {
|
||||
width: 1.25em;
|
||||
height: 1.25em;
|
||||
flex-shrink: 0;
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
.handover-schema-preview-form [data-field-index] {
|
||||
transition: background-color 0.2s ease, box-shadow 0.2s ease;
|
||||
border-radius: var(--app-border-radius);
|
||||
padding: calc(var(--app-spacing) * 0.3);
|
||||
margin: calc(var(--app-spacing) * -0.3);
|
||||
}
|
||||
|
||||
.handover-schema-preview-highlight {
|
||||
background-color: var(--app-secondary-focus);
|
||||
box-shadow: inset 0 0 0 1px var(--app-card-border-color);
|
||||
}
|
||||
|
||||
.handover-schema-preview-required {
|
||||
color: var(--app-danger-color, hsl(0 70% 55%));
|
||||
}
|
||||
|
||||
/* Handover protocol schema editor — reset core margin on buttons/inputs/selects */
|
||||
.handover-schema-split-editor button[type="button"],
|
||||
.handover-schema-row select,
|
||||
.handover-schema-row input,
|
||||
.handover-schema-row textarea,
|
||||
.handover-schema-row label {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
/* Handover protocol schema editor — insert divider */
|
||||
.handover-schema-insert-divider {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: calc(var(--app-spacing) * 0.5);
|
||||
padding: calc(var(--app-spacing) * 0.25) 0;
|
||||
}
|
||||
|
||||
.handover-schema-insert-divider::before,
|
||||
.handover-schema-insert-divider::after {
|
||||
content: '';
|
||||
flex: 1;
|
||||
height: 1px;
|
||||
background: var(--app-card-border-color);
|
||||
}
|
||||
|
||||
.handover-schema-insert-button {
|
||||
width: 1.5rem;
|
||||
height: 1.5rem;
|
||||
padding: 0;
|
||||
border-radius: 50%;
|
||||
font-size: 0.75rem;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
/* Handover protocol schema editor — field cards */
|
||||
.handover-schema-empty {
|
||||
color: var(--app-muted-color);
|
||||
font-style: italic;
|
||||
@@ -2345,31 +2478,41 @@
|
||||
|
||||
.handover-schema-list {
|
||||
display: grid;
|
||||
gap: calc(var(--app-spacing) * 0.5);
|
||||
margin-bottom: calc(var(--app-spacing) * 0.75);
|
||||
gap: calc(var(--app-spacing) * 0.75);
|
||||
margin-bottom: var(--app-spacing);
|
||||
}
|
||||
|
||||
.handover-schema-row {
|
||||
border: 1px solid var(--app-card-border-color);
|
||||
border-radius: var(--app-border-radius);
|
||||
padding: calc(var(--app-spacing) * 0.5);
|
||||
padding: calc(var(--app-spacing) * 0.75);
|
||||
background: var(--app-card-background-color);
|
||||
}
|
||||
|
||||
.handover-schema-row-header {
|
||||
display: flex;
|
||||
align-items: flex-end;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
gap: calc(var(--app-spacing) * 0.5);
|
||||
margin-bottom: calc(var(--app-spacing) * 0.5);
|
||||
}
|
||||
|
||||
.handover-schema-row-title {
|
||||
font-size: 0.8125rem;
|
||||
font-weight: 600;
|
||||
color: var(--app-muted-color);
|
||||
}
|
||||
|
||||
.handover-schema-row-actions {
|
||||
display: flex;
|
||||
gap: calc(var(--app-spacing) * 0.25);
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.handover-schema-row > .handover-schema-field-group {
|
||||
margin-bottom: calc(var(--app-spacing) * 0.5);
|
||||
}
|
||||
|
||||
.handover-schema-row-body {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fit, minmax(140px, 1fr));
|
||||
@@ -2377,10 +2520,14 @@
|
||||
align-items: end;
|
||||
}
|
||||
|
||||
.handover-schema-row-required {
|
||||
margin-top: calc(var(--app-spacing) * 0.4);
|
||||
}
|
||||
|
||||
.handover-schema-field-group {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: calc(var(--app-spacing) * 0.15);
|
||||
gap: calc(var(--app-spacing) * 0.2);
|
||||
}
|
||||
|
||||
.handover-schema-field-label {
|
||||
@@ -2400,26 +2547,30 @@
|
||||
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);
|
||||
margin-top: calc(var(--app-spacing) * 0.65);
|
||||
padding-top: calc(var(--app-spacing) * 0.65);
|
||||
border-top: 1px solid var(--app-card-border-color);
|
||||
}
|
||||
|
||||
.handover-schema-options > strong {
|
||||
display: block;
|
||||
.handover-schema-options-header {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
margin-bottom: calc(var(--app-spacing) * 0.4);
|
||||
}
|
||||
|
||||
.handover-schema-options-header > strong {
|
||||
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);
|
||||
gap: calc(var(--app-spacing) * 0.35);
|
||||
align-items: center;
|
||||
margin-bottom: calc(var(--app-spacing) * 0.25);
|
||||
margin-bottom: calc(var(--app-spacing) * 0.35);
|
||||
}
|
||||
|
||||
.handover-schema-option-row input[type="text"] {
|
||||
@@ -2427,22 +2578,4 @@
|
||||
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);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -15,7 +15,7 @@ if (container) {
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove all child nodes from an element (safe alternative to innerHTML = '').
|
||||
* Remove all child nodes from an element (safe alternative to setting markup).
|
||||
* @param {HTMLElement} el
|
||||
*/
|
||||
function clearElement(el) {
|
||||
@@ -28,12 +28,14 @@ function init(root) {
|
||||
const t = JSON.parse(root.dataset.translations || '{}');
|
||||
const initialFields = JSON.parse(root.dataset.initialFields || '[]');
|
||||
const hiddenInput = document.getElementById('handover-schema-json');
|
||||
const previewContainer = document.getElementById('handover-schema-preview');
|
||||
const form = hiddenInput?.closest('form');
|
||||
|
||||
/** @type {Array<object>} */
|
||||
let fields = initialFields.map((f) => ({ ...f, options: f.options ? f.options.map((o) => ({ ...o })) : [] }));
|
||||
|
||||
render();
|
||||
renderPreview();
|
||||
syncHiddenInput();
|
||||
|
||||
if (form) {
|
||||
@@ -52,21 +54,33 @@ function init(root) {
|
||||
const list = document.createElement('div');
|
||||
list.className = 'handover-schema-list';
|
||||
fields.forEach((field, index) => {
|
||||
if (index > 0) {
|
||||
list.appendChild(buildInsertDivider(index));
|
||||
}
|
||||
list.appendChild(buildFieldRow(field, index));
|
||||
});
|
||||
root.appendChild(list);
|
||||
}
|
||||
|
||||
const addButton = document.createElement('button');
|
||||
addButton.type = 'button';
|
||||
addButton.className = 'handover-schema-add-button';
|
||||
addButton.textContent = t.add_field || 'Add field';
|
||||
addButton.addEventListener('click', () => {
|
||||
fields.push({ type: 'text', key: '', label: '', required: false, options: [] });
|
||||
root.appendChild(buildInsertDivider(fields.length));
|
||||
}
|
||||
|
||||
function buildInsertDivider(insertIndex) {
|
||||
const divider = document.createElement('div');
|
||||
divider.className = 'handover-schema-insert-divider';
|
||||
const button = document.createElement('button');
|
||||
button.type = 'button';
|
||||
button.className = 'secondary outline small handover-schema-insert-button';
|
||||
button.setAttribute('data-tooltip', t.add_field || 'Add field');
|
||||
button.setAttribute('aria-label', t.add_field || 'Add field');
|
||||
button.appendChild(createIcon('bi-plus'));
|
||||
button.addEventListener('click', () => {
|
||||
fields.splice(insertIndex, 0, { type: 'text', key: '', label: '', required: false, options: [] });
|
||||
render();
|
||||
notifyChange();
|
||||
});
|
||||
root.appendChild(addButton);
|
||||
divider.appendChild(button);
|
||||
return divider;
|
||||
}
|
||||
|
||||
function buildFieldRow(field, index) {
|
||||
@@ -75,15 +89,49 @@ function init(root) {
|
||||
|
||||
const row = document.createElement('div');
|
||||
row.className = 'handover-schema-row';
|
||||
row.dataset.fieldIndex = index;
|
||||
row.addEventListener('mouseenter', () => highlightPreview(index, true));
|
||||
row.addEventListener('mouseleave', () => highlightPreview(index, false));
|
||||
|
||||
// --- Header with type + actions ---
|
||||
// --- Header: "Feld #N" title + action icons ---
|
||||
const header = document.createElement('div');
|
||||
header.className = 'handover-schema-row-header';
|
||||
|
||||
// Type select
|
||||
const typeGroup = createGroup(t.field_type || 'Type');
|
||||
const title = document.createElement('span');
|
||||
title.className = 'handover-schema-row-title';
|
||||
title.textContent = (t.field || 'Field') + ' #' + (index + 1);
|
||||
header.appendChild(title);
|
||||
|
||||
const actions = document.createElement('div');
|
||||
actions.className = 'handover-schema-row-actions';
|
||||
|
||||
if (index > 0) {
|
||||
actions.appendChild(createIconButton(
|
||||
'bi-arrow-up', 'secondary outline small',
|
||||
() => { [fields[index - 1], fields[index]] = [fields[index], fields[index - 1]]; render(); notifyChange(); },
|
||||
(t.move_up || 'Move up') + ' ' + (t.field_label || 'field') + ' ' + (index + 1),
|
||||
));
|
||||
}
|
||||
if (index < fields.length - 1) {
|
||||
actions.appendChild(createIconButton(
|
||||
'bi-arrow-down', 'secondary outline small',
|
||||
() => { [fields[index], fields[index + 1]] = [fields[index + 1], fields[index]]; render(); notifyChange(); },
|
||||
(t.move_down || 'Move down') + ' ' + (t.field_label || 'field') + ' ' + (index + 1),
|
||||
));
|
||||
}
|
||||
actions.appendChild(createIconButton(
|
||||
'bi-trash', 'danger outline small',
|
||||
() => { fields.splice(index, 1); render(); notifyChange(); },
|
||||
(t.remove_field || 'Remove field') + ' ' + (index + 1),
|
||||
));
|
||||
|
||||
header.appendChild(actions);
|
||||
row.appendChild(header);
|
||||
|
||||
// --- Type select (full width) ---
|
||||
const typeGroup = createGroup(t.field_type || 'Field type');
|
||||
const typeSelect = document.createElement('select');
|
||||
typeSelect.setAttribute('aria-label', (t.field_type || 'Type') + ' ' + (index + 1));
|
||||
typeSelect.setAttribute('aria-label', (t.field_type || 'Field type') + ' ' + (index + 1));
|
||||
FIELD_TYPES.forEach((ft) => {
|
||||
const opt = document.createElement('option');
|
||||
opt.value = ft;
|
||||
@@ -107,57 +155,12 @@ function init(root) {
|
||||
notifyChange();
|
||||
});
|
||||
typeGroup.appendChild(typeSelect);
|
||||
header.appendChild(typeGroup);
|
||||
row.appendChild(typeGroup);
|
||||
|
||||
// Actions
|
||||
const actions = document.createElement('div');
|
||||
actions.className = 'handover-schema-row-actions';
|
||||
|
||||
if (index > 0) {
|
||||
actions.appendChild(createActionButton(t.move_up || 'Up', 'handover-schema-button-move', () => {
|
||||
[fields[index - 1], fields[index]] = [fields[index], fields[index - 1]];
|
||||
render();
|
||||
notifyChange();
|
||||
}, (t.move_up || 'Move up') + ' ' + (t.field_label || 'field') + ' ' + (index + 1)));
|
||||
}
|
||||
if (index < fields.length - 1) {
|
||||
actions.appendChild(createActionButton(t.move_down || 'Down', 'handover-schema-button-move', () => {
|
||||
[fields[index], fields[index + 1]] = [fields[index + 1], fields[index]];
|
||||
render();
|
||||
notifyChange();
|
||||
}, (t.move_down || 'Move down') + ' ' + (t.field_label || 'field') + ' ' + (index + 1)));
|
||||
}
|
||||
actions.appendChild(createActionButton(t.remove_field || 'Remove', 'handover-schema-button-remove', () => {
|
||||
fields.splice(index, 1);
|
||||
render();
|
||||
notifyChange();
|
||||
}, (t.remove_field || 'Remove field') + ' ' + (index + 1)));
|
||||
|
||||
header.appendChild(actions);
|
||||
row.appendChild(header);
|
||||
|
||||
// --- Body fields ---
|
||||
// --- Body: label input ---
|
||||
const body = document.createElement('div');
|
||||
body.className = 'handover-schema-row-body';
|
||||
|
||||
// Key (hidden for display-only types)
|
||||
if (!isDisplayOnly) {
|
||||
const keyGroup = createGroup(t.field_key || 'Key');
|
||||
const keyInput = document.createElement('input');
|
||||
keyInput.type = 'text';
|
||||
keyInput.value = field.key || '';
|
||||
keyInput.placeholder = 'e.g. handover_date';
|
||||
keyInput.pattern = '[a-z][a-z0-9_]*';
|
||||
keyInput.setAttribute('aria-label', (t.field_key || 'Key') + ' ' + (index + 1));
|
||||
keyInput.addEventListener('input', () => {
|
||||
field.key = keyInput.value;
|
||||
notifyChange();
|
||||
});
|
||||
keyGroup.appendChild(keyInput);
|
||||
body.appendChild(keyGroup);
|
||||
}
|
||||
|
||||
// Label (or text content for paragraph)
|
||||
const labelKey = field.type === 'paragraph' ? (t.text_content || 'Text content') : (t.field_label || 'Label');
|
||||
const labelGroup = createGroup(labelKey);
|
||||
const labelInput = document.createElement(field.type === 'paragraph' ? 'textarea' : 'input');
|
||||
@@ -168,41 +171,52 @@ function init(root) {
|
||||
}
|
||||
labelInput.value = field.label || '';
|
||||
labelInput.setAttribute('aria-label', labelKey + ' ' + (index + 1));
|
||||
labelInput.addEventListener('input', () => {
|
||||
field.label = labelInput.value;
|
||||
notifyChange();
|
||||
});
|
||||
labelInput.addEventListener('input', () => { field.label = labelInput.value; notifyChange(); });
|
||||
labelGroup.appendChild(labelInput);
|
||||
body.appendChild(labelGroup);
|
||||
|
||||
// Required toggle (hidden for display-only types)
|
||||
row.appendChild(body);
|
||||
|
||||
// --- Required toggle — own row below key/label ---
|
||||
if (!isDisplayOnly) {
|
||||
const reqGroup = createGroup('');
|
||||
const reqRow = document.createElement('div');
|
||||
reqRow.className = 'handover-schema-row-required';
|
||||
const reqLabel = document.createElement('label');
|
||||
reqLabel.className = 'handover-schema-checkbox-label';
|
||||
const reqInput = document.createElement('input');
|
||||
reqInput.type = 'checkbox';
|
||||
reqInput.checked = !!field.required;
|
||||
reqInput.addEventListener('change', () => {
|
||||
field.required = reqInput.checked;
|
||||
notifyChange();
|
||||
});
|
||||
reqInput.addEventListener('change', () => { field.required = reqInput.checked; notifyChange(); });
|
||||
reqLabel.appendChild(reqInput);
|
||||
reqLabel.appendChild(document.createTextNode(' ' + (t.required || 'Required')));
|
||||
reqGroup.appendChild(reqLabel);
|
||||
body.appendChild(reqGroup);
|
||||
reqRow.appendChild(reqLabel);
|
||||
row.appendChild(reqRow);
|
||||
}
|
||||
|
||||
row.appendChild(body);
|
||||
|
||||
// --- Select options ---
|
||||
if (isSelect) {
|
||||
const optionsSection = document.createElement('div');
|
||||
optionsSection.className = 'handover-schema-options';
|
||||
|
||||
const optionsHeader = document.createElement('div');
|
||||
optionsHeader.className = 'handover-schema-options-header';
|
||||
const optionsTitle = document.createElement('strong');
|
||||
optionsTitle.textContent = t.options || 'Options';
|
||||
optionsSection.appendChild(optionsTitle);
|
||||
optionsHeader.appendChild(optionsTitle);
|
||||
|
||||
const addOptButton = document.createElement('button');
|
||||
addOptButton.type = 'button';
|
||||
addOptButton.className = 'secondary outline small';
|
||||
addOptButton.setAttribute('data-tooltip', t.add_option || 'Add option');
|
||||
addOptButton.setAttribute('aria-label', t.add_option || 'Add option');
|
||||
addOptButton.appendChild(createIcon('bi-plus-lg'));
|
||||
addOptButton.addEventListener('click', () => {
|
||||
field.options.push({ value: '', label: '' });
|
||||
render();
|
||||
notifyChange();
|
||||
});
|
||||
optionsHeader.appendChild(addOptButton);
|
||||
optionsSection.appendChild(optionsHeader);
|
||||
|
||||
if (!field.options) field.options = [];
|
||||
|
||||
@@ -210,48 +224,23 @@ function init(root) {
|
||||
const optRow = document.createElement('div');
|
||||
optRow.className = 'handover-schema-option-row';
|
||||
|
||||
const valInput = document.createElement('input');
|
||||
valInput.type = 'text';
|
||||
valInput.value = opt.value || '';
|
||||
valInput.placeholder = t.option_value || 'Value';
|
||||
valInput.setAttribute('aria-label', (t.option_value || 'Value') + ' ' + (optIndex + 1));
|
||||
valInput.addEventListener('input', () => {
|
||||
opt.value = valInput.value;
|
||||
notifyChange();
|
||||
});
|
||||
optRow.appendChild(valInput);
|
||||
|
||||
const lblInput = document.createElement('input');
|
||||
lblInput.type = 'text';
|
||||
lblInput.value = opt.label || '';
|
||||
lblInput.placeholder = t.option_label || 'Label';
|
||||
lblInput.placeholder = (t.option_label || 'Label') + ' ' + (optIndex + 1);
|
||||
lblInput.setAttribute('aria-label', (t.option_label || 'Label') + ' ' + (optIndex + 1));
|
||||
lblInput.addEventListener('input', () => {
|
||||
opt.label = lblInput.value;
|
||||
notifyChange();
|
||||
});
|
||||
lblInput.addEventListener('input', () => { opt.label = lblInput.value; notifyChange(); });
|
||||
optRow.appendChild(lblInput);
|
||||
|
||||
optRow.appendChild(createActionButton(t.remove_option || 'Remove', 'handover-schema-button-remove-option', () => {
|
||||
field.options.splice(optIndex, 1);
|
||||
render();
|
||||
notifyChange();
|
||||
}, (t.remove_option || 'Remove option') + ' ' + (optIndex + 1)));
|
||||
optRow.appendChild(createIconButton(
|
||||
'bi-x-lg', 'danger outline small',
|
||||
() => { field.options.splice(optIndex, 1); render(); notifyChange(); },
|
||||
(t.remove_option || 'Remove option') + ' ' + (optIndex + 1),
|
||||
));
|
||||
|
||||
optionsSection.appendChild(optRow);
|
||||
});
|
||||
|
||||
const addOptButton = document.createElement('button');
|
||||
addOptButton.type = 'button';
|
||||
addOptButton.className = 'handover-schema-add-option-button';
|
||||
addOptButton.textContent = t.add_option || 'Add option';
|
||||
addOptButton.addEventListener('click', () => {
|
||||
field.options.push({ value: '', label: '' });
|
||||
render();
|
||||
notifyChange();
|
||||
});
|
||||
optionsSection.appendChild(addOptButton);
|
||||
|
||||
row.appendChild(optionsSection);
|
||||
}
|
||||
|
||||
@@ -270,16 +259,120 @@ function init(root) {
|
||||
return group;
|
||||
}
|
||||
|
||||
function createActionButton(text, className, onClick, ariaLabel) {
|
||||
function createIcon(iconClass) {
|
||||
const icon = document.createElement('i');
|
||||
icon.className = 'bi ' + iconClass;
|
||||
return icon;
|
||||
}
|
||||
|
||||
function createIconButton(iconClass, buttonClass, onClick, ariaLabel) {
|
||||
const button = document.createElement('button');
|
||||
button.type = 'button';
|
||||
button.className = className;
|
||||
button.textContent = text;
|
||||
if (ariaLabel) button.setAttribute('aria-label', ariaLabel);
|
||||
button.className = buttonClass;
|
||||
button.setAttribute('aria-label', ariaLabel);
|
||||
button.appendChild(createIcon(iconClass));
|
||||
button.addEventListener('click', onClick);
|
||||
return button;
|
||||
}
|
||||
|
||||
function renderPreview() {
|
||||
if (!previewContainer) return;
|
||||
clearElement(previewContainer);
|
||||
|
||||
if (fields.length === 0) {
|
||||
const empty = document.createElement('p');
|
||||
empty.className = 'handover-schema-preview-empty';
|
||||
empty.textContent = t.preview_empty || 'Add fields to see a live preview.';
|
||||
previewContainer.appendChild(empty);
|
||||
return;
|
||||
}
|
||||
|
||||
const formEl = document.createElement('div');
|
||||
formEl.className = 'handover-schema-preview-form';
|
||||
|
||||
fields.forEach((field, index) => {
|
||||
if (field.type === 'heading') {
|
||||
const h = document.createElement('h3');
|
||||
h.dataset.fieldIndex = index;
|
||||
h.textContent = field.label || '';
|
||||
formEl.appendChild(h);
|
||||
return;
|
||||
}
|
||||
|
||||
if (field.type === 'paragraph') {
|
||||
const p = document.createElement('p');
|
||||
p.dataset.fieldIndex = index;
|
||||
p.textContent = field.label || '';
|
||||
formEl.appendChild(p);
|
||||
return;
|
||||
}
|
||||
|
||||
const group = document.createElement('div');
|
||||
group.className = 'handover-schema-preview-field';
|
||||
group.dataset.fieldIndex = index;
|
||||
|
||||
if (field.type === 'checkbox') {
|
||||
const input = document.createElement('input');
|
||||
input.type = 'checkbox';
|
||||
input.disabled = true;
|
||||
group.appendChild(input);
|
||||
const label = document.createElement('label');
|
||||
label.textContent = field.label || '';
|
||||
if (field.required) {
|
||||
const star = document.createElement('span');
|
||||
star.className = 'handover-schema-preview-required';
|
||||
star.textContent = ' *';
|
||||
label.appendChild(star);
|
||||
}
|
||||
group.appendChild(label);
|
||||
} else {
|
||||
const label = document.createElement('label');
|
||||
label.textContent = field.label || '';
|
||||
if (field.required) {
|
||||
const star = document.createElement('span');
|
||||
star.className = 'handover-schema-preview-required';
|
||||
star.textContent = ' *';
|
||||
label.appendChild(star);
|
||||
}
|
||||
group.appendChild(label);
|
||||
|
||||
let input;
|
||||
if (field.type === 'textarea') {
|
||||
input = document.createElement('textarea');
|
||||
input.rows = 3;
|
||||
} else if (field.type === 'select') {
|
||||
input = document.createElement('select');
|
||||
const placeholder = document.createElement('option');
|
||||
placeholder.value = '';
|
||||
placeholder.textContent = '';
|
||||
input.appendChild(placeholder);
|
||||
(field.options || []).forEach((opt) => {
|
||||
const option = document.createElement('option');
|
||||
option.textContent = opt.label || '';
|
||||
input.appendChild(option);
|
||||
});
|
||||
} else {
|
||||
input = document.createElement('input');
|
||||
input.type = field.type === 'number' ? 'number' : field.type === 'date' ? 'date' : 'text';
|
||||
}
|
||||
input.disabled = true;
|
||||
group.appendChild(input);
|
||||
}
|
||||
|
||||
formEl.appendChild(group);
|
||||
});
|
||||
|
||||
previewContainer.appendChild(formEl);
|
||||
}
|
||||
|
||||
function highlightPreview(index, active) {
|
||||
if (!previewContainer) return;
|
||||
const el = previewContainer.querySelector('[data-field-index="' + index + '"]');
|
||||
if (el) {
|
||||
el.classList.toggle('handover-schema-preview-highlight', active);
|
||||
}
|
||||
}
|
||||
|
||||
function syncHiddenInput() {
|
||||
if (!hiddenInput) return;
|
||||
hiddenInput.value = fields.length > 0 ? JSON.stringify(fields) : '';
|
||||
@@ -287,6 +380,7 @@ function init(root) {
|
||||
|
||||
function notifyChange() {
|
||||
syncHiddenInput();
|
||||
renderPreview();
|
||||
if (hiddenInput) {
|
||||
hiddenInput.dispatchEvent(new Event('input', { bubbles: true }));
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user