{#
* Plugin Name : ProductOption
*
* Copyright (C) BraTech Co., Ltd. All Rights Reserved.
* http://www.bratech.co.jp/
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
#}
{% extends '@admin/default_frame.twig' %}
{% set menus = ['product', 'option'] %}
{% form_theme form '@admin/Form/bootstrap_4_horizontal_layout.html.twig' %}
{% block title %}{{ 'productoption.admin.nav.product.option'|trans }}{% endblock %}
{% block sub_title %}{{ 'admin.product.product_management'|trans }}{% endblock %}
{% block javascript %}
<script src="https://ajax.googleapis.com/ajax/libs/jqueryui/1.12.1/jquery-ui.min.js"></script>
<script>
$(document).on('drop dragover', function(e) {
e.preventDefault();
});
$(function() {
// ファイルアップロード
var inputFileElement = document.querySelector('input[type=file]');
FilePond.setOptions({
server: {
process: {
url: '{{ path('admin_product_option_category_image_process') }}',
headers: {
'ECCUBE-CSRF-TOKEN': $('meta[name="eccube-csrf-token"]').attr('content'),
'X-Requested-With': 'XMLHttpRequest'
}
},
load: {
url: '{{ path('admin_product_option_category_image_load') }}?source=',
headers: {
'X-Requested-With': 'XMLHttpRequest'
}
},
revert: {
url: '{{ path('admin_product_option_category_image_revert') }}',
headers: {
'ECCUBE-CSRF-TOKEN': $('meta[name="eccube-csrf-token"]').attr('content'),
'X-Requested-With': 'XMLHttpRequest'
}
}
}
});
var pond = FilePond.create(inputFileElement, {
allowFileTypeValidation: true,
acceptedFileTypes: [
'image/gif',
'image/png',
'image/jpeg'
],
allowFileSizeValidation: true,
maxFileSize: 10000000,
maxFiles: 10,
allowBrowse: true,
allowDrop: true,
allowReorder: true,
labelIdle: '<i class="fa fa-cloud-upload fa-3x text-ec-lightGray mx-3 align-middle" aria-hidden="true" style="font-size: 40px"></i>{{ 'admin.common.drag_and_drop_image_description'|trans }}<span class="filepond--label-action">{{ 'admin.common.file_select'|trans }}</span>',
// 保存されている画像のロード
files: [
{% for image in form.images %}
{
source: '{{ image.vars.value }}',
options: {
type: 'local'
}
},
{% endfor %}
// 追加してすぐの画像のロード. バリデーションエラーの場合など.
{% for add_image in form.add_images %}
{
source: '{{ add_image.vars.value }}',
options: {
type: 'local'
}
},
{% endfor %}
]
});
// 画像が追加されたら add_images にファイル名を追加する
var proto_add = '{{ form_widget(form.add_images.vars.prototype) }}';
pond.on('processfile', function(error, file) {
if (error) {
console.log(error);
} else {
$('#upload-zone').append(
$(proto_add.replace(/__name__/g, file.id))
.val(file.serverId)
.addClass('add_images')
);
}
});
// 画像が削除されたら delete_images にファイル名を追加する
var proto_del = '{{ form_widget(form.delete_images.vars.prototype) }}';
pond.on('removefile', function(error, file) {
if (error) {
console.log(error);
} else {
// file.serverId にはアップロードしたファイル名が格納される.
// DBに登録されると URL path が入るためファイル名のみ抜き出す.
if (file.serverId) {
$('#upload-zone').append(
$(proto_del.replace(/__name__/g, file.id))
.val(file.serverId.split('/').pop())
.addClass('del_images')
);
}
// 追加してすぐ削除した画像があれば削除する
$('#upload-zone').find('#admin_option_category_add_images_' + file.id).remove(); // 追加してすぐ削除した画像
$('#upload-zone').find('.add_images[value="' + file.filename + '"]').remove(); // 追加後, バリデーションエラーが発生した後に削除した画像
}
});
// バリデーションエラーが出た場合に画像を保持するための hidden を追加しておく
var proto_image = '{{ form_widget(form.images.vars.prototype) }}';
{% for image in form.images %}
$('#upload-zone').append(
$(proto_image.replace(/__name__/g, '{{ loop.index0 }}'))
.val('{{ image.vars.value }}')
.addClass('images')
);
{% endfor %}
{% for add_image in form.add_images %}
$('#upload-zone').append(
$('{{ form_widget(add_image) }}')
.val('{{ add_image.vars.value }}')
.addClass('add_images')
);
{% endfor %}
{% for delete_image in form.delete_images %}
$('#upload-zone').append(
$('{{ form_widget(delete_image) }}').addClass('del_images')
);
{% endfor %}
var oldSortNos = [];
// 画面の中のsortNo一覧を保持
$('.sortable-item').each(function() {
oldSortNos.push(this.dataset.sortNo);
});
// rsort
oldSortNos.sort(function(a, b) {
return a - b;
}).reverse();
$('.sortable-container').sortable({
items: '> .sortable-item',
cursor: 'move',
update: function(e, ui) {
$('body').append($('<div class="modal-backdrop show"></div>'));
updateSortNoCategory();
}
});
var updateSortNoCategory = function() {
// 並び替え後にsortNoを更新
var newSortNos = {};
var i = 0;
$('.sortable-item').each(function() {
newSortNos[this.dataset.optionId] = oldSortNos[i];
i++;
});
$.ajax({
url: '{{ url('admin_product_option_category_sort_no_move') }}',
type: 'POST',
headers: {
'x-csrf-token': $('meta[name="x-csrf-token"]').attr('content')
},
data: newSortNos
}).done(function() {
// remove class disable
$('a.up.disabled').removeClass('disabled');
$('a.down.disabled').removeClass('disabled');
// First element
$('.sortable-item > li:nth-child(2) > div > div.col-auto.text-end > a.up').addClass('disabled');
}).always(function() {
redrawDisableAllows();
$('.modal-backdrop').remove();
});
};
// 最初と最後の↑↓を再描画
var redrawDisableAllows = function() {
var items = $('.sortable-item');
items.find('a').removeClass('disabled');
items.first().find('a.up').addClass('disabled');
items.last().find('a.down').addClass('disabled');
};
$('.sortable-item a.up').click(function(e) {
e.preventDefault();
var current = $(this).parents('.list-group-item');
current.prev().before(current);
$('body').append($('<div class="modal-backdrop show"></div>'));
updateSortNoCategory();
});
$('.sortable-item a.down').click(function(e) {
e.preventDefault();
var current = $(this).parents('.list-group-item');
current.next().after(current);
$('body').append($('<div class="modal-backdrop show"></div>'));
updateSortNoCategory();
});
var confirmFormChange = function(form, target, modal) {
var returnLink = form.find('input[type="hidden"][name*="return_link"]'),
saveBtn = modal.find('a[data-action="save"]'),
cancelBtn = modal.find('a[data-action="cancel"]');
modal.on('hidden.bs.modal', function() {
returnLink.val('');
});
saveBtn.on('click', function() {
returnLink.val($(this).data('return-link'));
form.submit();
});
target.on('click', function() {
modal.find('.modal-body .screen-name').text($(this).attr('title'));
modal.modal('show');
saveBtn.data('return-link', $(this).attr('href'));
cancelBtn.attr('href', $(this).attr('href'));
return false;
});
};
confirmFormChange($('#form1'), $('a[data-action="confirm"]'), $('#confirmFormChangeModal'))
// 削除モーダルのhrefとmessageの変更
$('#DeleteModal').on('shown.bs.modal', function(event) {
var target = $(event.relatedTarget);
// hrefの変更
$(this).find('[data-method="delete"]').attr('href', target.data('url'));
// messageの変更
$(this).find('p.modal-message').text(target.data('message'));
});
});
</script>
{% endblock javascript %}
{% block main %}
<!-- 移動確認モーダル-->
<div class="modal fade" id="confirmFormChangeModal" tabindex="-1" role="dialog"
aria-labelledby="confirmFormChangeModal" aria-hidden="true">
<div class="modal-dialog" role="document">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title">{{ 'admin.common.move_to_confirm_title'|trans }}</h5>
<button class="btn-close" type="button" data-dismiss="modal" aria-label="Close">
<span aria-hidden="true">×</span>
</button>
</div>
<div class="modal-body">
<p class="screen-name"></p>
</div>
<div class="modal-footer">
<a class="btn btn-ec-conversion" data-action="save" href="javascript:void(0)">
{{ 'admin.common.move_to_confirm_save_and_move'|trans }}
</a>
<a class="btn btn-ec-sub" data-action="cancel" href="javascript:void(0)">
{{ 'admin.common.move_to_confirm_move_only'|trans }}
</a>
</div>
</div>
</div>
</div>
<form role="form" class="form-row" name="form1" id="form1" method="post" action="?">
{{ form_widget(form._token) }}
{{ form_widget(form.return_link) }}
<div class="c-contentsArea__cols">
<div class="c-contentsArea__primaryCol">
<div class="c-primaryCol">
<div class="card rounded border-0 mb-4">
<div class="card-header">
<div class="row">
<div class="col-8"><span class="card-title">{{ 'productoption.admin.nav.product.option.category'|trans }}:{{ Option.backend_name }}</span>
</div>
<div class="col-4 text-end">
<a data-bs-toggle="collapse" href="#ordererInfo"
aria-expanded="false" aria-controls="ordererInfo">
<i class="fa fa-angle-up fa-lg"></i>
</a>
</div>
</div>
</div>
<div class="collapse show ec-cardCollapse" id="ordererInfo">
<div class="card-body">
<div class="row mb-2">
<div class="col-3">
<span>{{ form.children.name.vars.label }}</span>
<span class="badge bg-primary ms-1">{{ 'admin.common.required'|trans }}</span>
</div>
<div class="col">
{{ form_widget(form.name) }}
{{ form_errors(form.name) }}
</div>
</div>
<div class="row mb-2">
<div class="col-3">
<span>{{ form.children.value.vars.label }}</span>
</div>
<div class="col">
{{ form_widget(form.value) }}
{{ form_errors(form.value) }}
</div>
</div>
<div class="row mb-2">
<div class="col-3">
<span>{{ form.children.delivery_free_flg.vars.label }}</span>
</div>
<div class="col">
{{ form_widget(form.delivery_free_flg) }}
{{ form_errors(form.delivery_free_flg) }}
</div>
</div>
<div class="row mb-2">
<div class="col-3">
<span>{{ form.children.description.vars.label }}</span>
</div>
<div class="col">
{{ form_widget(form.description) }}
{{ form_errors(form.description) }}
</div>
</div>
<div class="row">
<div class="col-3">
<div class="d-inline-block">
<span>{{ form.children.option_image.vars.label }}</span>
</div>
</div>
<div class="col mb-2">
<p id="message"></p>
<div id="upload-zone" class="media rounded">
<div class="media-body">
{{ form_widget(form.option_image, { attr : { style : 'display:none;' } }) }}
{{ form_errors(form.option_image) }}
</div><!-- /.media-body -->
</div><!-- /.media -->
</div>
</div>
{% if Option.type != constant('Plugin\\ProductOption42\\Entity\\Option::CHECKBOX_TYPE') %}
<div class="row mb-2">
<div class="col-3">
<span>{{ form.children.disable_flg.vars.label }}</span>
</div>
<div class="col">
{{ form_widget(form.disable_flg) }}
{{ form_errors(form.disable_flg) }}
</div>
</div>
{% endif %}
<div class="row mb-2">
<div class="col-3">
<span>{{ form.children.init_flg.vars.label }}</span>
</div>
<div class="col">
{{ form_widget(form.init_flg) }}
{{ form_errors(form.init_flg) }}
</div>
</div>
<div class="row mb-2">
<div class="col-3">
<span>{{ form.children.hidden_flg.vars.label }}</span>
</div>
<div class="col">
{{ form_widget(form.hidden_flg) }}
{{ form_errors(form.hidden_flg) }}
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
<div class="c-conversionArea">
<div class="c-conversionArea__container">
<div class="row justify-content-between align-items-center">
<div class="col-6">
<div class="c-conversionArea__leftBlockItem">
<a class="c-baseLink" href="{{ path('admin_product_option') }}"
data-action="confirm" title="{{ 'admin.common.move_to_confirm_message'|trans({'%name%' : 'productoption.admin.product.option.category.list'|trans }) }}">
<i class="fa fa-backward" aria-hidden="true"></i><span>{{ 'productoption.admin.product.option.category.list'|trans }}</span>
</a>
</div>
</div>
<div class="col-6">
<div id="ex-conversion-action" class="row align-items-center justify-content-end">
<div class="col-auto">
<a href="{{ url('admin_product_option_category_new', {option_id: Option.id}) }}">
<button class="btn btn-ec-regular"
type="button">{{ 'productoption.admin.common.new'|trans }}</button>
</a>
</div>
<div class="col-auto">
<button class="btn btn-ec-conversion px-5"
type="submit">{{ 'productoption.admin.common.save'|trans }}</button>
</div>
</div>
</div>
</div>
</div>
</div>
</form>
{% if OptionCategories|length > 0 %}
<div class="c-contentsArea__cols">
<div class="c-contentsArea__primaryCol">
<div class="c-primaryCol">
<div class="card rounded border-0 mb-4">
<div class="card-body p-0">
<div class="card rounded border-0 mb-2">
<ul class="list-group list-group-flush sortable-container">
<li class="list-group-item">
<div class="row">
<div class="col-auto"><strong> </strong></div>
<div class="col-3"><strong>{{ 'productoption.admin.product.option.category.name'|trans }}</strong></div>
<div class="col-2"><strong>{{ 'productoption.admin.product.option.category.price'|trans }}</strong></div>
<div class="col-1"><strong>{{ 'productoption.admin.product.option.category.init_disp'|trans }}</strong></div>
<div class="col-1"><strong>{{ 'productoption.admin.product.option.category.disable_disp'|trans }}</strong></div>
<div class="col-auto"><strong>{{ 'productoption.admin.product.option.category.hidden_disp'|trans }}</strong></div>
</div>
</li>
{% for OptionCategory in OptionCategories %}
<li id="ex-class_name-{{ OptionCategory.id }}" class="list-group-item sortable-item" data-option-id="{{ OptionCategory.id }}" data-sort-no="{{ OptionCategory.sortNo }}">
<div class="row mode-view">
<div class="col-auto d-flex align-items-center"><i class="fa fa-bars text-ec-gray"></i></div>
<div class="col-3 d-flex align-items-center">
{{ OptionCategory.name }}
</div>
<div class="col-2 d-flex align-items-center">
{% if OptionCategory.value|length > 0 %}¥ {{ OptionCategory.value|number_format }}{% endif %}
</div>
<div class="col-1 d-flex align-items-center">
{% if OptionCategory.init_flg %}〇{% endif %}
</div>
<div class="col-1 d-flex align-items-center">
{% if OptionCategory.disable_flg %}〇{% endif %}
</div>
<div class="col d-flex align-items-center">
{% if OptionCategory.hidden_flg %}〇{% endif %}
</div>
<div class="col-auto text-end">
<a class="btn btn-ec-actionIcon mr-3 up {% if loop.first %}disabled{% endif %}" href="" data-toggle="tooltip" data-placement="top" title="{{ 'productoption.admin.common.up'|trans }}">
<i class="fa fa-arrow-up fa-lg text-secondary"></i>
</a>
<a class="btn btn-ec-actionIcon mr-3 down {% if loop.last %}disabled{% endif %}" href="" data-toggle="tooltip" data-placement="top" title="{{ 'productoption.admin.common.down'|trans }}">
<i class="fa fa-arrow-down fa-lg text-secondary"></i>
</a>
<a class="btn btn-ec-actionIcon mr-3 action-edit" data-toggle="tooltip" data-placement="top" title="{{ 'productoption.admin.product.option.edit'|trans }}" href="{{ url('admin_product_option_category_edit', {option_id : Option.id, id : OptionCategory.id }) }}">
<i class="fa fa-pencil fa-lg text-secondary"></i>
</a>
<div class="d-inline-block mr-2" data-tooltip="true"
data-placement="top" title="{{ 'admin.common.delete'|trans }}">
<a class="btn btn-ec-actionIcon"
data-bs-toggle="modal" data-bs-target="#DeleteModal"
data-url="{{ url('admin_product_option_category_delete', {'option_id' : Option.id ,'id' : OptionCategory.id}) }}"
data-message="{{ 'productoption.admin.product.option.category.modal.body'|trans }}">
<i class="fa fa-close fa-lg text-secondary"></i>
</a>
</div>
</div>
</div>
</li>
{% endfor %}
</ul>
<!-- 削除モーダル -->
<div class="modal fade" id="DeleteModal" tabindex="-1" role="dialog"
aria-labelledby="DeleteModal" aria-hidden="true">
<div class="modal-dialog" role="document">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title font-weight-bold">
{{ 'productoption.admin.product.option.category.modal.header'|trans }}
</h5>
<button class="btn-close" type="button" data-bs-dismiss="modal" aria-label="Close">
</button>
</div>
<div class="modal-body text-start">
<p class="text-start modal-message"><!-- jsでメッセージを挿入 --></p>
</div>
<div class="modal-footer">
<button class="btn btn-ec-sub" type="button" data-bs-dismiss="modal">
{{ 'admin.common.cancel'|trans }}
</button>
<a class="btn btn-ec-delete" href="#" {{ csrf_token_for_anchor() }}
data-method="delete" data-confirm="false">
{{ 'admin.common.delete'|trans }}
</a>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
<p>{{ 'productoption.admin.product.option.category.sortable'|trans }}</p>
</div>
</div>
</div>
{% else %}
<p>{{ 'productoption.admin.product.option.category.no_item'|trans }}</p>
{% endif %}
{% endblock %}