@nocios/crudify-ui 1.0.40 → 1.0.43
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.d.mts +1 -2
- package/dist/index.d.ts +1 -2
- package/dist/index.js +21 -88
- package/dist/index.mjs +19 -86
- package/package.json +2 -2
- package/tsup.config.ts +5 -5
- package/LOGIN_POSIBLES_IMPLEMENTACIONES.md +0 -281
package/dist/index.d.mts
CHANGED
|
@@ -5,7 +5,7 @@ import React from 'react';
|
|
|
5
5
|
type BoxScreenType = "login" | "signUp" | "forgotPassword" | "resetPassword" | "checkCode";
|
|
6
6
|
interface CrudifyLoginConfig {
|
|
7
7
|
publicApiKey?: string;
|
|
8
|
-
env?: "dev" | "stg" | "prod";
|
|
8
|
+
env?: "dev" | "stg" | "api" | "prod";
|
|
9
9
|
appName?: string;
|
|
10
10
|
logo?: string;
|
|
11
11
|
colors?: {
|
|
@@ -23,7 +23,6 @@ interface CrudifyLoginProps {
|
|
|
23
23
|
initialScreen?: BoxScreenType;
|
|
24
24
|
redirectUrl?: string;
|
|
25
25
|
autoReadFromCookies?: boolean;
|
|
26
|
-
onSubdomainLoginSuccess?: (token: string, config: CrudifyLoginConfig) => void;
|
|
27
26
|
}
|
|
28
27
|
|
|
29
28
|
declare const CrudifyLogin: React.FC<CrudifyLoginProps>;
|
package/dist/index.d.ts
CHANGED
|
@@ -5,7 +5,7 @@ import React from 'react';
|
|
|
5
5
|
type BoxScreenType = "login" | "signUp" | "forgotPassword" | "resetPassword" | "checkCode";
|
|
6
6
|
interface CrudifyLoginConfig {
|
|
7
7
|
publicApiKey?: string;
|
|
8
|
-
env?: "dev" | "stg" | "prod";
|
|
8
|
+
env?: "dev" | "stg" | "api" | "prod";
|
|
9
9
|
appName?: string;
|
|
10
10
|
logo?: string;
|
|
11
11
|
colors?: {
|
|
@@ -23,7 +23,6 @@ interface CrudifyLoginProps {
|
|
|
23
23
|
initialScreen?: BoxScreenType;
|
|
24
24
|
redirectUrl?: string;
|
|
25
25
|
autoReadFromCookies?: boolean;
|
|
26
|
-
onSubdomainLoginSuccess?: (token: string, config: CrudifyLoginConfig) => void;
|
|
27
26
|
}
|
|
28
27
|
|
|
29
28
|
declare const CrudifyLogin: React.FC<CrudifyLoginProps>;
|
package/dist/index.js
CHANGED
|
@@ -32,13 +32,13 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
|
|
|
32
32
|
var index_exports = {};
|
|
33
33
|
__export(index_exports, {
|
|
34
34
|
CrudifyLogin: () => CrudifyLogin_default,
|
|
35
|
-
crudify: () =>
|
|
35
|
+
crudify: () => import_crudify_browser2.default,
|
|
36
36
|
getCookie: () => getCookie,
|
|
37
37
|
secureLocalStorage: () => secureLocalStorage,
|
|
38
38
|
secureSessionStorage: () => secureSessionStorage
|
|
39
39
|
});
|
|
40
40
|
module.exports = __toCommonJS(index_exports);
|
|
41
|
-
var
|
|
41
|
+
var import_crudify_browser2 = __toESM(require("@nocios/crudify-browser"));
|
|
42
42
|
__reExport(index_exports, require("@nocios/crudify-browser"), module.exports);
|
|
43
43
|
|
|
44
44
|
// src/components/CrudifyLogin/index.tsx
|
|
@@ -177,27 +177,16 @@ var secureSessionStorage = new SecureStorage("sessionStorage");
|
|
|
177
177
|
var secureLocalStorage = new SecureStorage("localStorage");
|
|
178
178
|
|
|
179
179
|
// src/components/CrudifyLogin/Forms/LoginForm.tsx
|
|
180
|
-
var import_crudify_browser2 = __toESM(require("@nocios/crudify-browser"));
|
|
181
180
|
var import_jsx_runtime = require("react/jsx-runtime");
|
|
182
|
-
var LoginForm = ({
|
|
183
|
-
config,
|
|
184
|
-
onNavigate,
|
|
185
|
-
onLoginSuccess,
|
|
186
|
-
onError,
|
|
187
|
-
redirectUrl = "/",
|
|
188
|
-
onSubdomainLoginSuccess
|
|
189
|
-
}) => {
|
|
181
|
+
var LoginForm = ({ config, onNavigate, onLoginSuccess, onError, redirectUrl = "/" }) => {
|
|
190
182
|
const [username, setUsername] = (0, import_react2.useState)("");
|
|
191
183
|
const [password, setPassword] = (0, import_react2.useState)("");
|
|
192
|
-
const [subdomain, setSubdomain] = (0, import_react2.useState)("");
|
|
193
184
|
const [loading, setLoading] = (0, import_react2.useState)(false);
|
|
194
185
|
const [errors, setErrors] = (0, import_react2.useState)([]);
|
|
195
186
|
const [helperTextEmail, setHelperTextEmail] = (0, import_react2.useState)(null);
|
|
196
187
|
const [helperTextPassword, setHelperTextPassword] = (0, import_react2.useState)(null);
|
|
197
|
-
const [helperTextSubdomain, setHelperTextSubdomain] = (0, import_react2.useState)(null);
|
|
198
188
|
const { t } = (0, import_react_i18next.useTranslation)();
|
|
199
189
|
const usernameInputRef = (0, import_react2.useRef)(null);
|
|
200
|
-
const hasPublicApiKey = Boolean(config.publicApiKey);
|
|
201
190
|
const { crudify: crudifyFromHook } = useCrudifyLogin(config, {
|
|
202
191
|
showErrorNotifications: false,
|
|
203
192
|
showSuccessNotifications: false
|
|
@@ -234,41 +223,19 @@ var LoginForm = ({
|
|
|
234
223
|
setHelperTextPassword(t("login.passwordRequired"));
|
|
235
224
|
return;
|
|
236
225
|
}
|
|
237
|
-
if (!hasPublicApiKey && !subdomain.trim()) {
|
|
238
|
-
setHelperTextSubdomain(t("login.subdomainRequired", "Subdomain is required"));
|
|
239
|
-
return;
|
|
240
|
-
}
|
|
241
226
|
setErrors([]);
|
|
242
227
|
setHelperTextEmail(null);
|
|
243
228
|
setHelperTextPassword(null);
|
|
244
|
-
setHelperTextSubdomain(null);
|
|
245
229
|
setLoading(true);
|
|
246
230
|
try {
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
response = await crudifyFromHook.login(username, password);
|
|
250
|
-
} else {
|
|
251
|
-
try {
|
|
252
|
-
import_crudify_browser2.default.config(config.env || "prod");
|
|
253
|
-
response = await import_crudify_browser2.default.loginWithSubdomain(username, password, subdomain);
|
|
254
|
-
} catch (subdomainError) {
|
|
255
|
-
console.error("Error during subdomain login:", subdomainError);
|
|
256
|
-
throw subdomainError;
|
|
257
|
-
}
|
|
231
|
+
if (!crudifyFromHook) {
|
|
232
|
+
throw new Error("Crudify not initialized");
|
|
258
233
|
}
|
|
234
|
+
const response = await crudifyFromHook.login(username, password);
|
|
259
235
|
setLoading(false);
|
|
260
236
|
if (response.success) {
|
|
261
237
|
secureSessionStorage.setToken(response.data.token);
|
|
262
|
-
if (
|
|
263
|
-
const newConfig = {
|
|
264
|
-
...config,
|
|
265
|
-
publicApiKey: response.data.publicApiKey || config.publicApiKey,
|
|
266
|
-
appName: response.data.appName || config.appName,
|
|
267
|
-
logo: response.data.logo || config.logo,
|
|
268
|
-
colors: response.data.colors || config.colors
|
|
269
|
-
};
|
|
270
|
-
onSubdomainLoginSuccess(response.data.token, newConfig);
|
|
271
|
-
} else if (onLoginSuccess) {
|
|
238
|
+
if (onLoginSuccess) {
|
|
272
239
|
onLoginSuccess(response.data.token, redirectUrl);
|
|
273
240
|
}
|
|
274
241
|
} else {
|
|
@@ -289,15 +256,12 @@ var LoginForm = ({
|
|
|
289
256
|
if (fieldsWarning) {
|
|
290
257
|
if (fieldsWarning.username) setHelperTextEmail(fieldsWarning.username[0]);
|
|
291
258
|
if (fieldsWarning.password) setHelperTextPassword(fieldsWarning.password[0]);
|
|
292
|
-
if (fieldsWarning.subdomain) setHelperTextSubdomain(fieldsWarning.subdomain[0]);
|
|
293
259
|
} else if (status === "INVALID_CREDENTIALS") {
|
|
294
260
|
const translatedError = getSafeErrorTranslation(status);
|
|
295
261
|
setErrors([translatedError]);
|
|
296
262
|
} else if (status === "TOO_MANY_REQUESTS") {
|
|
297
263
|
const translatedError = getSafeErrorTranslation(status);
|
|
298
264
|
setErrors([translatedError]);
|
|
299
|
-
} else if (status === "SUBDOMAIN_NOT_FOUND") {
|
|
300
|
-
setHelperTextSubdomain(t("login.subdomainNotFound", "Subdomain not found"));
|
|
301
265
|
} else if (status) {
|
|
302
266
|
setErrors([getSafeErrorTranslation(status)]);
|
|
303
267
|
} else {
|
|
@@ -310,11 +274,9 @@ var LoginForm = ({
|
|
|
310
274
|
} else if (typeof errors2 === "object") {
|
|
311
275
|
const hasUsernameError = errors2.username && errors2.username.length > 0;
|
|
312
276
|
const hasPasswordError = errors2.password && errors2.password.length > 0;
|
|
313
|
-
|
|
314
|
-
if (hasUsernameError || hasPasswordError || hasSubdomainError) {
|
|
277
|
+
if (hasUsernameError || hasPasswordError) {
|
|
315
278
|
if (hasUsernameError) setHelperTextEmail(errors2.username[0]);
|
|
316
279
|
if (hasPasswordError) setHelperTextPassword(errors2.password[0]);
|
|
317
|
-
if (hasSubdomainError) setHelperTextSubdomain(errors2.subdomain[0]);
|
|
318
280
|
} else if (errors2._error && errors2._error.length > 0) {
|
|
319
281
|
const errorMessage = errors2._error[0];
|
|
320
282
|
if (typeof errorMessage === "string" && errorMessage.match(/^[A-Z_]+$/)) {
|
|
@@ -351,34 +313,6 @@ var LoginForm = ({
|
|
|
351
313
|
onKeyDown: handleKeyDown,
|
|
352
314
|
sx: { width: "100%", display: "flex", flexDirection: "column", gap: 2 },
|
|
353
315
|
children: [
|
|
354
|
-
!hasPublicApiKey && /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_material.Box, { sx: { mb: 1 }, children: [
|
|
355
|
-
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
356
|
-
import_material.Typography,
|
|
357
|
-
{
|
|
358
|
-
variant: "body2",
|
|
359
|
-
component: "label",
|
|
360
|
-
htmlFor: "subdomain",
|
|
361
|
-
sx: { display: "block", fontWeight: 500, color: "grey.700", mb: 0.5 },
|
|
362
|
-
children: t("login.subdomainLabel", "Subdomain")
|
|
363
|
-
}
|
|
364
|
-
),
|
|
365
|
-
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
366
|
-
import_material.TextField,
|
|
367
|
-
{
|
|
368
|
-
fullWidth: true,
|
|
369
|
-
id: "subdomain",
|
|
370
|
-
name: "subdomain",
|
|
371
|
-
type: "text",
|
|
372
|
-
value: subdomain,
|
|
373
|
-
disabled: loading,
|
|
374
|
-
onChange: (e) => setSubdomain(e.target.value),
|
|
375
|
-
error: !!helperTextSubdomain,
|
|
376
|
-
helperText: helperTextSubdomain,
|
|
377
|
-
placeholder: t("login.subdomainPlaceholder", "Enter your subdomain"),
|
|
378
|
-
required: true
|
|
379
|
-
}
|
|
380
|
-
)
|
|
381
|
-
] }),
|
|
382
316
|
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_material.Box, { sx: { mb: 1 }, children: [
|
|
383
317
|
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
384
318
|
import_material.Typography,
|
|
@@ -466,13 +400,13 @@ var ForgotPasswordForm = ({ config, onNavigate, onError }) => {
|
|
|
466
400
|
const [emailSent, setEmailSent] = (0, import_react3.useState)(false);
|
|
467
401
|
const [codeAlreadyExists, setCodeAlreadyExists] = (0, import_react3.useState)(false);
|
|
468
402
|
const { t } = (0, import_react_i18next2.useTranslation)();
|
|
469
|
-
const { crudify:
|
|
403
|
+
const { crudify: crudify2 } = useCrudifyLogin(config);
|
|
470
404
|
const validateEmail = (email2) => {
|
|
471
405
|
const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
|
|
472
406
|
return emailRegex.test(email2);
|
|
473
407
|
};
|
|
474
408
|
const handleSubmit = async () => {
|
|
475
|
-
if (loading || !
|
|
409
|
+
if (loading || !crudify2) return;
|
|
476
410
|
setErrors([]);
|
|
477
411
|
setHelperTextEmail(null);
|
|
478
412
|
if (!email) {
|
|
@@ -486,7 +420,7 @@ var ForgotPasswordForm = ({ config, onNavigate, onError }) => {
|
|
|
486
420
|
setLoading(true);
|
|
487
421
|
try {
|
|
488
422
|
const data = [{ operation: "requestPasswordReset", data: { email } }];
|
|
489
|
-
const response = await
|
|
423
|
+
const response = await crudify2.transaction(data);
|
|
490
424
|
if (response.success) {
|
|
491
425
|
if (response.data && response.data.existingCodeValid) {
|
|
492
426
|
setCodeAlreadyExists(true);
|
|
@@ -594,10 +528,10 @@ var ResetPasswordForm = ({ config, onNavigate, onError, searchParams }) => {
|
|
|
594
528
|
const [codeValidated, setCodeValidated] = (0, import_react4.useState)(false);
|
|
595
529
|
const [resetSuccess, setResetSuccess] = (0, import_react4.useState)(false);
|
|
596
530
|
const { t } = (0, import_react_i18next3.useTranslation)();
|
|
597
|
-
const { crudify:
|
|
531
|
+
const { crudify: crudify2 } = useCrudifyLogin(config);
|
|
598
532
|
(0, import_react4.useEffect)(() => {
|
|
599
533
|
const validateCode = async (emailToValidate, codeToValidate) => {
|
|
600
|
-
if (!
|
|
534
|
+
if (!crudify2) return;
|
|
601
535
|
try {
|
|
602
536
|
const data = [
|
|
603
537
|
{
|
|
@@ -605,7 +539,7 @@ var ResetPasswordForm = ({ config, onNavigate, onError, searchParams }) => {
|
|
|
605
539
|
data: { email: emailToValidate, codePassword: codeToValidate }
|
|
606
540
|
}
|
|
607
541
|
];
|
|
608
|
-
const response = await
|
|
542
|
+
const response = await crudify2.transaction(data);
|
|
609
543
|
if (response.success) {
|
|
610
544
|
setCodeValidated(true);
|
|
611
545
|
} else {
|
|
@@ -633,7 +567,7 @@ var ResetPasswordForm = ({ config, onNavigate, onError, searchParams }) => {
|
|
|
633
567
|
setValidatingCode(false);
|
|
634
568
|
setErrors([t("resetPassword.missingParameters")]);
|
|
635
569
|
}
|
|
636
|
-
}, [searchParams,
|
|
570
|
+
}, [searchParams, crudify2, t]);
|
|
637
571
|
const validatePasswords = () => {
|
|
638
572
|
setHelperTextNewPassword(null);
|
|
639
573
|
setHelperTextConfirmPassword(null);
|
|
@@ -656,7 +590,7 @@ var ResetPasswordForm = ({ config, onNavigate, onError, searchParams }) => {
|
|
|
656
590
|
return true;
|
|
657
591
|
};
|
|
658
592
|
const handleSubmit = async () => {
|
|
659
|
-
if (loading || !
|
|
593
|
+
if (loading || !crudify2) return;
|
|
660
594
|
setErrors([]);
|
|
661
595
|
if (!validatePasswords()) return;
|
|
662
596
|
setLoading(true);
|
|
@@ -667,7 +601,7 @@ var ResetPasswordForm = ({ config, onNavigate, onError, searchParams }) => {
|
|
|
667
601
|
data: { email, codePassword: code, newPassword }
|
|
668
602
|
}
|
|
669
603
|
];
|
|
670
|
-
const response = await
|
|
604
|
+
const response = await crudify2.transaction(data);
|
|
671
605
|
if (response.success) {
|
|
672
606
|
setResetSuccess(true);
|
|
673
607
|
} else {
|
|
@@ -811,7 +745,7 @@ var CheckCodeForm = ({ config, onNavigate, onError }) => {
|
|
|
811
745
|
const [helperTextEmail, setHelperTextEmail] = (0, import_react5.useState)(null);
|
|
812
746
|
const [helperTextCode, setHelperTextCode] = (0, import_react5.useState)(null);
|
|
813
747
|
const { t } = (0, import_react_i18next4.useTranslation)();
|
|
814
|
-
const { crudify:
|
|
748
|
+
const { crudify: crudify2 } = useCrudifyLogin(config);
|
|
815
749
|
(0, import_react5.useEffect)(() => {
|
|
816
750
|
const timer = setTimeout(() => {
|
|
817
751
|
const emailInput = document.getElementById("email");
|
|
@@ -824,7 +758,7 @@ var CheckCodeForm = ({ config, onNavigate, onError }) => {
|
|
|
824
758
|
return emailRegex.test(email2);
|
|
825
759
|
};
|
|
826
760
|
const handleSubmit = async () => {
|
|
827
|
-
if (loading || !
|
|
761
|
+
if (loading || !crudify2) return;
|
|
828
762
|
setErrors([]);
|
|
829
763
|
setHelperTextEmail(null);
|
|
830
764
|
setHelperTextCode(null);
|
|
@@ -848,7 +782,7 @@ var CheckCodeForm = ({ config, onNavigate, onError }) => {
|
|
|
848
782
|
data: { email, codePassword: code }
|
|
849
783
|
}
|
|
850
784
|
];
|
|
851
|
-
const response = await
|
|
785
|
+
const response = await crudify2.transaction(data);
|
|
852
786
|
if (response.success) {
|
|
853
787
|
const params = new URLSearchParams({ email, code }).toString();
|
|
854
788
|
onNavigate?.(`/login/resetPassword?${params}`);
|
|
@@ -970,7 +904,6 @@ var CrudifyLogin = ({
|
|
|
970
904
|
onNavigate,
|
|
971
905
|
onLoginSuccess,
|
|
972
906
|
onError,
|
|
973
|
-
onSubdomainLoginSuccess,
|
|
974
907
|
initialScreen = "login",
|
|
975
908
|
redirectUrl = "/",
|
|
976
909
|
autoReadFromCookies = true
|
|
@@ -1061,7 +994,7 @@ var CrudifyLogin = ({
|
|
|
1061
994
|
case "resetPassword":
|
|
1062
995
|
return /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(ResetPasswordForm_default, { ...commonProps, searchParams });
|
|
1063
996
|
default:
|
|
1064
|
-
return /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(LoginForm_default, { ...commonProps, onLoginSuccess
|
|
997
|
+
return /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(LoginForm_default, { ...commonProps, onLoginSuccess });
|
|
1065
998
|
}
|
|
1066
999
|
};
|
|
1067
1000
|
return /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
|
package/dist/index.mjs
CHANGED
|
@@ -138,27 +138,16 @@ var secureSessionStorage = new SecureStorage("sessionStorage");
|
|
|
138
138
|
var secureLocalStorage = new SecureStorage("localStorage");
|
|
139
139
|
|
|
140
140
|
// src/components/CrudifyLogin/Forms/LoginForm.tsx
|
|
141
|
-
import crudify2 from "@nocios/crudify-browser";
|
|
142
141
|
import { Fragment, jsx, jsxs } from "react/jsx-runtime";
|
|
143
|
-
var LoginForm = ({
|
|
144
|
-
config,
|
|
145
|
-
onNavigate,
|
|
146
|
-
onLoginSuccess,
|
|
147
|
-
onError,
|
|
148
|
-
redirectUrl = "/",
|
|
149
|
-
onSubdomainLoginSuccess
|
|
150
|
-
}) => {
|
|
142
|
+
var LoginForm = ({ config, onNavigate, onLoginSuccess, onError, redirectUrl = "/" }) => {
|
|
151
143
|
const [username, setUsername] = useState("");
|
|
152
144
|
const [password, setPassword] = useState("");
|
|
153
|
-
const [subdomain, setSubdomain] = useState("");
|
|
154
145
|
const [loading, setLoading] = useState(false);
|
|
155
146
|
const [errors, setErrors] = useState([]);
|
|
156
147
|
const [helperTextEmail, setHelperTextEmail] = useState(null);
|
|
157
148
|
const [helperTextPassword, setHelperTextPassword] = useState(null);
|
|
158
|
-
const [helperTextSubdomain, setHelperTextSubdomain] = useState(null);
|
|
159
149
|
const { t } = useTranslation();
|
|
160
150
|
const usernameInputRef = useRef(null);
|
|
161
|
-
const hasPublicApiKey = Boolean(config.publicApiKey);
|
|
162
151
|
const { crudify: crudifyFromHook } = useCrudifyLogin(config, {
|
|
163
152
|
showErrorNotifications: false,
|
|
164
153
|
showSuccessNotifications: false
|
|
@@ -195,41 +184,19 @@ var LoginForm = ({
|
|
|
195
184
|
setHelperTextPassword(t("login.passwordRequired"));
|
|
196
185
|
return;
|
|
197
186
|
}
|
|
198
|
-
if (!hasPublicApiKey && !subdomain.trim()) {
|
|
199
|
-
setHelperTextSubdomain(t("login.subdomainRequired", "Subdomain is required"));
|
|
200
|
-
return;
|
|
201
|
-
}
|
|
202
187
|
setErrors([]);
|
|
203
188
|
setHelperTextEmail(null);
|
|
204
189
|
setHelperTextPassword(null);
|
|
205
|
-
setHelperTextSubdomain(null);
|
|
206
190
|
setLoading(true);
|
|
207
191
|
try {
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
response = await crudifyFromHook.login(username, password);
|
|
211
|
-
} else {
|
|
212
|
-
try {
|
|
213
|
-
crudify2.config(config.env || "prod");
|
|
214
|
-
response = await crudify2.loginWithSubdomain(username, password, subdomain);
|
|
215
|
-
} catch (subdomainError) {
|
|
216
|
-
console.error("Error during subdomain login:", subdomainError);
|
|
217
|
-
throw subdomainError;
|
|
218
|
-
}
|
|
192
|
+
if (!crudifyFromHook) {
|
|
193
|
+
throw new Error("Crudify not initialized");
|
|
219
194
|
}
|
|
195
|
+
const response = await crudifyFromHook.login(username, password);
|
|
220
196
|
setLoading(false);
|
|
221
197
|
if (response.success) {
|
|
222
198
|
secureSessionStorage.setToken(response.data.token);
|
|
223
|
-
if (
|
|
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) {
|
|
199
|
+
if (onLoginSuccess) {
|
|
233
200
|
onLoginSuccess(response.data.token, redirectUrl);
|
|
234
201
|
}
|
|
235
202
|
} else {
|
|
@@ -250,15 +217,12 @@ var LoginForm = ({
|
|
|
250
217
|
if (fieldsWarning) {
|
|
251
218
|
if (fieldsWarning.username) setHelperTextEmail(fieldsWarning.username[0]);
|
|
252
219
|
if (fieldsWarning.password) setHelperTextPassword(fieldsWarning.password[0]);
|
|
253
|
-
if (fieldsWarning.subdomain) setHelperTextSubdomain(fieldsWarning.subdomain[0]);
|
|
254
220
|
} else if (status === "INVALID_CREDENTIALS") {
|
|
255
221
|
const translatedError = getSafeErrorTranslation(status);
|
|
256
222
|
setErrors([translatedError]);
|
|
257
223
|
} else if (status === "TOO_MANY_REQUESTS") {
|
|
258
224
|
const translatedError = getSafeErrorTranslation(status);
|
|
259
225
|
setErrors([translatedError]);
|
|
260
|
-
} else if (status === "SUBDOMAIN_NOT_FOUND") {
|
|
261
|
-
setHelperTextSubdomain(t("login.subdomainNotFound", "Subdomain not found"));
|
|
262
226
|
} else if (status) {
|
|
263
227
|
setErrors([getSafeErrorTranslation(status)]);
|
|
264
228
|
} else {
|
|
@@ -271,11 +235,9 @@ var LoginForm = ({
|
|
|
271
235
|
} else if (typeof errors2 === "object") {
|
|
272
236
|
const hasUsernameError = errors2.username && errors2.username.length > 0;
|
|
273
237
|
const hasPasswordError = errors2.password && errors2.password.length > 0;
|
|
274
|
-
|
|
275
|
-
if (hasUsernameError || hasPasswordError || hasSubdomainError) {
|
|
238
|
+
if (hasUsernameError || hasPasswordError) {
|
|
276
239
|
if (hasUsernameError) setHelperTextEmail(errors2.username[0]);
|
|
277
240
|
if (hasPasswordError) setHelperTextPassword(errors2.password[0]);
|
|
278
|
-
if (hasSubdomainError) setHelperTextSubdomain(errors2.subdomain[0]);
|
|
279
241
|
} else if (errors2._error && errors2._error.length > 0) {
|
|
280
242
|
const errorMessage = errors2._error[0];
|
|
281
243
|
if (typeof errorMessage === "string" && errorMessage.match(/^[A-Z_]+$/)) {
|
|
@@ -312,34 +274,6 @@ var LoginForm = ({
|
|
|
312
274
|
onKeyDown: handleKeyDown,
|
|
313
275
|
sx: { width: "100%", display: "flex", flexDirection: "column", gap: 2 },
|
|
314
276
|
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
277
|
/* @__PURE__ */ jsxs(Box, { sx: { mb: 1 }, children: [
|
|
344
278
|
/* @__PURE__ */ jsx(
|
|
345
279
|
Typography,
|
|
@@ -427,13 +361,13 @@ var ForgotPasswordForm = ({ config, onNavigate, onError }) => {
|
|
|
427
361
|
const [emailSent, setEmailSent] = useState2(false);
|
|
428
362
|
const [codeAlreadyExists, setCodeAlreadyExists] = useState2(false);
|
|
429
363
|
const { t } = useTranslation2();
|
|
430
|
-
const { crudify:
|
|
364
|
+
const { crudify: crudify2 } = useCrudifyLogin(config);
|
|
431
365
|
const validateEmail = (email2) => {
|
|
432
366
|
const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
|
|
433
367
|
return emailRegex.test(email2);
|
|
434
368
|
};
|
|
435
369
|
const handleSubmit = async () => {
|
|
436
|
-
if (loading || !
|
|
370
|
+
if (loading || !crudify2) return;
|
|
437
371
|
setErrors([]);
|
|
438
372
|
setHelperTextEmail(null);
|
|
439
373
|
if (!email) {
|
|
@@ -447,7 +381,7 @@ var ForgotPasswordForm = ({ config, onNavigate, onError }) => {
|
|
|
447
381
|
setLoading(true);
|
|
448
382
|
try {
|
|
449
383
|
const data = [{ operation: "requestPasswordReset", data: { email } }];
|
|
450
|
-
const response = await
|
|
384
|
+
const response = await crudify2.transaction(data);
|
|
451
385
|
if (response.success) {
|
|
452
386
|
if (response.data && response.data.existingCodeValid) {
|
|
453
387
|
setCodeAlreadyExists(true);
|
|
@@ -555,10 +489,10 @@ var ResetPasswordForm = ({ config, onNavigate, onError, searchParams }) => {
|
|
|
555
489
|
const [codeValidated, setCodeValidated] = useState3(false);
|
|
556
490
|
const [resetSuccess, setResetSuccess] = useState3(false);
|
|
557
491
|
const { t } = useTranslation3();
|
|
558
|
-
const { crudify:
|
|
492
|
+
const { crudify: crudify2 } = useCrudifyLogin(config);
|
|
559
493
|
useEffect2(() => {
|
|
560
494
|
const validateCode = async (emailToValidate, codeToValidate) => {
|
|
561
|
-
if (!
|
|
495
|
+
if (!crudify2) return;
|
|
562
496
|
try {
|
|
563
497
|
const data = [
|
|
564
498
|
{
|
|
@@ -566,7 +500,7 @@ var ResetPasswordForm = ({ config, onNavigate, onError, searchParams }) => {
|
|
|
566
500
|
data: { email: emailToValidate, codePassword: codeToValidate }
|
|
567
501
|
}
|
|
568
502
|
];
|
|
569
|
-
const response = await
|
|
503
|
+
const response = await crudify2.transaction(data);
|
|
570
504
|
if (response.success) {
|
|
571
505
|
setCodeValidated(true);
|
|
572
506
|
} else {
|
|
@@ -594,7 +528,7 @@ var ResetPasswordForm = ({ config, onNavigate, onError, searchParams }) => {
|
|
|
594
528
|
setValidatingCode(false);
|
|
595
529
|
setErrors([t("resetPassword.missingParameters")]);
|
|
596
530
|
}
|
|
597
|
-
}, [searchParams,
|
|
531
|
+
}, [searchParams, crudify2, t]);
|
|
598
532
|
const validatePasswords = () => {
|
|
599
533
|
setHelperTextNewPassword(null);
|
|
600
534
|
setHelperTextConfirmPassword(null);
|
|
@@ -617,7 +551,7 @@ var ResetPasswordForm = ({ config, onNavigate, onError, searchParams }) => {
|
|
|
617
551
|
return true;
|
|
618
552
|
};
|
|
619
553
|
const handleSubmit = async () => {
|
|
620
|
-
if (loading || !
|
|
554
|
+
if (loading || !crudify2) return;
|
|
621
555
|
setErrors([]);
|
|
622
556
|
if (!validatePasswords()) return;
|
|
623
557
|
setLoading(true);
|
|
@@ -628,7 +562,7 @@ var ResetPasswordForm = ({ config, onNavigate, onError, searchParams }) => {
|
|
|
628
562
|
data: { email, codePassword: code, newPassword }
|
|
629
563
|
}
|
|
630
564
|
];
|
|
631
|
-
const response = await
|
|
565
|
+
const response = await crudify2.transaction(data);
|
|
632
566
|
if (response.success) {
|
|
633
567
|
setResetSuccess(true);
|
|
634
568
|
} else {
|
|
@@ -772,7 +706,7 @@ var CheckCodeForm = ({ config, onNavigate, onError }) => {
|
|
|
772
706
|
const [helperTextEmail, setHelperTextEmail] = useState4(null);
|
|
773
707
|
const [helperTextCode, setHelperTextCode] = useState4(null);
|
|
774
708
|
const { t } = useTranslation4();
|
|
775
|
-
const { crudify:
|
|
709
|
+
const { crudify: crudify2 } = useCrudifyLogin(config);
|
|
776
710
|
useEffect3(() => {
|
|
777
711
|
const timer = setTimeout(() => {
|
|
778
712
|
const emailInput = document.getElementById("email");
|
|
@@ -785,7 +719,7 @@ var CheckCodeForm = ({ config, onNavigate, onError }) => {
|
|
|
785
719
|
return emailRegex.test(email2);
|
|
786
720
|
};
|
|
787
721
|
const handleSubmit = async () => {
|
|
788
|
-
if (loading || !
|
|
722
|
+
if (loading || !crudify2) return;
|
|
789
723
|
setErrors([]);
|
|
790
724
|
setHelperTextEmail(null);
|
|
791
725
|
setHelperTextCode(null);
|
|
@@ -809,7 +743,7 @@ var CheckCodeForm = ({ config, onNavigate, onError }) => {
|
|
|
809
743
|
data: { email, codePassword: code }
|
|
810
744
|
}
|
|
811
745
|
];
|
|
812
|
-
const response = await
|
|
746
|
+
const response = await crudify2.transaction(data);
|
|
813
747
|
if (response.success) {
|
|
814
748
|
const params = new URLSearchParams({ email, code }).toString();
|
|
815
749
|
onNavigate?.(`/login/resetPassword?${params}`);
|
|
@@ -931,7 +865,6 @@ var CrudifyLogin = ({
|
|
|
931
865
|
onNavigate,
|
|
932
866
|
onLoginSuccess,
|
|
933
867
|
onError,
|
|
934
|
-
onSubdomainLoginSuccess,
|
|
935
868
|
initialScreen = "login",
|
|
936
869
|
redirectUrl = "/",
|
|
937
870
|
autoReadFromCookies = true
|
|
@@ -1022,7 +955,7 @@ var CrudifyLogin = ({
|
|
|
1022
955
|
case "resetPassword":
|
|
1023
956
|
return /* @__PURE__ */ jsx5(ResetPasswordForm_default, { ...commonProps, searchParams });
|
|
1024
957
|
default:
|
|
1025
|
-
return /* @__PURE__ */ jsx5(LoginForm_default, { ...commonProps, onLoginSuccess
|
|
958
|
+
return /* @__PURE__ */ jsx5(LoginForm_default, { ...commonProps, onLoginSuccess });
|
|
1026
959
|
}
|
|
1027
960
|
};
|
|
1028
961
|
return /* @__PURE__ */ jsx5(
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@nocios/crudify-ui",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.43",
|
|
4
4
|
"description": "Biblioteca de componentes UI para Crudify",
|
|
5
5
|
"author": "Nocios",
|
|
6
6
|
"license": "MIT",
|
|
@@ -25,7 +25,7 @@
|
|
|
25
25
|
"@mui/icons-material": "^7.1.0",
|
|
26
26
|
"@mui/material": "^7.1.0",
|
|
27
27
|
"@mui/x-data-grid": "^8.5.1",
|
|
28
|
-
"@nocios/crudify-browser": "^1.0.
|
|
28
|
+
"@nocios/crudify-browser": "^1.0.84",
|
|
29
29
|
"crypto-js": "^4.2.0",
|
|
30
30
|
"i18next-browser-languagedetector": "^8.1.0",
|
|
31
31
|
"i18next-http-backend": "^3.0.2",
|
package/tsup.config.ts
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
import { defineConfig } from
|
|
1
|
+
import { defineConfig } from "tsup";
|
|
2
2
|
|
|
3
3
|
export default defineConfig({
|
|
4
|
-
entry: [
|
|
5
|
-
format: [
|
|
4
|
+
entry: ["src/index.ts"],
|
|
5
|
+
format: ["cjs", "esm"],
|
|
6
6
|
dts: true,
|
|
7
7
|
clean: true,
|
|
8
|
-
external: [
|
|
9
|
-
})
|
|
8
|
+
external: ["react", "react-dom", "@mui/material", "@mui/icons-material"],
|
|
9
|
+
});
|
|
@@ -1,281 +0,0 @@
|
|
|
1
|
-
# Análisis: Implementaciones Posibles para Manejo de Usuario Logueado
|
|
2
|
-
|
|
3
|
-
## Contexto Actual
|
|
4
|
-
|
|
5
|
-
Actualmente en `crudia-ui`:
|
|
6
|
-
- **DataProvider**: Maneja token, usuario (del JWT), inicialización de crudify, interceptores de expiración
|
|
7
|
-
- **Token**: Se guarda en secureSessionStorage y se extrae info básica del JWT (email, exp, etc.)
|
|
8
|
-
- **Datos completos del usuario**: Se obtienen con `findUserByEmail()` usando `readItems("users", {filter: {email}})`
|
|
9
|
-
- **Redirección automática**: Al expirar token, redirige a `/login`
|
|
10
|
-
|
|
11
|
-
## Opciones de Implementación
|
|
12
|
-
|
|
13
|
-
### ✅ **OPCIÓN 1: Provider Completo en la Librería (Recomendada)**
|
|
14
|
-
|
|
15
|
-
**Descripción:**
|
|
16
|
-
Crear un `CrudifyAuthProvider` en npm-crudify-ui que maneje completamente la autenticación y datos del usuario.
|
|
17
|
-
|
|
18
|
-
```tsx
|
|
19
|
-
// En npm-crudify-ui
|
|
20
|
-
export const CrudifyAuthProvider = ({
|
|
21
|
-
children,
|
|
22
|
-
onTokenExpired,
|
|
23
|
-
onError,
|
|
24
|
-
config
|
|
25
|
-
}) => {
|
|
26
|
-
// Maneja: token, usuario completo, crudify init, interceptores
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
// En crudia-ui
|
|
30
|
-
<CrudifyAuthProvider
|
|
31
|
-
onTokenExpired={() => navigate("/login")}
|
|
32
|
-
config={appConfig}
|
|
33
|
-
>
|
|
34
|
-
<App />
|
|
35
|
-
</CrudifyAuthProvider>
|
|
36
|
-
```
|
|
37
|
-
|
|
38
|
-
**✅ PROS:**
|
|
39
|
-
- **Reutilización máxima**: Todas las apps usan la misma lógica de auth
|
|
40
|
-
- **Mantenimiento centralizado**: Un solo lugar para bugs de auth
|
|
41
|
-
- **Consistencia**: Mismo comportamiento en todas las apps
|
|
42
|
-
- **Simplicidad**: Apps solo necesitan proveer callbacks
|
|
43
|
-
- **Testing**: Más fácil testear lógica de auth de forma aislada
|
|
44
|
-
- **Menos código**: Apps no duplican lógica de autenticación
|
|
45
|
-
|
|
46
|
-
**❌ CONTRAS:**
|
|
47
|
-
- **Migración inicial**: Hay que actualizar DataProvider existente
|
|
48
|
-
- **Flexibilidad**: Menos control granular en cada app
|
|
49
|
-
- **Dependencias**: Apps dependen más de la librería
|
|
50
|
-
|
|
51
|
-
**📋 IMPLEMENTACIÓN:**
|
|
52
|
-
```
|
|
53
|
-
npm-crudify-ui/
|
|
54
|
-
├── src/
|
|
55
|
-
│ ├── components/
|
|
56
|
-
│ │ ├── CrudifyLogin/
|
|
57
|
-
│ │ └── CrudifyAuthProvider/ # NUEVO
|
|
58
|
-
│ │ ├── index.tsx
|
|
59
|
-
│ │ ├── types.ts
|
|
60
|
-
│ │ ├── hooks/
|
|
61
|
-
│ │ │ ├── useAuth.ts
|
|
62
|
-
│ │ │ ├── useUserProfile.ts
|
|
63
|
-
│ │ │ └── useCrudifyInit.ts
|
|
64
|
-
│ │ └── utils/
|
|
65
|
-
│ │ ├── jwtUtils.ts
|
|
66
|
-
│ │ └── tokenInterceptors.ts
|
|
67
|
-
```
|
|
68
|
-
|
|
69
|
-
---
|
|
70
|
-
|
|
71
|
-
### 🔄 **OPCIÓN 2: Hook Compartido**
|
|
72
|
-
|
|
73
|
-
**Descripción:**
|
|
74
|
-
Crear hooks reutilizables pero mantener el Provider en cada app.
|
|
75
|
-
|
|
76
|
-
```tsx
|
|
77
|
-
// En npm-crudify-ui
|
|
78
|
-
export const useAuthToken = (config) => { /* lógica token */ }
|
|
79
|
-
export const useUserProfile = (userEmail) => { /* lógica usuario */ }
|
|
80
|
-
export const useCrudifyInit = (config, token) => { /* lógica init */ }
|
|
81
|
-
|
|
82
|
-
// En crudia-ui (DataProvider se mantiene)
|
|
83
|
-
const DataProvider = ({ children }) => {
|
|
84
|
-
const { token, setToken } = useAuthToken(config)
|
|
85
|
-
const { user } = useUserProfile(userEmail)
|
|
86
|
-
const { isInitialized } = useCrudifyInit(config, token)
|
|
87
|
-
// ...
|
|
88
|
-
}
|
|
89
|
-
```
|
|
90
|
-
|
|
91
|
-
**✅ PROS:**
|
|
92
|
-
- **Flexibilidad**: Cada app controla su Provider
|
|
93
|
-
- **Migración gradual**: Se puede migrar por partes
|
|
94
|
-
- **Personalización**: Apps pueden customizar comportamiento
|
|
95
|
-
- **Compatibilidad**: Fácil mantener el DataProvider actual
|
|
96
|
-
|
|
97
|
-
**❌ CONTRAS:**
|
|
98
|
-
- **Duplicación de código**: Cada app necesita su Provider
|
|
99
|
-
- **Inconsistencias**: Apps pueden implementar diferente
|
|
100
|
-
- **Mantenimiento**: Hay que actualizar múltiples lugares
|
|
101
|
-
- **Complejidad**: Apps necesitan conocer la implementación
|
|
102
|
-
|
|
103
|
-
---
|
|
104
|
-
|
|
105
|
-
### 🚫 **OPCIÓN 3: Solo Utilidades (No recomendada)**
|
|
106
|
-
|
|
107
|
-
**Descripción:**
|
|
108
|
-
Mover solo funciones utilitarias a la librería.
|
|
109
|
-
|
|
110
|
-
```tsx
|
|
111
|
-
// En npm-crudify-ui
|
|
112
|
-
export const parseJWT = (token) => { /* ... */ }
|
|
113
|
-
export const setupTokenInterceptor = (onExpired) => { /* ... */ }
|
|
114
|
-
export const findUserByEmail = (email) => { /* ... */ }
|
|
115
|
-
```
|
|
116
|
-
|
|
117
|
-
**✅ PROS:**
|
|
118
|
-
- **Migración mínima**: Casi no cambia el código existente
|
|
119
|
-
- **Control total**: Apps mantienen control completo
|
|
120
|
-
|
|
121
|
-
**❌ CONTRAS:**
|
|
122
|
-
- **Poca reutilización**: No se simplifica realmente
|
|
123
|
-
- **Duplicación**: Lógica principal sigue duplicada
|
|
124
|
-
- **Mantenimiento**: Sigue siendo complejo mantener
|
|
125
|
-
- **Valor limitado**: No justifica el esfuerzo
|
|
126
|
-
|
|
127
|
-
---
|
|
128
|
-
|
|
129
|
-
### ⚖️ **OPCIÓN 4: Híbrida (Provider + Hooks)**
|
|
130
|
-
|
|
131
|
-
**Descripción:**
|
|
132
|
-
Provider principal + hooks especializados para casos específicos.
|
|
133
|
-
|
|
134
|
-
```tsx
|
|
135
|
-
// Provider principal
|
|
136
|
-
<CrudifyAuthProvider>
|
|
137
|
-
<App />
|
|
138
|
-
</CrudifyAuthProvider>
|
|
139
|
-
|
|
140
|
-
// Hooks adicionales para casos específicos
|
|
141
|
-
const { refreshUserProfile } = useUserProfile()
|
|
142
|
-
const { extendToken } = useTokenManager()
|
|
143
|
-
```
|
|
144
|
-
|
|
145
|
-
**✅ PROS:**
|
|
146
|
-
- **Lo mejor de ambos**: Provider simple + hooks flexibles
|
|
147
|
-
- **Escalabilidad**: Fácil agregar funcionalidades
|
|
148
|
-
- **Gradual**: Se puede implementar por etapas
|
|
149
|
-
|
|
150
|
-
**❌ CONTRAS:**
|
|
151
|
-
- **Complejidad inicial**: Más código para implementar
|
|
152
|
-
- **Decisiones de diseño**: Qué va en Provider vs hooks
|
|
153
|
-
|
|
154
|
-
---
|
|
155
|
-
|
|
156
|
-
## 🎯 **RECOMENDACIÓN: OPCIÓN 1**
|
|
157
|
-
|
|
158
|
-
### **Por qué la Opción 1 es la mejor:**
|
|
159
|
-
|
|
160
|
-
1. **Alineada con el objetivo**: Librería reutilizable y fácil mantenimiento
|
|
161
|
-
2. **Soluciona el problema real**: Evita duplicar DataProvider en cada app
|
|
162
|
-
3. **Escalable**: Nuevas apps solo importan el provider
|
|
163
|
-
4. **Mantenible**: Un solo lugar para arreglar bugs de auth
|
|
164
|
-
5. **React best practices**: Providers son el patrón estándar para estado global
|
|
165
|
-
|
|
166
|
-
### **Plan de Migración:**
|
|
167
|
-
|
|
168
|
-
#### **Fase 1: Crear CrudifyAuthProvider en la librería**
|
|
169
|
-
```tsx
|
|
170
|
-
// npm-crudify-ui/src/components/CrudifyAuthProvider/
|
|
171
|
-
export interface CrudifyAuthConfig {
|
|
172
|
-
publicApiKey?: string
|
|
173
|
-
env?: 'dev' | 'stg' | 'prod'
|
|
174
|
-
onTokenExpired?: () => void
|
|
175
|
-
onAuthError?: (error: string) => void
|
|
176
|
-
autoLoginWithSubdomain?: boolean
|
|
177
|
-
}
|
|
178
|
-
|
|
179
|
-
export const CrudifyAuthProvider: React.FC<{
|
|
180
|
-
children: React.ReactNode
|
|
181
|
-
config: CrudifyAuthConfig
|
|
182
|
-
}>
|
|
183
|
-
```
|
|
184
|
-
|
|
185
|
-
#### **Fase 2: Migrar crudia-ui**
|
|
186
|
-
```tsx
|
|
187
|
-
// Reemplazar DataProvider con:
|
|
188
|
-
<CrudifyAuthProvider
|
|
189
|
-
config={{
|
|
190
|
-
onTokenExpired: () => navigate("/login"),
|
|
191
|
-
onAuthError: (error) => showNotification(error, "error")
|
|
192
|
-
}}
|
|
193
|
-
>
|
|
194
|
-
{children}
|
|
195
|
-
</CrudifyAuthProvider>
|
|
196
|
-
```
|
|
197
|
-
|
|
198
|
-
#### **Fase 3: Otras apps usan el mismo provider**
|
|
199
|
-
```tsx
|
|
200
|
-
// En nuevas apps:
|
|
201
|
-
<CrudifyAuthProvider config={appSpecificConfig}>
|
|
202
|
-
<MyApp />
|
|
203
|
-
</CrudifyAuthProvider>
|
|
204
|
-
```
|
|
205
|
-
|
|
206
|
-
### **Beneficios a Largo Plazo:**
|
|
207
|
-
|
|
208
|
-
- 🚀 **Nuevas apps**: Se implementan en minutos, no horas
|
|
209
|
-
- 🔧 **Bugs de auth**: Se arreglan una vez, se aplican a todas las apps
|
|
210
|
-
- 📚 **Consistencia**: Misma UX de autenticación en todo el ecosistema
|
|
211
|
-
- 🧪 **Testing**: Lógica de auth se puede testear de forma aislada
|
|
212
|
-
- 📖 **Documentación**: Un solo provider para documentar
|
|
213
|
-
|
|
214
|
-
---
|
|
215
|
-
|
|
216
|
-
## 🚀 **Implementación Sugerida**
|
|
217
|
-
|
|
218
|
-
### **Estructura del CrudifyAuthProvider:**
|
|
219
|
-
|
|
220
|
-
```tsx
|
|
221
|
-
interface CrudifyAuthContextValue {
|
|
222
|
-
// Estado básico
|
|
223
|
-
token: string | null
|
|
224
|
-
user: any | null
|
|
225
|
-
userProfile: any | null
|
|
226
|
-
isAuthenticated: boolean
|
|
227
|
-
isInitialized: boolean
|
|
228
|
-
|
|
229
|
-
// Métodos
|
|
230
|
-
login: (username: string, password: string) => Promise<LoginResponse>
|
|
231
|
-
loginWithSubdomain: (username: string, password: string, subdomain: string) => Promise<LoginResponse>
|
|
232
|
-
logout: () => void
|
|
233
|
-
refreshUserProfile: () => Promise<void>
|
|
234
|
-
|
|
235
|
-
// Estados de carga
|
|
236
|
-
loginLoading: boolean
|
|
237
|
-
profileLoading: boolean
|
|
238
|
-
initLoading: boolean
|
|
239
|
-
|
|
240
|
-
// Errores
|
|
241
|
-
authError: string | null
|
|
242
|
-
profileError: string | null
|
|
243
|
-
}
|
|
244
|
-
```
|
|
245
|
-
|
|
246
|
-
### **Uso en las apps:**
|
|
247
|
-
|
|
248
|
-
```tsx
|
|
249
|
-
// crudia-ui/src/App.tsx
|
|
250
|
-
<CrudifyAuthProvider
|
|
251
|
-
config={{
|
|
252
|
-
publicApiKey: appConfig.publicApiKey,
|
|
253
|
-
env: appConfig.env,
|
|
254
|
-
onTokenExpired: () => navigate("/login"),
|
|
255
|
-
onAuthError: (error) => showNotification(error, "error"),
|
|
256
|
-
autoReadFromCookies: true
|
|
257
|
-
}}
|
|
258
|
-
>
|
|
259
|
-
<Router>
|
|
260
|
-
<Routes>
|
|
261
|
-
<Route path="/login" element={<CrudifyLogin />} />
|
|
262
|
-
<Route path="/*" element={<ProtectedRoutes />} />
|
|
263
|
-
</Routes>
|
|
264
|
-
</Router>
|
|
265
|
-
</CrudifyAuthProvider>
|
|
266
|
-
```
|
|
267
|
-
|
|
268
|
-
### **Hook de uso:**
|
|
269
|
-
|
|
270
|
-
```tsx
|
|
271
|
-
// En cualquier componente
|
|
272
|
-
const { user, userProfile, logout, refreshUserProfile } = useAuth()
|
|
273
|
-
|
|
274
|
-
// Usuario básico (del JWT)
|
|
275
|
-
console.log(user.email, user.exp)
|
|
276
|
-
|
|
277
|
-
// Usuario completo (de la DB)
|
|
278
|
-
console.log(userProfile.firstName, userProfile.preferences)
|
|
279
|
-
```
|
|
280
|
-
|
|
281
|
-
¿Te parece bien esta propuesta? ¿Quieres que proceda con la implementación de la Opción 1?
|