first commit
This commit is contained in:
88
views/templates/admin/uploader.tpl
Normal file
88
views/templates/admin/uploader.tpl
Normal file
@@ -0,0 +1,88 @@
|
||||
{**
|
||||
This script block passes the unique, secure AJAX URL from the PHP controller to our JavaScript.
|
||||
The 'javascript' escaper is crucial to prevent encoding issues.
|
||||
**}
|
||||
<script type="text/javascript">
|
||||
var addLivePhotoAjaxUrl = '{$ajax_url|escape:'javascript':'UTF-8'}';
|
||||
</script>
|
||||
|
||||
<div class="panel">
|
||||
<div class="panel-heading">
|
||||
<i class="icon-camera"></i> {l s='Live Photo Uploader' d='Modules.Addlivephoto.Admin'}
|
||||
</div>
|
||||
|
||||
<div class="container-fluid">
|
||||
<div class="row">
|
||||
<div class="col-lg-8 col-lg-offset-2">
|
||||
|
||||
{* --- The New Unified Camera Interface --- *}
|
||||
<div id="alp-camera-view" class="my-3">
|
||||
<div id="alp-video-container" class="video-container">
|
||||
{* The video feed will be attached here by JavaScript *}
|
||||
<video id="alp-video" autoplay playsinline muted></video>
|
||||
|
||||
{* This overlay displays instructions and is the main tap target *}
|
||||
<div id="alp-viewfinder-overlay">
|
||||
<div id="alp-overlay-text"></div>
|
||||
</div>
|
||||
|
||||
{* This canvas is used for capturing the frame but is not visible *}
|
||||
<canvas id="alp-canvas" style="display: none;"></canvas>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{* --- Message Area (for non-critical feedback) --- *}
|
||||
<div id="alp-message-area" class="alert" style="display: none; text-align: center;"></div>
|
||||
|
||||
{* --- Product Information (hidden by default) --- *}
|
||||
<div id="alp-product-info" style="display: none;" class="card mt-4">
|
||||
<div class="card-header bg-success text-white">
|
||||
<h5 class="card-title mb-0">{l s='Product Found' d='Modules.Addlivephoto.Admin'}</h5>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<p><strong>{l s='Name:' d='Modules.Addlivephoto.Admin'}</strong> <span id="alp-product-name"></span></p>
|
||||
<p><strong>{l s='Prices:' d='Modules.Addlivephoto.Admin'}</strong> <span id="alp-product-prices"></span></p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{* --- Existing Photos (hidden by default) --- *}
|
||||
<div id="alp-existing-photos" style="display: none;" class="card mt-4">
|
||||
<div class="card-header">
|
||||
<h5 class="card-title mb-0">{l s='Existing Live Photos' d='Modules.Addlivephoto.Admin'}</h5>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<div id="alp-photos-container" class="d-flex flex-wrap gap-2">
|
||||
{* JavaScript will populate this area *}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{* --- Settings Section (at the bottom, out of the way) --- *}
|
||||
<div class="card mt-4">
|
||||
<div class="card-header">{l s='Camera Settings' d='Modules.Addlivephoto.Admin'}</div>
|
||||
<div class="card-body">
|
||||
<div class="form-group">
|
||||
<label for="alp-camera-selector" class="control-label">{l s='Select Camera:' d='Modules.Addlivephoto.Admin'}</label>
|
||||
<select id="alp-camera-selector" class="form-control"></select>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{* --- Manual Input Section (remains as a fallback) --- *}
|
||||
<div id="alp-manual-input" class="card mt-3">
|
||||
<div class="card-header">{l s='Or Enter Manually' d='Modules.Addlivephoto.Admin'}</div>
|
||||
<div class="card-body">
|
||||
<form id="alp-manual-form" class="form-inline">
|
||||
<div class="form-group">
|
||||
<label for="alp-manual-identifier" class="mr-2">{l s='Product ID or EAN13 Barcode:' d='Modules.Addlivephoto.Admin'}</label>
|
||||
<input type="text" id="alp-manual-identifier" class="form-control mr-2" placeholder="e.g., 4006381333931">
|
||||
</div>
|
||||
<button type="submit" class="btn btn-default"><i class="icon-search"></i> {l s='Find Product' d='Modules.Addlivephoto.Admin'}</button>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
228
views/templates/hook/displayProductPriceBlock.tpl
Normal file
228
views/templates/hook/displayProductPriceBlock.tpl
Normal file
@@ -0,0 +1,228 @@
|
||||
{*
|
||||
* 2007-2023 PrestaShop
|
||||
*
|
||||
* NOTICE OF LICENSE
|
||||
*
|
||||
* This source file is subject to the Academic Free License (AFL 3.0)
|
||||
* that is bundled with this package in the file LICENSE.txt.
|
||||
* It is also available through the world-wide-web at this URL:
|
||||
* http://opensource.org/licenses/afl-3.0.php
|
||||
* If you did not receive a copy of the license and are unable to
|
||||
* obtain it through the world-wide-web, please send an email
|
||||
* to license@prestashop.com so we can send you a copy immediately.
|
||||
*
|
||||
* DISCLAIMER
|
||||
*
|
||||
* Do not edit or add to this file if you wish to upgrade PrestaShop to newer
|
||||
* versions in the future. If you wish to customize PrestaShop for your
|
||||
* needs please refer to http://www.prestashop.com for more information.
|
||||
*
|
||||
* @author Your Name <your@email.com>
|
||||
* @copyright 2007-2023 PrestaShop SA
|
||||
* @license http://opensource.org/licenses/afl-3.0.php Academic Free License (AFL 3.0)
|
||||
* International Registered Trademark & Property of PrestaShop SA
|
||||
*}
|
||||
|
||||
{if isset($live_photos) && !empty($live_photos)}
|
||||
<div id="addlivephoto-container" class="mt-3">
|
||||
<h6 class="h6">{l s='Freshness Guaranteed: See Today\'s Stock' d='Modules.Addlivephoto.Shop'}</h6>
|
||||
|
||||
<div class="d-flex flex-wrap gap-2">
|
||||
{foreach from=$live_photos item=photo name=livephotoloop}
|
||||
<a href="{$photo.url|escape:'htmlall':'UTF-8'}" class="live-photo-thumb" data-bs-toggle="modal"
|
||||
data-bs-target="#livePhotoModal" data-photo-index="{$smarty.foreach.livephotoloop.index}"
|
||||
title="{$photo.title|escape:'htmlall':'UTF-8'}">
|
||||
<img src="{$photo.url|escape:'htmlall':'UTF-8'}" alt="{$photo.alt|escape:'htmlall':'UTF-8'}" class="img-thumbnail"
|
||||
width="80" height="80" loading="lazy">
|
||||
</a>
|
||||
{/foreach}
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
{* --- MODAL --- *}
|
||||
<div class="modal fade" id="livePhotoModal" tabindex="-1" aria-labelledby="livePhotoModalLabel" aria-hidden="true">
|
||||
<div class="modal-dialog modal-lg modal-dialog-centered">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<h5 class="modal-title" id="livePhotoModalLabel">{l s='Live Product Photo' d='Modules.Addlivephoto.Shop'}</h5>
|
||||
<button type="button" class="btn-close" data-bs-dismiss="modal"
|
||||
aria-label="{l s='Close' d='Shop.Theme.Actions'}"></button>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<div class="position-relative">
|
||||
<img id="livePhotoModalImage" src="" alt="" class="img-fluid w-100">
|
||||
<button id="livePhotoPrevBtn" class="btn btn-light modal-nav-btn prev"><</button>
|
||||
<button id="livePhotoNextBtn" class="btn btn-light modal-nav-btn next">></button>
|
||||
</div>
|
||||
<div class="text-muted text-wrap small mb-0">
|
||||
{l s='Please Note: This is a live photo of a randomly selected package from our current stock to show its freshness. The expiry date on the product you receive will be the same or newer, but the lot number may differ.' d='Modules.Addlivephoto.Shop'}
|
||||
</div>
|
||||
</div>
|
||||
<div class="modal-footer justify-content-start">
|
||||
|
||||
{* This caption is visible to the user and good for accessibility *}
|
||||
<p id="livePhotoModalCaption" class="text-muted text-wrap small mb-0"></p>
|
||||
|
||||
{*
|
||||
This hidden block provides rich metadata for SEO and AI crawlers (e.g., Google Images).
|
||||
It uses schema.org microdata to describe the image.
|
||||
*}
|
||||
<div class="visually-hidden" itemprop="image" itemscope itemtype="https://schema.org/ImageObject">
|
||||
<meta itemprop="contentUrl" id="livePhotoMetaUrl" content="">
|
||||
<meta itemprop="description" id="livePhotoMetaDesc" content="">
|
||||
<span itemprop="author" itemscope itemtype="https://schema.org/Organization">
|
||||
<meta itemprop="name" content="{$shop.name|escape:'htmlall':'UTF-8'}">
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{* --- STYLES AND SCRIPTS --- *}
|
||||
<style>
|
||||
.live-photo-thumb img {
|
||||
object-fit: cover;
|
||||
cursor: pointer;
|
||||
transition: transform 0.2s ease-in-out;
|
||||
}
|
||||
|
||||
.live-photo-thumb:hover img {
|
||||
transform: scale(1.05);
|
||||
border-color: var(--bs-primary);
|
||||
}
|
||||
|
||||
.modal-nav-btn {
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
transform: translateY(-50%);
|
||||
background-color: rgba(255, 255, 255, 0.7);
|
||||
border: 1px solid #ccc;
|
||||
z-index: 10;
|
||||
}
|
||||
|
||||
.modal-nav-btn.prev {
|
||||
left: 10px;
|
||||
}
|
||||
|
||||
.modal-nav-btn.next {
|
||||
right: 10px;
|
||||
}
|
||||
</style>
|
||||
|
||||
<script type="text/javascript">
|
||||
document.addEventListener('DOMContentLoaded', () => {
|
||||
// Data passed directly from Smarty to JavaScript
|
||||
const photos = {$live_photos|json_encode nofilter};
|
||||
let currentIndex = 0;
|
||||
|
||||
const modalElement = document.getElementById('livePhotoModal');
|
||||
if (!modalElement) return;
|
||||
|
||||
const modalImage = document.getElementById('livePhotoModalImage');
|
||||
const modalCaption = document.getElementById('livePhotoModalCaption');
|
||||
const prevBtn = document.getElementById('livePhotoPrevBtn');
|
||||
const nextBtn = document.getElementById('livePhotoNextBtn');
|
||||
|
||||
// SEO meta tags
|
||||
const metaUrl = document.getElementById('livePhotoMetaUrl');
|
||||
const metaDesc = document.getElementById('livePhotoMetaDesc');
|
||||
|
||||
const thumbnailLinks = document.querySelectorAll('.live-photo-thumb');
|
||||
|
||||
// Function to update the modal's content based on the current index
|
||||
const updateModalContent = (index) => {
|
||||
if (!photos[index]) return;
|
||||
|
||||
const photo = photos[index];
|
||||
modalImage.src = photo.url;
|
||||
modalImage.alt = photo.alt;
|
||||
modalCaption.textContent = photo.alt; // Use the descriptive alt text as a caption
|
||||
|
||||
// Update hidden SEO metadata
|
||||
metaUrl.setAttribute('content', photo.url);
|
||||
metaDesc.setAttribute('content', photo.alt);
|
||||
|
||||
// Show/hide navigation buttons
|
||||
prevBtn.style.display = (index === 0) ? 'none' : 'block';
|
||||
nextBtn.style.display = (index === photos.length - 1) ? 'none' : 'block';
|
||||
};
|
||||
|
||||
// Add click listeners to each thumbnail
|
||||
thumbnailLinks.forEach(link => {
|
||||
link.addEventListener('click', (e) => {
|
||||
e.preventDefault();
|
||||
currentIndex = parseInt(e.currentTarget.dataset.photoIndex, 10);
|
||||
updateModalContent(currentIndex);
|
||||
});
|
||||
});
|
||||
|
||||
// Add click listeners for modal navigation
|
||||
prevBtn.addEventListener('click', () => {
|
||||
if (currentIndex > 0) {
|
||||
currentIndex--;
|
||||
updateModalContent(currentIndex);
|
||||
}
|
||||
});
|
||||
|
||||
nextBtn.addEventListener('click', () => {
|
||||
if (currentIndex < photos.length - 1) {
|
||||
currentIndex++;
|
||||
updateModalContent(currentIndex);
|
||||
}
|
||||
});
|
||||
|
||||
// Add keyboard navigation for accessibility
|
||||
modalElement.addEventListener('keydown', (e) => {
|
||||
if (e.key === 'ArrowLeft') {
|
||||
prevBtn.click();
|
||||
} else if (e.key === 'ArrowRight') {
|
||||
nextBtn.click();
|
||||
}
|
||||
});
|
||||
});
|
||||
</script>
|
||||
<script type="text/javascript">
|
||||
document.addEventListener('DOMContentLoaded', () => {
|
||||
// Check if the gtag function is available to avoid errors
|
||||
if (typeof gtag !== 'function') {
|
||||
console.log('addLivePhoto GA4: gtag function not found.');
|
||||
return;
|
||||
}
|
||||
|
||||
// --- 1. Event for viewing the thumbnails ---
|
||||
// This event is sent once the thumbnails are rendered on the page.
|
||||
try {
|
||||
gtag('event', 'view_live_photo_thumbnail', {
|
||||
'event_category': 'product_page_engagement',
|
||||
'event_label': '{$product.name|escape:'javascript':'UTF-8'}',
|
||||
'product_id': '{$product.id|escape:'javascript':'UTF-8'}',
|
||||
'photo_count': {$live_photos|count}
|
||||
});
|
||||
console.log('addLivePhoto GA4: Fired event "view_live_photo_thumbnail" for product ID {$product.id}.');
|
||||
} catch (e) {
|
||||
console.error('addLivePhoto GA4: Error firing view event.', e);
|
||||
}
|
||||
|
||||
// --- 2. Event for clicking a thumbnail ---
|
||||
const thumbnailLinks = document.querySelectorAll('#addlivephoto-container .live-photo-thumb');
|
||||
|
||||
thumbnailLinks.forEach(link => {
|
||||
link.addEventListener('click', () => {
|
||||
try {
|
||||
gtag('event', 'click_live_photo', {
|
||||
'event_category': 'product_page_engagement',
|
||||
'event_label': '{$product.name|escape:'javascript':'UTF-8'}',
|
||||
'product_id': '{$product.id|escape:'javascript':'UTF-8'}',
|
||||
'photo_count': {$live_photos|count}
|
||||
});
|
||||
console.log('addLivePhoto GA4: Fired event "click_live_photo" for product ID {$product.id}.');
|
||||
} catch (e) {
|
||||
console.error('addLivePhoto GA4: Error firing click event.', e);
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
</script>
|
||||
{/if}
|
||||
Reference in New Issue
Block a user