feat(main): Add FAQ accordion

This commit is contained in:
2026-08-11 13:25:06 +02:00
parent 635c13a0ed
commit 1493081602
22 changed files with 459 additions and 4 deletions
@@ -1,6 +1,12 @@
{% import '@PrestaShop/Admin/macros.html.twig' as ps %}
{% form_theme customHtmlBlockForm '@PrestaShop/Admin/TwigTemplateForm/prestashop_ui_kit.html.twig' %}
{% macro faqFields(faqForm) %}
{{ form_row(faqForm.question) }}
{{ form_row(faqForm.answer) }}
{% endmacro %}
{% import _self as faqMacros %}
{% block customhtmlblock_form_block %}
<div class="card">
{{ form_start(customHtmlBlockForm) }}
@@ -10,6 +16,38 @@
</h3>
</div>
<div class="card-body">
{{ form_row(customHtmlBlockForm.id_ws_customhtml_group) }}
{{ form_row(customHtmlBlockForm.title) }}
{{ form_row(customHtmlBlockForm.type) }}
{{ form_row(customHtmlBlockForm.show_title) }}
{{ form_row(customHtmlBlockForm.content) }}
<div class="customhtmlblock-type-field customhtmlblock-type-faq">
<label class="form-control-label">
{{ 'FAQ items'|trans({}, 'Modules.Wscustomhtml.Admin') }}
</label>
<div
id="customhtmlblock-faq-items"
data-prototype="{{ faqMacros.faqFields(customHtmlBlockForm.faqs.vars.prototype)|e('html_attr') }}"
data-index="{{ customHtmlBlockForm.faqs|length }}"
>
{% for faqRow in customHtmlBlockForm.faqs %}
<div class="customhtmlblock-faq-item card mb-2">
<div class="card-body">
{{ faqMacros.faqFields(faqRow) }}
<button type="button" class="btn btn-outline-danger btn-sm customhtmlblock-remove-faq">
<i class="material-icons">delete</i> {{ 'Remove'|trans({}, 'Admin.Actions') }}
</button>
</div>
</div>
{% endfor %}
</div>
<button type="button" id="customhtmlblock-add-faq" class="btn btn-outline-primary btn-sm">
<i class="material-icons">add</i> {{ 'Add FAQ item'|trans({}, 'Modules.Wscustomhtml.Admin') }}
</button>
</div>
{{ form_row(customHtmlBlockForm.active) }}
{{ form_rest(customHtmlBlockForm) }}
</div>
<div class="card-footer">
@@ -13,6 +13,44 @@
window.prestashop.component.initComponents([
'TinyMCEEditor',
]);
function toggleBlockTypeFields() {
var type = $('#custom_html_block_type').val();
if (type === 'html') {
$('.customhtmlblock-type-html').show();
$('.customhtmlblock-type-faq').hide();
} else {
$('.customhtmlblock-type-html').hide();
$('.customhtmlblock-type-faq').show();
}
}
$('#custom_html_block_type').on('change', toggleBlockTypeFields);
toggleBlockTypeFields();
var removeFaqLabel = '{{ 'Remove'|trans({}, 'Admin.Actions')|e('js') }}';
var faqContainer = $('#customhtmlblock-faq-items');
$('#customhtmlblock-add-faq').on('click', function () {
var index = faqContainer.data('index');
var prototype = faqContainer.data('prototype').replace(/__name__/g, index);
var item = $('<div class="customhtmlblock-faq-item card mb-2"><div class="card-body"></div></div>');
item.find('.card-body')
.append(prototype)
.append(
'<button type="button" class="btn btn-outline-danger btn-sm customhtmlblock-remove-faq">' +
'<i class="material-icons">delete</i> ' + removeFaqLabel + '</button>'
);
faqContainer.append(item);
faqContainer.data('index', index + 1);
});
faqContainer.on('click', '.customhtmlblock-remove-faq', function () {
$(this).closest('.customhtmlblock-faq-item').remove();
});
});
</script>
{% endblock %}
@@ -13,6 +13,44 @@
window.prestashop.component.initComponents([
'TinyMCEEditor',
]);
function toggleBlockTypeFields() {
var type = $('#custom_html_block_type').val();
if (type === 'html') {
$('.customhtmlblock-type-html').show();
$('.customhtmlblock-type-faq').hide();
} else {
$('.customhtmlblock-type-html').hide();
$('.customhtmlblock-type-faq').show();
}
}
$('#custom_html_block_type').on('change', toggleBlockTypeFields);
toggleBlockTypeFields();
var removeFaqLabel = '{{ 'Remove'|trans({}, 'Admin.Actions')|e('js') }}';
var faqContainer = $('#customhtmlblock-faq-items');
$('#customhtmlblock-add-faq').on('click', function () {
var index = faqContainer.data('index');
var prototype = faqContainer.data('prototype').replace(/__name__/g, index);
var item = $('<div class="customhtmlblock-faq-item card mb-2"><div class="card-body"></div></div>');
item.find('.card-body')
.append(prototype)
.append(
'<button type="button" class="btn btn-outline-danger btn-sm customhtmlblock-remove-faq">' +
'<i class="material-icons">delete</i> ' + removeFaqLabel + '</button>'
);
faqContainer.append(item);
faqContainer.data('index', index + 1);
});
faqContainer.on('click', '.customhtmlblock-remove-faq', function () {
$(this).closest('.customhtmlblock-faq-item').remove();
});
});
</script>
{% endblock %}
+30
View File
@@ -0,0 +1,30 @@
{if !empty($block.faqs)}
<div class="accordion customhtml-faq-accordion" id="customhtml-faq-{$block.id_ws_customhtml|intval}">
{foreach from=$block.faqs item="faq" name="customhtmlFaqLoop"}
<div class="card">
<div class="card-header" id="customhtml-faq-heading-{$block.id_ws_customhtml|intval}-{$faq.id_ws_customhtml_faq|intval}">
<button
class="btn btn-link{if !$smarty.foreach.customhtmlFaqLoop.first} collapsed{/if}"
type="button"
data-toggle="collapse"
data-target="#customhtml-faq-collapse-{$block.id_ws_customhtml|intval}-{$faq.id_ws_customhtml_faq|intval}"
aria-expanded="{if $smarty.foreach.customhtmlFaqLoop.first}true{else}false{/if}"
aria-controls="customhtml-faq-collapse-{$block.id_ws_customhtml|intval}-{$faq.id_ws_customhtml_faq|intval}"
>
{$faq.question|escape:'html':'UTF-8'}
</button>
</div>
<div
id="customhtml-faq-collapse-{$block.id_ws_customhtml|intval}-{$faq.id_ws_customhtml_faq|intval}"
class="collapse{if $smarty.foreach.customhtmlFaqLoop.first} show{/if}"
aria-labelledby="customhtml-faq-heading-{$block.id_ws_customhtml|intval}-{$faq.id_ws_customhtml_faq|intval}"
data-parent="#customhtml-faq-{$block.id_ws_customhtml|intval}"
>
<div class="card-body">
{$faq.answer nofilter}
</div>
</div>
</div>
{/foreach}
</div>
{/if}
+5 -1
View File
@@ -7,7 +7,11 @@
{if $block.show_title && !empty($block.title)}
<div class="customhtml-title">{$block.title|escape:'html':'UTF-8'}</div>
{/if}
<div class="customhtml-content">{$block.content nofilter}</div>
{if $block.type == 'faq'}
{include file="module:ws_customhtml/views/templates/hook/_faq_accordion.tpl" block=$block}
{else}
<div class="customhtml-content">{$block.content nofilter}</div>
{/if}
</div>
{/foreach}
</div>