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,17 +1,29 @@
<template>
<section aria-label="File Upload Modal"
<section
aria-label="File Upload Modal"
class="relative h-full flex flex-col bg-white dark:bg-slate-900/70 shadow-xl rounded-md"
v-on:dragenter="dragEnterHandler" v-on:dragleave="dragLeaveHandler" v-on:dragover="dragOverHandler"
v-on:drop="dropHandler">
v-on:dragenter="dragEnterHandler"
v-on:dragleave="dragLeaveHandler"
v-on:dragover="dragOverHandler"
v-on:drop="dropHandler"
>
<!-- overlay -->
<div id="overlay" ref="overlay"
class="w-full h-full absolute top-0 left-0 pointer-events-none z-50 flex flex-col items-center justify-center rounded-md">
<div
id="overlay"
ref="overlay"
class="w-full h-full absolute top-0 left-0 pointer-events-none z-50 flex flex-col items-center justify-center rounded-md"
>
<i>
<svg class="fill-current w-12 h-12 mb-3 text-blue-700" xmlns="http://www.w3.org/2000/svg" width="24"
height="24" viewBox="0 0 24 24">
<svg
class="fill-current w-12 h-12 mb-3 text-blue-700"
xmlns="http://www.w3.org/2000/svg"
width="24"
height="24"
viewBox="0 0 24 24"
>
<path
d="M19.479 10.092c-.212-3.951-3.473-7.092-7.479-7.092-4.005 0-7.267 3.141-7.479 7.092-2.57.463-4.521 2.706-4.521 5.408 0 3.037 2.463 5.5 5.5 5.5h13c3.037 0 5.5-2.463 5.5-5.5 0-2.702-1.951-4.945-4.521-5.408zm-7.479-1.092l4 4h-3v4h-2v-4h-3l4-4z" />
d="M19.479 10.092c-.212-3.951-3.473-7.092-7.479-7.092-4.005 0-7.267 3.141-7.479 7.092-2.57.463-4.521 2.706-4.521 5.408 0 3.037 2.463 5.5 5.5 5.5h13c3.037 0 5.5-2.463 5.5-5.5 0-2.702-1.951-4.945-4.521-5.408zm-7.479-1.092l4 4h-3v4h-2v-4h-3l4-4z"
/>
</svg>
</i>
<p class="text-lg text-blue-700">Drop files to upload</p>
@ -19,8 +31,7 @@
<!-- Loading Spinner when processing big files -->
<div v-if="isLoading" class="absolute inset-0 z-60 flex items-center justify-center bg-gray-500 bg-opacity-50">
<svg class="animate-spin h-12 w-12 text-white" xmlns="http://www.w3.org/2000/svg" fill="none"
viewBox="0 0 24 24">
<svg class="animate-spin h-12 w-12 text-white" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24">
<circle class="opacity-25" cx="12" cy="12" r="10" stroke="currentColor" stroke-width="4"></circle>
<path class="opacity-75" fill="currentColor" d="M12 2a10 10 0 0110 10h-4a6 6 0 00-6-6V2z"></path>
</svg>
@ -29,21 +40,39 @@
<!-- scroll area -->
<div class="h-full p-8 w-full h-full flex flex-col">
<header class="flex items-center justify-center w-full">
<label for="dropzone-file"
class="flex flex-col items-center justify-center w-full h-64 border-2 border-gray-300 border-dashed rounded-lg cursor-pointer bg-gray-50 dark:bg-gray-700 hover:bg-gray-100 dark:border-gray-600 dark:hover:border-gray-500 dark:hover:bg-gray-600">
<label
for="dropzone-file"
class="flex flex-col items-center justify-center w-full h-64 border-2 border-gray-300 border-dashed rounded-lg cursor-pointer bg-gray-50 dark:bg-gray-700 hover:bg-gray-100 dark:border-gray-600 dark:hover:border-gray-500 dark:hover:bg-gray-600"
>
<div class="flex flex-col items-center justify-center pt-5 pb-6">
<svg aria-hidden="true" class="w-10 h-10 mb-3 text-gray-400" fill="none" stroke="currentColor"
viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
d="M7 16a4 4 0 01-.88-7.903A5 5 0 1115.9 6L16 6a5 5 0 011 9.9M15 13l-3-3m0 0l-3 3m3-3v12">
</path>
<svg
aria-hidden="true"
class="w-10 h-10 mb-3 text-gray-400"
fill="none"
stroke="currentColor"
viewBox="0 0 24 24"
xmlns="http://www.w3.org/2000/svg"
>
<path
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="2"
d="M7 16a4 4 0 01-.88-7.903A5 5 0 1115.9 6L16 6a5 5 0 011 9.9M15 13l-3-3m0 0l-3 3m3-3v12"
></path>
</svg>
<p class="mb-2 text-sm text-gray-500 dark:text-gray-400">
<span class="font-semibold">Click to upload</span> or drag and drop
</p>
</div>
<input id="dropzone-file" type="file" class="hidden" @click="showSpinner" @change="onChangeFile"
@cancel="cancelSpinner" multiple="true" />
<input
id="dropzone-file"
type="file"
class="hidden"
@click="showSpinner"
@change="onChangeFile"
@cancel="cancelSpinner"
multiple="true"
/>
</label>
</header>
@ -107,17 +136,16 @@
</section>
</article> -->
<!-- :class="errors && errors[`files.${index}`] ? 'bg-red-400' : 'bg-gray-100'" -->
<article tabindex="0"
class="bg-gray-100 group w-full h-full rounded-md cursor-pointer relative shadow-sm">
<section
class="flex flex-col rounded-md text-xs break-words w-full h-full z-20 absolute top-0 py-2 px-3">
<article tabindex="0" class="bg-gray-100 group w-full h-full rounded-md cursor-pointer relative shadow-sm">
<section class="flex flex-col rounded-md text-xs break-words w-full h-full z-20 absolute top-0 py-2 px-3">
<h1 class="flex-1 text-gray-700 group-hover:text-blue-800">{{ element.name }}</h1>
<div class="flex">
<p class="p-1 size text-xs text-gray-700">{{ getFileSize(element) }}</p>
<p class="p-1 size text-xs text-gray-700">sort: {{ element.sort_order }}</p>
<button
class="delete ml-auto focus:outline-none hover:bg-gray-300 p-1 rounded-md text-gray-800"
@click.prevent="removeFile(index)">
@click.prevent="removeFile(index)"
>
<DeleteIcon></DeleteIcon>
</button>
</div>
@ -131,11 +159,13 @@
<!--<ul id="deletetFiles"></ul> -->
<div>
<h1 v-if="deletetFiles.length > 0" class="pt-8 pb-3 font-semibold sm:text-lg text-gray-900">Files To
Delete</h1>
<h1 v-if="deletetFiles.length > 0" class="pt-8 pb-3 font-semibold sm:text-lg text-gray-900">Files To Delete</h1>
<ul id="deletetFiles" tag="ul" class="flex flex-1 flex-wrap -m-1">
<li v-for="(element, index) in deletetFiles" :key="index"
class="block p-1 w-1/2 sm:w-1/3 md:w-1/4 lg:w-1/6 xl:w-1/8 h-24">
<li
v-for="(element, index) in deletetFiles"
:key="index"
class="block p-1 w-1/2 sm:w-1/3 md:w-1/4 lg:w-1/6 xl:w-1/8 h-24"
>
<!-- <article
v-if="element.type.match('image.*')"
tabindex="0"
@ -160,17 +190,16 @@
</div>
</section>
</article> -->
<article tabindex="0"
class="bg-red-100 group w-full h-full rounded-md cursor-pointer relative shadow-sm">
<section
class="flex flex-col rounded-md text-xs break-words w-full h-full z-20 absolute top-0 py-2 px-3">
<article tabindex="0" class="bg-red-100 group w-full h-full rounded-md cursor-pointer relative shadow-sm">
<section class="flex flex-col rounded-md text-xs break-words w-full h-full z-20 absolute top-0 py-2 px-3">
<h1 class="flex-1 text-gray-700 group-hover:text-blue-800">{{ element.name }}</h1>
<div class="flex">
<!-- <p class="p-1 size text-xs text-gray-700">{{ getFileSize(element) }}</p> -->
<p class="p-1 size text-xs text-gray-700">{{ element.sort_order }}</p>
<button
class="delete ml-auto focus:outline-none hover:bg-gray-300 p-1 rounded-md text-gray-800"
@click.prevent="reactivateFile(index)">
@click.prevent="reactivateFile(index)"
>
<RefreshIcon></RefreshIcon>
</button>
</div>
@ -183,17 +212,19 @@
<div v-if="fileErrors" class="flex flex-col mt-6 animate-fade-in" v-for="fileError in fileErrors">
<div class="bg-yellow-500 border-l-4 border-orange-400 text-white p-4" role="alert">
<p class="font-bold">Be Warned</p>
<p>{{ fileError.join(', ') }}</p>
<p>{{ formatError(fileError) }}</p>
</div>
</div>
</div>
<!-- sticky footer -->
<footer class="flex justify-end px-8 pb-8 pt-4">
<button v-if="showClearButton" id="cancel"
<button
v-if="showClearButton"
id="cancel"
class="ml-3 rounded-sm px-3 py-1 hover:bg-gray-300 focus:shadow-outline focus:outline-none"
@click="clearAllFiles">
@click="clearAllFiles"
>
Clear
</button>
</footer>
@ -247,10 +278,8 @@ interface InteriaPage {
},
})
class FileUploadComponent extends Vue {
@Ref('overlay') overlay: HTMLDivElement;
public isLoading: boolean = false;
private counter: number = 0;
// @Prop() files: Array<TestFile>;
@ -261,7 +290,6 @@ class FileUploadComponent extends Vue {
})
files: Array<TethysFile | File>;
@Prop({
type: Array<File>,
default: [],
@ -335,6 +363,11 @@ class FileUploadComponent extends Vue {
++this.counter && this.overlay.classList.add('draggedover');
}
public formatError(error: string | string[] | undefined) {
if (!error) return '';
return Array.isArray(error) ? error.join(', ') : error;
}
public dragLeaveHandler() {
1 > --this.counter && this.overlay.classList.remove('draggedover');
}
@ -425,7 +458,6 @@ class FileUploadComponent extends Vue {
// this.isLoading = true;
// }
this._addFile(file);
}
}
// if (bigFileFound) {