Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

0SCAR0421 정연우 #2

Open
wants to merge 27 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
27 commits
Select commit Hold shift + click to select a range
06d75c4
Design: style 초기화 및 폰트설정
0SCAR0421 Nov 2, 2022
45c2799
Feat: api 모듈 작성
0SCAR0421 Nov 2, 2022
ba29b7d
Feat: Button 컴포넌트 작성
0SCAR0421 Nov 2, 2022
e33b5f0
Feat: filter 컴포넌트 작성
0SCAR0421 Nov 2, 2022
39ec0a1
Feat: layout 컴포넌트 작성
0SCAR0421 Nov 2, 2022
09a282b
Feat: 컴포넌트 매핑
0SCAR0421 Nov 2, 2022
919171a
Feat: cardatalist context 작성
0SCAR0421 Nov 2, 2022
849a48b
Feat: filerlist context 작성
0SCAR0421 Nov 2, 2022
c698ac8
Feat: router 작성
0SCAR0421 Nov 2, 2022
3d6085d
Feat: 세그먼트 변경 함수 작성
0SCAR0421 Nov 2, 2022
2ff03be
Feat: Home 페이지 작성
0SCAR0421 Nov 2, 2022
0486c53
Feat: Detail 페이지 작성
0SCAR0421 Nov 2, 2022
4857068
Feat: page 매핑
0SCAR0421 Nov 2, 2022
86cf57b
Feat: context api 적용
0SCAR0421 Nov 2, 2022
4a274ce
Feat: segment, name, day 변환 함수 작성
0SCAR0421 Nov 3, 2022
5f1b117
Design: 스타일 적용범위 및 폰트 추가
0SCAR0421 Nov 3, 2022
ede9e1e
Fix: fetch 로직 수정
0SCAR0421 Nov 3, 2022
53a3285
Feat: CartItem 컴포넌트 작성
0SCAR0421 Nov 3, 2022
3e865ab
Design: Layout 컴포넌트 디자인 수정
0SCAR0421 Nov 3, 2022
2388972
Fix: Layout 로직 수정
0SCAR0421 Nov 3, 2022
9c2c25d
Feat: Notification 컴포넌트 작성
0SCAR0421 Nov 3, 2022
c72a3eb
Feat: CarItem, Notification 컴포넌트 매핑
0SCAR0421 Nov 3, 2022
468c570
Fix: CarDataListContext 로직 수정
0SCAR0421 Nov 3, 2022
c0c974f
Feat: CarDetail 컴포넌트 작성 및 디자인
0SCAR0421 Nov 3, 2022
372f6ce
Feat: Home 페이지 작성
0SCAR0421 Nov 3, 2022
b1e09f1
Feat: amount에 3자리 단위로 쉼표 추가
0SCAR0421 Nov 3, 2022
5a7610a
Feat: amount에 3자리 단위로 쉼표 추가
0SCAR0421 Nov 3, 2022
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 12 additions & 1 deletion src/App.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,16 @@
import React from "react";
import Router from "@routes";
import { FilterListContextProvider } from "@/contexts/FilterListContext";
import { CarDataListContextProvider } from "./contexts/CarDataListContext";

function App() {
return <h1>Hello React!</h1>;
return (
<FilterListContextProvider>
<CarDataListContextProvider>
<Router />
</CarDataListContextProvider>
</FilterListContextProvider>
);
}

export default App;
12 changes: 12 additions & 0 deletions src/apis/index.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import axios from "../../node_modules/axios/index";

const instance = axios.create({
baseURL: "https://preonboarding.platdev.net/api/cars",
timeout: 2000,
});

export const getCarData = async (selectedMenu) => {
const res = await instance.get(``, { params: { segment: selectedMenu } });

return res.data.payload;
};
25 changes: 25 additions & 0 deletions src/components/Button/Button.styled.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import styled from "styled-components";

export const ButtonContainer = styled.button`
width: 62px;
height: 27px;
font-size: 14px;
font-weight: 700;
border-radius: 62px;
border: none;
outline: none;
color: ${(props) => (props.isSelected ? "var(--white)" : "var(--black)")};
background: ${(props) => (props.isSelected ? "var(--black)" : "var(--gray)")};
position: relative;
cursor: pointer;

&:last-child:after {
content: "";
display: block;
position: absolute;
right: -12px;
top: 0;
width: 12px;
height: 100%;
}
`;
19 changes: 19 additions & 0 deletions src/components/Button/index.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import { useContext } from "react";
import { FilterListContext } from "../../contexts/FilterListContext";
import { ButtonContainer } from "./Button.styled";

const Button = ({ isSelected, children }) => {
const { handleSelectFilter } = useContext(FilterListContext);

const handleButtonClick = () => {
handleSelectFilter(children);
};

return (
<ButtonContainer isSelected={isSelected} onClick={handleButtonClick}>
{children}
</ButtonContainer>
);
};

export default Button;
22 changes: 22 additions & 0 deletions src/components/CarFilter/CarFilter.styled.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import styled from "styled-components";

export const CarFilterContainer = styled.div`
height: 100%;
overflow: auto;
border-bottom: 1px solid var(--black);

&::-webkit-scrollbar {
width: 0;
height: 0;
}

> div {
display: grid;
height: 40px;
padding: 0 12px;
grid-template-columns: repeat(auto-fill, 62px);
gap: 8px;
align-items: center;
grid-auto-flow: column;
}
`;
22 changes: 22 additions & 0 deletions src/components/CarFilter/index.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import { Button } from "@components";
import { useContext } from "react";
import { FilterListContext } from "../../contexts/FilterListContext";
import { CarFilterContainer } from "./CarFilter.styled";

const CarFilter = () => {
const { filterList } = useContext(FilterListContext);

return (
<CarFilterContainer>
<div>
{filterList.map((e, i) => (
<Button key={e.name} isSelected={e.isSelected}>
{e.name}
</Button>
))}
</div>
</CarFilterContainer>
);
};

export default CarFilter;
38 changes: 38 additions & 0 deletions src/components/CarItem/CarItem.styled.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
import styled from "styled-components";

export const CarItemContainer = styled.div`
width: 100%;
height: 120px;
padding: 24px 20px;
display: flex;
justify-content: space-between;
border-bottom: 1px solid var(--black);
cursor: pointer;
`;

export const LeftItem = styled.div`
> div:first-child {
margin-bottom: 8px;
font-weight: 700;
line-height: 17px;
}

> div:last-child {
font-weight: 500;
font-size: 12px;
line-height: 15px;
}

> div > p {
padding: 0;
margin: 0;
}
`;

export const RightItem = styled.div`
background-image: url(${(props) => props.image});
background-position: center center;
background-repeat: no-repeat;
background-size: cover;
width: 152px;
`;
34 changes: 34 additions & 0 deletions src/components/CarItem/index.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
import React from "react";
import { useNavigate } from "../../../node_modules/react-router-dom/dist/index";
import { convertName } from "@utils/convertValue";
import { CarItemContainer, LeftItem, RightItem } from "./CarItem.styled";
import { convertFuel, convertMoneyString } from "../../utils/convertValue";

const CarItem = ({ id, brand, name, segment, fuelType, imageUrl, amount }) => {
const navigate = useNavigate();
const handleTargetPage = (target) => {
navigate(`/detail/${target}`);
};

return (
<CarItemContainer onClick={() => handleTargetPage(id)}>
<LeftItem>
<div>
<p>{brand}</p>
<p>{name}</p>
</div>
<div>
<p>
<span>{convertName(segment)}</span>
<span> / </span>
<span>{convertFuel(fuelType)}</span>
</p>
<p>월 {convertMoneyString(amount)} 원 부터</p>
</div>
</LeftItem>
<RightItem image={imageUrl} />
</CarItemContainer>
);
};

export default CarItem;
35 changes: 35 additions & 0 deletions src/components/Layout/Layout.styled.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
import styled from "styled-components";

export const LayoutContainer = styled.div`
max-width: 450px;
margin: 0 auto;
min-height: 100vh;
`;

export const LayoutNavBar = styled.div`
position: relative;
`;

export const PrevButton = styled.div`
position: absolute;
top: 20px;
left: 18px;
cursor: pointer;
`;

export const LayoutHeader = styled.h1`
display: flex;
justify-content: center;
align-items: center;
height: 60px;
font-weight: 700;
font-size: 17px;
margin: 0;
border-bottom: 1px solid var(--black);
`;

export const LayoutContent = styled.div`
min-height: 75vh;
height: 100%;
background: rosybrown;
`;
59 changes: 59 additions & 0 deletions src/components/Layout/index.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
import React from "react";
import { Outlet, useLocation } from "react-router-dom";
import {
LayoutContainer,
LayoutNavBar,
PrevButton,
LayoutHeader,
} from "@components/Layout/Layout.styled";
import { CarFilter } from "@components";
import { useNavigate } from "../../../node_modules/react-router-dom/dist/index";

const Layout = ({ childeren }) => {
const { pathname } = useLocation();
const navigate = useNavigate();

return (
<LayoutContainer>
<LayoutNavBar>
{pathname === "/" ? (
<>
<LayoutHeader>전체차량</LayoutHeader>
<CarFilter />
</>
) : (
<>
<PrevButton onClick={() => navigate(-1)}>
<svg
width="24"
height="24"
viewBox="0 0 24 24"
fill="none"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M19 12H5"
stroke="black"
strokeWidth="2"
strokeLinecap="round"
strokeLinejoin="round"
/>
<path
d="M12 19L5 12L12 5"
stroke="black"
strokeWidth="2"
strokeLinecap="round"
strokeLinejoin="round"
/>
</svg>
</PrevButton>
<LayoutHeader>차량상세</LayoutHeader>
</>
)}
{childeren || <Outlet />}
</LayoutNavBar>
</LayoutContainer>
);
};

export default Layout;
11 changes: 11 additions & 0 deletions src/components/Notification/Notification.styled.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import styled from "styled-components";

export const NotificationContainer = styled.div`
font-weight: 700;
font-size: 17px;
line-height: 21px;
display: flex;
justify-content: center;
align-items: center;
min-height: 75vh;
`;
8 changes: 8 additions & 0 deletions src/components/Notification/index.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
import React from "react";
import { NotificationContainer } from "./Notification.styled";

const Notification = ({ children }) => {
return <NotificationContainer>{children}</NotificationContainer>;
};

export default Notification;
5 changes: 5 additions & 0 deletions src/components/index.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
export { default as Button } from "./Button";
export { default as CarFilter } from "./CarFilter";
export { default as Layout } from "./Layout";
export { default as CarItem } from "./CarItem";
export { default as Notification } from "./Notification";
32 changes: 32 additions & 0 deletions src/contexts/CarDataListContext.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
import React, { useContext, useEffect, useState } from "react";
import { getCarData } from "@apis";
import { FilterListContext } from "./FilterListContext";
import { convertSegment } from "@utils/convertValue";

export const CarDataListContext = React.createContext({
carDataList: [],
});

export const CarDataListContextProvider = ({ children }) => {
const { selectedMenu, filterList } = useContext(FilterListContext);
const [carDataList, setCarDataList] = useState([]);
const [isLoading, setIsLoading] = useState(true);

useEffect(() => {
const handleCarDataList = async () => {
const segment = convertSegment(selectedMenu);

setIsLoading(true);
setCarDataList(await getCarData(segment === "All" ? "" : segment));
setIsLoading(false);
};

handleCarDataList();
}, [selectedMenu, filterList]);

return (
<CarDataListContext.Provider value={{ carDataList, isLoading }}>
{children}
</CarDataListContext.Provider>
);
};
37 changes: 37 additions & 0 deletions src/contexts/FilterListContext.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
import React, { useContext, useState } from "react";

export const FilterListContext = React.createContext({
filterList: [
{ name: "전체", isSelected: true },
{ name: "대형", isSelected: false },
{ name: "중형", isSelected: false },
{ name: "소형", isSelected: false },
{ name: "SUV", isSelected: false },
],
selectedMenu: "All",
handleSelectFilter: () => {},
});

export const FilterListContextProvider = ({ children }) => {
const { filterList, selectedMenu } = useContext(FilterListContext);
const [newFilterList, setNewFilterList] = useState(filterList);
const [newSelectedMenu, setNewSelectedMenu] = useState(selectedMenu);

const handleSelectFilter = (target) => {
setNewSelectedMenu(target);
setNewFilterList((prev) =>
prev.map((e) => {
e.isSelected = e.name === target;
return e;
}),
);
};

return (
<FilterListContext.Provider
value={{ filterList: newFilterList, selectedMenu: newSelectedMenu, handleSelectFilter }}
>
{children}
</FilterListContext.Provider>
);
};
Loading