Skip to content

Instantly share code, notes, and snippets.

@daltonrooney
Last active October 14, 2022 16:35
Show Gist options
  • Star 3 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save daltonrooney/927a68196e915829349b7a087a1644a5 to your computer and use it in GitHub Desktop.
Save daltonrooney/927a68196e915829349b7a087a1644a5 to your computer and use it in GitHub Desktop.
Craft CMS GQL query w/ filters and pagination
import Vue from 'vue'
import { debounce } from './utils/debounce';
import { ProCatalogCategoriesQuery } from './queries/proCatalogCategories.gql';
import { makeProCatalogResourcesQuery } from './queries/proCatalogResources';
import Loading from './components/loading.vue';
const GraphqlEndpoint = '/api'
const ResultsPerPage = 12;
const el = document.querySelector('#professional-catalog-app')
if (el !== null) {
const vm = new Vue({
delimiters: ['${', '}'],
components: {
Loading
},
data: {
results: [],
topics: [],
currentPage: 1,
totalPages: 0,
pagination: {
pages: [],
hasNext: false,
hasPrev: false,
nextPageUrl: '',
prevPageUrl: '',
},
baseUrl: '',
searching: false,
loaded: false,
selectedTopic: '',
selectedCourseType: '',
textKeywords: '',
},
watch: {
totalPages() {
this.updatePagination();
}
},
computed: {
catalogClass() {
return {
'catalog-items--searching': this.searching,
'catalog--loaded': true,
}
}
},
methods: {
async queryGraphQL(query) {
const response = await fetch(GraphqlEndpoint, {
method: 'POST',
cache: 'no-cache',
credentials: 'same-origin',
headers: {
'Content-Type': 'application/graphql'
},
body: query
});
return await response.json();
},
async updateCategories() {
const response = await this.queryGraphQL(ProCatalogCategoriesQuery);
this.topics = response.data.topics;
},
async updateResults() {
const { selectedTopic, selectedCourseType, textKeywords } = this;
const params = [];
this.searching = true;
if (selectedTopic) params.push(`courseTopic: ${selectedTopic}`);
if (selectedCourseType) params.push(`courseType: "${selectedCourseType}"`);
if (textKeywords) params.push(`search: "${textKeywords}"`);
params.push('section: "professionalLearningCourses"');
params.push(`limit: ${ResultsPerPage}`);
params.push(`offset: ${(this.currentPage - 1) * ResultsPerPage}`);
params.push('orderBy: "title ASC"');
const query = makeProCatalogResourcesQuery(params);
const response = await this.queryGraphQL(query);
if (response.data) {
this.totalPages = Math.ceil(Number(response.data.entryCount) / ResultsPerPage);
this.results = response.data.entries;
} else {
this.results = [];
this.totalPages = 0;
}
setTimeout(() => {
this.searching = false;
}, 1000);
},
updateCurrentPage() {
const pageRegex = window.location.pathname.match(/\/page\/(\d+)$/);
this.currentPage = 1;
if (pageRegex) {
this.currentPage = Number(pageRegex[1]);
}
},
formatDateRange(dateFrom, dateTo) {
if (!dateFrom || !dateTo) return '';
const [monthFrom, dayFrom] = dateFrom.split('-');
const [monthTo, dayTo] = dateTo.split('-');
if (monthFrom === monthTo) {
return `${monthFrom} ${dayFrom}-${dayTo}`;
} else {
return `${monthFrom} ${dayFrom} - ${monthTo} ${dayTo}`;
}
},
getBackgroundStyle(entry) {
const url = entry.featuredImage[0] ? entry.featuredImage[0].url : '';
return `background-image: url('${url}')`;
},
getPageUrl(index = this.currentPage) {
const { baseUrl } = this;
let url = new URL(baseUrl);
if (this.selectedCourseType) url.searchParams.set('courseType', this.selectedCourseType);
if (this.selectedTopic) url.searchParams.set('topic', this.selectedTopic);
if (this.textKeywords) url.searchParams.set('q', `${this.textKeywords}`);
if (index > 1) {
url.pathname += `/page/${index}`;
}
return url.toString();
},
resetPagination() {
this.currentPage = 1;
const url = this.getPageUrl();
if (window.history.replaceState) {
window.history.replaceState(null, '', url);
}
},
restoreFromUrlParams() {
const url = new URL(window.location.href);
const params = url.searchParams;
if (params.has('courseType')) this.selectedCourseType = params.get('courseType');
if (params.has('topic')) this.selectedTopic = params.get('topic');
if (params.has('q')) this.textKeywords = params.get('q');
},
updatePagination() {
const { pagination, currentPage, totalPages } = this;
pagination.pages = [];
for (let i = 0; i < totalPages; i++) {
const index = i + 1;
pagination.pages.push({
text: index,
url: this.getPageUrl(index),
current: index === currentPage
});
}
pagination.hasNext = currentPage < totalPages;
pagination.hasPrev = currentPage > 1;
pagination.nextPageUrl = this.getPageUrl(currentPage + 1);
pagination.prevPageUrl = this.getPageUrl(currentPage - 1);
},
onSearchFilterChanged() {
this.resetPagination();
this.updateCurrentPage();
this.updateResultsDebounced();
}
},
async mounted() {
this.baseUrl = this.$refs.pagination.getAttribute('data-base-url');
this.updateResultsDebounced = debounce(this.updateResults, 500);
this.restoreFromUrlParams();
this.updateCurrentPage();
await this.updateCategories();
await this.updateResults();
this.loaded = true;
}
})
vm.$mount(el)
}
export const ProCatalogCategoriesQuery = `
query {
topics: categories(group: "topic") {
... on topic_Category { id, title }
}
levels: categories(group: "level") {
... on level_Category { id, title }
}
}
`
export function makeProCatalogResourcesQuery(params = []) {
return `
{
entries(${params.join(', ')}) {
... on professionalLearningCourses_professionalLearningCourses_Entry {
id
url
title
}
}
entryCount(${params.join(', ')})
}`
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment