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.
163 lines
5 KiB
Vue
163 lines
5 KiB
Vue
<script lang="ts" setup>
|
|
import { computed } from 'vue';
|
|
import { Link, usePage } from '@inertiajs/vue3';
|
|
import { StyleService } from '@/Stores/style.service';
|
|
import { mdiMinus, mdiPlus } from '@mdi/js';
|
|
import { getButtonColor } from '@/colors';
|
|
import BaseIcon from '@/Components/BaseIcon.vue';
|
|
import { stardust } from '@eidellev/adonis-stardust/client';
|
|
import type { User } from '@/Dataset';
|
|
import { MenuItem } from '@headlessui/vue';
|
|
|
|
interface MenuItem {
|
|
href?: string;
|
|
route?: string;
|
|
icon?: string;
|
|
label: string;
|
|
target?: string;
|
|
color?: string;
|
|
children?: MenuItem[];
|
|
isOpen?: boolean;
|
|
roles?: string[];
|
|
}
|
|
|
|
const props = defineProps<{
|
|
item: MenuItem;
|
|
parentItem?: MenuItem;
|
|
// isDropdownList?: boolean;
|
|
}>();
|
|
const emit = defineEmits<{
|
|
(e: 'menu-click', event: Event, item: MenuItem): void;
|
|
}>();
|
|
|
|
// Retrieve authenticated user from page props
|
|
const user = computed<User>(() => usePage().props.authUser as User);
|
|
|
|
// Check if the menu item has children
|
|
const hasChildren = computed(() => {
|
|
return Array.isArray(props.item?.children) && props.item.children.length > 0;
|
|
});
|
|
|
|
const itemRoute = computed(() => (props.item && props.item.route ? stardust.route(props.item.route) : ''));
|
|
// const isCurrentRoute = computed(() => (props.item && props.item.route ? stardust.isCurrent(props.item.route): false));
|
|
|
|
// Determine which element to render based on 'href' or 'route'
|
|
const isComponent = computed(() => {
|
|
if (props.item.href) {
|
|
return 'a';
|
|
}
|
|
if (props.item.route) {
|
|
return Link;
|
|
}
|
|
return 'div';
|
|
});
|
|
|
|
// Check if any child route is active
|
|
const isChildActive = computed(() => {
|
|
if (props.item.children && props.item.children.length > 0) {
|
|
return props.item.children.some(child => child.route && stardust.isCurrent(child.route));
|
|
}
|
|
return false;
|
|
});
|
|
|
|
// Automatically use prop item.isOpen if set from the parent,
|
|
// or if one of its children is active then force open state.
|
|
const isOpen = computed(() => {
|
|
return props.item.isOpen || isChildActive.value;
|
|
});
|
|
|
|
|
|
const styleService = StyleService();
|
|
|
|
const hasColor = computed(() => props.item && props.item.color);
|
|
|
|
|
|
|
|
// const children = computed(() => {
|
|
// return props.item.children || [];
|
|
// });
|
|
|
|
const componentClass = computed(() => [
|
|
hasChildren ? 'py-3 px-6 text-sm font-semibold' : 'py-3 px-6',
|
|
hasColor.value ? getButtonColor(props.item.color, false, true) : styleService.asideMenuItemStyle,
|
|
]);
|
|
|
|
|
|
const menuClick = (event: Event) => {
|
|
emit('menu-click', event, props.item);
|
|
if (hasChildren.value) {
|
|
// Toggle open state if the menu has children
|
|
props.item.isOpen = !props.item.isOpen;
|
|
}
|
|
};
|
|
|
|
const activeStyle = computed(() => {
|
|
if (props.item.route && stardust.isCurrent(props.item.route)) {
|
|
// console.log(props.item.route);
|
|
return 'text-sky-600 font-bold';
|
|
} else {
|
|
return null;
|
|
}
|
|
});
|
|
|
|
const hasRoles = computed(() => {
|
|
if (props.item.roles) {
|
|
// Normalize user roles to strings in case roles are objects
|
|
const userRoles = (user.value.roles || []).map(r =>
|
|
typeof r === 'string' ? r : (r as any).name ?? String(r)
|
|
);
|
|
return userRoles.some(role => props.item.roles?.includes(role));
|
|
// return test;
|
|
}
|
|
return true
|
|
});
|
|
|
|
</script>
|
|
|
|
<!-- :target="props.item.target ?? null" -->
|
|
<template>
|
|
<li v-if="hasRoles">
|
|
<component :is="isComponent" :href="props.item.href ? props.item.href : itemRoute"
|
|
class="flex cursor-pointer dark:text-slate-300 dark:hover:text-white menu-item-wrapper"
|
|
:class="componentClass" @click="menuClick" :target="props.item.target || null">
|
|
<BaseIcon v-if="props.item.icon" :path="props.item.icon" class="flex-none menu-item-icon"
|
|
:class="activeStyle" w="w-16" :size="18" />
|
|
<div class="menu-item-label">
|
|
<span class="grow text-ellipsis line-clamp-1" :class="[activeStyle]">
|
|
{{ props.item.label }}
|
|
</span>
|
|
</div>
|
|
<!-- Display plus or minus icon if there are child items -->
|
|
<BaseIcon v-if="hasChildren" :path="isOpen ? mdiMinus : mdiPlus" class="flex-none"
|
|
:class="[activeStyle]" w="w-12" />
|
|
</component>
|
|
<!-- Render dropdown -->
|
|
<div class="menu-item-dropdown"
|
|
:class="[styleService.asideMenuDropdownStyle, isOpen ? 'block dark:bg-slate-800/50' : 'hidden']"
|
|
v-if="props.item.children && props.item.children.length > 0">
|
|
<ul>
|
|
|
|
<AsideMenuItem v-for="(childItem, index) in (props.item.children as any[])" :key="index" :item="childItem"
|
|
@menu-click="$emit('menu-click', $event, childItem)" />
|
|
</ul>
|
|
</div>
|
|
</li>
|
|
</template>
|
|
|
|
<style>
|
|
.menu-item-wrapper {
|
|
display: flex;
|
|
align-items: center;
|
|
cursor: pointer;
|
|
}
|
|
|
|
.menu-item-icon {
|
|
font-size: 2.5rem;
|
|
/* margin-right: 10px; */
|
|
}
|
|
|
|
.menu-item-dropdown {
|
|
/* margin-left: 10px; */
|
|
padding-left: 0.75rem;
|
|
}
|
|
</style>
|