Skip to content

Commit 24a4aa2

Browse files
authored
Merge pull request #158 from part3-4team-Taskify/feature/Gnb
[Style, Refactor] mydashboard: 레이아웃 수정 / edit: 여백, 정렬 조정 / login: 전체 페이지 비로그인 접근 방지 적용
2 parents 0fa4881 + 0a47175 commit 24a4aa2

File tree

9 files changed

+109
-75
lines changed

9 files changed

+109
-75
lines changed

src/components/modal/NewDashboard.tsx

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import { useState } from "react";
22
import Input from "../input/Input";
33
import Image from "next/image";
44
import { createDashboard } from "@/api/dashboards";
5+
import { toast } from "react-toastify";
56

67
interface Dashboard {
78
id: number;
@@ -38,7 +39,7 @@ export default function NewDashboard({ onClose, onCreate }: NewDashboardProps) {
3839
onCreate?.(response.data);
3940
onClose?.();
4041
} catch {
41-
alert("대시보드 생성에 실패했습니다.");
42+
toast.error("대시보드 생성에 실패했습니다.");
4243
} finally {
4344
setLoading(false);
4445
}

src/components/modalInput/ModalImage.tsx

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import Image from "next/image";
22
import { ChangeEvent, useEffect, useRef, useState } from "react";
33
import AddButton from "./AddButton";
44
import { uploadCardImage } from "@/api/card";
5+
import { toast } from "react-toastify";
56

67
interface ModalImageProps {
78
label: string;
@@ -51,7 +52,7 @@ export default function ModalImage({
5152
onImageSelect(imageUrl);
5253
} catch (error) {
5354
console.error("이미지 업로드 실패:", error);
54-
alert("이미지 업로드에 실패했어요.");
55+
toast.error("이미지 업로드에 실패했어요.");
5556
}
5657
};
5758

src/components/modalInput/ToDoModal.tsx

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import ModalTextarea from "@/components/modalInput/ModalTextarea";
66
import ModalImage from "@/components/modalInput/ModalImage";
77
import TextButton from "@/components/modalInput/TextButton";
88
import { useRouter } from "next/router";
9+
import { toast } from "react-toastify";
910

1011
interface TaskModalProps {
1112
isOpen: boolean;
@@ -66,7 +67,7 @@ export default function TaskModal({
6667
const assigneeUserId = selectedAssignee?.userId;
6768

6869
if (!assigneeUserId) {
69-
alert("담당자를 선택해 주세요.");
70+
toast("담당자를 선택해 주세요.");
7071
return;
7172
}
7273

@@ -85,7 +86,7 @@ export default function TaskModal({
8586
onClose();
8687
} catch (err) {
8788
console.error("카드 생성 실패:", err);
88-
alert("카드 생성에 실패했습니다.");
89+
toast.error("카드 생성에 실패했습니다.");
8990
}
9091
};
9192

src/components/table/invited/InvitedDashBoard.tsx

Lines changed: 74 additions & 59 deletions
Original file line numberDiff line numberDiff line change
@@ -95,56 +95,44 @@ function InvitedList({
9595
};
9696

9797
return (
98-
<div className="relative bg-white w-full max-w-[260px] sm:max-w-[504px] lg:max-w-[966px] mx-auto mt-[40px]">
98+
/* 초대 목록 헤더 */
99+
<div className="relative bg-white w-full max-w-[260px] sm:max-w-[504px] lg:max-w-[966px] mx-auto mt-1">
99100
{filteredData.length > 0 && (
100-
<div className="hidden sm:grid grid-cols-3 px-6 w-full h-[26px] justify-start items-center">
101-
<p className="lg:ml-10 font-normal text-[var(--color-gray2)]">이름</p>
102-
<p className="font-normal text-[var(--color-gray2)]">초대자</p>
103-
<p className="lg:ml-13 font-normal text-[var(--color-gray2)]">
101+
<div className="font-16r hidden sm:grid grid-cols-[3fr_2fr_3fr] px-4 w-full h-[26px] items-center mb-5">
102+
<p className="text-[var(--color-gray2)] whitespace-nowrap">이름</p>
103+
<p className="text-[var(--color-gray2)] whitespace-nowrap">초대자</p>
104+
<p className="text-center text-[var(--color-gray2)] whitespace-nowrap">
104105
수락 여부
105106
</p>
106107
</div>
107108
)}
108-
<div className="scroll-area h-[400px] overflow-y-auto overflow-x-hidden">
109-
{filteredData.length > 0
110-
? filteredData.map((invite, index) => (
111-
<div
112-
key={index}
113-
className="pb-5 mb-[20px] w-full max-w-[260px] sm:max-w-[504px] lg:max-w-[966px] h-auto sm:h-[50px]
114-
sm:grid sm:grid-cols-[1fr_1fr_1fr] sm:items-center flex flex-col gap-10 border-b border-[var(--color-gray4)]"
115-
>
116-
{/* 모바일 레이아웃 */}
117-
<div className="flex flex-col mt-1 sm:hidden">
109+
110+
{/* 리스트 */}
111+
<div className="scroll-area h-[150vw] max-h-[570px] sm:max-h-[320px] lg:max-h-[400px] overflow-y-auto overflow-x-hidden">
112+
{filteredData.length > 0 ? (
113+
filteredData.map((invite, index) => (
114+
<div
115+
key={index}
116+
className="pb-5 mb-[20px] w-full max-w-[260px] sm:max-w-[504px] lg:max-w-[966px]
117+
h-auto sm:h-[50px] border-b border-[var(--color-gray4)]
118+
sm:grid sm:grid-cols-[3fr_2fr_3fr] sm:items-center
119+
flex flex-col gap-10"
120+
>
121+
{/* 모바일 레이아웃 */}
122+
<div className="flex flex-col mt-1 sm:hidden px-4 w-full gap-2">
123+
<div className="flex justify-between">
118124
<span className="text-[var(--color-gray2)]">이름</span>
119-
<span className="text-[#333236]">{invite.title}</span>
120-
<span className="mr-3.5 text-[var(--color-gray2)]">
121-
초대자
122-
</span>{" "}
123-
<span className="text-[#333236]">{invite.nickname}</span>
124-
<div className="flex gap-2 mt-2 justify-center">
125-
<button
126-
className="cursor-pointer border px-3 py-1 rounded-md w-[84px] h-[32px] text-[var(--primary)] border-[var(--color-gray3)]"
127-
onClick={() => rejectInvite(invite.id)}
128-
>
129-
거절
130-
</button>
131-
<button
132-
className="cursor-pointer bg-[var(--primary)] text-white px-3 py-1 rounded-md w-[84px] h-[32px]"
133-
onClick={() => acceptInvite(invite.id)}
134-
>
135-
수락
136-
</button>
137-
</div>
125+
<span className="text-[#333236] font-medium">
126+
{invite.title}
127+
</span>
138128
</div>
139-
140-
{/* 웹, 태블릿 레이아웃 */}
141-
<p className="lg:ml-21 md:ml-11 ml-9 justify-left mt-1 w-full hidden sm:flex">
142-
{invite.title}
143-
</p>
144-
<p className="lg:mr-25 md:mr-10 ml-9 justify-left mt-1 hidden sm:flex">
145-
{invite.nickname}
146-
</p>
147-
<div className="lg:mr-5 gap-2 mt-1 mr-2 justify-between sm:justify-start hidden sm:flex">
129+
<div className="flex justify-between">
130+
<span className="text-[var(--color-gray2)]">초대자</span>
131+
<span className="text-[#333236] font-medium">
132+
{invite.nickname}
133+
</span>
134+
</div>
135+
<div className="flex gap-2 mt-1 justify-end">
148136
<button
149137
className="cursor-pointer border px-3 py-1 rounded-md w-[84px] h-[32px] text-[var(--primary)] border-[var(--color-gray3)]"
150138
onClick={() => rejectInvite(invite.id)}
@@ -159,15 +147,43 @@ function InvitedList({
159147
</button>
160148
</div>
161149
</div>
162-
))
163-
: !hasMore && <NoResultDashBoard searchTitle={searchTitle} />}{" "}
164-
{/* 검색 내역이 없을 경우 */}
150+
151+
{/* 웹/태블릿 레이아웃 */}
152+
<div className="hidden sm:flex items-center pl-4">
153+
<p className="text-[#333236]">{invite.title}</p>
154+
</div>
155+
<div className="hidden sm:flex items-center pl-5 lg:pl-0">
156+
<p className="text-[#333236]">{invite.nickname}</p>
157+
</div>
158+
<div className="hidden sm:flex items-center justify-center gap-2 mr-1 lg:mr-12">
159+
<button
160+
className="cursor-pointer border px-3 py-1 rounded-md w-[84px] h-[32px] text-[var(--primary)] border-[var(--color-gray3)]"
161+
onClick={() => rejectInvite(invite.id)}
162+
>
163+
거절
164+
</button>
165+
<button
166+
className="cursor-pointer bg-[var(--primary)] text-white px-3 py-1 rounded-md w-[84px] h-[32px]"
167+
onClick={() => acceptInvite(invite.id)}
168+
>
169+
수락
170+
</button>
171+
</div>
172+
</div>
173+
))
174+
) : !hasMore ? (
175+
<NoResultDashBoard searchTitle={searchTitle} />
176+
) : null}
177+
178+
{/* 검색 결과가 존재하지만 더 이상 데이터가 없을 때 */}
165179
{filteredData.length > 0 && !hasMore && (
166-
<p className="lg:mr-18 text-center text-gray-400 bg-transparent">
180+
<p className="text-center text-gray-400 bg-transparent">
167181
더 이상 초대 목록이 없습니다.
168182
</p>
169183
)}
170-
{hasMore && <div ref={observerRef} className="h-[50px] w-[50px]"></div>}
184+
185+
{/* 인터섹션 옵저버 */}
186+
{hasMore && <div ref={observerRef} className="h-[50px] w-[50px]" />}
171187
</div>
172188
</div>
173189
);
@@ -264,34 +280,33 @@ export default function InvitedDashBoard() {
264280
) : (
265281
<div className="relative bg-white rounded-lg shadow-md w-[260px] sm:w-[504px] lg:w-[1022px] h-[770px] sm:h-[592px] lg:h-[620px] max-w-none">
266282
<div className="flex flex-col p-6 w-full h-[104px]">
267-
<div className="flex flex-col w-[228px] sm:w-[448px] lg:w-[966px] gap-[24px]">
283+
<div className="flex flex-col w-full sm:w-[448px] lg:w-[966px] gap-[24px]">
268284
<p className="text-black3 text-xl sm:text-2xl font-bold">
269285
초대받은 대시보드
270286
</p>
271287

272-
<div className="relative w-[228px] sm:w-[448px] lg:w-[966px] mx-auto">
288+
<div className="relative w-full sm:w-[448px] lg:w-[966px] mx-auto">
273289
<input
274290
id="title"
275291
placeholder="검색"
276292
type="text"
277293
value={searchTitle}
278294
onChange={handleSearchInputChange}
279-
className="text-[var(--color-gray2)] w-full h-[40px] px-[40px] py-[6px] border border-[#D9D9D9] bg-white rounded-[6px] placeholder-gray-400 outline-none"
295+
className="text-black3 w-full h-[40px] pl-[40px] py-[6px] border border-[#D9D9D9] bg-white rounded-[6px] placeholder-gray-400 outline-none"
280296
/>
281297
<Search
282298
width={18}
283299
height={18}
284300
color="#333236"
285-
className="absolute left-[12px] top-1/2 transform -translate-y-1/2"
286-
/>
287-
288-
<InvitedList
289-
searchTitle={searchTitle}
290-
invitationData={invitationArray}
291-
fetchNextPage={fetchNextPage}
292-
hasMore={hasMore}
301+
className="absolute left-[16px] top-1/2 transform -translate-y-1/2 z-50"
293302
/>
294303
</div>
304+
<InvitedList
305+
searchTitle={searchTitle}
306+
invitationData={invitationArray}
307+
fetchNextPage={fetchNextPage}
308+
hasMore={hasMore}
309+
/>
295310
</div>
296311
</div>
297312
</div>

src/pages/dashboard/[dashboardId]/edit.tsx

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import React, { useState, useEffect } from "react";
22
import { useRouter } from "next/router";
3+
import { useAuthGuard } from "@/hooks/useAuthGuard";
34
import ChangeBebridge from "@/components/modal/ChangeBebridge";
45
import HeaderDashboardEdit from "@/components/gnb/HeaderDashboard";
56
import MemberList from "@/components/table/member/MemberList";
@@ -10,9 +11,11 @@ import { getDashboards } from "@/api/dashboards";
1011
import DeleteDashboardModal from "@/components/modal/DeleteDashboardModal";
1112
import { DashboardType } from "@/types/task";
1213
import { TEAM_ID } from "@/constants/team";
14+
import LoadingSpinner from "@/components/common/LoadingSpinner";
1315

1416
export default function EditDashboard() {
1517
const router = useRouter();
18+
const { user, isInitialized } = useAuthGuard();
1619
const [dashboardList, setDashboardList] = useState<DashboardType[]>([]);
1720
const { dashboardId } = router.query;
1821
const [isModalOpen, setIsModalOpen] = useState(false);
@@ -45,8 +48,14 @@ export default function EditDashboard() {
4548
};
4649

4750
useEffect(() => {
48-
fetchDashboards();
49-
}, []);
51+
if (isInitialized && user) {
52+
fetchDashboards();
53+
}
54+
}, [isInitialized, user]);
55+
56+
if (!isInitialized || !user) {
57+
return <LoadingSpinner />;
58+
}
5059

5160
return (
5261
<div className="flex h-screen overflow-hidden">

src/pages/dashboard/[dashboardId]/index.tsx

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
// src/pages/dashboard/[dashboardId]/index.tsx
22
import React, { useState, useEffect } from "react";
33
import { useRouter } from "next/router";
4+
import { useAuthGuard } from "@/hooks/useAuthGuard";
45
import { getColumns, createColumn } from "@/api/columns";
56
import { getCardsByColumn } from "@/api/card";
67
import { getDashboards } from "@/api/dashboards";
@@ -17,9 +18,11 @@ import ColumnsButton from "@/components/button/ColumnsButton";
1718
import AddColumnModal from "@/components/columnCard/AddColumnModal";
1819
import { TEAM_ID } from "@/constants/team";
1920
import { toast } from "react-toastify";
21+
import LoadingSpinner from "@/components/common/LoadingSpinner";
2022

2123
export default function Dashboard() {
2224
const router = useRouter();
25+
const { user, isInitialized } = useAuthGuard();
2326
const { dashboardId } = router.query;
2427
const [columns, setColumns] = useState<ColumnType[]>([]);
2528
const [tasksByColumn, setTasksByColumn] = useState<TasksByColumn>({});
@@ -41,12 +44,11 @@ export default function Dashboard() {
4144
const isMaxColumns = columns.length >= 10;
4245
const isCreateDisabled = isTitleEmpty || isDuplicate || isMaxColumns;
4346

44-
// router 준비되었을 때 렌더링
4547
useEffect(() => {
46-
if (router.isReady && dashboardId) {
48+
if (router.isReady && dashboardId && isInitialized && user) {
4749
setIsReady(true);
4850
}
49-
}, [router.isReady, dashboardId]);
51+
}, [router.isReady, dashboardId, isInitialized, user]);
5052

5153
// 대시보드 목록 불러오기
5254
const fetchDashboards = async () => {
@@ -60,7 +62,7 @@ export default function Dashboard() {
6062

6163
// 대시보드 및 칼럼/카드 데이터 패칭
6264
useEffect(() => {
63-
if (!isReady || !dashboardId) return;
65+
if (!isReady || !dashboardId || !isInitialized || !user) return;
6466

6567
fetchDashboards();
6668

@@ -93,9 +95,11 @@ export default function Dashboard() {
9395
};
9496

9597
fetchColumnsAndCards();
96-
}, [isReady, dashboardId]);
98+
}, [isReady, dashboardId, isInitialized, user]);
9799

98-
if (!isReady) return <div>로딩 중...</div>;
100+
if (!isReady || !isInitialized || !user) {
101+
return <LoadingSpinner />;
102+
}
99103

100104
return (
101105
<div className="flex h-screen overflow-hidden ">

src/pages/login.tsx

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import { postAuthData } from "@/api/auth";
66
import Link from "next/link";
77
import Input from "@/components/input/Input";
88
import { apiRoutes } from "@/api/apiRoutes";
9+
import { toast } from "react-toastify";
910

1011
export default function LoginPage() {
1112
const router = useRouter();
@@ -40,7 +41,7 @@ export default function LoginPage() {
4041
router.push("/mydashboard");
4142
} catch (error) {
4243
console.error("로그인 실패:", error);
43-
alert("로그인에 실패했습니다.");
44+
toast.error("로그인에 실패했습니다.");
4445
}
4546
};
4647

src/pages/mydashboard.tsx

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ import { DeleteModal } from "@/components/modal/DeleteModal";
1414
import LoadingSpinner from "@/components/common/LoadingSpinner";
1515
import { TEAM_ID } from "@/constants/team";
1616
import { Search } from "lucide-react";
17+
import { toast } from "react-toastify";
1718

1819
interface Dashboard {
1920
id: number;
@@ -113,7 +114,7 @@ export default function MyDashboardPage() {
113114
setSelectedDashboardId(null);
114115
fetchDashboards();
115116
} catch (error) {
116-
alert("대시보드 삭제에 실패했습니다.");
117+
toast.error("대시보드 삭제에 실패했습니다.");
117118
console.error("삭제 실패:", error);
118119
}
119120
};
@@ -195,7 +196,7 @@ export default function MyDashboardPage() {
195196

196197
{/* 초대받은 대시보드 */}
197198
<section className="w-full">
198-
<div className="mt-[74px]">
199+
<div className="mt-[50px]">
199200
<InvitedDashBoard />
200201
</div>
201202
</section>

src/pages/signup.tsx

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import Input from "@/components/input/Input";
55
import Link from "next/link";
66
import { Modal } from "@/components/modal/Modal";
77
import { CustomBtn } from "@/components/button/CustomButton";
8+
import { toast } from "react-toastify";
89

910
export default function SignUpPage() {
1011
const [email, setEmail] = useState("");
@@ -44,7 +45,7 @@ export default function SignUpPage() {
4445
setIsSuccessModalOpen(true);
4546
} catch (error) {
4647
console.error("회원가입 실패", error);
47-
alert("회원가입에 실패했습니다.");
48+
toast.error("회원가입에 실패했습니다.");
4849
}
4950
};
5051

0 commit comments

Comments
 (0)