7 Commits

Author SHA1 Message Date
a128906dc0 fix(falcon-PS9): prevent 500 error on AJAX filtering by normalizing data
Resolved a Fatal Error occurring during AJAX product listing requests (e.g., price slider).

- Issue: In PHP 8.2+, accessing properties of a 'CategoryLazyArray' object
  using array syntax (dot notation) triggers a Fatal Error.
- Cause: The Faceted Search module returns a LazyArray object during
  XHR requests, whereas standard page loads provide a native array.
- Solution: Implemented a normalization block using JSON serialization
  to flatten the object into a standard associative array. This ensures
  template syntax compatibility regardless of the request type or
  property visibility (protected vs public).
2026-01-09 10:52:21 +01:00
ddec414409 fix(falcon-PS9): Auto-format postcode
This removes the annoying error when you type in 8011XD instead of 8011 XD
2026-01-09 10:17:29 +01:00
48e776d80d feat(falcon-PS9): Add custom.scss file to follow 7-in-1 structure 2025-12-31 16:19:02 +01:00
abd2cdc145 fix(falcon-PS9): Removed nonexistent product-miniature-form import
Also added css folder locations
2025-11-20 14:35:36 +01:00
92a8db6e72 Merge branch 'feature/falcon-PS9' 2025-11-20 09:10:47 +01:00
44666297b3 fix: Improved active class added to account
Found a bug where when you click on "details"button in order history, all the customer links become active. This fixes that.
2025-11-19 17:29:32 +01:00
ae299283a7 Fix: Replace deprecated getBrightness with new method
New method created in is_themecore module: Uses use PrestaShop\PrestaShop\Core\Util\ColorBrightnessCalculator and adds as smarty function in SmartyHelperFunctions.php
2025-11-19 17:14:56 +01:00
10 changed files with 127 additions and 52 deletions

View File

@ -0,0 +1,19 @@
// NOTE: All bootstrap overrides have been configured under the abstracts/variables/bootstrap folder.
//Abstracts: Things used throughout the site such as utility classes and generic overrides.
//@import "custom/abstracts/base";
//@import "custom/abstracts/utilities";
// Components: parts of the theme itself that are not associated with a module.
//@import "custom/components/";
// Modules: Styling for specific modules.
//@import "custom/modules/";
//Layouts: Parts of a page, such as the header, footer, etc.
//@import "custom/layout/header";
//@import "custom/layout/footer";
// Pages
//@import "custom/pages/category";
//@import "custom/pages/product";

View File

@ -1,2 +1,3 @@
@import "abstracts/index";
@import "theme/index";
@import "custom";

View File

@ -5,3 +5,4 @@
@import "layout/index";
@import "components/index";
@import "custom/custom";

View File

@ -0,0 +1,22 @@
/* This is the main custom SCSS file for the Falcon theme.
I am loosely following the 7 in 1 structure for better organization of the styles. Learn more here:
https://medium.com/@diyorbekjuraev77/be-a-master-at-creating-the-7-1-sass-pattern-776fdfb5a3b1
NOTE: All bootstrap overrides have been configured under themes/falcon/_dev/css/abstracts/variables/bootstrap */
//Abstracts: Things used throughout the site such as utility classes and generic overrides.
//@import "abstracts/mixins";
// Components: parts of the theme itself that are not associated with a module.
//@import "components/buttons";
// Modules: Styling for specific modules.
//@import "modules/";
//Layouts: Parts of a page, such as the header, footer, etc.
//@import "layout/footer";
//@import "layout/header";
// Pages
//@import "pages/home";

View File

@ -1,73 +1,97 @@
import $ from 'jquery';
import '@js/theme/vendors/bootstrap/bootstrap-imports';
import 'bootstrap-touchspin';
import 'jquery-hoverintent';
import '@js/theme/components/dynamic-bootstrap-components';
import bsCustomFileInput from 'bs-custom-file-input';
import $ from "jquery";
import "@js/theme/vendors/bootstrap/bootstrap-imports";
import "bootstrap-touchspin";
import "jquery-hoverintent";
import "@js/theme/components/dynamic-bootstrap-components";
import bsCustomFileInput from "bs-custom-file-input";
import '@js/theme/components/selectors';
import '@js/theme/components/sliders';
import '@js/theme/components/responsive';
import '@js/theme/components/customer';
import '@js/theme/components/quickview';
import '@js/theme/components/product';
import '@js/theme/components/cart/cart';
import '@js/theme/components/cart/block-cart';
import "@js/theme/components/selectors";
import "@js/theme/components/sliders";
import "@js/theme/components/responsive";
import "@js/theme/components/customer";
import "@js/theme/components/quickview";
import "@js/theme/components/product";
import "@js/theme/components/cart/cart";
import "@js/theme/components/cart/block-cart";
import usePasswordPolicy from '@js/theme/components/usePasswordPolicy';
import prestashop from 'prestashop';
import EventEmitter from 'events';
import Form from '@js/theme/components/form';
import TopMenu from '@js/theme/components/TopMenu';
import usePasswordPolicy from "@js/theme/components/usePasswordPolicy";
import prestashop from "prestashop";
import EventEmitter from "events";
import Form from "@js/theme/components/form";
import TopMenu from "@js/theme/components/TopMenu";
import PageLazyLoad from '@js/theme/components/Lazyload';
import PageLoader from '@js/theme/components/PageLoader';
import useStickyElement from '@js/theme/components/useStickyElement';
import PageLazyLoad from "@js/theme/components/Lazyload";
import PageLoader from "@js/theme/components/PageLoader";
import useStickyElement from "@js/theme/components/useStickyElement";
/* eslint-disable */
// "inherit" EventEmitter
for (const i in EventEmitter.prototype) {
prestashop[i] = EventEmitter.prototype[i];
prestashop[i] = EventEmitter.prototype[i];
}
/* eslint-enable */
prestashop.pageLazyLoad = new PageLazyLoad({
selector: '.lazyload',
selector: ".lazyload",
});
prestashop.pageLoader = new PageLoader();
function accLinksTriggerActive() {
const url = window.location.pathname;
$('.js-customer-links a').each((i, el) => {
const $el = $(el);
const currentUrl = window.location.pathname + window.location.search;
if ($el.attr('href').indexOf(url) !== -1) {
$el.addClass('active');
}
});
$(".js-customer-links a").each((i, el) => {
const $el = $(el);
const linkHref = $el.attr("href");
let linkPath = linkHref;
try {
const linkUrl = new URL(linkHref, window.location.origin);
linkPath = linkUrl.pathname + linkUrl.search;
} catch (e) {}
if (
currentUrl === linkPath ||
(linkPath !== "/" && currentUrl.startsWith(linkPath))
) {
$el.addClass("active");
}
});
}
function initStickyHeader() {
const header = document.querySelector('.js-header-top');
const headerWrapper = document.querySelector('.js-header-top-wrapper');
const header = document.querySelector(".js-header-top");
const headerWrapper = document.querySelector(".js-header-top-wrapper");
if (header && headerWrapper) {
useStickyElement(header, headerWrapper);
}
if (header && headerWrapper) {
useStickyElement(header, headerWrapper);
}
}
$(() => {
initStickyHeader();
accLinksTriggerActive();
Form.init();
bsCustomFileInput.init();
const topMenu = new TopMenu('#_desktop_top_menu .js-main-menu');
usePasswordPolicy('.field-password-policy');
initStickyHeader();
accLinksTriggerActive();
Form.init();
bsCustomFileInput.init();
const topMenu = new TopMenu("#_desktop_top_menu .js-main-menu");
usePasswordPolicy(".field-password-policy");
topMenu.init();
topMenu.init();
$('.js-select-link').on('change', ({ target }) => {
window.location.href = $(target).val();
});
$(".js-select-link").on("change", ({ target }) => {
window.location.href = $(target).val();
});
// Postcode input formatting
const $postCodeInput = $("input[name='postcode']");
$postCodeInput.on("input", function () {
let value = $(this).val();
// Match 4 digits followed by 2 letters (e.g., 1234AB)
const match = value.match(/^(\d{4})([a-zA-Z]{2})$/);
if (match) {
// Add space between numbers and letters
const formatted = `${match[1]} ${match[2]}`;
$(this).val(formatted);
}
});
});

View File

@ -75,7 +75,7 @@
type="{if $facet.multipleSelectionAllowed}checkbox{else}radio{/if}" class="custom-control-input"
{if $filter.active } checked{/if}>
<label for="facet_input_{$_expand_id}_{$filter_key}" {if isset($filter.properties.color)}
class="custom-control-label custom-control-label-{if Tools::getBrightness($filter.properties.color) > 128}dark{else}bright{/if}"
class="custom-control-label custom-control-label-{if $filter.properties.color|is_bright}dark{else}bright{/if}"
{else} class="custom-control-label"
{/if}>
{if isset($filter.properties.color)}

View File

@ -1,5 +1,13 @@
{* Defensive: Handles LazyArray objects for category *}
{if is_object($category)}
{$category = $category|json_encode|json_decode:true}
{if isset($category.category)}
{$category = $category.category}
{/if}
{/if}
<div id="js-product-list-footer">
{if $category.additional_description && $listing.pagination.items_shown_from == 1}
{if isset($category.additional_description) && $category.additional_description && $listing.pagination.items_shown_from == 1}
<div id="category-description-2" class="cms-content my-3">
{$category.additional_description nofilter}
</div>

View File

@ -45,7 +45,7 @@
<div class="custom-control custom-radio-color">
<input class="custom-control-input" type="radio" data-product-attribute="{$id_attribute_group}" id="{$id_attribute_group}_{$id_attribute}" name="group[{$id_attribute_group}]" value="{$id_attribute}" title="{$group_attribute.name}"{if $group_attribute.selected} checked="checked"{/if}>
<label class="custom-control-label {if $group_attribute.html_color_code}custom-control-label-{if Tools::getBrightness($group_attribute.html_color_code) > 128}dark{else}bright{/if}{/if}" for="{$id_attribute_group}_{$id_attribute}" aria-label="{$group_attribute.name}">
<label class="custom-control-label {if $group_attribute.html_color_code}custom-control-label-{if $group_attribute.html_color_code|is_bright}dark{else}bright{/if}{/if}" for="{$id_attribute_group}_{$id_attribute}" aria-label="{$group_attribute.name}">
<span
{if $group_attribute.texture}
class="custom-control-input-color" style="background-image: url({$group_attribute.texture})"

View File

@ -60,7 +60,7 @@
<td class="hidden-md-down align-middle">{$order.details.payment}</td>
<td class="align-middle">
<span
class="label label-pill badge {if Tools::getBrightness($order.history.current.color) < 128}text-white{/if}"
class="label label-pill badge {if !$order.history.current.color|is_bright}text-white{/if}"
style="background-color:{$order.history.current.color}"
>
{$order.history.current.ostate_name}

View File

@ -89,7 +89,7 @@
<td>{$state.history_date}</td>
<td>
<span
class="label label-pill badge {if Tools::getBrightness($state.color) < 128}text-white{/if}"
class="label label-pill badge {if !$state.color|is_bright}text-white{/if}"
style="background-color:{$state.color}">
{$state.ostate_name}
</span>