feat: Implement global acceleration guide wizard
This commit is contained in:
parent
d2f8158594
commit
8125c4eaa7
2
next-env.d.ts
vendored
2
next-env.d.ts
vendored
@ -1,6 +1,6 @@
|
||||
/// <reference types="next" />
|
||||
/// <reference types="next/image-types/global" />
|
||||
import "./.next/types/routes.d.ts";
|
||||
import "./.next/dev/types/routes.d.ts";
|
||||
|
||||
// NOTE: This file should not be edited
|
||||
// see https://nextjs.org/docs/app/api-reference/config/typescript for more information.
|
||||
|
||||
@ -26,6 +26,7 @@ import { translations } from "../i18n/translations";
|
||||
import { useMoltbotStore } from "../lib/moltbotStore";
|
||||
import { cn } from "../lib/utils";
|
||||
import { AskAIDialog } from "../components/AskAIDialog";
|
||||
import { HeroCard } from "../components/HeroCard";
|
||||
import useSWR from "swr";
|
||||
|
||||
const iconMap: Record<string, any> = {
|
||||
@ -147,23 +148,20 @@ export function HeroSection() {
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex flex-col gap-4">
|
||||
{t.heroCards.map((card) => {
|
||||
const Icon = getIcon(card.title, PlusCircle);
|
||||
return (
|
||||
<div
|
||||
key={card.title}
|
||||
className="group flex items-start gap-4 rounded-2xl border border-surface-border bg-surface p-6 transition hover:border-primary/50 hover:bg-surface-hover"
|
||||
>
|
||||
<div className="mt-1 rounded-full border border-surface-border bg-surface-muted p-2 group-hover:border-primary/50 group-hover:text-primary">
|
||||
<Icon className="h-5 w-5" />
|
||||
</div>
|
||||
<div className="space-y-1">
|
||||
<h3 className="font-semibold text-heading">{card.title}</h3>
|
||||
<p className="text-sm text-text-muted">{card.description}</p>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
})}
|
||||
<div className="flex flex-col gap-4 relative">
|
||||
{t.heroCards.map((card) => {
|
||||
const Icon = getIcon(card.title, PlusCircle);
|
||||
return (
|
||||
<HeroCard
|
||||
key={card.title}
|
||||
icon={Icon}
|
||||
title={card.title}
|
||||
description={card.description}
|
||||
guide={card.guide}
|
||||
/>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
);
|
||||
|
||||
107
src/components/HeroCard.tsx
Normal file
107
src/components/HeroCard.tsx
Normal file
@ -0,0 +1,107 @@
|
||||
'use client';
|
||||
|
||||
import { useState } from 'react';
|
||||
import { ArrowRight, X, QrCode } from 'lucide-react';
|
||||
import { cn } from '../lib/utils';
|
||||
import Link from 'next/link';
|
||||
|
||||
interface GuideStep {
|
||||
text: string;
|
||||
link?: { url: string; label: string };
|
||||
code?: string;
|
||||
image?: string;
|
||||
}
|
||||
|
||||
interface HeroCardProps {
|
||||
icon: any;
|
||||
title: string;
|
||||
description: string;
|
||||
guide?: {
|
||||
title: string;
|
||||
steps: GuideStep[];
|
||||
dismiss: string;
|
||||
};
|
||||
}
|
||||
|
||||
export function HeroCard({ icon: Icon, title, description, guide }: HeroCardProps) {
|
||||
const [showGuide, setShowGuide] = useState(false);
|
||||
|
||||
return (
|
||||
<div
|
||||
className={cn(
|
||||
"group relative flex items-start gap-4 rounded-2xl border border-surface-border bg-surface p-6 transition-all duration-300",
|
||||
showGuide ? "border-primary shadow-lg ring-1 ring-primary/20 scale-[1.02]" : "hover:border-primary/50 hover:bg-surface-hover"
|
||||
)}
|
||||
onMouseEnter={() => guide && setShowGuide(true)}
|
||||
onMouseLeave={() => setShowGuide(false)}
|
||||
>
|
||||
<div className="mt-1 rounded-full border border-surface-border bg-surface-muted p-2 group-hover:border-primary/50 group-hover:text-primary">
|
||||
<Icon className="h-5 w-5" />
|
||||
</div>
|
||||
<div className="space-y-1 w-full">
|
||||
<h3 className="font-semibold text-heading">{title}</h3>
|
||||
<p className="text-sm text-text-muted">{description}</p>
|
||||
|
||||
{/* Guide Content Overlay */}
|
||||
{guide && showGuide && (
|
||||
<div className="absolute inset-x-0 top-full mt-2 z-50 animate-in fade-in slide-in-from-top-2">
|
||||
<div className="rounded-xl border border-primary/20 bg-surface/95 backdrop-blur-sm shadow-xl p-5 space-y-4">
|
||||
<div className="flex items-center justify-between border-b border-surface-border pb-2">
|
||||
<h4 className="font-semibold text-primary flex items-center gap-2">
|
||||
<span className="relative flex h-2 w-2">
|
||||
<span className="animate-ping absolute inline-flex h-full w-full rounded-full bg-primary opacity-75"></span>
|
||||
<span className="relative inline-flex rounded-full h-2 w-2 bg-primary"></span>
|
||||
</span>
|
||||
{guide.title}
|
||||
</h4>
|
||||
<button
|
||||
onClick={(e) => {
|
||||
e.stopPropagation();
|
||||
setShowGuide(false);
|
||||
}}
|
||||
className="text-xs text-text-muted hover:text-text flex items-center gap-1"
|
||||
>
|
||||
<X className="h-3 w-3" />
|
||||
{guide.dismiss}
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div className="space-y-4">
|
||||
{guide.steps.map((step, idx) => (
|
||||
<div key={idx} className="flex gap-3 text-sm text-text-subtle">
|
||||
<span className="flex-none flex items-center justify-center h-5 w-5 rounded-full bg-primary/10 text-primary text-xs font-bold">
|
||||
{idx + 1}
|
||||
</span>
|
||||
<div className="space-y-2 flex-1">
|
||||
<p className="leading-tight">{step.text}</p>
|
||||
{step.link && (
|
||||
<Link
|
||||
href={step.link.url}
|
||||
className="inline-flex items-center gap-1.5 text-xs font-medium text-primary hover:underline hover:text-primary-hover transition-colors"
|
||||
>
|
||||
{step.link.label}
|
||||
<ArrowRight className="h-3 w-3" />
|
||||
</Link>
|
||||
)}
|
||||
|
||||
{/* Special handling for VLESS QR code hint */}
|
||||
{idx === 2 && (
|
||||
<div className="mt-2 p-3 bg-surface-muted rounded-lg border border-surface-border flex items-center gap-3">
|
||||
<QrCode className="h-8 w-8 text-primary opacity-80" />
|
||||
<div className="text-xs text-text-muted">
|
||||
<p className="font-medium text-text">VLESS Protocol Ready</p>
|
||||
<p>Scan QR code in panel</p>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@ -580,7 +580,20 @@ export type Translation = {
|
||||
userCenter: UserCenterTranslation
|
||||
marketing: {
|
||||
home: MarketingHomeTranslation & {
|
||||
heroCards: { title: string; description: string }[]
|
||||
heroCards: {
|
||||
title: string
|
||||
description: string
|
||||
guide?: {
|
||||
title: string
|
||||
steps: {
|
||||
text: string
|
||||
link?: { url: string; label: string }
|
||||
code?: string // For VLESS link/QR or command
|
||||
image?: string // For QR code placeholder if needed
|
||||
}[]
|
||||
dismiss: string
|
||||
}
|
||||
}[]
|
||||
nextSteps: {
|
||||
title: string
|
||||
badge: string
|
||||
@ -1142,6 +1155,15 @@ export const translations: Record<'en' | 'zh', Translation> = {
|
||||
{
|
||||
title: 'Global Acceleration Network',
|
||||
description: 'Leverage globally distributed edge nodes to achieve high-speed application distribution and stable connections, ensuring users enjoy an ultra-fast access experience.',
|
||||
guide: {
|
||||
title: 'Global Acceleration Guide',
|
||||
dismiss: 'Exit Guide',
|
||||
steps: [
|
||||
{ text: 'svc.plus provides underlying overseas acceleration solutions to solve the last-mile barrier for you.' },
|
||||
{ text: 'Enter the panel to configure your node.', link: { url: '/panel', label: 'Go to Panel' } },
|
||||
{ text: 'Scan to join via VLESS protocol (clients supported: OneXray, etc.).', link: { url: 'https://github.com/OneXray/OneXray', label: 'Client Download' } },
|
||||
],
|
||||
},
|
||||
},
|
||||
{
|
||||
title: 'Full-link SaaS Hosting',
|
||||
@ -1909,6 +1931,15 @@ export const translations: Record<'en' | 'zh', Translation> = {
|
||||
{
|
||||
title: '全球加速网络',
|
||||
description: '依托全球分布的边缘节点,实现应用的高速分发与稳定连接,确保用户享受极速的访问体验。',
|
||||
guide: {
|
||||
title: '全球加速接入向导',
|
||||
dismiss: '退出向导',
|
||||
steps: [
|
||||
{ text: 'svc.plus 提供的底层海外加速方案将为你解决最后 1 公里的障碍。' },
|
||||
{ text: '点击下方按钮进入控制台配置节点。', link: { url: '/panel', label: '进入控制台' } },
|
||||
{ text: '支持 Vless 协议的客户端都可以扫码加入,例如 OneXray。', link: { url: 'https://github.com/OneXray/OneXray', label: '客户端下载' } },
|
||||
],
|
||||
},
|
||||
},
|
||||
{
|
||||
title: '全链路 SaaS 托管',
|
||||
|
||||
Loading…
Reference in New Issue
Block a user