- added npm package dotenv-webpack for using env variables on clientside
All checks were successful
CI Pipeline / japa-tests (push) Successful in 53s
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:
parent
a7142f694f
commit
d8bdce1369
23 changed files with 2181 additions and 853 deletions
|
@ -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;
|
||||
|
|
Loading…
Add table
editor.link_modal.header
Reference in a new issue