391 lines
13 KiB
HTML
391 lines
13 KiB
HTML
{% extends 'base.html' %}
|
|
{% block title %}Bewerk Liturgiebord - Digitale Liturgie{% endblock %}
|
|
{% block content %}
|
|
<style>
|
|
.edit-board-container {
|
|
padding: 1rem 1rem;
|
|
background: white;
|
|
border-radius: 0.5rem;
|
|
margin: 0 auto 1rem auto;
|
|
}
|
|
|
|
.board-title {
|
|
font-size: 2rem;
|
|
font-weight: 600;
|
|
margin-bottom: 1rem;
|
|
color: #000000;
|
|
text-align: left;
|
|
}
|
|
|
|
.lines-section {
|
|
margin-bottom: 2.5rem;
|
|
}
|
|
|
|
.line-input-group {
|
|
margin-bottom: 1.25rem;
|
|
}
|
|
|
|
.line-input-group label {
|
|
display: block;
|
|
margin-bottom: 0.75rem;
|
|
font-weight: 700;
|
|
color: #374151;
|
|
}
|
|
|
|
.line-input-group input {
|
|
width: 100%;
|
|
padding: 0.75rem 1rem;
|
|
font-size: 1.125rem;
|
|
border-radius: 0.5rem;
|
|
border: 1px solid #d1d5db;
|
|
box-shadow: inset 0 1px 2px rgb(0 0 0 / 0.1);
|
|
transition: border-color 0.2s ease-in-out, box-shadow 0.2s ease-in-out;
|
|
}
|
|
|
|
.line-input-group input:focus {
|
|
outline: none;
|
|
border-color: #2563eb;
|
|
box-shadow: 0 0 5px #93c5fd;
|
|
}
|
|
|
|
.button-group {
|
|
display: flex;
|
|
gap: 1rem;
|
|
justify-content: flex-start;
|
|
margin-top: 1.5rem;
|
|
}
|
|
|
|
.button-group button {
|
|
padding: 0.75rem 1.75rem;
|
|
font-size: 1rem;
|
|
border-radius: 0.5rem;
|
|
font-weight: 700;
|
|
transition: background-color 0.15s ease-in-out;
|
|
}
|
|
|
|
.schedule-section {
|
|
max-width: 100%;
|
|
margin-bottom: 2.5rem;
|
|
}
|
|
|
|
.schedule-section h3 {
|
|
margin-bottom: 1rem;
|
|
}
|
|
|
|
.schedule-section table {
|
|
width: 100%;
|
|
}
|
|
|
|
.preview-section {
|
|
max-width: 100%;
|
|
text-align: center;
|
|
font-family: 'Lora', serif;
|
|
}
|
|
|
|
.preview-wrapper {
|
|
width: 250px;
|
|
height: 444px; /* 9:16 aspect ratio */
|
|
margin: 0 auto;
|
|
background: #222;
|
|
border-radius: 0.5rem;
|
|
overflow: hidden;
|
|
}
|
|
|
|
iframe#live-preview-iframe {
|
|
width: 1080px;
|
|
height: 1920px;
|
|
border: 0;
|
|
pointer-events: none;
|
|
transform-origin: top left;
|
|
transform: scale(0.23);
|
|
margin: 0;
|
|
display: block;
|
|
}
|
|
|
|
@media (max-width: 767px) {
|
|
.edit-board-container {
|
|
padding: 15px;
|
|
margin-bottom: 1rem;
|
|
border-radius: 0.5rem;
|
|
}
|
|
|
|
.board-title {
|
|
font-size: 1.75rem;
|
|
margin-bottom: 1.5rem;
|
|
}
|
|
|
|
.button-group {
|
|
flex-direction: column;
|
|
gap: 8px;
|
|
}
|
|
|
|
.button-group button {
|
|
width: 100%;
|
|
}
|
|
|
|
.preview-wrapper {
|
|
width: 180px;
|
|
height: 320px;
|
|
margin: 0 auto;
|
|
border-radius: 0.5rem;
|
|
background: #222;
|
|
overflow: hidden;
|
|
}
|
|
|
|
iframe#live-preview-iframe {
|
|
transform: scale(0.23);
|
|
position: static;
|
|
width: 1080px;
|
|
height: 1920px;
|
|
margin: 0 auto;
|
|
display: block;
|
|
}
|
|
}
|
|
|
|
/* Highlight for drag-over between lines in preview */
|
|
.liturgie-line.line-over {
|
|
background: rgba(255,255,255,0.22);
|
|
border: 2px dashed #fff;
|
|
box-sizing: border-box;
|
|
outline: none;
|
|
transition: background 0.12s, border 0.12s;
|
|
}
|
|
|
|
.liturgie-line.swap-animate {
|
|
animation: swap-pulse 0.35s;
|
|
}
|
|
@keyframes swap-pulse {
|
|
0% { background: #ffe066; }
|
|
60% { background: #fff3cd; }
|
|
100% { background: none; }
|
|
}
|
|
</style>
|
|
|
|
<div class="edit-board-container">
|
|
<div style="display: flex; gap: 2rem; align-items: flex-start; flex-wrap: wrap;">
|
|
<!-- Left: Live (editable) preview -->
|
|
<form method="post" id="boardForm" style="flex: 1 1 300px; min-width: 250px; max-width:250px;">
|
|
<div class="preview-section">
|
|
<div class="preview-wrapper"
|
|
style="width:250px; height:444px; background: #222; border-radius: 0.5rem; overflow: hidden; position:relative;
|
|
background-image: {% if board.background_image %}url('{% if board.background_image.startswith('default_') %}{{ url_for('static', filename=board.background_image) }}{% else %}{{ url_for('uploaded_file', filename=board.background_image) }}{% endif %}'){% else %}none{% endif %}; background-size:cover; background-position:center;">
|
|
{% if board.background_image %}
|
|
<div class="liturgie-bg-overlay" style="
|
|
content:''; position:absolute; top:0; left:0; right:0; bottom:0;
|
|
background:rgba(0,0,0,0.3); z-index:1; pointer-events:none;">
|
|
</div>
|
|
{% endif %}
|
|
|
|
{% if schedule_active %}
|
|
{% set matched_schedules = schedules|selectattr('content', 'equalto', schedule_content)|list %}
|
|
<div style="position:absolute; top:0; left:0; right:0; bottom:0; z-index:10; display:flex; justify-content:center; align-items:center; background: rgba(0,0,0,0.7); color:white; font-weight:bold; font-size:1rem; text-align:center; padding: 1rem;">
|
|
Geplande dienst ({{ matched_schedules[0].name if matched_schedules|length > 0 else 'Onbekend' }}) is nu actief
|
|
</div>
|
|
{% endif %}
|
|
|
|
<div class="liturgie-board"
|
|
style="width:100%; height:100%; display:flex; flex-direction:column; justify-content:center; align-items:flex-start; gap:0.6vh; padding-left:5%; position:relative; z-index:2;">
|
|
{% if schedule_active %}
|
|
{% set lines = schedule_content.split('\\n') %}
|
|
{% for i in range(10) %}
|
|
<div class="liturgie-line" style="width:90%; font-size:28px; line-height:1.2; text-shadow:2px 2px 8px #000, 0 0 4px #000; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; margin:0; padding:0; color:#fff; text-align:left;">
|
|
{{ lines[i] if lines|length > i else ' ' }}
|
|
</div>
|
|
{% endfor %}
|
|
{% else %}
|
|
{% for i in range(1, 11) %}
|
|
<div class="liturgie-line"
|
|
contenteditable="true"
|
|
data-line="line{{ i }}"
|
|
draggable="true"
|
|
style="width:90%; font-size:28px; line-height:1.2; text-shadow:2px 2px 8px #000, 0 0 4px #000; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; margin:0; padding:0; color:#fff; text-align:left;"
|
|
>{{ board['line' ~ i] or ' ' }}</div>
|
|
{% endfor %}
|
|
{% endif %}
|
|
</div>
|
|
</div>
|
|
{% if schedule_active %}
|
|
<p style="color: red;">Er is een dienst actief! Pas deze aan via de planning.</p>
|
|
{% endif %}
|
|
</div>
|
|
</form>
|
|
<!-- Right: Schedule section -->
|
|
<div class="schedule-section" style="flex: 1 1 380px; min-width:330px;">
|
|
<form method="post" style="margin-bottom: 1rem;">
|
|
<input type="hidden" name="form_type" value="name">
|
|
<div style="display:flex; gap:8px; align-items:center;">
|
|
<input id="boardNameInput" name="name" type="text" value="{{ board.name }}" style="font-size:1.1rem; padding:0.3em 0.6em; border-radius:0.3em; border:1px solid #aaa; width:60%;">
|
|
<button type="submit" class="bg-[#f7d91a] text-black text-xs px-6 py-3 rounded hover:bg-yellow-300 font-semibold transition" style="margin-left:5px;">Opslaan</button>
|
|
</div>
|
|
</form>
|
|
<h3 class="text-xl font-semibold mb-3">Geplande diensten</h3>
|
|
<div class="overflow-x-auto rounded-lg border border-gray-300">
|
|
{% if schedules|length == 0 %}
|
|
<p class="p-4">Geen geplande inhoud.</p>
|
|
{% else %}
|
|
<table class="min-w-full divide-y divide-gray-200 bg-white">
|
|
<thead class="bg-gray-100">
|
|
<tr>
|
|
<th class="px-6 py-3 text-left text-sm font-semibold text-gray-700">Naam</th>
|
|
<th class="px-6 py-3 text-sm font-semibold text-gray-700 text-center">Datum</th>
|
|
<th class="px-6 py-3 text-sm font-semibold text-gray-700 text-center">Starttijd</th>
|
|
<th class="px-6 py-3 text-sm font-semibold text-gray-700 text-center">Eindtijd</th>
|
|
<th class="px-6 py-3 text-sm font-semibold text-gray-700 text-center">Acties</th>
|
|
</tr>
|
|
</thead>
|
|
<tbody class="divide-y divide-gray-200">
|
|
{% for s in schedules %}
|
|
<tr>
|
|
<td class="whitespace-nowrap px-6 py-4 text-gray-900 flex items-center gap-2">
|
|
<span style="display:inline-block; width:0.75rem; height:0.75rem; border-radius:9999px;"
|
|
title="{% if schedule_active and active_schedule and s.id == active_schedule.id %}Actief{% else %}Niet actief{% endif %}"
|
|
class="{% if schedule_active and active_schedule and s.id == active_schedule.id %}bg-green-500{% else %}bg-red-500{% endif %}">
|
|
</span>
|
|
{{ s.name }}
|
|
</td>
|
|
<td class="px-6 py-4 text-center text-gray-900">{{ s.date.strftime('%Y-%m-%d') }}</td>
|
|
<td class="px-6 py-4 text-center text-gray-900">{{ s.start_time.strftime('%H:%M') }}</td>
|
|
<td class="px-6 py-4 text-center text-gray-900">{{ s.end_time.strftime('%H:%M') }}</td>
|
|
<td class="px-6 py-4 text-center">
|
|
<!-- Removed delete button here: schedules can't be deleted from board edit page -->
|
|
<a href="{{ url_for('edit_global_schedule', schedule_id=s.id) }}" class="bg-[#f7d91a] text-black text-xs px-6 py-3 rounded hover:bg-yellow-300 font-semibold transition">Bewerken</a>
|
|
</td>
|
|
</tr>
|
|
{% endfor %}
|
|
</tbody>
|
|
</table>
|
|
{% endif %}
|
|
</div>
|
|
<a href="{{ url_for('add_global_schedule') }}" class="bg-[#f7d91a] text-black text-xs px-6 py-3 rounded hover:bg-yellow-300 font-semibold inline-block mt-3 transition">Dienst toevoegen</a>
|
|
<div class="button-group mt-3">
|
|
<button type="button" class="bg-white text-black border border-black px-6 py-3 rounded font-semibold hover:bg-gray-100 transition"
|
|
id="save-and-upload-bg"
|
|
data-edit-url="{{ url_for('edit_board', board_id=board.id) }}"
|
|
data-upload-url="{{ url_for('upload_background', board_id=board.id) }}">
|
|
Achtergrondafbeelding wijzigen
|
|
</button>
|
|
<!-- Removed Opslaan button as live save is implemented -->
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
</div>
|
|
|
|
|
|
{% if not schedule_active %}
|
|
<script>
|
|
// --- Line drag-and-swap logic ---
|
|
document.addEventListener('DOMContentLoaded', function () {
|
|
let dragSrc = null;
|
|
const lines = document.querySelectorAll('.liturgie-line[data-line]');
|
|
lines.forEach(line => {
|
|
line.addEventListener('dragstart', function (e) {
|
|
dragSrc = this;
|
|
this.classList.add('dragging');
|
|
});
|
|
line.addEventListener('dragend', function (e) {
|
|
this.classList.remove('dragging');
|
|
dragSrc = null;
|
|
lines.forEach(l => l.classList.remove('line-over'));
|
|
});
|
|
line.addEventListener('dragover', function (e) {
|
|
e.preventDefault();
|
|
if (this !== dragSrc) this.classList.add('line-over');
|
|
});
|
|
line.addEventListener('dragleave', function (e) {
|
|
this.classList.remove('line-over');
|
|
});
|
|
line.addEventListener('drop', function (e) {
|
|
e.preventDefault();
|
|
this.classList.remove('line-over');
|
|
if (dragSrc && dragSrc !== this) {
|
|
// Only swap textContent, not the whole element
|
|
const tmp = this.textContent;
|
|
this.textContent = dragSrc.textContent;
|
|
dragSrc.textContent = tmp;
|
|
// animation for both lines
|
|
this.classList.add('swap-animate');
|
|
dragSrc.classList.add('swap-animate');
|
|
setTimeout(() => {
|
|
this.classList.remove('swap-animate');
|
|
dragSrc.classList.remove('swap-animate');
|
|
}, 400);
|
|
// trigger input event for autosave
|
|
this.dispatchEvent(new Event('input'));
|
|
dragSrc.dispatchEvent(new Event('input'));
|
|
}
|
|
});
|
|
});
|
|
});
|
|
// --- End drag-and-swap logic ---
|
|
|
|
// On submit, copy preview lines to hidden fields
|
|
const boardForm = document.getElementById('boardForm');
|
|
if (boardForm) {
|
|
for (let i = 1; i <= 10; i++) {
|
|
// create hidden inputs for each line
|
|
let hidden = document.createElement('input');
|
|
hidden.type = 'hidden';
|
|
hidden.name = 'line'+i;
|
|
hidden.id = 'hidden-line'+i;
|
|
boardForm.appendChild(hidden);
|
|
}
|
|
|
|
async function saveLines() {
|
|
const formData = new FormData();
|
|
for (let i = 1; i <= 10; i++) {
|
|
const text = document.querySelector('.liturgie-line[data-line="line'+i+'"]').textContent;
|
|
formData.append('line'+i, text);
|
|
}
|
|
|
|
const response = await fetch(boardForm.action, {
|
|
method: 'POST',
|
|
body: formData,
|
|
headers: { 'X-Requested-With': 'XMLHttpRequest' }
|
|
});
|
|
|
|
if (!response.ok) {
|
|
console.error('Failed to save lines.');
|
|
}
|
|
}
|
|
|
|
// Add auto save event listeners to editable lines with debounce
|
|
for (let i = 1; i <= 10; i++) {
|
|
const editableLine = document.querySelector('.liturgie-line[data-line="line'+i+'"]');
|
|
if (editableLine) {
|
|
editableLine.addEventListener('input', () => {
|
|
if (window.autoSaveTimeout) clearTimeout(window.autoSaveTimeout);
|
|
window.autoSaveTimeout = setTimeout(saveLines, 1000);
|
|
});
|
|
}
|
|
}
|
|
|
|
// Prevent form's default submit to avoid page reload on autosave
|
|
boardForm.addEventListener('submit', function(e) {
|
|
if (document.getElementById('redirect_to_upload').value === '0') {
|
|
e.preventDefault();
|
|
saveLines();
|
|
}
|
|
});
|
|
}
|
|
|
|
// Added script for wallpaper button
|
|
document.addEventListener('DOMContentLoaded', function() {
|
|
const btn = document.getElementById('save-and-upload-bg');
|
|
if (btn) {
|
|
btn.addEventListener('click', function() {
|
|
const uploadUrl = this.getAttribute('data-upload-url');
|
|
if (uploadUrl) {
|
|
window.location.href = uploadUrl;
|
|
}
|
|
});
|
|
}
|
|
});
|
|
</script>
|
|
{% else %}
|
|
<script>
|
|
// When schedule is active, there's nothing to submit for the lines and nothing to sync.
|
|
</script>
|
|
{% endif %}
|
|
|
|
{% endblock %} |