+
Rôles
-
Activez les rôles à attribuer à cet utilisateur.
- {allRoles.length === 0 ? (
-
Aucun rôle disponible
- ) : (
-
- {allRoles.map((role) => (
- toggleRole(role.id)}
- label={
-
-
- {role.name}
-
- }
- description={role.description || undefined}
- />
- ))}
-
- )}
+
diff --git a/src/shared/components/TagInput.js b/src/shared/components/TagInput.js
new file mode 100644
index 0000000..d567b82
--- /dev/null
+++ b/src/shared/components/TagInput.js
@@ -0,0 +1,168 @@
+'use client';
+
+import { useState, useRef, useEffect } from 'react';
+
+const hexToRgb = (hex) => {
+ const result = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(hex);
+ return result
+ ? { r: parseInt(result[1], 16), g: parseInt(result[2], 16), b: parseInt(result[3], 16) }
+ : null;
+};
+
+const Pill = ({ option, onRemove }) => {
+ const rgb = option.color ? hexToRgb(option.color) : null;
+ const pillStyle = rgb
+ ? { backgroundColor: `rgba(${rgb.r},${rgb.g},${rgb.b},0.15)`, borderColor: `rgba(${rgb.r},${rgb.g},${rgb.b},0.4)`, color: option.color }
+ : {};
+ const fallbackClass = !rgb
+ ? 'bg-neutral-100 dark:bg-neutral-700 border-neutral-200 dark:border-neutral-600 text-neutral-700 dark:text-neutral-300'
+ : '';
+
+ return (
+
+ {option.color && (
+
+ )}
+ {option.label}
+
+
+ );
+};
+
+const TagInput = ({
+ options = [],
+ value = [],
+ onChange,
+ label,
+ description,
+ placeholder = 'Ajouter...',
+ error,
+}) => {
+ const [inputValue, setInputValue] = useState('');
+ const [isOpen, setIsOpen] = useState(false);
+ const containerRef = useRef(null);
+ const inputRef = useRef(null);
+
+ const selectedOptions = value.map(v => options.find(o => o.value === v)).filter(Boolean);
+ const filtered = options.filter(o =>
+ !value.includes(o.value) &&
+ o.label.toLowerCase().includes(inputValue.toLowerCase())
+ );
+
+ useEffect(() => {
+ const handleClickOutside = (e) => {
+ if (containerRef.current && !containerRef.current.contains(e.target)) {
+ setIsOpen(false);
+ }
+ };
+ document.addEventListener('mousedown', handleClickOutside);
+ return () => document.removeEventListener('mousedown', handleClickOutside);
+ }, []);
+
+ const selectOption = (option) => {
+ onChange([...value, option.value]);
+ setInputValue('');
+ inputRef.current?.focus();
+ };
+
+ const removeOption = (val) => {
+ onChange(value.filter(v => v !== val));
+ };
+
+ const handleKeyDown = (e) => {
+ if (e.key === 'Backspace' && inputValue === '' && value.length > 0) {
+ onChange(value.slice(0, -1));
+ }
+ if (e.key === 'Escape') {
+ setIsOpen(false);
+ }
+ };
+
+ return (
+
+ {label && (
+
+ )}
+
+
+
+ {description && !error && (
+
{description}
+ )}
+ {error && (
+
{error}
+ )}
+
+ );
+};
+
+export default TagInput;
diff --git a/src/shared/components/index.js b/src/shared/components/index.js
index b95cf2e..6cf585c 100644
--- a/src/shared/components/index.js
+++ b/src/shared/components/index.js
@@ -26,3 +26,4 @@ export { default as PasswordStrengthIndicator } from './PasswordStrengthIndicato
export { default as FilterTabs } from './FilterTabs';
export { default as Breadcrumb } from './Breadcrumb';
export { default as Switch } from './Switch';
+export { default as TagInput } from './TagInput';