Skip to content

Commit

Permalink
pkp/pkp-lib#1660 Customizable Reviewer Recommendations
Browse files Browse the repository at this point in the history
  • Loading branch information
touhidurabir committed Nov 14, 2024
1 parent 2b591e8 commit 988702d
Show file tree
Hide file tree
Showing 3 changed files with 355 additions and 0 deletions.
2 changes: 2 additions & 0 deletions src/components/Container/SettingsPage.vue
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import ThemeForm from '@/components/Form/context/ThemeForm.vue';
import DateTimeForm from '@/components/Form/context/DateTimeForm.vue';
import DoiSetupSettingsForm from '@/components/Form/context/DoiSetupSettingsForm.vue';
import DoiRegistrationSettingsForm from '@/components/Form/context/DoiRegistrationSettingsForm.vue';
import ReviewerRecommendationsListPanel from '@/components/ListPanel/reviewerRecommendations/ReviewerRecommendationsListPanel.vue';
export default {
name: 'SettingsPage',
Expand All @@ -20,6 +21,7 @@ export default {
DateTimeForm,
DoiSetupSettingsForm,
DoiRegistrationSettingsForm,
ReviewerRecommendationsListPanel,
},
extends: Page,
data() {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
<template>
<SideModalBody>
<template #title>
{{ title }}
</template>
<SideModalLayoutBasic>
<PkpForm
v-bind="activeForm"
@set="(...args) => emit('updateForm', ...args)"
@success="(...args) => emit('formSuccess', ...args)"
/>
</SideModalLayoutBasic>
</SideModalBody>
</template>

<script setup>
import SideModalBody from '@/components/Modal/SideModalBody.vue';
import SideModalLayoutBasic from '@/components/Modal/SideModalLayoutBasic.vue';
import PkpForm from '@/components/Form/Form.vue';
defineProps({
title: {type: String, required: true},
activeForm: {type: Object, required: true},
});
const emit = defineEmits(['updateForm', 'formSuccess']);
</script>
Original file line number Diff line number Diff line change
@@ -0,0 +1,327 @@
<template>
<div class="reviewerRecommendationsListPanel">
<slot>
<ListPanel :items="items">
<template #header>
<PkpHeader>
<h2>{{ title }}</h2>
<Spinner v-if="isLoading" />
<template #actions>
<PkpButton @click="openAddModal">
{{ addRecommendationLabel }}
</PkpButton>
</template>
</PkpHeader>
</template>
<template #item-title="{item}">
{{ localize(item.title) }}
<Badge v-bind="item.status ? {isPrimary: true} : {isWarnable: true}">
{{ item.status ? t('common.active') : t('common.inactive') }}
</Badge>
</template>
<template #item-actions="{item}">
<PkpButton @click="openEditModal(item.id)">
{{ t('common.edit') }}
</PkpButton>
<PkpButton
v-if="item.removable"
:is-warnable="true"
@click="openDeleteModal(item.id)"
>
{{ t('common.delete') }}
</PkpButton>
</template>
<template #footer>
<Pagination
v-if="lastPage > 1"
:current-page="currentPage"
:is-loading="isLoading"
:last-page="lastPage"
@set-page="setPage"
/>
</template>
</ListPanel>
</slot>
</div>
</template>

<script>
import PkpButton from '@/components/Button/Button.vue';
import Badge from '@/components/Badge/Badge.vue';
import Spinner from '@/components/Spinner/Spinner.vue';
import ListPanel from '@/components/ListPanel/ListPanel.vue';
import Pagination from '@/components/Pagination/Pagination.vue';
import PkpHeader from '@/components/Header/Header.vue';
import ajaxError from '@/mixins/ajaxError';
import dialog from '@/mixins/dialog.js';
import fetch from '@/mixins/fetch';
import cloneDeep from 'clone-deep';
import ReviewerRecommendationsEditModal from './ReviewerRecommendationsEditModal.vue';
import {useModal} from '@/composables/useModal';
export default {
components: {
PkpButton,
Spinner,
ListPanel,
Pagination,
PkpHeader,
Badge,
},
mixins: [dialog, fetch, ajaxError],
props: {
/** A localized string for the button to add an recommendation. */
addRecommendationLabel: {
type: String,
required: true,
},
/** A localized string for the confirmation message before deleting an recommendation. */
confirmDeleteMessage: {
type: String,
required: true,
},
/** A localized string for the button to delete an recommendation. */
deleteRecommendationLabel: {
type: String,
required: true,
},
/** A localized string for the button to edit an recommendation. */
editRecommendationLabel: {
type: String,
required: true,
},
/** The Form to edit an recommendation. */
form: {
type: Object,
required: true,
},
/** A unique id for this component. */
id: {
type: String,
required: true,
},
/** An array of recommendations. */
items: {
type: Array,
default() {
return [];
},
},
/** A count of all recommendations in the journal, press or preprint server. */
itemsMax: {
type: Number,
default() {
return 0;
},
},
/** The title of the list panel. */
title: {
type: String,
required: true,
},
},
emits: [
/** Emitted when an recommendation is added. Payload: `(recommendation)` */
'add:recommendation',
/** Emitted when a prop should be changed. Payload: `(id, newProps)` */
'set',
/** Emitted when an recommendation is updated. Payload: `(recommendation)` */
'update:recommendation',
],
data() {
return {
activeForm: null,
activeFormTitle: '',
};
},
methods: {
/**
* Clear the active form when the modal is closed
*
* @param {Object} event
*/
closeFormModal(event) {
this.activeForm = null;
this.activeFormTitle = '';
const {closeSideModal} = useModal();
closeSideModal(ReviewerRecommendationsEditModal);
},
/**
* The add/edit form has been successfully
* submitted.
*
* @param {Object} item
*/
formSuccess(item) {
if (this.activeForm.method === 'POST') {
this.offset = 0;
this.get();
pkp.eventBus.$emit('add:recommendation', item);
} else {
this.setItems(
this.items.map((i) => (i.id === item.id ? item : i)),
this.itemsMax,
);
pkp.eventBus.$emit('update:recommendation', item);
}
this.closeFormModal();
},
/**
* Open the modal to add an item
*/
openAddModal() {
let activeForm = cloneDeep(this.form);
activeForm.action = this.apiUrl;
activeForm.method = 'POST';
this.activeForm = activeForm;
this.activeFormTitle = this.addRecommendationLabel;
const {openSideModal} = useModal();
openSideModal(ReviewerRecommendationsEditModal, {
title: this.activeFormTitle,
activeForm,
onUpdateForm: this.updateForm,
onFormSuccess: this.formSuccess,
});
},
/**
* Open delete modal
*
* @param {Number} id
*/
openDeleteModal(id) {
const recommendation = this.items.find((a) => a.id === id);
if (typeof recommendation === 'undefined') {
this.ajaxErrorCallback({});
return;
}
this.openDialog({
name: 'delete',
title: this.deleteRecommendationLabel,
message: this.replaceLocaleParams(this.confirmDeleteMessage, {
title: this.localize(recommendation.title),
}),
actions: [
{
label: this.t('common.yes'),
isPrimary: true,
callback: (close) => {
var self = this;
$.ajax({
url: this.apiUrl + '/' + id,
type: 'POST',
headers: {
'X-Csrf-Token': pkp.currentUser.csrfToken,
'X-Http-Method-Override': 'DELETE',
},
error: self.ajaxErrorCallback,
success: function (r) {
self.setItems(
self.items.filter((i) => i.id !== id),
self.itemsMax,
);
close();
self.setFocusIn(self.$el);
},
});
},
},
{
label: this.t('common.no'),
isWarnable: true,
callback: (close) => close(),
},
],
});
},
/**
* Open the modal to edit an item
*
* @param {Number} id
*/
openEditModal(id) {
const recommendation = this.items.find(
(recommendation) => recommendation.id === id,
);
if (!recommendation) {
this.ajaxErrorCallback({});
return;
}
let activeForm = cloneDeep(this.form);
activeForm.action = this.apiUrl + '/' + id;
activeForm.method = 'PUT';
activeForm.fields = activeForm.fields.map((field) => {
if (Object.keys(recommendation).includes(field.name)) {
field.value = recommendation[field.name];
}
return field;
});
this.activeForm = activeForm;
this.activeFormTitle = this.editRecommendationLabel;
const {openSideModal} = useModal();
openSideModal(ReviewerRecommendationsEditModal, {
title: this.editRecommendationLabel,
activeForm,
onUpdateForm: this.updateForm,
onFormSuccess: this.formSuccess,
});
},
/**
* Set the list of items
*
* @see @/mixins/fetch.js
* @param {Array} items
* @param {Number} itemsMax
*/
setItems(items, itemsMax) {
this.$emit('set', this.id, {
items,
itemsMax,
});
},
/**
* Update form values when they change
*
* @param {String} formId
* @param {Object} data
*/
updateForm(formId, data) {
if (!this.activeForm) {
return;
}
let activeForm = this.activeForm;
Object.keys(data).forEach(function (key) {
activeForm[key] = data[key];
});
this.activeForm = activeForm;
},
},
};
</script>

<style lang="less">
@import '../../../styles/_import';
.reviewerRecommendationsListPanel {
.listPanel__item {
padding-top: 0.25rem;
padding-bottom: 0.25rem;
}
.listPanel__itemTitle {
font-weight: @normal;
}
}
</style>

0 comments on commit 988702d

Please sign in to comment.