Skip to content

Commit

Permalink
Merge pull request #157 from boostcampwm-2024/fe/feature/CropMarket_u…
Browse files Browse the repository at this point in the history
…pdate

[FE/feature] 작물시장 기능 및 UI 수정
  • Loading branch information
edder773 authored Dec 3, 2024
2 parents 18ab33e + 503eb19 commit e5bd09c
Show file tree
Hide file tree
Showing 19 changed files with 247 additions and 171 deletions.
9 changes: 8 additions & 1 deletion apps/backend/src/websocket/websocket.gateway.ts
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,14 @@ export class WebsocketGateway implements OnGatewayInit, OnGatewayConnection, OnG
WHERE member_id = $1
`;
const crops = await this.databaseService.query(query, [memberId]);
this.cropDataTransfer(memberId, crops.rows);
const data = crops.rows.map(crop => ({
cropId: crop.crop_id,
availableQuantity: crop.available_quantity,
pendingQuantity: crop.pending_quantity,
totalQuantity: crop.total_quantity
}));

this.cropDataTransfer(memberId, data);
});

await subscriber.pSubscribe('__keyspace@0__:orderBook:*', async (_, message) => {
Expand Down
101 changes: 66 additions & 35 deletions apps/frontend/src/components/CropMarket/AskingPrice.tsx
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import { useEffect, useRef } from 'react';

interface AskingPriceProps {
validCropName: string;
marketData: {
Expand All @@ -8,52 +10,81 @@ interface AskingPriceProps {
}

const AskingPrice: React.FC<AskingPriceProps> = ({ validCropName, marketData }) => {
const containerRef = useRef<HTMLDivElement>(null);

useEffect(() => {
if (containerRef.current) {
containerRef.current.scrollTop =
containerRef.current.scrollHeight / 2 - containerRef.current.clientHeight / 2;
}
}, [marketData.sellOrders, marketData.buyOrders]);

if (!validCropName) {
return <div>작물 정보가 없습니다.</div>;
}

const maxItems = 4;
const emptyOrder = { price: 0, quantity: 0 };

const sellOrdersToDisplay = [
...new Array(Math.max(0, maxItems - marketData.sellOrders.length)).fill(emptyOrder),
...marketData.sellOrders.sort((a, b) => b.price - a.price).slice(0, maxItems)
];

const buyOrdersToDisplay = [
...marketData.buyOrders.sort((a, b) => b.price - a.price).slice(0, maxItems),
...new Array(Math.max(0, maxItems - marketData.buyOrders.length)).fill(emptyOrder)
];

return (
<div className="w-full h-full md:text-xs lg:text-xs xl:text-sm">
<div className="grid grid-cols-[1fr_1fr_1fr] gap-1 font-semibold bg-gray-800 px-1">
<div className="w-full h-full md:text-xs lg:text-xs xl:text-sm flex flex-col">
<div className="grid grid-cols-[1fr_1fr_1fr] gap-1 font-semibold px-1">
<div className="text-center">구분</div>
<div className="text-center">가격</div>
<div className="text-center">수량</div>
</div>
<div className="md:h-24 lg:h-26 xl:h-28 overflow-y-auto scrollbar-hidden">
{marketData.sellOrders
.sort((a, b) => b.price - a.price)
.map((order, idx) => (
<div
key={idx}
className={`relative grid grid-cols-[1fr_1fr_1fr] gap-1 py-1 px-1 items-center ${
order.price === marketData.nowPrice ? 'border border-black' : ''
}`}
>
{order.price === marketData.nowPrice && (
<div className="absolute top-1/2 transform -translate-y-1/2 scale-x-[-1] w-0 h-0 border-t-[6px] border-t-transparent border-b-[6px] border-b-transparent border-r-[6px] border-r-black"></div>
)}
<div className="text-center">팔아요</div>
<div className="text-center">{order.price}</div>
<div className="font-bold text-center text-blue-600">{order.quantity}</div>
<div ref={containerRef} className="md:h-24 lg:h-26 xl:h-28 overflow-y-auto scrollbar-hidden">
{sellOrdersToDisplay.map((order, idx) => (
<div
key={idx}
className={`relative grid grid-cols-[1fr_1fr_1fr] gap-1 py-1 px-1 items-center ${
order.price === marketData.nowPrice && order.price !== 0 ? 'border border-black' : ''
}`}
>
{order.price === marketData.nowPrice && order.price !== 0 && (
<div className="absolute top-1/2 transform -translate-y-1/2 scale-x-[-1] w-0 h-0 border-t-[6px] border-t-transparent border-b-[6px] border-b-transparent border-r-[6px] border-r-black"></div>
)}
<div className="text-center">{order.price > 0 ? '팔아요' : '\u00A0'}</div>
<div className="text-center">
{order.price > 0 ? `₩ ${order.price.toLocaleString()}` : '\u00A0'}
</div>
<div className="font-bold text-center text-blue-600">
{order.quantity > 0 ? order.quantity.toLocaleString() : '\u00A0'}
</div>
</div>
))}
{(marketData.sellOrders.length > 0 || marketData.buyOrders.length > 0) && (
<hr className="bg-black h-[1px] border-none" />
)}
{buyOrdersToDisplay.map((order, idx) => (
<div
key={idx}
className={`relative grid grid-cols-[1fr_1fr_1fr] gap-1 py-1 px-1 items-center ${
order.price === marketData.nowPrice && order.price !== 0 ? 'border border-black' : ''
}`}
>
{order.price === marketData.nowPrice && order.price !== 0 && (
<div className="absolute top-1/2 transform -translate-y-1/2 scale-x-[-1] w-0 h-0 border-t-[6px] border-t-transparent border-b-[6px] border-b-transparent border-r-[6px] border-r-black"></div>
)}
<div className="text-center">{order.price > 0 ? '살게요' : '\u00A0'}</div>
<div className="text-center">
{order.price > 0 ? `₩ ${order.price.toLocaleString()}` : '\u00A0'}
</div>
))}
{marketData.buyOrders
.sort((a, b) => b.price - a.price)
.map((order, idx) => (
<div
key={idx}
className={`relative grid grid-cols-[1fr_1fr_1fr] gap-1 py-1 px-1 items-center ${
order.price === marketData.nowPrice ? 'border border-black' : ''
}`}
>
{order.price === marketData.nowPrice && (
<div className="absolute top-1/2 transform -translate-y-1/2 scale-x-[-1] w-0 h-0 border-t-[6px] border-t-transparent border-b-[6px] border-b-transparent border-r-[6px] border-r-black"></div>
)}
<div className="text-center">살게요</div>
<div className="text-center">{order.price}</div>
<div className="font-bold text-center text-red-500">{order.quantity}</div>
<div className="font-bold text-center text-red-500">
{order.quantity > 0 ? order.quantity.toLocaleString() : '\u00A0'}
</div>
))}
</div>
))}
</div>
</div>
);
Expand Down
4 changes: 2 additions & 2 deletions apps/frontend/src/components/CropMarket/OwnCrop.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -31,8 +31,8 @@ const OwnCrop: React.FC<OwnCropProps> = ({ ownCrop, cropNameList, nowPrice }) =>
return (
<tr key={cropId}>
<td>{cropDisplayName}</td>
<td>{totalQuantity}</td>
<td>{price * totalQuantity}</td>
<td>{totalQuantity.toLocaleString()}</td>
<td>{(price * totalQuantity).toLocaleString()}</td>
</tr>
);
})}
Expand Down
14 changes: 9 additions & 5 deletions apps/frontend/src/components/CropMarket/Pending.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,9 @@ const Pending: React.FC<PendingProps> = ({ cropNameList }) => {
fetchPending();
}, []);

const handleCancel = async (order: MergedPendingData) => {
const handleCancel = async (e: React.MouseEvent<HTMLButtonElement>, order: MergedPendingData) => {
e.currentTarget.blur();

try {
const data = {
cropId: order.cropId,
Expand Down Expand Up @@ -89,7 +91,7 @@ const Pending: React.FC<PendingProps> = ({ cropNameList }) => {
<div>{error}</div>
) : (
<>
<div className="grid grid-cols-[1.2fr_2fr_1fr_1fr_auto] gap-1 font-semibold bg-gray-800 py-1 px-1">
<div className="grid grid-cols-[1.2fr_2fr_1fr_1fr_auto] gap-1 font-semibold py-1 px-1">
<div className="text-center">작물명</div>
<div className="text-center">주문시간</div>
<div className="text-center">수량</div>
Expand All @@ -105,12 +107,14 @@ const Pending: React.FC<PendingProps> = ({ cropNameList }) => {
>
<div className="text-center">{cropList[pending.cropName]}</div>
<div className="text-center">{formatDate(pending.time)}</div>
<div className="text-center">{pending.quantity}</div>
<div className="text-center">{pending.unfilledQuantity.toLocaleString()}</div>
<div className="text-center">{pending.price.toLocaleString()}</div>
<div className="text-center">
<button
className="px-1 bg-green-500 text-white rounded text-[9px]"
onClick={() => handleCancel(pending)}
className={`px-1 rounded text-[9px] ${
pending.orderType === 'buy' ? 'bg-red-500' : 'bg-blue-500'
} text-white`}
onClick={e => handleCancel(e, pending)}
>
취소
</button>
Expand Down
Loading

0 comments on commit e5bd09c

Please sign in to comment.