@import "../../resources/scss/util/variables";
@import "../../resources/scss/util/mixins";
.block-job-listings {
padding: rem-calc(60px) 0;
&__wrapper {
@include bp($md) {
padding: 0 1rem;
}
@include bp($lg) {
padding: 0;
}
}
&__heading {
@include responsive-font($heading-7, $heading-6);
font-weight: 400;
margin-bottom: rem-calc(40px);
}
&__filters {
margin-bottom: rem-calc(28px);
@include bp($lg) {
margin-bottom: rem-calc(48px);
}
select {
@include filter-select;
margin-bottom: 14px;
@include bp($lg) {
margin-bottom: 0;
}
}
}
&__no-results,
&__error {
text-align: center;
padding: rem-calc(40px) 0;
color: $primary;
font-size: $heading-8;
}
.pagination-nav {
&:hover {
svg {
path {
stroke: $primary;
}
}
}
}
.pagination {
transition-delay: .3s;
margin-top: rem-calc(48px);
padding-bottom: 0;
@include bp($md) {
margin-top: 2rem;
}
@include bp($xl) {
margin-top: 4rem;
}
.pagination-pages {
display: flex;
flex-wrap: wrap;
justify-content: center;
align-items: center;
}
svg {
path {
stroke: $primary;
}
}
.page-numbers,
.pagination-nav {
padding: 8px 13px;
color: $primary;
text-decoration: none;
transition: background-color 0.3s ease;
margin: 0 5px;
background-color: $white;
&.current,
&[aria-current="page"],
&:hover {
background-color: $secondary-light-green;
}
}
}
}
class JobListings {
block;
currentPage = 1;
constructor(block) {
this.block = block;
this.filters = this.block.querySelectorAll('.block-job-listings__filters select');
this.init();
}
init() {
this.events();
this.loadFromURL();
}
events() {
this.filters.forEach(filter => {
filter.addEventListener('change', () => {
this.currentPage = 1; // Reset to first page when filters change
this.updateURL();
this.filterJobs();
});
});
// Set up pagination event delegation
const paginationContainer = this.block.querySelector('.pagination');
if (paginationContainer) {
paginationContainer.addEventListener('click', (e) => {
// Prevent default for all pagination links
if (e.target.closest('a')) {
e.preventDefault();
// Get the clicked element or its closest anchor
const pageLink = e.target.closest('a');
// Don't do anything if it's the current page or disabled
if (pageLink.classList.contains('current') || pageLink.classList.contains('disabled')) {
return;
}
// Get the page number from data-page attribute or text content
let pageNum;
if (pageLink.dataset.page) {
pageNum = parseInt(pageLink.dataset.page);
} else if (pageLink.classList.contains('page-numbers')) {
if (pageLink.classList.contains('next')) {
pageNum = this.currentPage + 1;
} else if (pageLink.classList.contains('prev')) {
pageNum = this.currentPage - 1;
} else {
pageNum = parseInt(pageLink.textContent);
}
}
if (!isNaN(pageNum)) {
this.currentPage = pageNum;
this.updateURL();
this.filterJobs();
}
}
});
}
}
loadFromURL() {
const pathParts = window.location.pathname.split('/');
const pageIndex = pathParts.indexOf('page');
// Load page number from URL path
if (pageIndex !== -1 && pathParts[pageIndex + 1]) {
this.currentPage = parseInt(pathParts[pageIndex + 1]);
}
// Load filter values from query parameters
const urlParams = new URLSearchParams(window.location.search);
this.filters.forEach(filter => {
const value = urlParams.get(filter.name);
if (value) {
filter.value = value;
}
});
// If we have any URL parameters or page number, perform the search
if (this.currentPage > 1 || Array.from(this.filters).some(filter => filter.value)) {
this.filterJobs();
}
}
updateURL() {
const urlParams = new URLSearchParams(window.location.search);
// Update filter parameters
this.filters.forEach(filter => {
if (filter.value) {
urlParams.set(filter.name, filter.value);
} else {
urlParams.delete(filter.name);
}
});
// Build the new URL
let newURL = window.location.pathname;
// Remove existing page number if present
newURL = newURL.replace(/\/page\/\d+\//, '/');
// Add page number if not on first page
if (this.currentPage > 1) {
newURL = newURL.replace(/\/$/, '') + '/page/' + this.currentPage + '/';
}
// Add query parameters if any exist
const queryString = urlParams.toString();
if (queryString) {
newURL += '?' + queryString;
}
// Update URL without reloading the page
window.history.pushState({}, '', newURL);
}
filterJobs() {
const params = {
paged: this.currentPage
};
this.filters.forEach(filter => {
const value = filter.value;
if (!value) {
return;
}
params[filter.name] = value;
});
fetch(`${window.location.protocol}//${window.location.hostname}/wp-json/wp/v2/jobs/search`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify(params)
})
.then(response => response.json())
.then(data => {
const jobsInner = this.block.querySelector('.block-job-listings__jobs-inner');
const paginationContainer = this.block.querySelector('.pagination');
if (data.count === 0) {
jobsInner.innerHTML = 'No jobs found matching your criteria.
';
if (paginationContainer) {
paginationContainer.style.display = 'none';
}
} else {
jobsInner.innerHTML = data.html;
if (paginationContainer) {
if (data.max_pages > 1) {
paginationContainer.innerHTML = data.pagination;
paginationContainer.style.display = 'block';
// Update current page from response
this.currentPage = data.current_page;
} else {
paginationContainer.style.display = 'none';
}
}
}
})
.catch(error => {
console.error('Error fetching jobs:', error);
this.block.querySelector('.block-job-listings__jobs-inner').innerHTML = 'An error occurred while searching. Please try again.
';
});
}
}
document.addEventListener('DOMContentLoaded', () => {
document.querySelectorAll('.block-job-listings').forEach((block) => {
new JobListings(block);
})
});
{
"$schema": "https://schemas.wp.org/trunk/block.json",
"apiVersion": 2,
"name": "strategiq/job-listings",
"title": "Job Listings",
"description": "Example block to be used as a template",
"category": "strategiq",
"icon": "strategiq",
"acf": {
"mode": "preview",
"renderTemplate": "block-job-listings.php"
},
"supports": {
"anchor": true,
"align": false,
"color": {
"background": true,
"text": false,
"gradients": true
},
"spacing": {
"padding": [
"top",
"bottom"
],
"margin": [
"top",
"bottom"
]
}
},
"example": {
"attributes": {
"mode": "preview",
"data": {
"heading_type": "h2",
"heading_text": "Example - Job Listings",
"content": "This is some example content to represent what the content will look like"
}
}
},
"style": ["file:../../assets/css/job-listings/block-job-listings.css"],
"viewScript": ["job-listings"]
}
This component is not currently used on any pages.