148 lines
6.1 KiB
TypeScript
148 lines
6.1 KiB
TypeScript
"use client";
|
||
|
||
import { motion } from "framer-motion";
|
||
import { ArrowRight, ImageIcon, PlayCircle } from "lucide-react";
|
||
import { Button } from "@/components/ui/button";
|
||
import { cn } from "@/lib/utils";
|
||
import { defaultAgentWorkflowShowcaseConfig } from "@/lib/agents/workflow-showcase-config";
|
||
import type { AgentWorkflowShowcaseConfig } from "@/types/agent";
|
||
|
||
interface AgentWorkflowShowcaseBannerProps {
|
||
config?: AgentWorkflowShowcaseConfig | null;
|
||
onActionClick?: () => void;
|
||
actionLabel?: string;
|
||
secondaryActionLabel?: string;
|
||
onSecondaryActionClick?: () => void;
|
||
className?: string;
|
||
}
|
||
|
||
export function AgentWorkflowShowcaseBanner({
|
||
config,
|
||
onActionClick,
|
||
actionLabel,
|
||
secondaryActionLabel,
|
||
onSecondaryActionClick,
|
||
className,
|
||
}: AgentWorkflowShowcaseBannerProps) {
|
||
const resolved = config || defaultAgentWorkflowShowcaseConfig;
|
||
const hasVideo = Boolean(resolved.videoUrl);
|
||
const hasPoster = Boolean(resolved.posterUrl);
|
||
const shouldRenderMedia = resolved.enabled !== false;
|
||
const resolvedActionLabel = actionLabel || resolved.actionText;
|
||
|
||
return (
|
||
<div className={cn("space-y-12 pt-6 md:space-y-14 md:pt-8", className)}>
|
||
<motion.div
|
||
className="text-center"
|
||
initial={{ opacity: 0, y: 20 }}
|
||
animate={{ opacity: 1, y: 0 }}
|
||
transition={{ duration: 0.6 }}
|
||
>
|
||
<h1 className="mb-3 text-3xl font-bold text-white md:text-4xl">{resolved.title}</h1>
|
||
<p className="mb-8 text-base text-white/50 md:mb-9">{resolved.subtitle}</p>
|
||
{resolvedActionLabel || secondaryActionLabel ? (
|
||
<div className="flex flex-col items-center justify-center gap-4 sm:flex-row">
|
||
{resolvedActionLabel ? (
|
||
<Button
|
||
type="button"
|
||
onClick={onActionClick}
|
||
className={cn(
|
||
"h-12 gap-2 rounded-full px-8",
|
||
"bg-gradient-to-r from-emerald-500 to-teal-500",
|
||
"text-base font-medium text-white",
|
||
"hover:from-emerald-400 hover:to-teal-400",
|
||
"shadow-[0_0_30px_rgba(16,185,129,0.3)]",
|
||
"transition-all duration-300"
|
||
)}
|
||
>
|
||
{resolvedActionLabel}
|
||
<ArrowRight className="h-5 w-5" />
|
||
</Button>
|
||
) : null}
|
||
{secondaryActionLabel ? (
|
||
<Button
|
||
type="button"
|
||
variant="outline"
|
||
onClick={onSecondaryActionClick}
|
||
className="h-12 rounded-full border-white/20 bg-transparent px-12 text-base font-medium text-white hover:border-emerald-400/50 hover:bg-white/[0.04]"
|
||
>
|
||
{secondaryActionLabel}
|
||
</Button>
|
||
) : null}
|
||
</div>
|
||
) : null}
|
||
</motion.div>
|
||
|
||
{shouldRenderMedia ? (
|
||
<motion.div
|
||
className="relative overflow-hidden rounded-[28px] border border-white/12 bg-[#101214] shadow-[inset_0_1px_0_rgba(255,255,255,0.04)]"
|
||
initial={{ opacity: 0, scale: 0.96 }}
|
||
animate={{ opacity: 1, scale: 1 }}
|
||
transition={{ duration: 0.6, delay: 0.15 }}
|
||
>
|
||
<div className="aspect-[10/3] w-full">
|
||
{hasVideo ? (
|
||
<video
|
||
key={resolved.videoUrl}
|
||
className="h-full w-full object-cover"
|
||
src={resolved.videoUrl}
|
||
poster={resolved.posterUrl || undefined}
|
||
autoPlay
|
||
muted
|
||
loop
|
||
playsInline
|
||
preload="metadata"
|
||
/>
|
||
) : hasPoster ? (
|
||
<img
|
||
src={resolved.posterUrl || ""}
|
||
alt={resolved.overlayText || resolved.title}
|
||
className="h-full w-full object-cover"
|
||
/>
|
||
) : (
|
||
<div className="flex h-full w-full flex-col items-center justify-center bg-[radial-gradient(circle_at_top,#1b2430_0%,#0e1013_55%,#070809_100%)] text-center">
|
||
<div className="mb-4 flex h-20 w-20 items-center justify-center rounded-3xl border border-white/10 bg-white/[0.04]">
|
||
<PlayCircle className="h-10 w-10 text-white/25" />
|
||
</div>
|
||
<div className="text-base font-medium text-white/70">顶部视频海报待配置</div>
|
||
<div className="mt-2 max-w-lg text-sm text-white/35">
|
||
在智能体系统设置中上传视频或封面图后,这里会自动展示循环播放海报。
|
||
</div>
|
||
</div>
|
||
)}
|
||
</div>
|
||
|
||
<div className="pointer-events-none absolute inset-0 bg-[radial-gradient(circle_at_center,rgba(16,185,129,0.12),transparent_42%)]" />
|
||
<div className="pointer-events-none absolute inset-0 bg-gradient-to-t from-black/70 via-black/10 to-black/35" />
|
||
|
||
{resolved.overlayText ? (
|
||
<div className="pointer-events-none absolute inset-0 flex items-center justify-center px-8 text-center">
|
||
<div className="max-w-[78%] text-2xl font-semibold tracking-[0.18em] text-white/22 sm:text-3xl lg:text-5xl">
|
||
{resolved.overlayText}
|
||
</div>
|
||
</div>
|
||
) : null}
|
||
|
||
<div className="pointer-events-none absolute inset-x-0 top-0 flex items-center justify-between px-5 py-5 sm:px-7">
|
||
<div className="rounded-full border border-white/14 bg-black/30 px-3 py-1 text-xs font-medium text-white/70 backdrop-blur-md">
|
||
视频海报
|
||
</div>
|
||
{hasVideo || hasPoster ? (
|
||
<div className="rounded-full border border-white/14 bg-black/30 px-3 py-1 text-xs text-white/55 backdrop-blur-md">
|
||
固定比例 10:3
|
||
</div>
|
||
) : null}
|
||
</div>
|
||
|
||
{resolved.footerText ? (
|
||
<div className="pointer-events-none absolute bottom-5 left-5 flex items-center gap-2 rounded-full border border-white/14 bg-black/35 px-3 py-1.5 text-sm text-white/75 backdrop-blur-md sm:left-7">
|
||
<ImageIcon className="h-4 w-4" />
|
||
<span>{resolved.footerText}</span>
|
||
</div>
|
||
) : null}
|
||
</motion.div>
|
||
) : null}
|
||
</div>
|
||
);
|
||
}
|