mirror of
https://git.v0id.ovh/n3wt-innov/n3wt-school.git
synced 2026-01-28 23:43:22 +00:00
145 lines
5.3 KiB
JavaScript
145 lines
5.3 KiB
JavaScript
import React, {
|
|
useState,
|
|
useEffect,
|
|
forwardRef,
|
|
useImperativeHandle,
|
|
} from 'react';
|
|
import { CheckCircle, Circle } from 'lucide-react';
|
|
|
|
const TreeView = forwardRef(function TreeView(
|
|
{ data, expandAll, onSelectionChange },
|
|
ref
|
|
) {
|
|
const [openDomains, setOpenDomains] = useState({});
|
|
const [openCategories, setOpenCategories] = useState({});
|
|
const [selectedCompetencies, setSelectedCompetencies] = useState({}); // { [competence_id]: true }
|
|
|
|
// N'ouvre ou ne ferme tout que si expandAll change explicitement
|
|
useEffect(() => {
|
|
if (!data) return;
|
|
const allDomains = {};
|
|
const allCategories = {};
|
|
if (expandAll) {
|
|
data.forEach((domaine) => {
|
|
allDomains[domaine.domaine_id] = true;
|
|
domaine.categories.forEach((cat) => {
|
|
allCategories[cat.categorie_id] = true;
|
|
});
|
|
});
|
|
setOpenDomains(allDomains);
|
|
setOpenCategories(allCategories);
|
|
} else {
|
|
// On ne ferme tout que si l'utilisateur décoche explicitement "Tout dérouler"
|
|
setOpenDomains({});
|
|
setOpenCategories({});
|
|
}
|
|
}, [expandAll]); // <-- uniquement expandAll
|
|
|
|
// Appelle le callback à chaque changement de sélection
|
|
useEffect(() => {
|
|
if (onSelectionChange) {
|
|
const selected = Object.entries(selectedCompetencies)
|
|
.filter(([_, selected]) => selected)
|
|
.map(([id]) => id);
|
|
onSelectionChange(selected);
|
|
}
|
|
}, [selectedCompetencies, onSelectionChange]);
|
|
|
|
const toggleDomain = (id) =>
|
|
setOpenDomains((prev) => ({ ...prev, [id]: !prev[id] }));
|
|
const toggleCategory = (id) =>
|
|
setOpenCategories((prev) => ({ ...prev, [id]: !prev[id] }));
|
|
|
|
const handleCompetenceClick = (competence) => {
|
|
if (competence.state === 'required') return;
|
|
setSelectedCompetencies((prev) => {
|
|
const next = {
|
|
...prev,
|
|
[competence.competence_id]: !prev[competence.competence_id],
|
|
};
|
|
console.log(competence);
|
|
return next;
|
|
});
|
|
};
|
|
|
|
// Pour exposer la sélection au parent
|
|
useImperativeHandle(ref, () => ({
|
|
getSelectedCompetencies: () => {
|
|
const selected = Object.entries(selectedCompetencies)
|
|
.filter(([_, selected]) => selected)
|
|
.map(([id]) => id);
|
|
return selected;
|
|
},
|
|
clearSelection: () => setSelectedCompetencies({}),
|
|
}));
|
|
|
|
return (
|
|
<div>
|
|
{data.map((domaine) => (
|
|
<div key={domaine.domaine_id} className="mb-4">
|
|
<button
|
|
className="w-full text-left px-3 py-2 bg-emerald-100 hover:bg-emerald-200 rounded font-semibold text-emerald-800"
|
|
onClick={() => toggleDomain(domaine.domaine_id)}
|
|
>
|
|
{openDomains[domaine.domaine_id] ? '▼' : '►'} {domaine.domaine_nom}
|
|
</button>
|
|
{openDomains[domaine.domaine_id] && (
|
|
<div className="ml-4">
|
|
{domaine.categories.map((categorie) => (
|
|
<div key={categorie.categorie_id} className="mb-2">
|
|
<button
|
|
className="w-full text-left px-2 py-1 bg-emerald-50 hover:bg-emerald-100 rounded text-emerald-700"
|
|
onClick={() => toggleCategory(categorie.categorie_id)}
|
|
>
|
|
{openCategories[categorie.categorie_id] ? '▼' : '►'}
|
|
{categorie.categorie_nom}
|
|
</button>
|
|
{openCategories[categorie.categorie_id] && (
|
|
<ul className="ml-4">
|
|
{categorie.competences.map((competence) => {
|
|
const isSelected =
|
|
selectedCompetencies[competence.competence_id];
|
|
return (
|
|
<li
|
|
key={competence.competence_id}
|
|
className={`py-1 flex items-center gap-2 ${
|
|
competence.state === 'required'
|
|
? 'text-emerald-700 font-bold'
|
|
: competence.state === 'custom'
|
|
? isSelected
|
|
? 'text-gray-500 cursor-pointer hover:text-emerald-600'
|
|
: 'text-emerald-600 font-semibold cursor-pointer'
|
|
: isSelected
|
|
? 'text-emerald-600 font-semibold cursor-pointer'
|
|
: 'text-gray-500 cursor-pointer hover:text-emerald-600'
|
|
}`}
|
|
onClick={() => handleCompetenceClick(competence)}
|
|
style={{
|
|
cursor:
|
|
competence.state === 'required'
|
|
? 'default'
|
|
: 'pointer',
|
|
userSelect: 'none',
|
|
}}
|
|
>
|
|
{competence.state === 'required' && (
|
|
<CheckCircle className="w-4 h-4 text-emerald-500" />
|
|
)}
|
|
{competence.nom}
|
|
</li>
|
|
);
|
|
})}
|
|
</ul>
|
|
)}
|
|
</div>
|
|
))}
|
|
</div>
|
|
)}
|
|
</div>
|
|
))}
|
|
</div>
|
|
);
|
|
});
|
|
|
|
export default TreeView;
|