Skip to content

Commit 10ec577

Browse files
authored
Merge pull request #89 from gyoyeon-kim/card-table
[Feat] Page: 대시보드 수정 페이지 / Moal : 초대하기
2 parents 8af8fa6 + 8dda85b commit 10ec577

File tree

17 files changed

+384
-70
lines changed

17 files changed

+384
-70
lines changed

public/svgs/close-white.svg

Lines changed: 4 additions & 0 deletions
Loading

src/api/apiRoutes.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -22,9 +22,9 @@ export const apiRoutes = {
2222
`/${TEAM_ID}/dashboards/${dashboardId}`, //get,put,delete
2323
//대시보드 초대하기
2424
DashboardInvite: (dashboardId: number) =>
25-
`/${TEAM_ID}/dashboards/${dashboardId}/dashboard-invite`, //post,get
25+
`/${TEAM_ID}/dashboards/${dashboardId}/invitations`, //post,get
2626
DashboardInviteDelete: (dashboardId: number, invitationId: number) =>
27-
`/${TEAM_ID}/dashboards/${dashboardId}/dashboard-invite/${invitationId}`, //delete
27+
`/${TEAM_ID}/dashboards/${dashboardId}/invitations/${invitationId}`, //delete
2828
//초대 받은 대시보드
2929
Invitations: () => `/${TEAM_ID}/members`, //get
3030
invitationDetail: (invitationId: number) =>

src/api/members.ts

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,8 +17,11 @@ export const getMembers = async (dashboardId?: string | string[]) => {
1717
typeof dashboardId === "string" ? Number(dashboardId) : undefined;
1818

1919
const response = await axios.get<ApiResponse>(
20-
`https://sp-taskify-api.vercel.app/13-4/members?page=1&size=20&dashboardId=${numericDashboardId}`,
20+
`${process.env.NEXT_PUBLIC_BASE_URL}13-4/members`,
2121
{
22+
params: {
23+
dashboardId: numericDashboardId,
24+
},
2225
headers: {
2326
Authorization: `Bearer ${token}`,
2427
},

src/components/gnb/HeaderBebridge.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ import { MemberType, UserType } from "../../types/users";
55
import { getMembers } from "@/api/members";
66
import { getUserInfo } from "@/api/user";
77
import RandomProfile from "../table/member/RandomProfile";
8-
import NewDashboard from "../modal/NewDashboard";
8+
import InviteDashboard from "../modal/InviteDashboard";
99

1010
interface HeaderBebridgeProps {
1111
dashboardId?: string | string[];
@@ -101,7 +101,7 @@ const HeaderBebridge: React.FC<HeaderBebridgeProps> = ({ dashboardId }) => {
101101
/>
102102
<span className="text-sm md:text-base text-gray1">초대하기</span>
103103
</button>
104-
{isModalOpen && <NewDashboard onClose={closeInviteModal} />}
104+
{isModalOpen && <InviteDashboard onClose={closeInviteModal} />}
105105
</div>
106106

107107
{/*4개의 프로필 아이콘 표시, 나머지 멤버 숫자 +n 아이콘으로 표시*/}

src/components/gnb/HeaderDashboard.tsx

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ import SkeletonUser from "../../shared/skeletonUser";
44
import { UserType } from "../../types/users";
55
import { getUserInfo } from "@/api/user";
66
import RandomProfile from "../table/member/RandomProfile";
7-
import NewDashboard from "../modal/NewDashboard";
7+
import InviteDashboard from "../modal/InviteDashboard";
88

99
const HeaderDashboard = () => {
1010
const [user, setUser] = useState<UserType | null>(null);
@@ -15,7 +15,7 @@ const HeaderDashboard = () => {
1515
const { dashboardId } = router.query;
1616

1717
const goToDashboardEdit = () => {
18-
router.push(`/dashboard/${dashboardId}/edit`);
18+
router.push(`/mypage`);
1919
};
2020
/*초대하기 버튼 클릭 시 모달 팝업 오픈*/
2121
const [isModalOpen, setIsModalOpen] = useState(false);
@@ -76,7 +76,7 @@ const HeaderDashboard = () => {
7676
/>
7777
<span className="text-sm md:text-base text-gray1">초대하기</span>
7878
</button>
79-
{isModalOpen && <NewDashboard onClose={closeInviteModal} />}
79+
{isModalOpen && <InviteDashboard onClose={closeInviteModal} />}
8080
</div>
8181

8282
{/*구분선*/}

src/components/modal/ChangeBebridge.tsx

Lines changed: 35 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { useState } from "react";
1+
import { useState, useEffect } from "react";
22
import { useRouter } from "next/router";
33
import Input from "../input/Input";
44
import Image from "next/image";
@@ -8,11 +8,41 @@ import { apiRoutes } from "@/api/apiRoutes";
88
const ChangeBebridge = () => {
99
const router = useRouter();
1010
const { dashboardId } = router.query; // dashboardId 쿼리값 받기
11+
const [dashboardDetail, setdashboardDetail] = useState<{ title?: string }>(
12+
{}
13+
);
1114
const [title, setTitle] = useState("");
1215
const [selected, setSelected] = useState<number | null>(null);
1316
const colors = ["#7ac555", "#760DDE", "#FF9800", "#76A5EA", "#E876EA"];
14-
const token = process.env.NEXT_PUBLIC_API_TOKEN;
1517

18+
/* 대시보드 이름 데이터 */
19+
useEffect(() => {
20+
const fetchDashboardTitle = async () => {
21+
try {
22+
const dashboardIdNumber = Number(dashboardId);
23+
const res = await axiosInstance.get(
24+
apiRoutes.DashboardDetail(dashboardIdNumber),
25+
{
26+
params: {
27+
dashboardId,
28+
},
29+
}
30+
);
31+
if (res.data) {
32+
const dashboardData = res.data;
33+
setdashboardDetail(dashboardData);
34+
console.log("dashboardData", dashboardData);
35+
}
36+
} catch (error) {
37+
console.error("대시보드 상세내용 불러오는데 오류 발생:", error);
38+
}
39+
};
40+
if (dashboardId) {
41+
fetchDashboardTitle();
42+
}
43+
}, [dashboardId]);
44+
45+
/* 대시보드 이름 변경 버튼 */
1646
const handleUpdate = async () => {
1747
const dashboardIdNumber = Number(dashboardId); // string dashboradId 값 number로 변경
1848
if (!dashboardId || !title || selected === null) return;
@@ -25,13 +55,7 @@ const ChangeBebridge = () => {
2555
try {
2656
const response = await axiosInstance.put(
2757
apiRoutes.DashboardDetail(dashboardIdNumber),
28-
payload,
29-
{
30-
headers: {
31-
Authorization: `Bearer ${token}`,
32-
"Content-Type": "application/json",
33-
},
34-
}
58+
payload
3559
);
3660
console.log("업데이트 성공:", response.data);
3761
alert("대시보드가 업데이트되었습니다!"); // 추후에 toast로 변경
@@ -48,11 +72,12 @@ const ChangeBebridge = () => {
4872
<Input
4973
type="text"
5074
onChange={setTitle}
51-
label="대시보드 이름"
75+
label={`대시보드 이름 : ${dashboardDetail?.title || ""}`}
5276
labelClassName="text-lg sm:text-base text-black3 mt-6"
5377
placeholder="뉴프로젝트"
5478
className="max-w-[620px] mb-1"
5579
/>
80+
5681
<div className="flex mt-3">
5782
{colors.map((color, index) => (
5883
<div key={index} className="relative">
Lines changed: 96 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,96 @@
1+
import { useState } from "react";
2+
import { useRouter } from "next/router";
3+
import Input from "../input/Input";
4+
import Image from "next/image";
5+
import axiosInstance from "@/api/axiosInstance";
6+
import { apiRoutes } from "@/api/apiRoutes";
7+
import { AxiosError } from "axios";
8+
9+
export default function InviteDashboard({ onClose }: { onClose?: () => void }) {
10+
const [email, setEmail] = useState("");
11+
const router = useRouter();
12+
const { dashboardId } = router.query;
13+
14+
const isValidEmail = (email: string) => {
15+
return /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(email);
16+
};
17+
18+
/* 초대하기 버튼 */
19+
const handleSubmit = async () => {
20+
const dashboardIdNumber = Number(dashboardId);
21+
if (!dashboardId || !email) return;
22+
23+
const payload = {
24+
email,
25+
};
26+
27+
try {
28+
const response = await axiosInstance.post(
29+
apiRoutes.DashboardInvite(dashboardIdNumber),
30+
payload
31+
);
32+
onClose?.(); // 함수 있을때만 실행
33+
window.location.reload();
34+
} catch (error) {
35+
console.error("초대 실패:", error);
36+
37+
if (error instanceof AxiosError) {
38+
if (error.response?.status === 403) {
39+
alert("초대 권한이 없습니다.");
40+
} else if (error.response?.status === 404) {
41+
alert("대시보드 또는 유저가 존재하지 않습니다.");
42+
} else {
43+
alert("오류가 발생했습니다.");
44+
}
45+
} else {
46+
alert("네트워크 오류가 발생했습니다.");
47+
}
48+
49+
window.location.reload();
50+
}
51+
};
52+
53+
return (
54+
<div className="fixed inset-0 flex items-center justify-center bg-black/35 z-50">
55+
<div className="bg-white p-6 rounded-lg shadow-lg w-[327px] sm:w-[568px] sm:h-[279px]">
56+
<div className="flex justify-between items-center">
57+
<h2 className="text-sm sm:text-[24px] font-bold">초대하기</h2>
58+
<Image
59+
src="/svgs/close-white.svg"
60+
alt="닫기"
61+
width={25}
62+
height={25}
63+
className="cursor-pointer"
64+
onClick={onClose}
65+
></Image>
66+
</div>
67+
<Input
68+
type="text"
69+
onChange={setEmail}
70+
label="이메일"
71+
labelClassName="text-lg sm:text-base text-black3 mt-6"
72+
placeholder="이메일을 입력해주세요"
73+
className="max-w-[620px] mb-1"
74+
/>
75+
76+
<div className="mt-8 flex justify-between">
77+
<button
78+
onClick={onClose}
79+
className="cursor-pointer sm:w-[256px] sm:h-[54px] w-[295px] h-[54px] rounded-[8px] border border-[var(--color-gray3)] text-[var(--color-gray1)]"
80+
>
81+
취소
82+
</button>
83+
<button
84+
onClick={handleSubmit}
85+
disabled={!email || !isValidEmail(email)}
86+
className={`cursor-pointer sm:w-[256px] sm:h-[54px] w-[295px] h-[54px] rounded-[8px]
87+
border border-[var(--color-gray3)] text-[var(--color-white)]
88+
${!email || !isValidEmail(email) ? "bg-gray-300 cursor-not-allowed" : "bg-[var(--primary)]"}`}
89+
>
90+
생성
91+
</button>
92+
</div>
93+
</div>
94+
</div>
95+
);
96+
}

src/components/modal/NewDashboard.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ export default function NewDashboard({ onClose }: { onClose?: () => void }) {
2121
try {
2222
setLoading(true);
2323
const response = await axios.post(
24-
`https://sp-taskify-api.vercel.app/13-4/dashboards`,
24+
`${process.env.NEXT_PUBLIC_BASE_URL}13-4/dashboards`,
2525
payload,
2626
{
2727
headers: {

0 commit comments

Comments
 (0)