Skip to content

Commit c81434d

Browse files
authored
Merge branch 'feature' into #59_페이지_대시보드_수정
2 parents 32f05e6 + 1d6a261 commit c81434d

File tree

16 files changed

+335
-131
lines changed

16 files changed

+335
-131
lines changed

public/ic/ic_imageDelete.svg

Lines changed: 5 additions & 0 deletions
Loading

src/components/common/sidebar/Sidebar.module.css

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
padding: 20px 12px 0 8px;
66
position: fixed;
77
border-right: 1px solid var(--gray-medium);
8+
z-index: 1;
89
}
910

1011
.menu {
@@ -106,6 +107,7 @@
106107
height: 344px;
107108
padding: 32px;
108109
border-radius: 10px;
110+
z-index: 11;
109111
}
110112

111113
.modal h2 {

src/components/common/sidebar/Sidebar.tsx

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,18 @@ export default function Sidebar() {
8181
setDashboards(sidebarDashboards);
8282
}, [sidebarDashboards]);
8383

84+
useEffect(() => {
85+
// 모달 dim 부분 스크롤 막기
86+
if (showModal) {
87+
document.body.style.overflow = 'hidden'; // 스크롤 비활성화
88+
} else {
89+
document.body.style.overflow = ''; // 기본 스크롤 상태로 복구
90+
}
91+
return () => {
92+
document.body.style.overflow = ''; // 컴포넌트가 unmount 될 때도 스크롤 상태 복구
93+
};
94+
}, [showModal]);
95+
8496
return (
8597
<div className={styles.sidebar}>
8698
<Link href="/" className={styles.logo}>

src/components/product/edit/InviteTitle/InviteList.tsx

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,16 +6,19 @@ import styles from './InviteList.module.css';
66

77
interface InviteListProps {
88
members: Invitaion[];
9+
setMembers: React.Dispatch<React.SetStateAction<Invitaion[]>>;
910
}
1011

11-
export default function InviteList({ members }: InviteListProps) {
12+
export default function InviteList({ members, setMembers }: InviteListProps) {
1213
const router = useRouter();
1314
const dashboardId = Number(router.query.id);
1415

1516
const handleDeleteClick = async (invitationId: number) => {
1617
try {
1718
await deleteInvitation(dashboardId, invitationId);
18-
router.reload();
19+
setMembers((prevMembers) =>
20+
prevMembers.filter((member) => member.id !== invitationId),
21+
);
1922
} catch (error) {
2023
throw new Error(`${error}`);
2124
}

src/components/product/edit/InviteTitle/InviteTitle.tsx

Lines changed: 54 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,12 @@ import getInvitations, {
99
import postInvite from '@/lib/invite/postInvite';
1010
import styles from './InviteTitle.module.css';
1111
import InviteList from './InviteList';
12+
import AuthModal from '@/components/common/modal/auth/AuthModal';
13+
import { toast } from 'react-toastify';
14+
15+
const INITIAL_VALUES = {
16+
email: '',
17+
};
1218

1319
export default function InviteTitle() {
1420
const [members, setMembers] = useState<GetInvitationsResponse['invitations']>(
@@ -17,9 +23,13 @@ export default function InviteTitle() {
1723
const [currentPage, setCurrentPage] = useState(1);
1824
const [totalPages, setTotalPages] = useState(1);
1925
const [isModalOpen, setIsModalOpen] = useState(false);
20-
const [emailValue, setEmailValue] = useState<{ email: string }>({
21-
email: '',
22-
});
26+
const [emailValue, setEmailValue] = useState<{ email: string }>(
27+
INITIAL_VALUES,
28+
);
29+
const [isModalVisible, setIsModalVisible] = useState(false);
30+
const [responseMessage, setResponseMessage] = useState('');
31+
const [alertMessage, setAlertMessage] = useState<string | null>(null);
32+
2333
const router = useRouter();
2434

2535
useEffect(() => {
@@ -41,21 +51,31 @@ export default function InviteTitle() {
4151
}
4252
};
4353
fetchInvitations();
44-
}, [router.query.id, currentPage]);
54+
}, [router.query.id, currentPage, isModalOpen]);
4555

46-
const handleCancelClick = () => {
47-
setIsModalOpen(false);
48-
};
56+
// 초대 요청을 보내고 alert
57+
useEffect(() => {
58+
if (!isModalOpen && alertMessage) {
59+
toast.success(alertMessage);
60+
setAlertMessage(null);
61+
}
62+
}, [isModalOpen, alertMessage]);
4963

5064
const submitInvite = async () => {
5165
const id = Number(router.query.id);
5266

5367
try {
54-
await postInvite({ id, email: emailValue.email });
68+
const response = await postInvite({ id, email: emailValue.email });
5569
setIsModalOpen(false);
56-
router.reload();
70+
setEmailValue(INITIAL_VALUES);
71+
setAlertMessage(
72+
`${response.invitee.nickname}님께 초대 요청을 보냈습니다.`,
73+
);
5774
} catch (error) {
58-
throw new Error(`${error}`);
75+
setIsModalOpen(false);
76+
setResponseMessage(error.message);
77+
setIsModalVisible(true);
78+
setEmailValue(INITIAL_VALUES);
5979
}
6080
};
6181

@@ -67,6 +87,11 @@ export default function InviteTitle() {
6787
}
6888
};
6989

90+
const handleCancelClick = () => {
91+
setIsModalOpen(false);
92+
setIsModalVisible(false);
93+
};
94+
7095
return (
7196
<section className={styles['title-container']}>
7297
<div className={styles['member-section']}>
@@ -101,24 +126,29 @@ export default function InviteTitle() {
101126
<div className={styles['name-section']}>
102127
<h2 className={styles['sub-title']}>이메일</h2>
103128
<div className={styles['list']}>
104-
<InviteList members={members} />
129+
<InviteList members={members} setMembers={setMembers} />
105130
</div>
106131
</div>
107-
<div>
108-
<InviteModal
109-
label="이메일"
110-
placeholder="이메일을 입력해 주세요"
111-
isOpen={isModalOpen}
112-
onClose={handleCancelClick}
113-
title="초대하기"
114-
inputValue={emailValue.email}
115-
onInputChange={(value) => setEmailValue({ email: value })}
116-
cancelTitle="취소"
117-
adaptTitle="생성"
132+
<InviteModal
133+
label="이메일"
134+
placeholder="이메일을 입력해 주세요"
135+
isOpen={isModalOpen}
136+
onClose={handleCancelClick}
137+
title="초대하기"
138+
inputValue={emailValue.email}
139+
onInputChange={(value) => setEmailValue({ email: value })}
140+
cancelTitle="취소"
141+
adaptTitle="초대"
142+
handleCancelClick={handleCancelClick}
143+
handleAdaptClick={submitInvite}
144+
/>
145+
146+
{isModalVisible && (
147+
<AuthModal
148+
message={responseMessage}
118149
handleCancelClick={handleCancelClick}
119-
handleAdaptClick={submitInvite}
120150
/>
121-
</div>
151+
)}
122152
</section>
123153
);
124154
}

src/components/product/edit/MainTitle/ColorSelector.tsx

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -7,11 +7,11 @@ export default function ColorSelector({
77
setSelectedColor,
88
}: ColorSelectorProps) {
99
const colors = [
10-
{ code: '#7AC555', style: styles.colorGreen },
11-
{ code: '#760DDE', style: styles.colorPurple },
12-
{ code: '#FFA500', style: styles.colorOrange },
13-
{ code: '#76A5EA', style: styles.colorBlue },
14-
{ code: '#E876EA', style: styles.colorPink },
10+
{ code: '#7ac555', style: styles.colorGreen },
11+
{ code: '#760dde', style: styles.colorPurple },
12+
{ code: '#ffa500', style: styles.colorOrange },
13+
{ code: '#76a5ea', style: styles.colorBlue },
14+
{ code: '#e876ea', style: styles.colorPink },
1515
];
1616

1717
return (
Lines changed: 37 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -1,56 +1,60 @@
11
.title-container {
2-
width: 620px;
3-
height: 344px;
4-
border-radius: 16px;
5-
padding: 32px 28px 32px 28px;
6-
margin-top: 20px;
2+
width: 620px;
3+
height: 344px;
4+
border-radius: 16px;
5+
padding: 32px 28px 32px 28px;
6+
margin-top: 20px;
77
background-color: var(--white);
8-
gap: 10px
8+
gap: 10px;
99
}
1010

1111
.header-section {
12-
display: flex;
13-
flex-direction: column;
14-
gap: 40px;
12+
display: flex;
13+
flex-direction: column;
14+
gap: 40px;
1515
}
1616

1717
.header-top {
18-
display: flex;
19-
flex-direction: column;
20-
gap: 24px;
18+
display: flex;
19+
flex-direction: column;
20+
gap: 24px;
2121
}
2222

2323
.title {
24-
font-weight: 700;
25-
font-size: 24px;
26-
line-height: 32px;
24+
font-weight: 700;
25+
font-size: 24px;
26+
line-height: 32px;
2727
}
2828

2929
.sub-container {
30-
display: flex;
31-
flex-direction: column;
32-
gap: 16px;
30+
display: flex;
31+
flex-direction: column;
32+
gap: 16px;
3333
}
3434

3535
.button {
36-
width: 564px;
36+
width: 564px;
37+
}
38+
39+
.sheader-button button {
40+
max-width: 100%;
3741
}
3842

3943
@media screen and (max-width: 1199px) {
40-
.title-container {
41-
width: 544px;
42-
}
44+
.title-container {
45+
width: 544px;
46+
}
4347
}
4448

4549
@media screen and (max-width: 743px) {
46-
.title-container {
47-
width: 284px;
48-
height: 312px;
49-
border-radius: 8px;
50-
padding: 20px 16px 20px 16px;
51-
}
52-
53-
.header-section {
54-
gap: 32px;
55-
}
56-
}
50+
.title-container {
51+
width: 284px;
52+
height: 312px;
53+
border-radius: 8px;
54+
padding: 20px 16px 20px 16px;
55+
}
56+
57+
.header-section {
58+
gap: 32px;
59+
}
60+
}

src/components/product/edit/MainTitle/MainTitle.tsx

Lines changed: 23 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,34 +1,48 @@
1-
import { useState } from 'react';
1+
import { useEffect, useState } from 'react';
22
import { useRouter } from 'next/router';
33
import CDSButton from '@/components/common/button/CDSButton';
44
import TitleTagInput from '@/components/common/input/info-input/TitleTagInput';
55
import putDashboards from '@/lib/editdashboard/putDashboards';
66
import ColorSelector from './ColorSelector';
77
import styles from './MainTitle.module.css';
8+
import { toast } from 'react-toastify';
9+
import useSidebarDashboards from '@/hooks/useSidebar';
810

911
interface MainTitleProps {
1012
dashboardtitle: string | null;
13+
dashboardColor: string | null;
1114
}
1215

13-
export default function MainTitle({ dashboardtitle }: MainTitleProps) {
14-
const [selectedColor, setSelectedColor] = useState<string>('');
16+
export default function MainTitle({
17+
dashboardtitle,
18+
dashboardColor,
19+
}: MainTitleProps) {
20+
const [selectedColor, setSelectedColor] = useState<string>(dashboardColor);
1521
const [title, setTitle] = useState(dashboardtitle || '');
22+
const [newTitle, setNewTitle] = useState(dashboardtitle || '');
1623
const router = useRouter();
1724
const dashboardId = Number(router.query.id);
1825

26+
const { fetchSidebarDashboards } = useSidebarDashboards();
27+
1928
const handleChange = (e: React.ChangeEvent<HTMLInputElement>) => {
2029
setTitle(e.target.value);
2130
};
2231

2332
const handleEditClick = async () => {
2433
try {
25-
const updatedTitle = await putDashboards({
34+
const updatedDashboard = await putDashboards({
2635
title,
2736
color: selectedColor,
2837
dashboardId,
2938
});
30-
setTitle(updatedTitle.title);
31-
router.reload();
39+
40+
setNewTitle(updatedDashboard.title);
41+
setTitle(updatedDashboard.title);
42+
setSelectedColor(updatedDashboard.color);
43+
44+
toast.success('변경 사항이 저장되었습니다.');
45+
fetchSidebarDashboards(1);
3246
} catch (error) {
3347
throw new Error(`${error}`);
3448
}
@@ -39,9 +53,9 @@ export default function MainTitle({ dashboardtitle }: MainTitleProps) {
3953
<div className={styles['header-section']}>
4054
<div className={styles['header-top']}>
4155
<h1 className={styles.title}>
42-
{dashboardtitle && dashboardtitle.length > 10
43-
? `${dashboardtitle.slice(0, 10)}...`
44-
: dashboardtitle}
56+
{newTitle && newTitle.length > 10
57+
? `${newTitle.slice(0, 10)}...`
58+
: newTitle}
4559
</h1>
4660
<div className={styles['sub-container']}>
4761
<TitleTagInput

0 commit comments

Comments
 (0)