Skip to content

Commit

Permalink
Merge pull request #130 from YAPP-Github/feature/DEV-53
Browse files Browse the repository at this point in the history
[ Feature/dev 53 ] 워크북 card list 불러오기 mock api 연결 및 테스트
  • Loading branch information
Happhee authored Aug 3, 2024
2 parents ab14dc7 + 9da3b93 commit f0766cf
Show file tree
Hide file tree
Showing 50 changed files with 1,018 additions and 263 deletions.
6 changes: 6 additions & 0 deletions next.config.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,12 @@ const nextConfig = {
"oopy.lazyrockets.com",
"eehhqckznniu25210545.cdn.ntruss.com",
"storage.mrblog.net",
"d3ex4vlh373syu.cloudfront.net",
"www.fig1.kr",
"cdn.maily.so",
"maily.so",
"pension.inmostadvisor.com",
"localhost",
],
},
webpack: (config, context) => {
Expand Down
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@
"react": "^18",
"react-dom": "^18",
"react-hook-form": "^7.51.5",
"react-intersection-observer": "^9.13.0",
"react-lottie-player": "^2.0.0",
"shadcn-ui": "^0.8.0",
"tailwind-merge": "^2.3.0",
Expand Down
16 changes: 16 additions & 0 deletions pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

5 changes: 5 additions & 0 deletions public/assets/icon/skeletonFewLogo.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
45 changes: 45 additions & 0 deletions src/common/hooks/useSusbscribeWorkbook.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
import { toast } from "@shared/components/ui/use-toast";
import { SUBSCRIBE_USER_ACTIONS } from "@subscription/constants/subscribe";
import { subscribeWorkbookOptions } from "@subscription/remotes/postSubscriptionQueryOptions";
import { useMutation } from "@tanstack/react-query";

export default function useSusbscribeWorkbook() {
const { mutate: subscribeWorkbook } = useMutation(subscribeWorkbookOptions());
const postSubscribeWorkbook = ({
workbookId,
handleSucess,
}: {
workbookId: string;
handleSucess?: () => void;
}) => {
try {
subscribeWorkbook(
{ workbookId },
{
onSuccess: () => {
toast({
title: SUBSCRIBE_USER_ACTIONS.SUBSCRIBE_SUCCESS,
});
if (handleSucess) handleSucess();
},
onError: (error) => {
let errorMessage = SUBSCRIBE_USER_ACTIONS.SUBSCRIBE_FAIL;
if (error && error.data && error.data.message) {
errorMessage = error.data.message || errorMessage;
}
toast({
title: errorMessage,
});
},
},
);
} catch (error) {
console.error("catch error", error);

toast({
title: SUBSCRIBE_USER_ACTIONS.SUBSCRIBE_FAIL,
});
}
};
return { postSubscribeWorkbook };
}
8 changes: 4 additions & 4 deletions src/common/types/category.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
export type CategoryInfo = {
export type CategoryServerInfo = {
code: number;
name: string;
};

export type CategoryInfoList = {
categories: CategoryInfo[];
export type CategoryClientInfo = {
code: number;
name: string;
};
6 changes: 4 additions & 2 deletions src/main/components/ArticleCard/index.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
"use client";
import { ArticleClientInfo } from "@main/types/article";
import { useRouter } from "next/navigation";
import { title } from "process";
import ArticleCardDetail from "../ArticleCardDetail";

export default function ArticleCard({
Expand All @@ -10,6 +9,7 @@ export default function ArticleCard({
thumbnail,
viewCount,
category,
title,
content,
withWorkbookList,
}: ArticleClientInfo) {
Expand All @@ -28,8 +28,10 @@ export default function ArticleCard({
<ArticleCardDetail.Title title={title} />
<ArticleCardDetail.Description content={content} />
<ArticleCardDetail.Thumbnail thumbnail={thumbnail} />
<ArticleCardDetail.WithWorkbookList
withWorkbookList={withWorkbookList}
/>
</article>
<ArticleCardDetail.WithWorkbookList withWorkbookList={withWorkbookList} />
</section>
);
}
50 changes: 31 additions & 19 deletions src/main/components/ArticleCardDetail/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,11 @@ import Link from "next/link";
import EyeIcon from "public/assets/icon/eye.svg";
import { ReactNode } from "react";

const RootComponentWrapper = ({ children }: { children: ReactNode }) => (
<section className="border-b-[0.5px] border-text-gray2 px-[20px] py-[26px]">
{children}
</section>
);
const TopComponentWrapper = ({ children }: { children: ReactNode }) => (
<div className="flex justify-between pb-[6px]">{children}</div>
);
Expand Down Expand Up @@ -36,7 +41,9 @@ const CategoryTag = ({ category }: Pick<ArticleClientInfo, "category">) => (

const Title = ({ title }: Pick<ArticleClientInfo, "title">) => (
<header className="mb-[4px]">
<h3 className="h3-bold text-text-gary1 text-ellipsis">{title}</h3>
<h3 className="h3-bold text-text-gary1 line-clamp-1 text-ellipsis">
{title}
</h3>
</header>
);
const Description = ({ content }: Pick<ArticleClientInfo, "content">) => (
Expand All @@ -58,27 +65,32 @@ const Thumbnail = ({ thumbnail }: Pick<ArticleClientInfo, "thumbnail">) => (
const WithWorkbookList = ({
withWorkbookList,
}: Pick<ArticleClientInfo, "withWorkbookList">) => (
<footer className="grid grid-cols-[130px_1fr] items-center py-[14px]">
<span className="sub3-medium text-text-gray1">
이 아티클이 포함된 학습지
</span>
<ul className="flex justify-end gap-[4px]">
{withWorkbookList.map(({ id, title }) => (
<li
key={`with-workbook-${id}`}
className={cn(
"sub3-semibold bg-background1 px-[5px] py-[2px] text-text-gray1",
"line-clamp-1 rounded border-[0.5px] border-text-gray2",
)}
>
<Link href={`/workbook/${id}`}>{title}</Link>
</li>
))}
</ul>
</footer>
<>
{withWorkbookList && (
<footer className="grid grid-cols-[130px_1fr] items-center py-[14px]">
<span className="sub3-medium text-text-gray1">
이 아티클이 포함된 학습지
</span>
<ul className="flex justify-end gap-[4px]">
{withWorkbookList.map(({ id, title }) => (
<li
key={`with-workbook-${id}`}
className={cn(
"sub3-semibold bg-background1 px-[5px] py-[2px] text-text-gray1",
"line-clamp-1 rounded border-[0.5px] border-text-gray2",
)}
>
<Link href={`/workbook/${id}`}>{title}</Link>
</li>
))}
</ul>
</footer>
)}
</>
);

const ArticleCardDetail = {
RootComponentWrapper,
TopComponentWrapper,
WriterProfile,
ViewCount,
Expand Down
47 changes: 47 additions & 0 deletions src/main/components/ArticleCardDetailSkeleton/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
import { Skeleton } from "@shared/components/ui/skeleton";
import SkeletonFewLogo from "public/assets/icon/skeletonFewLogo.svg";
const WriterProfileSkeleton = () => {
return <Skeleton className="h-[30px] w-[30px] rounded-full" />;
};
const ViewCountSkeleton = () => {
return (
<div className="flex items-center gap-[5px]">
<Skeleton className="h-[9px] w-[40px]" />
</div>
);
};
const CategoryTagSkeleton = () => {
return <Skeleton className="mb-[8px] h-[18px] w-[20%] py-[2px]" />;
};
const TitleSkeleton = () => {
return <Skeleton className="mb-[4px] h-[27px] w-[30%] bg-text-gray3" />;
};

const DescriptionSkeleton = () => {
const skeletonItems = new Array(4).fill(null);
return (
<div className="mb-[20px] flex flex-col gap-[3px]">
{skeletonItems.map((_, index) => (
<Skeleton key={index} className="h-[23px] w-full bg-text-gray3" />
))}
</div>
);
};

const ThumbnailSkeleton = () => {
return (
<Skeleton className="flex h-[170px] w-full items-center justify-center rounded bg-[#C4C4C4]">
<SkeletonFewLogo width={93} height={93} />
</Skeleton>
);
};

const ArticleCardDetailSkeleton = {
WriterProfileSkeleton,
ViewCountSkeleton,
CategoryTagSkeleton,
TitleSkeleton,
DescriptionSkeleton,
ThumbnailSkeleton,
};
export default ArticleCardDetailSkeleton;
99 changes: 32 additions & 67 deletions src/main/components/ArticleCardList/index.tsx
Original file line number Diff line number Diff line change
@@ -1,75 +1,40 @@
import { CategoryInfo } from "@common/types/category";
import ArticleCardModel from "@main/models/ArticleCardModel";
import { ArticleServerInfo } from "@main/types/article";
import { CategoryClientInfo } from "@common/types/category";
import { ENTIRE_CATEGORY } from "@main/constants";
import { getArticlesWithCategoryInfiniteQueryOptions } from "@main/remotes/getArticlesWithCategoryInfiniteQueryOptions";
import { useInfiniteQuery } from "@tanstack/react-query";
import { useEffect } from "react";
import { useInView } from "react-intersection-observer";
import ArticleCard from "../ArticleCard";
import ArticleCardListSkeleton from "../ArticleCardListSkeleton";
export default function ArticleCardList({ code }: Partial<CategoryClientInfo>) {
const { data, fetchNextPage, isLoading } = useInfiniteQuery({
...getArticlesWithCategoryInfiniteQueryOptions({
code: code !== undefined ? code : ENTIRE_CATEGORY,
}),
});
const { ref, inView } = useInView();

const data: ArticleServerInfo[] = [
{
id: 1,
writer: {
id: 1,
name: "안나포",
url: "https://storage.mrblog.net/files/dosi_draw/a3NgiDGW2H3NhsYp1Qp3RuWNzUx9sg8L2yyooYqF.jpg",
},
title: "ETF(상장 지수 펀드)란? 모르면 손해라고?",
content:
"오늘날 전동 킥보드의 인기는 누가 뭐래도 전동 킥보드 공유 서비스가 등장하면서부터입니다. 공유 서비스가 등장하기 이전 전동 킥보드는 주로 레저용으로 사용되다가 공유형 전동킥보드 서비스가 등장하면서 이동 수단으로 사용되기 시작합니다.Y",
problemIds: [1, 2, 3],
category: "경제",
createdAt: "2024-07-25T14:32:27.310368",
views: 1,
mainImageUrl:
"https://storage.mrblog.net/files/dosi_draw/a3NgiDGW2H3NhsYp1Qp3RuWNzUx9sg8L2yyooYqF.jpg",

includedWorkbooks: [
{
id: 1,
title: "사소한 것들의 역사",
},
{
id: 2,
title: "인모스트 경제레터",
},
],
},
{
id: 2,
writer: {
id: 1,
name: "몰티즈",
url: "https://storage.mrblog.net/files/dosi_draw/a3NgiDGW2H3NhsYp1Qp3RuWNzUx9sg8L2yyooYqF.jpg",
useEffect(
function fetchNextArticles() {
if (inView && !data?.isLast) {
fetchNextPage();
}
},
mainImageUrl:
"https://storage.mrblog.net/files/dosi_draw/a3NgiDGW2H3NhsYp1Qp3RuWNzUx9sg8L2yyooYqF.jpg",
title: "ETF(상장 지수 펀드)란? 모르면 손해라고?",
content: "ECONOMY",
problemIds: [1, 2, 3],
category: "경제",
createdAt: "2024-07-25T14:32:27.310368",
views: 1,
includedWorkbooks: [
{
id: 1,
title: "사소한 것들의 역사",
},
{
id: 2,
title: "인모스트 경제레터",
},
],
},
];

export default function ArticleCardList({ code, name }: CategoryInfo) {
const articleCardModel = new ArticleCardModel({
initArticleCardServerList: data,
});
[inView],
);
if (isLoading || !data?.articles) {
return <ArticleCardListSkeleton />;
}

const lastIdx = data?.articles.length - 1;
return (
<>
{articleCardModel.articleCardList().map((data, idx) => (
<ArticleCard {...data} key={`article-card-${idx}`} />
<section>
{data?.articles.map((data, idx) => (
<div key={`article-card-${idx}`}>
{idx === lastIdx && <div ref={ref} className="h-[2px]" />}
<ArticleCard {...data} />
</div>
))}
</>
</section>
);
}
23 changes: 23 additions & 0 deletions src/main/components/ArticleCardListSkeleton/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
import ArticleCardDetail from "../ArticleCardDetail";
import ArticleCardDetailSkeleton from "../ArticleCardDetailSkeleton";

export default function ArticleCardListSkeleton() {
return (
<>
{new Array(2).fill(null).map((_, idx) => (
<ArticleCardDetail.RootComponentWrapper
key={`article-card-skeleton-${idx}`}
>
<ArticleCardDetail.TopComponentWrapper>
<ArticleCardDetailSkeleton.WriterProfileSkeleton />
<ArticleCardDetailSkeleton.ViewCountSkeleton />
</ArticleCardDetail.TopComponentWrapper>
<ArticleCardDetailSkeleton.CategoryTagSkeleton />
<ArticleCardDetailSkeleton.TitleSkeleton />
<ArticleCardDetailSkeleton.DescriptionSkeleton />
<ArticleCardDetailSkeleton.ThumbnailSkeleton />
</ArticleCardDetail.RootComponentWrapper>
))}
</>
);
}
Loading

0 comments on commit f0766cf

Please sign in to comment.