This snippet and JavaScript can be added to your Shopify Product Page to display a fitment confirmation notice to customers.
A message confirming if the current product fits their selected vehicle or note (requires the customer to have used Plate Search or MMYVS).
Create the following snippet and JavaScript files in your theme code base.
snippets/partbot-vehicle-fitment.liquid
{% assign vehicle_ids = product.metafields.partbot.vehicle_ids %}
{% assign vehicle_years = product.metafields.partbot.vehicle_years %}
<script src="{{ 'partbot-vehicle-fitment.js' | asset_url }}" defer="defer"></script>
<partbot-vehicle-fitment
class="partbot-vehicle-fitment hidden"
data-vehicle-ids="{{ vehicle_ids }}"
data-vehicle-years="{{ vehicle_years | escape }}">
</partbot-vehicle-fitment>
assets/partbot-vehicle-fitment.js
We've styled the confirmation message using TailwindCSS and FontAwesome icons, you may wish to customise to match your theme and CSS classes.
class PartbotVehicleFitment extends HTMLElement {
constructor() {
super();
this.initialize();
if (this.isValid()) {
this.processSelectedVehicle();
this.renderFitment();
}
}
initialize() {
this.vehicleIds = this.parseVehicleIds();
this.vehicleYears = this.parseVehicleYears();
this.vehicleDescription = this.querySelectorAll(".js-vehicle-description");
this.selectedVehicle = this.getSelectedVehicle();
}
isValid() {
return this.vehicleIds?.length > 0 && this.selectedVehicle;
}
parseVehicleIds() {
return this.dataset.vehicleIds?.split(",").filter(Boolean) ?? [];
}
parseVehicleYears() {
return JSON.parse(this.dataset.vehicleYears);
}
getSelectedVehicle() {
return JSON.parse(localStorage.getItem("partbot_selected_vehicle"));
}
processSelectedVehicle() {
this.selectedVehicleYear = this.selectedVehicle.vehicle.year;
this.selectedVehicleIds = this.selectedVehicle.vehicle_ids ?? [];
this.updateVehicleDescription();
this.fitsVehicle = this.isYearCovered(
this.selectedVehicleYear,
this.selectedVehicleIds,
);
}
updateVehicleDescription() {
this.vehicleDescription.forEach(
(el) => (el.textContent = this.selectedVehicle.description),
);
}
isYearCovered(year, vehicleIds) {
const { year_from: yearFrom, year_to: yearTo } = this.vehicleYears;
const yearRanges = this.preprocessYearRanges(yearFrom, yearTo);
return yearRanges.some(
([start, end, fromIds, toIds]) =>
year >= start &&
year <= end &&
vehicleIds.some((id) => fromIds.includes(id) || toIds.includes(id)),
);
}
preprocessYearRanges(yearFrom, yearTo) {
return Object.entries(yearFrom).flatMap(([start, fromIds]) =>
Object.entries(yearTo).map(([end, toIds]) => [
parseInt(start),
parseInt(end),
fromIds || [],
toIds || [],
]),
);
}
renderFitment() {
const container = document.createElement("div");
container.className = `rounded w-full p-2 text-xs flex gap-2 items-start justify-start ${
this.fitsVehicle ? "bg-emerald-100" : "bg-rose-100"
}`;
const badge = this.createBadge();
const text = this.createText();
container.append(badge, text);
this.appendChild(container);
this.classList.remove("hidden");
}
createBadge() {
const badge = document.createElement("span");
badge.className = `rounded px-2 py-0.5 flex-shrink-0 ${
this.fitsVehicle
? "bg-emerald-600 text-emerald-50"
: "bg-rose-600 text-rose-50"
}`;
badge.innerHTML = `Fitment <i class="far fa-${
this.fitsVehicle ? "check" : "times"
} ml-1"></i>`;
return badge;
}
createText() {
const text = document.createElement("span");
const description = document.createElement("span");
description.className = "js-vehicle-description font-semibold";
description.textContent = this.selectedVehicle.description;
text.append(
`This part ${this.fitsVehicle ? "fits" : "does not fit"} your vehicle: `,
description,
);
return text;
}
}
customElements.define("partbot-vehicle-fitment", PartbotVehicleFitment);