Skip to content

Commit a071e4b

Browse files
authored
Merge pull request #127 from part3-4team-Taskify/feature/Gnb
[Feat] Modal: DeleteModal 컴포넌트 추가 / toast: 커스텀 토스트 추가 / 배포 에러 fix
2 parents bf23624 + 7965f71 commit a071e4b

File tree

10 files changed

+245
-86
lines changed

10 files changed

+245
-86
lines changed

src/components/button/CardButton.tsx

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,7 @@ import { useRouter } from "next/router";
33
import clsx from "clsx";
44
import Image from "next/image";
55

6-
interface CardButtonProps
7-
extends React.ButtonHTMLAttributes<HTMLButtonElement> {
6+
interface CardButtonProps extends React.ButtonHTMLAttributes<HTMLDivElement> {
87
fullWidth?: boolean;
98
title?: string;
109
showCrown?: boolean;
@@ -31,7 +30,7 @@ const CardButton: React.FC<CardButtonProps> = ({
3130
}) => {
3231
const router = useRouter();
3332

34-
const handleCardClick = (e: React.MouseEvent<HTMLButtonElement>) => {
33+
const handleCardClick = (e: React.MouseEvent<HTMLDivElement>) => {
3534
// 관리 상태에서 카드 클릭 이벤트 차단
3635
if (isEditMode) {
3736
e.preventDefault();
@@ -58,7 +57,7 @@ const CardButton: React.FC<CardButtonProps> = ({
5857
};
5958

6059
return (
61-
<button
60+
<div
6261
{...props}
6362
onClick={handleCardClick}
6463
className={clsx(
@@ -84,7 +83,9 @@ const CardButton: React.FC<CardButtonProps> = ({
8483
</svg>
8584

8685
{/* 제목 */}
87-
<span className="font-16sb truncate">{title}</span>
86+
<span className="font-16sb truncate max-w-[90px] sm:max-w-[100px] md:max-w-[120px] lg:max-w-[160px]">
87+
{title}
88+
</span>
8889

8990
{/* 왕관 */}
9091
{showCrown && (
@@ -93,7 +94,7 @@ const CardButton: React.FC<CardButtonProps> = ({
9394
alt="crown Icon"
9495
width={20}
9596
height={20}
96-
className="w-[20px] h-[20px]"
97+
className="hidden sm:block w-[20px] h-[20px]"
9798
/>
9899
)}
99100
</div>
@@ -125,7 +126,7 @@ const CardButton: React.FC<CardButtonProps> = ({
125126
className="ml-2"
126127
/>
127128
)}
128-
</button>
129+
</div>
129130
);
130131
};
131132

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
"use client";
2+
import "react-toastify/dist/ReactToastify.css";
3+
import { ToastContainer, Slide } from "react-toastify";
4+
5+
const CustomToastContainer = () => {
6+
return (
7+
<ToastContainer
8+
position="top-center"
9+
autoClose={2500}
10+
hideProgressBar={true}
11+
closeButton={false}
12+
pauseOnHover={false}
13+
newestOnTop
14+
transition={Slide}
15+
/>
16+
);
17+
};
18+
19+
export default CustomToastContainer;

src/components/gnb/HeaderDashboard.tsx

Lines changed: 39 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -12,8 +12,6 @@ import { MemberList, UserAvatars } from "@/components/gnb/Avatars";
1212
import UserMenu from "@/components/gnb/UserMenu";
1313
import MemberListMenu from "@/components/gnb/MemberListMenu";
1414
import InviteDashboard from "@/components/modal/InviteDashboard";
15-
import { ToastContainer, toast } from "react-toastify";
16-
import "react-toastify/dist/ReactToastify.css";
1715

1816
interface HeaderDashboardProps {
1917
variant?: "mydashboard" | "dashboard" | "edit" | "mypage";
@@ -123,16 +121,20 @@ const HeaderDashboard: React.FC<HeaderDashboardProps> = ({
123121

124122
return (
125123
<header className="w-full h-[60px] md:h-[70px] flex items-center justify-center bg-white border-b-[1px] border-b-[var(--color-gray3)]">
126-
<ToastContainer position="top-center" />
127124
<div className="w-full flex items-center justify-between pl-[4vw]">
128125
{errorMessage && (
129126
<p className="text-sm text-[var(--color-red)] px-4 py-2">
130127
{errorMessage}
131128
</p>
132129
)}
130+
131+
{/*헤더 제목*/}
133132
<div className="flex items-center gap-[8px]">
134133
<p
135-
className={`text-base text-black3 font-bold md:text-xl ${variant !== "mydashboard" ? "hidden md:block" : ""}`}
134+
className={clsx(
135+
"font-20b text-black3 whitespace-nowrap",
136+
variant !== "mydashboard" ? "hidden lg:block" : ""
137+
)}
136138
>
137139
{title}
138140
</p>
@@ -145,7 +147,7 @@ const HeaderDashboard: React.FC<HeaderDashboardProps> = ({
145147
className={
146148
variant === "mydashboard"
147149
? "inline-block"
148-
: "hidden md:inline-block"
150+
: "hidden lg:inline-block"
149151
}
150152
unoptimized
151153
priority
@@ -155,18 +157,17 @@ const HeaderDashboard: React.FC<HeaderDashboardProps> = ({
155157

156158
<div className="flex items-center">
157159
<div
158-
className={`flex gap-[6px] md:gap-[16px] ${variant === "mydashboard" ? "pr-[22px] md:pr-[32px]" : ""}`}
160+
className={clsx(
161+
"flex gap-[6px] md:gap-[16px]",
162+
variant === "mydashboard" ? "pr-[22px] md:pr-[32px]" : ""
163+
)}
159164
>
160165
{/*관리 버튼*/}
161-
{variant !== "edit" && (
166+
{(variant === "mydashboard" || dashboard?.createdByMe) && (
162167
<button
163168
onClick={() => {
164169
if (dashboardId) {
165-
if (dashboard && dashboard.createdByMe === true) {
166-
router.push(`/dashboard/${dashboardId}/edit`);
167-
} else {
168-
toast.error("대시보드 수정 권한이 없습니다.");
169-
}
170+
router.push(`/dashboard/${dashboardId}/edit`);
170171
} else {
171172
if (onToggleEditMode) {
172173
onToggleEditMode();
@@ -194,29 +195,35 @@ const HeaderDashboard: React.FC<HeaderDashboardProps> = ({
194195
)}
195196

196197
{/*초대하기 버튼*/}
197-
{variant !== "mydashboard" && variant !== "edit" && (
198-
<button
199-
onClick={openInviteModal}
200-
className="flex items-center justify-center w-[73px] h-[30px] md:w-[109px] md:h-[36px] lg:w-[116px] lg:h-[40px] rounded-[8px] border border-[var(--color-gray3)] gap-[10px] cursor-pointer"
201-
>
202-
<Image
203-
src="/svgs/add-box.svg"
204-
alt="초대하기 아이콘"
205-
width={20}
206-
height={20}
207-
className="hidden md:block"
208-
/>
209-
<span className="text-sm md:text-base text-gray1">
210-
초대하기
211-
</span>
212-
</button>
213-
)}
198+
{variant !== "mydashboard" &&
199+
variant !== "edit" &&
200+
dashboard?.createdByMe && (
201+
<button
202+
onClick={openInviteModal}
203+
className={clsx(
204+
"flex items-center justify-center",
205+
"w-[73px] h-[30px] md:w-[109px] md:h-[36px] lg:w-[116px] lg:h-[40px]",
206+
"border border-[var(--color-gray3)] rounded-[8px] gap-[10px] cursor-pointer"
207+
)}
208+
>
209+
<Image
210+
src="/svgs/add-box.svg"
211+
alt="초대하기 아이콘"
212+
width={20}
213+
height={20}
214+
className="hidden md:block"
215+
/>
216+
<span className="text-sm md:text-base text-gray1">
217+
초대하기
218+
</span>
219+
</button>
220+
)}
214221
{isModalOpen && <InviteDashboard onClose={closeInviteModal} />}
215222
</div>
216223

217224
{/*멤버 목록*/}
218225
{variant !== "mydashboard" && (
219-
<div className="relative flex items-center justify-center w-[150px] md:w-[190px] h-[60px] md:h-[70px]">
226+
<div className="relative flex items-center justify-center w-[150px] md:w-[190px] h-[60px] md:h-[70px] whitespace-nowrap">
220227
{isLoading ? (
221228
<SkeletonUser />
222229
) : (
@@ -248,7 +255,7 @@ const HeaderDashboard: React.FC<HeaderDashboardProps> = ({
248255
{/*유저 드롭다운 메뉴*/}
249256
<div
250257
onClick={() => setIsMenuOpen((prev) => !prev)}
251-
className="flex items-center gap-[12px] pl-[20px] md:pl-[30px] lg:pl-[35px] cursor-pointer"
258+
className="flex items-center gap-[12px] pl-[20px] md:pl-[30px] lg:pl-[35px] cursor-pointer overflow-hidden"
252259
>
253260
<UserMenu
254261
user={user}
@@ -262,7 +269,7 @@ const HeaderDashboard: React.FC<HeaderDashboardProps> = ({
262269
user && (
263270
<>
264271
<UserAvatars user={user} />
265-
<span className="hidden md:block text-black3 md:text-base md:font-medium">
272+
<span className="hidden md:block text-black3 md:text-base md:font-medium max-w-[90px] truncate whitespace-nowrap">
266273
{user.nickname}
267274
</span>
268275
</>

src/components/gnb/MemberListMenu.tsx

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import React, { useRef } from "react";
2+
import clsx from "clsx";
23
import { useClosePopup } from "@/hooks/useClosePopup";
34
import { UserAvatars } from "@/components/gnb/Avatars";
45
import { MemberType } from "@/types/users";
@@ -21,10 +22,14 @@ const MemberListMenu: React.FC<MemberListMenuProps> = ({
2122
return (
2223
<div
2324
ref={ref}
24-
className={`absolute top-full right-0 w-full z-50
25-
bg-white border border-[var(--color-gray3)] shadow
26-
transition-all duration-200 ease-out
27-
${isListOpen ? "opacity-100 translate-y-0" : "opacity-0 -translate-y-2 pointer-events-none"}`}
25+
className={clsx(
26+
"absolute top-full right-0 w-full z-50",
27+
"bg-white border border-[var(--color-gray3)] shadow",
28+
"transition-all duration-200 ease-out",
29+
isListOpen
30+
? "opacity-100 translate-y-0"
31+
: "opacity-0 -translate-y-2 pointer-events-none"
32+
)}
2833
>
2934
<ul className="flex flex-col font-16r max-h-[300px] overflow-y-auto">
3035
{members.map((member) => (
@@ -33,7 +38,7 @@ const MemberListMenu: React.FC<MemberListMenuProps> = ({
3338
className="px-4 py-2 flex items-center gap-2 hover:bg-[#f9f9f9]"
3439
>
3540
<UserAvatars user={member} />
36-
<span className="text-black3 text-sm md:text-base">
41+
<span className="text-black3 text-sm md:text-base truncate max-w-[60px] md:max-w-[90px]">
3742
{member.nickname}
3843
</span>
3944
</li>

src/components/gnb/UserMenu.tsx

Lines changed: 15 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import React, { useRef } from "react";
22
import { useRouter } from "next/router";
3+
import clsx from "clsx";
34
import { useClosePopup } from "@/hooks/useClosePopup";
45
import { User, LogOut, FolderPen } from "lucide-react";
56
import { UserType } from "@/types/users";
@@ -11,8 +12,12 @@ interface UserMenuProps {
1112
setIsMenuOpen: React.Dispatch<React.SetStateAction<boolean>>;
1213
}
1314

14-
const dropdownButtonStyles =
15-
"flex justify-center md:justify-start items-center w-full px-3 py-3 gap-3 font-16r text-black3 hover:text-[var(--primary)] hover:bg-[#f9f9f9] cursor-pointer";
15+
const dropdownButtonStyles = clsx(
16+
"flex justify-center md:justify-start items-center",
17+
"w-full px-3 py-3 gap-3",
18+
"text-sm lg:text-base text-black3",
19+
"hover:text-[var(--primary)] hover:bg-[#f9f9f9] cursor-pointer"
20+
);
1621

1722
const UserMenu: React.FC<UserMenuProps> = ({ isMenuOpen, setIsMenuOpen }) => {
1823
const { clearUser } = useUserStore();
@@ -31,10 +36,14 @@ const UserMenu: React.FC<UserMenuProps> = ({ isMenuOpen, setIsMenuOpen }) => {
3136
return (
3237
<div
3338
ref={ref}
34-
className={`absolute top-full right-0 w-full
35-
bg-white border border-[var(--color-gray3)] shadow z-50
36-
transition-all duration-200 ease-out
37-
${isMenuOpen ? "opacity-100 translate-y-0" : "opacity-0 -translate-y-2 pointer-events-none"}`}
39+
className={clsx(
40+
"absolute top-full right-0 w-full z-50",
41+
"bg-white border border-[var(--color-gray3)] shadow",
42+
"transition-all duration-200 ease-out",
43+
isMenuOpen
44+
? "opacity-100 translate-y-0"
45+
: "opacity-0 -translate-y-2 pointer-events-none"
46+
)}
3847
>
3948
<button
4049
onClick={() => router.push("/mypage")}

src/components/input/Input.tsx

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ interface GeneralInputProps {
1111
onChange?: (value: string) => void;
1212
value?: string;
1313
readOnly?: boolean; //입력방지 추가
14+
onKeyDown?: (e: React.KeyboardEvent<HTMLInputElement>) => void;
1415
}
1516

1617
interface SignInputProps extends Omit<GeneralInputProps, "type"> {
@@ -109,6 +110,7 @@ export default function Input(props: InputProps) {
109110
);
110111
setIsInvalid(true);
111112
}}
113+
onKeyDown={rest.onKeyDown}
112114
className={clsx(
113115
"peer flex h-[50px] w-full max-w-[520px] px-2 sm:px-4 py-2 rounded-lg transition-colors duration-200",
114116
"border border-[var(--color-gray3)] focus:border-[var(--primary)] focus:ring-0 focus:outline-none",

0 commit comments

Comments
 (0)