Skip to content

Commit

Permalink
[team-01][FE] 기획전 UI & 기능 구현 (codesquad-members-2022#83)
Browse files Browse the repository at this point in the history
* Docs: Readme Update

* 팀원 소개, Tools, 그라운드룰, 브랜치 전략, 컨벤션 등에 대한 정리

* Design: Navbar, Menu, Icons 컴포넌트 레이아웃 수정 - #10

Co-authored-by: Hemudi <hemudi@users.noreply.github.com>

* Feat: Navbar 마우스 이벤트 추가 - #10

* MouseEnter, MouseLeave 이벤트 추가
* SubMenuhover 효과 추가

Co-authored-by: Hemudi <hemudi@users.noreply.github.com>

* Design: Global Style 에 font-family 적용 - #15

* theme의 font family 삭제

Co-authored-by: Millie <jaypedia@users.noreply.github.com>

* Rename: Header 컴포넌트 폴더 생성해서 그룹화 - #15

Co-authored-by: Millie <jaypedia@users.noreply.github.com>

* Design: 기획전 TitleBox, TabBar UI 구현 - #15

* Tab Mouse hover 밑줄 효과 구현

Co-authored-by: Millie <jaypedia@users.noreply.github.com>

* Fix: 기획전 Tab hover 시 사용성 개선 - #15

* 기존 border 속성을 box-shadow 로 변경

Co-authored-by: Millie <jaypedia@users.noreply.github.com>

* Feat: API fetch 요청하는 util 함수 구현 - #15

* GET 요청만 우선적으로 구현

Co-authored-by: Hemudi <hemudi@users.noreply.github.com>

* Design: CardContainer를 Styled Components로 구현 - #15

Co-authored-by: Hemudi <hemudi@users.noreply.github.com>

* Design: Card Component UI 구현 -  #15

Co-authored-by: Hemudi <hemudi@users.noreply.github.com>

* Feat: fetch로 API 요청, Card 동적 생성 - #15

* 임시 데이터로 UI 먼저 구현

Co-authored-by: Hemudi <hemudi@users.noreply.github.com>

* Feat: Tab 클릭 시 fetch 요청 후 리렌더링 기능 구현 - #16

* fetch 요청 로직을 fetchTabData 함수로 분리
* 하드 코딩 부분은 추후 리팩토링 예정

Co-authored-by: Millie <jaypedia@users.noreply.github.com>

* Refactor: .gitignore 불필요한 옵션 제거 - #17

Co-authored-by: Millie <jaypedia@users.noreply.github.com>

* Refactor: Navbar의 div 태그 header로 변경 - #17

Co-authored-by: Millie <jaypedia@users.noreply.github.com>

* Refactor: menu와 tabData 데이터 분리 후 동적생성 - #18

* 하드코딩 되었던 컴포넌트를 data 분리 후 map 으로 동적 생성

Co-authored-by: Millie <jaypedia@users.noreply.github.com>

Co-authored-by: Hemudi <ksum1205@naver.com>
Co-authored-by: Hemudi <hemudi@users.noreply.github.com>
Co-authored-by: Millie <jaypedia@users.noreply.github.com>
  • Loading branch information
4 people authored Apr 24, 2022
1 parent 9d92169 commit 2584c77
Show file tree
Hide file tree
Showing 14 changed files with 314 additions and 68 deletions.
15 changes: 1 addition & 14 deletions FE/.gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -2,22 +2,9 @@

# dependencies
/node_modules
/.pnp
.pnp.js

# testing
/coverage

# production
/build

# misc
.DS_Store
.env.local
.env.development.local
.env.test.local
.env.production.local

npm-debug.log*
yarn-debug.log*
yarn-error.log*
.DS_Store
4 changes: 3 additions & 1 deletion FE/src/App.js
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
import Header from './Header';
import Header from './Header/Header';
import Special from './Special/Special';

function App() {
return (
<div className="App">
<Header />
<Special />
</div>
);
}
Expand Down
44 changes: 0 additions & 44 deletions FE/src/Header.jsx

This file was deleted.

42 changes: 42 additions & 0 deletions FE/src/Header/Header.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
import React, { useState } from 'react';
import { Navbar, Menu, Icons, MenuBox, MainMenu, SubMenuList, SubMenu } from './Header.style.js';
import { ReactComponent as Logo } from '../assets/logo.svg';
import { ReactComponent as Search } from '../assets/search.svg';
import { ReactComponent as Login } from '../assets/login.svg';
import { ReactComponent as Cart } from '../assets/cart.svg';
import menuData from '../data/headerMenu.js';

const Header = () => {
const [isOpen, setIsOpen] = useState(false);

const toggleSubMenu = () => {
setIsOpen(!isOpen);
};

return (
<Navbar onMouseEnter={toggleSubMenu} onMouseLeave={toggleSubMenu}>
<Logo />
<Menu>
{menuData.map((v, i) => {
return (
<MenuBox key={i}>
<MainMenu>{v.main}</MainMenu>
<SubMenuList isOpen={isOpen}>
{v.sub.map((name, i) => {
return <SubMenu key={i}>{name}</SubMenu>;
})}
</SubMenuList>
</MenuBox>
);
})}
</Menu>
<Icons>
<Search />
<Login />
<Cart />
</Icons>
</Navbar>
);
};

export default Header;
15 changes: 11 additions & 4 deletions FE/src/Header.style.js → FE/src/Header/Header.style.js
Original file line number Diff line number Diff line change
@@ -1,25 +1,25 @@
import styled from 'styled-components';

const Navbar = styled.div`
const Navbar = styled.header`
display: flex;
justify-content: space-between;
padding: 16px 80px;
width: 100%;
height: 170px;
height: auto;
border-bottom: 1px solid black;
`;

const Menu = styled.div`
display: flex;
width: 900px;
width: 75%;
justify-content: flex-start;
`;

const Icons = styled.div`
display: flex;
justify-content: space-between;
width: 100px;
padding: 10px 0;
padding-top: 15px;
`;

const MenuBox = styled.div`
Expand All @@ -35,10 +35,17 @@ const MainMenu = styled.div`

const SubMenuList = styled.ul`
font-size: ${props => props.theme.fontSize.small};
display: ${({ isOpen }) => (isOpen ? 'block' : 'none')};
`;

const SubMenu = styled.li`
margin-bottom: 8px;
cursor: pointer;
:hover {
text-decoration: underline;
color: ${props => props.theme.colors.grey2};
}
`;

export { Navbar, Menu, Icons, MenuBox, MainMenu, SubMenuList, SubMenu };
65 changes: 65 additions & 0 deletions FE/src/Special/Special.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
import React, { useEffect, useState } from 'react';
import Card from '../components/Card';
import { fetchData } from '../utils/utils';
import {
SpecialContainer,
SpecialBadge,
SpecialTitle,
SpecialTitleBox,
SpecialTabBar,
SpecialHeader,
SpecialTab,
CardContainer,
} from './Special.style';
import tabData from '../data/specialTab.js';

const Special = () => {
const [data, setData] = useState([]);
const [tabNum, setTabNum] = useState(0);

useEffect(() => {
fetchTabData();
}, []);

const handleTabClick = tabNum => {
setTabNum(tabNum);
fetchTabData();
};

const fetchTabData = async () => {
const TEST_URL = 'http://3.39.42.204/api/dishes';
const data = await fetchData(TEST_URL);
setData(data.response.slice(0, 3));
};

return (
<SpecialContainer>
<SpecialHeader>
<SpecialTitleBox>
<SpecialBadge>기획전</SpecialBadge>
<SpecialTitle>한 번 주문하면 두 번 반하는 반찬</SpecialTitle>
</SpecialTitleBox>
<SpecialTabBar>
{tabData.map((v, i) => {
return (
<SpecialTab
key={i}
onClick={() => handleTabClick(v.index)}
isSelected={tabNum === v.index ? true : false}
>
{v.name}
</SpecialTab>
);
})}
</SpecialTabBar>
</SpecialHeader>
<CardContainer>
{data.map((v, i) => (
<Card key={i} data={v} />
))}
</CardContainer>
</SpecialContainer>
);
};

export default Special;
64 changes: 64 additions & 0 deletions FE/src/Special/Special.style.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
import styled from 'styled-components';

const SpecialContainer = styled.div`
width: 100%;
`;

const SpecialHeader = styled.div`
padding: 56px 80px 0 80px;
border-bottom: 1px solid ${props => props.theme.colors.grey4};
`;

const SpecialTitleBox = styled.div`
display: flex;
margin-bottom: 24px;
`;

const SpecialBadge = styled.div`
width: 76px;
height: 42px;
border: 2px solid ${props => props.theme.colors.black};
border-radius: 20px;
line-height: 42px;
text-align: center;
margin-right: 16px;
`;

const SpecialTitle = styled.h1`
font-size: ${props => props.theme.fontSize.xLarge};
font-weight: ${props => props.theme.fontWeight.display};
color: ${props => props.theme.colors.black};
`;

const SpecialTabBar = styled.div`
display: flex;
width: 100%;
`;

const SpecialTab = styled.div`
font-size: ${props => props.theme.fontSize.large};
font-weight: ${props => props.theme.fontWeight.bold};
color: ${props => props.theme.colors.black};
margin-right: 32px;
padding-bottom: 17px;
box-shadow: 0 ${({ isSelected }) => (isSelected ? '1px' : '0')} ${props => props.theme.colors.black};
cursor: pointer;
`;

const CardContainer = styled.div`
display: flex;
justify-content: space-between;
padding: 34px 80px 56px 80px;
border-bottom: 1px solid ${props => props.theme.colors.grey4};
`;

export {
SpecialContainer,
SpecialHeader,
SpecialTitleBox,
SpecialBadge,
SpecialTitle,
SpecialTabBar,
SpecialTab,
CardContainer,
};
21 changes: 21 additions & 0 deletions FE/src/components/Card.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import React from 'react';
import { CardWrapper, SubTitle, Title, Thumbnail, PriceBox, SalePrice, Badge, DescriptionWrapper } from './Card.syle';

const Card = ({ data }) => {
return (
<CardWrapper>
<Thumbnail src={data.thumbnail} />
<DescriptionWrapper>
<Title>{data.name}</Title>
<SubTitle>{data.description}</SubTitle>
<PriceBox>
<Title>{data.normalPrice.toLocaleString('ko-KR')}</Title>
<SalePrice>{data.salePrice.toLocaleString('ko-KR')}</SalePrice>
</PriceBox>
</DescriptionWrapper>
<Badge>런칭특가</Badge>
</CardWrapper>
);
};

export default Card;
56 changes: 56 additions & 0 deletions FE/src/components/Card.syle.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
import styled from 'styled-components';

const CardWrapper = styled.div`
display: flex;
flex-direction: column;
cursor: pointer;
`;

const Thumbnail = styled.div`
width: 411px;
height: 411px;
background-image: url(${({ src }) => src});
background-repeat: no-repeat;
background-size: cover;
`;

const Title = styled.h3`
font-size: ${props => props.theme.fontSize.medium};
font-weight: ${props => props.theme.fontWeight.bold};
color: ${props => props.theme.colors.grey1};
margin-right: 8px;
`;

const SubTitle = styled.p`
font-size: ${props => props.theme.fontSize.small};
color: ${props => props.theme.colors.grey2};
margin: 8px 0;
`;

const PriceBox = styled.div`
display: flex;
`;

const SalePrice = styled.p`
font-size: ${props => props.theme.fontSize.small};
color: ${props => props.theme.colors.grey3};
text-decoration: line-through;
`;

const Badge = styled.div`
width: 76px;
height: 30px;
border-radius: 20px;
background-color: ${props => props.theme.colors.orange};
color: ${props => props.theme.colors.white};
font-size: ${props => props.theme.fontSize.xSmall};
font-weight: ${props => props.theme.fontWeight.medium};
text-align: center;
line-height: 30px;
`;

const DescriptionWrapper = styled.div`
margin: 16px 0;
`;

export { CardWrapper, Thumbnail, Title, SubTitle, PriceBox, SalePrice, Badge, DescriptionWrapper };
14 changes: 14 additions & 0 deletions FE/src/data/headerMenu.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
export default [
{
main: '든든한 메인 요리',
sub: ['육류 요리', '해산물 요리'],
},
{
main: '뜨끈한 국물요리',
sub: ['국/탕/찌개'],
},
{
main: '정갈한 밑반찬',
sub: ['나물/무침', '조림/볶음', '절임/장아찌'],
},
];
Loading

0 comments on commit 2584c77

Please sign in to comment.