{#
This file is part of EC-CUBE
Copyright(c) EC-CUBE CO.,LTD. All Rights Reserved.
http://www.ec-cube.co.jp/
For the full copyright and license information, please view the LICENSE
file that was distributed with this source code.
#}
{% extends 'default_frame.twig' %}
{% set body_class = 'product_page' %}
{% block stylesheet %}
<link rel="stylesheet" href="{{ asset('assets/css/swiper-bundle.min.css') }}">
<link rel="stylesheet" href="{{ asset('assets/css/product-detail.css') }}">
{% endblock %}
{% block javascript %}
<script>
eccube.classCategories = {{ class_categories_as_json(Product)|raw }};
// 規格2に選択肢を割り当てる。
function fnSetClassCategories(form, classcat_id2_selected) {
var $form = $(form);
var product_id = $form.find('input[name=product_id]').val();
var $sele1 = $form.find('select[name=classcategory_id1]');
var $sele2 = $form.find('select[name=classcategory_id2]');
eccube.setClassCategories($form, product_id, $sele1, $sele2, classcat_id2_selected);
}
{% if form.classcategory_id2 is defined %}
fnSetClassCategories(
$('#form1'), {{ form.classcategory_id2.vars.value|json_encode|raw }}
);
{% elseif form.classcategory_id1 is defined %}
eccube.checkStock($('#form1'), {{ Product.id }}, {{ form.classcategory_id1.vars.value|json_encode|raw }}, null);
{% endif %}
</script>
<script src="{{ asset('assets/js/swiper-bundle.min.js') }}"></script>
<script>
document.addEventListener('DOMContentLoaded', function() {
// bfcache無効化
window.addEventListener('pageshow', function(event) {
if (event.persisted) {
location.reload(true);
}
});
// Thumbnails swiper
const thumbnailsSwiper = new Swiper('#product-detail-thumbnails-swiper-desktop', {
spaceBetween: 6,
slidesPerView: 4,
freeMode: true,
watchSlidesProgress: true,
breakpoints: {
0: {
slidesPerView: 3,
},
600: {
slidesPerView: 4,
},
},
});
// Main swiper
const mainSwiper = new Swiper('#product-detail-main-swiper-desktop', {
spaceBetween: 0,
slidesPerView: 1,
thumbs: {
swiper: thumbnailsSwiper,
},
});
// Quantity controls
const quantityInput = document.querySelector('#quantity');
const quantityValue = document.querySelector('.product-info__quantity-value');
const minusBtn = document.querySelector('.product-info__quantity-btn--minus');
const plusBtn = document.querySelector('.product-info__quantity-btn--plus');
if (minusBtn && plusBtn && quantityInput) {
minusBtn.addEventListener('click', function() {
const currentValue = parseInt(quantityInput.value) || 1;
if (currentValue > 1) {
quantityInput.value = currentValue - 1;
if (quantityValue) {
quantityValue.textContent = quantityInput.value;
}
}
});
plusBtn.addEventListener('click', function() {
const currentValue = parseInt(quantityInput.value) || 1;
quantityInput.value = currentValue + 1;
if (quantityValue) {
quantityValue.textContent = quantityInput.value;
}
});
// Update display when input changes
quantityInput.addEventListener('change', function() {
if (quantityValue) {
quantityValue.textContent = this.value;
}
});
// Initial display
if (quantityValue) {
quantityValue.textContent = quantityInput.value || 1;
}
}
});
</script>
<script>
$(function() {
$('.add-cart').on('click', function(event) {
{% if form.classcategory_id1 is defined %}
// 規格1フォームの必須チェック
if ($('#classcategory_id1').val() == '__unselected' || $('#classcategory_id1').val() == '') {
$('#classcategory_id1')[0].setCustomValidity('{{ 'front.product.product_class_unselected'|trans }}');
return true;
} else {
$('#classcategory_id1')[0].setCustomValidity('');
}
{% endif %}
{% if form.classcategory_id2 is defined %}
// 規格2フォームの必須チェック
if ($('#classcategory_id2').val() == '__unselected' || $('#classcategory_id2').val() == '') {
$('#classcategory_id2')[0].setCustomValidity('{{ 'front.product.product_class_unselected'|trans }}');
return true;
} else {
$('#classcategory_id2')[0].setCustomValidity('');
}
{% endif %}
// 個数フォームのチェック
const quantityInput = $('#quantity');
if (quantityInput.length > 0) {
if (quantityInput.val() < 1) {
quantityInput[0].setCustomValidity('{{ 'front.product.invalid_quantity'|trans }}');
return true;
} else {
quantityInput[0].setCustomValidity('');
}
}
event.preventDefault();
$form = $('#form1');
$.ajax({
url: $form.attr('action'),
type: $form.attr('method'),
data: $form.serialize(),
dataType: 'json',
beforeSend: function(xhr, settings) {
// Buttonを無効にする
$('.add-cart').prop('disabled', true);
}
}).done(function(data) {
// レスポンス内のメッセージをalertで表示
$.each(data.messages, function() {
$('#ec-modal-header').html(this);
});
$('.ec-modal').show()
// カートブロックを更新する
$.ajax({
url: "{{ url('block_cart') }}",
type: 'GET',
dataType: 'html'
}).done(function(html) {
$('.ec-headerRole__cart').html(html);
});
}).fail(function(data) {
alert('{{ 'front.product.add_cart_error'|trans }}');
}).always(function(data) {
// Buttonを有効にする
$('.add-cart').prop('disabled', false);
});
});
});
$('.ec-modal-wrap').on('click', function(e) {
// モーダル内の処理は外側にバブリングさせない
e.stopPropagation();
});
$('.ec-modal-overlay, .ec-modal, .ec-modal-close, .ec-inlineBtn--cancel').on('click', function() {
$('.ec-modal').hide()
});
function reloadPage() {
window.location.reload();
}
</script>
<script type="application/ld+json">
{
"@context": "https://schema.org/",
"@type": "Product",
"name": "{{ Product.name }}",
"image": [
{% for img in Product.ProductImage %}
"{{ app.request.schemeAndHttpHost }}{{ asset(img, 'save_image') }}"{% if not loop.last %},{% endif %}
{% else %}
"{{ app.request.schemeAndHttpHost }}{{ asset(''|no_image_product, 'save_image') }}"
{% endfor %}
],
"description": "{{ Product.description_list | default(Product.description_detail) | replace({'\n': '', '\r': ''}) | slice(0,300) }}",
{% if Product.code_min %}
"sku": "{{ Product.code_min }}",
{% endif %}
"offers": {
"@type": "Offer",
"url": "{{ url('product_detail', {'id': Product.id}) }}",
"priceCurrency": "{{ eccube_config.currency }}",
"price": {{ Product.getPrice02IncTaxMin ? Product.getPrice02IncTaxMin : 0}},
"availability": "{{ Product.stock_find ? "InStock" : "OutOfStock" }}"
}
}
</script>
{% endblock %}
{% block main %}
<div class="contact_wrapper body-section-ui">
<!-- breadcrumb -->
<nav class="breadcrumb" aria-label="パンくずリスト">
<a class="breadcrumb__item" href="{{ url('homepage') }}">ホーム</a>
<img class="breadcrumb__separator" src="{{ asset('assets/img/default/icons/icon-breadcrumb-separator.svg') }}" alt="" width="12" height="12" />
{% if Product.ProductCategories is not empty %}
{% set firstCategory = Product.ProductCategories[0].Category %}
{% for Category in firstCategory.path %}
<a class="breadcrumb__item" href="{{ url('product_list') }}">備長炭一覧</a>
{% if not loop.last %}
<img class="breadcrumb__separator" src="{{ asset('assets/img/default/icons/icon-breadcrumb-separator.svg') }}" alt="" width="12" height="12" />
{% endif %}
{% endfor %}
<img class="breadcrumb__separator" src="{{ asset('assets/img/default/icons/icon-breadcrumb-separator.svg') }}" alt="" width="12" height="12" />
{% endif %}
<span class="breadcrumb__item breadcrumb__item--current">{{ Product.name }}</span>
</nav>
<div class="container-1200">
<!-- product detail -->
<div class="product-detail product-layout">
<!-- switch from mobile to desktop -->
{# #}
<div class="product-info__desktop product-layout-header">
<!-- Product Title -->
<div class="product-info__header">
{% if Product.ProductCategories is not empty %}
{% set firstCategory = Product.ProductCategories[0].Category %}
<div class="product-info__category-badge">{{ firstCategory.name }}</div>
{% endif %}
<h1 class="product-info__title">{{ Product.name }}</h1>
</div>
<!-- Price -->
<div class="product-info__price">
{% set priceIncTax = Product.getPrice02IncTaxMin %}
{% set priceExTax = Product.getPrice02Min %}
{% if Product.hasProductClass %}
{% if Product.getPrice02IncTaxMin == Product.getPrice02IncTaxMax %}
<span class="product-info__price-main">{{ priceIncTax|number_format(0, '.', ',') }}</span>
{% else %}
<span class="product-info__price-main">{{ priceIncTax|number_format(0, '.', ',') }}</span>
{% endif %}
{% else %}
<span class="product-info__price-main">{{ priceIncTax|number_format(0, '.', ',') }}</span>
{% endif %}
<span class="product-info__price-unit">円</span>
{% if priceExTax %}
<span class="product-info__price-tax">(税抜 {{ priceExTax|number_format(0, '.', ',') }}円)</span>
{% endif %}
</div>
<div class="product-info__product-info-wrapper">
<form class="product-info__form" action="{{ url('product_add_cart', {id:Product.id}) }}" method="post" id="form1" name="form1">
{% if form.classcategory_id1 is defined %}
<div class="product-info__variant-wrapper" style="margin-bottom: 16px;">
<div class="product-info__quantity-label" style="margin-bottom: 8px;">{{ form.classcategory_id1.vars.label|default('規格1') }}</div>
<div class="ec-select">
{{ form_widget(form.classcategory_id1) }}
{{ form_errors(form.classcategory_id1) }}
</div>
{% if form.classcategory_id2 is defined %}
<div class="product-info__quantity-label" style="margin-bottom: 8px; margin-top: 16px;">{{ form.classcategory_id2.vars.label|default('規格2') }}</div>
<div class="ec-select">
{{ form_widget(form.classcategory_id2) }}
{{ form_errors(form.classcategory_id2) }}
</div>
{% endif %}
</div>
{% endif %}
<!-- Quantity Selector -->
{% if form.quantity is defined %}
<div class="product-info__quantity-wrapper">
<div class="product-info__quantity-label">数量</div>
<div class="product-info__quantity">
<button class="product-info__quantity-btn product-info__quantity-btn--minus" type="button">
<svg width="20" height="20" viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg">
<rect x="3" y="8" width="14" height="2" fill="#353535"/>
</svg>
</button>
<span class="product-info__quantity-value">{{ form.quantity.vars.value|default(1) }}</span>
{{ form_widget(form.quantity, {'attr': {'style': 'display: none;'}}) }}
{{ form_errors(form.quantity) }}
<button class="product-info__quantity-btn product-info__quantity-btn--plus" type="button">
<svg width="20" height="20" viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M9 2H11V18H9V2Z" fill="#353535"/>
<path d="M2 9H18V11H2V9Z" fill="#353535"/>
</svg>
</button>
</div>
</div>
{% endif %}
<!-- Add to Cart Button -->
{% if Product.stock_find %}
<button class="product-info__cart-btn add-cart" type="button">
<span class="product-info__cart-text">
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M6 18C5.46957 18 4.96086 18.2107 4.58579 18.5858C4.21071 18.9609 4 19.4696 4 20C4 20.5304 4.21071 21.0391 4.58579 21.4142C4.96086 21.7893 5.46957 22 6 22C6.53043 22 7.03914 21.7893 7.41421 21.4142C7.78929 21.0391 8 20.5304 8 20C8 18.89 7.11 18 6 18ZM22 2H18.73L17.79 4H3C2.73478 4 2.48043 4.10536 2.29289 4.29289C2.10536 4.48043 2 4.73478 2 5C2 5.17 2.05 5.34 2.12 5.5L5.7 11.97C6.04 12.58 6.7 13 7.45 13H14.9L15.8 14.63L15.83 14.75C15.83 14.8163 15.8037 14.8799 15.7568 14.9268C15.7099 14.9737 15.6463 15 15.58 15H4V17H16C16.5304 17 17.0391 16.7893 17.4142 16.4142C17.7893 16.0391 18 15.5304 18 15C18 14.65 17.91 14.32 17.76 14.04L16.4 11.59L20 4H22V2ZM16 18C15.4696 18 14.9609 18.2107 14.5858 18.5858C14.2107 18.9609 14 19.4696 14 20C14 20.5304 14.2107 21.0391 14.5858 21.4142C14.9609 21.7893 15.4696 22 16 22C16.5304 22 17.0391 21.7893 17.4142 21.4142C17.7893 21.0391 18 20.5304 18 20C18 18.89 17.11 18 16 18ZM7 11L4.22 6H16.86L14.5 11H7Z" fill="#F5F5F5"/>
</svg>
<span>カートに入れる</span>
</span>
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M9 6L15 12L9 18" stroke="#F5F5F5" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
</svg>
</button>
{% else %}
<button class="product-info__cart-btn product-info__cart-btn--disabled" type="button" disabled="disabled">
<span class="product-info__cart-text">
<span>在庫切れ</span>
</span>
</button>
{% endif %}
{{ form_rest(form) }}
</form>
</div>
</div>
<div class="product-detail__left product-layout-media">
<div class="product-images">
<!-- Main Swiper -->
<div class="swiper product-images__main-swiper" id="product-detail-main-swiper-desktop">
<div class="swiper-wrapper">
{% for ProductImage in Product.ProductImage %}
<div class="swiper-slide">
<img src="{{ asset(ProductImage, 'save_image') }}" alt="{{ Product.name }}" />
</div>
{% else %}
<div class="swiper-slide">
<img src="{{ asset(''|no_image_product, 'save_image') }}" alt="{{ Product.name }}" />
</div>
{% endfor %}
</div>
</div>
<!-- Thumbnails Swiper -->
<div class="swiper product-images__thumbnails-swiper" id="product-detail-thumbnails-swiper-desktop">
<div class="swiper-wrapper">
{% for ProductImage in Product.ProductImage %}
<div class="swiper-slide">
<div class="product-images__thumbnail">
<img src="{{ asset(ProductImage, 'save_image') }}" alt="Thumbnail {{ loop.index }}" />
</div>
</div>
{% else %}
<div class="swiper-slide">
<div class="product-images__thumbnail">
<img src="{{ asset(''|no_image_product, 'save_image') }}" alt="Thumbnail" />
</div>
</div>
{% endfor %}
</div>
</div>
</div>
</div>
<div class="product-detail__right product-layout-detail">
<div class="product-info">
<!-- Product Details Section -->
{% if Product.product_details_info %}
<div class="product-info__section">
<div class="product-info__section-header">
<svg width="28" height="28" viewBox="0 0 28 28" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M18.0833 16.3333H17.1617L16.835 16.0183C18.0179 14.6464 18.6681 12.8948 18.6667 11.0833C18.6667 9.58349 18.2219 8.11734 17.3886 6.87026C16.5554 5.62319 15.371 4.65121 13.9854 4.07725C12.5997 3.50329 11.0749 3.35311 9.6039 3.64572C8.13288 3.93832 6.78166 4.66056 5.72111 5.72111C4.66056 6.78166 3.93832 8.13288 3.64572 9.6039C3.35311 11.0749 3.50329 12.5997 4.07725 13.9854C4.65121 15.371 5.62319 16.5554 6.87026 17.3886C8.11734 18.2219 9.58349 18.6667 11.0833 18.6667C12.9617 18.6667 14.6883 17.9783 16.0183 16.835L16.3333 17.1617V18.0833L22.1667 23.905L23.905 22.1667L18.0833 16.3333ZM11.0833 16.3333C8.17834 16.3333 5.83334 13.9883 5.83334 11.0833C5.83334 8.17834 8.17834 5.83334 11.0833 5.83334C13.9883 5.83334 16.3333 8.17834 16.3333 11.0833C16.3333 13.9883 13.9883 16.3333 11.0833 16.3333Z" fill="#353535"/>
</svg>
<h3 class="product-info__section-title">商品詳細</h3>
</div>
<div class="product-info__section-content">
<!-- Product Specs -->
<div class="product-info__specs">
{% set details_lines = Product.product_details_info|split("\n") %}
{% for line in details_lines %}
{% if line|trim %}
{% set normalized_line = line|trim|replace({' ': ' '}) %}
{% set parts = normalized_line|split(' ', 2) %}
{% if parts|length >= 2 %}
<div class="product-info__spec-row">
<div class="product-info__spec-label">{{ parts[0] }}</div>
<div class="product-info__spec-value">{{ parts[1] }}</div>
</div>
{% endif %}
{% endif %}
{% endfor %}
</div>
</div>
</div>
{% endif %}
<!-- Product Description Section -->
{% if Product.description_detail %}
<div class="product-info__section">
<div class="product-info__section-header">
<svg width="28" height="28" viewBox="0 0 28 28" fill="none" xmlns="http://www.w3.org/2000/svg">
<rect x="2.5" y="5.5" width="4" height="4" rx="2" fill="#353535"/>
<rect x="2.5" y="12.5" width="4" height="4" rx="2" fill="#353535"/>
<rect x="2.5" y="19.5" width="4" height="4" rx="2" fill="#353535"/>
<rect x="9" y="6" width="15" height="3" rx="1.5" fill="#353535"/>
<rect x="9" y="13" width="11" height="3" rx="1.5" fill="#353535"/>
<rect x="9" y="20" width="6" height="3" rx="1.5" fill="#353535"/>
</svg>
<h3 class="product-info__section-title">商品説明</h3>
</div>
<div class="product-info__description">
{{ Product.description_detail|raw|nl2br }}
</div>
</div>
{% endif %}
<!-- Notes Section -->
{% if Product.comment_detail %}
<div class="product-info__section">
<div class="product-info__section-header">
<svg width="28" height="28" viewBox="0 0 28 28" fill="none" xmlns="http://www.w3.org/2000/svg">
<circle cx="9.78125" cy="13" r="1.5" fill="#353535"/>
<circle cx="14" cy="13" r="1.5" fill="#353535"/>
<circle cx="18.2188" cy="13" r="1.5" fill="#353535"/>
<mask id="path-4-inside-1_211_2082" fill="white">
<path d="M14 4C20.0751 4 24 6.76238 24 12.5C24 18.2376 20.0751 21 14 21C13.4263 21 12.8718 20.9743 12.3379 20.9248L6 25V18.2246C4.71403 16.844 4 14.9428 4 12.5C4 6.76238 7.92487 4 14 4Z"/>
</mask>
<path d="M24 12.5L26 12.5V12.5H24ZM12.3379 20.9248L12.5224 18.9333L11.836 18.8698L11.2562 19.2425L12.3379 20.9248ZM6 25H4V28.6637L7.08167 26.6823L6 25ZM6 18.2246H8V17.4375L7.46349 16.8615L6 18.2246ZM4 12.5L2 12.5L4 12.5ZM14 4V6C16.771 6 18.7605 6.63437 20.0205 7.65343C21.2128 8.61777 22 10.1258 22 12.5H24H26C26 9.13659 24.8248 6.39461 22.5359 4.54335C20.3146 2.74682 17.3041 2 14 2V4ZM24 12.5L22 12.5C22 14.8742 21.2128 16.3822 20.0205 17.3466C18.7605 18.3656 16.771 19 14 19V21V23C17.3041 23 20.3146 22.2532 22.5359 20.4567C24.8248 18.6054 26 15.8634 26 12.5L24 12.5ZM14 21V19C13.486 19 12.9931 18.9769 12.5224 18.9333L12.3379 20.9248L12.1534 22.9163C12.7504 22.9716 13.3665 23 14 23V21ZM12.3379 20.9248L11.2562 19.2425L4.91833 23.3177L6 25L7.08167 26.6823L13.4196 22.6071L12.3379 20.9248ZM6 25H8V18.2246H6H4V25H6ZM6 18.2246L7.46349 16.8615C6.58412 15.9174 6 14.5356 6 12.5L4 12.5L2 12.5C2 15.3501 2.84393 17.7706 4.53651 19.5878L6 18.2246ZM4 12.5H6C6 10.1258 6.78723 8.61777 7.97954 7.65343C9.23952 6.63437 11.229 6 14 6V4V2C10.6959 2 7.68535 2.74682 5.46411 4.54335C3.1752 6.39461 2 9.13659 2 12.5H4Z" fill="#353535" mask="url(#path-4-inside-1_211_2082)"/>
</svg>
<h3 class="product-info__section-title product-info__section-title-20">備考</h3>
</div>
<div class="product-info__notes">
{{ Product.comment_detail|raw|nl2br }}
</div>
</div>
{% endif %}
</div>
</div>
</div>
<!-- btn back to product list -->
<div class="product-detail-button-wrapper">
<button class="btn_primary" type="button" onclick="history.back()">
<img class="btn_primary-icon" src="{{ asset('assets/img/default/icons/icon-arrow-nav-left.svg') }}" alt="" width="24" height="24" />
<span class="btn_primary-text">前のページに戻る</span>
</button>
</div>
</div>
</div>
<!-- Modal -->
<div class="ec-modal">
<div class="ec-modal-overlay" onclick="reloadPage()">
<div class="ec-modal-wrap">
<span class="ec-modal-close" onclick="reloadPage()"><span class="ec-icon"><img src="{{ asset('assets/icon/cross-dark.svg') }}" alt=""/></span></span>
<div id="ec-modal-header" class="text-center">{{ 'front.product.add_cart_complete'|trans }}</div>
<div class="ec-modal-box">
<div class="ec-role ec-role-modal-flex">
<span onclick="reloadPage()" class="ec-inlineBtn--cancel">{{ 'front.product.continue'|trans }}</span>
<a href="{{ url('cart') }}" class="ec-inlineBtn--action">{{ 'common.go_to_cart'|trans }}</a>
</div>
</div>
</div>
</div>
</div>
{% if Product.freearea %}
<div class="ec-productRole__description">
{{ include(template_from_string(Product.freearea)) }}
</div>
{% endif %}
{% endblock %}