feat: add per-integration content tabs to product edit form with CSS and JS
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -1,3 +1,11 @@
|
||||
<link href="https://cdn.quilljs.com/1.3.7/quill.snow.css" rel="stylesheet">
|
||||
<style>
|
||||
.wysiwyg-wrap { position: relative; z-index: 1; }
|
||||
.wysiwyg-wrap .ql-toolbar { border-radius: 4px 4px 0 0; border-color: var(--c-border, #d1d5db); background: #f8fafc; }
|
||||
.wysiwyg-wrap .ql-container { height: auto; border-radius: 0 0 4px 4px; border-color: var(--c-border, #d1d5db); font-size: 14px; font-family: inherit; }
|
||||
.wysiwyg-wrap .ql-editor { min-height: var(--editor-min-height, 80px); }
|
||||
</style>
|
||||
|
||||
<section class="card">
|
||||
<h1><?= $e($t('products.edit.title', ['id' => (string) ($productId ?? 0)])) ?></h1>
|
||||
<p class="muted"><?= $e($t('products.edit.description')) ?></p>
|
||||
@@ -17,12 +25,95 @@
|
||||
<input type="hidden" name="_token" value="<?= $e($csrfToken ?? '') ?>">
|
||||
<input type="hidden" name="id" value="<?= $e((string) ($productId ?? 0)) ?>">
|
||||
|
||||
<div class="form-grid">
|
||||
<label class="form-field">
|
||||
<span class="field-label"><?= $e($t('products.fields.name')) ?></span>
|
||||
<input class="form-control" type="text" name="name" required value="<?= $e((string) ($form['name'] ?? '')) ?>">
|
||||
</label>
|
||||
<?php
|
||||
$activeIntegrations = is_array($activeIntegrations ?? null) ? $activeIntegrations : [];
|
||||
$integrationTranslationsMap = is_array($integrationTranslationsMap ?? null) ? $integrationTranslationsMap : [];
|
||||
?>
|
||||
|
||||
<div class="content-tabs-card mt-0">
|
||||
<div class="content-tabs-nav" id="content-tabs-nav">
|
||||
<button type="button" class="content-tab-btn is-active" data-tab="global">
|
||||
<?= $e($t('products.content_tabs.global')) ?>
|
||||
</button>
|
||||
<?php foreach ($activeIntegrations as $integration): ?>
|
||||
<?php $intId = (int) ($integration['id'] ?? 0); ?>
|
||||
<?php if ($intId <= 0) continue; ?>
|
||||
<button type="button" class="content-tab-btn" data-tab="integration-<?= $e((string) $intId) ?>">
|
||||
<?= $e((string) ($integration['name'] ?? '#' . $intId)) ?>
|
||||
</button>
|
||||
<?php endforeach; ?>
|
||||
</div>
|
||||
|
||||
<!-- GLOBAL TAB -->
|
||||
<div class="content-tab-panel is-active" id="content-tab-global">
|
||||
<label class="form-field">
|
||||
<span class="field-label"><?= $e($t('products.fields.name')) ?> *</span>
|
||||
<input class="form-control" type="text" name="name" required value="<?= $e((string) ($form['name'] ?? '')) ?>">
|
||||
</label>
|
||||
|
||||
<div class="form-field mt-12">
|
||||
<span class="field-label"><?= $e($t('products.fields.short_description')) ?></span>
|
||||
<div class="wysiwyg-wrap">
|
||||
<div id="editor-short-description"></div>
|
||||
</div>
|
||||
<textarea name="short_description" id="input-short-description" style="display:none"><?= $e((string) ($form['short_description'] ?? '')) ?></textarea>
|
||||
</div>
|
||||
|
||||
<div class="form-field mt-12">
|
||||
<span class="field-label"><?= $e($t('products.fields.description')) ?></span>
|
||||
<div class="wysiwyg-wrap" style="--editor-min-height:180px">
|
||||
<div id="editor-description"></div>
|
||||
</div>
|
||||
<textarea name="description" id="input-description" style="display:none"><?= $e((string) ($form['description'] ?? '')) ?></textarea>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- PER-INTEGRATION TABS -->
|
||||
<?php foreach ($activeIntegrations as $integration): ?>
|
||||
<?php
|
||||
$intId = (int) ($integration['id'] ?? 0);
|
||||
if ($intId <= 0) continue;
|
||||
$intData = $integrationTranslationsMap[$intId] ?? [];
|
||||
$intName = isset($intData['name']) ? (string) $intData['name'] : '';
|
||||
$intShort = isset($intData['short_description']) ? (string) $intData['short_description'] : '';
|
||||
$intDesc = isset($intData['description']) ? (string) $intData['description'] : '';
|
||||
?>
|
||||
<div class="content-tab-panel" id="content-tab-integration-<?= $e((string) $intId) ?>">
|
||||
<p class="muted" style="margin-bottom:8px">
|
||||
Puste pole = używana wartość globalna.
|
||||
</p>
|
||||
|
||||
<label class="form-field">
|
||||
<span class="field-label"><?= $e($t('products.fields.name')) ?></span>
|
||||
<input class="form-control" type="text"
|
||||
name="integration_content[<?= $e((string) $intId) ?>][name]"
|
||||
value="<?= $e($intName) ?>">
|
||||
</label>
|
||||
|
||||
<div class="form-field mt-12">
|
||||
<span class="field-label"><?= $e($t('products.fields.short_description')) ?></span>
|
||||
<div class="wysiwyg-wrap">
|
||||
<div id="editor-int-short-<?= $e((string) $intId) ?>"></div>
|
||||
</div>
|
||||
<textarea name="integration_content[<?= $e((string) $intId) ?>][short_description]"
|
||||
id="input-int-short-<?= $e((string) $intId) ?>"
|
||||
style="display:none"><?= $e($intShort) ?></textarea>
|
||||
</div>
|
||||
|
||||
<div class="form-field mt-12">
|
||||
<span class="field-label"><?= $e($t('products.fields.description')) ?></span>
|
||||
<div class="wysiwyg-wrap" style="--editor-min-height:180px">
|
||||
<div id="editor-int-desc-<?= $e((string) $intId) ?>"></div>
|
||||
</div>
|
||||
<textarea name="integration_content[<?= $e((string) $intId) ?>][description]"
|
||||
id="input-int-desc-<?= $e((string) $intId) ?>"
|
||||
style="display:none"><?= $e($intDesc) ?></textarea>
|
||||
</div>
|
||||
</div>
|
||||
<?php endforeach; ?>
|
||||
</div>
|
||||
|
||||
<div class="form-grid">
|
||||
<label class="form-field">
|
||||
<span class="field-label">SKU</span>
|
||||
<input class="form-control" type="text" name="sku" value="<?= $e((string) ($form['sku'] ?? '')) ?>">
|
||||
@@ -101,16 +192,6 @@
|
||||
</label>
|
||||
</div>
|
||||
|
||||
<label class="form-field mt-16">
|
||||
<span class="field-label"><?= $e($t('products.fields.short_description')) ?></span>
|
||||
<textarea class="form-control" name="short_description" rows="3"><?= $e((string) ($form['short_description'] ?? '')) ?></textarea>
|
||||
</label>
|
||||
|
||||
<label class="form-field mt-12">
|
||||
<span class="field-label"><?= $e($t('products.fields.description')) ?></span>
|
||||
<textarea class="form-control" name="description" rows="6"><?= $e((string) ($form['description'] ?? '')) ?></textarea>
|
||||
</label>
|
||||
|
||||
<div class="form-grid mt-16">
|
||||
<label class="form-field">
|
||||
<span class="field-label"><?= $e($t('products.fields.meta_title')) ?></span>
|
||||
@@ -381,3 +462,90 @@
|
||||
});
|
||||
})();
|
||||
</script>
|
||||
|
||||
<script src="https://cdn.quilljs.com/1.3.7/quill.min.js"></script>
|
||||
<script>
|
||||
(function() {
|
||||
var toolbarShort = [
|
||||
['bold', 'italic', 'underline'],
|
||||
[{ list: 'bullet' }],
|
||||
['link', 'clean']
|
||||
];
|
||||
var toolbarFull = [
|
||||
[{ header: [1, 2, 3, false] }],
|
||||
['bold', 'italic', 'underline', 'strike'],
|
||||
[{ list: 'ordered' }, { list: 'bullet' }],
|
||||
['link', 'clean']
|
||||
];
|
||||
|
||||
var shortInput = document.getElementById('input-short-description');
|
||||
var descInput = document.getElementById('input-description');
|
||||
|
||||
var quillShort = new Quill('#editor-short-description', { theme: 'snow', modules: { toolbar: toolbarShort } });
|
||||
var quillDesc = new Quill('#editor-description', { theme: 'snow', modules: { toolbar: toolbarFull } });
|
||||
|
||||
if (shortInput && shortInput.value) quillShort.clipboard.dangerouslyPasteHTML(shortInput.value);
|
||||
if (descInput && descInput.value) quillDesc.clipboard.dangerouslyPasteHTML(descInput.value);
|
||||
|
||||
// --- per-integration editors ---
|
||||
var intEditors = []; // array of {shortQuill, descQuill, shortInput, descInput}
|
||||
|
||||
document.querySelectorAll('[id^="editor-int-short-"]').forEach(function(el) {
|
||||
var suffix = el.id.replace('editor-int-short-', '');
|
||||
var shortEl = el;
|
||||
var descEl = document.getElementById('editor-int-desc-' + suffix);
|
||||
var shortInp = document.getElementById('input-int-short-' + suffix);
|
||||
var descInp = document.getElementById('input-int-desc-' + suffix);
|
||||
|
||||
if (!shortEl || !descEl || !shortInp || !descInp) return;
|
||||
|
||||
var qShort = new Quill(shortEl, { theme: 'snow', modules: { toolbar: toolbarShort } });
|
||||
var qDesc = new Quill(descEl, { theme: 'snow', modules: { toolbar: toolbarFull } });
|
||||
|
||||
if (shortInp.value) qShort.clipboard.dangerouslyPasteHTML(shortInp.value);
|
||||
if (descInp.value) qDesc.clipboard.dangerouslyPasteHTML(descInp.value);
|
||||
|
||||
intEditors.push({ shortQuill: qShort, descQuill: qDesc, shortInput: shortInp, descInput: descInp });
|
||||
});
|
||||
|
||||
var form = document.querySelector('.product-form');
|
||||
if (form) {
|
||||
form.addEventListener('submit', function() {
|
||||
if (shortInput) shortInput.value = quillShort.root.innerHTML;
|
||||
if (descInput) descInput.value = quillDesc.root.innerHTML;
|
||||
intEditors.forEach(function(e) {
|
||||
e.shortInput.value = e.shortQuill.root.innerHTML;
|
||||
e.descInput.value = e.descQuill.root.innerHTML;
|
||||
});
|
||||
});
|
||||
}
|
||||
})();
|
||||
</script>
|
||||
|
||||
<script>
|
||||
(function() {
|
||||
var nav = document.getElementById('content-tabs-nav');
|
||||
if (!nav) return;
|
||||
|
||||
nav.addEventListener('click', function(e) {
|
||||
var btn = e.target.closest('.content-tab-btn');
|
||||
if (!btn) return;
|
||||
|
||||
var tabId = btn.getAttribute('data-tab');
|
||||
if (!tabId) return;
|
||||
|
||||
// deactivate all
|
||||
nav.querySelectorAll('.content-tab-btn').forEach(function(b) {
|
||||
b.classList.remove('is-active');
|
||||
});
|
||||
document.querySelectorAll('.content-tab-panel').forEach(function(p) {
|
||||
p.classList.remove('is-active');
|
||||
});
|
||||
|
||||
// activate selected
|
||||
btn.classList.add('is-active');
|
||||
var panel = document.getElementById('content-tab-' + tabId);
|
||||
if (panel) panel.classList.add('is-active');
|
||||
});
|
||||
})();
|
||||
</script>
|
||||
|
||||
Reference in New Issue
Block a user