2025-11-03 15:23:11 +00:00
|
|
|
|
// Globale state
|
|
|
|
|
|
let config = null;
|
2025-12-12 07:34:45 +00:00
|
|
|
|
let stellingkastItems = [];
|
|
|
|
|
|
let dragSrcEl = null;
|
2025-12-16 06:36:55 +00:00
|
|
|
|
let allSets = null;
|
|
|
|
|
|
let currentSetId = null;
|
2025-11-03 15:23:11 +00:00
|
|
|
|
|
|
|
|
|
|
// DOM elementen
|
|
|
|
|
|
const timerInput = document.getElementById('timer');
|
|
|
|
|
|
const fontSizeInput = document.getElementById('fontSize');
|
|
|
|
|
|
const buttonTextInput = document.getElementById('buttonText');
|
|
|
|
|
|
const finishTextInput = document.getElementById('finishText');
|
|
|
|
|
|
const colorLeftInput = document.getElementById('colorLeft');
|
|
|
|
|
|
const colorLeftTextInput = document.getElementById('colorLeftText');
|
|
|
|
|
|
const colorRightInput = document.getElementById('colorRight');
|
|
|
|
|
|
const colorRightTextInput = document.getElementById('colorRightText');
|
|
|
|
|
|
const statementsList = document.getElementById('statementsList');
|
|
|
|
|
|
const addStatementBtn = document.getElementById('addStatement');
|
|
|
|
|
|
const saveConfigBtn = document.getElementById('saveConfig');
|
|
|
|
|
|
const statusMessage = document.getElementById('statusMessage');
|
|
|
|
|
|
const statementCount = document.getElementById('statementCount');
|
|
|
|
|
|
|
2025-12-12 07:34:45 +00:00
|
|
|
|
// Stellingkast elementen
|
|
|
|
|
|
const openStellingkastBtn = document.getElementById('openStellingkast');
|
|
|
|
|
|
const closeStellingkastBtn = document.getElementById('closeStellingkast');
|
|
|
|
|
|
const stellingkastPanel = document.getElementById('stellingkastPanel');
|
|
|
|
|
|
const stellingkastList = document.getElementById('stellingkastList');
|
|
|
|
|
|
const searchStellingkastInput = document.getElementById('searchStellingkast');
|
|
|
|
|
|
|
2025-12-16 06:36:55 +00:00
|
|
|
|
// Sets elementen
|
|
|
|
|
|
const setSelector = document.getElementById('setSelector');
|
|
|
|
|
|
const saveAsNewSetBtn = document.getElementById('saveAsNewSet');
|
|
|
|
|
|
const deleteSetBtn = document.getElementById('deleteSet');
|
|
|
|
|
|
|
2025-12-12 07:34:45 +00:00
|
|
|
|
// Initialize
|
|
|
|
|
|
syncColorInputs();
|
2025-12-16 06:36:55 +00:00
|
|
|
|
// Start alle laad-acties onafhankelijk van elkaar
|
|
|
|
|
|
loadSets();
|
2025-12-12 07:34:45 +00:00
|
|
|
|
loadStellingkast();
|
|
|
|
|
|
|
2025-12-16 06:36:55 +00:00
|
|
|
|
// --- SETS MANAGEMENT ---
|
|
|
|
|
|
|
|
|
|
|
|
// Laad alle sets bij start
|
|
|
|
|
|
async function loadSets() {
|
|
|
|
|
|
try {
|
|
|
|
|
|
console.log('Fetching sets...');
|
|
|
|
|
|
const response = await fetch('/api/sets?t=' + Date.now());
|
|
|
|
|
|
if (!response.ok) throw new Error('Kon sets niet laden (' + response.status + ')');
|
|
|
|
|
|
|
|
|
|
|
|
allSets = await response.json();
|
|
|
|
|
|
console.log('Sets loaded:', allSets);
|
|
|
|
|
|
|
|
|
|
|
|
// Ensure structure
|
|
|
|
|
|
if (!allSets.sets) allSets.sets = [];
|
|
|
|
|
|
|
|
|
|
|
|
populateSetSelector();
|
|
|
|
|
|
|
|
|
|
|
|
// Laad actieve set of eerste set
|
|
|
|
|
|
if (allSets.activeSetId) {
|
|
|
|
|
|
currentSetId = allSets.activeSetId;
|
|
|
|
|
|
} else if (allSets.sets.length > 0) {
|
|
|
|
|
|
currentSetId = allSets.sets[0].id;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if (currentSetId) {
|
|
|
|
|
|
loadSetById(currentSetId);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
showStatus('Sets geladen!', 'success');
|
|
|
|
|
|
} catch (error) {
|
|
|
|
|
|
console.error('Fout bij laden sets:', error);
|
|
|
|
|
|
showStatus('Fout bij laden sets: ' + error.message, 'error');
|
|
|
|
|
|
// Fallback naar oude config.json manier
|
|
|
|
|
|
loadConfig();
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// Vul set selector dropdown
|
|
|
|
|
|
function populateSetSelector() {
|
|
|
|
|
|
if (!setSelector || !allSets) return;
|
|
|
|
|
|
|
|
|
|
|
|
setSelector.innerHTML = '<option value="">-- Nieuwe set --</option>';
|
|
|
|
|
|
|
|
|
|
|
|
allSets.sets.forEach(set => {
|
|
|
|
|
|
const option = document.createElement('option');
|
|
|
|
|
|
option.value = set.id;
|
|
|
|
|
|
option.textContent = set.name;
|
|
|
|
|
|
if (set.id === currentSetId) {
|
|
|
|
|
|
option.selected = true;
|
|
|
|
|
|
}
|
|
|
|
|
|
setSelector.appendChild(option);
|
|
|
|
|
|
});
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// Laad een specifieke set in de editor
|
|
|
|
|
|
function loadSetById(setId) {
|
|
|
|
|
|
const set = allSets.sets.find(s => s.id === setId);
|
|
|
|
|
|
if (!set || !set.config) {
|
|
|
|
|
|
console.error('Set niet gevonden:', setId);
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
config = set.config;
|
|
|
|
|
|
currentSetId = setId;
|
|
|
|
|
|
|
|
|
|
|
|
// Ensure minimal config structure
|
|
|
|
|
|
if (!config.stellingen) config.stellingen = [];
|
|
|
|
|
|
if (!config.colors) config.colors = { left: '#3B82F6', right: '#EF4444' };
|
|
|
|
|
|
|
|
|
|
|
|
populateForm();
|
|
|
|
|
|
showStatus(`Set geladen: ${set.name}`, 'success');
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// Event handler voor set selectie
|
|
|
|
|
|
function onSetSelected(event) {
|
|
|
|
|
|
const setId = event.target.value;
|
|
|
|
|
|
|
|
|
|
|
|
if (!setId) {
|
|
|
|
|
|
// "Nieuwe set" optie gekozen
|
|
|
|
|
|
currentSetId = null;
|
|
|
|
|
|
config = {
|
|
|
|
|
|
timer: 30,
|
|
|
|
|
|
fontSize: '3rem',
|
|
|
|
|
|
buttonText: 'Volgende Stelling',
|
|
|
|
|
|
finishText: 'Einde!',
|
|
|
|
|
|
colors: { left: '#3B82F6', right: '#EF4444' },
|
|
|
|
|
|
stellingen: []
|
|
|
|
|
|
};
|
|
|
|
|
|
populateForm();
|
|
|
|
|
|
showStatus('Nieuwe set - vul gegevens in en gebruik "Opslaan als Nieuwe Set"', 'info');
|
|
|
|
|
|
} else {
|
|
|
|
|
|
loadSetById(setId);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// Sla huidige config op naar actieve set
|
|
|
|
|
|
async function saveCurrentSet() {
|
|
|
|
|
|
if (!currentSetId) {
|
|
|
|
|
|
showStatus('Geen actieve set - gebruik "Opslaan als Nieuwe Set"', 'error');
|
|
|
|
|
|
return false;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
try {
|
|
|
|
|
|
// Update set in allSets
|
|
|
|
|
|
const setIndex = allSets.sets.findIndex(s => s.id === currentSetId);
|
|
|
|
|
|
if (setIndex === -1) {
|
|
|
|
|
|
showStatus('Set niet gevonden', 'error');
|
|
|
|
|
|
return false;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// Update config van deze set
|
|
|
|
|
|
allSets.sets[setIndex].config = { ...config };
|
|
|
|
|
|
allSets.activeSetId = currentSetId;
|
|
|
|
|
|
|
|
|
|
|
|
// Sla op naar server
|
|
|
|
|
|
const response = await fetch('/api/sets', {
|
|
|
|
|
|
method: 'POST',
|
|
|
|
|
|
headers: { 'Content-Type': 'application/json' },
|
|
|
|
|
|
body: JSON.stringify(allSets)
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
const result = await response.json();
|
|
|
|
|
|
|
|
|
|
|
|
if (result.success) {
|
|
|
|
|
|
showStatus('✅ Set opgeslagen!', 'success');
|
|
|
|
|
|
return true;
|
|
|
|
|
|
} else {
|
|
|
|
|
|
showStatus(`❌ Fout: ${result.error}`, 'error');
|
|
|
|
|
|
return false;
|
|
|
|
|
|
}
|
|
|
|
|
|
} catch (error) {
|
|
|
|
|
|
console.error('Fout bij opslaan set:', error);
|
|
|
|
|
|
showStatus('❌ Fout bij opslaan set', 'error');
|
|
|
|
|
|
return false;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// Sla op als nieuwe set (met naam prompt)
|
|
|
|
|
|
async function saveAsNewSet() {
|
|
|
|
|
|
const setName = prompt('Geef een naam voor deze set:', 'Nieuwe Workshop Set');
|
|
|
|
|
|
|
|
|
|
|
|
if (!setName || setName.trim() === '') {
|
|
|
|
|
|
showStatus('Opslaan geannuleerd', 'info');
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
try {
|
|
|
|
|
|
// Genereer unieke ID
|
|
|
|
|
|
const newId = 'set-' + Date.now();
|
|
|
|
|
|
|
|
|
|
|
|
// Maak nieuwe set
|
|
|
|
|
|
const newSet = {
|
|
|
|
|
|
id: newId,
|
|
|
|
|
|
name: setName.trim(),
|
|
|
|
|
|
config: { ...config }
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
// Voeg toe aan sets
|
|
|
|
|
|
allSets.sets.push(newSet);
|
|
|
|
|
|
allSets.activeSetId = newId;
|
|
|
|
|
|
currentSetId = newId;
|
|
|
|
|
|
|
|
|
|
|
|
// Sla op naar server
|
|
|
|
|
|
const response = await fetch('/api/sets', {
|
|
|
|
|
|
method: 'POST',
|
|
|
|
|
|
headers: { 'Content-Type': 'application/json' },
|
|
|
|
|
|
body: JSON.stringify(allSets)
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
const result = await response.json();
|
|
|
|
|
|
|
|
|
|
|
|
if (result.success) {
|
|
|
|
|
|
populateSetSelector();
|
|
|
|
|
|
setSelector.value = newId;
|
|
|
|
|
|
showStatus(`✅ Nieuwe set "${setName}" aangemaakt!`, 'success');
|
|
|
|
|
|
} else {
|
|
|
|
|
|
showStatus(`❌ Fout: ${result.error}`, 'error');
|
|
|
|
|
|
}
|
|
|
|
|
|
} catch (error) {
|
|
|
|
|
|
console.error('Fout bij aanmaken set:', error);
|
|
|
|
|
|
showStatus('❌ Fout bij aanmaken set', 'error');
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// Toon status bericht
|
|
|
|
|
|
function showStatus(message, type = 'info') {
|
|
|
|
|
|
if (!statusMessage) return;
|
|
|
|
|
|
|
|
|
|
|
|
statusMessage.textContent = message;
|
|
|
|
|
|
statusMessage.className = 'status-message ' + type;
|
|
|
|
|
|
statusMessage.style.display = 'block';
|
|
|
|
|
|
|
|
|
|
|
|
// Auto-hide na 5 seconden
|
|
|
|
|
|
setTimeout(() => {
|
|
|
|
|
|
statusMessage.style.display = 'none';
|
|
|
|
|
|
}, 5000);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// Fallback: Laad config op oude manier (voor backwards compatibility)
|
2025-11-03 15:23:11 +00:00
|
|
|
|
async function loadConfig() {
|
|
|
|
|
|
try {
|
2025-12-12 07:34:45 +00:00
|
|
|
|
console.log('Fetching config...');
|
|
|
|
|
|
const response = await fetch('/api/config?t=' + Date.now());
|
|
|
|
|
|
if (!response.ok) throw new Error('Kon config niet laden (' + response.status + ')');
|
2025-12-16 06:36:55 +00:00
|
|
|
|
|
2025-11-03 15:23:11 +00:00
|
|
|
|
config = await response.json();
|
2025-12-12 07:34:45 +00:00
|
|
|
|
console.log('Config loaded:', config);
|
2025-12-16 06:36:55 +00:00
|
|
|
|
|
2025-12-12 07:34:45 +00:00
|
|
|
|
// Ensure minimal config structure
|
|
|
|
|
|
if (!config.stellingen) config.stellingen = [];
|
|
|
|
|
|
if (!config.colors) config.colors = { left: '#3B82F6', right: '#EF4444' };
|
2025-12-16 06:36:55 +00:00
|
|
|
|
|
2025-11-03 15:23:11 +00:00
|
|
|
|
populateForm();
|
|
|
|
|
|
showStatus('Config geladen!', 'success');
|
|
|
|
|
|
} catch (error) {
|
|
|
|
|
|
console.error('Fout bij laden config:', error);
|
2025-12-12 07:34:45 +00:00
|
|
|
|
showStatus('Fout bij laden config: ' + error.message, 'error');
|
2025-11-03 15:23:11 +00:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// Vul formulier met config data
|
|
|
|
|
|
function populateForm() {
|
2025-12-12 07:34:45 +00:00
|
|
|
|
if (!config) return;
|
|
|
|
|
|
|
|
|
|
|
|
// Basis instellingen (met fallback values)
|
|
|
|
|
|
timerInput.value = config.timer || 30;
|
|
|
|
|
|
fontSizeInput.value = config.fontSize || '3rem';
|
|
|
|
|
|
buttonTextInput.value = config.buttonText || 'Volgende Stelling';
|
|
|
|
|
|
finishTextInput.value = config.finishText || 'Einde!';
|
|
|
|
|
|
|
|
|
|
|
|
// Safety check voor colors object
|
|
|
|
|
|
const leftColor = (config.colors && config.colors.left) ? config.colors.left : '#3B82F6';
|
|
|
|
|
|
const rightColor = (config.colors && config.colors.right) ? config.colors.right : '#EF4444';
|
|
|
|
|
|
|
|
|
|
|
|
colorLeftInput.value = leftColor;
|
|
|
|
|
|
colorLeftTextInput.value = leftColor;
|
|
|
|
|
|
colorRightInput.value = rightColor;
|
|
|
|
|
|
colorRightTextInput.value = rightColor;
|
2025-11-03 15:23:11 +00:00
|
|
|
|
|
|
|
|
|
|
// Render stellingen
|
|
|
|
|
|
renderStatements();
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// Render alle stellingen
|
|
|
|
|
|
function renderStatements() {
|
|
|
|
|
|
statementsList.innerHTML = '';
|
|
|
|
|
|
|
2025-12-12 07:34:45 +00:00
|
|
|
|
if (config.stellingen) {
|
|
|
|
|
|
config.stellingen.forEach((stelling, index) => {
|
|
|
|
|
|
addStatementRow(stelling, index);
|
|
|
|
|
|
});
|
|
|
|
|
|
}
|
2025-11-03 15:23:11 +00:00
|
|
|
|
|
|
|
|
|
|
updateCount();
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// Voeg een stelling rij toe aan de UI
|
|
|
|
|
|
function addStatementRow(stelling = { links: '', rechts: '' }, index) {
|
|
|
|
|
|
const row = document.createElement('div');
|
|
|
|
|
|
row.className = 'statement-row';
|
|
|
|
|
|
row.dataset.index = index;
|
2025-12-12 07:34:45 +00:00
|
|
|
|
row.setAttribute('draggable', 'true');
|
2025-11-03 15:23:11 +00:00
|
|
|
|
|
|
|
|
|
|
row.innerHTML = `
|
2025-12-12 07:34:45 +00:00
|
|
|
|
<div class="drag-handle" title="Sleep om te verplaatsen">☰</div>
|
2025-11-03 15:23:11 +00:00
|
|
|
|
<div class="statement-input">
|
|
|
|
|
|
<label>Stelling Links</label>
|
|
|
|
|
|
<input
|
|
|
|
|
|
type="text"
|
|
|
|
|
|
class="statement-left"
|
|
|
|
|
|
value="${stelling.links}"
|
|
|
|
|
|
placeholder="Bijv. Koffie"
|
|
|
|
|
|
data-index="${index}"
|
|
|
|
|
|
>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
<div class="statement-input">
|
|
|
|
|
|
<label>Stelling Rechts</label>
|
|
|
|
|
|
<input
|
|
|
|
|
|
type="text"
|
|
|
|
|
|
class="statement-right"
|
|
|
|
|
|
value="${stelling.rechts}"
|
|
|
|
|
|
placeholder="Bijv. Thee"
|
|
|
|
|
|
data-index="${index}"
|
|
|
|
|
|
>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
<button class="btn btn-remove" data-index="${index}">−</button>
|
|
|
|
|
|
`;
|
|
|
|
|
|
|
|
|
|
|
|
statementsList.appendChild(row);
|
|
|
|
|
|
|
|
|
|
|
|
// Event listeners voor inputs
|
|
|
|
|
|
const leftInput = row.querySelector('.statement-left');
|
|
|
|
|
|
const rightInput = row.querySelector('.statement-right');
|
|
|
|
|
|
const removeBtn = row.querySelector('.btn-remove');
|
|
|
|
|
|
|
|
|
|
|
|
leftInput.addEventListener('input', (e) => {
|
|
|
|
|
|
config.stellingen[index].links = e.target.value;
|
|
|
|
|
|
updateCount();
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
rightInput.addEventListener('input', (e) => {
|
|
|
|
|
|
config.stellingen[index].rechts = e.target.value;
|
|
|
|
|
|
updateCount();
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
removeBtn.addEventListener('click', () => {
|
|
|
|
|
|
if (confirm('Weet je zeker dat je deze stelling wilt verwijderen?')) {
|
|
|
|
|
|
removeStatement(index);
|
|
|
|
|
|
}
|
|
|
|
|
|
});
|
2025-12-12 07:34:45 +00:00
|
|
|
|
|
|
|
|
|
|
// Drag and Drop listeners
|
|
|
|
|
|
addDragListeners(row);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// Drag and Drop functionaliteit
|
|
|
|
|
|
function addDragListeners(row) {
|
|
|
|
|
|
row.addEventListener('dragstart', handleDragStart);
|
|
|
|
|
|
row.addEventListener('dragover', handleDragOver);
|
|
|
|
|
|
row.addEventListener('drop', handleDrop);
|
|
|
|
|
|
row.addEventListener('dragend', handleDragEnd);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
function handleDragStart(e) {
|
|
|
|
|
|
dragSrcEl = this;
|
|
|
|
|
|
e.dataTransfer.effectAllowed = 'move';
|
|
|
|
|
|
e.dataTransfer.setData('text/plain', this.dataset.index);
|
|
|
|
|
|
this.classList.add('dragging');
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
function handleDragOver(e) {
|
|
|
|
|
|
if (e.preventDefault) {
|
|
|
|
|
|
e.preventDefault();
|
|
|
|
|
|
}
|
|
|
|
|
|
e.dataTransfer.dropEffect = 'move';
|
|
|
|
|
|
return false;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
function handleDrop(e) {
|
|
|
|
|
|
if (e.stopPropagation) {
|
|
|
|
|
|
e.stopPropagation();
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
const sourceIndex = parseInt(e.dataTransfer.getData('text/plain'));
|
|
|
|
|
|
const targetIndex = parseInt(this.dataset.index);
|
|
|
|
|
|
|
|
|
|
|
|
if (dragSrcEl !== this && !isNaN(sourceIndex) && !isNaN(targetIndex)) {
|
|
|
|
|
|
// Swap in array
|
|
|
|
|
|
const itemToMove = config.stellingen[sourceIndex];
|
|
|
|
|
|
config.stellingen.splice(sourceIndex, 1);
|
|
|
|
|
|
config.stellingen.splice(targetIndex, 0, itemToMove);
|
|
|
|
|
|
|
|
|
|
|
|
// Re-render
|
|
|
|
|
|
renderStatements();
|
|
|
|
|
|
showStatus('Volgorde aangepast', 'success');
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
return false;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
function handleDragEnd(e) {
|
|
|
|
|
|
this.classList.remove('dragging');
|
|
|
|
|
|
const rows = document.querySelectorAll('.statement-row');
|
|
|
|
|
|
rows.forEach(row => row.classList.remove('dragging'));
|
2025-11-03 15:23:11 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// Verwijder stelling
|
|
|
|
|
|
function removeStatement(index) {
|
|
|
|
|
|
config.stellingen.splice(index, 1);
|
|
|
|
|
|
renderStatements();
|
|
|
|
|
|
showStatus('Stelling verwijderd', 'success');
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// Voeg nieuwe lege stelling toe
|
|
|
|
|
|
function addNewStatement() {
|
|
|
|
|
|
config.stellingen.push({ links: '', rechts: '' });
|
|
|
|
|
|
renderStatements();
|
|
|
|
|
|
|
|
|
|
|
|
// Focus op eerste input van nieuwe rij
|
|
|
|
|
|
const newRow = statementsList.lastElementChild;
|
|
|
|
|
|
const firstInput = newRow.querySelector('.statement-left');
|
|
|
|
|
|
firstInput.focus();
|
2025-12-12 07:34:45 +00:00
|
|
|
|
// Scroll naar beneden
|
|
|
|
|
|
newRow.scrollIntoView({ behavior: 'smooth' });
|
2025-11-03 15:23:11 +00:00
|
|
|
|
|
|
|
|
|
|
showStatus('Nieuwe stelling toegevoegd', 'success');
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// Update stelling count
|
|
|
|
|
|
function updateCount() {
|
2025-12-12 07:34:45 +00:00
|
|
|
|
if (!config || !config.stellingen) return;
|
|
|
|
|
|
|
2025-11-03 15:23:11 +00:00
|
|
|
|
const count = config.stellingen.filter(
|
|
|
|
|
|
s => s.links.trim() || s.rechts.trim()
|
|
|
|
|
|
).length;
|
|
|
|
|
|
statementCount.textContent = `${count} stelling${count !== 1 ? 'en' : ''}`;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// Sync kleur inputs (color picker <-> text)
|
|
|
|
|
|
function syncColorInputs() {
|
|
|
|
|
|
colorLeftInput.addEventListener('input', (e) => {
|
|
|
|
|
|
colorLeftTextInput.value = e.target.value;
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
colorLeftTextInput.addEventListener('input', (e) => {
|
|
|
|
|
|
if (e.target.value.match(/^#[0-9A-Fa-f]{6}$/)) {
|
|
|
|
|
|
colorLeftInput.value = e.target.value;
|
|
|
|
|
|
}
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
colorRightInput.addEventListener('input', (e) => {
|
|
|
|
|
|
colorRightTextInput.value = e.target.value;
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
colorRightTextInput.addEventListener('input', (e) => {
|
|
|
|
|
|
if (e.target.value.match(/^#[0-9A-Fa-f]{6}$/)) {
|
|
|
|
|
|
colorRightInput.value = e.target.value;
|
|
|
|
|
|
}
|
|
|
|
|
|
});
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// Sla config op naar server
|
|
|
|
|
|
async function saveConfig() {
|
|
|
|
|
|
try {
|
|
|
|
|
|
// Update config met form values
|
|
|
|
|
|
config.timer = parseInt(timerInput.value);
|
|
|
|
|
|
config.fontSize = fontSizeInput.value;
|
|
|
|
|
|
config.buttonText = buttonTextInput.value;
|
|
|
|
|
|
config.finishText = finishTextInput.value;
|
|
|
|
|
|
config.colors.left = colorLeftInput.value;
|
|
|
|
|
|
config.colors.right = colorRightInput.value;
|
|
|
|
|
|
|
|
|
|
|
|
// Filter lege stellingen eruit
|
|
|
|
|
|
config.stellingen = config.stellingen.filter(
|
|
|
|
|
|
s => s.links.trim() || s.rechts.trim()
|
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
|
|
// Validatie
|
|
|
|
|
|
if (config.stellingen.length === 0) {
|
|
|
|
|
|
showStatus('⚠️ Je moet minimaal 1 stelling toevoegen', 'error');
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-12-16 06:36:55 +00:00
|
|
|
|
// Als we sets gebruiken, sla via sets op
|
|
|
|
|
|
if (allSets && currentSetId) {
|
|
|
|
|
|
const saved = await saveCurrentSet();
|
|
|
|
|
|
if (saved) {
|
|
|
|
|
|
setTimeout(() => loadSetById(currentSetId), 500);
|
|
|
|
|
|
}
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// Fallback naar oude methode (legacy)
|
2025-11-03 15:23:11 +00:00
|
|
|
|
const response = await fetch('/api/save-config', {
|
|
|
|
|
|
method: 'POST',
|
|
|
|
|
|
headers: {
|
|
|
|
|
|
'Content-Type': 'application/json'
|
|
|
|
|
|
},
|
|
|
|
|
|
body: JSON.stringify(config)
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
const result = await response.json();
|
|
|
|
|
|
|
|
|
|
|
|
if (result.success) {
|
|
|
|
|
|
showStatus('✅ Config succesvol opgeslagen!', 'success');
|
|
|
|
|
|
setTimeout(() => loadConfig(), 500);
|
|
|
|
|
|
} else {
|
|
|
|
|
|
showStatus(`❌ Fout: ${result.error}`, 'error');
|
|
|
|
|
|
}
|
|
|
|
|
|
} catch (error) {
|
|
|
|
|
|
console.error('Fout bij opslaan:', error);
|
|
|
|
|
|
showStatus('❌ Fout bij opslaan config', 'error');
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-12-12 07:34:45 +00:00
|
|
|
|
// --- Stellingkast Functionaliteit ---
|
2025-11-03 15:23:11 +00:00
|
|
|
|
|
2025-12-12 07:34:45 +00:00
|
|
|
|
async function loadStellingkast() {
|
|
|
|
|
|
const listContainer = document.getElementById('stellingkastList');
|
|
|
|
|
|
if (!listContainer) return; // Safety check
|
|
|
|
|
|
|
|
|
|
|
|
listContainer.innerHTML = '<div style="padding:1rem; text-align:center; color:#666;">Stellingen laden...</div>';
|
|
|
|
|
|
|
|
|
|
|
|
try {
|
|
|
|
|
|
console.log('Fetching stellingkast...');
|
|
|
|
|
|
const response = await fetch('/stellingkast.json?t=' + Date.now());
|
|
|
|
|
|
if (response.ok) {
|
|
|
|
|
|
stellingkastItems = await response.json();
|
|
|
|
|
|
console.log('Stellingkast loaded:', stellingkastItems.length, 'items');
|
|
|
|
|
|
renderStellingkastList(stellingkastItems);
|
|
|
|
|
|
} else {
|
|
|
|
|
|
console.error('Kon stellingkast.json niet laden', response.status);
|
|
|
|
|
|
listContainer.innerHTML = '<div style="padding:1rem; text-align:center; color:red;">Kon stellingkast niet laden (' + response.status + ').</div>';
|
|
|
|
|
|
}
|
|
|
|
|
|
} catch (error) {
|
|
|
|
|
|
console.error('Fout bij laden stellingkast:', error);
|
|
|
|
|
|
listContainer.innerHTML = '<div style="padding:1rem; text-align:center; color:red;">Fout: ' + error.message + '</div>';
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
function renderStellingkastList(items) {
|
|
|
|
|
|
if (!stellingkastList) return;
|
|
|
|
|
|
|
|
|
|
|
|
stellingkastList.innerHTML = '';
|
|
|
|
|
|
|
|
|
|
|
|
if (!items || items.length === 0) {
|
|
|
|
|
|
stellingkastList.innerHTML = '<div style="padding:1rem; text-align:center; color:#666;">Geen stellingen gevonden.</div>';
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
items.forEach((item, index) => {
|
|
|
|
|
|
const el = document.createElement('div');
|
|
|
|
|
|
el.className = 'stellingkast-item';
|
|
|
|
|
|
el.innerHTML = `
|
|
|
|
|
|
<div class="stelling-text">
|
|
|
|
|
|
<div>
|
|
|
|
|
|
<span class="stelling-label">Links</span>
|
|
|
|
|
|
${item.links}
|
|
|
|
|
|
</div>
|
|
|
|
|
|
<div style="text-align: right;">
|
|
|
|
|
|
<span class="stelling-label">Rechts</span>
|
|
|
|
|
|
${item.rechts}
|
|
|
|
|
|
</div>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
<button class="btn-import" data-index="${index}">+ Toevoegen</button>
|
|
|
|
|
|
`;
|
|
|
|
|
|
|
|
|
|
|
|
// Add event listener
|
|
|
|
|
|
const btn = el.querySelector('.btn-import');
|
|
|
|
|
|
if (btn) {
|
|
|
|
|
|
btn.addEventListener('click', () => {
|
|
|
|
|
|
importStelling(item);
|
|
|
|
|
|
});
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
stellingkastList.appendChild(el);
|
|
|
|
|
|
});
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
function importStelling(item) {
|
|
|
|
|
|
if (!config) config = { stellingen: [] };
|
|
|
|
|
|
if (!config.stellingen) config.stellingen = [];
|
|
|
|
|
|
|
|
|
|
|
|
// Voeg toe aan config
|
|
|
|
|
|
config.stellingen.push({ ...item }); // Kopie om referentie issues te voorkomen
|
|
|
|
|
|
|
|
|
|
|
|
// Render opnieuw
|
|
|
|
|
|
renderStatements();
|
|
|
|
|
|
|
|
|
|
|
|
// Scroll naar beneden
|
|
|
|
|
|
const newRow = statementsList.lastElementChild;
|
|
|
|
|
|
if (newRow) {
|
|
|
|
|
|
newRow.scrollIntoView({ behavior: 'smooth' });
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// Feedback
|
|
|
|
|
|
showStatus('Stelling geïmporteerd!', 'success');
|
2025-11-03 15:23:11 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// Event listeners
|
2025-12-12 07:34:45 +00:00
|
|
|
|
if (addStatementBtn) addStatementBtn.addEventListener('click', addNewStatement);
|
|
|
|
|
|
if (saveConfigBtn) saveConfigBtn.addEventListener('click', saveConfig);
|
|
|
|
|
|
|
2025-12-16 06:36:55 +00:00
|
|
|
|
// Sets events
|
|
|
|
|
|
if (setSelector) setSelector.addEventListener('change', onSetSelected);
|
|
|
|
|
|
if (saveAsNewSetBtn) saveAsNewSetBtn.addEventListener('click', saveAsNewSet);
|
|
|
|
|
|
|
2025-12-12 07:34:45 +00:00
|
|
|
|
// Stellingkast events
|
|
|
|
|
|
if (openStellingkastBtn) {
|
|
|
|
|
|
openStellingkastBtn.addEventListener('click', () => {
|
|
|
|
|
|
stellingkastPanel.classList.add('open');
|
|
|
|
|
|
});
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if (closeStellingkastBtn) {
|
|
|
|
|
|
closeStellingkastBtn.addEventListener('click', () => {
|
|
|
|
|
|
stellingkastPanel.classList.remove('open');
|
|
|
|
|
|
});
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// Zoek functionaliteit
|
|
|
|
|
|
if (searchStellingkastInput) {
|
|
|
|
|
|
searchStellingkastInput.addEventListener('input', (e) => {
|
|
|
|
|
|
const term = e.target.value.toLowerCase();
|
|
|
|
|
|
if (stellingkastItems) {
|
|
|
|
|
|
const filtered = stellingkastItems.filter(item =>
|
|
|
|
|
|
(item.links && item.links.toLowerCase().includes(term)) ||
|
|
|
|
|
|
(item.rechts && item.rechts.toLowerCase().includes(term))
|
|
|
|
|
|
);
|
|
|
|
|
|
renderStellingkastList(filtered);
|
|
|
|
|
|
}
|
|
|
|
|
|
});
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// Klik buiten panel om te sluiten
|
|
|
|
|
|
document.addEventListener('click', (e) => {
|
|
|
|
|
|
if (stellingkastPanel && stellingkastPanel.classList.contains('open') &&
|
|
|
|
|
|
!stellingkastPanel.contains(e.target) &&
|
|
|
|
|
|
e.target !== openStellingkastBtn) {
|
|
|
|
|
|
stellingkastPanel.classList.remove('open');
|
|
|
|
|
|
}
|
|
|
|
|
|
});
|
2025-11-03 15:23:11 +00:00
|
|
|
|
|
|
|
|
|
|
// Keyboard shortcuts
|
|
|
|
|
|
document.addEventListener('keydown', (e) => {
|
|
|
|
|
|
// Ctrl/Cmd + S = opslaan
|
|
|
|
|
|
if ((e.ctrlKey || e.metaKey) && e.key === 's') {
|
|
|
|
|
|
e.preventDefault();
|
|
|
|
|
|
saveConfig();
|
|
|
|
|
|
}
|
2025-12-12 07:34:45 +00:00
|
|
|
|
// Escape sluit panel
|
|
|
|
|
|
if (e.key === 'Escape' && stellingkastPanel && stellingkastPanel.classList.contains('open')) {
|
|
|
|
|
|
stellingkastPanel.classList.remove('open');
|
|
|
|
|
|
}
|
2025-11-03 15:23:11 +00:00
|
|
|
|
});
|