<!DOCTYPE html>
<html lang="nl">
<head>
<meta charset="UTF-8">
<title>Textcreator Bibliotheek V7.5</title>
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<script src="https://cdnjs.cloudflare.com/ajax/libs/jspdf/2.5.1/jspdf.umd.min.js"></script>
<style>
body{
font-family:Arial;
background:#0f172a;
color:#fff;
margin:0;
padding:20px;
}
.container{
max-width:1100px;
margin:auto;
}
.card{
background:#1e293b;
padding:15px;
margin:10px 0;
border-radius:10px;
}
input, select{
width:100%;
padding:10px;
margin:6px 0;
border-radius:6px;
border:none;
}
button{
padding:8px 10px;
margin:4px;
border:none;
border-radius:6px;
cursor:pointer;
}
pre{
white-space:pre-wrap;
}
.meta{
opacity:0.75;
font-size:12px;
}
.actions{
margin-top:10px;
}
#library{
margin-top:20px;
}
</style>
</head>
<body>
<div class="container">
<h1>📚 Textcreator Bibliotheek V7.5</h1>
<div class="card">
<h2>Zoeken & Sorteren</h2>
<input
id="search"
type="text"
placeholder="Zoek op titel, maker of tekst..."
oninput="render()">
<select id="sort" onchange="render()">
<option value="newest">Nieuwste eerst</option>
<option value="oldest">Oudste eerst</option>
<option value="title">Titel A-Z</option>
<option value="author">Maker A-Z</option>
<option value="edited">Laatst gewijzigd</option>
<option value="views">Meest bekeken</option>
<option value="downloads">Meest gedownload</option>
<option value="favorite">Favorieten eerst</option>
</select>
<button onclick="loadGenerated()">🔄 Refresh</button>
<div class="meta" id="status">
Library wordt geladen...
</div>
</div>
<!-- LIBRARY CONTAINER -->
<div id="library">
<!-- items komen hier -->
</div>
</div> <!-- container sluiten -->
<script>
const API_URL =
"https://script.google.com/macros/s/AKfycbxUFCPwpAHajbA--MYcYWbLRFjRTvdBErrvC4qpgTPPQyeOZWlBWexdnbltDjClceiHsQ/exec";
const SITE_URL =
"https://sites.google.com/view/textcreator/bibliotheek";
/* =========================
STATE
========================= */
let generated = [];
/* =========================
STATUS SAFE UPDATE
========================= */
function setStatus(msg){
const el = document.getElementById("status");
if(el) el.innerText = msg;
console.log("[STATUS]", msg);
}
/* =========================
LOAD FROM SHEET
========================= */
async function loadGenerated(){
try {
setStatus("📡 Laden uit Google Sheet...");
const res = await fetch(API_URL + "?action=generated");
const text = await res.text();
let data;
try {
data = JSON.parse(text);
} catch(e){
console.error("JSON parse error:", text);
throw new Error("Invalid JSON from API");
}
generated = Array.isArray(data) ? data : [];
setStatus("✅ Loaded: " + generated.length + " items");
render(); // komt in deel 3
} catch(e){
console.error("LOAD ERROR", e);
generated = [];
setStatus("❌ Load error (check Apps Script)");
render(); // laat lege state zien
}
}
/* =========================
DATE FORMATTER
========================= */
function formatDate(ts){
if(!ts) return "-";
try {
return new Date(ts).toLocaleString("nl-NL");
} catch(e){
return ts;
}
}
/* =========================
INIT AUTO LOAD
========================= */
loadGenerated();
</script>
/* =========================
SORT ENGINE
========================= */
function sortData(data){
const mode = document.getElementById("sort")?.value || "newest";
const arr = [...data];
switch(mode){
case "newest":
return arr.sort((a,b)=>
new Date(b.timestamp || 0) - new Date(a.timestamp || 0)
);
case "oldest":
return arr.sort((a,b)=>
new Date(a.timestamp || 0) - new Date(b.timestamp || 0)
);
case "title":
return arr.sort((a,b)=>
(a.title || "").localeCompare(b.title || "")
);
case "author":
return arr.sort((a,b)=>
(a.name || "").localeCompare(b.name || "")
);
case "edited":
return arr.sort((a,b)=>
new Date(b.edited || b.timestamp || 0) -
new Date(a.edited || a.timestamp || 0)
);
case "views":
return arr.sort((a,b)=>
(b.views || 0) - (a.views || 0)
);
case "downloads":
return arr.sort((a,b)=>
(b.downloads || 0) - (a.downloads || 0)
);
case "favorite":
return arr.sort((a,b)=>
(b.favorite ? 1 : 0) - (a.favorite ? 1 : 0)
);
default:
return arr;
}
}
/* =========================
FILTER FUNCTION
========================= */
function getFiltered(){
const q = (document.getElementById("search")?.value || "")
.toLowerCase();
return generated.filter(i => {
return (
(i.title || "").toLowerCase().includes(q) ||
(i.name || "").toLowerCase().includes(q) ||
(i.text || "").toLowerCase().includes(q)
);
});
}
/* =========================
MAIN RENDER ENGINE
========================= */
function render(){
const list = document.getElementById("library");
if(!list) return;
let data = getFiltered();
data = sortData(data);
if(!data.length){
list.innerHTML = `
<div class="card">
Geen resultaten gevonden
</div>
`;
return;
}
list.innerHTML = "";
data.forEach((i, index)=>{
const div = document.createElement("div");
div.className = "card";
div.innerHTML = `
<h2>${i.title || "No title"}</h2>
<div class="meta">
👤 ${i.name || "-"} |
📧 ${i.email || "-"} |
📱 ${i.phone || "-"}
</div>
<div class="meta">
🕒 ${formatDate(i.timestamp)}
</div>
<pre>${i.text || ""}</pre>
<div class="actions">
<button onclick="copyItem(${index})">📋 Copy</button>
<button onclick="txtItem(${index})">💾 TXT</button>
<button onclick="pdfItem(${index})">📄 PDF</button>
<button onclick="waItem(${index})">📱 WhatsApp</button>
</div>
`;
list.appendChild(div);
});
}
/* =========================
SAFE GET ITEM
========================= */
function getItem(index){
let data = getFiltered();
data = sortData(data);
return data[index];
}
/* =========================
COPY
========================= */
function copyItem(index){
const i = getItem(index);
if(!i) return;
navigator.clipboard.writeText(
`${i.title}\n\n${i.text}\n\n👤 ${i.name}`
);
}
/* =========================
TXT EXPORT
========================= */
function txtItem(index){
const i = getItem(index);
if(!i) return;
const content =
`${i.title}
${i.text}
---
Maker: ${i.name}
Email: ${i.email}
Phone: ${i.phone}
Site: ${SITE_URL}
`;
const blob = new Blob([content], {type:"text/plain"});
const a = document.createElement("a");
a.href = URL.createObjectURL(blob);
a.download = (i.title || "text") + ".txt";
a.click();
}
/* =========================
PDF EXPORT
========================= */
function pdfItem(index){
const i = getItem(index);
if(!i) return;
const { jsPDF } = window.jspdf;
const doc = new jsPDF();
let content =
`${i.title}
${i.text}
Maker: ${i.name}
Email: ${i.email}
Phone: ${i.phone}
Site: ${SITE_URL}`;
doc.text(i.title || "Text", 10, 10);
let y = 20;
content.split("\n").forEach(line=>{
doc.text(line, 10, y);
y += 6;
});
doc.save((i.title || "text") + ".pdf");
}
/* =========================
WHATSAPP EXPORT
========================= */
function waItem(index){
const i = getItem(index);
if(!i) return;
const content =
`${i.title}
${i.text}
👤 ${i.name}
${SITE_URL}`;
window.open(
"https://wa.me/?text=" + encodeURIComponent(content)
);
}
/* =========================
EMPTY / SAFE FALLBACK UI
========================= */
function safe(val, fallback = "-"){
return (val === undefined || val === null || val === "")
? fallback
: val;
}
/* =========================
ENHANCED CARD STYLES (AUTO FIX)
========================= */
function injectStyles(){
const style = document.createElement("style");
style.innerHTML = `
.card{
box-shadow:0 4px 12px rgba(0,0,0,0.25);
}
.actions button{
background:#334155;
color:white;
}
.actions button:hover{
background:#475569;
}
pre{
margin-top:10px;
background:#0b1220;
padding:10px;
border-radius:6px;
}
`;
document.head.appendChild(style);
}
injectStyles();
/* =========================
REFRESH FIX (SMART RELOAD)
========================= */
function refresh(){
setStatus("🔄 Refreshing...");
loadGenerated();
}
/* override button if exists */
window.loadGenerated = loadGenerated;
/* =========================
EMPTY STATE FIX
========================= */
function renderEmpty(){
const list = document.getElementById("library");
list.innerHTML = `
<div class="card">
<h3>Geen data gevonden</h3>
<p>Controleer je Google Sheet API of Generated tab.</p>
</div>
`;
}
/* =========================
PATCH RENDER SAFETY
========================= */
const originalRender = render;
render = function(){
if(!generated || generated.length === 0){
renderEmpty();
return;
}
originalRender();
};
/* =========================
DEBUG PANEL (OPTIONAL)
========================= */
function debugPanel(){
console.log("=== DEBUG SHEET DATA ===");
console.log("Items:", generated.length);
console.table(generated.slice(0,5));
}
window.debugPanel = debugPanel;
/* =========================
AUTO INIT SAFETY
========================= */
window.addEventListener("load", () => {
setTimeout(() => {
if(!generated || generated.length === 0){
setStatus("⚠️ Geen data geladen - retry...");
loadGenerated();
}
}, 1500);
});
/* =========================
FINAL STATUS OVERRIDE
========================= */
setTimeout(() => {
if(generated && generated.length > 0){
setStatus("✅ Bibliotheek klaar: " + generated.length);
}
}, 2000);
</script>
</body>
</html>