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

FE-38 ✨ 마이페이지 내 에피그램 목록 조회 #126

Merged
merged 6 commits into from
Jul 30, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
10 changes: 10 additions & 0 deletions public/none-epi.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
93 changes: 93 additions & 0 deletions public/spinner.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
8 changes: 8 additions & 0 deletions src/apis/queries.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
import { createQueryKeyStore } from '@lukemorales/query-key-factory';
import { GetUserRequestType } from '@/schema/user';
import { GetMonthlyEmotionLogsRequestType } from '@/schema/emotion';
import { GetEpigramsParamsType } from '@/schema/epigrams';
import { getMe, getUser } from './user';
import getMonthlyEmotionLogs from './emotion';
import getEpigrams from './getEpigrams';

const quries = createQueryKeyStore({
user: {
Expand All @@ -21,6 +23,12 @@ const quries = createQueryKeyStore({
queryFn: () => getMonthlyEmotionLogs(request),
}),
},
epigrams: {
getEpigrams: (request: GetEpigramsParamsType) => ({
queryKey: ['getEpigrams', request],
queryFn: () => getEpigrams(request),
}),
},
});

export default quries;
7 changes: 7 additions & 0 deletions src/hooks/useGetEpigrams.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
import quries from '@/apis/queries';
import { GetEpigramsParamsType } from '@/schema/epigrams';
import { useQuery } from '@tanstack/react-query';

const useGetEpigrams = (requset: GetEpigramsParamsType) => useQuery(quries.epigrams.getEpigrams(requset));

export default useGetEpigrams;
4 changes: 2 additions & 2 deletions src/pageLayout/MypageLayout/EmotionMonthlyLogs.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -32,9 +32,9 @@ export default function EmotionMonthlyLogs({ userId }: EmotionMonthlyLogsProps)
const { data: monthlyEmotionLogs = [] } = useMonthlyEmotionLogs(emotionRequest);

return (
<>
<div className='pb-[110px]'>
<Calendar currentDate={currentDate} setCurrentDate={setCurrentDate} monthlyEmotionLogs={monthlyEmotionLogs} />
<Chart monthlyEmotionLogs={monthlyEmotionLogs} />
</>
</div>
);
}
78 changes: 78 additions & 0 deletions src/pageLayout/MypageLayout/MyContent.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
import { useEffect, useState } from 'react';
import useGetEpigrams from '@/hooks/useGetEpigrams';
import MyEpigrams from '@/user/ui-content/MyEpigrams';
import Image from 'next/image';
import { useToast } from '@/components/ui/use-toast';
import { EpigramsResponse } from '@/types/epigram.types';
import spinner from '../../../public/spinner.svg';

interface MyContentProps {
userId: number;
}

export default function MyContent({ userId }: MyContentProps) {
const limit = 3;
const [cursor, setCursor] = useState(0);
const [epigrams, setEpigrams] = useState<EpigramsResponse>({ totalCount: 0, nextCursor: null, list: [] });
const [isLoadingMore, setIsLoadingMore] = useState(false);
const { toast } = useToast();

const epigramsRequest = {
limit,
cursor,
writerId: userId,
};
const { data, isLoading, error } = useGetEpigrams(epigramsRequest);

useEffect(() => {
if (data && data.list.length > 0) {
setEpigrams((prev) => ({
totalCount: data.totalCount,
nextCursor: data.nextCursor,
list: [...prev.list, ...data.list],
}));
setIsLoadingMore(false);
}
}, [data]);

const handleMoreEpigramLoad = () => {
if (epigrams.nextCursor !== null) {
setCursor(epigrams.nextCursor);
setIsLoadingMore(true);
}
};

if (isLoading && !isLoadingMore) {
return <Image src={spinner} alt='로딩중' width={200} height={200} />;
}

if (error) {
toast({
description: error.message,
className: 'border-state-error text-state-error font-semibold',
});
}

return (
<div className='flex flex-col w-full lg:max-w-[640px] md:max-w-[640px] gap-12'>
<div className='inline-flex gap-6'>
<button type='button' className='text-black-600 font-semibold text-2xl'>
내 에피그램({epigrams.totalCount})
</button>
<button type='button' className='text-neutral-400 font-semibold text-2xl'>
내 댓글(0)
</button>
</div>
<div className='w-full py-[36px]'>
<div className='flex flex-col gap-[48px]'>
<MyEpigrams epigrams={epigrams.list} totalCount={epigrams.totalCount} onMoreEpigramLoad={handleMoreEpigramLoad} />
{isLoadingMore && (
<div className='w-full flex items-center justify-center lg:mt-[70px] md:mt-[50px]'>
<Image src={spinner} alt='로딩중' width={200} height={200} />
</div>
)}
</div>
</div>
</div>
);
}
9 changes: 2 additions & 7 deletions src/pageLayout/MypageLayout/MyPageLayout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import UserInfo from '@/types/user';
import EmotionMonthlyLogs from '@/pageLayout/MypageLayout/EmotionMonthlyLogs';
import Profile from '@/user/ui-profile/Profile';
import { useRouter } from 'next/navigation';
import MyContent from './MyContent';

export default function MyPageLayout() {
const { data, isLoading, isError }: { data: UserInfo | undefined; isLoading: boolean; isError: boolean } = useMeQuery();
Expand Down Expand Up @@ -34,13 +35,7 @@ export default function MyPageLayout() {
<EmotionMonthlyLogs userId={data.id} />
</div>
<div className='bg-background-100 flex flex-col items-center w-full py-[100px]'>
<div className='flex flex-col w-full lg:max-w-[640px] md:max-w-[640px] gap-12'>
<div className='inline-flex gap-6'>
<p className='text-neutral-400 font-semibold text-2xl'>내 에피그램(19)</p>
<p className='text-black-600 font-semibold text-2xl'>내 댓글(110)</p>
</div>
<div className='w-full'>댓글 컴포넌트</div>
</div>
<MyContent userId={data.id} />
</div>
</div>
);
Expand Down
2 changes: 1 addition & 1 deletion src/schema/epigrams.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ export const GetEpigramsParams = z.object({

export const GetEpigramsResponse = z.object({
totalCount: z.number(),
nextCursor: z.number(),
nextCursor: z.number().nullable(),
list: z.array(
z.object({
likeCount: z.number(),
Expand Down
17 changes: 17 additions & 0 deletions src/types/epigram.types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,3 +22,20 @@ export interface PatchCommentRequest {
isPrivate: boolean;
content: string;
}

export interface Epigram {
writerId: number;
id: number;
likeCount: number;
tags: { id: number; name: string }[];
referenceUrl: string;
referenceTitle: string;
author: string;
content: string;
}

export interface EpigramsResponse {
totalCount: number;
nextCursor: number | null;
list: Epigram[];
}
113 changes: 113 additions & 0 deletions src/user/ui-content/MyEpigrams.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,113 @@
import React from 'react';
import Image from 'next/image';
import { useRouter } from 'next/router';
import { Button } from '@/components/ui/button';
import { Epigram } from '@/types/epigram.types';
import NONE_EPI from '../../../public/none-epi.svg';

const sizeStyles = {
xs: 'w-[286px] max-h-[132px]',
sm: 'sm:w-[312px] sm:max-h-[152px]',
md: 'md:w-[384px] md:max-h-[180px]',
lg: 'lg:w-[540px] lg:max-h-[160px]',
xl: 'xl:w-[640px] xl:max-h-[196px]',
};

const textSizeStyles = {
xs: 'text-xs',
sm: 'sm:text-sm',
md: 'md:text-base',
lg: 'lg:text-xl',
xl: 'xl:text-2xl',
};

interface MyEpigramProps {
epigrams: Epigram[];
totalCount: number;
onMoreEpigramLoad: () => void;
}

function MyEpigrams({ epigrams, totalCount, onMoreEpigramLoad }: MyEpigramProps) {
const router = useRouter();

// 에피그램 등록 페이지 이동
const handleAddEpigram = () => {
router.push('/addEpigram');
};

// 에피그램 상세 페이지 이동
const handleEpigramClick = (epigramId: number) => {
router.push(`/epigram/${epigramId}`);
};

const handleKeyDown = (event: React.KeyboardEvent<HTMLDivElement>, epigramId: number) => {
if (event.key === 'Enter' || event.key === ' ') {
handleEpigramClick(epigramId);
}
};

return totalCount > 0 ? (
<div className='flex flex-col gap-[48px]'>
{epigrams.map((epigram) => (
<div
key={epigram.id}
onClick={() => handleEpigramClick(epigram.id)}
onKeyDown={(event) => handleKeyDown(event, epigram.id)}
role='button'
tabIndex={0}
className={`relative flex-col justify-start items-end gap-2 inline-flex cursor-pointer ${sizeStyles.xs} ${sizeStyles.sm} ${sizeStyles.md} ${sizeStyles.lg} ${sizeStyles.xl}`}
>
<div className='w-full p-[22px] bg-white rounded-[14.67px] shadow border border-zinc-100 flex-col justify-start items-start flex relative overflow-hidden'>
<div className='absolute inset-0 bg-stripes w-full h-full'></div>
<div className='relative w-full z-10 flex flex-col justify-start items-start flex-1'>
<div className='self-stretch flex-col justify-start items-start gap-2 flex'>
<div
className={`self-stretch ${textSizeStyles.xs} ${textSizeStyles.sm} ${textSizeStyles.md} ${textSizeStyles.lg} ${textSizeStyles.xl} text-neutral-700 font-normal font-iropkeBatang leading-normal`}
>
{epigram.content}
</div>
<div
className={`self-stretch ${textSizeStyles.xs} ${textSizeStyles.sm} ${textSizeStyles.md} ${textSizeStyles.lg} ${textSizeStyles.xl} text-right text-slate-400 font-normal font-iropkeBatang leading-normal`}
>
- {epigram.author} -
</div>
</div>
</div>
</div>
<div className='justify-start items-start gap-2 inline-flex'>
{epigram.tags.map((tag) => (
<div
key={tag.id}
className={`text-right ${textSizeStyles.xs} ${textSizeStyles.sm} ${textSizeStyles.md} ${textSizeStyles.lg} ${textSizeStyles.xl} text-slate-400 font-normal font-iropkeBatang leading-normal`}
>
#{tag.name}
</div>
))}
</div>
</div>
))}
{totalCount > epigrams.length && (
<div className='w-full flex items-center justify-center'>
<Button className='text-slate-400 border border-slate-300 rounded-[100px] py-[12px] px-[20px]' onClick={onMoreEpigramLoad}>
+ 더보기
</Button>
</div>
)}
</div>
) : (
<div className='flex flex-col gap-4 justify-center items-center'>
<Image src={NONE_EPI} alt='돋보기아이콘' width={144} height={144} />
<div className='flex flex-col gap-[48px] justify-center items-center'>
<div className='text-center'>
<p>아직 작성한 에피그램이 없어요!</p>
<p>에피그램을 작성하고 감정을 공유해보세요.</p>
</div>
<Button className='px-[18px] py-3 rounded-[100px] border border-neutral-200 justify-center items-center gap-1' onClick={handleAddEpigram}>
에피그램 만들기
</Button>
</div>
</div>
);
}

export default MyEpigrams;
Loading