Skip to content

Commit

Permalink
Glue-258-검색페이지-api-연결 (#66)
Browse files Browse the repository at this point in the history
* feat: s3 도메인 추가

* fix: 페이지네이션 컴포넌트 오류 수정 및 type 분리

* feat: 검색 api 연결

* feat: 검색 아이콘에 검색창 구현

* feat: searchQuery핸들링 훅 구현

* feat: �searchParmas를 사용한 검색 기능 구현

* chore: 알람 컴포넌트 제거

* chore: 불필요한 더미데이터 삭제

* Update src/app/search/components/SearchFetcher/SearchContext.tsx

Co-authored-by: SeongMin Kim <[email protected]>

* feat: 변경된 DTO 반영 및 블로그 라우팅 로직 추가

* refactor: pr 리뷰 반영

* refactor: fetcher 구조 리팩토링

* fix: 검색어 변경시 api 재요청 및 컴포넌트 재렌더링 되지 않는 오류 수정

* feat: placeholder를 이용하여 SearhBox컴포넌트에 검색어 표시

---------

Co-authored-by: SeongMin Kim <[email protected]>
  • Loading branch information
yeyounging and Collection50 authored Jun 9, 2024
1 parent 515fa16 commit 2ca4205
Show file tree
Hide file tree
Showing 19 changed files with 323 additions and 181 deletions.
6 changes: 6 additions & 0 deletions src/api/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,3 +4,9 @@ export interface BaseResponse<T> {
message: string;
result: T;
}

export interface PaginationInfo {
hasNext: boolean;
isFirst: boolean;
isLast: boolean;
}
120 changes: 0 additions & 120 deletions src/app/lib/dummyData.js

This file was deleted.

44 changes: 29 additions & 15 deletions src/app/search/components/BlogList/BlogCard.tsx
Original file line number Diff line number Diff line change
@@ -1,23 +1,37 @@
import { getBackgroundImageById, getTitleById } from '@/app/lib/dummyData';
import Image from 'next/image';
import Link from 'next/link';

export default function BlogCard({ id }: { id: number }) {
const backgroundImageSrc = getBackgroundImageById(id);
const title = getTitleById(id);
interface BlogCardProps {
id: number;
photoUrl: string;
title: string;
description: string;
}

export default function BlogCard({
id,
photoUrl = '/tempImage/bg.jpeg',
title,
description,
}: BlogCardProps) {
return (
<article className="flex flex-col items-center justify-start border border-[#979696]">
{backgroundImageSrc && (
<div className="relative w-250 h-130">
<Image
src={backgroundImageSrc}
alt="bg"
layout="fill"
objectFit="cover"
/>
<Link
href={`/blog/${id}`}
className="flex flex-col items-center justify-start border border-[#979696] rounded-2 shadow-sm"
>
<div className="relative w-250 h-130 group">
<Image
src={photoUrl}
alt="bg"
layout="fill"
objectFit="cover"
className="transition-opacity duration-300 group-hover:opacity-50"
/>
<div className="absolute inset-0 flex items-center justify-center text-12 opacity-0 group-hover:opacity-100 transition-opacity duration-300 font-pretendard font-bold">
{description}
</div>
)}
</div>
<p className="font-semibold p-4">{title}</p>
</article>
</Link>
);
}
37 changes: 30 additions & 7 deletions src/app/search/components/BlogList/index.tsx
Original file line number Diff line number Diff line change
@@ -1,14 +1,37 @@
import { users } from '@/app/lib/dummyData';
import { generateId } from '@/utils';
import Pagination from '@/app/subscribe/components/Pagination';
import BlogCard from './BlogCard';
import { useSearchContext } from '../SearchFetcher/SearchContext';

export default function BlogList() {
interface BlogListProps {
currentPage: number;
onPageChange: (page: number) => void;
}

export default function BlogList({ currentPage, onPageChange }: BlogListProps) {
const { blogInfoItem } = useSearchContext();
const { blogInfoList, isFirst, isLast } = blogInfoItem;
return (
<div className="mt-30">
<div className="flex flex-wrap gap-x-60 gap-y-30 justify-center">
{users.map((user) => (
<BlogCard key={user.id} id={user.id} />
))}
<div>
<div className="mt-30">
<div className="flex flex-wrap gap-x-60 gap-y-30 justify-center">
{blogInfoList.map(({ id, title, background, description }) => (
<BlogCard
id={id}
key={generateId()}
title={title}
photoUrl={background}
description={description}
/>
))}
</div>
</div>
<Pagination
isFirst={isFirst}
isLast={isLast}
currentPage={currentPage}
onPageChange={onPageChange}
/>
</div>
);
}
58 changes: 45 additions & 13 deletions src/app/search/components/ContentList/index.tsx
Original file line number Diff line number Diff line change
@@ -1,20 +1,52 @@
import { posts } from '@/app/lib/dummyData';
import Story from '@/components/Story';
import Image from 'next/image';
import Pagination from '@/app/subscribe/components/Pagination';
import Link from 'next/link';
import { useSearchContext } from '../SearchFetcher/SearchContext';

interface ContentListProps {
currentPage: number;
onPageChange: (page: number) => void;
}

export default function ContentList({
currentPage,
onPageChange,
}: ContentListProps) {
const { postSearchItem } = useSearchContext();
const { postSearchList, isFirst, isLast } = postSearchItem;

export default function ContentList() {
return (
<div className="flex flex-col gap-30">
{posts.map(({ id, src, title, content }) => (
<div key={id} className="flex flex-row gap-10">
<div className="absolute w-150 h-150">
<Image src={src} alt={String(id)} layout="fill" objectFit="cover" />
</div>
<div className="relative pl-170">
<Story key={id} title={title} content={content} />
</div>
</div>
))}
<div>
<div className="flex flex-col gap-30">
{postSearchList.map(({ blogId, title, postId, preview }) => (
<Link
href={`/blog/${blogId}/post/${postId}`}
key={postId}
className="flex flex-row gap-10 cursor-pointer"
>
<div className="absolute w-150 h-150">
{/* TODO: photoUrl 적용 */}
<Image
src="/tempImage/4.jpg"
alt={String(postId)}
layout="fill"
objectFit="cover"
className="rounded-3"
/>
</div>
<div className="relative pl-170 py-1">
<Story key={postId} title={title} content={preview} />
</div>
</Link>
))}
</div>
<Pagination
isFirst={isFirst}
isLast={isLast}
currentPage={currentPage}
onPageChange={onPageChange}
/>
</div>
);
}
18 changes: 16 additions & 2 deletions src/app/search/components/SearchBox/index.tsx
Original file line number Diff line number Diff line change
@@ -1,16 +1,30 @@
'use client';

import { Input, Search } from '@/components/Common';
import { useSearch } from '@/hooks';
import { useSearchParams } from 'next/navigation';
import { useState } from 'react';

export default function SearchBox() {
const { searchQuery, handleInputChange, handleKeyDown } = useSearch();
const query = useSearchParams().get('query') as string;
const [placeholder, setPlaceholder] = useState(query);

return (
<article className="flex justify-center py-30">
<div
className="h-42 w-480 flex flex-row items-center justify-between
rounded-2xl gap-15 p-10 border"
>
<Input
className="h-full w-full bg-transparent"
placeholder="Search blog or contents"
className="h-full w-full bg-transparent placeholder-[#191919]"
placeholder={placeholder}
type="text"
value={searchQuery}
onChange={handleInputChange}
onKeyDown={handleKeyDown}
onFocus={() => setPlaceholder('')}
onBlur={() => setPlaceholder(query)}
/>
<Search />
</div>
Expand Down
15 changes: 15 additions & 0 deletions src/app/search/components/SearchFallback/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
'use client';

import { Button } from '@/components/Common';
import { useErrorBoundaryContext } from '@/react-utils/ErrorboundaryContext';
import { StrictPropsWithChildren } from '@/types';

export default function SearchFallback({ children }: StrictPropsWithChildren) {
const { error, resetErrorBoundary } = useErrorBoundaryContext();

if (error !== null) {
// TODO: 변경
return <Button onClick={() => resetErrorBoundary()}>다시 시도하기</Button>;
}
return children;
}
9 changes: 9 additions & 0 deletions src/app/search/components/SearchFetcher/SearchContext.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
'use client';

import { generateContext } from '@/react-utils';
import { SearchResponse } from './types';

export const [SearchResultProvider, useSearchContext] =
generateContext<SearchResponse>({
name: 'search-result-context',
});
8 changes: 8 additions & 0 deletions src/app/search/components/SearchFetcher/api.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
import { http } from '@/api';
import { SearchResponse } from './types';

export const getSearchResult = (page: number, keyword: string, size: number) =>
http.get<SearchResponse>({
url: '/posts/search',
params: { keyword, page, size },
});
25 changes: 25 additions & 0 deletions src/app/search/components/SearchFetcher/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import { ReactNode, useEffect } from 'react';
import { SearchResultProvider } from './SearchContext';
import { useSearchResult } from './quries';

interface SearchResultFetcherProps {
children: ReactNode;
page: number;
size: number;
keyword: string;
}

export function SearchResultFetcher({
children,
page,
size,
keyword,
}: SearchResultFetcherProps) {
const { data, refetch } = useSearchResult(page, size, keyword);

useEffect(() => {
refetch();
}, [keyword, refetch]);

return <SearchResultProvider {...data}>{children}</SearchResultProvider>;
}
Loading

0 comments on commit 2ca4205

Please sign in to comment.