La Cooptation par les Pairs

L'excellence médicale se bâtit par la reconnaissance mutuelle. Participez à l'élaboration d'un réseau de soin d'élite en recommandant vos confrères les plus talentueux.

Tableau d'Excellence

Découvrez les praticiens les plus plébiscités par la communauté AksantiDoc.

Chargement du tableau...

Coopter un Confrère

Veuillez remplir le formulaire ci-dessous pour soumettre une nomination. Une justification de 100 caractères minimum est requise.

1
2
// API Config (Pure JS) const API_URL = 'http://localhost:3000/api'; // Session Mock (Pure JS - Replace with real Auth if needed) async function getSession() { // Pour la démo : On simule une session avec un ID fixe ou stocké en local let userId = localStorage.getItem('user_id') || 'demo-doctor-uuid'; return { user: { id: userId, email: 'docteur@exemple.com' } }; } // Auth Guard Simplified async function checkSession() { const session = await getSession(); if (!session) { window.location.href = '/login.html'; return; } const profile = document.getElementById('user-profile'); if (profile) { profile.classList.remove('hidden'); profile.classList.add('flex'); } const mobileProfile = document.getElementById('mobile-user-profile'); if (mobileProfile) { mobileProfile.classList.remove('hidden'); mobileProfile.classList.add('flex'); } const heroActions = document.getElementById('hero-user-actions'); if (heroActions) { heroActions.classList.remove('hidden'); heroActions.classList.add('flex'); } } // Fees Management (Pure JS) let currentFeeEditingId = null; let myFeesData = []; let hospitalData = []; window.openFeesModal = async () => { const modal = document.getElementById('fees-modal'); if (modal) { modal.classList.remove('hidden'); modal.classList.add('flex'); } await loadHospitals(); await loadMyFees(); }; window.closeFeesModal = () => { const modal = document.getElementById('fees-modal'); if (modal) modal.classList.add('hidden'); }; async function loadHospitals() { const res = await fetch(`${API_URL}/etablissements`); hospitalData = await res.json(); const select = document.getElementById('fee_hospital_id'); if (select) { select.innerHTML = hospitalData.map(h => ``).join(''); } } async function loadMyFees() { // Route à ajouter au serveur si nécessaire, ou filtrage côté client const res = await fetch(`${API_URL}/tarifs`); // Supposant que cette route existe myFeesData = await res.json(); // ... rendu de la liste des tarifs ... } // Load Leaderboard (Pure JS) async function loadLeaderboard() { try { const response = await fetch(`${API_URL}/leaderboard`); const mergedData = await response.json(); renderLeaderboard(mergedData); } catch (err) { console.error('Leaderboard error:', err); } } function renderLeaderboard(data) { const body = document.getElementById('leaderboard-body'); body.innerHTML = data.length > 0 ? '' : ' Aucune donnée disponible. '; const lang = localStorage.getItem('language') || 'fr'; const labels = { photo: window.translations[lang]?.cooptation?.table_photo || "Photo", name: window.translations[lang]?.cooptation?.table_name || "Nom", specialty: window.translations[lang]?.cooptation?.table_specialty || "Spécialité", graduation: window.translations[lang]?.cooptation?.table_graduation || "Promotion", count: window.translations[lang]?.cooptation?.table_count || "Cooptations" }; data.forEach((item, index) => { const row = document.createElement('tr'); row.className = 'block md:table-row border-b border-gray-100 hover:bg-gray-50 transition-colors mb-4 md:mb-0 bg-white md:bg-transparent rounded-xl md:rounded-none shadow-sm md:shadow-none p-4 md:p-0 border md:border-b'; // DIAGNOSTIC LOG if (index < 3) console.log(`Rendering Row ${index}: ${item.full_name}, Promo: ${item.graduation_year}`); const initials=item.initials || 'MD' ; const currentYear=2026; let levelBadge='' ; if (item.graduation_year) { const exp=currentYear - item.graduation_year; if (exp>= 12) { levelBadge = 'Senior'; } else if (exp >= 5) { levelBadge = 'Medior'; } else { levelBadge = 'Junior'; } } row.innerHTML = ` ${labels.photo}
${item.avatar_url ? `${item.full_name}` : `${initials}`}
${labels.name}
${item.full_name || '-'} ${item.hasHigherCooptation ? `` : '' }
${labels.specialty} ${item.specialty || '-'} ${labels.graduation}
${item.graduation_year ? item.graduation_year : '-'} ${levelBadge}
${labels.count}
${item.nombre_de_cooptations}
S:${item.breakdown?.senior || 0} M:${item.breakdown?.medior || 0} J:${item.breakdown?.junior || 0}
`; body.appendChild(row); }); } // Load Consultants for Select let allConsultants = []; async function loadConsultants() { try { const res = await fetch(`${API_URL}/praticiens`); const data = await res.json(); const session = await getSession(); allConsultants = data.filter(c => c.id !== session?.user?.id); renderConsultants(allConsultants); } catch (err) { console.error('Load consultants error:', err); } } function renderConsultants(list) { const select = document.getElementById('nominee_id'); const placeholder = select.querySelector('option[disabled]'); select.innerHTML = ''; if (placeholder) select.appendChild(placeholder); list.forEach(c => { const opt = document.createElement('option'); opt.value = c.id; const year = c.graduation_year ? ` - ${c.graduation_year}` : ''; opt.textContent = `${c.full_name} (${c.specialty || 'Généraliste'}${year})`; select.appendChild(opt); }); } window.filterConsultants = (val) => { const term = val.toLowerCase(); const filtered = allConsultants.filter(c => c.full_name.toLowerCase().includes(term) || (c.specialty && c.specialty.toLowerCase().includes(term)) ); renderConsultants(filtered); }; // Wizard Navigation window.nextStep = () => { const nomineeId = document.getElementById('nominee_id').value; if (!nomineeId) { alert('Veuillez sélectionner un praticien.'); return; } document.getElementById('form-step-1').classList.add('hidden'); document.getElementById('form-step-2').classList.remove('hidden'); document.getElementById('step-2-indicator').classList.remove('bg-gray-200', 'text-gray-500'); document.getElementById('step-2-indicator').classList.add('bg-primary', 'text-white'); }; window.prevStep = () => { document.getElementById('form-step-2').classList.add('hidden'); document.getElementById('form-step-1').classList.remove('hidden'); document.getElementById('step-2-indicator').classList.add('bg-gray-200', 'text-gray-500'); document.getElementById('step-2-indicator').classList.remove('bg-primary', 'text-white'); }; // Character counter const textarea = document.getElementById('justification'); const charCount = document.getElementById('char-count'); textarea.addEventListener('input', () => { const len = textarea.value.length; charCount.textContent = `${len} / 100 caractères min.`; charCount.className = len >= 100 ? 'text-xs text-green-500' : 'text-xs text-red-400'; }); // Form Submission document.getElementById('cooptation-form').addEventListener('submit', async (e) => { e.preventDefault(); const feedback = document.getElementById('form-feedback'); const submitBtn = document.getElementById('submit-btn'); const lang = localStorage.getItem('language') || 'fr'; const trans = window.translations?.[lang]?.cooptation || window.translations?.['fr']?.cooptation; submitBtn.disabled = true; submitBtn.innerHTML = ''; try { const session = await getSession(); if (!session?.user) { throw new Error('User not logged in'); } const nomineeId = document.getElementById('nominee_id').value; if (nomineeId === session.user.id) { throw new Error("Vous ne pouvez pas vous coopter vous-même."); } const res = await fetch(`${API_URL}/cooptations`, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ referrer_id: session.user.id, nominee_id: nomineeId, justification: document.getElementById('justification').value }) }); if (!res.ok) { const errorData = await res.json(); if (errorData.error && errorData.error.includes('Cooptation circulaire')) { throw new Error(trans.error_circular); } throw new Error(errorData.error || 'Erreur lors de la soumission'); } feedback.textContent = trans.success_msg; feedback.className = 'block p-4 rounded-lg text-center font-semibold bg-green-100 text-green-700 mt-4'; document.getElementById('cooptation-form').reset(); charCount.textContent = '0 / 100 caractères min.'; loadLeaderboard(); } catch (err) { feedback.textContent = err.message; feedback.className = 'block p-4 rounded-lg text-center font-semibold bg-red-100 text-red-700 mt-4'; } finally { submitBtn.disabled = false; submitBtn.innerHTML = trans.btn_submit; feedback.classList.remove('hidden'); } }); // Init window.addEventListener('translationsLoaded', applyTranslations); // Fallback if script already loaded if (window.translations) applyTranslations(); loadLeaderboard(); loadConsultants(); checkSession(); // Profile Completion Submission document.getElementById('profile-completion-form').addEventListener('submit', async (e) => { e.preventDefault(); const btn = document.getElementById('comp-submit-btn'); btn.disabled = true; btn.innerHTML = ' Initialisation...'; try { const { data: { user } } = await supabase.auth.getUser(); const fullName = document.getElementById('comp_full_name').value; const rpps = document.getElementById('comp_rpps').value; const specialty = document.getElementById('comp_specialty').value; const graduation = document.getElementById('comp_graduation').value; const { error } = await supabase .from('consultants') .insert([{ id: user.id, full_name: fullName, rpps_number: rpps, // Profile Completion (Pure JS) document.getElementById('profile-completion-form')?.addEventListener('submit', async (e) => { e.preventDefault(); const btn = e.target.querySelector('button'); btn.disabled = true; const formData = new FormData(e.target); const profileData = Object.fromEntries(formData.entries()); try { const session = await getSession(); const res = await fetch(`${API_URL}/praticiens`, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ ...profileData, id: session.user.id }) }); if (res.ok) { document.getElementById('profile-completion-section').classList.add('hidden'); document.getElementById('cooptation-form').closest('div').classList.remove('hidden'); loadLeaderboard(); } } catch (err) { alert("Erreur : " + err.message); } finally { btn.disabled = false; } }); // Curriculum Modal Logic let currentEditingId = null; let myCurriculumData = []; window.openCurriculumModal = async () => { try { currentEditingId = null; const form = document.getElementById('curriculum-form'); if (form) form.reset(); const lang = localStorage.getItem('language') || 'fr'; const trans = window.translations?.[lang]?.cooptation?.curriculum || {}; const submitBtn = document.getElementById('curr-submit-btn'); if (submitBtn) { submitBtn.innerHTML = trans.btn_save || 'Enregistrer'; } const modal = document.getElementById('curriculum-modal'); if (modal) { modal.classList.remove('hidden'); modal.classList.add('flex'); document.body.style.overflow = 'hidden'; } await loadMyCurriculum(); } catch (err) { console.error("Error opening Curriculum Modal:", err); } }; window.loadMyCurriculum = async () => { const listContainer = document.getElementById('my-curriculum-list'); if (!listContainer) return; const lang = localStorage.getItem('language') || 'fr'; const trans = window.translations[lang]?.cooptation?.curriculum || {}; listContainer.innerHTML = '
Chargement...
'; try { const { data: { user } } = await supabase.auth.getUser(); if (!user) return; const { data, error } = await supabase .from('consultant_curriculum') .select('*') .eq('consultant_id', user.id) .order('end_year', { ascending: false }); if (error) throw error; myCurriculumData = data || []; // Update badge const badge = document.getElementById('curr-count-badge'); if (badge) badge.textContent = myCurriculumData.length; if (myCurriculumData.length === 0) { listContainer.innerHTML = `
Aucune expérience enregistrée.
`; return; } const categoryIcons = { university: 'fa-university', publication: 'fa-book', award: 'fa-award', training: 'fa-briefcase' }; listContainer.innerHTML = myCurriculumData.map(item => `
${item.title}

${item.organization} • ${item.end_year}

`).join(''); } catch (err) { listContainer.innerHTML = `
Erreur: ${err.message}
`; } }; window.editCurriculumEntry = (id) => { const entry = myCurriculumData.find(e => e.id === id); if (!entry) return; currentEditingId = id; const lang = localStorage.getItem('language') || 'fr'; const trans = window.translations[lang]?.cooptation?.curriculum || {}; // Populate form document.querySelector(`input[name="category_type"][value="${entry.category_type}"]`).checked = true; document.getElementById('curr_title').value = entry.title || ''; document.getElementById('curr_organization').value = entry.organization || ''; document.getElementById('curr_city').value = entry.city || ''; document.getElementById('curr_country').value = entry.location_country || ''; document.getElementById('curr_start_year').value = entry.start_year || ''; document.getElementById('curr_end_year').value = entry.end_year || ''; document.getElementById('curr_description').value = entry.description || ''; document.getElementById('curr_url').value = entry.url_reference || ''; // Update button text document.getElementById('curr-submit-btn').innerHTML = trans.btn_update || 'Mettre à jour'; // Scroll form into view if needed (it's inside the modal, so just scroll the container) document.querySelector('.p-6.overflow-y-auto').scrollTo({ top: 0, behavior: 'smooth' }); }; window.deleteCurriculumEntry = async (id) => { const lang = localStorage.getItem('language') || 'fr'; const trans = window.translations[lang]?.cooptation?.curriculum || {}; if (!confirm(trans.confirm_delete || "Voulez-vous supprimer cette entrée ?")) return; try { const { error } = await supabase .from('consultant_curriculum') .delete() .eq('id', id); if (error) throw error; // Show floating success message (we can use the feedback div if it's visible) const feedback = document.getElementById('curriculum-feedback'); feedback.textContent = trans.feedback_delete_success || "Supprimé !"; feedback.className = "p-4 rounded-xl text-sm font-semibold text-center bg-green-100 text-green-700"; feedback.classList.remove('hidden'); setTimeout(() => feedback.classList.add('hidden'), 3000); await loadMyCurriculum(); } catch (err) { alert("Erreur: " + err.message); } }; window.closeCurriculumModal = () => { document.getElementById('curriculum-modal').classList.add('hidden'); document.getElementById('curriculum-modal').classList.remove('flex'); document.body.style.overflow = ''; }; document.getElementById('curriculum-form').addEventListener('submit', async (e) => { e.preventDefault(); const btn = document.getElementById('curr-submit-btn'); const feedback = document.getElementById('curriculum-feedback'); btn.disabled = true; btn.innerHTML = ' Enregistrement...'; feedback.classList.add('hidden'); try { const { data: { user } } = await supabase.auth.getUser(); if (!user) throw new Error("Session expirée. Veuillez vous reconnecter."); const category = document.querySelector('input[name="category_type"]:checked').value; const title = document.getElementById('curr_title').value; const organization = document.getElementById('curr_organization').value; const city = document.getElementById('curr_city').value; const country = document.getElementById('curr_country').value; const startYear = document.getElementById('curr_start_year').value ? parseInt(document.getElementById('curr_start_year').value) : null; const endYear = parseInt(document.getElementById('curr_end_year').value); const description = document.getElementById('curr_description').value; const url = document.getElementById('curr_url').value; const payload = { consultant_id: user.id, category_type: category, title: title, organization: organization, city: city, location_country: country, start_year: startYear, end_year: endYear, description: description, url_reference: url }; let error; if (currentEditingId) { const { error: err } = await supabase .from('consultant_curriculum') .update(payload) .eq('id', currentEditingId); error = err; } else { const { error: err } = await supabase .from('consultant_curriculum') .insert([payload]); error = err; } if (error) throw error; const lang = localStorage.getItem('language') || 'fr'; const trans = window.translations[lang]?.cooptation?.curriculum || {}; feedback.innerText = currentEditingId ? (trans.feedback_update_success || "Mis à jour !") : (trans.feedback_success || "Ajouté !"); feedback.className = 'block p-4 rounded-xl text-sm font-semibold text-center bg-green-50 text-green-700 border border-green-100 mb-4'; feedback.classList.remove('hidden'); document.getElementById('curriculum-form').reset(); currentEditingId = null; await loadMyCurriculum(); setTimeout(() => feedback.classList.add('hidden'), 2000); } catch (err) { feedback.innerText = "Erreur : " + err.message; feedback.className = 'block p-4 rounded-xl text-sm font-semibold text-center bg-red-50 text-red-700 border border-red-100 mb-4'; feedback.classList.remove('hidden'); } finally { const lang = localStorage.getItem('language') || 'fr'; const trans = window.translations[lang]?.cooptation?.curriculum || {}; btn.disabled = false; btn.innerHTML = currentEditingId ? `${trans.btn_update || 'Mettre à jour'}` : `${trans.btn_save || 'Enregistrer'}`; } }); async function loadHospitals() { const select = document.getElementById('fee_hospital_id'); if (!select) return; if (hospitalData.length > 0) { // Already loaded, just ensure placeholder is translated return; } try { const { data, error } = await supabase .from('hospitals') .select('id, name') .eq('is_active', true) .order('name'); if (error) throw error; hospitalData = data || []; const lang = localStorage.getItem('language') || 'fr'; const placeholder = window.translations[lang]?.cooptation?.fees?.hospital_placeholder || "Sélectionnez un établissement..."; select.innerHTML = `` + hospitalData.map(h => ``).join(''); } catch (err) { console.error('Error loading hospitals:', err); } } window.loadMyFees = async () => { const listContainer = document.getElementById('my-fees-list'); if (!listContainer) return; const lang = localStorage.getItem('language') || 'fr'; const trans = window.translations?.[lang]?.cooptation?.fees || {}; listContainer.innerHTML = `
${trans.loading || 'Chargement...'}
`; try { const { data: { user } } = await supabase.auth.getUser(); if (!user) return; // Manual Join Fallback: Fetch fees and hospitals separately to avoid 400 error from missing FK const { data: fees, error: feesError } = await supabase .from('consultant_fees') .select('*') .eq('consultant_id', user.id) .order('created_at', { ascending: false }); if (feesError) throw feesError; // Use globally cached hospitalData or fetch if empty if (hospitalData.length === 0) { await loadHospitals(); } // Merge data in JS myFeesData = (fees || []).map(fee => ({ ...fee, hospitals: hospitalData.find(h => h.id === fee.hospital_id) || { name: 'Établissement' } })); const badge = document.getElementById('fees-count-badge'); if (badge) { badge.textContent = myFeesData.length; badge.classList.toggle('hidden', myFeesData.length === 0); } if (myFeesData.length === 0) { listContainer.innerHTML = `
${trans.no_fees || 'Aucun tarif enregistré.'}
`; return; } listContainer.innerHTML = myFeesData.map(item => `
${item.currency}
${item.hospitals?.name || 'Établissement'}

${item.service_name}

Montant à afficher aux patients : ${(parseFloat(item.price_amount) + parseFloat(item.hospital_fees || 0)).toFixed(2)} ${item.currency}

Acte : ${item.price_amount} ${item.currency} ${item.hospital_fees ? `• Frais Hôp. : ${item.hospital_fees} ${item.currency}` : ''}
${item.payto ? `

Payé à : ${item.payto}

` : ''}
`).join(''); } catch (err) { listContainer.innerHTML = `
Erreur: ${err.message}
`; } }; window.editFee = (id) => { const fee = myFeesData.find(f => f.id === id); if (!fee) return; currentFeeEditingId = id; const lang = localStorage.getItem('language') || 'fr'; const trans = window.translations[lang]?.cooptation?.fees || {}; document.getElementById('fee_hospital_id').value = fee.hospital_id; document.getElementById('fee_service_name').value = fee.service_name; document.getElementById('fee_price_amount').value = fee.price_amount; document.getElementById('fee_currency').value = fee.currency; document.getElementById('fee_hospital_fees').value = fee.hospital_fees || ''; document.getElementById('fee_payto').value = fee.payto || ''; document.getElementById('fee_confirmation').checked = fee.confirmation || false; document.getElementById('fee-submit-btn').innerHTML = trans.btn_update || 'Mettre à jour'; if (typeof updateFeeTotal === 'function') updateFeeTotal(); document.getElementById('fee-form').scrollIntoView({ behavior: 'smooth' }); }; window.deleteFee = async (id) => { const lang = localStorage.getItem('language') || 'fr'; const trans = window.translations[lang]?.cooptation?.fees || {}; if (!confirm(trans.confirm_delete || "Voulez-vous supprimer ce tarif ?")) return; try { const { error } = await supabase.from('consultant_fees').delete().eq('id', id); if (error) throw error; const feedback = document.getElementById('fee-feedback'); feedback.textContent = trans.feedback_delete_success || "Supprimé !"; feedback.className = "p-4 rounded-xl text-sm font-semibold text-center bg-green-100 text-green-700 mb-4"; feedback.classList.remove('hidden'); setTimeout(() => feedback.classList.add('hidden'), 3000); await loadMyFees(); } catch (err) { alert("Erreur: " + err.message); } }; document.getElementById('fee-form').addEventListener('submit', async (e) => { e.preventDefault(); const btn = document.getElementById('fee-submit-btn'); const feedback = document.getElementById('fee-feedback'); const lang = localStorage.getItem('language') || 'fr'; const trans = window.translations[lang]?.cooptation?.fees || {}; btn.disabled = true; btn.innerHTML = ' ...'; try { const { data: { user } } = await supabase.auth.getUser(); if (!user) throw new Error("Session expirée"); const payload = { consultant_id: user.id, hospital_id: document.getElementById('fee_hospital_id').value, service_name: document.getElementById('fee_service_name').value, price_amount: parseFloat(document.getElementById('fee_price_amount').value), currency: document.getElementById('fee_currency').value, hospital_fees: parseFloat(document.getElementById('fee_hospital_fees').value) || null, payto: document.getElementById('fee_payto').value || null, confirmation: document.getElementById('fee_confirmation').checked }; let error; if (currentFeeEditingId) { const { error: err } = await supabase.from('consultant_fees').update(payload).eq('id', currentFeeEditingId); error = err; } else { const { error: err } = await supabase.from('consultant_fees').insert([payload]); error = err; } if (error) throw error; feedback.innerText = currentFeeEditingId ? (trans.feedback_update_success || "Mis à jour !") : (trans.feedback_success || "Ajouté !"); feedback.className = 'block p-4 rounded-xl text-sm font-semibold text-center bg-green-50 text-green-700 border border-green-100 mb-4'; feedback.classList.remove('hidden'); document.getElementById('fee-form').reset(); if (typeof updateFeeTotal === 'function') updateFeeTotal(); currentFeeEditingId = null; await loadMyFees(); setTimeout(() => feedback.classList.add('hidden'), 2000); } catch (err) { feedback.innerText = "Erreur : " + err.message; feedback.className = 'block p-4 rounded-xl text-sm font-semibold text-center bg-red-50 text-red-700 border border-red-100 mb-4'; feedback.classList.remove('hidden'); } finally { btn.disabled = false; btn.innerHTML = currentFeeEditingId ? trans.btn_update : trans.btn_save; } }); // CV Viewer Logic window.viewCV = async (consultantId) => { if (!consultantId || consultantId === 'undefined' || consultantId === 'null' || consultantId === '') { console.warn("Invalid consultantId passed to viewCV:", consultantId); return; } const modal = document.getElementById('cv-viewer-modal'); const content = document.getElementById('cv-viewer-content'); const lang = localStorage.getItem('language') || 'fr'; modal.classList.remove('hidden'); modal.classList.add('flex'); document.body.style.overflow = 'hidden'; content.innerHTML = '

Chargement du curriculum...

'; try { // Fetch consultant details first for the title const { data: consultant, error: cErr } = await supabase .from('consultants') .select('full_name, specialty') .eq('id', consultantId) .single(); if (consultant) { document.getElementById('cv-consultant-name').textContent = consultant.full_name; document.getElementById('cv-consultant-specialty').textContent = consultant.specialty || ''; } const { data: curriculum, error: err } = await supabase .from('consultant_curriculum') .select('*') .eq('consultant_id', consultantId) .order('end_year', { ascending: false }); if (err) throw err; if (!curriculum || curriculum.length === 0) { content.innerHTML = `

${window.translations[lang]?.cooptation?.no_cv_data || 'Aucune information disponible.'}

`; return; } // Group by category const groups = { university: [], publication: [], award: [], training: [] }; curriculum.forEach(item => { if (groups[item.category_type]) { groups[item.category_type].push(item); } }); let html = '
'; const categoryIcons = { university: 'fa-university', publication: 'fa-book', award: 'fa-award', training: 'fa-briefcase' }; const categoryLabels = { university: window.translations[lang]?.cooptation?.curriculum?.university || 'Formation', publication: window.translations[lang]?.cooptation?.curriculum?.publication || 'Publications', award: window.translations[lang]?.cooptation?.curriculum?.award || 'Prix & Distinctions', training: window.translations[lang]?.cooptation?.curriculum?.training || 'Stages' }; Object.keys(groups).forEach(cat => { if (groups[cat].length > 0) { html += `

${categoryLabels[cat]}

${groups[cat].map(item => `
${item.title}

${item.organization}

${item.city || item.location_country ? `

${[item.city, item.location_country].filter(Boolean).join(', ')}

` : ''}
${item.start_year ? item.start_year + ' - ' : ''}${item.end_year}
${item.description ? `

${item.description}

` : ''} ${item.url_reference ? ` ${window.translations[lang]?.cooptation?.curriculum?.url || 'Référence'} ` : ''}
`).join('')}
`; } }); html += '
'; content.innerHTML = html; } catch (err) { content.innerHTML = `
Erreur de chargement: ${err.message}
`; } }; window.closeCVViewer = () => { document.getElementById('cv-viewer-modal').classList.add('hidden'); document.getElementById('cv-viewer-modal').classList.remove('flex'); document.body.style.overflow = ''; }; // Scroll Logic window.addEventListener('scroll', () => { const topBtn = document.getElementById('scroll-top-btn'); const bottomBtn = document.getElementById('scroll-bottom-btn'); if (!topBtn || !bottomBtn) return; const scrollPos = window.scrollY; const totalHeight = document.documentElement.scrollHeight - window.innerHeight; if (scrollPos > 300) { topBtn.style.opacity = '1'; topBtn.style.visibility = 'visible'; } else { topBtn.style.opacity = '0'; topBtn.style.visibility = 'hidden'; } if (scrollPos < totalHeight - 300) { bottomBtn.style.opacity='1' ; bottomBtn.style.visibility='visible' ; } else { bottomBtn.style.opacity='0' ; bottomBtn.style.visibility='hidden' ; } }); // --- Second Opinion Fees Management --- let currentSoFeeEditingId=null; let mySoFeesData=[]; window.openSecondOpinionFeesModal=async ()=> { try { currentSoFeeEditingId = null; const form = document.getElementById('so-fee-form'); if (form) form.reset(); const { data: { session } } = await supabase.auth.getSession(); if (!session) return; // Get current doctor info const { data: consultant } = await supabase .from('consultants') .select('full_name, specialty') .eq('id', session.user.id) .single(); if (consultant) { document.getElementById('so_fee_specialty').value = consultant.specialty || ''; } const lang = localStorage.getItem('language') || 'fr'; const trans = window.translations?.[lang]?.cooptation?.second_opinion_fees || {}; const submitBtn = document.getElementById('so-fee-submit-btn'); if (submitBtn) { submitBtn.innerHTML = trans.btn_save || 'Enregistrer'; } const modal = document.getElementById('second-opinion-fees-modal'); if (modal) { modal.classList.remove('hidden'); modal.classList.add('flex'); document.body.style.overflow = 'hidden'; } await loadMySoFees(consultant?.full_name); } catch (err) { console.error("Error opening Second Opinion Fees Modal:", err); } }; window.closeSecondOpinionFeesModal = () => { const modal = document.getElementById('second-opinion-fees-modal'); if (modal) { modal.classList.add('hidden'); modal.classList.remove('flex'); document.body.style.overflow = ''; } }; window.loadMySoFees = async () => { const listContainer = document.getElementById('my-so-fees-list'); if (!listContainer) return; const lang = localStorage.getItem('language') || 'fr'; const trans = window.translations?.[lang]?.cooptation?.second_opinion_fees || {}; listContainer.innerHTML = `
Chargement...
`; try { const { data: { session } } = await supabase.auth.getSession(); if (!session) return; const { data, error } = await supabase .from('prix_second_avis') .select('*') .eq('consultant_id', session.user.id) .order('created_at', { ascending: false }); if (error) throw error; mySoFeesData = data || []; const badge = document.getElementById('so-fees-count-badge'); if (badge) { badge.textContent = mySoFeesData.length; badge.classList.toggle('hidden', mySoFeesData.length === 0); } if (mySoFeesData.length === 0) { listContainer.innerHTML = `
${trans.no_fees || 'Aucun tarif enregistré.'}
`; return; } listContainer.innerHTML = mySoFeesData.map(item => `
${item.devise}
${item.specialite}

${item.prix} ${item.devise}

`).join(''); } catch (err) { listContainer.innerHTML = `
Erreur: ${err.message}
`; } }; window.editSoFee = (id) => { const fee = mySoFeesData.find(f => f.id === id); if (!fee) return; currentSoFeeEditingId = id; const lang = localStorage.getItem('language') || 'fr'; const trans = window.translations[lang]?.cooptation?.second_opinion_fees || {}; document.getElementById('so_fee_specialty').value = fee.specialite; document.getElementById('so_fee_price').value = fee.prix; document.getElementById('so_fee_currency').value = fee.devise; document.getElementById('so-fee-submit-btn').innerHTML = trans.btn_update || 'Mettre à jour'; document.getElementById('so-fee-form').scrollIntoView({ behavior: 'smooth' }); }; window.deleteSoFee = async (id) => { const lang = localStorage.getItem('language') || 'fr'; const trans = window.translations[lang]?.cooptation?.second_opinion_fees || {}; if (!confirm(trans.confirm_delete || "Supprimer ?")) return; try { const { error } = await supabase.from('prix_second_avis').delete().eq('id', id); if (error) throw error; const feedback = document.getElementById('so-fee-feedback'); feedback.textContent = trans.feedback_delete_success || "Supprimé !"; feedback.className = "p-4 rounded-xl text-sm font-semibold text-center bg-green-100 text-green-700 mb-4"; feedback.classList.remove('hidden'); setTimeout(() => feedback.classList.add('hidden'), 3000); await loadMySoFees(); } catch (err) { alert("Erreur: " + err.message); } }; document.getElementById('so-fee-form').addEventListener('submit', async (e) => { e.preventDefault(); const btn = document.getElementById('so-fee-submit-btn'); const feedback = document.getElementById('so-fee-feedback'); const lang = localStorage.getItem('language') || 'fr'; const trans = window.translations[lang]?.cooptation?.second_opinion_fees || {}; btn.disabled = true; btn.innerHTML = ' ...'; try { const { data: { session } } = await supabase.auth.getSession(); if (!session) throw new Error("Session expirée."); const payload = { consultant_id: session.user.id, specialite: document.getElementById('so_fee_specialty').value, prix: parseFloat(document.getElementById('so_fee_price').value), devise: document.getElementById('so_fee_currency').value }; let error; if (currentSoFeeEditingId) { const { error: err } = await supabase.from('prix_second_avis').update(payload).eq('id', currentSoFeeEditingId); error = err; } else { const { error: err } = await supabase.from('prix_second_avis').insert([payload]); error = err; } if (error) throw error; feedback.innerText = currentSoFeeEditingId ? (trans.feedback_update_success || "Mis à jour !") : (trans.feedback_success || "Ajouté !"); feedback.className = 'block p-4 rounded-xl text-sm font-semibold text-center bg-green-50 text-green-700 border border-green-100 mb-4'; feedback.classList.remove('hidden'); if (!currentSoFeeEditingId) { document.getElementById('so_fee_price').value = ''; } currentSoFeeEditingId = null; await loadMySoFees(); setTimeout(() => feedback.classList.add('hidden'), 2000); } catch (err) { feedback.innerText = "Erreur : " + err.message; feedback.className = 'block p-4 rounded-xl text-sm font-semibold text-center bg-red-50 text-red-700 border border-red-100 mb-4'; feedback.classList.remove('hidden'); } finally { btn.disabled = false; btn.innerHTML = currentSoFeeEditingId ? trans.btn_update : trans.btn_save; } }); // --- End Second Opinion Fees ---