// Initialize Supabase client const supabaseUrl = 'https://your-project.supabase.co'; const supabaseKey = 'your-anon-key'; const supabase = window.supabase.createClient(supabaseUrl, supabaseKey); // App State let currentView = 'dashboard'; let recordingSession = { sentences: [], currentIndex: 0, recordings: [], startTime: null }; let mediaRecorder = null; let audioChunks = []; let recordingStartTime = null; // DOM Elements const views = { dashboard: document.getElementById('dashboard-view'), recording: document.getElementById('recording-view'), admin: document.getElementById('admin-view') }; // Initialize app document.addEventListener('DOMContentLoaded', () => { initializeEventListeners(); loadDashboardData(); checkAdminAccess(); }); // Event Listeners function initializeEventListeners() { // Dashboard document.getElementById('start-recording-btn').addEventListener('click', startRecordingSession); document.getElementById('recent-recordings-btn').addEventListener('click', toggleRecentWork); // Recording interface document.getElementById('record-btn').addEventListener('click', toggleRecording); document.getElementById('skip-btn').addEventListener('click', skipSentence); document.getElementById('next-btn').addEventListener('click', nextSentence); // Navigation will be handled by navbar component } // Navigation function showView(viewName) { Object.keys(views).forEach(key => { views[key].classList.add('hidden'); }); if (views[viewName]) { views[viewName].classList.remove('hidden'); currentView = viewName; if (viewName === 'admin') { loadAdminData(); } else if (viewName === 'dashboard') { loadDashboardData(); } } } // Dashboard Functions async function loadDashboardData() { try { // Mock data for MVP const mockData = { totalEarnings: '12.50', completedCount: 125, pendingCount: 8 }; document.getElementById('total-earnings').textContent = mockData.totalEarnings; document.getElementById('completed-count').textContent = mockData.completedCount; document.getElementById('pending-count').textContent = mockData.pendingCount; loadRecentRecordings(); } catch (error) { console.error('Error loading dashboard:', error); } } function loadRecentRecordings() { // Mock recent recordings const recentRecordings = [ { id: 1, frenchText: "Bonjour, comment allez-vous?", status: 'approved', date: '2024-01-15' }, { id: 2, frenchText: "Je voudrais un café", status: 'pending', date: '2024-01-15' }, { id: 3, frenchText: "Où est la gare?", status: 'approved', date: '2024-01-14' } ]; const recentList = document.getElementById('recent-list'); recentList.innerHTML = recentRecordings.map(rec => `

${rec.frenchText}

${rec.date}

${rec.status}
`).join(''); } function toggleRecentWork() { const recentWork = document.getElementById('recent-work'); recentWork.classList.toggle('hidden'); } // Recording Session Functions async function startRecordingSession() { try { // Load 10 random sentences (mock data for MVP) recordingSession.sentences = [ { id: 1, french_text: "Bonjour, comment allez-vous?", domain: "greetings" }, { id: 2, french_text: "Je voudrais un café, s'il vous plaît", domain: "food" }, { id: 3, french_text: "Où se trouve la bibliothèque?", domain: "directions" }, { id: 4, french_text: "Quel temps fait-il aujourd'hui?", domain: "weather" }, { id: 5, french_text: "Merci beaucoup pour votre aide", domain: "gratitude" }, { id: 6, french_text: "Je ne comprends pas", domain: "communication" }, { id: 7, french_text: "À quelle heure fermez-vous?", domain: "time" }, { id: 8, french_text: "Combien ça coûte?", domain: "shopping" }, { id: 9, french_text: "Pouvez-vous répéter, s'il vous plaît?", domain: "communication" }, { id: 10, french_text: "J'ai besoin d'un taxi", domain: "transport" } ]; recordingSession.currentIndex = 0; recordingSession.recordings = []; recordingSession.startTime = new Date(); showView('recording'); loadCurrentSentence(); } catch (error) { console.error('Error starting session:', error); alert('Error starting recording session'); } } function loadCurrentSentence() { const sentence = recordingSession.sentences[recordingSession.currentIndex]; if (!sentence) return; document.getElementById('current-sentence').textContent = recordingSession.currentIndex + 1; document.getElementById('progress-bar').style.width = `${((recordingSession.currentIndex + 1) / 10) * 100}%`; document.getElementById('french-sentence').textContent = sentence.french_text; // Reset UI resetRecordingUI(); // Load French audio (mock - in real app would fetch from storage) const frenchAudio = document.getElementById('french-audio'); frenchAudio.src = `https://www.soundhelix.com/examples/mp3/SoundHelix-Song-1.mp3?t=${sentence.id}`; } function resetRecordingUI() { const recordBtn = document.getElementById('record-btn'); const recordingIndicator = document.getElementById('recording-indicator'); const pulaarAudioSection = document.getElementById('pulaar-audio-section'); const nextBtn = document.getElementById('next-btn'); recordBtn.innerHTML = ''; recordBtn.classList.remove('recording-active', 'bg-green-500', 'hover:bg-green-600'); recordBtn.classList.add('bg-red-500', 'hover:bg-red-600'); recordingIndicator.classList.add('hidden'); pulaarAudioSection.classList.add('hidden'); nextBtn.disabled = true; feather.replace(); } // Recording Functions async function toggleRecording() { if (mediaRecorder && mediaRecorder.state === 'recording') { stopRecording(); } else { await startRecording(); } } async function startRecording() { try { const stream = await navigator.mediaDevices.getUserMedia({ audio: true }); mediaRecorder = new MediaRecorder(stream); audioChunks = []; mediaRecorder.ondataavailable = (event) => { audioChunks.push(event.data); }; mediaRecorder.onstop = () => { const audioBlob = new Blob(audioChunks, { type: 'audio/wav' }); const audioUrl = URL.createObjectURL(audioBlob); // Show the recording const pulaarAudio = document.getElementById('pulaar-audio'); pulaarAudio.src = audioUrl; document.getElementById('pulaar-audio-section').classList.remove('hidden'); // Enable next button document.getElementById('next-btn').disabled = false; // Store recording recordingSession.recordings[recordingSession.currentIndex] = { audioBlob: audioBlob, audioUrl: audioUrl, timestamp: new Date() }; // Stop all tracks stream.getTracks().forEach(track => track.stop()); }; mediaRecorder.start(); recordingStartTime = Date.now(); // Update UI const recordBtn = document.getElementById('record-btn'); recordBtn.innerHTML = ''; recordBtn.classList.remove('bg-red-500', 'hover:bg-red-600'); recordBtn.classList.add('bg-green-500', 'hover:bg-green-600', 'recording-active'); document.getElementById('recording-indicator').classList.remove('hidden'); // Update timer updateRecordingTimer(); feather.replace(); } catch (error) { console.error('Error starting recording:', error); alert('Could not access microphone. Please check permissions.'); } } function stopRecording() { if (mediaRecorder && mediaRecorder.state === 'recording') { mediaRecorder.stop(); } } function updateRecordingTimer() { if (mediaRecorder && mediaRecorder.state === 'recording') { const elapsed = Math.floor((Date.now() - recordingStartTime) / 1000); const minutes = Math.floor(elapsed / 60).toString().padStart(2, '0'); const seconds = (elapsed % 60).toString().padStart(2, '0'); document.getElementById('recording-timer').textContent = `${minutes}:${seconds}`; requestAnimationFrame(updateRecordingTimer); } } function skipSentence() { recordingSession.recordings[recordingSession.currentIndex] = null; nextSentence(); } function nextSentence() { recordingSession.currentIndex++; if (recordingSession.currentIndex >= recordingSession.sentences.length) { // Session complete finishRecordingSession(); } else { loadCurrentSentence(); } } async function finishRecordingSession() { // Calculate earnings const completedRecordings = recordingSession.recordings.filter(r => r !== null).length; const earnings = completedRecordings * 0.10; // Show completion message alert(`Session Complete!\n\nCompleted recordings: ${completedRecordings}\nEstimated earnings: €${earnings.toFixed(2)}\n\nYour recordings are now pending review.`); // Return to dashboard showView('dashboard'); // Refresh dashboard data loadDashboardData(); } // Admin Functions function checkAdminAccess() { // Check if user is admin (mock for MVP) const isAdmin = window.location.hash === '#admin'; if (isAdmin) { // In real app, check auth role } } async function loadAdminData() { try { // Mock data for MVP const mockSubmissions = [ { id: 1, userName: 'User 1', frenchText: "Bonjour, comment allez-vous?", frenchAudioUrl: 'https://www.soundhelix.com/examples/mp3/SoundHelix-Song-1.mp3', pulaarAudioUrl: 'https://www.soundhelix.com/examples/mp3/SoundHelix-Song-2.mp3', status: 'pending', createdAt: '2024-01-15T10:30:00Z' }, { id: 2, userName: 'User 2', frenchText: "Je voudrais un café", frenchAudioUrl: 'https://www.soundhelix.com/examples/mp3/SoundHelix-Song-3.mp3', pulaarAudioUrl: 'https://www.soundhelix.com/examples/mp3/SoundHelix-Song-4.mp3', status: 'pending', createdAt: '2024-01-15T09:45:00Z' } ]; // Update stats document.getElementById('admin-total-submissions').textContent = mockSubmissions.length; document.getElementById('admin-pending').textContent = mockSubmissions.filter(s => s.status === 'pending').length; document.getElementById('admin-approved').textContent = 0; document.getElementById('admin-rejected').textContent = 0; // Render submissions renderSubmissions(mockSubmissions); } catch (error) { console.error('Error loading admin data:', error); } } function renderSubmissions(submissions) { const submissionsList = document.getElementById('submissions-list'); submissionsList.innerHTML = submissions.map(sub => `

${sub.userName}

${new Date(sub.createdAt).toLocaleString()}

${sub.status}

${sub.frenchText}

French Audio:

Pulaar Audio:

`).join(''); feather.replace(); } async function updateSubmissionStatus(submissionId, status) { try { // In real app, update in Supabase console.log(`Updating submission ${submissionId} to ${status}`); // Reload data loadAdminData(); // Show feedback alert(`Submission ${status} successfully!`); } catch (error) { console.error('Error updating submission:', error); alert('Error updating submission status'); } } // Make functions globally available for onclick handlers window.updateSubmissionStatus = updateSubmissionStatus; window.showView = showView;