Cards control
This commit is contained in:
@@ -217,6 +217,7 @@ function TutorApp({ currentUser, onLogout }) {
|
||||
const [cardProgress, setCardProgress] = useState([])
|
||||
const [sessionCardStates, setSessionCardStates] = useState({})
|
||||
const [sessionLogs, setSessionLogs] = useState([])
|
||||
const [selectedSessionLog, setSelectedSessionLog] = useState(null)
|
||||
const [progressPanelOpen, setProgressPanelOpen] = useState(false)
|
||||
const [settingsPanelOpen, setSettingsPanelOpen] = useState(false)
|
||||
const mediaRecorderRef = useRef(null)
|
||||
@@ -309,6 +310,7 @@ function TutorApp({ currentUser, onLogout }) {
|
||||
setCardProgress([])
|
||||
setSessionCardStates({})
|
||||
setSessionLogs([])
|
||||
setSelectedSessionLog(null)
|
||||
if (selectedStudentId) {
|
||||
loadProgress(selectedStudentId)
|
||||
loadMessages(selectedStudentId)
|
||||
@@ -520,6 +522,7 @@ function TutorApp({ currentUser, onLogout }) {
|
||||
|
||||
async function saveLessonSession() {
|
||||
if (!selectedStudentId || !studentProgram) return
|
||||
const currentCardState = getActiveCardState(40, false)
|
||||
const conversation = messages.map((message) => ({
|
||||
id: message.id,
|
||||
role: message.role,
|
||||
@@ -542,10 +545,11 @@ function TutorApp({ currentUser, onLogout }) {
|
||||
lesson_id: studentProgram.id,
|
||||
lesson_title: studentProgram.title,
|
||||
conversation,
|
||||
card_states: getMergedCardStates(),
|
||||
card_states: getMergedCardStates(currentCardState),
|
||||
}),
|
||||
})
|
||||
await loadCardProgress(selectedStudentId, studentProgram.id)
|
||||
if (!isStudentUser) await loadSessionLogs(selectedStudentId)
|
||||
}
|
||||
|
||||
async function endSession() {
|
||||
@@ -697,12 +701,22 @@ function TutorApp({ currentUser, onLogout }) {
|
||||
async function deleteSessionLog(logId) {
|
||||
try {
|
||||
await apiFetch(`/admin/session-logs/${logId}`, { method: 'DELETE' })
|
||||
setSelectedSessionLog((current) => (current?.id === logId ? null : current))
|
||||
await loadSessionLogs(selectedStudentId)
|
||||
} catch (error) {
|
||||
setErrorMessage(error.message || 'Impossible d’effacer ce log.')
|
||||
}
|
||||
}
|
||||
|
||||
function parseLogJson(value) {
|
||||
try {
|
||||
const parsed = JSON.parse(value || '[]')
|
||||
return Array.isArray(parsed) ? parsed : []
|
||||
} catch {
|
||||
return []
|
||||
}
|
||||
}
|
||||
|
||||
function goToPreviousLessonStep() {
|
||||
if (activeSectionIndex > 0) {
|
||||
setActiveSectionIndex((index) => index - 1)
|
||||
@@ -764,7 +778,7 @@ function TutorApp({ currentUser, onLogout }) {
|
||||
})
|
||||
}
|
||||
|
||||
function getMergedCardStates() {
|
||||
function getMergedCardStates(extraState = null) {
|
||||
const fromProgress = cardProgress
|
||||
.filter((item) => item.lesson_id === studentProgram?.id)
|
||||
.map((item) => ({
|
||||
@@ -780,6 +794,7 @@ function TutorApp({ currentUser, onLogout }) {
|
||||
const merged = {}
|
||||
for (const item of fromProgress) merged[item.section_key] = item
|
||||
for (const item of Object.values(sessionCardStates)) merged[item.section_key] = item
|
||||
if (extraState) merged[extraState.section_key] = extraState
|
||||
return Object.values(merged)
|
||||
}
|
||||
|
||||
@@ -1590,9 +1605,14 @@ function TutorApp({ currentUser, onLogout }) {
|
||||
<strong>{log.lesson_title}</strong>
|
||||
<span>{new Date(log.ended_at).toLocaleString('fr-FR')}</span>
|
||||
</div>
|
||||
<button type="button" className="secondary-button" onClick={() => deleteSessionLog(log.id)}>
|
||||
Effacer
|
||||
</button>
|
||||
<div className="admin-log-actions">
|
||||
<button type="button" className="secondary-button" onClick={() => setSelectedSessionLog(log)}>
|
||||
Voir
|
||||
</button>
|
||||
<button type="button" className="secondary-button" onClick={() => deleteSessionLog(log.id)}>
|
||||
Effacer
|
||||
</button>
|
||||
</div>
|
||||
</article>
|
||||
))}
|
||||
</div>
|
||||
@@ -1602,6 +1622,32 @@ function TutorApp({ currentUser, onLogout }) {
|
||||
</section>
|
||||
)}
|
||||
|
||||
{selectedSessionLog && (
|
||||
<section className="admin-preview">
|
||||
<div className="admin-log-detail-head">
|
||||
<h2>Détail de séance</h2>
|
||||
<button type="button" className="secondary-button" onClick={() => setSelectedSessionLog(null)}>
|
||||
Fermer
|
||||
</button>
|
||||
</div>
|
||||
<div className="admin-log-detail">
|
||||
<h3>Résultats des cards</h3>
|
||||
{parseLogJson(selectedSessionLog.card_results).map((item) => (
|
||||
<article key={item.section_key} className="admin-log-card">
|
||||
<strong>{item.section_title}</strong>
|
||||
<span>{Math.round(item.comprehension_score)}% · {item.validated ? 'validée' : 'vue/tentée'}</span>
|
||||
</article>
|
||||
))}
|
||||
<h3>Conversation</h3>
|
||||
{parseLogJson(selectedSessionLog.conversation).map((message, index) => (
|
||||
<p key={`${message.id || index}`} className="admin-log-message">
|
||||
<strong>{message.role === 'assistant' ? 'Professeur TOP' : 'Élève'}:</strong> {message.content}
|
||||
</p>
|
||||
))}
|
||||
</div>
|
||||
</section>
|
||||
)}
|
||||
|
||||
{errorMessage && <p className="muted voice-status visible-status">{errorMessage}</p>}
|
||||
</main>
|
||||
</div>
|
||||
|
||||
@@ -217,6 +217,35 @@ button:disabled { opacity: 0.5; cursor: not-allowed; }
|
||||
color: #64748b;
|
||||
font-size: 0.9rem;
|
||||
}
|
||||
.admin-log-actions {
|
||||
display: flex;
|
||||
gap: 0.5rem;
|
||||
}
|
||||
.admin-log-detail-head {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
gap: 1rem;
|
||||
}
|
||||
.admin-log-detail {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 0.65rem;
|
||||
border: 1px solid #e5e7eb;
|
||||
border-radius: 12px;
|
||||
background: #f8fafc;
|
||||
padding: 1rem;
|
||||
}
|
||||
.admin-log-detail h3 {
|
||||
margin: 0.6rem 0 0;
|
||||
}
|
||||
.admin-log-detail h3:first-child {
|
||||
margin-top: 0;
|
||||
}
|
||||
.admin-log-message {
|
||||
margin: 0;
|
||||
line-height: 1.45;
|
||||
}
|
||||
.session-user {
|
||||
margin-left: auto;
|
||||
display: flex;
|
||||
|
||||
Reference in New Issue
Block a user