@mesob/auth-react 0.0.8 → 0.1.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 (75) hide show
  1. package/dist/components/auth/auth-page-layout.d.ts +1 -3
  2. package/dist/components/auth/auth-page-layout.js +1 -17
  3. package/dist/components/auth/auth-page-layout.js.map +1 -1
  4. package/dist/components/auth/countdown.js +70 -9
  5. package/dist/components/auth/countdown.js.map +1 -1
  6. package/dist/components/auth/forgot-password.js +101 -35
  7. package/dist/components/auth/forgot-password.js.map +1 -1
  8. package/dist/components/auth/pages/forgot-password-page.d.ts +2 -13
  9. package/dist/components/auth/pages/forgot-password-page.js +198 -126
  10. package/dist/components/auth/pages/forgot-password-page.js.map +1 -1
  11. package/dist/components/auth/pages/reset-password-page.d.ts +1 -12
  12. package/dist/components/auth/pages/reset-password-page.js +288 -200
  13. package/dist/components/auth/pages/reset-password-page.js.map +1 -1
  14. package/dist/components/auth/pages/sign-in-page.d.ts +1 -12
  15. package/dist/components/auth/pages/sign-in-page.js +352 -230
  16. package/dist/components/auth/pages/sign-in-page.js.map +1 -1
  17. package/dist/components/auth/pages/sign-up-page.d.ts +1 -11
  18. package/dist/components/auth/pages/sign-up-page.js +310 -216
  19. package/dist/components/auth/pages/sign-up-page.js.map +1 -1
  20. package/dist/components/auth/pages/verify-email-page.d.ts +2 -12
  21. package/dist/components/auth/pages/verify-email-page.js +203 -135
  22. package/dist/components/auth/pages/verify-email-page.js.map +1 -1
  23. package/dist/components/auth/pages/verify-phone-page.d.ts +1 -11
  24. package/dist/components/auth/pages/verify-phone-page.js +206 -137
  25. package/dist/components/auth/pages/verify-phone-page.js.map +1 -1
  26. package/dist/components/auth/reset-password-form.d.ts +1 -1
  27. package/dist/components/auth/reset-password-form.js +188 -106
  28. package/dist/components/auth/reset-password-form.js.map +1 -1
  29. package/dist/components/auth/sign-in.d.ts +3 -3
  30. package/dist/components/auth/sign-in.js +228 -109
  31. package/dist/components/auth/sign-in.js.map +1 -1
  32. package/dist/components/auth/sign-up.js +210 -122
  33. package/dist/components/auth/sign-up.js.map +1 -1
  34. package/dist/components/auth/verification-form.d.ts +1 -1
  35. package/dist/components/auth/verification-form.js +101 -53
  36. package/dist/components/auth/verification-form.js.map +1 -1
  37. package/dist/components/error-boundary.d.ts +27 -0
  38. package/dist/components/error-boundary.js +49 -0
  39. package/dist/components/error-boundary.js.map +1 -0
  40. package/dist/components/iam/permissions/permissions-page.d.ts +5 -0
  41. package/dist/components/iam/permissions/permissions-page.js +201 -0
  42. package/dist/components/iam/permissions/permissions-page.js.map +1 -0
  43. package/dist/components/iam/roles/roles-page.d.ts +5 -0
  44. package/dist/components/iam/roles/roles-page.js +199 -0
  45. package/dist/components/iam/roles/roles-page.js.map +1 -0
  46. package/dist/components/iam/sessions/sessions-page.d.ts +5 -0
  47. package/dist/components/iam/sessions/sessions-page.js +202 -0
  48. package/dist/components/iam/sessions/sessions-page.js.map +1 -0
  49. package/dist/components/iam/tenants/tenants-page.d.ts +5 -0
  50. package/dist/components/iam/tenants/tenants-page.js +202 -0
  51. package/dist/components/iam/tenants/tenants-page.js.map +1 -0
  52. package/dist/components/iam/users/users-page.d.ts +5 -0
  53. package/dist/components/iam/users/users-page.js +211 -0
  54. package/dist/components/iam/users/users-page.js.map +1 -0
  55. package/dist/components/profile/profile-page.d.ts +8 -0
  56. package/dist/components/profile/profile-page.js +163 -0
  57. package/dist/components/profile/profile-page.js.map +1 -0
  58. package/dist/components/shared/data-table/data-table.d.ts +22 -0
  59. package/dist/components/shared/data-table/data-table.js +85 -0
  60. package/dist/components/shared/data-table/data-table.js.map +1 -0
  61. package/dist/components/skeletons/auth-form-skeleton.d.ts +5 -0
  62. package/dist/components/skeletons/auth-form-skeleton.js +32 -0
  63. package/dist/components/skeletons/auth-form-skeleton.js.map +1 -0
  64. package/dist/components/skeletons/profile-skeleton.d.ts +5 -0
  65. package/dist/components/skeletons/profile-skeleton.js +33 -0
  66. package/dist/components/skeletons/profile-skeleton.js.map +1 -0
  67. package/dist/components/skeletons/table-skeleton.d.ts +9 -0
  68. package/dist/components/skeletons/table-skeleton.js +39 -0
  69. package/dist/components/skeletons/table-skeleton.js.map +1 -0
  70. package/dist/handle-error-BqDMxnQZ.d.ts +8 -0
  71. package/dist/index.d.ts +80 -208
  72. package/dist/index.js +2091 -1057
  73. package/dist/index.js.map +1 -1
  74. package/package.json +9 -3
  75. 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
- createContext,
10
- useCallback,
11
- useContext,
12
- useEffect,
13
- useState
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 AuthContext = createContext(null);
17
- var useAuth = () => {
18
- const context = useContext(AuthContext);
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("useAuth must be used within AuthProvider");
62
+ throw new Error("useApi must be used within MesobAuthProvider");
21
63
  }
22
64
  return context;
23
- };
24
-
25
- // src/client.ts
26
- var AuthError = class extends Error {
27
- code;
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
- var handleError = (err, setError, t) => {
90
- if (err instanceof AuthError) {
91
- let errorCode = "";
92
- if (err.code && validCodes.includes(err.code)) {
93
- errorCode = err.code;
94
- } else if (err.message) {
95
- const messageUpper = err.message.toUpperCase().trim();
96
- if (validCodes.includes(messageUpper)) {
97
- errorCode = messageUpper;
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: t("errors.fallback"),
110
- // or 'Error'
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
- const message = err instanceof Error ? err.message : t("errors.fallback");
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: "Jiret", width: 42, height: 42 }) }),
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
- Form,
163
- FormControl,
164
- FormField,
165
- FormItem,
166
- FormLabel,
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 { useTranslations } from "next-intl";
173
- import { useMemo, useState as useState2 } from "react";
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 t = useTranslations("Auth.signIn");
254
+ const { hooks } = useApi();
255
+ const t = useTranslator("Auth.signIn");
205
256
  const [showPassword, setShowPassword] = useState2(false);
206
- const identifierSchema = useMemo(
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
- account: identifier
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 handleIdentifierSubmit = identifierForm.handleSubmit(async (values) => {
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 normalizedAccount = phoneRegex.test(values.account) ? normalizePhone(values.account) : values.account;
243
- await onSubmit({ account: normalizedAccount, password: "" }, "identifier");
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
- { account: identifier, password: values.password },
248
- "password"
285
+ { identifier: normalizedIdentifier, password: "" },
286
+ "identifier"
249
287
  );
250
- });
251
- if (step === "password") {
252
- return /* @__PURE__ */ jsx3(Form, { ...passwordForm, children: /* @__PURE__ */ jsxs2("form", { onSubmit: handlePasswordSubmit, className: "space-y-4", children: [
253
- /* @__PURE__ */ jsxs2("div", { className: "text-center", children: [
254
- /* @__PURE__ */ jsx3("p", { className: "text-sm text-muted-foreground mb-2", children: t("form.enterPasswordFor") }),
255
- /* @__PURE__ */ jsx3("p", { className: "font-bold", children: identifier })
256
- ] }),
257
- /* @__PURE__ */ jsx3(
258
- FormField,
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
- control: passwordForm.control,
261
- name: "password",
262
- render: ({ field }) => /* @__PURE__ */ jsxs2(FormItem, { children: [
263
- /* @__PURE__ */ jsx3(FormLabel, { children: t("form.passwordLabel") }),
264
- /* @__PURE__ */ jsx3(FormControl, { children: /* @__PURE__ */ jsxs2("div", { className: "relative", children: [
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
- type: showPassword ? "text" : "password",
269
- placeholder: t("form.passwordPlaceholder"),
270
- autoComplete: "current-password",
271
- ...field
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
- "button",
276
- {
277
- type: "button",
278
- onClick: () => setShowPassword(!showPassword),
279
- className: "absolute right-3 top-1/2 -translate-y-1/2 text-muted-foreground hover:text-foreground",
280
- children: showPassword ? /* @__PURE__ */ jsx3(IconEyeOff, { className: "h-4 w-4" }) : /* @__PURE__ */ jsx3(IconEye, { className: "h-4 w-4" })
281
- }
282
- )
283
- ] }) }),
284
- /* @__PURE__ */ jsx3(FormMessage, {})
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
- redirectUrl,
323
- onNavigate,
324
- linkComponent: Link,
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 defaultRedirect = redirectUrl || "/dashboard";
335
- const forgotPasswordLink = links?.forgotPassword || "/auth/forgot-password";
336
- const signUpLink = links?.signUp || "/auth/sign-up";
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
- const checkResult = await client.checkUser({
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
- const res = await client.signInWithPassword({
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
- return /* @__PURE__ */ jsx4("div", { className: "space-y-4", children: /* @__PURE__ */ jsx4(
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: /* @__PURE__ */ jsx4(
429
- SignIn,
430
- {
431
- onSubmit: handleSubmit,
432
- isLoading,
433
- identifier,
434
- step
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
  };