- add validator for checking languages of translated titles and description
- add notification messages via notiwind.ts - add Project.ts - add new component TabelPersons.vue - add additional routes - npm updates
This commit is contained in:
parent
59a99ff3c8
commit
080c21126b
24 changed files with 979 additions and 349 deletions
|
@ -1,37 +1,34 @@
|
|||
<script>
|
||||
import { h, defineComponent } from 'vue'
|
||||
import { h, defineComponent } from 'vue';
|
||||
|
||||
export default defineComponent({
|
||||
name: 'BaseLevel',
|
||||
props: {
|
||||
mobile: Boolean,
|
||||
type: {
|
||||
type: String,
|
||||
default: 'justify-between'
|
||||
}
|
||||
},
|
||||
render () {
|
||||
const parentClass = [this.type, 'items-center']
|
||||
name: 'BaseLevel',
|
||||
props: {
|
||||
mobile: Boolean,
|
||||
type: {
|
||||
type: String,
|
||||
default: 'justify-between',
|
||||
},
|
||||
},
|
||||
render() {
|
||||
const parentClass = [this.type, 'items-center'];
|
||||
|
||||
const parentMobileClass = ['flex']
|
||||
const parentMobileClass = ['flex'];
|
||||
|
||||
const parentBaseClass = ['block', 'md:flex']
|
||||
const parentBaseClass = ['block', 'md:flex'];
|
||||
|
||||
const childBaseClass = ['flex', 'shrink-0', 'grow-0', 'items-center', 'justify-center']
|
||||
const childBaseClass = ['flex', 'shrink-0', 'grow-0', 'items-center', 'justify-center'];
|
||||
|
||||
return h(
|
||||
'div',
|
||||
{ class: parentClass.concat(this.mobile ? parentMobileClass : parentBaseClass) },
|
||||
this.$slots.default().map((element, index) => {
|
||||
const childClass = (!this.mobile && this.$slots.default().length > index + 1)
|
||||
? childBaseClass.concat(['mb-6', 'md:mb-0'])
|
||||
: childBaseClass
|
||||
return h(
|
||||
'div',
|
||||
{ class: parentClass.concat(this.mobile ? parentMobileClass : parentBaseClass) },
|
||||
this.$slots.default().map((element, index) => {
|
||||
const childClass =
|
||||
!this.mobile && this.$slots.default().length > index + 1 ? childBaseClass.concat(['mb-6', 'md:mb-0']) : childBaseClass;
|
||||
|
||||
return h('div', { class: childClass }, [
|
||||
element
|
||||
])
|
||||
})
|
||||
)
|
||||
}
|
||||
})
|
||||
return h('div', { class: childClass }, [element]);
|
||||
}),
|
||||
);
|
||||
},
|
||||
});
|
||||
</script>
|
||||
|
|
|
@ -112,7 +112,7 @@ if (props.ctrlKFocus) {
|
|||
<div class="relative">
|
||||
<select v-if="computedType === 'select'" :id="id" v-model="computedValue" :name="name" :class="inputElClass">
|
||||
<option v-if="placeholder" class="text-opacity-25" value="" disabled selected>{{ placeholder }}</option>
|
||||
<option v-for="option in options" :key="option.id ?? option" :value="option.value ?? option">
|
||||
<option v-for="(option, index) in options" :key="index" :value="option.value ?? index">
|
||||
{{ option.label ?? option }}
|
||||
</option>
|
||||
</select>
|
||||
|
|
66
resources/js/Components/NotificationToast.vue
Normal file
66
resources/js/Components/NotificationToast.vue
Normal file
|
@ -0,0 +1,66 @@
|
|||
<script setup lang="ts">
|
||||
|
||||
import { Notification, NotificationGroup } from '@/notiwind';
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<NotificationGroup position="bottom">
|
||||
<div class="z-50 fixed inset-x-0 top-0 flex items-start justify-end p-6 px-4 py-6 pointer-events-none">
|
||||
<div class="w-full max-w-sm">
|
||||
<Notification
|
||||
v-slot="{ notifications }"
|
||||
enter="transform ease-out duration-300 transition"
|
||||
enter-from="translate-y-2 opacity-0 sm:translate-y-0 sm:translate-x-4"
|
||||
enter-to="translate-y-0 opacity-100 sm:translate-x-0"
|
||||
leave="transition ease-in duration-500"
|
||||
leave-from="opacity-100"
|
||||
leave-to="opacity-0"
|
||||
move="transition duration-500"
|
||||
move-delay="delay-300"
|
||||
>
|
||||
<div v-for="notification in notifications" :key="notification.id">
|
||||
<div
|
||||
v-if="notification.type === 'info'"
|
||||
class="flex w-full max-w-sm mx-auto mt-4 overflow-hidden bg-white rounded-lg shadow-md"
|
||||
>
|
||||
<div class="flex items-center justify-center w-12 bg-blue-500">
|
||||
<svg class="w-6 h-6 text-white fill-current" viewBox="0 0 40 40" xmlns="http://www.w3.org/2000/svg">
|
||||
<path
|
||||
d="M20 3.33331C10.8 3.33331 3.33337 10.8 3.33337 20C3.33337 29.2 10.8 36.6666 20 36.6666C29.2 36.6666 36.6667 29.2 36.6667 20C36.6667 10.8 29.2 3.33331 20 3.33331ZM21.6667 28.3333H18.3334V25H21.6667V28.3333ZM21.6667 21.6666H18.3334V11.6666H21.6667V21.6666Z"
|
||||
/>
|
||||
</svg>
|
||||
</div>
|
||||
|
||||
<div class="px-4 py-2 -mx-3">
|
||||
<div class="mx-3">
|
||||
<span class="font-semibold text-blue-500">{{ notification.title }}</span>
|
||||
<p class="text-sm text-gray-600">T{{ notification.text }}</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div
|
||||
class="flex w-full max-w-sm mx-auto mt-4 overflow-hidden bg-white rounded-lg shadow-md"
|
||||
v-if="notification.type === 'warning'"
|
||||
>
|
||||
<div class="flex items-center justify-center w-12 bg-yellow-500">
|
||||
<svg class="w-6 h-6 text-white fill-current" viewBox="0 0 40 40" xmlns="http://www.w3.org/2000/svg">
|
||||
<path
|
||||
d="M20 3.33331C10.8 3.33331 3.33337 10.8 3.33337 20C3.33337 29.2 10.8 36.6666 20 36.6666C29.2 36.6666 36.6667 29.2 36.6667 20C36.6667 10.8 29.2 3.33331 20 3.33331ZM21.6667 28.3333H18.3334V25H21.6667V28.3333ZM21.6667 21.6666H18.3334V11.6666H21.6667V21.6666Z"
|
||||
/>
|
||||
</svg>
|
||||
</div>
|
||||
|
||||
<div class="px-4 py-2 -mx-3">
|
||||
<div class="mx-3">
|
||||
<span class="font-semibold text-yellow-500">{{ notification.title }}</span>
|
||||
<p class="text-sm text-gray-600">{{ notification.text }}</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</Notification>
|
||||
</div>
|
||||
</div>
|
||||
</NotificationGroup>
|
||||
</template>
|
|
@ -1,56 +1,42 @@
|
|||
<script setup>
|
||||
import { computed } from 'vue'
|
||||
import { colorsBgLight, colorsOutline } from '@/colors.js'
|
||||
import BaseIcon from '@/Components/BaseIcon.vue'
|
||||
import { computed } from 'vue';
|
||||
import { colorsBgLight, colorsOutline } from '@/colors.js';
|
||||
import BaseIcon from '@/Components/BaseIcon.vue';
|
||||
|
||||
const props = defineProps({
|
||||
text: {
|
||||
type: String,
|
||||
required: true
|
||||
},
|
||||
type: {
|
||||
type: String,
|
||||
required: true
|
||||
},
|
||||
icon: {
|
||||
type: String,
|
||||
default: null
|
||||
},
|
||||
small: Boolean,
|
||||
outline: Boolean,
|
||||
wrapped: Boolean
|
||||
})
|
||||
text: {
|
||||
type: String,
|
||||
required: true,
|
||||
},
|
||||
type: {
|
||||
type: String,
|
||||
required: true,
|
||||
},
|
||||
icon: {
|
||||
type: String,
|
||||
default: null,
|
||||
},
|
||||
small: Boolean,
|
||||
outline: Boolean,
|
||||
wrapped: Boolean,
|
||||
});
|
||||
|
||||
const componentClass = computed(() => {
|
||||
const baseColor = props.outline ? colorsOutline[props.type] : colorsBgLight[props.type]
|
||||
const baseColor = props.outline ? colorsOutline[props.type] : colorsBgLight[props.type];
|
||||
|
||||
const base = [
|
||||
'border',
|
||||
props.small ? 'py-1 px-4 text-xs rounded-full' : 'py-2 px-6 rounded-full',
|
||||
baseColor
|
||||
]
|
||||
const base = ['border', props.small ? 'py-1 px-4 text-xs rounded-full' : 'py-2 px-6 rounded-full', baseColor];
|
||||
|
||||
if (!props.wrapped) {
|
||||
base.push(props.small ? 'mr-1.5' : 'mr-3', 'last:mr-0')
|
||||
}
|
||||
if (!props.wrapped) {
|
||||
base.push(props.small ? 'mr-1.5' : 'mr-3', 'last:mr-0');
|
||||
}
|
||||
|
||||
return base
|
||||
})
|
||||
return base;
|
||||
});
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div
|
||||
class="inline-flex items-center capitalize"
|
||||
:class="componentClass"
|
||||
>
|
||||
<BaseIcon
|
||||
v-if="icon"
|
||||
:path="icon"
|
||||
h="h-4"
|
||||
w="w-4"
|
||||
:class="small ? 'mr-1' : 'mr-2'"
|
||||
:size="small ? 14 : 16"
|
||||
/>
|
||||
<span>{{ text }}</span>
|
||||
</div>
|
||||
<div class="inline-flex items-center capitalize" :class="componentClass">
|
||||
<BaseIcon v-if="icon" :path="icon" h="h-4" w="w-4" :class="small ? 'mr-1' : 'mr-2'" :size="small ? 14 : 16" />
|
||||
<span>{{ text }}</span>
|
||||
</div>
|
||||
</template>
|
||||
|
|
|
@ -17,68 +17,72 @@
|
|||
</ul> -->
|
||||
|
||||
<!-- <div class="flex-col justify-center relative"> -->
|
||||
<div class="relative">
|
||||
<input
|
||||
v-model="data.search"
|
||||
type="text"
|
||||
:class="inputElClass"
|
||||
:name="props.name"
|
||||
:placeholder="placeholder"
|
||||
autocomplete="off"
|
||||
@keydown.down="onArrowDown"
|
||||
@keydown.up="onArrowUp"
|
||||
/>
|
||||
<svg
|
||||
class="w-4 h-4 absolute left-2.5 top-3.5"
|
||||
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>
|
||||
</div>
|
||||
<!-- <ul v-if="data.isOpen" class="bg-white border border-gray-100 w-full mt-2 max-h-28 overflow-y-auto"> -->
|
||||
<!-- :ref="(el) => { ul[i] = el }" -->
|
||||
<ul v-if="data.isOpen" class="bg-white dark:bg-slate-800 w-full mt-2 max-h-28 overflow-y-auto scroll-smooth">
|
||||
<li
|
||||
class="pl-8 pr-2 py-1 border-b-2 border-gray-100 relative cursor-pointer hover:bg-yellow-50 hover:text-gray-900"
|
||||
:class="{
|
||||
'bg-yellow-50 text-gray-900': i == selectedIndex,
|
||||
}"
|
||||
v-for="(result, i) in data.results"
|
||||
:key="i"
|
||||
:ref="
|
||||
(el: HTMLLIElement | null) => {
|
||||
<div class="mb-6 mx-2 mt-2">
|
||||
<div class="relative">
|
||||
<input
|
||||
v-model="data.search"
|
||||
type="text"
|
||||
:class="inputElClass"
|
||||
:name="props.name"
|
||||
:placeholder="placeholder"
|
||||
autocomplete="off"
|
||||
@keydown.down="onArrowDown"
|
||||
@keydown.up="onArrowUp"
|
||||
@keydown.enter="onEnter"
|
||||
/>
|
||||
<svg
|
||||
class="w-4 h-4 absolute left-2.5 top-3.5"
|
||||
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>
|
||||
</div>
|
||||
<!-- <ul v-if="data.isOpen" class="bg-white border border-gray-100 w-full mt-2 max-h-28 overflow-y-auto"> -->
|
||||
<!-- :ref="(el) => { ul[i] = el }" -->
|
||||
<ul v-if="data.isOpen" class="bg-white dark:bg-slate-800 w-full mt-2 max-h-28 overflow-y-auto scroll-smooth">
|
||||
<li
|
||||
class="pl-8 pr-2 py-1 border-b-2 border-gray-100 relative cursor-pointer hover:bg-yellow-50 hover:text-gray-900"
|
||||
:class="{
|
||||
'bg-yellow-50 text-gray-900': i == selectedIndex,
|
||||
}"
|
||||
v-for="(result, i) in data.results"
|
||||
:key="i"
|
||||
@click.prevent="setResult(result)"
|
||||
:ref="
|
||||
(el: HTMLLIElement) => {
|
||||
ul[i] = el;
|
||||
}
|
||||
"
|
||||
>
|
||||
<svg class="absolute w-4 h-4 left-2 top-2" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" fill="currentColor">
|
||||
<path
|
||||
fill-rule="evenodd"
|
||||
d="M12.293 5.293a1 1 0 011.414 0l4 4a1 1 0 010 1.414l-4 4a1 1 0 01-1.414-1.414L14.586 11H3a1 1 0 110-2h11.586l-2.293-2.293a1 1 0 010-1.414z"
|
||||
clip-rule="evenodd"
|
||||
/>
|
||||
</svg>
|
||||
<!-- <b>Gar</b>{{ result.name }} -->
|
||||
>
|
||||
<svg class="absolute w-4 h-4 left-2 top-2" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" fill="currentColor">
|
||||
<path
|
||||
fill-rule="evenodd"
|
||||
d="M12.293 5.293a1 1 0 011.414 0l4 4a1 1 0 010 1.414l-4 4a1 1 0 01-1.414-1.414L14.586 11H3a1 1 0 110-2h11.586l-2.293-2.293a1 1 0 010-1.414z"
|
||||
clip-rule="evenodd"
|
||||
/>
|
||||
</svg>
|
||||
<!-- <b>Gar</b>{{ result.name }} -->
|
||||
|
||||
<!-- <span>
|
||||
<!-- <span>
|
||||
{{ makeBold(result.name) }}
|
||||
</span> -->
|
||||
<span
|
||||
v-for="(item, index) in makeBold(result.name)"
|
||||
:key="index"
|
||||
:class="{
|
||||
'font-bold': data.search.toLowerCase().includes(item.toLowerCase()),
|
||||
'is-active': index == selectedIndex,
|
||||
}"
|
||||
>
|
||||
{{ item }}
|
||||
</span>
|
||||
</li>
|
||||
</ul>
|
||||
<!-- </div> -->
|
||||
<span
|
||||
v-for="(item, index) in makeBold(result.name)"
|
||||
:key="index"
|
||||
:class="{
|
||||
'font-bold': data.search.toLowerCase().includes(item.toLowerCase()),
|
||||
'is-active': index == selectedIndex,
|
||||
}"
|
||||
>
|
||||
{{ item }}
|
||||
</span>
|
||||
<span> - {{ result.email }}</span>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
|
@ -119,6 +123,8 @@ let props = defineProps({
|
|||
ctrlKFocus: Boolean,
|
||||
});
|
||||
|
||||
const emit = defineEmits(['person']);
|
||||
|
||||
const inputElClass = computed(() => {
|
||||
const base = [
|
||||
'px-3 py-2 max-w-full focus:ring focus:outline-none border-gray-700 rounded w-full',
|
||||
|
@ -143,11 +149,11 @@ let data = reactive({
|
|||
let error = ref('');
|
||||
let selectedIndex: Ref<number> = ref(0);
|
||||
// const listItem = ref(null);
|
||||
const ul: Ref<Array<HTMLLIElement | null>> = ref([]);
|
||||
const ul: Ref<Array<HTMLLIElement>> = ref([]);
|
||||
|
||||
watch(selectedIndex, (selectedIndex) => {
|
||||
watch(selectedIndex, (selectedIndex: number) => {
|
||||
if (selectedIndex != null && ul.value != null) {
|
||||
const currentElement: HTMLLIElement | null = ul.value[selectedIndex];
|
||||
const currentElement: HTMLLIElement = ul.value[selectedIndex];
|
||||
currentElement &&
|
||||
currentElement.scrollIntoView({
|
||||
behavior: 'smooth',
|
||||
|
@ -161,13 +167,6 @@ watch(search, async () => {
|
|||
await onChange();
|
||||
});
|
||||
|
||||
// function clear() {
|
||||
// data.search = "";
|
||||
// data.isOpen = false;
|
||||
// data.results = [];
|
||||
// error.value = "";
|
||||
// // this.$emit("clear");
|
||||
// }
|
||||
|
||||
// function onChange() {
|
||||
// if (!props.source || !data.search) {
|
||||
|
@ -278,4 +277,30 @@ function onArrowUp() {
|
|||
selectedIndex.value = selectedIndex.value == 0 || selectedIndex.value == -1 ? data.results.length - 1 : selectedIndex.value - 1;
|
||||
}
|
||||
}
|
||||
|
||||
function setResult(person) {
|
||||
// this.search = person.full_name;
|
||||
clear();
|
||||
// this.$emit('person', person);
|
||||
emit('person', person);
|
||||
}
|
||||
|
||||
function clear() {
|
||||
data.search = '';
|
||||
data.isOpen = false;
|
||||
data.results = [];
|
||||
error.value = '';
|
||||
// this.$emit("clear");
|
||||
}
|
||||
|
||||
function onEnter() {
|
||||
if (Array.isArray(data.results) && data.results.length && selectedIndex.value !== -1 && selectedIndex.value < data.results.length) {
|
||||
//this.display = this.results[this.selectedIndex];
|
||||
const person = data.results[selectedIndex.value];
|
||||
// this.$emit('person', person);
|
||||
emit('person', person);
|
||||
clear();
|
||||
selectedIndex.value = -1;
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
|
157
resources/js/Components/TablePersons.vue
Normal file
157
resources/js/Components/TablePersons.vue
Normal file
|
@ -0,0 +1,157 @@
|
|||
<script setup lang="ts">
|
||||
import { computed, ref } from 'vue';
|
||||
// import { MainService } from '@/Stores/main';
|
||||
import { StyleService } from '@/Stores/style';
|
||||
import { mdiTrashCan } from '@mdi/js';
|
||||
// import CardBoxModal from '@/Components/CardBoxModal.vue';
|
||||
// import TableCheckboxCell from '@/Components/TableCheckboxCell.vue';
|
||||
import BaseLevel from '@/Components/BaseLevel.vue';
|
||||
import BaseButtons from '@/Components/BaseButtons.vue';
|
||||
import BaseButton from '@/Components/BaseButton.vue';
|
||||
import UserAvatar from '@/Components/UserAvatar.vue';
|
||||
// import Person from 'App/Models/Person';
|
||||
import { Person } from '@/Stores/main';
|
||||
|
||||
const props = defineProps({
|
||||
checkable: Boolean,
|
||||
persons: {
|
||||
type: Array<Person>,
|
||||
default: () => [],
|
||||
},
|
||||
});
|
||||
|
||||
const styleService = StyleService();
|
||||
// const mainService = MainService();
|
||||
const items = computed(() => props.persons);
|
||||
|
||||
// const isModalActive = ref(false);
|
||||
// const isModalDangerActive = ref(false);
|
||||
const perPage = ref(5);
|
||||
const currentPage = ref(0);
|
||||
// const checkedRows = ref([]);
|
||||
|
||||
const itemsPaginated = computed(() => items.value.slice(perPage.value * currentPage.value, perPage.value * (currentPage.value + 1)));
|
||||
|
||||
const numPages = computed(() => Math.ceil(items.value.length / perPage.value));
|
||||
|
||||
const currentPageHuman = computed(() => currentPage.value + 1);
|
||||
|
||||
const pagesList = computed(() => {
|
||||
const pagesList: Array<number> = [];
|
||||
|
||||
for (let i = 0; i < numPages.value; i++) {
|
||||
pagesList.push(i);
|
||||
}
|
||||
|
||||
return pagesList;
|
||||
});
|
||||
|
||||
const removeAuthor = (key) => {
|
||||
items.value.splice(key, 1);
|
||||
};
|
||||
|
||||
// const remove = (arr, cb) => {
|
||||
// const newArr = [];
|
||||
|
||||
// arr.forEach((item) => {
|
||||
// if (!cb(item)) {
|
||||
// newArr.push(item);
|
||||
// }
|
||||
// });
|
||||
|
||||
// return newArr;
|
||||
// };
|
||||
|
||||
// const checked = (isChecked, client) => {
|
||||
// if (isChecked) {
|
||||
// checkedRows.value.push(client);
|
||||
// } else {
|
||||
// checkedRows.value = remove(checkedRows.value, (row) => row.id === client.id);
|
||||
// }
|
||||
// };
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<!-- <CardBoxModal v-model="isModalActive" title="Sample modal">
|
||||
<p>Lorem ipsum dolor sit amet <b>adipiscing elit</b></p>
|
||||
<p>This is sample modal</p>
|
||||
</CardBoxModal>
|
||||
|
||||
<CardBoxModal v-model="isModalDangerActive" large-title="Please confirm" button="danger" has-cancel>
|
||||
<p>Lorem ipsum dolor sit amet <b>adipiscing elit</b></p>
|
||||
<p>This is sample modal</p>
|
||||
</CardBoxModal> -->
|
||||
|
||||
<!-- <div v-if="checkedRows.length" class="p-3 bg-gray-100/50 dark:bg-slate-800">
|
||||
<span v-for="checkedRow in checkedRows" :key="checkedRow.id"
|
||||
class="inline-block px-2 py-1 rounded-sm mr-2 text-sm bg-gray-100 dark:bg-slate-700">
|
||||
{{ checkedRow.name }}
|
||||
</span>
|
||||
</div> -->
|
||||
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<!-- <th v-if="checkable" /> -->
|
||||
<th class="hidden lg:table-cell"></th>
|
||||
<th>Name</th>
|
||||
<th>Email</th>
|
||||
<!-- <th>Name Type</th> -->
|
||||
<!-- <th>Progress</th> -->
|
||||
<th>Created</th>
|
||||
<th />
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr v-for="(client, index) in itemsPaginated" :key="client.id">
|
||||
<!-- <TableCheckboxCell v-if="checkable" @checked="checked($event, client)" /> -->
|
||||
<td class="border-b-0 lg:w-6 before:hidden hidden lg:table-cell">
|
||||
<UserAvatar :username="client.name" class="w-24 h-24 mx-auto lg:w-6 lg:h-6" />
|
||||
</td>
|
||||
<td data-label="Name">
|
||||
{{ client.name }}
|
||||
</td>
|
||||
<td data-label="Email">
|
||||
{{ client.email }}
|
||||
</td>
|
||||
<!-- <td data-label="Name Type">
|
||||
{{ client.name_type }}
|
||||
</td> -->
|
||||
<!-- <td data-label="Orcid">
|
||||
{{ client.identifier_orcid }}
|
||||
</td> -->
|
||||
<!-- <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">
|
||||
{{ client.progress }}
|
||||
</progress>
|
||||
</td> -->
|
||||
<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>
|
||||
</td>
|
||||
<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 @click.prevent="removeAuthor(index)" />
|
||||
</BaseButtons>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
<!-- :class="[ pagesList.length > 1 ? 'block' : 'hidden']" -->
|
||||
<div class="p-3 lg:px-6 border-t border-gray-100 dark:border-slate-800" >
|
||||
<BaseLevel>
|
||||
<BaseButtons>
|
||||
<BaseButton
|
||||
v-for="page in pagesList"
|
||||
:key="page"
|
||||
:active="page === currentPage"
|
||||
:label="page + 1"
|
||||
small
|
||||
:outline="styleService.darkMode"
|
||||
@click="currentPage = page"
|
||||
/>
|
||||
</BaseButtons>
|
||||
<small>Page {{ currentPageHuman }} of {{ numPages }}</small>
|
||||
</BaseLevel>
|
||||
</div>
|
||||
</template>
|
|
@ -1,15 +1,12 @@
|
|||
<script setup>
|
||||
import { computed } from 'vue'
|
||||
import { computed } from 'vue';
|
||||
// import { usePage } from '@inertiajs/vue3'
|
||||
import { usePage } from '@inertiajs/vue3'
|
||||
import UserAvatar from '@/Components/UserAvatar.vue'
|
||||
import { usePage } from '@inertiajs/vue3';
|
||||
import UserAvatar from '@/Components/UserAvatar.vue';
|
||||
|
||||
const userName = computed(() => usePage().props.auth?.user.name)
|
||||
const userName = computed(() => usePage().props.auth?.user.name);
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<UserAvatar
|
||||
v-bind:username="'userName'"
|
||||
api="initials"
|
||||
/>
|
||||
</template>
|
||||
<UserAvatar v-bind:username="'userName'" api="initials" />
|
||||
</template>
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
import { Ref } from 'vue';
|
||||
|
||||
export interface Dataset {
|
||||
[key: string]: string | Ref<string>| boolean | Array<Title> | (IErrorMessage | undefined);
|
||||
[key: string]: string | Ref<string>| boolean | Array<Title> | Array<Description>| Array<Person> | number | (IErrorMessage | undefined);
|
||||
language: Ref<string>;
|
||||
// licenses: Array<number>;
|
||||
rights: boolean;
|
||||
|
@ -9,6 +9,10 @@ export interface Dataset {
|
|||
creating_corporation: string;
|
||||
titles: Array<Title>;
|
||||
descriptions: Array<Description>;
|
||||
authors: Array<Person>;
|
||||
contributors: Array<Person>;
|
||||
project_id?: number;
|
||||
embargo_date?: string,
|
||||
errors?: IErrorMessage;
|
||||
// async (user): Promise<void>;
|
||||
}
|
||||
|
@ -25,6 +29,16 @@ export interface Description {
|
|||
language: string | Ref<string>;
|
||||
}
|
||||
|
||||
export interface Person {
|
||||
id: number;
|
||||
name: string;
|
||||
email: string;
|
||||
name_type: string;
|
||||
identifier_orcid: string;
|
||||
datasetCount: string;
|
||||
created_at: string;
|
||||
}
|
||||
|
||||
interface IErrorMessage {
|
||||
[key: string]: Array<string>;
|
||||
}
|
|
@ -4,6 +4,7 @@ import { StyleService } from '@/Stores/style';
|
|||
import NavBar from '@/Components/NavBar.vue';
|
||||
import AsideMenu from '@/Components/AsideMenu.vue';
|
||||
import FooterBar from '@/Components/FooterBar.vue';
|
||||
import NotificationToast from '@/Components/NotificationToast.vue';
|
||||
|
||||
const styleService = StyleService();
|
||||
|
||||
|
@ -34,4 +35,5 @@ const layoutService = LayoutService();
|
|||
<FooterBar />
|
||||
</div>
|
||||
</div>
|
||||
<NotificationToast></NotificationToast>
|
||||
</template>
|
||||
|
|
|
@ -1,8 +1,16 @@
|
|||
<script setup lang="ts">
|
||||
import { Head, useForm } from '@inertiajs/vue3';
|
||||
import { ref } from 'vue';
|
||||
import { Dataset, Title } from '@/Dataset';
|
||||
import { mdiDatabasePlus, mdiMinusCircle, mdiPlusCircle, mdiFinance, mdiInformationOutline, mdiBookOpenPageVariant } from '@mdi/js';
|
||||
import { Dataset, Description, Title } from '@/Dataset';
|
||||
import {
|
||||
mdiDatabasePlus,
|
||||
mdiMinusCircle,
|
||||
mdiPlusCircle,
|
||||
mdiFinance,
|
||||
mdiInformationOutline,
|
||||
mdiBookOpenPageVariant,
|
||||
mdiImageText,
|
||||
} from '@mdi/js';
|
||||
import LayoutAuthenticated from '@/Layouts/LayoutAuthenticated.vue';
|
||||
import SectionMain from '@/Components/SectionMain.vue';
|
||||
import SectionTitleLineWithButton from '@/Components/SectionTitleLineWithButton.vue';
|
||||
|
@ -24,6 +32,10 @@ import IconLanguage from '@/Components/Icons/Language.vue';
|
|||
import IconRecommendet from '@/Components/Icons/Recommendet.vue';
|
||||
import IconConfirm from '@/Components/Icons/Confirm.vue';
|
||||
import SearchAutocomplete from '@/Components/SearchAutocomplete.vue';
|
||||
import TablePersons from '@/Components/TablePersons.vue';
|
||||
import { MainService } from '@/Stores/main';
|
||||
import { notify } from '@/notiwind';
|
||||
// import NotificationToast from '@/Components/NotificationToast.vue';
|
||||
|
||||
const props = defineProps({
|
||||
licenses: {
|
||||
|
@ -42,12 +54,18 @@ const props = defineProps({
|
|||
type: Object,
|
||||
default: () => ({}),
|
||||
},
|
||||
projects: {
|
||||
type: Object,
|
||||
default: () => ({}),
|
||||
},
|
||||
errors: {
|
||||
type: Object,
|
||||
default: () => ({}),
|
||||
},
|
||||
});
|
||||
|
||||
const mainService = MainService();
|
||||
|
||||
// const form = useForm({
|
||||
// language: '',
|
||||
// licenses: [],
|
||||
|
@ -56,17 +74,57 @@ const props = defineProps({
|
|||
// });
|
||||
// let language: (string | Ref<string>) = ref('');
|
||||
let language = ref('');
|
||||
let dataset = {
|
||||
language: language,
|
||||
licenses: [],
|
||||
rights: false,
|
||||
type: '',
|
||||
creating_corporation: 'Tethys RDR',
|
||||
titles: [{ value: '', type: 'Main', language: language }],
|
||||
descriptions: [{ value: '', type: 'Abstract', language: language }],
|
||||
// errors: undefined,
|
||||
};
|
||||
// const form = useForm({
|
||||
let dataset: Dataset;
|
||||
if (Object.keys(mainService.dataset).length == 0) {
|
||||
// language = ref('');
|
||||
dataset = {
|
||||
language: language,
|
||||
licenses: [],
|
||||
rights: false,
|
||||
type: '',
|
||||
creating_corporation: 'Tethys RDR',
|
||||
titles: [{ value: '', type: 'Main', language: language }],
|
||||
descriptions: [{ value: '', type: 'Abstract', language: language }],
|
||||
authors: [],
|
||||
contributors: [],
|
||||
project_id: undefined,
|
||||
embargo_date: '',
|
||||
// errors: undefined,
|
||||
};
|
||||
// mainService.setDataset(dataset, language);
|
||||
} else {
|
||||
// console.log(mainService.dataset);
|
||||
language = ref(mainService.dataset.language);
|
||||
|
||||
// dataset = mainService.dataset;
|
||||
dataset = {
|
||||
language: language,
|
||||
licenses: mainService.dataset.licenses,
|
||||
rights: mainService.dataset.rights,
|
||||
type: mainService.dataset.type,
|
||||
creating_corporation: mainService.dataset.creating_corporation,
|
||||
titles: mainService.dataset.titles,
|
||||
descriptions: mainService.dataset.descriptions,
|
||||
authors: mainService.dataset.authors,
|
||||
contributors: mainService.dataset.contributors,
|
||||
project_id: mainService.dataset.project_id,
|
||||
embargo_date: mainService.dataset.embargo_date,
|
||||
};
|
||||
for (let index in mainService.dataset.titles) {
|
||||
let title: Title = mainService.dataset.titles[index];
|
||||
if (title.type == 'Main') {
|
||||
title.language = language;
|
||||
}
|
||||
}
|
||||
for (let index in mainService.dataset.descriptions) {
|
||||
let description: Description = mainService.dataset.descriptions[index];
|
||||
if (description.type == 'Abstract') {
|
||||
description.language = language;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// const form = useForm<Dataset>({
|
||||
// language: language,
|
||||
// licenses: [],
|
||||
// rights: false,
|
||||
|
@ -98,9 +156,13 @@ const formStep = ref(1);
|
|||
// };
|
||||
|
||||
const nextStep = async () => {
|
||||
let route = stardust.route('dataset.first.step');
|
||||
if (formStep.value == 2) {
|
||||
let route;
|
||||
if (formStep.value == 1) {
|
||||
route = stardust.route('dataset.first.step');
|
||||
} else if (formStep.value == 2) {
|
||||
route = stardust.route('dataset.second.step');
|
||||
} else if (formStep.value == 3) {
|
||||
route = stardust.route('dataset.third.step');
|
||||
}
|
||||
// formStep.value++;
|
||||
await form
|
||||
|
@ -111,6 +173,8 @@ const nextStep = async () => {
|
|||
.post(route, {
|
||||
form,
|
||||
onSuccess: () => {
|
||||
// console.log(form.data());
|
||||
mainService.setDataset(form.data());
|
||||
formStep.value++;
|
||||
},
|
||||
});
|
||||
|
@ -137,6 +201,29 @@ const addDescription = () => {
|
|||
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);
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
<template>
|
||||
|
@ -161,6 +248,7 @@ const removeDescription = (key) => {
|
|||
<SectionTitleLineWithButton :icon="mdiDatabasePlus" title="Submit dataset" main>
|
||||
<!-- <BaseButton :route-name="stardust.route('user.index')" :icon="mdiArrowLeftBoldOutline" label="Back"
|
||||
color="white" rounded-full small /> -->
|
||||
{{ formStep }}
|
||||
</SectionTitleLineWithButton>
|
||||
|
||||
<CardBox>
|
||||
|
@ -202,7 +290,7 @@ const removeDescription = (key) => {
|
|||
:type="'select'"
|
||||
placeholder="[Enter Language]"
|
||||
:errors="form.errors.language"
|
||||
:options="['de', 'en']"
|
||||
:options="{ de: 'de', en: 'en' }"
|
||||
>
|
||||
<div class="text-red-400 text-sm" v-if="form.errors.language">
|
||||
{{ form.errors.language.join(', ') }}
|
||||
|
@ -298,7 +386,7 @@ const removeDescription = (key) => {
|
|||
|
||||
<!-- titles -->
|
||||
<CardBox
|
||||
class="mb-6"
|
||||
class="mb-6 shadow"
|
||||
:has-form-data="true"
|
||||
title="Titles"
|
||||
:icon="mdiFinance"
|
||||
|
@ -394,7 +482,7 @@ const removeDescription = (key) => {
|
|||
required
|
||||
v-model="form.titles[index].language"
|
||||
type="select"
|
||||
:options="['de', 'en']"
|
||||
:options="{ de: 'de', en: 'en' }"
|
||||
placeholder="[select title language]"
|
||||
>
|
||||
<div class="text-red-400 text-sm" v-if="form.errors[`titles.${index}.language`]">
|
||||
|
@ -410,7 +498,8 @@ const removeDescription = (key) => {
|
|||
|
||||
<!-- Descriptions -->
|
||||
<CardBox
|
||||
class="mb-6"
|
||||
:icon="mdiImageText"
|
||||
class="mb-6 shadow"
|
||||
:has-form-data="true"
|
||||
title="Descriptions"
|
||||
:header-icon="mdiPlusCircle"
|
||||
|
@ -523,7 +612,7 @@ const removeDescription = (key) => {
|
|||
required
|
||||
v-model="form.descriptions[index].language"
|
||||
type="select"
|
||||
:options="['de', 'en']"
|
||||
:options="{ de: 'de', en: 'en' }"
|
||||
placeholder="[select title language]"
|
||||
>
|
||||
<div
|
||||
|
@ -539,31 +628,76 @@ const removeDescription = (key) => {
|
|||
</div>
|
||||
</CardBox>
|
||||
|
||||
<CardBox class="mb-6" :has-form-data="true" title="Authors" :icon="mdiBookOpenPageVariant" >
|
||||
<!-- 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>
|
||||
|
||||
<!-- 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" />
|
||||
</CardBox>
|
||||
<!-- <SectionTitleLineWithButton :icon="mdiChartPie" title="Trends overview (to do publications per year)" class="flex flex-col md:flex-row" >
|
||||
</SectionTitleLineWithButton> -->
|
||||
</div>
|
||||
|
||||
<!-- <label>To Do: Recommendet</label> -->
|
||||
<div v-if="formStep == 3">
|
||||
<label>To Do: Recommendet</label>
|
||||
<!-- <div class="w-full mx-2 flex-1 svelte-1l8159u">
|
||||
<div class="font-bold h-6 mt-3 text-gray-600 text-xs leading-8 uppercase">Username</div>
|
||||
<div class="bg-white my-2 p-1 flex border border-gray-200 rounded svelte-1l8159u">
|
||||
<input placeholder="Just a hint.." class="p-1 px-2 appearance-none outline-none w-full text-gray-800" />
|
||||
</div>
|
||||
</div>
|
||||
<div class="w-full mx-2 flex-1 svelte-1l8159u">
|
||||
<div class="font-bold h-6 mt-3 text-gray-600 text-xs leading-8 uppercase">Your Email</div>
|
||||
<div class="bg-white my-2 p-1 flex border border-gray-200 rounded svelte-1l8159u">
|
||||
<input placeholder="jhon@doe.com" class="p-1 px-2 appearance-none outline-none w-full text-gray-800" />
|
||||
</div>
|
||||
</div> -->
|
||||
<div class="flex flex-col md:flex-row">
|
||||
<FormField
|
||||
label="Project.."
|
||||
help="project is optional"
|
||||
:class="{ 'text-red-400': errors.project_id }"
|
||||
class="w-full mx-2 flex-1"
|
||||
>
|
||||
<FormControl
|
||||
required
|
||||
v-model="form.project_id"
|
||||
:type="'select'"
|
||||
placeholder="[Select Project]"
|
||||
:errors="form.errors.project_id"
|
||||
:options="projects"
|
||||
>
|
||||
<div class="text-red-400 text-sm" v-if="form.errors.project_id">
|
||||
{{ form.errors.project_id.join(', ') }}
|
||||
</div>
|
||||
</FormControl>
|
||||
</FormField>
|
||||
|
||||
<FormField
|
||||
label="Embargo Date.."
|
||||
help="embargo date is optional"
|
||||
:class="{ 'text-red-400': errors.embargo_date }"
|
||||
class="w-full mx-2 flex-1"
|
||||
>
|
||||
<FormControl
|
||||
required
|
||||
v-model="form.embargo_date"
|
||||
:type="'date'"
|
||||
placeholder="date('y-m-d')"
|
||||
:errors="form.errors.embargo_date"
|
||||
>
|
||||
<div class="text-red-400 text-sm" v-if="form.errors.embargo_date">
|
||||
{{ form.errors.embargo_date.join(', ') }}
|
||||
</div>
|
||||
</FormControl>
|
||||
</FormField>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div v-if="formStep == 4">
|
||||
|
|
|
@ -1,10 +1,13 @@
|
|||
import { defineStore } from 'pinia';
|
||||
import axios from 'axios';
|
||||
import { Dataset } from '@/Dataset';
|
||||
|
||||
interface Person {
|
||||
export interface Person {
|
||||
id: number;
|
||||
name: string;
|
||||
email: string;
|
||||
name_type: string;
|
||||
identifier_orcid: string;
|
||||
datasetCount: string;
|
||||
created_at: string;
|
||||
}
|
||||
|
@ -12,7 +15,7 @@ interface Person {
|
|||
interface TransactionItem {
|
||||
amount: number;
|
||||
account: string;
|
||||
name: string;
|
||||
name: string;
|
||||
date: string;
|
||||
type: string;
|
||||
business: string;
|
||||
|
@ -31,8 +34,13 @@ export const MainService = defineStore('main', {
|
|||
/* Sample data for starting dashboard(commonly used) */
|
||||
clients: [],
|
||||
history: [] as Array<TransactionItem>,
|
||||
|
||||
// api based data
|
||||
authors: [] as Array<Person>,
|
||||
// persons: [] as Array<Person>,
|
||||
datasets: [],
|
||||
|
||||
dataset: {} as Dataset,
|
||||
}),
|
||||
actions: {
|
||||
// payload = authenticated user
|
||||
|
@ -48,6 +56,27 @@ export const MainService = defineStore('main', {
|
|||
}
|
||||
},
|
||||
|
||||
setDataset(payload) {
|
||||
this.dataset = payload;
|
||||
|
||||
// this.dataset = {
|
||||
// language: language,
|
||||
// licenses: payload.licenses,
|
||||
// rights: payload.rights,
|
||||
// type: payload.type,
|
||||
// creating_corporation: payload.creating_corporation,
|
||||
// titles: payload.titles,
|
||||
// descriptions: payload.descriptions,
|
||||
// authors: payload.authors,
|
||||
// project_id: payload.project_id,
|
||||
// embargo_date: payload.embargo_date,
|
||||
// } as Dataset;
|
||||
// for (let index in payload.titles) {
|
||||
// let title = payload.titles[index];
|
||||
// title.language = language;
|
||||
// }
|
||||
},
|
||||
|
||||
fetch(sampleDataKey) {
|
||||
// sampleDataKey= clients or history
|
||||
axios
|
||||
|
|
16
resources/js/notiwind.ts
Normal file
16
resources/js/notiwind.ts
Normal file
|
@ -0,0 +1,16 @@
|
|||
// notiwind.ts
|
||||
import {
|
||||
createNotifier,
|
||||
NotificationGroup,
|
||||
defineNotificationComponent,
|
||||
} from "notiwind";
|
||||
|
||||
export type NotificationSchema = {
|
||||
type: string;
|
||||
title?: string;
|
||||
text: string;
|
||||
};
|
||||
|
||||
export const notify = createNotifier<NotificationSchema>();
|
||||
export const Notification = defineNotificationComponent<NotificationSchema>();
|
||||
export { NotificationGroup };
|
Loading…
Add table
editor.link_modal.header
Reference in a new issue