Skip to content

Commit 54965da

Browse files
authored
Merge pull request #80 from part3-4team-Taskify/api
[Feat] 나의 대쉬보드 페이지 작성
2 parents ab6c8c8 + 3d43bdf commit 54965da

File tree

2 files changed

+115
-10
lines changed

2 files changed

+115
-10
lines changed

src/components/input/Input.tsx

Lines changed: 17 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -14,13 +14,12 @@ interface GeneralInputProps {
1414
interface SignInputProps extends Omit<GeneralInputProps, "type"> {
1515
type: Extract<HTMLInputTypeAttribute, "text" | "email" | "password">;
1616
name: "email" | "nickname" | "password" | "passwordCheck";
17-
pattern: string;
18-
invalidMessage: string;
17+
pattern?: string;
18+
invalidMessage?: string;
1919
labelClassName?: string;
2020
wrapperClassName?: string;
2121
}
2222

23-
//주석석
2423
type InputProps = GeneralInputProps | SignInputProps;
2524

2625
export default function Input(props: InputProps) {
@@ -40,15 +39,24 @@ export default function Input(props: InputProps) {
4039

4140
const id = useId();
4241
const inputRef = useRef<HTMLInputElement>(null);
43-
const [htmlType, setHtmlType] = useState(type);
42+
const [htmlType, setHtmlType] = useState<HTMLInputTypeAttribute>(type);
4443
const [isInvalid, setIsInvalid] = useState(false);
4544

4645
const handleChange = (event: React.ChangeEvent<HTMLInputElement>) => {
46+
const value = event.target.value;
47+
4748
if (onChange) {
48-
onChange(event.target.value);
49+
onChange(value);
4950
}
51+
5052
event.target.setCustomValidity("");
51-
setIsInvalid(false);
53+
54+
if (pattern) {
55+
const regex = new RegExp(pattern);
56+
setIsInvalid(!regex.test(value));
57+
} else {
58+
setIsInvalid(false);
59+
}
5260
};
5361

5462
const handleBlur = (event: React.FocusEvent<HTMLInputElement>) => {
@@ -65,9 +73,7 @@ export default function Input(props: InputProps) {
6573
};
6674

6775
const togglePasswordTypeOnClick = () => {
68-
setHtmlType((prev: HTMLInputTypeAttribute) =>
69-
prev === "password" ? "text" : "password"
70-
);
76+
setHtmlType((prev) => (prev === "password" ? "text" : "password"));
7177
};
7278

7379
return (
@@ -132,8 +138,9 @@ export default function Input(props: InputProps) {
132138
</button>
133139
)}
134140
</div>
141+
135142
{isInvalid && invalidMessage && (
136-
<span className="mt-2 font-14r block text-[var(--color-red)]">
143+
<span className="font-14r block text-[var(--color-red)] mt-1">
137144
{invalidMessage}
138145
</span>
139146
)}

src/pages/mydashboard.tsx

Lines changed: 98 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,98 @@
1+
import React, { useState } from "react";
2+
import SideMenu from "@/components/SideMenu/SideMenu";
3+
import HeaderDashboard from "@/components/Gnb/HeaderDashboard";
4+
import InvitedDashBoard from "@/components/table/invited/InvitedDashBoard";
5+
import type { Dashboard } from "@/components/SideMenu/dashboard";
6+
import DashboardAddButton from "@/components/button/DashboardAddButton";
7+
import CardButton from "@/components/button/CardButton";
8+
import { PaginationBtn } from "@/components/button/PaginationBtn";
9+
10+
const dummyDashboards: Dashboard[] = [
11+
{
12+
id: 1,
13+
title: "기획안",
14+
color: "#F87171",
15+
createdAt: "2024-01-01T00:00:00Z",
16+
updatedAt: "2024-01-02T00:00:00Z",
17+
createdByMe: true,
18+
userId: 101,
19+
},
20+
{
21+
id: 2,
22+
title: "디자인 시안",
23+
color: "#60A5FA",
24+
createdAt: "2024-01-03T00:00:00Z",
25+
updatedAt: "2024-01-04T00:00:00Z",
26+
createdByMe: false,
27+
userId: 102,
28+
},
29+
];
30+
31+
export default function MyDashboardPage() {
32+
const [dashCards, setDashCards] = useState<string[]>([]);
33+
const [currentPage, setCurrentPage] = useState(1);
34+
const itemsPerPage = 6;
35+
36+
const handleAddDashboard = () => {
37+
const newTitle = `대시보드 ${dashCards.length}`;
38+
setDashCards((prev) => [...prev, newTitle]);
39+
};
40+
41+
const totalPages = Math.ceil((dashCards.length + 1) / itemsPerPage);
42+
const startIndex = (currentPage - 1) * itemsPerPage;
43+
const currentItems = [
44+
<DashboardAddButton key="add" onClick={handleAddDashboard} />,
45+
...dashCards.map((title, index) => (
46+
<CardButton key={index}>{title}</CardButton>
47+
)),
48+
].slice(startIndex, startIndex + itemsPerPage);
49+
50+
const handlePrevPage = () => {
51+
if (currentPage > 1) setCurrentPage((prev) => prev - 1);
52+
};
53+
54+
const handleNextPage = () => {
55+
if (currentPage < totalPages) setCurrentPage((prev) => prev + 1);
56+
};
57+
58+
return (
59+
<div className="flex h-screen overflow-hidden">
60+
<SideMenu dashboardList={dummyDashboards} />
61+
62+
<div className="flex flex-col flex-1 overflow-hidden">
63+
<HeaderDashboard />
64+
65+
<main className="flex-1 overflow-auto px-[80px] pt-[40px] pb-10 bg-[#f9f9f9] space-y-10">
66+
{/* 대시보드 카드 + 페이지네이션 */}
67+
<section className="flex flex-col items-start space-y-6">
68+
<div className="flex flex-wrap gap-x-[20px] gap-y-[16px] w-full max-w-[1100px]">
69+
{currentItems}
70+
</div>
71+
72+
{totalPages > 1 && (
73+
<div className="justify-end flex items-center w-full max-w-[1035px]">
74+
<span className="text-sm text-gray-500">
75+
{`${currentPage}페이지 중 ${totalPages}`}
76+
</span>
77+
<PaginationBtn
78+
direction="left"
79+
disabled={currentPage === 1}
80+
onClick={handlePrevPage}
81+
/>
82+
<PaginationBtn
83+
direction="right"
84+
disabled={currentPage === totalPages}
85+
onClick={handleNextPage}
86+
/>
87+
</div>
88+
)}
89+
</section>
90+
91+
<div className="mt-[74px] flex justify-start">
92+
<InvitedDashBoard />
93+
</div>
94+
</main>
95+
</div>
96+
</div>
97+
);
98+
}

0 commit comments

Comments
 (0)