Icone app
This commit is contained in:
@@ -5,8 +5,8 @@
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<meta name="theme-color" content="#2563eb" />
|
||||
<link rel="manifest" href="/manifest.webmanifest" />
|
||||
<link rel="icon" type="image/png" sizes="192x192" href="/icons/professeur-top-192.png" />
|
||||
<link rel="apple-touch-icon" href="/icons/professeur-top-192.png" />
|
||||
<link rel="icon" type="image/png" sizes="192x192" href="/icons/professeur-top-install-v2-192.png" />
|
||||
<link rel="apple-touch-icon" href="/icons/professeur-top-install-v2-192.png" />
|
||||
<title>Professeur virtuel</title>
|
||||
<script type="module" src="/src/main.jsx"></script>
|
||||
</head>
|
||||
|
||||
Binary file not shown.
|
Before Width: | Height: | Size: 42 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 268 KiB |
BIN
frontend/public/icons/professeur-top-install-v2-192.png
Normal file
BIN
frontend/public/icons/professeur-top-install-v2-192.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 44 KiB |
BIN
frontend/public/icons/professeur-top-install-v2-512.png
Normal file
BIN
frontend/public/icons/professeur-top-install-v2-512.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 289 KiB |
@@ -9,13 +9,13 @@
|
||||
"theme_color": "#2563eb",
|
||||
"icons": [
|
||||
{
|
||||
"src": "/icons/professeur-top-192.png",
|
||||
"src": "/icons/professeur-top-install-v2-192.png",
|
||||
"sizes": "192x192",
|
||||
"type": "image/png",
|
||||
"purpose": "any maskable"
|
||||
},
|
||||
{
|
||||
"src": "/icons/professeur-top-512.png",
|
||||
"src": "/icons/professeur-top-install-v2-512.png",
|
||||
"sizes": "512x512",
|
||||
"type": "image/png",
|
||||
"purpose": "any maskable"
|
||||
|
||||
9
frontend/public/sw.js
Normal file
9
frontend/public/sw.js
Normal file
@@ -0,0 +1,9 @@
|
||||
self.addEventListener('install', () => {
|
||||
self.skipWaiting()
|
||||
})
|
||||
|
||||
self.addEventListener('activate', (event) => {
|
||||
event.waitUntil(self.clients.claim())
|
||||
})
|
||||
|
||||
self.addEventListener('fetch', () => {})
|
||||
@@ -209,6 +209,7 @@ function TutorApp({ currentUser, onLogout }) {
|
||||
const [voiceProfiles, setVoiceProfiles] = useState([])
|
||||
const [selectedVoiceProfileId, setSelectedVoiceProfileId] = useState('')
|
||||
const [progressPanelOpen, setProgressPanelOpen] = useState(false)
|
||||
const [settingsPanelOpen, setSettingsPanelOpen] = useState(false)
|
||||
const mediaRecorderRef = useRef(null)
|
||||
const mediaStreamRef = useRef(null)
|
||||
const recordedChunksRef = useRef([])
|
||||
@@ -493,6 +494,7 @@ function TutorApp({ currentUser, onLogout }) {
|
||||
deactivateAutoListening(false)
|
||||
setSessionActive(false)
|
||||
setProgressPanelOpen(false)
|
||||
setSettingsPanelOpen(false)
|
||||
setCurrentInstruction('')
|
||||
setAssessment(null)
|
||||
setAssessmentAnswer('')
|
||||
@@ -942,6 +944,15 @@ function TutorApp({ currentUser, onLogout }) {
|
||||
<h1>{lessonFocus?.label || 'Cours du jour'}</h1>
|
||||
</div>
|
||||
<div className="lesson-actions">
|
||||
<button
|
||||
type="button"
|
||||
className="icon-button"
|
||||
aria-label="Parametres"
|
||||
title="Parametres"
|
||||
onClick={() => setSettingsPanelOpen(true)}
|
||||
>
|
||||
...
|
||||
</button>
|
||||
<button type="button" className="secondary-button" onClick={() => setProgressPanelOpen(true)}>
|
||||
Progression
|
||||
</button>
|
||||
@@ -973,18 +984,6 @@ function TutorApp({ currentUser, onLogout }) {
|
||||
}
|
||||
placeholder={assessment ? 'Ta reponse au mini-test...' : 'Ecris ta reponse...'}
|
||||
/>
|
||||
<select
|
||||
value={selectedVoiceProfileId}
|
||||
onChange={(event) => setSelectedVoiceProfileId(event.target.value)}
|
||||
title="Choisir la voix de Professeur TOP"
|
||||
>
|
||||
<option value="">Voix</option>
|
||||
{voiceProfiles.map((profile) => (
|
||||
<option key={profile.id} value={profile.id}>
|
||||
{formatVoiceLabel(profile)}
|
||||
</option>
|
||||
))}
|
||||
</select>
|
||||
<button type="button" onClick={startVoiceInput} disabled={isTranscribing}>
|
||||
{isAutoListening ? 'Stop micro' : isTranscribing ? 'Transcription...' : 'Dicter'}
|
||||
</button>
|
||||
@@ -1012,6 +1011,38 @@ function TutorApp({ currentUser, onLogout }) {
|
||||
</aside>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{settingsPanelOpen && (
|
||||
<div className="progress-panel-backdrop" role="presentation" onClick={() => setSettingsPanelOpen(false)}>
|
||||
<aside className="progress-panel card" role="dialog" aria-label="Parametres" onClick={(event) => event.stopPropagation()}>
|
||||
<div className="progress-panel-head">
|
||||
<h2>Parametres</h2>
|
||||
<button type="button" className="secondary-button" onClick={() => setSettingsPanelOpen(false)}>
|
||||
Fermer
|
||||
</button>
|
||||
</div>
|
||||
<label className="settings-field">
|
||||
<span>Voix de Professeur TOP</span>
|
||||
<select
|
||||
value={selectedVoiceProfileId}
|
||||
onChange={(event) => setSelectedVoiceProfileId(event.target.value)}
|
||||
>
|
||||
<option value="">Voix de Professeur TOP</option>
|
||||
{voiceProfiles.map((profile) => (
|
||||
<option key={profile.id} value={profile.id}>
|
||||
{formatVoiceLabel(profile)}
|
||||
</option>
|
||||
))}
|
||||
</select>
|
||||
</label>
|
||||
{selectedVoiceProfile && (
|
||||
<p className="muted voice-note visible-status">
|
||||
Voix IA non humaine: {selectedVoiceProfile.label} - {selectedVoiceProfile.description}
|
||||
</p>
|
||||
)}
|
||||
</aside>
|
||||
</div>
|
||||
)}
|
||||
</main>
|
||||
)}
|
||||
</div>
|
||||
|
||||
@@ -8,3 +8,9 @@ ReactDOM.createRoot(document.getElementById('root')).render(
|
||||
<App />
|
||||
</React.StrictMode>
|
||||
)
|
||||
|
||||
if ('serviceWorker' in navigator) {
|
||||
window.addEventListener('load', () => {
|
||||
navigator.serviceWorker.register('/sw.js').catch(() => {})
|
||||
})
|
||||
}
|
||||
|
||||
@@ -307,6 +307,14 @@ button:disabled { opacity: 0.5; cursor: not-allowed; }
|
||||
display: flex;
|
||||
gap: 0.75rem;
|
||||
}
|
||||
.icon-button {
|
||||
width: 44px;
|
||||
min-width: 44px;
|
||||
padding: 0.8rem 0;
|
||||
background: #e2e8f0;
|
||||
color: #14213d;
|
||||
font-weight: 800;
|
||||
}
|
||||
.student-home-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(2, minmax(0, 1fr));
|
||||
@@ -379,7 +387,7 @@ button:disabled { opacity: 0.5; cursor: not-allowed; }
|
||||
}
|
||||
.lesson-composer {
|
||||
display: grid;
|
||||
grid-template-columns: minmax(0, 1fr) 150px 120px 110px;
|
||||
grid-template-columns: minmax(0, 1fr) 120px 110px;
|
||||
gap: 0.6rem;
|
||||
}
|
||||
.lesson-status {
|
||||
@@ -410,6 +418,14 @@ button:disabled { opacity: 0.5; cursor: not-allowed; }
|
||||
gap: 1rem;
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
.settings-field {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 0.5rem;
|
||||
}
|
||||
.settings-field span {
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
@keyframes pulse {
|
||||
from { transform: scale(1); }
|
||||
@@ -538,6 +554,11 @@ button:disabled { opacity: 0.5; cursor: not-allowed; }
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.icon-button {
|
||||
width: 44px;
|
||||
min-width: 44px;
|
||||
}
|
||||
|
||||
.student-home {
|
||||
padding: 0.5rem;
|
||||
}
|
||||
@@ -594,8 +615,7 @@ button:disabled { opacity: 0.5; cursor: not-allowed; }
|
||||
gap: 0.5rem;
|
||||
}
|
||||
|
||||
.lesson-composer input,
|
||||
.lesson-composer select {
|
||||
.lesson-composer input {
|
||||
grid-column: 1 / -1;
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user