/* 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; }
DFM SECRETARIAT
Official Access Only
Invalid Credentials
Member Registry
| Name | Phone | Address | Debt | Action |
Financial Ledger
Record Payment
Monthly Subscription ($1)
Funeral Fund ($1)
Pledge
Tithe
Recent Transactions
Apostle Office
New Booking
// 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}
`;
});
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