@mesob/auth-react 0.0.8 → 0.1.0
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/auth/auth-page-layout.d.ts +1 -3
- package/dist/components/auth/auth-page-layout.js +1 -17
- package/dist/components/auth/auth-page-layout.js.map +1 -1
- package/dist/components/auth/countdown.js +70 -9
- package/dist/components/auth/countdown.js.map +1 -1
- package/dist/components/auth/forgot-password.js +101 -35
- package/dist/components/auth/forgot-password.js.map +1 -1
- package/dist/components/auth/pages/forgot-password-page.d.ts +2 -13
- package/dist/components/auth/pages/forgot-password-page.js +198 -126
- package/dist/components/auth/pages/forgot-password-page.js.map +1 -1
- package/dist/components/auth/pages/reset-password-page.d.ts +1 -12
- package/dist/components/auth/pages/reset-password-page.js +288 -200
- package/dist/components/auth/pages/reset-password-page.js.map +1 -1
- package/dist/components/auth/pages/sign-in-page.d.ts +1 -12
- package/dist/components/auth/pages/sign-in-page.js +352 -230
- package/dist/components/auth/pages/sign-in-page.js.map +1 -1
- package/dist/components/auth/pages/sign-up-page.d.ts +1 -11
- package/dist/components/auth/pages/sign-up-page.js +310 -216
- package/dist/components/auth/pages/sign-up-page.js.map +1 -1
- package/dist/components/auth/pages/verify-email-page.d.ts +2 -12
- package/dist/components/auth/pages/verify-email-page.js +203 -135
- package/dist/components/auth/pages/verify-email-page.js.map +1 -1
- package/dist/components/auth/pages/verify-phone-page.d.ts +1 -11
- package/dist/components/auth/pages/verify-phone-page.js +206 -137
- package/dist/components/auth/pages/verify-phone-page.js.map +1 -1
- package/dist/components/auth/reset-password-form.d.ts +1 -1
- package/dist/components/auth/reset-password-form.js +188 -106
- package/dist/components/auth/reset-password-form.js.map +1 -1
- package/dist/components/auth/sign-in.d.ts +3 -3
- package/dist/components/auth/sign-in.js +228 -109
- package/dist/components/auth/sign-in.js.map +1 -1
- package/dist/components/auth/sign-up.js +210 -122
- package/dist/components/auth/sign-up.js.map +1 -1
- package/dist/components/auth/verification-form.d.ts +1 -1
- package/dist/components/auth/verification-form.js +101 -53
- package/dist/components/auth/verification-form.js.map +1 -1
- package/dist/components/error-boundary.d.ts +27 -0
- package/dist/components/error-boundary.js +49 -0
- package/dist/components/error-boundary.js.map +1 -0
- package/dist/components/iam/permissions/permissions-page.d.ts +5 -0
- package/dist/components/iam/permissions/permissions-page.js +201 -0
- package/dist/components/iam/permissions/permissions-page.js.map +1 -0
- package/dist/components/iam/roles/roles-page.d.ts +5 -0
- package/dist/components/iam/roles/roles-page.js +199 -0
- package/dist/components/iam/roles/roles-page.js.map +1 -0
- package/dist/components/iam/sessions/sessions-page.d.ts +5 -0
- package/dist/components/iam/sessions/sessions-page.js +202 -0
- package/dist/components/iam/sessions/sessions-page.js.map +1 -0
- package/dist/components/iam/tenants/tenants-page.d.ts +5 -0
- package/dist/components/iam/tenants/tenants-page.js +202 -0
- package/dist/components/iam/tenants/tenants-page.js.map +1 -0
- package/dist/components/iam/users/users-page.d.ts +5 -0
- package/dist/components/iam/users/users-page.js +211 -0
- package/dist/components/iam/users/users-page.js.map +1 -0
- package/dist/components/profile/profile-page.d.ts +8 -0
- package/dist/components/profile/profile-page.js +163 -0
- package/dist/components/profile/profile-page.js.map +1 -0
- package/dist/components/shared/data-table/data-table.d.ts +22 -0
- package/dist/components/shared/data-table/data-table.js +85 -0
- package/dist/components/shared/data-table/data-table.js.map +1 -0
- package/dist/components/skeletons/auth-form-skeleton.d.ts +5 -0
- package/dist/components/skeletons/auth-form-skeleton.js +32 -0
- package/dist/components/skeletons/auth-form-skeleton.js.map +1 -0
- package/dist/components/skeletons/profile-skeleton.d.ts +5 -0
- package/dist/components/skeletons/profile-skeleton.js +33 -0
- package/dist/components/skeletons/profile-skeleton.js.map +1 -0
- package/dist/components/skeletons/table-skeleton.d.ts +9 -0
- package/dist/components/skeletons/table-skeleton.js +39 -0
- package/dist/components/skeletons/table-skeleton.js.map +1 -0
- package/dist/handle-error-BqDMxnQZ.d.ts +8 -0
- package/dist/index.d.ts +75 -208
- package/dist/index.js +2091 -1057
- package/dist/index.js.map +1 -1
- package/package.json +9 -3
- package/dist/handle-error-H0iqQxJ5.d.ts +0 -6
|
@@ -1,40 +1,75 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
|
|
3
3
|
// src/components/auth/pages/sign-in-page.tsx
|
|
4
|
-
import { useTranslations as useTranslations2 } from "next-intl";
|
|
5
|
-
import { useState as useState3 } from "react";
|
|
6
|
-
|
|
7
|
-
// src/context/auth-provider.tsx
|
|
8
4
|
import {
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
} from "react";
|
|
5
|
+
Alert,
|
|
6
|
+
AlertDescription,
|
|
7
|
+
AlertTitle
|
|
8
|
+
} from "@mesob/ui/components/alert";
|
|
9
|
+
import { toast } from "@mesob/ui/components/sonner";
|
|
10
|
+
import { IconAlertCircle } from "@tabler/icons-react";
|
|
11
|
+
import { useEffect as useEffect3, useState as useState3 } from "react";
|
|
12
|
+
|
|
13
|
+
// src/lib/translations.ts
|
|
14
|
+
function createTranslator(messages, namespace) {
|
|
15
|
+
return (key, params) => {
|
|
16
|
+
const fullKey = namespace ? `${namespace}.${key}` : key;
|
|
17
|
+
const keys = fullKey.split(".");
|
|
18
|
+
let value = messages;
|
|
19
|
+
for (const k of keys) {
|
|
20
|
+
if (value && typeof value === "object" && value !== null) {
|
|
21
|
+
value = value[k];
|
|
22
|
+
} else {
|
|
23
|
+
return fullKey;
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
if (typeof value !== "string") {
|
|
27
|
+
return fullKey;
|
|
28
|
+
}
|
|
29
|
+
if (params) {
|
|
30
|
+
return value.replace(
|
|
31
|
+
/\{(\w+)\}/g,
|
|
32
|
+
(_, param) => String(params[param] ?? `{${param}}`)
|
|
33
|
+
);
|
|
34
|
+
}
|
|
35
|
+
return value;
|
|
36
|
+
};
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
// src/provider.tsx
|
|
40
|
+
import { QueryClient, QueryClientProvider } from "@tanstack/react-query";
|
|
41
|
+
import { deepmerge } from "deepmerge-ts";
|
|
42
|
+
import createFetchClient from "openapi-fetch";
|
|
43
|
+
import createClient from "openapi-react-query";
|
|
44
|
+
import { createContext, useContext, useEffect, useState } from "react";
|
|
15
45
|
import { jsx } from "react/jsx-runtime";
|
|
16
|
-
var
|
|
17
|
-
var
|
|
18
|
-
|
|
46
|
+
var SessionContext = createContext(null);
|
|
47
|
+
var ApiContext = createContext(null);
|
|
48
|
+
var ConfigContext = createContext(null);
|
|
49
|
+
var queryClient = new QueryClient({
|
|
50
|
+
defaultOptions: {
|
|
51
|
+
queries: {
|
|
52
|
+
staleTime: 1e3 * 60 * 5,
|
|
53
|
+
gcTime: 1e3 * 60 * 10,
|
|
54
|
+
retry: 1,
|
|
55
|
+
refetchOnWindowFocus: false
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
});
|
|
59
|
+
function useApi() {
|
|
60
|
+
const context = useContext(ApiContext);
|
|
19
61
|
if (!context) {
|
|
20
|
-
throw new Error("
|
|
62
|
+
throw new Error("useApi must be used within MesobAuthProvider");
|
|
21
63
|
}
|
|
22
64
|
return context;
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
status;
|
|
29
|
-
details;
|
|
30
|
-
constructor(message, code, status, details) {
|
|
31
|
-
super(message);
|
|
32
|
-
this.name = "AuthError";
|
|
33
|
-
this.code = code;
|
|
34
|
-
this.status = status;
|
|
35
|
-
this.details = details;
|
|
65
|
+
}
|
|
66
|
+
function useConfig() {
|
|
67
|
+
const context = useContext(ConfigContext);
|
|
68
|
+
if (!context) {
|
|
69
|
+
throw new Error("useConfig must be used within MesobAuthProvider");
|
|
36
70
|
}
|
|
37
|
-
|
|
71
|
+
return context;
|
|
72
|
+
}
|
|
38
73
|
|
|
39
74
|
// src/constants/auth.error.codes.ts
|
|
40
75
|
var AUTH_ERROR_MAPPING = {
|
|
@@ -86,71 +121,67 @@ var AUTH_ERROR_MAPPING = {
|
|
|
86
121
|
var validCodes = Object.keys(AUTH_ERROR_MAPPING);
|
|
87
122
|
|
|
88
123
|
// src/utils/handle-error.ts
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
if (errorCode && AUTH_ERROR_MAPPING[errorCode]) {
|
|
101
|
-
const mapping = AUTH_ERROR_MAPPING[errorCode];
|
|
102
|
-
setError({
|
|
103
|
-
title: mapping.title,
|
|
104
|
-
description: mapping.description
|
|
105
|
-
});
|
|
106
|
-
return;
|
|
124
|
+
function isAuthError(err) {
|
|
125
|
+
return typeof err === "object" && err !== null && "message" in err && typeof err.message === "string";
|
|
126
|
+
}
|
|
127
|
+
function extractErrorCode(err) {
|
|
128
|
+
if (err.code && validCodes.includes(err.code)) {
|
|
129
|
+
return err.code;
|
|
130
|
+
}
|
|
131
|
+
if (err.message) {
|
|
132
|
+
const messageUpper = err.message.toUpperCase().trim();
|
|
133
|
+
if (validCodes.includes(messageUpper)) {
|
|
134
|
+
return messageUpper;
|
|
107
135
|
}
|
|
136
|
+
}
|
|
137
|
+
return "";
|
|
138
|
+
}
|
|
139
|
+
function handleAuthError(err, setError, t) {
|
|
140
|
+
const errorCode = extractErrorCode(err);
|
|
141
|
+
if (errorCode && AUTH_ERROR_MAPPING[errorCode]) {
|
|
142
|
+
const mapping = AUTH_ERROR_MAPPING[errorCode];
|
|
108
143
|
setError({
|
|
109
|
-
title:
|
|
110
|
-
|
|
111
|
-
description: err.message || t("errors.fallback")
|
|
144
|
+
title: mapping.title,
|
|
145
|
+
description: mapping.description
|
|
112
146
|
});
|
|
147
|
+
return;
|
|
148
|
+
}
|
|
149
|
+
setError({
|
|
150
|
+
title: t("errors.fallback"),
|
|
151
|
+
description: err.message || t("errors.fallback")
|
|
152
|
+
});
|
|
153
|
+
}
|
|
154
|
+
function handleGenericError(err, setError, t) {
|
|
155
|
+
const message = err instanceof Error ? err.message : t("errors.fallback");
|
|
156
|
+
setError({
|
|
157
|
+
title: "Error",
|
|
158
|
+
description: message
|
|
159
|
+
});
|
|
160
|
+
}
|
|
161
|
+
var handleError = (err, setError, t) => {
|
|
162
|
+
if (isAuthError(err)) {
|
|
163
|
+
handleAuthError(err, setError, t);
|
|
113
164
|
} else {
|
|
114
|
-
|
|
115
|
-
setError({
|
|
116
|
-
title: "Error",
|
|
117
|
-
description: message
|
|
118
|
-
});
|
|
165
|
+
handleGenericError(err, setError, t);
|
|
119
166
|
}
|
|
120
167
|
};
|
|
121
168
|
|
|
122
169
|
// src/components/auth/auth-page-layout.tsx
|
|
123
|
-
import {
|
|
124
|
-
Alert,
|
|
125
|
-
AlertDescription,
|
|
126
|
-
AlertTitle
|
|
127
|
-
} from "@mesob/ui/components/alert";
|
|
128
|
-
import { IconAlertCircle } from "@tabler/icons-react";
|
|
129
170
|
import { jsx as jsx2, jsxs } from "react/jsx-runtime";
|
|
130
171
|
var AuthPageLayout = ({
|
|
131
172
|
title,
|
|
132
173
|
description,
|
|
133
174
|
children,
|
|
134
|
-
error,
|
|
135
175
|
footer,
|
|
136
176
|
logoImage
|
|
137
177
|
}) => {
|
|
138
|
-
const errorContent = error ? (
|
|
139
|
-
// biome-ignore lint/style/noNestedTernary: <explanation>
|
|
140
|
-
typeof error === "string" ? { title: "Error", description: error } : error
|
|
141
|
-
) : null;
|
|
142
178
|
return /* @__PURE__ */ jsxs("div", { className: "space-y-4", children: [
|
|
143
|
-
/* @__PURE__ */ jsx2("div", { className: "flex size-8 mb-6 w-full items-center justify-center rounded-md", children: /* @__PURE__ */ jsx2("img", { src: logoImage || "", alt: "
|
|
179
|
+
/* @__PURE__ */ jsx2("div", { className: "flex size-8 mb-6 w-full items-center justify-center rounded-md", children: /* @__PURE__ */ jsx2("img", { src: logoImage || "", alt: "Mesob", width: 42, height: 42 }) }),
|
|
144
180
|
/* @__PURE__ */ jsxs("div", { className: "text-center", children: [
|
|
145
181
|
/* @__PURE__ */ jsx2("h1", { className: "text-2xl font-bold tracking-tight", children: title }),
|
|
146
182
|
description && /* @__PURE__ */ jsx2("p", { className: "mt-2 text-sm text-muted-foreground", children: description })
|
|
147
183
|
] }),
|
|
148
184
|
children,
|
|
149
|
-
errorContent && /* @__PURE__ */ jsxs(Alert, { variant: "destructive", children: [
|
|
150
|
-
/* @__PURE__ */ jsx2(IconAlertCircle, { className: "h-4 w-4" }),
|
|
151
|
-
/* @__PURE__ */ jsx2(AlertTitle, { children: errorContent.title }),
|
|
152
|
-
/* @__PURE__ */ jsx2(AlertDescription, { children: errorContent.description })
|
|
153
|
-
] }),
|
|
154
185
|
/* @__PURE__ */ jsx2("div", { className: "mt-2 w-full", children: footer && /* @__PURE__ */ jsx2("div", { className: "w-full text-center text-sm text-muted-foreground", children: footer }) })
|
|
155
186
|
] });
|
|
156
187
|
};
|
|
@@ -159,21 +190,24 @@ var AuthPageLayout = ({
|
|
|
159
190
|
import { zodResolver } from "@hookform/resolvers/zod";
|
|
160
191
|
import { Button } from "@mesob/ui/components/button";
|
|
161
192
|
import {
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
FormMessage
|
|
168
|
-
} from "@mesob/ui/components/form";
|
|
193
|
+
Field,
|
|
194
|
+
FieldError,
|
|
195
|
+
FieldGroup,
|
|
196
|
+
FieldLabel
|
|
197
|
+
} from "@mesob/ui/components/field";
|
|
169
198
|
import { Input } from "@mesob/ui/components/input";
|
|
170
199
|
import { Spinner } from "@mesob/ui/components/spinner";
|
|
171
200
|
import { IconEye, IconEyeOff } from "@tabler/icons-react";
|
|
172
|
-
import {
|
|
173
|
-
import {
|
|
174
|
-
import { useForm } from "react-hook-form";
|
|
201
|
+
import { useEffect as useEffect2, useState as useState2 } from "react";
|
|
202
|
+
import { Controller, useForm } from "react-hook-form";
|
|
175
203
|
import { z } from "zod";
|
|
176
204
|
|
|
205
|
+
// src/hooks/use-translator.ts
|
|
206
|
+
function useTranslator(namespace) {
|
|
207
|
+
const { config } = useConfig();
|
|
208
|
+
return createTranslator(config.messages || {}, namespace);
|
|
209
|
+
}
|
|
210
|
+
|
|
177
211
|
// src/utils/normalize-phone.ts
|
|
178
212
|
function normalizePhone(phone) {
|
|
179
213
|
const cleaned = phone.trim().replace(/\s/g, "");
|
|
@@ -194,182 +228,255 @@ function normalizePhone(phone) {
|
|
|
194
228
|
|
|
195
229
|
// src/components/auth/sign-in.tsx
|
|
196
230
|
import { jsx as jsx3, jsxs as jsxs2 } from "react/jsx-runtime";
|
|
231
|
+
var identifierSchema = (t) => z.object({
|
|
232
|
+
identifier: z.string().trim().min(1, { message: t("errors.requiredField") }).refine(
|
|
233
|
+
(val) => {
|
|
234
|
+
const isEmail = z.email().safeParse(val).success;
|
|
235
|
+
const phoneRegex = /^(\+2519|2519|09)\d{8}$/;
|
|
236
|
+
const isPhone2 = phoneRegex.test(val);
|
|
237
|
+
return isEmail || isPhone2;
|
|
238
|
+
},
|
|
239
|
+
{
|
|
240
|
+
message: t("errors.invalidEmailOrPhone")
|
|
241
|
+
}
|
|
242
|
+
)
|
|
243
|
+
});
|
|
244
|
+
var passwordSchema = (t) => z.object({
|
|
245
|
+
password: z.string().min(8, t("errors.passwordLength")).max(128, t("errors.longPasswordError"))
|
|
246
|
+
});
|
|
197
247
|
var SignIn = ({
|
|
198
248
|
onSubmit,
|
|
199
249
|
isLoading = false,
|
|
200
|
-
identifier = "",
|
|
250
|
+
identifier: initialIdentifier = "",
|
|
201
251
|
step = "identifier",
|
|
202
252
|
onBack: _onBack
|
|
203
253
|
}) => {
|
|
204
|
-
const
|
|
254
|
+
const { hooks } = useApi();
|
|
255
|
+
const t = useTranslator("Auth.signIn");
|
|
205
256
|
const [showPassword, setShowPassword] = useState2(false);
|
|
206
|
-
const
|
|
207
|
-
() => z.object({
|
|
208
|
-
account: z.string().trim().min(1, { message: t("errors.requiredField") }).refine(
|
|
209
|
-
(val) => {
|
|
210
|
-
const isEmail = z.string().email().safeParse(val).success;
|
|
211
|
-
const phoneRegex = /^(\+2519|2519|09)\d{8}$/;
|
|
212
|
-
const isPhone2 = phoneRegex.test(val);
|
|
213
|
-
return isEmail || isPhone2;
|
|
214
|
-
},
|
|
215
|
-
{
|
|
216
|
-
message: t("errors.invalidEmailOrPhone")
|
|
217
|
-
}
|
|
218
|
-
)
|
|
219
|
-
}),
|
|
220
|
-
[t]
|
|
221
|
-
);
|
|
222
|
-
const passwordSchema = useMemo(
|
|
223
|
-
() => z.object({
|
|
224
|
-
password: z.string().min(8, t("errors.passwordLength")).max(128, t("errors.longPasswordError"))
|
|
225
|
-
}),
|
|
226
|
-
[t]
|
|
227
|
-
);
|
|
257
|
+
const [currentIdentifier, setCurrentIdentifier] = useState2(initialIdentifier);
|
|
228
258
|
const identifierForm = useForm({
|
|
229
|
-
resolver: zodResolver(identifierSchema),
|
|
259
|
+
resolver: zodResolver(identifierSchema(t)),
|
|
230
260
|
defaultValues: {
|
|
231
|
-
|
|
232
|
-
}
|
|
261
|
+
identifier: initialIdentifier
|
|
262
|
+
},
|
|
263
|
+
mode: "onChange"
|
|
233
264
|
});
|
|
234
265
|
const passwordForm = useForm({
|
|
235
|
-
resolver: zodResolver(passwordSchema),
|
|
266
|
+
resolver: zodResolver(passwordSchema(t)),
|
|
236
267
|
defaultValues: {
|
|
237
268
|
password: ""
|
|
238
|
-
}
|
|
269
|
+
},
|
|
270
|
+
mode: "onChange"
|
|
239
271
|
});
|
|
240
|
-
const
|
|
272
|
+
const signInMutation = hooks.useMutation("post", "/sign-in");
|
|
273
|
+
useEffect2(() => {
|
|
274
|
+
if (initialIdentifier) {
|
|
275
|
+
identifierForm.setValue("identifier", initialIdentifier);
|
|
276
|
+
setCurrentIdentifier(initialIdentifier);
|
|
277
|
+
}
|
|
278
|
+
}, [initialIdentifier, identifierForm]);
|
|
279
|
+
const handleIdentifierSubmit = async (values) => {
|
|
241
280
|
const phoneRegex = /^(\+2519|2519|09)\d{8}$/;
|
|
242
|
-
const
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
const handlePasswordSubmit = passwordForm.handleSubmit(async (values) => {
|
|
281
|
+
const normalizedIdentifier = phoneRegex.test(values.identifier) ? normalizePhone(values.identifier) : values.identifier;
|
|
282
|
+
setCurrentIdentifier(normalizedIdentifier);
|
|
283
|
+
identifierForm.setValue("identifier", normalizedIdentifier);
|
|
246
284
|
await onSubmit(
|
|
247
|
-
{
|
|
248
|
-
"
|
|
285
|
+
{ identifier: normalizedIdentifier, password: "" },
|
|
286
|
+
"identifier"
|
|
249
287
|
);
|
|
250
|
-
}
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
288
|
+
};
|
|
289
|
+
const handlePasswordSubmit = async (values) => {
|
|
290
|
+
try {
|
|
291
|
+
await signInMutation.mutateAsync({
|
|
292
|
+
body: {
|
|
293
|
+
identifier: currentIdentifier || identifierForm.getValues("identifier"),
|
|
294
|
+
password: values.password
|
|
295
|
+
}
|
|
296
|
+
});
|
|
297
|
+
await onSubmit(
|
|
298
|
+
{
|
|
299
|
+
identifier: currentIdentifier || identifierForm.getValues("identifier"),
|
|
300
|
+
password: values.password
|
|
301
|
+
},
|
|
302
|
+
"password"
|
|
303
|
+
);
|
|
304
|
+
} catch {
|
|
305
|
+
await onSubmit(
|
|
259
306
|
{
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
307
|
+
identifier: currentIdentifier || identifierForm.getValues("identifier"),
|
|
308
|
+
password: values.password
|
|
309
|
+
},
|
|
310
|
+
"password"
|
|
311
|
+
);
|
|
312
|
+
}
|
|
313
|
+
};
|
|
314
|
+
const isPasswordStep = step === "password";
|
|
315
|
+
const isSubmitting = isLoading || signInMutation.isPending;
|
|
316
|
+
if (isPasswordStep) {
|
|
317
|
+
return /* @__PURE__ */ jsxs2(
|
|
318
|
+
"form",
|
|
319
|
+
{
|
|
320
|
+
id: "sign-in-password-form",
|
|
321
|
+
onSubmit: passwordForm.handleSubmit(handlePasswordSubmit),
|
|
322
|
+
children: [
|
|
323
|
+
/* @__PURE__ */ jsxs2(FieldGroup, { children: [
|
|
324
|
+
/* @__PURE__ */ jsxs2("div", { className: "text-center", children: [
|
|
325
|
+
/* @__PURE__ */ jsx3("p", { className: "text-sm text-muted-foreground mb-2", children: t("form.enterPasswordFor") }),
|
|
326
|
+
/* @__PURE__ */ jsx3("p", { className: "font-bold", children: currentIdentifier || identifierForm.getValues("identifier") })
|
|
327
|
+
] }),
|
|
328
|
+
/* @__PURE__ */ jsx3(
|
|
329
|
+
Controller,
|
|
330
|
+
{
|
|
331
|
+
name: "password",
|
|
332
|
+
control: passwordForm.control,
|
|
333
|
+
render: ({ field, fieldState }) => /* @__PURE__ */ jsxs2(Field, { "data-invalid": fieldState.invalid, children: [
|
|
334
|
+
/* @__PURE__ */ jsx3(FieldLabel, { htmlFor: "sign-in-password", children: t("form.passwordLabel") }),
|
|
335
|
+
/* @__PURE__ */ jsxs2("div", { className: "relative", children: [
|
|
336
|
+
/* @__PURE__ */ jsx3(
|
|
337
|
+
Input,
|
|
338
|
+
{
|
|
339
|
+
...field,
|
|
340
|
+
id: "sign-in-password",
|
|
341
|
+
type: showPassword ? "text" : "password",
|
|
342
|
+
placeholder: t("form.passwordPlaceholder"),
|
|
343
|
+
autoComplete: "current-password",
|
|
344
|
+
"aria-invalid": fieldState.invalid
|
|
345
|
+
}
|
|
346
|
+
),
|
|
347
|
+
/* @__PURE__ */ jsx3(
|
|
348
|
+
"button",
|
|
349
|
+
{
|
|
350
|
+
type: "button",
|
|
351
|
+
onClick: () => setShowPassword(!showPassword),
|
|
352
|
+
className: "absolute right-3 top-1/2 -translate-y-1/2 text-muted-foreground hover:text-foreground",
|
|
353
|
+
children: showPassword ? /* @__PURE__ */ jsx3(IconEyeOff, { className: "h-4 w-4" }) : /* @__PURE__ */ jsx3(IconEye, { className: "h-4 w-4" })
|
|
354
|
+
}
|
|
355
|
+
)
|
|
356
|
+
] }),
|
|
357
|
+
fieldState.invalid && /* @__PURE__ */ jsx3(FieldError, { errors: [fieldState.error] })
|
|
358
|
+
] })
|
|
359
|
+
}
|
|
360
|
+
)
|
|
361
|
+
] }),
|
|
362
|
+
/* @__PURE__ */ jsx3("div", { className: "mt-4", children: /* @__PURE__ */ jsxs2(Button, { type: "submit", className: "w-full", disabled: isSubmitting, children: [
|
|
363
|
+
isSubmitting && /* @__PURE__ */ jsx3(Spinner, {}),
|
|
364
|
+
isSubmitting ? t("form.submitting") : t("form.submit")
|
|
365
|
+
] }) })
|
|
366
|
+
]
|
|
367
|
+
}
|
|
368
|
+
);
|
|
369
|
+
}
|
|
370
|
+
return /* @__PURE__ */ jsxs2(
|
|
371
|
+
"form",
|
|
372
|
+
{
|
|
373
|
+
id: "sign-in-identifier-form",
|
|
374
|
+
onSubmit: identifierForm.handleSubmit(handleIdentifierSubmit),
|
|
375
|
+
children: [
|
|
376
|
+
/* @__PURE__ */ jsx3(FieldGroup, { children: /* @__PURE__ */ jsx3(
|
|
377
|
+
Controller,
|
|
378
|
+
{
|
|
379
|
+
name: "identifier",
|
|
380
|
+
control: identifierForm.control,
|
|
381
|
+
render: ({ field, fieldState }) => /* @__PURE__ */ jsxs2(Field, { "data-invalid": fieldState.invalid, children: [
|
|
382
|
+
/* @__PURE__ */ jsx3(FieldLabel, { htmlFor: "sign-in-identifier", children: t("form.accountLabel") }),
|
|
265
383
|
/* @__PURE__ */ jsx3(
|
|
266
384
|
Input,
|
|
267
385
|
{
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
386
|
+
...field,
|
|
387
|
+
id: "sign-in-identifier",
|
|
388
|
+
type: "text",
|
|
389
|
+
placeholder: t("form.accountPlaceholder"),
|
|
390
|
+
autoComplete: "username",
|
|
391
|
+
"aria-invalid": fieldState.invalid
|
|
272
392
|
}
|
|
273
393
|
),
|
|
274
|
-
/* @__PURE__ */ jsx3(
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
] })
|
|
286
|
-
}
|
|
287
|
-
),
|
|
288
|
-
/* @__PURE__ */ jsx3(Button, { type: "submit", className: "w-full", disabled: isLoading, children: isLoading ? t("form.submitting") : t("form.submit") })
|
|
289
|
-
] }) });
|
|
290
|
-
}
|
|
291
|
-
return /* @__PURE__ */ jsx3(Form, { ...identifierForm, children: /* @__PURE__ */ jsxs2("form", { onSubmit: handleIdentifierSubmit, className: "space-y-4", children: [
|
|
292
|
-
/* @__PURE__ */ jsx3(
|
|
293
|
-
FormField,
|
|
294
|
-
{
|
|
295
|
-
control: identifierForm.control,
|
|
296
|
-
name: "account",
|
|
297
|
-
render: ({ field }) => /* @__PURE__ */ jsxs2(FormItem, { children: [
|
|
298
|
-
/* @__PURE__ */ jsx3(FormLabel, { children: t("form.accountLabel") }),
|
|
299
|
-
/* @__PURE__ */ jsx3(FormControl, { children: /* @__PURE__ */ jsx3(
|
|
300
|
-
Input,
|
|
301
|
-
{
|
|
302
|
-
type: "text",
|
|
303
|
-
placeholder: t("form.accountPlaceholder"),
|
|
304
|
-
...field
|
|
305
|
-
}
|
|
306
|
-
) }),
|
|
307
|
-
/* @__PURE__ */ jsx3(FormMessage, {})
|
|
308
|
-
] })
|
|
309
|
-
}
|
|
310
|
-
),
|
|
311
|
-
/* @__PURE__ */ jsxs2(Button, { type: "submit", className: "w-full", disabled: isLoading, children: [
|
|
312
|
-
isLoading && /* @__PURE__ */ jsx3(Spinner, {}),
|
|
313
|
-
isLoading ? t("form.submitting") : t("form.continue")
|
|
314
|
-
] })
|
|
315
|
-
] }) });
|
|
394
|
+
fieldState.invalid && /* @__PURE__ */ jsx3(FieldError, { errors: [fieldState.error] })
|
|
395
|
+
] })
|
|
396
|
+
}
|
|
397
|
+
) }),
|
|
398
|
+
/* @__PURE__ */ jsx3("div", { className: "mt-4", children: /* @__PURE__ */ jsxs2(Button, { type: "submit", className: "w-full", disabled: isSubmitting, children: [
|
|
399
|
+
isSubmitting && /* @__PURE__ */ jsx3(Spinner, {}),
|
|
400
|
+
isSubmitting ? t("form.submitting") : t("form.continue")
|
|
401
|
+
] }) })
|
|
402
|
+
]
|
|
403
|
+
}
|
|
404
|
+
);
|
|
316
405
|
};
|
|
317
406
|
|
|
318
407
|
// src/components/auth/pages/sign-in-page.tsx
|
|
319
408
|
import { jsx as jsx4, jsxs as jsxs3 } from "react/jsx-runtime";
|
|
320
409
|
var isPhone = (s) => /^\+?[0-9()[\]\s-]{6,}$/.test(s);
|
|
321
|
-
var SignInPage = ({
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
links,
|
|
326
|
-
logoImage
|
|
327
|
-
}) => {
|
|
328
|
-
const t = useTranslations2("Auth.signIn");
|
|
329
|
-
const { client, setAuth } = useAuth();
|
|
410
|
+
var SignInPage = ({ redirectUrl } = {}) => {
|
|
411
|
+
const { hooks, setAuth } = useApi();
|
|
412
|
+
const { config } = useConfig();
|
|
413
|
+
const t = createTranslator(config.messages || {}, "Auth.signIn");
|
|
330
414
|
const [isLoading, setIsLoading] = useState3(false);
|
|
331
415
|
const [error, setError] = useState3(null);
|
|
332
416
|
const [step, setStep] = useState3("identifier");
|
|
333
417
|
const [identifier, setIdentifier] = useState3("");
|
|
334
|
-
const
|
|
335
|
-
const
|
|
336
|
-
const
|
|
418
|
+
const checkUserMutation = hooks.useMutation("post", "/check-user");
|
|
419
|
+
const signInMutation = hooks.useMutation("post", "/sign-in");
|
|
420
|
+
const defaultRedirect = redirectUrl || config.navigation?.defaultRedirectUrl || "/dashboard";
|
|
421
|
+
const forgotPasswordLink = config.navigation?.links?.forgotPassword || "/auth/forgot-password";
|
|
422
|
+
const signUpLink = config.navigation?.links?.signUp || "/auth/sign-up";
|
|
423
|
+
const Link = config.navigation?.linkComponent;
|
|
424
|
+
const onNavigate = config.navigation?.onNavigate || ((path) => {
|
|
425
|
+
if (typeof window !== "undefined") {
|
|
426
|
+
window.location.href = path;
|
|
427
|
+
}
|
|
428
|
+
});
|
|
429
|
+
const logoImage = config.ui.logoImage;
|
|
430
|
+
useEffect3(() => {
|
|
431
|
+
if (error) {
|
|
432
|
+
toast.error(error.title || "Error", {
|
|
433
|
+
description: error.description
|
|
434
|
+
});
|
|
435
|
+
}
|
|
436
|
+
}, [error]);
|
|
437
|
+
const handleIdentifierStep = async (identifierValue) => {
|
|
438
|
+
const checkResult = await checkUserMutation.mutateAsync({
|
|
439
|
+
body: {
|
|
440
|
+
identifier: identifierValue
|
|
441
|
+
}
|
|
442
|
+
});
|
|
443
|
+
if (checkResult.exists) {
|
|
444
|
+
setIdentifier(identifierValue);
|
|
445
|
+
setStep("password");
|
|
446
|
+
return;
|
|
447
|
+
}
|
|
448
|
+
const email = isPhone(identifierValue) ? "" : identifierValue;
|
|
449
|
+
if (email) {
|
|
450
|
+
onNavigate(`${signUpLink}?email=${encodeURIComponent(email)}`);
|
|
451
|
+
} else {
|
|
452
|
+
onNavigate(`${signUpLink}?phone=${encodeURIComponent(identifierValue)}`);
|
|
453
|
+
}
|
|
454
|
+
};
|
|
455
|
+
const handlePasswordStep = async (passwordValue) => {
|
|
456
|
+
const res = await signInMutation.mutateAsync({
|
|
457
|
+
body: {
|
|
458
|
+
identifier,
|
|
459
|
+
password: passwordValue
|
|
460
|
+
}
|
|
461
|
+
});
|
|
462
|
+
if ("verificationId" in res && res.verificationId) {
|
|
463
|
+
const verifyPath = isPhone(identifier) ? `/auth/verify-phone?context=sign-in&verificationId=${res.verificationId}&identifier=${encodeURIComponent(identifier)}` : `/auth/verify-email?verificationId=${res.verificationId}`;
|
|
464
|
+
onNavigate(verifyPath);
|
|
465
|
+
return;
|
|
466
|
+
}
|
|
467
|
+
if ("user" in res && "session" in res) {
|
|
468
|
+
setAuth(res);
|
|
469
|
+
}
|
|
470
|
+
onNavigate(defaultRedirect);
|
|
471
|
+
};
|
|
337
472
|
const handleSubmit = async (values, currentStep) => {
|
|
338
473
|
setIsLoading(true);
|
|
339
474
|
setError(null);
|
|
340
475
|
try {
|
|
341
476
|
if (currentStep === "identifier") {
|
|
342
|
-
|
|
343
|
-
identifier: values.account
|
|
344
|
-
});
|
|
345
|
-
if (checkResult.exists) {
|
|
346
|
-
setIdentifier(values.account);
|
|
347
|
-
setStep("password");
|
|
348
|
-
} else {
|
|
349
|
-
const email = isPhone(values.account) ? "" : values.account;
|
|
350
|
-
if (email) {
|
|
351
|
-
onNavigate(`${signUpLink}?email=${encodeURIComponent(email)}`);
|
|
352
|
-
} else {
|
|
353
|
-
onNavigate(
|
|
354
|
-
`${signUpLink}?phone=${encodeURIComponent(values.account)}`
|
|
355
|
-
);
|
|
356
|
-
}
|
|
357
|
-
return;
|
|
358
|
-
}
|
|
477
|
+
await handleIdentifierStep(values.identifier);
|
|
359
478
|
} else {
|
|
360
|
-
|
|
361
|
-
identifier,
|
|
362
|
-
password: values.password
|
|
363
|
-
});
|
|
364
|
-
if ("verificationId" in res && res.verificationId) {
|
|
365
|
-
const verifyPath = isPhone(identifier) ? `/auth/verify-phone?context=sign-in&verificationId=${res.verificationId}&identifier=${encodeURIComponent(identifier)}` : `/auth/verify-email?verificationId=${res.verificationId}`;
|
|
366
|
-
onNavigate(verifyPath);
|
|
367
|
-
return;
|
|
368
|
-
}
|
|
369
|
-
if ("user" in res && "session" in res) {
|
|
370
|
-
setAuth(res);
|
|
371
|
-
}
|
|
372
|
-
onNavigate(defaultRedirect);
|
|
479
|
+
await handlePasswordStep(values.password || "");
|
|
373
480
|
}
|
|
374
481
|
} catch (err) {
|
|
375
482
|
handleError(err, setError, t);
|
|
@@ -377,18 +484,26 @@ var SignInPage = ({
|
|
|
377
484
|
setIsLoading(false);
|
|
378
485
|
}
|
|
379
486
|
};
|
|
487
|
+
const isSubmitting = isLoading || checkUserMutation.isPending || signInMutation.isPending;
|
|
380
488
|
const handleBack = () => {
|
|
381
489
|
setStep("identifier");
|
|
382
490
|
setIdentifier("");
|
|
383
491
|
setError(null);
|
|
384
492
|
};
|
|
385
493
|
const isStepPassword = step === "password";
|
|
386
|
-
|
|
494
|
+
let errorContent = null;
|
|
495
|
+
if (error) {
|
|
496
|
+
if (typeof error === "string") {
|
|
497
|
+
errorContent = { title: "Error", description: error };
|
|
498
|
+
} else {
|
|
499
|
+
errorContent = error;
|
|
500
|
+
}
|
|
501
|
+
}
|
|
502
|
+
return /* @__PURE__ */ jsx4("div", { className: "space-y-4", children: /* @__PURE__ */ jsxs3(
|
|
387
503
|
AuthPageLayout,
|
|
388
504
|
{
|
|
389
505
|
title: t("title"),
|
|
390
506
|
description: isStepPassword ? void 0 : t("description"),
|
|
391
|
-
error,
|
|
392
507
|
logoImage,
|
|
393
508
|
footer: /* @__PURE__ */ jsxs3(
|
|
394
509
|
"div",
|
|
@@ -425,15 +540,22 @@ var SignInPage = ({
|
|
|
425
540
|
]
|
|
426
541
|
}
|
|
427
542
|
),
|
|
428
|
-
children:
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
543
|
+
children: [
|
|
544
|
+
/* @__PURE__ */ jsx4(
|
|
545
|
+
SignIn,
|
|
546
|
+
{
|
|
547
|
+
onSubmit: handleSubmit,
|
|
548
|
+
isLoading: isSubmitting,
|
|
549
|
+
identifier,
|
|
550
|
+
step
|
|
551
|
+
}
|
|
552
|
+
),
|
|
553
|
+
errorContent && /* @__PURE__ */ jsxs3(Alert, { variant: "destructive", className: "mt-4", children: [
|
|
554
|
+
/* @__PURE__ */ jsx4(IconAlertCircle, { className: "h-4 w-4" }),
|
|
555
|
+
/* @__PURE__ */ jsx4(AlertTitle, { children: errorContent.title }),
|
|
556
|
+
/* @__PURE__ */ jsx4(AlertDescription, { children: errorContent.description })
|
|
557
|
+
] })
|
|
558
|
+
]
|
|
437
559
|
}
|
|
438
560
|
) });
|
|
439
561
|
};
|