@@ -1,3 +1,8 @@
|
||||
/*!
|
||||
* Copyright (c) 2024 PLANKA Software GmbH
|
||||
* Licensed under the Fair Use License: https://github.com/plankanban/planka/blob/master/LICENSE.md
|
||||
*/
|
||||
|
||||
import ActionTypes from '../constants/ActionTypes';
|
||||
|
||||
const fetchActivities = (cardId) => ({
|
||||
@@ -24,31 +29,6 @@ fetchActivities.failure = (cardId, error) => ({
|
||||
},
|
||||
});
|
||||
|
||||
const toggleActivitiesDetails = (cardId, isVisible) => ({
|
||||
type: ActionTypes.ACTIVITIES_DETAILS_TOGGLE,
|
||||
payload: {
|
||||
cardId,
|
||||
isVisible,
|
||||
},
|
||||
});
|
||||
|
||||
toggleActivitiesDetails.success = (cardId, activities, users) => ({
|
||||
type: ActionTypes.ACTIVITIES_DETAILS_TOGGLE__SUCCESS,
|
||||
payload: {
|
||||
cardId,
|
||||
activities,
|
||||
users,
|
||||
},
|
||||
});
|
||||
|
||||
toggleActivitiesDetails.failure = (cardId, error) => ({
|
||||
type: ActionTypes.ACTIVITIES_DETAILS_TOGGLE__FAILURE,
|
||||
payload: {
|
||||
cardId,
|
||||
error,
|
||||
},
|
||||
});
|
||||
|
||||
const handleActivityCreate = (activity) => ({
|
||||
type: ActionTypes.ACTIVITY_CREATE_HANDLE,
|
||||
payload: {
|
||||
@@ -56,24 +36,7 @@ const handleActivityCreate = (activity) => ({
|
||||
},
|
||||
});
|
||||
|
||||
const handleActivityUpdate = (activity) => ({
|
||||
type: ActionTypes.ACTIVITY_UPDATE_HANDLE,
|
||||
payload: {
|
||||
activity,
|
||||
},
|
||||
});
|
||||
|
||||
const handleActivityDelete = (activity) => ({
|
||||
type: ActionTypes.ACTIVITY_DELETE_HANDLE,
|
||||
payload: {
|
||||
activity,
|
||||
},
|
||||
});
|
||||
|
||||
export default {
|
||||
fetchActivities,
|
||||
toggleActivitiesDetails,
|
||||
handleActivityCreate,
|
||||
handleActivityUpdate,
|
||||
handleActivityDelete,
|
||||
};
|
||||
|
||||
@@ -1,3 +1,8 @@
|
||||
/*!
|
||||
* Copyright (c) 2024 PLANKA Software GmbH
|
||||
* Licensed under the Fair Use License: https://github.com/plankanban/planka/blob/master/LICENSE.md
|
||||
*/
|
||||
|
||||
import ActionTypes from '../constants/ActionTypes';
|
||||
|
||||
const createAttachment = (attachment) => ({
|
||||
|
||||
@@ -0,0 +1,72 @@
|
||||
/*!
|
||||
* Copyright (c) 2024 PLANKA Software GmbH
|
||||
* Licensed under the Fair Use License: https://github.com/plankanban/planka/blob/master/LICENSE.md
|
||||
*/
|
||||
|
||||
import ActionTypes from '../constants/ActionTypes';
|
||||
|
||||
const createBackgroundImage = (backgroundImage) => ({
|
||||
type: ActionTypes.BACKGROUND_IMAGE_CREATE,
|
||||
payload: {
|
||||
backgroundImage,
|
||||
},
|
||||
});
|
||||
|
||||
createBackgroundImage.success = (localId, backgroundImage) => ({
|
||||
type: ActionTypes.BACKGROUND_IMAGE_CREATE__SUCCESS,
|
||||
payload: {
|
||||
localId,
|
||||
backgroundImage,
|
||||
},
|
||||
});
|
||||
|
||||
createBackgroundImage.failure = (localId, error) => ({
|
||||
type: ActionTypes.BACKGROUND_IMAGE_CREATE__FAILURE,
|
||||
payload: {
|
||||
localId,
|
||||
error,
|
||||
},
|
||||
});
|
||||
|
||||
const handleBackgroundImageCreate = (backgroundImage) => ({
|
||||
type: ActionTypes.BACKGROUND_IMAGE_CREATE_HANDLE,
|
||||
payload: {
|
||||
backgroundImage,
|
||||
},
|
||||
});
|
||||
|
||||
const deleteBackgroundImage = (id) => ({
|
||||
type: ActionTypes.BACKGROUND_IMAGE_DELETE,
|
||||
payload: {
|
||||
id,
|
||||
},
|
||||
});
|
||||
|
||||
deleteBackgroundImage.success = (backgroundImage) => ({
|
||||
type: ActionTypes.BACKGROUND_IMAGE_DELETE__SUCCESS,
|
||||
payload: {
|
||||
backgroundImage,
|
||||
},
|
||||
});
|
||||
|
||||
deleteBackgroundImage.failure = (id, error) => ({
|
||||
type: ActionTypes.BACKGROUND_IMAGE_DELETE__FAILURE,
|
||||
payload: {
|
||||
id,
|
||||
error,
|
||||
},
|
||||
});
|
||||
|
||||
const handleBackgroundImageDelete = (backgroundImage) => ({
|
||||
type: ActionTypes.BACKGROUND_IMAGE_DELETE_HANDLE,
|
||||
payload: {
|
||||
backgroundImage,
|
||||
},
|
||||
});
|
||||
|
||||
export default {
|
||||
createBackgroundImage,
|
||||
handleBackgroundImageCreate,
|
||||
deleteBackgroundImage,
|
||||
handleBackgroundImageDelete,
|
||||
};
|
||||
@@ -0,0 +1,104 @@
|
||||
/*!
|
||||
* Copyright (c) 2024 PLANKA Software GmbH
|
||||
* Licensed under the Fair Use License: https://github.com/plankanban/planka/blob/master/LICENSE.md
|
||||
*/
|
||||
|
||||
import ActionTypes from '../constants/ActionTypes';
|
||||
|
||||
const createBaseCustomFieldGroup = (baseCustomFieldGroup) => ({
|
||||
type: ActionTypes.BASE_CUSTOM_FIELD_GROUP_CREATE,
|
||||
payload: {
|
||||
baseCustomFieldGroup,
|
||||
},
|
||||
});
|
||||
|
||||
createBaseCustomFieldGroup.success = (localId, baseCustomFieldGroup) => ({
|
||||
type: ActionTypes.BASE_CUSTOM_FIELD_GROUP_CREATE__SUCCESS,
|
||||
payload: {
|
||||
localId,
|
||||
baseCustomFieldGroup,
|
||||
},
|
||||
});
|
||||
|
||||
createBaseCustomFieldGroup.failure = (localId, error) => ({
|
||||
type: ActionTypes.BASE_CUSTOM_FIELD_GROUP_CREATE__FAILURE,
|
||||
payload: {
|
||||
localId,
|
||||
error,
|
||||
},
|
||||
});
|
||||
|
||||
const handleBaseCustomFieldGroupCreate = (baseCustomFieldGroup) => ({
|
||||
type: ActionTypes.BASE_CUSTOM_FIELD_GROUP_CREATE_HANDLE,
|
||||
payload: {
|
||||
baseCustomFieldGroup,
|
||||
},
|
||||
});
|
||||
|
||||
const updateBaseCustomFieldGroup = (id, data) => ({
|
||||
type: ActionTypes.BASE_CUSTOM_FIELD_GROUP_UPDATE,
|
||||
payload: {
|
||||
id,
|
||||
data,
|
||||
},
|
||||
});
|
||||
|
||||
updateBaseCustomFieldGroup.success = (baseCustomFieldGroup) => ({
|
||||
type: ActionTypes.BASE_CUSTOM_FIELD_GROUP_UPDATE__SUCCESS,
|
||||
payload: {
|
||||
baseCustomFieldGroup,
|
||||
},
|
||||
});
|
||||
|
||||
updateBaseCustomFieldGroup.failure = (id, error) => ({
|
||||
type: ActionTypes.BASE_CUSTOM_FIELD_GROUP_UPDATE__FAILURE,
|
||||
payload: {
|
||||
id,
|
||||
error,
|
||||
},
|
||||
});
|
||||
|
||||
const handleBaseCustomFieldGroupUpdate = (baseCustomFieldGroup) => ({
|
||||
type: ActionTypes.BASE_CUSTOM_FIELD_GROUP_UPDATE_HANDLE,
|
||||
payload: {
|
||||
baseCustomFieldGroup,
|
||||
},
|
||||
});
|
||||
|
||||
const deleteBaseCustomFieldGroup = (id) => ({
|
||||
type: ActionTypes.BASE_CUSTOM_FIELD_GROUP_DELETE,
|
||||
payload: {
|
||||
id,
|
||||
},
|
||||
});
|
||||
|
||||
deleteBaseCustomFieldGroup.success = (baseCustomFieldGroup) => ({
|
||||
type: ActionTypes.BASE_CUSTOM_FIELD_GROUP_DELETE__SUCCESS,
|
||||
payload: {
|
||||
baseCustomFieldGroup,
|
||||
},
|
||||
});
|
||||
|
||||
deleteBaseCustomFieldGroup.failure = (id, error) => ({
|
||||
type: ActionTypes.BASE_CUSTOM_FIELD_GROUP_DELETE__FAILURE,
|
||||
payload: {
|
||||
id,
|
||||
error,
|
||||
},
|
||||
});
|
||||
|
||||
const handleBaseCustomFieldGroupDelete = (baseCustomFieldGroup) => ({
|
||||
type: ActionTypes.BASE_CUSTOM_FIELD_GROUP_DELETE_HANDLE,
|
||||
payload: {
|
||||
baseCustomFieldGroup,
|
||||
},
|
||||
});
|
||||
|
||||
export default {
|
||||
createBaseCustomFieldGroup,
|
||||
handleBaseCustomFieldGroupCreate,
|
||||
updateBaseCustomFieldGroup,
|
||||
handleBaseCustomFieldGroupUpdate,
|
||||
deleteBaseCustomFieldGroup,
|
||||
handleBaseCustomFieldGroupDelete,
|
||||
};
|
||||
@@ -1,3 +1,8 @@
|
||||
/*!
|
||||
* Copyright (c) 2024 PLANKA Software GmbH
|
||||
* Licensed under the Fair Use License: https://github.com/plankanban/planka/blob/master/LICENSE.md
|
||||
*/
|
||||
|
||||
import ActionTypes from '../constants/ActionTypes';
|
||||
|
||||
const createBoardMembership = (boardMembership) => ({
|
||||
@@ -25,10 +30,13 @@ createBoardMembership.failure = (localId, error) => ({
|
||||
|
||||
const handleBoardMembershipCreate = (
|
||||
boardMembership,
|
||||
isProjectAvailable,
|
||||
project,
|
||||
board,
|
||||
users,
|
||||
projectManagers,
|
||||
backgroundImages,
|
||||
baseCustomFieldGroups,
|
||||
boards,
|
||||
boardMemberships,
|
||||
labels,
|
||||
@@ -36,17 +44,25 @@ const handleBoardMembershipCreate = (
|
||||
cards,
|
||||
cardMemberships,
|
||||
cardLabels,
|
||||
taskLists,
|
||||
tasks,
|
||||
attachments,
|
||||
deletedNotifications,
|
||||
customFieldGroups,
|
||||
customFields,
|
||||
customFieldValues,
|
||||
notificationsToDelete,
|
||||
notificationServices,
|
||||
) => ({
|
||||
type: ActionTypes.BOARD_MEMBERSHIP_CREATE_HANDLE,
|
||||
payload: {
|
||||
boardMembership,
|
||||
isProjectAvailable,
|
||||
project,
|
||||
board,
|
||||
users,
|
||||
projectManagers,
|
||||
backgroundImages,
|
||||
baseCustomFieldGroups,
|
||||
boards,
|
||||
boardMemberships,
|
||||
labels,
|
||||
@@ -54,18 +70,14 @@ const handleBoardMembershipCreate = (
|
||||
cards,
|
||||
cardMemberships,
|
||||
cardLabels,
|
||||
taskLists,
|
||||
tasks,
|
||||
attachments,
|
||||
deletedNotifications,
|
||||
},
|
||||
});
|
||||
|
||||
handleBoardMembershipCreate.fetchProject = (id, currentUserId, currentBoardId) => ({
|
||||
type: ActionTypes.BOARD_MEMBERSHIP_CREATE_HANDLE__PROJECT_FETCH,
|
||||
payload: {
|
||||
id,
|
||||
currentUserId,
|
||||
currentBoardId,
|
||||
customFieldGroups,
|
||||
customFields,
|
||||
customFieldValues,
|
||||
notificationsToDelete,
|
||||
notificationServices,
|
||||
},
|
||||
});
|
||||
|
||||
@@ -99,17 +111,19 @@ const handleBoardMembershipUpdate = (boardMembership) => ({
|
||||
},
|
||||
});
|
||||
|
||||
const deleteBoardMembership = (id) => ({
|
||||
const deleteBoardMembership = (id, isCurrentUser) => ({
|
||||
type: ActionTypes.BOARD_MEMBERSHIP_DELETE,
|
||||
payload: {
|
||||
id,
|
||||
isCurrentUser,
|
||||
},
|
||||
});
|
||||
|
||||
deleteBoardMembership.success = (boardMembership) => ({
|
||||
deleteBoardMembership.success = (boardMembership, isCurrentUser) => ({
|
||||
type: ActionTypes.BOARD_MEMBERSHIP_DELETE__SUCCESS,
|
||||
payload: {
|
||||
boardMembership,
|
||||
isCurrentUser,
|
||||
},
|
||||
});
|
||||
|
||||
@@ -121,10 +135,11 @@ deleteBoardMembership.failure = (id, error) => ({
|
||||
},
|
||||
});
|
||||
|
||||
const handleBoardMembershipDelete = (boardMembership) => ({
|
||||
const handleBoardMembershipDelete = (boardMembership, isCurrentUser) => ({
|
||||
type: ActionTypes.BOARD_MEMBERSHIP_DELETE_HANDLE,
|
||||
payload: {
|
||||
boardMembership,
|
||||
isCurrentUser,
|
||||
},
|
||||
});
|
||||
|
||||
|
||||
@@ -1,3 +1,8 @@
|
||||
/*!
|
||||
* Copyright (c) 2024 PLANKA Software GmbH
|
||||
* Licensed under the Fair Use License: https://github.com/plankanban/planka/blob/master/LICENSE.md
|
||||
*/
|
||||
|
||||
import ActionTypes from '../constants/ActionTypes';
|
||||
|
||||
const createBoard = (board) => ({
|
||||
@@ -24,10 +29,11 @@ createBoard.failure = (localId, error) => ({
|
||||
},
|
||||
});
|
||||
|
||||
const handleBoardCreate = (board) => ({
|
||||
const handleBoardCreate = (board, boardMemberships) => ({
|
||||
type: ActionTypes.BOARD_CREATE_HANDLE,
|
||||
payload: {
|
||||
board,
|
||||
boardMemberships,
|
||||
},
|
||||
});
|
||||
|
||||
@@ -48,8 +54,12 @@ fetchBoard.success = (
|
||||
cards,
|
||||
cardMemberships,
|
||||
cardLabels,
|
||||
taskLists,
|
||||
tasks,
|
||||
attachments,
|
||||
customFieldGroups,
|
||||
customFields,
|
||||
customFieldValues,
|
||||
) => ({
|
||||
type: ActionTypes.BOARD_FETCH__SUCCESS,
|
||||
payload: {
|
||||
@@ -62,8 +72,12 @@ fetchBoard.success = (
|
||||
cards,
|
||||
cardMemberships,
|
||||
cardLabels,
|
||||
taskLists,
|
||||
tasks,
|
||||
attachments,
|
||||
customFieldGroups,
|
||||
customFields,
|
||||
customFieldValues,
|
||||
},
|
||||
});
|
||||
|
||||
@@ -105,6 +119,23 @@ const handleBoardUpdate = (board) => ({
|
||||
},
|
||||
});
|
||||
|
||||
const updateBoardContext = (id, value) => ({
|
||||
type: ActionTypes.BOARD_CONTEXT_UPDATE,
|
||||
payload: {
|
||||
id,
|
||||
value,
|
||||
},
|
||||
});
|
||||
|
||||
const searchInBoard = (id, value, currentListId) => ({
|
||||
type: ActionTypes.IN_BOARD_SEARCH,
|
||||
payload: {
|
||||
id,
|
||||
value,
|
||||
currentListId,
|
||||
},
|
||||
});
|
||||
|
||||
const deleteBoard = (id) => ({
|
||||
type: ActionTypes.BOARD_DELETE,
|
||||
payload: {
|
||||
@@ -140,6 +171,8 @@ export default {
|
||||
fetchBoard,
|
||||
updateBoard,
|
||||
handleBoardUpdate,
|
||||
updateBoardContext,
|
||||
searchInBoard,
|
||||
deleteBoard,
|
||||
handleBoardDelete,
|
||||
};
|
||||
|
||||
+118
-18
@@ -1,9 +1,67 @@
|
||||
/*!
|
||||
* Copyright (c) 2024 PLANKA Software GmbH
|
||||
* Licensed under the Fair Use License: https://github.com/plankanban/planka/blob/master/LICENSE.md
|
||||
*/
|
||||
|
||||
import ActionTypes from '../constants/ActionTypes';
|
||||
|
||||
const createCard = (card) => ({
|
||||
const fetchCards = (listId) => ({
|
||||
type: ActionTypes.CARDS_FETCH,
|
||||
payload: {
|
||||
listId,
|
||||
},
|
||||
});
|
||||
|
||||
fetchCards.success = (
|
||||
listId,
|
||||
cards,
|
||||
users,
|
||||
cardMemberships,
|
||||
cardLabels,
|
||||
taskLists,
|
||||
tasks,
|
||||
attachments,
|
||||
customFieldGroups,
|
||||
customFields,
|
||||
customFieldValues,
|
||||
) => ({
|
||||
type: ActionTypes.CARDS_FETCH__SUCCESS,
|
||||
payload: {
|
||||
listId,
|
||||
cards,
|
||||
users,
|
||||
cardMemberships,
|
||||
cardLabels,
|
||||
taskLists,
|
||||
tasks,
|
||||
attachments,
|
||||
customFieldGroups,
|
||||
customFields,
|
||||
customFieldValues,
|
||||
},
|
||||
});
|
||||
|
||||
fetchCards.failure = (listId, error) => ({
|
||||
type: ActionTypes.CARDS_FETCH__FAILURE,
|
||||
payload: {
|
||||
listId,
|
||||
error,
|
||||
},
|
||||
});
|
||||
|
||||
const handleCardsUpdate = (cards, activities) => ({
|
||||
type: ActionTypes.CARDS_UPDATE_HANDLE,
|
||||
payload: {
|
||||
cards,
|
||||
activities,
|
||||
},
|
||||
});
|
||||
|
||||
const createCard = (card, autoOpen) => ({
|
||||
type: ActionTypes.CARD_CREATE,
|
||||
payload: {
|
||||
card,
|
||||
autoOpen,
|
||||
},
|
||||
});
|
||||
|
||||
@@ -23,14 +81,30 @@ createCard.failure = (localId, error) => ({
|
||||
},
|
||||
});
|
||||
|
||||
const handleCardCreate = (card, cardMemberships, cardLabels, tasks, attachments) => ({
|
||||
const handleCardCreate = (
|
||||
card,
|
||||
users,
|
||||
cardMemberships,
|
||||
cardLabels,
|
||||
taskLists,
|
||||
tasks,
|
||||
attachments,
|
||||
customFieldGroups,
|
||||
customFields,
|
||||
customFieldValues,
|
||||
) => ({
|
||||
type: ActionTypes.CARD_CREATE_HANDLE,
|
||||
payload: {
|
||||
card,
|
||||
users,
|
||||
cardMemberships,
|
||||
cardLabels,
|
||||
taskLists,
|
||||
tasks,
|
||||
attachments,
|
||||
customFieldGroups,
|
||||
customFields,
|
||||
customFieldValues,
|
||||
},
|
||||
});
|
||||
|
||||
@@ -57,42 +131,75 @@ updateCard.failure = (id, error) => ({
|
||||
},
|
||||
});
|
||||
|
||||
const handleCardUpdate = (card, isFetched, cardMemberships, cardLabels, tasks, attachments) => ({
|
||||
const handleCardUpdate = (
|
||||
card,
|
||||
isFetched,
|
||||
users,
|
||||
cardMemberships,
|
||||
cardLabels,
|
||||
taskLists,
|
||||
tasks,
|
||||
attachments,
|
||||
customFieldGroups,
|
||||
customFields,
|
||||
customFieldValues,
|
||||
) => ({
|
||||
type: ActionTypes.CARD_UPDATE_HANDLE,
|
||||
payload: {
|
||||
card,
|
||||
isFetched,
|
||||
users,
|
||||
cardMemberships,
|
||||
cardLabels,
|
||||
taskLists,
|
||||
tasks,
|
||||
attachments,
|
||||
customFieldGroups,
|
||||
customFields,
|
||||
customFieldValues,
|
||||
},
|
||||
});
|
||||
|
||||
const duplicateCard = (id, card, taskIds) => ({
|
||||
const duplicateCard = (id, localId, data) => ({
|
||||
type: ActionTypes.CARD_DUPLICATE,
|
||||
payload: {
|
||||
id,
|
||||
card,
|
||||
taskIds,
|
||||
localId,
|
||||
data,
|
||||
},
|
||||
});
|
||||
|
||||
duplicateCard.success = (localId, card, cardMemberships, cardLabels, tasks) => ({
|
||||
duplicateCard.success = (
|
||||
localId,
|
||||
card,
|
||||
cardMemberships,
|
||||
cardLabels,
|
||||
taskLists,
|
||||
tasks,
|
||||
attachments,
|
||||
customFieldGroups,
|
||||
customFields,
|
||||
customFieldValues,
|
||||
) => ({
|
||||
type: ActionTypes.CARD_DUPLICATE__SUCCESS,
|
||||
payload: {
|
||||
localId,
|
||||
card,
|
||||
cardMemberships,
|
||||
cardLabels,
|
||||
taskLists,
|
||||
tasks,
|
||||
attachments,
|
||||
customFieldGroups,
|
||||
customFields,
|
||||
customFieldValues,
|
||||
},
|
||||
});
|
||||
|
||||
duplicateCard.failure = (id, error) => ({
|
||||
duplicateCard.failure = (localId, error) => ({
|
||||
type: ActionTypes.CARD_DUPLICATE__FAILURE,
|
||||
payload: {
|
||||
id,
|
||||
localId,
|
||||
error,
|
||||
},
|
||||
});
|
||||
@@ -126,15 +233,9 @@ const handleCardDelete = (card) => ({
|
||||
},
|
||||
});
|
||||
|
||||
const filterText = (boardId, text) => ({
|
||||
type: ActionTypes.TEXT_FILTER_IN_CURRENT_BOARD,
|
||||
payload: {
|
||||
boardId,
|
||||
text,
|
||||
},
|
||||
});
|
||||
|
||||
export default {
|
||||
fetchCards,
|
||||
handleCardsUpdate,
|
||||
createCard,
|
||||
handleCardCreate,
|
||||
updateCard,
|
||||
@@ -142,5 +243,4 @@ export default {
|
||||
duplicateCard,
|
||||
deleteCard,
|
||||
handleCardDelete,
|
||||
filterText,
|
||||
};
|
||||
|
||||
@@ -1,75 +0,0 @@
|
||||
import ActionTypes from '../constants/ActionTypes';
|
||||
|
||||
const createCommentActivity = (activity) => ({
|
||||
type: ActionTypes.COMMENT_ACTIVITY_CREATE,
|
||||
payload: {
|
||||
activity,
|
||||
},
|
||||
});
|
||||
|
||||
createCommentActivity.success = (localId, activity) => ({
|
||||
type: ActionTypes.COMMENT_ACTIVITY_CREATE__SUCCESS,
|
||||
payload: {
|
||||
localId,
|
||||
activity,
|
||||
},
|
||||
});
|
||||
|
||||
createCommentActivity.failure = (localId, error) => ({
|
||||
type: ActionTypes.COMMENT_ACTIVITY_CREATE__FAILURE,
|
||||
payload: {
|
||||
localId,
|
||||
error,
|
||||
},
|
||||
});
|
||||
|
||||
const updateCommentActivity = (id, data) => ({
|
||||
type: ActionTypes.COMMENT_ACTIVITY_UPDATE,
|
||||
payload: {
|
||||
id,
|
||||
data,
|
||||
},
|
||||
});
|
||||
|
||||
updateCommentActivity.success = (activity) => ({
|
||||
type: ActionTypes.COMMENT_ACTIVITY_UPDATE__SUCCESS,
|
||||
payload: {
|
||||
activity,
|
||||
},
|
||||
});
|
||||
|
||||
updateCommentActivity.failure = (id, error) => ({
|
||||
type: ActionTypes.COMMENT_ACTIVITY_UPDATE__FAILURE,
|
||||
payload: {
|
||||
id,
|
||||
error,
|
||||
},
|
||||
});
|
||||
|
||||
const deleteCommentActivity = (id) => ({
|
||||
type: ActionTypes.COMMENT_ACTIVITY_DELETE,
|
||||
payload: {
|
||||
id,
|
||||
},
|
||||
});
|
||||
|
||||
deleteCommentActivity.success = (activity) => ({
|
||||
type: ActionTypes.COMMENT_ACTIVITY_DELETE__SUCCESS,
|
||||
payload: {
|
||||
activity,
|
||||
},
|
||||
});
|
||||
|
||||
deleteCommentActivity.failure = (id, error) => ({
|
||||
type: ActionTypes.COMMENT_ACTIVITY_DELETE__FAILURE,
|
||||
payload: {
|
||||
id,
|
||||
error,
|
||||
},
|
||||
});
|
||||
|
||||
export default {
|
||||
createCommentActivity,
|
||||
updateCommentActivity,
|
||||
deleteCommentActivity,
|
||||
};
|
||||
@@ -0,0 +1,130 @@
|
||||
/*!
|
||||
* Copyright (c) 2024 PLANKA Software GmbH
|
||||
* Licensed under the Fair Use License: https://github.com/plankanban/planka/blob/master/LICENSE.md
|
||||
*/
|
||||
|
||||
import ActionTypes from '../constants/ActionTypes';
|
||||
|
||||
const fetchComments = (cardId) => ({
|
||||
type: ActionTypes.COMMENTS_FETCH,
|
||||
payload: {
|
||||
cardId,
|
||||
},
|
||||
});
|
||||
|
||||
fetchComments.success = (cardId, comments, users) => ({
|
||||
type: ActionTypes.COMMENTS_FETCH__SUCCESS,
|
||||
payload: {
|
||||
cardId,
|
||||
comments,
|
||||
users,
|
||||
},
|
||||
});
|
||||
|
||||
fetchComments.failure = (cardId, error) => ({
|
||||
type: ActionTypes.COMMENTS_FETCH__FAILURE,
|
||||
payload: {
|
||||
cardId,
|
||||
error,
|
||||
},
|
||||
});
|
||||
|
||||
const createComment = (comment) => ({
|
||||
type: ActionTypes.COMMENT_CREATE,
|
||||
payload: {
|
||||
comment,
|
||||
},
|
||||
});
|
||||
|
||||
createComment.success = (localId, comment) => ({
|
||||
type: ActionTypes.COMMENT_CREATE__SUCCESS,
|
||||
payload: {
|
||||
localId,
|
||||
comment,
|
||||
},
|
||||
});
|
||||
|
||||
createComment.failure = (localId, error) => ({
|
||||
type: ActionTypes.COMMENT_CREATE__FAILURE,
|
||||
payload: {
|
||||
localId,
|
||||
error,
|
||||
},
|
||||
});
|
||||
|
||||
const handleCommentCreate = (comment, users) => ({
|
||||
type: ActionTypes.COMMENT_CREATE_HANDLE,
|
||||
payload: {
|
||||
comment,
|
||||
users,
|
||||
},
|
||||
});
|
||||
|
||||
const updateComment = (id, data) => ({
|
||||
type: ActionTypes.COMMENT_UPDATE,
|
||||
payload: {
|
||||
id,
|
||||
data,
|
||||
},
|
||||
});
|
||||
|
||||
updateComment.success = (comment) => ({
|
||||
type: ActionTypes.COMMENT_UPDATE__SUCCESS,
|
||||
payload: {
|
||||
comment,
|
||||
},
|
||||
});
|
||||
|
||||
updateComment.failure = (id, error) => ({
|
||||
type: ActionTypes.COMMENT_UPDATE__FAILURE,
|
||||
payload: {
|
||||
id,
|
||||
error,
|
||||
},
|
||||
});
|
||||
|
||||
const handleCommentUpdate = (comment) => ({
|
||||
type: ActionTypes.COMMENT_UPDATE_HANDLE,
|
||||
payload: {
|
||||
comment,
|
||||
},
|
||||
});
|
||||
|
||||
const deleteComment = (id) => ({
|
||||
type: ActionTypes.COMMENT_DELETE,
|
||||
payload: {
|
||||
id,
|
||||
},
|
||||
});
|
||||
|
||||
deleteComment.success = (comment) => ({
|
||||
type: ActionTypes.COMMENT_DELETE__SUCCESS,
|
||||
payload: {
|
||||
comment,
|
||||
},
|
||||
});
|
||||
|
||||
deleteComment.failure = (id, error) => ({
|
||||
type: ActionTypes.COMMENT_DELETE__FAILURE,
|
||||
payload: {
|
||||
id,
|
||||
error,
|
||||
},
|
||||
});
|
||||
|
||||
const handleCommentDelete = (comment) => ({
|
||||
type: ActionTypes.COMMENT_DELETE_HANDLE,
|
||||
payload: {
|
||||
comment,
|
||||
},
|
||||
});
|
||||
|
||||
export default {
|
||||
fetchComments,
|
||||
createComment,
|
||||
handleCommentCreate,
|
||||
updateComment,
|
||||
handleCommentUpdate,
|
||||
deleteComment,
|
||||
handleCommentDelete,
|
||||
};
|
||||
@@ -1,3 +1,8 @@
|
||||
/*!
|
||||
* Copyright (c) 2024 PLANKA Software GmbH
|
||||
* Licensed under the Fair Use License: https://github.com/plankanban/planka/blob/master/LICENSE.md
|
||||
*/
|
||||
|
||||
import ActionTypes from '../constants/ActionTypes';
|
||||
|
||||
const initializeCore = (
|
||||
@@ -6,6 +11,8 @@ const initializeCore = (
|
||||
users,
|
||||
projects,
|
||||
projectManagers,
|
||||
backgroundImages,
|
||||
baseCustomFieldGroups,
|
||||
boards,
|
||||
boardMemberships,
|
||||
labels,
|
||||
@@ -13,10 +20,14 @@ const initializeCore = (
|
||||
cards,
|
||||
cardMemberships,
|
||||
cardLabels,
|
||||
taskLists,
|
||||
tasks,
|
||||
attachments,
|
||||
activities,
|
||||
customFieldGroups,
|
||||
customFields,
|
||||
customFieldValues,
|
||||
notifications,
|
||||
notificationServices,
|
||||
) => ({
|
||||
type: ActionTypes.CORE_INITIALIZE,
|
||||
payload: {
|
||||
@@ -25,6 +36,8 @@ const initializeCore = (
|
||||
users,
|
||||
projects,
|
||||
projectManagers,
|
||||
backgroundImages,
|
||||
baseCustomFieldGroups,
|
||||
boards,
|
||||
boardMemberships,
|
||||
labels,
|
||||
@@ -32,14 +45,17 @@ const initializeCore = (
|
||||
cards,
|
||||
cardMemberships,
|
||||
cardLabels,
|
||||
taskLists,
|
||||
tasks,
|
||||
attachments,
|
||||
activities,
|
||||
customFieldGroups,
|
||||
customFields,
|
||||
customFieldValues,
|
||||
notifications,
|
||||
notificationServices,
|
||||
},
|
||||
});
|
||||
|
||||
// TODO: with success?
|
||||
initializeCore.fetchConfig = (config) => ({
|
||||
type: ActionTypes.CORE_INITIALIZE__CONFIG_FETCH,
|
||||
payload: {
|
||||
@@ -47,13 +63,32 @@ initializeCore.fetchConfig = (config) => ({
|
||||
},
|
||||
});
|
||||
|
||||
const logout = (invalidateAccessToken) => ({
|
||||
type: ActionTypes.LOGOUT,
|
||||
const toggleFavorites = (isEnabled) => ({
|
||||
type: ActionTypes.FAVORITES_TOGGLE,
|
||||
payload: {
|
||||
invalidateAccessToken,
|
||||
isEnabled,
|
||||
},
|
||||
});
|
||||
|
||||
const toggleEditMode = (isEnabled) => ({
|
||||
type: ActionTypes.EDIT_MODE_TOGGLE,
|
||||
payload: {
|
||||
isEnabled,
|
||||
},
|
||||
});
|
||||
|
||||
const updateHomeView = (value) => ({
|
||||
type: ActionTypes.HOME_VIEW_UPDATE,
|
||||
payload: {
|
||||
value,
|
||||
},
|
||||
});
|
||||
|
||||
const logout = () => ({
|
||||
type: ActionTypes.LOGOUT,
|
||||
payload: {},
|
||||
});
|
||||
|
||||
logout.invalidateAccessToken = () => ({
|
||||
type: ActionTypes.LOGOUT__ACCESS_TOKEN_INVALIDATE,
|
||||
payload: {},
|
||||
@@ -61,5 +96,8 @@ logout.invalidateAccessToken = () => ({
|
||||
|
||||
export default {
|
||||
initializeCore,
|
||||
toggleFavorites,
|
||||
toggleEditMode,
|
||||
updateHomeView,
|
||||
logout,
|
||||
};
|
||||
|
||||
@@ -0,0 +1,104 @@
|
||||
/*!
|
||||
* Copyright (c) 2024 PLANKA Software GmbH
|
||||
* Licensed under the Fair Use License: https://github.com/plankanban/planka/blob/master/LICENSE.md
|
||||
*/
|
||||
|
||||
import ActionTypes from '../constants/ActionTypes';
|
||||
|
||||
const createCustomFieldGroup = (customFieldGroup) => ({
|
||||
type: ActionTypes.CUSTOM_FIELD_GROUP_CREATE,
|
||||
payload: {
|
||||
customFieldGroup,
|
||||
},
|
||||
});
|
||||
|
||||
createCustomFieldGroup.success = (localId, customFieldGroup) => ({
|
||||
type: ActionTypes.CUSTOM_FIELD_GROUP_CREATE__SUCCESS,
|
||||
payload: {
|
||||
localId,
|
||||
customFieldGroup,
|
||||
},
|
||||
});
|
||||
|
||||
createCustomFieldGroup.failure = (localId, error) => ({
|
||||
type: ActionTypes.CUSTOM_FIELD_GROUP_CREATE__FAILURE,
|
||||
payload: {
|
||||
localId,
|
||||
error,
|
||||
},
|
||||
});
|
||||
|
||||
const handleCustomFieldGroupCreate = (customFieldGroup) => ({
|
||||
type: ActionTypes.CUSTOM_FIELD_GROUP_CREATE_HANDLE,
|
||||
payload: {
|
||||
customFieldGroup,
|
||||
},
|
||||
});
|
||||
|
||||
const updateCustomFieldGroup = (id, data) => ({
|
||||
type: ActionTypes.CUSTOM_FIELD_GROUP_UPDATE,
|
||||
payload: {
|
||||
id,
|
||||
data,
|
||||
},
|
||||
});
|
||||
|
||||
updateCustomFieldGroup.success = (customFieldGroup) => ({
|
||||
type: ActionTypes.CUSTOM_FIELD_GROUP_UPDATE__SUCCESS,
|
||||
payload: {
|
||||
customFieldGroup,
|
||||
},
|
||||
});
|
||||
|
||||
updateCustomFieldGroup.failure = (id, error) => ({
|
||||
type: ActionTypes.CUSTOM_FIELD_GROUP_UPDATE__FAILURE,
|
||||
payload: {
|
||||
id,
|
||||
error,
|
||||
},
|
||||
});
|
||||
|
||||
const handleCustomFieldGroupUpdate = (customFieldGroup) => ({
|
||||
type: ActionTypes.CUSTOM_FIELD_GROUP_UPDATE_HANDLE,
|
||||
payload: {
|
||||
customFieldGroup,
|
||||
},
|
||||
});
|
||||
|
||||
const deleteCustomFieldGroup = (id) => ({
|
||||
type: ActionTypes.CUSTOM_FIELD_GROUP_DELETE,
|
||||
payload: {
|
||||
id,
|
||||
},
|
||||
});
|
||||
|
||||
deleteCustomFieldGroup.success = (customFieldGroup) => ({
|
||||
type: ActionTypes.CUSTOM_FIELD_GROUP_DELETE__SUCCESS,
|
||||
payload: {
|
||||
customFieldGroup,
|
||||
},
|
||||
});
|
||||
|
||||
deleteCustomFieldGroup.failure = (id, error) => ({
|
||||
type: ActionTypes.CUSTOM_FIELD_GROUP_DELETE__FAILURE,
|
||||
payload: {
|
||||
id,
|
||||
error,
|
||||
},
|
||||
});
|
||||
|
||||
const handleCustomFieldGroupDelete = (customFieldGroup) => ({
|
||||
type: ActionTypes.CUSTOM_FIELD_GROUP_DELETE_HANDLE,
|
||||
payload: {
|
||||
customFieldGroup,
|
||||
},
|
||||
});
|
||||
|
||||
export default {
|
||||
createCustomFieldGroup,
|
||||
handleCustomFieldGroupCreate,
|
||||
updateCustomFieldGroup,
|
||||
handleCustomFieldGroupUpdate,
|
||||
deleteCustomFieldGroup,
|
||||
handleCustomFieldGroupDelete,
|
||||
};
|
||||
@@ -0,0 +1,72 @@
|
||||
/*!
|
||||
* Copyright (c) 2024 PLANKA Software GmbH
|
||||
* Licensed under the Fair Use License: https://github.com/plankanban/planka/blob/master/LICENSE.md
|
||||
*/
|
||||
|
||||
import ActionTypes from '../constants/ActionTypes';
|
||||
|
||||
const updateCustomFieldValue = (customFieldValue) => ({
|
||||
type: ActionTypes.CUSTOM_FIELD_VALUE_UPDATE,
|
||||
payload: {
|
||||
customFieldValue,
|
||||
},
|
||||
});
|
||||
|
||||
updateCustomFieldValue.success = (localId, customFieldValue) => ({
|
||||
type: ActionTypes.CUSTOM_FIELD_VALUE_UPDATE__SUCCESS,
|
||||
payload: {
|
||||
localId,
|
||||
customFieldValue,
|
||||
},
|
||||
});
|
||||
|
||||
updateCustomFieldValue.failure = (localId, error) => ({
|
||||
type: ActionTypes.CUSTOM_FIELD_VALUE_UPDATE__FAILURE,
|
||||
payload: {
|
||||
localId,
|
||||
error,
|
||||
},
|
||||
});
|
||||
|
||||
const handleCustomFieldValueUpdate = (customFieldValue) => ({
|
||||
type: ActionTypes.CUSTOM_FIELD_VALUE_UPDATE_HANDLE,
|
||||
payload: {
|
||||
customFieldValue,
|
||||
},
|
||||
});
|
||||
|
||||
const deleteCustomFieldValue = (id) => ({
|
||||
type: ActionTypes.CUSTOM_FIELD_VALUE_DELETE,
|
||||
payload: {
|
||||
id,
|
||||
},
|
||||
});
|
||||
|
||||
deleteCustomFieldValue.success = (customFieldValue) => ({
|
||||
type: ActionTypes.CUSTOM_FIELD_VALUE_DELETE__SUCCESS,
|
||||
payload: {
|
||||
customFieldValue,
|
||||
},
|
||||
});
|
||||
|
||||
deleteCustomFieldValue.failure = (id, error) => ({
|
||||
type: ActionTypes.CUSTOM_FIELD_VALUE_DELETE__FAILURE,
|
||||
payload: {
|
||||
id,
|
||||
error,
|
||||
},
|
||||
});
|
||||
|
||||
const handleCustomFieldValueDelete = (customFieldValue) => ({
|
||||
type: ActionTypes.CUSTOM_FIELD_VALUE_DELETE_HANDLE,
|
||||
payload: {
|
||||
customFieldValue,
|
||||
},
|
||||
});
|
||||
|
||||
export default {
|
||||
updateCustomFieldValue,
|
||||
handleCustomFieldValueUpdate,
|
||||
deleteCustomFieldValue,
|
||||
handleCustomFieldValueDelete,
|
||||
};
|
||||
@@ -0,0 +1,104 @@
|
||||
/*!
|
||||
* Copyright (c) 2024 PLANKA Software GmbH
|
||||
* Licensed under the Fair Use License: https://github.com/plankanban/planka/blob/master/LICENSE.md
|
||||
*/
|
||||
|
||||
import ActionTypes from '../constants/ActionTypes';
|
||||
|
||||
const createCustomField = (customField) => ({
|
||||
type: ActionTypes.CUSTOM_FIELD_CREATE,
|
||||
payload: {
|
||||
customField,
|
||||
},
|
||||
});
|
||||
|
||||
createCustomField.success = (localId, customField) => ({
|
||||
type: ActionTypes.CUSTOM_FIELD_CREATE__SUCCESS,
|
||||
payload: {
|
||||
localId,
|
||||
customField,
|
||||
},
|
||||
});
|
||||
|
||||
createCustomField.failure = (localId, error) => ({
|
||||
type: ActionTypes.CUSTOM_FIELD_CREATE__FAILURE,
|
||||
payload: {
|
||||
localId,
|
||||
error,
|
||||
},
|
||||
});
|
||||
|
||||
const handleCustomFieldCreate = (customField) => ({
|
||||
type: ActionTypes.CUSTOM_FIELD_CREATE_HANDLE,
|
||||
payload: {
|
||||
customField,
|
||||
},
|
||||
});
|
||||
|
||||
const updateCustomField = (id, data) => ({
|
||||
type: ActionTypes.CUSTOM_FIELD_UPDATE,
|
||||
payload: {
|
||||
id,
|
||||
data,
|
||||
},
|
||||
});
|
||||
|
||||
updateCustomField.success = (customField) => ({
|
||||
type: ActionTypes.CUSTOM_FIELD_UPDATE__SUCCESS,
|
||||
payload: {
|
||||
customField,
|
||||
},
|
||||
});
|
||||
|
||||
updateCustomField.failure = (id, error) => ({
|
||||
type: ActionTypes.CUSTOM_FIELD_UPDATE__FAILURE,
|
||||
payload: {
|
||||
id,
|
||||
error,
|
||||
},
|
||||
});
|
||||
|
||||
const handleCustomFieldUpdate = (customField) => ({
|
||||
type: ActionTypes.CUSTOM_FIELD_UPDATE_HANDLE,
|
||||
payload: {
|
||||
customField,
|
||||
},
|
||||
});
|
||||
|
||||
const deleteCustomField = (id) => ({
|
||||
type: ActionTypes.CUSTOM_FIELD_DELETE,
|
||||
payload: {
|
||||
id,
|
||||
},
|
||||
});
|
||||
|
||||
deleteCustomField.success = (customField) => ({
|
||||
type: ActionTypes.CUSTOM_FIELD_DELETE__SUCCESS,
|
||||
payload: {
|
||||
customField,
|
||||
},
|
||||
});
|
||||
|
||||
deleteCustomField.failure = (id, error) => ({
|
||||
type: ActionTypes.CUSTOM_FIELD_DELETE__FAILURE,
|
||||
payload: {
|
||||
id,
|
||||
error,
|
||||
},
|
||||
});
|
||||
|
||||
const handleCustomFieldDelete = (customField) => ({
|
||||
type: ActionTypes.CUSTOM_FIELD_DELETE_HANDLE,
|
||||
payload: {
|
||||
customField,
|
||||
},
|
||||
});
|
||||
|
||||
export default {
|
||||
createCustomField,
|
||||
handleCustomFieldCreate,
|
||||
updateCustomField,
|
||||
handleCustomFieldUpdate,
|
||||
deleteCustomField,
|
||||
handleCustomFieldDelete,
|
||||
};
|
||||
@@ -1,3 +1,8 @@
|
||||
/*!
|
||||
* Copyright (c) 2024 PLANKA Software GmbH
|
||||
* Licensed under the Fair Use License: https://github.com/plankanban/planka/blob/master/LICENSE.md
|
||||
*/
|
||||
|
||||
import router from './router';
|
||||
import socket from './socket';
|
||||
import login from './login';
|
||||
@@ -6,16 +11,23 @@ import modals from './modals';
|
||||
import users from './users';
|
||||
import projects from './projects';
|
||||
import projectManagers from './project-managers';
|
||||
import backgroundImages from './background-images';
|
||||
import baseCustomFieldGroups from './base-custom-field-groups';
|
||||
import boards from './boards';
|
||||
import boardMemberships from './board-memberships';
|
||||
import labels from './labels';
|
||||
import lists from './lists';
|
||||
import cards from './cards';
|
||||
import taskLists from './task-lists';
|
||||
import tasks from './tasks';
|
||||
import attachments from './attachments';
|
||||
import customFieldGroups from './custom-field-groups';
|
||||
import customFields from './custom-fields';
|
||||
import customFieldValues from './custom-field-values';
|
||||
import comments from './comments';
|
||||
import activities from './activities';
|
||||
import commentActivities from './comment-activities';
|
||||
import notifications from './notifications';
|
||||
import notificationServices from './notification-services';
|
||||
|
||||
export default {
|
||||
...router,
|
||||
@@ -26,14 +38,21 @@ export default {
|
||||
...users,
|
||||
...projects,
|
||||
...projectManagers,
|
||||
...backgroundImages,
|
||||
...baseCustomFieldGroups,
|
||||
...boards,
|
||||
...boardMemberships,
|
||||
...labels,
|
||||
...lists,
|
||||
...cards,
|
||||
...taskLists,
|
||||
...tasks,
|
||||
...attachments,
|
||||
...customFieldGroups,
|
||||
...customFields,
|
||||
...customFieldValues,
|
||||
...comments,
|
||||
...activities,
|
||||
...commentActivities,
|
||||
...notifications,
|
||||
...notificationServices,
|
||||
};
|
||||
|
||||
@@ -1,3 +1,8 @@
|
||||
/*!
|
||||
* Copyright (c) 2024 PLANKA Software GmbH
|
||||
* Licensed under the Fair Use License: https://github.com/plankanban/planka/blob/master/LICENSE.md
|
||||
*/
|
||||
|
||||
import ActionTypes from '../constants/ActionTypes';
|
||||
|
||||
const createLabel = (label) => ({
|
||||
@@ -23,6 +28,32 @@ createLabel.failure = (localId, error) => ({
|
||||
},
|
||||
});
|
||||
|
||||
const createLabelFromCard = (cardId, label) => ({
|
||||
type: ActionTypes.LABEL_FROM_CARD_CREATE,
|
||||
payload: {
|
||||
cardId,
|
||||
label,
|
||||
},
|
||||
});
|
||||
|
||||
createLabelFromCard.success = (localId, label, cardLabel) => ({
|
||||
type: ActionTypes.LABEL_FROM_CARD_CREATE__SUCCESS,
|
||||
payload: {
|
||||
localId,
|
||||
label,
|
||||
cardLabel,
|
||||
},
|
||||
});
|
||||
|
||||
createLabelFromCard.failure = (cardId, localId, error) => ({
|
||||
type: ActionTypes.LABEL_FROM_CARD_CREATE__FAILURE,
|
||||
payload: {
|
||||
cardId,
|
||||
localId,
|
||||
error,
|
||||
},
|
||||
});
|
||||
|
||||
const handleLabelCreate = (label) => ({
|
||||
type: ActionTypes.LABEL_CREATE_HANDLE,
|
||||
payload: {
|
||||
@@ -151,24 +182,27 @@ const handleLabelFromCardRemove = (cardLabel) => ({
|
||||
},
|
||||
});
|
||||
|
||||
const addLabelToBoardFilter = (id, boardId) => ({
|
||||
const addLabelToBoardFilter = (id, boardId, currentListId) => ({
|
||||
type: ActionTypes.LABEL_TO_BOARD_FILTER_ADD,
|
||||
payload: {
|
||||
id,
|
||||
boardId,
|
||||
currentListId,
|
||||
},
|
||||
});
|
||||
|
||||
const removeLabelFromBoardFilter = (id, boardId) => ({
|
||||
const removeLabelFromBoardFilter = (id, boardId, currentListId) => ({
|
||||
type: ActionTypes.LABEL_FROM_BOARD_FILTER_REMOVE,
|
||||
payload: {
|
||||
id,
|
||||
boardId,
|
||||
currentListId,
|
||||
},
|
||||
});
|
||||
|
||||
export default {
|
||||
createLabel,
|
||||
createLabelFromCard,
|
||||
handleLabelCreate,
|
||||
updateLabel,
|
||||
handleLabelUpdate,
|
||||
|
||||
@@ -1,3 +1,8 @@
|
||||
/*!
|
||||
* Copyright (c) 2024 PLANKA Software GmbH
|
||||
* Licensed under the Fair Use License: https://github.com/plankanban/planka/blob/master/LICENSE.md
|
||||
*/
|
||||
|
||||
import ActionTypes from '../constants/ActionTypes';
|
||||
|
||||
const createList = (list) => ({
|
||||
@@ -84,25 +89,75 @@ sortList.failure = (id, error) => ({
|
||||
},
|
||||
});
|
||||
|
||||
const handleListSort = (list, cards) => ({
|
||||
type: ActionTypes.LIST_SORT_HANDLE,
|
||||
const moveListCards = (id, nextId, cardIds) => ({
|
||||
type: ActionTypes.LIST_CARDS_MOVE,
|
||||
payload: {
|
||||
list,
|
||||
cards,
|
||||
id,
|
||||
nextId,
|
||||
cardIds,
|
||||
},
|
||||
});
|
||||
|
||||
const deleteList = (id) => ({
|
||||
type: ActionTypes.LIST_DELETE,
|
||||
moveListCards.success = (list, cards, activities) => ({
|
||||
type: ActionTypes.LIST_CARDS_MOVE__SUCCESS,
|
||||
payload: {
|
||||
list,
|
||||
cards,
|
||||
activities,
|
||||
},
|
||||
});
|
||||
|
||||
moveListCards.failure = (id, error) => ({
|
||||
type: ActionTypes.LIST_CARDS_MOVE__FAILURE,
|
||||
payload: {
|
||||
id,
|
||||
error,
|
||||
},
|
||||
});
|
||||
|
||||
const clearList = (id) => ({
|
||||
type: ActionTypes.LIST_CLEAR,
|
||||
payload: {
|
||||
id,
|
||||
},
|
||||
});
|
||||
|
||||
deleteList.success = (list) => ({
|
||||
clearList.success = (list) => ({
|
||||
type: ActionTypes.LIST_CLEAR__SUCCESS,
|
||||
payload: {
|
||||
list,
|
||||
},
|
||||
});
|
||||
|
||||
clearList.failure = (id, error) => ({
|
||||
type: ActionTypes.LIST_CLEAR__FAILURE,
|
||||
payload: {
|
||||
id,
|
||||
error,
|
||||
},
|
||||
});
|
||||
|
||||
const handleListClear = (list) => ({
|
||||
type: ActionTypes.LIST_CLEAR_HANDLE,
|
||||
payload: {
|
||||
list,
|
||||
},
|
||||
});
|
||||
|
||||
const deleteList = (id, trashId, cardIds) => ({
|
||||
type: ActionTypes.LIST_DELETE,
|
||||
payload: {
|
||||
id,
|
||||
trashId,
|
||||
cardIds,
|
||||
},
|
||||
});
|
||||
|
||||
deleteList.success = (list, cards) => ({
|
||||
type: ActionTypes.LIST_DELETE__SUCCESS,
|
||||
payload: {
|
||||
list,
|
||||
cards,
|
||||
},
|
||||
});
|
||||
|
||||
@@ -114,10 +169,11 @@ deleteList.failure = (id, error) => ({
|
||||
},
|
||||
});
|
||||
|
||||
const handleListDelete = (list) => ({
|
||||
const handleListDelete = (list, cards) => ({
|
||||
type: ActionTypes.LIST_DELETE_HANDLE,
|
||||
payload: {
|
||||
list,
|
||||
cards,
|
||||
},
|
||||
});
|
||||
|
||||
@@ -127,7 +183,9 @@ export default {
|
||||
updateList,
|
||||
handleListUpdate,
|
||||
sortList,
|
||||
handleListSort,
|
||||
moveListCards,
|
||||
clearList,
|
||||
handleListClear,
|
||||
deleteList,
|
||||
handleListDelete,
|
||||
};
|
||||
|
||||
@@ -1,3 +1,8 @@
|
||||
/*!
|
||||
* Copyright (c) 2024 PLANKA Software GmbH
|
||||
* Licensed under the Fair Use License: https://github.com/plankanban/planka/blob/master/LICENSE.md
|
||||
*/
|
||||
|
||||
import ActionTypes from '../constants/ActionTypes';
|
||||
|
||||
const initializeLogin = (config) => ({
|
||||
@@ -28,20 +33,20 @@ authenticate.failure = (error) => ({
|
||||
},
|
||||
});
|
||||
|
||||
const authenticateUsingOidc = () => ({
|
||||
type: ActionTypes.USING_OIDC_AUTHENTICATE,
|
||||
const authenticateWithOidc = () => ({
|
||||
type: ActionTypes.WITH_OIDC_AUTHENTICATE,
|
||||
payload: {},
|
||||
});
|
||||
|
||||
authenticateUsingOidc.success = (accessToken) => ({
|
||||
type: ActionTypes.USING_OIDC_AUTHENTICATE__SUCCESS,
|
||||
authenticateWithOidc.success = (accessToken) => ({
|
||||
type: ActionTypes.WITH_OIDC_AUTHENTICATE__SUCCESS,
|
||||
payload: {
|
||||
accessToken,
|
||||
},
|
||||
});
|
||||
|
||||
authenticateUsingOidc.failure = (error) => ({
|
||||
type: ActionTypes.USING_OIDC_AUTHENTICATE__FAILURE,
|
||||
authenticateWithOidc.failure = (error) => ({
|
||||
type: ActionTypes.WITH_OIDC_AUTHENTICATE__FAILURE,
|
||||
payload: {
|
||||
error,
|
||||
},
|
||||
@@ -55,6 +60,6 @@ const clearAuthenticateError = () => ({
|
||||
export default {
|
||||
initializeLogin,
|
||||
authenticate,
|
||||
authenticateUsingOidc,
|
||||
authenticateWithOidc,
|
||||
clearAuthenticateError,
|
||||
};
|
||||
|
||||
@@ -1,9 +1,15 @@
|
||||
/*!
|
||||
* Copyright (c) 2024 PLANKA Software GmbH
|
||||
* Licensed under the Fair Use License: https://github.com/plankanban/planka/blob/master/LICENSE.md
|
||||
*/
|
||||
|
||||
import ActionTypes from '../constants/ActionTypes';
|
||||
|
||||
const openModal = (type) => ({
|
||||
const openModal = (type, params = {}) => ({
|
||||
type: ActionTypes.MODAL_OPEN,
|
||||
payload: {
|
||||
type,
|
||||
params,
|
||||
},
|
||||
});
|
||||
|
||||
|
||||
@@ -0,0 +1,127 @@
|
||||
/*!
|
||||
* Copyright (c) 2024 PLANKA Software GmbH
|
||||
* Licensed under the Fair Use License: https://github.com/plankanban/planka/blob/master/LICENSE.md
|
||||
*/
|
||||
|
||||
import ActionTypes from '../constants/ActionTypes';
|
||||
|
||||
const createNotificationService = (notificationService) => ({
|
||||
type: ActionTypes.NOTIFICATION_SERVICE_CREATE,
|
||||
payload: {
|
||||
notificationService,
|
||||
},
|
||||
});
|
||||
|
||||
createNotificationService.success = (localId, notificationService) => ({
|
||||
type: ActionTypes.NOTIFICATION_SERVICE_CREATE__SUCCESS,
|
||||
payload: {
|
||||
localId,
|
||||
notificationService,
|
||||
},
|
||||
});
|
||||
|
||||
createNotificationService.failure = (localId, error) => ({
|
||||
type: ActionTypes.NOTIFICATION_SERVICE_CREATE__FAILURE,
|
||||
payload: {
|
||||
localId,
|
||||
error,
|
||||
},
|
||||
});
|
||||
|
||||
const handleNotificationServiceCreate = (notificationService) => ({
|
||||
type: ActionTypes.NOTIFICATION_SERVICE_CREATE_HANDLE,
|
||||
payload: {
|
||||
notificationService,
|
||||
},
|
||||
});
|
||||
|
||||
const updateNotificationService = (id, data) => ({
|
||||
type: ActionTypes.NOTIFICATION_SERVICE_UPDATE,
|
||||
payload: {
|
||||
id,
|
||||
data,
|
||||
},
|
||||
});
|
||||
|
||||
updateNotificationService.success = (notificationService) => ({
|
||||
type: ActionTypes.NOTIFICATION_SERVICE_UPDATE__SUCCESS,
|
||||
payload: {
|
||||
notificationService,
|
||||
},
|
||||
});
|
||||
|
||||
updateNotificationService.failure = (id, error) => ({
|
||||
type: ActionTypes.NOTIFICATION_SERVICE_UPDATE__FAILURE,
|
||||
payload: {
|
||||
id,
|
||||
error,
|
||||
},
|
||||
});
|
||||
|
||||
const handleNotificationServiceUpdate = (notificationService) => ({
|
||||
type: ActionTypes.NOTIFICATION_SERVICE_UPDATE_HANDLE,
|
||||
payload: {
|
||||
notificationService,
|
||||
},
|
||||
});
|
||||
|
||||
const testNotificationService = (id) => ({
|
||||
type: ActionTypes.NOTIFICATION_SERVICE_TEST,
|
||||
payload: {
|
||||
id,
|
||||
},
|
||||
});
|
||||
|
||||
testNotificationService.success = (notificationService) => ({
|
||||
type: ActionTypes.NOTIFICATION_SERVICE_TEST__SUCCESS,
|
||||
payload: {
|
||||
notificationService,
|
||||
},
|
||||
});
|
||||
|
||||
testNotificationService.failure = (id, error) => ({
|
||||
type: ActionTypes.NOTIFICATION_SERVICE_TEST__FAILURE,
|
||||
payload: {
|
||||
id,
|
||||
error,
|
||||
},
|
||||
});
|
||||
|
||||
const deleteNotificationService = (id) => ({
|
||||
type: ActionTypes.NOTIFICATION_SERVICE_DELETE,
|
||||
payload: {
|
||||
id,
|
||||
},
|
||||
});
|
||||
|
||||
deleteNotificationService.success = (notificationService) => ({
|
||||
type: ActionTypes.NOTIFICATION_SERVICE_DELETE__SUCCESS,
|
||||
payload: {
|
||||
notificationService,
|
||||
},
|
||||
});
|
||||
|
||||
deleteNotificationService.failure = (id, error) => ({
|
||||
type: ActionTypes.NOTIFICATION_SERVICE_DELETE__FAILURE,
|
||||
payload: {
|
||||
id,
|
||||
error,
|
||||
},
|
||||
});
|
||||
|
||||
const handleNotificationServiceDelete = (notificationService) => ({
|
||||
type: ActionTypes.NOTIFICATION_SERVICE_DELETE_HANDLE,
|
||||
payload: {
|
||||
notificationService,
|
||||
},
|
||||
});
|
||||
|
||||
export default {
|
||||
createNotificationService,
|
||||
handleNotificationServiceCreate,
|
||||
updateNotificationService,
|
||||
handleNotificationServiceUpdate,
|
||||
testNotificationService,
|
||||
deleteNotificationService,
|
||||
handleNotificationServiceDelete,
|
||||
};
|
||||
@@ -1,12 +1,34 @@
|
||||
/*!
|
||||
* Copyright (c) 2024 PLANKA Software GmbH
|
||||
* Licensed under the Fair Use License: https://github.com/plankanban/planka/blob/master/LICENSE.md
|
||||
*/
|
||||
|
||||
import ActionTypes from '../constants/ActionTypes';
|
||||
|
||||
const handleNotificationCreate = (notification, users, cards, activities) => ({
|
||||
const deleteAllNotifications = () => ({
|
||||
type: ActionTypes.ALL_NOTIFICATIONS_DELETE,
|
||||
payload: {},
|
||||
});
|
||||
|
||||
deleteAllNotifications.success = (notifications) => ({
|
||||
type: ActionTypes.ALL_NOTIFICATIONS_DELETE__SUCCESS,
|
||||
payload: {
|
||||
notifications,
|
||||
},
|
||||
});
|
||||
|
||||
deleteAllNotifications.failure = (error) => ({
|
||||
type: ActionTypes.ALL_NOTIFICATIONS_DELETE__FAILURE,
|
||||
payload: {
|
||||
error,
|
||||
},
|
||||
});
|
||||
|
||||
const handleNotificationCreate = (notification, users) => ({
|
||||
type: ActionTypes.NOTIFICATION_CREATE_HANDLE,
|
||||
payload: {
|
||||
notification,
|
||||
users,
|
||||
cards,
|
||||
activities,
|
||||
},
|
||||
});
|
||||
|
||||
@@ -40,6 +62,7 @@ const handleNotificationDelete = (notification) => ({
|
||||
});
|
||||
|
||||
export default {
|
||||
deleteAllNotifications,
|
||||
handleNotificationCreate,
|
||||
deleteNotification,
|
||||
handleNotificationDelete,
|
||||
|
||||
@@ -1,3 +1,8 @@
|
||||
/*!
|
||||
* Copyright (c) 2024 PLANKA Software GmbH
|
||||
* Licensed under the Fair Use License: https://github.com/plankanban/planka/blob/master/LICENSE.md
|
||||
*/
|
||||
|
||||
import ActionTypes from '../constants/ActionTypes';
|
||||
|
||||
const createProjectManager = (projectManager) => ({
|
||||
@@ -25,10 +30,15 @@ createProjectManager.failure = (localId, error) => ({
|
||||
|
||||
const handleProjectManagerCreate = (
|
||||
projectManager,
|
||||
boardIds,
|
||||
isCurrentUser,
|
||||
isProjectAvailable,
|
||||
project,
|
||||
board,
|
||||
users,
|
||||
projectManagers,
|
||||
backgroundImages,
|
||||
baseCustomFieldGroups,
|
||||
boards,
|
||||
boardMemberships,
|
||||
labels,
|
||||
@@ -36,17 +46,27 @@ const handleProjectManagerCreate = (
|
||||
cards,
|
||||
cardMemberships,
|
||||
cardLabels,
|
||||
taskLists,
|
||||
tasks,
|
||||
attachments,
|
||||
deletedNotifications,
|
||||
customFieldGroups,
|
||||
customFields,
|
||||
customFieldValues,
|
||||
notificationsToDelete,
|
||||
notificationServices,
|
||||
) => ({
|
||||
type: ActionTypes.PROJECT_MANAGER_CREATE_HANDLE,
|
||||
payload: {
|
||||
projectManager,
|
||||
boardIds,
|
||||
isCurrentUser,
|
||||
isProjectAvailable,
|
||||
project,
|
||||
board,
|
||||
users,
|
||||
projectManagers,
|
||||
backgroundImages,
|
||||
baseCustomFieldGroups,
|
||||
boards,
|
||||
boardMemberships,
|
||||
labels,
|
||||
@@ -54,27 +74,21 @@ const handleProjectManagerCreate = (
|
||||
cards,
|
||||
cardMemberships,
|
||||
cardLabels,
|
||||
taskLists,
|
||||
tasks,
|
||||
attachments,
|
||||
deletedNotifications,
|
||||
customFieldGroups,
|
||||
customFields,
|
||||
customFieldValues,
|
||||
notificationsToDelete,
|
||||
notificationServices,
|
||||
},
|
||||
});
|
||||
|
||||
handleProjectManagerCreate.fetchProject = (id, currentUserId, currentBoardId) => ({
|
||||
type: ActionTypes.PROJECT_MANAGER_CREATE_HANDLE__PROJECT_FETCH,
|
||||
payload: {
|
||||
id,
|
||||
currentUserId,
|
||||
currentBoardId,
|
||||
},
|
||||
});
|
||||
|
||||
const deleteProjectManager = (id, isCurrentUser, isCurrentProject) => ({
|
||||
const deleteProjectManager = (id) => ({
|
||||
type: ActionTypes.PROJECT_MANAGER_DELETE,
|
||||
payload: {
|
||||
id,
|
||||
isCurrentUser,
|
||||
isCurrentProject,
|
||||
},
|
||||
});
|
||||
|
||||
@@ -93,12 +107,10 @@ deleteProjectManager.failure = (id, error) => ({
|
||||
},
|
||||
});
|
||||
|
||||
const handleProjectManagerDelete = (projectManager, isCurrentUser, isCurrentProject) => ({
|
||||
const handleProjectManagerDelete = (projectManager) => ({
|
||||
type: ActionTypes.PROJECT_MANAGER_DELETE_HANDLE,
|
||||
payload: {
|
||||
projectManager,
|
||||
isCurrentUser,
|
||||
isCurrentProject,
|
||||
},
|
||||
});
|
||||
|
||||
|
||||
@@ -1,5 +1,31 @@
|
||||
/*!
|
||||
* Copyright (c) 2024 PLANKA Software GmbH
|
||||
* Licensed under the Fair Use License: https://github.com/plankanban/planka/blob/master/LICENSE.md
|
||||
*/
|
||||
|
||||
import ActionTypes from '../constants/ActionTypes';
|
||||
|
||||
const searchProjects = (value) => ({
|
||||
type: ActionTypes.PROJECTS_SEARCH,
|
||||
payload: {
|
||||
value,
|
||||
},
|
||||
});
|
||||
|
||||
const updateProjectsOrder = (value) => ({
|
||||
type: ActionTypes.PROJECTS_ORDER_UPDATE,
|
||||
payload: {
|
||||
value,
|
||||
},
|
||||
});
|
||||
|
||||
const toggleHiddenProjects = (isVisible) => ({
|
||||
type: ActionTypes.HIDDEN_PROJECTS_TOGGLE,
|
||||
payload: {
|
||||
isVisible,
|
||||
},
|
||||
});
|
||||
|
||||
const createProject = (data) => ({
|
||||
type: ActionTypes.PROJECT_CREATE,
|
||||
payload: {
|
||||
@@ -22,14 +48,28 @@ createProject.failure = (error) => ({
|
||||
},
|
||||
});
|
||||
|
||||
const handleProjectCreate = (project, users, projectManagers, boards, boardMemberships) => ({
|
||||
const handleProjectCreate = (
|
||||
project,
|
||||
users,
|
||||
projectManagers,
|
||||
backgroundImages,
|
||||
baseCustomFieldGroups,
|
||||
boards,
|
||||
boardMemberships,
|
||||
customFields,
|
||||
notificationServices,
|
||||
) => ({
|
||||
type: ActionTypes.PROJECT_CREATE_HANDLE,
|
||||
payload: {
|
||||
project,
|
||||
users,
|
||||
projectManagers,
|
||||
backgroundImages,
|
||||
baseCustomFieldGroups,
|
||||
boards,
|
||||
boardMemberships,
|
||||
customFields,
|
||||
notificationServices,
|
||||
},
|
||||
});
|
||||
|
||||
@@ -56,32 +96,56 @@ updateProject.failure = (id, error) => ({
|
||||
},
|
||||
});
|
||||
|
||||
const handleProjectUpdate = (project) => ({
|
||||
const handleProjectUpdate = (
|
||||
project,
|
||||
boardIds,
|
||||
isAvailable,
|
||||
board,
|
||||
users,
|
||||
projectManagers,
|
||||
backgroundImages,
|
||||
baseCustomFieldGroups,
|
||||
boards,
|
||||
boardMemberships,
|
||||
labels,
|
||||
lists,
|
||||
cards,
|
||||
cardMemberships,
|
||||
cardLabels,
|
||||
taskLists,
|
||||
tasks,
|
||||
attachments,
|
||||
customFieldGroups,
|
||||
customFields,
|
||||
customFieldValues,
|
||||
notificationsToDelete,
|
||||
notificationServices,
|
||||
) => ({
|
||||
type: ActionTypes.PROJECT_UPDATE_HANDLE,
|
||||
payload: {
|
||||
project,
|
||||
},
|
||||
});
|
||||
|
||||
const updateProjectBackgroundImage = (id) => ({
|
||||
type: ActionTypes.PROJECT_BACKGROUND_IMAGE_UPDATE,
|
||||
payload: {
|
||||
id,
|
||||
},
|
||||
});
|
||||
|
||||
updateProjectBackgroundImage.success = (project) => ({
|
||||
type: ActionTypes.PROJECT_BACKGROUND_IMAGE_UPDATE__SUCCESS,
|
||||
payload: {
|
||||
project,
|
||||
},
|
||||
});
|
||||
|
||||
updateProjectBackgroundImage.failure = (id, error) => ({
|
||||
type: ActionTypes.PROJECT_BACKGROUND_IMAGE_UPDATE__FAILURE,
|
||||
payload: {
|
||||
id,
|
||||
error,
|
||||
boardIds,
|
||||
isAvailable,
|
||||
board,
|
||||
users,
|
||||
projectManagers,
|
||||
backgroundImages,
|
||||
baseCustomFieldGroups,
|
||||
boards,
|
||||
boardMemberships,
|
||||
labels,
|
||||
lists,
|
||||
cards,
|
||||
cardMemberships,
|
||||
cardLabels,
|
||||
taskLists,
|
||||
tasks,
|
||||
attachments,
|
||||
customFieldGroups,
|
||||
customFields,
|
||||
customFieldValues,
|
||||
notificationsToDelete,
|
||||
notificationServices,
|
||||
},
|
||||
});
|
||||
|
||||
@@ -115,11 +179,13 @@ const handleProjectDelete = (project) => ({
|
||||
});
|
||||
|
||||
export default {
|
||||
searchProjects,
|
||||
updateProjectsOrder,
|
||||
toggleHiddenProjects,
|
||||
createProject,
|
||||
handleProjectCreate,
|
||||
updateProject,
|
||||
handleProjectUpdate,
|
||||
updateProjectBackgroundImage,
|
||||
deleteProject,
|
||||
handleProjectDelete,
|
||||
};
|
||||
|
||||
@@ -1,6 +1,15 @@
|
||||
/*!
|
||||
* Copyright (c) 2024 PLANKA Software GmbH
|
||||
* Licensed under the Fair Use License: https://github.com/plankanban/planka/blob/master/LICENSE.md
|
||||
*/
|
||||
|
||||
import ActionTypes from '../constants/ActionTypes';
|
||||
|
||||
const handleLocationChange = (
|
||||
pathname,
|
||||
currentBoardId,
|
||||
currentCardId,
|
||||
isEditModeEnabled,
|
||||
board,
|
||||
users,
|
||||
projects,
|
||||
@@ -10,12 +19,20 @@ const handleLocationChange = (
|
||||
cards,
|
||||
cardMemberships,
|
||||
cardLabels,
|
||||
taskLists,
|
||||
tasks,
|
||||
attachments,
|
||||
deletedNotifications,
|
||||
customFieldGroups,
|
||||
customFields,
|
||||
customFieldValues,
|
||||
notificationsToDelete,
|
||||
) => ({
|
||||
type: ActionTypes.LOCATION_CHANGE_HANDLE,
|
||||
payload: {
|
||||
pathname,
|
||||
currentBoardId,
|
||||
currentCardId,
|
||||
isEditModeEnabled,
|
||||
board,
|
||||
users,
|
||||
projects,
|
||||
@@ -25,12 +42,21 @@ const handleLocationChange = (
|
||||
cards,
|
||||
cardMemberships,
|
||||
cardLabels,
|
||||
taskLists,
|
||||
tasks,
|
||||
attachments,
|
||||
deletedNotifications,
|
||||
customFieldGroups,
|
||||
customFields,
|
||||
customFieldValues,
|
||||
notificationsToDelete,
|
||||
},
|
||||
});
|
||||
|
||||
handleLocationChange.fetchContent = () => ({
|
||||
type: ActionTypes.LOCATION_CHANGE_HANDLE__CONTENT_FETCH,
|
||||
payload: {},
|
||||
});
|
||||
|
||||
handleLocationChange.fetchBoard = (id) => ({
|
||||
type: ActionTypes.LOCATION_CHANGE_HANDLE__BOARD_FETCH,
|
||||
payload: {
|
||||
|
||||
@@ -1,3 +1,8 @@
|
||||
/*!
|
||||
* Copyright (c) 2024 PLANKA Software GmbH
|
||||
* Licensed under the Fair Use License: https://github.com/plankanban/planka/blob/master/LICENSE.md
|
||||
*/
|
||||
|
||||
import ActionTypes from '../constants/ActionTypes';
|
||||
|
||||
const handleSocketDisconnect = () => ({
|
||||
@@ -6,11 +11,14 @@ const handleSocketDisconnect = () => ({
|
||||
});
|
||||
|
||||
const handleSocketReconnect = (
|
||||
config,
|
||||
user,
|
||||
board,
|
||||
users,
|
||||
projects,
|
||||
projectManagers,
|
||||
backgroundImages,
|
||||
baseCustomFieldGroups,
|
||||
boards,
|
||||
boardMemberships,
|
||||
labels,
|
||||
@@ -18,18 +26,25 @@ const handleSocketReconnect = (
|
||||
cards,
|
||||
cardMemberships,
|
||||
cardLabels,
|
||||
taskLists,
|
||||
tasks,
|
||||
attachments,
|
||||
activities,
|
||||
customFieldGroups,
|
||||
customFields,
|
||||
customFieldValues,
|
||||
notifications,
|
||||
notificationServices,
|
||||
) => ({
|
||||
type: ActionTypes.SOCKET_RECONNECT_HANDLE,
|
||||
payload: {
|
||||
config,
|
||||
user,
|
||||
board,
|
||||
users,
|
||||
projects,
|
||||
projectManagers,
|
||||
backgroundImages,
|
||||
baseCustomFieldGroups,
|
||||
boards,
|
||||
boardMemberships,
|
||||
labels,
|
||||
@@ -37,10 +52,14 @@ const handleSocketReconnect = (
|
||||
cards,
|
||||
cardMemberships,
|
||||
cardLabels,
|
||||
taskLists,
|
||||
tasks,
|
||||
attachments,
|
||||
activities,
|
||||
customFieldGroups,
|
||||
customFields,
|
||||
customFieldValues,
|
||||
notifications,
|
||||
notificationServices,
|
||||
},
|
||||
});
|
||||
|
||||
|
||||
@@ -0,0 +1,104 @@
|
||||
/*!
|
||||
* Copyright (c) 2024 PLANKA Software GmbH
|
||||
* Licensed under the Fair Use License: https://github.com/plankanban/planka/blob/master/LICENSE.md
|
||||
*/
|
||||
|
||||
import ActionTypes from '../constants/ActionTypes';
|
||||
|
||||
const createTaskList = (taskList) => ({
|
||||
type: ActionTypes.TASK_LIST_CREATE,
|
||||
payload: {
|
||||
taskList,
|
||||
},
|
||||
});
|
||||
|
||||
createTaskList.success = (localId, taskList) => ({
|
||||
type: ActionTypes.TASK_LIST_CREATE__SUCCESS,
|
||||
payload: {
|
||||
localId,
|
||||
taskList,
|
||||
},
|
||||
});
|
||||
|
||||
createTaskList.failure = (localId, error) => ({
|
||||
type: ActionTypes.TASK_LIST_CREATE__FAILURE,
|
||||
payload: {
|
||||
localId,
|
||||
error,
|
||||
},
|
||||
});
|
||||
|
||||
const handleTaskListCreate = (taskList) => ({
|
||||
type: ActionTypes.TASK_LIST_CREATE_HANDLE,
|
||||
payload: {
|
||||
taskList,
|
||||
},
|
||||
});
|
||||
|
||||
const updateTaskList = (id, data) => ({
|
||||
type: ActionTypes.TASK_LIST_UPDATE,
|
||||
payload: {
|
||||
id,
|
||||
data,
|
||||
},
|
||||
});
|
||||
|
||||
updateTaskList.success = (taskList) => ({
|
||||
type: ActionTypes.TASK_LIST_UPDATE__SUCCESS,
|
||||
payload: {
|
||||
taskList,
|
||||
},
|
||||
});
|
||||
|
||||
updateTaskList.failure = (id, error) => ({
|
||||
type: ActionTypes.TASK_LIST_UPDATE__FAILURE,
|
||||
payload: {
|
||||
id,
|
||||
error,
|
||||
},
|
||||
});
|
||||
|
||||
const handleTaskListUpdate = (taskList) => ({
|
||||
type: ActionTypes.TASK_LIST_UPDATE_HANDLE,
|
||||
payload: {
|
||||
taskList,
|
||||
},
|
||||
});
|
||||
|
||||
const deleteTaskList = (id) => ({
|
||||
type: ActionTypes.TASK_LIST_DELETE,
|
||||
payload: {
|
||||
id,
|
||||
},
|
||||
});
|
||||
|
||||
deleteTaskList.success = (taskList) => ({
|
||||
type: ActionTypes.TASK_LIST_DELETE__SUCCESS,
|
||||
payload: {
|
||||
taskList,
|
||||
},
|
||||
});
|
||||
|
||||
deleteTaskList.failure = (id, error) => ({
|
||||
type: ActionTypes.TASK_LIST_DELETE__FAILURE,
|
||||
payload: {
|
||||
id,
|
||||
error,
|
||||
},
|
||||
});
|
||||
|
||||
const handleTaskListDelete = (taskList) => ({
|
||||
type: ActionTypes.TASK_LIST_DELETE_HANDLE,
|
||||
payload: {
|
||||
taskList,
|
||||
},
|
||||
});
|
||||
|
||||
export default {
|
||||
createTaskList,
|
||||
handleTaskListCreate,
|
||||
updateTaskList,
|
||||
handleTaskListUpdate,
|
||||
deleteTaskList,
|
||||
handleTaskListDelete,
|
||||
};
|
||||
@@ -1,3 +1,8 @@
|
||||
/*!
|
||||
* Copyright (c) 2024 PLANKA Software GmbH
|
||||
* Licensed under the Fair Use License: https://github.com/plankanban/planka/blob/master/LICENSE.md
|
||||
*/
|
||||
|
||||
import ActionTypes from '../constants/ActionTypes';
|
||||
|
||||
const createTask = (task) => ({
|
||||
|
||||
@@ -1,3 +1,8 @@
|
||||
/*!
|
||||
* Copyright (c) 2024 PLANKA Software GmbH
|
||||
* Licensed under the Fair Use License: https://github.com/plankanban/planka/blob/master/LICENSE.md
|
||||
*/
|
||||
|
||||
import ActionTypes from '../constants/ActionTypes';
|
||||
|
||||
const createUser = (data) => ({
|
||||
@@ -56,12 +61,60 @@ updateUser.failure = (id, error) => ({
|
||||
},
|
||||
});
|
||||
|
||||
const handleUserUpdate = (user, users, isCurrent) => ({
|
||||
const handleUserUpdate = (
|
||||
user,
|
||||
projectIds,
|
||||
boardIds,
|
||||
config,
|
||||
board,
|
||||
users,
|
||||
projects,
|
||||
projectManagers,
|
||||
backgroundImages,
|
||||
baseCustomFieldGroups,
|
||||
boards,
|
||||
boardMemberships,
|
||||
labels,
|
||||
lists,
|
||||
cards,
|
||||
cardMemberships,
|
||||
cardLabels,
|
||||
taskLists,
|
||||
tasks,
|
||||
attachments,
|
||||
customFieldGroups,
|
||||
customFields,
|
||||
customFieldValues,
|
||||
notificationsToDelete,
|
||||
notificationServices,
|
||||
) => ({
|
||||
type: ActionTypes.USER_UPDATE_HANDLE,
|
||||
payload: {
|
||||
user,
|
||||
projectIds,
|
||||
boardIds,
|
||||
config,
|
||||
board,
|
||||
users,
|
||||
isCurrent,
|
||||
projects,
|
||||
projectManagers,
|
||||
backgroundImages,
|
||||
baseCustomFieldGroups,
|
||||
boards,
|
||||
boardMemberships,
|
||||
labels,
|
||||
lists,
|
||||
cards,
|
||||
cardMemberships,
|
||||
cardLabels,
|
||||
taskLists,
|
||||
tasks,
|
||||
attachments,
|
||||
customFieldGroups,
|
||||
customFields,
|
||||
customFieldValues,
|
||||
notificationsToDelete,
|
||||
notificationServices,
|
||||
},
|
||||
});
|
||||
|
||||
@@ -270,19 +323,22 @@ const handleUserFromCardRemove = (cardMembership) => ({
|
||||
},
|
||||
});
|
||||
|
||||
const addUserToBoardFilter = (id, boardId) => ({
|
||||
const addUserToBoardFilter = (id, boardId, replace, currentListId) => ({
|
||||
type: ActionTypes.USER_TO_BOARD_FILTER_ADD,
|
||||
payload: {
|
||||
id,
|
||||
boardId,
|
||||
replace,
|
||||
currentListId,
|
||||
},
|
||||
});
|
||||
|
||||
const removeUserFromBoardFilter = (id, boardId) => ({
|
||||
const removeUserFromBoardFilter = (id, boardId, currentListId) => ({
|
||||
type: ActionTypes.USER_FROM_BOARD_FILTER_REMOVE,
|
||||
payload: {
|
||||
id,
|
||||
boardId,
|
||||
currentListId,
|
||||
},
|
||||
});
|
||||
|
||||
|
||||
@@ -1,3 +1,8 @@
|
||||
/*!
|
||||
* Copyright (c) 2024 PLANKA Software GmbH
|
||||
* Licensed under the Fair Use License: https://github.com/plankanban/planka/blob/master/LICENSE.md
|
||||
*/
|
||||
|
||||
import http from './http';
|
||||
|
||||
/* Actions */
|
||||
@@ -5,13 +10,13 @@ import http from './http';
|
||||
const createAccessToken = (data, headers) =>
|
||||
http.post('/access-tokens?withHttpOnlyToken=true', data, headers);
|
||||
|
||||
const exchangeForAccessTokenUsingOidc = (data, headers) =>
|
||||
http.post('/access-tokens/exchange-using-oidc?withHttpOnlyToken=true', data, headers);
|
||||
const exchangeForAccessTokenWithOidc = (data, headers) =>
|
||||
http.post('/access-tokens/exchange-with-oidc?withHttpOnlyToken=true', data, headers);
|
||||
|
||||
const deleteCurrentAccessToken = (headers) => http.delete('/access-tokens/me', undefined, headers);
|
||||
|
||||
export default {
|
||||
createAccessToken,
|
||||
exchangeForAccessTokenUsingOidc,
|
||||
exchangeForAccessTokenWithOidc,
|
||||
deleteCurrentAccessToken,
|
||||
};
|
||||
|
||||
@@ -1,11 +1,17 @@
|
||||
/*!
|
||||
* Copyright (c) 2024 PLANKA Software GmbH
|
||||
* Licensed under the Fair Use License: https://github.com/plankanban/planka/blob/master/LICENSE.md
|
||||
*/
|
||||
|
||||
import socket from './socket';
|
||||
import { transformUser } from './users';
|
||||
|
||||
/* Transformers */
|
||||
|
||||
export const transformActivity = (activity) => ({
|
||||
...activity,
|
||||
createdAt: new Date(activity.createdAt),
|
||||
...(activity.createdAt && {
|
||||
createdAt: new Date(activity.createdAt),
|
||||
}),
|
||||
});
|
||||
|
||||
/* Actions */
|
||||
@@ -14,10 +20,6 @@ const getActivities = (cardId, data, headers) =>
|
||||
socket.get(`/cards/${cardId}/actions`, data, headers).then((body) => ({
|
||||
...body,
|
||||
items: body.items.map(transformActivity),
|
||||
included: {
|
||||
...body.included,
|
||||
users: body.included.users.map(transformUser),
|
||||
},
|
||||
}));
|
||||
|
||||
/* Event handlers */
|
||||
@@ -29,13 +31,7 @@ const makeHandleActivityCreate = (next) => (body) => {
|
||||
});
|
||||
};
|
||||
|
||||
const makeHandleActivityUpdate = makeHandleActivityCreate;
|
||||
|
||||
const makeHandleActivityDelete = makeHandleActivityCreate;
|
||||
|
||||
export default {
|
||||
getActivities,
|
||||
makeHandleActivityCreate,
|
||||
makeHandleActivityUpdate,
|
||||
makeHandleActivityDelete,
|
||||
};
|
||||
|
||||
@@ -1,3 +1,8 @@
|
||||
/*!
|
||||
* Copyright (c) 2024 PLANKA Software GmbH
|
||||
* Licensed under the Fair Use License: https://github.com/plankanban/planka/blob/master/LICENSE.md
|
||||
*/
|
||||
|
||||
import http from './http';
|
||||
import socket from './socket';
|
||||
|
||||
@@ -5,17 +10,34 @@ import socket from './socket';
|
||||
|
||||
export const transformAttachment = (attachment) => ({
|
||||
...attachment,
|
||||
createdAt: new Date(attachment.createdAt),
|
||||
...(attachment.createdAt && {
|
||||
createdAt: new Date(attachment.createdAt),
|
||||
}),
|
||||
});
|
||||
|
||||
/* Actions */
|
||||
|
||||
const createAttachment = (cardId, data, requestId, headers) =>
|
||||
http.post(`/cards/${cardId}/attachments?requestId=${requestId}`, data, headers).then((body) => ({
|
||||
const createAttachment = (cardId, data, headers) =>
|
||||
socket.post(`/cards/${cardId}/attachments`, data, headers).then((body) => ({
|
||||
...body,
|
||||
item: transformAttachment(body.item),
|
||||
}));
|
||||
|
||||
const createAttachmentWithFile = (cardId, { file, ...data }, requestId, headers) =>
|
||||
http
|
||||
.post(
|
||||
`/cards/${cardId}/attachments?requestId=${requestId}`,
|
||||
{
|
||||
...data,
|
||||
file,
|
||||
},
|
||||
headers,
|
||||
)
|
||||
.then((body) => ({
|
||||
...body,
|
||||
item: transformAttachment(body.item),
|
||||
}));
|
||||
|
||||
const updateAttachment = (id, data, headers) =>
|
||||
socket.patch(`/attachments/${id}`, data, headers).then((body) => ({
|
||||
...body,
|
||||
@@ -43,6 +65,7 @@ const makeHandleAttachmentDelete = makeHandleAttachmentCreate;
|
||||
|
||||
export default {
|
||||
createAttachment,
|
||||
createAttachmentWithFile,
|
||||
updateAttachment,
|
||||
deleteAttachment,
|
||||
makeHandleAttachmentCreate,
|
||||
|
||||
@@ -0,0 +1,27 @@
|
||||
/*!
|
||||
* Copyright (c) 2024 PLANKA Software GmbH
|
||||
* Licensed under the Fair Use License: https://github.com/plankanban/planka/blob/master/LICENSE.md
|
||||
*/
|
||||
|
||||
import http from './http';
|
||||
import socket from './socket';
|
||||
|
||||
/* Actions */
|
||||
|
||||
const createBackgroundImage = (projectId, { file, ...data }, requestId, headers) =>
|
||||
http.post(
|
||||
`/projects/${projectId}/background-images?requestId=${requestId}`,
|
||||
{
|
||||
...data,
|
||||
file,
|
||||
},
|
||||
headers,
|
||||
);
|
||||
|
||||
const deleteBackgroundImage = (id, headers) =>
|
||||
socket.delete(`/background-images/${id}`, undefined, headers);
|
||||
|
||||
export default {
|
||||
createBackgroundImage,
|
||||
deleteBackgroundImage,
|
||||
};
|
||||
Executable
+23
@@ -0,0 +1,23 @@
|
||||
/*!
|
||||
* Copyright (c) 2024 PLANKA Software GmbH
|
||||
* Licensed under the Fair Use License: https://github.com/plankanban/planka/blob/master/LICENSE.md
|
||||
*/
|
||||
|
||||
import socket from './socket';
|
||||
|
||||
/* Actions */
|
||||
|
||||
const createBaseCustomFieldGroup = (projectId, data, headers) =>
|
||||
socket.post(`/projects/${projectId}/base-custom-field-groups`, data, headers);
|
||||
|
||||
const updateBaseCustomFieldGroup = (id, data, headers) =>
|
||||
socket.patch(`/base-custom-field-groups/${id}`, data, headers);
|
||||
|
||||
const deleteBaseCustomFieldGroup = (id, headers) =>
|
||||
socket.delete(`/base-custom-field-groups/${id}`, undefined, headers);
|
||||
|
||||
export default {
|
||||
createBaseCustomFieldGroup,
|
||||
updateBaseCustomFieldGroup,
|
||||
deleteBaseCustomFieldGroup,
|
||||
};
|
||||
@@ -1,50 +1,23 @@
|
||||
/*!
|
||||
* Copyright (c) 2024 PLANKA Software GmbH
|
||||
* Licensed under the Fair Use License: https://github.com/plankanban/planka/blob/master/LICENSE.md
|
||||
*/
|
||||
|
||||
import socket from './socket';
|
||||
|
||||
/* Transformers */
|
||||
|
||||
export const transformBoardMembership = (boardMembership) => ({
|
||||
...boardMembership,
|
||||
createdAt: new Date(boardMembership.createdAt),
|
||||
});
|
||||
|
||||
/* Actions */
|
||||
|
||||
const createBoardMembership = (boardId, data, headers) =>
|
||||
socket.post(`/boards/${boardId}/memberships`, data, headers).then((body) => ({
|
||||
...body,
|
||||
item: transformBoardMembership(body.item),
|
||||
}));
|
||||
socket.post(`/boards/${boardId}/board-memberships`, data, headers);
|
||||
|
||||
const updateBoardMembership = (id, data, headers) =>
|
||||
socket.patch(`/board-memberships/${id}`, data, headers).then((body) => ({
|
||||
...body,
|
||||
item: transformBoardMembership(body.item),
|
||||
}));
|
||||
socket.patch(`/board-memberships/${id}`, data, headers);
|
||||
|
||||
const deleteBoardMembership = (id, headers) =>
|
||||
socket.delete(`/board-memberships/${id}`, undefined, headers).then((body) => ({
|
||||
...body,
|
||||
item: transformBoardMembership(body.item),
|
||||
}));
|
||||
|
||||
/* Event handlers */
|
||||
|
||||
const makeHandleBoardMembershipCreate = (next) => (body) => {
|
||||
next({
|
||||
...body,
|
||||
item: transformBoardMembership(body.item),
|
||||
});
|
||||
};
|
||||
|
||||
const makeHandleBoardMembershipUpdate = makeHandleBoardMembershipCreate;
|
||||
|
||||
const makeHandleBoardMembershipDelete = makeHandleBoardMembershipCreate;
|
||||
socket.delete(`/board-memberships/${id}`, undefined, headers);
|
||||
|
||||
export default {
|
||||
createBoardMembership,
|
||||
updateBoardMembership,
|
||||
deleteBoardMembership,
|
||||
makeHandleBoardMembershipCreate,
|
||||
makeHandleBoardMembershipUpdate,
|
||||
makeHandleBoardMembershipDelete,
|
||||
};
|
||||
|
||||
@@ -1,20 +1,17 @@
|
||||
import socket from './socket';
|
||||
/*!
|
||||
* Copyright (c) 2024 PLANKA Software GmbH
|
||||
* Licensed under the Fair Use License: https://github.com/plankanban/planka/blob/master/LICENSE.md
|
||||
*/
|
||||
|
||||
import http from './http';
|
||||
import { transformUser } from './users';
|
||||
import { transformBoardMembership } from './board-memberships';
|
||||
import socket from './socket';
|
||||
import { transformCard } from './cards';
|
||||
import { transformAttachment } from './attachments';
|
||||
|
||||
/* Actions */
|
||||
|
||||
const createBoard = (projectId, data, headers) =>
|
||||
socket.post(`/projects/${projectId}/boards`, data, headers).then((body) => ({
|
||||
...body,
|
||||
included: {
|
||||
...body.included,
|
||||
boardMemberships: body.included.boardMemberships.map(transformBoardMembership),
|
||||
},
|
||||
}));
|
||||
socket.post(`/projects/${projectId}/boards`, data, headers);
|
||||
|
||||
const createBoardWithImport = (projectId, data, requestId, headers) =>
|
||||
http.post(`/projects/${projectId}/boards?requestId=${requestId}`, data, headers);
|
||||
@@ -26,8 +23,6 @@ const getBoard = (id, subscribe, headers) =>
|
||||
...body,
|
||||
included: {
|
||||
...body.included,
|
||||
users: body.included.users.map(transformUser),
|
||||
boardMemberships: body.included.boardMemberships.map(transformBoardMembership),
|
||||
cards: body.included.cards.map(transformCard),
|
||||
attachments: body.included.attachments.map(transformAttachment),
|
||||
},
|
||||
|
||||
@@ -1,12 +1,17 @@
|
||||
/*!
|
||||
* Copyright (c) 2024 PLANKA Software GmbH
|
||||
* Licensed under the Fair Use License: https://github.com/plankanban/planka/blob/master/LICENSE.md
|
||||
*/
|
||||
|
||||
import socket from './socket';
|
||||
|
||||
/* Actions */
|
||||
|
||||
const createCardLabel = (cardId, data, headers) =>
|
||||
socket.post(`/cards/${cardId}/labels`, data, headers);
|
||||
socket.post(`/cards/${cardId}/card-labels`, data, headers);
|
||||
|
||||
const deleteCardLabel = (cardId, labelId, headers) =>
|
||||
socket.delete(`/cards/${cardId}/labels/${labelId}`, undefined, headers);
|
||||
socket.delete(`/cards/${cardId}/card-labels/labelId:${labelId}`, undefined, headers);
|
||||
|
||||
export default {
|
||||
createCardLabel,
|
||||
|
||||
@@ -1,12 +1,17 @@
|
||||
/*!
|
||||
* Copyright (c) 2024 PLANKA Software GmbH
|
||||
* Licensed under the Fair Use License: https://github.com/plankanban/planka/blob/master/LICENSE.md
|
||||
*/
|
||||
|
||||
import socket from './socket';
|
||||
|
||||
/* Actions */
|
||||
|
||||
const createCardMembership = (cardId, data, headers) =>
|
||||
socket.post(`/cards/${cardId}/memberships`, data, headers);
|
||||
socket.post(`/cards/${cardId}/card-memberships`, data, headers);
|
||||
|
||||
const deleteCardMembership = (cardId, userId, headers) =>
|
||||
socket.delete(`/cards/${cardId}/memberships?userId=${userId}`, undefined, headers);
|
||||
socket.delete(`/cards/${cardId}/card-memberships/userId:${userId}`, undefined, headers);
|
||||
|
||||
export default {
|
||||
createCardMembership,
|
||||
|
||||
+55
-2
@@ -1,5 +1,14 @@
|
||||
/*!
|
||||
* Copyright (c) 2024 PLANKA Software GmbH
|
||||
* Licensed under the Fair Use License: https://github.com/plankanban/planka/blob/master/LICENSE.md
|
||||
*/
|
||||
|
||||
import omit from 'lodash/omit';
|
||||
|
||||
import socket from './socket';
|
||||
import { transformAttachment } from './attachments';
|
||||
import { transformActivity } from './activities';
|
||||
import { transformNotification } from './notifications';
|
||||
|
||||
/* Transformers */
|
||||
|
||||
@@ -16,6 +25,12 @@ export const transformCard = (card) => ({
|
||||
}),
|
||||
},
|
||||
}),
|
||||
...(card.createdAt && {
|
||||
createdAt: new Date(card.createdAt),
|
||||
}),
|
||||
...(card.listChangedAt && {
|
||||
listChangedAt: new Date(card.listChangedAt),
|
||||
}),
|
||||
});
|
||||
|
||||
export const transformCardData = (data) => ({
|
||||
@@ -35,6 +50,16 @@ export const transformCardData = (data) => ({
|
||||
|
||||
/* Actions */
|
||||
|
||||
const getCards = (listId, data, headers) =>
|
||||
socket.get(`/lists/${listId}/cards`, data, headers).then((body) => ({
|
||||
...body,
|
||||
items: body.items.map(transformCard),
|
||||
included: {
|
||||
...body.included,
|
||||
attachments: body.included.attachments.map(transformAttachment),
|
||||
},
|
||||
}));
|
||||
|
||||
const createCard = (listId, data, headers) =>
|
||||
socket.post(`/lists/${listId}/cards`, transformCardData(data), headers).then((body) => ({
|
||||
...body,
|
||||
@@ -61,6 +86,20 @@ const duplicateCard = (id, data, headers) =>
|
||||
socket.post(`/cards/${id}/duplicate`, data, headers).then((body) => ({
|
||||
...body,
|
||||
item: transformCard(body.item),
|
||||
included: {
|
||||
...body.included,
|
||||
attachments: body.included.attachments.map(transformAttachment),
|
||||
},
|
||||
}));
|
||||
|
||||
const readCardNotifications = (id, headers) =>
|
||||
socket.post(`/cards/${id}/read-notifications`, undefined, headers).then((body) => ({
|
||||
...body,
|
||||
item: transformCard(body.item),
|
||||
included: {
|
||||
...body.included,
|
||||
notifications: body.included.notifications.map(transformNotification),
|
||||
},
|
||||
}));
|
||||
|
||||
const deleteCard = (id, headers) =>
|
||||
@@ -71,6 +110,17 @@ const deleteCard = (id, headers) =>
|
||||
|
||||
/* Event handlers */
|
||||
|
||||
const makeHandleCardsUpdate = (next) => (body) => {
|
||||
next({
|
||||
...body,
|
||||
items: body.items.map(transformCard),
|
||||
included: body.included && {
|
||||
...omit(body.included, 'actions'),
|
||||
activities: body.included.actions.map(transformActivity),
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
const makeHandleCardCreate = (next) => (body) => {
|
||||
next({
|
||||
...body,
|
||||
@@ -80,14 +130,17 @@ const makeHandleCardCreate = (next) => (body) => {
|
||||
|
||||
const makeHandleCardUpdate = makeHandleCardCreate;
|
||||
|
||||
const makeHandleCardDelete = makeHandleCardCreate;
|
||||
const makeHandleCardDelete = makeHandleCardUpdate;
|
||||
|
||||
export default {
|
||||
getCards,
|
||||
createCard,
|
||||
getCard,
|
||||
updateCard,
|
||||
deleteCard,
|
||||
duplicateCard,
|
||||
readCardNotifications,
|
||||
deleteCard,
|
||||
makeHandleCardsUpdate,
|
||||
makeHandleCardCreate,
|
||||
makeHandleCardUpdate,
|
||||
makeHandleCardDelete,
|
||||
|
||||
@@ -1,28 +0,0 @@
|
||||
import socket from './socket';
|
||||
import { transformActivity } from './activities';
|
||||
|
||||
/* Actions */
|
||||
|
||||
const createCommentActivity = (cardId, data, headers) =>
|
||||
socket.post(`/cards/${cardId}/comment-actions`, data, headers).then((body) => ({
|
||||
...body,
|
||||
item: transformActivity(body.item),
|
||||
}));
|
||||
|
||||
const updateCommentActivity = (id, data, headers) =>
|
||||
socket.patch(`/comment-actions/${id}`, data, headers).then((body) => ({
|
||||
...body,
|
||||
item: transformActivity(body.item),
|
||||
}));
|
||||
|
||||
const deleteCommentActivity = (id, headers) =>
|
||||
socket.delete(`/comment-actions/${id}`, undefined, headers).then((body) => ({
|
||||
...body,
|
||||
item: transformActivity(body.item),
|
||||
}));
|
||||
|
||||
export default {
|
||||
createCommentActivity,
|
||||
updateCommentActivity,
|
||||
deleteCommentActivity,
|
||||
};
|
||||
@@ -0,0 +1,64 @@
|
||||
/*!
|
||||
* Copyright (c) 2024 PLANKA Software GmbH
|
||||
* Licensed under the Fair Use License: https://github.com/plankanban/planka/blob/master/LICENSE.md
|
||||
*/
|
||||
|
||||
import socket from './socket';
|
||||
|
||||
/* Transformers */
|
||||
|
||||
export const transformComment = (comment) => ({
|
||||
...comment,
|
||||
...(comment.createdAt && {
|
||||
createdAt: new Date(comment.createdAt),
|
||||
}),
|
||||
});
|
||||
|
||||
/* Actions */
|
||||
|
||||
const getComments = (cardId, data, headers) =>
|
||||
socket.get(`/cards/${cardId}/comments`, data, headers).then((body) => ({
|
||||
...body,
|
||||
items: body.items.map(transformComment),
|
||||
}));
|
||||
|
||||
const createComment = (cardId, data, headers) =>
|
||||
socket.post(`/cards/${cardId}/comments`, data, headers).then((body) => ({
|
||||
...body,
|
||||
item: transformComment(body.item),
|
||||
}));
|
||||
|
||||
const updateComment = (id, data, headers) =>
|
||||
socket.patch(`/comments/${id}`, data, headers).then((body) => ({
|
||||
...body,
|
||||
item: transformComment(body.item),
|
||||
}));
|
||||
|
||||
const deleteComment = (id, headers) =>
|
||||
socket.delete(`/comments/${id}`, undefined, headers).then((body) => ({
|
||||
...body,
|
||||
item: transformComment(body.item),
|
||||
}));
|
||||
|
||||
/* Event handlers */
|
||||
|
||||
const makeHandleCommentCreate = (next) => (body) => {
|
||||
next({
|
||||
...body,
|
||||
item: transformComment(body.item),
|
||||
});
|
||||
};
|
||||
|
||||
const makeHandleCommentUpdate = makeHandleCommentCreate;
|
||||
|
||||
const makeHandleCommentDelete = makeHandleCommentUpdate;
|
||||
|
||||
export default {
|
||||
getComments,
|
||||
createComment,
|
||||
updateComment,
|
||||
deleteComment,
|
||||
makeHandleCommentCreate,
|
||||
makeHandleCommentUpdate,
|
||||
makeHandleCommentDelete,
|
||||
};
|
||||
@@ -0,0 +1,14 @@
|
||||
/*!
|
||||
* Copyright (c) 2024 PLANKA Software GmbH
|
||||
* Licensed under the Fair Use License: https://github.com/plankanban/planka/blob/master/LICENSE.md
|
||||
*/
|
||||
|
||||
import http from './http';
|
||||
|
||||
/* Actions */
|
||||
|
||||
const getConfig = (headers) => http.get('/config', undefined, headers);
|
||||
|
||||
export default {
|
||||
getConfig,
|
||||
};
|
||||
Executable
+31
@@ -0,0 +1,31 @@
|
||||
/*!
|
||||
* Copyright (c) 2024 PLANKA Software GmbH
|
||||
* Licensed under the Fair Use License: https://github.com/plankanban/planka/blob/master/LICENSE.md
|
||||
*/
|
||||
|
||||
import socket from './socket';
|
||||
|
||||
/* Actions */
|
||||
|
||||
const createCustomFieldGroupInBoard = (cardId, data, headers) =>
|
||||
socket.post(`/boards/${cardId}/custom-field-groups`, data, headers);
|
||||
|
||||
const createCustomFieldGroupInCard = (cardId, data, headers) =>
|
||||
socket.post(`/cards/${cardId}/custom-field-groups`, data, headers);
|
||||
|
||||
const getCustomFieldGroup = (id, headers) =>
|
||||
socket.get(`/custom-field-groups/${id}`, undefined, headers);
|
||||
|
||||
const updateCustomFieldGroup = (id, data, headers) =>
|
||||
socket.patch(`/custom-field-groups/${id}`, data, headers);
|
||||
|
||||
const deleteCustomFieldGroup = (id, headers) =>
|
||||
socket.delete(`/custom-field-groups/${id}`, undefined, headers);
|
||||
|
||||
export default {
|
||||
createCustomFieldGroupInBoard,
|
||||
createCustomFieldGroupInCard,
|
||||
getCustomFieldGroup,
|
||||
updateCustomFieldGroup,
|
||||
deleteCustomFieldGroup,
|
||||
};
|
||||
@@ -0,0 +1,27 @@
|
||||
/*!
|
||||
* Copyright (c) 2024 PLANKA Software GmbH
|
||||
* Licensed under the Fair Use License: https://github.com/plankanban/planka/blob/master/LICENSE.md
|
||||
*/
|
||||
|
||||
import socket from './socket';
|
||||
|
||||
/* Actions */
|
||||
|
||||
const updateCustomFieldValue = (cardId, customFieldGroupId, customFieldId, data, headers) =>
|
||||
socket.patch(
|
||||
`/cards/${cardId}/custom-field-values/customFieldGroupId:${customFieldGroupId}:customFieldId:${customFieldId}`,
|
||||
data,
|
||||
headers,
|
||||
);
|
||||
|
||||
const deleteCustomFieldValue = (cardId, customFieldGroupId, customFieldId, headers) =>
|
||||
socket.delete(
|
||||
`/cards/${cardId}/custom-field-values/customFieldGroupId:${customFieldGroupId}:customFieldId:${customFieldId}`,
|
||||
undefined,
|
||||
headers,
|
||||
);
|
||||
|
||||
export default {
|
||||
updateCustomFieldValue,
|
||||
deleteCustomFieldValue,
|
||||
};
|
||||
Executable
+27
@@ -0,0 +1,27 @@
|
||||
/*!
|
||||
* Copyright (c) 2024 PLANKA Software GmbH
|
||||
* Licensed under the Fair Use License: https://github.com/plankanban/planka/blob/master/LICENSE.md
|
||||
*/
|
||||
|
||||
import socket from './socket';
|
||||
|
||||
/* Actions */
|
||||
|
||||
const createCustomFieldInBaseGroup = (baseCustomFieldGroupId, data, headers) =>
|
||||
socket.post(`/base-custom-field-groups/${baseCustomFieldGroupId}/custom-fields`, data, headers);
|
||||
|
||||
const createCustomFieldInGroup = (customFieldGroupId, data, headers) =>
|
||||
socket.post(`/custom-field-groups/${customFieldGroupId}/custom-fields`, data, headers);
|
||||
|
||||
const updateCustomField = (id, data, headers) =>
|
||||
socket.patch(`/custom-fields/${id}`, data, headers);
|
||||
|
||||
const deleteCustomField = (id, headers) =>
|
||||
socket.delete(`/custom-fields/${id}`, undefined, headers);
|
||||
|
||||
export default {
|
||||
createCustomFieldInBaseGroup,
|
||||
createCustomFieldInGroup,
|
||||
updateCustomField,
|
||||
deleteCustomField,
|
||||
};
|
||||
@@ -1,4 +1,7 @@
|
||||
import { fetch } from 'whatwg-fetch';
|
||||
/*!
|
||||
* Copyright (c) 2024 PLANKA Software GmbH
|
||||
* Licensed under the Fair Use License: https://github.com/plankanban/planka/blob/master/LICENSE.md
|
||||
*/
|
||||
|
||||
import Config from '../constants/Config';
|
||||
|
||||
|
||||
+23
-4
@@ -1,10 +1,17 @@
|
||||
/*!
|
||||
* Copyright (c) 2024 PLANKA Software GmbH
|
||||
* Licensed under the Fair Use License: https://github.com/plankanban/planka/blob/master/LICENSE.md
|
||||
*/
|
||||
|
||||
import http from './http';
|
||||
import socket from './socket';
|
||||
import root from './root';
|
||||
import config from './config';
|
||||
import accessTokens from './access-tokens';
|
||||
import users from './users';
|
||||
import projects from './projects';
|
||||
import projectManagers from './project-managers';
|
||||
import backgroundImages from './background-images';
|
||||
import baseCustomFieldGroups from './base-custom-field-groups';
|
||||
import boards from './boards';
|
||||
import boardMemberships from './board-memberships';
|
||||
import labels from './labels';
|
||||
@@ -12,20 +19,27 @@ import lists from './lists';
|
||||
import cards from './cards';
|
||||
import cardMemberships from './card-memberships';
|
||||
import cardLabels from './card-labels';
|
||||
import taskLists from './task-lists';
|
||||
import tasks from './tasks';
|
||||
import attachments from './attachments';
|
||||
import customFieldGroups from './custom-field-groups';
|
||||
import customFields from './custom-fields';
|
||||
import customFieldValues from './custom-field-values';
|
||||
import comments from './comments';
|
||||
import activities from './activities';
|
||||
import commentActivities from './comment-activities';
|
||||
import notifications from './notifications';
|
||||
import notificationServices from './notification-services';
|
||||
|
||||
export { http, socket };
|
||||
|
||||
export default {
|
||||
...root,
|
||||
...config,
|
||||
...accessTokens,
|
||||
...users,
|
||||
...projects,
|
||||
...projectManagers,
|
||||
...backgroundImages,
|
||||
...baseCustomFieldGroups,
|
||||
...boards,
|
||||
...boardMemberships,
|
||||
...labels,
|
||||
@@ -33,9 +47,14 @@ export default {
|
||||
...cards,
|
||||
...cardMemberships,
|
||||
...cardLabels,
|
||||
...taskLists,
|
||||
...tasks,
|
||||
...attachments,
|
||||
...customFieldGroups,
|
||||
...customFields,
|
||||
...customFieldValues,
|
||||
...comments,
|
||||
...activities,
|
||||
...commentActivities,
|
||||
...notifications,
|
||||
...notificationServices,
|
||||
};
|
||||
|
||||
@@ -1,3 +1,8 @@
|
||||
/*!
|
||||
* Copyright (c) 2024 PLANKA Software GmbH
|
||||
* Licensed under the Fair Use License: https://github.com/plankanban/planka/blob/master/LICENSE.md
|
||||
*/
|
||||
|
||||
import socket from './socket';
|
||||
|
||||
/* Actions */
|
||||
|
||||
+44
-3
@@ -1,11 +1,30 @@
|
||||
/*!
|
||||
* Copyright (c) 2024 PLANKA Software GmbH
|
||||
* Licensed under the Fair Use License: https://github.com/plankanban/planka/blob/master/LICENSE.md
|
||||
*/
|
||||
|
||||
import omit from 'lodash/omit';
|
||||
|
||||
import socket from './socket';
|
||||
import { transformCard } from './cards';
|
||||
import { transformAttachment } from './attachments';
|
||||
import { transformActivity } from './activities';
|
||||
|
||||
/* Actions */
|
||||
|
||||
const createList = (boardId, data, headers) =>
|
||||
socket.post(`/boards/${boardId}/lists`, data, headers);
|
||||
|
||||
const getList = (id, headers) =>
|
||||
socket.get(`/lists/${id}`, undefined, headers).then((body) => ({
|
||||
...body,
|
||||
included: {
|
||||
...body.included,
|
||||
cards: body.included.cards.map(transformCard),
|
||||
attachments: body.included.attachments.map(transformAttachment),
|
||||
},
|
||||
}));
|
||||
|
||||
const updateList = (id, data, headers) => socket.patch(`/lists/${id}`, data, headers);
|
||||
|
||||
const sortList = (id, data, headers) =>
|
||||
@@ -17,11 +36,30 @@ const sortList = (id, data, headers) =>
|
||||
},
|
||||
}));
|
||||
|
||||
const deleteList = (id, headers) => socket.delete(`/lists/${id}`, undefined, headers);
|
||||
const moveListCards = (id, data, headers) =>
|
||||
socket.post(`/lists/${id}/move-cards`, data, headers).then((body) => ({
|
||||
...body,
|
||||
included: {
|
||||
...omit(body.included, 'actions'),
|
||||
cards: body.included.cards.map(transformCard),
|
||||
activities: body.included.actions.map(transformActivity),
|
||||
},
|
||||
}));
|
||||
|
||||
const clearList = (id, headers) => socket.post(`/lists/${id}/clear`, undefined, headers);
|
||||
|
||||
const deleteList = (id, headers) =>
|
||||
socket.delete(`/lists/${id}`, undefined, headers).then((body) => ({
|
||||
...body,
|
||||
included: {
|
||||
...body.included,
|
||||
cards: body.included.cards.map(transformCard),
|
||||
},
|
||||
}));
|
||||
|
||||
/* Event handlers */
|
||||
|
||||
const makeHandleListSort = (next) => (body) => {
|
||||
const makeHandleListDelete = (next) => (body) => {
|
||||
next({
|
||||
...body,
|
||||
included: {
|
||||
@@ -33,8 +71,11 @@ const makeHandleListSort = (next) => (body) => {
|
||||
|
||||
export default {
|
||||
createList,
|
||||
getList,
|
||||
updateList,
|
||||
sortList,
|
||||
moveListCards,
|
||||
clearList,
|
||||
deleteList,
|
||||
makeHandleListSort,
|
||||
makeHandleListDelete,
|
||||
};
|
||||
|
||||
Executable
+31
@@ -0,0 +1,31 @@
|
||||
/*!
|
||||
* Copyright (c) 2024 PLANKA Software GmbH
|
||||
* Licensed under the Fair Use License: https://github.com/plankanban/planka/blob/master/LICENSE.md
|
||||
*/
|
||||
|
||||
import socket from './socket';
|
||||
|
||||
/* Actions */
|
||||
|
||||
const createNotificationServiceInUser = (userId, data, headers) =>
|
||||
socket.post(`/users/${userId}/notification-services`, data, headers);
|
||||
|
||||
const createNotificationServiceInBoard = (boardId, data, headers) =>
|
||||
socket.post(`/boards/${boardId}/notification-services`, data, headers);
|
||||
|
||||
const updateNotificationService = (id, data, headers) =>
|
||||
socket.patch(`/notification-services/${id}`, data, headers);
|
||||
|
||||
const testNotificationService = (id, headers) =>
|
||||
socket.post(`/notification-services/${id}/test`, undefined, headers);
|
||||
|
||||
const deleteNotificationService = (id, headers) =>
|
||||
socket.delete(`/notification-services/${id}`, undefined, headers);
|
||||
|
||||
export default {
|
||||
createNotificationServiceInUser,
|
||||
createNotificationServiceInBoard,
|
||||
updateNotificationService,
|
||||
testNotificationService,
|
||||
deleteNotificationService,
|
||||
};
|
||||
@@ -1,15 +1,21 @@
|
||||
/*!
|
||||
* Copyright (c) 2024 PLANKA Software GmbH
|
||||
* Licensed under the Fair Use License: https://github.com/plankanban/planka/blob/master/LICENSE.md
|
||||
*/
|
||||
|
||||
import omit from 'lodash/omit';
|
||||
|
||||
import socket from './socket';
|
||||
import { transformUser } from './users';
|
||||
import { transformCard } from './cards';
|
||||
import { transformActivity } from './activities';
|
||||
|
||||
/* Transformers */
|
||||
|
||||
export const transformNotification = (notification) => ({
|
||||
...omit(notification, 'actionId'),
|
||||
activityId: notification.actionId,
|
||||
...(notification.actionId
|
||||
? {
|
||||
...omit(notification, 'actionId'),
|
||||
activityId: notification.actionId,
|
||||
}
|
||||
: notification),
|
||||
});
|
||||
|
||||
/* Actions */
|
||||
@@ -18,28 +24,25 @@ const getNotifications = (headers) =>
|
||||
socket.get('/notifications', undefined, headers).then((body) => ({
|
||||
...body,
|
||||
items: body.items.map(transformNotification),
|
||||
included: {
|
||||
...omit(body.included, 'actions'),
|
||||
users: body.included.users.map(transformUser),
|
||||
cards: body.included.cards.map(transformCard),
|
||||
activities: body.included.actions.map(transformActivity),
|
||||
},
|
||||
}));
|
||||
|
||||
const getNotification = (id, headers) =>
|
||||
/* const getNotification = (id, headers) =>
|
||||
socket.get(`/notifications/${id}`, undefined, headers).then((body) => ({
|
||||
...body,
|
||||
item: transformNotification(body.item),
|
||||
included: {
|
||||
...omit(body.included, 'actions'),
|
||||
users: body.included.users.map(transformUser),
|
||||
cards: body.included.cards.map(transformCard),
|
||||
activities: body.included.actions.map(transformActivity),
|
||||
},
|
||||
})); */
|
||||
|
||||
const updateNotification = (id, data, headers) =>
|
||||
socket.patch(`/notifications/${id}`, data, headers).then((body) => ({
|
||||
...body,
|
||||
item: transformNotification(body.item),
|
||||
}));
|
||||
|
||||
const updateNotifications = (ids, data, headers) =>
|
||||
socket.patch(`/notifications/${ids.join(',')}`, data, headers).then((body) => ({
|
||||
const readAllNotifications = (headers) =>
|
||||
socket.post('/notifications/read-all', undefined, headers).then((body) => ({
|
||||
...body,
|
||||
items: body.items.map(transformNotification),
|
||||
}));
|
||||
@@ -57,8 +60,9 @@ const makeHandleNotificationUpdate = makeHandleNotificationCreate;
|
||||
|
||||
export default {
|
||||
getNotifications,
|
||||
getNotification,
|
||||
updateNotifications,
|
||||
// getNotification,
|
||||
updateNotification,
|
||||
readAllNotifications,
|
||||
makeHandleNotificationCreate,
|
||||
makeHandleNotificationUpdate,
|
||||
};
|
||||
|
||||
@@ -1,40 +1,19 @@
|
||||
/*!
|
||||
* Copyright (c) 2024 PLANKA Software GmbH
|
||||
* Licensed under the Fair Use License: https://github.com/plankanban/planka/blob/master/LICENSE.md
|
||||
*/
|
||||
|
||||
import socket from './socket';
|
||||
|
||||
/* Transformers */
|
||||
|
||||
export const transformProjectManager = (projectManager) => ({
|
||||
...projectManager,
|
||||
createdAt: new Date(projectManager.createdAt),
|
||||
});
|
||||
|
||||
/* Actions */
|
||||
|
||||
const createProjectManager = (projectId, data, headers) =>
|
||||
socket.post(`/projects/${projectId}/managers`, data, headers).then((body) => ({
|
||||
...body,
|
||||
item: transformProjectManager(body.item),
|
||||
}));
|
||||
socket.post(`/projects/${projectId}/project-managers`, data, headers);
|
||||
|
||||
const deleteProjectManager = (id, headers) =>
|
||||
socket.delete(`/project-managers/${id}`, undefined, headers).then((body) => ({
|
||||
...body,
|
||||
item: transformProjectManager(body.item),
|
||||
}));
|
||||
|
||||
/* Event handlers */
|
||||
|
||||
const makeHandleProjectManagerCreate = (next) => (body) => {
|
||||
next({
|
||||
...body,
|
||||
item: transformProjectManager(body.item),
|
||||
});
|
||||
};
|
||||
|
||||
const makeHandleProjectManagerDelete = makeHandleProjectManagerCreate;
|
||||
socket.delete(`/project-managers/${id}`, undefined, headers);
|
||||
|
||||
export default {
|
||||
createProjectManager,
|
||||
deleteProjectManager,
|
||||
makeHandleProjectManagerCreate,
|
||||
makeHandleProjectManagerDelete,
|
||||
};
|
||||
|
||||
@@ -1,47 +1,20 @@
|
||||
import http from './http';
|
||||
/*!
|
||||
* Copyright (c) 2024 PLANKA Software GmbH
|
||||
* Licensed under the Fair Use License: https://github.com/plankanban/planka/blob/master/LICENSE.md
|
||||
*/
|
||||
|
||||
import socket from './socket';
|
||||
import { transformUser } from './users';
|
||||
import { transformProjectManager } from './project-managers';
|
||||
import { transformBoardMembership } from './board-memberships';
|
||||
|
||||
/* Actions */
|
||||
|
||||
const getProjects = (headers) =>
|
||||
socket.get('/projects', undefined, headers).then((body) => ({
|
||||
...body,
|
||||
included: {
|
||||
...body.included,
|
||||
users: body.included.users.map(transformUser),
|
||||
projectManagers: body.included.projectManagers.map(transformProjectManager),
|
||||
boardMemberships: body.included.boardMemberships.map(transformBoardMembership),
|
||||
},
|
||||
}));
|
||||
const getProjects = (headers) => socket.get('/projects', undefined, headers);
|
||||
|
||||
const createProject = (data, headers) =>
|
||||
socket.post('/projects', data, headers).then((body) => ({
|
||||
...body,
|
||||
included: {
|
||||
...body.included,
|
||||
projectManagers: body.included.projectManagers.map(transformProjectManager),
|
||||
},
|
||||
}));
|
||||
const createProject = (data, headers) => socket.post('/projects', data, headers);
|
||||
|
||||
const getProject = (id, headers) =>
|
||||
socket.get(`/projects/${id}`, undefined, headers).then((body) => ({
|
||||
...body,
|
||||
included: {
|
||||
...body.included,
|
||||
users: body.included.users.map(transformUser),
|
||||
projectManagers: body.included.projectManagers.map(transformProjectManager),
|
||||
boardMemberships: body.included.boardMemberships.map(transformBoardMembership),
|
||||
},
|
||||
}));
|
||||
const getProject = (id, headers) => socket.get(`/projects/${id}`, undefined, headers);
|
||||
|
||||
const updateProject = (id, data, headers) => socket.patch(`/projects/${id}`, data, headers);
|
||||
|
||||
const updateProjectBackgroundImage = (id, data, headers) =>
|
||||
http.post(`/projects/${id}/background-image`, data, headers);
|
||||
|
||||
const deleteProject = (id, headers) => socket.delete(`/projects/${id}`, undefined, headers);
|
||||
|
||||
export default {
|
||||
@@ -49,6 +22,5 @@ export default {
|
||||
createProject,
|
||||
getProject,
|
||||
updateProject,
|
||||
updateProjectBackgroundImage,
|
||||
deleteProject,
|
||||
};
|
||||
|
||||
@@ -1,9 +0,0 @@
|
||||
import http from './http';
|
||||
|
||||
/* Actions */
|
||||
|
||||
const getConfig = (headers) => http.get('/config', undefined, headers);
|
||||
|
||||
export default {
|
||||
getConfig,
|
||||
};
|
||||
@@ -1,3 +1,8 @@
|
||||
/*!
|
||||
* Copyright (c) 2024 PLANKA Software GmbH
|
||||
* Licensed under the Fair Use License: https://github.com/plankanban/planka/blob/master/LICENSE.md
|
||||
*/
|
||||
|
||||
import socketIOClient from 'socket.io-client';
|
||||
import sailsIOClient from 'sails.io.js';
|
||||
|
||||
@@ -5,15 +10,14 @@ import Config from '../constants/Config';
|
||||
|
||||
const io = sailsIOClient(socketIOClient);
|
||||
|
||||
io.sails.url = Config.SERVER_HOST_NAME;
|
||||
io.sails.url = Config.SERVER_BASE_URL;
|
||||
io.sails.autoConnect = false;
|
||||
io.sails.reconnection = true;
|
||||
io.sails.useCORSRouteToGetCookie = false;
|
||||
io.sails.environment = process.env.NODE_ENV;
|
||||
io.sails.environment = import.meta.env.MODE;
|
||||
|
||||
const { socket } = io;
|
||||
|
||||
socket.path = `${Config.SERVER_BASE_PATH}/socket.io`;
|
||||
socket.connect = socket._connect; // eslint-disable-line no-underscore-dangle
|
||||
|
||||
['GET', 'POST', 'PUT', 'PATCH', 'DELETE'].forEach((method) => {
|
||||
|
||||
@@ -0,0 +1,24 @@
|
||||
/*!
|
||||
* Copyright (c) 2024 PLANKA Software GmbH
|
||||
* Licensed under the Fair Use License: https://github.com/plankanban/planka/blob/master/LICENSE.md
|
||||
*/
|
||||
|
||||
import socket from './socket';
|
||||
|
||||
/* Actions */
|
||||
|
||||
const createTaskList = (cardId, data, headers) =>
|
||||
socket.post(`/cards/${cardId}/task-lists`, data, headers);
|
||||
|
||||
const getTaskList = (id, headers) => socket.get(`/task-lists/${id}`, undefined, headers);
|
||||
|
||||
const updateTaskList = (id, data, headers) => socket.patch(`/task-lists/${id}`, data, headers);
|
||||
|
||||
const deleteTaskList = (id, headers) => socket.delete(`/task-lists/${id}`, undefined, headers);
|
||||
|
||||
export default {
|
||||
createTaskList,
|
||||
getTaskList,
|
||||
updateTaskList,
|
||||
deleteTaskList,
|
||||
};
|
||||
@@ -1,8 +1,14 @@
|
||||
/*!
|
||||
* Copyright (c) 2024 PLANKA Software GmbH
|
||||
* Licensed under the Fair Use License: https://github.com/plankanban/planka/blob/master/LICENSE.md
|
||||
*/
|
||||
|
||||
import socket from './socket';
|
||||
|
||||
/* Actions */
|
||||
|
||||
const createTask = (cardId, data, headers) => socket.post(`/cards/${cardId}/tasks`, data, headers);
|
||||
const createTask = (taskListId, data, headers) =>
|
||||
socket.post(`/task-lists/${taskListId}/tasks`, data, headers);
|
||||
|
||||
const updateTask = (id, data, headers) => socket.patch(`/tasks/${id}`, data, headers);
|
||||
|
||||
|
||||
+17
-68
@@ -1,92 +1,44 @@
|
||||
/*!
|
||||
* Copyright (c) 2024 PLANKA Software GmbH
|
||||
* Licensed under the Fair Use License: https://github.com/plankanban/planka/blob/master/LICENSE.md
|
||||
*/
|
||||
|
||||
import http from './http';
|
||||
import socket from './socket';
|
||||
|
||||
/* Transformers */
|
||||
|
||||
export const transformUser = (user) => ({
|
||||
...user,
|
||||
createdAt: new Date(user.createdAt),
|
||||
});
|
||||
|
||||
/* Actions */
|
||||
|
||||
const getUsers = (headers) =>
|
||||
socket.get('/users', undefined, headers).then((body) => ({
|
||||
...body,
|
||||
items: body.items.map(transformUser),
|
||||
}));
|
||||
const getUsers = (headers) => socket.get('/users', undefined, headers);
|
||||
|
||||
const createUser = (data, headers) =>
|
||||
socket.post('/users', data, headers).then((body) => ({
|
||||
...body,
|
||||
item: transformUser(body.item),
|
||||
}));
|
||||
const createUser = (data, headers) => socket.post('/users', data, headers);
|
||||
|
||||
const getUser = (id, headers) =>
|
||||
/* const getUser = (id, headers) =>
|
||||
socket.get(`/users/${id}`, undefined, headers).then((body) => ({
|
||||
...body,
|
||||
item: transformUser(body.item),
|
||||
}));
|
||||
})); */
|
||||
|
||||
const getCurrentUser = (subscribe, headers) =>
|
||||
socket.get(`/users/me${subscribe ? '?subscribe=true' : ''}`, undefined, headers).then((body) => ({
|
||||
...body,
|
||||
item: transformUser(body.item),
|
||||
}));
|
||||
socket.get(`/users/me${subscribe ? '?subscribe=true' : ''}`, undefined, headers);
|
||||
|
||||
const updateUser = (id, data, headers) =>
|
||||
socket.patch(`/users/${id}`, data, headers).then((body) => ({
|
||||
...body,
|
||||
item: transformUser(body.item),
|
||||
}));
|
||||
const updateUser = (id, data, headers) => socket.patch(`/users/${id}`, data, headers);
|
||||
|
||||
const updateUserEmail = (id, data, headers) =>
|
||||
socket.patch(`/users/${id}/email`, data, headers).then((body) => ({
|
||||
...body,
|
||||
item: transformUser(body.item),
|
||||
}));
|
||||
const updateUserEmail = (id, data, headers) => socket.patch(`/users/${id}/email`, data, headers);
|
||||
|
||||
const updateUserPassword = (id, data, headers) =>
|
||||
socket.patch(`/users/${id}/password`, data, headers).then((body) => ({
|
||||
...body,
|
||||
item: transformUser(body.item),
|
||||
}));
|
||||
socket.patch(`/users/${id}/password`, data, headers);
|
||||
|
||||
const updateUserUsername = (id, data, headers) =>
|
||||
socket.patch(`/users/${id}/username`, data, headers).then((body) => ({
|
||||
...body,
|
||||
item: transformUser(body.item),
|
||||
}));
|
||||
socket.patch(`/users/${id}/username`, data, headers);
|
||||
|
||||
const updateUserAvatar = (id, data, headers) =>
|
||||
http.post(`/users/${id}/avatar`, data, headers).then((body) => ({
|
||||
...body,
|
||||
item: transformUser(body.item),
|
||||
}));
|
||||
const updateUserAvatar = (id, data, headers) => http.post(`/users/${id}/avatar`, data, headers);
|
||||
|
||||
const deleteUser = (id, headers) =>
|
||||
socket.delete(`/users/${id}`, undefined, headers).then((body) => ({
|
||||
...body,
|
||||
item: transformUser(body.item),
|
||||
}));
|
||||
|
||||
/* Event handlers */
|
||||
|
||||
const makeHandleUserCreate = (next) => (body) => {
|
||||
next({
|
||||
...body,
|
||||
item: transformUser(body.item),
|
||||
});
|
||||
};
|
||||
|
||||
const makeHandleUserUpdate = makeHandleUserCreate;
|
||||
|
||||
const makeHandleUserDelete = makeHandleUserCreate;
|
||||
const deleteUser = (id, headers) => socket.delete(`/users/${id}`, undefined, headers);
|
||||
|
||||
export default {
|
||||
getUsers,
|
||||
createUser,
|
||||
getUser,
|
||||
// getUser,
|
||||
getCurrentUser,
|
||||
updateUser,
|
||||
updateUserEmail,
|
||||
@@ -94,7 +46,4 @@ export default {
|
||||
updateUserUsername,
|
||||
updateUserAvatar,
|
||||
deleteUser,
|
||||
makeHandleUserCreate,
|
||||
makeHandleUserUpdate,
|
||||
makeHandleUserDelete,
|
||||
};
|
||||
|
||||
-2337
File diff suppressed because it is too large
Load Diff
-4
File diff suppressed because one or more lines are too long
Binary file not shown.
Binary file not shown.
File diff suppressed because it is too large
Load Diff
|
Before Width: | Height: | Size: 434 KiB |
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
|
Before Width: | Height: | Size: 559 KiB After Width: | Height: | Size: 157 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 1.9 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 22 KiB After Width: | Height: | Size: 25 KiB |
@@ -1,38 +0,0 @@
|
||||
import upperFirst from 'lodash/upperFirst';
|
||||
import camelCase from 'lodash/camelCase';
|
||||
import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import classNames from 'classnames';
|
||||
|
||||
import { ProjectBackgroundTypes } from '../../constants/Enums';
|
||||
|
||||
import styles from './Background.module.scss';
|
||||
import globalStyles from '../../styles.module.scss';
|
||||
|
||||
function Background({ type, name, imageUrl }) {
|
||||
return (
|
||||
<div
|
||||
className={classNames(
|
||||
styles.wrapper,
|
||||
type === ProjectBackgroundTypes.GRADIENT &&
|
||||
globalStyles[`background${upperFirst(camelCase(name))}`],
|
||||
)}
|
||||
style={{
|
||||
background: type === 'image' && `url("${imageUrl}") center / cover`,
|
||||
}}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
Background.propTypes = {
|
||||
type: PropTypes.string.isRequired,
|
||||
name: PropTypes.string,
|
||||
imageUrl: PropTypes.string,
|
||||
};
|
||||
|
||||
Background.defaultProps = {
|
||||
name: undefined,
|
||||
imageUrl: undefined,
|
||||
};
|
||||
|
||||
export default Background;
|
||||
@@ -1,10 +0,0 @@
|
||||
:global(#app) {
|
||||
.wrapper {
|
||||
height: 100%;
|
||||
max-height: 100vh;
|
||||
max-width: 100vw;
|
||||
position: fixed;
|
||||
width: 100%;
|
||||
z-index: -1;
|
||||
}
|
||||
}
|
||||
@@ -1,3 +0,0 @@
|
||||
import Background from './Background';
|
||||
|
||||
export default Background;
|
||||
@@ -1,201 +0,0 @@
|
||||
import React, { useCallback, useEffect, useRef, useState } from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { DragDropContext, Droppable } from 'react-beautiful-dnd';
|
||||
import { closePopup } from '../../lib/popup';
|
||||
|
||||
import DroppableTypes from '../../constants/DroppableTypes';
|
||||
import ListContainer from '../../containers/ListContainer';
|
||||
import CardModalContainer from '../../containers/CardModalContainer';
|
||||
import ListAdd from './ListAdd';
|
||||
import { ReactComponent as PlusMathIcon } from '../../assets/images/plus-math-icon.svg';
|
||||
|
||||
import styles from './Board.module.scss';
|
||||
import globalStyles from '../../styles.module.scss';
|
||||
|
||||
const parseDndId = (dndId) => dndId.split(':')[1];
|
||||
|
||||
const Board = React.memo(
|
||||
({ listIds, isCardModalOpened, canEdit, onListCreate, onListMove, onCardMove }) => {
|
||||
const [t] = useTranslation();
|
||||
const [isListAddOpened, setIsListAddOpened] = useState(false);
|
||||
|
||||
const wrapper = useRef(null);
|
||||
const prevPosition = useRef(null);
|
||||
|
||||
const handleAddListClick = useCallback(() => {
|
||||
setIsListAddOpened(true);
|
||||
}, []);
|
||||
|
||||
const handleAddListClose = useCallback(() => {
|
||||
setIsListAddOpened(false);
|
||||
}, []);
|
||||
|
||||
const handleDragStart = useCallback(() => {
|
||||
document.body.classList.add(globalStyles.dragging);
|
||||
closePopup();
|
||||
}, []);
|
||||
|
||||
const handleDragEnd = useCallback(
|
||||
({ draggableId, type, source, destination }) => {
|
||||
document.body.classList.remove(globalStyles.dragging);
|
||||
|
||||
if (
|
||||
!destination ||
|
||||
(source.droppableId === destination.droppableId && source.index === destination.index)
|
||||
) {
|
||||
return;
|
||||
}
|
||||
|
||||
const id = parseDndId(draggableId);
|
||||
|
||||
switch (type) {
|
||||
case DroppableTypes.LIST:
|
||||
onListMove(id, destination.index);
|
||||
|
||||
break;
|
||||
case DroppableTypes.CARD:
|
||||
onCardMove(id, parseDndId(destination.droppableId), destination.index);
|
||||
|
||||
break;
|
||||
default:
|
||||
}
|
||||
},
|
||||
[onListMove, onCardMove],
|
||||
);
|
||||
|
||||
const handleMouseDown = useCallback(
|
||||
(event) => {
|
||||
// If button is defined and not equal to 0 (left click)
|
||||
if (event.button) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (event.target !== wrapper.current && !event.target.dataset.dragScroller) {
|
||||
return;
|
||||
}
|
||||
|
||||
prevPosition.current = event.clientX;
|
||||
|
||||
window.getSelection().removeAllRanges();
|
||||
document.body.classList.add(globalStyles.dragScrolling);
|
||||
},
|
||||
[wrapper],
|
||||
);
|
||||
|
||||
const handleWindowMouseMove = useCallback(
|
||||
(event) => {
|
||||
if (prevPosition.current === null) {
|
||||
return;
|
||||
}
|
||||
|
||||
event.preventDefault();
|
||||
|
||||
window.scrollBy({
|
||||
left: prevPosition.current - event.clientX,
|
||||
});
|
||||
|
||||
prevPosition.current = event.clientX;
|
||||
},
|
||||
[prevPosition],
|
||||
);
|
||||
|
||||
const handleWindowMouseRelease = useCallback(() => {
|
||||
if (prevPosition.current === null) {
|
||||
return;
|
||||
}
|
||||
|
||||
prevPosition.current = null;
|
||||
document.body.classList.remove(globalStyles.dragScrolling);
|
||||
}, [prevPosition]);
|
||||
|
||||
useEffect(() => {
|
||||
document.body.style.overflowX = 'auto';
|
||||
|
||||
return () => {
|
||||
document.body.style.overflowX = null;
|
||||
};
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
if (isListAddOpened) {
|
||||
window.scroll(document.body.scrollWidth, 0);
|
||||
}
|
||||
}, [listIds, isListAddOpened]);
|
||||
|
||||
useEffect(() => {
|
||||
window.addEventListener('mousemove', handleWindowMouseMove);
|
||||
|
||||
window.addEventListener('mouseup', handleWindowMouseRelease);
|
||||
window.addEventListener('blur', handleWindowMouseRelease);
|
||||
window.addEventListener('contextmenu', handleWindowMouseRelease);
|
||||
|
||||
return () => {
|
||||
window.removeEventListener('mousemove', handleWindowMouseMove);
|
||||
|
||||
window.removeEventListener('mouseup', handleWindowMouseRelease);
|
||||
window.removeEventListener('blur', handleWindowMouseRelease);
|
||||
window.removeEventListener('contextmenu', handleWindowMouseRelease);
|
||||
};
|
||||
}, [handleWindowMouseMove, handleWindowMouseRelease]);
|
||||
|
||||
return (
|
||||
<>
|
||||
{/* eslint-disable-next-line jsx-a11y/no-static-element-interactions */}
|
||||
<div ref={wrapper} className={styles.wrapper} onMouseDown={handleMouseDown}>
|
||||
<div>
|
||||
<DragDropContext onDragStart={handleDragStart} onDragEnd={handleDragEnd}>
|
||||
<Droppable droppableId="board" type={DroppableTypes.LIST} direction="horizontal">
|
||||
{({ innerRef, droppableProps, placeholder }) => (
|
||||
<div
|
||||
{...droppableProps} // eslint-disable-line react/jsx-props-no-spreading
|
||||
data-drag-scroller
|
||||
ref={innerRef}
|
||||
className={styles.lists}
|
||||
>
|
||||
{listIds.map((listId, index) => (
|
||||
<ListContainer key={listId} id={listId} index={index} />
|
||||
))}
|
||||
{placeholder}
|
||||
{canEdit && (
|
||||
<div data-drag-scroller className={styles.list}>
|
||||
{isListAddOpened ? (
|
||||
<ListAdd onCreate={onListCreate} onClose={handleAddListClose} />
|
||||
) : (
|
||||
<button
|
||||
type="button"
|
||||
className={styles.addListButton}
|
||||
onClick={handleAddListClick}
|
||||
>
|
||||
<PlusMathIcon className={styles.addListButtonIcon} />
|
||||
<span className={styles.addListButtonText}>
|
||||
{listIds.length > 0
|
||||
? t('action.addAnotherList')
|
||||
: t('action.addList')}
|
||||
</span>
|
||||
</button>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
</Droppable>
|
||||
</DragDropContext>
|
||||
</div>
|
||||
</div>
|
||||
{isCardModalOpened && <CardModalContainer />}
|
||||
</>
|
||||
);
|
||||
},
|
||||
);
|
||||
|
||||
Board.propTypes = {
|
||||
listIds: PropTypes.array.isRequired, // eslint-disable-line react/forbid-prop-types
|
||||
isCardModalOpened: PropTypes.bool.isRequired,
|
||||
canEdit: PropTypes.bool.isRequired,
|
||||
onListCreate: PropTypes.func.isRequired,
|
||||
onListMove: PropTypes.func.isRequired,
|
||||
onCardMove: PropTypes.func.isRequired,
|
||||
};
|
||||
|
||||
export default Board;
|
||||
@@ -1,89 +0,0 @@
|
||||
import React, { useCallback, useEffect, useRef } from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { Button, Form, Input } from 'semantic-ui-react';
|
||||
import { useDidUpdate, useToggle } from '../../lib/hooks';
|
||||
|
||||
import { useClosableForm, useForm } from '../../hooks';
|
||||
|
||||
import styles from './ListAdd.module.scss';
|
||||
|
||||
const DEFAULT_DATA = {
|
||||
name: '',
|
||||
};
|
||||
|
||||
const ListAdd = React.memo(({ onCreate, onClose }) => {
|
||||
const [t] = useTranslation();
|
||||
const [data, handleFieldChange, setData] = useForm(DEFAULT_DATA);
|
||||
const [focusNameFieldState, focusNameField] = useToggle();
|
||||
|
||||
const nameField = useRef(null);
|
||||
|
||||
const handleFieldKeyDown = useCallback(
|
||||
(event) => {
|
||||
if (event.key === 'Escape') {
|
||||
onClose();
|
||||
}
|
||||
},
|
||||
[onClose],
|
||||
);
|
||||
|
||||
const [handleFieldBlur, handleControlMouseOver, handleControlMouseOut] = useClosableForm(onClose);
|
||||
|
||||
const handleSubmit = useCallback(() => {
|
||||
const cleanData = {
|
||||
...data,
|
||||
name: data.name.trim(),
|
||||
};
|
||||
|
||||
if (!cleanData.name) {
|
||||
nameField.current.select();
|
||||
return;
|
||||
}
|
||||
|
||||
onCreate(cleanData);
|
||||
|
||||
setData(DEFAULT_DATA);
|
||||
focusNameField();
|
||||
}, [onCreate, data, setData, focusNameField]);
|
||||
|
||||
useEffect(() => {
|
||||
nameField.current.focus();
|
||||
}, []);
|
||||
|
||||
useDidUpdate(() => {
|
||||
nameField.current.focus();
|
||||
}, [focusNameFieldState]);
|
||||
|
||||
return (
|
||||
<Form className={styles.wrapper} onSubmit={handleSubmit}>
|
||||
<Input
|
||||
ref={nameField}
|
||||
name="name"
|
||||
value={data.name}
|
||||
placeholder={t('common.enterListTitle')}
|
||||
className={styles.field}
|
||||
onKeyDown={handleFieldKeyDown}
|
||||
onChange={handleFieldChange}
|
||||
onBlur={handleFieldBlur}
|
||||
/>
|
||||
<div className={styles.controls}>
|
||||
{/* eslint-disable-next-line jsx-a11y/mouse-events-have-key-events */}
|
||||
<Button
|
||||
positive
|
||||
content={t('action.addList')}
|
||||
className={styles.button}
|
||||
onMouseOver={handleControlMouseOver}
|
||||
onMouseOut={handleControlMouseOut}
|
||||
/>
|
||||
</div>
|
||||
</Form>
|
||||
);
|
||||
});
|
||||
|
||||
ListAdd.propTypes = {
|
||||
onCreate: PropTypes.func.isRequired,
|
||||
onClose: PropTypes.func.isRequired,
|
||||
};
|
||||
|
||||
export default ListAdd;
|
||||
@@ -1,33 +0,0 @@
|
||||
:global(#app) {
|
||||
.button {
|
||||
min-height: 30px;
|
||||
vertical-align: top;
|
||||
}
|
||||
|
||||
.controls {
|
||||
margin-top: 4px;
|
||||
}
|
||||
|
||||
.field {
|
||||
border: none;
|
||||
border-radius: 3px;
|
||||
box-shadow: 0 1px 0 #ccc;
|
||||
color: #333;
|
||||
outline: none;
|
||||
overflow: hidden;
|
||||
width: 100%;
|
||||
|
||||
&:focus {
|
||||
border-color: #298fca;
|
||||
box-shadow: 0 0 2px #298fca;
|
||||
}
|
||||
}
|
||||
|
||||
.wrapper {
|
||||
background: #e2e4e6;
|
||||
border-radius: 3px;
|
||||
padding: 4px;
|
||||
transition: opacity 40ms ease-in;
|
||||
width: 272px;
|
||||
}
|
||||
}
|
||||
@@ -1,3 +0,0 @@
|
||||
import Board from './Board';
|
||||
|
||||
export default Board;
|
||||
@@ -1,97 +0,0 @@
|
||||
import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
|
||||
import Filters from './Filters';
|
||||
import Memberships from '../Memberships';
|
||||
import BoardMembershipPermissionsSelectStep from '../BoardMembershipPermissionsSelectStep';
|
||||
|
||||
import styles from './BoardActions.module.scss';
|
||||
|
||||
const BoardActions = React.memo(
|
||||
({
|
||||
memberships,
|
||||
labels,
|
||||
filterUsers,
|
||||
filterLabels,
|
||||
filterText,
|
||||
allUsers,
|
||||
canEdit,
|
||||
canEditMemberships,
|
||||
onMembershipCreate,
|
||||
onMembershipUpdate,
|
||||
onMembershipDelete,
|
||||
onUserToFilterAdd,
|
||||
onUserFromFilterRemove,
|
||||
onLabelToFilterAdd,
|
||||
onLabelFromFilterRemove,
|
||||
onLabelCreate,
|
||||
onLabelUpdate,
|
||||
onLabelMove,
|
||||
onLabelDelete,
|
||||
onTextFilterUpdate,
|
||||
}) => {
|
||||
return (
|
||||
<div className={styles.wrapper}>
|
||||
<div className={styles.actions}>
|
||||
<div className={styles.action}>
|
||||
<Memberships
|
||||
items={memberships}
|
||||
allUsers={allUsers}
|
||||
permissionsSelectStep={BoardMembershipPermissionsSelectStep}
|
||||
canEdit={canEditMemberships}
|
||||
onCreate={onMembershipCreate}
|
||||
onUpdate={onMembershipUpdate}
|
||||
onDelete={onMembershipDelete}
|
||||
/>
|
||||
</div>
|
||||
<div className={styles.action}>
|
||||
<Filters
|
||||
users={filterUsers}
|
||||
labels={filterLabels}
|
||||
filterText={filterText}
|
||||
allBoardMemberships={memberships}
|
||||
allLabels={labels}
|
||||
canEdit={canEdit}
|
||||
onUserAdd={onUserToFilterAdd}
|
||||
onUserRemove={onUserFromFilterRemove}
|
||||
onLabelAdd={onLabelToFilterAdd}
|
||||
onLabelRemove={onLabelFromFilterRemove}
|
||||
onLabelCreate={onLabelCreate}
|
||||
onLabelUpdate={onLabelUpdate}
|
||||
onLabelMove={onLabelMove}
|
||||
onLabelDelete={onLabelDelete}
|
||||
onTextFilterUpdate={onTextFilterUpdate}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
},
|
||||
);
|
||||
|
||||
BoardActions.propTypes = {
|
||||
/* eslint-disable react/forbid-prop-types */
|
||||
memberships: PropTypes.array.isRequired,
|
||||
labels: PropTypes.array.isRequired,
|
||||
filterUsers: PropTypes.array.isRequired,
|
||||
filterLabels: PropTypes.array.isRequired,
|
||||
filterText: PropTypes.string.isRequired,
|
||||
allUsers: PropTypes.array.isRequired,
|
||||
/* eslint-enable react/forbid-prop-types */
|
||||
canEdit: PropTypes.bool.isRequired,
|
||||
canEditMemberships: PropTypes.bool.isRequired,
|
||||
onMembershipCreate: PropTypes.func.isRequired,
|
||||
onMembershipUpdate: PropTypes.func.isRequired,
|
||||
onMembershipDelete: PropTypes.func.isRequired,
|
||||
onUserToFilterAdd: PropTypes.func.isRequired,
|
||||
onUserFromFilterRemove: PropTypes.func.isRequired,
|
||||
onLabelToFilterAdd: PropTypes.func.isRequired,
|
||||
onLabelFromFilterRemove: PropTypes.func.isRequired,
|
||||
onLabelCreate: PropTypes.func.isRequired,
|
||||
onLabelUpdate: PropTypes.func.isRequired,
|
||||
onLabelMove: PropTypes.func.isRequired,
|
||||
onLabelDelete: PropTypes.func.isRequired,
|
||||
onTextFilterUpdate: PropTypes.func.isRequired,
|
||||
};
|
||||
|
||||
export default BoardActions;
|
||||
@@ -1,26 +0,0 @@
|
||||
:global(#app) {
|
||||
.action {
|
||||
align-items: center;
|
||||
display: flex;
|
||||
flex: 0 0 auto;
|
||||
}
|
||||
|
||||
.actions {
|
||||
align-items: center;
|
||||
display: flex;
|
||||
gap: 20px;
|
||||
justify-content: flex-start;
|
||||
margin: 20px 20px;
|
||||
}
|
||||
|
||||
.wrapper {
|
||||
overflow-x: auto;
|
||||
overflow-y: hidden;
|
||||
-ms-overflow-style: none;
|
||||
scrollbar-width: none;
|
||||
|
||||
&::-webkit-scrollbar {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,190 +0,0 @@
|
||||
import React, { useCallback, useRef, useState } from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import classNames from 'classnames';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { Icon } from 'semantic-ui-react';
|
||||
import { usePopup } from '../../lib/popup';
|
||||
import { Input } from '../../lib/custom-ui';
|
||||
|
||||
import User from '../User';
|
||||
import Label from '../Label';
|
||||
import BoardMembershipsStep from '../BoardMembershipsStep';
|
||||
import LabelsStep from '../LabelsStep';
|
||||
|
||||
import styles from './Filters.module.scss';
|
||||
|
||||
const Filters = React.memo(
|
||||
({
|
||||
users,
|
||||
labels,
|
||||
filterText,
|
||||
allBoardMemberships,
|
||||
allLabels,
|
||||
canEdit,
|
||||
onUserAdd,
|
||||
onUserRemove,
|
||||
onLabelAdd,
|
||||
onLabelRemove,
|
||||
onLabelCreate,
|
||||
onLabelUpdate,
|
||||
onLabelMove,
|
||||
onLabelDelete,
|
||||
onTextFilterUpdate,
|
||||
}) => {
|
||||
const [t] = useTranslation();
|
||||
const [isSearchFocused, setIsSearchFocused] = useState(false);
|
||||
|
||||
const searchFieldRef = useRef(null);
|
||||
|
||||
const cancelSearch = useCallback(() => {
|
||||
onTextFilterUpdate('');
|
||||
searchFieldRef.current.blur();
|
||||
}, [onTextFilterUpdate]);
|
||||
|
||||
const handleRemoveUserClick = useCallback(
|
||||
(id) => {
|
||||
onUserRemove(id);
|
||||
},
|
||||
[onUserRemove],
|
||||
);
|
||||
|
||||
const handleRemoveLabelClick = useCallback(
|
||||
(id) => {
|
||||
onLabelRemove(id);
|
||||
},
|
||||
[onLabelRemove],
|
||||
);
|
||||
|
||||
const handleSearchChange = useCallback(
|
||||
(_, { value }) => {
|
||||
onTextFilterUpdate(value);
|
||||
},
|
||||
[onTextFilterUpdate],
|
||||
);
|
||||
|
||||
const handleSearchFocus = useCallback(() => {
|
||||
setIsSearchFocused(true);
|
||||
}, []);
|
||||
|
||||
const handleSearchKeyDown = useCallback(
|
||||
(event) => {
|
||||
if (event.key === 'Escape') {
|
||||
cancelSearch();
|
||||
}
|
||||
},
|
||||
[cancelSearch],
|
||||
);
|
||||
|
||||
const handleSearchBlur = useCallback(() => {
|
||||
setIsSearchFocused(false);
|
||||
}, []);
|
||||
|
||||
const handleCancelSearchClick = useCallback(() => {
|
||||
cancelSearch();
|
||||
}, [cancelSearch]);
|
||||
|
||||
const BoardMembershipsPopup = usePopup(BoardMembershipsStep);
|
||||
const LabelsPopup = usePopup(LabelsStep);
|
||||
|
||||
const isSearchActive = filterText || isSearchFocused;
|
||||
|
||||
return (
|
||||
<>
|
||||
<span className={styles.filter}>
|
||||
<BoardMembershipsPopup
|
||||
items={allBoardMemberships}
|
||||
currentUserIds={users.map((user) => user.id)}
|
||||
title="common.filterByMembers"
|
||||
onUserSelect={onUserAdd}
|
||||
onUserDeselect={onUserRemove}
|
||||
>
|
||||
<button type="button" className={styles.filterButton}>
|
||||
<span className={styles.filterTitle}>{`${t('common.members')}:`}</span>
|
||||
{users.length === 0 && <span className={styles.filterLabel}>{t('common.all')}</span>}
|
||||
</button>
|
||||
</BoardMembershipsPopup>
|
||||
{users.map((user) => (
|
||||
<span key={user.id} className={styles.filterItem}>
|
||||
<User
|
||||
name={user.name}
|
||||
avatarUrl={user.avatarUrl}
|
||||
size="tiny"
|
||||
onClick={() => handleRemoveUserClick(user.id)}
|
||||
/>
|
||||
</span>
|
||||
))}
|
||||
</span>
|
||||
<span className={styles.filter}>
|
||||
<LabelsPopup
|
||||
items={allLabels}
|
||||
currentIds={labels.map((label) => label.id)}
|
||||
title="common.filterByLabels"
|
||||
canEdit={canEdit}
|
||||
onSelect={onLabelAdd}
|
||||
onDeselect={onLabelRemove}
|
||||
onCreate={onLabelCreate}
|
||||
onUpdate={onLabelUpdate}
|
||||
onMove={onLabelMove}
|
||||
onDelete={onLabelDelete}
|
||||
>
|
||||
<button type="button" className={styles.filterButton}>
|
||||
<span className={styles.filterTitle}>{`${t('common.labels')}:`}</span>
|
||||
{labels.length === 0 && <span className={styles.filterLabel}>{t('common.all')}</span>}
|
||||
</button>
|
||||
</LabelsPopup>
|
||||
{labels.map((label) => (
|
||||
<span key={label.id} className={styles.filterItem}>
|
||||
<Label
|
||||
name={label.name}
|
||||
color={label.color}
|
||||
size="small"
|
||||
onClick={() => handleRemoveLabelClick(label.id)}
|
||||
/>
|
||||
</span>
|
||||
))}
|
||||
</span>
|
||||
<span className={styles.filter}>
|
||||
<Input
|
||||
ref={searchFieldRef}
|
||||
value={filterText}
|
||||
placeholder={t('common.searchCards')}
|
||||
icon={
|
||||
isSearchActive ? (
|
||||
<Icon link name="cancel" onClick={handleCancelSearchClick} />
|
||||
) : (
|
||||
'search'
|
||||
)
|
||||
}
|
||||
className={classNames(styles.search, !isSearchActive && styles.searchInactive)}
|
||||
onFocus={handleSearchFocus}
|
||||
onKeyDown={handleSearchKeyDown}
|
||||
onChange={handleSearchChange}
|
||||
onBlur={handleSearchBlur}
|
||||
/>
|
||||
</span>
|
||||
</>
|
||||
);
|
||||
},
|
||||
);
|
||||
|
||||
Filters.propTypes = {
|
||||
/* eslint-disable react/forbid-prop-types */
|
||||
users: PropTypes.array.isRequired,
|
||||
labels: PropTypes.array.isRequired,
|
||||
filterText: PropTypes.string.isRequired,
|
||||
allBoardMemberships: PropTypes.array.isRequired,
|
||||
allLabels: PropTypes.array.isRequired,
|
||||
/* eslint-enable react/forbid-prop-types */
|
||||
canEdit: PropTypes.bool.isRequired,
|
||||
onUserAdd: PropTypes.func.isRequired,
|
||||
onUserRemove: PropTypes.func.isRequired,
|
||||
onLabelAdd: PropTypes.func.isRequired,
|
||||
onLabelRemove: PropTypes.func.isRequired,
|
||||
onLabelCreate: PropTypes.func.isRequired,
|
||||
onLabelUpdate: PropTypes.func.isRequired,
|
||||
onLabelMove: PropTypes.func.isRequired,
|
||||
onLabelDelete: PropTypes.func.isRequired,
|
||||
onTextFilterUpdate: PropTypes.func.isRequired,
|
||||
};
|
||||
|
||||
export default Filters;
|
||||
@@ -1,3 +0,0 @@
|
||||
import BoardActions from './BoardActions';
|
||||
|
||||
export default BoardActions;
|
||||
-107
@@ -1,107 +0,0 @@
|
||||
import { dequal } from 'dequal';
|
||||
import omit from 'lodash/omit';
|
||||
import React, { useCallback, useState } from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { Button, Form, Menu, Radio, Segment } from 'semantic-ui-react';
|
||||
import { Popup } from '../../lib/custom-ui';
|
||||
|
||||
import { BoardMembershipRoles } from '../../constants/Enums';
|
||||
|
||||
import styles from './BoardMembershipPermissionsSelectStep.module.scss';
|
||||
|
||||
const BoardMembershipPermissionsSelectStep = React.memo(
|
||||
({ defaultData, title, buttonContent, onSelect, onBack, onClose }) => {
|
||||
const [t] = useTranslation();
|
||||
|
||||
const [data, setData] = useState(() => ({
|
||||
role: BoardMembershipRoles.EDITOR,
|
||||
canComment: null,
|
||||
...defaultData,
|
||||
}));
|
||||
|
||||
const handleSelectRoleClick = useCallback((role) => {
|
||||
setData((prevData) => ({
|
||||
...prevData,
|
||||
role,
|
||||
canComment: role === BoardMembershipRoles.VIEWER ? !!prevData.canComment : null,
|
||||
}));
|
||||
}, []);
|
||||
|
||||
const handleSettingChange = useCallback((_, { name: fieldName, checked: value }) => {
|
||||
setData((prevData) => ({
|
||||
...prevData,
|
||||
[fieldName]: value,
|
||||
}));
|
||||
}, []);
|
||||
|
||||
const handleSubmit = useCallback(() => {
|
||||
if (!dequal(data, defaultData)) {
|
||||
onSelect(data.role === BoardMembershipRoles.VIEWER ? data : omit(data, 'canComment'));
|
||||
}
|
||||
|
||||
onClose();
|
||||
}, [defaultData, onSelect, onClose, data]);
|
||||
|
||||
return (
|
||||
<>
|
||||
<Popup.Header onBack={onBack}>
|
||||
{t(title, {
|
||||
context: 'title',
|
||||
})}
|
||||
</Popup.Header>
|
||||
<Popup.Content>
|
||||
<Form onSubmit={handleSubmit}>
|
||||
<Menu secondary vertical className={styles.menu}>
|
||||
<Menu.Item
|
||||
active={data.role === BoardMembershipRoles.EDITOR}
|
||||
onClick={() => handleSelectRoleClick(BoardMembershipRoles.EDITOR)}
|
||||
>
|
||||
<div className={styles.menuItemTitle}>{t('common.editor')}</div>
|
||||
<div className={styles.menuItemDescription}>
|
||||
{t('common.canEditContentOfBoard')}
|
||||
</div>
|
||||
</Menu.Item>
|
||||
<Menu.Item
|
||||
active={data.role === BoardMembershipRoles.VIEWER}
|
||||
onClick={() => handleSelectRoleClick(BoardMembershipRoles.VIEWER)}
|
||||
>
|
||||
<div className={styles.menuItemTitle}>{t('common.viewer')}</div>
|
||||
<div className={styles.menuItemDescription}>{t('common.canOnlyViewBoard')}</div>
|
||||
</Menu.Item>
|
||||
</Menu>
|
||||
{data.role === BoardMembershipRoles.VIEWER && (
|
||||
<Segment basic className={styles.settings}>
|
||||
<Radio
|
||||
toggle
|
||||
name="canComment"
|
||||
checked={data.canComment}
|
||||
label={t('common.canComment')}
|
||||
onChange={handleSettingChange}
|
||||
/>
|
||||
</Segment>
|
||||
)}
|
||||
<Button positive content={t(buttonContent)} />
|
||||
</Form>
|
||||
</Popup.Content>
|
||||
</>
|
||||
);
|
||||
},
|
||||
);
|
||||
|
||||
BoardMembershipPermissionsSelectStep.propTypes = {
|
||||
defaultData: PropTypes.object, // eslint-disable-line react/forbid-prop-types
|
||||
title: PropTypes.string,
|
||||
buttonContent: PropTypes.string,
|
||||
onSelect: PropTypes.func.isRequired,
|
||||
onBack: PropTypes.func.isRequired,
|
||||
onClose: PropTypes.func.isRequired,
|
||||
};
|
||||
|
||||
BoardMembershipPermissionsSelectStep.defaultProps = {
|
||||
defaultData: undefined,
|
||||
title: 'common.selectPermissions',
|
||||
buttonContent: 'action.selectPermissions',
|
||||
};
|
||||
|
||||
export default BoardMembershipPermissionsSelectStep;
|
||||
-18
@@ -1,18 +0,0 @@
|
||||
:global(#app) {
|
||||
.menu {
|
||||
margin: 0 auto 8px;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.menuItemDescription {
|
||||
opacity: 0.5;
|
||||
}
|
||||
|
||||
.menuItemTitle {
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
|
||||
.settings {
|
||||
margin: 0 0 8px;
|
||||
}
|
||||
}
|
||||
@@ -1,3 +0,0 @@
|
||||
import BoardMembershipPermissionsSelectStep from './BoardMembershipPermissionsSelectStep';
|
||||
|
||||
export default BoardMembershipPermissionsSelectStep;
|
||||
@@ -1,103 +0,0 @@
|
||||
import React, { useCallback, useEffect, useMemo, useRef } from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { Menu } from 'semantic-ui-react';
|
||||
import { Input, Popup } from '../../lib/custom-ui';
|
||||
|
||||
import { useField } from '../../hooks';
|
||||
import Item from './Item';
|
||||
|
||||
import styles from './BoardMembershipsStep.module.scss';
|
||||
|
||||
const BoardMembershipsStep = React.memo(
|
||||
({ items, currentUserIds, title, onUserSelect, onUserDeselect, onBack }) => {
|
||||
const [t] = useTranslation();
|
||||
const [search, handleSearchChange] = useField('');
|
||||
const cleanSearch = useMemo(() => search.trim().toLowerCase(), [search]);
|
||||
|
||||
const filteredItems = useMemo(
|
||||
() =>
|
||||
items.filter(
|
||||
({ user }) =>
|
||||
user.email.includes(cleanSearch) ||
|
||||
user.name.toLowerCase().includes(cleanSearch) ||
|
||||
(user.username && user.username.includes(cleanSearch)),
|
||||
),
|
||||
[items, cleanSearch],
|
||||
);
|
||||
|
||||
const searchField = useRef(null);
|
||||
|
||||
const handleUserSelect = useCallback(
|
||||
(id) => {
|
||||
onUserSelect(id);
|
||||
},
|
||||
[onUserSelect],
|
||||
);
|
||||
|
||||
const handleUserDeselect = useCallback(
|
||||
(id) => {
|
||||
onUserDeselect(id);
|
||||
},
|
||||
[onUserDeselect],
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
searchField.current.focus({
|
||||
preventScroll: true,
|
||||
});
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<>
|
||||
<Popup.Header onBack={onBack}>
|
||||
{t(title, {
|
||||
context: 'title',
|
||||
})}
|
||||
</Popup.Header>
|
||||
<Popup.Content>
|
||||
<Input
|
||||
fluid
|
||||
ref={searchField}
|
||||
value={search}
|
||||
placeholder={t('common.searchMembers')}
|
||||
icon="search"
|
||||
onChange={handleSearchChange}
|
||||
/>
|
||||
{filteredItems.length > 0 && (
|
||||
<Menu secondary vertical className={styles.menu}>
|
||||
{filteredItems.map((item) => (
|
||||
<Item
|
||||
key={item.id}
|
||||
isPersisted={item.isPersisted}
|
||||
isActive={currentUserIds.includes(item.user.id)}
|
||||
user={item.user}
|
||||
onUserSelect={() => handleUserSelect(item.user.id)}
|
||||
onUserDeselect={() => handleUserDeselect(item.user.id)}
|
||||
/>
|
||||
))}
|
||||
</Menu>
|
||||
)}
|
||||
</Popup.Content>
|
||||
</>
|
||||
);
|
||||
},
|
||||
);
|
||||
|
||||
BoardMembershipsStep.propTypes = {
|
||||
/* eslint-disable react/forbid-prop-types */
|
||||
items: PropTypes.array.isRequired,
|
||||
currentUserIds: PropTypes.array.isRequired,
|
||||
/* eslint-enable react/forbid-prop-types */
|
||||
title: PropTypes.string,
|
||||
onUserSelect: PropTypes.func.isRequired,
|
||||
onUserDeselect: PropTypes.func.isRequired,
|
||||
onBack: PropTypes.func,
|
||||
};
|
||||
|
||||
BoardMembershipsStep.defaultProps = {
|
||||
title: 'common.members',
|
||||
onBack: undefined,
|
||||
};
|
||||
|
||||
export default BoardMembershipsStep;
|
||||
@@ -1,21 +0,0 @@
|
||||
:global(#app) {
|
||||
.menu {
|
||||
margin: 8px auto 0;
|
||||
max-height: 60vh;
|
||||
overflow-x: hidden;
|
||||
overflow-y: auto;
|
||||
width: 100%;
|
||||
|
||||
&::-webkit-scrollbar {
|
||||
width: 5px;
|
||||
}
|
||||
|
||||
&::-webkit-scrollbar-track {
|
||||
background: transparent;
|
||||
}
|
||||
|
||||
&::-webkit-scrollbar-thumb {
|
||||
border-radius: 3px;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,44 +0,0 @@
|
||||
import React, { useCallback } from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import classNames from 'classnames';
|
||||
import { Menu } from 'semantic-ui-react';
|
||||
|
||||
import User from '../User';
|
||||
|
||||
import styles from './Item.module.scss';
|
||||
|
||||
const Item = React.memo(({ isPersisted, isActive, user, onUserSelect, onUserDeselect }) => {
|
||||
const handleToggleClick = useCallback(() => {
|
||||
if (isActive) {
|
||||
onUserDeselect();
|
||||
} else {
|
||||
onUserSelect();
|
||||
}
|
||||
}, [isActive, onUserSelect, onUserDeselect]);
|
||||
|
||||
return (
|
||||
<Menu.Item
|
||||
active={isActive}
|
||||
disabled={!isPersisted}
|
||||
className={classNames(styles.menuItem, isActive && styles.menuItemActive)}
|
||||
onClick={handleToggleClick}
|
||||
>
|
||||
<span className={styles.user}>
|
||||
<User name={user.name} avatarUrl={user.avatarUrl} />
|
||||
</span>
|
||||
<div className={classNames(styles.menuItemText, isActive && styles.menuItemTextActive)}>
|
||||
{user.name}
|
||||
</div>
|
||||
</Menu.Item>
|
||||
);
|
||||
});
|
||||
|
||||
Item.propTypes = {
|
||||
isPersisted: PropTypes.bool.isRequired,
|
||||
isActive: PropTypes.bool.isRequired,
|
||||
user: PropTypes.object.isRequired, // eslint-disable-line react/forbid-prop-types
|
||||
onUserSelect: PropTypes.func.isRequired,
|
||||
onUserDeselect: PropTypes.func.isRequired,
|
||||
};
|
||||
|
||||
export default Item;
|
||||
@@ -1,3 +0,0 @@
|
||||
import BoardMembershipsStep from './BoardMembershipsStep';
|
||||
|
||||
export default BoardMembershipsStep;
|
||||
@@ -1,3 +0,0 @@
|
||||
import AddStep from './AddStep';
|
||||
|
||||
export default AddStep;
|
||||
@@ -1,145 +0,0 @@
|
||||
import pick from 'lodash/pick';
|
||||
import React, { useCallback, useRef } from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import classNames from 'classnames';
|
||||
import { Link } from 'react-router-dom';
|
||||
import { DragDropContext, Draggable, Droppable } from 'react-beautiful-dnd';
|
||||
import { Button, Icon } from 'semantic-ui-react';
|
||||
import { closePopup, usePopup } from '../../lib/popup';
|
||||
|
||||
import Paths from '../../constants/Paths';
|
||||
import DroppableTypes from '../../constants/DroppableTypes';
|
||||
import AddStep from './AddStep';
|
||||
import EditStep from './EditStep';
|
||||
|
||||
import styles from './Boards.module.scss';
|
||||
import globalStyles from '../../styles.module.scss';
|
||||
|
||||
const Boards = React.memo(({ items, currentId, canEdit, onCreate, onUpdate, onMove, onDelete }) => {
|
||||
const tabsWrapper = useRef(null);
|
||||
|
||||
const handleWheel = useCallback(({ deltaY }) => {
|
||||
tabsWrapper.current.scrollBy({
|
||||
left: deltaY,
|
||||
});
|
||||
}, []);
|
||||
|
||||
const handleDragStart = useCallback(() => {
|
||||
document.body.classList.add(globalStyles.dragging);
|
||||
closePopup();
|
||||
}, []);
|
||||
|
||||
const handleDragEnd = useCallback(
|
||||
({ draggableId, source, destination }) => {
|
||||
document.body.classList.remove(globalStyles.dragging);
|
||||
|
||||
if (!destination || source.index === destination.index) {
|
||||
return;
|
||||
}
|
||||
|
||||
onMove(draggableId, destination.index);
|
||||
},
|
||||
[onMove],
|
||||
);
|
||||
|
||||
const handleUpdate = useCallback(
|
||||
(id, data) => {
|
||||
onUpdate(id, data);
|
||||
},
|
||||
[onUpdate],
|
||||
);
|
||||
|
||||
const handleDelete = useCallback(
|
||||
(id) => {
|
||||
onDelete(id);
|
||||
},
|
||||
[onDelete],
|
||||
);
|
||||
|
||||
const AddPopup = usePopup(AddStep);
|
||||
const EditPopup = usePopup(EditStep);
|
||||
|
||||
const itemsNode = items.map((item, index) => (
|
||||
<Draggable
|
||||
key={item.id}
|
||||
draggableId={item.id}
|
||||
index={index}
|
||||
isDragDisabled={!item.isPersisted || !canEdit}
|
||||
>
|
||||
{({ innerRef, draggableProps, dragHandleProps }) => (
|
||||
// eslint-disable-next-line react/jsx-props-no-spreading
|
||||
<div {...draggableProps} ref={innerRef} className={styles.tabWrapper}>
|
||||
<div className={classNames(styles.tab, item.id === currentId && styles.tabActive)}>
|
||||
{item.isPersisted ? (
|
||||
<>
|
||||
<Link
|
||||
{...dragHandleProps} // eslint-disable-line react/jsx-props-no-spreading
|
||||
to={Paths.BOARDS.replace(':id', item.id)}
|
||||
title={item.name}
|
||||
className={styles.link}
|
||||
>
|
||||
{item.name}
|
||||
</Link>
|
||||
{canEdit && (
|
||||
<EditPopup
|
||||
defaultData={pick(item, 'name')}
|
||||
onUpdate={(data) => handleUpdate(item.id, data)}
|
||||
onDelete={() => handleDelete(item.id)}
|
||||
>
|
||||
<Button className={classNames(styles.editButton, styles.target)}>
|
||||
<Icon fitted name="pencil" size="small" />
|
||||
</Button>
|
||||
</EditPopup>
|
||||
)}
|
||||
</>
|
||||
) : (
|
||||
// eslint-disable-next-line react/jsx-props-no-spreading
|
||||
<span {...dragHandleProps} className={styles.link}>
|
||||
{item.name}
|
||||
</span>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</Draggable>
|
||||
));
|
||||
|
||||
return (
|
||||
<div className={styles.wrapper} onWheel={handleWheel}>
|
||||
<div ref={tabsWrapper} className={styles.tabsWrapper}>
|
||||
<DragDropContext onDragStart={handleDragStart} onDragEnd={handleDragEnd}>
|
||||
<Droppable droppableId="boards" type={DroppableTypes.BOARD} direction="horizontal">
|
||||
{({ innerRef, droppableProps, placeholder }) => (
|
||||
// eslint-disable-next-line react/jsx-props-no-spreading
|
||||
<div {...droppableProps} ref={innerRef} className={styles.tabs}>
|
||||
{itemsNode}
|
||||
{placeholder}
|
||||
{canEdit && (
|
||||
<AddPopup onCreate={onCreate}>
|
||||
<Button icon="plus" className={styles.addButton} />
|
||||
</AddPopup>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
</Droppable>
|
||||
</DragDropContext>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
});
|
||||
|
||||
Boards.propTypes = {
|
||||
items: PropTypes.array.isRequired, // eslint-disable-line react/forbid-prop-types
|
||||
currentId: PropTypes.string,
|
||||
canEdit: PropTypes.bool.isRequired,
|
||||
onCreate: PropTypes.func.isRequired,
|
||||
onUpdate: PropTypes.func.isRequired,
|
||||
onMove: PropTypes.func.isRequired,
|
||||
onDelete: PropTypes.func.isRequired,
|
||||
};
|
||||
|
||||
Boards.defaultProps = {
|
||||
currentId: undefined,
|
||||
};
|
||||
|
||||
export default Boards;
|
||||
@@ -1,104 +0,0 @@
|
||||
import { dequal } from 'dequal';
|
||||
import React, { useCallback, useEffect, useRef } from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { Button, Form } from 'semantic-ui-react';
|
||||
import { Input, Popup } from '../../lib/custom-ui';
|
||||
|
||||
import { useForm, useSteps } from '../../hooks';
|
||||
import DeleteStep from '../DeleteStep';
|
||||
|
||||
import styles from './EditStep.module.scss';
|
||||
|
||||
const StepTypes = {
|
||||
DELETE: 'DELETE',
|
||||
};
|
||||
|
||||
const EditStep = React.memo(({ defaultData, onUpdate, onDelete, onClose }) => {
|
||||
const [t] = useTranslation();
|
||||
|
||||
const [data, handleFieldChange] = useForm(() => ({
|
||||
name: '',
|
||||
...defaultData,
|
||||
}));
|
||||
|
||||
const [step, openStep, handleBack] = useSteps();
|
||||
|
||||
const nameField = useRef(null);
|
||||
|
||||
const handleSubmit = useCallback(() => {
|
||||
const cleanData = {
|
||||
...data,
|
||||
name: data.name.trim(),
|
||||
};
|
||||
|
||||
if (!cleanData.name) {
|
||||
nameField.current.select();
|
||||
return;
|
||||
}
|
||||
|
||||
if (!dequal(cleanData, defaultData)) {
|
||||
onUpdate(cleanData);
|
||||
}
|
||||
|
||||
onClose();
|
||||
}, [defaultData, onUpdate, onClose, data]);
|
||||
|
||||
const handleDeleteClick = useCallback(() => {
|
||||
openStep(StepTypes.DELETE);
|
||||
}, [openStep]);
|
||||
|
||||
useEffect(() => {
|
||||
nameField.current.select();
|
||||
}, []);
|
||||
|
||||
if (step && step.type === StepTypes.DELETE) {
|
||||
return (
|
||||
<DeleteStep
|
||||
title="common.deleteBoard"
|
||||
content="common.areYouSureYouWantToDeleteThisBoard"
|
||||
buttonContent="action.deleteBoard"
|
||||
onConfirm={onDelete}
|
||||
onBack={handleBack}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
<Popup.Header>
|
||||
{t('common.editBoard', {
|
||||
context: 'title',
|
||||
})}
|
||||
</Popup.Header>
|
||||
<Popup.Content>
|
||||
<Form onSubmit={handleSubmit}>
|
||||
<div className={styles.text}>{t('common.title')}</div>
|
||||
<Input
|
||||
fluid
|
||||
ref={nameField}
|
||||
name="name"
|
||||
value={data.name}
|
||||
className={styles.field}
|
||||
onChange={handleFieldChange}
|
||||
/>
|
||||
<Button positive content={t('action.save')} />
|
||||
</Form>
|
||||
<Button
|
||||
content={t('action.delete')}
|
||||
className={styles.deleteButton}
|
||||
onClick={handleDeleteClick}
|
||||
/>
|
||||
</Popup.Content>
|
||||
</>
|
||||
);
|
||||
});
|
||||
|
||||
EditStep.propTypes = {
|
||||
defaultData: PropTypes.object.isRequired, // eslint-disable-line react/forbid-prop-types
|
||||
onUpdate: PropTypes.func.isRequired,
|
||||
onDelete: PropTypes.func.isRequired,
|
||||
onClose: PropTypes.func.isRequired,
|
||||
};
|
||||
|
||||
export default EditStep;
|
||||
@@ -1,3 +0,0 @@
|
||||
import Boards from './Boards';
|
||||
|
||||
export default Boards;
|
||||
@@ -1,260 +0,0 @@
|
||||
import pick from 'lodash/pick';
|
||||
import React, { useCallback } from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { Menu } from 'semantic-ui-react';
|
||||
import { Popup } from '../../lib/custom-ui';
|
||||
|
||||
import { useSteps } from '../../hooks';
|
||||
import BoardMembershipsStep from '../BoardMembershipsStep';
|
||||
import LabelsStep from '../LabelsStep';
|
||||
import DueDateEditStep from '../DueDateEditStep';
|
||||
import StopwatchEditStep from '../StopwatchEditStep';
|
||||
import CardMoveStep from '../CardMoveStep';
|
||||
import DeleteStep from '../DeleteStep';
|
||||
|
||||
import styles from './ActionsStep.module.scss';
|
||||
|
||||
const StepTypes = {
|
||||
USERS: 'USERS',
|
||||
LABELS: 'LABELS',
|
||||
EDIT_DUE_DATE: 'EDIT_DUE_DATE',
|
||||
EDIT_STOPWATCH: 'EDIT_STOPWATCH',
|
||||
MOVE: 'MOVE',
|
||||
DELETE: 'DELETE',
|
||||
};
|
||||
|
||||
const ActionsStep = React.memo(
|
||||
({
|
||||
card,
|
||||
projectsToLists,
|
||||
boardMemberships,
|
||||
currentUserIds,
|
||||
labels,
|
||||
currentLabelIds,
|
||||
onNameEdit,
|
||||
onUpdate,
|
||||
onMove,
|
||||
onTransfer,
|
||||
onDuplicate,
|
||||
onDelete,
|
||||
onUserAdd,
|
||||
onUserRemove,
|
||||
onBoardFetch,
|
||||
onLabelAdd,
|
||||
onLabelRemove,
|
||||
onLabelCreate,
|
||||
onLabelUpdate,
|
||||
onLabelMove,
|
||||
onLabelDelete,
|
||||
onClose,
|
||||
}) => {
|
||||
const [t] = useTranslation();
|
||||
const [step, openStep, handleBack] = useSteps();
|
||||
|
||||
const handleEditNameClick = useCallback(() => {
|
||||
onNameEdit();
|
||||
onClose();
|
||||
}, [onNameEdit, onClose]);
|
||||
|
||||
const handleUsersClick = useCallback(() => {
|
||||
openStep(StepTypes.USERS);
|
||||
}, [openStep]);
|
||||
|
||||
const handleLabelsClick = useCallback(() => {
|
||||
openStep(StepTypes.LABELS);
|
||||
}, [openStep]);
|
||||
|
||||
const handleEditDueDateClick = useCallback(() => {
|
||||
openStep(StepTypes.EDIT_DUE_DATE);
|
||||
}, [openStep]);
|
||||
|
||||
const handleEditStopwatchClick = useCallback(() => {
|
||||
openStep(StepTypes.EDIT_STOPWATCH);
|
||||
}, [openStep]);
|
||||
|
||||
const handleMoveClick = useCallback(() => {
|
||||
openStep(StepTypes.MOVE);
|
||||
}, [openStep]);
|
||||
|
||||
const handleDuplicateClick = useCallback(() => {
|
||||
onDuplicate();
|
||||
onClose();
|
||||
}, [onDuplicate, onClose]);
|
||||
|
||||
const handleDeleteClick = useCallback(() => {
|
||||
openStep(StepTypes.DELETE);
|
||||
}, [openStep]);
|
||||
|
||||
const handleDueDateUpdate = useCallback(
|
||||
(dueDate) => {
|
||||
onUpdate({
|
||||
dueDate,
|
||||
});
|
||||
},
|
||||
[onUpdate],
|
||||
);
|
||||
|
||||
const handleStopwatchUpdate = useCallback(
|
||||
(stopwatch) => {
|
||||
onUpdate({
|
||||
stopwatch,
|
||||
});
|
||||
},
|
||||
[onUpdate],
|
||||
);
|
||||
|
||||
if (step) {
|
||||
switch (step.type) {
|
||||
case StepTypes.USERS:
|
||||
return (
|
||||
<BoardMembershipsStep
|
||||
items={boardMemberships}
|
||||
currentUserIds={currentUserIds}
|
||||
onUserSelect={onUserAdd}
|
||||
onUserDeselect={onUserRemove}
|
||||
onBack={handleBack}
|
||||
/>
|
||||
);
|
||||
case StepTypes.LABELS:
|
||||
return (
|
||||
<LabelsStep
|
||||
items={labels}
|
||||
currentIds={currentLabelIds}
|
||||
onSelect={onLabelAdd}
|
||||
onDeselect={onLabelRemove}
|
||||
onCreate={onLabelCreate}
|
||||
onUpdate={onLabelUpdate}
|
||||
onMove={onLabelMove}
|
||||
onDelete={onLabelDelete}
|
||||
onBack={handleBack}
|
||||
/>
|
||||
);
|
||||
case StepTypes.EDIT_DUE_DATE:
|
||||
return (
|
||||
<DueDateEditStep
|
||||
defaultValue={card.dueDate}
|
||||
onUpdate={handleDueDateUpdate}
|
||||
onBack={handleBack}
|
||||
onClose={onClose}
|
||||
/>
|
||||
);
|
||||
case StepTypes.EDIT_STOPWATCH:
|
||||
return (
|
||||
<StopwatchEditStep
|
||||
defaultValue={card.stopwatch}
|
||||
onUpdate={handleStopwatchUpdate}
|
||||
onBack={handleBack}
|
||||
onClose={onClose}
|
||||
/>
|
||||
);
|
||||
case StepTypes.MOVE:
|
||||
return (
|
||||
<CardMoveStep
|
||||
projectsToLists={projectsToLists}
|
||||
defaultPath={pick(card, ['projectId', 'boardId', 'listId'])}
|
||||
onMove={onMove}
|
||||
onTransfer={onTransfer}
|
||||
onBoardFetch={onBoardFetch}
|
||||
onBack={handleBack}
|
||||
onClose={onClose}
|
||||
/>
|
||||
);
|
||||
case StepTypes.DELETE:
|
||||
return (
|
||||
<DeleteStep
|
||||
title="common.deleteCard"
|
||||
content="common.areYouSureYouWantToDeleteThisCard"
|
||||
buttonContent="action.deleteCard"
|
||||
onConfirm={onDelete}
|
||||
onBack={handleBack}
|
||||
/>
|
||||
);
|
||||
default:
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
<Popup.Header>
|
||||
{t('common.cardActions', {
|
||||
context: 'title',
|
||||
})}
|
||||
</Popup.Header>
|
||||
<Popup.Content>
|
||||
<Menu secondary vertical className={styles.menu}>
|
||||
<Menu.Item className={styles.menuItem} onClick={handleEditNameClick}>
|
||||
{t('action.editTitle', {
|
||||
context: 'title',
|
||||
})}
|
||||
</Menu.Item>
|
||||
<Menu.Item className={styles.menuItem} onClick={handleUsersClick}>
|
||||
{t('common.members', {
|
||||
context: 'title',
|
||||
})}
|
||||
</Menu.Item>
|
||||
<Menu.Item className={styles.menuItem} onClick={handleLabelsClick}>
|
||||
{t('common.labels', {
|
||||
context: 'title',
|
||||
})}
|
||||
</Menu.Item>
|
||||
<Menu.Item className={styles.menuItem} onClick={handleEditDueDateClick}>
|
||||
{t('action.editDueDate', {
|
||||
context: 'title',
|
||||
})}
|
||||
</Menu.Item>
|
||||
<Menu.Item className={styles.menuItem} onClick={handleEditStopwatchClick}>
|
||||
{t('action.editStopwatch', {
|
||||
context: 'title',
|
||||
})}
|
||||
</Menu.Item>
|
||||
<Menu.Item className={styles.menuItem} onClick={handleMoveClick}>
|
||||
{t('action.moveCard', {
|
||||
context: 'title',
|
||||
})}
|
||||
</Menu.Item>
|
||||
<Menu.Item className={styles.menuItem} onClick={handleDuplicateClick}>
|
||||
{t('action.duplicateCard', {
|
||||
context: 'title',
|
||||
})}
|
||||
</Menu.Item>
|
||||
<Menu.Item className={styles.menuItem} onClick={handleDeleteClick}>
|
||||
{t('action.deleteCard', {
|
||||
context: 'title',
|
||||
})}
|
||||
</Menu.Item>
|
||||
</Menu>
|
||||
</Popup.Content>
|
||||
</>
|
||||
);
|
||||
},
|
||||
);
|
||||
|
||||
ActionsStep.propTypes = {
|
||||
/* eslint-disable react/forbid-prop-types */
|
||||
card: PropTypes.object.isRequired,
|
||||
projectsToLists: PropTypes.array.isRequired,
|
||||
boardMemberships: PropTypes.array.isRequired,
|
||||
currentUserIds: PropTypes.array.isRequired,
|
||||
labels: PropTypes.array.isRequired,
|
||||
currentLabelIds: PropTypes.array.isRequired,
|
||||
/* eslint-enable react/forbid-prop-types */
|
||||
onNameEdit: PropTypes.func.isRequired,
|
||||
onUpdate: PropTypes.func.isRequired,
|
||||
onMove: PropTypes.func.isRequired,
|
||||
onTransfer: PropTypes.func.isRequired,
|
||||
onDuplicate: PropTypes.func.isRequired,
|
||||
onDelete: PropTypes.func.isRequired,
|
||||
onUserAdd: PropTypes.func.isRequired,
|
||||
onUserRemove: PropTypes.func.isRequired,
|
||||
onBoardFetch: PropTypes.func.isRequired,
|
||||
onLabelAdd: PropTypes.func.isRequired,
|
||||
onLabelRemove: PropTypes.func.isRequired,
|
||||
onLabelCreate: PropTypes.func.isRequired,
|
||||
onLabelUpdate: PropTypes.func.isRequired,
|
||||
onLabelMove: PropTypes.func.isRequired,
|
||||
onLabelDelete: PropTypes.func.isRequired,
|
||||
onClose: PropTypes.func.isRequired,
|
||||
};
|
||||
|
||||
export default ActionsStep;
|
||||
@@ -1,11 +0,0 @@
|
||||
:global(#app) {
|
||||
.menu {
|
||||
margin: -7px -12px -5px;
|
||||
width: calc(100% + 24px);
|
||||
}
|
||||
|
||||
.menuItem {
|
||||
margin: 0;
|
||||
padding-left: 14px;
|
||||
}
|
||||
}
|
||||
@@ -1,289 +0,0 @@
|
||||
import React, { useCallback, useRef } from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import classNames from 'classnames';
|
||||
import { Button, Icon } from 'semantic-ui-react';
|
||||
import { Link } from 'react-router-dom';
|
||||
import { Draggable } from 'react-beautiful-dnd';
|
||||
import { usePopup } from '../../lib/popup';
|
||||
|
||||
import { startStopwatch, stopStopwatch } from '../../utils/stopwatch';
|
||||
import Paths from '../../constants/Paths';
|
||||
import Tasks from './Tasks';
|
||||
import NameEdit from './NameEdit';
|
||||
import ActionsStep from './ActionsStep';
|
||||
import User from '../User';
|
||||
import Label from '../Label';
|
||||
import DueDate from '../DueDate';
|
||||
import Stopwatch from '../Stopwatch';
|
||||
|
||||
import styles from './Card.module.scss';
|
||||
|
||||
const Card = React.memo(
|
||||
({
|
||||
id,
|
||||
index,
|
||||
name,
|
||||
description,
|
||||
dueDate,
|
||||
isDueDateCompleted,
|
||||
stopwatch,
|
||||
coverUrl,
|
||||
boardId,
|
||||
listId,
|
||||
projectId,
|
||||
isPersisted,
|
||||
attachmentsTotal,
|
||||
notificationsTotal,
|
||||
users,
|
||||
labels,
|
||||
tasks,
|
||||
allProjectsToLists,
|
||||
allBoardMemberships,
|
||||
allLabels,
|
||||
canEdit,
|
||||
onUpdate,
|
||||
onMove,
|
||||
onTransfer,
|
||||
onDuplicate,
|
||||
onDelete,
|
||||
onUserAdd,
|
||||
onUserRemove,
|
||||
onBoardFetch,
|
||||
onLabelAdd,
|
||||
onLabelRemove,
|
||||
onLabelCreate,
|
||||
onLabelUpdate,
|
||||
onLabelMove,
|
||||
onLabelDelete,
|
||||
}) => {
|
||||
const nameEdit = useRef(null);
|
||||
|
||||
const handleClick = useCallback(() => {
|
||||
if (document.activeElement) {
|
||||
document.activeElement.blur();
|
||||
}
|
||||
}, []);
|
||||
|
||||
const handleToggleStopwatchClick = useCallback(
|
||||
(event) => {
|
||||
event.preventDefault();
|
||||
|
||||
onUpdate({
|
||||
stopwatch: stopwatch.startedAt ? stopStopwatch(stopwatch) : startStopwatch(stopwatch),
|
||||
});
|
||||
},
|
||||
[stopwatch, onUpdate],
|
||||
);
|
||||
|
||||
const handleNameUpdate = useCallback(
|
||||
(newName) => {
|
||||
onUpdate({
|
||||
name: newName,
|
||||
});
|
||||
},
|
||||
[onUpdate],
|
||||
);
|
||||
|
||||
const handleNameEdit = useCallback(() => {
|
||||
nameEdit.current.open();
|
||||
}, []);
|
||||
|
||||
const ActionsPopup = usePopup(ActionsStep);
|
||||
|
||||
const contentNode = (
|
||||
<>
|
||||
{coverUrl && <img src={coverUrl} alt="" className={styles.cover} />}
|
||||
<div className={styles.details}>
|
||||
{labels.length > 0 && (
|
||||
<span className={styles.labels}>
|
||||
{labels.map((label) => (
|
||||
<span
|
||||
key={label.id}
|
||||
className={classNames(styles.attachment, styles.attachmentLeft)}
|
||||
>
|
||||
<Label name={label.name} color={label.color} size="tiny" />
|
||||
</span>
|
||||
))}
|
||||
</span>
|
||||
)}
|
||||
<div className={styles.name}>{name}</div>
|
||||
{tasks.length > 0 && <Tasks items={tasks} />}
|
||||
{(description ||
|
||||
dueDate ||
|
||||
stopwatch ||
|
||||
attachmentsTotal > 0 ||
|
||||
notificationsTotal > 0) && (
|
||||
<span className={styles.attachments}>
|
||||
{notificationsTotal > 0 && (
|
||||
<span
|
||||
className={classNames(
|
||||
styles.attachment,
|
||||
styles.attachmentLeft,
|
||||
styles.notification,
|
||||
)}
|
||||
>
|
||||
{notificationsTotal}
|
||||
</span>
|
||||
)}
|
||||
{dueDate && (
|
||||
<span className={classNames(styles.attachment, styles.attachmentLeft)}>
|
||||
<DueDate value={dueDate} isCompleted={isDueDateCompleted} size="tiny" />
|
||||
</span>
|
||||
)}
|
||||
{stopwatch && (
|
||||
<span className={classNames(styles.attachment, styles.attachmentLeft)}>
|
||||
<Stopwatch
|
||||
as="span"
|
||||
startedAt={stopwatch.startedAt}
|
||||
total={stopwatch.total}
|
||||
size="tiny"
|
||||
onClick={canEdit ? handleToggleStopwatchClick : undefined}
|
||||
/>
|
||||
</span>
|
||||
)}
|
||||
{description && (
|
||||
<span className={classNames(styles.attachment, styles.attachmentLeft)}>
|
||||
<span className={styles.attachmentContent}>
|
||||
<Icon name="align left" />
|
||||
</span>
|
||||
</span>
|
||||
)}
|
||||
{attachmentsTotal > 0 && (
|
||||
<span className={classNames(styles.attachment, styles.attachmentLeft)}>
|
||||
<span className={styles.attachmentContent}>
|
||||
<Icon name="attach" />
|
||||
{attachmentsTotal}
|
||||
</span>
|
||||
</span>
|
||||
)}
|
||||
</span>
|
||||
)}
|
||||
{users.length > 0 && (
|
||||
<span className={classNames(styles.attachments, styles.attachmentsRight)}>
|
||||
{users.map((user) => (
|
||||
<span
|
||||
key={user.id}
|
||||
className={classNames(styles.attachment, styles.attachmentRight)}
|
||||
>
|
||||
<User name={user.name} avatarUrl={user.avatarUrl} size="small" />
|
||||
</span>
|
||||
))}
|
||||
</span>
|
||||
)}
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
|
||||
return (
|
||||
<Draggable draggableId={`card:${id}`} index={index} isDragDisabled={!isPersisted || !canEdit}>
|
||||
{({ innerRef, draggableProps, dragHandleProps }) => (
|
||||
// eslint-disable-next-line react/jsx-props-no-spreading
|
||||
<div {...draggableProps} {...dragHandleProps} ref={innerRef} className={styles.wrapper}>
|
||||
<NameEdit ref={nameEdit} defaultValue={name} onUpdate={handleNameUpdate}>
|
||||
<div className={styles.card}>
|
||||
{isPersisted ? (
|
||||
<>
|
||||
<Link
|
||||
to={Paths.CARDS.replace(':id', id)}
|
||||
className={styles.content}
|
||||
onClick={handleClick}
|
||||
>
|
||||
{contentNode}
|
||||
</Link>
|
||||
{canEdit && (
|
||||
<ActionsPopup
|
||||
card={{
|
||||
dueDate,
|
||||
stopwatch,
|
||||
boardId,
|
||||
listId,
|
||||
projectId,
|
||||
}}
|
||||
projectsToLists={allProjectsToLists}
|
||||
boardMemberships={allBoardMemberships}
|
||||
currentUserIds={users.map((user) => user.id)}
|
||||
labels={allLabels}
|
||||
currentLabelIds={labels.map((label) => label.id)}
|
||||
onNameEdit={handleNameEdit}
|
||||
onUpdate={onUpdate}
|
||||
onMove={onMove}
|
||||
onTransfer={onTransfer}
|
||||
onDuplicate={onDuplicate}
|
||||
onDelete={onDelete}
|
||||
onUserAdd={onUserAdd}
|
||||
onUserRemove={onUserRemove}
|
||||
onBoardFetch={onBoardFetch}
|
||||
onLabelAdd={onLabelAdd}
|
||||
onLabelRemove={onLabelRemove}
|
||||
onLabelCreate={onLabelCreate}
|
||||
onLabelUpdate={onLabelUpdate}
|
||||
onLabelMove={onLabelMove}
|
||||
onLabelDelete={onLabelDelete}
|
||||
>
|
||||
<Button className={classNames(styles.actionsButton, styles.target)}>
|
||||
<Icon fitted name="pencil" size="small" />
|
||||
</Button>
|
||||
</ActionsPopup>
|
||||
)}
|
||||
</>
|
||||
) : (
|
||||
<span className={styles.content}>{contentNode}</span>
|
||||
)}
|
||||
</div>
|
||||
</NameEdit>
|
||||
</div>
|
||||
)}
|
||||
</Draggable>
|
||||
);
|
||||
},
|
||||
);
|
||||
|
||||
Card.propTypes = {
|
||||
id: PropTypes.string.isRequired,
|
||||
index: PropTypes.number.isRequired,
|
||||
name: PropTypes.string.isRequired,
|
||||
description: PropTypes.string,
|
||||
dueDate: PropTypes.instanceOf(Date),
|
||||
isDueDateCompleted: PropTypes.bool,
|
||||
stopwatch: PropTypes.object, // eslint-disable-line react/forbid-prop-types
|
||||
coverUrl: PropTypes.string,
|
||||
boardId: PropTypes.string.isRequired,
|
||||
listId: PropTypes.string.isRequired,
|
||||
projectId: PropTypes.string.isRequired,
|
||||
isPersisted: PropTypes.bool.isRequired,
|
||||
attachmentsTotal: PropTypes.number.isRequired,
|
||||
notificationsTotal: PropTypes.number.isRequired,
|
||||
/* eslint-disable react/forbid-prop-types */
|
||||
users: PropTypes.array.isRequired,
|
||||
labels: PropTypes.array.isRequired,
|
||||
tasks: PropTypes.array.isRequired,
|
||||
allProjectsToLists: PropTypes.array.isRequired,
|
||||
allBoardMemberships: PropTypes.array.isRequired,
|
||||
allLabels: PropTypes.array.isRequired,
|
||||
/* eslint-enable react/forbid-prop-types */
|
||||
canEdit: PropTypes.bool.isRequired,
|
||||
onUpdate: PropTypes.func.isRequired,
|
||||
onMove: PropTypes.func.isRequired,
|
||||
onTransfer: PropTypes.func.isRequired,
|
||||
onDuplicate: PropTypes.func.isRequired,
|
||||
onDelete: PropTypes.func.isRequired,
|
||||
onUserAdd: PropTypes.func.isRequired,
|
||||
onUserRemove: PropTypes.func.isRequired,
|
||||
onBoardFetch: PropTypes.func.isRequired,
|
||||
onLabelAdd: PropTypes.func.isRequired,
|
||||
onLabelRemove: PropTypes.func.isRequired,
|
||||
onLabelCreate: PropTypes.func.isRequired,
|
||||
onLabelUpdate: PropTypes.func.isRequired,
|
||||
onLabelMove: PropTypes.func.isRequired,
|
||||
onLabelDelete: PropTypes.func.isRequired,
|
||||
};
|
||||
|
||||
Card.defaultProps = {
|
||||
description: undefined,
|
||||
dueDate: undefined,
|
||||
isDueDateCompleted: undefined,
|
||||
stopwatch: undefined,
|
||||
coverUrl: undefined,
|
||||
};
|
||||
|
||||
export default Card;
|
||||
@@ -1,127 +0,0 @@
|
||||
import React, { useCallback, useEffect, useImperativeHandle, useRef, useState } from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import TextareaAutosize from 'react-textarea-autosize';
|
||||
import { Button, Form, TextArea } from 'semantic-ui-react';
|
||||
|
||||
import { useClosableForm, useField } from '../../hooks';
|
||||
import { focusEnd } from '../../utils/element-helpers';
|
||||
|
||||
import styles from './NameEdit.module.scss';
|
||||
|
||||
const NameEdit = React.forwardRef(({ children, defaultValue, onUpdate }, ref) => {
|
||||
const [t] = useTranslation();
|
||||
const [isOpened, setIsOpened] = useState(false);
|
||||
const [value, handleFieldChange, setValue] = useField(defaultValue);
|
||||
|
||||
const field = useRef(null);
|
||||
|
||||
const open = useCallback(() => {
|
||||
setIsOpened(true);
|
||||
setValue(defaultValue);
|
||||
}, [defaultValue, setValue]);
|
||||
|
||||
const close = useCallback(() => {
|
||||
setIsOpened(false);
|
||||
setValue(null);
|
||||
}, [setValue]);
|
||||
|
||||
const submit = useCallback(() => {
|
||||
const cleanValue = value.trim();
|
||||
|
||||
if (!cleanValue) {
|
||||
field.current.ref.current.select();
|
||||
return;
|
||||
}
|
||||
|
||||
if (cleanValue !== defaultValue) {
|
||||
onUpdate(cleanValue);
|
||||
}
|
||||
|
||||
close();
|
||||
}, [defaultValue, onUpdate, value, close]);
|
||||
|
||||
useImperativeHandle(
|
||||
ref,
|
||||
() => ({
|
||||
open,
|
||||
close,
|
||||
}),
|
||||
[open, close],
|
||||
);
|
||||
|
||||
const handleFieldKeyDown = useCallback(
|
||||
(event) => {
|
||||
switch (event.key) {
|
||||
case 'Enter':
|
||||
event.preventDefault();
|
||||
|
||||
submit();
|
||||
|
||||
break;
|
||||
case 'Escape':
|
||||
close();
|
||||
|
||||
break;
|
||||
default:
|
||||
}
|
||||
},
|
||||
[close, submit],
|
||||
);
|
||||
|
||||
const [handleFieldBlur, handleControlMouseOver, handleControlMouseOut] = useClosableForm(
|
||||
close,
|
||||
isOpened,
|
||||
);
|
||||
|
||||
const handleSubmit = useCallback(() => {
|
||||
submit();
|
||||
}, [submit]);
|
||||
|
||||
useEffect(() => {
|
||||
if (isOpened) {
|
||||
focusEnd(field.current.ref.current);
|
||||
}
|
||||
}, [isOpened]);
|
||||
|
||||
if (!isOpened) {
|
||||
return children;
|
||||
}
|
||||
|
||||
return (
|
||||
<Form onSubmit={handleSubmit}>
|
||||
<div className={styles.fieldWrapper}>
|
||||
<TextArea
|
||||
ref={field}
|
||||
as={TextareaAutosize}
|
||||
value={value}
|
||||
minRows={3}
|
||||
maxRows={8}
|
||||
spellCheck={false}
|
||||
className={styles.field}
|
||||
onKeyDown={handleFieldKeyDown}
|
||||
onChange={handleFieldChange}
|
||||
onBlur={handleFieldBlur}
|
||||
/>
|
||||
</div>
|
||||
<div className={styles.controls}>
|
||||
{/* eslint-disable-next-line jsx-a11y/mouse-events-have-key-events */}
|
||||
<Button
|
||||
positive
|
||||
content={t('action.save')}
|
||||
className={styles.submitButton}
|
||||
onMouseOver={handleControlMouseOver}
|
||||
onMouseOut={handleControlMouseOut}
|
||||
/>
|
||||
</div>
|
||||
</Form>
|
||||
);
|
||||
});
|
||||
|
||||
NameEdit.propTypes = {
|
||||
children: PropTypes.element.isRequired,
|
||||
defaultValue: PropTypes.string.isRequired,
|
||||
onUpdate: PropTypes.func.isRequired,
|
||||
};
|
||||
|
||||
export default React.memo(NameEdit);
|
||||
@@ -1,66 +0,0 @@
|
||||
import React, { useCallback } from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import classNames from 'classnames';
|
||||
import { Progress } from 'semantic-ui-react';
|
||||
import { useToggle } from '../../lib/hooks';
|
||||
|
||||
import Linkify from '../Linkify';
|
||||
|
||||
import styles from './Tasks.module.scss';
|
||||
|
||||
const Tasks = React.memo(({ items }) => {
|
||||
const [isOpened, toggleOpened] = useToggle();
|
||||
|
||||
const handleToggleClick = useCallback(
|
||||
(event) => {
|
||||
event.preventDefault();
|
||||
|
||||
toggleOpened();
|
||||
},
|
||||
[toggleOpened],
|
||||
);
|
||||
|
||||
const completedItems = items.filter((item) => item.isCompleted);
|
||||
|
||||
return (
|
||||
<>
|
||||
{/* eslint-disable-next-line jsx-a11y/click-events-have-key-events,
|
||||
jsx-a11y/no-static-element-interactions */}
|
||||
<div className={styles.button} onClick={handleToggleClick}>
|
||||
<span className={styles.progressWrapper}>
|
||||
<Progress
|
||||
autoSuccess
|
||||
value={completedItems.length}
|
||||
total={items.length}
|
||||
color="blue"
|
||||
size="tiny"
|
||||
className={styles.progress}
|
||||
/>
|
||||
</span>
|
||||
<span
|
||||
className={classNames(styles.count, isOpened ? styles.countOpened : styles.countClosed)}
|
||||
>
|
||||
{completedItems.length}/{items.length}
|
||||
</span>
|
||||
</div>
|
||||
{isOpened && (
|
||||
<ul className={styles.tasks}>
|
||||
{items.map((item) => (
|
||||
<li
|
||||
key={item.id}
|
||||
className={classNames(styles.task, item.isCompleted && styles.taskCompleted)}
|
||||
>
|
||||
<Linkify linkStopPropagation>{item.name}</Linkify>
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
});
|
||||
|
||||
Tasks.propTypes = {
|
||||
items: PropTypes.array.isRequired, // eslint-disable-line react/forbid-prop-types
|
||||
};
|
||||
|
||||
export default Tasks;
|
||||
@@ -1,3 +0,0 @@
|
||||
import Card from './Card';
|
||||
|
||||
export default Card;
|
||||
@@ -1,112 +0,0 @@
|
||||
import React, { useCallback } from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { Button, Comment, Icon, Loader, Visibility } from 'semantic-ui-react';
|
||||
|
||||
import { ActivityTypes } from '../../../constants/Enums';
|
||||
import CommentAdd from './CommentAdd';
|
||||
import Item from './Item';
|
||||
|
||||
import styles from './Activities.module.scss';
|
||||
|
||||
const Activities = React.memo(
|
||||
({
|
||||
items,
|
||||
isFetching,
|
||||
isAllFetched,
|
||||
isDetailsVisible,
|
||||
isDetailsFetching,
|
||||
canEdit,
|
||||
canEditAllComments,
|
||||
onFetch,
|
||||
onDetailsToggle,
|
||||
onCommentCreate,
|
||||
onCommentUpdate,
|
||||
onCommentDelete,
|
||||
}) => {
|
||||
const [t] = useTranslation();
|
||||
|
||||
const handleToggleDetailsClick = useCallback(() => {
|
||||
onDetailsToggle(!isDetailsVisible);
|
||||
}, [isDetailsVisible, onDetailsToggle]);
|
||||
|
||||
const handleCommentUpdate = useCallback(
|
||||
(id, data) => {
|
||||
onCommentUpdate(id, data);
|
||||
},
|
||||
[onCommentUpdate],
|
||||
);
|
||||
|
||||
const handleCommentDelete = useCallback(
|
||||
(id) => {
|
||||
onCommentDelete(id);
|
||||
},
|
||||
[onCommentDelete],
|
||||
);
|
||||
|
||||
return (
|
||||
<div className={styles.contentModule}>
|
||||
<div className={styles.moduleWrapper}>
|
||||
<Icon name="list ul" className={styles.moduleIcon} />
|
||||
<div className={styles.moduleHeader}>
|
||||
{t('common.actions')}
|
||||
<Button
|
||||
content={isDetailsVisible ? t('action.hideDetails') : t('action.showDetails')}
|
||||
className={styles.toggleButton}
|
||||
onClick={handleToggleDetailsClick}
|
||||
/>
|
||||
</div>
|
||||
{canEdit && <CommentAdd onCreate={onCommentCreate} />}
|
||||
<div className={styles.wrapper}>
|
||||
<Comment.Group>
|
||||
{items.map((item) =>
|
||||
item.type === ActivityTypes.COMMENT_CARD ? (
|
||||
<Item.Comment
|
||||
key={item.id}
|
||||
data={item.data}
|
||||
createdAt={item.createdAt}
|
||||
isPersisted={item.isPersisted}
|
||||
user={item.user}
|
||||
canEdit={(item.user.isCurrent && canEdit) || canEditAllComments}
|
||||
onUpdate={(data) => handleCommentUpdate(item.id, data)}
|
||||
onDelete={() => handleCommentDelete(item.id)}
|
||||
/>
|
||||
) : (
|
||||
<Item
|
||||
key={item.id}
|
||||
type={item.type}
|
||||
data={item.data}
|
||||
createdAt={item.createdAt}
|
||||
user={item.user}
|
||||
/>
|
||||
),
|
||||
)}
|
||||
</Comment.Group>
|
||||
</div>
|
||||
{isFetching || isDetailsFetching ? (
|
||||
<Loader active inverted inline="centered" size="small" className={styles.loader} />
|
||||
) : (
|
||||
!isAllFetched && <Visibility fireOnMount onOnScreen={onFetch} />
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
},
|
||||
);
|
||||
|
||||
Activities.propTypes = {
|
||||
items: PropTypes.array.isRequired, // eslint-disable-line react/forbid-prop-types
|
||||
isFetching: PropTypes.bool.isRequired,
|
||||
isAllFetched: PropTypes.bool.isRequired,
|
||||
isDetailsVisible: PropTypes.bool.isRequired,
|
||||
isDetailsFetching: PropTypes.bool.isRequired,
|
||||
canEdit: PropTypes.bool.isRequired,
|
||||
canEditAllComments: PropTypes.bool.isRequired,
|
||||
onFetch: PropTypes.func.isRequired,
|
||||
onDetailsToggle: PropTypes.func.isRequired,
|
||||
onCommentCreate: PropTypes.func.isRequired,
|
||||
onCommentUpdate: PropTypes.func.isRequired,
|
||||
onCommentDelete: PropTypes.func.isRequired,
|
||||
};
|
||||
|
||||
export default Activities;
|
||||
@@ -1,102 +0,0 @@
|
||||
import React, { useCallback, useRef, useState } from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import TextareaAutosize from 'react-textarea-autosize';
|
||||
import { Button, Form, TextArea } from 'semantic-ui-react';
|
||||
import { useDidUpdate, useToggle } from '../../../lib/hooks';
|
||||
|
||||
import { useClosableForm, useForm } from '../../../hooks';
|
||||
|
||||
import styles from './CommentAdd.module.scss';
|
||||
|
||||
const DEFAULT_DATA = {
|
||||
text: '',
|
||||
};
|
||||
|
||||
const CommentAdd = React.memo(({ onCreate }) => {
|
||||
const [t] = useTranslation();
|
||||
const [isOpened, setIsOpened] = useState(false);
|
||||
const [data, handleFieldChange, setData] = useForm(DEFAULT_DATA);
|
||||
const [selectTextFieldState, selectTextField] = useToggle();
|
||||
|
||||
const textField = useRef(null);
|
||||
|
||||
const close = useCallback(() => {
|
||||
setIsOpened(false);
|
||||
}, []);
|
||||
|
||||
const submit = useCallback(() => {
|
||||
const cleanData = {
|
||||
...data,
|
||||
text: data.text.trim(),
|
||||
};
|
||||
|
||||
if (!cleanData.text) {
|
||||
textField.current.ref.current.select();
|
||||
return;
|
||||
}
|
||||
|
||||
onCreate(cleanData);
|
||||
|
||||
setData(DEFAULT_DATA);
|
||||
selectTextField();
|
||||
}, [onCreate, data, setData, selectTextField]);
|
||||
|
||||
const handleFieldFocus = useCallback(() => {
|
||||
setIsOpened(true);
|
||||
}, []);
|
||||
|
||||
const handleFieldKeyDown = useCallback(
|
||||
(event) => {
|
||||
if (event.ctrlKey && event.key === 'Enter') {
|
||||
submit();
|
||||
}
|
||||
},
|
||||
[submit],
|
||||
);
|
||||
|
||||
const [handleFieldBlur, handleControlMouseOver, handleControlMouseOut] = useClosableForm(close);
|
||||
|
||||
const handleSubmit = useCallback(() => {
|
||||
submit();
|
||||
}, [submit]);
|
||||
|
||||
useDidUpdate(() => {
|
||||
textField.current.ref.current.focus();
|
||||
}, [selectTextFieldState]);
|
||||
|
||||
return (
|
||||
<Form onSubmit={handleSubmit}>
|
||||
<TextArea
|
||||
ref={textField}
|
||||
as={TextareaAutosize}
|
||||
name="text"
|
||||
value={data.text}
|
||||
placeholder={t('common.writeComment')}
|
||||
minRows={isOpened ? 3 : 1}
|
||||
spellCheck={false}
|
||||
className={styles.field}
|
||||
onFocus={handleFieldFocus}
|
||||
onKeyDown={handleFieldKeyDown}
|
||||
onChange={handleFieldChange}
|
||||
onBlur={handleFieldBlur}
|
||||
/>
|
||||
{isOpened && (
|
||||
<div className={styles.controls}>
|
||||
<Button
|
||||
positive
|
||||
content={t('action.addComment')}
|
||||
onMouseOver={handleControlMouseOver}
|
||||
onMouseOut={handleControlMouseOut}
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
</Form>
|
||||
);
|
||||
});
|
||||
|
||||
CommentAdd.propTypes = {
|
||||
onCreate: PropTypes.func.isRequired,
|
||||
};
|
||||
|
||||
export default CommentAdd;
|
||||
@@ -1,115 +0,0 @@
|
||||
import { dequal } from 'dequal';
|
||||
import React, { useCallback, useEffect, useImperativeHandle, useRef, useState } from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import TextareaAutosize from 'react-textarea-autosize';
|
||||
import { Button, Form, TextArea } from 'semantic-ui-react';
|
||||
|
||||
import { useForm } from '../../../hooks';
|
||||
import { focusEnd } from '../../../utils/element-helpers';
|
||||
|
||||
import styles from './CommentEdit.module.scss';
|
||||
|
||||
const CommentEdit = React.forwardRef(({ defaultData, onUpdate, text, actions }, ref) => {
|
||||
const [t] = useTranslation();
|
||||
const [isOpened, setIsOpened] = useState(false);
|
||||
const [data, handleFieldChange, setData] = useForm(null);
|
||||
|
||||
const textField = useRef(null);
|
||||
|
||||
const open = useCallback(() => {
|
||||
setIsOpened(true);
|
||||
setData({
|
||||
text: '',
|
||||
...defaultData,
|
||||
});
|
||||
}, [defaultData, setData]);
|
||||
|
||||
const close = useCallback(() => {
|
||||
setIsOpened(false);
|
||||
setData(null);
|
||||
}, [setData]);
|
||||
|
||||
const submit = useCallback(() => {
|
||||
const cleanData = {
|
||||
...data,
|
||||
text: data.text.trim(),
|
||||
};
|
||||
|
||||
if (cleanData.text && !dequal(cleanData, defaultData)) {
|
||||
onUpdate(cleanData);
|
||||
}
|
||||
|
||||
close();
|
||||
}, [defaultData, onUpdate, data, close]);
|
||||
|
||||
useImperativeHandle(
|
||||
ref,
|
||||
() => ({
|
||||
open,
|
||||
close,
|
||||
}),
|
||||
[open, close],
|
||||
);
|
||||
|
||||
const handleFieldKeyDown = useCallback(
|
||||
(event) => {
|
||||
if (event.ctrlKey && event.key === 'Enter') {
|
||||
submit();
|
||||
}
|
||||
},
|
||||
[submit],
|
||||
);
|
||||
|
||||
const handleFieldBlur = useCallback(() => {
|
||||
submit();
|
||||
}, [submit]);
|
||||
|
||||
const handleSubmit = useCallback(() => {
|
||||
submit();
|
||||
}, [submit]);
|
||||
|
||||
useEffect(() => {
|
||||
if (isOpened) {
|
||||
focusEnd(textField.current.ref.current);
|
||||
}
|
||||
}, [isOpened]);
|
||||
|
||||
if (!isOpened) {
|
||||
return (
|
||||
<>
|
||||
{actions}
|
||||
{text}
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<Form onSubmit={handleSubmit}>
|
||||
<TextArea
|
||||
ref={textField}
|
||||
as={TextareaAutosize}
|
||||
name="text"
|
||||
value={data.text}
|
||||
minRows={3}
|
||||
spellCheck={false}
|
||||
className={styles.field}
|
||||
onKeyDown={handleFieldKeyDown}
|
||||
onChange={handleFieldChange}
|
||||
onBlur={handleFieldBlur}
|
||||
/>
|
||||
<div className={styles.controls}>
|
||||
<Button positive content={t('action.save')} />
|
||||
</div>
|
||||
</Form>
|
||||
);
|
||||
});
|
||||
|
||||
CommentEdit.propTypes = {
|
||||
defaultData: PropTypes.object.isRequired, // eslint-disable-line react/forbid-prop-types
|
||||
onUpdate: PropTypes.func.isRequired,
|
||||
text: PropTypes.element.isRequired,
|
||||
actions: PropTypes.element.isRequired,
|
||||
};
|
||||
|
||||
export default React.memo(CommentEdit);
|
||||
@@ -1,89 +0,0 @@
|
||||
import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import classNames from 'classnames';
|
||||
import { useTranslation, Trans } from 'react-i18next';
|
||||
import { Comment } from 'semantic-ui-react';
|
||||
|
||||
import getDateFormat from '../../../utils/get-date-format';
|
||||
import { ActivityTypes } from '../../../constants/Enums';
|
||||
import ItemComment from './ItemComment';
|
||||
import User from '../../User';
|
||||
|
||||
import styles from './Item.module.scss';
|
||||
|
||||
const Item = React.memo(({ type, data, createdAt, user }) => {
|
||||
const [t] = useTranslation();
|
||||
|
||||
let contentNode;
|
||||
switch (type) {
|
||||
case ActivityTypes.CREATE_CARD:
|
||||
contentNode = (
|
||||
<Trans
|
||||
i18nKey="common.userAddedThisCardToList"
|
||||
values={{
|
||||
user: user.name,
|
||||
list: data.list.name,
|
||||
}}
|
||||
>
|
||||
<span className={styles.author}>{user.name}</span>
|
||||
<span className={styles.text}>
|
||||
{' added this card to '}
|
||||
{data.list.name}
|
||||
</span>
|
||||
</Trans>
|
||||
);
|
||||
|
||||
break;
|
||||
case ActivityTypes.MOVE_CARD:
|
||||
contentNode = (
|
||||
<Trans
|
||||
i18nKey="common.userMovedThisCardFromListToList"
|
||||
values={{
|
||||
user: user.name,
|
||||
fromList: data.fromList.name,
|
||||
toList: data.toList.name,
|
||||
}}
|
||||
>
|
||||
<span className={styles.author}>{user.name}</span>
|
||||
<span className={styles.text}>
|
||||
{' moved this card from '}
|
||||
{data.fromList.name}
|
||||
{' to '}
|
||||
{data.toList.name}
|
||||
</span>
|
||||
</Trans>
|
||||
);
|
||||
|
||||
break;
|
||||
default:
|
||||
contentNode = null;
|
||||
}
|
||||
|
||||
return (
|
||||
<Comment>
|
||||
<span className={styles.user}>
|
||||
<User name={user.name} avatarUrl={user.avatarUrl} />
|
||||
</span>
|
||||
<div className={classNames(styles.content)}>
|
||||
<div>{contentNode}</div>
|
||||
<span className={styles.date}>
|
||||
{t(`format:${getDateFormat(createdAt)}`, {
|
||||
postProcess: 'formatDate',
|
||||
value: createdAt,
|
||||
})}
|
||||
</span>
|
||||
</div>
|
||||
</Comment>
|
||||
);
|
||||
});
|
||||
|
||||
Item.Comment = ItemComment;
|
||||
|
||||
Item.propTypes = {
|
||||
type: PropTypes.string.isRequired,
|
||||
data: PropTypes.object.isRequired, // eslint-disable-line react/forbid-prop-types
|
||||
createdAt: PropTypes.instanceOf(Date).isRequired,
|
||||
user: PropTypes.object.isRequired, // eslint-disable-line react/forbid-prop-types
|
||||
};
|
||||
|
||||
export default Item;
|
||||
@@ -1,95 +0,0 @@
|
||||
import React, { useCallback, useRef } from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import classNames from 'classnames';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { Comment } from 'semantic-ui-react';
|
||||
import { usePopup } from '../../../lib/popup';
|
||||
import { Markdown } from '../../../lib/custom-ui';
|
||||
|
||||
import getDateFormat from '../../../utils/get-date-format';
|
||||
import CommentEdit from './CommentEdit';
|
||||
import User from '../../User';
|
||||
import DeleteStep from '../../DeleteStep';
|
||||
|
||||
import styles from './ItemComment.module.scss';
|
||||
|
||||
const ItemComment = React.memo(
|
||||
({ data, createdAt, isPersisted, user, canEdit, onUpdate, onDelete }) => {
|
||||
const [t] = useTranslation();
|
||||
|
||||
const commentEdit = useRef(null);
|
||||
|
||||
const handleEditClick = useCallback(() => {
|
||||
commentEdit.current.open();
|
||||
}, []);
|
||||
|
||||
const DeletePopup = usePopup(DeleteStep);
|
||||
|
||||
return (
|
||||
<Comment>
|
||||
<span className={styles.user}>
|
||||
<User name={user.name} avatarUrl={user.avatarUrl} />
|
||||
</span>
|
||||
<div className={classNames(styles.content)}>
|
||||
<CommentEdit
|
||||
ref={commentEdit}
|
||||
defaultData={data}
|
||||
onUpdate={onUpdate}
|
||||
text={
|
||||
<div className={styles.text}>
|
||||
<Markdown linkTarget="_blank">{data.text}</Markdown>
|
||||
</div>
|
||||
}
|
||||
actions={
|
||||
<div className={styles.title}>
|
||||
<span>
|
||||
<span className={styles.author}>{user.name}</span>
|
||||
<span className={styles.date}>
|
||||
{t(`format:${getDateFormat(createdAt)}`, {
|
||||
postProcess: 'formatDate',
|
||||
value: createdAt,
|
||||
})}
|
||||
</span>
|
||||
</span>
|
||||
{canEdit && (
|
||||
<Comment.Actions>
|
||||
<Comment.Action
|
||||
as="button"
|
||||
content={t('action.edit')}
|
||||
disabled={!isPersisted}
|
||||
onClick={handleEditClick}
|
||||
/>
|
||||
<DeletePopup
|
||||
title="common.deleteComment"
|
||||
content="common.areYouSureYouWantToDeleteThisComment"
|
||||
buttonContent="action.deleteComment"
|
||||
onConfirm={onDelete}
|
||||
>
|
||||
<Comment.Action
|
||||
as="button"
|
||||
content={t('action.delete')}
|
||||
disabled={!isPersisted}
|
||||
/>
|
||||
</DeletePopup>
|
||||
</Comment.Actions>
|
||||
)}
|
||||
</div>
|
||||
}
|
||||
/>
|
||||
</div>
|
||||
</Comment>
|
||||
);
|
||||
},
|
||||
);
|
||||
|
||||
ItemComment.propTypes = {
|
||||
data: PropTypes.object.isRequired, // eslint-disable-line react/forbid-prop-types
|
||||
createdAt: PropTypes.instanceOf(Date).isRequired,
|
||||
isPersisted: PropTypes.bool.isRequired,
|
||||
user: PropTypes.object.isRequired, // eslint-disable-line react/forbid-prop-types
|
||||
canEdit: PropTypes.bool.isRequired,
|
||||
onUpdate: PropTypes.func.isRequired,
|
||||
onDelete: PropTypes.func.isRequired,
|
||||
};
|
||||
|
||||
export default ItemComment;
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user