107 lines
3.5 KiB
TypeScript
107 lines
3.5 KiB
TypeScript
"use client";
|
|
|
|
/**
|
|
* QuizzesCTA — the way into /quizzes, in two shapes:
|
|
*
|
|
* - "tile": a card that leads the search page's "Start here" row.
|
|
* - "banner": a full-width row for the homepage, under sādhana / vāṇī.
|
|
*
|
|
* Renders nothing until there is at least one published quiz; with
|
|
* `pendingOnly` it also hides once every quiz has been finished, so the
|
|
* homepage only nudges when there's something left to do.
|
|
*/
|
|
|
|
import { useEffect, useState } from "react";
|
|
import Link from "next/link";
|
|
import { ArrowUpRight, ChevronRight, ClipboardCheck } from "lucide-react";
|
|
import { useAuth } from "@/components/auth/auth-provider";
|
|
import { quiz, type QuizCard } from "@/lib/quiz";
|
|
import { cn } from "@/lib/utils";
|
|
|
|
export function QuizzesCTA({
|
|
variant = "banner",
|
|
pendingOnly = false,
|
|
className,
|
|
}: {
|
|
variant?: "banner" | "tile";
|
|
pendingOnly?: boolean;
|
|
className?: string;
|
|
}) {
|
|
const { user } = useAuth();
|
|
const [cards, setCards] = useState<QuizCard[] | null>(null);
|
|
|
|
useEffect(() => {
|
|
let cancelled = false;
|
|
quiz
|
|
.list()
|
|
.then((c) => {
|
|
if (!cancelled) setCards(c);
|
|
})
|
|
.catch(() => {
|
|
/* CTA simply doesn't render */
|
|
});
|
|
return () => {
|
|
cancelled = true;
|
|
};
|
|
// Refetch on sign-in/out: progress is per user.
|
|
}, [user?.id]);
|
|
|
|
if (!cards?.length) return null;
|
|
|
|
if (pendingOnly && cards.every((c) => c.my.state === "completed")) {
|
|
return null;
|
|
}
|
|
|
|
const tagline = "Test your understanding of chapters, lectures and more";
|
|
|
|
if (variant === "tile") {
|
|
// Matches the search page's FeaturedTile so it sits in that grid as an
|
|
// equal: same padding, icon chip, title scale and corner arrow.
|
|
return (
|
|
<Link
|
|
href="/quizzes"
|
|
className={cn(
|
|
"group relative flex flex-col h-full rounded-2xl bg-card ring-1 ring-foreground/10 hover:ring-foreground/25 hover:shadow-md transition-all p-6",
|
|
className,
|
|
)}
|
|
>
|
|
<div className="flex items-start justify-between">
|
|
<div className="flex items-center justify-center size-11 rounded-xl bg-primary/12 text-primary">
|
|
<ClipboardCheck className="size-5" />
|
|
</div>
|
|
<ArrowUpRight className="size-4 text-muted-foreground/60 group-hover:text-foreground transition-colors" />
|
|
</div>
|
|
<h3 className="mt-5 font-[family-name:var(--font-title)] font-semibold text-2xl sm:text-[1.6rem] lg:text-[1.35rem] leading-tight text-foreground">
|
|
Quizzes
|
|
</h3>
|
|
<p className="mt-2 text-sm text-muted-foreground leading-relaxed flex-1">
|
|
{tagline}
|
|
</p>
|
|
</Link>
|
|
);
|
|
}
|
|
|
|
return (
|
|
<Link
|
|
href="/quizzes"
|
|
className={cn(
|
|
"group flex items-center gap-3.5 rounded-2xl border border-border bg-card p-4 text-left transition-colors hover:border-primary/40 hover:bg-muted/40",
|
|
className,
|
|
)}
|
|
>
|
|
<span className="inline-flex size-12 shrink-0 items-center justify-center rounded-full bg-primary/10 text-primary">
|
|
<ClipboardCheck className="size-6" />
|
|
</span>
|
|
<div className="min-w-0 flex-1">
|
|
<p className="text-base font-semibold leading-snug text-foreground">
|
|
Quizzes
|
|
</p>
|
|
<p className="truncate text-sm leading-snug text-muted-foreground">
|
|
{tagline}
|
|
</p>
|
|
</div>
|
|
<ChevronRight className="size-5 shrink-0 text-muted-foreground transition-transform group-hover:translate-x-0.5 group-hover:text-foreground" />
|
|
</Link>
|
|
);
|
|
}
|