Globinours/app/Views/settings_public_site.php

383 lines
31 KiB
PHP
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<?php require __DIR__ . '/settings_nav.php'; ?>
<div class="d-flex flex-wrap justify-content-between align-items-start gap-2 mb-3">
<div>
<h2 class="mb-1"><?= h(t('public_site.settings_heading')) ?></h2>
<div class="text-muted"><?= h(t('public_site.settings_intro')) ?></div>
</div><a class="btn btn-outline-primary" href="<?= h(PublicSiteController::url()) ?>" target="_blank">↗
<?= h(t('public_site.preview')) ?></a>
</div>
<?php if ($saved): ?><div class="alert alert-success">✅ <?= h(t('public_site.saved')) ?></div>
<?php endif; ?><?php if ($error): ?><div class="alert alert-danger">⚠️ <?= h($error) ?></div><?php endif; ?>
<div class="card mb-3">
<div class="card-header"><h3 class="card-title">🖼️ <?= h(t('public_site.image_library')) ?></h3></div>
<div class="card-body">
<form id="public-image-upload" method="post" action="/settings/public-site/image" enctype="multipart/form-data" class="row g-2 align-items-end mb-3">
<input type="hidden" name="csrf" value="<?= h(Auth::csrf()) ?>">
<div class="col-md"><label class="form-label"><?= h(t('public_site.image_file')) ?></label><input class="form-control" type="file" name="public_site_image" accept="image/jpeg,image/png,image/webp" required></div>
<div class="col-md-auto"><button class="btn btn-outline-primary"><?= h(t('public_site.image_upload')) ?></button></div>
</form>
<div class="form-hint mb-3" id="public-image-help"><?= h(t('public_site.image_help')) ?></div>
<div class="text-muted<?= $images ? ' d-none' : '' ?>" id="public-image-empty"><?= h(t('public_site.image_none')) ?></div>
<div class="row g-3" id="public-image-library">
<?php foreach ($images as $image): ?><div class="col-6 col-md-3 col-xl-2">
<div class="card h-100"><img src="<?= h($image['url']) ?>" alt="" class="card-img-top" style="height:110px;object-fit:cover">
<div class="card-body p-2"><button type="button" class="btn btn-sm btn-outline-secondary w-100 public-image-insert" data-url="<?= h($image['url']) ?>"><?= h(t('public_site.image_insert')) ?></button></div>
</div>
</div><?php endforeach; ?>
</div>
</div>
</div>
<form method="post" action="/settings/public-site/save"><input type="hidden" name="csrf" value="<?= h(Auth::csrf()) ?>">
<input type="hidden" id="public-site-navigation-order" name="public_site_navigation_order" value="<?= h($settings['public_site_navigation_order'] ?? '') ?>">
<div class="card mb-3">
<div class="card-status-top bg-green"></div>
<div class="card-header">
<h3 class="card-title"><?= h(t('public_site.activation_style')) ?></h3>
</div>
<div class="card-body">
<div class="row g-3">
<div class="col-12"><label class="form-check form-switch"><input class="form-check-input"
type="checkbox" name="public_site_enabled" value="1"
<?= ($settings['public_site_enabled'] ?? '0') === '1' ? 'checked' : '' ?>><span
class="form-check-label fw-bold"><?= h(t('public_site.enable')) ?></span></label>
<div class="form-hint"><?= h(t('public_site.enable_help')) ?></div>
</div>
<div class="col-12"><label class="form-label"><?= h(t('public_site.main_address')) ?></label>
<div class="row g-2">
<div class="col-md-6"><label class="form-selectgroup-item w-100"><input
class="form-selectgroup-input" type="radio" name="public_site_routing"
value="integrated"
<?= ($settings['public_site_routing'] ?? 'integrated') === 'integrated' ? 'checked' : '' ?>><span
class="form-selectgroup-label d-block"><strong><?= h(t('public_site.routing_management')) ?></strong><span
class="d-block text-muted small"><?= h(t('public_site.routing_management_help')) ?></span></span></label>
</div>
<div class="col-md-6"><label class="form-selectgroup-item w-100"><input
class="form-selectgroup-input" type="radio" name="public_site_routing" value="root"
<?= ($settings['public_site_routing'] ?? 'integrated') === 'root' ? 'checked' : '' ?>><span
class="form-selectgroup-label d-block"><strong><?= h(t('public_site.routing_public')) ?></strong><span
class="d-block text-muted small"><?= h(t('public_site.routing_public_help')) ?></span></span></label>
</div>
</div>
<div class="form-hint mt-2"><?= h(t('public_site.routing_no_server')) ?></div>
</div>
<div class="col-12"><label class="form-label"><?= h(t('public_site.theme')) ?></label>
<div class="row g-2">
<?php foreach (['warm' => ['🧡',t('public_site.theme_warm')],'nature' => ['🌿',t('public_site.theme_nature')],'minimal' => ['◻️',t('public_site.theme_minimal')],'custom' => ['🎨',t('public_site.theme_custom')]] as $key => $theme): ?>
<div class="col-md-3"><label class="form-selectgroup-item w-100"><input type="radio"
name="public_site_theme" value="<?= h($key) ?>" class="form-selectgroup-input"
<?= ($settings['public_site_theme'] ?? 'warm') === $key ? 'checked' : '' ?>><span
class="form-selectgroup-label d-flex align-items-center gap-2"><?= $theme[0] ?>
<strong><?= h($theme[1]) ?></strong></span></label></div><?php endforeach; ?></div>
</div>
<div class="col-md-6"><label class="form-label"><?= h(t('public_site.site_name')) ?></label><input
class="form-control" name="public_site_name" value="<?= h($settings['public_site_name']) ?>"
placeholder="<?= h($settings['association_name']) ?>"></div>
<div class="col-12"><label class="form-label"><?= h(t('public_site.custom_css')) ?></label><textarea
class="form-control font-monospace" name="public_site_custom_css" rows="8"
placeholder=":root { --accent: #d87945; }"><?= h($settings['public_site_custom_css']) ?></textarea>
<div class="form-hint"><?= h(t('public_site.custom_css_help')) ?></div></div>
</div>
</div>
</div>
<div class="d-flex flex-wrap align-items-center justify-content-between gap-2 mb-2">
<div><h3 class="mb-0"><?= h(t('public_site.site_pages')) ?></h3><div class="text-muted small"><?= h(t('public_site.site_pages_help')) ?></div></div>
<button class="btn btn-outline-primary" type="button" id="public-page-add">+ <?= h(t('public_site.add_page')) ?></button>
</div>
<div id="public-pages-sortable" class="mb-3">
<details class="card mb-2 public-page-panel public-sortable-page" data-page-key="home" draggable="true" open>
<summary class="card-header gap-2">
<span class="public-page-drag" title="<?= h(t('public_site.drag_page')) ?>" aria-hidden="true">⠿</span>
<h3 class="card-title">🏠 <?= h(t('public_site.home')) ?></h3>
<span class="ms-auto d-flex gap-1 public-page-actions"><button class="btn btn-sm btn-icon btn-outline-secondary public-page-up" type="button" aria-label="<?= h(t('public_site.move_up')) ?>">↑</button><button class="btn btn-sm btn-icon btn-outline-secondary public-page-down" type="button" aria-label="<?= h(t('public_site.move_down')) ?>">↓</button><span class="public-page-chevron" aria-hidden="true">⌄</span></span>
</summary>
<div class="card-body row g-3">
<div class="col-12"><label class="form-label"><?= h(t('common.title')) ?></label><input class="form-control"
name="public_site_home_title" value="<?= h($settings['public_site_home_title']) ?>" required></div>
<div class="col-12"><label class="form-label"><?= h(t('common.text')) ?></label><textarea
class="form-control public-rich-text" name="public_site_home_text"
rows="4"><?= h($settings['public_site_home_text']) ?></textarea></div>
</div>
</details>
<div class="card mb-2 public-sortable-page" data-page-key="animals" draggable="true"><div class="card-header gap-2"><span class="public-page-drag" title="<?= h(t('public_site.drag_page')) ?>" aria-hidden="true">⠿</span><h3 class="card-title">🐾 <?= h(t('public_site.animals')) ?></h3><span class="ms-auto d-flex gap-1 public-page-actions"><button class="btn btn-sm btn-icon btn-outline-secondary public-page-up" type="button" aria-label="<?= h(t('public_site.move_up')) ?>">↑</button><button class="btn btn-sm btn-icon btn-outline-secondary public-page-down" type="button" aria-label="<?= h(t('public_site.move_down')) ?>">↓</button></span></div></div>
<details class="card mb-2 public-page-panel public-sortable-page" data-page-key="about" draggable="true">
<summary class="card-header gap-2">
<span class="public-page-drag" title="<?= h(t('public_site.drag_page')) ?>" aria-hidden="true">⠿</span>
<h3 class="card-title">📖 <?= h(t('public_site.free_page')) ?></h3>
<span class="ms-auto d-flex gap-1 public-page-actions"><button class="btn btn-sm btn-icon btn-outline-secondary public-page-up" type="button" aria-label="<?= h(t('public_site.move_up')) ?>">↑</button><button class="btn btn-sm btn-icon btn-outline-secondary public-page-down" type="button" aria-label="<?= h(t('public_site.move_down')) ?>">↓</button><span class="public-page-chevron" aria-hidden="true">⌄</span></span>
</summary>
<div class="card-body row g-3">
<div class="col-12"><label class="form-check form-switch"><input class="form-check-input" type="checkbox"
name="public_site_about_enabled" value="1"
<?= ($settings['public_site_about_enabled'] ?? '0') === '1' ? 'checked' : '' ?>><span
class="form-check-label"><?= h(t('public_site.show_free_page')) ?></span></label></div>
<div class="col-md-4"><label class="form-label"><?= h(t('common.title')) ?></label><input
class="form-control" name="public_site_about_title"
value="<?= h($settings['public_site_about_title']) ?>"></div>
<div class="col-md-8"><label class="form-label"><?= h(t('common.text')) ?></label><textarea
class="form-control public-rich-text" name="public_site_about_text"
rows="5"><?= h($settings['public_site_about_text']) ?></textarea></div>
</div>
</details>
<details class="card mb-2 public-page-panel public-sortable-page" data-page-key="donation" draggable="true">
<summary class="card-header gap-2">
<span class="public-page-drag" title="<?= h(t('public_site.drag_page')) ?>" aria-hidden="true">⠿</span>
<h3 class="card-title">💝 <?= h(t('public_site.donation')) ?></h3>
<span class="ms-auto d-flex gap-1 public-page-actions"><button class="btn btn-sm btn-icon btn-outline-secondary public-page-up" type="button" aria-label="<?= h(t('public_site.move_up')) ?>">↑</button><button class="btn btn-sm btn-icon btn-outline-secondary public-page-down" type="button" aria-label="<?= h(t('public_site.move_down')) ?>">↓</button><span class="public-page-chevron" aria-hidden="true">⌄</span></span>
</summary>
<div class="card-body row g-3">
<div class="col-md-4"><label class="form-label"><?= h(t('common.title')) ?></label><input
class="form-control" name="public_site_donation_title"
value="<?= h($settings['public_site_donation_title']) ?>"></div>
<div class="col-md-8"><label class="form-label"><?= h(t('public_site.donation_url')) ?></label><input
class="form-control" type="url" name="public_site_donation_url"
value="<?= h($settings['public_site_donation_url']) ?>" placeholder="https://..."></div>
<div class="col-12"><label class="form-label"><?= h(t('common.text')) ?></label><textarea
class="form-control public-rich-text" name="public_site_donation_text"
rows="4"><?= h($settings['public_site_donation_text']) ?></textarea></div>
</div>
</details>
<details class="card mb-2 public-page-panel public-sortable-page" data-page-key="contact" draggable="true">
<summary class="card-header gap-2">
<span class="public-page-drag" title="<?= h(t('public_site.drag_page')) ?>" aria-hidden="true">⠿</span>
<h3 class="card-title">✉️ <?= h(t('public_site.contact')) ?></h3>
<span class="ms-auto d-flex gap-1 public-page-actions"><button class="btn btn-sm btn-icon btn-outline-secondary public-page-up" type="button" aria-label="<?= h(t('public_site.move_up')) ?>">↑</button><button class="btn btn-sm btn-icon btn-outline-secondary public-page-down" type="button" aria-label="<?= h(t('public_site.move_down')) ?>">↓</button><span class="public-page-chevron" aria-hidden="true">⌄</span></span>
</summary>
<div class="card-body row g-3">
<div class="col-12">
<div class="alert alert-info py-2"><?= h(t('public_site.contact_reuse')) ?></div><label
class="form-label"><?= h(t('common.text')) ?></label><textarea class="form-control public-rich-text"
name="public_site_contact_text" rows="3"><?= h($settings['public_site_contact_text']) ?></textarea>
</div>
<div class="col-md-6"><label class="form-label">Facebook</label><input class="form-control" type="url"
name="public_site_facebook_url" value="<?= h($settings['public_site_facebook_url']) ?>"></div>
<div class="col-md-6"><label class="form-label">Instagram</label><input class="form-control" type="url"
name="public_site_instagram_url" value="<?= h($settings['public_site_instagram_url']) ?>"></div>
<?php foreach (['youtube' => 'YouTube','tiktok' => 'TikTok','whatsapp' => 'WhatsApp','linkedin' => 'LinkedIn','x' => 'X','snapchat' => 'Snapchat'] as $network => $label): ?>
<div class="col-md-6"><label class="form-label"><?= h($label) ?></label><input class="form-control"
type="url" name="public_site_<?= h($network) ?>_url"
value="<?= h($settings['public_site_' . $network . '_url']) ?>" placeholder="https://..."></div>
<?php endforeach; ?>
</div>
</details>
<?php foreach (($customPages ?? []) as $index => $page): ?>
<details class="card mb-2 public-page-panel public-custom-page public-sortable-page" data-page-key="custom:<?= h($page['slug']) ?>" draggable="true">
<summary class="card-header gap-2">
<span class="public-page-drag" title="<?= h(t('public_site.drag_page')) ?>" aria-hidden="true">⠿</span>
<h3 class="card-title public-page-summary-title"><?= h($page['title']) ?></h3>
<span class="ms-auto d-flex gap-1 public-page-actions">
<button class="btn btn-sm btn-icon btn-outline-secondary public-page-up" type="button" title="<?= h(t('public_site.move_up')) ?>" aria-label="<?= h(t('public_site.move_up')) ?>">↑</button>
<button class="btn btn-sm btn-icon btn-outline-secondary public-page-down" type="button" title="<?= h(t('public_site.move_down')) ?>" aria-label="<?= h(t('public_site.move_down')) ?>">↓</button>
<span class="public-page-chevron" aria-hidden="true">⌄</span>
</span>
</summary>
<div class="card-body row g-3">
<input type="hidden" name="pages[<?= $index ?>][id]" value="<?= (int) $page['id'] ?>">
<input class="public-page-position" type="hidden" name="pages[<?= $index ?>][position]" value="<?= $index ?>">
<div class="col-12"><label class="form-check form-switch"><input class="form-check-input" type="checkbox" name="pages[<?= $index ?>][enabled]" value="1" <?= (int) $page['enabled'] === 1 ? 'checked' : '' ?>><span class="form-check-label"><?= h(t('public_site.page_published')) ?></span></label></div>
<div class="col-md-7"><label class="form-label"><?= h(t('public_site.page_title')) ?></label><input class="form-control public-page-title" name="pages[<?= $index ?>][title]" value="<?= h($page['title']) ?>" required></div>
<div class="col-md-5"><label class="form-label"><?= h(t('public_site.page_slug')) ?></label><input class="form-control public-page-slug" name="pages[<?= $index ?>][slug]" value="<?= h($page['slug']) ?>" required><div class="form-hint"><?= h(t('public_site.page_slug_help')) ?></div></div>
<div class="col-12"><label class="form-label"><?= h(t('common.text')) ?></label><textarea class="form-control public-rich-text" name="pages[<?= $index ?>][content]" rows="8"><?= h($page['content']) ?></textarea></div>
<div class="col-12"><label class="form-check text-danger"><input class="form-check-input" type="checkbox" name="pages[<?= $index ?>][delete]" value="1"><span class="form-check-label"><?= h(t('public_site.delete_page')) ?></span></label></div>
</div>
</details>
<?php endforeach; ?>
</div>
<template id="public-page-template">
<details class="card mb-2 public-page-panel public-custom-page public-sortable-page" data-page-key="custom:" draggable="true" open>
<summary class="card-header gap-2"><span class="public-page-drag" title="<?= h(t('public_site.drag_page')) ?>" aria-hidden="true">⠿</span><h3 class="card-title public-page-summary-title"><?= h(t('public_site.new_page')) ?></h3><span class="ms-auto d-flex gap-1 public-page-actions"><button class="btn btn-sm btn-icon btn-outline-secondary public-page-up" type="button" aria-label="<?= h(t('public_site.move_up')) ?>">↑</button><button class="btn btn-sm btn-icon btn-outline-secondary public-page-down" type="button" aria-label="<?= h(t('public_site.move_down')) ?>">↓</button><span class="public-page-chevron" aria-hidden="true">⌄</span></span></summary>
<div class="card-body row g-3"><input class="public-page-position" type="hidden" name="pages[__INDEX__][position]" value="0"><div class="col-12"><label class="form-check form-switch"><input class="form-check-input" type="checkbox" name="pages[__INDEX__][enabled]" value="1" checked><span class="form-check-label"><?= h(t('public_site.page_published')) ?></span></label></div><div class="col-md-7"><label class="form-label"><?= h(t('public_site.page_title')) ?></label><input class="form-control public-page-title" name="pages[__INDEX__][title]" required></div><div class="col-md-5"><label class="form-label"><?= h(t('public_site.page_slug')) ?></label><input class="form-control public-page-slug" name="pages[__INDEX__][slug]" required><div class="form-hint"><?= h(t('public_site.page_slug_help')) ?></div></div><div class="col-12"><label class="form-label"><?= h(t('common.text')) ?></label><textarea class="form-control public-rich-text" name="pages[__INDEX__][content]" rows="8"></textarea></div><div class="col-12"><button class="btn btn-outline-danger public-page-remove" type="button"><?= h(t('public_site.delete_page')) ?></button></div></div>
</details>
</template>
<details class="card mb-3 public-page-panel">
<summary class="card-header">
<h3 class="card-title">⚖️ <?= h(t('public_site.legal_privacy')) ?></h3>
<span class="public-page-chevron" aria-hidden="true">⌄</span>
</summary>
<div class="card-body row g-3">
<div class="col-md-6"><label class="form-label"><?= h(t('public_site.legal')) ?></label><textarea
class="form-control" name="public_site_legal_text"
rows="16"><?= h(PublicSiteController::legalText($settings)) ?></textarea></div>
<div class="col-md-6"><label class="form-label"><?= h(t('public_site.privacy')) ?></label><textarea
class="form-control" name="public_site_privacy_text"
rows="16"><?= h(PublicSiteController::privacyText($settings)) ?></textarea></div>
<div class="col-12 form-hint"><?= h(t('public_site.legal_hint')) ?></div>
</div>
</details>
<div class="text-end mb-4"><button class="btn btn-primary"><?= h(t('common.save')) ?></button></div>
</form>
<style>
.public-page-panel>summary{cursor:pointer;display:flex;align-items:center;list-style:none}.public-page-panel>summary::-webkit-details-marker{display:none}.public-page-chevron{display:inline-flex;transition:transform .2s;font-size:1.25rem}.public-page-panel[open]>summary .public-page-chevron{transform:rotate(180deg)}.public-page-drag{cursor:grab;font-size:1.35rem;color:var(--tblr-secondary);padding:.25rem}.public-custom-page.is-dragging{opacity:.45}.public-custom-page.drag-over{outline:2px dashed var(--tblr-primary);outline-offset:2px}.public-page-actions button{position:relative;z-index:2}
</style>
<script>
let activePublicEditor = null;
const imageLibrary = document.getElementById('public-image-library');
const imageHelp = document.getElementById('public-image-help');
const insertImage = (button) => {
const textarea = activePublicEditor || document.querySelector('.public-rich-text');
if (!textarea) return;
const alt = window.prompt(<?= json_encode(t('public_site.image_alt_prompt'), JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES) ?>, '');
if (alt === null) return;
const safeAlt = alt.replace(/[\[\]]/g, '').trim() || <?= json_encode(t('public_site.image_default_alt'), JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES) ?>;
textarea.setRangeText(`![${safeAlt}](${button.dataset.url})\n`, textarea.selectionStart, textarea.selectionEnd, 'end');
textarea.focus();
};
const enhanceTextarea = (textarea) => {
if (textarea.dataset.autoResize === '1') return;
textarea.dataset.autoResize = '1';
const resize = () => {
textarea.style.height = 'auto';
textarea.style.height = Math.max(140, textarea.scrollHeight + 8) + 'px';
};
textarea.addEventListener('input', resize);
resize();
};
const enhancePublicEditor = (textarea) => {
enhanceTextarea(textarea);
if (textarea.dataset.richEditor === '1') return;
textarea.dataset.richEditor = '1';
textarea.addEventListener('focus', () => { activePublicEditor = textarea; });
const toolbar = document.createElement('div');
toolbar.className = 'btn-list mb-2';
const actions = [['B', '**', '**'], ['I', '*', '*'], ['Titre', '## ', ''], ['Liste', '- ', ''], ['Lien', '[', '](https://)'], [<?= json_encode(t('public_site.markdown_table'), JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES) ?>, '| Colonne 1 | Colonne 2 |\n| --- | --- |\n| Valeur 1 | Valeur 2 |\n', '']];
actions.forEach(([label, before, after]) => {
const button = document.createElement('button');
button.type = 'button';
button.className = 'btn btn-sm btn-outline-secondary';
button.textContent = label;
button.addEventListener('click', () => {
const start = textarea.selectionStart;
const end = textarea.selectionEnd;
const selected = textarea.value.slice(start, end);
textarea.setRangeText(before + selected + after, start, end, 'select');
textarea.focus();
});
toolbar.append(button);
});
const imageButton = document.createElement('button');
imageButton.type = 'button';
imageButton.className = 'btn btn-sm btn-outline-primary';
imageButton.textContent = <?= json_encode('🖼️ ' . t('public_site.image_insert'), JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES) ?>;
imageButton.addEventListener('click', () => {
activePublicEditor = textarea;
imageHelp.textContent = <?= json_encode(t('public_site.image_target_selected'), JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES) ?>;
imageLibrary.scrollIntoView({behavior: 'smooth', block: 'center'});
});
toolbar.append(imageButton);
textarea.before(toolbar);
};
document.querySelectorAll('textarea').forEach(enhanceTextarea);
document.querySelectorAll('.public-rich-text').forEach(enhancePublicEditor);
document.querySelectorAll('.public-image-insert').forEach((button) => {
button.addEventListener('click', () => insertImage(button));
});
document.getElementById('public-image-upload').addEventListener('submit', async (event) => {
event.preventDefault();
const form = event.currentTarget;
const submit = form.querySelector('button');
submit.disabled = true;
try {
const response = await fetch(form.action, {method: 'POST', headers: {'Accept': 'application/json'}, body: new FormData(form)});
const data = await response.json();
if (!response.ok) throw new Error(data.error || <?= json_encode(t('public_site.image_invalid'), JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES) ?>);
const column = document.createElement('div');
column.className = 'col-6 col-md-3 col-xl-2';
column.innerHTML = '<div class="card h-100"><img alt="" class="card-img-top" style="height:110px;object-fit:cover"><div class="card-body p-2"><button type="button" class="btn btn-sm btn-outline-secondary w-100 public-image-insert"></button></div></div>';
const image = column.querySelector('img');
const insert = column.querySelector('button');
image.src = data.url;
insert.dataset.url = data.url;
insert.textContent = <?= json_encode(t('public_site.image_insert'), JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES) ?>;
insert.addEventListener('click', () => insertImage(insert));
imageLibrary.prepend(column);
document.getElementById('public-image-empty').classList.add('d-none');
form.reset();
insert.click();
} catch (error) {
window.alert(error.message);
} finally {
submit.disabled = false;
}
});
const pagesContainer = document.getElementById('public-pages-sortable');
const navigationOrderInput = document.getElementById('public-site-navigation-order');
let publicPageIndex = <?= (int) count($customPages ?? []) ?>;
const refreshPagePositions = () => {
const pages = [...pagesContainer.querySelectorAll('.public-sortable-page')];
pages.forEach((page, position) => {
const input = page.querySelector('.public-page-position');
if (input) input.value = String(position);
page.querySelector('.public-page-up')?.toggleAttribute('disabled', position === 0);
page.querySelector('.public-page-down')?.toggleAttribute('disabled', position === pages.length - 1);
});
navigationOrderInput.value = JSON.stringify(pages.map((page) => page.dataset.pageKey).filter((key) => key && key !== 'custom:'));
};
const slugify = (value) => value.normalize('NFD').replace(/[\u0300-\u036f]/g, '').toLowerCase().replace(/[^a-z0-9]+/g, '-').replace(/^-|-$/g, '').slice(0, 80);
const wirePage = (page) => {
const title = page.querySelector('.public-page-title');
const slug = page.querySelector('.public-page-slug');
let slugEdited = Boolean(slug?.value);
slug?.addEventListener('input', () => { slugEdited = Boolean(slug.value); });
slug?.addEventListener('input', () => { page.dataset.pageKey = 'custom:' + slugify(slug.value); refreshPagePositions(); });
title?.addEventListener('input', () => {
page.querySelector('.public-page-summary-title').textContent = title.value.trim() || <?= json_encode(t('public_site.new_page'), JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES) ?>;
if (!slugEdited && slug) {
slug.value = slugify(title.value);
page.dataset.pageKey = 'custom:' + slug.value;
refreshPagePositions();
}
});
page.querySelectorAll('.public-page-actions button').forEach((button) => button.addEventListener('click', (event) => event.stopPropagation()));
page.querySelectorAll('input,textarea,button').forEach((control) => control.addEventListener('mousedown', () => { page.draggable = false; }));
page.querySelector('.public-page-drag')?.addEventListener('mousedown', () => { page.draggable = true; });
page.querySelector('.public-page-up')?.addEventListener('click', () => {
if (page.previousElementSibling) pagesContainer.insertBefore(page, page.previousElementSibling);
refreshPagePositions();
});
page.querySelector('.public-page-down')?.addEventListener('click', () => {
if (page.nextElementSibling) pagesContainer.insertBefore(page.nextElementSibling, page);
refreshPagePositions();
});
page.querySelector('.public-page-remove')?.addEventListener('click', () => { page.remove(); refreshPagePositions(); });
page.querySelectorAll('textarea').forEach(enhanceTextarea);
page.querySelectorAll('.public-rich-text').forEach(enhancePublicEditor);
page.addEventListener('dragstart', (event) => {
if (!event.target.closest('.public-page-drag') && document.activeElement !== page.querySelector('.public-page-drag')) {
// The whole card remains draggable for browser compatibility.
}
page.classList.add('is-dragging');
event.dataTransfer.effectAllowed = 'move';
});
page.addEventListener('dragend', () => {
page.draggable = true;
page.classList.remove('is-dragging');
pagesContainer.querySelectorAll('.drag-over').forEach((item) => item.classList.remove('drag-over'));
refreshPagePositions();
});
};
let savedNavigationOrder = [];
try { savedNavigationOrder = JSON.parse(navigationOrderInput.value || '[]'); } catch (_) {}
const initialPages = [...pagesContainer.querySelectorAll('.public-sortable-page')];
const orderedPages = savedNavigationOrder.map((key) => initialPages.find((item) => item.dataset.pageKey === key)).filter(Boolean);
[...orderedPages, ...initialPages.filter((item) => !orderedPages.includes(item))].forEach((page) => pagesContainer.append(page));
pagesContainer.querySelectorAll('.public-sortable-page').forEach(wirePage);
pagesContainer.addEventListener('dragover', (event) => {
event.preventDefault();
const dragging = pagesContainer.querySelector('.is-dragging');
const target = event.target.closest('.public-sortable-page');
if (!dragging || !target || dragging === target) return;
pagesContainer.querySelectorAll('.drag-over').forEach((item) => item.classList.remove('drag-over'));
target.classList.add('drag-over');
const box = target.getBoundingClientRect();
pagesContainer.insertBefore(dragging, event.clientY < box.top + box.height / 2 ? target : target.nextElementSibling);
});
document.getElementById('public-page-add').addEventListener('click', () => {
const html = document.getElementById('public-page-template').innerHTML.replaceAll('__INDEX__', String(publicPageIndex++));
pagesContainer.insertAdjacentHTML('beforeend', html);
const page = pagesContainer.lastElementChild;
wirePage(page);
refreshPagePositions();
page.querySelector('.public-page-title').focus();
});
refreshPagePositions();
</script>