@nocios/crudify-ui 1.3.4 → 2.0.0
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/README.md +37 -39
- package/README_DEPTH.md +400 -216
- package/dist/index.d.mts +26 -26
- package/dist/index.d.ts +26 -26
- package/dist/index.js +1982 -1982
- package/dist/index.mjs +1837 -1837
- package/package.json +1 -1
package/dist/index.mjs
CHANGED
|
@@ -2027,19 +2027,631 @@ var UserProfileDisplay = ({
|
|
|
2027
2027
|
};
|
|
2028
2028
|
var UserProfileDisplay_default = UserProfileDisplay;
|
|
2029
2029
|
|
|
2030
|
+
// src/components/PublicPolicies/Policies.tsx
|
|
2031
|
+
import { useRef as useRef4 } from "react";
|
|
2032
|
+
import { useTranslation as useTranslation4 } from "react-i18next";
|
|
2033
|
+
import {
|
|
2034
|
+
Box as Box10,
|
|
2035
|
+
Typography as Typography10,
|
|
2036
|
+
Button as Button7,
|
|
2037
|
+
Stack as Stack3,
|
|
2038
|
+
Alert as Alert7,
|
|
2039
|
+
Divider as Divider3
|
|
2040
|
+
} from "@mui/material";
|
|
2041
|
+
import { Add } from "@mui/icons-material";
|
|
2042
|
+
|
|
2043
|
+
// src/components/PublicPolicies/PolicyItem/PolicyItem.tsx
|
|
2044
|
+
import { forwardRef } from "react";
|
|
2045
|
+
import { useTranslation as useTranslation3 } from "react-i18next";
|
|
2046
|
+
import {
|
|
2047
|
+
Box as Box9,
|
|
2048
|
+
FormControl,
|
|
2049
|
+
InputLabel,
|
|
2050
|
+
Select,
|
|
2051
|
+
MenuItem,
|
|
2052
|
+
IconButton as IconButton2,
|
|
2053
|
+
Typography as Typography9,
|
|
2054
|
+
FormHelperText as FormHelperText2,
|
|
2055
|
+
Stack as Stack2,
|
|
2056
|
+
Paper,
|
|
2057
|
+
Divider as Divider2,
|
|
2058
|
+
Button as Button6
|
|
2059
|
+
} from "@mui/material";
|
|
2060
|
+
import { Delete, SelectAll as SelectAll2, ClearAll as ClearAll2 } from "@mui/icons-material";
|
|
2061
|
+
|
|
2062
|
+
// src/components/PublicPolicies/FieldSelector/FieldSelector.tsx
|
|
2063
|
+
import { useState as useState8, useEffect as useEffect8, useRef as useRef3 } from "react";
|
|
2064
|
+
import { useTranslation as useTranslation2 } from "react-i18next";
|
|
2065
|
+
import {
|
|
2066
|
+
Box as Box8,
|
|
2067
|
+
Typography as Typography8,
|
|
2068
|
+
Button as Button5,
|
|
2069
|
+
Stack,
|
|
2070
|
+
FormHelperText,
|
|
2071
|
+
ToggleButton,
|
|
2072
|
+
ToggleButtonGroup
|
|
2073
|
+
} from "@mui/material";
|
|
2074
|
+
import {
|
|
2075
|
+
CheckCircle,
|
|
2076
|
+
Cancel,
|
|
2077
|
+
SelectAll,
|
|
2078
|
+
ClearAll
|
|
2079
|
+
} from "@mui/icons-material";
|
|
2080
|
+
import { jsx as jsx11, jsxs as jsxs8 } from "react/jsx-runtime";
|
|
2081
|
+
var FieldSelector = ({
|
|
2082
|
+
value,
|
|
2083
|
+
onChange,
|
|
2084
|
+
availableFields,
|
|
2085
|
+
error,
|
|
2086
|
+
disabled = false
|
|
2087
|
+
}) => {
|
|
2088
|
+
const { t } = useTranslation2();
|
|
2089
|
+
const [mode, setMode] = useState8("custom");
|
|
2090
|
+
const isUpdatingRef = useRef3(false);
|
|
2091
|
+
useEffect8(() => {
|
|
2092
|
+
const current = value || { allow: [], owner_allow: [], deny: [] };
|
|
2093
|
+
const all = new Set(availableFields);
|
|
2094
|
+
const allow = (current.allow || []).filter((f) => all.has(f));
|
|
2095
|
+
const owner = (current.owner_allow || []).filter((f) => all.has(f));
|
|
2096
|
+
const deny = (current.deny || []).filter((f) => all.has(f));
|
|
2097
|
+
availableFields.forEach((f) => {
|
|
2098
|
+
if (!allow.includes(f) && !owner.includes(f) && !deny.includes(f)) deny.push(f);
|
|
2099
|
+
});
|
|
2100
|
+
const normalized = { allow, owner_allow: owner, deny };
|
|
2101
|
+
if (JSON.stringify(normalized) !== JSON.stringify(current)) {
|
|
2102
|
+
onChange(normalized);
|
|
2103
|
+
}
|
|
2104
|
+
if (allow.length === availableFields.length) setMode("all");
|
|
2105
|
+
else if (deny.length === availableFields.length) setMode("none");
|
|
2106
|
+
else setMode("custom");
|
|
2107
|
+
}, [availableFields, value]);
|
|
2108
|
+
const setAllAllow = () => {
|
|
2109
|
+
isUpdatingRef.current = true;
|
|
2110
|
+
onChange({ allow: [...availableFields], owner_allow: [], deny: [] });
|
|
2111
|
+
setMode("all");
|
|
2112
|
+
setTimeout(() => {
|
|
2113
|
+
isUpdatingRef.current = false;
|
|
2114
|
+
}, 0);
|
|
2115
|
+
};
|
|
2116
|
+
const setAllDeny = () => {
|
|
2117
|
+
isUpdatingRef.current = true;
|
|
2118
|
+
onChange({ allow: [], owner_allow: [], deny: [...availableFields] });
|
|
2119
|
+
setMode("none");
|
|
2120
|
+
setTimeout(() => {
|
|
2121
|
+
isUpdatingRef.current = false;
|
|
2122
|
+
}, 0);
|
|
2123
|
+
};
|
|
2124
|
+
const getFieldState = (fieldName) => {
|
|
2125
|
+
if (value?.allow?.includes(fieldName)) return "allow";
|
|
2126
|
+
if (value?.owner_allow?.includes(fieldName)) return "owner_allow";
|
|
2127
|
+
return "deny";
|
|
2128
|
+
};
|
|
2129
|
+
const setFieldState = (fieldName, state) => {
|
|
2130
|
+
isUpdatingRef.current = true;
|
|
2131
|
+
const allow = new Set(value?.allow || []);
|
|
2132
|
+
const owner = new Set(value?.owner_allow || []);
|
|
2133
|
+
const deny = new Set(value?.deny || []);
|
|
2134
|
+
allow.delete(fieldName);
|
|
2135
|
+
owner.delete(fieldName);
|
|
2136
|
+
deny.delete(fieldName);
|
|
2137
|
+
if (state === "allow") allow.add(fieldName);
|
|
2138
|
+
if (state === "owner_allow") owner.add(fieldName);
|
|
2139
|
+
if (state === "deny") deny.add(fieldName);
|
|
2140
|
+
onChange({ allow: Array.from(allow), owner_allow: Array.from(owner), deny: Array.from(deny) });
|
|
2141
|
+
setMode("custom");
|
|
2142
|
+
setTimeout(() => {
|
|
2143
|
+
isUpdatingRef.current = false;
|
|
2144
|
+
}, 0);
|
|
2145
|
+
};
|
|
2146
|
+
if (availableFields.length === 0) {
|
|
2147
|
+
return /* @__PURE__ */ jsxs8(Box8, { children: [
|
|
2148
|
+
/* @__PURE__ */ jsx11(Typography8, { variant: "body2", color: "text.secondary", sx: { mb: 1 }, children: t("modules.form.publicPolicies.fields.conditions.label") }),
|
|
2149
|
+
/* @__PURE__ */ jsx11(Typography8, { variant: "body2", color: "text.secondary", sx: { fontStyle: "italic" }, children: t("modules.form.publicPolicies.fields.conditions.noFieldsAvailable") }),
|
|
2150
|
+
error && /* @__PURE__ */ jsx11(FormHelperText, { error: true, sx: { mt: 1 }, children: error })
|
|
2151
|
+
] });
|
|
2152
|
+
}
|
|
2153
|
+
return /* @__PURE__ */ jsxs8(Box8, { children: [
|
|
2154
|
+
/* @__PURE__ */ jsx11(Typography8, { variant: "body2", color: "text.secondary", sx: { mb: 2 }, children: t("modules.form.publicPolicies.fields.conditions.label") }),
|
|
2155
|
+
/* @__PURE__ */ jsxs8(Stack, { direction: "row", spacing: 1, sx: { mb: 3 }, children: [
|
|
2156
|
+
/* @__PURE__ */ jsx11(
|
|
2157
|
+
Button5,
|
|
2158
|
+
{
|
|
2159
|
+
variant: mode === "all" ? "contained" : "outlined",
|
|
2160
|
+
startIcon: /* @__PURE__ */ jsx11(SelectAll, {}),
|
|
2161
|
+
onClick: setAllAllow,
|
|
2162
|
+
disabled,
|
|
2163
|
+
size: "small",
|
|
2164
|
+
sx: {
|
|
2165
|
+
minWidth: 120,
|
|
2166
|
+
...mode === "all" && {
|
|
2167
|
+
backgroundColor: "#16a34a",
|
|
2168
|
+
"&:hover": { backgroundColor: "#15803d" }
|
|
2169
|
+
}
|
|
2170
|
+
},
|
|
2171
|
+
children: t("modules.form.publicPolicies.fields.conditions.allFields")
|
|
2172
|
+
}
|
|
2173
|
+
),
|
|
2174
|
+
/* @__PURE__ */ jsx11(
|
|
2175
|
+
Button5,
|
|
2176
|
+
{
|
|
2177
|
+
variant: mode === "none" ? "contained" : "outlined",
|
|
2178
|
+
startIcon: /* @__PURE__ */ jsx11(ClearAll, {}),
|
|
2179
|
+
onClick: setAllDeny,
|
|
2180
|
+
disabled,
|
|
2181
|
+
size: "small",
|
|
2182
|
+
sx: {
|
|
2183
|
+
minWidth: 120,
|
|
2184
|
+
...mode === "none" && {
|
|
2185
|
+
backgroundColor: "#cf222e",
|
|
2186
|
+
"&:hover": { backgroundColor: "#bc1f2c" }
|
|
2187
|
+
}
|
|
2188
|
+
},
|
|
2189
|
+
children: t("modules.form.publicPolicies.fields.conditions.noFields")
|
|
2190
|
+
}
|
|
2191
|
+
)
|
|
2192
|
+
] }),
|
|
2193
|
+
/* @__PURE__ */ jsxs8(Box8, { sx: { p: 2, border: "1px solid #d1d9e0", borderRadius: 1, backgroundColor: "#f6f8fa" }, children: [
|
|
2194
|
+
/* @__PURE__ */ jsx11(Typography8, { variant: "body2", color: "text.secondary", sx: { mb: 2 }, children: t("modules.form.publicPolicies.fields.conditions.help") }),
|
|
2195
|
+
/* @__PURE__ */ jsx11(Stack, { spacing: 1, children: availableFields.map((fieldName) => {
|
|
2196
|
+
const fieldState = getFieldState(fieldName);
|
|
2197
|
+
return /* @__PURE__ */ jsxs8(Stack, { direction: "row", spacing: 1, alignItems: "center", children: [
|
|
2198
|
+
/* @__PURE__ */ jsx11(Typography8, { variant: "body2", sx: { minWidth: 100, fontFamily: "monospace" }, children: fieldName }),
|
|
2199
|
+
/* @__PURE__ */ jsxs8(ToggleButtonGroup, { value: fieldState, exclusive: true, size: "small", children: [
|
|
2200
|
+
/* @__PURE__ */ jsxs8(
|
|
2201
|
+
ToggleButton,
|
|
2202
|
+
{
|
|
2203
|
+
value: "allow",
|
|
2204
|
+
onClick: () => setFieldState(fieldName, "allow"),
|
|
2205
|
+
disabled,
|
|
2206
|
+
sx: {
|
|
2207
|
+
px: 2,
|
|
2208
|
+
color: fieldState === "allow" ? "#ffffff" : "#6b7280",
|
|
2209
|
+
backgroundColor: fieldState === "allow" ? "#16a34a" : "#f3f4f6",
|
|
2210
|
+
borderColor: fieldState === "allow" ? "#16a34a" : "#d1d5db",
|
|
2211
|
+
"&:hover": {
|
|
2212
|
+
backgroundColor: fieldState === "allow" ? "#15803d" : "#e5e7eb",
|
|
2213
|
+
borderColor: fieldState === "allow" ? "#15803d" : "#9ca3af"
|
|
2214
|
+
},
|
|
2215
|
+
"&.Mui-selected": {
|
|
2216
|
+
backgroundColor: "#16a34a",
|
|
2217
|
+
color: "#ffffff",
|
|
2218
|
+
"&:hover": {
|
|
2219
|
+
backgroundColor: "#15803d"
|
|
2220
|
+
}
|
|
2221
|
+
}
|
|
2222
|
+
},
|
|
2223
|
+
children: [
|
|
2224
|
+
/* @__PURE__ */ jsx11(CheckCircle, { sx: { fontSize: 16, mr: 0.5 } }),
|
|
2225
|
+
t("modules.form.publicPolicies.fields.conditions.states.allow")
|
|
2226
|
+
]
|
|
2227
|
+
}
|
|
2228
|
+
),
|
|
2229
|
+
/* @__PURE__ */ jsx11(
|
|
2230
|
+
ToggleButton,
|
|
2231
|
+
{
|
|
2232
|
+
value: "owner_allow",
|
|
2233
|
+
onClick: () => setFieldState(fieldName, "owner_allow"),
|
|
2234
|
+
disabled,
|
|
2235
|
+
sx: {
|
|
2236
|
+
px: 2,
|
|
2237
|
+
color: fieldState === "owner_allow" ? "#ffffff" : "#6b7280",
|
|
2238
|
+
backgroundColor: fieldState === "owner_allow" ? "#0ea5e9" : "#f3f4f6",
|
|
2239
|
+
borderColor: fieldState === "owner_allow" ? "#0ea5e9" : "#d1d5db",
|
|
2240
|
+
"&:hover": {
|
|
2241
|
+
backgroundColor: fieldState === "owner_allow" ? "#0284c7" : "#e5e7eb",
|
|
2242
|
+
borderColor: fieldState === "owner_allow" ? "#0284c7" : "#9ca3af"
|
|
2243
|
+
},
|
|
2244
|
+
"&.Mui-selected": {
|
|
2245
|
+
backgroundColor: "#0ea5e9",
|
|
2246
|
+
color: "#ffffff",
|
|
2247
|
+
"&:hover": {
|
|
2248
|
+
backgroundColor: "#0284c7"
|
|
2249
|
+
}
|
|
2250
|
+
}
|
|
2251
|
+
},
|
|
2252
|
+
children: t("modules.form.publicPolicies.fields.conditions.states.ownerAllow")
|
|
2253
|
+
}
|
|
2254
|
+
),
|
|
2255
|
+
/* @__PURE__ */ jsxs8(
|
|
2256
|
+
ToggleButton,
|
|
2257
|
+
{
|
|
2258
|
+
value: "deny",
|
|
2259
|
+
onClick: () => setFieldState(fieldName, "deny"),
|
|
2260
|
+
disabled,
|
|
2261
|
+
sx: {
|
|
2262
|
+
px: 2,
|
|
2263
|
+
color: fieldState === "deny" ? "#ffffff" : "#6b7280",
|
|
2264
|
+
backgroundColor: fieldState === "deny" ? "#dc2626" : "#f3f4f6",
|
|
2265
|
+
borderColor: fieldState === "deny" ? "#dc2626" : "#d1d5db",
|
|
2266
|
+
"&:hover": {
|
|
2267
|
+
backgroundColor: fieldState === "deny" ? "#b91c1c" : "#e5e7eb",
|
|
2268
|
+
borderColor: fieldState === "deny" ? "#b91c1c" : "#9ca3af"
|
|
2269
|
+
},
|
|
2270
|
+
"&.Mui-selected": {
|
|
2271
|
+
backgroundColor: "#dc2626",
|
|
2272
|
+
color: "#ffffff",
|
|
2273
|
+
"&:hover": {
|
|
2274
|
+
backgroundColor: "#b91c1c"
|
|
2275
|
+
}
|
|
2276
|
+
}
|
|
2277
|
+
},
|
|
2278
|
+
children: [
|
|
2279
|
+
/* @__PURE__ */ jsx11(Cancel, { sx: { fontSize: 16, mr: 0.5 } }),
|
|
2280
|
+
t("modules.form.publicPolicies.fields.conditions.states.deny")
|
|
2281
|
+
]
|
|
2282
|
+
}
|
|
2283
|
+
)
|
|
2284
|
+
] })
|
|
2285
|
+
] }, fieldName);
|
|
2286
|
+
}) })
|
|
2287
|
+
] }),
|
|
2288
|
+
error && /* @__PURE__ */ jsx11(FormHelperText, { error: true, sx: { mt: 1 }, children: error })
|
|
2289
|
+
] });
|
|
2290
|
+
};
|
|
2291
|
+
var FieldSelector_default = FieldSelector;
|
|
2292
|
+
|
|
2293
|
+
// src/components/PublicPolicies/constants.ts
|
|
2294
|
+
var POLICY_ACTIONS = ["create", "read", "update", "delete"];
|
|
2295
|
+
var PREFERRED_POLICY_ORDER = [
|
|
2296
|
+
"create",
|
|
2297
|
+
"read",
|
|
2298
|
+
"update",
|
|
2299
|
+
"delete"
|
|
2300
|
+
];
|
|
2301
|
+
|
|
2302
|
+
// src/components/PublicPolicies/PolicyItem/PolicyItem.tsx
|
|
2303
|
+
import { jsx as jsx12, jsxs as jsxs9 } from "react/jsx-runtime";
|
|
2304
|
+
var PolicyItem = forwardRef(({
|
|
2305
|
+
policy,
|
|
2306
|
+
onChange,
|
|
2307
|
+
onRemove,
|
|
2308
|
+
availableFields,
|
|
2309
|
+
isSubmitting = false,
|
|
2310
|
+
usedActions,
|
|
2311
|
+
error
|
|
2312
|
+
}, ref) => {
|
|
2313
|
+
const { t } = useTranslation3();
|
|
2314
|
+
const takenActions = new Set(Array.from(usedActions || []));
|
|
2315
|
+
takenActions.delete(policy.action);
|
|
2316
|
+
const actionOptions = POLICY_ACTIONS.map((a) => ({
|
|
2317
|
+
value: a,
|
|
2318
|
+
label: t(`modules.form.publicPolicies.fields.action.options.${a}`)
|
|
2319
|
+
}));
|
|
2320
|
+
return /* @__PURE__ */ jsxs9(
|
|
2321
|
+
Paper,
|
|
2322
|
+
{
|
|
2323
|
+
ref,
|
|
2324
|
+
sx: {
|
|
2325
|
+
p: 3,
|
|
2326
|
+
border: "1px solid #d1d9e0",
|
|
2327
|
+
borderRadius: 2,
|
|
2328
|
+
position: "relative",
|
|
2329
|
+
backgroundColor: "#ffffff"
|
|
2330
|
+
},
|
|
2331
|
+
children: [
|
|
2332
|
+
/* @__PURE__ */ jsxs9(Box9, { sx: { display: "flex", justifyContent: "space-between", alignItems: "flex-start", mb: 3 }, children: [
|
|
2333
|
+
/* @__PURE__ */ jsx12(
|
|
2334
|
+
Typography9,
|
|
2335
|
+
{
|
|
2336
|
+
variant: "subtitle1",
|
|
2337
|
+
sx: {
|
|
2338
|
+
fontWeight: 600,
|
|
2339
|
+
color: "#111418",
|
|
2340
|
+
fontSize: "1rem"
|
|
2341
|
+
},
|
|
2342
|
+
children: t("modules.form.publicPolicies.policyTitle")
|
|
2343
|
+
}
|
|
2344
|
+
),
|
|
2345
|
+
/* @__PURE__ */ jsx12(
|
|
2346
|
+
IconButton2,
|
|
2347
|
+
{
|
|
2348
|
+
onClick: onRemove,
|
|
2349
|
+
size: "small",
|
|
2350
|
+
disabled: isSubmitting,
|
|
2351
|
+
"aria-label": t("modules.form.publicPolicies.removePolicy"),
|
|
2352
|
+
sx: {
|
|
2353
|
+
color: "#656d76",
|
|
2354
|
+
"&:hover": {
|
|
2355
|
+
color: "#cf222e",
|
|
2356
|
+
backgroundColor: "rgba(207, 34, 46, 0.1)"
|
|
2357
|
+
}
|
|
2358
|
+
},
|
|
2359
|
+
children: /* @__PURE__ */ jsx12(Delete, {})
|
|
2360
|
+
}
|
|
2361
|
+
)
|
|
2362
|
+
] }),
|
|
2363
|
+
/* @__PURE__ */ jsxs9(Stack2, { spacing: 3, children: [
|
|
2364
|
+
/* @__PURE__ */ jsx12(Stack2, { direction: { xs: "column", md: "row" }, spacing: 2, children: /* @__PURE__ */ jsx12(Box9, { sx: { flex: 1, minWidth: 200 }, children: /* @__PURE__ */ jsxs9(FormControl, { fullWidth: true, children: [
|
|
2365
|
+
/* @__PURE__ */ jsx12(InputLabel, { children: t("modules.form.publicPolicies.fields.action.label") }),
|
|
2366
|
+
/* @__PURE__ */ jsx12(
|
|
2367
|
+
Select,
|
|
2368
|
+
{
|
|
2369
|
+
value: policy.action,
|
|
2370
|
+
label: t("modules.form.publicPolicies.fields.action.label"),
|
|
2371
|
+
disabled: isSubmitting,
|
|
2372
|
+
onChange: (e) => {
|
|
2373
|
+
const newAction = e.target.value;
|
|
2374
|
+
const next = { ...policy, action: newAction };
|
|
2375
|
+
if (newAction === "delete") {
|
|
2376
|
+
next.permission = "deny";
|
|
2377
|
+
delete next.fields;
|
|
2378
|
+
} else {
|
|
2379
|
+
next.fields = { allow: [], owner_allow: [], deny: availableFields };
|
|
2380
|
+
delete next.permission;
|
|
2381
|
+
}
|
|
2382
|
+
onChange(next);
|
|
2383
|
+
},
|
|
2384
|
+
sx: {
|
|
2385
|
+
backgroundColor: "#ffffff",
|
|
2386
|
+
"&:hover .MuiOutlinedInput-notchedOutline": {
|
|
2387
|
+
borderColor: "#8c959f"
|
|
2388
|
+
},
|
|
2389
|
+
"&.Mui-focused .MuiOutlinedInput-notchedOutline": {
|
|
2390
|
+
borderColor: "#0969da",
|
|
2391
|
+
borderWidth: 2
|
|
2392
|
+
}
|
|
2393
|
+
},
|
|
2394
|
+
children: actionOptions.map((option) => {
|
|
2395
|
+
const disabledOption = takenActions.has(option.value);
|
|
2396
|
+
return /* @__PURE__ */ jsx12(MenuItem, { value: option.value, disabled: disabledOption, children: option.label }, option.value);
|
|
2397
|
+
})
|
|
2398
|
+
}
|
|
2399
|
+
),
|
|
2400
|
+
error && /* @__PURE__ */ jsx12(FormHelperText2, { error: true, children: error })
|
|
2401
|
+
] }) }) }),
|
|
2402
|
+
policy.action === "delete" ? /* @__PURE__ */ jsxs9(Box9, { children: [
|
|
2403
|
+
/* @__PURE__ */ jsx12(Typography9, { variant: "body2", color: "text.secondary", sx: { mb: 2 }, children: t("modules.form.publicPolicies.fields.conditions.label") }),
|
|
2404
|
+
/* @__PURE__ */ jsxs9(Stack2, { direction: "row", spacing: 1, sx: { mb: 3 }, children: [
|
|
2405
|
+
/* @__PURE__ */ jsx12(
|
|
2406
|
+
Button6,
|
|
2407
|
+
{
|
|
2408
|
+
variant: policy.permission === "*" ? "contained" : "outlined",
|
|
2409
|
+
startIcon: /* @__PURE__ */ jsx12(SelectAll2, {}),
|
|
2410
|
+
onClick: () => onChange({ ...policy, permission: "*" }),
|
|
2411
|
+
disabled: isSubmitting,
|
|
2412
|
+
size: "small",
|
|
2413
|
+
sx: {
|
|
2414
|
+
minWidth: 140,
|
|
2415
|
+
whiteSpace: "nowrap",
|
|
2416
|
+
...policy.permission === "*" && {
|
|
2417
|
+
backgroundColor: "#16a34a",
|
|
2418
|
+
"&:hover": { backgroundColor: "#15803d" }
|
|
2419
|
+
}
|
|
2420
|
+
},
|
|
2421
|
+
children: t("modules.form.publicPolicies.fields.conditions.allFields")
|
|
2422
|
+
}
|
|
2423
|
+
),
|
|
2424
|
+
/* @__PURE__ */ jsx12(
|
|
2425
|
+
Button6,
|
|
2426
|
+
{
|
|
2427
|
+
variant: policy.permission === "owner" ? "contained" : "outlined",
|
|
2428
|
+
onClick: () => onChange({ ...policy, permission: "owner" }),
|
|
2429
|
+
disabled: isSubmitting,
|
|
2430
|
+
size: "small",
|
|
2431
|
+
sx: {
|
|
2432
|
+
minWidth: 140,
|
|
2433
|
+
whiteSpace: "nowrap",
|
|
2434
|
+
...policy.permission === "owner" && {
|
|
2435
|
+
backgroundColor: "#0ea5e9",
|
|
2436
|
+
"&:hover": { backgroundColor: "#0284c7" }
|
|
2437
|
+
}
|
|
2438
|
+
},
|
|
2439
|
+
children: t("modules.form.publicPolicies.fields.conditions.states.ownerAllow")
|
|
2440
|
+
}
|
|
2441
|
+
),
|
|
2442
|
+
/* @__PURE__ */ jsx12(
|
|
2443
|
+
Button6,
|
|
2444
|
+
{
|
|
2445
|
+
variant: policy.permission === "deny" ? "contained" : "outlined",
|
|
2446
|
+
startIcon: /* @__PURE__ */ jsx12(ClearAll2, {}),
|
|
2447
|
+
onClick: () => onChange({ ...policy, permission: "deny" }),
|
|
2448
|
+
disabled: isSubmitting,
|
|
2449
|
+
size: "small",
|
|
2450
|
+
sx: {
|
|
2451
|
+
minWidth: 140,
|
|
2452
|
+
whiteSpace: "nowrap",
|
|
2453
|
+
...policy.permission === "deny" && {
|
|
2454
|
+
backgroundColor: "#cf222e",
|
|
2455
|
+
"&:hover": { backgroundColor: "#bc1f2c" }
|
|
2456
|
+
}
|
|
2457
|
+
},
|
|
2458
|
+
children: t("modules.form.publicPolicies.fields.conditions.noFields")
|
|
2459
|
+
}
|
|
2460
|
+
)
|
|
2461
|
+
] })
|
|
2462
|
+
] }) : /* @__PURE__ */ jsx12(
|
|
2463
|
+
FieldSelector_default,
|
|
2464
|
+
{
|
|
2465
|
+
value: policy.fields || { allow: [], owner_allow: [], deny: [] },
|
|
2466
|
+
onChange: (nextFields) => onChange({ ...policy, fields: nextFields }),
|
|
2467
|
+
availableFields,
|
|
2468
|
+
disabled: isSubmitting
|
|
2469
|
+
}
|
|
2470
|
+
),
|
|
2471
|
+
/* @__PURE__ */ jsx12(Paper, { variant: "outlined", sx: { p: 2, backgroundColor: "#f9fafb" }, children: policy.action === "delete" ? /* @__PURE__ */ jsxs9(Typography9, { variant: "body2", sx: { fontFamily: "monospace", color: "text.secondary" }, children: [
|
|
2472
|
+
/* @__PURE__ */ jsxs9(Box9, { component: "span", sx: {
|
|
2473
|
+
color: policy.permission === "*" ? "#16a34a" : policy.permission === "owner" ? "#0ea5e9" : "#dc2626"
|
|
2474
|
+
}, children: [
|
|
2475
|
+
t("modules.form.publicPolicies.fields.conditions.states.allow"),
|
|
2476
|
+
":"
|
|
2477
|
+
] }),
|
|
2478
|
+
" ",
|
|
2479
|
+
policy.permission || "-"
|
|
2480
|
+
] }) : /* @__PURE__ */ jsxs9(Stack2, { spacing: 0.5, divider: /* @__PURE__ */ jsx12(Divider2, { sx: { borderColor: "#e5e7eb" } }), children: [
|
|
2481
|
+
/* @__PURE__ */ jsxs9(Typography9, { variant: "body2", sx: { fontFamily: "monospace", color: "text.secondary" }, children: [
|
|
2482
|
+
/* @__PURE__ */ jsxs9(Box9, { component: "span", sx: { color: "#16a34a" }, children: [
|
|
2483
|
+
t("modules.form.publicPolicies.fields.conditions.states.allow"),
|
|
2484
|
+
":"
|
|
2485
|
+
] }),
|
|
2486
|
+
" ",
|
|
2487
|
+
(policy?.fields?.allow || []).join(", ") || "-"
|
|
2488
|
+
] }),
|
|
2489
|
+
/* @__PURE__ */ jsxs9(Typography9, { variant: "body2", sx: { fontFamily: "monospace", color: "text.secondary" }, children: [
|
|
2490
|
+
/* @__PURE__ */ jsxs9(Box9, { component: "span", sx: { color: "#0ea5e9" }, children: [
|
|
2491
|
+
t("modules.form.publicPolicies.fields.conditions.states.ownerAllow"),
|
|
2492
|
+
":"
|
|
2493
|
+
] }),
|
|
2494
|
+
" ",
|
|
2495
|
+
(policy?.fields?.owner_allow || []).join(", ") || "-"
|
|
2496
|
+
] }),
|
|
2497
|
+
/* @__PURE__ */ jsxs9(Typography9, { variant: "body2", sx: { fontFamily: "monospace", color: "text.secondary" }, children: [
|
|
2498
|
+
/* @__PURE__ */ jsxs9(Box9, { component: "span", sx: { color: "#dc2626" }, children: [
|
|
2499
|
+
t("modules.form.publicPolicies.fields.conditions.states.deny"),
|
|
2500
|
+
":"
|
|
2501
|
+
] }),
|
|
2502
|
+
" ",
|
|
2503
|
+
(policy?.fields?.deny || []).join(", ") || "-"
|
|
2504
|
+
] })
|
|
2505
|
+
] }) })
|
|
2506
|
+
] })
|
|
2507
|
+
]
|
|
2508
|
+
}
|
|
2509
|
+
);
|
|
2510
|
+
});
|
|
2511
|
+
var PolicyItem_default = PolicyItem;
|
|
2512
|
+
|
|
2513
|
+
// src/components/PublicPolicies/Policies.tsx
|
|
2514
|
+
import { Fragment as Fragment7, jsx as jsx13, jsxs as jsxs10 } from "react/jsx-runtime";
|
|
2515
|
+
var generateId = () => {
|
|
2516
|
+
const c = globalThis?.crypto;
|
|
2517
|
+
if (c && typeof c.randomUUID === "function") return c.randomUUID();
|
|
2518
|
+
return `${Date.now()}-${Math.random().toString(16).slice(2)}`;
|
|
2519
|
+
};
|
|
2520
|
+
var Policies = ({
|
|
2521
|
+
policies,
|
|
2522
|
+
onChange,
|
|
2523
|
+
availableFields,
|
|
2524
|
+
errors,
|
|
2525
|
+
isSubmitting = false
|
|
2526
|
+
}) => {
|
|
2527
|
+
const { t } = useTranslation4();
|
|
2528
|
+
const policyRefs = useRef4({});
|
|
2529
|
+
const takenActions = new Set((policies || []).map((p) => p.action).filter(Boolean));
|
|
2530
|
+
const remainingActions = PREFERRED_POLICY_ORDER.filter((a) => !takenActions.has(a));
|
|
2531
|
+
const canAddPolicy = remainingActions.length > 0;
|
|
2532
|
+
const addPolicy = () => {
|
|
2533
|
+
const defaultAction = remainingActions[0] || "create";
|
|
2534
|
+
const newPolicy = {
|
|
2535
|
+
id: generateId(),
|
|
2536
|
+
action: defaultAction
|
|
2537
|
+
};
|
|
2538
|
+
if (defaultAction === "delete") {
|
|
2539
|
+
newPolicy.permission = "deny";
|
|
2540
|
+
} else {
|
|
2541
|
+
newPolicy.fields = {
|
|
2542
|
+
allow: [],
|
|
2543
|
+
owner_allow: [],
|
|
2544
|
+
deny: availableFields
|
|
2545
|
+
};
|
|
2546
|
+
}
|
|
2547
|
+
const next = [...policies || [], newPolicy];
|
|
2548
|
+
onChange(next);
|
|
2549
|
+
setTimeout(() => {
|
|
2550
|
+
const newIndex = next.length - 1;
|
|
2551
|
+
const el = policyRefs.current[newIndex];
|
|
2552
|
+
if (el) {
|
|
2553
|
+
el.scrollIntoView({ behavior: "smooth", block: "center" });
|
|
2554
|
+
}
|
|
2555
|
+
}, 100);
|
|
2556
|
+
};
|
|
2557
|
+
const removePolicy = (index) => {
|
|
2558
|
+
const next = [...policies];
|
|
2559
|
+
next.splice(index, 1);
|
|
2560
|
+
onChange(next);
|
|
2561
|
+
};
|
|
2562
|
+
const arrayError = (() => {
|
|
2563
|
+
if (!errors) return null;
|
|
2564
|
+
if (typeof errors === "string") return errors;
|
|
2565
|
+
const msg = errors._error;
|
|
2566
|
+
return typeof msg === "string" ? msg : null;
|
|
2567
|
+
})();
|
|
2568
|
+
const usedActions = new Set((policies || []).map((p) => p.action));
|
|
2569
|
+
return /* @__PURE__ */ jsxs10(Fragment7, { children: [
|
|
2570
|
+
/* @__PURE__ */ jsx13(Divider3, { sx: { borderColor: "#e0e4e7" } }),
|
|
2571
|
+
/* @__PURE__ */ jsxs10(Box10, { children: [
|
|
2572
|
+
/* @__PURE__ */ jsx13(Box10, { display: "flex", justifyContent: "space-between", alignItems: "center", mb: 3, children: /* @__PURE__ */ jsxs10(Box10, { children: [
|
|
2573
|
+
/* @__PURE__ */ jsx13(
|
|
2574
|
+
Typography10,
|
|
2575
|
+
{
|
|
2576
|
+
variant: "h6",
|
|
2577
|
+
sx: {
|
|
2578
|
+
fontWeight: 600,
|
|
2579
|
+
color: "#111418",
|
|
2580
|
+
mb: 1
|
|
2581
|
+
},
|
|
2582
|
+
children: t("modules.form.publicPolicies.title")
|
|
2583
|
+
}
|
|
2584
|
+
),
|
|
2585
|
+
/* @__PURE__ */ jsx13(
|
|
2586
|
+
Typography10,
|
|
2587
|
+
{
|
|
2588
|
+
variant: "body2",
|
|
2589
|
+
color: "text.secondary",
|
|
2590
|
+
sx: { fontSize: "0.875rem" },
|
|
2591
|
+
children: t("modules.form.publicPolicies.description")
|
|
2592
|
+
}
|
|
2593
|
+
)
|
|
2594
|
+
] }) }),
|
|
2595
|
+
arrayError && /* @__PURE__ */ jsx13(Alert7, { severity: "error", sx: { mb: 3 }, children: arrayError }),
|
|
2596
|
+
/* @__PURE__ */ jsxs10(Stack3, { spacing: 3, children: [
|
|
2597
|
+
(policies || []).length === 0 ? /* @__PURE__ */ jsx13(Alert7, { severity: "info", children: t("modules.form.publicPolicies.noPolicies") }) : policies.map((policy, index) => /* @__PURE__ */ jsx13(
|
|
2598
|
+
PolicyItem_default,
|
|
2599
|
+
{
|
|
2600
|
+
ref: (el) => {
|
|
2601
|
+
policyRefs.current[index] = el;
|
|
2602
|
+
},
|
|
2603
|
+
policy,
|
|
2604
|
+
onChange: (nextPolicy) => {
|
|
2605
|
+
const next = [...policies];
|
|
2606
|
+
next[index] = nextPolicy;
|
|
2607
|
+
onChange(next);
|
|
2608
|
+
},
|
|
2609
|
+
onRemove: () => removePolicy(index),
|
|
2610
|
+
availableFields,
|
|
2611
|
+
isSubmitting,
|
|
2612
|
+
usedActions,
|
|
2613
|
+
error: typeof errors === "object" && errors && policy.id in errors ? errors[policy.id] : void 0
|
|
2614
|
+
},
|
|
2615
|
+
policy.id
|
|
2616
|
+
)),
|
|
2617
|
+
canAddPolicy && /* @__PURE__ */ jsx13(Box10, { children: /* @__PURE__ */ jsx13(
|
|
2618
|
+
Button7,
|
|
2619
|
+
{
|
|
2620
|
+
type: "button",
|
|
2621
|
+
variant: "outlined",
|
|
2622
|
+
startIcon: /* @__PURE__ */ jsx13(Add, {}),
|
|
2623
|
+
onClick: addPolicy,
|
|
2624
|
+
disabled: isSubmitting,
|
|
2625
|
+
sx: {
|
|
2626
|
+
borderColor: "#d0d7de",
|
|
2627
|
+
color: "#656d76",
|
|
2628
|
+
"&:hover": {
|
|
2629
|
+
borderColor: "#8c959f",
|
|
2630
|
+
backgroundColor: "transparent"
|
|
2631
|
+
}
|
|
2632
|
+
},
|
|
2633
|
+
children: t("modules.form.publicPolicies.addPolicy")
|
|
2634
|
+
}
|
|
2635
|
+
) })
|
|
2636
|
+
] })
|
|
2637
|
+
] })
|
|
2638
|
+
] });
|
|
2639
|
+
};
|
|
2640
|
+
var Policies_default = Policies;
|
|
2641
|
+
|
|
2030
2642
|
// src/hooks/useCrudifyUser.ts
|
|
2031
|
-
import { useState as
|
|
2643
|
+
import { useState as useState9, useEffect as useEffect9, useCallback as useCallback2, useRef as useRef5 } from "react";
|
|
2032
2644
|
import crudify3 from "@nocios/crudify-browser";
|
|
2033
2645
|
var useCrudifyUser = (options = {}) => {
|
|
2034
2646
|
const { autoFetch = true, retryOnError = false, maxRetries = 3 } = options;
|
|
2035
2647
|
const { isAuthenticated, isInitialized, user: jwtUser, token } = useCrudifyDataContext();
|
|
2036
|
-
const [userData, setUserData] =
|
|
2037
|
-
const [loading, setLoading] =
|
|
2038
|
-
const [error, setError] =
|
|
2039
|
-
const abortControllerRef =
|
|
2040
|
-
const mountedRef =
|
|
2041
|
-
const requestIdRef =
|
|
2042
|
-
const retryCountRef =
|
|
2648
|
+
const [userData, setUserData] = useState9(null);
|
|
2649
|
+
const [loading, setLoading] = useState9(false);
|
|
2650
|
+
const [error, setError] = useState9(null);
|
|
2651
|
+
const abortControllerRef = useRef5(null);
|
|
2652
|
+
const mountedRef = useRef5(true);
|
|
2653
|
+
const requestIdRef = useRef5(0);
|
|
2654
|
+
const retryCountRef = useRef5(0);
|
|
2043
2655
|
const getUserEmailFromJWT = useCallback2(() => {
|
|
2044
2656
|
if (!jwtUser) return null;
|
|
2045
2657
|
return jwtUser.email || jwtUser["cognito:username"] || null;
|
|
@@ -2182,14 +2794,14 @@ var useCrudifyUser = (options = {}) => {
|
|
|
2182
2794
|
}
|
|
2183
2795
|
}
|
|
2184
2796
|
}, [isInitialized, getUserEmailFromJWT, retryOnError, maxRetries]);
|
|
2185
|
-
|
|
2797
|
+
useEffect9(() => {
|
|
2186
2798
|
if (autoFetch && isAuthenticated && isInitialized) {
|
|
2187
2799
|
refreshProfile();
|
|
2188
2800
|
} else if (!isAuthenticated) {
|
|
2189
2801
|
clearProfile();
|
|
2190
2802
|
}
|
|
2191
2803
|
}, [autoFetch, isAuthenticated, isInitialized, refreshProfile, clearProfile]);
|
|
2192
|
-
|
|
2804
|
+
useEffect9(() => {
|
|
2193
2805
|
mountedRef.current = true;
|
|
2194
2806
|
return () => {
|
|
2195
2807
|
mountedRef.current = false;
|
|
@@ -2436,1949 +3048,1337 @@ var useCrudifyInstance = () => {
|
|
|
2436
3048
|
console.log(" - TokenManager token:", tmToken ? `${tmToken.substring(0, 20)}...` : "null");
|
|
2437
3049
|
console.log(" - crudify.getToken():", crudifyToken ? `${crudifyToken.substring(0, 20)}...` : "null");
|
|
2438
3050
|
console.log(" - Tokens match:", tmToken === crudifyToken);
|
|
2439
|
-
await ensureTokenSync();
|
|
2440
|
-
console.log("\u{1F4E1} useCrudifyInstance - getStructure: Calling crudify.getStructure");
|
|
2441
|
-
const response = await crudify5.getStructure(options);
|
|
2442
|
-
console.log("\u{1F4E1} useCrudifyInstance - getStructure: Response received", response);
|
|
2443
|
-
return response;
|
|
2444
|
-
} catch (error) {
|
|
2445
|
-
console.error("\u{1F4E1} useCrudifyInstance - getStructure: Error", error);
|
|
2446
|
-
throw error;
|
|
2447
|
-
}
|
|
2448
|
-
}, [ensureReady, ensureTokenSync]);
|
|
2449
|
-
const getStructurePublic = useCallback4(async (options) => {
|
|
2450
|
-
console.log("\u{1F4E1} useCrudifyInstance - getStructurePublic: Starting");
|
|
2451
|
-
await ensureReady("getStructurePublic");
|
|
2452
|
-
try {
|
|
2453
|
-
console.log("\u{1F4E1} useCrudifyInstance - getStructurePublic: Calling crudify.getStructurePublic");
|
|
2454
|
-
const response = await crudify5.getStructurePublic(options);
|
|
2455
|
-
console.log("\u{1F4E1} useCrudifyInstance - getStructurePublic: Response received", response);
|
|
2456
|
-
return response;
|
|
2457
|
-
} catch (error) {
|
|
2458
|
-
console.error("\u{1F4E1} useCrudifyInstance - getStructurePublic: Error", error);
|
|
2459
|
-
throw error;
|
|
2460
|
-
}
|
|
2461
|
-
}, [ensureReady]);
|
|
2462
|
-
const readItems = useCallback4(async (moduleKey, filter, options) => {
|
|
2463
|
-
await ensureReady("readItems");
|
|
2464
|
-
await ensureTokenSync();
|
|
2465
|
-
if (moduleKey === "__test_connection__") {
|
|
2466
|
-
console.error("\u{1F6A8} FOUND TEST CONNECTION CALL in useCrudifyInstance! Stack trace:");
|
|
2467
|
-
console.error(new Error().stack);
|
|
2468
|
-
}
|
|
2469
|
-
try {
|
|
2470
|
-
const response = await crudify5.readItems(moduleKey, filter || {}, options);
|
|
2471
|
-
return response;
|
|
2472
|
-
} catch (error) {
|
|
2473
|
-
console.error("\u{1F4CA} useCrudifyInstance - readItems error:", error);
|
|
2474
|
-
throw error;
|
|
2475
|
-
}
|
|
2476
|
-
}, [ensureReady, ensureTokenSync]);
|
|
2477
|
-
const readItem = useCallback4(async (moduleKey, filter, options) => {
|
|
2478
|
-
await ensureReady("readItem");
|
|
2479
|
-
try {
|
|
2480
|
-
const response = await crudify5.readItem(moduleKey, filter, options);
|
|
2481
|
-
return response;
|
|
2482
|
-
} catch (error) {
|
|
2483
|
-
console.error("\u{1F4CA} useCrudifyInstance - readItem error:", error);
|
|
2484
|
-
throw error;
|
|
2485
|
-
}
|
|
2486
|
-
}, [ensureReady]);
|
|
2487
|
-
const createItem = useCallback4(async (moduleKey, data, options) => {
|
|
2488
|
-
await ensureReady("createItem");
|
|
2489
|
-
try {
|
|
2490
|
-
const response = await crudify5.createItem(moduleKey, data, options);
|
|
2491
|
-
return response;
|
|
2492
|
-
} catch (error) {
|
|
2493
|
-
console.error("\u{1F4CA} useCrudifyInstance - createItem error:", error);
|
|
2494
|
-
throw error;
|
|
2495
|
-
}
|
|
2496
|
-
}, [ensureReady]);
|
|
2497
|
-
const updateItem = useCallback4(async (moduleKey, data, options) => {
|
|
2498
|
-
await ensureReady("updateItem");
|
|
2499
|
-
try {
|
|
2500
|
-
const response = await crudify5.updateItem(moduleKey, data, options);
|
|
2501
|
-
return response;
|
|
2502
|
-
} catch (error) {
|
|
2503
|
-
console.error("\u{1F4CA} useCrudifyInstance - updateItem error:", error);
|
|
2504
|
-
throw error;
|
|
2505
|
-
}
|
|
2506
|
-
}, [ensureReady]);
|
|
2507
|
-
const deleteItem = useCallback4(async (moduleKey, id, options) => {
|
|
2508
|
-
await ensureReady("deleteItem");
|
|
2509
|
-
try {
|
|
2510
|
-
const response = await crudify5.deleteItem(moduleKey, id, options);
|
|
2511
|
-
return response;
|
|
2512
|
-
} catch (error) {
|
|
2513
|
-
console.error("\u{1F4CA} useCrudifyInstance - deleteItem error:", error);
|
|
2514
|
-
throw error;
|
|
2515
|
-
}
|
|
2516
|
-
}, [ensureReady]);
|
|
2517
|
-
const transaction = useCallback4(async (operations, options) => {
|
|
2518
|
-
await ensureReady("transaction");
|
|
2519
|
-
try {
|
|
2520
|
-
const response = await crudify5.transaction(operations, options);
|
|
2521
|
-
return response;
|
|
2522
|
-
} catch (error) {
|
|
2523
|
-
console.error("\u{1F4CA} useCrudifyInstance - transaction error:", error);
|
|
2524
|
-
throw error;
|
|
2525
|
-
}
|
|
2526
|
-
}, [ensureReady]);
|
|
2527
|
-
const login = useCallback4(async (email, password) => {
|
|
2528
|
-
await ensureReady("login");
|
|
2529
|
-
try {
|
|
2530
|
-
const response = await crudify5.login(email, password);
|
|
2531
|
-
return response;
|
|
2532
|
-
} catch (error) {
|
|
2533
|
-
console.error("\u{1F4CA} useCrudifyInstance - login error:", error);
|
|
2534
|
-
throw error;
|
|
2535
|
-
}
|
|
2536
|
-
}, [ensureReady]);
|
|
2537
|
-
return {
|
|
2538
|
-
// Core operations
|
|
2539
|
-
getStructure,
|
|
2540
|
-
getStructurePublic,
|
|
2541
|
-
readItems,
|
|
2542
|
-
readItem,
|
|
2543
|
-
createItem,
|
|
2544
|
-
updateItem,
|
|
2545
|
-
deleteItem,
|
|
2546
|
-
transaction,
|
|
2547
|
-
login,
|
|
2548
|
-
// State information
|
|
2549
|
-
isReady,
|
|
2550
|
-
isInitialized,
|
|
2551
|
-
isInitializing,
|
|
2552
|
-
initializationError,
|
|
2553
|
-
// Utility methods
|
|
2554
|
-
waitForReady
|
|
2555
|
-
};
|
|
2556
|
-
};
|
|
2557
|
-
var getCrudifyInstanceAsync = async () => {
|
|
2558
|
-
console.log("\u{1F504} getCrudifyInstanceAsync - Starting");
|
|
2559
|
-
const { crudifyInitializer: crudifyInitializer2 } = await import("./CrudifyDataProvider-5GXGNQKI.mjs");
|
|
2560
|
-
console.log("\u{1F504} getCrudifyInstanceAsync - Checking if ready");
|
|
2561
|
-
console.log(" - crudifyInitializer.isReady():", crudifyInitializer2.isReady());
|
|
2562
|
-
console.log(" - crudifyInitializer.getStatus():", crudifyInitializer2.getStatus());
|
|
2563
|
-
if (!crudifyInitializer2.isReady()) {
|
|
2564
|
-
console.log("\u{1F504} getCrudifyInstanceAsync - Waiting for crudify initialization...");
|
|
2565
|
-
await crudifyInitializer2.waitForInitialization();
|
|
2566
|
-
if (!crudifyInitializer2.isReady()) {
|
|
2567
|
-
const error = crudifyInitializer2.getError();
|
|
2568
|
-
throw new Error(`Crudify initialization failed: ${error || "Unknown error after waiting"}`);
|
|
2569
|
-
}
|
|
2570
|
-
console.log("\u2705 getCrudifyInstanceAsync - Crudify is ready after waiting");
|
|
2571
|
-
} else {
|
|
2572
|
-
console.log("\u2705 getCrudifyInstanceAsync - Already ready, no wait needed");
|
|
2573
|
-
}
|
|
2574
|
-
console.log("\u2705 getCrudifyInstanceAsync - Returning crudify instance");
|
|
2575
|
-
return crudify5;
|
|
2576
|
-
};
|
|
2577
|
-
var getCrudifyInstanceSync = async () => {
|
|
2578
|
-
const { crudifyInitializer: crudifyInitializer2 } = await import("./CrudifyDataProvider-5GXGNQKI.mjs");
|
|
2579
|
-
if (!crudifyInitializer2.isReady()) {
|
|
2580
|
-
throw new Error("Crudify not ready. Use getCrudifyInstanceAsync() or call this from within a React component using useCrudifyInstance()");
|
|
2581
|
-
}
|
|
2582
|
-
return crudify5;
|
|
2583
|
-
};
|
|
2584
|
-
|
|
2585
|
-
// src/core/SessionManager.ts
|
|
2586
|
-
import crudify6 from "@nocios/crudify-browser";
|
|
2587
|
-
|
|
2588
|
-
// src/utils/tokenStorage.ts
|
|
2589
|
-
import CryptoJS from "crypto-js";
|
|
2590
|
-
var _TokenStorage = class _TokenStorage {
|
|
2591
|
-
/**
|
|
2592
|
-
* Configurar tipo de almacenamiento
|
|
2593
|
-
*/
|
|
2594
|
-
static setStorageType(type) {
|
|
2595
|
-
_TokenStorage.storageType = type;
|
|
2596
|
-
}
|
|
2597
|
-
/**
|
|
2598
|
-
* Verificar si el storage está disponible
|
|
2599
|
-
*/
|
|
2600
|
-
static isStorageAvailable(type) {
|
|
2601
|
-
try {
|
|
2602
|
-
const storage = window[type];
|
|
2603
|
-
const testKey = "__storage_test__";
|
|
2604
|
-
storage.setItem(testKey, "test");
|
|
2605
|
-
storage.removeItem(testKey);
|
|
2606
|
-
return true;
|
|
2607
|
-
} catch {
|
|
2608
|
-
return false;
|
|
2609
|
-
}
|
|
2610
|
-
}
|
|
2611
|
-
/**
|
|
2612
|
-
* Obtener instancia de storage
|
|
2613
|
-
*/
|
|
2614
|
-
static getStorage() {
|
|
2615
|
-
if (_TokenStorage.storageType === "none") return null;
|
|
2616
|
-
if (!_TokenStorage.isStorageAvailable(_TokenStorage.storageType)) {
|
|
2617
|
-
console.warn(`Crudify: ${_TokenStorage.storageType} not available, tokens won't persist`);
|
|
2618
|
-
return null;
|
|
2619
|
-
}
|
|
2620
|
-
return window[_TokenStorage.storageType];
|
|
2621
|
-
}
|
|
2622
|
-
/**
|
|
2623
|
-
* Encriptar datos sensibles
|
|
2624
|
-
*/
|
|
2625
|
-
static encrypt(data) {
|
|
2626
|
-
try {
|
|
2627
|
-
return CryptoJS.AES.encrypt(data, _TokenStorage.ENCRYPTION_KEY).toString();
|
|
2628
|
-
} catch (error) {
|
|
2629
|
-
console.error("Crudify: Encryption failed", error);
|
|
2630
|
-
return data;
|
|
2631
|
-
}
|
|
2632
|
-
}
|
|
2633
|
-
/**
|
|
2634
|
-
* Desencriptar datos
|
|
2635
|
-
*/
|
|
2636
|
-
static decrypt(encryptedData) {
|
|
2637
|
-
try {
|
|
2638
|
-
const bytes = CryptoJS.AES.decrypt(encryptedData, _TokenStorage.ENCRYPTION_KEY);
|
|
2639
|
-
const decrypted = bytes.toString(CryptoJS.enc.Utf8);
|
|
2640
|
-
return decrypted || encryptedData;
|
|
2641
|
-
} catch (error) {
|
|
2642
|
-
console.error("Crudify: Decryption failed", error);
|
|
2643
|
-
return encryptedData;
|
|
2644
|
-
}
|
|
2645
|
-
}
|
|
2646
|
-
/**
|
|
2647
|
-
* Guardar tokens de forma segura
|
|
2648
|
-
*/
|
|
2649
|
-
static saveTokens(tokens) {
|
|
2650
|
-
const storage = _TokenStorage.getStorage();
|
|
2651
|
-
if (!storage) return;
|
|
2652
|
-
try {
|
|
2653
|
-
const tokenData = {
|
|
2654
|
-
accessToken: tokens.accessToken,
|
|
2655
|
-
refreshToken: tokens.refreshToken,
|
|
2656
|
-
expiresAt: tokens.expiresAt,
|
|
2657
|
-
refreshExpiresAt: tokens.refreshExpiresAt,
|
|
2658
|
-
savedAt: Date.now()
|
|
2659
|
-
};
|
|
2660
|
-
const encrypted = _TokenStorage.encrypt(JSON.stringify(tokenData));
|
|
2661
|
-
storage.setItem(_TokenStorage.TOKEN_KEY, encrypted);
|
|
2662
|
-
console.debug("Crudify: Tokens saved successfully");
|
|
2663
|
-
} catch (error) {
|
|
2664
|
-
console.error("Crudify: Failed to save tokens", error);
|
|
2665
|
-
}
|
|
2666
|
-
}
|
|
2667
|
-
/**
|
|
2668
|
-
* Obtener tokens guardados
|
|
2669
|
-
*/
|
|
2670
|
-
static getTokens() {
|
|
2671
|
-
const storage = _TokenStorage.getStorage();
|
|
2672
|
-
if (!storage) return null;
|
|
2673
|
-
try {
|
|
2674
|
-
const encrypted = storage.getItem(_TokenStorage.TOKEN_KEY);
|
|
2675
|
-
if (!encrypted) return null;
|
|
2676
|
-
const decrypted = _TokenStorage.decrypt(encrypted);
|
|
2677
|
-
const tokenData = JSON.parse(decrypted);
|
|
2678
|
-
if (!tokenData.accessToken || !tokenData.refreshToken || !tokenData.expiresAt || !tokenData.refreshExpiresAt) {
|
|
2679
|
-
console.warn("Crudify: Incomplete token data found, clearing storage");
|
|
2680
|
-
_TokenStorage.clearTokens();
|
|
2681
|
-
return null;
|
|
2682
|
-
}
|
|
2683
|
-
if (Date.now() >= tokenData.refreshExpiresAt) {
|
|
2684
|
-
console.info("Crudify: Refresh token expired, clearing storage");
|
|
2685
|
-
_TokenStorage.clearTokens();
|
|
2686
|
-
return null;
|
|
2687
|
-
}
|
|
2688
|
-
return {
|
|
2689
|
-
accessToken: tokenData.accessToken,
|
|
2690
|
-
refreshToken: tokenData.refreshToken,
|
|
2691
|
-
expiresAt: tokenData.expiresAt,
|
|
2692
|
-
refreshExpiresAt: tokenData.refreshExpiresAt
|
|
2693
|
-
};
|
|
3051
|
+
await ensureTokenSync();
|
|
3052
|
+
console.log("\u{1F4E1} useCrudifyInstance - getStructure: Calling crudify.getStructure");
|
|
3053
|
+
const response = await crudify5.getStructure(options);
|
|
3054
|
+
console.log("\u{1F4E1} useCrudifyInstance - getStructure: Response received", response);
|
|
3055
|
+
return response;
|
|
2694
3056
|
} catch (error) {
|
|
2695
|
-
console.error("
|
|
2696
|
-
|
|
2697
|
-
return null;
|
|
3057
|
+
console.error("\u{1F4E1} useCrudifyInstance - getStructure: Error", error);
|
|
3058
|
+
throw error;
|
|
2698
3059
|
}
|
|
2699
|
-
}
|
|
2700
|
-
|
|
2701
|
-
|
|
2702
|
-
|
|
2703
|
-
static clearTokens() {
|
|
2704
|
-
const storage = _TokenStorage.getStorage();
|
|
2705
|
-
if (!storage) return;
|
|
3060
|
+
}, [ensureReady, ensureTokenSync]);
|
|
3061
|
+
const getStructurePublic = useCallback4(async (options) => {
|
|
3062
|
+
console.log("\u{1F4E1} useCrudifyInstance - getStructurePublic: Starting");
|
|
3063
|
+
await ensureReady("getStructurePublic");
|
|
2706
3064
|
try {
|
|
2707
|
-
|
|
2708
|
-
|
|
3065
|
+
console.log("\u{1F4E1} useCrudifyInstance - getStructurePublic: Calling crudify.getStructurePublic");
|
|
3066
|
+
const response = await crudify5.getStructurePublic(options);
|
|
3067
|
+
console.log("\u{1F4E1} useCrudifyInstance - getStructurePublic: Response received", response);
|
|
3068
|
+
return response;
|
|
2709
3069
|
} catch (error) {
|
|
2710
|
-
console.error("
|
|
3070
|
+
console.error("\u{1F4E1} useCrudifyInstance - getStructurePublic: Error", error);
|
|
3071
|
+
throw error;
|
|
2711
3072
|
}
|
|
2712
|
-
}
|
|
2713
|
-
|
|
2714
|
-
|
|
2715
|
-
|
|
2716
|
-
|
|
2717
|
-
|
|
2718
|
-
|
|
2719
|
-
}
|
|
2720
|
-
/**
|
|
2721
|
-
* Obtener información de expiración
|
|
2722
|
-
*/
|
|
2723
|
-
static getExpirationInfo() {
|
|
2724
|
-
const tokens = _TokenStorage.getTokens();
|
|
2725
|
-
if (!tokens) return null;
|
|
2726
|
-
const now = Date.now();
|
|
2727
|
-
return {
|
|
2728
|
-
accessExpired: now >= tokens.expiresAt,
|
|
2729
|
-
refreshExpired: now >= tokens.refreshExpiresAt,
|
|
2730
|
-
accessExpiresIn: Math.max(0, tokens.expiresAt - now),
|
|
2731
|
-
refreshExpiresIn: Math.max(0, tokens.refreshExpiresAt - now)
|
|
2732
|
-
};
|
|
2733
|
-
}
|
|
2734
|
-
/**
|
|
2735
|
-
* Actualizar solo el access token (después de refresh)
|
|
2736
|
-
*/
|
|
2737
|
-
static updateAccessToken(newAccessToken, newExpiresAt) {
|
|
2738
|
-
const existingTokens = _TokenStorage.getTokens();
|
|
2739
|
-
if (!existingTokens) {
|
|
2740
|
-
console.warn("Crudify: Cannot update access token, no existing tokens found");
|
|
2741
|
-
return;
|
|
3073
|
+
}, [ensureReady]);
|
|
3074
|
+
const readItems = useCallback4(async (moduleKey, filter, options) => {
|
|
3075
|
+
await ensureReady("readItems");
|
|
3076
|
+
await ensureTokenSync();
|
|
3077
|
+
if (moduleKey === "__test_connection__") {
|
|
3078
|
+
console.error("\u{1F6A8} FOUND TEST CONNECTION CALL in useCrudifyInstance! Stack trace:");
|
|
3079
|
+
console.error(new Error().stack);
|
|
2742
3080
|
}
|
|
2743
|
-
|
|
2744
|
-
|
|
2745
|
-
|
|
2746
|
-
|
|
2747
|
-
|
|
2748
|
-
|
|
2749
|
-
};
|
|
2750
|
-
_TokenStorage.TOKEN_KEY = "crudify_tokens";
|
|
2751
|
-
_TokenStorage.ENCRYPTION_KEY = "crudify_secure_key_v1";
|
|
2752
|
-
_TokenStorage.storageType = "localStorage";
|
|
2753
|
-
var TokenStorage = _TokenStorage;
|
|
2754
|
-
|
|
2755
|
-
// src/core/SessionManager.ts
|
|
2756
|
-
var SessionManager = class _SessionManager {
|
|
2757
|
-
constructor() {
|
|
2758
|
-
this.config = {};
|
|
2759
|
-
this.initialized = false;
|
|
2760
|
-
}
|
|
2761
|
-
static getInstance() {
|
|
2762
|
-
if (!_SessionManager.instance) {
|
|
2763
|
-
_SessionManager.instance = new _SessionManager();
|
|
3081
|
+
try {
|
|
3082
|
+
const response = await crudify5.readItems(moduleKey, filter || {}, options);
|
|
3083
|
+
return response;
|
|
3084
|
+
} catch (error) {
|
|
3085
|
+
console.error("\u{1F4CA} useCrudifyInstance - readItems error:", error);
|
|
3086
|
+
throw error;
|
|
2764
3087
|
}
|
|
2765
|
-
|
|
2766
|
-
|
|
2767
|
-
|
|
2768
|
-
|
|
2769
|
-
|
|
2770
|
-
|
|
2771
|
-
|
|
2772
|
-
console.
|
|
2773
|
-
|
|
3088
|
+
}, [ensureReady, ensureTokenSync]);
|
|
3089
|
+
const readItem = useCallback4(async (moduleKey, filter, options) => {
|
|
3090
|
+
await ensureReady("readItem");
|
|
3091
|
+
try {
|
|
3092
|
+
const response = await crudify5.readItem(moduleKey, filter, options);
|
|
3093
|
+
return response;
|
|
3094
|
+
} catch (error) {
|
|
3095
|
+
console.error("\u{1F4CA} useCrudifyInstance - readItem error:", error);
|
|
3096
|
+
throw error;
|
|
2774
3097
|
}
|
|
2775
|
-
|
|
2776
|
-
|
|
2777
|
-
|
|
2778
|
-
|
|
2779
|
-
|
|
2780
|
-
|
|
2781
|
-
|
|
2782
|
-
|
|
3098
|
+
}, [ensureReady]);
|
|
3099
|
+
const createItem = useCallback4(async (moduleKey, data, options) => {
|
|
3100
|
+
await ensureReady("createItem");
|
|
3101
|
+
try {
|
|
3102
|
+
const response = await crudify5.createItem(moduleKey, data, options);
|
|
3103
|
+
return response;
|
|
3104
|
+
} catch (error) {
|
|
3105
|
+
console.error("\u{1F4CA} useCrudifyInstance - createItem error:", error);
|
|
3106
|
+
throw error;
|
|
2783
3107
|
}
|
|
2784
|
-
|
|
2785
|
-
|
|
3108
|
+
}, [ensureReady]);
|
|
3109
|
+
const updateItem = useCallback4(async (moduleKey, data, options) => {
|
|
3110
|
+
await ensureReady("updateItem");
|
|
3111
|
+
try {
|
|
3112
|
+
const response = await crudify5.updateItem(moduleKey, data, options);
|
|
3113
|
+
return response;
|
|
3114
|
+
} catch (error) {
|
|
3115
|
+
console.error("\u{1F4CA} useCrudifyInstance - updateItem error:", error);
|
|
3116
|
+
throw error;
|
|
2786
3117
|
}
|
|
2787
|
-
|
|
2788
|
-
|
|
2789
|
-
|
|
2790
|
-
/**
|
|
2791
|
-
* Login con persistencia automática
|
|
2792
|
-
*/
|
|
2793
|
-
async login(email, password) {
|
|
3118
|
+
}, [ensureReady]);
|
|
3119
|
+
const deleteItem = useCallback4(async (moduleKey, id, options) => {
|
|
3120
|
+
await ensureReady("deleteItem");
|
|
2794
3121
|
try {
|
|
2795
|
-
|
|
2796
|
-
|
|
2797
|
-
if (!response.success) {
|
|
2798
|
-
this.log("Login failed:", response.errors);
|
|
2799
|
-
return {
|
|
2800
|
-
success: false,
|
|
2801
|
-
error: this.formatError(response.errors)
|
|
2802
|
-
};
|
|
2803
|
-
}
|
|
2804
|
-
const tokens = {
|
|
2805
|
-
accessToken: response.data.token,
|
|
2806
|
-
refreshToken: response.data.refreshToken,
|
|
2807
|
-
expiresAt: response.data.expiresAt,
|
|
2808
|
-
refreshExpiresAt: response.data.refreshExpiresAt
|
|
2809
|
-
};
|
|
2810
|
-
TokenStorage.saveTokens(tokens);
|
|
2811
|
-
this.log("Login successful, tokens saved");
|
|
2812
|
-
this.config.onLoginSuccess?.(tokens);
|
|
2813
|
-
return {
|
|
2814
|
-
success: true,
|
|
2815
|
-
tokens
|
|
2816
|
-
};
|
|
3122
|
+
const response = await crudify5.deleteItem(moduleKey, id, options);
|
|
3123
|
+
return response;
|
|
2817
3124
|
} catch (error) {
|
|
2818
|
-
|
|
2819
|
-
|
|
2820
|
-
success: false,
|
|
2821
|
-
error: error instanceof Error ? error.message : "Unknown error"
|
|
2822
|
-
};
|
|
3125
|
+
console.error("\u{1F4CA} useCrudifyInstance - deleteItem error:", error);
|
|
3126
|
+
throw error;
|
|
2823
3127
|
}
|
|
2824
|
-
}
|
|
2825
|
-
|
|
2826
|
-
|
|
2827
|
-
*/
|
|
2828
|
-
async logout() {
|
|
3128
|
+
}, [ensureReady]);
|
|
3129
|
+
const transaction = useCallback4(async (operations, options) => {
|
|
3130
|
+
await ensureReady("transaction");
|
|
2829
3131
|
try {
|
|
2830
|
-
|
|
2831
|
-
|
|
2832
|
-
TokenStorage.clearTokens();
|
|
2833
|
-
this.log("Logout successful");
|
|
2834
|
-
this.config.onLogout?.();
|
|
3132
|
+
const response = await crudify5.transaction(operations, options);
|
|
3133
|
+
return response;
|
|
2835
3134
|
} catch (error) {
|
|
2836
|
-
|
|
2837
|
-
|
|
3135
|
+
console.error("\u{1F4CA} useCrudifyInstance - transaction error:", error);
|
|
3136
|
+
throw error;
|
|
2838
3137
|
}
|
|
2839
|
-
}
|
|
2840
|
-
|
|
2841
|
-
|
|
2842
|
-
*/
|
|
2843
|
-
async restoreSession() {
|
|
3138
|
+
}, [ensureReady]);
|
|
3139
|
+
const login = useCallback4(async (email, password) => {
|
|
3140
|
+
await ensureReady("login");
|
|
2844
3141
|
try {
|
|
2845
|
-
|
|
2846
|
-
|
|
2847
|
-
if (!savedTokens) {
|
|
2848
|
-
this.log("No valid tokens found in storage");
|
|
2849
|
-
return false;
|
|
2850
|
-
}
|
|
2851
|
-
crudify6.setTokens({
|
|
2852
|
-
accessToken: savedTokens.accessToken,
|
|
2853
|
-
refreshToken: savedTokens.refreshToken,
|
|
2854
|
-
expiresAt: savedTokens.expiresAt,
|
|
2855
|
-
refreshExpiresAt: savedTokens.refreshExpiresAt
|
|
2856
|
-
});
|
|
2857
|
-
this.log("Session restored successfully");
|
|
2858
|
-
this.config.onSessionRestored?.(savedTokens);
|
|
2859
|
-
return true;
|
|
3142
|
+
const response = await crudify5.login(email, password);
|
|
3143
|
+
return response;
|
|
2860
3144
|
} catch (error) {
|
|
2861
|
-
|
|
2862
|
-
|
|
2863
|
-
|
|
3145
|
+
console.error("\u{1F4CA} useCrudifyInstance - login error:", error);
|
|
3146
|
+
throw error;
|
|
3147
|
+
}
|
|
3148
|
+
}, [ensureReady]);
|
|
3149
|
+
return {
|
|
3150
|
+
// Core operations
|
|
3151
|
+
getStructure,
|
|
3152
|
+
getStructurePublic,
|
|
3153
|
+
readItems,
|
|
3154
|
+
readItem,
|
|
3155
|
+
createItem,
|
|
3156
|
+
updateItem,
|
|
3157
|
+
deleteItem,
|
|
3158
|
+
transaction,
|
|
3159
|
+
login,
|
|
3160
|
+
// State information
|
|
3161
|
+
isReady,
|
|
3162
|
+
isInitialized,
|
|
3163
|
+
isInitializing,
|
|
3164
|
+
initializationError,
|
|
3165
|
+
// Utility methods
|
|
3166
|
+
waitForReady
|
|
3167
|
+
};
|
|
3168
|
+
};
|
|
3169
|
+
var getCrudifyInstanceAsync = async () => {
|
|
3170
|
+
console.log("\u{1F504} getCrudifyInstanceAsync - Starting");
|
|
3171
|
+
const { crudifyInitializer: crudifyInitializer2 } = await import("./CrudifyDataProvider-5GXGNQKI.mjs");
|
|
3172
|
+
console.log("\u{1F504} getCrudifyInstanceAsync - Checking if ready");
|
|
3173
|
+
console.log(" - crudifyInitializer.isReady():", crudifyInitializer2.isReady());
|
|
3174
|
+
console.log(" - crudifyInitializer.getStatus():", crudifyInitializer2.getStatus());
|
|
3175
|
+
if (!crudifyInitializer2.isReady()) {
|
|
3176
|
+
console.log("\u{1F504} getCrudifyInstanceAsync - Waiting for crudify initialization...");
|
|
3177
|
+
await crudifyInitializer2.waitForInitialization();
|
|
3178
|
+
if (!crudifyInitializer2.isReady()) {
|
|
3179
|
+
const error = crudifyInitializer2.getError();
|
|
3180
|
+
throw new Error(`Crudify initialization failed: ${error || "Unknown error after waiting"}`);
|
|
2864
3181
|
}
|
|
3182
|
+
console.log("\u2705 getCrudifyInstanceAsync - Crudify is ready after waiting");
|
|
3183
|
+
} else {
|
|
3184
|
+
console.log("\u2705 getCrudifyInstanceAsync - Already ready, no wait needed");
|
|
2865
3185
|
}
|
|
2866
|
-
|
|
2867
|
-
|
|
2868
|
-
|
|
2869
|
-
|
|
2870
|
-
|
|
3186
|
+
console.log("\u2705 getCrudifyInstanceAsync - Returning crudify instance");
|
|
3187
|
+
return crudify5;
|
|
3188
|
+
};
|
|
3189
|
+
var getCrudifyInstanceSync = async () => {
|
|
3190
|
+
const { crudifyInitializer: crudifyInitializer2 } = await import("./CrudifyDataProvider-5GXGNQKI.mjs");
|
|
3191
|
+
if (!crudifyInitializer2.isReady()) {
|
|
3192
|
+
throw new Error("Crudify not ready. Use getCrudifyInstanceAsync() or call this from within a React component using useCrudifyInstance()");
|
|
2871
3193
|
}
|
|
3194
|
+
return crudify5;
|
|
3195
|
+
};
|
|
3196
|
+
|
|
3197
|
+
// src/core/SessionManager.ts
|
|
3198
|
+
import crudify6 from "@nocios/crudify-browser";
|
|
3199
|
+
|
|
3200
|
+
// src/utils/tokenStorage.ts
|
|
3201
|
+
import CryptoJS from "crypto-js";
|
|
3202
|
+
var _TokenStorage = class _TokenStorage {
|
|
2872
3203
|
/**
|
|
2873
|
-
*
|
|
3204
|
+
* Configurar tipo de almacenamiento
|
|
2874
3205
|
*/
|
|
2875
|
-
|
|
2876
|
-
|
|
2877
|
-
const storageInfo = TokenStorage.getExpirationInfo();
|
|
2878
|
-
return {
|
|
2879
|
-
isLoggedIn: this.isAuthenticated(),
|
|
2880
|
-
crudifyTokens,
|
|
2881
|
-
storageInfo,
|
|
2882
|
-
hasValidTokens: TokenStorage.hasValidTokens()
|
|
2883
|
-
};
|
|
3206
|
+
static setStorageType(type) {
|
|
3207
|
+
_TokenStorage.storageType = type;
|
|
2884
3208
|
}
|
|
2885
3209
|
/**
|
|
2886
|
-
*
|
|
3210
|
+
* Verificar si el storage está disponible
|
|
2887
3211
|
*/
|
|
2888
|
-
|
|
3212
|
+
static isStorageAvailable(type) {
|
|
2889
3213
|
try {
|
|
2890
|
-
|
|
2891
|
-
const
|
|
2892
|
-
|
|
2893
|
-
|
|
2894
|
-
TokenStorage.clearTokens();
|
|
2895
|
-
this.config.onSessionExpired?.();
|
|
2896
|
-
return false;
|
|
2897
|
-
}
|
|
2898
|
-
const newTokens = {
|
|
2899
|
-
accessToken: response.data.token,
|
|
2900
|
-
refreshToken: response.data.refreshToken,
|
|
2901
|
-
expiresAt: response.data.expiresAt,
|
|
2902
|
-
refreshExpiresAt: response.data.refreshExpiresAt
|
|
2903
|
-
};
|
|
2904
|
-
TokenStorage.saveTokens(newTokens);
|
|
2905
|
-
this.log("Tokens refreshed and saved successfully");
|
|
3214
|
+
const storage = window[type];
|
|
3215
|
+
const testKey = "__storage_test__";
|
|
3216
|
+
storage.setItem(testKey, "test");
|
|
3217
|
+
storage.removeItem(testKey);
|
|
2906
3218
|
return true;
|
|
2907
|
-
} catch
|
|
2908
|
-
this.log("Token refresh error:", error);
|
|
2909
|
-
TokenStorage.clearTokens();
|
|
2910
|
-
this.config.onSessionExpired?.();
|
|
3219
|
+
} catch {
|
|
2911
3220
|
return false;
|
|
2912
3221
|
}
|
|
2913
3222
|
}
|
|
2914
3223
|
/**
|
|
2915
|
-
*
|
|
3224
|
+
* Obtener instancia de storage
|
|
2916
3225
|
*/
|
|
2917
|
-
|
|
2918
|
-
|
|
2919
|
-
|
|
2920
|
-
|
|
2921
|
-
|
|
2922
|
-
|
|
2923
|
-
|
|
2924
|
-
this.log("Auth error detected, attempting token refresh...");
|
|
2925
|
-
const refreshSuccess = await this.refreshTokens();
|
|
2926
|
-
if (!refreshSuccess) {
|
|
2927
|
-
this.log("Session expired, triggering callback");
|
|
2928
|
-
this.config.onSessionExpired?.();
|
|
2929
|
-
}
|
|
2930
|
-
}
|
|
2931
|
-
}
|
|
2932
|
-
return response;
|
|
2933
|
-
});
|
|
2934
|
-
this.log("Response interceptor configured");
|
|
3226
|
+
static getStorage() {
|
|
3227
|
+
if (_TokenStorage.storageType === "none") return null;
|
|
3228
|
+
if (!_TokenStorage.isStorageAvailable(_TokenStorage.storageType)) {
|
|
3229
|
+
console.warn(`Crudify: ${_TokenStorage.storageType} not available, tokens won't persist`);
|
|
3230
|
+
return null;
|
|
3231
|
+
}
|
|
3232
|
+
return window[_TokenStorage.storageType];
|
|
2935
3233
|
}
|
|
2936
3234
|
/**
|
|
2937
|
-
*
|
|
3235
|
+
* Encriptar datos sensibles
|
|
2938
3236
|
*/
|
|
2939
|
-
|
|
2940
|
-
|
|
2941
|
-
|
|
2942
|
-
|
|
2943
|
-
|
|
2944
|
-
|
|
2945
|
-
log(message, ...args) {
|
|
2946
|
-
if (this.config.enableLogging) {
|
|
2947
|
-
console.log(`[SessionManager] ${message}`, ...args);
|
|
2948
|
-
}
|
|
2949
|
-
}
|
|
2950
|
-
formatError(errors) {
|
|
2951
|
-
if (!errors) return "Unknown error";
|
|
2952
|
-
if (typeof errors === "string") return errors;
|
|
2953
|
-
if (typeof errors === "object") {
|
|
2954
|
-
const errorMessages = Object.values(errors).flat();
|
|
2955
|
-
return errorMessages.join(", ");
|
|
3237
|
+
static encrypt(data) {
|
|
3238
|
+
try {
|
|
3239
|
+
return CryptoJS.AES.encrypt(data, _TokenStorage.ENCRYPTION_KEY).toString();
|
|
3240
|
+
} catch (error) {
|
|
3241
|
+
console.error("Crudify: Encryption failed", error);
|
|
3242
|
+
return data;
|
|
2956
3243
|
}
|
|
2957
|
-
return "Authentication failed";
|
|
2958
3244
|
}
|
|
2959
|
-
|
|
2960
|
-
|
|
2961
|
-
|
|
2962
|
-
|
|
2963
|
-
function useSession(options = {}) {
|
|
2964
|
-
const [state, setState] = useState9({
|
|
2965
|
-
isAuthenticated: false,
|
|
2966
|
-
isLoading: true,
|
|
2967
|
-
isInitialized: false,
|
|
2968
|
-
tokens: null,
|
|
2969
|
-
error: null
|
|
2970
|
-
});
|
|
2971
|
-
const sessionManager = SessionManager.getInstance();
|
|
2972
|
-
const initialize = useCallback5(async () => {
|
|
3245
|
+
/**
|
|
3246
|
+
* Desencriptar datos
|
|
3247
|
+
*/
|
|
3248
|
+
static decrypt(encryptedData) {
|
|
2973
3249
|
try {
|
|
2974
|
-
|
|
2975
|
-
const
|
|
2976
|
-
|
|
2977
|
-
enableLogging: options.enableLogging ?? false,
|
|
2978
|
-
onSessionExpired: () => {
|
|
2979
|
-
setState((prev) => ({
|
|
2980
|
-
...prev,
|
|
2981
|
-
isAuthenticated: false,
|
|
2982
|
-
tokens: null,
|
|
2983
|
-
error: "Session expired"
|
|
2984
|
-
}));
|
|
2985
|
-
options.onSessionExpired?.();
|
|
2986
|
-
},
|
|
2987
|
-
onSessionRestored: (tokens) => {
|
|
2988
|
-
setState((prev) => ({
|
|
2989
|
-
...prev,
|
|
2990
|
-
isAuthenticated: true,
|
|
2991
|
-
tokens,
|
|
2992
|
-
error: null
|
|
2993
|
-
}));
|
|
2994
|
-
options.onSessionRestored?.(tokens);
|
|
2995
|
-
},
|
|
2996
|
-
onLoginSuccess: (tokens) => {
|
|
2997
|
-
setState((prev) => ({
|
|
2998
|
-
...prev,
|
|
2999
|
-
isAuthenticated: true,
|
|
3000
|
-
tokens,
|
|
3001
|
-
error: null
|
|
3002
|
-
}));
|
|
3003
|
-
},
|
|
3004
|
-
onLogout: () => {
|
|
3005
|
-
setState((prev) => ({
|
|
3006
|
-
...prev,
|
|
3007
|
-
isAuthenticated: false,
|
|
3008
|
-
tokens: null,
|
|
3009
|
-
error: null
|
|
3010
|
-
}));
|
|
3011
|
-
}
|
|
3012
|
-
};
|
|
3013
|
-
await sessionManager.initialize(config);
|
|
3014
|
-
sessionManager.setupResponseInterceptor();
|
|
3015
|
-
const isAuth = sessionManager.isAuthenticated();
|
|
3016
|
-
const tokenInfo = sessionManager.getTokenInfo();
|
|
3017
|
-
setState((prev) => ({
|
|
3018
|
-
...prev,
|
|
3019
|
-
isAuthenticated: isAuth,
|
|
3020
|
-
isInitialized: true,
|
|
3021
|
-
isLoading: false,
|
|
3022
|
-
tokens: tokenInfo.crudifyTokens.accessToken ? {
|
|
3023
|
-
accessToken: tokenInfo.crudifyTokens.accessToken,
|
|
3024
|
-
refreshToken: tokenInfo.crudifyTokens.refreshToken,
|
|
3025
|
-
expiresAt: tokenInfo.crudifyTokens.expiresAt,
|
|
3026
|
-
refreshExpiresAt: tokenInfo.crudifyTokens.refreshExpiresAt
|
|
3027
|
-
} : null
|
|
3028
|
-
}));
|
|
3250
|
+
const bytes = CryptoJS.AES.decrypt(encryptedData, _TokenStorage.ENCRYPTION_KEY);
|
|
3251
|
+
const decrypted = bytes.toString(CryptoJS.enc.Utf8);
|
|
3252
|
+
return decrypted || encryptedData;
|
|
3029
3253
|
} catch (error) {
|
|
3030
|
-
|
|
3031
|
-
|
|
3032
|
-
isLoading: false,
|
|
3033
|
-
isInitialized: true,
|
|
3034
|
-
error: error instanceof Error ? error.message : "Initialization failed"
|
|
3035
|
-
}));
|
|
3254
|
+
console.error("Crudify: Decryption failed", error);
|
|
3255
|
+
return encryptedData;
|
|
3036
3256
|
}
|
|
3037
|
-
}
|
|
3038
|
-
|
|
3039
|
-
|
|
3257
|
+
}
|
|
3258
|
+
/**
|
|
3259
|
+
* Guardar tokens de forma segura
|
|
3260
|
+
*/
|
|
3261
|
+
static saveTokens(tokens) {
|
|
3262
|
+
const storage = _TokenStorage.getStorage();
|
|
3263
|
+
if (!storage) return;
|
|
3040
3264
|
try {
|
|
3041
|
-
const
|
|
3042
|
-
|
|
3043
|
-
|
|
3044
|
-
|
|
3045
|
-
|
|
3046
|
-
|
|
3047
|
-
isLoading: false,
|
|
3048
|
-
error: null
|
|
3049
|
-
}));
|
|
3050
|
-
} else {
|
|
3051
|
-
setState((prev) => ({
|
|
3052
|
-
...prev,
|
|
3053
|
-
isAuthenticated: false,
|
|
3054
|
-
tokens: null,
|
|
3055
|
-
isLoading: false,
|
|
3056
|
-
error: result.error || "Login failed"
|
|
3057
|
-
}));
|
|
3058
|
-
}
|
|
3059
|
-
return result;
|
|
3060
|
-
} catch (error) {
|
|
3061
|
-
const errorMsg = error instanceof Error ? error.message : "Login failed";
|
|
3062
|
-
setState((prev) => ({
|
|
3063
|
-
...prev,
|
|
3064
|
-
isAuthenticated: false,
|
|
3065
|
-
tokens: null,
|
|
3066
|
-
isLoading: false,
|
|
3067
|
-
error: errorMsg
|
|
3068
|
-
}));
|
|
3069
|
-
return {
|
|
3070
|
-
success: false,
|
|
3071
|
-
error: errorMsg
|
|
3265
|
+
const tokenData = {
|
|
3266
|
+
accessToken: tokens.accessToken,
|
|
3267
|
+
refreshToken: tokens.refreshToken,
|
|
3268
|
+
expiresAt: tokens.expiresAt,
|
|
3269
|
+
refreshExpiresAt: tokens.refreshExpiresAt,
|
|
3270
|
+
savedAt: Date.now()
|
|
3072
3271
|
};
|
|
3073
|
-
|
|
3074
|
-
|
|
3075
|
-
|
|
3076
|
-
setState((prev) => ({ ...prev, isLoading: true }));
|
|
3077
|
-
try {
|
|
3078
|
-
await sessionManager.logout();
|
|
3079
|
-
setState((prev) => ({
|
|
3080
|
-
...prev,
|
|
3081
|
-
isAuthenticated: false,
|
|
3082
|
-
tokens: null,
|
|
3083
|
-
isLoading: false,
|
|
3084
|
-
error: null
|
|
3085
|
-
}));
|
|
3272
|
+
const encrypted = _TokenStorage.encrypt(JSON.stringify(tokenData));
|
|
3273
|
+
storage.setItem(_TokenStorage.TOKEN_KEY, encrypted);
|
|
3274
|
+
console.debug("Crudify: Tokens saved successfully");
|
|
3086
3275
|
} catch (error) {
|
|
3087
|
-
|
|
3088
|
-
...prev,
|
|
3089
|
-
isAuthenticated: false,
|
|
3090
|
-
tokens: null,
|
|
3091
|
-
isLoading: false,
|
|
3092
|
-
error: error instanceof Error ? error.message : "Logout error"
|
|
3093
|
-
}));
|
|
3276
|
+
console.error("Crudify: Failed to save tokens", error);
|
|
3094
3277
|
}
|
|
3095
|
-
}
|
|
3096
|
-
|
|
3278
|
+
}
|
|
3279
|
+
/**
|
|
3280
|
+
* Obtener tokens guardados
|
|
3281
|
+
*/
|
|
3282
|
+
static getTokens() {
|
|
3283
|
+
const storage = _TokenStorage.getStorage();
|
|
3284
|
+
if (!storage) return null;
|
|
3097
3285
|
try {
|
|
3098
|
-
const
|
|
3099
|
-
if (
|
|
3100
|
-
|
|
3101
|
-
|
|
3102
|
-
|
|
3103
|
-
|
|
3104
|
-
|
|
3105
|
-
|
|
3106
|
-
expiresAt: tokenInfo.crudifyTokens.expiresAt,
|
|
3107
|
-
refreshExpiresAt: tokenInfo.crudifyTokens.refreshExpiresAt
|
|
3108
|
-
} : null,
|
|
3109
|
-
error: null
|
|
3110
|
-
}));
|
|
3111
|
-
} else {
|
|
3112
|
-
setState((prev) => ({
|
|
3113
|
-
...prev,
|
|
3114
|
-
isAuthenticated: false,
|
|
3115
|
-
tokens: null,
|
|
3116
|
-
error: "Token refresh failed"
|
|
3117
|
-
}));
|
|
3286
|
+
const encrypted = storage.getItem(_TokenStorage.TOKEN_KEY);
|
|
3287
|
+
if (!encrypted) return null;
|
|
3288
|
+
const decrypted = _TokenStorage.decrypt(encrypted);
|
|
3289
|
+
const tokenData = JSON.parse(decrypted);
|
|
3290
|
+
if (!tokenData.accessToken || !tokenData.refreshToken || !tokenData.expiresAt || !tokenData.refreshExpiresAt) {
|
|
3291
|
+
console.warn("Crudify: Incomplete token data found, clearing storage");
|
|
3292
|
+
_TokenStorage.clearTokens();
|
|
3293
|
+
return null;
|
|
3118
3294
|
}
|
|
3119
|
-
|
|
3295
|
+
if (Date.now() >= tokenData.refreshExpiresAt) {
|
|
3296
|
+
console.info("Crudify: Refresh token expired, clearing storage");
|
|
3297
|
+
_TokenStorage.clearTokens();
|
|
3298
|
+
return null;
|
|
3299
|
+
}
|
|
3300
|
+
return {
|
|
3301
|
+
accessToken: tokenData.accessToken,
|
|
3302
|
+
refreshToken: tokenData.refreshToken,
|
|
3303
|
+
expiresAt: tokenData.expiresAt,
|
|
3304
|
+
refreshExpiresAt: tokenData.refreshExpiresAt
|
|
3305
|
+
};
|
|
3120
3306
|
} catch (error) {
|
|
3121
|
-
|
|
3122
|
-
|
|
3123
|
-
isAuthenticated: false,
|
|
3124
|
-
tokens: null,
|
|
3125
|
-
error: error instanceof Error ? error.message : "Token refresh failed"
|
|
3126
|
-
}));
|
|
3127
|
-
return false;
|
|
3128
|
-
}
|
|
3129
|
-
}, [sessionManager]);
|
|
3130
|
-
const clearError = useCallback5(() => {
|
|
3131
|
-
setState((prev) => ({ ...prev, error: null }));
|
|
3132
|
-
}, []);
|
|
3133
|
-
const getTokenInfo = useCallback5(() => {
|
|
3134
|
-
return sessionManager.getTokenInfo();
|
|
3135
|
-
}, [sessionManager]);
|
|
3136
|
-
useEffect9(() => {
|
|
3137
|
-
initialize();
|
|
3138
|
-
}, [initialize]);
|
|
3139
|
-
return {
|
|
3140
|
-
// Estado
|
|
3141
|
-
...state,
|
|
3142
|
-
// Acciones
|
|
3143
|
-
login,
|
|
3144
|
-
logout,
|
|
3145
|
-
refreshTokens,
|
|
3146
|
-
clearError,
|
|
3147
|
-
getTokenInfo,
|
|
3148
|
-
// Utilidades
|
|
3149
|
-
isExpiringSoon: state.tokens ? state.tokens.expiresAt - Date.now() < 5 * 60 * 1e3 : false,
|
|
3150
|
-
// 5 minutos
|
|
3151
|
-
expiresIn: state.tokens ? Math.max(0, state.tokens.expiresAt - Date.now()) : 0,
|
|
3152
|
-
refreshExpiresIn: state.tokens ? Math.max(0, state.tokens.refreshExpiresAt - Date.now()) : 0
|
|
3153
|
-
};
|
|
3154
|
-
}
|
|
3155
|
-
|
|
3156
|
-
// src/providers/SessionProvider.tsx
|
|
3157
|
-
import { createContext as createContext4, useContext as useContext4, useMemo as useMemo3 } from "react";
|
|
3158
|
-
import { Fragment as Fragment7, jsx as jsx11, jsxs as jsxs8 } from "react/jsx-runtime";
|
|
3159
|
-
var SessionContext = createContext4(void 0);
|
|
3160
|
-
function SessionProvider({ children, options = {} }) {
|
|
3161
|
-
const sessionHook = useSession(options);
|
|
3162
|
-
const sessionData = useMemo3(() => {
|
|
3163
|
-
if (!sessionHook.tokens?.accessToken || !sessionHook.isAuthenticated) {
|
|
3307
|
+
console.error("Crudify: Failed to retrieve tokens", error);
|
|
3308
|
+
_TokenStorage.clearTokens();
|
|
3164
3309
|
return null;
|
|
3165
3310
|
}
|
|
3311
|
+
}
|
|
3312
|
+
/**
|
|
3313
|
+
* Limpiar tokens almacenados
|
|
3314
|
+
*/
|
|
3315
|
+
static clearTokens() {
|
|
3316
|
+
const storage = _TokenStorage.getStorage();
|
|
3317
|
+
if (!storage) return;
|
|
3166
3318
|
try {
|
|
3167
|
-
|
|
3168
|
-
|
|
3169
|
-
const result = {
|
|
3170
|
-
_id: decoded.sub,
|
|
3171
|
-
email: decoded.email,
|
|
3172
|
-
subscriberKey: decoded.subscriber
|
|
3173
|
-
};
|
|
3174
|
-
Object.keys(decoded).forEach((key) => {
|
|
3175
|
-
if (!["sub", "email", "subscriber"].includes(key)) {
|
|
3176
|
-
result[key] = decoded[key];
|
|
3177
|
-
}
|
|
3178
|
-
});
|
|
3179
|
-
return result;
|
|
3180
|
-
}
|
|
3319
|
+
storage.removeItem(_TokenStorage.TOKEN_KEY);
|
|
3320
|
+
console.debug("Crudify: Tokens cleared from storage");
|
|
3181
3321
|
} catch (error) {
|
|
3182
|
-
console.error("
|
|
3322
|
+
console.error("Crudify: Failed to clear tokens", error);
|
|
3183
3323
|
}
|
|
3184
|
-
return null;
|
|
3185
|
-
}, [sessionHook.tokens?.accessToken, sessionHook.isAuthenticated]);
|
|
3186
|
-
const contextValue = {
|
|
3187
|
-
...sessionHook,
|
|
3188
|
-
sessionData
|
|
3189
|
-
};
|
|
3190
|
-
return /* @__PURE__ */ jsx11(SessionContext.Provider, { value: contextValue, children });
|
|
3191
|
-
}
|
|
3192
|
-
function useSessionContext() {
|
|
3193
|
-
const context = useContext4(SessionContext);
|
|
3194
|
-
if (context === void 0) {
|
|
3195
|
-
throw new Error("useSessionContext must be used within a SessionProvider");
|
|
3196
|
-
}
|
|
3197
|
-
return context;
|
|
3198
|
-
}
|
|
3199
|
-
function ProtectedRoute({
|
|
3200
|
-
children,
|
|
3201
|
-
fallback = /* @__PURE__ */ jsx11("div", { children: "Please log in to access this content" }),
|
|
3202
|
-
redirectTo
|
|
3203
|
-
}) {
|
|
3204
|
-
const { isAuthenticated, isLoading, isInitialized } = useSessionContext();
|
|
3205
|
-
if (!isInitialized || isLoading) {
|
|
3206
|
-
return /* @__PURE__ */ jsx11("div", { children: "Loading..." });
|
|
3207
3324
|
}
|
|
3208
|
-
|
|
3209
|
-
|
|
3210
|
-
|
|
3211
|
-
|
|
3212
|
-
|
|
3213
|
-
return
|
|
3325
|
+
/**
|
|
3326
|
+
* Verificar si hay tokens válidos guardados
|
|
3327
|
+
*/
|
|
3328
|
+
static hasValidTokens() {
|
|
3329
|
+
const tokens = _TokenStorage.getTokens();
|
|
3330
|
+
return tokens !== null;
|
|
3214
3331
|
}
|
|
3215
|
-
|
|
3216
|
-
|
|
3217
|
-
|
|
3218
|
-
|
|
3219
|
-
|
|
3220
|
-
|
|
3332
|
+
/**
|
|
3333
|
+
* Obtener información de expiración
|
|
3334
|
+
*/
|
|
3335
|
+
static getExpirationInfo() {
|
|
3336
|
+
const tokens = _TokenStorage.getTokens();
|
|
3337
|
+
if (!tokens) return null;
|
|
3338
|
+
const now = Date.now();
|
|
3339
|
+
return {
|
|
3340
|
+
accessExpired: now >= tokens.expiresAt,
|
|
3341
|
+
refreshExpired: now >= tokens.refreshExpiresAt,
|
|
3342
|
+
accessExpiresIn: Math.max(0, tokens.expiresAt - now),
|
|
3343
|
+
refreshExpiresIn: Math.max(0, tokens.refreshExpiresAt - now)
|
|
3344
|
+
};
|
|
3221
3345
|
}
|
|
3222
|
-
|
|
3223
|
-
|
|
3224
|
-
|
|
3225
|
-
|
|
3226
|
-
|
|
3227
|
-
|
|
3228
|
-
|
|
3229
|
-
|
|
3230
|
-
|
|
3231
|
-
|
|
3232
|
-
|
|
3233
|
-
|
|
3234
|
-
|
|
3235
|
-
|
|
3236
|
-
|
|
3237
|
-
|
|
3238
|
-
|
|
3239
|
-
|
|
3240
|
-
|
|
3241
|
-
|
|
3242
|
-
|
|
3243
|
-
|
|
3244
|
-
|
|
3245
|
-
|
|
3246
|
-
|
|
3247
|
-
|
|
3248
|
-
|
|
3249
|
-
|
|
3250
|
-
|
|
3251
|
-
|
|
3252
|
-
|
|
3253
|
-
|
|
3254
|
-
|
|
3255
|
-
|
|
3256
|
-
|
|
3257
|
-
|
|
3258
|
-
|
|
3259
|
-
|
|
3260
|
-
|
|
3261
|
-
" ",
|
|
3262
|
-
Math.round(session.expiresIn / 1e3 / 60),
|
|
3263
|
-
" minutes"
|
|
3264
|
-
] }),
|
|
3265
|
-
/* @__PURE__ */ jsxs8("div", { children: [
|
|
3266
|
-
/* @__PURE__ */ jsx11("strong", { children: "Refresh Expires In:" }),
|
|
3267
|
-
" ",
|
|
3268
|
-
Math.round(session.refreshExpiresIn / 1e3 / 60 / 60),
|
|
3269
|
-
" hours"
|
|
3270
|
-
] }),
|
|
3271
|
-
/* @__PURE__ */ jsxs8("div", { children: [
|
|
3272
|
-
/* @__PURE__ */ jsx11("strong", { children: "Expiring Soon:" }),
|
|
3273
|
-
" ",
|
|
3274
|
-
session.isExpiringSoon ? "Yes" : "No"
|
|
3275
|
-
] })
|
|
3276
|
-
] })
|
|
3277
|
-
] });
|
|
3278
|
-
}
|
|
3279
|
-
|
|
3280
|
-
// src/components/LoginComponent.tsx
|
|
3281
|
-
import { useState as useState10 } from "react";
|
|
3282
|
-
import { Button as Button5, TextField as TextField5, Box as Box8, Alert as Alert7, Typography as Typography8, CircularProgress as CircularProgress7 } from "@mui/material";
|
|
3283
|
-
import { jsx as jsx12, jsxs as jsxs9 } from "react/jsx-runtime";
|
|
3284
|
-
function LoginComponent() {
|
|
3285
|
-
const [email, setEmail] = useState10("");
|
|
3286
|
-
const [password, setPassword] = useState10("");
|
|
3287
|
-
const [showForm, setShowForm] = useState10(false);
|
|
3288
|
-
const {
|
|
3289
|
-
isAuthenticated,
|
|
3290
|
-
isLoading,
|
|
3291
|
-
error,
|
|
3292
|
-
login,
|
|
3293
|
-
logout,
|
|
3294
|
-
refreshTokens,
|
|
3295
|
-
clearError,
|
|
3296
|
-
isExpiringSoon,
|
|
3297
|
-
expiresIn
|
|
3298
|
-
} = useSessionContext();
|
|
3299
|
-
const handleLogin = async (e) => {
|
|
3300
|
-
e.preventDefault();
|
|
3301
|
-
if (!email || !password) {
|
|
3346
|
+
/**
|
|
3347
|
+
* Actualizar solo el access token (después de refresh)
|
|
3348
|
+
*/
|
|
3349
|
+
static updateAccessToken(newAccessToken, newExpiresAt) {
|
|
3350
|
+
const existingTokens = _TokenStorage.getTokens();
|
|
3351
|
+
if (!existingTokens) {
|
|
3352
|
+
console.warn("Crudify: Cannot update access token, no existing tokens found");
|
|
3353
|
+
return;
|
|
3354
|
+
}
|
|
3355
|
+
_TokenStorage.saveTokens({
|
|
3356
|
+
...existingTokens,
|
|
3357
|
+
accessToken: newAccessToken,
|
|
3358
|
+
expiresAt: newExpiresAt
|
|
3359
|
+
});
|
|
3360
|
+
}
|
|
3361
|
+
};
|
|
3362
|
+
_TokenStorage.TOKEN_KEY = "crudify_tokens";
|
|
3363
|
+
_TokenStorage.ENCRYPTION_KEY = "crudify_secure_key_v1";
|
|
3364
|
+
_TokenStorage.storageType = "localStorage";
|
|
3365
|
+
var TokenStorage = _TokenStorage;
|
|
3366
|
+
|
|
3367
|
+
// src/core/SessionManager.ts
|
|
3368
|
+
var SessionManager = class _SessionManager {
|
|
3369
|
+
constructor() {
|
|
3370
|
+
this.config = {};
|
|
3371
|
+
this.initialized = false;
|
|
3372
|
+
}
|
|
3373
|
+
static getInstance() {
|
|
3374
|
+
if (!_SessionManager.instance) {
|
|
3375
|
+
_SessionManager.instance = new _SessionManager();
|
|
3376
|
+
}
|
|
3377
|
+
return _SessionManager.instance;
|
|
3378
|
+
}
|
|
3379
|
+
/**
|
|
3380
|
+
* Inicializar el SessionManager
|
|
3381
|
+
*/
|
|
3382
|
+
async initialize(config = {}) {
|
|
3383
|
+
if (this.initialized) {
|
|
3384
|
+
console.warn("SessionManager: Already initialized");
|
|
3302
3385
|
return;
|
|
3303
3386
|
}
|
|
3304
|
-
|
|
3305
|
-
|
|
3306
|
-
|
|
3307
|
-
|
|
3308
|
-
|
|
3387
|
+
this.config = {
|
|
3388
|
+
storageType: "localStorage",
|
|
3389
|
+
autoRestore: true,
|
|
3390
|
+
enableLogging: false,
|
|
3391
|
+
...config
|
|
3392
|
+
};
|
|
3393
|
+
TokenStorage.setStorageType(this.config.storageType || "localStorage");
|
|
3394
|
+
if (this.config.enableLogging) {
|
|
3309
3395
|
}
|
|
3310
|
-
|
|
3311
|
-
|
|
3312
|
-
|
|
3313
|
-
|
|
3314
|
-
|
|
3315
|
-
await refreshTokens();
|
|
3316
|
-
};
|
|
3317
|
-
if (isAuthenticated) {
|
|
3318
|
-
return /* @__PURE__ */ jsxs9(Box8, { sx: { maxWidth: 600, mx: "auto", p: 3 }, children: [
|
|
3319
|
-
/* @__PURE__ */ jsx12(Typography8, { variant: "h4", gutterBottom: true, children: "Welcome! \u{1F389}" }),
|
|
3320
|
-
/* @__PURE__ */ jsx12(Alert7, { severity: "success", sx: { mb: 3 }, children: "You are successfully logged in with Refresh Token Pattern enabled" }),
|
|
3321
|
-
/* @__PURE__ */ jsxs9(Box8, { sx: { mb: 3, p: 2, bgcolor: "background.paper", border: 1, borderColor: "divider", borderRadius: 1 }, children: [
|
|
3322
|
-
/* @__PURE__ */ jsx12(Typography8, { variant: "h6", gutterBottom: true, children: "Token Status" }),
|
|
3323
|
-
/* @__PURE__ */ jsxs9(Typography8, { variant: "body2", color: "text.secondary", children: [
|
|
3324
|
-
"Access Token expires in: ",
|
|
3325
|
-
Math.round(expiresIn / 1e3 / 60),
|
|
3326
|
-
" minutes"
|
|
3327
|
-
] }),
|
|
3328
|
-
isExpiringSoon && /* @__PURE__ */ jsx12(Alert7, { severity: "warning", sx: { mt: 1 }, children: "Token expires soon - automatic refresh will happen" })
|
|
3329
|
-
] }),
|
|
3330
|
-
/* @__PURE__ */ jsxs9(Box8, { sx: { display: "flex", gap: 2, flexWrap: "wrap" }, children: [
|
|
3331
|
-
/* @__PURE__ */ jsx12(
|
|
3332
|
-
Button5,
|
|
3333
|
-
{
|
|
3334
|
-
variant: "contained",
|
|
3335
|
-
onClick: handleRefreshTokens,
|
|
3336
|
-
disabled: isLoading,
|
|
3337
|
-
startIcon: isLoading ? /* @__PURE__ */ jsx12(CircularProgress7, { size: 16 }) : null,
|
|
3338
|
-
children: "Refresh Tokens"
|
|
3339
|
-
}
|
|
3340
|
-
),
|
|
3341
|
-
/* @__PURE__ */ jsx12(
|
|
3342
|
-
Button5,
|
|
3343
|
-
{
|
|
3344
|
-
variant: "outlined",
|
|
3345
|
-
color: "error",
|
|
3346
|
-
onClick: handleLogout,
|
|
3347
|
-
disabled: isLoading,
|
|
3348
|
-
children: "Logout"
|
|
3349
|
-
}
|
|
3350
|
-
)
|
|
3351
|
-
] }),
|
|
3352
|
-
error && /* @__PURE__ */ jsx12(Alert7, { severity: "error", sx: { mt: 2 }, onClose: clearError, children: error })
|
|
3353
|
-
] });
|
|
3396
|
+
if (this.config.autoRestore) {
|
|
3397
|
+
await this.restoreSession();
|
|
3398
|
+
}
|
|
3399
|
+
this.initialized = true;
|
|
3400
|
+
this.log("SessionManager initialized successfully");
|
|
3354
3401
|
}
|
|
3355
|
-
|
|
3356
|
-
|
|
3357
|
-
|
|
3358
|
-
|
|
3359
|
-
|
|
3360
|
-
|
|
3361
|
-
|
|
3362
|
-
|
|
3363
|
-
|
|
3364
|
-
|
|
3365
|
-
|
|
3366
|
-
|
|
3402
|
+
/**
|
|
3403
|
+
* Login con persistencia automática
|
|
3404
|
+
*/
|
|
3405
|
+
async login(email, password) {
|
|
3406
|
+
try {
|
|
3407
|
+
this.log("Attempting login...");
|
|
3408
|
+
const response = await crudify6.login(email, password);
|
|
3409
|
+
if (!response.success) {
|
|
3410
|
+
this.log("Login failed:", response.errors);
|
|
3411
|
+
return {
|
|
3412
|
+
success: false,
|
|
3413
|
+
error: this.formatError(response.errors)
|
|
3414
|
+
};
|
|
3367
3415
|
}
|
|
3368
|
-
|
|
3369
|
-
|
|
3370
|
-
|
|
3371
|
-
|
|
3372
|
-
|
|
3373
|
-
|
|
3374
|
-
|
|
3375
|
-
|
|
3376
|
-
|
|
3377
|
-
|
|
3378
|
-
|
|
3379
|
-
|
|
3380
|
-
|
|
3381
|
-
|
|
3382
|
-
|
|
3383
|
-
|
|
3384
|
-
|
|
3385
|
-
|
|
3386
|
-
|
|
3387
|
-
|
|
3388
|
-
value: password,
|
|
3389
|
-
onChange: (e) => setPassword(e.target.value),
|
|
3390
|
-
margin: "normal",
|
|
3391
|
-
required: true,
|
|
3392
|
-
autoComplete: "current-password"
|
|
3393
|
-
}
|
|
3394
|
-
),
|
|
3395
|
-
/* @__PURE__ */ jsx12(
|
|
3396
|
-
Button5,
|
|
3397
|
-
{
|
|
3398
|
-
type: "submit",
|
|
3399
|
-
fullWidth: true,
|
|
3400
|
-
variant: "contained",
|
|
3401
|
-
size: "large",
|
|
3402
|
-
disabled: isLoading,
|
|
3403
|
-
startIcon: isLoading ? /* @__PURE__ */ jsx12(CircularProgress7, { size: 16 }) : null,
|
|
3404
|
-
sx: { mt: 3, mb: 2 },
|
|
3405
|
-
children: isLoading ? "Logging in..." : "Login"
|
|
3406
|
-
}
|
|
3407
|
-
)
|
|
3408
|
-
] }),
|
|
3409
|
-
error && /* @__PURE__ */ jsx12(Alert7, { severity: "error", sx: { mt: 2 }, onClose: clearError, children: error })
|
|
3410
|
-
] });
|
|
3411
|
-
}
|
|
3412
|
-
function SessionStatus() {
|
|
3413
|
-
const { isAuthenticated, isLoading, isExpiringSoon, expiresIn } = useSessionContext();
|
|
3414
|
-
if (isLoading) {
|
|
3415
|
-
return /* @__PURE__ */ jsxs9(Box8, { sx: { display: "flex", alignItems: "center", gap: 1 }, children: [
|
|
3416
|
-
/* @__PURE__ */ jsx12(CircularProgress7, { size: 16 }),
|
|
3417
|
-
/* @__PURE__ */ jsx12(Typography8, { variant: "caption", children: "Loading session..." })
|
|
3418
|
-
] });
|
|
3416
|
+
const tokens = {
|
|
3417
|
+
accessToken: response.data.token,
|
|
3418
|
+
refreshToken: response.data.refreshToken,
|
|
3419
|
+
expiresAt: response.data.expiresAt,
|
|
3420
|
+
refreshExpiresAt: response.data.refreshExpiresAt
|
|
3421
|
+
};
|
|
3422
|
+
TokenStorage.saveTokens(tokens);
|
|
3423
|
+
this.log("Login successful, tokens saved");
|
|
3424
|
+
this.config.onLoginSuccess?.(tokens);
|
|
3425
|
+
return {
|
|
3426
|
+
success: true,
|
|
3427
|
+
tokens
|
|
3428
|
+
};
|
|
3429
|
+
} catch (error) {
|
|
3430
|
+
this.log("Login error:", error);
|
|
3431
|
+
return {
|
|
3432
|
+
success: false,
|
|
3433
|
+
error: error instanceof Error ? error.message : "Unknown error"
|
|
3434
|
+
};
|
|
3435
|
+
}
|
|
3419
3436
|
}
|
|
3420
|
-
|
|
3421
|
-
|
|
3437
|
+
/**
|
|
3438
|
+
* Logout con limpieza de tokens
|
|
3439
|
+
*/
|
|
3440
|
+
async logout() {
|
|
3441
|
+
try {
|
|
3442
|
+
this.log("Logging out...");
|
|
3443
|
+
await crudify6.logout();
|
|
3444
|
+
TokenStorage.clearTokens();
|
|
3445
|
+
this.log("Logout successful");
|
|
3446
|
+
this.config.onLogout?.();
|
|
3447
|
+
} catch (error) {
|
|
3448
|
+
this.log("Logout error:", error);
|
|
3449
|
+
TokenStorage.clearTokens();
|
|
3450
|
+
}
|
|
3422
3451
|
}
|
|
3423
|
-
|
|
3424
|
-
|
|
3425
|
-
|
|
3426
|
-
|
|
3427
|
-
|
|
3428
|
-
"
|
|
3429
|
-
|
|
3430
|
-
|
|
3431
|
-
|
|
3432
|
-
|
|
3433
|
-
|
|
3434
|
-
|
|
3435
|
-
|
|
3436
|
-
|
|
3437
|
-
|
|
3438
|
-
|
|
3439
|
-
|
|
3440
|
-
|
|
3441
|
-
|
|
3442
|
-
|
|
3443
|
-
|
|
3444
|
-
|
|
3445
|
-
|
|
3446
|
-
|
|
3447
|
-
|
|
3448
|
-
|
|
3449
|
-
|
|
3450
|
-
|
|
3451
|
-
|
|
3452
|
-
|
|
3453
|
-
|
|
3454
|
-
|
|
3455
|
-
|
|
3456
|
-
|
|
3457
|
-
|
|
3458
|
-
|
|
3459
|
-
|
|
3460
|
-
|
|
3461
|
-
|
|
3462
|
-
|
|
3452
|
+
/**
|
|
3453
|
+
* Restaurar sesión desde storage
|
|
3454
|
+
*/
|
|
3455
|
+
async restoreSession() {
|
|
3456
|
+
try {
|
|
3457
|
+
this.log("Attempting to restore session...");
|
|
3458
|
+
const savedTokens = TokenStorage.getTokens();
|
|
3459
|
+
if (!savedTokens) {
|
|
3460
|
+
this.log("No valid tokens found in storage");
|
|
3461
|
+
return false;
|
|
3462
|
+
}
|
|
3463
|
+
crudify6.setTokens({
|
|
3464
|
+
accessToken: savedTokens.accessToken,
|
|
3465
|
+
refreshToken: savedTokens.refreshToken,
|
|
3466
|
+
expiresAt: savedTokens.expiresAt,
|
|
3467
|
+
refreshExpiresAt: savedTokens.refreshExpiresAt
|
|
3468
|
+
});
|
|
3469
|
+
this.log("Session restored successfully");
|
|
3470
|
+
this.config.onSessionRestored?.(savedTokens);
|
|
3471
|
+
return true;
|
|
3472
|
+
} catch (error) {
|
|
3473
|
+
this.log("Session restore error:", error);
|
|
3474
|
+
TokenStorage.clearTokens();
|
|
3475
|
+
return false;
|
|
3476
|
+
}
|
|
3477
|
+
}
|
|
3478
|
+
/**
|
|
3479
|
+
* Verificar si el usuario está autenticado
|
|
3480
|
+
*/
|
|
3481
|
+
isAuthenticated() {
|
|
3482
|
+
return crudify6.isLogin() || TokenStorage.hasValidTokens();
|
|
3483
|
+
}
|
|
3484
|
+
/**
|
|
3485
|
+
* Obtener información de tokens actuales
|
|
3486
|
+
*/
|
|
3487
|
+
getTokenInfo() {
|
|
3488
|
+
const crudifyTokens = crudify6.getTokenData();
|
|
3489
|
+
const storageInfo = TokenStorage.getExpirationInfo();
|
|
3490
|
+
return {
|
|
3491
|
+
isLoggedIn: this.isAuthenticated(),
|
|
3492
|
+
crudifyTokens,
|
|
3493
|
+
storageInfo,
|
|
3494
|
+
hasValidTokens: TokenStorage.hasValidTokens()
|
|
3495
|
+
};
|
|
3496
|
+
}
|
|
3497
|
+
/**
|
|
3498
|
+
* Refrescar tokens manualmente
|
|
3499
|
+
*/
|
|
3500
|
+
async refreshTokens() {
|
|
3501
|
+
try {
|
|
3502
|
+
this.log("Manually refreshing tokens...");
|
|
3503
|
+
const response = await crudify6.refreshAccessToken();
|
|
3504
|
+
if (!response.success) {
|
|
3505
|
+
this.log("Token refresh failed:", response.errors);
|
|
3506
|
+
TokenStorage.clearTokens();
|
|
3507
|
+
this.config.onSessionExpired?.();
|
|
3508
|
+
return false;
|
|
3463
3509
|
}
|
|
3464
|
-
|
|
3510
|
+
const newTokens = {
|
|
3511
|
+
accessToken: response.data.token,
|
|
3512
|
+
refreshToken: response.data.refreshToken,
|
|
3513
|
+
expiresAt: response.data.expiresAt,
|
|
3514
|
+
refreshExpiresAt: response.data.refreshExpiresAt
|
|
3515
|
+
};
|
|
3516
|
+
TokenStorage.saveTokens(newTokens);
|
|
3517
|
+
this.log("Tokens refreshed and saved successfully");
|
|
3518
|
+
return true;
|
|
3519
|
+
} catch (error) {
|
|
3520
|
+
this.log("Token refresh error:", error);
|
|
3521
|
+
TokenStorage.clearTokens();
|
|
3522
|
+
this.config.onSessionExpired?.();
|
|
3523
|
+
return false;
|
|
3465
3524
|
}
|
|
3466
|
-
|
|
3467
|
-
|
|
3468
|
-
|
|
3469
|
-
|
|
3525
|
+
}
|
|
3526
|
+
/**
|
|
3527
|
+
* Configurar interceptor de respuesta para manejo automático de errores
|
|
3528
|
+
*/
|
|
3529
|
+
setupResponseInterceptor() {
|
|
3530
|
+
crudify6.setResponseInterceptor(async (response) => {
|
|
3531
|
+
if (response.errors) {
|
|
3532
|
+
const hasAuthError = response.errors.some(
|
|
3533
|
+
(error) => error.message?.includes("Unauthorized") || error.message?.includes("Token") || error.extensions?.code === "UNAUTHENTICATED"
|
|
3534
|
+
);
|
|
3535
|
+
if (hasAuthError && TokenStorage.hasValidTokens()) {
|
|
3536
|
+
this.log("Auth error detected, attempting token refresh...");
|
|
3537
|
+
const refreshSuccess = await this.refreshTokens();
|
|
3538
|
+
if (!refreshSuccess) {
|
|
3539
|
+
this.log("Session expired, triggering callback");
|
|
3540
|
+
this.config.onSessionExpired?.();
|
|
3541
|
+
}
|
|
3542
|
+
}
|
|
3470
3543
|
}
|
|
3471
|
-
return;
|
|
3544
|
+
return response;
|
|
3545
|
+
});
|
|
3546
|
+
this.log("Response interceptor configured");
|
|
3547
|
+
}
|
|
3548
|
+
/**
|
|
3549
|
+
* Limpiar sesión completamente
|
|
3550
|
+
*/
|
|
3551
|
+
clearSession() {
|
|
3552
|
+
TokenStorage.clearTokens();
|
|
3553
|
+
crudify6.logout();
|
|
3554
|
+
this.log("Session cleared completely");
|
|
3555
|
+
}
|
|
3556
|
+
// Métodos privados
|
|
3557
|
+
log(message, ...args) {
|
|
3558
|
+
if (this.config.enableLogging) {
|
|
3559
|
+
console.log(`[SessionManager] ${message}`, ...args);
|
|
3472
3560
|
}
|
|
3473
|
-
|
|
3474
|
-
|
|
3561
|
+
}
|
|
3562
|
+
formatError(errors) {
|
|
3563
|
+
if (!errors) return "Unknown error";
|
|
3564
|
+
if (typeof errors === "string") return errors;
|
|
3565
|
+
if (typeof errors === "object") {
|
|
3566
|
+
const errorMessages = Object.values(errors).flat();
|
|
3567
|
+
return errorMessages.join(", ");
|
|
3475
3568
|
}
|
|
3476
|
-
|
|
3477
|
-
|
|
3478
|
-
|
|
3569
|
+
return "Authentication failed";
|
|
3570
|
+
}
|
|
3571
|
+
};
|
|
3572
|
+
|
|
3573
|
+
// src/hooks/useSession.ts
|
|
3574
|
+
import { useState as useState10, useEffect as useEffect10, useCallback as useCallback5 } from "react";
|
|
3575
|
+
function useSession(options = {}) {
|
|
3576
|
+
const [state, setState] = useState10({
|
|
3577
|
+
isAuthenticated: false,
|
|
3578
|
+
isLoading: true,
|
|
3579
|
+
isInitialized: false,
|
|
3580
|
+
tokens: null,
|
|
3581
|
+
error: null
|
|
3582
|
+
});
|
|
3583
|
+
const sessionManager = SessionManager.getInstance();
|
|
3584
|
+
const initialize = useCallback5(async () => {
|
|
3479
3585
|
try {
|
|
3480
|
-
|
|
3481
|
-
|
|
3482
|
-
|
|
3483
|
-
|
|
3484
|
-
|
|
3485
|
-
|
|
3486
|
-
|
|
3487
|
-
|
|
3488
|
-
|
|
3489
|
-
|
|
3490
|
-
|
|
3491
|
-
|
|
3492
|
-
|
|
3493
|
-
|
|
3494
|
-
|
|
3495
|
-
|
|
3496
|
-
|
|
3497
|
-
|
|
3498
|
-
|
|
3499
|
-
|
|
3500
|
-
|
|
3501
|
-
|
|
3502
|
-
|
|
3503
|
-
|
|
3504
|
-
|
|
3505
|
-
|
|
3506
|
-
|
|
3507
|
-
|
|
3508
|
-
|
|
3509
|
-
|
|
3510
|
-
|
|
3511
|
-
|
|
3512
|
-
|
|
3513
|
-
|
|
3514
|
-
|
|
3515
|
-
|
|
3516
|
-
|
|
3517
|
-
console.log("\u{1F464} useUserData - No items found in parsed data or items array is empty");
|
|
3518
|
-
}
|
|
3519
|
-
} catch (parseError) {
|
|
3520
|
-
console.error("\u{1F464} useUserData - Error parsing nested response data:", parseError);
|
|
3521
|
-
}
|
|
3522
|
-
} else if (response.data && typeof response.data === "object") {
|
|
3523
|
-
console.log("\u{1F464} useUserData - Found object format, checking for items");
|
|
3524
|
-
if (response.data.items && Array.isArray(response.data.items) && response.data.items.length > 0) {
|
|
3525
|
-
console.log("\u{1F464} useUserData - Found items in object format");
|
|
3526
|
-
userData2 = response.data.items[0];
|
|
3527
|
-
} else {
|
|
3528
|
-
console.log("\u{1F464} useUserData - No items found in object format");
|
|
3529
|
-
}
|
|
3530
|
-
} else if (response.data?.data?.response?.data) {
|
|
3531
|
-
console.log("\u{1F464} useUserData - Found double-nested data.data.response.data format");
|
|
3532
|
-
try {
|
|
3533
|
-
const rawData = response.data.data.response.data;
|
|
3534
|
-
console.log("\u{1F464} useUserData - Raw double-nested data:", rawData);
|
|
3535
|
-
const parsedData = typeof rawData === "string" ? JSON.parse(rawData) : rawData;
|
|
3536
|
-
console.log("\u{1F464} useUserData - Parsed double-nested data:", parsedData);
|
|
3537
|
-
if (parsedData && parsedData.items && Array.isArray(parsedData.items) && parsedData.items.length > 0) {
|
|
3538
|
-
userData2 = parsedData.items[0];
|
|
3539
|
-
console.log("\u{1F464} useUserData - Extracted user from double-nested items:", userData2);
|
|
3540
|
-
}
|
|
3541
|
-
} catch (parseError) {
|
|
3542
|
-
console.error("\u{1F464} useUserData - Error parsing double-nested response data:", parseError);
|
|
3543
|
-
}
|
|
3544
|
-
}
|
|
3545
|
-
}
|
|
3546
|
-
if (userData2) {
|
|
3547
|
-
console.log("\u{1F464} useUserData - User data found:", userData2);
|
|
3548
|
-
setUserData(userData2);
|
|
3549
|
-
setError(null);
|
|
3550
|
-
retryCountRef.current = 0;
|
|
3551
|
-
console.log("\u{1F464} useUserData - Profile loaded successfully:", userData2);
|
|
3552
|
-
} else {
|
|
3553
|
-
setError("User profile not found in database");
|
|
3554
|
-
setUserData(null);
|
|
3555
|
-
console.warn("\u{1F464} useUserData - User not found for email:", userEmail);
|
|
3556
|
-
}
|
|
3557
|
-
}
|
|
3558
|
-
} catch (err) {
|
|
3559
|
-
if (currentRequestId === requestIdRef.current && mountedRef.current) {
|
|
3560
|
-
const error2 = err;
|
|
3561
|
-
console.error("\u{1F464} useUserData - Error fetching profile:", error2);
|
|
3562
|
-
if (error2.name === "AbortError") {
|
|
3563
|
-
return;
|
|
3564
|
-
}
|
|
3565
|
-
const shouldRetry = retryOnError && retryCountRef.current < maxRetries && (error2.message?.includes("Network Error") || error2.message?.includes("Failed to fetch"));
|
|
3566
|
-
if (shouldRetry) {
|
|
3567
|
-
retryCountRef.current++;
|
|
3568
|
-
console.log(`\u{1F464} useUserData - Retrying profile fetch (${retryCountRef.current}/${maxRetries})`);
|
|
3569
|
-
setTimeout(() => {
|
|
3570
|
-
if (mountedRef.current) {
|
|
3571
|
-
refreshProfile();
|
|
3572
|
-
}
|
|
3573
|
-
}, 1e3 * retryCountRef.current);
|
|
3574
|
-
} else {
|
|
3575
|
-
setError("Failed to load user profile from database");
|
|
3576
|
-
setUserData(null);
|
|
3586
|
+
setState((prev) => ({ ...prev, isLoading: true, error: null }));
|
|
3587
|
+
const config = {
|
|
3588
|
+
autoRestore: options.autoRestore ?? true,
|
|
3589
|
+
enableLogging: options.enableLogging ?? false,
|
|
3590
|
+
onSessionExpired: () => {
|
|
3591
|
+
setState((prev) => ({
|
|
3592
|
+
...prev,
|
|
3593
|
+
isAuthenticated: false,
|
|
3594
|
+
tokens: null,
|
|
3595
|
+
error: "Session expired"
|
|
3596
|
+
}));
|
|
3597
|
+
options.onSessionExpired?.();
|
|
3598
|
+
},
|
|
3599
|
+
onSessionRestored: (tokens) => {
|
|
3600
|
+
setState((prev) => ({
|
|
3601
|
+
...prev,
|
|
3602
|
+
isAuthenticated: true,
|
|
3603
|
+
tokens,
|
|
3604
|
+
error: null
|
|
3605
|
+
}));
|
|
3606
|
+
options.onSessionRestored?.(tokens);
|
|
3607
|
+
},
|
|
3608
|
+
onLoginSuccess: (tokens) => {
|
|
3609
|
+
setState((prev) => ({
|
|
3610
|
+
...prev,
|
|
3611
|
+
isAuthenticated: true,
|
|
3612
|
+
tokens,
|
|
3613
|
+
error: null
|
|
3614
|
+
}));
|
|
3615
|
+
},
|
|
3616
|
+
onLogout: () => {
|
|
3617
|
+
setState((prev) => ({
|
|
3618
|
+
...prev,
|
|
3619
|
+
isAuthenticated: false,
|
|
3620
|
+
tokens: null,
|
|
3621
|
+
error: null
|
|
3622
|
+
}));
|
|
3577
3623
|
}
|
|
3624
|
+
};
|
|
3625
|
+
await sessionManager.initialize(config);
|
|
3626
|
+
sessionManager.setupResponseInterceptor();
|
|
3627
|
+
const isAuth = sessionManager.isAuthenticated();
|
|
3628
|
+
const tokenInfo = sessionManager.getTokenInfo();
|
|
3629
|
+
setState((prev) => ({
|
|
3630
|
+
...prev,
|
|
3631
|
+
isAuthenticated: isAuth,
|
|
3632
|
+
isInitialized: true,
|
|
3633
|
+
isLoading: false,
|
|
3634
|
+
tokens: tokenInfo.crudifyTokens.accessToken ? {
|
|
3635
|
+
accessToken: tokenInfo.crudifyTokens.accessToken,
|
|
3636
|
+
refreshToken: tokenInfo.crudifyTokens.refreshToken,
|
|
3637
|
+
expiresAt: tokenInfo.crudifyTokens.expiresAt,
|
|
3638
|
+
refreshExpiresAt: tokenInfo.crudifyTokens.refreshExpiresAt
|
|
3639
|
+
} : null
|
|
3640
|
+
}));
|
|
3641
|
+
} catch (error) {
|
|
3642
|
+
setState((prev) => ({
|
|
3643
|
+
...prev,
|
|
3644
|
+
isLoading: false,
|
|
3645
|
+
isInitialized: true,
|
|
3646
|
+
error: error instanceof Error ? error.message : "Initialization failed"
|
|
3647
|
+
}));
|
|
3648
|
+
}
|
|
3649
|
+
}, [options.autoRestore, options.enableLogging, options.onSessionExpired, options.onSessionRestored]);
|
|
3650
|
+
const login = useCallback5(async (email, password) => {
|
|
3651
|
+
setState((prev) => ({ ...prev, isLoading: true, error: null }));
|
|
3652
|
+
try {
|
|
3653
|
+
const result = await sessionManager.login(email, password);
|
|
3654
|
+
if (result.success && result.tokens) {
|
|
3655
|
+
setState((prev) => ({
|
|
3656
|
+
...prev,
|
|
3657
|
+
isAuthenticated: true,
|
|
3658
|
+
tokens: result.tokens,
|
|
3659
|
+
isLoading: false,
|
|
3660
|
+
error: null
|
|
3661
|
+
}));
|
|
3662
|
+
} else {
|
|
3663
|
+
setState((prev) => ({
|
|
3664
|
+
...prev,
|
|
3665
|
+
isAuthenticated: false,
|
|
3666
|
+
tokens: null,
|
|
3667
|
+
isLoading: false,
|
|
3668
|
+
error: result.error || "Login failed"
|
|
3669
|
+
}));
|
|
3578
3670
|
}
|
|
3579
|
-
|
|
3580
|
-
|
|
3581
|
-
|
|
3582
|
-
|
|
3583
|
-
|
|
3584
|
-
|
|
3585
|
-
|
|
3671
|
+
return result;
|
|
3672
|
+
} catch (error) {
|
|
3673
|
+
const errorMsg = error instanceof Error ? error.message : "Login failed";
|
|
3674
|
+
setState((prev) => ({
|
|
3675
|
+
...prev,
|
|
3676
|
+
isAuthenticated: false,
|
|
3677
|
+
tokens: null,
|
|
3678
|
+
isLoading: false,
|
|
3679
|
+
error: errorMsg
|
|
3680
|
+
}));
|
|
3681
|
+
return {
|
|
3682
|
+
success: false,
|
|
3683
|
+
error: errorMsg
|
|
3684
|
+
};
|
|
3586
3685
|
}
|
|
3587
|
-
}, [
|
|
3588
|
-
|
|
3589
|
-
|
|
3590
|
-
|
|
3591
|
-
|
|
3592
|
-
|
|
3686
|
+
}, [sessionManager]);
|
|
3687
|
+
const logout = useCallback5(async () => {
|
|
3688
|
+
setState((prev) => ({ ...prev, isLoading: true }));
|
|
3689
|
+
try {
|
|
3690
|
+
await sessionManager.logout();
|
|
3691
|
+
setState((prev) => ({
|
|
3692
|
+
...prev,
|
|
3693
|
+
isAuthenticated: false,
|
|
3694
|
+
tokens: null,
|
|
3695
|
+
isLoading: false,
|
|
3696
|
+
error: null
|
|
3697
|
+
}));
|
|
3698
|
+
} catch (error) {
|
|
3699
|
+
setState((prev) => ({
|
|
3700
|
+
...prev,
|
|
3701
|
+
isAuthenticated: false,
|
|
3702
|
+
tokens: null,
|
|
3703
|
+
isLoading: false,
|
|
3704
|
+
error: error instanceof Error ? error.message : "Logout error"
|
|
3705
|
+
}));
|
|
3593
3706
|
}
|
|
3594
|
-
}, [
|
|
3595
|
-
|
|
3596
|
-
|
|
3597
|
-
|
|
3598
|
-
|
|
3599
|
-
|
|
3600
|
-
|
|
3601
|
-
|
|
3707
|
+
}, [sessionManager]);
|
|
3708
|
+
const refreshTokens = useCallback5(async () => {
|
|
3709
|
+
try {
|
|
3710
|
+
const success = await sessionManager.refreshTokens();
|
|
3711
|
+
if (success) {
|
|
3712
|
+
const tokenInfo = sessionManager.getTokenInfo();
|
|
3713
|
+
setState((prev) => ({
|
|
3714
|
+
...prev,
|
|
3715
|
+
tokens: tokenInfo.crudifyTokens.accessToken ? {
|
|
3716
|
+
accessToken: tokenInfo.crudifyTokens.accessToken,
|
|
3717
|
+
refreshToken: tokenInfo.crudifyTokens.refreshToken,
|
|
3718
|
+
expiresAt: tokenInfo.crudifyTokens.expiresAt,
|
|
3719
|
+
refreshExpiresAt: tokenInfo.crudifyTokens.refreshExpiresAt
|
|
3720
|
+
} : null,
|
|
3721
|
+
error: null
|
|
3722
|
+
}));
|
|
3723
|
+
} else {
|
|
3724
|
+
setState((prev) => ({
|
|
3725
|
+
...prev,
|
|
3726
|
+
isAuthenticated: false,
|
|
3727
|
+
tokens: null,
|
|
3728
|
+
error: "Token refresh failed"
|
|
3729
|
+
}));
|
|
3602
3730
|
}
|
|
3603
|
-
|
|
3731
|
+
return success;
|
|
3732
|
+
} catch (error) {
|
|
3733
|
+
setState((prev) => ({
|
|
3734
|
+
...prev,
|
|
3735
|
+
isAuthenticated: false,
|
|
3736
|
+
tokens: null,
|
|
3737
|
+
error: error instanceof Error ? error.message : "Token refresh failed"
|
|
3738
|
+
}));
|
|
3739
|
+
return false;
|
|
3740
|
+
}
|
|
3741
|
+
}, [sessionManager]);
|
|
3742
|
+
const clearError = useCallback5(() => {
|
|
3743
|
+
setState((prev) => ({ ...prev, error: null }));
|
|
3604
3744
|
}, []);
|
|
3605
|
-
const
|
|
3606
|
-
|
|
3607
|
-
|
|
3608
|
-
|
|
3609
|
-
|
|
3610
|
-
};
|
|
3745
|
+
const getTokenInfo = useCallback5(() => {
|
|
3746
|
+
return sessionManager.getTokenInfo();
|
|
3747
|
+
}, [sessionManager]);
|
|
3748
|
+
useEffect10(() => {
|
|
3749
|
+
initialize();
|
|
3750
|
+
}, [initialize]);
|
|
3611
3751
|
return {
|
|
3612
|
-
|
|
3613
|
-
|
|
3614
|
-
|
|
3615
|
-
refreshProfile,
|
|
3616
|
-
clearProfile
|
|
3617
|
-
};
|
|
3618
|
-
};
|
|
3619
|
-
|
|
3620
|
-
// src/hooks/useAuth.ts
|
|
3621
|
-
import { useCallback as useCallback7 } from "react";
|
|
3622
|
-
var useAuth = () => {
|
|
3623
|
-
const {
|
|
3624
|
-
isAuthenticated,
|
|
3625
|
-
isLoading,
|
|
3626
|
-
isInitialized,
|
|
3627
|
-
tokens,
|
|
3628
|
-
error,
|
|
3629
|
-
sessionData,
|
|
3752
|
+
// Estado
|
|
3753
|
+
...state,
|
|
3754
|
+
// Acciones
|
|
3630
3755
|
login,
|
|
3631
3756
|
logout,
|
|
3632
3757
|
refreshTokens,
|
|
3633
3758
|
clearError,
|
|
3634
3759
|
getTokenInfo,
|
|
3635
|
-
|
|
3636
|
-
|
|
3637
|
-
|
|
3638
|
-
|
|
3639
|
-
|
|
3640
|
-
|
|
3641
|
-
|
|
3642
|
-
|
|
3643
|
-
|
|
3760
|
+
// Utilidades
|
|
3761
|
+
isExpiringSoon: state.tokens ? state.tokens.expiresAt - Date.now() < 5 * 60 * 1e3 : false,
|
|
3762
|
+
// 5 minutos
|
|
3763
|
+
expiresIn: state.tokens ? Math.max(0, state.tokens.expiresAt - Date.now()) : 0,
|
|
3764
|
+
refreshExpiresIn: state.tokens ? Math.max(0, state.tokens.refreshExpiresAt - Date.now()) : 0
|
|
3765
|
+
};
|
|
3766
|
+
}
|
|
3767
|
+
|
|
3768
|
+
// src/providers/SessionProvider.tsx
|
|
3769
|
+
import { createContext as createContext4, useContext as useContext4, useMemo as useMemo3 } from "react";
|
|
3770
|
+
import { Fragment as Fragment8, jsx as jsx14, jsxs as jsxs11 } from "react/jsx-runtime";
|
|
3771
|
+
var SessionContext = createContext4(void 0);
|
|
3772
|
+
function SessionProvider({ children, options = {} }) {
|
|
3773
|
+
const sessionHook = useSession(options);
|
|
3774
|
+
const sessionData = useMemo3(() => {
|
|
3775
|
+
if (!sessionHook.tokens?.accessToken || !sessionHook.isAuthenticated) {
|
|
3776
|
+
return null;
|
|
3644
3777
|
}
|
|
3645
|
-
|
|
3646
|
-
|
|
3647
|
-
|
|
3648
|
-
|
|
3649
|
-
|
|
3650
|
-
|
|
3651
|
-
|
|
3652
|
-
|
|
3653
|
-
|
|
3654
|
-
|
|
3655
|
-
|
|
3656
|
-
|
|
3657
|
-
|
|
3658
|
-
|
|
3659
|
-
|
|
3660
|
-
|
|
3661
|
-
|
|
3662
|
-
|
|
3663
|
-
|
|
3664
|
-
|
|
3665
|
-
|
|
3666
|
-
|
|
3667
|
-
|
|
3668
|
-
getTokenInfo,
|
|
3669
|
-
clearError
|
|
3778
|
+
try {
|
|
3779
|
+
const decoded = decodeJwtSafely(sessionHook.tokens.accessToken);
|
|
3780
|
+
if (decoded && decoded.sub && decoded.email && decoded.subscriber) {
|
|
3781
|
+
const result = {
|
|
3782
|
+
_id: decoded.sub,
|
|
3783
|
+
email: decoded.email,
|
|
3784
|
+
subscriberKey: decoded.subscriber
|
|
3785
|
+
};
|
|
3786
|
+
Object.keys(decoded).forEach((key) => {
|
|
3787
|
+
if (!["sub", "email", "subscriber"].includes(key)) {
|
|
3788
|
+
result[key] = decoded[key];
|
|
3789
|
+
}
|
|
3790
|
+
});
|
|
3791
|
+
return result;
|
|
3792
|
+
}
|
|
3793
|
+
} catch (error) {
|
|
3794
|
+
console.error("Error decoding JWT token for sessionData:", error);
|
|
3795
|
+
}
|
|
3796
|
+
return null;
|
|
3797
|
+
}, [sessionHook.tokens?.accessToken, sessionHook.isAuthenticated]);
|
|
3798
|
+
const contextValue = {
|
|
3799
|
+
...sessionHook,
|
|
3800
|
+
sessionData
|
|
3670
3801
|
};
|
|
3671
|
-
};
|
|
3802
|
+
return /* @__PURE__ */ jsx14(SessionContext.Provider, { value: contextValue, children });
|
|
3803
|
+
}
|
|
3804
|
+
function useSessionContext() {
|
|
3805
|
+
const context = useContext4(SessionContext);
|
|
3806
|
+
if (context === void 0) {
|
|
3807
|
+
throw new Error("useSessionContext must be used within a SessionProvider");
|
|
3808
|
+
}
|
|
3809
|
+
return context;
|
|
3810
|
+
}
|
|
3811
|
+
function ProtectedRoute({
|
|
3812
|
+
children,
|
|
3813
|
+
fallback = /* @__PURE__ */ jsx14("div", { children: "Please log in to access this content" }),
|
|
3814
|
+
redirectTo
|
|
3815
|
+
}) {
|
|
3816
|
+
const { isAuthenticated, isLoading, isInitialized } = useSessionContext();
|
|
3817
|
+
if (!isInitialized || isLoading) {
|
|
3818
|
+
return /* @__PURE__ */ jsx14("div", { children: "Loading..." });
|
|
3819
|
+
}
|
|
3820
|
+
if (!isAuthenticated) {
|
|
3821
|
+
if (redirectTo) {
|
|
3822
|
+
redirectTo();
|
|
3823
|
+
return null;
|
|
3824
|
+
}
|
|
3825
|
+
return /* @__PURE__ */ jsx14(Fragment8, { children: fallback });
|
|
3826
|
+
}
|
|
3827
|
+
return /* @__PURE__ */ jsx14(Fragment8, { children });
|
|
3828
|
+
}
|
|
3829
|
+
function SessionDebugInfo() {
|
|
3830
|
+
const session = useSessionContext();
|
|
3831
|
+
if (!session.isInitialized) {
|
|
3832
|
+
return /* @__PURE__ */ jsx14("div", { children: "Session not initialized" });
|
|
3833
|
+
}
|
|
3834
|
+
return /* @__PURE__ */ jsxs11("div", { style: {
|
|
3835
|
+
padding: "10px",
|
|
3836
|
+
margin: "10px",
|
|
3837
|
+
border: "1px solid #ccc",
|
|
3838
|
+
borderRadius: "4px",
|
|
3839
|
+
fontSize: "12px",
|
|
3840
|
+
fontFamily: "monospace"
|
|
3841
|
+
}, children: [
|
|
3842
|
+
/* @__PURE__ */ jsx14("h4", { children: "Session Debug Info" }),
|
|
3843
|
+
/* @__PURE__ */ jsxs11("div", { children: [
|
|
3844
|
+
/* @__PURE__ */ jsx14("strong", { children: "Authenticated:" }),
|
|
3845
|
+
" ",
|
|
3846
|
+
session.isAuthenticated ? "Yes" : "No"
|
|
3847
|
+
] }),
|
|
3848
|
+
/* @__PURE__ */ jsxs11("div", { children: [
|
|
3849
|
+
/* @__PURE__ */ jsx14("strong", { children: "Loading:" }),
|
|
3850
|
+
" ",
|
|
3851
|
+
session.isLoading ? "Yes" : "No"
|
|
3852
|
+
] }),
|
|
3853
|
+
/* @__PURE__ */ jsxs11("div", { children: [
|
|
3854
|
+
/* @__PURE__ */ jsx14("strong", { children: "Error:" }),
|
|
3855
|
+
" ",
|
|
3856
|
+
session.error || "None"
|
|
3857
|
+
] }),
|
|
3858
|
+
session.tokens && /* @__PURE__ */ jsxs11(Fragment8, { children: [
|
|
3859
|
+
/* @__PURE__ */ jsxs11("div", { children: [
|
|
3860
|
+
/* @__PURE__ */ jsx14("strong", { children: "Access Token:" }),
|
|
3861
|
+
" ",
|
|
3862
|
+
session.tokens.accessToken.substring(0, 20),
|
|
3863
|
+
"..."
|
|
3864
|
+
] }),
|
|
3865
|
+
/* @__PURE__ */ jsxs11("div", { children: [
|
|
3866
|
+
/* @__PURE__ */ jsx14("strong", { children: "Refresh Token:" }),
|
|
3867
|
+
" ",
|
|
3868
|
+
session.tokens.refreshToken.substring(0, 20),
|
|
3869
|
+
"..."
|
|
3870
|
+
] }),
|
|
3871
|
+
/* @__PURE__ */ jsxs11("div", { children: [
|
|
3872
|
+
/* @__PURE__ */ jsx14("strong", { children: "Access Expires In:" }),
|
|
3873
|
+
" ",
|
|
3874
|
+
Math.round(session.expiresIn / 1e3 / 60),
|
|
3875
|
+
" minutes"
|
|
3876
|
+
] }),
|
|
3877
|
+
/* @__PURE__ */ jsxs11("div", { children: [
|
|
3878
|
+
/* @__PURE__ */ jsx14("strong", { children: "Refresh Expires In:" }),
|
|
3879
|
+
" ",
|
|
3880
|
+
Math.round(session.refreshExpiresIn / 1e3 / 60 / 60),
|
|
3881
|
+
" hours"
|
|
3882
|
+
] }),
|
|
3883
|
+
/* @__PURE__ */ jsxs11("div", { children: [
|
|
3884
|
+
/* @__PURE__ */ jsx14("strong", { children: "Expiring Soon:" }),
|
|
3885
|
+
" ",
|
|
3886
|
+
session.isExpiringSoon ? "Yes" : "No"
|
|
3887
|
+
] })
|
|
3888
|
+
] })
|
|
3889
|
+
] });
|
|
3890
|
+
}
|
|
3672
3891
|
|
|
3673
|
-
// src/
|
|
3674
|
-
import {
|
|
3675
|
-
import
|
|
3676
|
-
|
|
3892
|
+
// src/components/LoginComponent.tsx
|
|
3893
|
+
import { useState as useState11 } from "react";
|
|
3894
|
+
import { Button as Button8, TextField as TextField5, Box as Box11, Alert as Alert8, Typography as Typography11, CircularProgress as CircularProgress7 } from "@mui/material";
|
|
3895
|
+
import { jsx as jsx15, jsxs as jsxs12 } from "react/jsx-runtime";
|
|
3896
|
+
function LoginComponent() {
|
|
3897
|
+
const [email, setEmail] = useState11("");
|
|
3898
|
+
const [password, setPassword] = useState11("");
|
|
3899
|
+
const [showForm, setShowForm] = useState11(false);
|
|
3677
3900
|
const {
|
|
3678
|
-
|
|
3901
|
+
isAuthenticated,
|
|
3679
3902
|
isLoading,
|
|
3680
3903
|
error,
|
|
3681
|
-
|
|
3682
|
-
|
|
3904
|
+
login,
|
|
3905
|
+
logout,
|
|
3906
|
+
refreshTokens,
|
|
3907
|
+
clearError,
|
|
3908
|
+
isExpiringSoon,
|
|
3909
|
+
expiresIn
|
|
3683
3910
|
} = useSessionContext();
|
|
3684
|
-
const
|
|
3685
|
-
|
|
3686
|
-
|
|
3687
|
-
|
|
3688
|
-
return new Promise((resolve, reject) => {
|
|
3689
|
-
const checkReady = () => {
|
|
3690
|
-
if (isReady()) {
|
|
3691
|
-
resolve();
|
|
3692
|
-
} else if (error) {
|
|
3693
|
-
reject(new Error(error));
|
|
3694
|
-
} else {
|
|
3695
|
-
setTimeout(checkReady, 100);
|
|
3696
|
-
}
|
|
3697
|
-
};
|
|
3698
|
-
checkReady();
|
|
3699
|
-
});
|
|
3700
|
-
}, [isReady, error]);
|
|
3701
|
-
const ensureReady = useCallback8(async () => {
|
|
3702
|
-
if (!isReady()) {
|
|
3703
|
-
throw new Error("System not ready. Check isInitialized, isLoading, and error states.");
|
|
3704
|
-
}
|
|
3705
|
-
}, [isReady]);
|
|
3706
|
-
const readItems = useCallback8(async (moduleKey, filter, options) => {
|
|
3707
|
-
await ensureReady();
|
|
3708
|
-
return await crudify8.readItems(moduleKey, filter || {}, options);
|
|
3709
|
-
}, [ensureReady]);
|
|
3710
|
-
const readItem = useCallback8(async (moduleKey, filter, options) => {
|
|
3711
|
-
await ensureReady();
|
|
3712
|
-
return await crudify8.readItem(moduleKey, filter, options);
|
|
3713
|
-
}, [ensureReady]);
|
|
3714
|
-
const createItem = useCallback8(async (moduleKey, data, options) => {
|
|
3715
|
-
await ensureReady();
|
|
3716
|
-
return await crudify8.createItem(moduleKey, data, options);
|
|
3717
|
-
}, [ensureReady]);
|
|
3718
|
-
const updateItem = useCallback8(async (moduleKey, data, options) => {
|
|
3719
|
-
await ensureReady();
|
|
3720
|
-
return await crudify8.updateItem(moduleKey, data, options);
|
|
3721
|
-
}, [ensureReady]);
|
|
3722
|
-
const deleteItem = useCallback8(async (moduleKey, id, options) => {
|
|
3723
|
-
await ensureReady();
|
|
3724
|
-
return await crudify8.deleteItem(moduleKey, id, options);
|
|
3725
|
-
}, [ensureReady]);
|
|
3726
|
-
const transaction = useCallback8(async (operations, options) => {
|
|
3727
|
-
await ensureReady();
|
|
3728
|
-
return await crudify8.transaction(operations, options);
|
|
3729
|
-
}, [ensureReady]);
|
|
3730
|
-
const login = useCallback8(async (email, password) => {
|
|
3731
|
-
try {
|
|
3732
|
-
const result = await sessionLogin(email, password);
|
|
3733
|
-
if (result.success) {
|
|
3734
|
-
return {
|
|
3735
|
-
success: true,
|
|
3736
|
-
data: result.tokens
|
|
3737
|
-
};
|
|
3738
|
-
} else {
|
|
3739
|
-
return {
|
|
3740
|
-
success: false,
|
|
3741
|
-
errors: result.error || "Login failed"
|
|
3742
|
-
};
|
|
3743
|
-
}
|
|
3744
|
-
} catch (error2) {
|
|
3745
|
-
return {
|
|
3746
|
-
success: false,
|
|
3747
|
-
errors: error2 instanceof Error ? error2.message : "Login failed"
|
|
3748
|
-
};
|
|
3911
|
+
const handleLogin = async (e) => {
|
|
3912
|
+
e.preventDefault();
|
|
3913
|
+
if (!email || !password) {
|
|
3914
|
+
return;
|
|
3749
3915
|
}
|
|
3750
|
-
|
|
3751
|
-
|
|
3752
|
-
|
|
3753
|
-
|
|
3754
|
-
|
|
3755
|
-
createItem,
|
|
3756
|
-
updateItem,
|
|
3757
|
-
deleteItem,
|
|
3758
|
-
transaction,
|
|
3759
|
-
login,
|
|
3760
|
-
// Estado
|
|
3761
|
-
isInitialized,
|
|
3762
|
-
isInitializing: isLoading,
|
|
3763
|
-
// El nuevo sistema usa isLoading
|
|
3764
|
-
initializationError: error,
|
|
3765
|
-
// Utilidades
|
|
3766
|
-
isReady,
|
|
3767
|
-
waitForReady
|
|
3768
|
-
};
|
|
3769
|
-
};
|
|
3770
|
-
|
|
3771
|
-
// src/components/PublicPolicies/Policies.tsx
|
|
3772
|
-
import { useRef as useRef6 } from "react";
|
|
3773
|
-
import { useTranslation as useTranslation4 } from "react-i18next";
|
|
3774
|
-
import {
|
|
3775
|
-
Box as Box11,
|
|
3776
|
-
Typography as Typography11,
|
|
3777
|
-
Button as Button8,
|
|
3778
|
-
Stack as Stack3,
|
|
3779
|
-
Alert as Alert8,
|
|
3780
|
-
Divider as Divider3
|
|
3781
|
-
} from "@mui/material";
|
|
3782
|
-
import { Add } from "@mui/icons-material";
|
|
3783
|
-
|
|
3784
|
-
// src/components/PublicPolicies/PolicyItem/PolicyItem.tsx
|
|
3785
|
-
import { forwardRef } from "react";
|
|
3786
|
-
import { useTranslation as useTranslation3 } from "react-i18next";
|
|
3787
|
-
import {
|
|
3788
|
-
Box as Box10,
|
|
3789
|
-
FormControl,
|
|
3790
|
-
InputLabel,
|
|
3791
|
-
Select,
|
|
3792
|
-
MenuItem,
|
|
3793
|
-
IconButton as IconButton2,
|
|
3794
|
-
Typography as Typography10,
|
|
3795
|
-
FormHelperText as FormHelperText2,
|
|
3796
|
-
Stack as Stack2,
|
|
3797
|
-
Paper,
|
|
3798
|
-
Divider as Divider2,
|
|
3799
|
-
Button as Button7
|
|
3800
|
-
} from "@mui/material";
|
|
3801
|
-
import { Delete, SelectAll as SelectAll2, ClearAll as ClearAll2 } from "@mui/icons-material";
|
|
3802
|
-
|
|
3803
|
-
// src/components/PublicPolicies/FieldSelector/FieldSelector.tsx
|
|
3804
|
-
import { useState as useState12, useEffect as useEffect11, useRef as useRef5 } from "react";
|
|
3805
|
-
import { useTranslation as useTranslation2 } from "react-i18next";
|
|
3806
|
-
import {
|
|
3807
|
-
Box as Box9,
|
|
3808
|
-
Typography as Typography9,
|
|
3809
|
-
Button as Button6,
|
|
3810
|
-
Stack,
|
|
3811
|
-
FormHelperText,
|
|
3812
|
-
ToggleButton,
|
|
3813
|
-
ToggleButtonGroup
|
|
3814
|
-
} from "@mui/material";
|
|
3815
|
-
import {
|
|
3816
|
-
CheckCircle,
|
|
3817
|
-
Cancel,
|
|
3818
|
-
SelectAll,
|
|
3819
|
-
ClearAll
|
|
3820
|
-
} from "@mui/icons-material";
|
|
3821
|
-
import { jsx as jsx13, jsxs as jsxs10 } from "react/jsx-runtime";
|
|
3822
|
-
var FieldSelector = ({
|
|
3823
|
-
value,
|
|
3824
|
-
onChange,
|
|
3825
|
-
availableFields,
|
|
3826
|
-
error,
|
|
3827
|
-
disabled = false
|
|
3828
|
-
}) => {
|
|
3829
|
-
const { t } = useTranslation2();
|
|
3830
|
-
const [mode, setMode] = useState12("custom");
|
|
3831
|
-
const isUpdatingRef = useRef5(false);
|
|
3832
|
-
useEffect11(() => {
|
|
3833
|
-
const current = value || { allow: [], owner_allow: [], deny: [] };
|
|
3834
|
-
const all = new Set(availableFields);
|
|
3835
|
-
const allow = (current.allow || []).filter((f) => all.has(f));
|
|
3836
|
-
const owner = (current.owner_allow || []).filter((f) => all.has(f));
|
|
3837
|
-
const deny = (current.deny || []).filter((f) => all.has(f));
|
|
3838
|
-
availableFields.forEach((f) => {
|
|
3839
|
-
if (!allow.includes(f) && !owner.includes(f) && !deny.includes(f)) deny.push(f);
|
|
3840
|
-
});
|
|
3841
|
-
const normalized = { allow, owner_allow: owner, deny };
|
|
3842
|
-
if (JSON.stringify(normalized) !== JSON.stringify(current)) {
|
|
3843
|
-
onChange(normalized);
|
|
3916
|
+
const result = await login(email, password);
|
|
3917
|
+
if (result.success) {
|
|
3918
|
+
setEmail("");
|
|
3919
|
+
setPassword("");
|
|
3920
|
+
setShowForm(false);
|
|
3844
3921
|
}
|
|
3845
|
-
if (allow.length === availableFields.length) setMode("all");
|
|
3846
|
-
else if (deny.length === availableFields.length) setMode("none");
|
|
3847
|
-
else setMode("custom");
|
|
3848
|
-
}, [availableFields, value]);
|
|
3849
|
-
const setAllAllow = () => {
|
|
3850
|
-
isUpdatingRef.current = true;
|
|
3851
|
-
onChange({ allow: [...availableFields], owner_allow: [], deny: [] });
|
|
3852
|
-
setMode("all");
|
|
3853
|
-
setTimeout(() => {
|
|
3854
|
-
isUpdatingRef.current = false;
|
|
3855
|
-
}, 0);
|
|
3856
|
-
};
|
|
3857
|
-
const setAllDeny = () => {
|
|
3858
|
-
isUpdatingRef.current = true;
|
|
3859
|
-
onChange({ allow: [], owner_allow: [], deny: [...availableFields] });
|
|
3860
|
-
setMode("none");
|
|
3861
|
-
setTimeout(() => {
|
|
3862
|
-
isUpdatingRef.current = false;
|
|
3863
|
-
}, 0);
|
|
3864
3922
|
};
|
|
3865
|
-
const
|
|
3866
|
-
|
|
3867
|
-
if (value?.owner_allow?.includes(fieldName)) return "owner_allow";
|
|
3868
|
-
return "deny";
|
|
3923
|
+
const handleLogout = async () => {
|
|
3924
|
+
await logout();
|
|
3869
3925
|
};
|
|
3870
|
-
const
|
|
3871
|
-
|
|
3872
|
-
const allow = new Set(value?.allow || []);
|
|
3873
|
-
const owner = new Set(value?.owner_allow || []);
|
|
3874
|
-
const deny = new Set(value?.deny || []);
|
|
3875
|
-
allow.delete(fieldName);
|
|
3876
|
-
owner.delete(fieldName);
|
|
3877
|
-
deny.delete(fieldName);
|
|
3878
|
-
if (state === "allow") allow.add(fieldName);
|
|
3879
|
-
if (state === "owner_allow") owner.add(fieldName);
|
|
3880
|
-
if (state === "deny") deny.add(fieldName);
|
|
3881
|
-
onChange({ allow: Array.from(allow), owner_allow: Array.from(owner), deny: Array.from(deny) });
|
|
3882
|
-
setMode("custom");
|
|
3883
|
-
setTimeout(() => {
|
|
3884
|
-
isUpdatingRef.current = false;
|
|
3885
|
-
}, 0);
|
|
3926
|
+
const handleRefreshTokens = async () => {
|
|
3927
|
+
await refreshTokens();
|
|
3886
3928
|
};
|
|
3887
|
-
if (
|
|
3888
|
-
return /* @__PURE__ */
|
|
3889
|
-
/* @__PURE__ */
|
|
3890
|
-
/* @__PURE__ */
|
|
3891
|
-
|
|
3929
|
+
if (isAuthenticated) {
|
|
3930
|
+
return /* @__PURE__ */ jsxs12(Box11, { sx: { maxWidth: 600, mx: "auto", p: 3 }, children: [
|
|
3931
|
+
/* @__PURE__ */ jsx15(Typography11, { variant: "h4", gutterBottom: true, children: "Welcome! \u{1F389}" }),
|
|
3932
|
+
/* @__PURE__ */ jsx15(Alert8, { severity: "success", sx: { mb: 3 }, children: "You are successfully logged in with Refresh Token Pattern enabled" }),
|
|
3933
|
+
/* @__PURE__ */ jsxs12(Box11, { sx: { mb: 3, p: 2, bgcolor: "background.paper", border: 1, borderColor: "divider", borderRadius: 1 }, children: [
|
|
3934
|
+
/* @__PURE__ */ jsx15(Typography11, { variant: "h6", gutterBottom: true, children: "Token Status" }),
|
|
3935
|
+
/* @__PURE__ */ jsxs12(Typography11, { variant: "body2", color: "text.secondary", children: [
|
|
3936
|
+
"Access Token expires in: ",
|
|
3937
|
+
Math.round(expiresIn / 1e3 / 60),
|
|
3938
|
+
" minutes"
|
|
3939
|
+
] }),
|
|
3940
|
+
isExpiringSoon && /* @__PURE__ */ jsx15(Alert8, { severity: "warning", sx: { mt: 1 }, children: "Token expires soon - automatic refresh will happen" })
|
|
3941
|
+
] }),
|
|
3942
|
+
/* @__PURE__ */ jsxs12(Box11, { sx: { display: "flex", gap: 2, flexWrap: "wrap" }, children: [
|
|
3943
|
+
/* @__PURE__ */ jsx15(
|
|
3944
|
+
Button8,
|
|
3945
|
+
{
|
|
3946
|
+
variant: "contained",
|
|
3947
|
+
onClick: handleRefreshTokens,
|
|
3948
|
+
disabled: isLoading,
|
|
3949
|
+
startIcon: isLoading ? /* @__PURE__ */ jsx15(CircularProgress7, { size: 16 }) : null,
|
|
3950
|
+
children: "Refresh Tokens"
|
|
3951
|
+
}
|
|
3952
|
+
),
|
|
3953
|
+
/* @__PURE__ */ jsx15(
|
|
3954
|
+
Button8,
|
|
3955
|
+
{
|
|
3956
|
+
variant: "outlined",
|
|
3957
|
+
color: "error",
|
|
3958
|
+
onClick: handleLogout,
|
|
3959
|
+
disabled: isLoading,
|
|
3960
|
+
children: "Logout"
|
|
3961
|
+
}
|
|
3962
|
+
)
|
|
3963
|
+
] }),
|
|
3964
|
+
error && /* @__PURE__ */ jsx15(Alert8, { severity: "error", sx: { mt: 2 }, onClose: clearError, children: error })
|
|
3892
3965
|
] });
|
|
3893
3966
|
}
|
|
3894
|
-
return /* @__PURE__ */
|
|
3895
|
-
/* @__PURE__ */
|
|
3896
|
-
/* @__PURE__ */
|
|
3897
|
-
|
|
3898
|
-
|
|
3967
|
+
return /* @__PURE__ */ jsxs12(Box11, { sx: { maxWidth: 400, mx: "auto", p: 3 }, children: [
|
|
3968
|
+
/* @__PURE__ */ jsx15(Typography11, { variant: "h4", gutterBottom: true, align: "center", children: "Login with Refresh Tokens" }),
|
|
3969
|
+
/* @__PURE__ */ jsx15(Alert8, { severity: "info", sx: { mb: 3 }, children: "This demo shows the new Refresh Token Pattern with automatic session management" }),
|
|
3970
|
+
!showForm ? /* @__PURE__ */ jsx15(
|
|
3971
|
+
Button8,
|
|
3972
|
+
{
|
|
3973
|
+
fullWidth: true,
|
|
3974
|
+
variant: "contained",
|
|
3975
|
+
size: "large",
|
|
3976
|
+
onClick: () => setShowForm(true),
|
|
3977
|
+
sx: { mt: 2 },
|
|
3978
|
+
children: "Show Login Form"
|
|
3979
|
+
}
|
|
3980
|
+
) : /* @__PURE__ */ jsxs12("form", { onSubmit: handleLogin, children: [
|
|
3981
|
+
/* @__PURE__ */ jsx15(
|
|
3982
|
+
TextField5,
|
|
3899
3983
|
{
|
|
3900
|
-
|
|
3901
|
-
|
|
3902
|
-
|
|
3903
|
-
|
|
3904
|
-
|
|
3905
|
-
|
|
3906
|
-
|
|
3907
|
-
|
|
3908
|
-
|
|
3909
|
-
|
|
3910
|
-
|
|
3911
|
-
|
|
3912
|
-
|
|
3984
|
+
fullWidth: true,
|
|
3985
|
+
label: "Email",
|
|
3986
|
+
type: "email",
|
|
3987
|
+
value: email,
|
|
3988
|
+
onChange: (e) => setEmail(e.target.value),
|
|
3989
|
+
margin: "normal",
|
|
3990
|
+
required: true,
|
|
3991
|
+
autoComplete: "email"
|
|
3992
|
+
}
|
|
3993
|
+
),
|
|
3994
|
+
/* @__PURE__ */ jsx15(
|
|
3995
|
+
TextField5,
|
|
3996
|
+
{
|
|
3997
|
+
fullWidth: true,
|
|
3998
|
+
label: "Password",
|
|
3999
|
+
type: "password",
|
|
4000
|
+
value: password,
|
|
4001
|
+
onChange: (e) => setPassword(e.target.value),
|
|
4002
|
+
margin: "normal",
|
|
4003
|
+
required: true,
|
|
4004
|
+
autoComplete: "current-password"
|
|
3913
4005
|
}
|
|
3914
4006
|
),
|
|
3915
|
-
/* @__PURE__ */
|
|
3916
|
-
|
|
4007
|
+
/* @__PURE__ */ jsx15(
|
|
4008
|
+
Button8,
|
|
3917
4009
|
{
|
|
3918
|
-
|
|
3919
|
-
|
|
3920
|
-
|
|
3921
|
-
|
|
3922
|
-
|
|
3923
|
-
|
|
3924
|
-
|
|
3925
|
-
|
|
3926
|
-
backgroundColor: "#cf222e",
|
|
3927
|
-
"&:hover": { backgroundColor: "#bc1f2c" }
|
|
3928
|
-
}
|
|
3929
|
-
},
|
|
3930
|
-
children: t("modules.form.publicPolicies.fields.conditions.noFields")
|
|
4010
|
+
type: "submit",
|
|
4011
|
+
fullWidth: true,
|
|
4012
|
+
variant: "contained",
|
|
4013
|
+
size: "large",
|
|
4014
|
+
disabled: isLoading,
|
|
4015
|
+
startIcon: isLoading ? /* @__PURE__ */ jsx15(CircularProgress7, { size: 16 }) : null,
|
|
4016
|
+
sx: { mt: 3, mb: 2 },
|
|
4017
|
+
children: isLoading ? "Logging in..." : "Login"
|
|
3931
4018
|
}
|
|
3932
4019
|
)
|
|
3933
4020
|
] }),
|
|
3934
|
-
/* @__PURE__ */
|
|
3935
|
-
/* @__PURE__ */ jsx13(Typography9, { variant: "body2", color: "text.secondary", sx: { mb: 2 }, children: t("modules.form.publicPolicies.fields.conditions.help") }),
|
|
3936
|
-
/* @__PURE__ */ jsx13(Stack, { spacing: 1, children: availableFields.map((fieldName) => {
|
|
3937
|
-
const fieldState = getFieldState(fieldName);
|
|
3938
|
-
return /* @__PURE__ */ jsxs10(Stack, { direction: "row", spacing: 1, alignItems: "center", children: [
|
|
3939
|
-
/* @__PURE__ */ jsx13(Typography9, { variant: "body2", sx: { minWidth: 100, fontFamily: "monospace" }, children: fieldName }),
|
|
3940
|
-
/* @__PURE__ */ jsxs10(ToggleButtonGroup, { value: fieldState, exclusive: true, size: "small", children: [
|
|
3941
|
-
/* @__PURE__ */ jsxs10(
|
|
3942
|
-
ToggleButton,
|
|
3943
|
-
{
|
|
3944
|
-
value: "allow",
|
|
3945
|
-
onClick: () => setFieldState(fieldName, "allow"),
|
|
3946
|
-
disabled,
|
|
3947
|
-
sx: {
|
|
3948
|
-
px: 2,
|
|
3949
|
-
color: fieldState === "allow" ? "#ffffff" : "#6b7280",
|
|
3950
|
-
backgroundColor: fieldState === "allow" ? "#16a34a" : "#f3f4f6",
|
|
3951
|
-
borderColor: fieldState === "allow" ? "#16a34a" : "#d1d5db",
|
|
3952
|
-
"&:hover": {
|
|
3953
|
-
backgroundColor: fieldState === "allow" ? "#15803d" : "#e5e7eb",
|
|
3954
|
-
borderColor: fieldState === "allow" ? "#15803d" : "#9ca3af"
|
|
3955
|
-
},
|
|
3956
|
-
"&.Mui-selected": {
|
|
3957
|
-
backgroundColor: "#16a34a",
|
|
3958
|
-
color: "#ffffff",
|
|
3959
|
-
"&:hover": {
|
|
3960
|
-
backgroundColor: "#15803d"
|
|
3961
|
-
}
|
|
3962
|
-
}
|
|
3963
|
-
},
|
|
3964
|
-
children: [
|
|
3965
|
-
/* @__PURE__ */ jsx13(CheckCircle, { sx: { fontSize: 16, mr: 0.5 } }),
|
|
3966
|
-
t("modules.form.publicPolicies.fields.conditions.states.allow")
|
|
3967
|
-
]
|
|
3968
|
-
}
|
|
3969
|
-
),
|
|
3970
|
-
/* @__PURE__ */ jsx13(
|
|
3971
|
-
ToggleButton,
|
|
3972
|
-
{
|
|
3973
|
-
value: "owner_allow",
|
|
3974
|
-
onClick: () => setFieldState(fieldName, "owner_allow"),
|
|
3975
|
-
disabled,
|
|
3976
|
-
sx: {
|
|
3977
|
-
px: 2,
|
|
3978
|
-
color: fieldState === "owner_allow" ? "#ffffff" : "#6b7280",
|
|
3979
|
-
backgroundColor: fieldState === "owner_allow" ? "#0ea5e9" : "#f3f4f6",
|
|
3980
|
-
borderColor: fieldState === "owner_allow" ? "#0ea5e9" : "#d1d5db",
|
|
3981
|
-
"&:hover": {
|
|
3982
|
-
backgroundColor: fieldState === "owner_allow" ? "#0284c7" : "#e5e7eb",
|
|
3983
|
-
borderColor: fieldState === "owner_allow" ? "#0284c7" : "#9ca3af"
|
|
3984
|
-
},
|
|
3985
|
-
"&.Mui-selected": {
|
|
3986
|
-
backgroundColor: "#0ea5e9",
|
|
3987
|
-
color: "#ffffff",
|
|
3988
|
-
"&:hover": {
|
|
3989
|
-
backgroundColor: "#0284c7"
|
|
3990
|
-
}
|
|
3991
|
-
}
|
|
3992
|
-
},
|
|
3993
|
-
children: t("modules.form.publicPolicies.fields.conditions.states.ownerAllow")
|
|
3994
|
-
}
|
|
3995
|
-
),
|
|
3996
|
-
/* @__PURE__ */ jsxs10(
|
|
3997
|
-
ToggleButton,
|
|
3998
|
-
{
|
|
3999
|
-
value: "deny",
|
|
4000
|
-
onClick: () => setFieldState(fieldName, "deny"),
|
|
4001
|
-
disabled,
|
|
4002
|
-
sx: {
|
|
4003
|
-
px: 2,
|
|
4004
|
-
color: fieldState === "deny" ? "#ffffff" : "#6b7280",
|
|
4005
|
-
backgroundColor: fieldState === "deny" ? "#dc2626" : "#f3f4f6",
|
|
4006
|
-
borderColor: fieldState === "deny" ? "#dc2626" : "#d1d5db",
|
|
4007
|
-
"&:hover": {
|
|
4008
|
-
backgroundColor: fieldState === "deny" ? "#b91c1c" : "#e5e7eb",
|
|
4009
|
-
borderColor: fieldState === "deny" ? "#b91c1c" : "#9ca3af"
|
|
4010
|
-
},
|
|
4011
|
-
"&.Mui-selected": {
|
|
4012
|
-
backgroundColor: "#dc2626",
|
|
4013
|
-
color: "#ffffff",
|
|
4014
|
-
"&:hover": {
|
|
4015
|
-
backgroundColor: "#b91c1c"
|
|
4016
|
-
}
|
|
4017
|
-
}
|
|
4018
|
-
},
|
|
4019
|
-
children: [
|
|
4020
|
-
/* @__PURE__ */ jsx13(Cancel, { sx: { fontSize: 16, mr: 0.5 } }),
|
|
4021
|
-
t("modules.form.publicPolicies.fields.conditions.states.deny")
|
|
4022
|
-
]
|
|
4023
|
-
}
|
|
4024
|
-
)
|
|
4025
|
-
] })
|
|
4026
|
-
] }, fieldName);
|
|
4027
|
-
}) })
|
|
4028
|
-
] }),
|
|
4029
|
-
error && /* @__PURE__ */ jsx13(FormHelperText, { error: true, sx: { mt: 1 }, children: error })
|
|
4021
|
+
error && /* @__PURE__ */ jsx15(Alert8, { severity: "error", sx: { mt: 2 }, onClose: clearError, children: error })
|
|
4030
4022
|
] });
|
|
4031
|
-
}
|
|
4032
|
-
|
|
4033
|
-
|
|
4034
|
-
|
|
4035
|
-
|
|
4036
|
-
|
|
4037
|
-
|
|
4038
|
-
|
|
4039
|
-
|
|
4040
|
-
|
|
4041
|
-
|
|
4023
|
+
}
|
|
4024
|
+
function SessionStatus() {
|
|
4025
|
+
const { isAuthenticated, isLoading, isExpiringSoon, expiresIn } = useSessionContext();
|
|
4026
|
+
if (isLoading) {
|
|
4027
|
+
return /* @__PURE__ */ jsxs12(Box11, { sx: { display: "flex", alignItems: "center", gap: 1 }, children: [
|
|
4028
|
+
/* @__PURE__ */ jsx15(CircularProgress7, { size: 16 }),
|
|
4029
|
+
/* @__PURE__ */ jsx15(Typography11, { variant: "caption", children: "Loading session..." })
|
|
4030
|
+
] });
|
|
4031
|
+
}
|
|
4032
|
+
if (!isAuthenticated) {
|
|
4033
|
+
return /* @__PURE__ */ jsx15(Typography11, { variant: "caption", color: "text.secondary", children: "Not logged in" });
|
|
4034
|
+
}
|
|
4035
|
+
return /* @__PURE__ */ jsxs12(Box11, { children: [
|
|
4036
|
+
/* @__PURE__ */ jsx15(Typography11, { variant: "caption", color: "success.main", children: "\u2713 Authenticated" }),
|
|
4037
|
+
isExpiringSoon && /* @__PURE__ */ jsxs12(Typography11, { variant: "caption", color: "warning.main", display: "block", children: [
|
|
4038
|
+
"\u26A0 Token expires in ",
|
|
4039
|
+
Math.round(expiresIn / 1e3 / 60),
|
|
4040
|
+
" min"
|
|
4041
|
+
] })
|
|
4042
|
+
] });
|
|
4043
|
+
}
|
|
4042
4044
|
|
|
4043
|
-
// src/
|
|
4044
|
-
import {
|
|
4045
|
-
|
|
4046
|
-
|
|
4047
|
-
|
|
4048
|
-
|
|
4049
|
-
|
|
4050
|
-
|
|
4051
|
-
|
|
4052
|
-
|
|
4053
|
-
|
|
4054
|
-
const
|
|
4055
|
-
const
|
|
4056
|
-
|
|
4057
|
-
|
|
4058
|
-
|
|
4059
|
-
|
|
4060
|
-
|
|
4061
|
-
|
|
4062
|
-
|
|
4063
|
-
|
|
4064
|
-
|
|
4065
|
-
|
|
4066
|
-
|
|
4067
|
-
|
|
4068
|
-
|
|
4069
|
-
|
|
4070
|
-
|
|
4071
|
-
|
|
4072
|
-
|
|
4073
|
-
|
|
4074
|
-
|
|
4075
|
-
|
|
4076
|
-
|
|
4077
|
-
|
|
4078
|
-
|
|
4079
|
-
|
|
4080
|
-
|
|
4081
|
-
|
|
4082
|
-
|
|
4083
|
-
|
|
4045
|
+
// src/hooks/useUserData.ts
|
|
4046
|
+
import { useState as useState12, useEffect as useEffect11, useCallback as useCallback6, useRef as useRef6 } from "react";
|
|
4047
|
+
import crudify7 from "@nocios/crudify-browser";
|
|
4048
|
+
var useUserData = (options = {}) => {
|
|
4049
|
+
const { autoFetch = true, retryOnError = false, maxRetries = 3 } = options;
|
|
4050
|
+
const { isAuthenticated, isInitialized, sessionData, tokens } = useSessionContext();
|
|
4051
|
+
const [userData, setUserData] = useState12(null);
|
|
4052
|
+
const [loading, setLoading] = useState12(false);
|
|
4053
|
+
const [error, setError] = useState12(null);
|
|
4054
|
+
const abortControllerRef = useRef6(null);
|
|
4055
|
+
const mountedRef = useRef6(true);
|
|
4056
|
+
const requestIdRef = useRef6(0);
|
|
4057
|
+
const retryCountRef = useRef6(0);
|
|
4058
|
+
const getUserEmail = useCallback6(() => {
|
|
4059
|
+
if (!sessionData) return null;
|
|
4060
|
+
return sessionData.email || sessionData["cognito:username"] || null;
|
|
4061
|
+
}, [sessionData]);
|
|
4062
|
+
const clearProfile = useCallback6(() => {
|
|
4063
|
+
setUserData(null);
|
|
4064
|
+
setError(null);
|
|
4065
|
+
setLoading(false);
|
|
4066
|
+
retryCountRef.current = 0;
|
|
4067
|
+
}, []);
|
|
4068
|
+
const refreshProfile = useCallback6(async () => {
|
|
4069
|
+
const userEmail = getUserEmail();
|
|
4070
|
+
console.log("\u{1F464} useUserData - Refreshing profile for:", userEmail);
|
|
4071
|
+
if (!userEmail) {
|
|
4072
|
+
if (mountedRef.current) {
|
|
4073
|
+
setError("No user email available from session data");
|
|
4074
|
+
setLoading(false);
|
|
4075
|
+
}
|
|
4076
|
+
return;
|
|
4077
|
+
}
|
|
4078
|
+
if (!isInitialized) {
|
|
4079
|
+
if (mountedRef.current) {
|
|
4080
|
+
setError("Session not initialized");
|
|
4081
|
+
setLoading(false);
|
|
4082
|
+
}
|
|
4083
|
+
return;
|
|
4084
|
+
}
|
|
4085
|
+
if (abortControllerRef.current) {
|
|
4086
|
+
abortControllerRef.current.abort();
|
|
4087
|
+
}
|
|
4088
|
+
const abortController = new AbortController();
|
|
4089
|
+
abortControllerRef.current = abortController;
|
|
4090
|
+
const currentRequestId = ++requestIdRef.current;
|
|
4091
|
+
try {
|
|
4092
|
+
if (mountedRef.current) {
|
|
4093
|
+
setLoading(true);
|
|
4094
|
+
setError(null);
|
|
4095
|
+
}
|
|
4096
|
+
console.log("\u{1F464} useUserData - Fetching profile data from database");
|
|
4097
|
+
const response = await crudify7.readItems("users", {
|
|
4098
|
+
filter: { email: userEmail },
|
|
4099
|
+
pagination: { limit: 1 }
|
|
4100
|
+
});
|
|
4101
|
+
console.log("\u{1F464} useUserData - Database response:", response);
|
|
4102
|
+
console.log("\u{1F464} useUserData - response.data:", response.data);
|
|
4103
|
+
console.log("\u{1F464} useUserData - response.data type:", typeof response.data);
|
|
4104
|
+
if (currentRequestId === requestIdRef.current && mountedRef.current && !abortController.signal.aborted) {
|
|
4105
|
+
let userData2 = null;
|
|
4106
|
+
if (response.success) {
|
|
4107
|
+
console.log("\u{1F464} useUserData - Processing successful response:", {
|
|
4108
|
+
dataType: typeof response.data,
|
|
4109
|
+
isArray: Array.isArray(response.data),
|
|
4110
|
+
hasResponse: !!response.data?.response,
|
|
4111
|
+
hasResponseData: !!response.data?.response?.data,
|
|
4112
|
+
responseDataType: typeof response.data?.response?.data
|
|
4113
|
+
});
|
|
4114
|
+
if (Array.isArray(response.data) && response.data.length > 0) {
|
|
4115
|
+
console.log("\u{1F464} useUserData - Found direct array format");
|
|
4116
|
+
userData2 = response.data[0];
|
|
4117
|
+
} else if (response.data?.response?.data) {
|
|
4118
|
+
console.log("\u{1F464} useUserData - Found nested response.data format");
|
|
4119
|
+
try {
|
|
4120
|
+
const rawData = response.data.response.data;
|
|
4121
|
+
console.log("\u{1F464} useUserData - Raw nested data:", rawData);
|
|
4122
|
+
console.log("\u{1F464} useUserData - Raw data type:", typeof rawData);
|
|
4123
|
+
const parsedData = typeof rawData === "string" ? JSON.parse(rawData) : rawData;
|
|
4124
|
+
console.log("\u{1F464} useUserData - Parsed nested data:", parsedData);
|
|
4125
|
+
if (parsedData && parsedData.items && Array.isArray(parsedData.items) && parsedData.items.length > 0) {
|
|
4126
|
+
userData2 = parsedData.items[0];
|
|
4127
|
+
console.log("\u{1F464} useUserData - Extracted user from nested items:", userData2);
|
|
4128
|
+
} else {
|
|
4129
|
+
console.log("\u{1F464} useUserData - No items found in parsed data or items array is empty");
|
|
4130
|
+
}
|
|
4131
|
+
} catch (parseError) {
|
|
4132
|
+
console.error("\u{1F464} useUserData - Error parsing nested response data:", parseError);
|
|
4084
4133
|
}
|
|
4085
|
-
)
|
|
4086
|
-
|
|
4087
|
-
|
|
4088
|
-
|
|
4089
|
-
|
|
4090
|
-
|
|
4091
|
-
|
|
4092
|
-
"aria-label": t("modules.form.publicPolicies.removePolicy"),
|
|
4093
|
-
sx: {
|
|
4094
|
-
color: "#656d76",
|
|
4095
|
-
"&:hover": {
|
|
4096
|
-
color: "#cf222e",
|
|
4097
|
-
backgroundColor: "rgba(207, 34, 46, 0.1)"
|
|
4098
|
-
}
|
|
4099
|
-
},
|
|
4100
|
-
children: /* @__PURE__ */ jsx14(Delete, {})
|
|
4134
|
+
} else if (response.data && typeof response.data === "object") {
|
|
4135
|
+
console.log("\u{1F464} useUserData - Found object format, checking for items");
|
|
4136
|
+
if (response.data.items && Array.isArray(response.data.items) && response.data.items.length > 0) {
|
|
4137
|
+
console.log("\u{1F464} useUserData - Found items in object format");
|
|
4138
|
+
userData2 = response.data.items[0];
|
|
4139
|
+
} else {
|
|
4140
|
+
console.log("\u{1F464} useUserData - No items found in object format");
|
|
4101
4141
|
}
|
|
4102
|
-
)
|
|
4103
|
-
|
|
4104
|
-
|
|
4105
|
-
|
|
4106
|
-
|
|
4107
|
-
|
|
4108
|
-
|
|
4109
|
-
{
|
|
4110
|
-
|
|
4111
|
-
|
|
4112
|
-
disabled: isSubmitting,
|
|
4113
|
-
onChange: (e) => {
|
|
4114
|
-
const newAction = e.target.value;
|
|
4115
|
-
const next = { ...policy, action: newAction };
|
|
4116
|
-
if (newAction === "delete") {
|
|
4117
|
-
next.permission = "deny";
|
|
4118
|
-
delete next.fields;
|
|
4119
|
-
} else {
|
|
4120
|
-
next.fields = { allow: [], owner_allow: [], deny: availableFields };
|
|
4121
|
-
delete next.permission;
|
|
4122
|
-
}
|
|
4123
|
-
onChange(next);
|
|
4124
|
-
},
|
|
4125
|
-
sx: {
|
|
4126
|
-
backgroundColor: "#ffffff",
|
|
4127
|
-
"&:hover .MuiOutlinedInput-notchedOutline": {
|
|
4128
|
-
borderColor: "#8c959f"
|
|
4129
|
-
},
|
|
4130
|
-
"&.Mui-focused .MuiOutlinedInput-notchedOutline": {
|
|
4131
|
-
borderColor: "#0969da",
|
|
4132
|
-
borderWidth: 2
|
|
4133
|
-
}
|
|
4134
|
-
},
|
|
4135
|
-
children: actionOptions.map((option) => {
|
|
4136
|
-
const disabledOption = takenActions.has(option.value);
|
|
4137
|
-
return /* @__PURE__ */ jsx14(MenuItem, { value: option.value, disabled: disabledOption, children: option.label }, option.value);
|
|
4138
|
-
})
|
|
4142
|
+
} else if (response.data?.data?.response?.data) {
|
|
4143
|
+
console.log("\u{1F464} useUserData - Found double-nested data.data.response.data format");
|
|
4144
|
+
try {
|
|
4145
|
+
const rawData = response.data.data.response.data;
|
|
4146
|
+
console.log("\u{1F464} useUserData - Raw double-nested data:", rawData);
|
|
4147
|
+
const parsedData = typeof rawData === "string" ? JSON.parse(rawData) : rawData;
|
|
4148
|
+
console.log("\u{1F464} useUserData - Parsed double-nested data:", parsedData);
|
|
4149
|
+
if (parsedData && parsedData.items && Array.isArray(parsedData.items) && parsedData.items.length > 0) {
|
|
4150
|
+
userData2 = parsedData.items[0];
|
|
4151
|
+
console.log("\u{1F464} useUserData - Extracted user from double-nested items:", userData2);
|
|
4139
4152
|
}
|
|
4140
|
-
)
|
|
4141
|
-
|
|
4142
|
-
] }) }) }),
|
|
4143
|
-
policy.action === "delete" ? /* @__PURE__ */ jsxs11(Box10, { children: [
|
|
4144
|
-
/* @__PURE__ */ jsx14(Typography10, { variant: "body2", color: "text.secondary", sx: { mb: 2 }, children: t("modules.form.publicPolicies.fields.conditions.label") }),
|
|
4145
|
-
/* @__PURE__ */ jsxs11(Stack2, { direction: "row", spacing: 1, sx: { mb: 3 }, children: [
|
|
4146
|
-
/* @__PURE__ */ jsx14(
|
|
4147
|
-
Button7,
|
|
4148
|
-
{
|
|
4149
|
-
variant: policy.permission === "*" ? "contained" : "outlined",
|
|
4150
|
-
startIcon: /* @__PURE__ */ jsx14(SelectAll2, {}),
|
|
4151
|
-
onClick: () => onChange({ ...policy, permission: "*" }),
|
|
4152
|
-
disabled: isSubmitting,
|
|
4153
|
-
size: "small",
|
|
4154
|
-
sx: {
|
|
4155
|
-
minWidth: 140,
|
|
4156
|
-
whiteSpace: "nowrap",
|
|
4157
|
-
...policy.permission === "*" && {
|
|
4158
|
-
backgroundColor: "#16a34a",
|
|
4159
|
-
"&:hover": { backgroundColor: "#15803d" }
|
|
4160
|
-
}
|
|
4161
|
-
},
|
|
4162
|
-
children: t("modules.form.publicPolicies.fields.conditions.allFields")
|
|
4163
|
-
}
|
|
4164
|
-
),
|
|
4165
|
-
/* @__PURE__ */ jsx14(
|
|
4166
|
-
Button7,
|
|
4167
|
-
{
|
|
4168
|
-
variant: policy.permission === "owner" ? "contained" : "outlined",
|
|
4169
|
-
onClick: () => onChange({ ...policy, permission: "owner" }),
|
|
4170
|
-
disabled: isSubmitting,
|
|
4171
|
-
size: "small",
|
|
4172
|
-
sx: {
|
|
4173
|
-
minWidth: 140,
|
|
4174
|
-
whiteSpace: "nowrap",
|
|
4175
|
-
...policy.permission === "owner" && {
|
|
4176
|
-
backgroundColor: "#0ea5e9",
|
|
4177
|
-
"&:hover": { backgroundColor: "#0284c7" }
|
|
4178
|
-
}
|
|
4179
|
-
},
|
|
4180
|
-
children: t("modules.form.publicPolicies.fields.conditions.states.ownerAllow")
|
|
4181
|
-
}
|
|
4182
|
-
),
|
|
4183
|
-
/* @__PURE__ */ jsx14(
|
|
4184
|
-
Button7,
|
|
4185
|
-
{
|
|
4186
|
-
variant: policy.permission === "deny" ? "contained" : "outlined",
|
|
4187
|
-
startIcon: /* @__PURE__ */ jsx14(ClearAll2, {}),
|
|
4188
|
-
onClick: () => onChange({ ...policy, permission: "deny" }),
|
|
4189
|
-
disabled: isSubmitting,
|
|
4190
|
-
size: "small",
|
|
4191
|
-
sx: {
|
|
4192
|
-
minWidth: 140,
|
|
4193
|
-
whiteSpace: "nowrap",
|
|
4194
|
-
...policy.permission === "deny" && {
|
|
4195
|
-
backgroundColor: "#cf222e",
|
|
4196
|
-
"&:hover": { backgroundColor: "#bc1f2c" }
|
|
4197
|
-
}
|
|
4198
|
-
},
|
|
4199
|
-
children: t("modules.form.publicPolicies.fields.conditions.noFields")
|
|
4200
|
-
}
|
|
4201
|
-
)
|
|
4202
|
-
] })
|
|
4203
|
-
] }) : /* @__PURE__ */ jsx14(
|
|
4204
|
-
FieldSelector_default,
|
|
4205
|
-
{
|
|
4206
|
-
value: policy.fields || { allow: [], owner_allow: [], deny: [] },
|
|
4207
|
-
onChange: (nextFields) => onChange({ ...policy, fields: nextFields }),
|
|
4208
|
-
availableFields,
|
|
4209
|
-
disabled: isSubmitting
|
|
4153
|
+
} catch (parseError) {
|
|
4154
|
+
console.error("\u{1F464} useUserData - Error parsing double-nested response data:", parseError);
|
|
4210
4155
|
}
|
|
4211
|
-
|
|
4212
|
-
|
|
4213
|
-
|
|
4214
|
-
|
|
4215
|
-
|
|
4216
|
-
|
|
4217
|
-
|
|
4218
|
-
|
|
4219
|
-
|
|
4220
|
-
|
|
4221
|
-
|
|
4222
|
-
|
|
4223
|
-
|
|
4224
|
-
|
|
4225
|
-
|
|
4226
|
-
|
|
4227
|
-
|
|
4228
|
-
|
|
4229
|
-
|
|
4230
|
-
|
|
4231
|
-
|
|
4232
|
-
|
|
4233
|
-
|
|
4234
|
-
|
|
4235
|
-
|
|
4236
|
-
|
|
4237
|
-
|
|
4238
|
-
|
|
4239
|
-
|
|
4240
|
-
|
|
4241
|
-
|
|
4242
|
-
|
|
4243
|
-
|
|
4244
|
-
|
|
4245
|
-
|
|
4246
|
-
|
|
4247
|
-
|
|
4248
|
-
|
|
4156
|
+
}
|
|
4157
|
+
}
|
|
4158
|
+
if (userData2) {
|
|
4159
|
+
console.log("\u{1F464} useUserData - User data found:", userData2);
|
|
4160
|
+
setUserData(userData2);
|
|
4161
|
+
setError(null);
|
|
4162
|
+
retryCountRef.current = 0;
|
|
4163
|
+
console.log("\u{1F464} useUserData - Profile loaded successfully:", userData2);
|
|
4164
|
+
} else {
|
|
4165
|
+
setError("User profile not found in database");
|
|
4166
|
+
setUserData(null);
|
|
4167
|
+
console.warn("\u{1F464} useUserData - User not found for email:", userEmail);
|
|
4168
|
+
}
|
|
4169
|
+
}
|
|
4170
|
+
} catch (err) {
|
|
4171
|
+
if (currentRequestId === requestIdRef.current && mountedRef.current) {
|
|
4172
|
+
const error2 = err;
|
|
4173
|
+
console.error("\u{1F464} useUserData - Error fetching profile:", error2);
|
|
4174
|
+
if (error2.name === "AbortError") {
|
|
4175
|
+
return;
|
|
4176
|
+
}
|
|
4177
|
+
const shouldRetry = retryOnError && retryCountRef.current < maxRetries && (error2.message?.includes("Network Error") || error2.message?.includes("Failed to fetch"));
|
|
4178
|
+
if (shouldRetry) {
|
|
4179
|
+
retryCountRef.current++;
|
|
4180
|
+
console.log(`\u{1F464} useUserData - Retrying profile fetch (${retryCountRef.current}/${maxRetries})`);
|
|
4181
|
+
setTimeout(() => {
|
|
4182
|
+
if (mountedRef.current) {
|
|
4183
|
+
refreshProfile();
|
|
4184
|
+
}
|
|
4185
|
+
}, 1e3 * retryCountRef.current);
|
|
4186
|
+
} else {
|
|
4187
|
+
setError("Failed to load user profile from database");
|
|
4188
|
+
setUserData(null);
|
|
4189
|
+
}
|
|
4190
|
+
}
|
|
4191
|
+
} finally {
|
|
4192
|
+
if (currentRequestId === requestIdRef.current && mountedRef.current) {
|
|
4193
|
+
setLoading(false);
|
|
4194
|
+
}
|
|
4195
|
+
if (abortControllerRef.current === abortController) {
|
|
4196
|
+
abortControllerRef.current = null;
|
|
4197
|
+
}
|
|
4249
4198
|
}
|
|
4250
|
-
);
|
|
4251
|
-
|
|
4252
|
-
|
|
4253
|
-
|
|
4254
|
-
|
|
4255
|
-
|
|
4256
|
-
|
|
4257
|
-
|
|
4258
|
-
|
|
4259
|
-
|
|
4260
|
-
|
|
4261
|
-
|
|
4262
|
-
|
|
4263
|
-
|
|
4264
|
-
|
|
4265
|
-
|
|
4266
|
-
isSubmitting = false
|
|
4267
|
-
}) => {
|
|
4268
|
-
const { t } = useTranslation4();
|
|
4269
|
-
const policyRefs = useRef6({});
|
|
4270
|
-
const takenActions = new Set((policies || []).map((p) => p.action).filter(Boolean));
|
|
4271
|
-
const remainingActions = PREFERRED_POLICY_ORDER.filter((a) => !takenActions.has(a));
|
|
4272
|
-
const canAddPolicy = remainingActions.length > 0;
|
|
4273
|
-
const addPolicy = () => {
|
|
4274
|
-
const defaultAction = remainingActions[0] || "create";
|
|
4275
|
-
const newPolicy = {
|
|
4276
|
-
id: generateId(),
|
|
4277
|
-
action: defaultAction
|
|
4199
|
+
}, [isInitialized, getUserEmail, retryOnError, maxRetries]);
|
|
4200
|
+
useEffect11(() => {
|
|
4201
|
+
if (autoFetch && isAuthenticated && isInitialized) {
|
|
4202
|
+
refreshProfile();
|
|
4203
|
+
} else if (!isAuthenticated) {
|
|
4204
|
+
clearProfile();
|
|
4205
|
+
}
|
|
4206
|
+
}, [autoFetch, isAuthenticated, isInitialized, refreshProfile, clearProfile]);
|
|
4207
|
+
useEffect11(() => {
|
|
4208
|
+
mountedRef.current = true;
|
|
4209
|
+
return () => {
|
|
4210
|
+
mountedRef.current = false;
|
|
4211
|
+
if (abortControllerRef.current) {
|
|
4212
|
+
abortControllerRef.current.abort();
|
|
4213
|
+
abortControllerRef.current = null;
|
|
4214
|
+
}
|
|
4278
4215
|
};
|
|
4279
|
-
|
|
4280
|
-
|
|
4216
|
+
}, []);
|
|
4217
|
+
const user = {
|
|
4218
|
+
session: sessionData,
|
|
4219
|
+
// Usar sessionData del nuevo sistema
|
|
4220
|
+
data: userData
|
|
4221
|
+
// Mantener userData del database igual que legacy
|
|
4222
|
+
};
|
|
4223
|
+
return {
|
|
4224
|
+
user,
|
|
4225
|
+
loading,
|
|
4226
|
+
error,
|
|
4227
|
+
refreshProfile,
|
|
4228
|
+
clearProfile
|
|
4229
|
+
};
|
|
4230
|
+
};
|
|
4231
|
+
|
|
4232
|
+
// src/hooks/useAuth.ts
|
|
4233
|
+
import { useCallback as useCallback7 } from "react";
|
|
4234
|
+
var useAuth = () => {
|
|
4235
|
+
const {
|
|
4236
|
+
isAuthenticated,
|
|
4237
|
+
isLoading,
|
|
4238
|
+
isInitialized,
|
|
4239
|
+
tokens,
|
|
4240
|
+
error,
|
|
4241
|
+
sessionData,
|
|
4242
|
+
login,
|
|
4243
|
+
logout,
|
|
4244
|
+
refreshTokens,
|
|
4245
|
+
clearError,
|
|
4246
|
+
getTokenInfo,
|
|
4247
|
+
isExpiringSoon,
|
|
4248
|
+
expiresIn,
|
|
4249
|
+
refreshExpiresIn
|
|
4250
|
+
} = useSessionContext();
|
|
4251
|
+
const setToken = useCallback7((token) => {
|
|
4252
|
+
if (token) {
|
|
4253
|
+
console.warn("useAuth.setToken() is deprecated. Use login() method instead for better security.");
|
|
4281
4254
|
} else {
|
|
4282
|
-
|
|
4283
|
-
|
|
4284
|
-
|
|
4285
|
-
|
|
4255
|
+
logout();
|
|
4256
|
+
}
|
|
4257
|
+
}, [logout]);
|
|
4258
|
+
const tokenExpiration = tokens?.expiresAt ? new Date(tokens.expiresAt) : null;
|
|
4259
|
+
return {
|
|
4260
|
+
// Estado básico (compatible con legacy)
|
|
4261
|
+
isAuthenticated,
|
|
4262
|
+
loading: isLoading,
|
|
4263
|
+
// En el nuevo sistema se llama isLoading
|
|
4264
|
+
error,
|
|
4265
|
+
// Datos del token (compatible con legacy + mejorado)
|
|
4266
|
+
token: tokens?.accessToken || null,
|
|
4267
|
+
user: sessionData,
|
|
4268
|
+
// sessionData del nuevo sistema (más rico que legacy)
|
|
4269
|
+
tokenExpiration,
|
|
4270
|
+
// Acciones (compatible con legacy + mejorado)
|
|
4271
|
+
setToken,
|
|
4272
|
+
logout,
|
|
4273
|
+
refreshToken: refreshTokens,
|
|
4274
|
+
// Funcionalidad real en el nuevo sistema
|
|
4275
|
+
// Nuevas funcionalidades del sistema de refresh tokens
|
|
4276
|
+
login,
|
|
4277
|
+
isExpiringSoon,
|
|
4278
|
+
expiresIn,
|
|
4279
|
+
refreshExpiresIn,
|
|
4280
|
+
getTokenInfo,
|
|
4281
|
+
clearError
|
|
4282
|
+
};
|
|
4283
|
+
};
|
|
4284
|
+
|
|
4285
|
+
// src/hooks/useData.ts
|
|
4286
|
+
import { useCallback as useCallback8 } from "react";
|
|
4287
|
+
import crudify8 from "@nocios/crudify-browser";
|
|
4288
|
+
var useData = () => {
|
|
4289
|
+
const {
|
|
4290
|
+
isInitialized,
|
|
4291
|
+
isLoading,
|
|
4292
|
+
error,
|
|
4293
|
+
isAuthenticated,
|
|
4294
|
+
login: sessionLogin
|
|
4295
|
+
} = useSessionContext();
|
|
4296
|
+
const isReady = useCallback8(() => {
|
|
4297
|
+
return isInitialized && !isLoading && !error;
|
|
4298
|
+
}, [isInitialized, isLoading, error]);
|
|
4299
|
+
const waitForReady = useCallback8(async () => {
|
|
4300
|
+
return new Promise((resolve, reject) => {
|
|
4301
|
+
const checkReady = () => {
|
|
4302
|
+
if (isReady()) {
|
|
4303
|
+
resolve();
|
|
4304
|
+
} else if (error) {
|
|
4305
|
+
reject(new Error(error));
|
|
4306
|
+
} else {
|
|
4307
|
+
setTimeout(checkReady, 100);
|
|
4308
|
+
}
|
|
4286
4309
|
};
|
|
4310
|
+
checkReady();
|
|
4311
|
+
});
|
|
4312
|
+
}, [isReady, error]);
|
|
4313
|
+
const ensureReady = useCallback8(async () => {
|
|
4314
|
+
if (!isReady()) {
|
|
4315
|
+
throw new Error("System not ready. Check isInitialized, isLoading, and error states.");
|
|
4287
4316
|
}
|
|
4288
|
-
|
|
4289
|
-
|
|
4290
|
-
|
|
4291
|
-
|
|
4292
|
-
|
|
4293
|
-
|
|
4294
|
-
|
|
4317
|
+
}, [isReady]);
|
|
4318
|
+
const readItems = useCallback8(async (moduleKey, filter, options) => {
|
|
4319
|
+
await ensureReady();
|
|
4320
|
+
return await crudify8.readItems(moduleKey, filter || {}, options);
|
|
4321
|
+
}, [ensureReady]);
|
|
4322
|
+
const readItem = useCallback8(async (moduleKey, filter, options) => {
|
|
4323
|
+
await ensureReady();
|
|
4324
|
+
return await crudify8.readItem(moduleKey, filter, options);
|
|
4325
|
+
}, [ensureReady]);
|
|
4326
|
+
const createItem = useCallback8(async (moduleKey, data, options) => {
|
|
4327
|
+
await ensureReady();
|
|
4328
|
+
return await crudify8.createItem(moduleKey, data, options);
|
|
4329
|
+
}, [ensureReady]);
|
|
4330
|
+
const updateItem = useCallback8(async (moduleKey, data, options) => {
|
|
4331
|
+
await ensureReady();
|
|
4332
|
+
return await crudify8.updateItem(moduleKey, data, options);
|
|
4333
|
+
}, [ensureReady]);
|
|
4334
|
+
const deleteItem = useCallback8(async (moduleKey, id, options) => {
|
|
4335
|
+
await ensureReady();
|
|
4336
|
+
return await crudify8.deleteItem(moduleKey, id, options);
|
|
4337
|
+
}, [ensureReady]);
|
|
4338
|
+
const transaction = useCallback8(async (operations, options) => {
|
|
4339
|
+
await ensureReady();
|
|
4340
|
+
return await crudify8.transaction(operations, options);
|
|
4341
|
+
}, [ensureReady]);
|
|
4342
|
+
const login = useCallback8(async (email, password) => {
|
|
4343
|
+
try {
|
|
4344
|
+
const result = await sessionLogin(email, password);
|
|
4345
|
+
if (result.success) {
|
|
4346
|
+
return {
|
|
4347
|
+
success: true,
|
|
4348
|
+
data: result.tokens
|
|
4349
|
+
};
|
|
4350
|
+
} else {
|
|
4351
|
+
return {
|
|
4352
|
+
success: false,
|
|
4353
|
+
errors: result.error || "Login failed"
|
|
4354
|
+
};
|
|
4295
4355
|
}
|
|
4296
|
-
}
|
|
4297
|
-
|
|
4298
|
-
|
|
4299
|
-
|
|
4300
|
-
|
|
4301
|
-
|
|
4356
|
+
} catch (error2) {
|
|
4357
|
+
return {
|
|
4358
|
+
success: false,
|
|
4359
|
+
errors: error2 instanceof Error ? error2.message : "Login failed"
|
|
4360
|
+
};
|
|
4361
|
+
}
|
|
4362
|
+
}, [sessionLogin]);
|
|
4363
|
+
return {
|
|
4364
|
+
// CRUD operations
|
|
4365
|
+
readItems,
|
|
4366
|
+
readItem,
|
|
4367
|
+
createItem,
|
|
4368
|
+
updateItem,
|
|
4369
|
+
deleteItem,
|
|
4370
|
+
transaction,
|
|
4371
|
+
login,
|
|
4372
|
+
// Estado
|
|
4373
|
+
isInitialized,
|
|
4374
|
+
isInitializing: isLoading,
|
|
4375
|
+
// El nuevo sistema usa isLoading
|
|
4376
|
+
initializationError: error,
|
|
4377
|
+
// Utilidades
|
|
4378
|
+
isReady,
|
|
4379
|
+
waitForReady
|
|
4302
4380
|
};
|
|
4303
|
-
const arrayError = (() => {
|
|
4304
|
-
if (!errors) return null;
|
|
4305
|
-
if (typeof errors === "string") return errors;
|
|
4306
|
-
const msg = errors._error;
|
|
4307
|
-
return typeof msg === "string" ? msg : null;
|
|
4308
|
-
})();
|
|
4309
|
-
const usedActions = new Set((policies || []).map((p) => p.action));
|
|
4310
|
-
return /* @__PURE__ */ jsxs12(Fragment8, { children: [
|
|
4311
|
-
/* @__PURE__ */ jsx15(Divider3, { sx: { borderColor: "#e0e4e7" } }),
|
|
4312
|
-
/* @__PURE__ */ jsxs12(Box11, { children: [
|
|
4313
|
-
/* @__PURE__ */ jsx15(Box11, { display: "flex", justifyContent: "space-between", alignItems: "center", mb: 3, children: /* @__PURE__ */ jsxs12(Box11, { children: [
|
|
4314
|
-
/* @__PURE__ */ jsx15(
|
|
4315
|
-
Typography11,
|
|
4316
|
-
{
|
|
4317
|
-
variant: "h6",
|
|
4318
|
-
sx: {
|
|
4319
|
-
fontWeight: 600,
|
|
4320
|
-
color: "#111418",
|
|
4321
|
-
mb: 1
|
|
4322
|
-
},
|
|
4323
|
-
children: t("modules.form.publicPolicies.title")
|
|
4324
|
-
}
|
|
4325
|
-
),
|
|
4326
|
-
/* @__PURE__ */ jsx15(
|
|
4327
|
-
Typography11,
|
|
4328
|
-
{
|
|
4329
|
-
variant: "body2",
|
|
4330
|
-
color: "text.secondary",
|
|
4331
|
-
sx: { fontSize: "0.875rem" },
|
|
4332
|
-
children: t("modules.form.publicPolicies.description")
|
|
4333
|
-
}
|
|
4334
|
-
)
|
|
4335
|
-
] }) }),
|
|
4336
|
-
arrayError && /* @__PURE__ */ jsx15(Alert8, { severity: "error", sx: { mb: 3 }, children: arrayError }),
|
|
4337
|
-
/* @__PURE__ */ jsxs12(Stack3, { spacing: 3, children: [
|
|
4338
|
-
(policies || []).length === 0 ? /* @__PURE__ */ jsx15(Alert8, { severity: "info", children: t("modules.form.publicPolicies.noPolicies") }) : policies.map((policy, index) => /* @__PURE__ */ jsx15(
|
|
4339
|
-
PolicyItem_default,
|
|
4340
|
-
{
|
|
4341
|
-
ref: (el) => {
|
|
4342
|
-
policyRefs.current[index] = el;
|
|
4343
|
-
},
|
|
4344
|
-
policy,
|
|
4345
|
-
onChange: (nextPolicy) => {
|
|
4346
|
-
const next = [...policies];
|
|
4347
|
-
next[index] = nextPolicy;
|
|
4348
|
-
onChange(next);
|
|
4349
|
-
},
|
|
4350
|
-
onRemove: () => removePolicy(index),
|
|
4351
|
-
availableFields,
|
|
4352
|
-
isSubmitting,
|
|
4353
|
-
usedActions,
|
|
4354
|
-
error: typeof errors === "object" && errors && policy.id in errors ? errors[policy.id] : void 0
|
|
4355
|
-
},
|
|
4356
|
-
policy.id
|
|
4357
|
-
)),
|
|
4358
|
-
canAddPolicy && /* @__PURE__ */ jsx15(Box11, { children: /* @__PURE__ */ jsx15(
|
|
4359
|
-
Button8,
|
|
4360
|
-
{
|
|
4361
|
-
type: "button",
|
|
4362
|
-
variant: "outlined",
|
|
4363
|
-
startIcon: /* @__PURE__ */ jsx15(Add, {}),
|
|
4364
|
-
onClick: addPolicy,
|
|
4365
|
-
disabled: isSubmitting,
|
|
4366
|
-
sx: {
|
|
4367
|
-
borderColor: "#d0d7de",
|
|
4368
|
-
color: "#656d76",
|
|
4369
|
-
"&:hover": {
|
|
4370
|
-
borderColor: "#8c959f",
|
|
4371
|
-
backgroundColor: "transparent"
|
|
4372
|
-
}
|
|
4373
|
-
},
|
|
4374
|
-
children: t("modules.form.publicPolicies.addPolicy")
|
|
4375
|
-
}
|
|
4376
|
-
) })
|
|
4377
|
-
] })
|
|
4378
|
-
] })
|
|
4379
|
-
] });
|
|
4380
4381
|
};
|
|
4381
|
-
var Policies_default = Policies;
|
|
4382
4382
|
export {
|
|
4383
4383
|
CrudifyDataProvider,
|
|
4384
4384
|
CrudifyLogin_default as CrudifyLogin,
|