mirror of
https://github.com/plankanban/planka.git
synced 2025-12-23 01:11:40 +03:00
feat: Add ability to copy/cut cards with shortcut support
This commit is contained in:
@@ -160,6 +160,72 @@ const handleCardUpdate = (
|
|||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const transferCard = (id, data) => ({
|
||||||
|
type: ActionTypes.CARD_TRANSFER,
|
||||||
|
payload: {
|
||||||
|
id,
|
||||||
|
data,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
transferCard.success = (
|
||||||
|
card,
|
||||||
|
users,
|
||||||
|
cardMemberships,
|
||||||
|
cardLabels,
|
||||||
|
taskLists,
|
||||||
|
tasks,
|
||||||
|
attachments,
|
||||||
|
customFieldGroups,
|
||||||
|
customFields,
|
||||||
|
customFieldValues,
|
||||||
|
) => ({
|
||||||
|
type: ActionTypes.CARD_TRANSFER__SUCCESS,
|
||||||
|
payload: {
|
||||||
|
card,
|
||||||
|
users,
|
||||||
|
cardMemberships,
|
||||||
|
cardLabels,
|
||||||
|
taskLists,
|
||||||
|
tasks,
|
||||||
|
attachments,
|
||||||
|
customFieldGroups,
|
||||||
|
customFields,
|
||||||
|
customFieldValues,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
transferCard.failure = (
|
||||||
|
id,
|
||||||
|
error,
|
||||||
|
card,
|
||||||
|
users,
|
||||||
|
cardMemberships,
|
||||||
|
cardLabels,
|
||||||
|
taskLists,
|
||||||
|
tasks,
|
||||||
|
attachments,
|
||||||
|
customFieldGroups,
|
||||||
|
customFields,
|
||||||
|
customFieldValues,
|
||||||
|
) => ({
|
||||||
|
type: ActionTypes.CARD_TRANSFER__FAILURE,
|
||||||
|
payload: {
|
||||||
|
id,
|
||||||
|
error,
|
||||||
|
card,
|
||||||
|
users,
|
||||||
|
cardMemberships,
|
||||||
|
cardLabels,
|
||||||
|
taskLists,
|
||||||
|
tasks,
|
||||||
|
attachments,
|
||||||
|
customFieldGroups,
|
||||||
|
customFields,
|
||||||
|
customFieldValues,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
const duplicateCard = (id, localId, data) => ({
|
const duplicateCard = (id, localId, data) => ({
|
||||||
type: ActionTypes.CARD_DUPLICATE,
|
type: ActionTypes.CARD_DUPLICATE,
|
||||||
payload: {
|
payload: {
|
||||||
@@ -204,6 +270,25 @@ duplicateCard.failure = (localId, error) => ({
|
|||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const copyCard = (id) => ({
|
||||||
|
type: ActionTypes.CARD_COPY,
|
||||||
|
payload: {
|
||||||
|
id,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
const cutCard = (id) => ({
|
||||||
|
type: ActionTypes.CARD_CUT,
|
||||||
|
payload: {
|
||||||
|
id,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
const pasteCard = () => ({
|
||||||
|
type: ActionTypes.CARD_PASTE,
|
||||||
|
payload: {},
|
||||||
|
});
|
||||||
|
|
||||||
const deleteCard = (id) => ({
|
const deleteCard = (id) => ({
|
||||||
type: ActionTypes.CARD_DELETE,
|
type: ActionTypes.CARD_DELETE,
|
||||||
payload: {
|
payload: {
|
||||||
@@ -240,7 +325,11 @@ export default {
|
|||||||
handleCardCreate,
|
handleCardCreate,
|
||||||
updateCard,
|
updateCard,
|
||||||
handleCardUpdate,
|
handleCardUpdate,
|
||||||
|
transferCard,
|
||||||
duplicateCard,
|
duplicateCard,
|
||||||
|
copyCard,
|
||||||
|
cutCard,
|
||||||
|
pasteCard,
|
||||||
deleteCard,
|
deleteCard,
|
||||||
handleCardDelete,
|
handleCardDelete,
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -30,12 +30,17 @@ const EndlessContent = React.memo(() => {
|
|||||||
[dispatch],
|
[dispatch],
|
||||||
);
|
);
|
||||||
|
|
||||||
|
const handleCardPaste = useCallback(() => {
|
||||||
|
dispatch(entryActions.pasteCardInCurrentList());
|
||||||
|
}, [dispatch]);
|
||||||
|
|
||||||
const viewProps = {
|
const viewProps = {
|
||||||
cardIds,
|
cardIds,
|
||||||
isCardsFetching,
|
isCardsFetching,
|
||||||
isAllCardsFetched,
|
isAllCardsFetched,
|
||||||
onCardsFetch: handleCardsFetch,
|
onCardsFetch: handleCardsFetch,
|
||||||
onCardCreate: handleCardCreate,
|
onCardCreate: handleCardCreate,
|
||||||
|
onCardPaste: handleCardPaste,
|
||||||
};
|
};
|
||||||
|
|
||||||
let View;
|
let View;
|
||||||
|
|||||||
@@ -26,6 +26,10 @@ const FiniteContent = React.memo(() => {
|
|||||||
[dispatch],
|
[dispatch],
|
||||||
);
|
);
|
||||||
|
|
||||||
|
const handleCardPaste = useCallback(() => {
|
||||||
|
dispatch(entryActions.pasteCardInCurrentContext());
|
||||||
|
}, [dispatch]);
|
||||||
|
|
||||||
let View;
|
let View;
|
||||||
switch (board.view) {
|
switch (board.view) {
|
||||||
case BoardViews.GRID:
|
case BoardViews.GRID:
|
||||||
@@ -39,7 +43,13 @@ const FiniteContent = React.memo(() => {
|
|||||||
default:
|
default:
|
||||||
}
|
}
|
||||||
|
|
||||||
return <View cardIds={cardIds} onCardCreate={canAddCard ? handleCardCreate : undefined} />;
|
return (
|
||||||
|
<View
|
||||||
|
cardIds={cardIds}
|
||||||
|
onCardCreate={canAddCard ? handleCardCreate : undefined}
|
||||||
|
onCardPaste={canAddCard ? handleCardPaste : undefined}
|
||||||
|
/>
|
||||||
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
export default FiniteContent;
|
export default FiniteContent;
|
||||||
|
|||||||
@@ -5,10 +5,11 @@
|
|||||||
|
|
||||||
import React, { useCallback, useState } from 'react';
|
import React, { useCallback, useState } from 'react';
|
||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
import { useSelector } from 'react-redux';
|
import classNames from 'classnames';
|
||||||
|
import { shallowEqual, useSelector } from 'react-redux';
|
||||||
import { useInView } from 'react-intersection-observer';
|
import { useInView } from 'react-intersection-observer';
|
||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
import { Button, Loader } from 'semantic-ui-react';
|
import { Button, Icon, Loader } from 'semantic-ui-react';
|
||||||
import { useWindowWidth } from '../../../lib/hooks';
|
import { useWindowWidth } from '../../../lib/hooks';
|
||||||
import { Masonry } from '../../../lib/custom-ui';
|
import { Masonry } from '../../../lib/custom-ui';
|
||||||
|
|
||||||
@@ -21,11 +22,18 @@ import PlusMathIcon from '../../../assets/images/plus-math-icon.svg?react';
|
|||||||
import styles from './GridView.module.scss';
|
import styles from './GridView.module.scss';
|
||||||
|
|
||||||
const GridView = React.memo(
|
const GridView = React.memo(
|
||||||
({ cardIds, isCardsFetching, isAllCardsFetched, onCardsFetch, onCardCreate }) => {
|
({ cardIds, isCardsFetching, isAllCardsFetched, onCardsFetch, onCardCreate, onCardPaste }) => {
|
||||||
const canAddCard = useSelector((state) => {
|
const clipboard = useSelector(selectors.selectClipboard);
|
||||||
|
|
||||||
|
const { canAddCard, canPasteCard } = useSelector((state) => {
|
||||||
const boardMembership = selectors.selectCurrentUserMembershipForCurrentBoard(state);
|
const boardMembership = selectors.selectCurrentUserMembershipForCurrentBoard(state);
|
||||||
return !!boardMembership && boardMembership.role === BoardMembershipRoles.EDITOR;
|
const isEditor = !!boardMembership && boardMembership.role === BoardMembershipRoles.EDITOR;
|
||||||
});
|
|
||||||
|
return {
|
||||||
|
canAddCard: isEditor,
|
||||||
|
canPasteCard: isEditor,
|
||||||
|
};
|
||||||
|
}, shallowEqual);
|
||||||
|
|
||||||
const [t] = useTranslation();
|
const [t] = useTranslation();
|
||||||
const [isAddCardOpened, setIsAddCardOpened] = useState(false);
|
const [isAddCardOpened, setIsAddCardOpened] = useState(false);
|
||||||
@@ -59,17 +67,31 @@ const GridView = React.memo(
|
|||||||
<AddCard onCreate={onCardCreate} onClose={handleAddCardClose} />
|
<AddCard onCreate={onCardCreate} onClose={handleAddCardClose} />
|
||||||
</div>
|
</div>
|
||||||
) : (
|
) : (
|
||||||
<Button
|
<div>
|
||||||
type="button"
|
<div className={styles.addCardButtonWrapper}>
|
||||||
disabled={!onCardCreate}
|
<Button
|
||||||
className={styles.addCardButton}
|
type="button"
|
||||||
onClick={handleAddCardClick}
|
disabled={!onCardCreate}
|
||||||
>
|
className={styles.addCardButton}
|
||||||
<PlusMathIcon className={styles.addCardButtonIcon} />
|
onClick={handleAddCardClick}
|
||||||
<span className={styles.addCardButtonText}>
|
>
|
||||||
{onCardCreate ? t('action.addCard') : t('common.atLeastOneListMustBePresent')}
|
<PlusMathIcon className={styles.addCardButtonIcon} />
|
||||||
</span>
|
<span className={styles.addCardButtonText}>
|
||||||
</Button>
|
{onCardCreate ? t('action.addCard') : t('common.atLeastOneListMustBePresent')}
|
||||||
|
</span>
|
||||||
|
</Button>
|
||||||
|
{onCardPaste && clipboard && canPasteCard && (
|
||||||
|
<Button
|
||||||
|
type="button"
|
||||||
|
disabled={!onCardCreate}
|
||||||
|
className={classNames(styles.addCardButton, styles.paste)}
|
||||||
|
onClick={onCardPaste}
|
||||||
|
>
|
||||||
|
<Icon fitted name="paste" />
|
||||||
|
</Button>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
))}
|
))}
|
||||||
{cardIds.map((cardId) => (
|
{cardIds.map((cardId) => (
|
||||||
<div key={cardId} className={styles.card}>
|
<div key={cardId} className={styles.card}>
|
||||||
@@ -97,6 +119,7 @@ GridView.propTypes = {
|
|||||||
isAllCardsFetched: PropTypes.bool,
|
isAllCardsFetched: PropTypes.bool,
|
||||||
onCardsFetch: PropTypes.func,
|
onCardsFetch: PropTypes.func,
|
||||||
onCardCreate: PropTypes.func,
|
onCardCreate: PropTypes.func,
|
||||||
|
onCardPaste: PropTypes.func,
|
||||||
};
|
};
|
||||||
|
|
||||||
GridView.defaultProps = {
|
GridView.defaultProps = {
|
||||||
@@ -104,6 +127,7 @@ GridView.defaultProps = {
|
|||||||
isAllCardsFetched: undefined,
|
isAllCardsFetched: undefined,
|
||||||
onCardsFetch: undefined,
|
onCardsFetch: undefined,
|
||||||
onCardCreate: undefined,
|
onCardCreate: undefined,
|
||||||
|
onCardPaste: undefined,
|
||||||
};
|
};
|
||||||
|
|
||||||
export default GridView;
|
export default GridView;
|
||||||
|
|||||||
@@ -10,16 +10,16 @@
|
|||||||
border-radius: 3px;
|
border-radius: 3px;
|
||||||
color: rgba(255, 255, 255, 0.72);
|
color: rgba(255, 255, 255, 0.72);
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
display: block;
|
|
||||||
fill: rgba(255, 255, 255, 0.72);
|
fill: rgba(255, 255, 255, 0.72);
|
||||||
|
flex: 1;
|
||||||
font-weight: normal;
|
font-weight: normal;
|
||||||
height: 42px;
|
height: 42px;
|
||||||
|
margin: 0;
|
||||||
min-height: 42px;
|
min-height: 42px;
|
||||||
padding: 11px;
|
padding: 11px;
|
||||||
text-align: left;
|
text-align: left;
|
||||||
transition: background 85ms ease-in, opacity 40ms ease-in,
|
transition: background 85ms ease-in, opacity 40ms ease-in,
|
||||||
border-color 85ms ease-in;
|
border-color 85ms ease-in;
|
||||||
width: 100%;
|
|
||||||
|
|
||||||
&:active {
|
&:active {
|
||||||
outline: none;
|
outline: none;
|
||||||
@@ -28,6 +28,10 @@
|
|||||||
&:hover {
|
&:hover {
|
||||||
background: rgba(0, 0, 0, 0.32);
|
background: rgba(0, 0, 0, 0.32);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
&.paste {
|
||||||
|
flex: 0 0 auto;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.addCardButtonIcon {
|
.addCardButtonIcon {
|
||||||
@@ -43,6 +47,11 @@
|
|||||||
vertical-align: top;
|
vertical-align: top;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.addCardButtonWrapper {
|
||||||
|
display: flex;
|
||||||
|
gap: 6px;
|
||||||
|
}
|
||||||
|
|
||||||
.card {
|
.card {
|
||||||
background: rgba(223, 227, 230, 0.8);
|
background: rgba(223, 227, 230, 0.8);
|
||||||
border-radius: 3px;
|
border-radius: 3px;
|
||||||
|
|||||||
@@ -6,10 +6,10 @@
|
|||||||
import React, { useCallback, useState } from 'react';
|
import React, { useCallback, useState } from 'react';
|
||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
import classNames from 'classnames';
|
import classNames from 'classnames';
|
||||||
import { useSelector } from 'react-redux';
|
import { shallowEqual, useSelector } from 'react-redux';
|
||||||
import { useInView } from 'react-intersection-observer';
|
import { useInView } from 'react-intersection-observer';
|
||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
import { Button, Loader } from 'semantic-ui-react';
|
import { Button, Icon, Loader } from 'semantic-ui-react';
|
||||||
|
|
||||||
import selectors from '../../../selectors';
|
import selectors from '../../../selectors';
|
||||||
import { BoardMembershipRoles } from '../../../constants/Enums';
|
import { BoardMembershipRoles } from '../../../constants/Enums';
|
||||||
@@ -20,11 +20,18 @@ import PlusMathIcon from '../../../assets/images/plus-math-icon.svg?react';
|
|||||||
import styles from './ListView.module.scss';
|
import styles from './ListView.module.scss';
|
||||||
|
|
||||||
const ListView = React.memo(
|
const ListView = React.memo(
|
||||||
({ cardIds, isCardsFetching, isAllCardsFetched, onCardsFetch, onCardCreate }) => {
|
({ cardIds, isCardsFetching, isAllCardsFetched, onCardsFetch, onCardCreate, onCardPaste }) => {
|
||||||
const canAddCard = useSelector((state) => {
|
const clipboard = useSelector(selectors.selectClipboard);
|
||||||
|
|
||||||
|
const { canAddCard, canPasteCard } = useSelector((state) => {
|
||||||
const boardMembership = selectors.selectCurrentUserMembershipForCurrentBoard(state);
|
const boardMembership = selectors.selectCurrentUserMembershipForCurrentBoard(state);
|
||||||
return !!boardMembership && boardMembership.role === BoardMembershipRoles.EDITOR;
|
const isEditor = !!boardMembership && boardMembership.role === BoardMembershipRoles.EDITOR;
|
||||||
});
|
|
||||||
|
return {
|
||||||
|
canAddCard: isEditor,
|
||||||
|
canPasteCard: isEditor,
|
||||||
|
};
|
||||||
|
}, shallowEqual);
|
||||||
|
|
||||||
const [t] = useTranslation();
|
const [t] = useTranslation();
|
||||||
const [isAddCardOpened, setIsAddCardOpened] = useState(false);
|
const [isAddCardOpened, setIsAddCardOpened] = useState(false);
|
||||||
@@ -54,17 +61,29 @@ const ListView = React.memo(
|
|||||||
<AddCard onCreate={onCardCreate} onClose={handleAddCardClose} />
|
<AddCard onCreate={onCardCreate} onClose={handleAddCardClose} />
|
||||||
</div>
|
</div>
|
||||||
) : (
|
) : (
|
||||||
<Button
|
<div className={styles.addCardButtonWrapper}>
|
||||||
type="button"
|
<Button
|
||||||
disabled={!onCardCreate}
|
type="button"
|
||||||
className={styles.addCardButton}
|
disabled={!onCardCreate}
|
||||||
onClick={handleAddCardClick}
|
className={styles.addCardButton}
|
||||||
>
|
onClick={handleAddCardClick}
|
||||||
<PlusMathIcon className={styles.addCardButtonIcon} />
|
>
|
||||||
<span className={styles.addCardButtonText}>
|
<PlusMathIcon className={styles.addCardButtonIcon} />
|
||||||
{onCardCreate ? t('action.addCard') : t('common.atLeastOneListMustBePresent')}
|
<span className={styles.addCardButtonText}>
|
||||||
</span>
|
{onCardCreate ? t('action.addCard') : t('common.atLeastOneListMustBePresent')}
|
||||||
</Button>
|
</span>
|
||||||
|
</Button>
|
||||||
|
{onCardPaste && clipboard && canPasteCard && (
|
||||||
|
<Button
|
||||||
|
type="button"
|
||||||
|
disabled={!onCardCreate}
|
||||||
|
className={classNames(styles.addCardButton, styles.paste)}
|
||||||
|
onClick={onCardPaste}
|
||||||
|
>
|
||||||
|
<Icon fitted name="paste" />
|
||||||
|
</Button>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
))}
|
))}
|
||||||
{cardIds.length > 0 && (
|
{cardIds.length > 0 && (
|
||||||
<div className={classNames(styles.segment, styles.cards)}>
|
<div className={classNames(styles.segment, styles.cards)}>
|
||||||
@@ -95,6 +114,7 @@ ListView.propTypes = {
|
|||||||
isAllCardsFetched: PropTypes.bool,
|
isAllCardsFetched: PropTypes.bool,
|
||||||
onCardsFetch: PropTypes.func,
|
onCardsFetch: PropTypes.func,
|
||||||
onCardCreate: PropTypes.func,
|
onCardCreate: PropTypes.func,
|
||||||
|
onCardPaste: PropTypes.func,
|
||||||
};
|
};
|
||||||
|
|
||||||
ListView.defaultProps = {
|
ListView.defaultProps = {
|
||||||
@@ -102,6 +122,7 @@ ListView.defaultProps = {
|
|||||||
isAllCardsFetched: undefined,
|
isAllCardsFetched: undefined,
|
||||||
onCardsFetch: undefined,
|
onCardsFetch: undefined,
|
||||||
onCardCreate: undefined,
|
onCardCreate: undefined,
|
||||||
|
onCardPaste: undefined,
|
||||||
};
|
};
|
||||||
|
|
||||||
export default ListView;
|
export default ListView;
|
||||||
|
|||||||
@@ -12,14 +12,14 @@
|
|||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
display: block;
|
display: block;
|
||||||
fill: rgba(255, 255, 255, 0.72);
|
fill: rgba(255, 255, 255, 0.72);
|
||||||
|
flex: 1;
|
||||||
font-weight: normal;
|
font-weight: normal;
|
||||||
height: 42px;
|
height: 42px;
|
||||||
margin-bottom: 12px;
|
margin: 0;
|
||||||
padding: 11px;
|
padding: 11px;
|
||||||
text-align: left;
|
text-align: left;
|
||||||
transition: background 85ms ease-in, opacity 40ms ease-in,
|
transition: background 85ms ease-in, opacity 40ms ease-in,
|
||||||
border-color 85ms ease-in;
|
border-color 85ms ease-in;
|
||||||
width: 100%;
|
|
||||||
|
|
||||||
&:active {
|
&:active {
|
||||||
outline: none;
|
outline: none;
|
||||||
@@ -28,6 +28,10 @@
|
|||||||
&:hover {
|
&:hover {
|
||||||
background: rgba(0, 0, 0, 0.32);
|
background: rgba(0, 0, 0, 0.32);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
&.paste {
|
||||||
|
flex: 0 0 auto;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.addCardButtonIcon {
|
.addCardButtonIcon {
|
||||||
@@ -43,6 +47,12 @@
|
|||||||
vertical-align: top;
|
vertical-align: top;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.addCardButtonWrapper {
|
||||||
|
display: flex;
|
||||||
|
gap: 6px;
|
||||||
|
margin-bottom: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
.card {
|
.card {
|
||||||
margin-bottom: 6px;
|
margin-bottom: 6px;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -18,15 +18,34 @@ import { isActiveTextElement } from '../../../utils/element-helpers';
|
|||||||
import { isModifierKeyPressed } from '../../../utils/event-helpers';
|
import { isModifierKeyPressed } from '../../../utils/event-helpers';
|
||||||
import { BoardShortcutsContext } from '../../../contexts';
|
import { BoardShortcutsContext } from '../../../contexts';
|
||||||
import Paths from '../../../constants/Paths';
|
import Paths from '../../../constants/Paths';
|
||||||
import { BoardMembershipRoles, ListTypes } from '../../../constants/Enums';
|
import {
|
||||||
|
BoardContexts,
|
||||||
|
BoardMembershipRoles,
|
||||||
|
BoardViews,
|
||||||
|
ListTypes,
|
||||||
|
} from '../../../constants/Enums';
|
||||||
import CardActionsStep from '../../cards/CardActionsStep';
|
import CardActionsStep from '../../cards/CardActionsStep';
|
||||||
|
|
||||||
|
const canCopyCard = (isManager, boardMembership) => {
|
||||||
|
if (isManager) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return !!boardMembership && boardMembership.role === BoardMembershipRoles.EDITOR;
|
||||||
|
};
|
||||||
|
|
||||||
|
const canCutCard = (boardMembership) =>
|
||||||
|
!!boardMembership && boardMembership.role === BoardMembershipRoles.EDITOR;
|
||||||
|
|
||||||
|
const canPasteCard = (boardMembership) =>
|
||||||
|
!!boardMembership && boardMembership.role === BoardMembershipRoles.EDITOR;
|
||||||
|
|
||||||
const canEditCardName = (boardMembership, list) => {
|
const canEditCardName = (boardMembership, list) => {
|
||||||
if (isListArchiveOrTrash(list)) {
|
if (isListArchiveOrTrash(list)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
return boardMembership && boardMembership.role === BoardMembershipRoles.EDITOR;
|
return !!boardMembership && boardMembership.role === BoardMembershipRoles.EDITOR;
|
||||||
};
|
};
|
||||||
|
|
||||||
const canArchiveCard = (boardMembership, list) => {
|
const canArchiveCard = (boardMembership, list) => {
|
||||||
@@ -34,7 +53,7 @@ const canArchiveCard = (boardMembership, list) => {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
return boardMembership && boardMembership.role === BoardMembershipRoles.EDITOR;
|
return !!boardMembership && boardMembership.role === BoardMembershipRoles.EDITOR;
|
||||||
};
|
};
|
||||||
|
|
||||||
const canUseCardMembers = (boardMembership, list) => {
|
const canUseCardMembers = (boardMembership, list) => {
|
||||||
@@ -42,7 +61,7 @@ const canUseCardMembers = (boardMembership, list) => {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
return boardMembership && boardMembership.role === BoardMembershipRoles.EDITOR;
|
return !!boardMembership && boardMembership.role === BoardMembershipRoles.EDITOR;
|
||||||
};
|
};
|
||||||
|
|
||||||
const canUseCardLabels = (boardMembership, list) => {
|
const canUseCardLabels = (boardMembership, list) => {
|
||||||
@@ -50,7 +69,7 @@ const canUseCardLabels = (boardMembership, list) => {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
return boardMembership && boardMembership.role === BoardMembershipRoles.EDITOR;
|
return !!boardMembership && boardMembership.role === BoardMembershipRoles.EDITOR;
|
||||||
};
|
};
|
||||||
|
|
||||||
const ShortcutsProvider = React.memo(({ children }) => {
|
const ShortcutsProvider = React.memo(({ children }) => {
|
||||||
@@ -58,8 +77,20 @@ const ShortcutsProvider = React.memo(({ children }) => {
|
|||||||
|
|
||||||
const dispatch = useDispatch();
|
const dispatch = useDispatch();
|
||||||
|
|
||||||
|
const selectedListRef = useRef(null);
|
||||||
const selectedCardRef = useRef(null);
|
const selectedCardRef = useRef(null);
|
||||||
|
|
||||||
|
const handleListMouseEnter = useCallback((id, onPaste) => {
|
||||||
|
selectedListRef.current = {
|
||||||
|
id,
|
||||||
|
onPaste,
|
||||||
|
};
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
const handleListMouseLeave = useCallback(() => {
|
||||||
|
selectedListRef.current = null;
|
||||||
|
}, []);
|
||||||
|
|
||||||
const handleCardMouseEnter = useCallback((id, editName, openActions) => {
|
const handleCardMouseEnter = useCallback((id, editName, openActions) => {
|
||||||
selectedCardRef.current = {
|
selectedCardRef.current = {
|
||||||
id,
|
id,
|
||||||
@@ -73,15 +104,106 @@ const ShortcutsProvider = React.memo(({ children }) => {
|
|||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
const contextValue = useMemo(
|
const contextValue = useMemo(
|
||||||
() => [handleCardMouseEnter, handleCardMouseLeave],
|
() => [handleListMouseEnter, handleListMouseLeave, handleCardMouseEnter, handleCardMouseLeave],
|
||||||
[handleCardMouseEnter, handleCardMouseLeave],
|
[handleListMouseEnter, handleListMouseLeave, handleCardMouseEnter, handleCardMouseLeave],
|
||||||
);
|
);
|
||||||
|
|
||||||
useDidUpdate(() => {
|
useDidUpdate(() => {
|
||||||
|
selectedListRef.current = null;
|
||||||
selectedCardRef.current = null;
|
selectedCardRef.current = null;
|
||||||
}, [cardId, boardId]);
|
}, [cardId, boardId]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
|
const handleCardCopy = (event) => {
|
||||||
|
if (!selectedCardRef.current) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const state = store.getState();
|
||||||
|
const card = selectors.selectCardById(state, selectedCardRef.current.id);
|
||||||
|
|
||||||
|
if (!card || !card.isPersisted) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const isManager = selectors.selectIsCurrentUserManagerForCurrentProject(state);
|
||||||
|
const boardMembership = selectors.selectCurrentUserMembershipForCurrentBoard(state);
|
||||||
|
|
||||||
|
if (!canCopyCard(isManager, boardMembership)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
event.preventDefault();
|
||||||
|
dispatch(entryActions.copyCard(card.id));
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleCardCut = (event) => {
|
||||||
|
if (!selectedCardRef.current) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const state = store.getState();
|
||||||
|
const card = selectors.selectCardById(state, selectedCardRef.current.id);
|
||||||
|
|
||||||
|
if (!card || !card.isPersisted) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const boardMembership = selectors.selectCurrentUserMembershipForCurrentBoard(state);
|
||||||
|
|
||||||
|
if (!canCutCard(boardMembership)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
event.preventDefault();
|
||||||
|
dispatch(entryActions.cutCard(card.id));
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleCardPaste = (event) => {
|
||||||
|
const state = store.getState();
|
||||||
|
const clipboard = selectors.selectClipboard(state);
|
||||||
|
|
||||||
|
if (!clipboard) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const board = selectors.selectCurrentBoard(state);
|
||||||
|
|
||||||
|
let listId;
|
||||||
|
if (board.context === BoardContexts.BOARD) {
|
||||||
|
if (board.view === BoardViews.KANBAN) {
|
||||||
|
listId = selectedListRef.current?.id;
|
||||||
|
} else {
|
||||||
|
listId = selectors.selectFirstKanbanListId(state);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
listId = selectors.selectCurrentListId(state);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!listId) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const list = selectors.selectListById(state, listId);
|
||||||
|
|
||||||
|
if (!list || !list.isPersisted) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const boardMembership = selectors.selectCurrentUserMembershipForCurrentBoard(state);
|
||||||
|
|
||||||
|
if (!canPasteCard(boardMembership)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
event.preventDefault();
|
||||||
|
dispatch(entryActions.pasteCard(list.id));
|
||||||
|
|
||||||
|
if (selectedListRef.current) {
|
||||||
|
selectedListRef.current.onPaste();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
const handleCardOpen = (event) => {
|
const handleCardOpen = (event) => {
|
||||||
if (!selectedCardRef.current) {
|
if (!selectedCardRef.current) {
|
||||||
return;
|
return;
|
||||||
@@ -234,6 +356,22 @@ const ShortcutsProvider = React.memo(({ children }) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (isModifierKeyPressed(event)) {
|
if (isModifierKeyPressed(event)) {
|
||||||
|
switch (event.key) {
|
||||||
|
case 'c':
|
||||||
|
handleCardCopy(event);
|
||||||
|
|
||||||
|
break;
|
||||||
|
case 'x':
|
||||||
|
handleCardCut(event);
|
||||||
|
|
||||||
|
break;
|
||||||
|
case 'v':
|
||||||
|
handleCardPaste(event);
|
||||||
|
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
}
|
||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -16,6 +16,7 @@ import { closePopup, usePopup } from '../../../lib/popup';
|
|||||||
import selectors from '../../../selectors';
|
import selectors from '../../../selectors';
|
||||||
import { BoardShortcutsContext } from '../../../contexts';
|
import { BoardShortcutsContext } from '../../../contexts';
|
||||||
import Paths from '../../../constants/Paths';
|
import Paths from '../../../constants/Paths';
|
||||||
|
import ClipboardTypes from '../../../constants/ClipboardTypes';
|
||||||
import { BoardMembershipRoles, CardTypes } from '../../../constants/Enums';
|
import { BoardMembershipRoles, CardTypes } from '../../../constants/Enums';
|
||||||
import ProjectContent from './ProjectContent';
|
import ProjectContent from './ProjectContent';
|
||||||
import StoryContent from './StoryContent';
|
import StoryContent from './StoryContent';
|
||||||
@@ -44,14 +45,25 @@ const Card = React.memo(({ id, isInline }) => {
|
|||||||
return selectIsCardWithIdRecent(state, id);
|
return selectIsCardWithIdRecent(state, id);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const isCut = useSelector((state) => {
|
||||||
|
const clipboard = selectors.selectClipboard(state);
|
||||||
|
return clipboard && clipboard.type === ClipboardTypes.CUT && card.id === clipboard.cardId;
|
||||||
|
});
|
||||||
|
|
||||||
const canUseActions = useSelector((state) => {
|
const canUseActions = useSelector((state) => {
|
||||||
|
const isManager = selectors.selectIsCurrentUserManagerForCurrentProject(state);
|
||||||
|
|
||||||
|
if (isManager) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
const boardMembership = selectors.selectCurrentUserMembershipForCurrentBoard(state);
|
const boardMembership = selectors.selectCurrentUserMembershipForCurrentBoard(state);
|
||||||
return !!boardMembership && boardMembership.role === BoardMembershipRoles.EDITOR;
|
return !!boardMembership && boardMembership.role === BoardMembershipRoles.EDITOR;
|
||||||
});
|
});
|
||||||
|
|
||||||
const dispatch = useDispatch();
|
const dispatch = useDispatch();
|
||||||
const [isEditNameOpened, setIsEditNameOpened] = useState(false);
|
const [isEditNameOpened, setIsEditNameOpened] = useState(false);
|
||||||
const [handleCardMouseEnter, handleCardMouseLeave] = useContext(BoardShortcutsContext);
|
const [, , handleCardMouseEnter, handleCardMouseLeave] = useContext(BoardShortcutsContext);
|
||||||
|
|
||||||
const actionsPopupRef = useRef(null);
|
const actionsPopupRef = useRef(null);
|
||||||
|
|
||||||
@@ -139,7 +151,11 @@ const Card = React.memo(({ id, isInline }) => {
|
|||||||
{/* eslint-disable-next-line jsx-a11y/click-events-have-key-events,
|
{/* eslint-disable-next-line jsx-a11y/click-events-have-key-events,
|
||||||
jsx-a11y/no-static-element-interactions */}
|
jsx-a11y/no-static-element-interactions */}
|
||||||
<div
|
<div
|
||||||
className={classNames(styles.content, card.isClosed && styles.contentDisabled)}
|
className={classNames(
|
||||||
|
styles.content,
|
||||||
|
card.isClosed && styles.contentDisabled,
|
||||||
|
isCut && styles.contentCut,
|
||||||
|
)}
|
||||||
onMouseEnter={handleMouseEnter}
|
onMouseEnter={handleMouseEnter}
|
||||||
onMouseLeave={handleCardMouseLeave}
|
onMouseLeave={handleCardMouseLeave}
|
||||||
onClick={handleClick}
|
onClick={handleClick}
|
||||||
|
|||||||
@@ -44,6 +44,10 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.contentCut {
|
||||||
|
opacity: 0.5;
|
||||||
|
}
|
||||||
|
|
||||||
.contentDisabled {
|
.contentDisabled {
|
||||||
filter: saturate(0.5);
|
filter: saturate(0.5);
|
||||||
opacity: 0.64;
|
opacity: 0.64;
|
||||||
|
|||||||
@@ -60,6 +60,8 @@ const CardActionsStep = React.memo(({ cardId, defaultStep, onNameEdit, onClose }
|
|||||||
canEditName,
|
canEditName,
|
||||||
canEditDueDate,
|
canEditDueDate,
|
||||||
canEditStopwatch,
|
canEditStopwatch,
|
||||||
|
canCopy,
|
||||||
|
canCut,
|
||||||
canDuplicate,
|
canDuplicate,
|
||||||
canMove,
|
canMove,
|
||||||
canRestore,
|
canRestore,
|
||||||
@@ -68,6 +70,8 @@ const CardActionsStep = React.memo(({ cardId, defaultStep, onNameEdit, onClose }
|
|||||||
canUseMembers,
|
canUseMembers,
|
||||||
canUseLabels,
|
canUseLabels,
|
||||||
} = useSelector((state) => {
|
} = useSelector((state) => {
|
||||||
|
const isManager = selectors.selectIsCurrentUserManagerForCurrentProject(state);
|
||||||
|
|
||||||
const boardMembership = selectors.selectCurrentUserMembershipForCurrentBoard(state);
|
const boardMembership = selectors.selectCurrentUserMembershipForCurrentBoard(state);
|
||||||
const isEditor = !!boardMembership && boardMembership.role === BoardMembershipRoles.EDITOR;
|
const isEditor = !!boardMembership && boardMembership.role === BoardMembershipRoles.EDITOR;
|
||||||
|
|
||||||
@@ -77,6 +81,8 @@ const CardActionsStep = React.memo(({ cardId, defaultStep, onNameEdit, onClose }
|
|||||||
canEditName: false,
|
canEditName: false,
|
||||||
canEditDueDate: false,
|
canEditDueDate: false,
|
||||||
canEditStopwatch: false,
|
canEditStopwatch: false,
|
||||||
|
canCopy: isManager || isEditor,
|
||||||
|
canCut: isEditor,
|
||||||
canDuplicate: false,
|
canDuplicate: false,
|
||||||
canMove: false,
|
canMove: false,
|
||||||
canRestore: isEditor,
|
canRestore: isEditor,
|
||||||
@@ -92,6 +98,8 @@ const CardActionsStep = React.memo(({ cardId, defaultStep, onNameEdit, onClose }
|
|||||||
canEditName: isEditor,
|
canEditName: isEditor,
|
||||||
canEditDueDate: isEditor,
|
canEditDueDate: isEditor,
|
||||||
canEditStopwatch: isEditor,
|
canEditStopwatch: isEditor,
|
||||||
|
canCopy: isManager || isEditor,
|
||||||
|
canCut: isEditor,
|
||||||
canDuplicate: isEditor,
|
canDuplicate: isEditor,
|
||||||
canMove: isEditor,
|
canMove: isEditor,
|
||||||
canRestore: null,
|
canRestore: null,
|
||||||
@@ -117,17 +125,20 @@ const CardActionsStep = React.memo(({ cardId, defaultStep, onNameEdit, onClose }
|
|||||||
[cardId, dispatch],
|
[cardId, dispatch],
|
||||||
);
|
);
|
||||||
|
|
||||||
const handleDuplicateClick = useCallback(() => {
|
const handleCopyClick = useCallback(() => {
|
||||||
dispatch(
|
dispatch(entryActions.copyCard(cardId));
|
||||||
entryActions.duplicateCard(cardId, {
|
|
||||||
name: `${card.name} (${t('common.copy', {
|
|
||||||
context: 'inline',
|
|
||||||
})})`,
|
|
||||||
}),
|
|
||||||
);
|
|
||||||
|
|
||||||
onClose();
|
onClose();
|
||||||
}, [cardId, onClose, card.name, dispatch, t]);
|
}, [cardId, onClose, dispatch]);
|
||||||
|
|
||||||
|
const handleCutClick = useCallback(() => {
|
||||||
|
dispatch(entryActions.cutCard(cardId));
|
||||||
|
onClose();
|
||||||
|
}, [cardId, onClose, dispatch]);
|
||||||
|
|
||||||
|
const handleDuplicateClick = useCallback(() => {
|
||||||
|
dispatch(entryActions.duplicateCard(cardId));
|
||||||
|
onClose();
|
||||||
|
}, [cardId, onClose, dispatch]);
|
||||||
|
|
||||||
const handleRestoreClick = useCallback(() => {
|
const handleRestoreClick = useCallback(() => {
|
||||||
dispatch(entryActions.moveCard(cardId, card.prevListId, undefined, true));
|
dispatch(entryActions.moveCard(cardId, card.prevListId, undefined, true));
|
||||||
@@ -344,6 +355,22 @@ const CardActionsStep = React.memo(({ cardId, defaultStep, onNameEdit, onClose }
|
|||||||
})}
|
})}
|
||||||
</Menu.Item>
|
</Menu.Item>
|
||||||
)}
|
)}
|
||||||
|
{canCopy && (
|
||||||
|
<Menu.Item className={styles.menuItem} onClick={handleCopyClick}>
|
||||||
|
<Icon name="copy outline" className={styles.menuItemIcon} />
|
||||||
|
{t('action.copyCard', {
|
||||||
|
context: 'title',
|
||||||
|
})}
|
||||||
|
</Menu.Item>
|
||||||
|
)}
|
||||||
|
{canCut && (
|
||||||
|
<Menu.Item className={styles.menuItem} onClick={handleCutClick}>
|
||||||
|
<Icon name="cut" className={styles.menuItemIcon} />
|
||||||
|
{t('action.cutCard', {
|
||||||
|
context: 'title',
|
||||||
|
})}
|
||||||
|
</Menu.Item>
|
||||||
|
)}
|
||||||
{canDuplicate && (
|
{canDuplicate && (
|
||||||
<Menu.Item className={styles.menuItem} onClick={handleDuplicateClick}>
|
<Menu.Item className={styles.menuItem} onClick={handleDuplicateClick}>
|
||||||
<Icon name="copy outline" className={styles.menuItemIcon} />
|
<Icon name="copy outline" className={styles.menuItemIcon} />
|
||||||
|
|||||||
@@ -34,17 +34,17 @@ const MoreActionsStep = React.memo(({ onClose }) => {
|
|||||||
const { canEditType, canDuplicate, canMove } = useSelector((state) => {
|
const { canEditType, canDuplicate, canMove } = useSelector((state) => {
|
||||||
const list = selectListById(state, card.listId);
|
const list = selectListById(state, card.listId);
|
||||||
|
|
||||||
|
const boardMembership = selectors.selectCurrentUserMembershipForCurrentBoard(state);
|
||||||
|
const isEditor = !!boardMembership && boardMembership.role === BoardMembershipRoles.EDITOR;
|
||||||
|
|
||||||
if (isListArchiveOrTrash(list)) {
|
if (isListArchiveOrTrash(list)) {
|
||||||
return {
|
return {
|
||||||
canEditType: false,
|
canEditType: false,
|
||||||
canDuplicate: false,
|
canDuplicate: false,
|
||||||
canMove: false,
|
canMove: isEditor,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
const boardMembership = selectors.selectCurrentUserMembershipForCurrentBoard(state);
|
|
||||||
const isEditor = !!boardMembership && boardMembership.role === BoardMembershipRoles.EDITOR;
|
|
||||||
|
|
||||||
return {
|
return {
|
||||||
canEditType: isEditor,
|
canEditType: isEditor,
|
||||||
canDuplicate: isEditor,
|
canDuplicate: isEditor,
|
||||||
@@ -68,14 +68,8 @@ const MoreActionsStep = React.memo(({ onClose }) => {
|
|||||||
);
|
);
|
||||||
|
|
||||||
const handleDuplicateClick = useCallback(() => {
|
const handleDuplicateClick = useCallback(() => {
|
||||||
dispatch(
|
dispatch(entryActions.duplicateCurrentCard());
|
||||||
entryActions.duplicateCurrentCard({
|
}, [dispatch]);
|
||||||
name: `${card.name} (${t('common.copy', {
|
|
||||||
context: 'inline',
|
|
||||||
})})`,
|
|
||||||
}),
|
|
||||||
);
|
|
||||||
}, [card.name, dispatch, t]);
|
|
||||||
|
|
||||||
const handleEditTypeClick = useCallback(() => {
|
const handleEditTypeClick = useCallback(() => {
|
||||||
openStep(StepTypes.EDIT_TYPE);
|
openStep(StepTypes.EDIT_TYPE);
|
||||||
|
|||||||
@@ -105,7 +105,7 @@ const ProjectContent = React.memo(() => {
|
|||||||
canSubscribe: isMember,
|
canSubscribe: isMember,
|
||||||
canJoin: false,
|
canJoin: false,
|
||||||
canDuplicate: false,
|
canDuplicate: false,
|
||||||
canMove: false,
|
canMove: isEditor,
|
||||||
canRestore: isEditor,
|
canRestore: isEditor,
|
||||||
canArchive: isEditor,
|
canArchive: isEditor,
|
||||||
canDelete: isEditor,
|
canDelete: isEditor,
|
||||||
|
|||||||
@@ -104,7 +104,7 @@ const StoryContent = React.memo(() => {
|
|||||||
canSubscribe: isMember,
|
canSubscribe: isMember,
|
||||||
canJoin: false,
|
canJoin: false,
|
||||||
canDuplicate: false,
|
canDuplicate: false,
|
||||||
canMove: false,
|
canMove: isEditor,
|
||||||
canRestore: isEditor,
|
canRestore: isEditor,
|
||||||
canArchive: isEditor,
|
canArchive: isEditor,
|
||||||
canDelete: isEditor,
|
canDelete: isEditor,
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ import React from 'react';
|
|||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
import { Icon, Message } from 'semantic-ui-react';
|
import { Icon, Message } from 'semantic-ui-react';
|
||||||
|
|
||||||
const FileIsTooBig = React.memo(() => {
|
const FileIsTooBigToast = React.memo(() => {
|
||||||
const [t] = useTranslation();
|
const [t] = useTranslation();
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@@ -18,4 +18,4 @@ const FileIsTooBig = React.memo(() => {
|
|||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
export default FileIsTooBig;
|
export default FileIsTooBigToast;
|
||||||
@@ -7,7 +7,7 @@ import React from 'react';
|
|||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
import { Icon, Message } from 'semantic-ui-react';
|
import { Icon, Message } from 'semantic-ui-react';
|
||||||
|
|
||||||
const NotEnoughStorage = React.memo(() => {
|
const NotEnoughStorageToast = React.memo(() => {
|
||||||
const [t] = useTranslation();
|
const [t] = useTranslation();
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@@ -18,4 +18,4 @@ const NotEnoughStorage = React.memo(() => {
|
|||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
export default NotEnoughStorage;
|
export default NotEnoughStorageToast;
|
||||||
@@ -0,0 +1,21 @@
|
|||||||
|
/*!
|
||||||
|
* Copyright (c) 2024 PLANKA Software GmbH
|
||||||
|
* Licensed under the Fair Use License: https://github.com/plankanban/planka/blob/master/LICENSE.md
|
||||||
|
*/
|
||||||
|
|
||||||
|
import React from 'react';
|
||||||
|
import { useTranslation } from 'react-i18next';
|
||||||
|
import { Icon, Message } from 'semantic-ui-react';
|
||||||
|
|
||||||
|
const SourceCardNotCopyableToast = React.memo(() => {
|
||||||
|
const [t] = useTranslation();
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Message visible negative size="tiny">
|
||||||
|
<Icon name="paste" />
|
||||||
|
{t('common.sourceCardIsNoLongerAvailableForCopying')}
|
||||||
|
</Message>
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
export default SourceCardNotCopyableToast;
|
||||||
@@ -0,0 +1,21 @@
|
|||||||
|
/*!
|
||||||
|
* Copyright (c) 2024 PLANKA Software GmbH
|
||||||
|
* Licensed under the Fair Use License: https://github.com/plankanban/planka/blob/master/LICENSE.md
|
||||||
|
*/
|
||||||
|
|
||||||
|
import React from 'react';
|
||||||
|
import { useTranslation } from 'react-i18next';
|
||||||
|
import { Icon, Message } from 'semantic-ui-react';
|
||||||
|
|
||||||
|
const SourceCardNotMovableToast = React.memo(() => {
|
||||||
|
const [t] = useTranslation();
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Message visible negative size="tiny">
|
||||||
|
<Icon name="paste" />
|
||||||
|
{t('common.sourceCardIsNoLongerAvailableForMoving')}
|
||||||
|
</Message>
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
export default SourceCardNotMovableToast;
|
||||||
@@ -7,14 +7,18 @@ import React from 'react';
|
|||||||
import { Toaster as HotToaster, ToastBar as HotToastBar } from 'react-hot-toast';
|
import { Toaster as HotToaster, ToastBar as HotToastBar } from 'react-hot-toast';
|
||||||
|
|
||||||
import ToastTypes from '../../../constants/ToastTypes';
|
import ToastTypes from '../../../constants/ToastTypes';
|
||||||
import FileIsTooBig from './FileIsTooBig';
|
import FileIsTooBigToast from './FileIsTooBigToast';
|
||||||
import NotEnoughStorage from './NotEnoughStorage';
|
import NotEnoughStorageToast from './NotEnoughStorageToast';
|
||||||
import EmptyTrashToast from './EmptyTrashToast';
|
import EmptyTrashToast from './EmptyTrashToast';
|
||||||
|
import SourceCardNotCopyableToast from './SourceCardNotCopyableToast';
|
||||||
|
import SourceCardNotMovableToast from './SourceCardNotMovableToast';
|
||||||
|
|
||||||
const TOAST_BY_TYPE = {
|
const TOAST_BY_TYPE = {
|
||||||
[ToastTypes.FILE_IS_TOO_BIG]: FileIsTooBig,
|
[ToastTypes.FILE_IS_TOO_BIG]: FileIsTooBigToast,
|
||||||
[ToastTypes.NOT_ENOUGH_STORAGE]: NotEnoughStorage,
|
[ToastTypes.NOT_ENOUGH_STORAGE]: NotEnoughStorageToast,
|
||||||
[ToastTypes.EMPTY_TRASH]: EmptyTrashToast,
|
[ToastTypes.EMPTY_TRASH]: EmptyTrashToast,
|
||||||
|
[ToastTypes.SOURCE_CARD_NOT_COPYABLE]: SourceCardNotCopyableToast,
|
||||||
|
[ToastTypes.SOURCE_CARD_NOT_MOVABLE]: SourceCardNotMovableToast,
|
||||||
};
|
};
|
||||||
|
|
||||||
const Toaster = React.memo(() => (
|
const Toaster = React.memo(() => (
|
||||||
|
|||||||
@@ -5,18 +5,19 @@
|
|||||||
|
|
||||||
import upperFirst from 'lodash/upperFirst';
|
import upperFirst from 'lodash/upperFirst';
|
||||||
import camelCase from 'lodash/camelCase';
|
import camelCase from 'lodash/camelCase';
|
||||||
import React, { useCallback, useMemo, useRef, useState } from 'react';
|
import React, { useCallback, useContext, useMemo, useRef, useState } from 'react';
|
||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
import classNames from 'classnames';
|
import classNames from 'classnames';
|
||||||
import { shallowEqual, useDispatch, useSelector } from 'react-redux';
|
import { shallowEqual, useDispatch, useSelector } from 'react-redux';
|
||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
import { Draggable, Droppable } from 'react-beautiful-dnd';
|
import { Draggable, Droppable } from 'react-beautiful-dnd';
|
||||||
import { Button, Icon } from 'semantic-ui-react';
|
import { Button, Icon } from 'semantic-ui-react';
|
||||||
import { useDidUpdate, useTransitioning } from '../../../lib/hooks';
|
import { useDidUpdate, useToggle, useTransitioning } from '../../../lib/hooks';
|
||||||
import { usePopup } from '../../../lib/popup';
|
import { usePopup } from '../../../lib/popup';
|
||||||
|
|
||||||
import selectors from '../../../selectors';
|
import selectors from '../../../selectors';
|
||||||
import entryActions from '../../../entry-actions';
|
import entryActions from '../../../entry-actions';
|
||||||
|
import { BoardShortcutsContext } from '../../../contexts';
|
||||||
import DroppableTypes from '../../../constants/DroppableTypes';
|
import DroppableTypes from '../../../constants/DroppableTypes';
|
||||||
import { BoardMembershipRoles, ListTypes } from '../../../constants/Enums';
|
import { BoardMembershipRoles, ListTypes } from '../../../constants/Enums';
|
||||||
import { ListTypeIcons } from '../../../constants/Icons';
|
import { ListTypeIcons } from '../../../constants/Icons';
|
||||||
@@ -47,28 +48,36 @@ const List = React.memo(({ id, index }) => {
|
|||||||
[],
|
[],
|
||||||
);
|
);
|
||||||
|
|
||||||
|
const clipboard = useSelector(selectors.selectClipboard);
|
||||||
const isFavoritesActive = useSelector(selectors.selectIsFavoritesActiveForCurrentUser);
|
const isFavoritesActive = useSelector(selectors.selectIsFavoritesActiveForCurrentUser);
|
||||||
|
|
||||||
const list = useSelector((state) => selectListById(state, id));
|
const list = useSelector((state) => selectListById(state, id));
|
||||||
const cardIds = useSelector((state) => selectFilteredCardIdsByListId(state, id));
|
const cardIds = useSelector((state) => selectFilteredCardIdsByListId(state, id));
|
||||||
|
|
||||||
const { canEdit, canArchiveCards, canAddCard, canDropCard } = useSelector((state) => {
|
const { canEdit, canArchiveCards, canAddCard, canPasteCard, canDropCard } = useSelector(
|
||||||
const isEditModeEnabled = selectors.selectIsEditModeEnabled(state); // TODO: move out?
|
(state) => {
|
||||||
|
const isEditModeEnabled = selectors.selectIsEditModeEnabled(state); // TODO: move out?
|
||||||
|
|
||||||
const boardMembership = selectors.selectCurrentUserMembershipForCurrentBoard(state);
|
const boardMembership = selectors.selectCurrentUserMembershipForCurrentBoard(state);
|
||||||
const isEditor = !!boardMembership && boardMembership.role === BoardMembershipRoles.EDITOR;
|
const isEditor = !!boardMembership && boardMembership.role === BoardMembershipRoles.EDITOR;
|
||||||
|
|
||||||
return {
|
return {
|
||||||
canEdit: isEditModeEnabled && isEditor,
|
canEdit: isEditModeEnabled && isEditor,
|
||||||
canArchiveCards: list.type === ListTypes.CLOSED && isEditor,
|
canArchiveCards: list.type === ListTypes.CLOSED && isEditor,
|
||||||
canAddCard: isEditor,
|
canAddCard: isEditor,
|
||||||
canDropCard: isEditor,
|
canPasteCard: isEditor,
|
||||||
};
|
canDropCard: isEditor,
|
||||||
}, shallowEqual);
|
};
|
||||||
|
},
|
||||||
|
shallowEqual,
|
||||||
|
);
|
||||||
|
|
||||||
const dispatch = useDispatch();
|
const dispatch = useDispatch();
|
||||||
const [t] = useTranslation();
|
const [t] = useTranslation();
|
||||||
const [isEditNameOpened, setIsEditNameOpened] = useState(false);
|
const [isEditNameOpened, setIsEditNameOpened] = useState(false);
|
||||||
const [addCardPosition, setAddCardPosition] = useState(null);
|
const [addCardPosition, setAddCardPosition] = useState(null);
|
||||||
|
const [scrollBottomState, scrollBottom] = useToggle();
|
||||||
|
const [handleListMouseEnter, handleListMouseLeave] = useContext(BoardShortcutsContext);
|
||||||
|
|
||||||
const wrapperRef = useRef(null);
|
const wrapperRef = useRef(null);
|
||||||
const cardsWrapperRef = useRef(null);
|
const cardsWrapperRef = useRef(null);
|
||||||
@@ -82,6 +91,17 @@ const List = React.memo(({ id, index }) => {
|
|||||||
[id, dispatch, addCardPosition],
|
[id, dispatch, addCardPosition],
|
||||||
);
|
);
|
||||||
|
|
||||||
|
const handlePasteCardClick = useCallback(() => {
|
||||||
|
dispatch(entryActions.pasteCard(id));
|
||||||
|
scrollBottom();
|
||||||
|
}, [id, dispatch, scrollBottom]);
|
||||||
|
|
||||||
|
const handleMouseEnter = useCallback(() => {
|
||||||
|
handleListMouseEnter(id, () => {
|
||||||
|
scrollBottom();
|
||||||
|
});
|
||||||
|
}, [id, scrollBottom, handleListMouseEnter]);
|
||||||
|
|
||||||
const handleHeaderClick = useCallback(() => {
|
const handleHeaderClick = useCallback(() => {
|
||||||
if (list.isPersisted && canEdit) {
|
if (list.isPersisted && canEdit) {
|
||||||
setIsEditNameOpened(true);
|
setIsEditNameOpened(true);
|
||||||
@@ -123,6 +143,10 @@ const List = React.memo(({ id, index }) => {
|
|||||||
addCardPosition === AddCardPositions.TOP ? 0 : cardsWrapperRef.current.scrollHeight;
|
addCardPosition === AddCardPositions.TOP ? 0 : cardsWrapperRef.current.scrollHeight;
|
||||||
}, [cardIds, addCardPosition]);
|
}, [cardIds, addCardPosition]);
|
||||||
|
|
||||||
|
useDidUpdate(() => {
|
||||||
|
cardsWrapperRef.current.scrollTop = cardsWrapperRef.current.scrollHeight;
|
||||||
|
}, [scrollBottomState]);
|
||||||
|
|
||||||
const ActionsPopup = usePopup(ActionsStep);
|
const ActionsPopup = usePopup(ActionsStep);
|
||||||
const ArchiveCardsPopup = usePopup(ArchiveCardsStep);
|
const ArchiveCardsPopup = usePopup(ArchiveCardsStep);
|
||||||
|
|
||||||
@@ -169,6 +193,8 @@ const List = React.memo(({ id, index }) => {
|
|||||||
data-drag-scroller
|
data-drag-scroller
|
||||||
ref={innerRef}
|
ref={innerRef}
|
||||||
className={styles.innerWrapper}
|
className={styles.innerWrapper}
|
||||||
|
onMouseEnter={handleMouseEnter}
|
||||||
|
onMouseLeave={handleListMouseLeave}
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
ref={wrapperRef}
|
ref={wrapperRef}
|
||||||
@@ -231,17 +257,29 @@ const List = React.memo(({ id, index }) => {
|
|||||||
<div className={styles.cardsOuterWrapper}>{cardsNode}</div>
|
<div className={styles.cardsOuterWrapper}>{cardsNode}</div>
|
||||||
</div>
|
</div>
|
||||||
{!addCardPosition && canAddCard && (
|
{!addCardPosition && canAddCard && (
|
||||||
<button
|
<div className={styles.addCardButtonWrapper}>
|
||||||
type="button"
|
<button
|
||||||
disabled={!list.isPersisted}
|
type="button"
|
||||||
className={styles.addCardButton}
|
disabled={!list.isPersisted}
|
||||||
onClick={handleAddCardClick}
|
className={styles.addCardButton}
|
||||||
>
|
onClick={handleAddCardClick}
|
||||||
<PlusMathIcon className={styles.addCardButtonIcon} />
|
>
|
||||||
<span className={styles.addCardButtonText}>
|
<PlusMathIcon className={styles.addCardButtonIcon} />
|
||||||
{cardIds.length > 0 ? t('action.addAnotherCard') : t('action.addCard')}
|
<span className={styles.addCardButtonText}>
|
||||||
</span>
|
{cardIds.length > 0 ? t('action.addAnotherCard') : t('action.addCard')}
|
||||||
</button>
|
</span>
|
||||||
|
</button>
|
||||||
|
{clipboard && canPasteCard && (
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
disabled={!list.isPersisted}
|
||||||
|
className={classNames(styles.addCardButton, styles.paste)}
|
||||||
|
onClick={handlePasteCardClick}
|
||||||
|
>
|
||||||
|
<Icon name="paste" />
|
||||||
|
</button>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -15,19 +15,22 @@
|
|||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
display: block;
|
display: block;
|
||||||
fill: #6b808c;
|
fill: #6b808c;
|
||||||
flex: 0 0 auto;
|
flex: 1;
|
||||||
font-weight: normal;
|
font-weight: normal;
|
||||||
height: 36px;
|
height: 36px;
|
||||||
outline: none;
|
outline: none;
|
||||||
padding: 8px;
|
padding: 8px;
|
||||||
text-align: left;
|
text-align: left;
|
||||||
width: 100%;
|
|
||||||
|
|
||||||
&:hover {
|
&:hover {
|
||||||
background: #c3cbd0;
|
background: #c3cbd0;
|
||||||
color: #17394d;
|
color: #17394d;
|
||||||
fill: #17394d;
|
fill: #17394d;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
&.paste {
|
||||||
|
flex: 0 0 auto;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.addCardButtonIcon {
|
.addCardButtonIcon {
|
||||||
@@ -44,6 +47,10 @@
|
|||||||
vertical-align: top;
|
vertical-align: top;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.addCardButtonWrapper {
|
||||||
|
display: flex;
|
||||||
|
}
|
||||||
|
|
||||||
.card {
|
.card {
|
||||||
margin-bottom: 8px;
|
margin-bottom: 8px;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -290,6 +290,9 @@ export default {
|
|||||||
CARD_DUPLICATE: 'CARD_DUPLICATE',
|
CARD_DUPLICATE: 'CARD_DUPLICATE',
|
||||||
CARD_DUPLICATE__SUCCESS: 'CARD_DUPLICATE__SUCCESS',
|
CARD_DUPLICATE__SUCCESS: 'CARD_DUPLICATE__SUCCESS',
|
||||||
CARD_DUPLICATE__FAILURE: 'CARD_DUPLICATE__FAILURE',
|
CARD_DUPLICATE__FAILURE: 'CARD_DUPLICATE__FAILURE',
|
||||||
|
CARD_COPY: 'CARD_COPY',
|
||||||
|
CARD_CUT: 'CARD_CUT',
|
||||||
|
CARD_PASTE: 'CARD_PASTE',
|
||||||
CARD_DELETE: 'CARD_DELETE',
|
CARD_DELETE: 'CARD_DELETE',
|
||||||
CARD_DELETE__SUCCESS: 'CARD_DELETE__SUCCESS',
|
CARD_DELETE__SUCCESS: 'CARD_DELETE__SUCCESS',
|
||||||
CARD_DELETE__FAILURE: 'CARD_DELETE__FAILURE',
|
CARD_DELETE__FAILURE: 'CARD_DELETE__FAILURE',
|
||||||
|
|||||||
12
client/src/constants/ClipboardTypes.js
Normal file
12
client/src/constants/ClipboardTypes.js
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
/*!
|
||||||
|
* Copyright (c) 2024 PLANKA Software GmbH
|
||||||
|
* Licensed under the Fair Use License: https://github.com/plankanban/planka/blob/master/LICENSE.md
|
||||||
|
*/
|
||||||
|
|
||||||
|
const COPY = 'COPY';
|
||||||
|
const CUT = 'CUT';
|
||||||
|
|
||||||
|
export default {
|
||||||
|
COPY,
|
||||||
|
CUT,
|
||||||
|
};
|
||||||
@@ -202,6 +202,11 @@ export default {
|
|||||||
CURRENT_CARD_TRANSFER: `${PREFIX}/CURRENT_CARD_TRANSFER`,
|
CURRENT_CARD_TRANSFER: `${PREFIX}/CURRENT_CARD_TRANSFER`,
|
||||||
CARD_DUPLICATE: `${PREFIX}/CARD_DUPLICATE`,
|
CARD_DUPLICATE: `${PREFIX}/CARD_DUPLICATE`,
|
||||||
CURRENT_CARD_DUPLICATE: `${PREFIX}/CURRENT_CARD_DUPLICATE`,
|
CURRENT_CARD_DUPLICATE: `${PREFIX}/CURRENT_CARD_DUPLICATE`,
|
||||||
|
CARD_COPY: `${PREFIX}/CARD_COPY`,
|
||||||
|
CARD_CUT: `${PREFIX}/CARD_CUT`,
|
||||||
|
CARD_PASTE: `${PREFIX}/CARD_PASTE`,
|
||||||
|
CARD_IN_CURRENT_CONTEXT_PASTE: `${PREFIX}/CARD_IN_CURRENT_CONTEXT_PASTE`,
|
||||||
|
CARD_IN_CURRENT_LIST_PASTE: `${PREFIX}/CARD_IN_CURRENT_LIST_PASTE`,
|
||||||
TO_ADJACENT_CARD_GO: `${PREFIX}/TO_ADJACENT_CARD_GO`,
|
TO_ADJACENT_CARD_GO: `${PREFIX}/TO_ADJACENT_CARD_GO`,
|
||||||
CARD_DELETE: `${PREFIX}/CARD_DELETE`,
|
CARD_DELETE: `${PREFIX}/CARD_DELETE`,
|
||||||
CURRENT_CARD_DELETE: `${PREFIX}/CURRENT_CARD_DELETE`,
|
CURRENT_CARD_DELETE: `${PREFIX}/CURRENT_CARD_DELETE`,
|
||||||
|
|||||||
@@ -6,9 +6,13 @@
|
|||||||
const FILE_IS_TOO_BIG = 'FILE_IS_TOO_BIG';
|
const FILE_IS_TOO_BIG = 'FILE_IS_TOO_BIG';
|
||||||
const NOT_ENOUGH_STORAGE = 'NOT_ENOUGH_STORAGE';
|
const NOT_ENOUGH_STORAGE = 'NOT_ENOUGH_STORAGE';
|
||||||
const EMPTY_TRASH = 'EMPTY_TRASH';
|
const EMPTY_TRASH = 'EMPTY_TRASH';
|
||||||
|
const SOURCE_CARD_NOT_COPYABLE = 'SOURCE_CARD_NOT_COPYABLE';
|
||||||
|
const SOURCE_CARD_NOT_MOVABLE = 'SOURCE_CARD_NOT_MOVABLE';
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
FILE_IS_TOO_BIG,
|
FILE_IS_TOO_BIG,
|
||||||
NOT_ENOUGH_STORAGE,
|
NOT_ENOUGH_STORAGE,
|
||||||
EMPTY_TRASH,
|
EMPTY_TRASH,
|
||||||
|
SOURCE_CARD_NOT_COPYABLE,
|
||||||
|
SOURCE_CARD_NOT_MOVABLE,
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -5,4 +5,4 @@
|
|||||||
|
|
||||||
import { createContext } from 'react';
|
import { createContext } from 'react';
|
||||||
|
|
||||||
export default createContext([null, null]);
|
export default createContext([null, null, null, null]);
|
||||||
|
|||||||
@@ -135,7 +135,7 @@ const transferCurrentCard = (boardId, listId, index = 0) => ({
|
|||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
const duplicateCard = (id, data) => ({
|
const duplicateCard = (id, data = {}) => ({
|
||||||
type: EntryActionTypes.CARD_DUPLICATE,
|
type: EntryActionTypes.CARD_DUPLICATE,
|
||||||
payload: {
|
payload: {
|
||||||
id,
|
id,
|
||||||
@@ -143,13 +143,44 @@ const duplicateCard = (id, data) => ({
|
|||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
const duplicateCurrentCard = (data) => ({
|
const duplicateCurrentCard = (data = {}) => ({
|
||||||
type: EntryActionTypes.CURRENT_CARD_DUPLICATE,
|
type: EntryActionTypes.CURRENT_CARD_DUPLICATE,
|
||||||
payload: {
|
payload: {
|
||||||
data,
|
data,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const copyCard = (id) => ({
|
||||||
|
type: EntryActionTypes.CARD_COPY,
|
||||||
|
payload: {
|
||||||
|
id,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
const cutCard = (id) => ({
|
||||||
|
type: EntryActionTypes.CARD_CUT,
|
||||||
|
payload: {
|
||||||
|
id,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
const pasteCard = (listId) => ({
|
||||||
|
type: EntryActionTypes.CARD_PASTE,
|
||||||
|
payload: {
|
||||||
|
listId,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
const pasteCardInCurrentContext = () => ({
|
||||||
|
type: EntryActionTypes.CARD_IN_CURRENT_CONTEXT_PASTE,
|
||||||
|
payload: {},
|
||||||
|
});
|
||||||
|
|
||||||
|
const pasteCardInCurrentList = () => ({
|
||||||
|
type: EntryActionTypes.CARD_IN_CURRENT_LIST_PASTE,
|
||||||
|
payload: {},
|
||||||
|
});
|
||||||
|
|
||||||
const goToAdjacentCard = (direction) => ({
|
const goToAdjacentCard = (direction) => ({
|
||||||
type: EntryActionTypes.TO_ADJACENT_CARD_GO,
|
type: EntryActionTypes.TO_ADJACENT_CARD_GO,
|
||||||
payload: {
|
payload: {
|
||||||
@@ -196,6 +227,11 @@ export default {
|
|||||||
transferCurrentCard,
|
transferCurrentCard,
|
||||||
duplicateCard,
|
duplicateCard,
|
||||||
duplicateCurrentCard,
|
duplicateCurrentCard,
|
||||||
|
copyCard,
|
||||||
|
cutCard,
|
||||||
|
pasteCard,
|
||||||
|
pasteCardInCurrentContext,
|
||||||
|
pasteCardInCurrentList,
|
||||||
goToAdjacentCard,
|
goToAdjacentCard,
|
||||||
deleteCard,
|
deleteCard,
|
||||||
deleteCurrentCard,
|
deleteCurrentCard,
|
||||||
|
|||||||
@@ -298,6 +298,8 @@ export default {
|
|||||||
showOnFrontOfCard: 'عرض في مقدمة البطاقة',
|
showOnFrontOfCard: 'عرض في مقدمة البطاقة',
|
||||||
smtp: 'SMTP',
|
smtp: 'SMTP',
|
||||||
sortList_title: 'فرز القائمة',
|
sortList_title: 'فرز القائمة',
|
||||||
|
sourceCardIsNoLongerAvailableForCopying: 'البطاقة المصدر لم تعد متاحة للنسخ.',
|
||||||
|
sourceCardIsNoLongerAvailableForMoving: 'البطاقة المصدر لم تعد متاحة للنقل.',
|
||||||
stopwatch: 'المؤقت',
|
stopwatch: 'المؤقت',
|
||||||
story: 'القصة',
|
story: 'القصة',
|
||||||
subscribeToCardWhenCommenting: 'الاشتراك في البطاقة عند التعليق',
|
subscribeToCardWhenCommenting: 'الاشتراك في البطاقة عند التعليق',
|
||||||
@@ -384,6 +386,7 @@ export default {
|
|||||||
archiveCards_title: 'أرشفة البطاقات',
|
archiveCards_title: 'أرشفة البطاقات',
|
||||||
assignAsOwner: 'تعيين كمالك',
|
assignAsOwner: 'تعيين كمالك',
|
||||||
cancel: 'إلغاء',
|
cancel: 'إلغاء',
|
||||||
|
copyCard_title: 'نسخ البطاقة',
|
||||||
createApiKey: 'إنشاء مفتاح API',
|
createApiKey: 'إنشاء مفتاح API',
|
||||||
createBoard: 'إنشاء لوحة',
|
createBoard: 'إنشاء لوحة',
|
||||||
createCustomFieldGroup: 'إنشاء مجموعة حقل مخصص',
|
createCustomFieldGroup: 'إنشاء مجموعة حقل مخصص',
|
||||||
@@ -391,6 +394,7 @@ export default {
|
|||||||
createLabel: 'إنشاء ملصق',
|
createLabel: 'إنشاء ملصق',
|
||||||
createNewLabel: 'إنشاء ملصق جديد',
|
createNewLabel: 'إنشاء ملصق جديد',
|
||||||
createProject: 'إنشاء مشروع',
|
createProject: 'إنشاء مشروع',
|
||||||
|
cutCard_title: 'قص البطاقة',
|
||||||
deactivateUser: 'إلغاء تفعيل المستخدم',
|
deactivateUser: 'إلغاء تفعيل المستخدم',
|
||||||
deactivateUser_title: 'إلغاء تفعيل المستخدم',
|
deactivateUser_title: 'إلغاء تفعيل المستخدم',
|
||||||
delete: 'حذف',
|
delete: 'حذف',
|
||||||
|
|||||||
@@ -310,6 +310,9 @@ export default {
|
|||||||
showOnFrontOfCard: 'Показване отпред на картата',
|
showOnFrontOfCard: 'Показване отпред на картата',
|
||||||
smtp: 'SMTP',
|
smtp: 'SMTP',
|
||||||
sortList_title: 'Сортиране на списък',
|
sortList_title: 'Сортиране на списък',
|
||||||
|
sourceCardIsNoLongerAvailableForCopying: 'Оригиналната карта вече не е налична за копиране.',
|
||||||
|
sourceCardIsNoLongerAvailableForMoving:
|
||||||
|
'Оригиналната карта вече не е налична за преместване.',
|
||||||
stopwatch: 'Хронометър',
|
stopwatch: 'Хронометър',
|
||||||
story: 'История',
|
story: 'История',
|
||||||
subscribeToCardWhenCommenting: 'Абониране за карта при коментиране',
|
subscribeToCardWhenCommenting: 'Абониране за карта при коментиране',
|
||||||
@@ -398,6 +401,7 @@ export default {
|
|||||||
archiveCards_title: 'Архивиране на карти',
|
archiveCards_title: 'Архивиране на карти',
|
||||||
assignAsOwner: 'Назначаване като собственик',
|
assignAsOwner: 'Назначаване като собственик',
|
||||||
cancel: 'Отказ',
|
cancel: 'Отказ',
|
||||||
|
copyCard_title: 'Копиране на карта',
|
||||||
createApiKey: 'Създаване на API ключ',
|
createApiKey: 'Създаване на API ключ',
|
||||||
createBoard: 'Създаване на табло',
|
createBoard: 'Създаване на табло',
|
||||||
createCustomFieldGroup: 'Създаване на група персонализирани полета',
|
createCustomFieldGroup: 'Създаване на група персонализирани полета',
|
||||||
@@ -405,6 +409,7 @@ export default {
|
|||||||
createLabel: 'Създаване на етикет',
|
createLabel: 'Създаване на етикет',
|
||||||
createNewLabel: 'Създаване на нов етикет',
|
createNewLabel: 'Създаване на нов етикет',
|
||||||
createProject: 'Създаване на проект',
|
createProject: 'Създаване на проект',
|
||||||
|
cutCard_title: 'Изрязване на карта',
|
||||||
deactivateUser: 'Деактивиране на потребител',
|
deactivateUser: 'Деактивиране на потребител',
|
||||||
deactivateUser_title: 'Деактивиране на потребител',
|
deactivateUser_title: 'Деактивиране на потребител',
|
||||||
delete: 'Изтриване',
|
delete: 'Изтриване',
|
||||||
|
|||||||
@@ -309,6 +309,10 @@ export default {
|
|||||||
showOnFrontOfCard: 'Mostrar a la part frontal de la targeta',
|
showOnFrontOfCard: 'Mostrar a la part frontal de la targeta',
|
||||||
smtp: 'SMTP',
|
smtp: 'SMTP',
|
||||||
sortList_title: 'Ordenar llista',
|
sortList_title: 'Ordenar llista',
|
||||||
|
sourceCardIsNoLongerAvailableForCopying:
|
||||||
|
"La targeta d'origen ja no està disponible per copiar.",
|
||||||
|
sourceCardIsNoLongerAvailableForMoving:
|
||||||
|
"La targeta d'origen ja no està disponible per moure.",
|
||||||
stopwatch: 'Cronòmetre',
|
stopwatch: 'Cronòmetre',
|
||||||
story: 'Història',
|
story: 'Història',
|
||||||
subscribeToCardWhenCommenting: "Subscriure's a la targeta en comentar",
|
subscribeToCardWhenCommenting: "Subscriure's a la targeta en comentar",
|
||||||
@@ -399,6 +403,7 @@ export default {
|
|||||||
archiveCards_title: 'Arxivar targetes',
|
archiveCards_title: 'Arxivar targetes',
|
||||||
assignAsOwner: 'Assignar com a propietari',
|
assignAsOwner: 'Assignar com a propietari',
|
||||||
cancel: 'Cancel·lar',
|
cancel: 'Cancel·lar',
|
||||||
|
copyCard_title: 'Copiar targeta',
|
||||||
createApiKey: 'Crear clau API',
|
createApiKey: 'Crear clau API',
|
||||||
createBoard: 'Crear tauler',
|
createBoard: 'Crear tauler',
|
||||||
createCustomFieldGroup: 'Crear grup de camps personalitzats',
|
createCustomFieldGroup: 'Crear grup de camps personalitzats',
|
||||||
@@ -406,6 +411,7 @@ export default {
|
|||||||
createLabel: 'Crear etiqueta',
|
createLabel: 'Crear etiqueta',
|
||||||
createNewLabel: 'Crear nova etiqueta',
|
createNewLabel: 'Crear nova etiqueta',
|
||||||
createProject: 'Crear projecte',
|
createProject: 'Crear projecte',
|
||||||
|
cutCard_title: 'Tallar targeta',
|
||||||
deactivateUser: 'Desactivar usuari',
|
deactivateUser: 'Desactivar usuari',
|
||||||
deactivateUser_title: 'Desactivar usuari',
|
deactivateUser_title: 'Desactivar usuari',
|
||||||
delete: 'Eliminar',
|
delete: 'Eliminar',
|
||||||
|
|||||||
@@ -301,6 +301,9 @@ export default {
|
|||||||
showOnFrontOfCard: 'Zobrazit na přední straně karty',
|
showOnFrontOfCard: 'Zobrazit na přední straně karty',
|
||||||
smtp: 'SMTP',
|
smtp: 'SMTP',
|
||||||
sortList_title: 'Řadit podle',
|
sortList_title: 'Řadit podle',
|
||||||
|
sourceCardIsNoLongerAvailableForCopying:
|
||||||
|
'Zdrojová karta již není k dispozici pro kopírování.',
|
||||||
|
sourceCardIsNoLongerAvailableForMoving: 'Zdrojová karta již není k dispozici pro přesunutí.',
|
||||||
stopwatch: 'Časovač',
|
stopwatch: 'Časovač',
|
||||||
story: 'Příběh',
|
story: 'Příběh',
|
||||||
subscribeToCardWhenCommenting: 'Odebírat karty při komentování',
|
subscribeToCardWhenCommenting: 'Odebírat karty při komentování',
|
||||||
@@ -388,6 +391,7 @@ export default {
|
|||||||
archiveCards_title: 'Archiv karet',
|
archiveCards_title: 'Archiv karet',
|
||||||
assignAsOwner: 'Přiřadit jako vlastníka',
|
assignAsOwner: 'Přiřadit jako vlastníka',
|
||||||
cancel: 'Zrušit',
|
cancel: 'Zrušit',
|
||||||
|
copyCard_title: 'Kopírovat kartu',
|
||||||
createApiKey: 'Vytvořit API klíč',
|
createApiKey: 'Vytvořit API klíč',
|
||||||
createBoard: 'Vytvořit nástěnku',
|
createBoard: 'Vytvořit nástěnku',
|
||||||
createCustomFieldGroup: 'Vytvořit vlastní skupinu polí',
|
createCustomFieldGroup: 'Vytvořit vlastní skupinu polí',
|
||||||
@@ -395,6 +399,7 @@ export default {
|
|||||||
createLabel: 'Vytvořit štítek',
|
createLabel: 'Vytvořit štítek',
|
||||||
createNewLabel: 'Vytvořit nový štítek',
|
createNewLabel: 'Vytvořit nový štítek',
|
||||||
createProject: 'Vytvořit projekt',
|
createProject: 'Vytvořit projekt',
|
||||||
|
cutCard_title: 'Vyjmout kartu',
|
||||||
deactivateUser: 'Deaktivace uživatele',
|
deactivateUser: 'Deaktivace uživatele',
|
||||||
deactivateUser_title: 'Deaktivace uživatele',
|
deactivateUser_title: 'Deaktivace uživatele',
|
||||||
delete: 'Smazat',
|
delete: 'Smazat',
|
||||||
|
|||||||
@@ -305,6 +305,10 @@ export default {
|
|||||||
showOnFrontOfCard: 'Vis på forsiden af kortet',
|
showOnFrontOfCard: 'Vis på forsiden af kortet',
|
||||||
smtp: 'SMTP',
|
smtp: 'SMTP',
|
||||||
sortList_title: 'Sortér liste',
|
sortList_title: 'Sortér liste',
|
||||||
|
sourceCardIsNoLongerAvailableForCopying:
|
||||||
|
'Kildekortet er ikke længere tilgængeligt til kopiering.',
|
||||||
|
sourceCardIsNoLongerAvailableForMoving:
|
||||||
|
'Kildekortet er ikke længere tilgængeligt til flytning.',
|
||||||
stopwatch: 'Stopur',
|
stopwatch: 'Stopur',
|
||||||
story: 'Story',
|
story: 'Story',
|
||||||
subscribeToCardWhenCommenting: 'Abonnér på kort ved kommentering',
|
subscribeToCardWhenCommenting: 'Abonnér på kort ved kommentering',
|
||||||
@@ -393,6 +397,7 @@ export default {
|
|||||||
archiveCards_title: 'Arkivér kort',
|
archiveCards_title: 'Arkivér kort',
|
||||||
assignAsOwner: 'Sæt som ejer',
|
assignAsOwner: 'Sæt som ejer',
|
||||||
cancel: 'Annuller',
|
cancel: 'Annuller',
|
||||||
|
copyCard_title: 'Kopiér kort',
|
||||||
createApiKey: 'Opret API-nøgle',
|
createApiKey: 'Opret API-nøgle',
|
||||||
createBoard: 'Opret tavle',
|
createBoard: 'Opret tavle',
|
||||||
createCustomFieldGroup: 'Opret brugerdefineret feltgruppe',
|
createCustomFieldGroup: 'Opret brugerdefineret feltgruppe',
|
||||||
@@ -400,6 +405,7 @@ export default {
|
|||||||
createLabel: 'Opret label',
|
createLabel: 'Opret label',
|
||||||
createNewLabel: 'Opret ny label',
|
createNewLabel: 'Opret ny label',
|
||||||
createProject: 'Opret projekt',
|
createProject: 'Opret projekt',
|
||||||
|
cutCard_title: 'Klip kort',
|
||||||
deactivateUser: 'Deaktivér bruger',
|
deactivateUser: 'Deaktivér bruger',
|
||||||
deactivateUser_title: 'Deaktivér bruger',
|
deactivateUser_title: 'Deaktivér bruger',
|
||||||
delete: 'Slet',
|
delete: 'Slet',
|
||||||
|
|||||||
@@ -321,6 +321,9 @@ export default {
|
|||||||
showOnFrontOfCard: 'Auf der Vorderseite der Karte anzeigen',
|
showOnFrontOfCard: 'Auf der Vorderseite der Karte anzeigen',
|
||||||
smtp: 'SMTP',
|
smtp: 'SMTP',
|
||||||
sortList_title: 'Liste sortieren',
|
sortList_title: 'Liste sortieren',
|
||||||
|
sourceCardIsNoLongerAvailableForCopying: 'Quellkarte ist nicht mehr zum Kopieren verfügbar.',
|
||||||
|
sourceCardIsNoLongerAvailableForMoving:
|
||||||
|
'Quellkarte ist nicht mehr zum Verschieben verfügbar.',
|
||||||
stopwatch: 'Stoppuhr',
|
stopwatch: 'Stoppuhr',
|
||||||
story: 'Wissen',
|
story: 'Wissen',
|
||||||
subscribeToCardWhenCommenting: 'Karte beim Kommentieren abonnieren',
|
subscribeToCardWhenCommenting: 'Karte beim Kommentieren abonnieren',
|
||||||
@@ -410,6 +413,7 @@ export default {
|
|||||||
archiveCards_title: 'Karten archivieren',
|
archiveCards_title: 'Karten archivieren',
|
||||||
assignAsOwner: 'Als Eigentümer zuweisen',
|
assignAsOwner: 'Als Eigentümer zuweisen',
|
||||||
cancel: 'Abbrechen',
|
cancel: 'Abbrechen',
|
||||||
|
copyCard_title: 'Karte Kopieren',
|
||||||
createApiKey: 'API-Schlüssel erstellen',
|
createApiKey: 'API-Schlüssel erstellen',
|
||||||
createBoard: 'Arbeitsbereich erstellen',
|
createBoard: 'Arbeitsbereich erstellen',
|
||||||
createCustomFieldGroup: 'Feldgruppe erstellen',
|
createCustomFieldGroup: 'Feldgruppe erstellen',
|
||||||
@@ -417,6 +421,7 @@ export default {
|
|||||||
createLabel: 'Label erstellen',
|
createLabel: 'Label erstellen',
|
||||||
createNewLabel: 'Neues Label erstellen',
|
createNewLabel: 'Neues Label erstellen',
|
||||||
createProject: 'Projekt erstellen',
|
createProject: 'Projekt erstellen',
|
||||||
|
cutCard_title: 'Karte Ausschneiden',
|
||||||
deactivateUser: 'Benutzer deaktivieren',
|
deactivateUser: 'Benutzer deaktivieren',
|
||||||
deactivateUser_title: 'Benutzer deaktivieren',
|
deactivateUser_title: 'Benutzer deaktivieren',
|
||||||
delete: 'Löschen',
|
delete: 'Löschen',
|
||||||
|
|||||||
@@ -318,6 +318,10 @@ export default {
|
|||||||
showOnFrontOfCard: 'Εμφάνιση στο μπροστινό μέρος της κάρτας',
|
showOnFrontOfCard: 'Εμφάνιση στο μπροστινό μέρος της κάρτας',
|
||||||
smtp: 'SMTP',
|
smtp: 'SMTP',
|
||||||
sortList_title: 'Ταξινόμηση λίστας',
|
sortList_title: 'Ταξινόμηση λίστας',
|
||||||
|
sourceCardIsNoLongerAvailableForCopying:
|
||||||
|
'Η κάρτα πηγής δεν είναι πλέον διαθέσιμη για αντιγραφή.',
|
||||||
|
sourceCardIsNoLongerAvailableForMoving:
|
||||||
|
'Η κάρτα πηγής δεν είναι πλέον διαθέσιμη για μετακίνηση.',
|
||||||
stopwatch: 'Χρονόμετρο',
|
stopwatch: 'Χρονόμετρο',
|
||||||
story: 'Ιστορία',
|
story: 'Ιστορία',
|
||||||
subscribeToCardWhenCommenting: 'Εγγραφή στην κάρτα κατά τη σχολιασμό',
|
subscribeToCardWhenCommenting: 'Εγγραφή στην κάρτα κατά τη σχολιασμό',
|
||||||
@@ -413,6 +417,7 @@ export default {
|
|||||||
archiveCards_title: 'Αρχειοθέτηση καρτών',
|
archiveCards_title: 'Αρχειοθέτηση καρτών',
|
||||||
assignAsOwner: 'Ορισμός ως ιδιοκτήτης',
|
assignAsOwner: 'Ορισμός ως ιδιοκτήτης',
|
||||||
cancel: 'Ακύρωση',
|
cancel: 'Ακύρωση',
|
||||||
|
copyCard_title: 'Αντιγραφή κάρτας',
|
||||||
createApiKey: 'Δημιουργία κλειδιού API',
|
createApiKey: 'Δημιουργία κλειδιού API',
|
||||||
createBoard: 'Δημιουργία πίνακα',
|
createBoard: 'Δημιουργία πίνακα',
|
||||||
createCustomFieldGroup: 'Δημιουργία ομάδας προσαρμοσμένων πεδίων',
|
createCustomFieldGroup: 'Δημιουργία ομάδας προσαρμοσμένων πεδίων',
|
||||||
@@ -420,6 +425,7 @@ export default {
|
|||||||
createLabel: 'Δημιουργία ετικέτας',
|
createLabel: 'Δημιουργία ετικέτας',
|
||||||
createNewLabel: 'Δημιουργία νέας ετικέτας',
|
createNewLabel: 'Δημιουργία νέας ετικέτας',
|
||||||
createProject: 'Δημιουργία έργου',
|
createProject: 'Δημιουργία έργου',
|
||||||
|
cutCard_title: 'Αποκοπή κάρτας',
|
||||||
deactivateUser: 'Απενεργοποίηση χρήστη',
|
deactivateUser: 'Απενεργοποίηση χρήστη',
|
||||||
deactivateUser_title: 'Απενεργοποίηση χρήστη',
|
deactivateUser_title: 'Απενεργοποίηση χρήστη',
|
||||||
delete: 'Διαγραφή',
|
delete: 'Διαγραφή',
|
||||||
|
|||||||
@@ -304,6 +304,8 @@ export default {
|
|||||||
showOnFrontOfCard: 'Show on front of card',
|
showOnFrontOfCard: 'Show on front of card',
|
||||||
smtp: 'SMTP',
|
smtp: 'SMTP',
|
||||||
sortList_title: 'Sort List',
|
sortList_title: 'Sort List',
|
||||||
|
sourceCardIsNoLongerAvailableForCopying: 'Source card is no longer available for copying.',
|
||||||
|
sourceCardIsNoLongerAvailableForMoving: 'Source card is no longer available for moving.',
|
||||||
stopwatch: 'Stopwatch',
|
stopwatch: 'Stopwatch',
|
||||||
story: 'Story',
|
story: 'Story',
|
||||||
subscribeToCardWhenCommenting: 'Subscribe to card when commenting',
|
subscribeToCardWhenCommenting: 'Subscribe to card when commenting',
|
||||||
@@ -391,6 +393,7 @@ export default {
|
|||||||
archiveCards_title: 'Archive Cards',
|
archiveCards_title: 'Archive Cards',
|
||||||
assignAsOwner: 'Assign as owner',
|
assignAsOwner: 'Assign as owner',
|
||||||
cancel: 'Cancel',
|
cancel: 'Cancel',
|
||||||
|
copyCard_title: 'Copy Card',
|
||||||
createApiKey: 'Create API key',
|
createApiKey: 'Create API key',
|
||||||
createBoard: 'Create board',
|
createBoard: 'Create board',
|
||||||
createCustomFieldGroup: 'Create custom field group',
|
createCustomFieldGroup: 'Create custom field group',
|
||||||
@@ -398,6 +401,7 @@ export default {
|
|||||||
createLabel: 'Create label',
|
createLabel: 'Create label',
|
||||||
createNewLabel: 'Create new label',
|
createNewLabel: 'Create new label',
|
||||||
createProject: 'Create project',
|
createProject: 'Create project',
|
||||||
|
cutCard_title: 'Cut Card',
|
||||||
deactivateUser: 'Deactivate user',
|
deactivateUser: 'Deactivate user',
|
||||||
deactivateUser_title: 'Deactivate User',
|
deactivateUser_title: 'Deactivate User',
|
||||||
delete: 'Delete',
|
delete: 'Delete',
|
||||||
|
|||||||
@@ -299,6 +299,8 @@ export default {
|
|||||||
showOnFrontOfCard: 'Show on front of card',
|
showOnFrontOfCard: 'Show on front of card',
|
||||||
smtp: 'SMTP',
|
smtp: 'SMTP',
|
||||||
sortList_title: 'Sort List',
|
sortList_title: 'Sort List',
|
||||||
|
sourceCardIsNoLongerAvailableForCopying: 'Source card is no longer available for copying.',
|
||||||
|
sourceCardIsNoLongerAvailableForMoving: 'Source card is no longer available for moving.',
|
||||||
stopwatch: 'Stopwatch',
|
stopwatch: 'Stopwatch',
|
||||||
story: 'Story',
|
story: 'Story',
|
||||||
subscribeToCardWhenCommenting: 'Subscribe to card when commenting',
|
subscribeToCardWhenCommenting: 'Subscribe to card when commenting',
|
||||||
@@ -386,6 +388,7 @@ export default {
|
|||||||
archiveCards_title: 'Archive Cards',
|
archiveCards_title: 'Archive Cards',
|
||||||
assignAsOwner: 'Assign as owner',
|
assignAsOwner: 'Assign as owner',
|
||||||
cancel: 'Cancel',
|
cancel: 'Cancel',
|
||||||
|
copyCard_title: 'Copy Card',
|
||||||
createApiKey: 'Create API key',
|
createApiKey: 'Create API key',
|
||||||
createBoard: 'Create board',
|
createBoard: 'Create board',
|
||||||
createCustomFieldGroup: 'Create custom field group',
|
createCustomFieldGroup: 'Create custom field group',
|
||||||
@@ -393,6 +396,7 @@ export default {
|
|||||||
createLabel: 'Create label',
|
createLabel: 'Create label',
|
||||||
createNewLabel: 'Create new label',
|
createNewLabel: 'Create new label',
|
||||||
createProject: 'Create project',
|
createProject: 'Create project',
|
||||||
|
cutCard_title: 'Cut Card',
|
||||||
deactivateUser: 'Deactivate user',
|
deactivateUser: 'Deactivate user',
|
||||||
deactivateUser_title: 'Deactivate User',
|
deactivateUser_title: 'Deactivate User',
|
||||||
delete: 'Delete',
|
delete: 'Delete',
|
||||||
|
|||||||
@@ -310,6 +310,10 @@ export default {
|
|||||||
showOnFrontOfCard: 'Mostrar en el frente de la tarjeta',
|
showOnFrontOfCard: 'Mostrar en el frente de la tarjeta',
|
||||||
smtp: 'SMTP',
|
smtp: 'SMTP',
|
||||||
sortList_title: 'Ordenar lista',
|
sortList_title: 'Ordenar lista',
|
||||||
|
sourceCardIsNoLongerAvailableForCopying:
|
||||||
|
'La tarjeta de origen ya no está disponible para copiar.',
|
||||||
|
sourceCardIsNoLongerAvailableForMoving:
|
||||||
|
'La tarjeta de origen ya no está disponible para mover.',
|
||||||
stopwatch: 'Cronómetro',
|
stopwatch: 'Cronómetro',
|
||||||
story: 'Historia',
|
story: 'Historia',
|
||||||
subscribeToCardWhenCommenting: 'Suscribirse a la tarjeta al comentar',
|
subscribeToCardWhenCommenting: 'Suscribirse a la tarjeta al comentar',
|
||||||
@@ -399,6 +403,7 @@ export default {
|
|||||||
archiveCards_title: 'Archivar tarjetas',
|
archiveCards_title: 'Archivar tarjetas',
|
||||||
assignAsOwner: 'Asignar como propietario',
|
assignAsOwner: 'Asignar como propietario',
|
||||||
cancel: 'Cancelar',
|
cancel: 'Cancelar',
|
||||||
|
copyCard_title: 'Copiar tarjeta',
|
||||||
createApiKey: 'Crear clave API',
|
createApiKey: 'Crear clave API',
|
||||||
createBoard: 'Crear tablero',
|
createBoard: 'Crear tablero',
|
||||||
createCustomFieldGroup: 'Crear grupo de campos personalizados',
|
createCustomFieldGroup: 'Crear grupo de campos personalizados',
|
||||||
@@ -406,6 +411,7 @@ export default {
|
|||||||
createLabel: 'Crear etiqueta',
|
createLabel: 'Crear etiqueta',
|
||||||
createNewLabel: 'Crear nueva etiqueta',
|
createNewLabel: 'Crear nueva etiqueta',
|
||||||
createProject: 'Crear proyecto',
|
createProject: 'Crear proyecto',
|
||||||
|
cutCard_title: 'Cortar tarjeta',
|
||||||
deactivateUser: 'Desactivar usuario',
|
deactivateUser: 'Desactivar usuario',
|
||||||
deactivateUser_title: 'Desactivar usuario',
|
deactivateUser_title: 'Desactivar usuario',
|
||||||
delete: 'Eliminar',
|
delete: 'Eliminar',
|
||||||
|
|||||||
@@ -305,6 +305,8 @@ export default {
|
|||||||
showOnFrontOfCard: 'Kuva kaardi ees',
|
showOnFrontOfCard: 'Kuva kaardi ees',
|
||||||
smtp: 'SMTP',
|
smtp: 'SMTP',
|
||||||
sortList_title: 'Nimekiri sorteerimine',
|
sortList_title: 'Nimekiri sorteerimine',
|
||||||
|
sourceCardIsNoLongerAvailableForCopying: 'Lähtekaart ei ole enam kopeerimiseks saadaval.',
|
||||||
|
sourceCardIsNoLongerAvailableForMoving: 'Lähtekaart ei ole enam liigutamiseks saadaval.',
|
||||||
stopwatch: 'Stopper',
|
stopwatch: 'Stopper',
|
||||||
story: 'Kirjeldus',
|
story: 'Kirjeldus',
|
||||||
subscribeToCardWhenCommenting: 'Telli kaart, kui kommenteerida',
|
subscribeToCardWhenCommenting: 'Telli kaart, kui kommenteerida',
|
||||||
@@ -393,6 +395,7 @@ export default {
|
|||||||
archiveCards_title: 'Arhiveeri kaardid',
|
archiveCards_title: 'Arhiveeri kaardid',
|
||||||
assignAsOwner: 'Määra omanikuks',
|
assignAsOwner: 'Määra omanikuks',
|
||||||
cancel: 'Tühista',
|
cancel: 'Tühista',
|
||||||
|
copyCard_title: 'Kopeeri kaart',
|
||||||
createApiKey: 'Loo API võti',
|
createApiKey: 'Loo API võti',
|
||||||
createBoard: 'Loo tahvel',
|
createBoard: 'Loo tahvel',
|
||||||
createCustomFieldGroup: 'Loo kohandatud väljade grupp',
|
createCustomFieldGroup: 'Loo kohandatud väljade grupp',
|
||||||
@@ -400,6 +403,7 @@ export default {
|
|||||||
createLabel: 'Loo silt',
|
createLabel: 'Loo silt',
|
||||||
createNewLabel: 'Loo uus silt',
|
createNewLabel: 'Loo uus silt',
|
||||||
createProject: 'Loo projekt',
|
createProject: 'Loo projekt',
|
||||||
|
cutCard_title: 'Lõika kaart',
|
||||||
deactivateUser: 'Deaktiveeri kasutaja',
|
deactivateUser: 'Deaktiveeri kasutaja',
|
||||||
deactivateUser_title: 'Deaktiveeri kasutaja',
|
deactivateUser_title: 'Deaktiveeri kasutaja',
|
||||||
delete: 'Kustuta',
|
delete: 'Kustuta',
|
||||||
|
|||||||
@@ -308,6 +308,8 @@ export default {
|
|||||||
showOnFrontOfCard: 'نمایش در جلوی کارت',
|
showOnFrontOfCard: 'نمایش در جلوی کارت',
|
||||||
smtp: 'SMTP',
|
smtp: 'SMTP',
|
||||||
sortList_title: 'مرتبسازی لیست',
|
sortList_title: 'مرتبسازی لیست',
|
||||||
|
sourceCardIsNoLongerAvailableForCopying: 'کارت مبدا دیگر برای کپی کردن در دسترس نیست.',
|
||||||
|
sourceCardIsNoLongerAvailableForMoving: 'کارت مبدا دیگر برای انتقال در دسترس نیست.',
|
||||||
stopwatch: 'کرنومتر',
|
stopwatch: 'کرنومتر',
|
||||||
story: 'داستان',
|
story: 'داستان',
|
||||||
subscribeToCardWhenCommenting: 'هنگام نظر دادن به کارت مشترک شو',
|
subscribeToCardWhenCommenting: 'هنگام نظر دادن به کارت مشترک شو',
|
||||||
@@ -395,6 +397,7 @@ export default {
|
|||||||
archiveCards_title: 'آرشیو کارتها',
|
archiveCards_title: 'آرشیو کارتها',
|
||||||
assignAsOwner: 'تعیین به عنوان مالک',
|
assignAsOwner: 'تعیین به عنوان مالک',
|
||||||
cancel: 'لغو',
|
cancel: 'لغو',
|
||||||
|
copyCard_title: 'کپی کارت',
|
||||||
createApiKey: 'ایجاد کلید API',
|
createApiKey: 'ایجاد کلید API',
|
||||||
createBoard: 'ایجاد برد',
|
createBoard: 'ایجاد برد',
|
||||||
createCustomFieldGroup: 'ایجاد گروه فیلد سفارشی',
|
createCustomFieldGroup: 'ایجاد گروه فیلد سفارشی',
|
||||||
@@ -402,6 +405,7 @@ export default {
|
|||||||
createLabel: 'ایجاد برچسب',
|
createLabel: 'ایجاد برچسب',
|
||||||
createNewLabel: 'ایجاد برچسب جدید',
|
createNewLabel: 'ایجاد برچسب جدید',
|
||||||
createProject: 'ایجاد پروژه',
|
createProject: 'ایجاد پروژه',
|
||||||
|
cutCard_title: 'برش کارت',
|
||||||
deactivateUser: 'غیرفعال کردن کاربر',
|
deactivateUser: 'غیرفعال کردن کاربر',
|
||||||
deactivateUser_title: 'غیرفعال کردن کاربر',
|
deactivateUser_title: 'غیرفعال کردن کاربر',
|
||||||
delete: 'حذف',
|
delete: 'حذف',
|
||||||
|
|||||||
@@ -301,6 +301,10 @@ export default {
|
|||||||
showOnFrontOfCard: 'Näytä kortin etupuolella',
|
showOnFrontOfCard: 'Näytä kortin etupuolella',
|
||||||
smtp: 'SMTP',
|
smtp: 'SMTP',
|
||||||
sortList_title: 'Lajittele lista',
|
sortList_title: 'Lajittele lista',
|
||||||
|
sourceCardIsNoLongerAvailableForCopying:
|
||||||
|
'Lähdekortti ei ole enää saatavilla kopiointia varten.',
|
||||||
|
sourceCardIsNoLongerAvailableForMoving:
|
||||||
|
'Lähdekortti ei ole enää saatavilla siirtämistä varten.',
|
||||||
stopwatch: 'Ajastin',
|
stopwatch: 'Ajastin',
|
||||||
story: 'Tarina',
|
story: 'Tarina',
|
||||||
subscribeToCardWhenCommenting: 'Tilaa kortti kommentoidessa',
|
subscribeToCardWhenCommenting: 'Tilaa kortti kommentoidessa',
|
||||||
@@ -392,6 +396,7 @@ export default {
|
|||||||
archiveCards_title: 'Arkistoi kortit',
|
archiveCards_title: 'Arkistoi kortit',
|
||||||
assignAsOwner: 'Aseta omistajaksi',
|
assignAsOwner: 'Aseta omistajaksi',
|
||||||
cancel: 'Peruuta',
|
cancel: 'Peruuta',
|
||||||
|
copyCard_title: 'Kopioi kortti',
|
||||||
createApiKey: 'Luo API-avain',
|
createApiKey: 'Luo API-avain',
|
||||||
createBoard: 'Luo taulu',
|
createBoard: 'Luo taulu',
|
||||||
createCustomFieldGroup: 'Luo mukautettujen kenttien ryhmä',
|
createCustomFieldGroup: 'Luo mukautettujen kenttien ryhmä',
|
||||||
@@ -399,6 +404,7 @@ export default {
|
|||||||
createLabel: 'Luo tunniste',
|
createLabel: 'Luo tunniste',
|
||||||
createNewLabel: 'Luo uusi tunniste',
|
createNewLabel: 'Luo uusi tunniste',
|
||||||
createProject: 'Luo projekti',
|
createProject: 'Luo projekti',
|
||||||
|
cutCard_title: 'Leikkaa kortti',
|
||||||
deactivateUser: 'Poista käyttäjä käytöstä',
|
deactivateUser: 'Poista käyttäjä käytöstä',
|
||||||
deactivateUser_title: 'Poista käyttäjä käytöstä',
|
deactivateUser_title: 'Poista käyttäjä käytöstä',
|
||||||
delete: 'Poista',
|
delete: 'Poista',
|
||||||
|
|||||||
@@ -309,6 +309,10 @@ export default {
|
|||||||
showOnFrontOfCard: 'Afficher sur le devant de la carte',
|
showOnFrontOfCard: 'Afficher sur le devant de la carte',
|
||||||
smtp: 'SMTP',
|
smtp: 'SMTP',
|
||||||
sortList_title: 'Trier la liste',
|
sortList_title: 'Trier la liste',
|
||||||
|
sourceCardIsNoLongerAvailableForCopying:
|
||||||
|
"La carte source n'est plus disponible pour la copie.",
|
||||||
|
sourceCardIsNoLongerAvailableForMoving:
|
||||||
|
"La carte source n'est plus disponible pour le déplacement.",
|
||||||
stopwatch: 'Minuteur',
|
stopwatch: 'Minuteur',
|
||||||
story: 'Story',
|
story: 'Story',
|
||||||
subscribeToCardWhenCommenting: 'S’abonner à la carte lors de la rédaction d’un commentaire',
|
subscribeToCardWhenCommenting: 'S’abonner à la carte lors de la rédaction d’un commentaire',
|
||||||
@@ -397,6 +401,7 @@ export default {
|
|||||||
archiveCards_title: 'Archiver les cartes',
|
archiveCards_title: 'Archiver les cartes',
|
||||||
assignAsOwner: 'Assigner comme propriétaire',
|
assignAsOwner: 'Assigner comme propriétaire',
|
||||||
cancel: 'Annuler',
|
cancel: 'Annuler',
|
||||||
|
copyCard_title: 'Copier la carte',
|
||||||
createApiKey: 'Créer une clé API',
|
createApiKey: 'Créer une clé API',
|
||||||
createBoard: 'Créer un tableau',
|
createBoard: 'Créer un tableau',
|
||||||
createCustomFieldGroup: 'Créer un groupe de champs personnalisés',
|
createCustomFieldGroup: 'Créer un groupe de champs personnalisés',
|
||||||
@@ -404,6 +409,7 @@ export default {
|
|||||||
createLabel: 'Créer une étiquette',
|
createLabel: 'Créer une étiquette',
|
||||||
createNewLabel: 'Créer une nouvelle étiquette',
|
createNewLabel: 'Créer une nouvelle étiquette',
|
||||||
createProject: 'Créer un projet',
|
createProject: 'Créer un projet',
|
||||||
|
cutCard_title: 'Couper la carte',
|
||||||
deactivateUser: 'Désactiver l’utilisateur',
|
deactivateUser: 'Désactiver l’utilisateur',
|
||||||
deactivateUser_title: 'Désactiver l’utilisateur',
|
deactivateUser_title: 'Désactiver l’utilisateur',
|
||||||
delete: 'Supprimer',
|
delete: 'Supprimer',
|
||||||
|
|||||||
@@ -299,6 +299,8 @@ export default {
|
|||||||
showOnFrontOfCard: 'Megjelenítés a kártya borítóján',
|
showOnFrontOfCard: 'Megjelenítés a kártya borítóján',
|
||||||
smtp: 'SMTP',
|
smtp: 'SMTP',
|
||||||
sortList_title: 'Rendezés listában',
|
sortList_title: 'Rendezés listában',
|
||||||
|
sourceCardIsNoLongerAvailableForCopying: 'A forráskártya már nem érhető el másoláshoz.',
|
||||||
|
sourceCardIsNoLongerAvailableForMoving: 'A forráskártya már nem érhető el áthelyezéshez.',
|
||||||
stopwatch: 'Stopper',
|
stopwatch: 'Stopper',
|
||||||
story: 'Story',
|
story: 'Story',
|
||||||
subscribeToCardWhenCommenting: 'Feliratkozás a kártyára kommenteléskor',
|
subscribeToCardWhenCommenting: 'Feliratkozás a kártyára kommenteléskor',
|
||||||
@@ -393,6 +395,7 @@ export default {
|
|||||||
archiveCards_title: 'Archív kártyák',
|
archiveCards_title: 'Archív kártyák',
|
||||||
assignAsOwner: 'Hozzárendelés tulajdonosnak',
|
assignAsOwner: 'Hozzárendelés tulajdonosnak',
|
||||||
cancel: 'Mégsem',
|
cancel: 'Mégsem',
|
||||||
|
copyCard_title: 'Kártya másolása',
|
||||||
createApiKey: 'API kulcs létrehozása',
|
createApiKey: 'API kulcs létrehozása',
|
||||||
createBoard: 'Tábla létrehozása',
|
createBoard: 'Tábla létrehozása',
|
||||||
createCustomFieldGroup: 'Egyedi mezőcsoport létrehozása',
|
createCustomFieldGroup: 'Egyedi mezőcsoport létrehozása',
|
||||||
@@ -400,6 +403,7 @@ export default {
|
|||||||
createLabel: 'Címke létrehozása',
|
createLabel: 'Címke létrehozása',
|
||||||
createNewLabel: 'Új címke létrehozása',
|
createNewLabel: 'Új címke létrehozása',
|
||||||
createProject: 'Projekt létrehozása',
|
createProject: 'Projekt létrehozása',
|
||||||
|
cutCard_title: 'Kártya kivágása',
|
||||||
deactivateUser: 'Felhasználó inaktiválása',
|
deactivateUser: 'Felhasználó inaktiválása',
|
||||||
deactivateUser_title: 'Felhasználó inaktiválása',
|
deactivateUser_title: 'Felhasználó inaktiválása',
|
||||||
delete: 'Törlés',
|
delete: 'Törlés',
|
||||||
|
|||||||
@@ -306,6 +306,8 @@ export default {
|
|||||||
showOnFrontOfCard: 'Tampilkan di depan kartu',
|
showOnFrontOfCard: 'Tampilkan di depan kartu',
|
||||||
smtp: 'SMTP',
|
smtp: 'SMTP',
|
||||||
sortList_title: 'Urutkan daftar',
|
sortList_title: 'Urutkan daftar',
|
||||||
|
sourceCardIsNoLongerAvailableForCopying: 'Kartu sumber tidak lagi tersedia untuk disalin.',
|
||||||
|
sourceCardIsNoLongerAvailableForMoving: 'Kartu sumber tidak lagi tersedia untuk dipindahkan.',
|
||||||
stopwatch: 'Stopwatch',
|
stopwatch: 'Stopwatch',
|
||||||
story: 'Cerita',
|
story: 'Cerita',
|
||||||
subscribeToCardWhenCommenting: 'Berlangganan kartu saat berkomentar',
|
subscribeToCardWhenCommenting: 'Berlangganan kartu saat berkomentar',
|
||||||
@@ -394,6 +396,7 @@ export default {
|
|||||||
archiveCards_title: 'Arsipkan kartu',
|
archiveCards_title: 'Arsipkan kartu',
|
||||||
assignAsOwner: 'Tetapkan sebagai pemilik',
|
assignAsOwner: 'Tetapkan sebagai pemilik',
|
||||||
cancel: 'Batal',
|
cancel: 'Batal',
|
||||||
|
copyCard_title: 'Salin Kartu',
|
||||||
createApiKey: 'Buat kunci API',
|
createApiKey: 'Buat kunci API',
|
||||||
createBoard: 'Tambah papan',
|
createBoard: 'Tambah papan',
|
||||||
createCustomFieldGroup: 'Buat grup bidang kustom',
|
createCustomFieldGroup: 'Buat grup bidang kustom',
|
||||||
@@ -401,6 +404,7 @@ export default {
|
|||||||
createLabel: 'Tambah label',
|
createLabel: 'Tambah label',
|
||||||
createNewLabel: 'Tambah label baru',
|
createNewLabel: 'Tambah label baru',
|
||||||
createProject: 'Tambah proyek',
|
createProject: 'Tambah proyek',
|
||||||
|
cutCard_title: 'Potong Kartu',
|
||||||
deactivateUser: 'Nonaktifkan pengguna',
|
deactivateUser: 'Nonaktifkan pengguna',
|
||||||
deactivateUser_title: 'Nonaktifkan pengguna',
|
deactivateUser_title: 'Nonaktifkan pengguna',
|
||||||
delete: 'Hapus',
|
delete: 'Hapus',
|
||||||
|
|||||||
@@ -306,6 +306,10 @@ export default {
|
|||||||
showOnFrontOfCard: 'Mostra davanti alla scheda',
|
showOnFrontOfCard: 'Mostra davanti alla scheda',
|
||||||
smtp: 'SMTP',
|
smtp: 'SMTP',
|
||||||
sortList_title: 'Ordina',
|
sortList_title: 'Ordina',
|
||||||
|
sourceCardIsNoLongerAvailableForCopying:
|
||||||
|
'La scheda sorgente non è più disponibile per la copia.',
|
||||||
|
sourceCardIsNoLongerAvailableForMoving:
|
||||||
|
'La scheda sorgente non è più disponibile per lo spostamento.',
|
||||||
stopwatch: 'Timer',
|
stopwatch: 'Timer',
|
||||||
story: 'Storia',
|
story: 'Storia',
|
||||||
subscribeToCardWhenCommenting: 'Iscrivimi alla scheda quando commento',
|
subscribeToCardWhenCommenting: 'Iscrivimi alla scheda quando commento',
|
||||||
@@ -395,6 +399,7 @@ export default {
|
|||||||
archiveCards_title: 'Archivia schede',
|
archiveCards_title: 'Archivia schede',
|
||||||
assignAsOwner: 'Assegna come proprietario',
|
assignAsOwner: 'Assegna come proprietario',
|
||||||
cancel: 'Annulla',
|
cancel: 'Annulla',
|
||||||
|
copyCard_title: 'Copia scheda',
|
||||||
createApiKey: 'Crea chiave API',
|
createApiKey: 'Crea chiave API',
|
||||||
createBoard: 'Crea bacheca',
|
createBoard: 'Crea bacheca',
|
||||||
createCustomFieldGroup: 'Crea campi personalizzati',
|
createCustomFieldGroup: 'Crea campi personalizzati',
|
||||||
@@ -402,6 +407,7 @@ export default {
|
|||||||
createLabel: 'Crea etichetta',
|
createLabel: 'Crea etichetta',
|
||||||
createNewLabel: 'Crea nuova etichetta',
|
createNewLabel: 'Crea nuova etichetta',
|
||||||
createProject: 'Crea progetto',
|
createProject: 'Crea progetto',
|
||||||
|
cutCard_title: 'Taglia scheda',
|
||||||
deactivateUser: 'Disattiva utente',
|
deactivateUser: 'Disattiva utente',
|
||||||
deactivateUser_title: 'Disattiva utente',
|
deactivateUser_title: 'Disattiva utente',
|
||||||
delete: 'Elimina',
|
delete: 'Elimina',
|
||||||
|
|||||||
@@ -302,6 +302,8 @@ export default {
|
|||||||
showOnFrontOfCard: 'カードの前面に表示',
|
showOnFrontOfCard: 'カードの前面に表示',
|
||||||
smtp: 'SMTP',
|
smtp: 'SMTP',
|
||||||
sortList_title: 'リストを並び替え',
|
sortList_title: 'リストを並び替え',
|
||||||
|
sourceCardIsNoLongerAvailableForCopying: 'ソースカードはコピーできなくなりました。',
|
||||||
|
sourceCardIsNoLongerAvailableForMoving: 'ソースカードは移動できなくなりました。',
|
||||||
stopwatch: 'タイマー',
|
stopwatch: 'タイマー',
|
||||||
story: 'ストーリー',
|
story: 'ストーリー',
|
||||||
subscribeToCardWhenCommenting: 'コメント時にカードを購読',
|
subscribeToCardWhenCommenting: 'コメント時にカードを購読',
|
||||||
@@ -391,6 +393,7 @@ export default {
|
|||||||
archiveCards_title: 'カードをアーカイブ',
|
archiveCards_title: 'カードをアーカイブ',
|
||||||
assignAsOwner: 'オーナーとして割り当て',
|
assignAsOwner: 'オーナーとして割り当て',
|
||||||
cancel: 'キャンセル',
|
cancel: 'キャンセル',
|
||||||
|
copyCard_title: 'カードをコピー',
|
||||||
createApiKey: 'APIキーを作成',
|
createApiKey: 'APIキーを作成',
|
||||||
createBoard: 'ボードを作成',
|
createBoard: 'ボードを作成',
|
||||||
createCustomFieldGroup: 'カスタムフィールドグループを作成',
|
createCustomFieldGroup: 'カスタムフィールドグループを作成',
|
||||||
@@ -398,6 +401,7 @@ export default {
|
|||||||
createLabel: 'ラベルを作成',
|
createLabel: 'ラベルを作成',
|
||||||
createNewLabel: '新しいラベルを作成',
|
createNewLabel: '新しいラベルを作成',
|
||||||
createProject: 'プロジェクトを作成',
|
createProject: 'プロジェクトを作成',
|
||||||
|
cutCard_title: 'カードを切り取り',
|
||||||
deactivateUser: 'ユーザーを非アクティブにする',
|
deactivateUser: 'ユーザーを非アクティブにする',
|
||||||
deactivateUser_title: 'ユーザーを非アクティブにする',
|
deactivateUser_title: 'ユーザーを非アクティブにする',
|
||||||
delete: '削除',
|
delete: '削除',
|
||||||
|
|||||||
@@ -296,6 +296,8 @@ export default {
|
|||||||
showOnFrontOfCard: '카드 앞면에 표시',
|
showOnFrontOfCard: '카드 앞면에 표시',
|
||||||
smtp: 'SMTP',
|
smtp: 'SMTP',
|
||||||
sortList_title: '목록 정렬',
|
sortList_title: '목록 정렬',
|
||||||
|
sourceCardIsNoLongerAvailableForCopying: '원본 카드를 더 이상 복사할 수 없습니다.',
|
||||||
|
sourceCardIsNoLongerAvailableForMoving: '원본 카드를 더 이상 이동할 수 없습니다.',
|
||||||
stopwatch: '스톱워치',
|
stopwatch: '스톱워치',
|
||||||
story: '스토리',
|
story: '스토리',
|
||||||
subscribeToCardWhenCommenting: '댓글 작성 시 카드 구독',
|
subscribeToCardWhenCommenting: '댓글 작성 시 카드 구독',
|
||||||
@@ -388,6 +390,7 @@ export default {
|
|||||||
archiveCards_title: '카드들 보관',
|
archiveCards_title: '카드들 보관',
|
||||||
assignAsOwner: '소유자로 지정',
|
assignAsOwner: '소유자로 지정',
|
||||||
cancel: '취소',
|
cancel: '취소',
|
||||||
|
copyCard_title: '카드 복사',
|
||||||
createApiKey: 'API 키 생성',
|
createApiKey: 'API 키 생성',
|
||||||
createBoard: '보드 생성',
|
createBoard: '보드 생성',
|
||||||
createCustomFieldGroup: '사용자 정의 필드 그룹 생성',
|
createCustomFieldGroup: '사용자 정의 필드 그룹 생성',
|
||||||
@@ -395,6 +398,7 @@ export default {
|
|||||||
createLabel: '라벨 생성',
|
createLabel: '라벨 생성',
|
||||||
createNewLabel: '새 라벨 생성',
|
createNewLabel: '새 라벨 생성',
|
||||||
createProject: '프로젝트 생성',
|
createProject: '프로젝트 생성',
|
||||||
|
cutCard_title: '카드 잘라내기',
|
||||||
deactivateUser: '사용자 비활성화',
|
deactivateUser: '사용자 비활성화',
|
||||||
deactivateUser_title: '사용자 비활성화',
|
deactivateUser_title: '사용자 비활성화',
|
||||||
delete: '삭제',
|
delete: '삭제',
|
||||||
|
|||||||
@@ -305,6 +305,9 @@ export default {
|
|||||||
showOnFrontOfCard: 'Tonen op voorkant van kaart',
|
showOnFrontOfCard: 'Tonen op voorkant van kaart',
|
||||||
smtp: 'SMTP',
|
smtp: 'SMTP',
|
||||||
sortList_title: 'Lijst sorteren',
|
sortList_title: 'Lijst sorteren',
|
||||||
|
sourceCardIsNoLongerAvailableForCopying: 'Bronkaart is niet meer beschikbaar voor kopiëren.',
|
||||||
|
sourceCardIsNoLongerAvailableForMoving:
|
||||||
|
'Bronkaart is niet meer beschikbaar voor verplaatsen.',
|
||||||
stopwatch: 'Stopwatch',
|
stopwatch: 'Stopwatch',
|
||||||
story: 'Verhaal',
|
story: 'Verhaal',
|
||||||
subscribeToCardWhenCommenting: 'Abonneren op kaart bij het plaatsen van commentaar',
|
subscribeToCardWhenCommenting: 'Abonneren op kaart bij het plaatsen van commentaar',
|
||||||
@@ -396,6 +399,7 @@ export default {
|
|||||||
archiveCards_title: 'Kaarten archiveren',
|
archiveCards_title: 'Kaarten archiveren',
|
||||||
assignAsOwner: 'Toewijzen als eigenaar',
|
assignAsOwner: 'Toewijzen als eigenaar',
|
||||||
cancel: 'Annuleren',
|
cancel: 'Annuleren',
|
||||||
|
copyCard_title: 'Kaart kopiëren',
|
||||||
createApiKey: 'API-sleutel aanmaken',
|
createApiKey: 'API-sleutel aanmaken',
|
||||||
createBoard: 'Bord aanmaken',
|
createBoard: 'Bord aanmaken',
|
||||||
createCustomFieldGroup: 'Aangepaste veldgroep aanmaken',
|
createCustomFieldGroup: 'Aangepaste veldgroep aanmaken',
|
||||||
@@ -403,6 +407,7 @@ export default {
|
|||||||
createLabel: 'Label aanmaken',
|
createLabel: 'Label aanmaken',
|
||||||
createNewLabel: 'Nieuw label aanmaken',
|
createNewLabel: 'Nieuw label aanmaken',
|
||||||
createProject: 'Project aanmaken',
|
createProject: 'Project aanmaken',
|
||||||
|
cutCard_title: 'Kaart knippen',
|
||||||
deactivateUser: 'Gebruiker deactiveren',
|
deactivateUser: 'Gebruiker deactiveren',
|
||||||
deactivateUser_title: 'Gebruiker deactiveren',
|
deactivateUser_title: 'Gebruiker deactiveren',
|
||||||
delete: 'Verwijderen',
|
delete: 'Verwijderen',
|
||||||
|
|||||||
@@ -304,6 +304,10 @@ export default {
|
|||||||
showOnFrontOfCard: 'Pokazuj na przodzie karty',
|
showOnFrontOfCard: 'Pokazuj na przodzie karty',
|
||||||
smtp: 'SMTP',
|
smtp: 'SMTP',
|
||||||
sortList_title: 'Sortowanie listy',
|
sortList_title: 'Sortowanie listy',
|
||||||
|
sourceCardIsNoLongerAvailableForCopying:
|
||||||
|
'Źródłowa karta nie jest już dostępna do skopiowania.',
|
||||||
|
sourceCardIsNoLongerAvailableForMoving:
|
||||||
|
'Źródłowa karta nie jest już dostępna do przeniesienia.',
|
||||||
stopwatch: 'Stoper',
|
stopwatch: 'Stoper',
|
||||||
story: 'Scenorys',
|
story: 'Scenorys',
|
||||||
subscribeToCardWhenCommenting: 'Subskrybuj kartę przy komentowaniu',
|
subscribeToCardWhenCommenting: 'Subskrybuj kartę przy komentowaniu',
|
||||||
@@ -392,6 +396,7 @@ export default {
|
|||||||
archiveCards_title: 'Archiwizuj karty',
|
archiveCards_title: 'Archiwizuj karty',
|
||||||
assignAsOwner: 'Przypisz jako właściciela',
|
assignAsOwner: 'Przypisz jako właściciela',
|
||||||
cancel: 'Anuluj',
|
cancel: 'Anuluj',
|
||||||
|
copyCard_title: 'Kopiuj kartę',
|
||||||
createApiKey: 'Utwórz klucz API',
|
createApiKey: 'Utwórz klucz API',
|
||||||
createBoard: 'Utwórz tablicę',
|
createBoard: 'Utwórz tablicę',
|
||||||
createCustomFieldGroup: 'Utwórz grupę pól własnych',
|
createCustomFieldGroup: 'Utwórz grupę pól własnych',
|
||||||
@@ -399,6 +404,7 @@ export default {
|
|||||||
createLabel: 'Utwórz oznaczenie',
|
createLabel: 'Utwórz oznaczenie',
|
||||||
createNewLabel: 'Utwórz nowe oznaczenie',
|
createNewLabel: 'Utwórz nowe oznaczenie',
|
||||||
createProject: 'Utwórz projekt',
|
createProject: 'Utwórz projekt',
|
||||||
|
cutCard_title: 'Wytnij kartę',
|
||||||
deactivateUser: 'Dezaktywuj użytkownika',
|
deactivateUser: 'Dezaktywuj użytkownika',
|
||||||
deactivateUser_title: 'Dezaktywuj użytkownika',
|
deactivateUser_title: 'Dezaktywuj użytkownika',
|
||||||
delete: 'Usuń',
|
delete: 'Usuń',
|
||||||
|
|||||||
@@ -307,6 +307,10 @@ export default {
|
|||||||
showOnFrontOfCard: 'Mostrar na frente do cartão',
|
showOnFrontOfCard: 'Mostrar na frente do cartão',
|
||||||
smtp: 'SMTP',
|
smtp: 'SMTP',
|
||||||
sortList_title: 'Ordenar lista',
|
sortList_title: 'Ordenar lista',
|
||||||
|
sourceCardIsNoLongerAvailableForCopying:
|
||||||
|
'O cartão de origem não está mais disponível para cópia.',
|
||||||
|
sourceCardIsNoLongerAvailableForMoving:
|
||||||
|
'O cartão de origem não está mais disponível para mover.',
|
||||||
stopwatch: 'Cronômetro',
|
stopwatch: 'Cronômetro',
|
||||||
story: 'História',
|
story: 'História',
|
||||||
subscribeToCardWhenCommenting: 'Inscrever-se no cartão ao comentar',
|
subscribeToCardWhenCommenting: 'Inscrever-se no cartão ao comentar',
|
||||||
@@ -395,6 +399,7 @@ export default {
|
|||||||
archiveCards_title: 'Arquivar cartões',
|
archiveCards_title: 'Arquivar cartões',
|
||||||
assignAsOwner: 'Atribuir como proprietário',
|
assignAsOwner: 'Atribuir como proprietário',
|
||||||
cancel: 'Cancelar',
|
cancel: 'Cancelar',
|
||||||
|
copyCard_title: 'Copiar cartão',
|
||||||
createApiKey: 'Criar chave API',
|
createApiKey: 'Criar chave API',
|
||||||
createBoard: 'Criar quadro',
|
createBoard: 'Criar quadro',
|
||||||
createCustomFieldGroup: 'Criar grupo de campos personalizados',
|
createCustomFieldGroup: 'Criar grupo de campos personalizados',
|
||||||
@@ -402,6 +407,7 @@ export default {
|
|||||||
createLabel: 'Criar rótulo',
|
createLabel: 'Criar rótulo',
|
||||||
createNewLabel: 'Criar novo rótulo',
|
createNewLabel: 'Criar novo rótulo',
|
||||||
createProject: 'Criar projeto',
|
createProject: 'Criar projeto',
|
||||||
|
cutCard_title: 'Cortar cartão',
|
||||||
deactivateUser: 'Desativar usuário',
|
deactivateUser: 'Desativar usuário',
|
||||||
deactivateUser_title: 'Desativar usuário',
|
deactivateUser_title: 'Desativar usuário',
|
||||||
delete: 'Excluir',
|
delete: 'Excluir',
|
||||||
|
|||||||
@@ -309,6 +309,10 @@ export default {
|
|||||||
showOnFrontOfCard: 'Mostrar na frente do cartão',
|
showOnFrontOfCard: 'Mostrar na frente do cartão',
|
||||||
smtp: 'SMTP',
|
smtp: 'SMTP',
|
||||||
sortList_title: 'Ordenar lista',
|
sortList_title: 'Ordenar lista',
|
||||||
|
sourceCardIsNoLongerAvailableForCopying:
|
||||||
|
'O cartão de origem já não está disponível para cópia.',
|
||||||
|
sourceCardIsNoLongerAvailableForMoving:
|
||||||
|
'O cartão de origem já não está disponível para mover.',
|
||||||
stopwatch: 'Cronómetro',
|
stopwatch: 'Cronómetro',
|
||||||
story: 'História',
|
story: 'História',
|
||||||
subscribeToCardWhenCommenting: 'Subscrever cartão ao comentar',
|
subscribeToCardWhenCommenting: 'Subscrever cartão ao comentar',
|
||||||
@@ -398,6 +402,7 @@ export default {
|
|||||||
archiveCards_title: 'Arquivar cartões',
|
archiveCards_title: 'Arquivar cartões',
|
||||||
assignAsOwner: 'Atribuir como proprietário',
|
assignAsOwner: 'Atribuir como proprietário',
|
||||||
cancel: 'Cancelar',
|
cancel: 'Cancelar',
|
||||||
|
copyCard_title: 'Copiar cartão',
|
||||||
createApiKey: 'Criar chave API',
|
createApiKey: 'Criar chave API',
|
||||||
createBoard: 'Criar quadro',
|
createBoard: 'Criar quadro',
|
||||||
createCustomFieldGroup: 'Criar grupo de campos personalizados',
|
createCustomFieldGroup: 'Criar grupo de campos personalizados',
|
||||||
@@ -405,6 +410,7 @@ export default {
|
|||||||
createLabel: 'Criar etiqueta',
|
createLabel: 'Criar etiqueta',
|
||||||
createNewLabel: 'Criar nova etiqueta',
|
createNewLabel: 'Criar nova etiqueta',
|
||||||
createProject: 'Criar projeto',
|
createProject: 'Criar projeto',
|
||||||
|
cutCard_title: 'Cortar cartão',
|
||||||
deactivateUser: 'Desativar utilizador',
|
deactivateUser: 'Desativar utilizador',
|
||||||
deactivateUser_title: 'Desativar utilizador',
|
deactivateUser_title: 'Desativar utilizador',
|
||||||
delete: 'Eliminar',
|
delete: 'Eliminar',
|
||||||
|
|||||||
@@ -303,6 +303,9 @@ export default {
|
|||||||
showOnFrontOfCard: 'Afișează pe fața cardului',
|
showOnFrontOfCard: 'Afișează pe fața cardului',
|
||||||
smtp: 'SMTP',
|
smtp: 'SMTP',
|
||||||
sortList_title: 'Sortează lista',
|
sortList_title: 'Sortează lista',
|
||||||
|
sourceCardIsNoLongerAvailableForCopying:
|
||||||
|
'Cardul sursă nu mai este disponibil pentru copiere.',
|
||||||
|
sourceCardIsNoLongerAvailableForMoving: 'Cardul sursă nu mai este disponibil pentru mutare.',
|
||||||
stopwatch: 'Cronometru',
|
stopwatch: 'Cronometru',
|
||||||
story: 'Poveste',
|
story: 'Poveste',
|
||||||
subscribeToCardWhenCommenting: 'Abonează-te la card când comentezi',
|
subscribeToCardWhenCommenting: 'Abonează-te la card când comentezi',
|
||||||
@@ -393,6 +396,7 @@ export default {
|
|||||||
archiveCards_title: 'Arhivează cardurile',
|
archiveCards_title: 'Arhivează cardurile',
|
||||||
assignAsOwner: 'Atribuie ca proprietar',
|
assignAsOwner: 'Atribuie ca proprietar',
|
||||||
cancel: 'Anulează',
|
cancel: 'Anulează',
|
||||||
|
copyCard_title: 'Copiază cardul',
|
||||||
createApiKey: 'Creează cheie API',
|
createApiKey: 'Creează cheie API',
|
||||||
createBoard: 'Creați tablă',
|
createBoard: 'Creați tablă',
|
||||||
createCustomFieldGroup: 'Creați grup de câmpuri personalizate',
|
createCustomFieldGroup: 'Creați grup de câmpuri personalizate',
|
||||||
@@ -400,6 +404,7 @@ export default {
|
|||||||
createLabel: 'Creați eticheta',
|
createLabel: 'Creați eticheta',
|
||||||
createNewLabel: 'Creați o nouă etichetă',
|
createNewLabel: 'Creați o nouă etichetă',
|
||||||
createProject: 'Creați proiect',
|
createProject: 'Creați proiect',
|
||||||
|
cutCard_title: 'Taie cardul',
|
||||||
deactivateUser: 'Dezactivați utilizatorul',
|
deactivateUser: 'Dezactivați utilizatorul',
|
||||||
deactivateUser_title: 'Dezactivați utilizatorul',
|
deactivateUser_title: 'Dezactivați utilizatorul',
|
||||||
delete: 'Ștergeți',
|
delete: 'Ștergeți',
|
||||||
|
|||||||
@@ -306,6 +306,10 @@ export default {
|
|||||||
showOnFrontOfCard: 'Показать на лицевой стороне карточки',
|
showOnFrontOfCard: 'Показать на лицевой стороне карточки',
|
||||||
smtp: 'SMTP',
|
smtp: 'SMTP',
|
||||||
sortList_title: 'Сортировка списка',
|
sortList_title: 'Сортировка списка',
|
||||||
|
sourceCardIsNoLongerAvailableForCopying:
|
||||||
|
'Исходная карточка больше не доступна для копирования.',
|
||||||
|
sourceCardIsNoLongerAvailableForMoving:
|
||||||
|
'Исходная карточка больше не доступна для перемещения.',
|
||||||
stopwatch: 'Секундомер',
|
stopwatch: 'Секундомер',
|
||||||
story: 'История',
|
story: 'История',
|
||||||
subscribeToCardWhenCommenting: 'Подписаться на карточку при комментировании',
|
subscribeToCardWhenCommenting: 'Подписаться на карточку при комментировании',
|
||||||
@@ -394,6 +398,7 @@ export default {
|
|||||||
archiveCards_title: 'Архивировать карточки',
|
archiveCards_title: 'Архивировать карточки',
|
||||||
assignAsOwner: 'Назначить владельцем',
|
assignAsOwner: 'Назначить владельцем',
|
||||||
cancel: 'Отменить',
|
cancel: 'Отменить',
|
||||||
|
copyCard_title: 'Копировать карточку',
|
||||||
createApiKey: 'Создать ключ API',
|
createApiKey: 'Создать ключ API',
|
||||||
createBoard: 'Создать доску',
|
createBoard: 'Создать доску',
|
||||||
createCustomFieldGroup: 'Создать группу настраиваемых полей',
|
createCustomFieldGroup: 'Создать группу настраиваемых полей',
|
||||||
@@ -401,6 +406,7 @@ export default {
|
|||||||
createLabel: 'Создать метку',
|
createLabel: 'Создать метку',
|
||||||
createNewLabel: 'Создать новую метку',
|
createNewLabel: 'Создать новую метку',
|
||||||
createProject: 'Создать проект',
|
createProject: 'Создать проект',
|
||||||
|
cutCard_title: 'Вырезать карточку',
|
||||||
deactivateUser: 'Деактивировать пользователя',
|
deactivateUser: 'Деактивировать пользователя',
|
||||||
deactivateUser_title: 'Деактивировать пользователя',
|
deactivateUser_title: 'Деактивировать пользователя',
|
||||||
delete: 'Удалить',
|
delete: 'Удалить',
|
||||||
|
|||||||
@@ -300,6 +300,8 @@ export default {
|
|||||||
showOnFrontOfCard: 'Zobraziť na prednej strane karty',
|
showOnFrontOfCard: 'Zobraziť na prednej strane karty',
|
||||||
smtp: 'SMTP',
|
smtp: 'SMTP',
|
||||||
sortList_title: 'Zoradiť zoznam',
|
sortList_title: 'Zoradiť zoznam',
|
||||||
|
sourceCardIsNoLongerAvailableForCopying: 'Zdrojová karta už nie je dostupná na kopírovanie.',
|
||||||
|
sourceCardIsNoLongerAvailableForMoving: 'Zdrojová karta už nie je dostupná na presunutie.',
|
||||||
stopwatch: 'Časovač',
|
stopwatch: 'Časovač',
|
||||||
story: 'Príbeh',
|
story: 'Príbeh',
|
||||||
subscribeToCardWhenCommenting: 'Odoberať kartu pri komentovaní',
|
subscribeToCardWhenCommenting: 'Odoberať kartu pri komentovaní',
|
||||||
@@ -387,6 +389,7 @@ export default {
|
|||||||
archiveCards_title: 'Archivovať karty',
|
archiveCards_title: 'Archivovať karty',
|
||||||
assignAsOwner: 'Prideliť ako vlastníka',
|
assignAsOwner: 'Prideliť ako vlastníka',
|
||||||
cancel: 'Zrušiť',
|
cancel: 'Zrušiť',
|
||||||
|
copyCard_title: 'Kopírovať kartu',
|
||||||
createApiKey: 'Vytvoriť API kľúč',
|
createApiKey: 'Vytvoriť API kľúč',
|
||||||
createBoard: 'Vytvoriť tabuľu',
|
createBoard: 'Vytvoriť tabuľu',
|
||||||
createCustomFieldGroup: 'Vytvoriť skupinu vlastných polí',
|
createCustomFieldGroup: 'Vytvoriť skupinu vlastných polí',
|
||||||
@@ -394,6 +397,7 @@ export default {
|
|||||||
createLabel: 'Vytvoriť štítok',
|
createLabel: 'Vytvoriť štítok',
|
||||||
createNewLabel: 'Vytvoriť nový štítok',
|
createNewLabel: 'Vytvoriť nový štítok',
|
||||||
createProject: 'Vytvoriť projekt',
|
createProject: 'Vytvoriť projekt',
|
||||||
|
cutCard_title: 'Vystrihnúť kartu',
|
||||||
deactivateUser: 'Deaktivovať používateľa',
|
deactivateUser: 'Deaktivovať používateľa',
|
||||||
deactivateUser_title: 'Deaktivovať používateľa',
|
deactivateUser_title: 'Deaktivovať používateľa',
|
||||||
delete: 'Zmazať',
|
delete: 'Zmazať',
|
||||||
|
|||||||
@@ -303,6 +303,8 @@ export default {
|
|||||||
showOnFrontOfCard: 'Прикажи на предњој страни картице',
|
showOnFrontOfCard: 'Прикажи на предњој страни картице',
|
||||||
smtp: 'SMTP',
|
smtp: 'SMTP',
|
||||||
sortList_title: 'Сложи списак',
|
sortList_title: 'Сложи списак',
|
||||||
|
sourceCardIsNoLongerAvailableForCopying: 'Изворна картица више није доступна за копирање.',
|
||||||
|
sourceCardIsNoLongerAvailableForMoving: 'Изворна картица више није доступна за премештање.',
|
||||||
stopwatch: 'Штоперица',
|
stopwatch: 'Штоперица',
|
||||||
story: 'Прича',
|
story: 'Прича',
|
||||||
subscribeToCardWhenCommenting: 'Претплати се на картицу при коментарисању',
|
subscribeToCardWhenCommenting: 'Претплати се на картицу при коментарисању',
|
||||||
@@ -390,6 +392,7 @@ export default {
|
|||||||
archiveCards_title: 'Архивирај картице',
|
archiveCards_title: 'Архивирај картице',
|
||||||
assignAsOwner: 'Додели као власника',
|
assignAsOwner: 'Додели као власника',
|
||||||
cancel: 'Откажи',
|
cancel: 'Откажи',
|
||||||
|
copyCard_title: 'Копирај картицу',
|
||||||
createApiKey: 'Креирај API кључ',
|
createApiKey: 'Креирај API кључ',
|
||||||
createBoard: 'Направи таблу',
|
createBoard: 'Направи таблу',
|
||||||
createCustomFieldGroup: 'Направи групу прилагођених поља',
|
createCustomFieldGroup: 'Направи групу прилагођених поља',
|
||||||
@@ -397,6 +400,7 @@ export default {
|
|||||||
createLabel: 'Направи ознаку',
|
createLabel: 'Направи ознаку',
|
||||||
createNewLabel: 'Направи нову ознаку',
|
createNewLabel: 'Направи нову ознаку',
|
||||||
createProject: 'Направи пројекат',
|
createProject: 'Направи пројекат',
|
||||||
|
cutCard_title: 'Исеци картицу',
|
||||||
deactivateUser: 'Деактивирај корисника',
|
deactivateUser: 'Деактивирај корисника',
|
||||||
deactivateUser_title: 'Деактивирај корисника',
|
deactivateUser_title: 'Деактивирај корисника',
|
||||||
delete: 'Обриши',
|
delete: 'Обриши',
|
||||||
|
|||||||
@@ -304,6 +304,8 @@ export default {
|
|||||||
showOnFrontOfCard: 'Prikaži na prednjoj strani kartice',
|
showOnFrontOfCard: 'Prikaži na prednjoj strani kartice',
|
||||||
smtp: 'SMTP',
|
smtp: 'SMTP',
|
||||||
sortList_title: 'Složi spisak',
|
sortList_title: 'Složi spisak',
|
||||||
|
sourceCardIsNoLongerAvailableForCopying: 'Izvorna kartica više nije dostupna za kopiranje.',
|
||||||
|
sourceCardIsNoLongerAvailableForMoving: 'Izvorna kartica više nije dostupna za premeštanje.',
|
||||||
stopwatch: 'Štoperica',
|
stopwatch: 'Štoperica',
|
||||||
story: 'Priča',
|
story: 'Priča',
|
||||||
subscribeToCardWhenCommenting: 'Pretplati se na karticu pri komentarisanju',
|
subscribeToCardWhenCommenting: 'Pretplati se na karticu pri komentarisanju',
|
||||||
@@ -392,6 +394,7 @@ export default {
|
|||||||
archiveCards_title: 'Arhiviraj kartice',
|
archiveCards_title: 'Arhiviraj kartice',
|
||||||
assignAsOwner: 'Dodeli kao vlasnika',
|
assignAsOwner: 'Dodeli kao vlasnika',
|
||||||
cancel: 'Otkaži',
|
cancel: 'Otkaži',
|
||||||
|
copyCard_title: 'Kopiraj karticu',
|
||||||
createApiKey: 'Kreiraj API ključ',
|
createApiKey: 'Kreiraj API ključ',
|
||||||
createBoard: 'Napravi tablu',
|
createBoard: 'Napravi tablu',
|
||||||
createCustomFieldGroup: 'Napravi grupu prilagođenih polja',
|
createCustomFieldGroup: 'Napravi grupu prilagođenih polja',
|
||||||
@@ -399,6 +402,7 @@ export default {
|
|||||||
createLabel: 'Napravi oznaku',
|
createLabel: 'Napravi oznaku',
|
||||||
createNewLabel: 'Napravi novu oznaku',
|
createNewLabel: 'Napravi novu oznaku',
|
||||||
createProject: 'Napravi projekat',
|
createProject: 'Napravi projekat',
|
||||||
|
cutCard_title: 'Iseci karticu',
|
||||||
deactivateUser: 'Deaktiviraj korisnika',
|
deactivateUser: 'Deaktiviraj korisnika',
|
||||||
deactivateUser_title: 'Deaktiviraj korisnika',
|
deactivateUser_title: 'Deaktiviraj korisnika',
|
||||||
delete: 'Obriši',
|
delete: 'Obriši',
|
||||||
|
|||||||
@@ -310,6 +310,10 @@ export default {
|
|||||||
showOnFrontOfCard: 'Visa på framsidan av kort',
|
showOnFrontOfCard: 'Visa på framsidan av kort',
|
||||||
smtp: 'SMTP',
|
smtp: 'SMTP',
|
||||||
sortList_title: 'Sortera lista',
|
sortList_title: 'Sortera lista',
|
||||||
|
sourceCardIsNoLongerAvailableForCopying:
|
||||||
|
'Källkortet är inte längre tillgängligt för kopiering.',
|
||||||
|
sourceCardIsNoLongerAvailableForMoving:
|
||||||
|
'Källkortet är inte längre tillgängligt för flyttning.',
|
||||||
stopwatch: 'Timer',
|
stopwatch: 'Timer',
|
||||||
story: 'Berättelse',
|
story: 'Berättelse',
|
||||||
subscribeToCardWhenCommenting: 'Prenumerera på kort vid kommentering',
|
subscribeToCardWhenCommenting: 'Prenumerera på kort vid kommentering',
|
||||||
@@ -399,6 +403,7 @@ export default {
|
|||||||
archiveCards_title: 'Arkivera kort',
|
archiveCards_title: 'Arkivera kort',
|
||||||
assignAsOwner: 'Tilldela som ägare',
|
assignAsOwner: 'Tilldela som ägare',
|
||||||
cancel: 'Avbryt',
|
cancel: 'Avbryt',
|
||||||
|
copyCard_title: 'Kopiera kort',
|
||||||
createApiKey: 'Skapa API-nyckel',
|
createApiKey: 'Skapa API-nyckel',
|
||||||
createBoard: 'Skapa tavla',
|
createBoard: 'Skapa tavla',
|
||||||
createCustomFieldGroup: 'Skapa anpassad fältgrupp',
|
createCustomFieldGroup: 'Skapa anpassad fältgrupp',
|
||||||
@@ -406,6 +411,7 @@ export default {
|
|||||||
createLabel: 'Skapa etikett',
|
createLabel: 'Skapa etikett',
|
||||||
createNewLabel: 'Skapa ny etikett',
|
createNewLabel: 'Skapa ny etikett',
|
||||||
createProject: 'Skapa projekt',
|
createProject: 'Skapa projekt',
|
||||||
|
cutCard_title: 'Klipp ut kort',
|
||||||
deactivateUser: 'Inaktivera användare',
|
deactivateUser: 'Inaktivera användare',
|
||||||
deactivateUser_title: 'Inaktivera användare',
|
deactivateUser_title: 'Inaktivera användare',
|
||||||
delete: 'Ta bort',
|
delete: 'Ta bort',
|
||||||
|
|||||||
@@ -306,6 +306,8 @@ export default {
|
|||||||
showOnFrontOfCard: 'Kartın ön yüzünde göster',
|
showOnFrontOfCard: 'Kartın ön yüzünde göster',
|
||||||
smtp: 'SMTP',
|
smtp: 'SMTP',
|
||||||
sortList_title: 'Listeyi sırala',
|
sortList_title: 'Listeyi sırala',
|
||||||
|
sourceCardIsNoLongerAvailableForCopying: 'Kaynak kart artık kopyalama için kullanılamıyor.',
|
||||||
|
sourceCardIsNoLongerAvailableForMoving: 'Kaynak kart artık taşıma için kullanılamıyor.',
|
||||||
stopwatch: 'kronometre',
|
stopwatch: 'kronometre',
|
||||||
story: 'Hikaye',
|
story: 'Hikaye',
|
||||||
subscribeToCardWhenCommenting: 'Yorum yaparken karta abone ol',
|
subscribeToCardWhenCommenting: 'Yorum yaparken karta abone ol',
|
||||||
@@ -397,6 +399,7 @@ export default {
|
|||||||
archiveCards_title: 'Kartları arşivle',
|
archiveCards_title: 'Kartları arşivle',
|
||||||
assignAsOwner: 'Sahip olarak ata',
|
assignAsOwner: 'Sahip olarak ata',
|
||||||
cancel: 'İptal',
|
cancel: 'İptal',
|
||||||
|
copyCard_title: 'Kartı kopyala',
|
||||||
createApiKey: 'API anahtarı oluştur',
|
createApiKey: 'API anahtarı oluştur',
|
||||||
createBoard: 'Pano oluştur',
|
createBoard: 'Pano oluştur',
|
||||||
createCustomFieldGroup: 'Özel alan grubu oluştur',
|
createCustomFieldGroup: 'Özel alan grubu oluştur',
|
||||||
@@ -404,6 +407,7 @@ export default {
|
|||||||
createLabel: 'Etiket oluştur',
|
createLabel: 'Etiket oluştur',
|
||||||
createNewLabel: 'Yeni etiket oluştur',
|
createNewLabel: 'Yeni etiket oluştur',
|
||||||
createProject: 'Proje oluştur',
|
createProject: 'Proje oluştur',
|
||||||
|
cutCard_title: 'Kartı kes',
|
||||||
deactivateUser: 'Kullanıcıyı devre dışı bırak',
|
deactivateUser: 'Kullanıcıyı devre dışı bırak',
|
||||||
deactivateUser_title: 'Kullanıcıyı devre dışı bırak',
|
deactivateUser_title: 'Kullanıcıyı devre dışı bırak',
|
||||||
delete: 'Sil',
|
delete: 'Sil',
|
||||||
|
|||||||
@@ -305,6 +305,8 @@ export default {
|
|||||||
showOnFrontOfCard: 'Показати на лицьовій стороні картки',
|
showOnFrontOfCard: 'Показати на лицьовій стороні картки',
|
||||||
smtp: 'SMTP',
|
smtp: 'SMTP',
|
||||||
sortList_title: 'Сортувати список',
|
sortList_title: 'Сортувати список',
|
||||||
|
sourceCardIsNoLongerAvailableForCopying: 'Вихідна картка більше недоступна для копіювання.',
|
||||||
|
sourceCardIsNoLongerAvailableForMoving: 'Вихідна картка більше недоступна для переміщення.',
|
||||||
stopwatch: 'Секундомір',
|
stopwatch: 'Секундомір',
|
||||||
story: 'Історія',
|
story: 'Історія',
|
||||||
subscribeToCardWhenCommenting: 'Підпишіться на картку при коментуванні',
|
subscribeToCardWhenCommenting: 'Підпишіться на картку при коментуванні',
|
||||||
@@ -392,6 +394,7 @@ export default {
|
|||||||
archiveCards_title: 'Архівувати картки',
|
archiveCards_title: 'Архівувати картки',
|
||||||
assignAsOwner: 'Призначити власником',
|
assignAsOwner: 'Призначити власником',
|
||||||
cancel: 'Скасувати',
|
cancel: 'Скасувати',
|
||||||
|
copyCard_title: 'Копіювати картку',
|
||||||
createApiKey: 'Створити ключ API',
|
createApiKey: 'Створити ключ API',
|
||||||
createBoard: 'Створити дошку',
|
createBoard: 'Створити дошку',
|
||||||
createCustomFieldGroup: 'Створити групу користувацьких полів',
|
createCustomFieldGroup: 'Створити групу користувацьких полів',
|
||||||
@@ -399,6 +402,7 @@ export default {
|
|||||||
createLabel: 'Створити мітку',
|
createLabel: 'Створити мітку',
|
||||||
createNewLabel: 'Створити нову мітку',
|
createNewLabel: 'Створити нову мітку',
|
||||||
createProject: 'Створити проект',
|
createProject: 'Створити проект',
|
||||||
|
cutCard_title: 'Вирізати картку',
|
||||||
deactivateUser: 'Деактивувати користувача',
|
deactivateUser: 'Деактивувати користувача',
|
||||||
deactivateUser_title: 'Деактивувати користувача',
|
deactivateUser_title: 'Деактивувати користувача',
|
||||||
delete: 'Видалити',
|
delete: 'Видалити',
|
||||||
|
|||||||
@@ -301,6 +301,8 @@ export default {
|
|||||||
showOnFrontOfCard: "Karta old tomonida ko'rsatish",
|
showOnFrontOfCard: "Karta old tomonida ko'rsatish",
|
||||||
smtp: 'SMTP',
|
smtp: 'SMTP',
|
||||||
sortList_title: "Ro'yxatni saralash",
|
sortList_title: "Ro'yxatni saralash",
|
||||||
|
sourceCardIsNoLongerAvailableForCopying: 'Manba karta nusxalash uchun endi mavjud emas.',
|
||||||
|
sourceCardIsNoLongerAvailableForMoving: 'Manba karta koʻchirish uchun endi mavjud emas.',
|
||||||
stopwatch: 'Taymer',
|
stopwatch: 'Taymer',
|
||||||
story: 'Hikoya',
|
story: 'Hikoya',
|
||||||
subscribeToCardWhenCommenting: "Izoh qoldirganida kartaga obuna bo'lish",
|
subscribeToCardWhenCommenting: "Izoh qoldirganida kartaga obuna bo'lish",
|
||||||
@@ -390,6 +392,7 @@ export default {
|
|||||||
archiveCards_title: 'Kartalarni arxivlash',
|
archiveCards_title: 'Kartalarni arxivlash',
|
||||||
assignAsOwner: 'Egasi sifatida tayinlash',
|
assignAsOwner: 'Egasi sifatida tayinlash',
|
||||||
cancel: 'Bekor qilish',
|
cancel: 'Bekor qilish',
|
||||||
|
copyCard_title: 'Kartani nusxalash',
|
||||||
createApiKey: 'API kalitini yaratish',
|
createApiKey: 'API kalitini yaratish',
|
||||||
createBoard: 'Doska yaratish',
|
createBoard: 'Doska yaratish',
|
||||||
createCustomFieldGroup: 'Maxsus maydon guruhi yaratish',
|
createCustomFieldGroup: 'Maxsus maydon guruhi yaratish',
|
||||||
@@ -397,6 +400,7 @@ export default {
|
|||||||
createLabel: 'Yorliq yaratish',
|
createLabel: 'Yorliq yaratish',
|
||||||
createNewLabel: 'Yangi yorliq yaratish',
|
createNewLabel: 'Yangi yorliq yaratish',
|
||||||
createProject: 'Loyiha yaratish',
|
createProject: 'Loyiha yaratish',
|
||||||
|
cutCard_title: 'Kartani kesish',
|
||||||
deactivateUser: 'Foydalanuvchini faolsizlantirish',
|
deactivateUser: 'Foydalanuvchini faolsizlantirish',
|
||||||
deactivateUser_title: 'Foydalanuvchini faolsizlantirish',
|
deactivateUser_title: 'Foydalanuvchini faolsizlantirish',
|
||||||
delete: "O'chirish",
|
delete: "O'chirish",
|
||||||
|
|||||||
@@ -283,6 +283,8 @@ export default {
|
|||||||
showOnFrontOfCard: '在卡片正面显示',
|
showOnFrontOfCard: '在卡片正面显示',
|
||||||
smtp: 'SMTP',
|
smtp: 'SMTP',
|
||||||
sortList_title: '排序列表',
|
sortList_title: '排序列表',
|
||||||
|
sourceCardIsNoLongerAvailableForCopying: '源卡片不再可供复制。',
|
||||||
|
sourceCardIsNoLongerAvailableForMoving: '源卡片不再可供移动。',
|
||||||
stopwatch: '计时器',
|
stopwatch: '计时器',
|
||||||
story: '故事',
|
story: '故事',
|
||||||
subscribeToCardWhenCommenting: '评论时自动关注卡片',
|
subscribeToCardWhenCommenting: '评论时自动关注卡片',
|
||||||
@@ -367,6 +369,7 @@ export default {
|
|||||||
archiveCards_title: '归档多个卡片',
|
archiveCards_title: '归档多个卡片',
|
||||||
assignAsOwner: '指定为所有者',
|
assignAsOwner: '指定为所有者',
|
||||||
cancel: '取消',
|
cancel: '取消',
|
||||||
|
copyCard_title: '复制卡片',
|
||||||
createApiKey: '创建API密钥',
|
createApiKey: '创建API密钥',
|
||||||
createBoard: '创建面板',
|
createBoard: '创建面板',
|
||||||
createCustomFieldGroup: '创建自定义字段组',
|
createCustomFieldGroup: '创建自定义字段组',
|
||||||
@@ -374,6 +377,7 @@ export default {
|
|||||||
createLabel: '创建标签',
|
createLabel: '创建标签',
|
||||||
createNewLabel: '创建新标签',
|
createNewLabel: '创建新标签',
|
||||||
createProject: '创建项目',
|
createProject: '创建项目',
|
||||||
|
cutCard_title: '剪切卡片',
|
||||||
deactivateUser: '停用用户',
|
deactivateUser: '停用用户',
|
||||||
deactivateUser_title: '停用用户',
|
deactivateUser_title: '停用用户',
|
||||||
delete: '删除',
|
delete: '删除',
|
||||||
|
|||||||
@@ -283,6 +283,8 @@ export default {
|
|||||||
showOnFrontOfCard: '在卡片正面顯示',
|
showOnFrontOfCard: '在卡片正面顯示',
|
||||||
smtp: 'SMTP',
|
smtp: 'SMTP',
|
||||||
sortList_title: '排序列表',
|
sortList_title: '排序列表',
|
||||||
|
sourceCardIsNoLongerAvailableForCopying: '來源卡片不再可供複製。',
|
||||||
|
sourceCardIsNoLongerAvailableForMoving: '來源卡片不再可供移動。',
|
||||||
stopwatch: '碼表',
|
stopwatch: '碼表',
|
||||||
story: '故事',
|
story: '故事',
|
||||||
subscribeToCardWhenCommenting: '評論時訂閱卡片',
|
subscribeToCardWhenCommenting: '評論時訂閱卡片',
|
||||||
@@ -367,6 +369,7 @@ export default {
|
|||||||
archiveCards_title: '封存卡片',
|
archiveCards_title: '封存卡片',
|
||||||
assignAsOwner: '指派為擁有者',
|
assignAsOwner: '指派為擁有者',
|
||||||
cancel: '取消',
|
cancel: '取消',
|
||||||
|
copyCard_title: '複製卡片',
|
||||||
createApiKey: '建立API金鑰',
|
createApiKey: '建立API金鑰',
|
||||||
createBoard: '創建看板',
|
createBoard: '創建看板',
|
||||||
createCustomFieldGroup: '創建自定義欄位群組',
|
createCustomFieldGroup: '創建自定義欄位群組',
|
||||||
@@ -374,6 +377,7 @@ export default {
|
|||||||
createLabel: '創建標籤',
|
createLabel: '創建標籤',
|
||||||
createNewLabel: '創建新標籤',
|
createNewLabel: '創建新標籤',
|
||||||
createProject: '創建專案',
|
createProject: '創建專案',
|
||||||
|
cutCard_title: '剪下卡片',
|
||||||
deactivateUser: '停用使用者',
|
deactivateUser: '停用使用者',
|
||||||
deactivateUser_title: '停用使用者',
|
deactivateUser_title: '停用使用者',
|
||||||
delete: '刪除',
|
delete: '刪除',
|
||||||
|
|||||||
@@ -60,6 +60,7 @@ export default class extends BaseModel {
|
|||||||
case ActionTypes.BOARD_MEMBERSHIP_CREATE_HANDLE:
|
case ActionTypes.BOARD_MEMBERSHIP_CREATE_HANDLE:
|
||||||
case ActionTypes.LIST_UPDATE_HANDLE:
|
case ActionTypes.LIST_UPDATE_HANDLE:
|
||||||
case ActionTypes.CARD_UPDATE_HANDLE:
|
case ActionTypes.CARD_UPDATE_HANDLE:
|
||||||
|
case ActionTypes.CARD_TRANSFER__FAILURE:
|
||||||
if (payload.attachments) {
|
if (payload.attachments) {
|
||||||
payload.attachments.forEach((attachment) => {
|
payload.attachments.forEach((attachment) => {
|
||||||
Attachment.upsert(prepareAttachment(attachment));
|
Attachment.upsert(prepareAttachment(attachment));
|
||||||
@@ -80,6 +81,7 @@ export default class extends BaseModel {
|
|||||||
case ActionTypes.BOARD_FETCH__SUCCESS:
|
case ActionTypes.BOARD_FETCH__SUCCESS:
|
||||||
case ActionTypes.CARDS_FETCH__SUCCESS:
|
case ActionTypes.CARDS_FETCH__SUCCESS:
|
||||||
case ActionTypes.CARD_CREATE_HANDLE:
|
case ActionTypes.CARD_CREATE_HANDLE:
|
||||||
|
case ActionTypes.CARD_TRANSFER__SUCCESS:
|
||||||
case ActionTypes.CARD_DUPLICATE__SUCCESS:
|
case ActionTypes.CARD_DUPLICATE__SUCCESS:
|
||||||
payload.attachments.forEach((attachment) => {
|
payload.attachments.forEach((attachment) => {
|
||||||
Attachment.upsert(prepareAttachment(attachment));
|
Attachment.upsert(prepareAttachment(attachment));
|
||||||
|
|||||||
@@ -3,6 +3,7 @@
|
|||||||
* Licensed under the Fair Use License: https://github.com/plankanban/planka/blob/master/LICENSE.md
|
* Licensed under the Fair Use License: https://github.com/plankanban/planka/blob/master/LICENSE.md
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
import keyBy from 'lodash/keyBy';
|
||||||
import { attr, fk, many, oneToOne } from 'redux-orm';
|
import { attr, fk, many, oneToOne } from 'redux-orm';
|
||||||
|
|
||||||
import BaseModel from './BaseModel';
|
import BaseModel from './BaseModel';
|
||||||
@@ -316,33 +317,28 @@ export default class extends BaseModel {
|
|||||||
case ActionTypes.CARD_UPDATE: {
|
case ActionTypes.CARD_UPDATE: {
|
||||||
const cardModel = Card.withId(payload.id);
|
const cardModel = Card.withId(payload.id);
|
||||||
|
|
||||||
// TODO: introduce separate action?
|
if (payload.data.listId && payload.data.listId !== cardModel.listId) {
|
||||||
if (payload.data.boardId && payload.data.boardId !== cardModel.boardId) {
|
payload.data.listChangedAt = new Date(); // eslint-disable-line no-param-reassign
|
||||||
cardModel.deleteWithRelated();
|
|
||||||
} else {
|
|
||||||
if (payload.data.listId && payload.data.listId !== cardModel.listId) {
|
|
||||||
payload.data.listChangedAt = new Date(); // eslint-disable-line no-param-reassign
|
|
||||||
}
|
|
||||||
|
|
||||||
if (payload.data.dueDate !== undefined) {
|
|
||||||
if (payload.data.dueDate) {
|
|
||||||
if (!cardModel.dueDate) {
|
|
||||||
payload.data.isDueCompleted = false; // eslint-disable-line no-param-reassign
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
payload.data.isDueCompleted = null; // eslint-disable-line no-param-reassign
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (payload.data.isClosed !== undefined && payload.data.isClosed !== cardModel.isClosed) {
|
|
||||||
cardModel.linkedTasks.update({
|
|
||||||
isCompleted: payload.data.isClosed,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
cardModel.update(payload.data);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (payload.data.dueDate !== undefined) {
|
||||||
|
if (payload.data.dueDate) {
|
||||||
|
if (!cardModel.dueDate) {
|
||||||
|
payload.data.isDueCompleted = false; // eslint-disable-line no-param-reassign
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
payload.data.isDueCompleted = null; // eslint-disable-line no-param-reassign
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (payload.data.isClosed !== undefined && payload.data.isClosed !== cardModel.isClosed) {
|
||||||
|
cardModel.linkedTasks.update({
|
||||||
|
isCompleted: payload.data.isClosed,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
cardModel.update(payload.data);
|
||||||
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case ActionTypes.CARD_UPDATE_HANDLE: {
|
case ActionTypes.CARD_UPDATE_HANDLE: {
|
||||||
@@ -378,14 +374,86 @@ export default class extends BaseModel {
|
|||||||
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case ActionTypes.CARD_DUPLICATE:
|
case ActionTypes.CARD_TRANSFER: {
|
||||||
Card.withId(payload.id).duplicate(payload.localId, payload.data);
|
const cardModel = Card.withId(payload.id);
|
||||||
|
|
||||||
|
if (cardModel) {
|
||||||
|
cardModel.update(payload.data);
|
||||||
|
cardModel.syncAfterBoardChange();
|
||||||
|
}
|
||||||
|
|
||||||
break;
|
break;
|
||||||
case ActionTypes.CARD_DUPLICATE__SUCCESS: {
|
}
|
||||||
Card.withId(payload.localId).deleteWithRelated();
|
case ActionTypes.CARD_TRANSFER__SUCCESS: {
|
||||||
|
const cardModel = Card.withId(payload.card.id);
|
||||||
|
|
||||||
const cardModel = Card.upsert(payload.card);
|
if (cardModel) {
|
||||||
|
cardModel.deleteWithRelated();
|
||||||
|
}
|
||||||
|
|
||||||
|
Card.upsert(payload.card);
|
||||||
|
|
||||||
|
if (payload.cardMemberships) {
|
||||||
|
payload.cardMemberships.forEach(({ cardId, userId }) => {
|
||||||
|
Card.withId(cardId).users.add(userId);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (payload.cardLabels) {
|
||||||
|
payload.cardLabels.forEach(({ cardId, labelId }) => {
|
||||||
|
Card.withId(cardId).labels.add(labelId);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case ActionTypes.CARD_TRANSFER__FAILURE: {
|
||||||
|
const cardModel = Card.withId(payload.id);
|
||||||
|
|
||||||
|
if (cardModel) {
|
||||||
|
cardModel.deleteWithRelated();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (payload.card) {
|
||||||
|
Card.upsert(payload.card);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (payload.cardMemberships) {
|
||||||
|
payload.cardMemberships.forEach(({ cardId, userId }) => {
|
||||||
|
Card.withId(cardId).users.add(userId);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (payload.cardLabels) {
|
||||||
|
payload.cardLabels.forEach(({ cardId, labelId }) => {
|
||||||
|
Card.withId(cardId).labels.add(labelId);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case ActionTypes.CARD_DUPLICATE: {
|
||||||
|
let cardModel = Card.withId(payload.id);
|
||||||
|
|
||||||
|
if (cardModel) {
|
||||||
|
cardModel = cardModel.duplicate(payload.localId, {
|
||||||
|
...payload.data,
|
||||||
|
listChangedAt: new Date(),
|
||||||
|
});
|
||||||
|
|
||||||
|
cardModel.syncAfterBoardChange();
|
||||||
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case ActionTypes.CARD_DUPLICATE__SUCCESS: {
|
||||||
|
let cardModel = Card.withId(payload.localId);
|
||||||
|
|
||||||
|
if (cardModel) {
|
||||||
|
cardModel.deleteWithRelated();
|
||||||
|
}
|
||||||
|
|
||||||
|
cardModel = Card.upsert(payload.card);
|
||||||
|
|
||||||
payload.cardMemberships.forEach(({ userId }) => {
|
payload.cardMemberships.forEach(({ userId }) => {
|
||||||
cardModel.users.add(userId);
|
cardModel.users.add(userId);
|
||||||
@@ -397,10 +465,15 @@ export default class extends BaseModel {
|
|||||||
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case ActionTypes.CARD_DUPLICATE__FAILURE:
|
case ActionTypes.CARD_DUPLICATE__FAILURE: {
|
||||||
Card.withId(payload.localId).deleteWithRelated();
|
const cardModel = Card.withId(payload.localId);
|
||||||
|
|
||||||
|
if (cardModel) {
|
||||||
|
cardModel.deleteWithRelated();
|
||||||
|
}
|
||||||
|
|
||||||
break;
|
break;
|
||||||
|
}
|
||||||
case ActionTypes.CARD_DELETE:
|
case ActionTypes.CARD_DELETE:
|
||||||
Card.withId(payload.id).deleteWithRelated();
|
Card.withId(payload.id).deleteWithRelated();
|
||||||
|
|
||||||
@@ -637,6 +710,47 @@ export default class extends BaseModel {
|
|||||||
return cardModel;
|
return cardModel;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
syncAfterBoardChange() {
|
||||||
|
if (!this.board) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const boardMemberships = this.board.memberships.toRefArray();
|
||||||
|
const userIdsSet = new Set(boardMemberships.map(({ userId }) => userId));
|
||||||
|
|
||||||
|
this.users.toRefArray().forEach((user) => {
|
||||||
|
if (userIdsSet.has(user.id)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.users.remove(user.id);
|
||||||
|
});
|
||||||
|
|
||||||
|
this.taskLists.toModelArray().forEach((taskListModel) => {
|
||||||
|
taskListModel.tasks.toModelArray().forEach((taskModel) => {
|
||||||
|
if (!taskModel.assigneeUserId || userIdsSet.has(taskModel.assigneeUserId)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
taskModel.update({
|
||||||
|
assigneeUserId: null,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
const labels = this.board.labels.toRefArray();
|
||||||
|
const labelByName = keyBy(labels, 'name');
|
||||||
|
|
||||||
|
this.labels.toRefArray().forEach((label) => {
|
||||||
|
if (!labelByName[label.name]) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.labels.remove(label.id);
|
||||||
|
this.labels.add(labelByName[label.name].id);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
deleteClearable() {
|
deleteClearable() {
|
||||||
this.users.clear();
|
this.users.clear();
|
||||||
this.labels.clear();
|
this.labels.clear();
|
||||||
|
|||||||
@@ -38,6 +38,7 @@ export default class extends BaseModel {
|
|||||||
case ActionTypes.BOARD_MEMBERSHIP_CREATE_HANDLE:
|
case ActionTypes.BOARD_MEMBERSHIP_CREATE_HANDLE:
|
||||||
case ActionTypes.LIST_UPDATE_HANDLE:
|
case ActionTypes.LIST_UPDATE_HANDLE:
|
||||||
case ActionTypes.CARD_UPDATE_HANDLE:
|
case ActionTypes.CARD_UPDATE_HANDLE:
|
||||||
|
case ActionTypes.CARD_TRANSFER__FAILURE:
|
||||||
if (payload.customFields) {
|
if (payload.customFields) {
|
||||||
payload.customFields.forEach((customField) => {
|
payload.customFields.forEach((customField) => {
|
||||||
CustomField.upsert(customField);
|
CustomField.upsert(customField);
|
||||||
@@ -59,6 +60,7 @@ export default class extends BaseModel {
|
|||||||
case ActionTypes.BOARD_FETCH__SUCCESS:
|
case ActionTypes.BOARD_FETCH__SUCCESS:
|
||||||
case ActionTypes.CARDS_FETCH__SUCCESS:
|
case ActionTypes.CARDS_FETCH__SUCCESS:
|
||||||
case ActionTypes.CARD_CREATE_HANDLE:
|
case ActionTypes.CARD_CREATE_HANDLE:
|
||||||
|
case ActionTypes.CARD_TRANSFER__SUCCESS:
|
||||||
case ActionTypes.CARD_DUPLICATE__SUCCESS:
|
case ActionTypes.CARD_DUPLICATE__SUCCESS:
|
||||||
payload.customFields.forEach((customField) => {
|
payload.customFields.forEach((customField) => {
|
||||||
CustomField.upsert(customField);
|
CustomField.upsert(customField);
|
||||||
|
|||||||
@@ -42,6 +42,7 @@ export default class extends BaseModel {
|
|||||||
case ActionTypes.BOARD_MEMBERSHIP_CREATE_HANDLE:
|
case ActionTypes.BOARD_MEMBERSHIP_CREATE_HANDLE:
|
||||||
case ActionTypes.LIST_UPDATE_HANDLE:
|
case ActionTypes.LIST_UPDATE_HANDLE:
|
||||||
case ActionTypes.CARD_UPDATE_HANDLE:
|
case ActionTypes.CARD_UPDATE_HANDLE:
|
||||||
|
case ActionTypes.CARD_TRANSFER__FAILURE:
|
||||||
if (payload.customFieldGroups) {
|
if (payload.customFieldGroups) {
|
||||||
payload.customFieldGroups.forEach((customFieldGroup) => {
|
payload.customFieldGroups.forEach((customFieldGroup) => {
|
||||||
CustomFieldGroup.upsert(customFieldGroup);
|
CustomFieldGroup.upsert(customFieldGroup);
|
||||||
@@ -62,6 +63,7 @@ export default class extends BaseModel {
|
|||||||
case ActionTypes.BOARD_FETCH__SUCCESS:
|
case ActionTypes.BOARD_FETCH__SUCCESS:
|
||||||
case ActionTypes.CARDS_FETCH__SUCCESS:
|
case ActionTypes.CARDS_FETCH__SUCCESS:
|
||||||
case ActionTypes.CARD_CREATE_HANDLE:
|
case ActionTypes.CARD_CREATE_HANDLE:
|
||||||
|
case ActionTypes.CARD_TRANSFER__SUCCESS:
|
||||||
case ActionTypes.CARD_DUPLICATE__SUCCESS:
|
case ActionTypes.CARD_DUPLICATE__SUCCESS:
|
||||||
payload.customFieldGroups.forEach((customFieldGroup) => {
|
payload.customFieldGroups.forEach((customFieldGroup) => {
|
||||||
CustomFieldGroup.upsert(customFieldGroup);
|
CustomFieldGroup.upsert(customFieldGroup);
|
||||||
|
|||||||
@@ -53,6 +53,7 @@ export default class extends BaseModel {
|
|||||||
case ActionTypes.BOARD_MEMBERSHIP_CREATE_HANDLE:
|
case ActionTypes.BOARD_MEMBERSHIP_CREATE_HANDLE:
|
||||||
case ActionTypes.LIST_UPDATE_HANDLE:
|
case ActionTypes.LIST_UPDATE_HANDLE:
|
||||||
case ActionTypes.CARD_UPDATE_HANDLE:
|
case ActionTypes.CARD_UPDATE_HANDLE:
|
||||||
|
case ActionTypes.CARD_TRANSFER__FAILURE:
|
||||||
if (payload.customFieldValues) {
|
if (payload.customFieldValues) {
|
||||||
payload.customFieldValues.forEach((customFieldValue) => {
|
payload.customFieldValues.forEach((customFieldValue) => {
|
||||||
CustomFieldValue.upsert(prepareCustomFieldValue(customFieldValue));
|
CustomFieldValue.upsert(prepareCustomFieldValue(customFieldValue));
|
||||||
@@ -73,6 +74,7 @@ export default class extends BaseModel {
|
|||||||
case ActionTypes.BOARD_FETCH__SUCCESS:
|
case ActionTypes.BOARD_FETCH__SUCCESS:
|
||||||
case ActionTypes.CARDS_FETCH__SUCCESS:
|
case ActionTypes.CARDS_FETCH__SUCCESS:
|
||||||
case ActionTypes.CARD_CREATE_HANDLE:
|
case ActionTypes.CARD_CREATE_HANDLE:
|
||||||
|
case ActionTypes.CARD_TRANSFER__SUCCESS:
|
||||||
case ActionTypes.CARD_DUPLICATE__SUCCESS:
|
case ActionTypes.CARD_DUPLICATE__SUCCESS:
|
||||||
payload.customFieldValues.forEach((customFieldValue) => {
|
payload.customFieldValues.forEach((customFieldValue) => {
|
||||||
CustomFieldValue.upsert(prepareCustomFieldValue(customFieldValue));
|
CustomFieldValue.upsert(prepareCustomFieldValue(customFieldValue));
|
||||||
|
|||||||
@@ -45,6 +45,7 @@ export default class extends BaseModel {
|
|||||||
case ActionTypes.BOARD_MEMBERSHIP_CREATE_HANDLE:
|
case ActionTypes.BOARD_MEMBERSHIP_CREATE_HANDLE:
|
||||||
case ActionTypes.LIST_UPDATE_HANDLE:
|
case ActionTypes.LIST_UPDATE_HANDLE:
|
||||||
case ActionTypes.CARD_UPDATE_HANDLE:
|
case ActionTypes.CARD_UPDATE_HANDLE:
|
||||||
|
case ActionTypes.CARD_TRANSFER__FAILURE:
|
||||||
if (payload.tasks) {
|
if (payload.tasks) {
|
||||||
payload.tasks.forEach((task) => {
|
payload.tasks.forEach((task) => {
|
||||||
Task.upsert(task);
|
Task.upsert(task);
|
||||||
@@ -65,6 +66,7 @@ export default class extends BaseModel {
|
|||||||
case ActionTypes.BOARD_FETCH__SUCCESS:
|
case ActionTypes.BOARD_FETCH__SUCCESS:
|
||||||
case ActionTypes.CARDS_FETCH__SUCCESS:
|
case ActionTypes.CARDS_FETCH__SUCCESS:
|
||||||
case ActionTypes.CARD_CREATE_HANDLE:
|
case ActionTypes.CARD_CREATE_HANDLE:
|
||||||
|
case ActionTypes.CARD_TRANSFER__SUCCESS:
|
||||||
case ActionTypes.CARD_DUPLICATE__SUCCESS:
|
case ActionTypes.CARD_DUPLICATE__SUCCESS:
|
||||||
payload.tasks.forEach((task) => {
|
payload.tasks.forEach((task) => {
|
||||||
Task.upsert(task);
|
Task.upsert(task);
|
||||||
|
|||||||
@@ -34,6 +34,7 @@ export default class extends BaseModel {
|
|||||||
case ActionTypes.BOARD_MEMBERSHIP_CREATE_HANDLE:
|
case ActionTypes.BOARD_MEMBERSHIP_CREATE_HANDLE:
|
||||||
case ActionTypes.LIST_UPDATE_HANDLE:
|
case ActionTypes.LIST_UPDATE_HANDLE:
|
||||||
case ActionTypes.CARD_UPDATE_HANDLE:
|
case ActionTypes.CARD_UPDATE_HANDLE:
|
||||||
|
case ActionTypes.CARD_TRANSFER__FAILURE:
|
||||||
if (payload.taskLists) {
|
if (payload.taskLists) {
|
||||||
payload.taskLists.forEach((taskList) => {
|
payload.taskLists.forEach((taskList) => {
|
||||||
TaskList.upsert(taskList);
|
TaskList.upsert(taskList);
|
||||||
@@ -54,6 +55,7 @@ export default class extends BaseModel {
|
|||||||
case ActionTypes.BOARD_FETCH__SUCCESS:
|
case ActionTypes.BOARD_FETCH__SUCCESS:
|
||||||
case ActionTypes.CARDS_FETCH__SUCCESS:
|
case ActionTypes.CARDS_FETCH__SUCCESS:
|
||||||
case ActionTypes.CARD_CREATE_HANDLE:
|
case ActionTypes.CARD_CREATE_HANDLE:
|
||||||
|
case ActionTypes.CARD_TRANSFER__SUCCESS:
|
||||||
case ActionTypes.CARD_DUPLICATE__SUCCESS:
|
case ActionTypes.CARD_DUPLICATE__SUCCESS:
|
||||||
payload.taskLists.forEach((taskList) => {
|
payload.taskLists.forEach((taskList) => {
|
||||||
TaskList.upsert(taskList);
|
TaskList.upsert(taskList);
|
||||||
|
|||||||
@@ -105,6 +105,7 @@ export default class extends BaseModel {
|
|||||||
case ActionTypes.BOARD_MEMBERSHIP_CREATE_HANDLE:
|
case ActionTypes.BOARD_MEMBERSHIP_CREATE_HANDLE:
|
||||||
case ActionTypes.LIST_UPDATE_HANDLE:
|
case ActionTypes.LIST_UPDATE_HANDLE:
|
||||||
case ActionTypes.CARD_UPDATE_HANDLE:
|
case ActionTypes.CARD_UPDATE_HANDLE:
|
||||||
|
case ActionTypes.CARD_TRANSFER__FAILURE:
|
||||||
if (payload.users) {
|
if (payload.users) {
|
||||||
payload.users.forEach((user) => {
|
payload.users.forEach((user) => {
|
||||||
User.upsert(user);
|
User.upsert(user);
|
||||||
@@ -351,6 +352,7 @@ export default class extends BaseModel {
|
|||||||
case ActionTypes.BOARD_FETCH__SUCCESS:
|
case ActionTypes.BOARD_FETCH__SUCCESS:
|
||||||
case ActionTypes.CARDS_FETCH__SUCCESS:
|
case ActionTypes.CARDS_FETCH__SUCCESS:
|
||||||
case ActionTypes.CARD_CREATE_HANDLE:
|
case ActionTypes.CARD_CREATE_HANDLE:
|
||||||
|
case ActionTypes.CARD_TRANSFER__SUCCESS:
|
||||||
case ActionTypes.COMMENTS_FETCH__SUCCESS:
|
case ActionTypes.COMMENTS_FETCH__SUCCESS:
|
||||||
case ActionTypes.COMMENT_CREATE_HANDLE:
|
case ActionTypes.COMMENT_CREATE_HANDLE:
|
||||||
case ActionTypes.ACTIVITIES_IN_BOARD_FETCH__SUCCESS:
|
case ActionTypes.ACTIVITIES_IN_BOARD_FETCH__SUCCESS:
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ import { LOCATION_CHANGE_HANDLE } from '../lib/redux-router';
|
|||||||
|
|
||||||
import ActionTypes from '../constants/ActionTypes';
|
import ActionTypes from '../constants/ActionTypes';
|
||||||
import ModalTypes from '../constants/ModalTypes';
|
import ModalTypes from '../constants/ModalTypes';
|
||||||
|
import ClipboardTypes from '../constants/ClipboardTypes';
|
||||||
import { HomeViews, ProjectOrders } from '../constants/Enums';
|
import { HomeViews, ProjectOrders } from '../constants/Enums';
|
||||||
|
|
||||||
const initialState = {
|
const initialState = {
|
||||||
@@ -15,6 +16,7 @@ const initialState = {
|
|||||||
isFavoritesEnabled: false,
|
isFavoritesEnabled: false,
|
||||||
isEditModeEnabled: false,
|
isEditModeEnabled: false,
|
||||||
modal: null,
|
modal: null,
|
||||||
|
clipboard: null,
|
||||||
config: null,
|
config: null,
|
||||||
boardId: null,
|
boardId: null,
|
||||||
cardId: null,
|
cardId: null,
|
||||||
@@ -175,6 +177,45 @@ export default (state = initialState, { type, payload }) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
return state;
|
return state;
|
||||||
|
case ActionTypes.CARD_DELETE:
|
||||||
|
if (payload.clipboard && payload.id === payload.clipboard.cardId) {
|
||||||
|
return {
|
||||||
|
...state,
|
||||||
|
clipboard: null,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
return state;
|
||||||
|
case ActionTypes.CARD_DELETE_HANDLE:
|
||||||
|
if (payload.clipboard && payload.card.id === payload.clipboard.cardId) {
|
||||||
|
return {
|
||||||
|
...state,
|
||||||
|
clipboard: null,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
return state;
|
||||||
|
case ActionTypes.CARD_COPY:
|
||||||
|
return {
|
||||||
|
...state,
|
||||||
|
clipboard: {
|
||||||
|
type: ClipboardTypes.COPY,
|
||||||
|
cardId: payload.id,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
case ActionTypes.CARD_CUT:
|
||||||
|
return {
|
||||||
|
...state,
|
||||||
|
clipboard: {
|
||||||
|
type: ClipboardTypes.CUT,
|
||||||
|
cardId: payload.id,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
case ActionTypes.CARD_PASTE:
|
||||||
|
return {
|
||||||
|
...state,
|
||||||
|
clipboard: null,
|
||||||
|
};
|
||||||
default:
|
default:
|
||||||
return state;
|
return state;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,6 +4,7 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
import { call, fork, join, put, race, select, take } from 'redux-saga/effects';
|
import { call, fork, join, put, race, select, take } from 'redux-saga/effects';
|
||||||
|
import toast from 'react-hot-toast';
|
||||||
import { LOCATION_CHANGE_HANDLE } from '../../../lib/redux-router';
|
import { LOCATION_CHANGE_HANDLE } from '../../../lib/redux-router';
|
||||||
|
|
||||||
import { goToBoard, goToCard } from './router';
|
import { goToBoard, goToCard } from './router';
|
||||||
@@ -11,9 +12,12 @@ import request from '../request';
|
|||||||
import selectors from '../../../selectors';
|
import selectors from '../../../selectors';
|
||||||
import actions from '../../../actions';
|
import actions from '../../../actions';
|
||||||
import api from '../../../api';
|
import api from '../../../api';
|
||||||
|
import i18n from '../../../i18n';
|
||||||
import { createLocalId } from '../../../utils/local-id';
|
import { createLocalId } from '../../../utils/local-id';
|
||||||
import { isListArchiveOrTrash, isListFinite } from '../../../utils/record-helpers';
|
import { isListArchiveOrTrash, isListFinite } from '../../../utils/record-helpers';
|
||||||
import ActionTypes from '../../../constants/ActionTypes';
|
import ActionTypes from '../../../constants/ActionTypes';
|
||||||
|
import ClipboardTypes from '../../../constants/ClipboardTypes';
|
||||||
|
import ToastTypes from '../../../constants/ToastTypes';
|
||||||
import { BoardViews, ListTypes, ListTypeStates } from '../../../constants/Enums';
|
import { BoardViews, ListTypes, ListTypeStates } from '../../../constants/Enums';
|
||||||
import LIST_TYPE_STATE_BY_TYPE from '../../../constants/ListTypeStateByType';
|
import LIST_TYPE_STATE_BY_TYPE from '../../../constants/ListTypeStateByType';
|
||||||
|
|
||||||
@@ -419,7 +423,92 @@ export function* transferCard(id, boardId, listId, index) {
|
|||||||
data.position = yield select(selectors.selectNextCardPosition, listId, index, id);
|
data.position = yield select(selectors.selectNextCardPosition, listId, index, id);
|
||||||
}
|
}
|
||||||
|
|
||||||
yield call(updateCard, id, data);
|
const typeState = LIST_TYPE_STATE_BY_TYPE[list.type];
|
||||||
|
|
||||||
|
yield put(
|
||||||
|
actions.transferCard(id, {
|
||||||
|
...data,
|
||||||
|
isClosed: typeState === ListTypeStates.CLOSED,
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
|
||||||
|
let card;
|
||||||
|
let updateError;
|
||||||
|
|
||||||
|
try {
|
||||||
|
({ item: card } = yield call(request, api.updateCard, id, data));
|
||||||
|
} catch (error) {
|
||||||
|
updateError = error;
|
||||||
|
}
|
||||||
|
|
||||||
|
let users;
|
||||||
|
let cardMemberships;
|
||||||
|
let cardLabels;
|
||||||
|
let taskLists;
|
||||||
|
let tasks;
|
||||||
|
let attachments;
|
||||||
|
let customFieldGroups;
|
||||||
|
let customFields;
|
||||||
|
let customFieldValues;
|
||||||
|
|
||||||
|
try {
|
||||||
|
({
|
||||||
|
item: card,
|
||||||
|
included: {
|
||||||
|
users,
|
||||||
|
cardMemberships,
|
||||||
|
cardLabels,
|
||||||
|
taskLists,
|
||||||
|
tasks,
|
||||||
|
attachments,
|
||||||
|
customFieldGroups,
|
||||||
|
customFields,
|
||||||
|
customFieldValues,
|
||||||
|
},
|
||||||
|
} = yield call(request, api.getCard, id));
|
||||||
|
} catch (error) {
|
||||||
|
yield put(actions.transferCard.failure(id, error));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (updateError) {
|
||||||
|
yield put(
|
||||||
|
actions.transferCard.failure(
|
||||||
|
id,
|
||||||
|
updateError,
|
||||||
|
card,
|
||||||
|
users,
|
||||||
|
cardMemberships,
|
||||||
|
cardLabels,
|
||||||
|
taskLists,
|
||||||
|
tasks,
|
||||||
|
attachments,
|
||||||
|
customFieldGroups,
|
||||||
|
customFields,
|
||||||
|
customFieldValues,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
yield call(toast, {
|
||||||
|
type: ToastTypes.SOURCE_CARD_NOT_MOVABLE,
|
||||||
|
});
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
yield put(
|
||||||
|
actions.transferCard.success(
|
||||||
|
card,
|
||||||
|
users,
|
||||||
|
cardMemberships,
|
||||||
|
cardLabels,
|
||||||
|
taskLists,
|
||||||
|
tasks,
|
||||||
|
attachments,
|
||||||
|
customFieldGroups,
|
||||||
|
customFields,
|
||||||
|
customFieldValues,
|
||||||
|
),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
export function* transferCurrentCard(boardId, listId, index) {
|
export function* transferCurrentCard(boardId, listId, index) {
|
||||||
@@ -431,23 +520,38 @@ export function* transferCurrentCard(boardId, listId, index) {
|
|||||||
export function* duplicateCard(id, data) {
|
export function* duplicateCard(id, data) {
|
||||||
const localId = yield call(createLocalId);
|
const localId = yield call(createLocalId);
|
||||||
const { cardId: currentCardId } = yield select(selectors.selectPath);
|
const { cardId: currentCardId } = yield select(selectors.selectPath);
|
||||||
const { boardId, listId } = yield select(selectors.selectCardById, id);
|
const sourceCard = yield select(selectors.selectCardById, id);
|
||||||
const index = yield select(selectors.selectCardIndexById, id);
|
|
||||||
|
const boardId = data.boardId || sourceCard.boardId;
|
||||||
|
const listId = data.listId || sourceCard.listId;
|
||||||
|
|
||||||
|
const list = yield select(selectors.selectListById, listId);
|
||||||
|
const typeState = LIST_TYPE_STATE_BY_TYPE[list.type];
|
||||||
|
|
||||||
|
const nextData = {
|
||||||
|
...data,
|
||||||
|
};
|
||||||
|
|
||||||
|
if (!nextData.position && isListFinite(list)) {
|
||||||
|
const index = yield select(selectors.selectCardIndexById, id);
|
||||||
|
nextData.position = yield select(selectors.selectNextCardPosition, listId, index + 1);
|
||||||
|
}
|
||||||
|
|
||||||
const currentUserMembership = yield select(
|
const currentUserMembership = yield select(
|
||||||
selectors.selectCurrentUserMembershipByBoardId,
|
selectors.selectCurrentUserMembershipByBoardId,
|
||||||
boardId,
|
boardId,
|
||||||
);
|
);
|
||||||
|
|
||||||
const nextData = {
|
|
||||||
...data,
|
|
||||||
position: yield select(selectors.selectNextCardPosition, listId, index + 1),
|
|
||||||
};
|
|
||||||
|
|
||||||
yield put(
|
yield put(
|
||||||
actions.duplicateCard(id, localId, {
|
actions.duplicateCard(id, localId, {
|
||||||
...nextData,
|
...nextData,
|
||||||
creatorUserId: currentUserMembership.userId,
|
creatorUserId: currentUserMembership.userId,
|
||||||
|
isClosed: typeState === ListTypeStates.CLOSED,
|
||||||
|
...(sourceCard && {
|
||||||
|
name: `${sourceCard.name} (${i18n.t('common.copy', {
|
||||||
|
context: 'inline',
|
||||||
|
})})`,
|
||||||
|
}),
|
||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -481,6 +585,11 @@ export function* duplicateCard(id, data) {
|
|||||||
} = yield call(request, api.duplicateCard, id, nextData));
|
} = yield call(request, api.duplicateCard, id, nextData));
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
yield put(actions.duplicateCard.failure(localId, error));
|
yield put(actions.duplicateCard.failure(localId, error));
|
||||||
|
|
||||||
|
yield call(toast, {
|
||||||
|
type: ToastTypes.SOURCE_CARD_NOT_COPYABLE,
|
||||||
|
});
|
||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -516,6 +625,54 @@ export function* duplicateCurrentCard(data) {
|
|||||||
yield call(duplicateCard, cardId, data);
|
yield call(duplicateCard, cardId, data);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function* copyCard(id) {
|
||||||
|
yield put(actions.copyCard(id));
|
||||||
|
}
|
||||||
|
|
||||||
|
export function* cutCard(id) {
|
||||||
|
yield put(actions.cutCard(id));
|
||||||
|
}
|
||||||
|
|
||||||
|
export function* pasteCard(listId) {
|
||||||
|
const list = yield select(selectors.selectListById, listId);
|
||||||
|
const clipboard = yield select(selectors.selectClipboard);
|
||||||
|
const sourceCard = yield select(selectors.selectCardById, clipboard.cardId);
|
||||||
|
|
||||||
|
yield put(actions.pasteCard());
|
||||||
|
|
||||||
|
if (clipboard.type === ClipboardTypes.COPY) {
|
||||||
|
const data = {
|
||||||
|
listId,
|
||||||
|
};
|
||||||
|
if (!sourceCard || list.boardId !== sourceCard.boardId) {
|
||||||
|
data.boardId = list.boardId;
|
||||||
|
}
|
||||||
|
if (isListFinite(list)) {
|
||||||
|
data.position = yield select(selectors.selectNextCardPosition, listId);
|
||||||
|
}
|
||||||
|
|
||||||
|
yield call(duplicateCard, clipboard.cardId, data);
|
||||||
|
} else if (clipboard.type === ClipboardTypes.CUT) {
|
||||||
|
if (sourceCard && listId === sourceCard.listId) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
yield call(transferCard, clipboard.cardId, list.boardId, list.id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export function* pasteCardInCurrentContext() {
|
||||||
|
const listId = yield select(selectors.selectFirstKanbanListId);
|
||||||
|
|
||||||
|
yield call(pasteCard, listId);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function* pasteCardInCurrentList() {
|
||||||
|
const currentListId = yield select(selectors.selectCurrentListId);
|
||||||
|
|
||||||
|
yield call(pasteCard, currentListId);
|
||||||
|
}
|
||||||
|
|
||||||
export function* goToAdjacentCard(direction) {
|
export function* goToAdjacentCard(direction) {
|
||||||
const card = yield select(selectors.selectCurrentCard);
|
const card = yield select(selectors.selectCurrentCard);
|
||||||
const list = yield select(selectors.selectListById, card.listId);
|
const list = yield select(selectors.selectListById, card.listId);
|
||||||
@@ -617,6 +774,11 @@ export default {
|
|||||||
transferCurrentCard,
|
transferCurrentCard,
|
||||||
duplicateCard,
|
duplicateCard,
|
||||||
duplicateCurrentCard,
|
duplicateCurrentCard,
|
||||||
|
copyCard,
|
||||||
|
cutCard,
|
||||||
|
pasteCard,
|
||||||
|
pasteCardInCurrentContext,
|
||||||
|
pasteCardInCurrentList,
|
||||||
goToAdjacentCard,
|
goToAdjacentCard,
|
||||||
deleteCard,
|
deleteCard,
|
||||||
deleteCurrentCard,
|
deleteCurrentCard,
|
||||||
|
|||||||
@@ -67,6 +67,13 @@ export default function* cardsWatchers() {
|
|||||||
takeEvery(EntryActionTypes.CURRENT_CARD_DUPLICATE, ({ payload: { data } }) =>
|
takeEvery(EntryActionTypes.CURRENT_CARD_DUPLICATE, ({ payload: { data } }) =>
|
||||||
services.duplicateCurrentCard(data),
|
services.duplicateCurrentCard(data),
|
||||||
),
|
),
|
||||||
|
takeEvery(EntryActionTypes.CARD_COPY, ({ payload: { id } }) => services.copyCard(id)),
|
||||||
|
takeEvery(EntryActionTypes.CARD_CUT, ({ payload: { id } }) => services.cutCard(id)),
|
||||||
|
takeEvery(EntryActionTypes.CARD_PASTE, ({ payload: { listId } }) => services.pasteCard(listId)),
|
||||||
|
takeEvery(EntryActionTypes.CARD_IN_CURRENT_CONTEXT_PASTE, () =>
|
||||||
|
services.pasteCardInCurrentContext(),
|
||||||
|
),
|
||||||
|
takeEvery(EntryActionTypes.CARD_IN_CURRENT_LIST_PASTE, () => services.pasteCardInCurrentList()),
|
||||||
takeEvery(EntryActionTypes.TO_ADJACENT_CARD_GO, ({ payload: { direction } }) =>
|
takeEvery(EntryActionTypes.TO_ADJACENT_CARD_GO, ({ payload: { direction } }) =>
|
||||||
services.goToAdjacentCard(direction),
|
services.goToAdjacentCard(direction),
|
||||||
),
|
),
|
||||||
|
|||||||
@@ -11,6 +11,8 @@ export const selectIsFavoritesEnabled = ({ core: { isFavoritesEnabled } }) => is
|
|||||||
|
|
||||||
export const selectIsEditModeEnabled = ({ core: { isEditModeEnabled } }) => isEditModeEnabled;
|
export const selectIsEditModeEnabled = ({ core: { isEditModeEnabled } }) => isEditModeEnabled;
|
||||||
|
|
||||||
|
export const selectClipboard = ({ core: { clipboard } }) => clipboard;
|
||||||
|
|
||||||
export const selectConfig = ({ core: { config } }) => config;
|
export const selectConfig = ({ core: { config } }) => config;
|
||||||
|
|
||||||
export const selectRecentCardId = ({ core: { recentCardId } }) => recentCardId;
|
export const selectRecentCardId = ({ core: { recentCardId } }) => recentCardId;
|
||||||
@@ -31,6 +33,7 @@ export default {
|
|||||||
selectIsLogouting,
|
selectIsLogouting,
|
||||||
selectIsFavoritesEnabled,
|
selectIsFavoritesEnabled,
|
||||||
selectIsEditModeEnabled,
|
selectIsEditModeEnabled,
|
||||||
|
selectClipboard,
|
||||||
selectConfig,
|
selectConfig,
|
||||||
selectRecentCardId,
|
selectRecentCardId,
|
||||||
selectPrevCardId,
|
selectPrevCardId,
|
||||||
|
|||||||
@@ -26,18 +26,25 @@
|
|||||||
* application/json:
|
* application/json:
|
||||||
* schema:
|
* schema:
|
||||||
* type: object
|
* type: object
|
||||||
* required:
|
|
||||||
* - position
|
|
||||||
* - name
|
|
||||||
* properties:
|
* properties:
|
||||||
|
* boardId:
|
||||||
|
* type: string
|
||||||
|
* description: ID of the board to duplicate the card to
|
||||||
|
* example: "1357158568008091265"
|
||||||
|
* listId:
|
||||||
|
* type: string
|
||||||
|
* description: ID of the list to duplicate the card to
|
||||||
|
* example: "1357158568008091266"
|
||||||
* position:
|
* position:
|
||||||
* type: number
|
* type: number
|
||||||
* minimum: 0
|
* minimum: 0
|
||||||
|
* nullable: true
|
||||||
* description: Position for the duplicated card within the list
|
* description: Position for the duplicated card within the list
|
||||||
* example: 65536
|
* example: 65536
|
||||||
* name:
|
* name:
|
||||||
* type: string
|
* type: string
|
||||||
* maxLength: 1024
|
* maxLength: 1024
|
||||||
|
* nullable: true
|
||||||
* description: Name/title for the duplicated card
|
* description: Name/title for the duplicated card
|
||||||
* example: Implement user authentication (copy)
|
* example: Implement user authentication (copy)
|
||||||
* responses:
|
* responses:
|
||||||
@@ -113,6 +120,8 @@
|
|||||||
* $ref: '#/components/responses/Forbidden'
|
* $ref: '#/components/responses/Forbidden'
|
||||||
* 404:
|
* 404:
|
||||||
* $ref: '#/components/responses/NotFound'
|
* $ref: '#/components/responses/NotFound'
|
||||||
|
* 422:
|
||||||
|
* $ref: '#/components/responses/UnprocessableEntity'
|
||||||
*/
|
*/
|
||||||
|
|
||||||
const { idInput } = require('../../../utils/inputs');
|
const { idInput } = require('../../../utils/inputs');
|
||||||
@@ -124,6 +133,18 @@ const Errors = {
|
|||||||
CARD_NOT_FOUND: {
|
CARD_NOT_FOUND: {
|
||||||
cardNotFound: 'Card not found',
|
cardNotFound: 'Card not found',
|
||||||
},
|
},
|
||||||
|
BOARD_NOT_FOUND: {
|
||||||
|
boardNotFound: 'Board not found',
|
||||||
|
},
|
||||||
|
LIST_NOT_FOUND: {
|
||||||
|
listNotFound: 'List not found',
|
||||||
|
},
|
||||||
|
LIST_MUST_BE_PRESENT: {
|
||||||
|
listMustBePresent: 'List must be present',
|
||||||
|
},
|
||||||
|
POSITION_MUST_BE_PRESENT: {
|
||||||
|
positionMustBePresent: 'Position must be present',
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
@@ -132,15 +153,17 @@ module.exports = {
|
|||||||
...idInput,
|
...idInput,
|
||||||
required: true,
|
required: true,
|
||||||
},
|
},
|
||||||
|
boardId: idInput,
|
||||||
|
listId: idInput,
|
||||||
position: {
|
position: {
|
||||||
type: 'number',
|
type: 'number',
|
||||||
min: 0,
|
min: 0,
|
||||||
required: true,
|
allowNull: true,
|
||||||
},
|
},
|
||||||
name: {
|
name: {
|
||||||
type: 'string',
|
type: 'string',
|
||||||
maxLength: 1024,
|
maxLength: 1024,
|
||||||
required: true,
|
allowNull: true,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
@@ -151,6 +174,18 @@ module.exports = {
|
|||||||
cardNotFound: {
|
cardNotFound: {
|
||||||
responseType: 'notFound',
|
responseType: 'notFound',
|
||||||
},
|
},
|
||||||
|
boardNotFound: {
|
||||||
|
responseType: 'notFound',
|
||||||
|
},
|
||||||
|
listNotFound: {
|
||||||
|
responseType: 'notFound',
|
||||||
|
},
|
||||||
|
listMustBePresent: {
|
||||||
|
responseType: 'unprocessableEntity',
|
||||||
|
},
|
||||||
|
positionMustBePresent: {
|
||||||
|
responseType: 'unprocessableEntity',
|
||||||
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
async fn(inputs) {
|
async fn(inputs) {
|
||||||
@@ -160,24 +195,60 @@ module.exports = {
|
|||||||
.getPathToProjectById(inputs.id)
|
.getPathToProjectById(inputs.id)
|
||||||
.intercept('pathNotFound', () => Errors.CARD_NOT_FOUND);
|
.intercept('pathNotFound', () => Errors.CARD_NOT_FOUND);
|
||||||
|
|
||||||
const boardMembership = await BoardMembership.qm.getOneByBoardIdAndUserId(
|
const isProjectManager = await sails.helpers.users.isProjectManager(currentUser.id, project.id);
|
||||||
|
|
||||||
|
let boardMembership = await BoardMembership.qm.getOneByBoardIdAndUserId(
|
||||||
board.id,
|
board.id,
|
||||||
currentUser.id,
|
currentUser.id,
|
||||||
);
|
);
|
||||||
|
|
||||||
if (!boardMembership) {
|
if (!isProjectManager) {
|
||||||
throw Errors.CARD_NOT_FOUND; // Forbidden
|
if (!boardMembership) {
|
||||||
|
throw Errors.CARD_NOT_FOUND; // Forbidden
|
||||||
|
}
|
||||||
|
|
||||||
|
if (boardMembership.role !== BoardMembership.Roles.EDITOR) {
|
||||||
|
throw Errors.NOT_ENOUGH_RIGHTS;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: allow for endless lists?
|
let nextProject;
|
||||||
if (!sails.helpers.lists.isFinite(list)) {
|
let nextBoard;
|
||||||
throw Errors.NOT_ENOUGH_RIGHTS;
|
|
||||||
|
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) {
|
||||||
|
throw Errors.LIST_NOT_FOUND; // Forbidden
|
||||||
}
|
}
|
||||||
|
|
||||||
if (boardMembership.role !== BoardMembership.Roles.EDITOR) {
|
if (boardMembership.role !== BoardMembership.Roles.EDITOR) {
|
||||||
throw Errors.NOT_ENOUGH_RIGHTS;
|
throw Errors.NOT_ENOUGH_RIGHTS;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let nextList;
|
||||||
|
if (!_.isUndefined(inputs.listId)) {
|
||||||
|
nextList = await List.qm.getOneById(inputs.listId, {
|
||||||
|
boardId: (nextBoard || board).id,
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!nextList) {
|
||||||
|
throw Errors.LIST_NOT_FOUND;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
const values = _.pick(inputs, ['position', 'name']);
|
const values = _.pick(inputs, ['position', 'name']);
|
||||||
|
|
||||||
const {
|
const {
|
||||||
@@ -190,17 +261,23 @@ module.exports = {
|
|||||||
customFieldGroups,
|
customFieldGroups,
|
||||||
customFields,
|
customFields,
|
||||||
customFieldValues,
|
customFieldValues,
|
||||||
} = await sails.helpers.cards.duplicateOne.with({
|
} = await sails.helpers.cards.duplicateOne
|
||||||
project,
|
.with({
|
||||||
board,
|
project,
|
||||||
list,
|
board,
|
||||||
record: card,
|
list,
|
||||||
values: {
|
record: card,
|
||||||
...values,
|
values: {
|
||||||
creatorUser: currentUser,
|
...values,
|
||||||
},
|
project: nextProject,
|
||||||
request: this.req,
|
board: nextBoard,
|
||||||
});
|
list: nextList,
|
||||||
|
creatorUser: currentUser,
|
||||||
|
},
|
||||||
|
request: this.req,
|
||||||
|
})
|
||||||
|
.intercept('positionMustBeInValues', () => Errors.POSITION_MUST_BE_PRESENT)
|
||||||
|
.intercept('listMustBeInValues', () => Errors.LIST_MUST_BE_PRESENT);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
item: nextCard,
|
item: nextCard,
|
||||||
|
|||||||
185
server/api/helpers/cards/copy-custom-fields.js
Normal file
185
server/api/helpers/cards/copy-custom-fields.js
Normal file
@@ -0,0 +1,185 @@
|
|||||||
|
/*!
|
||||||
|
* 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: {
|
||||||
|
fromRecord: {
|
||||||
|
type: 'ref',
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
|
toRecord: {
|
||||||
|
type: 'ref',
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
|
detachBoardCustomFieldGroups: {
|
||||||
|
type: 'boolean',
|
||||||
|
defaultsTo: true,
|
||||||
|
},
|
||||||
|
detachBaseCustomFieldGroups: {
|
||||||
|
type: 'boolean',
|
||||||
|
defaultsTo: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
|
async fn(inputs) {
|
||||||
|
const boardCustomFieldGroups = inputs.detachBoardCustomFieldGroups
|
||||||
|
? await CustomFieldGroup.qm.getByBoardId(inputs.fromRecord.boardId)
|
||||||
|
: [];
|
||||||
|
|
||||||
|
const cardCustomFieldGroups = await CustomFieldGroup.qm.getByCardId(inputs.fromRecord.id);
|
||||||
|
|
||||||
|
const customFieldGroups = [...boardCustomFieldGroups, ...cardCustomFieldGroups];
|
||||||
|
const customFieldGroupIds = sails.helpers.utils.mapRecords(customFieldGroups);
|
||||||
|
|
||||||
|
const customFields = await CustomField.qm.getByCustomFieldGroupIds(customFieldGroupIds);
|
||||||
|
|
||||||
|
let customFieldGroupsByBaseCustomFieldGroupId;
|
||||||
|
let baseCustomFieldGroupById;
|
||||||
|
let customFieldsByBaseCustomFieldGroupId;
|
||||||
|
let nextCustomFieldsTotal = customFields.length;
|
||||||
|
|
||||||
|
if (inputs.detachBaseCustomFieldGroups) {
|
||||||
|
customFieldGroupsByBaseCustomFieldGroupId = _.groupBy(
|
||||||
|
customFieldGroups.filter(({ baseCustomFieldGroupId }) => baseCustomFieldGroupId),
|
||||||
|
'baseCustomFieldGroupId',
|
||||||
|
);
|
||||||
|
|
||||||
|
const baseCustomFieldGroupIds = Object.keys(customFieldGroupsByBaseCustomFieldGroupId);
|
||||||
|
|
||||||
|
if (baseCustomFieldGroupIds.length > 0) {
|
||||||
|
const baseCustomFieldGroups =
|
||||||
|
await BaseCustomFieldGroup.qm.getByIds(baseCustomFieldGroupIds);
|
||||||
|
|
||||||
|
baseCustomFieldGroupById = _.keyBy(baseCustomFieldGroups, 'id');
|
||||||
|
|
||||||
|
const baseCustomFields = await CustomField.qm.getByBaseCustomFieldGroupIds(
|
||||||
|
Object.keys(baseCustomFieldGroupById),
|
||||||
|
);
|
||||||
|
|
||||||
|
customFieldsByBaseCustomFieldGroupId = _.groupBy(
|
||||||
|
baseCustomFields,
|
||||||
|
'baseCustomFieldGroupId',
|
||||||
|
);
|
||||||
|
|
||||||
|
nextCustomFieldsTotal += Object.entries(customFieldGroupsByBaseCustomFieldGroupId).reduce(
|
||||||
|
(result, [baseCustomFieldGroupId, customFieldGroupsItem]) => {
|
||||||
|
const customFieldsItem = customFieldsByBaseCustomFieldGroupId[baseCustomFieldGroupId];
|
||||||
|
|
||||||
|
if (!customFieldsItem) {
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
return result + customFieldsItem.length * customFieldGroupsItem.length;
|
||||||
|
},
|
||||||
|
0,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const customFieldValues = await CustomFieldValue.qm.getByCardId(inputs.fromRecord.id);
|
||||||
|
|
||||||
|
const ids = await sails.helpers.utils.generateIds(
|
||||||
|
customFieldGroups.length + nextCustomFieldsTotal,
|
||||||
|
);
|
||||||
|
|
||||||
|
const nextCustomFieldGroupIdByCustomFieldGroupId = {};
|
||||||
|
const nextCustomFieldGroupsValues = customFieldGroups.map((customFieldGroup, index) => {
|
||||||
|
const id = ids.shift();
|
||||||
|
nextCustomFieldGroupIdByCustomFieldGroupId[customFieldGroup.id] = id;
|
||||||
|
|
||||||
|
const values = {
|
||||||
|
..._.pick(customFieldGroup, ['position']),
|
||||||
|
id,
|
||||||
|
cardId: inputs.toRecord.id,
|
||||||
|
position: customFieldGroup.boardId
|
||||||
|
? POSITION_GAP * (index + 1)
|
||||||
|
: customFieldGroup.position + POSITION_GAP * boardCustomFieldGroups.length,
|
||||||
|
};
|
||||||
|
|
||||||
|
if (inputs.detachBaseCustomFieldGroups) {
|
||||||
|
values.name =
|
||||||
|
customFieldGroup.name ||
|
||||||
|
baseCustomFieldGroupById[customFieldGroup.baseCustomFieldGroupId].name;
|
||||||
|
} else {
|
||||||
|
Object.assign(values, {
|
||||||
|
name: customFieldGroup.name,
|
||||||
|
baseCustomFieldGroupId: customFieldGroup.baseCustomFieldGroupId,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return values;
|
||||||
|
});
|
||||||
|
|
||||||
|
const nextCustomFieldGroups = await CustomFieldGroup.qm.create(nextCustomFieldGroupsValues);
|
||||||
|
|
||||||
|
const nextCustomFieldIdByCustomFieldId = {};
|
||||||
|
const nextCustomFieldsValues = customFields.map((customField) => {
|
||||||
|
const id = ids.shift();
|
||||||
|
nextCustomFieldIdByCustomFieldId[customField.id] = id;
|
||||||
|
|
||||||
|
return {
|
||||||
|
..._.pick(customField, ['position', 'name', 'showOnFrontOfCard']),
|
||||||
|
id,
|
||||||
|
customFieldGroupId:
|
||||||
|
nextCustomFieldGroupIdByCustomFieldGroupId[customField.customFieldGroupId],
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
if (inputs.detachBaseCustomFieldGroups) {
|
||||||
|
Object.entries(customFieldGroupsByBaseCustomFieldGroupId).forEach(
|
||||||
|
([baseCustomFieldGroupId, customFieldGroupsItem]) => {
|
||||||
|
const customFieldsItem = customFieldsByBaseCustomFieldGroupId[baseCustomFieldGroupId];
|
||||||
|
|
||||||
|
if (!customFieldsItem) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
customFieldGroupsItem.forEach((customFieldGroup) => {
|
||||||
|
customFieldsItem.forEach((customField) => {
|
||||||
|
const groupedId = `${customFieldGroup.id}:${customField.id}`;
|
||||||
|
const id = ids.shift();
|
||||||
|
|
||||||
|
nextCustomFieldIdByCustomFieldId[groupedId] = id;
|
||||||
|
|
||||||
|
nextCustomFieldsValues.push({
|
||||||
|
..._.pick(customField, ['position', 'name', 'showOnFrontOfCard']),
|
||||||
|
id,
|
||||||
|
customFieldGroupId: nextCustomFieldGroupIdByCustomFieldGroupId[customFieldGroup.id],
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
const nextCustomFields = await CustomField.qm.create(nextCustomFieldsValues);
|
||||||
|
|
||||||
|
const nextCustomFieldValuesValues = customFieldValues.map((customFieldValue) => {
|
||||||
|
const groupedId = `${customFieldValue.customFieldGroupId}:${customFieldValue.customFieldId}`;
|
||||||
|
|
||||||
|
return {
|
||||||
|
..._.pick(customFieldValue, ['content']),
|
||||||
|
cardId: inputs.toRecord.id,
|
||||||
|
customFieldGroupId:
|
||||||
|
nextCustomFieldGroupIdByCustomFieldGroupId[customFieldValue.customFieldGroupId] ||
|
||||||
|
customFieldValue.customFieldGroupId,
|
||||||
|
customFieldId:
|
||||||
|
nextCustomFieldIdByCustomFieldId[groupedId] ||
|
||||||
|
nextCustomFieldIdByCustomFieldId[customFieldValue.customFieldId] ||
|
||||||
|
customFieldValue.customFieldId,
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
const nextCustomFieldValues = await CustomFieldValue.qm.create(nextCustomFieldValuesValues);
|
||||||
|
|
||||||
|
return {
|
||||||
|
customFieldGroups: nextCustomFieldGroups,
|
||||||
|
customFields: nextCustomFields,
|
||||||
|
customFieldValues: nextCustomFieldValues,
|
||||||
|
};
|
||||||
|
},
|
||||||
|
};
|
||||||
@@ -198,11 +198,10 @@ module.exports = {
|
|||||||
}
|
}
|
||||||
|
|
||||||
customFieldsItem.forEach((customField) => {
|
customFieldsItem.forEach((customField) => {
|
||||||
|
const groupedId = `${customFieldGroup.id}:${customField.id}`;
|
||||||
const id = ids.shift();
|
const id = ids.shift();
|
||||||
|
|
||||||
nextCustomFieldIdByCustomFieldIdByCardId[cardId][
|
nextCustomFieldIdByCustomFieldIdByCardId[cardId][groupedId] = id;
|
||||||
`${customFieldGroup.id}:${customField.id}`
|
|
||||||
] = id;
|
|
||||||
|
|
||||||
nextCustomFieldsValues.push({
|
nextCustomFieldsValues.push({
|
||||||
..._.pick(customField, ['name', 'showOnFrontOfCard', 'position']),
|
..._.pick(customField, ['name', 'showOnFrontOfCard', 'position']),
|
||||||
@@ -223,11 +222,10 @@ module.exports = {
|
|||||||
}
|
}
|
||||||
|
|
||||||
customFieldsItem.forEach((customField) => {
|
customFieldsItem.forEach((customField) => {
|
||||||
|
const groupedId = `${customFieldGroup.id}:${customField.id}`;
|
||||||
const id = ids.shift();
|
const id = ids.shift();
|
||||||
|
|
||||||
nextCustomFieldIdByCustomFieldIdByCardId[customFieldGroup.cardId][
|
nextCustomFieldIdByCustomFieldIdByCardId[customFieldGroup.cardId][groupedId] = id;
|
||||||
`${customFieldGroup.id}:${customField.id}`
|
|
||||||
] = id;
|
|
||||||
|
|
||||||
nextCustomFieldsValues.push({
|
nextCustomFieldsValues.push({
|
||||||
..._.pick(customField, ['name', 'showOnFrontOfCard', 'position']),
|
..._.pick(customField, ['name', 'showOnFrontOfCard', 'position']),
|
||||||
@@ -262,10 +260,11 @@ module.exports = {
|
|||||||
nextCustomFieldIdByCustomFieldIdByCardId[customFieldValue.cardId];
|
nextCustomFieldIdByCustomFieldIdByCardId[customFieldValue.cardId];
|
||||||
|
|
||||||
if (nextCustomFieldIdByCustomFieldId) {
|
if (nextCustomFieldIdByCustomFieldId) {
|
||||||
|
const groupedId = `${customFieldValue.customFieldGroupId}:${customFieldValue.customFieldId}`;
|
||||||
|
|
||||||
const nextCustomFieldId =
|
const nextCustomFieldId =
|
||||||
nextCustomFieldIdByCustomFieldId[
|
nextCustomFieldIdByCustomFieldId[groupedId] ||
|
||||||
`${customFieldValue.customFieldGroupId}:${customFieldValue.customFieldId}`
|
nextCustomFieldIdByCustomFieldId[customFieldValue.customFieldId];
|
||||||
] || nextCustomFieldIdByCustomFieldId[customFieldValue.customFieldId];
|
|
||||||
|
|
||||||
if (nextCustomFieldId) {
|
if (nextCustomFieldId) {
|
||||||
updateValues.customFieldId = nextCustomFieldId;
|
updateValues.customFieldId = nextCustomFieldId;
|
||||||
|
|||||||
@@ -25,10 +25,6 @@ module.exports = {
|
|||||||
type: 'ref',
|
type: 'ref',
|
||||||
required: true,
|
required: true,
|
||||||
},
|
},
|
||||||
join: {
|
|
||||||
type: 'boolean',
|
|
||||||
defaultsTo: false,
|
|
||||||
},
|
|
||||||
request: {
|
request: {
|
||||||
type: 'ref',
|
type: 'ref',
|
||||||
},
|
},
|
||||||
@@ -36,25 +32,58 @@ module.exports = {
|
|||||||
|
|
||||||
exits: {
|
exits: {
|
||||||
positionMustBeInValues: {},
|
positionMustBeInValues: {},
|
||||||
|
boardInValuesMustBelongToProject: {},
|
||||||
|
listMustBeInValues: {},
|
||||||
|
listInValuesMustBelongToBoard: {},
|
||||||
},
|
},
|
||||||
|
|
||||||
async fn(inputs) {
|
async fn(inputs) {
|
||||||
const { values } = inputs;
|
const { values } = inputs;
|
||||||
|
|
||||||
if (values.list) {
|
if (values.project && values.project.id === inputs.project.id) {
|
||||||
const typeState = List.TYPE_STATE_BY_TYPE[values.list.type];
|
delete values.project;
|
||||||
|
}
|
||||||
|
|
||||||
if (inputs.record.isClosed) {
|
const project = values.project || inputs.project;
|
||||||
if (typeState === List.TypeStates.OPENED) {
|
|
||||||
values.isClosed = false;
|
if (values.board) {
|
||||||
}
|
if (values.board.projectId !== project.id) {
|
||||||
} else if (typeState === List.TypeStates.CLOSED) {
|
throw 'boardInValuesMustBelongToProject';
|
||||||
values.isClosed = true;
|
}
|
||||||
|
|
||||||
|
if (values.board.id === inputs.board.id) {
|
||||||
|
delete values.board;
|
||||||
|
} else {
|
||||||
|
values.boardId = values.board.id;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const board = values.board || inputs.board;
|
||||||
|
|
||||||
|
if (values.list) {
|
||||||
|
if (values.list.boardId !== board.id) {
|
||||||
|
throw 'listInValuesMustBelongToBoard';
|
||||||
|
}
|
||||||
|
|
||||||
|
if (values.list.id === inputs.list.id) {
|
||||||
|
delete values.list;
|
||||||
|
} else {
|
||||||
|
values.listId = values.list.id;
|
||||||
|
}
|
||||||
|
} else if (values.board) {
|
||||||
|
throw 'listMustBeInValues';
|
||||||
|
}
|
||||||
|
|
||||||
const list = values.list || inputs.list;
|
const list = values.list || inputs.list;
|
||||||
|
|
||||||
|
if (sails.helpers.lists.isFinite(list)) {
|
||||||
|
if (values.list && _.isUndefined(values.position)) {
|
||||||
|
throw 'positionMustBeInValues';
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
values.position = null;
|
||||||
|
}
|
||||||
|
|
||||||
if (sails.helpers.lists.isFinite(list)) {
|
if (sails.helpers.lists.isFinite(list)) {
|
||||||
if (_.isUndefined(values.position)) {
|
if (_.isUndefined(values.position)) {
|
||||||
throw 'positionMustBeInValues';
|
throw 'positionMustBeInValues';
|
||||||
@@ -95,9 +124,54 @@ module.exports = {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let labelIds;
|
||||||
|
if (values.board) {
|
||||||
|
const prevLabels = await sails.helpers.cards.getLabels(inputs.record.id);
|
||||||
|
|
||||||
|
const labels = await Label.qm.getByBoardId(values.board.id);
|
||||||
|
const labelByName = _.keyBy(labels, 'name');
|
||||||
|
|
||||||
|
labelIds = await Promise.all(
|
||||||
|
prevLabels.map(async (label) => {
|
||||||
|
if (labelByName[label.name]) {
|
||||||
|
return labelByName[label.name].id;
|
||||||
|
}
|
||||||
|
|
||||||
|
const { id } = await sails.helpers.labels.createOne.with({
|
||||||
|
project,
|
||||||
|
values: {
|
||||||
|
..._.omit(label, ['id', 'boardId', 'createdAt', 'updatedAt']),
|
||||||
|
board,
|
||||||
|
},
|
||||||
|
actorUser: values.creatorUser,
|
||||||
|
});
|
||||||
|
|
||||||
|
return id;
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (values.list) {
|
||||||
|
const typeState = List.TYPE_STATE_BY_TYPE[values.list.type];
|
||||||
|
|
||||||
|
if (inputs.record.isClosed) {
|
||||||
|
if (typeState === List.TypeStates.OPENED) {
|
||||||
|
values.isClosed = false;
|
||||||
|
}
|
||||||
|
} else if (typeState === List.TypeStates.CLOSED) {
|
||||||
|
values.isClosed = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!values.name) {
|
||||||
|
const t = sails.helpers.utils.makeTranslator(values.creatorUser.language);
|
||||||
|
values.name = `${inputs.record.name} (${t('copy')})`;
|
||||||
|
}
|
||||||
|
|
||||||
let card = await Card.qm.createOne({
|
let card = await Card.qm.createOne({
|
||||||
..._.pick(inputs.record, [
|
..._.pick(inputs.record, [
|
||||||
'boardId',
|
'boardId',
|
||||||
|
'listId',
|
||||||
'prevListId',
|
'prevListId',
|
||||||
'type',
|
'type',
|
||||||
'name',
|
'name',
|
||||||
@@ -108,12 +182,16 @@ module.exports = {
|
|||||||
'isClosed',
|
'isClosed',
|
||||||
]),
|
]),
|
||||||
...values,
|
...values,
|
||||||
listId: list.id,
|
|
||||||
creatorUserId: values.creatorUser.id,
|
creatorUserId: values.creatorUser.id,
|
||||||
listChangedAt: new Date().toISOString(),
|
listChangedAt: new Date().toISOString(),
|
||||||
});
|
});
|
||||||
|
|
||||||
const cardMemberships = await CardMembership.qm.getByCardId(inputs.record.id);
|
const boardMemberUserIds = await sails.helpers.boards.getMemberUserIds(card.boardId);
|
||||||
|
const boardMemberUserIdsSet = new Set(boardMemberUserIds);
|
||||||
|
|
||||||
|
const cardMemberships = await CardMembership.qm.getByCardId(inputs.record.id, {
|
||||||
|
userIdOrIds: boardMemberUserIds,
|
||||||
|
});
|
||||||
|
|
||||||
const cardMembershipsValues = cardMemberships.map((cardMembership) => ({
|
const cardMembershipsValues = cardMemberships.map((cardMembership) => ({
|
||||||
..._.pick(cardMembership, ['userId']),
|
..._.pick(cardMembership, ['userId']),
|
||||||
@@ -122,10 +200,13 @@ module.exports = {
|
|||||||
|
|
||||||
const nextCardMemberships = await CardMembership.qm.create(cardMembershipsValues);
|
const nextCardMemberships = await CardMembership.qm.create(cardMembershipsValues);
|
||||||
|
|
||||||
const cardLabels = await CardLabel.qm.getByCardId(inputs.record.id);
|
if (!values.board) {
|
||||||
|
const cardLabels = await CardLabel.qm.getByCardId(inputs.record.id);
|
||||||
|
labelIds = sails.helpers.utils.mapRecords(cardLabels, 'labelId');
|
||||||
|
}
|
||||||
|
|
||||||
const cardLabelsValues = cardLabels.map((cardLabel) => ({
|
const cardLabelsValues = labelIds.map((labelId) => ({
|
||||||
..._.pick(cardLabel, ['labelId']),
|
labelId,
|
||||||
cardId: card.id,
|
cardId: card.id,
|
||||||
}));
|
}));
|
||||||
|
|
||||||
@@ -137,15 +218,7 @@ module.exports = {
|
|||||||
const tasks = await Task.qm.getByTaskListIds(taskListIds);
|
const tasks = await Task.qm.getByTaskListIds(taskListIds);
|
||||||
const attachments = await Attachment.qm.getByCardId(inputs.record.id);
|
const attachments = await Attachment.qm.getByCardId(inputs.record.id);
|
||||||
|
|
||||||
const customFieldGroups = await CustomFieldGroup.qm.getByCardId(inputs.record.id);
|
const ids = await sails.helpers.utils.generateIds(taskLists.length + attachments.length);
|
||||||
const customFieldGroupIds = sails.helpers.utils.mapRecords(customFieldGroups);
|
|
||||||
|
|
||||||
const customFields = await CustomField.qm.getByCustomFieldGroupIds(customFieldGroupIds);
|
|
||||||
const customFieldValues = await CustomFieldValue.qm.getByCardId(inputs.record.id);
|
|
||||||
|
|
||||||
const ids = await sails.helpers.utils.generateIds(
|
|
||||||
taskLists.length + attachments.length + customFieldGroups.length + customFields.length,
|
|
||||||
);
|
|
||||||
|
|
||||||
const nextTaskListIdByTaskListId = {};
|
const nextTaskListIdByTaskListId = {};
|
||||||
const nextTaskListsValues = await taskLists.map((taskList) => {
|
const nextTaskListsValues = await taskLists.map((taskList) => {
|
||||||
@@ -162,8 +235,9 @@ module.exports = {
|
|||||||
const nextTaskLists = await TaskList.qm.create(nextTaskListsValues);
|
const nextTaskLists = await TaskList.qm.create(nextTaskListsValues);
|
||||||
|
|
||||||
const nextTasksValues = tasks.map((task) => ({
|
const nextTasksValues = tasks.map((task) => ({
|
||||||
..._.pick(task, ['linkedCardId', 'assigneeUserId', 'position', 'name', 'isCompleted']),
|
..._.pick(task, ['linkedCardId', 'position', 'name', 'isCompleted']),
|
||||||
taskListId: nextTaskListIdByTaskListId[task.taskListId],
|
taskListId: nextTaskListIdByTaskListId[task.taskListId],
|
||||||
|
assigneeUserId: boardMemberUserIdsSet.has(task.assigneeUserId) ? task.assigneeUserId : null,
|
||||||
}));
|
}));
|
||||||
|
|
||||||
const nextTasks = await Task.qm.create(nextTasksValues);
|
const nextTasks = await Task.qm.create(nextTasksValues);
|
||||||
@@ -193,47 +267,16 @@ module.exports = {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const nextCustomFieldGroupIdByCustomFieldGroupId = {};
|
const {
|
||||||
const nextCustomFieldGroupsValues = customFieldGroups.map((customFieldGroup) => {
|
customFieldGroups: nextCustomFieldGroups,
|
||||||
const id = ids.shift();
|
customFields: nextCustomFields,
|
||||||
nextCustomFieldGroupIdByCustomFieldGroupId[customFieldGroup.id] = id;
|
customFieldValues: nextCustomFieldValues,
|
||||||
|
} = await sails.helpers.cards.copyCustomFields(
|
||||||
return {
|
inputs.record,
|
||||||
..._.pick(customFieldGroup, ['baseCustomFieldGroupId', 'position', 'name']),
|
card,
|
||||||
id,
|
!!values.board,
|
||||||
cardId: card.id,
|
!!values.project,
|
||||||
};
|
);
|
||||||
});
|
|
||||||
|
|
||||||
const nextCustomFieldGroups = await CustomFieldGroup.qm.create(nextCustomFieldGroupsValues);
|
|
||||||
|
|
||||||
const nextCustomFieldIdByCustomFieldId = {};
|
|
||||||
const nextCustomFieldsValues = customFields.map((customField) => {
|
|
||||||
const id = ids.shift();
|
|
||||||
nextCustomFieldIdByCustomFieldId[customField.id] = id;
|
|
||||||
|
|
||||||
return {
|
|
||||||
..._.pick(customField, ['position', 'name', 'showOnFrontOfCard']),
|
|
||||||
id,
|
|
||||||
customFieldGroupId:
|
|
||||||
nextCustomFieldGroupIdByCustomFieldGroupId[customField.customFieldGroupId],
|
|
||||||
};
|
|
||||||
});
|
|
||||||
|
|
||||||
const nextCustomFields = await CustomField.qm.create(nextCustomFieldsValues);
|
|
||||||
|
|
||||||
const nextCustomFieldValuesValues = customFieldValues.map((customFieldValue) => ({
|
|
||||||
..._.pick(customFieldValue, ['content']),
|
|
||||||
cardId: card.id,
|
|
||||||
customFieldGroupId:
|
|
||||||
nextCustomFieldGroupIdByCustomFieldGroupId[customFieldValue.customFieldGroupId] ||
|
|
||||||
customFieldValue.customFieldGroupId,
|
|
||||||
customFieldId:
|
|
||||||
nextCustomFieldIdByCustomFieldId[customFieldValue.customFieldId] ||
|
|
||||||
customFieldValue.customFieldId,
|
|
||||||
}));
|
|
||||||
|
|
||||||
const nextCustomFieldValues = await CustomFieldValue.qm.create(nextCustomFieldValuesValues);
|
|
||||||
|
|
||||||
sails.sockets.broadcast(
|
sails.sockets.broadcast(
|
||||||
`board:${card.boardId}`,
|
`board:${card.boardId}`,
|
||||||
@@ -252,8 +295,8 @@ module.exports = {
|
|||||||
buildData: () => ({
|
buildData: () => ({
|
||||||
item: card,
|
item: card,
|
||||||
included: {
|
included: {
|
||||||
projects: [inputs.project],
|
projects: [project],
|
||||||
boards: [inputs.board],
|
boards: [board],
|
||||||
lists: [list],
|
lists: [list],
|
||||||
cardMemberships: nextCardMemberships,
|
cardMemberships: nextCardMemberships,
|
||||||
cardLabels: nextCardLabels,
|
cardLabels: nextCardLabels,
|
||||||
@@ -291,6 +334,8 @@ module.exports = {
|
|||||||
}
|
}
|
||||||
|
|
||||||
await sails.helpers.actions.createOne.with({
|
await sails.helpers.actions.createOne.with({
|
||||||
|
project,
|
||||||
|
board,
|
||||||
list,
|
list,
|
||||||
webhooks,
|
webhooks,
|
||||||
values: {
|
values: {
|
||||||
@@ -302,8 +347,6 @@ module.exports = {
|
|||||||
},
|
},
|
||||||
user: values.creatorUser,
|
user: values.creatorUser,
|
||||||
},
|
},
|
||||||
project: inputs.project,
|
|
||||||
board: inputs.board,
|
|
||||||
});
|
});
|
||||||
|
|
||||||
return {
|
return {
|
||||||
|
|||||||
@@ -98,7 +98,11 @@ module.exports = {
|
|||||||
throw 'coverAttachmentInValuesMustContainImage';
|
throw 'coverAttachmentInValuesMustContainImage';
|
||||||
}
|
}
|
||||||
|
|
||||||
values.coverAttachmentId = values.coverAttachment.id;
|
if (values.coverAttachment.id === inputs.record.coverAttachmentId) {
|
||||||
|
delete values.coverAttachment;
|
||||||
|
} else {
|
||||||
|
values.coverAttachmentId = values.coverAttachment.id;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const dueDate = _.isUndefined(values.dueDate) ? inputs.record.dueDate : values.dueDate;
|
const dueDate = _.isUndefined(values.dueDate) ? inputs.record.dueDate : values.dueDate;
|
||||||
@@ -289,9 +293,14 @@ module.exports = {
|
|||||||
inputs.request,
|
inputs.request,
|
||||||
);
|
);
|
||||||
|
|
||||||
sails.sockets.broadcast(`board:${card.boardId}`, 'cardUpdate', {
|
sails.sockets.broadcast(
|
||||||
item: card,
|
`board:${card.boardId}`,
|
||||||
});
|
'cardUpdate',
|
||||||
|
{
|
||||||
|
item: card,
|
||||||
|
},
|
||||||
|
inputs.request,
|
||||||
|
);
|
||||||
|
|
||||||
// TODO: add transfer action
|
// TODO: add transfer action
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
@@ -13,10 +13,16 @@ const createOne = (values) => CardMembership.create({ ...values }).fetch();
|
|||||||
|
|
||||||
const getByIds = (ids) => defaultFind(ids);
|
const getByIds = (ids) => defaultFind(ids);
|
||||||
|
|
||||||
const getByCardId = (cardId) =>
|
const getByCardId = (cardId, { userIdOrIds } = {}) => {
|
||||||
defaultFind({
|
const criteria = {
|
||||||
cardId,
|
cardId,
|
||||||
});
|
};
|
||||||
|
if (userIdOrIds) {
|
||||||
|
criteria.userId = userIdOrIds;
|
||||||
|
}
|
||||||
|
|
||||||
|
return defaultFind(criteria);
|
||||||
|
};
|
||||||
|
|
||||||
const getByCardIds = (cardIds) =>
|
const getByCardIds = (cardIds) =>
|
||||||
defaultFind({
|
defaultFind({
|
||||||
|
|||||||
@@ -2,6 +2,7 @@
|
|||||||
"Archive": "أرشيف",
|
"Archive": "أرشيف",
|
||||||
"Card Created": "تم إنشاء البطاقة",
|
"Card Created": "تم إنشاء البطاقة",
|
||||||
"Card Moved": "تم نقل البطاقة",
|
"Card Moved": "تم نقل البطاقة",
|
||||||
|
"copy": "نسخة",
|
||||||
"New Comment": "تعليق جديد",
|
"New Comment": "تعليق جديد",
|
||||||
"Test Title": "عنوان تجريبي",
|
"Test Title": "عنوان تجريبي",
|
||||||
"This is a test text message!": "هذه رسالة نصية تجريبية!",
|
"This is a test text message!": "هذه رسالة نصية تجريبية!",
|
||||||
|
|||||||
@@ -2,6 +2,7 @@
|
|||||||
"Archive": "Архив",
|
"Archive": "Архив",
|
||||||
"Card Created": "Картата е създадена",
|
"Card Created": "Картата е създадена",
|
||||||
"Card Moved": "Картата е преместена",
|
"Card Moved": "Картата е преместена",
|
||||||
|
"copy": "копие",
|
||||||
"New Comment": "Нов коментар",
|
"New Comment": "Нов коментар",
|
||||||
"Test Title": "Тестово заглавие",
|
"Test Title": "Тестово заглавие",
|
||||||
"This is a test text message!": "Това е тестово текстово съобщение!",
|
"This is a test text message!": "Това е тестово текстово съобщение!",
|
||||||
|
|||||||
@@ -2,6 +2,7 @@
|
|||||||
"Archive": "Arxivar",
|
"Archive": "Arxivar",
|
||||||
"Card Created": "Targeta creada",
|
"Card Created": "Targeta creada",
|
||||||
"Card Moved": "Targeta moguda",
|
"Card Moved": "Targeta moguda",
|
||||||
|
"copy": "còpia",
|
||||||
"New Comment": "Comentari nou",
|
"New Comment": "Comentari nou",
|
||||||
"Test Title": "Títol de prova",
|
"Test Title": "Títol de prova",
|
||||||
"This is a test text message!": "Aquest és un missatge de text de prova!",
|
"This is a test text message!": "Aquest és un missatge de text de prova!",
|
||||||
|
|||||||
@@ -2,6 +2,7 @@
|
|||||||
"Archive": "Archiv",
|
"Archive": "Archiv",
|
||||||
"Card Created": "Karta vytvořena",
|
"Card Created": "Karta vytvořena",
|
||||||
"Card Moved": "Karta přesunuta",
|
"Card Moved": "Karta přesunuta",
|
||||||
|
"copy": "kopie",
|
||||||
"New Comment": "Nový komentář",
|
"New Comment": "Nový komentář",
|
||||||
"Test Title": "Testovací název",
|
"Test Title": "Testovací název",
|
||||||
"This is a test text message!": "Toto je testovací textová zpráva!",
|
"This is a test text message!": "Toto je testovací textová zpráva!",
|
||||||
|
|||||||
@@ -2,6 +2,7 @@
|
|||||||
"Archive": "Arkiv",
|
"Archive": "Arkiv",
|
||||||
"Card Created": "Kort oprettet",
|
"Card Created": "Kort oprettet",
|
||||||
"Card Moved": "Kort flyttet",
|
"Card Moved": "Kort flyttet",
|
||||||
|
"copy": "kopi",
|
||||||
"New Comment": "Ny kommentar",
|
"New Comment": "Ny kommentar",
|
||||||
"Test Title": "Test titel",
|
"Test Title": "Test titel",
|
||||||
"This is a test text message!": "Dette er en test tekstbesked!",
|
"This is a test text message!": "Dette er en test tekstbesked!",
|
||||||
|
|||||||
@@ -2,6 +2,7 @@
|
|||||||
"Archive": "Archiv",
|
"Archive": "Archiv",
|
||||||
"Card Created": "Karte erstellt",
|
"Card Created": "Karte erstellt",
|
||||||
"Card Moved": "Karte verschoben",
|
"Card Moved": "Karte verschoben",
|
||||||
|
"copy": "Kopie",
|
||||||
"New Comment": "Neuer Kommentar",
|
"New Comment": "Neuer Kommentar",
|
||||||
"Test Title": "Testtitel",
|
"Test Title": "Testtitel",
|
||||||
"This is a test text message!": "Dies ist eine Test-Textnachricht!",
|
"This is a test text message!": "Dies ist eine Test-Textnachricht!",
|
||||||
|
|||||||
@@ -2,6 +2,7 @@
|
|||||||
"Archive": "Αρχείο",
|
"Archive": "Αρχείο",
|
||||||
"Card Created": "Η κάρτα δημιουργήθηκε",
|
"Card Created": "Η κάρτα δημιουργήθηκε",
|
||||||
"Card Moved": "Η κάρτα μετακινήθηκε",
|
"Card Moved": "Η κάρτα μετακινήθηκε",
|
||||||
|
"copy": "αντίγραφο",
|
||||||
"New Comment": "Νέο σχόλιο",
|
"New Comment": "Νέο σχόλιο",
|
||||||
"Test Title": "Τίτλος δοκιμής",
|
"Test Title": "Τίτλος δοκιμής",
|
||||||
"This is a test text message!": "Αυτό είναι ένα δοκιμαστικό μήνυμα!",
|
"This is a test text message!": "Αυτό είναι ένα δοκιμαστικό μήνυμα!",
|
||||||
|
|||||||
@@ -2,6 +2,7 @@
|
|||||||
"Archive": "Archive",
|
"Archive": "Archive",
|
||||||
"Card Created": "Card Created",
|
"Card Created": "Card Created",
|
||||||
"Card Moved": "Card Moved",
|
"Card Moved": "Card Moved",
|
||||||
|
"copy": "copy",
|
||||||
"New Comment": "New Comment",
|
"New Comment": "New Comment",
|
||||||
"Test Title": "Test Title",
|
"Test Title": "Test Title",
|
||||||
"This is a test text message!": "This is a test text message!",
|
"This is a test text message!": "This is a test text message!",
|
||||||
|
|||||||
@@ -2,6 +2,7 @@
|
|||||||
"Archive": "Archive",
|
"Archive": "Archive",
|
||||||
"Card Created": "Card Created",
|
"Card Created": "Card Created",
|
||||||
"Card Moved": "Card Moved",
|
"Card Moved": "Card Moved",
|
||||||
|
"copy": "copy",
|
||||||
"New Comment": "New Comment",
|
"New Comment": "New Comment",
|
||||||
"Test Title": "Test Title",
|
"Test Title": "Test Title",
|
||||||
"This is a test text message!": "This is a test text message!",
|
"This is a test text message!": "This is a test text message!",
|
||||||
|
|||||||
@@ -2,6 +2,7 @@
|
|||||||
"Archive": "Archivo",
|
"Archive": "Archivo",
|
||||||
"Card Created": "Tarjeta creada",
|
"Card Created": "Tarjeta creada",
|
||||||
"Card Moved": "Tarjeta movida",
|
"Card Moved": "Tarjeta movida",
|
||||||
|
"copy": "copia",
|
||||||
"New Comment": "Nuevo comentario",
|
"New Comment": "Nuevo comentario",
|
||||||
"Test Title": "Título de prueba",
|
"Test Title": "Título de prueba",
|
||||||
"This is a test text message!": "¡Este es un mensaje de texto de prueba!",
|
"This is a test text message!": "¡Este es un mensaje de texto de prueba!",
|
||||||
|
|||||||
@@ -2,6 +2,7 @@
|
|||||||
"Archive": "Arhiiv",
|
"Archive": "Arhiiv",
|
||||||
"Card Created": "Kaart loodud",
|
"Card Created": "Kaart loodud",
|
||||||
"Card Moved": "Kaart liigutatud",
|
"Card Moved": "Kaart liigutatud",
|
||||||
|
"copy": "koopia",
|
||||||
"New Comment": "Uus kommentaar",
|
"New Comment": "Uus kommentaar",
|
||||||
"Test Title": "Testi pealkiri",
|
"Test Title": "Testi pealkiri",
|
||||||
"This is a test text message!": "See on testi tekstisõnum!",
|
"This is a test text message!": "See on testi tekstisõnum!",
|
||||||
|
|||||||
@@ -2,6 +2,7 @@
|
|||||||
"Archive": "بایگانی",
|
"Archive": "بایگانی",
|
||||||
"Card Created": "کارت ایجاد شد",
|
"Card Created": "کارت ایجاد شد",
|
||||||
"Card Moved": "کارت منتقل شد",
|
"Card Moved": "کارت منتقل شد",
|
||||||
|
"copy": "کپی",
|
||||||
"New Comment": "نظر جدید",
|
"New Comment": "نظر جدید",
|
||||||
"Test Title": "عنوان آزمایشی",
|
"Test Title": "عنوان آزمایشی",
|
||||||
"This is a test text message!": "این یک پیام متنی آزمایشی است!",
|
"This is a test text message!": "این یک پیام متنی آزمایشی است!",
|
||||||
|
|||||||
@@ -2,6 +2,7 @@
|
|||||||
"Archive": "Arkisto",
|
"Archive": "Arkisto",
|
||||||
"Card Created": "Kortti luotu",
|
"Card Created": "Kortti luotu",
|
||||||
"Card Moved": "Kortti siirretty",
|
"Card Moved": "Kortti siirretty",
|
||||||
|
"copy": "kopio",
|
||||||
"New Comment": "Uusi kommentti",
|
"New Comment": "Uusi kommentti",
|
||||||
"Test Title": "Testin otsikko",
|
"Test Title": "Testin otsikko",
|
||||||
"This is a test text message!": "Tämä on testiviesti!",
|
"This is a test text message!": "Tämä on testiviesti!",
|
||||||
|
|||||||
@@ -2,6 +2,7 @@
|
|||||||
"Archive": "Archive",
|
"Archive": "Archive",
|
||||||
"Card Created": "Carte créée",
|
"Card Created": "Carte créée",
|
||||||
"Card Moved": "Carte déplacée",
|
"Card Moved": "Carte déplacée",
|
||||||
|
"copy": "copie",
|
||||||
"New Comment": "Nouveau commentaire",
|
"New Comment": "Nouveau commentaire",
|
||||||
"Test Title": "Titre de test",
|
"Test Title": "Titre de test",
|
||||||
"This is a test text message!": "Ceci est un message texte de test !",
|
"This is a test text message!": "Ceci est un message texte de test !",
|
||||||
|
|||||||
@@ -2,6 +2,7 @@
|
|||||||
"Archive": "Archívum",
|
"Archive": "Archívum",
|
||||||
"Card Created": "Kártya létrehozva",
|
"Card Created": "Kártya létrehozva",
|
||||||
"Card Moved": "Kártya áthelyezve",
|
"Card Moved": "Kártya áthelyezve",
|
||||||
|
"copy": "másolat",
|
||||||
"New Comment": "Új hozzászólás",
|
"New Comment": "Új hozzászólás",
|
||||||
"Test Title": "Teszt cím",
|
"Test Title": "Teszt cím",
|
||||||
"This is a test text message!": "Ez itt egy szöveges teszt üzenet!",
|
"This is a test text message!": "Ez itt egy szöveges teszt üzenet!",
|
||||||
|
|||||||
@@ -2,6 +2,7 @@
|
|||||||
"Archive": "Arsip",
|
"Archive": "Arsip",
|
||||||
"Card Created": "Kartu dibuat",
|
"Card Created": "Kartu dibuat",
|
||||||
"Card Moved": "Kartu dipindahkan",
|
"Card Moved": "Kartu dipindahkan",
|
||||||
|
"copy": "salinan",
|
||||||
"New Comment": "Komentar baru",
|
"New Comment": "Komentar baru",
|
||||||
"Test Title": "Judul tes",
|
"Test Title": "Judul tes",
|
||||||
"This is a test text message!": "Ini adalah pesan teks tes!",
|
"This is a test text message!": "Ini adalah pesan teks tes!",
|
||||||
|
|||||||
@@ -2,6 +2,7 @@
|
|||||||
"Archive": "Archivio",
|
"Archive": "Archivio",
|
||||||
"Card Created": "Nuova task creata",
|
"Card Created": "Nuova task creata",
|
||||||
"Card Moved": "Task spostata",
|
"Card Moved": "Task spostata",
|
||||||
|
"copy": "copia",
|
||||||
"New Comment": "Nuovo commento",
|
"New Comment": "Nuovo commento",
|
||||||
"Test Title": "Titolo di test",
|
"Test Title": "Titolo di test",
|
||||||
"This is a test text message!": "Questo è un messaggio di testo di test!",
|
"This is a test text message!": "Questo è un messaggio di testo di test!",
|
||||||
|
|||||||
@@ -2,6 +2,7 @@
|
|||||||
"Archive": "アーカイブ",
|
"Archive": "アーカイブ",
|
||||||
"Card Created": "カードが作成されました",
|
"Card Created": "カードが作成されました",
|
||||||
"Card Moved": "カードが移動されました",
|
"Card Moved": "カードが移動されました",
|
||||||
|
"copy": "コピー",
|
||||||
"New Comment": "新しいコメント",
|
"New Comment": "新しいコメント",
|
||||||
"Test Title": "テストタイトル",
|
"Test Title": "テストタイトル",
|
||||||
"This is a test text message!": "これはテストテキストメッセージです!",
|
"This is a test text message!": "これはテストテキストメッセージです!",
|
||||||
|
|||||||
@@ -2,6 +2,7 @@
|
|||||||
"Archive": "보관함",
|
"Archive": "보관함",
|
||||||
"Card Created": "카드가 생성됨",
|
"Card Created": "카드가 생성됨",
|
||||||
"Card Moved": "카드가 이동됨",
|
"Card Moved": "카드가 이동됨",
|
||||||
|
"copy": "복사본",
|
||||||
"New Comment": "새 댓글",
|
"New Comment": "새 댓글",
|
||||||
"Test Title": "테스트 제목",
|
"Test Title": "테스트 제목",
|
||||||
"This is a test text message!": "이것은 테스트 텍스트 메시지입니다!",
|
"This is a test text message!": "이것은 테스트 텍스트 메시지입니다!",
|
||||||
|
|||||||
@@ -2,6 +2,7 @@
|
|||||||
"Archive": "Archief",
|
"Archive": "Archief",
|
||||||
"Card Created": "Kaart aangemaakt",
|
"Card Created": "Kaart aangemaakt",
|
||||||
"Card Moved": "Kaart verplaatst",
|
"Card Moved": "Kaart verplaatst",
|
||||||
|
"copy": "kopie",
|
||||||
"New Comment": "Nieuwe reactie",
|
"New Comment": "Nieuwe reactie",
|
||||||
"Test Title": "Test titel",
|
"Test Title": "Test titel",
|
||||||
"This is a test text message!": "Dit is een test tekstbericht!",
|
"This is a test text message!": "Dit is een test tekstbericht!",
|
||||||
|
|||||||
@@ -2,6 +2,7 @@
|
|||||||
"Archive": "Archiwum",
|
"Archive": "Archiwum",
|
||||||
"Card Created": "Karta utworzona",
|
"Card Created": "Karta utworzona",
|
||||||
"Card Moved": "Karta przeniesiona",
|
"Card Moved": "Karta przeniesiona",
|
||||||
|
"copy": "kopia",
|
||||||
"New Comment": "Nowy komentarz",
|
"New Comment": "Nowy komentarz",
|
||||||
"Test Title": "Tytuł testowy",
|
"Test Title": "Tytuł testowy",
|
||||||
"This is a test text message!": "To jest testowa wiadomość tekstowa!",
|
"This is a test text message!": "To jest testowa wiadomość tekstowa!",
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user