From fa103082a8dc59e3fc23f1770562747b72a17061 Mon Sep 17 00:00:00 2001 From: haticebabaoglu Date: Mon, 26 Jan 2026 16:13:27 +0300 Subject: [PATCH 1/3] feat(admin): add EntityForm component with comprehensive field validation MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Implement EntityForm component with dynamic field rendering - Add support for multiple field types (text, email, number, select, checkbox, switch, etc.) - Implement field validation with customizable rules (required, min, max, pattern) - Add form sections for organized field grouping - Include loading and error states with user feedback - Enhance Button component with type prop (button, submit, reset) - Update Alert success icon from 'v' to '✓' for better visual clarity - Add comprehensive Storybook stories for EntityForm with multiple examples --- .../admin/forms/EntityForm.stories.svelte | 537 +++++++++ .../components/admin/forms/EntityForm.svelte | 1005 +++++++++++++++++ .../src/components/core/buttons/Button.svelte | 8 +- .../src/components/core/feedback/Alert.svelte | 2 +- 4 files changed, 1550 insertions(+), 2 deletions(-) create mode 100644 hexawebshare/src/components/admin/forms/EntityForm.stories.svelte diff --git a/hexawebshare/src/components/admin/forms/EntityForm.stories.svelte b/hexawebshare/src/components/admin/forms/EntityForm.stories.svelte new file mode 100644 index 00000000..e29a9a7d --- /dev/null +++ b/hexawebshare/src/components/admin/forms/EntityForm.stories.svelte @@ -0,0 +1,537 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/hexawebshare/src/components/admin/forms/EntityForm.svelte b/hexawebshare/src/components/admin/forms/EntityForm.svelte index 315da078..2679e7d8 100644 --- a/hexawebshare/src/components/admin/forms/EntityForm.svelte +++ b/hexawebshare/src/components/admin/forms/EntityForm.svelte @@ -2,3 +2,1008 @@ SPDX-FileCopyrightText: 2025 hexaTune LLC SPDX-License-Identifier: MIT --> + + + + + +
+ {#if title || description} +
+ {#if title} + + {title} + + {/if} + {#if description} + + {description} + + {/if} +
+ {/if} + + {#if error || internalError} +
+ +
+ {/if} + + {#if success || internalSuccess} +
+ +
+ {/if} + +
+
+ {#if loading} +
+ +
+ {/if} + + {#if Object.keys(fieldsBySection().grouped).length > 0 || fieldsBySection().noSection.length > 0} + {#each Object.entries(fieldsBySection().grouped) as [sectionName, sectionFields]} + +
+ {#each sectionFields as field} + {@const fieldState = getFieldState(field)} + {#if field.render} + {@const customFieldSnippet = field.render( + field, + fieldState.value, + formErrors, + handleFieldChange + )} + {@render customFieldSnippet()} + {:else if field.type === 'text' || field.type === 'email' || field.type === 'password' || field.type === 'tel' || field.type === 'url' || field.type === 'date' || field.type === 'time' || field.type === 'datetime-local'} + { + const target = e.target as HTMLInputElement; + handleFieldChange(field.key, target.value); + }} + /> + {:else if field.type === 'number'} + { + const target = e.target as HTMLInputElement; + handleFieldChange(field.key, target.value ? Number(target.value) : undefined); + }} + /> + {:else if field.type === 'textarea'} + + { + const target = e.target as HTMLInputElement; + handleFieldChange(field.key, target.value); + }} + /> + {:else if field.type === 'select'} + { + const target = e.target as HTMLInputElement; + handleFieldChange(field.key, target.value); + }} + /> + {:else if field.type === 'number'} + { + const target = e.target as HTMLInputElement; + handleFieldChange(field.key, target.value ? Number(target.value) : undefined); + }} + /> + {:else if field.type === 'textarea'} + + { + const target = e.target as HTMLInputElement; + handleFieldChange(field.key, target.value); + }} + /> + {:else if field.type === 'select'} +