From 1b42d2fc07ebca065e203379778f7a05bd5a0022 Mon Sep 17 00:00:00 2001 From: Touhidur Rahman Date: Mon, 4 Aug 2025 18:09:05 +0600 Subject: [PATCH 1/5] pkp/pkp-lib#9295 refactor to remove publish column and depend on new status as STATUS_READY_TO_PUBLISH --- public/globals.js | 1 + src/components/Form/fields/FieldSelectIssue.vue | 2 +- .../workflow/composables/useWorkflowActions.js | 14 ++++++++++---- .../workflowConfigEditorialOJS.js | 5 ++++- 4 files changed, 16 insertions(+), 6 deletions(-) diff --git a/public/globals.js b/public/globals.js index 86a0a9c88..57a55e62a 100644 --- a/public/globals.js +++ b/public/globals.js @@ -100,6 +100,7 @@ window.pkp = { STATUS_PUBLISHED: 3, STATUS_DECLINED: 4, STATUS_SCHEDULED: 5, + STATUS_READY_TO_PUBLISH: 6, WORKFLOW_STAGE_ID_SUBMISSION: 1, WORKFLOW_STAGE_ID_INTERNAL_REVIEW: 2, WORKFLOW_STAGE_ID_EXTERNAL_REVIEW: 3, diff --git a/src/components/Form/fields/FieldSelectIssue.vue b/src/components/Form/fields/FieldSelectIssue.vue index 9dffd4d5f..e87f0d0e5 100644 --- a/src/components/Form/fields/FieldSelectIssue.vue +++ b/src/components/Form/fields/FieldSelectIssue.vue @@ -167,7 +167,7 @@ export default { const workflowStore = useWorkflowStore(); workflowStore.workflowAssignToIssue({}, (finishedData) => { - if (finishedData.data) { + if (finishedData?.data) { this.currentValue = finishedData.data.issueId; } }); diff --git a/src/pages/workflow/composables/useWorkflowActions.js b/src/pages/workflow/composables/useWorkflowActions.js index 839ebdda3..26b5fc0b4 100644 --- a/src/pages/workflow/composables/useWorkflowActions.js +++ b/src/pages/workflow/composables/useWorkflowActions.js @@ -126,11 +126,11 @@ export function useWorkflowActions() { return; } - // If the publication is marked as published, + // If the publication is marked as ready to publish, // and not assigned to an issue, or assigned to an issue that is not published (e.g. future issue), // we can publish the publication immediately as issueless or continuous publication if ( - selectedPublication.published && + selectedPublication.status === pkp.const.STATUS_READY_TO_PUBLISH && (selectedPublication.issueId === null || !pageInitConfig.publicationSettings.issuePublishedStatus[ selectedPublication.issueId @@ -143,7 +143,10 @@ export function useWorkflowActions() { return; } - if (selectedPublication.issueId === null || selectedPublication.published) { + if ( + selectedPublication.issueId === null || + selectedPublication.status === pkp.const.STATUS_READY_TO_PUBLISH + ) { const {url} = useLegacyGridUrl({ component: 'modals.publish.AssignToIssueHandler', op: 'assign', @@ -165,7 +168,10 @@ export function useWorkflowActions() { }, { onClose: async ({formId, data}) => { - if (data?.issueId || data?.published) { + if ( + data?.issueId || + data?.status === pkp.const.STATUS_READY_TO_PUBLISH + ) { workflowScheduleForPublication( {submission, selectedPublication}, finishedCallback, diff --git a/src/pages/workflow/composables/useWorkflowConfig/workflowConfigEditorialOJS.js b/src/pages/workflow/composables/useWorkflowConfig/workflowConfigEditorialOJS.js index 7de5afac9..ab5be612b 100644 --- a/src/pages/workflow/composables/useWorkflowConfig/workflowConfigEditorialOJS.js +++ b/src/pages/workflow/composables/useWorkflowConfig/workflowConfigEditorialOJS.js @@ -768,7 +768,10 @@ export const PublicationConfig = { if (!permissions.canPublish) { return []; } - if (selectedPublication.status === pkp.const.STATUS_QUEUED) { + if ( + selectedPublication.status === pkp.const.STATUS_QUEUED || + selectedPublication.status === pkp.const.STATUS_READY_TO_PUBLISH + ) { if ( hasSubmissionPassedStage( submission, From 28233f6bcc9d5bf1f6d47a046ee85f1de9e06dc7 Mon Sep 17 00:00:00 2001 From: Touhidur Rahman Date: Wed, 6 Aug 2025 23:04:48 +0600 Subject: [PATCH 2/5] pkp/pkp-lib#9295 separate component for issue selection --- public/globals.js | 1 + src/components/Form/FormGroup.vue | 2 + .../Form/fields/FieldIssueSelection.vue | 293 ++++++++++++++++++ src/composables/useForm.js | 1 + .../publication/WorkflowVersionSideModal.vue | 5 + .../composables/useWorkflowActions.js | 200 +++++++----- .../composables/useWorkflowVersionForm.js | 46 ++- 7 files changed, 474 insertions(+), 74 deletions(-) create mode 100644 src/components/Form/fields/FieldIssueSelection.vue diff --git a/public/globals.js b/public/globals.js index 57a55e62a..dd833b000 100644 --- a/public/globals.js +++ b/public/globals.js @@ -101,6 +101,7 @@ window.pkp = { STATUS_DECLINED: 4, STATUS_SCHEDULED: 5, STATUS_READY_TO_PUBLISH: 6, + STATUS_READY_TO_SCHEDULE: 7, WORKFLOW_STAGE_ID_SUBMISSION: 1, WORKFLOW_STAGE_ID_INTERNAL_REVIEW: 2, WORKFLOW_STAGE_ID_EXTERNAL_REVIEW: 3, diff --git a/src/components/Form/FormGroup.vue b/src/components/Form/FormGroup.vue index e2edb9c3b..d4898a87d 100644 --- a/src/components/Form/FormGroup.vue +++ b/src/components/Form/FormGroup.vue @@ -77,6 +77,7 @@ import FieldRichText from './fields/FieldRichText.vue'; import FieldSelect from './fields/FieldSelect.vue'; import FieldSelectIssue from './fields/FieldSelectIssue.vue'; import FieldSelectIssues from './fields/FieldSelectIssues.vue'; +import FieldIssueSelection from './fields/FieldIssueSelection.vue'; import FieldSelectSubmissions from './fields/FieldSelectSubmissions.vue'; import FieldSelectUsers from './fields/FieldSelectUsers.vue'; import FieldShowEnsuringLink from './fields/FieldShowEnsuringLink.vue'; @@ -112,6 +113,7 @@ export default { FieldSelect, FieldSelectIssue, FieldSelectIssues, + FieldIssueSelection, FieldSelectSubmissions, FieldSelectUsers, FieldShowEnsuringLink, diff --git a/src/components/Form/fields/FieldIssueSelection.vue b/src/components/Form/fields/FieldIssueSelection.vue new file mode 100644 index 000000000..0708c3a99 --- /dev/null +++ b/src/components/Form/fields/FieldIssueSelection.vue @@ -0,0 +1,293 @@ + + + diff --git a/src/composables/useForm.js b/src/composables/useForm.js index 161a8679e..b03e8acb0 100644 --- a/src/composables/useForm.js +++ b/src/composables/useForm.js @@ -552,6 +552,7 @@ export function useForm(_form = {}, {customSubmit} = {}) { initEmptyForm, addPage, addGroup, + addField, addFieldText, addFieldSelect, addFieldOptions, diff --git a/src/pages/workflow/components/publication/WorkflowVersionSideModal.vue b/src/pages/workflow/components/publication/WorkflowVersionSideModal.vue index c68780c46..851f6ad79 100644 --- a/src/pages/workflow/components/publication/WorkflowVersionSideModal.vue +++ b/src/pages/workflow/components/publication/WorkflowVersionSideModal.vue @@ -29,11 +29,16 @@ const props = defineProps({ type: Function, default: () => () => {}, }, + issueCount: { + type: Number, + default: 0, + }, }); const {form, set} = useWorkflowVersionForm( 'publish', props.onCloseFn, props.onSubmitFn, + props.issueCount, // Pass issue count to the form ); diff --git a/src/pages/workflow/composables/useWorkflowActions.js b/src/pages/workflow/composables/useWorkflowActions.js index 26b5fc0b4..4afb66e17 100644 --- a/src/pages/workflow/composables/useWorkflowActions.js +++ b/src/pages/workflow/composables/useWorkflowActions.js @@ -4,6 +4,7 @@ import {useLocalize} from '@/composables/useLocalize'; import {useUrl} from '@/composables/useUrl'; import {useFetch} from '@/composables/useFetch'; import {useLegacyGridUrl} from '@/composables/useLegacyGridUrl'; +import {useApp} from '@/composables/useApp'; import WorkflowModalChangeSubmissionLanguage from '@/pages/workflow/modals/WorkflowChangeSubmissionLanguageModal.vue'; import WorkflowVersionDialogBody from '@/pages/workflow/components/publication/WorkflowVersionDialogBody.vue'; @@ -37,10 +38,23 @@ export function useWorkflowActions() { redirectToPage(); } + /** + * Legacy function to assign a publication to an issue. + * + * NOTE: This function is kept for backward compatibility. + * For new implementations, consider using the combined version assignment + * and issue assignment functionality in workflowAssignPublicationStage. + * + * @param {Object} params - Parameters object + * @param {Object} params.submission - The submission object + * @param {Object} params.selectedPublication - The selected publication + * @param {Function} finishedCallback - Callback function to execute when finished + */ function workflowAssignToIssue( {submission, selectedPublication}, finishedCallback, ) { + console.log('Legacy workflowAssignToIssue called'); const {openLegacyModal} = useLegacyGridUrl({ component: 'modals.publish.AssignToIssueHandler', op: 'assign', @@ -84,8 +98,29 @@ export function useWorkflowActions() { ); } + /** + * Open a modal to assign publication stage and optionally assign to an issue. + * + * This function combines version assignment with issue assignment in a single modal. + * The modal will show: + * - Version stage selection (required for unassigned versions) + * - Version significance selection (major/minor) + * - Issue assignment options (only if issues exist in the journal) + * + * Issue assignment options include: + * - No issue assignment (publish immediately) + * - Assign to future issue and publish immediately + * - Assign to future issue and schedule only + * - Assign to current/back issue (publish immediately) + * + * @param {Object} params - Parameters object + * @param {Object} params.selectedPublication - The selected publication + * @param {Object} params.submission - The submission object + * @param {Object} params.pageInitConfig - Page initialization config containing issue count + * @param {Function} finishedCallback - Callback function to execute when finished + */ function workflowAssignPublicationStage( - {selectedPublication, submission}, + {selectedPublication, submission, pageInitConfig}, finishedCallback, ) { const {openSideModal, closeSideModal} = useModal(); @@ -94,9 +129,14 @@ export function useWorkflowActions() { closeSideModal(WorkflowVersionSideModal); } + // Get issue count from pageInitConfig if available + // This determines whether to show issue assignment options + const issueCount = pageInitConfig?.publicationSettings?.countIssues || 0; + openSideModal(WorkflowVersionSideModal, { onCloseFn, onSubmitFn: finishedCallback, + issueCount, }); } @@ -104,10 +144,23 @@ export function useWorkflowActions() { {pageInitConfig, selectedPublication, submission}, finishedCallback, ) { - // if version is unassigned, we need to assign it to a publication stage first - if (!selectedPublication.versionStage) { + const {isOJS} = useApp(); + + // if version is unassigned + // or OJS specific the publication status not to + // STATUS_READY_TO_PUBLISH or STATUS_READY_TO_SCHEDULE, + // we need to assign the publication stage and status (for issue or issueless context) + const requirePublicationStage = isOJS() + ? !selectedPublication.versionStage && + ![ + pkp.const.STATUS_READY_TO_PUBLISH, + pkp.const.STATUS_READY_TO_SCHEDULE, + ].includes(selectedPublication.status) + : !selectedPublication.versionStage; + + if (requirePublicationStage) { return workflowAssignPublicationStage( - {selectedPublication, submission}, + {selectedPublication, submission, pageInitConfig}, (publicationData) => workflowAssignToIssueAndScheduleForPublication( {pageInitConfig, selectedPublication: publicationData, submission}, @@ -116,78 +169,85 @@ export function useWorkflowActions() { ); } + workflowScheduleForPublication( + {submission, selectedPublication}, + finishedCallback, + ); + + // TODO : Rest of the codes are dead code, need cleanup + // if there are no issues, we can schedule the publication immediately - if (pageInitConfig.publicationSettings.countIssues === 0) { - workflowScheduleForPublication( - {submission, selectedPublication}, - finishedCallback, - ); + // if (pageInitConfig.publicationSettings.countIssues === 0) { + // workflowScheduleForPublication( + // {submission, selectedPublication}, + // finishedCallback, + // ); - return; - } + // return; + // } // If the publication is marked as ready to publish, // and not assigned to an issue, or assigned to an issue that is not published (e.g. future issue), // we can publish the publication immediately as issueless or continuous publication - if ( - selectedPublication.status === pkp.const.STATUS_READY_TO_PUBLISH && - (selectedPublication.issueId === null || - !pageInitConfig.publicationSettings.issuePublishedStatus[ - selectedPublication.issueId - ]) - ) { - workflowScheduleForPublication( - {submission, selectedPublication}, - finishedCallback, - ); - return; - } - - if ( - selectedPublication.issueId === null || - selectedPublication.status === pkp.const.STATUS_READY_TO_PUBLISH - ) { - const {url} = useLegacyGridUrl({ - component: 'modals.publish.AssignToIssueHandler', - op: 'assign', - params: { - submissionId: submission.id, - publicationId: selectedPublication.id, - }, - }); - const {openSideModal} = useModal(); - - openSideModal( - 'LegacyAjax', - { - legacyOptions: { - title: t('publication.selectIssue'), - url, - closeOnFormSuccessId: pkp.const.FORM_ASSIGN_TO_ISSUE, - }, - }, - { - onClose: async ({formId, data}) => { - if ( - data?.issueId || - data?.status === pkp.const.STATUS_READY_TO_PUBLISH - ) { - workflowScheduleForPublication( - {submission, selectedPublication}, - finishedCallback, - ); - } else { - finishedCallback(); - } - }, - }, - ); - } else { - workflowScheduleForPublication( - {submission, selectedPublication}, - finishedCallback, - ); - } + // if ( + // selectedPublication.status === pkp.const.STATUS_READY_TO_PUBLISH && + // (selectedPublication.issueId === null || + // !pageInitConfig.publicationSettings.issuePublishedStatus[ + // selectedPublication.issueId + // ]) + // ) { + // workflowScheduleForPublication( + // {submission, selectedPublication}, + // finishedCallback, + // ); + // return; + // } + + // if ( + // selectedPublication.issueId === null || + // selectedPublication.status === pkp.const.STATUS_READY_TO_PUBLISH + // ) { + // const {url} = useLegacyGridUrl({ + // component: 'modals.publish.AssignToIssueHandler', + // op: 'assign', + // params: { + // submissionId: submission.id, + // publicationId: selectedPublication.id, + // }, + // }); + // const {openSideModal} = useModal(); + + // openSideModal( + // 'LegacyAjax', + // { + // legacyOptions: { + // title: t('publication.selectIssue'), + // url, + // closeOnFormSuccessId: pkp.const.FORM_ASSIGN_TO_ISSUE, + // }, + // }, + // { + // onClose: async ({formId, data}) => { + // if ( + // data?.issueId || + // data?.status === pkp.const.STATUS_READY_TO_PUBLISH + // ) { + // workflowScheduleForPublication( + // {submission, selectedPublication}, + // finishedCallback, + // ); + // } else { + // finishedCallback(); + // } + // }, + // }, + // ); + // } else { + // workflowScheduleForPublication( + // {submission, selectedPublication}, + // finishedCallback, + // ); + // } } function workflowScheduleForPublication( diff --git a/src/pages/workflow/composables/useWorkflowVersionForm.js b/src/pages/workflow/composables/useWorkflowVersionForm.js index a017385df..0ed862f0a 100644 --- a/src/pages/workflow/composables/useWorkflowVersionForm.js +++ b/src/pages/workflow/composables/useWorkflowVersionForm.js @@ -5,6 +5,7 @@ import {useFetch} from '@/composables/useFetch'; import {useLocalize} from '@/composables/useLocalize'; import {useSubmission} from '@/composables/useSubmission'; import {useWorkflowStore} from '@/pages/workflow/workflowStore'; +import {useApp} from '@/composables/useApp'; const VERSION_MODE = { CREATE: 'createNewVersion', // the "Create New Version" action in the publication workflow menu @@ -48,12 +49,14 @@ export function useWorkflowVersionForm( versionMode = 'createNewVersion', closeDialog = () => {}, onSubmitFn = null, + issueCount = 0, ) { const store = useWorkflowStore(); const {t} = useLocalize(); const {getLatestPublication} = useSubmission(); let publications = []; let latestPublication = null; + const {isOJS} = useApp(); // Determine the mode based on the versionMode parameter // versionMode can be one of 'createNewVersion', 'sendToTextEditor', or 'publish' @@ -95,6 +98,29 @@ export function useWorkflowVersionForm( `submissions/${store.submission.id}/publications/${publicationId}/version`, ); + // Prepare request body with version data + const requestBody = { + versionStage: formData.versionStage, + versionIsMinor: formData.versionIsMinor, + }; + + if (isOJS()) { + // if the issue count is 0, e.g. issueless context, + // we can safely set the issueId and status to null and + // STATUS_READY_TO_PUBLISH + if (issueCount === 0) { + requestBody.issueId = null; + requestBody.status = pkp.const.STATUS_READY_TO_PUBLISH; + } + // Add issue assignment data if in publish mode and issue assignment is provided + else if (modeState.isPublishMode && formData.issueAssignment) { + const issueData = formData.issueAssignment; + + requestBody.issueId = issueData.issueId; + requestBody.status = issueData.publicationStatus; + } + } + const { fetch, data: publicationData, @@ -102,10 +128,7 @@ export function useWorkflowVersionForm( isSuccess, } = useFetch(versionUrl, { method: shouldCreateNewVersion ? 'POST' : 'PUT', - body: { - versionStage: formData.versionStage, - versionIsMinor: formData.versionIsMinor, - }, + body: requestBody, expectValidationError: true, }); @@ -138,6 +161,7 @@ export function useWorkflowVersionForm( const { form, initEmptyForm, + addField, addFieldSelect, addPage, addGroup, @@ -282,6 +306,20 @@ export function useWorkflowVersionForm( }), ); + // Issue assignment fields (only visible in publish mode) + if (modeState.isPublishMode && issueCount > 0 && isOJS()) { + addField('issueAssignment', { + component: 'FieldIssueSelection', + issueCount: issueCount, + value: { + assignmentType: 4, // 4 = CURRENT_BACK_ISSUES_PUBLISHED + issueId: store.selectedPublication?.issueId, + publicationStatus: store.selectedPublication?.status, + }, + isRequired: true, // Make it required when visible + }); + } + setValue( 'versionSource', modeState.isCreateMode ? latestPublication?.id : null, From 0bb1ee03dcc0e9bb109c21eab3649ea563380013 Mon Sep 17 00:00:00 2001 From: Touhidur Rahman Date: Mon, 11 Aug 2025 00:08:04 +0600 Subject: [PATCH 3/5] pkp/pkp-lib#9295 replaced issue from with new field issue selection --- public/globals.js | 2 - src/components/Form/FormGroup.vue | 4 +- .../Form/fields/FieldIssueSelection.vue | 216 ++++++++++++------ .../Form/fields/FieldSelectIssue.vue | 1 + src/composables/useLegacyGridUrl.test.js | 20 +- .../publication/WorkflowVersionSideModal.vue | 2 +- .../composables/useWorkflowActions.js | 6 +- .../composables/useWorkflowVersionForm.js | 11 +- 8 files changed, 173 insertions(+), 89 deletions(-) diff --git a/public/globals.js b/public/globals.js index dd833b000..86a0a9c88 100644 --- a/public/globals.js +++ b/public/globals.js @@ -100,8 +100,6 @@ window.pkp = { STATUS_PUBLISHED: 3, STATUS_DECLINED: 4, STATUS_SCHEDULED: 5, - STATUS_READY_TO_PUBLISH: 6, - STATUS_READY_TO_SCHEDULE: 7, WORKFLOW_STAGE_ID_SUBMISSION: 1, WORKFLOW_STAGE_ID_INTERNAL_REVIEW: 2, WORKFLOW_STAGE_ID_EXTERNAL_REVIEW: 3, diff --git a/src/components/Form/FormGroup.vue b/src/components/Form/FormGroup.vue index d4898a87d..b0a80ab65 100644 --- a/src/components/Form/FormGroup.vue +++ b/src/components/Form/FormGroup.vue @@ -75,7 +75,7 @@ import FieldRadioInput from './fields/FieldRadioInput.vue'; import FieldRichTextarea from './fields/FieldRichTextarea.vue'; import FieldRichText from './fields/FieldRichText.vue'; import FieldSelect from './fields/FieldSelect.vue'; -import FieldSelectIssue from './fields/FieldSelectIssue.vue'; +// import FieldSelectIssue from './fields/FieldSelectIssue.vue'; import FieldSelectIssues from './fields/FieldSelectIssues.vue'; import FieldIssueSelection from './fields/FieldIssueSelection.vue'; import FieldSelectSubmissions from './fields/FieldSelectSubmissions.vue'; @@ -111,7 +111,7 @@ export default { FieldRichTextarea, FieldRichText, FieldSelect, - FieldSelectIssue, + // FieldSelectIssue, FieldSelectIssues, FieldIssueSelection, FieldSelectSubmissions, diff --git a/src/components/Form/fields/FieldIssueSelection.vue b/src/components/Form/fields/FieldIssueSelection.vue index 0708c3a99..e5b459ce9 100644 --- a/src/components/Form/fields/FieldIssueSelection.vue +++ b/src/components/Form/fields/FieldIssueSelection.vue @@ -17,12 +17,11 @@ class="pkpFormField__description semantic-defaults" />
- -
[], }, - value: { + allErrors: { type: Object, - default: () => ({ - assignmentType: 4, // 4 = CURRENT_BACK_ISSUES_PUBLISHED - issueId: null, - publicationStatus: null, - }), + default: () => ({}), + }, + assignmentType: { + type: Number, + default: null, }, issueCount: { type: Number, default: 0, }, - allErrors: { + publication: { type: Object, - default: () => ({}), + required: true, + }, + isRequired: { + type: Boolean, + default: false, + }, + isPhpForm: { + type: Boolean, + default: false, }, }); -const emit = defineEmits(['change']); +const emit = defineEmits(['change', 'set-errors', 'set']); const {t} = useLocalize(); -const assignmentType = ref(props.value?.assignmentType || 4); // 4 = CURRENT_BACK_ISSUES_PUBLISHED -const selectedIssueId = ref(props.value?.issueId || null); +// Local state - track user selections const availableIssues = ref([]); const isLoadingIssues = ref(false); +// User selections - these are what the user has actually chosen +const selectedIssueId = ref(props.publication?.issueId || null); +const selectedAssignmentType = ref(props.assignmentType || null); + +const fetchedAssignmentType = ref(null); const assignmentOptions = ref([]); const isLoadingAssignmentOptions = ref(false); +// Computed final assignment type with fallback (only for initial/default values) +const finalAssignmentType = computed(() => { + return ( + selectedAssignmentType.value || + fetchedAssignmentType.value || + pkp.const.ISSUE_ASSIGNMENT_DEFAULT + ); +}); + const fetchAssignmentOptions = async () => { if (isLoadingAssignmentOptions.value) { return; @@ -123,7 +138,7 @@ const fetchAssignmentOptions = async () => { if (data.value) { const options = data.value.map((option) => ({ - value: option.value, // Use enum value (1, 2, 3, 4) directly + value: option.value, label: option.label, status: option.status, isPublished: option.isPublished, @@ -139,47 +154,88 @@ const fetchAssignmentOptions = async () => { } }; +const fetchAssignmentType = async () => { + // Skip if there is a preset assignment type + if (props.assignmentType !== null) { + return; + } + + try { + const {apiUrl} = useUrl( + `submissions/${props.publication.submissionId}/publications/${props.publication.id}/issueAssignmentStatus`, + ); + const {fetch, data} = useFetch(apiUrl); + await fetch(); + + if (data.value?.assignmentType) { + fetchedAssignmentType.value = data.value.assignmentType; + selectedAssignmentType.value = data.value.assignmentType; + } + } catch (error) { + console.error('Error fetching assignment type:', error); + selectedAssignmentType.value = pkp.const.ISSUE_ASSIGNMENT_DEFAULT; // Fallback to default + } +}; + const showIssueDropdown = computed(() => { + // Don't show dropdown if no assignment options are loaded yet + if (assignmentOptions.value.length === 0) { + return false; + } + const option = assignmentOptions.value.find( - (opt) => opt.value === assignmentType.value, + (opt) => opt.value === selectedAssignmentType.value, ); return option?.isPublished !== null; }); const shouldFetchPublishedIssues = computed(() => { const option = assignmentOptions.value.find( - (opt) => opt.value === assignmentType.value, + (opt) => opt.value === selectedAssignmentType.value, ); return option?.isPublished; }); -const publicationStatus = computed(() => { +const isValid = computed(() => { const option = assignmentOptions.value.find( - (opt) => opt.value === assignmentType.value, + (opt) => opt.value === selectedAssignmentType.value, // Use user's selection ); - return option?.status; + return option?.isPublished === null || selectedIssueId.value; // Use user's issue selection }); -const isValid = computed(() => { +const publicationStatus = computed(() => { const option = assignmentOptions.value.find( - (opt) => opt.value === assignmentType.value, + (opt) => opt.value === selectedAssignmentType.value, ); - return option?.isPublished === null || selectedIssueId.value; + return option?.status || null; }); const validationErrors = computed(() => { - if (props.isRequired && !isValid.value) { + if (!isValid.value) { return [t('publication.assignToIssue.validation.issueRequired')]; } return []; }); +// Emit validation errors to the form system +watch( + validationErrors, + (errors) => { + if (errors.length > 0) { + emit('set-errors', props.name, errors); + } else { + emit('set-errors', props.name, []); + } + }, + {immediate: true}, +); + const describedByErrorId = computed(() => { return `${useId()}-error`; }); const onAssignmentChange = (fieldName, propName, newValue) => { - assignmentType.value = newValue; + selectedAssignmentType.value = newValue; selectedIssueId.value = null; availableIssues.value = []; @@ -196,23 +252,37 @@ const onIssueChange = (fieldName, propName, newValue) => { }; const emitValue = () => { - const value = { - assignmentType: assignmentType.value, - issueId: selectedIssueId.value, - publicationStatus: publicationStatus.value, - }; - - // TODO: Remove this after testing - console.log('FieldIssueSelection emitting:', { - name: props.name, - formId: props.formId, - value: value, - assignmentType: assignmentType.value, - selectedIssueId: selectedIssueId.value, - publicationStatus: publicationStatus.value, - }); - - emit('change', props.name, 'value', value); + if (props.isPhpForm) { + if (assignmentOptions.value.length > 0) { + // TODO: Remove this after testing + // console.log('PHP Form - Emitting issueId, updating hiddenFields:', { + // issueId: selectedIssueId.value, // User's issue selection + // assignmentType: selectedAssignmentType.value, // User's assignment type selection + // publicationStatus: publicationStatus.value, // Calculated status from user's selection + // }); + + emit('change', 'issueId', 'value', selectedIssueId.value); + emit('change', 'prePublishStatus', 'value', publicationStatus.value); + } + } else { + const value = { + assignmentType: selectedAssignmentType.value, // User's assignment type selection + issueId: selectedIssueId.value, // User's issue selection + publicationStatus: publicationStatus.value, // Calculated status from user's selection + }; + + // TODO: Remove this after testing + // console.log('Vue Form - FieldIssueSelection emitting:', { + // name: props.name, + // formId: props.formId, + // value: value, + // assignmentType: selectedAssignmentType.value, // User's selection + // selectedIssueId: selectedIssueId.value, // User's selection + // publicationStatus: publicationStatus.value, // Calculated status + // }); + + emit('change', props.name, 'value', value); + } }; const fetchIssues = async () => { @@ -255,7 +325,7 @@ const formatIssuesForDropdown = (issues) => { })); }; -watch(assignmentType, (newType) => { +watch(selectedAssignmentType, (newType) => { selectedIssueId.value = null; availableIssues.value = []; @@ -263,31 +333,47 @@ watch(assignmentType, (newType) => { fetchIssues(); } - emitValue(); + // Small delay to ensure the dropdown visibility is updated + setTimeout(() => { + emitValue(); + }, 100); }); watch(selectedIssueId, () => { emitValue(); }); -// Watch for props.value changes to sync local state -watch( - () => props.value, - (newValue) => { - if (newValue) { - assignmentType.value = newValue.assignmentType || 4; // 4 = CURRENT_BACK_ISSUES_PUBLISHED - selectedIssueId.value = newValue.issueId || null; - } - }, - {deep: true}, -); - onMounted(async () => { - await fetchAssignmentOptions(); - emitValue(); + // Initialize user selections with props or publication values + selectedAssignmentType.value = props.assignmentType || null; + selectedIssueId.value = props.publication?.issueId || null; + + await Promise.all([fetchAssignmentOptions(), fetchAssignmentType()]); + + // Debug: Log the state after fetching + // console.log('FieldIssueSelection mounted:', { + // selectedAssignmentType: selectedAssignmentType.value, + // selectedIssueId: selectedIssueId.value, + // showIssueDropdown: showIssueDropdown.value, + // assignmentOptions: assignmentOptions.value, + // issueCount: props.issueCount + // }); + + // If we have an assignment type that requires issue selection and an existing issue ID, + // fetch the issues so the dropdown is populated and the correct issue is pre-selected + if ( + selectedAssignmentType.value && + selectedIssueId.value && + showIssueDropdown.value + ) { + await fetchIssues(); + } - if (showIssueDropdown.value) { - fetchIssues(); + // TODO : remove this once the emit issue for hidden field is fixed + if (props.isPhpForm) { + $('input[name="prePublishStatus"]').hide(); } + + emitValue(); }); diff --git a/src/components/Form/fields/FieldSelectIssue.vue b/src/components/Form/fields/FieldSelectIssue.vue index e87f0d0e5..9f5b975ac 100644 --- a/src/components/Form/fields/FieldSelectIssue.vue +++ b/src/components/Form/fields/FieldSelectIssue.vue @@ -1,3 +1,4 @@ + + + diff --git a/src/pages/workflow/components/publication/WorkflowVersionSideModal.vue b/src/pages/workflow/components/publication/WorkflowVersionSideModal.vue index 0174339bd..a3d51758c 100644 --- a/src/pages/workflow/components/publication/WorkflowVersionSideModal.vue +++ b/src/pages/workflow/components/publication/WorkflowVersionSideModal.vue @@ -1,11 +1,11 @@