Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
41 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
a060ba1
Merge dev into refactor/add-new-kanban
AlexandrNel Jun 15, 2026
9e2ca81
refactor(board): remove unused queries, update board and column schem…
AlexandrNel Jun 18, 2026
30c0c29
Merge branch 'dev' into refactor/add-new-kanban
AlexandrNel Jun 18, 2026
0205f26
refactor(board, project): replace projectId with projectSlug and upda…
AlexandrNel Jun 18, 2026
4fa4ea1
refactor(useClickOutside): replace handler dependency with useRef pat…
AlexandrNel Jun 18, 2026
6337dce
fix(board): update API endpoints and schemas for board columns and st…
AlexandrNel Jun 19, 2026
7539e21
refactor(auth, board): update imports to use type-only imports for types
AlexandrNel Jun 19, 2026
2728a60
fix: resolve PR review issues
AlexandrNel Jun 20, 2026
037e071
refactor: remove projectStore
AlexandrNel Jun 20, 2026
e817ba9
refactor: remove orderIndex field
AlexandrNel Jun 20, 2026
1bdd5fa
refactor: restructure project boards view handling
AlexandrNel Jun 21, 2026
ee4edde
chore: move constants to config & fix typos
AlexandrNel Jun 22, 2026
2a24927
refactor: fix PR issues
AlexandrNel Jun 22, 2026
5eddf1f
Merge branch 'dev' into refactor/add-new-kanban
AlexandrNel Jun 22, 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
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"
}
}
2 changes: 1 addition & 1 deletion src/entities/auth/api/http.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { api } from 'shared/api';
import * as SAuth from '../model/schemas';
import * as TAuth from '../model/types';
import type * as TAuth from '../model/types';

export class AuthHttp {
static signin(data: TAuth.SigninBody) {
Expand Down
130 changes: 130 additions & 0 deletions src/entities/board/api/http.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,130 @@
import { api } from 'shared/api';
import * as SBoard from '../model/schemas';
import type * as TBoard from '../model/types';

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

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

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

static updateBoard(projectSlug: string, boardSlug: string, data: TBoard.UpdateBoardBody) {
return api<TBoard.ActionResponse>({
url: `/projects/${projectSlug}/area/${boardSlug}`,
method: 'PUT',
data,
contracts: {
body: SBoard.UpdateBoardBody,
response: SBoard.ActionResponse,
},
});
}

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

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

static getBoardColumn(boardSlug: string, columnId: string, signal?: AbortSignal) {
return api<TBoard.BoardColumnResponse>({
url: `/area/${boardSlug}/states/${columnId}`,
method: 'GET',
contracts: {
response: SBoard.BoardColumn,
},
signal,
});
}

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

static updateBoardColumn(
boardSlug: string,
columnId: string,
data: TBoard.UpdateBoardColumnBody
) {
return api<TBoard.ActionResponse>({
url: `/area/${boardSlug}/states/${columnId}`,
method: 'PATCH',
data,
contracts: {
body: SBoard.UpdateBoardColumnBody,
response: SBoard.ActionResponse,
},
});
}

static removeBoardColumn(boardSlug: string, columnId: string) {
return api<TBoard.ActionResponse>({
url: `/area/${boardSlug}/states/${columnId}`,
method: 'DELETE',
contracts: {
response: SBoard.ActionResponse,
},
});
}
static restoreBoardColumn(boardSlug: string, columnId: string) {
return api<TBoard.ActionResponse>({
url: `/area/${boardSlug}/states/${columnId}/restore`,
method: 'POST',
contracts: {
response: SBoard.ActionResponse,
},
});
}
}
37 changes: 37 additions & 0 deletions src/entities/board/api/queries.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
import { queryOptions } from '@tanstack/react-query';
import { boardFabricKeys } from '../model/consts';
import { BoardHttp } from './http';

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

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

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

static getBoardColumn(slug: string, id: string) {
return queryOptions({
queryKey: boardFabricKeys.column(slug, id),
queryFn: async ({ signal }) => BoardHttp.getBoardColumn(slug, id, signal),
staleTime: 60_000,
});
}
}
14 changes: 14 additions & 0 deletions src/entities/board/config/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;
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/consts';
export { BoardHttp } from './api/http';
export { BoardQueries } from './api/queries';
export { BoardMapper, type KanbanBoardData } from './model/mapper';
export { BOARD_COLUMN_COLORS } from './config/colors';
export { useBoardStore } from './model/store';
11 changes: 11 additions & 0 deletions src/entities/board/model/consts.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import { createEntityKeys } from 'shared/lib/utils';

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

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

type KanbanTaskStub = {
id: string;
columnId: string;
};

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

Comment thread
kapitulin24 marked this conversation as resolved.
export class BoardMapper {
static toKanban(
board: BoardResponse,
columnList: BoardColumnResponse[],
taskList: unknown[]
): KanbanBoardData {
const sortedColumns = [...columnList].sort((a, b) => a.orderIndex - b.orderIndex);
const tasksByColumn: Record<string, unknown[]> = {};
const columns: Record<string, BoardColumnResponse> = {};

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

taskList?.forEach((task) => {
const kanbanTask = task as KanbanTaskStub;

if (tasksByColumn[kanbanTask.columnId]) {
tasksByColumn[kanbanTask.columnId].push(task);
} else {
console.warn(`Task ${kanbanTask.id} references unknown column ${kanbanTask.columnId}`);
}
});

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

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