Squashed commit of the following:
All checks were successful
Gitea Actions Demo / Explore-Gitea-Actions (push) Successful in 40s

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.
This commit is contained in:
Kaimbacher 2026-06-09 09:35:15 +02:00
commit 9368a0dd8d
38 changed files with 5588 additions and 6181 deletions

View file

@ -1,13 +1,22 @@
<script lang="ts" setup>
import { Head, usePage } from '@inertiajs/vue3';
import { mdiLicense, mdiCheckCircle, mdiCloseCircle, mdiAlertBoxOutline } from '@mdi/js';
import {
mdiLicense,
mdiCheckCircle,
mdiCloseCircle,
mdiAlertBoxOutline,
mdiFileDocumentOutline,
mdiCheckCircleOutline,
mdiPauseCircleOutline,
} from '@mdi/js';
import { computed, ComputedRef } from 'vue';
import LayoutAuthenticated from '@/Layouts/LayoutAuthenticated.vue';
import SectionMain from '@/Components/SectionMain.vue';
import SectionTitleLineWithButton from '@/Components/SectionTitleLineWithButton.vue';
import BaseButton from '@/Components/BaseButton.vue';
import CardBox from '@/Components/CardBox.vue';
import BaseButtons from '@/Components/BaseButtons.vue';
import BaseIcon from '@/Components/BaseIcon.vue';
import CardBox from '@/Components/CardBox.vue';
import NotificationBar from '@/Components/NotificationBar.vue';
import { stardust } from '@eidellev/adonis-stardust/client';
@ -32,6 +41,8 @@ const props = defineProps({
const flash: ComputedRef<any> = computed(() => usePage().props.flash);
const licenseCount = computed(() => props.licenses.length);
const activeCount = computed(() => props.licenses.filter((l) => l.active).length);
const inactiveCount = computed(() => licenseCount.value - activeCount.value);
const getLicenseColor = (index: number) => {
const colors = [
@ -51,18 +62,47 @@ const getLicenseColor = (index: number) => {
<Head title="Licenses" />
<SectionMain>
<SectionTitleLineWithButton :icon="mdiLicense" title="Licenses" main>
<div class="flex items-center gap-3">
<span class="text-sm text-gray-500 dark:text-gray-400 font-medium">
{{ licenseCount }} {{ licenseCount === 1 ? 'license' : 'licenses' }}
</span>
</div>
<span class="text-sm text-gray-500 dark:text-gray-400 font-medium">
{{ licenseCount }} {{ licenseCount === 1 ? 'license' : 'licenses' }}
</span>
</SectionTitleLineWithButton>
<NotificationBar v-if="flash.message" color="success" :icon="mdiAlertBoxOutline">
{{ flash.message }}
</NotificationBar>
<CardBox class="mb-6" has-table>
<!-- Summary stats -->
<div class="reveal grid grid-cols-1 sm:grid-cols-3 gap-4 mb-6">
<div class="flex items-center gap-4 rounded-xl border-l-4 border-blue-500 bg-white dark:bg-slate-900/40 shadow-sm p-4">
<div class="flex items-center justify-center w-11 h-11 rounded-lg bg-blue-100 dark:bg-blue-900/40 text-blue-600 dark:text-blue-300">
<BaseIcon :path="mdiFileDocumentOutline" size="22" />
</div>
<div>
<p class="text-2xl font-bold text-gray-900 dark:text-white leading-none">{{ licenseCount }}</p>
<p class="text-xs text-gray-500 dark:text-gray-400 mt-1">Total</p>
</div>
</div>
<div class="flex items-center gap-4 rounded-xl border-l-4 border-emerald-500 bg-white dark:bg-slate-900/40 shadow-sm p-4">
<div class="flex items-center justify-center w-11 h-11 rounded-lg bg-emerald-100 dark:bg-emerald-900/40 text-emerald-600 dark:text-emerald-300">
<BaseIcon :path="mdiCheckCircleOutline" size="22" />
</div>
<div>
<p class="text-2xl font-bold text-gray-900 dark:text-white leading-none">{{ activeCount }}</p>
<p class="text-xs text-gray-500 dark:text-gray-400 mt-1">Active</p>
</div>
</div>
<div class="flex items-center gap-4 rounded-xl border-l-4 border-gray-400 bg-white dark:bg-slate-900/40 shadow-sm p-4">
<div class="flex items-center justify-center w-11 h-11 rounded-lg bg-gray-100 dark:bg-slate-700 text-gray-600 dark:text-gray-300">
<BaseIcon :path="mdiPauseCircleOutline" size="22" />
</div>
<div>
<p class="text-2xl font-bold text-gray-900 dark:text-white leading-none">{{ inactiveCount }}</p>
<p class="text-xs text-gray-500 dark:text-gray-400 mt-1">Inactive</p>
</div>
</div>
</div>
<CardBox class="reveal reveal-1 mb-6" has-table>
<table>
<thead>
<tr>
@ -75,9 +115,12 @@ const getLicenseColor = (index: number) => {
<tbody>
<tr v-if="licenses.length === 0">
<td colspan="4" class="text-center py-12">
<td :colspan="can.edit ? 4 : 3" class="text-center py-14">
<div class="flex flex-col items-center justify-center text-gray-500 dark:text-gray-400">
<p class="text-lg font-medium mb-2">No licenses found</p>
<div class="flex items-center justify-center w-16 h-16 rounded-full bg-gray-100 dark:bg-slate-800 mb-4">
<BaseIcon :path="mdiLicense" size="32" class="text-gray-400" />
</div>
<p class="text-lg font-medium mb-1">No licenses found</p>
<p class="text-sm">Licenses will appear here once configured</p>
</div>
</td>
@ -157,3 +200,29 @@ const getLicenseColor = (index: number) => {
</SectionMain>
</LayoutAuthenticated>
</template>
<style scoped>
@keyframes fade-up {
from {
opacity: 0;
transform: translateY(12px);
}
to {
opacity: 1;
transform: translateY(0);
}
}
.reveal {
animation: fade-up 0.5s ease-out both;
}
.reveal-1 {
animation-delay: 0.1s;
}
@media (prefers-reduced-motion: reduce) {
.reveal {
animation: none;
}
}
</style>