diff --git a/src/features/admin/components/RoleEditModal.client.js b/src/features/admin/components/RoleEditModal.client.js index 48f999d..d78aaa4 100644 --- a/src/features/admin/components/RoleEditModal.client.js +++ b/src/features/admin/components/RoleEditModal.client.js @@ -1,7 +1,7 @@ 'use client'; import { useState, useEffect } from 'react'; -import { Input, Textarea, Switch, Modal } from '@zen/core/shared/components'; +import { Input, Textarea, Switch, Modal, ColorPicker } from '@zen/core/shared/components'; import { useToast } from '@zen/core/toast'; import { getPermissionGroups } from '@zen/core/users/constants'; @@ -136,18 +136,13 @@ const RoleEditModal = ({ roleId, isOpen, onClose, onSaved }) => { rows={2} placeholder="Description optionnelle..." /> -
- - setColor(e.target.value)} - className="w-8 h-8 rounded cursor-pointer border border-neutral-200 dark:border-neutral-700" - /> - {color} -
+
@@ -155,7 +150,7 @@ const RoleEditModal = ({ roleId, isOpen, onClose, onSaved }) => { {Object.entries(PERMISSION_GROUPS).map(([group, perms]) => (
-

+

{group}

diff --git a/src/shared/components/ColorPicker.client.js b/src/shared/components/ColorPicker.client.js new file mode 100644 index 0000000..832147e --- /dev/null +++ b/src/shared/components/ColorPicker.client.js @@ -0,0 +1,150 @@ +'use client'; + +import { useState, useRef } from 'react'; + +const PRESET_COLORS = [ + '#1abc9c', '#2ecc71', '#3498db', '#9b59b6', '#e91e63', + '#f1c40f', '#e67e22', '#e74c3c', '#95a5a6', '#607d8b', + '#11806a', '#1f8b4c', '#206694', '#71368a', '#ad1457', + '#c27c0e', '#a84300', '#992d22', '#979c9f', '#546e7a', +]; + +const isValidHex = (hex) => /^#[0-9a-fA-F]{6}$/.test(hex); + +const ColorPicker = ({ + value, + onChange, + label, + description, + required = false, + disabled = false, +}) => { + const [hexInput, setHexInput] = useState(value || '#6b7280'); + const nativeRef = useRef(null); + + const selected = isValidHex(value) ? value.toLowerCase() : '#6b7280'; + + const handleSwatchClick = (color) => { + if (disabled) return; + setHexInput(color); + onChange?.(color); + }; + + const handleHexChange = (e) => { + const raw = e.target.value; + setHexInput(raw); + const normalized = raw.startsWith('#') ? raw : `#${raw}`; + if (isValidHex(normalized)) { + onChange?.(normalized.toLowerCase()); + } + }; + + const handleHexBlur = () => { + const normalized = hexInput.startsWith('#') ? hexInput : `#${hexInput}`; + if (isValidHex(normalized)) { + const lower = normalized.toLowerCase(); + setHexInput(lower); + onChange?.(lower); + } else { + setHexInput(selected); + } + }; + + const handleCustomClick = () => { + if (disabled) return; + nativeRef.current?.click(); + }; + + const handleNativeChange = (e) => { + const color = e.target.value.toLowerCase(); + setHexInput(color); + onChange?.(color); + }; + + return ( +
+ {label && ( + + )} + {description && ( +

{description}

+ )} + +
+
+ {/* Custom color swatch */} + + + {/* Preset swatches */} + {PRESET_COLORS.map((color) => { + const isSelected = selected === color.toLowerCase(); + return ( + + ); + })} +
+ + {/* Hex input */} +
+
+ +
+
+
+ ); +}; + +export default ColorPicker; diff --git a/src/shared/components/index.js b/src/shared/components/index.js index b72ce0d..94f29d9 100644 --- a/src/shared/components/index.js +++ b/src/shared/components/index.js @@ -27,3 +27,4 @@ export { default as Breadcrumb } from './Breadcrumb'; export { default as Switch } from './Switch'; export { default as TagInput } from './TagInput'; export { default as UserAvatar } from './UserAvatar'; +export { default as ColorPicker } from './ColorPicker.client';