| |
| const supabaseUrl = 'https://your-project.supabase.co'; |
| const supabaseKey = 'your-anon-key'; |
| const supabase = window.supabase.createClient(supabaseUrl, supabaseKey); |
|
|
| |
| let currentView = 'dashboard'; |
| let recordingSession = { |
| sentences: [], |
| currentIndex: 0, |
| recordings: [], |
| startTime: null |
| }; |
|
|
| let mediaRecorder = null; |
| let audioChunks = []; |
| let recordingStartTime = null; |
|
|
| |
| const views = { |
| dashboard: document.getElementById('dashboard-view'), |
| recording: document.getElementById('recording-view'), |
| admin: document.getElementById('admin-view') |
| }; |
|
|
| |
| document.addEventListener('DOMContentLoaded', () => { |
| initializeEventListeners(); |
| loadDashboardData(); |
| checkAdminAccess(); |
| }); |
|
|
| |
| function initializeEventListeners() { |
| |
| document.getElementById('start-recording-btn').addEventListener('click', startRecordingSession); |
| document.getElementById('recent-recordings-btn').addEventListener('click', toggleRecentWork); |
| |
| |
| document.getElementById('record-btn').addEventListener('click', toggleRecording); |
| document.getElementById('skip-btn').addEventListener('click', skipSentence); |
| document.getElementById('next-btn').addEventListener('click', nextSentence); |
| |
| |
| } |
|
|
| |
| 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(); |
| } |
| } |
| } |
|
|
| |
| async function loadDashboardData() { |
| try { |
| |
| 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() { |
| |
| 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 => ` |
| <div class="flex items-center justify-between p-3 bg-gray-50 rounded-lg border border-gray-200"> |
| <div class="flex-1"> |
| <p class="text-sm text-gray-800">${rec.frenchText}</p> |
| <p class="text-xs text-gray-500">${rec.date}</p> |
| </div> |
| <span class="px-2 py-1 text-xs font-medium rounded-full ${ |
| rec.status === 'approved' ? 'bg-green-100 text-green-800' : 'bg-yellow-100 text-yellow-800' |
| }"> |
| ${rec.status} |
| </span> |
| </div> |
| `).join(''); |
| } |
|
|
| function toggleRecentWork() { |
| const recentWork = document.getElementById('recent-work'); |
| recentWork.classList.toggle('hidden'); |
| } |
|
|
| |
| async function startRecordingSession() { |
| try { |
| |
| 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; |
| |
| |
| resetRecordingUI(); |
| |
| |
| 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 = '<i data-feather="mic" class="w-12 h-12"></i>'; |
| 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(); |
| } |
|
|
| |
| 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); |
| |
| |
| const pulaarAudio = document.getElementById('pulaar-audio'); |
| pulaarAudio.src = audioUrl; |
| document.getElementById('pulaar-audio-section').classList.remove('hidden'); |
| |
| |
| document.getElementById('next-btn').disabled = false; |
| |
| |
| recordingSession.recordings[recordingSession.currentIndex] = { |
| audioBlob: audioBlob, |
| audioUrl: audioUrl, |
| timestamp: new Date() |
| }; |
| |
| |
| stream.getTracks().forEach(track => track.stop()); |
| }; |
| |
| mediaRecorder.start(); |
| recordingStartTime = Date.now(); |
| |
| |
| const recordBtn = document.getElementById('record-btn'); |
| recordBtn.innerHTML = '<i data-feather="square" class="w-12 h-12"></i>'; |
| 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'); |
| |
| |
| 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) { |
| |
| finishRecordingSession(); |
| } else { |
| loadCurrentSentence(); |
| } |
| } |
|
|
| async function finishRecordingSession() { |
| |
| const completedRecordings = recordingSession.recordings.filter(r => r !== null).length; |
| const earnings = completedRecordings * 0.10; |
| |
| |
| alert(`Session Complete!\n\nCompleted recordings: ${completedRecordings}\nEstimated earnings: €${earnings.toFixed(2)}\n\nYour recordings are now pending review.`); |
| |
| |
| showView('dashboard'); |
| |
| |
| loadDashboardData(); |
| } |
|
|
| |
| function checkAdminAccess() { |
| |
| const isAdmin = window.location.hash === '#admin'; |
| if (isAdmin) { |
| |
| } |
| } |
|
|
| async function loadAdminData() { |
| try { |
| |
| 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' |
| } |
| ]; |
| |
| |
| 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; |
| |
| |
| 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 => ` |
| <div class="bg-gray-50 rounded-lg p-4 border border-gray-200"> |
| <div class="mb-3"> |
| <div class="flex justify-between items-start mb-2"> |
| <div> |
| <p class="font-medium text-gray-800">${sub.userName}</p> |
| <p class="text-sm text-gray-600">${new Date(sub.createdAt).toLocaleString()}</p> |
| </div> |
| <span class="px-2 py-1 text-xs font-medium rounded-full bg-yellow-100 text-yellow-800"> |
| ${sub.status} |
| </span> |
| </div> |
| <p class="text-gray-700">${sub.frenchText}</p> |
| </div> |
| |
| <div class="grid md:grid-cols-2 gap-4 mb-4"> |
| <div> |
| <p class="text-sm font-medium text-gray-600 mb-1">French Audio:</p> |
| <audio controls class="w-full"> |
| <source src="${sub.frenchAudioUrl}" type="audio/mpeg"> |
| </audio> |
| </div> |
| <div> |
| <p class="text-sm font-medium text-gray-600 mb-1">Pulaar Audio:</p> |
| <audio controls class="w-full"> |
| <source src="${sub.pulaarAudioUrl}" type="audio/mpeg"> |
| </audio> |
| </div> |
| </div> |
| |
| <div class="flex gap-3"> |
| <button onclick="updateSubmissionStatus(${sub.id}, 'approved')" class="flex-1 bg-green-600 hover:bg-green-700 text-white font-medium py-2 px-4 rounded-lg transition duration-200"> |
| <i data-feather="check" class="w-4 h-4 inline mr-1"></i> |
| Approve |
| </button> |
| <button onclick="updateSubmissionStatus(${sub.id}, 'rejected')" class="flex-1 bg-red-600 hover:bg-red-700 text-white font-medium py-2 px-4 rounded-lg transition duration-200"> |
| <i data-feather="x" class="w-4 h-4 inline mr-1"></i> |
| Reject |
| </button> |
| </div> |
| </div> |
| `).join(''); |
| |
| feather.replace(); |
| } |
|
|
| async function updateSubmissionStatus(submissionId, status) { |
| try { |
| |
| console.log(`Updating submission ${submissionId} to ${status}`); |
| |
| |
| loadAdminData(); |
| |
| |
| alert(`Submission ${status} successfully!`); |
| } catch (error) { |
| console.error('Error updating submission:', error); |
| alert('Error updating submission status'); |
| } |
| } |
|
|
| |
| window.updateSubmissionStatus = updateSubmissionStatus; |
| window.showView = showView; |