From fcdf991f934cd5f60787456a900a9f779373dd10 Mon Sep 17 00:00:00 2001 From: MarconLP <13001502+MarconLP@users.noreply.github.com> Date: Thu, 16 Feb 2023 13:33:41 +0100 Subject: [PATCH 01/18] only redirect to workspace home and toast board not found error when boardId is undefined --- src/modules/services/boardSlice.ts | 5 +++-- src/pages/workspace/View.js | 12 ++++++++---- src/pages/workspace/view/Board.js | 10 +++------- 3 files changed, 14 insertions(+), 13 deletions(-) diff --git a/src/modules/services/boardSlice.ts b/src/modules/services/boardSlice.ts index 781f183..2559a65 100644 --- a/src/modules/services/boardSlice.ts +++ b/src/modules/services/boardSlice.ts @@ -341,7 +341,7 @@ export const boardApi = api.injectEndpoints({ channel.unbind() }, providesTags: board => (board ? [board._id, 'Board'] : []), - async onQueryStarted(args, { dispatch, queryFulfilled }) { + async onQueryStarted(boardId, { queryFulfilled }) { try { await queryFulfilled localStorage.setItem( @@ -349,7 +349,8 @@ export const boardApi = api.injectEndpoints({ window.location.pathname ) } catch { - toast('Could not find that Board') + if (boardId === undefined) + toast('Could not find that Board') } } }), diff --git a/src/pages/workspace/View.js b/src/pages/workspace/View.js index 8f12c2e..9e6dd7f 100644 --- a/src/pages/workspace/View.js +++ b/src/pages/workspace/View.js @@ -1,7 +1,7 @@ import Board from './view/Board' import ViewTopMenu from './view/ViewTopMenu' import { memo, useCallback, useRef, useState } from 'react' -import { useParams } from 'react-router-dom' +import { Navigate, useParams } from 'react-router-dom' import { useFetchWorkspaceQuery } from '../../modules/services/workspaceSlice' import { LinearProgress } from '@mui/material' import { useFetchBoardQuery } from '../../modules/services/boardSlice.ts' @@ -17,7 +17,7 @@ function View({ level, view, sidebarOpen, toggleSideBar }) { ?.boards.find(x => x.name === board)?._id }, [params.space, params.board, workspace?.spaces, workspace]) const boardId = findBoardId() - const { isFetching } = useFetchBoardQuery(boardId) + const { isFetching, error } = useFetchBoardQuery(boardId) const viewContainerRef = useRef(null) const [bla, setBla] = useState(false) const setViewContainerRef = useCallback(node => { @@ -27,6 +27,10 @@ function View({ level, view, sidebarOpen, toggleSideBar }) { } }, []) + if (error && !isFetching && boardId === undefined) { + return + } + if (level === 'list') { if (view === 'list') return ( @@ -37,13 +41,13 @@ function View({ level, view, sidebarOpen, toggleSideBar }) { sideBarClosed={!sidebarOpen} />
- {isFetching && } + {(isFetching || error) && }
- {viewContainerRef?.current && ( + {!isFetching && !error && viewContainerRef?.current && ( { return true }, []) - if (error && !isFetching) { - return - } - return ( Date: Thu, 16 Feb 2023 13:37:07 +0100 Subject: [PATCH 02/18] only show board name in ViewTopMenu if loading has completed --- src/pages/workspace/view/ViewTopMenu.js | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/pages/workspace/view/ViewTopMenu.js b/src/pages/workspace/view/ViewTopMenu.js index 8288c04..225a869 100644 --- a/src/pages/workspace/view/ViewTopMenu.js +++ b/src/pages/workspace/view/ViewTopMenu.js @@ -13,7 +13,7 @@ import FilterMenu from './viewTopMenu/FilterMenu.tsx' import ListIcon from '../../../styles/assets/ListIcon.tsx' function ViewTopMenu({ boardId, sideBarClosed, toggleSideBar }) { - const { data: board } = useFetchBoardQuery(boardId) + const { data: board, isFetching, error } = useFetchBoardQuery(boardId) const [shareDialogOpen, toggleShareDialogOpen] = useToggleState(false) const [groupByOpen, setGroupByOpen] = useState(null) const [search, setSearch] = useAtom(searchAtom) @@ -56,7 +56,11 @@ function ViewTopMenu({ boardId, sideBarClosed, toggleSideBar }) {
-

{board?.name || '...'}

+

+ {!isFetching && !error && board?.name + ? board?.name + : '...'} +

{/*
*/} {/* {[*/} From 667c5b1fe351937224bb9311a01d3151b07c8ed2 Mon Sep 17 00:00:00 2001 From: MarconLP <13001502+MarconLP@users.noreply.github.com> Date: Thu, 16 Feb 2023 15:27:34 +0100 Subject: [PATCH 03/18] fix unnecessary setFilters mutation when filters are unchanged --- src/pages/workspace/view/viewTopMenu/FilterMenu.tsx | 1 + 1 file changed, 1 insertion(+) diff --git a/src/pages/workspace/view/viewTopMenu/FilterMenu.tsx b/src/pages/workspace/view/viewTopMenu/FilterMenu.tsx index 8c7b270..ef3305f 100644 --- a/src/pages/workspace/view/viewTopMenu/FilterMenu.tsx +++ b/src/pages/workspace/view/viewTopMenu/FilterMenu.tsx @@ -87,6 +87,7 @@ export default function FilterMenu({ boardId }) { }, [board.filters]) const handleClose = open => { + if (filters === board.filters) return if (!open) { setFilters({ boardId, From 1c86d6fdfc2dee9b86a8b7126f50351e8dabd816 Mon Sep 17 00:00:00 2001 From: MarconLP <13001502+MarconLP@users.noreply.github.com> Date: Fri, 17 Feb 2023 09:59:31 +0100 Subject: [PATCH 04/18] automatically close sidebar when changing the current board on mobile devices --- src/pages/workspace/SideBar.js | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/pages/workspace/SideBar.js b/src/pages/workspace/SideBar.js index 329e861..15e6a0d 100644 --- a/src/pages/workspace/SideBar.js +++ b/src/pages/workspace/SideBar.js @@ -326,6 +326,13 @@ function SideBar(props) { > {provided => ( { + if ( + window.innerWidth <= + 576 + ) + props.toggleSideBar() + }} {...provided.draggableProps} ref={ provided.innerRef From 590ccab92f17c8c503cc65f6dda15c616d46ce76 Mon Sep 17 00:00:00 2001 From: MarconLP <13001502+MarconLP@users.noreply.github.com> Date: Sat, 18 Feb 2023 14:42:57 +0100 Subject: [PATCH 05/18] add views --- src/modules/services/boardSlice.ts | 4 +- src/pages/Workspace.js | 13 ++- src/pages/workspace/View.js | 25 +++-- src/pages/workspace/topMenu/GroupByPopover.js | 6 +- src/pages/workspace/topMenu/ShareDialog.js | 10 +- src/pages/workspace/view/Board.js | 4 +- src/pages/workspace/view/ViewTopMenu.js | 100 ++++++++++-------- src/pages/workspace/view/board/BoardView.js | 13 ++- .../view/board/taskGroup/TaskModal.tsx | 6 +- .../board/taskGroup/taskModal/Activity.js | 2 +- .../workspace/view/viewTopMenu/FilterMenu.tsx | 12 +-- 11 files changed, 113 insertions(+), 82 deletions(-) diff --git a/src/modules/services/boardSlice.ts b/src/modules/services/boardSlice.ts index 2559a65..9d6c08c 100644 --- a/src/modules/services/boardSlice.ts +++ b/src/modules/services/boardSlice.ts @@ -210,7 +210,7 @@ const setSharing = ({ board, share }) => { export const boardApi = api.injectEndpoints({ endpoints: builder => ({ fetchBoard: builder.query({ - query: boardId => `board/${boardId}`, + query: ({ boardId, viewId = '' }) => `board/${boardId}/${viewId}`, transformResponse: (response: any, meta, arg) => { return { ...response, @@ -218,7 +218,7 @@ export const boardApi = api.injectEndpoints({ } }, async onCacheEntryAdded( - boardId, + { boardId }, { updateCachedData, cacheDataLoaded, cacheEntryRemoved } ) { let channel diff --git a/src/pages/Workspace.js b/src/pages/Workspace.js index 0605c7a..0a0f401 100644 --- a/src/pages/Workspace.js +++ b/src/pages/Workspace.js @@ -54,7 +54,18 @@ function Workspace(props) { } /> + } + /> + x.name === board)?._id }, [params.space, params.board, workspace?.spaces, workspace]) const boardId = findBoardId() - const { isFetching, error } = useFetchBoardQuery(boardId) + const { isFetching, error } = useFetchBoardQuery({ boardId }) const viewContainerRef = useRef(null) const [bla, setBla] = useState(false) const setViewContainerRef = useCallback(node => { @@ -41,20 +41,25 @@ function View({ level, view, sidebarOpen, toggleSideBar }) { sideBarClosed={!sidebarOpen} />
- {(isFetching || error) && } + {(!params.view || isFetching || error) && ( + + )}
- {!isFetching && !error && viewContainerRef?.current && ( - - )} + {params.view && + !isFetching && + !error && + viewContainerRef?.current && ( + + )}
) diff --git a/src/pages/workspace/topMenu/GroupByPopover.js b/src/pages/workspace/topMenu/GroupByPopover.js index 2ac3ee6..faa9584 100644 --- a/src/pages/workspace/topMenu/GroupByPopover.js +++ b/src/pages/workspace/topMenu/GroupByPopover.js @@ -5,7 +5,7 @@ import { FontAwesomeIcon } from '@fortawesome/react-fontawesome' import { solid } from '@fortawesome/fontawesome-svg-core/import.macro' import React from 'react' -function GroupByPopover({ board, groupByOpen, setGroupByOpen }) { +function GroupByPopover({ view, board, groupByOpen, setGroupByOpen }) { const [setGroupBy] = useSetGroupByMutation() const handleClose = () => { @@ -48,7 +48,7 @@ function GroupByPopover({ board, groupByOpen, setGroupByOpen }) { } > {attribute.name} - {board?.groupBy === attribute._id && ( + {view?.groupBy === attribute._id && ( )}
@@ -59,7 +59,7 @@ function GroupByPopover({ board, groupByOpen, setGroupByOpen }) { onClick={() => handleGroupByChange('none')} > None - {(!board?.groupBy || board?.groupBy === 'none') && ( + {(!view?.groupBy || view?.groupBy === 'none') && ( )} diff --git a/src/pages/workspace/topMenu/ShareDialog.js b/src/pages/workspace/topMenu/ShareDialog.js index 29b01b1..5d9611b 100644 --- a/src/pages/workspace/topMenu/ShareDialog.js +++ b/src/pages/workspace/topMenu/ShareDialog.js @@ -6,12 +6,12 @@ import { FontAwesomeIcon } from '@fortawesome/react-fontawesome' import { solid } from '@fortawesome/fontawesome-svg-core/import.macro' import { useSetSharingMutation } from '../../../modules/services/boardSlice.ts' -function ShareDialog({ handleClose, open, board }) { +function ShareDialog({ handleClose, open, boardId, view }) { const [setSharing] = useSetSharingMutation() const toggleShare = (e, share) => { setSharing({ - boardId: board._id, + boardId, share }) } @@ -30,14 +30,14 @@ function ShareDialog({ handleClose, open, board }) {

Link sharing

- {board.sharing && ( + {view.sharing && (
@@ -46,7 +46,7 @@ function ShareDialog({ handleClose, open, board }) { diff --git a/src/pages/workspace/view/Board.js b/src/pages/workspace/view/Board.js index bf285bc..99aa5d8 100644 --- a/src/pages/workspace/view/Board.js +++ b/src/pages/workspace/view/Board.js @@ -8,7 +8,8 @@ import { forwardRef, useCallback } from 'react' function Board({ boardId }, viewContainerRef) { const params = useParams() const { data: workspace } = useFetchWorkspaceQuery(params.workspace) - const { data: board } = useFetchBoardQuery(boardId) + const { data: board } = useFetchBoardQuery({ boardId, viewId: params.view }) + const view = board?.views.find(x => x.id === params.view) const hasPerms = useCallback(permission => { return true @@ -19,6 +20,7 @@ function Board({ boardId }, viewContainerRef) { ref={viewContainerRef} members={workspace.members} board={board} + view={view} hasPerms={hasPerms} /> ) diff --git a/src/pages/workspace/view/ViewTopMenu.js b/src/pages/workspace/view/ViewTopMenu.js index 225a869..8fd6a94 100644 --- a/src/pages/workspace/view/ViewTopMenu.js +++ b/src/pages/workspace/view/ViewTopMenu.js @@ -8,29 +8,43 @@ import { solid } from '@fortawesome/fontawesome-svg-core/import.macro' import { useFetchBoardQuery } from '../../../modules/services/boardSlice.ts' import { useAtom } from 'jotai' import { searchAtom } from '../../../utils/atoms.ts' -import { useParams } from 'react-router-dom' +import { useNavigate, useParams } from 'react-router-dom' import FilterMenu from './viewTopMenu/FilterMenu.tsx' import ListIcon from '../../../styles/assets/ListIcon.tsx' function ViewTopMenu({ boardId, sideBarClosed, toggleSideBar }) { - const { data: board, isFetching, error } = useFetchBoardQuery(boardId) + const { data: board, isFetching, error } = useFetchBoardQuery({ boardId }) const [shareDialogOpen, toggleShareDialogOpen] = useToggleState(false) const [groupByOpen, setGroupByOpen] = useState(null) + const navigate = useNavigate() const [search, setSearch] = useAtom(searchAtom) const params = useParams() + const view = board?.views.find(x => x.id === params?.view) useEffect(() => { setSearch('') }, [params.board]) + useEffect(() => { + if (!params.view && board) { + const { workspace, space, board: boardId } = params + navigate(`/${workspace}/${space}/${boardId}/${board.views[0].id}`) + } + }, [params.board, board]) + const handleChange = e => { setSearch(e.target.value) } + const handleViewClick = id => { + const { workspace, space, board } = params + navigate(`/${workspace}/${space}/${board}/${id}`) + } + const groupBy = () => { - if (!board?.groupBy || board.groupBy === 'none') return false + if (!view?.groupBy || view.groupBy === 'none') return false return board.attributes.find( - attribute => attribute._id === board.groupBy + attribute => attribute._id === view.groupBy ).name } @@ -62,46 +76,34 @@ function ViewTopMenu({ boardId, sideBarClosed, toggleSideBar }) { : '...'}
- {/*
*/} - {/* {[*/} - {/* {*/} - {/* name: 'List',*/} - {/* icon: ,*/} - {/* active: true*/} - {/* },*/} - {/* {*/} - {/* name: 'Bugs',*/} - {/* icon: ,*/} - {/* active: false*/} - {/* }*/} - {/* ].map(({ name, icon, active }) => (*/} - {/* */} - {/* */} - {/* {icon}*/} - {/* */} - {/* {name}*/} - {/* */} - {/*
*/} - {/*
*/} - {/* ))}*/} - {/**/} +
+ {board?.views.map(({ name, id }) => ( +
handleViewClick(id)} + className="subpixel-antialiased flex justify-center items-center h-[60px] box-border cursor-pointer border-y-[3px] border-y-[white] border-solid" + > +
+ + + {name} + +
+
+ ))} +
- {board && ( + {view && ( @@ -142,7 +145,9 @@ function ViewTopMenu({ boardId, sideBarClosed, toggleSideBar }) { {/**/}
- {board && } + {board && view && ( + + )}
{ if (!board) return - if (!board.groupBy || board.groupBy === 'none') { + const view = board?.views.find(x => x.id === params?.view) + if (!view.groupBy || view.groupBy === 'none') { const fuse = new Fuse(board.tasks, { keys: ['name'], shouldSort: false @@ -59,7 +62,7 @@ function BoardView({ board, members, hasPerms }, viewContainerRef) { } const labels = _.cloneDeep( - board.attributes.find(attribute => attribute._id === board.groupBy) + board.attributes.find(attribute => attribute._id === view.groupBy) .labels ) @@ -72,7 +75,7 @@ function BoardView({ board, members, hasPerms }, viewContainerRef) { buildTree(board.tasks).map(task => { const value = task.options.find( - option => option.column === board.groupBy + option => option.column === view.groupBy )?.value const label = labels.find(label => label._id === value) if (value && label) label.tasks.push(task) @@ -119,7 +122,9 @@ function BoardView({ board, members, hasPerms }, viewContainerRef) { const sourceIndex = board.tasks.findIndex( x => x._id.toString() === result.draggableId ) - if (!(!board.groupBy || board.groupBy === 'none')) { + + const view = board?.views.find(x => x.id === params?.view) + if (!(!view.groupBy || view.groupBy === 'none')) { const destinationTask = groupedTasks.find( group => group._id === result.destination.droppableId ).tasks[result.destination.index] diff --git a/src/pages/workspace/view/board/taskGroup/TaskModal.tsx b/src/pages/workspace/view/board/taskGroup/TaskModal.tsx index 9881148..c68a580 100644 --- a/src/pages/workspace/view/board/taskGroup/TaskModal.tsx +++ b/src/pages/workspace/view/board/taskGroup/TaskModal.tsx @@ -31,7 +31,7 @@ interface Props { function TaskModal({ task, boardId, hasPerms, members }: Props) { const navigate = useNavigate() const params = useParams() - const { data: board } = useFetchBoardQuery(boardId) + const { data: board } = useFetchBoardQuery({ boardId }) const { data: user } = useFetchUserQuery() const [name, changeName] = useInputState(task.name) const [anchor, setAnchor] = useState(null) @@ -44,7 +44,9 @@ function TaskModal({ task, boardId, hasPerms, members }: Props) { if (document.activeElement instanceof HTMLElement) { document.activeElement.blur() } - navigate(`/${params.workspace}/${params.space}/${params.board}`) + navigate( + `/${params.workspace}/${params.space}/${params.board}/${params.view}` + ) } useEffect(() => { diff --git a/src/pages/workspace/view/board/taskGroup/taskModal/Activity.js b/src/pages/workspace/view/board/taskGroup/taskModal/Activity.js index bcd2a33..a5d6b15 100644 --- a/src/pages/workspace/view/board/taskGroup/taskModal/Activity.js +++ b/src/pages/workspace/view/board/taskGroup/taskModal/Activity.js @@ -4,7 +4,7 @@ import { diffWords } from 'diff' import { useFetchBoardQuery } from '../../../../../../modules/services/boardSlice.ts' function Activity({ timestamp, activity, boardId }) { - const { data: board } = useFetchBoardQuery(boardId) + const { data: board } = useFetchBoardQuery({ boardId }) const getDiff = () => { const groups = diffWords(activity.change.from, activity.change.to) diff --git a/src/pages/workspace/view/viewTopMenu/FilterMenu.tsx b/src/pages/workspace/view/viewTopMenu/FilterMenu.tsx index ef3305f..04330d6 100644 --- a/src/pages/workspace/view/viewTopMenu/FilterMenu.tsx +++ b/src/pages/workspace/view/viewTopMenu/FilterMenu.tsx @@ -15,8 +15,8 @@ const operations = { text: ['Contains', 'Does not contain', 'Is set', 'Is not set'] } -export default function FilterMenu({ boardId }) { - const { data: board } = useFetchBoardQuery(boardId) +export default function FilterMenu({ boardId, view }) { + const { data: board } = useFetchBoardQuery({ boardId }) const [setFilters] = useSetFiltersMutation() const [filters, setFiltersState] = useState([]) @@ -81,13 +81,13 @@ export default function FilterMenu({ boardId }) { } useEffect(() => { - if (board?.filters) { - setFiltersState(board.filters) + if (view?.filters) { + setFiltersState(view.filters) } - }, [board.filters]) + }, [view.filters]) const handleClose = open => { - if (filters === board.filters) return + if (filters === view.filters) return if (!open) { setFilters({ boardId, From a1fa3464c603fa6a17ccdbb4eabe2b31fc4e0386 Mon Sep 17 00:00:00 2001 From: MarconLP <13001502+MarconLP@users.noreply.github.com> Date: Sat, 18 Feb 2023 16:43:11 +0100 Subject: [PATCH 06/18] add view mutation routes --- src/modules/services/boardSlice.ts | 30 +++++++++++-------- src/pages/workspace/topMenu/GroupByPopover.js | 2 +- src/pages/workspace/topMenu/ShareDialog.js | 1 + .../workspace/view/viewTopMenu/FilterMenu.tsx | 2 +- 4 files changed, 21 insertions(+), 14 deletions(-) diff --git a/src/modules/services/boardSlice.ts b/src/modules/services/boardSlice.ts index 9d6c08c..055a553 100644 --- a/src/modules/services/boardSlice.ts +++ b/src/modules/services/boardSlice.ts @@ -5,8 +5,11 @@ import pusher from '../../pusher/pusher.ts' import handleQueryError from '../../utils/handleQueryError' import { flatten, removeChildrenOf } from '../../utils/taskUtils.ts' -const setGroupBy = ({ board, groupBy }) => { - board.groupBy = groupBy +const setGroupBy = ({ board, groupBy, viewId }) => { + console.log('bla') + // console.log('viewId') + // console.log(viewId) + // board.views.find(x => x._id === viewId).groupBy = groupBy } const addTask = ({ board, newTaskName, taskGroupId, _id, author, level }) => { const task = { @@ -355,20 +358,23 @@ export const boardApi = api.injectEndpoints({ } }), setGroupBy: builder.mutation({ - query: ({ boardId, groupBy }) => ({ - url: `board/${boardId}`, - method: 'PATCH', + query: ({ viewId, groupBy }) => ({ + url: `view/${viewId}/groupBy`, + method: 'PUT', body: { groupBy } }), async onQueryStarted( - { boardId, groupBy }, + { boardId, groupBy, viewId }, { dispatch, queryFulfilled } ) { + console.log({ boardId, groupBy, viewId }) const patchResult = dispatch( boardApi.util.updateQueryData( 'fetchBoard', boardId, - board => setGroupBy({ board, groupBy }) + board => { + setGroupBy({ board, groupBy, viewId }) + } ) ) try { @@ -914,9 +920,9 @@ export const boardApi = api.injectEndpoints({ } }), setSharing: builder.mutation({ - query: ({ boardId, share }) => ({ - url: `board/share/${boardId}`, - method: 'PATCH', + query: ({ viewId, share }) => ({ + url: `view/${viewId}/setSharing`, + method: 'PUT', body: { share } }), async onQueryStarted( @@ -963,8 +969,8 @@ export const boardApi = api.injectEndpoints({ } }), setFilters: builder.mutation({ - query: ({ boardId, filters }) => ({ - url: `board/${boardId}`, + query: ({ viewId, filters }) => ({ + url: `view/${viewId}/setFilters`, method: 'PUT', body: { filters } }), diff --git a/src/pages/workspace/topMenu/GroupByPopover.js b/src/pages/workspace/topMenu/GroupByPopover.js index faa9584..f72f425 100644 --- a/src/pages/workspace/topMenu/GroupByPopover.js +++ b/src/pages/workspace/topMenu/GroupByPopover.js @@ -13,7 +13,7 @@ function GroupByPopover({ view, board, groupByOpen, setGroupByOpen }) { } const handleGroupByChange = _id => { - setGroupBy({ boardId: board._id, groupBy: _id }) + setGroupBy({ boardId: board._id, viewId: view._id, groupBy: _id }) handleClose() } diff --git a/src/pages/workspace/topMenu/ShareDialog.js b/src/pages/workspace/topMenu/ShareDialog.js index 5d9611b..1191151 100644 --- a/src/pages/workspace/topMenu/ShareDialog.js +++ b/src/pages/workspace/topMenu/ShareDialog.js @@ -11,6 +11,7 @@ function ShareDialog({ handleClose, open, boardId, view }) { const toggleShare = (e, share) => { setSharing({ + viewId: view._id, boardId, share }) diff --git a/src/pages/workspace/view/viewTopMenu/FilterMenu.tsx b/src/pages/workspace/view/viewTopMenu/FilterMenu.tsx index 04330d6..e9ddb50 100644 --- a/src/pages/workspace/view/viewTopMenu/FilterMenu.tsx +++ b/src/pages/workspace/view/viewTopMenu/FilterMenu.tsx @@ -90,7 +90,7 @@ export default function FilterMenu({ boardId, view }) { if (filters === view.filters) return if (!open) { setFilters({ - boardId, + viewId: view._id, filters }) } From 07a3abdd268ba85701a852e889010533f68a8d21 Mon Sep 17 00:00:00 2001 From: MarconLP <13001502+MarconLP@users.noreply.github.com> Date: Sat, 18 Feb 2023 20:14:02 +0100 Subject: [PATCH 07/18] implement menu to add new views --- src/modules/services/boardSlice.ts | 25 +++++++- src/pages/workspace/view/ViewTopMenu.js | 2 + .../view/viewTopMenu/NewViewMenu.tsx | 64 +++++++++++++++++++ 3 files changed, 90 insertions(+), 1 deletion(-) create mode 100644 src/pages/workspace/view/viewTopMenu/NewViewMenu.tsx diff --git a/src/modules/services/boardSlice.ts b/src/modules/services/boardSlice.ts index 055a553..b66f33a 100644 --- a/src/modules/services/boardSlice.ts +++ b/src/modules/services/boardSlice.ts @@ -968,6 +968,28 @@ export const boardApi = api.injectEndpoints({ } } }), + addView: builder.mutation({ + query: ({ boardId, type }) => ({ + url: `view/${boardId}/addView`, + method: 'POST', + body: { type } + }), + invalidatesTags: (result, error, arg) => [arg.boardId], + async onQueryStarted({ boardId }, { dispatch, queryFulfilled }) { + const patchResult = dispatch( + boardApi.util.updateQueryData( + 'fetchBoard', + boardId, + board => {} + ) + ) + try { + await queryFulfilled + } catch (err) { + if (handleQueryError({ err })) patchResult.undo() + } + } + }), setFilters: builder.mutation({ query: ({ viewId, filters }) => ({ url: `view/${viewId}/setFilters`, @@ -1002,5 +1024,6 @@ export const { useRemoveWatcherMutation, useSetSharingMutation, useAddSubtaskMutation, - useSetFiltersMutation + useSetFiltersMutation, + useAddViewMutation } = boardApi diff --git a/src/pages/workspace/view/ViewTopMenu.js b/src/pages/workspace/view/ViewTopMenu.js index 8fd6a94..bd33340 100644 --- a/src/pages/workspace/view/ViewTopMenu.js +++ b/src/pages/workspace/view/ViewTopMenu.js @@ -11,6 +11,7 @@ import { searchAtom } from '../../../utils/atoms.ts' import { useNavigate, useParams } from 'react-router-dom' import FilterMenu from './viewTopMenu/FilterMenu.tsx' import ListIcon from '../../../styles/assets/ListIcon.tsx' +import NewViewMenu from './viewTopMenu/NewViewMenu.tsx' function ViewTopMenu({ boardId, sideBarClosed, toggleSideBar }) { const { data: board, isFetching, error } = useFetchBoardQuery({ boardId }) @@ -103,6 +104,7 @@ function ViewTopMenu({ boardId, sideBarClosed, toggleSideBar }) {
))} +
diff --git a/src/pages/workspace/view/viewTopMenu/NewViewMenu.tsx b/src/pages/workspace/view/viewTopMenu/NewViewMenu.tsx new file mode 100644 index 0000000..d1780bd --- /dev/null +++ b/src/pages/workspace/view/viewTopMenu/NewViewMenu.tsx @@ -0,0 +1,64 @@ +import { Menu, Transition } from '@headlessui/react' +import { Fragment } from 'react' +import ListIcon from '../../../../styles/assets/ListIcon.tsx' +import { useAddViewMutation } from '../../../../modules/services/boardSlice.ts' +import * as React from 'react' + +interface NewViewMenuProps { + boardId: string +} + +function NewViewMenu({ boardId }: NewViewMenuProps) { + const [addView] = useAddViewMutation() + + const handleAddView = (type: string): void => { + addView({ + boardId, + type + }) + } + + return ( + +
+ +
+
+ + + Add view + +
+
+
+
+ + +
+ handleAddView('list')}> + {({ active }) => ( + + )} + +
+
+
+
+ ) +} + +export default NewViewMenu From 5e47697516955fb3aff07fe4bb9a7deaaeec404d Mon Sep 17 00:00:00 2001 From: MarconLP <13001502+MarconLP@users.noreply.github.com> Date: Sat, 18 Feb 2023 20:57:05 +0100 Subject: [PATCH 08/18] add menu to viewItem and ability to delete views --- src/modules/services/boardSlice.ts | 25 +++- src/pages/workspace/view/ViewTopMenu.js | 36 ++--- .../view/viewTopMenu/ViewNavItem.tsx | 125 ++++++++++++++++++ 3 files changed, 159 insertions(+), 27 deletions(-) create mode 100644 src/pages/workspace/view/viewTopMenu/ViewNavItem.tsx diff --git a/src/modules/services/boardSlice.ts b/src/modules/services/boardSlice.ts index b66f33a..a480eee 100644 --- a/src/modules/services/boardSlice.ts +++ b/src/modules/services/boardSlice.ts @@ -990,6 +990,28 @@ export const boardApi = api.injectEndpoints({ } } }), + deleteView: builder.mutation({ + query: ({ boardId, viewId }) => ({ + url: `view/${boardId}/deleteView`, + method: 'POST', + body: { viewId } + }), + invalidatesTags: (result, error, arg) => [arg.boardId], + async onQueryStarted({ boardId }, { dispatch, queryFulfilled }) { + const patchResult = dispatch( + boardApi.util.updateQueryData( + 'fetchBoard', + boardId, + board => {} + ) + ) + try { + await queryFulfilled + } catch (err) { + if (handleQueryError({ err })) patchResult.undo() + } + } + }), setFilters: builder.mutation({ query: ({ viewId, filters }) => ({ url: `view/${viewId}/setFilters`, @@ -1025,5 +1047,6 @@ export const { useSetSharingMutation, useAddSubtaskMutation, useSetFiltersMutation, - useAddViewMutation + useAddViewMutation, + useDeleteViewMutation } = boardApi diff --git a/src/pages/workspace/view/ViewTopMenu.js b/src/pages/workspace/view/ViewTopMenu.js index bd33340..71195f3 100644 --- a/src/pages/workspace/view/ViewTopMenu.js +++ b/src/pages/workspace/view/ViewTopMenu.js @@ -10,8 +10,8 @@ import { useAtom } from 'jotai' import { searchAtom } from '../../../utils/atoms.ts' import { useNavigate, useParams } from 'react-router-dom' import FilterMenu from './viewTopMenu/FilterMenu.tsx' -import ListIcon from '../../../styles/assets/ListIcon.tsx' import NewViewMenu from './viewTopMenu/NewViewMenu.tsx' +import ViewNavItem from './viewTopMenu/ViewNavItem.tsx' function ViewTopMenu({ boardId, sideBarClosed, toggleSideBar }) { const { data: board, isFetching, error } = useFetchBoardQuery({ boardId }) @@ -78,31 +78,15 @@ function ViewTopMenu({ boardId, sideBarClosed, toggleSideBar }) {
- {board?.views.map(({ name, id }) => ( -
handleViewClick(id)} - className="subpixel-antialiased flex justify-center items-center h-[60px] box-border cursor-pointer border-y-[3px] border-y-[white] border-solid" - > -
- - - {name} - -
-
+ {board?.views.map(({ name, id, _id }) => ( + ))}
diff --git a/src/pages/workspace/view/viewTopMenu/ViewNavItem.tsx b/src/pages/workspace/view/viewTopMenu/ViewNavItem.tsx new file mode 100644 index 0000000..4b81a17 --- /dev/null +++ b/src/pages/workspace/view/viewTopMenu/ViewNavItem.tsx @@ -0,0 +1,125 @@ +import { FontAwesomeIcon } from '@fortawesome/react-fontawesome' +import ListIcon from '../../../../styles/assets/ListIcon.tsx' +import { regular, solid } from '@fortawesome/fontawesome-svg-core/import.macro' +import { Menu, Transition } from '@headlessui/react' +import { Fragment } from 'react' +import * as React from 'react' +import { useDeleteViewMutation } from '../../../../modules/services/boardSlice.ts' + +interface Props { + boardId: string + _id: string + id: string + name: string + handleViewClick: (id: string) => void + active: boolean +} + +export default function ({ + boardId, + _id, + active, + name, + id, + handleViewClick +}: Props) { + const [deleteView] = useDeleteViewMutation() + + const handleRename = () => {} + + const handleDelete = () => { + deleteView({ + boardId, + viewId: _id + }) + } + + return ( +
handleViewClick(id)} + className="subpixel-antialiased flex justify-center items-center h-[60px] box-border cursor-pointer border-y-[3px] border-y-[white] border-solid" + > +
+ + + {name} + + {active && ( + +
+ +
+ +
+
+
+ + +
+ + {({ active }) => ( +
+
+
+ +
+

+ Rename +

+
+
+ )} +
+ + {({ active }) => ( +
+
+
+ +
+

+ Delete +

+
+
+ )} +
+
+
+
+
+ )} +
+
+ ) +} From 9f6c031bba92def6ca0ce34780e4c54bad098579 Mon Sep 17 00:00:00 2001 From: MarconLP <13001502+MarconLP@users.noreply.github.com> Date: Sat, 18 Feb 2023 21:40:21 +0100 Subject: [PATCH 09/18] add ability to rename view --- src/modules/services/boardSlice.ts | 27 +++++++- src/pages/workspace/view/ViewTopMenu.js | 1 + .../view/viewTopMenu/ViewNavItem.tsx | 62 +++++++++++++++---- 3 files changed, 77 insertions(+), 13 deletions(-) diff --git a/src/modules/services/boardSlice.ts b/src/modules/services/boardSlice.ts index a480eee..f499e35 100644 --- a/src/modules/services/boardSlice.ts +++ b/src/modules/services/boardSlice.ts @@ -993,7 +993,7 @@ export const boardApi = api.injectEndpoints({ deleteView: builder.mutation({ query: ({ boardId, viewId }) => ({ url: `view/${boardId}/deleteView`, - method: 'POST', + method: 'DELETE', body: { viewId } }), invalidatesTags: (result, error, arg) => [arg.boardId], @@ -1012,6 +1012,28 @@ export const boardApi = api.injectEndpoints({ } } }), + renameView: builder.mutation({ + query: ({ viewId, name }) => ({ + url: `view/${viewId}/renameView`, + method: 'PUT', + body: { name } + }), + invalidatesTags: (result, error, arg) => [arg.boardId], + async onQueryStarted({ boardId }, { dispatch, queryFulfilled }) { + const patchResult = dispatch( + boardApi.util.updateQueryData( + 'fetchBoard', + boardId, + board => {} + ) + ) + try { + await queryFulfilled + } catch (err) { + if (handleQueryError({ err })) patchResult.undo() + } + } + }), setFilters: builder.mutation({ query: ({ viewId, filters }) => ({ url: `view/${viewId}/setFilters`, @@ -1048,5 +1070,6 @@ export const { useAddSubtaskMutation, useSetFiltersMutation, useAddViewMutation, - useDeleteViewMutation + useDeleteViewMutation, + useRenameViewMutation } = boardApi diff --git a/src/pages/workspace/view/ViewTopMenu.js b/src/pages/workspace/view/ViewTopMenu.js index 71195f3..d58acf5 100644 --- a/src/pages/workspace/view/ViewTopMenu.js +++ b/src/pages/workspace/view/ViewTopMenu.js @@ -80,6 +80,7 @@ function ViewTopMenu({ boardId, sideBarClosed, toggleSideBar }) {
{board?.views.map(({ name, id, _id }) => ( (false) + const [newName, changeNewName, , setNewName] = useInputState(name) + const [width, setWidth] = useState(100) + const ref = useRef() - const handleRename = () => {} + const handleRenameClick = () => { + setWidth(ref.current.offsetWidth) + setRenaming(true) + } + + const handleRename = () => { + renameView({ + viewId: _id, + name: newName + }) + setRenaming(false) + } const handleDelete = () => { deleteView({ @@ -34,6 +54,10 @@ export default function ({ }) } + useEffect(() => { + setNewName(name) + }, [name]) + return (
- - {name} - + {renaming ? ( + { + if (['Enter', 'Escape'].includes(e.key)) + e.currentTarget.blur() + }} + style={{ width: `${width}px` }} + onBlur={handleRename} + className="outline-none text-sm leading-[14px] font-medium text-[#4169e1]" + value={newName} + onChange={changeNewName} + autoFocus + /> + ) : ( + + {name} + + )} {active && (
@@ -77,7 +117,7 @@ export default function ({ {({ active }) => (
From d0e7be50f361f0ca2e5a26760bdeb3d29a94a25d Mon Sep 17 00:00:00 2001 From: MarconLP <13001502+MarconLP@users.noreply.github.com> Date: Sun, 19 Feb 2023 10:39:38 +0100 Subject: [PATCH 10/18] replace all useFetchBoardQueries to include viewId --- src/modules/services/boardSlice.ts | 33 +------------------ src/pages/workspace/SideBar.js | 5 ++- src/pages/workspace/View.js | 5 ++- src/pages/workspace/view/ViewTopMenu.js | 32 ++++++++++-------- .../view/board/taskGroup/TaskModal.tsx | 2 +- .../board/taskGroup/taskModal/Activity.js | 4 ++- .../workspace/view/viewTopMenu/FilterMenu.tsx | 8 ++++- 7 files changed, 39 insertions(+), 50 deletions(-) diff --git a/src/modules/services/boardSlice.ts b/src/modules/services/boardSlice.ts index f499e35..a40bf2a 100644 --- a/src/modules/services/boardSlice.ts +++ b/src/modules/services/boardSlice.ts @@ -6,10 +6,7 @@ import handleQueryError from '../../utils/handleQueryError' import { flatten, removeChildrenOf } from '../../utils/taskUtils.ts' const setGroupBy = ({ board, groupBy, viewId }) => { - console.log('bla') - // console.log('viewId') - // console.log(viewId) - // board.views.find(x => x._id === viewId).groupBy = groupBy + board.views.find(x => x._id === viewId).groupBy = groupBy } const addTask = ({ board, newTaskName, taskGroupId, _id, author, level }) => { const task = { @@ -367,7 +364,6 @@ export const boardApi = api.injectEndpoints({ { boardId, groupBy, viewId }, { dispatch, queryFulfilled } ) { - console.log({ boardId, groupBy, viewId }) const patchResult = dispatch( boardApi.util.updateQueryData( 'fetchBoard', @@ -420,33 +416,6 @@ export const boardApi = api.injectEndpoints({ } } }), - editTaskField: builder.mutation({ - query: ({ boardId, taskId, type, value }) => ({ - url: `task/${boardId}/${taskId}`, - method: 'PATCH', - body: { - type, - value - } - }), - async onQueryStarted( - { taskId, type, value, boardId }, - { dispatch, queryFulfilled } - ) { - const patchResult = dispatch( - boardApi.util.updateQueryData( - 'fetchBoard', - boardId, - board => editTaskField({ board, taskId, type, value }) - ) - ) - try { - await queryFulfilled - } catch (err) { - if (handleQueryError({ err })) patchResult.undo() - } - } - }), deleteTask: builder.mutation({ query: ({ boardId, taskId }) => ({ url: `task/${boardId}/${taskId}`, diff --git a/src/pages/workspace/SideBar.js b/src/pages/workspace/SideBar.js index 15e6a0d..1a0a5c5 100644 --- a/src/pages/workspace/SideBar.js +++ b/src/pages/workspace/SideBar.js @@ -354,7 +354,10 @@ function SideBar(props) { )}/${board.name.replaceAll( ' ', '-' - )}`} + )}/${ + board.views[0] + .id + }`} >
{board._id === diff --git a/src/pages/workspace/View.js b/src/pages/workspace/View.js index 899638b..76d90ea 100644 --- a/src/pages/workspace/View.js +++ b/src/pages/workspace/View.js @@ -17,7 +17,10 @@ function View({ level, view, sidebarOpen, toggleSideBar }) { ?.boards.find(x => x.name === board)?._id }, [params.space, params.board, workspace?.spaces, workspace]) const boardId = findBoardId() - const { isFetching, error } = useFetchBoardQuery({ boardId }) + const { isFetching, error } = useFetchBoardQuery({ + boardId, + viewId: params.view + }) const viewContainerRef = useRef(null) const [bla, setBla] = useState(false) const setViewContainerRef = useCallback(node => { diff --git a/src/pages/workspace/view/ViewTopMenu.js b/src/pages/workspace/view/ViewTopMenu.js index d58acf5..6e6359c 100644 --- a/src/pages/workspace/view/ViewTopMenu.js +++ b/src/pages/workspace/view/ViewTopMenu.js @@ -14,12 +14,16 @@ import NewViewMenu from './viewTopMenu/NewViewMenu.tsx' import ViewNavItem from './viewTopMenu/ViewNavItem.tsx' function ViewTopMenu({ boardId, sideBarClosed, toggleSideBar }) { - const { data: board, isFetching, error } = useFetchBoardQuery({ boardId }) + const params = useParams() + const { + data: board, + isFetching, + error + } = useFetchBoardQuery({ boardId, viewId: params.view }) const [shareDialogOpen, toggleShareDialogOpen] = useToggleState(false) const [groupByOpen, setGroupByOpen] = useState(null) const navigate = useNavigate() const [search, setSearch] = useAtom(searchAtom) - const params = useParams() const view = board?.views.find(x => x.id === params?.view) useEffect(() => { @@ -78,17 +82,19 @@ function ViewTopMenu({ boardId, sideBarClosed, toggleSideBar }) {
- {board?.views.map(({ name, id, _id }) => ( - - ))} + {!isFetching && + !error && + board?.views.map(({ name, id, _id }) => ( + + ))}
diff --git a/src/pages/workspace/view/board/taskGroup/TaskModal.tsx b/src/pages/workspace/view/board/taskGroup/TaskModal.tsx index c68a580..62c190f 100644 --- a/src/pages/workspace/view/board/taskGroup/TaskModal.tsx +++ b/src/pages/workspace/view/board/taskGroup/TaskModal.tsx @@ -31,7 +31,7 @@ interface Props { function TaskModal({ task, boardId, hasPerms, members }: Props) { const navigate = useNavigate() const params = useParams() - const { data: board } = useFetchBoardQuery({ boardId }) + const { data: board } = useFetchBoardQuery({ boardId, viewId: params.view }) const { data: user } = useFetchUserQuery() const [name, changeName] = useInputState(task.name) const [anchor, setAnchor] = useState(null) diff --git a/src/pages/workspace/view/board/taskGroup/taskModal/Activity.js b/src/pages/workspace/view/board/taskGroup/taskModal/Activity.js index a5d6b15..19177bf 100644 --- a/src/pages/workspace/view/board/taskGroup/taskModal/Activity.js +++ b/src/pages/workspace/view/board/taskGroup/taskModal/Activity.js @@ -2,9 +2,11 @@ import styles from '../../../../../../styles/TaskModal.module.scss' import RelativeDate from '../../../../../../utils/RelativeDate' import { diffWords } from 'diff' import { useFetchBoardQuery } from '../../../../../../modules/services/boardSlice.ts' +import { useParams } from 'react-router-dom' function Activity({ timestamp, activity, boardId }) { - const { data: board } = useFetchBoardQuery({ boardId }) + const params = useParams() + const { data: board } = useFetchBoardQuery({ boardId, viewId: params.view }) const getDiff = () => { const groups = diffWords(activity.change.from, activity.change.to) diff --git a/src/pages/workspace/view/viewTopMenu/FilterMenu.tsx b/src/pages/workspace/view/viewTopMenu/FilterMenu.tsx index e9ddb50..eab6813 100644 --- a/src/pages/workspace/view/viewTopMenu/FilterMenu.tsx +++ b/src/pages/workspace/view/viewTopMenu/FilterMenu.tsx @@ -8,6 +8,7 @@ import { useFetchBoardQuery, useSetFiltersMutation } from '../../../../modules/services/boardSlice.ts' +import { useParams } from 'react-router-dom' const operations = { status: ['Is', 'Is not', 'Is set', 'Is not set'], @@ -16,7 +17,11 @@ const operations = { } export default function FilterMenu({ boardId, view }) { - const { data: board } = useFetchBoardQuery({ boardId }) + const params = useParams() + const { data: board } = useFetchBoardQuery({ + boardId, + viewId: params?.view + }) const [setFilters] = useSetFiltersMutation() const [filters, setFiltersState] = useState([]) @@ -97,6 +102,7 @@ export default function FilterMenu({ boardId, view }) { } const used = filters.length >= 1 + if (!board) return return ( From 856afe9acd9c5af7d05c5550158f83c6d2a95e0f Mon Sep 17 00:00:00 2001 From: MarconLP <13001502+MarconLP@users.noreply.github.com> Date: Mon, 20 Feb 2023 15:06:57 +0100 Subject: [PATCH 11/18] update board mutations to include viewShortId --- src/modules/services/boardSlice.ts | 135 +++++++++++------- src/pages/workspace/View.js | 2 +- src/pages/workspace/topMenu/GroupByPopover.js | 7 +- src/pages/workspace/topMenu/ShareDialog.js | 3 + src/pages/workspace/view/Board.js | 5 +- src/pages/workspace/view/ViewTopMenu.js | 2 +- src/pages/workspace/view/board/BoardView.js | 8 +- src/pages/workspace/view/board/TaskGroup.tsx | 5 +- .../board/taskGroup/AddAttributePopover.js | 3 + .../view/board/taskGroup/AttributePopover.js | 9 +- .../view/board/taskGroup/NewTaskForm.tsx | 6 + .../workspace/view/board/taskGroup/Task.tsx | 5 +- .../view/board/taskGroup/TaskModal.tsx | 9 +- .../taskGroup/task/PersonColumnPopover.js | 6 +- .../taskGroup/task/TaskColumnPopover.tsx | 5 + .../view/board/taskGroup/task/TaskPopover.js | 3 + .../taskGroup/task/taskColumns/TextColumn.tsx | 3 + .../board/taskGroup/taskModal/Activity.js | 5 +- .../board/taskGroup/taskModal/Comment.tsx | 3 + .../taskGroup/taskModal/WatcherPopover.js | 14 +- .../taskModal/comment/CommentPopover.js | 18 ++- .../taskModal/comment/CommentReplySection.tsx | 3 + .../comment/commentReplySection/Reply.tsx | 3 + .../workspace/view/viewTopMenu/FilterMenu.tsx | 2 +- .../view/viewTopMenu/ViewNavItem.tsx | 4 + 25 files changed, 201 insertions(+), 67 deletions(-) diff --git a/src/modules/services/boardSlice.ts b/src/modules/services/boardSlice.ts index a40bf2a..9bab6e5 100644 --- a/src/modules/services/boardSlice.ts +++ b/src/modules/services/boardSlice.ts @@ -210,7 +210,9 @@ const setSharing = ({ board, share }) => { export const boardApi = api.injectEndpoints({ endpoints: builder => ({ fetchBoard: builder.query({ - query: ({ boardId, viewId = '' }) => `board/${boardId}/${viewId}`, + query: ({ boardId, viewShortId = '' }) => + `board/${boardId}/${viewShortId}`, + keepUnusedDataFor: 0, transformResponse: (response: any, meta, arg) => { return { ...response, @@ -341,7 +343,7 @@ export const boardApi = api.injectEndpoints({ channel.unbind() }, providesTags: board => (board ? [board._id, 'Board'] : []), - async onQueryStarted(boardId, { queryFulfilled }) { + async onQueryStarted({ boardId }, { queryFulfilled }) { try { await queryFulfilled localStorage.setItem( @@ -361,13 +363,15 @@ export const boardApi = api.injectEndpoints({ body: { groupBy } }), async onQueryStarted( - { boardId, groupBy, viewId }, + { boardId, groupBy, viewId, viewShortId }, { dispatch, queryFulfilled } ) { + console.log('viewShortId') + console.log(viewShortId) const patchResult = dispatch( boardApi.util.updateQueryData( 'fetchBoard', - boardId, + { boardId, viewShortId }, board => { setGroupBy({ board, groupBy, viewId }) } @@ -391,13 +395,22 @@ export const boardApi = api.injectEndpoints({ } }), async onQueryStarted( - { boardId, newTaskName, taskGroupId, _id, author, level }, + { + boardId, + newTaskName, + taskGroupId, + _id, + author, + level, + viewId, + viewShortId + }, { dispatch, queryFulfilled } ) { const patchResult = dispatch( boardApi.util.updateQueryData( 'fetchBoard', - boardId, + { boardId, viewShortId }, board => addTask({ board, @@ -422,13 +435,13 @@ export const boardApi = api.injectEndpoints({ method: 'DELETE' }), async onQueryStarted( - { taskId, boardId }, + { taskId, boardId, viewShortId }, { dispatch, queryFulfilled } ) { const patchResult = dispatch( boardApi.util.updateQueryData( 'fetchBoard', - boardId, + { boardId, viewShortId }, board => { deleteTask({ board, taskId }) } @@ -453,13 +466,20 @@ export const boardApi = api.injectEndpoints({ } }), async onQueryStarted( - { result, destinationIndex, newItems, sourceIndex, boardId }, + { + result, + destinationIndex, + newItems, + sourceIndex, + boardId, + viewShortId + }, { dispatch, queryFulfilled } ) { const patchResult = dispatch( boardApi.util.updateQueryData( 'fetchBoard', - boardId, + { boardId, viewShortId }, board => { // sortTask({ // board, @@ -492,13 +512,13 @@ export const boardApi = api.injectEndpoints({ } }), async onQueryStarted( - { column, value, type, taskId, boardId }, + { column, value, type, taskId, boardId, viewShortId }, { dispatch, queryFulfilled } ) { const patchResult = dispatch( boardApi.util.updateQueryData( 'fetchBoard', - boardId, + { boardId, viewShortId }, board => { editTaskField({ board, @@ -524,13 +544,13 @@ export const boardApi = api.injectEndpoints({ body: { content, _id } }), async onQueryStarted( - { content, author, taskId, boardId, _id }, + { content, author, taskId, boardId, _id, viewShortId }, { dispatch, queryFulfilled } ) { const patchResult = dispatch( boardApi.util.updateQueryData( 'fetchBoard', - boardId, + { boardId, viewShortId }, board => addTaskComment({ board, @@ -555,13 +575,13 @@ export const boardApi = api.injectEndpoints({ body: { content } }), async onQueryStarted( - { content, taskId, boardId, commentId }, + { content, taskId, boardId, commentId, viewShortId }, { dispatch, queryFulfilled } ) { const patchResult = dispatch( boardApi.util.updateQueryData( 'fetchBoard', - boardId, + { boardId, viewShortId }, board => editTaskComment({ board, @@ -584,13 +604,13 @@ export const boardApi = api.injectEndpoints({ method: 'DELETE' }), async onQueryStarted( - { taskId, boardId, commentId }, + { taskId, boardId, commentId, viewShortId }, { dispatch, queryFulfilled } ) { const patchResult = dispatch( boardApi.util.updateQueryData( 'fetchBoard', - boardId, + { boardId, viewShortId }, board => deleteTaskComment({ board, @@ -613,13 +633,21 @@ export const boardApi = api.injectEndpoints({ body: { content, _id } }), async onQueryStarted( - { content, author, taskId, boardId, commentId, _id }, + { + content, + author, + taskId, + boardId, + commentId, + _id, + viewShortId + }, { dispatch, queryFulfilled } ) { const patchResult = dispatch( boardApi.util.updateQueryData( 'fetchBoard', - boardId, + { boardId, viewShortId }, board => addReply({ board, @@ -675,13 +703,13 @@ export const boardApi = api.injectEndpoints({ method: 'DELETE' }), async onQueryStarted( - { taskId, boardId, replyId, commentId }, + { taskId, boardId, replyId, commentId, viewShortId }, { dispatch, queryFulfilled } ) { const patchResult = dispatch( boardApi.util.updateQueryData( 'fetchBoard', - boardId, + { boardId, viewShortId }, board => deleteReply({ board, @@ -704,13 +732,13 @@ export const boardApi = api.injectEndpoints({ method: 'DELETE' }), async onQueryStarted( - { taskId, optionId, boardId }, + { taskId, optionId, boardId, viewShortId }, { dispatch, queryFulfilled } ) { const patchResult = dispatch( boardApi.util.updateQueryData( 'fetchBoard', - boardId, + { boardId, viewShortId }, board => clearStatusTask({ board, taskId, optionId }) ) ) @@ -728,13 +756,13 @@ export const boardApi = api.injectEndpoints({ body: { type, _id } }), async onQueryStarted( - { type, _id, boardId }, + { type, _id, boardId, viewShortId }, { dispatch, queryFulfilled } ) { const patchResult = dispatch( boardApi.util.updateQueryData( 'fetchBoard', - boardId, + { boardId, viewShortId }, board => addAttribute({ board, type, _id }) ) ) @@ -752,13 +780,13 @@ export const boardApi = api.injectEndpoints({ body: { name } }), async onQueryStarted( - { name, attributeId, boardId }, + { name, attributeId, boardId, viewShortId }, { dispatch, queryFulfilled } ) { const patchResult = dispatch( boardApi.util.updateQueryData( 'fetchBoard', - boardId, + { boardId, viewShortId }, board => editAttributeName({ board, name, attributeId }) ) ) @@ -775,13 +803,13 @@ export const boardApi = api.injectEndpoints({ method: 'DELETE' }), async onQueryStarted( - { attributeId, boardId }, + { attributeId, boardId, viewShortId }, { dispatch, queryFulfilled } ) { const patchResult = dispatch( boardApi.util.updateQueryData( 'fetchBoard', - boardId, + { boardId, viewShortId }, board => deleteAttribute({ board, attributeId }) ) ) @@ -799,13 +827,13 @@ export const boardApi = api.injectEndpoints({ body: { result } }), async onQueryStarted( - { result, boardId }, + { result, boardId, viewShortId }, { dispatch, queryFulfilled } ) { const patchResult = dispatch( boardApi.util.updateQueryData( 'fetchBoard', - boardId, + { boardId, viewShortId }, board => sortAttribute({ board, result }) ) ) @@ -823,13 +851,13 @@ export const boardApi = api.injectEndpoints({ body: { name, labels } }), async onQueryStarted( - { name, labels, boardId }, + { name, labels, boardId, viewShortId }, { dispatch, queryFulfilled } ) { const patchResult = dispatch( boardApi.util.updateQueryData( 'fetchBoard', - boardId, + { boardId, viewShortId }, board => editAttributeLabels({ board, name, labels }) ) ) @@ -847,13 +875,13 @@ export const boardApi = api.injectEndpoints({ body: { userId: user._id } }), async onQueryStarted( - { task, user, boardId }, + { task, user, boardId, viewShortId }, { dispatch, queryFulfilled } ) { const patchResult = dispatch( boardApi.util.updateQueryData( 'fetchBoard', - boardId, + { boardId, viewShortId }, board => addWatcher({ board, task, user }) ) ) @@ -871,13 +899,13 @@ export const boardApi = api.injectEndpoints({ body: { userId: user._id } }), async onQueryStarted( - { task, user, boardId }, + { task, user, boardId, viewShortId }, { dispatch, queryFulfilled } ) { const patchResult = dispatch( boardApi.util.updateQueryData( 'fetchBoard', - boardId, + { boardId, viewShortId }, board => removeWatcher({ board, task, user }) ) ) @@ -895,13 +923,13 @@ export const boardApi = api.injectEndpoints({ body: { share } }), async onQueryStarted( - { boardId, share }, + { boardId, share, viewShortId }, { dispatch, queryFulfilled } ) { const patchResult = dispatch( boardApi.util.updateQueryData( 'fetchBoard', - boardId, + { boardId, viewShortId }, board => setSharing({ board, share }) ) ) @@ -913,20 +941,20 @@ export const boardApi = api.injectEndpoints({ } }), addSubtask: builder.mutation({ - query: ({ boardId, newTaskName, taskId }) => ({ + query: ({ boardId, newTaskName, taskId, viewShortId }) => ({ url: `task/${boardId}/${taskId}`, method: 'POST', body: { name: newTaskName } }), invalidatesTags: (result, error, arg) => [arg.boardId], async onQueryStarted( - { boardId, newTaskName, taskGroupId, _id, author }, + { boardId, newTaskName, taskGroupId, _id, author, viewShortId }, { dispatch, queryFulfilled } ) { const patchResult = dispatch( boardApi.util.updateQueryData( 'fetchBoard', - boardId, + { boardId, viewShortId }, board => {} ) ) @@ -944,11 +972,14 @@ export const boardApi = api.injectEndpoints({ body: { type } }), invalidatesTags: (result, error, arg) => [arg.boardId], - async onQueryStarted({ boardId }, { dispatch, queryFulfilled }) { + async onQueryStarted( + { boardId, viewShortId }, + { dispatch, queryFulfilled } + ) { const patchResult = dispatch( boardApi.util.updateQueryData( 'fetchBoard', - boardId, + { boardId, viewShortId }, board => {} ) ) @@ -966,11 +997,14 @@ export const boardApi = api.injectEndpoints({ body: { viewId } }), invalidatesTags: (result, error, arg) => [arg.boardId], - async onQueryStarted({ boardId }, { dispatch, queryFulfilled }) { + async onQueryStarted( + { boardId, viewShortId }, + { dispatch, queryFulfilled } + ) { const patchResult = dispatch( boardApi.util.updateQueryData( 'fetchBoard', - boardId, + { boardId, viewShortId }, board => {} ) ) @@ -988,11 +1022,14 @@ export const boardApi = api.injectEndpoints({ body: { name } }), invalidatesTags: (result, error, arg) => [arg.boardId], - async onQueryStarted({ boardId }, { dispatch, queryFulfilled }) { + async onQueryStarted( + { boardId, viewShortId }, + { dispatch, queryFulfilled } + ) { const patchResult = dispatch( boardApi.util.updateQueryData( 'fetchBoard', - boardId, + { boardId, viewShortId }, board => {} ) ) diff --git a/src/pages/workspace/View.js b/src/pages/workspace/View.js index 76d90ea..90f2f6b 100644 --- a/src/pages/workspace/View.js +++ b/src/pages/workspace/View.js @@ -19,7 +19,7 @@ function View({ level, view, sidebarOpen, toggleSideBar }) { const boardId = findBoardId() const { isFetching, error } = useFetchBoardQuery({ boardId, - viewId: params.view + viewShortId: params.view }) const viewContainerRef = useRef(null) const [bla, setBla] = useState(false) diff --git a/src/pages/workspace/topMenu/GroupByPopover.js b/src/pages/workspace/topMenu/GroupByPopover.js index f72f425..1421daf 100644 --- a/src/pages/workspace/topMenu/GroupByPopover.js +++ b/src/pages/workspace/topMenu/GroupByPopover.js @@ -13,7 +13,12 @@ function GroupByPopover({ view, board, groupByOpen, setGroupByOpen }) { } const handleGroupByChange = _id => { - setGroupBy({ boardId: board._id, viewId: view._id, groupBy: _id }) + setGroupBy({ + boardId: board._id, + viewId: view._id, + viewShortId: view.id, + groupBy: _id + }) handleClose() } diff --git a/src/pages/workspace/topMenu/ShareDialog.js b/src/pages/workspace/topMenu/ShareDialog.js index 1191151..dac4821 100644 --- a/src/pages/workspace/topMenu/ShareDialog.js +++ b/src/pages/workspace/topMenu/ShareDialog.js @@ -5,12 +5,15 @@ import styles from '../../../styles/ShareDialog.module.scss' import { FontAwesomeIcon } from '@fortawesome/react-fontawesome' import { solid } from '@fortawesome/fontawesome-svg-core/import.macro' import { useSetSharingMutation } from '../../../modules/services/boardSlice.ts' +import { useParams } from 'react-router-dom' function ShareDialog({ handleClose, open, boardId, view }) { + const params = useParams() const [setSharing] = useSetSharingMutation() const toggleShare = (e, share) => { setSharing({ + viewShortId: params.view, viewId: view._id, boardId, share diff --git a/src/pages/workspace/view/Board.js b/src/pages/workspace/view/Board.js index 99aa5d8..c2bc378 100644 --- a/src/pages/workspace/view/Board.js +++ b/src/pages/workspace/view/Board.js @@ -8,7 +8,10 @@ import { forwardRef, useCallback } from 'react' function Board({ boardId }, viewContainerRef) { const params = useParams() const { data: workspace } = useFetchWorkspaceQuery(params.workspace) - const { data: board } = useFetchBoardQuery({ boardId, viewId: params.view }) + const { data: board } = useFetchBoardQuery({ + boardId, + viewShortId: params.view + }) const view = board?.views.find(x => x.id === params.view) const hasPerms = useCallback(permission => { diff --git a/src/pages/workspace/view/ViewTopMenu.js b/src/pages/workspace/view/ViewTopMenu.js index 6e6359c..0540cb3 100644 --- a/src/pages/workspace/view/ViewTopMenu.js +++ b/src/pages/workspace/view/ViewTopMenu.js @@ -19,7 +19,7 @@ function ViewTopMenu({ boardId, sideBarClosed, toggleSideBar }) { data: board, isFetching, error - } = useFetchBoardQuery({ boardId, viewId: params.view }) + } = useFetchBoardQuery({ boardId, viewShortId: params.view }) const [shareDialogOpen, toggleShareDialogOpen] = useToggleState(false) const [groupByOpen, setGroupByOpen] = useState(null) const navigate = useNavigate() diff --git a/src/pages/workspace/view/board/BoardView.js b/src/pages/workspace/view/board/BoardView.js index 965a3c7..c84f0a3 100644 --- a/src/pages/workspace/view/board/BoardView.js +++ b/src/pages/workspace/view/board/BoardView.js @@ -47,6 +47,7 @@ function BoardView({ board, members, hasPerms }, viewContainerRef) { return ( ( (null) const [collapsedItems, setCollapsedItems] = useState([]) @@ -357,6 +358,7 @@ function TaskGroup(props: Props, viewContainerRef) { totalCount={flattenedTasks.length} itemContent={(i, task) => ( {Boolean(activeItem) && ( { props.close() @@ -20,6 +22,7 @@ function AddAttributePopover(props) { return } addAttribute({ + viewShortId: params.view, boardId: props.boardId, type, _id: ObjectId() diff --git a/src/pages/workspace/view/board/taskGroup/AttributePopover.js b/src/pages/workspace/view/board/taskGroup/AttributePopover.js index a9f41f3..4c52bc4 100644 --- a/src/pages/workspace/view/board/taskGroup/AttributePopover.js +++ b/src/pages/workspace/view/board/taskGroup/AttributePopover.js @@ -15,12 +15,14 @@ import { } from '../../../../../modules/services/boardSlice.ts' import { regular } from '@fortawesome/fontawesome-svg-core/import.macro' import { FontAwesomeIcon } from '@fortawesome/react-fontawesome' +import { useParams } from 'react-router-dom' function AttributePopover(props) { const [deleteDialogOpen, setDeleteDialogOpen] = useState(false) const [name, setName] = useState(props.attr.name) const [deleteAttribute] = useDeleteAttributeMutation() const [editAttributeName] = useEditAttributeNameMutation() + const params = useParams() useEffect(() => { setName(props.attr.name || '') @@ -33,12 +35,17 @@ function AttributePopover(props) { const handleAttributeDelete = async () => { setDeleteDialogOpen(!deleteDialogOpen) - deleteAttribute({ attributeId: props.attr._id, boardId: props.boardId }) + deleteAttribute({ + viewShortId: params.view, + attributeId: props.attr._id, + boardId: props.boardId + }) } const updateName = async () => { if (props.attr.name === name || name === '') return editAttributeName({ + viewShortId: params.view, attributeId: props.attr._id, name, boardId: props.boardId diff --git a/src/pages/workspace/view/board/taskGroup/NewTaskForm.tsx b/src/pages/workspace/view/board/taskGroup/NewTaskForm.tsx index 9e26435..f6d51cc 100644 --- a/src/pages/workspace/view/board/taskGroup/NewTaskForm.tsx +++ b/src/pages/workspace/view/board/taskGroup/NewTaskForm.tsx @@ -7,6 +7,7 @@ import { } from '../../../../../modules/services/boardSlice.ts' import { useRef, useState } from 'react' import { useClickAway } from 'react-use' +import { useParams } from 'react-router-dom' function NewTaskForm({ boardId, @@ -15,6 +16,7 @@ function NewTaskForm({ setNewTaskOpen, taskId }) { + const params = useParams() const { data: user } = useFetchUserQuery() const [addTask] = useAddTaskMutation() const [addSubtask] = useAddSubtaskMutation() @@ -31,6 +33,7 @@ function NewTaskForm({ resetNewTaskName() if (level >= 0) return handleSubTask() addTask({ + viewShortId: params.view, author: user.username, level: level + 1, boardId, @@ -55,6 +58,7 @@ function NewTaskForm({ const handleSubTask = () => { addSubtask({ + viewShortId: params.view, boardId, newTaskName, taskId @@ -65,12 +69,14 @@ function NewTaskForm({ multipleTasks.map(taskName => { if (level >= 0) { addSubtask({ + viewShortId: params.view, boardId, newTaskName: taskName, taskId }) } else { addTask({ + viewShortId: params.view, author: user.username, level: level + 1, boardId, diff --git a/src/pages/workspace/view/board/taskGroup/Task.tsx b/src/pages/workspace/view/board/taskGroup/Task.tsx index 9357cda..4bd00a4 100644 --- a/src/pages/workspace/view/board/taskGroup/Task.tsx +++ b/src/pages/workspace/view/board/taskGroup/Task.tsx @@ -26,6 +26,7 @@ interface Props { groupedTasks: Label[] taskGroupId: string index: number + viewShortId: string } function Task({ @@ -38,7 +39,8 @@ function Task({ handleCollapse, groupedTasks, taskGroupId, - index + index, + viewShortId }: Props) { const [anchor, setAnchor] = useState(null) const navigate = useNavigate() @@ -104,6 +106,7 @@ function Task({ setTaskEditing(false) if (task.name === taskName) return editTaskField({ + viewShortId: viewShortId, type: 'name', value: taskName, boardId, diff --git a/src/pages/workspace/view/board/taskGroup/TaskModal.tsx b/src/pages/workspace/view/board/taskGroup/TaskModal.tsx index 62c190f..28dcc37 100644 --- a/src/pages/workspace/view/board/taskGroup/TaskModal.tsx +++ b/src/pages/workspace/view/board/taskGroup/TaskModal.tsx @@ -31,7 +31,10 @@ interface Props { function TaskModal({ task, boardId, hasPerms, members }: Props) { const navigate = useNavigate() const params = useParams() - const { data: board } = useFetchBoardQuery({ boardId, viewId: params.view }) + const { data: board } = useFetchBoardQuery({ + boardId, + viewShortId: params.view + }) const { data: user } = useFetchUserQuery() const [name, changeName] = useInputState(task.name) const [anchor, setAnchor] = useState(null) @@ -64,7 +67,8 @@ function TaskModal({ task, boardId, hasPerms, members }: Props) { type: 'name', value: name, boardId, - taskId: task._id + taskId: task._id, + viewShortId: params.view }) } @@ -72,6 +76,7 @@ function TaskModal({ task, boardId, hasPerms, members }: Props) { if (editor.getJSON() === '') return await addTaskComment({ + viewShortId: params.view, _id: ObjectId(), boardId, content: editor.getJSON(), diff --git a/src/pages/workspace/view/board/taskGroup/task/PersonColumnPopover.js b/src/pages/workspace/view/board/taskGroup/task/PersonColumnPopover.js index 368763d..e50b51d 100644 --- a/src/pages/workspace/view/board/taskGroup/task/PersonColumnPopover.js +++ b/src/pages/workspace/view/board/taskGroup/task/PersonColumnPopover.js @@ -30,7 +30,8 @@ function PersonColumnPopover({ value: taskOption.value.filter(x => x !== user._id), type: 'person', boardId, - taskId: task._id + taskId: task._id, + viewShortId: params.view }) } else { let value = [user._id] @@ -40,7 +41,8 @@ function PersonColumnPopover({ value, type: 'person', boardId, - taskId: task._id + taskId: task._id, + viewShortId: params.view }) } } diff --git a/src/pages/workspace/view/board/taskGroup/task/TaskColumnPopover.tsx b/src/pages/workspace/view/board/taskGroup/task/TaskColumnPopover.tsx index 2af067b..f9daa99 100644 --- a/src/pages/workspace/view/board/taskGroup/task/TaskColumnPopover.tsx +++ b/src/pages/workspace/view/board/taskGroup/task/TaskColumnPopover.tsx @@ -12,6 +12,7 @@ import { FontAwesomeIcon } from '@fortawesome/react-fontawesome' import { solid } from '@fortawesome/fontawesome-svg-core/import.macro' import { Menu, Transition } from '@headlessui/react' import { Attribute, Label, FlatTask } from '../../../../../../types/Board' +import { useParams } from 'react-router-dom' interface Props { hasPerms: (string) => boolean @@ -32,6 +33,7 @@ function TaskColumnPopover({ const [editAttributeLabels] = useEditAttributeLabelsMutation() const [clearStatusTask] = useClearStatusTaskMutation() const [editTaskField] = useEditTaskFieldMutation() + const params = useParams() const [labelsEditing, setLabelsEditing] = useState(false) @@ -43,6 +45,7 @@ function TaskColumnPopover({ const toggleEdit = async () => { if (labelsEditing) { editAttributeLabels({ + viewShortId: params.view, boardId, name: attribute.name, labels: editingLabels @@ -111,6 +114,7 @@ function TaskColumnPopover({ editTaskField({ boardId, + viewShortId: params.view, taskId: task._id, column: attribute._id, value: id._id, @@ -124,6 +128,7 @@ function TaskColumnPopover({ if (labelsEditing) return clearStatusTask({ + viewShortId: params.view, boardId, taskId: task._id, optionId: attribute._id diff --git a/src/pages/workspace/view/board/taskGroup/task/TaskPopover.js b/src/pages/workspace/view/board/taskGroup/task/TaskPopover.js index 814a480..8c3618f 100644 --- a/src/pages/workspace/view/board/taskGroup/task/TaskPopover.js +++ b/src/pages/workspace/view/board/taskGroup/task/TaskPopover.js @@ -13,8 +13,10 @@ import styles from '../../../../../../styles/TaskPopover.module.scss' import { useDeleteTaskMutation } from '../../../../../../modules/services/boardSlice.ts' import { FontAwesomeIcon } from '@fortawesome/react-fontawesome' import { regular, solid } from '@fortawesome/fontawesome-svg-core/import.macro' +import { useParams } from 'react-router-dom' function TaskPopover(props) { + const params = useParams() const [deleteTask] = useDeleteTaskMutation() const [deleteDialog, setDeleteDialog] = useState(false) const { boardId, taskId } = props @@ -28,6 +30,7 @@ function TaskPopover(props) { const handleDelete = () => { setDeleteDialog(false) deleteTask({ + viewShortId: params.view, boardId, taskId }) diff --git a/src/pages/workspace/view/board/taskGroup/task/taskColumns/TextColumn.tsx b/src/pages/workspace/view/board/taskGroup/task/taskColumns/TextColumn.tsx index 32348ca..01cacd7 100644 --- a/src/pages/workspace/view/board/taskGroup/task/taskColumns/TextColumn.tsx +++ b/src/pages/workspace/view/board/taskGroup/task/taskColumns/TextColumn.tsx @@ -2,6 +2,7 @@ import { useEditTaskFieldMutation } from '../../../../../../../modules/services/ import { Attribute, Task } from '../../../../../../../types/Board' import useInputState from '../../../../../../../modules/hooks/useInputState.tsx' import { useEffect } from 'react' +import { useParams } from 'react-router-dom' type Props = { attribute: Attribute @@ -16,6 +17,7 @@ export default function TextColumn({ boardId, hasPerms }: Props) { + const params = useParams() const [editTaskField] = useEditTaskFieldMutation() const handleTextEdit = async e => { @@ -27,6 +29,7 @@ export default function TextColumn({ editTaskField({ column: e.target.name, + viewShortId: params.view, value: e.target.value, type: 'text', boardId, diff --git a/src/pages/workspace/view/board/taskGroup/taskModal/Activity.js b/src/pages/workspace/view/board/taskGroup/taskModal/Activity.js index 19177bf..3f92195 100644 --- a/src/pages/workspace/view/board/taskGroup/taskModal/Activity.js +++ b/src/pages/workspace/view/board/taskGroup/taskModal/Activity.js @@ -6,7 +6,10 @@ import { useParams } from 'react-router-dom' function Activity({ timestamp, activity, boardId }) { const params = useParams() - const { data: board } = useFetchBoardQuery({ boardId, viewId: params.view }) + const { data: board } = useFetchBoardQuery({ + boardId, + viewShortId: params.view + }) const getDiff = () => { const groups = diffWords(activity.change.from, activity.change.to) diff --git a/src/pages/workspace/view/board/taskGroup/taskModal/Comment.tsx b/src/pages/workspace/view/board/taskGroup/taskModal/Comment.tsx index 1825c51..4361af9 100644 --- a/src/pages/workspace/view/board/taskGroup/taskModal/Comment.tsx +++ b/src/pages/workspace/view/board/taskGroup/taskModal/Comment.tsx @@ -8,6 +8,7 @@ import Editor from '../../../../../../utils/Editor.tsx' import CommentReplySection from './comment/CommentReplySection.tsx' import RelativeDate from '../../../../../../utils/RelativeDate' import { Comment as CommentType } from '../../../../../../types/Board' +import { useParams } from 'react-router-dom' interface Props { comment: CommentType @@ -15,12 +16,14 @@ interface Props { boardId: string } function Comment({ comment, taskId, boardId }: Props) { + const params = useParams() const [editing, setEditing] = useState(false) const [replySectionOpen, setReplySectionOpen] = useState(false) const [editTaskComment] = useEditTaskCommentMutation() const handleSave = editor => { editTaskComment({ + viewShortId: params.view, boardId, taskId, commentId: comment._id, diff --git a/src/pages/workspace/view/board/taskGroup/taskModal/WatcherPopover.js b/src/pages/workspace/view/board/taskGroup/taskModal/WatcherPopover.js index d1420a6..c005dc9 100644 --- a/src/pages/workspace/view/board/taskGroup/taskModal/WatcherPopover.js +++ b/src/pages/workspace/view/board/taskGroup/taskModal/WatcherPopover.js @@ -25,9 +25,19 @@ function WatcherPopover({ task, anchor, setAnchor, boardId }) { const handleClick = (user, isWatcher) => { if (isWatcher) { - removeWatcher({ task: task, user: user, boardId }) + removeWatcher({ + viewShortId: params.view, + task: task, + user: user, + boardId + }) } else { - addWatcher({ task: task, user: user, boardId }) + addWatcher({ + viewShortId: params.view, + task: task, + user: user, + boardId + }) } } diff --git a/src/pages/workspace/view/board/taskGroup/taskModal/comment/CommentPopover.js b/src/pages/workspace/view/board/taskGroup/taskModal/comment/CommentPopover.js index cd6804d..bd94e0d 100644 --- a/src/pages/workspace/view/board/taskGroup/taskModal/comment/CommentPopover.js +++ b/src/pages/workspace/view/board/taskGroup/taskModal/comment/CommentPopover.js @@ -7,12 +7,14 @@ import { useDeleteReplyMutation, useDeleteTaskCommentMutation } from '../../../../../../../modules/services/boardSlice.ts' +import { useParams } from 'react-router-dom' function CommentPopover({ taskId, commentId, type, replyId, boardId }) { const [open, setOpen] = useState(null) const [deleteComment] = useDeleteTaskCommentMutation() const [deleteReply] = useDeleteReplyMutation() const lastEl = useRef() + const params = useParams() const items = [ { @@ -32,9 +34,21 @@ function CommentPopover({ taskId, commentId, type, replyId, boardId }) { ] const handleDelete = () => { - if (type === 'comment') deleteComment({ boardId, taskId, commentId }) + if (type === 'comment') + deleteComment({ + viewShortId: params.view, + boardId, + taskId, + commentId + }) else if (type === 'reply') - deleteReply({ boardId, taskId, commentId, replyId }) + deleteReply({ + viewShortId: params.view, + boardId, + taskId, + commentId, + replyId + }) setOpen(false) } diff --git a/src/pages/workspace/view/board/taskGroup/taskModal/comment/CommentReplySection.tsx b/src/pages/workspace/view/board/taskGroup/taskModal/comment/CommentReplySection.tsx index 9c73f07..3162c58 100644 --- a/src/pages/workspace/view/board/taskGroup/taskModal/comment/CommentReplySection.tsx +++ b/src/pages/workspace/view/board/taskGroup/taskModal/comment/CommentReplySection.tsx @@ -4,6 +4,7 @@ import Reply from './commentReplySection/Reply.tsx' import { useFetchUserQuery } from '../../../../../../../modules/services/userSlice' import { ObjectId } from '../../../../../../../utils' import { ReplyType } from '../../../../../../../types/Board' +import { useParams } from 'react-router-dom' interface Props { taskId: string @@ -15,9 +16,11 @@ interface Props { function CommentReplySection({ taskId, commentId, replies, boardId }: Props) { const { data: user } = useFetchUserQuery() const [addReply] = useAddReplyMutation() + const params = useParams() const handleAddReply = editor => { addReply({ + viewShortId: params.view, _id: ObjectId(), boardId, taskId, diff --git a/src/pages/workspace/view/board/taskGroup/taskModal/comment/commentReplySection/Reply.tsx b/src/pages/workspace/view/board/taskGroup/taskModal/comment/commentReplySection/Reply.tsx index a8f793e..bb4b4af 100644 --- a/src/pages/workspace/view/board/taskGroup/taskModal/comment/commentReplySection/Reply.tsx +++ b/src/pages/workspace/view/board/taskGroup/taskModal/comment/commentReplySection/Reply.tsx @@ -7,6 +7,7 @@ import { useState } from 'react' import { useEditReplyMutation } from '../../../../../../../../modules/services/boardSlice.ts' import RelativeDate from '../../../../../../../../utils/RelativeDate' import { Reply as ReplyType } from '../../../../../../../../types/Board' +import { useParams } from 'react-router-dom' interface Props { commentId: string @@ -18,9 +19,11 @@ interface Props { function Reply({ commentId, reply, taskId, boardId }: Props) { const [editing, setEditing] = useState(false) const [editReply] = useEditReplyMutation() + const params = useParams() const handleSave = editor => { editReply({ + viewShortId: params.view, boardId, taskId, commentId, diff --git a/src/pages/workspace/view/viewTopMenu/FilterMenu.tsx b/src/pages/workspace/view/viewTopMenu/FilterMenu.tsx index eab6813..e02f2e5 100644 --- a/src/pages/workspace/view/viewTopMenu/FilterMenu.tsx +++ b/src/pages/workspace/view/viewTopMenu/FilterMenu.tsx @@ -20,7 +20,7 @@ export default function FilterMenu({ boardId, view }) { const params = useParams() const { data: board } = useFetchBoardQuery({ boardId, - viewId: params?.view + viewShortId: params?.view }) const [setFilters] = useSetFiltersMutation() const [filters, setFiltersState] = useState([]) diff --git a/src/pages/workspace/view/viewTopMenu/ViewNavItem.tsx b/src/pages/workspace/view/viewTopMenu/ViewNavItem.tsx index 7cee20a..858a7fd 100644 --- a/src/pages/workspace/view/viewTopMenu/ViewNavItem.tsx +++ b/src/pages/workspace/view/viewTopMenu/ViewNavItem.tsx @@ -9,6 +9,7 @@ import { useRenameViewMutation } from '../../../../modules/services/boardSlice.ts' import useInputState from '../../../../modules/hooks/useInputState.tsx' +import { useParams } from 'react-router-dom' interface Props { boardId: string @@ -33,6 +34,7 @@ export default function ({ const [newName, changeNewName, , setNewName] = useInputState(name) const [width, setWidth] = useState(100) const ref = useRef() + const params = useParams() const handleRenameClick = () => { setWidth(ref.current.offsetWidth) @@ -41,6 +43,7 @@ export default function ({ const handleRename = () => { renameView({ + viewShortId: params.view, viewId: _id, name: newName }) @@ -49,6 +52,7 @@ export default function ({ const handleDelete = () => { deleteView({ + viewShortId: params.view, boardId, viewId: _id }) From 7df26803ec99cb6d7e88ddc4b588e3b84681fcbc Mon Sep 17 00:00:00 2001 From: MarconLP <13001502+MarconLP@users.noreply.github.com> Date: Mon, 20 Feb 2023 15:31:29 +0100 Subject: [PATCH 12/18] fix optimistic updates for task dnd doesn't work --- src/modules/services/boardSlice.ts | 4 ++-- src/pages/workspace/view/board/BoardView.js | 6 +++--- src/pages/workspace/view/board/TaskGroup.tsx | 1 + 3 files changed, 6 insertions(+), 5 deletions(-) diff --git a/src/modules/services/boardSlice.ts b/src/modules/services/boardSlice.ts index 9bab6e5..0671397 100644 --- a/src/modules/services/boardSlice.ts +++ b/src/modules/services/boardSlice.ts @@ -1021,7 +1021,7 @@ export const boardApi = api.injectEndpoints({ method: 'PUT', body: { name } }), - invalidatesTags: (result, error, arg) => [arg.boardId], + invalidatesTags: (result, error, arg) => ['Board'], async onQueryStarted( { boardId, viewShortId }, { dispatch, queryFulfilled } @@ -1046,7 +1046,7 @@ export const boardApi = api.injectEndpoints({ method: 'PUT', body: { filters } }), - invalidatesTags: (result, error, arg) => [arg.boardId] + invalidatesTags: (result, error, arg) => ['Board'] }) }) }) diff --git a/src/pages/workspace/view/board/BoardView.js b/src/pages/workspace/view/board/BoardView.js index c84f0a3..4028bac 100644 --- a/src/pages/workspace/view/board/BoardView.js +++ b/src/pages/workspace/view/board/BoardView.js @@ -33,8 +33,8 @@ function BoardView({ board, members, hasPerms }, viewContainerRef) { ) const groupTasks = useMemo(() => { - if (!board) return const view = board?.views.find(x => x.id === params?.view) + if (!view) return if (!view.groupBy || view.groupBy === 'none') { const fuse = new Fuse(board.tasks, { keys: ['name'], @@ -51,7 +51,7 @@ function BoardView({ board, members, hasPerms }, viewContainerRef) { ref={viewContainerRef} hasPerms={hasPerms} boardId={board._id} - groupBy={board?.groupBy} + groupBy={view?.groupBy} attributes={board.attributes} members={members} color={'rgb(196, 196, 196)'} @@ -93,7 +93,7 @@ function BoardView({ board, members, hasPerms }, viewContainerRef) { hasPerms={hasPerms} groupedTasks={labels} boardId={board._id} - groupBy={board?.groupBy} + groupBy={view?.groupBy} attributes={board.attributes} members={members} color={label.color} diff --git a/src/pages/workspace/view/board/TaskGroup.tsx b/src/pages/workspace/view/board/TaskGroup.tsx index bff74f5..1d1448e 100644 --- a/src/pages/workspace/view/board/TaskGroup.tsx +++ b/src/pages/workspace/view/board/TaskGroup.tsx @@ -180,6 +180,7 @@ function TaskGroup(props: Props, viewContainerRef) { const sortedTasks = arrayMove(clonedTasks, activeIndex, overIndex) sortTask({ + viewShortId, newItems: flatten(buildTree(sortedTasks)), boardId, taskId: clonedTasks[activeIndex]._id, From a540a562e6290a7b8c242a4d973739266c6f03d1 Mon Sep 17 00:00:00 2001 From: MarconLP <13001502+MarconLP@users.noreply.github.com> Date: Fri, 24 Feb 2023 18:32:18 +0100 Subject: [PATCH 13/18] wait for new viewIds when creating new boards --- src/modules/services/boardSlice.ts | 2 -- src/modules/services/workspaceSlice.js | 18 ++++++++++++------ 2 files changed, 12 insertions(+), 8 deletions(-) diff --git a/src/modules/services/boardSlice.ts b/src/modules/services/boardSlice.ts index 0671397..1e522ff 100644 --- a/src/modules/services/boardSlice.ts +++ b/src/modules/services/boardSlice.ts @@ -366,8 +366,6 @@ export const boardApi = api.injectEndpoints({ { boardId, groupBy, viewId, viewShortId }, { dispatch, queryFulfilled } ) { - console.log('viewShortId') - console.log(viewShortId) const patchResult = dispatch( boardApi.util.updateQueryData( 'fetchBoard', diff --git a/src/modules/services/workspaceSlice.js b/src/modules/services/workspaceSlice.js index d0d585f..a432fef 100644 --- a/src/modules/services/workspaceSlice.js +++ b/src/modules/services/workspaceSlice.js @@ -4,14 +4,17 @@ import handleQueryError from '../../utils/handleQueryError' import pusher from '../../pusher/pusher.ts' const addBoard = data => { - const { workspace, workspaceId, spaceId, name, _id } = data + const { workspace, viewShortId, viewId, spaceId, name, _id } = data const board = { _id, name, - workspace: workspaceId, - attributes: [], - taskGroups: [], - sharing: false + views: [ + { + name: 'List', + _id: viewId, + id: viewShortId + } + ] } workspace.spaces.find(space => space._id === spaceId).boards.push(board) return workspace @@ -147,6 +150,7 @@ export const workspaceApi = api.injectEndpoints({ { workspaceId, workspaceIdFriendly, spaceId, name, _id }, { dispatch, queryFulfilled } ) { + const { data } = await queryFulfilled const patchResult = dispatch( workspaceApi.util.updateQueryData( 'fetchWorkspace', @@ -157,7 +161,9 @@ export const workspaceApi = api.injectEndpoints({ workspaceId, spaceId, name, - _id + _id, + viewId: data.message.viewId, + viewShortId: data.message.viewShortId }) ) ) From 42c5fa0acf59211d0cb151144c45ffa46a88900a Mon Sep 17 00:00:00 2001 From: MarconLP <13001502+MarconLP@users.noreply.github.com> Date: Fri, 24 Feb 2023 19:22:20 +0100 Subject: [PATCH 14/18] transform sharedBoard to a sharedView component to work with views --- src/App.js | 4 ++-- src/modules/services/boardSlice.ts | 8 +++++--- src/modules/services/sharedContentSlice.js | 6 +++--- src/pages/{SharedBoard.js => SharedView.js} | 16 +++++++--------- src/pages/workspace/view/board/BoardView.js | 3 +-- 5 files changed, 18 insertions(+), 19 deletions(-) rename src/pages/{SharedBoard.js => SharedView.js} (85%) diff --git a/src/App.js b/src/App.js index 032c4e0..f2f1f1c 100644 --- a/src/App.js +++ b/src/App.js @@ -2,12 +2,12 @@ import './App.css' import { Route, Routes } from 'react-router-dom' import ErrorPage from './utils/ErrorPage' import PrivateRouter from './PrivateRouter' -import SharedBoard from './pages/SharedBoard' +import SharedView from './pages/SharedView' function App() { return ( - } /> + } /> } /> } /> diff --git a/src/modules/services/boardSlice.ts b/src/modules/services/boardSlice.ts index 1e522ff..d4fcfee 100644 --- a/src/modules/services/boardSlice.ts +++ b/src/modules/services/boardSlice.ts @@ -4,6 +4,7 @@ import { toast } from 'react-hot-toast' import pusher from '../../pusher/pusher.ts' import handleQueryError from '../../utils/handleQueryError' import { flatten, removeChildrenOf } from '../../utils/taskUtils.ts' +import { current } from 'immer' const setGroupBy = ({ board, groupBy, viewId }) => { board.views.find(x => x._id === viewId).groupBy = groupBy @@ -203,8 +204,9 @@ const removeWatcher = ({ board, task, user }) => { const localTask = board.tasks.find(x => x._id === task._id) localTask.watcher = localTask.watcher.filter(x => x._id !== user._id) } -const setSharing = ({ board, share }) => { - board.sharing = share +const setSharing = ({ board, share, viewShortId }) => { + const view = board.views.find(x => x.id === viewShortId) + view.sharing = share } export const boardApi = api.injectEndpoints({ @@ -928,7 +930,7 @@ export const boardApi = api.injectEndpoints({ boardApi.util.updateQueryData( 'fetchBoard', { boardId, viewShortId }, - board => setSharing({ board, share }) + board => setSharing({ board, share, viewShortId }) ) ) try { diff --git a/src/modules/services/sharedContentSlice.js b/src/modules/services/sharedContentSlice.js index 2d67d00..d7b2048 100644 --- a/src/modules/services/sharedContentSlice.js +++ b/src/modules/services/sharedContentSlice.js @@ -4,8 +4,8 @@ import { flatten } from '../../utils/taskUtils.ts' export const boardApi = api.injectEndpoints({ endpoints: builder => ({ - fetchSharedBoard: builder.query({ - query: boardId => `board/share/${boardId}`, + fetchSharedView: builder.query({ + query: viewId => `view/share/${viewId}`, transformResponse: (response, meta, arg) => { return { ...response, @@ -23,4 +23,4 @@ export const boardApi = api.injectEndpoints({ }) }) -export const { useFetchSharedBoardQuery } = boardApi +export const { useFetchSharedViewQuery } = boardApi diff --git a/src/pages/SharedBoard.js b/src/pages/SharedView.js similarity index 85% rename from src/pages/SharedBoard.js rename to src/pages/SharedView.js index 3b32aac..8a48fd3 100644 --- a/src/pages/SharedBoard.js +++ b/src/pages/SharedView.js @@ -2,16 +2,12 @@ import { Link, useParams } from 'react-router-dom' import styles from '../styles/SharedBoard.module.scss' import { CircularProgress } from '@mui/material' import BoardView from './workspace/view/board/BoardView' -import { useFetchSharedBoardQuery } from '../modules/services/sharedContentSlice' +import { useFetchSharedViewQuery } from '../modules/services/sharedContentSlice' import { useCallback, useRef, useState } from 'react' -function SharedBoard() { - const { boardId } = useParams() - const { - data: board, - isError, - isLoading - } = useFetchSharedBoardQuery(boardId) +function SharedView() { + const { viewId } = useParams() + const { data: board, isError, isLoading } = useFetchSharedViewQuery(viewId) const viewContainerRef = useRef(null) const [bla, setBla] = useState(false) const setViewContainerRef = useCallback(node => { @@ -50,6 +46,7 @@ function SharedBoard() { ) } + const view = board?.views.find(x => x._id === viewId) return (
{}} /> @@ -69,4 +67,4 @@ function SharedBoard() { ) } -export default SharedBoard +export default SharedView diff --git a/src/pages/workspace/view/board/BoardView.js b/src/pages/workspace/view/board/BoardView.js index 4028bac..bc8bdf8 100644 --- a/src/pages/workspace/view/board/BoardView.js +++ b/src/pages/workspace/view/board/BoardView.js @@ -20,7 +20,7 @@ import { useAtom } from 'jotai' import { searchAtom } from '../../../../utils/atoms.ts' import { useParams } from 'react-router-dom' -function BoardView({ board, members, hasPerms }, viewContainerRef) { +function BoardView({ board, members, view, hasPerms }, viewContainerRef) { const [sortAttribute] = useSortAttributeMutation() const [sortTask] = useSortTaskMutation() const [groupedTasks, setGroupedTasks] = useState([]) @@ -33,7 +33,6 @@ function BoardView({ board, members, hasPerms }, viewContainerRef) { ) const groupTasks = useMemo(() => { - const view = board?.views.find(x => x.id === params?.view) if (!view) return if (!view.groupBy || view.groupBy === 'none') { const fuse = new Fuse(board.tasks, { From a1858364a670a704463c0ee0cc42d28837d94504 Mon Sep 17 00:00:00 2001 From: MarconLP <13001502+MarconLP@users.noreply.github.com> Date: Wed, 8 Mar 2023 09:55:54 +0100 Subject: [PATCH 15/18] make views a scroll container that can shrink to 0 --- src/pages/workspace/view/ViewTopMenu.js | 38 ++++++++++--------- .../view/viewTopMenu/NewViewMenu.tsx | 2 +- .../view/viewTopMenu/ViewNavItem.tsx | 2 +- 3 files changed, 22 insertions(+), 20 deletions(-) diff --git a/src/pages/workspace/view/ViewTopMenu.js b/src/pages/workspace/view/ViewTopMenu.js index 0540cb3..cc15b4f 100644 --- a/src/pages/workspace/view/ViewTopMenu.js +++ b/src/pages/workspace/view/ViewTopMenu.js @@ -60,41 +60,43 @@ function ViewTopMenu({ boardId, sideBarClosed, toggleSideBar }) { return (
-
+
{sideBarClosed && (
)} -
+
-
+

{!isFetching && !error && board?.name ? board?.name : '...'}

-
- {!isFetching && - !error && - board?.views.map(({ name, id, _id }) => ( - - ))} +
+
+ {!isFetching && + !error && + board?.views.map(({ name, id, _id }) => ( + + ))} +
diff --git a/src/pages/workspace/view/viewTopMenu/NewViewMenu.tsx b/src/pages/workspace/view/viewTopMenu/NewViewMenu.tsx index d1780bd..bae345e 100644 --- a/src/pages/workspace/view/viewTopMenu/NewViewMenu.tsx +++ b/src/pages/workspace/view/viewTopMenu/NewViewMenu.tsx @@ -19,7 +19,7 @@ function NewViewMenu({ boardId }: NewViewMenuProps) { } return ( - +
diff --git a/src/pages/workspace/view/viewTopMenu/ViewNavItem.tsx b/src/pages/workspace/view/viewTopMenu/ViewNavItem.tsx index 858a7fd..a7b44fa 100644 --- a/src/pages/workspace/view/viewTopMenu/ViewNavItem.tsx +++ b/src/pages/workspace/view/viewTopMenu/ViewNavItem.tsx @@ -66,7 +66,7 @@ export default function ({
handleViewClick(id)} - className="subpixel-antialiased flex justify-center items-center h-[60px] box-border cursor-pointer border-y-[3px] border-y-[white] border-solid" + className="min-w-[0] shrink-0 subpixel-antialiased flex justify-center items-center h-[60px] box-border cursor-pointer border-y-[3px] border-y-[white] border-solid" >
Date: Thu, 9 Mar 2023 08:15:42 +0100 Subject: [PATCH 16/18] use popperjs to show menu within overflow-hidden div --- .../view/viewTopMenu/ViewNavItem.tsx | 127 ++++++++++-------- src/utils/usePopper.ts | 41 ++++++ 2 files changed, 109 insertions(+), 59 deletions(-) create mode 100644 src/utils/usePopper.ts diff --git a/src/pages/workspace/view/viewTopMenu/ViewNavItem.tsx b/src/pages/workspace/view/viewTopMenu/ViewNavItem.tsx index a7b44fa..89902b6 100644 --- a/src/pages/workspace/view/viewTopMenu/ViewNavItem.tsx +++ b/src/pages/workspace/view/viewTopMenu/ViewNavItem.tsx @@ -10,6 +10,7 @@ import { } from '../../../../modules/services/boardSlice.ts' import useInputState from '../../../../modules/hooks/useInputState.tsx' import { useParams } from 'react-router-dom' +import { usePopper } from '../../../../utils/usePopper.ts' interface Props { boardId: string @@ -36,6 +37,12 @@ export default function ({ const ref = useRef() const params = useParams() + const [trigger, container] = usePopper({ + placement: 'bottom-end', + strategy: 'fixed', + modifiers: [{ name: 'offset', options: { offset: [0, 10] } }] + }) + const handleRenameClick = () => { setWidth(ref.current.offsetWidth) setRenaming(true) @@ -98,69 +105,71 @@ export default function ({ )} {active && ( - -
- -
- -
-
-
- - -
- - {({ active }) => ( -
-
-
- + + +
+ +
+
+
+ + +
+ + {({ active }) => ( +
+
+
+ +
+

+ Rename +

-

- Rename -

-
- )} - - - {({ active }) => ( -
-
-
- + )} + + + {({ active }) => ( +
+
+
+ +
+

+ Delete +

-

- Delete -

-
- )} - -
- - + )} + +
+
+
+
)}
diff --git a/src/utils/usePopper.ts b/src/utils/usePopper.ts new file mode 100644 index 0000000..79da025 --- /dev/null +++ b/src/utils/usePopper.ts @@ -0,0 +1,41 @@ +import { RefCallback, useRef, useCallback, useMemo } from 'react' +import { createPopper, Options } from '@popperjs/core' + +/** + * Example implementation to use Popper: https://popper.js.org/ + */ +export function usePopper( + options?: Partial +): [RefCallback, RefCallback] { + let reference = useRef(null) + let popper = useRef(null) + + let cleanupCallback = useRef(() => {}) + + let instantiatePopper = useCallback(() => { + if (!reference.current) return + if (!popper.current) return + + if (cleanupCallback.current) cleanupCallback.current() + + cleanupCallback.current = createPopper( + reference.current, + popper.current, + options + ).destroy + }, [reference, popper, cleanupCallback, options]) + + return useMemo( + () => [ + referenceDomNode => { + reference.current = referenceDomNode + instantiatePopper() + }, + popperDomNode => { + popper.current = popperDomNode + instantiatePopper() + } + ], + [reference, popper, instantiatePopper] + ) +} From ab451fc739472ce1cad1226f5132febc6a5938b1 Mon Sep 17 00:00:00 2001 From: MarconLP <13001502+MarconLP@users.noreply.github.com> Date: Thu, 9 Mar 2023 08:19:09 +0100 Subject: [PATCH 17/18] add active style to ViewNavItem menu --- .../workspace/view/viewTopMenu/ViewNavItem.tsx | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/src/pages/workspace/view/viewTopMenu/ViewNavItem.tsx b/src/pages/workspace/view/viewTopMenu/ViewNavItem.tsx index 89902b6..8b02cfa 100644 --- a/src/pages/workspace/view/viewTopMenu/ViewNavItem.tsx +++ b/src/pages/workspace/view/viewTopMenu/ViewNavItem.tsx @@ -129,7 +129,13 @@ export default function ({ className="h-8" onClick={handleRenameClick} > -
+
-
+
Date: Thu, 9 Mar 2023 08:41:09 +0100 Subject: [PATCH 18/18] move ViewNavItem into scroll-container --- src/pages/workspace/view/ViewTopMenu.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/pages/workspace/view/ViewTopMenu.js b/src/pages/workspace/view/ViewTopMenu.js index cc15b4f..4bd38af 100644 --- a/src/pages/workspace/view/ViewTopMenu.js +++ b/src/pages/workspace/view/ViewTopMenu.js @@ -96,8 +96,9 @@ function ViewTopMenu({ boardId, sideBarClosed, toggleSideBar }) { active={id === params.view} /> ))} + +
-