test

/* CSS Reset & Basics */ :root { –primary: #2563eb; –dark: #0f172a; } body { font-family: system-ui, -apple-system, sans-serif; background: #f1f5f9; margin: 0; padding: 0; } .hidden { display: none !important; } /* Layout */ .app-container { display: flex; height: 100vh; overflow: hidden; } .sidebar { width: 260px; background: var(–dark); color: white; display: flex; flex-direction: column; } .content { flex: 1; overflow-y: auto; padding: 2rem; } /* Components */ .card { background: white; border-radius: 12px; padding: 1.5rem; box-shadow: 0 1px 3px rgba(0,0,0,0.1); margin-bottom: 1.5rem; } .btn { padding: 0.75rem 1.5rem; border-radius: 8px; font-weight: 600; cursor: pointer; border: none; transition: 0.2s; } .btn-primary { background: var(–primary); color: white; } .btn-primary:hover { background: #1d4ed8; } .btn-danger { background: #ef4444; color: white; } .input { width: 100%; padding: 0.75rem; border: 1px solid #e2e8f0; border-radius: 8px; margin-bottom: 1rem; box-sizing: border-box; } /* Nav */ .nav-item { padding: 1rem; cursor: pointer; opacity: 0.7; display: flex; align-items: center; gap: 10px; } .nav-item:hover, .nav-item.active { opacity: 1; background: rgba(255,255,255,0.1); } /* Calendar Grid */ .cal-grid { display: grid; grid-template-columns: repeat(7, 1fr); gap: 1px; background: #cbd5e1; border: 1px solid #cbd5e1; } .cal-day { background: white; min-height: 80px; padding: 4px; font-size: 12px; } .cal-event { background: #dbeafe; color: #1e40af; padding: 2px 4px; border-radius: 4px; margin-top: 2px; font-size: 10px; } /* Tables */ table { width: 100%; border-collapse: collapse; } th { text-align: left; color: #64748b; font-size: 0.75rem; text-transform: uppercase; padding: 1rem; border-bottom: 1px solid #e2e8f0; } td { padding: 1rem; border-bottom: 1px solid #f1f5f9; } /* Login */ .login-overlay { position: fixed; inset: 0; background: var(–dark); display: flex; align-items: center; justify-content: center; z-index: 999; } .loading { opacity: 0.5; pointer-events: none; } // CONFIG const AJAX_URL = ‘/wp-admin/admin-ajax.php’; // Standard WP endpoint let DB = { members: [], finances: [], bookings: [] }; let USER = null; let calDate = new Date(); let isBookMember = true; // AUTH function login() { const u = document.getElementById(‘l-user’).value; const p = document.getElementById(‘l-pass’).value; if (u === ‘Tafadzwa’ && p === ‘59910000’) { USER = { name: ‘Tafadzwa’, role: ‘admin’ }; init(); } else { if (u && p) { USER = { name: u, role: ‘secretary’ }; init(); } else { document.getElementById(‘l-err’).classList.remove(‘hidden’); } } } function init() { document.getElementById(‘login-screen’).classList.add(‘hidden’); document.getElementById(‘app-ui’).classList.remove(‘hidden’); document.getElementById(‘user-badge’).innerText = USER.role; if (USER.role === ‘admin’) document.getElementById(‘nav-admin’).classList.remove(‘hidden’); fetchData(); } // API CALLS async function api(op, data = {}) { let fd = new FormData(); fd.append(‘action’, ‘dfm_api’); fd.append(‘op’, op); for (let k in data) fd.append(k, data[k]); try { const res = await fetch(AJAX_URL, { method: ‘POST’, body: fd }); return await res.json(); } catch (e) { console.error(“API Error”, e); return { success: false }; } } async function fetchData() { const res = await api(‘fetch_all’); if (res.success) { DB.members = res.data.members || []; DB.finances = res.data.finances || []; DB.bookings = res.data.bookings || []; refreshUI(); } } // UI LOGIC function tab(id) { document.querySelectorAll(‘[id^=”view-“]’).forEach(el => el.classList.add(‘hidden’)); document.querySelectorAll(‘.nav-item’).forEach(el => el.classList.remove(‘active’)); document.getElementById(‘view-‘+id).classList.remove(‘hidden’); document.getElementById(‘nav-‘+id).classList.add(‘active’); if (id === ‘calendar’) renderCalendar(); } function refreshUI() { renderDash(); renderMembers(); renderFinance(); renderBookings(); populateSelects(); } // RENDERERS function renderDash() { document.getElementById(‘d-members’).innerText = DB.members.length; const rev = DB.finances.reduce((a,b) => a + parseFloat(b.amount), 0); document.getElementById(‘d-rev’).innerText = `$${rev.toFixed(2)}`; document.getElementById(‘d-book’).innerText = DB.bookings.length; // Debt Logic let debt = 0; const months = new Date().getMonth() + 1; DB.members.forEach(m => { const paid = DB.finances.filter(f => f.member_id == m.id && f.type != ‘pledge’).reduce((a,b) => a + parseFloat(b.amount), 0); const due = (months * 2); // $1 Sub + $1 Funeral debt += Math.max(0, due – paid); }); document.getElementById(‘d-debt’).innerText = `$${debt.toFixed(2)}`; } function renderMembers() { const q = document.getElementById(‘search-mem’).value.toLowerCase(); const list = document.getElementById(‘tbody-members’); list.innerHTML = ”; DB.members.filter(m => m.fname.toLowerCase().includes(q) || m.lname.toLowerCase().includes(q)).forEach(m => { const months = new Date().getMonth() + 1; const paid = DB.finances.filter(f => f.member_id == m.id).reduce((a,b) => a + parseFloat(b.amount), 0); const target = (months * 2) + parseFloat(m.pledge); const bal = target – paid; list.innerHTML += ` ${m.fname} ${m.lname} ${m.phone} ${m.address || ”} 0 ? ‘text-red-500’ : ‘text-green-500’} font-bold”>$${Math.max(0, bal).toFixed(2)} `; }); } function renderFinance() { const list = document.getElementById(‘tbody-fin’); list.innerHTML = ”; DB.finances.slice().reverse().forEach(f => { const m = DB.members.find(x => x.id == f.member_id); list.innerHTML += ` ${f.date} ${m ? m.fname + ‘ ‘ + m.lname : ‘Unknown’} ${f.type} $${f.amount} `; }); } function renderBookings() { const list = document.getElementById(‘booking-list’); list.innerHTML = ”; DB.bookings.forEach(b => { const name = b.is_guest == 1 ? b.guest_name + ‘ (Guest)’ : (DB.members.find(m => m.id == b.member_id)?.fname || ‘Unknown’); list.innerHTML += `
${name}
${b.booking_date} • ${b.purpose}
$${b.fee}
${b.status}
`; }); if (USER.role === ‘admin’) { const adminList = document.getElementById(‘admin-list’); adminList.innerHTML = list.innerHTML; } } function renderCalendar() { const grid = document.getElementById(‘cal-grid’); grid.innerHTML = ”; document.getElementById(‘cal-title’).innerText = calDate.toLocaleString(‘default’, { month: ‘long’, year: ‘numeric’ }); const days = new Date(calDate.getFullYear(), calDate.getMonth() + 1, 0).getDate(); const start = new Date(calDate.getFullYear(), calDate.getMonth(), 1).getDay(); for(let i=0; i<start; i++) grid.innerHTML += '
‘; for(let d=1; d b.booking_date === dateStr); let eventsHtml = bks.map(b => `
Office: ${b.is_guest == 1 ? ‘Guest’ : ‘Member’}
`).join(”); grid.innerHTML += `
${d} ${eventsHtml}
`; } } function moveCal(dir) { calDate.setMonth(calDate.getMonth() + dir); renderCalendar(); } // ACTIONS async function saveMember() { const data = { fname: document.getElementById(‘m-fname’).value, lname: document.getElementById(‘m-lname’).value, phone: document.getElementById(‘m-phone’).value, address: document.getElementById(‘m-addr’).value, pledge: document.getElementById(‘m-pledge’).value }; await api(‘save_member’, data); toggleModal(‘modal-member’); fetchData(); } async function saveFinance() { const data = { member_id: document.getElementById(‘pay-mem’).value, type: document.getElementById(‘pay-type’).value, amount: document.getElementById(‘pay-amt’).value }; await api(‘save_finance’, data); document.getElementById(‘pay-amt’).value = ”; fetchData(); } async function saveBooking() { const data = { is_guest: !isBookMember, member_id: document.getElementById(‘bk-mem’).value, guest_name: document.getElementById(‘bk-gn’).value, guest_contact: document.getElementById(‘bk-gc’).value, date: document.getElementById(‘bk-date’).value, fee: document.getElementById(‘bk-fee’).value, purpose: document.getElementById(‘bk-purp’).value }; await api(‘save_booking’, data); fetchData(); } function setBookMode(isMem) { isBookMember = isMem; document.getElementById(‘btn-bm’).className = isMem ? ‘flex-1 py-1 rounded bg-blue-100 text-blue-600 font-bold’ : ‘flex-1 py-1 rounded text-slate-400’; document.getElementById(‘btn-bv’).className = !isMem ? ‘flex-1 py-1 rounded bg-blue-100 text-blue-600 font-bold’ : ‘flex-1 py-1 rounded text-slate-400’; document.getElementById(‘bk-mem-wrap’).classList.toggle(‘hidden’, !isMem); document.getElementById(‘bk-gst-wrap’).classList.toggle(‘hidden’, isMem); } async function del(type, id) { if(confirm(‘Delete this record?’)) { await api(‘delete_item’, { type, id }); fetchData(); } } function toggleModal(id) { document.getElementById(id).classList.toggle(‘hidden’); } function populateSelects() { const s = document.getElementById(‘pay-mem’); const sb = document.getElementById(‘bk-mem’); const opts = DB.members.map(m => `${m.fname} ${m.lname}`).join(”); s.innerHTML = sb.innerHTML = opts; } // EXPORT PDF function exportPDF(type) { const { jsPDF } = window.jspdf; const doc = new jsPDF(); if (type === ‘registry’) { doc.text(“DFM Member Registry”, 14, 15); const data = DB.members.map(m => [m.fname + ‘ ‘ + m.lname, m.phone, m.address, ‘$’+m.pledge]); doc.autoTable({ head: [[‘Name’, ‘Phone’, ‘Address’, ‘Pledge’]], body: data, startY: 20 }); } else { doc.text(“DFM Financial Report”, 14, 15); const data = DB.finances.map(f => { const m = DB.members.find(x => x.id == f.member_id); return [f.date, m?.fname, f.type, ‘$’+f.amount]; }); doc.autoTable({ head: [[‘Date’, ‘Member’, ‘Type’, ‘Amount’]], body: data, startY: 20 }); } doc.save(`DFM_Report_${type}.pdf`); } Panda express temple tx