diff --git a/client/src/actions/entry/project.js b/client/src/actions/entry/project.js index f019caaa..d0affde8 100755 --- a/client/src/actions/entry/project.js +++ b/client/src/actions/entry/project.js @@ -14,6 +14,13 @@ export const updateCurrentProject = (data) => ({ }, }); +export const updateCurrentProjectBackgroundImage = (data) => ({ + type: EntryActionTypes.CURRENT_PROJECT_BACKGROUND_IMAGE_UPDATE, + payload: { + data, + }, +}); + export const deleteCurrentProject = () => ({ type: EntryActionTypes.CURRENT_PROJECT_DELETE, payload: {}, diff --git a/client/src/actions/project.js b/client/src/actions/project.js index 7bbf7308..18bd1a6e 100644 --- a/client/src/actions/project.js +++ b/client/src/actions/project.js @@ -90,6 +90,28 @@ export const updateProjectReceived = (project) => ({ }, }); +export const updateProjectBackgroundImageRequested = (id) => ({ + type: ActionTypes.PROJECT_BACKGROUND_IMAGE_UPDATE_REQUESTED, + payload: { + id, + }, +}); + +export const updateProjectBackgroundImageSucceeded = (project) => ({ + type: ActionTypes.PROJECT_BACKGROUND_IMAGE_UPDATE_SUCCEEDED, + payload: { + project, + }, +}); + +export const updateProjectBackgroundImageFailed = (id, error) => ({ + type: ActionTypes.PROJECT_BACKGROUND_IMAGE_UPDATE_FAILED, + payload: { + id, + error, + }, +}); + export const deleteProjectRequested = (id) => ({ type: ActionTypes.PROJECT_DELETE_REQUESTED, payload: { diff --git a/client/src/actions/user.js b/client/src/actions/user.js index 1ae875fd..7dafb26c 100644 --- a/client/src/actions/user.js +++ b/client/src/actions/user.js @@ -170,11 +170,10 @@ export const updateUserEmailRequested = (id, data) => ({ }, }); -export const updateUserEmailSucceeded = (id, email) => ({ +export const updateUserEmailSucceeded = (user) => ({ type: ActionTypes.USER_EMAIL_UPDATE_SUCCEEDED, payload: { - id, - email, + user, }, }); @@ -194,10 +193,10 @@ export const updateUserPasswordRequested = (id, data) => ({ }, }); -export const updateUserPasswordSucceeded = (id) => ({ +export const updateUserPasswordSucceeded = (user) => ({ type: ActionTypes.USER_PASSWORD_UPDATE_SUCCEEDED, payload: { - id, + user, }, }); @@ -217,11 +216,10 @@ export const updateUserUsernameRequested = (id, data) => ({ }, }); -export const updateUserUsernameSucceeded = (id, username) => ({ +export const updateUserUsernameSucceeded = (user) => ({ type: ActionTypes.USER_USERNAME_UPDATE_SUCCEEDED, payload: { - id, - username, + user, }, }); @@ -240,11 +238,10 @@ export const updateUserAvatarRequested = (id) => ({ }, }); -export const updateUserAvatarSucceeded = (id, avatarUrl) => ({ +export const updateUserAvatarSucceeded = (user) => ({ type: ActionTypes.USER_AVATAR_UPDATE_SUCCEEDED, payload: { - id, - avatarUrl, + user, }, }); diff --git a/client/src/api/projects.js b/client/src/api/projects.js index d9477214..c3ffc066 100755 --- a/client/src/api/projects.js +++ b/client/src/api/projects.js @@ -1,3 +1,4 @@ +import http from './http'; import socket from './socket'; /* Actions */ @@ -8,11 +9,15 @@ const createProject = (data, headers) => socket.post('/projects', data, 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 { getProjects, createProject, updateProject, + updateProjectBackgroundImage, deleteProject, }; diff --git a/client/src/api/users.js b/client/src/api/users.js index 272ce30f..b936f872 100755 --- a/client/src/api/users.js +++ b/client/src/api/users.js @@ -19,8 +19,7 @@ const updateUserPassword = (id, data, headers) => const updateUserUsername = (id, data, headers) => socket.patch(`/users/${id}/username`, data, headers); -const updateUserAvatar = (id, data, headers) => - http.post(`/users/${id}/update-avatar`, data, headers); +const updateUserAvatar = (id, data, headers) => http.post(`/users/${id}/avatar`, data, headers); const deleteUser = (id, headers) => socket.delete(`/users/${id}`, undefined, headers); diff --git a/client/src/components/Board/AddList.module.css b/client/src/components/Board/AddList.module.css index 1f5ee6ef..fdd4caa9 100644 --- a/client/src/components/Board/AddList.module.css +++ b/client/src/components/Board/AddList.module.css @@ -23,7 +23,7 @@ } .wrapper { - background-color: #e2e4e6; + background: #e2e4e6; border-radius: 3px; padding: 4px; transition: opacity 40ms ease-in; diff --git a/client/src/components/Board/Board.jsx b/client/src/components/Board/Board.jsx index be936b89..4342d191 100755 --- a/client/src/components/Board/Board.jsx +++ b/client/src/components/Board/Board.jsx @@ -110,6 +110,14 @@ const Board = React.memo( prevPosition.current = null; }, [prevPosition]); + useEffect(() => { + document.body.style.overflowX = 'auto'; + + return () => { + document.body.style.overflowX = null; + }; + }, []); + useEffect(() => { if (isAddListOpened) { window.scroll(document.body.scrollWidth, 0); diff --git a/client/src/components/Board/Board.module.css b/client/src/components/Board/Board.module.css index e6bb0293..fe0fffea 100644 --- a/client/src/components/Board/Board.module.css +++ b/client/src/components/Board/Board.module.css @@ -1,11 +1,11 @@ .addListButton { - background: hsla(0, 0%, 0%, 0.24); + background: rgba(0, 0, 0, 0.24); border: none; border-radius: 3px; - color: hsla(0, 0%, 100%, 0.72); + color: rgba(255, 255, 255, 0.72); cursor: pointer; display: block; - fill: hsla(0, 0%, 100%, 0.72); + fill: rgba(255, 255, 255, 0.72); font-weight: normal; height: 42px; padding: 11px; @@ -20,7 +20,7 @@ } .addListButton:hover { - background: hsla(0, 0%, 0%, 0.32); + background: rgba(0, 0, 0, 0.32); } .addListButtonIcon { diff --git a/client/src/components/Board/Filter.module.css b/client/src/components/Board/Filter.module.css index c66691a3..529824bf 100644 --- a/client/src/components/Board/Filter.module.css +++ b/client/src/components/Board/Filter.module.css @@ -23,7 +23,7 @@ } .filterLabel { - background: hsla(0, 0%, 0%, 0.24); + background: rgba(0, 0, 0, 0.24); border-radius: 3px; color: #fff; display: inline-block; @@ -33,7 +33,7 @@ } .filterLabel:hover { - background: hsla(0, 0%, 0%, 0.32); + background: rgba(0, 0, 0, 0.32); } .filterTitle { diff --git a/client/src/components/Boards/Boards.module.css b/client/src/components/Boards/Boards.module.css index 7471751d..474ed840 100644 --- a/client/src/components/Boards/Boards.module.css +++ b/client/src/components/Boards/Boards.module.css @@ -24,7 +24,7 @@ } .editButton:hover { - background: hsla(0, 0%, 100%, 0.08) !important; + background: rgba(255, 255, 255, 0.08) !important; } .link { @@ -45,7 +45,7 @@ } .tab:hover { - background: hsla(0, 0%, 0%, 0.24) !important; + background: rgba(0, 0, 0, 0.24) !important; } .tab:hover .target { @@ -53,11 +53,11 @@ } .tabActive { - background: hsla(0, 0%, 0%, 0.24) !important; + background: rgba(0, 0, 0, 0.24) !important; } .tabActive:hover { - background: hsla(0, 0%, 0%, 0.32) !important; + background: rgba(0, 0, 0, 0.32) !important; } .tabWrapper { @@ -66,7 +66,7 @@ } .tabs { - border-bottom: 2px solid hsla(0, 0%, 0%, 0.24); + border-bottom: 2px solid rgba(0, 0, 0, 0.24);; display: flex; height: 38px; flex: 0 0 auto; diff --git a/client/src/components/Card/Card.module.css b/client/src/components/Card/Card.module.css index 9776fb63..076bb407 100644 --- a/client/src/components/Card/Card.module.css +++ b/client/src/components/Card/Card.module.css @@ -49,7 +49,7 @@ } .card { - background-color: #fff; + background: #fff; border-radius: 3px; box-shadow: 0 1px 0 #ccc; position: relative; @@ -57,7 +57,7 @@ } .card:hover { - background-color: #f5f6f7; + background: #f5f6f7; border-bottom-color: rgba(9, 30, 66, 0.25); } diff --git a/client/src/components/Card/EditName.module.css b/client/src/components/Card/EditName.module.css index f774035a..f84b9399 100644 --- a/client/src/components/Card/EditName.module.css +++ b/client/src/components/Card/EditName.module.css @@ -10,7 +10,7 @@ } .fieldWrapper { - background-color: #fff !important; + background: #fff !important; border-radius: 3px !important; box-shadow: 0 1px 0 #ccc !important; margin-bottom: 8px !important; diff --git a/client/src/components/CardModal/Actions/ItemComment.module.css b/client/src/components/CardModal/Actions/ItemComment.module.css index e2ec035d..3e3a5568 100644 --- a/client/src/components/CardModal/Actions/ItemComment.module.css +++ b/client/src/components/CardModal/Actions/ItemComment.module.css @@ -22,7 +22,7 @@ } .text { - background-color: #fff; + background: #fff; border-radius: 0px 8px 8px; box-shadow: 0 1px 2px -1px rgba(9, 30, 66, 0.25), 0 0 0 1px rgba(9, 30, 66, 0.08); diff --git a/client/src/components/CardModal/AddAttachmentPopup.module.css b/client/src/components/CardModal/AddAttachmentPopup.module.css index 8f831bab..727afae9 100644 --- a/client/src/components/CardModal/AddAttachmentPopup.module.css +++ b/client/src/components/CardModal/AddAttachmentPopup.module.css @@ -1,5 +1,5 @@ .divider { - background-color: #eee; + background: #eee; border: 0; height: 1px; margin-bottom: 8px; diff --git a/client/src/components/CardModal/Attachments/Item.jsx b/client/src/components/CardModal/Attachments/Item.jsx index 90bedd3d..9ce4a7d9 100644 --- a/client/src/components/CardModal/Attachments/Item.jsx +++ b/client/src/components/CardModal/Attachments/Item.jsx @@ -60,7 +60,7 @@ const Item = React.memo(
{coverUrl ? ( diff --git a/client/src/components/CardModal/Attachments/Item.module.css b/client/src/components/CardModal/Attachments/Item.module.css index a1585676..45ca072f 100644 --- a/client/src/components/CardModal/Attachments/Item.module.css +++ b/client/src/components/CardModal/Attachments/Item.module.css @@ -13,7 +13,7 @@ } .button:hover { - background-color: rgba(9, 30, 66, 0.08) !important; + background: rgba(9, 30, 66, 0.08) !important; } .date { @@ -84,10 +84,7 @@ .thumbnail { border-radius: 3px; - background-color: rgba(9, 30, 66, 0.04); - background-position: center; - background-repeat: no-repeat; - background-size: cover; + background: rgba(9, 30, 66, 0.04); border-radius: 3px; height: 80px; left: 0; @@ -112,7 +109,7 @@ } .wrapper:hover .details { - background-color: rgba(9, 30, 66, 0.04); + background: rgba(9, 30, 66, 0.04); } .wrapper:hover .target { @@ -120,5 +117,5 @@ } .wrapperSubmitting { - background-color: rgba(9, 30, 66, 0.04); + background: rgba(9, 30, 66, 0.04); } diff --git a/client/src/components/CardModal/CardModal.module.css b/client/src/components/CardModal/CardModal.module.css index 839073a5..a64227a4 100644 --- a/client/src/components/CardModal/CardModal.module.css +++ b/client/src/components/CardModal/CardModal.module.css @@ -100,7 +100,7 @@ } .descriptionButton:hover { - background-color: rgba(9, 30, 66, 0.08); + background: rgba(9, 30, 66, 0.08); color: #092d42; } diff --git a/client/src/components/CardModal/Tasks/Item.module.css b/client/src/components/CardModal/Tasks/Item.module.css index 87aa13fc..7f67a506 100644 --- a/client/src/components/CardModal/Tasks/Item.module.css +++ b/client/src/components/CardModal/Tasks/Item.module.css @@ -13,7 +13,7 @@ } .button:hover { - background-color: rgba(9, 30, 66, 0.08) !important; + background: rgba(9, 30, 66, 0.08) !important; } .checkboxWrapper { @@ -30,7 +30,7 @@ } .content:hover { - background-color: rgba(9, 30, 66, 0.04); + background: rgba(9, 30, 66, 0.04); } .content:hover .target { diff --git a/client/src/components/CardModal/Tasks/Tasks.module.css b/client/src/components/CardModal/Tasks/Tasks.module.css index 0d71f82b..4009ddd9 100644 --- a/client/src/components/CardModal/Tasks/Tasks.module.css +++ b/client/src/components/CardModal/Tasks/Tasks.module.css @@ -20,7 +20,7 @@ } .taskButton:hover { - background-color: rgba(9, 30, 66, 0.04); + background: rgba(9, 30, 66, 0.04); color: #092d42; } diff --git a/client/src/components/Header/Header.module.css b/client/src/components/Header/Header.module.css index f52d890a..0c6feb53 100644 --- a/client/src/components/Header/Header.module.css +++ b/client/src/components/Header/Header.module.css @@ -3,7 +3,7 @@ } .item:hover { - background: hsla(0, 0%, 0%, 0.32) !important; + background: rgba(0, 0, 0, 0.32) !important; } .logo { @@ -52,7 +52,7 @@ } .wrapper { - background: hsla(0, 0%, 0%, 0.24); + background: rgba(0, 0, 0, 0.24); display: flex; flex: 0 0 auto; } diff --git a/client/src/components/List/AddCard.module.css b/client/src/components/List/AddCard.module.css index 0c8b5768..e9802a80 100644 --- a/client/src/components/List/AddCard.module.css +++ b/client/src/components/List/AddCard.module.css @@ -9,7 +9,7 @@ } .fieldWrapper { - background-color: #fff !important; + background: #fff !important; border-radius: 3px !important; box-shadow: 0 1px 0 #ccc !important; margin-bottom: 8px !important; diff --git a/client/src/components/List/List.module.css b/client/src/components/List/List.module.css index 50f3b185..12cd9d98 100644 --- a/client/src/components/List/List.module.css +++ b/client/src/components/List/List.module.css @@ -16,7 +16,7 @@ } .addCardButton:hover { - background-color: #c3cbd0 !important; + background: #c3cbd0 !important; color: #17394d !important; fill: #17394d !important; } @@ -70,7 +70,7 @@ } .headerButton:hover { - background-color: rgba(9, 30, 66, 0.13) !important; + background: rgba(9, 30, 66, 0.13) !important; color: #516b7a !important; } diff --git a/client/src/components/Login/Login.module.css b/client/src/components/Login/Login.module.css index d8b3692a..5dd60b75 100644 --- a/client/src/components/Login/Login.module.css +++ b/client/src/components/Login/Login.module.css @@ -1,6 +1,5 @@ .cover { - background: url("../../assets/images/cover.jpg") no-repeat center center; - background-size: cover; + background: url("../../assets/images/cover.jpg") center / cover !important; } .descriptionSubtitle { @@ -20,7 +19,7 @@ } .descriptionWrapperOverlay { - background-color: rgba(33, 33, 33, 0.5); + background: rgba(33, 33, 33, 0.5); bottom: 0; height: 100%; left: 0; @@ -37,7 +36,7 @@ } .fullHeight { - background-color: #fff; + background: #fff; height: 100%; } diff --git a/client/src/components/Project/ActionsPopup/ActionsPopup.jsx b/client/src/components/Project/ActionsPopup/ActionsPopup.jsx new file mode 100755 index 00000000..9ded6d84 --- /dev/null +++ b/client/src/components/Project/ActionsPopup/ActionsPopup.jsx @@ -0,0 +1,142 @@ +import React, { useCallback } from 'react'; +import PropTypes from 'prop-types'; +import { useTranslation } from 'react-i18next'; +import { Menu } from 'semantic-ui-react'; +import { withPopup } from '../../../lib/popup'; +import { Popup } from '../../../lib/custom-ui'; + +import { useSteps } from '../../../hooks'; +import EditNameStep from './EditNameStep'; +import EditBackgroundStep from './EditBackgroundStep'; +import DeleteStep from '../../DeleteStep'; + +import styles from './ActionsPopup.module.css'; + +const StepTypes = { + EDIT_NAME: 'EDIT_NAME', + EDIT_BACKGROUND: 'EDIT_BACKGROUND', + DELETE: 'DELETE', +}; + +const ActionsStep = React.memo( + ({ project, onUpdate, onBackgroundImageUpdate, onDelete, onClose }) => { + const [t] = useTranslation(); + const [step, openStep, handleBack] = useSteps(); + + const handleEditNameClick = useCallback(() => { + openStep(StepTypes.EDIT_NAME); + }, [openStep]); + + const handleEditBackgroundClick = useCallback(() => { + openStep(StepTypes.EDIT_BACKGROUND); + }, [openStep]); + + const handleDeleteClick = useCallback(() => { + openStep(StepTypes.DELETE); + }, [openStep]); + + const handleNameUpdate = useCallback( + (newName) => { + onUpdate({ + name: newName, + }); + }, + [onUpdate], + ); + + const handleBackgroundUpdate = useCallback( + (newBackground) => { + onUpdate({ + background: newBackground, + }); + }, + [onUpdate], + ); + + const handleBackgroundImageDelete = useCallback(() => { + onUpdate({ + background: null, + backgroundImage: null, + }); + }, [onUpdate]); + + if (step) { + if (step) { + switch (step.type) { + case StepTypes.EDIT_NAME: + return ( + + ); + case StepTypes.EDIT_BACKGROUND: + return ( + + ); + case StepTypes.DELETE: + return ( + + ); + default: + } + } + } + + return ( + <> + + {t('common.projectActions', { + context: 'title', + })} + + + + + {t('action.editTitle', { + context: 'title', + })} + + + {t('action.editBackground', { + context: 'title', + })} + + + {t('action.deleteProject', { + context: 'title', + })} + + + + + ); + }, +); + +ActionsStep.propTypes = { + project: PropTypes.object.isRequired, // eslint-disable-line react/forbid-prop-types + onUpdate: PropTypes.func.isRequired, + onBackgroundImageUpdate: PropTypes.func.isRequired, + onDelete: PropTypes.func.isRequired, + onClose: PropTypes.func.isRequired, +}; + +export default withPopup(ActionsStep); diff --git a/client/src/components/Project/ActionsPopup/ActionsPopup.module.css b/client/src/components/Project/ActionsPopup/ActionsPopup.module.css new file mode 100644 index 00000000..7e894e9e --- /dev/null +++ b/client/src/components/Project/ActionsPopup/ActionsPopup.module.css @@ -0,0 +1,9 @@ +.menu { + margin: -7px -12px -5px !important; + width: calc(100% + 24px) !important; +} + +.menuItem { + margin: 0 !important; + padding-left: 14px !important; +} diff --git a/client/src/components/Project/ActionsPopup/EditBackgroundStep.jsx b/client/src/components/Project/ActionsPopup/EditBackgroundStep.jsx new file mode 100644 index 00000000..9560fe63 --- /dev/null +++ b/client/src/components/Project/ActionsPopup/EditBackgroundStep.jsx @@ -0,0 +1,74 @@ +import React, { useCallback, useEffect, useRef } from 'react'; +import PropTypes from 'prop-types'; +import { useTranslation } from 'react-i18next'; +import { Button } from 'semantic-ui-react'; +import { FilePicker, Popup } from '../../../lib/custom-ui'; + +import styles from './EditBackgroundStep.module.css'; + +const EditBackgroundStep = React.memo( + ({ defaultValue, isImageUpdating, onImageUpdate, onImageDelete, onBack }) => { + const [t] = useTranslation(); + + const field = useRef(null); + + const handleFileSelect = useCallback( + (file) => { + onImageUpdate({ + file, + }); + }, + [onImageUpdate], + ); + + useEffect(() => { + field.current.focus(); + }, []); + + return ( + <> + + {t('common.editBackground', { + context: 'title', + })} + + +
+ +
+ {defaultValue && ( +