Skip to main content
Glama

MCPDemo - Visual SQL Chat Platform

by Ayi456
PlansDialog.tsx6.85 kB
import { Link } from 'react-router-dom' import useAuth from '@/store/useAuth' import { useMemo } from 'react' export default function PlansDialog({ open, onClose }: { open: boolean; onClose: () => void }) { const { user } = useAuth() const plans = useMemo(() => ([ { id: 'basic' as const, name: '基础版', price: '¥29/月', quota: { daily: 500, monthly: 15000 }, features: [ '每日 500 次 · 每月 15000 次', '优先队列', '配额提醒' ], ctaText: (loggedIn: boolean) => loggedIn ? '立即升级' : '立即开通', ctaHref: (loggedIn: boolean) => loggedIn ? '/account' : '/register' }, { id: 'premium' as const, name: '高级版', price: '¥99/月', quota: { daily: 2000, monthly: 60000 }, features: [ '每日 2000 次 · 每月 60000 次', 'VIP 优先', '高级功能', '专属支持' ], ctaText: (loggedIn: boolean) => loggedIn ? '立即升级' : '立即开通', ctaHref: (loggedIn: boolean) => loggedIn ? '/account' : '/register' }, { id: 'enterprise' as const, name: '企业版', price: '按需报价', quota: { daily: '10,000+', monthly: '300,000+' }, features: [ '定制化方案', 'SLA 保障', '企业能力' ], ctaText: () => '联系销售', ctaHref: () => 'mailto:sales@example.com' } ]), []) const accents: Record<'basic' | 'premium' | 'enterprise', string> = { basic: 'from-blue-500 to-indigo-600', premium: 'from-purple-500 to-pink-600', enterprise: 'from-amber-500 to-orange-600' } if (!open) return null return ( <div className="fixed inset-0 z-50 flex items-center justify-center"> <div className="absolute inset-0 bg-black/40" onClick={onClose} /> <div className="relative bg-white dark:bg-gray-800 rounded-2xl shadow-2xl w-full max-w-7xl p-8"> {/* Header */} <div className="flex items-center justify-between mb-4"> <div> <h3 className="text-xl font-semibold text-gray-900 dark:text-white">选择订阅计划</h3> <p className="text-sm text-gray-500 dark:text-gray-400">选择最适合您需求的订阅计划</p> </div> <button className="p-2 rounded-lg hover:bg-gray-100 dark:hover:bg-gray-700 text-gray-500 dark:text-gray-400" onClick={onClose} aria-label="关闭" > <svg className="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24"> <path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M6 18L18 6M6 6l12 12" /> </svg> </button> </div> {/* Plans grid */} <div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-2 xl:grid-cols-3 gap-8"> {plans.map((p) => { const isCurrent = user?.plan === p.id const isEnterprise = p.id === 'enterprise' return ( <div key={p.id} className="group relative overflow-hidden card-modern p-8 min-h-[420px] flex flex-col transform-gpu transition-all duration-300 hover:-translate-y-1 hover:scale-[1.01] hover:shadow-2xl hover:shadow-gray-200/40 dark:hover:shadow-black/20 hover:ring-1 hover:ring-blue-500/10 dark:hover:ring-blue-400/10"> <div className={`absolute inset-x-0 top-0 h-1 bg-gradient-to-r ${accents[p.id]} opacity-90 transition-opacity duration-300 group-hover:opacity-100`}></div> <div className="mb-3 flex items-center justify-between"> <h4 className="text-lg font-medium text-gray-900 dark:text-white">{p.name}</h4> {isCurrent && ( <span className="text-xs px-2 py-0.5 rounded-full bg-green-100 dark:bg-green-900/30 text-green-700 dark:text-green-300">当前套餐</span> )} </div> <div className="text-3xl font-extrabold text-gray-900 dark:text-white">{p.price}</div> <div className="my-3 h-px bg-gray-200 dark:bg-gray-800" /> <div className="text-[15px] text-gray-600 dark:text-gray-400">每日 {p.quota.daily} · 每月 {p.quota.monthly}</div> <ul className="mt-4 text-[15px] text-gray-700 dark:text-gray-300 space-y-2 leading-relaxed"> {p.features.map((f, idx) => ( <li key={idx} className="flex items-start gap-2"> <svg className="w-3.5 h-3.5 mt-0.5 text-green-500 flex-shrink-0" fill="none" stroke="currentColor" viewBox="0 0 24 24"> <path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M5 13l4 4L19 7" /> </svg> <span>{f}</span> </li> ))} </ul> {isEnterprise ? ( <a href={p.ctaHref(!!user)} className={isCurrent ? 'mt-auto w-full inline-flex items-center justify-center rounded-xl h-12 text-base bg-white dark:bg-gray-800 border border-gray-200 dark:border-gray-700 text-gray-500 dark:text-gray-300 cursor-default' : `mt-auto w-full inline-flex items-center justify-center rounded-xl h-12 text-base text-white bg-gradient-to-r ${accents[p.id]} shadow-sm transition-all duration-300 group-hover:shadow-md group-hover:-translate-y-0.5 hover:opacity-95 focus:outline-none focus-visible:ring-2 focus-visible:ring-blue-500/60`} aria-disabled={isCurrent} onClick={(e) => { if (isCurrent) e.preventDefault() }} > {isCurrent ? '已激活' : p.ctaText(!!user)} </a> ) : ( <Link to={p.ctaHref(!!user)} className={isCurrent ? 'mt-auto w-full inline-flex items-center justify-center rounded-xl h-12 text-base bg-white dark:bg-gray-800 border border-gray-200 dark:border-gray-700 text-gray-500 dark:text-gray-300 cursor-default' : `mt-auto w-full inline-flex items-center justify-center rounded-xl h-12 text-base text-white bg-gradient-to-r ${accents[p.id]} shadow-sm transition-all duration-300 group-hover:shadow-md group-hover:-translate-y-0.5 hover:opacity-95 focus:outline-none focus-visible:ring-2 focus-visible:ring-blue-500/60`} aria-disabled={isCurrent} onClick={(e) => { if (isCurrent) e.preventDefault() }} > {isCurrent ? '已激活' : p.ctaText(!!user)} </Link> )} </div> ) })} </div> </div> </div> ) }

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/Ayi456/visual-mcp'

If you have feedback or need assistance with the MCP directory API, please join our Discord server