<!DOCTYPE html>
<html lang="nl">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Stijn Gabeler - Tabblad Manager Portal</title>
<script src="https://cdnjs.cloudflare.com/ajax/libs/Sortable/1.15.0/Sortable.min.js"></script>
<style>
:root {
--bg-color: #f0f4f8;
--card-bg: #ffffff;
--text-color: #2d3748;
--primary-color: #3182ce;
--secondary-color: #2b6cb0;
--accent-color: #319795;
--border-color: #e2e8f0;
}
body {
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
background-color: var(--bg-color);
color: var(--text-color);
margin: 0;
padding: 20px;
line-height: 1.5;
}
header {
text-align: center;
margin-bottom: 20px;
padding: 25px;
background: linear-gradient(135deg, #2b6cb0, #319795);
color: white;
border-radius: 12px;
box-shadow: 0 4px 6px rgba(0,0,0,0.1);
}
header h1 { margin: 0 0 5px 0; font-size: 2.2rem; }
header p { margin: 0; font-size: 1rem; opacity: 0.9; }
/* Navigatiebalk met echte tabbladnamen */
.tab-nav {
display: flex;
flex-wrap: wrap;
gap: 8px;
justify-content: center;
margin-bottom: 20px;
}
.tab-btn {
background: #e2e8f0;
border: none;
padding: 10px 16px;
border-radius: 8px;
font-weight: bold;
cursor: pointer;
color: #4a5568;
transition: all 0.2s;
}
.tab-btn:hover { background: #cbd5e0; }
.tab-btn.active { background: var(--primary-color); color: white; }
.toolbar {
display: flex;
flex-wrap: wrap;
gap: 10px;
justify-content: center;
margin-bottom: 25px;
background: var(--card-bg);
padding: 15px;
border-radius: 10px;
box-shadow: 0 2px 4px rgba(0,0,0,0.05);
}
.toolbar input, .toolbar button {
padding: 8px 14px;
border-radius: 6px;
border: 1px solid var(--border-color);
font-size: 0.9rem;
cursor: pointer;
}
.toolbar input { flex: 1; min-width: 250px; }
.btn { background: var(--primary-color); color: white; border: none; font-weight: bold; }
.btn:hover { background: var(--secondary-color); }
.btn-accent { background: var(--accent-color); color: white; border: none; font-weight: bold; }
.container {
max-width: 900px;
margin: 0 auto;
}
.card {
background: var(--card-bg);
border-radius: 10px;
padding: 25px;
box-shadow: 0 2px 4px rgba(0,0,0,0.04);
border: 1px solid var(--border-color);
}
.card h2 {
font-size: 1.3rem;
margin-top: 0;
border-bottom: 2px solid var(--primary-color);
padding-bottom: 8px;
display: flex;
justify-content: space-between;
align-items: center;
}
.link-list {
list-style: none;
padding: 0;
margin: 0;
max-height: 500px;
overflow-y: auto;
}
.link-list li {
padding: 8px 10px;
border-bottom: 1px dashed var(--border-color);
display: flex;
justify-content: space-between;
align-items: center;
background: #fff;
margin-bottom: 4px;
border-radius: 4px;
cursor: grab;
}
.link-list li:active { cursor: grabbing; }
.link-list li:hover { background: #f7fafc; }
.link-list a {
color: var(--primary-color);
text-decoration: none;
font-weight: 500;
word-break: break-all;
flex: 1;
}
.link-list a:hover { text-decoration: underline; }
.edit-btn {
background: #edf2f7;
border: 1px solid #cbd5e0;
padding: 3px 8px;
border-radius: 4px;
cursor: pointer;
font-size: 0.8rem;
margin-left: 10px;
}
.edit-btn:hover { background: #e2e8f0; }
.loading { text-align: center; font-size: 1.1rem; padding: 40px; color: #718096; }
footer { text-align: center; margin-top: 40px; color: #718096; font-size: 0.9rem; }
</style>
</head>
<body>
<header>
<h1>Stijn Gabeler — Tabblad Manager Portal</h1>
<p>Constantijn Gabeler (Stijn Online) • Selecteer een tabblad, versleep de links of bewerk ze direct</p>
</header>
<!-- Knoppen met echte tabbladnamen -->
<div class="tab-nav" id="tabNav">
<span class="loading">Echte tabbladnamen ophalen...</span>
</div>
<div class="toolbar">
<input type="text" id="searchInput" placeholder="🔍 Zoek in dit tabblad..." onkeyup="filterLinks()">
<button class="btn" onclick="sortAlphabetical()">🔤 Sorteer Alfabetisch</button>
<button class="btn-accent" onclick="exportJSON()">📥 Exporteer JSON</button>
<button class="btn" onclick="exportCSV()">📊 Exporteer CSV</button>
<button class="btn" onclick="exportTXT()">📝 Exporteer TXT</button>
</div>
<!-- Inhoud van het actieve tabblad -->
<div class="container">
<div class="card" id="cardContainer">
<h2>Selecteer een tabblad hierboven</h2>
<div class="loading">Kies een tabblad om de links in te laden.</div>
</div>
</div>
<footer>
<p>© 2026 Stijn Gabeler (Constantijn Gabeler) — Alle rechten voorbehouden.</p>
</footer>
<script>
let currentSheetName = "";
let currentSheetData = [];
window.onload = function() {
google.script.run
.withSuccessHandler(renderTabNav)
.withFailureHandler(err => {
document.getElementById('tabNav').innerHTML = '<span style="color:red;">Fout: ' + err.message + '</span>';
})
.getSheetNames();
};
function renderTabNav(names) {
let nav = document.getElementById('tabNav');
nav.innerHTML = "";
if (names.length === 0) {
nav.innerHTML = "Geen tabbladen gevonden.";
return;
}
names.forEach((name, index) => {
let btn = document.createElement('button');
btn.className = 'tab-btn' + (index === 0 ? ' active' : '');
btn.textContent = name;
btn.onclick = function() {
document.querySelectorAll('.tab-btn').forEach(b => b.classList.remove('active'));
btn.classList.add('active');
loadSheet(name);
};
nav.appendChild(btn);
});
loadSheet(names[0]);
}
function loadSheet(sheetName) {
currentSheetName = sheetName;
let container = document.getElementById('cardContainer');
container.innerHTML = `<h2>Laden van '${sheetName}'...</h2><div class="loading">Unieke links ophalen...</div>`;
google.script.run
.withSuccessHandler(data => {
currentSheetData = data;
renderCard(sheetName, data);
})
.withFailureHandler(err => {
container.innerHTML = `<h2>Fout</h2><p>Kon tabblad niet laden: ${err.message}</p>`;
})
.getSheetDataByName(sheetName);
}
function renderCard(sheetName, rows) {
let container = document.getElementById('cardContainer');
container.innerHTML = "";
let h2 = document.createElement('h2');
h2.innerHTML = `${sheetName} <span style="font-size:0.8rem; color:#718096;">(Versleep items of bewerk ze)</span>`;
container.appendChild(h2);
let ul = document.createElement('ul');
ul.className = 'link-list sortable-list';
if (rows.length === 0) {
let li = document.createElement('li');
li.textContent = "Geen inhoud gevonden in dit tabblad.";
ul.appendChild(li);
} else {
rows.forEach((row, rowIndex) => {
row.forEach((item, itemIndex) => {
let li = document.createElement('li');
let a = document.createElement('a');
a.href = item.url;
a.target = "_blank";
a.textContent = item.text;
a.onclick = function() {
trackClick(item.text, item.url);
};
let editBtn = document.createElement('button');
editBtn.className = 'edit-btn';
editBtn.textContent = '✏️ Bewerk';
editBtn.onclick = function(e) {
e.stopPropagation();
editItem(rowIndex, itemIndex);
};
li.appendChild(a);
li.appendChild(editBtn);
ul.appendChild(li);
});
});
}
container.appendChild(ul);
// Activeer Drag & Drop (verslepen) per tabblad
new Sortable(ul, {
animation: 150,
ghostClass: 'blue-background-class'
});
}
// Bewerk tekst of URL direct in de viewer
function editItem(rowIndex, itemIndex) {
let item = currentSheetData[rowIndex][itemIndex];
let newText = prompt("Bewerk de link-tekst:", item.text);
if (newText === null) return;
let newUrl = prompt("Bewerk de URL:", item.url);
if (newUrl === null) return;
currentSheetData[rowIndex][itemIndex].text = newText;
currentSheetData[rowIndex][itemIndex].url = newUrl;
renderCard(currentSheetName, currentSheetData);
}
function trackClick(linkName, url) {
google.script.run.registerClick(linkName, url);
}
function filterLinks() {
let input = document.getElementById('searchInput').value.toLowerCase();
document.querySelectorAll('.link-list li').forEach(li => {
let text = li.textContent.toLowerCase();
li.style.display = text.includes(input) ? "" : "none";
});
}
function sortAlphabetical() {
let ul = document.querySelector('.sortable-list');
if (!ul) return;
let items = Array.from(ul.querySelectorAll('li'));
items.sort((a, b) => a.querySelector('a').textContent.localeCompare(b.querySelector('a').textContent));
items.forEach(item => ul.appendChild(item));
}
function exportJSON() {
let blob = new Blob([JSON.stringify({ [currentSheetName]: currentSheetData }, null, 2)], {type: 'application/json'});
downloadBlob(blob, `${currentSheetName}_Backup.json`);
}
function exportCSV() {
let csv = "Tabblad,LinkTekst,URL\n";
currentSheetData.forEach(row => {
row.forEach(item => {
csv += `"${currentSheetName}","${item.text}","${item.url}"\n`;
});
});
let blob = new Blob([csv], {type: 'text/csv'});
downloadBlob(blob, `${currentSheetName}_Export.csv`);
}
function exportTXT() {
let txt = `STIJN GABELER - TABBLAD: ${currentSheetName}\n=====================================\n\n`;
currentSheetData.forEach(row => {
row.forEach(item => {
txt += ` - ${item.text} (${item.url})\n`;
});
});
let blob = new Blob([txt], {type: 'text/plain'});
downloadBlob(blob, `${currentSheetName}_Export.txt`);
}
function downloadBlob(blob, filename) {
let url = URL.createObjectURL(blob);
let a = document.createElement('a');
a.href = url;
a.download = filename;
a.click();
}
</script>
</body>
</html>