Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions website/modules/case-studies-page/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ module.exports = {
{ name: 'industry' },
{ name: 'stack' },
{ name: 'caseStudyType' },
{ name: 'partner' },
],
pieces: 'case-studies',
piecesFiltersUrl: '/case-studies',
Expand Down Expand Up @@ -59,6 +60,7 @@ module.exports = {
industry: {},
stack: {},
caseStudyType: {},
partner: {},
});
}
},
Expand Down
123 changes: 81 additions & 42 deletions website/modules/case-studies-page/services/NavigationService.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,24 +9,63 @@
*/
class NavigationService {
/**
* Converts tag slugs to IDs
* Converts slugs to document IDs for a given piece module
* @param {Object} apos - ApostropheCMS instance
* @param {Object} req - Request object
* @param {Array} slugs - Array of tag slugs
* @returns {Promise<Array>} Promise resolving to array of tag IDs
* @param {Array|string} slugs - Array of slugs or single slug (Express may pass string for one query param)
* @param {string} moduleKey - Key of the piece module in apos.modules (e.g. 'cases-tags', 'business-partner')
* @returns {Promise<Array>} Promise resolving to array of document IDs
*/
static async convertSlugsToIds(apos, req, slugs) {
const tagPromises = slugs.map(async (slug) => {
const results = await apos.modules['cases-tags']
static async convertSlugsToIdsForModule(apos, req, slugs, moduleKey) {
let slugList = [];
if (Array.isArray(slugs)) {
slugList = slugs;
} else if (slugs) {
slugList = [slugs];
}
const docPromises = slugList.map(async (slug) => {
const results = await apos.modules[moduleKey]
.find(req, { slug })
.toArray();
if (results.length > 0) {
return results[0];
}
return null;
});
const tags = await Promise.all(tagPromises);
return tags.filter((tag) => tag).map((tag) => tag.aposDocId);
const docs = await Promise.all(docPromises);
return docs.filter((doc) => doc).map((doc) => doc.aposDocId);
}

/**
* Converts tag slugs to IDs (cases-tags module)
* @param {Object} apos - ApostropheCMS instance
* @param {Object} req - Request object
* @param {Array} slugs - Array of tag slugs
* @returns {Promise<Array>} Promise resolving to array of tag IDs
*/
static convertSlugsToIds(apos, req, slugs) {
return NavigationService.convertSlugsToIdsForModule(
apos,
req,
slugs,
'cases-tags',
);
}

/**
* Converts business partner slugs to IDs
* @param {Object} apos - ApostropheCMS instance
* @param {Object} req - Request object
* @param {Array} slugs - Array of partner slugs
* @returns {Promise<Array>} Promise resolving to array of partner IDs
*/
static convertPartnerSlugsToIds(apos, req, slugs) {
return NavigationService.convertSlugsToIdsForModule(
apos,
req,
slugs,
'business-partner',
);
}

/**
Expand All @@ -37,45 +76,45 @@ class NavigationService {
* @returns {Promise<Object>} Promise resolving to modified query object
*/
static async applyFiltersToQuery(query, req, apos) {
const filterConfigs = [
{
param: 'industry',
convert: NavigationService.convertSlugsToIds,
field: 'industryIds',
},
{
param: 'stack',
convert: NavigationService.convertSlugsToIds,
field: 'stackIds',
},
{
param: 'caseStudyType',
convert: NavigationService.convertSlugsToIds,
field: 'caseStudyTypeIds',
},
{
param: 'partner',
convert: NavigationService.convertPartnerSlugsToIds,
field: 'partnerIds',
},
];
const idArrays = await Promise.all(
filterConfigs.map((config) => {
if (req.query[config.param]?.length) {
return config.convert(apos, req, req.query[config.param]);
}
return Promise.resolve([]);
}),
);
let filteredQuery = query;

if (req.query.industry && req.query.industry.length > 0) {
const industryIds = await NavigationService.convertSlugsToIds(
apos,
req,
req.query.industry,
);
if (industryIds.length > 0) {
for (let index = 0; index < filterConfigs.length; index += 1) {
const ids = idArrays[index];
if (ids.length > 0) {
filteredQuery = filteredQuery.and({
industryIds: { $in: industryIds },
[filterConfigs[index].field]: { $in: ids },
});
}
}

if (req.query.stack && req.query.stack.length > 0) {
const stackIds = await NavigationService.convertSlugsToIds(
apos,
req,
req.query.stack,
);
if (stackIds.length > 0) {
filteredQuery = filteredQuery.and({ stackIds: { $in: stackIds } });
}
}

if (req.query.caseStudyType && req.query.caseStudyType.length > 0) {
const caseStudyTypeIds = await NavigationService.convertSlugsToIds(
apos,
req,
req.query.caseStudyType,
);
if (caseStudyTypeIds.length > 0) {
filteredQuery = filteredQuery.and({
caseStudyTypeIds: { $in: caseStudyTypeIds },
});
}
}

return filteredQuery;
}

Expand Down
49 changes: 33 additions & 16 deletions website/modules/case-studies-page/services/TagCountService.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,16 +9,16 @@
*/
class TagCountService {
/**
* Creates a mapping from tag IDs to tag slugs
* @param {Array} casesTags - Array of tag objects
* @returns {Object} Map of tag ID to slug
* Creates a mapping from document ID to slug for any array of docs with aposDocId and slug
* @param {Array} docs - Array of document objects with aposDocId and slug
* @returns {Object} Map of document ID to slug
*/
static createTagMap(casesTags) {
const tagMap = {};
casesTags.forEach((tag) => {
tagMap[tag.aposDocId] = tag.slug;
static createIdToSlugMap(docs) {
const map = {};
docs.forEach((doc) => {
map[doc.aposDocId] = doc.slug;
});
return tagMap;
return map;
}

/**
Expand Down Expand Up @@ -47,21 +47,25 @@ class TagCountService {
* @param {Object} req - ApostropheCMS request object
* @param {Object} aposModules - ApostropheCMS modules
* @param {Object} options - Module options
* @returns {Promise<Array>} Promise resolving to [caseStudies, casesTags] arrays
* @returns {Promise<Array>} Promise resolving to [caseStudies, casesTags, businessPartners] arrays
*/
static async fetchCaseStudiesAndTags(req, aposModules, options) {
const caseStudies = await aposModules[options.pieces].find(req).toArray();
const casesTags = await aposModules['cases-tags'].find(req).toArray();
return [caseStudies, casesTags];
const [caseStudies, casesTags, businessPartners] = await Promise.all([
aposModules[options.pieces].find(req).toArray(),
aposModules['cases-tags'].find(req).toArray(),
aposModules['business-partner'].find(req).toArray(),
]);
return [caseStudies, casesTags, businessPartners];
}

/**
* Processes case studies to count tags by type
* @param {Array} caseStudies - Array of case study objects
* @param {Object} tagMap - Map of tag ID to slug
* @param {Object} partnerMap - Map of partner ID to slug
* @param {Object} tagCounts - Object to store all tag counts
*/
static processCaseStudies(caseStudies, tagMap, tagCounts) {
static processCaseStudies(caseStudies, tagMap, partnerMap, tagCounts) {
caseStudies.forEach((study) => {
TagCountService.countTagsOfType(
study.industryIds,
Expand All @@ -76,6 +80,12 @@ class TagCountService {
tagMap,
tagCounts.caseStudyType,
);

TagCountService.countTagsOfType(
study.partnerIds,
partnerMap,
tagCounts.partner,
);
});
}

Expand All @@ -91,14 +101,21 @@ class TagCountService {
industry: {},
stack: {},
caseStudyType: {},
partner: {},
};

const [caseStudies, casesTags] =
const [caseStudies, casesTags, businessPartners] =
await TagCountService.fetchCaseStudiesAndTags(req, aposModules, options);

const tagMap = TagCountService.createTagMap(casesTags);
const tagMap = TagCountService.createIdToSlugMap(casesTags);
const partnerMap = TagCountService.createIdToSlugMap(businessPartners);

TagCountService.processCaseStudies(caseStudies, tagMap, tagCounts);
TagCountService.processCaseStudies(
caseStudies,
tagMap,
partnerMap,
tagCounts,
);

return tagCounts;
}
Expand Down
8 changes: 8 additions & 0 deletions website/modules/case-studies-page/services/UrlService.js
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,14 @@ class UrlService {
});
}

// Add partner parameters
if (query.partner) {
const partners = UrlService.ensureArray(query.partner);
partners.forEach((partner, index) => {
params.append(`partner[${index}]`, partner);
});
}

// Add search parameter
if (query.search) {
params.append('search', query.search);
Expand Down
32 changes: 28 additions & 4 deletions website/modules/case-studies-page/views/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@
<div class="cs_filter-info">
<div class="filters-cta">Filter Case Studies</div>
{% set hasActiveFilters = data.query.industry or data.query.stack or
data.query.caseStudyType %} {% if hasActiveFilters %}
data.query.caseStudyType or data.query.partner %} {% if hasActiveFilters %}
<p class="items-count">
{{ data.totalPieces }} Ite{% if data.totalPieces == 1 %}m{% else %}ms{%
endif %} Found
Expand All @@ -32,7 +32,7 @@
<div class="clear-all">
<a
class="clear-all-link"
href="{{ data.url | build({ industry: null, stack: null, caseStudyType: null }) }}#filter"
href="{{ data.url | build({ industry: null, stack: null, caseStudyType: null, partner: null }) }}#filter"
aria-label="Clear all filters"
>
<span class="clear-all-content">
Expand All @@ -46,7 +46,7 @@

<div class="selected-tags">
<ul class="selected-tags-list">
{% for filterType in ['industry', 'stack', 'caseStudyType'] %} {% if
{% for filterType in ['industry', 'stack', 'caseStudyType', 'partner'] %} {% if
data.query[filterType] %} {% for tag in data.piecesFilters[filterType]
%} {# Check if tag is selected (handle both array and string) #} {%
set isTagSelected = false %} {% if data.query[filterType].length is
Expand Down Expand Up @@ -79,6 +79,14 @@
>
<img src="/images/close.svg" alt="Close Icon" />
</a>
{% elif filterType == 'partner' %}
<a
class="remove-tag"
href="{{ data.url | build({}, { partner: { $pull: tag.value } }) }}#filter"
aria-label="Remove partner tag {{ tag.label }}"
>
<img src="/images/close.svg" alt="Close Icon" />
</a>
{% endif %}
</li>
{% endif %} {% endfor %} {% endif %} {% endfor %}
Expand All @@ -91,7 +99,7 @@
<div class="tags-filter">
{# Display all filter types #} {% for filterType, filterLabel in [
['industry', 'Industry'], ['caseStudyType', 'Case Type'], ['stack',
'Technology'] ] %} {% set tags = data.piecesFilters[filterType] %} {% if
'Technology'], ['partner', 'Partner'] ] %} {% set tags = data.piecesFilters[filterType] %} {% if
tags and tags.length %}
<div class="filter-section">
<!-- prettier-ignore -->
Expand Down Expand Up @@ -175,6 +183,14 @@
<span class="tag-label">{{ tag.label }}</span>
<span class="tag-count">[ {{ count }} ]</span>
</a>
{% elif filterType == 'partner' %}
<a
class="tag-link"
href="{{ data.url | build({}, { partner: { $pull: tag.value } }) }}#filter"
>
<span class="tag-label">{{ tag.label }}</span>
<span class="tag-count">[ {{ count }} ]</span>
</a>
{% endif %}
</li>
{% else %}
Expand Down Expand Up @@ -206,6 +222,14 @@
<span class="tag-label">{{ tag.label }}</span>
<span class="tag-count">[ {{ count }} ]</span>
</a>
{% elif filterType == 'partner' %}
<a
class="tag-link"
href="{{ data.url | build({}, { partner: { $addToSet: tag.value } }) }}#filter"
>
<span class="tag-label">{{ tag.label }}</span>
<span class="tag-count">[ {{ count }} ]</span>
</a>
{% endif %}
</li>
{% endif %} {% endfor %}
Expand Down
3 changes: 3 additions & 0 deletions website/modules/case-studies/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -199,6 +199,9 @@ module.exports = {
_stack: {
label: 'Stack',
},
_partner: {
label: 'Partner',
},
},
},
helpers() {
Expand Down
Loading