hotfix (dataset): implement reject to reviewer functionality for editors
- Added "rejected_to_reviewer" state to the `ServerStates` enum. - Implemented routes and controller actions (`rejectToReviewer`, `rejectToReviewerUpdate`) for editors to reject datasets back to reviewers with a rejection note. - Added UI elements (button) in the editor dataset index and publish views to trigger the "reject to reviewer" action. - Updated the reviewer dataset index view to display datasets in the "rejected_to_reviewer" state and show the editor's rejection note. - Modified the reviewer dataset review page to allow reviewers to view and accept datasets that have been rejected back to them by editors. - Updated the database migration to include the "rejected_to_reviewer" state in the `documents_server_state_check` constraint. - Updated dependencies (pinia, redis).
This commit is contained in:
parent
c245c8e97d
commit
be6b38d0a3
15 changed files with 1647 additions and 404 deletions
|
@ -227,7 +227,11 @@ const formatServerState = (state: string) => {
|
|||
color="info" :icon="mdiLibraryShelves" :label="'Classify'" small
|
||||
class="col-span-1">
|
||||
</BaseButton>
|
||||
|
||||
|
||||
<BaseButton v-if="can.publish && (dataset.server_state == 'reviewed')"
|
||||
:route-name="stardust.route('editor.dataset.rejectToReviewer', [dataset.id])"
|
||||
color="info" :icon="mdiUndo" :label="'Reject To Reviewer'" small
|
||||
class="col-span-1" />
|
||||
<BaseButton v-if="can.publish && (dataset.server_state == 'reviewed')"
|
||||
:route-name="stardust.route('editor.dataset.publish', [dataset.id])"
|
||||
color="info" :icon="mdiBookEdit" :label="'Publish'" small
|
||||
|
|
|
@ -10,7 +10,7 @@ import FormControl from '@/Components/FormControl.vue';
|
|||
import BaseButton from '@/Components/BaseButton.vue';
|
||||
import BaseButtons from '@/Components/BaseButtons.vue';
|
||||
import { stardust } from '@eidellev/adonis-stardust/client';
|
||||
import { mdiArrowLeftBoldOutline, mdiReiterate } from '@mdi/js';
|
||||
import { mdiArrowLeftBoldOutline, mdiReiterate, mdiBookEdit, mdiUndo } from '@mdi/js';
|
||||
import FormValidationErrors from '@/Components/FormValidationErrors.vue';
|
||||
|
||||
const props = defineProps({
|
||||
|
@ -18,6 +18,10 @@ const props = defineProps({
|
|||
type: Object,
|
||||
default: () => ({}),
|
||||
},
|
||||
can: {
|
||||
type: Object,
|
||||
default: () => ({}),
|
||||
},
|
||||
});
|
||||
|
||||
const flash: Ref<any> = computed(() => {
|
||||
|
@ -93,7 +97,11 @@ const handleSubmit = async (e) => {
|
|||
</p>
|
||||
<BaseButtons>
|
||||
<BaseButton type="submit" color="info" label="Set published"
|
||||
:class="{ 'opacity-25': form.processing }" :disabled="form.processing" />
|
||||
:class="{ 'opacity-25': form.processing }" :disabled="form.processing" :icon="mdiBookEdit" small />
|
||||
<BaseButton v-if="can.reject && (dataset.server_state == 'reviewed')"
|
||||
:route-name="stardust.route('editor.dataset.rejectToReviewer', [dataset.id])"
|
||||
color="info" :icon="mdiUndo" :label="'Reject To Reviewer'" small
|
||||
class="col-span-1" />
|
||||
</BaseButtons>
|
||||
</template>
|
||||
</CardBox>
|
||||
|
|
117
resources/js/Pages/Editor/Dataset/RejectToReviewer.vue
Normal file
117
resources/js/Pages/Editor/Dataset/RejectToReviewer.vue
Normal file
|
@ -0,0 +1,117 @@
|
|||
<script setup lang="ts">
|
||||
import LayoutAuthenticated from '@/Layouts/LayoutAuthenticated.vue';
|
||||
import SectionMain from '@/Components/SectionMain.vue';
|
||||
import SectionTitleLineWithButton from '@/Components/SectionTitleLineWithButton.vue';
|
||||
import { useForm, Head, usePage } from '@inertiajs/vue3';
|
||||
import { computed, Ref } from 'vue';
|
||||
import CardBox from '@/Components/CardBox.vue';
|
||||
import FormField from '@/Components/FormField.vue';
|
||||
import FormControl from '@/Components/FormControl.vue';
|
||||
import BaseButton from '@/Components/BaseButton.vue';
|
||||
import BaseButtons from '@/Components/BaseButtons.vue';
|
||||
import { stardust } from '@eidellev/adonis-stardust/client';
|
||||
import { mdiArrowLeftBoldOutline, mdiReiterate } from '@mdi/js';
|
||||
import FormValidationErrors from '@/Components/FormValidationErrors.vue';
|
||||
|
||||
const props = defineProps({
|
||||
dataset: {
|
||||
type: Object,
|
||||
default: () => ({}),
|
||||
},
|
||||
});
|
||||
|
||||
// Define the computed property for the label
|
||||
const computedLabel = computed(() => {
|
||||
return `Reject to reviewer: ${props.dataset.reviewer?.login || 'Unknown User'}`;
|
||||
});
|
||||
const computedEmailLabel = computed(() => {
|
||||
return props.dataset.reviewer?.email || '';
|
||||
});
|
||||
|
||||
|
||||
const flash: Ref<any> = computed(() => {
|
||||
return usePage().props.flash;
|
||||
});
|
||||
const errors: Ref<any> = computed(() => {
|
||||
return usePage().props.errors;
|
||||
});
|
||||
|
||||
const form = useForm({
|
||||
server_state: 'rejected_to_reviewer',
|
||||
reject_editor_note: '',
|
||||
send_email: false,
|
||||
});
|
||||
|
||||
const handleSubmit = async (e: SubmitEvent) => {
|
||||
e.preventDefault();
|
||||
await form.put(stardust.route('editor.dataset.rejectToReviewerUpdate', [props.dataset.id]));
|
||||
// await form.put(stardust.route('editor.dataset.update', [props.dataset.id]));
|
||||
};
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<LayoutAuthenticated>
|
||||
|
||||
<Head title="Reject reviewed dataset" />
|
||||
<SectionMain>
|
||||
<SectionTitleLineWithButton :icon="mdiReiterate" title="Reject reviewed dataset to reviewer" main>
|
||||
<BaseButton :route-name="stardust.route('editor.dataset.list')" :icon="mdiArrowLeftBoldOutline"
|
||||
label="Back" color="white" rounded-full small />
|
||||
</SectionTitleLineWithButton>
|
||||
|
||||
<CardBox form @submit.prevent="handleSubmit">
|
||||
<FormValidationErrors v-bind:errors="errors" />
|
||||
|
||||
|
||||
<FormField label="server state" :class="{ 'text-red-400': form.errors.server_state }">
|
||||
<FormControl v-model="form.server_state" type="text" placeholder="-- server state --"
|
||||
:is-read-only="true" :error="form.errors.server_state">
|
||||
<div class="text-red-400 text-sm" v-if="form.errors.server_state">
|
||||
{{ form.errors.server_state }}
|
||||
</div>
|
||||
</FormControl>
|
||||
</FormField>
|
||||
<FormField label="reject note" :class="{ 'text-red-400': form.errors.reject_editor_note }">
|
||||
<FormControl v-model="form.reject_editor_note" type="textarea"
|
||||
placeholder="-- reject note for reviewer --" :error="form.errors.reject_editor_note">
|
||||
<div class="text-red-400 text-sm" v-if="form.errors.reject_editor_note">
|
||||
{{ form.errors.reject_editor_note }}
|
||||
</div>
|
||||
</FormControl>
|
||||
</FormField>
|
||||
|
||||
<!-- <FormControl
|
||||
type="checkbox"
|
||||
v-model="form.send_email"
|
||||
:error="form.errors.send_email">
|
||||
</FormControl> -->
|
||||
|
||||
<FormField label="Email Notification">
|
||||
<label for="send_email" class="flex items-center mr-6 mb-3">
|
||||
<input type="checkbox" id="send_email" v-model="form.send_email" class="mr-2" />
|
||||
<span class="check"></span>
|
||||
<a class="pl-2 " target="_blank">send email to reviewer
|
||||
<span class="text-blue-600 hover:underline">
|
||||
{{ computedEmailLabel }}
|
||||
</span>
|
||||
</a>
|
||||
</label>
|
||||
</FormField>
|
||||
|
||||
<div v-if="flash && flash.warning" class="flex flex-col mt-6 animate-fade-in">
|
||||
<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>{{ flash.warning }}</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<template #footer>
|
||||
<BaseButtons>
|
||||
<BaseButton type="submit" color="info" :label="computedLabel"
|
||||
:class="{ 'opacity-25': form.processing }" :disabled="form.processing" />
|
||||
</BaseButtons>
|
||||
</template>
|
||||
</CardBox>
|
||||
</SectionMain>
|
||||
</LayoutAuthenticated>
|
||||
</template>
|
|
@ -51,6 +51,8 @@ const getRowClass = (dataset) => {
|
|||
rowclass = 'bg-released';
|
||||
} else if (dataset.server_state == 'published') {
|
||||
rowclass = 'bg-published';
|
||||
} else if (dataset.server_state == 'rejected_to_reviewer') {
|
||||
rowclass = 'bg-rejected-reviewer';
|
||||
} else {
|
||||
rowclass = '';
|
||||
}
|
||||
|
@ -131,6 +133,21 @@ const formatServerState = (state: string) => {
|
|||
</td>
|
||||
<td class="py-4 whitespace-nowrap text-gray-700">
|
||||
<div class="text-sm">{{ formatServerState(dataset.server_state) }}</div>
|
||||
<div v-if="dataset.server_state === 'rejected_to_reviewer' && dataset.reject_editor_note"
|
||||
class="inline-block relative ml-2 group">
|
||||
<button
|
||||
class="w-5 h-5 rounded-full bg-gray-200 text-gray-600 text-xs flex items-center justify-center focus:outline-none hover:bg-gray-300">
|
||||
i
|
||||
</button>
|
||||
<div
|
||||
class="absolute left-0 top-full mt-1 w-64 bg-white shadow-lg rounded-md p-3 text-xs text-left z-50 transform scale-0 origin-top-left transition-transform duration-100 group-hover:scale-100">
|
||||
<p class="text-gray-700 max-h-40 overflow-y-auto overflow-x-hidden whitespace-normal break-words">
|
||||
{{ dataset.reject_editor_note }}
|
||||
</p>
|
||||
<div class="absolute -top-1 left-1 w-2 h-2 bg-white transform rotate-45">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</td>
|
||||
|
||||
|
||||
|
@ -146,12 +163,13 @@ const formatServerState = (state: string) => {
|
|||
<td
|
||||
class="py-4 whitespace-nowrap text-right text-sm font-medium text-gray-700">
|
||||
<BaseButtons type="justify-start lg:justify-end" no-wrap>
|
||||
<BaseButton v-if="can.review && (dataset.server_state == 'approved')"
|
||||
<BaseButton
|
||||
v-if="can.reject && (dataset.server_state == 'approved' || dataset.server_state == 'rejected_to_reviewer')"
|
||||
:route-name="stardust.route('reviewer.dataset.review', [dataset.id])"
|
||||
color="info" :icon="mdiGlasses" :label="'View'" small />
|
||||
|
||||
<BaseButton
|
||||
v-if="can.reject && (dataset.server_state == 'approved')"
|
||||
v-if="can.reject && (dataset.server_state == 'approved' || dataset.server_state == 'rejected_to_reviewer')"
|
||||
:route-name="stardust.route('reviewer.dataset.reject', [dataset.id])"
|
||||
color="info" :icon="mdiReiterate" :label="'Reject'" small />
|
||||
</BaseButtons>
|
||||
|
|
|
@ -10,17 +10,20 @@ import BaseButtons from '@/Components/BaseButtons.vue';
|
|||
import { stardust } from '@eidellev/adonis-stardust/client';
|
||||
import { mdiArrowLeftBoldOutline, mdiGlasses } from '@mdi/js';
|
||||
import FormValidationErrors from '@/Components/FormValidationErrors.vue';
|
||||
import { mdiReiterate } from '@mdi/js';
|
||||
import { mdiReiterate, mdiBookOpenPageVariant, mdiFinance } from '@mdi/js';
|
||||
import MapComponentView from '@/Components/Map/MapComponentView.vue';
|
||||
import IconSvg from '@/Components/Icons/IconSvg.vue';
|
||||
import CardBoxSimple from '@/Components/CardBoxSimple.vue';
|
||||
|
||||
const props = defineProps({
|
||||
dataset: {
|
||||
type: Object,
|
||||
default: () => ({}),
|
||||
},
|
||||
fields: {
|
||||
type: Object,
|
||||
required: true,
|
||||
},
|
||||
// fields: {
|
||||
// type: Object,
|
||||
// required: true,
|
||||
// },
|
||||
can: {
|
||||
type: Object,
|
||||
default: () => ({}),
|
||||
|
@ -34,18 +37,6 @@ const errors: Ref<any> = computed(() => {
|
|||
return usePage().props.errors;
|
||||
});
|
||||
|
||||
// const form = useForm({
|
||||
// preferred_reviewer: '',
|
||||
// preferred_reviewer_email: '',
|
||||
// preferation: 'yes_preferation',
|
||||
|
||||
// // preferation: '',
|
||||
// // isPreferationRequired: false,
|
||||
// });
|
||||
|
||||
|
||||
// const isPreferationRequired = computed(() => form.preferation === 'yes_preferation');
|
||||
|
||||
const handleSubmit = async (e) => {
|
||||
e.preventDefault();
|
||||
|
||||
|
@ -53,6 +44,19 @@ const handleSubmit = async (e) => {
|
|||
// await form.put(stardust.route('dataset.releaseUpdate', [props.dataset.id]));
|
||||
// // await form.put(stardust.route('editor.dataset.update', [props.dataset.id]));
|
||||
};
|
||||
|
||||
|
||||
const getFileSize = (file: File) => {
|
||||
if (file.size > 1024) {
|
||||
if (file.size > 1048576) {
|
||||
return Math.round(file.size / 1048576) + 'mb';
|
||||
} else {
|
||||
return Math.round(file.size / 1024) + 'kb';
|
||||
}
|
||||
} else {
|
||||
return file.size + 'b';
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
|
@ -64,7 +68,7 @@ const handleSubmit = async (e) => {
|
|||
<BaseButton :route-name="stardust.route('reviewer.dataset.list')" :icon="mdiArrowLeftBoldOutline"
|
||||
label="Back" color="white" rounded-full small />
|
||||
</SectionTitleLineWithButton>
|
||||
<CardBox form @submit.prevent="handleSubmit">
|
||||
<component is="form" form @submit.prevent="handleSubmit">
|
||||
<FormValidationErrors v-bind:errors="errors" />
|
||||
|
||||
<div v-if="flash && flash.warning" class="flex flex-col mt-6 animate-fade-in">
|
||||
|
@ -74,26 +78,546 @@ const handleSubmit = async (e) => {
|
|||
</div>
|
||||
</div>
|
||||
|
||||
<div class="flex flex-col">
|
||||
<div class="flex flex-row items-center justify-between dark:bg-slate-900 bg-gray-200 p-2 mb-2"
|
||||
v-for="(fieldValue, field) in fields" :key="field">
|
||||
|
||||
<label :for="field" class="font-bold h-6 mt-3 text-xs leading-8 uppercase">{{ field }}</label>
|
||||
<span class="text-sm text-gray-600" v-html="fieldValue"></span>
|
||||
<!-- <div class="mb-4"> -->
|
||||
|
||||
<CardBoxSimple>
|
||||
<div class="mb-6">
|
||||
<h4 class="text-sm font-medium text-gray-700 dark:text-gray-300 mb-2">Language</h4>
|
||||
<div class="bg-emerald-50 dark:bg-emerald-900/30 border border-emerald-200 dark:border-emerald-800
|
||||
rounded-lg p-3 shadow-sm">
|
||||
<div class="flex items-center">
|
||||
<IconSvg path="language" :size="20"
|
||||
className="mr-2 text-emerald-600 dark:text-emerald-400" />
|
||||
<span class="text-emerald-700 dark:text-emerald-300 font-medium">
|
||||
{{ dataset.language || 'Not specified' }}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<template #footer>
|
||||
<BaseButtons>
|
||||
<!-- <BaseButton type="submit" color="info" label="Receive"
|
||||
<!-- Licenses -->
|
||||
<div class="mb-6">
|
||||
<h4 class="text-sm font-medium text-gray-700 dark:text-gray-300 mb-3">Licenses</h4>
|
||||
<div v-if="dataset.licenses && dataset.licenses.length > 0" class="space-y-2">
|
||||
<div v-for="license in dataset.licenses" :key="license.id" class="bg-emerald-50 dark:bg-emerald-900/30 border border-emerald-200 dark:border-emerald-800
|
||||
rounded-lg p-3 shadow-sm hover:bg-emerald-100 dark:hover:bg-emerald-900/50 transition-colors">
|
||||
<div class="flex items-center">
|
||||
<IconSvg path="license" :size="20"
|
||||
className="mr-2 text-emerald-600 dark:text-emerald-400" />
|
||||
<span class="text-emerald-700 dark:text-emerald-300">
|
||||
{{ license.name }}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div v-else class="text-center py-4">
|
||||
<p class="text-gray-500 dark:text-gray-400 italic">No licenses specified</p>
|
||||
</div>
|
||||
<div v-if="dataset.licenses && dataset.licenses.length > 0"
|
||||
class="mt-3 text-xs text-gray-500 dark:text-gray-400">
|
||||
Total licenses: {{ dataset.licenses.length }}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="flex flex-col md:flex-row gap-4 mb-6">
|
||||
<!-- (3) dataset_type -->
|
||||
<div class="w-full mx-2 flex-1">
|
||||
<h4 class="text-sm font-medium text-gray-700 dark:text-gray-300 mb-2">Dataset Type</h4>
|
||||
<div class="bg-emerald-50 dark:bg-emerald-900/30 border border-emerald-200 dark:border-emerald-800
|
||||
rounded-lg p-3 shadow-sm">
|
||||
<div class="flex items-center">
|
||||
<IconSvg path="book" :size="20"
|
||||
className="mr-2 text-emerald-600 dark:text-emerald-400" />
|
||||
<span class="text-emerald-700 dark:text-emerald-300 font-medium">
|
||||
{{ dataset.type || 'Not specified' }}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- (4) creating_corporation -->
|
||||
<div class="w-full mx-2 flex-1">
|
||||
<h4 class="text-sm font-medium text-gray-700 dark:text-gray-300 mb-2">Creating
|
||||
Corporation
|
||||
</h4>
|
||||
<div class="bg-emerald-50 dark:bg-emerald-900/30 border border-emerald-200 dark:border-emerald-800
|
||||
rounded-lg p-3 shadow-sm">
|
||||
<div class="flex items-center">
|
||||
<IconSvg path="building" :size="20"
|
||||
className="mr-2 text-emerald-600 dark:text-emerald-400" />
|
||||
<span class="text-emerald-700 dark:text-emerald-300 font-medium">
|
||||
{{ dataset.creating_corporation || 'Not specified' }}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="flex flex-col md:flex-row gap-4 mb-6">
|
||||
<!-- (9) project_id -->
|
||||
<div class="w-full mx-2 flex-1">
|
||||
<h4 class="text-sm font-medium text-gray-700 dark:text-gray-300 mb-2">Project</h4>
|
||||
<div class="bg-emerald-50 dark:bg-emerald-900/30 border border-emerald-200 dark:border-emerald-800
|
||||
rounded-lg p-3 shadow-sm">
|
||||
<span class="text-emerald-700 dark:text-emerald-300">
|
||||
{{ dataset.project?.label || 'Not specified' }}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
<!-- (10) embargo_date -->
|
||||
<div class="w-full mx-2 flex-1">
|
||||
<h4 class="text-sm font-medium text-gray-700 dark:text-gray-300 mb-2">Embargo Date</h4>
|
||||
<div class="bg-emerald-50 dark:bg-emerald-900/30 border border-emerald-200 dark:border-emerald-800
|
||||
rounded-lg p-3 shadow-sm">
|
||||
<span v-if="dataset.embargo_date" class="text-emerald-700 dark:text-emerald-300">
|
||||
{{ dataset.embargo_date }}
|
||||
</span>
|
||||
<span v-else class="text-gray-500 dark:text-gray-400 italic">
|
||||
No embargo date set
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</CardBoxSimple>
|
||||
|
||||
<!-- (5) titles -->
|
||||
<CardBox class="mb-6 shadow" :has-form-data="false" title="Titles" :icon="mdiFinance"
|
||||
:show-header-icon="false">
|
||||
<div class="p-4">
|
||||
<!-- Main Title (highlighted) -->
|
||||
<h4 class="text-sm font-medium text-gray-700 dark:text-gray-300 mb-3">Main Title</h4>
|
||||
<div class="bg-emerald-50 dark:bg-emerald-900/30 border-l-4 border border-emerald-300 dark:border-emerald-700
|
||||
rounded-lg p-4 shadow-sm mb-4">
|
||||
<div class="flex justify-between items-start mb-2">
|
||||
<span class="inline-flex items-center px-2.5 py-0.5 rounded-full text-xs font-medium bg-emerald-200
|
||||
dark:bg-emerald-800 text-emerald-800 dark:text-emerald-200">
|
||||
{{ dataset.titles[0].language }}
|
||||
</span>
|
||||
<span class="inline-flex items-center px-2.5 py-0.5 rounded-full text-xs font-medium bg-emerald-200
|
||||
dark:bg-emerald-800 text-emerald-800 dark:text-emerald-200">
|
||||
Main
|
||||
</span>
|
||||
</div>
|
||||
<h3
|
||||
class="text-lg font-medium text-emerald-800 dark:text-emerald-200 whitespace-pre-line break-words overflow-wrap-anywhere">
|
||||
{{ dataset.titles[0].value }}
|
||||
</h3>
|
||||
</div>
|
||||
|
||||
<!-- Additional titles -->
|
||||
<div v-if="dataset.titles.length > 1">
|
||||
<h4 class="text-sm font-medium text-gray-700 dark:text-gray-300 mb-3">Additional Titles
|
||||
</h4>
|
||||
<div class="grid gap-3">
|
||||
<template v-for="(title, index) in dataset.titles" :key="index">
|
||||
<div v-if="title.type != 'Main'"
|
||||
class="bg-emerald-50/70 dark:bg-emerald-900/20 border border-emerald-200 dark:border-emerald-800
|
||||
rounded-lg p-3 shadow-sm hover:bg-emerald-50 dark:hover:bg-emerald-900/30 transition-colors">
|
||||
<div class="flex justify-between items-start mb-2">
|
||||
<span class="inline-flex items-center px-2.5 py-0.5 rounded-full text-xs font-medium
|
||||
bg-emerald-100 dark:bg-emerald-800/70 text-emerald-700 dark:text-emerald-300">
|
||||
{{ title.language }}
|
||||
</span>
|
||||
<span class="inline-flex items-center px-2.5 py-0.5 rounded-full text-xs font-medium
|
||||
bg-emerald-100 dark:bg-emerald-800/70 text-emerald-700 dark:text-emerald-300">
|
||||
{{ title.type }}
|
||||
</span>
|
||||
</div>
|
||||
<p
|
||||
class="text-emerald-700 dark:text-emerald-300 font-medium whitespace-pre-line break-words overflow-wrap-anywhere">
|
||||
{{ title.value }}
|
||||
</p>
|
||||
</div>
|
||||
</template>
|
||||
</div>
|
||||
</div>
|
||||
<div class="mt-3 text-xs text-gray-500 dark:text-gray-400">
|
||||
Total titles: {{ dataset.titles.length }}
|
||||
</div>
|
||||
</div>
|
||||
</CardBox>
|
||||
|
||||
<!-- (6) descriptions -->
|
||||
<CardBox class="mb-6 shadow" :has-form-data="false" title="Descriptions" :icon="mdiFinance"
|
||||
:show-header-icon="false">
|
||||
<!-- Main Abstract (highlighted) -->
|
||||
<div class="p-4">
|
||||
<h4 class="text-sm font-medium text-gray-700 dark:text-gray-300 mb-3">Main Abstract</h4>
|
||||
<div class="bg-emerald-50 dark:bg-emerald-900/30 border-l-4 border border-emerald-300 dark:border-emerald-700
|
||||
rounded-lg p-4 shadow-sm mb-4">
|
||||
<div class="flex justify-between items-start mb-2">
|
||||
<span class="inline-flex items-center px-2.5 py-0.5 rounded-full text-xs font-medium bg-emerald-200
|
||||
dark:bg-emerald-800 text-emerald-800 dark:text-emerald-200">
|
||||
{{ dataset.descriptions[0].language }}
|
||||
</span>
|
||||
<span class="inline-flex items-center px-2.5 py-0.5 rounded-full text-xs font-medium bg-emerald-200
|
||||
dark:bg-emerald-800 text-emerald-800 dark:text-emerald-200">
|
||||
Abstract
|
||||
</span>
|
||||
</div>
|
||||
<div class="prose prose-emerald dark:prose-invert max-w-none">
|
||||
<p
|
||||
class="text-emerald-800 dark:text-emerald-200 whitespace-pre-line break-words overflow-wrap-anywhere">
|
||||
{{ dataset.descriptions[0].value }}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
<!-- Additional descriptions -->
|
||||
<div v-if="dataset.descriptions.length > 1">
|
||||
<h4 class="text-sm font-medium text-gray-700 dark:text-gray-300 mb-3">Additional
|
||||
Descriptions</h4>
|
||||
<div class="grid gap-3">
|
||||
<div v-for="(item, index) in dataset.descriptions" :key="index">
|
||||
<div v-if="item.type != 'Abstract'" class="bg-emerald-50/70 dark:bg-emerald-900/20 border border-emerald-200
|
||||
dark:border-emerald-800
|
||||
rounded-lg p-3 shadow-sm hover:bg-emerald-50 dark:hover:bg-emerald-900/30
|
||||
transition-colors">
|
||||
<div class="flex justify-between items-start mb-2">
|
||||
<span class="inline-flex items-center px-2.5 py-0.5 rounded-full text-xs font-medium
|
||||
bg-emerald-100 dark:bg-emerald-800/70 text-emerald-700 dark:text-emerald-300">
|
||||
{{ item.language }}
|
||||
</span>
|
||||
<span class="inline-flex items-center px-2.5 py-0.5 rounded-full text-xs font-medium
|
||||
bg-emerald-100 dark:bg-emerald-800/70 text-emerald-700 dark:text-emerald-300">
|
||||
{{ item.type }}
|
||||
</span>
|
||||
</div>
|
||||
<p
|
||||
class="text-emerald-700 dark:text-emerald-300 text-sm whitespace-pre-line break-words overflow-wrap-anywhere">
|
||||
{{ item.value }}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="mt-3 text-xs text-gray-500 dark:text-gray-400">
|
||||
Total descriptions: {{ dataset.descriptions.length }}
|
||||
</div>
|
||||
</div>
|
||||
</CardBox>
|
||||
|
||||
|
||||
<!-- (7) authors -->
|
||||
<CardBox class="mb-6 shadow" has-table title="Creators" :icon="mdiBookOpenPageVariant"
|
||||
:show-header-icon="false">
|
||||
<div v-if="dataset.authors.length === 0" class="text-center py-6">
|
||||
<p class="text-gray-500 dark:text-gray-400 italic">No authors defined</p>
|
||||
</div>
|
||||
<div v-else class="p-4">
|
||||
<!-- <h4 class="text-sm font-medium text-gray-700 dark:text-gray-300 mb-3">Dataset Authors:</h4> -->
|
||||
<div class="grid gap-3">
|
||||
<div v-for="(author, index) in dataset.authors" :key="index" class="bg-emerald-50 dark:bg-emerald-900/30 border border-emerald-200 dark:border-emerald-800
|
||||
rounded-lg p-3 shadow-sm hover:bg-emerald-100 dark:hover:bg-emerald-900/50 transition-colors">
|
||||
<div class="flex flex-col md:flex-row md:items-center">
|
||||
<div class="flex-1">
|
||||
<p class="font-medium text-emerald-700 dark:text-emerald-300">
|
||||
{{ author.academic_title }} {{ author.first_name }} {{ author.last_name
|
||||
}}
|
||||
</p>
|
||||
<div class="mt-2 grid grid-cols-1 md:grid-cols-2 gap-2">
|
||||
<div v-if="author.email" class="flex items-center text-sm">
|
||||
<IconSvg path="email" :size="16"
|
||||
className="mr-1 text-emerald-600 dark:text-emerald-400" />
|
||||
<span class="text-emerald-600 dark:text-emerald-400">{{ author.email
|
||||
}}</span>
|
||||
</div>
|
||||
<div v-if="author.identifier_orcid" class="flex items-center text-sm">
|
||||
<IconSvg path="idCard" :size="16"
|
||||
className="mr-1 text-emerald-600 dark:text-emerald-400">
|
||||
</IconSvg>
|
||||
<span class="text-emerald-600 dark:text-emerald-400">ORCID: {{
|
||||
author.identifier_orcid
|
||||
}}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div v-if="author.academic_title" class="mt-2 md:mt-0 md:ml-4">
|
||||
<span class="bg-emerald-200 dark:bg-emerald-800 text-emerald-800 dark:text-emerald-200
|
||||
text-xs px-2 py-1 rounded-full">
|
||||
{{ author.academic_title }}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div v-if="dataset.authors.length > 0" class="mt-3 text-xs text-gray-500 dark:text-gray-400">
|
||||
Total authors: {{ dataset.authors.length }}
|
||||
</div>
|
||||
</div>
|
||||
</CardBox>
|
||||
|
||||
<!-- (8) contributors -->
|
||||
<CardBox class="mb-6 shadow" has-table title="Contributors" :icon="mdiBookOpenPageVariant"
|
||||
:show-header-icon="false">
|
||||
<div v-if="dataset.contributors.length === 0" class="text-center py-6">
|
||||
<p class="text-gray-500 dark:text-gray-400 italic">No contributors defined</p>
|
||||
</div>
|
||||
<div v-else class="p-4">
|
||||
<!-- <h4 class="text-sm font-medium text-gray-700 dark:text-gray-300 mb-3">Dataset Contributors:
|
||||
</h4> -->
|
||||
<div class="grid gap-3">
|
||||
<div v-for="(contributor, index) in dataset.contributors" :key="index" class="bg-emerald-50 dark:bg-emerald-900/30 border border-emerald-200 dark:border-emerald-800
|
||||
rounded-lg p-3 shadow-sm hover:bg-emerald-100 dark:hover:bg-emerald-900/50 transition-colors">
|
||||
<div class="flex flex-col md:flex-row md:items-center">
|
||||
<div class="flex-1">
|
||||
<div class="flex flex-wrap items-center gap-2">
|
||||
<p class="font-medium text-emerald-700 dark:text-emerald-300">
|
||||
{{ contributor.academic_title }} {{ contributor.first_name }} {{
|
||||
contributor.last_name
|
||||
}}
|
||||
</p>
|
||||
<span v-if="contributor.pivot_contributor_type" class="bg-emerald-200 dark:bg-emerald-800 text-emerald-800 dark:text-emerald-200
|
||||
text-xs px-2 py-1 rounded-full">
|
||||
{{ contributor.pivot_contributor_type }}
|
||||
</span>
|
||||
</div>
|
||||
<div class="mt-2 grid grid-cols-1 md:grid-cols-2 gap-2">
|
||||
<div v-if="contributor.email" class="flex items-center text-sm">
|
||||
<IconSvg path="email" :size="16"
|
||||
className="mr-1 text-emerald-600 dark:text-emerald-400" />
|
||||
<span class="text-emerald-600 dark:text-emerald-400">{{
|
||||
contributor.email }}</span>
|
||||
</div>
|
||||
<div v-if="contributor.identifier_orcid" class="flex items-center text-sm">
|
||||
<IconSvg path="idCard" :size="16"
|
||||
className="mr-1 text-emerald-600 dark:text-emerald-400">
|
||||
</IconSvg>
|
||||
<span class="text-emerald-600 dark:text-emerald-400">ORCID: {{
|
||||
contributor.identifier_orcid }}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div v-if="contributor.academic_title" class="mt-2 md:mt-0 md:ml-4">
|
||||
<span class="bg-emerald-200 dark:bg-emerald-800 text-emerald-800 dark:text-emerald-200
|
||||
text-xs px-2 py-1 rounded-full">
|
||||
{{ contributor.academic_title }}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div v-if="dataset.contributors.length > 0"
|
||||
class="mt-3 text-xs text-gray-500 dark:text-gray-400">
|
||||
Total contributors: {{ dataset.contributors.length }}
|
||||
</div>
|
||||
</div>
|
||||
</CardBox>
|
||||
|
||||
|
||||
<!-- Map component -->
|
||||
<CardBoxSimple>
|
||||
<h4 class="text-sm font-medium text-gray-700 dark:text-gray-300 mb-3">Geographic Coverage</h4>
|
||||
<!-- Map container with emerald styling -->
|
||||
<div class="bg-emerald-50 dark:bg-emerald-900/30 border border-emerald-200 dark:border-emerald-800
|
||||
rounded-lg shadow-sm overflow-hidden">
|
||||
<!-- The actual map component -->
|
||||
<div class="h-64 rounded-md overflow-hidden">
|
||||
<!-- Use the simplified map component -->
|
||||
<MapComponentView v-if="dataset.coverage" :coverage="dataset.coverage" height="250px"
|
||||
:mapId="'dataset-review-map'" />
|
||||
</div>
|
||||
<!-- Optional: Add a caption or description -->
|
||||
<div class="mt-2 text-xs text-emerald-600 dark:text-emerald-400 text-center">
|
||||
Geographic extent of the dataset
|
||||
</div>
|
||||
</div>
|
||||
<!-- Coordinates display below the map -->
|
||||
<div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-3 mt-3">
|
||||
<!-- x min -->
|
||||
<div>
|
||||
<label class="block text-xs font-medium text-gray-600 dark:text-gray-400 mb-1">X Min
|
||||
(Longitude)</label>
|
||||
<div class="bg-emerald-50 dark:bg-emerald-900/30 border border-emerald-200 dark:border-emerald-800
|
||||
rounded-lg p-2.5 shadow-sm">
|
||||
<span class="text-emerald-700 dark:text-emerald-300">
|
||||
{{ dataset.coverage.x_min }}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- x max -->
|
||||
<div>
|
||||
<label class="block text-xs font-medium text-gray-600 dark:text-gray-400 mb-1">X Max
|
||||
(Longitude)</label>
|
||||
<div class="bg-emerald-50 dark:bg-emerald-900/30 border border-emerald-200 dark:border-emerald-800
|
||||
rounded-lg p-2.5 shadow-sm">
|
||||
<span class="text-emerald-700 dark:text-emerald-300">
|
||||
{{ dataset.coverage.x_max }}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- y min -->
|
||||
<div>
|
||||
<label class="block text-xs font-medium text-gray-600 dark:text-gray-400 mb-1">Y Min
|
||||
(Latitude)</label>
|
||||
<div class="bg-emerald-50 dark:bg-emerald-900/30 border border-emerald-200 dark:border-emerald-800
|
||||
rounded-lg p-2.5 shadow-sm">
|
||||
<span class="text-emerald-700 dark:text-emerald-300">
|
||||
{{ dataset.coverage.y_min }}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- y max -->
|
||||
<div>
|
||||
<label class="block text-xs font-medium text-gray-600 dark:text-gray-400 mb-1">Y Max
|
||||
(Latitude)</label>
|
||||
<div class="bg-emerald-50 dark:bg-emerald-900/30 border border-emerald-200 dark:border-emerald-800
|
||||
rounded-lg p-2.5 shadow-sm">
|
||||
<span class="text-emerald-700 dark:text-emerald-300">
|
||||
{{ dataset.coverage.y_max }}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</CardBoxSimple>
|
||||
|
||||
<!-- References -->
|
||||
<CardBoxSimple>
|
||||
<div v-if="dataset.references.length === 0" class="text-center py-6">
|
||||
<p class="text-gray-500 dark:text-gray-400 italic">No references added.</p>
|
||||
</div>
|
||||
<div v-else class="p-4">
|
||||
<h4 class="text-sm font-medium text-gray-700 dark:text-gray-300 mb-3">Dataset References:
|
||||
</h4>
|
||||
<div class="grid gap-3">
|
||||
<div v-for="(item, index) in dataset.references" :key="index" class="bg-emerald-50 dark:bg-emerald-900/30 border border-emerald-200 dark:border-emerald-800
|
||||
rounded-lg p-3 shadow-sm hover:bg-emerald-100 dark:hover:bg-emerald-900/50 transition-colors">
|
||||
<div class="flex flex-col md:flex-row md:items-center md:justify-between">
|
||||
<div class="flex-1">
|
||||
<p class="font-medium text-emerald-700 dark:text-emerald-300">{{ item.value
|
||||
}}</p>
|
||||
<p class="text-sm text-emerald-600 dark:text-emerald-400 mt-1">{{ item.label
|
||||
}}</p>
|
||||
</div>
|
||||
<div class="flex mt-2 md:mt-0 space-x-2">
|
||||
<span class="bg-emerald-200 dark:bg-emerald-800 text-emerald-800 dark:text-emerald-200
|
||||
text-xs px-2 py-1 rounded-full">
|
||||
{{ item.type }}
|
||||
</span>
|
||||
<span class="bg-emerald-200 dark:bg-emerald-800 text-emerald-800 dark:text-emerald-200
|
||||
text-xs px-2 py-1 rounded-full">
|
||||
{{ item.relation }}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div v-if="dataset.references.length > 0" class="mt-3 text-xs text-gray-500 dark:text-gray-400">
|
||||
Total references: {{ dataset.references.length }}
|
||||
</div>
|
||||
</div>
|
||||
</CardBoxSimple>
|
||||
|
||||
|
||||
|
||||
<!-- Keywords -->
|
||||
<CardBoxSimple>
|
||||
<div v-if="dataset.subjects.length === 0" class="text-center py-6">
|
||||
<p class="text-gray-500 dark:text-gray-400 italic">No keywords added.</p>
|
||||
</div>
|
||||
<div v-else class="p-4">
|
||||
<h4 class="text-sm font-medium text-gray-700 dark:text-gray-300 mb-3">Keywords/Subjects:
|
||||
</h4>
|
||||
<div class="flex flex-wrap gap-2">
|
||||
<div v-for="(subject, index) in dataset.subjects" :key="index" class="bg-emerald-50 dark:bg-emerald-900/30 text-emerald-700 dark:text-emerald-300
|
||||
px-3 py-1.5 rounded-full text-sm font-medium border border-emerald-200
|
||||
dark:border-emerald-800 shadow-sm hover:bg-emerald-100
|
||||
dark:hover:bg-emerald-900/50 transition-colors">
|
||||
<span>{{ subject.value }}</span>
|
||||
<span class="ml-1 text-xs text-emerald-600 dark:text-emerald-400">({{ subject.type
|
||||
}})</span>
|
||||
</div>
|
||||
</div>
|
||||
<div v-if="dataset.subjects.length > 0" class="mt-3 text-xs text-gray-500 dark:text-gray-400">
|
||||
Total keywords: {{ dataset.subjects.length }}
|
||||
</div>
|
||||
</div>
|
||||
</CardBoxSimple>
|
||||
|
||||
|
||||
<!-- download file list -->
|
||||
<CardBoxSimple>
|
||||
<h4 class="text-sm font-medium text-gray-700 dark:text-gray-300 mb-3">Dataset Files</h4>
|
||||
<div v-if="dataset.files && dataset.files.length > 0" class="space-y-2">
|
||||
<div v-for="file in dataset.files" :key="file.id" class="bg-emerald-50 dark:bg-emerald-900/30 border border-emerald-200 dark:border-emerald-800
|
||||
rounded-lg p-3 shadow-sm hover:bg-emerald-100 dark:hover:bg-emerald-900/50 transition-colors">
|
||||
<div class="flex items-center justify-between">
|
||||
<div class="flex items-center space-x-3 flex-1">
|
||||
<div class="flex-shrink-0">
|
||||
<svg class="h-6 w-6 text-emerald-600 dark:text-emerald-400"
|
||||
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="M9 12h6m-6 4h6m2 5H7a2 2 0 01-2-2V5a2 2 0 012-2h5.586a1 1 0 01.707.293l5.414 5.414a1 1 0 01.293.707V19a2 2 0 01-2 2z" />
|
||||
</svg>
|
||||
</div>
|
||||
<div class="min-w-0 flex-1">
|
||||
<p class="text-sm font-medium text-emerald-700 dark:text-emerald-300 truncate">
|
||||
{{ file.label }}
|
||||
</p>
|
||||
<p class="text-xs text-emerald-600/70 dark:text-emerald-400/70 truncate">
|
||||
{{ getFileSize(file) }}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="ml-2 flex-shrink-0">
|
||||
<a v-if="file.id != undefined"
|
||||
:href="stardust.route('reviewer.file.download', [file.id])" class="inline-flex items-center px-3 py-1.5 border border-emerald-300 dark:border-emerald-700
|
||||
text-xs font-medium rounded-full text-emerald-700 bg-emerald-100
|
||||
dark:text-emerald-200 dark:bg-emerald-800/70 hover:bg-emerald-200
|
||||
dark:hover:bg-emerald-800 transition-colors">
|
||||
<IconSvg path="download" :size="20" className="mr-1" />
|
||||
Download
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div v-else
|
||||
class="text-center py-6 bg-emerald-50/50 dark:bg-emerald-900/10 rounded-lg border border-emerald-100 dark:border-emerald-900/30">
|
||||
<svg xmlns="http://www.w3.org/2000/svg"
|
||||
class="h-12 w-12 mx-auto text-emerald-300 dark:text-emerald-700" fill="none"
|
||||
viewBox="0 0 24 24" stroke="currentColor">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="1.5"
|
||||
d="M9 13h6m-3-3v6m5 5H7a2 2 0 01-2-2V5a2 2 0 012-2h5.586a1 1 0 01.707.293l5.414 5.414a1 1 0 01.293.707V19a2 2 0 01-2 2z" />
|
||||
</svg>
|
||||
<p class="mt-2 text-emerald-600 dark:text-emerald-400 italic">No files attached to this dataset
|
||||
</p>
|
||||
</div>
|
||||
<div v-if="dataset.files && dataset.files.length > 0"
|
||||
class="mt-3 text-xs text-gray-500 dark:text-gray-400">
|
||||
Total files: {{ dataset.files.length }}
|
||||
</div>
|
||||
</CardBoxSimple>
|
||||
|
||||
|
||||
|
||||
<BaseButtons>
|
||||
<!-- <BaseButton type="submit" color="info" label="Receive"
|
||||
:class="{ 'opacity-25': router.processing }" :disabled="form.processing" /> -->
|
||||
<BaseButton type="submit" color="info" label="Accept" />
|
||||
<BaseButton v-if="can.reject && (dataset.server_state == 'approved')"
|
||||
:route-name="stardust.route('reviewer.dataset.reject', [dataset.id])" color="info"
|
||||
:icon="mdiReiterate" :label="'Reject'" />
|
||||
</BaseButtons>
|
||||
</template>
|
||||
</CardBox>
|
||||
<BaseButton v-if="can.reject && (dataset.server_state == 'approved' || dataset.server_state == 'rejected_to_reviewer')" type="submit" color="info" label="Accept" small />
|
||||
|
||||
<BaseButton
|
||||
v-if="can.reject && (dataset.server_state == 'approved' || dataset.server_state == 'rejected_to_reviewer')"
|
||||
:route-name="stardust.route('reviewer.dataset.reject', [dataset.id])" color="info"
|
||||
:icon="mdiReiterate" :label="'Reject'" small />
|
||||
</BaseButtons>
|
||||
|
||||
</component>
|
||||
</SectionMain>
|
||||
</LayoutAuthenticated>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
.break-words {
|
||||
word-break: break-word;
|
||||
}
|
||||
|
||||
.overflow-wrap-anywhere {
|
||||
overflow-wrap: anywhere;
|
||||
}
|
||||
</style>
|
Loading…
Add table
editor.link_modal.header
Reference in a new issue