- Added stats cards to display active, completed, overdue, and due-today tasks on the dashboard. - Implemented search functionality to filter tasks in real-time based on user input. - Introduced quick-pick date buttons for easier task date selection. - Updated task rendering logic to handle empty states for task lists. - Improved overall user interface with new CSS styles for stat cards and buttons. chore: Update environment variables and backend error handling - Fixed formatting in the .env file for consistency. - Enhanced error handling in the backend API for better debugging. feat: Revamp frontend pages with new features and pricing sections - Redesigned the index.html page to include a hero section and feature highlights. - Created a new features page with dynamic loading of features from JSON. - Implemented a pricing page that loads plans from JSON and highlights the featured plan. - Added support and FAQ sections with dynamic content loading and contact form functionality. - Introduced JSON files for FAQs, features, and pricing to allow easy updates without code changes.
79 lines
2.4 KiB
JavaScript
79 lines
2.4 KiB
JavaScript
/**
|
|
* support.js
|
|
*
|
|
* Powers the Support page.
|
|
*
|
|
* Features:
|
|
* 1. FAQ accordion
|
|
* - Loads Q&A pairs from `./data/faq.json`.
|
|
* - Renders each as a Bootstrap accordion item so only one
|
|
* answer is open at a time.
|
|
*
|
|
* 2. Contact form
|
|
* - Validates that all fields have been filled in.
|
|
* - Displays an inline success or error alert above the form.
|
|
* - Does not currently persist messages; the form is purely a
|
|
* frontend demonstration (see README / features notes).
|
|
*/
|
|
|
|
const faqContainer = document.getElementById('faqContainer');
|
|
const contactForm = document.getElementById('contactForm');
|
|
const contactStatus = document.getElementById('contactStatus');
|
|
|
|
async function loadFaqs() {
|
|
try {
|
|
const response = await fetch('./data/faq.json');
|
|
if (!response.ok) {
|
|
throw new Error(`Failed to load FAQs: ${response.status}`);
|
|
}
|
|
const data = await response.json();
|
|
renderFaqs(data.faqs);
|
|
} catch (error) {
|
|
console.error('Error:', error);
|
|
faqContainer.innerHTML = `<p class="text-white text-center">Unable to load FAQs.</p>`;
|
|
}
|
|
}
|
|
|
|
function renderFaqs(faqs) {
|
|
faqContainer.innerHTML = '';
|
|
faqs.forEach((faq, i) => {
|
|
const item = document.createElement('div');
|
|
item.className = 'accordion-item';
|
|
item.innerHTML = `
|
|
<h2 class="accordion-header">
|
|
<button class="accordion-button collapsed" type="button" data-bs-toggle="collapse"
|
|
data-bs-target="#faq-${i}" aria-expanded="false" aria-controls="faq-${i}">
|
|
${faq.question}
|
|
</button>
|
|
</h2>
|
|
<div id="faq-${i}" class="accordion-collapse collapse" data-bs-parent="#faqContainer">
|
|
<div class="accordion-body">${faq.answer}</div>
|
|
</div>
|
|
`;
|
|
faqContainer.appendChild(item);
|
|
});
|
|
}
|
|
|
|
contactForm.addEventListener('submit', (event) => {
|
|
event.preventDefault();
|
|
const name = contactForm.name.value.trim();
|
|
const email = contactForm.email.value.trim();
|
|
const message = contactForm.message.value.trim();
|
|
|
|
if (!name || !email || !message) {
|
|
showStatus('Please fill out all fields.', 'danger');
|
|
return;
|
|
}
|
|
|
|
showStatus('Thanks, your message has been received. We will get back to you shortly.', 'success');
|
|
contactForm.reset();
|
|
});
|
|
|
|
function showStatus(message, type) {
|
|
contactStatus.className = `alert alert-${type}`;
|
|
contactStatus.textContent = message;
|
|
contactStatus.classList.remove('d-none');
|
|
}
|
|
|
|
window.addEventListener('DOMContentLoaded', loadFaqs);
|