@asteby/metacore-runtime-react 35.0.0 → 35.1.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/CHANGELOG.md +6 -0
- package/dist/color-picker-field.d.ts +13 -0
- package/dist/color-picker-field.d.ts.map +1 -0
- package/dist/color-picker-field.js +181 -0
- package/dist/dynamic-form.d.ts +1 -0
- package/dist/dynamic-form.d.ts.map +1 -1
- package/dist/dynamic-form.js +3 -1
- package/dist/index.d.ts +2 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -1
- package/dist/permissions-manager.d.ts +5 -0
- package/dist/permissions-manager.d.ts.map +1 -1
- package/dist/permissions-manager.js +67 -23
- package/package.json +1 -1
- package/src/__tests__/color-picker-field.test.tsx +18 -0
- package/src/color-picker-field.tsx +296 -0
- package/src/dynamic-form.tsx +9 -1
- package/src/index.ts +2 -0
- package/src/permissions-manager.tsx +98 -39
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,11 @@
|
|
|
1
1
|
# @asteby/metacore-runtime-react
|
|
2
2
|
|
|
3
|
+
## 35.1.0
|
|
4
|
+
|
|
5
|
+
### Minor Changes
|
|
6
|
+
|
|
7
|
+
- 431f813: Role chips: Lucide icon + modern HSL color picker (popover SV plane / hue / hex) in PermissionsManager; ColorPickerField also used for DynamicForm color fields.
|
|
8
|
+
|
|
3
9
|
## 35.0.0
|
|
4
10
|
|
|
5
11
|
### Minor Changes
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
export declare const DEFAULT_ROLE_COLOR = "#3b82f6";
|
|
2
|
+
export interface ColorPickerFieldProps {
|
|
3
|
+
value?: string;
|
|
4
|
+
onChange: (hex: string) => void;
|
|
5
|
+
/** Optional label for a11y on the trigger. */
|
|
6
|
+
'aria-label'?: string;
|
|
7
|
+
className?: string;
|
|
8
|
+
disabled?: boolean;
|
|
9
|
+
}
|
|
10
|
+
/** Normalize to #rrggbb or '' if invalid. */
|
|
11
|
+
export declare function normalizeHex(raw: unknown): string;
|
|
12
|
+
export declare function ColorPickerField({ value, onChange, 'aria-label': ariaLabel, className, disabled, }: ColorPickerFieldProps): import("react").JSX.Element;
|
|
13
|
+
//# sourceMappingURL=color-picker-field.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"color-picker-field.d.ts","sourceRoot":"","sources":["../src/color-picker-field.tsx"],"names":[],"mappings":"AAcA,eAAO,MAAM,kBAAkB,YAAY,CAAA;AAE3C,MAAM,WAAW,qBAAqB;IAClC,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,QAAQ,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,IAAI,CAAA;IAC/B,8CAA8C;IAC9C,YAAY,CAAC,EAAE,MAAM,CAAA;IACrB,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB,QAAQ,CAAC,EAAE,OAAO,CAAA;CACrB;AAQD,6CAA6C;AAC7C,wBAAgB,YAAY,CAAC,GAAG,EAAE,OAAO,GAAG,MAAM,CAajD;AAwED,wBAAgB,gBAAgB,CAAC,EAC7B,KAAK,EACL,QAAQ,EACR,YAAY,EAAE,SAAmB,EACjC,SAAS,EACT,QAAQ,GACX,EAAE,qBAAqB,+BA4KvB"}
|
|
@@ -0,0 +1,181 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
// ColorPickerField — modern popover color control (SV plane + hue + hex).
|
|
3
|
+
// Used by PermissionsManager role dialog and DynamicForm `type: "color"`.
|
|
4
|
+
// Value is always a #rrggbb string (empty → falls back to DEFAULT_HEX visually).
|
|
5
|
+
import { useCallback, useEffect, useMemo, useRef, useState } from 'react';
|
|
6
|
+
import { Pipette } from 'lucide-react';
|
|
7
|
+
import { cn } from '@asteby/metacore-ui/lib';
|
|
8
|
+
import { Button, Input, Popover, PopoverContent, PopoverTrigger, } from '@asteby/metacore-ui/primitives';
|
|
9
|
+
export const DEFAULT_ROLE_COLOR = '#3b82f6';
|
|
10
|
+
function clamp(n, min, max) {
|
|
11
|
+
return Math.min(max, Math.max(min, n));
|
|
12
|
+
}
|
|
13
|
+
/** Normalize to #rrggbb or '' if invalid. */
|
|
14
|
+
export function normalizeHex(raw) {
|
|
15
|
+
if (typeof raw !== 'string')
|
|
16
|
+
return '';
|
|
17
|
+
let s = raw.trim();
|
|
18
|
+
if (!s)
|
|
19
|
+
return '';
|
|
20
|
+
if (s[0] !== '#')
|
|
21
|
+
s = `#${s}`;
|
|
22
|
+
if (/^#[0-9a-fA-F]{3}$/.test(s)) {
|
|
23
|
+
const r = s[1], g = s[2], b = s[3];
|
|
24
|
+
s = `#${r}${r}${g}${g}${b}${b}`;
|
|
25
|
+
}
|
|
26
|
+
if (!/^#[0-9a-fA-F]{6}$/.test(s))
|
|
27
|
+
return '';
|
|
28
|
+
return s.toLowerCase();
|
|
29
|
+
}
|
|
30
|
+
function hexToRgb(hex) {
|
|
31
|
+
const n = normalizeHex(hex);
|
|
32
|
+
if (!n)
|
|
33
|
+
return null;
|
|
34
|
+
return {
|
|
35
|
+
r: parseInt(n.slice(1, 3), 16),
|
|
36
|
+
g: parseInt(n.slice(3, 5), 16),
|
|
37
|
+
b: parseInt(n.slice(5, 7), 16),
|
|
38
|
+
};
|
|
39
|
+
}
|
|
40
|
+
function rgbToHex(r, g, b) {
|
|
41
|
+
const h = (n) => clamp(Math.round(n), 0, 255).toString(16).padStart(2, '0');
|
|
42
|
+
return `#${h(r)}${h(g)}${h(b)}`;
|
|
43
|
+
}
|
|
44
|
+
function rgbToHsv(r, g, b) {
|
|
45
|
+
r /= 255;
|
|
46
|
+
g /= 255;
|
|
47
|
+
b /= 255;
|
|
48
|
+
const max = Math.max(r, g, b);
|
|
49
|
+
const min = Math.min(r, g, b);
|
|
50
|
+
const d = max - min;
|
|
51
|
+
let h = 0;
|
|
52
|
+
if (d !== 0) {
|
|
53
|
+
if (max === r)
|
|
54
|
+
h = ((g - b) / d) % 6;
|
|
55
|
+
else if (max === g)
|
|
56
|
+
h = (b - r) / d + 2;
|
|
57
|
+
else
|
|
58
|
+
h = (r - g) / d + 4;
|
|
59
|
+
h *= 60;
|
|
60
|
+
if (h < 0)
|
|
61
|
+
h += 360;
|
|
62
|
+
}
|
|
63
|
+
const s = max === 0 ? 0 : d / max;
|
|
64
|
+
return { h, s, v: max };
|
|
65
|
+
}
|
|
66
|
+
function hsvToRgb(h, s, v) {
|
|
67
|
+
const c = v * s;
|
|
68
|
+
const x = c * (1 - Math.abs(((h / 60) % 2) - 1));
|
|
69
|
+
const m = v - c;
|
|
70
|
+
let rp = 0, gp = 0, bp = 0;
|
|
71
|
+
if (h < 60)
|
|
72
|
+
[rp, gp, bp] = [c, x, 0];
|
|
73
|
+
else if (h < 120)
|
|
74
|
+
[rp, gp, bp] = [x, c, 0];
|
|
75
|
+
else if (h < 180)
|
|
76
|
+
[rp, gp, bp] = [0, c, x];
|
|
77
|
+
else if (h < 240)
|
|
78
|
+
[rp, gp, bp] = [0, x, c];
|
|
79
|
+
else if (h < 300)
|
|
80
|
+
[rp, gp, bp] = [x, 0, c];
|
|
81
|
+
else
|
|
82
|
+
[rp, gp, bp] = [c, 0, x];
|
|
83
|
+
return {
|
|
84
|
+
r: (rp + m) * 255,
|
|
85
|
+
g: (gp + m) * 255,
|
|
86
|
+
b: (bp + m) * 255,
|
|
87
|
+
};
|
|
88
|
+
}
|
|
89
|
+
function hexToHsv(hex) {
|
|
90
|
+
const rgb = hexToRgb(hex) || hexToRgb(DEFAULT_ROLE_COLOR);
|
|
91
|
+
return rgbToHsv(rgb.r, rgb.g, rgb.b);
|
|
92
|
+
}
|
|
93
|
+
function hsvToHex(hsv) {
|
|
94
|
+
const { r, g, b } = hsvToRgb(hsv.h, hsv.s, hsv.v);
|
|
95
|
+
return rgbToHex(r, g, b);
|
|
96
|
+
}
|
|
97
|
+
function hueCss(h) {
|
|
98
|
+
const { r, g, b } = hsvToRgb(h, 1, 1);
|
|
99
|
+
return rgbToHex(r, g, b);
|
|
100
|
+
}
|
|
101
|
+
export function ColorPickerField({ value, onChange, 'aria-label': ariaLabel = 'Color', className, disabled, }) {
|
|
102
|
+
const hex = normalizeHex(value) || DEFAULT_ROLE_COLOR;
|
|
103
|
+
const [open, setOpen] = useState(false);
|
|
104
|
+
const [hsv, setHsv] = useState(() => hexToHsv(hex));
|
|
105
|
+
const [hexDraft, setHexDraft] = useState(hex);
|
|
106
|
+
const svRef = useRef(null);
|
|
107
|
+
const dragging = useRef(false);
|
|
108
|
+
// Sync from external value when popover closed (or first open).
|
|
109
|
+
useEffect(() => {
|
|
110
|
+
if (dragging.current)
|
|
111
|
+
return;
|
|
112
|
+
const next = normalizeHex(value) || DEFAULT_ROLE_COLOR;
|
|
113
|
+
setHsv(hexToHsv(next));
|
|
114
|
+
setHexDraft(next);
|
|
115
|
+
}, [value, open]);
|
|
116
|
+
const commit = useCallback((next) => {
|
|
117
|
+
setHsv(next);
|
|
118
|
+
const out = hsvToHex(next);
|
|
119
|
+
setHexDraft(out);
|
|
120
|
+
onChange(out);
|
|
121
|
+
}, [onChange]);
|
|
122
|
+
const setFromPointer = useCallback((clientX, clientY) => {
|
|
123
|
+
const el = svRef.current;
|
|
124
|
+
if (!el)
|
|
125
|
+
return;
|
|
126
|
+
const rect = el.getBoundingClientRect();
|
|
127
|
+
const s = clamp((clientX - rect.left) / rect.width, 0, 1);
|
|
128
|
+
const v = clamp(1 - (clientY - rect.top) / rect.height, 0, 1);
|
|
129
|
+
commit({ ...hsv, s, v });
|
|
130
|
+
}, [commit, hsv]);
|
|
131
|
+
useEffect(() => {
|
|
132
|
+
if (!open)
|
|
133
|
+
return;
|
|
134
|
+
const onMove = (e) => {
|
|
135
|
+
if (!dragging.current)
|
|
136
|
+
return;
|
|
137
|
+
setFromPointer(e.clientX, e.clientY);
|
|
138
|
+
};
|
|
139
|
+
const onUp = () => {
|
|
140
|
+
dragging.current = false;
|
|
141
|
+
};
|
|
142
|
+
window.addEventListener('pointermove', onMove);
|
|
143
|
+
window.addEventListener('pointerup', onUp);
|
|
144
|
+
return () => {
|
|
145
|
+
window.removeEventListener('pointermove', onMove);
|
|
146
|
+
window.removeEventListener('pointerup', onUp);
|
|
147
|
+
};
|
|
148
|
+
}, [open, setFromPointer]);
|
|
149
|
+
const svStyle = useMemo(() => ({
|
|
150
|
+
background: `
|
|
151
|
+
linear-gradient(to top, #000, transparent),
|
|
152
|
+
linear-gradient(to right, #fff, ${hueCss(hsv.h)})
|
|
153
|
+
`,
|
|
154
|
+
}), [hsv.h]);
|
|
155
|
+
const applyHexDraft = () => {
|
|
156
|
+
const n = normalizeHex(hexDraft);
|
|
157
|
+
if (!n) {
|
|
158
|
+
setHexDraft(hsvToHex(hsv));
|
|
159
|
+
return;
|
|
160
|
+
}
|
|
161
|
+
setHsv(hexToHsv(n));
|
|
162
|
+
setHexDraft(n);
|
|
163
|
+
onChange(n);
|
|
164
|
+
};
|
|
165
|
+
return (_jsxs(Popover, { open: open, onOpenChange: setOpen, children: [_jsx(PopoverTrigger, { asChild: true, children: _jsxs(Button, { type: "button", variant: "outline", disabled: disabled, "aria-label": ariaLabel, className: cn('h-10 w-full justify-start gap-3 px-2.5 font-normal', className), children: [_jsx("span", { className: "h-6 w-6 shrink-0 rounded-md border border-black/10 shadow-sm ring-1 ring-black/5 dark:border-white/10", style: { background: hex }, "aria-hidden": true }), _jsx("span", { className: "font-mono text-sm uppercase tracking-wide text-foreground", children: hex }), _jsx(Pipette, { className: "ml-auto h-3.5 w-3.5 text-muted-foreground" })] }) }), _jsx(PopoverContent, { className: "w-[260px] p-3", align: "start", children: _jsxs("div", { className: "flex flex-col gap-3", children: [_jsx("div", { ref: svRef, role: "slider", "aria-label": "Saturaci\u00F3n y brillo", tabIndex: 0, className: "relative h-36 w-full cursor-crosshair touch-none overflow-hidden rounded-lg border border-border/60", style: svStyle, onPointerDown: (e) => {
|
|
166
|
+
dragging.current = true;
|
|
167
|
+
e.target.setPointerCapture?.(e.pointerId);
|
|
168
|
+
setFromPointer(e.clientX, e.clientY);
|
|
169
|
+
}, children: _jsx("span", { className: "pointer-events-none absolute h-4 w-4 -translate-x-1/2 -translate-y-1/2 rounded-full border-2 border-white shadow-md ring-1 ring-black/30", style: {
|
|
170
|
+
left: `${hsv.s * 100}%`,
|
|
171
|
+
top: `${(1 - hsv.v) * 100}%`,
|
|
172
|
+
background: hsvToHex(hsv),
|
|
173
|
+
} }) }), _jsxs("div", { className: "flex flex-col gap-1.5", children: [_jsx("label", { className: "text-[11px] font-medium uppercase tracking-wide text-muted-foreground", children: "Tono" }), _jsx("input", { type: "range", min: 0, max: 360, step: 1, value: Math.round(hsv.h), "aria-label": "Tono", onChange: (e) => commit({ ...hsv, h: Number(e.target.value) }), className: "h-3 w-full cursor-pointer appearance-none rounded-full", style: {
|
|
174
|
+
background: 'linear-gradient(to right, #f00, #ff0, #0f0, #0ff, #00f, #f0f, #f00)',
|
|
175
|
+
} })] }), _jsxs("div", { className: "flex items-center gap-2", children: [_jsx("span", { className: "h-9 w-9 shrink-0 rounded-md border border-black/10 shadow-inner ring-1 ring-black/5", style: { background: hsvToHex(hsv) }, "aria-hidden": true }), _jsx(Input, { value: hexDraft, spellCheck: false, "aria-label": "Hexadecimal", className: "h-9 font-mono uppercase", onChange: (e) => setHexDraft(e.target.value), onBlur: applyHexDraft, onKeyDown: (e) => {
|
|
176
|
+
if (e.key === 'Enter') {
|
|
177
|
+
e.preventDefault();
|
|
178
|
+
applyHexDraft();
|
|
179
|
+
}
|
|
180
|
+
} })] })] }) })] }));
|
|
181
|
+
}
|
package/dist/dynamic-form.d.ts
CHANGED
|
@@ -7,6 +7,7 @@ export { DynamicSelectField } from './dynamic-select-field';
|
|
|
7
7
|
export { DynamicDateField } from './dynamic-date-field';
|
|
8
8
|
export { UploadField } from './upload-field';
|
|
9
9
|
export { IconPickerField } from './icon-picker-field';
|
|
10
|
+
export { ColorPickerField, DEFAULT_ROLE_COLOR, normalizeHex } from './color-picker-field';
|
|
10
11
|
export interface DynamicFormProps {
|
|
11
12
|
fields: ActionFieldDef[];
|
|
12
13
|
initialValues?: Record<string, any>;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"dynamic-form.d.ts","sourceRoot":"","sources":["../src/dynamic-form.tsx"],"names":[],"mappings":"AAIA,OAAO,EAAwB,KAAK,UAAU,EAAE,MAAM,eAAe,CAAA;AAcrE,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,SAAS,CAAA;AAC7C,OAAO,EACH,cAAc,EACd,aAAa,EAOhB,MAAM,uBAAuB,CAAA;
|
|
1
|
+
{"version":3,"file":"dynamic-form.d.ts","sourceRoot":"","sources":["../src/dynamic-form.tsx"],"names":[],"mappings":"AAIA,OAAO,EAAwB,KAAK,UAAU,EAAE,MAAM,eAAe,CAAA;AAcrE,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,SAAS,CAAA;AAC7C,OAAO,EACH,cAAc,EACd,aAAa,EAOhB,MAAM,uBAAuB,CAAA;AAW9B,OAAO,EAAE,cAAc,EAAE,aAAa,EAAE,CAAA;AACxC,OAAO,EAAE,gBAAgB,EAAE,MAAM,sBAAsB,CAAA;AACvD,OAAO,EAAE,kBAAkB,EAAE,MAAM,wBAAwB,CAAA;AAC3D,OAAO,EAAE,gBAAgB,EAAE,MAAM,sBAAsB,CAAA;AACvD,OAAO,EAAE,WAAW,EAAE,MAAM,gBAAgB,CAAA;AAC5C,OAAO,EAAE,eAAe,EAAE,MAAM,qBAAqB,CAAA;AACrD,OAAO,EAAE,gBAAgB,EAAE,kBAAkB,EAAE,YAAY,EAAE,MAAM,sBAAsB,CAAA;AAEzF,MAAM,WAAW,gBAAgB;IAC7B,MAAM,EAAE,cAAc,EAAE,CAAA;IACxB,aAAa,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAA;IACnC,QAAQ,EAAE,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,KAAK,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAA;IAC/D,QAAQ,CAAC,EAAE,MAAM,IAAI,CAAA;IACrB,WAAW,CAAC,EAAE,MAAM,CAAA;IACpB,WAAW,CAAC,EAAE,MAAM,CAAA;IACpB,QAAQ,CAAC,EAAE,OAAO,CAAA;IAClB;;;;;;OAMG;IACH,UAAU,CAAC,EAAE,UAAU,CAAA;CAC1B;AAED,wBAAgB,WAAW,CAAC,EACxB,MAAM,EACN,aAAa,EACb,QAAQ,EACR,QAAQ,EACR,WAAuB,EACvB,WAAwB,EACxB,QAAgB,EAChB,UAAU,GACb,EAAE,gBAAgB,+BA8LlB"}
|
package/dist/dynamic-form.js
CHANGED
|
@@ -15,12 +15,14 @@ import { DynamicSelectField } from './dynamic-select-field';
|
|
|
15
15
|
import { DynamicDateField } from './dynamic-date-field';
|
|
16
16
|
import { UploadField } from './upload-field';
|
|
17
17
|
import { IconPickerField } from './icon-picker-field';
|
|
18
|
+
import { ColorPickerField } from './color-picker-field';
|
|
18
19
|
export { buildZodSchema, resolveWidget };
|
|
19
20
|
export { DynamicLineItems } from './dynamic-line-items';
|
|
20
21
|
export { DynamicSelectField } from './dynamic-select-field';
|
|
21
22
|
export { DynamicDateField } from './dynamic-date-field';
|
|
22
23
|
export { UploadField } from './upload-field';
|
|
23
24
|
export { IconPickerField } from './icon-picker-field';
|
|
25
|
+
export { ColorPickerField, DEFAULT_ROLE_COLOR, normalizeHex } from './color-picker-field';
|
|
24
26
|
export function DynamicForm({ fields, initialValues, onSubmit, onCancel, submitLabel = 'Guardar', cancelLabel = 'Cancelar', disabled = false, formLayout, }) {
|
|
25
27
|
const [values, setValues] = useState({});
|
|
26
28
|
const [errors, setErrors] = useState({});
|
|
@@ -230,7 +232,7 @@ function FieldRenderer({ field, value, onChange, initialValues, effectiveOptions
|
|
|
230
232
|
// / future MDX editor pick it up without breaking the contract.
|
|
231
233
|
return _jsx(Textarea, { id: field.key, "data-widget": "richtext", value: value || '', onChange: (e) => onChange(e.target.value), placeholder: field.placeholder });
|
|
232
234
|
case 'color':
|
|
233
|
-
return _jsx(
|
|
235
|
+
return (_jsx(ColorPickerField, { "aria-label": field.label || field.key, value: typeof value === 'string' ? value : '', onChange: onChange }));
|
|
234
236
|
case 'select':
|
|
235
237
|
return (_jsxs(Select, { value: value || '', onValueChange: onChange, children: [_jsx(SelectTrigger, { className: "w-full", children: _jsx(SelectValue, { placeholder: field.placeholder || 'Seleccionar...' }) }), _jsx(SelectContent, { children: (effectiveOptions ?? field.options)?.map((opt) => _jsx(SelectItem, { value: opt.value, children: opt.label }, opt.value)) })] }));
|
|
236
238
|
case 'switch':
|
package/dist/index.d.ts
CHANGED
|
@@ -28,7 +28,8 @@ export * from './slot';
|
|
|
28
28
|
export * from './capability-gate';
|
|
29
29
|
export { PermissionsProvider, useCan, usePermissionsActive, makeCan, capabilityForActionKey, modelCapability, gateTableMetadata, resolveRowActions, type CanFn, type PermissionsProviderProps, } from './permissions-context';
|
|
30
30
|
export { useDynamicRowActions, type UseDynamicRowActionsParams, type DynamicRowActions, } from './dynamic-row-actions';
|
|
31
|
-
export {
|
|
31
|
+
export { ColorPickerField, DEFAULT_ROLE_COLOR, normalizeHex } from './color-picker-field';
|
|
32
|
+
export { PermissionsManager, moduleActionCapability, moduleCapabilities, grantedCountForModule, capabilitySetsEqual, defaultActionIcon, suggestRoleIcon, normalizeCatalogGroups, flattenGroups, filterModuleGroups, groupModuleActions, type PermissionsManagerProps, type PermissionsCatalog, type GroupedPermissionsCatalog, type FlatPermissionsCatalog, type ModuleGroup, type PermissionModuleDef, type PermissionActionDef, type GeneralPermissionDef, type RoleDef, type RoleInput, } from './permissions-manager';
|
|
32
33
|
export * from './org-runtime-context';
|
|
33
34
|
export * from './org-runtime-provider';
|
|
34
35
|
export * from './navigation-builder';
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
|
package/dist/index.js
CHANGED
|
@@ -33,7 +33,8 @@ export * from './slot';
|
|
|
33
33
|
export * from './capability-gate';
|
|
34
34
|
export { PermissionsProvider, useCan, usePermissionsActive, makeCan, capabilityForActionKey, modelCapability, gateTableMetadata, resolveRowActions, } from './permissions-context';
|
|
35
35
|
export { useDynamicRowActions, } from './dynamic-row-actions';
|
|
36
|
-
export {
|
|
36
|
+
export { ColorPickerField, DEFAULT_ROLE_COLOR, normalizeHex } from './color-picker-field';
|
|
37
|
+
export { PermissionsManager, moduleActionCapability, moduleCapabilities, grantedCountForModule, capabilitySetsEqual, defaultActionIcon, suggestRoleIcon, normalizeCatalogGroups, flattenGroups, filterModuleGroups, groupModuleActions, } from './permissions-manager';
|
|
37
38
|
export * from './org-runtime-context';
|
|
38
39
|
export * from './org-runtime-provider';
|
|
39
40
|
export * from './navigation-builder';
|
|
@@ -91,11 +91,14 @@ export interface RoleDef {
|
|
|
91
91
|
label?: string;
|
|
92
92
|
/** Accent color (hex) for the role chip. */
|
|
93
93
|
color?: string;
|
|
94
|
+
/** Lucide PascalCase name (or image path) for the role chip. */
|
|
95
|
+
icon?: string;
|
|
94
96
|
}
|
|
95
97
|
export interface RoleInput {
|
|
96
98
|
name: string;
|
|
97
99
|
label?: string;
|
|
98
100
|
color?: string;
|
|
101
|
+
icon?: string;
|
|
99
102
|
}
|
|
100
103
|
export interface PermissionsManagerProps {
|
|
101
104
|
/** Loads the module×action universe + general flags (grouped or flat). */
|
|
@@ -136,6 +139,8 @@ export declare function grantedCountForModule(granted: ReadonlySet<string>, modu
|
|
|
136
139
|
export declare function capabilitySetsEqual(a: ReadonlySet<string>, b: ReadonlySet<string>): boolean;
|
|
137
140
|
/** Default lucide icon when the manifest action doesn't declare one. */
|
|
138
141
|
export declare function defaultActionIcon(actionKey: string, kind?: string): string;
|
|
142
|
+
/** Suggest a Lucide icon from the role label (create-dialog default). */
|
|
143
|
+
export declare function suggestRoleIcon(label: string): string;
|
|
139
144
|
/**
|
|
140
145
|
* Normalize whatever `loadModules` returned into the canonical grouped shape.
|
|
141
146
|
*
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"permissions-manager.d.ts","sourceRoot":"","sources":["../src/permissions-manager.tsx"],"names":[],"mappings":"AAyBA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAA;
|
|
1
|
+
{"version":3,"file":"permissions-manager.d.ts","sourceRoot":"","sources":["../src/permissions-manager.tsx"],"names":[],"mappings":"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"}
|
|
@@ -30,6 +30,8 @@ import { toast } from 'sonner';
|
|
|
30
30
|
import { cn } from '@asteby/metacore-ui/lib';
|
|
31
31
|
import { AlertDialog, AlertDialogAction, AlertDialogCancel, AlertDialogContent, AlertDialogDescription, AlertDialogFooter, AlertDialogHeader, AlertDialogTitle, Badge, Button, Card, CardContent, CardDescription, CardHeader, CardTitle, Checkbox, Command, CommandEmpty, CommandGroup, CommandInput, CommandItem, CommandList, Dialog, DialogContent, DialogFooter, DialogHeader, DialogTitle, Input, Label, Popover, PopoverContent, PopoverTrigger, Separator, Skeleton, Collapsible, CollapsibleContent, CollapsibleTrigger, } from '@asteby/metacore-ui/primitives';
|
|
32
32
|
import { DynamicIcon } from './dynamic-icon';
|
|
33
|
+
import { IconPickerField } from './icon-picker-field';
|
|
34
|
+
import { ColorPickerField, DEFAULT_ROLE_COLOR } from './color-picker-field';
|
|
33
35
|
// ---------------------------------------------------------------------------
|
|
34
36
|
// Pure helpers (exported for hosts/tests)
|
|
35
37
|
// ---------------------------------------------------------------------------
|
|
@@ -133,17 +135,29 @@ function slugify(label) {
|
|
|
133
135
|
.replace(/[^a-z0-9]+/g, '_')
|
|
134
136
|
.replace(/^_+|_+$/g, '');
|
|
135
137
|
}
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
]
|
|
138
|
+
/** Suggest a Lucide icon from the role label (create-dialog default). */
|
|
139
|
+
export function suggestRoleIcon(label) {
|
|
140
|
+
const s = fold(label);
|
|
141
|
+
const rules = [
|
|
142
|
+
[/cajer|cash|teller|bank/, 'Banknote'],
|
|
143
|
+
[/vend|seller|sales|shop|mostrador/, 'ShoppingBag'],
|
|
144
|
+
[/mecanic|technic|wrench|taller/, 'Wrench'],
|
|
145
|
+
[/jefe|lead|manager|gerente|supervisor/, 'HardHat'],
|
|
146
|
+
[/almacen|ware|stock|invent/, 'Warehouse'],
|
|
147
|
+
[/compr|purch|buyer/, 'Truck'],
|
|
148
|
+
[/contad|account|ledger/, 'Calculator'],
|
|
149
|
+
[/nomina|payroll|sueldo/, 'Wallet'],
|
|
150
|
+
[/rh|hr|human|emplead|people/, 'Users'],
|
|
151
|
+
[/fleet|flota|vehic/, 'Car'],
|
|
152
|
+
[/admin|dueño|owner/, 'ShieldCheck'],
|
|
153
|
+
[/view|observa|lectura|read/, 'Eye'],
|
|
154
|
+
];
|
|
155
|
+
for (const [re, icon] of rules) {
|
|
156
|
+
if (re.test(s))
|
|
157
|
+
return icon;
|
|
158
|
+
}
|
|
159
|
+
return 'Shield';
|
|
160
|
+
}
|
|
147
161
|
/**
|
|
148
162
|
* Normalize whatever `loadModules` returned into the canonical grouped shape.
|
|
149
163
|
*
|
|
@@ -237,7 +251,14 @@ export function PermissionsManager({ loadModules, loadRoles, loadRolePermissions
|
|
|
237
251
|
const [moduleOpen, setModuleOpen] = React.useState(false);
|
|
238
252
|
// Pending role switch while there are unsaved changes.
|
|
239
253
|
const [pendingRoleId, setPendingRoleId] = React.useState(null);
|
|
240
|
-
const [roleDialog, setRoleDialog] = React.useState({
|
|
254
|
+
const [roleDialog, setRoleDialog] = React.useState({
|
|
255
|
+
open: false,
|
|
256
|
+
mode: 'create',
|
|
257
|
+
label: '',
|
|
258
|
+
color: DEFAULT_ROLE_COLOR,
|
|
259
|
+
icon: 'Shield',
|
|
260
|
+
grantAll: false,
|
|
261
|
+
});
|
|
241
262
|
const [roleSaving, setRoleSaving] = React.useState(false);
|
|
242
263
|
const [deleteOpen, setDeleteOpen] = React.useState(false);
|
|
243
264
|
const [deleting, setDeleting] = React.useState(false);
|
|
@@ -389,6 +410,7 @@ export function PermissionsManager({ loadModules, loadRoles, loadRolePermissions
|
|
|
389
410
|
name: slugify(label),
|
|
390
411
|
label,
|
|
391
412
|
color: roleDialog.color,
|
|
413
|
+
icon: roleDialog.icon || suggestRoleIcon(label),
|
|
392
414
|
});
|
|
393
415
|
const rs = await loadRoles();
|
|
394
416
|
setRoles(rs);
|
|
@@ -412,6 +434,7 @@ export function PermissionsManager({ loadModules, loadRoles, loadRolePermissions
|
|
|
412
434
|
name: activeRole.name,
|
|
413
435
|
label,
|
|
414
436
|
color: roleDialog.color,
|
|
437
|
+
icon: roleDialog.icon || suggestRoleIcon(label),
|
|
415
438
|
});
|
|
416
439
|
await refreshRoles(activeRole.id);
|
|
417
440
|
toast.success('Rol actualizado');
|
|
@@ -451,7 +474,8 @@ export function PermissionsManager({ loadModules, loadRoles, loadRolePermissions
|
|
|
451
474
|
open: true,
|
|
452
475
|
mode: 'edit',
|
|
453
476
|
label: activeRole.label || activeRole.name,
|
|
454
|
-
color: activeRole.color ||
|
|
477
|
+
color: activeRole.color || DEFAULT_ROLE_COLOR,
|
|
478
|
+
icon: activeRole.icon || suggestRoleIcon(activeRole.label || activeRole.name),
|
|
455
479
|
grantAll: false,
|
|
456
480
|
});
|
|
457
481
|
};
|
|
@@ -476,18 +500,21 @@ export function PermissionsManager({ loadModules, loadRoles, loadRolePermissions
|
|
|
476
500
|
open: true,
|
|
477
501
|
mode: 'create',
|
|
478
502
|
label: '',
|
|
479
|
-
color:
|
|
503
|
+
color: DEFAULT_ROLE_COLOR,
|
|
504
|
+
icon: 'Shield',
|
|
480
505
|
grantAll: false,
|
|
481
|
-
}), children: [_jsx(Plus, { className: "mr-1.5 h-4 w-4" }), " Nuevo rol"] })), _jsxs(Button, { variant: "outline", onClick: () => setAllPermissions(true), disabled: checksDisabled || !allCatalogCapabilities.length, title: "Marca todos los permisos de todos los m\u00F3dulos", children: [_jsx(CheckCheck, { className: "mr-1.5 h-4 w-4" }), " Otorgar todo"] }), _jsxs(Button, { variant: "outline", onClick: () => setAllPermissions(false), disabled: checksDisabled || !draft?.size, title: "Quita todos los permisos del rol", children: [_jsx(Eraser, { className: "mr-1.5 h-4 w-4" }), " Quitar todo"] }), _jsxs(Button, { onClick: handleSave, disabled: !dirty || saving || !activeRole, className: "bg-emerald-600 text-white hover:bg-emerald-700", children: [_jsx(Save, { className: "mr-1.5 h-4 w-4" }), saving ? 'Guardando…' : 'Guardar permisos'] })] })] }), _jsxs("div", { className: "grid items-start gap-4 lg:grid-cols-[340px_1fr]", children: [_jsxs("div", { className: "flex flex-col gap-4", children: [_jsxs(Card, { children: [_jsxs(CardHeader, { children: [_jsx(CardTitle, { className: "text-base", children: "Rol" }), _jsx(CardDescription, { children: "Selecciona el rol a configurar." })] }), _jsxs(CardContent, { className: "flex flex-col gap-3", children: [_jsxs("div", { className: "flex items-center gap-1.5", children: [_jsxs(Popover, { open: roleOpen, onOpenChange: setRoleOpen, children: [_jsx(PopoverTrigger, { asChild: true, children: _jsxs(Button, { variant: "outline", role: "combobox", "aria-expanded": roleOpen, className: "min-w-0 flex-1 justify-between font-normal", children: [_jsxs("span", { className: "flex min-w-0 items-center gap-2", children: [activeRole && (_jsx("span", { className: "h-
|
|
482
|
-
background: activeRole.color || '#
|
|
483
|
-
|
|
506
|
+
}), children: [_jsx(Plus, { className: "mr-1.5 h-4 w-4" }), " Nuevo rol"] })), _jsxs(Button, { variant: "outline", onClick: () => setAllPermissions(true), disabled: checksDisabled || !allCatalogCapabilities.length, title: "Marca todos los permisos de todos los m\u00F3dulos", children: [_jsx(CheckCheck, { className: "mr-1.5 h-4 w-4" }), " Otorgar todo"] }), _jsxs(Button, { variant: "outline", onClick: () => setAllPermissions(false), disabled: checksDisabled || !draft?.size, title: "Quita todos los permisos del rol", children: [_jsx(Eraser, { className: "mr-1.5 h-4 w-4" }), " Quitar todo"] }), _jsxs(Button, { onClick: handleSave, disabled: !dirty || saving || !activeRole, className: "bg-emerald-600 text-white hover:bg-emerald-700", children: [_jsx(Save, { className: "mr-1.5 h-4 w-4" }), saving ? 'Guardando…' : 'Guardar permisos'] })] })] }), _jsxs("div", { className: "grid items-start gap-4 lg:grid-cols-[340px_1fr]", children: [_jsxs("div", { className: "flex flex-col gap-4", children: [_jsxs(Card, { children: [_jsxs(CardHeader, { children: [_jsx(CardTitle, { className: "text-base", children: "Rol" }), _jsx(CardDescription, { children: "Selecciona el rol a configurar." })] }), _jsxs(CardContent, { className: "flex flex-col gap-3", children: [_jsxs("div", { className: "flex items-center gap-1.5", children: [_jsxs(Popover, { open: roleOpen, onOpenChange: setRoleOpen, children: [_jsx(PopoverTrigger, { asChild: true, children: _jsxs(Button, { variant: "outline", role: "combobox", "aria-expanded": roleOpen, className: "min-w-0 flex-1 justify-between font-normal", children: [_jsxs("span", { className: "flex min-w-0 items-center gap-2", children: [activeRole && (_jsx("span", { className: "flex h-6 w-6 shrink-0 items-center justify-center rounded-md", style: {
|
|
507
|
+
background: `${activeRole.color || '#64748b'}22`,
|
|
508
|
+
color: activeRole.color || '#64748b',
|
|
509
|
+
}, "aria-hidden": "true", children: _jsx(DynamicIcon, { name: activeRole.icon || 'Shield', className: "h-3.5 w-3.5" }) })), _jsx("span", { className: "truncate", children: activeRole
|
|
484
510
|
? activeRole.label || activeRole.name
|
|
485
511
|
: 'Seleccionar rol…' })] }), _jsx(ChevronsUpDown, { className: "ml-2 h-4 w-4 shrink-0 opacity-50" })] }) }), _jsx(PopoverContent, { className: "w-[280px] p-0", align: "start", children: _jsxs(Command, { children: [_jsx(CommandInput, { placeholder: "Buscar rol\u2026" }), _jsxs(CommandList, { children: [_jsx(CommandEmpty, { children: "Sin resultados." }), _jsx(CommandGroup, { children: (roles ?? []).map((role) => (_jsxs(CommandItem, { value: `${role.label || ''} ${role.name}`, onSelect: () => {
|
|
486
512
|
requestRoleSwitch(role.id);
|
|
487
513
|
setRoleOpen(false);
|
|
488
|
-
}, children: [_jsx("span", { className: "mr-2 h-
|
|
489
|
-
background: role.color || '#
|
|
490
|
-
|
|
514
|
+
}, children: [_jsx("span", { className: "mr-2 flex h-6 w-6 shrink-0 items-center justify-center rounded-md", style: {
|
|
515
|
+
background: `${role.color || '#64748b'}22`,
|
|
516
|
+
color: role.color || '#64748b',
|
|
517
|
+
}, "aria-hidden": "true", children: _jsx(DynamicIcon, { name: role.icon || 'Shield', className: "h-3.5 w-3.5" }) }), _jsx("span", { className: "truncate", children: role.label || role.name }), role.id === activeRoleId && (_jsx(Check, { className: "ml-auto h-4 w-4" }))] }, role.id))) })] })] }) })] }), updateRole && (_jsx(Button, { variant: "outline", size: "icon", className: "h-9 w-9 shrink-0", "aria-label": "Editar rol", disabled: !activeRole, onClick: openEditRole, children: _jsx(Pencil, { className: "h-4 w-4" }) })), deleteRole && (_jsx(Button, { variant: "outline", size: "icon", className: "h-9 w-9 shrink-0 text-destructive hover:text-destructive", "aria-label": "Eliminar rol", disabled: !activeRole, onClick: () => setDeleteOpen(true), children: _jsx(Trash2, { className: "h-4 w-4" }) }))] }), (general?.length ?? 0) > 0 && (_jsxs(_Fragment, { children: [_jsx(Separator, {}), _jsxs("div", { children: [_jsx("h3", { className: "mb-2 text-sm font-semibold", children: "Permisos Generales" }), _jsx("div", { className: "flex flex-col gap-2", children: general.map((g) => (_jsx(CapabilityCheck, { checked: draft?.has(g.key) ?? false, disabled: checksDisabled, onToggle: () => toggleCapability(g.key), label: g.label, description: g.description }, g.key))) })] })] }))] })] }), _jsxs(Card, { children: [_jsxs(CardHeader, { children: [_jsx(CardTitle, { className: "text-base", children: "M\u00F3dulo" }), _jsx(CardDescription, { children: "Elige el m\u00F3dulo cuyas acciones quieres configurar." })] }), _jsx(CardContent, { children: _jsxs(Popover, { open: moduleOpen, onOpenChange: setModuleOpen, children: [_jsx(PopoverTrigger, { asChild: true, children: _jsxs(Button, { variant: "outline", role: "combobox", "aria-expanded": moduleOpen, className: "w-full justify-between font-normal", children: [_jsxs("span", { className: "flex min-w-0 items-center gap-2", children: [activeModule && (_jsx(DynamicIcon, { name: activeModule.icon ||
|
|
491
518
|
(activeModule.kind === 'screen'
|
|
492
519
|
? 'Eye'
|
|
493
520
|
: 'Square'), className: "h-4 w-4 shrink-0 opacity-70" })), _jsx("span", { className: "truncate", children: activeModule
|
|
@@ -506,9 +533,26 @@ export function PermissionsManager({ loadModules, loadRoles, loadRolePermissions
|
|
|
506
533
|
: 'Configura los permisos del módulo seleccionado.' })] }), activeRole && activeModule && (_jsxs("div", { className: "flex items-center gap-2", children: [_jsxs(Badge, { variant: "secondary", className: "tabular-nums", children: [moduleGranted, "/", moduleTotal] }), _jsxs(Button, { variant: "outline", size: "sm", className: "h-8", disabled: checksDisabled || moduleGranted === moduleTotal, onClick: () => setModuleAll(true), children: [_jsx(CheckCheck, { className: "mr-1.5 h-3.5 w-3.5" }), " Marcar todo"] }), _jsxs(Button, { variant: "outline", size: "sm", className: "h-8", disabled: checksDisabled || moduleGranted === 0, onClick: () => setModuleAll(false), children: [_jsx(Eraser, { className: "mr-1.5 h-3.5 w-3.5" }), " Limpiar"] })] }))] }) }), _jsx(CardContent, { children: !activeRole ? (_jsx(EmptyHint, { text: "Selecciona un rol para configurar sus permisos." })) : loadingPerms ? (_jsx("div", { className: "grid gap-2 sm:grid-cols-2", children: Array.from({ length: 6 }).map((_, i) => (_jsx(Skeleton, { className: "h-14 w-full" }, i))) })) : !activeModule ? (_jsx(EmptyHint, { text: "Selecciona un m\u00F3dulo de la lista para ver sus acciones." })) : (_jsx(ModuleActionsPanel, { module: activeModule, draft: draft, checksDisabled: checksDisabled, onToggle: toggleCapability })) })] })] }), _jsx(AlertDialog, { open: pendingRoleId !== null, onOpenChange: (open) => !open && setPendingRoleId(null), children: _jsxs(AlertDialogContent, { children: [_jsxs(AlertDialogHeader, { children: [_jsx(AlertDialogTitle, { children: "Cambios sin guardar" }), _jsx(AlertDialogDescription, { children: "Tienes cambios sin guardar en este rol. Si cambias de rol se descartar\u00E1n." })] }), _jsxs(AlertDialogFooter, { children: [_jsx(AlertDialogCancel, { children: "Cancelar" }), _jsx(AlertDialogAction, { onClick: () => {
|
|
507
534
|
setActiveRoleId(pendingRoleId);
|
|
508
535
|
setPendingRoleId(null);
|
|
509
|
-
}, children: "Descartar y cambiar" })] })] }) }), _jsx(Dialog, { open: roleDialog.open, onOpenChange: (open) => setRoleDialog((d) => ({ ...d, open })), children: _jsxs(DialogContent, { className: "sm:max-w-md", children: [_jsx(DialogHeader, { children: _jsx(DialogTitle, { children: roleDialog.mode === 'create' ? 'Nuevo rol' : 'Editar rol' }) }), _jsxs("div", { className: "flex flex-col gap-4 py-2", children: [_jsxs("div", { className: "flex flex-col gap-2", children: [_jsx(Label, { htmlFor: "pm-role-name", children: "Nombre del rol" }), _jsx(Input, { id: "pm-role-name", value: roleDialog.label, placeholder: "Ej. Cajero", onChange: (e) =>
|
|
510
|
-
|
|
511
|
-
|
|
536
|
+
}, children: "Descartar y cambiar" })] })] }) }), _jsx(Dialog, { open: roleDialog.open, onOpenChange: (open) => setRoleDialog((d) => ({ ...d, open })), children: _jsxs(DialogContent, { className: "sm:max-w-md", children: [_jsx(DialogHeader, { children: _jsx(DialogTitle, { children: roleDialog.mode === 'create' ? 'Nuevo rol' : 'Editar rol' }) }), _jsxs("div", { className: "flex flex-col gap-4 py-2", children: [_jsxs("div", { className: "flex flex-col gap-2", children: [_jsx(Label, { htmlFor: "pm-role-name", children: "Nombre del rol" }), _jsx(Input, { id: "pm-role-name", value: roleDialog.label, placeholder: "Ej. Cajero", onChange: (e) => {
|
|
537
|
+
const label = e.target.value;
|
|
538
|
+
setRoleDialog((d) => ({
|
|
539
|
+
...d,
|
|
540
|
+
label,
|
|
541
|
+
icon: d.icon === suggestRoleIcon(d.label) ||
|
|
542
|
+
d.icon === 'Shield' ||
|
|
543
|
+
!d.icon
|
|
544
|
+
? suggestRoleIcon(label)
|
|
545
|
+
: d.icon,
|
|
546
|
+
}));
|
|
547
|
+
} })] }), _jsxs("div", { className: "flex flex-col gap-2", children: [_jsx(Label, { children: "\u00CDcono" }), _jsx(IconPickerField, { field: {
|
|
548
|
+
key: 'icon',
|
|
549
|
+
label: 'Ícono',
|
|
550
|
+
type: 'text',
|
|
551
|
+
widget: 'icon',
|
|
552
|
+
}, value: roleDialog.icon, onChange: (v) => setRoleDialog((d) => ({
|
|
553
|
+
...d,
|
|
554
|
+
icon: typeof v === 'string' && v ? v : 'Shield',
|
|
555
|
+
})) })] }), _jsxs("div", { className: "flex flex-col gap-2", children: [_jsx(Label, { children: "Color" }), _jsx(ColorPickerField, { "aria-label": "Color del rol", value: roleDialog.color, onChange: (color) => setRoleDialog((d) => ({ ...d, color })) })] }), roleDialog.mode === 'create' && (_jsxs("label", { className: "flex cursor-pointer items-start gap-3 rounded-lg border p-3 text-sm hover:bg-muted/40", children: [_jsx("input", { type: "checkbox", className: "mt-0.5 size-4 accent-emerald-600", checked: roleDialog.grantAll, onChange: (e) => setRoleDialog((d) => ({
|
|
512
556
|
...d,
|
|
513
557
|
grantAll: e.target.checked,
|
|
514
558
|
})) }), _jsxs("span", { children: [_jsx("span", { className: "font-medium", children: "Otorgar todos los permisos" }), _jsx("span", { className: "mt-0.5 block text-xs text-muted-foreground", children: "Acceso completo a todos los m\u00F3dulos del cat\u00E1logo (como un admin). Puedes quitar permisos despu\u00E9s." })] })] }))] }), _jsxs(DialogFooter, { children: [_jsx(Button, { variant: "outline", onClick: () => setRoleDialog((d) => ({ ...d, open: false })), disabled: roleSaving, children: "Cancelar" }), _jsx(Button, { onClick: handleRoleSubmit, disabled: roleSaving || !roleDialog.label.trim(), children: roleSaving
|
package/package.json
CHANGED
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { describe, expect, it } from 'vitest'
|
|
2
|
+
import { normalizeHex, DEFAULT_ROLE_COLOR } from '../color-picker-field'
|
|
3
|
+
|
|
4
|
+
describe('normalizeHex', () => {
|
|
5
|
+
it('accepts #rgb and expands', () => {
|
|
6
|
+
expect(normalizeHex('#0af')).toBe('#00aaff')
|
|
7
|
+
})
|
|
8
|
+
it('lowercases #rrggbb', () => {
|
|
9
|
+
expect(normalizeHex('#3B82F6')).toBe('#3b82f6')
|
|
10
|
+
})
|
|
11
|
+
it('rejects garbage', () => {
|
|
12
|
+
expect(normalizeHex('blue')).toBe('')
|
|
13
|
+
expect(normalizeHex('#gg0000')).toBe('')
|
|
14
|
+
})
|
|
15
|
+
it('default constant is valid', () => {
|
|
16
|
+
expect(normalizeHex(DEFAULT_ROLE_COLOR)).toBe('#3b82f6')
|
|
17
|
+
})
|
|
18
|
+
})
|
|
@@ -0,0 +1,296 @@
|
|
|
1
|
+
// ColorPickerField — modern popover color control (SV plane + hue + hex).
|
|
2
|
+
// Used by PermissionsManager role dialog and DynamicForm `type: "color"`.
|
|
3
|
+
// Value is always a #rrggbb string (empty → falls back to DEFAULT_HEX visually).
|
|
4
|
+
import { useCallback, useEffect, useMemo, useRef, useState } from 'react'
|
|
5
|
+
import { Pipette } from 'lucide-react'
|
|
6
|
+
import { cn } from '@asteby/metacore-ui/lib'
|
|
7
|
+
import {
|
|
8
|
+
Button,
|
|
9
|
+
Input,
|
|
10
|
+
Popover,
|
|
11
|
+
PopoverContent,
|
|
12
|
+
PopoverTrigger,
|
|
13
|
+
} from '@asteby/metacore-ui/primitives'
|
|
14
|
+
|
|
15
|
+
export const DEFAULT_ROLE_COLOR = '#3b82f6'
|
|
16
|
+
|
|
17
|
+
export interface ColorPickerFieldProps {
|
|
18
|
+
value?: string
|
|
19
|
+
onChange: (hex: string) => void
|
|
20
|
+
/** Optional label for a11y on the trigger. */
|
|
21
|
+
'aria-label'?: string
|
|
22
|
+
className?: string
|
|
23
|
+
disabled?: boolean
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
type HSV = { h: number; s: number; v: number }
|
|
27
|
+
|
|
28
|
+
function clamp(n: number, min: number, max: number) {
|
|
29
|
+
return Math.min(max, Math.max(min, n))
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
/** Normalize to #rrggbb or '' if invalid. */
|
|
33
|
+
export function normalizeHex(raw: unknown): string {
|
|
34
|
+
if (typeof raw !== 'string') return ''
|
|
35
|
+
let s = raw.trim()
|
|
36
|
+
if (!s) return ''
|
|
37
|
+
if (s[0] !== '#') s = `#${s}`
|
|
38
|
+
if (/^#[0-9a-fA-F]{3}$/.test(s)) {
|
|
39
|
+
const r = s[1],
|
|
40
|
+
g = s[2],
|
|
41
|
+
b = s[3]
|
|
42
|
+
s = `#${r}${r}${g}${g}${b}${b}`
|
|
43
|
+
}
|
|
44
|
+
if (!/^#[0-9a-fA-F]{6}$/.test(s)) return ''
|
|
45
|
+
return s.toLowerCase()
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
function hexToRgb(hex: string): { r: number; g: number; b: number } | null {
|
|
49
|
+
const n = normalizeHex(hex)
|
|
50
|
+
if (!n) return null
|
|
51
|
+
return {
|
|
52
|
+
r: parseInt(n.slice(1, 3), 16),
|
|
53
|
+
g: parseInt(n.slice(3, 5), 16),
|
|
54
|
+
b: parseInt(n.slice(5, 7), 16),
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
function rgbToHex(r: number, g: number, b: number): string {
|
|
59
|
+
const h = (n: number) =>
|
|
60
|
+
clamp(Math.round(n), 0, 255).toString(16).padStart(2, '0')
|
|
61
|
+
return `#${h(r)}${h(g)}${h(b)}`
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
function rgbToHsv(r: number, g: number, b: number): HSV {
|
|
65
|
+
r /= 255
|
|
66
|
+
g /= 255
|
|
67
|
+
b /= 255
|
|
68
|
+
const max = Math.max(r, g, b)
|
|
69
|
+
const min = Math.min(r, g, b)
|
|
70
|
+
const d = max - min
|
|
71
|
+
let h = 0
|
|
72
|
+
if (d !== 0) {
|
|
73
|
+
if (max === r) h = ((g - b) / d) % 6
|
|
74
|
+
else if (max === g) h = (b - r) / d + 2
|
|
75
|
+
else h = (r - g) / d + 4
|
|
76
|
+
h *= 60
|
|
77
|
+
if (h < 0) h += 360
|
|
78
|
+
}
|
|
79
|
+
const s = max === 0 ? 0 : d / max
|
|
80
|
+
return { h, s, v: max }
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
function hsvToRgb(h: number, s: number, v: number): { r: number; g: number; b: number } {
|
|
84
|
+
const c = v * s
|
|
85
|
+
const x = c * (1 - Math.abs(((h / 60) % 2) - 1))
|
|
86
|
+
const m = v - c
|
|
87
|
+
let rp = 0,
|
|
88
|
+
gp = 0,
|
|
89
|
+
bp = 0
|
|
90
|
+
if (h < 60) [rp, gp, bp] = [c, x, 0]
|
|
91
|
+
else if (h < 120) [rp, gp, bp] = [x, c, 0]
|
|
92
|
+
else if (h < 180) [rp, gp, bp] = [0, c, x]
|
|
93
|
+
else if (h < 240) [rp, gp, bp] = [0, x, c]
|
|
94
|
+
else if (h < 300) [rp, gp, bp] = [x, 0, c]
|
|
95
|
+
else [rp, gp, bp] = [c, 0, x]
|
|
96
|
+
return {
|
|
97
|
+
r: (rp + m) * 255,
|
|
98
|
+
g: (gp + m) * 255,
|
|
99
|
+
b: (bp + m) * 255,
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
function hexToHsv(hex: string): HSV {
|
|
104
|
+
const rgb = hexToRgb(hex) || hexToRgb(DEFAULT_ROLE_COLOR)!
|
|
105
|
+
return rgbToHsv(rgb.r, rgb.g, rgb.b)
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
function hsvToHex(hsv: HSV): string {
|
|
109
|
+
const { r, g, b } = hsvToRgb(hsv.h, hsv.s, hsv.v)
|
|
110
|
+
return rgbToHex(r, g, b)
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
function hueCss(h: number): string {
|
|
114
|
+
const { r, g, b } = hsvToRgb(h, 1, 1)
|
|
115
|
+
return rgbToHex(r, g, b)
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
export function ColorPickerField({
|
|
119
|
+
value,
|
|
120
|
+
onChange,
|
|
121
|
+
'aria-label': ariaLabel = 'Color',
|
|
122
|
+
className,
|
|
123
|
+
disabled,
|
|
124
|
+
}: ColorPickerFieldProps) {
|
|
125
|
+
const hex = normalizeHex(value) || DEFAULT_ROLE_COLOR
|
|
126
|
+
const [open, setOpen] = useState(false)
|
|
127
|
+
const [hsv, setHsv] = useState<HSV>(() => hexToHsv(hex))
|
|
128
|
+
const [hexDraft, setHexDraft] = useState(hex)
|
|
129
|
+
const svRef = useRef<HTMLDivElement>(null)
|
|
130
|
+
const dragging = useRef(false)
|
|
131
|
+
|
|
132
|
+
// Sync from external value when popover closed (or first open).
|
|
133
|
+
useEffect(() => {
|
|
134
|
+
if (dragging.current) return
|
|
135
|
+
const next = normalizeHex(value) || DEFAULT_ROLE_COLOR
|
|
136
|
+
setHsv(hexToHsv(next))
|
|
137
|
+
setHexDraft(next)
|
|
138
|
+
}, [value, open])
|
|
139
|
+
|
|
140
|
+
const commit = useCallback(
|
|
141
|
+
(next: HSV) => {
|
|
142
|
+
setHsv(next)
|
|
143
|
+
const out = hsvToHex(next)
|
|
144
|
+
setHexDraft(out)
|
|
145
|
+
onChange(out)
|
|
146
|
+
},
|
|
147
|
+
[onChange],
|
|
148
|
+
)
|
|
149
|
+
|
|
150
|
+
const setFromPointer = useCallback(
|
|
151
|
+
(clientX: number, clientY: number) => {
|
|
152
|
+
const el = svRef.current
|
|
153
|
+
if (!el) return
|
|
154
|
+
const rect = el.getBoundingClientRect()
|
|
155
|
+
const s = clamp((clientX - rect.left) / rect.width, 0, 1)
|
|
156
|
+
const v = clamp(1 - (clientY - rect.top) / rect.height, 0, 1)
|
|
157
|
+
commit({ ...hsv, s, v })
|
|
158
|
+
},
|
|
159
|
+
[commit, hsv],
|
|
160
|
+
)
|
|
161
|
+
|
|
162
|
+
useEffect(() => {
|
|
163
|
+
if (!open) return
|
|
164
|
+
const onMove = (e: PointerEvent) => {
|
|
165
|
+
if (!dragging.current) return
|
|
166
|
+
setFromPointer(e.clientX, e.clientY)
|
|
167
|
+
}
|
|
168
|
+
const onUp = () => {
|
|
169
|
+
dragging.current = false
|
|
170
|
+
}
|
|
171
|
+
window.addEventListener('pointermove', onMove)
|
|
172
|
+
window.addEventListener('pointerup', onUp)
|
|
173
|
+
return () => {
|
|
174
|
+
window.removeEventListener('pointermove', onMove)
|
|
175
|
+
window.removeEventListener('pointerup', onUp)
|
|
176
|
+
}
|
|
177
|
+
}, [open, setFromPointer])
|
|
178
|
+
|
|
179
|
+
const svStyle = useMemo(
|
|
180
|
+
() => ({
|
|
181
|
+
background: `
|
|
182
|
+
linear-gradient(to top, #000, transparent),
|
|
183
|
+
linear-gradient(to right, #fff, ${hueCss(hsv.h)})
|
|
184
|
+
`,
|
|
185
|
+
}),
|
|
186
|
+
[hsv.h],
|
|
187
|
+
)
|
|
188
|
+
|
|
189
|
+
const applyHexDraft = () => {
|
|
190
|
+
const n = normalizeHex(hexDraft)
|
|
191
|
+
if (!n) {
|
|
192
|
+
setHexDraft(hsvToHex(hsv))
|
|
193
|
+
return
|
|
194
|
+
}
|
|
195
|
+
setHsv(hexToHsv(n))
|
|
196
|
+
setHexDraft(n)
|
|
197
|
+
onChange(n)
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
return (
|
|
201
|
+
<Popover open={open} onOpenChange={setOpen}>
|
|
202
|
+
<PopoverTrigger asChild>
|
|
203
|
+
<Button
|
|
204
|
+
type="button"
|
|
205
|
+
variant="outline"
|
|
206
|
+
disabled={disabled}
|
|
207
|
+
aria-label={ariaLabel}
|
|
208
|
+
className={cn(
|
|
209
|
+
'h-10 w-full justify-start gap-3 px-2.5 font-normal',
|
|
210
|
+
className,
|
|
211
|
+
)}
|
|
212
|
+
>
|
|
213
|
+
<span
|
|
214
|
+
className="h-6 w-6 shrink-0 rounded-md border border-black/10 shadow-sm ring-1 ring-black/5 dark:border-white/10"
|
|
215
|
+
style={{ background: hex }}
|
|
216
|
+
aria-hidden
|
|
217
|
+
/>
|
|
218
|
+
<span className="font-mono text-sm uppercase tracking-wide text-foreground">
|
|
219
|
+
{hex}
|
|
220
|
+
</span>
|
|
221
|
+
<Pipette className="ml-auto h-3.5 w-3.5 text-muted-foreground" />
|
|
222
|
+
</Button>
|
|
223
|
+
</PopoverTrigger>
|
|
224
|
+
<PopoverContent className="w-[260px] p-3" align="start">
|
|
225
|
+
<div className="flex flex-col gap-3">
|
|
226
|
+
<div
|
|
227
|
+
ref={svRef}
|
|
228
|
+
role="slider"
|
|
229
|
+
aria-label="Saturación y brillo"
|
|
230
|
+
tabIndex={0}
|
|
231
|
+
className="relative h-36 w-full cursor-crosshair touch-none overflow-hidden rounded-lg border border-border/60"
|
|
232
|
+
style={svStyle}
|
|
233
|
+
onPointerDown={(e) => {
|
|
234
|
+
dragging.current = true
|
|
235
|
+
;(e.target as HTMLElement).setPointerCapture?.(e.pointerId)
|
|
236
|
+
setFromPointer(e.clientX, e.clientY)
|
|
237
|
+
}}
|
|
238
|
+
>
|
|
239
|
+
<span
|
|
240
|
+
className="pointer-events-none absolute h-4 w-4 -translate-x-1/2 -translate-y-1/2 rounded-full border-2 border-white shadow-md ring-1 ring-black/30"
|
|
241
|
+
style={{
|
|
242
|
+
left: `${hsv.s * 100}%`,
|
|
243
|
+
top: `${(1 - hsv.v) * 100}%`,
|
|
244
|
+
background: hsvToHex(hsv),
|
|
245
|
+
}}
|
|
246
|
+
/>
|
|
247
|
+
</div>
|
|
248
|
+
|
|
249
|
+
<div className="flex flex-col gap-1.5">
|
|
250
|
+
<label className="text-[11px] font-medium uppercase tracking-wide text-muted-foreground">
|
|
251
|
+
Tono
|
|
252
|
+
</label>
|
|
253
|
+
<input
|
|
254
|
+
type="range"
|
|
255
|
+
min={0}
|
|
256
|
+
max={360}
|
|
257
|
+
step={1}
|
|
258
|
+
value={Math.round(hsv.h)}
|
|
259
|
+
aria-label="Tono"
|
|
260
|
+
onChange={(e) =>
|
|
261
|
+
commit({ ...hsv, h: Number(e.target.value) })
|
|
262
|
+
}
|
|
263
|
+
className="h-3 w-full cursor-pointer appearance-none rounded-full"
|
|
264
|
+
style={{
|
|
265
|
+
background:
|
|
266
|
+
'linear-gradient(to right, #f00, #ff0, #0f0, #0ff, #00f, #f0f, #f00)',
|
|
267
|
+
}}
|
|
268
|
+
/>
|
|
269
|
+
</div>
|
|
270
|
+
|
|
271
|
+
<div className="flex items-center gap-2">
|
|
272
|
+
<span
|
|
273
|
+
className="h-9 w-9 shrink-0 rounded-md border border-black/10 shadow-inner ring-1 ring-black/5"
|
|
274
|
+
style={{ background: hsvToHex(hsv) }}
|
|
275
|
+
aria-hidden
|
|
276
|
+
/>
|
|
277
|
+
<Input
|
|
278
|
+
value={hexDraft}
|
|
279
|
+
spellCheck={false}
|
|
280
|
+
aria-label="Hexadecimal"
|
|
281
|
+
className="h-9 font-mono uppercase"
|
|
282
|
+
onChange={(e) => setHexDraft(e.target.value)}
|
|
283
|
+
onBlur={applyHexDraft}
|
|
284
|
+
onKeyDown={(e) => {
|
|
285
|
+
if (e.key === 'Enter') {
|
|
286
|
+
e.preventDefault()
|
|
287
|
+
applyHexDraft()
|
|
288
|
+
}
|
|
289
|
+
}}
|
|
290
|
+
/>
|
|
291
|
+
</div>
|
|
292
|
+
</div>
|
|
293
|
+
</PopoverContent>
|
|
294
|
+
</Popover>
|
|
295
|
+
)
|
|
296
|
+
}
|
package/src/dynamic-form.tsx
CHANGED
|
@@ -35,6 +35,7 @@ import { DynamicSelectField } from './dynamic-select-field'
|
|
|
35
35
|
import { DynamicDateField } from './dynamic-date-field'
|
|
36
36
|
import { UploadField } from './upload-field'
|
|
37
37
|
import { IconPickerField } from './icon-picker-field'
|
|
38
|
+
import { ColorPickerField } from './color-picker-field'
|
|
38
39
|
|
|
39
40
|
export { buildZodSchema, resolveWidget }
|
|
40
41
|
export { DynamicLineItems } from './dynamic-line-items'
|
|
@@ -42,6 +43,7 @@ export { DynamicSelectField } from './dynamic-select-field'
|
|
|
42
43
|
export { DynamicDateField } from './dynamic-date-field'
|
|
43
44
|
export { UploadField } from './upload-field'
|
|
44
45
|
export { IconPickerField } from './icon-picker-field'
|
|
46
|
+
export { ColorPickerField, DEFAULT_ROLE_COLOR, normalizeHex } from './color-picker-field'
|
|
45
47
|
|
|
46
48
|
export interface DynamicFormProps {
|
|
47
49
|
fields: ActionFieldDef[]
|
|
@@ -399,7 +401,13 @@ function FieldRenderer({
|
|
|
399
401
|
// / future MDX editor pick it up without breaking the contract.
|
|
400
402
|
return <Textarea id={field.key} data-widget="richtext" value={value || ''} onChange={(e: React.ChangeEvent<HTMLTextAreaElement>) => onChange(e.target.value)} placeholder={field.placeholder} />
|
|
401
403
|
case 'color':
|
|
402
|
-
return
|
|
404
|
+
return (
|
|
405
|
+
<ColorPickerField
|
|
406
|
+
aria-label={field.label || field.key}
|
|
407
|
+
value={typeof value === 'string' ? value : ''}
|
|
408
|
+
onChange={onChange}
|
|
409
|
+
/>
|
|
410
|
+
)
|
|
403
411
|
case 'select':
|
|
404
412
|
return (
|
|
405
413
|
<Select value={value || ''} onValueChange={onChange}>
|
package/src/index.ts
CHANGED
|
@@ -178,6 +178,7 @@ export {
|
|
|
178
178
|
type UseDynamicRowActionsParams,
|
|
179
179
|
type DynamicRowActions,
|
|
180
180
|
} from './dynamic-row-actions'
|
|
181
|
+
export { ColorPickerField, DEFAULT_ROLE_COLOR, normalizeHex } from './color-picker-field'
|
|
181
182
|
export {
|
|
182
183
|
PermissionsManager,
|
|
183
184
|
moduleActionCapability,
|
|
@@ -185,6 +186,7 @@ export {
|
|
|
185
186
|
grantedCountForModule,
|
|
186
187
|
capabilitySetsEqual,
|
|
187
188
|
defaultActionIcon,
|
|
189
|
+
suggestRoleIcon,
|
|
188
190
|
normalizeCatalogGroups,
|
|
189
191
|
flattenGroups,
|
|
190
192
|
filterModuleGroups,
|
|
@@ -79,6 +79,9 @@ import {
|
|
|
79
79
|
CollapsibleTrigger,
|
|
80
80
|
} from '@asteby/metacore-ui/primitives'
|
|
81
81
|
import { DynamicIcon } from './dynamic-icon'
|
|
82
|
+
import { IconPickerField } from './icon-picker-field'
|
|
83
|
+
import { ColorPickerField, DEFAULT_ROLE_COLOR } from './color-picker-field'
|
|
84
|
+
import type { ActionFieldDef } from './types'
|
|
82
85
|
|
|
83
86
|
// ---------------------------------------------------------------------------
|
|
84
87
|
// Types (mirror of `GET /api/permissions/modules` + the host sidebar nav)
|
|
@@ -183,12 +186,15 @@ export interface RoleDef {
|
|
|
183
186
|
label?: string
|
|
184
187
|
/** Accent color (hex) for the role chip. */
|
|
185
188
|
color?: string
|
|
189
|
+
/** Lucide PascalCase name (or image path) for the role chip. */
|
|
190
|
+
icon?: string
|
|
186
191
|
}
|
|
187
192
|
|
|
188
193
|
export interface RoleInput {
|
|
189
194
|
name: string
|
|
190
195
|
label?: string
|
|
191
196
|
color?: string
|
|
197
|
+
icon?: string
|
|
192
198
|
}
|
|
193
199
|
|
|
194
200
|
export interface PermissionsManagerProps {
|
|
@@ -330,17 +336,28 @@ function slugify(label: string): string {
|
|
|
330
336
|
.replace(/^_+|_+$/g, '')
|
|
331
337
|
}
|
|
332
338
|
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
]
|
|
339
|
+
/** Suggest a Lucide icon from the role label (create-dialog default). */
|
|
340
|
+
export function suggestRoleIcon(label: string): string {
|
|
341
|
+
const s = fold(label)
|
|
342
|
+
const rules: Array<[RegExp, string]> = [
|
|
343
|
+
[/cajer|cash|teller|bank/, 'Banknote'],
|
|
344
|
+
[/vend|seller|sales|shop|mostrador/, 'ShoppingBag'],
|
|
345
|
+
[/mecanic|technic|wrench|taller/, 'Wrench'],
|
|
346
|
+
[/jefe|lead|manager|gerente|supervisor/, 'HardHat'],
|
|
347
|
+
[/almacen|ware|stock|invent/, 'Warehouse'],
|
|
348
|
+
[/compr|purch|buyer/, 'Truck'],
|
|
349
|
+
[/contad|account|ledger/, 'Calculator'],
|
|
350
|
+
[/nomina|payroll|sueldo/, 'Wallet'],
|
|
351
|
+
[/rh|hr|human|emplead|people/, 'Users'],
|
|
352
|
+
[/fleet|flota|vehic/, 'Car'],
|
|
353
|
+
[/admin|dueño|owner/, 'ShieldCheck'],
|
|
354
|
+
[/view|observa|lectura|read/, 'Eye'],
|
|
355
|
+
]
|
|
356
|
+
for (const [re, icon] of rules) {
|
|
357
|
+
if (re.test(s)) return icon
|
|
358
|
+
}
|
|
359
|
+
return 'Shield'
|
|
360
|
+
}
|
|
344
361
|
|
|
345
362
|
/**
|
|
346
363
|
* Normalize whatever `loadModules` returned into the canonical grouped shape.
|
|
@@ -548,8 +565,16 @@ export function PermissionsManager({
|
|
|
548
565
|
mode: 'create' | 'edit'
|
|
549
566
|
label: string
|
|
550
567
|
color: string
|
|
568
|
+
icon: string
|
|
551
569
|
grantAll: boolean
|
|
552
|
-
}>({
|
|
570
|
+
}>({
|
|
571
|
+
open: false,
|
|
572
|
+
mode: 'create',
|
|
573
|
+
label: '',
|
|
574
|
+
color: DEFAULT_ROLE_COLOR,
|
|
575
|
+
icon: 'Shield',
|
|
576
|
+
grantAll: false,
|
|
577
|
+
})
|
|
553
578
|
const [roleSaving, setRoleSaving] = React.useState(false)
|
|
554
579
|
const [deleteOpen, setDeleteOpen] = React.useState(false)
|
|
555
580
|
const [deleting, setDeleting] = React.useState(false)
|
|
@@ -711,6 +736,7 @@ export function PermissionsManager({
|
|
|
711
736
|
name: slugify(label),
|
|
712
737
|
label,
|
|
713
738
|
color: roleDialog.color,
|
|
739
|
+
icon: roleDialog.icon || suggestRoleIcon(label),
|
|
714
740
|
})
|
|
715
741
|
const rs = await loadRoles()
|
|
716
742
|
setRoles(rs)
|
|
@@ -732,6 +758,7 @@ export function PermissionsManager({
|
|
|
732
758
|
name: activeRole.name,
|
|
733
759
|
label,
|
|
734
760
|
color: roleDialog.color,
|
|
761
|
+
icon: roleDialog.icon || suggestRoleIcon(label),
|
|
735
762
|
})
|
|
736
763
|
await refreshRoles(activeRole.id)
|
|
737
764
|
toast.success('Rol actualizado')
|
|
@@ -769,7 +796,8 @@ export function PermissionsManager({
|
|
|
769
796
|
open: true,
|
|
770
797
|
mode: 'edit',
|
|
771
798
|
label: activeRole.label || activeRole.name,
|
|
772
|
-
color: activeRole.color ||
|
|
799
|
+
color: activeRole.color || DEFAULT_ROLE_COLOR,
|
|
800
|
+
icon: activeRole.icon || suggestRoleIcon(activeRole.label || activeRole.name),
|
|
773
801
|
grantAll: false,
|
|
774
802
|
})
|
|
775
803
|
}
|
|
@@ -843,7 +871,8 @@ export function PermissionsManager({
|
|
|
843
871
|
open: true,
|
|
844
872
|
mode: 'create',
|
|
845
873
|
label: '',
|
|
846
|
-
color:
|
|
874
|
+
color: DEFAULT_ROLE_COLOR,
|
|
875
|
+
icon: 'Shield',
|
|
847
876
|
grantAll: false,
|
|
848
877
|
})
|
|
849
878
|
}
|
|
@@ -901,12 +930,18 @@ export function PermissionsManager({
|
|
|
901
930
|
<span className="flex min-w-0 items-center gap-2">
|
|
902
931
|
{activeRole && (
|
|
903
932
|
<span
|
|
904
|
-
className="h-
|
|
933
|
+
className="flex h-6 w-6 shrink-0 items-center justify-center rounded-md"
|
|
905
934
|
style={{
|
|
906
|
-
background: activeRole.color || '#
|
|
935
|
+
background: `${activeRole.color || '#64748b'}22`,
|
|
936
|
+
color: activeRole.color || '#64748b',
|
|
907
937
|
}}
|
|
908
938
|
aria-hidden="true"
|
|
909
|
-
|
|
939
|
+
>
|
|
940
|
+
<DynamicIcon
|
|
941
|
+
name={activeRole.icon || 'Shield'}
|
|
942
|
+
className="h-3.5 w-3.5"
|
|
943
|
+
/>
|
|
944
|
+
</span>
|
|
910
945
|
)}
|
|
911
946
|
<span className="truncate">
|
|
912
947
|
{activeRole
|
|
@@ -933,12 +968,18 @@ export function PermissionsManager({
|
|
|
933
968
|
}}
|
|
934
969
|
>
|
|
935
970
|
<span
|
|
936
|
-
className="mr-2 h-
|
|
971
|
+
className="mr-2 flex h-6 w-6 shrink-0 items-center justify-center rounded-md"
|
|
937
972
|
style={{
|
|
938
|
-
background: role.color || '#
|
|
973
|
+
background: `${role.color || '#64748b'}22`,
|
|
974
|
+
color: role.color || '#64748b',
|
|
939
975
|
}}
|
|
940
976
|
aria-hidden="true"
|
|
941
|
-
|
|
977
|
+
>
|
|
978
|
+
<DynamicIcon
|
|
979
|
+
name={role.icon || 'Shield'}
|
|
980
|
+
className="h-3.5 w-3.5"
|
|
981
|
+
/>
|
|
982
|
+
</span>
|
|
942
983
|
<span className="truncate">
|
|
943
984
|
{role.label || role.name}
|
|
944
985
|
</span>
|
|
@@ -1224,30 +1265,48 @@ export function PermissionsManager({
|
|
|
1224
1265
|
id="pm-role-name"
|
|
1225
1266
|
value={roleDialog.label}
|
|
1226
1267
|
placeholder="Ej. Cajero"
|
|
1227
|
-
onChange={(e: React.ChangeEvent<HTMLInputElement>) =>
|
|
1228
|
-
|
|
1268
|
+
onChange={(e: React.ChangeEvent<HTMLInputElement>) => {
|
|
1269
|
+
const label = e.target.value
|
|
1270
|
+
setRoleDialog((d) => ({
|
|
1271
|
+
...d,
|
|
1272
|
+
label,
|
|
1273
|
+
icon:
|
|
1274
|
+
d.icon === suggestRoleIcon(d.label) ||
|
|
1275
|
+
d.icon === 'Shield' ||
|
|
1276
|
+
!d.icon
|
|
1277
|
+
? suggestRoleIcon(label)
|
|
1278
|
+
: d.icon,
|
|
1279
|
+
}))
|
|
1280
|
+
}}
|
|
1281
|
+
/>
|
|
1282
|
+
</div>
|
|
1283
|
+
<div className="flex flex-col gap-2">
|
|
1284
|
+
<Label>Ícono</Label>
|
|
1285
|
+
<IconPickerField
|
|
1286
|
+
field={
|
|
1287
|
+
{
|
|
1288
|
+
key: 'icon',
|
|
1289
|
+
label: 'Ícono',
|
|
1290
|
+
type: 'text',
|
|
1291
|
+
widget: 'icon',
|
|
1292
|
+
} as ActionFieldDef
|
|
1293
|
+
}
|
|
1294
|
+
value={roleDialog.icon}
|
|
1295
|
+
onChange={(v) =>
|
|
1296
|
+
setRoleDialog((d) => ({
|
|
1297
|
+
...d,
|
|
1298
|
+
icon: typeof v === 'string' && v ? v : 'Shield',
|
|
1299
|
+
}))
|
|
1229
1300
|
}
|
|
1230
1301
|
/>
|
|
1231
1302
|
</div>
|
|
1232
1303
|
<div className="flex flex-col gap-2">
|
|
1233
1304
|
<Label>Color</Label>
|
|
1234
|
-
<
|
|
1235
|
-
|
|
1236
|
-
|
|
1237
|
-
|
|
1238
|
-
|
|
1239
|
-
aria-label={`Color ${c}`}
|
|
1240
|
-
onClick={() => setRoleDialog((d) => ({ ...d, color: c }))}
|
|
1241
|
-
className={cn(
|
|
1242
|
-
'h-7 w-7 rounded-full border-2 transition-transform',
|
|
1243
|
-
roleDialog.color === c
|
|
1244
|
-
? 'scale-110 border-foreground'
|
|
1245
|
-
: 'border-transparent hover:scale-105',
|
|
1246
|
-
)}
|
|
1247
|
-
style={{ background: c }}
|
|
1248
|
-
/>
|
|
1249
|
-
))}
|
|
1250
|
-
</div>
|
|
1305
|
+
<ColorPickerField
|
|
1306
|
+
aria-label="Color del rol"
|
|
1307
|
+
value={roleDialog.color}
|
|
1308
|
+
onChange={(color) => setRoleDialog((d) => ({ ...d, color }))}
|
|
1309
|
+
/>
|
|
1251
1310
|
</div>
|
|
1252
1311
|
{roleDialog.mode === 'create' && (
|
|
1253
1312
|
<label className="flex cursor-pointer items-start gap-3 rounded-lg border p-3 text-sm hover:bg-muted/40">
|