tethys.backend/resources/js/Components/FormField.vue
Arno Kaimbacher 9368a0dd8d
All checks were successful
Gitea Actions Demo / Explore-Gitea-Actions (push) Successful in 40s
Squashed commit of the following:
commit 579f0878e5240dc17db69be1e0b0c0f5af7ef9fe
Author: Arno Kaimbacher <arno.kaimbacher@geosphere.at>
Date:   Tue Jun 9 09:25:44 2026 +0200

    feat: Refactor error handling in Dataset Edit form and improve validation messages

    - Updated error handling in the Dataset Edit form to use a centralized formatError function for displaying validation messages.
    - Enhanced user feedback by ensuring that error messages are displayed consistently across various fields.
    - Modified the validation rule for arrayContainsTypes to provide clearer error messages for missing main and translated titles/abstracts.
    - Introduced a new ValidationService to manage manual construction of validation errors.
    - Updated Vite configuration to streamline asset loading and improve performance.
    - Adjusted Inertia setup to utilize dynamic imports for page-specific assets.
    - Cleaned up unnecessary comments and code in various files for better readability.

commit 5efddc2a58c0e164fef585cc7344c06155dbc2c1
Author: Arno Kaimbacher <arno.kaimbacher@geosphere.at>
Date:   Mon Jan 12 17:02:47 2026 +0100

    feat: add dataset change detection and form submission composables

    - Implemented `useDatasetChangeDetection` for tracking unsaved changes in dataset forms, including comparisons for licenses, basic properties, files, coverage, and more.
    - Added `useDatasetFormSubmission` for handling dataset form submissions with validation, success/error handling, and auto-save functionality.
2026-06-09 09:35:15 +02:00

75 lines
No EOL
2.1 KiB
Vue

<script setup>
import { computed, useSlots } from 'vue';
const props = defineProps({
label: { type: String, default: null },
labelFor: { type: String, default: null },
help: { type: String, default: null },
// Handles Inertia.js string errors or standard array errors
errors: { type: [String, Array], default: null },
});
const slots = useSlots();
// Normalize errors to an array for consistent rendering
const errorList = computed(() => {
if (!props.errors) return [];
return Array.isArray(props.errors) ? props.errors : [props.errors];
});
const hasErrors = computed(() => errorList.value.length > 0);
const wrapperClass = computed(() => {
const base = [];
const children = slots.default?.().filter(node => node.type.toString() !== 'Symbol(v-cmt)') || [];
// Apply grid logic only if there are multiple child controls
if (children.length > 1) {
base.push('grid grid-cols-1 gap-3');
if (children.length === 2) base.push('md:grid-cols-2');
}
return base;
});
</script>
<template>
<div class="mb-6 last:mb-0">
<label
v-if="label"
:for="labelFor"
class="block font-bold text-xs uppercase tracking-wide mb-2 transition-colors duration-200"
:class="hasErrors ? 'text-red-600 dark:text-red-400' : 'text-gray-700 dark:text-slate-300'"
>
{{ label }}
</label>
<div :class="wrapperClass">
<slot />
</div>
<div class="mt-1 min-h-[1.25rem]">
<transition-group
enter-active-class="transition duration-150 ease-out"
enter-from-class="transform -translate-y-1 opacity-0"
enter-to-class="transform translate-y-0 opacity-100"
>
<p
v-for="(error, index) in errorList"
:key="`err-${index}`"
class="text-xs text-red-600 dark:text-red-400 italic font-medium"
>
{{ error }}
</p>
<p
v-if="!hasErrors && help"
key="help-text"
class="text-xs text-gray-500 dark:text-slate-400"
>
{{ help }}
</p>
</transition-group>
</div>
</div>
</template>