- addes @adonisjs/redis fo saving session into redis with redis.ts contract and config
Some checks failed
CI Pipeline / japa-tests (push) Failing after 52s

- npm updated
- added createHashValues and dlete inside File.ts
- added dataset_count property inside Subject.ts
- corrected rotes.ts with correct permissions
This commit is contained in:
Kaimbacher 2023-11-27 17:17:22 +01:00
parent d8bdce1369
commit b6fdfbff41
29 changed files with 496 additions and 201 deletions

View file

@ -10,6 +10,7 @@ import OverlayLayer from '@/Components/OverlayLayer.vue';
// let menu = reactive({});
// menu = computed(() => usePage().props.navigation?.menu);
const layoutService = LayoutService();
</script>

View file

@ -1,6 +1,6 @@
<script setup>
import { ref, computed } from 'vue';
import { Link } from '@inertiajs/vue3';
<script lang="ts" setup>
import { ref, computed, ComputedRef } from 'vue';
import { Link, usePage } from '@inertiajs/vue3';
// import { Link } from '@inertiajs/inertia-vue3';
import { StyleService } from '@/Stores/style';
@ -9,6 +9,7 @@ import { getButtonColor } from '@/colors.js';
import BaseIcon from '@/Components/BaseIcon.vue';
import AsideMenuList from '@/Components/AsideMenuList.vue';
import { stardust } from '@eidellev/adonis-stardust/client';
import type { User } from '@/Dataset';
const props = defineProps({
item: {
@ -18,6 +19,10 @@ const props = defineProps({
isDropdownList: Boolean,
});
const user: ComputedRef<User> = computed(() => {
return usePage().props.authUser as User;
});
const itemRoute = computed(() => (props.item && props.item.route ? stardust.route(props.item.route) : ''));
// const isCurrentRoute = computed(() => (props.item && props.item.route ? stardust.isCurrent(props.item.route): false));
const itemHref = computed(() => (props.item && props.item.href ? props.item.href : ''));
@ -65,12 +70,20 @@ const is = computed(() => {
return 'div';
});
const hasRoles = computed(() => {
if (props.item.roles) {
return user.value.roles.some(role => props.item.roles.includes(role.name));
// return test;
}
return true
});
// props.routeName && stardust.isCurrent(props.routeName) ? props.activeColor : null
</script>
<!-- :target="props.item.target ?? null" -->
<template>
<li>
<li v-if="hasRoles">
<!-- <component :is="itemHref ? 'div' : Link" :href="itemHref ? itemHref : itemRoute" -->
<component
:is="is"

View file

@ -19,10 +19,10 @@ const menuClick = (event, item) => {
<template>
<ul>
<AsideMenuItem
v-for="(item, index) in menu"
v-for="(menuItem, index) in menu"
:key="index"
v-bind:item="item"
:is-dropdown-list="item.children?.length > 0"
v-bind:item="menuItem"
:is-dropdown-list="menuItem.children?.length > 0"
@menu-click="menuClick"
/>
</ul>

View file

@ -250,7 +250,7 @@ class FileUploadComponent extends Vue {
}
@Watch("files", {
deep: true
deep: true //also in case of pushing
})
public propertyWatcher(newItems: Array<TethysFile>) {
// Update sort_order based on the new index when the list is changed
@ -361,10 +361,11 @@ class FileUploadComponent extends Vue {
let localUrl: string = "";
if (file instanceof File) {
localUrl = URL.createObjectURL(file as Blob);
} else if (file.filePath) {
} else if (file.fileData) {
// const blob = new Blob([file.fileData]);
// localUrl = URL.createObjectURL(blob);
const parsed = JSON.parse(file.fileData);
file.fileData = "";
// retrieve the original buffer of data
const buff = Buffer.from(parsed.blob, "base64");
const blob = new Blob([buff], { type: 'application/octet-stream' });

View file

@ -34,7 +34,7 @@ import UserAvatarCurrentUser from '@/Components/UserAvatarCurrentUser.vue';
import BaseIcon from '@/Components/BaseIcon.vue';
import NavBarSearch from '@/Components/NavBarSearch.vue';
import { stardust } from '@eidellev/adonis-stardust/client';
import type User from 'App/Models/User';
import type { User } from '@/Dataset';
// const mainStore = MainService();
// const userName = computed(() =>mainStore.userName);
@ -68,6 +68,9 @@ const menuNavBarToggle = () => {
const menuOpenLg = () => {
layoutStore.isAsideLgActive = true;
};
const userHasRoles = (roleNames): boolean => {
return user.value.roles.some(role => roleNames.includes(role.name));
};
// const logout = () => {
// // router.post(route('logout'))
@ -133,7 +136,7 @@ const logout = async () => {
<NavBarItem :route-name="'admin.account.info'">
<NavBarItemLabel :icon="mdiAccount" label="My Profile" />
</NavBarItem>
<NavBarItem :route-name="'settings'">
<NavBarItem v-if="userHasRoles(['moderator', 'administrator'])" :route-name="'settings'">
<NavBarItemLabel :icon="mdiCogOutline" label="Settings" />
</NavBarItem>
<NavBarItem>

View file

@ -3,13 +3,9 @@
<div class="flex">
<!-- <label for="search-dropdown" class="mb-2 text-sm font-medium text-gray-900 sr-only dark:text-white">Your Email</label> -->
<div class="relative" data-te-dropdown-ref>
<button
id="states-button"
data-dropdown-toggle="dropdown-states"
<button id="states-button" data-dropdown-toggle="dropdown-states"
class="whitespace-nowrap h-12 z-10 inline-flex items-center py-2.5 px-4 text-sm font-medium text-center text-gray-500 bg-gray-100 border border-gray-300 rounded-l-lg hover:bg-gray-200 focus:ring-4 focus:outline-none focus:ring-gray-100 dark:bg-gray-700 dark:hover:bg-gray-600 dark:focus:ring-gray-700 dark:text-white dark:border-gray-600"
type="button"
@click.prevent="showStates"
>
type="button" @click.prevent="showStates">
<!-- <svg aria-hidden="true" class="h-3 mr-2" viewBox="0 0 15 12" fill="none" xmlns="http://www.w3.org/2000/svg">
<rect x="0.5" width="14" height="12" rx="2" fill="white" />
<mask id="mask0_12694_49953" style="mask-type: alpha" maskUnits="userSpaceOnUse" x="0" y="0" width="15" height="12">
@ -69,26 +65,20 @@
</svg> -->
<!-- eng -->
{{ language }}
<svg aria-hidden="true" class="w-4 h-4 ml-1" fill="currentColor" viewBox="0 0 20 20" xmlns="http://www.w3.org/2000/svg">
<path
fill-rule="evenodd"
<svg aria-hidden="true" class="w-4 h-4 ml-1" fill="currentColor" viewBox="0 0 20 20"
xmlns="http://www.w3.org/2000/svg">
<path fill-rule="evenodd"
d="M5.293 7.293a1 1 0 011.414 0L10 10.586l3.293-3.293a1 1 0 111.414 1.414l-4 4a1 1 0 01-1.414 0l-4-4a1 1 0 010-1.414z"
clip-rule="evenodd"
></path>
clip-rule="evenodd"></path>
</svg>
</button>
<!-- class="w-full overflow-visible z-10 bg-white divide-y divide-gray-100 rounded-lg shadow w-44 dark:bg-gray-700"-->
<div
id="dropdown-states"
v-show="statesToggle"
class="absolute z-[1000] float-left m-0 min-w-max list-none overflow-hidden rounded-lg border-none bg-white bg-clip-padding text-left text-base shadow-lg dark:bg-neutral-700"
>
<div id="dropdown-states" v-show="statesToggle"
class="absolute z-[1000] float-left m-0 min-w-max list-none overflow-hidden rounded-lg border-none bg-white bg-clip-padding text-left text-base shadow-lg dark:bg-neutral-700">
<ul class="py-2 text-sm text-gray-700 dark:text-gray-200" aria-labelledby="states-button">
<li v-for="(item, index) in dropDownStates" :key="index" @click.prevent="setLanguage(item)">
<button
type="button"
class="inline-flex w-full px-4 py-2 text-sm text-gray-700 hover:bg-gray-100 dark:text-gray-400 dark:hover:bg-gray-600 dark:hover:text-white"
>
<button type="button"
class="inline-flex w-full px-4 py-2 text-sm text-gray-700 hover:bg-gray-100 dark:text-gray-400 dark:hover:bg-gray-600 dark:hover:text-white">
<div class="inline-flex items-center">
<span v-html="item.svg"></span>
{{ item.name }}
@ -102,57 +92,30 @@
<div class="w-full relative">
<!-- :class="inputElClass" -->
<!-- class="block p-2.5 w-full z-20 text-sm text-gray-900 bg-gray-50 rounded-r-lg border-l-gray-50 border-l-2 border border-gray-300 focus:ring-blue-500 focus:border-blue-500 dark:bg-gray-700 dark:border-l-gray-700 dark:border-gray-600 dark:placeholder-gray-400 dark:text-white dark:focus:border-blue-500" -->
<input
v-model="computedValue"
type="text"
:name="props.name"
autocomplete="off"
:class="inputElClass"
placeholder="Search Keywords..."
required
@input="handleInput"
/>
<input v-model="computedValue" type="text" :name="props.name" autocomplete="off" :class="inputElClass"
placeholder="Search Keywords..." required @input="handleInput" />
<!-- v-model="data.search" -->
<svg
class="w-4 h-4 absolute left-2.5 top-3.5"
v-show="computedValue.length < 2"
xmlns="http://www.w3.org/2000/svg"
fill="none"
viewBox="0 0 24 24"
stroke="currentColor"
>
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M21 21l-6-6m2-5a7 7 0 11-14 0 7 7 0 0114 0z" />
<svg class="w-4 h-4 absolute left-2.5 top-3.5" v-show="computedValue.length < 2"
xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke="currentColor">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
d="M21 21l-6-6m2-5a7 7 0 11-14 0 7 7 0 0114 0z" />
</svg>
<svg
class="w-4 h-4 absolute left-2.5 top-3.5"
v-show="computedValue.length >= 2"
xmlns="http://www.w3.org/2000/svg"
fill="none"
viewBox="0 0 24 24"
stroke="currentColor"
@click="
() => {
<svg class="w-4 h-4 absolute left-2.5 top-3.5" v-show="computedValue.length >= 2"
xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke="currentColor" @click="() => {
computedValue = '';
data.isOpen = false;
}
"
>
">
<path stroke-linecap="round" stroke-linejoin="round" d="M6 18L18 6M6 6l12 12" />
</svg>
</div>
<slot />
</div>
<ul
v-if="data.isOpen"
class="absolute absolute z-[1000] float-left m-0 list-none bg-white dark:bg-slate-800 m-0 max-h-32 overflow-y-auto scroll-smooth min-w-full"
>
<li
class="leading-3 pl-4 py-3 border-b-2 line border-gray-100 relative cursor-pointer hover:bg-yellow-50 hover:text-gray-900"
v-for="(item, index) in data.results"
@click.prevent="setResult(item)"
:key="index"
>
<ul v-if="data.isOpen"
class="absolute absolute z-[1000] float-left m-0 list-none bg-white dark:bg-slate-800 m-0 max-h-32 overflow-y-auto scroll-smooth min-w-full">
<li class="leading-3 pl-4 py-3 border-b-2 line border-gray-100 relative cursor-pointer hover:bg-yellow-50 hover:text-gray-900"
v-for="(item, index) in data.results" @click.prevent="setResult(item)" :key="index">
<!-- <a href="${BASE}?uri=${a.s.value}&lang=${USER_LANG}"> -->
<strong class="text-sm"> {{ item.title.value }}</strong>
<!-- </a> -->
@ -406,7 +369,10 @@ function setResult(item) {
computedValue.value = item.title.value;
clear();
// this.$emit('person', person);
emit('subject', language.value);
emit('subject', {
language: language.value,
uri: item.s.value
});
}
function clear() {

View file

@ -8,7 +8,6 @@ import { mdiTrashCan } from '@mdi/js';
import BaseLevel from '@/Components/BaseLevel.vue';
import BaseButtons from '@/Components/BaseButtons.vue';
import BaseButton from '@/Components/BaseButton.vue';
// import Person from 'App/Models/Person';
import { Subject } from '@/Dataset';
// import FormField from '@/Components/FormField.vue';
import FormControl from '@/Components/FormControl.vue';
@ -120,7 +119,7 @@ const removeItem = (key) => {
<UserAvatar :username="client.value" class="w-24 h-24 mx-auto lg:w-6 lg:h-6" />
</td> -->
<td data-label="Type" scope="row">
<FormControl required v-model="item.type" :type="'select'" placeholder="[Enter Language]" :options="props.subjectTypes">
<FormControl required v-model="item.type" @update:modelValue="() => {item.external_key = undefined; item.value= '';}" :type="'select'" placeholder="[Enter Language]" :options="props.subjectTypes">
<div class="text-red-400 text-sm" v-if="errors[`subjects.${index}.type`]">
{{ errors[`subjects.${index}.type`].join(', ') }}
</div>
@ -132,8 +131,9 @@ const removeItem = (key) => {
v-if="item.type !== 'uncontrolled'"
v-model="item.value"
@subject="
(language) => {
item.language = language;
(result) => {
item.language = result.language;
item.external_key = result.uri;
}
"
>

View file

@ -1,4 +1,24 @@
import { Ref } from 'vue';
import { DateTime } from 'luxon';
export interface User {
id: number;
login: string;
email: string;
password: string;
createdAt: DateTime;
updatedAt: DateTime;
roles: Array<Role>;
}
export interface Role {
id: number;
display_name: string;
name: string;
description: string;
created_at: DateTime;
updated_at: DateTime;
}
export interface Dataset {
[key: string]:
@ -75,11 +95,12 @@ export interface TethysFile {
}
export interface Subject {
// id: number;
id?: number;
language: string;
type: string;
value: string;
external_key?: string;
dataset_count: number;
}
export interface DatasetReference {
// id: number;

View file

@ -290,14 +290,15 @@ 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 });
// });
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,
files: files,
rights: form.rights && form.rights == true ? 'true' : 'false',
}))
.post(route, {
@ -404,7 +405,7 @@ const onMapInitialized = (newItem) => {
adds a new Keyword
*/
const addKeyword = () => {
let newSubject: Subject = { value: 'test', language: '', type: 'uncontrolled' };
let newSubject: Subject = { value: 'test', language: '', type: 'uncontrolled', dataset_count: 0 };
//this.dataset.files.push(uploadedFiles[i]);
form.subjects.push(newSubject);
};

View file

@ -8,11 +8,15 @@
color="white" rounded-full small />
</SectionTitleLineWithButton>
<NotificationBar v-if="flash.message" color="success" :icon="mdiAlertBoxOutline">
{{ flash.message }}
</NotificationBar>
<FormValidationErrors v-bind:errors="errors" />
<!-- max-w-2xl max-width: 42rem; /* 672px */ -->
<!-- <div class="max-w-2xl mx-auto"> -->
<CardBox :form="true">
<FormValidationErrors v-bind:errors="errors" />
<!-- <FormValidationErrors v-bind:errors="errors" /> -->
<div class="mb-4">
<!-- <label for="title" class="block text-gray-700 font-bold mb-2">Title:</label>
<input
@ -218,8 +222,8 @@
<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 v-if="item.id == undefined"
@click.prevent="removeDescription(index)" />
<BaseButton color="danger" :icon="mdiTrashCan" small
v-if="item.id == undefined" @click.prevent="removeDescription(index)" />
</BaseButtons>
</td>
</tr>
@ -469,7 +473,8 @@
// import { Component, Vue, Prop, Setup, toNative } from 'vue-facing-decorator';
// import AuthLayout from '@/Layouts/Auth.vue';
import LayoutAuthenticated from '@/Layouts/LayoutAuthenticated.vue';
import { useForm, Head } from '@inertiajs/vue3';
import { useForm, Head, usePage } from '@inertiajs/vue3';
import { computed, ComputedRef } from 'vue';
// import { ref } from 'vue';
// import { MainService } from '@/Stores/main';
// import FormInput from '@/Components/FormInput.vue'; // @/Components/FormInput.vue'
@ -502,8 +507,10 @@ import {
mdiTrashCan,
mdiBookOpenPageVariant,
mdiEarthPlus,
mdiAlertBoxOutline,
} from '@mdi/js';
import { notify } from '@/notiwind';
import NotificationBar from '@/Components/NotificationBar.vue';
const props = defineProps({
errors: {
@ -557,6 +564,11 @@ const props = defineProps({
});
const flash: ComputedRef<any> = computed(() => {
// let test = usePage();
// console.log(test);
return usePage().props.flash;
});
// const projects = reactive([]);
@ -639,11 +651,17 @@ const submit = async (): Promise<void> => {
form.licenses = form.licenses.map((obj) => obj.id.toString());
}
// const files = form.files.map((obj) => {
// return new File([obj.blob], obj.label, { type: obj.type, lastModified: obj.lastModified });
// });
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 });
let file = new File([obj.blob], `${obj.label}?sortOrder=${obj.sort_order}`, { type: obj.type, lastModified: obj.lastModified });
// fileUploads[obj.sort_order] = file;
fileUploads.push(file);
} else {
// return normal request input
fileInputs.push(obj);
@ -723,7 +741,7 @@ const onAddContributor = (person) => {
};
const addKeyword = () => {
let newSubject: Subject = { value: 'test', language: '', type: 'uncontrolled' };
let newSubject: Subject = { value: 'test', language: '', type: 'uncontrolled', dataset_count: 0 };
//this.dataset.files.push(uploadedFiles[i]);
form.subjects.push(newSubject);
};
@ -743,7 +761,7 @@ const onMapInitialized = (newItem) => {
};
</script>
<style>
<style scoped>
.max-w-2xl {
max-width: 2xl;
}

View file

@ -118,7 +118,7 @@ const flash: ComputedRef<any> = computed(() => {
:route-name="stardust.route('dataset.release', [dataset.id])" color="info"
:icon="mdiLockOpen" :label="'Release'" small />
<!-- && (dataset.server_state === 'inprogress' || dataset.server_state === 'rejected_editor')" -->
<BaseButton v-if="can.edit" :route-name="stardust.route('dataset.edit', [dataset.id])"
<BaseButton :route-name="stardust.route('dataset.edit', [dataset.id])"
color="info" :icon="mdiSquareEditOutline" :label="'Edit'" small />
<!-- @click="destroy(dataset.id)" -->
<BaseButton v-if="can.delete" color="danger"

View file

@ -215,8 +215,8 @@ class EditComponent extends Vue {
}
}
public addKeyword() {
let newSubject: Subject = { value: 'test', language: '', type: 'uncontrolled' };
public addKeyword() {
let newSubject: Subject = { value: 'test', language: '', type: 'uncontrolled', dataset_count: 0 };
//this.dataset.files.push(uploadedFiles[i]);
this.form.subjects.push(newSubject);
}

View file

@ -36,6 +36,7 @@ export default [
// route: 'dataset.create',
icon: mdiDatabasePlus,
label: 'Submitter',
permissions: ['submitter'],
children: [
{
route: 'dataset.list',