| @@ -7,8 +7,9 @@ | ||
| 7 | 7 | use FluentCart\App\Helpers\Helper; |
| 8 | 8 | use FluentCart\App\Http\Routes\WebRoutes; |
| 9 | 9 | use FluentCart\App\Modules\Templating\AssetLoader; |
| 10 | 10 | use FluentCart\Framework\Pagination\CursorPaginator; |
| 11 | +use FluentCart\Framework\Pagination\LengthAwarePaginator; | |
| 11 | 12 | use FluentCart\Framework\Support\Arr; |
| 12 | 13 | |
| 13 | 14 | class ShopAppRenderer |
| 14 | 15 | { |
| @@ -42,8 +43,10 @@ | ||
| 42 | 43 | protected $total = 0; |
| 43 | 44 | |
| 44 | 45 | protected $isWildcardFilterEnabled = false; |
| 45 | 46 | |
| 47 | + protected $enableSortBy = true; | |
| 48 | + | |
| 46 | 49 | public function __construct($products = [], $config = []) |
| 47 | 50 | { |
| 48 | 51 | |
| 49 | 52 | $defaultFilters = Arr::get($config, 'default_filters', []); |
| @@ -76,8 +79,9 @@ | ||
| 76 | 79 | $this->paginator = 'scroll'; |
| 77 | 80 | } |
| 78 | 81 | $this->productBoxGridSize = $config['product_box_grid_size'] ?? 4; |
| 79 | 82 | $this->isWildcardFilterEnabled = Arr::get($config, 'enable_wildcard_filter', false); |
| 83 | + $this->enableSortBy = Arr::get($config, 'enable_sort_by', true); | |
| 80 | 84 | |
| 81 | 85 | if (Arr::get($products, 'products', [])) { |
| 82 | 86 | $this->products = Arr::get($products, 'products', []); |
| 83 | 87 | } else { |
| @@ -87,8 +91,15 @@ | ||
| 87 | 91 | if($this->products instanceof CursorPaginator){ |
| 88 | 92 | $this->total = 0; |
| 89 | 93 | }else{ |
| 90 | 94 | $this->total = Arr::get($products, 'total', 0); |
| 95 | + if (!$this->total && $this->products instanceof LengthAwarePaginator) { | |
| 96 | + // A raw LengthAwarePaginator's ArrayAccess proxies to its item | |
| 97 | + // collection, not its own properties, so Arr::get($products, 'total') | |
| 98 | + // above always misses when the caller passes the paginator directly | |
| 99 | + // instead of the normalized ['products' => ..., 'total' => ...] shape. | |
| 100 | + $this->total = $this->products->total(); | |
| 101 | + } | |
| 91 | 102 | } |
| 92 | 103 | //$this->total = $products['total']; |
| 93 | 104 | |
| 94 | 105 | |
| @@ -119,8 +130,17 @@ | ||
| 119 | 130 | if (Arr::get($config, 'allow_out_of_stock')) { |
| 120 | 131 | $this->defaultFilters['allow_out_of_stock'] = true; |
| 121 | 132 | } |
| 122 | 133 | |
| 134 | + // Merge wildcard search preset into defaultFilters so it persists in AJAX re-fetches | |
| 135 | + $wildcard = Arr::get($defaultFilters, 'wildcard', ''); | |
| 136 | + if ($wildcard !== '') { | |
| 137 | + $this->defaultFilters['wildcard'] = $wildcard; | |
| 138 | + if (empty($this->defaultFilters['enabled'])) { | |
| 139 | + $this->defaultFilters['enabled'] = true; | |
| 140 | + } | |
| 141 | + } | |
| 142 | + | |
| 123 | 143 | // Merge sort_by from config filters into defaultFilters for AJAX persistence |
| 124 | 144 | $configFilters = Arr::get($config, 'filters', []); |
| 125 | 145 | if (is_string($configFilters)) { |
| 126 | 146 | $configFilters = json_decode($configFilters, true) ?: []; |
| @@ -132,13 +152,19 @@ | ||
| 132 | 152 | $this->defaultFilters['enabled'] = true; |
| 133 | 153 | } |
| 134 | 154 | } |
| 135 | 155 | |
| 136 | - if (Arr::get($this->customFilters, 'price_range', false)) { | |
| 156 | + $priceRange = Arr::get($this->customFilters, 'price_range', false); | |
| 157 | + if ($priceRange) { | |
| 158 | + // Accepts true (default "Price" label) or ['label' => '...'] so | |
| 159 | + // integrations can name the price filter the same way the | |
| 160 | + // taxonomies config names taxonomy filters. | |
| 137 | 161 | $this->filters['price_range'] = [ |
| 138 | 162 | "filter_type" => "range", |
| 139 | 163 | "is_meta" => false, |
| 140 | - "label" => "Price", | |
| 164 | + "label" => is_array($priceRange) && !empty($priceRange['label']) | |
| 165 | + ? $priceRange['label'] | |
| 166 | + : "Price", | |
| 141 | 167 | "enabled" => true, |
| 142 | 168 | ]; |
| 143 | 169 | } |
| 144 | 170 | |
| @@ -237,8 +263,13 @@ | ||
| 237 | 263 | } |
| 238 | 264 | if ($onSale) { |
| 239 | 265 | $wrapperAttributes['data-on-sale'] = '1'; |
| 240 | 266 | } |
| 267 | + // Persist hide_excerpt so AJAX pagination/filtering renders cards the | |
| 268 | + // same way as the initial response (Paginator.js round-trips it). | |
| 269 | + if (Arr::get($this->config, 'hide_excerpt', false)) { | |
| 270 | + $wrapperAttributes['data-hide-excerpt'] = '1'; | |
| 271 | + } | |
| 241 | 272 | ?> |
| 242 | 273 | <div class="fct-products-wrapper" data-fluent-cart-shop-app data-fluent-cart-product-wrapper role="main" aria-label="<?php esc_attr_e('Products', 'fluent-cart'); ?>"> |
| 243 | 274 | <?php $this->renderViewSwitcher(); ?> |
| 244 | 275 | <div <?php RenderHelper::renderAtts($wrapperAttributes); ?>> |
| @@ -275,9 +306,9 @@ | ||
| 275 | 306 | ?> |
| 276 | 307 | <div class="fct-shop-view-switcher-wrap"> |
| 277 | 308 | <?php $this->renderViewSwitcherButton(); ?> |
| 278 | 309 | <?php |
| 279 | - if ($this->isFilterEnabled) { | |
| 310 | + if ($this->isFilterEnabled && $this->enableSortBy) { | |
| 280 | 311 | $this->renderSortByFilter(); |
| 281 | 312 | } |
| 282 | 313 | ?> |
| 283 | 314 | </div> |
| @@ -520,9 +551,9 @@ | ||
| 520 | 551 | if ($index === 0) { |
| 521 | 552 | $cursorAttr = Arr::get($cursor, 'cursor', ''); |
| 522 | 553 | } |
| 523 | 554 | |
| 524 | - (new \FluentCart\App\Services\Renderer\ProductCardRender($product, ['cursor' => $cursorAttr]))->render(); | |
| 555 | + (new \FluentCart\App\Services\Renderer\ProductCardRender($product, ['cursor' => $cursorAttr, 'hide_excerpt' => Arr::get($this->config, 'hide_excerpt', false)]))->render(); | |
| 525 | 556 | ?> |
| 526 | 557 | <?php } ?> |
| 527 | 558 | <?php |
| 528 | 559 | } |