feat(overview) : add YouTube video section
This commit is contained in:
@@ -3,6 +3,7 @@ import Link from "next/link";
|
||||
import { ArrowUpRightIcon, ChevronLeftIcon } from "lucide-react";
|
||||
|
||||
import { Badge } from "@/components/ui/badge";
|
||||
import { Button } from "@/components/ui/button";
|
||||
import {
|
||||
Card,
|
||||
CardAction,
|
||||
@@ -33,6 +34,7 @@ import {
|
||||
talentRelationSymbol,
|
||||
talentTypeLabel,
|
||||
} from "@/lib/guides/talents";
|
||||
import { parseYouTubeUrl } from "@/lib/guides/youtube";
|
||||
import { cn } from "@/lib/utils";
|
||||
|
||||
function hasSection(guide: PublicGuide, slug: string): boolean {
|
||||
@@ -112,54 +114,51 @@ export function StructuredGuideShell({
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
<ScrollableNav
|
||||
ariaLabel={`เมนูคู่มือ ${guide.name}`}
|
||||
className="mx-auto min-h-11 w-full max-w-7xl rounded-xl border bg-card p-2"
|
||||
>
|
||||
<Link
|
||||
href={`/${guide.slug}`}
|
||||
className={cn(
|
||||
"flex min-h-8 shrink-0 items-center gap-2 whitespace-nowrap rounded-md px-3 text-sm outline-none focus-visible:ring-2 focus-visible:ring-ring",
|
||||
!section
|
||||
? "bg-muted text-foreground"
|
||||
: "text-muted-foreground hover:bg-muted hover:text-foreground",
|
||||
)}
|
||||
prefetch
|
||||
{section ? (
|
||||
<ScrollableNav
|
||||
ariaLabel={`เมนูคู่มือ ${guide.name}`}
|
||||
className="mx-auto min-h-11 w-full max-w-7xl rounded-xl border bg-card p-2"
|
||||
>
|
||||
<Image
|
||||
src={navigationIconPath("overview")!}
|
||||
alt=""
|
||||
width={20}
|
||||
height={20}
|
||||
className="size-5 object-contain"
|
||||
/>
|
||||
Overview
|
||||
</Link>
|
||||
{sections.map((item) => (
|
||||
<Link
|
||||
key={item.id}
|
||||
href={`/${guide.slug}/${item.slug}`}
|
||||
className={cn(
|
||||
"flex min-h-8 shrink-0 items-center gap-2 whitespace-nowrap rounded-md px-3 text-sm outline-none focus-visible:ring-2 focus-visible:ring-ring",
|
||||
section === item.slug
|
||||
? "bg-muted text-foreground"
|
||||
: "text-muted-foreground hover:bg-muted hover:text-foreground",
|
||||
)}
|
||||
href={`/${guide.slug}`}
|
||||
className="flex min-h-8 shrink-0 items-center gap-2 whitespace-nowrap rounded-md px-3 text-sm text-muted-foreground outline-none hover:bg-muted hover:text-foreground focus-visible:ring-2 focus-visible:ring-ring"
|
||||
prefetch
|
||||
>
|
||||
{navigationIconPath(item.slug) ? (
|
||||
<Image
|
||||
src={navigationIconPath(item.slug)!}
|
||||
alt=""
|
||||
width={20}
|
||||
height={20}
|
||||
className="size-5 object-contain"
|
||||
/>
|
||||
) : null}
|
||||
{item.title}
|
||||
<Image
|
||||
src={navigationIconPath("overview")!}
|
||||
alt=""
|
||||
width={20}
|
||||
height={20}
|
||||
className="size-5 object-contain"
|
||||
/>
|
||||
Overview
|
||||
</Link>
|
||||
))}
|
||||
</ScrollableNav>
|
||||
{sections.map((item) => (
|
||||
<Link
|
||||
key={item.id}
|
||||
href={`/${guide.slug}/${item.slug}`}
|
||||
className={cn(
|
||||
"flex min-h-8 shrink-0 items-center gap-2 whitespace-nowrap rounded-md px-3 text-sm outline-none focus-visible:ring-2 focus-visible:ring-ring",
|
||||
section === item.slug
|
||||
? "bg-muted text-foreground"
|
||||
: "text-muted-foreground hover:bg-muted hover:text-foreground",
|
||||
)}
|
||||
prefetch
|
||||
>
|
||||
{navigationIconPath(item.slug) ? (
|
||||
<Image
|
||||
src={navigationIconPath(item.slug)!}
|
||||
alt=""
|
||||
width={20}
|
||||
height={20}
|
||||
className="size-5 object-contain"
|
||||
/>
|
||||
) : null}
|
||||
{item.title}
|
||||
</Link>
|
||||
))}
|
||||
</ScrollableNav>
|
||||
) : null}
|
||||
</div>
|
||||
<main className="mx-auto w-full max-w-7xl p-4 sm:p-6 lg:p-10">
|
||||
{section ? (
|
||||
@@ -179,6 +178,10 @@ function GuideOverview({
|
||||
guide: PublicGuide;
|
||||
sections: ReturnType<typeof publicSections>;
|
||||
}) {
|
||||
const youtubeVideo = guide.youtubeUrl
|
||||
? parseYouTubeUrl(guide.youtubeUrl)
|
||||
: null;
|
||||
|
||||
return (
|
||||
<div className="flex flex-col gap-8">
|
||||
<div className="mx-auto flex max-w-3xl flex-col gap-3 text-center">
|
||||
@@ -186,55 +189,94 @@ function GuideOverview({
|
||||
<h1 className="font-heading text-4xl font-bold">{guide.name}</h1>
|
||||
<p className="text-lg font-semibold">{guide.overview}</p>
|
||||
</div>
|
||||
<div className="relative mx-auto aspect-square w-full max-w-3xl overflow-hidden rounded-xl border bg-muted">
|
||||
<Image
|
||||
src={guide.coverUrl}
|
||||
alt={`ไกด์ ${guide.name}`}
|
||||
fill
|
||||
priority
|
||||
className="object-cover"
|
||||
/>
|
||||
</div>
|
||||
{sections.length ? (
|
||||
<div className="grid gap-4 sm:grid-cols-2 lg:grid-cols-4">
|
||||
{sections.map((item) => (
|
||||
<Link key={item.id} href={`/${guide.slug}/${item.slug}`} prefetch>
|
||||
<Card className="h-full">
|
||||
<CardHeader>
|
||||
{navigationIconPath(item.slug) ? (
|
||||
<Image
|
||||
src={navigationIconPath(item.slug)!}
|
||||
alt=""
|
||||
width={40}
|
||||
height={40}
|
||||
className="size-10 object-contain"
|
||||
/>
|
||||
) : null}
|
||||
<CardTitle>
|
||||
{publicSectionTitle(item.slug, item.title)}
|
||||
</CardTitle>
|
||||
<CardDescription>เปิดดูคำแนะนำฉบับเต็ม</CardDescription>
|
||||
<CardAction>
|
||||
<ArrowUpRightIcon
|
||||
aria-hidden="true"
|
||||
className="size-5 text-primary"
|
||||
/>
|
||||
</CardAction>
|
||||
</CardHeader>
|
||||
</Card>
|
||||
</Link>
|
||||
))}
|
||||
<div className="grid items-start gap-4 lg:grid-cols-2">
|
||||
<div className="relative aspect-square w-full overflow-hidden rounded-xl border bg-muted">
|
||||
<Image
|
||||
src={guide.coverUrl}
|
||||
alt={`ไกด์ ${guide.name}`}
|
||||
fill
|
||||
priority
|
||||
className="object-cover"
|
||||
/>
|
||||
</div>
|
||||
) : (
|
||||
<Card>
|
||||
<CardHeader>
|
||||
<CardTitle>กำลังจัดทำเนื้อหา</CardTitle>
|
||||
<CardDescription>
|
||||
กลับมาตรวจสอบอีกครั้งหลังผู้ดูแลเพิ่มข้อมูล
|
||||
</CardDescription>
|
||||
</CardHeader>
|
||||
</Card>
|
||||
)}
|
||||
{sections.length ? (
|
||||
<nav
|
||||
aria-label={`เมนูคู่มือ ${guide.name}`}
|
||||
className="flex flex-col gap-3"
|
||||
>
|
||||
{sections.map((item) => (
|
||||
<Link
|
||||
key={item.id}
|
||||
href={`/${guide.slug}/${item.slug}`}
|
||||
className="rounded-xl outline-none focus-visible:ring-2 focus-visible:ring-ring"
|
||||
prefetch
|
||||
>
|
||||
<Card size="sm" className="transition-colors hover:bg-muted/50">
|
||||
<CardHeader>
|
||||
{navigationIconPath(item.slug) ? (
|
||||
<Image
|
||||
src={navigationIconPath(item.slug)!}
|
||||
alt=""
|
||||
width={32}
|
||||
height={32}
|
||||
className="size-8 object-contain"
|
||||
/>
|
||||
) : null}
|
||||
<CardTitle>
|
||||
{publicSectionTitle(item.slug, item.title)}
|
||||
</CardTitle>
|
||||
<CardDescription>เปิดดูคำแนะนำฉบับเต็ม</CardDescription>
|
||||
<CardAction>
|
||||
<ArrowUpRightIcon
|
||||
aria-hidden="true"
|
||||
className="size-5 text-primary"
|
||||
/>
|
||||
</CardAction>
|
||||
</CardHeader>
|
||||
</Card>
|
||||
</Link>
|
||||
))}
|
||||
</nav>
|
||||
) : (
|
||||
<Card>
|
||||
<CardHeader>
|
||||
<CardTitle>กำลังจัดทำเนื้อหา</CardTitle>
|
||||
<CardDescription>
|
||||
กลับมาตรวจสอบอีกครั้งหลังผู้ดูแลเพิ่มข้อมูล
|
||||
</CardDescription>
|
||||
</CardHeader>
|
||||
</Card>
|
||||
)}
|
||||
</div>
|
||||
{youtubeVideo ? (
|
||||
<section className="flex flex-col items-center gap-3">
|
||||
<div className="aspect-video w-full overflow-hidden rounded-xl border bg-muted">
|
||||
<iframe
|
||||
src={youtubeVideo.embedUrl}
|
||||
title={`วิดีโอไกด์ ${guide.name}`}
|
||||
loading="lazy"
|
||||
allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share"
|
||||
referrerPolicy="strict-origin-when-cross-origin"
|
||||
allowFullScreen
|
||||
className="size-full"
|
||||
/>
|
||||
</div>
|
||||
<Button
|
||||
variant="outline"
|
||||
render={
|
||||
<a
|
||||
href={youtubeVideo.watchUrl}
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
/>
|
||||
}
|
||||
nativeButton={false}
|
||||
>
|
||||
ดูวิดีโอต้นฉบับบน YouTube
|
||||
<ArrowUpRightIcon data-icon="inline-end" />
|
||||
</Button>
|
||||
</section>
|
||||
) : null}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user