/** * GiftFlow - Main Application Logic */ document.addEventListener('DOMContentLoaded', () => { // --- STATE --- let DB = {}; let ADMIN_DB = { users: [], families: [], links: [] }; // Initialisé avec des tableaux vides const state = { authHeader: null, currentUser: null, userFamilies: [], selectedFamilyId: null, currentView: 'login', // login, familySelector, app, admin appSubView: 'dashboard', viewingMemberId: null, selectedArchiveYear: null, selectedArchiveMemberId: null, adminTab: 'users' }; const translations = {}; let currentLang = 'fr'; // --- DOM --- const loginContainer = document.getElementById('login-container'); const familyContainer = document.getElementById('family-selector-container'); const appContainer = document.getElementById('app-container'); const mainContent = document.getElementById('main-content'); const familyList = document.getElementById('family-list'); const currentUserSpan = document.getElementById('current-user-name'); const navDashboard = document.getElementById('nav-dashboard'); const navArchives = document.getElementById('nav-archives'); const navLogo = document.getElementById('nav-logo'); const navSwitch = document.getElementById('nav-switch'); const navAdmin = document.getElementById('nav-admin'); const langSwitcher = document.getElementById('lang-switcher'); const addFab = document.getElementById('add-fab'); const modal = document.getElementById('add-modal'); const closeModalBtn = document.querySelector('.close-button'); const addForm = document.getElementById('add-form'); const modalTypeSelector = document.getElementById('modal-type-selector'); // --- UTILS --- const findById = (coll, id) => coll.find(i => i.id === id); const formatCurrency = (amt) => new Intl.NumberFormat(currentLang, { style: 'currency', currency: 'EUR' }).format(amt); const escapeHtml = (value) => String(value ?? '').replace(/[&<>"']/g, (char) => ({ '&': '&', '<': '<', '>': '>', '"': '"', "'": ''' }[char])); const t = (key, params = {}) => { let text = key.split('.').reduce((o, i) => o?.[i], translations[currentLang]); if (!text) return `[${key}]`; // Affiche la clé pour déboguer si manquant for (const [k, v] of Object.entries(params)) text = text.replace(`{${k}}`, v); return text; }; const getUserRole = (user) => user?.role || (user?.isAdmin ? 'admin' : 'member'); const canManageAdmin = (user) => getUserRole(user) === 'admin'; const canViewArchives = (user) => ['admin', 'privileged'].includes(getUserRole(user)); // Fonction pour appliquer les traductions sur le DOM actuel function applyTranslations() { document.querySelectorAll('[data-translate]').forEach(el => { el.innerHTML = t(el.dataset.translate); }); // Traduction des éléments spécifiques non gérés par data-translate if(state.currentView === 'familySelector') { document.querySelector('#family-selector-container h2').textContent = t('familySelector.title'); } } // --- API & AUTH --- async function apiFetch(url, options = {}) { const headers = { ...options.headers, 'Authorization': state.authHeader, 'Content-Type': 'application/json' }; const res = await fetch(url, { ...options, headers }); if (res.status === 401) { window.handleLogout(); throw new Error("Auth error"); } if (!res.ok) throw new Error("API Error"); if (res.status === 204) return; return res.json(); } window.handleLogout = () => { state.authHeader = null; state.currentUser = null; state.userFamilies = []; state.selectedFamilyId = null; localStorage.removeItem('giftflowUser'); localStorage.removeItem('giftflowAuth'); document.getElementById('login-form').reset(); document.getElementById('login-error').textContent = ''; switchView('login'); }; async function handleLogin(e) { e.preventDefault(); const u = e.target.username.value; const p = e.target.password.value; state.authHeader = `Basic ${btoa(u + ':' + p)}`; try { const res = await apiFetch('/api/login', { method: 'POST' }); state.currentUser = res.user; state.userFamilies = res.families; localStorage.setItem('giftflowAuth', state.authHeader); if (state.userFamilies.length === 1) { selectFamily(state.userFamilies[0].id); } else if (state.userFamilies.length > 1) { switchView('familySelector'); } else if (canManageAdmin(state.currentUser)) { switchView('admin'); } else { alert("No family assigned."); window.handleLogout(); } } catch (e) { document.getElementById('login-error').textContent = t('login.error'); state.authHeader = null; } } // --- INIT --- async function init() { // Load Language const savedLang = localStorage.getItem('giftflow_lang') || 'fr'; langSwitcher.innerHTML = ``; langSwitcher.value = savedLang; langSwitcher.onchange = (e) => loadLang(e.target.value); await loadLang(savedLang); // Setup Events document.getElementById('login-form').onsubmit = handleLogin; document.getElementById('logout-btn').onclick = window.handleLogout; const goToDashboard = (e) => { e.preventDefault(); if(state.selectedFamilyId) { state.appSubView = 'dashboard'; switchView('app'); renderApp(); } else if (state.userFamilies.length > 1) { switchView('familySelector'); } }; navDashboard.onclick = goToDashboard; navLogo.onclick = goToDashboard; navArchives.onclick = (e) => { e.preventDefault(); if(!canViewArchives(state.currentUser)) return; state.appSubView = 'archives'; state.selectedArchiveYear = null; switchView('app'); renderApp(); }; navSwitch.onclick = (e) => { e.preventDefault(); switchView('familySelector'); }; navAdmin.onclick = (e) => { e.preventDefault(); if(canManageAdmin(state.currentUser)) switchView('admin'); }; addFab.onclick = () => openAddModal(); closeModalBtn.onclick = closeModal; window.onclick = (e) => { if(e.target === modal) closeModal(); }; // Check Session const savedAuth = localStorage.getItem('giftflowAuth'); if (savedAuth) { state.authHeader = savedAuth; try { const res = await apiFetch('/api/login', { method: 'POST' }); state.currentUser = res.user; state.userFamilies = res.families; if (state.userFamilies.length === 1) { selectFamily(state.userFamilies[0].id); } else if (state.userFamilies.length > 1) { switchView('familySelector'); } else if (canManageAdmin(state.currentUser)) { switchView('admin'); } } catch (e) { window.handleLogout(); } } else { switchView('login'); } } async function loadLang(lang) { if(!translations[lang]) { try { const res = await fetch(`/locales/${lang}.json`); translations[lang] = await res.json(); } catch(e) { console.error("Lang error"); return; } } currentLang = lang; localStorage.setItem('giftflow_lang', lang); document.documentElement.lang = lang; applyTranslations(); // Applique les traductions statiques // Refresh current view content if(state.currentUser) { currentUserSpan.textContent = `${t('header.loggedInAs')} ${state.currentUser.username}`; if(state.currentView === 'app') renderApp(); if(state.currentView === 'admin') renderAdmin(); // Re-render admin to apply lang if(state.currentView === 'familySelector') renderFamilySelector(); } } // --- VIEW MANAGER --- function switchView(view) { if (view === 'admin' && !canManageAdmin(state.currentUser)) view = 'app'; state.currentView = view; loginContainer.classList.remove('active'); familyContainer.style.display = 'none'; appContainer.classList.remove('active'); // Header Links Visibility if (state.currentUser) { navAdmin.classList.toggle('hidden', !canManageAdmin(state.currentUser)); navArchives.classList.toggle('hidden', !canViewArchives(state.currentUser)); navSwitch.classList.toggle('hidden', state.userFamilies.length <= 1); } if (view === 'login') loginContainer.classList.add('active'); if (view === 'familySelector') { familyContainer.style.display = 'block'; renderFamilySelector(); } if (view === 'app') { appContainer.classList.add('active'); renderApp(); } if (view === 'admin') { appContainer.classList.add('active'); loadAndRenderAdmin(); } applyTranslations(); // Traduit les éléments statiques nouvellement affichés } window.selectFamily = async (id) => { state.selectedFamilyId = id; try { DB = await apiFetch(`/api/family/${id}/data`); state.appSubView = 'dashboard'; switchView('app'); } catch(e) { alert("Error loading family"); } }; function renderFamilySelector() { document.querySelector('#family-selector-container h2').textContent = t('familySelector.title'); familyList.innerHTML = state.userFamilies.map(f => `

${f.name}

${t('familySelector.clickToEnter')}

`).join(''); } // --- APP RENDERERS --- function renderApp() { currentUserSpan.textContent = `${t('header.loggedInAs')} ${state.currentUser.username}`; document.querySelectorAll('nav a').forEach(a => a.classList.remove('active')); addFab.style.display = 'none'; if(state.currentView === 'admin') { navAdmin.classList.add('active'); return; } if(state.appSubView === 'dashboard') { navDashboard.classList.add('active'); const others = DB.members.filter(m => m.id !== state.currentUser.id); mainContent.innerHTML = `

${t('dashboard.title')}

${others.map(m => { const iC = DB.giftIdeas.filter(i => i.targetMemberId === m.id).length; const pC = DB.purchasedGifts.filter(g => g.targetMemberId === m.id && new Date(g.purchaseDate).getFullYear() === new Date().getFullYear()).length; return `

${m.username}

${iC} ${iC===1?t('dashboard.ideas_one'):t('dashboard.ideas_other')} | ${pC} ${pC===1?t('dashboard.purchases_one'):t('dashboard.purchases_other')}

`; }).join('')}
`; } if(state.appSubView === 'memberProfile') { navDashboard.classList.add('active'); addFab.style.display = 'flex'; const m = findById(DB.members, state.viewingMemberId); if(!m) { state.appSubView = 'dashboard'; renderApp(); return; } const ideas = DB.giftIdeas.filter(i => i.targetMemberId === m.id); const gifts = DB.purchasedGifts.filter(g => g.targetMemberId === m.id && new Date(g.purchaseDate).getFullYear() === new Date().getFullYear()); mainContent.innerHTML = `

${t('memberProfile.title', {name: m.username})}

${t('memberProfile.ideasTitle', {count: ideas.length})}

${ideas.length > 0 ? ideas.map(renderIdeaCard).join('') : `

${t('memberProfile.noIdeas')}

`}

${t('memberProfile.purchasesTitle', {count: gifts.length})}

${gifts.length > 0 ? gifts.map(renderGiftCard).join('') : `

${t('memberProfile.noPurchases')}

`}
`; } if(state.appSubView === 'archives') { if(!canViewArchives(state.currentUser)) { state.appSubView = 'dashboard'; renderApp(); return; } navArchives.classList.add('active'); const curYear = new Date().getFullYear(); const past = DB.purchasedGifts.filter(g => new Date(g.purchaseDate).getFullYear() < curYear); const years = [...new Set(past.map(g => new Date(g.purchaseDate).getFullYear()))].sort((a,b)=>b-a); let html = `

${t('archives.title')}

${years.map(y => ``).join('')}
`; if(state.selectedArchiveYear) { const yearGifts = past.filter(g => new Date(g.purchaseDate).getFullYear() === state.selectedArchiveYear); if(!state.selectedArchiveMemberId) { const mids = [...new Set(yearGifts.map(g => g.targetMemberId))]; html += `

${t('archives.giftsForWhom', {year: state.selectedArchiveYear})}

`; if(mids.length > 0) { html += `
${mids.map(id => { const m = findById(DB.members, id); if(!m) return ''; const gc = yearGifts.filter(g=>g.targetMemberId===id).length; return `

${m.username}

${gc} ${gc===1?t('archives.giftsReceived_one'):t('archives.giftsReceived_other')}

`; }).join('')}
`; } else { html += `

${t('archives.noGiftsForYear')}

`; } } else { const m = findById(DB.members, state.selectedArchiveMemberId); const mg = yearGifts.filter(g => g.targetMemberId === m.id); html += `

${t('archives.giftsForMemberInYear', {name: m.username, year: state.selectedArchiveYear})}

${mg.map(renderGiftCard).join('')}
`; } } else { html += `

${t('archives.selectYear')}

`; } mainContent.innerHTML = html; } } // Windows global for inline HTML events window.viewMember = (id) => { state.viewingMemberId = id; state.appSubView = 'memberProfile'; renderApp(); }; window.selectArchiveYear = (y) => { state.selectedArchiveYear = y; state.selectedArchiveMemberId = null; renderApp(); }; window.selectArchiveMember = (id) => { state.selectedArchiveMemberId = id; renderApp(); }; // --- CARDS --- function renderIdeaCard(i) { const creator = findById(DB.members, i.creatorId)?.username || '?'; const comment = i.comment?.trim(); const link = i.link?.trim(); return `

${escapeHtml(i.title)}

${i.estimatedPrice ? `
${formatCurrency(i.estimatedPrice)} ${t('giftCard.priceEstimated')}
` : `
${t('giftCard.priceUndefined')}
`}
${t('giftCard.createdBy', {name: escapeHtml(creator)})}
${comment ? `
${escapeHtml(comment)}
` : ''} ${link ? `

${t('giftCard.link')}

` : ''}
`; } function renderGiftCard(g) { const payer = findById(DB.members, g.payerId)?.username || '?'; const statuses = DB.reimbursementStatus.filter(s => s.giftId === g.id); const share = g.totalPrice / g.reimbursementMemberIds.length; const comment = g.comment?.trim(); const link = g.link?.trim(); return `

${g.name}

${formatCurrency(g.totalPrice)}

${t('purchasedCard.purchasedAt', {store: escapeHtml(g.store)})}

${t('purchasedCard.paidBy', {name: payer})}

${comment ? `
${escapeHtml(comment)}
` : ''} ${link ? `

${t('purchasedCard.link')}

` : ''} ${statuses.map(s => { const m = findById(DB.members, s.memberId); if(!m) return ''; const canEdit = state.currentUser.id !== g.targetMemberId; let sC='', sT='', rT=''; if(s.status==='Non Remboursé'){sC='status-unpaid';sT=`${t('reimbursement.statusUnpaid')}`;} if(s.status==='Tout Remboursé'){sC='status-paid';sT=``;} if(s.status==='Partiellement Remboursé'){sC='status-partial';sT=`${t('reimbursement.statusPartial',{amount:formatCurrency(s.amountPaid)})}`; rT=`
${t('reimbursement.remaining',{amount:formatCurrency(share-s.amountPaid)})}
`;} const acts = canEdit ? `
` : '—'; return ``; }).join('')}
${t('reimbursement.tableHeaderMember')}${t('reimbursement.tableHeaderStatus')}${t('reimbursement.tableHeaderActions')}
${m.username}
${t('reimbursement.amountDue',{amount:formatCurrency(share)})}
${sT}${rT}${acts}
`; } // Delegation for Cards mainContent.addEventListener('click', async (e) => { const card = e.target.closest('.gift-card'); // Status/Partial Actions (inside card but specific DOM) if (e.target.closest('.btn-status-change')) { const btn = e.target.closest('.btn-status-change'); const row = btn.closest('tr'); const sId = row.dataset.statusId; const due = parseFloat(row.dataset.amountDue); const status = btn.dataset.newStatus; await apiFetch(`/api/status/${sId}`, { method: 'PUT', body: JSON.stringify({ status, amountPaid: status === 'Tout Remboursé' ? due : 0 }) }); refresh(); return; } if (e.target.closest('.btn-partial-payment')) { const btn = e.target.closest('.btn-partial-payment'); const row = btn.closest('tr'); const input = row.querySelector('.input-partial-payment'); const val = parseFloat(input.value); const due = parseFloat(row.dataset.amountDue); if (!isNaN(val)) { await apiFetch(`/api/status/${row.dataset.statusId}`, { method: 'PUT', body: JSON.stringify({ amountPaid: val, status: val >= due ? 'Tout Remboursé' : 'Partiellement Remboursé' }) }); refresh(); } return; } if(!card) return; const iId = parseInt(card.dataset.ideaId); const gId = parseInt(card.dataset.giftId); if(e.target.closest('.edit-idea-btn')) openEditIdeaModal(findById(DB.giftIdeas, iId)); if(e.target.closest('.convert-btn')) openAddModalForConversion(findById(DB.giftIdeas, iId)); if(e.target.closest('.edit-gift-btn')) openEditGiftModal(findById(DB.purchasedGifts, gId)); if(e.target.closest('.revert-to-idea-btn')) { if(confirm(t('alerts.confirmRevertIdea'))) { await apiFetch(`/api/gifts/${gId}/revert-to-idea`, { method: 'POST' }); refresh(); } } if(e.target.closest('.delete-btn')) { if(iId && confirm(t('alerts.confirmDeleteIdea'))) { await apiFetch(`/api/ideas/${iId}`, { method: 'DELETE' }); refresh(); } else if(gId && confirm(t('alerts.confirmDeleteGift'))) { await apiFetch(`/api/gifts/${gId}`, { method: 'DELETE' }); refresh(); } } }); async function refresh() { if(state.selectedFamilyId) DB = await apiFetch(`/api/family/${state.selectedFamilyId}/data`); renderApp(); } // --- MODAL --- function openModal(title, html) { modal.querySelector('#modal-title').textContent = title; modalTypeSelector.style.display = 'none'; addForm.innerHTML = html; addForm.classList.add('visible'); modal.classList.add('active'); } function closeModal() { modal.classList.remove('active'); addForm.innerHTML=''; } window.openAddModal = () => { modal.querySelector('#modal-title').textContent = t('modal.addTitle'); modalTypeSelector.style.display = 'block'; addForm.innerHTML = ''; addForm.classList.remove('visible'); delete addForm.dataset.adminAction; delete addForm.dataset.convertId; delete addForm.dataset.editIdeaId; delete addForm.dataset.editGiftId; modalTypeSelector.querySelectorAll('.btn').forEach(b => { b.classList.remove('active'); b.onclick = () => { renderForm(b.dataset.formType); b.classList.add('active'); }; }); modal.classList.add('active'); }; function openAddModalForConversion(idea) { window.openAddModal(); modalTypeSelector.style.display='none'; addForm.dataset.convertId = idea.id; renderForm('purchase'); addForm.querySelector('[name=name]').value = idea.title; if(idea.estimatedPrice) addForm.querySelector('[name=totalPrice]').value = idea.estimatedPrice; if(idea.link) addForm.querySelector('[name=link]').value = idea.link; if(idea.comment) addForm.querySelector('[name=comment]').value = idea.comment; } function openEditIdeaModal(idea) { window.openAddModal(); modalTypeSelector.style.display='none'; modal.querySelector('#modal-title').textContent = t('modal.editIdeaTitle'); addForm.dataset.editIdeaId = idea.id; renderForm('idea', true); addForm.querySelector('[name=title]').value = idea.title; addForm.querySelector('[name=estimatedPrice]').value = idea.estimatedPrice || ''; addForm.querySelector('[name=link]').value = idea.link || ''; addForm.querySelector('[name=comment]').value = idea.comment || ''; addForm.querySelector('.submit-btn').textContent = t('modal.editIdeaBtn'); } function openEditGiftModal(gift) { window.openAddModal(); modalTypeSelector.style.display='none'; modal.querySelector('#modal-title').textContent = t('modal.editTitle'); addForm.dataset.editGiftId = gift.id; renderForm('purchase', true); addForm.querySelector('[name=name]').value = gift.name; addForm.querySelector('[name=totalPrice]').value = gift.totalPrice; addForm.querySelector('[name=store]').value = gift.store; addForm.querySelector('[name=link]').value = gift.link || ''; addForm.querySelector('[name=comment]').value = gift.comment || ''; addForm.querySelector('[name=purchaseDate]').value = new Date(gift.purchaseDate).toISOString().slice(0,10); addForm.querySelector('[name=payerId]').value = gift.payerId; gift.reimbursementMemberIds.forEach(mid => { const c = addForm.querySelector(`input[value="${mid}"]`); if(c) c.checked=true; }); addForm.querySelector('.submit-btn').textContent = t('modal.editGiftBtn'); } function renderForm(type, isEdit=false) { addForm.dataset.type = type; const tm = findById(DB.members, state.viewingMemberId); let h = ''; if (type === 'idea') { h = `

${t('modal.for', {name: tm.username})}

`; } else { const opts = DB.members.map(m => ``).join(''); const chks = DB.members.map(m => ``).join(''); h = `
${chks}

${t('modal.for', {name: tm.username})}

`; } addForm.innerHTML = h; addForm.classList.add('visible'); } addForm.onsubmit = async (e) => { e.preventDefault(); const fd = new FormData(addForm); const adminAction = addForm.dataset.adminAction; if (adminAction) { try { if(adminAction === 'addUser') { const role = fd.get('role') || 'member'; await apiFetch('/api/admin/users', { method: 'POST', body: JSON.stringify({username: fd.get('u'), password: fd.get('p'), role, isAdmin: role === 'admin', familyIds: Array.from(document.querySelectorAll('input[name=fams]:checked')).map(c=>c.value) }) }); } if(adminAction === 'editUser') { const role = fd.get('role') || 'member'; await apiFetch(`/api/admin/users/${addForm.dataset.adminId}`, { method: 'PUT', body: JSON.stringify({username: fd.get('u'), password: fd.get('p'), role, isAdmin: role === 'admin', familyIds: Array.from(document.querySelectorAll('input[name=fams]:checked')).map(c=>c.value) }) }); } if(adminAction === 'addFam') { await apiFetch('/api/admin/families', { method: 'POST', body: JSON.stringify({name: fd.get('n')}) }); } if(adminAction === 'editFam') { await apiFetch(`/api/admin/families/${addForm.dataset.adminId}`, { method: 'PUT', body: JSON.stringify({name: fd.get('n')}) }); } // NOUVEAU: Gestion des membres de famille depuis l'onglet famille if(adminAction === 'editFamilyMembers') { const memberIds = Array.from(document.querySelectorAll('input[name=memberIds]:checked')).map(c=>c.value); await apiFetch(`/api/admin/families/${addForm.dataset.adminId}/members`, { method: 'PUT', body: JSON.stringify({ memberIds }) }); } closeModal(); loadAndRenderAdmin(); } catch(e) { console.error(e); alert("Admin Error"); } return; } // App logic const type = addForm.dataset.type; const payload = { familyId: state.selectedFamilyId }; try { if (type === 'idea') { payload.title = fd.get('title'); payload.estimatedPrice = parseFloat(fd.get('estimatedPrice')) || null; payload.link = fd.get('link')?.trim() || null; payload.comment = fd.get('comment')?.trim() || null; if(addForm.dataset.editIdeaId) { await apiFetch(`/api/ideas/${addForm.dataset.editIdeaId}`, { method: 'PUT', body: JSON.stringify(payload) }); } else { payload.targetMemberId = state.viewingMemberId; payload.creatorId = state.currentUser.id; payload.creationDate = new Date().toISOString(); await apiFetch('/api/ideas', { method: 'POST', body: JSON.stringify(payload) }); } } else { payload.name = fd.get('name'); payload.totalPrice = parseFloat(fd.get('totalPrice')); payload.store = fd.get('store'); payload.link = fd.get('link')?.trim() || null; payload.comment = fd.get('comment')?.trim() || null; payload.purchaseDate = new Date(fd.get('purchaseDate')).toISOString(); payload.payerId = parseInt(fd.get('payerId')); payload.targetMemberId = state.viewingMemberId; payload.reimbursementMemberIds = Array.from(document.querySelectorAll('input[name=mids]:checked')).map(c => parseInt(c.value)); if(addForm.dataset.editGiftId) await apiFetch(`/api/gifts/${addForm.dataset.editGiftId}`, { method: 'PUT', body: JSON.stringify(payload) }); else { let url = '/api/gifts'; if(addForm.dataset.convertId) url+=`?deleteIdeaId=${addForm.dataset.convertId}`; await apiFetch(url, { method: 'POST', body: JSON.stringify(payload) }); } } closeModal(); refresh(); } catch(e) { alert("Save Error"); } }; // --- ADMIN DASHBOARD --- async function loadAndRenderAdmin() { try { ADMIN_DB = await apiFetch('/api/admin/data'); // Check if data loaded correctly if (!ADMIN_DB.users) ADMIN_DB.users = []; if (!ADMIN_DB.families) ADMIN_DB.families = []; if (!ADMIN_DB.links) ADMIN_DB.links = []; renderAdmin(); } catch(e) { mainContent.innerHTML = "Admin Load Error"; } } function renderAdmin() { navDashboard.classList.remove('active'); navArchives.classList.remove('active'); navAdmin.classList.add('active'); addFab.style.display = 'none'; mainContent.innerHTML = `

${t('admin.title')}

`; const p = document.getElementById('admin-panel'); if(state.adminTab === 'users') { p.innerHTML = ` ${ADMIN_DB.users.map(u => { const fams = ADMIN_DB.links.filter(l=>l.userId===u.id).map(l=>ADMIN_DB.families.find(f=>f.id===l.familyId)?.name).filter(Boolean).join(', '); return ``; }).join('')}
${t('admin.username')}${t('admin.role')}${t('admin.families')}${t('admin.actions')}
${u.username}${t(`admin.role_${getUserRole(u)}`)}${fams}
`; } else { p.innerHTML = ` ${ADMIN_DB.families.map(f => { const c = ADMIN_DB.links.filter(l=>l.familyId===f.id).length; return ``; }).join('')}
${t('admin.name')}${t('admin.members')}${t('admin.actions')}
${f.name} ${c}
`; } } window.setAdminTab = (t) => { state.adminTab = t; renderAdmin(); }; window.admAddUser = () => { const fams = ADMIN_DB.families.map(f => ``).join(''); openModal(t('admin.addUser'), `
${fams}
`); addForm.dataset.adminAction = 'addUser'; }; window.admEditUser = (id) => { const u = ADMIN_DB.users.find(u=>u.id===id); const fams = ADMIN_DB.families.map(f => { const has = ADMIN_DB.links.some(l=>l.userId===id && l.familyId===f.id); return ``; }).join(''); openModal(t('admin.edit'), `
${fams}
`); addForm.dataset.adminAction = 'editUser'; addForm.dataset.adminId = id; }; window.admAddFam = () => { openModal(t('admin.addFamily'), `
`); addForm.dataset.adminAction = 'addFam'; }; window.admEditFam = (id) => { const fam = ADMIN_DB.families.find(f => f.id === id); openModal(t('admin.edit'), `
`); addForm.dataset.adminAction = 'editFam'; addForm.dataset.adminId = id; }; window.admDeleteFam = async (id) => { // Message en dur ou via t('alerts...') si vous l'ajoutez aux JSON if (confirm("Attention : Supprimer cette famille supprimera aussi TOUS les cadeaux et idées associés. Continuer ?")) { try { await apiFetch(`/api/admin/families/${id}`, { method: 'DELETE' }); loadAndRenderAdmin(); } catch (e) { alert("Erreur lors de la suppression."); } } }; // Fonction pour gérer les membres d'une famille depuis l'onglet Familles window.admManage = (fid) => { const fam = ADMIN_DB.families.find(f=>f.id===fid); const checks = ADMIN_DB.users.map(u => { const has = ADMIN_DB.links.some(l => l.familyId===fid && l.userId===u.id); return ``; }).join(''); openModal(t('admin.manageMembersTitle', {name: fam.name}), `
${checks}
`); addForm.dataset.adminAction = 'editFamilyMembers'; addForm.dataset.adminId = fid; }; // Start init(); });