@lastbrain/ai-ui-react 1.0.68 → 1.0.69
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/dist/components/AiChipLabel.d.ts +8 -3
- package/dist/components/AiChipLabel.d.ts.map +1 -1
- package/dist/components/AiChipLabel.js +21 -70
- package/dist/components/AiContextButton.d.ts +5 -1
- package/dist/components/AiContextButton.d.ts.map +1 -1
- package/dist/components/AiContextButton.js +67 -291
- package/dist/components/AiImageButton.d.ts +5 -1
- package/dist/components/AiImageButton.d.ts.map +1 -1
- package/dist/components/AiImageButton.js +6 -142
- package/dist/components/AiInput.d.ts +5 -3
- package/dist/components/AiInput.d.ts.map +1 -1
- package/dist/components/AiInput.js +13 -25
- package/dist/components/AiPromptPanel.d.ts.map +1 -1
- package/dist/components/AiPromptPanel.js +58 -212
- package/dist/components/AiSelect.d.ts +5 -3
- package/dist/components/AiSelect.d.ts.map +1 -1
- package/dist/components/AiSelect.js +21 -30
- package/dist/components/AiStatusButton.d.ts +4 -1
- package/dist/components/AiStatusButton.d.ts.map +1 -1
- package/dist/components/AiStatusButton.js +198 -668
- package/dist/components/AiTextarea.d.ts +4 -2
- package/dist/components/AiTextarea.d.ts.map +1 -1
- package/dist/components/AiTextarea.js +14 -26
- package/dist/components/LBApiKeySelector.d.ts.map +1 -1
- package/dist/components/LBApiKeySelector.js +5 -166
- package/dist/components/LBConnectButton.d.ts +4 -7
- package/dist/components/LBConnectButton.d.ts.map +1 -1
- package/dist/components/LBConnectButton.js +17 -86
- package/dist/components/LBSigninModal.d.ts +1 -1
- package/dist/components/LBSigninModal.d.ts.map +1 -1
- package/dist/components/LBSigninModal.js +42 -320
- package/dist/examples/AiUiPremiumShowcase.d.ts +2 -0
- package/dist/examples/AiUiPremiumShowcase.d.ts.map +1 -0
- package/dist/examples/AiUiPremiumShowcase.js +15 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -0
- package/dist/styles/inline.d.ts +1 -0
- package/dist/styles/inline.d.ts.map +1 -1
- package/dist/styles/inline.js +25 -129
- package/dist/styles.css +1268 -369
- package/dist/types.d.ts +3 -0
- package/dist/types.d.ts.map +1 -1
- package/package.json +2 -2
- package/src/components/AiChipLabel.tsx +64 -101
- package/src/components/AiContextButton.tsx +138 -430
- package/src/components/AiImageButton.tsx +29 -190
- package/src/components/AiInput.tsx +49 -74
- package/src/components/AiPromptPanel.tsx +71 -254
- package/src/components/AiSelect.tsx +61 -69
- package/src/components/AiStatusButton.tsx +477 -1313
- package/src/components/AiTextarea.tsx +49 -64
- package/src/components/LBApiKeySelector.tsx +86 -274
- package/src/components/LBConnectButton.tsx +46 -334
- package/src/components/LBSigninModal.tsx +140 -481
- package/src/examples/AiUiPremiumShowcase.tsx +91 -0
- package/src/index.ts +3 -0
- package/src/styles/inline.ts +27 -148
- package/src/styles.css +1268 -369
- package/src/types.ts +3 -0
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
|
|
3
|
-
import { useState } from "react";
|
|
3
|
+
import { useEffect, useMemo, useState } from "react";
|
|
4
|
+
import { createPortal } from "react-dom";
|
|
5
|
+
import { AlertCircle, Loader2, Lock, Mail, Sparkles, X } from "lucide-react";
|
|
4
6
|
import { useLB } from "../context/LBAuthProvider";
|
|
5
7
|
import { LBApiKeySelector } from "./LBApiKeySelector";
|
|
6
|
-
import { Mail, Lock, Sparkles, X, Loader2, AlertCircle } from "lucide-react";
|
|
7
8
|
|
|
8
9
|
export interface LBSigninModalProps {
|
|
9
10
|
isOpen: boolean;
|
|
@@ -11,564 +12,222 @@ export interface LBSigninModalProps {
|
|
|
11
12
|
}
|
|
12
13
|
|
|
13
14
|
export function LBSigninModal({ isOpen, onClose }: LBSigninModalProps) {
|
|
14
|
-
|
|
15
|
+
const lbContext = useLB();
|
|
16
|
+
const [portalRoot, setPortalRoot] = useState<HTMLElement | null>(null);
|
|
17
|
+
|
|
15
18
|
const [email, setEmail] = useState("");
|
|
16
19
|
const [password, setPassword] = useState("");
|
|
17
20
|
const [loading, setLoading] = useState(false);
|
|
18
21
|
const [error, setError] = useState("");
|
|
19
22
|
const [showKeySelector, setShowKeySelector] = useState(false);
|
|
20
|
-
const [currentApiKeys, setCurrentApiKeys] = useState<any[]>([]);
|
|
21
|
-
|
|
22
|
-
// Appeler useLB() inconditionnellement - hook doit toujours être appelé
|
|
23
|
-
const lbContext = useLB();
|
|
24
|
-
|
|
25
|
-
// Vérifier si le contexte est valide
|
|
26
|
-
if (!lbContext || !isOpen) {
|
|
27
|
-
return null;
|
|
28
|
-
}
|
|
23
|
+
const [currentApiKeys, setCurrentApiKeys] = useState<any[]>([]);
|
|
29
24
|
|
|
30
|
-
// Extraire les valeurs du contexte
|
|
31
25
|
const {
|
|
32
26
|
login,
|
|
33
27
|
selectApiKeyWithToken,
|
|
34
28
|
fetchApiKeys,
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
29
|
+
} = lbContext || {};
|
|
30
|
+
|
|
31
|
+
const canRender = Boolean(isOpen && lbContext && login);
|
|
32
|
+
|
|
33
|
+
const panelTitle = useMemo(() => "Connexion LastBrain", []);
|
|
38
34
|
|
|
39
|
-
|
|
35
|
+
useEffect(() => {
|
|
36
|
+
setPortalRoot(document.body);
|
|
37
|
+
}, []);
|
|
38
|
+
|
|
39
|
+
if (!canRender || !portalRoot) {
|
|
40
|
+
return null;
|
|
41
|
+
}
|
|
40
42
|
|
|
41
43
|
const handleSubmit = async (e: React.FormEvent) => {
|
|
42
44
|
e.preventDefault();
|
|
45
|
+
if (!login) {
|
|
46
|
+
return;
|
|
47
|
+
}
|
|
48
|
+
|
|
43
49
|
setError("");
|
|
44
50
|
setLoading(true);
|
|
45
51
|
|
|
46
52
|
try {
|
|
47
53
|
const result = await login(email, password);
|
|
48
|
-
if (result.success) {
|
|
49
|
-
if (result.needsKeySelection) {
|
|
50
|
-
// L'utilisateur doit choisir une clé API
|
|
51
|
-
// Utiliser l'access token retourné directement par login
|
|
52
|
-
if (fetchApiKeys && result.accessToken) {
|
|
53
|
-
try {
|
|
54
|
-
const keys = await fetchApiKeys(result.accessToken);
|
|
55
|
-
setCurrentApiKeys(keys);
|
|
56
|
-
setShowKeySelector(true);
|
|
57
|
-
} catch (keyError) {
|
|
58
|
-
setError("Erreur lors de la récupération des clés API");
|
|
59
|
-
console.error("Failed to fetch API keys:", keyError);
|
|
60
|
-
}
|
|
61
|
-
} else {
|
|
62
|
-
setError("Token d'accès non disponible");
|
|
63
|
-
}
|
|
64
|
-
} else {
|
|
65
|
-
// Connexion réussie, fermer le modal
|
|
66
|
-
onClose();
|
|
67
|
-
}
|
|
68
|
-
} else {
|
|
54
|
+
if (!result.success) {
|
|
69
55
|
setError(result.error || "Échec de la connexion");
|
|
56
|
+
return;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
if (!result.needsKeySelection) {
|
|
60
|
+
onClose();
|
|
61
|
+
return;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
if (!fetchApiKeys || !result.accessToken) {
|
|
65
|
+
setError("Token d'accès non disponible");
|
|
66
|
+
return;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
try {
|
|
70
|
+
const keys = await fetchApiKeys(result.accessToken);
|
|
71
|
+
setCurrentApiKeys(keys || []);
|
|
72
|
+
setShowKeySelector(true);
|
|
73
|
+
} catch (keyError) {
|
|
74
|
+
console.error("Failed to fetch API keys:", keyError);
|
|
75
|
+
setError("Erreur lors de la récupération des clés API");
|
|
70
76
|
}
|
|
71
77
|
} catch (err) {
|
|
72
|
-
setError(
|
|
73
|
-
err instanceof Error ? err.message : "Une erreur s'est produite"
|
|
74
|
-
);
|
|
78
|
+
setError(err instanceof Error ? err.message : "Une erreur s'est produite");
|
|
75
79
|
} finally {
|
|
76
80
|
setLoading(false);
|
|
77
81
|
}
|
|
78
82
|
};
|
|
79
83
|
|
|
80
84
|
const handleKeySelect = async (apiKeyId: string) => {
|
|
81
|
-
if (!selectApiKeyWithToken)
|
|
85
|
+
if (!selectApiKeyWithToken) {
|
|
86
|
+
return;
|
|
87
|
+
}
|
|
82
88
|
|
|
83
89
|
try {
|
|
84
90
|
await selectApiKeyWithToken(apiKeyId);
|
|
85
91
|
setShowKeySelector(false);
|
|
86
92
|
onClose();
|
|
87
93
|
} catch (err) {
|
|
88
|
-
setError(
|
|
89
|
-
err instanceof Error ? err.message : "Erreur lors de la sélection"
|
|
90
|
-
);
|
|
94
|
+
setError(err instanceof Error ? err.message : "Erreur lors de la sélection");
|
|
91
95
|
setShowKeySelector(false);
|
|
92
96
|
}
|
|
93
97
|
};
|
|
94
98
|
|
|
95
99
|
const handleCancelKeySelection = () => {
|
|
96
100
|
setShowKeySelector(false);
|
|
97
|
-
// Fermer complètement le modal au lieu de rester dans l'état de sélection
|
|
98
101
|
onClose();
|
|
99
102
|
};
|
|
100
103
|
|
|
101
|
-
// Si on doit afficher le sélecteur de clés
|
|
102
104
|
if (showKeySelector && currentApiKeys.length > 0) {
|
|
103
|
-
return (
|
|
105
|
+
return createPortal(
|
|
104
106
|
<LBApiKeySelector
|
|
105
107
|
apiKeys={currentApiKeys}
|
|
106
108
|
onSelect={handleKeySelect}
|
|
107
109
|
onCancel={handleCancelKeySelection}
|
|
108
|
-
isOpen
|
|
109
|
-
|
|
110
|
+
isOpen
|
|
111
|
+
/>,
|
|
112
|
+
portalRoot
|
|
110
113
|
);
|
|
111
114
|
}
|
|
112
115
|
|
|
113
|
-
|
|
114
|
-
if (e.key === "Escape") {
|
|
115
|
-
onClose();
|
|
116
|
-
}
|
|
117
|
-
};
|
|
118
|
-
|
|
119
|
-
return (
|
|
116
|
+
return createPortal(
|
|
120
117
|
<div
|
|
121
|
-
|
|
122
|
-
position: "fixed",
|
|
123
|
-
top: 0,
|
|
124
|
-
left: 0,
|
|
125
|
-
right: 0,
|
|
126
|
-
bottom: 0,
|
|
127
|
-
width: "100vw",
|
|
128
|
-
height: "100vh",
|
|
129
|
-
backgroundColor: "rgba(0, 0, 0, 0.75)",
|
|
130
|
-
backdropFilter: "blur(12px)",
|
|
131
|
-
WebkitBackdropFilter: "blur(12px)",
|
|
132
|
-
display: "flex",
|
|
133
|
-
alignItems: "center",
|
|
134
|
-
justifyContent: "center",
|
|
135
|
-
zIndex: 9999,
|
|
136
|
-
padding: "16px",
|
|
137
|
-
animation: "fadeIn 0.2s ease-out",
|
|
138
|
-
overflow: "auto",
|
|
139
|
-
}}
|
|
118
|
+
className="ai-signin-overlay"
|
|
140
119
|
onClick={onClose}
|
|
141
|
-
onKeyDown={
|
|
120
|
+
onKeyDown={(e) => {
|
|
121
|
+
if (e.key === "Escape") {
|
|
122
|
+
onClose();
|
|
123
|
+
}
|
|
124
|
+
}}
|
|
125
|
+
role="dialog"
|
|
126
|
+
aria-modal="true"
|
|
127
|
+
aria-label={panelTitle}
|
|
142
128
|
>
|
|
143
|
-
<div
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
maxWidth: "440px",
|
|
150
|
-
width: "100%",
|
|
151
|
-
border:
|
|
152
|
-
"1px solid light-dark(rgba(226, 232, 240, 0.8), rgba(139, 92, 246, 0.3))",
|
|
153
|
-
display: "flex",
|
|
154
|
-
flexDirection: "column",
|
|
155
|
-
maxHeight: "90vh",
|
|
156
|
-
overflow: "hidden",
|
|
157
|
-
position: "relative",
|
|
158
|
-
animation: "slideUp 0.3s ease-out",
|
|
159
|
-
}}
|
|
160
|
-
onClick={(e) => e.stopPropagation()}
|
|
161
|
-
>
|
|
162
|
-
{/* Gradient Background Effect */}
|
|
163
|
-
<div
|
|
164
|
-
style={{
|
|
165
|
-
position: "absolute",
|
|
166
|
-
top: 0,
|
|
167
|
-
left: 0,
|
|
168
|
-
right: 0,
|
|
169
|
-
height: "200px",
|
|
170
|
-
background:
|
|
171
|
-
"radial-gradient(ellipse at top, rgba(139, 92, 246, 0.15), transparent 70%)",
|
|
172
|
-
pointerEvents: "none",
|
|
173
|
-
zIndex: 0,
|
|
174
|
-
}}
|
|
175
|
-
/>
|
|
176
|
-
|
|
177
|
-
{/* Header */}
|
|
178
|
-
<div
|
|
179
|
-
style={{
|
|
180
|
-
padding: "32px 32px 24px",
|
|
181
|
-
display: "flex",
|
|
182
|
-
flexDirection: "column",
|
|
183
|
-
alignItems: "center",
|
|
184
|
-
gap: "16px",
|
|
185
|
-
position: "relative",
|
|
186
|
-
zIndex: 1,
|
|
187
|
-
}}
|
|
129
|
+
<div className="ai-signin-panel" onClick={(e) => e.stopPropagation()}>
|
|
130
|
+
<button
|
|
131
|
+
type="button"
|
|
132
|
+
className="ai-icon-btn ai-signin-close"
|
|
133
|
+
onClick={onClose}
|
|
134
|
+
aria-label="Fermer"
|
|
188
135
|
>
|
|
189
|
-
{
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
alignItems: "center",
|
|
198
|
-
justifyContent: "center",
|
|
199
|
-
boxShadow: "0 8px 24px rgba(139, 92, 246, 0.3)",
|
|
200
|
-
animation: "pulse 2s ease-in-out infinite",
|
|
201
|
-
}}
|
|
202
|
-
>
|
|
203
|
-
<Sparkles size={32} color="#ffffff" strokeWidth={2.5} />
|
|
204
|
-
</div>
|
|
205
|
-
|
|
206
|
-
{/* Title */}
|
|
207
|
-
<div style={{ textAlign: "center" }}>
|
|
208
|
-
<h2
|
|
209
|
-
style={{
|
|
210
|
-
margin: "0 0 8px 0",
|
|
211
|
-
fontSize: "24px",
|
|
212
|
-
fontWeight: 700,
|
|
213
|
-
color: "light-dark(#1e293b, #f8fafc)",
|
|
214
|
-
letterSpacing: "-0.02em",
|
|
215
|
-
}}
|
|
216
|
-
>
|
|
217
|
-
Connexion LastBrain
|
|
218
|
-
</h2>
|
|
219
|
-
<p
|
|
220
|
-
style={{
|
|
221
|
-
margin: 0,
|
|
222
|
-
fontSize: "14px",
|
|
223
|
-
color: "light-dark(#64748b, #94a3b8)",
|
|
224
|
-
fontWeight: 400,
|
|
225
|
-
}}
|
|
226
|
-
>
|
|
227
|
-
Accédez à vos outils d'intelligence artificielle
|
|
228
|
-
</p>
|
|
136
|
+
<X size={16} />
|
|
137
|
+
</button>
|
|
138
|
+
|
|
139
|
+
<div className="ai-signin-header">
|
|
140
|
+
<div className="ai-center mb-3">
|
|
141
|
+
<span className="ai-icon-badge">
|
|
142
|
+
<Sparkles size={22} />
|
|
143
|
+
</span>
|
|
229
144
|
</div>
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
style={{
|
|
235
|
-
position: "absolute",
|
|
236
|
-
top: "24px",
|
|
237
|
-
right: "24px",
|
|
238
|
-
background: "transparent",
|
|
239
|
-
border: "none",
|
|
240
|
-
color: "light-dark(#64748b, #94a3b8)",
|
|
241
|
-
cursor: "pointer",
|
|
242
|
-
padding: "8px",
|
|
243
|
-
borderRadius: "10px",
|
|
244
|
-
transition: "all 0.2s ease",
|
|
245
|
-
display: "flex",
|
|
246
|
-
alignItems: "center",
|
|
247
|
-
justifyContent: "center",
|
|
248
|
-
}}
|
|
249
|
-
onMouseEnter={(e) => {
|
|
250
|
-
e.currentTarget.style.background =
|
|
251
|
-
"light-dark(rgba(100, 116, 139, 0.1), rgba(148, 163, 184, 0.15))";
|
|
252
|
-
e.currentTarget.style.color = "light-dark(#1e293b, #f8fafc)";
|
|
253
|
-
}}
|
|
254
|
-
onMouseLeave={(e) => {
|
|
255
|
-
e.currentTarget.style.background = "transparent";
|
|
256
|
-
e.currentTarget.style.color = "light-dark(#64748b, #94a3b8)";
|
|
257
|
-
}}
|
|
258
|
-
aria-label="Close"
|
|
259
|
-
>
|
|
260
|
-
<X size={20} strokeWidth={2} />
|
|
261
|
-
</button>
|
|
145
|
+
<h2 className="ai-signin-title">{panelTitle}</h2>
|
|
146
|
+
<p className="ai-signin-subtitle">
|
|
147
|
+
Connectez-vous pour activer les composants IA dans votre app.
|
|
148
|
+
</p>
|
|
262
149
|
</div>
|
|
263
150
|
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
flex: 1,
|
|
270
|
-
position: "relative",
|
|
271
|
-
zIndex: 1,
|
|
272
|
-
}}
|
|
273
|
-
>
|
|
274
|
-
<form
|
|
275
|
-
onSubmit={handleSubmit}
|
|
276
|
-
style={{ display: "flex", flexDirection: "column", gap: "20px" }}
|
|
277
|
-
>
|
|
278
|
-
{/* Email Input */}
|
|
279
|
-
<div>
|
|
280
|
-
<label
|
|
281
|
-
style={{
|
|
282
|
-
display: "flex",
|
|
283
|
-
alignItems: "center",
|
|
284
|
-
gap: "8px",
|
|
285
|
-
fontSize: "13px",
|
|
286
|
-
fontWeight: 600,
|
|
287
|
-
color: "light-dark(#1e293b, #f8fafc)",
|
|
288
|
-
marginBottom: "10px",
|
|
289
|
-
letterSpacing: "0.01em",
|
|
290
|
-
}}
|
|
291
|
-
>
|
|
292
|
-
<Mail size={16} strokeWidth={2.5} />
|
|
151
|
+
<div className="ai-signin-content">
|
|
152
|
+
<form onSubmit={handleSubmit}>
|
|
153
|
+
<div className="ai-input-row">
|
|
154
|
+
<label htmlFor="lb-signin-email" className="ai-input-label ai-row">
|
|
155
|
+
<Mail size={14} className="ai-inline-icon" />
|
|
293
156
|
Email
|
|
294
157
|
</label>
|
|
295
|
-
<div
|
|
296
|
-
<
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
borderRadius: "12px",
|
|
310
|
-
background: "light-dark(#f8fafc, #0f172a)",
|
|
311
|
-
color: "light-dark(#1e293b, #f8fafc)",
|
|
312
|
-
outline: "none",
|
|
313
|
-
transition: "all 0.2s ease",
|
|
314
|
-
fontWeight: 500,
|
|
315
|
-
}}
|
|
316
|
-
onFocus={(e) => {
|
|
317
|
-
e.currentTarget.style.borderColor =
|
|
318
|
-
"rgba(139, 92, 246, 0.6)";
|
|
319
|
-
e.currentTarget.style.boxShadow =
|
|
320
|
-
"0 0 0 4px rgba(139, 92, 246, 0.15)";
|
|
321
|
-
e.currentTarget.style.background =
|
|
322
|
-
"light-dark(#ffffff, #1e293b)";
|
|
323
|
-
}}
|
|
324
|
-
onBlur={(e) => {
|
|
325
|
-
e.currentTarget.style.borderColor =
|
|
326
|
-
"light-dark(#e2e8f0, #334155)";
|
|
327
|
-
e.currentTarget.style.boxShadow = "none";
|
|
328
|
-
e.currentTarget.style.background =
|
|
329
|
-
"light-dark(#f8fafc, #0f172a)";
|
|
330
|
-
}}
|
|
331
|
-
/>
|
|
158
|
+
<div className="ai-control-group ai-glow">
|
|
159
|
+
<div className="ai-shell ai-size-md ai-radius-full">
|
|
160
|
+
<input
|
|
161
|
+
id="lb-signin-email"
|
|
162
|
+
className="ai-control ai-control-input ai-size-md ai-radius-full"
|
|
163
|
+
type="email"
|
|
164
|
+
value={email}
|
|
165
|
+
onChange={(e) => setEmail(e.target.value)}
|
|
166
|
+
required
|
|
167
|
+
autoFocus
|
|
168
|
+
autoComplete="email"
|
|
169
|
+
placeholder="votre@email.com"
|
|
170
|
+
/>
|
|
171
|
+
</div>
|
|
332
172
|
</div>
|
|
333
173
|
</div>
|
|
334
174
|
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
style={{
|
|
339
|
-
display: "flex",
|
|
340
|
-
alignItems: "center",
|
|
341
|
-
gap: "8px",
|
|
342
|
-
fontSize: "13px",
|
|
343
|
-
fontWeight: 600,
|
|
344
|
-
color: "light-dark(#1e293b, #f8fafc)",
|
|
345
|
-
marginBottom: "10px",
|
|
346
|
-
letterSpacing: "0.01em",
|
|
347
|
-
}}
|
|
348
|
-
>
|
|
349
|
-
<Lock size={16} strokeWidth={2.5} />
|
|
175
|
+
<div className="ai-input-row">
|
|
176
|
+
<label htmlFor="lb-signin-password" className="ai-input-label ai-row">
|
|
177
|
+
<Lock size={14} className="ai-inline-icon" />
|
|
350
178
|
Mot de passe
|
|
351
179
|
</label>
|
|
352
|
-
<div
|
|
353
|
-
<
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
borderRadius: "12px",
|
|
366
|
-
background: "light-dark(#f8fafc, #0f172a)",
|
|
367
|
-
color: "light-dark(#1e293b, #f8fafc)",
|
|
368
|
-
outline: "none",
|
|
369
|
-
transition: "all 0.2s ease",
|
|
370
|
-
letterSpacing: "0.15em",
|
|
371
|
-
fontWeight: 500,
|
|
372
|
-
}}
|
|
373
|
-
onFocus={(e) => {
|
|
374
|
-
e.currentTarget.style.borderColor =
|
|
375
|
-
"rgba(139, 92, 246, 0.6)";
|
|
376
|
-
e.currentTarget.style.boxShadow =
|
|
377
|
-
"0 0 0 4px rgba(139, 92, 246, 0.15)";
|
|
378
|
-
e.currentTarget.style.background =
|
|
379
|
-
"light-dark(#ffffff, #1e293b)";
|
|
380
|
-
}}
|
|
381
|
-
onBlur={(e) => {
|
|
382
|
-
e.currentTarget.style.borderColor =
|
|
383
|
-
"light-dark(#e2e8f0, #334155)";
|
|
384
|
-
e.currentTarget.style.boxShadow = "none";
|
|
385
|
-
e.currentTarget.style.background =
|
|
386
|
-
"light-dark(#f8fafc, #0f172a)";
|
|
387
|
-
}}
|
|
388
|
-
/>
|
|
180
|
+
<div className="ai-control-group ai-glow">
|
|
181
|
+
<div className="ai-shell ai-size-md ai-radius-full">
|
|
182
|
+
<input
|
|
183
|
+
id="lb-signin-password"
|
|
184
|
+
className="ai-control ai-control-input ai-size-md ai-radius-full"
|
|
185
|
+
type="password"
|
|
186
|
+
value={password}
|
|
187
|
+
onChange={(e) => setPassword(e.target.value)}
|
|
188
|
+
required
|
|
189
|
+
autoComplete="current-password"
|
|
190
|
+
placeholder="••••••••"
|
|
191
|
+
/>
|
|
192
|
+
</div>
|
|
389
193
|
</div>
|
|
390
194
|
</div>
|
|
391
195
|
|
|
392
|
-
{
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
padding: "14px 16px",
|
|
397
|
-
background: "rgba(239, 68, 68, 0.1)",
|
|
398
|
-
border: "2px solid rgba(239, 68, 68, 0.35)",
|
|
399
|
-
borderRadius: "12px",
|
|
400
|
-
color: "light-dark(#dc2626, #fca5a5)",
|
|
401
|
-
fontSize: "14px",
|
|
402
|
-
display: "flex",
|
|
403
|
-
alignItems: "start",
|
|
404
|
-
gap: "12px",
|
|
405
|
-
animation: "shake 0.4s ease",
|
|
406
|
-
}}
|
|
407
|
-
>
|
|
408
|
-
<AlertCircle
|
|
409
|
-
size={20}
|
|
410
|
-
style={{ flexShrink: 0, marginTop: "2px" }}
|
|
411
|
-
strokeWidth={2.5}
|
|
412
|
-
/>
|
|
413
|
-
<span style={{ flex: 1, lineHeight: "1.5", fontWeight: 500 }}>
|
|
414
|
-
{error}
|
|
415
|
-
</span>
|
|
196
|
+
{error ? (
|
|
197
|
+
<div className="ai-signin-error" role="alert">
|
|
198
|
+
<AlertCircle size={16} />
|
|
199
|
+
<span>{error}</span>
|
|
416
200
|
</div>
|
|
417
|
-
)}
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
borderRadius: "12px",
|
|
434
|
-
cursor: loading ? "not-allowed" : "pointer",
|
|
435
|
-
transition: "all 0.2s ease",
|
|
436
|
-
display: "flex",
|
|
437
|
-
alignItems: "center",
|
|
438
|
-
justifyContent: "center",
|
|
439
|
-
gap: "10px",
|
|
440
|
-
letterSpacing: "0.01em",
|
|
441
|
-
opacity: loading ? 0.6 : 1,
|
|
442
|
-
boxShadow: loading
|
|
443
|
-
? "none"
|
|
444
|
-
: "0 4px 14px rgba(139, 92, 246, 0.4)",
|
|
445
|
-
}}
|
|
446
|
-
onMouseEnter={(e) => {
|
|
447
|
-
if (!loading) {
|
|
448
|
-
e.currentTarget.style.transform = "translateY(-2px)";
|
|
449
|
-
e.currentTarget.style.boxShadow =
|
|
450
|
-
"0 8px 24px rgba(139, 92, 246, 0.6)";
|
|
451
|
-
}
|
|
452
|
-
}}
|
|
453
|
-
onMouseLeave={(e) => {
|
|
454
|
-
e.currentTarget.style.transform = "translateY(0)";
|
|
455
|
-
e.currentTarget.style.boxShadow =
|
|
456
|
-
"0 4px 14px rgba(139, 92, 246, 0.4)";
|
|
457
|
-
}}
|
|
458
|
-
>
|
|
459
|
-
{loading ? (
|
|
460
|
-
<>
|
|
461
|
-
<Loader2
|
|
462
|
-
size={20}
|
|
463
|
-
strokeWidth={2.5}
|
|
464
|
-
style={{ animation: "spin 1s linear infinite" }}
|
|
465
|
-
/>
|
|
466
|
-
Connexion en cours...
|
|
467
|
-
</>
|
|
468
|
-
) : (
|
|
469
|
-
<>
|
|
470
|
-
<Sparkles size={20} strokeWidth={2.5} />
|
|
471
|
-
Se connecter
|
|
472
|
-
</>
|
|
473
|
-
)}
|
|
474
|
-
</button>
|
|
201
|
+
) : null}
|
|
202
|
+
|
|
203
|
+
<div className="ai-signin-actions">
|
|
204
|
+
<button type="submit" className="ai-btn ai-btn--auth" disabled={loading}>
|
|
205
|
+
{loading ? (
|
|
206
|
+
<>
|
|
207
|
+
<Loader2 size={16} className="ai-spinner" />
|
|
208
|
+
Connexion...
|
|
209
|
+
</>
|
|
210
|
+
) : (
|
|
211
|
+
<>
|
|
212
|
+
<Sparkles size={16} />
|
|
213
|
+
Se connecter
|
|
214
|
+
</>
|
|
215
|
+
)}
|
|
216
|
+
</button>
|
|
475
217
|
|
|
476
|
-
{/* Signup Link */}
|
|
477
|
-
<div
|
|
478
|
-
style={{
|
|
479
|
-
marginTop: "8px",
|
|
480
|
-
padding: "20px",
|
|
481
|
-
background: "rgba(139, 92, 246, 0.05)",
|
|
482
|
-
border: "1px solid rgba(139, 92, 246, 0.2)",
|
|
483
|
-
borderRadius: "16px",
|
|
484
|
-
textAlign: "center",
|
|
485
|
-
}}
|
|
486
|
-
>
|
|
487
|
-
<p
|
|
488
|
-
style={{
|
|
489
|
-
margin: "0 0 14px 0",
|
|
490
|
-
fontSize: "14px",
|
|
491
|
-
color: "light-dark(#64748b, #94a3b8)",
|
|
492
|
-
fontWeight: 500,
|
|
493
|
-
}}
|
|
494
|
-
>
|
|
495
|
-
Pas encore de compte ?
|
|
496
|
-
</p>
|
|
497
218
|
<a
|
|
498
219
|
href="https://prompt.lastbrain.io/signup"
|
|
499
220
|
target="_blank"
|
|
500
221
|
rel="noopener noreferrer"
|
|
501
|
-
|
|
502
|
-
display: "inline-flex",
|
|
503
|
-
alignItems: "center",
|
|
504
|
-
gap: "8px",
|
|
505
|
-
padding: "10px 20px",
|
|
506
|
-
fontSize: "14px",
|
|
507
|
-
fontWeight: 600,
|
|
508
|
-
color: "#8b5cf6",
|
|
509
|
-
textDecoration: "none",
|
|
510
|
-
border: "2px solid rgba(139, 92, 246, 0.3)",
|
|
511
|
-
borderRadius: "10px",
|
|
512
|
-
transition: "all 0.2s ease",
|
|
513
|
-
}}
|
|
514
|
-
onMouseEnter={(e) => {
|
|
515
|
-
e.currentTarget.style.background = "rgba(139, 92, 246, 0.1)";
|
|
516
|
-
e.currentTarget.style.borderColor = "rgba(139, 92, 246, 0.5)";
|
|
517
|
-
e.currentTarget.style.transform = "translateY(-1px)";
|
|
518
|
-
}}
|
|
519
|
-
onMouseLeave={(e) => {
|
|
520
|
-
e.currentTarget.style.background = "transparent";
|
|
521
|
-
e.currentTarget.style.borderColor = "rgba(139, 92, 246, 0.3)";
|
|
522
|
-
e.currentTarget.style.transform = "translateY(0)";
|
|
523
|
-
}}
|
|
222
|
+
className="ai-btn ai-btn--ghost"
|
|
524
223
|
>
|
|
525
|
-
|
|
526
|
-
Créer un compte gratuitement
|
|
224
|
+
Créer un compte
|
|
527
225
|
</a>
|
|
528
226
|
</div>
|
|
529
227
|
</form>
|
|
530
228
|
</div>
|
|
531
229
|
</div>
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
{`
|
|
535
|
-
@keyframes fadeIn {
|
|
536
|
-
from { opacity: 0; }
|
|
537
|
-
to { opacity: 1; }
|
|
538
|
-
}
|
|
539
|
-
|
|
540
|
-
@keyframes slideUp {
|
|
541
|
-
from {
|
|
542
|
-
opacity: 0;
|
|
543
|
-
transform: translateY(20px) scale(0.95);
|
|
544
|
-
}
|
|
545
|
-
to {
|
|
546
|
-
opacity: 1;
|
|
547
|
-
transform: translateY(0) scale(1);
|
|
548
|
-
}
|
|
549
|
-
}
|
|
550
|
-
|
|
551
|
-
@keyframes spin {
|
|
552
|
-
from { transform: rotate(0deg); }
|
|
553
|
-
to { transform: rotate(360deg); }
|
|
554
|
-
}
|
|
555
|
-
|
|
556
|
-
@keyframes pulse {
|
|
557
|
-
0%, 100% {
|
|
558
|
-
box-shadow: 0 8px 24px rgba(139, 92, 246, 0.3);
|
|
559
|
-
}
|
|
560
|
-
50% {
|
|
561
|
-
box-shadow: 0 8px 32px rgba(139, 92, 246, 0.5);
|
|
562
|
-
}
|
|
563
|
-
}
|
|
564
|
-
|
|
565
|
-
@keyframes shake {
|
|
566
|
-
0%, 100% { transform: translateX(0); }
|
|
567
|
-
25% { transform: translateX(-8px); }
|
|
568
|
-
75% { transform: translateX(8px); }
|
|
569
|
-
}
|
|
570
|
-
`}
|
|
571
|
-
</style>
|
|
572
|
-
</div>
|
|
230
|
+
</div>,
|
|
231
|
+
portalRoot
|
|
573
232
|
);
|
|
574
233
|
}
|