Skip to content

Commit bcbd7e5

Browse files
committed
seo and access optimisation
1 parent ecea1d2 commit bcbd7e5

File tree

10 files changed

+899
-320
lines changed

10 files changed

+899
-320
lines changed

view/frontend/layout/catalog_category_view.xml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,8 @@
88
<page xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
99
xsi:noNamespaceSchemaLocation="urn:magento:framework:View/Layout/etc/page_configuration.xsd">
1010
<body>
11+
<!-- Override product list template for category pages -->
12+
<referenceBlock name="category.products.list" template="React_React::product/list.phtml"/>
1113
<referenceBlock name="category.product.type.details.renderers">
1214
<block class="Magento\Swatches\Block\Product\Renderer\Listing\Configurable"
1315
name="category.product.type.details.renderers.configurable" as="configurable"

view/frontend/layout/catalog_product_view.xml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,5 +8,8 @@
88
<argument name="viewModel" xsi:type="object">Magento\Catalog\ViewModel\Product\Breadcrumbs</argument>
99
</arguments>
1010
</referenceBlock>
11+
<!-- Override product list items template for related/upsell/cross-sell products -->
12+
<referenceBlock name="catalog.product.related" template="React_React::product/list/items.phtml"/>
13+
<referenceBlock name="product.info.upsell" template="React_React::product/list/items.phtml"/>
1114
</body>
1215
</page>

view/frontend/layout/default.xml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,5 +44,9 @@
4444
<argument name="logo_height" xsi:type="string">50</argument>
4545
</arguments>
4646
</referenceBlock>
47+
<!-- Override wishlist sidebar template to remove Knockout.js -->
48+
<referenceBlock name="wishlist_sidebar" template="React_React::sidebar.phtml"/>
49+
<!-- Override compare sidebar template to remove Knockout.js -->
50+
<referenceBlock name="catalog.compare.sidebar" template="React_React::product/compare/sidebar.phtml"/>
4751
</body>
4852
</page>
Lines changed: 124 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,124 @@
1+
<?php
2+
/**
3+
* Copyright © Magento, Inc. All rights reserved.
4+
* See COPYING.txt for license details.
5+
*/
6+
7+
/** @var \Magento\Framework\View\Element\Template $block */
8+
/** @var \Magento\Framework\Escaper $escaper */
9+
?>
10+
<div class="block block-compare" data-role="compare-products-sidebar" id="compare-sidebar-container"></div>
11+
12+
<script>
13+
(function(){
14+
'use strict';
15+
16+
// Render compare sidebar from localStorage
17+
function renderCompareSidebar(){
18+
var container=document.getElementById('compare-sidebar-container');
19+
if(!container) return;
20+
21+
// Get compare products from localStorage
22+
var compareData=null;
23+
var items=[];
24+
try{
25+
var customerDataStr=localStorage.getItem('mage-cache-storage');
26+
if(customerDataStr){
27+
var customerData=JSON.parse(customerDataStr);
28+
compareData=customerData['compare-products'];
29+
if(compareData&&compareData.items){
30+
items=Array.isArray(compareData.items)?compareData.items:[];
31+
}
32+
}
33+
}catch(e){
34+
// Silently fail - no compare data available
35+
}
36+
37+
// If no items, show empty message
38+
if(items.length===0){
39+
container.innerHTML='<div class="block-title"><strong id="block-compare-heading" role="heading" aria-level="2">Compare Products</strong></div><div class="empty">You have no items to compare.</div>';
40+
return;
41+
}
42+
43+
var count=items.length;
44+
var listUrl=(compareData&&compareData.listUrl)?compareData.listUrl:'/catalog/product_compare/';
45+
var clearAllUrl='/catalog/product_compare/clear/';
46+
47+
// Escape functions
48+
function escapeHtml(text){
49+
var div=document.createElement('div');
50+
div.textContent=text;
51+
return div.innerHTML;
52+
}
53+
function escapeAttr(text){
54+
return String(text).replace(/"/g,'&quot;').replace(/'/g,'&#x27;');
55+
}
56+
57+
// Build HTML
58+
var html='<div class="block-title">';
59+
html+='<strong id="block-compare-heading" role="heading" aria-level="2">Compare Products</strong>';
60+
html+='<span class="counter qty">'+count+'</span>';
61+
html+='</div>';
62+
html+='<div class="block-content" aria-labelledby="block-compare-heading">';
63+
html+='<ol id="compare-items" class="product-items product-items-names">';
64+
65+
items.forEach(function(item){
66+
var productId=item.id||item.product_id||'';
67+
var productUrl=item.product_url||'/catalog/product/view/id/'+productId;
68+
var productName=item.name||'Product';
69+
var removeUrl=item.remove_url||'#';
70+
71+
productName=escapeHtml(productName);
72+
productUrl=escapeAttr(productUrl);
73+
productId=escapeAttr(productId);
74+
removeUrl=escapeAttr(removeUrl);
75+
76+
html+='<li class="product-item">';
77+
html+='<input type="hidden" class="compare-item-id" value="'+productId+'"/>';
78+
html+='<strong class="product-item-name">';
79+
html+='<a href="'+productUrl+'" class="product-item-link" rel="nofollow noindex" data-noindex="true">';
80+
html+=productName;
81+
html+='</a></strong>';
82+
html+='<a href="#" data-post="'+removeUrl+'" title="Remove This Item" class="action delete" rel="nofollow noindex" data-noindex="true">';
83+
html+='<span>Remove This Item</span></a>';
84+
html+='</li>';
85+
});
86+
87+
html+='</ol>';
88+
html+='<div class="actions-toolbar">';
89+
html+='<div class="primary">';
90+
html+='<a href="'+listUrl+'" class="action compare primary" rel="nofollow noindex" data-noindex="true">';
91+
html+='<span>Compare</span></a></div>';
92+
html+='<div class="secondary">';
93+
var clearAllPost=JSON.stringify({"action":clearAllUrl,"data":{"uenc":"","confirmation":true,"confirmationMessage":"Are you sure you want to remove all items from your Compare Products list?"}});
94+
html+='<a id="compare-clear-all" href="#" class="action clear" data-post=\''+clearAllPost.replace(/'/g,'&#x27;')+'\' rel="nofollow noindex" data-noindex="true">';
95+
html+='<span>Clear All</span></a></div></div></div>';
96+
97+
container.innerHTML=html;
98+
}
99+
100+
// Run on DOM ready
101+
if(document.readyState==='loading'){
102+
document.addEventListener('DOMContentLoaded',renderCompareSidebar);
103+
}else{
104+
renderCompareSidebar();
105+
}
106+
107+
// Listen for localStorage changes (cross-tab)
108+
window.addEventListener('storage',function(e){
109+
if(e.key==='mage-cache-storage'){
110+
renderCompareSidebar();
111+
}
112+
});
113+
114+
// Check periodically for changes (same-tab updates)
115+
var lastCheck=localStorage.getItem('mage-cache-storage');
116+
setInterval(function(){
117+
var current=localStorage.getItem('mage-cache-storage');
118+
if(current!==lastCheck){
119+
lastCheck=current;
120+
renderCompareSidebar();
121+
}
122+
},1000);
123+
})();
124+
</script>
Lines changed: 173 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,173 @@
1+
<?php
2+
/**
3+
* Copyright © Magento, Inc. All rights reserved.
4+
* See COPYING.txt for license details.
5+
*/
6+
use Magento\Framework\App\Action\Action;
7+
8+
?>
9+
<?php
10+
/**
11+
* Product list template
12+
*
13+
* @var $block \Magento\Catalog\Block\Product\ListProduct
14+
* @var \Magento\Framework\Escaper $escaper
15+
* @var \Magento\Framework\View\Helper\SecureHtmlRenderer $secureRenderer
16+
*/
17+
?>
18+
<?php
19+
$_productCollection = $block->getLoadedProductCollection();
20+
/** @var \Magento\Catalog\Helper\Output $_helper */
21+
$_helper = $block->getData('outputHelper');
22+
?>
23+
<?php if (!$_productCollection->count()): ?>
24+
<div class="message info empty">
25+
<div><?= $escaper->escapeHtml(__('We can\'t find products matching the selection.')) ?></div>
26+
</div>
27+
<?php else: ?>
28+
<?= $block->getToolbarHtml() ?>
29+
<?= $block->getAdditionalHtml() ?>
30+
<?php
31+
if ($block->getMode() === 'grid') {
32+
$viewMode = 'grid';
33+
$imageDisplayArea = 'category_page_grid';
34+
$showDescription = false;
35+
$templateType = \Magento\Catalog\Block\Product\ReviewRendererInterface::SHORT_VIEW;
36+
} else {
37+
$viewMode = 'list';
38+
$imageDisplayArea = 'category_page_list';
39+
$showDescription = true;
40+
$templateType = \Magento\Catalog\Block\Product\ReviewRendererInterface::FULL_VIEW;
41+
}
42+
/**
43+
* Position for actions regarding image size changing in vde if needed
44+
*/
45+
$pos = $block->getPositioned();
46+
?>
47+
<div class="products wrapper <?= /* @noEscape */ $viewMode ?> products-<?= /* @noEscape */ $viewMode ?>"
48+
data-template-override="React_React::product/list.phtml">
49+
<ol class="products list items product-items">
50+
<?php /** @var $_product \Magento\Catalog\Model\Product */ ?>
51+
<?php foreach ($_productCollection as $_product): ?>
52+
<?php
53+
// Ensure product URL is always valid and crawlable
54+
$productUrl = $_product->getProductUrl();
55+
if (empty($productUrl) || $productUrl === '#') {
56+
$productUrl = $block->getUrl('catalog/product/view', ['id' => $_product->getId()]);
57+
}
58+
?>
59+
<li class="item product product-item">
60+
<div class="product-item-info"
61+
id="product-item-info_<?= /* @noEscape */ $_product->getId() ?>"
62+
data-container="product-<?= /* @noEscape */ $viewMode ?>">
63+
<?php
64+
$productImage = $block->getImage($_product, $imageDisplayArea);
65+
if ($pos != null) {
66+
$position = 'left:' . $productImage->getWidth() . 'px;'
67+
. 'top:' . $productImage->getHeight() . 'px;';
68+
}
69+
?>
70+
<?php // Product Image ?>
71+
<a href="<?= $escaper->escapeUrl($productUrl) ?>"
72+
class="product photo product-item-photo"
73+
tabindex="-1">
74+
<?= $productImage->toHtml() ?>
75+
</a>
76+
<div class="product details product-item-details">
77+
<?php $_productNameStripped = $block->stripTags($_product->getName(), null, true); ?>
78+
<strong class="product name product-item-name">
79+
<a class="product-item-link"
80+
href="<?= $escaper->escapeUrl($productUrl) ?>">
81+
<?=/* @noEscape */ $_helper->productAttribute($_product, $_product->getName(), 'name')?>
82+
</a>
83+
</strong>
84+
<?= $block->getReviewsSummaryHtml($_product, $templateType) ?>
85+
<?= /* @noEscape */ $block->getProductPrice($_product) ?>
86+
87+
<?= $block->getProductDetailsHtml($_product) ?>
88+
89+
<div class="product-item-inner">
90+
<div class="product actions product-item-actions">
91+
<div class="actions-primary">
92+
<?php if ($_product->isSaleable()):?>
93+
<?php $postParams = $block->getAddToCartPostParams($_product); ?>
94+
<form data-role="tocart-form"
95+
data-product-sku="<?= $escaper->escapeHtml($_product->getSku()) ?>"
96+
action="<?= $escaper->escapeUrl($postParams['action']) ?>"
97+
data-mage-init='{"catalogAddToCart": {}}'
98+
method="post">
99+
<?php $options = $block->getData('viewModel')->getOptionsData($_product); ?>
100+
<?php foreach ($options as $optionItem): ?>
101+
<input type="hidden"
102+
name="<?= $escaper->escapeHtml($optionItem['name']) ?>"
103+
value="<?= $escaper->escapeHtml($optionItem['value']) ?>">
104+
<?php endforeach; ?>
105+
<input type="hidden"
106+
name="product"
107+
value="<?= /* @noEscape */ $postParams['data']['product'] ?>">
108+
<input type="hidden"
109+
name="<?= /* @noEscape */ Action::PARAM_NAME_URL_ENCODED ?>"
110+
value="<?=
111+
/* @noEscape */ $postParams['data'][Action::PARAM_NAME_URL_ENCODED]
112+
?>">
113+
<?= $block->getBlockHtml('formkey') ?>
114+
<button type="submit"
115+
title="<?= $escaper->escapeHtmlAttr(__('Add to Cart')) ?>"
116+
class="action tocart primary"
117+
disabled>
118+
<span><?= $escaper->escapeHtml(__('Add to Cart')) ?></span>
119+
</button>
120+
</form>
121+
<?php else:?>
122+
<?php if ($_product->isAvailable()):?>
123+
<div class="stock available">
124+
<span><?= $escaper->escapeHtml(__('In stock')) ?></span></div>
125+
<?php else:?>
126+
<div class="stock unavailable">
127+
<span><?= $escaper->escapeHtml(__('Out of stock')) ?></span></div>
128+
<?php endif; ?>
129+
<?php endif; ?>
130+
</div>
131+
<?= ($pos && strpos($pos, $viewMode . '-primary')) ?
132+
/* @noEscape */ $secureRenderer->renderStyleAsTag(
133+
$position,
134+
'product-item-info_' . $_product->getId() . ' div.actions-primary'
135+
) : '' ?>
136+
<div data-role="add-to-links" class="actions-secondary">
137+
<?php if ($addToBlock = $block->getChildBlock('addto')): ?>
138+
<?= $addToBlock->setProduct($_product)->getChildHtml() ?>
139+
<?php endif; ?>
140+
</div>
141+
<?= ($pos && strpos($pos, $viewMode . '-secondary')) ?
142+
/* @noEscape */ $secureRenderer->renderStyleAsTag(
143+
$position,
144+
'product-item-info_' . $_product->getId() . ' div.actions-secondary'
145+
) : '' ?>
146+
</div>
147+
<?php if ($showDescription): ?>
148+
<div class="product description product-item-description">
149+
<?= /* @noEscape */ $_helper->productAttribute(
150+
$_product,
151+
$_product->getShortDescription(),
152+
'short_description'
153+
) ?>
154+
<a href="<?= $escaper->escapeUrl($productUrl) ?>"
155+
title="<?= /* @noEscape */ $_productNameStripped ?>"
156+
class="action more"><?= $escaper->escapeHtml(__('Learn More')) ?></a>
157+
</div>
158+
<?php endif; ?>
159+
</div>
160+
</div>
161+
</div>
162+
<?= ($pos && strpos($pos, $viewMode . '-actions')) ?
163+
/* @noEscape */ $secureRenderer->renderStyleAsTag(
164+
$position,
165+
'product-item-info_' . $_product->getId() . ' div.product-item-actions'
166+
) : '' ?>
167+
</li>
168+
<?php endforeach; ?>
169+
</ol>
170+
</div>
171+
<?= $block->getChildBlock('toolbar')->setIsBottom(true)->toHtml() ?>
172+
<?php // phpcs:ignore Magento2.Legacy.PhtmlTemplate ?>
173+
<?php endif; ?>

0 commit comments

Comments
 (0)