11import MindMapInfoItem from "./MindMapInfoItem" ;
22import { Button , Input } from "@headlessui/react" ;
33import { useEffect , useState } from "react" ;
4- import { FaPlus , FaSearch } from "react-icons/fa" ;
4+ import { FaArrowDown , FaPlus , FaArrowUp } from "react-icons/fa" ;
55import { useNavigate } from "react-router-dom" ;
66import useDashBoard from "@/api/fetchHooks/useDashBoard" ;
77import NoMindMap from "@/components/Dashboard/NoMindMap" ;
88import { useConnectionStore } from "@/store/useConnectionStore" ;
99import { IoSearch } from "react-icons/io5" ;
1010
11+ type SortKey = "title" | "ownerName" | "createDate" ;
12+ type SortOrder = "asc" | "desc" ;
13+
1114export default function UserDashBoard ( ) {
1215 const { data } = useDashBoard ( ) ;
13- const [ searchContent , setSearchContent ] = useState ( "" ) ;
16+ const [ filteredData , setFilteredData ] = useState ( data ) ;
17+ const [ sortKey , setSortKey ] = useState < SortKey > ( "createDate" ) ;
18+ const [ sortOrder , setSortOrder ] = useState < SortOrder > ( "desc" ) ;
19+ const [ searchQuery , setSearchQuery ] = useState ( "" ) ;
1420 const navigate = useNavigate ( ) ;
1521 const createConnection = useConnectionStore ( ( state ) => state . createConnection ) ;
1622 const updateOwnedMindMap = useConnectionStore ( ( state ) => state . updateOwnedMindMap ) ;
@@ -21,17 +27,96 @@ export default function UserDashBoard() {
2127 }
2228 } , [ data ] ) ;
2329
30+ useEffect ( ( ) => {
31+ applyFilters ( data , searchQuery ) ;
32+ } , [ sortKey , sortOrder ] ) ;
33+
2434 function searchData ( content : string ) {
25- if ( ! data ) return [ ] ;
2635 return data . filter ( ( item ) => item . title . includes ( content ) || item . keyword . some ( ( k ) => k . includes ( content ) ) ) ;
2736 }
2837
29- const filteredData = searchData ( searchContent ) ;
38+ function sortData ( items , key : SortKey , order : SortOrder ) {
39+ return [ ...items ] . sort ( ( a , b ) => {
40+ const valueA = key === "createDate" ? new Date ( a [ key ] ) . getTime ( ) : a [ key ] ;
41+ const valueB = key === "createDate" ? new Date ( b [ key ] ) . getTime ( ) : b [ key ] ;
42+
43+ if ( order === "asc" ) return valueA < valueB ? - 1 : valueA > valueB ? 1 : 0 ;
44+ return valueA > valueB ? - 1 : valueA < valueB ? 1 : 0 ;
45+ } ) ;
46+ }
47+
48+ function applyFilters ( originalData , query : string ) {
49+ let result = query ? searchData ( query ) : originalData ;
50+ result = sortData ( result , sortKey , sortOrder ) ;
51+ setFilteredData ( result ) ;
52+ }
53+
54+ function handleSearchChange ( query : string ) {
55+ setSearchQuery ( query ) ;
56+ applyFilters ( data , query ) ;
57+ }
58+
59+ function handleSortChange ( key : SortKey , order : SortOrder ) {
60+ setSortKey ( key ) ;
61+ setSortOrder ( order ) ;
62+ applyFilters ( data , searchQuery ) ;
63+ }
64+
65+ function handleDeleteMindMap ( id : number ) {
66+ setFilteredData ( ( prevData ) => prevData . filter ( ( item ) => item . id !== id ) ) ;
67+ }
3068
3169 if ( ! data . length ) return < NoMindMap /> ;
3270
71+ const buttonBaseClass = "flex items-center gap-0.5 text-sm" ;
72+ const buttonInactiveClass = "text-grayscale-300 hover:text-white" ;
73+ const buttonActiveClass = "text-white" ;
74+
3375 return (
34- < div className = "relative flex h-full w-full flex-col rounded-[20px] bg-grayscale-700 px-8 pb-24 pt-8" >
76+ < div className = "relative flex h-full w-full flex-col rounded-[20px] bg-grayscale-700 px-8 pb-24 pt-3" >
77+ < div className = "mb-2 flex items-center justify-end gap-2" >
78+ < Button
79+ className = { `${ buttonBaseClass } ${ sortKey === "createDate" && sortOrder === "desc" ? buttonActiveClass : buttonInactiveClass } ` }
80+ onClick = { ( ) => handleSortChange ( "createDate" , "desc" ) }
81+ >
82+ 최신 순
83+ </ Button >
84+ < span > |</ span >
85+ < Button
86+ className = { `${ buttonBaseClass } ${ sortKey === "createDate" && sortOrder === "asc" ? buttonActiveClass : buttonInactiveClass } ` }
87+ onClick = { ( ) => handleSortChange ( "createDate" , "asc" ) }
88+ >
89+ 오래된 순
90+ </ Button >
91+ < span > |</ span >
92+ < Button
93+ className = { `${ buttonBaseClass } ${ sortKey === "title" && sortOrder === "asc" ? buttonActiveClass : buttonInactiveClass } ` }
94+ onClick = { ( ) => handleSortChange ( "title" , "asc" ) }
95+ >
96+ 제목 순< FaArrowUp />
97+ </ Button >
98+ < span > |</ span >
99+ < Button
100+ className = { `${ buttonBaseClass } ${ sortKey === "title" && sortOrder === "desc" ? buttonActiveClass : buttonInactiveClass } ` }
101+ onClick = { ( ) => handleSortChange ( "title" , "desc" ) }
102+ >
103+ 제목 순< FaArrowDown />
104+ </ Button >
105+ < span > |</ span >
106+ < Button
107+ className = { `${ buttonBaseClass } ${ sortKey === "ownerName" && sortOrder === "asc" ? buttonActiveClass : buttonInactiveClass } ` }
108+ onClick = { ( ) => handleSortChange ( "ownerName" , "asc" ) }
109+ >
110+ 소유자 이름 순< FaArrowUp />
111+ </ Button >
112+ < span > |</ span >
113+ < Button
114+ className = { `${ buttonBaseClass } ${ sortKey === "ownerName" && sortOrder === "desc" ? buttonActiveClass : buttonInactiveClass } ` }
115+ onClick = { ( ) => handleSortChange ( "ownerName" , "desc" ) }
116+ >
117+ 소유자 이름 순< FaArrowDown />
118+ </ Button >
119+ </ div >
35120 < header className = "flex items-center justify-between px-3 py-2 font-bold" >
36121 < div className = "min-w-72 pl-2" > 제목</ div >
37122 < div className = "min-w-60 pl-8" > 키워드</ div >
@@ -40,7 +125,7 @@ export default function UserDashBoard() {
40125 </ header >
41126 < div className = "no-scrollbar h-[calc(100%-40px)] overflow-y-scroll border-b-[1px] border-t-[1px] border-grayscale-500" >
42127 { filteredData . map ( ( info , i ) => (
43- < MindMapInfoItem key = { `dashboard-${ i } ` } data = { info } index = { i } />
128+ < MindMapInfoItem key = { `dashboard-${ info . id } ` } data = { info } index = { i } onDelete = { handleDeleteMindMap } />
44129 ) ) }
45130 </ div >
46131 < div className = "absolute bottom-8 right-8" >
@@ -55,9 +140,7 @@ export default function UserDashBoard() {
55140 < div className = "absolute right-0 top-[-47px] flex items-center gap-2 rounded-2xl bg-grayscale-700 px-4 py-2 text-grayscale-200" >
56141 < Input
57142 className = "w-48 bg-inherit text-xs focus:outline-none"
58- onChange = { ( e ) => {
59- setSearchContent ( e . target . value ) ;
60- } }
143+ onChange = { ( e ) => handleSearchChange ( e . target . value ) }
61144 placeholder = "키워드나 제목을 입력하세요"
62145 />
63146 < IoSearch size = { 24 } />
0 commit comments