Skip to content

Commit 839dbf3

Browse files
authored
Merge pull request #97 from codeit-sprint-part3-6team/#95_페이지_나의대시보드
#95 페이지 나의대시보드
2 parents ab178cd + 18fc674 commit 839dbf3

File tree

14 files changed

+260
-185
lines changed

14 files changed

+260
-185
lines changed

next.config.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ import type { NextConfig } from 'next';
22

33
const nextConfig: NextConfig = {
44
/* config options here */
5-
reactStrictMode: true,
5+
reactStrictMode: false,
66
webpack(config) {
77
config.module.rules.push({
88
test: /\.svg$/, // SVG 파일을 찾습니다.

src/components/common/sidebar/Sidebar.tsx

Lines changed: 45 additions & 61 deletions
Original file line numberDiff line numberDiff line change
@@ -1,55 +1,25 @@
1-
import React, { useState, useEffect } from 'react';
1+
import React, { useEffect, useState } from 'react';
2+
import { useSelector } from 'react-redux';
3+
import { RootState } from '@/redux/store';
4+
import { Dashboard } from '@/type/dashboard';
25
import styles from '@/components/common/sidebar/Sidebar.module.css';
3-
import getDashboards from '@/lib/mydashboard/getDashboard';
6+
import useSidebarDashboards from '@/hooks/useSidebar';
47
import Logo from 'public/images/img_logo.svg';
58
import TextLogo from 'public/images/img_textlogo.svg';
69
import PlusBtn from 'public/ic/ic_plus.svg';
710
import CrownIcon from 'public/ic/ic_crown.svg';
811
import CDSButton from '../button/CDSButton';
912

1013
export default function Sidebar() {
11-
const [menu, setMenu] = useState([]);
1214
const [currentPage, setCurrentPage] = useState(1);
13-
const [totalPages, setTotalPages] = useState(1);
14-
const [cursorId, setCursorId] = useState<number | null>(0);
15-
const [isLoading, setIsLoading] = useState(false);
15+
const [dashbaords, setDashboards] = useState<Dashboard[]>([]);
1616

17-
const pageSize = 10;
17+
const { isLoading, totalPages, fetchSidebarDashboards } =
18+
useSidebarDashboards();
1819

19-
useEffect(() => {
20-
const fetchDashboards = async () => {
21-
try {
22-
setIsLoading(true);
23-
const response = await getDashboards({
24-
page: currentPage,
25-
size: pageSize,
26-
cursorId: cursorId || 0,
27-
navigationMethod: 'pagination',
28-
});
29-
30-
const { dashboards, totalCount, cursorId: newCursorId } = response;
31-
32-
const formattedDashboards = dashboards.map(
33-
({ id, title, color, createdByMe }) => ({
34-
id,
35-
title,
36-
color,
37-
createdByMe,
38-
}),
39-
);
40-
41-
setMenu(formattedDashboards);
42-
setCursorId(newCursorId);
43-
setTotalPages(Math.ceil(totalCount / pageSize));
44-
} catch (error) {
45-
console.error('대시보드 데이터를 가져오는데 실패했습니다:', error);
46-
} finally {
47-
setIsLoading(false);
48-
}
49-
};
50-
51-
fetchDashboards();
52-
}, [currentPage, cursorId]);
20+
const sidebarDashboards = useSelector(
21+
(state: RootState) => state.dashboard.sidebarDashboards,
22+
);
5323

5424
const handlePageChange = (direction: 'prev' | 'next') => {
5525
if (direction === 'prev' && currentPage > 1) {
@@ -59,6 +29,14 @@ export default function Sidebar() {
5929
}
6030
};
6131

32+
useEffect(() => {
33+
fetchSidebarDashboards(currentPage);
34+
}, [currentPage]);
35+
36+
useEffect(() => {
37+
setDashboards(sidebarDashboards);
38+
}, [sidebarDashboards]);
39+
6240
return (
6341
<div className={styles.sidebar}>
6442
<div className={styles.logo}>
@@ -80,26 +58,33 @@ export default function Sidebar() {
8058
</div>
8159

8260
{/* 동적으로 렌더링되는 메뉴 */}
83-
<ul className={styles['menu-list']}>
84-
{menu.map((item) => (
85-
<li key={item.id} className={styles['menu-list-dashboard']}>
86-
<div className={styles['dashboard-item']}>
87-
<span
88-
className={styles['color-circle']}
89-
style={{ backgroundColor: item.color }}
90-
/>
91-
<span className={styles['dashboard-title']}>
92-
{item.title}
93-
</span>
94-
{item.createdByMe && (
95-
<span className={styles['crown-icon']}>
96-
<CrownIcon width={16} height={16} />
61+
{dashbaords && dashbaords.length > 0 ? (
62+
<ul className={styles['menu-list']}>
63+
{dashbaords.map((item) => (
64+
<li
65+
key={`Sidebar_${item.id}`}
66+
className={styles['menu-list-dashboard']}
67+
>
68+
<div className={styles['dashboard-item']}>
69+
<span
70+
className={styles['color-circle']}
71+
style={{ backgroundColor: item.color }}
72+
/>
73+
<span className={styles['dashboard-title']}>
74+
{item.title}
9775
</span>
98-
)}
99-
</div>
100-
</li>
101-
))}
102-
</ul>
76+
{item.createdByMe && (
77+
<span className={styles['crown-icon']}>
78+
<CrownIcon width={16} height={16} />
79+
</span>
80+
)}
81+
</div>
82+
</li>
83+
))}
84+
</ul>
85+
) : (
86+
<span>대시보드가 없습니다.</span>
87+
)}
10388
</div>
10489

10590
{/* 페이지네이션 컨트롤 */}
@@ -109,7 +94,6 @@ export default function Sidebar() {
10994
onClick={() => handlePageChange('prev')}
11095
disabled={currentPage === 1 || isLoading}
11196
/>
112-
11397
<CDSButton
11498
btnType="pagination_next"
11599
onClick={() => handlePageChange('next')}

src/components/product/mydashboard/DashboardList.module.css

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -60,10 +60,11 @@
6060
}
6161

6262
.modal input {
63+
height: 50px;
6364
margin-top: 8px;
6465
border: 1px solid var(--gray-medium);
6566
width: 100%;
66-
padding: 15px 16px;
67+
padding: 0 16px;
6768
border-radius: 8px;
6869
}
6970

@@ -73,6 +74,10 @@
7374
color: var(--black-medium);
7475
}
7576

77+
.modal-input:focus {
78+
border: 1px solid var(--gray-medium);
79+
}
80+
7681
.color-picker {
7782
margin-top: 16px;
7883
margin-bottom: 40px;

src/components/product/mydashboard/DashboardList.tsx

Lines changed: 37 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -4,18 +4,24 @@ import getDashboards from '@/lib/mydashboard/getDashboard';
44
import postDashboards from '@/lib/mydashboard/postDashboard';
55
import CDSButton from '@/components/common/button/CDSButton';
66
import OverlayContainer from '@/components/common/modal/overlay-container/OverlayContainer';
7+
import useSidebarDashboards from '@/hooks/useSidebar';
8+
import { Dashboard } from '@/type/dashboard';
9+
import { BadgeColor } from '@/type/button';
710
import styles from './DashboardList.module.css';
811

912
export default function DashboardList() {
10-
const [dashboards, setDashboards] = useState([]);
1113
const [currentPage, setCurrentPage] = useState(1);
12-
const [totalPages, setTotalPages] = useState(1);
13-
14+
const [totalPages, setTotalPages] = useState(0);
15+
const [dashboards, setDashboards] = useState<Dashboard[]>([]);
1416
const [showModal, setShowModal] = useState(false);
1517
const [newDashboardName, setNewDashboardName] = useState('');
1618
const [selectedColor, setSelectedColor] = useState('#7ac555');
1719
const [isLoading, setIsLoading] = useState(false);
1820

21+
const pageSize = 5;
22+
23+
const { fetchSidebarDashboards } = useSidebarDashboards();
24+
1925
const router = useRouter();
2026

2127
const colors = ['#7ac555', '#760dde', '#ffa500', '#76a5ea', '#e876ea'];
@@ -40,11 +46,11 @@ export default function DashboardList() {
4046
const fetchDashboards = async () => {
4147
const response = await getDashboards({
4248
page: currentPage,
43-
size: 5,
49+
size: pageSize,
4450
navigationMethod: 'pagination',
4551
});
4652
setDashboards(response.dashboards);
47-
setTotalPages(Math.ceil(response.totalCount / 5));
53+
setTotalPages(Math.ceil(response.totalCount / pageSize));
4854
};
4955

5056
const handleNewDashboard = async () => {
@@ -54,16 +60,16 @@ export default function DashboardList() {
5460
}
5561

5662
setIsLoading(true);
57-
5863
try {
5964
await postDashboards({
6065
title: newDashboardName,
6166
color: selectedColor,
6267
});
68+
await fetchDashboards();
69+
await fetchSidebarDashboards(currentPage);
6370
closeModal();
64-
fetchDashboards();
6571
} catch (error) {
66-
alert(error.message || '대시보드 생성에 실패했습니다.');
72+
console.error('대시보드 생성 실패:', error);
6773
} finally {
6874
setIsLoading(false);
6975
}
@@ -76,21 +82,30 @@ export default function DashboardList() {
7682
return (
7783
<div className={styles['dashboard-container']}>
7884
<ul className={styles['dashboard-list']}>
79-
<CDSButton btnType="dashboard_add" onClick={openModal}>
80-
새로운 대시보드
81-
</CDSButton>
82-
{dashboards.map((item) => (
83-
<li key={item.id} className={styles.dashboard}>
84-
<CDSButton
85-
btnType="dashboard_card"
86-
badge={item.color}
87-
owner={item.createdByMe}
88-
onClick={() => handleClick(item.id)}
89-
>
90-
{item.title}
91-
</CDSButton>
85+
<li>
86+
<CDSButton btnType="dashboard_add" onClick={openModal}>
87+
새로운 대시보드
88+
</CDSButton>
89+
</li>
90+
91+
{Array.isArray(dashboards) && dashboards.length > 0 ? (
92+
dashboards.map((item) => (
93+
<li key={`DashboardList_${item.id}`} className={styles.dashboard}>
94+
<CDSButton
95+
btnType="dashboard_card"
96+
badge={item.color as BadgeColor}
97+
owner={item.createdByMe}
98+
onClick={() => handleClick(item.id)}
99+
>
100+
{item.title}
101+
</CDSButton>
102+
</li>
103+
))
104+
) : (
105+
<li className={styles['menu-list-dashboard']}>
106+
<span>대시보드가 없습니다.</span>
92107
</li>
93-
))}
108+
)}
94109
</ul>
95110

96111
{/* 페이지네이션 */}

src/components/product/mydashboard/InvitationList.module.css

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,14 +12,19 @@
1212

1313
.input-container {
1414
width: 100%;
15-
height: 40px;
1615
border: 1px solid var(--gray-medium);
1716
border-radius: 6px;
18-
padding: 7px 16px;
17+
gap: 8px;
1918
display: flex;
19+
align-items: center;
2020
gap: 8px;
2121
}
2222

23+
.icon {
24+
margin-left: 16px;
25+
cursor: pointer;
26+
}
27+
2328
.input::placeholder {
2429
font-size: 16px;
2530
font-weight: 400;
@@ -28,6 +33,7 @@
2833

2934
.input {
3035
border-style: none;
36+
height: 40px;
3137
}
3238

3339
.input:focus {

src/components/product/mydashboard/InvitationList.tsx

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -116,7 +116,7 @@ export default function InvitationList() {
116116
width={24}
117117
height={24}
118118
onClick={handleSearch}
119-
style={{ cursor: 'pointer' }}
119+
className={styles.icon}
120120
/>
121121
<input
122122
placeholder="검색"
@@ -139,7 +139,10 @@ export default function InvitationList() {
139139

140140
<tbody>
141141
{invitations.map((invite) => (
142-
<tr key={invite.id} className={styles['table-body']}>
142+
<tr
143+
key={`InvitationList_${invite.id}`}
144+
className={styles['table-body']}
145+
>
143146
<td className={styles['table-content']}>
144147
<span className={styles['table-content-title']}>이름</span>
145148
<span className={styles['table-content-dashboard']}>

src/hooks/useSidebar.ts

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
import { useState } from 'react';
2+
import { useDispatch } from 'react-redux';
3+
import { AppDispatch } from '@/redux/store';
4+
import { setSidebarDashboards } from '@/redux/dashboardSlice';
5+
import getDashboards from '@/lib/mydashboard/getDashboard';
6+
7+
export default function useSidebarDashboards() {
8+
const pageSize = 10;
9+
const [isLoading, setIsLoading] = useState(false);
10+
const [totalPages, setTotalPages] = useState(1);
11+
const dispatch = useDispatch<AppDispatch>();
12+
13+
const fetchSidebarDashboards = async (currentPage: number) => {
14+
try {
15+
setIsLoading(true);
16+
const response = await getDashboards({
17+
page: currentPage,
18+
size: pageSize,
19+
navigationMethod: 'pagination',
20+
});
21+
22+
setTotalPages(Math.ceil(response.totalCount / pageSize));
23+
dispatch(setSidebarDashboards(response.dashboards)); // 사이드바 상태 업데이트
24+
} catch (error) {
25+
console.error('대시보드 데이터를 가져오는데 실패했습니다:', error);
26+
} finally {
27+
setIsLoading(false);
28+
}
29+
};
30+
31+
return { isLoading, totalPages, fetchSidebarDashboards };
32+
}

src/lib/mydashboard/getDashboard.ts

Lines changed: 1 addition & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -1,28 +1,6 @@
1+
import { GetDashboardsParams, GetDashboardsResponse } from '@/type/dashboard';
12
import instance from '../instance';
23

3-
interface GetDashboardsParams {
4-
cursorId?: number;
5-
page: number;
6-
size: number;
7-
navigationMethod: 'infiniteScroll' | 'pagination';
8-
}
9-
10-
interface Dashboard {
11-
id: number;
12-
title: string;
13-
color: string;
14-
createdAt: string;
15-
updatedAt: string;
16-
createdByMe: boolean;
17-
userId: number;
18-
}
19-
20-
interface GetDashboardsResponse {
21-
cursorId: number | null;
22-
totalCount: number;
23-
dashboards: Dashboard[];
24-
}
25-
264
export default async function getDashboards(
275
params: GetDashboardsParams,
286
): Promise<GetDashboardsResponse> {

0 commit comments

Comments
 (0)