@nocios/crudify-ui 1.3.2 → 1.3.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.d.mts CHANGED
@@ -1330,4 +1330,30 @@ interface UseDataReturn {
1330
1330
  */
1331
1331
  declare const useData: () => UseDataReturn;
1332
1332
 
1333
- export { type ApiError, type BoxScreenType, type CrudifyApiResponse, type CrudifyConfig, type CrudifyDataContextState, CrudifyDataProvider, type CrudifyDataProviderProps, CrudifyLogin, type CrudifyLoginConfig, type CrudifyLoginProps, type CrudifyLoginTranslations, type CrudifyTransactionResponse, type CrudifyUserData, ERROR_CODES, ERROR_SEVERITY_MAP, type ErrorCode, type ErrorSeverity, type ForgotPasswordRequest, type JWTPayload$1 as JWTPayload, type JwtPayload, LoginComponent, type LoginRequest, type LoginResponse, type LoginResult, type ParsedError, ProtectedRoute, type ProtectedRouteProps, type ResetPasswordRequest, type ResolvedConfig, type SessionConfig, SessionDebugInfo, SessionManager, SessionProvider, type SessionProviderProps, type SessionState, SessionStatus, type StorageType, type TokenData, TokenStorage, type TransactionResponseData, type UseAuthReturn, type UseCrudifyAuthReturn, type UseCrudifyConfigReturn, type UseCrudifyDataReturn, type UseCrudifyInstanceReturn, type UseCrudifyUserOptions, type UseCrudifyUserReturn, type UseDataReturn, type UseSessionOptions, type UseUserDataOptions, type UseUserDataReturn, type UserData, type UserLoginData, type UserProfile, UserProfileDisplay, type ValidateCodeRequest, type ValidationError, configurationManager, crudifyInitializer, decodeJwtSafely, getCookie, getCrudifyInstanceAsync, getCrudifyInstanceSync, getCurrentUserEmail, getErrorMessage, handleCrudifyError, isTokenExpired, parseApiError, parseJavaScriptError, parseTransactionError, secureLocalStorage, secureSessionStorage, tokenManager, useAuth, useCrudifyAuth, useCrudifyConfig, useCrudifyData, useCrudifyDataContext, useCrudifyInstance, useCrudifyLogin, useCrudifyUser, useData, useSession, useSessionContext, useUserData, useUserProfile };
1333
+ declare const POLICY_ACTIONS: readonly ["create", "read", "update", "delete"];
1334
+ type PolicyAction = typeof POLICY_ACTIONS[number];
1335
+ declare const PREFERRED_POLICY_ORDER: PolicyAction[];
1336
+
1337
+ type Policy = {
1338
+ id: string;
1339
+ action: PolicyAction;
1340
+ fields?: {
1341
+ allow: string[];
1342
+ owner_allow: string[];
1343
+ deny: string[];
1344
+ };
1345
+ permission?: string;
1346
+ };
1347
+ type FieldErrorMap = string | ({
1348
+ _error?: string;
1349
+ } & Record<string, string | undefined>);
1350
+ interface PoliciesProps {
1351
+ policies: Policy[];
1352
+ onChange: (next: Policy[]) => void;
1353
+ availableFields: string[];
1354
+ errors?: FieldErrorMap;
1355
+ isSubmitting?: boolean;
1356
+ }
1357
+ declare const Policies: React.FC<PoliciesProps>;
1358
+
1359
+ export { type ApiError, type BoxScreenType, type CrudifyApiResponse, type CrudifyConfig, type CrudifyDataContextState, CrudifyDataProvider, type CrudifyDataProviderProps, CrudifyLogin, type CrudifyLoginConfig, type CrudifyLoginProps, type CrudifyLoginTranslations, type CrudifyTransactionResponse, type CrudifyUserData, ERROR_CODES, ERROR_SEVERITY_MAP, type ErrorCode, type ErrorSeverity, type ForgotPasswordRequest, type JWTPayload$1 as JWTPayload, type JwtPayload, LoginComponent, type LoginRequest, type LoginResponse, type LoginResult, POLICY_ACTIONS, PREFERRED_POLICY_ORDER, type ParsedError, Policies, type PolicyAction, ProtectedRoute, type ProtectedRouteProps, type ResetPasswordRequest, type ResolvedConfig, type SessionConfig, SessionDebugInfo, SessionManager, SessionProvider, type SessionProviderProps, type SessionState, SessionStatus, type StorageType, type TokenData, TokenStorage, type TransactionResponseData, type UseAuthReturn, type UseCrudifyAuthReturn, type UseCrudifyConfigReturn, type UseCrudifyDataReturn, type UseCrudifyInstanceReturn, type UseCrudifyUserOptions, type UseCrudifyUserReturn, type UseDataReturn, type UseSessionOptions, type UseUserDataOptions, type UseUserDataReturn, type UserData, type UserLoginData, type UserProfile, UserProfileDisplay, type ValidateCodeRequest, type ValidationError, configurationManager, crudifyInitializer, decodeJwtSafely, getCookie, getCrudifyInstanceAsync, getCrudifyInstanceSync, getCurrentUserEmail, getErrorMessage, handleCrudifyError, isTokenExpired, parseApiError, parseJavaScriptError, parseTransactionError, secureLocalStorage, secureSessionStorage, tokenManager, useAuth, useCrudifyAuth, useCrudifyConfig, useCrudifyData, useCrudifyDataContext, useCrudifyInstance, useCrudifyLogin, useCrudifyUser, useData, useSession, useSessionContext, useUserData, useUserProfile };
package/dist/index.d.ts CHANGED
@@ -1330,4 +1330,30 @@ interface UseDataReturn {
1330
1330
  */
1331
1331
  declare const useData: () => UseDataReturn;
1332
1332
 
1333
- export { type ApiError, type BoxScreenType, type CrudifyApiResponse, type CrudifyConfig, type CrudifyDataContextState, CrudifyDataProvider, type CrudifyDataProviderProps, CrudifyLogin, type CrudifyLoginConfig, type CrudifyLoginProps, type CrudifyLoginTranslations, type CrudifyTransactionResponse, type CrudifyUserData, ERROR_CODES, ERROR_SEVERITY_MAP, type ErrorCode, type ErrorSeverity, type ForgotPasswordRequest, type JWTPayload$1 as JWTPayload, type JwtPayload, LoginComponent, type LoginRequest, type LoginResponse, type LoginResult, type ParsedError, ProtectedRoute, type ProtectedRouteProps, type ResetPasswordRequest, type ResolvedConfig, type SessionConfig, SessionDebugInfo, SessionManager, SessionProvider, type SessionProviderProps, type SessionState, SessionStatus, type StorageType, type TokenData, TokenStorage, type TransactionResponseData, type UseAuthReturn, type UseCrudifyAuthReturn, type UseCrudifyConfigReturn, type UseCrudifyDataReturn, type UseCrudifyInstanceReturn, type UseCrudifyUserOptions, type UseCrudifyUserReturn, type UseDataReturn, type UseSessionOptions, type UseUserDataOptions, type UseUserDataReturn, type UserData, type UserLoginData, type UserProfile, UserProfileDisplay, type ValidateCodeRequest, type ValidationError, configurationManager, crudifyInitializer, decodeJwtSafely, getCookie, getCrudifyInstanceAsync, getCrudifyInstanceSync, getCurrentUserEmail, getErrorMessage, handleCrudifyError, isTokenExpired, parseApiError, parseJavaScriptError, parseTransactionError, secureLocalStorage, secureSessionStorage, tokenManager, useAuth, useCrudifyAuth, useCrudifyConfig, useCrudifyData, useCrudifyDataContext, useCrudifyInstance, useCrudifyLogin, useCrudifyUser, useData, useSession, useSessionContext, useUserData, useUserProfile };
1333
+ declare const POLICY_ACTIONS: readonly ["create", "read", "update", "delete"];
1334
+ type PolicyAction = typeof POLICY_ACTIONS[number];
1335
+ declare const PREFERRED_POLICY_ORDER: PolicyAction[];
1336
+
1337
+ type Policy = {
1338
+ id: string;
1339
+ action: PolicyAction;
1340
+ fields?: {
1341
+ allow: string[];
1342
+ owner_allow: string[];
1343
+ deny: string[];
1344
+ };
1345
+ permission?: string;
1346
+ };
1347
+ type FieldErrorMap = string | ({
1348
+ _error?: string;
1349
+ } & Record<string, string | undefined>);
1350
+ interface PoliciesProps {
1351
+ policies: Policy[];
1352
+ onChange: (next: Policy[]) => void;
1353
+ availableFields: string[];
1354
+ errors?: FieldErrorMap;
1355
+ isSubmitting?: boolean;
1356
+ }
1357
+ declare const Policies: React.FC<PoliciesProps>;
1358
+
1359
+ export { type ApiError, type BoxScreenType, type CrudifyApiResponse, type CrudifyConfig, type CrudifyDataContextState, CrudifyDataProvider, type CrudifyDataProviderProps, CrudifyLogin, type CrudifyLoginConfig, type CrudifyLoginProps, type CrudifyLoginTranslations, type CrudifyTransactionResponse, type CrudifyUserData, ERROR_CODES, ERROR_SEVERITY_MAP, type ErrorCode, type ErrorSeverity, type ForgotPasswordRequest, type JWTPayload$1 as JWTPayload, type JwtPayload, LoginComponent, type LoginRequest, type LoginResponse, type LoginResult, POLICY_ACTIONS, PREFERRED_POLICY_ORDER, type ParsedError, Policies, type PolicyAction, ProtectedRoute, type ProtectedRouteProps, type ResetPasswordRequest, type ResolvedConfig, type SessionConfig, SessionDebugInfo, SessionManager, SessionProvider, type SessionProviderProps, type SessionState, SessionStatus, type StorageType, type TokenData, TokenStorage, type TransactionResponseData, type UseAuthReturn, type UseCrudifyAuthReturn, type UseCrudifyConfigReturn, type UseCrudifyDataReturn, type UseCrudifyInstanceReturn, type UseCrudifyUserOptions, type UseCrudifyUserReturn, type UseDataReturn, type UseSessionOptions, type UseUserDataOptions, type UseUserDataReturn, type UserData, type UserLoginData, type UserProfile, UserProfileDisplay, type ValidateCodeRequest, type ValidationError, configurationManager, crudifyInitializer, decodeJwtSafely, getCookie, getCrudifyInstanceAsync, getCrudifyInstanceSync, getCurrentUserEmail, getErrorMessage, handleCrudifyError, isTokenExpired, parseApiError, parseJavaScriptError, parseTransactionError, secureLocalStorage, secureSessionStorage, tokenManager, useAuth, useCrudifyAuth, useCrudifyConfig, useCrudifyData, useCrudifyDataContext, useCrudifyInstance, useCrudifyLogin, useCrudifyUser, useData, useSession, useSessionContext, useUserData, useUserProfile };
package/dist/index.js CHANGED
@@ -1290,6 +1290,9 @@ __export(index_exports, {
1290
1290
  ERROR_CODES: () => ERROR_CODES,
1291
1291
  ERROR_SEVERITY_MAP: () => ERROR_SEVERITY_MAP,
1292
1292
  LoginComponent: () => LoginComponent,
1293
+ POLICY_ACTIONS: () => POLICY_ACTIONS,
1294
+ PREFERRED_POLICY_ORDER: () => PREFERRED_POLICY_ORDER,
1295
+ Policies: () => Policies_default,
1293
1296
  ProtectedRoute: () => ProtectedRoute,
1294
1297
  SessionDebugInfo: () => SessionDebugInfo,
1295
1298
  SessionManager: () => SessionManager,
@@ -5074,6 +5077,585 @@ var useData = () => {
5074
5077
  waitForReady
5075
5078
  };
5076
5079
  };
5080
+
5081
+ // src/components/PublicPolicies/Policies.tsx
5082
+ var import_react24 = require("react");
5083
+ var import_react_i18next3 = require("react-i18next");
5084
+ var import_material11 = require("@mui/material");
5085
+ var import_icons_material4 = require("@mui/icons-material");
5086
+
5087
+ // src/components/PublicPolicies/PolicyItem/PolicyItem.tsx
5088
+ var import_react23 = require("react");
5089
+ var import_react_i18next2 = require("react-i18next");
5090
+ var import_material10 = require("@mui/material");
5091
+ var import_icons_material3 = require("@mui/icons-material");
5092
+
5093
+ // src/components/PublicPolicies/FieldSelector/FieldSelector.tsx
5094
+ var import_react22 = require("react");
5095
+ var import_react_i18next = require("react-i18next");
5096
+ var import_material9 = require("@mui/material");
5097
+ var import_icons_material2 = require("@mui/icons-material");
5098
+ var import_jsx_runtime14 = require("react/jsx-runtime");
5099
+ var FieldSelector = ({
5100
+ value,
5101
+ onChange,
5102
+ availableFields,
5103
+ error,
5104
+ disabled = false
5105
+ }) => {
5106
+ const { t } = (0, import_react_i18next.useTranslation)();
5107
+ const [mode, setMode] = (0, import_react22.useState)("custom");
5108
+ const isUpdatingRef = (0, import_react22.useRef)(false);
5109
+ (0, import_react22.useEffect)(() => {
5110
+ const current = value || { allow: [], owner_allow: [], deny: [] };
5111
+ const all = new Set(availableFields);
5112
+ const allow = (current.allow || []).filter((f) => all.has(f));
5113
+ const owner = (current.owner_allow || []).filter((f) => all.has(f));
5114
+ const deny = (current.deny || []).filter((f) => all.has(f));
5115
+ availableFields.forEach((f) => {
5116
+ if (!allow.includes(f) && !owner.includes(f) && !deny.includes(f)) deny.push(f);
5117
+ });
5118
+ const normalized = { allow, owner_allow: owner, deny };
5119
+ if (JSON.stringify(normalized) !== JSON.stringify(current)) {
5120
+ onChange(normalized);
5121
+ }
5122
+ if (allow.length === availableFields.length) setMode("all");
5123
+ else if (deny.length === availableFields.length) setMode("none");
5124
+ else setMode("custom");
5125
+ }, [availableFields, value]);
5126
+ const setAllAllow = () => {
5127
+ isUpdatingRef.current = true;
5128
+ onChange({ allow: [...availableFields], owner_allow: [], deny: [] });
5129
+ setMode("all");
5130
+ setTimeout(() => {
5131
+ isUpdatingRef.current = false;
5132
+ }, 0);
5133
+ };
5134
+ const setAllDeny = () => {
5135
+ isUpdatingRef.current = true;
5136
+ onChange({ allow: [], owner_allow: [], deny: [...availableFields] });
5137
+ setMode("none");
5138
+ setTimeout(() => {
5139
+ isUpdatingRef.current = false;
5140
+ }, 0);
5141
+ };
5142
+ const getFieldState = (fieldName) => {
5143
+ if (value?.allow?.includes(fieldName)) return "allow";
5144
+ if (value?.owner_allow?.includes(fieldName)) return "owner_allow";
5145
+ return "deny";
5146
+ };
5147
+ const setFieldState = (fieldName, state) => {
5148
+ isUpdatingRef.current = true;
5149
+ const allow = new Set(value?.allow || []);
5150
+ const owner = new Set(value?.owner_allow || []);
5151
+ const deny = new Set(value?.deny || []);
5152
+ allow.delete(fieldName);
5153
+ owner.delete(fieldName);
5154
+ deny.delete(fieldName);
5155
+ if (state === "allow") allow.add(fieldName);
5156
+ if (state === "owner_allow") owner.add(fieldName);
5157
+ if (state === "deny") deny.add(fieldName);
5158
+ onChange({ allow: Array.from(allow), owner_allow: Array.from(owner), deny: Array.from(deny) });
5159
+ setMode("custom");
5160
+ setTimeout(() => {
5161
+ isUpdatingRef.current = false;
5162
+ }, 0);
5163
+ };
5164
+ if (availableFields.length === 0) {
5165
+ return /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)(import_material9.Box, { children: [
5166
+ /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(import_material9.Typography, { variant: "body2", color: "text.secondary", sx: { mb: 1 }, children: t("modules.form.publicPolicies.fields.conditions.label") }),
5167
+ /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(import_material9.Typography, { variant: "body2", color: "text.secondary", sx: { fontStyle: "italic" }, children: t("modules.form.publicPolicies.fields.conditions.noFieldsAvailable") }),
5168
+ error && /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(import_material9.FormHelperText, { error: true, sx: { mt: 1 }, children: error })
5169
+ ] });
5170
+ }
5171
+ return /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)(import_material9.Box, { children: [
5172
+ /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(import_material9.Typography, { variant: "body2", color: "text.secondary", sx: { mb: 2 }, children: t("modules.form.publicPolicies.fields.conditions.label") }),
5173
+ /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)(import_material9.Stack, { direction: "row", spacing: 1, sx: { mb: 3 }, children: [
5174
+ /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(
5175
+ import_material9.Button,
5176
+ {
5177
+ variant: mode === "all" ? "contained" : "outlined",
5178
+ startIcon: /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(import_icons_material2.SelectAll, {}),
5179
+ onClick: setAllAllow,
5180
+ disabled,
5181
+ size: "small",
5182
+ sx: {
5183
+ minWidth: 120,
5184
+ ...mode === "all" && {
5185
+ backgroundColor: "#16a34a",
5186
+ "&:hover": { backgroundColor: "#15803d" }
5187
+ }
5188
+ },
5189
+ children: t("modules.form.publicPolicies.fields.conditions.allFields")
5190
+ }
5191
+ ),
5192
+ /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(
5193
+ import_material9.Button,
5194
+ {
5195
+ variant: mode === "none" ? "contained" : "outlined",
5196
+ startIcon: /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(import_icons_material2.ClearAll, {}),
5197
+ onClick: setAllDeny,
5198
+ disabled,
5199
+ size: "small",
5200
+ sx: {
5201
+ minWidth: 120,
5202
+ ...mode === "none" && {
5203
+ backgroundColor: "#cf222e",
5204
+ "&:hover": { backgroundColor: "#bc1f2c" }
5205
+ }
5206
+ },
5207
+ children: t("modules.form.publicPolicies.fields.conditions.noFields")
5208
+ }
5209
+ )
5210
+ ] }),
5211
+ /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)(import_material9.Box, { sx: { p: 2, border: "1px solid #d1d9e0", borderRadius: 1, backgroundColor: "#f6f8fa" }, children: [
5212
+ /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(import_material9.Typography, { variant: "body2", color: "text.secondary", sx: { mb: 2 }, children: t("modules.form.publicPolicies.fields.conditions.help") }),
5213
+ /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(import_material9.Stack, { spacing: 1, children: availableFields.map((fieldName) => {
5214
+ const fieldState = getFieldState(fieldName);
5215
+ return /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)(import_material9.Stack, { direction: "row", spacing: 1, alignItems: "center", children: [
5216
+ /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(import_material9.Typography, { variant: "body2", sx: { minWidth: 100, fontFamily: "monospace" }, children: fieldName }),
5217
+ /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)(import_material9.ToggleButtonGroup, { value: fieldState, exclusive: true, size: "small", children: [
5218
+ /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)(
5219
+ import_material9.ToggleButton,
5220
+ {
5221
+ value: "allow",
5222
+ onClick: () => setFieldState(fieldName, "allow"),
5223
+ disabled,
5224
+ sx: {
5225
+ px: 2,
5226
+ color: fieldState === "allow" ? "#ffffff" : "#6b7280",
5227
+ backgroundColor: fieldState === "allow" ? "#16a34a" : "#f3f4f6",
5228
+ borderColor: fieldState === "allow" ? "#16a34a" : "#d1d5db",
5229
+ "&:hover": {
5230
+ backgroundColor: fieldState === "allow" ? "#15803d" : "#e5e7eb",
5231
+ borderColor: fieldState === "allow" ? "#15803d" : "#9ca3af"
5232
+ },
5233
+ "&.Mui-selected": {
5234
+ backgroundColor: "#16a34a",
5235
+ color: "#ffffff",
5236
+ "&:hover": {
5237
+ backgroundColor: "#15803d"
5238
+ }
5239
+ }
5240
+ },
5241
+ children: [
5242
+ /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(import_icons_material2.CheckCircle, { sx: { fontSize: 16, mr: 0.5 } }),
5243
+ t("modules.form.publicPolicies.fields.conditions.states.allow")
5244
+ ]
5245
+ }
5246
+ ),
5247
+ /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(
5248
+ import_material9.ToggleButton,
5249
+ {
5250
+ value: "owner_allow",
5251
+ onClick: () => setFieldState(fieldName, "owner_allow"),
5252
+ disabled,
5253
+ sx: {
5254
+ px: 2,
5255
+ color: fieldState === "owner_allow" ? "#ffffff" : "#6b7280",
5256
+ backgroundColor: fieldState === "owner_allow" ? "#0ea5e9" : "#f3f4f6",
5257
+ borderColor: fieldState === "owner_allow" ? "#0ea5e9" : "#d1d5db",
5258
+ "&:hover": {
5259
+ backgroundColor: fieldState === "owner_allow" ? "#0284c7" : "#e5e7eb",
5260
+ borderColor: fieldState === "owner_allow" ? "#0284c7" : "#9ca3af"
5261
+ },
5262
+ "&.Mui-selected": {
5263
+ backgroundColor: "#0ea5e9",
5264
+ color: "#ffffff",
5265
+ "&:hover": {
5266
+ backgroundColor: "#0284c7"
5267
+ }
5268
+ }
5269
+ },
5270
+ children: t("modules.form.publicPolicies.fields.conditions.states.ownerAllow")
5271
+ }
5272
+ ),
5273
+ /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)(
5274
+ import_material9.ToggleButton,
5275
+ {
5276
+ value: "deny",
5277
+ onClick: () => setFieldState(fieldName, "deny"),
5278
+ disabled,
5279
+ sx: {
5280
+ px: 2,
5281
+ color: fieldState === "deny" ? "#ffffff" : "#6b7280",
5282
+ backgroundColor: fieldState === "deny" ? "#dc2626" : "#f3f4f6",
5283
+ borderColor: fieldState === "deny" ? "#dc2626" : "#d1d5db",
5284
+ "&:hover": {
5285
+ backgroundColor: fieldState === "deny" ? "#b91c1c" : "#e5e7eb",
5286
+ borderColor: fieldState === "deny" ? "#b91c1c" : "#9ca3af"
5287
+ },
5288
+ "&.Mui-selected": {
5289
+ backgroundColor: "#dc2626",
5290
+ color: "#ffffff",
5291
+ "&:hover": {
5292
+ backgroundColor: "#b91c1c"
5293
+ }
5294
+ }
5295
+ },
5296
+ children: [
5297
+ /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(import_icons_material2.Cancel, { sx: { fontSize: 16, mr: 0.5 } }),
5298
+ t("modules.form.publicPolicies.fields.conditions.states.deny")
5299
+ ]
5300
+ }
5301
+ )
5302
+ ] })
5303
+ ] }, fieldName);
5304
+ }) })
5305
+ ] }),
5306
+ error && /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(import_material9.FormHelperText, { error: true, sx: { mt: 1 }, children: error })
5307
+ ] });
5308
+ };
5309
+ var FieldSelector_default = FieldSelector;
5310
+
5311
+ // src/components/PublicPolicies/constants.ts
5312
+ var POLICY_ACTIONS = ["create", "read", "update", "delete"];
5313
+ var PREFERRED_POLICY_ORDER = [
5314
+ "create",
5315
+ "read",
5316
+ "update",
5317
+ "delete"
5318
+ ];
5319
+
5320
+ // src/components/PublicPolicies/PolicyItem/PolicyItem.tsx
5321
+ var import_jsx_runtime15 = require("react/jsx-runtime");
5322
+ var PolicyItem = (0, import_react23.forwardRef)(({
5323
+ policy,
5324
+ onChange,
5325
+ onRemove,
5326
+ availableFields,
5327
+ isSubmitting = false,
5328
+ usedActions,
5329
+ error
5330
+ }, ref) => {
5331
+ const { t } = (0, import_react_i18next2.useTranslation)();
5332
+ const takenActions = new Set(Array.from(usedActions || []));
5333
+ takenActions.delete(policy.action);
5334
+ const actionOptions = POLICY_ACTIONS.map((a) => ({
5335
+ value: a,
5336
+ label: t(`modules.form.publicPolicies.fields.action.options.${a}`)
5337
+ }));
5338
+ return /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)(
5339
+ import_material10.Paper,
5340
+ {
5341
+ ref,
5342
+ sx: {
5343
+ p: 3,
5344
+ border: "1px solid #d1d9e0",
5345
+ borderRadius: 2,
5346
+ position: "relative",
5347
+ backgroundColor: "#ffffff"
5348
+ },
5349
+ children: [
5350
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)(import_material10.Box, { sx: { display: "flex", justifyContent: "space-between", alignItems: "flex-start", mb: 3 }, children: [
5351
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
5352
+ import_material10.Typography,
5353
+ {
5354
+ variant: "subtitle1",
5355
+ sx: {
5356
+ fontWeight: 600,
5357
+ color: "#111418",
5358
+ fontSize: "1rem"
5359
+ },
5360
+ children: t("modules.form.publicPolicies.policyTitle")
5361
+ }
5362
+ ),
5363
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
5364
+ import_material10.IconButton,
5365
+ {
5366
+ onClick: onRemove,
5367
+ size: "small",
5368
+ disabled: isSubmitting,
5369
+ "aria-label": t("modules.form.publicPolicies.removePolicy"),
5370
+ sx: {
5371
+ color: "#656d76",
5372
+ "&:hover": {
5373
+ color: "#cf222e",
5374
+ backgroundColor: "rgba(207, 34, 46, 0.1)"
5375
+ }
5376
+ },
5377
+ children: /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(import_icons_material3.Delete, {})
5378
+ }
5379
+ )
5380
+ ] }),
5381
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)(import_material10.Stack, { spacing: 3, children: [
5382
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(import_material10.Stack, { direction: { xs: "column", md: "row" }, spacing: 2, children: /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(import_material10.Box, { sx: { flex: 1, minWidth: 200 }, children: /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)(import_material10.FormControl, { fullWidth: true, children: [
5383
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(import_material10.InputLabel, { children: t("modules.form.publicPolicies.fields.action.label") }),
5384
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
5385
+ import_material10.Select,
5386
+ {
5387
+ value: policy.action,
5388
+ label: t("modules.form.publicPolicies.fields.action.label"),
5389
+ disabled: isSubmitting,
5390
+ onChange: (e) => {
5391
+ const newAction = e.target.value;
5392
+ const next = { ...policy, action: newAction };
5393
+ if (newAction === "delete") {
5394
+ next.permission = "deny";
5395
+ delete next.fields;
5396
+ } else {
5397
+ next.fields = { allow: [], owner_allow: [], deny: availableFields };
5398
+ delete next.permission;
5399
+ }
5400
+ onChange(next);
5401
+ },
5402
+ sx: {
5403
+ backgroundColor: "#ffffff",
5404
+ "&:hover .MuiOutlinedInput-notchedOutline": {
5405
+ borderColor: "#8c959f"
5406
+ },
5407
+ "&.Mui-focused .MuiOutlinedInput-notchedOutline": {
5408
+ borderColor: "#0969da",
5409
+ borderWidth: 2
5410
+ }
5411
+ },
5412
+ children: actionOptions.map((option) => {
5413
+ const disabledOption = takenActions.has(option.value);
5414
+ return /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(import_material10.MenuItem, { value: option.value, disabled: disabledOption, children: option.label }, option.value);
5415
+ })
5416
+ }
5417
+ ),
5418
+ error && /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(import_material10.FormHelperText, { error: true, children: error })
5419
+ ] }) }) }),
5420
+ policy.action === "delete" ? /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)(import_material10.Box, { children: [
5421
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(import_material10.Typography, { variant: "body2", color: "text.secondary", sx: { mb: 2 }, children: t("modules.form.publicPolicies.fields.conditions.label") }),
5422
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)(import_material10.Stack, { direction: "row", spacing: 1, sx: { mb: 3 }, children: [
5423
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
5424
+ import_material10.Button,
5425
+ {
5426
+ variant: policy.permission === "*" ? "contained" : "outlined",
5427
+ startIcon: /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(import_icons_material3.SelectAll, {}),
5428
+ onClick: () => onChange({ ...policy, permission: "*" }),
5429
+ disabled: isSubmitting,
5430
+ size: "small",
5431
+ sx: {
5432
+ minWidth: 140,
5433
+ whiteSpace: "nowrap",
5434
+ ...policy.permission === "*" && {
5435
+ backgroundColor: "#16a34a",
5436
+ "&:hover": { backgroundColor: "#15803d" }
5437
+ }
5438
+ },
5439
+ children: t("modules.form.publicPolicies.fields.conditions.allFields")
5440
+ }
5441
+ ),
5442
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
5443
+ import_material10.Button,
5444
+ {
5445
+ variant: policy.permission === "owner" ? "contained" : "outlined",
5446
+ onClick: () => onChange({ ...policy, permission: "owner" }),
5447
+ disabled: isSubmitting,
5448
+ size: "small",
5449
+ sx: {
5450
+ minWidth: 140,
5451
+ whiteSpace: "nowrap",
5452
+ ...policy.permission === "owner" && {
5453
+ backgroundColor: "#0ea5e9",
5454
+ "&:hover": { backgroundColor: "#0284c7" }
5455
+ }
5456
+ },
5457
+ children: t("modules.form.publicPolicies.fields.conditions.states.ownerAllow")
5458
+ }
5459
+ ),
5460
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
5461
+ import_material10.Button,
5462
+ {
5463
+ variant: policy.permission === "deny" ? "contained" : "outlined",
5464
+ startIcon: /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(import_icons_material3.ClearAll, {}),
5465
+ onClick: () => onChange({ ...policy, permission: "deny" }),
5466
+ disabled: isSubmitting,
5467
+ size: "small",
5468
+ sx: {
5469
+ minWidth: 140,
5470
+ whiteSpace: "nowrap",
5471
+ ...policy.permission === "deny" && {
5472
+ backgroundColor: "#cf222e",
5473
+ "&:hover": { backgroundColor: "#bc1f2c" }
5474
+ }
5475
+ },
5476
+ children: t("modules.form.publicPolicies.fields.conditions.noFields")
5477
+ }
5478
+ )
5479
+ ] })
5480
+ ] }) : /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
5481
+ FieldSelector_default,
5482
+ {
5483
+ value: policy.fields || { allow: [], owner_allow: [], deny: [] },
5484
+ onChange: (nextFields) => onChange({ ...policy, fields: nextFields }),
5485
+ availableFields,
5486
+ disabled: isSubmitting
5487
+ }
5488
+ ),
5489
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(import_material10.Paper, { variant: "outlined", sx: { p: 2, backgroundColor: "#f9fafb" }, children: policy.action === "delete" ? /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)(import_material10.Typography, { variant: "body2", sx: { fontFamily: "monospace", color: "text.secondary" }, children: [
5490
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)(import_material10.Box, { component: "span", sx: {
5491
+ color: policy.permission === "*" ? "#16a34a" : policy.permission === "owner" ? "#0ea5e9" : "#dc2626"
5492
+ }, children: [
5493
+ t("modules.form.publicPolicies.fields.conditions.states.allow"),
5494
+ ":"
5495
+ ] }),
5496
+ " ",
5497
+ policy.permission || "-"
5498
+ ] }) : /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)(import_material10.Stack, { spacing: 0.5, divider: /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(import_material10.Divider, { sx: { borderColor: "#e5e7eb" } }), children: [
5499
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)(import_material10.Typography, { variant: "body2", sx: { fontFamily: "monospace", color: "text.secondary" }, children: [
5500
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)(import_material10.Box, { component: "span", sx: { color: "#16a34a" }, children: [
5501
+ t("modules.form.publicPolicies.fields.conditions.states.allow"),
5502
+ ":"
5503
+ ] }),
5504
+ " ",
5505
+ (policy?.fields?.allow || []).join(", ") || "-"
5506
+ ] }),
5507
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)(import_material10.Typography, { variant: "body2", sx: { fontFamily: "monospace", color: "text.secondary" }, children: [
5508
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)(import_material10.Box, { component: "span", sx: { color: "#0ea5e9" }, children: [
5509
+ t("modules.form.publicPolicies.fields.conditions.states.ownerAllow"),
5510
+ ":"
5511
+ ] }),
5512
+ " ",
5513
+ (policy?.fields?.owner_allow || []).join(", ") || "-"
5514
+ ] }),
5515
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)(import_material10.Typography, { variant: "body2", sx: { fontFamily: "monospace", color: "text.secondary" }, children: [
5516
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)(import_material10.Box, { component: "span", sx: { color: "#dc2626" }, children: [
5517
+ t("modules.form.publicPolicies.fields.conditions.states.deny"),
5518
+ ":"
5519
+ ] }),
5520
+ " ",
5521
+ (policy?.fields?.deny || []).join(", ") || "-"
5522
+ ] })
5523
+ ] }) })
5524
+ ] })
5525
+ ]
5526
+ }
5527
+ );
5528
+ });
5529
+ var PolicyItem_default = PolicyItem;
5530
+
5531
+ // src/components/PublicPolicies/Policies.tsx
5532
+ var import_jsx_runtime16 = require("react/jsx-runtime");
5533
+ var generateId = () => {
5534
+ const c = globalThis?.crypto;
5535
+ if (c && typeof c.randomUUID === "function") return c.randomUUID();
5536
+ return `${Date.now()}-${Math.random().toString(16).slice(2)}`;
5537
+ };
5538
+ var Policies = ({
5539
+ policies,
5540
+ onChange,
5541
+ availableFields,
5542
+ errors,
5543
+ isSubmitting = false
5544
+ }) => {
5545
+ const { t } = (0, import_react_i18next3.useTranslation)();
5546
+ const policyRefs = (0, import_react24.useRef)({});
5547
+ const takenActions = new Set((policies || []).map((p) => p.action).filter(Boolean));
5548
+ const remainingActions = PREFERRED_POLICY_ORDER.filter((a) => !takenActions.has(a));
5549
+ const canAddPolicy = remainingActions.length > 0;
5550
+ const addPolicy = () => {
5551
+ const defaultAction = remainingActions[0] || "create";
5552
+ const newPolicy = {
5553
+ id: generateId(),
5554
+ action: defaultAction
5555
+ };
5556
+ if (defaultAction === "delete") {
5557
+ newPolicy.permission = "deny";
5558
+ } else {
5559
+ newPolicy.fields = {
5560
+ allow: [],
5561
+ owner_allow: [],
5562
+ deny: availableFields
5563
+ };
5564
+ }
5565
+ const next = [...policies || [], newPolicy];
5566
+ onChange(next);
5567
+ setTimeout(() => {
5568
+ const newIndex = next.length - 1;
5569
+ const el = policyRefs.current[newIndex];
5570
+ if (el) {
5571
+ el.scrollIntoView({ behavior: "smooth", block: "center" });
5572
+ }
5573
+ }, 100);
5574
+ };
5575
+ const removePolicy = (index) => {
5576
+ const next = [...policies];
5577
+ next.splice(index, 1);
5578
+ onChange(next);
5579
+ };
5580
+ const arrayError = (() => {
5581
+ if (!errors) return null;
5582
+ if (typeof errors === "string") return errors;
5583
+ const msg = errors._error;
5584
+ return typeof msg === "string" ? msg : null;
5585
+ })();
5586
+ const usedActions = new Set((policies || []).map((p) => p.action));
5587
+ return /* @__PURE__ */ (0, import_jsx_runtime16.jsxs)(import_jsx_runtime16.Fragment, { children: [
5588
+ /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(import_material11.Divider, { sx: { borderColor: "#e0e4e7" } }),
5589
+ /* @__PURE__ */ (0, import_jsx_runtime16.jsxs)(import_material11.Box, { children: [
5590
+ /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(import_material11.Box, { display: "flex", justifyContent: "space-between", alignItems: "center", mb: 3, children: /* @__PURE__ */ (0, import_jsx_runtime16.jsxs)(import_material11.Box, { children: [
5591
+ /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(
5592
+ import_material11.Typography,
5593
+ {
5594
+ variant: "h6",
5595
+ sx: {
5596
+ fontWeight: 600,
5597
+ color: "#111418",
5598
+ mb: 1
5599
+ },
5600
+ children: t("modules.form.publicPolicies.title")
5601
+ }
5602
+ ),
5603
+ /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(
5604
+ import_material11.Typography,
5605
+ {
5606
+ variant: "body2",
5607
+ color: "text.secondary",
5608
+ sx: { fontSize: "0.875rem" },
5609
+ children: t("modules.form.publicPolicies.description")
5610
+ }
5611
+ )
5612
+ ] }) }),
5613
+ arrayError && /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(import_material11.Alert, { severity: "error", sx: { mb: 3 }, children: arrayError }),
5614
+ /* @__PURE__ */ (0, import_jsx_runtime16.jsxs)(import_material11.Stack, { spacing: 3, children: [
5615
+ (policies || []).length === 0 ? /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(import_material11.Alert, { severity: "info", children: t("modules.form.publicPolicies.noPolicies") }) : policies.map((policy, index) => /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(
5616
+ PolicyItem_default,
5617
+ {
5618
+ ref: (el) => {
5619
+ policyRefs.current[index] = el;
5620
+ },
5621
+ policy,
5622
+ onChange: (nextPolicy) => {
5623
+ const next = [...policies];
5624
+ next[index] = nextPolicy;
5625
+ onChange(next);
5626
+ },
5627
+ onRemove: () => removePolicy(index),
5628
+ availableFields,
5629
+ isSubmitting,
5630
+ usedActions,
5631
+ error: typeof errors === "object" && errors && policy.id in errors ? errors[policy.id] : void 0
5632
+ },
5633
+ policy.id
5634
+ )),
5635
+ canAddPolicy && /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(import_material11.Box, { children: /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(
5636
+ import_material11.Button,
5637
+ {
5638
+ type: "button",
5639
+ variant: "outlined",
5640
+ startIcon: /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(import_icons_material4.Add, {}),
5641
+ onClick: addPolicy,
5642
+ disabled: isSubmitting,
5643
+ sx: {
5644
+ borderColor: "#d0d7de",
5645
+ color: "#656d76",
5646
+ "&:hover": {
5647
+ borderColor: "#8c959f",
5648
+ backgroundColor: "transparent"
5649
+ }
5650
+ },
5651
+ children: t("modules.form.publicPolicies.addPolicy")
5652
+ }
5653
+ ) })
5654
+ ] })
5655
+ ] })
5656
+ ] });
5657
+ };
5658
+ var Policies_default = Policies;
5077
5659
  // Annotate the CommonJS export names for ESM import in node:
5078
5660
  0 && (module.exports = {
5079
5661
  CrudifyDataProvider,
@@ -5081,6 +5663,9 @@ var useData = () => {
5081
5663
  ERROR_CODES,
5082
5664
  ERROR_SEVERITY_MAP,
5083
5665
  LoginComponent,
5666
+ POLICY_ACTIONS,
5667
+ PREFERRED_POLICY_ORDER,
5668
+ Policies,
5084
5669
  ProtectedRoute,
5085
5670
  SessionDebugInfo,
5086
5671
  SessionManager,
package/dist/index.mjs CHANGED
@@ -3767,12 +3767,627 @@ var useData = () => {
3767
3767
  waitForReady
3768
3768
  };
3769
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);
3844
+ }
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
+ };
3865
+ const getFieldState = (fieldName) => {
3866
+ if (value?.allow?.includes(fieldName)) return "allow";
3867
+ if (value?.owner_allow?.includes(fieldName)) return "owner_allow";
3868
+ return "deny";
3869
+ };
3870
+ const setFieldState = (fieldName, state) => {
3871
+ isUpdatingRef.current = true;
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);
3886
+ };
3887
+ if (availableFields.length === 0) {
3888
+ return /* @__PURE__ */ jsxs10(Box9, { children: [
3889
+ /* @__PURE__ */ jsx13(Typography9, { variant: "body2", color: "text.secondary", sx: { mb: 1 }, children: t("modules.form.publicPolicies.fields.conditions.label") }),
3890
+ /* @__PURE__ */ jsx13(Typography9, { variant: "body2", color: "text.secondary", sx: { fontStyle: "italic" }, children: t("modules.form.publicPolicies.fields.conditions.noFieldsAvailable") }),
3891
+ error && /* @__PURE__ */ jsx13(FormHelperText, { error: true, sx: { mt: 1 }, children: error })
3892
+ ] });
3893
+ }
3894
+ return /* @__PURE__ */ jsxs10(Box9, { children: [
3895
+ /* @__PURE__ */ jsx13(Typography9, { variant: "body2", color: "text.secondary", sx: { mb: 2 }, children: t("modules.form.publicPolicies.fields.conditions.label") }),
3896
+ /* @__PURE__ */ jsxs10(Stack, { direction: "row", spacing: 1, sx: { mb: 3 }, children: [
3897
+ /* @__PURE__ */ jsx13(
3898
+ Button6,
3899
+ {
3900
+ variant: mode === "all" ? "contained" : "outlined",
3901
+ startIcon: /* @__PURE__ */ jsx13(SelectAll, {}),
3902
+ onClick: setAllAllow,
3903
+ disabled,
3904
+ size: "small",
3905
+ sx: {
3906
+ minWidth: 120,
3907
+ ...mode === "all" && {
3908
+ backgroundColor: "#16a34a",
3909
+ "&:hover": { backgroundColor: "#15803d" }
3910
+ }
3911
+ },
3912
+ children: t("modules.form.publicPolicies.fields.conditions.allFields")
3913
+ }
3914
+ ),
3915
+ /* @__PURE__ */ jsx13(
3916
+ Button6,
3917
+ {
3918
+ variant: mode === "none" ? "contained" : "outlined",
3919
+ startIcon: /* @__PURE__ */ jsx13(ClearAll, {}),
3920
+ onClick: setAllDeny,
3921
+ disabled,
3922
+ size: "small",
3923
+ sx: {
3924
+ minWidth: 120,
3925
+ ...mode === "none" && {
3926
+ backgroundColor: "#cf222e",
3927
+ "&:hover": { backgroundColor: "#bc1f2c" }
3928
+ }
3929
+ },
3930
+ children: t("modules.form.publicPolicies.fields.conditions.noFields")
3931
+ }
3932
+ )
3933
+ ] }),
3934
+ /* @__PURE__ */ jsxs10(Box9, { sx: { p: 2, border: "1px solid #d1d9e0", borderRadius: 1, backgroundColor: "#f6f8fa" }, children: [
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 })
4030
+ ] });
4031
+ };
4032
+ var FieldSelector_default = FieldSelector;
4033
+
4034
+ // src/components/PublicPolicies/constants.ts
4035
+ var POLICY_ACTIONS = ["create", "read", "update", "delete"];
4036
+ var PREFERRED_POLICY_ORDER = [
4037
+ "create",
4038
+ "read",
4039
+ "update",
4040
+ "delete"
4041
+ ];
4042
+
4043
+ // src/components/PublicPolicies/PolicyItem/PolicyItem.tsx
4044
+ import { jsx as jsx14, jsxs as jsxs11 } from "react/jsx-runtime";
4045
+ var PolicyItem = forwardRef(({
4046
+ policy,
4047
+ onChange,
4048
+ onRemove,
4049
+ availableFields,
4050
+ isSubmitting = false,
4051
+ usedActions,
4052
+ error
4053
+ }, ref) => {
4054
+ const { t } = useTranslation3();
4055
+ const takenActions = new Set(Array.from(usedActions || []));
4056
+ takenActions.delete(policy.action);
4057
+ const actionOptions = POLICY_ACTIONS.map((a) => ({
4058
+ value: a,
4059
+ label: t(`modules.form.publicPolicies.fields.action.options.${a}`)
4060
+ }));
4061
+ return /* @__PURE__ */ jsxs11(
4062
+ Paper,
4063
+ {
4064
+ ref,
4065
+ sx: {
4066
+ p: 3,
4067
+ border: "1px solid #d1d9e0",
4068
+ borderRadius: 2,
4069
+ position: "relative",
4070
+ backgroundColor: "#ffffff"
4071
+ },
4072
+ children: [
4073
+ /* @__PURE__ */ jsxs11(Box10, { sx: { display: "flex", justifyContent: "space-between", alignItems: "flex-start", mb: 3 }, children: [
4074
+ /* @__PURE__ */ jsx14(
4075
+ Typography10,
4076
+ {
4077
+ variant: "subtitle1",
4078
+ sx: {
4079
+ fontWeight: 600,
4080
+ color: "#111418",
4081
+ fontSize: "1rem"
4082
+ },
4083
+ children: t("modules.form.publicPolicies.policyTitle")
4084
+ }
4085
+ ),
4086
+ /* @__PURE__ */ jsx14(
4087
+ IconButton2,
4088
+ {
4089
+ onClick: onRemove,
4090
+ size: "small",
4091
+ disabled: isSubmitting,
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, {})
4101
+ }
4102
+ )
4103
+ ] }),
4104
+ /* @__PURE__ */ jsxs11(Stack2, { spacing: 3, children: [
4105
+ /* @__PURE__ */ jsx14(Stack2, { direction: { xs: "column", md: "row" }, spacing: 2, children: /* @__PURE__ */ jsx14(Box10, { sx: { flex: 1, minWidth: 200 }, children: /* @__PURE__ */ jsxs11(FormControl, { fullWidth: true, children: [
4106
+ /* @__PURE__ */ jsx14(InputLabel, { children: t("modules.form.publicPolicies.fields.action.label") }),
4107
+ /* @__PURE__ */ jsx14(
4108
+ Select,
4109
+ {
4110
+ value: policy.action,
4111
+ label: t("modules.form.publicPolicies.fields.action.label"),
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
+ })
4139
+ }
4140
+ ),
4141
+ error && /* @__PURE__ */ jsx14(FormHelperText2, { error: true, children: error })
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
4210
+ }
4211
+ ),
4212
+ /* @__PURE__ */ jsx14(Paper, { variant: "outlined", sx: { p: 2, backgroundColor: "#f9fafb" }, children: policy.action === "delete" ? /* @__PURE__ */ jsxs11(Typography10, { variant: "body2", sx: { fontFamily: "monospace", color: "text.secondary" }, children: [
4213
+ /* @__PURE__ */ jsxs11(Box10, { component: "span", sx: {
4214
+ color: policy.permission === "*" ? "#16a34a" : policy.permission === "owner" ? "#0ea5e9" : "#dc2626"
4215
+ }, children: [
4216
+ t("modules.form.publicPolicies.fields.conditions.states.allow"),
4217
+ ":"
4218
+ ] }),
4219
+ " ",
4220
+ policy.permission || "-"
4221
+ ] }) : /* @__PURE__ */ jsxs11(Stack2, { spacing: 0.5, divider: /* @__PURE__ */ jsx14(Divider2, { sx: { borderColor: "#e5e7eb" } }), children: [
4222
+ /* @__PURE__ */ jsxs11(Typography10, { variant: "body2", sx: { fontFamily: "monospace", color: "text.secondary" }, children: [
4223
+ /* @__PURE__ */ jsxs11(Box10, { component: "span", sx: { color: "#16a34a" }, children: [
4224
+ t("modules.form.publicPolicies.fields.conditions.states.allow"),
4225
+ ":"
4226
+ ] }),
4227
+ " ",
4228
+ (policy?.fields?.allow || []).join(", ") || "-"
4229
+ ] }),
4230
+ /* @__PURE__ */ jsxs11(Typography10, { variant: "body2", sx: { fontFamily: "monospace", color: "text.secondary" }, children: [
4231
+ /* @__PURE__ */ jsxs11(Box10, { component: "span", sx: { color: "#0ea5e9" }, children: [
4232
+ t("modules.form.publicPolicies.fields.conditions.states.ownerAllow"),
4233
+ ":"
4234
+ ] }),
4235
+ " ",
4236
+ (policy?.fields?.owner_allow || []).join(", ") || "-"
4237
+ ] }),
4238
+ /* @__PURE__ */ jsxs11(Typography10, { variant: "body2", sx: { fontFamily: "monospace", color: "text.secondary" }, children: [
4239
+ /* @__PURE__ */ jsxs11(Box10, { component: "span", sx: { color: "#dc2626" }, children: [
4240
+ t("modules.form.publicPolicies.fields.conditions.states.deny"),
4241
+ ":"
4242
+ ] }),
4243
+ " ",
4244
+ (policy?.fields?.deny || []).join(", ") || "-"
4245
+ ] })
4246
+ ] }) })
4247
+ ] })
4248
+ ]
4249
+ }
4250
+ );
4251
+ });
4252
+ var PolicyItem_default = PolicyItem;
4253
+
4254
+ // src/components/PublicPolicies/Policies.tsx
4255
+ import { Fragment as Fragment8, jsx as jsx15, jsxs as jsxs12 } from "react/jsx-runtime";
4256
+ var generateId = () => {
4257
+ const c = globalThis?.crypto;
4258
+ if (c && typeof c.randomUUID === "function") return c.randomUUID();
4259
+ return `${Date.now()}-${Math.random().toString(16).slice(2)}`;
4260
+ };
4261
+ var Policies = ({
4262
+ policies,
4263
+ onChange,
4264
+ availableFields,
4265
+ errors,
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
4278
+ };
4279
+ if (defaultAction === "delete") {
4280
+ newPolicy.permission = "deny";
4281
+ } else {
4282
+ newPolicy.fields = {
4283
+ allow: [],
4284
+ owner_allow: [],
4285
+ deny: availableFields
4286
+ };
4287
+ }
4288
+ const next = [...policies || [], newPolicy];
4289
+ onChange(next);
4290
+ setTimeout(() => {
4291
+ const newIndex = next.length - 1;
4292
+ const el = policyRefs.current[newIndex];
4293
+ if (el) {
4294
+ el.scrollIntoView({ behavior: "smooth", block: "center" });
4295
+ }
4296
+ }, 100);
4297
+ };
4298
+ const removePolicy = (index) => {
4299
+ const next = [...policies];
4300
+ next.splice(index, 1);
4301
+ onChange(next);
4302
+ };
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
+ var Policies_default = Policies;
3770
4382
  export {
3771
4383
  CrudifyDataProvider,
3772
4384
  CrudifyLogin_default as CrudifyLogin,
3773
4385
  ERROR_CODES,
3774
4386
  ERROR_SEVERITY_MAP,
3775
4387
  LoginComponent,
4388
+ POLICY_ACTIONS,
4389
+ PREFERRED_POLICY_ORDER,
4390
+ Policies_default as Policies,
3776
4391
  ProtectedRoute,
3777
4392
  SessionDebugInfo,
3778
4393
  SessionManager,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@nocios/crudify-ui",
3
- "version": "1.3.2",
3
+ "version": "1.3.4",
4
4
  "description": "Biblioteca de componentes UI para Crudify",
5
5
  "author": "Nocios",
6
6
  "license": "MIT",