diff --git a/client/package-lock.json b/client/package-lock.json index 2359c47..29dad64 100644 --- a/client/package-lock.json +++ b/client/package-lock.json @@ -25,6 +25,7 @@ "react-infinite-scroll-component": "^6.1.0", "react-router-dom": "^6.4.3", "react-scripts": "5.0.1", + "react-slick": "^0.29.0", "recoil": "^0.7.6", "styled-components": "^5.3.6", "web-vitals": "^2.1.4" @@ -41,6 +42,7 @@ "@storybook/preset-create-react-app": "^4.1.2", "@storybook/react": "^6.5.13", "@storybook/testing-library": "^0.0.13", + "@types/react-slick": "^0.23.10", "@types/styled-components": "^5.1.26", "@typescript-eslint/eslint-plugin": "^5.42.1", "babel-plugin-named-exports-order": "^0.0.2", @@ -11338,6 +11340,15 @@ "@types/react": "*" } }, + "node_modules/@types/react-slick": { + "version": "0.23.10", + "resolved": "https://registry.npmjs.org/@types/react-slick/-/react-slick-0.23.10.tgz", + "integrity": "sha512-ZiqdencANDZy6sWOWJ54LDvebuXFEhDlHtXU9FFipQR2BcYU2QJxZhvJPW6YK7cocibUiNn+YvDTbt1HtCIBVA==", + "dev": true, + "dependencies": { + "@types/react": "*" + } + }, "node_modules/@types/recoil": { "version": "0.0.9", "resolved": "https://registry.npmjs.org/@types/recoil/-/recoil-0.0.9.tgz", @@ -14118,6 +14129,11 @@ "node": ">=0.10.0" } }, + "node_modules/classnames": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/classnames/-/classnames-2.3.2.tgz", + "integrity": "sha512-CSbhY4cFEJRe6/GQzIk5qXZ4Jeg5pcsP7b5peFSDpffpe1cqjASH/n9UTjBwOp6XpMSTwQ8Za2K5V02ueA7Tmw==" + }, "node_modules/clean-css": { "version": "5.3.1", "resolved": "https://registry.npmjs.org/clean-css/-/clean-css-5.3.1.tgz", @@ -16288,6 +16304,11 @@ "node": ">=10.13.0" } }, + "node_modules/enquire.js": { + "version": "2.1.6", + "resolved": "https://registry.npmjs.org/enquire.js/-/enquire.js-2.1.6.tgz", + "integrity": "sha512-/KujNpO+PT63F7Hlpu4h3pE3TokKRHN26JYmQpPyjkRD/N57R7bPDNojMXdi7uveAKjYB7yQnartCxZnFWr0Xw==" + }, "node_modules/entities": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/entities/-/entities-2.2.0.tgz", @@ -22435,6 +22456,14 @@ "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", "integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==" }, + "node_modules/json2mq": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/json2mq/-/json2mq-0.2.0.tgz", + "integrity": "sha512-SzoRg7ux5DWTII9J2qkrZrqV1gt+rTaoufMxEzXbS26Uid0NwaJd123HcoB80TgubEppxxIGdNxCx50fEoEWQA==", + "dependencies": { + "string-convert": "^0.2.0" + } + }, "node_modules/json5": { "version": "2.2.1", "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.1.tgz", @@ -26780,6 +26809,22 @@ } } }, + "node_modules/react-slick": { + "version": "0.29.0", + "resolved": "https://registry.npmjs.org/react-slick/-/react-slick-0.29.0.tgz", + "integrity": "sha512-TGdOKE+ZkJHHeC4aaoH85m8RnFyWqdqRfAGkhd6dirmATXMZWAxOpTLmw2Ll/jPTQ3eEG7ercFr/sbzdeYCJXA==", + "dependencies": { + "classnames": "^2.2.5", + "enquire.js": "^2.1.6", + "json2mq": "^0.2.0", + "lodash.debounce": "^4.0.8", + "resize-observer-polyfill": "^1.5.0" + }, + "peerDependencies": { + "react": "^0.14.0 || ^15.0.1 || ^16.0.0 || ^17.0.0 || ^18.0.0", + "react-dom": "^0.14.0 || ^15.0.1 || ^16.0.0 || ^17.0.0 || ^18.0.0" + } + }, "node_modules/read-cache": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/read-cache/-/read-cache-1.0.0.tgz", @@ -27341,6 +27386,11 @@ "resolved": "https://registry.npmjs.org/requires-port/-/requires-port-1.0.0.tgz", "integrity": "sha512-KigOCHcocU3XODJxsu8i/j8T9tzT4adHiecwORRQ0ZZFcp7ahwXuRU1m+yuO90C5ZUyGeGfocHDI14M3L3yDAQ==" }, + "node_modules/resize-observer-polyfill": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/resize-observer-polyfill/-/resize-observer-polyfill-1.5.1.tgz", + "integrity": "sha512-LwZrotdHOo12nQuZlHEmtuXdqGoOD0OhaxopaNFxWzInpEgaLWoVuAMbTzixuosCx2nEG58ngzW3vxdWoxIgdg==" + }, "node_modules/resolve": { "version": "1.22.1", "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.1.tgz", @@ -29050,6 +29100,11 @@ "safe-buffer": "~5.2.0" } }, + "node_modules/string-convert": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/string-convert/-/string-convert-0.2.1.tgz", + "integrity": "sha512-u/1tdPl4yQnPBjnVrmdLo9gtuLvELKsAoRapekWggdiQNvvvum+jYF329d84NAa660KQw7pB2n36KrIKVoXa3A==" + }, "node_modules/string-length": { "version": "4.0.2", "resolved": "https://registry.npmjs.org/string-length/-/string-length-4.0.2.tgz", @@ -40584,6 +40639,15 @@ "@types/react": "*" } }, + "@types/react-slick": { + "version": "0.23.10", + "resolved": "https://registry.npmjs.org/@types/react-slick/-/react-slick-0.23.10.tgz", + "integrity": "sha512-ZiqdencANDZy6sWOWJ54LDvebuXFEhDlHtXU9FFipQR2BcYU2QJxZhvJPW6YK7cocibUiNn+YvDTbt1HtCIBVA==", + "dev": true, + "requires": { + "@types/react": "*" + } + }, "@types/recoil": { "version": "0.0.9", "resolved": "https://registry.npmjs.org/@types/recoil/-/recoil-0.0.9.tgz", @@ -42775,6 +42839,11 @@ } } }, + "classnames": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/classnames/-/classnames-2.3.2.tgz", + "integrity": "sha512-CSbhY4cFEJRe6/GQzIk5qXZ4Jeg5pcsP7b5peFSDpffpe1cqjASH/n9UTjBwOp6XpMSTwQ8Za2K5V02ueA7Tmw==" + }, "clean-css": { "version": "5.3.1", "resolved": "https://registry.npmjs.org/clean-css/-/clean-css-5.3.1.tgz", @@ -44454,6 +44523,11 @@ "tapable": "^2.2.0" } }, + "enquire.js": { + "version": "2.1.6", + "resolved": "https://registry.npmjs.org/enquire.js/-/enquire.js-2.1.6.tgz", + "integrity": "sha512-/KujNpO+PT63F7Hlpu4h3pE3TokKRHN26JYmQpPyjkRD/N57R7bPDNojMXdi7uveAKjYB7yQnartCxZnFWr0Xw==" + }, "entities": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/entities/-/entities-2.2.0.tgz", @@ -48987,6 +49061,14 @@ "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", "integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==" }, + "json2mq": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/json2mq/-/json2mq-0.2.0.tgz", + "integrity": "sha512-SzoRg7ux5DWTII9J2qkrZrqV1gt+rTaoufMxEzXbS26Uid0NwaJd123HcoB80TgubEppxxIGdNxCx50fEoEWQA==", + "requires": { + "string-convert": "^0.2.0" + } + }, "json5": { "version": "2.2.1", "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.1.tgz", @@ -52101,6 +52183,18 @@ "workbox-webpack-plugin": "^6.4.1" } }, + "react-slick": { + "version": "0.29.0", + "resolved": "https://registry.npmjs.org/react-slick/-/react-slick-0.29.0.tgz", + "integrity": "sha512-TGdOKE+ZkJHHeC4aaoH85m8RnFyWqdqRfAGkhd6dirmATXMZWAxOpTLmw2Ll/jPTQ3eEG7ercFr/sbzdeYCJXA==", + "requires": { + "classnames": "^2.2.5", + "enquire.js": "^2.1.6", + "json2mq": "^0.2.0", + "lodash.debounce": "^4.0.8", + "resize-observer-polyfill": "^1.5.0" + } + }, "read-cache": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/read-cache/-/read-cache-1.0.0.tgz", @@ -52526,6 +52620,11 @@ "resolved": "https://registry.npmjs.org/requires-port/-/requires-port-1.0.0.tgz", "integrity": "sha512-KigOCHcocU3XODJxsu8i/j8T9tzT4adHiecwORRQ0ZZFcp7ahwXuRU1m+yuO90C5ZUyGeGfocHDI14M3L3yDAQ==" }, + "resize-observer-polyfill": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/resize-observer-polyfill/-/resize-observer-polyfill-1.5.1.tgz", + "integrity": "sha512-LwZrotdHOo12nQuZlHEmtuXdqGoOD0OhaxopaNFxWzInpEgaLWoVuAMbTzixuosCx2nEG58ngzW3vxdWoxIgdg==" + }, "resolve": { "version": "1.22.1", "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.1.tgz", @@ -53892,6 +53991,11 @@ "safe-buffer": "~5.2.0" } }, + "string-convert": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/string-convert/-/string-convert-0.2.1.tgz", + "integrity": "sha512-u/1tdPl4yQnPBjnVrmdLo9gtuLvELKsAoRapekWggdiQNvvvum+jYF329d84NAa660KQw7pB2n36KrIKVoXa3A==" + }, "string-length": { "version": "4.0.2", "resolved": "https://registry.npmjs.org/string-length/-/string-length-4.0.2.tgz", diff --git a/client/package.json b/client/package.json index a3be3be..13510a7 100644 --- a/client/package.json +++ b/client/package.json @@ -20,6 +20,7 @@ "react-infinite-scroll-component": "^6.1.0", "react-router-dom": "^6.4.3", "react-scripts": "5.0.1", + "react-slick": "^0.29.0", "recoil": "^0.7.6", "styled-components": "^5.3.6", "web-vitals": "^2.1.4" @@ -72,6 +73,7 @@ "@storybook/preset-create-react-app": "^4.1.2", "@storybook/react": "^6.5.13", "@storybook/testing-library": "^0.0.13", + "@types/react-slick": "^0.23.10", "@types/styled-components": "^5.1.26", "@typescript-eslint/eslint-plugin": "^5.42.1", "babel-plugin-named-exports-order": "^0.0.2", diff --git a/client/public/index.html b/client/public/index.html index 42bea02..28bd3e5 100644 --- a/client/public/index.html +++ b/client/public/index.html @@ -9,6 +9,10 @@ + + React App diff --git a/client/src/App.tsx b/client/src/App.tsx index 4eab60f..b0092c9 100644 --- a/client/src/App.tsx +++ b/client/src/App.tsx @@ -1,7 +1,8 @@ import React from "react"; import SignUp from "#pages/SignUp/SignUp"; import Login from "#pages/Login/Login"; -import MainPage from "#pages/MainPage/MainPage"; +import MenuPage from "#pages/Menu/MenuPage"; +import MainPage from "#pages/Main/MainPage"; import Courses from "#pages/Courses/Courses"; import { Route, Routes } from "react-router-dom"; import NewCourse from "#pages/NewCourse/NewCourse"; @@ -18,6 +19,7 @@ function App() { return ( } /> + } /> } /> } /> } /> diff --git a/client/src/components/Card/RecruitTextCard/RecruitTextCard.tsx b/client/src/components/Card/RecruitTextCard/RecruitTextCard.tsx new file mode 100644 index 0000000..d8be0d8 --- /dev/null +++ b/client/src/components/Card/RecruitTextCard/RecruitTextCard.tsx @@ -0,0 +1,32 @@ +import DetailLabel from "#components/DetailLabel/DetailLabel"; +import { Recruit } from "#types/Recruit"; +import { getDisplayPaceString } from "#utils/stringUtils"; +import { useNavigate } from "react-router-dom"; +import styled from "styled-components"; + +const Card = styled.div` + padding: 12px 16px; + box-shadow: 0px 2px 4px rgba(0, 0, 0, 0.25); + border-radius: 8px; + div:not(:last-child) { + margin-bottom: 30px; + } +`; + +interface RecruitTextCardProps { + data: Recruit; +} + +const RecruitTextCard = ({ data }: RecruitTextCardProps) => { + const navigate = useNavigate(); + return ( + navigate(`/recruit/${data.id}`)}> + + + + + + + ); +}; +export default RecruitTextCard; diff --git a/client/src/components/Header/Header.tsx b/client/src/components/Header/Header.tsx index d6b9f9f..14c7140 100644 --- a/client/src/components/Header/Header.tsx +++ b/client/src/components/Header/Header.tsx @@ -16,7 +16,6 @@ const HeaderWrapper = styled.div` img { width: 24px; height: 24px; - cursor: pointer; } div { width: 24px; @@ -26,14 +25,16 @@ const HeaderWrapper = styled.div` interface HeaderProps { text: string; + isMain?: boolean; } -const Header = ({ text }: HeaderProps) => { +const Header = ({ text, isMain = false }: HeaderProps) => { const navigate = useNavigate(); const userInfo = useRecoilValue(userState); return ( - navigate(-1)} /> + {isMain ?
: navigate(-1)} />} +

{text}

navigate(userInfo.accessToken ? "/mypage" : "/login")} /> diff --git a/client/src/hooks/queries/useCoursesQuery.ts b/client/src/hooks/queries/useCoursesQuery.ts new file mode 100644 index 0000000..ebe497a --- /dev/null +++ b/client/src/hooks/queries/useCoursesQuery.ts @@ -0,0 +1,11 @@ +import useHttpGet from "#hooks/http/useHttpGet"; +import { Course } from "#types/Course"; +import HttpResponse from "#types/dto/HttpResponse"; +import { useQuery } from "@tanstack/react-query"; + +const useCoursesQuery = () => { + const { get } = useHttpGet>(); + return useQuery>(["course"], async () => get(`/course`).then((res) => res || {})); +}; + +export default useCoursesQuery; diff --git a/client/src/hooks/queries/useRecruitDetailQuery.ts b/client/src/hooks/queries/useRecruitDetailQuery.ts index cad4e72..8d15783 100644 --- a/client/src/hooks/queries/useRecruitDetailQuery.ts +++ b/client/src/hooks/queries/useRecruitDetailQuery.ts @@ -1,12 +1,10 @@ import useHttpGet from "#hooks/http/useHttpGet"; import HttpResponse from "#types/dto/HttpResponse"; -import { Recruit } from "#types/Recruit"; +import { RecruitDetail } from "#types/RecruitDetail"; import { useQuery } from "@tanstack/react-query"; const useRecruitDetailQuery = (id: number) => { - const { get } = useHttpGet>(); - return useQuery(["recruit", id], async () => get(`/recruit/${id}`).then((res) => res.data), { - refetchInterval: 2000, - }); + const { get } = useHttpGet>(); + return useQuery(["recruit", id], async () => get(`/recruit/${id}`).then((res) => res.data)); }; export default useRecruitDetailQuery; diff --git a/client/src/hooks/queries/useRecruitsQuery.ts b/client/src/hooks/queries/useRecruitsQuery.ts new file mode 100644 index 0000000..11932e1 --- /dev/null +++ b/client/src/hooks/queries/useRecruitsQuery.ts @@ -0,0 +1,11 @@ +import useHttpGet from "#hooks/http/useHttpGet"; +import HttpResponse from "#types/dto/HttpResponse"; +import { Recruit } from "#types/Recruit"; +import { useQuery } from "@tanstack/react-query"; + +const useRecruitsQuery = () => { + const { get } = useHttpGet>(); + return useQuery>(["recruit"], async () => get(`/recruit`).then((res) => res || {})); +}; + +export default useRecruitsQuery; diff --git a/client/src/pages/CourseDetail/CourseDetail.tsx b/client/src/pages/CourseDetail/CourseDetail.tsx index 1c93f99..46c2718 100644 --- a/client/src/pages/CourseDetail/CourseDetail.tsx +++ b/client/src/pages/CourseDetail/CourseDetail.tsx @@ -78,31 +78,30 @@ const CourseDetail = () => { }; if (isLoading) return
Loading...
; + if (!data) return
404
; return ( <>
{renderMap()} {data?.title} - {data && ( - -
- 출발점 -

{data.hDong.name}

-
-
- 총 거리 -

{(data.pathLength / 3000).toFixed(2)}km

-
-
- 게시자 -

{data.userId}

-
- -
- )} + +
+ 출발점 +

{data.hDong.name}

+
+
+ 총 거리 +

{(data.pathLength / 3000).toFixed(2)}km

+
+
+ 게시자 +

{data.userId}

+
+ +
diff --git a/client/src/pages/Main/MainPage.styles.ts b/client/src/pages/Main/MainPage.styles.ts new file mode 100644 index 0000000..b6ab782 --- /dev/null +++ b/client/src/pages/Main/MainPage.styles.ts @@ -0,0 +1,39 @@ +import styled from "styled-components"; +import { COLOR } from "styles/color"; +import { flexRowSpaceBetween } from "styles/flex"; +import { fontXLarge } from "styles/font"; + +export const MainPageContainer = styled.div` + padding: 15px 0px; + > div:nth-child(1) { + margin-bottom: 40px; + } +`; + +export const CarouselWrapper = styled.div` + .slick-slide { + padding: 5px; + } + .slick-dots { + margin-top: 30px; + .slick-active { + button { + ::before { + color: ${COLOR.ORANGE} !important; + } + } + } + li { + margin: 0 1px; + } + } +`; + +export const TitleWrapper = styled.div` + ${flexRowSpaceBetween}; + padding: 0px 10px; +`; + +export const ListTitle = styled.span` + ${fontXLarge(COLOR.BLACK, 500)}; +`; diff --git a/client/src/pages/Main/MainPage.tsx b/client/src/pages/Main/MainPage.tsx new file mode 100644 index 0000000..4456b71 --- /dev/null +++ b/client/src/pages/Main/MainPage.tsx @@ -0,0 +1,61 @@ +import Header from "#components/Header/Header"; +import MoreButton from "#components/MoreButton/MoreButton"; +import Slider, { Settings } from "react-slick"; +import CourseCard from "#components/Card/CourseCard/CourseCard"; +import { CarouselWrapper, ListTitle, MainPageContainer, TitleWrapper } from "./MainPage.styles"; +import RecruitTextCard from "#components/Card/RecruitTextCard/RecruitTextCard"; +import useCoursesQuery from "#hooks/queries/useCoursesQuery"; +import useRecruitsQuery from "#hooks/queries/useRecruitsQuery"; +import { Course } from "#types/Course"; +import { Recruit } from "#types/Recruit"; + +const settings: Settings = { + centerMode: true, + infinite: true, + slidesToShow: 1, + dots: true, + slidesToScroll: 1, +}; + +const MainPage = () => { + const { data: course, isLoading: coursesLoading } = useCoursesQuery(); + const { data: recruit, isLoading: recruitsLoading } = useRecruitsQuery(); + if (coursesLoading || recruitsLoading) return
Loading...
; + if (!course || !recruit) return
404
; + + return ( + <> +
+ +
+ + 코스 목록 + + + + + {course.data.map((c: Course, idx: number) => ( + + ))} + + +
+
+ + 모집 목록 + + + + + {recruit.data.map((r: Recruit, idx: number) => ( + + ))} + + +
+
+ + ); +}; + +export default MainPage; diff --git a/client/src/pages/MainPage/MainPage.tsx b/client/src/pages/MainPage/MainPage.tsx index 3a34aa6..4bd045c 100644 --- a/client/src/pages/MainPage/MainPage.tsx +++ b/client/src/pages/MainPage/MainPage.tsx @@ -27,6 +27,9 @@ const MainPage = () => { const handleRecruitsClick = () => { navigate("/recruits"); }; + const handleMainPage = () => { + navigate("/main"); + }; const handleCourseMockClick = () => { navigate("/mock/courses"); }; @@ -43,6 +46,7 @@ const MainPage = () => { + diff --git a/client/src/pages/Menu/MenuPage.tsx b/client/src/pages/Menu/MenuPage.tsx new file mode 100644 index 0000000..6033de2 --- /dev/null +++ b/client/src/pages/Menu/MenuPage.tsx @@ -0,0 +1,54 @@ +import React from "react"; +import { useNavigate } from "react-router-dom"; + +/* + 임시 메인페이지입니다 (라우팅 / 내비게이션 테스팅용) + */ +const MainPage = () => { + const navigate = useNavigate(); + const handleLoginClick = () => { + navigate("/login"); + }; + const handleSignUpClick = () => { + navigate("/signup"); + }; + const handleCoursesClick = () => { + navigate("/courses"); + }; + const handleCourseNewClick = () => { + navigate("/course/new"); + }; + const handleRecruitDetailClick = () => { + navigate("/recruit/1"); + }; + const handleCourseDetailClick = () => { + navigate("/course/1"); + }; + const handleRecruitsClick = () => { + navigate("/recruits"); + }; + + const handleMainPage = () => { + navigate("/main"); + }; + + const handleCourseMockClick = () => { + navigate("/mock/course"); + }; + + return ( + <> + + + + + + + + + + + ); +}; + +export default MainPage; diff --git a/client/src/pages/RecruitDetail/RecruitDetail.tsx b/client/src/pages/RecruitDetail/RecruitDetail.tsx index ebff2ea..da54926 100644 --- a/client/src/pages/RecruitDetail/RecruitDetail.tsx +++ b/client/src/pages/RecruitDetail/RecruitDetail.tsx @@ -15,12 +15,14 @@ const RecruitDetail = () => { const { data, isLoading } = useRecruitDetailQuery(Number(id)); const { post } = useHttpPost(); - post("/recruit/join", { recruitId: String(id) }); + + if (isLoading) return
Loading...
; + if (!data) return
404
; const renderMap = useCallback( useShowMap({ height: `${window.innerHeight - 307}px`, - center: getMiddlePoint(typeof data?.course.path === "string" ? JSON.parse(data?.course.path || `[]`) : []), - runningPath: typeof data?.course.path === "string" ? JSON.parse(data?.course.path || `[]`) : [], + center: getMiddlePoint(data.path), + runningPath: data.path, level: 5, }).renderMap, [data], @@ -32,9 +34,6 @@ const RecruitDetail = () => { } catch {} }, []); - if (isLoading) return
Loading...
; - if (!data) return
404
; - return ( <>
@@ -43,11 +42,11 @@ const RecruitDetail = () => {
출발점 -

{data.course.hDong.name}

+

{data.hDong.name}

총거리 -

{data.course.pathLength}km

+

{data.pathLength}km

페이스 diff --git a/client/src/styles/color.ts b/client/src/styles/color.ts index 9b885ae..7f7ebf5 100644 --- a/client/src/styles/color.ts +++ b/client/src/styles/color.ts @@ -5,4 +5,5 @@ export enum COLOR { DARK_GRAY = "#757D87", LIGHT_GRAY = "#808080", BABY_BLUE = "#ACB7C7", + ORANGE = "#FFB800", } diff --git a/client/src/types/RecruitDetail.ts b/client/src/types/RecruitDetail.ts new file mode 100644 index 0000000..932412c --- /dev/null +++ b/client/src/types/RecruitDetail.ts @@ -0,0 +1,16 @@ +import { hDong } from "./hDong"; +import { LatLng } from "./LatLng"; + +export interface RecruitDetail { + title: string; + startTime: string; + maxPpl: number; + currentPpl: number; + path: LatLng[]; + pathLength: number; + pace: number; + hDong: hDong; + userId: string; + isParticipating: boolean; + isAuthor: boolean; +} diff --git a/client/src/types/dto/GetCoursesRes.ts b/client/src/types/dto/GetCoursesRes.ts new file mode 100644 index 0000000..530e8b9 --- /dev/null +++ b/client/src/types/dto/GetCoursesRes.ts @@ -0,0 +1,8 @@ +import { Course } from "#types/Course"; + +interface GetCoursesRes { + statusCode: number; + data: Course[]; +} + +export default GetCoursesRes; diff --git a/server/src/recruit/dto/request/create-recruit.request.ts b/server/src/recruit/dto/request/create-recruit.request.ts new file mode 100644 index 0000000..469ea81 --- /dev/null +++ b/server/src/recruit/dto/request/create-recruit.request.ts @@ -0,0 +1,31 @@ +import { Type } from "class-transformer"; +import { IsNumber, IsString } from "class-validator"; +import { Recruit } from "../../../common/entities/recruit.entity"; + +export class CreateRecruitReqDto { + @IsString() + private title: string; + + @Type(() => Date) + private startTime: Date; + + @Type(() => Number) + @IsNumber() + private maxPpl: number; + + @Type(() => Number) + @IsNumber() + private pace: number; + + @Type(() => Number) + @IsNumber() + private userId: number; + + @Type(() => Number) + @IsNumber() + private courseId: number; + + toEntity(): Recruit { + return Recruit.of(this.title, this.startTime, this.maxPpl, this.pace, this.userId, this.courseId); + } +} diff --git a/server/src/recruit/dto/request/get-recruit.request.ts b/server/src/recruit/dto/request/get-recruit.request.ts new file mode 100644 index 0000000..9278a06 --- /dev/null +++ b/server/src/recruit/dto/request/get-recruit.request.ts @@ -0,0 +1,110 @@ +import { Transform, Type } from "class-transformer"; +import { IsBoolean, IsNumber, IsString, IsOptional } from "class-validator"; +export class GetRecruitDto { + @IsOptional() + @IsString() + private query?: string | undefined; + + @IsOptional() + @Type(() => Number) + @IsNumber() + private hour?: number; + + @IsOptional() + @Type(() => Number) + @IsNumber() + private dist?: number; + + @IsOptional() + @Type(() => Number) + @IsNumber() + private maxLen?: number; + + @IsOptional() + @Type(() => Number) + @IsNumber() + private minLen?: number; + + @IsOptional() + @IsBoolean() + @Transform(({ value }) => { + if (value === "true") return true; + if (value === "false") return false; + return value; + }) + private title?: boolean; + + @IsOptional() + @IsBoolean() + @Transform(({ value }) => { + if (value === "true") return true; + if (value === "false") return false; + return value; + }) + private author?: boolean; + + @IsOptional() + @IsBoolean() + @Transform(({ value }) => { + if (value === "true") return true; + if (value === "false") return false; + return value; + }) + private avail?: boolean; + + @IsOptional() + @Type(() => Number) + @IsNumber() + private page?: number; + + @IsOptional() + @Type(() => Number) + @IsNumber() + private pageSize?: number; + + getQuery(): string | undefined { + return this.query; + } + + getHour(): number | undefined { + return this.hour; + } + + getDist(): number | undefined { + return this.dist; + } + + getMaxLength(): number | undefined { + return this.maxLen; + } + + getMinLength(): number | undefined { + return this.minLen; + } + + getTitle(): boolean { + if (this.title === false) { + return false; + } + return true; + } + + getAuthor(): boolean { + if (this.author === false) { + return false; + } + return true; + } + + getAvail(): boolean { + return this.avail; + } + + getPage(): number { + return this.page || 1; + } + + getPageSize(): number { + return this.pageSize || 10; + } +}