From e9d88c9304d19935e60446dafa694fcd0456d484 Mon Sep 17 00:00:00 2001 From: choiseona Date: Sun, 22 Dec 2024 03:11:05 +0900 Subject: [PATCH 01/15] =?UTF-8?q?Feat:=20Pagination=20=EA=B3=B5=ED=86=B5?= =?UTF-8?q?=20=EC=BB=B4=ED=8F=AC=EB=84=8C=ED=8A=B8=20=EC=A0=9C=EC=9E=91=20?= =?UTF-8?q?=EB=B0=8F=20=EC=A0=81=EC=9A=A9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - antd 사용에서 공통 컴포넌트로 변경 - AdminTopicContent의 Pagination에 적용 - AdminInstanceContent의 Pagination에 적용 --- .../AdminInstanceContent.tsx | 27 ++--- .../AdminTopicContent/AdminTopicContent.tsx | 27 ++--- src/components/Common/Pagination/index.tsx | 113 ++++++++++++++++++ 3 files changed, 137 insertions(+), 30 deletions(-) create mode 100644 src/components/Common/Pagination/index.tsx diff --git a/src/components/Admin/AdminInstance/InstanceListComponent/AdminInstanceContent/AdminInstanceContent.tsx b/src/components/Admin/AdminInstance/InstanceListComponent/AdminInstanceContent/AdminInstanceContent.tsx index 77909105..bb719af4 100644 --- a/src/components/Admin/AdminInstance/InstanceListComponent/AdminInstanceContent/AdminInstanceContent.tsx +++ b/src/components/Admin/AdminInstance/InstanceListComponent/AdminInstanceContent/AdminInstanceContent.tsx @@ -3,14 +3,13 @@ import CreateBtn from "@/components/Admin/CreateBtn/CreateBtn"; import { useInstanceListQuery } from "@/hooks/queries/useAdminInstanceQuery"; import { useTopicDetailQuery } from "@/hooks/queries/useAdminTopicQuery"; import { decrypt } from "@/hooks/useCrypto"; -import { Pagination } from "antd"; -import { useEffect, useState } from "react"; +import { useState } from "react"; import { useLocation } from "react-router-dom"; import InstanceListComponent from "../InstanceListComponent"; +import { Pagination } from "@/components/Common/Pagination"; function AdminInstanceContent() { - const [pageNumber, setPageNumber] = useState(0); - const [totalNumber, setTotalNumber] = useState(0); + const [pageNumber, setPageNumber] = useState(1); const location = useLocation(); const topicId = location.state.topicId; @@ -27,10 +26,6 @@ function AdminInstanceContent() { setPageNumber(page); }; - useEffect(() => { - setTotalNumber(instanceContent.totalElements); - }, [instanceContent]); - return ( <> @@ -43,13 +38,15 @@ function AdminInstanceContent() { - + {instanceContent.totalElements > 0 && ( + + )} ); diff --git a/src/components/Admin/AdminTopic/TopicListComponent/AdminTopicContent/AdminTopicContent.tsx b/src/components/Admin/AdminTopic/TopicListComponent/AdminTopicContent/AdminTopicContent.tsx index 32b43de4..4a73b245 100644 --- a/src/components/Admin/AdminTopic/TopicListComponent/AdminTopicContent/AdminTopicContent.tsx +++ b/src/components/Admin/AdminTopic/TopicListComponent/AdminTopicContent/AdminTopicContent.tsx @@ -1,13 +1,12 @@ import { AdminListLayOut } from "@/components/Admin/AdminLayOut/AdminListLayOut/AdminListLayOut"; import CreateBtn from "@/components/Admin/CreateBtn/CreateBtn"; -import { Pagination } from "antd"; import TopicListComponents from "@/components/Admin/AdminTopic/TopicListComponent/TopicListComponent"; -import { useEffect, useState } from "react"; +import { useState } from "react"; import { useTopicListQuery } from "@/hooks/queries/useAdminTopicQuery"; +import { Pagination } from "@/components/Common/Pagination"; function AdminTopicContent() { - const [pageNumber, setPageNumber] = useState(0); - const [totalNumber, setTotalNumber] = useState(0); + const [pageNumber, setPageNumber] = useState(1); const { data: adminData } = useTopicListQuery({ pageNumber: pageNumber - 1, @@ -18,10 +17,6 @@ function AdminTopicContent() { setPageNumber(page); }; - useEffect(() => { - setTotalNumber(adminData.totalElements); - }, [adminData]); - return ( <> @@ -31,13 +26,15 @@ function AdminTopicContent() { - + {adminData.totalElements > 0 && ( + + )} ); diff --git a/src/components/Common/Pagination/index.tsx b/src/components/Common/Pagination/index.tsx new file mode 100644 index 00000000..d1cccd36 --- /dev/null +++ b/src/components/Common/Pagination/index.tsx @@ -0,0 +1,113 @@ +import { useState } from "react"; + +interface PaginationProps { + currentPage: number; + totalPages: number; + limit: number; + onPageChange: (page: number) => void; + className?: string; +} + +export const Pagination = ({ + currentPage, + totalPages, + limit, + onPageChange, + className = "", +}: PaginationProps) => { + const getVisibleStartPage = (targetPage: number, limit: number) => { + return Math.floor((targetPage - 1) / limit) * limit + 1; + }; + + const [visibleStartPage, setVisibleStartPage] = useState( + getVisibleStartPage(currentPage + 1, limit) + ); + const visibleEndPage = Math.min(visibleStartPage + limit - 1, totalPages); + + const pageNumbers = Array.from( + { length: visibleEndPage - visibleStartPage + 1 }, + (_, i) => visibleStartPage + i + ); + + const onClickPrevPage = () => { + if (currentPage > 1) { + const newPage = currentPage - 1; + onPageChange(newPage); + setVisibleStartPage(getVisibleStartPage(newPage, limit)); + } + }; + + const onClickNextPage = () => { + if (currentPage < totalPages) { + const newPage = currentPage + 1; + onPageChange(newPage); + setVisibleStartPage(getVisibleStartPage(newPage, limit)); + } + }; + + const onClickPage = (page: number) => { + onPageChange(page); + setVisibleStartPage(getVisibleStartPage(page, limit)); + }; + + const PageTurner = () => { + return ( + + + + ); + }; + + const pageButtonClass = `rounded-md disabled:bg-white disabled:border-gray-300 disabled:text-gray-300 text-_coral-70 border-_coral-70 border w-10 flex justify-center items-center hover:bg-_coral-70 hover:text-white`; + + return ( +
+
+ + +
+ {pageNumbers.map((page) => ( + + ))} +
+ + +
+
+ ); +}; From b04784b3731196409043ed1b5a889668792c46be Mon Sep 17 00:00:00 2001 From: choiseona Date: Mon, 23 Dec 2024 15:25:04 +0900 Subject: [PATCH 02/15] =?UTF-8?q?Feat:=20InstanceCreate=EC=9D=98=20Button,?= =?UTF-8?q?=20DatePicker,=20Form,=20Image,=20Input,=20Select,=20Upload,=20?= =?UTF-8?q?UploadProps=EC=9D=84=20antd=20=EC=97=86=EC=9D=B4=20=EA=B5=AC?= =?UTF-8?q?=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- package.json | 4 + src/components/Common/Form/index.tsx | 151 ++++++ src/components/Common/Pagination/index.tsx | 4 +- src/index.css | 32 ++ .../InstanceCreate/InstanceCreate.tsx | 444 ++++++++---------- src/utils/makeFileToBase64Image.ts | 24 + yarn.lock | 78 +++ 7 files changed, 488 insertions(+), 249 deletions(-) create mode 100644 src/components/Common/Form/index.tsx create mode 100644 src/utils/makeFileToBase64Image.ts diff --git a/package.json b/package.json index 577fdf64..9a41a71c 100644 --- a/package.json +++ b/package.json @@ -26,6 +26,7 @@ "antd": "^5.12.5", "axios": "^1.6.3", "crypto-js": "^4.2.0", + "date-fns": "^4.1.0", "formik": "^2.4.5", "modal": "^1.2.0", "moment": "^2.30.1", @@ -33,9 +34,11 @@ "qs": "^6.11.2", "react": "^18.2.0", "react-custom-scrollbars": "^4.2.1", + "react-datepicker": "^7.5.0", "react-dom": "^18.2.0", "react-error-boundary": "^4.0.13", "react-facebook-login": "^4.1.1", + "react-hook-form": "^7.54.2", "react-intersection-observer": "^9.8.1", "react-modal": "^3.16.1", "react-query": "^3.39.3", @@ -60,6 +63,7 @@ "@types/crypto-js": "^4.2.2", "@types/jest": "^29.5.11", "@types/react": "^18.2.43", + "@types/react-datepicker": "^7.0.0", "@types/react-dom": "^18.2.17", "@types/react-query": "^1.2.9", "@typescript-eslint/eslint-plugin": "^6.14.0", diff --git a/src/components/Common/Form/index.tsx b/src/components/Common/Form/index.tsx new file mode 100644 index 00000000..597e5792 --- /dev/null +++ b/src/components/Common/Form/index.tsx @@ -0,0 +1,151 @@ +import { + InputHTMLAttributes, + ReactNode, + SelectHTMLAttributes, + TextareaHTMLAttributes, +} from "react"; +import { FieldError, Merge, UseFormRegisterReturn } from "react-hook-form"; + +interface InputProps extends InputHTMLAttributes { + id: string; + label: string; + error?: FieldError; + registration?: UseFormRegisterReturn; // optional로 변경 + children?: ReactNode; +} + +export const Input = ({ + id, + label, + error, + registration, + children, + ...props +}: InputProps) => { + // registration이 있을 때만 destructuring + const { ref, ...restRegistration } = registration || {}; + + return ( +
+
+ + {children ? ( + children + ) : ( + + )} +
+ {error && ( +

{error.message}

+ )} +
+ ); +}; + +interface TextAreaProps extends TextareaHTMLAttributes { + id: string; + label: string; + error?: FieldError; + registration: UseFormRegisterReturn; +} + +export const TextArea = ({ + id, + label, + error, + registration, + ...props +}: TextAreaProps) => { + const { ref, ...restRegistration } = registration; + + return ( +
+
+ +