From 9958443c14330e5b333382b25dcb70b5e9e494ad Mon Sep 17 00:00:00 2001 From: david Date: Fri, 20 Mar 2026 18:08:39 +0300 Subject: [PATCH] Add right sidebar to board detail page --- frontend/package-lock.json | 16 ++ frontend/package.json | 1 + frontend/src/App.tsx | 11 +- frontend/src/components/BoardSidebar.tsx | 40 +++++ frontend/src/components/NarrowPageLayout.tsx | 5 + frontend/src/components/WidePageLayout.tsx | 5 + frontend/src/pages/BoardDetail.tsx | 156 +++++++++---------- frontend/src/pages/BoardEpics.tsx | 35 +++++ frontend/src/pages/Boards.tsx | 57 +++---- frontend/tsconfig.node.json | 5 +- frontend/vite.config.ts | 6 + 11 files changed, 226 insertions(+), 111 deletions(-) create mode 100644 frontend/src/components/BoardSidebar.tsx create mode 100644 frontend/src/components/NarrowPageLayout.tsx create mode 100644 frontend/src/components/WidePageLayout.tsx create mode 100644 frontend/src/pages/BoardEpics.tsx diff --git a/frontend/package-lock.json b/frontend/package-lock.json index 0bd44d1..7342578 100644 --- a/frontend/package-lock.json +++ b/frontend/package-lock.json @@ -23,6 +23,7 @@ "@testing-library/jest-dom": "^6.1.5", "@testing-library/react": "^14.1.2", "@testing-library/user-event": "^14.5.1", + "@types/node": "^25.5.0", "@types/react": "^18.3.28", "@types/react-dom": "^18.3.7", "@typescript-eslint/eslint-plugin": "^8.56.1", @@ -1614,6 +1615,15 @@ "integrity": "sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==", "dev": true }, + "node_modules/@types/node": { + "version": "25.5.0", + "resolved": "https://registry.npmjs.org/@types/node/-/node-25.5.0.tgz", + "integrity": "sha512-jp2P3tQMSxWugkCUKLRPVUpGaL5MVFwF8RDuSRztfwgN1wmqJeMSbKlnEtQqU8UrhTmzEmZdu2I6v2dpp7XIxw==", + "dev": true, + "dependencies": { + "undici-types": "~7.18.0" + } + }, "node_modules/@types/prop-types": { "version": "15.7.15", "resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.15.tgz", @@ -7018,6 +7028,12 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/undici-types": { + "version": "7.18.2", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.18.2.tgz", + "integrity": "sha512-AsuCzffGHJybSaRrmr5eHr81mwJU3kjw6M+uprWvCXiNeN9SOGwQ3Jn8jb8m3Z6izVgknn1R0FTCEAP2QrLY/w==", + "dev": true + }, "node_modules/universalify": { "version": "0.2.0", "resolved": "https://registry.npmjs.org/universalify/-/universalify-0.2.0.tgz", diff --git a/frontend/package.json b/frontend/package.json index c7df5ce..c176716 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -29,6 +29,7 @@ "@testing-library/jest-dom": "^6.1.5", "@testing-library/react": "^14.1.2", "@testing-library/user-event": "^14.5.1", + "@types/node": "^25.5.0", "@types/react": "^18.3.28", "@types/react-dom": "^18.3.7", "@typescript-eslint/eslint-plugin": "^8.56.1", diff --git a/frontend/src/App.tsx b/frontend/src/App.tsx index faf9ae7..59e60db 100644 --- a/frontend/src/App.tsx +++ b/frontend/src/App.tsx @@ -16,6 +16,7 @@ import { Boards } from './pages/Boards'; import { BoardCreate } from './pages/BoardCreate'; import { BoardEdit } from './pages/BoardEdit'; import { BoardDetail } from './pages/BoardDetail'; +import { BoardEpics } from './pages/BoardEpics'; import { CardDetail } from './pages/CardDetail'; const App = () => { @@ -35,7 +36,7 @@ const App = () => {
-
+
{ } /> + + + + } + /> +

Board Menu

+ +
+ ); +} diff --git a/frontend/src/components/NarrowPageLayout.tsx b/frontend/src/components/NarrowPageLayout.tsx new file mode 100644 index 0000000..d592e51 --- /dev/null +++ b/frontend/src/components/NarrowPageLayout.tsx @@ -0,0 +1,5 @@ +import { ReactNode } from 'react'; + +export const NarrowPageLayout = ({ children }: { children: ReactNode }) => { + return
{children}
; +}; diff --git a/frontend/src/components/WidePageLayout.tsx b/frontend/src/components/WidePageLayout.tsx new file mode 100644 index 0000000..4d88706 --- /dev/null +++ b/frontend/src/components/WidePageLayout.tsx @@ -0,0 +1,5 @@ +import { ReactNode } from 'react'; + +export const WidePageLayout = ({ children }: { children: ReactNode }) => { + return
{children}
; +}; diff --git a/frontend/src/pages/BoardDetail.tsx b/frontend/src/pages/BoardDetail.tsx index 12e532b..d2473cf 100644 --- a/frontend/src/pages/BoardDetail.tsx +++ b/frontend/src/pages/BoardDetail.tsx @@ -5,6 +5,7 @@ import { useListMutations } from '../hooks/useListMutations'; import { SortableKanbanColumn } from '../components/kanban/SortableKanbanColumn'; import { CreateListModal } from '../components/kanban/CreateListModal'; import { CardPreviewModal } from '../components/CardPreviewModal'; +import { BoardSidebar } from '../components/BoardSidebar'; import { useModal } from '../context/modals/useModal'; import { DndContext, @@ -20,6 +21,7 @@ import { import { SortableContext, horizontalListSortingStrategy } from '@dnd-kit/sortable'; import { Card as CardType, ListWithCards } from '../types/kanban'; import { useState } from 'react'; +import { WidePageLayout } from '@/components/WidePageLayout'; export function BoardDetail() { const { id } = useParams<{ id: string }>(); @@ -65,12 +67,8 @@ export function BoardDetail() { const handleDragOver = (event: DragOverEvent) => { const { active, over } = event; - // console.log('---handleDragOver', event); if (!over) return; - // const activeId = parseInt(active.id as string); - // const overId = parseInt(over.id as string); - const overIdStr = (over.id as string).split('_')[1]; const overId = parseInt(overIdStr, 10); const activeIdStr = (active.id as string).split('_')[1]; @@ -105,8 +103,6 @@ export function BoardDetail() { // Handle column reordering if (activeType === 'COLUMN') { - // todo find over column id, - let overListIndex = -1; const activeList = board.lists.find((l) => l.id === activeId); if (overType === 'CARD') { @@ -118,12 +114,7 @@ export function BoardDetail() { overListIndex = board.lists.findIndex((l) => l.id === overId); } - // console.log('-------active.id', active.id) - // console.log('-------overType.id', overType) - // console.log('-------overListIndex', overListIndex) - const activeListIndex = board.lists.findIndex((l) => l.id === activeId); - // overListIndex = board.lists.findIndex((l) => l.id === overId); if (activeListIndex === -1 || overListIndex === -1 || !activeList) return; @@ -133,15 +124,6 @@ export function BoardDetail() { reorderedLists.splice(overListIndex, 0, movedList); await updateList(activeList.id, { name: activeList.name, pos: overListIndex }); - - // // Update all list positions - // for (let i = 0; i < reorderedLists.length; i++) { - // const list = reorderedLists[i]; - // if (list.pos !== i) { - // await updateList(list.id, { name: list.name, pos: i }); - // } - // } - return; } @@ -236,71 +218,81 @@ export function BoardDetail() { return (
-
-
- - ← Back to Boards - -

{board.name}

- {board.description &&

{board.description}

} + +
+
+ + ← Back to Boards + +

{board.name}

+ {board.description &&

{board.description}

} +
+
+ + Edit Board + + +
-
- - Edit Board - - + + +
+
+
+ + `COLUMN_${list.id}`)} + strategy={horizontalListSortingStrategy} + > +
+ {board.lists.map((list) => ( + handleEditList(list.id, name)} + onListDelete={() => handleDeleteList(list.id)} + /> + ))} +
+
+ + + {activeCard ? ( +
+

{activeCard.name}

+
+ ) : activeList ? ( +
+

{activeList.name}

+ + {activeList.cards.length} cards + +
+ ) : null} +
+
+
+ +
- - - `COLUMN_${list.id}`)} - strategy={horizontalListSortingStrategy} - > -
- {board.lists.map((list) => ( - handleEditList(list.id, name)} - onListDelete={() => handleDeleteList(list.id)} - /> - ))} -
-
- - - {activeCard ? ( -
-

{activeCard.name}

-
- ) : activeList ? ( -
-

{activeList.name}

- - {activeList.cards.length} cards - -
- ) : null} -
-
); } diff --git a/frontend/src/pages/BoardEpics.tsx b/frontend/src/pages/BoardEpics.tsx new file mode 100644 index 0000000..76df061 --- /dev/null +++ b/frontend/src/pages/BoardEpics.tsx @@ -0,0 +1,35 @@ +import { useParams, Link } from 'react-router-dom'; +import { WidePageLayout } from '../components/WidePageLayout'; + +export function BoardEpics() { + const { id } = useParams<{ id: string }>(); + + return ( +
+ +
+ + ← Back to Board + +

Epics

+

Manage and view epics for this board

+
+
+ + +
+
+
📋
+

Epics Coming Soon

+

+ This page will allow you to create and manage epics for your board. +

+
+
+
+
+ ); +} diff --git a/frontend/src/pages/Boards.tsx b/frontend/src/pages/Boards.tsx index e2fc694..b01af59 100644 --- a/frontend/src/pages/Boards.tsx +++ b/frontend/src/pages/Boards.tsx @@ -1,42 +1,45 @@ import { Link } from 'react-router-dom'; import { useBoards } from '../hooks/useBoards'; import { BoardCard } from '../components/kanban/BoardCard'; +import { NarrowPageLayout } from '@/components/NarrowPageLayout'; export function Boards() { const { boards, deleteBoard } = useBoards(); return ( -
-
-
-

My Boards

-

Manage your Kanban boards

-
- - + Create Board - -
- - {boards.length === 0 ? ( -
-

No boards yet

+ +
+
+
+

My Boards

+

Manage your Kanban boards

+
- Create your first board + + Create Board
- ) : ( -
- {boards.map((board) => ( - - ))} -
- )} -
+ + {boards.length === 0 ? ( +
+

No boards yet

+ + Create your first board + +
+ ) : ( +
+ {boards.map((board) => ( + + ))} +
+ )} +
+ ); } diff --git a/frontend/tsconfig.node.json b/frontend/tsconfig.node.json index 099658c..a5ed6df 100644 --- a/frontend/tsconfig.node.json +++ b/frontend/tsconfig.node.json @@ -4,7 +4,10 @@ "skipLibCheck": true, "module": "ESNext", "moduleResolution": "bundler", - "allowSyntheticDefaultImports": true + "allowSyntheticDefaultImports": true, + "paths": { + "@/*": ["./src/*"] + } }, "include": ["vite.config.ts"] } \ No newline at end of file diff --git a/frontend/vite.config.ts b/frontend/vite.config.ts index 34243ce..74e3e7e 100644 --- a/frontend/vite.config.ts +++ b/frontend/vite.config.ts @@ -1,9 +1,15 @@ import { defineConfig } from 'vite' import react from '@vitejs/plugin-react' +import path from 'path' // https://vitejs.dev/config/ export default defineConfig({ plugins: [react()], + resolve: { + alias: { + '@': path.resolve(__dirname, './src'), + }, + }, server: { port: 3000, proxy: {