feat: upgrade EmptyState component with illustration and learn more support

Add illustration and learnMoreLink props to EmptyState (backward compatible).
Create EmptyStateIllustrations.tsx with 7 brand-themed SVG illustrations.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
chihlasm
2026-03-17 00:57:27 -04:00
parent f7271e22ae
commit 85d1ed8028
2 changed files with 161 additions and 3 deletions

View File

@@ -1,23 +1,51 @@
import type { ReactNode } from 'react'
import { Link } from 'react-router-dom'
import { cn } from '@/lib/utils'
interface EmptyStateProps {
icon?: ReactNode
illustration?: ReactNode
title: string
description?: string
action?: ReactNode
learnMoreLink?: string
learnMoreText?: string
className?: string
}
export function EmptyState({ icon, title, description, action, className }: EmptyStateProps) {
export function EmptyState({
icon,
illustration,
title,
description,
action,
learnMoreLink,
learnMoreText = 'Learn more',
className,
}: EmptyStateProps) {
return (
<div className={cn('flex flex-col items-center justify-center py-12 text-center', className)}>
{icon && <div className="mb-4 text-muted-foreground">{icon}</div>}
{illustration && (
<div className="mb-6 opacity-60">
{illustration}
</div>
)}
{!illustration && icon && (
<div className="mb-4 text-muted-foreground">{icon}</div>
)}
<h3 className="text-lg font-semibold text-foreground">{title}</h3>
{description && (
<p className="mt-1 max-w-sm text-sm text-muted-foreground">{description}</p>
<p className="mt-2 max-w-sm text-sm text-muted-foreground">{description}</p>
)}
{action && <div className="mt-4">{action}</div>}
{learnMoreLink && (
<Link
to={learnMoreLink}
className="mt-3 text-sm text-muted-foreground hover:text-foreground transition-colors"
>
{learnMoreText} &rarr;
</Link>
)}
</div>
)
}

View File

@@ -0,0 +1,130 @@
/**
* SVG illustrations for EmptyState components.
* Each uses the brand cyan palette (#06b6d4 / #22d3ee) at low opacity.
* ViewBox: 80x60, simple line art style.
*/
export function FlowIllustration() {
return (
<svg width="80" height="60" viewBox="0 0 80 60" fill="none" xmlns="http://www.w3.org/2000/svg">
{/* Root node */}
<circle cx="40" cy="10" r="6" fill="rgba(6,182,212,0.15)" stroke="#06b6d4" strokeWidth="1.5" />
{/* Branches */}
<line x1="40" y1="16" x2="20" y2="34" stroke="#06b6d4" strokeWidth="1.5" />
<line x1="40" y1="16" x2="60" y2="34" stroke="#06b6d4" strokeWidth="1.5" />
{/* Left child */}
<circle cx="20" cy="38" r="5" fill="rgba(6,182,212,0.15)" stroke="#22d3ee" strokeWidth="1.5" />
{/* Right child */}
<circle cx="60" cy="38" r="5" fill="rgba(6,182,212,0.15)" stroke="#22d3ee" strokeWidth="1.5" />
{/* Leaf branches */}
<line x1="20" y1="43" x2="12" y2="52" stroke="#22d3ee" strokeWidth="1" />
<line x1="20" y1="43" x2="28" y2="52" stroke="#22d3ee" strokeWidth="1" />
<circle cx="12" cy="54" r="3" fill="rgba(34,211,238,0.15)" stroke="#22d3ee" strokeWidth="1" />
<circle cx="28" cy="54" r="3" fill="rgba(34,211,238,0.15)" stroke="#22d3ee" strokeWidth="1" />
</svg>
)
}
export function AnalyticsIllustration() {
return (
<svg width="80" height="60" viewBox="0 0 80 60" fill="none" xmlns="http://www.w3.org/2000/svg">
{/* Bars */}
<rect x="12" y="38" width="10" height="16" rx="2" fill="rgba(6,182,212,0.15)" stroke="#06b6d4" strokeWidth="1.5" />
<rect x="26" y="28" width="10" height="26" rx="2" fill="rgba(6,182,212,0.15)" stroke="#22d3ee" strokeWidth="1.5" />
<rect x="40" y="20" width="10" height="34" rx="2" fill="rgba(6,182,212,0.15)" stroke="#06b6d4" strokeWidth="1.5" />
<rect x="54" y="10" width="10" height="44" rx="2" fill="rgba(6,182,212,0.15)" stroke="#22d3ee" strokeWidth="1.5" />
{/* Baseline */}
<line x1="8" y1="56" x2="72" y2="56" stroke="#06b6d4" strokeWidth="1" opacity="0.5" />
</svg>
)
}
export function SessionIllustration() {
return (
<svg width="80" height="60" viewBox="0 0 80 60" fill="none" xmlns="http://www.w3.org/2000/svg">
{/* Card 1 */}
<rect x="12" y="8" width="56" height="12" rx="3" fill="rgba(6,182,212,0.15)" stroke="#06b6d4" strokeWidth="1.5" />
<circle cx="22" cy="14" r="2" fill="#06b6d4" />
<line x1="28" y1="14" x2="56" y2="14" stroke="#06b6d4" strokeWidth="1" opacity="0.5" />
{/* Card 2 */}
<rect x="12" y="24" width="56" height="12" rx="3" fill="rgba(6,182,212,0.15)" stroke="#22d3ee" strokeWidth="1.5" />
<circle cx="22" cy="30" r="2" fill="#22d3ee" />
<line x1="28" y1="30" x2="52" y2="30" stroke="#22d3ee" strokeWidth="1" opacity="0.5" />
{/* Card 3 */}
<rect x="12" y="40" width="56" height="12" rx="3" fill="rgba(6,182,212,0.15)" stroke="#06b6d4" strokeWidth="1.5" />
<circle cx="22" cy="46" r="2" fill="#06b6d4" />
<line x1="28" y1="46" x2="48" y2="46" stroke="#06b6d4" strokeWidth="1" opacity="0.5" />
</svg>
)
}
export function IntegrationIllustration() {
return (
<svg width="80" height="60" viewBox="0 0 80 60" fill="none" xmlns="http://www.w3.org/2000/svg">
{/* Left box */}
<rect x="6" y="18" width="22" height="24" rx="4" fill="rgba(6,182,212,0.15)" stroke="#06b6d4" strokeWidth="1.5" />
<line x1="12" y1="26" x2="22" y2="26" stroke="#06b6d4" strokeWidth="1" opacity="0.6" />
<line x1="12" y1="30" x2="20" y2="30" stroke="#06b6d4" strokeWidth="1" opacity="0.4" />
{/* Right box */}
<rect x="52" y="18" width="22" height="24" rx="4" fill="rgba(6,182,212,0.15)" stroke="#22d3ee" strokeWidth="1.5" />
<line x1="58" y1="26" x2="68" y2="26" stroke="#22d3ee" strokeWidth="1" opacity="0.6" />
<line x1="58" y1="30" x2="66" y2="30" stroke="#22d3ee" strokeWidth="1" opacity="0.4" />
{/* Dashed arrows */}
<line x1="30" y1="26" x2="50" y2="26" stroke="#06b6d4" strokeWidth="1.5" strokeDasharray="3 2" />
<line x1="50" y1="34" x2="30" y2="34" stroke="#22d3ee" strokeWidth="1.5" strokeDasharray="3 2" />
{/* Arrow tips */}
<path d="M48 23 L52 26 L48 29" stroke="#06b6d4" strokeWidth="1.5" fill="none" />
<path d="M32 31 L28 34 L32 37" stroke="#22d3ee" strokeWidth="1.5" fill="none" />
</svg>
)
}
export function StepLibraryIllustration() {
return (
<svg width="80" height="60" viewBox="0 0 80 60" fill="none" xmlns="http://www.w3.org/2000/svg">
{/* List items */}
<circle cx="18" cy="14" r="3" fill="rgba(6,182,212,0.15)" stroke="#06b6d4" strokeWidth="1.5" />
<line x1="26" y1="14" x2="62" y2="14" stroke="#06b6d4" strokeWidth="1.5" opacity="0.5" />
<circle cx="18" cy="27" r="3" fill="rgba(6,182,212,0.15)" stroke="#22d3ee" strokeWidth="1.5" />
<line x1="26" y1="27" x2="58" y2="27" stroke="#22d3ee" strokeWidth="1.5" opacity="0.5" />
<circle cx="18" cy="40" r="3" fill="rgba(6,182,212,0.15)" stroke="#06b6d4" strokeWidth="1.5" />
<line x1="26" y1="40" x2="54" y2="40" stroke="#06b6d4" strokeWidth="1.5" opacity="0.5" />
<circle cx="18" cy="53" r="3" fill="rgba(34,211,238,0.15)" stroke="#22d3ee" strokeWidth="1.5" />
<line x1="26" y1="53" x2="50" y2="53" stroke="#22d3ee" strokeWidth="1.5" opacity="0.5" />
</svg>
)
}
export function ScriptIllustration() {
return (
<svg width="80" height="60" viewBox="0 0 80 60" fill="none" xmlns="http://www.w3.org/2000/svg">
{/* Terminal window */}
<rect x="8" y="4" width="64" height="52" rx="4" fill="rgba(6,182,212,0.15)" stroke="#06b6d4" strokeWidth="1.5" />
{/* Title bar */}
<line x1="8" y1="14" x2="72" y2="14" stroke="#06b6d4" strokeWidth="1" opacity="0.3" />
<circle cx="16" cy="9" r="2" fill="#06b6d4" opacity="0.4" />
<circle cx="23" cy="9" r="2" fill="#22d3ee" opacity="0.4" />
{/* Code lines */}
<line x1="16" y1="22" x2="40" y2="22" stroke="#06b6d4" strokeWidth="1.5" opacity="0.6" />
<line x1="20" y1="30" x2="52" y2="30" stroke="#22d3ee" strokeWidth="1.5" opacity="0.5" />
<line x1="20" y1="38" x2="46" y2="38" stroke="#06b6d4" strokeWidth="1.5" opacity="0.4" />
<line x1="16" y1="46" x2="36" y2="46" stroke="#22d3ee" strokeWidth="1.5" opacity="0.5" />
</svg>
)
}
export function ShareIllustration() {
return (
<svg width="80" height="60" viewBox="0 0 80 60" fill="none" xmlns="http://www.w3.org/2000/svg">
{/* Center node */}
<circle cx="28" cy="30" r="8" fill="rgba(6,182,212,0.15)" stroke="#06b6d4" strokeWidth="1.5" />
{/* Top-right node */}
<circle cx="58" cy="14" r="6" fill="rgba(6,182,212,0.15)" stroke="#22d3ee" strokeWidth="1.5" />
{/* Bottom-right node */}
<circle cx="58" cy="46" r="6" fill="rgba(6,182,212,0.15)" stroke="#22d3ee" strokeWidth="1.5" />
{/* Connecting lines */}
<line x1="35" y1="25" x2="52" y2="17" stroke="#06b6d4" strokeWidth="1.5" />
<line x1="35" y1="35" x2="52" y2="43" stroke="#06b6d4" strokeWidth="1.5" />
</svg>
)
}