claudmax 2.0.0 → 2.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (124) hide show
  1. package/claudmax-1.0.16.tgz +0 -0
  2. package/{packages/cli/index.js → index.js} +2 -0
  3. package/package.json +27 -55
  4. package/.claude/settings.local.json +0 -7
  5. package/.env.example +0 -24
  6. package/.github/workflows/publish.yml +0 -31
  7. package/README.md +0 -178
  8. package/claudmax-mcp-1.0.2.tgz +0 -0
  9. package/help +0 -0
  10. package/help-wal +0 -0
  11. package/next-env.d.ts +0 -6
  12. package/next.config.mjs +0 -43
  13. package/packages/cli/claudmax-1.0.16.tgz +0 -0
  14. package/packages/cli/package.json +0 -33
  15. package/packages/mcp/claudmax-mcp-1.0.0.tgz +0 -0
  16. package/packages/mcp/claudmax-mcp-1.0.1.tgz +0 -0
  17. package/packages/mcp/claudmax-mcp-1.0.2.tgz +0 -0
  18. package/packages/mcp/claudmax-mcp-1.0.3.tgz +0 -0
  19. package/packages/mcp/index.js +0 -129
  20. package/packages/mcp/package-lock.json +0 -1146
  21. package/packages/mcp/package.json +0 -32
  22. package/postcss.config.mjs +0 -6
  23. package/prisma/schema.prisma +0 -130
  24. package/prisma/seed.ts +0 -27
  25. package/public/favicon.svg +0 -10
  26. package/public/robots.txt +0 -10
  27. package/run_build.sh +0 -4
  28. package/scripts/migrate-plans.js +0 -98
  29. package/scripts/seed-blog.ts +0 -1014
  30. package/src/app/admin/dashboard/AdminDashboardClient.tsx +0 -1546
  31. package/src/app/admin/dashboard/page.tsx +0 -13
  32. package/src/app/admin/page.tsx +0 -132
  33. package/src/app/api/admin/auth/me/route.ts +0 -34
  34. package/src/app/api/admin/health/route.ts +0 -110
  35. package/src/app/api/admin/keys/[id]/route.ts +0 -116
  36. package/src/app/api/admin/keys/route.ts +0 -192
  37. package/src/app/api/admin/keys-list/route.ts +0 -81
  38. package/src/app/api/admin/login/route.ts +0 -72
  39. package/src/app/api/admin/logout/route.ts +0 -8
  40. package/src/app/api/admin/migrate/route.ts +0 -133
  41. package/src/app/api/admin/plans/[id]/route.ts +0 -65
  42. package/src/app/api/admin/plans/route.ts +0 -66
  43. package/src/app/api/admin/posts/[id]/route.ts +0 -81
  44. package/src/app/api/admin/posts/route.ts +0 -83
  45. package/src/app/api/admin/seed/route.ts +0 -145
  46. package/src/app/api/admin/settings/route.ts +0 -44
  47. package/src/app/api/admin/stats/route.ts +0 -74
  48. package/src/app/api/admin/users/[id]/route.ts +0 -166
  49. package/src/app/api/admin/users/plans/route.ts +0 -45
  50. package/src/app/api/admin/users/route.ts +0 -202
  51. package/src/app/api/blog/[slug]/route.ts +0 -22
  52. package/src/app/api/blog/route.ts +0 -40
  53. package/src/app/api/cron/daily-status/route.ts +0 -208
  54. package/src/app/api/support/chat/route.ts +0 -55
  55. package/src/app/api/support/chat/session/route.ts +0 -62
  56. package/src/app/api/support/chat/stream/route.ts +0 -44
  57. package/src/app/api/support/email/route.ts +0 -63
  58. package/src/app/api/tools/understand_image/route.ts +0 -113
  59. package/src/app/api/tools/upload/route.ts +0 -179
  60. package/src/app/api/tools/web_search/route.ts +0 -99
  61. package/src/app/api/v1/audio/route.ts +0 -67
  62. package/src/app/api/v1/audio/speech/route.ts +0 -73
  63. package/src/app/api/v1/chat/completions/route.ts +0 -3
  64. package/src/app/api/v1/chat/route.ts +0 -1079
  65. package/src/app/api/v1/images/generations/route.ts +0 -93
  66. package/src/app/api/v1/info/route.ts +0 -30
  67. package/src/app/api/v1/key-status/route.ts +0 -109
  68. package/src/app/api/v1/key-status/stream/route.ts +0 -135
  69. package/src/app/api/v1/messages/count_tokens/route.ts +0 -22
  70. package/src/app/api/v1/messages/route.ts +0 -807
  71. package/src/app/api/v1/models/route.ts +0 -14
  72. package/src/app/api/v1/route.ts +0 -18
  73. package/src/app/blog/BlogClient.tsx +0 -193
  74. package/src/app/blog/[slug]/page.tsx +0 -117
  75. package/src/app/blog/page.tsx +0 -20
  76. package/src/app/check-usage/CheckUsageClient.tsx +0 -186
  77. package/src/app/check-usage/layout.tsx +0 -11
  78. package/src/app/check-usage/page.tsx +0 -15
  79. package/src/app/docs/layout.tsx +0 -16
  80. package/src/app/docs/page.tsx +0 -1055
  81. package/src/app/faq/FAQClient.tsx +0 -227
  82. package/src/app/faq/page.tsx +0 -21
  83. package/src/app/globals.css +0 -75
  84. package/src/app/layout.tsx +0 -80
  85. package/src/app/page.tsx +0 -256
  86. package/src/app/reseller/ResellerClient.tsx +0 -435
  87. package/src/app/reseller/page.tsx +0 -15
  88. package/src/app/setup.ps1/route.ts +0 -79
  89. package/src/app/setup.sh/route.ts +0 -113
  90. package/src/app/sitemap.ts +0 -50
  91. package/src/app/status/StatusClient.tsx +0 -103
  92. package/src/app/status/layout.tsx +0 -11
  93. package/src/app/status/page.tsx +0 -15
  94. package/src/app/support/SupportClient.tsx +0 -411
  95. package/src/app/support/page.tsx +0 -25
  96. package/src/app/v1/chat/completions/route.ts +0 -3
  97. package/src/app/v1/chat/route.ts +0 -4
  98. package/src/app/v1/messages/route.ts +0 -3
  99. package/src/components/Footer.tsx +0 -120
  100. package/src/components/Header.tsx +0 -131
  101. package/src/components/landing/features.tsx +0 -99
  102. package/src/components/ui/badge.tsx +0 -32
  103. package/src/components/ui/button.tsx +0 -46
  104. package/src/components/ui/card.tsx +0 -50
  105. package/src/components/ui/dialog.tsx +0 -97
  106. package/src/components/ui/dropdown-menu.tsx +0 -156
  107. package/src/components/ui/input.tsx +0 -21
  108. package/src/components/ui/label.tsx +0 -15
  109. package/src/components/ui/separator.tsx +0 -22
  110. package/src/components/ui/switch.tsx +0 -27
  111. package/src/components/ui/tabs.tsx +0 -51
  112. package/src/components/ui/toast.tsx +0 -103
  113. package/src/lib/auth.ts +0 -45
  114. package/src/lib/prisma.ts +0 -20
  115. package/src/lib/providers.ts +0 -158
  116. package/src/lib/security.ts +0 -165
  117. package/src/lib/utils.ts +0 -14
  118. package/src/middleware.ts +0 -30
  119. package/tailwind.config.ts +0 -53
  120. package/tsconfig.json +0 -41
  121. package/tsconfig.tsbuildinfo +0 -1
  122. package/vercel.json +0 -8
  123. /package/{packages/cli/bin → bin}/claudmax.js +0 -0
  124. /package/{packages/cli/claudmax-1.0.17.tgz → claudmax-1.0.17.tgz} +0 -0
@@ -1,120 +0,0 @@
1
- 'use client';
2
-
3
- import Link from 'next/link';
4
- import { useState, useEffect } from 'react';
5
- import { BookOpen, Activity, FileText, Github, Mail, MessageCircle, HelpCircle, PenLine } from 'lucide-react';
6
-
7
- export default function Footer() {
8
- const year = new Date().getFullYear();
9
- const [supportEnabled, setSupportEnabled] = useState(true);
10
-
11
- useEffect(() => {
12
- fetch('/api/admin/settings')
13
- .then(r => r.ok ? r.json() : { supportEnabled: true })
14
- .then(d => setSupportEnabled(d.supportEnabled ?? true))
15
- .catch(() => setSupportEnabled(true));
16
- }, []);
17
-
18
- const baseFooterLinks = [
19
- {
20
- Product: [
21
- { href: '/docs', label: 'Documentation' },
22
- { href: '/blog', label: 'Blog' },
23
- { href: '/faq', label: 'FAQ' },
24
- { href: '/support', label: 'Support', show: supportEnabled },
25
- { href: '/status', label: 'API Status' },
26
- ],
27
- },
28
- {
29
- Resources: [
30
- { href: '/docs?section=claude_code', label: 'Claude Code Setup' },
31
- { href: '/docs?section=cursor', label: 'Cursor Setup' },
32
- { href: '/docs?section=limits', label: 'Rate Limits' },
33
- { href: '/docs?section=auth', label: 'Authentication' },
34
- ],
35
- },
36
- {
37
- Company: [
38
- { href: '/check-usage', label: 'Check Usage' },
39
- { href: '/docs?section=troubleshooting', label: 'Troubleshooting' },
40
- { href: '/docs?section=mcp', label: 'MCP Tools' },
41
- { href: '/docs?section=models', label: 'Models' },
42
- ],
43
- },
44
- ];
45
-
46
- const footerLinks = baseFooterLinks.map(group => {
47
- const [[category, links]] = Object.entries(group);
48
- return {
49
- [category]: links.filter((l: any) => l.show !== false),
50
- };
51
- });
52
-
53
- const socialLinks: never[] = [];
54
-
55
- return (
56
- <footer className="bg-white border-t border-gray-200 mt-auto">
57
- {/* Main footer */}
58
- <div className="max-w-7xl mx-auto px-4 sm:px-6 py-12">
59
- <div className="grid grid-cols-2 md:grid-cols-4 gap-8 lg:gap-12">
60
- {/* Brand */}
61
- <div className="col-span-2 md:col-span-1">
62
- <Link href="/" className="flex items-center gap-2.5 mb-4">
63
- <div className="w-8 h-8 rounded-lg bg-gradient-to-br from-[#5244F3] to-[#6B63E8] flex items-center justify-center">
64
- <svg width="16" height="16" viewBox="0 0 24 24" fill="none" className="text-white">
65
- <path d="M13 2L3 14h9l-1 8 10-12h-9l1-8z" fill="currentColor"/>
66
- </svg>
67
- </div>
68
- <span className="text-[#111827] font-bold text-base">ClaudMax</span>
69
- </Link>
70
- <p className="text-[#9CA3AF] text-sm leading-relaxed mb-4">
71
- Claude API gateway powered by OpenRouter. Works with Claude Code, Cursor, Windsurf, and every Anthropic SDK.
72
- </p>
73
- <div />
74
- </div>
75
-
76
- {/* Links */}
77
- {footerLinks.map((group, i) => (
78
- <div key={i}>
79
- {Object.entries(group).map(([category, links]) => (
80
- <div key={category}>
81
- <p className="text-[#111827] text-xs font-bold uppercase tracking-widest mb-4">
82
- {category}
83
- </p>
84
- <ul className="space-y-2.5">
85
- {(links as any[]).map((link) => (
86
- <li key={link.href}>
87
- <Link
88
- href={link.href}
89
- className="text-[#6B7280] hover:text-[#5244F3] text-sm transition-colors"
90
- >
91
- {link.label}
92
- </Link>
93
- </li>
94
- ))}
95
- </ul>
96
- </div>
97
- ))}
98
- </div>
99
- ))}
100
- </div>
101
-
102
- {/* Bottom bar */}
103
- <div className="mt-10 pt-8 border-t border-gray-100 flex flex-col sm:flex-row items-center justify-between gap-4">
104
- <div className="flex flex-wrap items-center gap-4 text-xs text-[#9CA3AF]">
105
- <span>© {year} ClaudMax. All rights reserved.</span>
106
- <span className="hidden sm:inline text-gray-200">·</span>
107
- <span>API Base: <code className="font-mono text-[#6B7280]">https://api.claudmax.pro</code></span>
108
- </div>
109
- <div className="flex items-center gap-4 text-xs text-[#9CA3AF]">
110
- <Link href="/docs" className="hover:text-[#5244F3] transition-colors">Docs</Link>
111
- <Link href="/blog" className="hover:text-[#5244F3] transition-colors">Blog</Link>
112
- <Link href="/faq" className="hover:text-[#5244F3] transition-colors">FAQ</Link>
113
- {supportEnabled && <Link href="/support" className="hover:text-[#5244F3] transition-colors">Support</Link>}
114
- <Link href="/status" className="hover:text-[#5244F3] transition-colors">Status</Link>
115
- </div>
116
- </div>
117
- </div>
118
- </footer>
119
- );
120
- }
@@ -1,131 +0,0 @@
1
- 'use client';
2
-
3
- import Link from 'next/link';
4
- import { usePathname } from 'next/navigation';
5
- import { useState, useEffect } from 'react';
6
- import { BookOpen, Activity, FileText, MessageCircle, HelpCircle, PenLine, X, Menu } from 'lucide-react';
7
-
8
- export default function Header() {
9
- const pathname = usePathname();
10
- const [mobileOpen, setMobileOpen] = useState(false);
11
- const [supportEnabled, setSupportEnabled] = useState(true);
12
-
13
- useEffect(() => {
14
- fetch('/api/admin/settings')
15
- .then(r => r.ok ? r.json() : { supportEnabled: true })
16
- .then(d => setSupportEnabled(d.supportEnabled ?? true))
17
- .catch(() => setSupportEnabled(true));
18
- }, []);
19
-
20
- const baseLinks = [
21
- { href: '/docs', label: 'Documentation', icon: <BookOpen className="w-4 h-4" /> },
22
- { href: '/blog', label: 'Blog', icon: <PenLine className="w-4 h-4" /> },
23
- { href: '/faq', label: 'FAQ', icon: <HelpCircle className="w-4 h-4" /> },
24
- { href: '/support', label: 'Support', icon: <MessageCircle className="w-4 h-4" />, show: supportEnabled },
25
- { href: '/status', label: 'Status', icon: <Activity className="w-4 h-4" /> },
26
- { href: '/check-usage', label: 'Usage', icon: <FileText className="w-4 h-4" /> },
27
- ];
28
-
29
- const navLinks = baseLinks.filter(l => l.show !== false);
30
-
31
- const isActive = (href: string) => {
32
- if (href === '/') return pathname === '/';
33
- return pathname.startsWith(href);
34
- };
35
-
36
- return (
37
- <>
38
- <header className="sticky top-0 z-50 bg-white/95 backdrop-blur-md border-b border-gray-200 shrink-0">
39
- <div className="max-w-7xl mx-auto px-4 sm:px-6 h-14 flex items-center justify-between gap-4">
40
- {/* Logo */}
41
- <Link href="/" className="flex items-center gap-2.5 shrink-0">
42
- <div className="w-8 h-8 rounded-lg bg-gradient-to-br from-[#5244F3] to-[#6B63E8] flex items-center justify-center shadow-sm">
43
- <svg width="16" height="16" viewBox="0 0 24 24" fill="none" className="text-white">
44
- <path d="M13 2L3 14h9l-1 8 10-12h-9l1-8z" fill="currentColor"/>
45
- </svg>
46
- </div>
47
- <span className="text-[#111827] font-bold text-base tracking-tight">ClaudMax</span>
48
- </Link>
49
-
50
- {/* Desktop nav */}
51
- <nav className="hidden md:flex items-center gap-1">
52
- {navLinks.map((link) => (
53
- <Link
54
- key={link.href}
55
- href={link.href}
56
- className={`flex items-center gap-1.5 px-3 py-1.5 rounded-lg text-sm font-medium transition-all ${
57
- isActive(link.href)
58
- ? 'bg-purple-50 text-purple-700'
59
- : 'text-[#6B7280] hover:text-[#111827] hover:bg-gray-100'
60
- }`}
61
- >
62
- {link.icon}
63
- {link.label}
64
- </Link>
65
- ))}
66
- </nav>
67
-
68
- {/* Desktop CTA */}
69
- <div className="hidden md:flex items-center gap-2 shrink-0">
70
- <Link href="/check-usage">
71
- <button className="bg-white hover:bg-gray-50 text-[#374151] border border-gray-200 rounded-full px-4 py-1.5 text-sm font-medium transition-all">
72
- Check Usage
73
- </button>
74
- </Link>
75
- <Link href="/docs">
76
- <button className="bg-[#5244F3] hover:bg-[#4338CA] text-white rounded-full px-4 py-1.5 text-sm font-semibold transition-all shadow-sm">
77
- Get Started
78
- </button>
79
- </Link>
80
- </div>
81
-
82
- {/* Mobile hamburger */}
83
- <button
84
- onClick={() => setMobileOpen(!mobileOpen)}
85
- className="md:hidden w-9 h-9 flex items-center justify-center rounded-lg hover:bg-gray-100 transition-colors"
86
- aria-label="Toggle menu"
87
- >
88
- {mobileOpen ? (
89
- <X className="w-5 h-5 text-[#374151]" />
90
- ) : (
91
- <Menu className="w-5 h-5 text-[#374151]" />
92
- )}
93
- </button>
94
- </div>
95
- </header>
96
-
97
- {/* Mobile menu */}
98
- {mobileOpen && (
99
- <div className="md:hidden bg-white border-b border-gray-200 px-4 py-3 space-y-1">
100
- {navLinks.map((link) => (
101
- <Link
102
- key={link.href}
103
- href={link.href}
104
- onClick={() => setMobileOpen(false)}
105
- className={`flex items-center gap-2.5 px-3 py-2.5 rounded-lg text-sm font-medium transition-all ${
106
- isActive(link.href)
107
- ? 'bg-purple-50 text-purple-700'
108
- : 'text-[#6B7280] hover:text-[#111827] hover:bg-gray-50'
109
- }`}
110
- >
111
- {link.icon}
112
- {link.label}
113
- </Link>
114
- ))}
115
- <div className="flex items-center gap-2 pt-2 border-t border-gray-100 mt-2">
116
- <Link href="/check-usage" onClick={() => setMobileOpen(false)} className="flex-1">
117
- <button className="w-full bg-white hover:bg-gray-50 text-[#374151] border border-gray-200 rounded-full px-4 py-2 text-sm font-medium transition-all">
118
- Check Usage
119
- </button>
120
- </Link>
121
- <Link href="/docs" onClick={() => setMobileOpen(false)} className="flex-1">
122
- <button className="w-full bg-[#5244F3] hover:bg-[#4338CA] text-white rounded-full px-4 py-2 text-sm font-semibold transition-all">
123
- Get Started
124
- </button>
125
- </Link>
126
- </div>
127
- </div>
128
- )}
129
- </>
130
- );
131
- }
@@ -1,99 +0,0 @@
1
- 'use client';
2
-
3
- import { Code2, Globe, Zap, Shield, KeyRound, BarChart3, Terminal } from 'lucide-react';
4
- import { useEffect, useState, useRef } from 'react';
5
-
6
- const features = [
7
- {
8
- icon: Globe,
9
- title: 'OpenAI-Compatible',
10
- description:
11
- 'Drop-in replacement for OpenAI API. Change your baseURL and add your API key — everything works instantly.',
12
- color: 'from-blue-500 to-cyan-500',
13
- },
14
- {
15
- icon: Code2,
16
- title: 'Claude Code Models',
17
- description:
18
- 'Access the full lineup: Claude 3.5 Haiku, Sonnet, and Opus. All the power of Claude Code in your IDE.',
19
- color: 'from-purple-500 to-pink-500',
20
- },
21
- {
22
- icon: Zap,
23
- title: 'Blazing Fast',
24
- description:
25
- 'Optimized proxy infrastructure with sub-200ms latency. No rate limit headaches, just productive coding.',
26
- color: 'from-amber-500 to-orange-500',
27
- },
28
- {
29
- icon: KeyRound,
30
- title: 'Simple API Keys',
31
- description:
32
- 'Generate keys in seconds from your dashboard. Use them in Cursor, Cline, OpenClau, Continue.dev, and 50+ tools.',
33
- color: 'from-emerald-500 to-teal-500',
34
- },
35
- {
36
- icon: BarChart3,
37
- title: 'Usage Analytics',
38
- description:
39
- 'Track every request, token, and cost in real-time. Know exactly what you\'re spending and optimize accordingly.',
40
- color: 'from-indigo-500 to-violet-500',
41
- },
42
- {
43
- icon: Shield,
44
- title: 'Secure by Default',
45
- description:
46
- 'Keys are hashed, never logged in plaintext. Per-key rate limits and usage caps keep your account safe.',
47
- color: 'from-rose-500 to-red-500',
48
- },
49
- ];
50
-
51
- export function Features() {
52
- const [visible, setVisible] = useState(false);
53
- const ref = useRef<HTMLElement>(null);
54
-
55
- useEffect(() => {
56
- const observer = new IntersectionObserver(
57
- ([entry]) => { if (entry.isIntersecting) setVisible(true); },
58
- { threshold: 0.1 }
59
- );
60
- if (ref.current) observer.observe(ref.current);
61
- return () => observer.disconnect();
62
- }, []);
63
-
64
- return (
65
- <section ref={ref} id="features" className="py-24 relative">
66
- <div className="absolute inset-0 grid-bg" />
67
- <div className="max-w-6xl mx-auto px-4 sm:px-6 lg:px-8 relative z-10">
68
- <div className={`text-center mb-16 transition-all duration-700 ${visible ? 'opacity-100 translate-y-0' : 'opacity-0 translate-y-8'}`}>
69
- <h2 className="text-3xl sm:text-4xl font-bold mb-4">
70
- Everything you need to{' '}
71
- <span className="gradient-text">ship faster</span>
72
- </h2>
73
- <p className="text-muted-foreground text-lg max-w-xl mx-auto">
74
- Built for developers who want Claude\'s intelligence without the complexity.
75
- </p>
76
- </div>
77
-
78
- <div className="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 gap-6">
79
- {features.map((feature, i) => {
80
- const Icon = feature.icon;
81
- return (
82
- <div
83
- key={feature.title}
84
- className={`group relative rounded-2xl border border-border/50 bg-card/50 backdrop-blur p-6 hover:border-purple-500/30 transition-all duration-500 hover:shadow-lg hover:shadow-purple-500/5 ${visible ? 'opacity-100 translate-y-0' : 'opacity-0 translate-y-8'}`}
85
- style={{ transitionDelay: `${i * 80}ms` }}
86
- >
87
- <div className={`inline-flex p-2.5 rounded-xl bg-gradient-to-br ${feature.color} shadow-lg mb-4`}>
88
- <Icon className="w-5 h-5 text-white" />
89
- </div>
90
- <h3 className="text-lg font-bold mb-2">{feature.title}</h3>
91
- <p className="text-sm text-muted-foreground leading-relaxed">{feature.description}</p>
92
- </div>
93
- );
94
- })}
95
- </div>
96
- </div>
97
- </section>
98
- );
99
- }
@@ -1,32 +0,0 @@
1
- import * as React from 'react';
2
- import { cva, type VariantProps } from 'class-variance-authority';
3
- import { cn } from '@/lib/utils';
4
-
5
- const badgeVariants = cva(
6
- 'inline-flex items-center rounded-full border px-2.5 py-0.5 text-xs font-semibold transition-colors focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2',
7
- {
8
- variants: {
9
- variant: {
10
- default: 'border-transparent bg-primary text-primary-foreground',
11
- secondary: 'border-transparent bg-secondary text-secondary-foreground',
12
- destructive: 'border-transparent bg-destructive text-destructive-foreground',
13
- outline: 'text-foreground',
14
- success: 'border-transparent bg-emerald-500/20 text-emerald-400',
15
- warning: 'border-transparent bg-amber-500/20 text-amber-400',
16
- },
17
- },
18
- defaultVariants: {
19
- variant: 'default',
20
- },
21
- }
22
- );
23
-
24
- export interface BadgeProps
25
- extends React.HTMLAttributes<HTMLDivElement>,
26
- VariantProps<typeof badgeVariants> {}
27
-
28
- function Badge({ className, variant, ...props }: BadgeProps) {
29
- return <div className={cn(badgeVariants({ variant }), className)} {...props} />;
30
- }
31
-
32
- export { Badge, badgeVariants };
@@ -1,46 +0,0 @@
1
- import * as React from 'react';
2
- import { Slot } from '@radix-ui/react-slot';
3
- import { cva, type VariantProps } from 'class-variance-authority';
4
- import { cn } from '@/lib/utils';
5
-
6
- const buttonVariants = cva(
7
- 'inline-flex items-center justify-center whitespace-nowrap rounded-lg text-sm font-semibold ring-offset-background transition-all duration-200 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50',
8
- {
9
- variants: {
10
- variant: {
11
- default: 'bg-primary text-primary-foreground hover:opacity-90 shadow-lg shadow-primary/25',
12
- destructive: 'bg-destructive text-destructive-foreground hover:opacity-90',
13
- outline: 'border border-input bg-background hover:bg-accent hover:text-accent-foreground',
14
- secondary: 'bg-secondary text-secondary-foreground hover:bg-secondary/80',
15
- ghost: 'hover:bg-accent hover:text-accent-foreground',
16
- link: 'text-primary underline-offset-4 hover:underline',
17
- },
18
- size: {
19
- default: 'h-10 px-5 py-2',
20
- sm: 'h-9 rounded-md px-3',
21
- lg: 'h-12 rounded-xl px-8 text-base',
22
- icon: 'h-10 w-10',
23
- },
24
- },
25
- defaultVariants: {
26
- variant: 'default',
27
- size: 'default',
28
- },
29
- }
30
- );
31
-
32
- export interface ButtonProps
33
- extends React.ButtonHTMLAttributes<HTMLButtonElement>,
34
- VariantProps<typeof buttonVariants> {
35
- asChild?: boolean;
36
- }
37
-
38
- const Button = React.forwardRef<HTMLButtonElement, ButtonProps>(
39
- ({ className, variant, size, asChild = false, ...props }, ref) => {
40
- const Comp = asChild ? Slot : 'button';
41
- return <Comp className={cn(buttonVariants({ variant, size, className }))} ref={ref} {...props} />;
42
- }
43
- );
44
- Button.displayName = 'Button';
45
-
46
- export { Button, buttonVariants };
@@ -1,50 +0,0 @@
1
- import * as React from 'react';
2
- import { cn } from '@/lib/utils';
3
-
4
- const Card = React.forwardRef<HTMLDivElement, React.HTMLAttributes<HTMLDivElement>>(
5
- ({ className, ...props }, ref) => (
6
- <div
7
- ref={ref}
8
- className={cn('rounded-xl border bg-card text-card-foreground shadow-sm', className)}
9
- {...props}
10
- />
11
- )
12
- );
13
- Card.displayName = 'Card';
14
-
15
- const CardHeader = React.forwardRef<HTMLDivElement, React.HTMLAttributes<HTMLDivElement>>(
16
- ({ className, ...props }, ref) => (
17
- <div ref={ref} className={cn('flex flex-col space-y-1.5 p-6', className)} {...props} />
18
- )
19
- );
20
- CardHeader.displayName = 'CardHeader';
21
-
22
- const CardTitle = React.forwardRef<HTMLParagraphElement, React.HTMLAttributes<HTMLHeadingElement>>(
23
- ({ className, ...props }, ref) => (
24
- <h3 ref={ref} className={cn('text-2xl font-bold leading-none tracking-tight', className)} {...props} />
25
- )
26
- );
27
- CardTitle.displayName = 'CardTitle';
28
-
29
- const CardDescription = React.forwardRef<HTMLParagraphElement, React.HTMLAttributes<HTMLParagraphElement>>(
30
- ({ className, ...props }, ref) => (
31
- <p ref={ref} className={cn('text-sm text-muted-foreground', className)} {...props} />
32
- )
33
- );
34
- CardDescription.displayName = 'CardDescription';
35
-
36
- const CardContent = React.forwardRef<HTMLDivElement, React.HTMLAttributes<HTMLDivElement>>(
37
- ({ className, ...props }, ref) => (
38
- <div ref={ref} className={cn('p-6 pt-0', className)} {...props} />
39
- )
40
- );
41
- CardContent.displayName = 'CardContent';
42
-
43
- const CardFooter = React.forwardRef<HTMLDivElement, React.HTMLAttributes<HTMLDivElement>>(
44
- ({ className, ...props }, ref) => (
45
- <div ref={ref} className={cn('flex items-center p-6 pt-0', className)} {...props} />
46
- )
47
- );
48
- CardFooter.displayName = 'CardFooter';
49
-
50
- export { Card, CardHeader, CardFooter, CardTitle, CardDescription, CardContent };
@@ -1,97 +0,0 @@
1
- 'use client';
2
-
3
- import * as React from 'react';
4
- import * as DialogPrimitive from '@radix-ui/react-dialog';
5
- import { X } from 'lucide-react';
6
- import { cn } from '@/lib/utils';
7
-
8
- const Dialog = DialogPrimitive.Root;
9
- const DialogTrigger = DialogPrimitive.Trigger;
10
- const DialogPortal = DialogPrimitive.Portal;
11
- const DialogClose = DialogPrimitive.Close;
12
-
13
- const DialogOverlay = React.forwardRef<
14
- React.ElementRef<typeof DialogPrimitive.Overlay>,
15
- React.ComponentPropsWithoutRef<typeof DialogPrimitive.Overlay>
16
- >(({ className, ...props }, ref) => (
17
- <DialogPrimitive.Overlay
18
- ref={ref}
19
- className={cn(
20
- 'fixed inset-0 z-50 bg-black/80 backdrop-blur-sm data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0',
21
- className
22
- )}
23
- {...props}
24
- />
25
- ));
26
- DialogOverlay.displayName = DialogPrimitive.Overlay.displayName;
27
-
28
- const DialogContent = React.forwardRef<
29
- React.ElementRef<typeof DialogPrimitive.Content>,
30
- React.ComponentPropsWithoutRef<typeof DialogPrimitive.Content>
31
- >(({ className, children, ...props }, ref) => (
32
- <DialogPortal>
33
- <DialogOverlay />
34
- <DialogPrimitive.Content
35
- ref={ref}
36
- className={cn(
37
- 'fixed left-[50%] top-[50%] z-50 grid w-full max-w-lg translate-x-[-50%] translate-y-[-50%] gap-4 border bg-card shadow-2xl duration-200 data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[state=closed]:slide-out-to-left-1/2 data-[state=closed]:slide-out-to-top-[48%] data-[state=open]:slide-in-from-left-1/2 data-[state=open]:slide-in-from-top-[48%] sm:rounded-2xl',
38
- className
39
- )}
40
- {...props}
41
- >
42
- {children}
43
- <DialogPrimitive.Close className="absolute right-4 top-4 rounded-sm opacity-70 ring-offset-background transition-opacity hover:opacity-100 focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2 disabled:pointer-events-none data-[state=open]:bg-accent data-[state=open]:text-muted-foreground">
44
- <X className="h-4 w-4" />
45
- <span className="sr-only">Close</span>
46
- </DialogPrimitive.Close>
47
- </DialogPrimitive.Content>
48
- </DialogPortal>
49
- ));
50
- DialogContent.displayName = DialogPrimitive.Content.displayName;
51
-
52
- const DialogHeader = ({ className, ...props }: React.HTMLAttributes<HTMLDivElement>) => (
53
- <div className={cn('flex flex-col space-y-1.5 text-center sm:text-left', className)} {...props} />
54
- );
55
- DialogHeader.displayName = 'DialogHeader';
56
-
57
- const DialogFooter = ({ className, ...props }: React.HTMLAttributes<HTMLDivElement>) => (
58
- <div className={cn('flex flex-col-reverse sm:flex-row sm:justify-end sm:space-x-2', className)} {...props} />
59
- );
60
- DialogFooter.displayName = 'DialogFooter';
61
-
62
- const DialogTitle = React.forwardRef<
63
- React.ElementRef<typeof DialogPrimitive.Title>,
64
- React.ComponentPropsWithoutRef<typeof DialogPrimitive.Title>
65
- >(({ className, ...props }, ref) => (
66
- <DialogPrimitive.Title
67
- ref={ref}
68
- className={cn('text-lg font-semibold leading-none tracking-tight', className)}
69
- {...props}
70
- />
71
- ));
72
- DialogTitle.displayName = DialogPrimitive.Title.displayName;
73
-
74
- const DialogDescription = React.forwardRef<
75
- React.ElementRef<typeof DialogPrimitive.Description>,
76
- React.ComponentPropsWithoutRef<typeof DialogPrimitive.Description>
77
- >(({ className, ...props }, ref) => (
78
- <DialogPrimitive.Description
79
- ref={ref}
80
- className={cn('text-sm text-muted-foreground', className)}
81
- {...props}
82
- />
83
- ));
84
- DialogDescription.displayName = DialogPrimitive.Description.displayName;
85
-
86
- export {
87
- Dialog,
88
- DialogPortal,
89
- DialogOverlay,
90
- DialogClose,
91
- DialogTrigger,
92
- DialogContent,
93
- DialogHeader,
94
- DialogFooter,
95
- DialogTitle,
96
- DialogDescription,
97
- };