Skip to content

Commit 5cce91d

Browse files
authored
Merge pull request #256 from boostcampwm-2024/feature-dashboard-fe
[Feature-dashboard-fe] ๋Œ€์‹œ๋ณด๋“œ ์ •๋ ฌ ๊ธฐ๋Šฅ ์ถ”๊ฐ€
2 parents aa9633c + 3001876 commit 5cce91d

File tree

2 files changed

+95
-11
lines changed

2 files changed

+95
-11
lines changed

โ€Žclient/src/components/Dashboard/MindMapInfoItem.tsxโ€Ž

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
1-
import profile from "@/assets/profile.png";
21
import useModal from "@/hooks/useModal";
32
import { Button } from "@headlessui/react";
43
import extractDate from "@/utils/extractDate";
@@ -13,9 +12,10 @@ import { useConnectionStore } from "@/store/useConnectionStore";
1312
interface MindMapInfoItemProps {
1413
data: DashBoard;
1514
index: number;
15+
onDelete: (id: number) => void;
1616
}
1717

18-
export default function MindMapInfoItem({ data, index }: MindMapInfoItemProps) {
18+
export default function MindMapInfoItem({ data, index, onDelete }: MindMapInfoItemProps) {
1919
const { open, openModal, closeModal } = useModal();
2020
const keywordData = data.keyword.slice(0, 4);
2121
const navigate = useNavigate();
@@ -31,6 +31,7 @@ export default function MindMapInfoItem({ data, index }: MindMapInfoItemProps) {
3131
function handleDelete() {
3232
mutation.mutate();
3333
closeModal();
34+
onDelete(data.id);
3435
}
3536

3637
function navigateToMindMap() {

โ€Žclient/src/components/Dashboard/UserDashBoard.tsxโ€Ž

Lines changed: 92 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,22 @@
11
import MindMapInfoItem from "./MindMapInfoItem";
22
import { Button, Input } from "@headlessui/react";
33
import { useEffect, useState } from "react";
4-
import { FaPlus, FaSearch } from "react-icons/fa";
4+
import { FaArrowDown, FaPlus, FaArrowUp } from "react-icons/fa";
55
import { useNavigate } from "react-router-dom";
66
import useDashBoard from "@/api/fetchHooks/useDashBoard";
77
import NoMindMap from "@/components/Dashboard/NoMindMap";
88
import { useConnectionStore } from "@/store/useConnectionStore";
99
import { IoSearch } from "react-icons/io5";
1010

11+
type SortKey = "title" | "ownerName" | "createDate";
12+
type SortOrder = "asc" | "desc";
13+
1114
export 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

Comments
ย (0)