@nocios/crudify-ui 1.0.38 → 1.0.39

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.
@@ -0,0 +1,47 @@
1
+ export * from '@nocios/crudify-browser';
2
+ export { default as crudify } from '@nocios/crudify-browser';
3
+ import React from 'react';
4
+
5
+ type BoxScreenType = "login" | "signUp" | "forgotPassword" | "resetPassword" | "checkCode";
6
+ interface CrudifyLoginConfig {
7
+ publicApiKey?: string;
8
+ env?: "dev" | "stg" | "prod";
9
+ appName?: string;
10
+ logo?: string;
11
+ colors?: {
12
+ primaryColor?: string;
13
+ bgColor?: string;
14
+ [key: string]: string | undefined;
15
+ };
16
+ loginActions?: string[];
17
+ }
18
+ interface CrudifyLoginProps {
19
+ config?: CrudifyLoginConfig;
20
+ onNavigate?: (path: string) => void;
21
+ onLoginSuccess?: (token: string, redirectUrl?: string) => void;
22
+ onError?: (error: string) => void;
23
+ initialScreen?: BoxScreenType;
24
+ redirectUrl?: string;
25
+ autoReadFromCookies?: boolean;
26
+ onSubdomainLoginSuccess?: (token: string, config: CrudifyLoginConfig) => void;
27
+ }
28
+
29
+ declare const CrudifyLogin: React.FC<CrudifyLoginProps>;
30
+
31
+ declare const getCookie: (name: string) => string | null;
32
+
33
+ declare class SecureStorage {
34
+ private readonly encryptionKey;
35
+ private readonly storage;
36
+ constructor(storageType?: "localStorage" | "sessionStorage");
37
+ private generateEncryptionKey;
38
+ setItem(key: string, value: string, expiryMinutes?: number): void;
39
+ getItem(key: string): string | null;
40
+ removeItem(key: string): void;
41
+ setToken(token: string): void;
42
+ getToken(): string | null;
43
+ }
44
+ declare const secureSessionStorage: SecureStorage;
45
+ declare const secureLocalStorage: SecureStorage;
46
+
47
+ export { type BoxScreenType, CrudifyLogin, type CrudifyLoginConfig, type CrudifyLoginProps, getCookie, secureLocalStorage, secureSessionStorage };
@@ -0,0 +1,47 @@
1
+ export * from '@nocios/crudify-browser';
2
+ export { default as crudify } from '@nocios/crudify-browser';
3
+ import React from 'react';
4
+
5
+ type BoxScreenType = "login" | "signUp" | "forgotPassword" | "resetPassword" | "checkCode";
6
+ interface CrudifyLoginConfig {
7
+ publicApiKey?: string;
8
+ env?: "dev" | "stg" | "prod";
9
+ appName?: string;
10
+ logo?: string;
11
+ colors?: {
12
+ primaryColor?: string;
13
+ bgColor?: string;
14
+ [key: string]: string | undefined;
15
+ };
16
+ loginActions?: string[];
17
+ }
18
+ interface CrudifyLoginProps {
19
+ config?: CrudifyLoginConfig;
20
+ onNavigate?: (path: string) => void;
21
+ onLoginSuccess?: (token: string, redirectUrl?: string) => void;
22
+ onError?: (error: string) => void;
23
+ initialScreen?: BoxScreenType;
24
+ redirectUrl?: string;
25
+ autoReadFromCookies?: boolean;
26
+ onSubdomainLoginSuccess?: (token: string, config: CrudifyLoginConfig) => void;
27
+ }
28
+
29
+ declare const CrudifyLogin: React.FC<CrudifyLoginProps>;
30
+
31
+ declare const getCookie: (name: string) => string | null;
32
+
33
+ declare class SecureStorage {
34
+ private readonly encryptionKey;
35
+ private readonly storage;
36
+ constructor(storageType?: "localStorage" | "sessionStorage");
37
+ private generateEncryptionKey;
38
+ setItem(key: string, value: string, expiryMinutes?: number): void;
39
+ getItem(key: string): string | null;
40
+ removeItem(key: string): void;
41
+ setToken(token: string): void;
42
+ getToken(): string | null;
43
+ }
44
+ declare const secureSessionStorage: SecureStorage;
45
+ declare const secureLocalStorage: SecureStorage;
46
+
47
+ export { type BoxScreenType, CrudifyLogin, type CrudifyLoginConfig, type CrudifyLoginProps, getCookie, secureLocalStorage, secureSessionStorage };
package/dist/index.mjs ADDED
@@ -0,0 +1,1095 @@
1
+ // src/index.ts
2
+ import { default as default2 } from "@nocios/crudify-browser";
3
+ export * from "@nocios/crudify-browser";
4
+
5
+ // src/components/CrudifyLogin/index.tsx
6
+ import { useState as useState5, useMemo as useMemo2 } from "react";
7
+ import { useTranslation as useTranslation5 } from "react-i18next";
8
+ import { Paper, Box as Box5, Typography as Typography5 } from "@mui/material";
9
+
10
+ // src/components/CrudifyLogin/Forms/LoginForm.tsx
11
+ import { useTranslation } from "react-i18next";
12
+ import { useState, useEffect, useRef } from "react";
13
+ import { Typography, TextField, Button, Box, CircularProgress, Alert, Link } from "@mui/material";
14
+
15
+ // src/components/CrudifyLogin/hooks/useCrudifyLogin.ts
16
+ import { useMemo } from "react";
17
+ import crudify from "@nocios/crudify-browser";
18
+ var useCrudifyLogin = (config, _options = {}) => {
19
+ const crudifyMethods = useMemo(() => {
20
+ if (!config.publicApiKey) {
21
+ return null;
22
+ }
23
+ return {
24
+ login: crudify.login,
25
+ transaction: crudify.transaction
26
+ };
27
+ }, [config.publicApiKey, config.env]);
28
+ return { crudify: crudifyMethods };
29
+ };
30
+
31
+ // src/components/CrudifyLogin/utils/secureStorage.ts
32
+ import CryptoJS from "crypto-js";
33
+ var SecureStorage = class {
34
+ constructor(storageType = "sessionStorage") {
35
+ this.encryptionKey = this.generateEncryptionKey();
36
+ this.storage = storageType === "localStorage" ? window.localStorage : window.sessionStorage;
37
+ }
38
+ generateEncryptionKey() {
39
+ const browserFingerprint = [
40
+ navigator.userAgent,
41
+ navigator.language,
42
+ (/* @__PURE__ */ new Date()).getTimezoneOffset(),
43
+ screen.colorDepth,
44
+ screen.width,
45
+ screen.height,
46
+ "crudify-login"
47
+ ].join("|");
48
+ return CryptoJS.SHA256(browserFingerprint).toString();
49
+ }
50
+ setItem(key, value, expiryMinutes) {
51
+ try {
52
+ const encrypted = CryptoJS.AES.encrypt(value, this.encryptionKey).toString();
53
+ this.storage.setItem(key, encrypted);
54
+ if (expiryMinutes) {
55
+ const expiryTime = (/* @__PURE__ */ new Date()).getTime() + expiryMinutes * 60 * 1e3;
56
+ this.storage.setItem(`${key}_expiry`, expiryTime.toString());
57
+ }
58
+ } catch (error) {
59
+ console.error("Failed to encrypt and store data:", error);
60
+ }
61
+ }
62
+ getItem(key) {
63
+ try {
64
+ const expiryKey = `${key}_expiry`;
65
+ const expiry = this.storage.getItem(expiryKey);
66
+ if (expiry) {
67
+ const expiryTime = parseInt(expiry, 10);
68
+ if ((/* @__PURE__ */ new Date()).getTime() > expiryTime) {
69
+ this.removeItem(key);
70
+ return null;
71
+ }
72
+ }
73
+ const encrypted = this.storage.getItem(key);
74
+ if (!encrypted) return null;
75
+ const decrypted = CryptoJS.AES.decrypt(encrypted, this.encryptionKey);
76
+ const result = decrypted.toString(CryptoJS.enc.Utf8);
77
+ if (!result) {
78
+ console.warn("Failed to decrypt stored data - may be corrupted");
79
+ this.removeItem(key);
80
+ return null;
81
+ }
82
+ return result;
83
+ } catch (error) {
84
+ console.error("Failed to decrypt data:", error);
85
+ this.removeItem(key);
86
+ return null;
87
+ }
88
+ }
89
+ removeItem(key) {
90
+ this.storage.removeItem(key);
91
+ this.storage.removeItem(`${key}_expiry`);
92
+ }
93
+ setToken(token) {
94
+ try {
95
+ const parts = token.split(".");
96
+ if (parts.length === 3) {
97
+ const payload = JSON.parse(atob(parts[1]));
98
+ if (payload.exp) {
99
+ const expiryTime = payload.exp * 1e3;
100
+ const now = (/* @__PURE__ */ new Date()).getTime();
101
+ const minutesUntilExpiry = Math.floor((expiryTime - now) / (60 * 1e3));
102
+ if (minutesUntilExpiry > 0) {
103
+ this.setItem("authToken", token, minutesUntilExpiry);
104
+ return;
105
+ }
106
+ }
107
+ }
108
+ } catch (error) {
109
+ console.warn("Failed to parse token expiry, using default expiry");
110
+ }
111
+ this.setItem("authToken", token, 24 * 60);
112
+ }
113
+ getToken() {
114
+ const token = this.getItem("authToken");
115
+ if (token) {
116
+ try {
117
+ const parts = token.split(".");
118
+ if (parts.length === 3) {
119
+ const payload = JSON.parse(atob(parts[1]));
120
+ if (payload.exp) {
121
+ const now = Math.floor(Date.now() / 1e3);
122
+ if (payload.exp < now) {
123
+ this.removeItem("authToken");
124
+ return null;
125
+ }
126
+ }
127
+ }
128
+ } catch (error) {
129
+ console.warn("Failed to validate token expiry");
130
+ this.removeItem("authToken");
131
+ return null;
132
+ }
133
+ }
134
+ return token;
135
+ }
136
+ };
137
+ var secureSessionStorage = new SecureStorage("sessionStorage");
138
+ var secureLocalStorage = new SecureStorage("localStorage");
139
+
140
+ // src/components/CrudifyLogin/Forms/LoginForm.tsx
141
+ import crudify2 from "@nocios/crudify-browser";
142
+ import { Fragment, jsx, jsxs } from "react/jsx-runtime";
143
+ var LoginForm = ({
144
+ config,
145
+ onNavigate,
146
+ onLoginSuccess,
147
+ onError,
148
+ redirectUrl = "/",
149
+ onSubdomainLoginSuccess
150
+ }) => {
151
+ const [username, setUsername] = useState("");
152
+ const [password, setPassword] = useState("");
153
+ const [subdomain, setSubdomain] = useState("");
154
+ const [loading, setLoading] = useState(false);
155
+ const [errors, setErrors] = useState([]);
156
+ const [helperTextEmail, setHelperTextEmail] = useState(null);
157
+ const [helperTextPassword, setHelperTextPassword] = useState(null);
158
+ const [helperTextSubdomain, setHelperTextSubdomain] = useState(null);
159
+ const { t } = useTranslation();
160
+ const usernameInputRef = useRef(null);
161
+ const hasPublicApiKey = Boolean(config.publicApiKey);
162
+ const { crudify: crudifyFromHook } = useCrudifyLogin(config, {
163
+ showErrorNotifications: false,
164
+ showSuccessNotifications: false
165
+ });
166
+ useEffect(() => {
167
+ const timer = setTimeout(() => {
168
+ if (usernameInputRef.current) usernameInputRef.current.focus();
169
+ }, 100);
170
+ return () => clearTimeout(timer);
171
+ }, []);
172
+ const getSafeErrorTranslation = (errorCode) => {
173
+ const possibleKeys = [
174
+ `errors.auth.${errorCode}`,
175
+ `errors.data.${errorCode}`,
176
+ `errors.system.${errorCode}`,
177
+ `errors.${errorCode}`,
178
+ `login.${errorCode.toLowerCase()}`
179
+ ];
180
+ for (const key of possibleKeys) {
181
+ const translated = t(key);
182
+ if (translated !== key) {
183
+ return translated;
184
+ }
185
+ }
186
+ return t("error.unknown");
187
+ };
188
+ const handleLogin = async () => {
189
+ if (loading) return;
190
+ if (!username.trim()) {
191
+ setHelperTextEmail(t("login.usernameRequired"));
192
+ return;
193
+ }
194
+ if (!password.trim()) {
195
+ setHelperTextPassword(t("login.passwordRequired"));
196
+ return;
197
+ }
198
+ if (!hasPublicApiKey && !subdomain.trim()) {
199
+ setHelperTextSubdomain(t("login.subdomainRequired", "Subdomain is required"));
200
+ return;
201
+ }
202
+ setErrors([]);
203
+ setHelperTextEmail(null);
204
+ setHelperTextPassword(null);
205
+ setHelperTextSubdomain(null);
206
+ setLoading(true);
207
+ try {
208
+ let response;
209
+ if (hasPublicApiKey && crudifyFromHook) {
210
+ response = await crudifyFromHook.login(username, password);
211
+ } else {
212
+ const data = [
213
+ {
214
+ operation: "loginWithSubdomain",
215
+ data: { username, password, subdomain }
216
+ }
217
+ ];
218
+ response = await crudify2.transaction(data);
219
+ }
220
+ setLoading(false);
221
+ if (response.success) {
222
+ secureSessionStorage.setToken(response.data.token);
223
+ if (!hasPublicApiKey && onSubdomainLoginSuccess) {
224
+ const newConfig = {
225
+ ...config,
226
+ publicApiKey: response.data.publicApiKey || config.publicApiKey,
227
+ appName: response.data.appName || config.appName,
228
+ logo: response.data.logo || config.logo,
229
+ colors: response.data.colors || config.colors
230
+ };
231
+ onSubdomainLoginSuccess(response.data.token, newConfig);
232
+ } else if (onLoginSuccess) {
233
+ onLoginSuccess(response.data.token, redirectUrl);
234
+ }
235
+ } else {
236
+ handleLoginError(response);
237
+ }
238
+ } catch (error) {
239
+ setLoading(false);
240
+ console.error("Login error:", error);
241
+ setErrors([t("error.unknown")]);
242
+ if (onError) {
243
+ onError(typeof error === "string" ? error : t("error.unknown"));
244
+ }
245
+ }
246
+ };
247
+ const handleLoginError = (response) => {
248
+ if (response.data?.response) {
249
+ const { data: errorMessage, status, fieldsWarning } = response.data.response;
250
+ if (fieldsWarning) {
251
+ if (fieldsWarning.username) setHelperTextEmail(fieldsWarning.username[0]);
252
+ if (fieldsWarning.password) setHelperTextPassword(fieldsWarning.password[0]);
253
+ if (fieldsWarning.subdomain) setHelperTextSubdomain(fieldsWarning.subdomain[0]);
254
+ } else if (status === "INVALID_CREDENTIALS") {
255
+ const translatedError = getSafeErrorTranslation(status);
256
+ setErrors([translatedError]);
257
+ } else if (status === "TOO_MANY_REQUESTS") {
258
+ const translatedError = getSafeErrorTranslation(status);
259
+ setErrors([translatedError]);
260
+ } else if (status === "SUBDOMAIN_NOT_FOUND") {
261
+ setHelperTextSubdomain(t("login.subdomainNotFound", "Subdomain not found"));
262
+ } else if (status) {
263
+ setErrors([getSafeErrorTranslation(status)]);
264
+ } else {
265
+ setErrors([typeof errorMessage === "string" ? errorMessage : t("error.unknown")]);
266
+ }
267
+ } else if (response.errors) {
268
+ const errors2 = response.errors;
269
+ if (typeof errors2 === "string") {
270
+ setErrors([errors2]);
271
+ } else if (typeof errors2 === "object") {
272
+ const hasUsernameError = errors2.username && errors2.username.length > 0;
273
+ const hasPasswordError = errors2.password && errors2.password.length > 0;
274
+ const hasSubdomainError = errors2.subdomain && errors2.subdomain.length > 0;
275
+ if (hasUsernameError || hasPasswordError || hasSubdomainError) {
276
+ if (hasUsernameError) setHelperTextEmail(errors2.username[0]);
277
+ if (hasPasswordError) setHelperTextPassword(errors2.password[0]);
278
+ if (hasSubdomainError) setHelperTextSubdomain(errors2.subdomain[0]);
279
+ } else if (errors2._error && errors2._error.length > 0) {
280
+ const errorMessage = errors2._error[0];
281
+ if (typeof errorMessage === "string" && errorMessage.match(/^[A-Z_]+$/)) {
282
+ const translatedError = getSafeErrorTranslation(errorMessage);
283
+ setErrors([translatedError]);
284
+ } else setErrors([errorMessage]);
285
+ } else if (errors2._graphql && errors2._graphql[0] === "INVALID_CREDENTIALS") {
286
+ setErrors([getSafeErrorTranslation("INVALID_CREDENTIALS")]);
287
+ } else {
288
+ setErrors([t("error.unknown")]);
289
+ }
290
+ }
291
+ } else {
292
+ setErrors([t("error.unknown")]);
293
+ }
294
+ };
295
+ const handleSubmit = (e) => {
296
+ e.preventDefault();
297
+ handleLogin();
298
+ };
299
+ const handleKeyDown = (e) => {
300
+ if (e.key === "Enter" && !loading) {
301
+ e.preventDefault();
302
+ handleLogin();
303
+ }
304
+ };
305
+ return /* @__PURE__ */ jsxs(Fragment, { children: [
306
+ /* @__PURE__ */ jsxs(
307
+ Box,
308
+ {
309
+ component: "form",
310
+ noValidate: true,
311
+ onSubmit: handleSubmit,
312
+ onKeyDown: handleKeyDown,
313
+ sx: { width: "100%", display: "flex", flexDirection: "column", gap: 2 },
314
+ children: [
315
+ !hasPublicApiKey && /* @__PURE__ */ jsxs(Box, { sx: { mb: 1 }, children: [
316
+ /* @__PURE__ */ jsx(
317
+ Typography,
318
+ {
319
+ variant: "body2",
320
+ component: "label",
321
+ htmlFor: "subdomain",
322
+ sx: { display: "block", fontWeight: 500, color: "grey.700", mb: 0.5 },
323
+ children: t("login.subdomainLabel", "Subdomain")
324
+ }
325
+ ),
326
+ /* @__PURE__ */ jsx(
327
+ TextField,
328
+ {
329
+ fullWidth: true,
330
+ id: "subdomain",
331
+ name: "subdomain",
332
+ type: "text",
333
+ value: subdomain,
334
+ disabled: loading,
335
+ onChange: (e) => setSubdomain(e.target.value),
336
+ error: !!helperTextSubdomain,
337
+ helperText: helperTextSubdomain,
338
+ placeholder: t("login.subdomainPlaceholder", "Enter your subdomain"),
339
+ required: true
340
+ }
341
+ )
342
+ ] }),
343
+ /* @__PURE__ */ jsxs(Box, { sx: { mb: 1 }, children: [
344
+ /* @__PURE__ */ jsx(
345
+ Typography,
346
+ {
347
+ variant: "body2",
348
+ component: "label",
349
+ htmlFor: "email",
350
+ sx: { display: "block", fontWeight: 500, color: "grey.700", mb: 0.5 },
351
+ children: t("login.usernameOrEmailLabel")
352
+ }
353
+ ),
354
+ /* @__PURE__ */ jsx(
355
+ TextField,
356
+ {
357
+ fullWidth: true,
358
+ id: "email",
359
+ name: "email",
360
+ type: "email",
361
+ value: username,
362
+ disabled: loading,
363
+ onChange: (e) => setUsername(e.target.value),
364
+ error: !!helperTextEmail,
365
+ helperText: helperTextEmail,
366
+ autoComplete: "email",
367
+ placeholder: t("login.usernameOrEmailPlaceholder"),
368
+ inputRef: usernameInputRef,
369
+ required: true
370
+ }
371
+ )
372
+ ] }),
373
+ /* @__PURE__ */ jsxs(Box, { sx: { mb: 1 }, children: [
374
+ /* @__PURE__ */ jsx(
375
+ Typography,
376
+ {
377
+ variant: "body2",
378
+ component: "label",
379
+ htmlFor: "password",
380
+ sx: { display: "block", fontWeight: 500, color: "grey.700", mb: 0.5 },
381
+ children: t("login.passwordLabel")
382
+ }
383
+ ),
384
+ /* @__PURE__ */ jsx(
385
+ TextField,
386
+ {
387
+ fullWidth: true,
388
+ id: "password",
389
+ name: "password",
390
+ type: "password",
391
+ value: password,
392
+ disabled: loading,
393
+ onChange: (e) => setPassword(e.target.value),
394
+ error: !!helperTextPassword,
395
+ helperText: helperTextPassword,
396
+ autoComplete: "current-password",
397
+ placeholder: t("login.passwordPlaceholder"),
398
+ required: true
399
+ }
400
+ )
401
+ ] }),
402
+ config.loginActions?.includes("forgotPassword") && /* @__PURE__ */ jsx(Box, { sx: { display: "flex", justifyContent: "flex-end", alignItems: "center" }, children: /* @__PURE__ */ jsx(Link, { sx: { cursor: "pointer" }, onClick: () => onNavigate?.("/login/forgotPassword"), variant: "body2", color: "secondary", children: t("login.forgotPasswordLink") }) }),
403
+ /* @__PURE__ */ jsx(Button, { disabled: loading, type: "submit", fullWidth: true, variant: "contained", color: "primary", sx: { mt: 1, mb: 2 }, children: loading ? /* @__PURE__ */ jsx(CircularProgress, { size: 20 }) : t("login.loginButton") })
404
+ ]
405
+ }
406
+ ),
407
+ /* @__PURE__ */ jsx(Box, { children: errors.length > 0 && errors.map((error, index) => /* @__PURE__ */ jsx(Alert, { variant: "filled", sx: { mt: 2 }, severity: "error", children: /* @__PURE__ */ jsx("div", { children: error }) }, index)) }),
408
+ config.loginActions?.includes("createUser") && /* @__PURE__ */ jsxs(Typography, { variant: "body2", align: "center", sx: { color: "text.secondary", mt: 3 }, children: [
409
+ t("login.noAccountPrompt"),
410
+ " ",
411
+ /* @__PURE__ */ jsx(Link, { sx: { cursor: "pointer" }, onClick: () => onNavigate?.("/public/users/create"), fontWeight: "medium", color: "secondary", children: t("login.signUpLink") })
412
+ ] })
413
+ ] });
414
+ };
415
+ var LoginForm_default = LoginForm;
416
+
417
+ // src/components/CrudifyLogin/Forms/ForgotPasswordForm.tsx
418
+ import { useTranslation as useTranslation2 } from "react-i18next";
419
+ import { useState as useState2 } from "react";
420
+ import { Typography as Typography2, TextField as TextField2, Button as Button2, Box as Box2, CircularProgress as CircularProgress2, Alert as Alert2, Link as Link2 } from "@mui/material";
421
+ import { Fragment as Fragment2, jsx as jsx2, jsxs as jsxs2 } from "react/jsx-runtime";
422
+ var ForgotPasswordForm = ({ config, onNavigate, onError }) => {
423
+ const [email, setEmail] = useState2("");
424
+ const [loading, setLoading] = useState2(false);
425
+ const [errors, setErrors] = useState2([]);
426
+ const [helperTextEmail, setHelperTextEmail] = useState2(null);
427
+ const [emailSent, setEmailSent] = useState2(false);
428
+ const [codeAlreadyExists, setCodeAlreadyExists] = useState2(false);
429
+ const { t } = useTranslation2();
430
+ const { crudify: crudify3 } = useCrudifyLogin(config);
431
+ const validateEmail = (email2) => {
432
+ const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
433
+ return emailRegex.test(email2);
434
+ };
435
+ const handleSubmit = async () => {
436
+ if (loading || !crudify3) return;
437
+ setErrors([]);
438
+ setHelperTextEmail(null);
439
+ if (!email) {
440
+ setHelperTextEmail(t("forgotPassword.emailRequired"));
441
+ return;
442
+ }
443
+ if (!validateEmail(email)) {
444
+ setHelperTextEmail(t("forgotPassword.invalidEmail"));
445
+ return;
446
+ }
447
+ setLoading(true);
448
+ try {
449
+ const data = [{ operation: "requestPasswordReset", data: { email } }];
450
+ const response = await crudify3.transaction(data);
451
+ if (response.success) {
452
+ if (response.data && response.data.existingCodeValid) {
453
+ setCodeAlreadyExists(true);
454
+ } else {
455
+ setEmailSent(true);
456
+ }
457
+ } else {
458
+ if (response.errors) {
459
+ if (typeof response.errors === "string") {
460
+ setErrors([response.errors]);
461
+ } else if (response.errors.email) {
462
+ setHelperTextEmail(response.errors.email[0]);
463
+ } else {
464
+ setErrors([t("error.unknown")]);
465
+ }
466
+ } else {
467
+ setErrors([t("error.unknown")]);
468
+ }
469
+ }
470
+ } catch (error) {
471
+ console.error("Forgot password error:", error);
472
+ setErrors([t("error.unknown")]);
473
+ if (onError) {
474
+ onError(typeof error === "string" ? error : t("error.unknown"));
475
+ }
476
+ } finally {
477
+ setLoading(false);
478
+ }
479
+ };
480
+ const handleFormSubmit = (e) => {
481
+ e.preventDefault();
482
+ handleSubmit();
483
+ };
484
+ if (emailSent) {
485
+ return /* @__PURE__ */ jsxs2(Box2, { sx: { textAlign: "center" }, children: [
486
+ /* @__PURE__ */ jsx2(Alert2, { severity: "success", sx: { mb: 3 }, children: t("forgotPassword.emailSentMessage") }),
487
+ /* @__PURE__ */ jsx2(Typography2, { variant: "body2", sx: { mb: 2 }, children: t("forgotPassword.checkEmailInstructions") }),
488
+ /* @__PURE__ */ jsx2(Link2, { sx: { cursor: "pointer" }, onClick: () => onNavigate?.("/login/checkCode"), variant: "body2", color: "primary", children: t("forgotPassword.enterCodeLink") }),
489
+ /* @__PURE__ */ jsx2(Box2, { sx: { mt: 2 }, children: /* @__PURE__ */ jsx2(Link2, { sx: { cursor: "pointer" }, onClick: () => onNavigate?.("/login"), variant: "body2", color: "secondary", children: t("common.backToLogin") }) })
490
+ ] });
491
+ }
492
+ if (codeAlreadyExists) {
493
+ return /* @__PURE__ */ jsxs2(Box2, { sx: { textAlign: "center" }, children: [
494
+ /* @__PURE__ */ jsx2(Alert2, { severity: "info", sx: { mb: 3 }, children: t("forgotPassword.codeAlreadyExistsMessage") }),
495
+ /* @__PURE__ */ jsx2(Link2, { sx: { cursor: "pointer" }, onClick: () => onNavigate?.("/login/checkCode"), variant: "body2", color: "primary", children: t("forgotPassword.enterCodeLink") }),
496
+ /* @__PURE__ */ jsx2(Box2, { sx: { mt: 2 }, children: /* @__PURE__ */ jsx2(Link2, { sx: { cursor: "pointer" }, onClick: () => onNavigate?.("/login"), variant: "body2", color: "secondary", children: t("common.backToLogin") }) })
497
+ ] });
498
+ }
499
+ return /* @__PURE__ */ jsxs2(Fragment2, { children: [
500
+ /* @__PURE__ */ jsx2(Typography2, { variant: "h5", component: "h1", sx: { mb: 3, textAlign: "center" }, children: t("forgotPassword.title") }),
501
+ /* @__PURE__ */ jsx2(Typography2, { variant: "body2", sx: { mb: 3, textAlign: "center", color: "text.secondary" }, children: t("forgotPassword.instructions") }),
502
+ /* @__PURE__ */ jsxs2(Box2, { component: "form", noValidate: true, onSubmit: handleFormSubmit, children: [
503
+ /* @__PURE__ */ jsxs2(Box2, { sx: { mb: 2 }, children: [
504
+ /* @__PURE__ */ jsx2(
505
+ Typography2,
506
+ {
507
+ variant: "body2",
508
+ component: "label",
509
+ htmlFor: "email",
510
+ sx: { display: "block", fontWeight: 500, color: "grey.700", mb: 0.5 },
511
+ children: t("forgotPassword.emailLabel")
512
+ }
513
+ ),
514
+ /* @__PURE__ */ jsx2(
515
+ TextField2,
516
+ {
517
+ fullWidth: true,
518
+ id: "email",
519
+ name: "email",
520
+ type: "email",
521
+ value: email,
522
+ disabled: loading,
523
+ onChange: (e) => setEmail(e.target.value),
524
+ error: !!helperTextEmail,
525
+ helperText: helperTextEmail,
526
+ autoComplete: "email",
527
+ placeholder: t("forgotPassword.emailPlaceholder"),
528
+ required: true
529
+ }
530
+ )
531
+ ] }),
532
+ /* @__PURE__ */ jsx2(Button2, { disabled: loading, type: "submit", fullWidth: true, variant: "contained", color: "primary", sx: { mt: 2, mb: 2 }, children: loading ? /* @__PURE__ */ jsx2(CircularProgress2, { size: 20 }) : t("forgotPassword.sendCodeButton") }),
533
+ /* @__PURE__ */ jsx2(Box2, { sx: { textAlign: "center" }, children: /* @__PURE__ */ jsx2(Link2, { sx: { cursor: "pointer" }, onClick: () => onNavigate?.("/login"), variant: "body2", color: "secondary", children: t("common.backToLogin") }) })
534
+ ] }),
535
+ errors.length > 0 && /* @__PURE__ */ jsx2(Box2, { sx: { mt: 2 }, children: errors.map((error, index) => /* @__PURE__ */ jsx2(Alert2, { variant: "filled", severity: "error", children: error }, index)) })
536
+ ] });
537
+ };
538
+ var ForgotPasswordForm_default = ForgotPasswordForm;
539
+
540
+ // src/components/CrudifyLogin/Forms/ResetPasswordForm.tsx
541
+ import { useTranslation as useTranslation3 } from "react-i18next";
542
+ import { useState as useState3, useEffect as useEffect2 } from "react";
543
+ import { Typography as Typography3, TextField as TextField3, Button as Button3, Box as Box3, CircularProgress as CircularProgress3, Alert as Alert3, Link as Link3 } from "@mui/material";
544
+ import { Fragment as Fragment3, jsx as jsx3, jsxs as jsxs3 } from "react/jsx-runtime";
545
+ var ResetPasswordForm = ({ config, onNavigate, onError, searchParams }) => {
546
+ const [newPassword, setNewPassword] = useState3("");
547
+ const [confirmPassword, setConfirmPassword] = useState3("");
548
+ const [loading, setLoading] = useState3(false);
549
+ const [errors, setErrors] = useState3([]);
550
+ const [helperTextNewPassword, setHelperTextNewPassword] = useState3(null);
551
+ const [helperTextConfirmPassword, setHelperTextConfirmPassword] = useState3(null);
552
+ const [email, setEmail] = useState3("");
553
+ const [code, setCode] = useState3("");
554
+ const [validatingCode, setValidatingCode] = useState3(true);
555
+ const [codeValidated, setCodeValidated] = useState3(false);
556
+ const [resetSuccess, setResetSuccess] = useState3(false);
557
+ const { t } = useTranslation3();
558
+ const { crudify: crudify3 } = useCrudifyLogin(config);
559
+ useEffect2(() => {
560
+ const validateCode = async (emailToValidate, codeToValidate) => {
561
+ if (!crudify3) return;
562
+ try {
563
+ const data = [
564
+ {
565
+ operation: "validatePasswordResetCode",
566
+ data: { email: emailToValidate, codePassword: codeToValidate }
567
+ }
568
+ ];
569
+ const response = await crudify3.transaction(data);
570
+ if (response.success) {
571
+ setCodeValidated(true);
572
+ } else {
573
+ setErrors([t("resetPassword.invalidCode")]);
574
+ }
575
+ } catch (error) {
576
+ console.error("Code validation error:", error);
577
+ setErrors([t("error.unknown")]);
578
+ } finally {
579
+ setValidatingCode(false);
580
+ }
581
+ };
582
+ if (searchParams) {
583
+ const emailParam = searchParams.get("email");
584
+ const codeParam = searchParams.get("code");
585
+ if (emailParam && codeParam) {
586
+ setEmail(emailParam);
587
+ setCode(codeParam);
588
+ validateCode(emailParam, codeParam);
589
+ } else {
590
+ setValidatingCode(false);
591
+ setErrors([t("resetPassword.missingParameters")]);
592
+ }
593
+ } else {
594
+ setValidatingCode(false);
595
+ setErrors([t("resetPassword.missingParameters")]);
596
+ }
597
+ }, [searchParams, crudify3, t]);
598
+ const validatePasswords = () => {
599
+ setHelperTextNewPassword(null);
600
+ setHelperTextConfirmPassword(null);
601
+ if (!newPassword) {
602
+ setHelperTextNewPassword(t("resetPassword.newPasswordRequired"));
603
+ return false;
604
+ }
605
+ if (newPassword.length < 8) {
606
+ setHelperTextNewPassword(t("resetPassword.passwordTooShort"));
607
+ return false;
608
+ }
609
+ if (!confirmPassword) {
610
+ setHelperTextConfirmPassword(t("resetPassword.confirmPasswordRequired"));
611
+ return false;
612
+ }
613
+ if (newPassword !== confirmPassword) {
614
+ setHelperTextConfirmPassword(t("resetPassword.passwordsDoNotMatch"));
615
+ return false;
616
+ }
617
+ return true;
618
+ };
619
+ const handleSubmit = async () => {
620
+ if (loading || !crudify3) return;
621
+ setErrors([]);
622
+ if (!validatePasswords()) return;
623
+ setLoading(true);
624
+ try {
625
+ const data = [
626
+ {
627
+ operation: "validateAndResetPassword",
628
+ data: { email, codePassword: code, newPassword }
629
+ }
630
+ ];
631
+ const response = await crudify3.transaction(data);
632
+ if (response.success) {
633
+ setResetSuccess(true);
634
+ } else {
635
+ if (response.errors) {
636
+ if (typeof response.errors === "string") {
637
+ setErrors([response.errors]);
638
+ } else {
639
+ if (response.errors.newPassword) {
640
+ setHelperTextNewPassword(response.errors.newPassword[0]);
641
+ }
642
+ if (response.errors._error) {
643
+ setErrors(response.errors._error);
644
+ }
645
+ }
646
+ } else {
647
+ setErrors([t("error.unknown")]);
648
+ }
649
+ }
650
+ } catch (error) {
651
+ console.error("Reset password error:", error);
652
+ setErrors([t("error.unknown")]);
653
+ if (onError) {
654
+ onError(typeof error === "string" ? error : t("error.unknown"));
655
+ }
656
+ } finally {
657
+ setLoading(false);
658
+ }
659
+ };
660
+ const handleFormSubmit = (e) => {
661
+ e.preventDefault();
662
+ handleSubmit();
663
+ };
664
+ const handleKeyDown = (e) => {
665
+ if (e.key === "Enter" && !loading) {
666
+ e.preventDefault();
667
+ handleSubmit();
668
+ }
669
+ };
670
+ if (validatingCode) {
671
+ return /* @__PURE__ */ jsxs3(Box3, { sx: { textAlign: "center", py: 4 }, children: [
672
+ /* @__PURE__ */ jsx3(CircularProgress3, { size: 40 }),
673
+ /* @__PURE__ */ jsx3(Typography3, { variant: "body2", sx: { mt: 2 }, children: t("resetPassword.validatingCode") })
674
+ ] });
675
+ }
676
+ if (resetSuccess) {
677
+ return /* @__PURE__ */ jsxs3(Box3, { sx: { textAlign: "center" }, children: [
678
+ /* @__PURE__ */ jsx3(Alert3, { severity: "success", sx: { mb: 3 }, children: t("resetPassword.successMessage") }),
679
+ /* @__PURE__ */ jsx3(Typography3, { variant: "body2", sx: { mb: 3 }, children: t("resetPassword.successInstructions") }),
680
+ /* @__PURE__ */ jsx3(Button3, { variant: "contained", color: "primary", onClick: () => onNavigate?.("/login"), fullWidth: true, children: t("resetPassword.goToLoginButton") })
681
+ ] });
682
+ }
683
+ if (!codeValidated) {
684
+ return /* @__PURE__ */ jsxs3(Box3, { sx: { textAlign: "center" }, children: [
685
+ /* @__PURE__ */ jsx3(Alert3, { severity: "error", sx: { mb: 3 }, children: t("resetPassword.codeExpiredOrInvalid") }),
686
+ /* @__PURE__ */ jsxs3(Box3, { sx: { display: "flex", flexDirection: "column", gap: 2 }, children: [
687
+ /* @__PURE__ */ jsx3(Button3, { variant: "contained", color: "primary", onClick: () => onNavigate?.("/login/forgotPassword"), fullWidth: true, children: t("resetPassword.requestNewCodeButton") }),
688
+ /* @__PURE__ */ jsx3(Link3, { sx: { cursor: "pointer" }, onClick: () => onNavigate?.("/login"), variant: "body2", color: "secondary", children: t("common.backToLogin") })
689
+ ] })
690
+ ] });
691
+ }
692
+ return /* @__PURE__ */ jsxs3(Fragment3, { children: [
693
+ /* @__PURE__ */ jsx3(Typography3, { variant: "h5", component: "h1", sx: { mb: 3, textAlign: "center" }, children: t("resetPassword.title") }),
694
+ /* @__PURE__ */ jsx3(Typography3, { variant: "body2", sx: { mb: 3, textAlign: "center", color: "text.secondary" }, children: t("resetPassword.instructions") }),
695
+ /* @__PURE__ */ jsxs3(Box3, { component: "form", noValidate: true, onSubmit: handleFormSubmit, onKeyDown: handleKeyDown, children: [
696
+ /* @__PURE__ */ jsxs3(Box3, { sx: { mb: 2 }, children: [
697
+ /* @__PURE__ */ jsx3(
698
+ Typography3,
699
+ {
700
+ variant: "body2",
701
+ component: "label",
702
+ htmlFor: "newPassword",
703
+ sx: { display: "block", fontWeight: 500, color: "grey.700", mb: 0.5 },
704
+ children: t("resetPassword.newPasswordLabel")
705
+ }
706
+ ),
707
+ /* @__PURE__ */ jsx3(
708
+ TextField3,
709
+ {
710
+ fullWidth: true,
711
+ id: "newPassword",
712
+ name: "newPassword",
713
+ type: "password",
714
+ value: newPassword,
715
+ disabled: loading,
716
+ onChange: (e) => setNewPassword(e.target.value),
717
+ error: !!helperTextNewPassword,
718
+ helperText: helperTextNewPassword,
719
+ autoComplete: "new-password",
720
+ placeholder: t("resetPassword.newPasswordPlaceholder"),
721
+ required: true
722
+ }
723
+ )
724
+ ] }),
725
+ /* @__PURE__ */ jsxs3(Box3, { sx: { mb: 2 }, children: [
726
+ /* @__PURE__ */ jsx3(
727
+ Typography3,
728
+ {
729
+ variant: "body2",
730
+ component: "label",
731
+ htmlFor: "confirmPassword",
732
+ sx: { display: "block", fontWeight: 500, color: "grey.700", mb: 0.5 },
733
+ children: t("resetPassword.confirmPasswordLabel")
734
+ }
735
+ ),
736
+ /* @__PURE__ */ jsx3(
737
+ TextField3,
738
+ {
739
+ fullWidth: true,
740
+ id: "confirmPassword",
741
+ name: "confirmPassword",
742
+ type: "password",
743
+ value: confirmPassword,
744
+ disabled: loading,
745
+ onChange: (e) => setConfirmPassword(e.target.value),
746
+ error: !!helperTextConfirmPassword,
747
+ helperText: helperTextConfirmPassword,
748
+ autoComplete: "new-password",
749
+ placeholder: t("resetPassword.confirmPasswordPlaceholder"),
750
+ required: true
751
+ }
752
+ )
753
+ ] }),
754
+ /* @__PURE__ */ jsx3(Button3, { disabled: loading, type: "submit", fullWidth: true, variant: "contained", color: "primary", sx: { mt: 2, mb: 2 }, children: loading ? /* @__PURE__ */ jsx3(CircularProgress3, { size: 20 }) : t("resetPassword.resetPasswordButton") }),
755
+ /* @__PURE__ */ jsx3(Box3, { sx: { textAlign: "center" }, children: /* @__PURE__ */ jsx3(Link3, { sx: { cursor: "pointer" }, onClick: () => onNavigate?.("/login"), variant: "body2", color: "secondary", children: t("common.backToLogin") }) })
756
+ ] }),
757
+ errors.length > 0 && /* @__PURE__ */ jsx3(Box3, { sx: { mt: 2 }, children: errors.map((error, index) => /* @__PURE__ */ jsx3(Alert3, { variant: "filled", severity: "error", children: error }, index)) })
758
+ ] });
759
+ };
760
+ var ResetPasswordForm_default = ResetPasswordForm;
761
+
762
+ // src/components/CrudifyLogin/Forms/CheckCodeForm.tsx
763
+ import { useTranslation as useTranslation4 } from "react-i18next";
764
+ import { useState as useState4, useEffect as useEffect3 } from "react";
765
+ import { Typography as Typography4, TextField as TextField4, Button as Button4, Box as Box4, CircularProgress as CircularProgress4, Alert as Alert4, Link as Link4 } from "@mui/material";
766
+ import { Fragment as Fragment4, jsx as jsx4, jsxs as jsxs4 } from "react/jsx-runtime";
767
+ var CheckCodeForm = ({ config, onNavigate, onError }) => {
768
+ const [email, setEmail] = useState4("");
769
+ const [code, setCode] = useState4("");
770
+ const [loading, setLoading] = useState4(false);
771
+ const [errors, setErrors] = useState4([]);
772
+ const [helperTextEmail, setHelperTextEmail] = useState4(null);
773
+ const [helperTextCode, setHelperTextCode] = useState4(null);
774
+ const { t } = useTranslation4();
775
+ const { crudify: crudify3 } = useCrudifyLogin(config);
776
+ useEffect3(() => {
777
+ const timer = setTimeout(() => {
778
+ const emailInput = document.getElementById("email");
779
+ if (emailInput) emailInput.focus();
780
+ }, 100);
781
+ return () => clearTimeout(timer);
782
+ }, []);
783
+ const validateEmail = (email2) => {
784
+ const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
785
+ return emailRegex.test(email2);
786
+ };
787
+ const handleSubmit = async () => {
788
+ if (loading || !crudify3) return;
789
+ setErrors([]);
790
+ setHelperTextEmail(null);
791
+ setHelperTextCode(null);
792
+ if (!email) {
793
+ setHelperTextEmail(t("checkCode.emailRequired"));
794
+ return;
795
+ }
796
+ if (!validateEmail(email)) {
797
+ setHelperTextEmail(t("checkCode.invalidEmail"));
798
+ return;
799
+ }
800
+ if (!code) {
801
+ setHelperTextCode(t("checkCode.codeRequired"));
802
+ return;
803
+ }
804
+ setLoading(true);
805
+ try {
806
+ const data = [
807
+ {
808
+ operation: "validatePasswordResetCode",
809
+ data: { email, codePassword: code }
810
+ }
811
+ ];
812
+ const response = await crudify3.transaction(data);
813
+ if (response.success) {
814
+ const params = new URLSearchParams({ email, code }).toString();
815
+ onNavigate?.(`/login/resetPassword?${params}`);
816
+ } else {
817
+ if (response.errors) {
818
+ if (typeof response.errors === "string") {
819
+ setErrors([response.errors]);
820
+ } else {
821
+ if (response.errors.email) setHelperTextEmail(response.errors.email[0]);
822
+ if (response.errors.code) setHelperTextCode(response.errors.code[0]);
823
+ if (response.errors._error) setErrors(response.errors._error);
824
+ }
825
+ } else {
826
+ setErrors([t("error.unknown")]);
827
+ }
828
+ }
829
+ } catch (error) {
830
+ console.error("Check code error:", error);
831
+ setErrors([t("error.unknown")]);
832
+ if (onError) {
833
+ onError(typeof error === "string" ? error : t("error.unknown"));
834
+ }
835
+ } finally {
836
+ setLoading(false);
837
+ }
838
+ };
839
+ const handleFormSubmit = (e) => {
840
+ e.preventDefault();
841
+ handleSubmit();
842
+ };
843
+ const handleKeyDown = (e) => {
844
+ if (e.key === "Enter" && !loading) {
845
+ e.preventDefault();
846
+ handleSubmit();
847
+ }
848
+ };
849
+ return /* @__PURE__ */ jsxs4(Fragment4, { children: [
850
+ /* @__PURE__ */ jsx4(Typography4, { variant: "h5", component: "h1", sx: { mb: 3, textAlign: "center" }, children: t("checkCode.title") }),
851
+ /* @__PURE__ */ jsx4(Typography4, { variant: "body2", sx: { mb: 3, textAlign: "center", color: "text.secondary" }, children: t("checkCode.instructions") }),
852
+ /* @__PURE__ */ jsxs4(Box4, { component: "form", noValidate: true, onSubmit: handleFormSubmit, onKeyDown: handleKeyDown, children: [
853
+ /* @__PURE__ */ jsxs4(Box4, { sx: { mb: 2 }, children: [
854
+ /* @__PURE__ */ jsx4(
855
+ Typography4,
856
+ {
857
+ variant: "body2",
858
+ component: "label",
859
+ htmlFor: "email",
860
+ sx: { display: "block", fontWeight: 500, color: "grey.700", mb: 0.5 },
861
+ children: t("checkCode.emailLabel")
862
+ }
863
+ ),
864
+ /* @__PURE__ */ jsx4(
865
+ TextField4,
866
+ {
867
+ fullWidth: true,
868
+ id: "email",
869
+ name: "email",
870
+ type: "email",
871
+ value: email,
872
+ disabled: loading,
873
+ onChange: (e) => setEmail(e.target.value),
874
+ error: !!helperTextEmail,
875
+ helperText: helperTextEmail,
876
+ autoComplete: "email",
877
+ placeholder: t("checkCode.emailPlaceholder"),
878
+ required: true
879
+ }
880
+ )
881
+ ] }),
882
+ /* @__PURE__ */ jsxs4(Box4, { sx: { mb: 2 }, children: [
883
+ /* @__PURE__ */ jsx4(
884
+ Typography4,
885
+ {
886
+ variant: "body2",
887
+ component: "label",
888
+ htmlFor: "code",
889
+ sx: { display: "block", fontWeight: 500, color: "grey.700", mb: 0.5 },
890
+ children: t("checkCode.codeLabel")
891
+ }
892
+ ),
893
+ /* @__PURE__ */ jsx4(
894
+ TextField4,
895
+ {
896
+ fullWidth: true,
897
+ id: "code",
898
+ name: "code",
899
+ type: "text",
900
+ value: code,
901
+ disabled: loading,
902
+ onChange: (e) => setCode(e.target.value),
903
+ error: !!helperTextCode,
904
+ helperText: helperTextCode,
905
+ placeholder: t("checkCode.codePlaceholder"),
906
+ required: true
907
+ }
908
+ )
909
+ ] }),
910
+ /* @__PURE__ */ jsx4(Button4, { disabled: loading, type: "submit", fullWidth: true, variant: "contained", color: "primary", sx: { mt: 2, mb: 2 }, children: loading ? /* @__PURE__ */ jsx4(CircularProgress4, { size: 20 }) : t("checkCode.verifyButton") }),
911
+ /* @__PURE__ */ jsxs4(Box4, { sx: { textAlign: "center", display: "flex", flexDirection: "column", gap: 1 }, children: [
912
+ /* @__PURE__ */ jsx4(Link4, { sx: { cursor: "pointer" }, onClick: () => onNavigate?.("/login/forgotPassword"), variant: "body2", color: "primary", children: t("checkCode.resendCodeLink") }),
913
+ /* @__PURE__ */ jsx4(Link4, { sx: { cursor: "pointer" }, onClick: () => onNavigate?.("/login"), variant: "body2", color: "secondary", children: t("common.backToLogin") })
914
+ ] })
915
+ ] }),
916
+ errors.length > 0 && /* @__PURE__ */ jsx4(Box4, { sx: { mt: 2 }, children: errors.map((error, index) => /* @__PURE__ */ jsx4(Alert4, { variant: "filled", severity: "error", children: error }, index)) })
917
+ ] });
918
+ };
919
+ var CheckCodeForm_default = CheckCodeForm;
920
+
921
+ // src/components/CrudifyLogin/utils/cookies.ts
922
+ var getCookie = (name) => {
923
+ const match = document.cookie.match(new RegExp("(^|;)\\s*" + name + "=([^;]+)"));
924
+ return match ? match[2] : null;
925
+ };
926
+
927
+ // src/components/CrudifyLogin/index.tsx
928
+ import { jsx as jsx5, jsxs as jsxs5 } from "react/jsx-runtime";
929
+ var CrudifyLogin = ({
930
+ config: providedConfig,
931
+ onNavigate,
932
+ onLoginSuccess,
933
+ onError,
934
+ onSubdomainLoginSuccess,
935
+ initialScreen = "login",
936
+ redirectUrl = "/",
937
+ autoReadFromCookies = true
938
+ }) => {
939
+ const { t } = useTranslation5();
940
+ const [currentScreen, setCurrentScreen] = useState5(initialScreen);
941
+ const [searchParams, setSearchParams] = useState5();
942
+ const finalConfig = useMemo2(() => {
943
+ let cookieConfig = {};
944
+ if (autoReadFromCookies) {
945
+ try {
946
+ const publicApiKeyFromCookie = getCookie("publicApiKey");
947
+ const envFromCookie = getCookie("environment") || "prod";
948
+ const appNameFromCookie = getCookie("appName") || "Crudia";
949
+ const loginActionsFromCookie = (getCookie("loginActions") ?? "").split(",").map((action) => action.trim()).filter(Boolean);
950
+ let logoValue;
951
+ let appColorsValue = { primaryColor: "#1066BA" };
952
+ try {
953
+ const encodedLogo = getCookie("logo");
954
+ if (encodedLogo) {
955
+ const decodedLogo = decodeURIComponent(encodedLogo);
956
+ if (decodedLogo.startsWith("http")) logoValue = decodedLogo;
957
+ }
958
+ } catch (e) {
959
+ console.warn("Could not decode logo from cookie, using default.", e);
960
+ }
961
+ try {
962
+ const colorsCookie = getCookie("colors");
963
+ if (colorsCookie) {
964
+ const decodedColorsString = decodeURIComponent(colorsCookie);
965
+ const parsedColors = JSON.parse(decodedColorsString);
966
+ appColorsValue = { ...appColorsValue, ...parsedColors };
967
+ }
968
+ } catch (e) {
969
+ console.error("Failed to parse colors from cookie, using defaults.", e);
970
+ }
971
+ cookieConfig = {
972
+ publicApiKey: publicApiKeyFromCookie || void 0,
973
+ env: envFromCookie,
974
+ appName: decodeURIComponent(appNameFromCookie),
975
+ loginActions: loginActionsFromCookie,
976
+ logo: logoValue,
977
+ colors: appColorsValue
978
+ };
979
+ } catch (e) {
980
+ console.error("Error reading configuration from cookies:", e);
981
+ }
982
+ }
983
+ return {
984
+ publicApiKey: providedConfig?.publicApiKey || cookieConfig.publicApiKey,
985
+ env: providedConfig?.env || cookieConfig.env || "prod",
986
+ appName: providedConfig?.appName || cookieConfig.appName || "Crudia",
987
+ logo: providedConfig?.logo || cookieConfig.logo,
988
+ colors: { ...cookieConfig.colors, ...providedConfig?.colors },
989
+ loginActions: providedConfig?.loginActions || cookieConfig.loginActions || []
990
+ };
991
+ }, [providedConfig, autoReadFromCookies]);
992
+ const handleNavigate = (path) => {
993
+ const [basePath, queryString] = path.split("?");
994
+ if (queryString) {
995
+ setSearchParams(new URLSearchParams(queryString));
996
+ } else {
997
+ setSearchParams(void 0);
998
+ }
999
+ if (basePath.includes("/forgotPassword")) {
1000
+ setCurrentScreen("forgotPassword");
1001
+ } else if (basePath.includes("/checkCode")) {
1002
+ setCurrentScreen("checkCode");
1003
+ } else if (basePath.includes("/resetPassword")) {
1004
+ setCurrentScreen("resetPassword");
1005
+ } else {
1006
+ setCurrentScreen("login");
1007
+ }
1008
+ onNavigate?.(path);
1009
+ };
1010
+ const renderCurrentForm = () => {
1011
+ const commonProps = {
1012
+ config: finalConfig,
1013
+ onNavigate: handleNavigate,
1014
+ onError,
1015
+ redirectUrl
1016
+ };
1017
+ switch (currentScreen) {
1018
+ case "forgotPassword":
1019
+ return /* @__PURE__ */ jsx5(ForgotPasswordForm_default, { ...commonProps });
1020
+ case "checkCode":
1021
+ return /* @__PURE__ */ jsx5(CheckCodeForm_default, { ...commonProps });
1022
+ case "resetPassword":
1023
+ return /* @__PURE__ */ jsx5(ResetPasswordForm_default, { ...commonProps, searchParams });
1024
+ default:
1025
+ return /* @__PURE__ */ jsx5(LoginForm_default, { ...commonProps, onLoginSuccess, onSubdomainLoginSuccess });
1026
+ }
1027
+ };
1028
+ return /* @__PURE__ */ jsx5(
1029
+ Box5,
1030
+ {
1031
+ sx: {
1032
+ display: "flex",
1033
+ justifyContent: "center",
1034
+ alignItems: "center",
1035
+ minHeight: "100vh",
1036
+ padding: 2,
1037
+ backgroundColor: finalConfig.colors?.bgColor || "#f5f5f5"
1038
+ },
1039
+ children: /* @__PURE__ */ jsxs5(
1040
+ Paper,
1041
+ {
1042
+ elevation: 3,
1043
+ sx: {
1044
+ p: 4,
1045
+ width: "100%",
1046
+ maxWidth: 400,
1047
+ display: "flex",
1048
+ flexDirection: "column",
1049
+ background: finalConfig.colors?.bgColor || "#fff"
1050
+ },
1051
+ children: [
1052
+ /* @__PURE__ */ jsx5(Box5, { sx: { display: "flex", justifyContent: "center", mb: 3 }, children: /* @__PURE__ */ jsx5(
1053
+ "img",
1054
+ {
1055
+ src: finalConfig.logo || "/nocios-default.png",
1056
+ alt: t("login.logoAlt"),
1057
+ style: {
1058
+ width: "100%",
1059
+ maxWidth: "150px",
1060
+ height: "auto"
1061
+ },
1062
+ onError: (e) => {
1063
+ const target = e.target;
1064
+ target.src = "/nocios-default.png";
1065
+ }
1066
+ }
1067
+ ) }),
1068
+ finalConfig.appName && /* @__PURE__ */ jsx5(
1069
+ Typography5,
1070
+ {
1071
+ variant: "h6",
1072
+ component: "h1",
1073
+ sx: {
1074
+ textAlign: "center",
1075
+ mb: 2,
1076
+ color: finalConfig.colors?.primaryColor || "#1066BA"
1077
+ },
1078
+ children: finalConfig.appName
1079
+ }
1080
+ ),
1081
+ renderCurrentForm()
1082
+ ]
1083
+ }
1084
+ )
1085
+ }
1086
+ );
1087
+ };
1088
+ var CrudifyLogin_default = CrudifyLogin;
1089
+ export {
1090
+ CrudifyLogin_default as CrudifyLogin,
1091
+ default2 as crudify,
1092
+ getCookie,
1093
+ secureLocalStorage,
1094
+ secureSessionStorage
1095
+ };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@nocios/crudify-ui",
3
- "version": "1.0.38",
3
+ "version": "1.0.39",
4
4
  "description": "Biblioteca de componentes UI para Crudify",
5
5
  "author": "Nocios",
6
6
  "license": "MIT",
@@ -15,7 +15,7 @@
15
15
  }
16
16
  },
17
17
  "scripts": {
18
- "build": "tsup src/index.ts",
18
+ "build": "tsup",
19
19
  "start": "vite example",
20
20
  "prepublishOnly": "npm run build"
21
21
  },
package/tsup.config.ts ADDED
@@ -0,0 +1,9 @@
1
+ import { defineConfig } from 'tsup'
2
+
3
+ export default defineConfig({
4
+ entry: ['src/index.ts'],
5
+ format: ['cjs', 'esm'],
6
+ dts: true,
7
+ clean: true,
8
+ external: ['react', 'react-dom', '@mui/material', '@mui/icons-material']
9
+ })