flowyml 1.1.0__py3-none-any.whl
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.
- flowyml/__init__.py +207 -0
- flowyml/assets/__init__.py +22 -0
- flowyml/assets/artifact.py +40 -0
- flowyml/assets/base.py +209 -0
- flowyml/assets/dataset.py +100 -0
- flowyml/assets/featureset.py +301 -0
- flowyml/assets/metrics.py +104 -0
- flowyml/assets/model.py +82 -0
- flowyml/assets/registry.py +157 -0
- flowyml/assets/report.py +315 -0
- flowyml/cli/__init__.py +5 -0
- flowyml/cli/experiment.py +232 -0
- flowyml/cli/init.py +256 -0
- flowyml/cli/main.py +327 -0
- flowyml/cli/run.py +75 -0
- flowyml/cli/stack_cli.py +532 -0
- flowyml/cli/ui.py +33 -0
- flowyml/core/__init__.py +68 -0
- flowyml/core/advanced_cache.py +274 -0
- flowyml/core/approval.py +64 -0
- flowyml/core/cache.py +203 -0
- flowyml/core/checkpoint.py +148 -0
- flowyml/core/conditional.py +373 -0
- flowyml/core/context.py +155 -0
- flowyml/core/error_handling.py +419 -0
- flowyml/core/executor.py +354 -0
- flowyml/core/graph.py +185 -0
- flowyml/core/parallel.py +452 -0
- flowyml/core/pipeline.py +764 -0
- flowyml/core/project.py +253 -0
- flowyml/core/resources.py +424 -0
- flowyml/core/scheduler.py +630 -0
- flowyml/core/scheduler_config.py +32 -0
- flowyml/core/step.py +201 -0
- flowyml/core/step_grouping.py +292 -0
- flowyml/core/templates.py +226 -0
- flowyml/core/versioning.py +217 -0
- flowyml/integrations/__init__.py +1 -0
- flowyml/integrations/keras.py +134 -0
- flowyml/monitoring/__init__.py +1 -0
- flowyml/monitoring/alerts.py +57 -0
- flowyml/monitoring/data.py +102 -0
- flowyml/monitoring/llm.py +160 -0
- flowyml/monitoring/monitor.py +57 -0
- flowyml/monitoring/notifications.py +246 -0
- flowyml/registry/__init__.py +5 -0
- flowyml/registry/model_registry.py +491 -0
- flowyml/registry/pipeline_registry.py +55 -0
- flowyml/stacks/__init__.py +27 -0
- flowyml/stacks/base.py +77 -0
- flowyml/stacks/bridge.py +288 -0
- flowyml/stacks/components.py +155 -0
- flowyml/stacks/gcp.py +499 -0
- flowyml/stacks/local.py +112 -0
- flowyml/stacks/migration.py +97 -0
- flowyml/stacks/plugin_config.py +78 -0
- flowyml/stacks/plugins.py +401 -0
- flowyml/stacks/registry.py +226 -0
- flowyml/storage/__init__.py +26 -0
- flowyml/storage/artifacts.py +246 -0
- flowyml/storage/materializers/__init__.py +20 -0
- flowyml/storage/materializers/base.py +133 -0
- flowyml/storage/materializers/keras.py +185 -0
- flowyml/storage/materializers/numpy.py +94 -0
- flowyml/storage/materializers/pandas.py +142 -0
- flowyml/storage/materializers/pytorch.py +135 -0
- flowyml/storage/materializers/sklearn.py +110 -0
- flowyml/storage/materializers/tensorflow.py +152 -0
- flowyml/storage/metadata.py +931 -0
- flowyml/tracking/__init__.py +1 -0
- flowyml/tracking/experiment.py +211 -0
- flowyml/tracking/leaderboard.py +191 -0
- flowyml/tracking/runs.py +145 -0
- flowyml/ui/__init__.py +15 -0
- flowyml/ui/backend/Dockerfile +31 -0
- flowyml/ui/backend/__init__.py +0 -0
- flowyml/ui/backend/auth.py +163 -0
- flowyml/ui/backend/main.py +187 -0
- flowyml/ui/backend/routers/__init__.py +0 -0
- flowyml/ui/backend/routers/assets.py +45 -0
- flowyml/ui/backend/routers/execution.py +179 -0
- flowyml/ui/backend/routers/experiments.py +49 -0
- flowyml/ui/backend/routers/leaderboard.py +118 -0
- flowyml/ui/backend/routers/notifications.py +72 -0
- flowyml/ui/backend/routers/pipelines.py +110 -0
- flowyml/ui/backend/routers/plugins.py +192 -0
- flowyml/ui/backend/routers/projects.py +85 -0
- flowyml/ui/backend/routers/runs.py +66 -0
- flowyml/ui/backend/routers/schedules.py +222 -0
- flowyml/ui/backend/routers/traces.py +84 -0
- flowyml/ui/frontend/Dockerfile +20 -0
- flowyml/ui/frontend/README.md +315 -0
- flowyml/ui/frontend/dist/assets/index-DFNQnrUj.js +448 -0
- flowyml/ui/frontend/dist/assets/index-pWI271rZ.css +1 -0
- flowyml/ui/frontend/dist/index.html +16 -0
- flowyml/ui/frontend/index.html +15 -0
- flowyml/ui/frontend/nginx.conf +26 -0
- flowyml/ui/frontend/package-lock.json +3545 -0
- flowyml/ui/frontend/package.json +33 -0
- flowyml/ui/frontend/postcss.config.js +6 -0
- flowyml/ui/frontend/src/App.jsx +21 -0
- flowyml/ui/frontend/src/app/assets/page.jsx +397 -0
- flowyml/ui/frontend/src/app/dashboard/page.jsx +295 -0
- flowyml/ui/frontend/src/app/experiments/[experimentId]/page.jsx +255 -0
- flowyml/ui/frontend/src/app/experiments/page.jsx +360 -0
- flowyml/ui/frontend/src/app/leaderboard/page.jsx +133 -0
- flowyml/ui/frontend/src/app/pipelines/page.jsx +454 -0
- flowyml/ui/frontend/src/app/plugins/page.jsx +48 -0
- flowyml/ui/frontend/src/app/projects/page.jsx +292 -0
- flowyml/ui/frontend/src/app/runs/[runId]/page.jsx +682 -0
- flowyml/ui/frontend/src/app/runs/page.jsx +470 -0
- flowyml/ui/frontend/src/app/schedules/page.jsx +585 -0
- flowyml/ui/frontend/src/app/settings/page.jsx +314 -0
- flowyml/ui/frontend/src/app/tokens/page.jsx +456 -0
- flowyml/ui/frontend/src/app/traces/page.jsx +246 -0
- flowyml/ui/frontend/src/components/Layout.jsx +108 -0
- flowyml/ui/frontend/src/components/PipelineGraph.jsx +295 -0
- flowyml/ui/frontend/src/components/header/Header.jsx +72 -0
- flowyml/ui/frontend/src/components/plugins/AddPluginDialog.jsx +121 -0
- flowyml/ui/frontend/src/components/plugins/InstalledPlugins.jsx +124 -0
- flowyml/ui/frontend/src/components/plugins/PluginBrowser.jsx +167 -0
- flowyml/ui/frontend/src/components/plugins/PluginManager.jsx +60 -0
- flowyml/ui/frontend/src/components/sidebar/Sidebar.jsx +145 -0
- flowyml/ui/frontend/src/components/ui/Badge.jsx +26 -0
- flowyml/ui/frontend/src/components/ui/Button.jsx +34 -0
- flowyml/ui/frontend/src/components/ui/Card.jsx +44 -0
- flowyml/ui/frontend/src/components/ui/CodeSnippet.jsx +38 -0
- flowyml/ui/frontend/src/components/ui/CollapsibleCard.jsx +53 -0
- flowyml/ui/frontend/src/components/ui/DataView.jsx +175 -0
- flowyml/ui/frontend/src/components/ui/EmptyState.jsx +49 -0
- flowyml/ui/frontend/src/components/ui/ExecutionStatus.jsx +122 -0
- flowyml/ui/frontend/src/components/ui/KeyValue.jsx +25 -0
- flowyml/ui/frontend/src/components/ui/ProjectSelector.jsx +134 -0
- flowyml/ui/frontend/src/contexts/ProjectContext.jsx +79 -0
- flowyml/ui/frontend/src/contexts/ThemeContext.jsx +54 -0
- flowyml/ui/frontend/src/index.css +11 -0
- flowyml/ui/frontend/src/layouts/MainLayout.jsx +23 -0
- flowyml/ui/frontend/src/main.jsx +10 -0
- flowyml/ui/frontend/src/router/index.jsx +39 -0
- flowyml/ui/frontend/src/services/pluginService.js +90 -0
- flowyml/ui/frontend/src/utils/api.js +47 -0
- flowyml/ui/frontend/src/utils/cn.js +6 -0
- flowyml/ui/frontend/tailwind.config.js +31 -0
- flowyml/ui/frontend/vite.config.js +21 -0
- flowyml/ui/utils.py +77 -0
- flowyml/utils/__init__.py +67 -0
- flowyml/utils/config.py +308 -0
- flowyml/utils/debug.py +240 -0
- flowyml/utils/environment.py +346 -0
- flowyml/utils/git.py +319 -0
- flowyml/utils/logging.py +61 -0
- flowyml/utils/performance.py +314 -0
- flowyml/utils/stack_config.py +296 -0
- flowyml/utils/validation.py +270 -0
- flowyml-1.1.0.dist-info/METADATA +372 -0
- flowyml-1.1.0.dist-info/RECORD +159 -0
- flowyml-1.1.0.dist-info/WHEEL +4 -0
- flowyml-1.1.0.dist-info/entry_points.txt +3 -0
- flowyml-1.1.0.dist-info/licenses/LICENSE +17 -0
|
@@ -0,0 +1,314 @@
|
|
|
1
|
+
import React, { useState, useEffect } from 'react';
|
|
2
|
+
import { Key, Copy, Trash2, Plus, Eye, EyeOff, Calendar, Shield, CheckCircle2 } from 'lucide-react';
|
|
3
|
+
import { Card } from '../../components/ui/Card';
|
|
4
|
+
import { Button } from '../../components/ui/Button';
|
|
5
|
+
import { Badge } from '../../components/ui/Badge';
|
|
6
|
+
import { format } from 'date-fns';
|
|
7
|
+
|
|
8
|
+
export function Settings() {
|
|
9
|
+
const [tokens, setTokens] = useState([]);
|
|
10
|
+
const [loading, setLoading] = useState(true);
|
|
11
|
+
const [showCreateModal, setShowCreateModal] = useState(false);
|
|
12
|
+
const [newTokenName, setNewTokenName] = useState('');
|
|
13
|
+
const [createdToken, setCreatedToken] = useState(null);
|
|
14
|
+
const [visibleTokens, setVisibleTokens] = useState(new Set());
|
|
15
|
+
|
|
16
|
+
useEffect(() => {
|
|
17
|
+
fetchTokens();
|
|
18
|
+
}, []);
|
|
19
|
+
|
|
20
|
+
const fetchTokens = async () => {
|
|
21
|
+
try {
|
|
22
|
+
const response = await fetch('/api/execution/tokens');
|
|
23
|
+
const data = await response.json();
|
|
24
|
+
setTokens(data.tokens || []);
|
|
25
|
+
} catch (error) {
|
|
26
|
+
console.error('Failed to fetch tokens:', error);
|
|
27
|
+
} finally {
|
|
28
|
+
setLoading(false);
|
|
29
|
+
}
|
|
30
|
+
};
|
|
31
|
+
|
|
32
|
+
const createToken = async (e) => {
|
|
33
|
+
e.preventDefault();
|
|
34
|
+
try {
|
|
35
|
+
const response = await fetch('/api/execution/tokens', {
|
|
36
|
+
method: 'POST',
|
|
37
|
+
headers: { 'Content-Type': 'application/json' },
|
|
38
|
+
body: JSON.stringify({ name: newTokenName })
|
|
39
|
+
});
|
|
40
|
+
const data = await response.json();
|
|
41
|
+
setCreatedToken(data.token);
|
|
42
|
+
setNewTokenName('');
|
|
43
|
+
fetchTokens();
|
|
44
|
+
} catch (error) {
|
|
45
|
+
console.error('Failed to create token:', error);
|
|
46
|
+
}
|
|
47
|
+
};
|
|
48
|
+
|
|
49
|
+
const deleteToken = async (tokenId) => {
|
|
50
|
+
if (!confirm('Are you sure you want to delete this token? This action cannot be undone.')) return;
|
|
51
|
+
|
|
52
|
+
try {
|
|
53
|
+
await fetch(`/api/execution/tokens/${tokenId}`, {
|
|
54
|
+
method: 'DELETE'
|
|
55
|
+
});
|
|
56
|
+
fetchTokens();
|
|
57
|
+
} catch (error) {
|
|
58
|
+
console.error('Failed to delete token:', error);
|
|
59
|
+
}
|
|
60
|
+
};
|
|
61
|
+
|
|
62
|
+
const copyToClipboard = (text) => {
|
|
63
|
+
navigator.clipboard.writeText(text);
|
|
64
|
+
// Could add a toast notification here
|
|
65
|
+
};
|
|
66
|
+
|
|
67
|
+
const toggleTokenVisibility = (tokenId) => {
|
|
68
|
+
setVisibleTokens(prev => {
|
|
69
|
+
const newSet = new Set(prev);
|
|
70
|
+
if (newSet.has(tokenId)) {
|
|
71
|
+
newSet.delete(tokenId);
|
|
72
|
+
} else {
|
|
73
|
+
newSet.add(tokenId);
|
|
74
|
+
}
|
|
75
|
+
return newSet;
|
|
76
|
+
});
|
|
77
|
+
};
|
|
78
|
+
|
|
79
|
+
const maskToken = (token) => {
|
|
80
|
+
return `${token.substring(0, 8)}${'•'.repeat(32)}${token.substring(token.length - 8)}`;
|
|
81
|
+
};
|
|
82
|
+
|
|
83
|
+
if (loading) {
|
|
84
|
+
return (
|
|
85
|
+
<div className="flex items-center justify-center min-h-screen">
|
|
86
|
+
<div className="animate-spin rounded-full h-12 w-12 border-b-2 border-primary-600"></div>
|
|
87
|
+
</div>
|
|
88
|
+
);
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
return (
|
|
92
|
+
<div className="p-6 max-w-6xl mx-auto space-y-6">
|
|
93
|
+
{/* Header */}
|
|
94
|
+
<div className="flex items-center justify-between">
|
|
95
|
+
<div>
|
|
96
|
+
<h1 className="text-3xl font-bold text-slate-900 dark:text-white flex items-center gap-3">
|
|
97
|
+
<div className="p-3 bg-gradient-to-br from-primary-500 to-purple-500 rounded-xl text-white">
|
|
98
|
+
<Key size={28} />
|
|
99
|
+
</div>
|
|
100
|
+
API Tokens
|
|
101
|
+
</h1>
|
|
102
|
+
<p className="text-slate-500 dark:text-slate-400 mt-2">
|
|
103
|
+
Manage API tokens for programmatic access to flowyml
|
|
104
|
+
</p>
|
|
105
|
+
</div>
|
|
106
|
+
<Button
|
|
107
|
+
onClick={() => setShowCreateModal(true)}
|
|
108
|
+
className="flex items-center gap-2 bg-gradient-to-r from-primary-600 to-purple-600 hover:from-primary-700 hover:to-purple-700"
|
|
109
|
+
>
|
|
110
|
+
<Plus size={16} />
|
|
111
|
+
Create Token
|
|
112
|
+
</Button>
|
|
113
|
+
</div>
|
|
114
|
+
|
|
115
|
+
{/* Security Notice */}
|
|
116
|
+
<Card className="border-l-4 border-l-amber-500 bg-amber-50 dark:bg-amber-900/10">
|
|
117
|
+
<div className="flex items-start gap-3">
|
|
118
|
+
<Shield className="text-amber-600 dark:text-amber-400 flex-shrink-0 mt-0.5" size={20} />
|
|
119
|
+
<div>
|
|
120
|
+
<h3 className="font-semibold text-amber-900 dark:text-amber-200">Security Notice</h3>
|
|
121
|
+
<p className="text-sm text-amber-800 dark:text-amber-300 mt-1">
|
|
122
|
+
Treat your API tokens like passwords. Never share them publicly or commit them to version control.
|
|
123
|
+
</p>
|
|
124
|
+
</div>
|
|
125
|
+
</div>
|
|
126
|
+
</Card>
|
|
127
|
+
|
|
128
|
+
{/* Token List */}
|
|
129
|
+
<div className="grid gap-4">
|
|
130
|
+
{tokens.length === 0 ? (
|
|
131
|
+
<Card className="text-center py-16 bg-slate-50 dark:bg-slate-800/30">
|
|
132
|
+
<div className="mx-auto w-20 h-20 bg-slate-100 dark:bg-slate-700 rounded-2xl flex items-center justify-center mb-6">
|
|
133
|
+
<Key className="text-slate-400" size={32} />
|
|
134
|
+
</div>
|
|
135
|
+
<h3 className="text-xl font-bold text-slate-900 dark:text-white mb-2">No API tokens yet</h3>
|
|
136
|
+
<p className="text-slate-500 max-w-md mx-auto mb-6">
|
|
137
|
+
Create your first API token to start making programmatic requests to flowyml
|
|
138
|
+
</p>
|
|
139
|
+
<Button
|
|
140
|
+
onClick={() => setShowCreateModal(true)}
|
|
141
|
+
className="bg-primary-600 hover:bg-primary-700"
|
|
142
|
+
>
|
|
143
|
+
<Plus size={16} className="mr-2" />
|
|
144
|
+
Create Your First Token
|
|
145
|
+
</Button>
|
|
146
|
+
</Card>
|
|
147
|
+
) : (
|
|
148
|
+
tokens.map((token) => (
|
|
149
|
+
<Card key={token.id} className="hover:shadow-lg transition-all duration-200">
|
|
150
|
+
<div className="flex items-center justify-between gap-4">
|
|
151
|
+
<div className="flex-1 min-w-0">
|
|
152
|
+
<div className="flex items-center gap-3 mb-3">
|
|
153
|
+
<div className="p-2 bg-primary-50 dark:bg-primary-900/20 rounded-lg">
|
|
154
|
+
<Key className="text-primary-600 dark:text-primary-400" size={20} />
|
|
155
|
+
</div>
|
|
156
|
+
<div className="flex-1 min-w-0">
|
|
157
|
+
<h3 className="text-lg font-semibold text-slate-900 dark:text-white truncate">
|
|
158
|
+
{token.name}
|
|
159
|
+
</h3>
|
|
160
|
+
<div className="flex items-center gap-2 flex-wrap">
|
|
161
|
+
<span className="text-xs text-slate-500 dark:text-slate-400 flex items-center gap-1">
|
|
162
|
+
<Calendar size={12} />
|
|
163
|
+
Created {format(new Date(token.created_at), 'MMM d, yyyy')}
|
|
164
|
+
</span>
|
|
165
|
+
<Badge variant="secondary" className="text-xs">
|
|
166
|
+
{token.id}
|
|
167
|
+
</Badge>
|
|
168
|
+
</div>
|
|
169
|
+
</div>
|
|
170
|
+
</div>
|
|
171
|
+
|
|
172
|
+
{/* Token Value */}
|
|
173
|
+
<div className="bg-slate-50 dark:bg-slate-800 rounded-lg p-3 font-mono text-sm border border-slate-200 dark:border-slate-700">
|
|
174
|
+
<div className="flex items-center gap-2">
|
|
175
|
+
<code className="flex-1 truncate text-slate-700 dark:text-slate-300">
|
|
176
|
+
{visibleTokens.has(token.id) ? token.token : maskToken(token.token)}
|
|
177
|
+
</code>
|
|
178
|
+
<div className="flex items-center gap-1">
|
|
179
|
+
<button
|
|
180
|
+
onClick={() => toggleTokenVisibility(token.id)}
|
|
181
|
+
className="p-1.5 hover:bg-slate-200 dark:hover:bg-slate-700 rounded transition-colors"
|
|
182
|
+
title={visibleTokens.has(token.id) ? 'Hide token' : 'Show token'}
|
|
183
|
+
>
|
|
184
|
+
{visibleTokens.has(token.id) ? (
|
|
185
|
+
<EyeOff size={16} className="text-slate-500" />
|
|
186
|
+
) : (
|
|
187
|
+
<Eye size={16} className="text-slate-500" />
|
|
188
|
+
)}
|
|
189
|
+
</button>
|
|
190
|
+
<button
|
|
191
|
+
onClick={() => copyToClipboard(token.token)}
|
|
192
|
+
className="p-1.5 hover:bg-slate-200 dark:hover:bg-slate-700 rounded transition-colors"
|
|
193
|
+
title="Copy to clipboard"
|
|
194
|
+
>
|
|
195
|
+
<Copy size={16} className="text-slate-500" />
|
|
196
|
+
</button>
|
|
197
|
+
</div>
|
|
198
|
+
</div>
|
|
199
|
+
</div>
|
|
200
|
+
</div>
|
|
201
|
+
|
|
202
|
+
<Button
|
|
203
|
+
onClick={() => deleteToken(token.id)}
|
|
204
|
+
variant="ghost"
|
|
205
|
+
className="text-rose-600 hover:text-rose-700 hover:bg-rose-50 dark:hover:bg-rose-900/20 flex-shrink-0"
|
|
206
|
+
>
|
|
207
|
+
<Trash2 size={18} />
|
|
208
|
+
</Button>
|
|
209
|
+
</div>
|
|
210
|
+
</Card>
|
|
211
|
+
))
|
|
212
|
+
)}
|
|
213
|
+
</div>
|
|
214
|
+
|
|
215
|
+
{/* Create Token Modal */}
|
|
216
|
+
{showCreateModal && (
|
|
217
|
+
<div className="fixed inset-0 bg-black/50 flex items-center justify-center z-50 p-4">
|
|
218
|
+
<Card className="max-w-lg w-full">
|
|
219
|
+
<h2 className="text-2xl font-bold text-slate-900 dark:text-white mb-4 flex items-center gap-2">
|
|
220
|
+
<Plus className="text-primary-600" size={24} />
|
|
221
|
+
Create New Token
|
|
222
|
+
</h2>
|
|
223
|
+
|
|
224
|
+
<form onSubmit={createToken} className="space-y-4">
|
|
225
|
+
<div>
|
|
226
|
+
<label className="block text-sm font-medium text-slate-700 dark:text-slate-300 mb-2">
|
|
227
|
+
Token Name
|
|
228
|
+
</label>
|
|
229
|
+
<input
|
|
230
|
+
type="text"
|
|
231
|
+
value={newTokenName}
|
|
232
|
+
onChange={(e) => setNewTokenName(e.target.value)}
|
|
233
|
+
placeholder="e.g., Production API, CI/CD Pipeline"
|
|
234
|
+
className="w-full px-4 py-2 border border-slate-300 dark:border-slate-600 rounded-lg focus:ring-2 focus:ring-primary-500 focus:border-transparent bg-white dark:bg-slate-800 text-slate-900 dark:text-white"
|
|
235
|
+
required
|
|
236
|
+
/>
|
|
237
|
+
<p className="text-xs text-slate-500 dark:text-slate-400 mt-1">
|
|
238
|
+
Choose a descriptive name to identify this token's purpose
|
|
239
|
+
</p>
|
|
240
|
+
</div>
|
|
241
|
+
|
|
242
|
+
<div className="flex gap-3 justify-end pt-4">
|
|
243
|
+
<Button
|
|
244
|
+
type="button"
|
|
245
|
+
variant="ghost"
|
|
246
|
+
onClick={() => setShowCreateModal(false)}
|
|
247
|
+
>
|
|
248
|
+
Cancel
|
|
249
|
+
</Button>
|
|
250
|
+
<Button
|
|
251
|
+
type="submit"
|
|
252
|
+
className="bg-primary-600 hover:bg-primary-700"
|
|
253
|
+
>
|
|
254
|
+
Create Token
|
|
255
|
+
</Button>
|
|
256
|
+
</div>
|
|
257
|
+
</form>
|
|
258
|
+
</Card>
|
|
259
|
+
</div>
|
|
260
|
+
)}
|
|
261
|
+
|
|
262
|
+
{/* Token Created Success Modal */}
|
|
263
|
+
{createdToken && (
|
|
264
|
+
<div className="fixed inset-0 bg-black/50 flex items-center justify-center z-50 p-4">
|
|
265
|
+
<Card className="max-w-2xl w-full">
|
|
266
|
+
<div className="text-center mb-6">
|
|
267
|
+
<div className="mx-auto w-16 h-16 bg-green-100 dark:bg-green-900/20 rounded-full flex items-center justify-center mb-4">
|
|
268
|
+
<CheckCircle2 className="text-green-600 dark:text-green-400" size={32} />
|
|
269
|
+
</div>
|
|
270
|
+
<h2 className="text-2xl font-bold text-slate-900 dark:text-white">Token Created Successfully!</h2>
|
|
271
|
+
<p className="text-slate-500 dark:text-slate-400 mt-2">
|
|
272
|
+
Copy this token now. You won't be able to see it again.
|
|
273
|
+
</p>
|
|
274
|
+
</div>
|
|
275
|
+
|
|
276
|
+
<div className="bg-slate-50 dark:bg-slate-800 rounded-lg p-4 mb-6 border-2 border-primary-200 dark:border-primary-800">
|
|
277
|
+
<div className="flex items-center gap-2 mb-2">
|
|
278
|
+
<code className="flex-1 font-mono text-sm break-all text-slate-900 dark:text-white">
|
|
279
|
+
{createdToken}
|
|
280
|
+
</code>
|
|
281
|
+
<button
|
|
282
|
+
onClick={() => copyToClipboard(createdToken)}
|
|
283
|
+
className="p-2 bg-primary-600 hover:bg-primary-700 text-white rounded-lg transition-colors flex-shrink-0"
|
|
284
|
+
title="Copy to clipboard"
|
|
285
|
+
>
|
|
286
|
+
<Copy size={18} />
|
|
287
|
+
</button>
|
|
288
|
+
</div>
|
|
289
|
+
</div>
|
|
290
|
+
|
|
291
|
+
<div className="bg-amber-50 dark:bg-amber-900/10 border border-amber-200 dark:border-amber-800 rounded-lg p-4 mb-6">
|
|
292
|
+
<div className="flex gap-2">
|
|
293
|
+
<Shield className="text-amber-600 dark:text-amber-400 flex-shrink-0" size={20} />
|
|
294
|
+
<div className="text-sm text-amber-800 dark:text-amber-200">
|
|
295
|
+
<strong>Important:</strong> Store this token securely. It won't be displayed again.
|
|
296
|
+
</div>
|
|
297
|
+
</div>
|
|
298
|
+
</div>
|
|
299
|
+
|
|
300
|
+
<Button
|
|
301
|
+
onClick={() => {
|
|
302
|
+
setCreatedToken(null);
|
|
303
|
+
setShowCreateModal(false);
|
|
304
|
+
}}
|
|
305
|
+
className="w-full bg-primary-600 hover:bg-primary-700"
|
|
306
|
+
>
|
|
307
|
+
I've Saved My Token
|
|
308
|
+
</Button>
|
|
309
|
+
</Card>
|
|
310
|
+
</div>
|
|
311
|
+
)}
|
|
312
|
+
</div>
|
|
313
|
+
);
|
|
314
|
+
}
|