@lastbrain/module-auth 0.1.1 → 0.1.3
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/README.md +504 -0
- package/dist/api/admin/users.d.ts +36 -0
- package/dist/api/admin/users.d.ts.map +1 -0
- package/dist/api/admin/users.js +90 -0
- package/dist/api/auth/me.d.ts +17 -0
- package/dist/api/auth/me.d.ts.map +1 -0
- package/dist/api/auth/me.js +34 -0
- package/dist/api/auth/profile.d.ts +32 -0
- package/dist/api/auth/profile.d.ts.map +1 -0
- package/dist/api/auth/profile.js +108 -0
- package/dist/api/storage.d.ts +13 -0
- package/dist/api/storage.d.ts.map +1 -0
- package/dist/api/storage.js +47 -0
- package/dist/auth.build.config.d.ts.map +1 -1
- package/dist/auth.build.config.js +21 -0
- package/dist/web/admin/users.d.ts.map +1 -1
- package/dist/web/admin/users.js +87 -2
- package/dist/web/auth/dashboard.d.ts +1 -1
- package/dist/web/auth/dashboard.d.ts.map +1 -1
- package/dist/web/auth/dashboard.js +42 -2
- package/dist/web/auth/profile.d.ts.map +1 -1
- package/dist/web/auth/profile.js +152 -2
- package/dist/web/auth/reglage.d.ts.map +1 -1
- package/dist/web/auth/reglage.js +98 -2
- package/package.json +5 -4
- package/src/api/admin/users.ts +124 -0
- package/src/api/auth/me.ts +48 -0
- package/src/api/auth/profile.ts +156 -0
- package/src/api/public/signin.ts +31 -0
- package/src/api/storage.ts +63 -0
- package/src/auth.build.config.ts +137 -0
- package/src/components/Doc.tsx +310 -0
- package/src/index.ts +12 -0
- package/src/server.ts +2 -0
- package/src/web/admin/users.tsx +266 -0
- package/src/web/auth/dashboard.tsx +202 -0
- package/src/web/auth/profile.tsx +381 -0
- package/src/web/auth/reglage.tsx +304 -0
- package/src/web/public/ResetPassword.tsx +3 -0
- package/src/web/public/SignInPage.tsx +255 -0
- package/src/web/public/SignUpPage.tsx +293 -0
|
@@ -0,0 +1,293 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
import {
|
|
4
|
+
Button,
|
|
5
|
+
Card,
|
|
6
|
+
CardBody,
|
|
7
|
+
Input,
|
|
8
|
+
Link,
|
|
9
|
+
Chip,
|
|
10
|
+
addToast,
|
|
11
|
+
} from "@lastbrain/ui";
|
|
12
|
+
import { useRouter, useSearchParams } from "next/navigation";
|
|
13
|
+
import { Suspense, useState } from "react";
|
|
14
|
+
import {
|
|
15
|
+
Mail,
|
|
16
|
+
Lock,
|
|
17
|
+
User,
|
|
18
|
+
ArrowRight,
|
|
19
|
+
Sparkles,
|
|
20
|
+
CheckCircle2,
|
|
21
|
+
} from "lucide-react";
|
|
22
|
+
import { supabaseBrowserClient } from "@lastbrain/core";
|
|
23
|
+
|
|
24
|
+
function SignUpForm() {
|
|
25
|
+
const router = useRouter();
|
|
26
|
+
const searchParams = useSearchParams();
|
|
27
|
+
const [fullName, setFullName] = useState("");
|
|
28
|
+
const [email, setEmail] = useState("");
|
|
29
|
+
const [password, setPassword] = useState("");
|
|
30
|
+
const [confirmPassword, setConfirmPassword] = useState("");
|
|
31
|
+
const [loading, setLoading] = useState(false);
|
|
32
|
+
const [error, setError] = useState<string | null>(null);
|
|
33
|
+
const [success, setSuccess] = useState<string | null>(null);
|
|
34
|
+
|
|
35
|
+
// Récupérer le paramètre redirect
|
|
36
|
+
const redirectUrl = searchParams.get("redirect");
|
|
37
|
+
|
|
38
|
+
const handleSubmit = async (event: React.FormEvent<HTMLFormElement>) => {
|
|
39
|
+
event.preventDefault();
|
|
40
|
+
setError(null);
|
|
41
|
+
setSuccess(null);
|
|
42
|
+
|
|
43
|
+
if (password !== confirmPassword) {
|
|
44
|
+
setError("Les mots de passe ne correspondent pas.");
|
|
45
|
+
return;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
if (password.length < 6) {
|
|
49
|
+
setError("Le mot de passe doit contenir au moins 6 caractères.");
|
|
50
|
+
return;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
setLoading(true);
|
|
54
|
+
|
|
55
|
+
try {
|
|
56
|
+
const { data, error: signUpError } =
|
|
57
|
+
await supabaseBrowserClient.auth.signUp({
|
|
58
|
+
email,
|
|
59
|
+
password,
|
|
60
|
+
options: {
|
|
61
|
+
emailRedirectTo: `${window.location.origin}/api/auth/callback${
|
|
62
|
+
redirectUrl ? `?next=${encodeURIComponent(redirectUrl)}` : ""
|
|
63
|
+
}`,
|
|
64
|
+
data: {
|
|
65
|
+
full_name: fullName,
|
|
66
|
+
},
|
|
67
|
+
},
|
|
68
|
+
});
|
|
69
|
+
|
|
70
|
+
if (signUpError) {
|
|
71
|
+
setError(signUpError.message);
|
|
72
|
+
return;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
// Si la confirmation par email est requise
|
|
76
|
+
if (data.user && !data.session) {
|
|
77
|
+
setSuccess(
|
|
78
|
+
"Compte créé avec succès ! Veuillez vérifier votre email pour confirmer votre compte."
|
|
79
|
+
);
|
|
80
|
+
return;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
// Si l'utilisateur est directement connecté (confirmation email désactivée)
|
|
84
|
+
if (data.session) {
|
|
85
|
+
if (redirectUrl) {
|
|
86
|
+
window.location.href = redirectUrl;
|
|
87
|
+
} else {
|
|
88
|
+
window.location.href = "/auth/dashboard";
|
|
89
|
+
}
|
|
90
|
+
return;
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
setSuccess("Compte créé. Vous pouvez désormais vous connecter.");
|
|
94
|
+
setTimeout(() => {
|
|
95
|
+
if (redirectUrl) {
|
|
96
|
+
router.push(`/signin?redirect=${encodeURIComponent(redirectUrl)}`);
|
|
97
|
+
} else {
|
|
98
|
+
router.push("/signin");
|
|
99
|
+
}
|
|
100
|
+
}, 2000);
|
|
101
|
+
} catch (err) {
|
|
102
|
+
addToast({
|
|
103
|
+
title: "Erreur",
|
|
104
|
+
description: "Une erreur inattendue est survenue.",
|
|
105
|
+
color: "danger",
|
|
106
|
+
});
|
|
107
|
+
} finally {
|
|
108
|
+
setLoading(false);
|
|
109
|
+
}
|
|
110
|
+
};
|
|
111
|
+
|
|
112
|
+
return (
|
|
113
|
+
<div className="pt-12 relative min-h-screen w-full overflow-hidden bg-gradient-to-br from-secondary-50/30 to-primary-50/30 dark:from-secondary-950/50 dark:to-primary-950/50">
|
|
114
|
+
{/* Background decoration */}
|
|
115
|
+
<div className="absolute inset-0 bg-grid-slate-100 dark:bg-grid-slate-800/20 mask-[linear-gradient(0deg,white,rgba(255,255,255,0.6))] dark:mask-[linear-gradient(0deg,rgba(255,255,255,0.1),rgba(255,255,255,0.05))]" />
|
|
116
|
+
|
|
117
|
+
<div className="relative flex min-h-screen items-center justify-center px-4 py-12">
|
|
118
|
+
<div className="w-full max-w-md">
|
|
119
|
+
{/* Header */}
|
|
120
|
+
<div className="mb-8 text-center">
|
|
121
|
+
<Chip
|
|
122
|
+
color="secondary"
|
|
123
|
+
variant="flat"
|
|
124
|
+
startContent={<Sparkles className="w-4 h-4" />}
|
|
125
|
+
className="mb-4 p-2"
|
|
126
|
+
>
|
|
127
|
+
Rejoignez-nous
|
|
128
|
+
</Chip>
|
|
129
|
+
<h1 className="mb-3 text-4xl font-bold">
|
|
130
|
+
<span className="bg-gradient-to-r from-secondary-600 to-primary-600 bg-clip-text text-transparent">
|
|
131
|
+
Commencer gratuitement
|
|
132
|
+
</span>
|
|
133
|
+
</h1>
|
|
134
|
+
<p className="text-default-600 dark:text-default-400">
|
|
135
|
+
Créez votre compte en quelques secondes
|
|
136
|
+
</p>
|
|
137
|
+
</div>
|
|
138
|
+
|
|
139
|
+
{/* Form Card */}
|
|
140
|
+
<Card className="border border-default-200/60 bg-background/60 backdrop-blur-md backdrop-saturate-150 shadow-xl">
|
|
141
|
+
<CardBody className="gap-6 p-8">
|
|
142
|
+
<form onSubmit={handleSubmit} className="flex flex-col gap-6">
|
|
143
|
+
<Input
|
|
144
|
+
label="Nom complet"
|
|
145
|
+
type="text"
|
|
146
|
+
placeholder="Jean Dupont"
|
|
147
|
+
value={fullName}
|
|
148
|
+
onChange={(e) => setFullName(e.target.value)}
|
|
149
|
+
startContent={<User className="h-4 w-4 text-default-400" />}
|
|
150
|
+
classNames={{
|
|
151
|
+
input: "text-base",
|
|
152
|
+
inputWrapper:
|
|
153
|
+
"border border-default-200/60 hover:border-secondary-400 focus-within:border-secondary-500",
|
|
154
|
+
}}
|
|
155
|
+
/>
|
|
156
|
+
|
|
157
|
+
<Input
|
|
158
|
+
label="Email"
|
|
159
|
+
type="email"
|
|
160
|
+
placeholder="votre@email.com"
|
|
161
|
+
value={email}
|
|
162
|
+
onChange={(e) => setEmail(e.target.value)}
|
|
163
|
+
required
|
|
164
|
+
startContent={<Mail className="h-4 w-4 text-default-400" />}
|
|
165
|
+
classNames={{
|
|
166
|
+
input: "text-base",
|
|
167
|
+
inputWrapper:
|
|
168
|
+
"border border-default-200/60 hover:border-secondary-400 focus-within:border-secondary-500",
|
|
169
|
+
}}
|
|
170
|
+
/>
|
|
171
|
+
|
|
172
|
+
<Input
|
|
173
|
+
label="Mot de passe"
|
|
174
|
+
type="password"
|
|
175
|
+
placeholder="••••••••"
|
|
176
|
+
value={password}
|
|
177
|
+
onChange={(e) => setPassword(e.target.value)}
|
|
178
|
+
required
|
|
179
|
+
minLength={6}
|
|
180
|
+
startContent={<Lock className="h-4 w-4 text-default-400" />}
|
|
181
|
+
classNames={{
|
|
182
|
+
input: "text-base",
|
|
183
|
+
inputWrapper:
|
|
184
|
+
"border border-default-200/60 hover:border-secondary-400 focus-within:border-secondary-500",
|
|
185
|
+
}}
|
|
186
|
+
description="Minimum 6 caractères"
|
|
187
|
+
/>
|
|
188
|
+
|
|
189
|
+
<Input
|
|
190
|
+
label="Confirmer le mot de passe"
|
|
191
|
+
type="password"
|
|
192
|
+
placeholder="••••••••"
|
|
193
|
+
value={confirmPassword}
|
|
194
|
+
onChange={(e) => setConfirmPassword(e.target.value)}
|
|
195
|
+
required
|
|
196
|
+
minLength={6}
|
|
197
|
+
startContent={<Lock className="h-4 w-4 text-default-400" />}
|
|
198
|
+
classNames={{
|
|
199
|
+
input: "text-base",
|
|
200
|
+
inputWrapper:
|
|
201
|
+
"border border-default-200/60 hover:border-secondary-400 focus-within:border-secondary-500",
|
|
202
|
+
}}
|
|
203
|
+
/>
|
|
204
|
+
|
|
205
|
+
{error && (
|
|
206
|
+
<div className="rounded-lg border border-danger-200 bg-danger-50/50 px-4 py-3 dark:border-danger-800 dark:bg-danger-950/50">
|
|
207
|
+
<p className="text-sm text-danger-600 dark:text-danger-400">
|
|
208
|
+
{error}
|
|
209
|
+
</p>
|
|
210
|
+
</div>
|
|
211
|
+
)}
|
|
212
|
+
|
|
213
|
+
{success && (
|
|
214
|
+
<div className="rounded-lg border border-success-200 bg-success-50/50 px-4 py-3 dark:border-success-800 dark:bg-success-950/50">
|
|
215
|
+
<div className="flex items-center gap-2">
|
|
216
|
+
<CheckCircle2 className="h-4 w-4 text-success-600 dark:text-success-400" />
|
|
217
|
+
<p className="text-sm text-success-600 dark:text-success-400">
|
|
218
|
+
{success}
|
|
219
|
+
</p>
|
|
220
|
+
</div>
|
|
221
|
+
</div>
|
|
222
|
+
)}
|
|
223
|
+
|
|
224
|
+
<Button
|
|
225
|
+
type="submit"
|
|
226
|
+
color="secondary"
|
|
227
|
+
size="lg"
|
|
228
|
+
className="w-full font-semibold"
|
|
229
|
+
endContent={
|
|
230
|
+
loading ? null : <ArrowRight className="h-5 w-5" />
|
|
231
|
+
}
|
|
232
|
+
isLoading={loading}
|
|
233
|
+
>
|
|
234
|
+
Créer mon compte
|
|
235
|
+
</Button>
|
|
236
|
+
</form>
|
|
237
|
+
|
|
238
|
+
{/* Divider */}
|
|
239
|
+
<div className="relative flex items-center gap-4 py-2">
|
|
240
|
+
<div className="h-px flex-1 bg-default-200" />
|
|
241
|
+
<span className="text-xs text-default-400">OU</span>
|
|
242
|
+
<div className="h-px flex-1 bg-default-200" />
|
|
243
|
+
</div>
|
|
244
|
+
|
|
245
|
+
{/* Sign in link */}
|
|
246
|
+
<div className="text-center">
|
|
247
|
+
<p className="text-sm text-default-600">
|
|
248
|
+
Déjà inscrit ?{" "}
|
|
249
|
+
<Link
|
|
250
|
+
href={
|
|
251
|
+
redirectUrl
|
|
252
|
+
? `/signin?redirect=${encodeURIComponent(redirectUrl)}`
|
|
253
|
+
: "/signin"
|
|
254
|
+
}
|
|
255
|
+
className="font-semibold text-secondary-600 hover:text-secondary-700"
|
|
256
|
+
>
|
|
257
|
+
Se connecter
|
|
258
|
+
</Link>
|
|
259
|
+
</p>
|
|
260
|
+
</div>
|
|
261
|
+
</CardBody>
|
|
262
|
+
</Card>
|
|
263
|
+
|
|
264
|
+
{/* Footer */}
|
|
265
|
+
<p className="mt-8 text-center text-xs text-default-400">
|
|
266
|
+
En créant un compte, vous acceptez nos{" "}
|
|
267
|
+
<Link
|
|
268
|
+
href="/legal/terms"
|
|
269
|
+
className="underline hover:text-secondary-500"
|
|
270
|
+
>
|
|
271
|
+
conditions d'utilisation
|
|
272
|
+
</Link>{" "}
|
|
273
|
+
et notre{" "}
|
|
274
|
+
<Link
|
|
275
|
+
href="/legal/privacy"
|
|
276
|
+
className="underline hover:text-secondary-500"
|
|
277
|
+
>
|
|
278
|
+
politique de confidentialité
|
|
279
|
+
</Link>
|
|
280
|
+
</p>
|
|
281
|
+
</div>
|
|
282
|
+
</div>
|
|
283
|
+
</div>
|
|
284
|
+
);
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
export function SignUpPage() {
|
|
288
|
+
return (
|
|
289
|
+
<Suspense fallback={<div>Chargement...</div>}>
|
|
290
|
+
<SignUpForm />
|
|
291
|
+
</Suspense>
|
|
292
|
+
);
|
|
293
|
+
}
|