diff --git a/banchan/src/components/StateProvider.jsx b/banchan/src/components/StateProvider.jsx index 9f0315f0c..d402200ff 100644 --- a/banchan/src/components/StateProvider.jsx +++ b/banchan/src/components/StateProvider.jsx @@ -1,7 +1,7 @@ -import { useState } from "react"; +// import { useState } from "react"; import Header from "./header/Header"; import MainPage from "./main/MainPage"; -import Test from "./Test"; +// import Test from "./Test"; const StateProvider = () => { // const [loginState, setLoginState] = useState(false); @@ -11,7 +11,7 @@ const StateProvider = () => { <>
- + {/* */} ); }; diff --git a/banchan/src/components/main/CarouselList.jsx b/banchan/src/components/main/CarouselList.jsx new file mode 100644 index 000000000..fc1e461ec --- /dev/null +++ b/banchan/src/components/main/CarouselList.jsx @@ -0,0 +1,36 @@ +import { useEffect, useState } from 'react'; +import styled from 'styled-components'; +import Card from '../utils/Card'; +import theme from '../utils/styles/theme'; + +const CarouselList = (props) => { + const [products, setProducts] = useState([]); + + useEffect(() => { + fetch( + 'https://h3rb9c0ugl.execute-api.ap-northeast-2.amazonaws.com/develop/baminchan/main' + ) + .then((response) => response.json()) + .then((result) => setProducts(result.body)) + .then((error) => console.log('error', error)); + }, []); + + return ( + + {products.map((product) => ( + + ))} + + ); +}; + +const StyledUl = styled.ul` + display: flex; + justify-content: space-evenly; +`; + +export default CarouselList; diff --git a/banchan/src/components/main/CarouselSection.jsx b/banchan/src/components/main/CarouselSection.jsx index 0998ededa..b32da27bb 100644 --- a/banchan/src/components/main/CarouselSection.jsx +++ b/banchan/src/components/main/CarouselSection.jsx @@ -1,30 +1,134 @@ -import { useEffect, useState } from "react"; -import Card from "../utils/Card"; -import theme from "../utils/styles/theme"; +import { useEffect, useRef, useState } from 'react'; +// import { mockData, temp } from "../utils/mockData.js"; +import Card from '../utils/Card'; +import styled from 'styled-components'; +import IconButton from '../utils/button/IconButton'; +import { CenterContainer } from '../utils/styles/common'; -const CarouselSection = (props) => { +const CarouselSection = ({ key, url, title, onModal }) => { const [products, setProducts] = useState([]); + // const [products, setProducts] = useState(mockData); + const [currentX, setX] = useState(0); + const [currentIndex, setCurrentIndex] = useState(4); + const [rightDisabled, setLeftDisabled] = useState(false); + const [leftDisabled, setRightDisabled] = useState(true); + const slides = useRef(); + const slideCount = 4; + const slideWidth = 320; + const totalWidth = 320 * slideCount + 16; useEffect(() => { - fetch( - "https://h3rb9c0ugl.execute-api.ap-northeast-2.amazonaws.com/develop/baminchan/main" - ) - .then((response) => response.json()) - .then((result) => setProducts(result.body)) - .then((error) => console.log("error", error)); + (async () => { + const response = await fetch(url); + const result = await response.json(); + setProducts(result.body); + })(); }, []); + const moveSlide = (speed, distance, nextIndex) => { + slides.current.style.transition = `${speed}ms`; + slides.current.style.transform = `translateX(${distance}px)`; + setX(distance); + setCurrentIndex(nextIndex); + }; + + const moveRight = () => { + const remainSlideCount = products.length - currentIndex; + const distance = + remainSlideCount >= slideCount + ? currentX - totalWidth + : currentX - slideWidth * remainSlideCount; + const nextIndex = + remainSlideCount >= slideCount + ? currentIndex + slideCount + : currentIndex + remainSlideCount; + moveSlide(300, distance, nextIndex); + distance && setRightDisabled(false); + nextIndex >= products.length && setLeftDisabled(true); + }; + + const moveLeft = () => { + const remainSlideCount = currentIndex - slideCount; + const distance = + remainSlideCount >= slideCount + ? currentX + totalWidth + : currentX + slideWidth * remainSlideCount; + const nextIndex = + remainSlideCount >= slideCount + ? currentIndex - slideCount + : currentIndex - remainSlideCount; + moveSlide(300, distance, nextIndex); + !distance && setRightDisabled(true); + nextIndex < products.length && setLeftDisabled(false); + }; + return ( -
    - {products.map((product) => ( - - ))} -
+ + + {title} + + + {products.map((product) => ( + props.theme.cardSizes.M} + margin={8} + onModal={onModal} + /> + ))} + + + + + + + + ); }; +const SectionContainer = styled(CenterContainer)` + position: relative; + margin: 30px 0; + /*border: 1px solid blue;*/ + width: 1320px; +`; + +const SectionBox = styled.div` + /*border: 1px solid violet;*/ +`; + +const SectionContent = styled.div` + position: relative; + display: flex; + margin: 20px 0; + width: 1296px; /* 원래 1280 */ + height: 500px; + /*border: 1px solid black;*/ + overflow: hidden; +`; + +const CardList = styled(CenterContainer)` + list-style: none; + align-items: start; + position: absolute; +`; + +const SectionTitle = styled.div` + font-size: ${(props) => props.theme.fontSizes.XL}; + font-weight: bold; + color: ${(props) => props.theme.darkGray}; + margin: 20px 0; +`; + +const SectionButton = styled.div` + position: absolute; + top: 50%; + transform: translateY(-50%); + width: 100%; + display: flex; + justify-content: space-between; +`; + export default CarouselSection; diff --git a/banchan/src/components/main/CarouselSectionList.jsx b/banchan/src/components/main/CarouselSectionList.jsx index 0909dfc1b..ea668f8f5 100644 --- a/banchan/src/components/main/CarouselSectionList.jsx +++ b/banchan/src/components/main/CarouselSectionList.jsx @@ -1,3 +1,34 @@ -const CarouselSectionList = (props) => <>; +import { useState } from 'react'; +import { URLS } from '../utils/variables.js'; +import CarouselSection from './CarouselSection.jsx'; +import styled from 'styled-components'; +import { CenterContainer } from '../utils/styles/common.jsx'; + +const CarouselSectionList = (props) => { + const [sections, setSections] = useState([ + { id: 0, kind: 'main', title: '모두가 좋아하는 든든한 메인요리' }, + { id: 1, kind: 'soup', title: '정성이 담긴 뜨끈한 국물요리' }, + { id: 2, kind: 'side', title: '식탁을 풍성하게 하는 정갈한 밑반찬' }, + ]); + + return ( + + {sections.map((section) => ( + + ))} + + ); +}; + +const CarouselContainer = styled(CenterContainer)` + /*border: 1px solid red;*/ + display: flex; + flex-direction: column; +`; export default CarouselSectionList; diff --git a/banchan/src/components/main/MainPage.jsx b/banchan/src/components/main/MainPage.jsx index 8bfc3e5a2..1929e5602 100644 --- a/banchan/src/components/main/MainPage.jsx +++ b/banchan/src/components/main/MainPage.jsx @@ -1,13 +1,62 @@ -import CarouselSection from "./CarouselSection"; -import TabSection from "./tab/TabSection"; +import TabSection from './tab/TabSection'; +import CarouselSectionList from './CarouselSectionList'; +import { useEffect, useState } from 'react'; +import styled from 'styled-components'; +import { CenterContainer } from '../utils/styles/common'; +import Modal from './Modal'; const MainPage = (props) => { + const [modalState, setModalState] = useState(false); + const [modalData, setModalData] = useState({}); + const [detailDataMap, setDetailDataMap] = useState(new Map()); + + useEffect(() => { + fetch( + 'https://h3rb9c0ugl.execute-api.ap-northeast-2.amazonaws.com/develop/baminchan/detail' + ) + .then((res) => res.json()) + .then((response) => { + response.body.forEach((e) => { + setDetailDataMap(detailDataMap.set(e.hash, e.data)); + }); + }); + }, []); + + const handleModal = (product) => { + setModalState(true); + const detailData = detailDataMap.get(product.detail_hash); + setModalData({ ...product, ...detailData }); + }; + return ( <> - - + + + {modalState && ( + + + + + + + )} ); }; +const ModalBackground = styled(CenterContainer)` + position: fixed; + top: 0; + left: 0; + background: rgba(0, 0, 0, 0.4); + width: 100%; + height: 100%; + z-index: 5; +`; + +const ModalContainer = styled.div` + display: flex; + align-items: flex-start; +`; + export default MainPage; diff --git a/banchan/src/components/main/Modal.jsx b/banchan/src/components/main/Modal.jsx new file mode 100644 index 000000000..55e8c4ca7 --- /dev/null +++ b/banchan/src/components/main/Modal.jsx @@ -0,0 +1,77 @@ +import styled from 'styled-components'; +import Price from '../utils/Price'; +import TextButton from '../utils/button/TextButton'; +import { + LabelList, + StyledDescription, + StyledTitle, +} from '../utils/styles/common'; + +const Modal = ({ product }) => { + console.log(product); + return ( + + + product-thumbnail + + {product.thumb_images.map((i) => ( +
  • + +
  • + ))} +
    +
    + + + {product.title} + {product.description} +
    + + +
    +
    + +
    적립금: {product.point}
    +
    배송정보: {product.delivery_info}
    +
    배송비 : {product.delivery_fee}
    +
    + + + 여기는 총 주문 금액이 들어갈 예정입니다. 카운트를 같이 계산해서.. + + +
    +
    + ); +}; + +const ModalCard = styled.div` + background: white; + width: 960px; + height: 1076px; +`; + +const ThumbnailUL = styled.div` + display: flex; +`; + +const Thumbnail = styled.img` + width: 100px; + height: 100px; +`; + +const ProductImage = styled.div``; + +const Information = styled.div``; + +const ProductMainInfo = styled.div``; + +// const LabelList = styled.div``; + +const ProductBuyInfo = styled.div``; + +const ProductCount = styled.div``; + +const ProductPrice = styled.div``; + +export default Modal; diff --git a/banchan/src/components/main/tab/TabSection.jsx b/banchan/src/components/main/tab/TabSection.jsx index 968149085..e0b3b7f85 100644 --- a/banchan/src/components/main/tab/TabSection.jsx +++ b/banchan/src/components/main/tab/TabSection.jsx @@ -1,11 +1,11 @@ -import { useState, useEffect } from "react"; -import styled from "styled-components"; -import Card from "../../utils/Card"; -import { CenterContainer } from "../../utils/styles/common"; -import { Button, SectionTitle, CardList } from "../../utils/styles/common"; +import { useState, useEffect } from 'react'; +import styled from 'styled-components'; +import Card from '../../utils/Card'; +import { CenterContainer } from '../../utils/styles/common'; +import { Button, SectionTitle, CardList } from '../../utils/styles/common'; const tempUrl = - "https://h3rb9c0ugl.execute-api.ap-northeast-2.amazonaws.com/develop/baminchan/best"; + 'https://h3rb9c0ugl.execute-api.ap-northeast-2.amazonaws.com/develop/baminchan/best'; const TabSection = (props) => { const [activeTab, setActiveTab] = useState(0); @@ -48,6 +48,7 @@ const TabSection = (props) => { product={item} cardSize={(props) => props.theme.cardSizes.L} margin={12} + onModal={props.onModal} /> ))} @@ -66,7 +67,7 @@ const TabButton = styled(Button)` : props.theme.colors.lightGrayBG}; color: ${(props) => props.activated ? props.theme.colors.darkGray : props.theme.colors.gray}; - font-weight: ${(props) => props.activated && "bold"}; + font-weight: ${(props) => props.activated && 'bold'}; width: 201px; height: 58px; margin-right: 8px; diff --git a/banchan/src/components/utils/Card.jsx b/banchan/src/components/utils/Card.jsx index a60cd5c7a..29dedaf39 100644 --- a/banchan/src/components/utils/Card.jsx +++ b/banchan/src/components/utils/Card.jsx @@ -1,20 +1,43 @@ -import styled from "styled-components"; -import theme from "../utils/styles/theme.js"; -import Price from "./Price"; -import Label from "./Label"; -import { CenterContainer } from "./styles/common.jsx"; +import styled from 'styled-components'; +import theme from '../utils/styles/theme.js'; +import Price from './Price'; +import Label from './Label'; +import { + CenterContainer, + LabelList, + StyledDescription, + StyledTitle, +} from './styles/common.jsx'; const mockImage = - "https://recipe1.ezmember.co.kr/cache/recipe/2020/09/23/5e308abb30b00ecb9c1b9b398db5b4451.jpg"; + 'https://recipe1.ezmember.co.kr/cache/recipe/2020/09/23/5e308abb30b00ecb9c1b9b398db5b4451.jpg'; -const Card = ({ product, cardSize, margin = 0, type }) => { +const Card = ({ product, cardSize, margin = 0, type, onModal }) => { return ( - - + onModal(product)} + > + + + + {product.delivery_type.reduce((acc, val, idx, array) => { + acc.push( +

    + {val} {idx < array.length - 1 ? : ''} +

    + ); + return acc; + }, [])} +
    +
    + +
    {product.title} {product.description} @@ -25,15 +48,15 @@ const Card = ({ product, cardSize, margin = 0, type }) => { ); }; -const LabelList = styled(CenterContainer)` - justify-content: start; -`; - const StyledLi = styled.li` width: ${(props) => props.cardSize}; margin: 0 ${(props) => props.margin}px; `; +const StyledThumbnail = styled.div` + position: relative; +`; + const StyledImg = styled.img` border-radius: ${theme.borders.radius}; margin-bottom: 16px; @@ -41,15 +64,31 @@ const StyledImg = styled.img` height: ${(props) => props.cardSize}; `; -const StyledTitle = styled.div` - font-size: ${theme.fontSizes.S}; - color: ${theme.colors.darkGray}; +const StyledHoverLayer = styled(CenterContainer)` + color: ${(props) => props.theme.colors.white}; + opacity: 0; + position: absolute; + top: 0; + left: 0; + width: ${(props) => props.cardSize}; + height: ${(props) => props.cardSize}; + border-radius: ${theme.borders.radius}; + z-index: 4; + font-size: ${(props) => props.theme.fontSizes.XL}; + font-weight: bold; + + &:hover { + background: linear-gradient(0deg, rgba(0, 0, 0, 0.6), rgba(0, 0, 0, 0.6)); + opacity: 1; + } `; -const StyledDescription = styled.div` - font-size: ${theme.fontSizes.XS}; - color: ${theme.colors.gray}; - margin: 8px 0px; +const DeliveryTypeList = styled.div``; + +const Divider = styled.div` + border: 1px solid ${(props) => props.theme.colors.white}; + width: 90px; + margin: 16px 0; `; export default Card; diff --git a/banchan/src/components/utils/Price.jsx b/banchan/src/components/utils/Price.jsx index 25603f6ad..ff4a2ef1b 100644 --- a/banchan/src/components/utils/Price.jsx +++ b/banchan/src/components/utils/Price.jsx @@ -1,6 +1,6 @@ -import styled from "styled-components"; -import theme from "../utils/styles/theme.js"; -import { CenterContainer } from "./styles/common.jsx"; +import styled from 'styled-components'; +import theme from '../utils/styles/theme.js'; +import { CenterContainer } from './styles/common.jsx'; const Price = ({ product }) => { const isOnSale = !!product.n_price; @@ -9,8 +9,8 @@ const Price = ({ product }) => { } return ( - {product.n_price} - {product.s_price} + {product.s_price} + {product.n_price} ); }; diff --git a/banchan/src/components/utils/button/IconButton.jsx b/banchan/src/components/utils/button/IconButton.jsx index d65681221..dd160431d 100644 --- a/banchan/src/components/utils/button/IconButton.jsx +++ b/banchan/src/components/utils/button/IconButton.jsx @@ -3,9 +3,9 @@ import theme from "../styles/theme"; import { BUTTON_TYPE } from "../variables.js"; import { Button } from "../styles/common"; -const IconButton = ({ type, fn }) => { +const IconButton = ({ type, fn, disabled = false }) => { return ( - + {BUTTON_TYPE[type]} ); @@ -13,12 +13,9 @@ const IconButton = ({ type, fn }) => { const StyledIconButton = styled(Button)` cursor: pointer; - color: ${({ type }) => - type === "UP" || type === "DOWN" - ? theme.colors.darkGray - : theme.colors.lightGray}; - font-size: ${({ type }) => - type === "UP" || type === "DOWN" ? theme.fontSizes.M : theme.fontSizes.XL}; + color: ${(props) => + props.disabled || props.type === "SEARCH" ? props.theme.colors.lightGray : props.theme.colors.darkGray}; + font-size: ${({ type }) => (type === "UP" || type === "DOWN" ? theme.fontSizes.M : "30px")}; `; export default IconButton; diff --git a/banchan/src/components/utils/mockData.js b/banchan/src/components/utils/mockData.js new file mode 100644 index 000000000..e0264ae49 --- /dev/null +++ b/banchan/src/components/utils/mockData.js @@ -0,0 +1,145 @@ +// 9개 +const mockData = [ + { + detail_hash: "HBBCC", + image: "http://public.codesquad.kr/jk/storeapp/data/fdb0d5fcfb86e332505785225a6d9ade.jpg", + alt: "[마샐미디쉬] 유자소스 연근무침 250g", + delivery_type: ["새벽배송", "전국택배"], + title: "[마샐미디쉬] 유자소스 연근무침 250g", + description: "이것은 테스트용입니다.", + s_price: "1000원", + }, + { + detail_hash: "H1939", + image: "http://public.codesquad.kr/jk/storeapp/data/e5646e5fc09a01a9243979b229e0572b.jpg", + alt: "[동네부엌] 쇠고기야채장조림 200g", + delivery_type: ["새벽배송", "전국택배"], + title: "[동네부엌] 쇠고기야채장조림 200g", + description: "이것은 테스트용입니다.", + s_price: "1000원", + }, + { + detail_hash: "H8EA5", + image: "http://public.codesquad.kr/jk/storeapp/data/4cfd1954861ebd18b5b53e558a8e902e.jpg", + alt: "[소중한식사] 도라지초무침 150g", + delivery_type: ["새벽배송", "전국택배"], + title: "[소중한식사] 도라지초무침 150g", + description: "이것은 테스트용입니다.", + s_price: "1000원", + }, + { + detail_hash: "H602F", + image: "http://public.codesquad.kr/jk/storeapp/data/422befe07f7e2860b9a83a8d7049ec2e.jpg", + alt: "[미노리키친] 일본식 우엉조림(킨피라고보) 80g", + delivery_type: ["새벽배송", "전국택배"], + title: "[미노리키친] 일본식 우엉조림(킨피라고보) 80g", + description: "이것은 테스트용입니다.", + s_price: "1000원", + }, + { + detail_hash: "H9F0B", + image: "http://public.codesquad.kr/jk/storeapp/data/d0b5d2be962947d9534e2140d1b34b2d.jpg", + alt: "[빅마마의밥친구] 갈치포무침 150g", + delivery_type: ["새벽배송", "전국택배"], + title: "[빅마마의밥친구] 갈치포무침 150g", + description: "이것은 테스트용입니다.", + s_price: "1000원", + }, + { + detail_hash: "H0FC6", + image: "http://public.codesquad.kr/jk/storeapp/data/f6817349118d4c671da8dca9065649a9.jpg", + alt: "[마더앤찬] 명란치즈계란말이 230g", + delivery_type: ["새벽배송", "전국택배"], + title: "[마더앤찬] 명란치즈계란말이 230g", + description: "이것은 테스트용입니다.", + s_price: "1000원", + }, + { + detail_hash: "HCCFE", + image: "http://public.codesquad.kr/jk/storeapp/data/757878b14ee5a8d5af905c154fc38f01.jpg", + alt: "[옹가솜씨] 달걀곤약조림 330g", + delivery_type: ["새벽배송", "전국택배"], + title: "[옹가솜씨] 달걀곤약조림 330g", + description: "이것은 테스트용입니다.", + s_price: "1000원", + }, + { + detail_hash: "HB9C1", + image: "http://public.codesquad.kr/jk/storeapp/data/043cf496f07899e7515f761e29d1ffa9.jpg", + alt: "[너의반찬] 미소된장 고추무침 200g", + delivery_type: ["새벽배송", "전국택배"], + title: "[너의반찬] 미소된장 고추무침 200g", + description: "이것은 테스트용입니다.", + s_price: "1000원", + }, + { + detail_hash: "SKDNFS", + image: "https://upload.wikimedia.org/wikipedia/commons/thumb/1/10/Banchan_2.jpg/330px-Banchan_2.jpg", + alt: "위키백과 반찬", + delivery_type: ["새벽배송", "전국택배"], + title: "위키백과 반찬", + description: "이것은 테스트용입니다.", + s_price: "1000원", + }, +]; + +// 6개 +const temp = [ + { + detail_hash: "235DSN", + image: + "https://upload.wikimedia.org/wikipedia/commons/thumb/2/21/0606_hanjeongsik_damyang.jpg/361px-0606_hanjeongsik_damyang.jpg", + alt: "전라도 반찬", + delivery_type: ["새벽배송", "전국택배"], + title: "전라도 반찬", + description: "이것은 테스트용입니다.", + s_price: "1000원", + }, + { + detail_hash: "11111", + image: "https://upload.wikimedia.org/wikipedia/commons/b/b1/Korean_cuisine-Banchan-11.jpg", + alt: "이것은 111111", + delivery_type: ["새벽배송", "전국택배"], + title: "이것은 111111", + description: "이것은 테스트용입니다.", + s_price: "1000원", + }, + { + detail_hash: "22222", + image: "https://upload.wikimedia.org/wikipedia/commons/thumb/1/10/Banchan_2.jpg/1280px-Banchan_2.jpg", + alt: "이것은 22222", + delivery_type: ["새벽배송", "전국택배"], + title: "이것은 22222", + description: "이것은 테스트용입니다.", + s_price: "1000원", + }, + { + detail_hash: "33333", + image: "https://upload.wikimedia.org/wikipedia/commons/4/4f/Korean_cuisine-Ojingeochae_bokkeum-01.jpg", + alt: "이것은 33333", + delivery_type: ["새벽배송", "전국택배"], + title: "이것은 33333", + description: "이것은 테스트용입니다.", + s_price: "1000원", + }, + { + detail_hash: "44444", + image: "https://upload.wikimedia.org/wikipedia/commons/0/0e/1005_eggjjim.jpg", + alt: "이것은 44444", + delivery_type: ["새벽배송", "전국택배"], + title: "이것은 44444", + description: "이것은 테스트용입니다.", + s_price: "1000원", + }, + { + detail_hash: "55555", + image: "https://upload.wikimedia.org/wikipedia/commons/c/c4/Korean_pancake-Samsaek_jeon-01.jpg", + alt: "이것은 55555", + delivery_type: ["새벽배송", "전국택배"], + title: "이것은 55555", + description: "이것은 테스트용입니다.", + s_price: "1000원", + }, +]; + +export { mockData, temp }; diff --git a/banchan/src/components/utils/styles/common.jsx b/banchan/src/components/utils/styles/common.jsx index 9439f2916..4f99bcc8d 100644 --- a/banchan/src/components/utils/styles/common.jsx +++ b/banchan/src/components/utils/styles/common.jsx @@ -1,5 +1,5 @@ -import styled from "styled-components"; -import theme from "./theme.js"; +import styled from 'styled-components'; +import theme from './theme.js'; const Button = styled.button` all: unset; @@ -24,4 +24,27 @@ const CardList = styled.ul` justify-content: center; `; -export { Button, CenterContainer, SectionTitle, CardList }; +const StyledTitle = styled.div` + font-size: ${theme.fontSizes.S}; + color: ${theme.colors.darkGray}; +`; + +const StyledDescription = styled.div` + font-size: ${theme.fontSizes.XS}; + color: ${theme.colors.gray}; + margin: 8px 0px; +`; + +const LabelList = styled(CenterContainer)` + justify-content: start; +`; + +export { + Button, + CenterContainer, + SectionTitle, + CardList, + StyledTitle, + StyledDescription, + LabelList, +}; diff --git a/banchan/src/components/utils/variables.js b/banchan/src/components/utils/variables.js index cd564e5ba..77952b737 100644 --- a/banchan/src/components/utils/variables.js +++ b/banchan/src/components/utils/variables.js @@ -25,4 +25,8 @@ const LABEL_TYPE = { 베스트: "BEST", }; -export { BUTTON_TYPE, LABEL_TYPE }; +const URLS = { + base: "https://h3rb9c0ugl.execute-api.ap-northeast-2.amazonaws.com/develop/baminchan/", +}; + +export { BUTTON_TYPE, LABEL_TYPE, URLS };