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:
@@ -65,7 +65,6 @@ class SoftwareProductService
|
||||
$fieldNum = $index + 1;
|
||||
$type = (string) ($field['type'] ?? '');
|
||||
$label = trim((string) ($field['label'] ?? ''));
|
||||
$key = trim((string) ($field['key'] ?? ''));
|
||||
$isDisplayOnly = in_array($type, self::DISPLAY_ONLY_TYPES, true);
|
||||
|
||||
if (!in_array($type, self::ALLOWED_FIELD_TYPES, true)) {
|
||||
@@ -77,13 +76,10 @@ class SoftwareProductService
|
||||
$errors["field_{$fieldNum}_label"] = t('Field %d: label is required', $fieldNum);
|
||||
}
|
||||
|
||||
if (!$isDisplayOnly) {
|
||||
if ($key === '') {
|
||||
$errors["field_{$fieldNum}_key"] = t('Field %d: key is required', $fieldNum);
|
||||
} elseif (!preg_match('/^[a-z][a-z0-9_]*$/', $key)) {
|
||||
$errors["field_{$fieldNum}_key"] = t('Field %d: key must start with a letter and contain only lowercase letters, digits, and underscores', $fieldNum);
|
||||
} elseif (isset($seenKeys[$key])) {
|
||||
$errors["field_{$fieldNum}_key"] = t('Field %d: duplicate key "%s"', $fieldNum, $key);
|
||||
if (!$isDisplayOnly && $label !== '') {
|
||||
$key = self::slugify($label);
|
||||
if (isset($seenKeys[$key])) {
|
||||
$errors["field_{$fieldNum}_key"] = t('Field %d: generates duplicate key "%s" — use a more distinct label', $fieldNum, $key);
|
||||
} else {
|
||||
$seenKeys[$key] = true;
|
||||
}
|
||||
@@ -96,8 +92,8 @@ class SoftwareProductService
|
||||
} else {
|
||||
foreach ($options as $optIdx => $opt) {
|
||||
$optNum = $optIdx + 1;
|
||||
if (trim((string) ($opt['value'] ?? '')) === '') {
|
||||
$errors["field_{$fieldNum}_option_{$optNum}"] = t('Field %d, option %d: value is required', $fieldNum, $optNum);
|
||||
if (trim((string) ($opt['label'] ?? '')) === '') {
|
||||
$errors["field_{$fieldNum}_option_{$optNum}"] = t('Field %d, option %d: label is required', $fieldNum, $optNum);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -143,16 +139,17 @@ class SoftwareProductService
|
||||
$entry = ['type' => $type, 'label' => trim((string) ($field['label'] ?? ''))];
|
||||
|
||||
if (!$isDisplayOnly) {
|
||||
$entry['key'] = trim((string) ($field['key'] ?? ''));
|
||||
$entry['key'] = self::slugify($entry['label']);
|
||||
$entry['required'] = !empty($field['required']);
|
||||
}
|
||||
|
||||
if ($type === 'select' && is_array($field['options'] ?? null)) {
|
||||
$entry['options'] = [];
|
||||
foreach ($field['options'] as $opt) {
|
||||
$label = trim((string) ($opt['label'] ?? ''));
|
||||
$entry['options'][] = [
|
||||
'value' => trim((string) ($opt['value'] ?? '')),
|
||||
'label' => trim((string) ($opt['label'] ?? '')),
|
||||
'value' => self::slugify($label),
|
||||
'label' => $label,
|
||||
];
|
||||
}
|
||||
}
|
||||
@@ -163,6 +160,19 @@ class SoftwareProductService
|
||||
return $normalized;
|
||||
}
|
||||
|
||||
private static function slugify(string $text): string
|
||||
{
|
||||
$slug = mb_strtolower($text);
|
||||
$slug = preg_replace('/[äÄ]/', 'ae', $slug);
|
||||
$slug = preg_replace('/[öÖ]/', 'oe', $slug);
|
||||
$slug = preg_replace('/[üÜ]/', 'ue', $slug);
|
||||
$slug = preg_replace('/ß/', 'ss', $slug);
|
||||
$slug = preg_replace('/[^a-z0-9]+/', '_', $slug);
|
||||
$slug = trim($slug, '_');
|
||||
|
||||
return $slug === '' ? 'option' : $slug;
|
||||
}
|
||||
|
||||
/**
|
||||
* Update the user-managed name field for a software product.
|
||||
*
|
||||
|
||||
Reference in New Issue
Block a user