@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.
Files changed (117) hide show
  1. package/dist/components/auth/auth-card.js +1 -1
  2. package/dist/components/auth/auth-card.js.map +1 -1
  3. package/dist/components/auth/{auth-page-layout.d.ts → auth-layout.d.ts} +3 -3
  4. package/dist/components/auth/{auth-page-layout.js → auth-layout.js} +4 -4
  5. package/dist/components/auth/auth-layout.js.map +1 -0
  6. package/dist/components/auth/countdown.js +15 -6
  7. package/dist/components/auth/countdown.js.map +1 -1
  8. package/dist/components/auth/forgot-password.d.ts +1 -9
  9. package/dist/components/auth/forgot-password.js +267 -43
  10. package/dist/components/auth/forgot-password.js.map +1 -1
  11. package/dist/components/auth/reset-password-form.d.ts +2 -10
  12. package/dist/components/auth/reset-password-form.js +366 -118
  13. package/dist/components/auth/reset-password-form.js.map +1 -1
  14. package/dist/components/auth/sign-in.d.ts +2 -10
  15. package/dist/components/auth/sign-in.js +358 -130
  16. package/dist/components/auth/sign-in.js.map +1 -1
  17. package/dist/components/auth/sign-up.d.ts +2 -10
  18. package/dist/components/auth/sign-up.js +379 -131
  19. package/dist/components/auth/sign-up.js.map +1 -1
  20. package/dist/components/auth/verification-form.d.ts +2 -16
  21. package/dist/components/auth/verification-form.js +15 -6
  22. package/dist/components/auth/verification-form.js.map +1 -1
  23. package/dist/components/auth/verify-email.d.ts +10 -0
  24. package/dist/components/auth/{pages/verify-email-page.js → verify-email.js} +54 -34
  25. package/dist/components/auth/verify-email.js.map +1 -0
  26. package/dist/components/auth/verify-phone.d.ts +11 -0
  27. package/dist/components/auth/{pages/verify-phone-page.js → verify-phone.js} +53 -46
  28. package/dist/components/auth/verify-phone.js.map +1 -0
  29. package/dist/components/iam/permissions.d.ts +5 -0
  30. package/dist/components/iam/{permissions/permissions-page.js → permissions.js} +29 -13
  31. package/dist/components/iam/permissions.js.map +1 -0
  32. package/dist/components/iam/roles.d.ts +5 -0
  33. package/dist/components/iam/{roles/roles-page.js → roles.js} +29 -13
  34. package/dist/components/iam/roles.js.map +1 -0
  35. package/dist/components/iam/sessions.d.ts +5 -0
  36. package/dist/components/iam/{sessions/sessions-page.js → sessions.js} +13 -11
  37. package/dist/components/iam/sessions.js.map +1 -0
  38. package/dist/components/iam/tenants.d.ts +5 -0
  39. package/dist/components/iam/{tenants/tenants-page.js → tenants.js} +13 -11
  40. package/dist/components/iam/tenants.js.map +1 -0
  41. package/dist/components/iam/users.d.ts +5 -0
  42. package/dist/components/iam/{users/users-page.js → users.js} +13 -11
  43. package/dist/components/iam/users.js.map +1 -0
  44. package/dist/components/profile/account.d.ts +5 -0
  45. package/dist/components/profile/account.js +66 -0
  46. package/dist/components/profile/account.js.map +1 -0
  47. package/dist/components/profile/change-email-form.d.ts +5 -0
  48. package/dist/components/profile/change-email-form.js +721 -0
  49. package/dist/components/profile/change-email-form.js.map +1 -0
  50. package/dist/components/profile/change-password-form.d.ts +5 -0
  51. package/dist/components/profile/change-password-form.js +256 -0
  52. package/dist/components/profile/change-password-form.js.map +1 -0
  53. package/dist/components/profile/change-phone-form.d.ts +5 -0
  54. package/dist/components/profile/change-phone-form.js +758 -0
  55. package/dist/components/profile/change-phone-form.js.map +1 -0
  56. package/dist/components/profile/change-profile.d.ts +9 -0
  57. package/dist/components/profile/change-profile.js +37 -0
  58. package/dist/components/profile/change-profile.js.map +1 -0
  59. package/dist/components/profile/otp-verification-modal.d.ts +15 -0
  60. package/dist/components/profile/otp-verification-modal.js +261 -0
  61. package/dist/components/profile/otp-verification-modal.js.map +1 -0
  62. package/dist/components/profile/request-change-email-form.d.ts +10 -0
  63. package/dist/components/profile/request-change-email-form.js +293 -0
  64. package/dist/components/profile/request-change-email-form.js.map +1 -0
  65. package/dist/components/profile/request-change-phone-form.d.ts +10 -0
  66. package/dist/components/profile/request-change-phone-form.js +331 -0
  67. package/dist/components/profile/request-change-phone-form.js.map +1 -0
  68. package/dist/components/profile/security.d.ts +5 -0
  69. package/dist/components/profile/security.js +1439 -0
  70. package/dist/components/profile/security.js.map +1 -0
  71. package/dist/components/profile/verify-change-email-form.d.ts +11 -0
  72. package/dist/components/profile/verify-change-email-form.js +402 -0
  73. package/dist/components/profile/verify-change-email-form.js.map +1 -0
  74. package/dist/components/profile/verify-change-phone-form.d.ts +11 -0
  75. package/dist/components/profile/verify-change-phone-form.js +406 -0
  76. package/dist/components/profile/verify-change-phone-form.js.map +1 -0
  77. package/dist/components/shared/{data-table/data-table.js → data-table.js} +2 -2
  78. package/dist/components/shared/data-table.js.map +1 -0
  79. package/dist/index.d.ts +42 -93
  80. package/dist/index.js +2298 -1300
  81. package/dist/index.js.map +1 -1
  82. package/dist/types-D3s9oE-5.d.ts +75 -0
  83. package/dist/verification-form-ipSRTtQB.d.ts +22 -0
  84. package/package.json +3 -2
  85. package/dist/components/auth/auth-page-layout.js.map +0 -1
  86. package/dist/components/auth/pages/forgot-password-page.d.ts +0 -6
  87. package/dist/components/auth/pages/forgot-password-page.js +0 -362
  88. package/dist/components/auth/pages/forgot-password-page.js.map +0 -1
  89. package/dist/components/auth/pages/reset-password-page.d.ts +0 -9
  90. package/dist/components/auth/pages/reset-password-page.js +0 -514
  91. package/dist/components/auth/pages/reset-password-page.js.map +0 -1
  92. package/dist/components/auth/pages/sign-in-page.d.ts +0 -8
  93. package/dist/components/auth/pages/sign-in-page.js +0 -565
  94. package/dist/components/auth/pages/sign-in-page.js.map +0 -1
  95. package/dist/components/auth/pages/sign-up-page.d.ts +0 -9
  96. package/dist/components/auth/pages/sign-up-page.js +0 -518
  97. package/dist/components/auth/pages/sign-up-page.js.map +0 -1
  98. package/dist/components/auth/pages/verify-email-page.d.ts +0 -10
  99. package/dist/components/auth/pages/verify-email-page.js.map +0 -1
  100. package/dist/components/auth/pages/verify-phone-page.d.ts +0 -11
  101. package/dist/components/auth/pages/verify-phone-page.js.map +0 -1
  102. package/dist/components/iam/permissions/permissions-page.d.ts +0 -5
  103. package/dist/components/iam/permissions/permissions-page.js.map +0 -1
  104. package/dist/components/iam/roles/roles-page.d.ts +0 -5
  105. package/dist/components/iam/roles/roles-page.js.map +0 -1
  106. package/dist/components/iam/sessions/sessions-page.d.ts +0 -5
  107. package/dist/components/iam/sessions/sessions-page.js.map +0 -1
  108. package/dist/components/iam/tenants/tenants-page.d.ts +0 -5
  109. package/dist/components/iam/tenants/tenants-page.js.map +0 -1
  110. package/dist/components/iam/users/users-page.d.ts +0 -5
  111. package/dist/components/iam/users/users-page.js.map +0 -1
  112. package/dist/components/profile/profile-page.d.ts +0 -8
  113. package/dist/components/profile/profile-page.js +0 -163
  114. package/dist/components/profile/profile-page.js.map +0 -1
  115. package/dist/components/shared/data-table/data-table.js.map +0 -1
  116. package/dist/handle-error-BqDMxnQZ.d.ts +0 -8
  117. /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 as useEffect2, useState as useState2 } from "react";
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, useEffect, useState } from "react";
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/sign-in.tsx
227
+ // src/components/auth/auth-layout.tsx
105
228
  import { jsx as jsx2, jsxs } from "react/jsx-runtime";
106
- var identifierSchema = (t) => z.object({
107
- identifier: z.string().trim().min(1, { message: t("errors.requiredField") }).refine(
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 phoneRegex = /^(\+2519|2519|09)\d{8}$/;
111
- const isPhone = phoneRegex.test(val);
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
- onSubmit,
124
- isLoading = false,
125
- identifier: initialIdentifier = "",
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 [currentIdentifier, setCurrentIdentifier] = useState2(initialIdentifier);
133
- const identifierForm = useForm({
134
- resolver: zodResolver(identifierSchema(t)),
135
- defaultValues: {
136
- identifier: initialIdentifier
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
- const signInMutation = hooks.useMutation("post", "/sign-in");
148
- useEffect2(() => {
149
- if (initialIdentifier) {
150
- identifierForm.setValue("identifier", initialIdentifier);
151
- setCurrentIdentifier(initialIdentifier);
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
- }, [initialIdentifier, identifierForm]);
154
- const handleIdentifierSubmit = async (values) => {
155
- const phoneRegex = /^(\+2519|2519|09)\d{8}$/;
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: currentIdentifier || identifierForm.getValues("identifier"),
345
+ identifier: username,
169
346
  password: values.password
170
347
  }
171
348
  });
172
- await onSubmit(
173
- {
174
- identifier: currentIdentifier || identifierForm.getValues("identifier"),
175
- password: values.password
176
- },
177
- "password"
178
- );
179
- } catch {
180
- await onSubmit(
181
- {
182
- identifier: currentIdentifier || identifierForm.getValues("identifier"),
183
- password: values.password
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 isPasswordStep = step === "password";
190
- const isSubmitting = isLoading || signInMutation.isPending;
191
- if (isPasswordStep) {
192
- return /* @__PURE__ */ jsxs(
193
- "form",
194
- {
195
- id: "sign-in-password-form",
196
- onSubmit: passwordForm.handleSubmit(handlePasswordSubmit),
197
- children: [
198
- /* @__PURE__ */ jsxs(FieldGroup, { children: [
199
- /* @__PURE__ */ jsxs("div", { className: "text-center", children: [
200
- /* @__PURE__ */ jsx2("p", { className: "text-sm text-muted-foreground mb-2", children: t("form.enterPasswordFor") }),
201
- /* @__PURE__ */ jsx2("p", { className: "font-bold", children: currentIdentifier || identifierForm.getValues("identifier") })
202
- ] }),
203
- /* @__PURE__ */ jsx2(
204
- Controller,
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
- name: "password",
207
- control: passwordForm.control,
208
- render: ({ field, fieldState }) => /* @__PURE__ */ jsxs(Field, { "data-invalid": fieldState.invalid, children: [
209
- /* @__PURE__ */ jsx2(FieldLabel, { htmlFor: "sign-in-password", children: t("form.passwordLabel") }),
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__ */ jsx2("div", { className: "mt-4", children: /* @__PURE__ */ jsxs(Button, { type: "submit", className: "w-full", disabled: isSubmitting, children: [
238
- isSubmitting && /* @__PURE__ */ jsx2(Spinner, {}),
239
- isSubmitting ? t("form.submitting") : t("form.submit")
240
- ] }) })
241
- ]
242
- }
243
- );
244
- }
245
- return /* @__PURE__ */ jsxs(
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-identifier-form",
249
- onSubmit: identifierForm.handleSubmit(handleIdentifierSubmit),
441
+ id: "sign-in-form",
442
+ onSubmit: usernameForm.handleSubmit(handleUsernameSubmit),
250
443
  children: [
251
- /* @__PURE__ */ jsx2(FieldGroup, { children: /* @__PURE__ */ jsx2(
444
+ /* @__PURE__ */ jsx3(FieldGroup, { children: /* @__PURE__ */ jsx3(
252
445
  Controller,
253
446
  {
254
- name: "identifier",
255
- control: identifierForm.control,
256
- render: ({ field, fieldState }) => /* @__PURE__ */ jsxs(Field, { "data-invalid": fieldState.invalid, children: [
257
- /* @__PURE__ */ jsx2(FieldLabel, { htmlFor: "sign-in-identifier", children: t("form.accountLabel") }),
258
- /* @__PURE__ */ jsx2(
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-identifier",
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__ */ jsx2(FieldError, { errors: [fieldState.error] })
462
+ fieldState.invalid && /* @__PURE__ */ jsx3(FieldError, { errors: [fieldState.error] })
270
463
  ] })
271
464
  }
272
465
  ) }),
273
- /* @__PURE__ */ jsx2("div", { className: "mt-4", children: /* @__PURE__ */ jsxs(Button, { type: "submit", className: "w-full", disabled: isSubmitting, children: [
274
- isSubmitting && /* @__PURE__ */ jsx2(Spinner, {}),
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