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" ;
45import { useSearchParams } from "next/navigation" ;
56import Link from "next/link" ;
67import KanbanBoardsSearch from "@/app/home/kanban/_components/KanbanBoardsSearch" ;
7- import { getMembers } from "@/app/home/projects/actions" ;
8+ import type { SimpleMemberData } from "@/app/home/projects/actions" ;
89import pathsConfig from "@/config/paths.config" ;
910import { ArrowRightIcon , IconArchive , IconSearch } from "@/public/assets/svgs" ;
1011import { useUserStore } from "@/store/codev-store" ;
1112import { KanbanBoardType , KanbanColumnType } from "@/types/home/codev" ;
12- import { useQuery } from "@tanstack/react-query" ;
1313import toast from "react-hot-toast" ;
1414import { Archive } from "lucide-react" ;
1515
@@ -19,77 +19,87 @@ import ArchiveColumn from "./ArchiveColumn";
1919import KanbanAddMembersButton from "./kanban_modals/KanbanAddMembersButton" ;
2020import KanbanColumnAddButton from "./kanban_modals/KanbanColumnAddButton" ;
2121import KanbanColumnAddModal from "./kanban_modals/KanbanColumnAddModal" ;
22- import KanbanBoardColumnContainer from "./KanbanBoardColumnContainer" ;
2322import UserTaskFilter from "./UserTaskFilter" ;
2423import { 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
2757interface 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