Skip to content

Commit

Permalink
Merge pull request #8 from CHZZK-Study/feat/product-list-markup
Browse files Browse the repository at this point in the history
Feat: 상품 등록 페이지 마크업 및 Button 공통 컴포넌트 생성
  • Loading branch information
aquaman122 authored Jul 23, 2024
2 parents 5e5a0e5 + 5ac2eff commit 23d6000
Show file tree
Hide file tree
Showing 8 changed files with 388 additions and 4 deletions.
11 changes: 11 additions & 0 deletions .eslintrc.cjs
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,17 @@ module.exports = {
'warn',
{ allowConstantExport: true },
],
"react/react-in-jsx-scope": "off",
// 함수형 컴포넌트 정의 규칙 수정
'react/function-component-definition': [
'error',
{
namedComponents: 'arrow-function',
unnamedComponents: 'arrow-function',
},
],
// Button 간의 Type구분 꺼두기
'react/button-has-type': 'off',
'react/react-in-jsx-scope': 'off',
},
parserOptions: {
Expand Down
6 changes: 6 additions & 0 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
},
"dependencies": {
"@reduxjs/toolkit": "^2.2.6",
"classnames": "^2.5.1",
"dotenv": "^16.4.5",
"react": "^18.3.1",
"react-dom": "^18.3.1",
Expand Down
7 changes: 3 additions & 4 deletions src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,7 @@ import React from 'react';
import { RouterProvider } from 'react-router-dom';
import { router } from './routing';

function App() {
return <RouterProvider router={router} />;
}

const App = () => {
return <<RouterProvider router={router} />;
};
export default App;
165 changes: 165 additions & 0 deletions src/ProductList.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,165 @@
import React, { useState } from 'react';
import classNames from 'classnames';
import { LuUsers } from 'react-icons/lu';
import { FaHeart } from 'react-icons/fa';
import Layout from './ui/Layout';
import Button from './components/common/Button';
import ongoingProducts from './mock/data/ongoingData';
import upcomingProducts from './mock/data/upcomingData';

const ProductList = () => {
const [activeTab, setActiveTab] = useState('ongoing');
const [activeFilter, setActiveFilter] = useState('');

return (
<Layout
header={
<header className="flex items-center justify-between p-4 border-b">
<button className="text-gray-500" aria-label="뒤로 가기">
<svg
className="w-6 h-6"
fill="none"
stroke="currentColor"
viewBox="0 0 24 24"
>
<path
strokeLinecap="round"
strokeLinejoin="round"
strokeWidth="2"
d="M15 19l-7-7 7-7"
/>
</svg>
</button>
<h1 className="text-lg font-semibold">상품 경매 목록</h1>
<div />
</header>
}
footer={null}
>
<div className="flex justify-center w-full mt-3">
{/* eslint-disable-next-line jsx-a11y/click-events-have-key-events, jsx-a11y/no-static-element-interactions */}
<div
className={classNames(
'flex justify-center items-center w-full py-2 ml-4 cursor-pointer text-sm',
activeTab === 'ongoing'
? 'border-b-2 border-black'
: 'border-b-2 border-gray-300',
)}
onClick={() => setActiveTab('ongoing')}
>
진행 중인 경매
</div>
{/* eslint-disable-next-line jsx-a11y/click-events-have-key-events, jsx-a11y/no-static-element-interactions */}
<div
className={classNames(
'flex justify-center w-full items-center py-2 mr-4 cursor-pointer text-sm',
activeTab === 'upcoming'
? 'border-b-2 border-black'
: 'border-b-2 border-gray-300',
)}
onClick={() => setActiveTab('upcoming')}
>
사전 등록 경매
</div>
</div>
<div className="flex h-22px space-x-3 p-4">
<Button
size="xsmall"
color={classNames(activeFilter === 'popular' ? 'black' : 'white')}
hoverColor="black"
type="button"
className="rounded-full"
onClick={() => setActiveFilter('popular')}
>
인기
</Button>
<Button
size="xsmall"
color={classNames(activeFilter === 'highPrice' ? 'black' : 'white')}
hoverColor="black"
type="button"
className="rounded-full"
onClick={() => setActiveFilter('highPrice')}
>
높은 가격순
</Button>
<Button
size="xsmall"
color={classNames(activeFilter === 'lowPrice' ? 'black' : 'white')}
hoverColor="black"
type="button"
className="rounded-full"
onClick={() => setActiveFilter('lowPrice')}
>
낮은 가격순
</Button>
<Button
size="xsmall"
color={classNames(activeFilter === 'latest' ? 'black' : 'white')}
hoverColor="black"
type="button"
className="rounded-full"
onClick={() => setActiveFilter('latest')}
>
최신순
</Button>
</div>
<div className="p-4 h-[calc(100vh-100px)] overflow-y-auto">
{activeTab === 'ongoing'
? ongoingProducts.map((product) => (
<div key={product.id} className="mb-4">
<div className="flex h-[96px]">
<div className="w-[96px] h-full bg-gray-300" />
<div className="flex flex-col gap-[8px] ml-4">
<div>
<p className="text-xs">{product.name}</p>
</div>
<div>
<p className="text-xs font-semibold text-gray-500">
{`시작가 ${product.startPrice}`}
</p>
<div className="flex gap-1">
<LuUsers />
<p className="text-xs text-gray-500">
{`${product.activeUserCount}명 참여 중`}
</p>
</div>
</div>
<Button
color="black"
type="button"
className={`${product.isBidding ? 'bg-gray-700' : 'bg-black'} w-[100px] h-[33px] rounded-md`}
>
{product.isBidding ? '경매 참여 중' : '경매 참여하기'}
</Button>
</div>
</div>
</div>
))
: upcomingProducts.map((product) => (
<div key={product.id} className="mb-4">
<div className="flex h-[96px]">
<div className="w-[96px] h-full bg-gray-300" />
<div className="flex flex-col gap-[8px] ml-4">
<div>
<p className="text-xs">{product.name}</p>
</div>
<div>
<p className="text-xs font-semibold text-gray-500">
{`시작가 ${product.startPrice}`}
</p>
</div>
<div className="flex items-center gap-1">
<FaHeart />
<p className="text-xs text-gray-500">{product.likes}</p>
</div>
</div>
</div>
</div>
))}
</div>
</Layout>
);
};

export default ProductList;
68 changes: 68 additions & 0 deletions src/components/common/Button.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
import React from 'react';
import classNames from 'classnames';

interface ButtonProps extends React.ButtonHTMLAttributes<HTMLButtonElement> {
children: React.ReactNode;
size?: 'xsmall' | 'small' | 'medium' | 'large';
disabled?: boolean;
color: string;
hoverColor?: string;
}

const Button: React.FC<ButtonProps> = ({
className,
children,
size,
color,
hoverColor,
disabled,
onClick,
type,
}) => {
const baseClasses = 'focus:outline-none';
const colorClasses = classNames({
'bg-black text-white': color === 'black',
'bg-white text-black border border-black': color === 'white',
[`bg-${color}`]: color !== 'black' && color !== 'white',
});
const sizeClasses = classNames({
'px-2 py-0.5 text-xs': size === 'xsmall',
'px-2 py-1 text-sm': size === 'small',
'px-4 py-2 text-base': size === 'medium',
'px-6 py-3 text-lg': size === 'large',
});
const hoverColorClasses = classNames({
'hover:bg-black hover:text-white': hoverColor === 'black',
'hover:bg-white hover:text-black border border-black':
hoverColor === 'white',
});
const combinedClasses = classNames(
colorClasses,
baseClasses,
sizeClasses,
hoverColorClasses,
className,
{
'opacity-50 cursor-not-allowed': disabled,
},
);

return (
<button
className={combinedClasses}
disabled={disabled}
onClick={onClick}
type={type}
>
{children}
</button>
);
};

Button.defaultProps = {
disabled: false,
hoverColor: '',
size: 'small',
};

export default Button;
76 changes: 76 additions & 0 deletions src/mock/data/ongoingData.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
const ongoingProducts = [
{
id: 1,
name: '[나이키] 신발',
startPrice: '10,000원',
timeLeft: '16시간 남음',
activeUserCount: '11',
isBidding: false,
},
{
id: 2,
name: '[나이키] 신발',
startPrice: '10,000원',
timeLeft: '1시간 남음',
activeUserCount: '11',
isBidding: true,
},
{
id: 3,
name: '[나이키] 신발',
startPrice: '10,000원',
timeLeft: '1시간 남음',
activeUserCount: '11',
isBidding: true,
},
{
id: 4,
name: '[나이키] 신발',
startPrice: '10,000원',
timeLeft: '4시간 남음',
activeUserCount: '11',
isBidding: true,
},
{
id: 5,
name: '[나이키] 신발',
startPrice: '10,000원',
timeLeft: '5시간 남음',
activeUserCount: '11',
isBidding: true,
},
{
id: 6,
name: '[나이키] 신발',
startPrice: '10,000원',
timeLeft: '6시간 남음',
activeUserCount: '11',
isBidding: true,
},
{
id: 7,
name: '[나이키] 신발',
startPrice: '10,000원',
timeLeft: '7시간 남음',
activeUserCount: '11',
isBidding: true,
},
{
id: 8,
name: '[나이키] 신발',
startPrice: '10,000원',
timeLeft: '8시간 남음',
activeUserCount: '11',
isBidding: true,
},
{
id: 9,
name: '[나이키] 신발',
startPrice: '10,000원',
timeLeft: '9시간 남음',
activeUserCount: '11',
isBidding: true,
},
];

export default ongoingProducts;
Loading

0 comments on commit 23d6000

Please sign in to comment.