diff --git a/packages/core/admin/ee/admin/pages/SettingsPage/pages/ReviewWorkflows/components/Stages/Stage/Stage.js b/packages/core/admin/ee/admin/pages/SettingsPage/pages/ReviewWorkflows/components/Stages/Stage/Stage.js index c0f8366272..9a96661b05 100644 --- a/packages/core/admin/ee/admin/pages/SettingsPage/pages/ReviewWorkflows/components/Stages/Stage/Stage.js +++ b/packages/core/admin/ee/admin/pages/SettingsPage/pages/ReviewWorkflows/components/Stages/Stage/Stage.js @@ -143,8 +143,8 @@ export function Stage({ const { trackUsage } = useTracking(); const dispatch = useDispatch(); const [isOpen, setIsOpen] = React.useState(isOpenDefault); - const [nameField, nameMeta] = useField(`stages.${index}.name`); - const [colorField, colorMeta] = useField(`stages.${index}.color`); + const [nameField, nameMeta, nameHelper] = useField(`stages.${index}.name`); + const [colorField, colorMeta, colorHelper] = useField(`stages.${index}.color`); const [{ handlerId, isDragging, handleKeyDown }, stageRef, dropRef, dragRef, dragPreviewRef] = useDragAndDrop(canReorder, { index, @@ -249,7 +249,7 @@ export function Stage({ })} error={nameMeta.error ?? false} onChange={(event) => { - nameField.onChange(event); + nameHelper.setValue(event.target.value); dispatch(updateStage(id, { name: event.target.value })); }} required @@ -278,7 +278,7 @@ export function Stage({ name={colorField.name} options={colorOptions} onChange={({ value }) => { - colorField.onChange({ target: { value } }); + colorHelper.setValue(value); dispatch(updateStage(id, { color: value })); }} // If no color was found in all the valid theme colors it means a user diff --git a/packages/core/admin/ee/admin/pages/SettingsPage/pages/ReviewWorkflows/components/WorkflowAttributes/WorkflowAttributes.js b/packages/core/admin/ee/admin/pages/SettingsPage/pages/ReviewWorkflows/components/WorkflowAttributes/WorkflowAttributes.js index a526845fc8..e3268d60f2 100644 --- a/packages/core/admin/ee/admin/pages/SettingsPage/pages/ReviewWorkflows/components/WorkflowAttributes/WorkflowAttributes.js +++ b/packages/core/admin/ee/admin/pages/SettingsPage/pages/ReviewWorkflows/components/WorkflowAttributes/WorkflowAttributes.js @@ -10,8 +10,8 @@ import { updateWorkflow } from '../../actions'; export function WorkflowAttributes({ contentTypes: { collectionTypes, singleTypes } }) { const { formatMessage } = useIntl(); const dispatch = useDispatch(); - const [nameField, nameMeta] = useField('name'); - const [contentTypesField, contentTypesMeta] = useField('contentTypes'); + const [nameField, nameMeta, nameHelper] = useField('name'); + const [contentTypesField, contentTypesMeta, contentTypesHelper] = useField('contentTypes'); return ( @@ -26,7 +26,7 @@ export function WorkflowAttributes({ contentTypes: { collectionTypes, singleType error={nameMeta.error ?? false} onChange={(event) => { dispatch(updateWorkflow({ name: event.target.value })); - nameField.onChange(event); + nameHelper.setValue(event.target.value); }} required /> @@ -53,7 +53,7 @@ export function WorkflowAttributes({ contentTypes: { collectionTypes, singleType })} onChange={(values) => { dispatch(updateWorkflow({ contentTypes: values })); - contentTypesField.onChange({ target: { value: values } }); + contentTypesHelper.setValue(values); }} options={[ { @@ -82,7 +82,6 @@ export function WorkflowAttributes({ contentTypes: { collectionTypes, singleType id: 'Settings.review-workflows.workflow.contentTypes.placeholder', defaultMessage: 'Select', })} - required /> diff --git a/packages/core/admin/ee/admin/pages/SettingsPage/pages/ReviewWorkflows/hooks/tests/useReviewWorkflows.test.js b/packages/core/admin/ee/admin/pages/SettingsPage/pages/ReviewWorkflows/hooks/tests/useReviewWorkflows.test.js index d770c51647..ad1f30f0c5 100644 --- a/packages/core/admin/ee/admin/pages/SettingsPage/pages/ReviewWorkflows/hooks/tests/useReviewWorkflows.test.js +++ b/packages/core/admin/ee/admin/pages/SettingsPage/pages/ReviewWorkflows/hooks/tests/useReviewWorkflows.test.js @@ -65,7 +65,7 @@ describe('useReviewWorkflows', () => { expect(result.current.workflows.isLoading).toBe(true); expect(get).toBeCalledWith('/admin/review-workflows/workflows/', { - params: { populate: 'stages' }, + params: { sort: 'name:asc', populate: 'stages' }, }); await waitFor(() => expect(result.current.workflows.isLoading).toBe(false)); diff --git a/packages/core/admin/ee/admin/pages/SettingsPage/pages/ReviewWorkflows/hooks/useReviewWorkflows.js b/packages/core/admin/ee/admin/pages/SettingsPage/pages/ReviewWorkflows/hooks/useReviewWorkflows.js index 6a2a15ec38..9338507e5f 100644 --- a/packages/core/admin/ee/admin/pages/SettingsPage/pages/ReviewWorkflows/hooks/useReviewWorkflows.js +++ b/packages/core/admin/ee/admin/pages/SettingsPage/pages/ReviewWorkflows/hooks/useReviewWorkflows.js @@ -9,7 +9,7 @@ export function useReviewWorkflows(workflowId) { const client = useQueryClient(); const workflowQueryKey = [QUERY_BASE_KEY, workflowId ?? 'default']; - async function fetchWorkflows({ params = { populate: 'stages' } }) { + async function fetchWorkflows({ params = { sort: 'name:asc', populate: 'stages' } }) { try { const { data: { data }, diff --git a/packages/core/admin/ee/admin/pages/SettingsPage/pages/ReviewWorkflows/pages/CreateView/CreateView.js b/packages/core/admin/ee/admin/pages/SettingsPage/pages/ReviewWorkflows/pages/CreateView/CreateView.js index 1b2dc60a7e..fa5212cffb 100644 --- a/packages/core/admin/ee/admin/pages/SettingsPage/pages/ReviewWorkflows/pages/CreateView/CreateView.js +++ b/packages/core/admin/ee/admin/pages/SettingsPage/pages/ReviewWorkflows/pages/CreateView/CreateView.js @@ -3,6 +3,7 @@ import { useFormik, Form, FormikProvider } from 'formik'; import { useIntl } from 'react-intl'; import { useDispatch, useSelector } from 'react-redux'; import { useMutation } from 'react-query'; +import { useHistory } from 'react-router-dom'; import { CheckPagePermissions, @@ -28,6 +29,7 @@ import * as Layout from '../../components/Layout'; export function ReviewWorkflowsCreateView() { const { formatMessage } = useIntl(); const { post } = useFetchClient(); + const { push } = useHistory(); const { formatAPIError } = useAPIErrorHandler(); const dispatch = useDispatch(); const toggleNotification = useNotification(); @@ -42,7 +44,7 @@ export function ReviewWorkflowsCreateView() { async ({ workflow }) => { const { data: { data }, - } = await post(`/admin/review-workflows/workflow`, { + } = await post(`/admin/review-workflows/workflows`, { data: workflow, }); @@ -52,7 +54,10 @@ export function ReviewWorkflowsCreateView() { onSuccess() { toggleNotification({ type: 'success', - message: { id: 'notification.success.saved', defaultMessage: 'Saved' }, + message: { + id: 'Settings.review-workflows.create.page.notification.success', + defaultMessage: 'Workflow successfully created', + }, }); }, } @@ -60,9 +65,11 @@ export function ReviewWorkflowsCreateView() { const submitForm = async () => { try { - const res = await mutateAsync({ workflow: currentWorkflow }); + const workflow = await mutateAsync({ workflow: currentWorkflow }); - return res; + push(`/settings/review-workflows/${workflow.id}`); + + return workflow; } catch (error) { toggleNotification({ type: 'warning', @@ -71,8 +78,6 @@ export function ReviewWorkflowsCreateView() { return null; } - - // TODO: redirect to edit view }; const formik = useFormik({ @@ -82,14 +87,13 @@ export function ReviewWorkflowsCreateView() { submitForm(); }, validationSchema: getWorkflowValidationSchema({ formatMessage }), - validateOnChange: false, }); useInjectReducer(REDUX_NAMESPACE, reducer); React.useEffect(() => { dispatch(resetWorkflow()); - }, [dispatch, collectionTypes, singleTypes]); + }, [dispatch]); return ( diff --git a/packages/core/admin/ee/admin/pages/SettingsPage/pages/ReviewWorkflows/pages/EditView/EditView.js b/packages/core/admin/ee/admin/pages/SettingsPage/pages/ReviewWorkflows/pages/EditView/EditView.js index 6733b1f03b..b473a9498f 100644 --- a/packages/core/admin/ee/admin/pages/SettingsPage/pages/ReviewWorkflows/pages/EditView/EditView.js +++ b/packages/core/admin/ee/admin/pages/SettingsPage/pages/ReviewWorkflows/pages/EditView/EditView.js @@ -112,7 +112,6 @@ export function ReviewWorkflowsEditView() { } }, validationSchema: getWorkflowValidationSchema({ formatMessage }), - validateOnChange: true, }); useInjectReducer(REDUX_NAMESPACE, reducer); diff --git a/packages/core/admin/ee/admin/pages/SettingsPage/pages/ReviewWorkflows/pages/ListView/ListView.js b/packages/core/admin/ee/admin/pages/SettingsPage/pages/ReviewWorkflows/pages/ListView/ListView.js index 90e6db51cb..8ccd9e227d 100644 --- a/packages/core/admin/ee/admin/pages/SettingsPage/pages/ReviewWorkflows/pages/ListView/ListView.js +++ b/packages/core/admin/ee/admin/pages/SettingsPage/pages/ReviewWorkflows/pages/ListView/ListView.js @@ -8,6 +8,7 @@ import { ConfirmDialog, Link, LinkButton, + onRowClick, pxToRem, useAPIErrorHandler, useFetchClient, @@ -34,7 +35,17 @@ import adminPermissions from '../../../../../../../../admin/src/permissions'; import * as Layout from '../../components/Layout'; const ActionLink = styled(Link)` + align-items: center; + height: ${pxToRem(32)}; + display: flex; + justify-content: center; + padding: ${({ theme }) => `${theme.spaces[2]}}`}; + width: ${pxToRem(32)}; + svg { + height: ${pxToRem(12)}; + width: ${pxToRem(12)}; + path { fill: ${({ theme }) => theme.colors.neutral500}; } @@ -154,6 +165,14 @@ export function ReviewWorkflowsListView() { })} + + + {formatMessage({ + id: 'Settings.review-workflows.list.page.list.column.contentTypes.title', + defaultMessage: 'Content Types', + })} + + {formatMessage({ @@ -168,7 +187,16 @@ export function ReviewWorkflowsListView() { {workflowsData.data.map((workflow) => ( push(`/settings/review-workflows/${workflow.id}`)} + {...onRowClick({ + fn(event) { + // Abort row onClick event when the user click on the delete button + if (event.target.nodeName === 'BUTTON') { + return; + } + + push(`/settings/review-workflows/${workflow.id}`); + }, + })} key={`workflow-${workflow.id}`} > @@ -180,24 +208,12 @@ export function ReviewWorkflowsListView() { {workflow.stages.length} - - {workflowsData.data.length > 1 && ( - } - noBorder - onClick={() => { - handleDeleteWorkflow(workflow.id); - }} - /> - )} - + + {(workflow?.contentTypes ?? []).join(', ')} + + + + + + } + noBorder + onClick={() => { + handleDeleteWorkflow(workflow.id); + }} + /> diff --git a/packages/core/admin/ee/admin/pages/SettingsPage/pages/ReviewWorkflows/reducer/index.js b/packages/core/admin/ee/admin/pages/SettingsPage/pages/ReviewWorkflows/reducer/index.js index 5b7de0e94e..2b7f891bda 100644 --- a/packages/core/admin/ee/admin/pages/SettingsPage/pages/ReviewWorkflows/reducer/index.js +++ b/packages/core/admin/ee/admin/pages/SettingsPage/pages/ReviewWorkflows/reducer/index.js @@ -20,10 +20,13 @@ export const initialState = { clientState: { currentWorkflow: { data: { + name: '', + contentTypes: [], stages: [ { color: STAGE_COLOR_DEFAULT, name: '', + __temp_key__: 1, }, ], }, @@ -159,8 +162,8 @@ export function reducer(state = initialState, action) { draft.serverState.workflow ); } else { - // if there is no workflow on the server, the workflow can never be dirty - draft.clientState.currentWorkflow.isDirty = false; + // if there is no workflow on the server, the workflow is awalys considered dirty + draft.clientState.currentWorkflow.isDirty = true; } }); } diff --git a/packages/core/admin/ee/admin/pages/SettingsPage/pages/ReviewWorkflows/reducer/tests/index.test.js b/packages/core/admin/ee/admin/pages/SettingsPage/pages/ReviewWorkflows/reducer/tests/index.test.js index d47bbac275..ebbf477499 100644 --- a/packages/core/admin/ee/admin/pages/SettingsPage/pages/ReviewWorkflows/reducer/tests/index.test.js +++ b/packages/core/admin/ee/admin/pages/SettingsPage/pages/ReviewWorkflows/reducer/tests/index.test.js @@ -553,15 +553,11 @@ describe('Admin | Settings | Review Workflows | reducer', () => { expect.objectContaining({ clientState: expect.objectContaining({ currentWorkflow: expect.objectContaining({ - data: { - stages: [ - { - color: '#4945ff', - name: '', - }, - ], - }, - isDirty: false, + data: expect.objectContaining({ + name: '', + stages: [expect.objectContaining({ name: '', __temp_key__: 1 })], + }), + isDirty: true, }), }), }) diff --git a/packages/core/admin/ee/admin/pages/SettingsPage/pages/ReviewWorkflows/utils/getWorkflowValidationSchema.js b/packages/core/admin/ee/admin/pages/SettingsPage/pages/ReviewWorkflows/utils/getWorkflowValidationSchema.js index 31f80daf33..e7b2368b61 100644 --- a/packages/core/admin/ee/admin/pages/SettingsPage/pages/ReviewWorkflows/utils/getWorkflowValidationSchema.js +++ b/packages/core/admin/ee/admin/pages/SettingsPage/pages/ReviewWorkflows/utils/getWorkflowValidationSchema.js @@ -2,36 +2,48 @@ import * as yup from 'yup'; export function getWorkflowValidationSchema({ formatMessage }) { return yup.object({ - contentTypes: yup.array().of(yup.string()).required(), - name: yup.string().required(), + contentTypes: yup.array().of(yup.string()), + name: yup + .string() + .max( + 255, + formatMessage({ + id: 'Settings.review-workflows.validation.name.max-length', + defaultMessage: 'Name can not be longer than 255 characters', + }) + ) + .required(), - stages: yup.array().of( - yup.object().shape({ - name: yup - .string() - .required( - formatMessage({ - id: 'Settings.review-workflows.validation.stage.name', - defaultMessage: 'Name is required', - }) - ) - .max( - 255, - formatMessage({ - id: 'Settings.review-workflows.validation.stage.max-length', - defaultMessage: 'Name can not be longer than 255 characters', - }) - ), - color: yup - .string() - .required( - formatMessage({ - id: 'Settings.review-workflows.validation.stage.color', - defaultMessage: 'Color is required', - }) - ) - .matches(/^#(?:[0-9a-fA-F]{3}){1,2}$/i), - }) - ), + stages: yup + .array() + .of( + yup.object().shape({ + name: yup + .string() + .required( + formatMessage({ + id: 'Settings.review-workflows.validation.stage.name', + defaultMessage: 'Name is required', + }) + ) + .max( + 255, + formatMessage({ + id: 'Settings.review-workflows.validation.stage.max-length', + defaultMessage: 'Name can not be longer than 255 characters', + }) + ), + color: yup + .string() + .required( + formatMessage({ + id: 'Settings.review-workflows.validation.stage.color', + defaultMessage: 'Color is required', + }) + ) + .matches(/^#(?:[0-9a-fA-F]{3}){1,2}$/i), + }) + ) + .min(1), }); }