@lastbrain/ai-ui-react 1.0.41 → 1.0.43

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.
@@ -7,6 +7,7 @@
7
7
 
8
8
  import React from "react";
9
9
  import { useLB } from "../context/LBAuthProvider";
10
+ import type { LBApiKey } from "@lastbrain/ai-ui-core";
10
11
 
11
12
  interface LBConnectButtonProps {
12
13
  /** Texte du bouton */
@@ -78,14 +79,14 @@ interface LBAuthModalProps {
78
79
  }
79
80
 
80
81
  function LBAuthModal({ onClose }: LBAuthModalProps) {
81
- const { login, fetchApiKeys, selectApiKey, status } = useLB();
82
+ const { login, fetchApiKeys, selectApiKey } = useLB();
82
83
  const [step, setStep] = React.useState<"login" | "select-key">("login");
83
84
  const [email, setEmail] = React.useState("");
84
85
  const [password, setPassword] = React.useState("");
85
86
  const [error, setError] = React.useState("");
86
87
  const [loading, setLoading] = React.useState(false);
87
88
  const [accessToken, setAccessToken] = React.useState("");
88
- const [apiKeys, setApiKeys] = React.useState<any[]>([]);
89
+ const [apiKeys, setApiKeys] = React.useState<LBApiKey[]>([]);
89
90
 
90
91
  const handleLogin = async (e: React.FormEvent) => {
91
92
  e.preventDefault();
@@ -94,21 +95,14 @@ function LBAuthModal({ onClose }: LBAuthModalProps) {
94
95
 
95
96
  try {
96
97
  const result = await login(email, password);
97
- setAccessToken(result.accessToken);
98
-
99
- // Récupérer les clés API
100
- const keys = await fetchApiKeys(result.accessToken);
101
- setApiKeys(keys);
102
-
103
- if (keys.length === 0) {
104
- setError(
105
- "Aucune clé API active trouvée. Créez-en une dans votre dashboard LastBrain."
106
- );
98
+ if (!result.success) {
99
+ setError(result.error || "Échec de la connexion");
107
100
  return;
108
101
  }
109
102
 
110
- // Passer à l'étape de sélection
111
- setStep("select-key");
103
+ // Note: avec le nouveau système, login gère automatiquement
104
+ // la récupération et sélection de clé. Ce modal n'est plus nécessaire.
105
+ onClose(true);
112
106
  } catch (err) {
113
107
  setError(err instanceof Error ? err.message : "Échec de la connexion");
114
108
  } finally {
@@ -131,24 +125,32 @@ function LBAuthModal({ onClose }: LBAuthModalProps) {
131
125
  };
132
126
 
133
127
  return (
134
- <div className="fixed inset-0 bg-gradient-to-br from-slate-900/95 via-purple-900/95 to-slate-900/95 backdrop-blur-sm flex items-center justify-center z-50 p-4">
135
- <div className="bg-white dark:bg-gradient-to-br dark:from-slate-800 dark:to-slate-900 rounded-2xl shadow-2xl max-w-md w-full border border-slate-200 dark:border-slate-700 overflow-hidden">
128
+ <div className="fixed inset-0 bg-black/60 dark:bg-black/80 backdrop-blur-md flex items-center justify-center z-50 p-4 animate-in fade-in duration-200">
129
+ <div className="bg-white/95 dark:bg-slate-900/95 backdrop-blur-xl rounded-3xl shadow-2xl max-w-md w-full border border-slate-200/50 dark:border-slate-700/50 overflow-hidden animate-in zoom-in-95 duration-200">
136
130
  {/* Header avec gradient */}
137
- <div className="bg-gradient-to-r from-violet-500 to-purple-600 p-6 text-white">
138
- <div className="flex justify-between items-center">
139
- <div>
140
- <h2 className="text-2xl font-bold flex items-center gap-2">
141
- {step === "login" ? "🔐 Connexion" : "🔑 Sélection clé API"}
142
- </h2>
143
- <p className="text-violet-100 text-sm mt-1">
131
+ <div className="relative bg-gradient-to-r from-violet-600 via-purple-600 to-fuchsia-600 dark:from-violet-500 dark:via-purple-500 dark:to-fuchsia-500 px-6 py-8 text-white overflow-hidden">
132
+ {/* Pattern background */}
133
+ <div className="absolute inset-0 bg-[url('data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iNjAiIGhlaWdodD0iNjAiIHZpZXdCb3g9IjAgMCA2MCA2MCIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj48ZyBmaWxsPSJub25lIiBmaWxsLXJ1bGU9ImV2ZW5vZGQiPjxnIGZpbGw9IiNmZmYiIGZpbGwtb3BhY2l0eT0iMC4wNSI+PHBhdGggZD0iTTM2IDE0YzAtMTEuMDUgOC45NS0yMCAyMC0yMHMyMCA4Ljk1IDIwIDIwLTguOTUgMjAtMjAgMjAtMjAtOC45NS0yMC0yMHoiLz48L2c+PC9nPjwvc3ZnPg==')] opacity-30"></div>
134
+
135
+ <div className="relative flex justify-between items-start">
136
+ <div className="flex-1">
137
+ <div className="flex items-center gap-3 mb-2">
138
+ <div className="w-10 h-10 rounded-xl bg-white/20 backdrop-blur-sm flex items-center justify-center">
139
+ {step === "login" ? "🔐" : "🔑"}
140
+ </div>
141
+ <h2 className="text-2xl font-bold tracking-tight">
142
+ {step === "login" ? "Connexion" : "Sélection clé"}
143
+ </h2>
144
+ </div>
145
+ <p className="text-white/90 text-sm font-medium mt-1 ml-13">
144
146
  {step === "login"
145
147
  ? "Accédez à vos outils IA"
146
- : "Créez une session sécurisée 72h"}
148
+ : "Session sécurisée 72h"}
147
149
  </p>
148
150
  </div>
149
151
  <button
150
152
  onClick={() => onClose(false)}
151
- className="text-white/80 hover:text-white hover:bg-white/20 rounded-lg p-2 transition-colors"
153
+ className="text-white/80 hover:text-white hover:bg-white/20 rounded-lg p-2 transition-all duration-200 backdrop-blur-sm"
152
154
  >
153
155
  <svg
154
156
  className="w-5 h-5"
@@ -159,7 +161,7 @@ function LBAuthModal({ onClose }: LBAuthModalProps) {
159
161
  <path
160
162
  strokeLinecap="round"
161
163
  strokeLinejoin="round"
162
- strokeWidth={2}
164
+ strokeWidth={2.5}
163
165
  d="M6 18L18 6M6 6l12 12"
164
166
  />
165
167
  </svg>
@@ -167,43 +169,49 @@ function LBAuthModal({ onClose }: LBAuthModalProps) {
167
169
  </div>
168
170
  </div>
169
171
 
170
- <div className="p-6">
172
+ <div className="p-8">
171
173
  {step === "login" ? (
172
- <form onSubmit={handleLogin} className="space-y-4">
173
- <div>
174
- <label className="block text-sm font-semibold mb-2 text-slate-700 dark:text-slate-300">
175
- 📧 Email
174
+ <form onSubmit={handleLogin} className="space-y-5">
175
+ <div className="space-y-2">
176
+ <label className="block text-sm font-semibold text-slate-700 dark:text-slate-200 mb-2 tracking-wide">
177
+ 📧 Adresse email
176
178
  </label>
177
179
  <input
178
180
  type="email"
179
181
  value={email}
180
182
  onChange={(e) => setEmail(e.target.value)}
181
- className="w-full px-4 py-3 border-2 border-slate-200 dark:border-slate-600 rounded-lg dark:bg-slate-800 dark:text-white focus:border-violet-500 focus:ring-2 focus:ring-violet-200 dark:focus:ring-violet-900 transition-all outline-none"
183
+ className="w-full px-4 py-3.5 text-base border-2 border-slate-200 dark:border-slate-600 rounded-xl bg-white dark:bg-slate-800 text-slate-900 dark:text-white placeholder:text-slate-400 dark:placeholder:text-slate-500 focus:border-violet-500 dark:focus:border-violet-400 focus:ring-4 focus:ring-violet-100 dark:focus:ring-violet-900/30 transition-all outline-none font-medium tracking-wide"
182
184
  placeholder="votre@email.com"
183
185
  required
184
186
  autoFocus
187
+ autoComplete="email"
188
+ style={{ letterSpacing: "0.01em" }}
185
189
  />
186
190
  </div>
187
191
 
188
- <div>
189
- <label className="block text-sm font-semibold mb-2 text-slate-700 dark:text-slate-300">
192
+ <div className="space-y-2">
193
+ <label className="block text-sm font-semibold text-slate-700 dark:text-slate-200 mb-2 tracking-wide">
190
194
  🔒 Mot de passe
191
195
  </label>
192
196
  <input
193
197
  type="password"
194
198
  value={password}
195
199
  onChange={(e) => setPassword(e.target.value)}
196
- className="w-full px-4 py-3 border-2 border-slate-200 dark:border-slate-600 rounded-lg dark:bg-slate-800 dark:text-white focus:border-violet-500 focus:ring-2 focus:ring-violet-200 dark:focus:ring-violet-900 transition-all outline-none"
200
+ className="w-full px-4 py-3.5 text-base border-2 border-slate-200 dark:border-slate-600 rounded-xl bg-white dark:bg-slate-800 text-slate-900 dark:text-white placeholder:text-slate-400 dark:placeholder:text-slate-500 focus:border-violet-500 dark:focus:border-violet-400 focus:ring-4 focus:ring-violet-100 dark:focus:ring-violet-900/30 transition-all outline-none font-medium tracking-wide"
197
201
  placeholder="••••••••"
198
202
  required
203
+ autoComplete="current-password"
204
+ style={{ letterSpacing: "0.15em" }}
199
205
  />
200
206
  </div>
201
207
 
202
208
  {error && (
203
- <div className="bg-red-50 dark:bg-red-900/20 border-l-4 border-red-500 p-4 rounded">
204
- <div className="flex items-start gap-2">
205
- <span className="text-red-500 text-xl">⚠️</span>
206
- <p className="text-red-700 dark:text-red-400 text-sm font-medium">
209
+ <div className="bg-red-50 dark:bg-red-900/20 border-l-4 border-red-500 dark:border-red-400 px-4 py-3.5 rounded-lg animate-in slide-in-from-top-2 duration-200">
210
+ <div className="flex items-start gap-3">
211
+ <span className="text-red-500 dark:text-red-400 text-xl flex-shrink-0 mt-0.5">
212
+ ⚠️
213
+ </span>
214
+ <p className="text-red-700 dark:text-red-300 text-sm font-medium leading-relaxed">
207
215
  {error}
208
216
  </p>
209
217
  </div>
@@ -213,10 +221,10 @@ function LBAuthModal({ onClose }: LBAuthModalProps) {
213
221
  <button
214
222
  type="submit"
215
223
  disabled={loading}
216
- className="w-full px-6 py-3 bg-gradient-to-r from-violet-500 to-purple-600 text-white font-semibold rounded-lg hover:from-violet-600 hover:to-purple-700 disabled:from-slate-400 disabled:to-slate-500 disabled:cursor-not-allowed transition-all duration-200 shadow-lg hover:shadow-xl transform hover:-translate-y-0.5"
224
+ className="w-full mt-6 px-6 py-4 bg-gradient-to-r from-violet-600 to-purple-600 hover:from-violet-700 hover:to-purple-700 disabled:from-slate-400 disabled:to-slate-500 text-white font-bold text-base rounded-xl disabled:cursor-not-allowed transition-all duration-200 shadow-lg shadow-violet-500/30 hover:shadow-xl hover:shadow-violet-500/40 disabled:shadow-none transform hover:translate-y-[-2px] active:translate-y-0 disabled:translate-y-0 tracking-wide"
217
225
  >
218
226
  {loading ? (
219
- <span className="flex items-center justify-center gap-2">
227
+ <span className="flex items-center justify-center gap-3">
220
228
  <svg
221
229
  className="animate-spin h-5 w-5"
222
230
  xmlns="http://www.w3.org/2000/svg"
@@ -237,33 +245,35 @@ function LBAuthModal({ onClose }: LBAuthModalProps) {
237
245
  d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z"
238
246
  ></path>
239
247
  </svg>
240
- Connexion en cours...
248
+ <span className="font-semibold">Connexion en cours...</span>
241
249
  </span>
242
250
  ) : (
243
- "🚀 Se connecter"
251
+ <span className="flex items-center justify-center gap-2">
252
+ 🚀 <span>Se connecter</span>
253
+ </span>
244
254
  )}
245
255
  </button>
246
256
 
247
- <div className="relative my-6">
257
+ <div className="relative my-8">
248
258
  <div className="absolute inset-0 flex items-center">
249
259
  <div className="w-full border-t border-slate-200 dark:border-slate-700"></div>
250
260
  </div>
251
261
  <div className="relative flex justify-center text-sm">
252
- <span className="px-4 bg-white dark:bg-slate-800 text-slate-500">
262
+ <span className="px-4 bg-white dark:bg-slate-900 text-slate-500 dark:text-slate-400 font-medium">
253
263
  ou
254
264
  </span>
255
265
  </div>
256
266
  </div>
257
267
 
258
- <div className="bg-gradient-to-r from-violet-50 to-purple-50 dark:from-violet-900/20 dark:to-purple-900/20 rounded-lg p-4 border border-violet-200 dark:border-violet-800">
259
- <p className="text-sm text-slate-700 dark:text-slate-300 text-center">
268
+ <div className="bg-gradient-to-br from-violet-50 to-purple-50 dark:from-violet-900/20 dark:to-purple-900/20 rounded-xl p-5 border border-violet-200 dark:border-violet-800/50 backdrop-blur-sm">
269
+ <p className="text-sm text-slate-700 dark:text-slate-300 text-center font-medium mb-3">
260
270
  Pas encore de compte ?
261
271
  </p>
262
272
  <a
263
- href="https://lastbrain.io/signup"
273
+ href="https://prompt.lastbrain.io/signup"
264
274
  target="_blank"
265
275
  rel="noopener noreferrer"
266
- className="block mt-2 text-center text-violet-600 dark:text-violet-400 hover:text-violet-700 dark:hover:text-violet-300 font-semibold text-sm hover:underline"
276
+ className="block text-center px-4 py-3 bg-white dark:bg-slate-800 text-violet-600 dark:text-violet-400 hover:text-violet-700 dark:hover:text-violet-300 font-bold text-sm rounded-lg hover:shadow-md transition-all duration-200 border-2 border-violet-200 dark:border-violet-700"
267
277
  >
268
278
  ✨ Créer un compte gratuitement
269
279
  </a>
@@ -0,0 +1,399 @@
1
+ "use client";
2
+
3
+ import { useState } from "react";
4
+ import { useLB } from "../context/LBAuthProvider";
5
+
6
+ export interface LBSigninModalProps {
7
+ isOpen: boolean;
8
+ onClose: () => void;
9
+ }
10
+
11
+ export function LBSigninModal({ isOpen, onClose }: LBSigninModalProps) {
12
+ // Vérifier si LBProvider est disponible
13
+ let login:
14
+ | ((
15
+ email: string,
16
+ password: string
17
+ ) => Promise<{ success: boolean; error?: string }>)
18
+ | undefined;
19
+
20
+ try {
21
+ const lbContext = useLB();
22
+ login = lbContext.login;
23
+ } catch {
24
+ // LBProvider n'est pas disponible, ne pas rendre le modal
25
+ return null;
26
+ }
27
+
28
+ const [email, setEmail] = useState("");
29
+ const [password, setPassword] = useState("");
30
+ const [loading, setLoading] = useState(false);
31
+ const [error, setError] = useState("");
32
+
33
+ if (!isOpen || !login) return null;
34
+
35
+ const handleSubmit = async (e: React.FormEvent) => {
36
+ e.preventDefault();
37
+ setError("");
38
+ setLoading(true);
39
+
40
+ try {
41
+ const result = await login(email, password);
42
+ if (result.success) {
43
+ onClose();
44
+ } else {
45
+ setError(result.error || "Échec de la connexion");
46
+ }
47
+ } catch (err) {
48
+ setError(
49
+ err instanceof Error ? err.message : "Une erreur s'est produite"
50
+ );
51
+ } finally {
52
+ setLoading(false);
53
+ }
54
+ };
55
+
56
+ const handleKeyDown = (e: React.KeyboardEvent) => {
57
+ if (e.key === "Escape") {
58
+ onClose();
59
+ }
60
+ };
61
+
62
+ return (
63
+ <div
64
+ style={{
65
+ position: "fixed",
66
+ inset: 0,
67
+ backgroundColor: "rgba(0, 0, 0, 0.6)",
68
+ backdropFilter: "blur(8px)",
69
+ WebkitBackdropFilter: "blur(8px)",
70
+ display: "flex",
71
+ alignItems: "center",
72
+ justifyContent: "center",
73
+ zIndex: 9999,
74
+ padding: "16px",
75
+ }}
76
+ onClick={onClose}
77
+ onKeyDown={handleKeyDown}
78
+ >
79
+ <div
80
+ style={{
81
+ backgroundColor: "var(--ai-bg-primary, #1f2937)",
82
+ borderRadius: "12px",
83
+ boxShadow: "0 25px 50px -12px rgba(0, 0, 0, 0.5)",
84
+ maxWidth: "420px",
85
+ width: "100%",
86
+ border: "1px solid var(--ai-border-primary, #374151)",
87
+ display: "flex",
88
+ flexDirection: "column",
89
+ maxHeight: "85vh",
90
+ overflow: "hidden",
91
+ }}
92
+ onClick={(e) => e.stopPropagation()}
93
+ >
94
+ {/* Header */}
95
+ <div
96
+ style={{
97
+ padding: "20px 24px",
98
+ borderBottom: "1px solid var(--ai-border-primary, #374151)",
99
+ display: "flex",
100
+ justifyContent: "space-between",
101
+ alignItems: "center",
102
+ background: "var(--ai-bg-secondary, #111827)",
103
+ }}
104
+ >
105
+ <h2
106
+ style={{
107
+ margin: 0,
108
+ fontSize: "18px",
109
+ fontWeight: 600,
110
+ color: "var(--ai-text-primary, #f9fafb)",
111
+ display: "flex",
112
+ alignItems: "center",
113
+ gap: "8px",
114
+ }}
115
+ >
116
+ 🔐 Connexion LastBrain
117
+ </h2>
118
+ <button
119
+ onClick={onClose}
120
+ style={{
121
+ background: "transparent",
122
+ border: "none",
123
+ color: "var(--ai-text-secondary, #9ca3af)",
124
+ cursor: "pointer",
125
+ fontSize: "24px",
126
+ lineHeight: 1,
127
+ padding: "4px 8px",
128
+ borderRadius: "6px",
129
+ transition: "all 0.2s ease",
130
+ }}
131
+ onMouseEnter={(e) => {
132
+ e.currentTarget.style.background =
133
+ "var(--ai-bg-tertiary, #374151)";
134
+ e.currentTarget.style.color = "var(--ai-text-primary, #f9fafb)";
135
+ }}
136
+ onMouseLeave={(e) => {
137
+ e.currentTarget.style.background = "transparent";
138
+ e.currentTarget.style.color = "var(--ai-text-secondary, #9ca3af)";
139
+ }}
140
+ aria-label="Close"
141
+ >
142
+ ×
143
+ </button>
144
+ </div>
145
+
146
+ {/* Body */}
147
+ <div
148
+ style={{
149
+ padding: "24px",
150
+ overflow: "auto",
151
+ flex: 1,
152
+ }}
153
+ >
154
+ <form
155
+ onSubmit={handleSubmit}
156
+ style={{ display: "flex", flexDirection: "column", gap: "16px" }}
157
+ >
158
+ <div>
159
+ <label
160
+ style={{
161
+ display: "block",
162
+ fontSize: "13px",
163
+ fontWeight: 600,
164
+ color: "var(--ai-text-secondary, #9ca3af)",
165
+ marginBottom: "8px",
166
+ letterSpacing: "0.02em",
167
+ }}
168
+ >
169
+ 📧 Email
170
+ </label>
171
+ <input
172
+ type="email"
173
+ value={email}
174
+ onChange={(e) => setEmail(e.target.value)}
175
+ required
176
+ autoFocus
177
+ autoComplete="email"
178
+ placeholder="votre@email.com"
179
+ style={{
180
+ width: "100%",
181
+ padding: "10px 12px",
182
+ fontSize: "14px",
183
+ border: "1px solid var(--ai-border-primary, #374151)",
184
+ borderRadius: "8px",
185
+ background: "var(--ai-bg-secondary, #111827)",
186
+ color: "var(--ai-text-primary, #f9fafb)",
187
+ outline: "none",
188
+ transition: "all 0.2s ease",
189
+ letterSpacing: "0.01em",
190
+ }}
191
+ onFocus={(e) => {
192
+ e.currentTarget.style.borderColor = "#8b5cf6";
193
+ e.currentTarget.style.boxShadow =
194
+ "0 0 0 3px rgba(139, 92, 246, 0.1)";
195
+ }}
196
+ onBlur={(e) => {
197
+ e.currentTarget.style.borderColor =
198
+ "var(--ai-border-primary, #374151)";
199
+ e.currentTarget.style.boxShadow = "none";
200
+ }}
201
+ />
202
+ </div>
203
+
204
+ <div>
205
+ <label
206
+ style={{
207
+ display: "block",
208
+ fontSize: "13px",
209
+ fontWeight: 600,
210
+ color: "var(--ai-text-secondary, #9ca3af)",
211
+ marginBottom: "8px",
212
+ letterSpacing: "0.02em",
213
+ }}
214
+ >
215
+ 🔒 Mot de passe
216
+ </label>
217
+ <input
218
+ type="password"
219
+ value={password}
220
+ onChange={(e) => setPassword(e.target.value)}
221
+ required
222
+ autoComplete="current-password"
223
+ placeholder="••••••••"
224
+ style={{
225
+ width: "100%",
226
+ padding: "10px 12px",
227
+ fontSize: "14px",
228
+ border: "1px solid var(--ai-border-primary, #374151)",
229
+ borderRadius: "8px",
230
+ background: "var(--ai-bg-secondary, #111827)",
231
+ color: "var(--ai-text-primary, #f9fafb)",
232
+ outline: "none",
233
+ transition: "all 0.2s ease",
234
+ letterSpacing: "0.15em",
235
+ }}
236
+ onFocus={(e) => {
237
+ e.currentTarget.style.borderColor = "#8b5cf6";
238
+ e.currentTarget.style.boxShadow =
239
+ "0 0 0 3px rgba(139, 92, 246, 0.1)";
240
+ }}
241
+ onBlur={(e) => {
242
+ e.currentTarget.style.borderColor =
243
+ "var(--ai-border-primary, #374151)";
244
+ e.currentTarget.style.boxShadow = "none";
245
+ }}
246
+ />
247
+ </div>
248
+
249
+ {error && (
250
+ <div
251
+ style={{
252
+ padding: "12px",
253
+ background: "rgba(239, 68, 68, 0.1)",
254
+ border: "1px solid rgba(239, 68, 68, 0.3)",
255
+ borderRadius: "8px",
256
+ color: "#ef4444",
257
+ fontSize: "13px",
258
+ display: "flex",
259
+ alignItems: "start",
260
+ gap: "8px",
261
+ }}
262
+ >
263
+ <span style={{ fontSize: "16px", flexShrink: 0 }}>⚠️</span>
264
+ <span style={{ flex: 1, lineHeight: "1.5" }}>{error}</span>
265
+ </div>
266
+ )}
267
+
268
+ <button
269
+ type="submit"
270
+ disabled={loading}
271
+ style={{
272
+ width: "100%",
273
+ padding: "12px",
274
+ fontSize: "14px",
275
+ fontWeight: 600,
276
+ color: "#ffffff",
277
+ background: loading
278
+ ? "var(--ai-bg-tertiary, #374151)"
279
+ : "linear-gradient(135deg, #8b5cf6 0%, #7c3aed 100%)",
280
+ border: "none",
281
+ borderRadius: "8px",
282
+ cursor: loading ? "not-allowed" : "pointer",
283
+ transition: "all 0.2s ease",
284
+ display: "flex",
285
+ alignItems: "center",
286
+ justifyContent: "center",
287
+ gap: "8px",
288
+ letterSpacing: "0.02em",
289
+ opacity: loading ? 0.6 : 1,
290
+ }}
291
+ onMouseEnter={(e) => {
292
+ if (!loading) {
293
+ e.currentTarget.style.transform = "translateY(-1px)";
294
+ e.currentTarget.style.boxShadow =
295
+ "0 10px 20px rgba(139, 92, 246, 0.3)";
296
+ }
297
+ }}
298
+ onMouseLeave={(e) => {
299
+ e.currentTarget.style.transform = "translateY(0)";
300
+ e.currentTarget.style.boxShadow = "none";
301
+ }}
302
+ >
303
+ {loading ? (
304
+ <>
305
+ <svg
306
+ style={{
307
+ animation: "spin 1s linear infinite",
308
+ width: "16px",
309
+ height: "16px",
310
+ }}
311
+ viewBox="0 0 24 24"
312
+ fill="none"
313
+ xmlns="http://www.w3.org/2000/svg"
314
+ >
315
+ <circle
316
+ cx="12"
317
+ cy="12"
318
+ r="10"
319
+ stroke="currentColor"
320
+ strokeWidth="4"
321
+ opacity="0.25"
322
+ />
323
+ <path
324
+ d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4z"
325
+ fill="currentColor"
326
+ opacity="0.75"
327
+ />
328
+ </svg>
329
+ Connexion en cours...
330
+ </>
331
+ ) : (
332
+ <>
333
+ <span>🚀</span>
334
+ Se connecter
335
+ </>
336
+ )}
337
+ </button>
338
+
339
+ <div
340
+ style={{
341
+ marginTop: "8px",
342
+ padding: "16px",
343
+ background: "rgba(139, 92, 246, 0.05)",
344
+ border: "1px solid rgba(139, 92, 246, 0.2)",
345
+ borderRadius: "8px",
346
+ textAlign: "center",
347
+ }}
348
+ >
349
+ <p
350
+ style={{
351
+ margin: "0 0 12px 0",
352
+ fontSize: "13px",
353
+ color: "var(--ai-text-secondary, #9ca3af)",
354
+ }}
355
+ >
356
+ Pas encore de compte ?
357
+ </p>
358
+ <a
359
+ href="https://lastbrain.io/signup"
360
+ target="_blank"
361
+ rel="noopener noreferrer"
362
+ style={{
363
+ display: "inline-block",
364
+ padding: "8px 16px",
365
+ fontSize: "13px",
366
+ fontWeight: 600,
367
+ color: "#8b5cf6",
368
+ textDecoration: "none",
369
+ border: "1px solid rgba(139, 92, 246, 0.3)",
370
+ borderRadius: "6px",
371
+ transition: "all 0.2s ease",
372
+ }}
373
+ onMouseEnter={(e) => {
374
+ e.currentTarget.style.background = "rgba(139, 92, 246, 0.1)";
375
+ e.currentTarget.style.borderColor = "#8b5cf6";
376
+ }}
377
+ onMouseLeave={(e) => {
378
+ e.currentTarget.style.background = "transparent";
379
+ e.currentTarget.style.borderColor = "rgba(139, 92, 246, 0.3)";
380
+ }}
381
+ >
382
+ ✨ Créer un compte gratuitement
383
+ </a>
384
+ </div>
385
+ </form>
386
+ </div>
387
+ </div>
388
+
389
+ <style>
390
+ {`
391
+ @keyframes spin {
392
+ from { transform: rotate(0deg); }
393
+ to { transform: rotate(360deg); }
394
+ }
395
+ `}
396
+ </style>
397
+ </div>
398
+ );
399
+ }