feat: workshop sets management systeem toegevoegd
Nieuw sets systeem voor het beheren van meerdere workshop configuraties. Nieuwe functionaliteit: - Sets dropdown bovenaan editor voor eenvoudig switchen - "Opslaan als Nieuwe Set" knop voor nieuwe configuraties - Meerdere complete workshop sets opslaan en laden - Elke set bevat eigen stellingen, kleuren, timer en teksten - Automatische migratie van huidige config naar default set Backend wijzigingen: - GET/POST /api/sets endpoints toegevoegd - Helper functies voor sets management en config updates - Automatische initialisatie van sets.json bij eerste gebruik Frontend wijzigingen: - Sets sectie met dropdown en acties knoppen (editor.html) - Styling met blauwe accent border (editor.css) - Complete sets management logic (editor.js) - Event handlers voor set selectie en opslaan Data structuur: - sets.json: database met alle workshop sets - config.json: blijft actieve configuratie voor presentatie Backwards compatible: - Systeem werkt zonder sets.json (legacy fallback) - Presentatie modus ongewijzigd (gebruikt config.json) 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
This commit is contained in:
parent
0e1eb25649
commit
0775ee6161
7 changed files with 609 additions and 60 deletions
83
CLAUDE.md
83
CLAUDE.md
|
|
@ -25,7 +25,7 @@ Een presentatie tool voor workshops waar deelnemers tussen twee stellingen moete
|
|||
### Tech Stack
|
||||
- Frontend: Pure HTML/CSS/JavaScript
|
||||
- Backend: Python Flask + flask-cors
|
||||
- Config driven via JSON
|
||||
- Config driven via JSON (sets.json voor meerdere configuraties)
|
||||
- Geen externe dependencies voor presentatie modus
|
||||
|
||||
## Bestandsstructuur
|
||||
|
|
@ -35,11 +35,13 @@ Een presentatie tool voor workshops waar deelnemers tussen twee stellingen moete
|
|||
├── index.html # Presentatie modus (hoofdspel)
|
||||
├── app.js # Game logic en timer animatie
|
||||
├── style.css # Presentatie styling
|
||||
├── config.json # Stellingen database (JSON)
|
||||
├── editor.html # Web-based config editor
|
||||
├── editor.js # Editor functionaliteit
|
||||
├── config.json # Actieve configuratie (gebruikt door presentatie)
|
||||
├── sets.json # Database met alle workshop sets
|
||||
├── stellingkast.json # Bibliotheek met stellingen voor import
|
||||
├── editor.html # Web-based config editor met sets beheer
|
||||
├── editor.js # Editor functionaliteit en sets management
|
||||
├── editor.css # Editor styling
|
||||
├── server.py # Flask backend voor editor
|
||||
├── server.py # Flask backend voor editor en sets API
|
||||
├── requirements.txt # Python dependencies
|
||||
├── .gitignore # Exclude venv, .DS_Store, etc
|
||||
└── README.md # Uitgebreide documentatie
|
||||
|
|
@ -67,15 +69,17 @@ pip install -r requirements.txt
|
|||
python server.py
|
||||
|
||||
# Open editor
|
||||
open http://localhost:5000/editor.html
|
||||
open http://localhost:8000/editor.html
|
||||
```
|
||||
|
||||
**Editor features:**
|
||||
- Toevoegen/verwijderen stellingen
|
||||
- Timer aanpassen per stelling
|
||||
- Kleuren aanpassen
|
||||
- Preview kleuren
|
||||
- Auto-save naar config.json
|
||||
- **Sets beheer:** Meerdere workshop configuraties opslaan en laden
|
||||
- **Sets dropdown:** Selecteer actieve set of maak nieuwe set aan
|
||||
- Toevoegen/verwijderen stellingen via drag & drop
|
||||
- Stellingkast met bibliotheek voor snelle import
|
||||
- Timer aanpassen per set
|
||||
- Kleuren aanpassen per set
|
||||
- Auto-save naar sets.json en config.json
|
||||
|
||||
## Session History
|
||||
|
||||
|
|
@ -168,4 +172,59 @@ Tijdens gebruik bleek de knop onderaan moeilijk leesbaar door de overlay, en de
|
|||
|
||||
**Status:**
|
||||
- Features volledig werkend en getest in browser
|
||||
- Editor robuuster gemaakt tegen laadfouten
|
||||
- Editor robuuster gemaakt tegen laadfouten
|
||||
|
||||
### 2025-12-16 - Workshop Sets Management
|
||||
**Nieuwe features:**
|
||||
- **Sets systeem:** Meerdere complete workshop configuraties kunnen opslaan en laden
|
||||
- **Sets dropdown:** Bovenaan editor voor eenvoudig switchen tussen sets
|
||||
- **"Opslaan als Nieuwe Set"** knop voor nieuwe configuraties aanmaken
|
||||
- **Automatische migratie:** Huidige config.json wordt bij eerste gebruik omgezet naar default set
|
||||
- **Backwards compatibility:** Systeem blijft werken zonder sets.json (legacy fallback)
|
||||
|
||||
**Gewijzigde bestanden:**
|
||||
- `server.py` - Nieuwe endpoints: GET/POST `/api/sets`, helper functies voor sets management
|
||||
- `editor.html` - Sets sectie met dropdown en knoppen toegevoegd
|
||||
- `editor.css` - Styling voor sets UI (blauwe border-left accent)
|
||||
- `editor.js` - Complete sets management logica: laden, switchen, opslaan, nieuwe sets aanmaken
|
||||
- `config.json` - Blijft actieve configuratie voor presentatie modus
|
||||
|
||||
**Nieuwe bestanden:**
|
||||
- `sets.json` - Database met alle workshop sets (wordt automatisch aangemaakt)
|
||||
|
||||
**Hoe het werkt:**
|
||||
1. Editor openen → Zie dropdown met alle sets
|
||||
2. Set selecteren → Laadt stellingen en instellingen van die set
|
||||
3. Bewerken en opslaan → Overschrijft huidige set
|
||||
4. "Opslaan als Nieuwe Set" → Maakt nieuwe set aan met custom naam
|
||||
5. Presentatie starten → Gebruikt laatst opgeslagen set uit config.json
|
||||
|
||||
**Data structuur sets.json:**
|
||||
```json
|
||||
{
|
||||
"sets": [
|
||||
{
|
||||
"id": "default",
|
||||
"name": "Standaard Set",
|
||||
"config": { /* complete config */ }
|
||||
},
|
||||
{
|
||||
"id": "set-1234567890",
|
||||
"name": "PKM Workshop 2025",
|
||||
"config": { /* complete config */ }
|
||||
}
|
||||
],
|
||||
"activeSetId": "default"
|
||||
}
|
||||
```
|
||||
|
||||
**Waarom:**
|
||||
Facilitators kunnen nu meerdere workshop configuraties voorbereiden (bijv. verschillende doelgroepen, thema's) en eenvoudig tussen sets wisselen zonder telkens stellingen handmatig te moeten aanpassen.
|
||||
|
||||
**Status:**
|
||||
- Backend endpoints volledig werkend (GET/POST /api/sets)
|
||||
- Frontend UI en logic compleet geïmplementeerd
|
||||
- Automatische migratie van bestaande config.json getest
|
||||
- Sets kunnen worden aangemaakt, geladen, gewijzigd en opgeslagen
|
||||
- Backwards compatible met oude systeem
|
||||
- Klaar voor productie gebruik
|
||||
56
config.json
56
config.json
|
|
@ -1,60 +1,36 @@
|
|||
{
|
||||
"buttonText": "Volgende stelling",
|
||||
"buttonText": "Volgende",
|
||||
"colors": {
|
||||
"left": "#3b82f6",
|
||||
"right": "#ef4444"
|
||||
"left": "#10b981",
|
||||
"right": "#f59e0b"
|
||||
},
|
||||
"finishText": "Dat was het! Having fun yet?",
|
||||
"fontSize": "3rem",
|
||||
"finishText": "Bedankt!",
|
||||
"fontSize": "2.5rem",
|
||||
"stellingen": [
|
||||
{
|
||||
"links": "Koffie",
|
||||
"rechts": "Thee"
|
||||
"links": "Inbox Zero held",
|
||||
"rechts": "1000+ ongelezen mails"
|
||||
},
|
||||
{
|
||||
"links": "Structurele planner",
|
||||
"rechts": "Creatieve chaoot"
|
||||
},
|
||||
{
|
||||
"links": "Ik gebruik meer sneltoetsen",
|
||||
"rechts": "Ik ben van team muisgebruik"
|
||||
},
|
||||
{
|
||||
"links": "We hebben duidelijke afspraken over naamgeving van bestanden",
|
||||
"rechts": "Mijn naamgeving van bestanden is veel logischer"
|
||||
},
|
||||
{
|
||||
"links": "Mappenstructuur",
|
||||
"rechts": "Zoekfunctie"
|
||||
},
|
||||
{
|
||||
"links": "Ik maak eigen notities op één plek",
|
||||
"rechts": "Ik maak overal notities en zoek me suf"
|
||||
"links": "Alles digitaal",
|
||||
"rechts": "Mijn papieren notitieboek is heilig"
|
||||
},
|
||||
{
|
||||
"links": "Samenwerken in één document",
|
||||
"rechts": "Concept_versie_3_def_final.docx mailen"
|
||||
},
|
||||
{
|
||||
"links": "Mijn TO-DO lijst is actueel",
|
||||
"rechts": "Mijn TO-DO lijst is fictie"
|
||||
"links": "Camera aan tijdens Teams",
|
||||
"rechts": "Lekker onzichtbaar luisteren"
|
||||
},
|
||||
{
|
||||
"links": "Ik gebruik tags en labels",
|
||||
"rechts": "Ik stop alles in mapjes"
|
||||
"links": "Agenda blokken voor focus",
|
||||
"rechts": "Mijn deur staat altijd open (digitaal)"
|
||||
},
|
||||
{
|
||||
"links": "Browser met 50+ open tabbladen",
|
||||
"rechts": "Opgeruimde browser"
|
||||
},
|
||||
{
|
||||
"links": "Kennis zit in mijn hoofd",
|
||||
"rechts": "Kennis staat in het systeem"
|
||||
},
|
||||
{
|
||||
"links": "Ik kom vandaag vooral halen",
|
||||
"rechts": "Ik kom vandaag ook brengen"
|
||||
"links": "Ik weet wat de AVG van me vraagt",
|
||||
"rechts": "Privacy is voor de juristen"
|
||||
}
|
||||
],
|
||||
"timer": 10
|
||||
"timer": 3
|
||||
}
|
||||
60
editor.css
60
editor.css
|
|
@ -77,6 +77,66 @@ section h2 {
|
|||
font-weight: 600;
|
||||
}
|
||||
|
||||
/* Sets Section Styling */
|
||||
.sets-section {
|
||||
border-left: 4px solid #3b82f6;
|
||||
}
|
||||
|
||||
.sets-controls {
|
||||
display: flex;
|
||||
gap: 2rem;
|
||||
align-items: flex-start;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
|
||||
.set-selector-group {
|
||||
flex: 1;
|
||||
min-width: 300px;
|
||||
}
|
||||
|
||||
.set-selector-group label {
|
||||
display: block;
|
||||
font-weight: 500;
|
||||
margin-bottom: 0.5rem;
|
||||
color: #374151;
|
||||
}
|
||||
|
||||
.set-select {
|
||||
width: 100%;
|
||||
padding: 0.75rem;
|
||||
border: 2px solid #e5e7eb;
|
||||
border-radius: 6px;
|
||||
font-size: 1rem;
|
||||
background: white;
|
||||
cursor: pointer;
|
||||
transition: border-color 0.2s;
|
||||
}
|
||||
|
||||
.set-select:hover {
|
||||
border-color: #3b82f6;
|
||||
}
|
||||
|
||||
.set-select:focus {
|
||||
outline: none;
|
||||
border-color: #3b82f6;
|
||||
box-shadow: 0 0 0 3px rgba(59, 130, 246, 0.1);
|
||||
}
|
||||
|
||||
.set-actions {
|
||||
display: flex;
|
||||
gap: 0.75rem;
|
||||
align-items: flex-end;
|
||||
}
|
||||
|
||||
.btn-danger {
|
||||
background: #ef4444;
|
||||
color: white;
|
||||
}
|
||||
|
||||
.btn-danger:hover {
|
||||
background: #dc2626;
|
||||
}
|
||||
|
||||
/* Settings grid */
|
||||
.settings-grid {
|
||||
display: grid;
|
||||
|
|
|
|||
17
editor.html
17
editor.html
|
|
@ -16,6 +16,23 @@
|
|||
</header>
|
||||
|
||||
<main>
|
||||
<!-- Workshop Sets -->
|
||||
<section class="sets-section">
|
||||
<h2>📂 Workshop Sets</h2>
|
||||
<div class="sets-controls">
|
||||
<div class="set-selector-group">
|
||||
<label for="setSelector">Actieve Set:</label>
|
||||
<select id="setSelector" class="set-select">
|
||||
<option value="">Laden...</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="set-actions">
|
||||
<button id="saveAsNewSet" class="btn btn-secondary">💾 Opslaan als Nieuwe Set</button>
|
||||
<button id="deleteSet" class="btn btn-danger" style="display:none;">🗑️ Verwijder Set</button>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- Basis instellingen -->
|
||||
<section class="settings-section">
|
||||
<h2>⚙️ Instellingen</h2>
|
||||
|
|
|
|||
233
editor.js
233
editor.js
|
|
@ -2,6 +2,8 @@
|
|||
let config = null;
|
||||
let stellingkastItems = [];
|
||||
let dragSrcEl = null;
|
||||
let allSets = null;
|
||||
let currentSetId = null;
|
||||
|
||||
// DOM elementen
|
||||
const timerInput = document.getElementById('timer');
|
||||
|
|
@ -25,26 +27,229 @@ const stellingkastPanel = document.getElementById('stellingkastPanel');
|
|||
const stellingkastList = document.getElementById('stellingkastList');
|
||||
const searchStellingkastInput = document.getElementById('searchStellingkast');
|
||||
|
||||
// Sets elementen
|
||||
const setSelector = document.getElementById('setSelector');
|
||||
const saveAsNewSetBtn = document.getElementById('saveAsNewSet');
|
||||
const deleteSetBtn = document.getElementById('deleteSet');
|
||||
|
||||
// Initialize
|
||||
syncColorInputs();
|
||||
// Start beide laad-acties onafhankelijk van elkaar
|
||||
loadConfig();
|
||||
// Start alle laad-acties onafhankelijk van elkaar
|
||||
loadSets();
|
||||
loadStellingkast();
|
||||
|
||||
// Laad config bij start
|
||||
// --- 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)
|
||||
async function loadConfig() {
|
||||
try {
|
||||
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 + ')');
|
||||
|
||||
|
||||
config = await response.json();
|
||||
console.log('Config loaded:', config);
|
||||
|
||||
|
||||
// Ensure minimal config structure
|
||||
if (!config.stellingen) config.stellingen = [];
|
||||
if (!config.colors) config.colors = { left: '#3B82F6', right: '#EF4444' };
|
||||
|
||||
|
||||
populateForm();
|
||||
showStatus('Config geladen!', 'success');
|
||||
} catch (error) {
|
||||
|
|
@ -276,7 +481,16 @@ async function saveConfig() {
|
|||
return;
|
||||
}
|
||||
|
||||
// POST naar server
|
||||
// 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)
|
||||
const response = await fetch('/api/save-config', {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
|
|
@ -289,7 +503,6 @@ async function saveConfig() {
|
|||
|
||||
if (result.success) {
|
||||
showStatus('✅ Config succesvol opgeslagen!', 'success');
|
||||
// Reload om lege stellingen te verwijderen
|
||||
setTimeout(() => loadConfig(), 500);
|
||||
} else {
|
||||
showStatus(`❌ Fout: ${result.error}`, 'error');
|
||||
|
|
@ -388,6 +601,10 @@ function importStelling(item) {
|
|||
if (addStatementBtn) addStatementBtn.addEventListener('click', addNewStatement);
|
||||
if (saveConfigBtn) saveConfigBtn.addEventListener('click', saveConfig);
|
||||
|
||||
// Sets events
|
||||
if (setSelector) setSelector.addEventListener('change', onSetSelected);
|
||||
if (saveAsNewSetBtn) saveAsNewSetBtn.addEventListener('click', saveAsNewSet);
|
||||
|
||||
// Stellingkast events
|
||||
if (openStellingkastBtn) {
|
||||
openStellingkastBtn.addEventListener('click', () => {
|
||||
|
|
|
|||
71
server.py
71
server.py
|
|
@ -13,6 +13,7 @@ app = Flask(__name__)
|
|||
CORS(app) # Enable CORS voor alle routes
|
||||
|
||||
CONFIG_FILE = 'config.json'
|
||||
SETS_FILE = 'sets.json'
|
||||
|
||||
# Serve static files
|
||||
@app.route('/')
|
||||
|
|
@ -59,6 +60,76 @@ def save_config():
|
|||
except Exception as e:
|
||||
return jsonify({'error': str(e)}), 500
|
||||
|
||||
# API endpoint om alle sets te laden
|
||||
@app.route('/api/sets', methods=['GET'])
|
||||
def get_sets():
|
||||
try:
|
||||
# Als sets.json niet bestaat, maak initieel bestand aan
|
||||
if not os.path.exists(SETS_FILE):
|
||||
initialize_sets_file()
|
||||
|
||||
with open(SETS_FILE, 'r', encoding='utf-8') as f:
|
||||
sets_data = json.load(f)
|
||||
return jsonify(sets_data)
|
||||
except Exception as e:
|
||||
return jsonify({'error': str(e)}), 500
|
||||
|
||||
# API endpoint om sets op te slaan
|
||||
@app.route('/api/sets', methods=['POST'])
|
||||
def save_sets():
|
||||
try:
|
||||
data = request.json
|
||||
|
||||
# Validatie: check of sets array bestaat
|
||||
if 'sets' not in data:
|
||||
return jsonify({'error': 'Geen sets gevonden'}), 400
|
||||
|
||||
# Schrijf naar sets.json met mooie formatting
|
||||
with open(SETS_FILE, 'w', encoding='utf-8') as f:
|
||||
json.dump(data, f, indent=2, ensure_ascii=False)
|
||||
|
||||
# Update config.json met actieve set als die gezet is
|
||||
if 'activeSetId' in data and data['activeSetId']:
|
||||
active_set = next((s for s in data['sets'] if s['id'] == data['activeSetId']), None)
|
||||
if active_set and 'config' in active_set:
|
||||
update_active_config(active_set['config'])
|
||||
|
||||
return jsonify({'success': True, 'message': 'Sets opgeslagen!'})
|
||||
except Exception as e:
|
||||
return jsonify({'error': str(e)}), 500
|
||||
|
||||
# Helper: Update config.json met set configuratie
|
||||
def update_active_config(set_config):
|
||||
with open(CONFIG_FILE, 'w', encoding='utf-8') as f:
|
||||
json.dump(set_config, f, indent=2, ensure_ascii=False)
|
||||
|
||||
# Helper: Initialiseer sets.json met huidige config als default set
|
||||
def initialize_sets_file():
|
||||
try:
|
||||
# Lees huidige config.json
|
||||
with open(CONFIG_FILE, 'r', encoding='utf-8') as f:
|
||||
current_config = json.load(f)
|
||||
|
||||
# Maak initiële sets structuur
|
||||
initial_sets = {
|
||||
'sets': [
|
||||
{
|
||||
'id': 'default',
|
||||
'name': 'Standaard Set',
|
||||
'config': current_config
|
||||
}
|
||||
],
|
||||
'activeSetId': 'default'
|
||||
}
|
||||
|
||||
# Schrijf naar sets.json
|
||||
with open(SETS_FILE, 'w', encoding='utf-8') as f:
|
||||
json.dump(initial_sets, f, indent=2, ensure_ascii=False)
|
||||
|
||||
print("✅ sets.json aangemaakt met default set")
|
||||
except Exception as e:
|
||||
print(f"⚠️ Fout bij initialiseren sets.json: {e}")
|
||||
|
||||
if __name__ == '__main__':
|
||||
print("🎯 IJsbreker Server gestart!")
|
||||
print("📝 Editor: http://localhost:8000/editor.html")
|
||||
|
|
|
|||
149
sets.json
Normal file
149
sets.json
Normal file
|
|
@ -0,0 +1,149 @@
|
|||
{
|
||||
"activeSetId": "set-1765866890733",
|
||||
"sets": [
|
||||
{
|
||||
"config": {
|
||||
"buttonText": "Volgende stelling",
|
||||
"colors": {
|
||||
"left": "#3b82f6",
|
||||
"right": "#ef4444"
|
||||
},
|
||||
"finishText": "Dat was het! Having fun yet?",
|
||||
"fontSize": "3rem",
|
||||
"stellingen": [
|
||||
{
|
||||
"links": "Koffie",
|
||||
"rechts": "Thee"
|
||||
},
|
||||
{
|
||||
"links": "Structurele planner",
|
||||
"rechts": "Creatieve chaoot"
|
||||
},
|
||||
{
|
||||
"links": "Ik gebruik meer sneltoetsen",
|
||||
"rechts": "Ik ben van team muisgebruik"
|
||||
},
|
||||
{
|
||||
"links": "We hebben duidelijke afspraken over naamgeving van bestanden",
|
||||
"rechts": "Mijn naamgeving van bestanden is veel logischer"
|
||||
},
|
||||
{
|
||||
"links": "Mappenstructuur",
|
||||
"rechts": "Zoekfunctie"
|
||||
},
|
||||
{
|
||||
"links": "Ik maak eigen notities op één plek",
|
||||
"rechts": "Ik maak overal notities en zoek me suf"
|
||||
},
|
||||
{
|
||||
"links": "Samenwerken in één document",
|
||||
"rechts": "Concept_versie_3_def_final.docx mailen"
|
||||
},
|
||||
{
|
||||
"links": "Mijn TO-DO lijst is actueel",
|
||||
"rechts": "Mijn TO-DO lijst is fictie"
|
||||
},
|
||||
{
|
||||
"links": "Ik gebruik tags en labels",
|
||||
"rechts": "Ik stop alles in mapjes"
|
||||
},
|
||||
{
|
||||
"links": "Browser met 50+ open tabbladen",
|
||||
"rechts": "Opgeruimde browser"
|
||||
},
|
||||
{
|
||||
"links": "Kennis zit in mijn hoofd",
|
||||
"rechts": "Kennis staat in het systeem"
|
||||
},
|
||||
{
|
||||
"links": "Ik kom vandaag vooral halen",
|
||||
"rechts": "Ik kom vandaag ook brengen"
|
||||
}
|
||||
],
|
||||
"timer": 10
|
||||
},
|
||||
"id": "default",
|
||||
"name": "Standaard Set"
|
||||
},
|
||||
{
|
||||
"config": {
|
||||
"buttonText": "Volgende",
|
||||
"colors": {
|
||||
"left": "#10b981",
|
||||
"right": "#f59e0b"
|
||||
},
|
||||
"finishText": "Bedankt!",
|
||||
"fontSize": "2.5rem",
|
||||
"stellingen": [
|
||||
{
|
||||
"links": "Inbox Zero held",
|
||||
"rechts": "1000+ ongelezen mails"
|
||||
},
|
||||
{
|
||||
"links": "Alles digitaal",
|
||||
"rechts": "Mijn papieren notitieboek is heilig"
|
||||
},
|
||||
{
|
||||
"links": "Samenwerken in één document",
|
||||
"rechts": "Concept_versie_3_def_final.docx mailen"
|
||||
},
|
||||
{
|
||||
"links": "Camera aan tijdens Teams",
|
||||
"rechts": "Lekker onzichtbaar luisteren"
|
||||
},
|
||||
{
|
||||
"links": "Agenda blokken voor focus",
|
||||
"rechts": "Mijn deur staat altijd open (digitaal)"
|
||||
},
|
||||
{
|
||||
"links": "Ik weet wat de AVG van me vraagt",
|
||||
"rechts": "Privacy is voor de juristen"
|
||||
}
|
||||
],
|
||||
"timer": 3
|
||||
},
|
||||
"id": "pkm-workshop",
|
||||
"name": "PKM Workshop 2025"
|
||||
},
|
||||
{
|
||||
"id": "set-1765866890733",
|
||||
"name": "Kerstboom",
|
||||
"config": {
|
||||
"buttonText": "Volgende",
|
||||
"colors": {
|
||||
"left": "#10b981",
|
||||
"right": "#f59e0b"
|
||||
},
|
||||
"finishText": "Bedankt!",
|
||||
"fontSize": "2.5rem",
|
||||
"stellingen": [
|
||||
{
|
||||
"links": "Inbox Zero held",
|
||||
"rechts": "1000+ ongelezen mails"
|
||||
},
|
||||
{
|
||||
"links": "Alles digitaal",
|
||||
"rechts": "Mijn papieren notitieboek is heilig"
|
||||
},
|
||||
{
|
||||
"links": "Samenwerken in één document",
|
||||
"rechts": "Concept_versie_3_def_final.docx mailen"
|
||||
},
|
||||
{
|
||||
"links": "Camera aan tijdens Teams",
|
||||
"rechts": "Lekker onzichtbaar luisteren"
|
||||
},
|
||||
{
|
||||
"links": "Agenda blokken voor focus",
|
||||
"rechts": "Mijn deur staat altijd open (digitaal)"
|
||||
},
|
||||
{
|
||||
"links": "Ik weet wat de AVG van me vraagt",
|
||||
"rechts": "Privacy is voor de juristen"
|
||||
}
|
||||
],
|
||||
"timer": 3
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
Loading…
Reference in a new issue