@nocios/crudify-ui 4.0.8 → 4.0.9
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/GlobalNotificationProvider-C3iWgM1z.d.mts +35 -0
- package/dist/GlobalNotificationProvider-C3iWgM1z.d.ts +35 -0
- package/dist/api-Djqihi4n.d.mts +82 -0
- package/dist/api-Djqihi4n.d.ts +82 -0
- package/dist/chunk-6EBMA4HZ.js +1 -0
- package/dist/chunk-BJ6PIVZR.mjs +1 -0
- package/dist/chunk-D3OQLCQW.mjs +1 -0
- package/dist/chunk-FBT222LR.mjs +1 -0
- package/dist/chunk-JZVX4CN3.mjs +1 -0
- package/dist/chunk-NNY4A73V.js +1 -0
- package/dist/chunk-OGQX5FM5.js +1 -0
- package/dist/chunk-T2CPA46I.mjs +1 -0
- package/dist/chunk-TAEX76JC.js +1 -0
- package/dist/chunk-YICJIX2K.js +1 -0
- package/dist/chunk-YIIUEOXC.js +1 -0
- package/dist/chunk-YS3C7YG5.mjs +1 -0
- package/dist/components.d.mts +4 -0
- package/dist/components.d.ts +4 -0
- package/dist/components.js +1 -0
- package/dist/components.mjs +1 -0
- package/dist/hooks.d.mts +5 -0
- package/dist/hooks.d.ts +5 -0
- package/dist/hooks.js +1 -0
- package/dist/hooks.mjs +1 -0
- package/dist/index-CY6Qkw3q.d.mts +78 -0
- package/dist/index-CjC4JwH9.d.ts +427 -0
- package/dist/index-DxFMT2hN.d.ts +78 -0
- package/dist/index-FiBZQvXG.d.mts +427 -0
- package/dist/index.d.mts +8 -732
- package/dist/index.d.ts +8 -732
- package/dist/index.js +1 -4906
- package/dist/index.mjs +1 -4890
- package/dist/utils.d.mts +124 -0
- package/dist/utils.d.ts +124 -0
- package/dist/utils.js +1 -0
- package/dist/utils.mjs +1 -0
- package/package.json +24 -14
package/dist/index.mjs
CHANGED
|
@@ -1,4890 +1 @@
|
|
|
1
|
-
|
|
2
|
-
import { default as default2 } from "@nocios/crudify-browser";
|
|
3
|
-
export * from "@nocios/crudify-browser";
|
|
4
|
-
|
|
5
|
-
// src/components/CrudifyLogin/index.tsx
|
|
6
|
-
import { Box as Box7, Typography as Typography6 } from "@mui/material";
|
|
7
|
-
|
|
8
|
-
// src/components/CrudifyLogin/context/I18nProvider.tsx
|
|
9
|
-
import { createContext, useContext, useMemo } from "react";
|
|
10
|
-
|
|
11
|
-
// src/components/CrudifyLogin/hooks/useTranslationsFromUrl.ts
|
|
12
|
-
import { useState, useEffect } from "react";
|
|
13
|
-
var useTranslationsFromUrl = (url, providedTranslations) => {
|
|
14
|
-
const [translations, setTranslations] = useState({});
|
|
15
|
-
const [loading, setLoading] = useState(false);
|
|
16
|
-
const [error, setError] = useState(null);
|
|
17
|
-
useEffect(() => {
|
|
18
|
-
console.log("\u{1F527} [I18nProvider] Hybrid translation loading:", {
|
|
19
|
-
hasProvidedTranslations: !!providedTranslations && Object.keys(providedTranslations).length > 0,
|
|
20
|
-
hasUrl: !!url,
|
|
21
|
-
providedKeys: providedTranslations ? Object.keys(providedTranslations).length : 0
|
|
22
|
-
});
|
|
23
|
-
if (providedTranslations && Object.keys(providedTranslations).length > 0) {
|
|
24
|
-
console.log("\u2705 [I18nProvider] Using provided translations (highest priority)");
|
|
25
|
-
setTranslations(providedTranslations);
|
|
26
|
-
setLoading(false);
|
|
27
|
-
setError(null);
|
|
28
|
-
return;
|
|
29
|
-
}
|
|
30
|
-
if (!url) {
|
|
31
|
-
console.log("\u26A0\uFE0F [I18nProvider] No translations provided, using empty object (keys will show as-is)");
|
|
32
|
-
setTranslations({});
|
|
33
|
-
setLoading(false);
|
|
34
|
-
setError(null);
|
|
35
|
-
return;
|
|
36
|
-
}
|
|
37
|
-
console.log("\u{1F310} [I18nProvider] Loading translations from URL:", url);
|
|
38
|
-
let isCancelled = false;
|
|
39
|
-
setLoading(true);
|
|
40
|
-
setError(null);
|
|
41
|
-
fetch(url).then((response) => {
|
|
42
|
-
if (!response.ok) {
|
|
43
|
-
throw new Error(`Failed to load translations: ${response.status}`);
|
|
44
|
-
}
|
|
45
|
-
return response.json();
|
|
46
|
-
}).then((data) => {
|
|
47
|
-
if (!isCancelled) {
|
|
48
|
-
console.log("\u2705 [I18nProvider] Translations loaded successfully from URL:", {
|
|
49
|
-
url,
|
|
50
|
-
keysLoaded: Object.keys(data).length
|
|
51
|
-
});
|
|
52
|
-
setTranslations(data);
|
|
53
|
-
setLoading(false);
|
|
54
|
-
}
|
|
55
|
-
}).catch((err) => {
|
|
56
|
-
if (!isCancelled) {
|
|
57
|
-
console.error("\u274C [I18nProvider] Failed to load translations from URL:", url, err);
|
|
58
|
-
setError(err.message);
|
|
59
|
-
console.log("\u{1F504} [I18nProvider] Falling back to empty translations (keys will show as-is)");
|
|
60
|
-
setTranslations({});
|
|
61
|
-
setLoading(false);
|
|
62
|
-
}
|
|
63
|
-
});
|
|
64
|
-
return () => {
|
|
65
|
-
isCancelled = true;
|
|
66
|
-
};
|
|
67
|
-
}, [url, providedTranslations]);
|
|
68
|
-
return {
|
|
69
|
-
translations,
|
|
70
|
-
loading,
|
|
71
|
-
error
|
|
72
|
-
};
|
|
73
|
-
};
|
|
74
|
-
|
|
75
|
-
// src/components/CrudifyLogin/context/I18nProvider.tsx
|
|
76
|
-
import { jsx } from "react/jsx-runtime";
|
|
77
|
-
var I18nContext = createContext(null);
|
|
78
|
-
var getNestedValue = (obj, path) => {
|
|
79
|
-
if (obj && obj[path]) return obj[path];
|
|
80
|
-
return path.split(".").reduce((current, key) => {
|
|
81
|
-
return current && typeof current === "object" ? current[key] : void 0;
|
|
82
|
-
}, obj);
|
|
83
|
-
};
|
|
84
|
-
var I18nProvider = ({ children, translations, translationsUrl, language = "en" }) => {
|
|
85
|
-
const { translations: loadedTranslations, loading } = useTranslationsFromUrl(translationsUrl, translations);
|
|
86
|
-
const t = useMemo(() => {
|
|
87
|
-
return (key, variables) => {
|
|
88
|
-
let value = getNestedValue(loadedTranslations, key);
|
|
89
|
-
if (value === void 0 || value === null) {
|
|
90
|
-
console.log(`\u{1F50D} [I18nProvider] Translation not found for key: "${key}" - showing key as-is`);
|
|
91
|
-
value = key;
|
|
92
|
-
}
|
|
93
|
-
if (variables && typeof value === "string") {
|
|
94
|
-
Object.entries(variables).forEach(([varKey, varValue]) => {
|
|
95
|
-
value = value.replace(new RegExp(`{{${varKey}}}`, "g"), varValue);
|
|
96
|
-
});
|
|
97
|
-
}
|
|
98
|
-
return typeof value === "string" ? value : key;
|
|
99
|
-
};
|
|
100
|
-
}, [loadedTranslations]);
|
|
101
|
-
const contextValue = useMemo(
|
|
102
|
-
() => ({
|
|
103
|
-
t,
|
|
104
|
-
language
|
|
105
|
-
}),
|
|
106
|
-
[t, language]
|
|
107
|
-
);
|
|
108
|
-
if (loading) return /* @__PURE__ */ jsx("div", { children: "Loading translations..." });
|
|
109
|
-
return /* @__PURE__ */ jsx(I18nContext.Provider, { value: contextValue, children });
|
|
110
|
-
};
|
|
111
|
-
var useTranslation = () => {
|
|
112
|
-
const context = useContext(I18nContext);
|
|
113
|
-
if (!context) {
|
|
114
|
-
throw new Error("useTranslation must be used within I18nProvider");
|
|
115
|
-
}
|
|
116
|
-
return context;
|
|
117
|
-
};
|
|
118
|
-
|
|
119
|
-
// src/components/CrudifyLogin/context/CrudifyProvider.tsx
|
|
120
|
-
import { createContext as createContext2, useContext as useContext2, useEffect as useEffect2, useState as useState2 } from "react";
|
|
121
|
-
import crudify from "@nocios/crudify-browser";
|
|
122
|
-
import { jsx as jsx2 } from "react/jsx-runtime";
|
|
123
|
-
var CrudifyContext = createContext2(void 0);
|
|
124
|
-
var CrudifyProvider = ({ config, children }) => {
|
|
125
|
-
const [isLoading, setIsLoading] = useState2(true);
|
|
126
|
-
const [error, setError] = useState2(null);
|
|
127
|
-
const [isInitialized, setIsInitialized] = useState2(false);
|
|
128
|
-
const [initializationKey, setInitializationKey] = useState2("");
|
|
129
|
-
console.log("\u{1F50D} CrudifyProvider - Received config:", config);
|
|
130
|
-
useEffect2(() => {
|
|
131
|
-
console.log("\u{1F50D} CrudifyProvider - useEffect - config.publicApiKey:", config.publicApiKey);
|
|
132
|
-
console.log("\u{1F50D} CrudifyProvider - useEffect - full config:", config);
|
|
133
|
-
if (!config.publicApiKey) {
|
|
134
|
-
console.log("\u274C CrudifyProvider - No publicApiKey provided, setting error");
|
|
135
|
-
setError("No publicApiKey provided");
|
|
136
|
-
setIsLoading(false);
|
|
137
|
-
setIsInitialized(false);
|
|
138
|
-
return;
|
|
139
|
-
}
|
|
140
|
-
console.log("\u2705 CrudifyProvider - publicApiKey found, proceeding with initialization");
|
|
141
|
-
const currentKey = `${config.publicApiKey}-${config.env}`;
|
|
142
|
-
if (currentKey === initializationKey && isInitialized) {
|
|
143
|
-
setIsLoading(false);
|
|
144
|
-
return;
|
|
145
|
-
}
|
|
146
|
-
const initializeCrudify = async () => {
|
|
147
|
-
setIsLoading(true);
|
|
148
|
-
setError(null);
|
|
149
|
-
setIsInitialized(false);
|
|
150
|
-
try {
|
|
151
|
-
crudify.config(config.env || "prod");
|
|
152
|
-
await crudify.init(config.publicApiKey, "none");
|
|
153
|
-
if (typeof crudify.transaction === "function" && typeof crudify.login === "function") {
|
|
154
|
-
setIsInitialized(true);
|
|
155
|
-
setInitializationKey(currentKey);
|
|
156
|
-
} else {
|
|
157
|
-
throw new Error("Crudify methods not properly initialized");
|
|
158
|
-
}
|
|
159
|
-
} catch (err) {
|
|
160
|
-
const errorMessage = err instanceof Error ? err.message : "Failed to initialize Crudify";
|
|
161
|
-
setError(errorMessage);
|
|
162
|
-
setIsInitialized(false);
|
|
163
|
-
} finally {
|
|
164
|
-
setIsLoading(false);
|
|
165
|
-
}
|
|
166
|
-
};
|
|
167
|
-
initializeCrudify();
|
|
168
|
-
}, [config.publicApiKey, config.env, initializationKey, isInitialized]);
|
|
169
|
-
const value = {
|
|
170
|
-
crudify: isInitialized ? crudify : null,
|
|
171
|
-
isLoading,
|
|
172
|
-
error,
|
|
173
|
-
isInitialized
|
|
174
|
-
};
|
|
175
|
-
return /* @__PURE__ */ jsx2(CrudifyContext.Provider, { value, children });
|
|
176
|
-
};
|
|
177
|
-
var useCrudify = () => {
|
|
178
|
-
const context = useContext2(CrudifyContext);
|
|
179
|
-
if (context === void 0) {
|
|
180
|
-
throw new Error("useCrudify must be used within a CrudifyProvider");
|
|
181
|
-
}
|
|
182
|
-
return context;
|
|
183
|
-
};
|
|
184
|
-
|
|
185
|
-
// src/components/CrudifyLogin/context/LoginStateProvider.tsx
|
|
186
|
-
import { createContext as createContext3, useContext as useContext3, useReducer, useEffect as useEffect3 } from "react";
|
|
187
|
-
|
|
188
|
-
// src/components/CrudifyLogin/utils/cookies.ts
|
|
189
|
-
var getCookie = (name) => {
|
|
190
|
-
const match = document.cookie.match(new RegExp("(^|;)\\s*" + name + "=([^;]+)"));
|
|
191
|
-
return match ? match[2] : null;
|
|
192
|
-
};
|
|
193
|
-
|
|
194
|
-
// src/components/CrudifyLogin/context/LoginStateProvider.tsx
|
|
195
|
-
import { jsx as jsx3 } from "react/jsx-runtime";
|
|
196
|
-
var initialState = {
|
|
197
|
-
currentScreen: "login",
|
|
198
|
-
searchParams: {},
|
|
199
|
-
formData: {
|
|
200
|
-
username: "",
|
|
201
|
-
password: "",
|
|
202
|
-
email: "",
|
|
203
|
-
code: "",
|
|
204
|
-
newPassword: "",
|
|
205
|
-
confirmPassword: ""
|
|
206
|
-
},
|
|
207
|
-
loading: false,
|
|
208
|
-
errors: {
|
|
209
|
-
global: []
|
|
210
|
-
},
|
|
211
|
-
emailSent: false,
|
|
212
|
-
codeAlreadyExists: false,
|
|
213
|
-
codeValidated: false,
|
|
214
|
-
fromCodeVerification: false,
|
|
215
|
-
config: {}
|
|
216
|
-
};
|
|
217
|
-
function loginStateReducer(state, action) {
|
|
218
|
-
switch (action.type) {
|
|
219
|
-
case "SET_SCREEN":
|
|
220
|
-
const newState = {
|
|
221
|
-
...state,
|
|
222
|
-
currentScreen: action.payload.screen,
|
|
223
|
-
searchParams: action.payload.params || state.searchParams,
|
|
224
|
-
// Clear form errors when changing screens
|
|
225
|
-
errors: { global: [] }
|
|
226
|
-
};
|
|
227
|
-
const newUrl = new URLSearchParams(newState.searchParams);
|
|
228
|
-
const newPath = newUrl.toString() ? `?${newUrl.toString()}` : window.location.pathname;
|
|
229
|
-
try {
|
|
230
|
-
window.history.replaceState({}, "", newPath);
|
|
231
|
-
} catch (error) {
|
|
232
|
-
}
|
|
233
|
-
return newState;
|
|
234
|
-
case "SET_SEARCH_PARAMS":
|
|
235
|
-
return {
|
|
236
|
-
...state,
|
|
237
|
-
searchParams: action.payload
|
|
238
|
-
};
|
|
239
|
-
case "UPDATE_FORM_DATA":
|
|
240
|
-
return {
|
|
241
|
-
...state,
|
|
242
|
-
formData: {
|
|
243
|
-
...state.formData,
|
|
244
|
-
...action.payload
|
|
245
|
-
},
|
|
246
|
-
// Clear related errors when updating form data
|
|
247
|
-
errors: {
|
|
248
|
-
...state.errors,
|
|
249
|
-
...Object.keys(action.payload).reduce(
|
|
250
|
-
(acc, key) => ({
|
|
251
|
-
...acc,
|
|
252
|
-
[key]: void 0
|
|
253
|
-
}),
|
|
254
|
-
{}
|
|
255
|
-
)
|
|
256
|
-
}
|
|
257
|
-
};
|
|
258
|
-
case "SET_LOADING":
|
|
259
|
-
return {
|
|
260
|
-
...state,
|
|
261
|
-
loading: action.payload
|
|
262
|
-
};
|
|
263
|
-
case "SET_ERRORS":
|
|
264
|
-
return {
|
|
265
|
-
...state,
|
|
266
|
-
errors: {
|
|
267
|
-
...state.errors,
|
|
268
|
-
...action.payload
|
|
269
|
-
}
|
|
270
|
-
};
|
|
271
|
-
case "CLEAR_ERRORS":
|
|
272
|
-
return {
|
|
273
|
-
...state,
|
|
274
|
-
errors: { global: [] }
|
|
275
|
-
};
|
|
276
|
-
case "SET_EMAIL_SENT":
|
|
277
|
-
return {
|
|
278
|
-
...state,
|
|
279
|
-
emailSent: action.payload
|
|
280
|
-
};
|
|
281
|
-
case "SET_CODE_ALREADY_EXISTS":
|
|
282
|
-
return {
|
|
283
|
-
...state,
|
|
284
|
-
codeAlreadyExists: action.payload
|
|
285
|
-
};
|
|
286
|
-
case "SET_CODE_VALIDATED":
|
|
287
|
-
return {
|
|
288
|
-
...state,
|
|
289
|
-
codeValidated: action.payload
|
|
290
|
-
};
|
|
291
|
-
case "SET_FROM_CODE_VERIFICATION":
|
|
292
|
-
return {
|
|
293
|
-
...state,
|
|
294
|
-
fromCodeVerification: action.payload
|
|
295
|
-
};
|
|
296
|
-
case "RESET_FORM":
|
|
297
|
-
return {
|
|
298
|
-
...state,
|
|
299
|
-
formData: initialState.formData,
|
|
300
|
-
errors: { global: [] },
|
|
301
|
-
loading: false,
|
|
302
|
-
emailSent: false,
|
|
303
|
-
codeAlreadyExists: false,
|
|
304
|
-
codeValidated: false,
|
|
305
|
-
fromCodeVerification: false
|
|
306
|
-
};
|
|
307
|
-
case "INIT_CONFIG":
|
|
308
|
-
return {
|
|
309
|
-
...state,
|
|
310
|
-
config: action.payload
|
|
311
|
-
};
|
|
312
|
-
default:
|
|
313
|
-
return state;
|
|
314
|
-
}
|
|
315
|
-
}
|
|
316
|
-
var LoginStateContext = createContext3(void 0);
|
|
317
|
-
var LoginStateProvider = ({
|
|
318
|
-
children,
|
|
319
|
-
initialScreen = "login",
|
|
320
|
-
config: providedConfig,
|
|
321
|
-
autoReadFromCookies = true
|
|
322
|
-
}) => {
|
|
323
|
-
const [state, dispatch] = useReducer(loginStateReducer, {
|
|
324
|
-
...initialState,
|
|
325
|
-
currentScreen: initialScreen
|
|
326
|
-
});
|
|
327
|
-
useEffect3(() => {
|
|
328
|
-
const buildFinalConfig = () => {
|
|
329
|
-
let cookieConfig = {};
|
|
330
|
-
if (autoReadFromCookies) {
|
|
331
|
-
try {
|
|
332
|
-
const encodedLogo = getCookie("logo");
|
|
333
|
-
if (encodedLogo) {
|
|
334
|
-
const decodedLogo = decodeURIComponent(encodedLogo);
|
|
335
|
-
if (decodedLogo.startsWith("http")) {
|
|
336
|
-
cookieConfig.logo = decodedLogo;
|
|
337
|
-
}
|
|
338
|
-
}
|
|
339
|
-
} catch (e) {
|
|
340
|
-
console.error("Error reading configuration from cookies:", e);
|
|
341
|
-
}
|
|
342
|
-
}
|
|
343
|
-
return {
|
|
344
|
-
publicApiKey: providedConfig?.publicApiKey,
|
|
345
|
-
env: providedConfig?.env,
|
|
346
|
-
appName: providedConfig?.appName,
|
|
347
|
-
logo: providedConfig?.logo || cookieConfig.logo,
|
|
348
|
-
loginActions: providedConfig?.loginActions
|
|
349
|
-
};
|
|
350
|
-
};
|
|
351
|
-
dispatch({ type: "INIT_CONFIG", payload: buildFinalConfig() });
|
|
352
|
-
}, [providedConfig, autoReadFromCookies]);
|
|
353
|
-
useEffect3(() => {
|
|
354
|
-
const urlParams = new URLSearchParams(window.location.search);
|
|
355
|
-
const paramsObject = {};
|
|
356
|
-
urlParams.forEach((value2, key) => {
|
|
357
|
-
paramsObject[key] = value2;
|
|
358
|
-
});
|
|
359
|
-
if (Object.keys(paramsObject).length > 0) {
|
|
360
|
-
dispatch({ type: "SET_SEARCH_PARAMS", payload: paramsObject });
|
|
361
|
-
}
|
|
362
|
-
if (initialScreen === "checkCode" && paramsObject.email) {
|
|
363
|
-
dispatch({
|
|
364
|
-
type: "UPDATE_FORM_DATA",
|
|
365
|
-
payload: { email: paramsObject.email, code: paramsObject.code || "" }
|
|
366
|
-
});
|
|
367
|
-
}
|
|
368
|
-
if (initialScreen === "resetPassword" && paramsObject.link) {
|
|
369
|
-
dispatch({ type: "SET_SEARCH_PARAMS", payload: paramsObject });
|
|
370
|
-
}
|
|
371
|
-
}, [initialScreen]);
|
|
372
|
-
const setScreen = (screen2, params) => {
|
|
373
|
-
dispatch({ type: "SET_SCREEN", payload: { screen: screen2, params } });
|
|
374
|
-
};
|
|
375
|
-
const updateFormData = (data) => {
|
|
376
|
-
dispatch({ type: "UPDATE_FORM_DATA", payload: data });
|
|
377
|
-
};
|
|
378
|
-
const setFieldError = (field, error) => {
|
|
379
|
-
dispatch({ type: "SET_ERRORS", payload: { [field]: error } });
|
|
380
|
-
};
|
|
381
|
-
const clearErrors = () => {
|
|
382
|
-
dispatch({ type: "CLEAR_ERRORS" });
|
|
383
|
-
};
|
|
384
|
-
const setLoading = (loading) => {
|
|
385
|
-
dispatch({ type: "SET_LOADING", payload: loading });
|
|
386
|
-
};
|
|
387
|
-
const value = {
|
|
388
|
-
state,
|
|
389
|
-
dispatch,
|
|
390
|
-
setScreen,
|
|
391
|
-
updateFormData,
|
|
392
|
-
setFieldError,
|
|
393
|
-
clearErrors,
|
|
394
|
-
setLoading
|
|
395
|
-
};
|
|
396
|
-
return /* @__PURE__ */ jsx3(LoginStateContext.Provider, { value, children });
|
|
397
|
-
};
|
|
398
|
-
var useLoginState = () => {
|
|
399
|
-
const context = useContext3(LoginStateContext);
|
|
400
|
-
if (context === void 0) {
|
|
401
|
-
throw new Error("useLoginState must be used within a LoginStateProvider");
|
|
402
|
-
}
|
|
403
|
-
return context;
|
|
404
|
-
};
|
|
405
|
-
|
|
406
|
-
// src/providers/SessionProvider.tsx
|
|
407
|
-
import React5, { createContext as createContext5, useContext as useContext5, useMemo as useMemo2 } from "react";
|
|
408
|
-
|
|
409
|
-
// src/hooks/useSession.ts
|
|
410
|
-
import { useState as useState3, useEffect as useEffect4, useCallback } from "react";
|
|
411
|
-
|
|
412
|
-
// src/core/SessionManager.ts
|
|
413
|
-
import crudify2 from "@nocios/crudify-browser";
|
|
414
|
-
|
|
415
|
-
// src/utils/tokenStorage.ts
|
|
416
|
-
import CryptoJS from "crypto-js";
|
|
417
|
-
var _TokenStorage = class _TokenStorage {
|
|
418
|
-
/**
|
|
419
|
-
* Configurar tipo de almacenamiento
|
|
420
|
-
*/
|
|
421
|
-
static setStorageType(type) {
|
|
422
|
-
_TokenStorage.storageType = type;
|
|
423
|
-
}
|
|
424
|
-
/**
|
|
425
|
-
* Generar clave de encriptación única y persistente
|
|
426
|
-
*/
|
|
427
|
-
static generateEncryptionKey() {
|
|
428
|
-
const browserInfo = [
|
|
429
|
-
navigator.userAgent,
|
|
430
|
-
navigator.language,
|
|
431
|
-
navigator.platform,
|
|
432
|
-
screen.width,
|
|
433
|
-
screen.height,
|
|
434
|
-
Date.now().toString(),
|
|
435
|
-
Math.random().toString(36)
|
|
436
|
-
].join("|");
|
|
437
|
-
return CryptoJS.SHA256(browserInfo).toString();
|
|
438
|
-
}
|
|
439
|
-
/**
|
|
440
|
-
* Obtener o generar clave de encriptación
|
|
441
|
-
*/
|
|
442
|
-
static getEncryptionKey() {
|
|
443
|
-
if (_TokenStorage.encryptionKey) {
|
|
444
|
-
return _TokenStorage.encryptionKey;
|
|
445
|
-
}
|
|
446
|
-
const storage = window.localStorage;
|
|
447
|
-
if (!storage) {
|
|
448
|
-
_TokenStorage.encryptionKey = _TokenStorage.generateEncryptionKey();
|
|
449
|
-
return _TokenStorage.encryptionKey;
|
|
450
|
-
}
|
|
451
|
-
try {
|
|
452
|
-
let existingKey = storage.getItem(_TokenStorage.ENCRYPTION_KEY_STORAGE);
|
|
453
|
-
if (!existingKey || existingKey.length < 32) {
|
|
454
|
-
existingKey = _TokenStorage.generateEncryptionKey();
|
|
455
|
-
storage.setItem(_TokenStorage.ENCRYPTION_KEY_STORAGE, existingKey);
|
|
456
|
-
}
|
|
457
|
-
_TokenStorage.encryptionKey = existingKey;
|
|
458
|
-
return existingKey;
|
|
459
|
-
} catch (error) {
|
|
460
|
-
console.warn("Crudify: Cannot persist encryption key, using temporary key");
|
|
461
|
-
_TokenStorage.encryptionKey = _TokenStorage.generateEncryptionKey();
|
|
462
|
-
return _TokenStorage.encryptionKey;
|
|
463
|
-
}
|
|
464
|
-
}
|
|
465
|
-
/**
|
|
466
|
-
* Verificar si el storage está disponible
|
|
467
|
-
*/
|
|
468
|
-
static isStorageAvailable(type) {
|
|
469
|
-
try {
|
|
470
|
-
const storage = window[type];
|
|
471
|
-
const testKey = "__storage_test__";
|
|
472
|
-
storage.setItem(testKey, "test");
|
|
473
|
-
storage.removeItem(testKey);
|
|
474
|
-
return true;
|
|
475
|
-
} catch {
|
|
476
|
-
return false;
|
|
477
|
-
}
|
|
478
|
-
}
|
|
479
|
-
/**
|
|
480
|
-
* Obtener instancia de storage
|
|
481
|
-
*/
|
|
482
|
-
static getStorage() {
|
|
483
|
-
if (_TokenStorage.storageType === "none") return null;
|
|
484
|
-
if (!_TokenStorage.isStorageAvailable(_TokenStorage.storageType)) {
|
|
485
|
-
console.warn(`Crudify: ${_TokenStorage.storageType} not available, tokens won't persist`);
|
|
486
|
-
return null;
|
|
487
|
-
}
|
|
488
|
-
return window[_TokenStorage.storageType];
|
|
489
|
-
}
|
|
490
|
-
/**
|
|
491
|
-
* Encriptar datos sensibles
|
|
492
|
-
*/
|
|
493
|
-
static encrypt(data) {
|
|
494
|
-
try {
|
|
495
|
-
const encryptionKey = _TokenStorage.getEncryptionKey();
|
|
496
|
-
return CryptoJS.AES.encrypt(data, encryptionKey).toString();
|
|
497
|
-
} catch (error) {
|
|
498
|
-
console.error("Crudify: Encryption failed", error);
|
|
499
|
-
return data;
|
|
500
|
-
}
|
|
501
|
-
}
|
|
502
|
-
/**
|
|
503
|
-
* Desencriptar datos
|
|
504
|
-
*/
|
|
505
|
-
static decrypt(encryptedData) {
|
|
506
|
-
try {
|
|
507
|
-
const encryptionKey = _TokenStorage.getEncryptionKey();
|
|
508
|
-
const bytes = CryptoJS.AES.decrypt(encryptedData, encryptionKey);
|
|
509
|
-
const decrypted = bytes.toString(CryptoJS.enc.Utf8);
|
|
510
|
-
return decrypted || encryptedData;
|
|
511
|
-
} catch (error) {
|
|
512
|
-
console.error("Crudify: Decryption failed", error);
|
|
513
|
-
return encryptedData;
|
|
514
|
-
}
|
|
515
|
-
}
|
|
516
|
-
/**
|
|
517
|
-
* Guardar tokens de forma segura
|
|
518
|
-
*/
|
|
519
|
-
static saveTokens(tokens) {
|
|
520
|
-
const storage = _TokenStorage.getStorage();
|
|
521
|
-
if (!storage) return;
|
|
522
|
-
try {
|
|
523
|
-
const tokenData = {
|
|
524
|
-
accessToken: tokens.accessToken,
|
|
525
|
-
refreshToken: tokens.refreshToken,
|
|
526
|
-
expiresAt: tokens.expiresAt,
|
|
527
|
-
refreshExpiresAt: tokens.refreshExpiresAt,
|
|
528
|
-
savedAt: Date.now()
|
|
529
|
-
};
|
|
530
|
-
const encrypted = _TokenStorage.encrypt(JSON.stringify(tokenData));
|
|
531
|
-
storage.setItem(_TokenStorage.TOKEN_KEY, encrypted);
|
|
532
|
-
console.debug("Crudify: Tokens saved successfully");
|
|
533
|
-
} catch (error) {
|
|
534
|
-
console.error("Crudify: Failed to save tokens", error);
|
|
535
|
-
}
|
|
536
|
-
}
|
|
537
|
-
/**
|
|
538
|
-
* Obtener tokens guardados
|
|
539
|
-
*/
|
|
540
|
-
static getTokens() {
|
|
541
|
-
const storage = _TokenStorage.getStorage();
|
|
542
|
-
if (!storage) return null;
|
|
543
|
-
try {
|
|
544
|
-
const encrypted = storage.getItem(_TokenStorage.TOKEN_KEY);
|
|
545
|
-
if (!encrypted) return null;
|
|
546
|
-
const decrypted = _TokenStorage.decrypt(encrypted);
|
|
547
|
-
const tokenData = JSON.parse(decrypted);
|
|
548
|
-
if (!tokenData.accessToken || !tokenData.refreshToken || !tokenData.expiresAt || !tokenData.refreshExpiresAt) {
|
|
549
|
-
console.warn("Crudify: Incomplete token data found, clearing storage");
|
|
550
|
-
_TokenStorage.clearTokens();
|
|
551
|
-
return null;
|
|
552
|
-
}
|
|
553
|
-
if (Date.now() >= tokenData.refreshExpiresAt) {
|
|
554
|
-
console.info("Crudify: Refresh token expired, clearing storage");
|
|
555
|
-
_TokenStorage.clearTokens();
|
|
556
|
-
return null;
|
|
557
|
-
}
|
|
558
|
-
return {
|
|
559
|
-
accessToken: tokenData.accessToken,
|
|
560
|
-
refreshToken: tokenData.refreshToken,
|
|
561
|
-
expiresAt: tokenData.expiresAt,
|
|
562
|
-
refreshExpiresAt: tokenData.refreshExpiresAt
|
|
563
|
-
};
|
|
564
|
-
} catch (error) {
|
|
565
|
-
console.error("Crudify: Failed to retrieve tokens", error);
|
|
566
|
-
_TokenStorage.clearTokens();
|
|
567
|
-
return null;
|
|
568
|
-
}
|
|
569
|
-
}
|
|
570
|
-
/**
|
|
571
|
-
* Limpiar tokens almacenados
|
|
572
|
-
*/
|
|
573
|
-
static clearTokens() {
|
|
574
|
-
const storage = _TokenStorage.getStorage();
|
|
575
|
-
if (!storage) return;
|
|
576
|
-
try {
|
|
577
|
-
storage.removeItem(_TokenStorage.TOKEN_KEY);
|
|
578
|
-
console.debug("Crudify: Tokens cleared from storage");
|
|
579
|
-
} catch (error) {
|
|
580
|
-
console.error("Crudify: Failed to clear tokens", error);
|
|
581
|
-
}
|
|
582
|
-
}
|
|
583
|
-
/**
|
|
584
|
-
* Rotar clave de encriptación (limpia tokens existentes por seguridad)
|
|
585
|
-
*/
|
|
586
|
-
static rotateEncryptionKey() {
|
|
587
|
-
try {
|
|
588
|
-
_TokenStorage.clearTokens();
|
|
589
|
-
_TokenStorage.encryptionKey = null;
|
|
590
|
-
const storage = window.localStorage;
|
|
591
|
-
if (storage) {
|
|
592
|
-
storage.removeItem(_TokenStorage.ENCRYPTION_KEY_STORAGE);
|
|
593
|
-
}
|
|
594
|
-
console.info("Crudify: Encryption key rotated successfully");
|
|
595
|
-
} catch (error) {
|
|
596
|
-
console.error("Crudify: Failed to rotate encryption key", error);
|
|
597
|
-
}
|
|
598
|
-
}
|
|
599
|
-
/**
|
|
600
|
-
* Verificar si hay tokens válidos guardados
|
|
601
|
-
*/
|
|
602
|
-
static hasValidTokens() {
|
|
603
|
-
const tokens = _TokenStorage.getTokens();
|
|
604
|
-
return tokens !== null;
|
|
605
|
-
}
|
|
606
|
-
/**
|
|
607
|
-
* Obtener información de expiración
|
|
608
|
-
*/
|
|
609
|
-
static getExpirationInfo() {
|
|
610
|
-
const tokens = _TokenStorage.getTokens();
|
|
611
|
-
if (!tokens) return null;
|
|
612
|
-
const now = Date.now();
|
|
613
|
-
return {
|
|
614
|
-
accessExpired: now >= tokens.expiresAt,
|
|
615
|
-
refreshExpired: now >= tokens.refreshExpiresAt,
|
|
616
|
-
accessExpiresIn: Math.max(0, tokens.expiresAt - now),
|
|
617
|
-
refreshExpiresIn: Math.max(0, tokens.refreshExpiresAt - now)
|
|
618
|
-
};
|
|
619
|
-
}
|
|
620
|
-
/**
|
|
621
|
-
* Actualizar solo el access token (después de refresh)
|
|
622
|
-
*/
|
|
623
|
-
static updateAccessToken(newAccessToken, newExpiresAt) {
|
|
624
|
-
const existingTokens = _TokenStorage.getTokens();
|
|
625
|
-
if (!existingTokens) {
|
|
626
|
-
console.warn("Crudify: Cannot update access token, no existing tokens found");
|
|
627
|
-
return;
|
|
628
|
-
}
|
|
629
|
-
_TokenStorage.saveTokens({
|
|
630
|
-
...existingTokens,
|
|
631
|
-
accessToken: newAccessToken,
|
|
632
|
-
expiresAt: newExpiresAt
|
|
633
|
-
});
|
|
634
|
-
}
|
|
635
|
-
};
|
|
636
|
-
_TokenStorage.TOKEN_KEY = "crudify_tokens";
|
|
637
|
-
_TokenStorage.ENCRYPTION_KEY_STORAGE = "crudify_enc_key";
|
|
638
|
-
_TokenStorage.encryptionKey = null;
|
|
639
|
-
_TokenStorage.storageType = "localStorage";
|
|
640
|
-
var TokenStorage = _TokenStorage;
|
|
641
|
-
|
|
642
|
-
// src/utils/errorTranslation.ts
|
|
643
|
-
var ERROR_TRANSLATION_HIERARCHY = [
|
|
644
|
-
"errors.{category}.{code}",
|
|
645
|
-
// errors.auth.INVALID_CREDENTIALS
|
|
646
|
-
"errors.{code}",
|
|
647
|
-
// errors.INVALID_CREDENTIALS
|
|
648
|
-
"login.{code}",
|
|
649
|
-
// login.INVALID_CREDENTIALS (legacy)
|
|
650
|
-
"error.{code}",
|
|
651
|
-
// error.INVALID_CREDENTIALS (singular)
|
|
652
|
-
"messages.{code}",
|
|
653
|
-
// messages.INVALID_CREDENTIALS
|
|
654
|
-
"{code}"
|
|
655
|
-
// INVALID_CREDENTIALS (direct)
|
|
656
|
-
];
|
|
657
|
-
var ERROR_CATEGORY_MAP = {
|
|
658
|
-
// Authentication errors
|
|
659
|
-
"INVALID_CREDENTIALS": "auth",
|
|
660
|
-
"UNAUTHORIZED": "auth",
|
|
661
|
-
"INVALID_API_KEY": "auth",
|
|
662
|
-
"USER_NOT_FOUND": "auth",
|
|
663
|
-
"USER_NOT_ACTIVE": "auth",
|
|
664
|
-
"NO_PERMISSION": "auth",
|
|
665
|
-
"SESSION_EXPIRED": "auth",
|
|
666
|
-
// Data errors
|
|
667
|
-
"ITEM_NOT_FOUND": "data",
|
|
668
|
-
"NOT_FOUND": "data",
|
|
669
|
-
"IN_USE": "data",
|
|
670
|
-
"DUPLICATE_ENTRY": "data",
|
|
671
|
-
// Validation errors
|
|
672
|
-
"FIELD_ERROR": "validation",
|
|
673
|
-
"BAD_REQUEST": "validation",
|
|
674
|
-
"INVALID_EMAIL": "validation",
|
|
675
|
-
"INVALID_CODE": "validation",
|
|
676
|
-
"REQUIRED_FIELD": "validation",
|
|
677
|
-
// System errors
|
|
678
|
-
"INTERNAL_SERVER_ERROR": "system",
|
|
679
|
-
"DATABASE_CONNECTION_ERROR": "system",
|
|
680
|
-
"INVALID_CONFIGURATION": "system",
|
|
681
|
-
"UNKNOWN_OPERATION": "system",
|
|
682
|
-
"TIMEOUT_ERROR": "system",
|
|
683
|
-
"NETWORK_ERROR": "system",
|
|
684
|
-
// Rate limiting
|
|
685
|
-
"TOO_MANY_REQUESTS": "rate_limit"
|
|
686
|
-
};
|
|
687
|
-
var DEFAULT_ERROR_MESSAGES = {
|
|
688
|
-
"INVALID_CREDENTIALS": "Invalid username or password",
|
|
689
|
-
"UNAUTHORIZED": "You are not authorized to perform this action",
|
|
690
|
-
"SESSION_EXPIRED": "Your session has expired. Please log in again.",
|
|
691
|
-
"USER_NOT_FOUND": "User not found",
|
|
692
|
-
"ITEM_NOT_FOUND": "Item not found",
|
|
693
|
-
"FIELD_ERROR": "Invalid field value",
|
|
694
|
-
"INTERNAL_SERVER_ERROR": "An internal error occurred",
|
|
695
|
-
"NETWORK_ERROR": "Network connection error",
|
|
696
|
-
"TIMEOUT_ERROR": "Request timeout",
|
|
697
|
-
"UNKNOWN_OPERATION": "Unknown operation",
|
|
698
|
-
"INVALID_EMAIL": "Invalid email format",
|
|
699
|
-
"INVALID_CODE": "Invalid code",
|
|
700
|
-
"TOO_MANY_REQUESTS": "Too many requests, please try again later"
|
|
701
|
-
};
|
|
702
|
-
function translateErrorCode(errorCode, config) {
|
|
703
|
-
const { translateFn, currentLanguage, enableDebug } = config;
|
|
704
|
-
if (enableDebug) {
|
|
705
|
-
console.log(`\u{1F50D} [ErrorTranslation] Translating error code: ${errorCode} (lang: ${currentLanguage || "unknown"})`);
|
|
706
|
-
}
|
|
707
|
-
const normalizedCode = errorCode.toUpperCase();
|
|
708
|
-
const category = ERROR_CATEGORY_MAP[normalizedCode];
|
|
709
|
-
const translationKeys = ERROR_TRANSLATION_HIERARCHY.map((pattern) => {
|
|
710
|
-
return pattern.replace("{category}", category || "general").replace("{code}", normalizedCode);
|
|
711
|
-
});
|
|
712
|
-
if (enableDebug) {
|
|
713
|
-
console.log(`\u{1F511} [ErrorTranslation] Searching keys:`, translationKeys);
|
|
714
|
-
}
|
|
715
|
-
for (const key of translationKeys) {
|
|
716
|
-
const translated = translateFn(key);
|
|
717
|
-
if (enableDebug) {
|
|
718
|
-
console.log(`\u{1F50D} [ErrorTranslation] Checking key: "${key}" -> result: "${translated}" (same as key: ${translated === key})`);
|
|
719
|
-
}
|
|
720
|
-
if (translated && translated !== key) {
|
|
721
|
-
if (enableDebug) {
|
|
722
|
-
console.log(`\u2705 [ErrorTranslation] Found translation at key: ${key} = "${translated}"`);
|
|
723
|
-
}
|
|
724
|
-
return translated;
|
|
725
|
-
}
|
|
726
|
-
}
|
|
727
|
-
const defaultMessage = DEFAULT_ERROR_MESSAGES[normalizedCode];
|
|
728
|
-
if (defaultMessage) {
|
|
729
|
-
if (enableDebug) {
|
|
730
|
-
console.log(`\u{1F504} [ErrorTranslation] Using default message: "${defaultMessage}"`);
|
|
731
|
-
}
|
|
732
|
-
return defaultMessage;
|
|
733
|
-
}
|
|
734
|
-
const friendlyCode = normalizedCode.replace(/_/g, " ").toLowerCase().replace(/\b\w/g, (l) => l.toUpperCase());
|
|
735
|
-
if (enableDebug) {
|
|
736
|
-
console.log(`\u26A0\uFE0F [ErrorTranslation] No translation found, using friendly code: "${friendlyCode}"`);
|
|
737
|
-
}
|
|
738
|
-
return friendlyCode;
|
|
739
|
-
}
|
|
740
|
-
function translateErrorCodes(errorCodes, config) {
|
|
741
|
-
return errorCodes.map((code) => translateErrorCode(code, config));
|
|
742
|
-
}
|
|
743
|
-
function translateError(error, config) {
|
|
744
|
-
const { enableDebug } = config;
|
|
745
|
-
if (enableDebug) {
|
|
746
|
-
console.log(`\u{1F50D} [ErrorTranslation] Translating error:`, error);
|
|
747
|
-
}
|
|
748
|
-
const translatedCode = translateErrorCode(error.code, config);
|
|
749
|
-
if (translatedCode !== error.code.toUpperCase() && translatedCode !== error.code) {
|
|
750
|
-
if (enableDebug) {
|
|
751
|
-
console.log(`\u2705 [ErrorTranslation] Using hierarchical translation: "${translatedCode}"`);
|
|
752
|
-
}
|
|
753
|
-
if (error.field) {
|
|
754
|
-
return `${error.field}: ${translatedCode}`;
|
|
755
|
-
}
|
|
756
|
-
return translatedCode;
|
|
757
|
-
}
|
|
758
|
-
if (error.message && !error.message.includes("Error:") && error.message.length > 0 && error.message !== error.code) {
|
|
759
|
-
if (enableDebug) {
|
|
760
|
-
console.log(`\u{1F504} [ErrorTranslation] No hierarchical translation found, using API message: "${error.message}"`);
|
|
761
|
-
}
|
|
762
|
-
return error.message;
|
|
763
|
-
}
|
|
764
|
-
if (enableDebug) {
|
|
765
|
-
console.log(`\u26A0\uFE0F [ErrorTranslation] Using final fallback: "${translatedCode}"`);
|
|
766
|
-
}
|
|
767
|
-
if (error.field) {
|
|
768
|
-
return `${error.field}: ${translatedCode}`;
|
|
769
|
-
}
|
|
770
|
-
return translatedCode;
|
|
771
|
-
}
|
|
772
|
-
function createErrorTranslator(translateFn, options = {}) {
|
|
773
|
-
const config = {
|
|
774
|
-
translateFn,
|
|
775
|
-
currentLanguage: options.currentLanguage,
|
|
776
|
-
enableDebug: options.enableDebug || false
|
|
777
|
-
};
|
|
778
|
-
return {
|
|
779
|
-
translateErrorCode: (code) => translateErrorCode(code, config),
|
|
780
|
-
translateErrorCodes: (codes) => translateErrorCodes(codes, config),
|
|
781
|
-
translateError: (error) => translateError(error, config),
|
|
782
|
-
// Método de conveniencia para errores de API
|
|
783
|
-
translateApiError: (apiResponse) => {
|
|
784
|
-
if (apiResponse?.data?.response?.status) {
|
|
785
|
-
return translateErrorCode(apiResponse.data.response.status, config);
|
|
786
|
-
}
|
|
787
|
-
if (apiResponse?.status) {
|
|
788
|
-
return translateErrorCode(apiResponse.status, config);
|
|
789
|
-
}
|
|
790
|
-
if (apiResponse?.code) {
|
|
791
|
-
return translateErrorCode(apiResponse.code, config);
|
|
792
|
-
}
|
|
793
|
-
return "Unknown error";
|
|
794
|
-
}
|
|
795
|
-
};
|
|
796
|
-
}
|
|
797
|
-
|
|
798
|
-
// src/core/SessionManager.ts
|
|
799
|
-
var SessionManager = class _SessionManager {
|
|
800
|
-
constructor() {
|
|
801
|
-
this.config = {};
|
|
802
|
-
this.initialized = false;
|
|
803
|
-
}
|
|
804
|
-
static getInstance() {
|
|
805
|
-
if (!_SessionManager.instance) {
|
|
806
|
-
_SessionManager.instance = new _SessionManager();
|
|
807
|
-
}
|
|
808
|
-
return _SessionManager.instance;
|
|
809
|
-
}
|
|
810
|
-
/**
|
|
811
|
-
* Inicializar el SessionManager
|
|
812
|
-
*/
|
|
813
|
-
async initialize(config = {}) {
|
|
814
|
-
if (this.initialized) {
|
|
815
|
-
console.warn("SessionManager: Already initialized");
|
|
816
|
-
return;
|
|
817
|
-
}
|
|
818
|
-
this.config = {
|
|
819
|
-
storageType: "localStorage",
|
|
820
|
-
autoRestore: true,
|
|
821
|
-
enableLogging: false,
|
|
822
|
-
...config
|
|
823
|
-
};
|
|
824
|
-
TokenStorage.setStorageType(this.config.storageType || "localStorage");
|
|
825
|
-
if (this.config.enableLogging) {
|
|
826
|
-
}
|
|
827
|
-
if (this.config.autoRestore) {
|
|
828
|
-
await this.restoreSession();
|
|
829
|
-
}
|
|
830
|
-
this.initialized = true;
|
|
831
|
-
this.log("SessionManager initialized successfully");
|
|
832
|
-
}
|
|
833
|
-
/**
|
|
834
|
-
* Login con persistencia automática
|
|
835
|
-
*/
|
|
836
|
-
async login(email, password) {
|
|
837
|
-
try {
|
|
838
|
-
this.log("Attempting login...");
|
|
839
|
-
const response = await crudify2.login(email, password);
|
|
840
|
-
if (!response.success) {
|
|
841
|
-
this.log("Login failed:", response.errors);
|
|
842
|
-
return {
|
|
843
|
-
success: false,
|
|
844
|
-
error: this.formatError(response.errors),
|
|
845
|
-
rawResponse: response
|
|
846
|
-
// Preservar respuesta original para manejo de errores específicos
|
|
847
|
-
};
|
|
848
|
-
}
|
|
849
|
-
const tokens = {
|
|
850
|
-
accessToken: response.data.token,
|
|
851
|
-
refreshToken: response.data.refreshToken,
|
|
852
|
-
expiresAt: response.data.expiresAt,
|
|
853
|
-
refreshExpiresAt: response.data.refreshExpiresAt
|
|
854
|
-
};
|
|
855
|
-
TokenStorage.saveTokens(tokens);
|
|
856
|
-
this.log("Login successful, tokens saved");
|
|
857
|
-
this.config.onLoginSuccess?.(tokens);
|
|
858
|
-
return {
|
|
859
|
-
success: true,
|
|
860
|
-
tokens,
|
|
861
|
-
data: response.data
|
|
862
|
-
// Incluir datos del usuario
|
|
863
|
-
};
|
|
864
|
-
} catch (error) {
|
|
865
|
-
this.log("Login error:", error);
|
|
866
|
-
return {
|
|
867
|
-
success: false,
|
|
868
|
-
error: error instanceof Error ? error.message : "Unknown error"
|
|
869
|
-
};
|
|
870
|
-
}
|
|
871
|
-
}
|
|
872
|
-
/**
|
|
873
|
-
* Logout con limpieza de tokens
|
|
874
|
-
*/
|
|
875
|
-
async logout() {
|
|
876
|
-
try {
|
|
877
|
-
this.log("Logging out...");
|
|
878
|
-
await crudify2.logout();
|
|
879
|
-
TokenStorage.clearTokens();
|
|
880
|
-
this.log("Logout successful");
|
|
881
|
-
this.config.onLogout?.();
|
|
882
|
-
} catch (error) {
|
|
883
|
-
this.log("Logout error:", error);
|
|
884
|
-
TokenStorage.clearTokens();
|
|
885
|
-
}
|
|
886
|
-
}
|
|
887
|
-
/**
|
|
888
|
-
* Restaurar sesión desde storage
|
|
889
|
-
*/
|
|
890
|
-
async restoreSession() {
|
|
891
|
-
try {
|
|
892
|
-
this.log("Attempting to restore session...");
|
|
893
|
-
const savedTokens = TokenStorage.getTokens();
|
|
894
|
-
if (!savedTokens) {
|
|
895
|
-
this.log("No valid tokens found in storage");
|
|
896
|
-
return false;
|
|
897
|
-
}
|
|
898
|
-
crudify2.setTokens({
|
|
899
|
-
accessToken: savedTokens.accessToken,
|
|
900
|
-
refreshToken: savedTokens.refreshToken,
|
|
901
|
-
expiresAt: savedTokens.expiresAt,
|
|
902
|
-
refreshExpiresAt: savedTokens.refreshExpiresAt
|
|
903
|
-
});
|
|
904
|
-
try {
|
|
905
|
-
const isValid = crudify2.isLogin();
|
|
906
|
-
if (!isValid) {
|
|
907
|
-
this.log("Restored tokens are invalid, clearing storage");
|
|
908
|
-
TokenStorage.clearTokens();
|
|
909
|
-
return false;
|
|
910
|
-
}
|
|
911
|
-
} catch (validationError) {
|
|
912
|
-
this.log("Token validation failed:", validationError);
|
|
913
|
-
TokenStorage.clearTokens();
|
|
914
|
-
return false;
|
|
915
|
-
}
|
|
916
|
-
this.log("Session restored successfully");
|
|
917
|
-
this.config.onSessionRestored?.(savedTokens);
|
|
918
|
-
return true;
|
|
919
|
-
} catch (error) {
|
|
920
|
-
this.log("Session restore error:", error);
|
|
921
|
-
TokenStorage.clearTokens();
|
|
922
|
-
return false;
|
|
923
|
-
}
|
|
924
|
-
}
|
|
925
|
-
/**
|
|
926
|
-
* Verificar si el usuario está autenticado
|
|
927
|
-
*/
|
|
928
|
-
isAuthenticated() {
|
|
929
|
-
return crudify2.isLogin() || TokenStorage.hasValidTokens();
|
|
930
|
-
}
|
|
931
|
-
/**
|
|
932
|
-
* Obtener información de tokens actuales
|
|
933
|
-
*/
|
|
934
|
-
getTokenInfo() {
|
|
935
|
-
const crudifyTokens = crudify2.getTokenData();
|
|
936
|
-
const storageInfo = TokenStorage.getExpirationInfo();
|
|
937
|
-
return {
|
|
938
|
-
isLoggedIn: this.isAuthenticated(),
|
|
939
|
-
crudifyTokens,
|
|
940
|
-
storageInfo,
|
|
941
|
-
hasValidTokens: TokenStorage.hasValidTokens()
|
|
942
|
-
};
|
|
943
|
-
}
|
|
944
|
-
/**
|
|
945
|
-
* Refrescar tokens manualmente
|
|
946
|
-
*/
|
|
947
|
-
async refreshTokens() {
|
|
948
|
-
try {
|
|
949
|
-
this.log("Manually refreshing tokens...");
|
|
950
|
-
const response = await crudify2.refreshAccessToken();
|
|
951
|
-
if (!response.success) {
|
|
952
|
-
this.log("Token refresh failed:", response.errors);
|
|
953
|
-
TokenStorage.clearTokens();
|
|
954
|
-
this.config.showNotification?.(this.getSessionExpiredMessage(), "warning");
|
|
955
|
-
this.config.onSessionExpired?.();
|
|
956
|
-
return false;
|
|
957
|
-
}
|
|
958
|
-
const newTokens = {
|
|
959
|
-
accessToken: response.data.token,
|
|
960
|
-
refreshToken: response.data.refreshToken,
|
|
961
|
-
expiresAt: response.data.expiresAt,
|
|
962
|
-
refreshExpiresAt: response.data.refreshExpiresAt
|
|
963
|
-
};
|
|
964
|
-
TokenStorage.saveTokens(newTokens);
|
|
965
|
-
this.log("Tokens refreshed and saved successfully");
|
|
966
|
-
return true;
|
|
967
|
-
} catch (error) {
|
|
968
|
-
this.log("Token refresh error:", error);
|
|
969
|
-
TokenStorage.clearTokens();
|
|
970
|
-
this.config.showNotification?.(this.getSessionExpiredMessage(), "warning");
|
|
971
|
-
this.config.onSessionExpired?.();
|
|
972
|
-
return false;
|
|
973
|
-
}
|
|
974
|
-
}
|
|
975
|
-
/**
|
|
976
|
-
* Configurar interceptor de respuesta para manejo automático de errores
|
|
977
|
-
*/
|
|
978
|
-
setupResponseInterceptor() {
|
|
979
|
-
crudify2.setResponseInterceptor(async (response) => {
|
|
980
|
-
const authError = this.detectAuthorizationError(response);
|
|
981
|
-
if (authError.isAuthError) {
|
|
982
|
-
console.warn("\u{1F6A8} SessionManager - Authorization error detected:", {
|
|
983
|
-
errorType: authError.errorType,
|
|
984
|
-
errorDetails: authError.errorDetails,
|
|
985
|
-
fullResponse: response
|
|
986
|
-
});
|
|
987
|
-
if (authError.isRefreshTokenInvalid || authError.isTokenRefreshFailed) {
|
|
988
|
-
this.log("Refresh token invalid or refresh already failed, clearing session");
|
|
989
|
-
TokenStorage.clearTokens();
|
|
990
|
-
this.config.showNotification?.(this.getSessionExpiredMessage(), "warning");
|
|
991
|
-
this.config.onSessionExpired?.();
|
|
992
|
-
return response;
|
|
993
|
-
}
|
|
994
|
-
if (TokenStorage.hasValidTokens() && !authError.isIrrecoverable) {
|
|
995
|
-
this.log("Auth error detected, attempting token refresh...");
|
|
996
|
-
const refreshSuccess = await this.refreshTokens();
|
|
997
|
-
if (!refreshSuccess) {
|
|
998
|
-
this.log("Token refresh failed, triggering session expired");
|
|
999
|
-
this.config.onSessionExpired?.();
|
|
1000
|
-
}
|
|
1001
|
-
} else {
|
|
1002
|
-
this.log("Auth error with no valid tokens or irrecoverable error, triggering session expired");
|
|
1003
|
-
TokenStorage.clearTokens();
|
|
1004
|
-
this.config.showNotification?.(this.getSessionExpiredMessage(), "warning");
|
|
1005
|
-
this.config.onSessionExpired?.();
|
|
1006
|
-
}
|
|
1007
|
-
}
|
|
1008
|
-
return response;
|
|
1009
|
-
});
|
|
1010
|
-
this.log("Response interceptor configured");
|
|
1011
|
-
}
|
|
1012
|
-
/**
|
|
1013
|
-
* Detectar errores de autorización en todos los formatos posibles
|
|
1014
|
-
*/
|
|
1015
|
-
detectAuthorizationError(response) {
|
|
1016
|
-
const result = {
|
|
1017
|
-
isAuthError: false,
|
|
1018
|
-
isRefreshTokenInvalid: false,
|
|
1019
|
-
isTokenRefreshFailed: false,
|
|
1020
|
-
isIrrecoverable: false,
|
|
1021
|
-
errorType: "",
|
|
1022
|
-
errorDetails: null
|
|
1023
|
-
};
|
|
1024
|
-
if (response.errors) {
|
|
1025
|
-
if (Array.isArray(response.errors)) {
|
|
1026
|
-
const hasAuthError = response.errors.some(
|
|
1027
|
-
(error) => error.errorType === "Unauthorized" || error.message?.includes("Unauthorized") || error.message?.includes("Not Authorized") || error.message?.includes("Token") || error.extensions?.code === "UNAUTHENTICATED" || error.message?.includes("NOT_AUTHORIZED")
|
|
1028
|
-
);
|
|
1029
|
-
if (hasAuthError) {
|
|
1030
|
-
result.isAuthError = true;
|
|
1031
|
-
result.errorType = "GraphQL Array Format";
|
|
1032
|
-
result.errorDetails = response.errors;
|
|
1033
|
-
}
|
|
1034
|
-
} else if (typeof response.errors === "object") {
|
|
1035
|
-
const errorMessages = Object.values(response.errors).flat();
|
|
1036
|
-
const hasAuthError = errorMessages.some(
|
|
1037
|
-
(message) => typeof message === "string" && (message.includes("NOT_AUTHORIZED") || message.includes("TOKEN_REFRESH_FAILED") || message.includes("PLEASE_LOGIN") || message.includes("Unauthorized") || message.includes("UNAUTHENTICATED") || message.includes("Token"))
|
|
1038
|
-
);
|
|
1039
|
-
if (hasAuthError) {
|
|
1040
|
-
result.isAuthError = true;
|
|
1041
|
-
result.errorType = "GraphQL Object Format";
|
|
1042
|
-
result.errorDetails = response.errors;
|
|
1043
|
-
result.isTokenRefreshFailed = errorMessages.some(
|
|
1044
|
-
(message) => typeof message === "string" && message.includes("TOKEN_REFRESH_FAILED")
|
|
1045
|
-
);
|
|
1046
|
-
}
|
|
1047
|
-
}
|
|
1048
|
-
}
|
|
1049
|
-
if (!result.isAuthError && response.data?.response?.status === "UNAUTHORIZED") {
|
|
1050
|
-
result.isAuthError = true;
|
|
1051
|
-
result.errorType = "Status UNAUTHORIZED";
|
|
1052
|
-
result.errorDetails = response.data.response;
|
|
1053
|
-
result.isIrrecoverable = true;
|
|
1054
|
-
}
|
|
1055
|
-
if (!result.isAuthError && response.data?.response?.data) {
|
|
1056
|
-
try {
|
|
1057
|
-
const parsedData = JSON.parse(response.data.response.data);
|
|
1058
|
-
if (parsedData.error === "REFRESH_TOKEN_INVALID" || parsedData.error === "TOKEN_EXPIRED") {
|
|
1059
|
-
result.isAuthError = true;
|
|
1060
|
-
result.errorType = "Parsed Data Format";
|
|
1061
|
-
result.errorDetails = parsedData;
|
|
1062
|
-
result.isRefreshTokenInvalid = true;
|
|
1063
|
-
result.isIrrecoverable = true;
|
|
1064
|
-
}
|
|
1065
|
-
} catch {
|
|
1066
|
-
}
|
|
1067
|
-
}
|
|
1068
|
-
if (!result.isAuthError && response.errorCode) {
|
|
1069
|
-
const errorCode = response.errorCode;
|
|
1070
|
-
const isAuthErrorCode = errorCode === "UNAUTHORIZED" || errorCode === "UNAUTHENTICATED" || errorCode === "TOKEN_EXPIRED";
|
|
1071
|
-
if (isAuthErrorCode) {
|
|
1072
|
-
result.isAuthError = true;
|
|
1073
|
-
result.errorType = "Error Code Format";
|
|
1074
|
-
result.errorDetails = { errorCode };
|
|
1075
|
-
}
|
|
1076
|
-
}
|
|
1077
|
-
return result;
|
|
1078
|
-
}
|
|
1079
|
-
/**
|
|
1080
|
-
* Limpiar sesión completamente
|
|
1081
|
-
*/
|
|
1082
|
-
clearSession() {
|
|
1083
|
-
TokenStorage.clearTokens();
|
|
1084
|
-
crudify2.logout();
|
|
1085
|
-
this.log("Session cleared completely");
|
|
1086
|
-
}
|
|
1087
|
-
/**
|
|
1088
|
-
* Obtener mensaje de sesión expirada traducido
|
|
1089
|
-
*/
|
|
1090
|
-
getSessionExpiredMessage() {
|
|
1091
|
-
if (this.config.translateFn) {
|
|
1092
|
-
return translateErrorCode("SESSION_EXPIRED", {
|
|
1093
|
-
translateFn: this.config.translateFn,
|
|
1094
|
-
enableDebug: this.config.enableLogging
|
|
1095
|
-
});
|
|
1096
|
-
}
|
|
1097
|
-
return "Tu sesi\xF3n ha expirado. Por favor, inicia sesi\xF3n nuevamente.";
|
|
1098
|
-
}
|
|
1099
|
-
// Métodos privados
|
|
1100
|
-
log(message, ...args) {
|
|
1101
|
-
if (this.config.enableLogging) {
|
|
1102
|
-
console.log(`[SessionManager] ${message}`, ...args);
|
|
1103
|
-
}
|
|
1104
|
-
}
|
|
1105
|
-
formatError(errors) {
|
|
1106
|
-
if (!errors) return "Unknown error";
|
|
1107
|
-
if (typeof errors === "string") return errors;
|
|
1108
|
-
if (typeof errors === "object") {
|
|
1109
|
-
const errorMessages = Object.values(errors).flat();
|
|
1110
|
-
return errorMessages.join(", ");
|
|
1111
|
-
}
|
|
1112
|
-
return "Authentication failed";
|
|
1113
|
-
}
|
|
1114
|
-
};
|
|
1115
|
-
|
|
1116
|
-
// src/hooks/useSession.ts
|
|
1117
|
-
function useSession(options = {}) {
|
|
1118
|
-
const [state, setState] = useState3({
|
|
1119
|
-
isAuthenticated: false,
|
|
1120
|
-
isLoading: true,
|
|
1121
|
-
isInitialized: false,
|
|
1122
|
-
tokens: null,
|
|
1123
|
-
error: null
|
|
1124
|
-
});
|
|
1125
|
-
const sessionManager = SessionManager.getInstance();
|
|
1126
|
-
const initialize = useCallback(async () => {
|
|
1127
|
-
try {
|
|
1128
|
-
setState((prev) => ({ ...prev, isLoading: true, error: null }));
|
|
1129
|
-
const config = {
|
|
1130
|
-
autoRestore: options.autoRestore ?? true,
|
|
1131
|
-
enableLogging: options.enableLogging ?? false,
|
|
1132
|
-
showNotification: options.showNotification,
|
|
1133
|
-
translateFn: options.translateFn,
|
|
1134
|
-
onSessionExpired: () => {
|
|
1135
|
-
setState((prev) => ({
|
|
1136
|
-
...prev,
|
|
1137
|
-
isAuthenticated: false,
|
|
1138
|
-
tokens: null,
|
|
1139
|
-
error: "Session expired"
|
|
1140
|
-
}));
|
|
1141
|
-
options.onSessionExpired?.();
|
|
1142
|
-
},
|
|
1143
|
-
onSessionRestored: (tokens) => {
|
|
1144
|
-
setState((prev) => ({
|
|
1145
|
-
...prev,
|
|
1146
|
-
isAuthenticated: true,
|
|
1147
|
-
tokens,
|
|
1148
|
-
error: null
|
|
1149
|
-
}));
|
|
1150
|
-
options.onSessionRestored?.(tokens);
|
|
1151
|
-
},
|
|
1152
|
-
onLoginSuccess: (tokens) => {
|
|
1153
|
-
setState((prev) => ({
|
|
1154
|
-
...prev,
|
|
1155
|
-
isAuthenticated: true,
|
|
1156
|
-
tokens,
|
|
1157
|
-
error: null
|
|
1158
|
-
}));
|
|
1159
|
-
},
|
|
1160
|
-
onLogout: () => {
|
|
1161
|
-
setState((prev) => ({
|
|
1162
|
-
...prev,
|
|
1163
|
-
isAuthenticated: false,
|
|
1164
|
-
tokens: null,
|
|
1165
|
-
error: null
|
|
1166
|
-
}));
|
|
1167
|
-
}
|
|
1168
|
-
};
|
|
1169
|
-
await sessionManager.initialize(config);
|
|
1170
|
-
sessionManager.setupResponseInterceptor();
|
|
1171
|
-
const isAuth = sessionManager.isAuthenticated();
|
|
1172
|
-
const tokenInfo = sessionManager.getTokenInfo();
|
|
1173
|
-
setState((prev) => ({
|
|
1174
|
-
...prev,
|
|
1175
|
-
isAuthenticated: isAuth,
|
|
1176
|
-
isInitialized: true,
|
|
1177
|
-
isLoading: false,
|
|
1178
|
-
tokens: tokenInfo.crudifyTokens.accessToken ? {
|
|
1179
|
-
accessToken: tokenInfo.crudifyTokens.accessToken,
|
|
1180
|
-
refreshToken: tokenInfo.crudifyTokens.refreshToken,
|
|
1181
|
-
expiresAt: tokenInfo.crudifyTokens.expiresAt,
|
|
1182
|
-
refreshExpiresAt: tokenInfo.crudifyTokens.refreshExpiresAt
|
|
1183
|
-
} : null
|
|
1184
|
-
}));
|
|
1185
|
-
} catch (error) {
|
|
1186
|
-
const errorMessage = error instanceof Error ? error.message : "Initialization failed";
|
|
1187
|
-
setState((prev) => ({
|
|
1188
|
-
...prev,
|
|
1189
|
-
isLoading: false,
|
|
1190
|
-
isInitialized: true,
|
|
1191
|
-
error: errorMessage
|
|
1192
|
-
}));
|
|
1193
|
-
}
|
|
1194
|
-
}, [options.autoRestore, options.enableLogging, options.onSessionExpired, options.onSessionRestored]);
|
|
1195
|
-
const login = useCallback(
|
|
1196
|
-
async (email, password) => {
|
|
1197
|
-
setState((prev) => ({ ...prev, isLoading: true, error: null }));
|
|
1198
|
-
try {
|
|
1199
|
-
const result = await sessionManager.login(email, password);
|
|
1200
|
-
if (result.success && result.tokens) {
|
|
1201
|
-
setState((prev) => ({
|
|
1202
|
-
...prev,
|
|
1203
|
-
isAuthenticated: true,
|
|
1204
|
-
tokens: result.tokens,
|
|
1205
|
-
isLoading: false,
|
|
1206
|
-
error: null
|
|
1207
|
-
}));
|
|
1208
|
-
} else {
|
|
1209
|
-
setState((prev) => ({
|
|
1210
|
-
...prev,
|
|
1211
|
-
isAuthenticated: false,
|
|
1212
|
-
tokens: null,
|
|
1213
|
-
isLoading: false,
|
|
1214
|
-
error: null
|
|
1215
|
-
// No establecer error global para errores de login
|
|
1216
|
-
}));
|
|
1217
|
-
}
|
|
1218
|
-
return result;
|
|
1219
|
-
} catch (error) {
|
|
1220
|
-
const errorMsg = error instanceof Error ? error.message : "Login failed";
|
|
1221
|
-
const isCredentialError = errorMsg.includes("INVALID_CREDENTIALS") || errorMsg.includes("Invalid email") || errorMsg.includes("Invalid password") || errorMsg.includes("credentials");
|
|
1222
|
-
setState((prev) => ({
|
|
1223
|
-
...prev,
|
|
1224
|
-
isAuthenticated: false,
|
|
1225
|
-
tokens: null,
|
|
1226
|
-
isLoading: false,
|
|
1227
|
-
error: isCredentialError ? null : errorMsg
|
|
1228
|
-
// Solo errores del sistema van al estado global
|
|
1229
|
-
}));
|
|
1230
|
-
return {
|
|
1231
|
-
success: false,
|
|
1232
|
-
error: errorMsg
|
|
1233
|
-
};
|
|
1234
|
-
}
|
|
1235
|
-
},
|
|
1236
|
-
[sessionManager]
|
|
1237
|
-
);
|
|
1238
|
-
const logout = useCallback(async () => {
|
|
1239
|
-
setState((prev) => ({ ...prev, isLoading: true }));
|
|
1240
|
-
try {
|
|
1241
|
-
await sessionManager.logout();
|
|
1242
|
-
setState((prev) => ({
|
|
1243
|
-
...prev,
|
|
1244
|
-
isAuthenticated: false,
|
|
1245
|
-
tokens: null,
|
|
1246
|
-
isLoading: false,
|
|
1247
|
-
error: null
|
|
1248
|
-
}));
|
|
1249
|
-
} catch (error) {
|
|
1250
|
-
setState((prev) => ({
|
|
1251
|
-
...prev,
|
|
1252
|
-
isAuthenticated: false,
|
|
1253
|
-
tokens: null,
|
|
1254
|
-
isLoading: false,
|
|
1255
|
-
error: error instanceof Error ? error.message : "Logout error"
|
|
1256
|
-
}));
|
|
1257
|
-
}
|
|
1258
|
-
}, [sessionManager]);
|
|
1259
|
-
const refreshTokens = useCallback(async () => {
|
|
1260
|
-
try {
|
|
1261
|
-
const success = await sessionManager.refreshTokens();
|
|
1262
|
-
if (success) {
|
|
1263
|
-
const tokenInfo = sessionManager.getTokenInfo();
|
|
1264
|
-
setState((prev) => ({
|
|
1265
|
-
...prev,
|
|
1266
|
-
tokens: tokenInfo.crudifyTokens.accessToken ? {
|
|
1267
|
-
accessToken: tokenInfo.crudifyTokens.accessToken,
|
|
1268
|
-
refreshToken: tokenInfo.crudifyTokens.refreshToken,
|
|
1269
|
-
expiresAt: tokenInfo.crudifyTokens.expiresAt,
|
|
1270
|
-
refreshExpiresAt: tokenInfo.crudifyTokens.refreshExpiresAt
|
|
1271
|
-
} : null,
|
|
1272
|
-
error: null
|
|
1273
|
-
}));
|
|
1274
|
-
} else {
|
|
1275
|
-
setState((prev) => ({
|
|
1276
|
-
...prev,
|
|
1277
|
-
isAuthenticated: false,
|
|
1278
|
-
tokens: null,
|
|
1279
|
-
error: "Token refresh failed"
|
|
1280
|
-
}));
|
|
1281
|
-
}
|
|
1282
|
-
return success;
|
|
1283
|
-
} catch (error) {
|
|
1284
|
-
setState((prev) => ({
|
|
1285
|
-
...prev,
|
|
1286
|
-
isAuthenticated: false,
|
|
1287
|
-
tokens: null,
|
|
1288
|
-
error: error instanceof Error ? error.message : "Token refresh failed"
|
|
1289
|
-
}));
|
|
1290
|
-
return false;
|
|
1291
|
-
}
|
|
1292
|
-
}, [sessionManager]);
|
|
1293
|
-
const clearError = useCallback(() => {
|
|
1294
|
-
setState((prev) => ({ ...prev, error: null }));
|
|
1295
|
-
}, []);
|
|
1296
|
-
const getTokenInfo = useCallback(() => {
|
|
1297
|
-
return sessionManager.getTokenInfo();
|
|
1298
|
-
}, [sessionManager]);
|
|
1299
|
-
useEffect4(() => {
|
|
1300
|
-
initialize();
|
|
1301
|
-
}, [initialize]);
|
|
1302
|
-
return {
|
|
1303
|
-
// Estado
|
|
1304
|
-
...state,
|
|
1305
|
-
// Acciones
|
|
1306
|
-
login,
|
|
1307
|
-
logout,
|
|
1308
|
-
refreshTokens,
|
|
1309
|
-
clearError,
|
|
1310
|
-
getTokenInfo,
|
|
1311
|
-
// Utilidades
|
|
1312
|
-
isExpiringSoon: state.tokens ? state.tokens.expiresAt - Date.now() < 5 * 60 * 1e3 : false,
|
|
1313
|
-
// 5 minutos
|
|
1314
|
-
expiresIn: state.tokens ? Math.max(0, state.tokens.expiresAt - Date.now()) : 0,
|
|
1315
|
-
refreshExpiresIn: state.tokens ? Math.max(0, state.tokens.refreshExpiresAt - Date.now()) : 0
|
|
1316
|
-
};
|
|
1317
|
-
}
|
|
1318
|
-
|
|
1319
|
-
// src/utils/jwtUtils.ts
|
|
1320
|
-
var decodeJwtSafely = (token) => {
|
|
1321
|
-
try {
|
|
1322
|
-
const parts = token.split(".");
|
|
1323
|
-
if (parts.length !== 3) {
|
|
1324
|
-
console.warn("Invalid JWT format: token must have 3 parts");
|
|
1325
|
-
return null;
|
|
1326
|
-
}
|
|
1327
|
-
const payload = parts[1];
|
|
1328
|
-
const paddedPayload = payload + "=".repeat((4 - payload.length % 4) % 4);
|
|
1329
|
-
const decodedPayload = JSON.parse(atob(paddedPayload));
|
|
1330
|
-
return decodedPayload;
|
|
1331
|
-
} catch (error) {
|
|
1332
|
-
console.warn("Failed to decode JWT token:", error);
|
|
1333
|
-
return null;
|
|
1334
|
-
}
|
|
1335
|
-
};
|
|
1336
|
-
var getCurrentUserEmail = () => {
|
|
1337
|
-
try {
|
|
1338
|
-
let token = null;
|
|
1339
|
-
token = sessionStorage.getItem("authToken");
|
|
1340
|
-
console.log("\u{1F50D} getCurrentUserEmail - authToken:", token ? `${token.substring(0, 20)}...` : null);
|
|
1341
|
-
if (!token) {
|
|
1342
|
-
token = sessionStorage.getItem("token");
|
|
1343
|
-
console.log("\u{1F50D} getCurrentUserEmail - token:", token ? `${token.substring(0, 20)}...` : null);
|
|
1344
|
-
}
|
|
1345
|
-
if (!token) {
|
|
1346
|
-
token = localStorage.getItem("authToken") || localStorage.getItem("token");
|
|
1347
|
-
console.log("\u{1F50D} getCurrentUserEmail - localStorage:", token ? `${token.substring(0, 20)}...` : null);
|
|
1348
|
-
}
|
|
1349
|
-
if (!token) {
|
|
1350
|
-
console.warn("\u{1F50D} getCurrentUserEmail - No token found in any storage");
|
|
1351
|
-
return null;
|
|
1352
|
-
}
|
|
1353
|
-
const payload = decodeJwtSafely(token);
|
|
1354
|
-
if (!payload) {
|
|
1355
|
-
console.warn("\u{1F50D} getCurrentUserEmail - Failed to decode token");
|
|
1356
|
-
return null;
|
|
1357
|
-
}
|
|
1358
|
-
const email = payload.email || payload["cognito:username"] || null;
|
|
1359
|
-
console.log("\u{1F50D} getCurrentUserEmail - Extracted email:", email);
|
|
1360
|
-
return email;
|
|
1361
|
-
} catch (error) {
|
|
1362
|
-
console.warn("Failed to get current user email:", error);
|
|
1363
|
-
return null;
|
|
1364
|
-
}
|
|
1365
|
-
};
|
|
1366
|
-
var isTokenExpired = (token) => {
|
|
1367
|
-
try {
|
|
1368
|
-
const payload = decodeJwtSafely(token);
|
|
1369
|
-
if (!payload || !payload.exp) return true;
|
|
1370
|
-
const currentTime = Math.floor(Date.now() / 1e3);
|
|
1371
|
-
return payload.exp < currentTime;
|
|
1372
|
-
} catch {
|
|
1373
|
-
return true;
|
|
1374
|
-
}
|
|
1375
|
-
};
|
|
1376
|
-
|
|
1377
|
-
// src/components/GlobalNotificationProvider.tsx
|
|
1378
|
-
import { useState as useState4, createContext as createContext4, useContext as useContext4, useCallback as useCallback2, useEffect as useEffect5 } from "react";
|
|
1379
|
-
import { Snackbar, Alert, Box, Portal } from "@mui/material";
|
|
1380
|
-
import { v4 as uuidv4 } from "uuid";
|
|
1381
|
-
import DOMPurify from "dompurify";
|
|
1382
|
-
import { jsx as jsx4, jsxs } from "react/jsx-runtime";
|
|
1383
|
-
var GlobalNotificationContext = createContext4(null);
|
|
1384
|
-
var sanitizeNotificationContent = (html) => {
|
|
1385
|
-
return DOMPurify.sanitize(html, {
|
|
1386
|
-
// Solo permitir tags seguros para notificaciones
|
|
1387
|
-
ALLOWED_TAGS: ["b", "i", "em", "strong", "br", "span"],
|
|
1388
|
-
ALLOWED_ATTR: ["class"],
|
|
1389
|
-
// Remover scripts y eventos
|
|
1390
|
-
FORBID_TAGS: ["script", "iframe", "object", "embed"],
|
|
1391
|
-
FORBID_ATTR: ["onload", "onerror", "onclick", "onmouseover", "onfocus", "onblur"],
|
|
1392
|
-
// Configuración adicional de seguridad
|
|
1393
|
-
WHOLE_DOCUMENT: false,
|
|
1394
|
-
RETURN_DOM: false,
|
|
1395
|
-
RETURN_DOM_FRAGMENT: false,
|
|
1396
|
-
RETURN_TRUSTED_TYPE: false
|
|
1397
|
-
});
|
|
1398
|
-
};
|
|
1399
|
-
var GlobalNotificationProvider = ({
|
|
1400
|
-
children,
|
|
1401
|
-
maxNotifications = 5,
|
|
1402
|
-
defaultAutoHideDuration = 6e3,
|
|
1403
|
-
position = { vertical: "top", horizontal: "right" },
|
|
1404
|
-
enabled = false,
|
|
1405
|
-
// ✅ Por defecto DESACTIVADO
|
|
1406
|
-
allowHtml = false
|
|
1407
|
-
// Por defecto no permitir HTML
|
|
1408
|
-
}) => {
|
|
1409
|
-
const [notifications, setNotifications] = useState4([]);
|
|
1410
|
-
const showNotification = useCallback2(
|
|
1411
|
-
(message, severity = "info", options) => {
|
|
1412
|
-
if (!enabled) {
|
|
1413
|
-
return "";
|
|
1414
|
-
}
|
|
1415
|
-
if (!message || typeof message !== "string") {
|
|
1416
|
-
console.warn("\u26A0\uFE0F GlobalNotificationProvider: Invalid message provided");
|
|
1417
|
-
return "";
|
|
1418
|
-
}
|
|
1419
|
-
if (message.length > 1e3) {
|
|
1420
|
-
console.warn("\u26A0\uFE0F GlobalNotificationProvider: Message too long, truncating");
|
|
1421
|
-
message = message.substring(0, 1e3) + "...";
|
|
1422
|
-
}
|
|
1423
|
-
const id = uuidv4();
|
|
1424
|
-
const newNotification = {
|
|
1425
|
-
id,
|
|
1426
|
-
message,
|
|
1427
|
-
severity,
|
|
1428
|
-
autoHideDuration: options?.autoHideDuration ?? defaultAutoHideDuration,
|
|
1429
|
-
persistent: options?.persistent ?? false,
|
|
1430
|
-
allowHtml: options?.allowHtml ?? allowHtml
|
|
1431
|
-
// Usar valor del provider por defecto
|
|
1432
|
-
};
|
|
1433
|
-
setNotifications((prev) => {
|
|
1434
|
-
const updatedNotifications = prev.length >= maxNotifications ? prev.slice(-(maxNotifications - 1)) : prev;
|
|
1435
|
-
return [...updatedNotifications, newNotification];
|
|
1436
|
-
});
|
|
1437
|
-
return id;
|
|
1438
|
-
},
|
|
1439
|
-
[maxNotifications, defaultAutoHideDuration, enabled, allowHtml]
|
|
1440
|
-
);
|
|
1441
|
-
const hideNotification = useCallback2((id) => {
|
|
1442
|
-
setNotifications((prev) => prev.filter((notification) => notification.id !== id));
|
|
1443
|
-
}, []);
|
|
1444
|
-
const clearAllNotifications = useCallback2(() => {
|
|
1445
|
-
setNotifications([]);
|
|
1446
|
-
}, []);
|
|
1447
|
-
const value = {
|
|
1448
|
-
showNotification,
|
|
1449
|
-
hideNotification,
|
|
1450
|
-
clearAllNotifications
|
|
1451
|
-
};
|
|
1452
|
-
return /* @__PURE__ */ jsxs(GlobalNotificationContext.Provider, { value, children: [
|
|
1453
|
-
children,
|
|
1454
|
-
enabled && /* @__PURE__ */ jsx4(Portal, { children: /* @__PURE__ */ jsx4(
|
|
1455
|
-
Box,
|
|
1456
|
-
{
|
|
1457
|
-
sx: {
|
|
1458
|
-
position: "fixed",
|
|
1459
|
-
zIndex: 9999,
|
|
1460
|
-
[position.vertical]: position.vertical === "top" ? 24 : 24,
|
|
1461
|
-
[position.horizontal]: position.horizontal === "right" ? 24 : position.horizontal === "left" ? 24 : "50%",
|
|
1462
|
-
...position.horizontal === "center" && {
|
|
1463
|
-
transform: "translateX(-50%)"
|
|
1464
|
-
},
|
|
1465
|
-
display: "flex",
|
|
1466
|
-
flexDirection: position.vertical === "top" ? "column" : "column-reverse",
|
|
1467
|
-
gap: 1,
|
|
1468
|
-
maxWidth: "400px",
|
|
1469
|
-
width: "auto"
|
|
1470
|
-
},
|
|
1471
|
-
children: notifications.map((notification) => /* @__PURE__ */ jsx4(NotificationItem, { notification, onClose: () => hideNotification(notification.id) }, notification.id))
|
|
1472
|
-
}
|
|
1473
|
-
) })
|
|
1474
|
-
] });
|
|
1475
|
-
};
|
|
1476
|
-
var NotificationItem = ({ notification, onClose }) => {
|
|
1477
|
-
const [open, setOpen] = useState4(true);
|
|
1478
|
-
const handleClose = useCallback2(
|
|
1479
|
-
(_event, reason) => {
|
|
1480
|
-
if (reason === "clickaway") return;
|
|
1481
|
-
setOpen(false);
|
|
1482
|
-
setTimeout(onClose, 300);
|
|
1483
|
-
},
|
|
1484
|
-
[onClose]
|
|
1485
|
-
);
|
|
1486
|
-
useEffect5(() => {
|
|
1487
|
-
if (!notification.persistent && notification.autoHideDuration) {
|
|
1488
|
-
const timer = setTimeout(() => {
|
|
1489
|
-
handleClose();
|
|
1490
|
-
}, notification.autoHideDuration);
|
|
1491
|
-
return () => clearTimeout(timer);
|
|
1492
|
-
}
|
|
1493
|
-
}, [notification.autoHideDuration, notification.persistent, handleClose]);
|
|
1494
|
-
return /* @__PURE__ */ jsx4(
|
|
1495
|
-
Snackbar,
|
|
1496
|
-
{
|
|
1497
|
-
open,
|
|
1498
|
-
onClose: handleClose,
|
|
1499
|
-
sx: {
|
|
1500
|
-
position: "relative",
|
|
1501
|
-
"& .MuiSnackbarContent-root": {
|
|
1502
|
-
minWidth: "auto"
|
|
1503
|
-
}
|
|
1504
|
-
},
|
|
1505
|
-
TransitionProps: {
|
|
1506
|
-
enter: true,
|
|
1507
|
-
exit: true
|
|
1508
|
-
},
|
|
1509
|
-
children: /* @__PURE__ */ jsx4(
|
|
1510
|
-
Alert,
|
|
1511
|
-
{
|
|
1512
|
-
variant: "filled",
|
|
1513
|
-
severity: notification.severity,
|
|
1514
|
-
onClose: handleClose,
|
|
1515
|
-
sx: {
|
|
1516
|
-
width: "100%",
|
|
1517
|
-
minWidth: "280px",
|
|
1518
|
-
maxWidth: "400px",
|
|
1519
|
-
wordBreak: "break-word"
|
|
1520
|
-
},
|
|
1521
|
-
children: notification.allowHtml ? /* @__PURE__ */ jsx4("span", { dangerouslySetInnerHTML: { __html: sanitizeNotificationContent(notification.message) } }) : /* @__PURE__ */ jsx4("span", { children: notification.message })
|
|
1522
|
-
}
|
|
1523
|
-
)
|
|
1524
|
-
}
|
|
1525
|
-
);
|
|
1526
|
-
};
|
|
1527
|
-
var useGlobalNotification = () => {
|
|
1528
|
-
const context = useContext4(GlobalNotificationContext);
|
|
1529
|
-
if (!context) {
|
|
1530
|
-
throw new Error("useGlobalNotification debe ser usado dentro de un GlobalNotificationProvider");
|
|
1531
|
-
}
|
|
1532
|
-
return context;
|
|
1533
|
-
};
|
|
1534
|
-
|
|
1535
|
-
// src/providers/SessionProvider.tsx
|
|
1536
|
-
import { Fragment, jsx as jsx5, jsxs as jsxs2 } from "react/jsx-runtime";
|
|
1537
|
-
var SessionContext = createContext5(void 0);
|
|
1538
|
-
function InnerSessionProvider({
|
|
1539
|
-
children,
|
|
1540
|
-
options = {},
|
|
1541
|
-
config: propConfig,
|
|
1542
|
-
showNotifications = false,
|
|
1543
|
-
notificationOptions = {}
|
|
1544
|
-
}) {
|
|
1545
|
-
let showNotificationFn;
|
|
1546
|
-
try {
|
|
1547
|
-
const { showNotification } = useGlobalNotification();
|
|
1548
|
-
showNotificationFn = showNotification;
|
|
1549
|
-
} catch {
|
|
1550
|
-
}
|
|
1551
|
-
const enhancedOptions = React5.useMemo(
|
|
1552
|
-
() => ({
|
|
1553
|
-
...options,
|
|
1554
|
-
showNotification: showNotificationFn,
|
|
1555
|
-
// TODO: Agregar translateFn cuando esté disponible
|
|
1556
|
-
onSessionExpired: () => {
|
|
1557
|
-
options.onSessionExpired?.();
|
|
1558
|
-
}
|
|
1559
|
-
}),
|
|
1560
|
-
[options, showNotificationFn]
|
|
1561
|
-
);
|
|
1562
|
-
const sessionHook = useSession(enhancedOptions);
|
|
1563
|
-
const resolvedConfig = useMemo2(() => {
|
|
1564
|
-
let publicApiKey;
|
|
1565
|
-
let env;
|
|
1566
|
-
let appName;
|
|
1567
|
-
let loginActions;
|
|
1568
|
-
let logo;
|
|
1569
|
-
let configSource = "unknown";
|
|
1570
|
-
if (propConfig?.publicApiKey) {
|
|
1571
|
-
publicApiKey = propConfig.publicApiKey;
|
|
1572
|
-
configSource = "props";
|
|
1573
|
-
}
|
|
1574
|
-
if (propConfig?.env) {
|
|
1575
|
-
env = propConfig.env;
|
|
1576
|
-
}
|
|
1577
|
-
if (propConfig?.appName) {
|
|
1578
|
-
appName = propConfig.appName;
|
|
1579
|
-
}
|
|
1580
|
-
if (propConfig?.loginActions) {
|
|
1581
|
-
loginActions = propConfig.loginActions;
|
|
1582
|
-
}
|
|
1583
|
-
if (propConfig?.logo) {
|
|
1584
|
-
logo = propConfig.logo;
|
|
1585
|
-
}
|
|
1586
|
-
if (!publicApiKey) {
|
|
1587
|
-
const cookieApiKey = getCookie("publicApiKey");
|
|
1588
|
-
const cookieEnv = getCookie("environment");
|
|
1589
|
-
const cookieAppName = getCookie("appName");
|
|
1590
|
-
const cookieLoginActions = getCookie("loginActions");
|
|
1591
|
-
const cookieLogo = getCookie("logo");
|
|
1592
|
-
if (cookieApiKey) {
|
|
1593
|
-
publicApiKey = cookieApiKey;
|
|
1594
|
-
configSource = "cookies";
|
|
1595
|
-
}
|
|
1596
|
-
if (cookieEnv && ["dev", "stg", "prod"].includes(cookieEnv)) {
|
|
1597
|
-
env = cookieEnv;
|
|
1598
|
-
}
|
|
1599
|
-
if (cookieAppName) {
|
|
1600
|
-
appName = decodeURIComponent(cookieAppName);
|
|
1601
|
-
}
|
|
1602
|
-
if (cookieLoginActions) {
|
|
1603
|
-
const decodedActions = decodeURIComponent(cookieLoginActions);
|
|
1604
|
-
loginActions = decodedActions.split(",").map((s) => s.trim()).filter(Boolean);
|
|
1605
|
-
}
|
|
1606
|
-
if (cookieLogo) logo = decodeURIComponent(cookieLogo);
|
|
1607
|
-
}
|
|
1608
|
-
return {
|
|
1609
|
-
publicApiKey,
|
|
1610
|
-
env,
|
|
1611
|
-
appName,
|
|
1612
|
-
loginActions,
|
|
1613
|
-
logo
|
|
1614
|
-
};
|
|
1615
|
-
}, [propConfig]);
|
|
1616
|
-
const sessionData = useMemo2(() => {
|
|
1617
|
-
if (!sessionHook.tokens?.accessToken || !sessionHook.isAuthenticated) {
|
|
1618
|
-
return null;
|
|
1619
|
-
}
|
|
1620
|
-
try {
|
|
1621
|
-
const decoded = decodeJwtSafely(sessionHook.tokens.accessToken);
|
|
1622
|
-
if (decoded && decoded.sub && decoded.email && decoded.subscriber) {
|
|
1623
|
-
const result = {
|
|
1624
|
-
_id: decoded.sub,
|
|
1625
|
-
email: decoded.email,
|
|
1626
|
-
subscriberKey: decoded.subscriber
|
|
1627
|
-
};
|
|
1628
|
-
Object.keys(decoded).forEach((key) => {
|
|
1629
|
-
if (!["sub", "email", "subscriber"].includes(key)) {
|
|
1630
|
-
result[key] = decoded[key];
|
|
1631
|
-
}
|
|
1632
|
-
});
|
|
1633
|
-
return result;
|
|
1634
|
-
}
|
|
1635
|
-
} catch (error) {
|
|
1636
|
-
console.error("Error decoding JWT token for sessionData:", error);
|
|
1637
|
-
}
|
|
1638
|
-
return null;
|
|
1639
|
-
}, [sessionHook.tokens?.accessToken, sessionHook.isAuthenticated]);
|
|
1640
|
-
const contextValue = {
|
|
1641
|
-
...sessionHook,
|
|
1642
|
-
sessionData,
|
|
1643
|
-
config: resolvedConfig
|
|
1644
|
-
};
|
|
1645
|
-
const notificationConfig = {
|
|
1646
|
-
enabled: showNotifications,
|
|
1647
|
-
maxNotifications: notificationOptions.maxNotifications || 5,
|
|
1648
|
-
defaultAutoHideDuration: notificationOptions.defaultAutoHideDuration || 6e3,
|
|
1649
|
-
position: notificationOptions.position || { vertical: "top", horizontal: "right" }
|
|
1650
|
-
};
|
|
1651
|
-
return /* @__PURE__ */ jsx5(SessionContext.Provider, { value: contextValue, children });
|
|
1652
|
-
}
|
|
1653
|
-
function SessionProvider(props) {
|
|
1654
|
-
const notificationConfig = {
|
|
1655
|
-
enabled: props.showNotifications,
|
|
1656
|
-
maxNotifications: props.notificationOptions?.maxNotifications || 5,
|
|
1657
|
-
defaultAutoHideDuration: props.notificationOptions?.defaultAutoHideDuration || 6e3,
|
|
1658
|
-
position: props.notificationOptions?.position || { vertical: "top", horizontal: "right" },
|
|
1659
|
-
allowHtml: props.notificationOptions?.allowHtml || false
|
|
1660
|
-
// Pasar allowHtml desde notificationOptions
|
|
1661
|
-
};
|
|
1662
|
-
return /* @__PURE__ */ jsx5(GlobalNotificationProvider, { ...notificationConfig, children: /* @__PURE__ */ jsx5(InnerSessionProvider, { ...props }) });
|
|
1663
|
-
}
|
|
1664
|
-
function useSessionContext() {
|
|
1665
|
-
const context = useContext5(SessionContext);
|
|
1666
|
-
if (context === void 0) {
|
|
1667
|
-
throw new Error("useSessionContext must be used within a SessionProvider");
|
|
1668
|
-
}
|
|
1669
|
-
return context;
|
|
1670
|
-
}
|
|
1671
|
-
function ProtectedRoute({ children, fallback = /* @__PURE__ */ jsx5("div", { children: "Please log in to access this content" }), redirectTo }) {
|
|
1672
|
-
const { isAuthenticated, isLoading, isInitialized } = useSessionContext();
|
|
1673
|
-
if (!isInitialized || isLoading) {
|
|
1674
|
-
return /* @__PURE__ */ jsx5("div", { children: "Loading..." });
|
|
1675
|
-
}
|
|
1676
|
-
if (!isAuthenticated) {
|
|
1677
|
-
if (redirectTo) {
|
|
1678
|
-
redirectTo();
|
|
1679
|
-
return null;
|
|
1680
|
-
}
|
|
1681
|
-
return /* @__PURE__ */ jsx5(Fragment, { children: fallback });
|
|
1682
|
-
}
|
|
1683
|
-
return /* @__PURE__ */ jsx5(Fragment, { children });
|
|
1684
|
-
}
|
|
1685
|
-
function SessionDebugInfo() {
|
|
1686
|
-
const session = useSessionContext();
|
|
1687
|
-
if (!session.isInitialized) {
|
|
1688
|
-
return /* @__PURE__ */ jsx5("div", { children: "Session not initialized" });
|
|
1689
|
-
}
|
|
1690
|
-
return /* @__PURE__ */ jsxs2(
|
|
1691
|
-
"div",
|
|
1692
|
-
{
|
|
1693
|
-
style: {
|
|
1694
|
-
padding: "10px",
|
|
1695
|
-
margin: "10px",
|
|
1696
|
-
border: "1px solid #ccc",
|
|
1697
|
-
borderRadius: "4px",
|
|
1698
|
-
fontSize: "12px",
|
|
1699
|
-
fontFamily: "monospace"
|
|
1700
|
-
},
|
|
1701
|
-
children: [
|
|
1702
|
-
/* @__PURE__ */ jsx5("h4", { children: "Session Debug Info" }),
|
|
1703
|
-
/* @__PURE__ */ jsxs2("div", { children: [
|
|
1704
|
-
/* @__PURE__ */ jsx5("strong", { children: "Authenticated:" }),
|
|
1705
|
-
" ",
|
|
1706
|
-
session.isAuthenticated ? "Yes" : "No"
|
|
1707
|
-
] }),
|
|
1708
|
-
/* @__PURE__ */ jsxs2("div", { children: [
|
|
1709
|
-
/* @__PURE__ */ jsx5("strong", { children: "Loading:" }),
|
|
1710
|
-
" ",
|
|
1711
|
-
session.isLoading ? "Yes" : "No"
|
|
1712
|
-
] }),
|
|
1713
|
-
/* @__PURE__ */ jsxs2("div", { children: [
|
|
1714
|
-
/* @__PURE__ */ jsx5("strong", { children: "Error:" }),
|
|
1715
|
-
" ",
|
|
1716
|
-
session.error || "None"
|
|
1717
|
-
] }),
|
|
1718
|
-
session.tokens && /* @__PURE__ */ jsxs2(Fragment, { children: [
|
|
1719
|
-
/* @__PURE__ */ jsxs2("div", { children: [
|
|
1720
|
-
/* @__PURE__ */ jsx5("strong", { children: "Access Token:" }),
|
|
1721
|
-
" ",
|
|
1722
|
-
session.tokens.accessToken.substring(0, 20),
|
|
1723
|
-
"..."
|
|
1724
|
-
] }),
|
|
1725
|
-
/* @__PURE__ */ jsxs2("div", { children: [
|
|
1726
|
-
/* @__PURE__ */ jsx5("strong", { children: "Refresh Token:" }),
|
|
1727
|
-
" ",
|
|
1728
|
-
session.tokens.refreshToken.substring(0, 20),
|
|
1729
|
-
"..."
|
|
1730
|
-
] }),
|
|
1731
|
-
/* @__PURE__ */ jsxs2("div", { children: [
|
|
1732
|
-
/* @__PURE__ */ jsx5("strong", { children: "Access Expires In:" }),
|
|
1733
|
-
" ",
|
|
1734
|
-
Math.round(session.expiresIn / 1e3 / 60),
|
|
1735
|
-
" minutes"
|
|
1736
|
-
] }),
|
|
1737
|
-
/* @__PURE__ */ jsxs2("div", { children: [
|
|
1738
|
-
/* @__PURE__ */ jsx5("strong", { children: "Refresh Expires In:" }),
|
|
1739
|
-
" ",
|
|
1740
|
-
Math.round(session.refreshExpiresIn / 1e3 / 60 / 60),
|
|
1741
|
-
" hours"
|
|
1742
|
-
] }),
|
|
1743
|
-
/* @__PURE__ */ jsxs2("div", { children: [
|
|
1744
|
-
/* @__PURE__ */ jsx5("strong", { children: "Expiring Soon:" }),
|
|
1745
|
-
" ",
|
|
1746
|
-
session.isExpiringSoon ? "Yes" : "No"
|
|
1747
|
-
] })
|
|
1748
|
-
] })
|
|
1749
|
-
]
|
|
1750
|
-
}
|
|
1751
|
-
);
|
|
1752
|
-
}
|
|
1753
|
-
|
|
1754
|
-
// src/components/CrudifyLogin/Forms/LoginForm.tsx
|
|
1755
|
-
import { useEffect as useEffect6, useRef } from "react";
|
|
1756
|
-
import { Typography, TextField, Button, Box as Box2, CircularProgress, Alert as Alert2, Link } from "@mui/material";
|
|
1757
|
-
|
|
1758
|
-
// src/utils/errorHandler.ts
|
|
1759
|
-
var ERROR_CODES = {
|
|
1760
|
-
// Authentication Errors
|
|
1761
|
-
INVALID_CREDENTIALS: "INVALID_CREDENTIALS",
|
|
1762
|
-
UNAUTHORIZED: "UNAUTHORIZED",
|
|
1763
|
-
INVALID_API_KEY: "INVALID_API_KEY",
|
|
1764
|
-
USER_NOT_FOUND: "USER_NOT_FOUND",
|
|
1765
|
-
USER_NOT_ACTIVE: "USER_NOT_ACTIVE",
|
|
1766
|
-
NO_PERMISSION: "NO_PERMISSION",
|
|
1767
|
-
// Data Errors
|
|
1768
|
-
ITEM_NOT_FOUND: "ITEM_NOT_FOUND",
|
|
1769
|
-
NOT_FOUND: "NOT_FOUND",
|
|
1770
|
-
IN_USE: "IN_USE",
|
|
1771
|
-
// Validation Errors
|
|
1772
|
-
FIELD_ERROR: "FIELD_ERROR",
|
|
1773
|
-
BAD_REQUEST: "BAD_REQUEST",
|
|
1774
|
-
INVALID_EMAIL: "INVALID_EMAIL",
|
|
1775
|
-
INVALID_CODE: "INVALID_CODE",
|
|
1776
|
-
// System Errors
|
|
1777
|
-
INTERNAL_SERVER_ERROR: "INTERNAL_SERVER_ERROR",
|
|
1778
|
-
DATABASE_CONNECTION_ERROR: "DATABASE_CONNECTION_ERROR",
|
|
1779
|
-
INVALID_CONFIGURATION: "INVALID_CONFIGURATION",
|
|
1780
|
-
UNKNOWN_OPERATION: "UNKNOWN_OPERATION",
|
|
1781
|
-
// Rate Limiting
|
|
1782
|
-
TOO_MANY_REQUESTS: "TOO_MANY_REQUESTS",
|
|
1783
|
-
// Network Errors
|
|
1784
|
-
NETWORK_ERROR: "NETWORK_ERROR",
|
|
1785
|
-
TIMEOUT_ERROR: "TIMEOUT_ERROR"
|
|
1786
|
-
};
|
|
1787
|
-
var ERROR_SEVERITY_MAP = {
|
|
1788
|
-
// Authentication - warning (user can fix)
|
|
1789
|
-
[ERROR_CODES.INVALID_CREDENTIALS]: "warning",
|
|
1790
|
-
[ERROR_CODES.UNAUTHORIZED]: "warning",
|
|
1791
|
-
[ERROR_CODES.INVALID_API_KEY]: "error",
|
|
1792
|
-
[ERROR_CODES.USER_NOT_FOUND]: "warning",
|
|
1793
|
-
[ERROR_CODES.USER_NOT_ACTIVE]: "warning",
|
|
1794
|
-
[ERROR_CODES.NO_PERMISSION]: "warning",
|
|
1795
|
-
// Data - info (might be expected)
|
|
1796
|
-
[ERROR_CODES.ITEM_NOT_FOUND]: "info",
|
|
1797
|
-
[ERROR_CODES.NOT_FOUND]: "info",
|
|
1798
|
-
[ERROR_CODES.IN_USE]: "warning",
|
|
1799
|
-
// Validation - warning (user input issue)
|
|
1800
|
-
[ERROR_CODES.FIELD_ERROR]: "warning",
|
|
1801
|
-
[ERROR_CODES.BAD_REQUEST]: "warning",
|
|
1802
|
-
[ERROR_CODES.INVALID_EMAIL]: "warning",
|
|
1803
|
-
[ERROR_CODES.INVALID_CODE]: "warning",
|
|
1804
|
-
// System - error (server issue)
|
|
1805
|
-
[ERROR_CODES.INTERNAL_SERVER_ERROR]: "error",
|
|
1806
|
-
[ERROR_CODES.DATABASE_CONNECTION_ERROR]: "error",
|
|
1807
|
-
[ERROR_CODES.INVALID_CONFIGURATION]: "error",
|
|
1808
|
-
[ERROR_CODES.UNKNOWN_OPERATION]: "error",
|
|
1809
|
-
// Rate limiting - warning with higher duration
|
|
1810
|
-
[ERROR_CODES.TOO_MANY_REQUESTS]: "warning",
|
|
1811
|
-
// Network - error
|
|
1812
|
-
[ERROR_CODES.NETWORK_ERROR]: "error",
|
|
1813
|
-
[ERROR_CODES.TIMEOUT_ERROR]: "error"
|
|
1814
|
-
};
|
|
1815
|
-
function parseApiError(response) {
|
|
1816
|
-
const errors = [];
|
|
1817
|
-
try {
|
|
1818
|
-
const apiResponse = response;
|
|
1819
|
-
if (apiResponse.data && typeof apiResponse.data === "object") {
|
|
1820
|
-
const responseData = apiResponse.data;
|
|
1821
|
-
if (responseData.response) {
|
|
1822
|
-
const { status, fieldsWarning } = responseData.response;
|
|
1823
|
-
if (fieldsWarning && typeof fieldsWarning === "object") {
|
|
1824
|
-
Object.entries(fieldsWarning).forEach(([field, messages]) => {
|
|
1825
|
-
if (Array.isArray(messages) && messages.length > 0) {
|
|
1826
|
-
errors.push({
|
|
1827
|
-
code: ERROR_CODES.FIELD_ERROR,
|
|
1828
|
-
message: messages[0],
|
|
1829
|
-
severity: "warning",
|
|
1830
|
-
field
|
|
1831
|
-
});
|
|
1832
|
-
}
|
|
1833
|
-
});
|
|
1834
|
-
}
|
|
1835
|
-
if (status && typeof status === "string") {
|
|
1836
|
-
const errorCode = status;
|
|
1837
|
-
if (ERROR_SEVERITY_MAP[errorCode]) {
|
|
1838
|
-
errors.push({
|
|
1839
|
-
code: errorCode,
|
|
1840
|
-
message: getErrorMessage(errorCode),
|
|
1841
|
-
severity: ERROR_SEVERITY_MAP[errorCode]
|
|
1842
|
-
});
|
|
1843
|
-
}
|
|
1844
|
-
}
|
|
1845
|
-
}
|
|
1846
|
-
}
|
|
1847
|
-
if (apiResponse.errors) {
|
|
1848
|
-
if (typeof apiResponse.errors === "string") {
|
|
1849
|
-
errors.push({
|
|
1850
|
-
code: ERROR_CODES.BAD_REQUEST,
|
|
1851
|
-
message: apiResponse.errors,
|
|
1852
|
-
severity: "warning"
|
|
1853
|
-
});
|
|
1854
|
-
} else if (typeof apiResponse.errors === "object") {
|
|
1855
|
-
const errorObj = apiResponse.errors;
|
|
1856
|
-
Object.entries(errorObj).forEach(([field, messages]) => {
|
|
1857
|
-
if (Array.isArray(messages) && messages.length > 0) {
|
|
1858
|
-
if (field === "_error") {
|
|
1859
|
-
messages.forEach((msg) => {
|
|
1860
|
-
const errorCode = typeof msg === "string" && isValidErrorCode(msg) ? msg : ERROR_CODES.BAD_REQUEST;
|
|
1861
|
-
errors.push({
|
|
1862
|
-
code: errorCode,
|
|
1863
|
-
message: typeof msg === "string" ? msg : getErrorMessage(errorCode),
|
|
1864
|
-
severity: ERROR_SEVERITY_MAP[errorCode] || "warning"
|
|
1865
|
-
});
|
|
1866
|
-
});
|
|
1867
|
-
} else if (field === "_graphql") {
|
|
1868
|
-
messages.forEach((msg) => {
|
|
1869
|
-
if (typeof msg === "string") {
|
|
1870
|
-
const errorCode = isValidErrorCode(msg) ? msg : ERROR_CODES.BAD_REQUEST;
|
|
1871
|
-
errors.push({
|
|
1872
|
-
code: errorCode,
|
|
1873
|
-
message: getErrorMessage(errorCode),
|
|
1874
|
-
severity: ERROR_SEVERITY_MAP[errorCode] || "warning"
|
|
1875
|
-
});
|
|
1876
|
-
}
|
|
1877
|
-
});
|
|
1878
|
-
} else {
|
|
1879
|
-
errors.push({
|
|
1880
|
-
code: ERROR_CODES.FIELD_ERROR,
|
|
1881
|
-
message: typeof messages[0] === "string" ? messages[0] : "Validation error",
|
|
1882
|
-
severity: "warning",
|
|
1883
|
-
field
|
|
1884
|
-
});
|
|
1885
|
-
}
|
|
1886
|
-
}
|
|
1887
|
-
});
|
|
1888
|
-
}
|
|
1889
|
-
}
|
|
1890
|
-
if (errors.length === 0 && apiResponse.success === false) {
|
|
1891
|
-
errors.push({
|
|
1892
|
-
code: ERROR_CODES.BAD_REQUEST,
|
|
1893
|
-
message: "Request failed",
|
|
1894
|
-
severity: "warning"
|
|
1895
|
-
});
|
|
1896
|
-
}
|
|
1897
|
-
} catch (error) {
|
|
1898
|
-
errors.push({
|
|
1899
|
-
code: ERROR_CODES.INTERNAL_SERVER_ERROR,
|
|
1900
|
-
message: "Failed to parse error response",
|
|
1901
|
-
severity: "error",
|
|
1902
|
-
details: { originalError: error }
|
|
1903
|
-
});
|
|
1904
|
-
}
|
|
1905
|
-
return errors.length > 0 ? errors : [
|
|
1906
|
-
{
|
|
1907
|
-
code: ERROR_CODES.INTERNAL_SERVER_ERROR,
|
|
1908
|
-
message: "Unknown error occurred",
|
|
1909
|
-
severity: "error"
|
|
1910
|
-
}
|
|
1911
|
-
];
|
|
1912
|
-
}
|
|
1913
|
-
function parseTransactionError(response) {
|
|
1914
|
-
try {
|
|
1915
|
-
const transactionResponse = response;
|
|
1916
|
-
if (transactionResponse.data && Array.isArray(transactionResponse.data)) {
|
|
1917
|
-
const errors = [];
|
|
1918
|
-
transactionResponse.data.forEach((item, index) => {
|
|
1919
|
-
if (item.response?.status === "TOO_MANY_REQUESTS") {
|
|
1920
|
-
errors.push({
|
|
1921
|
-
code: ERROR_CODES.TOO_MANY_REQUESTS,
|
|
1922
|
-
message: getErrorMessage(ERROR_CODES.TOO_MANY_REQUESTS),
|
|
1923
|
-
severity: "warning",
|
|
1924
|
-
details: { transactionIndex: index }
|
|
1925
|
-
});
|
|
1926
|
-
} else if (!item.response || item.response.status !== "OK") {
|
|
1927
|
-
errors.push({
|
|
1928
|
-
code: ERROR_CODES.BAD_REQUEST,
|
|
1929
|
-
message: "Transaction failed",
|
|
1930
|
-
severity: "warning",
|
|
1931
|
-
details: { transactionIndex: index, response: item.response }
|
|
1932
|
-
});
|
|
1933
|
-
}
|
|
1934
|
-
});
|
|
1935
|
-
return errors;
|
|
1936
|
-
}
|
|
1937
|
-
return parseApiError(response);
|
|
1938
|
-
} catch (error) {
|
|
1939
|
-
return [
|
|
1940
|
-
{
|
|
1941
|
-
code: ERROR_CODES.INTERNAL_SERVER_ERROR,
|
|
1942
|
-
message: "Failed to parse transaction error",
|
|
1943
|
-
severity: "error",
|
|
1944
|
-
details: { originalError: error }
|
|
1945
|
-
}
|
|
1946
|
-
];
|
|
1947
|
-
}
|
|
1948
|
-
}
|
|
1949
|
-
function isValidErrorCode(code) {
|
|
1950
|
-
return Object.values(ERROR_CODES).includes(code);
|
|
1951
|
-
}
|
|
1952
|
-
function getErrorMessage(code) {
|
|
1953
|
-
const messages = {
|
|
1954
|
-
[ERROR_CODES.INVALID_CREDENTIALS]: "Invalid email or password",
|
|
1955
|
-
[ERROR_CODES.UNAUTHORIZED]: "You are not authorized to perform this action",
|
|
1956
|
-
[ERROR_CODES.INVALID_API_KEY]: "Invalid API key",
|
|
1957
|
-
[ERROR_CODES.USER_NOT_FOUND]: "User not found",
|
|
1958
|
-
[ERROR_CODES.USER_NOT_ACTIVE]: "User account is not active",
|
|
1959
|
-
[ERROR_CODES.NO_PERMISSION]: "You do not have permission to perform this action",
|
|
1960
|
-
[ERROR_CODES.ITEM_NOT_FOUND]: "Item not found",
|
|
1961
|
-
[ERROR_CODES.NOT_FOUND]: "Resource not found",
|
|
1962
|
-
[ERROR_CODES.IN_USE]: "Resource is currently in use",
|
|
1963
|
-
[ERROR_CODES.FIELD_ERROR]: "Validation error",
|
|
1964
|
-
[ERROR_CODES.BAD_REQUEST]: "Invalid request",
|
|
1965
|
-
[ERROR_CODES.INVALID_EMAIL]: "Please enter a valid email address",
|
|
1966
|
-
[ERROR_CODES.INVALID_CODE]: "Invalid or expired code",
|
|
1967
|
-
[ERROR_CODES.INTERNAL_SERVER_ERROR]: "Internal server error",
|
|
1968
|
-
[ERROR_CODES.DATABASE_CONNECTION_ERROR]: "Database connection error",
|
|
1969
|
-
[ERROR_CODES.INVALID_CONFIGURATION]: "Invalid configuration",
|
|
1970
|
-
[ERROR_CODES.UNKNOWN_OPERATION]: "Unknown operation",
|
|
1971
|
-
[ERROR_CODES.TOO_MANY_REQUESTS]: "Too many requests. Please try again later.",
|
|
1972
|
-
[ERROR_CODES.NETWORK_ERROR]: "Network error. Please check your connection.",
|
|
1973
|
-
[ERROR_CODES.TIMEOUT_ERROR]: "Request timed out. Please try again."
|
|
1974
|
-
};
|
|
1975
|
-
return messages[code] || "An unknown error occurred";
|
|
1976
|
-
}
|
|
1977
|
-
function parseJavaScriptError(error) {
|
|
1978
|
-
if (error instanceof Error) {
|
|
1979
|
-
if (error.name === "AbortError") {
|
|
1980
|
-
return {
|
|
1981
|
-
code: ERROR_CODES.TIMEOUT_ERROR,
|
|
1982
|
-
message: "Request was cancelled",
|
|
1983
|
-
severity: "info"
|
|
1984
|
-
};
|
|
1985
|
-
}
|
|
1986
|
-
if (error.message.includes("NetworkError") || error.message.includes("Failed to fetch")) {
|
|
1987
|
-
return {
|
|
1988
|
-
code: ERROR_CODES.NETWORK_ERROR,
|
|
1989
|
-
message: getErrorMessage(ERROR_CODES.NETWORK_ERROR),
|
|
1990
|
-
severity: "error"
|
|
1991
|
-
};
|
|
1992
|
-
}
|
|
1993
|
-
return {
|
|
1994
|
-
code: ERROR_CODES.INTERNAL_SERVER_ERROR,
|
|
1995
|
-
message: error.message || "An unexpected error occurred",
|
|
1996
|
-
severity: "error",
|
|
1997
|
-
details: { originalError: error }
|
|
1998
|
-
};
|
|
1999
|
-
}
|
|
2000
|
-
return {
|
|
2001
|
-
code: ERROR_CODES.INTERNAL_SERVER_ERROR,
|
|
2002
|
-
message: "An unknown error occurred",
|
|
2003
|
-
severity: "error",
|
|
2004
|
-
details: { originalError: error }
|
|
2005
|
-
};
|
|
2006
|
-
}
|
|
2007
|
-
function handleCrudifyError(error) {
|
|
2008
|
-
if (error instanceof Error) {
|
|
2009
|
-
return [parseJavaScriptError(error)];
|
|
2010
|
-
}
|
|
2011
|
-
if (typeof error === "object" && error !== null) {
|
|
2012
|
-
const response = error;
|
|
2013
|
-
if (response.data && Array.isArray(response.data)) {
|
|
2014
|
-
return parseTransactionError(error);
|
|
2015
|
-
}
|
|
2016
|
-
return parseApiError(error);
|
|
2017
|
-
}
|
|
2018
|
-
return [
|
|
2019
|
-
{
|
|
2020
|
-
code: ERROR_CODES.INTERNAL_SERVER_ERROR,
|
|
2021
|
-
message: "An unknown error occurred",
|
|
2022
|
-
severity: "error",
|
|
2023
|
-
details: { originalError: error }
|
|
2024
|
-
}
|
|
2025
|
-
];
|
|
2026
|
-
}
|
|
2027
|
-
|
|
2028
|
-
// src/components/CrudifyLogin/Forms/LoginForm.tsx
|
|
2029
|
-
import { Fragment as Fragment2, jsx as jsx6, jsxs as jsxs3 } from "react/jsx-runtime";
|
|
2030
|
-
var LoginForm = ({ onScreenChange, onExternalNavigate, onLoginSuccess, onError, redirectUrl = "/" }) => {
|
|
2031
|
-
const { crudify: crudify7 } = useCrudify();
|
|
2032
|
-
const { state, updateFormData, setFieldError, clearErrors, setLoading } = useLoginState();
|
|
2033
|
-
const { login: sessionLogin } = useSessionContext();
|
|
2034
|
-
const translationContext = useTranslation();
|
|
2035
|
-
const { t } = translationContext;
|
|
2036
|
-
const i18n = translationContext.i18n;
|
|
2037
|
-
const usernameInputRef = useRef(null);
|
|
2038
|
-
const errorTranslator = createErrorTranslator(t, {
|
|
2039
|
-
currentLanguage: i18n?.language,
|
|
2040
|
-
enableDebug: false
|
|
2041
|
-
});
|
|
2042
|
-
const getRedirectUrl = () => {
|
|
2043
|
-
if (state.searchParams.redirect) {
|
|
2044
|
-
try {
|
|
2045
|
-
const decodedPath = decodeURIComponent(state.searchParams.redirect);
|
|
2046
|
-
if (decodedPath.startsWith("/") && !decodedPath.startsWith("//")) {
|
|
2047
|
-
return decodedPath;
|
|
2048
|
-
}
|
|
2049
|
-
} catch (error) {
|
|
2050
|
-
}
|
|
2051
|
-
}
|
|
2052
|
-
return redirectUrl || "/";
|
|
2053
|
-
};
|
|
2054
|
-
useEffect6(() => {
|
|
2055
|
-
const timer = setTimeout(() => {
|
|
2056
|
-
if (usernameInputRef.current) usernameInputRef.current.focus();
|
|
2057
|
-
}, 100);
|
|
2058
|
-
return () => clearTimeout(timer);
|
|
2059
|
-
}, []);
|
|
2060
|
-
const translateError2 = (parsedError) => {
|
|
2061
|
-
console.log("\u{1F50D} [LoginForm] Translating parsed error:", parsedError);
|
|
2062
|
-
const result = errorTranslator.translateError({
|
|
2063
|
-
code: parsedError.code,
|
|
2064
|
-
message: parsedError.message,
|
|
2065
|
-
field: parsedError.field
|
|
2066
|
-
});
|
|
2067
|
-
console.log("\u{1F50D} [LoginForm] Translation result:", result);
|
|
2068
|
-
return result;
|
|
2069
|
-
};
|
|
2070
|
-
const handleLogin = async () => {
|
|
2071
|
-
if (state.loading) return;
|
|
2072
|
-
if (!state.formData.username.trim()) {
|
|
2073
|
-
setFieldError("username", t("login.usernameRequired"));
|
|
2074
|
-
return;
|
|
2075
|
-
}
|
|
2076
|
-
if (!state.formData.password.trim()) {
|
|
2077
|
-
setFieldError("password", t("login.passwordRequired"));
|
|
2078
|
-
return;
|
|
2079
|
-
}
|
|
2080
|
-
clearErrors();
|
|
2081
|
-
setLoading(true);
|
|
2082
|
-
try {
|
|
2083
|
-
const response = await sessionLogin(state.formData.username, state.formData.password);
|
|
2084
|
-
setLoading(false);
|
|
2085
|
-
if (response.success) {
|
|
2086
|
-
console.log("\u{1F510} LoginForm - Login successful via SessionProvider, calling onLoginSuccess");
|
|
2087
|
-
const finalRedirectUrl = getRedirectUrl();
|
|
2088
|
-
if (onLoginSuccess) {
|
|
2089
|
-
onLoginSuccess(response.data, finalRedirectUrl);
|
|
2090
|
-
}
|
|
2091
|
-
} else {
|
|
2092
|
-
const errorToHandle = response.rawResponse || response;
|
|
2093
|
-
handleLoginError(errorToHandle);
|
|
2094
|
-
}
|
|
2095
|
-
} catch (error) {
|
|
2096
|
-
setLoading(false);
|
|
2097
|
-
const parsedErrors = handleCrudifyError(error);
|
|
2098
|
-
const translatedErrors = parsedErrors.map(translateError2);
|
|
2099
|
-
setFieldError("global", translatedErrors);
|
|
2100
|
-
if (onError) {
|
|
2101
|
-
onError(translatedErrors.join(", "));
|
|
2102
|
-
}
|
|
2103
|
-
}
|
|
2104
|
-
};
|
|
2105
|
-
const handleLoginError = (response) => {
|
|
2106
|
-
const parsedErrors = handleCrudifyError(response);
|
|
2107
|
-
parsedErrors.forEach((error) => {
|
|
2108
|
-
if (error.field) {
|
|
2109
|
-
setFieldError(error.field, translateError2(error));
|
|
2110
|
-
} else {
|
|
2111
|
-
const currentGlobalErrors = state.errors.global || [];
|
|
2112
|
-
setFieldError("global", [...currentGlobalErrors, translateError2(error)]);
|
|
2113
|
-
}
|
|
2114
|
-
});
|
|
2115
|
-
};
|
|
2116
|
-
const handleSubmit = (e) => {
|
|
2117
|
-
e.preventDefault();
|
|
2118
|
-
handleLogin();
|
|
2119
|
-
};
|
|
2120
|
-
const handleKeyDown = (e) => {
|
|
2121
|
-
if (e.key === "Enter" && !state.loading) {
|
|
2122
|
-
e.preventDefault();
|
|
2123
|
-
handleLogin();
|
|
2124
|
-
}
|
|
2125
|
-
};
|
|
2126
|
-
return /* @__PURE__ */ jsxs3(Fragment2, { children: [
|
|
2127
|
-
/* @__PURE__ */ jsxs3(
|
|
2128
|
-
Box2,
|
|
2129
|
-
{
|
|
2130
|
-
component: "form",
|
|
2131
|
-
noValidate: true,
|
|
2132
|
-
onSubmit: handleSubmit,
|
|
2133
|
-
onKeyDown: handleKeyDown,
|
|
2134
|
-
sx: { width: "100%", display: "flex", flexDirection: "column", gap: 2 },
|
|
2135
|
-
children: [
|
|
2136
|
-
/* @__PURE__ */ jsxs3(Box2, { sx: { mb: 1 }, children: [
|
|
2137
|
-
/* @__PURE__ */ jsx6(
|
|
2138
|
-
Typography,
|
|
2139
|
-
{
|
|
2140
|
-
variant: "body2",
|
|
2141
|
-
component: "label",
|
|
2142
|
-
htmlFor: "email",
|
|
2143
|
-
sx: { display: "block", fontWeight: 500, color: "grey.700", mb: 0.5 },
|
|
2144
|
-
children: t("login.usernameOrEmailLabel")
|
|
2145
|
-
}
|
|
2146
|
-
),
|
|
2147
|
-
/* @__PURE__ */ jsx6(
|
|
2148
|
-
TextField,
|
|
2149
|
-
{
|
|
2150
|
-
fullWidth: true,
|
|
2151
|
-
id: "email",
|
|
2152
|
-
name: "email",
|
|
2153
|
-
type: "email",
|
|
2154
|
-
value: state.formData.username,
|
|
2155
|
-
disabled: state.loading,
|
|
2156
|
-
onChange: (e) => updateFormData({ username: e.target.value }),
|
|
2157
|
-
error: !!state.errors.username,
|
|
2158
|
-
helperText: state.errors.username,
|
|
2159
|
-
autoComplete: "email",
|
|
2160
|
-
placeholder: t("login.usernameOrEmailPlaceholder"),
|
|
2161
|
-
inputRef: usernameInputRef,
|
|
2162
|
-
required: true
|
|
2163
|
-
}
|
|
2164
|
-
)
|
|
2165
|
-
] }),
|
|
2166
|
-
/* @__PURE__ */ jsxs3(Box2, { sx: { mb: 1 }, children: [
|
|
2167
|
-
/* @__PURE__ */ jsx6(
|
|
2168
|
-
Typography,
|
|
2169
|
-
{
|
|
2170
|
-
variant: "body2",
|
|
2171
|
-
component: "label",
|
|
2172
|
-
htmlFor: "password",
|
|
2173
|
-
sx: { display: "block", fontWeight: 500, color: "grey.700", mb: 0.5 },
|
|
2174
|
-
children: t("login.passwordLabel")
|
|
2175
|
-
}
|
|
2176
|
-
),
|
|
2177
|
-
/* @__PURE__ */ jsx6(
|
|
2178
|
-
TextField,
|
|
2179
|
-
{
|
|
2180
|
-
fullWidth: true,
|
|
2181
|
-
id: "password",
|
|
2182
|
-
name: "password",
|
|
2183
|
-
type: "password",
|
|
2184
|
-
value: state.formData.password,
|
|
2185
|
-
disabled: state.loading,
|
|
2186
|
-
onChange: (e) => updateFormData({ password: e.target.value }),
|
|
2187
|
-
error: !!state.errors.password,
|
|
2188
|
-
helperText: state.errors.password,
|
|
2189
|
-
autoComplete: "current-password",
|
|
2190
|
-
placeholder: t("login.passwordPlaceholder"),
|
|
2191
|
-
required: true
|
|
2192
|
-
}
|
|
2193
|
-
)
|
|
2194
|
-
] }),
|
|
2195
|
-
state.config.loginActions?.includes("forgotPassword") && /* @__PURE__ */ jsx6(Box2, { sx: { display: "flex", justifyContent: "flex-end", alignItems: "center" }, children: /* @__PURE__ */ jsx6(
|
|
2196
|
-
Link,
|
|
2197
|
-
{
|
|
2198
|
-
sx: { cursor: "pointer" },
|
|
2199
|
-
onClick: () => {
|
|
2200
|
-
onScreenChange?.("forgotPassword", state.searchParams);
|
|
2201
|
-
},
|
|
2202
|
-
variant: "body2",
|
|
2203
|
-
color: "secondary",
|
|
2204
|
-
children: t("login.forgotPasswordLink")
|
|
2205
|
-
}
|
|
2206
|
-
) }),
|
|
2207
|
-
/* @__PURE__ */ jsx6(Button, { disabled: state.loading, type: "submit", fullWidth: true, variant: "contained", color: "primary", sx: { mt: 1, mb: 2 }, children: state.loading ? /* @__PURE__ */ jsx6(CircularProgress, { size: 20 }) : t("login.loginButton") })
|
|
2208
|
-
]
|
|
2209
|
-
}
|
|
2210
|
-
),
|
|
2211
|
-
/* @__PURE__ */ jsx6(Box2, { children: state.errors.global && state.errors.global.length > 0 && state.errors.global.map((error, index) => /* @__PURE__ */ jsx6(Alert2, { variant: "filled", sx: { mt: 2 }, severity: "error", children: /* @__PURE__ */ jsx6("div", { children: error }) }, index)) }),
|
|
2212
|
-
state.config.loginActions?.includes("createUser") && /* @__PURE__ */ jsxs3(Typography, { variant: "body2", align: "center", sx: { color: "text.secondary", mt: 3 }, children: [
|
|
2213
|
-
t("login.noAccountPrompt"),
|
|
2214
|
-
" ",
|
|
2215
|
-
/* @__PURE__ */ jsx6(
|
|
2216
|
-
Link,
|
|
2217
|
-
{
|
|
2218
|
-
sx: { cursor: "pointer" },
|
|
2219
|
-
onClick: () => {
|
|
2220
|
-
const searchString = Object.keys(state.searchParams).length > 0 ? `?${new URLSearchParams(state.searchParams).toString()}` : "";
|
|
2221
|
-
const signupUrl = `/public/users/create${searchString}`;
|
|
2222
|
-
onExternalNavigate?.(signupUrl);
|
|
2223
|
-
},
|
|
2224
|
-
fontWeight: "medium",
|
|
2225
|
-
color: "secondary",
|
|
2226
|
-
children: t("login.signUpLink")
|
|
2227
|
-
}
|
|
2228
|
-
)
|
|
2229
|
-
] })
|
|
2230
|
-
] });
|
|
2231
|
-
};
|
|
2232
|
-
var LoginForm_default = LoginForm;
|
|
2233
|
-
|
|
2234
|
-
// src/components/CrudifyLogin/Forms/ForgotPasswordForm.tsx
|
|
2235
|
-
import { useState as useState5 } from "react";
|
|
2236
|
-
import { Typography as Typography2, TextField as TextField2, Button as Button2, Box as Box3, CircularProgress as CircularProgress2, Alert as Alert3, Link as Link2 } from "@mui/material";
|
|
2237
|
-
import { Fragment as Fragment3, jsx as jsx7, jsxs as jsxs4 } from "react/jsx-runtime";
|
|
2238
|
-
var ForgotPasswordForm = ({ onScreenChange, onError }) => {
|
|
2239
|
-
const { crudify: crudify7 } = useCrudify();
|
|
2240
|
-
const [email, setEmail] = useState5("");
|
|
2241
|
-
const [loading, setLoading] = useState5(false);
|
|
2242
|
-
const [errors, setErrors] = useState5([]);
|
|
2243
|
-
const [helperTextEmail, setHelperTextEmail] = useState5(null);
|
|
2244
|
-
const [emailSent, setEmailSent] = useState5(false);
|
|
2245
|
-
const [codeAlreadyExists, setCodeAlreadyExists] = useState5(false);
|
|
2246
|
-
const { t } = useTranslation();
|
|
2247
|
-
const translateError2 = (parsedError) => {
|
|
2248
|
-
const possibleKeys = [
|
|
2249
|
-
`errors.auth.${parsedError.code}`,
|
|
2250
|
-
`errors.data.${parsedError.code}`,
|
|
2251
|
-
`errors.system.${parsedError.code}`,
|
|
2252
|
-
`errors.${parsedError.code}`,
|
|
2253
|
-
`forgotPassword.${parsedError.code.toLowerCase()}`
|
|
2254
|
-
];
|
|
2255
|
-
for (const key of possibleKeys) {
|
|
2256
|
-
const translated = t(key);
|
|
2257
|
-
if (translated !== key) {
|
|
2258
|
-
return translated;
|
|
2259
|
-
}
|
|
2260
|
-
}
|
|
2261
|
-
return parsedError.message || t("error.unknown");
|
|
2262
|
-
};
|
|
2263
|
-
const validateEmail = (email2) => {
|
|
2264
|
-
const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
|
|
2265
|
-
return emailRegex.test(email2);
|
|
2266
|
-
};
|
|
2267
|
-
const handleSubmit = async () => {
|
|
2268
|
-
if (loading || !crudify7) return;
|
|
2269
|
-
setErrors([]);
|
|
2270
|
-
setHelperTextEmail(null);
|
|
2271
|
-
if (!email) {
|
|
2272
|
-
setHelperTextEmail(t("forgotPassword.emailRequired"));
|
|
2273
|
-
return;
|
|
2274
|
-
}
|
|
2275
|
-
if (!validateEmail(email)) {
|
|
2276
|
-
setHelperTextEmail(t("forgotPassword.invalidEmail"));
|
|
2277
|
-
return;
|
|
2278
|
-
}
|
|
2279
|
-
setLoading(true);
|
|
2280
|
-
try {
|
|
2281
|
-
const data = [{ operation: "requestPasswordReset", data: { email } }];
|
|
2282
|
-
const response = await crudify7.transaction(data);
|
|
2283
|
-
if (response.success) {
|
|
2284
|
-
if (response.data && response.data.existingCodeValid) {
|
|
2285
|
-
setCodeAlreadyExists(true);
|
|
2286
|
-
} else {
|
|
2287
|
-
setEmailSent(true);
|
|
2288
|
-
}
|
|
2289
|
-
} else {
|
|
2290
|
-
const parsedErrors = handleCrudifyError(response);
|
|
2291
|
-
const translatedErrors = parsedErrors.map(translateError2);
|
|
2292
|
-
setErrors(translatedErrors);
|
|
2293
|
-
}
|
|
2294
|
-
} catch (error) {
|
|
2295
|
-
const parsedErrors = handleCrudifyError(error);
|
|
2296
|
-
const translatedErrors = parsedErrors.map(translateError2);
|
|
2297
|
-
setErrors(translatedErrors);
|
|
2298
|
-
if (onError) {
|
|
2299
|
-
onError(translatedErrors.join(", "));
|
|
2300
|
-
}
|
|
2301
|
-
} finally {
|
|
2302
|
-
setLoading(false);
|
|
2303
|
-
}
|
|
2304
|
-
};
|
|
2305
|
-
const handleBack = () => {
|
|
2306
|
-
onScreenChange?.("login");
|
|
2307
|
-
};
|
|
2308
|
-
const handleGoToCheckCode = () => {
|
|
2309
|
-
if (emailSent || codeAlreadyExists) {
|
|
2310
|
-
onScreenChange?.("checkCode", { email });
|
|
2311
|
-
return;
|
|
2312
|
-
}
|
|
2313
|
-
if (!email) {
|
|
2314
|
-
setHelperTextEmail(t("forgotPassword.emailRequired"));
|
|
2315
|
-
return;
|
|
2316
|
-
}
|
|
2317
|
-
if (!validateEmail(email)) {
|
|
2318
|
-
setHelperTextEmail(t("forgotPassword.invalidEmail"));
|
|
2319
|
-
return;
|
|
2320
|
-
}
|
|
2321
|
-
onScreenChange?.("checkCode", { email });
|
|
2322
|
-
};
|
|
2323
|
-
if (emailSent || codeAlreadyExists) {
|
|
2324
|
-
return /* @__PURE__ */ jsx7(Fragment3, { children: /* @__PURE__ */ jsxs4(Box3, { sx: { width: "100%", display: "flex", flexDirection: "column", gap: 2, textAlign: "center" }, children: [
|
|
2325
|
-
/* @__PURE__ */ jsxs4(Box3, { sx: { mb: 2 }, children: [
|
|
2326
|
-
/* @__PURE__ */ jsx7(Typography2, { variant: "h5", component: "h1", sx: { mb: 1, fontWeight: 600 }, children: codeAlreadyExists ? t("forgotPassword.codeAlreadyExistsMessage") : t("forgotPassword.emailSentMessage") }),
|
|
2327
|
-
/* @__PURE__ */ jsx7(Typography2, { variant: "body2", sx: { color: codeAlreadyExists ? "success.main" : "grey.600" }, children: codeAlreadyExists ? t("forgotPassword.checkEmailInstructions") : t("forgotPassword.checkEmailInstructions") })
|
|
2328
|
-
] }),
|
|
2329
|
-
/* @__PURE__ */ jsx7(Button2, { type: "button", onClick: handleGoToCheckCode, fullWidth: true, variant: "contained", color: "primary", sx: { mt: 2, mb: 2 }, children: t("forgotPassword.enterCodeLink") }),
|
|
2330
|
-
/* @__PURE__ */ jsx7(Box3, { sx: { display: "flex", justifyContent: "center", alignItems: "center" }, children: /* @__PURE__ */ jsx7(Link2, { sx: { cursor: "pointer" }, onClick: handleBack, variant: "body2", color: "secondary", children: t("common.back") }) })
|
|
2331
|
-
] }) });
|
|
2332
|
-
}
|
|
2333
|
-
return /* @__PURE__ */ jsxs4(Fragment3, { children: [
|
|
2334
|
-
/* @__PURE__ */ jsxs4(Box3, { component: "form", noValidate: true, sx: { width: "100%", display: "flex", flexDirection: "column", gap: 2 }, children: [
|
|
2335
|
-
/* @__PURE__ */ jsxs4(Box3, { sx: { mb: 2 }, children: [
|
|
2336
|
-
/* @__PURE__ */ jsx7(Typography2, { variant: "h5", component: "h1", sx: { mb: 1, fontWeight: 600 }, children: t("forgotPassword.title") }),
|
|
2337
|
-
/* @__PURE__ */ jsx7(Typography2, { variant: "body2", sx: { color: "grey.600" }, children: t("forgotPassword.instructions") })
|
|
2338
|
-
] }),
|
|
2339
|
-
/* @__PURE__ */ jsxs4(Box3, { sx: { mb: 1 }, children: [
|
|
2340
|
-
/* @__PURE__ */ jsx7(
|
|
2341
|
-
Typography2,
|
|
2342
|
-
{
|
|
2343
|
-
variant: "body2",
|
|
2344
|
-
component: "label",
|
|
2345
|
-
htmlFor: "email",
|
|
2346
|
-
sx: { display: "block", fontWeight: 500, color: "grey.700", mb: 0.5 },
|
|
2347
|
-
children: t("forgotPassword.emailLabel")
|
|
2348
|
-
}
|
|
2349
|
-
),
|
|
2350
|
-
/* @__PURE__ */ jsx7(
|
|
2351
|
-
TextField2,
|
|
2352
|
-
{
|
|
2353
|
-
fullWidth: true,
|
|
2354
|
-
id: "email",
|
|
2355
|
-
name: "email",
|
|
2356
|
-
type: "email",
|
|
2357
|
-
value: email,
|
|
2358
|
-
disabled: loading,
|
|
2359
|
-
onChange: (e) => setEmail(e.target.value),
|
|
2360
|
-
error: !!helperTextEmail,
|
|
2361
|
-
helperText: helperTextEmail,
|
|
2362
|
-
autoComplete: "email",
|
|
2363
|
-
placeholder: t("forgotPassword.emailPlaceholder"),
|
|
2364
|
-
required: true
|
|
2365
|
-
}
|
|
2366
|
-
)
|
|
2367
|
-
] }),
|
|
2368
|
-
/* @__PURE__ */ jsx7(Button2, { disabled: loading, type: "button", onClick: handleSubmit, fullWidth: true, variant: "contained", color: "primary", sx: { mt: 2, mb: 2 }, children: loading ? /* @__PURE__ */ jsx7(CircularProgress2, { size: 20 }) : t("forgotPassword.sendCodeButton") }),
|
|
2369
|
-
/* @__PURE__ */ jsxs4(Box3, { sx: { display: "flex", justifyContent: "center", alignItems: "center", gap: 2 }, children: [
|
|
2370
|
-
/* @__PURE__ */ jsx7(Link2, { sx: { cursor: "pointer" }, onClick: handleBack, variant: "body2", color: "secondary", children: t("common.back") }),
|
|
2371
|
-
/* @__PURE__ */ jsx7(Typography2, { variant: "body2", sx: { color: "grey.400" }, children: "\u2022" }),
|
|
2372
|
-
/* @__PURE__ */ jsx7(Link2, { sx: { cursor: "pointer" }, onClick: handleGoToCheckCode, variant: "body2", color: "secondary", children: t("login.alreadyHaveCodeLink") })
|
|
2373
|
-
] })
|
|
2374
|
-
] }),
|
|
2375
|
-
/* @__PURE__ */ jsx7(Box3, { children: errors.length > 0 && errors.map((error, index) => /* @__PURE__ */ jsx7(Alert3, { variant: "filled", sx: { mt: 2 }, severity: "error", children: error }, index)) })
|
|
2376
|
-
] });
|
|
2377
|
-
};
|
|
2378
|
-
var ForgotPasswordForm_default = ForgotPasswordForm;
|
|
2379
|
-
|
|
2380
|
-
// src/components/CrudifyLogin/Forms/ResetPasswordForm.tsx
|
|
2381
|
-
import { useState as useState6, useEffect as useEffect7 } from "react";
|
|
2382
|
-
import { Typography as Typography3, TextField as TextField3, Button as Button3, Box as Box4, CircularProgress as CircularProgress3, Alert as Alert4, Link as Link3 } from "@mui/material";
|
|
2383
|
-
import { Fragment as Fragment4, jsx as jsx8, jsxs as jsxs5 } from "react/jsx-runtime";
|
|
2384
|
-
var ResetPasswordForm = ({ onScreenChange, onError, searchParams, onResetSuccess }) => {
|
|
2385
|
-
const { crudify: crudify7 } = useCrudify();
|
|
2386
|
-
const [newPassword, setNewPassword] = useState6("");
|
|
2387
|
-
const [confirmPassword, setConfirmPassword] = useState6("");
|
|
2388
|
-
const [loading, setLoading] = useState6(false);
|
|
2389
|
-
const [errors, setErrors] = useState6([]);
|
|
2390
|
-
const [helperTextNewPassword, setHelperTextNewPassword] = useState6(null);
|
|
2391
|
-
const [helperTextConfirmPassword, setHelperTextConfirmPassword] = useState6(null);
|
|
2392
|
-
const [email, setEmail] = useState6("");
|
|
2393
|
-
const [code, setCode] = useState6("");
|
|
2394
|
-
const [fromCodeVerification, setFromCodeVerification] = useState6(false);
|
|
2395
|
-
const [validatingCode, setValidatingCode] = useState6(true);
|
|
2396
|
-
const [codeValidated, setCodeValidated] = useState6(false);
|
|
2397
|
-
const [pendingValidation, setPendingValidation] = useState6(null);
|
|
2398
|
-
const [isValidating, setIsValidating] = useState6(false);
|
|
2399
|
-
const { t } = useTranslation();
|
|
2400
|
-
const translateError2 = (parsedError) => {
|
|
2401
|
-
const possibleKeys = [
|
|
2402
|
-
`errors.auth.${parsedError.code}`,
|
|
2403
|
-
`errors.data.${parsedError.code}`,
|
|
2404
|
-
`errors.system.${parsedError.code}`,
|
|
2405
|
-
`errors.${parsedError.code}`,
|
|
2406
|
-
`resetPassword.${parsedError.code.toLowerCase()}`
|
|
2407
|
-
];
|
|
2408
|
-
for (const key of possibleKeys) {
|
|
2409
|
-
const translated = t(key);
|
|
2410
|
-
if (translated !== key) {
|
|
2411
|
-
return translated;
|
|
2412
|
-
}
|
|
2413
|
-
}
|
|
2414
|
-
return parsedError.message || t("error.unknown");
|
|
2415
|
-
};
|
|
2416
|
-
const getParam = (key) => {
|
|
2417
|
-
if (!searchParams) return null;
|
|
2418
|
-
if (searchParams instanceof URLSearchParams) {
|
|
2419
|
-
return searchParams.get(key);
|
|
2420
|
-
}
|
|
2421
|
-
return searchParams[key] || null;
|
|
2422
|
-
};
|
|
2423
|
-
useEffect7(() => {
|
|
2424
|
-
if (!searchParams) {
|
|
2425
|
-
return;
|
|
2426
|
-
}
|
|
2427
|
-
if (searchParams) {
|
|
2428
|
-
const fromCodeVerificationParam = getParam("fromCodeVerification");
|
|
2429
|
-
const emailParam = getParam("email");
|
|
2430
|
-
const codeParam = getParam("code");
|
|
2431
|
-
if (fromCodeVerificationParam === "true" && emailParam && codeParam) {
|
|
2432
|
-
setEmail(emailParam);
|
|
2433
|
-
setCode(codeParam);
|
|
2434
|
-
setFromCodeVerification(true);
|
|
2435
|
-
setCodeValidated(true);
|
|
2436
|
-
setValidatingCode(false);
|
|
2437
|
-
return;
|
|
2438
|
-
}
|
|
2439
|
-
const linkParam = getParam("link");
|
|
2440
|
-
if (linkParam) {
|
|
2441
|
-
try {
|
|
2442
|
-
const decodedLink = decodeURIComponent(linkParam);
|
|
2443
|
-
const [linkCode, linkEmail] = decodedLink.split("/");
|
|
2444
|
-
if (linkCode && linkEmail && linkCode.length === 6) {
|
|
2445
|
-
setCode(linkCode);
|
|
2446
|
-
setEmail(linkEmail);
|
|
2447
|
-
setFromCodeVerification(false);
|
|
2448
|
-
setPendingValidation({ email: linkEmail, code: linkCode });
|
|
2449
|
-
return;
|
|
2450
|
-
}
|
|
2451
|
-
} catch (error) {
|
|
2452
|
-
}
|
|
2453
|
-
}
|
|
2454
|
-
if (emailParam && codeParam) {
|
|
2455
|
-
setEmail(emailParam);
|
|
2456
|
-
setCode(codeParam);
|
|
2457
|
-
setFromCodeVerification(false);
|
|
2458
|
-
setPendingValidation({ email: emailParam, code: codeParam });
|
|
2459
|
-
return;
|
|
2460
|
-
}
|
|
2461
|
-
}
|
|
2462
|
-
setErrors([t("resetPassword.invalidCode")]);
|
|
2463
|
-
setValidatingCode(false);
|
|
2464
|
-
setTimeout(() => onScreenChange?.("forgotPassword"), 3e3);
|
|
2465
|
-
}, [searchParams, crudify7, t, onScreenChange]);
|
|
2466
|
-
useEffect7(() => {
|
|
2467
|
-
if (crudify7 && pendingValidation && !isValidating) {
|
|
2468
|
-
setIsValidating(true);
|
|
2469
|
-
const validateCode = async (emailToValidate, codeToValidate) => {
|
|
2470
|
-
try {
|
|
2471
|
-
const data = [
|
|
2472
|
-
{
|
|
2473
|
-
operation: "validatePasswordResetCode",
|
|
2474
|
-
data: { email: emailToValidate, codePassword: codeToValidate }
|
|
2475
|
-
}
|
|
2476
|
-
];
|
|
2477
|
-
const response = await crudify7.transaction(data);
|
|
2478
|
-
if (response.data && Array.isArray(response.data)) {
|
|
2479
|
-
const validationResult = response.data[0];
|
|
2480
|
-
if (validationResult && validationResult.response && validationResult.response.status === "OK") {
|
|
2481
|
-
setCodeValidated(true);
|
|
2482
|
-
return;
|
|
2483
|
-
}
|
|
2484
|
-
}
|
|
2485
|
-
if (response.success) {
|
|
2486
|
-
setCodeValidated(true);
|
|
2487
|
-
} else {
|
|
2488
|
-
const parsedErrors = handleCrudifyError(response);
|
|
2489
|
-
const translatedErrors = parsedErrors.map(translateError2);
|
|
2490
|
-
setErrors(translatedErrors);
|
|
2491
|
-
setTimeout(() => onScreenChange?.("forgotPassword"), 3e3);
|
|
2492
|
-
}
|
|
2493
|
-
} catch (error) {
|
|
2494
|
-
const parsedErrors = handleCrudifyError(error);
|
|
2495
|
-
const translatedErrors = parsedErrors.map(translateError2);
|
|
2496
|
-
setErrors(translatedErrors);
|
|
2497
|
-
setTimeout(() => onScreenChange?.("forgotPassword"), 3e3);
|
|
2498
|
-
} finally {
|
|
2499
|
-
setValidatingCode(false);
|
|
2500
|
-
setPendingValidation(null);
|
|
2501
|
-
setIsValidating(false);
|
|
2502
|
-
}
|
|
2503
|
-
};
|
|
2504
|
-
validateCode(pendingValidation.email, pendingValidation.code);
|
|
2505
|
-
}
|
|
2506
|
-
}, [crudify7, pendingValidation, t, onScreenChange]);
|
|
2507
|
-
const validatePassword = (password) => {
|
|
2508
|
-
if (password.length < 8) {
|
|
2509
|
-
return t("resetPassword.passwordTooShort");
|
|
2510
|
-
}
|
|
2511
|
-
return null;
|
|
2512
|
-
};
|
|
2513
|
-
const handleSubmit = async () => {
|
|
2514
|
-
if (loading || !crudify7) return;
|
|
2515
|
-
setErrors([]);
|
|
2516
|
-
setHelperTextNewPassword(null);
|
|
2517
|
-
setHelperTextConfirmPassword(null);
|
|
2518
|
-
let hasErrors = false;
|
|
2519
|
-
if (!newPassword) {
|
|
2520
|
-
setHelperTextNewPassword(t("resetPassword.newPasswordRequired"));
|
|
2521
|
-
hasErrors = true;
|
|
2522
|
-
} else {
|
|
2523
|
-
const passwordError = validatePassword(newPassword);
|
|
2524
|
-
if (passwordError) {
|
|
2525
|
-
setHelperTextNewPassword(passwordError);
|
|
2526
|
-
hasErrors = true;
|
|
2527
|
-
}
|
|
2528
|
-
}
|
|
2529
|
-
if (!confirmPassword) {
|
|
2530
|
-
setHelperTextConfirmPassword(t("resetPassword.confirmPasswordRequired"));
|
|
2531
|
-
hasErrors = true;
|
|
2532
|
-
} else if (newPassword !== confirmPassword) {
|
|
2533
|
-
setHelperTextConfirmPassword(t("resetPassword.passwordsDoNotMatch"));
|
|
2534
|
-
hasErrors = true;
|
|
2535
|
-
}
|
|
2536
|
-
if (hasErrors) return;
|
|
2537
|
-
setLoading(true);
|
|
2538
|
-
try {
|
|
2539
|
-
const data = [
|
|
2540
|
-
{
|
|
2541
|
-
operation: "validateAndResetPassword",
|
|
2542
|
-
data: { email, codePassword: code, newPassword }
|
|
2543
|
-
}
|
|
2544
|
-
];
|
|
2545
|
-
const response = await crudify7.transaction(data);
|
|
2546
|
-
if (response.success) {
|
|
2547
|
-
setErrors([]);
|
|
2548
|
-
setTimeout(() => {
|
|
2549
|
-
onResetSuccess?.();
|
|
2550
|
-
}, 1e3);
|
|
2551
|
-
} else {
|
|
2552
|
-
const parsedErrors = handleCrudifyError(response);
|
|
2553
|
-
const translatedErrors = parsedErrors.map(translateError2);
|
|
2554
|
-
setErrors(translatedErrors);
|
|
2555
|
-
}
|
|
2556
|
-
} catch (error) {
|
|
2557
|
-
const parsedErrors = handleCrudifyError(error);
|
|
2558
|
-
const translatedErrors = parsedErrors.map(translateError2);
|
|
2559
|
-
setErrors(translatedErrors);
|
|
2560
|
-
if (onError) {
|
|
2561
|
-
onError(translatedErrors.join(", "));
|
|
2562
|
-
}
|
|
2563
|
-
}
|
|
2564
|
-
setLoading(false);
|
|
2565
|
-
};
|
|
2566
|
-
const handleBack = () => {
|
|
2567
|
-
if (fromCodeVerification) {
|
|
2568
|
-
onScreenChange?.("checkCode", { email });
|
|
2569
|
-
} else {
|
|
2570
|
-
onScreenChange?.("forgotPassword");
|
|
2571
|
-
}
|
|
2572
|
-
};
|
|
2573
|
-
if (validatingCode) {
|
|
2574
|
-
return /* @__PURE__ */ jsx8(Box4, { sx: { display: "flex", justifyContent: "center", alignItems: "center", minHeight: "300px" }, children: /* @__PURE__ */ jsx8(CircularProgress3, {}) });
|
|
2575
|
-
}
|
|
2576
|
-
if (!codeValidated) {
|
|
2577
|
-
return /* @__PURE__ */ jsx8(Box4, { children: errors.length > 0 && errors.map((error, index) => /* @__PURE__ */ jsx8(Alert4, { variant: "filled", sx: { mt: 2 }, severity: "error", children: error }, index)) });
|
|
2578
|
-
}
|
|
2579
|
-
return /* @__PURE__ */ jsxs5(Fragment4, { children: [
|
|
2580
|
-
/* @__PURE__ */ jsxs5(Box4, { component: "form", noValidate: true, sx: { width: "100%", display: "flex", flexDirection: "column", gap: 2 }, children: [
|
|
2581
|
-
/* @__PURE__ */ jsxs5(Box4, { sx: { mb: 2 }, children: [
|
|
2582
|
-
/* @__PURE__ */ jsx8(Typography3, { variant: "h5", component: "h1", sx: { mb: 1, fontWeight: 600 }, children: t("resetPassword.title") }),
|
|
2583
|
-
/* @__PURE__ */ jsx8(Typography3, { variant: "body2", sx: { color: "grey.600" }, children: t("resetPassword.instructions") })
|
|
2584
|
-
] }),
|
|
2585
|
-
/* @__PURE__ */ jsxs5(Box4, { sx: { mb: 1 }, children: [
|
|
2586
|
-
/* @__PURE__ */ jsx8(
|
|
2587
|
-
Typography3,
|
|
2588
|
-
{
|
|
2589
|
-
variant: "body2",
|
|
2590
|
-
component: "label",
|
|
2591
|
-
htmlFor: "newPassword",
|
|
2592
|
-
sx: { display: "block", fontWeight: 500, color: "grey.700", mb: 0.5 },
|
|
2593
|
-
children: t("resetPassword.newPasswordLabel")
|
|
2594
|
-
}
|
|
2595
|
-
),
|
|
2596
|
-
/* @__PURE__ */ jsx8(
|
|
2597
|
-
TextField3,
|
|
2598
|
-
{
|
|
2599
|
-
fullWidth: true,
|
|
2600
|
-
id: "newPassword",
|
|
2601
|
-
name: "newPassword",
|
|
2602
|
-
type: "password",
|
|
2603
|
-
value: newPassword,
|
|
2604
|
-
disabled: loading,
|
|
2605
|
-
onChange: (e) => setNewPassword(e.target.value),
|
|
2606
|
-
error: !!helperTextNewPassword,
|
|
2607
|
-
helperText: helperTextNewPassword,
|
|
2608
|
-
autoComplete: "new-password",
|
|
2609
|
-
placeholder: t("resetPassword.newPasswordPlaceholder"),
|
|
2610
|
-
required: true
|
|
2611
|
-
}
|
|
2612
|
-
)
|
|
2613
|
-
] }),
|
|
2614
|
-
/* @__PURE__ */ jsxs5(Box4, { sx: { mb: 1 }, children: [
|
|
2615
|
-
/* @__PURE__ */ jsx8(
|
|
2616
|
-
Typography3,
|
|
2617
|
-
{
|
|
2618
|
-
variant: "body2",
|
|
2619
|
-
component: "label",
|
|
2620
|
-
htmlFor: "confirmPassword",
|
|
2621
|
-
sx: { display: "block", fontWeight: 500, color: "grey.700", mb: 0.5 },
|
|
2622
|
-
children: t("resetPassword.confirmPasswordLabel")
|
|
2623
|
-
}
|
|
2624
|
-
),
|
|
2625
|
-
/* @__PURE__ */ jsx8(
|
|
2626
|
-
TextField3,
|
|
2627
|
-
{
|
|
2628
|
-
fullWidth: true,
|
|
2629
|
-
id: "confirmPassword",
|
|
2630
|
-
name: "confirmPassword",
|
|
2631
|
-
type: "password",
|
|
2632
|
-
value: confirmPassword,
|
|
2633
|
-
disabled: loading,
|
|
2634
|
-
onChange: (e) => setConfirmPassword(e.target.value),
|
|
2635
|
-
error: !!helperTextConfirmPassword,
|
|
2636
|
-
helperText: helperTextConfirmPassword,
|
|
2637
|
-
autoComplete: "new-password",
|
|
2638
|
-
placeholder: t("resetPassword.confirmPasswordPlaceholder"),
|
|
2639
|
-
required: true
|
|
2640
|
-
}
|
|
2641
|
-
)
|
|
2642
|
-
] }),
|
|
2643
|
-
/* @__PURE__ */ jsx8(Button3, { disabled: loading, type: "button", onClick: handleSubmit, fullWidth: true, variant: "contained", color: "primary", sx: { mt: 2, mb: 2 }, children: loading ? /* @__PURE__ */ jsx8(CircularProgress3, { size: 20 }) : t("resetPassword.resetPasswordButton") }),
|
|
2644
|
-
/* @__PURE__ */ jsx8(Box4, { sx: { display: "flex", justifyContent: "center", alignItems: "center" }, children: /* @__PURE__ */ jsx8(Link3, { sx: { cursor: "pointer" }, onClick: handleBack, variant: "body2", color: "secondary", children: t("common.back") }) })
|
|
2645
|
-
] }),
|
|
2646
|
-
/* @__PURE__ */ jsx8(Box4, { children: errors.length > 0 && errors.map((error, index) => /* @__PURE__ */ jsx8(Alert4, { variant: "filled", sx: { mt: 2 }, severity: "error", children: error }, index)) })
|
|
2647
|
-
] });
|
|
2648
|
-
};
|
|
2649
|
-
var ResetPasswordForm_default = ResetPasswordForm;
|
|
2650
|
-
|
|
2651
|
-
// src/components/CrudifyLogin/Forms/CheckCodeForm.tsx
|
|
2652
|
-
import { useState as useState7, useEffect as useEffect8 } from "react";
|
|
2653
|
-
import { Typography as Typography4, TextField as TextField4, Button as Button4, Box as Box5, CircularProgress as CircularProgress4, Alert as Alert5, Link as Link4 } from "@mui/material";
|
|
2654
|
-
import { Fragment as Fragment5, jsx as jsx9, jsxs as jsxs6 } from "react/jsx-runtime";
|
|
2655
|
-
var CheckCodeForm = ({ onScreenChange, onError, searchParams }) => {
|
|
2656
|
-
const { crudify: crudify7 } = useCrudify();
|
|
2657
|
-
const [code, setCode] = useState7("");
|
|
2658
|
-
const [loading, setLoading] = useState7(false);
|
|
2659
|
-
const [errors, setErrors] = useState7([]);
|
|
2660
|
-
const [helperTextCode, setHelperTextCode] = useState7(null);
|
|
2661
|
-
const [email, setEmail] = useState7("");
|
|
2662
|
-
const { t } = useTranslation();
|
|
2663
|
-
const getParam = (key) => {
|
|
2664
|
-
if (!searchParams) return null;
|
|
2665
|
-
if (searchParams instanceof URLSearchParams) {
|
|
2666
|
-
return searchParams.get(key);
|
|
2667
|
-
}
|
|
2668
|
-
return searchParams[key] || null;
|
|
2669
|
-
};
|
|
2670
|
-
const translateError2 = (parsedError) => {
|
|
2671
|
-
const possibleKeys = [
|
|
2672
|
-
`errors.auth.${parsedError.code}`,
|
|
2673
|
-
`errors.data.${parsedError.code}`,
|
|
2674
|
-
`errors.system.${parsedError.code}`,
|
|
2675
|
-
`errors.${parsedError.code}`,
|
|
2676
|
-
`checkCode.${parsedError.code.toLowerCase()}`
|
|
2677
|
-
];
|
|
2678
|
-
for (const key of possibleKeys) {
|
|
2679
|
-
const translated = t(key);
|
|
2680
|
-
if (translated !== key) {
|
|
2681
|
-
return translated;
|
|
2682
|
-
}
|
|
2683
|
-
}
|
|
2684
|
-
return parsedError.message || t("error.unknown");
|
|
2685
|
-
};
|
|
2686
|
-
useEffect8(() => {
|
|
2687
|
-
const emailParam = getParam("email");
|
|
2688
|
-
if (emailParam) {
|
|
2689
|
-
setEmail(emailParam);
|
|
2690
|
-
} else {
|
|
2691
|
-
onScreenChange?.("forgotPassword");
|
|
2692
|
-
}
|
|
2693
|
-
}, [searchParams, onScreenChange]);
|
|
2694
|
-
const handleSubmit = async () => {
|
|
2695
|
-
if (loading || !crudify7) return;
|
|
2696
|
-
setErrors([]);
|
|
2697
|
-
setHelperTextCode(null);
|
|
2698
|
-
if (!code) {
|
|
2699
|
-
setHelperTextCode(t("checkCode.codeRequired"));
|
|
2700
|
-
return;
|
|
2701
|
-
}
|
|
2702
|
-
if (code.length !== 6) {
|
|
2703
|
-
setHelperTextCode(t("checkCode.codeRequired"));
|
|
2704
|
-
return;
|
|
2705
|
-
}
|
|
2706
|
-
setLoading(true);
|
|
2707
|
-
try {
|
|
2708
|
-
const data = [
|
|
2709
|
-
{
|
|
2710
|
-
operation: "validatePasswordResetCode",
|
|
2711
|
-
data: { email, codePassword: code }
|
|
2712
|
-
}
|
|
2713
|
-
];
|
|
2714
|
-
const response = await crudify7.transaction(data);
|
|
2715
|
-
if (response.success) {
|
|
2716
|
-
onScreenChange?.("resetPassword", { email, code, fromCodeVerification: "true" });
|
|
2717
|
-
} else {
|
|
2718
|
-
const parsedErrors = handleCrudifyError(response);
|
|
2719
|
-
const translatedErrors = parsedErrors.map(translateError2);
|
|
2720
|
-
setErrors(translatedErrors);
|
|
2721
|
-
setLoading(false);
|
|
2722
|
-
}
|
|
2723
|
-
} catch (error) {
|
|
2724
|
-
const parsedErrors = handleCrudifyError(error);
|
|
2725
|
-
const translatedErrors = parsedErrors.map(translateError2);
|
|
2726
|
-
setErrors(translatedErrors);
|
|
2727
|
-
setLoading(false);
|
|
2728
|
-
if (onError) {
|
|
2729
|
-
onError(translatedErrors.join(", "));
|
|
2730
|
-
}
|
|
2731
|
-
}
|
|
2732
|
-
};
|
|
2733
|
-
const handleBack = () => {
|
|
2734
|
-
onScreenChange?.("forgotPassword");
|
|
2735
|
-
};
|
|
2736
|
-
const handleCodeChange = (event) => {
|
|
2737
|
-
const value = event.target.value.replace(/\D/g, "").slice(0, 6);
|
|
2738
|
-
setCode(value);
|
|
2739
|
-
};
|
|
2740
|
-
return /* @__PURE__ */ jsxs6(Fragment5, { children: [
|
|
2741
|
-
/* @__PURE__ */ jsxs6(Box5, { component: "form", noValidate: true, sx: { width: "100%", display: "flex", flexDirection: "column", gap: 2 }, children: [
|
|
2742
|
-
/* @__PURE__ */ jsxs6(Box5, { sx: { mb: 2 }, children: [
|
|
2743
|
-
/* @__PURE__ */ jsx9(Typography4, { variant: "h5", component: "h1", sx: { mb: 1, fontWeight: 600 }, children: t("checkCode.title") }),
|
|
2744
|
-
/* @__PURE__ */ jsx9(Typography4, { variant: "body2", sx: { color: "grey.600" }, children: t("checkCode.instructions") })
|
|
2745
|
-
] }),
|
|
2746
|
-
/* @__PURE__ */ jsxs6(Box5, { sx: { mb: 1 }, children: [
|
|
2747
|
-
/* @__PURE__ */ jsx9(
|
|
2748
|
-
Typography4,
|
|
2749
|
-
{
|
|
2750
|
-
variant: "body2",
|
|
2751
|
-
component: "label",
|
|
2752
|
-
htmlFor: "code",
|
|
2753
|
-
sx: { display: "block", fontWeight: 500, color: "grey.700", mb: 0.5 },
|
|
2754
|
-
children: t("checkCode.codeLabel")
|
|
2755
|
-
}
|
|
2756
|
-
),
|
|
2757
|
-
/* @__PURE__ */ jsx9(
|
|
2758
|
-
TextField4,
|
|
2759
|
-
{
|
|
2760
|
-
fullWidth: true,
|
|
2761
|
-
id: "code",
|
|
2762
|
-
name: "code",
|
|
2763
|
-
type: "text",
|
|
2764
|
-
value: code,
|
|
2765
|
-
disabled: loading,
|
|
2766
|
-
onChange: handleCodeChange,
|
|
2767
|
-
error: !!helperTextCode,
|
|
2768
|
-
helperText: helperTextCode,
|
|
2769
|
-
placeholder: t("checkCode.codePlaceholder"),
|
|
2770
|
-
inputProps: {
|
|
2771
|
-
maxLength: 6,
|
|
2772
|
-
style: { textAlign: "center", fontSize: "1.5rem", letterSpacing: "0.4rem" }
|
|
2773
|
-
},
|
|
2774
|
-
required: true
|
|
2775
|
-
}
|
|
2776
|
-
)
|
|
2777
|
-
] }),
|
|
2778
|
-
/* @__PURE__ */ jsx9(
|
|
2779
|
-
Button4,
|
|
2780
|
-
{
|
|
2781
|
-
disabled: loading || code.length !== 6,
|
|
2782
|
-
type: "button",
|
|
2783
|
-
onClick: handleSubmit,
|
|
2784
|
-
fullWidth: true,
|
|
2785
|
-
variant: "contained",
|
|
2786
|
-
color: "primary",
|
|
2787
|
-
sx: { mt: 2, mb: 2 },
|
|
2788
|
-
children: loading ? /* @__PURE__ */ jsx9(CircularProgress4, { size: 20 }) : t("checkCode.verifyButton")
|
|
2789
|
-
}
|
|
2790
|
-
),
|
|
2791
|
-
/* @__PURE__ */ jsx9(Box5, { sx: { display: "flex", justifyContent: "center", alignItems: "center" }, children: /* @__PURE__ */ jsx9(Link4, { sx: { cursor: "pointer" }, onClick: handleBack, variant: "body2", color: "secondary", children: t("common.back") }) })
|
|
2792
|
-
] }),
|
|
2793
|
-
/* @__PURE__ */ jsx9(Box5, { children: errors.length > 0 && errors.map((error, index) => /* @__PURE__ */ jsx9(Alert5, { sx: { mt: 2 }, severity: "error", children: error }, index)) })
|
|
2794
|
-
] });
|
|
2795
|
-
};
|
|
2796
|
-
var CheckCodeForm_default = CheckCodeForm;
|
|
2797
|
-
|
|
2798
|
-
// src/components/CrudifyLogin/components/CrudifyInitializer.tsx
|
|
2799
|
-
import { Box as Box6, CircularProgress as CircularProgress5, Alert as Alert6, Typography as Typography5 } from "@mui/material";
|
|
2800
|
-
import { Fragment as Fragment6, jsx as jsx10, jsxs as jsxs7 } from "react/jsx-runtime";
|
|
2801
|
-
var CrudifyInitializer = ({ children, fallback }) => {
|
|
2802
|
-
const { isLoading, error, isInitialized } = useCrudify();
|
|
2803
|
-
const { t } = useTranslation();
|
|
2804
|
-
if (isLoading) {
|
|
2805
|
-
return fallback || /* @__PURE__ */ jsxs7(
|
|
2806
|
-
Box6,
|
|
2807
|
-
{
|
|
2808
|
-
sx: {
|
|
2809
|
-
display: "flex",
|
|
2810
|
-
flexDirection: "column",
|
|
2811
|
-
alignItems: "center",
|
|
2812
|
-
justifyContent: "center",
|
|
2813
|
-
minHeight: "200px",
|
|
2814
|
-
gap: 2
|
|
2815
|
-
},
|
|
2816
|
-
children: [
|
|
2817
|
-
/* @__PURE__ */ jsx10(CircularProgress5, {}),
|
|
2818
|
-
/* @__PURE__ */ jsx10(Typography5, { variant: "body2", color: "text.secondary", children: t("login.initializing") !== "login.initializing" ? t("login.initializing") : "Initializing..." })
|
|
2819
|
-
]
|
|
2820
|
-
}
|
|
2821
|
-
);
|
|
2822
|
-
}
|
|
2823
|
-
if (error) {
|
|
2824
|
-
return /* @__PURE__ */ jsx10(Alert6, { severity: "error", sx: { mt: 2 }, children: /* @__PURE__ */ jsxs7(Typography5, { variant: "body2", children: [
|
|
2825
|
-
t("login.initializationError") !== "login.initializationError" ? t("login.initializationError") : "Initialization error",
|
|
2826
|
-
":",
|
|
2827
|
-
" ",
|
|
2828
|
-
error
|
|
2829
|
-
] }) });
|
|
2830
|
-
}
|
|
2831
|
-
if (!isInitialized) {
|
|
2832
|
-
return /* @__PURE__ */ jsx10(Alert6, { severity: "warning", sx: { mt: 2 }, children: /* @__PURE__ */ jsx10(Typography5, { variant: "body2", children: t("login.notInitialized") !== "login.notInitialized" ? t("login.notInitialized") : "System not initialized" }) });
|
|
2833
|
-
}
|
|
2834
|
-
return /* @__PURE__ */ jsx10(Fragment6, { children });
|
|
2835
|
-
};
|
|
2836
|
-
|
|
2837
|
-
// src/components/CrudifyLogin/index.tsx
|
|
2838
|
-
import { jsx as jsx11, jsxs as jsxs8 } from "react/jsx-runtime";
|
|
2839
|
-
var CrudifyLoginInternal = ({
|
|
2840
|
-
onScreenChange,
|
|
2841
|
-
onExternalNavigate,
|
|
2842
|
-
onLoginSuccess,
|
|
2843
|
-
onError,
|
|
2844
|
-
redirectUrl = "/"
|
|
2845
|
-
}) => {
|
|
2846
|
-
const { t } = useTranslation();
|
|
2847
|
-
const { state, setScreen } = useLoginState();
|
|
2848
|
-
const { config } = useSessionContext();
|
|
2849
|
-
const { showNotification } = useGlobalNotification();
|
|
2850
|
-
const handleScreenChange = (screen2, params) => {
|
|
2851
|
-
let finalParams = params;
|
|
2852
|
-
if (screen2 === "login") {
|
|
2853
|
-
finalParams = {};
|
|
2854
|
-
} else if (screen2 === "forgotPassword" && !params) {
|
|
2855
|
-
finalParams = {};
|
|
2856
|
-
}
|
|
2857
|
-
setScreen(screen2, finalParams);
|
|
2858
|
-
onScreenChange?.(screen2, finalParams);
|
|
2859
|
-
};
|
|
2860
|
-
const renderCurrentForm = () => {
|
|
2861
|
-
const commonProps = {
|
|
2862
|
-
onScreenChange: handleScreenChange,
|
|
2863
|
-
onExternalNavigate,
|
|
2864
|
-
onError,
|
|
2865
|
-
redirectUrl
|
|
2866
|
-
};
|
|
2867
|
-
switch (state.currentScreen) {
|
|
2868
|
-
case "forgotPassword":
|
|
2869
|
-
return /* @__PURE__ */ jsx11(ForgotPasswordForm_default, { ...commonProps });
|
|
2870
|
-
case "checkCode":
|
|
2871
|
-
return /* @__PURE__ */ jsx11(CheckCodeForm_default, { ...commonProps, searchParams: state.searchParams });
|
|
2872
|
-
case "resetPassword":
|
|
2873
|
-
return /* @__PURE__ */ jsx11(
|
|
2874
|
-
ResetPasswordForm_default,
|
|
2875
|
-
{
|
|
2876
|
-
...commonProps,
|
|
2877
|
-
searchParams: state.searchParams,
|
|
2878
|
-
onResetSuccess: () => {
|
|
2879
|
-
const message = t("resetPassword.successMessage");
|
|
2880
|
-
showNotification(message, "success");
|
|
2881
|
-
handleScreenChange("login");
|
|
2882
|
-
}
|
|
2883
|
-
}
|
|
2884
|
-
);
|
|
2885
|
-
default:
|
|
2886
|
-
return /* @__PURE__ */ jsx11(LoginForm_default, { ...commonProps, onLoginSuccess });
|
|
2887
|
-
}
|
|
2888
|
-
};
|
|
2889
|
-
return /* @__PURE__ */ jsxs8(CrudifyInitializer, { children: [
|
|
2890
|
-
/* @__PURE__ */ jsx11(Box7, { sx: { display: "flex", justifyContent: "center", mb: 3 }, children: /* @__PURE__ */ jsx11(
|
|
2891
|
-
"img",
|
|
2892
|
-
{
|
|
2893
|
-
src: config.logo || "/nocios-default.png",
|
|
2894
|
-
alt: t("login.logoAlt"),
|
|
2895
|
-
style: { width: "100%", maxWidth: "150px", height: "auto" },
|
|
2896
|
-
onError: (e) => {
|
|
2897
|
-
const target = e.target;
|
|
2898
|
-
target.src = "/nocios-default.png";
|
|
2899
|
-
}
|
|
2900
|
-
}
|
|
2901
|
-
) }),
|
|
2902
|
-
!config.logo && config.appName && /* @__PURE__ */ jsx11(Typography6, { variant: "h6", component: "h1", sx: { textAlign: "center", mb: 2 }, children: config.appName }),
|
|
2903
|
-
renderCurrentForm()
|
|
2904
|
-
] });
|
|
2905
|
-
};
|
|
2906
|
-
var CrudifyLogin = ({
|
|
2907
|
-
translations,
|
|
2908
|
-
translationsUrl,
|
|
2909
|
-
language = "en",
|
|
2910
|
-
initialScreen = "login",
|
|
2911
|
-
autoReadFromCookies = true,
|
|
2912
|
-
...props
|
|
2913
|
-
}) => {
|
|
2914
|
-
const { config } = useSessionContext();
|
|
2915
|
-
return /* @__PURE__ */ jsx11(I18nProvider, { translations, translationsUrl, language, children: /* @__PURE__ */ jsx11(CrudifyProvider, { config, children: /* @__PURE__ */ jsx11(LoginStateProvider, { config, initialScreen, autoReadFromCookies, children: /* @__PURE__ */ jsx11(CrudifyLoginInternal, { ...props }) }) }) });
|
|
2916
|
-
};
|
|
2917
|
-
var CrudifyLogin_default = CrudifyLogin;
|
|
2918
|
-
|
|
2919
|
-
// src/components/UserProfile/UserProfileDisplay.tsx
|
|
2920
|
-
import {
|
|
2921
|
-
Box as Box8,
|
|
2922
|
-
Card,
|
|
2923
|
-
CardContent,
|
|
2924
|
-
Typography as Typography7,
|
|
2925
|
-
Chip,
|
|
2926
|
-
Avatar,
|
|
2927
|
-
Divider,
|
|
2928
|
-
CircularProgress as CircularProgress6,
|
|
2929
|
-
Alert as Alert7,
|
|
2930
|
-
List,
|
|
2931
|
-
ListItem,
|
|
2932
|
-
ListItemText,
|
|
2933
|
-
ListItemIcon,
|
|
2934
|
-
Collapse,
|
|
2935
|
-
IconButton
|
|
2936
|
-
} from "@mui/material";
|
|
2937
|
-
import {
|
|
2938
|
-
Person,
|
|
2939
|
-
Email,
|
|
2940
|
-
Badge,
|
|
2941
|
-
Security,
|
|
2942
|
-
Schedule,
|
|
2943
|
-
AccountCircle,
|
|
2944
|
-
ExpandMore,
|
|
2945
|
-
ExpandLess,
|
|
2946
|
-
Info
|
|
2947
|
-
} from "@mui/icons-material";
|
|
2948
|
-
|
|
2949
|
-
// src/hooks/useUserProfile.ts
|
|
2950
|
-
import { useState as useState8, useEffect as useEffect9, useCallback as useCallback3, useRef as useRef2 } from "react";
|
|
2951
|
-
import crudify3 from "@nocios/crudify-browser";
|
|
2952
|
-
var useUserProfile = (options = {}) => {
|
|
2953
|
-
const { autoFetch = true, retryOnError = false, maxRetries = 3 } = options;
|
|
2954
|
-
const [userProfile, setUserProfile] = useState8(null);
|
|
2955
|
-
const [loading, setLoading] = useState8(false);
|
|
2956
|
-
const [error, setError] = useState8(null);
|
|
2957
|
-
const [extendedData, setExtendedData] = useState8({});
|
|
2958
|
-
const abortControllerRef = useRef2(null);
|
|
2959
|
-
const mountedRef = useRef2(true);
|
|
2960
|
-
const requestIdRef = useRef2(0);
|
|
2961
|
-
const retryCountRef = useRef2(0);
|
|
2962
|
-
const clearProfile = useCallback3(() => {
|
|
2963
|
-
setUserProfile(null);
|
|
2964
|
-
setError(null);
|
|
2965
|
-
setLoading(false);
|
|
2966
|
-
setExtendedData({});
|
|
2967
|
-
}, []);
|
|
2968
|
-
const refreshProfile = useCallback3(async () => {
|
|
2969
|
-
const userEmail = getCurrentUserEmail();
|
|
2970
|
-
if (!userEmail) {
|
|
2971
|
-
if (mountedRef.current) {
|
|
2972
|
-
setError("No user email available");
|
|
2973
|
-
setLoading(false);
|
|
2974
|
-
}
|
|
2975
|
-
return;
|
|
2976
|
-
}
|
|
2977
|
-
if (abortControllerRef.current) {
|
|
2978
|
-
abortControllerRef.current.abort();
|
|
2979
|
-
}
|
|
2980
|
-
const abortController = new AbortController();
|
|
2981
|
-
abortControllerRef.current = abortController;
|
|
2982
|
-
const currentRequestId = ++requestIdRef.current;
|
|
2983
|
-
try {
|
|
2984
|
-
if (mountedRef.current) {
|
|
2985
|
-
setLoading(true);
|
|
2986
|
-
setError(null);
|
|
2987
|
-
}
|
|
2988
|
-
const response = await crudify3.readItems("users", {
|
|
2989
|
-
filter: { email: userEmail },
|
|
2990
|
-
pagination: { limit: 1 }
|
|
2991
|
-
});
|
|
2992
|
-
if (currentRequestId === requestIdRef.current && mountedRef.current && !abortController.signal.aborted) {
|
|
2993
|
-
if (response.success && response.data && response.data.length > 0) {
|
|
2994
|
-
const userData = response.data[0];
|
|
2995
|
-
setUserProfile(userData);
|
|
2996
|
-
const additionalData = {
|
|
2997
|
-
fullProfile: userData,
|
|
2998
|
-
totalFields: Object.keys(userData).length,
|
|
2999
|
-
displayData: {
|
|
3000
|
-
id: userData.id,
|
|
3001
|
-
email: userData.email,
|
|
3002
|
-
username: userData.username,
|
|
3003
|
-
firstName: userData.firstName,
|
|
3004
|
-
lastName: userData.lastName,
|
|
3005
|
-
fullName: userData.fullName || `${userData.firstName || ""} ${userData.lastName || ""}`.trim(),
|
|
3006
|
-
role: userData.role,
|
|
3007
|
-
permissions: userData.permissions || [],
|
|
3008
|
-
isActive: userData.isActive,
|
|
3009
|
-
lastLogin: userData.lastLogin,
|
|
3010
|
-
createdAt: userData.createdAt,
|
|
3011
|
-
updatedAt: userData.updatedAt,
|
|
3012
|
-
// Include any custom fields
|
|
3013
|
-
...Object.keys(userData).filter(
|
|
3014
|
-
(key) => ![
|
|
3015
|
-
"id",
|
|
3016
|
-
"email",
|
|
3017
|
-
"username",
|
|
3018
|
-
"firstName",
|
|
3019
|
-
"lastName",
|
|
3020
|
-
"fullName",
|
|
3021
|
-
"role",
|
|
3022
|
-
"permissions",
|
|
3023
|
-
"isActive",
|
|
3024
|
-
"lastLogin",
|
|
3025
|
-
"createdAt",
|
|
3026
|
-
"updatedAt"
|
|
3027
|
-
].includes(key)
|
|
3028
|
-
).reduce((acc, key) => ({ ...acc, [key]: userData[key] }), {})
|
|
3029
|
-
}
|
|
3030
|
-
};
|
|
3031
|
-
setExtendedData(additionalData);
|
|
3032
|
-
setError(null);
|
|
3033
|
-
retryCountRef.current = 0;
|
|
3034
|
-
} else {
|
|
3035
|
-
setError("User profile not found");
|
|
3036
|
-
setUserProfile(null);
|
|
3037
|
-
setExtendedData({});
|
|
3038
|
-
}
|
|
3039
|
-
}
|
|
3040
|
-
} catch (err) {
|
|
3041
|
-
if (currentRequestId === requestIdRef.current && mountedRef.current) {
|
|
3042
|
-
const error2 = err;
|
|
3043
|
-
if (error2.name === "AbortError") {
|
|
3044
|
-
return;
|
|
3045
|
-
}
|
|
3046
|
-
const shouldRetry = retryOnError && retryCountRef.current < maxRetries && (error2.message?.includes("Network Error") || error2.message?.includes("Failed to fetch"));
|
|
3047
|
-
if (shouldRetry) {
|
|
3048
|
-
retryCountRef.current++;
|
|
3049
|
-
setTimeout(() => {
|
|
3050
|
-
if (mountedRef.current) {
|
|
3051
|
-
refreshProfile();
|
|
3052
|
-
}
|
|
3053
|
-
}, 1e3 * retryCountRef.current);
|
|
3054
|
-
} else {
|
|
3055
|
-
setError("Failed to load user profile");
|
|
3056
|
-
setUserProfile(null);
|
|
3057
|
-
setExtendedData({});
|
|
3058
|
-
}
|
|
3059
|
-
}
|
|
3060
|
-
} finally {
|
|
3061
|
-
if (currentRequestId === requestIdRef.current && mountedRef.current) {
|
|
3062
|
-
setLoading(false);
|
|
3063
|
-
}
|
|
3064
|
-
if (abortControllerRef.current === abortController) {
|
|
3065
|
-
abortControllerRef.current = null;
|
|
3066
|
-
}
|
|
3067
|
-
}
|
|
3068
|
-
}, [retryOnError, maxRetries]);
|
|
3069
|
-
useEffect9(() => {
|
|
3070
|
-
if (autoFetch) {
|
|
3071
|
-
refreshProfile();
|
|
3072
|
-
}
|
|
3073
|
-
}, [autoFetch, refreshProfile]);
|
|
3074
|
-
useEffect9(() => {
|
|
3075
|
-
mountedRef.current = true;
|
|
3076
|
-
return () => {
|
|
3077
|
-
mountedRef.current = false;
|
|
3078
|
-
if (abortControllerRef.current) {
|
|
3079
|
-
abortControllerRef.current.abort();
|
|
3080
|
-
abortControllerRef.current = null;
|
|
3081
|
-
}
|
|
3082
|
-
};
|
|
3083
|
-
}, []);
|
|
3084
|
-
return {
|
|
3085
|
-
userProfile,
|
|
3086
|
-
loading,
|
|
3087
|
-
error,
|
|
3088
|
-
extendedData,
|
|
3089
|
-
refreshProfile,
|
|
3090
|
-
clearProfile
|
|
3091
|
-
};
|
|
3092
|
-
};
|
|
3093
|
-
|
|
3094
|
-
// src/components/UserProfile/UserProfileDisplay.tsx
|
|
3095
|
-
import { useState as useState9 } from "react";
|
|
3096
|
-
import { Fragment as Fragment7, jsx as jsx12, jsxs as jsxs9 } from "react/jsx-runtime";
|
|
3097
|
-
var UserProfileDisplay = ({
|
|
3098
|
-
showExtendedData = true,
|
|
3099
|
-
showProfileCard = true,
|
|
3100
|
-
autoRefresh = true
|
|
3101
|
-
}) => {
|
|
3102
|
-
const { userProfile, loading, error, extendedData, refreshProfile } = useUserProfile({
|
|
3103
|
-
autoFetch: autoRefresh,
|
|
3104
|
-
retryOnError: true,
|
|
3105
|
-
maxRetries: 3
|
|
3106
|
-
});
|
|
3107
|
-
const [showAllFields, setShowAllFields] = useState9(false);
|
|
3108
|
-
if (loading) {
|
|
3109
|
-
return /* @__PURE__ */ jsxs9(Box8, { display: "flex", justifyContent: "center", alignItems: "center", p: 3, children: [
|
|
3110
|
-
/* @__PURE__ */ jsx12(CircularProgress6, {}),
|
|
3111
|
-
/* @__PURE__ */ jsx12(Typography7, { variant: "body2", sx: { ml: 2 }, children: "Cargando perfil de usuario..." })
|
|
3112
|
-
] });
|
|
3113
|
-
}
|
|
3114
|
-
if (error) {
|
|
3115
|
-
return /* @__PURE__ */ jsxs9(
|
|
3116
|
-
Alert7,
|
|
3117
|
-
{
|
|
3118
|
-
severity: "error",
|
|
3119
|
-
action: /* @__PURE__ */ jsx12(IconButton, { color: "inherit", size: "small", onClick: refreshProfile, children: /* @__PURE__ */ jsx12(Typography7, { variant: "caption", children: "Reintentar" }) }),
|
|
3120
|
-
children: [
|
|
3121
|
-
"Error al cargar el perfil: ",
|
|
3122
|
-
error
|
|
3123
|
-
]
|
|
3124
|
-
}
|
|
3125
|
-
);
|
|
3126
|
-
}
|
|
3127
|
-
if (!userProfile) {
|
|
3128
|
-
return /* @__PURE__ */ jsx12(Alert7, { severity: "warning", children: "No se encontr\xF3 informaci\xF3n del usuario" });
|
|
3129
|
-
}
|
|
3130
|
-
const displayData = extendedData?.displayData || {};
|
|
3131
|
-
const totalFields = extendedData?.totalFields || 0;
|
|
3132
|
-
const formatDate = (dateString) => {
|
|
3133
|
-
if (!dateString) return "No disponible";
|
|
3134
|
-
try {
|
|
3135
|
-
return new Date(dateString).toLocaleString("es-ES", {
|
|
3136
|
-
year: "numeric",
|
|
3137
|
-
month: "short",
|
|
3138
|
-
day: "numeric",
|
|
3139
|
-
hour: "2-digit",
|
|
3140
|
-
minute: "2-digit"
|
|
3141
|
-
});
|
|
3142
|
-
} catch {
|
|
3143
|
-
return dateString;
|
|
3144
|
-
}
|
|
3145
|
-
};
|
|
3146
|
-
const renderFieldValue = (key, value) => {
|
|
3147
|
-
if (value === null || value === void 0) return "No disponible";
|
|
3148
|
-
if (typeof value === "boolean") return value ? "S\xED" : "No";
|
|
3149
|
-
if (Array.isArray(value)) return value.length > 0 ? value.join(", ") : "Ninguno";
|
|
3150
|
-
if (typeof value === "object") return JSON.stringify(value, null, 2);
|
|
3151
|
-
return String(value);
|
|
3152
|
-
};
|
|
3153
|
-
const basicFields = [
|
|
3154
|
-
{ key: "id", label: "ID", icon: /* @__PURE__ */ jsx12(Badge, {}) },
|
|
3155
|
-
{ key: "email", label: "Email", icon: /* @__PURE__ */ jsx12(Email, {}) },
|
|
3156
|
-
{ key: "username", label: "Usuario", icon: /* @__PURE__ */ jsx12(Person, {}) },
|
|
3157
|
-
{ key: "fullName", label: "Nombre completo", icon: /* @__PURE__ */ jsx12(AccountCircle, {}) },
|
|
3158
|
-
{ key: "role", label: "Rol", icon: /* @__PURE__ */ jsx12(Security, {}) }
|
|
3159
|
-
];
|
|
3160
|
-
const extendedFields = [
|
|
3161
|
-
{ key: "firstName", label: "Nombre" },
|
|
3162
|
-
{ key: "lastName", label: "Apellido" },
|
|
3163
|
-
{ key: "isActive", label: "Activo" },
|
|
3164
|
-
{ key: "lastLogin", label: "\xDAltimo login" },
|
|
3165
|
-
{ key: "createdAt", label: "Creado" },
|
|
3166
|
-
{ key: "updatedAt", label: "Actualizado" }
|
|
3167
|
-
];
|
|
3168
|
-
const knownFields = [...basicFields.map((f) => f.key), ...extendedFields.map((f) => f.key), "permissions"];
|
|
3169
|
-
const customFields = Object.keys(displayData).filter((key) => !knownFields.includes(key)).map((key) => ({ key, label: key }));
|
|
3170
|
-
return /* @__PURE__ */ jsxs9(Box8, { children: [
|
|
3171
|
-
showProfileCard && /* @__PURE__ */ jsx12(Card, { sx: { mb: 2 }, children: /* @__PURE__ */ jsxs9(CardContent, { children: [
|
|
3172
|
-
/* @__PURE__ */ jsxs9(Box8, { display: "flex", alignItems: "center", mb: 2, children: [
|
|
3173
|
-
/* @__PURE__ */ jsx12(
|
|
3174
|
-
Avatar,
|
|
3175
|
-
{
|
|
3176
|
-
src: displayData.avatar,
|
|
3177
|
-
sx: { width: 56, height: 56, mr: 2 },
|
|
3178
|
-
children: displayData.fullName?.[0] || displayData.username?.[0] || displayData.email?.[0]
|
|
3179
|
-
}
|
|
3180
|
-
),
|
|
3181
|
-
/* @__PURE__ */ jsxs9(Box8, { children: [
|
|
3182
|
-
/* @__PURE__ */ jsx12(Typography7, { variant: "h6", children: displayData.fullName || displayData.username || displayData.email }),
|
|
3183
|
-
/* @__PURE__ */ jsx12(Typography7, { variant: "body2", color: "text.secondary", children: displayData.role || "Usuario" }),
|
|
3184
|
-
displayData.isActive !== void 0 && /* @__PURE__ */ jsx12(
|
|
3185
|
-
Chip,
|
|
3186
|
-
{
|
|
3187
|
-
label: displayData.isActive ? "Activo" : "Inactivo",
|
|
3188
|
-
color: displayData.isActive ? "success" : "error",
|
|
3189
|
-
size: "small",
|
|
3190
|
-
sx: { mt: 0.5 }
|
|
3191
|
-
}
|
|
3192
|
-
)
|
|
3193
|
-
] })
|
|
3194
|
-
] }),
|
|
3195
|
-
/* @__PURE__ */ jsx12(Box8, { display: "grid", gridTemplateColumns: "repeat(auto-fit, minmax(250px, 1fr))", gap: 2, children: basicFields.map(
|
|
3196
|
-
({ key, label, icon }) => displayData[key] ? /* @__PURE__ */ jsxs9(Box8, { display: "flex", alignItems: "center", children: [
|
|
3197
|
-
/* @__PURE__ */ jsx12(Box8, { sx: { mr: 1, color: "text.secondary" }, children: icon }),
|
|
3198
|
-
/* @__PURE__ */ jsxs9(Box8, { children: [
|
|
3199
|
-
/* @__PURE__ */ jsx12(Typography7, { variant: "caption", color: "text.secondary", children: label }),
|
|
3200
|
-
/* @__PURE__ */ jsx12(Typography7, { variant: "body2", children: renderFieldValue(key, displayData[key]) })
|
|
3201
|
-
] })
|
|
3202
|
-
] }, key) : null
|
|
3203
|
-
) }),
|
|
3204
|
-
displayData.permissions && Array.isArray(displayData.permissions) && displayData.permissions.length > 0 && /* @__PURE__ */ jsxs9(Box8, { mt: 2, children: [
|
|
3205
|
-
/* @__PURE__ */ jsx12(Typography7, { variant: "caption", color: "text.secondary", display: "block", children: "Permisos" }),
|
|
3206
|
-
/* @__PURE__ */ jsxs9(Box8, { display: "flex", flexWrap: "wrap", gap: 0.5, mt: 0.5, children: [
|
|
3207
|
-
displayData.permissions.slice(0, 5).map((permission, index) => /* @__PURE__ */ jsx12(Chip, { label: permission, size: "small", variant: "outlined" }, index)),
|
|
3208
|
-
displayData.permissions.length > 5 && /* @__PURE__ */ jsx12(Chip, { label: `+${displayData.permissions.length - 5} m\xE1s`, size: "small" })
|
|
3209
|
-
] })
|
|
3210
|
-
] })
|
|
3211
|
-
] }) }),
|
|
3212
|
-
showExtendedData && /* @__PURE__ */ jsx12(Card, { children: /* @__PURE__ */ jsxs9(CardContent, { children: [
|
|
3213
|
-
/* @__PURE__ */ jsxs9(Box8, { display: "flex", justifyContent: "space-between", alignItems: "center", mb: 2, children: [
|
|
3214
|
-
/* @__PURE__ */ jsxs9(Typography7, { variant: "h6", display: "flex", alignItems: "center", children: [
|
|
3215
|
-
/* @__PURE__ */ jsx12(Info, { sx: { mr: 1 } }),
|
|
3216
|
-
"Informaci\xF3n Detallada"
|
|
3217
|
-
] }),
|
|
3218
|
-
/* @__PURE__ */ jsx12(Chip, { label: `${totalFields} campos totales`, size: "small" })
|
|
3219
|
-
] }),
|
|
3220
|
-
/* @__PURE__ */ jsxs9(List, { dense: true, children: [
|
|
3221
|
-
extendedFields.map(({ key, label }) => displayData[key] !== void 0 && /* @__PURE__ */ jsxs9(ListItem, { divider: true, children: [
|
|
3222
|
-
/* @__PURE__ */ jsx12(ListItemIcon, { children: /* @__PURE__ */ jsx12(Schedule, { fontSize: "small" }) }),
|
|
3223
|
-
/* @__PURE__ */ jsx12(
|
|
3224
|
-
ListItemText,
|
|
3225
|
-
{
|
|
3226
|
-
primary: label,
|
|
3227
|
-
secondary: key.includes("At") || key.includes("Login") ? formatDate(displayData[key]) : renderFieldValue(key, displayData[key])
|
|
3228
|
-
}
|
|
3229
|
-
)
|
|
3230
|
-
] }, key)),
|
|
3231
|
-
customFields.length > 0 && /* @__PURE__ */ jsxs9(Fragment7, { children: [
|
|
3232
|
-
/* @__PURE__ */ jsx12(Divider, { sx: { my: 1 } }),
|
|
3233
|
-
/* @__PURE__ */ jsx12(ListItem, { children: /* @__PURE__ */ jsx12(
|
|
3234
|
-
ListItemText,
|
|
3235
|
-
{
|
|
3236
|
-
primary: /* @__PURE__ */ jsxs9(Box8, { display: "flex", justifyContent: "space-between", alignItems: "center", children: [
|
|
3237
|
-
/* @__PURE__ */ jsxs9(Typography7, { variant: "subtitle2", children: [
|
|
3238
|
-
"Campos Personalizados (",
|
|
3239
|
-
customFields.length,
|
|
3240
|
-
")"
|
|
3241
|
-
] }),
|
|
3242
|
-
/* @__PURE__ */ jsx12(IconButton, { size: "small", onClick: () => setShowAllFields(!showAllFields), children: showAllFields ? /* @__PURE__ */ jsx12(ExpandLess, {}) : /* @__PURE__ */ jsx12(ExpandMore, {}) })
|
|
3243
|
-
] })
|
|
3244
|
-
}
|
|
3245
|
-
) }),
|
|
3246
|
-
/* @__PURE__ */ jsx12(Collapse, { in: showAllFields, children: customFields.map(({ key, label }) => /* @__PURE__ */ jsx12(ListItem, { sx: { pl: 4 }, children: /* @__PURE__ */ jsx12(
|
|
3247
|
-
ListItemText,
|
|
3248
|
-
{
|
|
3249
|
-
primary: label,
|
|
3250
|
-
secondary: renderFieldValue(key, displayData[key])
|
|
3251
|
-
}
|
|
3252
|
-
) }, key)) })
|
|
3253
|
-
] })
|
|
3254
|
-
] }),
|
|
3255
|
-
/* @__PURE__ */ jsxs9(Box8, { mt: 2, display: "flex", justifyContent: "space-between", alignItems: "center", children: [
|
|
3256
|
-
/* @__PURE__ */ jsxs9(Typography7, { variant: "caption", color: "text.secondary", children: [
|
|
3257
|
-
"\xDAltima actualizaci\xF3n: ",
|
|
3258
|
-
formatDate(displayData.updatedAt)
|
|
3259
|
-
] }),
|
|
3260
|
-
/* @__PURE__ */ jsx12(IconButton, { size: "small", onClick: refreshProfile, disabled: loading, children: /* @__PURE__ */ jsx12(Typography7, { variant: "caption", children: "Actualizar" }) })
|
|
3261
|
-
] })
|
|
3262
|
-
] }) })
|
|
3263
|
-
] });
|
|
3264
|
-
};
|
|
3265
|
-
var UserProfileDisplay_default = UserProfileDisplay;
|
|
3266
|
-
|
|
3267
|
-
// src/components/PublicPolicies/Policies.tsx
|
|
3268
|
-
import { useRef as useRef4 } from "react";
|
|
3269
|
-
import { useTranslation as useTranslation4 } from "react-i18next";
|
|
3270
|
-
import {
|
|
3271
|
-
Box as Box11,
|
|
3272
|
-
Typography as Typography10,
|
|
3273
|
-
Button as Button7,
|
|
3274
|
-
Stack as Stack3,
|
|
3275
|
-
Alert as Alert8,
|
|
3276
|
-
Divider as Divider3
|
|
3277
|
-
} from "@mui/material";
|
|
3278
|
-
import { Add } from "@mui/icons-material";
|
|
3279
|
-
|
|
3280
|
-
// src/components/PublicPolicies/PolicyItem/PolicyItem.tsx
|
|
3281
|
-
import { forwardRef } from "react";
|
|
3282
|
-
import { useTranslation as useTranslation3 } from "react-i18next";
|
|
3283
|
-
import {
|
|
3284
|
-
Box as Box10,
|
|
3285
|
-
FormControl,
|
|
3286
|
-
InputLabel,
|
|
3287
|
-
Select,
|
|
3288
|
-
MenuItem,
|
|
3289
|
-
IconButton as IconButton2,
|
|
3290
|
-
Typography as Typography9,
|
|
3291
|
-
FormHelperText as FormHelperText2,
|
|
3292
|
-
Stack as Stack2,
|
|
3293
|
-
Paper,
|
|
3294
|
-
Divider as Divider2,
|
|
3295
|
-
Button as Button6
|
|
3296
|
-
} from "@mui/material";
|
|
3297
|
-
import { Delete, SelectAll as SelectAll2, ClearAll as ClearAll2 } from "@mui/icons-material";
|
|
3298
|
-
|
|
3299
|
-
// src/components/PublicPolicies/FieldSelector/FieldSelector.tsx
|
|
3300
|
-
import { useState as useState10, useEffect as useEffect10, useRef as useRef3 } from "react";
|
|
3301
|
-
import { useTranslation as useTranslation2 } from "react-i18next";
|
|
3302
|
-
import {
|
|
3303
|
-
Box as Box9,
|
|
3304
|
-
Typography as Typography8,
|
|
3305
|
-
Button as Button5,
|
|
3306
|
-
Stack,
|
|
3307
|
-
FormHelperText,
|
|
3308
|
-
ToggleButton,
|
|
3309
|
-
ToggleButtonGroup
|
|
3310
|
-
} from "@mui/material";
|
|
3311
|
-
import {
|
|
3312
|
-
CheckCircle,
|
|
3313
|
-
Cancel,
|
|
3314
|
-
SelectAll,
|
|
3315
|
-
ClearAll
|
|
3316
|
-
} from "@mui/icons-material";
|
|
3317
|
-
import { jsx as jsx13, jsxs as jsxs10 } from "react/jsx-runtime";
|
|
3318
|
-
var FieldSelector = ({
|
|
3319
|
-
value,
|
|
3320
|
-
onChange,
|
|
3321
|
-
availableFields,
|
|
3322
|
-
error,
|
|
3323
|
-
disabled = false
|
|
3324
|
-
}) => {
|
|
3325
|
-
const { t } = useTranslation2();
|
|
3326
|
-
const [mode, setMode] = useState10("custom");
|
|
3327
|
-
const isUpdatingRef = useRef3(false);
|
|
3328
|
-
useEffect10(() => {
|
|
3329
|
-
const current = value || { allow: [], owner_allow: [], deny: [] };
|
|
3330
|
-
const all = new Set(availableFields);
|
|
3331
|
-
const allow = (current.allow || []).filter((f) => all.has(f));
|
|
3332
|
-
const owner = (current.owner_allow || []).filter((f) => all.has(f));
|
|
3333
|
-
const deny = (current.deny || []).filter((f) => all.has(f));
|
|
3334
|
-
availableFields.forEach((f) => {
|
|
3335
|
-
if (!allow.includes(f) && !owner.includes(f) && !deny.includes(f)) deny.push(f);
|
|
3336
|
-
});
|
|
3337
|
-
const normalized = { allow, owner_allow: owner, deny };
|
|
3338
|
-
if (JSON.stringify(normalized) !== JSON.stringify(current)) {
|
|
3339
|
-
onChange(normalized);
|
|
3340
|
-
}
|
|
3341
|
-
if (allow.length === availableFields.length) setMode("all");
|
|
3342
|
-
else if (deny.length === availableFields.length) setMode("none");
|
|
3343
|
-
else setMode("custom");
|
|
3344
|
-
}, [availableFields, value]);
|
|
3345
|
-
const setAllAllow = () => {
|
|
3346
|
-
isUpdatingRef.current = true;
|
|
3347
|
-
onChange({ allow: [...availableFields], owner_allow: [], deny: [] });
|
|
3348
|
-
setMode("all");
|
|
3349
|
-
setTimeout(() => {
|
|
3350
|
-
isUpdatingRef.current = false;
|
|
3351
|
-
}, 0);
|
|
3352
|
-
};
|
|
3353
|
-
const setAllDeny = () => {
|
|
3354
|
-
isUpdatingRef.current = true;
|
|
3355
|
-
onChange({ allow: [], owner_allow: [], deny: [...availableFields] });
|
|
3356
|
-
setMode("none");
|
|
3357
|
-
setTimeout(() => {
|
|
3358
|
-
isUpdatingRef.current = false;
|
|
3359
|
-
}, 0);
|
|
3360
|
-
};
|
|
3361
|
-
const getFieldState = (fieldName) => {
|
|
3362
|
-
if (value?.allow?.includes(fieldName)) return "allow";
|
|
3363
|
-
if (value?.owner_allow?.includes(fieldName)) return "owner_allow";
|
|
3364
|
-
return "deny";
|
|
3365
|
-
};
|
|
3366
|
-
const setFieldState = (fieldName, state) => {
|
|
3367
|
-
isUpdatingRef.current = true;
|
|
3368
|
-
const allow = new Set(value?.allow || []);
|
|
3369
|
-
const owner = new Set(value?.owner_allow || []);
|
|
3370
|
-
const deny = new Set(value?.deny || []);
|
|
3371
|
-
allow.delete(fieldName);
|
|
3372
|
-
owner.delete(fieldName);
|
|
3373
|
-
deny.delete(fieldName);
|
|
3374
|
-
if (state === "allow") allow.add(fieldName);
|
|
3375
|
-
if (state === "owner_allow") owner.add(fieldName);
|
|
3376
|
-
if (state === "deny") deny.add(fieldName);
|
|
3377
|
-
onChange({ allow: Array.from(allow), owner_allow: Array.from(owner), deny: Array.from(deny) });
|
|
3378
|
-
setMode("custom");
|
|
3379
|
-
setTimeout(() => {
|
|
3380
|
-
isUpdatingRef.current = false;
|
|
3381
|
-
}, 0);
|
|
3382
|
-
};
|
|
3383
|
-
if (availableFields.length === 0) {
|
|
3384
|
-
return /* @__PURE__ */ jsxs10(Box9, { children: [
|
|
3385
|
-
/* @__PURE__ */ jsx13(Typography8, { variant: "body2", color: "text.secondary", sx: { mb: 1 }, children: t("modules.form.publicPolicies.fields.conditions.label") }),
|
|
3386
|
-
/* @__PURE__ */ jsx13(Typography8, { variant: "body2", color: "text.secondary", sx: { fontStyle: "italic" }, children: t("modules.form.publicPolicies.fields.conditions.noFieldsAvailable") }),
|
|
3387
|
-
error && /* @__PURE__ */ jsx13(FormHelperText, { error: true, sx: { mt: 1 }, children: error })
|
|
3388
|
-
] });
|
|
3389
|
-
}
|
|
3390
|
-
return /* @__PURE__ */ jsxs10(Box9, { children: [
|
|
3391
|
-
/* @__PURE__ */ jsx13(Typography8, { variant: "body2", color: "text.secondary", sx: { mb: 2 }, children: t("modules.form.publicPolicies.fields.conditions.label") }),
|
|
3392
|
-
/* @__PURE__ */ jsxs10(Stack, { direction: "row", spacing: 1, sx: { mb: 3 }, children: [
|
|
3393
|
-
/* @__PURE__ */ jsx13(
|
|
3394
|
-
Button5,
|
|
3395
|
-
{
|
|
3396
|
-
variant: mode === "all" ? "contained" : "outlined",
|
|
3397
|
-
startIcon: /* @__PURE__ */ jsx13(SelectAll, {}),
|
|
3398
|
-
onClick: setAllAllow,
|
|
3399
|
-
disabled,
|
|
3400
|
-
size: "small",
|
|
3401
|
-
sx: {
|
|
3402
|
-
minWidth: 120,
|
|
3403
|
-
...mode === "all" && {
|
|
3404
|
-
backgroundColor: "#16a34a",
|
|
3405
|
-
"&:hover": { backgroundColor: "#15803d" }
|
|
3406
|
-
}
|
|
3407
|
-
},
|
|
3408
|
-
children: t("modules.form.publicPolicies.fields.conditions.allFields")
|
|
3409
|
-
}
|
|
3410
|
-
),
|
|
3411
|
-
/* @__PURE__ */ jsx13(
|
|
3412
|
-
Button5,
|
|
3413
|
-
{
|
|
3414
|
-
variant: mode === "none" ? "contained" : "outlined",
|
|
3415
|
-
startIcon: /* @__PURE__ */ jsx13(ClearAll, {}),
|
|
3416
|
-
onClick: setAllDeny,
|
|
3417
|
-
disabled,
|
|
3418
|
-
size: "small",
|
|
3419
|
-
sx: {
|
|
3420
|
-
minWidth: 120,
|
|
3421
|
-
...mode === "none" && {
|
|
3422
|
-
backgroundColor: "#cf222e",
|
|
3423
|
-
"&:hover": { backgroundColor: "#bc1f2c" }
|
|
3424
|
-
}
|
|
3425
|
-
},
|
|
3426
|
-
children: t("modules.form.publicPolicies.fields.conditions.noFields")
|
|
3427
|
-
}
|
|
3428
|
-
)
|
|
3429
|
-
] }),
|
|
3430
|
-
/* @__PURE__ */ jsxs10(Box9, { sx: { p: 2, border: "1px solid #d1d9e0", borderRadius: 1, backgroundColor: "#f6f8fa" }, children: [
|
|
3431
|
-
/* @__PURE__ */ jsx13(Typography8, { variant: "body2", color: "text.secondary", sx: { mb: 2 }, children: t("modules.form.publicPolicies.fields.conditions.help") }),
|
|
3432
|
-
/* @__PURE__ */ jsx13(Stack, { spacing: 1, children: availableFields.map((fieldName) => {
|
|
3433
|
-
const fieldState = getFieldState(fieldName);
|
|
3434
|
-
return /* @__PURE__ */ jsxs10(Stack, { direction: "row", spacing: 1, alignItems: "center", children: [
|
|
3435
|
-
/* @__PURE__ */ jsx13(Typography8, { variant: "body2", sx: { minWidth: 100, fontFamily: "monospace" }, children: fieldName }),
|
|
3436
|
-
/* @__PURE__ */ jsxs10(ToggleButtonGroup, { value: fieldState, exclusive: true, size: "small", children: [
|
|
3437
|
-
/* @__PURE__ */ jsxs10(
|
|
3438
|
-
ToggleButton,
|
|
3439
|
-
{
|
|
3440
|
-
value: "allow",
|
|
3441
|
-
onClick: () => setFieldState(fieldName, "allow"),
|
|
3442
|
-
disabled,
|
|
3443
|
-
sx: {
|
|
3444
|
-
px: 2,
|
|
3445
|
-
color: fieldState === "allow" ? "#ffffff" : "#6b7280",
|
|
3446
|
-
backgroundColor: fieldState === "allow" ? "#16a34a" : "#f3f4f6",
|
|
3447
|
-
borderColor: fieldState === "allow" ? "#16a34a" : "#d1d5db",
|
|
3448
|
-
"&:hover": {
|
|
3449
|
-
backgroundColor: fieldState === "allow" ? "#15803d" : "#e5e7eb",
|
|
3450
|
-
borderColor: fieldState === "allow" ? "#15803d" : "#9ca3af"
|
|
3451
|
-
},
|
|
3452
|
-
"&.Mui-selected": {
|
|
3453
|
-
backgroundColor: "#16a34a",
|
|
3454
|
-
color: "#ffffff",
|
|
3455
|
-
"&:hover": {
|
|
3456
|
-
backgroundColor: "#15803d"
|
|
3457
|
-
}
|
|
3458
|
-
}
|
|
3459
|
-
},
|
|
3460
|
-
children: [
|
|
3461
|
-
/* @__PURE__ */ jsx13(CheckCircle, { sx: { fontSize: 16, mr: 0.5 } }),
|
|
3462
|
-
t("modules.form.publicPolicies.fields.conditions.states.allow")
|
|
3463
|
-
]
|
|
3464
|
-
}
|
|
3465
|
-
),
|
|
3466
|
-
/* @__PURE__ */ jsx13(
|
|
3467
|
-
ToggleButton,
|
|
3468
|
-
{
|
|
3469
|
-
value: "owner_allow",
|
|
3470
|
-
onClick: () => setFieldState(fieldName, "owner_allow"),
|
|
3471
|
-
disabled,
|
|
3472
|
-
sx: {
|
|
3473
|
-
px: 2,
|
|
3474
|
-
color: fieldState === "owner_allow" ? "#ffffff" : "#6b7280",
|
|
3475
|
-
backgroundColor: fieldState === "owner_allow" ? "#0ea5e9" : "#f3f4f6",
|
|
3476
|
-
borderColor: fieldState === "owner_allow" ? "#0ea5e9" : "#d1d5db",
|
|
3477
|
-
"&:hover": {
|
|
3478
|
-
backgroundColor: fieldState === "owner_allow" ? "#0284c7" : "#e5e7eb",
|
|
3479
|
-
borderColor: fieldState === "owner_allow" ? "#0284c7" : "#9ca3af"
|
|
3480
|
-
},
|
|
3481
|
-
"&.Mui-selected": {
|
|
3482
|
-
backgroundColor: "#0ea5e9",
|
|
3483
|
-
color: "#ffffff",
|
|
3484
|
-
"&:hover": {
|
|
3485
|
-
backgroundColor: "#0284c7"
|
|
3486
|
-
}
|
|
3487
|
-
}
|
|
3488
|
-
},
|
|
3489
|
-
children: t("modules.form.publicPolicies.fields.conditions.states.ownerAllow")
|
|
3490
|
-
}
|
|
3491
|
-
),
|
|
3492
|
-
/* @__PURE__ */ jsxs10(
|
|
3493
|
-
ToggleButton,
|
|
3494
|
-
{
|
|
3495
|
-
value: "deny",
|
|
3496
|
-
onClick: () => setFieldState(fieldName, "deny"),
|
|
3497
|
-
disabled,
|
|
3498
|
-
sx: {
|
|
3499
|
-
px: 2,
|
|
3500
|
-
color: fieldState === "deny" ? "#ffffff" : "#6b7280",
|
|
3501
|
-
backgroundColor: fieldState === "deny" ? "#dc2626" : "#f3f4f6",
|
|
3502
|
-
borderColor: fieldState === "deny" ? "#dc2626" : "#d1d5db",
|
|
3503
|
-
"&:hover": {
|
|
3504
|
-
backgroundColor: fieldState === "deny" ? "#b91c1c" : "#e5e7eb",
|
|
3505
|
-
borderColor: fieldState === "deny" ? "#b91c1c" : "#9ca3af"
|
|
3506
|
-
},
|
|
3507
|
-
"&.Mui-selected": {
|
|
3508
|
-
backgroundColor: "#dc2626",
|
|
3509
|
-
color: "#ffffff",
|
|
3510
|
-
"&:hover": {
|
|
3511
|
-
backgroundColor: "#b91c1c"
|
|
3512
|
-
}
|
|
3513
|
-
}
|
|
3514
|
-
},
|
|
3515
|
-
children: [
|
|
3516
|
-
/* @__PURE__ */ jsx13(Cancel, { sx: { fontSize: 16, mr: 0.5 } }),
|
|
3517
|
-
t("modules.form.publicPolicies.fields.conditions.states.deny")
|
|
3518
|
-
]
|
|
3519
|
-
}
|
|
3520
|
-
)
|
|
3521
|
-
] })
|
|
3522
|
-
] }, fieldName);
|
|
3523
|
-
}) })
|
|
3524
|
-
] }),
|
|
3525
|
-
error && /* @__PURE__ */ jsx13(FormHelperText, { error: true, sx: { mt: 1 }, children: error })
|
|
3526
|
-
] });
|
|
3527
|
-
};
|
|
3528
|
-
var FieldSelector_default = FieldSelector;
|
|
3529
|
-
|
|
3530
|
-
// src/components/PublicPolicies/constants.ts
|
|
3531
|
-
var POLICY_ACTIONS = ["create", "read", "update", "delete"];
|
|
3532
|
-
var PREFERRED_POLICY_ORDER = [
|
|
3533
|
-
"create",
|
|
3534
|
-
"read",
|
|
3535
|
-
"update",
|
|
3536
|
-
"delete"
|
|
3537
|
-
];
|
|
3538
|
-
|
|
3539
|
-
// src/components/PublicPolicies/PolicyItem/PolicyItem.tsx
|
|
3540
|
-
import { jsx as jsx14, jsxs as jsxs11 } from "react/jsx-runtime";
|
|
3541
|
-
var PolicyItem = forwardRef(({
|
|
3542
|
-
policy,
|
|
3543
|
-
onChange,
|
|
3544
|
-
onRemove,
|
|
3545
|
-
availableFields,
|
|
3546
|
-
isSubmitting = false,
|
|
3547
|
-
usedActions,
|
|
3548
|
-
error
|
|
3549
|
-
}, ref) => {
|
|
3550
|
-
const { t } = useTranslation3();
|
|
3551
|
-
const takenActions = new Set(Array.from(usedActions || []));
|
|
3552
|
-
takenActions.delete(policy.action);
|
|
3553
|
-
const actionOptions = POLICY_ACTIONS.map((a) => ({
|
|
3554
|
-
value: a,
|
|
3555
|
-
label: t(`modules.form.publicPolicies.fields.action.options.${a}`)
|
|
3556
|
-
}));
|
|
3557
|
-
return /* @__PURE__ */ jsxs11(
|
|
3558
|
-
Paper,
|
|
3559
|
-
{
|
|
3560
|
-
ref,
|
|
3561
|
-
sx: {
|
|
3562
|
-
p: 3,
|
|
3563
|
-
border: "1px solid #d1d9e0",
|
|
3564
|
-
borderRadius: 2,
|
|
3565
|
-
position: "relative",
|
|
3566
|
-
backgroundColor: "#ffffff"
|
|
3567
|
-
},
|
|
3568
|
-
children: [
|
|
3569
|
-
/* @__PURE__ */ jsxs11(Box10, { sx: { display: "flex", justifyContent: "space-between", alignItems: "flex-start", mb: 3 }, children: [
|
|
3570
|
-
/* @__PURE__ */ jsx14(
|
|
3571
|
-
Typography9,
|
|
3572
|
-
{
|
|
3573
|
-
variant: "subtitle1",
|
|
3574
|
-
sx: {
|
|
3575
|
-
fontWeight: 600,
|
|
3576
|
-
color: "#111418",
|
|
3577
|
-
fontSize: "1rem"
|
|
3578
|
-
},
|
|
3579
|
-
children: t("modules.form.publicPolicies.policyTitle")
|
|
3580
|
-
}
|
|
3581
|
-
),
|
|
3582
|
-
/* @__PURE__ */ jsx14(
|
|
3583
|
-
IconButton2,
|
|
3584
|
-
{
|
|
3585
|
-
onClick: onRemove,
|
|
3586
|
-
size: "small",
|
|
3587
|
-
disabled: isSubmitting,
|
|
3588
|
-
"aria-label": t("modules.form.publicPolicies.removePolicy"),
|
|
3589
|
-
sx: {
|
|
3590
|
-
color: "#656d76",
|
|
3591
|
-
"&:hover": {
|
|
3592
|
-
color: "#cf222e",
|
|
3593
|
-
backgroundColor: "rgba(207, 34, 46, 0.1)"
|
|
3594
|
-
}
|
|
3595
|
-
},
|
|
3596
|
-
children: /* @__PURE__ */ jsx14(Delete, {})
|
|
3597
|
-
}
|
|
3598
|
-
)
|
|
3599
|
-
] }),
|
|
3600
|
-
/* @__PURE__ */ jsxs11(Stack2, { spacing: 3, children: [
|
|
3601
|
-
/* @__PURE__ */ jsx14(Stack2, { direction: { xs: "column", md: "row" }, spacing: 2, children: /* @__PURE__ */ jsx14(Box10, { sx: { flex: 1, minWidth: 200 }, children: /* @__PURE__ */ jsxs11(FormControl, { fullWidth: true, children: [
|
|
3602
|
-
/* @__PURE__ */ jsx14(InputLabel, { children: t("modules.form.publicPolicies.fields.action.label") }),
|
|
3603
|
-
/* @__PURE__ */ jsx14(
|
|
3604
|
-
Select,
|
|
3605
|
-
{
|
|
3606
|
-
value: policy.action,
|
|
3607
|
-
label: t("modules.form.publicPolicies.fields.action.label"),
|
|
3608
|
-
disabled: isSubmitting,
|
|
3609
|
-
onChange: (e) => {
|
|
3610
|
-
const newAction = e.target.value;
|
|
3611
|
-
const next = { ...policy, action: newAction };
|
|
3612
|
-
if (newAction === "delete") {
|
|
3613
|
-
next.permission = "deny";
|
|
3614
|
-
delete next.fields;
|
|
3615
|
-
} else {
|
|
3616
|
-
next.fields = { allow: [], owner_allow: [], deny: availableFields };
|
|
3617
|
-
delete next.permission;
|
|
3618
|
-
}
|
|
3619
|
-
onChange(next);
|
|
3620
|
-
},
|
|
3621
|
-
sx: {
|
|
3622
|
-
backgroundColor: "#ffffff",
|
|
3623
|
-
"&:hover .MuiOutlinedInput-notchedOutline": {
|
|
3624
|
-
borderColor: "#8c959f"
|
|
3625
|
-
},
|
|
3626
|
-
"&.Mui-focused .MuiOutlinedInput-notchedOutline": {
|
|
3627
|
-
borderColor: "#0969da",
|
|
3628
|
-
borderWidth: 2
|
|
3629
|
-
}
|
|
3630
|
-
},
|
|
3631
|
-
children: actionOptions.map((option) => {
|
|
3632
|
-
const disabledOption = takenActions.has(option.value);
|
|
3633
|
-
return /* @__PURE__ */ jsx14(MenuItem, { value: option.value, disabled: disabledOption, children: option.label }, option.value);
|
|
3634
|
-
})
|
|
3635
|
-
}
|
|
3636
|
-
),
|
|
3637
|
-
error && /* @__PURE__ */ jsx14(FormHelperText2, { error: true, children: error })
|
|
3638
|
-
] }) }) }),
|
|
3639
|
-
policy.action === "delete" ? /* @__PURE__ */ jsxs11(Box10, { children: [
|
|
3640
|
-
/* @__PURE__ */ jsx14(Typography9, { variant: "body2", color: "text.secondary", sx: { mb: 2 }, children: t("modules.form.publicPolicies.fields.conditions.label") }),
|
|
3641
|
-
/* @__PURE__ */ jsxs11(Stack2, { direction: "row", spacing: 1, sx: { mb: 3 }, children: [
|
|
3642
|
-
/* @__PURE__ */ jsx14(
|
|
3643
|
-
Button6,
|
|
3644
|
-
{
|
|
3645
|
-
variant: policy.permission === "*" ? "contained" : "outlined",
|
|
3646
|
-
startIcon: /* @__PURE__ */ jsx14(SelectAll2, {}),
|
|
3647
|
-
onClick: () => onChange({ ...policy, permission: "*" }),
|
|
3648
|
-
disabled: isSubmitting,
|
|
3649
|
-
size: "small",
|
|
3650
|
-
sx: {
|
|
3651
|
-
minWidth: 140,
|
|
3652
|
-
whiteSpace: "nowrap",
|
|
3653
|
-
...policy.permission === "*" && {
|
|
3654
|
-
backgroundColor: "#16a34a",
|
|
3655
|
-
"&:hover": { backgroundColor: "#15803d" }
|
|
3656
|
-
}
|
|
3657
|
-
},
|
|
3658
|
-
children: t("modules.form.publicPolicies.fields.conditions.allFields")
|
|
3659
|
-
}
|
|
3660
|
-
),
|
|
3661
|
-
/* @__PURE__ */ jsx14(
|
|
3662
|
-
Button6,
|
|
3663
|
-
{
|
|
3664
|
-
variant: policy.permission === "owner" ? "contained" : "outlined",
|
|
3665
|
-
onClick: () => onChange({ ...policy, permission: "owner" }),
|
|
3666
|
-
disabled: isSubmitting,
|
|
3667
|
-
size: "small",
|
|
3668
|
-
sx: {
|
|
3669
|
-
minWidth: 140,
|
|
3670
|
-
whiteSpace: "nowrap",
|
|
3671
|
-
...policy.permission === "owner" && {
|
|
3672
|
-
backgroundColor: "#0ea5e9",
|
|
3673
|
-
"&:hover": { backgroundColor: "#0284c7" }
|
|
3674
|
-
}
|
|
3675
|
-
},
|
|
3676
|
-
children: t("modules.form.publicPolicies.fields.conditions.states.ownerAllow")
|
|
3677
|
-
}
|
|
3678
|
-
),
|
|
3679
|
-
/* @__PURE__ */ jsx14(
|
|
3680
|
-
Button6,
|
|
3681
|
-
{
|
|
3682
|
-
variant: policy.permission === "deny" ? "contained" : "outlined",
|
|
3683
|
-
startIcon: /* @__PURE__ */ jsx14(ClearAll2, {}),
|
|
3684
|
-
onClick: () => onChange({ ...policy, permission: "deny" }),
|
|
3685
|
-
disabled: isSubmitting,
|
|
3686
|
-
size: "small",
|
|
3687
|
-
sx: {
|
|
3688
|
-
minWidth: 140,
|
|
3689
|
-
whiteSpace: "nowrap",
|
|
3690
|
-
...policy.permission === "deny" && {
|
|
3691
|
-
backgroundColor: "#cf222e",
|
|
3692
|
-
"&:hover": { backgroundColor: "#bc1f2c" }
|
|
3693
|
-
}
|
|
3694
|
-
},
|
|
3695
|
-
children: t("modules.form.publicPolicies.fields.conditions.noFields")
|
|
3696
|
-
}
|
|
3697
|
-
)
|
|
3698
|
-
] })
|
|
3699
|
-
] }) : /* @__PURE__ */ jsx14(
|
|
3700
|
-
FieldSelector_default,
|
|
3701
|
-
{
|
|
3702
|
-
value: policy.fields || { allow: [], owner_allow: [], deny: [] },
|
|
3703
|
-
onChange: (nextFields) => onChange({ ...policy, fields: nextFields }),
|
|
3704
|
-
availableFields,
|
|
3705
|
-
disabled: isSubmitting
|
|
3706
|
-
}
|
|
3707
|
-
),
|
|
3708
|
-
/* @__PURE__ */ jsx14(Paper, { variant: "outlined", sx: { p: 2, backgroundColor: "#f9fafb" }, children: policy.action === "delete" ? /* @__PURE__ */ jsxs11(Typography9, { variant: "body2", sx: { fontFamily: "monospace", color: "text.secondary" }, children: [
|
|
3709
|
-
/* @__PURE__ */ jsxs11(Box10, { component: "span", sx: {
|
|
3710
|
-
color: policy.permission === "*" ? "#16a34a" : policy.permission === "owner" ? "#0ea5e9" : "#dc2626"
|
|
3711
|
-
}, children: [
|
|
3712
|
-
t("modules.form.publicPolicies.fields.conditions.states.allow"),
|
|
3713
|
-
":"
|
|
3714
|
-
] }),
|
|
3715
|
-
" ",
|
|
3716
|
-
policy.permission || "-"
|
|
3717
|
-
] }) : /* @__PURE__ */ jsxs11(Stack2, { spacing: 0.5, divider: /* @__PURE__ */ jsx14(Divider2, { sx: { borderColor: "#e5e7eb" } }), children: [
|
|
3718
|
-
/* @__PURE__ */ jsxs11(Typography9, { variant: "body2", sx: { fontFamily: "monospace", color: "text.secondary" }, children: [
|
|
3719
|
-
/* @__PURE__ */ jsxs11(Box10, { component: "span", sx: { color: "#16a34a" }, children: [
|
|
3720
|
-
t("modules.form.publicPolicies.fields.conditions.states.allow"),
|
|
3721
|
-
":"
|
|
3722
|
-
] }),
|
|
3723
|
-
" ",
|
|
3724
|
-
(policy?.fields?.allow || []).join(", ") || "-"
|
|
3725
|
-
] }),
|
|
3726
|
-
/* @__PURE__ */ jsxs11(Typography9, { variant: "body2", sx: { fontFamily: "monospace", color: "text.secondary" }, children: [
|
|
3727
|
-
/* @__PURE__ */ jsxs11(Box10, { component: "span", sx: { color: "#0ea5e9" }, children: [
|
|
3728
|
-
t("modules.form.publicPolicies.fields.conditions.states.ownerAllow"),
|
|
3729
|
-
":"
|
|
3730
|
-
] }),
|
|
3731
|
-
" ",
|
|
3732
|
-
(policy?.fields?.owner_allow || []).join(", ") || "-"
|
|
3733
|
-
] }),
|
|
3734
|
-
/* @__PURE__ */ jsxs11(Typography9, { variant: "body2", sx: { fontFamily: "monospace", color: "text.secondary" }, children: [
|
|
3735
|
-
/* @__PURE__ */ jsxs11(Box10, { component: "span", sx: { color: "#dc2626" }, children: [
|
|
3736
|
-
t("modules.form.publicPolicies.fields.conditions.states.deny"),
|
|
3737
|
-
":"
|
|
3738
|
-
] }),
|
|
3739
|
-
" ",
|
|
3740
|
-
(policy?.fields?.deny || []).join(", ") || "-"
|
|
3741
|
-
] })
|
|
3742
|
-
] }) })
|
|
3743
|
-
] })
|
|
3744
|
-
]
|
|
3745
|
-
}
|
|
3746
|
-
);
|
|
3747
|
-
});
|
|
3748
|
-
var PolicyItem_default = PolicyItem;
|
|
3749
|
-
|
|
3750
|
-
// src/components/PublicPolicies/Policies.tsx
|
|
3751
|
-
import { Fragment as Fragment8, jsx as jsx15, jsxs as jsxs12 } from "react/jsx-runtime";
|
|
3752
|
-
var generateId = () => {
|
|
3753
|
-
const c = globalThis?.crypto;
|
|
3754
|
-
if (c && typeof c.randomUUID === "function") return c.randomUUID();
|
|
3755
|
-
return `${Date.now()}-${Math.random().toString(16).slice(2)}`;
|
|
3756
|
-
};
|
|
3757
|
-
var Policies = ({
|
|
3758
|
-
policies,
|
|
3759
|
-
onChange,
|
|
3760
|
-
availableFields,
|
|
3761
|
-
errors,
|
|
3762
|
-
isSubmitting = false
|
|
3763
|
-
}) => {
|
|
3764
|
-
const { t } = useTranslation4();
|
|
3765
|
-
const policyRefs = useRef4({});
|
|
3766
|
-
const takenActions = new Set((policies || []).map((p) => p.action).filter(Boolean));
|
|
3767
|
-
const remainingActions = PREFERRED_POLICY_ORDER.filter((a) => !takenActions.has(a));
|
|
3768
|
-
const canAddPolicy = remainingActions.length > 0;
|
|
3769
|
-
const addPolicy = () => {
|
|
3770
|
-
const defaultAction = remainingActions[0] || "create";
|
|
3771
|
-
const newPolicy = {
|
|
3772
|
-
id: generateId(),
|
|
3773
|
-
action: defaultAction
|
|
3774
|
-
};
|
|
3775
|
-
if (defaultAction === "delete") {
|
|
3776
|
-
newPolicy.permission = "deny";
|
|
3777
|
-
} else {
|
|
3778
|
-
newPolicy.fields = {
|
|
3779
|
-
allow: [],
|
|
3780
|
-
owner_allow: [],
|
|
3781
|
-
deny: availableFields
|
|
3782
|
-
};
|
|
3783
|
-
}
|
|
3784
|
-
const next = [...policies || [], newPolicy];
|
|
3785
|
-
onChange(next);
|
|
3786
|
-
setTimeout(() => {
|
|
3787
|
-
const newIndex = next.length - 1;
|
|
3788
|
-
const el = policyRefs.current[newIndex];
|
|
3789
|
-
if (el) {
|
|
3790
|
-
el.scrollIntoView({ behavior: "smooth", block: "center" });
|
|
3791
|
-
}
|
|
3792
|
-
}, 100);
|
|
3793
|
-
};
|
|
3794
|
-
const removePolicy = (index) => {
|
|
3795
|
-
const next = [...policies];
|
|
3796
|
-
next.splice(index, 1);
|
|
3797
|
-
onChange(next);
|
|
3798
|
-
};
|
|
3799
|
-
const arrayError = (() => {
|
|
3800
|
-
if (!errors) return null;
|
|
3801
|
-
if (typeof errors === "string") return errors;
|
|
3802
|
-
const msg = errors._error;
|
|
3803
|
-
return typeof msg === "string" ? msg : null;
|
|
3804
|
-
})();
|
|
3805
|
-
const usedActions = new Set((policies || []).map((p) => p.action));
|
|
3806
|
-
return /* @__PURE__ */ jsxs12(Fragment8, { children: [
|
|
3807
|
-
/* @__PURE__ */ jsx15(Divider3, { sx: { borderColor: "#e0e4e7" } }),
|
|
3808
|
-
/* @__PURE__ */ jsxs12(Box11, { children: [
|
|
3809
|
-
/* @__PURE__ */ jsx15(Box11, { display: "flex", justifyContent: "space-between", alignItems: "center", mb: 3, children: /* @__PURE__ */ jsxs12(Box11, { children: [
|
|
3810
|
-
/* @__PURE__ */ jsx15(
|
|
3811
|
-
Typography10,
|
|
3812
|
-
{
|
|
3813
|
-
variant: "h6",
|
|
3814
|
-
sx: {
|
|
3815
|
-
fontWeight: 600,
|
|
3816
|
-
color: "#111418",
|
|
3817
|
-
mb: 1
|
|
3818
|
-
},
|
|
3819
|
-
children: t("modules.form.publicPolicies.title")
|
|
3820
|
-
}
|
|
3821
|
-
),
|
|
3822
|
-
/* @__PURE__ */ jsx15(
|
|
3823
|
-
Typography10,
|
|
3824
|
-
{
|
|
3825
|
-
variant: "body2",
|
|
3826
|
-
color: "text.secondary",
|
|
3827
|
-
sx: { fontSize: "0.875rem" },
|
|
3828
|
-
children: t("modules.form.publicPolicies.description")
|
|
3829
|
-
}
|
|
3830
|
-
)
|
|
3831
|
-
] }) }),
|
|
3832
|
-
arrayError && /* @__PURE__ */ jsx15(Alert8, { severity: "error", sx: { mb: 3 }, children: arrayError }),
|
|
3833
|
-
/* @__PURE__ */ jsxs12(Stack3, { spacing: 3, children: [
|
|
3834
|
-
(policies || []).length === 0 ? /* @__PURE__ */ jsx15(Alert8, { severity: "info", children: t("modules.form.publicPolicies.noPolicies") }) : policies.map((policy, index) => /* @__PURE__ */ jsx15(
|
|
3835
|
-
PolicyItem_default,
|
|
3836
|
-
{
|
|
3837
|
-
ref: (el) => {
|
|
3838
|
-
policyRefs.current[index] = el;
|
|
3839
|
-
},
|
|
3840
|
-
policy,
|
|
3841
|
-
onChange: (nextPolicy) => {
|
|
3842
|
-
const next = [...policies];
|
|
3843
|
-
next[index] = nextPolicy;
|
|
3844
|
-
onChange(next);
|
|
3845
|
-
},
|
|
3846
|
-
onRemove: () => removePolicy(index),
|
|
3847
|
-
availableFields,
|
|
3848
|
-
isSubmitting,
|
|
3849
|
-
usedActions,
|
|
3850
|
-
error: typeof errors === "object" && errors && policy.id in errors ? errors[policy.id] : void 0
|
|
3851
|
-
},
|
|
3852
|
-
policy.id
|
|
3853
|
-
)),
|
|
3854
|
-
canAddPolicy && /* @__PURE__ */ jsx15(Box11, { children: /* @__PURE__ */ jsx15(
|
|
3855
|
-
Button7,
|
|
3856
|
-
{
|
|
3857
|
-
type: "button",
|
|
3858
|
-
variant: "outlined",
|
|
3859
|
-
startIcon: /* @__PURE__ */ jsx15(Add, {}),
|
|
3860
|
-
onClick: addPolicy,
|
|
3861
|
-
disabled: isSubmitting,
|
|
3862
|
-
sx: {
|
|
3863
|
-
borderColor: "#d0d7de",
|
|
3864
|
-
color: "#656d76",
|
|
3865
|
-
"&:hover": {
|
|
3866
|
-
borderColor: "#8c959f",
|
|
3867
|
-
backgroundColor: "transparent"
|
|
3868
|
-
}
|
|
3869
|
-
},
|
|
3870
|
-
children: t("modules.form.publicPolicies.addPolicy")
|
|
3871
|
-
}
|
|
3872
|
-
) })
|
|
3873
|
-
] })
|
|
3874
|
-
] })
|
|
3875
|
-
] });
|
|
3876
|
-
};
|
|
3877
|
-
var Policies_default = Policies;
|
|
3878
|
-
|
|
3879
|
-
// src/components/LoginComponent.tsx
|
|
3880
|
-
import { useState as useState11 } from "react";
|
|
3881
|
-
import { Button as Button8, TextField as TextField5, Box as Box12, Alert as Alert9, Typography as Typography11, CircularProgress as CircularProgress7 } from "@mui/material";
|
|
3882
|
-
import { jsx as jsx16, jsxs as jsxs13 } from "react/jsx-runtime";
|
|
3883
|
-
function LoginComponent() {
|
|
3884
|
-
const [email, setEmail] = useState11("");
|
|
3885
|
-
const [password, setPassword] = useState11("");
|
|
3886
|
-
const [showForm, setShowForm] = useState11(false);
|
|
3887
|
-
const {
|
|
3888
|
-
isAuthenticated,
|
|
3889
|
-
isLoading,
|
|
3890
|
-
error,
|
|
3891
|
-
login,
|
|
3892
|
-
logout,
|
|
3893
|
-
refreshTokens,
|
|
3894
|
-
clearError,
|
|
3895
|
-
isExpiringSoon,
|
|
3896
|
-
expiresIn
|
|
3897
|
-
} = useSessionContext();
|
|
3898
|
-
const handleLogin = async (e) => {
|
|
3899
|
-
e.preventDefault();
|
|
3900
|
-
if (!email || !password) {
|
|
3901
|
-
return;
|
|
3902
|
-
}
|
|
3903
|
-
const result = await login(email, password);
|
|
3904
|
-
if (result.success) {
|
|
3905
|
-
setEmail("");
|
|
3906
|
-
setPassword("");
|
|
3907
|
-
setShowForm(false);
|
|
3908
|
-
}
|
|
3909
|
-
};
|
|
3910
|
-
const handleLogout = async () => {
|
|
3911
|
-
await logout();
|
|
3912
|
-
};
|
|
3913
|
-
const handleRefreshTokens = async () => {
|
|
3914
|
-
await refreshTokens();
|
|
3915
|
-
};
|
|
3916
|
-
if (isAuthenticated) {
|
|
3917
|
-
return /* @__PURE__ */ jsxs13(Box12, { sx: { maxWidth: 600, mx: "auto", p: 3 }, children: [
|
|
3918
|
-
/* @__PURE__ */ jsx16(Typography11, { variant: "h4", gutterBottom: true, children: "Welcome! \u{1F389}" }),
|
|
3919
|
-
/* @__PURE__ */ jsx16(Alert9, { severity: "success", sx: { mb: 3 }, children: "You are successfully logged in with Refresh Token Pattern enabled" }),
|
|
3920
|
-
/* @__PURE__ */ jsxs13(Box12, { sx: { mb: 3, p: 2, bgcolor: "background.paper", border: 1, borderColor: "divider", borderRadius: 1 }, children: [
|
|
3921
|
-
/* @__PURE__ */ jsx16(Typography11, { variant: "h6", gutterBottom: true, children: "Token Status" }),
|
|
3922
|
-
/* @__PURE__ */ jsxs13(Typography11, { variant: "body2", color: "text.secondary", children: [
|
|
3923
|
-
"Access Token expires in: ",
|
|
3924
|
-
Math.round(expiresIn / 1e3 / 60),
|
|
3925
|
-
" minutes"
|
|
3926
|
-
] }),
|
|
3927
|
-
isExpiringSoon && /* @__PURE__ */ jsx16(Alert9, { severity: "warning", sx: { mt: 1 }, children: "Token expires soon - automatic refresh will happen" })
|
|
3928
|
-
] }),
|
|
3929
|
-
/* @__PURE__ */ jsxs13(Box12, { sx: { display: "flex", gap: 2, flexWrap: "wrap" }, children: [
|
|
3930
|
-
/* @__PURE__ */ jsx16(
|
|
3931
|
-
Button8,
|
|
3932
|
-
{
|
|
3933
|
-
variant: "contained",
|
|
3934
|
-
onClick: handleRefreshTokens,
|
|
3935
|
-
disabled: isLoading,
|
|
3936
|
-
startIcon: isLoading ? /* @__PURE__ */ jsx16(CircularProgress7, { size: 16 }) : null,
|
|
3937
|
-
children: "Refresh Tokens"
|
|
3938
|
-
}
|
|
3939
|
-
),
|
|
3940
|
-
/* @__PURE__ */ jsx16(
|
|
3941
|
-
Button8,
|
|
3942
|
-
{
|
|
3943
|
-
variant: "outlined",
|
|
3944
|
-
color: "error",
|
|
3945
|
-
onClick: handleLogout,
|
|
3946
|
-
disabled: isLoading,
|
|
3947
|
-
children: "Logout"
|
|
3948
|
-
}
|
|
3949
|
-
)
|
|
3950
|
-
] }),
|
|
3951
|
-
error && /* @__PURE__ */ jsx16(Alert9, { severity: "error", sx: { mt: 2 }, onClose: clearError, children: error })
|
|
3952
|
-
] });
|
|
3953
|
-
}
|
|
3954
|
-
return /* @__PURE__ */ jsxs13(Box12, { sx: { maxWidth: 400, mx: "auto", p: 3 }, children: [
|
|
3955
|
-
/* @__PURE__ */ jsx16(Typography11, { variant: "h4", gutterBottom: true, align: "center", children: "Login with Refresh Tokens" }),
|
|
3956
|
-
/* @__PURE__ */ jsx16(Alert9, { severity: "info", sx: { mb: 3 }, children: "This demo shows the new Refresh Token Pattern with automatic session management" }),
|
|
3957
|
-
!showForm ? /* @__PURE__ */ jsx16(
|
|
3958
|
-
Button8,
|
|
3959
|
-
{
|
|
3960
|
-
fullWidth: true,
|
|
3961
|
-
variant: "contained",
|
|
3962
|
-
size: "large",
|
|
3963
|
-
onClick: () => setShowForm(true),
|
|
3964
|
-
sx: { mt: 2 },
|
|
3965
|
-
children: "Show Login Form"
|
|
3966
|
-
}
|
|
3967
|
-
) : /* @__PURE__ */ jsxs13("form", { onSubmit: handleLogin, children: [
|
|
3968
|
-
/* @__PURE__ */ jsx16(
|
|
3969
|
-
TextField5,
|
|
3970
|
-
{
|
|
3971
|
-
fullWidth: true,
|
|
3972
|
-
label: "Email",
|
|
3973
|
-
type: "email",
|
|
3974
|
-
value: email,
|
|
3975
|
-
onChange: (e) => setEmail(e.target.value),
|
|
3976
|
-
margin: "normal",
|
|
3977
|
-
required: true,
|
|
3978
|
-
autoComplete: "email"
|
|
3979
|
-
}
|
|
3980
|
-
),
|
|
3981
|
-
/* @__PURE__ */ jsx16(
|
|
3982
|
-
TextField5,
|
|
3983
|
-
{
|
|
3984
|
-
fullWidth: true,
|
|
3985
|
-
label: "Password",
|
|
3986
|
-
type: "password",
|
|
3987
|
-
value: password,
|
|
3988
|
-
onChange: (e) => setPassword(e.target.value),
|
|
3989
|
-
margin: "normal",
|
|
3990
|
-
required: true,
|
|
3991
|
-
autoComplete: "current-password"
|
|
3992
|
-
}
|
|
3993
|
-
),
|
|
3994
|
-
/* @__PURE__ */ jsx16(
|
|
3995
|
-
Button8,
|
|
3996
|
-
{
|
|
3997
|
-
type: "submit",
|
|
3998
|
-
fullWidth: true,
|
|
3999
|
-
variant: "contained",
|
|
4000
|
-
size: "large",
|
|
4001
|
-
disabled: isLoading,
|
|
4002
|
-
startIcon: isLoading ? /* @__PURE__ */ jsx16(CircularProgress7, { size: 16 }) : null,
|
|
4003
|
-
sx: { mt: 3, mb: 2 },
|
|
4004
|
-
children: isLoading ? "Logging in..." : "Login"
|
|
4005
|
-
}
|
|
4006
|
-
)
|
|
4007
|
-
] }),
|
|
4008
|
-
error && /* @__PURE__ */ jsx16(Alert9, { severity: "error", sx: { mt: 2 }, onClose: clearError, children: error })
|
|
4009
|
-
] });
|
|
4010
|
-
}
|
|
4011
|
-
function SessionStatus() {
|
|
4012
|
-
const { isAuthenticated, isLoading, isExpiringSoon, expiresIn } = useSessionContext();
|
|
4013
|
-
if (isLoading) {
|
|
4014
|
-
return /* @__PURE__ */ jsxs13(Box12, { sx: { display: "flex", alignItems: "center", gap: 1 }, children: [
|
|
4015
|
-
/* @__PURE__ */ jsx16(CircularProgress7, { size: 16 }),
|
|
4016
|
-
/* @__PURE__ */ jsx16(Typography11, { variant: "caption", children: "Loading session..." })
|
|
4017
|
-
] });
|
|
4018
|
-
}
|
|
4019
|
-
if (!isAuthenticated) {
|
|
4020
|
-
return /* @__PURE__ */ jsx16(Typography11, { variant: "caption", color: "text.secondary", children: "Not logged in" });
|
|
4021
|
-
}
|
|
4022
|
-
return /* @__PURE__ */ jsxs13(Box12, { children: [
|
|
4023
|
-
/* @__PURE__ */ jsx16(Typography11, { variant: "caption", color: "success.main", children: "\u2713 Authenticated" }),
|
|
4024
|
-
isExpiringSoon && /* @__PURE__ */ jsxs13(Typography11, { variant: "caption", color: "warning.main", display: "block", children: [
|
|
4025
|
-
"\u26A0 Token expires in ",
|
|
4026
|
-
Math.round(expiresIn / 1e3 / 60),
|
|
4027
|
-
" min"
|
|
4028
|
-
] })
|
|
4029
|
-
] });
|
|
4030
|
-
}
|
|
4031
|
-
|
|
4032
|
-
// src/hooks/useUserData.ts
|
|
4033
|
-
import { useState as useState12, useEffect as useEffect11, useCallback as useCallback4, useRef as useRef5 } from "react";
|
|
4034
|
-
import crudify4 from "@nocios/crudify-browser";
|
|
4035
|
-
var useUserData = (options = {}) => {
|
|
4036
|
-
const { autoFetch = true, retryOnError = false, maxRetries = 3 } = options;
|
|
4037
|
-
const { isAuthenticated, isInitialized, sessionData, tokens } = useSessionContext();
|
|
4038
|
-
const [userData, setUserData] = useState12(null);
|
|
4039
|
-
const [loading, setLoading] = useState12(false);
|
|
4040
|
-
const [error, setError] = useState12(null);
|
|
4041
|
-
const abortControllerRef = useRef5(null);
|
|
4042
|
-
const mountedRef = useRef5(true);
|
|
4043
|
-
const requestIdRef = useRef5(0);
|
|
4044
|
-
const retryCountRef = useRef5(0);
|
|
4045
|
-
const getUserEmail = useCallback4(() => {
|
|
4046
|
-
if (!sessionData) return null;
|
|
4047
|
-
return sessionData.email || sessionData["cognito:username"] || null;
|
|
4048
|
-
}, [sessionData]);
|
|
4049
|
-
const clearProfile = useCallback4(() => {
|
|
4050
|
-
setUserData(null);
|
|
4051
|
-
setError(null);
|
|
4052
|
-
setLoading(false);
|
|
4053
|
-
retryCountRef.current = 0;
|
|
4054
|
-
}, []);
|
|
4055
|
-
const refreshProfile = useCallback4(async () => {
|
|
4056
|
-
const userEmail = getUserEmail();
|
|
4057
|
-
if (!userEmail) {
|
|
4058
|
-
if (mountedRef.current) {
|
|
4059
|
-
setError("No user email available from session data");
|
|
4060
|
-
setLoading(false);
|
|
4061
|
-
}
|
|
4062
|
-
return;
|
|
4063
|
-
}
|
|
4064
|
-
if (!isInitialized) {
|
|
4065
|
-
if (mountedRef.current) {
|
|
4066
|
-
setError("Session not initialized");
|
|
4067
|
-
setLoading(false);
|
|
4068
|
-
}
|
|
4069
|
-
return;
|
|
4070
|
-
}
|
|
4071
|
-
if (abortControllerRef.current) {
|
|
4072
|
-
abortControllerRef.current.abort();
|
|
4073
|
-
}
|
|
4074
|
-
const abortController = new AbortController();
|
|
4075
|
-
abortControllerRef.current = abortController;
|
|
4076
|
-
const currentRequestId = ++requestIdRef.current;
|
|
4077
|
-
try {
|
|
4078
|
-
if (mountedRef.current) {
|
|
4079
|
-
setLoading(true);
|
|
4080
|
-
setError(null);
|
|
4081
|
-
}
|
|
4082
|
-
const response = await crudify4.readItems("users", {
|
|
4083
|
-
filter: { email: userEmail },
|
|
4084
|
-
pagination: { limit: 1 }
|
|
4085
|
-
});
|
|
4086
|
-
if (currentRequestId === requestIdRef.current && mountedRef.current && !abortController.signal.aborted) {
|
|
4087
|
-
let userData2 = null;
|
|
4088
|
-
if (response.success) {
|
|
4089
|
-
if (Array.isArray(response.data) && response.data.length > 0) {
|
|
4090
|
-
userData2 = response.data[0];
|
|
4091
|
-
} else if (response.data?.response?.data) {
|
|
4092
|
-
try {
|
|
4093
|
-
const rawData = response.data.response.data;
|
|
4094
|
-
const parsedData = typeof rawData === "string" ? JSON.parse(rawData) : rawData;
|
|
4095
|
-
if (parsedData && parsedData.items && Array.isArray(parsedData.items) && parsedData.items.length > 0) {
|
|
4096
|
-
userData2 = parsedData.items[0];
|
|
4097
|
-
} else {
|
|
4098
|
-
}
|
|
4099
|
-
} catch (parseError) {
|
|
4100
|
-
}
|
|
4101
|
-
} else if (response.data && typeof response.data === "object") {
|
|
4102
|
-
if (response.data.items && Array.isArray(response.data.items) && response.data.items.length > 0) {
|
|
4103
|
-
userData2 = response.data.items[0];
|
|
4104
|
-
} else {
|
|
4105
|
-
}
|
|
4106
|
-
} else if (response.data?.data?.response?.data) {
|
|
4107
|
-
try {
|
|
4108
|
-
const rawData = response.data.data.response.data;
|
|
4109
|
-
const parsedData = typeof rawData === "string" ? JSON.parse(rawData) : rawData;
|
|
4110
|
-
if (parsedData && parsedData.items && Array.isArray(parsedData.items) && parsedData.items.length > 0) {
|
|
4111
|
-
userData2 = parsedData.items[0];
|
|
4112
|
-
}
|
|
4113
|
-
} catch (parseError) {
|
|
4114
|
-
}
|
|
4115
|
-
}
|
|
4116
|
-
}
|
|
4117
|
-
if (userData2) {
|
|
4118
|
-
setUserData(userData2);
|
|
4119
|
-
setError(null);
|
|
4120
|
-
retryCountRef.current = 0;
|
|
4121
|
-
} else {
|
|
4122
|
-
setError("User profile not found in database");
|
|
4123
|
-
setUserData(null);
|
|
4124
|
-
}
|
|
4125
|
-
}
|
|
4126
|
-
} catch (err) {
|
|
4127
|
-
if (currentRequestId === requestIdRef.current && mountedRef.current) {
|
|
4128
|
-
const error2 = err;
|
|
4129
|
-
if (error2.name === "AbortError") {
|
|
4130
|
-
return;
|
|
4131
|
-
}
|
|
4132
|
-
const shouldRetry = retryOnError && retryCountRef.current < maxRetries && (error2.message?.includes("Network Error") || error2.message?.includes("Failed to fetch"));
|
|
4133
|
-
if (shouldRetry) {
|
|
4134
|
-
retryCountRef.current++;
|
|
4135
|
-
setTimeout(() => {
|
|
4136
|
-
if (mountedRef.current) {
|
|
4137
|
-
refreshProfile();
|
|
4138
|
-
}
|
|
4139
|
-
}, 1e3 * retryCountRef.current);
|
|
4140
|
-
} else {
|
|
4141
|
-
setError("Failed to load user profile from database");
|
|
4142
|
-
setUserData(null);
|
|
4143
|
-
}
|
|
4144
|
-
}
|
|
4145
|
-
} finally {
|
|
4146
|
-
if (currentRequestId === requestIdRef.current && mountedRef.current) {
|
|
4147
|
-
setLoading(false);
|
|
4148
|
-
}
|
|
4149
|
-
if (abortControllerRef.current === abortController) {
|
|
4150
|
-
abortControllerRef.current = null;
|
|
4151
|
-
}
|
|
4152
|
-
}
|
|
4153
|
-
}, [isInitialized, getUserEmail, retryOnError, maxRetries]);
|
|
4154
|
-
useEffect11(() => {
|
|
4155
|
-
if (autoFetch && isAuthenticated && isInitialized) {
|
|
4156
|
-
refreshProfile();
|
|
4157
|
-
} else if (!isAuthenticated) {
|
|
4158
|
-
clearProfile();
|
|
4159
|
-
}
|
|
4160
|
-
}, [autoFetch, isAuthenticated, isInitialized, refreshProfile, clearProfile]);
|
|
4161
|
-
useEffect11(() => {
|
|
4162
|
-
mountedRef.current = true;
|
|
4163
|
-
return () => {
|
|
4164
|
-
mountedRef.current = false;
|
|
4165
|
-
if (abortControllerRef.current) {
|
|
4166
|
-
abortControllerRef.current.abort();
|
|
4167
|
-
abortControllerRef.current = null;
|
|
4168
|
-
}
|
|
4169
|
-
};
|
|
4170
|
-
}, []);
|
|
4171
|
-
const user = {
|
|
4172
|
-
session: sessionData,
|
|
4173
|
-
// Usar sessionData del nuevo sistema
|
|
4174
|
-
data: userData
|
|
4175
|
-
// Mantener userData del database igual que legacy
|
|
4176
|
-
};
|
|
4177
|
-
return {
|
|
4178
|
-
user,
|
|
4179
|
-
loading,
|
|
4180
|
-
error,
|
|
4181
|
-
refreshProfile,
|
|
4182
|
-
clearProfile
|
|
4183
|
-
};
|
|
4184
|
-
};
|
|
4185
|
-
|
|
4186
|
-
// src/hooks/useAuth.ts
|
|
4187
|
-
import { useCallback as useCallback5 } from "react";
|
|
4188
|
-
var useAuth = () => {
|
|
4189
|
-
const {
|
|
4190
|
-
isAuthenticated,
|
|
4191
|
-
isLoading,
|
|
4192
|
-
isInitialized,
|
|
4193
|
-
tokens,
|
|
4194
|
-
error,
|
|
4195
|
-
sessionData,
|
|
4196
|
-
login,
|
|
4197
|
-
logout,
|
|
4198
|
-
refreshTokens,
|
|
4199
|
-
clearError,
|
|
4200
|
-
getTokenInfo,
|
|
4201
|
-
isExpiringSoon,
|
|
4202
|
-
expiresIn,
|
|
4203
|
-
refreshExpiresIn
|
|
4204
|
-
} = useSessionContext();
|
|
4205
|
-
const setToken = useCallback5((token) => {
|
|
4206
|
-
if (token) {
|
|
4207
|
-
console.warn("useAuth.setToken() is deprecated. Use login() method instead for better security.");
|
|
4208
|
-
} else {
|
|
4209
|
-
logout();
|
|
4210
|
-
}
|
|
4211
|
-
}, [logout]);
|
|
4212
|
-
const tokenExpiration = tokens?.expiresAt ? new Date(tokens.expiresAt) : null;
|
|
4213
|
-
return {
|
|
4214
|
-
// Estado básico (compatible con legacy)
|
|
4215
|
-
isAuthenticated,
|
|
4216
|
-
loading: isLoading,
|
|
4217
|
-
// En el nuevo sistema se llama isLoading
|
|
4218
|
-
error,
|
|
4219
|
-
// Datos del token (compatible con legacy + mejorado)
|
|
4220
|
-
token: tokens?.accessToken || null,
|
|
4221
|
-
user: sessionData,
|
|
4222
|
-
// sessionData del nuevo sistema (más rico que legacy)
|
|
4223
|
-
tokenExpiration,
|
|
4224
|
-
// Acciones (compatible con legacy + mejorado)
|
|
4225
|
-
setToken,
|
|
4226
|
-
logout,
|
|
4227
|
-
refreshToken: refreshTokens,
|
|
4228
|
-
// Funcionalidad real en el nuevo sistema
|
|
4229
|
-
// Nuevas funcionalidades del sistema de refresh tokens
|
|
4230
|
-
login,
|
|
4231
|
-
isExpiringSoon,
|
|
4232
|
-
expiresIn,
|
|
4233
|
-
refreshExpiresIn,
|
|
4234
|
-
getTokenInfo,
|
|
4235
|
-
clearError
|
|
4236
|
-
};
|
|
4237
|
-
};
|
|
4238
|
-
|
|
4239
|
-
// src/hooks/useData.ts
|
|
4240
|
-
import { useCallback as useCallback6 } from "react";
|
|
4241
|
-
import crudify5 from "@nocios/crudify-browser";
|
|
4242
|
-
var useData = () => {
|
|
4243
|
-
const {
|
|
4244
|
-
isInitialized,
|
|
4245
|
-
isLoading,
|
|
4246
|
-
error,
|
|
4247
|
-
isAuthenticated,
|
|
4248
|
-
login: sessionLogin
|
|
4249
|
-
} = useSessionContext();
|
|
4250
|
-
const isReady = useCallback6(() => {
|
|
4251
|
-
return isInitialized && !isLoading && !error;
|
|
4252
|
-
}, [isInitialized, isLoading, error]);
|
|
4253
|
-
const waitForReady = useCallback6(async () => {
|
|
4254
|
-
return new Promise((resolve, reject) => {
|
|
4255
|
-
const checkReady = () => {
|
|
4256
|
-
if (isReady()) {
|
|
4257
|
-
resolve();
|
|
4258
|
-
} else if (error) {
|
|
4259
|
-
reject(new Error(error));
|
|
4260
|
-
} else {
|
|
4261
|
-
setTimeout(checkReady, 100);
|
|
4262
|
-
}
|
|
4263
|
-
};
|
|
4264
|
-
checkReady();
|
|
4265
|
-
});
|
|
4266
|
-
}, [isReady, error]);
|
|
4267
|
-
const ensureReady = useCallback6(async () => {
|
|
4268
|
-
if (!isReady()) {
|
|
4269
|
-
throw new Error("System not ready. Check isInitialized, isLoading, and error states.");
|
|
4270
|
-
}
|
|
4271
|
-
}, [isReady]);
|
|
4272
|
-
const readItems = useCallback6(async (moduleKey, filter, options) => {
|
|
4273
|
-
await ensureReady();
|
|
4274
|
-
return await crudify5.readItems(moduleKey, filter || {}, options);
|
|
4275
|
-
}, [ensureReady]);
|
|
4276
|
-
const readItem = useCallback6(async (moduleKey, filter, options) => {
|
|
4277
|
-
await ensureReady();
|
|
4278
|
-
return await crudify5.readItem(moduleKey, filter, options);
|
|
4279
|
-
}, [ensureReady]);
|
|
4280
|
-
const createItem = useCallback6(async (moduleKey, data, options) => {
|
|
4281
|
-
await ensureReady();
|
|
4282
|
-
return await crudify5.createItem(moduleKey, data, options);
|
|
4283
|
-
}, [ensureReady]);
|
|
4284
|
-
const updateItem = useCallback6(async (moduleKey, data, options) => {
|
|
4285
|
-
await ensureReady();
|
|
4286
|
-
return await crudify5.updateItem(moduleKey, data, options);
|
|
4287
|
-
}, [ensureReady]);
|
|
4288
|
-
const deleteItem = useCallback6(async (moduleKey, id, options) => {
|
|
4289
|
-
await ensureReady();
|
|
4290
|
-
return await crudify5.deleteItem(moduleKey, id, options);
|
|
4291
|
-
}, [ensureReady]);
|
|
4292
|
-
const transaction = useCallback6(async (operations, options) => {
|
|
4293
|
-
await ensureReady();
|
|
4294
|
-
return await crudify5.transaction(operations, options);
|
|
4295
|
-
}, [ensureReady]);
|
|
4296
|
-
const login = useCallback6(async (email, password) => {
|
|
4297
|
-
try {
|
|
4298
|
-
const result = await sessionLogin(email, password);
|
|
4299
|
-
if (result.success) {
|
|
4300
|
-
return {
|
|
4301
|
-
success: true,
|
|
4302
|
-
data: result.tokens
|
|
4303
|
-
};
|
|
4304
|
-
} else {
|
|
4305
|
-
return {
|
|
4306
|
-
success: false,
|
|
4307
|
-
errors: result.error || "Login failed"
|
|
4308
|
-
};
|
|
4309
|
-
}
|
|
4310
|
-
} catch (error2) {
|
|
4311
|
-
return {
|
|
4312
|
-
success: false,
|
|
4313
|
-
errors: error2 instanceof Error ? error2.message : "Login failed"
|
|
4314
|
-
};
|
|
4315
|
-
}
|
|
4316
|
-
}, [sessionLogin]);
|
|
4317
|
-
return {
|
|
4318
|
-
// CRUD operations
|
|
4319
|
-
readItems,
|
|
4320
|
-
readItem,
|
|
4321
|
-
createItem,
|
|
4322
|
-
updateItem,
|
|
4323
|
-
deleteItem,
|
|
4324
|
-
transaction,
|
|
4325
|
-
login,
|
|
4326
|
-
// Estado
|
|
4327
|
-
isInitialized,
|
|
4328
|
-
isInitializing: isLoading,
|
|
4329
|
-
// El nuevo sistema usa isLoading
|
|
4330
|
-
initializationError: error,
|
|
4331
|
-
// Utilidades
|
|
4332
|
-
isReady,
|
|
4333
|
-
waitForReady
|
|
4334
|
-
};
|
|
4335
|
-
};
|
|
4336
|
-
|
|
4337
|
-
// src/components/CrudifyLogin/utils/secureStorage.ts
|
|
4338
|
-
import CryptoJS2 from "crypto-js";
|
|
4339
|
-
var SecureStorage = class {
|
|
4340
|
-
constructor(storageType = "sessionStorage") {
|
|
4341
|
-
this.encryptionKey = this.generateEncryptionKey();
|
|
4342
|
-
this.storage = storageType === "localStorage" ? window.localStorage : window.sessionStorage;
|
|
4343
|
-
}
|
|
4344
|
-
generateEncryptionKey() {
|
|
4345
|
-
const browserFingerprint = [
|
|
4346
|
-
navigator.userAgent,
|
|
4347
|
-
navigator.language,
|
|
4348
|
-
(/* @__PURE__ */ new Date()).getTimezoneOffset(),
|
|
4349
|
-
screen.colorDepth,
|
|
4350
|
-
screen.width,
|
|
4351
|
-
screen.height,
|
|
4352
|
-
"crudify-login"
|
|
4353
|
-
].join("|");
|
|
4354
|
-
return CryptoJS2.SHA256(browserFingerprint).toString();
|
|
4355
|
-
}
|
|
4356
|
-
setItem(key, value, expiryMinutes) {
|
|
4357
|
-
try {
|
|
4358
|
-
const encrypted = CryptoJS2.AES.encrypt(value, this.encryptionKey).toString();
|
|
4359
|
-
this.storage.setItem(key, encrypted);
|
|
4360
|
-
if (expiryMinutes) {
|
|
4361
|
-
const expiryTime = (/* @__PURE__ */ new Date()).getTime() + expiryMinutes * 60 * 1e3;
|
|
4362
|
-
this.storage.setItem(`${key}_expiry`, expiryTime.toString());
|
|
4363
|
-
}
|
|
4364
|
-
} catch (error) {
|
|
4365
|
-
console.error("Failed to encrypt and store data:", error);
|
|
4366
|
-
}
|
|
4367
|
-
}
|
|
4368
|
-
getItem(key) {
|
|
4369
|
-
try {
|
|
4370
|
-
const expiryKey = `${key}_expiry`;
|
|
4371
|
-
const expiry = this.storage.getItem(expiryKey);
|
|
4372
|
-
if (expiry) {
|
|
4373
|
-
const expiryTime = parseInt(expiry, 10);
|
|
4374
|
-
if ((/* @__PURE__ */ new Date()).getTime() > expiryTime) {
|
|
4375
|
-
this.removeItem(key);
|
|
4376
|
-
return null;
|
|
4377
|
-
}
|
|
4378
|
-
}
|
|
4379
|
-
const encrypted = this.storage.getItem(key);
|
|
4380
|
-
if (!encrypted) return null;
|
|
4381
|
-
const decrypted = CryptoJS2.AES.decrypt(encrypted, this.encryptionKey);
|
|
4382
|
-
const result = decrypted.toString(CryptoJS2.enc.Utf8);
|
|
4383
|
-
if (!result) {
|
|
4384
|
-
console.warn("Failed to decrypt stored data - may be corrupted");
|
|
4385
|
-
this.removeItem(key);
|
|
4386
|
-
return null;
|
|
4387
|
-
}
|
|
4388
|
-
return result;
|
|
4389
|
-
} catch (error) {
|
|
4390
|
-
console.error("Failed to decrypt data:", error);
|
|
4391
|
-
this.removeItem(key);
|
|
4392
|
-
return null;
|
|
4393
|
-
}
|
|
4394
|
-
}
|
|
4395
|
-
removeItem(key) {
|
|
4396
|
-
this.storage.removeItem(key);
|
|
4397
|
-
this.storage.removeItem(`${key}_expiry`);
|
|
4398
|
-
}
|
|
4399
|
-
setToken(token) {
|
|
4400
|
-
try {
|
|
4401
|
-
const parts = token.split(".");
|
|
4402
|
-
if (parts.length === 3) {
|
|
4403
|
-
const payload = JSON.parse(atob(parts[1]));
|
|
4404
|
-
if (payload.exp) {
|
|
4405
|
-
const expiryTime = payload.exp * 1e3;
|
|
4406
|
-
const now = (/* @__PURE__ */ new Date()).getTime();
|
|
4407
|
-
const minutesUntilExpiry = Math.floor((expiryTime - now) / (60 * 1e3));
|
|
4408
|
-
if (minutesUntilExpiry > 0) {
|
|
4409
|
-
this.setItem("authToken", token, minutesUntilExpiry);
|
|
4410
|
-
return;
|
|
4411
|
-
}
|
|
4412
|
-
}
|
|
4413
|
-
}
|
|
4414
|
-
} catch (error) {
|
|
4415
|
-
console.warn("Failed to parse token expiry, using default expiry");
|
|
4416
|
-
}
|
|
4417
|
-
this.setItem("authToken", token, 24 * 60);
|
|
4418
|
-
}
|
|
4419
|
-
getToken() {
|
|
4420
|
-
const token = this.getItem("authToken");
|
|
4421
|
-
if (token) {
|
|
4422
|
-
try {
|
|
4423
|
-
const parts = token.split(".");
|
|
4424
|
-
if (parts.length === 3) {
|
|
4425
|
-
const payload = JSON.parse(atob(parts[1]));
|
|
4426
|
-
if (payload.exp) {
|
|
4427
|
-
const now = Math.floor(Date.now() / 1e3);
|
|
4428
|
-
if (payload.exp < now) {
|
|
4429
|
-
this.removeItem("authToken");
|
|
4430
|
-
return null;
|
|
4431
|
-
}
|
|
4432
|
-
}
|
|
4433
|
-
}
|
|
4434
|
-
} catch (error) {
|
|
4435
|
-
console.warn("Failed to validate token expiry");
|
|
4436
|
-
this.removeItem("authToken");
|
|
4437
|
-
return null;
|
|
4438
|
-
}
|
|
4439
|
-
}
|
|
4440
|
-
return token;
|
|
4441
|
-
}
|
|
4442
|
-
};
|
|
4443
|
-
var secureSessionStorage = new SecureStorage("sessionStorage");
|
|
4444
|
-
var secureLocalStorage = new SecureStorage("localStorage");
|
|
4445
|
-
|
|
4446
|
-
// src/hooks/useCrudifyWithNotifications.ts
|
|
4447
|
-
import { useCallback as useCallback7 } from "react";
|
|
4448
|
-
import crudify6 from "@nocios/crudify-browser";
|
|
4449
|
-
var ERROR_SEVERITY_MAP2 = {
|
|
4450
|
-
// Errores de autenticación - warning porque el usuario puede corregirlos
|
|
4451
|
-
INVALID_CREDENTIALS: "warning",
|
|
4452
|
-
UNAUTHORIZED: "warning",
|
|
4453
|
-
INVALID_API_KEY: "error",
|
|
4454
|
-
// Errores de usuario/permisos - warning
|
|
4455
|
-
USER_NOT_FOUND: "warning",
|
|
4456
|
-
USER_NOT_ACTIVE: "warning",
|
|
4457
|
-
NO_PERMISSION: "warning",
|
|
4458
|
-
// Errores de datos - info porque pueden ser esperados
|
|
4459
|
-
ITEM_NOT_FOUND: "info",
|
|
4460
|
-
NOT_FOUND: "info",
|
|
4461
|
-
IN_USE: "warning",
|
|
4462
|
-
// Errores de validación - warning
|
|
4463
|
-
FIELD_ERROR: "warning",
|
|
4464
|
-
BAD_REQUEST: "warning",
|
|
4465
|
-
// Errores del sistema - error
|
|
4466
|
-
INTERNAL_SERVER_ERROR: "error",
|
|
4467
|
-
DATABASE_CONNECTION_ERROR: "error",
|
|
4468
|
-
INVALID_CONFIGURATION: "error",
|
|
4469
|
-
UNKNOWN_OPERATION: "error",
|
|
4470
|
-
// Rate limiting - warning con mayor duración
|
|
4471
|
-
TOO_MANY_REQUESTS: "warning"
|
|
4472
|
-
};
|
|
4473
|
-
var ERROR_TRANSLATION_MAP = {
|
|
4474
|
-
INVALID_CREDENTIALS: "errors.auth.INVALID_CREDENTIALS",
|
|
4475
|
-
UNAUTHORIZED: "errors.auth.UNAUTHORIZED",
|
|
4476
|
-
INVALID_API_KEY: "errors.auth.INVALID_API_KEY",
|
|
4477
|
-
USER_NOT_FOUND: "errors.auth.USER_NOT_FOUND",
|
|
4478
|
-
USER_NOT_ACTIVE: "errors.auth.USER_NOT_ACTIVE",
|
|
4479
|
-
NO_PERMISSION: "errors.auth.NO_PERMISSION",
|
|
4480
|
-
ITEM_NOT_FOUND: "errors.data.ITEM_NOT_FOUND",
|
|
4481
|
-
NOT_FOUND: "errors.data.NOT_FOUND",
|
|
4482
|
-
IN_USE: "errors.data.IN_USE",
|
|
4483
|
-
FIELD_ERROR: "errors.data.FIELD_ERROR",
|
|
4484
|
-
BAD_REQUEST: "errors.data.BAD_REQUEST",
|
|
4485
|
-
INTERNAL_SERVER_ERROR: "errors.system.INTERNAL_SERVER_ERROR",
|
|
4486
|
-
DATABASE_CONNECTION_ERROR: "errors.system.DATABASE_CONNECTION_ERROR",
|
|
4487
|
-
INVALID_CONFIGURATION: "errors.system.INVALID_CONFIGURATION",
|
|
4488
|
-
UNKNOWN_OPERATION: "errors.system.UNKNOWN_OPERATION",
|
|
4489
|
-
TOO_MANY_REQUESTS: "errors.system.TOO_MANY_REQUESTS"
|
|
4490
|
-
};
|
|
4491
|
-
var useCrudifyWithNotifications = (options = {}) => {
|
|
4492
|
-
const { showNotification } = useGlobalNotification();
|
|
4493
|
-
const {
|
|
4494
|
-
showSuccessNotifications = false,
|
|
4495
|
-
showErrorNotifications = true,
|
|
4496
|
-
customErrorMessages = {},
|
|
4497
|
-
defaultErrorMessage = "Ha ocurrido un error inesperado",
|
|
4498
|
-
autoHideDuration = 6e3,
|
|
4499
|
-
appStructure = [],
|
|
4500
|
-
translateFn = (key) => key
|
|
4501
|
-
// Fallback si no hay función de traducción
|
|
4502
|
-
} = options;
|
|
4503
|
-
const shouldShowNotification = useCallback7((response) => {
|
|
4504
|
-
if (!response.success && response.errors) {
|
|
4505
|
-
const hasFieldErrors = Object.keys(response.errors).some((key) => key !== "_error" && key !== "_graphql" && key !== "_transaction");
|
|
4506
|
-
if (hasFieldErrors) return false;
|
|
4507
|
-
if (response.errors._transaction?.includes("ONE_OR_MORE_OPERATIONS_FAILED")) return false;
|
|
4508
|
-
if (response.errors._error?.includes("TOO_MANY_REQUESTS")) return false;
|
|
4509
|
-
}
|
|
4510
|
-
if (!response.success && response.data?.response?.status === "TOO_MANY_REQUESTS") return false;
|
|
4511
|
-
return true;
|
|
4512
|
-
}, []);
|
|
4513
|
-
const getSafeTranslation = useCallback7(
|
|
4514
|
-
(key, fallback) => {
|
|
4515
|
-
const translated = translateFn(key);
|
|
4516
|
-
if (translated === key) return fallback || translateFn("error.unknown");
|
|
4517
|
-
return translated;
|
|
4518
|
-
},
|
|
4519
|
-
[translateFn]
|
|
4520
|
-
);
|
|
4521
|
-
const isCrudOperation = useCallback7((operation) => {
|
|
4522
|
-
return ["create", "update", "delete"].includes(operation);
|
|
4523
|
-
}, []);
|
|
4524
|
-
const shouldShowSuccessNotification = useCallback7(
|
|
4525
|
-
(operation, moduleKey) => {
|
|
4526
|
-
if (!showSuccessNotifications) return false;
|
|
4527
|
-
if (isCrudOperation(operation) && moduleKey) return true;
|
|
4528
|
-
return appStructure.some((action) => action.key === operation);
|
|
4529
|
-
},
|
|
4530
|
-
[showSuccessNotifications, appStructure, isCrudOperation]
|
|
4531
|
-
);
|
|
4532
|
-
const getSuccessMessage = useCallback7(
|
|
4533
|
-
(operation, moduleKey, actionConfig) => {
|
|
4534
|
-
const operationKey = actionConfig?.key && typeof actionConfig.key === "string" ? actionConfig.key : operation;
|
|
4535
|
-
const successKey = `action.onSuccess.${operationKey}`;
|
|
4536
|
-
const successMessage = getSafeTranslation(successKey);
|
|
4537
|
-
if (successMessage !== translateFn("error.unknown")) {
|
|
4538
|
-
if (isCrudOperation(operationKey) && moduleKey) {
|
|
4539
|
-
const itemNameKey = `action.${moduleKey}Singular`;
|
|
4540
|
-
const itemName = getSafeTranslation(itemNameKey);
|
|
4541
|
-
if (itemName !== translateFn("error.unknown")) {
|
|
4542
|
-
return translateFn(successKey, { item: itemName });
|
|
4543
|
-
} else {
|
|
4544
|
-
const withoutItemKey = `action.onSuccess.${operationKey}WithoutItem`;
|
|
4545
|
-
const withoutItemMessage = getSafeTranslation(withoutItemKey);
|
|
4546
|
-
return withoutItemMessage !== translateFn("error.unknown") ? withoutItemMessage : successMessage;
|
|
4547
|
-
}
|
|
4548
|
-
}
|
|
4549
|
-
return successMessage;
|
|
4550
|
-
}
|
|
4551
|
-
return translateFn("success.transaction");
|
|
4552
|
-
},
|
|
4553
|
-
[getSafeTranslation, translateFn, isCrudOperation]
|
|
4554
|
-
);
|
|
4555
|
-
const getErrorMessage2 = useCallback7(
|
|
4556
|
-
(response) => {
|
|
4557
|
-
if (response.errorCode && customErrorMessages[response.errorCode]) {
|
|
4558
|
-
return customErrorMessages[response.errorCode];
|
|
4559
|
-
}
|
|
4560
|
-
if (response.errorCode && ERROR_TRANSLATION_MAP[response.errorCode]) {
|
|
4561
|
-
return getSafeTranslation(ERROR_TRANSLATION_MAP[response.errorCode]);
|
|
4562
|
-
}
|
|
4563
|
-
if (response.errorCode) {
|
|
4564
|
-
const possibleKeys = [
|
|
4565
|
-
`errors.auth.${response.errorCode}`,
|
|
4566
|
-
`errors.data.${response.errorCode}`,
|
|
4567
|
-
`errors.system.${response.errorCode}`,
|
|
4568
|
-
`errors.${response.errorCode}`
|
|
4569
|
-
];
|
|
4570
|
-
for (const key of possibleKeys) {
|
|
4571
|
-
const translated = getSafeTranslation(key);
|
|
4572
|
-
if (translated !== translateFn("error.unknown")) {
|
|
4573
|
-
return translated;
|
|
4574
|
-
}
|
|
4575
|
-
}
|
|
4576
|
-
}
|
|
4577
|
-
if (typeof response.data === "string" && response.data.startsWith("errors.")) {
|
|
4578
|
-
const translated = getSafeTranslation(response.data);
|
|
4579
|
-
if (translated !== translateFn("error.unknown")) {
|
|
4580
|
-
return translated;
|
|
4581
|
-
}
|
|
4582
|
-
}
|
|
4583
|
-
if (response.errors && Object.keys(response.errors).length > 0) {
|
|
4584
|
-
const errorFields = Object.keys(response.errors);
|
|
4585
|
-
if (errorFields.length === 1 && errorFields[0] === "_transaction") {
|
|
4586
|
-
const transactionErrors = response.errors["_transaction"];
|
|
4587
|
-
if (transactionErrors?.includes("ONE_OR_MORE_OPERATIONS_FAILED")) {
|
|
4588
|
-
return "";
|
|
4589
|
-
}
|
|
4590
|
-
if (Array.isArray(transactionErrors) && transactionErrors.length > 0) {
|
|
4591
|
-
const firstError = transactionErrors[0];
|
|
4592
|
-
if (typeof firstError === "string" && firstError !== "ONE_OR_MORE_OPERATIONS_FAILED") {
|
|
4593
|
-
try {
|
|
4594
|
-
const parsed = JSON.parse(firstError);
|
|
4595
|
-
if (Array.isArray(parsed) && parsed.length > 0) {
|
|
4596
|
-
const firstTransactionError = parsed[0];
|
|
4597
|
-
if (firstTransactionError?.response?.errorCode) {
|
|
4598
|
-
const errorCode = firstTransactionError.response.errorCode;
|
|
4599
|
-
if (ERROR_TRANSLATION_MAP[errorCode]) {
|
|
4600
|
-
return getSafeTranslation(ERROR_TRANSLATION_MAP[errorCode]);
|
|
4601
|
-
}
|
|
4602
|
-
const possibleKeys = [
|
|
4603
|
-
`errors.auth.${errorCode}`,
|
|
4604
|
-
`errors.data.${errorCode}`,
|
|
4605
|
-
`errors.system.${errorCode}`,
|
|
4606
|
-
`errors.${errorCode}`
|
|
4607
|
-
];
|
|
4608
|
-
for (const key of possibleKeys) {
|
|
4609
|
-
const translated = getSafeTranslation(key);
|
|
4610
|
-
if (translated !== getSafeTranslation("error.unknown")) {
|
|
4611
|
-
return translated;
|
|
4612
|
-
}
|
|
4613
|
-
}
|
|
4614
|
-
}
|
|
4615
|
-
if (firstTransactionError?.response?.data) {
|
|
4616
|
-
return firstTransactionError.response.data;
|
|
4617
|
-
}
|
|
4618
|
-
}
|
|
4619
|
-
if (parsed?.response?.message) {
|
|
4620
|
-
const errorMsg = parsed.response.message.toLowerCase();
|
|
4621
|
-
if (errorMsg.includes("expired")) {
|
|
4622
|
-
return getSafeTranslation("resetPassword.linkExpired", "El enlace ha expirado");
|
|
4623
|
-
} else if (errorMsg.includes("invalid")) {
|
|
4624
|
-
return getSafeTranslation("resetPassword.invalidCode", "C\xF3digo inv\xE1lido");
|
|
4625
|
-
}
|
|
4626
|
-
return parsed.response.message;
|
|
4627
|
-
}
|
|
4628
|
-
} catch {
|
|
4629
|
-
if (firstError.toLowerCase().includes("expired")) {
|
|
4630
|
-
return getSafeTranslation("resetPassword.linkExpired", "El enlace ha expirado");
|
|
4631
|
-
} else if (firstError.toLowerCase().includes("invalid")) {
|
|
4632
|
-
return getSafeTranslation("resetPassword.invalidCode", "C\xF3digo inv\xE1lido");
|
|
4633
|
-
}
|
|
4634
|
-
return firstError;
|
|
4635
|
-
}
|
|
4636
|
-
}
|
|
4637
|
-
}
|
|
4638
|
-
return getSafeTranslation("error.transaction", "Error en la operaci\xF3n");
|
|
4639
|
-
}
|
|
4640
|
-
if (errorFields.length === 1 && errorFields[0] === "_error") {
|
|
4641
|
-
const errorMessages = response.errors["_error"];
|
|
4642
|
-
return Array.isArray(errorMessages) ? errorMessages[0] : String(errorMessages);
|
|
4643
|
-
}
|
|
4644
|
-
if (errorFields.length === 1 && errorFields[0] === "_graphql") {
|
|
4645
|
-
return getSafeTranslation("errors.system.DATABASE_CONNECTION_ERROR");
|
|
4646
|
-
}
|
|
4647
|
-
return `${getSafeTranslation("errors.data.FIELD_ERROR")}: ${errorFields.join(", ")}`;
|
|
4648
|
-
}
|
|
4649
|
-
return defaultErrorMessage || translateFn("error.unknown");
|
|
4650
|
-
},
|
|
4651
|
-
[customErrorMessages, defaultErrorMessage, translateFn, getSafeTranslation]
|
|
4652
|
-
);
|
|
4653
|
-
const getErrorSeverity = useCallback7((response) => {
|
|
4654
|
-
if (response.errorCode && ERROR_SEVERITY_MAP2[response.errorCode]) {
|
|
4655
|
-
return ERROR_SEVERITY_MAP2[response.errorCode];
|
|
4656
|
-
}
|
|
4657
|
-
return "error";
|
|
4658
|
-
}, []);
|
|
4659
|
-
const createItem = useCallback7(
|
|
4660
|
-
async (moduleKey, data, options2) => {
|
|
4661
|
-
const response = await crudify6.createItem(moduleKey, data, options2);
|
|
4662
|
-
if (!response.success && showErrorNotifications && shouldShowNotification(response)) {
|
|
4663
|
-
const message = getErrorMessage2(response);
|
|
4664
|
-
const severity = getErrorSeverity(response);
|
|
4665
|
-
showNotification(message, severity, { autoHideDuration });
|
|
4666
|
-
} else if (response.success) {
|
|
4667
|
-
const actionConfig = options2?.actionConfig;
|
|
4668
|
-
const operation = actionConfig?.key || "create";
|
|
4669
|
-
const effectiveModuleKey = actionConfig?.moduleKey || moduleKey;
|
|
4670
|
-
if (shouldShowSuccessNotification(operation, effectiveModuleKey)) {
|
|
4671
|
-
const message = getSuccessMessage(operation, effectiveModuleKey, actionConfig);
|
|
4672
|
-
showNotification(message, "success", { autoHideDuration });
|
|
4673
|
-
}
|
|
4674
|
-
}
|
|
4675
|
-
return response;
|
|
4676
|
-
},
|
|
4677
|
-
[
|
|
4678
|
-
showErrorNotifications,
|
|
4679
|
-
shouldShowSuccessNotification,
|
|
4680
|
-
showNotification,
|
|
4681
|
-
getErrorMessage2,
|
|
4682
|
-
getErrorSeverity,
|
|
4683
|
-
getSuccessMessage,
|
|
4684
|
-
autoHideDuration,
|
|
4685
|
-
shouldShowNotification
|
|
4686
|
-
]
|
|
4687
|
-
);
|
|
4688
|
-
const updateItem = useCallback7(
|
|
4689
|
-
async (moduleKey, data, options2) => {
|
|
4690
|
-
const response = await crudify6.updateItem(moduleKey, data, options2);
|
|
4691
|
-
if (!response.success && showErrorNotifications && shouldShowNotification(response)) {
|
|
4692
|
-
const message = getErrorMessage2(response);
|
|
4693
|
-
const severity = getErrorSeverity(response);
|
|
4694
|
-
showNotification(message, severity, { autoHideDuration });
|
|
4695
|
-
} else if (response.success) {
|
|
4696
|
-
const actionConfig = options2?.actionConfig;
|
|
4697
|
-
const operation = actionConfig?.key || "update";
|
|
4698
|
-
const effectiveModuleKey = actionConfig?.moduleKey || moduleKey;
|
|
4699
|
-
if (shouldShowSuccessNotification(operation, effectiveModuleKey)) {
|
|
4700
|
-
const message = getSuccessMessage(operation, effectiveModuleKey, actionConfig);
|
|
4701
|
-
showNotification(message, "success", { autoHideDuration });
|
|
4702
|
-
}
|
|
4703
|
-
}
|
|
4704
|
-
return response;
|
|
4705
|
-
},
|
|
4706
|
-
[
|
|
4707
|
-
showErrorNotifications,
|
|
4708
|
-
shouldShowSuccessNotification,
|
|
4709
|
-
showNotification,
|
|
4710
|
-
getErrorMessage2,
|
|
4711
|
-
getErrorSeverity,
|
|
4712
|
-
getSuccessMessage,
|
|
4713
|
-
autoHideDuration,
|
|
4714
|
-
shouldShowNotification
|
|
4715
|
-
]
|
|
4716
|
-
);
|
|
4717
|
-
const deleteItem = useCallback7(
|
|
4718
|
-
async (moduleKey, id, options2) => {
|
|
4719
|
-
const response = await crudify6.deleteItem(moduleKey, id, options2);
|
|
4720
|
-
if (!response.success && showErrorNotifications && shouldShowNotification(response)) {
|
|
4721
|
-
const message = getErrorMessage2(response);
|
|
4722
|
-
const severity = getErrorSeverity(response);
|
|
4723
|
-
showNotification(message, severity, { autoHideDuration });
|
|
4724
|
-
} else if (response.success) {
|
|
4725
|
-
const actionConfig = options2?.actionConfig;
|
|
4726
|
-
const operation = actionConfig?.key || "delete";
|
|
4727
|
-
const effectiveModuleKey = actionConfig?.moduleKey || moduleKey;
|
|
4728
|
-
if (shouldShowSuccessNotification(operation, effectiveModuleKey)) {
|
|
4729
|
-
const message = getSuccessMessage(operation, effectiveModuleKey, actionConfig);
|
|
4730
|
-
showNotification(message, "success", { autoHideDuration });
|
|
4731
|
-
}
|
|
4732
|
-
}
|
|
4733
|
-
return response;
|
|
4734
|
-
},
|
|
4735
|
-
[
|
|
4736
|
-
showErrorNotifications,
|
|
4737
|
-
shouldShowSuccessNotification,
|
|
4738
|
-
showNotification,
|
|
4739
|
-
getErrorMessage2,
|
|
4740
|
-
getErrorSeverity,
|
|
4741
|
-
getSuccessMessage,
|
|
4742
|
-
autoHideDuration,
|
|
4743
|
-
shouldShowNotification
|
|
4744
|
-
]
|
|
4745
|
-
);
|
|
4746
|
-
const readItem = useCallback7(
|
|
4747
|
-
async (moduleKey, filter, options2) => {
|
|
4748
|
-
const response = await crudify6.readItem(moduleKey, filter, options2);
|
|
4749
|
-
if (!response.success && showErrorNotifications && shouldShowNotification(response)) {
|
|
4750
|
-
const message = getErrorMessage2(response);
|
|
4751
|
-
const severity = getErrorSeverity(response);
|
|
4752
|
-
showNotification(message, severity, { autoHideDuration });
|
|
4753
|
-
}
|
|
4754
|
-
return response;
|
|
4755
|
-
},
|
|
4756
|
-
[showErrorNotifications, showNotification, getErrorMessage2, getErrorSeverity, autoHideDuration, shouldShowNotification]
|
|
4757
|
-
);
|
|
4758
|
-
const readItems = useCallback7(
|
|
4759
|
-
async (moduleKey, filter, options2) => {
|
|
4760
|
-
const response = await crudify6.readItems(moduleKey, filter, options2);
|
|
4761
|
-
if (!response.success && showErrorNotifications && shouldShowNotification(response)) {
|
|
4762
|
-
const message = getErrorMessage2(response);
|
|
4763
|
-
const severity = getErrorSeverity(response);
|
|
4764
|
-
showNotification(message, severity, { autoHideDuration });
|
|
4765
|
-
}
|
|
4766
|
-
return response;
|
|
4767
|
-
},
|
|
4768
|
-
[showErrorNotifications, showNotification, getErrorMessage2, getErrorSeverity, autoHideDuration, shouldShowNotification]
|
|
4769
|
-
);
|
|
4770
|
-
const transaction = useCallback7(
|
|
4771
|
-
async (data, options2) => {
|
|
4772
|
-
const response = await crudify6.transaction(data, options2);
|
|
4773
|
-
const skipNotifications = options2?.skipNotifications === true;
|
|
4774
|
-
if (!skipNotifications && !response.success && showErrorNotifications && shouldShowNotification(response)) {
|
|
4775
|
-
const message = getErrorMessage2(response);
|
|
4776
|
-
const severity = getErrorSeverity(response);
|
|
4777
|
-
showNotification(message, severity, { autoHideDuration });
|
|
4778
|
-
} else if (!skipNotifications && response.success) {
|
|
4779
|
-
let operation = "transaction";
|
|
4780
|
-
let moduleKey;
|
|
4781
|
-
let actionConfig = null;
|
|
4782
|
-
if (options2?.actionConfig) {
|
|
4783
|
-
actionConfig = options2.actionConfig;
|
|
4784
|
-
operation = actionConfig.key;
|
|
4785
|
-
moduleKey = actionConfig.moduleKey;
|
|
4786
|
-
} else if (Array.isArray(data) && data.length > 0 && data[0].operation) {
|
|
4787
|
-
operation = data[0].operation;
|
|
4788
|
-
actionConfig = appStructure.find((action) => action.key === operation);
|
|
4789
|
-
if (actionConfig) {
|
|
4790
|
-
moduleKey = actionConfig.moduleKey;
|
|
4791
|
-
}
|
|
4792
|
-
}
|
|
4793
|
-
if (shouldShowSuccessNotification(operation, moduleKey)) {
|
|
4794
|
-
const message = getSuccessMessage(operation, moduleKey, actionConfig);
|
|
4795
|
-
showNotification(message, "success", { autoHideDuration });
|
|
4796
|
-
}
|
|
4797
|
-
}
|
|
4798
|
-
return response;
|
|
4799
|
-
},
|
|
4800
|
-
[
|
|
4801
|
-
showErrorNotifications,
|
|
4802
|
-
shouldShowSuccessNotification,
|
|
4803
|
-
showNotification,
|
|
4804
|
-
getErrorMessage2,
|
|
4805
|
-
getErrorSeverity,
|
|
4806
|
-
getSuccessMessage,
|
|
4807
|
-
autoHideDuration,
|
|
4808
|
-
shouldShowNotification,
|
|
4809
|
-
appStructure
|
|
4810
|
-
]
|
|
4811
|
-
);
|
|
4812
|
-
const handleResponse = useCallback7(
|
|
4813
|
-
(response, successMessage) => {
|
|
4814
|
-
if (!response.success && showErrorNotifications && shouldShowNotification(response)) {
|
|
4815
|
-
const message = getErrorMessage2(response);
|
|
4816
|
-
const severity = getErrorSeverity(response);
|
|
4817
|
-
showNotification(message, severity, { autoHideDuration });
|
|
4818
|
-
} else if (response.success && showSuccessNotifications && successMessage) {
|
|
4819
|
-
showNotification(successMessage, "success", { autoHideDuration });
|
|
4820
|
-
}
|
|
4821
|
-
return response;
|
|
4822
|
-
},
|
|
4823
|
-
[
|
|
4824
|
-
showErrorNotifications,
|
|
4825
|
-
showSuccessNotifications,
|
|
4826
|
-
showNotification,
|
|
4827
|
-
getErrorMessage2,
|
|
4828
|
-
getErrorSeverity,
|
|
4829
|
-
autoHideDuration,
|
|
4830
|
-
shouldShowNotification,
|
|
4831
|
-
translateFn
|
|
4832
|
-
]
|
|
4833
|
-
);
|
|
4834
|
-
return {
|
|
4835
|
-
// Métodos de crudify con notificaciones automáticas
|
|
4836
|
-
createItem,
|
|
4837
|
-
updateItem,
|
|
4838
|
-
deleteItem,
|
|
4839
|
-
readItem,
|
|
4840
|
-
readItems,
|
|
4841
|
-
transaction,
|
|
4842
|
-
// Método genérico para manejar respuestas
|
|
4843
|
-
handleResponse,
|
|
4844
|
-
// Funciones de utilidad
|
|
4845
|
-
getErrorMessage: getErrorMessage2,
|
|
4846
|
-
getErrorSeverity,
|
|
4847
|
-
shouldShowNotification
|
|
4848
|
-
};
|
|
4849
|
-
};
|
|
4850
|
-
export {
|
|
4851
|
-
CrudifyLogin_default as CrudifyLogin,
|
|
4852
|
-
ERROR_CODES,
|
|
4853
|
-
ERROR_SEVERITY_MAP,
|
|
4854
|
-
GlobalNotificationProvider,
|
|
4855
|
-
LoginComponent,
|
|
4856
|
-
POLICY_ACTIONS,
|
|
4857
|
-
PREFERRED_POLICY_ORDER,
|
|
4858
|
-
Policies_default as Policies,
|
|
4859
|
-
ProtectedRoute,
|
|
4860
|
-
SessionDebugInfo,
|
|
4861
|
-
SessionManager,
|
|
4862
|
-
SessionProvider,
|
|
4863
|
-
SessionStatus,
|
|
4864
|
-
TokenStorage,
|
|
4865
|
-
UserProfileDisplay_default as UserProfileDisplay,
|
|
4866
|
-
createErrorTranslator,
|
|
4867
|
-
default2 as crudify,
|
|
4868
|
-
decodeJwtSafely,
|
|
4869
|
-
getCookie,
|
|
4870
|
-
getCurrentUserEmail,
|
|
4871
|
-
getErrorMessage,
|
|
4872
|
-
handleCrudifyError,
|
|
4873
|
-
isTokenExpired,
|
|
4874
|
-
parseApiError,
|
|
4875
|
-
parseJavaScriptError,
|
|
4876
|
-
parseTransactionError,
|
|
4877
|
-
secureLocalStorage,
|
|
4878
|
-
secureSessionStorage,
|
|
4879
|
-
translateError,
|
|
4880
|
-
translateErrorCode,
|
|
4881
|
-
translateErrorCodes,
|
|
4882
|
-
useAuth,
|
|
4883
|
-
useCrudifyWithNotifications,
|
|
4884
|
-
useData,
|
|
4885
|
-
useGlobalNotification,
|
|
4886
|
-
useSession,
|
|
4887
|
-
useSessionContext,
|
|
4888
|
-
useUserData,
|
|
4889
|
-
useUserProfile
|
|
4890
|
-
};
|
|
1
|
+
import{a as U,b as L,c as v,d as O,e as h,f as k,g as A}from"./chunk-JZVX4CN3.mjs";import{a as N,b,c as _,d as w}from"./chunk-D3OQLCQW.mjs";import{a as r,b as a,c as n,d as m,e as l,f as x,g as d,h as y,i as c,j as T}from"./chunk-FBT222LR.mjs";import{a as I,b as q}from"./chunk-T2CPA46I.mjs";import{a as S,b as P,c as R,d as g,e as E,f as C,g as D}from"./chunk-BJ6PIVZR.mjs";import{a as o,b as e,c as t,d as s,e as i,f as p,g as f,h as u}from"./chunk-YS3C7YG5.mjs";import{default as J}from"@nocios/crudify-browser";export*from"@nocios/crudify-browser";export{U as CrudifyLogin,S as ERROR_CODES,P as ERROR_SEVERITY_MAP,m as GlobalNotificationProvider,k as LoginComponent,v as POLICY_ACTIONS,O as PREFERRED_POLICY_ORDER,h as Policies,y as ProtectedRoute,c as SessionDebugInfo,a as SessionManager,x as SessionProvider,A as SessionStatus,r as TokenStorage,L as UserProfileDisplay,i as createErrorTranslator,J as crudify,p as decodeJwtSafely,o as getCookie,f as getCurrentUserEmail,E as getErrorMessage,D as handleCrudifyError,u as isTokenExpired,R as parseApiError,C as parseJavaScriptError,g as parseTransactionError,q as secureLocalStorage,I as secureSessionStorage,s as translateError,e as translateErrorCode,t as translateErrorCodes,b as useAuth,w as useCrudifyWithNotifications,_ as useData,l as useGlobalNotification,n as useSession,d as useSessionContext,N as useUserData,T as useUserProfile};
|