@mesob/auth-react 0.1.1 → 0.2.1
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-card.js +1 -1
- package/dist/components/auth/auth-card.js.map +1 -1
- package/dist/components/auth/{auth-page-layout.d.ts → auth-layout.d.ts} +3 -3
- package/dist/components/auth/{auth-page-layout.js → auth-layout.js} +4 -4
- package/dist/components/auth/auth-layout.js.map +1 -0
- package/dist/components/auth/countdown.js +15 -6
- package/dist/components/auth/countdown.js.map +1 -1
- package/dist/components/auth/forgot-password.d.ts +1 -9
- package/dist/components/auth/forgot-password.js +267 -43
- package/dist/components/auth/forgot-password.js.map +1 -1
- package/dist/components/auth/reset-password-form.d.ts +2 -10
- package/dist/components/auth/reset-password-form.js +366 -118
- package/dist/components/auth/reset-password-form.js.map +1 -1
- package/dist/components/auth/sign-in.d.ts +2 -10
- package/dist/components/auth/sign-in.js +358 -130
- package/dist/components/auth/sign-in.js.map +1 -1
- package/dist/components/auth/sign-up.d.ts +2 -10
- package/dist/components/auth/sign-up.js +379 -131
- package/dist/components/auth/sign-up.js.map +1 -1
- package/dist/components/auth/verification-form.d.ts +2 -16
- package/dist/components/auth/verification-form.js +15 -6
- package/dist/components/auth/verification-form.js.map +1 -1
- package/dist/components/auth/verify-email.d.ts +10 -0
- package/dist/components/auth/{pages/verify-email-page.js → verify-email.js} +54 -34
- package/dist/components/auth/verify-email.js.map +1 -0
- package/dist/components/auth/verify-phone.d.ts +11 -0
- package/dist/components/auth/{pages/verify-phone-page.js → verify-phone.js} +53 -46
- package/dist/components/auth/verify-phone.js.map +1 -0
- package/dist/components/iam/permissions.d.ts +5 -0
- package/dist/components/iam/{permissions/permissions-page.js → permissions.js} +29 -13
- package/dist/components/iam/permissions.js.map +1 -0
- package/dist/components/iam/roles.d.ts +5 -0
- package/dist/components/iam/{roles/roles-page.js → roles.js} +29 -13
- package/dist/components/iam/roles.js.map +1 -0
- package/dist/components/iam/sessions.d.ts +5 -0
- package/dist/components/iam/{sessions/sessions-page.js → sessions.js} +13 -11
- package/dist/components/iam/sessions.js.map +1 -0
- package/dist/components/iam/tenants.d.ts +5 -0
- package/dist/components/iam/{tenants/tenants-page.js → tenants.js} +13 -11
- package/dist/components/iam/tenants.js.map +1 -0
- package/dist/components/iam/users.d.ts +5 -0
- package/dist/components/iam/{users/users-page.js → users.js} +13 -11
- package/dist/components/iam/users.js.map +1 -0
- package/dist/components/profile/account.d.ts +5 -0
- package/dist/components/profile/account.js +66 -0
- package/dist/components/profile/account.js.map +1 -0
- package/dist/components/profile/change-email-form.d.ts +5 -0
- package/dist/components/profile/change-email-form.js +721 -0
- package/dist/components/profile/change-email-form.js.map +1 -0
- package/dist/components/profile/change-password-form.d.ts +5 -0
- package/dist/components/profile/change-password-form.js +256 -0
- package/dist/components/profile/change-password-form.js.map +1 -0
- package/dist/components/profile/change-phone-form.d.ts +5 -0
- package/dist/components/profile/change-phone-form.js +758 -0
- package/dist/components/profile/change-phone-form.js.map +1 -0
- package/dist/components/profile/change-profile.d.ts +9 -0
- package/dist/components/profile/change-profile.js +37 -0
- package/dist/components/profile/change-profile.js.map +1 -0
- package/dist/components/profile/otp-verification-modal.d.ts +15 -0
- package/dist/components/profile/otp-verification-modal.js +261 -0
- package/dist/components/profile/otp-verification-modal.js.map +1 -0
- package/dist/components/profile/request-change-email-form.d.ts +10 -0
- package/dist/components/profile/request-change-email-form.js +293 -0
- package/dist/components/profile/request-change-email-form.js.map +1 -0
- package/dist/components/profile/request-change-phone-form.d.ts +10 -0
- package/dist/components/profile/request-change-phone-form.js +331 -0
- package/dist/components/profile/request-change-phone-form.js.map +1 -0
- package/dist/components/profile/security.d.ts +5 -0
- package/dist/components/profile/security.js +1439 -0
- package/dist/components/profile/security.js.map +1 -0
- package/dist/components/profile/verify-change-email-form.d.ts +11 -0
- package/dist/components/profile/verify-change-email-form.js +402 -0
- package/dist/components/profile/verify-change-email-form.js.map +1 -0
- package/dist/components/profile/verify-change-phone-form.d.ts +11 -0
- package/dist/components/profile/verify-change-phone-form.js +406 -0
- package/dist/components/profile/verify-change-phone-form.js.map +1 -0
- package/dist/components/shared/{data-table/data-table.js → data-table.js} +2 -2
- package/dist/components/shared/data-table.js.map +1 -0
- package/dist/index.d.ts +42 -93
- package/dist/index.js +2298 -1300
- package/dist/index.js.map +1 -1
- package/dist/types-D3s9oE-5.d.ts +75 -0
- package/dist/verification-form-ipSRTtQB.d.ts +22 -0
- package/package.json +3 -2
- package/dist/components/auth/auth-page-layout.js.map +0 -1
- package/dist/components/auth/pages/forgot-password-page.d.ts +0 -6
- package/dist/components/auth/pages/forgot-password-page.js +0 -362
- package/dist/components/auth/pages/forgot-password-page.js.map +0 -1
- package/dist/components/auth/pages/reset-password-page.d.ts +0 -9
- package/dist/components/auth/pages/reset-password-page.js +0 -514
- package/dist/components/auth/pages/reset-password-page.js.map +0 -1
- package/dist/components/auth/pages/sign-in-page.d.ts +0 -8
- package/dist/components/auth/pages/sign-in-page.js +0 -565
- package/dist/components/auth/pages/sign-in-page.js.map +0 -1
- package/dist/components/auth/pages/sign-up-page.d.ts +0 -9
- package/dist/components/auth/pages/sign-up-page.js +0 -518
- package/dist/components/auth/pages/sign-up-page.js.map +0 -1
- package/dist/components/auth/pages/verify-email-page.d.ts +0 -10
- package/dist/components/auth/pages/verify-email-page.js.map +0 -1
- package/dist/components/auth/pages/verify-phone-page.d.ts +0 -11
- package/dist/components/auth/pages/verify-phone-page.js.map +0 -1
- package/dist/components/iam/permissions/permissions-page.d.ts +0 -5
- package/dist/components/iam/permissions/permissions-page.js.map +0 -1
- package/dist/components/iam/roles/roles-page.d.ts +0 -5
- package/dist/components/iam/roles/roles-page.js.map +0 -1
- package/dist/components/iam/sessions/sessions-page.d.ts +0 -5
- package/dist/components/iam/sessions/sessions-page.js.map +0 -1
- package/dist/components/iam/tenants/tenants-page.d.ts +0 -5
- package/dist/components/iam/tenants/tenants-page.js.map +0 -1
- package/dist/components/iam/users/users-page.d.ts +0 -5
- package/dist/components/iam/users/users-page.js.map +0 -1
- package/dist/components/profile/profile-page.d.ts +0 -8
- package/dist/components/profile/profile-page.js +0 -163
- package/dist/components/profile/profile-page.js.map +0 -1
- package/dist/components/shared/data-table/data-table.js.map +0 -1
- package/dist/handle-error-BqDMxnQZ.d.ts +0 -8
- /package/dist/components/shared/{data-table/data-table.d.ts → data-table.d.ts} +0 -0
|
@@ -2,6 +2,11 @@
|
|
|
2
2
|
|
|
3
3
|
// src/components/auth/sign-in.tsx
|
|
4
4
|
import { zodResolver } from "@hookform/resolvers/zod";
|
|
5
|
+
import {
|
|
6
|
+
Alert,
|
|
7
|
+
AlertDescription,
|
|
8
|
+
AlertTitle
|
|
9
|
+
} from "@mesob/ui/components/alert";
|
|
5
10
|
import { Button } from "@mesob/ui/components/button";
|
|
6
11
|
import {
|
|
7
12
|
Field,
|
|
@@ -10,12 +15,17 @@ import {
|
|
|
10
15
|
FieldLabel
|
|
11
16
|
} from "@mesob/ui/components/field";
|
|
12
17
|
import { Input } from "@mesob/ui/components/input";
|
|
18
|
+
import { useMesob as useMesob2 } from "@mesob/ui/components/mesob-context";
|
|
13
19
|
import { Spinner } from "@mesob/ui/components/spinner";
|
|
14
|
-
import { IconEye, IconEyeOff } from "@tabler/icons-react";
|
|
15
|
-
import { useEffect
|
|
20
|
+
import { IconAlertCircle, IconEye, IconEyeOff } from "@tabler/icons-react";
|
|
21
|
+
import { useEffect, useState as useState2 } from "react";
|
|
16
22
|
import { Controller, useForm } from "react-hook-form";
|
|
23
|
+
import { toast } from "sonner";
|
|
17
24
|
import { z } from "zod";
|
|
18
25
|
|
|
26
|
+
// src/hooks/use-translator.ts
|
|
27
|
+
import { useMesob } from "@mesob/ui/components/mesob-context";
|
|
28
|
+
|
|
19
29
|
// src/lib/translations.ts
|
|
20
30
|
function createTranslator(messages, namespace) {
|
|
21
31
|
return (key, params) => {
|
|
@@ -47,7 +57,12 @@ import { QueryClient, QueryClientProvider } from "@tanstack/react-query";
|
|
|
47
57
|
import { deepmerge } from "deepmerge-ts";
|
|
48
58
|
import createFetchClient from "openapi-fetch";
|
|
49
59
|
import createClient from "openapi-react-query";
|
|
50
|
-
import { createContext, useContext,
|
|
60
|
+
import { createContext, useContext, useMemo, useState } from "react";
|
|
61
|
+
|
|
62
|
+
// src/utils/cookie.ts
|
|
63
|
+
var isProduction = typeof process !== "undefined" && process.env.NODE_ENV === "production";
|
|
64
|
+
|
|
65
|
+
// src/provider.tsx
|
|
51
66
|
import { jsx } from "react/jsx-runtime";
|
|
52
67
|
var SessionContext = createContext(null);
|
|
53
68
|
var ApiContext = createContext(null);
|
|
@@ -55,9 +70,6 @@ var ConfigContext = createContext(null);
|
|
|
55
70
|
var queryClient = new QueryClient({
|
|
56
71
|
defaultOptions: {
|
|
57
72
|
queries: {
|
|
58
|
-
staleTime: 1e3 * 60 * 5,
|
|
59
|
-
gcTime: 1e3 * 60 * 10,
|
|
60
|
-
retry: 1,
|
|
61
73
|
refetchOnWindowFocus: false
|
|
62
74
|
}
|
|
63
75
|
}
|
|
@@ -79,37 +91,168 @@ function useConfig() {
|
|
|
79
91
|
|
|
80
92
|
// src/hooks/use-translator.ts
|
|
81
93
|
function useTranslator(namespace) {
|
|
94
|
+
const mesob = useMesob();
|
|
82
95
|
const { config } = useConfig();
|
|
96
|
+
if (mesob?.t) {
|
|
97
|
+
return (key, params) => mesob.t(namespace ? `${namespace}.${key}` : key, params);
|
|
98
|
+
}
|
|
83
99
|
return createTranslator(config.messages || {}, namespace);
|
|
84
100
|
}
|
|
85
101
|
|
|
102
|
+
// src/constants/auth.error.codes.ts
|
|
103
|
+
var AUTH_ERROR_MAPPING = {
|
|
104
|
+
USER_NOT_FOUND: {
|
|
105
|
+
title: "Account Not Found",
|
|
106
|
+
description: "We could not find an account with that identifier. Please check your spelling or sign up."
|
|
107
|
+
},
|
|
108
|
+
INVALID_PASSWORD: {
|
|
109
|
+
title: "Invalid Password",
|
|
110
|
+
description: "The password you entered is incorrect. Please try again."
|
|
111
|
+
},
|
|
112
|
+
USER_EXISTS: {
|
|
113
|
+
title: "Account Already Exists",
|
|
114
|
+
description: "An account with this identifier already exists. Please sign in instead."
|
|
115
|
+
},
|
|
116
|
+
VERIFICATION_EXPIRED: {
|
|
117
|
+
title: "Verification Expired",
|
|
118
|
+
description: "The verification code or link has expired. Please request a new one."
|
|
119
|
+
},
|
|
120
|
+
VERIFICATION_MISMATCH: {
|
|
121
|
+
title: "Invalid Code",
|
|
122
|
+
description: "The verification code you entered is invalid. Please double-check and try again."
|
|
123
|
+
},
|
|
124
|
+
VERIFICATION_NOT_FOUND: {
|
|
125
|
+
title: "Verification Not Found",
|
|
126
|
+
description: "We could not find a pending verification request. Please restart the process."
|
|
127
|
+
},
|
|
128
|
+
TOO_MANY_ATTEMPTS: {
|
|
129
|
+
title: "Too Many Attempts",
|
|
130
|
+
description: "You have made too many requests recently. Please wait a moment before trying again."
|
|
131
|
+
},
|
|
132
|
+
REQUIRES_VERIFICATION: {
|
|
133
|
+
title: "Verification Required",
|
|
134
|
+
description: "You need to verify your account before you can continue. Please check your email or phone."
|
|
135
|
+
},
|
|
136
|
+
UNAUTHORIZED: {
|
|
137
|
+
title: "Unauthorized",
|
|
138
|
+
description: "You are not authorized to perform this action. Please sign in again."
|
|
139
|
+
},
|
|
140
|
+
ACCESS_DENIED: {
|
|
141
|
+
title: "Access Denied",
|
|
142
|
+
description: "You do not have permission to access this resource. Please contact support if you believe this is an error."
|
|
143
|
+
},
|
|
144
|
+
HAS_NO_PASSWORD: {
|
|
145
|
+
title: "No Password Set",
|
|
146
|
+
description: "Your account does not have a password set (e.g. social login). Please sign in with your provider or reset your password."
|
|
147
|
+
}
|
|
148
|
+
};
|
|
149
|
+
var validCodes = Object.keys(AUTH_ERROR_MAPPING);
|
|
150
|
+
|
|
151
|
+
// src/utils/handle-error.ts
|
|
152
|
+
function isAuthError(err) {
|
|
153
|
+
return typeof err === "object" && err !== null && "message" in err && typeof err.message === "string";
|
|
154
|
+
}
|
|
155
|
+
function extractErrorCode(err) {
|
|
156
|
+
if (err.code && validCodes.includes(err.code)) {
|
|
157
|
+
return err.code;
|
|
158
|
+
}
|
|
159
|
+
if (err.message) {
|
|
160
|
+
const messageUpper = err.message.toUpperCase().trim();
|
|
161
|
+
if (validCodes.includes(messageUpper)) {
|
|
162
|
+
return messageUpper;
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
return "";
|
|
166
|
+
}
|
|
167
|
+
function sanitizeErrorMessage(message) {
|
|
168
|
+
const lowerMessage = message.toLowerCase();
|
|
169
|
+
const isDatabaseError = lowerMessage.includes("failed query") || lowerMessage.includes("select") || lowerMessage.includes("insert") || lowerMessage.includes("update") || lowerMessage.includes("delete") || lowerMessage.includes("from") || lowerMessage.includes("where") || lowerMessage.includes("limit") || lowerMessage.includes("params:") || lowerMessage.includes("query") || message.includes('"iam".') || message.includes('"tenants"') || message.includes('"users"') || message.includes('"sessions"') || message.includes('"accounts"') || lowerMessage.includes("relation") || lowerMessage.includes("column") || lowerMessage.includes("syntax error") || lowerMessage.includes("database") || lowerMessage.includes("postgres") || lowerMessage.includes("sql");
|
|
170
|
+
if (isDatabaseError) {
|
|
171
|
+
return "An error occurred while processing your request";
|
|
172
|
+
}
|
|
173
|
+
return message;
|
|
174
|
+
}
|
|
175
|
+
function handleAuthError(err, setError, t) {
|
|
176
|
+
const errorCode = extractErrorCode(err);
|
|
177
|
+
if (errorCode && AUTH_ERROR_MAPPING[errorCode]) {
|
|
178
|
+
const mapping = AUTH_ERROR_MAPPING[errorCode];
|
|
179
|
+
setError({
|
|
180
|
+
title: mapping.title,
|
|
181
|
+
description: mapping.description
|
|
182
|
+
});
|
|
183
|
+
return;
|
|
184
|
+
}
|
|
185
|
+
const sanitizedMessage = sanitizeErrorMessage(
|
|
186
|
+
err.message || t("errors.fallback")
|
|
187
|
+
);
|
|
188
|
+
setError({
|
|
189
|
+
title: t("errors.fallback"),
|
|
190
|
+
description: sanitizedMessage
|
|
191
|
+
});
|
|
192
|
+
}
|
|
193
|
+
function handleGenericError(err, setError, t) {
|
|
194
|
+
const rawMessage = err instanceof Error ? err.message : t("errors.fallback");
|
|
195
|
+
const sanitizedMessage = sanitizeErrorMessage(rawMessage);
|
|
196
|
+
setError({
|
|
197
|
+
title: "Error",
|
|
198
|
+
description: sanitizedMessage
|
|
199
|
+
});
|
|
200
|
+
}
|
|
201
|
+
var handleError = (err, setError, t) => {
|
|
202
|
+
if (isAuthError(err)) {
|
|
203
|
+
handleAuthError(err, setError, t);
|
|
204
|
+
} else {
|
|
205
|
+
handleGenericError(err, setError, t);
|
|
206
|
+
}
|
|
207
|
+
};
|
|
208
|
+
|
|
86
209
|
// src/utils/normalize-phone.ts
|
|
87
210
|
function normalizePhone(phone) {
|
|
88
211
|
const cleaned = phone.trim().replace(/\s/g, "");
|
|
89
|
-
if (cleaned.startsWith("+2519")) {
|
|
212
|
+
if (cleaned.startsWith("+2519") || cleaned.startsWith("+2517")) {
|
|
90
213
|
return cleaned;
|
|
91
214
|
}
|
|
92
|
-
if (cleaned.startsWith("2519")) {
|
|
215
|
+
if (cleaned.startsWith("2519") || cleaned.startsWith("2517")) {
|
|
93
216
|
return `+${cleaned}`;
|
|
94
217
|
}
|
|
95
|
-
if (cleaned.startsWith("09")) {
|
|
218
|
+
if (cleaned.startsWith("09") || cleaned.startsWith("07")) {
|
|
96
219
|
return `+251${cleaned.slice(1)}`;
|
|
97
220
|
}
|
|
98
|
-
if (cleaned.startsWith("9") && cleaned.length === 9) {
|
|
221
|
+
if ((cleaned.startsWith("9") || cleaned.startsWith("7")) && cleaned.length === 9) {
|
|
99
222
|
return `+251${cleaned}`;
|
|
100
223
|
}
|
|
101
224
|
return cleaned;
|
|
102
225
|
}
|
|
103
226
|
|
|
104
|
-
// src/components/auth/
|
|
227
|
+
// src/components/auth/auth-layout.tsx
|
|
105
228
|
import { jsx as jsx2, jsxs } from "react/jsx-runtime";
|
|
106
|
-
var
|
|
107
|
-
|
|
229
|
+
var AuthLayout = ({
|
|
230
|
+
title,
|
|
231
|
+
description,
|
|
232
|
+
children,
|
|
233
|
+
footer,
|
|
234
|
+
logoImage
|
|
235
|
+
}) => {
|
|
236
|
+
return /* @__PURE__ */ jsxs("div", { className: "space-y-4", children: [
|
|
237
|
+
/* @__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 }) }),
|
|
238
|
+
/* @__PURE__ */ jsxs("div", { className: "text-center", children: [
|
|
239
|
+
/* @__PURE__ */ jsx2("h1", { className: "text-2xl font-bold tracking-tight", children: title }),
|
|
240
|
+
description && /* @__PURE__ */ jsx2("p", { className: "mt-2 text-sm text-muted-foreground", children: description })
|
|
241
|
+
] }),
|
|
242
|
+
children,
|
|
243
|
+
/* @__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 }) })
|
|
244
|
+
] });
|
|
245
|
+
};
|
|
246
|
+
|
|
247
|
+
// src/components/auth/sign-in.tsx
|
|
248
|
+
import { jsx as jsx3, jsxs as jsxs2 } from "react/jsx-runtime";
|
|
249
|
+
var isPhone = (s) => /^\+?[0-9()[\]\s-]{6,}$/.test(s);
|
|
250
|
+
var usernameSchema = (t, phoneRegex) => z.object({
|
|
251
|
+
username: z.string().trim().min(1, { message: t("errors.requiredField") }).refine(
|
|
108
252
|
(val) => {
|
|
109
253
|
const isEmail = z.email().safeParse(val).success;
|
|
110
|
-
const
|
|
111
|
-
|
|
112
|
-
return isEmail || isPhone;
|
|
254
|
+
const isPhone2 = phoneRegex.test(val);
|
|
255
|
+
return isEmail || isPhone2;
|
|
113
256
|
},
|
|
114
257
|
{
|
|
115
258
|
message: t("errors.invalidEmailOrPhone")
|
|
@@ -119,164 +262,249 @@ var identifierSchema = (t) => z.object({
|
|
|
119
262
|
var passwordSchema = (t) => z.object({
|
|
120
263
|
password: z.string().min(8, t("errors.passwordLength")).max(128, t("errors.longPasswordError"))
|
|
121
264
|
});
|
|
122
|
-
var SignIn = ({
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
step = "identifier",
|
|
127
|
-
onBack: _onBack
|
|
128
|
-
}) => {
|
|
129
|
-
const { hooks } = useApi();
|
|
265
|
+
var SignIn = ({ redirectUrl } = {}) => {
|
|
266
|
+
const { hooks, setAuth } = useApi();
|
|
267
|
+
const { config } = useConfig();
|
|
268
|
+
const mesob = useMesob2();
|
|
130
269
|
const t = useTranslator("Auth.signIn");
|
|
270
|
+
const Link = mesob?.linkComponent ?? config.navigation?.linkComponent;
|
|
271
|
+
const [isLoading, setIsLoading] = useState2(false);
|
|
272
|
+
const [error, setError] = useState2(null);
|
|
273
|
+
const [showPasswordField, setShowPasswordField] = useState2(false);
|
|
131
274
|
const [showPassword, setShowPassword] = useState2(false);
|
|
132
|
-
const [
|
|
133
|
-
const
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
275
|
+
const [username, setUsername] = useState2("");
|
|
276
|
+
const [isChecking, setIsChecking] = useState2(false);
|
|
277
|
+
const checkUserMutation = hooks.useMutation("post", "/check-account");
|
|
278
|
+
const signInMutation = hooks.useMutation("post", "/sign-in");
|
|
279
|
+
const phoneRegex = typeof config.phoneRegex === "string" ? new RegExp(config.phoneRegex) : config.phoneRegex || /^(\+2519|\+2517|2519|2517|09|07)\d{8}$/;
|
|
280
|
+
const defaultRedirect = redirectUrl || config.navigation?.defaultRedirectUrl || "/dashboard";
|
|
281
|
+
const forgotPasswordLink = config.navigation?.links?.forgotPassword || "/auth/forgot-password";
|
|
282
|
+
const signUpLink = config.navigation?.links?.signUp || "/auth/sign-up";
|
|
283
|
+
const onNavigate = config.navigation?.onNavigate || ((path) => {
|
|
284
|
+
if (typeof window !== "undefined") {
|
|
285
|
+
window.location.href = path;
|
|
286
|
+
}
|
|
287
|
+
});
|
|
288
|
+
const logoImage = config.ui.logoImage;
|
|
289
|
+
const usernameForm = useForm({
|
|
290
|
+
resolver: zodResolver(usernameSchema(t, phoneRegex)),
|
|
291
|
+
defaultValues: { username: "" },
|
|
138
292
|
mode: "onChange"
|
|
139
293
|
});
|
|
140
294
|
const passwordForm = useForm({
|
|
141
295
|
resolver: zodResolver(passwordSchema(t)),
|
|
142
|
-
defaultValues: {
|
|
143
|
-
password: ""
|
|
144
|
-
},
|
|
296
|
+
defaultValues: { password: "" },
|
|
145
297
|
mode: "onChange"
|
|
146
298
|
});
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
299
|
+
useEffect(() => {
|
|
300
|
+
if (error) {
|
|
301
|
+
toast.error(error.title || "Error", {
|
|
302
|
+
description: error.description
|
|
303
|
+
});
|
|
304
|
+
}
|
|
305
|
+
}, [error]);
|
|
306
|
+
const handleCheckAccount = async (usernameValue) => {
|
|
307
|
+
setIsChecking(true);
|
|
308
|
+
try {
|
|
309
|
+
const normalizedUsername = phoneRegex.test(usernameValue) ? normalizePhone(usernameValue) : usernameValue;
|
|
310
|
+
const result = await checkUserMutation.mutateAsync({
|
|
311
|
+
body: {
|
|
312
|
+
username: normalizedUsername
|
|
313
|
+
}
|
|
314
|
+
});
|
|
315
|
+
if (result.exists) {
|
|
316
|
+
setUsername(normalizedUsername);
|
|
317
|
+
setShowPasswordField(true);
|
|
318
|
+
} else {
|
|
319
|
+
const email = isPhone(normalizedUsername) ? "" : normalizedUsername;
|
|
320
|
+
if (email) {
|
|
321
|
+
onNavigate(`${signUpLink}?email=${encodeURIComponent(email)}`);
|
|
322
|
+
} else {
|
|
323
|
+
onNavigate(
|
|
324
|
+
`${signUpLink}?phone=${encodeURIComponent(normalizedUsername)}`
|
|
325
|
+
);
|
|
326
|
+
}
|
|
327
|
+
}
|
|
328
|
+
} catch {
|
|
329
|
+
usernameForm.setError("username", {
|
|
330
|
+
message: t("errors.checkAccountFailed")
|
|
331
|
+
});
|
|
332
|
+
} finally {
|
|
333
|
+
setIsChecking(false);
|
|
152
334
|
}
|
|
153
|
-
}
|
|
154
|
-
const
|
|
155
|
-
|
|
156
|
-
const normalizedIdentifier = phoneRegex.test(values.identifier) ? normalizePhone(values.identifier) : values.identifier;
|
|
157
|
-
setCurrentIdentifier(normalizedIdentifier);
|
|
158
|
-
identifierForm.setValue("identifier", normalizedIdentifier);
|
|
159
|
-
await onSubmit(
|
|
160
|
-
{ identifier: normalizedIdentifier, password: "" },
|
|
161
|
-
"identifier"
|
|
162
|
-
);
|
|
335
|
+
};
|
|
336
|
+
const handleUsernameSubmit = async (values) => {
|
|
337
|
+
await handleCheckAccount(values.username);
|
|
163
338
|
};
|
|
164
339
|
const handlePasswordSubmit = async (values) => {
|
|
340
|
+
setIsLoading(true);
|
|
341
|
+
setError(null);
|
|
165
342
|
try {
|
|
166
|
-
await signInMutation.mutateAsync({
|
|
343
|
+
const res = await signInMutation.mutateAsync({
|
|
167
344
|
body: {
|
|
168
|
-
identifier:
|
|
345
|
+
identifier: username,
|
|
169
346
|
password: values.password
|
|
170
347
|
}
|
|
171
348
|
});
|
|
172
|
-
|
|
173
|
-
{
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
"password"
|
|
186
|
-
);
|
|
349
|
+
if ("verificationId" in res && res.verificationId) {
|
|
350
|
+
const verifyPath = isPhone(username) ? `/auth/verify-phone?context=sign-in&verificationId=${res.verificationId}&identifier=${encodeURIComponent(username)}` : `/auth/verify-email?verificationId=${res.verificationId}`;
|
|
351
|
+
onNavigate(verifyPath);
|
|
352
|
+
return;
|
|
353
|
+
}
|
|
354
|
+
if ("user" in res && "session" in res) {
|
|
355
|
+
setAuth(res);
|
|
356
|
+
}
|
|
357
|
+
onNavigate(defaultRedirect);
|
|
358
|
+
} catch (err) {
|
|
359
|
+
handleError(err, setError, t);
|
|
360
|
+
} finally {
|
|
361
|
+
setIsLoading(false);
|
|
187
362
|
}
|
|
188
363
|
};
|
|
189
|
-
const
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
364
|
+
const handleBack = () => {
|
|
365
|
+
setShowPasswordField(false);
|
|
366
|
+
setUsername("");
|
|
367
|
+
passwordForm.reset();
|
|
368
|
+
};
|
|
369
|
+
const isSubmitting = isLoading || checkUserMutation.isPending || signInMutation.isPending || isChecking;
|
|
370
|
+
let errorContent = null;
|
|
371
|
+
if (error) {
|
|
372
|
+
if (typeof error === "string") {
|
|
373
|
+
errorContent = { title: "Error", description: error };
|
|
374
|
+
} else {
|
|
375
|
+
errorContent = error;
|
|
376
|
+
}
|
|
377
|
+
}
|
|
378
|
+
const formContent = showPasswordField ? /* @__PURE__ */ jsxs2(
|
|
379
|
+
"form",
|
|
380
|
+
{
|
|
381
|
+
id: "sign-in-form",
|
|
382
|
+
onSubmit: passwordForm.handleSubmit(handlePasswordSubmit),
|
|
383
|
+
children: [
|
|
384
|
+
/* @__PURE__ */ jsxs2(FieldGroup, { children: [
|
|
385
|
+
/* @__PURE__ */ jsxs2("div", { className: "text-center mb-4", children: [
|
|
386
|
+
/* @__PURE__ */ jsx3("p", { className: "text-sm text-muted-foreground mb-1", children: t("form.enterPasswordFor") }),
|
|
387
|
+
/* @__PURE__ */ jsx3("p", { className: "font-medium text-foreground", children: username }),
|
|
388
|
+
/* @__PURE__ */ jsx3(
|
|
389
|
+
"button",
|
|
205
390
|
{
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
/* @__PURE__ */ jsxs("div", { className: "relative", children: [
|
|
211
|
-
/* @__PURE__ */ jsx2(
|
|
212
|
-
Input,
|
|
213
|
-
{
|
|
214
|
-
...field,
|
|
215
|
-
id: "sign-in-password",
|
|
216
|
-
type: showPassword ? "text" : "password",
|
|
217
|
-
placeholder: t("form.passwordPlaceholder"),
|
|
218
|
-
autoComplete: "current-password",
|
|
219
|
-
"aria-invalid": fieldState.invalid
|
|
220
|
-
}
|
|
221
|
-
),
|
|
222
|
-
/* @__PURE__ */ jsx2(
|
|
223
|
-
"button",
|
|
224
|
-
{
|
|
225
|
-
type: "button",
|
|
226
|
-
onClick: () => setShowPassword(!showPassword),
|
|
227
|
-
className: "absolute right-3 top-1/2 -translate-y-1/2 text-muted-foreground hover:text-foreground",
|
|
228
|
-
children: showPassword ? /* @__PURE__ */ jsx2(IconEyeOff, { className: "h-4 w-4" }) : /* @__PURE__ */ jsx2(IconEye, { className: "h-4 w-4" })
|
|
229
|
-
}
|
|
230
|
-
)
|
|
231
|
-
] }),
|
|
232
|
-
fieldState.invalid && /* @__PURE__ */ jsx2(FieldError, { errors: [fieldState.error] })
|
|
233
|
-
] })
|
|
391
|
+
type: "button",
|
|
392
|
+
onClick: handleBack,
|
|
393
|
+
className: "text-sm text-primary hover:underline mt-2",
|
|
394
|
+
children: t("changeAccount")
|
|
234
395
|
}
|
|
235
396
|
)
|
|
236
397
|
] }),
|
|
237
|
-
/* @__PURE__ */
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
398
|
+
/* @__PURE__ */ jsx3(
|
|
399
|
+
Controller,
|
|
400
|
+
{
|
|
401
|
+
name: "password",
|
|
402
|
+
control: passwordForm.control,
|
|
403
|
+
render: ({ field, fieldState }) => /* @__PURE__ */ jsxs2(Field, { "data-invalid": fieldState.invalid, children: [
|
|
404
|
+
/* @__PURE__ */ jsx3(FieldLabel, { htmlFor: "sign-in-password", children: t("form.passwordLabel") }),
|
|
405
|
+
/* @__PURE__ */ jsxs2("div", { className: "relative", children: [
|
|
406
|
+
/* @__PURE__ */ jsx3(
|
|
407
|
+
Input,
|
|
408
|
+
{
|
|
409
|
+
...field,
|
|
410
|
+
id: "sign-in-password",
|
|
411
|
+
type: showPassword ? "text" : "password",
|
|
412
|
+
placeholder: t("form.passwordPlaceholder"),
|
|
413
|
+
autoComplete: "current-password",
|
|
414
|
+
"aria-invalid": fieldState.invalid
|
|
415
|
+
}
|
|
416
|
+
),
|
|
417
|
+
/* @__PURE__ */ jsx3(
|
|
418
|
+
"button",
|
|
419
|
+
{
|
|
420
|
+
type: "button",
|
|
421
|
+
onClick: () => setShowPassword(!showPassword),
|
|
422
|
+
className: "absolute right-3 top-1/2 -translate-y-1/2 text-muted-foreground hover:text-foreground",
|
|
423
|
+
children: showPassword ? /* @__PURE__ */ jsx3(IconEyeOff, { className: "h-4 w-4" }) : /* @__PURE__ */ jsx3(IconEye, { className: "h-4 w-4" })
|
|
424
|
+
}
|
|
425
|
+
)
|
|
426
|
+
] }),
|
|
427
|
+
fieldState.invalid && /* @__PURE__ */ jsx3(FieldError, { errors: [fieldState.error] })
|
|
428
|
+
] })
|
|
429
|
+
}
|
|
430
|
+
)
|
|
431
|
+
] }),
|
|
432
|
+
/* @__PURE__ */ jsx3("div", { className: "mt-4", children: /* @__PURE__ */ jsxs2(Button, { type: "submit", className: "w-full", disabled: isSubmitting, children: [
|
|
433
|
+
isSubmitting && /* @__PURE__ */ jsx3(Spinner, {}),
|
|
434
|
+
isSubmitting ? t("form.submitting") : t("form.submit")
|
|
435
|
+
] }) })
|
|
436
|
+
]
|
|
437
|
+
}
|
|
438
|
+
) : /* @__PURE__ */ jsxs2(
|
|
246
439
|
"form",
|
|
247
440
|
{
|
|
248
|
-
id: "sign-in-
|
|
249
|
-
onSubmit:
|
|
441
|
+
id: "sign-in-form",
|
|
442
|
+
onSubmit: usernameForm.handleSubmit(handleUsernameSubmit),
|
|
250
443
|
children: [
|
|
251
|
-
/* @__PURE__ */
|
|
444
|
+
/* @__PURE__ */ jsx3(FieldGroup, { children: /* @__PURE__ */ jsx3(
|
|
252
445
|
Controller,
|
|
253
446
|
{
|
|
254
|
-
name: "
|
|
255
|
-
control:
|
|
256
|
-
render: ({ field, fieldState }) => /* @__PURE__ */
|
|
257
|
-
/* @__PURE__ */
|
|
258
|
-
/* @__PURE__ */
|
|
447
|
+
name: "username",
|
|
448
|
+
control: usernameForm.control,
|
|
449
|
+
render: ({ field, fieldState }) => /* @__PURE__ */ jsxs2(Field, { "data-invalid": fieldState.invalid, children: [
|
|
450
|
+
/* @__PURE__ */ jsx3(FieldLabel, { htmlFor: "sign-in-username", children: t("form.accountLabel") }),
|
|
451
|
+
/* @__PURE__ */ jsx3(
|
|
259
452
|
Input,
|
|
260
453
|
{
|
|
261
454
|
...field,
|
|
262
|
-
id: "sign-in-
|
|
455
|
+
id: "sign-in-username",
|
|
263
456
|
type: "text",
|
|
264
457
|
placeholder: t("form.accountPlaceholder"),
|
|
265
458
|
autoComplete: "username",
|
|
266
459
|
"aria-invalid": fieldState.invalid
|
|
267
460
|
}
|
|
268
461
|
),
|
|
269
|
-
fieldState.invalid && /* @__PURE__ */
|
|
462
|
+
fieldState.invalid && /* @__PURE__ */ jsx3(FieldError, { errors: [fieldState.error] })
|
|
270
463
|
] })
|
|
271
464
|
}
|
|
272
465
|
) }),
|
|
273
|
-
/* @__PURE__ */
|
|
274
|
-
isSubmitting && /* @__PURE__ */
|
|
466
|
+
/* @__PURE__ */ jsx3("div", { className: "mt-4", children: /* @__PURE__ */ jsxs2(Button, { type: "submit", className: "w-full", disabled: isSubmitting, children: [
|
|
467
|
+
isSubmitting && /* @__PURE__ */ jsx3(Spinner, {}),
|
|
275
468
|
isSubmitting ? t("form.submitting") : t("form.continue")
|
|
276
469
|
] }) })
|
|
277
470
|
]
|
|
278
471
|
}
|
|
279
472
|
);
|
|
473
|
+
return /* @__PURE__ */ jsx3("div", { className: "space-y-4", children: /* @__PURE__ */ jsxs2(
|
|
474
|
+
AuthLayout,
|
|
475
|
+
{
|
|
476
|
+
title: t("title"),
|
|
477
|
+
description: t("description"),
|
|
478
|
+
logoImage,
|
|
479
|
+
footer: showPasswordField ? /* @__PURE__ */ jsx3("div", { className: "flex items-center justify-center w-full", children: Link ? /* @__PURE__ */ jsx3(
|
|
480
|
+
Link,
|
|
481
|
+
{
|
|
482
|
+
href: forgotPasswordLink,
|
|
483
|
+
className: "text-primary inline-block hover:underline",
|
|
484
|
+
children: t("footer.forgotPassword")
|
|
485
|
+
}
|
|
486
|
+
) : /* @__PURE__ */ jsx3(
|
|
487
|
+
"a",
|
|
488
|
+
{
|
|
489
|
+
href: forgotPasswordLink,
|
|
490
|
+
onClick: (e) => {
|
|
491
|
+
e.preventDefault();
|
|
492
|
+
onNavigate(forgotPasswordLink);
|
|
493
|
+
},
|
|
494
|
+
className: "text-primary inline-block hover:underline",
|
|
495
|
+
children: t("footer.forgotPassword")
|
|
496
|
+
}
|
|
497
|
+
) }) : void 0,
|
|
498
|
+
children: [
|
|
499
|
+
formContent,
|
|
500
|
+
errorContent && /* @__PURE__ */ jsxs2(Alert, { variant: "destructive", className: "mt-4", children: [
|
|
501
|
+
/* @__PURE__ */ jsx3(IconAlertCircle, { className: "h-4 w-4" }),
|
|
502
|
+
/* @__PURE__ */ jsx3(AlertTitle, { children: errorContent.title }),
|
|
503
|
+
/* @__PURE__ */ jsx3(AlertDescription, { children: errorContent.description })
|
|
504
|
+
] })
|
|
505
|
+
]
|
|
506
|
+
}
|
|
507
|
+
) });
|
|
280
508
|
};
|
|
281
509
|
export {
|
|
282
510
|
SignIn
|