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 781f183..d4fcfee 100644
--- a/src/modules/services/boardSlice.ts
+++ b/src/modules/services/boardSlice.ts
@@ -4,9 +4,10 @@ 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 }) => {
- board.groupBy = groupBy
+const setGroupBy = ({ board, groupBy, viewId }) => {
+ board.views.find(x => x._id === viewId).groupBy = groupBy
}
const addTask = ({ board, newTaskName, taskGroupId, _id, author, level }) => {
const task = {
@@ -203,14 +204,17 @@ 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({
endpoints: builder => ({
fetchBoard: builder.query({
- query: boardId => `board/${boardId}`,
+ query: ({ boardId, viewShortId = '' }) =>
+ `board/${boardId}/${viewShortId}`,
+ keepUnusedDataFor: 0,
transformResponse: (response: any, meta, arg) => {
return {
...response,
@@ -218,7 +222,7 @@ export const boardApi = api.injectEndpoints({
}
},
async onCacheEntryAdded(
- boardId,
+ { boardId },
{ updateCachedData, cacheDataLoaded, cacheEntryRemoved }
) {
let channel
@@ -341,7 +345,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,25 +353,28 @@ export const boardApi = api.injectEndpoints({
window.location.pathname
)
} catch {
- toast('Could not find that Board')
+ if (boardId === undefined)
+ toast('Could not find that Board')
}
}
}),
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, viewShortId },
{ dispatch, queryFulfilled }
) {
const patchResult = dispatch(
boardApi.util.updateQueryData(
'fetchBoard',
- boardId,
- board => setGroupBy({ board, groupBy })
+ { boardId, viewShortId },
+ board => {
+ setGroupBy({ board, groupBy, viewId })
+ }
)
)
try {
@@ -388,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,
@@ -413,46 +429,19 @@ 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}`,
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 })
}
@@ -477,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,
@@ -516,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,
@@ -548,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,
@@ -579,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,
@@ -608,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,
@@ -637,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,
@@ -699,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,
@@ -728,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 })
)
)
@@ -752,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 })
)
)
@@ -776,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 })
)
)
@@ -799,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 })
)
)
@@ -823,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 })
)
)
@@ -847,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 })
)
)
@@ -871,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 })
)
)
@@ -895,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 })
)
)
@@ -913,20 +917,20 @@ 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(
- { boardId, share },
+ { boardId, share, viewShortId },
{ dispatch, queryFulfilled }
) {
const patchResult = dispatch(
boardApi.util.updateQueryData(
'fetchBoard',
- boardId,
- board => setSharing({ board, share })
+ { boardId, viewShortId },
+ board => setSharing({ board, share, viewShortId })
)
)
try {
@@ -937,20 +941,95 @@ 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 => {}
+ )
+ )
+ try {
+ await queryFulfilled
+ } catch (err) {
+ if (handleQueryError({ err })) patchResult.undo()
+ }
+ }
+ }),
+ addView: builder.mutation({
+ query: ({ boardId, type }) => ({
+ url: `view/${boardId}/addView`,
+ method: 'POST',
+ body: { type }
+ }),
+ invalidatesTags: (result, error, arg) => [arg.boardId],
+ async onQueryStarted(
+ { boardId, viewShortId },
+ { dispatch, queryFulfilled }
+ ) {
+ const patchResult = dispatch(
+ boardApi.util.updateQueryData(
+ 'fetchBoard',
+ { boardId, viewShortId },
+ board => {}
+ )
+ )
+ try {
+ await queryFulfilled
+ } catch (err) {
+ if (handleQueryError({ err })) patchResult.undo()
+ }
+ }
+ }),
+ deleteView: builder.mutation({
+ query: ({ boardId, viewId }) => ({
+ url: `view/${boardId}/deleteView`,
+ method: 'DELETE',
+ body: { viewId }
+ }),
+ invalidatesTags: (result, error, arg) => [arg.boardId],
+ async onQueryStarted(
+ { boardId, viewShortId },
+ { dispatch, queryFulfilled }
+ ) {
+ const patchResult = dispatch(
+ boardApi.util.updateQueryData(
+ 'fetchBoard',
+ { boardId, viewShortId },
+ board => {}
+ )
+ )
+ try {
+ await queryFulfilled
+ } catch (err) {
+ if (handleQueryError({ err })) patchResult.undo()
+ }
+ }
+ }),
+ renameView: builder.mutation({
+ query: ({ viewId, name }) => ({
+ url: `view/${viewId}/renameView`,
+ method: 'PUT',
+ body: { name }
+ }),
+ invalidatesTags: (result, error, arg) => ['Board'],
+ async onQueryStarted(
+ { boardId, viewShortId },
+ { dispatch, queryFulfilled }
+ ) {
+ const patchResult = dispatch(
+ boardApi.util.updateQueryData(
+ 'fetchBoard',
+ { boardId, viewShortId },
board => {}
)
)
@@ -962,12 +1041,12 @@ 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 }
}),
- invalidatesTags: (result, error, arg) => [arg.boardId]
+ invalidatesTags: (result, error, arg) => ['Board']
})
})
})
@@ -995,5 +1074,8 @@ export const {
useRemoveWatcherMutation,
useSetSharingMutation,
useAddSubtaskMutation,
- useSetFiltersMutation
+ useSetFiltersMutation,
+ useAddViewMutation,
+ useDeleteViewMutation,
+ useRenameViewMutation
} = boardApi
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/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
})
)
)
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.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) {
}
/>
+ }
+ />
+
{provided => (
{
+ if (
+ window.innerWidth <=
+ 576
+ )
+ props.toggleSideBar()
+ }}
{...provided.draggableProps}
ref={
provided.innerRef
@@ -347,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 8f12c2e..90f2f6b 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,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 } = useFetchBoardQuery(boardId)
+ const { isFetching, error } = useFetchBoardQuery({
+ boardId,
+ viewShortId: params.view
+ })
const viewContainerRef = useRef(null)
const [bla, setBla] = useState(false)
const setViewContainerRef = useCallback(node => {
@@ -27,6 +30,10 @@ function View({ level, view, sidebarOpen, toggleSideBar }) {
}
}, [])
+ if (error && !isFetching && boardId === undefined) {
+ return
+ }
+
if (level === 'list') {
if (view === 'list')
return (
@@ -37,20 +44,25 @@ function View({ level, view, sidebarOpen, toggleSideBar }) {
sideBarClosed={!sidebarOpen}
/>
- {isFetching && }
+ {(!params.view || 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..1421daf 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 = () => {
@@ -13,7 +13,12 @@ function GroupByPopover({ board, groupByOpen, setGroupByOpen }) {
}
const handleGroupByChange = _id => {
- setGroupBy({ boardId: board._id, groupBy: _id })
+ setGroupBy({
+ boardId: board._id,
+ viewId: view._id,
+ viewShortId: view.id,
+ groupBy: _id
+ })
handleClose()
}
@@ -48,7 +53,7 @@ function GroupByPopover({ board, groupByOpen, setGroupByOpen }) {
}
>
{attribute.name}
- {board?.groupBy === attribute._id && (
+ {view?.groupBy === attribute._id && (
)}
@@ -59,7 +64,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..dac4821 100644
--- a/src/pages/workspace/topMenu/ShareDialog.js
+++ b/src/pages/workspace/topMenu/ShareDialog.js
@@ -5,13 +5,17 @@ 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, board }) {
+function ShareDialog({ handleClose, open, boardId, view }) {
+ const params = useParams()
const [setSharing] = useSetSharingMutation()
const toggleShare = (e, share) => {
setSharing({
- boardId: board._id,
+ viewShortId: params.view,
+ viewId: view._id,
+ boardId,
share
})
}
@@ -30,14 +34,14 @@ function ShareDialog({ handleClose, open, board }) {
- {board.sharing && (
+ {view.sharing && (
@@ -46,7 +50,7 @@ function ShareDialog({ handleClose, open, board }) {
diff --git a/src/pages/workspace/view/Board.js b/src/pages/workspace/view/Board.js
index e905d66..c2bc378 100644
--- a/src/pages/workspace/view/Board.js
+++ b/src/pages/workspace/view/Board.js
@@ -1,28 +1,29 @@
import '../../../styles/Board.css'
-import { Navigate, useParams } from 'react-router-dom'
+import { useParams } from 'react-router-dom'
import { useFetchWorkspaceQuery } from '../../../modules/services/workspaceSlice'
import { useFetchBoardQuery } from '../../../modules/services/boardSlice.ts'
import BoardView from './board/BoardView'
import { forwardRef, useCallback } from 'react'
-function Board(props, viewContainerRef) {
+function Board({ boardId }, viewContainerRef) {
const params = useParams()
const { data: workspace } = useFetchWorkspaceQuery(params.workspace)
- const { data: board, error, isFetching } = useFetchBoardQuery(props.boardId)
+ const { data: board } = useFetchBoardQuery({
+ boardId,
+ viewShortId: params.view
+ })
+ const view = board?.views.find(x => x.id === params.view)
const hasPerms = useCallback(permission => {
return true
}, [])
- if (error && !isFetching) {
- return
- }
-
return (
)
diff --git a/src/pages/workspace/view/ViewTopMenu.js b/src/pages/workspace/view/ViewTopMenu.js
index 8288c04..4bd38af 100644
--- a/src/pages/workspace/view/ViewTopMenu.js
+++ b/src/pages/workspace/view/ViewTopMenu.js
@@ -8,29 +8,48 @@ 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'
+import NewViewMenu from './viewTopMenu/NewViewMenu.tsx'
+import ViewNavItem from './viewTopMenu/ViewNavItem.tsx'
function ViewTopMenu({ boardId, sideBarClosed, toggleSideBar }) {
- const { data: board } = useFetchBoardQuery(boardId)
+ const params = useParams()
+ const {
+ data: board,
+ isFetching,
+ error
+ } = useFetchBoardQuery({ boardId, viewShortId: 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(() => {
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
}
@@ -41,63 +60,46 @@ function ViewTopMenu({ boardId, sideBarClosed, toggleSideBar }) {
return (
-
+
{sideBarClosed && (
)}
-
+
-
-
{board?.name || '...'}
+
+
+ {!isFetching && !error && board?.name
+ ? board?.name
+ : '...'}
+
+
+
+
+ {!isFetching &&
+ !error &&
+ board?.views.map(({ name, id, _id }) => (
+
+ ))}
+
+
+
- {/*
*/}
- {/* {[*/}
- {/* {*/}
- {/* name: 'List',*/}
- {/* icon:
,*/}
- {/* active: true*/}
- {/* },*/}
- {/* {*/}
- {/* name: 'Bugs',*/}
- {/* icon:
,*/}
- {/* active: false*/}
- {/* }*/}
- {/* ].map(({ name, icon, active }) => (*/}
- {/*
*/}
- {/*
*/}
- {/* {icon}*/}
- {/* */}
- {/* {name}*/}
- {/* */}
- {/*
*/}
- {/*
*/}
- {/* ))}*/}
- {/*
*/}
- {board && (
+ {view && (
@@ -138,7 +141,9 @@ function ViewTopMenu({ boardId, sideBarClosed, toggleSideBar }) {
{/*
*/}
- {board &&
}
+ {board && view && (
+
+ )}
{
- if (!board) return
- if (!board.groupBy || board.groupBy === 'none') {
+ if (!view) return
+ if (!view.groupBy || view.groupBy === 'none') {
const fuse = new Fuse(board.tasks, {
keys: ['name'],
shouldSort: false
@@ -44,10 +46,11 @@ function BoardView({ board, members, hasPerms }, viewContainerRef) {
return (
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)
@@ -84,11 +87,12 @@ function BoardView({ board, members, hasPerms }, viewContainerRef) {
return labels.map(label => (
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]
@@ -145,7 +151,11 @@ function BoardView({ board, members, hasPerms }, viewContainerRef) {
boardId: board._id
})
} else if (/^attribute /gm.test(result.type)) {
- sortAttribute({ result, boardId: board._id })
+ sortAttribute({
+ viewShortId: params.view,
+ result,
+ boardId: board._id
+ })
}
}
diff --git a/src/pages/workspace/view/board/TaskGroup.tsx b/src/pages/workspace/view/board/TaskGroup.tsx
index 807f68c..1d1448e 100644
--- a/src/pages/workspace/view/board/TaskGroup.tsx
+++ b/src/pages/workspace/view/board/TaskGroup.tsx
@@ -40,10 +40,11 @@ interface Props {
taskGroupId: string
members: Member[]
groupedTasks: Label[]
+ viewShortId: string
}
function TaskGroup(props: Props, viewContainerRef) {
- const { hasPerms, tasks, boardId, groupBy, attributes } = props
+ const { hasPerms, tasks, boardId, groupBy, attributes, viewShortId } = props
const [collapsed, setCollapsed] = useState(false)
const [activeItem, setActiveItem] = useState(null)
const [collapsedItems, setCollapsedItems] = useState([])
@@ -179,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,
@@ -357,6 +359,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 9881148..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)
+ const { data: board } = useFetchBoardQuery({
+ boardId,
+ viewShortId: params.view
+ })
const { data: user } = useFetchUserQuery()
const [name, changeName] = useInputState(task.name)
const [anchor, setAnchor] = useState(null)
@@ -44,7 +47,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(() => {
@@ -62,7 +67,8 @@ function TaskModal({ task, boardId, hasPerms, members }: Props) {
type: 'name',
value: name,
boardId,
- taskId: task._id
+ taskId: task._id,
+ viewShortId: params.view
})
}
@@ -70,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 bcd2a33..3f92195 100644
--- a/src/pages/workspace/view/board/taskGroup/taskModal/Activity.js
+++ b/src/pages/workspace/view/board/taskGroup/taskModal/Activity.js
@@ -2,9 +2,14 @@ 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,
+ 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 8c7b270..e02f2e5 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'],
@@ -15,8 +16,12 @@ 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 params = useParams()
+ const { data: board } = useFetchBoardQuery({
+ boardId,
+ viewShortId: params?.view
+ })
const [setFilters] = useSetFiltersMutation()
const [filters, setFiltersState] = useState([])
@@ -81,21 +86,23 @@ 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 === view.filters) return
if (!open) {
setFilters({
- boardId,
+ viewId: view._id,
filters
})
}
}
const used = filters.length >= 1
+ if (!board) return
return (
diff --git a/src/pages/workspace/view/viewTopMenu/NewViewMenu.tsx b/src/pages/workspace/view/viewTopMenu/NewViewMenu.tsx
new file mode 100644
index 0000000..bae345e
--- /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 (
+
+ )
+}
+
+export default NewViewMenu
diff --git a/src/pages/workspace/view/viewTopMenu/ViewNavItem.tsx b/src/pages/workspace/view/viewTopMenu/ViewNavItem.tsx
new file mode 100644
index 0000000..8b02cfa
--- /dev/null
+++ b/src/pages/workspace/view/viewTopMenu/ViewNavItem.tsx
@@ -0,0 +1,190 @@
+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, useEffect, useRef, useState } from 'react'
+import * as React from 'react'
+import {
+ useDeleteViewMutation,
+ useRenameViewMutation
+} 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
+ _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 [renameView] = useRenameViewMutation()
+ const [renaming, setRenaming] = useState(false)
+ const [newName, changeNewName, , setNewName] = useInputState(name)
+ const [width, setWidth] = useState(100)
+ 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)
+ }
+
+ const handleRename = () => {
+ renameView({
+ viewShortId: params.view,
+ viewId: _id,
+ name: newName
+ })
+ setRenaming(false)
+ }
+
+ const handleDelete = () => {
+ deleteView({
+ viewShortId: params.view,
+ boardId,
+ viewId: _id
+ })
+ }
+
+ useEffect(() => {
+ setNewName(name)
+ }, [name])
+
+ return (
+ handleViewClick(id)}
+ 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"
+ >
+
+
+ {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 && (
+
+ )}
+
+
+ )
+}
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]
+ )
+}