@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,310 @@
1
+ import React from "react";
2
+ import {
3
+ Card,
4
+ CardBody,
5
+ CardHeader,
6
+ Snippet,
7
+ Chip,
8
+ TableStructure,
9
+ } from "@lastbrain/ui";
10
+
11
+ export function AuthModuleDoc() {
12
+ return (
13
+ <div className="space-y-6">
14
+ {/* Header */}
15
+ <div className="flex items-start justify-between">
16
+ <div>
17
+ <h2 className="text-3xl font-bold mb-2">Module Auth</h2>
18
+ <p className="text-slate-600 dark:text-slate-400">
19
+ Authentification complète avec gestion des profils utilisateur
20
+ </p>
21
+ </div>
22
+ <Chip color="primary" variant="flat">
23
+ v0.1.0
24
+ </Chip>
25
+ </div>
26
+
27
+ {/* Author */}
28
+ <Card>
29
+ <CardHeader>
30
+ <h3 className="text-xl font-semibold">📝 Informations</h3>
31
+ </CardHeader>
32
+ <CardBody className="space-y-2">
33
+ <div>
34
+ <span className="font-semibold">Auteur:</span> LastBrain Team
35
+ </div>
36
+ <div>
37
+ <span className="font-semibold">Package:</span>{" "}
38
+ @lastbrain/module-auth
39
+ </div>
40
+ <div>
41
+ <span className="font-semibold">Dernière mise à jour:</span> 21
42
+ novembre 2025
43
+ </div>
44
+ </CardBody>
45
+ </Card>
46
+
47
+ {/* Pages disponibles */}
48
+ <Card>
49
+ <CardHeader>
50
+ <h3 className="text-xl font-semibold">📄 Pages Disponibles</h3>
51
+ </CardHeader>
52
+ <CardBody className="space-y-3">
53
+ <div>
54
+ <h4 className="font-semibold mb-2">Pages Publiques</h4>
55
+ <div className="space-y-2">
56
+ <div className="flex items-center gap-2">
57
+ <Chip size="sm" color="success" variant="flat">
58
+ GET
59
+ </Chip>
60
+ <code className="text-sm">/signin</code>
61
+ <span className="text-slate-600 dark:text-slate-400">
62
+ - Connexion
63
+ </span>
64
+ </div>
65
+ <div className="flex items-center gap-2">
66
+ <Chip size="sm" color="success" variant="flat">
67
+ GET
68
+ </Chip>
69
+ <code className="text-sm">/signup</code>
70
+ <span className="text-slate-600 dark:text-slate-400">
71
+ - Inscription
72
+ </span>
73
+ </div>
74
+ <div className="flex items-center gap-2">
75
+ <Chip size="sm" color="success" variant="flat">
76
+ GET
77
+ </Chip>
78
+ <code className="text-sm">/reset-password</code>
79
+ <span className="text-slate-600 dark:text-slate-400">
80
+ - Réinitialisation mot de passe
81
+ </span>
82
+ </div>
83
+ </div>
84
+ </div>
85
+ <div>
86
+ <h4 className="font-semibold mb-2">Pages Protégées (Auth)</h4>
87
+ <div className="space-y-2">
88
+ <div className="flex items-center gap-2">
89
+ <Chip size="sm" color="primary" variant="flat">
90
+ GET
91
+ </Chip>
92
+ <code className="text-sm">/auth/dashboard</code>
93
+ <span className="text-slate-600 dark:text-slate-400">
94
+ - Tableau de bord
95
+ </span>
96
+ </div>
97
+ <div className="flex items-center gap-2">
98
+ <Chip size="sm" color="primary" variant="flat">
99
+ GET
100
+ </Chip>
101
+ <code className="text-sm">/auth/profile</code>
102
+ <span className="text-slate-600 dark:text-slate-400">
103
+ - Profil utilisateur
104
+ </span>
105
+ </div>
106
+ <div className="flex items-center gap-2">
107
+ <Chip size="sm" color="primary" variant="flat">
108
+ GET
109
+ </Chip>
110
+ <code className="text-sm">/auth/reglage</code>
111
+ <span className="text-slate-600 dark:text-slate-400">
112
+ - Paramètres
113
+ </span>
114
+ </div>
115
+ </div>
116
+ </div>
117
+ <div>
118
+ <h4 className="font-semibold mb-2">Pages Admin</h4>
119
+ <div className="space-y-2">
120
+ <div className="flex items-center gap-2">
121
+ <Chip size="sm" color="danger" variant="flat">
122
+ GET
123
+ </Chip>
124
+ <code className="text-sm">/admin/users</code>
125
+ <span className="text-slate-600 dark:text-slate-400">
126
+ - Gestion des utilisateurs
127
+ </span>
128
+ </div>
129
+ </div>
130
+ </div>
131
+ </CardBody>
132
+ </Card>
133
+
134
+ {/* Routes API */}
135
+ <Card>
136
+ <CardHeader>
137
+ <h3 className="text-xl font-semibold">🔌 Routes API</h3>
138
+ </CardHeader>
139
+ <CardBody className="space-y-2">
140
+ <div className="flex items-center gap-2">
141
+ <Chip size="sm" color="warning" variant="flat">
142
+ POST
143
+ </Chip>
144
+ <code className="text-sm">/api/auth/signin</code>
145
+ <span className="text-slate-600 dark:text-slate-400">
146
+ - Authentification
147
+ </span>
148
+ </div>
149
+ <div className="flex items-center gap-2">
150
+ <Chip size="sm" color="warning" variant="flat">
151
+ POST
152
+ </Chip>
153
+ <code className="text-sm">/api/auth/signup</code>
154
+ <span className="text-slate-600 dark:text-slate-400">
155
+ - Création de compte
156
+ </span>
157
+ </div>
158
+ <div className="flex items-center gap-2">
159
+ <Chip size="sm" color="warning" variant="flat">
160
+ POST
161
+ </Chip>
162
+ <code className="text-sm">/api/auth/signout</code>
163
+ <span className="text-slate-600 dark:text-slate-400">
164
+ - Déconnexion
165
+ </span>
166
+ </div>
167
+ <div className="flex items-center gap-2">
168
+ <Chip size="sm" color="warning" variant="flat">
169
+ POST
170
+ </Chip>
171
+ <code className="text-sm">/api/auth/reset-password</code>
172
+ <span className="text-slate-600 dark:text-slate-400">
173
+ - Réinitialisation
174
+ </span>
175
+ </div>
176
+ </CardBody>
177
+ </Card>
178
+
179
+ {/* Structure */}
180
+ <Card>
181
+ <CardHeader>
182
+ <h3 className="text-xl font-semibold">📁 Structure</h3>
183
+ </CardHeader>
184
+ <CardBody>
185
+ <Snippet
186
+ symbol=""
187
+ color="default"
188
+ size="sm"
189
+ hideSymbol
190
+ hideCopyButton
191
+ >
192
+ <div className="flex flex-col gap-1 text-xs font-mono">
193
+ <span>module-auth/</span>
194
+ <span>├── src/</span>
195
+ <span>│ ├── web/</span>
196
+ <span>│ │ ├── signin/</span>
197
+ <span>│ │ ├── signup/</span>
198
+ <span>│ │ ├── dashboard/</span>
199
+ <span>│ │ ├── profile/</span>
200
+ <span>│ │ └── reglage/</span>
201
+ <span>│ ├── api/</span>
202
+ <span>│ │ ├── signin.ts</span>
203
+ <span>│ │ ├── signup.ts</span>
204
+ <span>│ │ ├── signout.ts</span>
205
+ <span>│ │ └── reset-password.ts</span>
206
+ <span>│ ├── components/</span>
207
+ <span>│ │ └── Doc.tsx</span>
208
+ <span>│ └── auth.build.config.ts</span>
209
+ <span>└── supabase/</span>
210
+ <span> └── migrations/</span>
211
+ <span> └── 20251112000000_user_init.sql</span>
212
+ </div>
213
+ </Snippet>
214
+ </CardBody>
215
+ </Card>
216
+
217
+ {/* Tables de données */}
218
+ <Card>
219
+ <CardHeader>
220
+ <h3 className="text-xl font-semibold">🗄️ Tables de Données</h3>
221
+ </CardHeader>
222
+ <CardBody className="space-y-4">
223
+ <TableStructure
224
+ tableName="user_profil"
225
+ title="user_profil"
226
+ description="Profil étendu des utilisateurs (prénom, nom, téléphone, avatar, bio, etc.)"
227
+ />
228
+ <TableStructure
229
+ tableName="user_address"
230
+ title="user_address"
231
+ description="Adresses des utilisateurs (livraison, facturation)"
232
+ />
233
+ <TableStructure
234
+ tableName="user_notifications"
235
+ title="user_notifications"
236
+ description="Préférences de notifications (email, SMS, push)"
237
+ />
238
+ </CardBody>
239
+ </Card>
240
+
241
+ {/* Installation */}
242
+ <Card>
243
+ <CardHeader>
244
+ <h3 className="text-xl font-semibold">📦 Installation</h3>
245
+ </CardHeader>
246
+ <CardBody className="space-y-4">
247
+ <div className="flex flex-col gap-2">
248
+ <Snippet symbol="" color="default" size="sm">
249
+ pnpm lastbrain add-module auth
250
+ </Snippet>
251
+ <Snippet symbol="" color="default" size="sm">
252
+ pnpm build:modules
253
+ </Snippet>
254
+ <Snippet symbol="" color="default" size="sm">
255
+ supabase migration up
256
+ </Snippet>
257
+ </div>
258
+
259
+ {/* Danger Zone */}
260
+ <div className="border-2 border-danger rounded-lg p-4 mt-6">
261
+ <h4 className="text-lg font-semibold text-danger mb-3">
262
+ ⚠️ Danger Zone
263
+ </h4>
264
+ <p className="text-sm text-slate-600 dark:text-slate-400 mb-3">
265
+ La suppression du module supprimera toutes les pages, routes API
266
+ et migrations associées. Cette action est irréversible.
267
+ </p>
268
+ <div className="flex flex-col gap-2">
269
+ <Snippet symbol="" color="danger" size="sm">
270
+ pnpm lastbrain remove-module auth
271
+ </Snippet>
272
+ <Snippet symbol="" color="danger" size="sm">
273
+ pnpm build:modules
274
+ </Snippet>
275
+ </div>
276
+ </div>
277
+ </CardBody>
278
+ </Card>
279
+
280
+ {/* Utilisation */}
281
+ <Card>
282
+ <CardHeader>
283
+ <h3 className="text-xl font-semibold">💡 Utilisation</h3>
284
+ </CardHeader>
285
+ <CardBody className="space-y-3">
286
+ <div>
287
+ <h4 className="font-semibold mb-2">Hooks disponibles</h4>
288
+ <Snippet symbol="" color="default" size="sm">
289
+ import {"{ useAuthSession }"} from "@lastbrain/module-auth";
290
+ </Snippet>
291
+ </div>
292
+ <div>
293
+ <h4 className="font-semibold mb-2 mt-4">Exemple d'utilisation</h4>
294
+ <Snippet symbol="" color="default" size="sm" hideSymbol>
295
+ <div className="flex flex-col gap-1">
296
+ <span>const {"{ session, user }"} = useAuthSession();</span>
297
+ <span>
298
+ if (!session) return &lt;p&gt;Non connecté&lt;/p&gt;;
299
+ </span>
300
+ <span>
301
+ return &lt;div&gt;Bonjour {"{user.email}"}&lt;/div&gt;;
302
+ </span>
303
+ </div>
304
+ </Snippet>
305
+ </div>
306
+ </CardBody>
307
+ </Card>
308
+ </div>
309
+ );
310
+ }
package/src/index.ts ADDED
@@ -0,0 +1,12 @@
1
+ // Client Components uniquement
2
+ export { SignInPage } from "./web/public/SignInPage.js";
3
+ export { SignUpPage } from "./web/public/SignUpPage.js";
4
+ export { ResetPassword } from "./web/public/ResetPassword.js";
5
+ export { DashboardPage } from "./web/auth/dashboard.js";
6
+ export { ProfilePage } from "./web/auth/profile.js";
7
+ export { ReglagePage } from "./web/auth/reglage.js";
8
+ export { AdminUsersPage } from "./web/admin/users.js";
9
+ // Documentation
10
+ export { AuthModuleDoc } from "./components/Doc.js";
11
+ // Configuration de build (utilisée par les scripts)
12
+ export { default as authBuildConfig } from "./auth.build.config.js";
package/src/server.ts ADDED
@@ -0,0 +1,2 @@
1
+ // Server-only exports (Route Handlers, Server Actions, etc.)
2
+ export { POST as signInApi } from "./api/public/signin.js";
@@ -0,0 +1,266 @@
1
+ "use client";
2
+
3
+ import { useEffect, useState } from "react";
4
+ import {
5
+ Card,
6
+ CardBody,
7
+ CardHeader,
8
+ Table,
9
+ TableHeader,
10
+ TableColumn,
11
+ TableBody,
12
+ TableRow,
13
+ TableCell,
14
+ Spinner,
15
+ Chip,
16
+ Input,
17
+ Button,
18
+ Pagination,
19
+ Avatar,
20
+ addToast,
21
+ } from "@lastbrain/ui";
22
+ import { Users, Search, RefreshCw } from "lucide-react";
23
+
24
+ interface User {
25
+ id: string;
26
+ email: string;
27
+ created_at: string;
28
+ profile: {
29
+ first_name?: string;
30
+ last_name?: string;
31
+ avatar_url?: string;
32
+ company?: string;
33
+ location?: string;
34
+ };
35
+ }
36
+
37
+ interface PaginationData {
38
+ page: number;
39
+ per_page: number;
40
+ total: number;
41
+ total_pages: number;
42
+ }
43
+
44
+ export function AdminUsersPage() {
45
+ const [users, setUsers] = useState<User[]>([]);
46
+ const [isLoading, setIsLoading] = useState(true);
47
+ const [error, setError] = useState<string | null>(null);
48
+ const [searchQuery, setSearchQuery] = useState("");
49
+ const [pagination, setPagination] = useState<PaginationData>({
50
+ page: 1,
51
+ per_page: 20,
52
+ total: 0,
53
+ total_pages: 0,
54
+ });
55
+
56
+ useEffect(() => {
57
+ fetchUsers();
58
+ }, [pagination.page]);
59
+
60
+ const fetchUsers = async () => {
61
+ try {
62
+ setIsLoading(true);
63
+ const params = new URLSearchParams({
64
+ page: pagination.page.toString(),
65
+ per_page: pagination.per_page.toString(),
66
+ });
67
+
68
+ if (searchQuery) {
69
+ params.append("search", searchQuery);
70
+ }
71
+
72
+ const response = await fetch(`/api/admin/users?${params}`);
73
+
74
+ if (response.status === 403) {
75
+ setError("You don't have permission to access this page");
76
+ addToast({
77
+ title: "Access Denied",
78
+ description: "Superadmin access required",
79
+ color: "danger",
80
+ });
81
+ return;
82
+ }
83
+
84
+ if (!response.ok) {
85
+ throw new Error("Failed to fetch users");
86
+ }
87
+
88
+ const result = await response.json();
89
+ setUsers(result.data || []);
90
+ if (result.pagination) {
91
+ setPagination(result.pagination);
92
+ }
93
+ setError(null);
94
+ } catch (err) {
95
+ setError(err instanceof Error ? err.message : "An error occurred");
96
+ addToast({
97
+ title: "Error",
98
+ description: "Failed to load users",
99
+ color: "danger",
100
+ });
101
+ } finally {
102
+ setIsLoading(false);
103
+ }
104
+ };
105
+
106
+ const handleSearch = () => {
107
+ setPagination((prev) => ({ ...prev, page: 1 }));
108
+ fetchUsers();
109
+ };
110
+
111
+ const handlePageChange = (page: number) => {
112
+ setPagination((prev) => ({ ...prev, page }));
113
+ };
114
+
115
+ const formatDate = (dateString: string) => {
116
+ return new Date(dateString).toLocaleDateString("en-US", {
117
+ year: "numeric",
118
+ month: "short",
119
+ day: "numeric",
120
+ });
121
+ };
122
+
123
+ if (error && users.length === 0) {
124
+ return (
125
+ <div className="pt-12 pb-12 max-w-7xl mx-auto px-4">
126
+ <Card>
127
+ <CardBody>
128
+ <p className="text-danger">{error}</p>
129
+ </CardBody>
130
+ </Card>
131
+ </div>
132
+ );
133
+ }
134
+
135
+ return (
136
+ <div className="pt-12 pb-12 max-w-7xl mx-auto px-4">
137
+ <div className="flex items-center gap-2 mb-8">
138
+ <Users className="w-8 h-8" />
139
+ <h1 className="text-3xl font-bold">User Management</h1>
140
+ </div>
141
+
142
+ <Card>
143
+ <CardHeader>
144
+ <div className="flex flex-col md:flex-row gap-4 w-full">
145
+ <div className="flex gap-2 flex-1">
146
+ <Input
147
+ placeholder="Search by email or name..."
148
+ value={searchQuery}
149
+ onChange={(e) => setSearchQuery(e.target.value)}
150
+ onKeyPress={(e) => {
151
+ if (e.key === "Enter") {
152
+ handleSearch();
153
+ }
154
+ }}
155
+ startContent={<Search className="w-4 h-4 text-default-400" />}
156
+ className="flex-1"
157
+ />
158
+ <Button
159
+ color="primary"
160
+ onPress={handleSearch}
161
+ isDisabled={isLoading}
162
+ >
163
+ Search
164
+ </Button>
165
+ </div>
166
+ <Button
167
+ variant="flat"
168
+ onPress={fetchUsers}
169
+ isDisabled={isLoading}
170
+ startContent={<RefreshCw className="w-4 h-4" />}
171
+ >
172
+ Refresh
173
+ </Button>
174
+ </div>
175
+ </CardHeader>
176
+ <CardBody>
177
+ {isLoading ? (
178
+ <div className="flex justify-center items-center py-12">
179
+ <Spinner size="lg" label="Loading users..." />
180
+ </div>
181
+ ) : users.length === 0 ? (
182
+ <div className="text-center py-12 text-default-500">
183
+ No users found
184
+ </div>
185
+ ) : (
186
+ <>
187
+ <Table aria-label="Users table">
188
+ <TableHeader>
189
+ <TableColumn>USER</TableColumn>
190
+ <TableColumn>EMAIL</TableColumn>
191
+ <TableColumn>COMPANY</TableColumn>
192
+ <TableColumn>LOCATION</TableColumn>
193
+ <TableColumn>CREATED</TableColumn>
194
+ <TableColumn>STATUS</TableColumn>
195
+ </TableHeader>
196
+ <TableBody>
197
+ {users.map((user) => {
198
+ const fullName =
199
+ user.profile?.first_name && user.profile?.last_name
200
+ ? `${user.profile.first_name} ${user.profile.last_name}`
201
+ : "N/A";
202
+
203
+ return (
204
+ <TableRow key={user.id}>
205
+ <TableCell>
206
+ <div className="flex items-center gap-2">
207
+ <Avatar
208
+ src={user.profile?.avatar_url}
209
+ name={fullName}
210
+ size="sm"
211
+ />
212
+ <span className="text-small font-medium">
213
+ {fullName}
214
+ </span>
215
+ </div>
216
+ </TableCell>
217
+ <TableCell>
218
+ <span className="text-small">{user.email}</span>
219
+ </TableCell>
220
+ <TableCell>
221
+ <span className="text-small">
222
+ {user.profile?.company || "-"}
223
+ </span>
224
+ </TableCell>
225
+ <TableCell>
226
+ <span className="text-small">
227
+ {user.profile?.location || "-"}
228
+ </span>
229
+ </TableCell>
230
+ <TableCell>
231
+ <span className="text-small">
232
+ {formatDate(user.created_at)}
233
+ </span>
234
+ </TableCell>
235
+ <TableCell>
236
+ <Chip color="success" size="sm" variant="flat">
237
+ Active
238
+ </Chip>
239
+ </TableCell>
240
+ </TableRow>
241
+ );
242
+ })}
243
+ </TableBody>
244
+ </Table>
245
+
246
+ {pagination.total_pages > 1 && (
247
+ <div className="flex justify-center mt-4">
248
+ <Pagination
249
+ total={pagination.total_pages}
250
+ page={pagination.page}
251
+ onChange={handlePageChange}
252
+ showControls
253
+ />
254
+ </div>
255
+ )}
256
+
257
+ <div className="mt-4 text-small text-default-500 text-center">
258
+ Showing {users.length} of {pagination.total} users
259
+ </div>
260
+ </>
261
+ )}
262
+ </CardBody>
263
+ </Card>
264
+ </div>
265
+ );
266
+ }