- daraggable crators and contributors inside Pages/Submitter/Dataset/Create.Vue

- typescript and prettier updates
- FileUpload component with dark mode and dragable uploads
- comment FontFamily in tailwind.config.js
This commit is contained in:
Kaimbacher 2023-06-16 16:44:28 +02:00
parent f76a92da2c
commit c4f4eff0d9
18 changed files with 1138 additions and 951 deletions

View File

@ -274,19 +274,27 @@ export default class DatasetController {
language: schema.string({ trim: true }, [rules.minLength(2), rules.maxLength(255)]), language: schema.string({ trim: true }, [rules.minLength(2), rules.maxLength(255)]),
}), }),
), ),
file: schema.file({ // file: schema.file({
size: '100mb', // size: '100mb',
extnames: ['jpg', 'gif', 'png'], // extnames: ['jpg', 'gif', 'png'],
}), // }),
upload: schema.object().members({ files: schema.array([rules.minLength(1)]).members(
label: schema.string({ trim: true }, [rules.maxLength(255)]), schema.file({
size: '100mb',
extnames: ['jpg', 'gif', 'png'],
}),
),
// upload: schema.object().members({
// label: schema.string({ trim: true }, [rules.maxLength(255)]),
// label: schema.string({ trim: true }, [ // // label: schema.string({ trim: true }, [
// // rules.minLength(3), // // // rules.minLength(3),
// // rules.maxLength(255), // // // rules.maxLength(255),
// ]), // // ]),
}), // }),
}); });
// const coverImages = request.file('files');
// node ace make:validator CreateUser // node ace make:validator CreateUser
try { try {
// Step 2 - Validate request body against the schema // Step 2 - Validate request body against the schema
@ -298,23 +306,25 @@ export default class DatasetController {
// return response.badRequest(error.messages); // return response.badRequest(error.messages);
throw error; throw error;
} }
const coverImage = request.file('file'); const coverImage = request.files('files')[0];
if (coverImage) { if (coverImage) {
// clientName: 'Gehaltsschema.png'
// clientName: 'Gehaltsschema.png'
// extname: 'png' // extname: 'png'
// fieldName: 'file' // fieldName: 'file'
// size: 135624 // size: 135624
//const datasetFolder = 'files/' . dataset->id; //const datasetFolder = 'files/' . dataset->id;
// await coverImage.moveToDisk('./') // await coverImage.moveToDisk('./')
await coverImage.moveToDisk('/test_dataset', { await coverImage.moveToDisk(
name: 'renamed-file-name.jpg', '/test_dataset2',
overwrite: true, // overwrite in case of conflict {
},'local'); name: 'renamed-file-name.jpg',
overwrite: true, // overwrite in case of conflict
},
'local',
);
// let path = coverImage.filePath; // let path = coverImage.filePath;
} }
// const user = await User.create(input); // const user = await User.create(input);
// if (request.input('roles')) { // if (request.input('roles')) {
@ -362,7 +372,7 @@ export default class DatasetController {
'subjects.*.type.required': 'keyword type is required', 'subjects.*.type.required': 'keyword type is required',
'subjects.*.language.required': 'language of keyword is required', 'subjects.*.language.required': 'language of keyword is required',
'file.size': 'file size is to big', 'files.*.size': 'file size is to big',
'file.extnames': 'file extension is not supported', 'files.extnames': 'file extension is not supported',
}; };
} }

View File

@ -33,7 +33,7 @@ export default class Dataset extends BaseModel {
@column.dateTime({ columnName: 'server_date_published' }) @column.dateTime({ columnName: 'server_date_published' })
public ServerDatePublished: DateTime; public serverDatePublished: DateTime;
@column.dateTime({ autoCreate: true, columnName: 'created_at' }) @column.dateTime({ autoCreate: true, columnName: 'created_at' })
public createdAt: DateTime; public createdAt: DateTime;

View File

@ -2,7 +2,7 @@ import { BaseCommand } from '@adonisjs/core/build/standalone';
import crypto from 'crypto'; import crypto from 'crypto';
import fs from 'fs'; import fs from 'fs';
// import Config from '@ioc:Adonis/Core/Config'; // import Config from '@ioc:Adonis/Core/Config';
import Logger from '@ioc:Adonis/Core/Logger' import Logger from '@ioc:Adonis/Core/Logger';
export default class ValidateChecksum extends BaseCommand { export default class ValidateChecksum extends BaseCommand {
/** /**
@ -40,7 +40,7 @@ export default class ValidateChecksum extends BaseCommand {
const files = await File.query().preload('hashvalues'); const files = await File.query().preload('hashvalues');
// const logLevel = Config.get('app.logger.level', 'info'); // const logLevel = Config.get('app.logger.level', 'info');
// console.log(this.logger.) // console.log(this.logger.)
for (var file of files) { for (var file of files) {
let hashValue = await file.related('hashvalues').query().pluck('value', 'type'); let hashValue = await file.related('hashvalues').query().pluck('value', 'type');
@ -56,9 +56,7 @@ export default class ValidateChecksum extends BaseCommand {
} }
if (hashValue['md5'] == calculatedMd5FileHash) { if (hashValue['md5'] == calculatedMd5FileHash) {
Logger.info( Logger.info(`File id ${file.id}: stored md5 checksum: ${calculatedMd5FileHash}, control md5 checksum: ${hashValue['md5']}`);
`File id ${file.id}: stored md5 checksum: ${calculatedMd5FileHash}, control md5 checksum: ${hashValue['md5']}`,
);
} else { } else {
Logger.error( Logger.error(
`File id ${file.id}: stored md5 checksum: ${calculatedMd5FileHash}, control md5 checksum: ${hashValue['md5']}`, `File id ${file.id}: stored md5 checksum: ${calculatedMd5FileHash}, control md5 checksum: ${hashValue['md5']}`,

View File

@ -155,9 +155,8 @@ export const logger: LoggerConfig = {
*/ */
level: Env.get('LOG_LEVEL', 'info'), level: Env.get('LOG_LEVEL', 'info'),
redact: { redact: {
paths: ['password', '*.password'], paths: ['password', '*.password'],
}, },
/* /*
|-------------------------------------------------------------------------- |--------------------------------------------------------------------------

View File

@ -32,12 +32,7 @@ const bodyParserConfig: BodyParserConfig = {
encoding: 'utf-8', encoding: 'utf-8',
limit: '1mb', limit: '1mb',
strict: true, strict: true,
types: [ types: ['application/json', 'application/json-patch+json', 'application/vnd.api+json', 'application/csp-report'],
'application/json',
'application/json-patch+json',
'application/vnd.api+json',
'application/csp-report',
],
}, },
/* /*

View File

@ -1,9 +1,5 @@
declare module '@ioc:Adonis/Core/Validator' { declare module '@ioc:Adonis/Core/Validator' {
interface Rules { interface Rules {
translatedLanguage( translatedLanguage(mainLanguageField: string, typeField: string): Rule;
mainLanguageField: string,
typeField: string
): Rule
} }
} }

1454
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -63,10 +63,10 @@
"pinia": "^2.0.30", "pinia": "^2.0.30",
"pino-pretty": "^10.0.0", "pino-pretty": "^10.0.0",
"postcss-loader": "^7.0.2", "postcss-loader": "^7.0.2",
"prettier": "^2.8.3", "prettier": "^2.8.8",
"tailwindcss": "^3.2.4", "tailwindcss": "^3.2.4",
"ts-loader": "^9.4.2", "ts-loader": "^9.4.2",
"typescript": "^4.9.5", "typescript": "^5.1.3",
"vue": "^3.2.47", "vue": "^3.2.47",
"vue-loader": "^17.0.1", "vue-loader": "^17.0.1",
"youch": "^3.2.0", "youch": "^3.2.0",
@ -96,6 +96,7 @@
"proxy-addr": "^2.0.7", "proxy-addr": "^2.0.7",
"reflect-metadata": "^0.1.13", "reflect-metadata": "^0.1.13",
"source-map-support": "^0.5.21", "source-map-support": "^0.5.21",
"vue-facing-decorator": "^2.1.13" "vue-facing-decorator": "^2.1.13",
"vuedraggable": "^4.1.0"
} }
} }

View File

@ -1,7 +1,7 @@
<template> <template>
<div <section
aria-label="File Upload Modal" aria-label="File Upload Modal"
class="relative h-full flex flex-col bg-white shadow-xl rounded-md" class="relative h-full flex flex-col bg-white dark:bg-slate-900/70 shadow-xl rounded-md"
v-on:dragenter="dragEnterHandler" v-on:dragenter="dragEnterHandler"
v-on:dragleave="dragLeaveHandler" v-on:dragleave="dragLeaveHandler"
v-on:dragover="dragOverHandler" v-on:dragover="dragOverHandler"
@ -35,7 +35,7 @@
</div> </div>
<!-- scroll area --> <!-- scroll area -->
<div class="h-full overflow-auto p-8 w-full h-full flex flex-col"> <div class="h-full p-8 w-full h-full flex flex-col">
<!-- <header id="dropzone" class="border-dashed border-2 border-gray-400 py-12 flex flex-col justify-center items-center"> <!-- <header id="dropzone" class="border-dashed border-2 border-gray-400 py-12 flex flex-col justify-center items-center">
<p class="mb-3 font-semibold text-gray-900 flex flex-wrap justify-center"> <p class="mb-3 font-semibold text-gray-900 flex flex-wrap justify-center">
<span>Drag and drop your</span>&nbsp;<span>files anywhere or</span> <span>Drag and drop your</span>&nbsp;<span>files anywhere or</span>
@ -48,7 +48,7 @@
<header class="flex items-center justify-center w-full"> <header class="flex items-center justify-center w-full">
<label <label
for="dropzone-file" 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-teal-50 dark:hover:bg-bray-800 dark:bg-gray-700 hover:bg-gray-100 dark:border-gray-600 dark:hover:border-gray-500 dark:hover:bg-gray-600" 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"> <div class="flex flex-col items-center justify-center pt-5 pb-6">
<svg <svg
@ -71,19 +71,20 @@
</p> </p>
<!-- <p class="text-xs text-gray-500 dark:text-gray-400">SVG, PNG, JPG or GIF (MAX. 800x400px)</p> --> <!-- <p class="text-xs text-gray-500 dark:text-gray-400">SVG, PNG, JPG or GIF (MAX. 800x400px)</p> -->
</div> </div>
<!-- <input id="dropzone-file" type="file" class="hidden" @change="onChangeFile" /> --> <input id="dropzone-file" type="file" class="hidden" @change="onChangeFile" />
</label> </label>
</header> </header>
<h1 class="pt-8 pb-3 font-semibold sm:text-lg text-gray-900">To Upload</h1> <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"> <!-- <ul id="gallery" class="flex flex-1 flex-wrap -m-1"> -->
<li <draggable id="gallery" tag="ul" class="flex flex-1 flex-wrap -m-1" v-model="files" item-key="sorting">
<!-- <li
v-if="files.length == 0" v-if="files.length == 0"
id="empty" id="empty"
class="h-full w-full text-center flex flex-col items-center justify-center items-center" class="h-full w-full text-center flex flex-col items-center justify-center items-center"
> >
<svg <svg
class="w-20 h-20 mb-3 text-gray-400" class="w-20 h-20 mb-3 text-gray-400"
version="1.1" version="1.1"
id="Capa_1" id="Capa_1"
@ -105,69 +106,87 @@
</g> </g>
</svg> </svg>
<span class="text-small text-gray-500">No files selected</span> <span class="text-small text-gray-500">No files selected</span>
</li> </li> -->
<li v-for="(file, index) in files" :key="index" 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"> <template #item="{ index, element }">
<article <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">
v-if="file.type.match('image.*')" <article
tabindex="0" v-if="element.type.match('image.*')"
class="group hasImage w-full h-full rounded-md bg-gray-100 cursor-pointer relative text-transparent hover:text-white shadow-sm" 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="file.name" <img
:src="generateURL(file)" :alt="element.name"
class="img-preview w-full h-full sticky object-cover rounded-md bg-fixed" :src="generateURL(element)"
/> class="img-preview w-full h-full sticky object-cover rounded-md bg-fixed opacity-75"
<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">{{ file.name }}</h1> <!-- <section
<div class="flex"> 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"
<p class="p-1 size text-xs">{{ getFileSize(file) }}</p> >
<p class="p-1 text-xs" v-if="errors[`files.${index}`]">
{{ errors[`files.${index}`].join(', ') }}
</p>
<button <button
class="delete ml-auto focus:outline-none hover:bg-gray-300 p-1 rounded-md" class="delete ml-auto focus:outline-none hover:bg-gray-300 p-1 rounded-md"
@click="removeFile(index)" @click="removeFile(index)"
> >
<svg <DeleteIcon></DeleteIcon>
class="pointer-events-none fill-current w-4 h-4 ml-auto"
xmlns="http://www.w3.org/2000/svg"
width="24"
height="24"
viewBox="0 0 24 24"
>
<path
class="pointer-events-none"
d="M3 6l3 18h12l3-18h-18zm19-4v2h-20v-2h5.711c.9 0 1.631-1.099 1.631-2h5.316c0 .901.73 2 1.631 2h5.711z"
/>
</svg>
</button> </button>
</div> </section> -->
</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">
</article> <h1 class="flex-1">{{ element.name }}</h1>
<article v-else tabindex="0" class="group w-full h-full rounded-md bg-gray-100 cursor-pointer relative shadow-sm"> <div class="flex">
<section class="flex flex-col rounded-md text-xs break-words w-full h-full z-20 absolute top-0 py-2 px-3"> <p class="p-1 size text-xs">{{ getFileSize(element) }}</p>
<h1 class="flex-1 group-hover:text-blue-800">{{ file.name }}</h1> <p class="p-1 size text-xs text-gray-700">{{ index }}</p>
<div class="flex"> <button
<p class="p-1 size text-xs text-gray-700">{{ getFileSize(file) }}</p> class="delete ml-auto focus:outline-none hover:bg-gray-300 p-1 rounded-md"
<button @click="removeFile(index)"
class="delete ml-auto focus:outline-none hover:bg-gray-300 p-1 rounded-md text-gray-800"
@click="removeFile(index)"
>
<svg
class="pointer-events-none fill-current w-4 h-4 ml-auto"
xmlns="http://www.w3.org/2000/svg"
width="24"
height="24"
viewBox="0 0 24 24"
> >
<path <DeleteIcon></DeleteIcon>
class="pointer-events-none" </button>
d="M3 6l3 18h12l3-18h-18zm19-4v2h-20v-2h5.711c.9 0 1.631-1.099 1.631-2h5.316c0 .901.73 2 1.631 2h5.711z" </div>
/> </section>
</svg> <!-- <div class="text-red-400 text-sm w-full h-full rounded-md cursor-pointer relative shadow-sm" v-if="errors[`files.${index}`]">
</button> {{ errors[`files.${index}`].join(', ') }}
</div> </div> -->
</section> </article>
</article> <!-- :class="errors && errors[`files.${index}`] ? 'bg-red-400' : 'bg-gray-100'" -->
</li> <article v-else tabindex="0" class="bg-gray-100 group w-full h-full rounded-md cursor-pointer relative shadow-sm">
</ul> <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>
<button
class="delete ml-auto focus:outline-none hover:bg-gray-300 p-1 rounded-md text-gray-800"
@click="removeFile(index)"
>
<DeleteIcon></DeleteIcon>
</button>
</div>
</section>
</article>
</li>
</template>
</draggable>
<!-- </ul> -->
<div v-if="fileErrors" class="flex flex-col mt-6 animate-fade-in" v-for="fileError in fileErrors">
<div class="bg-yellow-500 border-l-4 border-orange-400 text-white p-4" role="alert">
<p class="font-bold">Be Warned</p>
<p>{{ fileError.join(', ') }}</p>
</div>
</div>
<!-- <div class="text-red-400 text-sm" v-if="errors && Array.isArray(errors['files.0'])">
{{ errors['files.0'].join(', ') }}
</div> -->
<!-- <div v-if="hasErrors">
<div class="font-medium text-red-600">Whoops! Something went wrong.</div>
<ul class="mt-3 list-disc list-inside text-sm text-red-600">
<li v-for="(error, key) in errors" :key="key">{{ error }}</li>
</ul>
</div> -->
</div> </div>
<!-- sticky footer --> <!-- sticky footer -->
@ -180,37 +199,77 @@
Clear Clear
</button> </button>
</footer> </footer>
</div> </section>
</template> </template>
<script lang="ts"> <script lang="ts">
import { Component, Vue, Prop, Ref } from 'vue-facing-decorator'; import { Component, Vue, Prop, Ref } from 'vue-facing-decorator';
import BaseButton from './BaseButton.vue'; // import BaseButton from './BaseButton.vue';
// import { mdiPlus, mdiMinus } from '@mdi/js'; import { usePage } from '@inertiajs/vue3';
import { mdiTrashCan } from '@mdi/js'; import DeleteIcon from '@/Components/Icons/Delete.vue';
// import { Page, PageProps, Errors, ErrorBag } from '@inertiajs/inertia';
import Draggable from 'vuedraggable';
import { TestFile } from '@/Dataset';
interface IDictionary {
[index: string]: Array<string>;
}
export declare type ErrorBag1 = Record<string, Array<string>>;
interface InteriaPage {
// extends Page<PageProps> {
[key: string]: boolean | number | string | Object | (string | null);
errors: IDictionary;
props: {
// [key: string]: Array<string>;
errors: IDictionary;
auth: {
user: {
name: string;
};
};
// laravelVersion: string;
// phpVersion: string;
};
}
@Component({ @Component({
name: 'file-upload', name: 'file-upload',
components: { components: {
BaseButton, DeleteIcon,
mdiTrashCan, Draggable,
}, },
}) })
export default class FileUploadComponent extends Vue { export default class FileUploadComponent extends Vue {
// mdiPlus = mdiPlus;
// mdiMinus = mdiMinus;
/** /**
* Connect map id. * Connect map id.
*/ */
@Prop() public mapId: string; // @Prop({
// type: Object,
// default: () => ({}),
// })
// errors: IErrorMessage;
@Ref('overlay') overlay: HTMLDivElement; @Ref('overlay') overlay: HTMLDivElement;
private counter: number = 0; private counter: number = 0;
files: Array<File> = []; // @Prop() files: Array<TestFile>;
@Prop({
type: Array<TestFile>,
default: [],
})
modelValue: Array<TestFile>;
// mdiTrashCan = mdiTrashCan; // mdiTrashCan = mdiTrashCan;
get files() {
return this.modelValue;
}
set files(value: Array<TestFile>) {
// this.modelValue = value;
this.modelValue.length = 0;
this.modelValue.push(...value);
}
dragEnterHandler(e) { dragEnterHandler(e) {
e.preventDefault(); e.preventDefault();
if (!this._hasFiles(e.dataTransfer)) { if (!this._hasFiles(e.dataTransfer)) {
@ -240,12 +299,48 @@ export default class FileUploadComponent extends Vue {
dropHandler(event) { dropHandler(event) {
event.preventDefault(); event.preventDefault();
for (const file of event.dataTransfer.files) { for (const file of event.dataTransfer.files) {
// let fileName = String(file.name.replace(/\.[^/.]+$/, ''));
// file.label = fileName;
this._addFile(file); this._addFile(file);
} }
this.overlay.classList.remove('draggedover'); this.overlay.classList.remove('draggedover');
this.counter = 0; this.counter = 0;
} }
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;
this._addFile(file);
}
// this.overlay.classList.remove('draggedover');
this.counter = 0;
}
get errors(): IDictionary {
if (usePage().props.errors) {
// return usePage().props.errors;
return usePage<InteriaPage>().props.errors;
} else {
return {};
}
}
get hasErrors(): boolean {
return Object.keys(this.errors).length > 0;
}
get fileErrors() {
return Object.fromEntries(Object.entries(this.errors).filter(([key]) => key.startsWith('file')));
}
clearAllFiles(event) { clearAllFiles(event) {
event.preventDefault(); event.preventDefault();
this.files.splice(0); this.files.splice(0);
@ -255,16 +350,6 @@ export default class FileUploadComponent extends Vue {
this.files.splice(key, 1); this.files.splice(key, 1);
} }
// check if file is of type image and prepend the initialied
// template to the target element
private _addFile(file) {
// const isImage = file.type.match('image.*');
// const objectURL = URL.createObjectURL(file);
// this.files[objectURL] = file;
this.files.push(file);
}
generateURL(file) { generateURL(file) {
let fileSrc = URL.createObjectURL(file); let fileSrc = URL.createObjectURL(file);
setTimeout(() => { setTimeout(() => {
@ -285,6 +370,18 @@ 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);
// this.files[objectURL] = file;
// let test: TethysFile = { upload: file, label: "dfdsfs", sorting: 0 };
// file.sorting = this.files.length;
this.files.push(file);
}
// use to check if a file is being dragged // use to check if a file is being dragged
private _hasFiles({ types = [] as Array<string> }) { private _hasFiles({ types = [] as Array<string> }) {
return types.indexOf('Files') > -1; return types.indexOf('Files') > -1;
@ -300,6 +397,10 @@ export default class FileUploadComponent extends Vue {
background: rgba(5, 5, 5, 0.45); background: rgba(5, 5, 5, 0.45);
} }
section.hasError {
background-color: rgba(5, 5, 5, 0.4);
}
#overlay p, #overlay p,
i { i {
opacity: 0; opacity: 0;

View File

@ -0,0 +1,40 @@
<template>
<svg
class="pointer-events-none fill-current w-4 h-4 ml-auto"
xmlns="http://www.w3.org/2000/svg"
width="24"
height="24"
viewBox="0 0 24 24"
>
<path
class="pointer-events-none"
d="M3 6l3 18h12l3-18h-18zm19-4v2h-20v-2h5.711c.9 0 1.631-1.099 1.631-2h5.316c0 .901.73 2 1.631 2h5.711z"
/>
</svg>
</template>
<!-- <script>
export default {
name: 'Icon_Mandatory',
};
</script> -->
<style scoped>
.my-svg-component {
/* Scoped CSS here */
width: 100%;
height: 100%;
fill: none;
stroke: currentColor;
stroke-width: 1;
stroke-linecap: round;
stroke-linejoin: round;
}
/* path,
circle {
stroke: #ffffff;
stroke-width: 6px;
fill: none;
} */
</style>

View File

@ -11,6 +11,7 @@ import BaseButton from '@/Components/BaseButton.vue';
import UserAvatar from '@/Components/UserAvatar.vue'; import UserAvatar from '@/Components/UserAvatar.vue';
// import Person from 'App/Models/Person'; // import Person from 'App/Models/Person';
import { Person } from '@/Stores/main'; import { Person } from '@/Stores/main';
import Draggable from 'vuedraggable';
const props = defineProps({ const props = defineProps({
checkable: Boolean, checkable: Boolean,
@ -22,7 +23,20 @@ const props = defineProps({
const styleService = StyleService(); const styleService = StyleService();
// const mainService = MainService(); // const mainService = MainService();
const items = computed(() => props.persons); // const items = computed(() => props.persons);
const items = computed({
get() {
return props.persons;
},
// setter
set(value) {
// Note: we are using destructuring assignment syntax here.
props.persons.length = 0;
props.persons.push(...value);
},
});
// const isModalActive = ref(false); // const isModalActive = ref(false);
// const isModalDangerActive = ref(false); // const isModalDangerActive = ref(false);
@ -30,7 +44,18 @@ const perPage = ref(5);
const currentPage = ref(0); const currentPage = ref(0);
// const checkedRows = ref([]); // const checkedRows = ref([]);
const itemsPaginated = computed(() => items.value.slice(perPage.value * currentPage.value, perPage.value * (currentPage.value + 1))); const itemsPaginated = computed({
get() {
return items.value.slice(perPage.value * currentPage.value, perPage.value * (currentPage.value + 1));
},
// setter
set(value) {
// Note: we are using destructuring assignment syntax here.
props.persons.length = 0;
props.persons.push(...value);
},
});
const numPages = computed(() => Math.ceil(items.value.length / perPage.value)); const numPages = computed(() => Math.ceil(items.value.length / perPage.value));
@ -93,6 +118,7 @@ const removeAuthor = (key) => {
<thead> <thead>
<tr> <tr>
<!-- <th v-if="checkable" /> --> <!-- <th v-if="checkable" /> -->
<th scope="col">ID</th>
<th class="hidden lg:table-cell"></th> <th class="hidden lg:table-cell"></th>
<th>Name</th> <th>Name</th>
<th>Email</th> <th>Email</th>
@ -102,44 +128,50 @@ const removeAuthor = (key) => {
<th /> <th />
</tr> </tr>
</thead> </thead>
<tbody> <!-- <tbody> -->
<tr v-for="(client, index) in itemsPaginated" :key="client.id"> <!-- <tr v-for="(client, index) in itemsPaginated" :key="client.id"> -->
<!-- <TableCheckboxCell v-if="checkable" @checked="checked($event, client)" /> --> <draggable id="galliwasery" tag="tbody" v-model="items" item-key="id">
<td class="border-b-0 lg:w-6 before:hidden hidden lg:table-cell"> <template #item="{ index, element }">
<UserAvatar :username="client.name" class="w-24 h-24 mx-auto lg:w-6 lg:h-6" /> <tr>
</td> <td scope="row">{{ index + 1 }}</td>
<td data-label="Name"> <!-- <TableCheckboxCell v-if="checkable" @checked="checked($event, client)" /> -->
{{ client.name }} <td class="border-b-0 lg:w-6 before:hidden hidden lg:table-cell">
</td> <UserAvatar :username="element.name" class="w-24 h-24 mx-auto lg:w-6 lg:h-6" />
<td data-label="Email"> </td>
{{ client.email }} <td data-label="Name">
</td> {{ element.name }}
<!-- <td data-label="Name Type"> </td>
<td data-label="Email">
{{ element.email }}
</td>
<!-- <td data-label="Name Type">
{{ client.name_type }} {{ client.name_type }}
</td> --> </td> -->
<!-- <td data-label="Orcid"> <!-- <td data-label="Orcid">
{{ client.identifier_orcid }} {{ client.identifier_orcid }}
</td> --> </td> -->
<!-- <td data-label="Progress" class="lg:w-32"> <!-- <td data-label="Progress" class="lg:w-32">
<progress class="flex w-2/5 self-center lg:w-full" max="100" v-bind:value="client.progress"> <progress class="flex w-2/5 self-center lg:w-full" max="100" v-bind:value="client.progress">
{{ client.progress }} {{ client.progress }}
</progress> </progress>
</td> --> </td> -->
<td data-label="Created" class="lg:w-1 whitespace-nowrap"> <td data-label="Created" class="lg:w-1 whitespace-nowrap">
<small class="text-gray-500 dark:text-slate-400" :title="client.created_at">{{ client.created_at }}</small> <small class="text-gray-500 dark:text-slate-400" :title="element.created_at">{{ element.created_at }}</small>
</td> </td>
<td class="before:hidden lg:w-1 whitespace-nowrap"> <td class="before:hidden lg:w-1 whitespace-nowrap">
<BaseButtons type="justify-start lg:justify-end" no-wrap> <BaseButtons type="justify-start lg:justify-end" no-wrap>
<!-- <BaseButton color="info" :icon="mdiEye" small @click="isModalActive = true" /> --> <!-- <BaseButton color="info" :icon="mdiEye" small @click="isModalActive = true" /> -->
<BaseButton color="danger" :icon="mdiTrashCan" small @click.prevent="removeAuthor(index)" /> <BaseButton color="danger" :icon="mdiTrashCan" small @click.prevent="removeAuthor(index)" />
</BaseButtons> </BaseButtons>
</td> </td>
</tr> </tr>
</tbody> </template>
</draggable>
<!-- </tbody> -->
</table> </table>
<!-- :class="[ pagesList.length > 1 ? 'block' : 'hidden']" --> <!-- :class="[ pagesList.length > 1 ? 'block' : 'hidden']" -->
<div class="p-3 lg:px-6 border-t border-gray-100 dark:border-slate-800" > <div class="p-3 lg:px-6 border-t border-gray-100 dark:border-slate-800">
<BaseLevel> <!-- <BaseLevel>
<BaseButtons> <BaseButtons>
<BaseButton <BaseButton
v-for="page in pagesList" v-for="page in pagesList"
@ -152,6 +184,6 @@ const removeAuthor = (key) => {
/> />
</BaseButtons> </BaseButtons>
<small>Page {{ currentPageHuman }} of {{ numPages }}</small> <small>Page {{ currentPageHuman }} of {{ numPages }}</small>
</BaseLevel> </BaseLevel> -->
</div> </div>
</template> </template>

View File

@ -1,7 +1,7 @@
import { Ref } from 'vue'; import { Ref } from 'vue';
export interface Dataset { export interface Dataset {
[key: string]: string | Ref<string>| boolean | Array<Title> | Array<Description>| Array<Person> | number | (IErrorMessage | undefined) | Coverage | TethysFile | File; [key: string]: string | Ref<string>| boolean | Array<Title> | Array<Description>| Array<Person> | number | (IErrorMessage | undefined) | Coverage | Array<File>;
language: Ref<string>; language: Ref<string>;
// licenses: Array<number>; // licenses: Array<number>;
rights: boolean; rights: boolean;
@ -17,15 +17,26 @@ export interface Dataset {
errors?: IErrorMessage; errors?: IErrorMessage;
// async (user): Promise<void>; // async (user): Promise<void>;
subjects: Array<Subject>, subjects: Array<Subject>,
file: File | undefined, files: Array<TestFile> | undefined,
upload: TethysFile // upload: TethysFile
} }
export interface TethysFile {
/** Provides information about files and allows JavaScript in a web page to access their content. */
export interface TestFile extends Blob {
readonly lastModified: number;
readonly name: string;
readonly webkitRelativePath: string;
label: string, label: string,
sorting: number, sorting: number,
} }
// export interface TethysFile {
// label: string,
// sorting: number,
// upload: File,
// }
export interface Subject { export interface Subject {
// id: number; // id: number;
language: string language: string

View File

@ -127,8 +127,8 @@ if (Object.keys(mainService.dataset).length == 0) {
{ value: '', type: 'uncontrolled', language: language.value }, { value: '', type: 'uncontrolled', language: language.value },
{ value: '', type: 'uncontrolled', language: language.value }, { value: '', type: 'uncontrolled', language: language.value },
], ],
file: undefined, files: [],
upload: { label: 'test', sorting: 0 }, // upload: { label: 'test', sorting: 0 },
}; };
// Set the form's current values as the new defaults... // Set the form's current values as the new defaults...
// mainService.setDataset(dataset, language); // mainService.setDataset(dataset, language);
@ -151,8 +151,8 @@ if (Object.keys(mainService.dataset).length == 0) {
embargo_date: mainService.dataset.embargo_date, embargo_date: mainService.dataset.embargo_date,
coverage: mainService.dataset.coverage, coverage: mainService.dataset.coverage,
subjects: mainService.dataset.subjects, subjects: mainService.dataset.subjects,
file: mainService.dataset.file, files: mainService.dataset.files,
upload: mainService.dataset.upload, // upload: mainService.dataset.upload,
}; };
for (let index in mainService.dataset.titles) { for (let index in mainService.dataset.titles) {
let title: Title = mainService.dataset.titles[index]; let title: Title = mainService.dataset.titles[index];
@ -217,7 +217,7 @@ watch(depth, (currentValue) => {
// let time= "no_time"; // let time= "no_time";
const isModalActive = ref(false); const isModalActive = ref(false);
const formStep = ref(4); const formStep = ref(1);
const mapOptions: MapOptions = { const mapOptions: MapOptions = {
center: [48.208174, 16.373819], center: [48.208174, 16.373819],
@ -281,7 +281,7 @@ const submit = async () => {
rights: form.rights && form.rights == true ? 'true' : 'false', rights: form.rights && form.rights == true ? 'true' : 'false',
})) }))
.post(route, { .post(route, {
forceFormData: true, // forceFormData: true,
onSuccess: () => { onSuccess: () => {
// console.log(form.data()); // console.log(form.data());
// mainService.clearDataset(); // mainService.clearDataset();
@ -324,7 +324,7 @@ const submit = async () => {
{ value: '', type: 'uncontrolled', language: language.value }, { value: '', type: 'uncontrolled', language: language.value },
{ value: '', type: 'uncontrolled', language: language.value }, { value: '', type: 'uncontrolled', language: language.value },
], ],
file: undefined, files: [] as Array<File>,
upload: { label: 'test', sorting: 0 }, upload: { label: 'test', sorting: 0 },
}; };
form = useForm<Dataset>(dataset); form = useForm<Dataset>(dataset);
@ -388,15 +388,15 @@ const addKeyword = () => {
form.subjects.push(newSubject); form.subjects.push(newSubject);
}; };
const onChangeFile = (event) => { // const onChangeFile = (event) => {
// let uploadedFile = event.target.files[0]; // // let uploadedFile = event.target.files[0];
let fileName = String(event.target.files[0].name.replace(/\.[^/.]+$/, '')); // let fileName = String(event.target.files[0].name.replace(/\.[^/.]+$/, ''));
form.file = event.target.files[0]; // form.file = event.target.files[0];
form.upload.label = fileName; // form.upload.label = fileName;
// form.upload = file; // // form.upload = file;
// console.log(file.file); // // console.log(file.file);
}; // };
/* /*
Removes a selected keyword Removes a selected keyword
*/ */
@ -1115,7 +1115,7 @@ const onChangeFile = (event) => {
</p> </p>
</div> --> </div> -->
<FileUploadComponent></FileUploadComponent> <FileUploadComponent v-model="form.files"></FileUploadComponent>
<div class="text-red-400 text-sm" v-if="form.errors['file'] && Array.isArray(form.errors['file'])"> <div class="text-red-400 text-sm" v-if="form.errors['file'] && Array.isArray(form.errors['file'])">
{{ form.errors['file'].join(', ') }} {{ form.errors['file'].join(', ') }}

View File

@ -33,38 +33,38 @@ import FormInput from '@/Components/FormInput.vue'; // @/Components/For
// }); // });
export interface IErrorMessage { export interface IErrorMessage {
[key: string]: Array<string>; [key: string]: Array<string>;
} }
@Component({ @Component({
options: { options: {
layout: AuthLayout, layout: AuthLayout,
}, },
name: 'RegisterViewComponent', name: 'RegisterViewComponent',
components: { components: {
NButton, NButton,
FormInput, FormInput,
}, },
}) })
export default class RegisterViewComponent extends Vue { export default class RegisterViewComponent extends Vue {
// Component Property // Component Property
@Prop({ @Prop({
type: Object, type: Object,
default: () => ({}), default: () => ({}),
}) })
errors: IErrorMessage; errors: IErrorMessage;
// Data Property // Data Property
form: InertiaForm<any> = useForm({ form: InertiaForm<any> = useForm({
email: '', email: '',
password: '', password: '',
}); });
results: Array<any> = []; results: Array<any> = [];
// Component method // Component method
public async submit(): Promise<void> { public async submit(): Promise<void> {
// await Inertia.post('/app/register', this.form); // await Inertia.post('/app/register', this.form);
await router.post('/app/register', this.form); await router.post('/app/register', this.form);
} }
} }

View File

@ -1,16 +1,12 @@
// notiwind.ts // notiwind.ts
import { import { createNotifier, NotificationGroup, defineNotificationComponent } from 'notiwind';
createNotifier,
NotificationGroup, export type NotificationSchema = {
defineNotificationComponent,
} from "notiwind";
export type NotificationSchema = {
type: string; type: string;
title?: string; title?: string;
text: string; text: string;
}; };
export const notify = createNotifier<NotificationSchema>(); export const notify = createNotifier<NotificationSchema>();
export const Notification = defineNotificationComponent<NotificationSchema>(); export const Notification = defineNotificationComponent<NotificationSchema>();
export { NotificationGroup }; export { NotificationGroup };

View File

@ -149,9 +149,7 @@ Route.group(() => {
.as('dataset.third.step') .as('dataset.third.step')
.middleware(['auth', 'can:dataset-submit']); .middleware(['auth', 'can:dataset-submit']);
Route.post('/dataset/submit', 'DatasetController.store') Route.post('/dataset/submit', 'DatasetController.store').as('dataset.submit').middleware(['auth', 'can:dataset-submit']);
.as('dataset.submit')
.middleware(['auth', 'can:dataset-submit']);
// Route.get('/user/:id', 'UsersController.show').as('user.show').where('id', Route.matchers.number()); // Route.get('/user/:id', 'UsersController.show').as('user.show').where('id', Route.matchers.number());
// Route.get('/user/:id/edit', 'UsersController.edit').as('user.edit').where('id', Route.matchers.number()); // Route.get('/user/:id/edit', 'UsersController.edit').as('user.edit').where('id', Route.matchers.number());

View File

@ -15,10 +15,10 @@ module.exports = {
'primary': '#22C55E', 'primary': '#22C55E',
'primary-dark': '#DCFCE7', 'primary-dark': '#DCFCE7',
}, },
fontFamily: { // fontFamily: {
sans: ['Inter', ...defaultTheme.fontFamily.sans], // sans: ['Inter', ...defaultTheme.fontFamily.sans],
logo: ['Archivo Black', ...defaultTheme.fontFamily.sans], // logo: ['Archivo Black', ...defaultTheme.fontFamily.sans],
}, // },
zIndex: { zIndex: {
'-1': '-1', '-1': '-1',
}, },

View File

@ -274,8 +274,8 @@ Encore.addLoader({
// loaders: { // loaders: {
// ts: 'ts-loader', // ts: 'ts-loader',
// }, // },
// cacheDirectory: 'C:\\Users\\kaiarn\\Documents\\Software\\tethys.viewer\\node_modules\\.cache\\vue-loader', cacheDirectory: 'C:\\Users\\kaiarn\\Documents\\Software\\tethys.viewer\\node_modules\\.cache\\vue-loader',
// cacheIdentifier: 'f930df3e', cacheIdentifier: 'f930df3e',
babelParserPlugins: ['jsx', 'classProperties', 'decorators-legacy'], babelParserPlugins: ['jsx', 'classProperties', 'decorators-legacy'],
}, },
}) })