Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
27 commits
Select commit Hold shift + click to select a range
6a87f81
feature: install Kanban from reui, replace kanban
AlexandrNel May 28, 2026
4a00907
fix: fix gidratation error
AlexandrNel May 28, 2026
70c4253
feat: add checkbox
AlexandrNel May 28, 2026
c63b107
refactor: update kanban board and mock data
AlexandrNel May 28, 2026
beb3c4e
refactor: add link highligting for projects
AlexandrNel May 28, 2026
83b14fe
refactor: enhance KanbanBoard layout and improve column handle visibi…
AlexandrNel May 29, 2026
67e3128
refactor: update MockBoard structure and enhance TaskColumn with icons
AlexandrNel May 29, 2026
648975c
refactor: create Task component and update TaskCard integration in Ta…
AlexandrNel May 29, 2026
1a50435
refactor: enhance ProjectKanban and TaskColumn components with improv…
AlexandrNel May 31, 2026
a7b7664
feat(task): add entitry 'task'
AlexandrNel Jun 2, 2026
936aab7
feat(board): add entitry 'board'
AlexandrNel Jun 2, 2026
922b215
feat: add useProjectStore
AlexandrNel Jun 2, 2026
f435c5e
feat(board): add all schemas, types, api routes and queires
AlexandrNel Jun 2, 2026
45cd5b1
refactor(task): remove TaskCard component and clean up exports
AlexandrNel Jun 3, 2026
66a3785
feat(color-picker): add ColorPicker component and color constants
AlexandrNel Jun 3, 2026
a0eb4a9
feat(task): implement CreateTask components and hooks for task creation
AlexandrNel Jun 3, 2026
596f708
feat(board): add CreateBoard components, form, and hooks for board cr…
AlexandrNel Jun 3, 2026
ac2b134
feat(board/remove/column): add RemoveColumn components, form, and hoo…
AlexandrNel Jun 3, 2026
5bd82a2
feat(board): implement board management with Zustand store and Kanban…
AlexandrNel Jun 3, 2026
67a1b58
refactor(config): update steiger configuration to disable public-api …
AlexandrNel Jun 3, 2026
7514a8d
feat(board/column): add CreateBoardColumn components, form, and dialo…
AlexandrNel Jun 3, 2026
c9ff640
feat(board/remove/column): enhance useRemoveColumn hook and RemoveCol…
AlexandrNel Jun 3, 2026
7c94933
feat(board/column/create): add color selection with ColorPicker and d…
AlexandrNel Jun 3, 2026
de47e36
chore: delete console.log
AlexandrNel Jun 3, 2026
3021ecf
feat(board): enhance useBoardsPage with error handling and refetch ca…
AlexandrNel Jun 3, 2026
c3a5ab9
Merge branch 'dev' into refactor/add-new-kanban
AlexandrNel Jun 3, 2026
17110d4
fix(ProjectBoardsPage): update PageProps type definition for params
AlexandrNel Jun 3, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 7 additions & 1 deletion app/layout.tsx
Original file line number Diff line number Diff line change
@@ -1,9 +1,15 @@
import 'app/styles/global.css';
import { AppProviders } from 'app/providers/AppProviders';

import { Inter } from 'next/font/google';

const inter = Inter({
subsets: ['cyrillic'],
});

export default function RootLayout({ children }: { children: React.ReactNode }) {
return (
<html lang="en">
<html lang="en" className={inter.className}>
<body>
<AppProviders>{children}</AppProviders>
</body>
Expand Down
3 changes: 2 additions & 1 deletion components.json
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
"hooks": "shared/hooks/shadcn"
},
"registries": {
"@kibo-ui": "https://www.kibo-ui.com/r/{name}.json"
"@kibo-ui": "https://www.kibo-ui.com/r/{name}.json",
"@reui": "https://reui.io/r/{style}/{name}.json"
}
}
173 changes: 173 additions & 0 deletions src/entities/board/api/http.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,173 @@
import { api } from 'shared/api';
import * as SBoard from '../model/schemas';
import * as TBoard from '../model/types';

export class BoardHttp {
static getBoardList(projectId: string, signal?: AbortSignal) {
return api<TBoard.BoardListResponse>({
url: `/projects/${projectId}/boards`,
method: 'GET',
contracts: {
response: SBoard.BoardListResponse,
},
signal,
});
}

static getBoard(projectId: string, id: string, signal?: AbortSignal) {
return api<TBoard.BoardResponse>({
url: `/projects/${projectId}/boards/${id}`,
method: 'GET',
contracts: {
response: SBoard.Board,
},
signal,
});
}

static createBoard(projectId: string, data: TBoard.CreateBoardBody) {
return api<TBoard.CreateBoardResponse>({
url: `/projects/${projectId}/boards`,
method: 'POST',
data,
contracts: {
body: SBoard.CreateBoardBody,
response: SBoard.CreateBoardResponse,
},
});
}

static updateBoard(projectId: string, id: string, data: TBoard.UpdateBoardBody) {
return api<TBoard.ActionResponse>({
url: `/projects/${projectId}/boards/${id}`,
method: 'PATCH',
data,
contracts: {
body: SBoard.UpdateBoardBody,
response: SBoard.ActionResponse,
},
});
}

static removeBoard(projectId: string, id: string) {
return api<TBoard.ActionResponse>({
url: `/projects/${projectId}/boards/${id}`,
method: 'DELETE',
contracts: {
response: SBoard.ActionResponse,
},
});
}

static getBoardColumnList(boardId: string, signal?: AbortSignal) {
return api<TBoard.BoardColumnListResponse>({
url: `/boards/${boardId}/columns`,
method: 'GET',
contracts: {
response: SBoard.BoardColumnListResponse,
},
signal,
});
}

static getBoardColumn(boardId: string, id: string, signal?: AbortSignal) {
return api<TBoard.BoardColumnResponse>({
url: `/boards/${boardId}/columns/${id}`,
method: 'GET',
contracts: {
response: SBoard.BoardColumn,
},
signal,
});
}

static createBoardColumn(boardId: string, data: TBoard.CreateBoardColumnBody) {
return api<TBoard.CreateBoardColumnResponse>({
url: `/boards/${boardId}/columns`,
method: 'POST',
data,
contracts: {
body: SBoard.CreateBoardColumnBody,
response: SBoard.CreateBoardColumnResponse,
},
});
}

static updateBoardColumn(boardId: string, id: string, data: TBoard.UpdateBoardColumnBody) {
return api<TBoard.ActionResponse>({
url: `/boards/${boardId}/columns/${id}`,
method: 'PATCH',
data,
contracts: {
body: SBoard.UpdateBoardColumnBody,
response: SBoard.ActionResponse,
},
});
}

static removeBoardColumn(boardId: string, id: string) {
return api<TBoard.ActionResponse>({
url: `/boards/${boardId}/columns/${id}`,
method: 'DELETE',
contracts: {
response: SBoard.ActionResponse,
},
});
}

static getBoardViewList(boardId: string, signal?: AbortSignal) {
return api<TBoard.BoardViewListResponse>({
url: `/boards/${boardId}/views`,
method: 'GET',
contracts: {
response: SBoard.BoardViewListResponse,
},
signal,
});
}

static getBoardView(boardId: string, id: string, signal?: AbortSignal) {
return api<TBoard.BoardViewResponse>({
url: `/boards/${boardId}/views/${id}`,
method: 'GET',
contracts: {
response: SBoard.BoardView,
},
signal,
});
}

static createBoardView(boardId: string, data: TBoard.CreateBoardViewBody) {
return api<TBoard.CreateBoardViewResponse>({
url: `/boards/${boardId}/views`,
method: 'POST',
data,
contracts: {
body: SBoard.CreateBoardViewBody,
response: SBoard.CreateBoardViewResponse,
},
});
}

static updateBoardView(boardId: string, id: string, data: TBoard.UpdateBoardViewBody) {
return api<TBoard.ActionResponse>({
url: `/boards/${boardId}/views/${id}`,
method: 'PATCH',
data,
contracts: {
body: SBoard.UpdateBoardViewBody,
response: SBoard.ActionResponse,
},
});
}

static removeBoardView(boardId: string, id: string) {
return api<TBoard.ActionResponse>({
url: `/boards/${boardId}/views/${id}`,
method: 'DELETE',
contracts: {
response: SBoard.ActionResponse,
},
});
}
}
53 changes: 53 additions & 0 deletions src/entities/board/api/queries.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
import { queryOptions } from '@tanstack/react-query';
import { boardFabricKeys } from '../model/conts';
import { BoardHttp } from './http';

export class BoardQueries {
static getBoardList(projectId: string) {
return queryOptions({
queryKey: boardFabricKeys.list(projectId),
queryFn: async ({ signal }) => BoardHttp.getBoardList(projectId, signal),
staleTime: 60_000,
});
}

static getBoard(projectId: string, id: string) {
return queryOptions({
queryKey: boardFabricKeys.detail(projectId, id),
queryFn: async ({ signal }) => BoardHttp.getBoard(projectId, id, signal),
staleTime: 60_000,
});
}

static getBoardColumnList(boardId: string) {
return queryOptions({
queryKey: boardFabricKeys.columns(boardId),
queryFn: async ({ signal }) => BoardHttp.getBoardColumnList(boardId, signal),
staleTime: 60_000,
});
}

static getBoardColumn(boardId: string, id: string) {
return queryOptions({
queryKey: boardFabricKeys.column(boardId, id),
queryFn: async ({ signal }) => BoardHttp.getBoardColumn(boardId, id, signal),
staleTime: 60_000,
});
}

static getBoardViewList(boardId: string) {
return queryOptions({
queryKey: boardFabricKeys.views(boardId),
queryFn: async ({ signal }) => BoardHttp.getBoardViewList(boardId, signal),
staleTime: 60_000,
});
}

static getBoardView(boardId: string, id: string) {
return queryOptions({
queryKey: boardFabricKeys.view(boardId, id),
queryFn: async ({ signal }) => BoardHttp.getBoardView(boardId, id, signal),
staleTime: 60_000,
});
}
}
8 changes: 8 additions & 0 deletions src/entities/board/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
export type * as TBoard from './model/types';
export * as SBoard from './model/schemas';
export { boardFabricKeys } from './model/conts';
export { BoardHttp } from './api/http';
export { BoardQueries } from './api/queries';
export { mockBoard } from './model/mock-data';
export { BoardMapper, type BoardWithTasks } from './model/mapper';
export { BOARD_COLUMN_COLORS } from './lib/colors';
14 changes: 14 additions & 0 deletions src/entities/board/lib/colors.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
export const BOARD_COLUMN_COLORS = [
'#9FA8DA',
'#7E57C2',
'#9575CD',
'#AB47BC',
'#F06292',
'#FF8A65',
'#4FC3F7',
'#4DB6AC',
'#81C784',
'#DCE775',
'#FFF176',
'#FFB74D',
] as const;
9 changes: 9 additions & 0 deletions src/entities/board/model/conts.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import { createEntityKeys } from 'shared/lib/utils';

export const boardFabricKeys = createEntityKeys('board', {
detail: (projectId: string, id: string) => ['projects', projectId, 'boards', id],
columns: (boardId: string) => ['boards', boardId, 'columns'],
column: (boardId: string, id: string) => ['boards', boardId, 'columns', id],
views: (boardId: string) => ['boards', boardId, 'views'],
view: (boardId: string, id: string) => ['boards', boardId, 'views', id],
});
40 changes: 40 additions & 0 deletions src/entities/board/model/mapper.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
import { BoardColumnResponse, BoardResponse } from './types';

// TODO: добавить таски в типы, когда они появятся в API

export type BoardWithTasks = {
board: BoardResponse;
columns: Record<string, BoardColumnResponse>;
tasksByColumn: Record<string, unknown[]>;
};

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Сюда закладывай как фабрику, вы будете тут через ?layout получать большой список, и по айдишникам вставлять все

Во фабрике нужно сделать виды, kanban/gant/matrix, ну и в таком духе, это на будущее

export class BoardMapper {
static toBoardWithTasks(board: BoardResponse): BoardWithTasks {
const sortedColumns = [...board.boardColumns].sort((a, b) => a.position - b.position);
const tasksByColumn: Record<string, unknown[]> = {};
const columns: Record<string, BoardColumnResponse> = {};

sortedColumns.forEach((column) => {
tasksByColumn[column.id] = [];
columns[column.id] = column;
});

// tasks?.forEach((task) => {
// if (tasksByColumn[task.columnId]) {
// tasksByColumn[task.columnId].push(task);
// } else {
// console.warn(`Task ${task.id} references unknown column ${task.columnId}`);
// }
// });

// Object.keys(tasksByColumn).forEach((columnId) => {
// tasksByColumn[columnId].sort((a, b) => a.position - b.position);
// });

return {
board,
columns,
tasksByColumn,
};
}
}
Loading
Loading