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>
|
||||
|
|
@ -1,6 +1,13 @@
|
|||
<script lang="ts" setup>
|
||||
import { Head, useForm } from '@inertiajs/vue3';
|
||||
import { mdiFolderPlus, mdiArrowLeftBoldOutline, mdiFormTextarea, mdiContentSave } from '@mdi/js';
|
||||
import {
|
||||
mdiFolderPlus,
|
||||
mdiArrowLeftBoldOutline,
|
||||
mdiFormTextarea,
|
||||
mdiContentSave,
|
||||
mdiTagOutline,
|
||||
mdiText,
|
||||
} from '@mdi/js';
|
||||
import LayoutAuthenticated from '@/Layouts/LayoutAuthenticated.vue';
|
||||
import SectionMain from '@/Components/SectionMain.vue';
|
||||
import SectionTitleLineWithButton from '@/Components/SectionTitleLineWithButton.vue';
|
||||
|
|
@ -37,46 +44,48 @@ const submit = async () => {
|
|||
/>
|
||||
</SectionTitleLineWithButton>
|
||||
|
||||
<CardBox form @submit.prevent="submit()" class="shadow-lg">
|
||||
<div class="grid grid-cols-1 gap-6">
|
||||
<FormField label="Label" help="Lowercase letters, numbers, and hyphens only" :class="{ 'text-red-400': form.errors.label }">
|
||||
<CardBox form @submit.prevent="submit()" class="relative overflow-hidden shadow-lg">
|
||||
<!-- Subtle accent bar -->
|
||||
<div class="absolute inset-x-0 top-0 h-1 bg-gradient-to-r from-blue-500 via-indigo-500 to-blue-500"></div>
|
||||
|
||||
<div class="grid grid-cols-1 gap-7 pt-2">
|
||||
<!-- Label -->
|
||||
<FormField
|
||||
label="Label"
|
||||
help="Lowercase letters, numbers, and hyphens only"
|
||||
:errors="form.errors.label"
|
||||
>
|
||||
<FormControl
|
||||
v-model="form.label"
|
||||
v-model="form.label"
|
||||
:icon="mdiTagOutline"
|
||||
type="text"
|
||||
placeholder="e.g., my-awesome-project"
|
||||
placeholder="e.g., my-awesome-project"
|
||||
required
|
||||
:error="form.errors.label"
|
||||
class="transition-all focus:ring-2 focus:ring-blue-500"
|
||||
>
|
||||
<div class="text-red-400 text-sm mt-1" v-if="form.errors.label">
|
||||
{{ form.errors.label }}
|
||||
</div>
|
||||
</FormControl>
|
||||
class="font-mono transition-all duration-200 focus-within:ring-2 focus-within:ring-blue-500/40"
|
||||
/>
|
||||
</FormField>
|
||||
|
||||
<!-- Name -->
|
||||
<FormField
|
||||
label="Name"
|
||||
help="Required. Project title shown to users"
|
||||
:class="{ 'text-red-400': form.errors.name }"
|
||||
:errors="form.errors.name"
|
||||
>
|
||||
<FormControl
|
||||
v-model="form.name"
|
||||
:icon="mdiText"
|
||||
type="text"
|
||||
placeholder="Enter a descriptive titel..."
|
||||
placeholder="Enter a descriptive title..."
|
||||
required
|
||||
:error="form.errors.name"
|
||||
class="font-mono transition-all focus:ring-2 focus:ring-blue-500"
|
||||
>
|
||||
<div class="text-red-400 text-sm mt-1" v-if="form.errors.name">
|
||||
{{ form.errors.name }}
|
||||
</div>
|
||||
</FormControl>
|
||||
class="transition-all duration-200 focus-within:ring-2 focus-within:ring-blue-500/40"
|
||||
/>
|
||||
</FormField>
|
||||
|
||||
<!-- Description -->
|
||||
<FormField
|
||||
label="Description"
|
||||
help="Optional. Detailed description of the project"
|
||||
:class="{ 'text-red-400': form.errors.description }"
|
||||
:errors="form.errors.description"
|
||||
>
|
||||
<FormControl
|
||||
v-model="form.description"
|
||||
|
|
@ -84,19 +93,19 @@ const submit = async () => {
|
|||
name="description"
|
||||
type="textarea"
|
||||
placeholder="Describe what this project is about..."
|
||||
:error="form.errors.description"
|
||||
class="transition-all focus:ring-2 focus:ring-blue-500"
|
||||
>
|
||||
<div class="text-red-400 text-sm mt-1" v-if="form.errors.description">
|
||||
{{ form.errors.description }}
|
||||
</div>
|
||||
</FormControl>
|
||||
class="transition-all duration-200 focus-within:ring-2 focus-within:ring-blue-500/40"
|
||||
/>
|
||||
</FormField>
|
||||
</div>
|
||||
|
||||
<template #footer>
|
||||
<BaseButtons class="justify-between">
|
||||
<BaseButton :route-name="stardust.route('settings.project.index')" label="Cancel" color="white" outline />
|
||||
<BaseButton
|
||||
:route-name="stardust.route('settings.project.index')"
|
||||
label="Cancel"
|
||||
color="white"
|
||||
outline
|
||||
/>
|
||||
<BaseButton
|
||||
type="submit"
|
||||
color="info"
|
||||
|
|
@ -104,7 +113,7 @@ const submit = async () => {
|
|||
label="Create Project"
|
||||
:class="{ 'opacity-25': form.processing }"
|
||||
:disabled="form.processing"
|
||||
class="transition-all hover:shadow-lg"
|
||||
class="transition-all hover:shadow-lg hover:-translate-y-0.5"
|
||||
/>
|
||||
</BaseButtons>
|
||||
</template>
|
||||
|
|
@ -116,15 +125,17 @@ const submit = async () => {
|
|||
>
|
||||
<div class="flex items-start gap-4">
|
||||
<div class="flex-shrink-0">
|
||||
<div class="w-10 h-10 rounded-full bg-blue-500 dark:bg-blue-600 flex items-center justify-center">
|
||||
<div
|
||||
class="w-10 h-10 rounded-full bg-blue-500 dark:bg-blue-600 flex items-center justify-center shadow-md shadow-blue-500/30"
|
||||
>
|
||||
<span class="text-white text-lg">💡</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="flex-1">
|
||||
<h3 class="text-sm font-semibold text-gray-900 dark:text-white mb-2">Quick Tips</h3>
|
||||
<ul class="text-sm text-gray-700 dark:text-gray-300 space-y-1">
|
||||
<li>• <strong>Label</strong> is a technical identifier (use lowercase and hyphens) </li>
|
||||
<li>• <strong>Name</strong> is what users will see in the interface - short title</li>
|
||||
<li>• <strong>Label</strong> is a technical identifier (use lowercase and hyphens)</li>
|
||||
<li>• <strong>Name</strong> is what users will see in the interface — short title</li>
|
||||
<li>• <strong>Description</strong> helps team members understand the project's purpose</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
|
@ -132,4 +143,4 @@ const submit = async () => {
|
|||
</CardBox>
|
||||
</SectionMain>
|
||||
</LayoutAuthenticated>
|
||||
</template>
|
||||
</template>
|
||||
|
|
@ -1,6 +1,13 @@
|
|||
<script lang="ts" setup>
|
||||
import { Head, useForm } from '@inertiajs/vue3';
|
||||
import { mdiFolderEdit, mdiArrowLeftBoldOutline, mdiFormTextarea, mdiContentSave } from '@mdi/js';
|
||||
import {
|
||||
mdiFolderEdit,
|
||||
mdiArrowLeftBoldOutline,
|
||||
mdiFormTextarea,
|
||||
mdiContentSave,
|
||||
mdiTagOutline,
|
||||
mdiText,
|
||||
} from '@mdi/js';
|
||||
import LayoutAuthenticated from '@/Layouts/LayoutAuthenticated.vue';
|
||||
import SectionMain from '@/Components/SectionMain.vue';
|
||||
import SectionTitleLineWithButton from '@/Components/SectionTitleLineWithButton.vue';
|
||||
|
|
@ -43,45 +50,40 @@ const submit = async () => {
|
|||
small
|
||||
/>
|
||||
</SectionTitleLineWithButton>
|
||||
|
||||
<CardBox form @submit.prevent="submit()" class="shadow-lg">
|
||||
<div class="grid grid-cols-1 gap-6">
|
||||
<FormField
|
||||
label="Label"
|
||||
help="Lowercase letters, numbers, and hyphens only"
|
||||
>
|
||||
<FormControl
|
||||
v-model="form.label"
|
||||
type="text"
|
||||
help="Lowercase letters, numbers, and hyphens only"
|
||||
:is-read-only=true
|
||||
class="bg-gray-100 dark:bg-slate-800 cursor-not-allowed opacity-75"
|
||||
|
||||
<CardBox form @submit.prevent="submit()" class="relative overflow-hidden shadow-lg">
|
||||
<!-- Subtle accent bar -->
|
||||
<div class="absolute inset-x-0 top-0 h-1 bg-gradient-to-r from-blue-500 via-indigo-500 to-blue-500"></div>
|
||||
|
||||
<div class="grid grid-cols-1 gap-7 pt-2">
|
||||
<!-- Label (read-only) -->
|
||||
<FormField label="Label" help="Lowercase letters, numbers, and hyphens only">
|
||||
<FormControl
|
||||
v-model="form.label"
|
||||
:icon="mdiTagOutline"
|
||||
type="text"
|
||||
:is-read-only="true"
|
||||
class="font-mono bg-gray-100 dark:bg-slate-800 cursor-not-allowed opacity-75"
|
||||
/>
|
||||
</FormField>
|
||||
|
||||
<FormField
|
||||
label="Name"
|
||||
help="Required. Project title shown to users"
|
||||
:class="{ 'text-red-400': form.errors.name }"
|
||||
>
|
||||
<FormControl
|
||||
v-model="form.name"
|
||||
type="text"
|
||||
placeholder="Enter Name"
|
||||
required
|
||||
:error="form.errors.name"
|
||||
class="font-mono transition-all focus:ring-2 focus:ring-blue-500"
|
||||
>
|
||||
<div class="text-red-400 text-sm mt-1" v-if="form.errors.name">
|
||||
{{ form.errors.name }}
|
||||
</div>
|
||||
</FormControl>
|
||||
<!-- Name -->
|
||||
<FormField label="Name" help="Required. Project title shown to users" :errors="form.errors.name">
|
||||
<FormControl
|
||||
v-model="form.name"
|
||||
:icon="mdiText"
|
||||
type="text"
|
||||
placeholder="Enter Name"
|
||||
required
|
||||
class="transition-all duration-200 focus-within:ring-2 focus-within:ring-blue-500/40"
|
||||
/>
|
||||
</FormField>
|
||||
|
||||
<!-- Description -->
|
||||
<FormField
|
||||
label="Description"
|
||||
help="Optional. Detailed description of the project"
|
||||
:class="{ 'text-red-400': form.errors.description }"
|
||||
:errors="form.errors.description"
|
||||
>
|
||||
<FormControl
|
||||
v-model="form.description"
|
||||
|
|
@ -89,13 +91,8 @@ const submit = async () => {
|
|||
name="description"
|
||||
type="textarea"
|
||||
placeholder="Enter project description..."
|
||||
:error="form.errors.description"
|
||||
class="transition-all focus:ring-2 focus:ring-blue-500"
|
||||
>
|
||||
<div class="text-red-400 text-sm mt-1" v-if="form.errors.description">
|
||||
{{ form.errors.description }}
|
||||
</div>
|
||||
</FormControl>
|
||||
class="transition-all duration-200 focus-within:ring-2 focus-within:ring-blue-500/40"
|
||||
/>
|
||||
</FormField>
|
||||
</div>
|
||||
|
||||
|
|
@ -114,17 +111,19 @@ const submit = async () => {
|
|||
label="Save Changes"
|
||||
:class="{ 'opacity-25': form.processing }"
|
||||
:disabled="form.processing"
|
||||
class="transition-all hover:shadow-lg"
|
||||
class="transition-all hover:shadow-lg hover:-translate-y-0.5"
|
||||
/>
|
||||
</BaseButtons>
|
||||
</template>
|
||||
</CardBox>
|
||||
|
||||
<!-- Project Info Card -->
|
||||
<CardBox class="mt-6 bg-gradient-to-br from-gray-50 to-gray-100 dark:from-slate-800 dark:to-slate-900">
|
||||
<CardBox class="mt-6 bg-gradient-to-br from-gray-50 to-gray-100 dark:from-slate-800 dark:to-slate-900 border-l-4 border-blue-500">
|
||||
<div class="flex items-start gap-4">
|
||||
<div class="flex-shrink-0">
|
||||
<div class="w-12 h-12 rounded-lg bg-blue-500 dark:bg-blue-600 flex items-center justify-center">
|
||||
<div
|
||||
class="w-12 h-12 rounded-lg bg-blue-500 dark:bg-blue-600 flex items-center justify-center shadow-md shadow-blue-500/30"
|
||||
>
|
||||
<span class="text-white text-xl font-bold">
|
||||
{{ project.label.charAt(0).toUpperCase() }}
|
||||
</span>
|
||||
|
|
@ -139,11 +138,11 @@ const submit = async () => {
|
|||
</p>
|
||||
<div class="flex items-center gap-4 text-xs text-gray-500 dark:text-gray-500">
|
||||
<span>
|
||||
<span class="font-medium">Created:</span>
|
||||
<span class="font-medium">Created:</span>
|
||||
{{ new Date(project.created_at).toLocaleDateString() }}
|
||||
</span>
|
||||
<span>
|
||||
<span class="font-medium">Updated:</span>
|
||||
<span class="font-medium">Updated:</span>
|
||||
{{ new Date(project.updated_at).toLocaleDateString() }}
|
||||
</span>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -27,9 +27,20 @@ const form = useForm({
|
|||
permissions: [],
|
||||
});
|
||||
|
||||
const submit = async () => {
|
||||
await form.post(stardust.route('settings.role.store'));
|
||||
const submit = () => {
|
||||
form.post(stardust.route('settings.role.store'), {
|
||||
preserveScroll: true,
|
||||
onSuccess: () => form.reset(),
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Sicherer Helper für die Fehleranzeige
|
||||
*/
|
||||
// const formatError = (error: string | string[] | undefined) => {
|
||||
// if (!error) return '';
|
||||
// return Array.isArray(error) ? error.join(', ') : error;
|
||||
// };
|
||||
</script>
|
||||
|
||||
<template>
|
||||
|
|
@ -46,58 +57,48 @@ const submit = async () => {
|
|||
small
|
||||
/>
|
||||
</SectionTitleLineWithButton>
|
||||
<!-- <CardBox form @submit.prevent="form.post(stardust.route('role.store'))"> -->
|
||||
<CardBox form @submit.prevent="submit()">
|
||||
<FormField label="Name" help="Required. Role name" :class="{ 'text-red-400': form.errors.name }">
|
||||
<FormControl v-model="form.name" type="text" placeholder="Enter Name" required :error="form.errors.name">
|
||||
<div class="text-red-400 text-sm" v-if="form.errors.name">
|
||||
{{ form.errors.name }}
|
||||
</div>
|
||||
|
||||
<CardBox form @submit.prevent="submit">
|
||||
<FormField label="Name" help="Required. Technical role name" :errors="form.errors.name">
|
||||
<FormControl v-model="form.name" type="text" placeholder="e.g. manager" :error="form.errors.name"> </FormControl>
|
||||
</FormField>
|
||||
|
||||
<FormField label="Display Name" help="Optional. Readable name" :errors="form.errors.display_name">
|
||||
<FormControl v-model="form.display_name" placeholder="e.g. Project Manager" :error="form.errors.display_name">
|
||||
</FormControl>
|
||||
</FormField>
|
||||
|
||||
<FormField label="Display Name" help="Optional. Display name" :class="{ 'text-red-400': form.errors.display_name }">
|
||||
<FormControl v-model="form.display_name" name="display_name" :error="form.errors.display_name">
|
||||
<div class="text-red-400 text-sm" v-if="form.errors.display_name">
|
||||
{{ form.errors.display_name }}
|
||||
</div>
|
||||
</FormControl>
|
||||
</FormField>
|
||||
|
||||
<FormField
|
||||
label="Description"
|
||||
help="Optional. Description of new role"
|
||||
:class="{ 'text-red-400': form.errors.description }"
|
||||
>
|
||||
<FormField label="Description" help="Optional. What does this role do?" :errors="form.errors.description">
|
||||
<FormControl
|
||||
v-model="form.description"
|
||||
v-bind:icon="mdiFormTextarea"
|
||||
name="display_name"
|
||||
:type="'textarea'"
|
||||
:icon="mdiFormTextarea"
|
||||
type="textarea"
|
||||
placeholder="Role description..."
|
||||
:error="form.errors.description"
|
||||
>
|
||||
<div class="text-red-400 text-sm" v-if="form.errors.description">
|
||||
{{ form.errors.description }}
|
||||
</div>
|
||||
</FormControl>
|
||||
</FormField>
|
||||
|
||||
<BaseDivider />
|
||||
|
||||
<FormField label="Permissions" wrap-body>
|
||||
<FormField
|
||||
label="Permissions"
|
||||
wrap-body
|
||||
:class="{ 'text-red-400': form.errors.permissions }"
|
||||
:errors="form.errors.permissions"
|
||||
>
|
||||
<FormCheckRadioGroup v-model="form.permissions" name="permissions" is-column :options="props.permissions" />
|
||||
<!-- <div class="text-red-400 text-sm mt-1" v-if="form.errors.permissions">
|
||||
{{ formatError(form.errors.permissions) }}
|
||||
</div> -->
|
||||
</FormField>
|
||||
<div class="text-red-400 text-sm" v-if="form.errors.permissions && Array.isArray(form.errors.permissions)">
|
||||
<!-- {{ errors.password_confirmation }} -->
|
||||
{{ form.errors.permissions.join(', ') }}
|
||||
</div>
|
||||
|
||||
<template #footer>
|
||||
<BaseButtons>
|
||||
<BaseButton
|
||||
type="submit"
|
||||
color="info"
|
||||
label="Submit"
|
||||
label="Create Role"
|
||||
:class="{ 'opacity-25': form.processing }"
|
||||
:disabled="form.processing"
|
||||
/>
|
||||
|
|
|
|||
|
|
@ -14,31 +14,45 @@ import BaseButtons from '@/Components/BaseButtons.vue';
|
|||
import { stardust } from '@eidellev/adonis-stardust/client';
|
||||
|
||||
const props = defineProps({
|
||||
role: {
|
||||
type: Object,
|
||||
default: () => ({}),
|
||||
},
|
||||
permissions: {
|
||||
type: Object,
|
||||
default: () => ({}),
|
||||
},
|
||||
roleHasPermissions: {
|
||||
type: Object,
|
||||
default: () => ({}),
|
||||
},
|
||||
role: { type: Object, default: () => ({}) },
|
||||
permissions: { type: Object, default: () => ({}) },
|
||||
roleHasPermissions: { type: Object, default: () => ({}) },
|
||||
});
|
||||
|
||||
const form = useForm({
|
||||
_method: 'put',
|
||||
name: props.role.name,
|
||||
description: props.role.description,
|
||||
permissions: props.roleHasPermissions,
|
||||
display_name: props.role.display_name || '', // Neu hinzugefügt
|
||||
description: props.role.description || '',
|
||||
permissions: props.roleHasPermissions || [],
|
||||
});
|
||||
|
||||
const submit = async () => {
|
||||
// await Inertia.post(stardust.route('user.store'), form); old
|
||||
await form.put(stardust.route('settings.role.update', [props.role.id]));
|
||||
// await router.put(stardust.route('settings.role.update', [props.role.id]), form);
|
||||
const submit = () => {
|
||||
form.put(stardust.route('settings.role.update', [props.role.id]), {
|
||||
preserveScroll: true,
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Sicherer Helper für die Fehleranzeige
|
||||
*/
|
||||
// const formatError = (error: string | string[] | undefined) => {
|
||||
// if (!error) return '';
|
||||
// return Array.isArray(error) ? error.join(', ') : error;
|
||||
// };
|
||||
|
||||
/**
|
||||
* AUTOMATISCHER ERROR-CLEANER
|
||||
* Löscht Fehlermeldungen sofort, wenn der User mit der Korrektur beginnt.
|
||||
*/
|
||||
// watch(() => ({ ...form.data() }), (newData, oldData) => {
|
||||
// for (const key in newData) {
|
||||
// // Vergleich via JSON.stringify für Arrays (permissions)
|
||||
// if (JSON.stringify(newData[key]) !== JSON.stringify(oldData[key]) && form.errors[key]) {
|
||||
// form.clearErrors(key as any);
|
||||
// }
|
||||
// }
|
||||
// }, { deep: true });
|
||||
</script>
|
||||
|
||||
<template>
|
||||
|
|
@ -55,50 +69,38 @@ const submit = async () => {
|
|||
small
|
||||
/>
|
||||
</SectionTitleLineWithButton>
|
||||
<!-- <CardBox form @submit.prevent="form.put(stardust.route('role.update', [props.role.id]))"> -->
|
||||
<CardBox form @submit.prevent="submit()">
|
||||
<FormField label="Name" :class="{ 'text-red-400': form.errors.name }">
|
||||
<FormControl v-model="form.name" type="text" placeholder="Enter Name" required :error="form.errors.name" :is-read-only=true>
|
||||
<div class="text-red-400 text-sm" v-if="form.errors.name">
|
||||
{{ form.errors.name }}
|
||||
</div>
|
||||
</FormControl>
|
||||
|
||||
<CardBox form @submit.prevent="submit">
|
||||
<FormField label="System Name" help="Technical identifier, cannot be changed." :errors="form.errors.name">
|
||||
<FormControl v-model="form.name" :error="form.errors.name" :is-read-only="true" />
|
||||
</FormField>
|
||||
|
||||
<FormField
|
||||
label="Description"
|
||||
help="Optional. Description of new role"
|
||||
:class="{ 'text-red-400': form.errors.description }"
|
||||
>
|
||||
<FormField label="Display Name" help="User-friendly name for this role." :errors="form.errors.display_name">
|
||||
<FormControl v-model="form.display_name" placeholder="e.g. Administrator" :error="form.errors.display_name" />
|
||||
</FormField>
|
||||
|
||||
<FormField label="Description" :errors="form.errors.description">
|
||||
<FormControl
|
||||
v-model="form.description"
|
||||
v-bind:icon="mdiFormTextarea"
|
||||
name="display_name"
|
||||
:type="'textarea'"
|
||||
:icon="mdiFormTextarea"
|
||||
type="textarea"
|
||||
placeholder="Role description..."
|
||||
:error="form.errors.description"
|
||||
>
|
||||
<div class="text-red-400 text-sm" v-if="form.errors.description">
|
||||
{{ form.errors.description }}
|
||||
</div>
|
||||
</FormControl>
|
||||
/>
|
||||
</FormField>
|
||||
|
||||
<BaseDivider />
|
||||
|
||||
<FormField label="Permissions" wrap-body>
|
||||
<FormField label="Permissions" :errors="form.errors.permissions">
|
||||
<FormCheckRadioGroup v-model="form.permissions" name="permissions" is-column :options="props.permissions" />
|
||||
</FormField>
|
||||
<div class="text-red-400 text-sm" v-if="form.errors.permissions && Array.isArray(form.errors.permissions)">
|
||||
<!-- {{ errors.password_confirmation }} -->
|
||||
{{ form.errors.permissions.join(', ') }}
|
||||
</div>
|
||||
|
||||
<template #footer>
|
||||
<BaseButtons>
|
||||
<BaseButton
|
||||
type="submit"
|
||||
color="info"
|
||||
label="Submit"
|
||||
label="Update Role"
|
||||
:class="{ 'opacity-25': form.processing }"
|
||||
:disabled="form.processing"
|
||||
/>
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
<script setup lang="ts">
|
||||
import { ref } from 'vue';
|
||||
import { Head, useForm, router } from '@inertiajs/vue3';
|
||||
import { Head, useForm } from '@inertiajs/vue3';
|
||||
import { mdiAccountKey, mdiArrowLeftBoldOutline } from '@mdi/js';
|
||||
import LayoutAuthenticated from '@/Layouts/LayoutAuthenticated.vue';
|
||||
import SectionMain from '@/Components/SectionMain.vue';
|
||||
|
|
@ -13,29 +13,27 @@ import BaseDivider from '@/Components/BaseDivider.vue';
|
|||
import BaseButton from '@/Components/BaseButton.vue';
|
||||
import BaseButtons from '@/Components/BaseButtons.vue';
|
||||
import { stardust } from '@eidellev/adonis-stardust/client';
|
||||
// import { Inertia } from '@inertiajs/inertia';
|
||||
import PasswordMeter from '@/Components/SimplePasswordMeter/password-meter.vue';
|
||||
|
||||
const enabled = ref(false);
|
||||
const handleScore = (score: number) => {
|
||||
if (score >= 4){
|
||||
enabled.value = true;
|
||||
} else {
|
||||
enabled.value = false;
|
||||
}
|
||||
};
|
||||
|
||||
const props = defineProps({
|
||||
roles: {
|
||||
type: Object,
|
||||
default: () => ({}),
|
||||
},
|
||||
// Globale Errors als Fallback, falls nicht über useForm gearbeitet wird
|
||||
errors: {
|
||||
type: Object,
|
||||
default: () => ({}),
|
||||
},
|
||||
});
|
||||
|
||||
const enabled = ref(false);
|
||||
|
||||
const handleScore = (score: number) => {
|
||||
// Passwort muss stark genug sein (Score >= 4)
|
||||
enabled.value = score >= 4;
|
||||
};
|
||||
|
||||
const form = useForm({
|
||||
login: '',
|
||||
first_name: '',
|
||||
|
|
@ -46,24 +44,30 @@ const form = useForm({
|
|||
roles: [],
|
||||
});
|
||||
|
||||
const submit = async () => {
|
||||
// await router.post(stardust.route('settings.user.store'), form);
|
||||
await form.post(stardust.route('settings.user.store'), {
|
||||
const submit = () => {
|
||||
// await router.post(stardust.route('settings.user.store'), form);
|
||||
form.post(stardust.route('settings.user.store'), {
|
||||
preserveScroll: true,
|
||||
onSuccess: () => {
|
||||
form.reset();
|
||||
enabled.value = false;
|
||||
},
|
||||
onError: () => {
|
||||
if (form.errors.new_password) {
|
||||
form.reset('new_password');
|
||||
form.reset('new_password', 'password_confirmation');
|
||||
enabled.value = false;
|
||||
// newPasswordInput.value.focus();
|
||||
// newPasswordInput.value?.focus();
|
||||
}
|
||||
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Helper um Fehler sicher anzuzeigen (String oder Array)
|
||||
*/
|
||||
const formatError = (error: string | string[] | undefined) => {
|
||||
if (!error) return '';
|
||||
return Array.isArray(error) ? error.join(', ') : error;
|
||||
};
|
||||
</script>
|
||||
|
||||
<template>
|
||||
|
|
@ -80,78 +84,68 @@ const submit = async () => {
|
|||
small
|
||||
/>
|
||||
</SectionTitleLineWithButton>
|
||||
<!-- @submit.prevent="form.post(stardust.route('settings.user.store'))" -->
|
||||
<CardBox form @submit.prevent="submit()">
|
||||
<FormField label="Login" :class="{ 'text-red-400': errors.login }">
|
||||
<FormControl v-model="form.login" type="text" placeholder="Enter Login" :errors="errors.login">
|
||||
<div class="text-red-400 text-sm" v-if="errors.login && Array.isArray(errors.login)">
|
||||
<!-- {{ errors.login }} -->
|
||||
{{ errors.login.join(', ') }}
|
||||
|
||||
<CardBox form @submit.prevent="submit">
|
||||
<FormField label="Login" :class="{ 'text-red-400': form.errors.login }">
|
||||
<FormControl v-model="form.login" type="text" placeholder="Enter Login" :errors="form.errors.login">
|
||||
<div class="text-red-400 text-sm mt-1" v-if="form.errors.login">
|
||||
{{ formatError(form.errors.login) }}
|
||||
</div>
|
||||
</FormControl>
|
||||
</FormField>
|
||||
|
||||
<FormField label="First Name" :class="{ 'text-red-400': errors.first_name }">
|
||||
<FormControl v-model="form.first_name" type="text" placeholder="Enter First Name" :errors="errors.first_name">
|
||||
<div class="text-red-400 text-sm" v-if="errors.first_name && Array.isArray(errors.first_name)">
|
||||
{{ errors.first_name.join(', ') }}
|
||||
<FormField label="First Name" :class="{ 'text-red-400': form.errors.first_name }">
|
||||
<FormControl v-model="form.first_name" type="text" placeholder="Enter First Name" :errors="form.errors.first_name">
|
||||
<div class="text-red-400 text-sm mt-1" v-if="form.errors.first_name">
|
||||
{{ formatError(form.errors.first_name) }}
|
||||
</div>
|
||||
</FormControl>
|
||||
</FormField>
|
||||
|
||||
<FormField label="Last Name" :class="{ 'text-red-400': errors.last_name }">
|
||||
<FormControl v-model="form.last_name" type="text" placeholder="Enter Last Name" :errors="errors.last_name">
|
||||
<div class="text-red-400 text-sm" v-if="errors.last_name && Array.isArray(errors.last_name)">
|
||||
{{ errors.last_name.join(', ') }}
|
||||
<FormField label="Last Name" :class="{ 'text-red-400': form.errors.last_name }">
|
||||
<FormControl v-model="form.last_name" type="text" placeholder="Enter Last Name" :errors="form.errors.last_name">
|
||||
<div class="text-red-400 text-sm mt-1" v-if="form.errors.last_name">
|
||||
{{ formatError(form.errors.last_name) }}
|
||||
</div>
|
||||
</FormControl>
|
||||
</FormField>
|
||||
|
||||
<FormField label="Email" :class="{ 'text-red-400': errors.email }">
|
||||
<FormControl v-model="form.email" type="text" placeholder="Enter Email" :errors="errors.email">
|
||||
<div class="text-red-400 text-sm" v-if="errors.email && Array.isArray(errors.email)">
|
||||
<!-- {{ errors.email }} -->
|
||||
{{ errors.email.join(', ') }}
|
||||
<FormField label="Email" :class="{ 'text-red-400': form.errors.email }">
|
||||
<FormControl v-model="form.email" type="text" placeholder="Enter Email" :errors="form.errors.email">
|
||||
<div class="text-red-400 text-sm mt-1" v-if="form.errors.email">
|
||||
{{ formatError(form.errors.email) }}
|
||||
</div>
|
||||
</FormControl>
|
||||
</FormField>
|
||||
|
||||
<!-- <FormField label="Password" :class="{ 'text-red-400': errors.password }">
|
||||
<FormControl v-model="form.password" type="password" placeholder="Enter Password" :errors="errors.password">
|
||||
<div class="text-red-400 text-sm" v-if="errors.password && Array.isArray(errors.password)">
|
||||
{{ errors.password.join(', ') }}
|
||||
</div>
|
||||
</FormControl>
|
||||
</FormField>
|
||||
<password-meter :password="form.password" @score="handleScore" /> -->
|
||||
<PasswordMeter v-model="form.new_password" :errors="form.errors" @score="handleScore" />
|
||||
<!-- <FormField label="Password" :class="{ 'text-red-400': form.errors.new_password }">
|
||||
<div class="text-red-400 text-sm mt-1" v-if="form.errors.new_password">
|
||||
{{ formatError(form.errors.new_password) }}
|
||||
</div>
|
||||
</FormField> -->
|
||||
<PasswordMeter v-model="form.new_password" :errors="form.errors" @score="handleScore" />
|
||||
|
||||
<FormField label="Password Confirmation" :class="{ 'text-red-400': errors.password_confirmation }">
|
||||
<FormField label="Password Confirmation" :class="{ 'text-red-400': form.errors.password_confirmation }">
|
||||
<FormControl
|
||||
v-model="form.password_confirmation"
|
||||
type="password"
|
||||
placeholder="Enter Password Confirmation"
|
||||
:errors="errors.password"
|
||||
placeholder="Confirm Password"
|
||||
:errors="form.errors.password_confirmation"
|
||||
>
|
||||
<div
|
||||
class="text-red-400 text-sm"
|
||||
v-if="errors.password_confirmation && Array.isArray(errors.password_confirmation)"
|
||||
>
|
||||
<!-- {{ errors.password_confirmation }} -->
|
||||
{{ errors.password_confirmation.join(', ') }}
|
||||
<div class="text-red-400 text-sm mt-1" v-if="form.errors.password_confirmation">
|
||||
{{ formatError(form.errors.password_confirmation) }}
|
||||
</div>
|
||||
</FormControl>
|
||||
</FormField>
|
||||
|
||||
<BaseDivider />
|
||||
|
||||
<FormField label="Roles" wrap-body :class="{ 'text-red-400': errors.roles }">
|
||||
<FormField label="Roles" wrap-body :class="{ 'text-red-400': form.errors.roles }">
|
||||
<FormCheckRadioGroup v-model="form.roles" name="roles" is-column :options="props.roles" />
|
||||
<div class="text-red-400 text-sm mt-1" v-if="form.errors.roles">
|
||||
{{ formatError(form.errors.roles) }}
|
||||
</div>
|
||||
</FormField>
|
||||
<div class="text-red-400 text-sm" v-if="errors.roles && Array.isArray(errors.roles)">
|
||||
<!-- {{ errors.password_confirmation }} -->
|
||||
{{ errors.roles.join(', ') }}
|
||||
</div>
|
||||
|
||||
<template #footer>
|
||||
<BaseButtons>
|
||||
|
|
@ -159,12 +153,12 @@ const submit = async () => {
|
|||
type="submit"
|
||||
color="info"
|
||||
label="Submit"
|
||||
:class="{ 'opacity-25': form.processing }"
|
||||
:disabled="form.processing == true || enabled == false"
|
||||
:class="{ 'opacity-25': form.processing }"
|
||||
:disabled="form.processing || !enabled"
|
||||
/>
|
||||
</BaseButtons>
|
||||
</template>
|
||||
</CardBox>
|
||||
</SectionMain>
|
||||
</LayoutAuthenticated>
|
||||
</template>
|
||||
</template>
|
||||
|
|
@ -1,6 +1,6 @@
|
|||
<script setup lang="ts">
|
||||
import { ref } from 'vue';
|
||||
import { Head, useForm, router } from '@inertiajs/vue3';
|
||||
import { Head, useForm } from '@inertiajs/vue3';
|
||||
import { mdiAccountKey, mdiArrowLeftBoldOutline } from '@mdi/js';
|
||||
import LayoutAuthenticated from '@/Layouts/LayoutAuthenticated.vue';
|
||||
import SectionMain from '@/Components/SectionMain.vue';
|
||||
|
|
@ -13,29 +13,18 @@ import BaseDivider from '@/Components/BaseDivider.vue';
|
|||
import BaseButton from '@/Components/BaseButton.vue';
|
||||
import BaseButtons from '@/Components/BaseButtons.vue';
|
||||
import { stardust } from '@eidellev/adonis-stardust/client';
|
||||
// import { Inertia } from '@inertiajs/inertia';
|
||||
import PasswordMeter from '@/Components/SimplePasswordMeter/password-meter.vue';
|
||||
|
||||
const enabled = ref(false);
|
||||
const props = defineProps({
|
||||
user: {
|
||||
type: Object,
|
||||
default: () => ({}),
|
||||
},
|
||||
roles: {
|
||||
type: Object,
|
||||
default: () => ({}),
|
||||
},
|
||||
userHasRoles: {
|
||||
type: Object,
|
||||
default: () => ({}),
|
||||
},
|
||||
errors: {
|
||||
type: Object,
|
||||
default: () => ({}),
|
||||
},
|
||||
user: { type: Object, default: () => ({}) },
|
||||
roles: { type: Object, default: () => ({}) },
|
||||
userHasRoles: { type: Object, default: () => ({}) },
|
||||
errors: { type: Object, default: () => ({}) }, // Fallback
|
||||
});
|
||||
|
||||
// enabled ist true, solange kein (schwaches) Passwort eingegeben wird
|
||||
const enabled = ref(true);
|
||||
|
||||
const form = useForm({
|
||||
_method: 'put',
|
||||
login: props.user.login,
|
||||
|
|
@ -47,30 +36,34 @@ const form = useForm({
|
|||
roles: props.userHasRoles, // fill actual user roles from db
|
||||
});
|
||||
|
||||
const submit = async () => {
|
||||
// await Inertia.post(stardust.route('user.store'), form);
|
||||
// await router.put(stardust.route('settings.user.update', [props.user.id]), form);
|
||||
await form.put(stardust.route('settings.user.update', [props.user.id]), {
|
||||
const submit = () => {
|
||||
form.put(stardust.route('settings.user.update', [props.user.id]), {
|
||||
preserveScroll: true,
|
||||
onSuccess: () => {
|
||||
form.reset();
|
||||
// Bei Erfolg Passwortfelder leeren
|
||||
form.reset('new_password', 'password_confirmation');
|
||||
},
|
||||
onError: () => {
|
||||
if (form.errors.new_password) {
|
||||
form.reset('new_password');
|
||||
form.reset('new_password', 'password_confirmation');
|
||||
enabled.value = false;
|
||||
// newPasswordInput.value.focus();
|
||||
// newPasswordInput.value?.focus();
|
||||
}
|
||||
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
const formatError = (error: string | string[] | undefined) => {
|
||||
if (!error) return '';
|
||||
return Array.isArray(error) ? error.join(', ') : error;
|
||||
};
|
||||
|
||||
const handleScore = (score: number) => {
|
||||
if (score >= 4){
|
||||
// Wenn das Feld leer ist, ist der Status egal (Passwort wird nicht geändert)
|
||||
// Wenn etwas drin steht, muss der Score >= 4 sein
|
||||
if (form.new_password === '') {
|
||||
enabled.value = true;
|
||||
} else {
|
||||
enabled.value = false;
|
||||
enabled.value = score >= 4;
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
|
@ -89,89 +82,87 @@ const handleScore = (score: number) => {
|
|||
small
|
||||
/>
|
||||
</SectionTitleLineWithButton>
|
||||
<!-- <CardBox form @submit.prevent="form.put(stardust.route('user.update', [props.user.id]))"> -->
|
||||
<CardBox form @submit.prevent="submit()">
|
||||
<FormField label="Enter Login" :class="{ 'text-red-400': errors.name }">
|
||||
<FormControl v-model="form.login" type="text" placeholder="Name" :errors="errors.login">
|
||||
<div class="text-red-400 text-sm" v-if="errors.login">
|
||||
{{ errors.login }}
|
||||
|
||||
<CardBox form @submit.prevent="submit">
|
||||
<FormField label="Login" :class="{ 'text-red-400': form.errors.login }">
|
||||
<FormControl v-model="form.login" type="text" placeholder="Login" :errors="form.errors.login">
|
||||
<div class="text-red-400 text-sm mt-1" v-if="form.errors.login">
|
||||
{{ formatError(form.errors.login) }}
|
||||
</div>
|
||||
</FormControl>
|
||||
</FormField>
|
||||
|
||||
<FormField label="First Name" :class="{ 'text-red-400': errors.first_name }">
|
||||
<FormControl v-model="form.first_name" type="text" placeholder="Enter First Name" :errors="errors.first_name">
|
||||
<div class="text-red-400 text-sm" v-if="errors.first_name && Array.isArray(errors.first_name)">
|
||||
{{ errors.first_name.join(', ') }}
|
||||
<FormField label="First Name" :class="{ 'text-red-400': form.errors.first_name }">
|
||||
<FormControl v-model="form.first_name" type="text" placeholder="First Name" :errors="form.errors.first_name">
|
||||
<div class="text-red-400 text-sm mt-1" v-if="form.errors.first_name">
|
||||
{{ formatError(form.errors.first_name) }}
|
||||
</div>
|
||||
</FormControl>
|
||||
</FormField>
|
||||
|
||||
<FormField label="Last Name" :class="{ 'text-red-400': errors.last_name }">
|
||||
<FormControl v-model="form.last_name" type="text" placeholder="Enter Last Name" :errors="errors.last_name">
|
||||
<div class="text-red-400 text-sm" v-if="errors.last_name && Array.isArray(errors.last_name)">
|
||||
{{ errors.last_name.join(', ') }}
|
||||
<FormField label="Last Name" :class="{ 'text-red-400': form.errors.last_name }">
|
||||
<FormControl v-model="form.last_name" type="text" placeholder="Last Name" :errors="form.errors.last_name">
|
||||
<div class="text-red-400 text-sm mt-1" v-if="form.errors.last_name">
|
||||
{{ formatError(form.errors.last_name) }}
|
||||
</div>
|
||||
</FormControl>
|
||||
</FormField>
|
||||
|
||||
<FormField label="Enter Email" :class="{ 'text-red-400': errors.email }">
|
||||
<FormControl v-model="form.email" type="text" placeholder="Email" :errors="errors.email">
|
||||
<div class="text-red-400 text-sm" v-if="errors.email && Array.isArray(errors.email)">
|
||||
{{ errors.email.join(', ') }}
|
||||
<FormField label="Email" :class="{ 'text-red-400': form.errors.email }">
|
||||
<FormControl v-model="form.email" type="text" placeholder="Email" :errors="form.errors.email">
|
||||
<div class="text-red-400 text-sm mt-1" v-if="form.errors.email">
|
||||
{{ formatError(form.errors.email) }}
|
||||
</div>
|
||||
</FormControl>
|
||||
</FormField>
|
||||
|
||||
<!-- <FormField label="Password" :class="{ 'text-red-400': errors.password }">
|
||||
<FormControl v-model="form.password" type="password" placeholder="Enter Password" :errors="errors.password">
|
||||
<div class="text-red-400 text-sm" v-if="errors.password">
|
||||
{{ errors.password }}
|
||||
</div>
|
||||
</FormControl>
|
||||
</FormField> -->
|
||||
<div class="py-4">
|
||||
<PasswordMeter
|
||||
field-label="Reset User Password (leave blank to keep current)"
|
||||
:show-required-message="false"
|
||||
v-model="form.new_password"
|
||||
:errors="form.errors"
|
||||
@score="handleScore"
|
||||
/>
|
||||
<!-- <div class="text-red-400 text-sm mt-1" v-if="form.errors.new_password">
|
||||
{{ formatError(form.errors.new_password) }}
|
||||
</div> -->
|
||||
</div>
|
||||
|
||||
<PasswordMeter field-label="Reset User Password" :show-required-message="false" ref="newPasswordInput" v-model="form.new_password" :errors="form.errors" @score="handleScore" />
|
||||
|
||||
|
||||
<FormField label="Password Confirmation" :class="{ 'text-red-400': errors.password_confirmation }">
|
||||
<FormField label="Password Confirmation" :class="{ 'text-red-400': form.errors.password_confirmation }">
|
||||
<FormControl
|
||||
v-model="form.password_confirmation"
|
||||
type="password"
|
||||
placeholder="Enter Password Confirmation"
|
||||
:errors="errors.password"
|
||||
placeholder="Confirm New Password"
|
||||
:errors="form.errors.password_confirmation"
|
||||
>
|
||||
<div
|
||||
class="text-red-400 text-sm"
|
||||
v-if="errors.password_confirmation && Array.isArray(errors.password_confirmation)"
|
||||
>
|
||||
{{ errors.password_confirmation.join(', ') }}
|
||||
<div class="text-red-400 text-sm mt-1" v-if="form.errors.password_confirmation">
|
||||
{{ formatError(form.errors.password_confirmation) }}
|
||||
</div>
|
||||
</FormControl>
|
||||
</FormField>
|
||||
|
||||
<BaseDivider />
|
||||
|
||||
<FormField label="Roles" wrap-body :class="{ 'text-red-400': errors.roles }">
|
||||
<FormField label="Roles" wrap-body :class="{ 'text-red-400': form.errors.roles }">
|
||||
<FormCheckRadioGroup v-model="form.roles" name="roles" is-column :options="props.roles" />
|
||||
<div class="text-red-400 text-sm mt-1" v-if="form.errors.roles">
|
||||
{{ formatError(form.errors.roles) }}
|
||||
</div>
|
||||
</FormField>
|
||||
<div class="text-red-400 text-sm" v-if="errors.roles && Array.isArray(errors.roles)">
|
||||
<!-- {{ errors.password_confirmation }} -->
|
||||
{{ errors.roles.join(', ') }}
|
||||
</div>
|
||||
|
||||
<template #footer>
|
||||
<BaseButtons>
|
||||
<BaseButton
|
||||
type="submit"
|
||||
color="info"
|
||||
label="Submit"
|
||||
label="Update User"
|
||||
:class="{ 'opacity-25': form.processing }"
|
||||
:disabled="form.processing == true|| (form.new_password != '' && enabled == false)"
|
||||
:disabled="form.processing || !enabled"
|
||||
/>
|
||||
</BaseButtons>
|
||||
</template>
|
||||
</CardBox>
|
||||
</SectionMain>
|
||||
</LayoutAuthenticated>
|
||||
</template>
|
||||
</template>
|
||||
|
|
@ -48,9 +48,22 @@ mainService.fetchApi('authors');
|
|||
mainService.fetchApi('datasets');
|
||||
mainService.fetchChartData();
|
||||
|
||||
// Safe role check: authUser or its roles may be absent depending on auth state
|
||||
const userHasRoles = (roleNames: Array<string>): boolean => {
|
||||
return user.value.roles.some((role) => roleNames.includes(role.name));
|
||||
return user.value?.roles?.some((role) => roleNames.includes(role)) ?? false;
|
||||
};
|
||||
|
||||
// Greeting that adapts to the time of day
|
||||
const greeting = computed(() => {
|
||||
const h = new Date().getHours();
|
||||
if (h < 12) return 'Good morning';
|
||||
if (h < 18) return 'Good afternoon';
|
||||
return 'Good evening';
|
||||
});
|
||||
|
||||
const today = computed(() =>
|
||||
new Date().toLocaleDateString(undefined, { weekday: 'long', year: 'numeric', month: 'long', day: 'numeric' }),
|
||||
);
|
||||
</script>
|
||||
|
||||
<template>
|
||||
|
|
@ -58,67 +71,62 @@ const userHasRoles = (roleNames: Array<string>): boolean => {
|
|||
<Head title="Dashboard" />
|
||||
|
||||
<SectionMain>
|
||||
<SectionTitleLineWithButton :icon="mdiChartTimelineVariant" title="Dashboard Overview" main>
|
||||
<div class="text-sm text-gray-500 dark:text-gray-400">
|
||||
Welcome back, <span class="font-semibold">{{ user.login }}</span>
|
||||
</div>
|
||||
</SectionTitleLineWithButton>
|
||||
<!-- Greeting hero -->
|
||||
<div
|
||||
class="reveal relative overflow-hidden rounded-2xl mb-8 p-6 md:p-8 bg-gradient-to-br from-slate-900 via-slate-800 to-slate-900 text-white shadow-xl ring-1 ring-white/5"
|
||||
>
|
||||
<!-- grüner Akzent-Glow statt weißer Kreise -->
|
||||
<div class="absolute -right-10 -top-10 w-48 h-48 rounded-full bg-emerald-500/15 blur-3xl"></div>
|
||||
<div class="absolute -left-8 -bottom-12 w-40 h-40 rounded-full bg-green-400/10 blur-3xl"></div>
|
||||
<!-- schmale Akzentkante links -->
|
||||
<div class="absolute inset-y-0 left-0 w-1 bg-gradient-to-b from-green-400 to-emerald-600"></div>
|
||||
<div class="relative">
|
||||
<p class="text-sm font-medium text-slate-400">{{ today }}</p>
|
||||
<h1 class="mt-1 text-2xl md:text-3xl font-bold tracking-tight">
|
||||
{{ greeting }}, <span class="text-green-400">{{ user?.login ?? 'there' }}</span>
|
||||
</h1>
|
||||
<p class="mt-2 text-sm text-slate-400 max-w-xl">
|
||||
Here's an overview of authors, publications and submitters across the repository.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Stats Grid -->
|
||||
<div class="grid grid-cols-1 gap-6 lg:grid-cols-3 mb-6">
|
||||
<CardBoxWidget
|
||||
trend="12%"
|
||||
trend-type="up"
|
||||
color="text-emerald-500"
|
||||
:icon="mdiAccountMultiple"
|
||||
:number="authors.length"
|
||||
label="Authors"
|
||||
class="hover:shadow-lg transition-shadow duration-300"
|
||||
/>
|
||||
<CardBoxWidget
|
||||
trend-type="info"
|
||||
color="text-blue-500"
|
||||
:icon="mdiDatabaseOutline"
|
||||
:number="datasets.length"
|
||||
label="Publications"
|
||||
class="hover:shadow-lg transition-shadow duration-300"
|
||||
/>
|
||||
<CardBoxWidget
|
||||
trend-type="up"
|
||||
color="text-purple-500"
|
||||
:icon="mdiChartTimelineVariant"
|
||||
:number="submitters.length"
|
||||
label="Submitters"
|
||||
class="hover:shadow-lg transition-shadow duration-300"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<!-- <div class="grid grid-cols-1 lg:grid-cols-2 gap-6 mb-6">
|
||||
<div class="flex flex-col justify-between">
|
||||
<CardBoxClient
|
||||
v-for="client in authorBarItems"
|
||||
:key="client.id"
|
||||
:name="client.name"
|
||||
:email="client.email"
|
||||
:date="client.created_at"
|
||||
:text="client.identifier_orcid"
|
||||
:count="client.dataset_count"
|
||||
|
||||
/>
|
||||
</div> <!--
|
||||
<div class="flex flex-col justify-between">
|
||||
<CardBoxDataset
|
||||
v-for="(dataset, index) in datasetBarItems"
|
||||
:key="index"
|
||||
:dataset="dataset"
|
||||
<div class="reveal reveal-1 grid grid-cols-1 gap-6 lg:grid-cols-3 mb-8">
|
||||
<div class="rounded-xl border-l-4 border-emerald-500 bg-white dark:bg-slate-900/40 shadow-sm hover:shadow-lg hover:-translate-y-1 transition-all duration-300">
|
||||
<CardBoxWidget
|
||||
trend="12%"
|
||||
trend-type="up"
|
||||
color="text-emerald-500"
|
||||
:icon="mdiAccountMultiple"
|
||||
:number="authors.length"
|
||||
label="Authors"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div class="rounded-xl border-l-4 border-blue-500 bg-white dark:bg-slate-900/40 shadow-sm hover:shadow-lg hover:-translate-y-1 transition-all duration-300">
|
||||
<CardBoxWidget
|
||||
trend-type="info"
|
||||
color="text-blue-500"
|
||||
:icon="mdiDatabaseOutline"
|
||||
:number="datasets.length"
|
||||
label="Publications"
|
||||
/>
|
||||
</div>
|
||||
<div class="rounded-xl border-l-4 border-purple-500 bg-white dark:bg-slate-900/40 shadow-sm hover:shadow-lg hover:-translate-y-1 transition-all duration-300">
|
||||
<CardBoxWidget
|
||||
trend-type="up"
|
||||
color="text-purple-500"
|
||||
:icon="mdiChartTimelineVariant"
|
||||
:number="submitters.length"
|
||||
label="Submitters"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Recent Datasets Section -->
|
||||
<div v-if="datasetBarItems.length > 0" class="mb-6">
|
||||
<div v-if="datasetBarItems.length > 0" class="reveal reveal-2 mb-8">
|
||||
<SectionTitleLineWithButton :icon="mdiTrendingUp" title="Recent Publications">
|
||||
<span class="text-sm text-gray-500 dark:text-gray-400"> Latest {{ datasetBarItems.length }} publications </span>
|
||||
<span class="text-sm text-gray-500 dark:text-gray-400">Latest {{ datasetBarItems.length }} publications</span>
|
||||
</SectionTitleLineWithButton>
|
||||
|
||||
<div class="grid grid-cols-1 gap-4">
|
||||
|
|
@ -131,18 +139,16 @@ const userHasRoles = (roleNames: Array<string>): boolean => {
|
|||
</div>
|
||||
</div>
|
||||
|
||||
<!-- <SectionBannerStarOnGitHub /> -->
|
||||
|
||||
<!-- Chart Section -->
|
||||
<SectionTitleLineWithButton :icon="mdiChartPie" title="Trends Overview" class="mt-8">
|
||||
<span class="text-sm text-gray-500 dark:text-gray-400"> Publications per month </span>
|
||||
<SectionTitleLineWithButton :icon="mdiChartPie" title="Trends Overview" class="reveal reveal-3 mt-8">
|
||||
<span class="text-sm text-gray-500 dark:text-gray-400">Publications per month</span>
|
||||
</SectionTitleLineWithButton>
|
||||
|
||||
<CardBox
|
||||
title="Performance"
|
||||
:icon="mdiFinance"
|
||||
:header-icon="mdiReload"
|
||||
class="mb-6 shadow-lg"
|
||||
class="reveal reveal-3 mb-6 shadow-lg"
|
||||
@header-icon-click="fillChartData"
|
||||
>
|
||||
<div v-if="isLoadingChart" class="flex items-center justify-center h-96">
|
||||
|
|
@ -162,14 +168,46 @@ const userHasRoles = (roleNames: Array<string>): boolean => {
|
|||
<!-- Admin Section -->
|
||||
<template v-if="userHasRoles(['administrator'])">
|
||||
<SectionTitleLineWithButton :icon="mdiAccountMultiple" title="Submitters Management" class="mt-8">
|
||||
<span class="text-sm text-gray-500 dark:text-gray-400"> Administrator view </span>
|
||||
<span class="text-sm text-gray-500 dark:text-gray-400">Administrator view</span>
|
||||
</SectionTitleLineWithButton>
|
||||
|
||||
<CardBox :icon="mdiMonitorCellphone" title="All Submitters" has-table class="shadow-lg">
|
||||
<TableSampleClients />
|
||||
</CardBox>
|
||||
</template>
|
||||
|
||||
</SectionMain>
|
||||
</LayoutAuthenticated>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
/* Staggered entrance reveal */
|
||||
@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.08s;
|
||||
}
|
||||
.reveal-2 {
|
||||
animation-delay: 0.16s;
|
||||
}
|
||||
.reveal-3 {
|
||||
animation-delay: 0.24s;
|
||||
}
|
||||
|
||||
@media (prefers-reduced-motion: reduce) {
|
||||
.reveal {
|
||||
animation: none;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
File diff suppressed because it is too large
Load diff
|
|
@ -38,7 +38,6 @@ import { MainService } from '@/Stores/main';
|
|||
import { notify } from '@/notiwind';
|
||||
import MapComponent from '@/Components/Map/map.component.vue';
|
||||
import { MapOptions } from '@/Components/Map/MapOptions';
|
||||
// import { LatLngBoundsExpression } from 'leaflet/src/geo/LatLngBounds';
|
||||
import { LatLngBoundsExpression } from 'leaflet';
|
||||
import { LayerOptions } from '@/Components/Map/LayerOptions';
|
||||
import TableKeywords from '@/Components/TableKeywords.vue';
|
||||
|
|
@ -90,14 +89,11 @@ const props = defineProps({
|
|||
});
|
||||
|
||||
const flash: ComputedRef<any> = computed(() => {
|
||||
// let test = usePage();
|
||||
// console.log(test);
|
||||
return usePage().props.flash;
|
||||
});
|
||||
|
||||
// Computed property to determine the placeholder based on the selected option
|
||||
const getPlaceholder = computed(() => (type: string) => {
|
||||
|
||||
switch (type) {
|
||||
case 'DOI':
|
||||
return 'https://doi.org/10.24341/tethys.236';
|
||||
|
|
@ -118,19 +114,9 @@ const getPlaceholder = computed(() => (type: string) => {
|
|||
|
||||
const mainService = MainService();
|
||||
|
||||
// let serrors = reactive([]);
|
||||
|
||||
// const form = useForm({
|
||||
// language: '',
|
||||
// licenses: [],
|
||||
// type: '',
|
||||
// titles: [{ value: '', type: 'Main', language: Dataset.language }],
|
||||
// });
|
||||
// let language: (string | Ref<string>) = ref('');
|
||||
let language = ref('');
|
||||
let dataset: Dataset;
|
||||
if (Object.keys(mainService.dataset).length == 0) {
|
||||
// language = ref('');
|
||||
dataset = {
|
||||
language: language.value,
|
||||
licenses: [],
|
||||
|
|
@ -158,7 +144,6 @@ if (Object.keys(mainService.dataset).length == 0) {
|
|||
time_max: undefined,
|
||||
time_absolut: undefined,
|
||||
},
|
||||
// errors: undefined,
|
||||
subjects: [
|
||||
{ value: '', type: 'uncontrolled', language: language.value },
|
||||
{ value: '', type: 'uncontrolled', language: language.value },
|
||||
|
|
@ -166,15 +151,10 @@ if (Object.keys(mainService.dataset).length == 0) {
|
|||
],
|
||||
references: [],
|
||||
files: [],
|
||||
// upload: { label: 'test', sorting: 0 },
|
||||
};
|
||||
// Set the form's current values as the new defaults...
|
||||
// mainService.setDataset(dataset, language);
|
||||
} else {
|
||||
// console.log(mainService.dataset);
|
||||
language.value = mainService.dataset.language;
|
||||
|
||||
// dataset = mainService.dataset;
|
||||
dataset = {
|
||||
language: mainService.dataset.language,
|
||||
licenses: mainService.dataset.licenses,
|
||||
|
|
@ -191,25 +171,9 @@ if (Object.keys(mainService.dataset).length == 0) {
|
|||
subjects: mainService.dataset.subjects,
|
||||
references: mainService.dataset.references,
|
||||
files: mainService.dataset.files,
|
||||
// upload: mainService.dataset.upload,
|
||||
};
|
||||
// for (let index in mainService.dataset.titles) {
|
||||
// let title: Title = mainService.dataset.titles[index];
|
||||
// if (title.type == 'Main') {
|
||||
// title.language = language;
|
||||
// }
|
||||
// }
|
||||
// for (let index in mainService.dataset.descriptions) {
|
||||
// let description: Description = mainService.dataset.descriptions[index];
|
||||
// if (description.type == 'Abstract') {
|
||||
// description.language = language;
|
||||
// }
|
||||
// }
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
// const form = useForm<Dataset>({
|
||||
// language: language,
|
||||
// licenses: [],
|
||||
|
|
@ -262,7 +226,6 @@ watch(depth, (currentValue) => {
|
|||
form.coverage.depth_max = undefined;
|
||||
}
|
||||
});
|
||||
// let time= "no_time";
|
||||
let time = ref('no_time');
|
||||
watch(time, (currentValue) => {
|
||||
if (currentValue == 'absolut') {
|
||||
|
|
@ -293,14 +256,6 @@ const fitBounds: LatLngBoundsExpression = [
|
|||
];
|
||||
const mapId = 'test';
|
||||
|
||||
// const submit = async () => {
|
||||
// await router.post(stardust.route('user.store'), form, {
|
||||
// onSuccess: () => {
|
||||
// form.reset(), (formStep.value = 1);
|
||||
// },
|
||||
// });
|
||||
// };
|
||||
|
||||
const nextStep = async () => {
|
||||
let route = "";
|
||||
if (formStep.value == 1) {
|
||||
|
|
@ -313,9 +268,7 @@ const nextStep = async () => {
|
|||
// When posting in steps 1-3, remove any file uploads from the data.
|
||||
await form
|
||||
.transform((data: Dataset) => {
|
||||
// Create payload and set rights (transforming to a string if needed)
|
||||
const payload: any = { ...data, rights: data.rights ? 'true' : 'false' };
|
||||
// Remove the files property so that the partial update is done without files
|
||||
if (payload.files) {
|
||||
delete payload.files;
|
||||
}
|
||||
|
|
@ -323,7 +276,6 @@ const nextStep = async () => {
|
|||
})
|
||||
.post(route, {
|
||||
onSuccess: () => {
|
||||
// console.log(form.data());
|
||||
mainService.setDataset(form.data());
|
||||
formStep.value++;
|
||||
},
|
||||
|
|
@ -340,7 +292,6 @@ const submit = async () => {
|
|||
return new File([obj.blob], obj.label, { type: obj.type, lastModified: obj.lastModified, sort_order: obj.sort_order });
|
||||
});
|
||||
|
||||
// formStep.value++;
|
||||
await form
|
||||
.transform((data) => ({
|
||||
...data,
|
||||
|
|
@ -348,14 +299,7 @@ const submit = async () => {
|
|||
rights: form.rights && form.rights == true ? 'true' : 'false',
|
||||
}))
|
||||
.post(route, {
|
||||
// forceFormData: true,
|
||||
onSuccess: () => {
|
||||
// console.log(form.data());
|
||||
// mainService.clearDataset();
|
||||
// mainService.setDataset(form.data());
|
||||
// formStep.value++;
|
||||
// form.reset();
|
||||
|
||||
language.value = '';
|
||||
formStep.value = 1;
|
||||
let dataset = {
|
||||
|
|
@ -385,7 +329,6 @@ const submit = async () => {
|
|||
time_max: undefined,
|
||||
time_absolut: undefined,
|
||||
},
|
||||
// errors: undefined,
|
||||
subjects: [
|
||||
{ value: '', type: 'uncontrolled', language: language.value },
|
||||
{ value: '', type: 'uncontrolled', language: language.value },
|
||||
|
|
@ -408,7 +351,6 @@ const addNewAuthor = () => {
|
|||
|
||||
const addTitle = () => {
|
||||
let newTitle: Title = { value: '', language: '', type: '' };
|
||||
//this.dataset.files.push(uploadedFiles[i]);
|
||||
form.titles.push(newTitle);
|
||||
};
|
||||
const removeTitle = (key: number) => {
|
||||
|
|
@ -417,7 +359,6 @@ const removeTitle = (key: number) => {
|
|||
|
||||
const addDescription = () => {
|
||||
let newDescription = { value: '', language: '', type: '' };
|
||||
//this.dataset.files.push(uploadedFiles[i]);
|
||||
form.descriptions.push(newDescription);
|
||||
};
|
||||
const removeDescription = (key: number) => {
|
||||
|
|
@ -446,55 +387,39 @@ const onAddContributor = (person: Person) => {
|
|||
} else if (form.authors.filter((e) => e.id === person.id).length > 0) {
|
||||
notify({ type: 'warning', title: 'Warning', text: 'person is already defined as author' }, 4000);
|
||||
} else {
|
||||
// person.pivot = { contributor_type: '' };
|
||||
// // person.pivot = { name_type: '', contributor_type: '' };
|
||||
form.contributors.push(person);
|
||||
notify({ type: 'info', text: 'person has been successfully added as contributor' }, 4000);
|
||||
}
|
||||
};
|
||||
// const onMapInitializedEvent = "onMapInitializedEvent";
|
||||
|
||||
const onMapInitialized = (newItem: any) => {
|
||||
// notify({ type: 'info', text: message });
|
||||
console.log(newItem);
|
||||
};
|
||||
|
||||
/*
|
||||
adds a new Keyword
|
||||
*/
|
||||
* adds a new Keyword
|
||||
*/
|
||||
const addKeyword = () => {
|
||||
let newSubject: Subject = { value: '', language: '', type: 'uncontrolled' };
|
||||
//this.dataset.files.push(uploadedFiles[i]);
|
||||
form.subjects.push(newSubject);
|
||||
};
|
||||
|
||||
const addReference = () => {
|
||||
let newReference = { value: '', label: '', relation: '', type: '' };
|
||||
//this.dataset.files.push(uploadedFiles[i]);
|
||||
form.references.push(newReference);
|
||||
};
|
||||
|
||||
/*
|
||||
Removes a selected reference
|
||||
*/
|
||||
* Removes a selected reference
|
||||
*/
|
||||
const removeReference = (key: number) => {
|
||||
form.references.splice(key, 1);
|
||||
};
|
||||
/*
|
||||
|
||||
// const onChangeFile = (event) => {
|
||||
// // let uploadedFile = event.target.files[0];
|
||||
|
||||
// let fileName = String(event.target.files[0].name.replace(/\.[^/.]+$/, ''));
|
||||
// form.file = event.target.files[0];
|
||||
// form.upload.label = fileName;
|
||||
// // form.upload = file;
|
||||
// // console.log(file.file);
|
||||
// };
|
||||
/*
|
||||
Removes a selected keyword
|
||||
*/
|
||||
// const removeKeyword = (key) => {
|
||||
// form.subjects.splice(key, 1);
|
||||
// };
|
||||
const formatError = (error: string | string[] | undefined | null): string => {
|
||||
if (!error) return '';
|
||||
return Array.isArray(error) ? error.join(', ') : error;
|
||||
};
|
||||
</script>
|
||||
|
||||
<template>
|
||||
|
|
@ -526,8 +451,6 @@ Removes a selected keyword
|
|||
<Head title="Submit Dataset" />
|
||||
<SectionMain>
|
||||
<SectionTitleLineWithButton :icon="mdiDatabasePlus" title="Submit dataset" main>
|
||||
<!-- <BaseButton :route-name="stardust.route('user.index')" :icon="mdiArrowLeftBoldOutline" label="Back"
|
||||
color="white" rounded-full small /> -->
|
||||
{{ formStep }}
|
||||
</SectionTitleLineWithButton>
|
||||
<NotificationBar v-if="flash.message" color="success" :icon="mdiAlertBoxOutline">
|
||||
|
|
@ -538,8 +461,6 @@ Removes a selected keyword
|
|||
<CardBox>
|
||||
<div class="mx-4 p-4">
|
||||
<div class="flex items-center">
|
||||
<!-- <label>{{ form.titles[0].language }}</label>
|
||||
<label>{{ form.language }}</label> -->
|
||||
<icon-wizard :is-current="formStep == 1" :is-checked="formStep > 1" :label="'Step 1'">
|
||||
<icon-language></icon-language>
|
||||
</icon-wizard>
|
||||
|
|
@ -559,31 +480,23 @@ Removes a selected keyword
|
|||
</div>
|
||||
</div>
|
||||
|
||||
<!-- mt-8: margin-top: 2rem; /* 32px */ 4 p-4: spacing 1rem 16px-->
|
||||
<div class="mt-8 p-4">
|
||||
<div v-if="formStep == 1">
|
||||
<div class="flex flex-col md:flex-row">
|
||||
<FormField label="Language *" help="required: select dataset main language"
|
||||
:class="{ 'text-red-400': errors.language }" class="w-full mx-2 flex-1">
|
||||
:errors="form.errors.language" class="w-full mx-2 flex-1">
|
||||
<FormControl required v-model="language" :type="'select'" placeholder="[Enter Language]"
|
||||
:errors="form.errors.language" :options="{ de: 'de', en: 'en' }">
|
||||
<div class="text-red-400 text-sm" v-if="form.errors.language">
|
||||
{{ form.errors.language.join(', ') }}
|
||||
</div>
|
||||
</FormControl>
|
||||
</FormField>
|
||||
</div>
|
||||
|
||||
<FormField label="Licenses" wrap-body :class="{ 'text-red-400': form.errors.licenses }"
|
||||
<FormField label="Licenses" wrap-body :errors="form.errors.licenses"
|
||||
class="mt-8 w-full mx-2 flex-1">
|
||||
<FormCheckRadioGroup type="radio" v-model="form.licenses" name="licenses" is-column
|
||||
:options="props.licenses" />
|
||||
</FormField>
|
||||
|
||||
<!-- <label for="rights">
|
||||
<input class="form-checkbox" name="rights" id="rights" type="checkbox" v-model="dataset.rights" />
|
||||
terms and conditions
|
||||
</label> -->
|
||||
<FormField label="Rights"
|
||||
help="You must agree that you have read the Terms and Conditions. Please click on the 'i' icon to find a read the policy"
|
||||
wrap-body :class="{ 'text-red-400': form.errors.rights }"
|
||||
|
|
@ -592,77 +505,55 @@ Removes a selected keyword
|
|||
<input type="checkbox" id="rights" required v-model="form.rights" />
|
||||
<span class="check" />
|
||||
<a class="pl-2" target="_blank">terms and conditions </a>
|
||||
<!-- <BaseButton color="modern" :icon="mdiInformationOutline" small @click="isModalActive = true" /> -->
|
||||
<BaseIcon v-if="mdiInformationOutline" :path="mdiInformationOutline"
|
||||
@click.prevent="isModalActive = true" />
|
||||
</label>
|
||||
</FormField>
|
||||
<div class="text-red-400 text-sm" v-if="errors.rights && Array.isArray(errors.rights)">
|
||||
<!-- {{ errors.password_confirmation }} -->
|
||||
{{ errors.rights.join(', ') }}
|
||||
<div class="text-red-400 text-sm" v-if="form.errors.rights">
|
||||
{{ formatError(form.errors.rights) }}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div v-if="formStep == 2">
|
||||
<!-- <CardBox title="Performance" :icon="mdiFinance" :header-icon="mdiReload" class="mb-6"> -->
|
||||
<div class="flex flex-col md:flex-row">
|
||||
<FormField label="Dataset Type *" help="required: dataset type"
|
||||
:class="{ 'text-red-400': form.errors.type }" class="w-full mx-2 flex-1">
|
||||
<FormField label="Dataset Type *" help="required: dataset type" :errors="form.errors.type"
|
||||
class="w-full mx-2 flex-1">
|
||||
<FormControl required v-model="form.type" :type="'select'"
|
||||
placeholder="-- select type --" :errors="errors.type" :options="doctypes">
|
||||
<div class="text-red-400 text-sm"
|
||||
v-if="form.errors.type && Array.isArray(form.errors.type)">
|
||||
{{ form.errors.type.join(', ') }}
|
||||
</div>
|
||||
placeholder="-- select type --" :errors="form.errors.type" :options="doctypes">
|
||||
</FormControl>
|
||||
</FormField>
|
||||
<!-- <div class="w-full mx-2 flex-1 svelte-1l8159u"></div> -->
|
||||
<!-- Creating Corporation -->
|
||||
<FormField label="Creating Corporation *"
|
||||
:class="{ 'text-red-400': form.errors.creating_corporation }"
|
||||
<FormField label="Creating Corporation *" :errors="form.errors.creating_corporation"
|
||||
class="w-full mx-2 flex-1">
|
||||
<FormControl required v-model="form.creating_corporation" type="text"
|
||||
placeholder="[enter creating corporation]" :is-read-only="true">
|
||||
<div class="text-red-400 text-sm"
|
||||
v-if="form.errors.creating_corporation && Array.isArray(form.errors.creating_corporation)">
|
||||
{{ form.errors.creating_corporation.join(', ') }}
|
||||
</div>
|
||||
</FormControl>
|
||||
</FormField>
|
||||
</div>
|
||||
<!-- <BaseDivider /> -->
|
||||
|
||||
<!-- titles -->
|
||||
<CardBox class="mb-6 shadow" :has-form-data="true" title="Titles" :icon="mdiFinance"
|
||||
:header-icon="mdiPlusCircle" v-on:header-icon-click="addTitle()">
|
||||
<!-- <div class="py-6 border-t border-gray-100 dark:border-slate-800"> -->
|
||||
<div v-if="form.errors.titles"
|
||||
class="mx-2 mb-4 p-3 bg-red-100 border-l-4 border-red-500 text-red-700 text-sm">
|
||||
{{ formatError(form.errors.titles) }}
|
||||
</div>
|
||||
<div class="flex flex-col md:flex-row">
|
||||
<FormField label="Main Title *" help="required: main title"
|
||||
:class="{ 'text-red-400': form.errors['titles.0.value'] }"
|
||||
class="w-full mx-2 flex-1">
|
||||
:errors="form.errors['titles.0.value']" class="w-full mx-2 flex-1">
|
||||
<FormControl required v-model="form.titles[0].value" type="textarea"
|
||||
placeholder="[enter main title]" :show-char-count="true"
|
||||
:max-input-length="255">
|
||||
<div class="text-red-400 text-sm"
|
||||
v-if="form.errors['titles.0.value'] && Array.isArray(form.errors['titles.0.value'])">
|
||||
{{ form.errors['titles.0.value'].join(', ') }}
|
||||
</div>
|
||||
</FormControl>
|
||||
</FormField>
|
||||
<FormField label="Main Title Language*" help="required: main title language"
|
||||
:class="{ 'text-red-400': form.errors['titles.0.language'] }"
|
||||
class="w-full mx-2 flex-1">
|
||||
:errors="form.errors['titles.0.language']" class="w-full mx-2 flex-1">
|
||||
<FormControl required v-model="form.titles[0].language" type="text"
|
||||
:is-read-only="true">
|
||||
<div class="text-red-400 text-sm"
|
||||
v-if="form.errors['titles.0.language'] && Array.isArray(form.errors['titles.0.language'])">
|
||||
{{ form.errors['titles.0.language'].join(', ') }}
|
||||
</div>
|
||||
</FormControl>
|
||||
</FormField>
|
||||
</div>
|
||||
<label v-if="form.titles.length > 1">additional titles </label>
|
||||
<!-- <BaseButton :icon="mdiPlusCircle" @click.prevent="addTitle()" color="modern" rounded-full small /> -->
|
||||
<div v-if="form.titles.length > 1">
|
||||
<div v-for="(item, index) in form.titles">
|
||||
<div class="flex flex-col md:flex-row" v-if="item.type != 'Main'">
|
||||
|
|
@ -670,75 +561,53 @@ Removes a selected keyword
|
|||
<BaseButton :icon="mdiMinusCircle" class="mt-1"
|
||||
@click.prevent="removeTitle(index)" color="modern" small />
|
||||
</FormField>
|
||||
<FormField label="Title Value *"
|
||||
:class="{ 'text-red-400': form.errors[`titles.${index}.value`] }"
|
||||
<FormField label="Title Value *" :errors="form.errors[`titles.${index}.value`]"
|
||||
class="w-full mx-2 flex-1">
|
||||
<FormControl required v-model="form.titles[index].value" type="textarea"
|
||||
placeholder="[enter main title]">
|
||||
<div class="text-red-400 text-sm"
|
||||
v-if="form.errors[`titles.${index}.value`]">
|
||||
{{ form.errors[`titles.${index}.value`].join(', ') }}
|
||||
</div>
|
||||
</FormControl>
|
||||
</FormField>
|
||||
<FormField label="Title Type*"
|
||||
:class="{ 'text-red-400': form.errors[`titles.${index}.type`] }"
|
||||
<FormField label="Title Type*" :errors="form.errors[`titles.${index}.type`]"
|
||||
class="w-full mx-2 flex-1">
|
||||
<FormControl required v-model="form.titles[index].type" type="select"
|
||||
:options="titletypes" placeholder="[select title type]">
|
||||
<div class="text-red-400 text-sm"
|
||||
v-if="Array.isArray(form.errors[`titles.${index}.type`])">
|
||||
{{ form.errors[`titles.${index}.type`].join(', ') }}
|
||||
</div>
|
||||
</FormControl>
|
||||
</FormField>
|
||||
<FormField label="Title Language*"
|
||||
:class="{ 'text-red-400': form.errors[`titles.${index}.language`] }"
|
||||
:errors="form.errors[`titles.${index}.language`]"
|
||||
class="w-full mx-2 flex-1">
|
||||
<FormControl required v-model="form.titles[index].language" type="select"
|
||||
:options="{ de: 'de', en: 'en' }" placeholder="[select title language]">
|
||||
<div class="text-red-400 text-sm"
|
||||
v-if="form.errors[`titles.${index}.language`]">
|
||||
{{ form.errors[`titles.${index}.language`].join(', ') }}
|
||||
</div>
|
||||
</FormControl>
|
||||
</FormField>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<!-- </div> -->
|
||||
</CardBox>
|
||||
|
||||
<!-- Descriptions -->
|
||||
<CardBox :icon="mdiImageText" class="mb-6 shadow" :has-form-data="true" title="Descriptions"
|
||||
:header-icon="mdiPlusCircle" v-on:header-icon-click="addDescription()">
|
||||
<div v-if="form.errors.descriptions"
|
||||
class="mx-2 mb-4 p-3 bg-red-100 border-l-4 border-red-500 text-red-700 text-sm">
|
||||
{{ formatError(form.errors.descriptions) }}
|
||||
</div>
|
||||
<div class="flex flex-col md:flex-row">
|
||||
<FormField label="Main Abstract *" help="required: main abstract"
|
||||
:class="{ 'text-red-400': form.errors['descriptions.0.value'] }"
|
||||
class="w-full mx-2 flex-1">
|
||||
:errors="form.errors['descriptions.0.value']" class="w-full mx-2 flex-1">
|
||||
<FormControl required v-model="form.descriptions[0].value" type="textarea"
|
||||
placeholder="[enter main abstract]" :show-char-count="true"
|
||||
:max-input-length="2500">
|
||||
<div class="text-red-400 text-sm"
|
||||
v-if="form.errors['descriptions.0.value'] && Array.isArray(form.errors['descriptions.0.value'])">
|
||||
{{ form.errors['descriptions.0.value'].join(', ') }}
|
||||
</div>
|
||||
</FormControl>
|
||||
</FormField>
|
||||
<FormField label="Main Description Language*" help="required: main abstract language"
|
||||
:class="{ 'text-red-400': form.errors['descriptions.0.language'] }"
|
||||
class="w-full mx-2 flex-1">
|
||||
:errors="form.errors['descriptions.0.language']" class="w-full mx-2 flex-1">
|
||||
<FormControl required v-model="form.descriptions[0].language" type="text"
|
||||
:is-read-only="true">
|
||||
<div class="text-red-400 text-sm" v-if="form.errors['descriptions.0.value'] && Array.isArray(form.errors['descriptions.0.language'])
|
||||
">
|
||||
{{ form.errors['descriptions.0.language'].join(', ') }}
|
||||
</div>
|
||||
</FormControl>
|
||||
</FormField>
|
||||
</div>
|
||||
<label v-if="form.descriptions.length > 1">additional descriptions: </label>
|
||||
<!-- <BaseButton :icon="mdiPlusCircle" @click.prevent="addTitle()" color="modern" rounded-full small /> -->
|
||||
<div v-if="form.descriptions.length > 1">
|
||||
<div v-for="(item, index) in form.descriptions">
|
||||
<div class="flex flex-col md:flex-row" v-if="item.type != 'Abstract'">
|
||||
|
|
@ -747,40 +616,26 @@ Removes a selected keyword
|
|||
@click.prevent="removeDescription(index)" color="modern" small />
|
||||
</FormField>
|
||||
<FormField label="Description Value *"
|
||||
:class="{ 'text-red-400': form.errors[`descriptions.${index}.value`] }"
|
||||
:errors="form.errors[`descriptions.${index}.value`]"
|
||||
class="w-full mx-2 flex-1">
|
||||
<FormControl required v-model="form.descriptions[index].value" type="textarea"
|
||||
placeholder="[enter additional description]" :show-char-count="true"
|
||||
:max-input-length="2500">
|
||||
<div class="text-red-400 text-sm" v-if="form.errors[`descriptions.${index}.value`] &&
|
||||
Array.isArray(form.errors[`descriptions.${index}.value`])
|
||||
">
|
||||
{{ form.errors[`descriptions.${index}.value`].join(', ') }}
|
||||
</div>
|
||||
<FormControl required v-model="form.descriptions[index].value"
|
||||
type="textarea" placeholder="[enter additional description]"
|
||||
:show-char-count="true" :max-input-length="2500">
|
||||
</FormControl>
|
||||
</FormField>
|
||||
<FormField label="Description Type *"
|
||||
:class="{ 'text-red-400': form.errors[`descriptions.${index}.type`] }"
|
||||
:errors="form.errors[`descriptions.${index}.type`]"
|
||||
class="w-full mx-2 flex-1">
|
||||
<FormControl required v-model="form.descriptions[index].type" type="select"
|
||||
:options="descriptiontypes" placeholder="[select description type]">
|
||||
<div class="text-red-400 text-sm" v-if="form.errors[`descriptions.${index}.type`] &&
|
||||
Array.isArray(form.errors[`descriptions.${index}.type`])
|
||||
">
|
||||
{{ form.errors[`descriptions.${index}.type`].join(', ') }}
|
||||
</div>
|
||||
</FormControl>
|
||||
</FormField>
|
||||
<FormField label="Description Language*"
|
||||
:class="{ 'text-red-400': form.errors[`titdescriptionsles.${index}.language`] }"
|
||||
:errors="form.errors[`descriptions.${index}.language`]"
|
||||
class="w-full mx-2 flex-1">
|
||||
<FormControl required v-model="form.descriptions[index].language"
|
||||
type="select" :options="{ de: 'de', en: 'en' }"
|
||||
placeholder="[select title language]">
|
||||
<div class="text-red-400 text-sm"
|
||||
v-if="form.errors && Array.isArray(form.errors[`descriptions.${index}.language`])">
|
||||
{{ form.errors[`descriptions.${index}.language`].join(', ') }}
|
||||
</div>
|
||||
</FormControl>
|
||||
</FormField>
|
||||
</div>
|
||||
|
|
@ -789,15 +644,17 @@ Removes a selected keyword
|
|||
</CardBox>
|
||||
|
||||
<!-- authors -->
|
||||
<CardBox class="mb-6 shadow" has-table title="Creators" :icon="mdiBookOpenPageVariant" :show-header-icon="false">
|
||||
<CardBox class="mb-6 shadow" has-table title="Creators" :icon="mdiBookOpenPageVariant"
|
||||
:show-header-icon="false">
|
||||
<div v-if="form.errors.authors"
|
||||
class="mx-2 mb-4 p-3 bg-red-100 border-l-4 border-red-500 text-red-700 text-sm">
|
||||
{{ formatError(form.errors.authors) }}
|
||||
</div>
|
||||
<SearchAutocomplete source="/api/persons" :response-property="'first_name'"
|
||||
placeholder="search in person table...." v-on:person="onAddAuthor"></SearchAutocomplete>
|
||||
|
||||
<TablePersons :errors="form.errors" :persons="form.authors" :relation="'authors'"
|
||||
v-if="form.authors.length > 0" />
|
||||
<div class="text-red-400 text-sm" v-if="form.errors.authors && Array.isArray(form.errors.authors)">
|
||||
{{ form.errors.authors.join(', ') }}
|
||||
</div>
|
||||
<div class="w-full md:w-1/2">
|
||||
<label class="block" for="additionalCreators">Add additional creator(s) if creator is
|
||||
not in database</label>
|
||||
|
|
@ -807,7 +664,12 @@ Removes a selected keyword
|
|||
</CardBox>
|
||||
|
||||
<!-- contributors -->
|
||||
<CardBox class="mb-6 shadow" has-table title="Contributors" :icon="mdiBookOpenPageVariant" :show-header-icon="false">
|
||||
<CardBox class="mb-6 shadow" has-table title="Contributors" :icon="mdiBookOpenPageVariant"
|
||||
:show-header-icon="false">
|
||||
<div v-if="form.errors.contributors"
|
||||
class="mx-2 mb-4 p-3 bg-red-100 border-l-4 border-red-500 text-red-700 text-sm">
|
||||
{{ formatError(form.errors.contributors) }}
|
||||
</div>
|
||||
<SearchAutocomplete source="/api/persons" :response-property="'first_name'"
|
||||
placeholder="search in person table...." v-on:person="onAddContributor">
|
||||
</SearchAutocomplete>
|
||||
|
|
@ -815,10 +677,6 @@ Removes a selected keyword
|
|||
<TablePersons :persons="form.contributors" :relation="'contributors'"
|
||||
v-if="form.contributors.length > 0" :contributortypes="contributorTypes"
|
||||
:errors="form.errors" />
|
||||
<div class="text-red-400 text-sm"
|
||||
v-if="form.errors.contributors && Array.isArray(form.errors.contributors)">
|
||||
{{ form.errors.contributors.join(', ') }}
|
||||
</div>
|
||||
<div class="w-full md:w-1/2">
|
||||
<label class="block" for="additionalCreators">Add additional contributor(s) if
|
||||
contributor is not in database</label>
|
||||
|
|
@ -827,89 +685,65 @@ Removes a selected keyword
|
|||
</div>
|
||||
</CardBox>
|
||||
</div>
|
||||
|
||||
<!-- <label>To Do: Recommendet</label> -->
|
||||
<!-- <label>To Do: Recommendet</label> -->
|
||||
<div v-if="formStep == 3">
|
||||
<div class="flex flex-col md:flex-row">
|
||||
<FormField label="Project.." help="project is optional"
|
||||
:class="{ 'text-red-400': errors.project_id }" class="w-full mx-2 flex-1">
|
||||
:errors="form.errors.project_id" class="w-full mx-2 flex-1">
|
||||
<FormControl required v-model="form.project_id" :type="'select'"
|
||||
placeholder="[Select Project]" :errors="form.errors.project_id" :options="projects">
|
||||
<div class="text-red-400 text-sm" v-if="form.errors.project_id">
|
||||
{{ form.errors.project_id.join(', ') }}
|
||||
</div>
|
||||
</FormControl>
|
||||
</FormField>
|
||||
|
||||
<FormField label="Embargo Date.." help="embargo date is optional"
|
||||
:class="{ 'text-red-400': errors.embargo_date }" class="w-full mx-2 flex-1">
|
||||
:errors="form.errors.embargo_date" class="w-full mx-2 flex-1">
|
||||
<FormControl required v-model="form.embargo_date" :type="'date'"
|
||||
placeholder="date('y-m-d')" :errors="form.errors.embargo_date">
|
||||
<div class="text-red-400 text-sm" v-if="form.errors.embargo_date">
|
||||
{{ form.errors.embargo_date.join(', ') }}
|
||||
</div>
|
||||
</FormControl>
|
||||
</FormField>
|
||||
</div>
|
||||
<CardBox class="mb-6 shadow" has-table title="Geo Location" :icon="mdiEarthPlus" :show-header-icon="false">
|
||||
<!-- @onMapInitialized="onMapInitialized" -->
|
||||
<!-- v-bind-event="{ mapId, name: mapId }" -->
|
||||
<CardBox class="mb-6 shadow" has-table title="Geo Location" :icon="mdiEarthPlus"
|
||||
:show-header-icon="false">
|
||||
<MapComponent :mapOptions="mapOptions" :baseMaps="baseMaps" :fitBounds="fitBounds"
|
||||
:coverage="form.coverage" :mapId="mapId"
|
||||
v-bind-event:onMapInitializedEvent="onMapInitialized"></MapComponent>
|
||||
<!-- <label v-bind-event="{ for: mapId }" /> -->
|
||||
|
||||
<div class="flex flex-col md:flex-row">
|
||||
<!-- x min and max -->
|
||||
<FormField label="Coverage X Min"
|
||||
:class="{ 'text-red-400': form.errors['coverage.x_min'] }"
|
||||
:errors="form.errors['coverage.x_min']"
|
||||
class="w-full mx-2 flex-1">
|
||||
<FormControl required v-model="form.coverage.x_min" type="text" inputmode="numeric"
|
||||
pattern="\d*" placeholder="[enter x_min]">
|
||||
<div class="text-red-400 text-sm"
|
||||
v-if="form.errors['coverage.x_min'] && Array.isArray(form.errors['coverage.x_min'])">
|
||||
{{ form.errors['coverage.x_min'].join(', ') }}
|
||||
</div>
|
||||
</FormControl>
|
||||
</FormField>
|
||||
<FormField label="Coverage X Max"
|
||||
:class="{ 'text-red-400': form.errors['coverage.x_max'] }"
|
||||
:errors="form.errors['coverage.x_max']"
|
||||
class="w-full mx-2 flex-1">
|
||||
<FormControl required v-model="form.coverage.x_max" type="text"
|
||||
placeholder="[enter x_max]">
|
||||
<div class="text-red-400 text-sm"
|
||||
v-if="form.errors['coverage.x_max'] && Array.isArray(form.errors['coverage.x_max'])">
|
||||
{{ form.errors['coverage.x_max'].join(', ') }}
|
||||
</div>
|
||||
</FormControl>
|
||||
</FormField>
|
||||
<!-- y min and max -->
|
||||
<FormField label="Coverage Y Min"
|
||||
:class="{ 'text-red-400': form.errors['coverage.y_min'] }"
|
||||
:errors="form.errors['coverage.y_min']"
|
||||
class="w-full mx-2 flex-1">
|
||||
<FormControl required v-model="form.coverage.y_min" type="text"
|
||||
placeholder="[enter y_min]">
|
||||
<div class="text-red-400 text-sm"
|
||||
v-if="form.errors['coverage.y_min'] && Array.isArray(form.errors['coverage.y_min'])">
|
||||
{{ form.errors['coverage.y_min'].join(', ') }}
|
||||
</div>
|
||||
</FormControl>
|
||||
</FormField>
|
||||
<FormField label="Coverage Y Max"
|
||||
:class="{ 'text-red-400': form.errors['coverage.y_max'] }"
|
||||
:errors="form.errors['coverage.y_max']"
|
||||
class="w-full mx-2 flex-1">
|
||||
<FormControl required v-model="form.coverage.y_max" type="text"
|
||||
placeholder="[enter y_max]">
|
||||
<div class="text-red-400 text-sm"
|
||||
v-if="form.errors['coverage.y_max'] && Array.isArray(form.errors['coverage.y_max'])">
|
||||
{{ form.errors['coverage.y_max'].join(', ') }}
|
||||
</div>
|
||||
</FormControl>
|
||||
</FormField>
|
||||
</div>
|
||||
</CardBox>
|
||||
|
||||
<CardBox class="mb-6 shadow" has-table title="Coverage Information" :icon="mdiEarthPlus" :show-header-icon="false">
|
||||
<CardBox class="mb-6 shadow" has-table title="Coverage Information" :icon="mdiEarthPlus"
|
||||
:show-header-icon="false">
|
||||
<!-- elevation menu -->
|
||||
<div class="flex flex-col md:flex-row mb-3 space-y-2 md:space-y-0 md:space-x-4">
|
||||
<label for="elevation-option-one" class="pure-radio mb-2 md:mb-0">
|
||||
|
|
@ -928,36 +762,21 @@ Removes a selected keyword
|
|||
</div>
|
||||
<div class="flex flex-col md:flex-row">
|
||||
<FormField v-if="elevation === 'absolut'" label="elevation absolut"
|
||||
:class="{ 'text-red-400': form.errors['coverage.elevation_absolut'] }"
|
||||
class="w-full mx-2 flex-1">
|
||||
:errors="form.errors['coverage.elevation_absolut']" class="w-full mx-2 flex-1">
|
||||
<FormControl required v-model="form.coverage.elevation_absolut" type="text"
|
||||
placeholder="[enter elevation_absolut]">
|
||||
<div class="text-red-400 text-sm"
|
||||
v-if="Array.isArray(form.errors['coverage.elevation_absolut'])">
|
||||
{{ form.errors['coverage.elevation_absolut'].join(', ') }}
|
||||
</div>
|
||||
</FormControl>
|
||||
</FormField>
|
||||
<FormField v-if="elevation === 'range'" label="elevation min"
|
||||
:class="{ 'text-red-400': form.errors['coverage.elevation_min'] }"
|
||||
class="w-full mx-2 flex-1">
|
||||
:errors="form.errors['coverage.elevation_min']" class="w-full mx-2 flex-1">
|
||||
<FormControl required v-model="form.coverage.elevation_min" type="text"
|
||||
placeholder="[enter elevation_min]">
|
||||
<div class="text-red-400 text-sm"
|
||||
v-if="Array.isArray(form.errors['coverage.elevation_min'])">
|
||||
{{ form.errors['coverage.elevation_min'].join(', ') }}
|
||||
</div>
|
||||
</FormControl>
|
||||
</FormField>
|
||||
<FormField v-if="elevation === 'range'" label="elevation max"
|
||||
:class="{ 'text-red-400': form.errors['coverage.elevation_max'] }"
|
||||
class="w-full mx-2 flex-1">
|
||||
:errors="form.errors['coverage.elevation_max']" class="w-full mx-2 flex-1">
|
||||
<FormControl required v-model="form.coverage.elevation_max" type="text"
|
||||
placeholder="[enter elevation_max]">
|
||||
<div class="text-red-400 text-sm"
|
||||
v-if="Array.isArray(form.errors['coverage.elevation_max'])">
|
||||
{{ form.errors['coverage.elevation_max'].join(', ') }}
|
||||
</div>
|
||||
</FormControl>
|
||||
</FormField>
|
||||
</div>
|
||||
|
|
@ -979,36 +798,21 @@ Removes a selected keyword
|
|||
</div>
|
||||
<div class="flex flex-col md:flex-row">
|
||||
<FormField v-if="depth === 'absolut'" label="depth absolut"
|
||||
:class="{ 'text-red-400': form.errors['coverage.depth_absolut'] }"
|
||||
class="w-full mx-2 flex-1">
|
||||
:errors="form.errors['coverage.depth_absolut']" class="w-full mx-2 flex-1">
|
||||
<FormControl required v-model="form.coverage.depth_absolut" type="text"
|
||||
placeholder="[enter depth_absolut]">
|
||||
<div class="text-red-400 text-sm"
|
||||
v-if="Array.isArray(form.errors['coverage.depth_absolut'])">
|
||||
{{ form.errors['coverage.depth_absolut'].join(', ') }}
|
||||
</div>
|
||||
</FormControl>
|
||||
</FormField>
|
||||
<FormField v-if="depth === 'range'" label="depth min"
|
||||
:class="{ 'text-red-400': form.errors['coverage.depth_min'] }"
|
||||
class="w-full mx-2 flex-1">
|
||||
:errors="form.errors['coverage.depth_min']" class="w-full mx-2 flex-1">
|
||||
<FormControl required v-model="form.coverage.depth_min" type="text"
|
||||
placeholder="[enter depth_min]">
|
||||
<div class="text-red-400 text-sm"
|
||||
v-if="Array.isArray(form.errors['coverage.depth_min'])">
|
||||
{{ form.errors['coverage.depth_min'].join(', ') }}
|
||||
</div>
|
||||
</FormControl>
|
||||
</FormField>
|
||||
<FormField v-if="depth === 'range'" label="depth max"
|
||||
:class="{ 'text-red-400': form.errors['coverage.depth_max'] }"
|
||||
class="w-full mx-2 flex-1">
|
||||
:errors="form.errors['coverage.depth_max']" class="w-full mx-2 flex-1">
|
||||
<FormControl required v-model="form.coverage.depth_max" type="text"
|
||||
placeholder="[enter depth_max]">
|
||||
<div class="text-red-400 text-sm"
|
||||
v-if="Array.isArray(form.errors['coverage.depth_max'])">
|
||||
{{ form.errors['coverage.depth_max'].join(', ') }}
|
||||
</div>
|
||||
</FormControl>
|
||||
</FormField>
|
||||
</div>
|
||||
|
|
@ -1030,36 +834,24 @@ Removes a selected keyword
|
|||
</div>
|
||||
<div class="flex flex-col md:flex-row">
|
||||
<FormField v-if="time === 'absolut'" label="time absolut"
|
||||
:class="{ 'text-red-400': form.errors['coverage.time_absolut'] }"
|
||||
:errors="form.errors['coverage.time_absolut']"
|
||||
class="w-full mx-2 flex-1">
|
||||
<FormControl required v-model="form.coverage.time_absolut" type="datetime-local"
|
||||
placeholder="[enter time_absolut]">
|
||||
<div class="text-red-400 text-sm"
|
||||
v-if="Array.isArray(form.errors['coverage.time_absolut'])">
|
||||
{{ form.errors['coverage.time_absolut'].join(', ') }}
|
||||
</div>
|
||||
</FormControl>
|
||||
</FormField>
|
||||
<FormField v-if="time === 'range'" label="time min"
|
||||
:class="{ 'text-red-400': form.errors['coverage.time_min'] }"
|
||||
:errors="form.errors['coverage.time_min']"
|
||||
class="w-full mx-2 flex-1">
|
||||
<FormControl required v-model="form.coverage.time_min" type="datetime-local"
|
||||
placeholder="[enter time_min]">
|
||||
<div class="text-red-400 text-sm"
|
||||
v-if="Array.isArray(form.errors['coverage.time_min'])">
|
||||
{{ form.errors['coverage.time_min'].join(', ') }}
|
||||
</div>
|
||||
</FormControl>
|
||||
</FormField>
|
||||
<FormField v-if="time === 'range'" label="time max"
|
||||
:class="{ 'text-red-400': form.errors['coverage.time_max'] }"
|
||||
:errors="form.errors['coverage.time_max']"
|
||||
class="w-full mx-2 flex-1">
|
||||
<FormControl required v-model="form.coverage.time_max" type="datetime-local"
|
||||
placeholder="[enter time_max]">
|
||||
<div class="text-red-400 text-sm"
|
||||
v-if="Array.isArray(form.errors['coverage.time_max'])">
|
||||
{{ form.errors['coverage.time_max'].join(', ') }}
|
||||
</div>
|
||||
</FormControl>
|
||||
</FormField>
|
||||
</div>
|
||||
|
|
@ -1085,53 +877,44 @@ Removes a selected keyword
|
|||
<tbody>
|
||||
<tr v-for="(item, index) in form.references">
|
||||
<td data-label="Reference Value">
|
||||
<!-- <input name="Reference Value" class="form-control"
|
||||
placeholder="[VALUE]" v-model="item.value" /> -->
|
||||
<FormControl required v-model="item.value" :type="'text'"
|
||||
:placeholder="getPlaceholder(form.references[index].type)"
|
||||
:errors="form.errors.embargo_date">
|
||||
:errors="form.errors[`references.${index}.value`]">
|
||||
<div class="text-red-400 text-sm"
|
||||
v-if="form.errors[`references.${index}.value`] && Array.isArray(form.errors[`references.${index}.value`])">
|
||||
{{ form.errors[`references.${index}.value`].join(', ') }}
|
||||
v-if="form.errors[`references.${index}.value`]">
|
||||
{{ formatError(form.errors[`references.${index}.value`]) }}
|
||||
</div>
|
||||
</FormControl>
|
||||
|
||||
</td>
|
||||
<td>
|
||||
<FormControl required v-model="form.references[index].type" type="select"
|
||||
:options="referenceIdentifierTypes" placeholder="[type]">
|
||||
<div class="text-red-400 text-sm"
|
||||
v-if="Array.isArray(form.errors[`references.${index}.type`])">
|
||||
{{ form.errors[`references.${index}.type`].join(', ') }}
|
||||
v-if="form.errors[`references.${index}.type`]">
|
||||
{{ formatError(form.errors[`references.${index}.type`]) }}
|
||||
</div>
|
||||
</FormControl>
|
||||
</td>
|
||||
|
||||
<td>
|
||||
<!-- {!! Form::select('Reference[Relation]', $relationTypes, null,
|
||||
['placeholder' => '[relationType]', 'v-model' => 'item.relation',
|
||||
'data-vv-scope' => 'step-2'])
|
||||
!!} -->
|
||||
<FormControl required v-model="form.references[index].relation"
|
||||
type="select" :options="relationTypes" placeholder="[relation type]">
|
||||
<div class="text-red-400 text-sm"
|
||||
v-if="Array.isArray(form.errors[`references.${index}.relation`])">
|
||||
{{ form.errors[`references.${index}.relation`].join(', ') }}
|
||||
v-if="form.errors[`references.${index}.relation`]">
|
||||
{{ formatError(form.errors[`references.${index}.relation`]) }}
|
||||
</div>
|
||||
</FormControl>
|
||||
</td>
|
||||
<td data-label="Reference Label">
|
||||
<!-- <input name="Reference Label" class="form-control" v-model="item.label" /> -->
|
||||
<FormControl required v-model="form.references[index].label" type="text"
|
||||
placeholder="[reference label]">
|
||||
<div class="text-red-400 text-sm"
|
||||
v-if="form.errors[`references.${index}.label`] && Array.isArray(form.errors[`references.${index}.label`])">
|
||||
{{ form.errors[`references.${index}.label`].join(', ') }}
|
||||
v-if="form.errors[`references.${index}.label`]">
|
||||
{{ formatError(form.errors[`references.${index}.label`]) }}
|
||||
</div>
|
||||
</FormControl>
|
||||
</td>
|
||||
<td class="before:hidden lg:w-1 whitespace-nowrap">
|
||||
<!-- <BaseButton color="info" :icon="mdiEye" small @click="isModalActive = true" /> -->
|
||||
<BaseButton color="danger" :icon="mdiTrashCan" small
|
||||
@click.prevent="removeReference(index)" />
|
||||
</td>
|
||||
|
|
@ -1143,40 +926,26 @@ Removes a selected keyword
|
|||
|
||||
<CardBox class="mb-6 shadow" has-table title="Dataset Keywords" :icon="mdiEarthPlus"
|
||||
:header-icon="mdiPlusCircle" v-on:header-icon-click="addKeyword">
|
||||
<!-- <ul>
|
||||
<li v-for="(subject, index) in form.subjects" :key="index">
|
||||
{{ subject.value }} <BaseButton color="danger" :icon="mdiTrashCan" small @click.prevent="removeKeyword(index)" />
|
||||
</li>
|
||||
</ul> -->
|
||||
<div v-if="form.errors.subjects"
|
||||
class="mx-2 mb-4 p-3 bg-red-100 border-l-4 border-red-500 text-red-700 text-sm">
|
||||
{{ formatError(form.errors.subjects) }}
|
||||
</div>
|
||||
<TableKeywords :keywords="form.subjects" :errors="form.errors" :subjectTypes="subjectTypes"
|
||||
v-if="form.subjects.length > 0" />
|
||||
</CardBox>
|
||||
</div>
|
||||
|
||||
<div v-if="formStep == 4">
|
||||
<!-- <progress v-if="form.progress" :value="form.progress.percentage" max="100">
|
||||
{{ form.progress.percentage }}%
|
||||
</progress> -->
|
||||
<!-- <p v-if="isSaving">Uploading @{{ fileCount }} files...</p> -->
|
||||
<!-- <div class="dropbox">
|
||||
<input type="file" multiple name="files" @change="onChangeFile" class="input-file" />
|
||||
<p>
|
||||
Drag your file(s) here to begin<br />
|
||||
or click to browse
|
||||
</p>
|
||||
</div> -->
|
||||
|
||||
<FileUploadComponent :files="form.files"></FileUploadComponent>
|
||||
|
||||
<div class="text-red-400 text-sm"
|
||||
v-if="form.errors['file'] && Array.isArray(form.errors['file'])">
|
||||
{{ form.errors['file'].join(', ') }}
|
||||
<div class="text-red-400 text-sm" v-if="form.errors['file']">
|
||||
{{ formatError(form.errors['file']) }}
|
||||
</div>
|
||||
<div class="text-red-400 text-sm"
|
||||
v-if="form.errors['upload.label'] && Array.isArray(form.errors['upload.label'])">
|
||||
{{ form.errors['upload.label'].join(', ') }}
|
||||
<div class="text-red-400 text-sm" v-if="form.errors['upload.label']">
|
||||
{{ formatError(form.errors['upload.label']) }}
|
||||
</div>
|
||||
<!-- <label v-if="form.upload">{{ form.upload?.label }}</label> -->
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
|
@ -1216,15 +985,6 @@ Removes a selected keyword
|
|||
<path class="opacity-75" fill="currentColor" d="M12 2a10 10 0 0110 10h-4a6 6 0 00-6-6V2z"></path>
|
||||
</svg>
|
||||
</div>
|
||||
<!-- <div
|
||||
class="fixed inset-0 flex items-center justify-center bg-gray-500 bg-opacity-50">
|
||||
<svg class="animate-spin h-10 w-10 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="M4 12a8 8 0 0116 0 8 8 0 01-16 0zm2 0a6 6 0 0112 0 6 6 0 01-12 0z"></path>
|
||||
</svg>
|
||||
</div> -->
|
||||
|
||||
</SectionMain>
|
||||
</LayoutAuthenticated>
|
||||
|
|
@ -1269,4 +1029,4 @@ span.remove-file {
|
|||
cursor: pointer;
|
||||
/* float: right; */
|
||||
}
|
||||
</style>
|
||||
</style>
|
||||
|
|
@ -72,7 +72,7 @@
|
|||
<FormField
|
||||
label="Language *"
|
||||
help="required: select dataset main language"
|
||||
:class="{ 'text-red-400': form.errors.language }"
|
||||
:errors="form.errors.language"
|
||||
>
|
||||
<FormControl
|
||||
required
|
||||
|
|
@ -81,15 +81,12 @@
|
|||
placeholder="[Enter Language]"
|
||||
:errors="form.errors.language"
|
||||
:options="{ de: 'de', en: 'en' }"
|
||||
>
|
||||
<div class="text-red-400 text-sm" v-if="form.errors.language">
|
||||
{{ form.errors.language.join(', ') }}
|
||||
</div>
|
||||
>
|
||||
</FormControl>
|
||||
</FormField>
|
||||
|
||||
<!-- (3) dataset_type -->
|
||||
<FormField label="Dataset Type *" help="required: dataset type" :class="{ 'text-red-400': form.errors.type }">
|
||||
<FormField label="Dataset Type *" help="required: dataset type" :errors="form.errors.type">
|
||||
<FormControl
|
||||
required
|
||||
v-model="form.type"
|
||||
|
|
@ -97,16 +94,13 @@
|
|||
placeholder="-- select type --"
|
||||
:errors="form.errors.type"
|
||||
:options="doctypes"
|
||||
>
|
||||
<div class="text-red-400 text-sm" v-if="form.errors.type && Array.isArray(form.errors.type)">
|
||||
{{ form.errors.type.join(', ') }}
|
||||
</div>
|
||||
>
|
||||
</FormControl>
|
||||
</FormField>
|
||||
<!-- (4) creating_corporation -->
|
||||
<FormField
|
||||
label="Creating Corporation *"
|
||||
:class="{ 'text-red-400': form.errors.creating_corporation }"
|
||||
label="Creating Corporation *"
|
||||
:errors="form.errors.creating_corporation"
|
||||
class="w-full mx-2 flex-1"
|
||||
>
|
||||
<FormControl
|
||||
|
|
@ -115,13 +109,7 @@
|
|||
type="text"
|
||||
placeholder="[enter creating corporation]"
|
||||
:is-read-only="true"
|
||||
>
|
||||
<div
|
||||
class="text-red-400 text-sm"
|
||||
v-if="form.errors.creating_corporation && Array.isArray(form.errors.creating_corporation)"
|
||||
>
|
||||
{{ form.errors.creating_corporation.join(', ') }}
|
||||
</div>
|
||||
>
|
||||
</FormControl>
|
||||
</FormField>
|
||||
</div>
|
||||
|
|
@ -152,6 +140,7 @@
|
|||
<span class="w-8 h-8 rounded-full bg-purple-500 text-white flex items-center justify-center text-sm">3</span>
|
||||
Titles
|
||||
</h2>
|
||||
<!-- <pre class="text-xs text-red-500">{{ form.errors }}</pre> -->
|
||||
<!-- (5) titles -->
|
||||
<CardBox
|
||||
class="ml-10 shadow-md"
|
||||
|
|
@ -163,9 +152,9 @@
|
|||
<div class="flex flex-col md:flex-row">
|
||||
<FormField
|
||||
label="Main Title *"
|
||||
help="required: main title"
|
||||
:class="{ 'text-red-400': form.errors['titles.0.value'] }"
|
||||
help="required: main title"
|
||||
class="w-full mr-1 flex-1"
|
||||
:errors="form.errors[`titles.0.value`]"
|
||||
>
|
||||
<FormControl
|
||||
required
|
||||
|
|
@ -174,28 +163,16 @@
|
|||
placeholder="[enter main title]"
|
||||
:show-char-count="true"
|
||||
:max-input-length="255"
|
||||
>
|
||||
<div
|
||||
class="text-red-400 text-sm"
|
||||
v-if="form.errors['titles.0.value'] && Array.isArray(form.errors['titles.0.value'])"
|
||||
>
|
||||
{{ form.errors['titles.0.value'].join(', ') }}
|
||||
</div>
|
||||
</FormControl>
|
||||
>
|
||||
</FormControl>
|
||||
</FormField>
|
||||
<FormField
|
||||
label="Main Title Language*"
|
||||
help="required: main title language"
|
||||
:class="{ 'text-red-400': form.errors['titles.0.language'] }"
|
||||
:errors="form.errors['titles.0.language']"
|
||||
class="w-full ml-1 flex-1"
|
||||
>
|
||||
<FormControl required v-model="form.titles[0].language" type="text" :is-read-only="true">
|
||||
<div
|
||||
class="text-red-400 text-sm"
|
||||
v-if="form.errors['titles.0.language'] && Array.isArray(form.errors['titles.0.language'])"
|
||||
>
|
||||
{{ form.errors['titles.0.language'].join(', ') }}
|
||||
</div>
|
||||
<FormControl required v-model="form.titles[0].language" type="text" :is-read-only="true">
|
||||
</FormControl>
|
||||
</FormField>
|
||||
</div>
|
||||
|
|
@ -224,7 +201,7 @@
|
|||
placeholder="[enter main title]"
|
||||
>
|
||||
<div class="text-red-400 text-sm" v-if="form.errors[`titles.${index}.value`]">
|
||||
{{ form.errors[`titles.${index}.value`].join(', ') }}
|
||||
{{ formatError(form.errors[`titles.${index}.value`]) }}
|
||||
</div>
|
||||
</FormControl>
|
||||
</td>
|
||||
|
|
@ -236,8 +213,8 @@
|
|||
:options="titletypes"
|
||||
placeholder="[select title type]"
|
||||
>
|
||||
<div class="text-red-400 text-sm" v-if="Array.isArray(form.errors[`titles.${index}.type`])">
|
||||
{{ form.errors[`titles.${index}.type`].join(', ') }}
|
||||
<div class="text-red-400 text-sm" v-if="form.errors[`titles.${index}.type`]">
|
||||
{{ formatError(form.errors[`titles.${index}.type`]) }}
|
||||
</div>
|
||||
</FormControl>
|
||||
</td>
|
||||
|
|
@ -250,7 +227,7 @@
|
|||
placeholder="[select title language]"
|
||||
>
|
||||
<div class="text-red-400 text-sm" v-if="form.errors[`titles.${index}.language`]">
|
||||
{{ form.errors[`titles.${index}.language`].join(', ') }}
|
||||
{{ formatError(form.errors[`titles.${index}.language`]) }}
|
||||
</div>
|
||||
</FormControl>
|
||||
</td>
|
||||
|
|
@ -293,7 +270,7 @@
|
|||
<FormField
|
||||
label="Main Abstract *"
|
||||
help="required: main abstract"
|
||||
:class="{ 'text-red-400': form.errors['descriptions.0.value'] }"
|
||||
:errors="form.errors['descriptions.0.value']"
|
||||
class="w-full mr-1 flex-1"
|
||||
>
|
||||
<FormControl
|
||||
|
|
@ -303,28 +280,16 @@
|
|||
placeholder="[enter main abstract]"
|
||||
:show-char-count="true"
|
||||
:max-input-length="2500"
|
||||
>
|
||||
<div
|
||||
class="text-red-400 text-sm"
|
||||
v-if="form.errors['descriptions.0.value'] && Array.isArray(form.errors['descriptions.0.value'])"
|
||||
>
|
||||
{{ form.errors['descriptions.0.value'].join(', ') }}
|
||||
</div>
|
||||
>
|
||||
</FormControl>
|
||||
</FormField>
|
||||
<FormField
|
||||
label="Main Description Language*"
|
||||
help="required: main abstract language"
|
||||
:class="{ 'text-red-400': form.errors['descriptions.0.language'] }"
|
||||
:errors="form.errors['descriptions.0.language']"
|
||||
class="w-full ml-1 flex-1"
|
||||
>
|
||||
<FormControl required v-model="form.descriptions[0].language" type="text" :is-read-only="true">
|
||||
<div
|
||||
class="text-red-400 text-sm"
|
||||
v-if="form.errors['descriptions.0.value'] && Array.isArray(form.errors['descriptions.0.language'])"
|
||||
>
|
||||
{{ form.errors['descriptions.0.language'].join(', ') }}
|
||||
</div>
|
||||
<FormControl required v-model="form.descriptions[0].language" type="text" :is-read-only="true">
|
||||
</FormControl>
|
||||
</FormField>
|
||||
</div>
|
||||
|
|
@ -350,7 +315,7 @@
|
|||
placeholder="[enter main title]"
|
||||
>
|
||||
<div class="text-red-400 text-sm" v-if="form.errors[`descriptions.${index}.value`]">
|
||||
{{ form.errors[`descriptions.${index}.value`].join(', ') }}
|
||||
{{ formatError(form.errors[`descriptions.${index}.value`]) }}
|
||||
</div>
|
||||
</FormControl>
|
||||
</td>
|
||||
|
|
@ -364,9 +329,9 @@
|
|||
>
|
||||
<div
|
||||
class="text-red-400 text-sm"
|
||||
v-if="Array.isArray(form.errors[`descriptions.${index}.type`])"
|
||||
v-if="form.errors[`descriptions.${index}.type`]"
|
||||
>
|
||||
{{ form.errors[`descriptions.${index}.type`].join(', ') }}
|
||||
{{ formatError(form.errors[`descriptions.${index}.type`]) }}
|
||||
</div>
|
||||
</FormControl>
|
||||
</td>
|
||||
|
|
@ -379,7 +344,7 @@
|
|||
placeholder="[select title language]"
|
||||
>
|
||||
<div class="text-red-400 text-sm" v-if="form.errors[`descriptions.${index}.language`]">
|
||||
{{ form.errors[`descriptions.${index}.language`].join(', ') }}
|
||||
{{ formatError(form.errors[`descriptions.${index}.language`]) }}
|
||||
</div>
|
||||
</FormControl>
|
||||
</td>
|
||||
|
|
@ -462,8 +427,8 @@
|
|||
:errors="form.errors"
|
||||
:relation="'authors'"
|
||||
/>
|
||||
<div class="text-red-400 text-sm" v-if="form.errors.authors && Array.isArray(form.errors.authors)">
|
||||
{{ form.errors.authors.join(', ') }}
|
||||
<div class="text-red-400 text-sm" v-if="form.errors.authors">
|
||||
{{ formatError(form.errors.authors) }}
|
||||
</div>
|
||||
</CardBox>
|
||||
|
||||
|
|
@ -499,8 +464,8 @@
|
|||
:errors="form.errors"
|
||||
:relation="'contributors'"
|
||||
/>
|
||||
<div class="text-red-400 text-sm" v-if="form.errors.contributors && Array.isArray(form.errors.contributors)">
|
||||
{{ form.errors.contributors.join(', ') }}
|
||||
<div class="text-red-400 text-sm" v-if="form.errors.contributors">
|
||||
{{ formatError(form.errors.contributors) }}
|
||||
</div>
|
||||
</CardBox>
|
||||
</div>
|
||||
|
|
@ -522,7 +487,7 @@
|
|||
<FormField
|
||||
label="Project.."
|
||||
help="project is optional"
|
||||
:class="{ 'text-red-400': form.errors.project_id }"
|
||||
:errors="form.errors.project_id"
|
||||
class="w-full mx-2 flex-1"
|
||||
>
|
||||
<FormControl
|
||||
|
|
@ -533,9 +498,9 @@
|
|||
:errors="form.errors.project_id"
|
||||
:options="projects"
|
||||
>
|
||||
<div class="text-red-400 text-sm" v-if="form.errors.project_id">
|
||||
<!-- <div class="text-red-400 text-sm" v-if="form.errors.project_id">
|
||||
{{ form.errors.project_id.join(', ') }}
|
||||
</div>
|
||||
</div> -->
|
||||
</FormControl>
|
||||
</FormField>
|
||||
|
||||
|
|
@ -543,7 +508,7 @@
|
|||
<FormField
|
||||
label="Embargo Date.."
|
||||
help="embargo date is optional"
|
||||
:class="{ 'text-red-400': form.errors.embargo_date }"
|
||||
:errors="form.errors.embargo_date"
|
||||
class="w-full mx-2 flex-1"
|
||||
>
|
||||
<FormControl
|
||||
|
|
@ -605,23 +570,15 @@
|
|||
<tbody>
|
||||
<tr v-for="(item, index) in form.references">
|
||||
<td data-label="Reference Value">
|
||||
<!-- <input name="Reference Value" class="form-control"
|
||||
placeholder="[VALUE]" v-model="item.value" /> -->
|
||||
<FormControl
|
||||
required
|
||||
v-model="item.value"
|
||||
:type="'text'"
|
||||
placeholder="[VALUE]"
|
||||
:errors="form.errors.embargo_date"
|
||||
:errors="form.errors[`references.${index}.value`]"
|
||||
>
|
||||
<div
|
||||
class="text-red-400 text-sm"
|
||||
v-if="
|
||||
form.errors[`references.${index}.value`] &&
|
||||
Array.isArray(form.errors[`references.${index}.value`])
|
||||
"
|
||||
>
|
||||
{{ form.errors[`references.${index}.value`].join(', ') }}
|
||||
<div class="text-red-400 text-sm" v-if="form.errors[`references.${index}.value`]">
|
||||
{{ formatError(form.errors[`references.${index}.value`]) }}
|
||||
</div>
|
||||
</FormControl>
|
||||
</td>
|
||||
|
|
@ -633,11 +590,8 @@
|
|||
:options="referenceIdentifierTypes"
|
||||
placeholder="[type]"
|
||||
>
|
||||
<div
|
||||
class="text-red-400 text-sm"
|
||||
v-if="Array.isArray(form.errors[`references.${index}.type`])"
|
||||
>
|
||||
{{ form.errors[`references.${index}.type`].join(', ') }}
|
||||
<div class="text-red-400 text-sm" v-if="form.errors[`references.${index}.type`]">
|
||||
{{ formatError(form.errors[`references.${index}.type`]) }}
|
||||
</div>
|
||||
</FormControl>
|
||||
</td>
|
||||
|
|
@ -650,16 +604,12 @@
|
|||
:options="relationTypes"
|
||||
placeholder="[relation type]"
|
||||
>
|
||||
<div
|
||||
class="text-red-400 text-sm"
|
||||
v-if="Array.isArray(form.errors[`references.${index}.relation`])"
|
||||
>
|
||||
{{ form.errors[`references.${index}.relation`].join(', ') }}
|
||||
<div class="text-red-400 text-sm" v-if="form.errors[`references.${index}.relation`]">
|
||||
{{ formatError(form.errors[`references.${index}.relation`]) }}
|
||||
</div>
|
||||
</FormControl>
|
||||
</td>
|
||||
<td data-label="Reference Label">
|
||||
<!-- <input name="Reference Label" class="form-control" v-model="item.label" /> -->
|
||||
<FormControl
|
||||
required
|
||||
v-model="form.references[index].label"
|
||||
|
|
@ -667,12 +617,11 @@
|
|||
placeholder="[reference label]"
|
||||
>
|
||||
<div class="text-red-400 text-sm" v-if="form.errors[`references.${index}.label`]">
|
||||
{{ form.errors[`references.${index}.label`].join(', ') }}
|
||||
{{ formatError(form.errors[`references.${index}.label`]) }}
|
||||
</div>
|
||||
</FormControl>
|
||||
</td>
|
||||
<td class="before:hidden lg:w-1 whitespace-nowrap">
|
||||
<!-- <BaseButton color="info" :icon="mdiEye" small @click="isModalActive = true" /> -->
|
||||
<BaseButton color="danger" :icon="mdiTrashCan" small @click.prevent="removeReference(index)" />
|
||||
</td>
|
||||
</tr>
|
||||
|
|
@ -747,64 +696,20 @@
|
|||
class="mb-4 rounded-lg overflow-hidden shadow-md"
|
||||
/>
|
||||
</div>
|
||||
<div class="flex flex-col md:flex-row ml-10">
|
||||
<div class="flex flex-col md:flex-row ml-10">
|
||||
<!-- x min and max -->
|
||||
<FormField
|
||||
label="Coverage X Min"
|
||||
:class="{ 'text-red-400': form.errors['coverage.x_min'] }"
|
||||
class="w-full mx-2 flex-1"
|
||||
>
|
||||
<FormControl required v-model="form.coverage.x_min" type="text" placeholder="[enter x_min]">
|
||||
<div
|
||||
class="text-red-400 text-sm"
|
||||
v-if="form.errors['coverage.x_min'] && Array.isArray(form.errors['coverage.x_min'])"
|
||||
>
|
||||
{{ form.errors['coverage.x_min'].join(', ') }}
|
||||
</div>
|
||||
</FormControl>
|
||||
<FormField label="Coverage X Min" :errors="form.errors['coverage.x_min']" class="w-full mx-2 flex-1">
|
||||
<FormControl required v-model="form.coverage.x_min" type="text" placeholder="[enter x_min]" />
|
||||
</FormField>
|
||||
<FormField
|
||||
label="Coverage X Max"
|
||||
:class="{ 'text-red-400': form.errors['coverage.x_max'] }"
|
||||
class="w-full mx-2 flex-1"
|
||||
>
|
||||
<FormControl required v-model="form.coverage.x_max" type="text" placeholder="[enter x_max]">
|
||||
<div
|
||||
class="text-red-400 text-sm"
|
||||
v-if="form.errors['coverage.x_max'] && Array.isArray(form.errors['coverage.x_max'])"
|
||||
>
|
||||
{{ form.errors['coverage.x_max'].join(', ') }}
|
||||
</div>
|
||||
</FormControl>
|
||||
<FormField label="Coverage X Max" :errors="form.errors['coverage.x_max']" class="w-full mx-2 flex-1">
|
||||
<FormControl required v-model="form.coverage.x_max" type="text" placeholder="[enter x_max]" />
|
||||
</FormField>
|
||||
<!-- y min and max -->
|
||||
<FormField
|
||||
label="Coverage Y Min"
|
||||
:class="{ 'text-red-400': form.errors['coverage.y_min'] }"
|
||||
class="w-full mx-2 flex-1"
|
||||
>
|
||||
<FormControl required v-model="form.coverage.y_min" type="text" placeholder="[enter y_min]">
|
||||
<div
|
||||
class="text-red-400 text-sm"
|
||||
v-if="form.errors['coverage.y_min'] && Array.isArray(form.errors['coverage.y_min'])"
|
||||
>
|
||||
{{ form.errors['coverage.y_min'].join(', ') }}
|
||||
</div>
|
||||
</FormControl>
|
||||
<FormField label="Coverage Y Min" :errors="form.errors['coverage.y_min']" class="w-full mx-2 flex-1">
|
||||
<FormControl required v-model="form.coverage.y_min" type="text" placeholder="[enter y_min]" />
|
||||
</FormField>
|
||||
<FormField
|
||||
label="Coverage Y Max"
|
||||
:class="{ 'text-red-400': form.errors['coverage.y_max'] }"
|
||||
class="w-full mx-2 flex-1"
|
||||
>
|
||||
<FormControl required v-model="form.coverage.y_max" type="text" placeholder="[enter y_max]">
|
||||
<div
|
||||
class="text-red-400 text-sm"
|
||||
v-if="form.errors['coverage.y_max'] && Array.isArray(form.errors['coverage.y_max'])"
|
||||
>
|
||||
{{ form.errors['coverage.y_max'].join(', ') }}
|
||||
</div>
|
||||
</FormControl>
|
||||
<FormField label="Coverage Y Max" :errors="form.errors['coverage.y_max']" class="w-full mx-2 flex-1">
|
||||
<FormControl required v-model="form.coverage.y_max" type="text" placeholder="[enter y_max]" />
|
||||
</FormField>
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -1028,6 +933,7 @@ const mapId = 'test';
|
|||
props.dataset.filesToDelete = [];
|
||||
props.dataset.subjectsToDelete = [];
|
||||
props.dataset.referencesToDelete = [];
|
||||
|
||||
let form = useForm<Dataset>(props.dataset as Dataset);
|
||||
|
||||
// Add this computed property to the script section
|
||||
|
|
@ -1446,6 +1352,11 @@ const onMapInitialized = (newItem: any) => {
|
|||
console.log(newItem);
|
||||
};
|
||||
|
||||
const formatError = (error: string | string[] | undefined) => {
|
||||
if (!error) return '';
|
||||
return Array.isArray(error) ? error.join(', ') : error;
|
||||
};
|
||||
|
||||
// Add this method to generate change summaries
|
||||
const getChangesSummary = () => {
|
||||
const changes = [];
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue