From 2f7832d23896cd9d3e3db384a3c48d1a6e59ea57 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jarda=20Kot=C4=9B=C5=A1ovec?= Date: Tue, 26 Nov 2024 17:04:03 +0100 Subject: [PATCH] pkp/pkp-lib#10618 Remove some redundant logic from omp, which can be inherited from OJS --- src/composables/useFetch.js | 10 +- src/pages/dashboard/dashboardPageStore.js | 2 + .../useWorkflowConfig/useWorkflowConfigOJS.js | 69 +++--------- .../useWorkflowConfig/useWorkflowConfigOMP.js | 102 ++++-------------- .../useWorkflowConfig/useWorkflowConfigOPS.js | 76 +++---------- .../workflowConfigEditorialOJS.js | 7 +- .../workflowConfigEditorialOMP.js | 45 +------- .../workflowConfigEditorialOPS.js | 22 +--- .../workflowConfigHelpers.js | 26 +++++ src/utils/deepMerge.js | 31 ++++++ 10 files changed, 128 insertions(+), 262 deletions(-) create mode 100644 src/pages/workflow/composables/useWorkflowConfig/workflowConfigHelpers.js create mode 100644 src/utils/deepMerge.js diff --git a/src/composables/useFetch.js b/src/composables/useFetch.js index f18ddb321..2a154c6db 100644 --- a/src/composables/useFetch.js +++ b/src/composables/useFetch.js @@ -1,6 +1,7 @@ import {ref, unref} from 'vue'; import {ofetch, createFetch} from 'ofetch'; import {useModalStore} from '@/stores/modalStore'; +import {useDebounceFn} from '@vueuse/core'; let ofetchInstance = ofetch; @@ -26,7 +27,8 @@ export function getCSRFToken() { * @param {Object} [options.body] - The request payload, typically used with 'POST', 'PUT', or 'DELETE' requests. * @param {Object} [options.headers] - Additional HTTP headers to be sent with the request. * @param {string} [options.method] - The HTTP method to be used for the request (e.g., 'GET', 'POST', etc.). - * + * @param {number} options.debouncedMs - When the fetch should be debounce, this defines the delay + * @returns {Object} An object containing several reactive properties and a method for performing the fetch operation: * @returns {Ref} return.data - A ref object containing the response data from the fetch operation. * @returns {Ref} return.validationError - A ref object containing validation error data, relevant when `expectValidationError` is true. @@ -62,7 +64,7 @@ export function useFetch(url, options = {}) { let lastRequestController = null; - async function fetch() { + async function _fetch() { if (lastRequestController) { // abort in-flight request lastRequestController.abort(); @@ -123,6 +125,10 @@ export function useFetch(url, options = {}) { } } + let fetch = _fetch; + if (options.debouncedMs) { + fetch = useDebounceFn(_fetch); + } return { data, isSuccess, diff --git a/src/pages/dashboard/dashboardPageStore.js b/src/pages/dashboard/dashboardPageStore.js index 7fb681a76..212317cc4 100644 --- a/src/pages/dashboard/dashboardPageStore.js +++ b/src/pages/dashboard/dashboardPageStore.js @@ -201,6 +201,8 @@ export const useDashboardPageStore = defineComponentStore( currentPage, pageSize: countPerPage, query: submissionsQuery, + // to avoid multiple fetch calls while view changing watchers triggers query params recalculation + debouncedMs: 2, }); watch( [submissionsUrl, submissionsQuery, currentPage], diff --git a/src/pages/workflow/composables/useWorkflowConfig/useWorkflowConfigOJS.js b/src/pages/workflow/composables/useWorkflowConfig/useWorkflowConfigOJS.js index 852ad915f..6e21e56fc 100644 --- a/src/pages/workflow/composables/useWorkflowConfig/useWorkflowConfigOJS.js +++ b/src/pages/workflow/composables/useWorkflowConfig/useWorkflowConfigOJS.js @@ -1,38 +1,16 @@ import {DashboardPageTypes} from '@/pages/dashboard/dashboardPageStore'; -import * as ConfigAuthorShared from './workflowConfigAuthorOJS'; -import * as ConfigEditorialShared from './workflowConfigEditorialOJS'; - import * as ConfigAuthorOJS from './workflowConfigAuthorOJS'; import * as ConfigEditorialOJS from './workflowConfigEditorialOJS'; +import {consolidateCommonAndSpecificItems} from './workflowConfigHelpers'; export function useWorkflowConfigOJS({dashboardPage}) { let Configs = null; if (dashboardPage === DashboardPageTypes.EDITORIAL_DASHBOARD) { - Configs = { - getHeaderItems: ConfigEditorialOJS.getHeaderItems, - WorkflowConfig: { - ...ConfigEditorialShared.WorkflowConfig, - ...ConfigEditorialOJS.WorkflowConfig, - }, - PublicationConfig: { - ...ConfigEditorialShared.PublicationConfig, - ...ConfigEditorialOJS.PublicationConfig, - }, - }; + Configs = ConfigEditorialOJS; } else { - Configs = { - getHeaderItems: ConfigEditorialOJS.getHeaderItems, - WorkflowConfig: { - ...ConfigAuthorShared.WorkflowConfig, - ...ConfigAuthorOJS.WorkflowConfig, - }, - PublicationConfig: { - ...ConfigAuthorShared.PublicationConfig, - ...ConfigAuthorOJS.PublicationConfig, - }, - }; + Configs = ConfigAuthorOJS; } function _getItems( @@ -60,20 +38,13 @@ export function useWorkflowConfigOJS({dashboardPage}) { return []; } - const commonItems = - Configs.WorkflowConfig?.common?.[getterFnName]?.(itemsArgs); - - // early return, if common logic decides there is nothing more to show - if (commonItems?.shouldContinue === false) { - return commonItems?.items || []; - } - - return [ - ...(commonItems?.items || []), - ...(Configs.WorkflowConfig[selectedMenuState.stageId]?.[getterFnName]?.( - itemsArgs, - ) || []), - ]; + return consolidateCommonAndSpecificItems( + Configs.WorkflowConfig, + // workflow config is using stageId as keys + selectedMenuState.stageId, + getterFnName, + itemsArgs, + ); } else if (selectedMenuState.primaryMenuItem === 'publication') { const itemsArgs = { submission, @@ -86,19 +57,13 @@ export function useWorkflowConfigOJS({dashboardPage}) { return []; } - const commonItems = - Configs.PublicationConfig?.common?.[getterFnName]?.(itemsArgs); - - if (commonItems?.shouldContinue === false) { - return commonItems?.items || []; - } - - return [ - ...(commonItems?.items || []), - ...(Configs.PublicationConfig[selectedMenuState.secondaryMenuItem]?.[ - getterFnName - ]?.(itemsArgs) || []), - ]; + return consolidateCommonAndSpecificItems( + Configs.PublicationConfig, + // publication config is using menu name as keys + selectedMenuState.secondaryMenuItem, + getterFnName, + itemsArgs, + ); } } diff --git a/src/pages/workflow/composables/useWorkflowConfig/useWorkflowConfigOMP.js b/src/pages/workflow/composables/useWorkflowConfig/useWorkflowConfigOMP.js index 8453d3f25..48d6c7fe8 100644 --- a/src/pages/workflow/composables/useWorkflowConfig/useWorkflowConfigOMP.js +++ b/src/pages/workflow/composables/useWorkflowConfig/useWorkflowConfigOMP.js @@ -1,42 +1,20 @@ -import {useLocalize} from '@/composables/useLocalize'; import {DashboardPageTypes} from '@/pages/dashboard/dashboardPageStore'; - +import {deepMerge} from '@/utils/deepMerge'; import * as ConfigAuthorOJS from './workflowConfigAuthorOJS'; import * as ConfigEditorialOJS from './workflowConfigEditorialOJS'; import * as ConfigAuthorOMP from './workflowConfigAuthorOMP'; import * as ConfigEditorialOMP from './workflowConfigEditorialOMP'; -export function useWorkflowConfigOMP({dashboardPage}) { - const {t} = useLocalize(); +import {consolidateCommonAndSpecificItems} from './workflowConfigHelpers'; +export function useWorkflowConfigOMP({dashboardPage}) { let Configs = null; if (dashboardPage === DashboardPageTypes.EDITORIAL_DASHBOARD) { - Configs = { - getHeaderItems: ConfigEditorialOMP.getHeaderItems, - WorkflowConfig: { - ...ConfigEditorialOJS.WorkflowConfig, - ...ConfigEditorialOMP.WorkflowConfig, - }, - PublicationConfig: { - ...ConfigEditorialOJS.PublicationConfig, - ...ConfigEditorialOMP.PublicationConfig, - }, - MarketingConfig: ConfigEditorialOMP.MarketingConfig, - }; + Configs = deepMerge(deepMerge({}, ConfigEditorialOJS), ConfigEditorialOMP); } else { - Configs = { - getHeaderItems: ConfigEditorialOMP.getHeaderItems, - WorkflowConfig: { - ...ConfigAuthorOJS.WorkflowConfig, - ...ConfigAuthorOMP.WorkflowConfig, - }, - PublicationConfig: { - ...ConfigAuthorOJS.PublicationConfig, - ...ConfigAuthorOMP.PublicationConfig, - }, - }; + Configs = deepMerge(deepMerge({}, ConfigAuthorOJS), ConfigAuthorOMP); } function _getItems( @@ -64,33 +42,12 @@ export function useWorkflowConfigOMP({dashboardPage}) { return []; } - if (!permissions.accessibleStages.includes(selectedMenuState.stageId)) { - if (getterFnName === 'getPrimaryItems') { - return [ - { - component: 'PrimaryBasicMetadata', - props: {body: t('user.authorization.accessibleWorkflowStage')}, - }, - ]; - } else { - return []; - } - } - - const commonItems = - Configs.WorkflowConfig?.common?.[getterFnName]?.(itemsArgs); - - // early return, if common logic decides there is nothing more to show - if (commonItems?.shouldContinue === false) { - return commonItems?.items || []; - } - - return [ - ...(commonItems?.items || []), - ...(Configs.WorkflowConfig[selectedMenuState.stageId]?.[getterFnName]?.( - itemsArgs, - ) || []), - ]; + return consolidateCommonAndSpecificItems( + Configs.WorkflowConfig, + selectedMenuState.stageId, + getterFnName, + itemsArgs, + ); } else if (selectedMenuState.primaryMenuItem === 'publication') { const itemsArgs = { submission, @@ -103,18 +60,12 @@ export function useWorkflowConfigOMP({dashboardPage}) { return []; } - const commonItems = - Configs.PublicationConfig?.common?.[getterFnName]?.(itemsArgs); - - if (commonItems?.shouldContinue === false) { - return commonItems?.items || []; - } - return [ - ...(commonItems?.items || []), - ...(Configs.PublicationConfig[selectedMenuState.secondaryMenuItem]?.[ - getterFnName - ]?.(itemsArgs) || []), - ]; + return consolidateCommonAndSpecificItems( + Configs.PublicationConfig, + selectedMenuState.secondaryMenuItem, + getterFnName, + itemsArgs, + ); } else if (selectedMenuState.primaryMenuItem === 'marketing') { const itemsArgs = { submission, @@ -126,19 +77,12 @@ export function useWorkflowConfigOMP({dashboardPage}) { return []; } - const commonItems = - Configs.MarketingConfig?.common?.[getterFnName]?.(itemsArgs); - - if (commonItems?.shouldContinue === false) { - return commonItems?.items || []; - } - - return [ - ...(commonItems?.items || []), - ...(Configs.MarketingConfig[selectedMenuState.secondaryMenuItem]?.[ - getterFnName - ]?.(itemsArgs) || []), - ]; + return consolidateCommonAndSpecificItems( + Configs.MarketingConfig, + selectedMenuState.secondaryMenuItem, + getterFnName, + itemsArgs, + ); } } diff --git a/src/pages/workflow/composables/useWorkflowConfig/useWorkflowConfigOPS.js b/src/pages/workflow/composables/useWorkflowConfig/useWorkflowConfigOPS.js index da379b3b8..4b56a47fa 100644 --- a/src/pages/workflow/composables/useWorkflowConfig/useWorkflowConfigOPS.js +++ b/src/pages/workflow/composables/useWorkflowConfig/useWorkflowConfigOPS.js @@ -1,34 +1,17 @@ -import {useLocalize} from '@/composables/useLocalize'; import {DashboardPageTypes} from '@/pages/dashboard/dashboardPageStore'; import * as ConfigAuthorOPS from './workflowConfigAuthorOPS'; import * as ConfigEditorialOPS from './workflowConfigEditorialOPS'; -export function useWorkflowConfigOPS({dashboardPage}) { - const {t} = useLocalize(); +import {consolidateCommonAndSpecificItems} from './workflowConfigHelpers'; +export function useWorkflowConfigOPS({dashboardPage}) { let Configs = null; if (dashboardPage === DashboardPageTypes.EDITORIAL_DASHBOARD) { - Configs = { - getHeaderItems: ConfigEditorialOPS.getHeaderItems, - WorkflowConfig: { - ...ConfigEditorialOPS.WorkflowConfig, - }, - PublicationConfig: { - ...ConfigEditorialOPS.PublicationConfig, - }, - }; + Configs = ConfigEditorialOPS; } else { - Configs = { - getHeaderItems: ConfigEditorialOPS.getHeaderItems, - WorkflowConfig: { - ...ConfigAuthorOPS.WorkflowConfig, - }, - PublicationConfig: { - ...ConfigAuthorOPS.PublicationConfig, - }, - }; + Configs = ConfigAuthorOPS; } function _getItems( @@ -56,32 +39,12 @@ export function useWorkflowConfigOPS({dashboardPage}) { return []; } - if (!permissions.accessibleStages.includes(selectedMenuState.stageId)) { - if (getterFnName === 'getPrimaryItems') { - return [ - { - component: 'PrimaryBasicMetadata', - props: {body: t('user.authorization.accessibleWorkflowStage')}, - }, - ]; - } else { - return []; - } - } - const commonItems = - Configs.WorkflowConfig?.common?.[getterFnName]?.(itemsArgs); - - // early return, if common logic decides there is nothing more to show - if (commonItems?.shouldContinue === false) { - return commonItems?.items || []; - } - - return [ - ...(commonItems?.items || []), - ...(Configs.WorkflowConfig[selectedMenuState.stageId]?.[getterFnName]?.( - itemsArgs, - ) || []), - ]; + return consolidateCommonAndSpecificItems( + Configs.WorkflowConfig, + selectedMenuState.stageId, + getterFnName, + itemsArgs, + ); } else if (selectedMenuState.primaryMenuItem === 'publication') { const itemsArgs = { submission, @@ -94,19 +57,12 @@ export function useWorkflowConfigOPS({dashboardPage}) { return []; } - const commonItems = - Configs.PublicationConfig?.common?.[getterFnName]?.(itemsArgs); - - if (commonItems?.shouldContinue === false) { - return commonItems?.items || []; - } - - return [ - ...(commonItems?.items || []), - ...(Configs.PublicationConfig[selectedMenuState.secondaryMenuItem]?.[ - getterFnName - ]?.(itemsArgs) || []), - ]; + return consolidateCommonAndSpecificItems( + Configs.PublicationConfig, + selectedMenuState.secondaryMenuItem, + getterFnName, + itemsArgs, + ); } } diff --git a/src/pages/workflow/composables/useWorkflowConfig/workflowConfigEditorialOJS.js b/src/pages/workflow/composables/useWorkflowConfig/workflowConfigEditorialOJS.js index 623c46fed..d81c9cf1d 100644 --- a/src/pages/workflow/composables/useWorkflowConfig/workflowConfigEditorialOJS.js +++ b/src/pages/workflow/composables/useWorkflowConfig/workflowConfigEditorialOJS.js @@ -3,6 +3,7 @@ import {Actions} from '../useWorkflowActions'; import {useSubmission} from '@/composables/useSubmission'; import {Actions as WorkflowActions} from '../useWorkflowActions'; import {Actions as DecisionActions} from '../useWorkflowDecisions'; +import {addItemIf} from './workflowConfigHelpers'; const { hasSubmissionPassedStage, getActiveStage, @@ -11,12 +12,6 @@ const { } = useSubmission(); const {t} = useLocalize(); -export function addItemIf(items, item, condition) { - if (condition) { - items.push(item); - } -} - export function getHeaderItems({ submission, selectedPublication, diff --git a/src/pages/workflow/composables/useWorkflowConfig/workflowConfigEditorialOMP.js b/src/pages/workflow/composables/useWorkflowConfig/workflowConfigEditorialOMP.js index 580fce3e0..e760e6306 100644 --- a/src/pages/workflow/composables/useWorkflowConfig/workflowConfigEditorialOMP.js +++ b/src/pages/workflow/composables/useWorkflowConfig/workflowConfigEditorialOMP.js @@ -2,10 +2,11 @@ import {useLocalize} from '@/composables/useLocalize'; import {Actions} from '../useWorkflowActions'; import {useSubmission} from '@/composables/useSubmission'; import {Actions as DecisionActions} from '../useWorkflowDecisions'; -import {addItemIf} from './workflowConfigEditorialOJS'; +import {addItemIf} from './workflowConfigHelpers'; const {hasSubmissionPassedStage, getStageById, isDecisionAvailable} = useSubmission(); const {t} = useLocalize(); + export function getHeaderItems({ submission, selectedPublication, @@ -57,50 +58,8 @@ export function getHeaderItems({ export const WorkflowConfig = { [pkp.const.WORKFLOW_STAGE_ID_SUBMISSION]: { - getPrimaryItems: ({submission, selectedStageId, selectedReviewRound}) => { - const items = []; - - if ( - hasSubmissionPassedStage( - submission, - pkp.const.WORKFLOW_STAGE_ID_SUBMISSION, - ) - ) { - items.push({component: 'SubmissionStatus', props: {submission}}); - } - - items.push({ - component: 'FileManager', - props: { - namespace: 'SUBMISSION_FILES', - submission: submission, - submissionStageId: selectedStageId, - }, - }); - - items.push({ - component: 'DiscussionManager', - props: {submissionId: submission.id, stageId: selectedStageId}, - }); - - return items; - }, - getSecondaryItems: ({submission, selectedReviewRound, selectedStageId}) => { - const items = []; - items.push({ - component: 'ParticipantManager', - props: { - submission, - submissionStageId: selectedStageId, - }, - }); - - return items; - }, - getActionItems: ({submission, selectedStageId, selectedReviewRound}) => { const items = []; - addItemIf( items, { diff --git a/src/pages/workflow/composables/useWorkflowConfig/workflowConfigEditorialOPS.js b/src/pages/workflow/composables/useWorkflowConfig/workflowConfigEditorialOPS.js index 6ab79adf3..5284300f1 100644 --- a/src/pages/workflow/composables/useWorkflowConfig/workflowConfigEditorialOPS.js +++ b/src/pages/workflow/composables/useWorkflowConfig/workflowConfigEditorialOPS.js @@ -2,7 +2,8 @@ import {useLocalize} from '@/composables/useLocalize'; import {Actions} from '../useWorkflowActions'; import {useSubmission} from '@/composables/useSubmission'; import {Actions as DecisionActions} from '../useWorkflowDecisions'; -import {addItemIf} from './workflowConfigEditorialOJS'; +import {addItemIf} from './workflowConfigHelpers'; + const {hasSubmissionPassedStage, isDecisionAvailable, getActiveStage} = useSubmission(); const {t} = useLocalize(); @@ -412,25 +413,6 @@ export const PublicationConfig = { ]; }, }, - jats: { - getPrimaryItems: ({ - submission, - selectedPublication, - pageInitConfig, - permissions, - }) => { - return [ - { - component: 'WorkflowPublicationJats', - props: { - canEdit: permissions.canEditPublication, - submission, - publication: selectedPublication, - }, - }, - ]; - }, - }, galleys: { getPrimaryItems: ({submission, selectedPublication, permissions}) => { return [ diff --git a/src/pages/workflow/composables/useWorkflowConfig/workflowConfigHelpers.js b/src/pages/workflow/composables/useWorkflowConfig/workflowConfigHelpers.js new file mode 100644 index 000000000..00b320dbf --- /dev/null +++ b/src/pages/workflow/composables/useWorkflowConfig/workflowConfigHelpers.js @@ -0,0 +1,26 @@ +// Useful for some common functionality thats covered in Config.common, +// like permissions or Stage not initiated +export function consolidateCommonAndSpecificItems( + Config, + configItemKey, + getterFnName, + itemsArgs, +) { + const commonItems = Config?.common?.[getterFnName]?.(itemsArgs); + + // early return, if common logic decides there should not be any specific items displayed + if (commonItems?.shouldContinue === false) { + return commonItems?.items || []; + } + + return [ + ...(commonItems?.items || []), + ...(Config?.[configItemKey]?.[getterFnName]?.(itemsArgs) || []), + ]; +} + +export function addItemIf(items, item, condition) { + if (condition) { + items.push(item); + } +} diff --git a/src/utils/deepMerge.js b/src/utils/deepMerge.js new file mode 100644 index 000000000..84d045289 --- /dev/null +++ b/src/utils/deepMerge.js @@ -0,0 +1,31 @@ +/** + * Simple object check. + * @param item + * @returns {boolean} + */ +export function isObject(item) { + return item && typeof item === 'object' && !Array.isArray(item); +} + +/** + * Deep merge two objects. + * @param target + * @param ...sources + */ +export function deepMerge(target, ...sources) { + if (!sources.length) return target; + const source = sources.shift(); + + if (isObject(target) && isObject(source)) { + for (const key in source) { + if (isObject(source[key])) { + if (!target[key]) Object.assign(target, {[key]: {}}); + deepMerge(target[key], source[key]); + } else { + Object.assign(target, {[key]: source[key]}); + } + } + } + + return deepMerge(target, ...sources); +}