feat: update API controllers, validations, and Vue components
All checks were successful
CI / container-job (push) Successful in 49s
All checks were successful
CI / container-job (push) Successful in 49s
- Modified Api/Authors.Controller.ts to use only personal types and sort by dataset_count. - Completely rewritten AvatarController.ts. - Added new Api/CollectionsController.ts for querying collections and collection_roles. - Modified Api/DatasetController.ts to preload titles, identifier and order by server_date_published. - Modified FileController.ts to serve files from /storage/app/data/ instead of /storage/app/public. - Added new Api/UserController for requesting submitters (getSubmitters). - Improved OaiController.ts with performant DB queries for better ResumptionToken handling. - Modified Submitter/DatasetController.ts by adding a categorize method for library classification. - Rewritten ResumptionToken.ts. - Improved TokenWorkerService.ts to utilize browser fingerprint. - Edited dataset.ts by adding the doiIdentifier property. - Enhanced person.ts to improve the fullName property. - Completely rewritten AsideMenuItem.vue component. - Updated CarBoxClient.vue to use TypeScript. - Added new CardBoxDataset.vue for displaying recent datasets on the dashboard. - Completely rewritten TableSampleClients.vue for the dashboard. - Completely rewritten UserAvatar.vue. - Made small layout changes in Dashboard.vue. - Added new Category.vue for browsing scientific collections. - Adapted the pinia store in main.ts. - Added additional routes in start/routes.ts and start/api/routes.ts. - Improved referenceValidation.ts for better ISBN existence checking. - NPM dependency updates.
This commit is contained in:
parent
36cd7a757b
commit
b540547e4c
34 changed files with 1757 additions and 1018 deletions
|
@ -1,17 +1,19 @@
|
|||
<script setup>
|
||||
import { computed, ref } from 'vue';
|
||||
<script lang="ts" setup>
|
||||
import { computed, ref, Ref } from 'vue';
|
||||
import { MainService } from '@/Stores/main';
|
||||
import { StyleService } from '@/Stores/style.service';
|
||||
import { mdiEye, mdiTrashCan } from '@mdi/js';
|
||||
import { mdiEye } 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 dayjs from 'dayjs';
|
||||
import { User } from '@/Stores/main';
|
||||
|
||||
defineProps({
|
||||
checkable: Boolean,
|
||||
checkable: Boolean,
|
||||
});
|
||||
|
||||
const styleService = StyleService();
|
||||
|
@ -19,128 +21,124 @@ const mainService = MainService();
|
|||
const items = computed(() => mainService.clients);
|
||||
|
||||
const isModalActive = ref(false);
|
||||
const isModalDangerActive = ref(false);
|
||||
// const isModalDangerActive = ref(false);
|
||||
const perPage = ref(5);
|
||||
const currentPage = ref(0);
|
||||
const checkedRows = ref([]);
|
||||
const currentClient: Ref<User | null> = ref(null);
|
||||
|
||||
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 = [];
|
||||
|
||||
for (let i = 0; i < numPages.value; i++) {
|
||||
pagesList.push(i);
|
||||
}
|
||||
|
||||
return pagesList;
|
||||
const pagesList = [];
|
||||
for (let i = 0; i < numPages.value; i++) {
|
||||
pagesList.push(i);
|
||||
}
|
||||
return pagesList;
|
||||
});
|
||||
|
||||
const remove = (arr, cb) => {
|
||||
const newArr = [];
|
||||
|
||||
arr.forEach((item) => {
|
||||
if (!cb(item)) {
|
||||
newArr.push(item);
|
||||
}
|
||||
});
|
||||
|
||||
return newArr;
|
||||
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);
|
||||
}
|
||||
if (isChecked) {
|
||||
checkedRows.value.push(client);
|
||||
} else {
|
||||
checkedRows.value = remove(checkedRows.value, (row) => row.id === client.id);
|
||||
}
|
||||
};
|
||||
|
||||
const showModal = (client: User) => {
|
||||
currentClient.value = client;
|
||||
isModalActive.value = true;
|
||||
};
|
||||
</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>
|
||||
<CardBoxModal v-model="isModalActive" :title="currentClient ? currentClient.login : ''">
|
||||
<div v-if="currentClient">
|
||||
<p>Login: {{ currentClient.login }}</p>
|
||||
<p>Email: {{ currentClient.email }}</p>
|
||||
<p>Created: {{ currentClient?.created_at ? dayjs(currentClient.created_at).format('MMM D, YYYY h:mm A') : 'N/A' }}
|
||||
</p>
|
||||
</div>
|
||||
</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> -->
|
||||
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<th v-if="checkable" />
|
||||
<th />
|
||||
<th>Name</th>
|
||||
<th>Email</th>
|
||||
<th>City</th>
|
||||
<th>Progress</th>
|
||||
<th>Created</th>
|
||||
<th />
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr v-for="client in itemsPaginated" :key="client.id">
|
||||
<TableCheckboxCell v-if="checkable" @checked="checked($event, client)" />
|
||||
<td class="border-b-0 lg:w-6 before:hidden">
|
||||
<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="City">
|
||||
{{ client.city }}
|
||||
</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">{{ client.created }}</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="isModalDangerActive = true" />
|
||||
</BaseButtons>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
<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>
|
||||
<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.login }}
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<th v-if="checkable" />
|
||||
<th />
|
||||
<th>Login</th>
|
||||
<th>Email</th>
|
||||
<th>Created</th>
|
||||
<th />
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr v-for="client in itemsPaginated" :key="client.id">
|
||||
<TableCheckboxCell v-if="checkable" @checked="checked($event, client)" />
|
||||
<td class="border-b-0 lg:w-6 before:hidden">
|
||||
<!-- <UserAvatar :username="client.login" :avatar="client.avatar" class="w-24 h-24 mx-auto lg:w-6 lg:h-6" /> -->
|
||||
<div v-if="client.avatar">
|
||||
<UserAvatar :default-url="client.avatar ? '/public' + client.avatar : ''"
|
||||
:username="client.first_name + ' ' + client.last_name" class="w-24 h-24 mx-auto lg:w-6 lg:h-6" />
|
||||
</div>
|
||||
|
||||
|
||||
<div v-else>
|
||||
<UserAvatar :username="client.first_name + ' ' + client.last_name" class="w-24 h-24 mx-auto lg:w-6 lg:h-6" />
|
||||
</div>
|
||||
</td>
|
||||
<td data-label="Login">
|
||||
{{ client.login }}
|
||||
</td>
|
||||
<td data-label="Email">
|
||||
{{ client.email }}
|
||||
</td>
|
||||
<td data-label="Created">
|
||||
<small class="text-gray-500 dark:text-slate-400"
|
||||
:title="client.created_at ? dayjs(client.created_at).format('MMM D, YYYY h:mm A') : 'N/A'">
|
||||
{{ client.created_at ? dayjs(client.created_at).format('MMM D, YYYY h:mm A') : 'N/A' }}
|
||||
</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="showModal(client)" />
|
||||
<!-- <BaseButton color="danger" :icon="mdiTrashCan" small @click="isModalDangerActive = true" /> -->
|
||||
</BaseButtons>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
<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>
|
||||
|
|
Loading…
Add table
editor.link_modal.header
Reference in a new issue