create eleve

This commit is contained in:
2026-04-25 20:35:43 +02:00
parent bd7c67fc93
commit c420d539da
5 changed files with 215 additions and 46 deletions

View File

@@ -171,9 +171,16 @@ function LoginPage({ onLogin }) {
}
function TutorApp({ currentUser, onLogout }) {
const isStudentUser = currentUser.role === 'student'
const [students, setStudents] = useState([])
const [selectedStudentId, setSelectedStudentId] = useState('')
const [form, setForm] = useState({ first_name: '', age: 8, grade: 'CM1' })
const [form, setForm] = useState({
first_name: '',
age: 8,
grade: 'CM1',
username: '',
password: '',
})
const [messages, setMessages] = useState([])
const [input, setInput] = useState('')
const [progress, setProgress] = useState([])
@@ -218,9 +225,15 @@ function TutorApp({ currentUser, onLogout }) {
[voiceProfiles, selectedVoiceProfileId]
)
const averageScore = useMemo(() => {
if (!progress.length) return 0
const total = progress.reduce((sum, item) => sum + item.mastery_score, 0)
return Math.round(total / progress.length)
}, [progress])
useEffect(() => {
loadStudents()
}, [])
}, [currentUser])
function pushAudioDebug(message) {
if (!DEBUG_AUDIO) return
@@ -265,6 +278,18 @@ function TutorApp({ currentUser, onLogout }) {
}, [])
async function loadStudents() {
if (isStudentUser) {
if (currentUser.student) {
setStudents([currentUser.student])
setSelectedStudentId(String(currentUser.student.id))
} else {
setStudents([])
setSelectedStudentId('')
setErrorMessage('Ce compte eleve n est pas encore rattache a une fiche eleve.')
}
return
}
try {
setErrorMessage('')
const data = await apiFetch('/students')
@@ -301,22 +326,33 @@ function TutorApp({ currentUser, onLogout }) {
async function createStudent(e) {
e.preventDefault()
if (isStudentUser) return
if (!form.first_name.trim()) {
setErrorMessage('Le prénom est obligatoire.')
return
}
if (!form.username.trim() || !form.password) {
setErrorMessage('Identifiant et mot de passe eleve obligatoires.')
return
}
try {
setErrorMessage('')
const data = await apiFetch('/students', {
const data = await apiFetch('/admin/student-accounts', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ ...form, first_name: form.first_name.trim(), age: Number(form.age) }),
body: JSON.stringify({
...form,
first_name: form.first_name.trim(),
username: form.username.trim(),
age: Number(form.age),
}),
})
await loadStudents()
setSelectedStudentId(String(data.id))
setSelectedStudentId(String(data.student.id))
setForm({ first_name: '', age: 8, grade: 'CM1', username: '', password: '' })
} catch (error) {
setErrorMessage(error.message || 'Impossible de créer lélève.')
setErrorMessage(error.message || 'Impossible de creer le compte eleve.')
}
}
@@ -744,37 +780,60 @@ function TutorApp({ currentUser, onLogout }) {
return (
<div className="layout">
<aside className="sidebar card">
<h2>Élève</h2>
<select value={selectedStudentId} onChange={(event) => setSelectedStudentId(event.target.value)}>
<option value="">Choisir un élève</option>
{students.map((student) => (
<option key={student.id} value={student.id}>
{student.first_name} · {student.grade}
</option>
))}
</select>
<h2>{isStudentUser ? 'Ma seance' : 'Eleves'}</h2>
{!isStudentUser && (
<>
<select value={selectedStudentId} onChange={(event) => setSelectedStudentId(event.target.value)}>
<option value="">Choisir un eleve</option>
{students.map((student) => (
<option key={student.id} value={student.id}>
{student.first_name} · {student.grade}
</option>
))}
</select>
<form onSubmit={createStudent} className="stack">
<input
placeholder="Prénom"
value={form.first_name}
onChange={(event) => setForm({ ...form, first_name: event.target.value })}
/>
<input
type="number"
min="8"
max="12"
value={form.age}
onChange={(event) => setForm({ ...form, age: event.target.value })}
/>
<select value={form.grade} onChange={(event) => setForm({ ...form, grade: event.target.value })}>
<option>CE2</option>
<option>CM1</option>
<option>CM2</option>
<option>6e</option>
</select>
<button type="submit">Créer un élève</button>
</form>
<form onSubmit={createStudent} className="stack">
<input
placeholder="Prenom"
value={form.first_name}
onChange={(event) => setForm({ ...form, first_name: event.target.value })}
/>
<input
type="number"
min="8"
max="12"
value={form.age}
onChange={(event) => setForm({ ...form, age: event.target.value })}
/>
<select value={form.grade} onChange={(event) => setForm({ ...form, grade: event.target.value })}>
<option>CE2</option>
<option>CM1</option>
<option>CM2</option>
<option>6e</option>
</select>
<input
placeholder="Identifiant eleve"
value={form.username}
onChange={(event) => setForm({ ...form, username: event.target.value })}
/>
<input
placeholder="Mot de passe eleve"
type="password"
value={form.password}
onChange={(event) => setForm({ ...form, password: event.target.value })}
/>
<button type="submit">Creer le compte eleve</button>
</form>
</>
)}
{isStudentUser && selectedStudent && (
<div className="session-summary">
<strong>{selectedStudent.first_name}</strong>
<span>{selectedStudent.grade}</span>
<span>Score moyen: {averageScore}%</span>
</div>
)}
<button onClick={startSession} disabled={!selectedStudentId}>Démarrer la séance</button>
<button onClick={fetchAssessment} disabled={!selectedStudentId}>Lancer un mini-test</button>
@@ -797,7 +856,7 @@ function TutorApp({ currentUser, onLogout }) {
</p>
</div>
<div className="session-user">
<span>{currentUser.username} · {currentUser.role}</span>
<span>{currentUser.username} · {currentUser.role}</span>
<button type="button" className="secondary-button" onClick={onLogout}>
Deconnexion
</button>

View File

@@ -41,6 +41,15 @@ button:disabled { opacity: 0.5; cursor: not-allowed; }
.stack { display: flex; flex-direction: column; gap: 0.75rem; }
.small-gap { gap: 0.5rem; }
.hero { display: flex; align-items: center; gap: 1rem; flex-wrap: wrap; }
.session-summary {
display: flex;
flex-direction: column;
gap: 0.35rem;
border: 1px solid #e5e7eb;
border-radius: 16px;
padding: 0.75rem;
background: #f8fafc;
}
.session-user {
margin-left: auto;
display: flex;