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',
+ })}
+
+
+
+
+ >
+ );
+});
+
+MoveStep.propTypes = {
+ id: PropTypes.string.isRequired,
+ onBack: PropTypes.func,
+ onClose: PropTypes.func.isRequired,
+};
+
+MoveStep.defaultProps = {
+ onBack: undefined,
+};
+
+export default MoveStep;
diff --git a/client/src/components/lists/List/MoveStep.module.scss b/client/src/components/lists/List/MoveStep.module.scss
new file mode 100644
index 00000000..352a33b8
--- /dev/null
+++ b/client/src/components/lists/List/MoveStep.module.scss
@@ -0,0 +1,17 @@
+/*!
+ * Copyright (c) 2024 PLANKA Software GmbH
+ * Licensed under the Fair Use License: https://github.com/plankanban/planka/blob/master/LICENSE.md
+ */
+
+:global(#app) {
+ .field {
+ margin-bottom: 8px;
+ }
+
+ .text {
+ color: #444444;
+ font-size: 12px;
+ font-weight: bold;
+ padding-bottom: 6px;
+ }
+}
diff --git a/client/src/constants/EntryActionTypes.js b/client/src/constants/EntryActionTypes.js
index 6b6ea340..7097bdfe 100755
--- a/client/src/constants/EntryActionTypes.js
+++ b/client/src/constants/EntryActionTypes.js
@@ -164,6 +164,7 @@ export default {
LIST_UPDATE: `${PREFIX}/LIST_UPDATE`,
LIST_UPDATE_HANDLE: `${PREFIX}/LIST_UPDATE_HANDLE`,
LIST_MOVE: `${PREFIX}/LIST_MOVE`,
+ LIST_TRANSFER: `${PREFIX}/LIST_TRANSFER`,
LIST_SORT: `${PREFIX}/LIST_SORT`,
LIST_CARDS_TO_ARCHIVE_LIST_MOVE: `${PREFIX}/LIST_CARDS_TO_ARCHIVE_LIST_MOVE`,
TRASH_LIST_IN_CURRENT_BOARD_CLEAR: `${PREFIX}/TRASH_LIST_IN_CURRENT_BOARD_CLEAR`,
diff --git a/client/src/entry-actions/lists.js b/client/src/entry-actions/lists.js
index 2f245d3b..13d3e7cc 100755
--- a/client/src/entry-actions/lists.js
+++ b/client/src/entry-actions/lists.js
@@ -42,6 +42,15 @@ const moveList = (id, index) => ({
},
});
+const transferList = (id, boardId, index = 0) => ({
+ type: EntryActionTypes.LIST_TRANSFER,
+ payload: {
+ id,
+ boardId,
+ index,
+ },
+});
+
const sortList = (id, data) => {
return {
type: EntryActionTypes.LIST_SORT,
@@ -92,6 +101,7 @@ export default {
updateList,
handleListUpdate,
moveList,
+ transferList,
sortList,
moveListCardsToArchiveList,
clearTrashListInCurrentBoard,
diff --git a/client/src/locales/ar-YE/core.js b/client/src/locales/ar-YE/core.js
index 7bfe7d88..c56086f6 100644
--- a/client/src/locales/ar-YE/core.js
+++ b/client/src/locales/ar-YE/core.js
@@ -204,6 +204,7 @@ export default {
moreActions: null,
moreActions_title: null,
moveCard_title: 'نقل البطاقة',
+ moveList_title: null,
myOwn_title: null,
name: 'الاسم',
newEmail: 'بريد إلكتروني جديد',
@@ -408,6 +409,7 @@ export default {
makeProjectShared_title: null,
move: 'نقل',
moveCard_title: 'نقل البطاقة',
+ moveList_title: null,
remove: 'حذف',
removeAssignee: null,
removeColor: null,
diff --git a/client/src/locales/bg-BG/core.js b/client/src/locales/bg-BG/core.js
index 5d7d189b..ef11c811 100644
--- a/client/src/locales/bg-BG/core.js
+++ b/client/src/locales/bg-BG/core.js
@@ -208,6 +208,7 @@ export default {
moreActions: null,
moreActions_title: null,
moveCard_title: 'Преместване на карта',
+ moveList_title: null,
myOwn_title: null,
name: 'Име',
newEmail: 'Нов имейл',
@@ -413,6 +414,7 @@ export default {
makeProjectShared_title: null,
move: 'Преместване',
moveCard_title: 'Преместване на карта',
+ moveList_title: null,
remove: 'Премахване',
removeAssignee: null,
removeColor: null,
diff --git a/client/src/locales/cs-CZ/core.js b/client/src/locales/cs-CZ/core.js
index a0548b7d..71baafb7 100644
--- a/client/src/locales/cs-CZ/core.js
+++ b/client/src/locales/cs-CZ/core.js
@@ -217,6 +217,7 @@ export default {
moreActions: null,
moreActions_title: null,
moveCard_title: 'Přesunout kartu',
+ moveList_title: null,
myOwn_title: 'Moje vlastní',
name: 'Jméno',
newEmail: 'Nový e-mail',
@@ -423,6 +424,7 @@ export default {
makeProjectShared_title: 'Vytvořit sdílený projekt',
move: 'Přesunout',
moveCard_title: 'Přesunout kartu',
+ moveList_title: null,
remove: 'Odstranit',
removeAssignee: 'Odstranit přiřazení',
removeColor: 'Smazat barvu',
diff --git a/client/src/locales/da-DK/core.js b/client/src/locales/da-DK/core.js
index 0a781a0f..72fdbb95 100644
--- a/client/src/locales/da-DK/core.js
+++ b/client/src/locales/da-DK/core.js
@@ -223,6 +223,7 @@ export default {
moreActions: null,
moreActions_title: null,
moveCard_title: 'Flyt kort',
+ moveList_title: null,
myOwn_title: 'Egne',
name: 'Navn',
newEmail: 'Ny e-mail',
@@ -433,6 +434,7 @@ export default {
makeProjectShared_title: 'Del projekt',
move: 'Flyt',
moveCard_title: 'Flyt kort',
+ moveList_title: null,
remove: 'Fjern',
removeAssignee: 'Fjern ansvarlig',
removeColor: 'Fjern farve',
diff --git a/client/src/locales/de-DE/core.js b/client/src/locales/de-DE/core.js
index 4d4d64fc..35d4628a 100644
--- a/client/src/locales/de-DE/core.js
+++ b/client/src/locales/de-DE/core.js
@@ -232,6 +232,7 @@ export default {
moreActions: null,
moreActions_title: null,
moveCard_title: 'Karte verschieben',
+ moveList_title: null,
myOwn_title: 'Meine eigenen',
name: 'Name',
newEmail: 'Neue E-Mail-Adresse',
@@ -438,6 +439,7 @@ export default {
makeProjectShared_title: 'Projekt freigeben',
move: 'Verschieben',
moveCard_title: 'Karte bewegen',
+ moveList_title: null,
remove: 'Löschen',
removeAssignee: 'Zuständigen entfernen',
removeColor: 'Farbe löschen',
diff --git a/client/src/locales/el-GR/core.js b/client/src/locales/el-GR/core.js
index 50cfb8d5..ea74b1a3 100644
--- a/client/src/locales/el-GR/core.js
+++ b/client/src/locales/el-GR/core.js
@@ -233,6 +233,7 @@ export default {
moreActions: null,
moreActions_title: null,
moveCard_title: 'Μετακίνηση κάρτας',
+ moveList_title: null,
myOwn_title: 'Δικά μου',
name: 'Όνομα',
newEmail: 'Νέο e-mail',
@@ -449,6 +450,7 @@ export default {
makeProjectShared_title: 'Κάντε το έργο κοινόχρηστο',
move: 'Μετακίνηση',
moveCard_title: 'Μετακίνηση κάρτας',
+ moveList_title: null,
remove: 'Αφαίρεση',
removeAssignee: 'Αφαίρεση υπευθύνου',
removeColor: 'Αφαίρεση χρώματος',
diff --git a/client/src/locales/en-GB/core.js b/client/src/locales/en-GB/core.js
index c9595f1d..6a626afe 100644
--- a/client/src/locales/en-GB/core.js
+++ b/client/src/locales/en-GB/core.js
@@ -222,6 +222,7 @@ export default {
moreActions: 'More actions',
moreActions_title: 'More Actions',
moveCard_title: 'Move Card',
+ moveList_title: 'Move List',
myOwn_title: 'My Own',
name: 'Name',
newEmail: 'New e-mail',
@@ -432,6 +433,7 @@ export default {
makeProjectShared_title: 'Make Project Shared',
move: 'Move',
moveCard_title: 'Move Card',
+ moveList_title: 'Move List',
remove: 'Remove',
removeAssignee: 'Remove assignee',
removeColor: 'Remove color',
diff --git a/client/src/locales/en-US/core.js b/client/src/locales/en-US/core.js
index 1da15e35..c22b14d1 100644
--- a/client/src/locales/en-US/core.js
+++ b/client/src/locales/en-US/core.js
@@ -217,6 +217,7 @@ export default {
moreActions: 'More actions',
moreActions_title: 'More Actions',
moveCard_title: 'Move Card',
+ moveList_title: 'Move List',
myOwn_title: 'My Own',
name: 'Name',
newEmail: 'New e-mail',
@@ -427,6 +428,7 @@ export default {
makeProjectShared_title: 'Make Project Shared',
move: 'Move',
moveCard_title: 'Move Card',
+ moveList_title: 'Move List',
remove: 'Remove',
removeAssignee: 'Remove assignee',
removeColor: 'Remove color',
diff --git a/client/src/locales/es-ES/core.js b/client/src/locales/es-ES/core.js
index 9d6f45b9..357ab375 100644
--- a/client/src/locales/es-ES/core.js
+++ b/client/src/locales/es-ES/core.js
@@ -223,6 +223,7 @@ export default {
moreActions: null,
moreActions_title: null,
moveCard_title: 'Mover tarjeta',
+ moveList_title: null,
myOwn_title: 'Propios',
name: 'Nombre',
newEmail: 'Nuevo correo',
@@ -432,6 +433,7 @@ export default {
makeProjectShared_title: 'Hacer proyecto compartido',
move: 'Mover',
moveCard_title: 'Mover Tarjeta',
+ moveList_title: null,
remove: 'Remover',
removeAssignee: 'Eliminar asignado',
removeColor: 'Eliminar color',
diff --git a/client/src/locales/et-EE/core.js b/client/src/locales/et-EE/core.js
index 424a63da..7d9f67d5 100644
--- a/client/src/locales/et-EE/core.js
+++ b/client/src/locales/et-EE/core.js
@@ -222,6 +222,7 @@ export default {
moreActions: null,
moreActions_title: null,
moveCard_title: 'Liiguta kaart',
+ moveList_title: null,
myOwn_title: 'Minu omanik',
name: 'Nimi',
newEmail: 'Uus e-post',
@@ -431,6 +432,7 @@ export default {
makeProjectShared_title: 'Muuda projekt jagatavaks',
move: 'Liiguta',
moveCard_title: 'Liiguta kaart',
+ moveList_title: null,
remove: 'Eemalda',
removeAssignee: 'Eemalda vastutaja',
removeColor: 'Eemalda värv',
diff --git a/client/src/locales/fa-IR/core.js b/client/src/locales/fa-IR/core.js
index 38a90c60..9ea3333a 100644
--- a/client/src/locales/fa-IR/core.js
+++ b/client/src/locales/fa-IR/core.js
@@ -205,6 +205,7 @@ export default {
moreActions: null,
moreActions_title: null,
moveCard_title: 'انتقال کارت',
+ moveList_title: null,
myOwn_title: null,
name: 'نام',
newEmail: 'ایمیل جدید',
@@ -410,6 +411,7 @@ export default {
makeProjectShared_title: null,
move: 'انتقال',
moveCard_title: 'انتقال کارت',
+ moveList_title: null,
remove: 'حذف',
removeAssignee: null,
removeColor: null,
diff --git a/client/src/locales/fi-FI/core.js b/client/src/locales/fi-FI/core.js
index ca7c8a1c..95246dd9 100644
--- a/client/src/locales/fi-FI/core.js
+++ b/client/src/locales/fi-FI/core.js
@@ -218,6 +218,7 @@ export default {
moreActions: null,
moreActions_title: null,
moveCard_title: 'Siirrä kortti',
+ moveList_title: null,
myOwn_title: 'Omat',
name: 'Nimi',
newEmail: 'Uusi sähköposti',
@@ -431,6 +432,7 @@ export default {
makeProjectShared_title: 'Jaa projekti',
move: 'Siirrä',
moveCard_title: 'Siirrä kortti',
+ moveList_title: null,
remove: 'Poista',
removeAssignee: 'Poista vastuuhenkilö',
removeColor: 'Poista väri',
diff --git a/client/src/locales/fr-FR/core.js b/client/src/locales/fr-FR/core.js
index 384bfb4c..a5941479 100644
--- a/client/src/locales/fr-FR/core.js
+++ b/client/src/locales/fr-FR/core.js
@@ -226,6 +226,7 @@ export default {
moreActions: null,
moreActions_title: null,
moveCard_title: 'Déplacer la carte',
+ moveList_title: null,
myOwn_title: 'Mes projets privés',
name: 'Nom',
newEmail: 'Nouvel e-mail',
@@ -435,6 +436,7 @@ export default {
makeProjectShared_title: "Transformer le projet en projet d'équipe",
move: 'Déplacer',
moveCard_title: 'Déplacer la carte',
+ moveList_title: null,
remove: 'Supprimer',
removeAssignee: 'Retirer le responsable',
removeColor: 'Supprimer la couleur',
diff --git a/client/src/locales/hu-HU/core.js b/client/src/locales/hu-HU/core.js
index 0ed76079..8d7c15b8 100644
--- a/client/src/locales/hu-HU/core.js
+++ b/client/src/locales/hu-HU/core.js
@@ -215,6 +215,7 @@ export default {
moreActions: 'További műveletek',
moreActions_title: 'További műveletek',
moveCard_title: 'Kártya áthelyezése',
+ moveList_title: null,
myOwn_title: 'Saját',
name: 'Név',
newEmail: 'Új e-mail',
@@ -432,6 +433,7 @@ export default {
makeProjectShared_title: 'Projekt megosztása',
move: 'Áthelyezés',
moveCard_title: 'Kártya áthelyezése',
+ moveList_title: null,
remove: 'Eltávolítás',
removeAssignee: 'Felelős eltávolítása',
removeColor: 'Szín eltávolítása',
diff --git a/client/src/locales/id-ID/core.js b/client/src/locales/id-ID/core.js
index a8c0af32..58afb85e 100644
--- a/client/src/locales/id-ID/core.js
+++ b/client/src/locales/id-ID/core.js
@@ -207,6 +207,7 @@ export default {
moreActions: null,
moreActions_title: null,
moveCard_title: 'Pindahkan Kartu',
+ moveList_title: null,
myOwn_title: null,
name: 'Nama',
newEmail: 'E-mail baru',
@@ -412,6 +413,7 @@ export default {
makeProjectShared_title: null,
move: 'Pindah',
moveCard_title: 'Pindahkan Kartu',
+ moveList_title: null,
remove: 'Hapus',
removeAssignee: null,
removeColor: null,
diff --git a/client/src/locales/it-IT/core.js b/client/src/locales/it-IT/core.js
index db924ce0..74488736 100644
--- a/client/src/locales/it-IT/core.js
+++ b/client/src/locales/it-IT/core.js
@@ -224,6 +224,7 @@ export default {
moreActions: 'Altre azioni',
moreActions_title: 'Altre azioni',
moveCard_title: 'Sposta scheda',
+ moveList_title: null,
myOwn_title: 'Personali',
name: 'Nome',
newEmail: 'Nuova e-mail',
@@ -436,6 +437,7 @@ export default {
makeProjectShared_title: 'Rendi progetto condiviso',
move: 'Muovi',
moveCard_title: 'Muovi scheda',
+ moveList_title: null,
remove: 'Rimuovi',
removeAssignee: 'Rimuovi assegnatario',
removeColor: 'Remove colore',
diff --git a/client/src/locales/ja-JP/core.js b/client/src/locales/ja-JP/core.js
index c0f358ae..2a25075f 100644
--- a/client/src/locales/ja-JP/core.js
+++ b/client/src/locales/ja-JP/core.js
@@ -207,6 +207,7 @@ export default {
moreActions: null,
moreActions_title: null,
moveCard_title: 'カードを移動',
+ moveList_title: null,
myOwn_title: null,
name: '名前',
newEmail: '新しいEメール',
@@ -412,6 +413,7 @@ export default {
makeProjectShared_title: null,
move: '移動',
moveCard_title: 'カードを移動',
+ moveList_title: null,
remove: '削除',
removeAssignee: null,
removeColor: null,
diff --git a/client/src/locales/ko-KR/core.js b/client/src/locales/ko-KR/core.js
index fc8939a0..081108db 100644
--- a/client/src/locales/ko-KR/core.js
+++ b/client/src/locales/ko-KR/core.js
@@ -205,6 +205,7 @@ export default {
moreActions: null,
moreActions_title: null,
moveCard_title: '카드 이동',
+ moveList_title: null,
myOwn_title: null,
name: '이름',
newEmail: '새 이메일',
@@ -411,6 +412,7 @@ export default {
makeProjectShared_title: null,
move: '이동',
moveCard_title: '카드 이동',
+ moveList_title: null,
remove: '제거',
removeAssignee: null,
removeColor: '색상 제거',
diff --git a/client/src/locales/nl-NL/core.js b/client/src/locales/nl-NL/core.js
index 693b37f1..5d149452 100644
--- a/client/src/locales/nl-NL/core.js
+++ b/client/src/locales/nl-NL/core.js
@@ -207,6 +207,7 @@ export default {
moreActions: null,
moreActions_title: null,
moveCard_title: 'Kaart verplaatsen',
+ moveList_title: null,
myOwn_title: null,
name: 'Naam',
newEmail: 'Nieuwe e-mail',
@@ -413,6 +414,7 @@ export default {
makeProjectShared_title: null,
move: 'Verplaatsen',
moveCard_title: 'Kaart verplaatsen',
+ moveList_title: null,
remove: 'Verwijderen',
removeAssignee: null,
removeColor: null,
diff --git a/client/src/locales/pl-PL/core.js b/client/src/locales/pl-PL/core.js
index f74816a9..fa79a0f0 100644
--- a/client/src/locales/pl-PL/core.js
+++ b/client/src/locales/pl-PL/core.js
@@ -214,6 +214,7 @@ export default {
moreActions: null,
moreActions_title: null,
moveCard_title: 'Przenoszenie Karty',
+ moveList_title: null,
myOwn_title: 'Moje',
name: 'Nazwa',
newEmail: 'Nowy e-mail',
@@ -423,6 +424,7 @@ export default {
makeProjectShared_title: 'Udostępnij Projekt',
move: 'Przenieś',
moveCard_title: 'Przenieś Kartę',
+ moveList_title: null,
remove: 'Usuń',
removeAssignee: 'Usuń osobę przypisaną',
removeColor: 'Usuń kolor',
diff --git a/client/src/locales/pt-BR/core.js b/client/src/locales/pt-BR/core.js
index dd807b2a..99ca769b 100644
--- a/client/src/locales/pt-BR/core.js
+++ b/client/src/locales/pt-BR/core.js
@@ -224,6 +224,7 @@ export default {
moreActions: 'Mais ações',
moreActions_title: 'Mais Ações',
moveCard_title: 'Mover Cartão',
+ moveList_title: null,
myOwn_title: 'Meus',
name: 'Nome',
newEmail: 'Novo e-mail',
@@ -435,6 +436,7 @@ export default {
makeProjectShared_title: 'Tornar Projeto Compartilhado',
move: 'Mover',
moveCard_title: 'Mover Cartão',
+ moveList_title: null,
remove: 'Remover',
removeAssignee: 'Remover responsável',
removeColor: 'Remover cor',
diff --git a/client/src/locales/pt-PT/core.js b/client/src/locales/pt-PT/core.js
index 6a394b93..98e9d471 100644
--- a/client/src/locales/pt-PT/core.js
+++ b/client/src/locales/pt-PT/core.js
@@ -208,6 +208,7 @@ export default {
moreActions: null,
moreActions_title: null,
moveCard_title: 'Mover Cartão',
+ moveList_title: null,
myOwn_title: null,
name: 'Nome',
newEmail: 'Novo e-mail',
@@ -414,6 +415,7 @@ export default {
makeProjectShared_title: null,
move: 'Mover',
moveCard_title: 'Mover Cartão',
+ moveList_title: null,
remove: 'Remover',
removeAssignee: null,
removeColor: null,
diff --git a/client/src/locales/ro-RO/core.js b/client/src/locales/ro-RO/core.js
index 0bf70d58..3c0d1b4e 100644
--- a/client/src/locales/ro-RO/core.js
+++ b/client/src/locales/ro-RO/core.js
@@ -207,6 +207,7 @@ export default {
moreActions: null,
moreActions_title: null,
moveCard_title: 'Mutați cardul',
+ moveList_title: null,
myOwn_title: null,
name: 'Nume',
newEmail: 'Email nou',
@@ -413,6 +414,7 @@ export default {
makeProjectShared_title: null,
move: 'Mutați',
moveCard_title: 'Mutați cardul',
+ moveList_title: null,
remove: 'Eliminați',
removeAssignee: null,
removeColor: null,
diff --git a/client/src/locales/ru-RU/core.js b/client/src/locales/ru-RU/core.js
index 248bea54..e46de1df 100644
--- a/client/src/locales/ru-RU/core.js
+++ b/client/src/locales/ru-RU/core.js
@@ -221,6 +221,7 @@ export default {
moreActions: null,
moreActions_title: null,
moveCard_title: 'Перемещение карточки',
+ moveList_title: null,
myOwn_title: 'Мой собственный',
name: 'Имя',
newEmail: 'Новый e-mail',
@@ -427,6 +428,7 @@ export default {
makeProjectShared_title: 'Сделать проект общим',
move: 'Переместить',
moveCard_title: 'Переместить карточку',
+ moveList_title: null,
remove: 'Убрать',
removeAssignee: 'Удалить исполнителя',
removeColor: 'Удалить цвет',
diff --git a/client/src/locales/sk-SK/core.js b/client/src/locales/sk-SK/core.js
index b40ca899..e30dfd8e 100644
--- a/client/src/locales/sk-SK/core.js
+++ b/client/src/locales/sk-SK/core.js
@@ -207,6 +207,7 @@ export default {
moreActions: null,
moreActions_title: null,
moveCard_title: 'Presunúť kartu',
+ moveList_title: null,
myOwn_title: null,
name: 'Meno',
newEmail: 'Nový e-mail',
@@ -412,6 +413,7 @@ export default {
makeProjectShared_title: null,
move: 'Presunúť',
moveCard_title: 'Presunúť kartu',
+ moveList_title: null,
remove: 'Odstrániť',
removeAssignee: null,
removeColor: null,
diff --git a/client/src/locales/sr-Cyrl-RS/core.js b/client/src/locales/sr-Cyrl-RS/core.js
index e6cb5adb..b0e1bc39 100644
--- a/client/src/locales/sr-Cyrl-RS/core.js
+++ b/client/src/locales/sr-Cyrl-RS/core.js
@@ -207,6 +207,7 @@ export default {
moreActions: null,
moreActions_title: null,
moveCard_title: 'Премести картицу',
+ moveList_title: null,
myOwn_title: null,
name: 'Име',
newEmail: 'Нова е-пошта',
@@ -412,6 +413,7 @@ export default {
makeProjectShared_title: null,
move: 'Премести',
moveCard_title: 'Премести картицу',
+ moveList_title: null,
remove: 'Уклони',
removeAssignee: null,
removeColor: null,
diff --git a/client/src/locales/sr-Latn-RS/core.js b/client/src/locales/sr-Latn-RS/core.js
index a3be38a3..4be00134 100644
--- a/client/src/locales/sr-Latn-RS/core.js
+++ b/client/src/locales/sr-Latn-RS/core.js
@@ -204,6 +204,7 @@ export default {
moreActions: null,
moreActions_title: null,
moveCard_title: 'Premesti karticu',
+ moveList_title: null,
myOwn_title: null,
name: 'Ime',
newEmail: 'Nova e-pošta',
@@ -409,6 +410,7 @@ export default {
makeProjectShared_title: null,
move: 'Premesti',
moveCard_title: 'Premesti karticu',
+ moveList_title: null,
remove: 'Ukloni',
removeAssignee: null,
removeColor: null,
diff --git a/client/src/locales/sv-SE/core.js b/client/src/locales/sv-SE/core.js
index 7a1aff7a..5c3763a9 100644
--- a/client/src/locales/sv-SE/core.js
+++ b/client/src/locales/sv-SE/core.js
@@ -206,6 +206,7 @@ export default {
moreActions: null,
moreActions_title: null,
moveCard_title: 'Flytta kort',
+ moveList_title: null,
myOwn_title: null,
name: 'Namn',
newEmail: 'Ny e-mail',
@@ -411,6 +412,7 @@ export default {
makeProjectShared_title: null,
move: 'Flytta',
moveCard_title: 'Flytta kort',
+ moveList_title: null,
remove: 'Ta bort',
removeAssignee: null,
removeColor: null,
diff --git a/client/src/locales/tr-TR/core.js b/client/src/locales/tr-TR/core.js
index fc7eee19..81d29cac 100644
--- a/client/src/locales/tr-TR/core.js
+++ b/client/src/locales/tr-TR/core.js
@@ -204,6 +204,7 @@ export default {
moreActions: null,
moreActions_title: null,
moveCard_title: 'Kartı Taşı',
+ moveList_title: null,
myOwn_title: null,
name: 'isim',
newEmail: 'Yeni e-posta adresi',
@@ -409,6 +410,7 @@ export default {
makeProjectShared_title: null,
move: 'Taşı',
moveCard_title: 'Kartı Taşı',
+ moveList_title: null,
remove: 'Sil',
removeAssignee: null,
removeColor: null,
diff --git a/client/src/locales/uk-UA/core.js b/client/src/locales/uk-UA/core.js
index 1f788dcc..9e551f86 100644
--- a/client/src/locales/uk-UA/core.js
+++ b/client/src/locales/uk-UA/core.js
@@ -219,6 +219,7 @@ export default {
moreActions: 'Більше дій',
moreActions_title: 'Більше дій',
moveCard_title: 'Перемістити Картку',
+ moveList_title: null,
myOwn_title: 'Моя власна',
name: 'Назва',
newEmail: 'Нова електронна пошта',
@@ -429,6 +430,7 @@ export default {
makeProjectShared_title: 'Зробити Проект спільним',
move: 'Перемістити',
moveCard_title: 'Перемістити Картку',
+ moveList_title: null,
remove: 'Видалити',
removeAssignee: 'Видалити правонаступника',
removeColor: 'Видалити колір',
diff --git a/client/src/locales/uz-UZ/core.js b/client/src/locales/uz-UZ/core.js
index 707f5805..81555989 100644
--- a/client/src/locales/uz-UZ/core.js
+++ b/client/src/locales/uz-UZ/core.js
@@ -203,6 +203,7 @@ export default {
moreActions: null,
moreActions_title: null,
moveCard_title: "Kartani Ko'chirish",
+ moveList_title: null,
myOwn_title: null,
name: 'Ism',
newEmail: 'Yangi e-mail',
@@ -408,6 +409,7 @@ export default {
makeProjectShared_title: null,
move: "Ko'chirish",
moveCard_title: "Kartani Ko'chirish",
+ moveList_title: null,
remove: "O'chirish",
removeAssignee: null,
removeColor: null,
diff --git a/client/src/locales/zh-CN/core.js b/client/src/locales/zh-CN/core.js
index 47f34460..7511e17f 100644
--- a/client/src/locales/zh-CN/core.js
+++ b/client/src/locales/zh-CN/core.js
@@ -204,6 +204,7 @@ export default {
moreActions: null,
moreActions_title: null,
moveCard_title: '移动卡片',
+ moveList_title: null,
myOwn_title: '我的',
name: '姓名',
newEmail: '新邮箱',
@@ -410,6 +411,7 @@ export default {
makeProjectShared_title: '将项目设为共享',
move: '移动',
moveCard_title: '移动卡片',
+ moveList_title: null,
remove: '删除',
removeAssignee: '移除负责人',
removeColor: '移除颜色',
diff --git a/client/src/locales/zh-TW/core.js b/client/src/locales/zh-TW/core.js
index 559138a4..dd25a23c 100644
--- a/client/src/locales/zh-TW/core.js
+++ b/client/src/locales/zh-TW/core.js
@@ -201,6 +201,7 @@ export default {
moreActions: null,
moreActions_title: null,
moveCard_title: '移動卡片',
+ moveList_title: null,
myOwn_title: null,
name: '姓名',
newEmail: '新郵箱',
@@ -405,6 +406,7 @@ export default {
makeProjectShared_title: null,
move: '移動',
moveCard_title: '移動卡片',
+ moveList_title: null,
remove: '刪除',
removeAssignee: null,
removeColor: null,
diff --git a/client/src/models/Attachment.js b/client/src/models/Attachment.js
index c1c8ad4b..f41387de 100644
--- a/client/src/models/Attachment.js
+++ b/client/src/models/Attachment.js
@@ -58,6 +58,7 @@ export default class extends BaseModel {
case ActionTypes.PROJECT_UPDATE_HANDLE:
case ActionTypes.PROJECT_MANAGER_CREATE_HANDLE:
case ActionTypes.BOARD_MEMBERSHIP_CREATE_HANDLE:
+ case ActionTypes.LIST_UPDATE_HANDLE:
case ActionTypes.CARD_UPDATE_HANDLE:
if (payload.attachments) {
payload.attachments.forEach((attachment) => {
diff --git a/client/src/models/Card.js b/client/src/models/Card.js
index e13c0ed2..e7da438f 100755
--- a/client/src/models/Card.js
+++ b/client/src/models/Card.js
@@ -88,6 +88,7 @@ export default class extends BaseModel {
case ActionTypes.PROJECT_UPDATE_HANDLE:
case ActionTypes.PROJECT_MANAGER_CREATE_HANDLE:
case ActionTypes.BOARD_MEMBERSHIP_CREATE_HANDLE:
+ case ActionTypes.LIST_UPDATE_HANDLE:
if (payload.cards) {
payload.cards.forEach((card) => {
Card.upsert(card);
diff --git a/client/src/models/CustomField.js b/client/src/models/CustomField.js
index f9f61263..5820bc1e 100644
--- a/client/src/models/CustomField.js
+++ b/client/src/models/CustomField.js
@@ -36,6 +36,7 @@ export default class extends BaseModel {
case ActionTypes.PROJECT_UPDATE_HANDLE:
case ActionTypes.PROJECT_MANAGER_CREATE_HANDLE:
case ActionTypes.BOARD_MEMBERSHIP_CREATE_HANDLE:
+ case ActionTypes.LIST_UPDATE_HANDLE:
case ActionTypes.CARD_UPDATE_HANDLE:
if (payload.customFields) {
payload.customFields.forEach((customField) => {
diff --git a/client/src/models/CustomFieldGroup.js b/client/src/models/CustomFieldGroup.js
index b1c4e2d9..910cb864 100644
--- a/client/src/models/CustomFieldGroup.js
+++ b/client/src/models/CustomFieldGroup.js
@@ -40,6 +40,7 @@ export default class extends BaseModel {
case ActionTypes.PROJECT_UPDATE_HANDLE:
case ActionTypes.PROJECT_MANAGER_CREATE_HANDLE:
case ActionTypes.BOARD_MEMBERSHIP_CREATE_HANDLE:
+ case ActionTypes.LIST_UPDATE_HANDLE:
case ActionTypes.CARD_UPDATE_HANDLE:
if (payload.customFieldGroups) {
payload.customFieldGroups.forEach((customFieldGroup) => {
diff --git a/client/src/models/CustomFieldValue.js b/client/src/models/CustomFieldValue.js
index d6d2551b..f9580907 100644
--- a/client/src/models/CustomFieldValue.js
+++ b/client/src/models/CustomFieldValue.js
@@ -51,6 +51,7 @@ export default class extends BaseModel {
case ActionTypes.PROJECT_UPDATE_HANDLE:
case ActionTypes.PROJECT_MANAGER_CREATE_HANDLE:
case ActionTypes.BOARD_MEMBERSHIP_CREATE_HANDLE:
+ case ActionTypes.LIST_UPDATE_HANDLE:
case ActionTypes.CARD_UPDATE_HANDLE:
if (payload.customFieldValues) {
payload.customFieldValues.forEach((customFieldValue) => {
diff --git a/client/src/models/List.js b/client/src/models/List.js
index f27436be..63bd04b9 100755
--- a/client/src/models/List.js
+++ b/client/src/models/List.js
@@ -135,47 +135,21 @@ export default class extends BaseModel {
case ActionTypes.LIST_UPDATE: {
const listModel = List.withId(payload.id);
- let isClosed;
- if (payload.data.type) {
- const changedTypeState = getChangedTypeState(listModel, payload.data);
-
- if (changedTypeState === ListTypeStates.OPENED) {
- isClosed = false;
- } else if (changedTypeState === ListTypeStates.CLOSED) {
- isClosed = true;
- }
- }
-
- listModel.update(payload.data);
-
- if (isClosed !== undefined) {
- listModel.cards.toModelArray().forEach((cardModel) => {
- cardModel.update({
- isClosed,
- });
-
- cardModel.linkedTasks.update({
- isCompleted: isClosed,
- });
- });
- }
-
- break;
- }
- case ActionTypes.LIST_UPDATE_HANDLE: {
- const listModel = List.withId(payload.list.id);
-
- if (listModel) {
- const changedTypeState = getChangedTypeState(listModel, payload.list);
-
+ if (payload.data.boardId && payload.data.boardId !== listModel.boardId) {
+ listModel.deleteWithRelated();
+ } else {
let isClosed;
- if (changedTypeState === ListTypeStates.OPENED) {
- isClosed = false;
- } else if (changedTypeState === ListTypeStates.CLOSED) {
- isClosed = true;
+ if (payload.data.type) {
+ const changedTypeState = getChangedTypeState(listModel, payload.data);
+
+ if (changedTypeState === ListTypeStates.OPENED) {
+ isClosed = false;
+ } else if (changedTypeState === ListTypeStates.CLOSED) {
+ isClosed = true;
+ }
}
- listModel.update(prepareList(payload.list));
+ listModel.update(payload.data);
if (isClosed !== undefined) {
listModel.cards.toModelArray().forEach((cardModel) => {
@@ -188,7 +162,44 @@ export default class extends BaseModel {
});
});
}
- } else {
+ }
+
+ break;
+ }
+ // TODO: refactor
+ case ActionTypes.LIST_UPDATE_HANDLE: {
+ const listModel = List.withId(payload.list.id);
+
+ if (listModel) {
+ if (payload.list.boardId === null || payload.isFetched) {
+ listModel.deleteWithRelated();
+ }
+
+ if (payload.list.boardId !== null) {
+ const changedTypeState = getChangedTypeState(listModel, payload.list);
+
+ let isClosed;
+ if (changedTypeState === ListTypeStates.OPENED) {
+ isClosed = false;
+ } else if (changedTypeState === ListTypeStates.CLOSED) {
+ isClosed = true;
+ }
+
+ listModel.update(prepareList(payload.list));
+
+ if (isClosed !== undefined) {
+ listModel.cards.toModelArray().forEach((cardModel) => {
+ cardModel.update({
+ isClosed,
+ });
+
+ cardModel.linkedTasks.update({
+ isCompleted: isClosed,
+ });
+ });
+ }
+ }
+ } else if (payload.list.boardId !== null) {
List.upsert(prepareList(payload.list));
}
diff --git a/client/src/models/Task.js b/client/src/models/Task.js
index 10a27cd3..7057e4ab 100755
--- a/client/src/models/Task.js
+++ b/client/src/models/Task.js
@@ -43,6 +43,7 @@ export default class extends BaseModel {
case ActionTypes.PROJECT_UPDATE_HANDLE:
case ActionTypes.PROJECT_MANAGER_CREATE_HANDLE:
case ActionTypes.BOARD_MEMBERSHIP_CREATE_HANDLE:
+ case ActionTypes.LIST_UPDATE_HANDLE:
case ActionTypes.CARD_UPDATE_HANDLE:
if (payload.tasks) {
payload.tasks.forEach((task) => {
diff --git a/client/src/models/TaskList.js b/client/src/models/TaskList.js
index 97ce9fe4..9d2a71bf 100755
--- a/client/src/models/TaskList.js
+++ b/client/src/models/TaskList.js
@@ -32,6 +32,7 @@ export default class extends BaseModel {
case ActionTypes.PROJECT_UPDATE_HANDLE:
case ActionTypes.PROJECT_MANAGER_CREATE_HANDLE:
case ActionTypes.BOARD_MEMBERSHIP_CREATE_HANDLE:
+ case ActionTypes.LIST_UPDATE_HANDLE:
case ActionTypes.CARD_UPDATE_HANDLE:
if (payload.taskLists) {
payload.taskLists.forEach((taskList) => {
diff --git a/client/src/models/User.js b/client/src/models/User.js
index be547c8a..2acbb9d0 100755
--- a/client/src/models/User.js
+++ b/client/src/models/User.js
@@ -93,6 +93,7 @@ export default class extends BaseModel {
case ActionTypes.LOCATION_CHANGE_HANDLE:
case ActionTypes.PROJECT_UPDATE_HANDLE:
case ActionTypes.BOARD_MEMBERSHIP_CREATE_HANDLE:
+ case ActionTypes.LIST_UPDATE_HANDLE:
case ActionTypes.CARD_UPDATE_HANDLE:
if (payload.users) {
payload.users.forEach((user) => {
diff --git a/client/src/sagas/core/services/lists.js b/client/src/sagas/core/services/lists.js
index acd6c3e2..b38e6b6c 100644
--- a/client/src/sagas/core/services/lists.js
+++ b/client/src/sagas/core/services/lists.js
@@ -6,6 +6,7 @@
import { call, put, select } from 'redux-saga/effects';
import toast from 'react-hot-toast';
+import { goToBoard } from './router';
import request from '../request';
import selectors from '../../../selectors';
import actions from '../../../actions';
@@ -65,7 +66,71 @@ export function* updateList(id, data) {
}
export function* handleListUpdate(list) {
- yield put(actions.handleListUpdate(list));
+ const currentCard = yield select(selectors.selectCurrentCard);
+
+ let fetch = false;
+ if (list.boardId) {
+ const isAvailableForCurrentUser = yield select(
+ selectors.selectIsListWithIdAvailableForCurrentUser,
+ list.id,
+ );
+
+ fetch = !isAvailableForCurrentUser;
+ }
+
+ let users;
+ let cards;
+ let cardMemberships;
+ let cardLabels;
+ let taskLists;
+ let tasks;
+ let attachments;
+ let customFieldGroups;
+ let customFields;
+ let customFieldValues;
+
+ if (fetch) {
+ try {
+ ({
+ item: list, // eslint-disable-line no-param-reassign
+ included: {
+ users,
+ cards,
+ cardMemberships,
+ cardLabels,
+ taskLists,
+ tasks,
+ attachments,
+ customFieldGroups,
+ customFields,
+ customFieldValues,
+ },
+ } = yield call(request, api.getList, list.id));
+ } catch {
+ fetch = false;
+ }
+ }
+
+ yield put(
+ actions.handleListUpdate(
+ list,
+ fetch,
+ users,
+ cards,
+ cardMemberships,
+ cardLabels,
+ taskLists,
+ tasks,
+ attachments,
+ customFieldGroups,
+ customFields,
+ customFieldValues,
+ ),
+ );
+
+ if (list.boardId === null && currentCard && list.id === currentCard.listId) {
+ yield call(goToBoard, currentCard.boardId);
+ }
}
export function* moveList(id, index) {
@@ -77,6 +142,19 @@ export function* moveList(id, index) {
});
}
+export function* transferList(id, boardId) {
+ const currentCard = yield select(selectors.selectCurrentCard);
+
+ // TODO: hack?
+ if (currentCard && id === currentCard.listId) {
+ yield call(goToBoard, currentCard.boardId);
+ }
+
+ yield call(updateList, id, {
+ boardId,
+ });
+}
+
export function* sortList(id, data) {
yield put(actions.sortList(id, data));
@@ -190,6 +268,7 @@ export default {
updateList,
handleListUpdate,
moveList,
+ transferList,
sortList,
moveListCardsToArchiveList,
clearTrashListInCurrentBoard,
diff --git a/client/src/sagas/core/watchers/lists.js b/client/src/sagas/core/watchers/lists.js
index 59f1241a..d92f9dd3 100644
--- a/client/src/sagas/core/watchers/lists.js
+++ b/client/src/sagas/core/watchers/lists.js
@@ -28,6 +28,9 @@ export default function* listsWatchers() {
takeEvery(EntryActionTypes.LIST_SORT, ({ payload: { id, data } }) =>
services.sortList(id, data),
),
+ takeEvery(EntryActionTypes.LIST_TRANSFER, ({ payload: { id, boardId, index } }) =>
+ services.transferList(id, boardId, index),
+ ),
takeEvery(EntryActionTypes.LIST_CARDS_TO_ARCHIVE_LIST_MOVE, ({ payload: { id } }) =>
services.moveListCardsToArchiveList(id),
),
diff --git a/client/src/selectors/lists.js b/client/src/selectors/lists.js
index a528ce8f..633bfe03 100644
--- a/client/src/selectors/lists.js
+++ b/client/src/selectors/lists.js
@@ -7,6 +7,7 @@ import { createSelector } from 'redux-orm';
import orm from '../orm';
import { selectPath } from './router';
+import { selectCurrentUserId } from './users';
import { isLocalId } from '../utils/local-id';
import { BoardContexts, ListTypes } from '../constants/Enums';
@@ -64,6 +65,22 @@ export const makeSelectFilteredCardIdsByListId = () =>
export const selectFilteredCardIdsByListId = makeSelectFilteredCardIdsByListId();
+export const selectIsListWithIdAvailableForCurrentUser = createSelector(
+ orm,
+ (_, id) => id,
+ (state) => selectCurrentUserId(state),
+ ({ List, User }, id, currentUserId) => {
+ const listModel = List.withId(id);
+
+ if (!listModel) {
+ return false;
+ }
+
+ const currentUserModel = User.withId(currentUserId);
+ return listModel.isAvailableForUser(currentUserModel);
+ },
+);
+
export const selectCurrentListId = createSelector(
orm,
(state) => selectPath(state).boardId,
@@ -154,6 +171,7 @@ export default {
selectCardIdsByListId,
makeSelectFilteredCardIdsByListId,
selectFilteredCardIdsByListId,
+ selectIsListWithIdAvailableForCurrentUser,
selectCurrentListId,
selectCurrentList,
selectFirstFiniteListId,
diff --git a/client/src/selectors/users.js b/client/src/selectors/users.js
index 1da85bf8..7b1d0470 100644
--- a/client/src/selectors/users.js
+++ b/client/src/selectors/users.js
@@ -197,6 +197,35 @@ export const selectFavoriteProjectIdsForCurrentUser = createSelector(
},
);
+export const selectProjectsToBoardsWithEditorRightsForCurrentUser = createSelector(
+ orm,
+ (state) => selectCurrentUserId(state),
+ ({ User }, id) => {
+ if (!id) {
+ return id;
+ }
+
+ const userModel = User.withId(id);
+
+ if (!userModel) {
+ return userModel;
+ }
+
+ return userModel.getMembershipProjectsModelArray().map((projectModel) => ({
+ ...projectModel.ref,
+ boards: projectModel.getBoardsModelArrayForUserWithId(id).flatMap((boardModel) => {
+ const boardMembersipModel = boardModel.getMembershipModelByUserId(id);
+
+ if (boardMembersipModel.role !== BoardMembershipRoles.EDITOR) {
+ return [];
+ }
+
+ return boardModel.ref;
+ }),
+ }));
+ },
+);
+
export const selectProjectsToListsWithEditorRightsForCurrentUser = createSelector(
orm,
(state) => selectCurrentUserId(state),
@@ -334,6 +363,7 @@ export default {
selectFilteredProjectIdsForCurrentUser,
selectFilteredProjctIdsByGroupForCurrentUser,
selectFavoriteProjectIdsForCurrentUser,
+ selectProjectsToBoardsWithEditorRightsForCurrentUser,
selectProjectsToListsWithEditorRightsForCurrentUser,
selectBoardIdsForCurrentUser,
selectNotificationIdsForCurrentUser,
diff --git a/server/api/controllers/lists/update.js b/server/api/controllers/lists/update.js
index 6de53eee..c9537f36 100755
--- a/server/api/controllers/lists/update.js
+++ b/server/api/controllers/lists/update.js
@@ -12,6 +12,9 @@ const Errors = {
LIST_NOT_FOUND: {
listNotFound: 'List not found',
},
+ BOARD_NOT_FOUND: {
+ boardNotFound: 'Board not found',
+ },
};
module.exports = {
@@ -20,6 +23,7 @@ module.exports = {
...idInput,
required: true,
},
+ boardId: idInput,
type: {
type: 'string',
isIn: List.FINITE_TYPES,
@@ -47,6 +51,9 @@ module.exports = {
listNotFound: {
responseType: 'notFound',
},
+ boardNotFound: {
+ responseType: 'notFound',
+ },
},
async fn(inputs) {
@@ -59,7 +66,7 @@ module.exports = {
let { list } = pathToProject;
const { board, project } = pathToProject;
- const boardMembership = await BoardMembership.qm.getOneByBoardIdAndUserId(
+ let boardMembership = await BoardMembership.qm.getOneByBoardIdAndUserId(
board.id,
currentUser.id,
);
@@ -76,13 +83,39 @@ module.exports = {
throw Errors.NOT_ENOUGH_RIGHTS;
}
+ let nextProject;
+ let nextBoard;
+
+ if (!_.isUndefined(inputs.boardId)) {
+ ({ board: nextBoard, project: nextProject } = await sails.helpers.boards
+ .getPathToProjectById(inputs.boardId)
+ .intercept('pathNotFound', () => Errors.BOARD_NOT_FOUND));
+
+ boardMembership = await BoardMembership.qm.getOneByBoardIdAndUserId(
+ nextBoard.id,
+ currentUser.id,
+ );
+
+ if (!boardMembership) {
+ throw Errors.BOARD_NOT_FOUND; // Forbidden
+ }
+
+ if (boardMembership.role !== BoardMembership.Roles.EDITOR) {
+ throw Errors.NOT_ENOUGH_RIGHTS;
+ }
+ }
+
const values = _.pick(inputs, ['type', 'position', 'name', 'color']);
list = await sails.helpers.lists.updateOne.with({
- values,
project,
board,
record: list,
+ values: {
+ ...values,
+ project: nextProject,
+ board: nextBoard,
+ },
actorUser: currentUser,
request: this.req,
});
diff --git a/server/api/helpers/cards/detach-custom-fields.js b/server/api/helpers/cards/detach-custom-fields.js
new file mode 100644
index 00000000..794c7335
--- /dev/null
+++ b/server/api/helpers/cards/detach-custom-fields.js
@@ -0,0 +1,279 @@
+/*!
+ * Copyright (c) 2024 PLANKA Software GmbH
+ * Licensed under the Fair Use License: https://github.com/plankanban/planka/blob/master/LICENSE.md
+ */
+
+const { POSITION_GAP } = require('../../../constants');
+
+module.exports = {
+ inputs: {
+ idOrIds: {
+ type: 'json',
+ required: true,
+ },
+ boardId: {
+ type: 'ref',
+ required: true,
+ },
+ withBaseCustomFieldGroups: {
+ type: 'boolean',
+ required: true,
+ },
+ },
+
+ async fn(inputs) {
+ const cardIds = _.isString(inputs.idOrIds) ? [inputs.idOrIds] : inputs.idOrIds;
+
+ const boardCustomFieldGroups = await CustomFieldGroup.qm.getByBoardId(inputs.boardId);
+ const boardCustomFieldGroupIds = sails.helpers.utils.mapRecords(boardCustomFieldGroups);
+
+ const boardCustomFields =
+ await CustomField.qm.getByCustomFieldGroupIds(boardCustomFieldGroupIds);
+
+ const cardsCustomFieldGroups = await CustomFieldGroup.qm.getByCardIds(cardIds);
+ const customFieldGroupsByCardId = _.groupBy(cardsCustomFieldGroups, 'cardId');
+
+ let basedBoardCustomFieldGroups;
+ let basedCardCustomFieldGroups;
+ let baseCustomFieldGroupById;
+ let customFieldsByBaseCustomFieldGroupId;
+
+ if (inputs.withBaseCustomFieldGroups) {
+ basedBoardCustomFieldGroups = boardCustomFieldGroups.filter(
+ ({ baseCustomFieldGroupId }) => baseCustomFieldGroupId,
+ );
+
+ basedCardCustomFieldGroups = cardsCustomFieldGroups.filter(
+ ({ baseCustomFieldGroupId }) => baseCustomFieldGroupId,
+ );
+
+ const basedCustomFieldGroups = [
+ ...basedBoardCustomFieldGroups,
+ ...basedCardCustomFieldGroups,
+ ];
+
+ const baseCustomFieldGroupIds = sails.helpers.utils.mapRecords(
+ basedCustomFieldGroups,
+ 'baseCustomFieldGroupId',
+ true,
+ );
+
+ const baseCustomFieldGroups = await BaseCustomFieldGroup.qm.getByIds(baseCustomFieldGroupIds);
+ baseCustomFieldGroupById = _.keyBy(baseCustomFieldGroups, 'id');
+
+ const baseCustomFields = await CustomField.qm.getByBaseCustomFieldGroupIds(
+ Object.keys(baseCustomFieldGroupById),
+ );
+
+ customFieldsByBaseCustomFieldGroupId = _.groupBy(baseCustomFields, 'baseCustomFieldGroupId');
+ }
+
+ let idsTotal = (boardCustomFieldGroups.length + boardCustomFields.length) * cardIds.length;
+
+ if (inputs.withBaseCustomFieldGroups) {
+ idsTotal += basedBoardCustomFieldGroups.reduce((result, customFieldGroup) => {
+ const customFieldsItem =
+ customFieldsByBaseCustomFieldGroupId[customFieldGroup.baseCustomFieldGroupId];
+
+ return result + (customFieldsItem ? customFieldsItem.length : 0) * cardIds.length;
+ }, 0);
+
+ idsTotal += basedCardCustomFieldGroups.reduce((result, customFieldGroup) => {
+ const customFieldsItem =
+ customFieldsByBaseCustomFieldGroupId[customFieldGroup.baseCustomFieldGroupId];
+
+ return result + (customFieldsItem ? customFieldsItem.length : 0);
+ }, 0);
+ }
+
+ const ids = await sails.helpers.utils.generateIds(idsTotal);
+
+ const nextCustomFieldGroupIdByCustomFieldGroupIdByCardId = {};
+ const nextCustomFieldGroupsValues = (
+ await Promise.all(
+ cardIds.map(async (cardId) => {
+ const customFieldGroupIdByCustomFieldGroupId = {};
+ const customFieldGroupsValues = boardCustomFieldGroups.map((customFieldGroup, index) => {
+ const id = ids.shift();
+ customFieldGroupIdByCustomFieldGroupId[customFieldGroup.id] = id;
+
+ const values = {
+ ..._.pick(customFieldGroup, ['baseCustomFieldGroupId', 'name']),
+ id,
+ cardId,
+ position: POSITION_GAP * (index + 1),
+ };
+
+ if (inputs.withBaseCustomFieldGroups && customFieldGroup.baseCustomFieldGroupId) {
+ values.baseCustomFieldGroupId = null;
+
+ if (!customFieldGroup.name) {
+ values.name =
+ baseCustomFieldGroupById[customFieldGroup.baseCustomFieldGroupId].name;
+ }
+ }
+
+ return values;
+ });
+
+ nextCustomFieldGroupIdByCustomFieldGroupIdByCardId[cardId] =
+ customFieldGroupIdByCustomFieldGroupId;
+
+ if (customFieldGroupsValues.length > 0) {
+ const cardCustomFieldGroups = customFieldGroupsByCardId[cardId];
+
+ if (cardCustomFieldGroups && cardCustomFieldGroups.length > 0) {
+ const { position } = customFieldGroupsValues[customFieldGroupsValues.length - 1];
+
+ await Promise.all(
+ cardCustomFieldGroups.map((customFieldGroup) =>
+ CustomFieldGroup.qm.updateOne(customFieldGroup.id, {
+ position: customFieldGroup.position + position,
+ }),
+ ),
+ );
+ }
+ }
+
+ return customFieldGroupsValues;
+ }),
+ )
+ ).flat();
+
+ await CustomFieldGroup.qm.create(nextCustomFieldGroupsValues);
+
+ if (inputs.withBaseCustomFieldGroups) {
+ await CustomFieldGroup.qm.update(
+ {
+ cardId: cardIds,
+ baseCustomFieldGroupId: {
+ '!=': null,
+ },
+ },
+ {
+ baseCustomFieldGroupId: null,
+ },
+ );
+
+ const unnamedCustomFieldGroups = basedCardCustomFieldGroups.filter(({ name }) => !name);
+
+ await Promise.all(
+ unnamedCustomFieldGroups.map((customFieldGroup) =>
+ CustomFieldGroup.qm.updateOne(customFieldGroup.id, {
+ name: baseCustomFieldGroupById[customFieldGroup.baseCustomFieldGroupId].name,
+ }),
+ ),
+ );
+ }
+
+ const nextCustomFieldIdByCustomFieldIdByCardId = {};
+ const nextCustomFieldsValues = cardIds.flatMap((cardId) => {
+ const customFieldIdByCustomFieldId = {};
+ const customFieldsValues = boardCustomFields.map((customField) => {
+ const id = ids.shift();
+ customFieldIdByCustomFieldId[customField.id] = id;
+
+ return {
+ ..._.pick(customField, ['name', 'showOnFrontOfCard', 'position']),
+ id,
+ customFieldGroupId:
+ nextCustomFieldGroupIdByCustomFieldGroupIdByCardId[cardId][
+ customField.customFieldGroupId
+ ],
+ };
+ });
+
+ nextCustomFieldIdByCustomFieldIdByCardId[cardId] = customFieldIdByCustomFieldId;
+ return customFieldsValues;
+ });
+
+ if (inputs.withBaseCustomFieldGroups) {
+ cardIds.forEach((cardId) => {
+ basedBoardCustomFieldGroups.forEach((customFieldGroup) => {
+ const customFieldsItem =
+ customFieldsByBaseCustomFieldGroupId[customFieldGroup.baseCustomFieldGroupId];
+
+ if (!customFieldsItem) {
+ return;
+ }
+
+ customFieldsItem.forEach((customField) => {
+ const id = ids.shift();
+
+ nextCustomFieldIdByCustomFieldIdByCardId[cardId][
+ `${customFieldGroup.id}:${customField.id}`
+ ] = id;
+
+ nextCustomFieldsValues.push({
+ ..._.pick(customField, ['name', 'showOnFrontOfCard', 'position']),
+ id,
+ customFieldGroupId:
+ nextCustomFieldGroupIdByCustomFieldGroupIdByCardId[cardId][customFieldGroup.id],
+ });
+ });
+ });
+ });
+
+ basedCardCustomFieldGroups.forEach((customFieldGroup) => {
+ const customFieldsItem =
+ customFieldsByBaseCustomFieldGroupId[customFieldGroup.baseCustomFieldGroupId];
+
+ if (!customFieldsItem) {
+ return;
+ }
+
+ customFieldsItem.forEach((customField) => {
+ const id = ids.shift();
+
+ nextCustomFieldIdByCustomFieldIdByCardId[customFieldGroup.cardId][
+ `${customFieldGroup.id}:${customField.id}`
+ ] = id;
+
+ nextCustomFieldsValues.push({
+ ..._.pick(customField, ['name', 'showOnFrontOfCard', 'position']),
+ id,
+ customFieldGroupId: customFieldGroup.id,
+ });
+ });
+ });
+ }
+
+ await CustomField.qm.create(nextCustomFieldsValues);
+
+ const customFieldGroupIds = boardCustomFieldGroupIds;
+ if (inputs.withBaseCustomFieldGroups) {
+ customFieldGroupIds.push(...sails.helpers.utils.mapRecords(basedCardCustomFieldGroups));
+ }
+
+ const customFieldValues = await CustomFieldValue.qm.getByCardIds(cardIds, {
+ customFieldGroupIdOrIds: customFieldGroupIds,
+ });
+
+ await Promise.all(
+ customFieldValues.map((customFieldValue) => {
+ const updateValues = {
+ customFieldGroupId:
+ nextCustomFieldGroupIdByCustomFieldGroupIdByCardId[customFieldValue.cardId][
+ customFieldValue.customFieldGroupId
+ ],
+ };
+
+ const nextCustomFieldIdByCustomFieldId =
+ nextCustomFieldIdByCustomFieldIdByCardId[customFieldValue.cardId];
+
+ if (nextCustomFieldIdByCustomFieldId) {
+ const nextCustomFieldId =
+ nextCustomFieldIdByCustomFieldId[
+ `${customFieldValue.customFieldGroupId}:${customFieldValue.customFieldId}`
+ ] || nextCustomFieldIdByCustomFieldId[customFieldValue.customFieldId];
+
+ if (nextCustomFieldId) {
+ updateValues.customFieldId = nextCustomFieldId;
+ }
+ }
+
+ return CustomFieldValue.qm.updateOne(customFieldValue.id, updateValues);
+ }),
+ );
+ },
+};
diff --git a/server/api/helpers/cards/update-one.js b/server/api/helpers/cards/update-one.js
index b202466c..e84b7d96 100644
--- a/server/api/helpers/cards/update-one.js
+++ b/server/api/helpers/cards/update-one.js
@@ -3,8 +3,6 @@
* Licensed under the Fair Use License: https://github.com/plankanban/planka/blob/master/LICENSE.md
*/
-const { POSITION_GAP } = require('../../../constants');
-
module.exports = {
inputs: {
record: {
@@ -186,194 +184,10 @@ module.exports = {
},
);
- const boardCustomFieldGroups = await CustomFieldGroup.qm.getByBoardId(inputs.board.id);
- const boardCustomFieldGroupIds = sails.helpers.utils.mapRecords(boardCustomFieldGroups);
-
- const boardCustomFields =
- await CustomField.qm.getByCustomFieldGroupIds(boardCustomFieldGroupIds);
-
- const cardCustomFieldGroups = await CustomFieldGroup.qm.getByCardId(inputs.record.id);
-
- let basedCardCustomFieldGroups;
- let basedCustomFieldGroups;
- let baseCustomFieldGroupById;
- let customFieldsByBaseCustomFieldGroupId;
-
- if (values.project) {
- const basedBoardCustomFieldGroups = boardCustomFieldGroups.filter(
- ({ baseCustomFieldGroupId }) => baseCustomFieldGroupId,
- );
-
- basedCardCustomFieldGroups = cardCustomFieldGroups.filter(
- ({ baseCustomFieldGroupId }) => baseCustomFieldGroupId,
- );
-
- basedCustomFieldGroups = [...basedBoardCustomFieldGroups, ...basedCardCustomFieldGroups];
-
- const baseCustomFieldGroupIds = sails.helpers.utils.mapRecords(
- basedCustomFieldGroups,
- 'baseCustomFieldGroupId',
- true,
- );
-
- const baseCustomFieldGroups =
- await BaseCustomFieldGroup.qm.getByIds(baseCustomFieldGroupIds);
-
- baseCustomFieldGroupById = _.keyBy(baseCustomFieldGroups, 'id');
-
- const baseCustomFields = await CustomField.qm.getByBaseCustomFieldGroupIds(
- Object.keys(baseCustomFieldGroupById),
- );
-
- customFieldsByBaseCustomFieldGroupId = _.groupBy(
- baseCustomFields,
- 'baseCustomFieldGroupId',
- );
- }
-
- let idsTotal = boardCustomFieldGroups.length + boardCustomFields.length;
-
- if (values.project) {
- idsTotal += basedCustomFieldGroups.reduce((result, customFieldGroup) => {
- const customFieldsItem =
- customFieldsByBaseCustomFieldGroupId[customFieldGroup.baseCustomFieldGroupId];
-
- return result + (customFieldsItem ? customFieldsItem.length : 0);
- }, 0);
- }
-
- const ids = await sails.helpers.utils.generateIds(idsTotal);
-
- const nextCustomFieldGroupIdByCustomFieldGroupId = {};
- const nextCustomFieldGroupsValues = boardCustomFieldGroups.map(
- (customFieldGroup, index) => {
- const id = ids.shift();
- nextCustomFieldGroupIdByCustomFieldGroupId[customFieldGroup.id] = id;
-
- const nextValues = {
- ..._.pick(customFieldGroup, ['baseCustomFieldGroupId', 'name']),
- id,
- cardId: inputs.record.id,
- position: POSITION_GAP * (index + 1),
- };
-
- if (values.project && customFieldGroup.baseCustomFieldGroupId) {
- nextValues.baseCustomFieldGroupId = null;
-
- if (!customFieldGroup.name) {
- nextValues.name =
- baseCustomFieldGroupById[customFieldGroup.baseCustomFieldGroupId].name;
- }
- }
-
- return nextValues;
- },
- );
-
- if (nextCustomFieldGroupsValues.length > 0) {
- const { position } = nextCustomFieldGroupsValues[nextCustomFieldGroupsValues.length - 1];
-
- await Promise.all(
- cardCustomFieldGroups.map((customFieldGroup) =>
- CustomFieldGroup.qm.updateOne(customFieldGroup.id, {
- position: customFieldGroup.position + position,
- }),
- ),
- );
- }
-
- await CustomFieldGroup.qm.create(nextCustomFieldGroupsValues);
-
- if (values.project) {
- await CustomFieldGroup.qm.update(
- {
- cardId: inputs.record.id,
- baseCustomFieldGroupId: {
- '!=': null,
- },
- },
- {
- baseCustomFieldGroupId: null,
- },
- );
-
- const unnamedCustomFieldGroups = basedCardCustomFieldGroups.filter(({ name }) => !name);
-
- await Promise.all(
- unnamedCustomFieldGroups.map((customFieldGroup) =>
- CustomFieldGroup.qm.updateOne(customFieldGroup.id, {
- name: baseCustomFieldGroupById[customFieldGroup.baseCustomFieldGroupId].name,
- }),
- ),
- );
- }
-
- const nextCustomFieldIdByCustomFieldId = {};
- const nextCustomFieldsValues = boardCustomFields.map((customField) => {
- const id = ids.shift();
- nextCustomFieldIdByCustomFieldId[customField.id] = id;
-
- return {
- ..._.pick(customField, ['name', 'showOnFrontOfCard', 'position']),
- id,
- customFieldGroupId:
- nextCustomFieldGroupIdByCustomFieldGroupId[customField.customFieldGroupId],
- };
- });
-
- if (values.project) {
- basedCustomFieldGroups.forEach((customFieldGroup) => {
- const customFieldsItem =
- customFieldsByBaseCustomFieldGroupId[customFieldGroup.baseCustomFieldGroupId];
-
- if (!customFieldsItem) {
- return;
- }
-
- customFieldsItem.forEach((customField) => {
- const id = ids.shift();
- nextCustomFieldIdByCustomFieldId[`${customFieldGroup.id}:${customField.id}`] = id;
-
- nextCustomFieldsValues.push({
- ..._.pick(customField, ['name', 'showOnFrontOfCard', 'position']),
- id,
- customFieldGroupId:
- nextCustomFieldGroupIdByCustomFieldGroupId[customFieldGroup.id] ||
- customFieldGroup.id,
- });
- });
- });
- }
-
- await CustomField.qm.create(nextCustomFieldsValues);
-
- const customFieldGroupIds = boardCustomFieldGroupIds;
- if (values.project) {
- customFieldGroupIds.push(...sails.helpers.utils.mapRecords(basedCardCustomFieldGroups));
- }
-
- const customFieldValues = await CustomFieldValue.qm.getByCardId(inputs.record.id, {
- customFieldGroupIdOrIds: customFieldGroupIds,
- });
-
- await Promise.all(
- customFieldValues.map((customFieldValue) => {
- const updateValues = {
- customFieldGroupId:
- nextCustomFieldGroupIdByCustomFieldGroupId[customFieldValue.customFieldGroupId],
- };
-
- const nextCustomFieldId =
- nextCustomFieldIdByCustomFieldId[
- `${customFieldValue.customFieldGroupId}:${customFieldValue.customFieldId}`
- ] || nextCustomFieldIdByCustomFieldId[customFieldValue.customFieldId];
-
- if (nextCustomFieldId) {
- updateValues.customFieldId = nextCustomFieldId;
- }
-
- return CustomFieldValue.qm.updateOne(customFieldValue.id, updateValues);
- }),
+ await sails.helpers.cards.detachCustomFields(
+ inputs.record.id,
+ inputs.board.id,
+ !!values.project,
);
}
diff --git a/server/api/helpers/lists/update-one.js b/server/api/helpers/lists/update-one.js
index 5166a151..1f8a3f3e 100644
--- a/server/api/helpers/lists/update-one.js
+++ b/server/api/helpers/lists/update-one.js
@@ -30,14 +30,35 @@ module.exports = {
},
},
+ exits: {
+ boardInValuesMustBelongToProject: {},
+ },
+
async fn(inputs) {
const { values } = inputs;
+ if (values.project && values.project.id === inputs.project.id) {
+ delete values.project;
+ }
+
+ const project = values.project || inputs.project;
+
+ if (values.board) {
+ if (values.board.projectId !== project.id) {
+ throw 'boardInValuesMustBelongToProject';
+ }
+
+ if (values.board.id === inputs.board.id) {
+ delete values.board;
+ } else {
+ values.boardId = values.board.id;
+ }
+ }
+
+ const board = values.board || inputs.board;
+
if (!_.isUndefined(values.position)) {
- const lists = await sails.helpers.boards.getFiniteListsById(
- inputs.board.id,
- inputs.record.id,
- );
+ const lists = await sails.helpers.boards.getFiniteListsById(board.id, inputs.record.id);
const { position, repositions } = sails.helpers.utils.insertToPositionables(
values.position,
@@ -60,7 +81,7 @@ module.exports = {
},
);
- sails.sockets.broadcast(`board:${inputs.board.id}`, 'listUpdate', {
+ sails.sockets.broadcast(`board:${board.id}`, 'listUpdate', {
item: {
id: reposition.record.id,
position: reposition.position,
@@ -72,17 +93,126 @@ module.exports = {
}
}
+ let cardIdsByLabelId;
+ let prevLabels;
+
+ if (values.board) {
+ const cards = await Card.qm.getByListId(inputs.record.id);
+ const cardIds = sails.helpers.utils.mapRecords(cards);
+
+ const cardLabels = await CardLabel.qm.getByCardIds(cardIds);
+
+ cardIdsByLabelId = cardLabels.reduce(
+ (result, { cardId, labelId }) => ({
+ ...result,
+ [labelId]: [...(result[labelId] || []), cardId],
+ }),
+ {},
+ );
+
+ prevLabels = await Label.qm.getByIds(Object.keys(cardIdsByLabelId));
+
+ const boardMemberUserIds = await sails.helpers.boards.getMemberUserIds(values.board.id);
+
+ await CardSubscription.qm.delete({
+ cardId: cardIds,
+ userId: {
+ '!=': boardMemberUserIds,
+ },
+ });
+
+ await CardMembership.qm.delete({
+ cardId: cardIds,
+ userId: {
+ '!=': boardMemberUserIds,
+ },
+ });
+
+ await CardLabel.qm.delete({
+ cardId: cardIds,
+ });
+
+ const taskLists = await TaskList.qm.getByCardIds(cardIds);
+ const taskListIds = sails.helpers.utils.mapRecords(taskLists);
+
+ await Task.qm.update(
+ {
+ taskListId: taskListIds,
+ assigneeUserId: {
+ '!=': boardMemberUserIds,
+ },
+ },
+ {
+ assigneeUserId: null,
+ },
+ );
+
+ await sails.helpers.cards.detachCustomFields(cardIds, inputs.board.id, !!values.project);
+ }
+
const { list, tasks } = await List.qm.updateOne(inputs.record.id, values);
if (list) {
- sails.sockets.broadcast(
- `board:${list.boardId}`,
- 'listUpdate',
- {
+ if (values.board) {
+ if (prevLabels.length > 0) {
+ const labels = await Label.qm.getByBoardId(list.boardId);
+ const labelByName = _.keyBy(labels, 'name');
+
+ const cardLabelsValues = (
+ await Promise.all(
+ prevLabels.map(async (label) => {
+ let labelId;
+ if (labelByName[label.name]) {
+ ({ id: labelId } = labelByName[label.name]);
+ } else {
+ ({ id: labelId } = await sails.helpers.labels.createOne.with({
+ project,
+ values: {
+ ..._.omit(label, ['id', 'boardId', 'createdAt', 'updatedAt']),
+ board,
+ },
+ actorUser: inputs.actorUser,
+ }));
+ }
+
+ return cardIdsByLabelId[label.id].map((cardId) => ({
+ cardId,
+ labelId,
+ }));
+ }),
+ )
+ ).flat();
+
+ await CardLabel.qm.create(cardLabelsValues);
+ }
+
+ sails.sockets.broadcast(
+ `board:${inputs.board.id}`,
+ 'listUpdate',
+ {
+ item: {
+ id: list.id,
+ boardId: null,
+ },
+ },
+ inputs.request,
+ );
+
+ sails.sockets.broadcast(`board:${list.boardId}`, 'listUpdate', {
item: list,
- },
- inputs.request,
- );
+ });
+
+ // TODO: add transfer action
+ } else {
+ sails.sockets.broadcast(
+ `board:${list.boardId}`,
+ 'listUpdate',
+ {
+ item: list,
+ },
+ inputs.request,
+ );
+ }
if (tasks) {
const taskListIds = sails.helpers.utils.mapRecords(tasks, 'taskListId', true);
diff --git a/server/api/hooks/query-methods/models/CustomFieldValue.js b/server/api/hooks/query-methods/models/CustomFieldValue.js
index df1ea10c..5cea5248 100644
--- a/server/api/hooks/query-methods/models/CustomFieldValue.js
+++ b/server/api/hooks/query-methods/models/CustomFieldValue.js
@@ -3,7 +3,13 @@
* Licensed under the Fair Use License: https://github.com/plankanban/planka/blob/master/LICENSE.md
*/
-const defaultFind = (criteria) => CustomFieldValue.find(criteria).sort('id');
+const defaultFind = (criteria, { customFieldGroupIdOrIds }) => {
+ if (customFieldGroupIdOrIds) {
+ criteria.customFieldGroupId = customFieldGroupIdOrIds; // eslint-disable-line no-param-reassign
+ }
+
+ return CustomFieldValue.find(criteria).sort('id');
+};
/* Query methods */
@@ -41,22 +47,21 @@ const createOrUpdateOne = async (values) => {
const getByIds = (ids) => defaultFind(ids);
-const getByCardId = (cardId, { customFieldGroupIdOrIds } = {}) => {
- const criteria = {
- cardId,
- };
+const getByCardId = (cardId, { customFieldGroupIdOrIds } = {}) =>
+ defaultFind(
+ {
+ cardId,
+ },
+ { customFieldGroupIdOrIds },
+ );
- if (customFieldGroupIdOrIds) {
- criteria.customFieldGroupId = customFieldGroupIdOrIds;
- }
-
- return defaultFind(criteria);
-};
-
-const getByCardIds = (cardIds) =>
- defaultFind({
- cardId: cardIds,
- });
+const getByCardIds = (cardIds, { customFieldGroupIdOrIds } = {}) =>
+ defaultFind(
+ {
+ cardId: cardIds,
+ },
+ { customFieldGroupIdOrIds },
+ );
const getByCustomFieldGroupId = (customFieldGroupId) =>
defaultFind({
diff --git a/server/api/hooks/query-methods/models/List.js b/server/api/hooks/query-methods/models/List.js
index edfc4a3f..2f93e10a 100644
--- a/server/api/hooks/query-methods/models/List.js
+++ b/server/api/hooks/query-methods/models/List.js
@@ -52,13 +52,13 @@ const getOneTrashByBoardId = (boardId) =>
});
const updateOne = async (criteria, values) => {
- if (values.type) {
+ if (values.boardId || values.type) {
return sails.getDatastore().transaction(async (db) => {
const [whereQuery, whereQueryValues] = buildWhereQuery(criteria);
const queryResult = await sails
.sendNativeQuery(
- `SELECT type FROM list WHERE ${whereQuery} LIMIT 1 FOR UPDATE`,
+ `SELECT board_id, type FROM list WHERE ${whereQuery} LIMIT 1 FOR UPDATE`,
whereQueryValues,
)
.usingConnection(db);
@@ -68,6 +68,7 @@ const updateOne = async (criteria, values) => {
}
const prev = {
+ boardId: queryResult.rows[0].board_id,
type: queryResult.rows[0].type,
};
@@ -79,6 +80,17 @@ const updateOne = async (criteria, values) => {
let tasks = [];
if (list) {
+ if (list.boardId !== prev.boardId) {
+ await Card.update(
+ {
+ listId: list.id,
+ },
+ {
+ boardId: list.boardId,
+ },
+ ).usingConnection(db);
+ }
+
const prevTypeState = List.TYPE_STATE_BY_TYPE[prev.type];
const typeState = List.TYPE_STATE_BY_TYPE[list.type];