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.hDong.name}
+
+
+
총 거리
+
{(data.pathLength / 3000).toFixed(2)}km
+
+
+
+
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;
+ }
+}