feat: Rattachement d'un dossier de compétences à une période scolaire

(configuration dans l'établissement) [#16]
This commit is contained in:
N3WT DE COMPET
2025-05-22 01:25:34 +02:00
parent 0fe6c76189
commit 7de839ee5c
18 changed files with 450 additions and 274 deletions

View File

@ -32,7 +32,7 @@ export default function GradesDomainBarChart({ studentCompetencies }) {
};
return (
<div className="w-3/4 flex flex-col items-center gap-4 bg-stone-50 p-6 rounded-lg shadow-sm border border-gray-200">
<div className="w-full flex flex-col items-center gap-4 bg-stone-50 p-6 rounded-lg shadow-sm border border-gray-200">
<h2 className="text-xl font-semibold mb-2">Moyenne par domaine</h2>
<div className="w-full flex flex-col gap-2">
{domainStats.map((d) => (

View File

@ -1,117 +0,0 @@
import React, { useState, useEffect } from 'react';
import { BASE_URL } from '@/utils/Url';
export default function StudentInput({
label,
selectedStudent,
setSelectedStudent,
searchStudents,
establishmentId,
required = false,
}) {
const [inputValue, setInputValue] = useState('');
const [suggestions, setSuggestions] = useState([]);
const [selectedIndex, setSelectedIndex] = useState(-1);
// Désélectionner si l'input ne correspond plus à l'élève sélectionné
useEffect(() => {
if (
selectedStudent &&
inputValue !==
`${selectedStudent.last_name} ${selectedStudent.first_name}`
) {
setSelectedStudent(null);
}
// eslint-disable-next-line
}, [inputValue]);
const handleInputChange = async (e) => {
const value = e.target.value;
setInputValue(value);
if (value.trim() !== '') {
try {
const results = await searchStudents(establishmentId, value);
setSuggestions(results);
} catch {
setSuggestions([]);
}
} else {
setSuggestions([]);
}
};
const handleSuggestionClick = (student) => {
setSelectedStudent(student);
setInputValue(`${student.last_name} ${student.first_name}`);
setSuggestions([]);
};
const handleKeyDown = (e) => {
if (e.key === 'Enter') {
e.preventDefault();
if (selectedIndex >= 0 && selectedIndex < suggestions.length) {
handleSuggestionClick(suggestions[selectedIndex]);
}
} else if (e.key === 'ArrowDown') {
setSelectedIndex((prev) =>
prev < suggestions.length - 1 ? prev + 1 : 0
);
} else if (e.key === 'ArrowUp') {
setSelectedIndex((prev) =>
prev > 0 ? prev - 1 : suggestions.length - 1
);
}
};
return (
<div>
<label className="block text-sm font-medium text-gray-700">
{label}
{required && <span className="text-red-500 ml-1">*</span>}
</label>
<input
type="text"
value={inputValue}
onChange={handleInputChange}
onKeyDown={handleKeyDown}
placeholder="Rechercher un élève"
className="mt-1 px-3 py-2 block w-full border rounded-md"
required={required}
/>
{suggestions.length > 0 && (
<ul className="border rounded mt-2 bg-white shadow">
{suggestions.map((student, idx) => (
<li
key={student.id}
className={`flex items-center gap-2 p-2 cursor-pointer transition-colors ${
idx === selectedIndex
? 'bg-emerald-100 text-emerald-800'
: 'hover:bg-emerald-50'
}`}
onClick={() => handleSuggestionClick(student)}
onMouseEnter={() => setSelectedIndex(idx)}
>
{student.photo ? (
<img
src={`${BASE_URL}${student.photo}`}
alt={`${student.first_name} ${student.last_name}`}
className="w-8 h-8 object-cover rounded-full border"
/>
) : (
<div className="w-8 h-8 flex items-center justify-center bg-gray-200 rounded-full text-gray-500 font-semibold">
{student.first_name?.[0]}
{student.last_name?.[0]}
</div>
)}
<span>
{student.last_name} {student.first_name} ({student.level}) -{' '}
{student.associated_class_name}
</span>
</li>
))}
</ul>
)}
</div>
);
}