Skip to content
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
20 changes: 6 additions & 14 deletions components/features/my-page/recommend/RecommendedBookList.tsx
Original file line number Diff line number Diff line change
@@ -1,10 +1,9 @@
"use client";

import { useEffect, useState } from "react";
import { useQuery } from "@tanstack/react-query";
import { Skeleton } from "@/components/ui/skeleton";
import { RecommendedBookListItem } from "./RecommendedBookListItem";
import { fetchRecommendBooks } from "@/lib/mock/my-page-recommend-book";
import type { MyPageRecommendBooksResponse } from "@/types/myPage";
import { getRecommendBooks, MY_PAGE_QUERY_KEYS } from "@/lib/api/endpoints/myPage";

function ListItemSkeleton() {
return (
Expand All @@ -25,17 +24,10 @@ function ListItemSkeleton() {
}

export function RecommendedBookList() {
const [booksData, setBooksData] =
useState<MyPageRecommendBooksResponse | null>(null);
const [isLoading, setIsLoading] = useState(true);
const [isError, setIsError] = useState(false);

useEffect(() => {
fetchRecommendBooks()
.then((data) => setBooksData(data))
.catch(() => setIsError(true))
.finally(() => setIsLoading(false));
}, []);
const { data: booksData, isLoading, isError } = useQuery({
queryKey: MY_PAGE_QUERY_KEYS.recommendBooks,
queryFn: getRecommendBooks,
});

if (isError) {
return (
Expand Down
20 changes: 6 additions & 14 deletions components/features/my-page/recommend/RecommendedHomePostList.tsx
Original file line number Diff line number Diff line change
@@ -1,10 +1,9 @@
"use client";

import { useEffect, useState } from "react";
import { useQuery } from "@tanstack/react-query";
import { Skeleton } from "@/components/ui/skeleton";
import { RecommendedHomePostListItem } from "./RecommendedHomePostListItem";
import { fetchRecommendHomePosts } from "@/lib/mock/my-page-recommend-home";
import type { MyPageRecommendContentsResponse } from "@/types/myPage";
import { getRecommendContents, MY_PAGE_QUERY_KEYS } from "@/lib/api/endpoints/myPage";

function ListItemSkeleton() {
return (
Expand All @@ -21,17 +20,10 @@ function ListItemSkeleton() {
}

export function RecommendedHomePostList() {
const [postsData, setPostsData] =
useState<MyPageRecommendContentsResponse | null>(null);
const [isLoading, setIsLoading] = useState(true);
const [isError, setIsError] = useState(false);

useEffect(() => {
fetchRecommendHomePosts()
.then((data) => setPostsData(data))
.catch(() => setIsError(true))
.finally(() => setIsLoading(false));
}, []);
const { data: postsData, isLoading, isError } = useQuery({
queryKey: MY_PAGE_QUERY_KEYS.recommendContents,
queryFn: getRecommendContents,
});

if (isError) {
return (
Expand Down
94 changes: 47 additions & 47 deletions components/features/my-page/recommend/RecommendedSection.tsx
Original file line number Diff line number Diff line change
@@ -1,20 +1,18 @@
"use client";

import { useEffect, useState } from "react";
import Link from "next/link";
import { ArrowRight } from "lucide-react";
import { useQuery } from "@tanstack/react-query";
import { Skeleton } from "@/components/ui/skeleton";
import { RecommendedHomePostCard } from "./RecommendedHomePostCard";
import { RecommendedVideoCard } from "./RecommendedVideoCard";
import { RecommendedBookCard } from "./RecommendedBookCard";
import { fetchRecommendHomePosts } from "@/lib/mock/my-page-recommend-home";
import { fetchRecommendVideos } from "@/lib/mock/my-page-recommend-video";
import { fetchRecommendBooks } from "@/lib/mock/my-page-recommend-book";
import type {
MyPageRecommendContentsResponse,
MyPageRecommendYoutubeResponse,
MyPageRecommendBooksResponse,
} from "@/types/myPage";
import {
getRecommendContents,
getRecommendYoutube,
getRecommendBooks,
MY_PAGE_QUERY_KEYS,
} from "@/lib/api/endpoints/myPage";

function SubSectionHeader({ title, href }: { title: string; href: string }) {
return (
Expand Down Expand Up @@ -58,47 +56,37 @@ function BookCardSkeleton() {
}

export function RecommendedSection() {
const [homePostsData, setHomePostsData] =
useState<MyPageRecommendContentsResponse | null>(null);
const [videosData, setVideosData] =
useState<MyPageRecommendYoutubeResponse | null>(null);
const [booksData, setBooksData] =
useState<MyPageRecommendBooksResponse | null>(null);
const [isLoading, setIsLoading] = useState(true);
const [isError, setIsError] = useState(false);
const {
data: homePostsData,
isLoading: homeLoading,
isError: homeError,
} = useQuery({
queryKey: MY_PAGE_QUERY_KEYS.recommendContents,
queryFn: getRecommendContents,
});

useEffect(() => {
Promise.all([
fetchRecommendHomePosts(4),
fetchRecommendVideos(4),
fetchRecommendBooks(4),
])
.then(([postsData, vidsData, bks]) => {
setHomePostsData(postsData);
setVideosData(vidsData);
setBooksData(bks);
})
.catch(() => setIsError(true))
.finally(() => setIsLoading(false));
}, []);
const {
data: videosData,
isLoading: videosLoading,
isError: videosError,
} = useQuery({
queryKey: MY_PAGE_QUERY_KEYS.recommendYoutube,
queryFn: getRecommendYoutube,
});

const {
data: booksData,
isLoading: booksLoading,
isError: booksError,
} = useQuery({
queryKey: MY_PAGE_QUERY_KEYS.recommendBooks,
queryFn: getRecommendBooks,
});

const homePosts = homePostsData?.contents ?? [];
const videos = videosData?.videos ?? [];
const books = booksData?.books ?? [];

if (isError) {
return (
<section>
<h2 className="mb-3 text-base font-semibold text-foreground">
사용자 맞춤 추천
</h2>
<p className="text-sm text-muted-foreground">
불러오는 중 오류가 발생했습니다.
</p>
</section>
);
}

return (
<section>
<h2 className="mb-3 text-base font-semibold text-foreground">
Expand All @@ -108,12 +96,16 @@ export function RecommendedSection() {
<div className="space-y-8">
<div>
<SubSectionHeader title="홈 추천 글" href="/my-page/recommend/home" />
{isLoading ? (
{homeLoading ? (
<div className="grid grid-cols-2 gap-3 sm:grid-cols-3 lg:grid-cols-4">
{Array.from({ length: 4 }).map((_, i) => (
<CardSkeleton key={i} />
))}
</div>
) : homeError ? (
<p className="text-sm text-muted-foreground">
불러오는 중 오류가 발생했습니다.
</p>
) : !homePostsData?.isPersonalized ? (
<p className="text-sm text-muted-foreground">
{homePostsData?.message ??
Expand All @@ -135,12 +127,16 @@ export function RecommendedSection() {
title="추천 유튜브"
href="/my-page/recommend/video"
/>
{isLoading ? (
{videosLoading ? (
<div className="grid grid-cols-2 gap-3 sm:grid-cols-3 lg:grid-cols-4">
{Array.from({ length: 4 }).map((_, i) => (
<CardSkeleton key={i} />
))}
</div>
) : videosError ? (
<p className="text-sm text-muted-foreground">
불러오는 중 오류가 발생했습니다.
</p>
) : !videosData?.isPersonalized ? (
<p className="text-sm text-muted-foreground">
{videosData?.message ?? "아직 추천할 영상이 부족해요. 더 많은 글을 읽어보세요!"}
Expand All @@ -160,12 +156,16 @@ export function RecommendedSection() {

<div>
<SubSectionHeader title="추천 서적" href="/my-page/recommend/book" />
{isLoading ? (
{booksLoading ? (
<div className="grid grid-cols-2 gap-3 sm:grid-cols-3 lg:grid-cols-4">
{Array.from({ length: 4 }).map((_, i) => (
<BookCardSkeleton key={i} />
))}
</div>
) : booksError ? (
<p className="text-sm text-muted-foreground">
불러오는 중 오류가 발생했습니다.
</p>
) : !booksData?.isPersonalized ? (
<p className="text-sm text-muted-foreground">
{booksData?.message ?? "아직 추천할 도서가 부족해요. 더 많은 글을 읽어보세요!"}
Expand Down
20 changes: 6 additions & 14 deletions components/features/my-page/recommend/RecommendedVideoList.tsx
Original file line number Diff line number Diff line change
@@ -1,10 +1,9 @@
"use client";

import { useEffect, useState } from "react";
import { useQuery } from "@tanstack/react-query";
import { Skeleton } from "@/components/ui/skeleton";
import { RecommendedVideoListItem } from "./RecommendedVideoListItem";
import { fetchRecommendVideos } from "@/lib/mock/my-page-recommend-video";
import type { MyPageRecommendYoutubeResponse } from "@/types/myPage";
import { getRecommendYoutube, MY_PAGE_QUERY_KEYS } from "@/lib/api/endpoints/myPage";

function ListItemSkeleton() {
return (
Expand All @@ -20,17 +19,10 @@ function ListItemSkeleton() {
}

export function RecommendedVideoList() {
const [videosData, setVideosData] =
useState<MyPageRecommendYoutubeResponse | null>(null);
const [isLoading, setIsLoading] = useState(true);
const [isError, setIsError] = useState(false);

useEffect(() => {
fetchRecommendVideos()
.then((data) => setVideosData(data))
.catch(() => setIsError(true))
.finally(() => setIsLoading(false));
}, []);
const { data: videosData, isLoading, isError } = useQuery({
queryKey: MY_PAGE_QUERY_KEYS.recommendYoutube,
queryFn: getRecommendYoutube,
});

if (isError) {
return (
Expand Down
34 changes: 34 additions & 0 deletions lib/api/endpoints/myPage.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
import { apiClient } from "../client";
import type { ApiResponse } from "@/types/api";
import type {
MyPageRecommendContentsResponse,
MyPageRecommendYoutubeResponse,
MyPageRecommendBooksResponse,
} from "@/types/myPage";

export const MY_PAGE_QUERY_KEYS = {
recommendContents: ["myPage", "recommendContents"] as const,
recommendYoutube: ["myPage", "recommendYoutube"] as const,
recommendBooks: ["myPage", "recommendBooks"] as const,
};

export async function getRecommendContents(): Promise<MyPageRecommendContentsResponse> {
const res = await apiClient.get<ApiResponse<MyPageRecommendContentsResponse>>(
"/recommend/contents",
);
return res.data.data;
}

export async function getRecommendYoutube(): Promise<MyPageRecommendYoutubeResponse> {
const res = await apiClient.get<ApiResponse<MyPageRecommendYoutubeResponse>>(
"/recommend/youtube",
);
return res.data.data;
}

export async function getRecommendBooks(): Promise<MyPageRecommendBooksResponse> {
const res = await apiClient.get<ApiResponse<MyPageRecommendBooksResponse>>(
"/recommend/books",
);
return res.data.data;
}
Loading
Loading