상세 컨텐츠

본문 제목

[React] 해커톤 웹 프로젝트 - 2

Web 개발

by javadocq 2024. 8. 9. 15:17

본문

728x90

이번에는 Goods Page에 대해 알아보자.

 

Ranking 페이지와 구조는 비슷하나 이번에는 Dropdown이 있고 최신순, 추천순으로 정렬도 가능하다.

 

Ranking 페이지와 똑같이 Grid 방식을 이용했고 컴포넌트도 동일하게 이용을 하였다.

 

또, Goods 상품을 클릭 시 구매할 수 있는 GoodsBuy 페이지로도 이동이 가능하다.

 

<Goods.jsx>

import React, { useState } from 'react';
import all_filter from '../assets/ranking-all-filter.svg';
import pet_filter from '../assets/ranking-pet-filter.svg';
import ocean_filter from '../assets/ranking-ocean-filter.svg';
import hill_filter from '../assets/ranking-hill-filter.svg';
import camping_filter from '../assets/ranking-camping-filter.svg';
import season_filter from '../assets/ranking-season-filter.svg';
import array_drop_down from '../assets/arrow_drop_down.svg';
import array_drop_up from '../assets/arrow_drop_up.svg';

import Foot from '../components/Foot.jsx';
import GoodsImg from '../components/GoodsImg.jsx';
import GoodsRanking from '../components/GoodsRanking';
import FilterButton from '../components/FilterButton.jsx';
import Dropdown from '../components/Dropdown.jsx';

import '../styles/Goods.css';

const goodsFilterButtons = [
{ id: 'all-button', label: '전체', icon: all_filter },
{ id: 'pet-button', label: '반려동물', icon: pet_filter },
{ id: 'ocean-button', label: '바다', icon: ocean_filter },
{ id: 'hill-button', label: '산', icon: hill_filter },
{ id: 'camping-button', label: '캠핑', icon: camping_filter },
{ id: 'season-button', label: '계절', icon: season_filter },
{ id: 'character-button', label: '캐릭터', icon: season_filter },
{ id: 'animal-button', label: '동물', icon: season_filter },
{ id: 'sight-button', label: '풍경', icon: season_filter },
{ id: 'etc-button', label: '기타', icon: season_filter }
];


export default function Goods() {
const [view, setView] = useState(false);
const [selectedSort, setSelectedSort] = useState('최신순');
const [selectedCategory, setSelectedCategory] = useState('all-button');
const list = ['최신순', '추천순'];

const handleSortChange = (sortOption) => {
setSelectedSort(sortOption);
setView(false);
};

const handleCategoryChange = (categoryId) => {
setSelectedCategory(categoryId);
};

return (
<div className='Goods-wrap'>
<div className="Goods-main-best">
<GoodsRanking count={3} />
</div>

<div className="goods-filter">
<FilterButton
filterButtons={goodsFilterButtons}
setCategory={handleCategoryChange}
/>
</div>
<div className='goods-sort' onClick={() => setView(!view)}>
<p style={{ color: 'rgba(45, 45, 45, 0.40)', fontSize: '1.3rem', fontStyle: 'normal', fontWeight: '400', position:'relative'}}>
정렬 방식: <span style={{ color: '#000', fontSize: '1.3rem', fontStyle: 'normal', fontWeight: '500' }}>{selectedSort}</span>
{view ? <img src={array_drop_up} alt="dropdown up" /> : <img src={array_drop_down} alt="dropdown down" />}
{view && (
<Dropdown style={{position:'absolute'}}onSortChange={handleSortChange} selectedSort={selectedSort} list = {list}/>
)}
</p>
 
</div>
 

<div className="Goods-main-goods">
<GoodsImg selectedCategory={selectedCategory} selectedSort={selectedSort} />
</div>

<footer className="Goods-foot">
<Foot />
</footer>
</div>
);
}

 

 

<GoodsRanking.jsx>

 

import React, { useEffect, useState } from 'react';
import axios from 'axios';
import rankingShadow from '../assets/ranking_shadow.png';
import '../styles/RankingImg.css';

const BASE_URL = "http://3.39.26.152:8000";

const GoodsRankingBox = ({ index }) => {
const [ranking, setRanking] = useState([]);

// 상품 순위 데이터를 가져오는 비동기 함수
const fetchGoodsRanking = async () => {
try {
const response = await axios.get(`${BASE_URL}/api/goods/ranking`);
if (Array.isArray(response.data)) {
setRanking(response.data); // 배열로 설정
} else {
console.error("응답이 배열이 아닙니다:", response.data);
}
} catch (error) {
console.error("Error fetching ranking data:", error);
}
};
 
useEffect(() => {
fetchGoodsRanking();
}, []);

// 데이터가 로드되지 않았을 경우 로딩 상태 처리
if (!ranking) {
return <div>Loading...</div>;
}

return (
<div className="rankingImg-box">
<div className="number-container">
<p className='best-ranking-number'>{index + 1}</p>
</div>
<div className='ranking-box-container'>
<img src={rankingShadow} alt="Ranking Shadow" className="best-ranking-shadow" />
<img src={`http://${ranking[index]?.image}`} alt="Best Ranking" className="best-ranking" />
</div>
</div>
);
};

const GoodsRanking = ({ count }) => {
return (
<div className="rankingImg-wrap">
{Array.from({ length: count }, (_, index) => (
<GoodsRankingBox key={index} index={index} />
))}
</div>
);
};

export default GoodsRanking;

 

<Dropdown.jsx>

 

import React from 'react';
import '../styles/Dropdown.css';

export default function Dropdown({ onSortChange, selectedSort, list }) {
const sortOptions = list

const handleClick = (option) => {
onSortChange(option);
};

return (
<ul className='dropdown'>
{sortOptions.map(option => (
<li
key={option}
onClick={() => handleClick(option)}
style={{ backgroundColor: selectedSort === option ? '#F0F0F0' : '#FFF', width : '120px' }}
>
{option}
</li>
))}
</ul>
);
}

 

 

<GoodsBuy.jsx>

 

import React, { useState } from "react";
import { useLocation } from "react-router-dom";
import goodsBuyplus from "../assets/goodsBuy-plus.svg";
import goodsBuyminus from "../assets/goodsBuy-minus.svg";
import "../styles/GoodsBuy.css";

export default function GoodsBuy() {
const location = useLocation();
const { imgSrc, itemName, itemPrice } = location.state || {};
const [count, setCount] = useState(1);

function handleBuy() {
alert(`${count}개 구매되었습니다.`);
}

function handleValue(increment) {
setCount((prevCount) => Math.max(1, prevCount + increment)); // 최소 1개 유지
}

const formatPrice = (price) => {
return new Intl.NumberFormat("ko-KR").format(price);
};

return (
<div className="GoodsBuy-wrap">
<main className="GoodsBuy-main">
<div className="GoodsBuy-left">
<img
src={`http://${imgSrc}`}
alt="굿즈 사진"
className="GoodsBuy-img"
/>
</div>

<div className="GoodsBuy-right">
<div className="GoodsBuy-description">
<p className="GoodsBuy-producer">{itemName}</p>
<p className="GoodsBuy-introduction">{itemName}</p>
<p className="GoodsBuy-introduction-price">
{`${formatPrice(itemPrice)}원`}
</p>
</div>
<div className="GoodsBuy-line"></div>
<div className="GoodsBuy-countBox">
<p className="GoodsBuy-small-title">{itemName}</p>
<div className="GoodsBuy-count">
<button className="plus-minus" onClick={() => handleValue(1)}>
<img src={goodsBuyplus} alt="플러스기호" />
</button>
{count}
<button
className="plus-minus"
onClick={() => handleValue(-1)}
disabled={count <= 1}
>
<img src={goodsBuyminus} alt="마이너스기호" />
</button>
<span className="GoodsBuy-countPrice">
{`${formatPrice(count * itemPrice)}원`}
</span>
</div>
</div>
<div className="GoodsBuy-totalPrice-box">
<div className="GoodsBuy-price-description">총 가격</div>
<div className="GoodsBuy-totalPrice">
{`${formatPrice(count * itemPrice)}원`}
</div>
</div>
<button className="GoodsBuy-button" onClick={handleBuy}>
<p className="GoodsBuy-button-p">바로 구매하기</p>
</button>
</div>
</main>
</div>
);
}

 

GoodsImg -> GoodsBuy로 이동하는 과정에서는 useNavigation, useLocation을 사용하여 이미지, 제목, 가격정도를 넘겨주고

 

GoodsBuy에서 임의로 count 값에 따라 가격이 달라지게 만들어 놓았다.

 

 

 

 

 

 

'Web 개발' 카테고리의 다른 글

[React] 해커톤 웹 프로젝트 - 4  (0) 2024.08.11
[React] 해커톤 웹 프로젝트 - 3  (0) 2024.08.10
[React] 해커톤 웹 프로젝트 - 1  (1) 2024.08.08
[React] onKeyDown  (0) 2024.08.07
[React] useEffect  (0) 2024.08.06

관련글 더보기