Skip to content

Commit

Permalink
feat: 수영장 즐겨찾기 기능을 구현
Browse files Browse the repository at this point in the history
* feat: 수영장 즐겨찾기 api 연결

* feat: invalidateQueries 처리

* design: 검색 결과 없을 시 띄워주는 글 색깔 수정
  • Loading branch information
wokbjso authored Aug 2, 2024
1 parent c6165cd commit 0a86901
Show file tree
Hide file tree
Showing 9 changed files with 85 additions and 16 deletions.
2 changes: 1 addition & 1 deletion apis/fetch-data.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ import { cookies } from 'next/headers';
export async function fetchData<T>(
endpoint: string,
method: 'GET' | 'POST' | 'PUT' | 'DELETE' | 'PATCH',
body?: '',
body?: object,
): Promise<T> {
const BASE_URL = process.env.NEXT_PUBLIC_SERVER_URL;
const url = `${BASE_URL}${endpoint}`;
Expand Down
10 changes: 10 additions & 0 deletions app/api/pool/favorite/route.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import { NextRequest, NextResponse } from 'next/server';

import { fetchData } from '@/apis/fetch-data';

export async function PUT(request: NextRequest) {
const body = (await request.json()) as Promise<{ poolId: number }>;
const data = await fetchData<string>(`/pool/favorite`, 'PUT', body);

return NextResponse.json(data);
}
2 changes: 1 addition & 1 deletion app/api/pool/search/initial/route.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import { SearchPoolInitialResultResponse } from '@/features/record';

export async function GET() {
const data = await fetchData<SearchPoolInitialResultResponse>(
'/pool/search/initial',
`/pool/search/initial`,
'GET',
);

Expand Down
3 changes: 2 additions & 1 deletion components/atoms/icons/star-icon-fill.tsx
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
export function StarIconFill() {
export function StarIconFill(props?: React.SVGProps<SVGSVGElement>) {
return (
<svg
xmlns="http://www.w3.org/2000/svg"
width="24"
height="24"
viewBox="0 0 24 24"
fill="none"
{...props}
>
<path
fillRule="evenodd"
Expand Down
10 changes: 8 additions & 2 deletions components/atoms/icons/star-icon.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,12 @@
export function StarIcon() {
export function StarIcon(props?: React.SVGProps<SVGSVGElement>) {
return (
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" fill="none">
<svg
xmlns="http://www.w3.org/2000/svg"
width="24"
height="24"
fill="none"
{...props}
>
<path
fill="#37383C"
fillOpacity=".28"
Expand Down
2 changes: 1 addition & 1 deletion features/record/apis/use-search-pool.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ async function searchPool(nameQuery: string, cursorId: unknown) {

export default function useSearchPool(nameQuery: string) {
const { data, ...queryInfo } = useInfiniteQuery<SearchPoolResultResponse>({
queryKey: ['getSearchMovie', nameQuery],
queryKey: ['useSearchPool', nameQuery],
queryFn: ({ pageParam = undefined }) => searchPool(nameQuery, pageParam),
initialPageParam: undefined,
getNextPageParam: (lastPage) =>
Expand Down
26 changes: 26 additions & 0 deletions features/record/apis/use-toggle-favorite.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import { useMutation, useQueryClient } from '@tanstack/react-query';

async function toggleFavorite(poolId: number) {
const res = await fetch(`/api/pool/favorite`, {
method: 'PUT',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({ poolId }),
});

return res.json();
}

export function useToggleFavorite() {
const queryClient = useQueryClient();
return useMutation({
mutationFn: toggleFavorite,
onSuccess: () => {
// eslint-disable-next-line @typescript-eslint/no-floating-promises
queryClient.invalidateQueries({
queryKey: ['useSearchPoolInitial'],
});
},
});
}
44 changes: 34 additions & 10 deletions features/record/components/molecules/pool-search-result-element.tsx
Original file line number Diff line number Diff line change
@@ -1,13 +1,14 @@
'use client';

import { useSetAtom } from 'jotai';
import { forwardRef } from 'react';
import { forwardRef, useEffect, useState } from 'react';
import { useFormContext } from 'react-hook-form';

import { StarIcon, StarIconFill } from '@/components/atoms';
import { css, cx } from '@/styled-system/css';
import { flex } from '@/styled-system/patterns';

import { useToggleFavorite } from '../../apis/use-toggle-favorite';
import { isPoolSearchPageModalOpen } from '../../store';

interface PoolSearchListElementProps {
Expand All @@ -23,28 +24,46 @@ export const PoolSearchResultElement = forwardRef<
HTMLLIElement,
PoolSearchListElementProps
>(({ poolId, name, address, isFavorite, className, assignRef }, ref) => {
const [favorite, setFavorite] = useState(isFavorite);

const { setValue } = useFormContext();
const setIsPoolSearchPageModalOpen = useSetAtom(isPoolSearchPageModalOpen);
const { mutate: toggleFavorite, isPending } = useToggleFavorite();

const handleElementClick = (name: string, poolId: number) => {
setValue('poolId', poolId);
setValue('poolName', name);
setIsPoolSearchPageModalOpen({ isOpen: false, jumpDirection: 'backward' });
setIsPoolSearchPageModalOpen({
isOpen: false,
jumpDirection: 'backward',
});
};

const handleStarIconClick = () => {
toggleFavorite(poolId);
if (!isPending) setFavorite((prev) => !prev);
};

PoolSearchResultElement.displayName = 'PoolSearchResultElement';
useEffect(() => {
if (favorite !== isFavorite) {
setFavorite(isFavorite);
}
}, [isFavorite]);

return (
<li
ref={assignRef ? ref : undefined}
className={cx(listStyles, className)}
onClick={() => handleElementClick(name, poolId)}
>
<div className={textStyles.layout}>
<li ref={assignRef ? ref : undefined} className={cx(listStyles, className)}>
<div
className={textStyles.layout}
onClick={() => handleElementClick(name, poolId)}
>
<span className={textStyles.name}>{name}</span>
<span className={textStyles.address}>{address}</span>
</div>
{isFavorite ? <StarIconFill /> : <StarIcon />}
{favorite ? (
<StarIconFill onClick={handleStarIconClick} />
) : (
<StarIcon onClick={handleStarIconClick} />
)}
</li>
);
});
Expand Down Expand Up @@ -72,5 +91,10 @@ const textStyles = {
textStyle: 'body2.normal',
fontWeight: 400,
color: 'text.alternative',
whiteSpace: 'nowrap',
overflow: 'hidden',
textOverflow: 'ellipsis',
}),
};

PoolSearchResultElement.displayName = 'PoolSearchResultElement';
Original file line number Diff line number Diff line change
Expand Up @@ -63,9 +63,11 @@ const textStyles = {
textStyle: 'heading6',
fontWeight: 500,
marginBottom: '4px',
color: 'text.normal',
}),
subNoResult: css({
textStyle: 'body2.normal',
fontWeight: 400,
color: 'text.alternative',
}),
};

0 comments on commit 0a86901

Please sign in to comment.