@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.
- package/dist/components/AiContextButton.d.ts.map +1 -1
- package/dist/components/AiContextButton.js +12 -3
- package/dist/components/AiImageButton.d.ts.map +1 -1
- package/dist/components/AiImageButton.js +12 -3
- package/dist/components/AiInput.d.ts.map +1 -1
- package/dist/components/AiInput.js +12 -3
- package/dist/components/AiStatusButton.d.ts.map +1 -1
- package/dist/components/AiStatusButton.js +187 -13
- package/dist/components/AiTextarea.d.ts.map +1 -1
- package/dist/components/AiTextarea.js +12 -2
- package/dist/components/LBConnectButton.d.ts.map +1 -1
- package/dist/components/LBConnectButton.js +9 -12
- package/dist/components/LBSigninModal.d.ts +6 -0
- package/dist/components/LBSigninModal.d.ts.map +1 -0
- package/dist/components/LBSigninModal.js +232 -0
- package/dist/context/LBAuthProvider.d.ts +5 -2
- package/dist/context/LBAuthProvider.d.ts.map +1 -1
- package/dist/context/LBAuthProvider.js +67 -35
- package/dist/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -0
- package/package.json +2 -2
- package/src/components/AiContextButton.tsx +15 -5
- package/src/components/AiImageButton.tsx +15 -5
- package/src/components/AiInput.tsx +15 -5
- package/src/components/AiStatusButton.tsx +356 -38
- package/src/components/AiTextarea.tsx +15 -2
- package/src/components/LBConnectButton.tsx +59 -49
- package/src/components/LBSigninModal.tsx +399 -0
- package/src/context/LBAuthProvider.tsx +86 -44
- package/src/index.ts +1 -0
|
@@ -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
|
|
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<
|
|
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
|
-
|
|
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
|
-
//
|
|
111
|
-
|
|
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-
|
|
135
|
-
<div className="bg-white dark:bg-
|
|
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-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
<
|
|
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
|
-
: "
|
|
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-
|
|
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-
|
|
172
|
+
<div className="p-8">
|
|
171
173
|
{step === "login" ? (
|
|
172
|
-
<form onSubmit={handleLogin} className="space-y-
|
|
173
|
-
<div>
|
|
174
|
-
<label className="block text-sm font-semibold
|
|
175
|
-
📧
|
|
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-
|
|
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
|
|
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-
|
|
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
|
|
204
|
-
<div className="flex items-start gap-
|
|
205
|
-
<span className="text-red-500 text-xl"
|
|
206
|
-
|
|
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-
|
|
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-
|
|
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
|
-
"
|
|
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-
|
|
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-
|
|
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-
|
|
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
|
|
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
|
+
}
|