diff --git a/package-lock.json b/package-lock.json
index f1c2e1b..cbffc3f 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -19,6 +19,7 @@
"react-router-dom": "^6.4.2",
"react-scripts": "5.0.1",
"styled-components": "^5.3.6",
+ "styled-reset": "^4.4.2",
"web-vitals": "^2.1.4"
},
"devDependencies": {
@@ -15277,6 +15278,21 @@
"node": ">=4"
}
},
+ "node_modules/styled-reset": {
+ "version": "4.4.2",
+ "resolved": "https://registry.npmjs.org/styled-reset/-/styled-reset-4.4.2.tgz",
+ "integrity": "sha512-VzVhEZHpO/CD/F5ZllqTAY+GTaKlNDZt5mTrtPf/kXZSe85+wMkhRIiPARgvCP9/HQMk+ZGaEWk1IkdP2SYAUQ==",
+ "engines": {
+ "node": ">=16.0.0"
+ },
+ "funding": {
+ "type": "ko-fi",
+ "url": "https://ko-fi.com/zacanger"
+ },
+ "peerDependencies": {
+ "styled-components": ">=4.0.0 || >=5.0.0"
+ }
+ },
"node_modules/stylehacks": {
"version": "5.1.1",
"resolved": "https://registry.npmjs.org/stylehacks/-/stylehacks-5.1.1.tgz",
@@ -28076,6 +28092,12 @@
}
}
},
+ "styled-reset": {
+ "version": "4.4.2",
+ "resolved": "https://registry.npmjs.org/styled-reset/-/styled-reset-4.4.2.tgz",
+ "integrity": "sha512-VzVhEZHpO/CD/F5ZllqTAY+GTaKlNDZt5mTrtPf/kXZSe85+wMkhRIiPARgvCP9/HQMk+ZGaEWk1IkdP2SYAUQ==",
+ "requires": {}
+ },
"stylehacks": {
"version": "5.1.1",
"resolved": "https://registry.npmjs.org/stylehacks/-/stylehacks-5.1.1.tgz",
diff --git a/package.json b/package.json
index 513f9cb..718c88f 100644
--- a/package.json
+++ b/package.json
@@ -13,6 +13,7 @@
"react-router-dom": "^6.4.2",
"react-scripts": "5.0.1",
"styled-components": "^5.3.6",
+ "styled-reset": "^4.4.2",
"web-vitals": "^2.1.4"
},
"scripts": {
diff --git a/public/index.html b/public/index.html
index 1684344..51b2a60 100644
--- a/public/index.html
+++ b/public/index.html
@@ -3,6 +3,12 @@
+
+
+
diff --git a/src/App.js b/src/App.js
deleted file mode 100644
index 491dfe5..0000000
--- a/src/App.js
+++ /dev/null
@@ -1,5 +0,0 @@
-function App() {
- return Hello React!
;
-}
-
-export default App;
diff --git a/src/App.jsx b/src/App.jsx
new file mode 100644
index 0000000..4477dd2
--- /dev/null
+++ b/src/App.jsx
@@ -0,0 +1,14 @@
+import Providers from "./context/Providers";
+import AppRouter from "./routes/AppRouter";
+import GlobalStyle from "./styles/globalStyles";
+
+function App() {
+ return (
+
+
+
+
+ );
+}
+
+export default App;
diff --git a/src/api/carAPI.js b/src/api/carAPI.js
new file mode 100644
index 0000000..0356de1
--- /dev/null
+++ b/src/api/carAPI.js
@@ -0,0 +1,70 @@
+import { CAR_API_URL, CAR_FUELTYPE, CAR_SEGMENT } from "./constant";
+import { createInstance } from "./createInstance";
+
+const BASE_URL = "https://preonboarding.platdev.net/api";
+
+class carAPI {
+ #API;
+ #instance;
+ #fuelType;
+ #segment;
+
+ constructor() {
+ this.#instance = createInstance({
+ url: BASE_URL,
+ config: {
+ timeout: 3000,
+ },
+ });
+ this.#API = CAR_API_URL;
+ this.#fuelType = CAR_FUELTYPE;
+ this.#segment = CAR_SEGMENT;
+ }
+
+ getAllCar() {
+ return this.#instance.get(this.#API.cars);
+ }
+
+ getFuelTypeSegmentCars({ fuelType, segment }) {
+ return this.#instance.get(this.#API.cars, {
+ params: {
+ fuelType,
+ segment,
+ },
+ });
+ }
+
+ getFuelTypeCars({ fuleType }) {
+ return this.#instance.get(this.#API.cars, {
+ params: {
+ fuleType,
+ },
+ });
+ }
+
+ async getSegmentCars({ segment }) {
+ return this.#instance.get(this.#API.cars, {
+ params: {
+ segment,
+ },
+ });
+ }
+
+ async getSmallCars() {
+ return this.getSegmentCars({ segment: this.#segment.small });
+ }
+
+ async getMediumCars() {
+ return this.getSegmentCars({ segment: this.#segment.medium });
+ }
+
+ async getLargeCars() {
+ return this.getSegmentCars({ segment: this.#segment.large });
+ }
+
+ async getSuvCars() {
+ return this.getSegmentCars({ segment: this.#segment.suv });
+ }
+}
+
+export default new carAPI();
diff --git a/src/api/constant.js b/src/api/constant.js
new file mode 100644
index 0000000..8752a63
--- /dev/null
+++ b/src/api/constant.js
@@ -0,0 +1,16 @@
+export const CAR_API_URL = {
+ cars: "/cars",
+};
+
+export const CAR_FUELTYPE = {
+ gasiline: "gasoline",
+ hybrid: "hybrid",
+ ev: "ev",
+};
+
+export const CAR_SEGMENT = {
+ small: "C",
+ medium: "D",
+ large: "E",
+ suv: "SUV",
+};
diff --git a/src/api/createInstance.js b/src/api/createInstance.js
new file mode 100644
index 0000000..d50c6a0
--- /dev/null
+++ b/src/api/createInstance.js
@@ -0,0 +1,8 @@
+import axios from "axios";
+
+export const createInstance = ({ url, config }) => {
+ return axios.create({
+ baseURL: url,
+ ...config,
+ });
+};
diff --git a/src/assets/icons/iconBack.png b/src/assets/icons/iconBack.png
new file mode 100644
index 0000000..56a68b8
Binary files /dev/null and b/src/assets/icons/iconBack.png differ
diff --git a/src/components/CarDetail/CarAdditionalProducts.jsx b/src/components/CarDetail/CarAdditionalProducts.jsx
new file mode 100644
index 0000000..b740558
--- /dev/null
+++ b/src/components/CarDetail/CarAdditionalProducts.jsx
@@ -0,0 +1,23 @@
+import React from "react";
+import { formatAmout } from "../../utils/formatAmount";
+import DetailInfo from "../common/DetailInfo";
+import SubHeader from "../common/SubHeader";
+
+const CarAdditionalProducts = ({ additionalProducts }) => {
+ return (
+
+
+
+ {additionalProducts &&
+ additionalProducts.map((element) => {
+ const { name, amount } = element;
+ return (
+
+ );
+ })}
+
+
+ );
+};
+
+export default CarAdditionalProducts;
diff --git a/src/components/CarDetail/CarDetail.jsx b/src/components/CarDetail/CarDetail.jsx
new file mode 100644
index 0000000..05db0d5
--- /dev/null
+++ b/src/components/CarDetail/CarDetail.jsx
@@ -0,0 +1,55 @@
+import React from "react";
+import styled from "styled-components";
+import { useLocation } from "../../../node_modules/react-router-dom/dist/index";
+import CarAdditionalProducts from "./CarAdditionalProducts";
+import CarInfo from "./CarInfo";
+import CarInsurance from "./CarInsurance";
+import CarMainDetail from "./CarMainDetail";
+
+const CarDetail = () => {
+ const {
+ state: {
+ state: {
+ startDate,
+ amount,
+ attribute: { brand, name, segment, fuelType, imageUrl },
+ insurance,
+ additionalProducts,
+ },
+ },
+ } = useLocation();
+
+ return (
+
+
+
+
+
+
+
+
+
+
+
+ );
+};
+
+const S = {
+ CarImg: styled.div`
+ width: 100%;
+ min-height: 20rem;
+ display: flex;
+ justify-content: center;
+ position: relative;
+ background-color: ${({ theme }) => theme.color.gray};
+ img {
+ max-width: 200px;
+ width: 100%;
+ }
+ `,
+ Detail: styled.div`
+ display: flex;
+ flex-direction: column;
+ `,
+};
+export default CarDetail;
diff --git a/src/components/CarDetail/CarDetailHeader.jsx b/src/components/CarDetail/CarDetailHeader.jsx
new file mode 100644
index 0000000..b01c728
--- /dev/null
+++ b/src/components/CarDetail/CarDetailHeader.jsx
@@ -0,0 +1,35 @@
+import styled from "styled-components";
+import { useNavigate } from "../../../node_modules/react-router-dom/dist/index";
+import icon from "../../assets/icons/iconBack.png";
+const CarDetailHeader = () => {
+ const navigate = useNavigate();
+ return (
+
+ navigate(-1)}>
+
+
+ 차량상세
+
+ );
+};
+
+const S = {
+ Header: styled.div`
+ padding: 2rem 15rem;
+ text-align: center;
+ position: relative;
+ h1 {
+ min-width: 6.4rem;
+ max-height: 6rem;
+ font-weight: 700;
+ font-size: 17px;
+ line-height: 21px;
+ color: ${({ theme }) => theme.black};
+ }
+ `,
+ BackButon: styled.button`
+ position: absolute;
+ left: 5%;
+ `,
+};
+export default CarDetailHeader;
diff --git a/src/components/CarDetail/CarInfo.jsx b/src/components/CarDetail/CarInfo.jsx
new file mode 100644
index 0000000..63f1f24
--- /dev/null
+++ b/src/components/CarDetail/CarInfo.jsx
@@ -0,0 +1,27 @@
+import React from "react";
+import styled from "styled-components";
+import DetailInfo from "../common/DetailInfo";
+import SubHeader from "../common/SubHeader";
+
+//TODO: utls로 분리
+const day = ["월", "화", "수", "목", "금", "토", "일"];
+const startDateFormat = (stringDate) => {
+ const date = new Date(stringDate);
+ return `${date.getMonth() + 1} 월 ${date.getDate()}일 (${day[date.getDay()]}) 부터 `;
+};
+
+const CarInfo = ({ segment, fuelType, startDate }) => {
+ return (
+
+
+
+
+
+
+ );
+};
+
+const S = {
+ CarInfo: styled.div``,
+};
+export default CarInfo;
diff --git a/src/components/CarDetail/CarInsurance.jsx b/src/components/CarDetail/CarInsurance.jsx
new file mode 100644
index 0000000..dd0b01d
--- /dev/null
+++ b/src/components/CarDetail/CarInsurance.jsx
@@ -0,0 +1,20 @@
+import React from "react";
+import DetailInfo from "../common/DetailInfo";
+import SubHeader from "../common/SubHeader";
+
+const CarInsurance = ({ insurance }) => {
+ return (
+
+
+
+ {insurance &&
+ insurance.map((element) => {
+ const { name, description } = element;
+ return ;
+ })}
+
+
+ );
+};
+
+export default CarInsurance;
diff --git a/src/components/CarDetail/CarMainDetail.jsx b/src/components/CarDetail/CarMainDetail.jsx
new file mode 100644
index 0000000..7b7ab85
--- /dev/null
+++ b/src/components/CarDetail/CarMainDetail.jsx
@@ -0,0 +1,45 @@
+import React from "react";
+import styled from "styled-components";
+import { formatAmout } from "../../utils/formatAmount";
+
+const CarMainDetail = ({ brand, name, amount }) => {
+ return (
+
+ {brand}
+ {name}
+
+ 월 {formatAmout(amount)} 원
+
+
+ );
+};
+
+const S = {
+ MainDetail: styled.div`
+ display: flex;
+ flex-direction: column;
+ gap: 0.2rem;
+ font-size: inherit;
+ padding: 2rem;
+ strong {
+ font-weight: 700;
+ font-size: 1.4rem;
+ }
+ > :first-child {
+ font-size: 2rem;
+ }
+
+ > :nth-child(2) {
+ font-size: 2.4rem;
+ margin-bottom: 0.8rem;
+ }
+ `,
+
+ AmountContainer: styled.span`
+ text-align: right;
+ font-weight: 400;
+ font-size: 1.7rem;
+ `,
+};
+
+export default CarMainDetail;
diff --git a/src/components/CarMain.jsx/CarHeader.jsx b/src/components/CarMain.jsx/CarHeader.jsx
new file mode 100644
index 0000000..1ea76cc
--- /dev/null
+++ b/src/components/CarMain.jsx/CarHeader.jsx
@@ -0,0 +1,24 @@
+import styled from "styled-components";
+
+const CarHeader = () => {
+ return (
+
+ 전체 차량
+
+ );
+};
+
+const S = {
+ Header: styled.div`
+ padding: 2rem 16rem;
+ text-align: center;
+ h1 {
+ min-width: 6.4rem;
+ font-weight: 700;
+ font-size: 17px;
+ line-height: 21px;
+ color: ${({ theme }) => theme.black};
+ }
+ `,
+};
+export default CarHeader;
diff --git a/src/components/CarMain.jsx/CarItem.jsx b/src/components/CarMain.jsx/CarItem.jsx
new file mode 100644
index 0000000..144905f
--- /dev/null
+++ b/src/components/CarMain.jsx/CarItem.jsx
@@ -0,0 +1,65 @@
+import React from "react";
+import styled from "styled-components";
+import { carModel } from "../../utils/carModel";
+import { formatAmout } from "../../utils/formatAmount";
+import New from "../common/New";
+
+const inDay = (date) => {
+ const today = new Date();
+ const day = new Date(date);
+ return today.getFullYear() === day.getFullYear() && today.getDate() >= day.getDate();
+};
+
+const CarItem = ({ brand, name, segment, fuelType, amount, imageUrl, createdAt }) => {
+ return (
+
+
+ {brand}
+ {name}
+ {`${segment} / ${carModel.fuelType[fuelType]}`}
+ 월 {formatAmout(amount)} 부터
+
+
+ {} {inDay(createdAt) ? : null}
+
+
+ );
+};
+
+const S = {
+ CarItemContainer: styled.li`
+ width: 100%;
+ height: 120px;
+ color: ${({ theme }) => theme.black};
+ border-bottom: 1px solid ${({ theme }) => theme.black};
+ padding: 1.25em 1.55em;
+ display: flex;
+ justify-content: space-between;
+ font-size: 1.2rem;
+ `,
+ CarInfo: styled.div`
+ display: flex;
+ flex-direction: column;
+ gap: 0.2rem;
+ font-size: inherit;
+ strong {
+ font-weight: 700;
+ font-size: 1.4rem;
+ }
+ > :nth-child(2) {
+ margin-bottom: 0.8rem;
+ }
+ `,
+
+ CarImg: styled.div`
+ position: relative;
+ width: 20rem;
+ background-color: ${({ theme }) => theme.color.gray};
+ img {
+ max-width: 200px;
+ width: 100%;
+ }
+ `,
+};
+
+export default CarItem;
diff --git a/src/components/CarMain.jsx/CarList.jsx b/src/components/CarMain.jsx/CarList.jsx
new file mode 100644
index 0000000..aaf5e62
--- /dev/null
+++ b/src/components/CarMain.jsx/CarList.jsx
@@ -0,0 +1,45 @@
+import React from "react";
+import styled from "styled-components";
+import { Link } from "../../../node_modules/react-router-dom/dist/index";
+import CarItem from "./CarItem";
+import Message from "../common/Message";
+
+const CarList = ({ carInfo, isLoading }) => {
+ if (isLoading) return ;
+
+ return (
+
+ {carInfo.length > 0 ? (
+ carInfo.map((car) => {
+ const {
+ id,
+ amount,
+ createdAt,
+ attribute: { brand, name, segment, fuelType, imageUrl },
+ } = car;
+ return (
+
+
+
+ );
+ })
+ ) : (
+
+ )}
+
+ );
+};
+
+const S = {
+ CarListContainer: styled.ul``,
+};
+
+export default CarList;
diff --git a/src/components/CarMain.jsx/Category.jsx b/src/components/CarMain.jsx/Category.jsx
new file mode 100644
index 0000000..2527b4c
--- /dev/null
+++ b/src/components/CarMain.jsx/Category.jsx
@@ -0,0 +1,31 @@
+import React from "react";
+import styled from "styled-components";
+import { useTags } from "../../hooks/useTags";
+import Tag from "../common/Tag";
+
+const Category = ({ setAllCar }) => {
+ const { tags, handleClick } = useTags(setAllCar);
+
+ return (
+
+ {tags
+ ? tags.map((tag) => (
+
+ ))
+ : null}
+
+ );
+};
+
+const S = {
+ Category: styled.ul`
+ display: flex;
+ align-items: center;
+ justify-content: flex-start;
+ gap: 0.8rem;
+ padding: 0.6rem;
+ border-top: 1px solid ${({ theme }) => theme.black};
+ border-bottom: 1px solid ${({ theme }) => theme.black};
+ `,
+};
+export default Category;
diff --git a/src/components/Layout.jsx b/src/components/Layout.jsx
new file mode 100644
index 0000000..794525c
--- /dev/null
+++ b/src/components/Layout.jsx
@@ -0,0 +1,17 @@
+import React from "react";
+import styled from "styled-components";
+import { Outlet } from "../../node_modules/react-router-dom/dist/index";
+
+const Layout = () => {
+ return (
+
+
+
+ );
+};
+
+const Wrraper = styled.div`
+ width: 100%;
+ max-width: 450px;
+`;
+export default Layout;
diff --git a/src/components/common/Button.jsx b/src/components/common/Button.jsx
new file mode 100644
index 0000000..96feada
--- /dev/null
+++ b/src/components/common/Button.jsx
@@ -0,0 +1,25 @@
+import React from "react";
+import styled from "styled-components";
+
+const Button = ({ label, onClick, color = "primary" }) => {
+ return (
+
+
+
+ );
+};
+
+export const ButtonContainer = styled.button`
+ padding: 0.5em 1.8em;
+ color: ${({ theme, color }) => theme.fontColor[color]};
+ background-color: ${({ theme, color }) => theme.bgColor[color]};
+ border-radius: 62px;
+ width: 100%;
+ min-width: 6.2rem;
+ min-height: 27px;
+ font-size: 1.4rem;
+ text-align: center;
+ box-sizing: border-box;
+`;
+
+export default Button;
diff --git a/src/components/common/DetailInfo.jsx b/src/components/common/DetailInfo.jsx
new file mode 100644
index 0000000..c9885eb
--- /dev/null
+++ b/src/components/common/DetailInfo.jsx
@@ -0,0 +1,35 @@
+import React from "react";
+import styled from "styled-components";
+
+const DetailInfo = ({ name, description }) => {
+ return (
+
+
+ {name}
+ {description}
+
+
+ );
+};
+
+const S = {
+ Info: styled.div`
+ display: flex;
+ flex-direction: column;
+ gap: 2rem;
+ padding: 1.2rem 2rem;
+ span {
+ display: flex;
+ justify-content: space-between;
+ }
+ strong {
+ font-weight: 700;
+ font-size: 1.7rem;
+ }
+ p {
+ font-weight: 400;
+ font-size: 1.7rem;
+ }
+ `,
+};
+export default DetailInfo;
diff --git a/src/components/common/Message.jsx b/src/components/common/Message.jsx
new file mode 100644
index 0000000..e7d7b9e
--- /dev/null
+++ b/src/components/common/Message.jsx
@@ -0,0 +1,26 @@
+import React from "react";
+import styled from "styled-components";
+
+const Message = ({ message }) => {
+ return (
+
+ {message}
+
+ );
+};
+
+const S = {
+ Container: styled.div`
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ height: 50vh;
+ text-align: center;
+ h1 {
+ font-weight: 700;
+ font-size: 1.7rem;
+ }
+ `,
+};
+
+export default Message;
diff --git a/src/components/common/New.jsx b/src/components/common/New.jsx
new file mode 100644
index 0000000..4027c7b
--- /dev/null
+++ b/src/components/common/New.jsx
@@ -0,0 +1,25 @@
+import React from "react";
+import styled from "styled-components";
+import { ButtonContainer } from "./Button";
+
+const New = () => {
+ return (
+
+
+
+ );
+};
+
+const S = {
+ New: styled(ButtonContainer)`
+ max-width: 5.2rem;
+ max-height: 2.2rem;
+ position: absolute;
+ top: 0;
+ right: 0;
+ transform: translate(20%, -50%);
+ padding: 0.4rem 0.3rem;
+ `,
+};
+
+export default New;
diff --git a/src/components/common/SubHeader.jsx b/src/components/common/SubHeader.jsx
new file mode 100644
index 0000000..e160ce4
--- /dev/null
+++ b/src/components/common/SubHeader.jsx
@@ -0,0 +1,24 @@
+import React from "react";
+import styled from "styled-components";
+
+const SubHeader = ({ title }) => {
+ return (
+
+ {title}
+
+ );
+};
+
+const S = {
+ SubHeader: styled.div`
+ padding: 2rem;
+ display: flex;
+ align-items: center;
+ height: 4.8rem;
+ font-weight: 700;
+ font-size: 1.7rem;
+ background-color: ${({ theme }) => theme.bgColor["tertiary"]};
+ color: ${({ theme }) => theme.fontColor["tertiary"]};
+ `,
+};
+export default SubHeader;
diff --git a/src/components/common/Tag.jsx b/src/components/common/Tag.jsx
new file mode 100644
index 0000000..9479688
--- /dev/null
+++ b/src/components/common/Tag.jsx
@@ -0,0 +1,12 @@
+import React from "react";
+import Button from "./Button";
+
+const Tag = ({ label, isActive, onClick }) => {
+ return (
+
+
+
+ );
+};
+
+export default Tag;
diff --git a/src/context/CarProvider.jsx b/src/context/CarProvider.jsx
new file mode 100644
index 0000000..eee93ee
--- /dev/null
+++ b/src/context/CarProvider.jsx
@@ -0,0 +1,55 @@
+import { createContext, useContext, useMemo, useState } from "react";
+import carAPI from "../api/carAPI";
+
+const CarInfoValueContext = createContext(null);
+const CarInfoActionContext = createContext(null);
+
+const useCarInfo = () => {
+ const value = useContext(CarInfoValueContext);
+ if (value === null) {
+ throw new Error("useCarInfo should be used within TodosProvider");
+ }
+ return value;
+};
+
+const useCarInfoActions = () => {
+ const value = useContext(CarInfoActionContext);
+ if (value === null) {
+ throw new Error("useCarInfoActions should be used within TodosProvider");
+ }
+ return value;
+};
+
+const CarInfoProvider = ({ children }) => {
+ const [carInfo, setCarInfo] = useState([]);
+ const [isLoading, setIsLoading] = useState(false);
+ const actions = useMemo(
+ () => ({
+ async getAllCar() {
+ setIsLoading(true);
+ const {
+ data: { payload },
+ } = await carAPI.getAllCar();
+ setIsLoading(false);
+ setCarInfo(payload);
+ },
+ async setAllCar(fetchData) {
+ setIsLoading(true);
+ const {
+ data: { payload },
+ } = await fetchData();
+ setIsLoading(false);
+ setCarInfo(payload);
+ },
+ }),
+ [],
+ );
+
+ return (
+
+ {children}
+
+ );
+};
+
+export { useCarInfo, useCarInfoActions, CarInfoProvider };
diff --git a/src/context/ColorProvider.jsx b/src/context/ColorProvider.jsx
new file mode 100644
index 0000000..a084bea
--- /dev/null
+++ b/src/context/ColorProvider.jsx
@@ -0,0 +1,8 @@
+import { ThemeProvider } from "styled-components";
+import theme from "../styles/theme";
+
+const ColorProvider = ({ children }) => {
+ return {children};
+};
+
+export { ColorProvider };
diff --git a/src/context/Providers.jsx b/src/context/Providers.jsx
new file mode 100644
index 0000000..fc2d21e
--- /dev/null
+++ b/src/context/Providers.jsx
@@ -0,0 +1,12 @@
+import { CarInfoProvider } from "./CarProvider";
+import { ColorProvider } from "./ColorProvider";
+
+const Providers = ({ children }) => {
+ return (
+
+ {children}
+
+ );
+};
+
+export default Providers;
diff --git a/src/hooks/useTags.js b/src/hooks/useTags.js
new file mode 100644
index 0000000..1b8b2f1
--- /dev/null
+++ b/src/hooks/useTags.js
@@ -0,0 +1,57 @@
+import { useState } from "react";
+import carAPI from "../api/carAPI";
+import { carModel } from "../utils/carModel";
+
+const initalTags = [
+ {
+ label: "전체",
+ isActive: true,
+ handleClick: () => carAPI.getAllCar(),
+ },
+ {
+ label: carModel.segment.C,
+ isActive: false,
+ handleClick: () => carAPI.getSmallCars(),
+ },
+ {
+ label: carModel.segment.D,
+ isActive: false,
+ handleClick: () => carAPI.getMediumCars(),
+ },
+ {
+ label: carModel.segment.E,
+ isActive: false,
+ handleClick: () => carAPI.getLargeCars(),
+ },
+];
+
+export const useTags = (setAllCar) => {
+ const [tags, setTags] = useState(initalTags);
+
+ const toggle = (findIndex) => {
+ const newTags = tags.map((tag, index) => {
+ if (findIndex === index)
+ return {
+ isActive: !tag.isActive,
+ label: tag.label,
+ handleClick: tag.handleClick,
+ };
+ else
+ return {
+ isActive: false,
+ label: tag.label,
+ handleClick: tag.handleClick,
+ };
+ });
+ setTags(newTags);
+ };
+
+ const handleClick = async (e) => {
+ const curLabel = e.target.innerText;
+ const findIndex = tags.findIndex((tag) => tag.label === curLabel);
+ toggle(findIndex);
+ setAllCar(await tags[findIndex].handleClick);
+ };
+
+ return { tags, handleClick };
+};
diff --git a/src/index.css b/src/index.css
deleted file mode 100644
index 714ab0e..0000000
--- a/src/index.css
+++ /dev/null
@@ -1,11 +0,0 @@
-body {
- margin: 0;
- font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", "Roboto", "Oxygen", "Ubuntu",
- "Cantarell", "Fira Sans", "Droid Sans", "Helvetica Neue", sans-serif;
- -webkit-font-smoothing: antialiased;
- -moz-osx-font-smoothing: grayscale;
-}
-
-code {
- font-family: source-code-pro, Menlo, Monaco, Consolas, "Courier New", monospace;
-}
diff --git a/src/index.js b/src/index.jsx
similarity index 89%
rename from src/index.js
rename to src/index.jsx
index a64e7d5..71a354b 100644
--- a/src/index.js
+++ b/src/index.jsx
@@ -1,6 +1,5 @@
import React from "react";
import ReactDOM from "react-dom/client";
-import "./index.css";
import App from "./App";
const root = ReactDOM.createRoot(document.getElementById("root"));
diff --git a/src/pages/CarDetailPage.jsx b/src/pages/CarDetailPage.jsx
new file mode 100644
index 0000000..0b847d0
--- /dev/null
+++ b/src/pages/CarDetailPage.jsx
@@ -0,0 +1,19 @@
+import React from "react";
+import styled from "styled-components";
+import CarDetail from "../components/CarDetail/CarDetail";
+import CarDetailHeader from "../components/CarDetail/CarDetailHeader";
+
+const CarDetailPage = () => {
+ return (
+
+
+
+
+ );
+};
+
+const S = {
+ DetailContainer: styled.div``,
+};
+
+export default CarDetailPage;
diff --git a/src/pages/MainCarPage.jsx b/src/pages/MainCarPage.jsx
new file mode 100644
index 0000000..a3457ca
--- /dev/null
+++ b/src/pages/MainCarPage.jsx
@@ -0,0 +1,30 @@
+import React, { useEffect } from "react";
+import styled from "styled-components";
+import Category from "../components/CarMain.jsx/Category";
+import CarHeader from "../components/CarMain.jsx/CarHeader";
+import CarList from "../components/CarMain.jsx/CarList";
+import { useCarInfo, useCarInfoActions } from "../context/CarProvider";
+
+const CarMain = () => {
+ const { carInfo, isLoading } = useCarInfo();
+
+ const { getAllCar, setAllCar } = useCarInfoActions();
+
+ useEffect(() => {
+ getAllCar();
+ }, [getAllCar]);
+
+ return (
+
+
+
+
+
+ );
+};
+
+export default CarMain;
+
+const S = {
+ CarContainer: styled.div``,
+};
diff --git a/src/pages/NotFoundPage.jsx b/src/pages/NotFoundPage.jsx
new file mode 100644
index 0000000..75f177c
--- /dev/null
+++ b/src/pages/NotFoundPage.jsx
@@ -0,0 +1,7 @@
+import React from "react";
+
+const NotFound = () => {
+ return NotFound
;
+};
+
+export default NotFound;
diff --git a/src/routes/AppRouter.jsx b/src/routes/AppRouter.jsx
new file mode 100644
index 0000000..8de81a5
--- /dev/null
+++ b/src/routes/AppRouter.jsx
@@ -0,0 +1,21 @@
+import React from "react";
+import { BrowserRouter, Route, Routes } from "../../node_modules/react-router-dom/dist/index";
+import Layout from "../components/Layout";
+import { routes } from "./routePath";
+
+const AppRouter = () => {
+ return (
+
+
+ }>
+ {routes.map((route) => {
+ const { path, element } = route;
+ return ;
+ })}
+
+
+
+ );
+};
+
+export default AppRouter;
diff --git a/src/routes/routePath.js b/src/routes/routePath.js
new file mode 100644
index 0000000..a662ff2
--- /dev/null
+++ b/src/routes/routePath.js
@@ -0,0 +1,24 @@
+import NotFoundPage from "../pages/NotFoundPage";
+import CarDetailPage from "../pages/CarDetailPage";
+import CarMainPage from "../pages/MainCarPage";
+
+export const routePath = {
+ carMainPage: "/",
+ carDetailPage: "detail/:id",
+ notFoundPage: "*",
+};
+
+export const routes = [
+ {
+ path: routePath.carMainPage,
+ element: ,
+ },
+ {
+ path: routePath.carDetailPage,
+ element: ,
+ },
+ {
+ path: routePath.notFoundPage,
+ element: ,
+ },
+];
diff --git a/src/styles/globalStyles.js b/src/styles/globalStyles.js
new file mode 100644
index 0000000..a632707
--- /dev/null
+++ b/src/styles/globalStyles.js
@@ -0,0 +1,19 @@
+import { createGlobalStyle } from "styled-components";
+import reset from "styled-reset";
+const GlobalStyle = createGlobalStyle`
+ ${reset}
+ html { font-size: 62.5%;
+ font-family: 'Inter', sans-serif;
+ }
+ button,a {all:unset}
+ div,li{
+ box-sizing: border-box;
+ }
+
+ #root{
+ display:flex ;
+ justify-content:center ;
+ }
+`;
+
+export default GlobalStyle;
diff --git a/src/styles/theme.js b/src/styles/theme.js
new file mode 100644
index 0000000..8c3a2b0
--- /dev/null
+++ b/src/styles/theme.js
@@ -0,0 +1,26 @@
+const color = {
+ black: "#000000",
+ gray: "#d9d9d9",
+ blue: "#0094ff",
+ white: "#FFFFFF",
+};
+
+const bgColor = {
+ primary: color.black,
+ secondary: color.gray,
+ tertiary: color.blue,
+};
+
+const fontColor = {
+ primary: color.white,
+ secondary: color.black,
+ tertiary: color.white,
+};
+
+const theme = {
+ color,
+ bgColor,
+ fontColor,
+};
+
+export default theme;
diff --git a/src/utils/carModel.js b/src/utils/carModel.js
new file mode 100644
index 0000000..01623cf
--- /dev/null
+++ b/src/utils/carModel.js
@@ -0,0 +1,20 @@
+export const carModel = {
+ fuelType: {
+ gasoline: "가솔린",
+ ev: "전기",
+ hybid: "하이브리드",
+ },
+ segment: {
+ C: "소형",
+ D: "중형",
+ E: "대형",
+ },
+};
+
+export const getCarSegment = () => {
+ return Object.values(carModel.segment);
+};
+
+export const getCarFuelType = () => {
+ return Object.values(carModel.fuelType);
+};
diff --git a/src/utils/formatAmount.js b/src/utils/formatAmount.js
new file mode 100644
index 0000000..8467bb8
--- /dev/null
+++ b/src/utils/formatAmount.js
@@ -0,0 +1,3 @@
+export const formatAmout = (amount) => {
+ return amount.toString().replace(/\B(?