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

commit 579f0878e5240dc17db69be1e0b0c0f5af7ef9fe
Author: Arno Kaimbacher <arno.kaimbacher@geosphere.at>
Date:   Tue Jun 9 09:25:44 2026 +0200

    feat: Refactor error handling in Dataset Edit form and improve validation messages

    - Updated error handling in the Dataset Edit form to use a centralized formatError function for displaying validation messages.
    - Enhanced user feedback by ensuring that error messages are displayed consistently across various fields.
    - Modified the validation rule for arrayContainsTypes to provide clearer error messages for missing main and translated titles/abstracts.
    - Introduced a new ValidationService to manage manual construction of validation errors.
    - Updated Vite configuration to streamline asset loading and improve performance.
    - Adjusted Inertia setup to utilize dynamic imports for page-specific assets.
    - Cleaned up unnecessary comments and code in various files for better readability.

commit 5efddc2a58c0e164fef585cc7344c06155dbc2c1
Author: Arno Kaimbacher <arno.kaimbacher@geosphere.at>
Date:   Mon Jan 12 17:02:47 2026 +0100

    feat: add dataset change detection and form submission composables

    - Implemented `useDatasetChangeDetection` for tracking unsaved changes in dataset forms, including comparisons for licenses, basic properties, files, coverage, and more.
    - Added `useDatasetFormSubmission` for handling dataset form submissions with validation, success/error handling, and auto-save functionality.
This commit is contained in:
Kaimbacher 2026-06-09 09:35:15 +02:00
commit 9368a0dd8d
38 changed files with 5588 additions and 6181 deletions

View file

@ -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>