Files
Announcements/templates/control.html
T

312 lines
13 KiB
HTML
Executable File
Raw 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.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Control Panel - Church Announcements</title>
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<!-- SortableJS & Flatpickr CSS -->
<script src="https://cdnjs.cloudflare.com/ajax/libs/Sortable/1.15.0/Sortable.min.js"></script>
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/flatpickr/dist/flatpickr.min.css">
<style>
/* Basic styling */
body { background: #222; color: #fff; font-family: sans-serif; margin: 0; padding: 0; }
#pageTitle { text-align: center; margin: 1rem 0; font-family: 'Roboto', sans-serif; text-transform: uppercase; font-weight: bold; letter-spacing: 1px; font-size: 2rem; }
.preview-container { max-width: 1000px; margin: 0 auto 1rem; text-align: center; }
.preview-iframe { width: 50%; aspect-ratio: 16/9; border: 3px solid #fff; border-radius: 10px; box-shadow: 0 0 10px rgba(0,0,0,0.5); }
.tab-nav { display: flex; justify-content: center; margin-bottom: 1rem; }
.tab-nav button { background: #444; border: none; color: #fff; padding: .5rem 1rem; margin: 0 .5rem; cursor: pointer; font-size: 1rem; border-radius: 4px; transition: background .3s; }
.tab-nav button:hover { background: #666; }
.active-tab { background: #4caf50 !important; }
#announcementsSection { display: block; }
#bibleSection { display: none; max-width: 1000px; margin: 0 auto; padding: 1rem 0; }
#bibleSection h2 { text-align: center; margin-bottom: 1rem; }
/* Slider-style toggle */
#activeFilterContainer { text-align: right; padding: 0 1rem; }
.toggle-label { position: relative; display: inline-block; width: 60px; height: 34px; }
.toggle-label input { opacity: 0; width: 0; height: 0; position: absolute; }
.toggle-slider { position: absolute; top: 0; left: 0; right: 0; bottom: 0; background: #888; border-radius: 34px; transition: .4s; cursor: pointer; }
.toggle-slider:before { content: ""; position: absolute; height: 26px; width: 26px; left: 4px; bottom: 4px; background: #fff; border-radius: 50%; transition: .4s; }
.toggle-label input:checked + .toggle-slider { background: #4caf50; }
.toggle-label input:checked + .toggle-slider:before { transform: translateX(26px); }
.toggle-slider .icon { position: absolute; top: 50%; left: 50%; transform: translate(-50%, -50%); font-size: 14px; pointer-events: none; opacity: 0; transition: opacity .2s; color: #4caf50; }
.toggle-label input:checked + .toggle-slider .icon.check { opacity: 1; }
.toggle-label input:not(:checked) + .toggle-slider .icon.cross { opacity: 1; }
/* Slides grid */
.slides-container { display: grid; grid-template-columns: repeat(auto-fill, minmax(325px,1fr)); gap: 2rem; padding: 1rem; justify-items: center; }
.slide-item { width: 150px; cursor: pointer; position: relative; transition: outline-color .3s ease; }
.slide-item img { width: 100%; border-radius: 10px; outline: 3px solid transparent; outline-offset: -3px; box-shadow: 0 0 10px rgba(0,0,0,0.5); transition: outline-color .3s ease; }
.slide-item.active img { outline-color: lime; }
@media(min-width:768px){ .slide-item{ width:325px; } }
/* Bible search table */
#bibleSearchContainer { text-align: center; margin-bottom: 1rem; }
#bibleSearchInput { width: 60%; padding: .5rem; font-size:1rem; background:#333; border:1px solid #555; border-radius:4px; color:#fff; }
#bibleSearchButton { padding:.5rem 1rem; margin-left:.5rem; background:#4caf50; border:none; border-radius:4px; color:#fff; cursor:pointer; }
#bibleTable { width:100%; border-collapse:collapse; }
#bibleTable th, #bibleTable td { border:1px solid #555; padding:.5rem; text-align:left; }
#bibleTable th { background:#333; }
.custom-context-menu { position:absolute; display:none; z-index:9999; background:#333; border:1px solid #444; border-radius:8px; padding:8px; box-shadow:0 2px 5px rgba(0,0,0,0.5); min-width:160px; }
</style>
</head>
<body>
<h1 id="pageTitle">Announcements</h1>
<div class="preview-container">
<iframe id="previewIframe" class="preview-iframe" src="http://127.0.0.1:5000/"></iframe>
</div>
<!-- Slider toggle -->
<div id="activeFilterContainer">
<label class="toggle-label">
<input type="checkbox" id="activeFilterToggle">
<span class="toggle-slider">
<span class="icon check">✔</span>
<span class="icon cross">✖</span>
</span>
</label>
</div>
<!-- Tabs -->
<div class="tab-nav">
<button id="announcementsTab" class="active-tab">Announcements</button>
<button id="bibleTab">Bible Verses</button>
</div>
<!-- Announcements Section -->
<div id="announcementsSection">
<div class="slides-container" id="slidesContainer">
{% for slide, data in statuses %}
<div class="slide-item {% if data.active %}active{% endif %}"
data-slide="{{ slide }}"
data-duration="{{ data.duration|default(7) }}"
data-title="{{ data.title|default(slide.split('.')[0]) }}"
data-end_date="{{ data.end_date|default('') }}">
<img src="{{ slide_urls[slide] }}" alt="{{ slide }}">
</div>
{% endfor %}
</div>
</div>
<!-- Bible Verses Section -->
<div id="bibleSection">
<h2>Bible Verses</h2>
<div id="bibleSearchContainer">
<input type="text" id="bibleSearchInput" placeholder="Search verses..." />
<button id="bibleSearchButton">Search</button>
</div>
<table id="bibleTable">
<thead>
<tr>
<th>Book</th><th>Chapter</th><th>Verse</th><th>Text</th><th>Translation</th><th>Add/Remove</th>
</tr>
</thead>
<tbody></tbody>
</table>
</div>
<!-- Context Menu -->
<div class="custom-context-menu" id="customContextMenu">
<!-- existing context-menu items + submenus -->
</div>
<!-- Scripts -->
<script src="https://cdnjs.cloudflare.com/ajax/libs/socket.io/4.5.4/socket.io.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/flatpickr"></script>
<script>
// 1) Load persisted registry
let bibleRegistry = [];
document.addEventListener('DOMContentLoaded', () => {
fetch('/bible_registry')
.then(r => r.json())
.then(data => { bibleRegistry = data; })
.catch(console.error);
});
// 2) Tab switching
const announcementsTab = document.getElementById('announcementsTab'),
bibleTab = document.getElementById('bibleTab'),
announcementsSec = document.getElementById('announcementsSection'),
bibleSec = document.getElementById('bibleSection'),
pageTitle = document.getElementById('pageTitle'),
activeToggle = document.getElementById('activeFilterToggle'),
slidesContainer = document.getElementById('slidesContainer');
announcementsTab.addEventListener('click', () => {
announcementsSec.style.display = 'block';
bibleSec.style.display = 'none';
announcementsTab.classList.add('active-tab');
bibleTab.classList.remove('active-tab');
pageTitle.innerText = 'Announcements';
activeToggle.parentElement.style.display = 'block';
});
bibleTab.addEventListener('click', () => {
announcementsSec.style.display = 'none';
bibleSec.style.display = 'block';
bibleTab.classList.add('active-tab');
announcementsTab.classList.remove('active-tab');
pageTitle.innerText = 'Bible Verses';
activeToggle.parentElement.style.display = 'none';
});
// 3) Drag-and-drop ordering
let isDragging = false;
Sortable.create(slidesContainer, {
animation: 150, delay: 50,
onStart: () => { isDragging = true; },
onEnd: () => {
isDragging = false;
const order = Array.from(slidesContainer.children).map(c => c.dataset.slide);
fetch('/update_order', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ order })
});
}
});
// 4) Toggle hiding inactive slides
activeToggle.addEventListener('change', () => {
slidesContainer.querySelectorAll('.slide-item').forEach(item => {
item.style.display = activeToggle.checked && !item.classList.contains('active')
? 'none' : 'block';
});
});
// 5) Click slide to activate/deactivate
slidesContainer.addEventListener('click', e => {
if (isDragging) return;
const item = e.target.closest('.slide-item');
if (item && e.button === 0) {
item.classList.toggle('active');
fetch('/update_active', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
slide: item.dataset.slide,
active: item.classList.contains('active')
})
});
}
});
// 6) Bible search & add/remove logic
const bibleSearchInput = document.getElementById('bibleSearchInput'),
bibleSearchButton = document.getElementById('bibleSearchButton'),
bibleTableBody = document.querySelector('#bibleTable tbody');
bibleSearchButton.addEventListener('click', () => {
const q = bibleSearchInput.value.trim();
bibleTableBody.innerHTML = '';
// Empty query → show what's in registry
if (!q) {
bibleRegistry.forEach(entry => {
const idx = entry.reference.lastIndexOf(' '),
book = entry.reference.slice(0, idx),
[chapter, verse] = entry.reference.slice(idx + 1).split(':'),
tr = document.createElement('tr');
tr.innerHTML = `
<td>${book}</td>
<td>${chapter}</td>
<td>${verse}</td>
<td>${entry.verse}</td>
<td></td>
<td></td>
`;
const btn = document.createElement('button');
btn.textContent = '–';
btn.style.width = '2rem';
btn.dataset.id = entry.id;
btn.addEventListener('click', () => {
fetch('/remove_bible_verse', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ id: Number(btn.dataset.id) })
})
.then(r => r.json())
.then(resp => {
if (resp.status === 'success') {
bibleRegistry = bibleRegistry.filter(e => e.id !== entry.id);
tr.remove();
}
});
});
tr.cells[5].appendChild(btn);
bibleTableBody.appendChild(tr);
});
return;
}
// Non-empty → query DB
fetch('/search_verses?q=' + encodeURIComponent(q))
.then(r => r.json())
.then(results => {
results.forEach(verse => {
const ref = `${verse.book} ${verse.chapter}:${verse.verse}`,
exist = bibleRegistry.find(e => e.reference === ref),
tr = document.createElement('tr');
tr.innerHTML = `
<td>${verse.book}</td>
<td>${verse.chapter}</td>
<td>${verse.verse}</td>
<td>${verse.text}</td>
<td>${verse.translation}</td>
<td></td>
`;
const btn = document.createElement('button');
btn.style.width = '2rem';
if (exist) {
btn.textContent = '–';
btn.dataset.id = exist.id;
} else {
btn.textContent = '+';
}
btn.addEventListener('click', () => {
if (btn.textContent === '+') {
// Add
fetch('/add_bible_verse', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ verse: verse.text, reference: ref })
})
.then(r => r.json())
.then(resp => {
if (resp.status === 'success') {
btn.textContent = '–';
btn.dataset.id = resp.entry.id;
bibleRegistry.push(resp.entry);
}
});
} else {
// Remove
const id = Number(btn.dataset.id);
fetch('/remove_bible_verse', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ id })
})
.then(r => r.json())
.then(resp => {
if (resp.status === 'success') {
bibleRegistry = bibleRegistry.filter(e => e.id !== id);
btn.textContent = '+';
}
});
}
});
tr.cells[5].appendChild(btn);
bibleTableBody.appendChild(tr);
});
});
});
bibleSearchInput.addEventListener('keydown', e => {
if (e.key === 'Enter') bibleSearchButton.click();
});
</script>
</body>
</html>