2026-05-19 23:11:32 -05:00
|
|
|
import type { ReactNode } from "react";
|
|
|
|
|
import Link from "next/link";
|
|
|
|
|
import { ArrowUpRight, Info } from "lucide-react";
|
|
|
|
|
import { cn } from "@/lib/utils";
|
2026-05-24 22:38:26 -05:00
|
|
|
import {
|
|
|
|
|
sectionRail,
|
|
|
|
|
sectionTone,
|
|
|
|
|
toneBadge,
|
|
|
|
|
type BadgeTone,
|
|
|
|
|
type SectionToneKey,
|
|
|
|
|
} from "./tokens";
|
2026-05-19 23:11:32 -05:00
|
|
|
|
|
|
|
|
interface DashboardSectionProps {
|
|
|
|
|
title: string;
|
|
|
|
|
infoText?: string;
|
|
|
|
|
badge?: { text: string; tone: BadgeTone };
|
|
|
|
|
action?: { label: string; href: string };
|
|
|
|
|
children: ReactNode;
|
|
|
|
|
className?: string;
|
2026-05-24 22:38:26 -05:00
|
|
|
tone?: SectionToneKey;
|
2026-05-19 23:11:32 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Standard dashboard card section.
|
|
|
|
|
* Uses .surface-card so all color values come from design tokens —
|
|
|
|
|
* no hardcoded palette classes.
|
|
|
|
|
*/
|
|
|
|
|
export function DashboardSection({
|
|
|
|
|
title,
|
|
|
|
|
infoText,
|
|
|
|
|
badge,
|
|
|
|
|
action,
|
|
|
|
|
children,
|
|
|
|
|
className,
|
2026-05-24 22:38:26 -05:00
|
|
|
tone = "neutral",
|
2026-05-19 23:11:32 -05:00
|
|
|
}: DashboardSectionProps) {
|
|
|
|
|
return (
|
2026-05-24 22:38:26 -05:00
|
|
|
<section
|
|
|
|
|
className={cn(
|
|
|
|
|
"relative overflow-hidden rounded-xl border p-4 shadow-lush md:p-6",
|
|
|
|
|
sectionTone[tone],
|
|
|
|
|
className,
|
|
|
|
|
)}
|
|
|
|
|
>
|
|
|
|
|
<span
|
|
|
|
|
className={cn(
|
|
|
|
|
"pointer-events-none absolute inset-x-4 top-0 h-px",
|
|
|
|
|
sectionRail[tone],
|
|
|
|
|
)}
|
|
|
|
|
/>
|
2026-05-19 23:11:32 -05:00
|
|
|
<div className="mb-4 flex items-center justify-between gap-3">
|
|
|
|
|
<div className="flex items-center gap-1.5">
|
|
|
|
|
<h3 className="text-lg font-semibold text-strong">{title}</h3>
|
|
|
|
|
{infoText && (
|
2026-05-24 22:38:26 -05:00
|
|
|
<span
|
|
|
|
|
className="inline-flex text-muted"
|
|
|
|
|
title={infoText}
|
|
|
|
|
aria-label={infoText}
|
|
|
|
|
>
|
2026-05-19 23:11:32 -05:00
|
|
|
<Info className="h-3.5 w-3.5" />
|
|
|
|
|
</span>
|
|
|
|
|
)}
|
|
|
|
|
</div>
|
|
|
|
|
<div className="flex items-center gap-2">
|
|
|
|
|
{badge && (
|
2026-05-24 22:38:26 -05:00
|
|
|
<span
|
|
|
|
|
className={cn(
|
|
|
|
|
"inline-flex items-center rounded-full px-2 py-0.5 text-[11px] font-medium",
|
|
|
|
|
toneBadge[badge.tone],
|
|
|
|
|
)}
|
|
|
|
|
>
|
2026-05-19 23:11:32 -05:00
|
|
|
{badge.text}
|
|
|
|
|
</span>
|
|
|
|
|
)}
|
|
|
|
|
{action && (
|
|
|
|
|
<Link
|
|
|
|
|
href={action.href}
|
|
|
|
|
className="inline-flex items-center gap-1 text-xs text-muted transition hover:text-strong"
|
|
|
|
|
>
|
|
|
|
|
{action.label}
|
|
|
|
|
<ArrowUpRight className="h-3.5 w-3.5" />
|
|
|
|
|
</Link>
|
|
|
|
|
)}
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
{children}
|
|
|
|
|
</section>
|
|
|
|
|
);
|
|
|
|
|
}
|