better-auth-studio 1.0.6 → 1.0.7
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.
- package/package.json +8 -1
- package/frontend/index.html +0 -13
- package/frontend/package-lock.json +0 -4675
- package/frontend/package.json +0 -52
- package/frontend/pnpm-lock.yaml +0 -4020
- package/frontend/postcss.config.js +0 -6
- package/frontend/src/App.tsx +0 -36
- package/frontend/src/components/CommandPalette.tsx +0 -219
- package/frontend/src/components/Layout.tsx +0 -159
- package/frontend/src/components/ui/badge.tsx +0 -40
- package/frontend/src/components/ui/button.tsx +0 -53
- package/frontend/src/components/ui/card.tsx +0 -78
- package/frontend/src/components/ui/input.tsx +0 -20
- package/frontend/src/components/ui/label.tsx +0 -19
- package/frontend/src/components/ui/select.tsx +0 -71
- package/frontend/src/index.css +0 -130
- package/frontend/src/lib/utils.ts +0 -6
- package/frontend/src/main.tsx +0 -10
- package/frontend/src/pages/Dashboard.tsx +0 -231
- package/frontend/src/pages/OrganizationDetails.tsx +0 -1281
- package/frontend/src/pages/Organizations.tsx +0 -874
- package/frontend/src/pages/Sessions.tsx +0 -623
- package/frontend/src/pages/Settings.tsx +0 -1019
- package/frontend/src/pages/TeamDetails.tsx +0 -666
- package/frontend/src/pages/Users.tsx +0 -728
- package/frontend/tailwind.config.js +0 -75
- package/frontend/tsconfig.json +0 -31
- package/frontend/tsconfig.node.json +0 -10
- package/frontend/vite.config.ts +0 -31
- package/src/auth-adapter.ts +0 -473
- package/src/cli.ts +0 -51
- package/src/config.ts +0 -320
- package/src/data.ts +0 -351
- package/src/routes.ts +0 -1585
- package/src/studio.ts +0 -86
- package/test-project/README.md +0 -0
- package/test-project/better-auth.db +0 -0
- package/test-project/better-auth_migrations/2025-08-27T15-55-04.099Z.sql +0 -7
- package/test-project/better-auth_migrations/2025-09-04T02-33-19.422Z.sql +0 -7
- package/test-project/package.json +0 -29
- package/test-project/pnpm-lock.yaml +0 -1728
- package/test-project/src/auth.ts +0 -47
- package/test-project/src/index.ts +0 -40
- package/tsconfig.json +0 -21
package/frontend/src/App.tsx
DELETED
|
@@ -1,36 +0,0 @@
|
|
|
1
|
-
import { BrowserRouter as Router, Routes, Route } from 'react-router-dom'
|
|
2
|
-
import { Toaster } from 'sonner'
|
|
3
|
-
import Layout from './components/Layout'
|
|
4
|
-
import Dashboard from './pages/Dashboard'
|
|
5
|
-
import Users from './pages/Users'
|
|
6
|
-
import Organizations from './pages/Organizations'
|
|
7
|
-
import OrganizationDetails from './pages/OrganizationDetails'
|
|
8
|
-
import TeamDetails from './pages/TeamDetails'
|
|
9
|
-
import Sessions from './pages/Sessions'
|
|
10
|
-
import Settings from './pages/Settings'
|
|
11
|
-
|
|
12
|
-
function App() {
|
|
13
|
-
return (
|
|
14
|
-
<Router>
|
|
15
|
-
<Layout>
|
|
16
|
-
<Routes>
|
|
17
|
-
<Route path="/" element={<Dashboard />} />
|
|
18
|
-
<Route path="/users" element={<Users />} />
|
|
19
|
-
<Route path="/organizations" element={<Organizations />} />
|
|
20
|
-
<Route path="/organizations/:orgId" element={<OrganizationDetails />} />
|
|
21
|
-
<Route path="/teams/:teamId" element={<TeamDetails />} />
|
|
22
|
-
<Route path="/sessions" element={<Sessions />} />
|
|
23
|
-
<Route path="/settings" element={<Settings />} />
|
|
24
|
-
</Routes>
|
|
25
|
-
</Layout>
|
|
26
|
-
<Toaster
|
|
27
|
-
theme="dark"
|
|
28
|
-
position="top-right"
|
|
29
|
-
richColors
|
|
30
|
-
closeButton
|
|
31
|
-
/>
|
|
32
|
-
</Router>
|
|
33
|
-
)
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
export default App
|
|
@@ -1,219 +0,0 @@
|
|
|
1
|
-
import { useState, useEffect } from 'react'
|
|
2
|
-
import { useNavigate } from 'react-router-dom'
|
|
3
|
-
import { Command } from 'cmdk'
|
|
4
|
-
import {
|
|
5
|
-
Search,
|
|
6
|
-
Users,
|
|
7
|
-
Building2,
|
|
8
|
-
Settings,
|
|
9
|
-
BarChart3,
|
|
10
|
-
UserPlus,
|
|
11
|
-
Mail,
|
|
12
|
-
Plus,
|
|
13
|
-
ArrowRight
|
|
14
|
-
} from 'lucide-react'
|
|
15
|
-
|
|
16
|
-
interface CommandItem {
|
|
17
|
-
id: string
|
|
18
|
-
title: string
|
|
19
|
-
description: string
|
|
20
|
-
icon: React.ComponentType<any>
|
|
21
|
-
action: () => void
|
|
22
|
-
category: string
|
|
23
|
-
keywords?: string[]
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
interface CommandPaletteProps {
|
|
27
|
-
isOpen: boolean
|
|
28
|
-
onClose: () => void
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
export default function CommandPalette({ isOpen, onClose }: CommandPaletteProps) {
|
|
32
|
-
const navigate = useNavigate()
|
|
33
|
-
const [search, setSearch] = useState('')
|
|
34
|
-
|
|
35
|
-
const commands: CommandItem[] = [
|
|
36
|
-
{
|
|
37
|
-
id: 'users',
|
|
38
|
-
title: 'Users',
|
|
39
|
-
description: 'Manage users and their accounts',
|
|
40
|
-
icon: Users,
|
|
41
|
-
action: () => navigate('/users'),
|
|
42
|
-
category: 'Navigation',
|
|
43
|
-
keywords: ['user', 'account', 'profile']
|
|
44
|
-
},
|
|
45
|
-
{
|
|
46
|
-
id: 'organizations',
|
|
47
|
-
title: 'Organizations',
|
|
48
|
-
description: 'Manage organizations and teams',
|
|
49
|
-
icon: Building2,
|
|
50
|
-
action: () => navigate('/organizations'),
|
|
51
|
-
category: 'Navigation',
|
|
52
|
-
keywords: ['org', 'company', 'team']
|
|
53
|
-
},
|
|
54
|
-
{
|
|
55
|
-
id: 'sessions',
|
|
56
|
-
title: 'Sessions',
|
|
57
|
-
description: 'View active user sessions',
|
|
58
|
-
icon: BarChart3,
|
|
59
|
-
action: () => navigate('/sessions'),
|
|
60
|
-
category: 'Navigation',
|
|
61
|
-
keywords: ['session', 'login', 'active']
|
|
62
|
-
},
|
|
63
|
-
{
|
|
64
|
-
id: 'settings',
|
|
65
|
-
title: 'Settings',
|
|
66
|
-
description: 'Configure Better Auth Studio',
|
|
67
|
-
icon: Settings,
|
|
68
|
-
action: () => navigate('/settings'),
|
|
69
|
-
category: 'Navigation',
|
|
70
|
-
keywords: ['config', 'setup', 'preferences']
|
|
71
|
-
},
|
|
72
|
-
{
|
|
73
|
-
id: 'dashboard',
|
|
74
|
-
title: 'Dashboard',
|
|
75
|
-
description: 'View overview and statistics',
|
|
76
|
-
icon: BarChart3,
|
|
77
|
-
action: () => navigate('/'),
|
|
78
|
-
category: 'Navigation',
|
|
79
|
-
keywords: ['overview', 'stats', 'home']
|
|
80
|
-
},
|
|
81
|
-
{
|
|
82
|
-
id: 'create-user',
|
|
83
|
-
title: 'Create User',
|
|
84
|
-
description: 'Add a new user to the system',
|
|
85
|
-
icon: UserPlus,
|
|
86
|
-
action: () => navigate('/users'),
|
|
87
|
-
category: 'Actions',
|
|
88
|
-
keywords: ['add', 'new', 'register']
|
|
89
|
-
},
|
|
90
|
-
{
|
|
91
|
-
id: 'create-organization',
|
|
92
|
-
title: 'Create Organization',
|
|
93
|
-
description: 'Create a new organization',
|
|
94
|
-
icon: Plus,
|
|
95
|
-
action: () => navigate('/organizations'),
|
|
96
|
-
category: 'Actions',
|
|
97
|
-
keywords: ['add', 'new', 'org']
|
|
98
|
-
},
|
|
99
|
-
{
|
|
100
|
-
id: 'invite-user',
|
|
101
|
-
title: 'Invite User',
|
|
102
|
-
description: 'Send invitation to join organization',
|
|
103
|
-
icon: Mail,
|
|
104
|
-
action: () => navigate('/organizations'),
|
|
105
|
-
category: 'Actions',
|
|
106
|
-
keywords: ['invite', 'email', 'send']
|
|
107
|
-
}
|
|
108
|
-
]
|
|
109
|
-
|
|
110
|
-
const filteredCommands = commands.filter((command) => {
|
|
111
|
-
const searchLower = search.toLowerCase()
|
|
112
|
-
return (
|
|
113
|
-
command.title.toLowerCase().includes(searchLower) ||
|
|
114
|
-
command.description.toLowerCase().includes(searchLower) ||
|
|
115
|
-
command.keywords?.some(keyword => keyword.toLowerCase().includes(searchLower))
|
|
116
|
-
)
|
|
117
|
-
})
|
|
118
|
-
|
|
119
|
-
const groupedCommands = filteredCommands.reduce((acc, command) => {
|
|
120
|
-
if (!acc[command.category]) {
|
|
121
|
-
acc[command.category] = []
|
|
122
|
-
}
|
|
123
|
-
acc[command.category].push(command)
|
|
124
|
-
return acc
|
|
125
|
-
}, {} as Record<string, CommandItem[]>)
|
|
126
|
-
|
|
127
|
-
useEffect(() => {
|
|
128
|
-
const handleKeyDown = (e: KeyboardEvent) => {
|
|
129
|
-
if ((e.metaKey || e.ctrlKey) && e.key === 'k') {
|
|
130
|
-
e.preventDefault()
|
|
131
|
-
}
|
|
132
|
-
}
|
|
133
|
-
|
|
134
|
-
document.addEventListener('keydown', handleKeyDown)
|
|
135
|
-
return () => document.removeEventListener('keydown', handleKeyDown)
|
|
136
|
-
}, [])
|
|
137
|
-
|
|
138
|
-
useEffect(() => {
|
|
139
|
-
const handleEscape = (e: KeyboardEvent) => {
|
|
140
|
-
if (e.key === 'Escape') {
|
|
141
|
-
onClose()
|
|
142
|
-
}
|
|
143
|
-
}
|
|
144
|
-
|
|
145
|
-
if (isOpen) {
|
|
146
|
-
document.addEventListener('keydown', handleEscape)
|
|
147
|
-
return () => document.removeEventListener('keydown', handleEscape)
|
|
148
|
-
}
|
|
149
|
-
}, [isOpen, onClose])
|
|
150
|
-
|
|
151
|
-
if (!isOpen) return null
|
|
152
|
-
|
|
153
|
-
return (
|
|
154
|
-
<div className="fixed inset-0 bg-black/50 flex items-start justify-center pt-[20vh] z-50">
|
|
155
|
-
<div className="bg-black/90 border border-dashed border-white/20 rounded-none w-full max-w-2xl mx-4">
|
|
156
|
-
<Command className="p-2">
|
|
157
|
-
<div className="flex items-center border-b border-dashed border-white/10 px-3 pb-3">
|
|
158
|
-
<Search className="w-4 h-4 text-gray-400 mr-3" />
|
|
159
|
-
<Command.Input
|
|
160
|
-
value={search}
|
|
161
|
-
onValueChange={setSearch}
|
|
162
|
-
placeholder="Search for actions, pages, or commands..."
|
|
163
|
-
className="flex-1 bg-transparent text-white placeholder-gray-400 outline-none"
|
|
164
|
-
autoFocus
|
|
165
|
-
/>
|
|
166
|
-
<kbd className="hidden sm:inline-flex items-center px-2 py-1 text-xs text-gray-400 border border-dashed border-white/20 rounded-sm">
|
|
167
|
-
ESC
|
|
168
|
-
</kbd>
|
|
169
|
-
</div>
|
|
170
|
-
|
|
171
|
-
<Command.List className="max-h-96 overflow-y-auto p-2">
|
|
172
|
-
{Object.keys(groupedCommands).length === 0 ? (
|
|
173
|
-
<div className="text-center py-8 text-gray-400">
|
|
174
|
-
<Search className="w-8 h-8 mx-auto mb-2 opacity-50" />
|
|
175
|
-
<p>No results found</p>
|
|
176
|
-
<p className="text-sm">Try a different search term</p>
|
|
177
|
-
</div>
|
|
178
|
-
) : (
|
|
179
|
-
Object.entries(groupedCommands).map(([category, categoryCommands]) => (
|
|
180
|
-
<div key={category} className="mb-4">
|
|
181
|
-
<Command.Group heading={category} className="text-xs text-gray-400 font-medium mb-2 px-2">
|
|
182
|
-
{categoryCommands.map((command) => {
|
|
183
|
-
const Icon = command.icon
|
|
184
|
-
return (
|
|
185
|
-
<Command.Item
|
|
186
|
-
key={command.id}
|
|
187
|
-
value={command.id}
|
|
188
|
-
onSelect={() => {
|
|
189
|
-
command.action()
|
|
190
|
-
onClose()
|
|
191
|
-
}}
|
|
192
|
-
className="flex items-center space-x-3 px-3 py-2 rounded-none hover:bg-white/5 cursor-pointer transition-colors"
|
|
193
|
-
>
|
|
194
|
-
<Icon className="w-4 h-4 text-white" />
|
|
195
|
-
<div className="flex-1">
|
|
196
|
-
<div className="text-white font-light">{command.title}</div>
|
|
197
|
-
<div className="text-sm text-gray-400">{command.description}</div>
|
|
198
|
-
</div>
|
|
199
|
-
<ArrowRight className="w-4 h-4 text-gray-400" />
|
|
200
|
-
</Command.Item>
|
|
201
|
-
)
|
|
202
|
-
})}
|
|
203
|
-
</Command.Group>
|
|
204
|
-
</div>
|
|
205
|
-
))
|
|
206
|
-
)}
|
|
207
|
-
</Command.List>
|
|
208
|
-
|
|
209
|
-
<div className="border-t border-dashed border-white/10 px-3 py-2 text-xs text-gray-400">
|
|
210
|
-
<div className="flex items-center justify-between">
|
|
211
|
-
<span>Press <kbd className="px-1 py-0.5 border border-dashed border-white/20 rounded-sm">↑↓</kbd> to navigate</span>
|
|
212
|
-
<span>Press <kbd className="px-1 py-0.5 border border-dashed border-white/20 rounded-sm">Enter</kbd> to select</span>
|
|
213
|
-
</div>
|
|
214
|
-
</div>
|
|
215
|
-
</Command>
|
|
216
|
-
</div>
|
|
217
|
-
</div>
|
|
218
|
-
)
|
|
219
|
-
}
|
|
@@ -1,159 +0,0 @@
|
|
|
1
|
-
import { ReactNode, useState, useEffect } from 'react'
|
|
2
|
-
import { Link, useLocation } from 'react-router-dom'
|
|
3
|
-
import {
|
|
4
|
-
LayoutDashboard,
|
|
5
|
-
Users,
|
|
6
|
-
Building2,
|
|
7
|
-
Settings,
|
|
8
|
-
Search
|
|
9
|
-
} from 'lucide-react'
|
|
10
|
-
import { Button } from './ui/button'
|
|
11
|
-
import { Badge } from './ui/badge'
|
|
12
|
-
import CommandPalette from './CommandPalette'
|
|
13
|
-
|
|
14
|
-
interface LayoutProps {
|
|
15
|
-
children: ReactNode
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
interface Counts {
|
|
19
|
-
users: number
|
|
20
|
-
organizations: number
|
|
21
|
-
sessions: number
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
export default function Layout({ children }: LayoutProps) {
|
|
25
|
-
const location = useLocation()
|
|
26
|
-
const [counts, setCounts] = useState<Counts>({ users: 0, organizations: 0, sessions: 0 })
|
|
27
|
-
const [loading, setLoading] = useState(true)
|
|
28
|
-
const [isCommandPaletteOpen, setIsCommandPaletteOpen] = useState(false)
|
|
29
|
-
|
|
30
|
-
useEffect(() => {
|
|
31
|
-
fetchCounts()
|
|
32
|
-
}, [])
|
|
33
|
-
|
|
34
|
-
useEffect(() => {
|
|
35
|
-
const handleKeyDown = (e: KeyboardEvent) => {
|
|
36
|
-
if ((e.metaKey || e.ctrlKey) && e.key === 'k') {
|
|
37
|
-
e.preventDefault()
|
|
38
|
-
setIsCommandPaletteOpen(true)
|
|
39
|
-
}
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
document.addEventListener('keydown', handleKeyDown)
|
|
43
|
-
return () => document.removeEventListener('keydown', handleKeyDown)
|
|
44
|
-
}, [])
|
|
45
|
-
|
|
46
|
-
const fetchCounts = async () => {
|
|
47
|
-
try {
|
|
48
|
-
const response = await fetch('/api/counts')
|
|
49
|
-
const data = await response.json()
|
|
50
|
-
setCounts(data)
|
|
51
|
-
} catch (error) {
|
|
52
|
-
console.error('Failed to fetch counts:', error)
|
|
53
|
-
} finally {
|
|
54
|
-
setLoading(false)
|
|
55
|
-
}
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
const formatCount = (count: number): string => {
|
|
59
|
-
if (count >= 1000) {
|
|
60
|
-
return (count / 1000).toFixed(1).replace(/\.0$/, '') + 'k'
|
|
61
|
-
}
|
|
62
|
-
return count.toString()
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
const navigation = [
|
|
66
|
-
{ name: 'Dashboard', href: '/', icon: LayoutDashboard },
|
|
67
|
-
{ name: 'Users', href: '/users', icon: Users, badge: loading ? '...' : formatCount(counts.users) },
|
|
68
|
-
{ name: 'Organizations', href: '/organizations', icon: Building2, badge: loading ? '...' : formatCount(counts.organizations) },
|
|
69
|
-
// { name: 'Sessions', href: '/sessions', icon: Shield, badge: loading ? '...' : formatCount(counts.sessions) },
|
|
70
|
-
{ name: 'Settings', href: '/settings', icon: Settings },
|
|
71
|
-
]
|
|
72
|
-
|
|
73
|
-
return (
|
|
74
|
-
<div className="min-h-screen bg-black">
|
|
75
|
-
{/* Top Navigation */}
|
|
76
|
-
<div className="bg-black/70 border-b border-white/15">
|
|
77
|
-
<div className="flex items-center justify-between px-6 py-4">
|
|
78
|
-
<div className="flex items-center space-x-4">
|
|
79
|
-
<div className="flex items-center space-x-2">
|
|
80
|
-
<div className="w-8 h-8 bg-white rounded-lg flex items-center justify-center">
|
|
81
|
-
<span className="text-black font-bold text-lg">⚡</span>
|
|
82
|
-
</div>
|
|
83
|
-
<div>
|
|
84
|
-
<h1 className="text-xl font-bold text-white">Better Auth Studio</h1>
|
|
85
|
-
<p className="text-xs text-gray-300">v1.0.0</p>
|
|
86
|
-
</div>
|
|
87
|
-
</div>
|
|
88
|
-
</div>
|
|
89
|
-
|
|
90
|
-
<div className="flex items-center space-x-4">
|
|
91
|
-
<div className="relative">
|
|
92
|
-
<Search className="absolute left-3 top-1/2 transform -translate-y-1/2 text-gray-400 w-4 h-4" />
|
|
93
|
-
<input
|
|
94
|
-
type="text"
|
|
95
|
-
placeholder="Search..."
|
|
96
|
-
onClick={() => setIsCommandPaletteOpen(true)}
|
|
97
|
-
className="pl-10 pr-4 py-2 bg-black border rounded-none border-gray-600 text-white border-dashed focus:ring-2 focus:ring-white focus:border-transparent transition-colors placeholder-gray-400 cursor-pointer"
|
|
98
|
-
readOnly
|
|
99
|
-
/>
|
|
100
|
-
<kbd className="absolute right-3 top-1/2 transform -translate-y-1/2 text-xs text-gray-400 border border-dashed border-white/20 rounded-sm px-1.5 py-0.5">
|
|
101
|
-
⌘K
|
|
102
|
-
</kbd>
|
|
103
|
-
</div>
|
|
104
|
-
<a href="https://better-auth.com/docs" target="_blank">
|
|
105
|
-
<Button variant="ghost" className="text-gray-300 bg-transparent hover:bg-transparent hover:bg-gray-900 border-dashed">
|
|
106
|
-
Docs
|
|
107
|
-
</Button>
|
|
108
|
-
</a>
|
|
109
|
-
<a href="https://better-auth.com/support" target="_blank">
|
|
110
|
-
<Button variant="ghost" className="text-gray-300 bg-transparent hover:bg-transparent hover:bg-gray-900 border-dashed">
|
|
111
|
-
Support
|
|
112
|
-
</Button>
|
|
113
|
-
</a>
|
|
114
|
-
</div>
|
|
115
|
-
</div>
|
|
116
|
-
</div>
|
|
117
|
-
|
|
118
|
-
{/* Top Navigation Tabs */}
|
|
119
|
-
<div className="bg-black/50 border-b border-white/10">
|
|
120
|
-
<div className="px-6">
|
|
121
|
-
<nav className="flex space-x-8">
|
|
122
|
-
{navigation.map((item) => {
|
|
123
|
-
const isActive = location.pathname === item.href
|
|
124
|
-
return (
|
|
125
|
-
<Link
|
|
126
|
-
key={item.name}
|
|
127
|
-
to={item.href}
|
|
128
|
-
className={`flex items-center space-x-2 px-3 py-4 text-sm font-medium border-b-2 transition-all duration-200 ${isActive
|
|
129
|
-
? 'border-white text-white'
|
|
130
|
-
: 'border-transparent text-gray-400 hover:text-white hover:border-gray-300'
|
|
131
|
-
}`}
|
|
132
|
-
>
|
|
133
|
-
<item.icon className="w-4 h-4" />
|
|
134
|
-
<span>{item.name}</span>
|
|
135
|
-
{item.badge && (
|
|
136
|
-
<Badge variant="secondary" className="text-xs bg-white/10 border border-white/20 rounded-sm">
|
|
137
|
-
{item.badge}
|
|
138
|
-
</Badge>
|
|
139
|
-
)}
|
|
140
|
-
</Link>
|
|
141
|
-
)
|
|
142
|
-
})}
|
|
143
|
-
</nav>
|
|
144
|
-
</div>
|
|
145
|
-
</div>
|
|
146
|
-
|
|
147
|
-
{/* Main Content */}
|
|
148
|
-
<div className="flex-1 p-0">
|
|
149
|
-
{children}
|
|
150
|
-
</div>
|
|
151
|
-
|
|
152
|
-
{/* Command Palette */}
|
|
153
|
-
<CommandPalette
|
|
154
|
-
isOpen={isCommandPaletteOpen}
|
|
155
|
-
onClose={() => setIsCommandPaletteOpen(false)}
|
|
156
|
-
/>
|
|
157
|
-
</div>
|
|
158
|
-
)
|
|
159
|
-
}
|
|
@@ -1,40 +0,0 @@
|
|
|
1
|
-
import * as React from "react"
|
|
2
|
-
import { cva, type VariantProps } from "class-variance-authority"
|
|
3
|
-
|
|
4
|
-
import { cn } from "@/lib/utils"
|
|
5
|
-
|
|
6
|
-
const badgeVariants = cva(
|
|
7
|
-
"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",
|
|
8
|
-
{
|
|
9
|
-
variants: {
|
|
10
|
-
variant: {
|
|
11
|
-
default:
|
|
12
|
-
"border-transparent bg-white text-black hover:bg-white",
|
|
13
|
-
secondary:
|
|
14
|
-
"border-transparent bg-gray-800 text-white hover:bg-gray-800 border border-gray-600",
|
|
15
|
-
destructive:
|
|
16
|
-
"border-transparent bg-red-500 text-white hover:bg-red-600",
|
|
17
|
-
outline: "text-gray-300 border-gray-600",
|
|
18
|
-
success: "border-transparent bg-green-900 text-green-300",
|
|
19
|
-
warning: "border-transparent bg-yellow-900 text-yellow-300",
|
|
20
|
-
error: "border-transparent bg-red-900 text-red-300",
|
|
21
|
-
info: "border-transparent bg-gray-800 text-white border border-gray-600",
|
|
22
|
-
},
|
|
23
|
-
},
|
|
24
|
-
defaultVariants: {
|
|
25
|
-
variant: "default",
|
|
26
|
-
},
|
|
27
|
-
}
|
|
28
|
-
)
|
|
29
|
-
|
|
30
|
-
export interface BadgeProps
|
|
31
|
-
extends React.HTMLAttributes<HTMLDivElement>,
|
|
32
|
-
VariantProps<typeof badgeVariants> {}
|
|
33
|
-
|
|
34
|
-
function Badge({ className, variant, ...props }: BadgeProps) {
|
|
35
|
-
return (
|
|
36
|
-
<div className={cn(badgeVariants({ variant }), className)} {...props} />
|
|
37
|
-
)
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
export { Badge, badgeVariants }
|
|
@@ -1,53 +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
|
-
|
|
5
|
-
import { cn } from "@/lib/utils"
|
|
6
|
-
|
|
7
|
-
const buttonVariants = cva(
|
|
8
|
-
"inline-flex items-center justify-center whitespace-nowrap rounded-lg text-sm font-medium transition-colors focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring disabled:pointer-events-none disabled:opacity-50",
|
|
9
|
-
{
|
|
10
|
-
variants: {
|
|
11
|
-
variant: {
|
|
12
|
-
default: "bg-white text-black hover:bg-white shadow",
|
|
13
|
-
destructive: "bg-red-500 text-white hover:bg-red-600 shadow",
|
|
14
|
-
outline: "border border-white bg-black hover:bg-black hover:text-white",
|
|
15
|
-
secondary: "bg-black text-white hover:bg-black shadow border border-white",
|
|
16
|
-
ghost: "hover:bg-black text-white hover:text-white",
|
|
17
|
-
link: "text-white underline-offset-4 hover:underline",
|
|
18
|
-
},
|
|
19
|
-
size: {
|
|
20
|
-
default: "h-9 px-4 py-2",
|
|
21
|
-
sm: "h-8 rounded-md px-3 text-xs",
|
|
22
|
-
lg: "h-10 rounded-md px-8",
|
|
23
|
-
icon: "h-9 w-9",
|
|
24
|
-
},
|
|
25
|
-
},
|
|
26
|
-
defaultVariants: {
|
|
27
|
-
variant: "default",
|
|
28
|
-
size: "default",
|
|
29
|
-
},
|
|
30
|
-
}
|
|
31
|
-
)
|
|
32
|
-
|
|
33
|
-
export interface ButtonProps
|
|
34
|
-
extends React.ButtonHTMLAttributes<HTMLButtonElement>,
|
|
35
|
-
VariantProps<typeof buttonVariants> {
|
|
36
|
-
asChild?: boolean
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
const Button = React.forwardRef<HTMLButtonElement, ButtonProps>(
|
|
40
|
-
({ className, variant, size, asChild = false, ...props }, ref) => {
|
|
41
|
-
const Comp = asChild ? Slot : "button"
|
|
42
|
-
return (
|
|
43
|
-
<Comp
|
|
44
|
-
className={cn(buttonVariants({ variant, size, className }))}
|
|
45
|
-
ref={ref}
|
|
46
|
-
{...props}
|
|
47
|
-
/>
|
|
48
|
-
)
|
|
49
|
-
}
|
|
50
|
-
)
|
|
51
|
-
Button.displayName = "Button"
|
|
52
|
-
|
|
53
|
-
export { Button, buttonVariants }
|
|
@@ -1,78 +0,0 @@
|
|
|
1
|
-
import * as React from "react"
|
|
2
|
-
import { cn } from "@/lib/utils"
|
|
3
|
-
|
|
4
|
-
const Card = React.forwardRef<
|
|
5
|
-
HTMLDivElement,
|
|
6
|
-
React.HTMLAttributes<HTMLDivElement>
|
|
7
|
-
>(({ className, ...props }, ref) => (
|
|
8
|
-
<div
|
|
9
|
-
ref={ref}
|
|
10
|
-
className={cn(
|
|
11
|
-
"rounded-lg border border-gray-700 bg-gray-900 text-white shadow-sm",
|
|
12
|
-
className
|
|
13
|
-
)}
|
|
14
|
-
{...props}
|
|
15
|
-
/>
|
|
16
|
-
))
|
|
17
|
-
Card.displayName = "Card"
|
|
18
|
-
|
|
19
|
-
const CardHeader = React.forwardRef<
|
|
20
|
-
HTMLDivElement,
|
|
21
|
-
React.HTMLAttributes<HTMLDivElement>
|
|
22
|
-
>(({ className, ...props }, ref) => (
|
|
23
|
-
<div
|
|
24
|
-
ref={ref}
|
|
25
|
-
className={cn("flex flex-col space-y-1.5 p-6", className)}
|
|
26
|
-
{...props}
|
|
27
|
-
/>
|
|
28
|
-
))
|
|
29
|
-
CardHeader.displayName = "CardHeader"
|
|
30
|
-
|
|
31
|
-
const CardTitle = React.forwardRef<
|
|
32
|
-
HTMLParagraphElement,
|
|
33
|
-
React.HTMLAttributes<HTMLHeadingElement>
|
|
34
|
-
>(({ className, ...props }, ref) => (
|
|
35
|
-
<h3
|
|
36
|
-
ref={ref}
|
|
37
|
-
className={cn(
|
|
38
|
-
"text-2xl font-semibold leading-none tracking-tight text-white",
|
|
39
|
-
className
|
|
40
|
-
)}
|
|
41
|
-
{...props}
|
|
42
|
-
/>
|
|
43
|
-
))
|
|
44
|
-
CardTitle.displayName = "CardTitle"
|
|
45
|
-
|
|
46
|
-
const CardDescription = React.forwardRef<
|
|
47
|
-
HTMLParagraphElement,
|
|
48
|
-
React.HTMLAttributes<HTMLParagraphElement>
|
|
49
|
-
>(({ className, ...props }, ref) => (
|
|
50
|
-
<p
|
|
51
|
-
ref={ref}
|
|
52
|
-
className={cn("text-sm text-gray-300", className)}
|
|
53
|
-
{...props}
|
|
54
|
-
/>
|
|
55
|
-
))
|
|
56
|
-
CardDescription.displayName = "CardDescription"
|
|
57
|
-
|
|
58
|
-
const CardContent = React.forwardRef<
|
|
59
|
-
HTMLDivElement,
|
|
60
|
-
React.HTMLAttributes<HTMLDivElement>
|
|
61
|
-
>(({ className, ...props }, ref) => (
|
|
62
|
-
<div ref={ref} className={cn("p-6 pt-0", className)} {...props} />
|
|
63
|
-
))
|
|
64
|
-
CardContent.displayName = "CardContent"
|
|
65
|
-
|
|
66
|
-
const CardFooter = React.forwardRef<
|
|
67
|
-
HTMLDivElement,
|
|
68
|
-
React.HTMLAttributes<HTMLDivElement>
|
|
69
|
-
>(({ className, ...props }, ref) => (
|
|
70
|
-
<div
|
|
71
|
-
ref={ref}
|
|
72
|
-
className={cn("flex items-center p-6 pt-0", className)}
|
|
73
|
-
{...props}
|
|
74
|
-
/>
|
|
75
|
-
))
|
|
76
|
-
CardFooter.displayName = "CardFooter"
|
|
77
|
-
|
|
78
|
-
export { Card, CardHeader, CardFooter, CardTitle, CardDescription, CardContent }
|
|
@@ -1,20 +0,0 @@
|
|
|
1
|
-
import * as React from "react"
|
|
2
|
-
|
|
3
|
-
export interface InputProps
|
|
4
|
-
extends React.InputHTMLAttributes<HTMLInputElement> {}
|
|
5
|
-
|
|
6
|
-
const Input = React.forwardRef<HTMLInputElement, InputProps>(
|
|
7
|
-
({ className, type, ...props }, ref) => {
|
|
8
|
-
return (
|
|
9
|
-
<input
|
|
10
|
-
type={type}
|
|
11
|
-
className={`flex h-10 w-full border border-dashed border-white/20 bg-black/30 px-3 py-2 text-sm text-white placeholder:text-gray-400 focus:outline-none focus:ring-1 focus:ring-white focus:border-white/40 disabled:cursor-not-allowed disabled:opacity-50 rounded-none ${className}`}
|
|
12
|
-
ref={ref}
|
|
13
|
-
{...props}
|
|
14
|
-
/>
|
|
15
|
-
)
|
|
16
|
-
}
|
|
17
|
-
)
|
|
18
|
-
Input.displayName = "Input"
|
|
19
|
-
|
|
20
|
-
export { Input }
|
|
@@ -1,19 +0,0 @@
|
|
|
1
|
-
import * as React from "react"
|
|
2
|
-
|
|
3
|
-
export interface LabelProps
|
|
4
|
-
extends React.LabelHTMLAttributes<HTMLLabelElement> {}
|
|
5
|
-
|
|
6
|
-
const Label = React.forwardRef<HTMLLabelElement, LabelProps>(
|
|
7
|
-
({ className, ...props }, ref) => {
|
|
8
|
-
return (
|
|
9
|
-
<label
|
|
10
|
-
className={`text-sm font-light text-white ${className}`}
|
|
11
|
-
ref={ref}
|
|
12
|
-
{...props}
|
|
13
|
-
/>
|
|
14
|
-
)
|
|
15
|
-
}
|
|
16
|
-
)
|
|
17
|
-
Label.displayName = "Label"
|
|
18
|
-
|
|
19
|
-
export { Label }
|