โ† Back to Dashboard

๐Ÿข CVCWVUAA Complete Management Hub

Your One-Stop Solution: Member Management โ€ข Receipt Generation โ€ข Financial Tracking

โœ… This is your MAIN TOOL - Everything you need in one place! No more switching between different tools.
0
Total Members
0
Current on Dues
0
Overdue Dues
0
Pending Payment
๐Ÿ› ๏ธ Bulk Actions:
Member Name Email & Phone Address Membership Type Dues Status 2025 Last Payment Financial Actions
`; // Plain text version for email clients that don't support HTML const textBody = `Dear Fellow Mountaineer, This is a friendly reminder that your 2025 membership dues are overdue for the Central Virginia Chapter of West Virginia University Alumni Association. PAYMENT OPTIONS: Online Payment: โ€ข Single Membership ($25): https://www.paypal.com/donate/?hosted_button_id=P2MJTS44SXS4U โ€ข Family Addition ($15): https://www.paypal.com/donate/?hosted_button_id=H6L88M4VBJS8W Mail Payment: Make checks payable to CVCWVUAA Mail to: 4701 Stoney Creek Parkway, Chester, VA 23831 Your membership helps support scholarships and chapter activities. Thank you for your continued support! Let's Go Mountaineers! ๐Ÿ”ฅ Central Virginia Chapter WVU Alumni Association https://cvawvuaa.org`; // Create modal for email preview const modal = document.createElement('div'); modal.className = 'modal'; modal.innerHTML = ` `; document.body.appendChild(modal); modal.style.display = 'flex'; // Store the HTML body for later use modal.htmlBody = htmlBody; modal.textBody = textBody; } function showHtmlPreview() { const modal = document.querySelector('.modal'); const preview = document.getElementById('emailPreview'); const textarea = document.getElementById('emailBody'); preview.style.display = 'block'; textarea.style.display = 'none'; preview.innerHTML = modal.htmlBody; } function showTextVersion() { const modal = document.querySelector('.modal'); const preview = document.getElementById('emailPreview'); const textarea = document.getElementById('emailBody'); preview.style.display = 'none'; textarea.style.display = 'block'; textarea.value = modal.textBody; } function openEmailClient() { const subject = document.getElementById('emailSubject').value; const overdueMembers = members.filter(m => m.duesStatus === 'overdue' && m.email); const emails = overdueMembers.map(m => m.email).join(';'); // Use text version for mailto links (HTML not supported in mailto) const modal = document.querySelector('.modal'); const textBody = modal.textBody; const mailtoLink = `mailto:${emails}?subject=${encodeURIComponent(subject)}&body=${encodeURIComponent(textBody)}`; // Try multiple methods to open email try { // Method 1: Direct window.open const emailWindow = window.open(mailtoLink); // Method 2: If popup blocked, try location.href setTimeout(() => { if (!emailWindow || emailWindow.closed) { window.location.href = mailtoLink; } }, 100); closeEmailModal(); alert(`โœ… Email client should open with ${overdueMembers.length} recipients.\n\nNote: The email will open in text format (HTML formatting not supported in mailto links).\n\nFor HTML email, use "Copy Email Info" and paste into your email program, then switch to HTML mode.`); } catch (error) { alert('โŒ Could not open email client automatically. Please use the "Copy Email Info" button to copy the details and paste them manually into your email program.'); } } function copyEmailInfo() { const subject = document.getElementById('emailSubject').value; const overdueMembers = members.filter(m => m.duesStatus === 'overdue' && m.email); const emails = overdueMembers.map(m => m.email).join('; '); const modal = document.querySelector('.modal'); // Create comprehensive email info with both versions const emailInfo = `TO: ${emails} SUBJECT: ${subject} =================== HTML VERSION (Recommended) =================== ${modal.htmlBody} =================== TEXT VERSION (Fallback) =================== ${modal.textBody} INSTRUCTIONS: 1. Copy the HTML version above 2. Paste into your email program 3. Switch your email to HTML/Rich Text mode 4. The formatting and clickable links will appear 5. If HTML doesn't work, use the text version instead`; navigator.clipboard.writeText(emailInfo).then(() => { closeEmailModal(); alert(`๐Ÿ“‹ Complete email information copied to clipboard!\n\nRecipients: ${overdueMembers.length} members\n\nIncludes both HTML (with clickable links) and text versions.\n\nPaste into your email program and use HTML mode for best results.`); }).catch(() => { // Fallback for browsers that don't support clipboard API const textArea = document.createElement('textarea'); textArea.value = emailInfo; document.body.appendChild(textArea); textArea.select(); document.execCommand('copy'); document.body.removeChild(textArea); closeEmailModal(); alert(`๐Ÿ“‹ Email information copied to clipboard!\n\nRecipients: ${overdueMembers.length} members\n\nIncludes HTML and text versions.`); }); } function closeEmailModal() { const modals = document.querySelectorAll('.modal'); modals.forEach(modal => modal.remove()); } function exportDuesReport() { const report = members.map(m => `${m.name}\t${m.email}\t${m.type}\t${m.duesStatus}\t${formatDate(m.lastPayment)}` ).join('\n'); const csvContent = `Name\tEmail\tType\tDues Status\tLast Payment\n${report}`; const blob = new Blob([csvContent], { type: 'text/plain' }); const url = URL.createObjectURL(blob); const a = document.createElement('a'); a.href = url; a.download = `CVCWVUAA-Dues-Report-${new Date().toISOString().split('T')[0]}.txt`; document.body.appendChild(a); a.click(); document.body.removeChild(a); URL.revokeObjectURL(url); alert('๐Ÿ“Š Dues report exported!'); } function bulkDeleteOverdue() { const overdueMembers = members.filter(m => m.duesStatus === 'overdue'); if (overdueMembers.length === 0) { alert('No overdue members found'); return; } showConfirmation( `Delete ${overdueMembers.length} overdue members?`, `This will permanently remove all members marked as overdue. This action cannot be undone.`, () => { members = members.filter(m => m.duesStatus !== 'overdue'); updateDisplay(); alert(`๐Ÿ—‘๏ธ Deleted ${overdueMembers.length} overdue members`); } ); } function contactMember(email) { window.open(`mailto:${email}?subject=From CVCWVUAA President`); } // Receipt Generation Functions function openReceiptGenerator() { window.open('receipt-generator.html', '_blank', 'width=1200,height=800,scrollbars=yes,resizable=yes'); } function createMemberReceipt(memberId) { console.log('Create receipt called with ID:', memberId, 'type:', typeof memberId); const member = members.find(m => m.id == memberId || m.id === memberId); if (!member) { console.error('Member not found for receipt. ID:', memberId); console.log('Available member IDs:', members.map(m => ({id: m.id, type: typeof m.id}))); alert('Member not found'); return; } // Open receipt generator with pre-filled member data const receiptWindow = window.open('receipt-generator.html', '_blank', 'width=1200,height=800,scrollbars=yes,resizable=yes'); // Wait for the receipt generator to load, then populate with member data receiptWindow.onload = function() { setTimeout(() => { // Pre-populate the receipt generator form if (receiptWindow.document.getElementById('memberName')) { receiptWindow.document.getElementById('memberName').value = member.name || ''; receiptWindow.document.getElementById('memberEmail').value = member.email || ''; receiptWindow.document.getElementById('memberPhone').value = member.phone || ''; // Format address - debug logging console.log('Member address data:', { streetAddress: member.streetAddress, city: member.city, state: member.state, zip: member.zip, fullAddress: member.fullAddress }); const addressParts = [member.streetAddress, member.city, member.state, member.zip].filter(x => x && x.trim()); const formattedAddress = addressParts.length > 0 ? addressParts.join(', ') : (member.fullAddress || ''); receiptWindow.document.getElementById('memberAddress').value = formattedAddress; receiptWindow.document.getElementById('memberSince').value = member.memberSince || member.joinDate || 'Not available'; // Pre-populate payment amounts if available if (member.duesAmount && member.duesAmount > 0) { receiptWindow.document.getElementById('membershipAmount').value = member.duesAmount.toFixed(2); } if (member.scholarshipAmount && member.scholarshipAmount > 0) { receiptWindow.document.getElementById('donationAmount').value = member.scholarshipAmount.toFixed(2); } if (member.generalFundAmount && member.generalFundAmount > 0) { receiptWindow.document.getElementById('otherAmount').value = member.generalFundAmount.toFixed(2); } // Set last payment date if available if (member.lastPayment) { const paymentDate = new Date(member.lastPayment); if (!isNaN(paymentDate.getTime())) { receiptWindow.document.getElementById('paymentDate').value = paymentDate.toISOString().split('T')[0]; } } // Trigger total calculation if (receiptWindow.calculateTotal) { receiptWindow.calculateTotal(); } } }, 500); // Give the window time to fully load }; } function generateReceiptForPayment(memberId, paymentData) { const member = members.find(m => m.id === memberId); if (!member) { alert('Member not found'); return; } // Prepare receipt data const memberData = { name: member.name, email: member.email, phone: member.phone, address: [member.streetAddress, member.city, member.state, member.zip].filter(x => x).join(', '), memberSince: member.memberSince || member.joinDate }; // Generate receipt number const now = new Date(); const year = now.getFullYear(); const timestamp = Date.now().toString().slice(-6); const receiptNumber = `CVCWV-${year}-${timestamp}`; const receiptData = { receiptNumber: receiptNumber, date: paymentData.date || new Date().toLocaleDateString(), method: paymentData.method || 'Not specified', transactionId: paymentData.transactionId || 'N/A', processedBy: 'CVCWVUAA Admin', membershipAmount: paymentData.membershipAmount || '0.00', eventAmount: paymentData.eventAmount || '0.00', donationAmount: paymentData.donationAmount || '0.00', otherAmount: paymentData.otherAmount || '0.00' }; // Open receipt template with populated data const receiptWindow = window.open('receipt-template.html', '_blank'); receiptWindow.onload = function() { receiptWindow.receiptData = { member: memberData, payment: receiptData }; receiptWindow.populateReceipt(memberData, receiptData); }; // Save payment record to member if (!member.payments) { member.payments = []; } const totalAmount = parseFloat(paymentData.membershipAmount || 0) + parseFloat(paymentData.eventAmount || 0) + parseFloat(paymentData.donationAmount || 0) + parseFloat(paymentData.otherAmount || 0); member.payments.push({ date: paymentData.date, amount: totalAmount, method: paymentData.method, receiptNumber: receiptNumber, breakdown: { membership: parseFloat(paymentData.membershipAmount || 0), event: parseFloat(paymentData.eventAmount || 0), donation: parseFloat(paymentData.donationAmount || 0), other: parseFloat(paymentData.otherAmount || 0) } }); // Update member totals member.totalAmountPaid = (member.totalAmountPaid || 0) + totalAmount; member.lastPaymentDate = paymentData.date; member.lastPaymentAmount = totalAmount; // Mark as admin modified to preserve data member.hasAdminPaymentChanges = true; // Save to localStorage localStorage.setItem('cvcwvuaa-members', JSON.stringify(members)); return receiptNumber; } // Initialize on page load document.addEventListener('DOMContentLoaded', function() { initializeMembers(); });