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
9 changes: 3 additions & 6 deletions app/(main)/my-page/wrong-quizzes/page.tsx
Original file line number Diff line number Diff line change
@@ -1,11 +1,8 @@
import Link from "next/link";
import { ArrowLeft } from "lucide-react";
import { WrongQuizList } from "@/components/features/my-page/quizzes/WrongQuizList";
import { fetchMyWrongQuizzes } from "@/lib/mock/my-page-wrong-quizzes";

export default async function WrongQuizzesPage() {
const quizzes = await fetchMyWrongQuizzes();
import { WrongQuizListWrapper } from "@/components/features/my-page/quizzes/WrongQuizListWrapper";

export default function WrongQuizzesPage() {
return (
<div className="mx-auto max-w-5xl space-y-6 px-4 py-8 lg:px-8">
<Link
Expand All @@ -20,7 +17,7 @@ export default async function WrongQuizzesPage() {
틀린 퀴즈들
</h1>

<WrongQuizList quizzes={quizzes} />
<WrongQuizListWrapper />
</div>
);
}
50 changes: 2 additions & 48 deletions components/features/my-page/MyPage.tsx
Original file line number Diff line number Diff line change
@@ -1,37 +1,8 @@
"use client";

import Link from "next/link";
import { ChevronRight } from "lucide-react";
import { Skeleton } from "@/components/ui/skeleton";
import { ScrappedPostsSection } from "./scraps/ScrappedPostsSection";
import { WrongQuizSection } from "./quizzes/WrongQuizSection";

function SectionHeader({ title, href }: { title: string; href?: string }) {
return (
<div className="mb-4 flex items-center justify-between">
<h2 className="text-base font-semibold text-foreground">{title}</h2>
{href && (
<Link
href={href}
className="flex items-center gap-0.5 text-sm text-muted-foreground transition-colors hover:text-foreground"
>
전체 보기
<ChevronRight className="h-4 w-4" />
</Link>
)}
</div>
);
}

function SkeletonCards({ count = 4 }: { count?: number }) {
return (
<div className="grid grid-cols-2 gap-3 sm:grid-cols-3 lg:grid-cols-4">
{Array.from({ length: count }).map((_, i) => (
<Skeleton key={i} className="h-28 rounded-xl" />
))}
</div>
);
}
import { RecommendedSection } from "./recommend/RecommendedSection";

export default function MyPage() {
return (
Expand All @@ -52,24 +23,7 @@ export default function MyPage() {
<WrongQuizSection />

{/* 3. 추천 */}
<section className="space-y-8">
<SectionHeader title="추천" />

<div>
<SectionHeader title="홈 글" />
<SkeletonCards />
</div>

<div>
<SectionHeader title="유튜브" />
<SkeletonCards />
</div>

<div>
<SectionHeader title="서적" />
<SkeletonCards />
</div>
</section>
<RecommendedSection />
</div>
);
}
14 changes: 14 additions & 0 deletions components/features/my-page/quizzes/WrongQuizListItemSkeleton.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import { Skeleton } from "@/components/ui/skeleton";

export function WrongQuizListItemSkeleton() {
return (
<div className="flex gap-4 px-2 py-3">
<Skeleton className="aspect-[3/2] w-36 shrink-0 rounded-sm" />
<div className="flex flex-1 flex-col gap-2">
<Skeleton className="h-3.5 w-full rounded" />
<Skeleton className="h-3.5 w-4/5 rounded" />
<Skeleton className="mt-auto h-3 w-2/5 rounded" />
</div>
</div>
);
}
46 changes: 46 additions & 0 deletions components/features/my-page/quizzes/WrongQuizListWrapper.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
"use client";

import { useEffect, useState } from "react";
import { WrongQuizList } from "./WrongQuizList";
import { WrongQuizListItemSkeleton } from "./WrongQuizListItemSkeleton";
import { fetchMyWrongQuizzes } from "@/lib/mock/my-page-wrong-quizzes";
import type { MyPageQuizHistory } from "@/types/myPage";

export function WrongQuizListWrapper() {
const [quizzes, setQuizzes] = useState<MyPageQuizHistory[]>([]);
const [isLoading, setIsLoading] = useState(true);
const [isError, setIsError] = useState(false);

useEffect(() => {
fetchMyWrongQuizzes()
.then((data) => {
setQuizzes(data);
})
.catch(() => {
setIsError(true);
})
.finally(() => {
setIsLoading(false);
});
}, []);

if (isError) {
return (
<p className="text-sm text-muted-foreground">
불러오는 중 오류가 발생했습니다.
</p>
);
}

if (isLoading) {
return (
<div className="divide-y divide-border">
{Array.from({ length: 5 }).map((_, i) => (
<WrongQuizListItemSkeleton key={i} />
))}
</div>
);
}

return <WrongQuizList quizzes={quizzes} />;
}
21 changes: 16 additions & 5 deletions components/features/my-page/quizzes/WrongQuizSection.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,12 +11,19 @@ import type { MyPageQuizHistory } from "@/types/myPage";
export function WrongQuizSection() {
const [quizzes, setQuizzes] = useState<MyPageQuizHistory[]>([]);
const [isLoading, setIsLoading] = useState(true);
const [isError, setIsError] = useState(false);

useEffect(() => {
fetchMyWrongQuizzesPreview(4).then((data) => {
setQuizzes(data);
setIsLoading(false);
});
fetchMyWrongQuizzesPreview(4)
.then((data) => {
setQuizzes(data);
})
.catch(() => {
setIsError(true);
})
.finally(() => {
setIsLoading(false);
});
}, []);

return (
Expand All @@ -32,7 +39,11 @@ export function WrongQuizSection() {
</Link>
</div>

{isLoading ? (
{isError ? (
<p className="text-sm text-muted-foreground">
불러오는 중 오류가 발생했습니다.
</p>
) : isLoading ? (
<div className="grid grid-cols-2 gap-3 sm:grid-cols-3 lg:grid-cols-4">
{Array.from({ length: 4 }).map((_, i) => (
<div
Expand Down
43 changes: 43 additions & 0 deletions components/features/my-page/recommend/RecommendedBookCard.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
import Image from "next/image";
import { BookOpen } from "lucide-react";
import type { MyPageRecommendBook } from "@/types/myPage";

export function RecommendedBookCard({ book }: { book: MyPageRecommendBook }) {
const { title, authors, cover, url, publisher, publishedAt } = book;
const year = publishedAt.slice(0, 4);

return (
<a href={url} target="_blank" rel="noopener noreferrer">
<div className="group flex h-full flex-col overflow-hidden rounded-md border border-border bg-card">
<div className="relative aspect-[3/4] w-full overflow-hidden bg-muted">
{cover ? (
<Image
fill
src={cover}
alt={title}
className="object-cover transition-transform duration-200"
/>
) : (
<div className="flex h-full w-full items-center justify-center">
<BookOpen className="h-6 w-6 text-muted-foreground/30" />
</div>
)}
</div>

<div className="flex flex-1 flex-col gap-1 p-3">
<p className="line-clamp-2 flex-1 text-sm font-medium leading-snug tracking-[-0.01em] text-foreground">
{title}
</p>
<span className="text-xs text-muted-foreground">
{authors.join(", ")}
</span>
<span className="text-xs text-muted-foreground">
{publisher}
<span className="mx-1">·</span>
{year}
</span>
</div>
</div>
</a>
);
}
48 changes: 48 additions & 0 deletions components/features/my-page/recommend/RecommendedHomePostCard.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
import Link from "next/link";
import Image from "next/image";
import { FileText } from "lucide-react";
import { SourceLogo } from "@/components/features/home/SourceLogo";
import { formatDate } from "@/lib/utils";
import type { MyPageRecommendHomePost } from "@/types/myPage";

export function RecommendedHomePostCard({
post,
}: {
post: MyPageRecommendHomePost;
}) {
const { contentId, title, sourceName, thumbnail, date } = post;

return (
<Link href={`/home/${contentId}`}>
<div className="group flex h-full flex-col overflow-hidden rounded-md border border-border bg-card">
<div className="relative aspect-video w-full overflow-hidden bg-muted">
{thumbnail ? (
<Image
fill
src={thumbnail}
alt={title}
className="object-cover transition-transform duration-200"
/>
) : (
<div className="flex h-full w-full items-center justify-center">
<FileText className="h-6 w-6 text-muted-foreground/30" />
</div>
)}
</div>

<div className="flex flex-1 flex-col gap-1.5 p-3">
<div className="flex items-center gap-1.5">
<SourceLogo sourceName={sourceName} size={13} />
<span className="text-xs text-muted-foreground">{sourceName}</span>
</div>
<p className="line-clamp-2 flex-1 text-sm font-medium leading-snug tracking-[-0.01em] text-foreground">
{title}
</p>
<span className="text-xs text-muted-foreground">
{formatDate(date)}
</span>
</div>
</div>
</Link>
);
}
Loading
Loading