diff --git a/client/src/actions/lists.js b/client/src/actions/lists.js index eca13e7b..1c705d27 100644 --- a/client/src/actions/lists.js +++ b/client/src/actions/lists.js @@ -58,10 +58,34 @@ updateList.failure = (id, error) => ({ }, }); -const handleListUpdate = (list) => ({ +const handleListUpdate = ( + list, + isFetched, + users, + cards, + cardMemberships, + cardLabels, + taskLists, + tasks, + attachments, + customFieldGroups, + customFields, + customFieldValues, +) => ({ type: ActionTypes.LIST_UPDATE_HANDLE, payload: { list, + isFetched, + users, + cards, + cardMemberships, + cardLabels, + taskLists, + tasks, + attachments, + customFieldGroups, + customFields, + customFieldValues, }, }); diff --git a/client/src/components/lists/List/ActionsStep.jsx b/client/src/components/lists/List/ActionsStep.jsx index fc1a91f0..3e196dae 100755 --- a/client/src/components/lists/List/ActionsStep.jsx +++ b/client/src/components/lists/List/ActionsStep.jsx @@ -16,6 +16,7 @@ import { useSteps } from '../../../hooks'; import { ListTypes } from '../../../constants/Enums'; import EditColorStep from './EditColorStep'; import SortStep from './SortStep'; +import MoveStep from './MoveStep'; import SelectListTypeStep from '../SelectListTypeStep'; import ConfirmationStep from '../../common/ConfirmationStep'; import ArchiveCardsStep from '../../cards/ArchiveCardsStep'; @@ -26,6 +27,7 @@ const StepTypes = { EDIT_TYPE: 'EDIT_TYPE', EDIT_COLOR: 'EDIT_COLOR', SORT: 'SORT', + MOVE: 'MOVE', ARCHIVE_CARDS: 'ARCHIVE_CARDS', DELETE: 'DELETE', }; @@ -76,6 +78,10 @@ const ActionsStep = React.memo(({ listId, onNameEdit, onCardAdd, onClose }) => { openStep(StepTypes.SORT); }, [openStep]); + const handleMoveClick = useCallback(() => { + openStep(StepTypes.MOVE); + }, [openStep]); + const handleArchiveCardsClick = useCallback(() => { openStep(StepTypes.ARCHIVE_CARDS); }, [openStep]); @@ -102,6 +108,8 @@ const ActionsStep = React.memo(({ listId, onNameEdit, onCardAdd, onClose }) => { return ; case StepTypes.SORT: return ; + case StepTypes.MOVE: + return ; case StepTypes.ARCHIVE_CARDS: return ; case StepTypes.DELETE: @@ -157,6 +165,12 @@ const ActionsStep = React.memo(({ listId, onNameEdit, onCardAdd, onClose }) => { context: 'title', })} + + + {t('action.moveList', { + context: 'title', + })} + {list.type === ListTypes.CLOSED && ( diff --git a/client/src/components/lists/List/MoveStep.jsx b/client/src/components/lists/List/MoveStep.jsx new file mode 100644 index 00000000..efd25fe9 --- /dev/null +++ b/client/src/components/lists/List/MoveStep.jsx @@ -0,0 +1,133 @@ +/*! + * Copyright (c) 2024 PLANKA Software GmbH + * Licensed under the Fair Use License: https://github.com/plankanban/planka/blob/master/LICENSE.md + */ + +import React, { useMemo, useCallback } from 'react'; +import PropTypes from 'prop-types'; +import { useDispatch, useSelector } from 'react-redux'; +import { useTranslation } from 'react-i18next'; +import { Button, Dropdown, Form } from 'semantic-ui-react'; +import { Popup } from '../../../lib/custom-ui'; + +import selectors from '../../../selectors'; +import entryActions from '../../../entry-actions'; +import { useForm } from '../../../hooks'; + +import styles from './MoveStep.module.scss'; + +const MoveStep = React.memo(({ id, onBack, onClose }) => { + const selectListById = useMemo(() => selectors.makeSelectListById(), []); + const selectBoardById = useMemo(() => selectors.makeSelectBoardById(), []); + + const projectsToBoards = useSelector( + selectors.selectProjectsToBoardsWithEditorRightsForCurrentUser, + ); + + const list = useSelector((state) => selectListById(state, id)); + const projectId = useSelector((state) => selectBoardById(state, list.boardId).projectId); + + const dispatch = useDispatch(); + const [t] = useTranslation(); + + const defaultPath = useMemo( + () => ({ + projectId, + boardId: list.boardId, + }), + [list.boardId, projectId], + ); + + const [path, handleFieldChange] = useForm(() => ({ + projectId: null, + boardId: null, + ...defaultPath, + })); + + const selectedProject = useMemo( + () => projectsToBoards.find((project) => project.id === path.projectId) || null, + [projectsToBoards, path.projectId], + ); + + const selectedBoard = useMemo( + () => + (selectedProject && selectedProject.boards.find((board) => board.id === path.boardId)) || + null, + [selectedProject, path.boardId], + ); + + const handleSubmit = useCallback(() => { + if (selectedBoard.id !== defaultPath.boardId) { + dispatch(entryActions.transferList(id, selectedBoard.id)); + } + + onClose(); + }, [id, onClose, dispatch, defaultPath, selectedBoard]); + + return ( + <> + + {t('common.moveList', { + context: 'title', + })} + + +
+
{t('common.project')}
+ ({ + text: project.name, + value: project.id, + }))} + value={selectedProject && selectedProject.id} + placeholder={ + projectsToBoards.length === 0 ? t('common.noProjects') : t('common.selectProject') + } + disabled={projectsToBoards.length === 0} + className={styles.field} + onChange={handleFieldChange} + /> + {selectedProject && ( + <> +
{t('common.board')}
+ ({ + text: board.name, + value: board.id, + }))} + value={selectedBoard && selectedBoard.id} + placeholder={ + selectedProject.boards.length === 0 + ? t('common.noBoards') + : t('common.selectBoard') + } + disabled={selectedProject.boards.length === 0} + className={styles.field} + onChange={handleFieldChange} + /> + + )} +