flowyml 1.7.2__py3-none-any.whl → 1.8.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.
Files changed (126) hide show
  1. flowyml/assets/base.py +15 -0
  2. flowyml/assets/metrics.py +5 -0
  3. flowyml/cli/main.py +709 -0
  4. flowyml/cli/stack_cli.py +138 -25
  5. flowyml/core/__init__.py +17 -0
  6. flowyml/core/executor.py +161 -26
  7. flowyml/core/image_builder.py +129 -0
  8. flowyml/core/log_streamer.py +227 -0
  9. flowyml/core/orchestrator.py +22 -2
  10. flowyml/core/pipeline.py +34 -10
  11. flowyml/core/routing.py +558 -0
  12. flowyml/core/step.py +9 -1
  13. flowyml/core/step_grouping.py +49 -35
  14. flowyml/core/types.py +407 -0
  15. flowyml/monitoring/alerts.py +10 -0
  16. flowyml/monitoring/notifications.py +104 -25
  17. flowyml/monitoring/slack_blocks.py +323 -0
  18. flowyml/plugins/__init__.py +251 -0
  19. flowyml/plugins/alerters/__init__.py +1 -0
  20. flowyml/plugins/alerters/slack.py +168 -0
  21. flowyml/plugins/base.py +752 -0
  22. flowyml/plugins/config.py +478 -0
  23. flowyml/plugins/deployers/__init__.py +22 -0
  24. flowyml/plugins/deployers/gcp_cloud_run.py +200 -0
  25. flowyml/plugins/deployers/sagemaker.py +306 -0
  26. flowyml/plugins/deployers/vertex.py +290 -0
  27. flowyml/plugins/integration.py +369 -0
  28. flowyml/plugins/manager.py +510 -0
  29. flowyml/plugins/model_registries/__init__.py +22 -0
  30. flowyml/plugins/model_registries/mlflow.py +159 -0
  31. flowyml/plugins/model_registries/sagemaker.py +489 -0
  32. flowyml/plugins/model_registries/vertex.py +386 -0
  33. flowyml/plugins/orchestrators/__init__.py +13 -0
  34. flowyml/plugins/orchestrators/sagemaker.py +443 -0
  35. flowyml/plugins/orchestrators/vertex_ai.py +461 -0
  36. flowyml/plugins/registries/__init__.py +13 -0
  37. flowyml/plugins/registries/ecr.py +321 -0
  38. flowyml/plugins/registries/gcr.py +313 -0
  39. flowyml/plugins/registry.py +454 -0
  40. flowyml/plugins/stack.py +494 -0
  41. flowyml/plugins/stack_config.py +537 -0
  42. flowyml/plugins/stores/__init__.py +13 -0
  43. flowyml/plugins/stores/gcs.py +460 -0
  44. flowyml/plugins/stores/s3.py +453 -0
  45. flowyml/plugins/trackers/__init__.py +11 -0
  46. flowyml/plugins/trackers/mlflow.py +316 -0
  47. flowyml/plugins/validators/__init__.py +3 -0
  48. flowyml/plugins/validators/deepchecks.py +119 -0
  49. flowyml/registry/__init__.py +2 -1
  50. flowyml/registry/model_environment.py +109 -0
  51. flowyml/registry/model_registry.py +241 -96
  52. flowyml/serving/__init__.py +17 -0
  53. flowyml/serving/model_server.py +628 -0
  54. flowyml/stacks/__init__.py +60 -0
  55. flowyml/stacks/aws.py +93 -0
  56. flowyml/stacks/base.py +62 -0
  57. flowyml/stacks/components.py +12 -0
  58. flowyml/stacks/gcp.py +44 -9
  59. flowyml/stacks/plugins.py +115 -0
  60. flowyml/stacks/registry.py +2 -1
  61. flowyml/storage/sql.py +401 -12
  62. flowyml/tracking/experiment.py +8 -5
  63. flowyml/ui/backend/Dockerfile +87 -16
  64. flowyml/ui/backend/auth.py +12 -2
  65. flowyml/ui/backend/main.py +149 -5
  66. flowyml/ui/backend/routers/ai_context.py +226 -0
  67. flowyml/ui/backend/routers/assets.py +23 -4
  68. flowyml/ui/backend/routers/auth.py +96 -0
  69. flowyml/ui/backend/routers/deployments.py +660 -0
  70. flowyml/ui/backend/routers/model_explorer.py +597 -0
  71. flowyml/ui/backend/routers/plugins.py +103 -51
  72. flowyml/ui/backend/routers/projects.py +91 -8
  73. flowyml/ui/backend/routers/runs.py +20 -1
  74. flowyml/ui/backend/routers/schedules.py +22 -17
  75. flowyml/ui/backend/routers/templates.py +319 -0
  76. flowyml/ui/backend/routers/websocket.py +2 -2
  77. flowyml/ui/frontend/Dockerfile +55 -6
  78. flowyml/ui/frontend/dist/assets/index-B5AsPTSz.css +1 -0
  79. flowyml/ui/frontend/dist/assets/index-dFbZ8wD8.js +753 -0
  80. flowyml/ui/frontend/dist/index.html +2 -2
  81. flowyml/ui/frontend/dist/logo.png +0 -0
  82. flowyml/ui/frontend/nginx.conf +65 -4
  83. flowyml/ui/frontend/package-lock.json +1404 -74
  84. flowyml/ui/frontend/package.json +3 -0
  85. flowyml/ui/frontend/public/logo.png +0 -0
  86. flowyml/ui/frontend/src/App.jsx +10 -7
  87. flowyml/ui/frontend/src/app/auth/Login.jsx +90 -0
  88. flowyml/ui/frontend/src/app/dashboard/page.jsx +8 -8
  89. flowyml/ui/frontend/src/app/deployments/page.jsx +786 -0
  90. flowyml/ui/frontend/src/app/model-explorer/page.jsx +1031 -0
  91. flowyml/ui/frontend/src/app/pipelines/page.jsx +12 -2
  92. flowyml/ui/frontend/src/app/projects/[projectId]/_components/ProjectExperimentsList.jsx +19 -6
  93. flowyml/ui/frontend/src/app/runs/[runId]/page.jsx +36 -24
  94. flowyml/ui/frontend/src/app/runs/page.jsx +8 -2
  95. flowyml/ui/frontend/src/app/settings/page.jsx +267 -253
  96. flowyml/ui/frontend/src/components/AssetDetailsPanel.jsx +29 -7
  97. flowyml/ui/frontend/src/components/Layout.jsx +6 -0
  98. flowyml/ui/frontend/src/components/PipelineGraph.jsx +79 -29
  99. flowyml/ui/frontend/src/components/RunDetailsPanel.jsx +36 -6
  100. flowyml/ui/frontend/src/components/RunMetaPanel.jsx +113 -0
  101. flowyml/ui/frontend/src/components/ai/AIAssistantButton.jsx +71 -0
  102. flowyml/ui/frontend/src/components/ai/AIAssistantPanel.jsx +420 -0
  103. flowyml/ui/frontend/src/components/header/Header.jsx +22 -0
  104. flowyml/ui/frontend/src/components/plugins/PluginManager.jsx +4 -4
  105. flowyml/ui/frontend/src/components/plugins/{ZenMLIntegration.jsx → StackImport.jsx} +38 -12
  106. flowyml/ui/frontend/src/components/sidebar/Sidebar.jsx +36 -13
  107. flowyml/ui/frontend/src/contexts/AIAssistantContext.jsx +245 -0
  108. flowyml/ui/frontend/src/contexts/AuthContext.jsx +108 -0
  109. flowyml/ui/frontend/src/hooks/useAIContext.js +156 -0
  110. flowyml/ui/frontend/src/hooks/useWebGPU.js +54 -0
  111. flowyml/ui/frontend/src/layouts/MainLayout.jsx +6 -0
  112. flowyml/ui/frontend/src/router/index.jsx +47 -20
  113. flowyml/ui/frontend/src/services/pluginService.js +3 -1
  114. flowyml/ui/server_manager.py +5 -5
  115. flowyml/ui/utils.py +157 -39
  116. flowyml/utils/config.py +37 -15
  117. flowyml/utils/model_introspection.py +123 -0
  118. flowyml/utils/observability.py +30 -0
  119. flowyml-1.8.0.dist-info/METADATA +174 -0
  120. {flowyml-1.7.2.dist-info → flowyml-1.8.0.dist-info}/RECORD +123 -65
  121. {flowyml-1.7.2.dist-info → flowyml-1.8.0.dist-info}/WHEEL +1 -1
  122. flowyml/ui/frontend/dist/assets/index-B40RsQDq.css +0 -1
  123. flowyml/ui/frontend/dist/assets/index-CjI0zKCn.js +0 -685
  124. flowyml-1.7.2.dist-info/METADATA +0 -477
  125. {flowyml-1.7.2.dist-info → flowyml-1.8.0.dist-info}/entry_points.txt +0 -0
  126. {flowyml-1.7.2.dist-info → flowyml-1.8.0.dist-info}/licenses/LICENSE +0 -0
@@ -1,315 +1,329 @@
1
1
  import React, { useState, useEffect } from 'react';
2
- import { Key, Copy, Trash2, Plus, Eye, EyeOff, Calendar, Shield, CheckCircle2 } from 'lucide-react';
2
+ import {
3
+ Settings as SettingsIcon,
4
+ Palette,
5
+ Bell,
6
+ Database,
7
+ Shield,
8
+ Globe,
9
+ Zap,
10
+ Clock,
11
+ Save,
12
+ RefreshCw,
13
+ CheckCircle2,
14
+ Moon,
15
+ Sun,
16
+ Monitor
17
+ } from 'lucide-react';
3
18
  import { Card } from '../../components/ui/Card';
4
19
  import { Button } from '../../components/ui/Button';
5
20
  import { Badge } from '../../components/ui/Badge';
6
- import { format } from 'date-fns';
7
21
 
8
22
  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());
23
+ const [settings, setSettings] = useState({
24
+ theme: 'system',
25
+ notificationsEnabled: true,
26
+ emailAlerts: false,
27
+ autoRefresh: true,
28
+ refreshInterval: 30,
29
+ timezone: 'auto',
30
+ dataRetention: 30,
31
+ artifactUpload: false,
32
+ debugMode: false
33
+ });
34
+ const [saved, setSaved] = useState(false);
35
+ const [serverInfo, setServerInfo] = useState(null);
15
36
 
16
37
  useEffect(() => {
17
- fetchTokens();
38
+ fetchServerInfo();
18
39
  }, []);
19
40
 
20
- const fetchTokens = async () => {
41
+ const fetchServerInfo = async () => {
21
42
  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();
43
+ const response = await fetch('/api/execution/info');
44
+ if (response.ok) {
45
+ const data = await response.json();
46
+ setServerInfo(data);
47
+ }
57
48
  } catch (error) {
58
- console.error('Failed to delete token:', error);
49
+ console.error('Failed to fetch server info:', error);
59
50
  }
60
51
  };
61
52
 
62
- const copyToClipboard = (text) => {
63
- navigator.clipboard.writeText(text);
64
- // Could add a toast notification here
53
+ const handleSettingChange = (key, value) => {
54
+ setSettings(prev => ({ ...prev, [key]: value }));
55
+ setSaved(false);
65
56
  };
66
57
 
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
- });
58
+ const saveSettings = () => {
59
+ // In a real app, this would save to the backend
60
+ localStorage.setItem('flowyml_settings', JSON.stringify(settings));
61
+ setSaved(true);
62
+ setTimeout(() => setSaved(false), 3000);
77
63
  };
78
64
 
79
- const maskToken = (token) => {
80
- if (!token || typeof token !== 'string') return '••••••••••••••••••••••••••••••••••••••••••••••••';
81
- return `${token.substring(0, 8)}${''.repeat(32)}${token.substring(token.length - 8)}`;
82
- };
83
-
84
- if (loading) {
85
- return (
86
- <div className="flex items-center justify-center min-h-screen">
87
- <div className="animate-spin rounded-full h-12 w-12 border-b-2 border-primary-600"></div>
88
- </div>
89
- );
90
- }
65
+ const ThemeButton = ({ theme, icon: Icon, label }) => (
66
+ <button
67
+ onClick={() => handleSettingChange('theme', theme)}
68
+ className={`flex flex-col items-center justify-center p-4 rounded-xl border-2 transition-all ${settings.theme === theme
69
+ ? 'border-primary-500 bg-primary-50 dark:bg-primary-900/20'
70
+ : 'border-slate-200 dark:border-slate-700 hover:border-slate-300 dark:hover:border-slate-600'
71
+ }`}
72
+ >
73
+ <Icon size={24} className={settings.theme === theme ? 'text-primary-600' : 'text-slate-500'} />
74
+ <span className={`mt-2 text-sm font-medium ${settings.theme === theme ? 'text-primary-600' : 'text-slate-600 dark:text-slate-400'
75
+ }`}>
76
+ {label}
77
+ </span>
78
+ </button>
79
+ );
91
80
 
92
81
  return (
93
- <div className="p-6 max-w-6xl mx-auto space-y-6">
82
+ <div className="p-6 max-w-4xl mx-auto space-y-6">
94
83
  {/* Header */}
95
84
  <div className="flex items-center justify-between">
96
85
  <div>
97
86
  <h1 className="text-3xl font-bold text-slate-900 dark:text-white flex items-center gap-3">
98
- <div className="p-3 bg-gradient-to-br from-primary-500 to-purple-500 rounded-xl text-white">
99
- <Key size={28} />
87
+ <div className="p-3 bg-gradient-to-br from-slate-600 to-slate-800 rounded-xl text-white">
88
+ <SettingsIcon size={28} />
100
89
  </div>
101
- API Tokens
90
+ Settings
102
91
  </h1>
103
92
  <p className="text-slate-500 dark:text-slate-400 mt-2">
104
- Manage API tokens for programmatic access to flowyml
93
+ Configure your FlowyML dashboard preferences
105
94
  </p>
106
95
  </div>
107
96
  <Button
108
- onClick={() => setShowCreateModal(true)}
97
+ onClick={saveSettings}
109
98
  className="flex items-center gap-2 bg-gradient-to-r from-primary-600 to-purple-600 hover:from-primary-700 hover:to-purple-700"
110
99
  >
111
- <Plus size={16} />
112
- Create Token
100
+ {saved ? <CheckCircle2 size={16} /> : <Save size={16} />}
101
+ {saved ? 'Saved!' : 'Save Settings'}
113
102
  </Button>
114
103
  </div>
115
104
 
116
- {/* Security Notice */}
117
- <Card className="border-l-4 border-l-amber-500 bg-amber-50 dark:bg-amber-900/10">
118
- <div className="flex items-start gap-3">
119
- <Shield className="text-amber-600 dark:text-amber-400 flex-shrink-0 mt-0.5" size={20} />
105
+ {/* Appearance */}
106
+ <Card>
107
+ <div className="flex items-center gap-3 mb-6">
108
+ <div className="p-2 bg-purple-100 dark:bg-purple-900/20 rounded-lg">
109
+ <Palette className="text-purple-600 dark:text-purple-400" size={20} />
110
+ </div>
120
111
  <div>
121
- <h3 className="font-semibold text-amber-900 dark:text-amber-200">Security Notice</h3>
122
- <p className="text-sm text-amber-800 dark:text-amber-300 mt-1">
123
- Treat your API tokens like passwords. Never share them publicly or commit them to version control.
124
- </p>
112
+ <h2 className="text-lg font-semibold text-slate-900 dark:text-white">Appearance</h2>
113
+ <p className="text-sm text-slate-500 dark:text-slate-400">Customize the look and feel</p>
125
114
  </div>
126
115
  </div>
127
- </Card>
128
116
 
129
- {/* Token List */}
130
- <div className="grid gap-4">
131
- {tokens.length === 0 ? (
132
- <Card className="text-center py-16 bg-slate-50 dark:bg-slate-800/30">
133
- <div className="mx-auto w-20 h-20 bg-slate-100 dark:bg-slate-700 rounded-2xl flex items-center justify-center mb-6">
134
- <Key className="text-slate-400" size={32} />
135
- </div>
136
- <h3 className="text-xl font-bold text-slate-900 dark:text-white mb-2">No API tokens yet</h3>
137
- <p className="text-slate-500 max-w-md mx-auto mb-6">
138
- Create your first API token to start making programmatic requests to flowyml
139
- </p>
140
- <Button
141
- onClick={() => setShowCreateModal(true)}
142
- className="bg-primary-600 hover:bg-primary-700"
143
- >
144
- <Plus size={16} className="mr-2" />
145
- Create Your First Token
146
- </Button>
147
- </Card>
148
- ) : (
149
- Array.isArray(tokens) && tokens.map((token) => (
150
- <Card key={token.id} className="hover:shadow-lg transition-all duration-200">
151
- <div className="flex items-center justify-between gap-4">
152
- <div className="flex-1 min-w-0">
153
- <div className="flex items-center gap-3 mb-3">
154
- <div className="p-2 bg-primary-50 dark:bg-primary-900/20 rounded-lg">
155
- <Key className="text-primary-600 dark:text-primary-400" size={20} />
156
- </div>
157
- <div className="flex-1 min-w-0">
158
- <h3 className="text-lg font-semibold text-slate-900 dark:text-white truncate">
159
- {token.name}
160
- </h3>
161
- <div className="flex items-center gap-2 flex-wrap">
162
- <span className="text-xs text-slate-500 dark:text-slate-400 flex items-center gap-1">
163
- <Calendar size={12} />
164
- Created {format(new Date(token.created_at), 'MMM d, yyyy')}
165
- </span>
166
- <Badge variant="secondary" className="text-xs">
167
- {token.id}
168
- </Badge>
169
- </div>
170
- </div>
171
- </div>
117
+ <div className="grid grid-cols-3 gap-4">
118
+ <ThemeButton theme="light" icon={Sun} label="Light" />
119
+ <ThemeButton theme="dark" icon={Moon} label="Dark" />
120
+ <ThemeButton theme="system" icon={Monitor} label="System" />
121
+ </div>
122
+ </Card>
172
123
 
173
- {/* Token Value */}
174
- <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">
175
- <div className="flex items-center gap-2">
176
- <code className="flex-1 truncate text-slate-700 dark:text-slate-300">
177
- {visibleTokens.has(token.id) ? token.token : maskToken(token.token)}
178
- </code>
179
- <div className="flex items-center gap-1">
180
- <button
181
- onClick={() => toggleTokenVisibility(token.id)}
182
- className="p-1.5 hover:bg-slate-200 dark:hover:bg-slate-700 rounded transition-colors"
183
- title={visibleTokens.has(token.id) ? 'Hide token' : 'Show token'}
184
- >
185
- {visibleTokens.has(token.id) ? (
186
- <EyeOff size={16} className="text-slate-500" />
187
- ) : (
188
- <Eye size={16} className="text-slate-500" />
189
- )}
190
- </button>
191
- <button
192
- onClick={() => copyToClipboard(token.token)}
193
- className="p-1.5 hover:bg-slate-200 dark:hover:bg-slate-700 rounded transition-colors"
194
- title="Copy to clipboard"
195
- >
196
- <Copy size={16} className="text-slate-500" />
197
- </button>
198
- </div>
199
- </div>
200
- </div>
201
- </div>
124
+ {/* Notifications */}
125
+ <Card>
126
+ <div className="flex items-center gap-3 mb-6">
127
+ <div className="p-2 bg-blue-100 dark:bg-blue-900/20 rounded-lg">
128
+ <Bell className="text-blue-600 dark:text-blue-400" size={20} />
129
+ </div>
130
+ <div>
131
+ <h2 className="text-lg font-semibold text-slate-900 dark:text-white">Notifications</h2>
132
+ <p className="text-sm text-slate-500 dark:text-slate-400">Manage alert preferences</p>
133
+ </div>
134
+ </div>
202
135
 
203
- <Button
204
- onClick={() => deleteToken(token.id)}
205
- variant="ghost"
206
- className="text-rose-600 hover:text-rose-700 hover:bg-rose-50 dark:hover:bg-rose-900/20 flex-shrink-0"
207
- >
208
- <Trash2 size={18} />
209
- </Button>
136
+ <div className="space-y-4">
137
+ <label className="flex items-center justify-between cursor-not-allowed opacity-60">
138
+ <div>
139
+ <div className="font-medium text-slate-900 dark:text-white flex items-center gap-2">
140
+ Push Notifications
141
+ <Badge variant="secondary" className="text-xs">Coming Soon</Badge>
210
142
  </div>
211
- </Card>
212
- ))
213
- )}
214
- </div>
215
-
216
- {/* Create Token Modal */}
217
- {showCreateModal && (
218
- <div className="fixed inset-0 bg-black/50 flex items-center justify-center z-50 p-4">
219
- <Card className="max-w-lg w-full">
220
- <h2 className="text-2xl font-bold text-slate-900 dark:text-white mb-4 flex items-center gap-2">
221
- <Plus className="text-primary-600" size={24} />
222
- Create New Token
223
- </h2>
143
+ <div className="text-sm text-slate-500">Get notified about pipeline completions</div>
144
+ </div>
145
+ <input
146
+ type="checkbox"
147
+ checked={settings.notificationsEnabled}
148
+ disabled
149
+ className="w-5 h-5 rounded text-slate-400 cursor-not-allowed"
150
+ />
151
+ </label>
224
152
 
225
- <form onSubmit={createToken} className="space-y-4">
226
- <div>
227
- <label className="block text-sm font-medium text-slate-700 dark:text-slate-300 mb-2">
228
- Token Name
229
- </label>
230
- <input
231
- type="text"
232
- value={newTokenName}
233
- onChange={(e) => setNewTokenName(e.target.value)}
234
- placeholder="e.g., Production API, CI/CD Pipeline"
235
- 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"
236
- required
237
- />
238
- <p className="text-xs text-slate-500 dark:text-slate-400 mt-1">
239
- Choose a descriptive name to identify this token's purpose
240
- </p>
153
+ <label className="flex items-center justify-between cursor-not-allowed opacity-60">
154
+ <div>
155
+ <div className="font-medium text-slate-900 dark:text-white flex items-center gap-2">
156
+ Email Alerts
157
+ <Badge variant="secondary" className="text-xs">Coming Soon</Badge>
241
158
  </div>
159
+ <div className="text-sm text-slate-500">Receive email for failed pipelines</div>
160
+ </div>
161
+ <input
162
+ type="checkbox"
163
+ checked={settings.emailAlerts}
164
+ disabled
165
+ className="w-5 h-5 rounded text-slate-400 cursor-not-allowed"
166
+ />
167
+ </label>
168
+ </div>
169
+ </Card>
242
170
 
243
- <div className="flex gap-3 justify-end pt-4">
244
- <Button
245
- type="button"
246
- variant="ghost"
247
- onClick={() => setShowCreateModal(false)}
248
- >
249
- Cancel
250
- </Button>
251
- <Button
252
- type="submit"
253
- className="bg-primary-600 hover:bg-primary-700"
254
- >
255
- Create Token
256
- </Button>
257
- </div>
258
- </form>
259
- </Card>
171
+ {/* Data & Storage */}
172
+ <Card>
173
+ <div className="flex items-center gap-3 mb-6">
174
+ <div className="p-2 bg-green-100 dark:bg-green-900/20 rounded-lg">
175
+ <Database className="text-green-600 dark:text-green-400" size={20} />
176
+ </div>
177
+ <div>
178
+ <h2 className="text-lg font-semibold text-slate-900 dark:text-white">Data & Storage</h2>
179
+ <p className="text-sm text-slate-500 dark:text-slate-400">Configure data handling</p>
180
+ </div>
260
181
  </div>
261
- )}
262
182
 
263
- {/* Token Created Success Modal */}
264
- {createdToken && (
265
- <div className="fixed inset-0 bg-black/50 flex items-center justify-center z-50 p-4">
266
- <Card className="max-w-2xl w-full">
267
- <div className="text-center mb-6">
268
- <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">
269
- <CheckCircle2 className="text-green-600 dark:text-green-400" size={32} />
183
+ <div className="space-y-4">
184
+ <label className="flex items-center justify-between cursor-not-allowed opacity-60">
185
+ <div>
186
+ <div className="font-medium text-slate-900 dark:text-white flex items-center gap-2">
187
+ Auto-upload Artifacts
188
+ <Badge variant="secondary" className="text-xs">Coming Soon</Badge>
270
189
  </div>
271
- <h2 className="text-2xl font-bold text-slate-900 dark:text-white">Token Created Successfully!</h2>
272
- <p className="text-slate-500 dark:text-slate-400 mt-2">
273
- Copy this token now. You won't be able to see it again.
274
- </p>
190
+ <div className="text-sm text-slate-500">Automatically upload artifacts to remote storage</div>
275
191
  </div>
192
+ <input
193
+ type="checkbox"
194
+ checked={settings.artifactUpload}
195
+ disabled
196
+ className="w-5 h-5 rounded text-slate-400 cursor-not-allowed"
197
+ />
198
+ </label>
276
199
 
277
- <div className="bg-slate-50 dark:bg-slate-800 rounded-lg p-4 mb-6 border-2 border-primary-200 dark:border-primary-800">
278
- <div className="flex items-center gap-2 mb-2">
279
- <code className="flex-1 font-mono text-sm break-all text-slate-900 dark:text-white">
280
- {createdToken}
281
- </code>
282
- <button
283
- onClick={() => copyToClipboard(createdToken)}
284
- className="p-2 bg-primary-600 hover:bg-primary-700 text-white rounded-lg transition-colors flex-shrink-0"
285
- title="Copy to clipboard"
286
- >
287
- <Copy size={18} />
288
- </button>
200
+ <div className="opacity-60">
201
+ <div className="flex items-center justify-between mb-2">
202
+ <div>
203
+ <div className="font-medium text-slate-900 dark:text-white flex items-center gap-2">
204
+ Data Retention
205
+ <Badge variant="secondary" className="text-xs">Coming Soon</Badge>
206
+ </div>
207
+ <div className="text-sm text-slate-500">Days to keep run history</div>
289
208
  </div>
209
+ <Badge variant="secondary">{settings.dataRetention} days</Badge>
210
+ </div>
211
+ <input
212
+ type="range"
213
+ min="7"
214
+ max="365"
215
+ value={settings.dataRetention}
216
+ disabled
217
+ className="w-full h-2 bg-slate-200 dark:bg-slate-700 rounded-lg appearance-none cursor-not-allowed"
218
+ />
219
+ </div>
220
+ </div>
221
+ </Card>
222
+
223
+ {/* Performance */}
224
+ <Card>
225
+ <div className="flex items-center gap-3 mb-6">
226
+ <div className="p-2 bg-amber-100 dark:bg-amber-900/20 rounded-lg">
227
+ <Zap className="text-amber-600 dark:text-amber-400" size={20} />
228
+ </div>
229
+ <div>
230
+ <h2 className="text-lg font-semibold text-slate-900 dark:text-white">Performance</h2>
231
+ <p className="text-sm text-slate-500 dark:text-slate-400">Dashboard behavior settings</p>
232
+ </div>
233
+ </div>
234
+
235
+ <div className="space-y-4">
236
+ <label className="flex items-center justify-between cursor-pointer">
237
+ <div>
238
+ <div className="font-medium text-slate-900 dark:text-white">Auto-refresh Data</div>
239
+ <div className="text-sm text-slate-500">Automatically refresh dashboard data</div>
290
240
  </div>
241
+ <input
242
+ type="checkbox"
243
+ checked={settings.autoRefresh}
244
+ onChange={(e) => handleSettingChange('autoRefresh', e.target.checked)}
245
+ className="w-5 h-5 rounded text-primary-600 focus:ring-primary-500"
246
+ />
247
+ </label>
291
248
 
292
- <div className="bg-amber-50 dark:bg-amber-900/10 border border-amber-200 dark:border-amber-800 rounded-lg p-4 mb-6">
293
- <div className="flex gap-2">
294
- <Shield className="text-amber-600 dark:text-amber-400 flex-shrink-0" size={20} />
295
- <div className="text-sm text-amber-800 dark:text-amber-200">
296
- <strong>Important:</strong> Store this token securely. It won't be displayed again.
249
+ {settings.autoRefresh && (
250
+ <div>
251
+ <div className="flex items-center justify-between mb-2">
252
+ <div>
253
+ <div className="font-medium text-slate-900 dark:text-white">Refresh Interval</div>
254
+ <div className="text-sm text-slate-500">Seconds between refreshes</div>
297
255
  </div>
256
+ <Badge variant="secondary">{settings.refreshInterval}s</Badge>
298
257
  </div>
258
+ <input
259
+ type="range"
260
+ min="10"
261
+ max="120"
262
+ step="10"
263
+ value={settings.refreshInterval}
264
+ onChange={(e) => handleSettingChange('refreshInterval', parseInt(e.target.value))}
265
+ className="w-full h-2 bg-slate-200 dark:bg-slate-700 rounded-lg appearance-none cursor-pointer"
266
+ />
299
267
  </div>
268
+ )}
300
269
 
301
- <Button
302
- onClick={() => {
303
- setCreatedToken(null);
304
- setShowCreateModal(false);
305
- }}
306
- className="w-full bg-primary-600 hover:bg-primary-700"
307
- >
308
- I've Saved My Token
309
- </Button>
310
- </Card>
270
+ <label className="flex items-center justify-between cursor-pointer">
271
+ <div>
272
+ <div className="font-medium text-slate-900 dark:text-white">Debug Mode</div>
273
+ <div className="text-sm text-slate-500">Show verbose logging in console</div>
274
+ </div>
275
+ <input
276
+ type="checkbox"
277
+ checked={settings.debugMode}
278
+ onChange={(e) => handleSettingChange('debugMode', e.target.checked)}
279
+ className="w-5 h-5 rounded text-primary-600 focus:ring-primary-500"
280
+ />
281
+ </label>
311
282
  </div>
312
- )}
283
+ </Card>
284
+
285
+ {/* Server Info */}
286
+ <Card className="bg-slate-50 dark:bg-slate-800/50">
287
+ <div className="flex items-center gap-3 mb-6">
288
+ <div className="p-2 bg-slate-200 dark:bg-slate-700 rounded-lg">
289
+ <Globe className="text-slate-600 dark:text-slate-400" size={20} />
290
+ </div>
291
+ <div>
292
+ <h2 className="text-lg font-semibold text-slate-900 dark:text-white">Server Information</h2>
293
+ <p className="text-sm text-slate-500 dark:text-slate-400">FlowyML backend details</p>
294
+ </div>
295
+ </div>
296
+
297
+ <div className="grid grid-cols-2 gap-4 text-sm">
298
+ <div>
299
+ <div className="text-slate-500 dark:text-slate-400">Version</div>
300
+ <div className="font-medium text-slate-900 dark:text-white">{serverInfo?.version || '0.1.0'}</div>
301
+ </div>
302
+ <div>
303
+ <div className="text-slate-500 dark:text-slate-400">Environment</div>
304
+ <div className="font-medium text-slate-900 dark:text-white">{serverInfo?.environment || 'Development'}</div>
305
+ </div>
306
+ <div>
307
+ <div className="text-slate-500 dark:text-slate-400">Database</div>
308
+ <div className="font-medium text-slate-900 dark:text-white">{serverInfo?.database || 'PostgreSQL'}</div>
309
+ </div>
310
+ <div>
311
+ <div className="text-slate-500 dark:text-slate-400">Uptime</div>
312
+ <div className="font-medium text-slate-900 dark:text-white">{serverInfo?.uptime || 'N/A'}</div>
313
+ </div>
314
+ </div>
315
+ </Card>
316
+
317
+ {/* Footer Branding */}
318
+ <div className="text-center pt-8 pb-4 border-t border-slate-200 dark:border-slate-700">
319
+ <div className="flex items-center justify-center gap-2 text-slate-500 dark:text-slate-400">
320
+ <span className="text-sm">Made with ❤️ by</span>
321
+ <span className="font-semibold text-primary-600 dark:text-primary-400">UnicoLab</span>
322
+ </div>
323
+ <p className="text-xs text-slate-400 dark:text-slate-500 mt-1">
324
+ FlowyML - Next-generation MLOps Platform
325
+ </p>
326
+ </div>
313
327
  </div>
314
328
  );
315
329
  }