Skip to content

Commit 44dd412

Browse files
committed
kanban board updates RSC
1 parent 1094b6e commit 44dd412

51 files changed

Lines changed: 2597 additions & 1123 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.
Lines changed: 95 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,95 @@
1+
"use client";
2+
3+
4+
5+
import { useMemo } from "react";
6+
7+
8+
9+
import type { SimpleMemberData } from "@/app/home/projects/actions";
10+
11+
import { filterBoardByQuery } from "@/lib/kanban/board-mappers";
12+
13+
import {
14+
15+
useKanbanBoardMeta,
16+
17+
useKanbanColumns,
18+
19+
} from "@/store/kanban-board/KanbanBoardProvider";
20+
21+
22+
23+
import KanbanBoard from "./_components/KanbanBoard";
24+
25+
26+
27+
type KanbanBoardPageClientProps = {
28+
29+
projectId: string;
30+
31+
query?: string;
32+
33+
members: SimpleMemberData[];
34+
35+
draftCount: number;
36+
37+
};
38+
39+
40+
41+
export default function KanbanBoardPageClient({
42+
43+
projectId,
44+
45+
query = "",
46+
47+
members,
48+
49+
draftCount,
50+
51+
}: KanbanBoardPageClientProps) {
52+
53+
const columns = useKanbanColumns();
54+
55+
const { board } = useKanbanBoardMeta();
56+
57+
58+
59+
const boardData = useMemo(() => {
60+
61+
const withColumns = { ...board, kanban_columns: columns };
62+
63+
return filterBoardByQuery(withColumns, query);
64+
65+
}, [board, columns, query]);
66+
67+
68+
69+
return (
70+
71+
<div className="w-full">
72+
73+
<div className="flex flex-col gap-4 pt-4">
74+
75+
<KanbanBoard
76+
77+
projectId={projectId}
78+
79+
boardData={boardData}
80+
81+
members={members}
82+
83+
draftCount={draftCount}
84+
85+
/>
86+
87+
</div>
88+
89+
</div>
90+
91+
);
92+
93+
}
94+
95+

‎apps/codebility/app/home/kanban/[projectId]/[id]/_components/ArchiveColumn.tsx‎

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ import { createClientClientComponent } from "@/utils/supabase/client";
66
import { Task } from "@/types/home/codev";
77
import { IconArchive } from "@/public/assets/svgs";
88

9-
import { useModal } from "@/hooks/use-modal";
9+
import { useKanbanModal } from "@/hooks/use-modal-kanban";
1010
import toast from "react-hot-toast";
1111
import ArchiveTask from "./ArchiveTask";
1212

@@ -19,7 +19,7 @@ export default function ArchiveColumn({ projectId, boardId }: ArchiveColumnProps
1919
const [archivedTasks, setArchivedTasks] = useState<Task[]>([]);
2020
const [isLoading, setIsLoading] = useState(true);
2121
const [supabase, setSupabase] = useState<any>(null);
22-
const { onOpen } = useModal();
22+
const { onOpen } = useKanbanModal();
2323

2424
const searchParams = useSearchParams();
2525
const highlightTaskId = searchParams.get("taskId");

‎apps/codebility/app/home/kanban/[projectId]/[id]/_components/KanbanBoard.tsx‎

Lines changed: 53 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,15 @@
11
"use client";
22

3-
import { memo, useCallback, useEffect, useMemo, useState } from "react";
3+
import { memo, useEffect, useMemo, useState } from "react";
4+
import dynamic from "next/dynamic";
45
import { useSearchParams } from "next/navigation";
56
import Link from "next/link";
67
import KanbanBoardsSearch from "@/app/home/kanban/_components/KanbanBoardsSearch";
7-
import { getMembers } from "@/app/home/projects/actions";
8+
import type { SimpleMemberData } from "@/app/home/projects/actions";
89
import pathsConfig from "@/config/paths.config";
910
import { ArrowRightIcon, IconArchive, IconSearch } from "@/public/assets/svgs";
1011
import { useUserStore } from "@/store/codev-store";
1112
import { KanbanBoardType, KanbanColumnType } from "@/types/home/codev";
12-
import { useQuery } from "@tanstack/react-query";
1313
import toast from "react-hot-toast";
1414
import { Archive } from "lucide-react";
1515

@@ -19,77 +19,87 @@ import ArchiveColumn from "./ArchiveColumn";
1919
import KanbanAddMembersButton from "./kanban_modals/KanbanAddMembersButton";
2020
import KanbanColumnAddButton from "./kanban_modals/KanbanColumnAddButton";
2121
import KanbanColumnAddModal from "./kanban_modals/KanbanColumnAddModal";
22-
import KanbanBoardColumnContainer from "./KanbanBoardColumnContainer";
2322
import UserTaskFilter from "./UserTaskFilter";
2423
import { useKanbanTaskUrlModal } from "@/hooks/useKanbanTaskUrlModal";
25-
import { getDraftCount } from "../actions";
24+
import { useMemberTaskFilter } from "@/hooks/use-member-task-filter";
25+
26+
const KanbanBoardColumnContainer = dynamic(
27+
() => import("./KanbanBoardColumnContainer"),
28+
{
29+
ssr: false,
30+
loading: () => (
31+
<div className="overflow-x-auto overflow-y-hidden">
32+
<ol className="flex min-h-[calc(100vh-12rem)] w-full flex-wrap gap-4 p-2 md:p-4">
33+
{Array.from({ length: 4 }).map((_, index) => (
34+
<li
35+
key={index}
36+
className="relative flex h-full w-[calc(100vw-2rem)] min-w-[280px] flex-col overflow-hidden rounded-xl border border-gray-200 bg-gray-50 shadow-sm dark:border-white/10 dark:bg-white/5 md:w-[350px] md:min-w-[350px] lg:w-[400px] lg:min-w-[400px]"
37+
>
38+
<div className="border-b border-gray-200 bg-gradient-to-r from-gray-100 to-gray-50 p-4 dark:border-white/20 dark:from-white/10 dark:to-white/15">
39+
<div className="h-6 w-32 animate-pulse rounded bg-gray-200 dark:bg-gray-700" />
40+
</div>
41+
<div className="flex flex-col gap-3 p-3">
42+
{Array.from({ length: 2 }).map((__, taskIndex) => (
43+
<div
44+
key={taskIndex}
45+
className="h-24 animate-pulse rounded-xl bg-gray-100 dark:bg-white/10"
46+
/>
47+
))}
48+
</div>
49+
</li>
50+
))}
51+
</ol>
52+
</div>
53+
),
54+
},
55+
);
2656

2757
interface KanbanBoardProps {
2858
boardData: KanbanBoardType & { kanban_columns: KanbanColumnType[] };
2959
projectId: string;
60+
members: SimpleMemberData[];
61+
draftCount: number;
3062
}
3163

32-
function KanbanBoard({ boardData, projectId }: KanbanBoardProps) {
64+
function KanbanBoard({
65+
boardData,
66+
projectId,
67+
members: allMembers,
68+
draftCount,
69+
}: KanbanBoardProps) {
3370
const user = useUserStore((state) => state.user);
3471

3572
useKanbanTaskUrlModal(boardData);
3673

74+
const { activeFilter, handleFilterClick, loadingMemberId } =
75+
useMemberTaskFilter();
76+
3777
const canAddColumn = user?.role_id === 1 || user?.role_id === 5;
3878
const canAddMember = canAddColumn;
3979

4080
const searchParams = useSearchParams();
4181
const viewParam = searchParams.get("view");
4282

43-
const [activeFilter, setActiveFilter] = useState<string | null>(null);
4483
const [showArchive, setShowArchive] = useState(viewParam === "archive");
45-
const [draftCount, setDraftCount] = useState<number>(0);
46-
47-
const { data: allMembers = [], isLoading: isMembersLoading } = useQuery({
48-
queryKey: ["members", projectId],
49-
queryFn: async () => {
50-
const result = await getMembers(projectId);
51-
return result.data || [];
52-
},
53-
enabled: !!projectId,
54-
staleTime: 5 * 60 * 1000,
55-
refetchOnWindowFocus: false,
56-
});
5784

5885
const members = useMemo(
5986
() =>
60-
allMembers.map((user) => ({
61-
userId: user.id,
62-
userName: `${user.first_name} ${user.last_name}`,
63-
imageUrl: user.image_url,
64-
isActive: activeFilter === user.id,
87+
allMembers.map((member) => ({
88+
userId: member.id,
89+
userName: `${member.first_name} ${member.last_name}`,
90+
imageUrl: member.image_url,
91+
isActive: activeFilter === member.id,
92+
isLoading: loadingMemberId === member.id,
6593
})),
66-
[allMembers, activeFilter],
94+
[allMembers, activeFilter, loadingMemberId],
6795
);
6896

69-
useEffect(() => {
70-
if (user?.id && projectId) {
71-
loadDraftCount();
72-
}
73-
}, [user?.id, projectId]);
74-
75-
const loadDraftCount = async () => {
76-
if (!user?.id || !projectId) return;
77-
const result = await getDraftCount(projectId, user.id);
78-
if (result.success && result.count !== undefined) {
79-
setDraftCount(result.count);
80-
}
81-
};
82-
8397
useEffect(() => {
8498
if (viewParam === "archive") {
8599
setShowArchive(true);
86100
}
87101
}, [viewParam]);
88102

89-
const handleFilterClick = useCallback((userId: string) => {
90-
setActiveFilter((prev) => (prev === userId ? null : userId));
91-
}, []);
92-
93103
if (!boardData) {
94104
return (
95105
<div className="text-center text-red-500">Board data not found.</div>
@@ -277,7 +287,6 @@ function KanbanBoard({ boardData, projectId }: KanbanBoardProps) {
277287
{!showArchive ? (
278288
<KanbanBoardColumnContainer
279289
projectId={projectId}
280-
columns={boardData.kanban_columns || []}
281290
activeFilter={activeFilter}
282291
/>
283292
) : (

0 commit comments

Comments
 (0)