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:
2026-04-15 18:08:12 +02:00
parent 44153f513f
commit 2ec632ac8b
7 changed files with 494 additions and 221 deletions

View File

@@ -128,8 +128,8 @@ class SoftwareProductServiceTest extends TestCase
->willReturn(true);
$result = $service->saveHandoverSchema('INFO', [
['type' => 'date', 'key' => 'handover_date', 'label' => 'Handover date', 'required' => true],
['type' => 'textarea', 'key' => 'notes', 'label' => 'Notes', 'required' => false],
['type' => 'date', 'label' => 'Handover date', 'required' => true],
['type' => 'textarea', 'label' => 'Notes', 'required' => false],
]);
$this->assertTrue($result['ok']);
@@ -149,37 +149,25 @@ class SoftwareProductServiceTest extends TestCase
$this->assertTrue($result['ok']);
}
public function testSaveHandoverSchemaRejectsDuplicateKeys(): void
public function testSaveHandoverSchemaRejectsDuplicateGeneratedKeys(): void
{
[$service] = $this->createServiceWithProduct();
$result = $service->saveHandoverSchema('INFO', [
['type' => 'text', 'key' => 'name', 'label' => 'Name 1'],
['type' => 'text', 'key' => 'name', 'label' => 'Name 2'],
['type' => 'text', 'label' => 'Name'],
['type' => 'text', 'label' => 'Name'],
]);
$this->assertFalse($result['ok']);
$this->assertArrayHasKey('field_2_key', $result['errors']);
}
public function testSaveHandoverSchemaRejectsInvalidKeyFormat(): void
{
[$service] = $this->createServiceWithProduct();
$result = $service->saveHandoverSchema('INFO', [
['type' => 'text', 'key' => '1invalid', 'label' => 'Bad key'],
]);
$this->assertFalse($result['ok']);
$this->assertArrayHasKey('field_1_key', $result['errors']);
}
public function testSaveHandoverSchemaRejectsUnknownType(): void
{
[$service] = $this->createServiceWithProduct();
$result = $service->saveHandoverSchema('INFO', [
['type' => 'unknown_type', 'key' => 'field', 'label' => 'Field'],
['type' => 'unknown_type', 'label' => 'Field'],
]);
$this->assertFalse($result['ok']);
@@ -192,7 +180,7 @@ class SoftwareProductServiceTest extends TestCase
$fields = [];
for ($i = 0; $i < 51; $i++) {
$fields[] = ['type' => 'text', 'key' => 'field_' . $i, 'label' => 'Field ' . $i];
$fields[] = ['type' => 'text', 'label' => 'Field ' . $i];
}
$result = $service->saveHandoverSchema('INFO', $fields);
@@ -206,13 +194,54 @@ class SoftwareProductServiceTest extends TestCase
[$service] = $this->createServiceWithProduct();
$result = $service->saveHandoverSchema('INFO', [
['type' => 'select', 'key' => 'choice', 'label' => 'Choice', 'options' => []],
['type' => 'select', 'label' => 'Choice', 'options' => []],
]);
$this->assertFalse($result['ok']);
$this->assertArrayHasKey('field_1_options', $result['errors']);
}
public function testSaveHandoverSchemaRejectsSelectOptionWithEmptyLabel(): void
{
[$service] = $this->createServiceWithProduct();
$result = $service->saveHandoverSchema('INFO', [
['type' => 'select', 'label' => 'Choice', 'options' => [
['label' => ''],
]],
]);
$this->assertFalse($result['ok']);
$this->assertArrayHasKey('field_1_option_1', $result['errors']);
}
public function testSaveHandoverSchemaAutoGeneratesOptionValues(): void
{
[$service, $repository] = $this->createServiceWithProduct();
$repository->expects($this->once())
->method('updateHandoverSchema')
->with('INFO', $this->callback(function ($json) {
$data = json_decode($json, true);
$options = $data['fields'][0]['options'] ?? [];
return count($options) === 2
&& $options[0]['value'] === 'niedrig'
&& $options[0]['label'] === 'Niedrig'
&& $options[1]['value'] === 'sehr_hoch'
&& $options[1]['label'] === 'Sehr hoch';
}))
->willReturn(true);
$result = $service->saveHandoverSchema('INFO', [
['type' => 'select', 'label' => 'Risk', 'options' => [
['label' => 'Niedrig'],
['label' => 'Sehr hoch'],
]],
]);
$this->assertTrue($result['ok']);
}
public function testSaveHandoverSchemaAllowsHeadingWithoutKey(): void
{
[$service, $repository] = $this->createServiceWithProduct();
@@ -244,7 +273,7 @@ class SoftwareProductServiceTest extends TestCase
->willReturn(true);
$result = $service->saveHandoverSchema('INFO', [
['type' => 'text', 'key' => 'name', 'label' => 'Name'],
['type' => 'text', 'label' => 'Name'],
]);
$this->assertTrue($result['ok']);
@@ -257,7 +286,7 @@ class SoftwareProductServiceTest extends TestCase
$service = $this->createService($repository);
$result = $service->saveHandoverSchema('NONEXISTENT', [
['type' => 'text', 'key' => 'name', 'label' => 'Name'],
['type' => 'text', 'label' => 'Name'],
]);
$this->assertFalse($result['ok']);