create eleve
This commit is contained in:
@@ -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>
|
||||
|
||||
@@ -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;
|
||||
|
||||
Reference in New Issue
Block a user