- added npm package dotenv-webpack for using env variables on clientside
All checks were successful
CI Pipeline / japa-tests (push) Successful in 53s

- added API File Controller for downloading files e.g. /api/download/1022
- also create has codes by submitting new dataset
- added edit dataset functionalities for role submitter
- added the following route for role submitter: /dataset/:id/update', 'DatasetController.update'
- created extra UpdateDatasetValidator.ts for validating updated dataset
- npm updates
This commit is contained in:
Kaimbacher 2023-11-22 17:06:55 +01:00
parent a7142f694f
commit d8bdce1369
23 changed files with 2181 additions and 853 deletions

View file

@ -1,34 +1,21 @@
<template>
<section
aria-label="File Upload Modal"
<section aria-label="File Upload Modal"
class="relative h-full flex flex-col bg-white dark:bg-slate-900/70 shadow-xl rounded-md"
v-on:dragenter="dragEnterHandler"
v-on:dragleave="dragLeaveHandler"
v-on:dragover="dragOverHandler"
v-on:drop="dropHandler"
>
v-on:dragenter="dragEnterHandler" v-on:dragleave="dragLeaveHandler" v-on:dragover="dragOverHandler"
v-on:drop="dropHandler">
<!-- ondrop="dropHandler(event);"
ondragover="dragOverHandler(event);"
ondragleave="dragLeaveHandler(event);"
ondragenter="dragEnterHandler(event);" -->
<!-- overlay -->
<div
id="overlay"
ref="overlay"
class="w-full h-full absolute top-0 left-0 pointer-events-none z-50 flex flex-col items-center justify-center rounded-md"
>
<div id="overlay" ref="overlay"
class="w-full h-full absolute top-0 left-0 pointer-events-none z-50 flex flex-col items-center justify-center rounded-md">
<i>
<svg
class="fill-current w-12 h-12 mb-3 text-blue-700"
xmlns="http://www.w3.org/2000/svg"
width="24"
height="24"
viewBox="0 0 24 24"
>
<svg class="fill-current w-12 h-12 mb-3 text-blue-700" xmlns="http://www.w3.org/2000/svg" width="24"
height="24" viewBox="0 0 24 24">
<path
d="M19.479 10.092c-.212-3.951-3.473-7.092-7.479-7.092-4.005 0-7.267 3.141-7.479 7.092-2.57.463-4.521 2.706-4.521 5.408 0 3.037 2.463 5.5 5.5 5.5h13c3.037 0 5.5-2.463 5.5-5.5 0-2.702-1.951-4.945-4.521-5.408zm-7.479-1.092l4 4h-3v4h-2v-4h-3l4-4z"
/>
d="M19.479 10.092c-.212-3.951-3.473-7.092-7.479-7.092-4.005 0-7.267 3.141-7.479 7.092-2.57.463-4.521 2.706-4.521 5.408 0 3.037 2.463 5.5 5.5 5.5h13c3.037 0 5.5-2.463 5.5-5.5 0-2.702-1.951-4.945-4.521-5.408zm-7.479-1.092l4 4h-3v4h-2v-4h-3l4-4z" />
</svg>
</i>
<p class="text-lg text-blue-700">Drop files to upload</p>
@ -46,25 +33,14 @@
</button>
</header> -->
<header class="flex items-center justify-center w-full">
<label
for="dropzone-file"
class="flex flex-col items-center justify-center w-full h-64 border-2 border-gray-300 border-dashed rounded-lg cursor-pointer bg-gray-50 dark:bg-gray-700 hover:bg-gray-100 dark:border-gray-600 dark:hover:border-gray-500 dark:hover:bg-gray-600"
>
<label for="dropzone-file"
class="flex flex-col items-center justify-center w-full h-64 border-2 border-gray-300 border-dashed rounded-lg cursor-pointer bg-gray-50 dark:bg-gray-700 hover:bg-gray-100 dark:border-gray-600 dark:hover:border-gray-500 dark:hover:bg-gray-600">
<div class="flex flex-col items-center justify-center pt-5 pb-6">
<svg
aria-hidden="true"
class="w-10 h-10 mb-3 text-gray-400"
fill="none"
stroke="currentColor"
viewBox="0 0 24 24"
xmlns="http://www.w3.org/2000/svg"
>
<path
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="2"
d="M7 16a4 4 0 01-.88-7.903A5 5 0 1115.9 6L16 6a5 5 0 011 9.9M15 13l-3-3m0 0l-3 3m3-3v12"
></path>
<svg aria-hidden="true" class="w-10 h-10 mb-3 text-gray-400" fill="none" stroke="currentColor"
viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
d="M7 16a4 4 0 01-.88-7.903A5 5 0 1115.9 6L16 6a5 5 0 011 9.9M15 13l-3-3m0 0l-3 3m3-3v12">
</path>
</svg>
<p class="mb-2 text-sm text-gray-500 dark:text-gray-400">
<span class="font-semibold">Click to upload</span> or drag and drop
@ -78,7 +54,8 @@
<h1 class="pt-8 pb-3 font-semibold sm:text-lg text-gray-900">To Upload</h1>
<!-- <ul id="gallery" class="flex flex-1 flex-wrap -m-1"> -->
<draggable id="gallery" tag="ul" class="flex flex-1 flex-wrap -m-1" v-model="files" item-key="sorting">
<draggable id="galleryxy" tag="ul" class="flex flex-1 flex-wrap -m-1" v-model="items" item-key="sort_order">
<!-- <li
v-if="files.length == 0"
id="empty"
@ -108,17 +85,12 @@
<span class="text-small text-gray-500">No files selected</span>
</li> -->
<template #item="{ index, element }">
<li class="block p-1 w-1/2 sm:w-1/3 md:w-1/4 lg:w-1/6 xl:w-1/8 h-24">
<article
v-if="element.type.match('image.*')"
tabindex="0"
class="bg-gray-50 group hasImage w-full h-full rounded-md cursor-pointer relative text-transparent hover:text-white shadow-sm"
>
<img
:alt="element.name"
:src="generateURL(element)"
class="img-preview w-full h-full sticky object-cover rounded-md bg-fixed opacity-75"
/>
<li class="block p-1 w-1/2 sm:w-1/3 md:w-1/4 lg:w-1/6 xl:w-1/8 h-24" :key="index">
<article v-if="element.type.match('image.*')" tabindex="0"
class="bg-gray-50 group hasImage w-full h-full rounded-md cursor-pointer relative text-transparent hover:text-white shadow-sm">
<!-- :src="element.fileSrc" :src="generateURL(element)" -->
<img :alt="element.name" :src="element.fileSrc"
class="img-preview w-full h-full sticky object-cover rounded-md bg-fixed opacity-75" />
<!-- <section
class="hasError text-red-500 shadow-sm font-semibold flex flex-row rounded-md text-xs break-words w-full h-full z-21 absolute top-0 py-2 px-3"
>
@ -132,15 +104,14 @@
<DeleteIcon></DeleteIcon>
</button>
</section> -->
<section class="flex flex-col rounded-md text-xs break-words w-full h-full z-20 absolute top-0 py-2 px-3">
<section
class="flex flex-col rounded-md text-xs break-words w-full h-full z-20 absolute top-0 py-2 px-3">
<h1 class="flex-1">{{ element.name }}</h1>
<div class="flex">
<p class="p-1 size text-xs">{{ getFileSize(element) }}</p>
<p class="p-1 size text-xs text-gray-700">{{ index }}</p>
<button
class="delete ml-auto focus:outline-none hover:bg-gray-300 p-1 rounded-md"
@click="removeFile(index)"
>
<p class="p-1 size text-xs text-gray-700">{{ element.sort_order }}</p>
<button class="delete ml-auto focus:outline-none hover:bg-gray-300 p-1 rounded-md"
@click="removeFile(index)">
<DeleteIcon></DeleteIcon>
</button>
</div>
@ -150,16 +121,17 @@
</div> -->
</article>
<!-- :class="errors && errors[`files.${index}`] ? 'bg-red-400' : 'bg-gray-100'" -->
<article v-else tabindex="0" class="bg-gray-100 group w-full h-full rounded-md cursor-pointer relative shadow-sm">
<section class="flex flex-col rounded-md text-xs break-words w-full h-full z-20 absolute top-0 py-2 px-3">
<article v-else tabindex="0"
class="bg-gray-100 group w-full h-full rounded-md cursor-pointer relative shadow-sm">
<section
class="flex flex-col rounded-md text-xs break-words w-full h-full z-20 absolute top-0 py-2 px-3">
<h1 class="flex-1 text-gray-700 group-hover:text-blue-800">{{ element.name }}</h1>
<div class="flex">
<p class="p-1 size text-xs text-gray-700">{{ getFileSize(element) }}</p>
<p class="p-1 size text-xs text-gray-700">{{ index }}</p>
<p class="p-1 size text-xs text-gray-700">{{ element.sort_order }}</p>
<button
class="delete ml-auto focus:outline-none hover:bg-gray-300 p-1 rounded-md text-gray-800"
@click="removeFile(index)"
>
@click="removeFile(index)">
<DeleteIcon></DeleteIcon>
</button>
</div>
@ -191,11 +163,8 @@
<!-- sticky footer -->
<footer class="flex justify-end px-8 pb-8 pt-4">
<button
id="cancel"
class="ml-3 rounded-sm px-3 py-1 hover:bg-gray-300 focus:shadow-outline focus:outline-none"
@click="clearAllFiles"
>
<button id="cancel" class="ml-3 rounded-sm px-3 py-1 hover:bg-gray-300 focus:shadow-outline focus:outline-none"
@click="clearAllFiles">
Clear
</button>
</footer>
@ -203,13 +172,20 @@
</template>
<script lang="ts">
import { Component, Vue, Prop, Ref } from 'vue-facing-decorator';
// import BaseButton from './BaseButton.vue';
import { Component, Vue, Prop, Ref, Watch } from 'vue-facing-decorator';
import { usePage } from '@inertiajs/vue3';
import DeleteIcon from '@/Components/Icons/Delete.vue';
// import { Page, PageProps, Errors, ErrorBag } from '@inertiajs/inertia';
import Draggable from 'vuedraggable';
import { TestFile } from '@/Dataset';
import { Buffer } from 'buffer';
import { TethysFile } from '@/Dataset';
// lastModified: 1691759507591
// lastModifiedDate: Fri Aug 11 2023 15:11:47 GMT+0200 (Mitteleuropäische Sommerzeit)
// name: 'freieIP.png'
// size: 112237
// type: 'image/png'
// webkitRelativePath: ''
interface IDictionary {
[index: string]: Array<string>;
@ -239,7 +215,7 @@ interface InteriaPage {
Draggable,
},
})
export default class FileUploadComponent extends Vue {
class FileUploadComponent extends Vue {
/**
* Connect map id.
*/
@ -255,22 +231,45 @@ export default class FileUploadComponent extends Vue {
// @Prop() files: Array<TestFile>;
@Prop({
type: Array<TestFile>,
type: Array<File>,
default: [],
})
modelValue: Array<TestFile>;
// mdiTrashCan = mdiTrashCan;
files: Array<TethysFile | File>;
get files() {
return this.modelValue;
get items(): Array<TethysFile | File> {
return this.files;
}
set files(value: Array<TestFile>) {
set items(values: Array<TethysFile | File>) {
// this.modelValue = value;
this.modelValue.length = 0;
this.modelValue.push(...value);
this.files.length = 0;
this.files.push(...values);
// values.forEach((item, index) => {
// item.sort_order = index + 1; // Assuming sort_order starts from 1
// this.files.push(item);
// });
}
dragEnterHandler(e) {
@Watch("files", {
deep: true
})
public propertyWatcher(newItems: Array<TethysFile>) {
// Update sort_order based on the new index when the list is changed
newItems.forEach((item, index) => {
item.sort_order = index + 1; // Assuming sort_order starts from 1
});
}
public created() {
for (const file of this.files) {
if (!(file instanceof File)) {
// console.log(`${file.name} path is ${file.filePath} here.`);
this.generateURL(file);
// console.log(`${file.fileSrc} path.`);
}
}
}
public dragEnterHandler(e) {
e.preventDefault();
if (!this._hasFiles(e.dataTransfer)) {
return;
@ -278,17 +277,17 @@ export default class FileUploadComponent extends Vue {
++this.counter && this.overlay.classList.add('draggedover');
}
dragLeaveHandler() {
public dragLeaveHandler() {
1 > --this.counter && this.overlay.classList.remove('draggedover');
}
dragOverHandler(e) {
public dragOverHandler(e) {
if (this._hasFiles(e.dataTransfer)) {
e.preventDefault();
}
}
startDrag(evt, item) {
public startDrag(evt, item) {
evt.dataTransfer.dropEffect = 'move';
evt.dataTransfer.effectAllowed = 'move';
evt.dataTransfer.setData('itemID', item.id);
@ -296,28 +295,31 @@ export default class FileUploadComponent extends Vue {
// reset counter and append file to gallery when file is dropped
dropHandler(event) {
public dropHandler(event) {
event.preventDefault();
for (const file of event.dataTransfer.files) {
// let fileName = String(file.name.replace(/\.[^/.]+$/, ''));
// file.label = fileName;
// if (file.type.match('image.*')) {
// this.generateURL(file);
// }
this._addFile(file);
}
this.overlay.classList.remove('draggedover');
this.counter = 0;
}
onChangeFile(event) {
public onChangeFile(event) {
event.preventDefault();
// 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;
// console.log(file.file);
for (const file of event.target.files) {
// let fileName = String(event.target.files[0].name.replace(/\.[^/.]+$/, ''));
// file.label = fileName;
// if (file.type.match('image.*')) {
// this.generateURL(file);
// }
this._addFile(file);
}
// this.overlay.classList.remove('draggedover');
@ -341,24 +343,58 @@ export default class FileUploadComponent extends Vue {
return Object.fromEntries(Object.entries(this.errors).filter(([key]) => key.startsWith('file')));
}
clearAllFiles(event) {
public clearAllFiles(event) {
event.preventDefault();
this.files.splice(0);
this.items.splice(0);
}
removeFile(key) {
this.files.splice(key, 1);
public removeFile(key) {
this.items.splice(key, 1);
}
generateURL(file) {
let fileSrc = URL.createObjectURL(file);
setTimeout(() => {
URL.revokeObjectURL(fileSrc);
}, 1000);
return fileSrc;
public generateURL(file: TethysFile | File): string {
// const arrayBuffer = Buffer.from(file.fileData.data);
// const blob = new Blob([file.fileData.data], { type: 'application/octet-stream' });
// const blob = new Blob([file.fileData], { type: 'image/png'});
// let fileSrc = file.fileData;
let localUrl: string = "";
if (file instanceof File) {
localUrl = URL.createObjectURL(file as Blob);
} else if (file.filePath) {
// const blob = new Blob([file.fileData]);
// localUrl = URL.createObjectURL(blob);
const parsed = JSON.parse(file.fileData);
// retrieve the original buffer of data
const buff = Buffer.from(parsed.blob, "base64");
const blob = new Blob([buff], { type: 'application/octet-stream' });
// file.blob = blob;
localUrl = URL.createObjectURL(blob);
file.fileSrc = localUrl;
}
// setTimeout(() => {
// URL.revokeObjectURL(localUrl);
// }, 1000);
return localUrl;
}
getFileSize(file) {
// private async downloadFile(id: number): Promise<string> {
// const response = await axios.get<Blob>(`/api/download/${id}`, {
// responseType: 'blob',
// });
// const url = URL.createObjectURL(response.data);
// setTimeout(() => {
// URL.revokeObjectURL(url);
// }, 1000);
// return url;
// }
public getFileSize(file) {
if (file.size > 1024) {
if (file.size > 1048576) {
return Math.round(file.size / 1048576) + 'mb';
@ -370,29 +406,75 @@ export default class FileUploadComponent extends Vue {
}
}
// check if file is of type image and prepend the initialied
// template to the target element
private _addFile(file: TestFile) {
// const isImage = file.type.match('image.*');
// const objectURL = URL.createObjectURL(file);
// private _addFile(file) {
// // const isImage = file.type.match('image.*');
// // const objectURL = URL.createObjectURL(file);
// this.files[objectURL] = file;
// let test: TethysFile = { upload: file, label: "dfdsfs", sorting: 0 };
// file.sorting = this.files.length;
this.files.push(file);
// // this.files[objectURL] = file;
// // let test: TethysFile = { upload: file, label: "dfdsfs", sorting: 0 };
// // file.sorting = this.files.length;
// file.sort_order = (this.items.length + 1),
// this.files.push(file);
// }
private _addFile(file: File) {
// const reader = new FileReader();
// reader.onload = (event) => {
// const base64Data = (event.target as FileReader).result as string;
// this.items.push(test);
// };
// reader.readAsDataURL(file);
if (file instanceof File) {
// const base64Data = await this.readBase64(file);
let test: TethysFile = {
label: file.name,
name: file.name,
size: file.size,
file_size: file.size,
// fileData: JSON.stringify({ blob: base64Data }),
// filePath: file.mozFullPath,
// path_name: file.mozFullPath,
webkitRelativePath: '',
lastModified: file.lastModified,
type: file.type,
mime_type: file.type,
visible_in_frontdoor: false,
visible_in_oai: false,
fileSrc: file.type.match('image.*')? this.generateURL(file) : "",
blob: file as Blob,
sort_order: (this.items.length + 1),
};
// this.items.push(test);
this.items[this.items.length] = test;
}
else {
this.items.push(file);
}
}
// private async readBase64(blob: Blob): Promise<string> {
// return new Promise<string>((resolve, reject) => {
// const reader = new FileReader();
// reader.onload = (event) => resolve((event.target as FileReader).result as string);
// reader.onerror = reject;
// reader.readAsDataURL(blob);
// });
// }
// use to check if a file is being dragged
private _hasFiles({ types = [] as Array<string> }) {
return types.indexOf('Files') > -1;
}
}
export default FileUploadComponent;
</script>
<style lang="css">
.hasImage:hover section {
background-color: rgba(5, 5, 5, 0.4);
}
.hasImage:hover button:hover {
background: rgba(5, 5, 5, 0.45);
}
@ -409,6 +491,7 @@ i {
#overlay.draggedover {
background-color: rgba(255, 255, 255, 0.7);
}
#overlay.draggedover p,
#overlay.draggedover i {
opacity: 1;

View file

@ -1,4 +1,4 @@
<script setup>
<script setup lang="ts">
import { computed } from 'vue';
import FormCheckRadio from '@/Components/FormCheckRadio.vue';
const props = defineProps({
@ -13,7 +13,7 @@ const props = defineProps({
type: {
type: String,
default: 'checkbox',
validator: (value) => ['checkbox', 'radio', 'switch'].includes(value),
validator: (value: string) => ['checkbox', 'radio', 'switch'].includes(value),
},
componentClass: {
type: String,
@ -27,11 +27,34 @@ const props = defineProps({
});
const emit = defineEmits(['update:modelValue']);
const computedValue = computed({
get: () => props.modelValue,
// get: () => props.modelValue,
get: () => {
// const ids = props.modelValue.map((obj) => obj.id);
// return ids;
if (Array.isArray(props.modelValue)) {
if (props.modelValue.every((item) => typeof item === 'number')) {
return props.modelValue;
} else if (props.modelValue.every((item) => hasIdAttribute(item))) {
const ids = props.modelValue.map((obj) => obj.id.toString());
return ids;
}
return props.modelValue;
}
// return props.modelValue;
},
set: (value) => {
emit('update:modelValue', value);
},
});
// Define a type guard to check if an object has an 'id' attribute
// function hasIdAttribute(obj: any): obj is { id: any } {
// return typeof obj === 'object' && 'id' in obj;
// }
const hasIdAttribute = (obj: any): obj is { id: any } => {
return typeof obj === 'object' && 'id' in obj;
};
</script>
<template>

View file

@ -59,8 +59,9 @@ Map.include({
});
const DEFAULT_BASE_LAYER_NAME = 'BaseLayer';
const DEFAULT_BASE_LAYER_ATTRIBUTION = '&copy; <a target="_blank" href="http://osm.org/copyright">OpenStreetMap</a> contributors';
// const OPEN_SEARCH_HOST = 'http://localhost:9200';
const OPEN_SEARCH_HOST = 'http://192.168.21.18';
// const OPENSEARCH_HOST = 'http://localhost:9200';
const OPENSEARCH_HOST = 'http://192.168.21.18';
// const OPENSEARCH_HOST = `http://${process.env.OPENSEARCH_PUBLIC_HOST}`;
let map: Map;
const props = defineProps({
@ -226,7 +227,7 @@ const handleDrawEventCreated = async (event) => {
try {
let response = await axios({
method: 'POST',
url: OPEN_SEARCH_HOST + '/tethys-records/_search',
url: OPENSEARCH_HOST + '/tethys-records/_search',
headers: { 'Content-Type': 'application/json' },
data: {
size: 1000,

View file

@ -6,7 +6,7 @@ import { gradientBgPurplePink, gradientBgDark, gradientBgPinkRed, gradientBgGree
const props = defineProps({
bg: {
type: String,
required: true,
required: false,
validator: (value) => ['purplePink', 'pinkRed', 'greenBlue'].includes(value),
},
});
@ -23,9 +23,11 @@ const colorClass = computed(() => {
return gradientBgPinkRed;
case 'greenBlue':
return gradientBgGreenBlue;
default:
return 'bg-white text-black dark:bg-slate-900/70 dark:text-white';
}
return 'bg-white';
// return 'bg-white';
});
</script>

View file

@ -12,9 +12,10 @@ export interface Dataset {
| (IErrorMessage | undefined)
| Coverage
| Array<DatasetReference>
| Array<File>;
| Array<File>
| (Array<number> | Array<Object>);
language: Ref<string>;
// licenses: Array<number>;
licenses: Array<number> | Array<Object>;
rights: boolean;
type: string;
creating_corporation: string;
@ -29,24 +30,49 @@ export interface Dataset {
// async (user): Promise<void>;
subjects: Array<Subject>;
references: Array<DatasetReference>;
files: Array<TestFile> | undefined;
files: Array<TethysFile>;
// upload: TethysFile
}
/** Provides information about files and allows JavaScript in a web page to access their content. */
export interface TestFile extends Blob {
// export interface TethysFile {
// readonly lastModified: number;
// readonly name: string;
// readonly webkitRelativePath: string;
// id: number;
// label: string;
// sorting: number;
// filePath: string;
// fileSrc: string;
// }
export interface TethysFile {
readonly lastModified: number;
readonly name: string;
readonly webkitRelativePath: string;
id?: number;
label: string;
sorting: number;
}
// sorting: number;
// path_name?: string; //only db path_name
filePath?: string;
fileSrc?: string;
blob: Blob;
fileData?: any;
// export interface TethysFile {
// label: string,
// sorting: number,
// upload: File,
// }
//additional:
comment?: string;
document_id?: number;
file_size: number;
language?: string;
mime_type: string;
type?: string;
size: number;
sort_order: number;
visible_in_frontdoor: boolean;
visible_in_oai: boolean;
}
export interface Subject {
// id: number;
@ -64,12 +90,14 @@ export interface DatasetReference {
}
export interface Title {
id?: number;
value: string;
type: string;
language: string | Ref<string>;
}
export interface Description {
id?: number;
value: string;
type: string;
language: string | Ref<string>;

View file

@ -34,6 +34,7 @@ import FormControl from '@/Components/FormControl.vue';
<LayoutGuest>
<Head title="Login" />
<!-- <SectionFullScreen v-slot="{ cardClass }" :bg="'greenBlue'"> -->
<SectionFullScreen v-slot="{ cardClass }">
<a class="text-2xl font-semibold flex justify-center items-center mb-8 lg:mb-10">
<img src="/logo.svg" class="h-10 mr-4" alt="Windster Logo" />

View file

@ -22,9 +22,7 @@ import CardBox from '@/Components/CardBox.vue';
import FormField from '@/Components/FormField.vue';
import FormControl from '@/Components/FormControl.vue';
import FormCheckRadioGroup from '@/Components/FormCheckRadioGroup.vue';
// 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 CardBoxModal from '@/Components/CardBoxModal.vue';
@ -196,7 +194,7 @@ if (Object.keys(mainService.dataset).length == 0) {
// titles: [{ value: '', type: 'Main', language: language }],
// descriptions: [{ value: '', type: 'Abstract', language: language }],
// });
let form = useForm<Dataset>(dataset);
let form = useForm<Dataset>(dataset as Dataset);
// form.defaults();
// const emit = defineEmits(['update:modelValue', 'setRef']);
@ -292,11 +290,14 @@ const submit = async () => {
// this.currentStatus = STATUS_SAVING;
// serrors = [];
// const files = form.files.map((obj) => {
// return new File([obj.blob], obj.label, { type: obj.type, lastModified: obj.lastModified });
// });
// formStep.value++;
await form
.transform((data) => ({
...data,
...data,
rights: form.rights && form.rights == true ? 'true' : 'false',
}))
.post(route, {
@ -729,8 +730,9 @@ Removes a selected keyword
</SearchAutocomplete>
<TablePersons :persons="form.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)">
: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>
</CardBox>
@ -1013,7 +1015,7 @@ Removes a selected keyword
</p>
</div> -->
<FileUploadComponent v-model="form.files"></FileUploadComponent>
<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(', ') }}
@ -1039,7 +1041,8 @@ Removes a selected keyword
Next
</button>
<button v-if="formStep == 4" :disabled="form.processing" :class="{ 'opacity-25': form.processing }"
<button v-if="formStep == 4" :disabled="form.processing"
:class="{ 'opacity-25': form.processing }"
class="text-base hover:scale-110 focus:outline-none flex justify-center px-4 py-2 rounded font-bold cursor-pointer hover:bg-teal-200 bg-teal-100 text-teal-700 border duration-200 ease-in-out border-teal-600 transition"
@click.stop="submit">
Save

View file

@ -12,6 +12,7 @@
<!-- <div class="max-w-2xl mx-auto"> -->
<CardBox :form="true">
<FormValidationErrors v-bind:errors="errors" />
<div class="mb-4">
<!-- <label for="title" class="block text-gray-700 font-bold mb-2">Title:</label>
<input
@ -21,6 +22,7 @@
v-model="form.language"
/> -->
<div class="flex flex-col md:flex-row">
<!-- (1) language field -->
<FormField label="Language *" help="required: select dataset main language"
:class="{ 'text-red-400': errors.language }" class="w-full flex-1">
<FormControl required v-model="form.language" :type="'select'" placeholder="[Enter Language]"
@ -31,17 +33,41 @@
</FormControl>
</FormField>
</div>
<FormField label="Dataset Type *" help="required: dataset type"
:class="{ 'text-red-400': form.errors.type }">
<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>
</FormControl>
<!-- (2) licenses -->
<FormField label="Licenses" wrap-body :class="{ 'text-red-400': form.errors.licenses }"
class="mt-8 w-full mx-2 flex-1">
<FormCheckRadioGroup v-model="form.licenses" name="licenses" is-column :options="licenses" />
</FormField>
<!-- titles -->
<div class="flex flex-col md:flex-row">
<!-- (3) dataset_type -->
<FormField label="Dataset Type *" help="required: dataset type"
:class="{ 'text-red-400': 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>
</FormControl>
</FormField>
<!-- (4) creating_corporation -->
<FormField label="Creating Corporation *"
:class="{ 'text-red-400': 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 />
<!-- (5) titles -->
<CardBox class="mb-6 shadow" :has-form-data="false" title="Titles" :icon="mdiFinance"
:header-icon="mdiPlusCircle" v-on:header-icon-click="addTitle()">
<div class="flex flex-col md:flex-row">
@ -79,8 +105,8 @@
</tr>
</thead>
<tbody>
<template v-for="(item, index) in form.titles" :key="index">
<tr v-if="item.type != 'Main'">
<template v-for="(title, index) in form.titles" :key="index">
<tr v-if="title.type != 'Main'">
<!-- <td scope="row">{{ index + 1 }}</td> -->
<td data-label="Title Value">
<FormControl required v-model="form.titles[index].value" type="text"
@ -113,7 +139,7 @@
<BaseButtons type="justify-start lg:justify-end" no-wrap>
<!-- <BaseButton color="info" :icon="mdiEye" small @click="isModalActive = true" /> -->
<BaseButton color="danger" :icon="mdiTrashCan" small
@click.prevent="removeTitle(index)" />
v-if="title.id == undefined" @click.prevent="removeTitle(index)" />
</BaseButtons>
</td>
</tr>
@ -122,6 +148,7 @@
</table>
</CardBox>
<!-- (6) descriptions -->
<CardBox class="mb-6 shadow" :has-form-data="false" title="Descriptions" :icon="mdiFinance"
:header-icon="mdiPlusCircle" v-on:header-icon-click="addDescription()">
<div class="flex flex-col md:flex-row">
@ -172,7 +199,7 @@
</td>
<td data-label="Description Type">
<FormControl required v-model="form.descriptions[index].type" type="select"
:options="props.descriptiontypes" placeholder="[select title type]">
:options="descriptiontypes" placeholder="[select title type]">
<div class="text-red-400 text-sm"
v-if="Array.isArray(form.errors[`descriptions.${index}.type`])">
{{ form.errors[`descriptions.${index}.type`].join(', ') }}
@ -191,7 +218,7 @@
<td class="before:hidden lg:w-1 whitespace-nowrap">
<BaseButtons type="justify-start lg:justify-end" no-wrap>
<!-- <BaseButton color="info" :icon="mdiEye" small @click="isModalActive = true" /> -->
<BaseButton color="danger" :icon="mdiTrashCan" small
<BaseButton color="danger" :icon="mdiTrashCan" small v-if="item.id == undefined"
@click.prevent="removeDescription(index)" />
</BaseButtons>
</td>
@ -201,14 +228,182 @@
</table>
</CardBox>
<MapComponent v-if="form.coverage"
:mapOptions="mapOptions"
:baseMaps="baseMaps"
:fitBounds="fitBounds"
:coverage="form.coverage"
:mapId="mapId"
v-bind-event:onMapInitializedEvent="onMapInitialized"
></MapComponent>
<!-- (7) authors -->
<CardBox class="mb-6 shadow" has-table title="Authors" :icon="mdiBookOpenPageVariant">
<SearchAutocomplete source="/api/persons" :response-property="'first_name'"
placeholder="search in person table...." v-on:person="onAddAuthor"></SearchAutocomplete>
<TablePersons :persons="form.authors" v-if="form.authors.length > 0" />
<div class="text-red-400 text-sm" v-if="errors.authors && Array.isArray(errors.authors)">
{{ errors.authors.join(', ') }}
</div>
</CardBox>
<!-- (8) contributors -->
<CardBox class="mb-6 shadow" has-table title="Contributors" :icon="mdiBookOpenPageVariant">
<SearchAutocomplete source="/api/persons" :response-property="'first_name'"
placeholder="search in person table...." v-on:person="onAddContributor">
</SearchAutocomplete>
<TablePersons :persons="form.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>
</CardBox>
<div class="flex flex-col md:flex-row">
<!-- (9) project_id -->
<FormField label="Project.." help="project is optional"
:class="{ 'text-red-400': 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>
<!-- (10) embargo_date -->
<FormField label="Embargo Date.." help="embargo date is optional"
:class="{ 'text-red-400': errors.embargo_date }" class="w-full mx-2 flex-1">
<FormControl 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>
<BaseDivider />
<MapComponent v-if="form.coverage" :mapOptions="mapOptions" :baseMaps="baseMaps" :fitBounds="fitBounds"
:coverage="form.coverage" :mapId="mapId" v-bind-event:onMapInitializedEvent="onMapInitialized">
</MapComponent>
<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'] }"
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>
<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>
<!-- 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>
<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>
</div>
<CardBox class="mb-6 shadow" has-table title="Dataset References" :header-icon="mdiPlusCircle"
v-on:header-icon-click="addReference">
<table class="table-fixed border-green-900" v-if="form.references.length">
<thead>
<tr>
<th class="w-4/12">Value</th>
<th class="w-2/12">Type</th>
<th class="w-3/12">Relation</th>
<th class="w-2/12">Label</th>
<th class="w-1/12"></th>
</tr>
</thead>
<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">
<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>
</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(', ') }}
</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(', ') }}
</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(', ') }}
</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>
</tbody>
</table>
</CardBox>
<BaseDivider />
<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> -->
<TableKeywords :keywords="form.subjects" :errors="form.errors" :subjectTypes="subjectTypes"
v-if="form.subjects.length > 0" />
</CardBox>
</div>
@ -233,18 +428,14 @@
</option>
</select> -->
</div>
<div class="mb-4">
<label for="license" class="block text-gray-700 font-bold mb-2">License:</label>
<!-- <select
id="license"
class="shadow appearance-none border rounded w-full py-2 px-3 text-gray-700 leading-tight focus:outline-none focus:shadow-outline"
v-model="dataset.license_id"
>
<option v-for="license in licenses" :key="license.id" :value="license.id" class="block px-4 py-2 text-gray-700">
{{ license.name_long }}
</option>
</select> -->
<FileUploadComponent :files="form.files"></FileUploadComponent>
<div class="text-red-400 text-sm" v-if="form.errors['file'] && Array.isArray(form.errors['files'])">
{{ form.errors['files'].join(', ') }}
</div>
<!-- Add more input fields for the other properties of the dataset -->
<!-- <button
type="submit"
@ -252,11 +443,17 @@
>
Save
</button> -->
<template #footer>
<BaseButtons>
<BaseButton type="submit" label="Submit" color="info" :class="{ 'opacity-25': form.processing }"
:disabled="form.processing">
<BaseButton @click.stop="submit" :disabled="form.processing" label="Save" color="info"
:class="{ 'opacity-25': form.processing }" small>
</BaseButton>
<!-- <button :disabled="form.processing" :class="{ 'opacity-25': form.processing }"
class="text-base hover:scale-110 focus:outline-none flex justify-center px-4 py-2 rounded font-bold cursor-pointer hover:bg-teal-200 bg-teal-100 text-teal-700 border duration-200 ease-in-out border-teal-600 transition"
@click.stop="submit">
Save
</button> -->
</BaseButtons>
</template>
</CardBox>
@ -266,31 +463,61 @@
</template>
<script setup lang="ts">
import { Head, useForm } from '@inertiajs/vue3';
// import EditComponent from "./../EditComponent";
// export default EditComponent;
// import { Component, Vue, Prop, Setup, toNative } from 'vue-facing-decorator';
// import AuthLayout from '@/Layouts/Auth.vue';
import LayoutAuthenticated from '@/Layouts/LayoutAuthenticated.vue';
import { Dataset, Title } from '@/Dataset';
import { useForm, Head } from '@inertiajs/vue3';
// import { ref } from 'vue';
// import { MainService } from '@/Stores/main';
// import FormInput from '@/Components/FormInput.vue'; // @/Components/FormInput.vue'
import { Dataset, Title, Subject, TethysFile } from '@/Dataset';
import { stardust } from '@eidellev/adonis-stardust/client';
import FormField from '@/Components/FormField.vue';
import FormControl from '@/Components/FormControl.vue';
import SectionMain from '@/Components/SectionMain.vue';
import SectionTitleLineWithButton from '@/Components/SectionTitleLineWithButton.vue';
import { mdiImageText, mdiArrowLeftBoldOutline, mdiPlusCircle, mdiFinance, mdiTrashCan } from '@mdi/js';
// import BaseDivider from '@/Components/BaseDivider.vue';
import FormCheckRadioGroup from '@/Components/FormCheckRadioGroup.vue';
import BaseButton from '@/Components/BaseButton.vue';
import BaseButtons from '@/Components/BaseButtons.vue';
import BaseDivider from '@/Components/BaseDivider.vue';
import CardBox from '@/Components/CardBox.vue';
import { stardust } from '@eidellev/adonis-stardust/client';
import MapComponent from '@/Components/Map/map.component.vue';
import SearchAutocomplete from '@/Components/SearchAutocomplete.vue';
import TablePersons from '@/Components/TablePersons.vue';
import TableKeywords from '@/Components/TableKeywords.vue';
import FormValidationErrors from '@/Components/FormValidationErrors.vue';
import FileUploadComponent from '@/Components/FileUpload.vue';
import { MapOptions } from '@/Components/Map/MapOptions';
import { LatLngBoundsExpression } from 'leaflet/src/geo/LatLngBounds';
import { LayerOptions } from '@/Components/Map/LayerOptions';
import {
mdiImageText,
mdiArrowLeftBoldOutline,
mdiPlusCircle,
mdiFinance,
mdiTrashCan,
mdiBookOpenPageVariant,
mdiEarthPlus,
} from '@mdi/js';
import { notify } from '@/notiwind';
const props = defineProps({
dataset: {
errors: {
type: Object,
default: () => ({}),
},
licenses: {
type: Object,
default: () => ({}),
},
languages: {
type: Object,
default: () => ({}),
},
languages: {},
doctypes: {
type: Object,
default: () => ({}),
@ -299,14 +526,37 @@ const props = defineProps({
type: Object,
default: () => ({}),
},
projects: {
type: Object,
default: () => ({}),
},
descriptiontypes: {
type: Object,
default: () => ({}),
},
errors: {
contributorTypes: {
type: Object,
default: () => ({}),
},
subjectTypes: {
type: Object,
default: () => ({}),
},
referenceIdentifierTypes: {
type: Object,
default: () => ({}),
},
relationTypes: {
type: Object,
default: () => ({}),
},
dataset: {
type: Object,
default: () => ({}),
},
});
// const projects = reactive([]);
@ -325,7 +575,42 @@ const fitBounds: LatLngBoundsExpression = [
];
const mapId = 'test';
// const downloadFile = async (id: string): Promise<string> => {
// const response = await axios.get<Blob>(`/api/download/${id}`, {
// responseType: 'blob',
// });
// const url = URL.createObjectURL(response.data);
// setTimeout(() => {
// URL.revokeObjectURL(url);
// }, 1000);
// return url;
// };
// for (const file of props.dataset.files) {
// // console.log(`${file.name} path is ${file.filePath} here.`);
// file.fileSrc = ref("");
// // downloadFile(file.id).then((value: string) => {
// // file.fileSrc = ref(value);
// // form = useForm<Dataset>(props.dataset as Dataset);
// // });
// }
let form = useForm<Dataset>(props.dataset as Dataset);
// const mainService = MainService();
// mainService.fetchfiles(props.dataset);
// const files = computed(() => props.dataset.file);
// let form = useForm<Dataset>(props.dataset as Dataset);
// const form = useForm({
// _method: 'put',
// login: props.user.login,
@ -342,9 +627,63 @@ let form = useForm<Dataset>(props.dataset as Dataset);
// this.projects = data.projects;
// this.licenses = data.licenses;
// }
const submit = async (): Promise<void> => {
let route = stardust.route('dataset.update', [props.dataset.id]);
// await Inertia.post('/app/register', this.form);
// await router.post('/app/register', this.form);
if (form.licenses.every((item) => hasIdAttribute(item))) {
form.licenses = form.licenses.map((obj) => obj.id.toString());
}
const [fileUploads, fileInputs] = form.files?.reduce(
([fileUploads, fileInputs], obj) => {
if (!obj.id) {
// return MultipartFile for file upload
fileUploads[obj.sort_order] = new File([obj.blob], obj.label, { type: obj.type, lastModified: obj.lastModified });
} else {
// return normal request input
fileInputs.push(obj);
}
return [fileUploads, fileInputs];
},
[[], []] as [Array<File>, Array<TethysFile>]
) as [Array<File>, Array<TethysFile>];
await form
.transform((data) => ({
...data,
licenses: form.licenses.every((item) => hasIdAttribute(item))
? form.licenses.map((obj) => obj.id.toString())
: form.licenses,
files: fileUploads,
fileInputs: fileInputs,
// files: form.files.map((obj) => {
// let file;
// if (!obj.id) {
// // return MultipartFile for file upload
// file = new File([obj.blob], obj.label, { type: obj.type, lastModified: obj.lastModified });
// } else {
// // return normal request input
// file = obj;
// }
// return file;
// }),
rights: 'true',
}))
.put(route);
};
const hasIdAttribute = (obj: any): obj is { id: any } => {
return typeof obj === 'object' && 'id' in obj;
};
const addTitle = () => {
let newTitle: Title = { value: '', language: '', type: '' };
//this.dataset.files.push(uploadedFiles[i]);
form.titles.push(newTitle);
};
const removeTitle = (key) => {
@ -353,20 +692,58 @@ const removeTitle = (key) => {
const addDescription = () => {
let newDescription = { value: '', language: '', type: '' };
//this.dataset.files.push(uploadedFiles[i]);
form.descriptions.push(newDescription);
};
const removeDescription = (key) => {
form.descriptions.splice(key, 1);
};
const onAddAuthor = (person) => {
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 if (form.contributors.filter((e) => e.id === person.id).length > 0) {
notify({ type: 'warning', title: 'Warning', text: 'person is already defined as contributor' });
} else {
form.authors.push(person);
notify({ type: 'info', text: 'person has been successfully added as author' });
}
};
const onAddContributor = (person) => {
if (form.contributors.filter((e) => e.id === person.id).length > 0) {
notify({ type: 'warning', title: 'Warning', text: 'person is already defined as contributor' }, 4000);
} 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 addKeyword = () => {
let newSubject: Subject = { value: 'test', 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);
};
const removeReference = (key) => {
form.references.splice(key, 1);
};
const onMapInitialized = (newItem) => {
// notify({ type: 'info', text: message });
console.log(newItem);
};
</script>
<!-- <style>
<style>
.max-w-2xl {
max-width: 2xl;
}
@ -398,4 +775,4 @@ const onMapInitialized = (newItem) => {
0 2px 4px 0 rgba(66, 72, 78, 0.12),
0 4px 8px 0 rgba(66, 72, 78, 0.16);
}
</style> -->
</style>

View file

@ -0,0 +1,240 @@
import { Component, Vue, Prop, toNative } from 'vue-facing-decorator';
// import AuthLayout from '@/Layouts/Auth.vue';
import LayoutAuthenticated from '@/Layouts/LayoutAuthenticated.vue';
import { useForm, InertiaForm, Head } from '@inertiajs/vue3';
import FormInput from '@/Components/FormInput.vue'; // @/Components/FormInput.vue'
import { Dataset, Title, Subject } from '@/Dataset';
import { stardust } from '@eidellev/adonis-stardust/client';
import FormField from '@/Components/FormField.vue';
import FormControl from '@/Components/FormControl.vue';
import SectionMain from '@/Components/SectionMain.vue';
import SectionTitleLineWithButton from '@/Components/SectionTitleLineWithButton.vue';
import FormCheckRadioGroup from '@/Components/FormCheckRadioGroup.vue';
import BaseButton from '@/Components/BaseButton.vue';
import BaseButtons from '@/Components/BaseButtons.vue';
import BaseDivider from '@/Components/BaseDivider.vue';
import CardBox from '@/Components/CardBox.vue';
import MapComponent from '@/Components/Map/map.component.vue';
import SearchAutocomplete from '@/Components/SearchAutocomplete.vue';
import TablePersons from '@/Components/TablePersons.vue';
import TableKeywords from '@/Components/TableKeywords.vue';
import FormValidationErrors from '@/Components/FormValidationErrors.vue';
import FileUploadComponent from '@/Components/FileUpload.vue';
import { MapOptions } from '@/Components/Map/MapOptions';
import { LatLngBoundsExpression } from 'leaflet/src/geo/LatLngBounds';
import { LayerOptions } from '@/Components/Map/LayerOptions';
import {
mdiImageText,
mdiArrowLeftBoldOutline,
mdiPlusCircle,
mdiFinance,
mdiTrashCan,
mdiBookOpenPageVariant,
mdiEarthPlus,
} from '@mdi/js';
import { notify } from '@/notiwind';
export interface IErrorMessage {
[key: string]: Array<string>;
}
@Component({
name: 'EditComponent',
components: {
LayoutAuthenticated,
FormInput,
Head,
FormField,
FormControl,
SectionMain,
SectionTitleLineWithButton,
FormCheckRadioGroup,
BaseButton,
BaseButtons,
BaseDivider,
CardBox,
MapComponent,
SearchAutocomplete,
TablePersons,
TableKeywords,
FormValidationErrors,
FileUploadComponent,
},
})
class EditComponent extends Vue {
// Component Property
@Prop({ type: Object, default: () => ({}) })
public errors: IErrorMessage;
@Prop({ type: Object, default: () => ({}) })
public licenses;
@Prop({ type: Object, default: () => ({}) })
public languages;
@Prop({ type: Object, default: () => ({}) })
public doctypes;
@Prop({ type: Object, default: () => ({}) })
public titletypes;
@Prop({ type: Object, default: () => ({}) })
public projects;
@Prop({ type: Object, default: () => ({}) })
public descriptiontypes;
@Prop({ type: Object, default: () => {} })
public contributorTypes;
@Prop({ type: Object, default: () => ({}) })
public subjectTypes;
@Prop({ type: Object, default: () => ({}) })
public referenceIdentifierTypes;
@Prop({ type: Object, default: () => ({}) })
public relationTypes;
@Prop({ type: Object, default: () => ({}) })
public dataset: Dataset;
// @Prop({
// type: Object,
// default: () => ({}),
// })
// public datasetHasLicenses;
// Data Property
// public form: InertiaForm<Dataset>; // = useForm<Dataset>(this.dataset as Dataset);
// public form : InertiaForm<Dataset>= useForm<Dataset>([]);
// @Setup(() => useForm<Dataset>(this.dataset as Dataset))
public form: InertiaForm<Dataset>;
// @Hook
created() {
this.form = useForm<Dataset>(this.dataset as Dataset);
// this.form.licenses = this.datasetHasLicenses;
this.form.uploads = [];
}
public mapOptions: MapOptions = {
center: [48.208174, 16.373819],
zoom: 3,
zoomControl: false,
attributionControl: false,
};
public baseMaps: Map<string, LayerOptions> = new Map<string, LayerOptions>();
public fitBounds: LatLngBoundsExpression = [
[46.4318173285, 9.47996951665],
[49.0390742051, 16.9796667823],
];
public mapId = 'test';
mdiImageText = mdiImageText;
mdiArrowLeftBoldOutline = mdiArrowLeftBoldOutline;
mdiPlusCircle = mdiPlusCircle;
mdiFinance = mdiFinance;
mdiTrashCan = mdiTrashCan;
mdiBookOpenPageVariant = mdiBookOpenPageVariant;
mdiEarthPlus = mdiEarthPlus;
stardust = stardust;
// mounted() {
// this.form = useForm<Dataset>(this.dataset as Dataset);// Initialize myData with the value of propValue
// }
// public results: Array<any> = [];
// Component method
public async submit(): Promise<void> {
let route = this.stardust.route('dataset.update', [this.dataset.id]);
// await Inertia.post('/app/register', this.form);
// await router.post('/app/register', this.form);
if (this.form.licenses.every((item) => this.hasIdAttribute(item))) {
this.form.licenses = this.form.licenses.map((obj) => obj.id.toString());
}
await this.form
.transform((data) => ({
...data,
licenses: this.form.licenses.every((item) => this.hasIdAttribute(item))
? this.form.licenses.map((obj) => obj.id.toString())
: this.form.licenses,
rights: 'true',
}))
.put(route);
}
private hasIdAttribute(obj: any): obj is { id: any } {
return typeof obj === 'object' && 'id' in obj;
}
public addTitle(): void {
const newTitle: Title = { value: '', language: '', type: '' };
this.form.titles.push(newTitle);
}
public removeTitle(key: number): void {
this.form.titles.splice(key, 1);
}
public addDescription(): void {
const newDescription = { value: '', language: '', type: '' };
this.form.descriptions.push(newDescription);
}
public removeDescription(key: number): void {
this.form.descriptions.splice(key, 1);
}
public onAddAuthor(person) {
if (this.form.authors.filter((e) => e.id === person.id).length > 0) {
notify({ type: 'warning', title: 'Warning', text: 'person is already defined as author' }, 4000);
} else if (this.form.contributors.filter((e) => e.id === person.id).length > 0) {
notify({ type: 'warning', title: 'Warning', text: 'person is already defined as contributor' });
} else {
this.form.authors.push(person);
notify({ type: 'info', text: 'person has been successfully added as author' });
}
}
public onAddContributor(person) {
if (this.form.contributors.filter((e) => e.id === person.id).length > 0) {
notify({ type: 'warning', title: 'Warning', text: 'person is already defined as contributor' }, 4000);
} else if (this.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: '' };
this.form.contributors.push(person);
notify({ type: 'info', text: 'person has been successfully added as contributor' }, 4000);
}
}
public addKeyword() {
let newSubject: Subject = { value: 'test', language: '', type: 'uncontrolled' };
//this.dataset.files.push(uploadedFiles[i]);
this.form.subjects.push(newSubject);
}
public addReference() {
let newReference = { value: '', label: '', relation: '', type: '' };
//this.dataset.files.push(uploadedFiles[i]);
this.form.references.push(newReference);
}
public removeReference(key) {
this.form.references.splice(key, 1);
}
public onMapInitialized(newItem: any): void {
console.log(newItem);
}
}
export default toNative(EditComponent);
// export default toNative(EditComponent);

View file

@ -39,6 +39,7 @@ export const MainService = defineStore('main', {
authors: [] as Array<Person>,
// persons: [] as Array<Person>,
datasets: [],
files:[],
dataset: {} as Dataset,
}),
@ -108,5 +109,19 @@ export const MainService = defineStore('main', {
alert(error.message);
});
},
// fetchfiles(id) {
// // sampleDataKey= authors or datasets
// axios
// .get(`api/files/${id}`)
// .then((r) => {
// if (r.data) {
// this[sampleDataKey] = r.data;
// }
// })
// .catch((error) => {
// alert(error.message);
// });
// },
},
});