Squashed commit of the following:
All checks were successful
Gitea Actions Demo / Explore-Gitea-Actions (push) Successful in 40s
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:
parent
0680879e2f
commit
9368a0dd8d
38 changed files with 5588 additions and 6181 deletions
|
|
@ -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>
|
||||
Loading…
Add table
Add a link
Reference in a new issue