app/Plugin/ProductOption42/Resource/template/admin/Product/option_category.twig line 1

Open in your IDE?
  1. {#
  2. * Plugin Name : ProductOption
  3. *
  4. * Copyright (C) BraTech Co., Ltd. All Rights Reserved.
  5. * http://www.bratech.co.jp/
  6. *
  7. * For the full copyright and license information, please view the LICENSE
  8. * file that was distributed with this source code.
  9. #}
  10. {% extends '@admin/default_frame.twig' %}
  11. {% set menus = ['product', 'option'] %}
  12. {% form_theme form '@admin/Form/bootstrap_4_horizontal_layout.html.twig' %}
  13. {% block title %}{{ 'productoption.admin.nav.product.option'|trans }}{% endblock %}
  14. {% block sub_title %}{{ 'admin.product.product_management'|trans }}{% endblock %}
  15. {% block javascript %}
  16.     <script src="https://ajax.googleapis.com/ajax/libs/jqueryui/1.12.1/jquery-ui.min.js"></script>
  17.     <script>
  18.         $(document).on('drop dragover', function(e) {
  19.             e.preventDefault();
  20.         });
  21.         $(function() {
  22.             // ファイルアップロード
  23.             var inputFileElement = document.querySelector('input[type=file]');
  24.             FilePond.setOptions({
  25.                 server: {
  26.                     process: {
  27.                         url: '{{ path('admin_product_option_category_image_process') }}',
  28.                         headers: {
  29.                             'ECCUBE-CSRF-TOKEN': $('meta[name="eccube-csrf-token"]').attr('content'),
  30.                             'X-Requested-With': 'XMLHttpRequest'
  31.                         }
  32.                     },
  33.                     load: {
  34.                         url: '{{ path('admin_product_option_category_image_load') }}?source=',
  35.                         headers: {
  36.                             'X-Requested-With': 'XMLHttpRequest'
  37.                         }
  38.                     },
  39.                     revert: {
  40.                         url: '{{ path('admin_product_option_category_image_revert') }}',
  41.                         headers: {
  42.                             'ECCUBE-CSRF-TOKEN': $('meta[name="eccube-csrf-token"]').attr('content'),
  43.                             'X-Requested-With': 'XMLHttpRequest'
  44.                         }
  45.                     }
  46.                 }
  47.             });
  48.             var pond = FilePond.create(inputFileElement, {
  49.                 allowFileTypeValidation: true,
  50.                 acceptedFileTypes: [
  51.                     'image/gif',
  52.                     'image/png',
  53.                     'image/jpeg'
  54.                 ],
  55.                 allowFileSizeValidation: true,
  56.                 maxFileSize: 10000000,
  57.                 maxFiles: 10,
  58.                 allowBrowse: true,
  59.                 allowDrop: true,
  60.                 allowReorder: true,
  61.                 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>',
  62.                 // 保存されている画像のロード
  63.                 files: [
  64.                     {% for image in form.images %}
  65.                     {
  66.                         source: '{{ image.vars.value }}',
  67.                         options: {
  68.                             type: 'local'
  69.                         }
  70.                     },
  71.                     {% endfor %}
  72.                     // 追加してすぐの画像のロード. バリデーションエラーの場合など.
  73.                     {% for add_image in form.add_images %}
  74.                     {
  75.                         source: '{{ add_image.vars.value }}',
  76.                         options: {
  77.                             type: 'local'
  78.                         }
  79.                     },
  80.                     {% endfor %}
  81.                 ]
  82.             });
  83.             // 画像が追加されたら add_images にファイル名を追加する
  84.             var proto_add = '{{ form_widget(form.add_images.vars.prototype) }}';
  85.             pond.on('processfile', function(error, file) {
  86.                 if (error) {
  87.                     console.log(error);
  88.                 } else {
  89.                     $('#upload-zone').append(
  90.                         $(proto_add.replace(/__name__/g, file.id))
  91.                             .val(file.serverId)
  92.                             .addClass('add_images')
  93.                     );
  94.                 }
  95.             });
  96.             // 画像が削除されたら delete_images にファイル名を追加する
  97.             var proto_del = '{{ form_widget(form.delete_images.vars.prototype) }}';
  98.             pond.on('removefile', function(error, file) {
  99.                 if (error) {
  100.                     console.log(error);
  101.                 } else {
  102.                     // file.serverId にはアップロードしたファイル名が格納される.
  103.                     // DBに登録されると URL path が入るためファイル名のみ抜き出す.
  104.                     if (file.serverId) {
  105.                         $('#upload-zone').append(
  106.                             $(proto_del.replace(/__name__/g, file.id))
  107.                                 .val(file.serverId.split('/').pop())
  108.                                 .addClass('del_images')
  109.                         );
  110.                     }
  111.                     // 追加してすぐ削除した画像があれば削除する
  112.                     $('#upload-zone').find('#admin_option_category_add_images_' + file.id).remove(); // 追加してすぐ削除した画像
  113.                     $('#upload-zone').find('.add_images[value="' + file.filename + '"]').remove(); // 追加後, バリデーションエラーが発生した後に削除した画像
  114.                 }
  115.             });
  116.             // バリデーションエラーが出た場合に画像を保持するための hidden を追加しておく
  117.             var proto_image = '{{ form_widget(form.images.vars.prototype) }}';
  118.             {% for image in form.images %}
  119.                 $('#upload-zone').append(
  120.                     $(proto_image.replace(/__name__/g, '{{ loop.index0 }}'))
  121.                         .val('{{ image.vars.value }}')
  122.                         .addClass('images')
  123.                 );
  124.             {% endfor %}
  125.             {% for add_image in form.add_images %}
  126.                 $('#upload-zone').append(
  127.                     $('{{ form_widget(add_image) }}')
  128.                         .val('{{ add_image.vars.value }}')
  129.                         .addClass('add_images')
  130.                 );
  131.             {% endfor %}
  132.             {% for delete_image in form.delete_images %}
  133.                 $('#upload-zone').append(
  134.                     $('{{ form_widget(delete_image) }}').addClass('del_images')
  135.                 );
  136.             {% endfor %}
  137.             var oldSortNos = [];
  138.             // 画面の中のsortNo一覧を保持
  139.             $('.sortable-item').each(function() {
  140.                 oldSortNos.push(this.dataset.sortNo);
  141.             });
  142.             // rsort
  143.             oldSortNos.sort(function(a, b) {
  144.                 return a - b;
  145.             }).reverse();
  146.             $('.sortable-container').sortable({
  147.                 items: '> .sortable-item',
  148.                 cursor: 'move',
  149.                 update: function(e, ui) {
  150.                     $('body').append($('<div class="modal-backdrop show"></div>'));
  151.                     updateSortNoCategory();
  152.                 }
  153.             });
  154.             var updateSortNoCategory = function() {
  155.                 // 並び替え後にsortNoを更新
  156.                 var newSortNos = {};
  157.                 var i = 0;
  158.                 $('.sortable-item').each(function() {
  159.                     newSortNos[this.dataset.optionId] = oldSortNos[i];
  160.                     i++;
  161.                 });
  162.                 $.ajax({
  163.                     url: '{{ url('admin_product_option_category_sort_no_move') }}',
  164.                     type: 'POST',
  165.                     headers: {
  166.                         'x-csrf-token': $('meta[name="x-csrf-token"]').attr('content')
  167.                     },
  168.                     data: newSortNos
  169.                 }).done(function() {
  170.                     // remove class disable
  171.                     $('a.up.disabled').removeClass('disabled');
  172.                     $('a.down.disabled').removeClass('disabled');
  173.                     // First element
  174.                     $('.sortable-item > li:nth-child(2) > div > div.col-auto.text-end > a.up').addClass('disabled');
  175.                 }).always(function() {
  176.                     redrawDisableAllows();
  177.                     $('.modal-backdrop').remove();
  178.                 });
  179.             };
  180.             // 最初と最後の↑↓を再描画
  181.             var redrawDisableAllows = function() {
  182.                 var items = $('.sortable-item');
  183.                 items.find('a').removeClass('disabled');
  184.                 items.first().find('a.up').addClass('disabled');
  185.                 items.last().find('a.down').addClass('disabled');
  186.             };
  187.             $('.sortable-item a.up').click(function(e) {
  188.                 e.preventDefault();
  189.                 var current = $(this).parents('.list-group-item');
  190.                 current.prev().before(current);
  191.                 $('body').append($('<div class="modal-backdrop show"></div>'));
  192.                 updateSortNoCategory();
  193.             });
  194.             $('.sortable-item a.down').click(function(e) {
  195.                 e.preventDefault();
  196.                 var current = $(this).parents('.list-group-item');
  197.                 current.next().after(current);
  198.                 $('body').append($('<div class="modal-backdrop show"></div>'));
  199.                 updateSortNoCategory();
  200.             });
  201.             var confirmFormChange = function(form, target, modal) {
  202.                 var returnLink = form.find('input[type="hidden"][name*="return_link"]'),
  203.                     saveBtn = modal.find('a[data-action="save"]'),
  204.                     cancelBtn = modal.find('a[data-action="cancel"]');
  205.                 modal.on('hidden.bs.modal', function() {
  206.                     returnLink.val('');
  207.                 });
  208.                 saveBtn.on('click', function() {
  209.                     returnLink.val($(this).data('return-link'));
  210.                     form.submit();
  211.                 });
  212.                 target.on('click', function() {
  213.                     modal.find('.modal-body .screen-name').text($(this).attr('title'));
  214.                     modal.modal('show');
  215.                     saveBtn.data('return-link', $(this).attr('href'));
  216.                     cancelBtn.attr('href', $(this).attr('href'));
  217.                     return false;
  218.                 });
  219.             };
  220.             confirmFormChange($('#form1'), $('a[data-action="confirm"]'), $('#confirmFormChangeModal'))
  221.             // 削除モーダルのhrefとmessageの変更
  222.             $('#DeleteModal').on('shown.bs.modal', function(event) {
  223.                 var target = $(event.relatedTarget);
  224.                 // hrefの変更
  225.                 $(this).find('[data-method="delete"]').attr('href', target.data('url'));
  226.                 // messageの変更
  227.                 $(this).find('p.modal-message').text(target.data('message'));
  228.             });
  229.         });
  230.     </script>
  231. {% endblock javascript %}
  232. {% block main %}
  233.     <!-- 移動確認モーダル-->
  234.     <div class="modal fade" id="confirmFormChangeModal" tabindex="-1" role="dialog"
  235.          aria-labelledby="confirmFormChangeModal" aria-hidden="true">
  236.         <div class="modal-dialog" role="document">
  237.             <div class="modal-content">
  238.                 <div class="modal-header">
  239.                     <h5 class="modal-title">{{ 'admin.common.move_to_confirm_title'|trans }}</h5>
  240.                     <button class="btn-close" type="button" data-dismiss="modal" aria-label="Close">
  241.                         <span aria-hidden="true">×</span>
  242.                     </button>
  243.                 </div>
  244.                 <div class="modal-body">
  245.                     <p class="screen-name"></p>
  246.                 </div>
  247.                 <div class="modal-footer">
  248.                     <a class="btn btn-ec-conversion" data-action="save" href="javascript:void(0)">
  249.                         {{ 'admin.common.move_to_confirm_save_and_move'|trans }}
  250.                     </a>
  251.                     <a class="btn btn-ec-sub" data-action="cancel" href="javascript:void(0)">
  252.                         {{ 'admin.common.move_to_confirm_move_only'|trans }}
  253.                     </a>
  254.                 </div>
  255.             </div>
  256.         </div>
  257.     </div>
  258.     <form role="form" class="form-row" name="form1" id="form1" method="post" action="?">
  259.         {{ form_widget(form._token) }}
  260.         {{ form_widget(form.return_link) }}
  261.         <div class="c-contentsArea__cols">
  262.             <div class="c-contentsArea__primaryCol">
  263.                 <div class="c-primaryCol">
  264.                     <div class="card rounded border-0 mb-4">
  265.                         <div class="card-header">
  266.                             <div class="row">
  267.                                 <div class="col-8"><span class="card-title">{{ 'productoption.admin.nav.product.option.category'|trans }}:{{ Option.backend_name }}</span>
  268.                                 </div>
  269.                                 <div class="col-4 text-end">
  270.                                     <a data-bs-toggle="collapse" href="#ordererInfo"
  271.                                        aria-expanded="false" aria-controls="ordererInfo">
  272.                                         <i class="fa fa-angle-up fa-lg"></i>
  273.                                     </a>
  274.                                 </div>
  275.                             </div>
  276.                         </div>
  277.                         <div class="collapse show ec-cardCollapse" id="ordererInfo">
  278.                             <div class="card-body">
  279.                                 <div class="row mb-2">
  280.                                     <div class="col-3">
  281.                                         <span>{{ form.children.name.vars.label }}</span>
  282.                                         <span class="badge bg-primary ms-1">{{ 'admin.common.required'|trans }}</span>
  283.                                     </div>
  284.                                     <div class="col">
  285.                                         {{ form_widget(form.name) }}
  286.                                         {{ form_errors(form.name) }}
  287.                                     </div>
  288.                                 </div>
  289.                                 <div class="row mb-2">
  290.                                     <div class="col-3">
  291.                                         <span>{{ form.children.value.vars.label }}</span>
  292.                                     </div>
  293.                                     <div class="col">
  294.                                         {{ form_widget(form.value) }}
  295.                                         {{ form_errors(form.value) }}
  296.                                     </div>
  297.                                 </div>
  298.                                 <div class="row mb-2">
  299.                                     <div class="col-3">
  300.                                         <span>{{ form.children.delivery_free_flg.vars.label }}</span>
  301.                                     </div>
  302.                                     <div class="col">
  303.                                         {{ form_widget(form.delivery_free_flg) }}
  304.                                         {{ form_errors(form.delivery_free_flg) }}
  305.                                     </div>
  306.                                 </div>
  307.                                 <div class="row mb-2">
  308.                                     <div class="col-3">
  309.                                         <span>{{ form.children.description.vars.label }}</span>
  310.                                     </div>
  311.                                     <div class="col">
  312.                                         {{ form_widget(form.description) }}
  313.                                         {{ form_errors(form.description) }}
  314.                                     </div>
  315.                                 </div>
  316.                                 <div class="row">
  317.                                     <div class="col-3">
  318.                                         <div class="d-inline-block">
  319.                                             <span>{{ form.children.option_image.vars.label }}</span>
  320.                                         </div>
  321.                                     </div>
  322.                                     <div class="col mb-2">
  323.                                         <p id="message"></p>
  324.                                         <div id="upload-zone" class="media rounded">
  325.                                             <div class="media-body">
  326.                                                 {{ form_widget(form.option_image, { attr : { style : 'display:none;' } }) }}
  327.                                                 {{ form_errors(form.option_image) }}
  328.                                             </div><!-- /.media-body -->
  329.                                         </div><!-- /.media -->
  330.                                     </div>
  331.                                 </div>
  332.                                 {% if Option.type != constant('Plugin\\ProductOption42\\Entity\\Option::CHECKBOX_TYPE') %}
  333.                                 <div class="row mb-2">
  334.                                     <div class="col-3">
  335.                                         <span>{{ form.children.disable_flg.vars.label }}</span>
  336.                                     </div>
  337.                                     <div class="col">
  338.                                         {{ form_widget(form.disable_flg) }}
  339.                                         {{ form_errors(form.disable_flg) }}
  340.                                     </div>
  341.                                 </div>
  342.                                 {% endif %}
  343.                                 <div class="row mb-2">
  344.                                     <div class="col-3">
  345.                                         <span>{{ form.children.init_flg.vars.label }}</span>
  346.                                     </div>
  347.                                     <div class="col">
  348.                                         {{ form_widget(form.init_flg) }}
  349.                                         {{ form_errors(form.init_flg) }}
  350.                                     </div>
  351.                                 </div>
  352.                                 <div class="row mb-2">
  353.                                     <div class="col-3">
  354.                                         <span>{{ form.children.hidden_flg.vars.label }}</span>
  355.                                     </div>
  356.                                     <div class="col">
  357.                                         {{ form_widget(form.hidden_flg) }}
  358.                                         {{ form_errors(form.hidden_flg) }}
  359.                                     </div>
  360.                                 </div>
  361.                             </div>
  362.                         </div>
  363.                     </div>
  364.                 </div>
  365.             </div>
  366.         </div>
  367.         <div class="c-conversionArea">
  368.             <div class="c-conversionArea__container">
  369.                 <div class="row justify-content-between align-items-center">
  370.                     <div class="col-6">
  371.                         <div class="c-conversionArea__leftBlockItem">
  372.                             <a class="c-baseLink" href="{{ path('admin_product_option') }}"
  373.                                data-action="confirm" title="{{ 'admin.common.move_to_confirm_message'|trans({'%name%' : 'productoption.admin.product.option.category.list'|trans }) }}">
  374.                                 <i class="fa fa-backward" aria-hidden="true"></i><span>{{ 'productoption.admin.product.option.category.list'|trans }}</span>
  375.                             </a>
  376.                         </div>
  377.                     </div>
  378.                     <div class="col-6">
  379.                         <div id="ex-conversion-action" class="row align-items-center justify-content-end">
  380.                             <div class="col-auto">
  381.                                 <a href="{{ url('admin_product_option_category_new', {option_id: Option.id}) }}">
  382.                                 <button class="btn btn-ec-regular"
  383.                                         type="button">{{ 'productoption.admin.common.new'|trans }}</button>
  384.                                 </a>
  385.                             </div>
  386.                             <div class="col-auto">
  387.                                 <button class="btn btn-ec-conversion px-5"
  388.                                         type="submit">{{ 'productoption.admin.common.save'|trans }}</button>
  389.                             </div>
  390.                         </div>
  391.                     </div>
  392.                 </div>
  393.             </div>
  394.         </div>
  395.     </form>
  396.     {% if OptionCategories|length > 0 %}
  397.     <div class="c-contentsArea__cols">
  398.         <div class="c-contentsArea__primaryCol">
  399.             <div class="c-primaryCol">
  400.                 <div class="card rounded border-0 mb-4">
  401.                     <div class="card-body p-0">
  402.                         <div class="card rounded border-0 mb-2">
  403.                             <ul class="list-group list-group-flush sortable-container">
  404.                                 <li class="list-group-item">
  405.                                     <div class="row">
  406.                                         <div class="col-auto"><strong>&nbsp;</strong></div>
  407.                                         <div class="col-3"><strong>{{ 'productoption.admin.product.option.category.name'|trans }}</strong></div>
  408.                                         <div class="col-2"><strong>{{ 'productoption.admin.product.option.category.price'|trans }}</strong></div>
  409.                                         <div class="col-1"><strong>{{ 'productoption.admin.product.option.category.init_disp'|trans }}</strong></div>
  410.                                         <div class="col-1"><strong>{{ 'productoption.admin.product.option.category.disable_disp'|trans }}</strong></div>
  411.                                         <div class="col-auto"><strong>{{ 'productoption.admin.product.option.category.hidden_disp'|trans }}</strong></div>
  412.                                     </div>
  413.                                 </li>
  414.                                 {% for OptionCategory in OptionCategories %}
  415.                                     <li id="ex-class_name-{{ OptionCategory.id }}" class="list-group-item sortable-item" data-option-id="{{ OptionCategory.id }}" data-sort-no="{{ OptionCategory.sortNo }}">
  416.                                         <div class="row mode-view">
  417.                                             <div class="col-auto d-flex align-items-center"><i class="fa fa-bars text-ec-gray"></i></div>
  418.                                             <div class="col-3 d-flex align-items-center">
  419.                                                 {{ OptionCategory.name }}
  420.                                             </div>
  421.                                             <div class="col-2 d-flex align-items-center">
  422.                                                 {% if OptionCategory.value|length > 0 %}¥ {{ OptionCategory.value|number_format }}{% endif %}
  423.                                             </div>
  424.                                             <div class="col-1 d-flex align-items-center">
  425.                                                 {% if OptionCategory.init_flg %}〇{% endif %}
  426.                                             </div>
  427.                                             <div class="col-1 d-flex align-items-center">
  428.                                                 {% if OptionCategory.disable_flg %}〇{% endif %}
  429.                                             </div>
  430.                                             <div class="col d-flex align-items-center">
  431.                                                 {% if OptionCategory.hidden_flg %}〇{% endif %}
  432.                                             </div>
  433.                                             <div class="col-auto text-end">
  434.                                                 <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 }}">
  435.                                                     <i class="fa fa-arrow-up fa-lg text-secondary"></i>
  436.                                                 </a>
  437.                                                 <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 }}">
  438.                                                     <i class="fa fa-arrow-down fa-lg text-secondary"></i>
  439.                                                 </a>
  440.                                                 <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 }) }}">
  441.                                                     <i class="fa fa-pencil fa-lg text-secondary"></i>
  442.                                                 </a>
  443.                                                 <div class="d-inline-block mr-2" data-tooltip="true"
  444.                                                      data-placement="top" title="{{ 'admin.common.delete'|trans }}">
  445.                                                     <a class="btn btn-ec-actionIcon"
  446.                                                        data-bs-toggle="modal" data-bs-target="#DeleteModal"
  447.                                                        data-url="{{ url('admin_product_option_category_delete', {'option_id' : Option.id ,'id' : OptionCategory.id}) }}"
  448.                                                        data-message="{{ 'productoption.admin.product.option.category.modal.body'|trans }}">
  449.                                                         <i class="fa fa-close fa-lg text-secondary"></i>
  450.                                                     </a>
  451.                                                 </div>
  452.                                             </div>
  453.                                         </div>
  454.                                     </li>
  455.                                 {% endfor %}
  456.                             </ul>
  457.                             <!-- 削除モーダル -->
  458.                             <div class="modal fade" id="DeleteModal" tabindex="-1" role="dialog"
  459.                                 aria-labelledby="DeleteModal" aria-hidden="true">
  460.                                 <div class="modal-dialog" role="document">
  461.                                     <div class="modal-content">
  462.                                         <div class="modal-header">
  463.                                             <h5 class="modal-title font-weight-bold">
  464.                                                 {{ 'productoption.admin.product.option.category.modal.header'|trans }}
  465.                                             </h5>
  466.                                             <button class="btn-close" type="button" data-bs-dismiss="modal" aria-label="Close">
  467.                                             </button>
  468.                                         </div>
  469.                                         <div class="modal-body text-start">
  470.                                             <p class="text-start modal-message"><!-- jsでメッセージを挿入 --></p>
  471.                                         </div>
  472.                                         <div class="modal-footer">
  473.                                             <button class="btn btn-ec-sub" type="button" data-bs-dismiss="modal">
  474.                                                 {{ 'admin.common.cancel'|trans }}
  475.                                             </button>
  476.                                             <a class="btn btn-ec-delete" href="#" {{ csrf_token_for_anchor() }}
  477.                                             data-method="delete" data-confirm="false">
  478.                                                 {{ 'admin.common.delete'|trans }}
  479.                                             </a>
  480.                                         </div>
  481.                                     </div>
  482.                                 </div>
  483.                             </div>
  484.                         </div>
  485.                     </div>
  486.                 </div>
  487.                 <p>{{ 'productoption.admin.product.option.category.sortable'|trans }}</p>
  488.             </div>
  489.         </div>
  490.     </div>
  491.     {% else %}
  492.         <p>{{ 'productoption.admin.product.option.category.no_item'|trans }}</p>
  493.     {% endif %}
  494. {% endblock %}