split cards

This commit is contained in:
2026-05-01 21:12:05 +02:00
parent 214dd0b6a6
commit 2b65876525
6 changed files with 171 additions and 25 deletions

View File

@@ -212,6 +212,7 @@ function TutorApp({ currentUser, onLogout }) {
const [programStatus, setProgramStatus] = useState(null)
const [studentProgram, setStudentProgram] = useState(null)
const [activeAssetIndex, setActiveAssetIndex] = useState(0)
const [activeSectionIndex, setActiveSectionIndex] = useState(0)
const [assetFullscreenOpen, setAssetFullscreenOpen] = useState(false)
const [progressPanelOpen, setProgressPanelOpen] = useState(false)
const [settingsPanelOpen, setSettingsPanelOpen] = useState(false)
@@ -323,6 +324,7 @@ function TutorApp({ currentUser, onLogout }) {
useEffect(() => {
setActiveAssetIndex(0)
setActiveSectionIndex(0)
}, [studentProgram?.id])
useEffect(() => {
@@ -588,6 +590,30 @@ function TutorApp({ currentUser, onLogout }) {
}
}
function goToPreviousLessonStep() {
if (activeSectionIndex > 0) {
setActiveSectionIndex((index) => index - 1)
return
}
if (activeAssetIndex > 0) {
const previousAsset = lessonAssets[activeAssetIndex - 1]
setActiveAssetIndex((index) => index - 1)
setActiveSectionIndex(Math.max((previousAsset?.sections?.length || 1) - 1, 0))
}
}
function goToNextLessonStep() {
const sectionCount = activeSections.length || 1
if (activeSectionIndex < sectionCount - 1) {
setActiveSectionIndex((index) => index + 1)
return
}
if (activeAssetIndex < lessonAssets.length - 1) {
setActiveAssetIndex((index) => index + 1)
setActiveSectionIndex(0)
}
}
async function finishSpeechPlayback(debugMessage) {
setSpeaking(false)
if (audioPlaybackUrlRef.current) {
@@ -941,6 +967,21 @@ function TutorApp({ currentUser, onLogout }) {
const activeLessonTitle = studentProgram?.title || lessonFocus?.label || 'Cours du jour'
const lessonAssets = studentProgram?.assets || []
const activeAsset = lessonAssets[activeAssetIndex] || lessonAssets[0] || null
const activeSections = activeAsset?.sections || []
const activeSection = activeSections[activeSectionIndex] || null
const totalLessonSteps = lessonAssets.reduce(
(total, asset) => total + Math.max(asset.sections?.length || 0, 1),
0
)
const completedBeforeAsset = lessonAssets
.slice(0, activeAssetIndex)
.reduce((total, asset) => total + Math.max(asset.sections?.length || 0, 1), 0)
const activeLessonStep = totalLessonSteps
? completedBeforeAsset + (activeSection ? activeSectionIndex + 1 : 1)
: 0
const lessonStepPercent = totalLessonSteps
? Math.min(100, Math.round((activeLessonStep / totalLessonSteps) * 100))
: 0
const lessonSummary = lastAssistantMessage?.content || 'Aucune ancienne séance trouvée pour le moment. Professeur TOP commencera par une reprise courte.'
const lastStudentWork = lastUserMessage?.content || 'Aucune réponse récente enregistrée.'
const nextLessonAdvice = studentProgram
@@ -1033,7 +1074,7 @@ function TutorApp({ currentUser, onLogout }) {
<span className="stage-label">Professeur TOP</span>
{activeAsset && (
<span className="asset-counter">
Fiche {activeAssetIndex + 1}/{lessonAssets.length}
{lessonStepPercent}% · Fiche {activeAssetIndex + 1}/{lessonAssets.length}
</span>
)}
</div>
@@ -1045,21 +1086,21 @@ function TutorApp({ currentUser, onLogout }) {
{activeAsset && (
<div className="lesson-asset-view" aria-label="Fiche de leçon active">
<div className="lesson-asset-toolbar">
<strong>{activeAsset.title}</strong>
<strong>{activeSection?.title || activeAsset.title}</strong>
<div className="lesson-asset-actions">
<button
type="button"
className="secondary-button"
onClick={() => setActiveAssetIndex((index) => Math.max(index - 1, 0))}
disabled={activeAssetIndex === 0}
onClick={goToPreviousLessonStep}
disabled={activeLessonStep <= 1}
>
Précédente
</button>
<button
type="button"
className="secondary-button"
onClick={() => setActiveAssetIndex((index) => Math.min(index + 1, lessonAssets.length - 1))}
disabled={activeAssetIndex >= lessonAssets.length - 1}
onClick={goToNextLessonStep}
disabled={activeLessonStep >= totalLessonSteps}
>
Suivante
</button>
@@ -1071,7 +1112,10 @@ function TutorApp({ currentUser, onLogout }) {
onClick={() => setAssetFullscreenOpen(true)}
aria-label="Agrandir la fiche"
>
<img src={activeAsset.url} alt={activeAsset.title} />
<img
src={activeSection?.url || activeAsset.url}
alt={activeSection?.title || activeAsset.title}
/>
<span>Agrandir la fiche</span>
</button>
</div>
@@ -1154,28 +1198,33 @@ function TutorApp({ currentUser, onLogout }) {
{assetFullscreenOpen && activeAsset && (
<div className="asset-fullscreen" role="dialog" aria-label="Fiche agrandie">
<div className="asset-fullscreen-bar">
<strong>Fiche {activeAssetIndex + 1}/{lessonAssets.length}</strong>
<strong>
Étape {activeLessonStep}/{totalLessonSteps} · {activeSection?.title || activeAsset.title}
</strong>
<button type="button" className="secondary-button" onClick={() => setAssetFullscreenOpen(false)}>
Fermer
</button>
</div>
<div className="asset-fullscreen-stage">
<img src={activeAsset.url} alt={activeAsset.title} />
<img
src={activeSection?.url || activeAsset.url}
alt={activeSection?.title || activeAsset.title}
/>
</div>
<div className="asset-fullscreen-controls">
<button
type="button"
className="secondary-button"
onClick={() => setActiveAssetIndex((index) => Math.max(index - 1, 0))}
disabled={activeAssetIndex === 0}
onClick={goToPreviousLessonStep}
disabled={activeLessonStep <= 1}
>
Précédente
</button>
<button
type="button"
className="secondary-button"
onClick={() => setActiveAssetIndex((index) => Math.min(index + 1, lessonAssets.length - 1))}
disabled={activeAssetIndex >= lessonAssets.length - 1}
onClick={goToNextLessonStep}
disabled={activeLessonStep >= totalLessonSteps}
>
Suivante
</button>

View File

@@ -824,7 +824,7 @@ button:disabled { opacity: 0.5; cursor: not-allowed; }
}
.lesson-asset-open img {
max-height: 24dvh;
max-height: 31dvh;
}
.asset-fullscreen {
@@ -832,7 +832,8 @@ button:disabled { opacity: 0.5; cursor: not-allowed; }
}
.asset-fullscreen-stage img {
width: max(100%, 1120px);
width: 100%;
min-height: 100%;
}
.asset-fullscreen-controls {