Icone app
This commit is contained in:
@@ -5,8 +5,8 @@
|
|||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||||
<meta name="theme-color" content="#2563eb" />
|
<meta name="theme-color" content="#2563eb" />
|
||||||
<link rel="manifest" href="/manifest.webmanifest" />
|
<link rel="manifest" href="/manifest.webmanifest" />
|
||||||
<link rel="icon" type="image/png" sizes="192x192" 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-192.png" />
|
<link rel="apple-touch-icon" href="/icons/professeur-top-install-v2-192.png" />
|
||||||
<title>Professeur virtuel</title>
|
<title>Professeur virtuel</title>
|
||||||
<script type="module" src="/src/main.jsx"></script>
|
<script type="module" src="/src/main.jsx"></script>
|
||||||
</head>
|
</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",
|
"theme_color": "#2563eb",
|
||||||
"icons": [
|
"icons": [
|
||||||
{
|
{
|
||||||
"src": "/icons/professeur-top-192.png",
|
"src": "/icons/professeur-top-install-v2-192.png",
|
||||||
"sizes": "192x192",
|
"sizes": "192x192",
|
||||||
"type": "image/png",
|
"type": "image/png",
|
||||||
"purpose": "any maskable"
|
"purpose": "any maskable"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"src": "/icons/professeur-top-512.png",
|
"src": "/icons/professeur-top-install-v2-512.png",
|
||||||
"sizes": "512x512",
|
"sizes": "512x512",
|
||||||
"type": "image/png",
|
"type": "image/png",
|
||||||
"purpose": "any maskable"
|
"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 [voiceProfiles, setVoiceProfiles] = useState([])
|
||||||
const [selectedVoiceProfileId, setSelectedVoiceProfileId] = useState('')
|
const [selectedVoiceProfileId, setSelectedVoiceProfileId] = useState('')
|
||||||
const [progressPanelOpen, setProgressPanelOpen] = useState(false)
|
const [progressPanelOpen, setProgressPanelOpen] = useState(false)
|
||||||
|
const [settingsPanelOpen, setSettingsPanelOpen] = useState(false)
|
||||||
const mediaRecorderRef = useRef(null)
|
const mediaRecorderRef = useRef(null)
|
||||||
const mediaStreamRef = useRef(null)
|
const mediaStreamRef = useRef(null)
|
||||||
const recordedChunksRef = useRef([])
|
const recordedChunksRef = useRef([])
|
||||||
@@ -493,6 +494,7 @@ function TutorApp({ currentUser, onLogout }) {
|
|||||||
deactivateAutoListening(false)
|
deactivateAutoListening(false)
|
||||||
setSessionActive(false)
|
setSessionActive(false)
|
||||||
setProgressPanelOpen(false)
|
setProgressPanelOpen(false)
|
||||||
|
setSettingsPanelOpen(false)
|
||||||
setCurrentInstruction('')
|
setCurrentInstruction('')
|
||||||
setAssessment(null)
|
setAssessment(null)
|
||||||
setAssessmentAnswer('')
|
setAssessmentAnswer('')
|
||||||
@@ -942,6 +944,15 @@ function TutorApp({ currentUser, onLogout }) {
|
|||||||
<h1>{lessonFocus?.label || 'Cours du jour'}</h1>
|
<h1>{lessonFocus?.label || 'Cours du jour'}</h1>
|
||||||
</div>
|
</div>
|
||||||
<div className="lesson-actions">
|
<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)}>
|
<button type="button" className="secondary-button" onClick={() => setProgressPanelOpen(true)}>
|
||||||
Progression
|
Progression
|
||||||
</button>
|
</button>
|
||||||
@@ -973,18 +984,6 @@ function TutorApp({ currentUser, onLogout }) {
|
|||||||
}
|
}
|
||||||
placeholder={assessment ? 'Ta reponse au mini-test...' : 'Ecris ta reponse...'}
|
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}>
|
<button type="button" onClick={startVoiceInput} disabled={isTranscribing}>
|
||||||
{isAutoListening ? 'Stop micro' : isTranscribing ? 'Transcription...' : 'Dicter'}
|
{isAutoListening ? 'Stop micro' : isTranscribing ? 'Transcription...' : 'Dicter'}
|
||||||
</button>
|
</button>
|
||||||
@@ -1012,6 +1011,38 @@ function TutorApp({ currentUser, onLogout }) {
|
|||||||
</aside>
|
</aside>
|
||||||
</div>
|
</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>
|
</main>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -8,3 +8,9 @@ ReactDOM.createRoot(document.getElementById('root')).render(
|
|||||||
<App />
|
<App />
|
||||||
</React.StrictMode>
|
</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;
|
display: flex;
|
||||||
gap: 0.75rem;
|
gap: 0.75rem;
|
||||||
}
|
}
|
||||||
|
.icon-button {
|
||||||
|
width: 44px;
|
||||||
|
min-width: 44px;
|
||||||
|
padding: 0.8rem 0;
|
||||||
|
background: #e2e8f0;
|
||||||
|
color: #14213d;
|
||||||
|
font-weight: 800;
|
||||||
|
}
|
||||||
.student-home-grid {
|
.student-home-grid {
|
||||||
display: grid;
|
display: grid;
|
||||||
grid-template-columns: repeat(2, minmax(0, 1fr));
|
grid-template-columns: repeat(2, minmax(0, 1fr));
|
||||||
@@ -379,7 +387,7 @@ button:disabled { opacity: 0.5; cursor: not-allowed; }
|
|||||||
}
|
}
|
||||||
.lesson-composer {
|
.lesson-composer {
|
||||||
display: grid;
|
display: grid;
|
||||||
grid-template-columns: minmax(0, 1fr) 150px 120px 110px;
|
grid-template-columns: minmax(0, 1fr) 120px 110px;
|
||||||
gap: 0.6rem;
|
gap: 0.6rem;
|
||||||
}
|
}
|
||||||
.lesson-status {
|
.lesson-status {
|
||||||
@@ -410,6 +418,14 @@ button:disabled { opacity: 0.5; cursor: not-allowed; }
|
|||||||
gap: 1rem;
|
gap: 1rem;
|
||||||
margin-bottom: 1rem;
|
margin-bottom: 1rem;
|
||||||
}
|
}
|
||||||
|
.settings-field {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 0.5rem;
|
||||||
|
}
|
||||||
|
.settings-field span {
|
||||||
|
font-weight: 700;
|
||||||
|
}
|
||||||
|
|
||||||
@keyframes pulse {
|
@keyframes pulse {
|
||||||
from { transform: scale(1); }
|
from { transform: scale(1); }
|
||||||
@@ -538,6 +554,11 @@ button:disabled { opacity: 0.5; cursor: not-allowed; }
|
|||||||
width: 100%;
|
width: 100%;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.icon-button {
|
||||||
|
width: 44px;
|
||||||
|
min-width: 44px;
|
||||||
|
}
|
||||||
|
|
||||||
.student-home {
|
.student-home {
|
||||||
padding: 0.5rem;
|
padding: 0.5rem;
|
||||||
}
|
}
|
||||||
@@ -594,8 +615,7 @@ button:disabled { opacity: 0.5; cursor: not-allowed; }
|
|||||||
gap: 0.5rem;
|
gap: 0.5rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
.lesson-composer input,
|
.lesson-composer input {
|
||||||
.lesson-composer select {
|
|
||||||
grid-column: 1 / -1;
|
grid-column: 1 / -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user