Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

dev merge #80

Merged
merged 28 commits into from
Oct 1, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
28 commits
Select commit Hold shift + click to select a range
af97cd4
fix: productList μŠ€νƒ€μΌ μˆ˜μ •
aquaman122 Sep 30, 2024
636d6ec
fix: SSE 45000ms error ν•΄κ²°
CLOUDoort Oct 1, 2024
8e051d1
type: NotificationTypeμ—μ„œ INotification으둜 λ³€κ²½
CLOUDoort Oct 1, 2024
9ccc76c
fix: 둜그인 ν›„ ν™ˆνŽ˜μ΄μ§€ λ¦¬λ‹€μ΄λ ‰νŠΈν•˜λŠ” 경우 toast μ œν•œ
CLOUDoort Oct 1, 2024
7f27725
Merge pull request #70 from CHZZK-Study/fix/notification
CLOUDoort Oct 1, 2024
e826045
feat: 사전경맀 / 경맀 μƒμ„ΈνŽ˜μ΄μ§€ 뢄리
luminox93 Oct 1, 2024
4ad2fdc
feat: 사전경맀 / 경맀 μƒμ„ΈνŽ˜μ΄μ§€ 뢄리
luminox93 Oct 1, 2024
8280180
refactor: Details νŽ˜μ΄μ§€ μˆ˜μ •
luminox93 Oct 1, 2024
597cd97
refactor: Details νŽ˜μ΄μ§€ μˆ˜μ •
luminox93 Oct 1, 2024
dacc16a
refactor: PreAuction νŽ˜μ΄μ§€ 뢄리
luminox93 Oct 1, 2024
88cf15f
fix: λ§ˆμ΄νŽ˜μ΄μ§€ κΈ°λŠ₯ μΆ”κ°€
aquaman122 Oct 1, 2024
6b750a6
chore: console.log μ‚­μ œ
aquaman122 Oct 1, 2024
96b541d
feat: EmptyBoundary 적용
CLOUDoort Oct 1, 2024
f167eee
fix: 기타 μˆ˜μ •
CLOUDoort Oct 1, 2024
34632be
fix: heartbeat μ„€μ •
CLOUDoort Oct 1, 2024
444b774
feat: κΈ°λ³Έ 이미지 μ„€μ •
CLOUDoort Oct 1, 2024
5bbc40d
style: 기타 μŠ€νƒ€μΌ μˆ˜μ •
CLOUDoort Oct 1, 2024
40929f2
Merge pull request #76 from CHZZK-Study/feat/emptyStateBoundary
CLOUDoort Oct 1, 2024
99f4721
feat: AuctionItem μΈν„°νŽ˜μ΄μŠ€ 생성
luminox93 Oct 1, 2024
9b2bc52
fix: AuctionItem import μœ„μΉ˜ μˆ˜μ •
luminox93 Oct 1, 2024
05dfcd0
fix: ν”„λ‘œν•„ μˆ˜μ •νŽ˜μ΄μ§€ linkκ°’ μˆ˜μ •
aquaman122 Oct 1, 2024
71a45b9
fix: DetailPages 제거
luminox93 Oct 1, 2024
c81a884
feat: PreAuctionItem interface 생성
luminox93 Oct 1, 2024
d03ebda
fix: λΆˆν•„μš”ν•œ ꡬ문 제거
luminox93 Oct 1, 2024
74f546d
test: bid flow
CLOUDoort Oct 1, 2024
3e1eaa9
Merge pull request #77 from CHZZK-Study/feat/detail
CLOUDoort Oct 1, 2024
282b37e
merge: fix/apis-link
aquaman122 Oct 1, 2024
e89a953
fix: merge 좩돌 ν•΄κ²°
CLOUDoort Oct 1, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions src/@types/Notification.d.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
declare module 'Notification' {
export interface RealTimeNotificationType {
export interface IRealTimeNotification {
notificationId: number;
message: string;
type: string;
auctionId?: number;
}

export interface NotificationType {
export interface INotification {
id: number;
type: string;
message: string;
Expand Down
32 changes: 32 additions & 0 deletions src/components/bid/queries.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
import { UseMutateFunction, useMutation } from '@tanstack/react-query';

import { API_END_POINT } from '@/constants/api';
import { httpClient } from '@/api/axios';
import { toast } from 'sonner';
import { useNavigate } from 'react-router-dom';

interface IBidData {
auctionId: number;
amount: number;
}

export const usePostBid = (
auctionId: number
): {
mutate: UseMutateFunction<void, Error, IBidData, unknown>;
} => {
const postBid = async (bidData: IBidData) => {
await httpClient.post(API_END_POINT.BID, bidData);
};

const navigate = useNavigate();
const { mutate } = useMutation({
mutationFn: postBid,
onSuccess: () => {
toast.success('μž…μ°° 성곡!');
navigate(`/auctions/auction/${auctionId}`);
},
});

return { mutate };
};
16 changes: 16 additions & 0 deletions src/components/common/EmptyBoundary.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import EmptyFallback from './EmptyFallback';
import { ReactNode } from 'react';

interface EmptyBoundaryProps {
dataLength: number;
type: string;
children: ReactNode;
}

const EmptyBoundary = ({ dataLength, type, children }: EmptyBoundaryProps) => {
if (dataLength === 0) return <EmptyFallback type={type} />;

return children;
};

export default EmptyBoundary;
7 changes: 7 additions & 0 deletions src/components/common/EmptyFallback.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
import { EMPTY_MESSAGE } from '@/constants/emptyMessage';

const EmptyFallback = ({ type }: { type: string }) => {
return <div className='flex items-center justify-center w-full h-full md:text-heading1 text-heading3 text-mainCheeseYellow'>{EMPTY_MESSAGE[type]}</div>;
};

export default EmptyFallback;
11 changes: 1 addition & 10 deletions src/components/common/ProductItem.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
import jordanBlackImage from '@/assets/images/jordan_black.jpeg';
import { getTimeColor } from '@/utils/getTimeColor';
import { useEffect } from 'react';

export interface ProductProps {
id: number;
Expand All @@ -19,21 +18,13 @@ export interface ProductProps {
const ProductItem = ({
product,
children,
onDelete,
}: {
product: ProductProps;
children: React.ReactNode;
onDelete: (id: number) => void;
}) => {
const remainHour = Math.floor(product.timeRemaining ?? 0 / 3600);
const remainHour = Math.floor((product.timeRemaining ?? 0) / 3600);
const timeColor = getTimeColor(remainHour);

useEffect(() => {
if (product.timeRemaining === 0) {
onDelete(product.id);
}
}, [product.timeRemaining, onDelete, product.id]);

return (
<div key={product.id} className="mb-4">
<div className="flex flex-col">
Expand Down
18 changes: 5 additions & 13 deletions src/components/common/loadingAndError/ErrorFallback.tsx
Original file line number Diff line number Diff line change
@@ -1,22 +1,14 @@
import { FallbackProps } from 'react-error-boundary';
import Button from '../Button';
import NoData from './NoData';
import { FallbackProps } from 'react-error-boundary';

const ErrorFallback = ({ error, resetErrorBoundary }: FallbackProps) => {
if (!error) return null;
const renderUI = error.response.data.status === 404;
return (
<div className='flex flex-col items-center justify-center w-full h-full gap-3'>
{renderUI ? (
<NoData />
) : (
<>
<span className='text-heading1 text-cheeseYellow'>Error</span>
<Button type='button' color='cheeseYellow' onClick={resetErrorBoundary}>
λ‹€μ‹œ μ‹œλ„ν•˜κΈ°
</Button>
</>
)}
<span className='text-heading1 text-cheeseYellow'>Error</span>
<Button type='button' color='cheeseYellow' onClick={resetErrorBoundary}>
λ‹€μ‹œ μ‹œλ„ν•˜κΈ°
</Button>
</div>
);
};
Expand Down
5 changes: 0 additions & 5 deletions src/components/common/loadingAndError/NoData.tsx

This file was deleted.

5 changes: 3 additions & 2 deletions src/components/common/route/PublicRoute.tsx
Original file line number Diff line number Diff line change
@@ -1,14 +1,15 @@
import { isLoggedIn } from '@/store/authSlice';
import { ReactNode } from 'react';
import { useSelector } from 'react-redux';
import { Navigate } from 'react-router-dom';
import { Navigate, useLocation } from 'react-router-dom';
import { toast } from 'sonner';

const PublicRoute = ({ children }: { children: ReactNode }) => {
const isLogin = useSelector(isLoggedIn);
const { search } = useLocation();

if (isLogin) {
toast.warning('이미 둜그인된 μ‚¬μš©μžμž…λ‹ˆλ‹€.');
if (search === '') toast.warning('이미 둜그인된 μ‚¬μš©μžμž…λ‹ˆλ‹€.');
return <Navigate to='/' replace />;
}

Expand Down
28 changes: 28 additions & 0 deletions src/components/details/AuctionItem.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
export interface AuctionItem {
productId: number;
sellerName: string;
name: string;
description: string;
minPrice: number;
timeRemaining: number;
status: string;
isSeller: boolean;
participantCount: number;
isParticipating: boolean;
bidId: number | null;
bidAmount: number;
remainingBidCount: number;
imageList: string[];
}

export interface PreAuctionItem {
productId: number;
productName: string;
sellerName: string;
minPrice: number;
createdAt: string;
description: string;
likeCount: number;
isLiked: boolean;
imageUrls: string[];
}
18 changes: 9 additions & 9 deletions src/components/details/ProgressBar.tsx
Original file line number Diff line number Diff line change
@@ -1,21 +1,23 @@
/* eslint-disable prettier/prettier */
import { useState, useEffect } from 'react';

interface ProgressBarProps {
initialTimeRemaining: number; // Time remaining in seconds from the server
totalTime: number; // Total auction time in seconds (86,400 seconds)
isLoading: boolean;
initialTimeRemaining: number | '';
totalTime: number;
}

const ProgressBar: React.FC<ProgressBarProps> = ({
initialTimeRemaining,
totalTime,
isLoading,
}) => {
const [timeRemaining, setTimeRemaining] = useState(initialTimeRemaining);
const [timeRemaining, setTimeRemaining] = useState<number>(
typeof initialTimeRemaining === 'number' ? initialTimeRemaining : 0
);

useEffect(() => {
// Reset timeRemaining when initialTimeRemaining changes
setTimeRemaining(initialTimeRemaining);
setTimeRemaining(
typeof initialTimeRemaining === 'number' ? initialTimeRemaining : 0
);

const interval = setInterval(() => {
setTimeRemaining((prevTime) => {
Expand All @@ -30,10 +32,8 @@ const ProgressBar: React.FC<ProgressBarProps> = ({
return () => clearInterval(interval);
}, [initialTimeRemaining]);

// Calculate progress bar width
const progressBarWidth = (timeRemaining / totalTime) * 100;

// Format time as HH:MM:SS
const hours = Math.floor(timeRemaining / 3600);
const minutes = Math.floor((timeRemaining % 3600) / 60);
const seconds = timeRemaining % 60;
Expand Down
2 changes: 1 addition & 1 deletion src/components/heart/queries.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ export const useGetPreRegisterHeart = () => {
const getPreRegisterHeart = async (): Promise<PreRegisterAuction[]> => {
const response = await httpClient.get(`${API_END_POINT.PRE_REGISTER}/history`);

return response.data;
return response.data.items;
};

const { data: preRegisterHeartList } = useSuspenseQuery({
Expand Down
19 changes: 6 additions & 13 deletions src/components/home/CategoryList.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { useNavigate } from 'react-router-dom';
import { categories } from '../../constants/categories';
import { useNavigate } from 'react-router-dom';

const CategoryItem = ({ name, icon }: { name: string; icon: string }) => {
const navigate = useNavigate();
Expand All @@ -8,25 +8,18 @@ const CategoryItem = ({ name, icon }: { name: string; icon: string }) => {
navigate(`/product/list?category=${name}`);
};
return (
<li
className="flex flex-col items-center gap-3 cursor-pointer"
onClick={onClickCategory}
>
<img
src={icon}
alt={`${name} μΉ΄ν…Œκ³ λ¦¬`}
className="sm:w-[7rem] sm:h-[7em] w-[4rem] h-[4rem] rounded-full bg-categoryColor sm:p-7 p-3"
/>
<div className="text-body2 text-gray1">{name}</div>
<li className='flex flex-col items-center gap-3 cursor-pointer' onClick={onClickCategory}>
<img src={icon} alt={`${name} μΉ΄ν…Œκ³ λ¦¬`} className='sm:w-[5rem] sm:h-[5em] w-[3rem] h-[3rem] rounded-full bg-categoryColor sm:p-4 p-2' />
<div className='text-body2 text-gray1'>{name}</div>
</li>
);
};

const CategoryList = () => {
return (
<ul className="flex flex-wrap items-center gap-5">
<ul className='flex flex-wrap items-center gap-5'>
{Object.values(categories).map((el) => (
<CategoryItem key={el.value} name={el.value} icon={el.icon} />
<CategoryItem key={el.value} name={el.code} icon={el.icon} />
))}
</ul>
);
Expand Down
46 changes: 14 additions & 32 deletions src/components/home/HomeAuctionItem.tsx
Original file line number Diff line number Diff line change
@@ -1,53 +1,35 @@
import { truncateText } from '@/utils/truncateText';
import type { PreRegisterAuction, RegisterAuction } from 'Auction';
import { useNavigate } from 'react-router-dom';
import ParticipantCount from '../common/atomic/ParticipantCount';
import MinPrice from '../common/atomic/MinPrice';

import LikeCount from '../common/atomic/LikeCount';
import MinPrice from '../common/atomic/MinPrice';
import ParticipantCount from '../common/atomic/ParticipantCount';
import TimeLabel from '../common/atomic/TimeLabel';
import { addDefaultImg } from '@/utils/addDefaultImg';
import { truncateText } from '@/utils/truncateText';
import { useNavigate } from 'react-router-dom';

type HomeAuctionItemProps<T> = T extends 'pre-register'
? { kind: 'pre-register'; auction: PreRegisterAuction }
: { kind: 'register'; auction: RegisterAuction };

const HomeAuctionItem = <T extends 'pre-register' | 'register'>({
kind,
auction,
}: HomeAuctionItemProps<T>) => {
const HomeAuctionItem = <T extends 'pre-register' | 'register'>({ kind, auction }: HomeAuctionItemProps<T>) => {
const navigate = useNavigate();
const handleClick = () =>
navigate(
kind === 'register'
? `/auctions/auction/${auction.id}`
: `/auctions/pre-auction/${auction.id}`,
);
const handleClick = () => navigate(kind === 'register' ? `/auctions/auction/${auction.id}` : `/auctions/pre-auction/${auction.id}`);
const name = truncateText(auction.name);

return (
<figure
className="flex flex-col min-w-[11rem] gap-2 border rounded text-body2 cursor-pointer"
aria-label={kind}
onClick={handleClick}
>
<div className="relative">
<img
src={auction.cdnPath}
alt={`${kind}_이미지`}
className="object-cover w-full h-[10rem] rounded-t"
/>
<figure className='flex flex-col min-w-[11rem] gap-2 border rounded text-body2 cursor-pointer' aria-label={kind} onClick={handleClick}>
<div className='relative'>
<img src={auction.cdnPath} alt={`${kind}_이미지`} className='object-cover w-full h-[10rem] rounded-t' onError={addDefaultImg} />
{kind === 'register' && <TimeLabel time={auction.timeRemaining} />}
</div>
<figcaption className="flex flex-col gap-2 p-2">
<div aria-label={`${kind}_이름`} className="text-gray1">
<figcaption className='flex flex-col gap-2 p-2'>
<div aria-label={`${kind}_이름`} className='text-gray1'>
{name}
</div>
<div>
<MinPrice price={auction.minPrice} />
{kind === 'register' ? (
<ParticipantCount count={auction.participantCount} />
) : (
<LikeCount count={auction.likeCount} />
)}
{kind === 'register' ? <ParticipantCount count={auction.participantCount} /> : <LikeCount count={auction.likeCount} />}
</div>
</figcaption>
</figure>
Expand Down
18 changes: 5 additions & 13 deletions src/components/home/HomeRegisterBtn.tsx
Original file line number Diff line number Diff line change
@@ -1,31 +1,23 @@
import ROUTERS from '@/constants/route';
import { AiOutlinePlus } from 'react-icons/ai';
import ROUTERS from '@/constants/route';
import { useNavigate } from 'react-router-dom';

const HomeRegisterBtn = ({ isScrolled }: { isScrolled: boolean }) => {
const navigate = useNavigate();

return (
<div className="sticky z-50 flex justify-end w-full cursor-pointer bottom-2 right-2">
<div className='sticky bottom-0 right-0 z-50 flex justify-end w-full cursor-pointer'>
<button
className={`relative text-center text-white rounded-full bg-cheeseYellow transition-all text-button duration-500 h-14 p-4 ${
isScrolled ? 'w-full' : 'w-14'
}`}
onClick={() => navigate(ROUTERS.REGISTER)}
>
<span
className={`absolute inset-0 flex justify-center items-center transition-opacity duration-500 ${
isScrolled ? 'opacity-100' : 'opacity-0'
}`}
>
<span className={`absolute inset-0 flex justify-center items-center transition-opacity duration-500 ${isScrolled ? 'opacity-100' : 'opacity-0'}`}>
경맀 λ“±λ‘ν•˜κΈ°
</span>
<span
className={`absolute inset-0 flex justify-center items-center transition-opacity duration-500 ${
isScrolled ? 'opacity-0' : 'opacity-100'
}`}
>
<AiOutlinePlus aria-label="plus_icon" className="size-7" />
<span className={`absolute inset-0 flex justify-center items-center transition-opacity duration-500 ${isScrolled ? 'opacity-0' : 'opacity-100'}`}>
<AiOutlinePlus aria-label='plus_icon' className='size-7' />
</span>
</button>
</div>
Expand Down
Loading