Skip to content

Commit 0ba049d

Browse files
authored
Merge pull request #111 from hyeonjiroh/feat/#104/dashboard-edit-page
feat: #110/대시보드 수정 페이지 일부 기능 구현
2 parents 70a3bd0 + f00a54f commit 0ba049d

18 files changed

Lines changed: 374 additions & 68 deletions

File tree

src/app/(after-login)/dashboard/[dashboardid]/edit/_components/BackButton.tsx

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -18,12 +18,12 @@ export default function BackButton() {
1818
>
1919
<Image
2020
src={BackIcon}
21-
className="scale-x-[-1] w-[18px] h-[18px] tablet:w-[18px] tablet:h-[18px]"
21+
className="scale-x-[-1] size-[18px] tablet:size-[18px]"
2222
alt=""
2323
/>
24-
<div className="font-medium text-md text-gray-800 tablet:text-lg">
24+
<p className="font-medium text-md text-gray-800 tablet:text-lg">
2525
돌아가기
26-
</div>
26+
</p>
2727
</button>
2828
</div>
2929
);

src/app/(after-login)/dashboard/[dashboardid]/edit/_components/DashboardEditSection.tsx

Lines changed: 84 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,98 @@
11
"use client";
22

3+
import { useEffect, useState } from "react";
4+
import { useDashboardStore } from "@/lib/store/useDashboardStore";
5+
import { DashboardDetail } from "@/lib/types";
6+
import { fetchDashboard, putDashboard } from "@/lib/apis/dashboardsApi";
7+
import ColorPalette, {
8+
ColorCode,
9+
} from "@/components/common/color-palette/ColorPalette";
310
import Input from "@/components/common/input/Input";
411
import Button from "@/components/common/button/Button";
512

6-
export default function DashboardEditSection() {
13+
export default function DashboardEditSection({
14+
id,
15+
token,
16+
}: {
17+
id: number;
18+
token: string;
19+
}) {
20+
const [data, setData] = useState<DashboardDetail | null>(null);
21+
const [dashboardName, setDashboardName] = useState("");
22+
const [selectedColor, setSelectedColor] = useState<ColorCode | "">("");
23+
const [isFormValid, setIsFormValid] = useState(false);
24+
const setDashboardId = useDashboardStore((state) => state.setDashboardId);
25+
26+
useEffect(() => {
27+
const getData = async () => {
28+
const data = await fetchDashboard({
29+
token,
30+
id: String(id),
31+
});
32+
setData(data);
33+
setDashboardName(data.title);
34+
setSelectedColor(data.color);
35+
};
36+
37+
getData();
38+
}, []);
39+
40+
const onColorSelect = (color: ColorCode | "") => {
41+
setSelectedColor(() => {
42+
return color;
43+
});
44+
};
45+
46+
useEffect(() => {
47+
const trimmedValue = dashboardName.trim();
48+
const isValid = trimmedValue !== "" && selectedColor !== "";
49+
setIsFormValid(isValid);
50+
}, [dashboardName, selectedColor]);
51+
52+
const onDashboardNameChange = (e: React.ChangeEvent<HTMLInputElement>) => {
53+
setDashboardName(e.target.value);
54+
};
55+
56+
const editDashboard = async () => {
57+
await putDashboard({
58+
token,
59+
title: dashboardName,
60+
color: selectedColor,
61+
id,
62+
});
63+
64+
window.location.replace(`/dashboard/${id}`);
65+
setDashboardId(String(id));
66+
};
67+
68+
if (!data) return;
69+
770
return (
871
<div className="w-full p-4 rounded-lg bg-white tablet:p-6">
972
<div className="flex flex-col gap-10 tablet:gap-6">
10-
<div className="font-bold text-2lg text-gray-800 tablet:text-2xl">
11-
대시보드 이름
73+
<div className="font-bold text-xl text-gray-800 tablet:text-2xl">
74+
{data.title}
1275
</div>
13-
<div className="flex flex-col gap-6">
76+
<div className="flex flex-col gap-8 tablet:gap-10">
1477
<div className="flex flex-col gap-4">
15-
<Input label="대시보드 이름" />
78+
<Input
79+
label="대시보드 이름"
80+
value={dashboardName}
81+
placeholder="대시보드 이름을 입력하세요"
82+
onChange={onDashboardNameChange}
83+
/>
84+
<ColorPalette
85+
onSelect={onColorSelect}
86+
selectedColor={selectedColor}
87+
/>
1688
</div>
17-
<Button variant="purple">변경</Button>
89+
<Button
90+
variant="purple"
91+
onClick={editDashboard}
92+
disabled={!isFormValid}
93+
>
94+
변경
95+
</Button>
1896
</div>
1997
</div>
2098
</div>
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
"use client";
2+
3+
import { useRouter } from "next/navigation";
4+
import { useDashboardStore } from "@/lib/store/useDashboardStore";
5+
import { deleteDashboard } from "@/lib/apis/dashboardsApi";
6+
import ROUTE from "@/lib/constants/route";
7+
8+
export default function DeleteButton({
9+
id,
10+
token,
11+
}: {
12+
id: number;
13+
token: string;
14+
}) {
15+
const router = useRouter();
16+
const setDashboardId = useDashboardStore((state) => state.setDashboardId);
17+
18+
const handleDeleteClick = async () => {
19+
await deleteDashboard({
20+
token,
21+
id,
22+
});
23+
24+
router.push(ROUTE.MYDASHBOARD);
25+
setDashboardId(null);
26+
};
27+
28+
return (
29+
<button
30+
type="button"
31+
onClick={handleDeleteClick}
32+
className="max-w-[320px] rounded-lg border border-gray-400 bg-gray-200 font-medium text-lg text-gray-800 tablet:h-[62px] tablet:text-2lg hover:bg-gray-300"
33+
>
34+
대시보드 삭제하기
35+
</button>
36+
);
37+
}
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
import { Invitation } from "@/lib/types";
2+
import { deleteInvitation } from "@/lib/apis/dashboardsApi";
3+
import Button from "@/components/common/button/Button";
4+
5+
type InvitationCardProps = Invitation & {
6+
token: string;
7+
};
8+
9+
export default function InvitationCard({
10+
id,
11+
dashboard,
12+
invitee,
13+
token,
14+
}: InvitationCardProps) {
15+
const handleDeleteClick = async () => {
16+
await deleteInvitation({
17+
token,
18+
dashboardId: dashboard.id,
19+
invitationId: id,
20+
});
21+
22+
window.location.reload();
23+
};
24+
25+
return (
26+
<div className="py-3 border-b border-gray-400 tablet:py-4">
27+
<div className="flex justify-between items-center">
28+
<div>{invitee.email}</div>
29+
<Button
30+
variant="whiteViolet"
31+
onClick={handleDeleteClick}
32+
className="w-[52px] max-h-[32px] tablet:w-[84px]"
33+
>
34+
취소
35+
</Button>
36+
</div>
37+
</div>
38+
);
39+
}
Lines changed: 64 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,13 @@
11
"use client";
22

3+
import { useEffect, useState } from "react";
4+
import { Invitation } from "@/lib/types";
5+
import { fetchInvitationList } from "@/lib/apis/dashboardsApi";
36
import Pagination from "@/components/common/pagenation-button/PagenationButton";
7+
import InvitationCard from "./InvitationCard";
8+
import InviteModalButton from "./InviteModalButton";
9+
10+
const PAGE_SIZE = 4;
411

512
export default function InvitationSection({
613
id,
@@ -9,27 +16,67 @@ export default function InvitationSection({
916
id: number;
1017
token: string;
1118
}) {
12-
// id 값과 token 값 사용하고 나서는 지워도 되는 코드들
13-
console.log(id);
14-
console.log(token);
19+
const [items, setItems] = useState<Invitation[]>([]);
20+
const [page, setPage] = useState(1);
21+
const [totalCount, setTotalCount] = useState(0);
22+
const totalPage = Math.ceil(totalCount / PAGE_SIZE);
23+
24+
const handleLoad = async () => {
25+
const { invitations, totalCount } = await fetchInvitationList({
26+
token,
27+
id,
28+
page,
29+
size: PAGE_SIZE,
30+
});
31+
setItems(invitations);
32+
setTotalCount(totalCount);
33+
};
34+
35+
useEffect(() => {
36+
handleLoad();
37+
}, [page]);
38+
39+
const handlePrevPage = () => {
40+
if (page > 1) {
41+
setPage((prev) => prev - 1);
42+
}
43+
};
44+
45+
const handleNextPage = () => {
46+
if (page < totalPage) {
47+
setPage((prev) => prev + 1);
48+
}
49+
};
1550

1651
return (
17-
<div className="w-full p-4 rounded-lg bg-white tablet:p-6">
18-
<div className="flex items-center justify-between">
19-
<h2 className="font-bold text-2lg text-gray-800 tablet:text-2xl">
20-
초대 내역
21-
</h2>
22-
<div className="flex items-center gap-2">
23-
<span>1 페이지 중 1</span>
24-
<Pagination
25-
currentPage={1}
26-
totalPages={1}
27-
onPageChange={(page) => {
28-
console.log(`구성원 페이지 ${page}로 변경`);
29-
}}
30-
/>
52+
<div className="flex flex-col gap-[26px] w-full p-4 rounded-lg bg-white tablet:gap-[17px] tablet:p-6">
53+
<div className="flex justify-between items-center tablet:items-start">
54+
<div className="flex flex-col gap-[14px] tablet:gap-8">
55+
<h2 className="font-bold text-xl text-gray-800 tablet:text-2xl">
56+
초대 내역
57+
</h2>
58+
<p className="font-normal text-md text-gray-500">이메일</p>
59+
</div>
60+
<div className="flex flex-col items-end gap-3 tablet:flex-row tablet:items-center tablet:gap-4">
61+
<div className="flex items-center gap-3 tablet:gap-4">
62+
<p className="font-normal text-xs text-gray-800 tablet:text-md">
63+
{totalPage} 페이지 중 {page}
64+
</p>
65+
<Pagination
66+
currentPage={page}
67+
totalPages={totalPage}
68+
onPrevClick={handlePrevPage}
69+
onNextClick={handleNextPage}
70+
/>
71+
</div>
72+
<InviteModalButton />
3173
</div>
3274
</div>
75+
<div>
76+
{items.map((item) => (
77+
<InvitationCard key={item.id} {...item} token={token} />
78+
))}
79+
</div>
3380
</div>
3481
);
3582
}
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
import { useModalStore } from "@/lib/store/useModalStore";
2+
import Button from "@/components/common/button/Button";
3+
import Image from "next/image";
4+
import AddIcon from "../../../../../../../public/icon/add_box_icon.svg";
5+
6+
export default function InviteModalButton() {
7+
const { openModal } = useModalStore();
8+
9+
return (
10+
<Button
11+
variant="purple"
12+
radius="sm"
13+
onClick={() => {
14+
openModal("invite");
15+
}}
16+
className="flex gap-[6px] w-[86px] max-h-[26px] tablet:gap-2 tablet:w-[105px] tablet:max-h-[32px]"
17+
>
18+
<Image
19+
src={AddIcon}
20+
className="size-[14px] invert brightness-0 tablet:size-4"
21+
alt=""
22+
/>
23+
<div className="font-medium text-xs leading-[18px] tablet:text-md">
24+
초대하기
25+
</div>
26+
</Button>
27+
);
28+
}

src/app/(after-login)/dashboard/[dashboardid]/edit/_components/MemberSection.tsx

Lines changed: 1 addition & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,5 @@
11
"use client";
22

3-
import Pagination from "@/components/common/pagenation-button/PagenationButton";
4-
53
export default function MemberSection({
64
id,
75
token,
@@ -16,18 +14,11 @@ export default function MemberSection({
1614
return (
1715
<div className="w-full p-4 rounded-lg bg-white tablet:p-6">
1816
<div className="flex items-center justify-between">
19-
<h2 className="font-bold text-2lg text-gray-800 tablet:text-2xl">
17+
<h2 className="font-bold text-xl text-gray-800 tablet:text-2xl">
2018
구성원
2119
</h2>
2220
<div className="flex items-center gap-2">
2321
<span>1 페이지 중 1</span>
24-
<Pagination
25-
currentPage={1}
26-
totalPages={1}
27-
onPageChange={(page) => {
28-
console.log(`구성원 페이지 ${page}로 변경`);
29-
}}
30-
/>
3122
</div>
3223
</div>
3324
</div>

src/app/(after-login)/dashboard/[dashboardid]/edit/page.tsx

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,22 +3,24 @@ import BackButton from "./_components/BackButton";
33
import DashboardEditSection from "./_components/DashboardEditSection";
44
import InvitationSection from "./_components/InvitationSection";
55
import MemberSection from "./_components/MemberSection";
6+
import DeleteButton from "./_components/DeleteButton";
67

78
export default function Page({ params }: { params: { dashboardid: string } }) {
89
const dashboardId = Number(params.dashboardid);
910
const accessToken = cookies().get("accessToken")?.value ?? "";
1011

1112
return (
12-
<div>
13-
<div className="flex flex-col px-3 py-4 tablet:px-5">
14-
<div className="flex flex-col gap-[6px] tablet:gap-[29px]">
13+
<div className="flex flex-col px-3 py-4 tablet:px-5">
14+
<div className="flex flex-col gap-6">
15+
<div className="flex flex-col gap-[10px] tablet:gap-[19px] pc:gap-[34px]">
1516
<BackButton />
1617
<div className="flex flex-col gap-4 max-w-[620px]">
17-
<DashboardEditSection />
18+
<DashboardEditSection id={dashboardId} token={accessToken} />
1819
<MemberSection id={dashboardId} token={accessToken} />
1920
<InvitationSection id={dashboardId} token={accessToken} />
2021
</div>
2122
</div>
23+
<DeleteButton id={dashboardId} token={accessToken} />
2224
</div>
2325
</div>
2426
);

src/app/(after-login)/mypage/_components/BackButton.tsx

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -18,12 +18,12 @@ export default function BackButton() {
1818
>
1919
<Image
2020
src={BackIcon}
21-
className="scale-x-[-1] w-[18px] h-[18px] tablet:w-[18px] tablet:h-[18px]"
21+
className="scale-x-[-1] size-[18px] tablet:size-[18px]"
2222
alt=""
2323
/>
24-
<div className="font-medium text-md text-gray-800 tablet:text-lg">
24+
<p className="font-medium text-md text-gray-800 tablet:text-lg">
2525
돌아가기
26-
</div>
26+
</p>
2727
</button>
2828
</div>
2929
);

0 commit comments

Comments
 (0)