Skip to content

Commit

Permalink
Merge pull request #22 from b-and-it/feat/21
Browse files Browse the repository at this point in the history
Feat/21 Infinite Scrolling, Promotion View 완료
  • Loading branch information
jpark0506 authored Jul 5, 2024
2 parents 90b16fd + 6268c3e commit e9f8dea
Show file tree
Hide file tree
Showing 7 changed files with 229 additions and 126 deletions.
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
import React, {CSSProperties} from 'react'
import HashLoader from 'react-spinners/HashLoader'
type Props = {
isLoading: boolean
text: string;
isLoading: boolean;
}

const override: CSSProperties = {
Expand All @@ -11,12 +12,12 @@ const override: CSSProperties = {
};

const Loading = (props: Props) => {
const { isLoading } = props
const { isLoading, text } = props
return (
<div className='flex flex-col items-center justify-items-center w-full bg-system-background my-24'>
<HashLoader color={'#1FDA00'} loading={isLoading} size={150} />
<div className='text-primary text-plg text-center font-bold mt-5'>
공연을 등록 중입니다.
{text}
</div>
</div>
)
Expand Down
169 changes: 82 additions & 87 deletions src/main/components/search/search.tsx
Original file line number Diff line number Diff line change
@@ -1,98 +1,93 @@
import React,{useEffect, useState} from 'react'
import React, { useEffect, useRef, useState, useCallback } from 'react'
import SearchBox from './searchbox';
import SearchResult from './searchresult';
import { axiosAPI } from '../../../axios';
import { PromotionCard } from '../../../promotion/types/common';
import Loading from '../../../common/loading';
type Props = {}

type Result = {
promotion_id : number,
img : string | undefined,
band_name : string,
title : string,
location : string,
date : string,
price : string,
like : boolean,
like_num : number
}

const exampleDataList :Result[] = [
{
promotion_id: 0,
img: undefined,
band_name: "밴드 이름 1",
title: "공연 Title 1",
location: "위치 1",
date: "2024.06.01 토",
price: "40,000₩",
like: true,
like_num: 32
},
{
promotion_id: 1,
img: undefined,
band_name: "밴드 이름 2",
title: "공연 Title 2",
location: "위치 2",
date: "2024.06.15 토",
price: "50,000₩",
like: false,
like_num: 29
},
{
promotion_id: 2,
img: undefined,
band_name: "밴드 이름 3",
title: "공연 Title 3",
location: "위치 3",
date: "2024.07.01 일",
price: "30,000₩",
like: false,
like_num: 16
},
{
promotion_id: 3,
img: undefined,
band_name: "밴드 이름 4",
title: "공연 Title 4",
location: "위치 4",
date: "2024.07.20 일",
price: "45,000₩",
like: false,
like_num: 50
},
{
promotion_id: 4,
img: undefined,
band_name: "밴드 이름 5",
title: "공연 Title 5",
location: "위치 5",
date: "2024.08.05 월",
price: "35,000₩",
like: false,
like_num: 16
}
];

const Search = (props: Props) => {
const [query, setQuery] = useState("");
const [results, setResults] = useState(exampleDataList);
const [results, setResults] = useState<PromotionCard[]>([]);
const target = useRef<HTMLDivElement | null>(null);
const [isFetching, setIsFetching] = useState(false);
const [stop, setStop] = useState(false);
const [page, setPage] = useState(0); // 현재 페이지를 추적

useEffect(() => {
const filteredResults = exampleDataList.filter((result) => {
return result.title.toLowerCase().includes(query.toLowerCase())})

setResults(filteredResults)
console.log(filteredResults)
},[query])
// 검색 결과를 가져오는 함수
const fetchResults = useCallback(async () => {
if (isFetching || stop) return;// 이미 요청 중이거나 중지 상태이면 반환
setIsFetching(true);
try {
const res = query === ""
? await axiosAPI.get(`/api/promotions?currentPage=${page}`)
: await axiosAPI.get(`/api/promotions/search?currentPage=${page}&keyword=${encodeURIComponent(query)}`);
console.log(res)
const promotionResult = res.data.result.promotionList;
if (promotionResult.length === 0) {
setStop(true); // 더 이상 데이터가 없으면 중지 상태로 설정
}
else {
// 더 이상 데이터가 없는 경우 2
if(promotionResult.length > 0 && promotionResult.length < 10) {
setStop(true);
}
setResults((prevResults) => [...prevResults, ...res.data.result.promotionList]);
}
} catch (err) {
setStop(true); // 에러 발생 시 중지 상태로 설정
console.error(err);
} finally {
setIsFetching(false); // 요청 완료 후 isFetching 상태 변경
}
}, [query, page, stop]);

// 쿼리가 변경될 때 새로운 결과를 가져오기
useEffect(() => {
setResults([]);
setPage(0);
setStop(false);// 새 검색 시 정지 상태 해제
}, [query]);

return (
<div className='flex flex-col h-full w-full mt-5'>
<SearchBox value={query} onChange={(e:React.ChangeEvent<HTMLInputElement>) => setQuery(e.target.value)}/>
<SearchResult results={results}/>
</div>

)

// 페이지가 변경될 때 결과를 가져오기
useEffect(() => {
if (!stop) {
fetchResults();
}
}, [page, fetchResults, stop]);

// 무한 스크롤을 위해 타겟 요소를 감시
useEffect(() => {
const observer = new IntersectionObserver((entries) => {
if (entries[0].isIntersecting && !isFetching && !stop) {
setPage((prevPage) => prevPage + 1);
}
}, {
root: null,
rootMargin: '100px',
threshold: 1.0
});

if (target.current) {
observer.observe(target.current);
}

return () => {
if (target.current) {
observer.unobserve(target.current);
}
};
}, [isFetching, stop]);

return (
<div className='flex flex-col h-full w-full mt-5'>
<SearchBox value={query} onChange={(e: React.ChangeEvent<HTMLInputElement>) => setQuery(e.target.value)} />
<SearchResult results={results} />
<div ref={target} style={{ height: '1px' }}></div>
{isFetching && <Loading text={"공연을 등록중입니다."} isLoading={isFetching}/>}
</div>
)
}

export default Search
export default Search;
61 changes: 40 additions & 21 deletions src/main/components/search/searchcard.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import React from 'react'
import React, { useEffect } from 'react'
import calendar_icon from '../../img/calendar_icon.png';
import location_icon from '../../img/location_icon.png';
import checker_img from '../../img/checker_img.png';
Expand All @@ -7,42 +7,56 @@ import like_icon_false from '../../img/like_icon_false.png';
import simple_img from '../../img/simple_img.png';
import LikeButton from '../../../common/components/buttons/like_button';
import { useNavigate } from 'react-router-dom';


type Result = {
promotion_id : number,
img : string | undefined,
band_name : string,
title : string,
location : string,
date : string,
price : string,
like : boolean,
like_num : number
}
import { PromotionCard } from '../../../promotion/types/common';

type Props = {
result : Result
result: PromotionCard
}

const SearchCard : React.FC<Props> = (props: Props) => {

const navigate = useNavigate();
const WEEKDAY = ['일', '월', '화', '수', '목', '금', '토'];
const {
promotionId,
title,
team,
thumbnail,
date,
location,
startTime,
endTime,
entranceFee,
writer: {name: writer_name, nickname: writer_nickname, profileImg: writer_profileImg},
} = props.result

const { img, band_name, title, location, date, price, like, like_num } = props.result
const convertStringToDate = (time: string) => {
const timeArr = time.split(':');
let hour = String(parseInt(timeArr[0]));
let minute = String(parseInt(timeArr[1]));
if (hour.length === 1) {
hour = '0' + hour;
}
if (minute.length === 1) {
minute = '0' + minute;
}

return `${hour}:${minute}`
}

const onClick = () => {
navigate(`/promotion/${props.result.promotion_id}`);
navigate(`/promotion/${promotionId}`);
}
return (
<div onClick={onClick} className="min-w-full mx-auto min-h-1/4 bg-white rounded-lg shadow-md overflow-hidden relative">
<div className="relative">
<div className="h-40 w-full flex items-center justify-center">
<img src={simple_img} alt="checker" className="h-full w-full object-cover" />
<img src={thumbnail === '' ? checker_img : thumbnail} alt="checker" className="h-full w-full object-cover" />
</div>
<LikeButton like={like} like_num={like_num}/>
{/* <LikeButton like={true} like_num={1}/> */}
</div>
<div className="pt-2 px-10px">
<h2 className="text-pxs text-text-plain">{band_name}</h2>
<h2 className="text-pxs text-text-plain">{team}</h2>
<p className="text-plg text-text-plain">{title}</p>
</div>
<div className="">
Expand All @@ -63,8 +77,13 @@ const SearchCard : React.FC<Props> = (props: Props) => {
<div className="flex flex-row items-center">
<img src={calendar_icon} alt="calendar" className="w-4 h-4 mr-1"/>
<div className="text-text-plain text-pxs">{date}</div>
<div>&nbsp;</div>
<div className="text-text-plain text-pxs">{WEEKDAY[new Date(date).getDay()]}</div>
<div>&nbsp;</div>
<div className="text-text-plain text-pxs">{`${convertStringToDate(startTime)}~`}</div>
<div className="text-text-plain text-pxs">{convertStringToDate(endTime)}</div>
</div>
<div className="text-plg text-primary">{price}</div>
<div className="text-plg text-primary">{`${entranceFee}₩`}</div>
</div>
</div>
</div>
Expand Down
16 changes: 3 additions & 13 deletions src/main/components/search/searchresult.tsx
Original file line number Diff line number Diff line change
@@ -1,26 +1,16 @@
import React from 'react'
import SearchCard from './searchcard'
type Result = {
promotion_id : number,
img : string | undefined,
band_name : string,
title : string,
location : string,
date : string,
price : string,
like : boolean,
like_num : number
}
import { PromotionCard } from '../../../promotion/types'


type Props = {
results : Result []
results : PromotionCard[]
}


const searchresult : React.FC<Props>= (props: Props) => {

const result : Result[]= props.results
const result : PromotionCard[]= props.results
return (
<div className='flex flex-col w-full space-y-5 items-center justify-center'>
{
Expand Down
4 changes: 2 additions & 2 deletions src/promotion/promotionscreate.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import Result from './components/promotionview/result';
import store from '../store/store'
import axiosSecureAPI from '../axios'
import { convertMerdian } from '../utils/time'
import Loading from './components/loading'
import Loading from '../common/loading'
type Props = {}

type Step = {
Expand Down Expand Up @@ -112,7 +112,7 @@ const PromotionCreate = (props: Props) => {
</div>
<div className="flex flex-col w-full">
<FormNav title={title} currentStepIndex={currentStepIndex}/>
{!isSuccess ? isLoading ? <Loading isLoading={isLoading}/> :
{!isSuccess ? isLoading ? <Loading text={"공연을 등록중입니다."} isLoading={isLoading}/> :
step.length !== 0 ?
step[currentStepIndex].element : <div> No Element </div>
:
Expand Down
Loading

0 comments on commit e9f8dea

Please sign in to comment.