// Function to load company logo in the header async function loadHeaderLogo() { try { const response = await fetch('/company/logo'); const data = await response.json(); const siteLogoImg = document.getElementById('site-company-logo'); if (siteLogoImg && data.logo) { siteLogoImg.src = data.logo; // Now uses local URL siteLogoImg.style.display = 'inline-block'; } else if (siteLogoImg) { siteLogoImg.style.display = 'none'; } } catch (error) { console.error('Error loading header logo:', error); const siteLogoImg = document.getElementById('site-company-logo'); if (siteLogoImg) siteLogoImg.style.display = 'none'; } } // Call the function to load the header logo when the DOM is loaded document.addEventListener('DOMContentLoaded', loadHeaderLogo); // Function to create a job card function createJobCard(job) { return `
${job.companyName || ''}
${job.position || 'Position Available'}
Salary:${job.salary || ''}
Category:${job.category || ''}
Type:${job.type || ''}
Education:${job.education || ''}
Location:${job.location || ''}
Requirements:
`; } // Store all jobs fetched initially let allJobs = []; // Function to load jobs async function loadJobs() { try { const response = await fetch('/jobs'); if (!response.ok) { throw new Error('Failed to fetch jobs'); } const jobs = await response.json(); allJobs = jobs; // Store all fetched jobs displayJobs(allJobs, 'jobs-grid'); // Display all jobs initially } catch (error) { console.error('Error loading jobs:', error); document.getElementById('jobs-grid').innerHTML = '
Failed to load jobs. Please try again later.
'; } } // Function to display a list of jobs in a specific grid function displayJobs(jobsToDisplay, gridId) { const jobCardsContainer = document.getElementById(gridId); jobCardsContainer.innerHTML = jobsToDisplay.map(job => { // Calculate experience level for display let expLevel = ''; if (job.experience) expLevel = getExperienceLevel(job.experience); return createJobCard({ ...job, experience: expLevel }); }).join(''); // Add click event listeners to job cards jobCardsContainer.querySelectorAll('.job-card').forEach((card, idx) => { card.addEventListener('click', () => { const job = jobsToDisplay[idx]; showJobDetails(job); openJobModal(); }); }); } // Filter options are now handled by static HTML checkboxes for categories // --- Modern Backend-Driven Filtering, Search, and Infinite Scroll --- const locationFilter = document.getElementById('location'); const experienceFilter = document.getElementById('experience'); const typeFilter = document.getElementById('type'); const timingFilter = document.getElementById('timing'); const educationFilter = document.getElementById('education'); const jobsGrid = document.getElementById('jobs-grid'); const allJobsGrid = document.getElementById('all-jobs-grid'); const noJobsFallback = document.getElementById('no-jobs-fallback'); let jobs = []; let skip = 0; const limit = 15; // fetch latest 15 per page let loading = false; let allLoaded = false; let lastQuery = ''; function getFiltersQuery() { const params = new URLSearchParams(); const searchBar = document.getElementById('search-bar'); if (searchBar && searchBar.value) params.append('search', searchBar.value.trim()); // Handle multiple type selection from checkboxes const selectedTypes = Array.from(document.querySelectorAll('input[name="type"]:checked')) .map(checkbox => checkbox.value); if (selectedTypes.length > 0) { selectedTypes.forEach(type => { params.append('types[]', type); }); } // Handle multiple education selection from checkboxes const selectedEducation = Array.from(document.querySelectorAll('input[name="education"]:checked')) .map(checkbox => checkbox.value); if (selectedEducation.length > 0) { selectedEducation.forEach(edu => { params.append('education[]', edu); }); } // Handle multiple experience selection from checkboxes const selectedExperience = Array.from(document.querySelectorAll('input[name="experience"]:checked')) .map(checkbox => checkbox.value); if (selectedExperience.length > 0) { selectedExperience.forEach(exp => { params.append('experience[]', exp); }); } // Handle multiple category selection from checkboxes const selectedCategories = Array.from(document.querySelectorAll('input[name="category"]:checked')) .map(checkbox => checkbox.value); if (selectedCategories.length > 0) { selectedCategories.forEach(category => { params.append('categories[]', category); }); } params.append('skip', skip); params.append('limit', limit); return params.toString(); } function showSpinner() { let spinner = document.getElementById('infinite-scroll-spinner'); if (!spinner) { spinner = document.createElement('div'); spinner.id = 'infinite-scroll-spinner'; spinner.innerHTML = '
'; jobsGrid.parentNode.appendChild(spinner); } spinner.style.display = 'flex'; } function hideSpinner() { const spinner = document.getElementById('infinite-scroll-spinner'); if (spinner) spinner.style.display = 'none'; } async function fetchJobs(reset = false) { if (loading || allLoaded) return; loading = true; showSpinner(); if (reset) { skip = 0; allLoaded = false; jobs = []; jobsGrid.innerHTML = ''; hideNoJobs(); } const query = getFiltersQuery(); lastQuery = query; try { const res = await fetch(`/jobs?${query}`); const data = await res.json(); if (reset) jobs = []; if (Array.isArray(data.jobs)) { jobs = jobs.concat(data.jobs); skip += data.jobs.length; if (data.jobs.length < limit) allLoaded = true; } else { allLoaded = true; } renderJobs(); if (jobs.length === 0) showNoJobs(); else hideNoJobs(); } catch (err) { showNoJobs('Failed to load jobs. Please try again later.'); } hideSpinner(); loading = false; } function renderJobs() { jobsGrid.innerHTML = jobs.map(createJobCard).join(''); jobsGrid.querySelectorAll('.job-card').forEach((card, idx) => { card.addEventListener('click', () => { const job = jobs[idx]; showJobDetails(job); openJobModal(); }); }); } function showNoJobs(msg) { noJobsFallback.style.display = 'block'; noJobsFallback.querySelector('.no-jobs-message').textContent = msg || 'No jobs match your criteria.'; allJobsGrid.innerHTML = ''; } function hideNoJobs() { noJobsFallback.style.display = 'none'; } function onFilterChange() { skip = 0; allLoaded = false; fetchJobs(true); } // --- Search and Filter Optimization --- // Only trigger search/filter on Enter or search icon click document.addEventListener('DOMContentLoaded', function() { const searchBar = document.getElementById('search-bar'); const searchIconBtn = document.querySelector('.search-icon-btn'); if (searchBar) { searchBar.addEventListener('keydown', function(e) { if (e.key === 'Enter') { e.preventDefault(); console.log('Search triggered by Enter key'); onFilterChange(); } }); // Add visual feedback for search searchBar.addEventListener('input', function() { // Optional: Add real-time search feedback if (searchBar.value.length > 2) { searchBar.style.borderColor = 'var(--accent-color)'; } else { searchBar.style.borderColor = 'var(--border-color)'; } }); } if (searchIconBtn) { searchIconBtn.addEventListener('click', function(e) { e.preventDefault(); console.log('Search triggered by button click'); onFilterChange(); }); } }); // Remove input event for searchBar (no more search on every keystroke) // --- Filter options are now handled by HTML checkboxes for categories --- // --- Experience Level Calculation --- function getExperienceLevel(years) { if (typeof years === 'string') years = parseFloat(years); if (isNaN(years) || years <= 1) return 'Entry'; if (years > 1 && years <= 3) return 'Mid'; return 'Senior'; } // Use getExperienceLevel when displaying/filtering jobs function displayJobs(jobsToDisplay, gridId) { const jobCardsContainer = document.getElementById(gridId); jobCardsContainer.innerHTML = jobsToDisplay.map(job => { // Calculate experience level for display let expLevel = ''; if (job.experience) expLevel = getExperienceLevel(job.experience); return createJobCard({ ...job, experience: expLevel }); }).join(''); // Add click event listeners to job cards jobCardsContainer.querySelectorAll('.job-card').forEach((card, idx) => { card.addEventListener('click', () => { const job = jobsToDisplay[idx]; showJobDetails(job); openJobModal(); }); }); } // Infinite scroll window.addEventListener('scroll', () => { if (loading || allLoaded) return; if ((window.innerHeight + window.scrollY) >= (document.body.offsetHeight - 300)) { fetchJobs(); } }); // Debounce helper function debounce(fn, delay) { let timeout; return function(...args) { clearTimeout(timeout); timeout = setTimeout(() => fn.apply(this, args), delay); }; } // Initial load fetchJobs(true); // Function to show job details in modal function showJobDetails(job) { window.currentJobId = job.id; document.getElementById('modal-company-logo').src = job.companyLogo || '/images/default-company.svg'; document.getElementById('modal-job-title').textContent = job.position || 'Position Available'; document.getElementById('modal-company-name').textContent = job.companyName || ''; document.getElementById('modal-location').textContent = job.location || ''; document.getElementById('modal-salary').textContent = job.salary || ''; document.getElementById('modal-type').textContent = job.type || ''; document.getElementById('modal-category').textContent = job.category || ''; document.getElementById('modal-timing').textContent = job.timing || ''; document.getElementById('modal-education').textContent = job.education || ''; document.getElementById('modal-experience').textContent = job.experience || ''; document.getElementById('modal-contact').textContent = job.contactNumber || ''; document.getElementById('modal-description').textContent = job.description || ''; document.getElementById('modal-requirements').innerHTML = (job.requirements && job.requirements.length > 0) ? `` : 'No specific requirements listed'; // Handle job image display const jobImageElement = document.getElementById('modal-job-image'); if (job.jobImage && job.jobImage.trim() !== '') { jobImageElement.src = job.jobImage; jobImageElement.style.display = 'block'; } else { console.log('No job image, hiding element'); jobImageElement.style.display = 'none'; } } // Function to hide job details modal function hideJobDetailsModal() { document.getElementById('job-modal').style.display = 'none'; } // Function to show the application modal function showApplicationModal(jobId) { document.getElementById('application-job-id').value = jobId; document.getElementById('application-modal').style.display = 'flex'; // Use flex to center modal } // Close modal when clicking the close button or outside the modal const closeJobModalBtn = document.getElementById('close-job-modal'); if (closeJobModalBtn) { closeJobModalBtn.addEventListener('click', hideJobDetailsModal); } const closeApplicationModalBtn = document.getElementById('close-application-modal'); if (closeApplicationModalBtn) { closeApplicationModalBtn.addEventListener('click', hideApplicationModal); } window.addEventListener('click', (e) => { const jobModal = document.getElementById('job-modal'); const applicationModal = document.getElementById('application-modal'); // Check if the click is outside the modal content if (e.target === jobModal) { hideJobDetailsModal(); } else if (e.target === applicationModal) { hideApplicationModal(); } }); // Modal open/close logic function closeJobModal() { document.getElementById('job-modal').style.display = 'none'; } function openJobModal() { document.getElementById('job-modal').style.display = 'block'; } function closeApplyModal() { document.getElementById('apply-modal').style.display = 'none'; } function openApplyForm() { closeJobModal(); document.getElementById('apply-modal').style.display = 'block'; document.getElementById('apply-job-title').textContent = document.getElementById('modal-job-title').textContent; } // Handle application form submission const applyForm = document.getElementById('applyJobForm'); const applyLoadingSpinner = document.getElementById('apply-loading-spinner'); if (applyForm) { applyForm.addEventListener('submit', async function(e) { e.preventDefault(); const form = e.target; const formData = new FormData(form); const jobId = window.currentJobId; if (!jobId) { alert('No job selected.'); return; } // Show spinner if (applyLoadingSpinner) applyLoadingSpinner.style.display = 'flex'; try { const response = await fetch(`/jobs/${jobId}/apply`, { method: 'POST', body: formData }); const result = await response.json(); // Hide spinner if (applyLoadingSpinner) applyLoadingSpinner.style.display = 'none'; if (response.ok) { alert('Application submitted successfully!'); closeApplyModal(); form.reset(); } else { alert(result.message || 'Failed to submit application.'); } } catch (err) { // Hide spinner if (applyLoadingSpinner) applyLoadingSpinner.style.display = 'none'; alert('Error submitting application.'); } }); } // Add modal functions function showModal(message) { const modal = document.getElementById('authModal'); const modalMessage = document.getElementById('modalMessage'); modalMessage.textContent = message; modal.style.display = 'flex'; } function closeModal() { const modal = document.getElementById('authModal'); modal.style.display = 'none'; } // Handle restricted link click document.getElementById('add-opportunity-link').addEventListener('click', async (e) => { e.preventDefault(); try { const response = await fetch('/auth/check-session'); const data = await response.json(); if (!data.isLoggedIn) { showModal('Please log in to add job opportunities.'); } else if (data.userType === 'applicant') { showModal('Applicants cannot add job opportunities. Please contact the admin for more information.'); } else { window.location.href = '/jobs/add'; } } catch (error) { console.error('Error checking session:', error); showModal('An error occurred. Please try again later.'); } }); // Close modal when clicking outside window.addEventListener('click', (e) => { const modal = document.getElementById('authModal'); if (e.target === modal) { closeModal(); } }); // Add a function to check login status async function isUserLoggedIn() { try { const response = await fetch('/auth/check-session'); const data = await response.json(); return data.isLoggedIn; } catch (err) { return false; } } // Update openApplyForm to check login status async function openApplyForm() { const loggedIn = await isUserLoggedIn(); if (!loggedIn) { alert('Please login to apply.'); return; } closeJobModal(); document.getElementById('apply-modal').style.display = 'block'; document.getElementById('apply-job-title').textContent = document.getElementById('modal-job-title').textContent; } // --- New Filter Modal Logic --- document.addEventListener('DOMContentLoaded', function() { const filterModal = document.getElementById('filter-modal'); const filterToggleBtn = document.getElementById('filter-toggle-btn'); const closeFilterModalBtn = document.getElementById('close-filter-modal'); const applyFiltersBtn = document.getElementById('apply-filters-btn'); const clearFiltersBtn = document.getElementById('clear-filters-btn'); // Open filter modal if (filterToggleBtn) { filterToggleBtn.addEventListener('click', (e) => { e.preventDefault(); filterModal.classList.add('open'); loadSavedFilters(); // Load previously saved filters }); } // Close filter modal if (closeFilterModalBtn) { closeFilterModalBtn.addEventListener('click', () => { filterModal.classList.remove('open'); }); } // Close modal when clicking outside if (filterModal) { filterModal.addEventListener('click', (e) => { if (e.target === filterModal) { filterModal.classList.remove('open'); } }); } // Apply filters if (applyFiltersBtn) { applyFiltersBtn.addEventListener('click', () => { applyFilters(); filterModal.classList.remove('open'); }); } // Clear all filters if (clearFiltersBtn) { clearFiltersBtn.addEventListener('click', () => { clearAllFilters(); }); } }); // Function to apply filters and fetch jobs function applyFilters() { // Save current filter state saveFilters(); // Reset pagination skip = 0; allLoaded = false; // Fetch jobs with new filters fetchJobs(true); } // Function to save filter state to localStorage function saveFilters() { const filters = { types: Array.from(document.querySelectorAll('input[name="type"]:checked')).map(cb => cb.value), education: Array.from(document.querySelectorAll('input[name="education"]:checked')).map(cb => cb.value), experience: Array.from(document.querySelectorAll('input[name="experience"]:checked')).map(cb => cb.value), categories: Array.from(document.querySelectorAll('input[name="category"]:checked')).map(cb => cb.value) }; localStorage.setItem('jobFilters', JSON.stringify(filters)); } // Function to load saved filter state function loadSavedFilters() { const savedFilters = localStorage.getItem('jobFilters'); if (savedFilters) { const filters = JSON.parse(savedFilters); // Clear all checkboxes first document.querySelectorAll('.filter-checkbox').forEach(cb => cb.checked = false); // Apply saved filters if (filters.types) { filters.types.forEach(type => { const checkbox = document.querySelector(`input[name="type"][value="${type}"]`); if (checkbox) checkbox.checked = true; }); } if (filters.education) { filters.education.forEach(edu => { const checkbox = document.querySelector(`input[name="education"][value="${edu}"]`); if (checkbox) checkbox.checked = true; }); } if (filters.experience) { filters.experience.forEach(exp => { const checkbox = document.querySelector(`input[name="experience"][value="${exp}"]`); if (checkbox) checkbox.checked = true; }); } if (filters.categories) { filters.categories.forEach(category => { const checkbox = document.querySelector(`input[name="category"][value="${category}"]`); if (checkbox) checkbox.checked = true; }); } } } // Function to clear all filters function clearAllFilters() { document.querySelectorAll('.filter-checkbox').forEach(cb => cb.checked = false); localStorage.removeItem('jobFilters'); }