@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.
Files changed (41) hide show
  1. package/README.md +504 -0
  2. package/dist/api/admin/users.d.ts +36 -0
  3. package/dist/api/admin/users.d.ts.map +1 -0
  4. package/dist/api/admin/users.js +90 -0
  5. package/dist/api/auth/me.d.ts +17 -0
  6. package/dist/api/auth/me.d.ts.map +1 -0
  7. package/dist/api/auth/me.js +34 -0
  8. package/dist/api/auth/profile.d.ts +32 -0
  9. package/dist/api/auth/profile.d.ts.map +1 -0
  10. package/dist/api/auth/profile.js +108 -0
  11. package/dist/api/storage.d.ts +13 -0
  12. package/dist/api/storage.d.ts.map +1 -0
  13. package/dist/api/storage.js +47 -0
  14. package/dist/auth.build.config.d.ts.map +1 -1
  15. package/dist/auth.build.config.js +21 -0
  16. package/dist/web/admin/users.d.ts.map +1 -1
  17. package/dist/web/admin/users.js +87 -2
  18. package/dist/web/auth/dashboard.d.ts +1 -1
  19. package/dist/web/auth/dashboard.d.ts.map +1 -1
  20. package/dist/web/auth/dashboard.js +42 -2
  21. package/dist/web/auth/profile.d.ts.map +1 -1
  22. package/dist/web/auth/profile.js +152 -2
  23. package/dist/web/auth/reglage.d.ts.map +1 -1
  24. package/dist/web/auth/reglage.js +98 -2
  25. package/package.json +5 -4
  26. package/src/api/admin/users.ts +124 -0
  27. package/src/api/auth/me.ts +48 -0
  28. package/src/api/auth/profile.ts +156 -0
  29. package/src/api/public/signin.ts +31 -0
  30. package/src/api/storage.ts +63 -0
  31. package/src/auth.build.config.ts +137 -0
  32. package/src/components/Doc.tsx +310 -0
  33. package/src/index.ts +12 -0
  34. package/src/server.ts +2 -0
  35. package/src/web/admin/users.tsx +266 -0
  36. package/src/web/auth/dashboard.tsx +202 -0
  37. package/src/web/auth/profile.tsx +381 -0
  38. package/src/web/auth/reglage.tsx +304 -0
  39. package/src/web/public/ResetPassword.tsx +3 -0
  40. package/src/web/public/SignInPage.tsx +255 -0
  41. 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
+ }