@lastbrain/module-auth 2.0.27 → 2.0.31
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 +55 -7
- package/dist/api/admin/signup-stats.d.ts.map +1 -1
- package/dist/api/admin/signup-stats.js +2 -1
- package/dist/api/admin/storage/usage.d.ts +18 -0
- package/dist/api/admin/storage/usage.d.ts.map +1 -0
- package/dist/api/admin/storage/usage.js +100 -0
- package/dist/api/admin/users/[id]/notifications.d.ts.map +1 -1
- package/dist/api/admin/users/[id]/notifications.js +3 -2
- package/dist/api/admin/users/[id].d.ts.map +1 -1
- package/dist/api/admin/users/[id].js +3 -2
- package/dist/api/admin/users/reactivate/[id].d.ts +16 -0
- package/dist/api/admin/users/reactivate/[id].d.ts.map +1 -0
- package/dist/api/admin/users/reactivate/[id].js +59 -0
- package/dist/api/admin/users/suspend/[id].d.ts +16 -0
- package/dist/api/admin/users/suspend/[id].d.ts.map +1 -0
- package/dist/api/admin/users/suspend/[id].js +59 -0
- package/dist/api/admin/users-by-source.d.ts.map +1 -1
- package/dist/api/admin/users-by-source.js +2 -1
- package/dist/api/admin/users.d.ts.map +1 -1
- package/dist/api/admin/users.js +53 -2
- package/dist/api/auth/account/email-change.d.ts +7 -0
- package/dist/api/auth/account/email-change.d.ts.map +1 -0
- package/dist/api/auth/account/email-change.js +39 -0
- package/dist/api/auth/account/reset-password.d.ts +7 -0
- package/dist/api/auth/account/reset-password.d.ts.map +1 -0
- package/dist/api/auth/account/reset-password.js +36 -0
- package/dist/api/auth/check-username.d.ts +9 -0
- package/dist/api/auth/check-username.d.ts.map +1 -0
- package/dist/api/auth/check-username.js +35 -0
- package/dist/api/auth/establish-session.d.ts +2 -0
- package/dist/api/auth/establish-session.d.ts.map +1 -0
- package/dist/api/auth/establish-session.js +23 -0
- package/dist/api/auth/me.d.ts +4 -4
- package/dist/api/auth/me.d.ts.map +1 -1
- package/dist/api/auth/me.js +28 -6
- package/dist/api/auth/profile.d.ts.map +1 -1
- package/dist/api/auth/profile.js +6 -3
- package/dist/api/auth/storage/recalculate.d.ts +15 -0
- package/dist/api/auth/storage/recalculate.d.ts.map +1 -0
- package/dist/api/auth/storage/recalculate.js +68 -0
- package/dist/api/auth/storage/usage.d.ts +10 -0
- package/dist/api/auth/storage/usage.d.ts.map +1 -0
- package/dist/api/auth/storage/usage.js +86 -0
- package/dist/api/public/add-welcome-bonus.d.ts +16 -0
- package/dist/api/public/add-welcome-bonus.d.ts.map +1 -0
- package/dist/api/public/add-welcome-bonus.js +177 -0
- package/dist/api/public/callback.d.ts +3 -0
- package/dist/api/public/callback.d.ts.map +1 -0
- package/dist/api/public/callback.js +197 -0
- package/dist/api/public/reset-password.d.ts +3 -0
- package/dist/api/public/reset-password.d.ts.map +1 -0
- package/dist/api/public/reset-password.js +43 -0
- package/dist/api/public/set-session.d.ts +7 -0
- package/dist/api/public/set-session.d.ts.map +1 -0
- package/dist/api/public/set-session.js +55 -0
- package/dist/api/public/signin.d.ts.map +1 -1
- package/dist/api/public/signin.js +31 -0
- package/dist/api/public/signup.d.ts.map +1 -1
- package/dist/api/public/signup.js +38 -27
- package/dist/api/public/webhook/storage-addon.d.ts +9 -0
- package/dist/api/public/webhook/storage-addon.d.ts.map +1 -0
- package/dist/api/public/webhook/storage-addon.js +155 -0
- package/dist/api/storage.js +2 -2
- package/dist/auth.build.config.d.ts.map +1 -1
- package/dist/auth.build.config.js +126 -11
- package/dist/components/AccountButton.d.ts.map +1 -1
- package/dist/components/AccountButton.js +54 -28
- package/dist/components/Doc.d.ts.map +1 -1
- package/dist/components/Doc.js +1 -1
- package/dist/components/HasProfil.d.ts +4 -0
- package/dist/components/HasProfil.d.ts.map +1 -0
- package/dist/components/HasProfil.js +39 -0
- package/dist/components/auth/dashboard.d.ts +1 -1
- package/dist/components/auth/dashboard.d.ts.map +1 -1
- package/dist/components/auth/dashboard.js +34 -7
- package/dist/index.d.ts +2 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -0
- package/dist/lib/app-branding-data.d.ts +22 -0
- package/dist/lib/app-branding-data.d.ts.map +1 -0
- package/dist/lib/app-branding-data.js +49 -0
- package/dist/lib/auth-email-service.d.ts +57 -0
- package/dist/lib/auth-email-service.d.ts.map +1 -0
- package/dist/lib/auth-email-service.js +382 -0
- package/dist/lib/auth-email-templates.d.ts +2 -0
- package/dist/lib/auth-email-templates.d.ts.map +1 -0
- package/dist/lib/auth-email-templates.js +1 -0
- package/dist/lib/site-url.d.ts +3 -0
- package/dist/lib/site-url.d.ts.map +1 -0
- package/dist/lib/site-url.js +11 -0
- package/dist/sitemap/manifest.d.ts +9 -0
- package/dist/sitemap/manifest.d.ts.map +1 -0
- package/dist/sitemap/manifest.js +14 -0
- package/dist/web/admin/signup-stats.js +3 -3
- package/dist/web/admin/user-detail.d.ts.map +1 -1
- package/dist/web/admin/user-detail.js +135 -14
- package/dist/web/admin/users-by-signup-source.js +2 -2
- package/dist/web/admin/users.d.ts.map +1 -1
- package/dist/web/admin/users.js +26 -7
- package/dist/web/auth/folder.d.ts.map +1 -1
- package/dist/web/auth/folder.js +4 -3
- package/dist/web/auth/profile.d.ts.map +1 -1
- package/dist/web/auth/profile.js +132 -13
- package/dist/web/auth/reglage.d.ts.map +1 -1
- package/dist/web/auth/reglage.js +15 -8
- package/dist/web/public/ResetPassword.d.ts.map +1 -1
- package/dist/web/public/ResetPassword.js +172 -2
- package/dist/web/public/SignInPage.d.ts.map +1 -1
- package/dist/web/public/SignInPage.js +39 -3
- package/dist/web/public/SignUpPage.d.ts.map +1 -1
- package/dist/web/public/SignUpPage.js +7 -2
- package/dist/web/public/auth-code-error.d.ts +2 -0
- package/dist/web/public/auth-code-error.d.ts.map +1 -0
- package/dist/web/public/auth-code-error.js +14 -0
- package/dist/web/public/confirm.d.ts +2 -0
- package/dist/web/public/confirm.d.ts.map +1 -0
- package/dist/web/public/confirm.js +157 -0
- package/package.json +10 -5
- package/src/api/admin/signup-stats.ts +2 -1
- package/src/api/admin/storage/usage.ts +141 -0
- package/src/api/admin/users/[id]/notifications.ts +3 -2
- package/src/api/admin/users/[id].ts +3 -2
- package/src/api/admin/users/reactivate/[id].ts +88 -0
- package/src/api/admin/users/suspend/[id].ts +85 -0
- package/src/api/admin/users-by-source.ts +2 -1
- package/src/api/admin/users.ts +59 -2
- package/src/api/auth/account/email-change.ts +54 -0
- package/src/api/auth/account/reset-password.ts +47 -0
- package/src/api/auth/check-username.ts +52 -0
- package/src/api/auth/establish-session.ts +32 -0
- package/src/api/auth/me.ts +29 -7
- package/src/api/auth/profile.ts +6 -2
- package/src/api/auth/storage/recalculate.ts +108 -0
- package/src/api/auth/storage/usage.ts +113 -0
- package/src/api/public/add-welcome-bonus.ts +229 -0
- package/src/api/public/callback.ts +307 -0
- package/src/api/public/reset-password.ts +52 -0
- package/src/api/public/set-session.ts +73 -0
- package/src/api/public/signin.ts +36 -0
- package/src/api/public/signup.ts +44 -37
- package/src/api/public/webhook/storage-addon.ts +267 -0
- package/src/api/storage.ts +2 -2
- package/src/auth.build.config.ts +126 -11
- package/src/components/AccountButton.tsx +114 -90
- package/src/components/Doc.tsx +47 -9
- package/src/components/HasProfil.tsx +63 -0
- package/src/components/auth/dashboard.tsx +54 -13
- package/src/i18n/en.json +76 -8
- package/src/i18n/es.json +330 -0
- package/src/i18n/fr.json +74 -8
- package/src/index.ts +2 -0
- package/src/lib/app-branding-data.ts +90 -0
- package/src/lib/auth-email-service.ts +508 -0
- package/src/lib/auth-email-templates.ts +5 -0
- package/src/lib/site-url.ts +17 -0
- package/src/sitemap/manifest.ts +26 -0
- package/src/web/admin/signup-stats.tsx +3 -3
- package/src/web/admin/user-detail.tsx +314 -15
- package/src/web/admin/users-by-signup-source.tsx +2 -2
- package/src/web/admin/users.tsx +50 -14
- package/src/web/auth/folder.tsx +23 -5
- package/src/web/auth/profile.tsx +227 -13
- package/src/web/auth/reglage.tsx +55 -24
- package/src/web/public/ResetPassword.tsx +301 -1
- package/src/web/public/SignInPage.tsx +43 -3
- package/src/web/public/SignUpPage.tsx +14 -5
- package/src/web/public/auth-code-error.tsx +49 -0
- package/src/web/public/confirm.tsx +195 -0
- package/supabase/migrations/20251112000001_auto_profile_and_admin_view.sql +3 -1
- package/supabase/migrations/20251127100000_rename_body_to_message.sql +8 -3
- package/supabase/migrations/20260120150001_add_user_storage.sql +105 -0
- package/supabase/migrations/20260122131200_add_global_addons_system.sql +305 -0
- package/supabase/migrations/20260123100000_enable_vector_extension.sql +9 -0
- package/supabase/migrations/20260123140000_module_auth_fix_get_user_limits_null.sql +93 -0
- package/supabase/migrations/20260123145000_module_auth_fix_circular_storage_base.sql +89 -0
- package/supabase/migrations/20260129400000_add_username_to_user_profil.sql +33 -0
- package/dist/web/auth/dashboard.d.ts +0 -2
- package/dist/web/auth/dashboard.d.ts.map +0 -1
- package/dist/web/auth/dashboard.js +0 -48
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
|
|
3
|
-
import { AppLink, Button } from "@lastbrain/ui";
|
|
3
|
+
import { AppLink, Button, ThemeSwitcher } from "@lastbrain/ui";
|
|
4
4
|
import { Avatar } from "@lastbrain/ui";
|
|
5
5
|
import {
|
|
6
6
|
Dropdown,
|
|
@@ -10,8 +10,9 @@ import {
|
|
|
10
10
|
} from "@lastbrain/ui";
|
|
11
11
|
import * as LucideIcons from "lucide-react";
|
|
12
12
|
import type { User } from "@supabase/supabase-js";
|
|
13
|
-
import { useModuleTranslation
|
|
13
|
+
import { useModuleTranslation } from "@lastbrain/app";
|
|
14
14
|
import { useLanguage } from "@lastbrain/app";
|
|
15
|
+
import { useLocalizedRouter, supabaseBrowserClient } from "@lastbrain/core";
|
|
15
16
|
|
|
16
17
|
interface AccountButtonProps {
|
|
17
18
|
item: {
|
|
@@ -42,49 +43,46 @@ export const AccountButton = ({
|
|
|
42
43
|
}: AccountButtonProps) => {
|
|
43
44
|
const { lang } = useLanguage();
|
|
44
45
|
const t = useModuleTranslation("auth");
|
|
46
|
+
const router = useLocalizedRouter();
|
|
45
47
|
|
|
46
48
|
if (!user)
|
|
47
49
|
return (
|
|
48
50
|
<>
|
|
49
|
-
<div className="block
|
|
51
|
+
<div className="block sm:hidden flex flex-inline gap-2 item-center">
|
|
50
52
|
<Button
|
|
51
53
|
as={AppLink}
|
|
52
54
|
href="/signin"
|
|
53
55
|
radius="full"
|
|
54
56
|
isIconOnly
|
|
55
|
-
variant="light"
|
|
56
|
-
color="primary"
|
|
57
|
-
>
|
|
58
|
-
<LucideIcons.LogIn size={16} />
|
|
59
|
-
</Button>
|
|
60
|
-
<Button
|
|
61
|
-
as={AppLink}
|
|
62
|
-
href="/signup"
|
|
63
|
-
radius="full"
|
|
64
|
-
isIconOnly
|
|
65
57
|
variant="flat"
|
|
66
|
-
|
|
67
|
-
color="secondary"
|
|
58
|
+
color="primary"
|
|
68
59
|
>
|
|
69
60
|
<LucideIcons.UserPlus2 size={16} />
|
|
70
61
|
</Button>
|
|
62
|
+
<div className="ms-2">
|
|
63
|
+
<ThemeSwitcher />
|
|
64
|
+
</div>
|
|
71
65
|
</div>
|
|
72
|
-
<div className="hidden
|
|
66
|
+
<div className="hidden sm:block flex flex-inline gap-2 item-center">
|
|
73
67
|
<Button
|
|
74
68
|
as={AppLink}
|
|
75
69
|
href="/signin"
|
|
76
70
|
startContent={<LucideIcons.LogIn size={16} />}
|
|
77
71
|
variant="light"
|
|
78
72
|
color="primary"
|
|
73
|
+
radius="full"
|
|
74
|
+
size="sm"
|
|
79
75
|
>
|
|
80
76
|
{t("signin")}
|
|
81
77
|
</Button>
|
|
82
78
|
<Button
|
|
83
79
|
as={AppLink}
|
|
84
80
|
href="/signup"
|
|
85
|
-
variant="
|
|
81
|
+
variant="bordered"
|
|
86
82
|
className="ml-2"
|
|
87
83
|
color="secondary"
|
|
84
|
+
radius="full"
|
|
85
|
+
size="sm"
|
|
88
86
|
startContent={<LucideIcons.UserPlus2 size={16} />}
|
|
89
87
|
>
|
|
90
88
|
{t("signup")}
|
|
@@ -94,80 +92,106 @@ export const AccountButton = ({
|
|
|
94
92
|
);
|
|
95
93
|
|
|
96
94
|
return (
|
|
97
|
-
<
|
|
98
|
-
<
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
<
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
95
|
+
<div className="flex flex-inline gap-2">
|
|
96
|
+
<Dropdown>
|
|
97
|
+
<DropdownTrigger>
|
|
98
|
+
{user?.user_metadata.avatar ? (
|
|
99
|
+
<Avatar
|
|
100
|
+
size="sm"
|
|
101
|
+
src={
|
|
102
|
+
user?.user_metadata.avatar
|
|
103
|
+
? `/api/storage/${user?.user_metadata.avatar}`
|
|
104
|
+
: undefined
|
|
105
|
+
}
|
|
106
|
+
title={user.email}
|
|
107
|
+
fallback={<LucideIcons.User2 size={18} />}
|
|
108
|
+
classNames={{
|
|
109
|
+
base: "cursor-pointer bg-white/0",
|
|
110
|
+
icon: "text-default-700",
|
|
111
|
+
}}
|
|
112
|
+
/>
|
|
113
|
+
) : (
|
|
114
|
+
<Button size="sm" variant="flat" radius="full" isIconOnly>
|
|
115
|
+
<LucideIcons.User2 size={16} />
|
|
116
|
+
</Button>
|
|
117
|
+
)}
|
|
118
|
+
</DropdownTrigger>
|
|
119
|
+
|
|
120
|
+
<DropdownMenu
|
|
121
|
+
className="relative"
|
|
122
|
+
items={[
|
|
123
|
+
{
|
|
124
|
+
key: "hello",
|
|
125
|
+
label: `${t("hello")} ${
|
|
126
|
+
user?.user_metadata?.full_name || user.email
|
|
127
|
+
}`,
|
|
128
|
+
isReadOnly: true,
|
|
129
|
+
},
|
|
130
|
+
...accountMenu.map((item) => ({
|
|
131
|
+
key: item.path,
|
|
132
|
+
label: item.title,
|
|
133
|
+
description: item.description,
|
|
134
|
+
icon: item.icon,
|
|
135
|
+
isLogout:
|
|
136
|
+
item.path.includes("signout") || item.path.includes("logout"),
|
|
137
|
+
href:
|
|
138
|
+
item.path.includes("signout") || item.path.includes("logout")
|
|
139
|
+
? undefined
|
|
140
|
+
: item.path,
|
|
141
|
+
})),
|
|
142
|
+
]}
|
|
143
|
+
>
|
|
144
|
+
{(item: {
|
|
145
|
+
key: string;
|
|
146
|
+
label: string;
|
|
147
|
+
description?: string;
|
|
148
|
+
icon?: string;
|
|
149
|
+
isLogout?: boolean;
|
|
150
|
+
href?: string;
|
|
151
|
+
isReadOnly?: boolean;
|
|
152
|
+
}) => {
|
|
153
|
+
const Icon = item.icon ? getIcon(item.icon) : null;
|
|
120
154
|
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
})),
|
|
142
|
-
]}
|
|
143
|
-
>
|
|
144
|
-
{(item: {
|
|
145
|
-
key: string;
|
|
146
|
-
label: string;
|
|
147
|
-
description?: string;
|
|
148
|
-
icon?: string;
|
|
149
|
-
isLogout?: boolean;
|
|
150
|
-
href?: string;
|
|
151
|
-
isReadOnly?: boolean;
|
|
152
|
-
}) => {
|
|
153
|
-
const Icon = item.icon ? getIcon(item.icon) : null;
|
|
155
|
+
const handleLogout = async () => {
|
|
156
|
+
try {
|
|
157
|
+
if (onLogout) {
|
|
158
|
+
await onLogout();
|
|
159
|
+
} else {
|
|
160
|
+
// default fallback: sign out via supabase client
|
|
161
|
+
try {
|
|
162
|
+
await supabaseBrowserClient.auth.signOut();
|
|
163
|
+
} catch (_e) {
|
|
164
|
+
// ignore
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
} finally {
|
|
168
|
+
try {
|
|
169
|
+
router.push("/");
|
|
170
|
+
} catch (_e) {
|
|
171
|
+
// ignore routing errors
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
};
|
|
154
175
|
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
176
|
+
return (
|
|
177
|
+
<DropdownItem
|
|
178
|
+
as={AppLink}
|
|
179
|
+
key={item.key}
|
|
180
|
+
href={item.href}
|
|
181
|
+
onPress={item.isLogout ? handleLogout : undefined}
|
|
182
|
+
color={item.isLogout ? "danger" : "default"}
|
|
183
|
+
description={!item.isLogout && item.description}
|
|
184
|
+
startContent={Icon && <Icon size={16} />}
|
|
185
|
+
isDisabled={item.isReadOnly}
|
|
186
|
+
isReadOnly={item.isReadOnly}
|
|
187
|
+
>
|
|
188
|
+
{item.label}
|
|
189
|
+
</DropdownItem>
|
|
190
|
+
);
|
|
191
|
+
}}
|
|
192
|
+
</DropdownMenu>
|
|
193
|
+
</Dropdown>
|
|
194
|
+
<ThemeSwitcher />
|
|
195
|
+
</div>
|
|
172
196
|
);
|
|
173
197
|
};
|
package/src/components/Doc.tsx
CHANGED
|
@@ -93,6 +93,24 @@ export function Doc() {
|
|
|
93
93
|
- ResetPassword
|
|
94
94
|
</span>
|
|
95
95
|
</div>
|
|
96
|
+
<div className="flex items-start gap-2">
|
|
97
|
+
<Chip size="sm" color="success" variant="flat">
|
|
98
|
+
GET
|
|
99
|
+
</Chip>
|
|
100
|
+
<code className="text-sm">/confirm</code>
|
|
101
|
+
<span className="text-sm text-slate-600 dark:text-slate-400">
|
|
102
|
+
- ConfirmPage
|
|
103
|
+
</span>
|
|
104
|
+
</div>
|
|
105
|
+
<div className="flex items-start gap-2">
|
|
106
|
+
<Chip size="sm" color="success" variant="flat">
|
|
107
|
+
GET
|
|
108
|
+
</Chip>
|
|
109
|
+
<code className="text-sm">/auth-code-error</code>
|
|
110
|
+
<span className="text-sm text-slate-600 dark:text-slate-400">
|
|
111
|
+
- AuthCodeErrorPage
|
|
112
|
+
</span>
|
|
113
|
+
</div>
|
|
96
114
|
</div>
|
|
97
115
|
</div>
|
|
98
116
|
<div>
|
|
@@ -100,15 +118,6 @@ export function Doc() {
|
|
|
100
118
|
Pages Protégées (Auth)
|
|
101
119
|
</h3>
|
|
102
120
|
<div className="space-y-2">
|
|
103
|
-
<div className="flex items-start gap-2">
|
|
104
|
-
<Chip size="sm" color="primary" variant="flat">
|
|
105
|
-
GET
|
|
106
|
-
</Chip>
|
|
107
|
-
<code className="text-sm">/dashboard</code>
|
|
108
|
-
<span className="text-sm text-slate-600 dark:text-slate-400">
|
|
109
|
-
- DashboardPage
|
|
110
|
-
</span>
|
|
111
|
-
</div>
|
|
112
121
|
<div className="flex items-start gap-2">
|
|
113
122
|
<Chip size="sm" color="primary" variant="flat">
|
|
114
123
|
GET
|
|
@@ -201,6 +210,25 @@ export function Doc() {
|
|
|
201
210
|
</Chip>
|
|
202
211
|
</div>
|
|
203
212
|
</div>
|
|
213
|
+
<div>
|
|
214
|
+
<h3 className="text-lg font-semibold mb-2">
|
|
215
|
+
<code>/api/public/callback</code>
|
|
216
|
+
</h3>
|
|
217
|
+
<div className="flex gap-2">
|
|
218
|
+
<Chip size="sm" color="success" variant="flat">
|
|
219
|
+
GET
|
|
220
|
+
</Chip>
|
|
221
|
+
<Chip size="sm" color="primary" variant="flat">
|
|
222
|
+
POST
|
|
223
|
+
</Chip>
|
|
224
|
+
<Chip size="sm" color="warning" variant="flat">
|
|
225
|
+
PUT
|
|
226
|
+
</Chip>
|
|
227
|
+
<Chip size="sm" color="danger" variant="flat">
|
|
228
|
+
DELETE
|
|
229
|
+
</Chip>
|
|
230
|
+
</div>
|
|
231
|
+
</div>
|
|
204
232
|
<div>
|
|
205
233
|
<h3 className="text-lg font-semibold mb-2">
|
|
206
234
|
<code>/api/auth/profile</code>
|
|
@@ -284,6 +312,16 @@ export function Doc() {
|
|
|
284
312
|
title="user_notifications"
|
|
285
313
|
description="Table user_notifications du module auth"
|
|
286
314
|
/>
|
|
315
|
+
<TableStructure
|
|
316
|
+
tableName="global_addons"
|
|
317
|
+
title="global_addons"
|
|
318
|
+
description="Table global_addons du module auth"
|
|
319
|
+
/>
|
|
320
|
+
<TableStructure
|
|
321
|
+
tableName="user_global_addons"
|
|
322
|
+
title="user_global_addons"
|
|
323
|
+
description="Table user_global_addons du module auth"
|
|
324
|
+
/>
|
|
287
325
|
</CardBody>
|
|
288
326
|
</Card>
|
|
289
327
|
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
import { logger, useModuleTranslation } from "@lastbrain/core";
|
|
2
|
+
import { Alert, AppLink, Skeleton } from "@lastbrain/ui";
|
|
3
|
+
import { useEffect, useState } from "react";
|
|
4
|
+
|
|
5
|
+
export const HasProfil = ({
|
|
6
|
+
hasUpdate,
|
|
7
|
+
}: {
|
|
8
|
+
hasUpdate?: (value: boolean | null) => void;
|
|
9
|
+
}) => {
|
|
10
|
+
const [profil, setProfil] = useState<any>(null);
|
|
11
|
+
const t = useModuleTranslation("auth");
|
|
12
|
+
const [loading, setLoading] = useState(true);
|
|
13
|
+
|
|
14
|
+
useEffect(() => {
|
|
15
|
+
const fetchStats = async () => {
|
|
16
|
+
try {
|
|
17
|
+
const responseProfil = await fetch("/api/auth/profile");
|
|
18
|
+
if (!responseProfil.ok) {
|
|
19
|
+
throw new Error("Failed to load stats");
|
|
20
|
+
}
|
|
21
|
+
const jsonProfil = await responseProfil.json();
|
|
22
|
+
|
|
23
|
+
setProfil(jsonProfil.data);
|
|
24
|
+
if (jsonProfil.data && jsonProfil.data.first_name) {
|
|
25
|
+
hasUpdate?.(true);
|
|
26
|
+
} else {
|
|
27
|
+
hasUpdate?.(false);
|
|
28
|
+
}
|
|
29
|
+
} catch (error) {
|
|
30
|
+
logger.error("[HasProfil] stats error", error);
|
|
31
|
+
} finally {
|
|
32
|
+
setLoading(false);
|
|
33
|
+
}
|
|
34
|
+
};
|
|
35
|
+
|
|
36
|
+
fetchStats();
|
|
37
|
+
}, []);
|
|
38
|
+
|
|
39
|
+
if (loading) {
|
|
40
|
+
return (
|
|
41
|
+
<div className="mb-6 mt-6">
|
|
42
|
+
<Skeleton className="w-full h-12 w-full rounded-md" />
|
|
43
|
+
</div>
|
|
44
|
+
);
|
|
45
|
+
}
|
|
46
|
+
return (
|
|
47
|
+
<div className="mb-6 mt-6">
|
|
48
|
+
{!profil ||
|
|
49
|
+
(!profil.first_name ? (
|
|
50
|
+
<Alert
|
|
51
|
+
color="danger"
|
|
52
|
+
endContent={
|
|
53
|
+
<AppLink className="underline" href="/auth/profile">
|
|
54
|
+
Go
|
|
55
|
+
</AppLink>
|
|
56
|
+
}
|
|
57
|
+
>
|
|
58
|
+
{t("please_complete_your_profile")}
|
|
59
|
+
</Alert>
|
|
60
|
+
) : null)}
|
|
61
|
+
</div>
|
|
62
|
+
);
|
|
63
|
+
};
|
|
@@ -9,10 +9,13 @@ import {
|
|
|
9
9
|
Chip,
|
|
10
10
|
Divider,
|
|
11
11
|
Avatar,
|
|
12
|
+
Skeleton,
|
|
12
13
|
} from "@lastbrain/ui";
|
|
13
14
|
|
|
14
15
|
import { User, Mail, Calendar, Shield, LayoutDashboard } from "lucide-react";
|
|
15
16
|
import { useModuleTranslation } from "@lastbrain/core";
|
|
17
|
+
import { useRouter } from "next/navigation";
|
|
18
|
+
import { HasProfil } from "../HasProfil";
|
|
16
19
|
|
|
17
20
|
interface UserData {
|
|
18
21
|
id: string;
|
|
@@ -33,6 +36,8 @@ export function DashboardPage() {
|
|
|
33
36
|
const [userData, setUserData] = useState<UserData | null>(null);
|
|
34
37
|
const [isLoading, setIsLoading] = useState(true);
|
|
35
38
|
const [error, setError] = useState<string | null>(null);
|
|
39
|
+
const [hasUpdate, setHasUpdate] = useState<boolean | null>(null);
|
|
40
|
+
const router = useRouter();
|
|
36
41
|
|
|
37
42
|
useEffect(() => {
|
|
38
43
|
fetchUserData();
|
|
@@ -41,13 +46,36 @@ export function DashboardPage() {
|
|
|
41
46
|
const fetchUserData = async () => {
|
|
42
47
|
try {
|
|
43
48
|
setIsLoading(true);
|
|
44
|
-
const response = await fetch("/api/auth/me");
|
|
49
|
+
const response = await fetch("/api/auth/me", { credentials: "include" });
|
|
45
50
|
|
|
46
51
|
if (!response.ok) {
|
|
47
52
|
throw new Error("Failed to fetch user data");
|
|
48
53
|
}
|
|
49
54
|
|
|
50
55
|
const result = await response.json();
|
|
56
|
+
|
|
57
|
+
// Si le serveur a synchronisé la locale côté cookie, rediriger
|
|
58
|
+
// le client vers le même chemin préfixé par la langue du profil.
|
|
59
|
+
if (result?.localeUpdated && result?.profileLang) {
|
|
60
|
+
try {
|
|
61
|
+
const profileLang = result.profileLang as string;
|
|
62
|
+
const pathname = window.location.pathname;
|
|
63
|
+
const search = window.location.search || "";
|
|
64
|
+
// Retirer un préfixe langue existant
|
|
65
|
+
const pathWithoutLang =
|
|
66
|
+
pathname.replace(/^\/[a-z]{2}(?=\/|$)/, "") || "/";
|
|
67
|
+
// Éviter de rediriger vers des routes API
|
|
68
|
+
const safePath = pathWithoutLang.startsWith("/api")
|
|
69
|
+
? "/"
|
|
70
|
+
: pathWithoutLang;
|
|
71
|
+
const target = `${profileLang.startsWith("/") ? profileLang : "/" + profileLang}${safePath}${search}`;
|
|
72
|
+
router.replace(target);
|
|
73
|
+
return;
|
|
74
|
+
} catch (e) {
|
|
75
|
+
console.debug("dashboard: locale redirect failed", e);
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
|
|
51
79
|
setUserData(result.data);
|
|
52
80
|
} catch (err) {
|
|
53
81
|
setError(err instanceof Error ? err.message : "An error occurred");
|
|
@@ -84,16 +112,25 @@ export function DashboardPage() {
|
|
|
84
112
|
}
|
|
85
113
|
|
|
86
114
|
if (!userData) {
|
|
87
|
-
return
|
|
115
|
+
return (
|
|
116
|
+
<>
|
|
117
|
+
<HasProfil />
|
|
118
|
+
</>
|
|
119
|
+
);
|
|
88
120
|
}
|
|
89
121
|
|
|
90
122
|
const fullName =
|
|
91
|
-
userData
|
|
123
|
+
userData?.profile?.first_name && userData.profile?.last_name
|
|
92
124
|
? `${userData.profile.first_name} ${userData.profile.last_name}`
|
|
93
125
|
: "User";
|
|
94
126
|
|
|
95
127
|
return (
|
|
96
|
-
<div className="
|
|
128
|
+
<div className="max-w-6xl mx-auto">
|
|
129
|
+
<HasProfil
|
|
130
|
+
hasUpdate={(v) => {
|
|
131
|
+
setHasUpdate(v);
|
|
132
|
+
}}
|
|
133
|
+
/>
|
|
97
134
|
<div className="flex flex-inline items-center gap-2 mb-4">
|
|
98
135
|
<LayoutDashboard size={24} />
|
|
99
136
|
<h1 className="text-3xl font-bold ">
|
|
@@ -162,15 +199,19 @@ export function DashboardPage() {
|
|
|
162
199
|
<span className="text-small">
|
|
163
200
|
{t("dashboard.profile") || "Profile"}
|
|
164
201
|
</span>
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
202
|
+
{hasUpdate === null ? (
|
|
203
|
+
<Skeleton className="w-16 h-6 rounded-full" />
|
|
204
|
+
) : (
|
|
205
|
+
<Chip
|
|
206
|
+
color={hasUpdate ? "success" : "warning"}
|
|
207
|
+
size="sm"
|
|
208
|
+
variant="flat"
|
|
209
|
+
>
|
|
210
|
+
{hasUpdate
|
|
211
|
+
? t("dashboard.complete") || "Complete"
|
|
212
|
+
: t("dashboard.incomplete") || "Incomplete"}
|
|
213
|
+
</Chip>
|
|
214
|
+
)}
|
|
174
215
|
</div>
|
|
175
216
|
</div>
|
|
176
217
|
</CardBody>
|
package/src/i18n/en.json
CHANGED
|
@@ -17,9 +17,11 @@
|
|
|
17
17
|
"hello": "Hello",
|
|
18
18
|
"menu.notifications": "Notifications",
|
|
19
19
|
"menu.notifications_desc": "Your notifications",
|
|
20
|
+
"module-auth.folder.storage_usage": "Storage Usage",
|
|
20
21
|
"menu.account": "Account",
|
|
21
22
|
"menu.account_desc": "Manage your account",
|
|
22
23
|
"menu.theme": "Theme",
|
|
24
|
+
"module-auth.or": "OR",
|
|
23
25
|
"menu.theme_desc": "Change the theme",
|
|
24
26
|
"menu.dashboard": "Dashboard",
|
|
25
27
|
"menu.dashboard_desc": "Access your dashboard",
|
|
@@ -119,6 +121,19 @@
|
|
|
119
121
|
"profile.avatar": "Profile picture",
|
|
120
122
|
"profile.save": "Save",
|
|
121
123
|
"profile.saving": "Saving...",
|
|
124
|
+
"profile.security": "Security",
|
|
125
|
+
"profile.security_new_email": "New email",
|
|
126
|
+
"profile.security_new_email_placeholder": "new@email.com",
|
|
127
|
+
"profile.security_email_change_btn": "Send confirmation",
|
|
128
|
+
"profile.security_email_change_sent": "Confirmation sent",
|
|
129
|
+
"profile.security_email_change_desc": "Check the new email to confirm the change",
|
|
130
|
+
"profile.security_email_required": "New email is required",
|
|
131
|
+
"profile.security_error": "Error",
|
|
132
|
+
"profile.security_reset_sent": "Link sent",
|
|
133
|
+
"profile.security_reset_desc": "Check your email to reset your password",
|
|
134
|
+
"profile.security_reset_title": "Reset password",
|
|
135
|
+
"profile.security_reset_help": "We will send a reset link to your email",
|
|
136
|
+
"profile.security_reset_btn": "Send link",
|
|
122
137
|
"profile.success": "Success",
|
|
123
138
|
"profile.updated": "Profile updated successfully",
|
|
124
139
|
"profile.success_title": "Success",
|
|
@@ -164,16 +179,20 @@
|
|
|
164
179
|
"signup_stats.total": "Total signups",
|
|
165
180
|
"signup_stats.lastbrain": "LastBrain signups",
|
|
166
181
|
"signup_stats.recipe": "Recipe signups",
|
|
167
|
-
"reset_password.title": "Reset password",
|
|
168
|
-
"reset_password.subtitle": "Enter your email to receive a reset link",
|
|
169
|
-
"reset_password.email_placeholder": "your@email.com",
|
|
170
|
-
"reset_password.submit": "Send link",
|
|
171
|
-
"reset_password.success": "Check your email for the reset link",
|
|
172
|
-
"reset_password.error": "Error sending link",
|
|
173
|
-
"reset_password.
|
|
182
|
+
"module-auth.reset_password.title": "Reset password",
|
|
183
|
+
"module-auth.reset_password.subtitle": "Enter your email to receive a reset link",
|
|
184
|
+
"module-auth.reset_password.email_placeholder": "your@email.com",
|
|
185
|
+
"module-auth.reset_password.submit": "Send link",
|
|
186
|
+
"module-auth.reset_password.success": "Check your email for the reset link",
|
|
187
|
+
"module-auth.reset_password.error": "Error sending link",
|
|
188
|
+
"module-auth.reset_password.link_expired": "Invalid or expired link, please request a new one.",
|
|
189
|
+
"module-auth.reset_password.back_to_signin": "Back to sign in",
|
|
174
190
|
"folder.loading": "Loading folder...",
|
|
175
191
|
"folder.error": "Error",
|
|
176
192
|
"folder.title": "Folder",
|
|
193
|
+
"please_complete_your_profile": "Please complete your profile to continue.",
|
|
194
|
+
"module-auth.dashboard.incomplete": "Incomplete",
|
|
195
|
+
|
|
177
196
|
"users_by_source.title": "Users by signup source",
|
|
178
197
|
"users_by_source.search_placeholder": "Search by email or name...",
|
|
179
198
|
"users_by_source.filter_label": "Filter by source",
|
|
@@ -242,6 +261,10 @@
|
|
|
242
261
|
"user_detail.notification_type_danger": "Danger",
|
|
243
262
|
"user_detail.notification_type_success": "Success",
|
|
244
263
|
"user_detail.send_button": "Send notification",
|
|
264
|
+
"user_detail.tab_storage": "Storage",
|
|
265
|
+
"user_detail.storage_usage": "Storage usage",
|
|
266
|
+
"user_detail.storage_addons": "Active storage addons",
|
|
267
|
+
"user_detail.no_storage_addons": "No active storage addons",
|
|
245
268
|
"user_detail.tab_settings": "Settings",
|
|
246
269
|
"user_detail.admin_actions": "Admin actions",
|
|
247
270
|
"user_detail.reset_password_btn": "Reset password",
|
|
@@ -261,5 +284,50 @@
|
|
|
261
284
|
"users.column_actions": "ACTIONS",
|
|
262
285
|
"users.never": "Never",
|
|
263
286
|
"users.view_button": "View",
|
|
264
|
-
"users.showing_results": "Showing {{count}} of {{total}} users"
|
|
287
|
+
"users.showing_results": "Showing {{count}} of {{total}} users",
|
|
288
|
+
"module-auth.settings.account": "Account Settings",
|
|
289
|
+
"module-auth.settings.appearance": "Appearance",
|
|
290
|
+
"module-auth.settings.dark": "Dark",
|
|
291
|
+
"module-auth.settings.email_notifications": "Email Notifications",
|
|
292
|
+
"module-auth.settings.email_notifications_desc": "Receive email notifications for important updates",
|
|
293
|
+
"module-auth.settings.error": "Error",
|
|
294
|
+
"module-auth.settings.language": "Language",
|
|
295
|
+
"module-auth.settings.language_placeholder": "Select a language",
|
|
296
|
+
"module-auth.settings.language_region": "Language & Region",
|
|
297
|
+
"module-auth.settings.lang_de": "Deutsch",
|
|
298
|
+
"module-auth.settings.lang_en": "English",
|
|
299
|
+
"module-auth.settings.lang_es": "Español",
|
|
300
|
+
"module-auth.settings.lang_fr": "Français",
|
|
301
|
+
"module-auth.settings.light": "Light",
|
|
302
|
+
"module-auth.settings.load_error_description": "Failed to load settings",
|
|
303
|
+
"module-auth.settings.load_error_title": "Error",
|
|
304
|
+
"module-auth.settings.loading": "Loading settings...",
|
|
305
|
+
"module-auth.settings.marketing_emails": "Marketing Emails",
|
|
306
|
+
"module-auth.settings.marketing_emails_desc": "Receive emails about new features and updates",
|
|
307
|
+
"module-auth.settings.notifications": "Notifications",
|
|
308
|
+
"module-auth.settings.push_notifications": "Push Notifications",
|
|
309
|
+
"module-auth.settings.push_notifications_desc": "Receive push notifications in your browser",
|
|
310
|
+
"module-auth.settings.reset": "Reset",
|
|
311
|
+
"module-auth.settings.save_button": "Save Settings",
|
|
312
|
+
"module-auth.settings.saving": "Saving...",
|
|
313
|
+
"module-auth.settings.select_theme": "Select a theme",
|
|
314
|
+
"module-auth.settings.success": "Success",
|
|
315
|
+
"module-auth.settings.system": "System",
|
|
316
|
+
"module-auth.settings.theme": "Theme",
|
|
317
|
+
"module-auth.settings.timezone": "Timezone",
|
|
318
|
+
"module-auth.settings.timezone_placeholder": "Select a timezone",
|
|
319
|
+
"module-auth.settings.tz_la": "America/Los_Angeles",
|
|
320
|
+
"module-auth.settings.tz_ny": "America/New_York",
|
|
321
|
+
"module-auth.settings.tz_paris": "Europe/Paris",
|
|
322
|
+
"module-auth.settings.tz_tokyo": "Asia/Tokyo",
|
|
323
|
+
"module-auth.settings.tz_utc": "UTC",
|
|
324
|
+
"module-auth.settings.update_failed": "Failed to update settings",
|
|
325
|
+
"module-auth.settings.updated": "Settings updated successfully",
|
|
326
|
+
"module-auth.user_detail.suspended": "Suspended",
|
|
327
|
+
"module-auth.user_detail.active": "Active",
|
|
328
|
+
"module-auth.user_detail.reactivate_account_btn": "Reactivate account",
|
|
329
|
+
"signin.suspended_title": "Account Suspended",
|
|
330
|
+
"module-auth.folder.note": "Note: This space groups the files generated by your different applications and modules. All files remain centralized and accessible from your personal space.",
|
|
331
|
+
|
|
332
|
+
"signin.suspended_message": "Your account has been suspended. Please contact support for more information."
|
|
265
333
|
}
|