тут очень много с карточкой товара и импортом.
This commit is contained in:
@@ -84,8 +84,8 @@ final class CatalogController
|
|||||||
['title' => 'Каталог', 'url' => '/catalog'],
|
['title' => 'Каталог', 'url' => '/catalog'],
|
||||||
];
|
];
|
||||||
|
|
||||||
if (!empty($product['category_id'])) {
|
if (!empty($product['display_category_id'] ?? $product['category_id'] ?? null)) {
|
||||||
$breadcrumbs = $categoryRepository->breadcrumbs((int) $product['category_id']);
|
$breadcrumbs = $categoryRepository->breadcrumbs((int) ($product['display_category_id'] ?? $product['category_id']));
|
||||||
}
|
}
|
||||||
|
|
||||||
$breadcrumbs[] = ['title' => (string) $product['name']];
|
$breadcrumbs[] = ['title' => (string) $product['name']];
|
||||||
|
|||||||
@@ -50,13 +50,21 @@ final class ProductRepository extends BaseRepository
|
|||||||
$placeholders = implode(',', array_fill(0, count($categoryIds), '?'));
|
$placeholders = implode(',', array_fill(0, count($categoryIds), '?'));
|
||||||
$sql = $this->previewSelectSql(
|
$sql = $this->previewSelectSql(
|
||||||
'AND (
|
'AND (
|
||||||
p.category_id IN (' . $placeholders . ')
|
EXISTS (
|
||||||
OR EXISTS (
|
|
||||||
SELECT 1
|
SELECT 1
|
||||||
FROM product_category_links pcl
|
FROM product_category_links pcl
|
||||||
WHERE pcl.product_id = p.id
|
WHERE pcl.product_id = p.id
|
||||||
AND pcl.category_id IN (' . $placeholders . ')
|
AND pcl.category_id IN (' . $placeholders . ')
|
||||||
)
|
)
|
||||||
|
OR (
|
||||||
|
p.category_id IN (' . $placeholders . ')
|
||||||
|
AND (p.legacy_path IS NULL OR p.legacy_path NOT LIKE \'/katalog/product/view/1/%\')
|
||||||
|
AND NOT EXISTS (
|
||||||
|
SELECT 1
|
||||||
|
FROM product_category_links pcl_any
|
||||||
|
WHERE pcl_any.product_id = p.id
|
||||||
|
)
|
||||||
|
)
|
||||||
)
|
)
|
||||||
ORDER BY p.sort_order, p.name',
|
ORDER BY p.sort_order, p.name',
|
||||||
$limit
|
$limit
|
||||||
@@ -74,9 +82,23 @@ final class ProductRepository extends BaseRepository
|
|||||||
public function findBySlug(string $slug): ?array
|
public function findBySlug(string $slug): ?array
|
||||||
{
|
{
|
||||||
$statement = $this->pdo()->prepare(
|
$statement = $this->pdo()->prepare(
|
||||||
'SELECT p.*, c.name AS category_name, c.slug AS category_slug
|
'SELECT
|
||||||
|
p.*,
|
||||||
|
COALESCE(linked_c.id, c.id) AS display_category_id,
|
||||||
|
COALESCE(linked_c.name, c.name) AS category_name,
|
||||||
|
COALESCE(linked_c.slug, c.slug) AS category_slug
|
||||||
FROM products p
|
FROM products p
|
||||||
LEFT JOIN categories c ON c.id = p.category_id
|
LEFT JOIN categories c ON c.id = p.category_id
|
||||||
|
LEFT JOIN categories linked_c
|
||||||
|
ON linked_c.id = (
|
||||||
|
SELECT pcl.category_id
|
||||||
|
FROM product_category_links pcl
|
||||||
|
INNER JOIN categories lc ON lc.id = pcl.category_id
|
||||||
|
WHERE pcl.product_id = p.id
|
||||||
|
AND lc.is_active = 1
|
||||||
|
ORDER BY lc.parent_id IS NULL, pcl.sort_order, pcl.category_id
|
||||||
|
LIMIT 1
|
||||||
|
)
|
||||||
WHERE p.slug = :slug
|
WHERE p.slug = :slug
|
||||||
AND p.is_published = 1
|
AND p.is_published = 1
|
||||||
AND p.is_available = 1
|
AND p.is_available = 1
|
||||||
|
|||||||
@@ -170,6 +170,28 @@ function execute_old_site_import_batch(string $sqlPath, string $progressPath, ar
|
|||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function normalize_old_site_product_categories(PDO $pdo): void
|
||||||
|
{
|
||||||
|
$pdo->exec(
|
||||||
|
"UPDATE products p
|
||||||
|
SET p.category_id = (
|
||||||
|
SELECT pcl.category_id
|
||||||
|
FROM product_category_links pcl
|
||||||
|
INNER JOIN categories c ON c.id = pcl.category_id
|
||||||
|
WHERE pcl.product_id = p.id
|
||||||
|
AND c.is_active = 1
|
||||||
|
ORDER BY c.parent_id IS NULL, pcl.sort_order, pcl.category_id
|
||||||
|
LIMIT 1
|
||||||
|
)
|
||||||
|
WHERE p.legacy_path LIKE '/katalog/product/view/1/%'
|
||||||
|
AND EXISTS (
|
||||||
|
SELECT 1
|
||||||
|
FROM product_category_links pcl_exists
|
||||||
|
WHERE pcl_exists.product_id = p.id
|
||||||
|
)"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
||||||
try {
|
try {
|
||||||
$action = (string) ($_POST['action'] ?? 'start');
|
$action = (string) ($_POST['action'] ?? 'start');
|
||||||
@@ -186,6 +208,7 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
|||||||
$progress = execute_old_site_import_batch($sqlPath, $progressPath, $progress);
|
$progress = execute_old_site_import_batch($sqlPath, $progressPath, $progress);
|
||||||
|
|
||||||
if (!empty($progress['done'])) {
|
if (!empty($progress['done'])) {
|
||||||
|
normalize_old_site_product_categories(Database::pdo());
|
||||||
$message = 'Импорт выполнен. SQL-команд выполнено: ' . (int) $progress['executed'] . '.';
|
$message = 'Импорт выполнен. SQL-команд выполнено: ' . (int) $progress['executed'] . '.';
|
||||||
} else {
|
} else {
|
||||||
$message = 'Импорт продолжается: ' . (float) $progress['percent'] . '%, команд за этот шаг: ' . (int) $progress['batch_executed'] . '.';
|
$message = 'Импорт продолжается: ' . (float) $progress['percent'] . '%, команд за этот шаг: ' . (int) $progress['batch_executed'] . '.';
|
||||||
|
|||||||
+168
-21
@@ -1007,7 +1007,13 @@ a:hover {
|
|||||||
font-weight: 700;
|
font-weight: 700;
|
||||||
}
|
}
|
||||||
|
|
||||||
.product-grid,
|
.product-grid {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: repeat(auto-fill, minmax(220px, 1fr));
|
||||||
|
gap: 16px;
|
||||||
|
align-items: stretch;
|
||||||
|
}
|
||||||
|
|
||||||
.admin-grid {
|
.admin-grid {
|
||||||
display: grid;
|
display: grid;
|
||||||
grid-template-columns: repeat(auto-fill, minmax(180px, 1fr));
|
grid-template-columns: repeat(auto-fill, minmax(180px, 1fr));
|
||||||
@@ -1024,6 +1030,13 @@ a:hover {
|
|||||||
box-shadow: 0 10px 24px rgba(22, 60, 43, 0.05);
|
box-shadow: 0 10px 24px rgba(22, 60, 43, 0.05);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.product-card {
|
||||||
|
position: relative;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
min-height: 0;
|
||||||
|
}
|
||||||
|
|
||||||
.product-card-image {
|
.product-card-image {
|
||||||
display: block;
|
display: block;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
@@ -1044,6 +1057,23 @@ a:hover {
|
|||||||
transform: scale(1.025);
|
transform: scale(1.025);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.product-card-image-placeholder {
|
||||||
|
display: grid;
|
||||||
|
place-items: center;
|
||||||
|
aspect-ratio: 4 / 3;
|
||||||
|
color: var(--brand-green);
|
||||||
|
font-weight: 900;
|
||||||
|
background:
|
||||||
|
linear-gradient(135deg, rgba(23, 61, 44, 0.08), rgba(196, 61, 49, 0.08)),
|
||||||
|
#eef4ea;
|
||||||
|
}
|
||||||
|
|
||||||
|
.product-card-image-placeholder span {
|
||||||
|
display: inline-flex;
|
||||||
|
border-bottom: 2px solid var(--brand-red);
|
||||||
|
padding-bottom: 3px;
|
||||||
|
}
|
||||||
|
|
||||||
.product-card p {
|
.product-card p {
|
||||||
margin: 0 0 10px;
|
margin: 0 0 10px;
|
||||||
font-size: 14px;
|
font-size: 14px;
|
||||||
@@ -1051,15 +1081,33 @@ a:hover {
|
|||||||
}
|
}
|
||||||
|
|
||||||
.product-card .product-category {
|
.product-card .product-category {
|
||||||
|
display: flex;
|
||||||
|
align-items: flex-start;
|
||||||
|
overflow: hidden;
|
||||||
margin-bottom: 8px;
|
margin-bottom: 8px;
|
||||||
font-size: 12px;
|
font-size: 12px;
|
||||||
|
line-height: 1.2;
|
||||||
font-weight: 700;
|
font-weight: 700;
|
||||||
text-transform: uppercase;
|
text-transform: uppercase;
|
||||||
color: #7a856f;
|
color: #7a856f;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.product-card h3 {
|
||||||
|
display: -webkit-box;
|
||||||
|
overflow: hidden;
|
||||||
|
min-height: 50px;
|
||||||
|
margin-bottom: 12px;
|
||||||
|
line-height: 1.22;
|
||||||
|
-webkit-line-clamp: 3;
|
||||||
|
-webkit-box-orient: vertical;
|
||||||
|
}
|
||||||
|
|
||||||
.price-line {
|
.price-line {
|
||||||
display: block;
|
display: flex;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
gap: 4px 6px;
|
||||||
|
align-items: baseline;
|
||||||
|
min-height: 22px;
|
||||||
margin-bottom: 6px;
|
margin-bottom: 6px;
|
||||||
font-size: 14px;
|
font-size: 14px;
|
||||||
color: #2d372f;
|
color: #2d372f;
|
||||||
@@ -1067,25 +1115,58 @@ a:hover {
|
|||||||
|
|
||||||
.package-price-line {
|
.package-price-line {
|
||||||
position: relative;
|
position: relative;
|
||||||
display: flex;
|
display: grid;
|
||||||
flex-wrap: wrap;
|
grid-template-columns: minmax(0, 1fr) auto 22px;
|
||||||
gap: 4px 7px;
|
gap: 6px;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
|
min-height: 30px;
|
||||||
|
border-top: 1px solid #e3e8de;
|
||||||
|
padding-top: 10px;
|
||||||
|
font-size: 14px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.package-price-label {
|
||||||
|
min-width: 0;
|
||||||
|
color: #2d372f;
|
||||||
|
}
|
||||||
|
|
||||||
|
.package-price-values {
|
||||||
|
display: inline-flex;
|
||||||
|
gap: 6px;
|
||||||
|
align-items: baseline;
|
||||||
|
justify-content: flex-end;
|
||||||
|
min-width: 0;
|
||||||
|
color: var(--brand-green);
|
||||||
|
white-space: nowrap;
|
||||||
|
}
|
||||||
|
|
||||||
|
.package-price-note {
|
||||||
|
display: block;
|
||||||
|
margin: 3px 0 8px;
|
||||||
|
font-size: 12px;
|
||||||
|
line-height: 1.25;
|
||||||
|
color: var(--muted);
|
||||||
}
|
}
|
||||||
|
|
||||||
.old-price {
|
.old-price {
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
margin-right: 8px;
|
margin-right: 0;
|
||||||
font-size: 13px;
|
font-size: 13px;
|
||||||
font-weight: 400;
|
font-weight: 400;
|
||||||
color: #8b9485;
|
color: #8b9485;
|
||||||
text-decoration: line-through;
|
text-decoration: line-through;
|
||||||
|
white-space: nowrap;
|
||||||
}
|
}
|
||||||
|
|
||||||
.variant-choice {
|
.variant-choice {
|
||||||
display: grid;
|
display: grid;
|
||||||
|
align-self: start;
|
||||||
gap: 7px;
|
gap: 7px;
|
||||||
margin-top: 12px;
|
min-height: 76px;
|
||||||
|
margin: 0 0 12px;
|
||||||
|
border-top: 0;
|
||||||
|
border-bottom: 1px solid #edf1e9;
|
||||||
|
padding-bottom: 10px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.variant-choice > span {
|
.variant-choice > span {
|
||||||
@@ -1098,20 +1179,27 @@ a:hover {
|
|||||||
display: flex;
|
display: flex;
|
||||||
flex-wrap: wrap;
|
flex-wrap: wrap;
|
||||||
gap: 6px;
|
gap: 6px;
|
||||||
|
align-items: flex-start;
|
||||||
|
min-height: 32px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.variant-choice-button {
|
.variant-choice-button {
|
||||||
appearance: none;
|
appearance: none;
|
||||||
|
max-width: 100%;
|
||||||
|
box-sizing: border-box;
|
||||||
min-height: 30px;
|
min-height: 30px;
|
||||||
border: 1px solid #cfd8c9;
|
border: 1px solid #cfd8c9;
|
||||||
border-radius: 7px;
|
border-radius: 7px;
|
||||||
padding: 6px 9px;
|
padding: 6px 9px;
|
||||||
font: inherit;
|
font: inherit;
|
||||||
font-size: 13px;
|
font-size: 12px;
|
||||||
|
line-height: 1.15;
|
||||||
font-weight: 800;
|
font-weight: 800;
|
||||||
color: var(--brand-green);
|
color: var(--brand-green);
|
||||||
background: #ffffff;
|
background: #ffffff;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
|
overflow-wrap: anywhere;
|
||||||
|
transition: background-color 0.12s ease, border-color 0.12s ease, color 0.12s ease;
|
||||||
}
|
}
|
||||||
|
|
||||||
.variant-choice-button:hover,
|
.variant-choice-button:hover,
|
||||||
@@ -1123,11 +1211,12 @@ a:hover {
|
|||||||
|
|
||||||
.add-to-cart-button {
|
.add-to-cart-button {
|
||||||
appearance: none;
|
appearance: none;
|
||||||
|
align-self: end;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
min-height: 40px;
|
min-height: 40px;
|
||||||
border: 0;
|
border: 0;
|
||||||
border-radius: 8px;
|
border-radius: 8px;
|
||||||
margin-top: 14px;
|
margin-top: 0;
|
||||||
padding: 10px 12px;
|
padding: 10px 12px;
|
||||||
font: inherit;
|
font: inherit;
|
||||||
font-size: 14px;
|
font-size: 14px;
|
||||||
@@ -1141,11 +1230,58 @@ a:hover {
|
|||||||
background: #102f21;
|
background: #102f21;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.product-card-actions {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: minmax(96px, 0.44fr) minmax(0, 1fr);
|
||||||
|
gap: 8px;
|
||||||
|
align-items: stretch;
|
||||||
|
min-height: 40px;
|
||||||
|
margin-top: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.quantity-stepper {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: 30px minmax(32px, 1fr) 30px;
|
||||||
|
min-width: 0;
|
||||||
|
overflow: hidden;
|
||||||
|
border: 1px solid #cfd8c9;
|
||||||
|
border-radius: 8px;
|
||||||
|
background: #ffffff;
|
||||||
|
}
|
||||||
|
|
||||||
|
.quantity-stepper button,
|
||||||
|
.quantity-stepper input {
|
||||||
|
min-width: 0;
|
||||||
|
border: 0;
|
||||||
|
font: inherit;
|
||||||
|
font-size: 14px;
|
||||||
|
font-weight: 900;
|
||||||
|
text-align: center;
|
||||||
|
color: var(--brand-green);
|
||||||
|
background: transparent;
|
||||||
|
}
|
||||||
|
|
||||||
|
.quantity-stepper button {
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
.quantity-stepper button:hover {
|
||||||
|
color: #ffffff;
|
||||||
|
background: var(--brand-green);
|
||||||
|
}
|
||||||
|
|
||||||
|
.quantity-stepper input {
|
||||||
|
border-inline: 1px solid #e3e8de;
|
||||||
|
padding: 0 3px;
|
||||||
|
}
|
||||||
|
|
||||||
.price-tier-trigger {
|
.price-tier-trigger {
|
||||||
appearance: none;
|
appearance: none;
|
||||||
display: inline-grid;
|
display: none;
|
||||||
place-items: center;
|
place-items: center;
|
||||||
|
justify-self: end;
|
||||||
width: 20px;
|
width: 20px;
|
||||||
|
min-width: 20px;
|
||||||
height: 20px;
|
height: 20px;
|
||||||
border: 1px solid #b9c6b1;
|
border: 1px solid #b9c6b1;
|
||||||
border-radius: 50%;
|
border-radius: 50%;
|
||||||
@@ -1153,11 +1289,16 @@ a:hover {
|
|||||||
font: inherit;
|
font: inherit;
|
||||||
font-size: 12px;
|
font-size: 12px;
|
||||||
font-weight: 900;
|
font-weight: 900;
|
||||||
|
line-height: 1;
|
||||||
color: var(--brand-green);
|
color: var(--brand-green);
|
||||||
background: #ffffff;
|
background: #ffffff;
|
||||||
cursor: help;
|
cursor: help;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
body[data-payment-mode="cash"] .price-tier-trigger {
|
||||||
|
display: inline-grid;
|
||||||
|
}
|
||||||
|
|
||||||
.cart-link span {
|
.cart-link span {
|
||||||
display: inline-grid;
|
display: inline-grid;
|
||||||
place-items: center;
|
place-items: center;
|
||||||
@@ -1174,9 +1315,9 @@ a:hover {
|
|||||||
|
|
||||||
.price-tier-popover {
|
.price-tier-popover {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
left: 16px;
|
|
||||||
right: 16px;
|
right: 16px;
|
||||||
bottom: 58px;
|
bottom: 62px;
|
||||||
|
left: 16px;
|
||||||
z-index: 8;
|
z-index: 8;
|
||||||
display: grid;
|
display: grid;
|
||||||
gap: 9px;
|
gap: 9px;
|
||||||
@@ -1188,20 +1329,17 @@ a:hover {
|
|||||||
box-shadow: 0 18px 44px rgba(23, 61, 44, 0.18);
|
box-shadow: 0 18px 44px rgba(23, 61, 44, 0.18);
|
||||||
opacity: 0;
|
opacity: 0;
|
||||||
visibility: hidden;
|
visibility: hidden;
|
||||||
transform: translateY(6px);
|
|
||||||
pointer-events: none;
|
pointer-events: none;
|
||||||
|
transform: translateY(6px);
|
||||||
transition: opacity 0.15s ease, transform 0.15s ease, visibility 0.15s ease;
|
transition: opacity 0.15s ease, transform 0.15s ease, visibility 0.15s ease;
|
||||||
}
|
}
|
||||||
|
|
||||||
.product-card {
|
body[data-payment-mode="cash"] .package-price-line:has(.price-tier-trigger:hover) + .price-tier-popover,
|
||||||
position: relative;
|
body[data-payment-mode="cash"] .price-tier-popover:hover {
|
||||||
}
|
|
||||||
|
|
||||||
.product-card:hover .price-tier-popover,
|
|
||||||
.product-card:focus-within .price-tier-popover {
|
|
||||||
opacity: 1;
|
opacity: 1;
|
||||||
visibility: visible;
|
visibility: visible;
|
||||||
transform: translateY(0);
|
transform: translateY(0);
|
||||||
|
pointer-events: auto;
|
||||||
}
|
}
|
||||||
|
|
||||||
.price-tier-popover strong {
|
.price-tier-popover strong {
|
||||||
@@ -1218,7 +1356,7 @@ a:hover {
|
|||||||
.price-tier-popover div {
|
.price-tier-popover div {
|
||||||
display: grid;
|
display: grid;
|
||||||
grid-template-columns: minmax(0, 1fr) auto;
|
grid-template-columns: minmax(0, 1fr) auto;
|
||||||
gap: 10px;
|
gap: 8px;
|
||||||
align-items: baseline;
|
align-items: baseline;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1226,6 +1364,7 @@ a:hover {
|
|||||||
font-size: 12px;
|
font-size: 12px;
|
||||||
line-height: 1.25;
|
line-height: 1.25;
|
||||||
color: var(--muted);
|
color: var(--muted);
|
||||||
|
white-space: nowrap;
|
||||||
}
|
}
|
||||||
|
|
||||||
.price-tier-popover dd {
|
.price-tier-popover dd {
|
||||||
@@ -1320,7 +1459,7 @@ a:hover {
|
|||||||
|
|
||||||
.variant-row {
|
.variant-row {
|
||||||
display: grid;
|
display: grid;
|
||||||
grid-template-columns: minmax(0, 1.2fr) minmax(130px, 0.7fr) minmax(110px, 0.55fr) minmax(120px, 0.55fr);
|
grid-template-columns: minmax(0, 1.2fr) minmax(130px, 0.7fr) minmax(110px, 0.55fr) minmax(220px, 0.75fr);
|
||||||
gap: 12px;
|
gap: 12px;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
border: 1px solid var(--line);
|
border: 1px solid var(--line);
|
||||||
@@ -1329,6 +1468,10 @@ a:hover {
|
|||||||
background: #f8faf5;
|
background: #f8faf5;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.variant-row .product-card-actions {
|
||||||
|
margin-top: 0;
|
||||||
|
}
|
||||||
|
|
||||||
.variant-row b {
|
.variant-row b {
|
||||||
color: var(--brand-green);
|
color: var(--brand-green);
|
||||||
text-align: right;
|
text-align: right;
|
||||||
@@ -2338,6 +2481,10 @@ a:hover {
|
|||||||
grid-template-columns: 1fr;
|
grid-template-columns: 1fr;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.product-card-actions {
|
||||||
|
grid-template-columns: 1fr;
|
||||||
|
}
|
||||||
|
|
||||||
.variant-row b {
|
.variant-row b {
|
||||||
text-align: left;
|
text-align: left;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -62,8 +62,15 @@ $mainImage = $product['main_image_path'] ?? '';
|
|||||||
<?php endif; ?>
|
<?php endif; ?>
|
||||||
<span data-dynamic-package-price data-base-package-price="<?= e((string) $variant['package_price']) ?>"><?= e(money_label($variant['package_price'])) ?></span>
|
<span data-dynamic-package-price data-base-package-price="<?= e((string) $variant['package_price']) ?>"><?= e(money_label($variant['package_price'])) ?></span>
|
||||||
</b>
|
</b>
|
||||||
|
<div class="product-card-actions">
|
||||||
|
<div class="quantity-stepper" data-quantity-control aria-label="Количество фасовок">
|
||||||
|
<button type="button" data-quantity-minus aria-label="Уменьшить количество">-</button>
|
||||||
|
<input data-quantity-input type="text" inputmode="numeric" value="1" aria-label="Количество">
|
||||||
|
<button type="button" data-quantity-plus aria-label="Увеличить количество">+</button>
|
||||||
|
</div>
|
||||||
<button class="add-to-cart-button" type="button">В корзину</button>
|
<button class="add-to-cart-button" type="button">В корзину</button>
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
<?php endforeach; ?>
|
<?php endforeach; ?>
|
||||||
</section>
|
</section>
|
||||||
<?php endif; ?>
|
<?php endif; ?>
|
||||||
|
|||||||
+71
-14
@@ -266,8 +266,7 @@ try {
|
|||||||
const tierValues = (base) => ({
|
const tierValues = (base) => ({
|
||||||
base: base,
|
base: base,
|
||||||
cash20: base * 0.95,
|
cash20: base * 0.95,
|
||||||
cash50: base * 0.93,
|
cash50: base * 0.93
|
||||||
invoice: base * 1.08
|
|
||||||
});
|
});
|
||||||
|
|
||||||
const updateDynamicPrices = (scope) => {
|
const updateDynamicPrices = (scope) => {
|
||||||
@@ -287,7 +286,7 @@ try {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const updateCardPrices = (card) => {
|
const updateCardPrices = (card) => {
|
||||||
const packageNode = card.querySelector('[data-package-price]');
|
const packageNode = card.querySelector('[data-dynamic-package-price]');
|
||||||
updateDynamicPrices(card);
|
updateDynamicPrices(card);
|
||||||
|
|
||||||
if (packageNode) {
|
if (packageNode) {
|
||||||
@@ -297,12 +296,10 @@ try {
|
|||||||
const tierBase = card.querySelector('[data-tier-base]');
|
const tierBase = card.querySelector('[data-tier-base]');
|
||||||
const tierCash20 = card.querySelector('[data-tier-cash-20]');
|
const tierCash20 = card.querySelector('[data-tier-cash-20]');
|
||||||
const tierCash50 = card.querySelector('[data-tier-cash-50]');
|
const tierCash50 = card.querySelector('[data-tier-cash-50]');
|
||||||
const tierInvoice = card.querySelector('[data-tier-invoice]');
|
|
||||||
|
|
||||||
if (tierBase) tierBase.textContent = formatRub(tiers.base);
|
if (tierBase) tierBase.textContent = formatRub(tiers.base);
|
||||||
if (tierCash20) tierCash20.textContent = formatRub(tiers.cash20);
|
if (tierCash20) tierCash20.textContent = formatRub(tiers.cash20);
|
||||||
if (tierCash50) tierCash50.textContent = formatRub(tiers.cash50);
|
if (tierCash50) tierCash50.textContent = formatRub(tiers.cash50);
|
||||||
if (tierInvoice) tierInvoice.textContent = formatRub(tiers.invoice);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
};
|
};
|
||||||
@@ -378,6 +375,8 @@ try {
|
|||||||
|
|
||||||
const updateModeButtons = () => {
|
const updateModeButtons = () => {
|
||||||
const payment = getPaymentMode();
|
const payment = getPaymentMode();
|
||||||
|
document.body.dataset.paymentMode = payment;
|
||||||
|
|
||||||
document.querySelectorAll('[data-payment-mode]').forEach((button) => {
|
document.querySelectorAll('[data-payment-mode]').forEach((button) => {
|
||||||
button.classList.toggle('is-active', button.dataset.paymentMode === payment);
|
button.classList.toggle('is-active', button.dataset.paymentMode === payment);
|
||||||
});
|
});
|
||||||
@@ -425,6 +424,37 @@ try {
|
|||||||
window.RybStockPricing.setPaymentMode(modeButton.dataset.paymentMode || 'cash');
|
window.RybStockPricing.setPaymentMode(modeButton.dataset.paymentMode || 'cash');
|
||||||
});
|
});
|
||||||
|
|
||||||
|
document.addEventListener('click', (event) => {
|
||||||
|
const quantityButton = event.target.closest('[data-quantity-minus], [data-quantity-plus]');
|
||||||
|
|
||||||
|
if (!quantityButton) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const control = quantityButton.closest('[data-quantity-control]');
|
||||||
|
const input = control?.querySelector('[data-quantity-input]');
|
||||||
|
|
||||||
|
if (!input) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const current = Math.max(1, parseInt(input.value || '1', 10) || 1);
|
||||||
|
const next = quantityButton.matches('[data-quantity-plus]')
|
||||||
|
? current + 1
|
||||||
|
: Math.max(1, current - 1);
|
||||||
|
input.value = String(next);
|
||||||
|
});
|
||||||
|
|
||||||
|
document.addEventListener('input', (event) => {
|
||||||
|
const input = event.target.closest('[data-quantity-input]');
|
||||||
|
|
||||||
|
if (!input) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
input.value = String(Math.max(1, parseInt(input.value.replace(/\D/g, '') || '1', 10) || 1));
|
||||||
|
});
|
||||||
|
|
||||||
document.addEventListener('click', (event) => {
|
document.addEventListener('click', (event) => {
|
||||||
const addButton = event.target.closest('.add-to-cart-button');
|
const addButton = event.target.closest('.add-to-cart-button');
|
||||||
|
|
||||||
@@ -435,19 +465,21 @@ try {
|
|||||||
const productScope = addButton.closest('.price-aware');
|
const productScope = addButton.closest('.price-aware');
|
||||||
const row = addButton.closest('.variant-row');
|
const row = addButton.closest('.variant-row');
|
||||||
const variantButton = productScope?.querySelector('.variant-choice-button.is-active');
|
const variantButton = productScope?.querySelector('.variant-choice-button.is-active');
|
||||||
const packagePriceNode = row?.querySelector('[data-dynamic-package-price]') || productScope?.querySelector('[data-package-price]');
|
const packagePriceNode = row?.querySelector('[data-dynamic-package-price]') || productScope?.querySelector('[data-dynamic-package-price]');
|
||||||
|
const quantityInput = row?.querySelector('[data-quantity-input]') || productScope?.querySelector('[data-quantity-input]');
|
||||||
|
|
||||||
if (!productScope || !packagePriceNode) {
|
if (!productScope || !packagePriceNode) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const addQuantity = Math.max(1, parseInt(quantityInput?.value || '1', 10) || 1);
|
||||||
const variantId = row?.dataset.variantId || variantButton?.dataset.variantId || 'default';
|
const variantId = row?.dataset.variantId || variantButton?.dataset.variantId || 'default';
|
||||||
const key = String(productScope.dataset.productId || '') + ':' + variantId;
|
const key = String(productScope.dataset.productId || '') + ':' + variantId;
|
||||||
const items = getCartItems();
|
const items = getCartItems();
|
||||||
const existing = items.find((item) => item.key === key);
|
const existing = items.find((item) => item.key === key);
|
||||||
|
|
||||||
if (existing) {
|
if (existing) {
|
||||||
existing.quantity = Number(existing.quantity || 0) + 1;
|
existing.quantity = Number(existing.quantity || 0) + addQuantity;
|
||||||
} else {
|
} else {
|
||||||
items.push({
|
items.push({
|
||||||
key,
|
key,
|
||||||
@@ -459,7 +491,7 @@ try {
|
|||||||
variantName: row?.dataset.variantName || variantButton?.dataset.variantName || 'Фасовка',
|
variantName: row?.dataset.variantName || variantButton?.dataset.variantName || 'Фасовка',
|
||||||
basePackagePrice: Number(packagePriceNode.dataset.basePackagePrice || 0),
|
basePackagePrice: Number(packagePriceNode.dataset.basePackagePrice || 0),
|
||||||
baseUnitPrice: Number(row?.dataset.baseUnitPrice || variantButton?.dataset.baseUnitPrice || 0),
|
baseUnitPrice: Number(row?.dataset.baseUnitPrice || variantButton?.dataset.baseUnitPrice || 0),
|
||||||
quantity: 1
|
quantity: addQuantity
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -526,15 +558,23 @@ try {
|
|||||||
});
|
});
|
||||||
|
|
||||||
const unitNode = card.querySelector('[data-unit-label]');
|
const unitNode = card.querySelector('[data-unit-label]');
|
||||||
const unitPriceNode = card.querySelector('[data-price-unit]');
|
const unitPriceNode = card.querySelector('[data-dynamic-unit-price]');
|
||||||
const packagePriceNode = card.querySelector('[data-package-price]');
|
const packagePriceNode = card.querySelector('[data-dynamic-package-price]');
|
||||||
const oldPriceNode = card.querySelector('[data-old-price]');
|
const packageLabelNode = card.querySelector('[data-package-label-display]');
|
||||||
|
const packageLabelNoteNode = card.querySelector('[data-package-label-note]');
|
||||||
|
const unitLabelNoteNode = card.querySelector('[data-unit-label-note]');
|
||||||
|
const oldPriceNode = card.querySelector('[data-old-package-price-display]');
|
||||||
|
const oldUnitPriceNode = card.querySelector('[data-old-unit-price-display]');
|
||||||
|
|
||||||
if (unitNode) {
|
if (unitNode) {
|
||||||
unitNode.textContent = button.dataset.unit || 'ед.';
|
unitNode.textContent = button.dataset.unit || 'ед.';
|
||||||
}
|
}
|
||||||
|
|
||||||
if (unitPriceNode && button.dataset.priceUnit) {
|
if (unitLabelNoteNode) {
|
||||||
|
unitLabelNoteNode.textContent = button.dataset.unit || 'ед.';
|
||||||
|
}
|
||||||
|
|
||||||
|
if (unitPriceNode) {
|
||||||
unitPriceNode.dataset.baseUnitPrice = button.dataset.baseUnitPrice || '';
|
unitPriceNode.dataset.baseUnitPrice = button.dataset.baseUnitPrice || '';
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -542,9 +582,26 @@ try {
|
|||||||
packagePriceNode.dataset.basePackagePrice = button.dataset.basePackagePrice || '';
|
packagePriceNode.dataset.basePackagePrice = button.dataset.basePackagePrice || '';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (packageLabelNode) {
|
||||||
|
packageLabelNode.textContent = button.dataset.variantPackageLabel || button.dataset.variantName || 'Фасовка';
|
||||||
|
}
|
||||||
|
|
||||||
|
if (packageLabelNoteNode) {
|
||||||
|
packageLabelNoteNode.textContent = button.dataset.variantPackageLabel || button.dataset.variantName || 'Фасовка';
|
||||||
|
}
|
||||||
|
|
||||||
if (oldPriceNode) {
|
if (oldPriceNode) {
|
||||||
oldPriceNode.textContent = button.dataset.oldPrice || '';
|
const oldPackagePrice = button.dataset.oldPrice || '';
|
||||||
oldPriceNode.hidden = !button.dataset.oldPrice;
|
oldPriceNode.textContent = oldPackagePrice;
|
||||||
|
oldPriceNode.hidden = oldPackagePrice === '';
|
||||||
|
}
|
||||||
|
|
||||||
|
if (oldUnitPriceNode) {
|
||||||
|
const oldUnitPrice = Number(button.dataset.oldUnitPrice || 0);
|
||||||
|
const baseUnitPrice = Number(button.dataset.baseUnitPrice || 0);
|
||||||
|
const hasOldUnitPrice = oldUnitPrice > baseUnitPrice;
|
||||||
|
oldUnitPriceNode.textContent = hasOldUnitPrice ? formatRub(oldUnitPrice) : '';
|
||||||
|
oldUnitPriceNode.hidden = !hasOldUnitPrice;
|
||||||
}
|
}
|
||||||
|
|
||||||
updateCardPrices(card);
|
updateCardPrices(card);
|
||||||
|
|||||||
@@ -9,11 +9,26 @@ $activeVariant = $variants[0] ?? [
|
|||||||
'unit' => $product['unit'] ?? '',
|
'unit' => $product['unit'] ?? '',
|
||||||
'price_per_unit' => $product['price_per_unit'] ?? null,
|
'price_per_unit' => $product['price_per_unit'] ?? null,
|
||||||
'package_price' => $product['package_price'] ?? null,
|
'package_price' => $product['package_price'] ?? null,
|
||||||
|
'package_quantity' => $product['package_quantity'] ?? 0,
|
||||||
'old_package_price' => $product['old_package_price'] ?? null,
|
'old_package_price' => $product['old_package_price'] ?? null,
|
||||||
];
|
];
|
||||||
$cardId = 'product-card-' . (int) ($product['id'] ?? 0);
|
$cardId = 'product-card-' . (int) ($product['id'] ?? 0);
|
||||||
$activePackagePrice = (float) ($activeVariant['package_price'] ?? 0);
|
$activePackagePrice = (float) ($activeVariant['package_price'] ?? 0);
|
||||||
$activeUnitPrice = $activeVariant['price_per_unit'] === null ? null : (float) $activeVariant['price_per_unit'];
|
$activeUnitPrice = $activeVariant['price_per_unit'] === null ? null : (float) $activeVariant['price_per_unit'];
|
||||||
|
$activePackageQuantity = (float) ($activeVariant['package_quantity'] ?? 0);
|
||||||
|
$activeVariantOldPackagePrice = $activeVariant['old_package_price'] ?? null;
|
||||||
|
$activeOldPackagePrice = (
|
||||||
|
$activeVariantOldPackagePrice !== null
|
||||||
|
&& (float) $activeVariantOldPackagePrice > $activePackagePrice
|
||||||
|
)
|
||||||
|
? (float) $activeVariantOldPackagePrice
|
||||||
|
: null;
|
||||||
|
$activeOldUnitPrice = ($activeOldPackagePrice !== null && $activePackageQuantity > 0 && $activeUnitPrice !== null)
|
||||||
|
? $activeOldPackagePrice / $activePackageQuantity
|
||||||
|
: null;
|
||||||
|
if ($activeOldUnitPrice !== null && $activeOldUnitPrice <= $activeUnitPrice) {
|
||||||
|
$activeOldUnitPrice = null;
|
||||||
|
}
|
||||||
?>
|
?>
|
||||||
<article
|
<article
|
||||||
class="product-card price-aware"
|
class="product-card price-aware"
|
||||||
@@ -27,38 +42,37 @@ $activeUnitPrice = $activeVariant['price_per_unit'] === null ? null : (float) $a
|
|||||||
<a class="product-card-image" href="/product/<?= e($product['slug']) ?>" aria-label="<?= e($product['name']) ?>">
|
<a class="product-card-image" href="/product/<?= e($product['slug']) ?>" aria-label="<?= e($product['name']) ?>">
|
||||||
<img src="<?= e((string) $product['main_image_path']) ?>" alt="<?= e((string) $product['name']) ?>">
|
<img src="<?= e((string) $product['main_image_path']) ?>" alt="<?= e((string) $product['name']) ?>">
|
||||||
</a>
|
</a>
|
||||||
|
<?php else: ?>
|
||||||
|
<a class="product-card-image product-card-image-placeholder" href="/product/<?= e($product['slug']) ?>" aria-label="<?= e($product['name']) ?>">
|
||||||
|
<span>Рыбсток</span>
|
||||||
|
</a>
|
||||||
<?php endif; ?>
|
<?php endif; ?>
|
||||||
|
|
||||||
<p class="product-category"><?= e($product['category_display_name'] ?? $product['category_name'] ?? 'Каталог') ?></p>
|
<p class="product-category"><?= e($product['category_display_name'] ?? $product['category_name'] ?? 'Каталог') ?></p>
|
||||||
<h3><a href="/product/<?= e($product['slug']) ?>"><?= e($product['name']) ?></a></h3>
|
<h3><a href="/product/<?= e($product['slug']) ?>"><?= e($product['name']) ?></a></h3>
|
||||||
|
|
||||||
<?php if ($activeVariant['price_per_unit'] !== null): ?>
|
|
||||||
<span class="price-line">Цена за <span data-unit-label><?= e(unit_label((string) $activeVariant['unit'])) ?></span>: <b data-price-unit data-dynamic-unit-price data-base-unit-price="<?= e((string) $activeUnitPrice) ?>"><?= e(money_label($activeVariant['price_per_unit'])) ?></b></span>
|
|
||||||
<?php endif; ?>
|
|
||||||
|
|
||||||
<?php if ($activeVariant['package_price'] !== null): ?>
|
|
||||||
<strong class="package-price-line">
|
|
||||||
<span class="old-price" data-old-price<?= empty($activeVariant['old_package_price']) ? ' hidden' : '' ?>><?= e(money_label($activeVariant['old_package_price'] ?? null)) ?></span>
|
|
||||||
За упаковку: <span data-package-price data-dynamic-package-price data-base-package-price="<?= e((string) $activePackagePrice) ?>"><?= e(money_label($activeVariant['package_price'])) ?></span>
|
|
||||||
<button class="price-tier-trigger" type="button" aria-label="Показать градацию цены">?</button>
|
|
||||||
</strong>
|
|
||||||
<?php endif; ?>
|
|
||||||
|
|
||||||
<div class="price-tier-popover" role="tooltip">
|
|
||||||
<strong>Варианты цены</strong>
|
|
||||||
<dl>
|
|
||||||
<div><dt>Базовая цена</dt><dd data-tier-base><?= e(money_label($activePackagePrice)) ?></dd></div>
|
|
||||||
<div><dt>Цена при сумме заказа от 20 000р. наличными</dt><dd data-tier-cash-20><?= e(money_label(floor($activePackagePrice * 0.95))) ?></dd></div>
|
|
||||||
<div><dt>Цена при сумме заказа от 50 000р. наличными</dt><dd data-tier-cash-50><?= e(money_label(floor($activePackagePrice * 0.93))) ?></dd></div>
|
|
||||||
<div><dt>Оплата по расчетному счету</dt><dd data-tier-invoice><?= e(money_label(floor($activePackagePrice * 1.08))) ?></dd></div>
|
|
||||||
</dl>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<?php if ($variants !== []): ?>
|
<?php if ($variants !== []): ?>
|
||||||
<div class="variant-choice" aria-label="Выбор фасовки">
|
<div class="variant-choice" aria-label="Выбор фасовки">
|
||||||
<span>Фасовка:</span>
|
<span>Доступные фасовки:</span>
|
||||||
<div class="variant-choice-list">
|
<div class="variant-choice-list">
|
||||||
<?php foreach ($variants as $index => $variant): ?>
|
<?php foreach ($variants as $index => $variant): ?>
|
||||||
|
<?php
|
||||||
|
$variantPackagePrice = (float) ($variant['package_price'] ?? 0);
|
||||||
|
$variantUnitPrice = $variant['price_per_unit'] === null ? null : (float) $variant['price_per_unit'];
|
||||||
|
$variantPackageQuantity = (float) ($variant['package_quantity'] ?? 0);
|
||||||
|
$variantOldPackagePrice = (
|
||||||
|
!empty($variant['old_package_price'])
|
||||||
|
&& (float) $variant['old_package_price'] > $variantPackagePrice
|
||||||
|
)
|
||||||
|
? (float) $variant['old_package_price']
|
||||||
|
: null;
|
||||||
|
$variantOldUnitPrice = ($variantOldPackagePrice !== null && $variantPackageQuantity > 0 && $variantUnitPrice !== null)
|
||||||
|
? $variantOldPackagePrice / $variantPackageQuantity
|
||||||
|
: null;
|
||||||
|
if ($variantOldUnitPrice !== null && $variantOldUnitPrice <= $variantUnitPrice) {
|
||||||
|
$variantOldUnitPrice = null;
|
||||||
|
}
|
||||||
|
?>
|
||||||
<button
|
<button
|
||||||
class="variant-choice-button<?= $index === 0 ? ' is-active' : '' ?>"
|
class="variant-choice-button<?= $index === 0 ? ' is-active' : '' ?>"
|
||||||
type="button"
|
type="button"
|
||||||
@@ -67,10 +81,12 @@ $activeUnitPrice = $activeVariant['price_per_unit'] === null ? null : (float) $a
|
|||||||
data-variant-name="<?= e((string) ($variant['name'] ?? '')) ?>"
|
data-variant-name="<?= e((string) ($variant['name'] ?? '')) ?>"
|
||||||
data-unit="<?= e(unit_label((string) $variant['unit'])) ?>"
|
data-unit="<?= e(unit_label((string) $variant['unit'])) ?>"
|
||||||
data-base-unit-price="<?= e((string) ($variant['price_per_unit'] ?? '')) ?>"
|
data-base-unit-price="<?= e((string) ($variant['price_per_unit'] ?? '')) ?>"
|
||||||
|
data-old-unit-price="<?= e($variantOldUnitPrice === null ? '' : (string) $variantOldUnitPrice) ?>"
|
||||||
data-base-package-price="<?= e((string) ($variant['package_price'] ?? '')) ?>"
|
data-base-package-price="<?= e((string) ($variant['package_price'] ?? '')) ?>"
|
||||||
data-price-unit="<?= e(money_label($variant['price_per_unit'])) ?>"
|
data-variant-package-label="<?= e((string) ($variant['name'] ?? '')) ?>"
|
||||||
data-package-price="<?= e(money_label($variant['package_price'])) ?>"
|
data-variant-unit-price-label="<?= e(money_label($variant['price_per_unit'])) ?>"
|
||||||
data-old-price="<?= e(money_label($variant['old_package_price'] ?? null)) ?>"
|
data-variant-package-price-label="<?= e(money_label($variant['package_price'])) ?>"
|
||||||
|
data-old-price="<?= e($variantOldPackagePrice === null ? '' : money_label($variantOldPackagePrice)) ?>"
|
||||||
>
|
>
|
||||||
<?= e((string) $variant['name']) ?>
|
<?= e((string) $variant['name']) ?>
|
||||||
</button>
|
</button>
|
||||||
@@ -79,5 +95,43 @@ $activeUnitPrice = $activeVariant['price_per_unit'] === null ? null : (float) $a
|
|||||||
</div>
|
</div>
|
||||||
<?php endif; ?>
|
<?php endif; ?>
|
||||||
|
|
||||||
|
<?php if ($activeVariant['price_per_unit'] !== null): ?>
|
||||||
|
<span class="price-line">
|
||||||
|
Цена за <span data-unit-label><?= e(unit_label((string) $activeVariant['unit'])) ?></span>:
|
||||||
|
<span class="old-price" data-old-unit-price-display<?= $activeOldUnitPrice === null ? ' hidden' : '' ?>><?= e(money_label($activeOldUnitPrice)) ?></span>
|
||||||
|
<b data-price-unit data-dynamic-unit-price data-base-unit-price="<?= e((string) $activeUnitPrice) ?>"><?= e(money_label($activeVariant['price_per_unit'])) ?></b>
|
||||||
|
</span>
|
||||||
|
<?php endif; ?>
|
||||||
|
|
||||||
|
<?php if ($activeVariant['package_price'] !== null): ?>
|
||||||
|
<strong class="package-price-line">
|
||||||
|
<span class="package-price-label">За фасовку <span data-package-label-display><?= e((string) $activeVariant['name']) ?></span>:</span>
|
||||||
|
<span class="package-price-values">
|
||||||
|
<span class="old-price" data-old-package-price-display<?= $activeOldPackagePrice === null ? ' hidden' : '' ?>><?= e(money_label($activeOldPackagePrice)) ?></span>
|
||||||
|
<span data-package-price data-dynamic-package-price data-base-package-price="<?= e((string) $activePackagePrice) ?>"><?= e(money_label($activeVariant['package_price'])) ?></span>
|
||||||
|
</span>
|
||||||
|
<button class="price-tier-trigger" type="button" aria-label="Показать градацию цены">?</button>
|
||||||
|
</strong>
|
||||||
|
<?php if ($activeVariant['price_per_unit'] !== null): ?>
|
||||||
|
<span class="package-price-note">Цена за <span data-unit-label-note><?= e(unit_label((string) $activeVariant['unit'])) ?></span> × <span data-package-label-note><?= e((string) $activeVariant['name']) ?></span></span>
|
||||||
|
<?php endif; ?>
|
||||||
|
<?php endif; ?>
|
||||||
|
|
||||||
|
<div class="price-tier-popover" role="tooltip">
|
||||||
|
<strong>Варианты цены за наличный расчет:</strong>
|
||||||
|
<dl>
|
||||||
|
<div><dt>Базовая цена</dt><dd data-tier-base><?= e(money_label($activePackagePrice)) ?></dd></div>
|
||||||
|
<div><dt>При заказе от 20 000р.</dt><dd data-tier-cash-20><?= e(money_label(floor($activePackagePrice * 0.95))) ?></dd></div>
|
||||||
|
<div><dt>При заказе от 50 000р.</dt><dd data-tier-cash-50><?= e(money_label(floor($activePackagePrice * 0.93))) ?></dd></div>
|
||||||
|
</dl>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="product-card-actions">
|
||||||
|
<div class="quantity-stepper" data-quantity-control aria-label="Количество фасовок">
|
||||||
|
<button type="button" data-quantity-minus aria-label="Уменьшить количество">-</button>
|
||||||
|
<input data-quantity-input type="text" inputmode="numeric" value="1" aria-label="Количество">
|
||||||
|
<button type="button" data-quantity-plus aria-label="Увеличить количество">+</button>
|
||||||
|
</div>
|
||||||
<button class="add-to-cart-button" type="button">В корзину</button>
|
<button class="add-to-cart-button" type="button">В корзину</button>
|
||||||
|
</div>
|
||||||
</article>
|
</article>
|
||||||
|
|||||||
@@ -383,7 +383,7 @@
|
|||||||
15. Нужно ли сохранять расчет доставки в заказе или только показывать клиенту предварительно?
|
15. Нужно ли сохранять расчет доставки в заказе или только показывать клиенту предварительно?
|
||||||
16. Нужен ли ручной пересчет доставки администратором в заказе?
|
16. Нужен ли ручной пересчет доставки администратором в заказе?
|
||||||
17. Как учитывать подъем 390 руб.: отдельной галочкой в корзине или только текстом?
|
17. Как учитывать подъем 390 руб.: отдельной галочкой в корзине или только текстом?
|
||||||
18. Нужен ли выбор даты доставки в календаре?
|
18. Нужен ли выбор даты доставки в календаре..S?
|
||||||
19. Нужно ли запрещать выбор выходных и праздников в календаре?
|
19. Нужно ли запрещать выбор выходных и праздников в календаре?
|
||||||
20. Где брать производственный календарь РФ: встроенный список, API или ручная настройка в админке?
|
20. Где брать производственный календарь РФ: встроенный список, API или ручная настройка в админке?
|
||||||
21. Какой должен быть внешний стиль: более премиальный, складской/оптовый, домашний семейный или нейтральный продуктовый?
|
21. Какой должен быть внешний стиль: более премиальный, складской/оптовый, домашний семейный или нейтральный продуктовый?
|
||||||
|
|||||||
Reference in New Issue
Block a user