feat(guide) : improve extra cards and navigation
CI / Verify (push) Successful in 1m25s
CI / Build immutable images and deploy (push) Failing after 50s

This commit is contained in:
2026-09-04 22:40:27 +07:00 Unverified
parent 5f74ce1de6
commit ced9b41737
16 changed files with 3780 additions and 224 deletions
+31 -60
View File
@@ -1,7 +1,6 @@
import Image from "next/image";
import Link from "next/link";
import { ArrowUpRightIcon, ChevronLeftIcon } from "lucide-react";
import { ViewTransition } from "react";
import { Badge } from "@/components/ui/badge";
import { Button } from "@/components/ui/button";
@@ -39,6 +38,7 @@ import {
talentTypeLabel,
} from "@/lib/guides/talents";
import { parseYouTubeUrl } from "@/lib/guides/youtube";
import { highlightTextSegments } from "@/lib/guides/highlights";
import { cn } from "@/lib/utils";
function hasSection(guide: PublicGuide, slug: string): boolean {
@@ -125,66 +125,44 @@ export function StructuredGuideShell({
<Link
href={`/${guide.slug}`}
className={cn(
"relative flex min-h-8 shrink-0 items-center whitespace-nowrap rounded-md px-3 text-sm outline-none focus-visible:ring-2 focus-visible:ring-ring",
"flex min-h-8 shrink-0 items-center gap-2 whitespace-nowrap rounded-md px-3 text-sm transition-colors outline-none focus-visible:ring-2 focus-visible:ring-ring",
!section
? "text-foreground"
? "bg-muted text-foreground"
: "text-muted-foreground hover:bg-muted hover:text-foreground",
)}
prefetch
>
{!section ? (
<ViewTransition
name={`guide-nav-active-${guide.id}`}
share="guide-nav-pill"
default="none"
>
<span aria-hidden="true" className="absolute inset-0 rounded-md bg-muted" />
</ViewTransition>
) : null}
<span className="relative flex items-center gap-2">
<Image
src={navigationIconPath("overview")!}
alt=""
width={20}
height={20}
className="size-5 object-contain"
/>
Overview
</span>
<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(
"relative flex min-h-8 shrink-0 items-center whitespace-nowrap rounded-md px-3 text-sm outline-none focus-visible:ring-2 focus-visible:ring-ring",
"flex min-h-8 shrink-0 items-center gap-2 whitespace-nowrap rounded-md px-3 text-sm transition-colors outline-none focus-visible:ring-2 focus-visible:ring-ring",
section === item.slug
? "text-foreground"
? "bg-muted text-foreground"
: "text-muted-foreground hover:bg-muted hover:text-foreground",
)}
prefetch
>
{section === item.slug ? (
<ViewTransition
name={`guide-nav-active-${guide.id}`}
share="guide-nav-pill"
default="none"
>
<span aria-hidden="true" className="absolute inset-0 rounded-md bg-muted" />
</ViewTransition>
{navigationIconPath(item.slug) ? (
<Image
src={navigationIconPath(item.slug)!}
alt=""
width={20}
height={20}
className="size-5 object-contain"
/>
) : null}
<span className="relative flex items-center gap-2">
{navigationIconPath(item.slug) ? (
<Image
src={navigationIconPath(item.slug)!}
alt=""
width={20}
height={20}
className="size-5 object-contain"
/>
) : null}
{item.title}
</span>
{item.title}
</Link>
))}
</ScrollableNav>
@@ -1029,9 +1007,7 @@ function ExtraSection({ extra }: { extra: PublicGuide["extras"][number] }) {
<Card
key={row.id}
className="overflow-hidden py-0"
style={row.backgroundColor && row.textColor
? { backgroundColor: row.backgroundColor, color: row.textColor }
: undefined}
style={row.backgroundColor ? { backgroundColor: row.backgroundColor } : undefined}
>
{row.imageUrl ? (
<div
@@ -1052,20 +1028,15 @@ function ExtraSection({ extra }: { extra: PublicGuide["extras"][number] }) {
</div>
) : null}
<CardHeader className="pt-5">
<CardTitle style={row.textColor ? { color: row.textColor } : undefined}>
{row.heading}
</CardTitle>
<CardDescription
className="whitespace-pre-wrap"
style={row.textColor ? { color: row.textColor } : undefined}
>
{row.body}
<CardTitle>{row.heading}</CardTitle>
<CardDescription className="whitespace-pre-wrap">
{highlightTextSegments(row.body, row.highlights).map((segment, index) => (
<span key={index} style={segment.color ? { color: segment.color } : undefined}>
{segment.text}
</span>
))}
</CardDescription>
{row.percent ? (
<Badge style={row.textColor ? { color: row.textColor } : undefined}>
{metric(row.percent)}
</Badge>
) : null}
{row.percent ? <Badge>{metric(row.percent)}</Badge> : null}
</CardHeader>
</Card>
))}