"Stimmen" verwandelt: Fans reichen selbst Testimonials ein
Nutzer-Wunsch 20.08.2026: "eine richtig geile kachel ... wo die leute mit namen und tiktok namen mir eine nachricht schreiben können die ich in die verwaltung kriege, und dann kann ich die ausgewählten über die verwaltungsseite auf die website hinzufügen ... das wird mega persoenlich zu den fans, bitte wirklich krass geil speziell." - Neue, augenschonend gestaltete Einreich-Kachel auf stimmen.html: Name, TikTok-Name (optional), Nachricht -- landet NIE automatisch oeffentlich, sondern immer erst als Entwurf in der Verwaltung. Warmer Babyblau/Rosé-Farbverlauf, wandernder Lichtschein, pulsierendes Herz -- ersetzt die 3 ewigen "Hier steht bald..."-Platzhalterkarten. - Neue Verwaltungs-Sektion "💬 Stimmen verwalten": Warteschlange (offene zuerst), Freigeben/Ablehnen/Löschen pro Eintrag. - Freigegebene Stimmen erscheinen automatisch in einer neuen, spezielleren Zitat-Kartenoptik (großes Anführungszeichen, Name + TikTok-Chip) -- Abschnitt bleibt komplett unsichtbar, solange keine einzige freigegeben wurde. - Backend: neue Tabelle `testimonials` (Migration 0006), routes/ testimonials.js (oeffentliches Einreichen + Lesen freigegebener, admin-Warteschlange + Status/Loeschen mit neuer TESTIMONIALS_MANAGE- Berechtigung). 18 automatisierte Tests gegen eine Fake-DB bestanden. - Alles per Playwright visuell durchgespielt: leerer Zustand, Einreichen -> Erfolgsmeldung, freigegebene Stimmen-Anzeige, Verwaltungs-Warteschlange mit allen Aktionen. Backend-Teil (server-internal/, cloudflare-worker/migrations/) noch ohne Deploy-Zugriff -- Dogi muss ihn manuell auf dogiintern ausrollen. Co-Authored-By: Claude Opus 5 <[email protected]>
This commit is contained in:
@@ -18,10 +18,10 @@
|
||||
<meta name="twitter:image" content="https://dogfather-universe.com/assets/img/og-cover.jpg" />
|
||||
<meta name="robots" content="noindex" />
|
||||
<title>Seite nicht gefunden — DOGFATHER UNIVERSE</title>
|
||||
<link rel="stylesheet" href="assets/css/main.css?v=20260820j" />
|
||||
<link rel="stylesheet" href="assets/css/theme-dogfather.css?v=20260820j" />
|
||||
<link rel="stylesheet" href="assets/css/theme-hasidog.css?v=20260820j" />
|
||||
<link rel="stylesheet" href="assets/css/theme-spicymedia.css?v=20260820j" />
|
||||
<link rel="stylesheet" href="assets/css/main.css?v=20260820k" />
|
||||
<link rel="stylesheet" href="assets/css/theme-dogfather.css?v=20260820k" />
|
||||
<link rel="stylesheet" href="assets/css/theme-hasidog.css?v=20260820k" />
|
||||
<link rel="stylesheet" href="assets/css/theme-spicymedia.css?v=20260820k" />
|
||||
</head>
|
||||
<body>
|
||||
<div id="site-header"></div>
|
||||
@@ -41,7 +41,7 @@
|
||||
</main>
|
||||
|
||||
<div id="site-footer"></div>
|
||||
<script src="assets/js/i18n-404.js?v=20260820j"></script>
|
||||
<script src="assets/js/main.js?v=20260820j"></script>
|
||||
<script src="assets/js/i18n-404.js?v=20260820k"></script>
|
||||
<script src="assets/js/main.js?v=20260820k"></script>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
+7
-7
@@ -19,10 +19,10 @@
|
||||
<meta name="twitter:description" content="Unterstütze Dogfather dauerhaft mit der DogiCrew 🩵 und sammle exklusive Treueprämien — die Teddy-Kollektion entsteht in Kooperation mit VanVan." />
|
||||
<meta name="twitter:image" content="https://dogfather-universe.com/assets/img/og-cover.jpg" />
|
||||
|
||||
<link rel="stylesheet" href="assets/css/main.css?v=20260820j" />
|
||||
<link rel="stylesheet" href="assets/css/theme-dogfather.css?v=20260820j" />
|
||||
<link rel="stylesheet" href="assets/css/theme-hasidog.css?v=20260820j" />
|
||||
<link rel="stylesheet" href="assets/css/theme-spicymedia.css?v=20260820j" />
|
||||
<link rel="stylesheet" href="assets/css/main.css?v=20260820k" />
|
||||
<link rel="stylesheet" href="assets/css/theme-dogfather.css?v=20260820k" />
|
||||
<link rel="stylesheet" href="assets/css/theme-hasidog.css?v=20260820k" />
|
||||
<link rel="stylesheet" href="assets/css/theme-spicymedia.css?v=20260820k" />
|
||||
<style>
|
||||
/* =====================================================================
|
||||
Preis-/Anmelde-Kiste — komplett neu gestaltet (Nutzer-Feedback
|
||||
@@ -454,9 +454,9 @@
|
||||
</main>
|
||||
|
||||
<div id="site-footer"></div>
|
||||
<script src="assets/js/i18n-abonnieren.js?v=20260820j"></script>
|
||||
<script src="assets/js/supporter.js?v=20260820j"></script>
|
||||
<script src="assets/js/main.js?v=20260820j"></script>
|
||||
<script src="assets/js/i18n-abonnieren.js?v=20260820k"></script>
|
||||
<script src="assets/js/supporter.js?v=20260820k"></script>
|
||||
<script src="assets/js/main.js?v=20260820k"></script>
|
||||
<script>
|
||||
(function () {
|
||||
const S = window.DogiSupporter;
|
||||
|
||||
@@ -20,10 +20,10 @@
|
||||
<meta name="twitter:image" content="https://dogfather-universe.com/assets/img/og-cover.jpg" />
|
||||
<meta name="robots" content="noindex" />
|
||||
|
||||
<link rel="stylesheet" href="assets/css/main.css?v=20260820j" />
|
||||
<link rel="stylesheet" href="assets/css/theme-dogfather.css?v=20260820j" />
|
||||
<link rel="stylesheet" href="assets/css/theme-hasidog.css?v=20260820j" />
|
||||
<link rel="stylesheet" href="assets/css/theme-spicymedia.css?v=20260820j" />
|
||||
<link rel="stylesheet" href="assets/css/main.css?v=20260820k" />
|
||||
<link rel="stylesheet" href="assets/css/theme-dogfather.css?v=20260820k" />
|
||||
<link rel="stylesheet" href="assets/css/theme-hasidog.css?v=20260820k" />
|
||||
<link rel="stylesheet" href="assets/css/theme-spicymedia.css?v=20260820k" />
|
||||
</head>
|
||||
<body>
|
||||
<div id="site-header"></div>
|
||||
@@ -134,6 +134,6 @@
|
||||
</main>
|
||||
|
||||
<div id="site-footer"></div>
|
||||
<script src="assets/js/main.js?v=20260820j"></script>
|
||||
<script src="assets/js/main.js?v=20260820k"></script>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
+7
-7
@@ -19,10 +19,10 @@
|
||||
<meta name="twitter:description" content="Detailansicht einer Aktion oder eines Projekts von Team Dogi." />
|
||||
<meta name="twitter:image" content="https://dogfather-universe.com/assets/img/og-cover.jpg" />
|
||||
|
||||
<link rel="stylesheet" href="assets/css/main.css?v=20260820j" />
|
||||
<link rel="stylesheet" href="assets/css/theme-dogfather.css?v=20260820j" />
|
||||
<link rel="stylesheet" href="assets/css/theme-hasidog.css?v=20260820j" />
|
||||
<link rel="stylesheet" href="assets/css/theme-spicymedia.css?v=20260820j" />
|
||||
<link rel="stylesheet" href="assets/css/main.css?v=20260820k" />
|
||||
<link rel="stylesheet" href="assets/css/theme-dogfather.css?v=20260820k" />
|
||||
<link rel="stylesheet" href="assets/css/theme-hasidog.css?v=20260820k" />
|
||||
<link rel="stylesheet" href="assets/css/theme-spicymedia.css?v=20260820k" />
|
||||
</head>
|
||||
<body>
|
||||
<div id="site-header"></div>
|
||||
@@ -51,9 +51,9 @@
|
||||
</main>
|
||||
|
||||
<div id="site-footer"></div>
|
||||
<script src="assets/js/data-aktionen.js?v=20260820j"></script>
|
||||
<script src="assets/js/i18n-aktion-detail.js?v=20260820j"></script>
|
||||
<script src="assets/js/main.js?v=20260820j"></script>
|
||||
<script src="assets/js/data-aktionen.js?v=20260820k"></script>
|
||||
<script src="assets/js/i18n-aktion-detail.js?v=20260820k"></script>
|
||||
<script src="assets/js/main.js?v=20260820k"></script>
|
||||
<script>
|
||||
document.addEventListener("DOMContentLoaded", () => {
|
||||
const params = new URLSearchParams(location.search);
|
||||
|
||||
@@ -3716,3 +3716,75 @@ body.start-seite .music-toggle:focus-visible {
|
||||
.stream-radar-watch-btn[hidden] { display: none; }
|
||||
.stream-radar-watch-btn { margin-top: 1.1rem; }
|
||||
.stream-radar-hinweis { margin-top: 1rem; opacity: .8; }
|
||||
|
||||
/* ===================================================================
|
||||
"Stimmen" — eigene Nachricht einreichen (20.08.2026, Nutzer-Wunsch:
|
||||
"eine richtig geile kachel ... das wird mega persoenlich zu den fans,
|
||||
bitte wirklich krass geil speziell"). Warmer Babyblau/Rosé/Lila-
|
||||
Farbverlauf statt der neutralen Standardkarte, wandernder Lichtschein
|
||||
am Rand (dieselbe Zwei-Ebenen-Technik wie schon an mehreren Stellen
|
||||
dieses Projekts, siehe .supp-card::before) — bewusst augenschonend
|
||||
(gedeckte Farben, sanfte Bewegung, respektiert reduzierte Bewegung). ================================================================= */
|
||||
.stimmen-einreichen-kachel {
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
max-width: 560px;
|
||||
margin: 0 auto;
|
||||
padding: 2rem 2.2rem 2.4rem;
|
||||
border-radius: var(--radius);
|
||||
border: 1.5px solid rgba(143, 217, 234, .35);
|
||||
background:
|
||||
radial-gradient(circle at 15% 0%, rgba(255, 154, 208, .1), transparent 55%),
|
||||
radial-gradient(circle at 85% 100%, rgba(143, 217, 234, .1), transparent 55%),
|
||||
var(--card-bg);
|
||||
box-shadow: 0 24px 60px rgba(0, 0, 0, .35);
|
||||
}
|
||||
.stimmen-einreichen-glanz {
|
||||
content: "";
|
||||
position: absolute;
|
||||
top: 0; left: 8%; right: 8%;
|
||||
height: 1.5px;
|
||||
background: linear-gradient(90deg, transparent, rgba(255, 154, 208, .9), rgba(143, 217, 234, .9), transparent);
|
||||
animation: stimmen-glanz-wandert 5s ease-in-out infinite;
|
||||
}
|
||||
@keyframes stimmen-glanz-wandert { 0%, 100% { opacity: .4; } 50% { opacity: 1; } }
|
||||
@media (prefers-reduced-motion: reduce) { .stimmen-einreichen-glanz { animation: none; opacity: .7; } }
|
||||
.stimmen-einreichen-kopf { text-align: center; margin-bottom: 1.6rem; }
|
||||
.stimmen-einreichen-herz {
|
||||
display: inline-block;
|
||||
font-size: 2.4rem;
|
||||
filter: drop-shadow(0 0 16px rgba(255, 154, 208, .6));
|
||||
animation: stimmen-herz-puls 3s ease-in-out infinite;
|
||||
}
|
||||
@keyframes stimmen-herz-puls { 0%, 100% { transform: scale(1); } 50% { transform: scale(1.1); } }
|
||||
@media (prefers-reduced-motion: reduce) { .stimmen-einreichen-herz { animation: none; } }
|
||||
.stimmen-einreichen-kopf h2 { margin: .5rem 0 .6rem; }
|
||||
.stimmen-einreichen-kopf p { color: var(--text-muted); }
|
||||
#stimmen-formular .field { margin-bottom: 1rem; }
|
||||
.stimmen-erfolg { color: #baf7d6; font-size: .9rem; margin-top: 1rem; display: none; text-align: center; }
|
||||
.stimmen-erfolg.zeige { display: block; }
|
||||
.stimmen-fehler { color: #ff9a9a; font-size: .88rem; margin-top: 1rem; display: none; text-align: center; }
|
||||
.stimmen-fehler.zeige { display: block; }
|
||||
|
||||
/* Freigegebene Stimmen — gehobenere Zitat-Karte statt der schlichten
|
||||
.card, mit farbigem Anführungszeichen + TikTok-Chip. */
|
||||
.stimmen-karte {
|
||||
position: relative;
|
||||
padding: 1.8rem 1.8rem 1.5rem;
|
||||
border-radius: calc(var(--radius) - 4px);
|
||||
border: 1px solid var(--border);
|
||||
background: var(--card-bg);
|
||||
}
|
||||
.stimmen-karte::before {
|
||||
content: "„";
|
||||
position: absolute;
|
||||
top: .2rem; left: 1.1rem;
|
||||
font-size: 3.5rem;
|
||||
font-family: Georgia, serif;
|
||||
color: rgba(143, 217, 234, .35);
|
||||
line-height: 1;
|
||||
}
|
||||
.stimmen-karte-text { position: relative; z-index: 1; margin: 1.2rem 0 1rem; line-height: 1.7; }
|
||||
.stimmen-karte-fuss { display: flex; align-items: center; gap: .5rem; font-size: .88rem; }
|
||||
.stimmen-karte-name { font-weight: 700; color: var(--text); }
|
||||
.stimmen-karte-tiktok { color: var(--accent); font-size: .82rem; }
|
||||
|
||||
+37
-27
@@ -16,37 +16,47 @@ window.I18N_PAGE = {
|
||||
de: "Was Community und Team sagen.", "de-CH": "Was d Community und s Team säge.",
|
||||
en: "What the community and the team say.", fr: "Ce que disent la communauté et l'équipe.", pt: "O que a comunidade e a equipe dizem.",
|
||||
},
|
||||
st_quote_community: {
|
||||
de: `„Hier steht bald eine echte Stimme aus der Community. 🩵"`,
|
||||
"de-CH": `„Da staht bald e echti Stimm us de Community. 🩵"`,
|
||||
en: `"A real voice from the community will be here soon. 🩵"`,
|
||||
fr: `« Un vrai témoignage de la communauté arrivera bientôt ici. 🩵 »`,
|
||||
pt: `„Em breve, uma voz real da comunidade estará aqui. 🩵"`,
|
||||
/* Eigene Stimme einreichen (20.08.2026) */
|
||||
st_form_titel: {
|
||||
de: "Deine Stimme zählt", "de-CH": "Dini Stimm zählt",
|
||||
en: "Your Voice Matters", fr: "Ta voix compte", pt: "Sua voz importa",
|
||||
},
|
||||
st_label_community: {
|
||||
de: "— Community-Mitglied", "de-CH": "— Community-Mitglied",
|
||||
en: "— Community Member", fr: "— Membre de la communauté", pt: "— Membro da comunidade",
|
||||
st_form_text: {
|
||||
de: "Was bedeutet dir Team Dogi? Schreib uns deine Geschichte — mit etwas Glück liest DogFather sie persönlich und stellt sie hier auf die Seite.",
|
||||
"de-CH": "Was bedütet dir Team Dogi? Schriib eus dini Gschicht — mit chli Glück list DogFather sie persönlich und stellt sie da uf d Site.",
|
||||
en: "What does Team Dogi mean to you? Write us your story — with a bit of luck, DogFather will read it personally and feature it here on the site.",
|
||||
fr: "Que représente Team Dogi pour toi ? Écris-nous ton histoire — avec un peu de chance, DogFather la lira personnellement et la publiera ici.",
|
||||
pt: "O que a Team Dogi significa para você? Escreva sua história — com um pouco de sorte, o DogFather vai ler pessoalmente e publicar aqui.",
|
||||
},
|
||||
st_quote_team: {
|
||||
de: `„Hier steht bald eine echte Stimme aus dem Team. 🩵"`,
|
||||
"de-CH": `„Da staht bald e echti Stimm vom Team. 🩵"`,
|
||||
en: `"A real voice from the team will be here soon. 🩵"`,
|
||||
fr: `« Un vrai témoignage de l'équipe arrivera bientôt ici. 🩵 »`,
|
||||
pt: `„Em breve, uma voz real da equipe estará aqui. 🩵"`,
|
||||
st_form_name_label: {
|
||||
de: "Dein Name", "de-CH": "Dis Name",
|
||||
en: "Your Name", fr: "Ton nom", pt: "Seu nome",
|
||||
},
|
||||
st_label_team: {
|
||||
de: "— Team-Mitglied", "de-CH": "— Team-Mitglied",
|
||||
en: "— Team Member", fr: "— Membre de l'équipe", pt: "— Membro da equipe",
|
||||
st_form_tiktok_label: {
|
||||
de: "TikTok-Name (optional)", "de-CH": "TikTok-Name (optional)",
|
||||
en: "TikTok Username (optional)", fr: "Nom TikTok (facultatif)", pt: "Nome no TikTok (opcional)",
|
||||
},
|
||||
st_quote_scout: {
|
||||
de: `„Hier steht bald eine echte Stimme von Scouts & Creatorn. 🩵"`,
|
||||
"de-CH": `„Da staht bald e echti Stimm vo Scouts & Creator. 🩵"`,
|
||||
en: `"A real voice from scouts & creators will be here soon. 🩵"`,
|
||||
fr: `« Un vrai témoignage de scouts et créateurs arrivera bientôt ici. 🩵 »`,
|
||||
pt: `„Em breve, uma voz real de scouts e criadores estará aqui. 🩵"`,
|
||||
st_form_nachricht_label: {
|
||||
de: "Deine Nachricht", "de-CH": "Dini Nachricht",
|
||||
en: "Your Message", fr: "Ton message", pt: "Sua mensagem",
|
||||
},
|
||||
st_label_scout: {
|
||||
de: "— Scout/Creator", "de-CH": "— Scout/Creator",
|
||||
en: "— Scout/Creator", fr: "— Scout/Créateur", pt: "— Scout/Criador",
|
||||
st_form_nachricht_placeholder: {
|
||||
de: "Erzähl uns, was dir Team Dogi bedeutet …", "de-CH": "Verzell eus, was dir Team Dogi bedütet …",
|
||||
en: "Tell us what Team Dogi means to you …", fr: "Dis-nous ce que Team Dogi représente pour toi …", pt: "Conte-nos o que a Team Dogi significa para você …",
|
||||
},
|
||||
st_form_absenden: {
|
||||
de: "💙 Nachricht einreichen", "de-CH": "💙 Nachricht ischicke",
|
||||
en: "💙 Submit Message", fr: "💙 Envoyer le message", pt: "💙 Enviar mensagem",
|
||||
},
|
||||
st_form_erfolg: {
|
||||
de: "Danke dir! Deine Nachricht wurde eingereicht und wird von Dogi persönlich geprüft. 🩵",
|
||||
"de-CH": "Danke dir! Dini Nachricht isch ischickt worde und wird vo Dogi persönlich gprüeft. 🩵",
|
||||
en: "Thank you! Your message has been submitted and will be reviewed by Dogi personally. 🩵",
|
||||
fr: "Merci ! Ton message a été envoyé et sera examiné personnellement par Dogi. 🩵",
|
||||
pt: "Obrigado! Sua mensagem foi enviada e será revisada pessoalmente pelo Dogi. 🩵",
|
||||
},
|
||||
st_echte_titel: {
|
||||
de: "Das sagt die Community", "de-CH": "Das seit d Community",
|
||||
en: "What the Community Says", fr: "Ce que dit la communauté", pt: "O que a comunidade diz",
|
||||
},
|
||||
};
|
||||
|
||||
@@ -0,0 +1,80 @@
|
||||
/* =====================================================================
|
||||
stimmen.js — NUR für stimmen.html (20.08.2026, Nutzer-Wunsch: "eine
|
||||
richtig geile kachel ... wo die leute mit namen und tiktok namen mir
|
||||
eine nachricht schreiben können die ich in die verwaltung kriege").
|
||||
Formular schickt eine Einreichung ab (landet als Entwurf, siehe
|
||||
server-internal/routes/testimonials.js), zweiter Teil lädt die bereits
|
||||
freigegebenen Stimmen und zeigt sie an — bleibt komplett unsichtbar,
|
||||
solange keine einzige freigegeben wurde. ===================================================================== */
|
||||
(function () {
|
||||
const API_BASE_URL = "https://postfach.dogfather-universe.com";
|
||||
|
||||
const formular = document.getElementById("stimmen-formular");
|
||||
if (formular) {
|
||||
const erfolgEl = document.getElementById("stimmen-erfolg");
|
||||
const fehlerEl = document.getElementById("stimmen-fehler");
|
||||
formular.addEventListener("submit", async (e) => {
|
||||
e.preventDefault();
|
||||
erfolgEl.classList.remove("zeige");
|
||||
fehlerEl.classList.remove("zeige");
|
||||
|
||||
const name = document.getElementById("st-name").value.trim();
|
||||
const tiktokUsername = document.getElementById("st-tiktok").value.trim();
|
||||
const nachricht = document.getElementById("st-nachricht").value.trim();
|
||||
|
||||
const absendenBtn = formular.querySelector("button[type=submit]");
|
||||
absendenBtn.disabled = true;
|
||||
try {
|
||||
const res = await fetch(`${API_BASE_URL}/testimonials/submit`, {
|
||||
method: "POST",
|
||||
headers: { "Content-Type": "application/json" },
|
||||
body: JSON.stringify({ name, tiktokUsername, nachricht }),
|
||||
});
|
||||
const data = await res.json();
|
||||
if (!data.ok) {
|
||||
fehlerEl.textContent = data.error || "Einreichen hat leider nicht geklappt. Bitte versuch es nochmal.";
|
||||
fehlerEl.classList.add("zeige");
|
||||
return;
|
||||
}
|
||||
formular.hidden = true;
|
||||
erfolgEl.classList.add("zeige");
|
||||
} catch {
|
||||
fehlerEl.textContent = "Verbindung zum Server fehlgeschlagen. Bitte versuch es nochmal.";
|
||||
fehlerEl.classList.add("zeige");
|
||||
} finally {
|
||||
absendenBtn.disabled = false;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function escapeHtml(text) {
|
||||
return String(text ?? "").replace(/[&<>"']/g, (z) => ({ "&": "&", "<": "<", ">": ">", '"': """, "'": "'" }[z]));
|
||||
}
|
||||
|
||||
function ladeEchteStimmen() {
|
||||
fetch(`${API_BASE_URL}/testimonials`, { cache: "no-store" })
|
||||
.then((res) => res.json())
|
||||
.then((data) => {
|
||||
const liste = data && data.ok ? data.testimonials : [];
|
||||
if (!liste.length) return; // Abschnitt bleibt hidden, kein Platzhalter mehr
|
||||
const root = document.getElementById("stimmen-echte-root");
|
||||
const grid = document.getElementById("stimmen-echte-grid");
|
||||
grid.innerHTML = liste
|
||||
.map(
|
||||
(t) => `<div class="stimmen-karte">
|
||||
<p class="stimmen-karte-text">${escapeHtml(t.nachricht)}</p>
|
||||
<div class="stimmen-karte-fuss">
|
||||
<span class="stimmen-karte-name">${escapeHtml(t.name)}</span>
|
||||
${t.tiktokUsername ? `<span class="stimmen-karte-tiktok">@${escapeHtml(t.tiktokUsername)}</span>` : ""}
|
||||
</div>
|
||||
</div>`
|
||||
)
|
||||
.join("");
|
||||
root.hidden = false;
|
||||
})
|
||||
.catch(() => {
|
||||
/* Abschnitt bleibt einfach unsichtbar -- kein Fehler nötig. */
|
||||
});
|
||||
}
|
||||
ladeEchteStimmen();
|
||||
})();
|
||||
@@ -19,10 +19,10 @@
|
||||
<meta name="twitter:description" content="Du bist Streamer und suchst einen Manager? Bewirb dich als Creator bei Spicy Media." />
|
||||
<meta name="twitter:image" content="https://dogfather-universe.com/assets/img/og-cover.jpg" />
|
||||
|
||||
<link rel="stylesheet" href="assets/css/main.css?v=20260820j" />
|
||||
<link rel="stylesheet" href="assets/css/theme-dogfather.css?v=20260820j" />
|
||||
<link rel="stylesheet" href="assets/css/theme-hasidog.css?v=20260820j" />
|
||||
<link rel="stylesheet" href="assets/css/theme-spicymedia.css?v=20260820j" />
|
||||
<link rel="stylesheet" href="assets/css/main.css?v=20260820k" />
|
||||
<link rel="stylesheet" href="assets/css/theme-dogfather.css?v=20260820k" />
|
||||
<link rel="stylesheet" href="assets/css/theme-hasidog.css?v=20260820k" />
|
||||
<link rel="stylesheet" href="assets/css/theme-spicymedia.css?v=20260820k" />
|
||||
</head>
|
||||
<body class="mit-hintergrund bewerben-seite" style="--page-bg:url('/assets/img/bg-bewerben-creator.jpg');--page-bg-mobile:url('/assets/img/bg-bewerben-creator-mobile.jpg');">
|
||||
<div id="site-header"></div>
|
||||
@@ -70,8 +70,8 @@
|
||||
</main>
|
||||
|
||||
<div id="site-footer"></div>
|
||||
<script src="assets/js/forms.js?v=20260820j"></script>
|
||||
<script src="assets/js/i18n-bewerben-creator.js?v=20260820j"></script>
|
||||
<script src="assets/js/main.js?v=20260820j"></script>
|
||||
<script src="assets/js/forms.js?v=20260820k"></script>
|
||||
<script src="assets/js/i18n-bewerben-creator.js?v=20260820k"></script>
|
||||
<script src="assets/js/main.js?v=20260820k"></script>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
@@ -22,10 +22,10 @@
|
||||
<meta name="twitter:description" content="Kooperationsanfrage an Manager DogFather / Spicy Media." />
|
||||
<meta name="twitter:image" content="https://dogfather-universe.com/assets/img/og-cover.jpg" />
|
||||
|
||||
<link rel="stylesheet" href="assets/css/main.css?v=20260820j" />
|
||||
<link rel="stylesheet" href="assets/css/theme-dogfather.css?v=20260820j" />
|
||||
<link rel="stylesheet" href="assets/css/theme-hasidog.css?v=20260820j" />
|
||||
<link rel="stylesheet" href="assets/css/theme-spicymedia.css?v=20260820j" />
|
||||
<link rel="stylesheet" href="assets/css/main.css?v=20260820k" />
|
||||
<link rel="stylesheet" href="assets/css/theme-dogfather.css?v=20260820k" />
|
||||
<link rel="stylesheet" href="assets/css/theme-hasidog.css?v=20260820k" />
|
||||
<link rel="stylesheet" href="assets/css/theme-spicymedia.css?v=20260820k" />
|
||||
</head>
|
||||
<body class="mit-hintergrund bewerben-seite" style="--page-bg:url('/assets/img/bg-bewerben.jpg');--page-bg-mobile:url('/assets/img/bg-bewerben-mobile.jpg');">
|
||||
<div id="site-header"></div>
|
||||
@@ -68,8 +68,8 @@
|
||||
</main>
|
||||
|
||||
<div id="site-footer"></div>
|
||||
<script src="assets/js/forms.js?v=20260820j"></script>
|
||||
<script src="assets/js/i18n-bewerben-kooperation.js?v=20260820j"></script>
|
||||
<script src="assets/js/main.js?v=20260820j"></script>
|
||||
<script src="assets/js/forms.js?v=20260820k"></script>
|
||||
<script src="assets/js/i18n-bewerben-kooperation.js?v=20260820k"></script>
|
||||
<script src="assets/js/main.js?v=20260820k"></script>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
+7
-7
@@ -19,10 +19,10 @@
|
||||
<meta name="twitter:description" content="Werde Modi bei Team Dogi — DogFathers persönliches Community-Team." />
|
||||
<meta name="twitter:image" content="https://dogfather-universe.com/assets/img/og-cover.jpg" />
|
||||
|
||||
<link rel="stylesheet" href="assets/css/main.css?v=20260820j" />
|
||||
<link rel="stylesheet" href="assets/css/theme-dogfather.css?v=20260820j" />
|
||||
<link rel="stylesheet" href="assets/css/theme-hasidog.css?v=20260820j" />
|
||||
<link rel="stylesheet" href="assets/css/theme-spicymedia.css?v=20260820j" />
|
||||
<link rel="stylesheet" href="assets/css/main.css?v=20260820k" />
|
||||
<link rel="stylesheet" href="assets/css/theme-dogfather.css?v=20260820k" />
|
||||
<link rel="stylesheet" href="assets/css/theme-hasidog.css?v=20260820k" />
|
||||
<link rel="stylesheet" href="assets/css/theme-spicymedia.css?v=20260820k" />
|
||||
</head>
|
||||
<body class="mit-hintergrund bewerben-seite" style="--page-bg:url('/assets/img/bg-dogfather.jpg');--page-bg-mobile:url('/assets/img/bg-dogfather-mobile.jpg');">
|
||||
<div id="site-header"></div>
|
||||
@@ -79,8 +79,8 @@
|
||||
</main>
|
||||
|
||||
<div id="site-footer"></div>
|
||||
<script src="assets/js/forms.js?v=20260820j"></script>
|
||||
<script src="assets/js/i18n-bewerben-modi.js?v=20260820j"></script>
|
||||
<script src="assets/js/main.js?v=20260820j"></script>
|
||||
<script src="assets/js/forms.js?v=20260820k"></script>
|
||||
<script src="assets/js/i18n-bewerben-modi.js?v=20260820k"></script>
|
||||
<script src="assets/js/main.js?v=20260820k"></script>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
+7
-7
@@ -19,10 +19,10 @@
|
||||
<meta name="twitter:description" content="Werde Scout für DogFather bei der Spicy Media Agentur — finde neue Talente." />
|
||||
<meta name="twitter:image" content="https://dogfather-universe.com/assets/img/og-cover.jpg" />
|
||||
|
||||
<link rel="stylesheet" href="assets/css/main.css?v=20260820j" />
|
||||
<link rel="stylesheet" href="assets/css/theme-dogfather.css?v=20260820j" />
|
||||
<link rel="stylesheet" href="assets/css/theme-hasidog.css?v=20260820j" />
|
||||
<link rel="stylesheet" href="assets/css/theme-spicymedia.css?v=20260820j" />
|
||||
<link rel="stylesheet" href="assets/css/main.css?v=20260820k" />
|
||||
<link rel="stylesheet" href="assets/css/theme-dogfather.css?v=20260820k" />
|
||||
<link rel="stylesheet" href="assets/css/theme-hasidog.css?v=20260820k" />
|
||||
<link rel="stylesheet" href="assets/css/theme-spicymedia.css?v=20260820k" />
|
||||
</head>
|
||||
<body class="mit-hintergrund bewerben-seite" style="--page-bg:url('/assets/img/bg-scouts.jpg');--page-bg-mobile:url('/assets/img/bg-scouts-mobile.jpg');">
|
||||
<div id="site-header"></div>
|
||||
@@ -71,8 +71,8 @@
|
||||
</main>
|
||||
|
||||
<div id="site-footer"></div>
|
||||
<script src="assets/js/forms.js?v=20260820j"></script>
|
||||
<script src="assets/js/i18n-bewerben-scout.js?v=20260820j"></script>
|
||||
<script src="assets/js/main.js?v=20260820j"></script>
|
||||
<script src="assets/js/forms.js?v=20260820k"></script>
|
||||
<script src="assets/js/i18n-bewerben-scout.js?v=20260820k"></script>
|
||||
<script src="assets/js/main.js?v=20260820k"></script>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
+6
-6
@@ -19,10 +19,10 @@
|
||||
<meta name="twitter:description" content="Werde Teil vom Team: Modi bei Team Dogi, Scout oder Creator bei Spicy Media, oder Kooperation anfragen." />
|
||||
<meta name="twitter:image" content="https://dogfather-universe.com/assets/img/og-cover.jpg" />
|
||||
|
||||
<link rel="stylesheet" href="assets/css/main.css?v=20260820j" />
|
||||
<link rel="stylesheet" href="assets/css/theme-dogfather.css?v=20260820j" />
|
||||
<link rel="stylesheet" href="assets/css/theme-hasidog.css?v=20260820j" />
|
||||
<link rel="stylesheet" href="assets/css/theme-spicymedia.css?v=20260820j" />
|
||||
<link rel="stylesheet" href="assets/css/main.css?v=20260820k" />
|
||||
<link rel="stylesheet" href="assets/css/theme-dogfather.css?v=20260820k" />
|
||||
<link rel="stylesheet" href="assets/css/theme-hasidog.css?v=20260820k" />
|
||||
<link rel="stylesheet" href="assets/css/theme-spicymedia.css?v=20260820k" />
|
||||
</head>
|
||||
<body class="mit-hintergrund bewerben-seite" style="--page-bg:url('/assets/img/bg-bewerben-split.jpg');--page-bg-mobile:url('/assets/img/bg-bewerben-split-mobile.jpg');">
|
||||
<div id="site-header"></div>
|
||||
@@ -85,7 +85,7 @@
|
||||
</main>
|
||||
|
||||
<div id="site-footer"></div>
|
||||
<script src="assets/js/i18n-bewerben.js?v=20260820j"></script>
|
||||
<script src="assets/js/main.js?v=20260820j"></script>
|
||||
<script src="assets/js/i18n-bewerben.js?v=20260820k"></script>
|
||||
<script src="assets/js/main.js?v=20260820k"></script>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
+6
-6
@@ -19,10 +19,10 @@
|
||||
<meta name="twitter:description" content="Der Husky an DogFathers Seite — das tierische Gesicht der HasiDog-Welt." />
|
||||
<meta name="twitter:image" content="https://dogfather-universe.com/assets/img/og-cover.jpg" />
|
||||
|
||||
<link rel="stylesheet" href="assets/css/main.css?v=20260820j" />
|
||||
<link rel="stylesheet" href="assets/css/theme-dogfather.css?v=20260820j" />
|
||||
<link rel="stylesheet" href="assets/css/theme-hasidog.css?v=20260820j" />
|
||||
<link rel="stylesheet" href="assets/css/theme-spicymedia.css?v=20260820j" />
|
||||
<link rel="stylesheet" href="assets/css/main.css?v=20260820k" />
|
||||
<link rel="stylesheet" href="assets/css/theme-dogfather.css?v=20260820k" />
|
||||
<link rel="stylesheet" href="assets/css/theme-hasidog.css?v=20260820k" />
|
||||
<link rel="stylesheet" href="assets/css/theme-spicymedia.css?v=20260820k" />
|
||||
</head>
|
||||
<body class="mit-hintergrund" style="--page-bg:url('/assets/img/bg-casper.jpg');--page-bg-mobile:url('/assets/img/bg-casper-mobile.jpg');">
|
||||
<div id="site-header"></div>
|
||||
@@ -85,7 +85,7 @@
|
||||
</main>
|
||||
|
||||
<div id="site-footer"></div>
|
||||
<script src="assets/js/i18n-casper.js?v=20260820j"></script>
|
||||
<script src="assets/js/main.js?v=20260820j"></script>
|
||||
<script src="assets/js/i18n-casper.js?v=20260820k"></script>
|
||||
<script src="assets/js/main.js?v=20260820k"></script>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
@@ -0,0 +1,23 @@
|
||||
-- =====================================================================
|
||||
-- "Stimmen" / Testimonials (20.08.2026) — Fans können auf stimmen.html
|
||||
-- selbst eine Nachricht mit Name + TikTok-Namen einreichen. Landet erst
|
||||
-- als Entwurf in der Verwaltung ("eingereicht") -- öffentlich sichtbar
|
||||
-- wird ein Eintrag NUR, wenn Dogi/VanVan ihn dort ausdrücklich freigeben
|
||||
-- (Nutzer-Wunsch: "die ausgewählten über die verwaltungsseite auf die
|
||||
-- website hinzufügen"). Eigene Tabelle statt app_settings, weil dies eine
|
||||
-- WACHSENDE Liste ist (beliebig viele Einreichungen), keine feste Anzahl
|
||||
-- Slots wie "Event des Jahres".
|
||||
-- =====================================================================
|
||||
|
||||
CREATE TABLE IF NOT EXISTS testimonials (
|
||||
id TEXT PRIMARY KEY,
|
||||
name TEXT NOT NULL,
|
||||
tiktok_username TEXT,
|
||||
nachricht TEXT NOT NULL,
|
||||
status TEXT NOT NULL DEFAULT 'eingereicht', -- eingereicht | freigegeben | abgelehnt
|
||||
created_at TEXT NOT NULL,
|
||||
entschieden_am TEXT,
|
||||
entschieden_von TEXT
|
||||
);
|
||||
|
||||
CREATE INDEX IF NOT EXISTS idx_testimonials_status ON testimonials (status, created_at);
|
||||
+6
-6
@@ -19,10 +19,10 @@
|
||||
<meta name="twitter:description" content="Team Dogi ist Familie — die Community rund um DogFather und HasiDog." />
|
||||
<meta name="twitter:image" content="https://dogfather-universe.com/assets/img/og-cover.jpg" />
|
||||
|
||||
<link rel="stylesheet" href="assets/css/main.css?v=20260820j" />
|
||||
<link rel="stylesheet" href="assets/css/theme-dogfather.css?v=20260820j" />
|
||||
<link rel="stylesheet" href="assets/css/theme-hasidog.css?v=20260820j" />
|
||||
<link rel="stylesheet" href="assets/css/theme-spicymedia.css?v=20260820j" />
|
||||
<link rel="stylesheet" href="assets/css/main.css?v=20260820k" />
|
||||
<link rel="stylesheet" href="assets/css/theme-dogfather.css?v=20260820k" />
|
||||
<link rel="stylesheet" href="assets/css/theme-hasidog.css?v=20260820k" />
|
||||
<link rel="stylesheet" href="assets/css/theme-spicymedia.css?v=20260820k" />
|
||||
</head>
|
||||
<body class="mit-hintergrund" style="--page-bg:url('/assets/img/bg-community.jpg');--page-bg-mobile:url('/assets/img/bg-community-mobile.jpg');">
|
||||
<div id="site-header"></div>
|
||||
@@ -84,7 +84,7 @@
|
||||
</main>
|
||||
|
||||
<div id="site-footer"></div>
|
||||
<script src="assets/js/i18n-community.js?v=20260820j"></script>
|
||||
<script src="assets/js/main.js?v=20260820j"></script>
|
||||
<script src="assets/js/i18n-community.js?v=20260820k"></script>
|
||||
<script src="assets/js/main.js?v=20260820k"></script>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
+7
-7
@@ -19,10 +19,10 @@
|
||||
<meta name="twitter:description" content="Content Creator, die Teil von DogFather / Spicy Media sind." />
|
||||
<meta name="twitter:image" content="https://dogfather-universe.com/assets/img/og-cover.jpg" />
|
||||
|
||||
<link rel="stylesheet" href="assets/css/main.css?v=20260820j" />
|
||||
<link rel="stylesheet" href="assets/css/theme-dogfather.css?v=20260820j" />
|
||||
<link rel="stylesheet" href="assets/css/theme-hasidog.css?v=20260820j" />
|
||||
<link rel="stylesheet" href="assets/css/theme-spicymedia.css?v=20260820j" />
|
||||
<link rel="stylesheet" href="assets/css/main.css?v=20260820k" />
|
||||
<link rel="stylesheet" href="assets/css/theme-dogfather.css?v=20260820k" />
|
||||
<link rel="stylesheet" href="assets/css/theme-hasidog.css?v=20260820k" />
|
||||
<link rel="stylesheet" href="assets/css/theme-spicymedia.css?v=20260820k" />
|
||||
</head>
|
||||
<body class="mit-hintergrund" style="--page-bg:url('/assets/img/bg-creator.jpg');--page-bg-mobile:url('/assets/img/bg-creator-mobile.jpg');">
|
||||
<div id="site-header"></div>
|
||||
@@ -59,9 +59,9 @@
|
||||
</main>
|
||||
|
||||
<div id="site-footer"></div>
|
||||
<script src="assets/js/data-creator.js?v=20260820j"></script>
|
||||
<script src="assets/js/i18n-creator.js?v=20260820j"></script>
|
||||
<script src="assets/js/main.js?v=20260820j"></script>
|
||||
<script src="assets/js/data-creator.js?v=20260820k"></script>
|
||||
<script src="assets/js/i18n-creator.js?v=20260820k"></script>
|
||||
<script src="assets/js/main.js?v=20260820k"></script>
|
||||
<script>
|
||||
document.addEventListener("DOMContentLoaded", () => {
|
||||
const grid = document.getElementById("creator-grid");
|
||||
|
||||
+6
-6
@@ -19,10 +19,10 @@
|
||||
<meta name="twitter:description" content="Wer ist DogFather wirklich? Persönlichkeit, Werte und die Geschichte hinter dem Content." />
|
||||
<meta name="twitter:image" content="https://dogfather-universe.com/assets/img/og-cover.jpg" />
|
||||
|
||||
<link rel="stylesheet" href="assets/css/main.css?v=20260820j" />
|
||||
<link rel="stylesheet" href="assets/css/theme-dogfather.css?v=20260820j" />
|
||||
<link rel="stylesheet" href="assets/css/theme-hasidog.css?v=20260820j" />
|
||||
<link rel="stylesheet" href="assets/css/theme-spicymedia.css?v=20260820j" />
|
||||
<link rel="stylesheet" href="assets/css/main.css?v=20260820k" />
|
||||
<link rel="stylesheet" href="assets/css/theme-dogfather.css?v=20260820k" />
|
||||
<link rel="stylesheet" href="assets/css/theme-hasidog.css?v=20260820k" />
|
||||
<link rel="stylesheet" href="assets/css/theme-spicymedia.css?v=20260820k" />
|
||||
</head>
|
||||
<body class="mit-hintergrund" style="--page-bg:url('/assets/img/bg-dogfather-person.jpg');--page-bg-mobile:url('/assets/img/bg-dogfather-person-mobile.jpg');">
|
||||
<div id="site-header"></div>
|
||||
@@ -158,7 +158,7 @@
|
||||
</main>
|
||||
|
||||
<div id="site-footer"></div>
|
||||
<script src="assets/js/i18n-dogfather.js?v=20260820j"></script>
|
||||
<script src="assets/js/main.js?v=20260820j"></script>
|
||||
<script src="assets/js/i18n-dogfather.js?v=20260820k"></script>
|
||||
<script src="assets/js/main.js?v=20260820k"></script>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
@@ -26,8 +26,8 @@
|
||||
<meta name="apple-mobile-web-app-capable" content="yes" />
|
||||
<meta name="apple-mobile-web-app-status-bar-style" content="black-translucent" />
|
||||
<meta name="apple-mobile-web-app-title" content="DogFather" />
|
||||
<link rel="stylesheet" href="assets/css/main.css?v=20260820j" />
|
||||
<link rel="stylesheet" href="assets/css/theme-dogfather.css?v=20260820j" />
|
||||
<link rel="stylesheet" href="assets/css/main.css?v=20260820k" />
|
||||
<link rel="stylesheet" href="assets/css/theme-dogfather.css?v=20260820k" />
|
||||
<style>
|
||||
/* =====================================================================
|
||||
Zugangsseite — bewusst kein braves Standard-Login-Formular, sondern
|
||||
|
||||
+6
-6
@@ -19,10 +19,10 @@
|
||||
<meta name="twitter:description" content="HasiDog: Maskottchen, Musikprojekt und die verspielte, familiäre Seite von Team Dogi." />
|
||||
<meta name="twitter:image" content="https://dogfather-universe.com/assets/img/og-cover.jpg" />
|
||||
|
||||
<link rel="stylesheet" href="assets/css/main.css?v=20260820j" />
|
||||
<link rel="stylesheet" href="assets/css/theme-dogfather.css?v=20260820j" />
|
||||
<link rel="stylesheet" href="assets/css/theme-hasidog.css?v=20260820j" />
|
||||
<link rel="stylesheet" href="assets/css/theme-spicymedia.css?v=20260820j" />
|
||||
<link rel="stylesheet" href="assets/css/main.css?v=20260820k" />
|
||||
<link rel="stylesheet" href="assets/css/theme-dogfather.css?v=20260820k" />
|
||||
<link rel="stylesheet" href="assets/css/theme-hasidog.css?v=20260820k" />
|
||||
<link rel="stylesheet" href="assets/css/theme-spicymedia.css?v=20260820k" />
|
||||
</head>
|
||||
<body class="mit-hintergrund" style="--page-bg:url('/assets/img/bg-hasidog.jpg');--page-bg-mobile:url('/assets/img/bg-hasidog-mobile.jpg');">
|
||||
<div id="site-header"></div>
|
||||
@@ -182,7 +182,7 @@
|
||||
</main>
|
||||
|
||||
<div id="site-footer"></div>
|
||||
<script src="assets/js/i18n-hasidog.js?v=20260820j"></script>
|
||||
<script src="assets/js/main.js?v=20260820j"></script>
|
||||
<script src="assets/js/i18n-hasidog.js?v=20260820k"></script>
|
||||
<script src="assets/js/main.js?v=20260820k"></script>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
+9
-9
@@ -19,10 +19,10 @@
|
||||
<meta name="twitter:description" content="DOGFATHER UNIVERSE: die Heimat von DogFather als Streamer, der HasiDog-Community und Manager DogFather bei Spicy Media." />
|
||||
<meta name="twitter:image" content="https://dogfather-universe.com/assets/img/og-cover.jpg" />
|
||||
|
||||
<link rel="stylesheet" href="assets/css/main.css?v=20260820j" />
|
||||
<link rel="stylesheet" href="assets/css/theme-dogfather.css?v=20260820j" />
|
||||
<link rel="stylesheet" href="assets/css/theme-hasidog.css?v=20260820j" />
|
||||
<link rel="stylesheet" href="assets/css/theme-spicymedia.css?v=20260820j" />
|
||||
<link rel="stylesheet" href="assets/css/main.css?v=20260820k" />
|
||||
<link rel="stylesheet" href="assets/css/theme-dogfather.css?v=20260820k" />
|
||||
<link rel="stylesheet" href="assets/css/theme-hasidog.css?v=20260820k" />
|
||||
<link rel="stylesheet" href="assets/css/theme-spicymedia.css?v=20260820k" />
|
||||
</head>
|
||||
<body class="start-seite">
|
||||
<div id="site-header"></div>
|
||||
@@ -293,11 +293,11 @@
|
||||
</main>
|
||||
|
||||
<div id="site-footer"></div>
|
||||
<script src="assets/js/data-modis.js?v=20260820j"></script>
|
||||
<script src="assets/js/data-scouts.js?v=20260820j"></script>
|
||||
<script src="assets/js/data-aktionen.js?v=20260820j"></script>
|
||||
<script src="assets/js/i18n-index.js?v=20260820j"></script>
|
||||
<script src="assets/js/main.js?v=20260820j"></script>
|
||||
<script src="assets/js/data-modis.js?v=20260820k"></script>
|
||||
<script src="assets/js/data-scouts.js?v=20260820k"></script>
|
||||
<script src="assets/js/data-aktionen.js?v=20260820k"></script>
|
||||
<script src="assets/js/i18n-index.js?v=20260820k"></script>
|
||||
<script src="assets/js/main.js?v=20260820k"></script>
|
||||
<script>
|
||||
// Gleiche URL wie in assets/js/forms.js / postfach.html — läuft seit
|
||||
// 05.08.2026 auf dem eigenen Server statt auf Cloudflare Workers.
|
||||
|
||||
+6
-6
@@ -19,10 +19,10 @@
|
||||
<meta name="twitter:description" content="Kontakt, Impressum und Datenschutz von DOGFATHER UNIVERSE." />
|
||||
<meta name="twitter:image" content="https://dogfather-universe.com/assets/img/og-cover.jpg" />
|
||||
|
||||
<link rel="stylesheet" href="assets/css/main.css?v=20260820j" />
|
||||
<link rel="stylesheet" href="assets/css/theme-dogfather.css?v=20260820j" />
|
||||
<link rel="stylesheet" href="assets/css/theme-hasidog.css?v=20260820j" />
|
||||
<link rel="stylesheet" href="assets/css/theme-spicymedia.css?v=20260820j" />
|
||||
<link rel="stylesheet" href="assets/css/main.css?v=20260820k" />
|
||||
<link rel="stylesheet" href="assets/css/theme-dogfather.css?v=20260820k" />
|
||||
<link rel="stylesheet" href="assets/css/theme-hasidog.css?v=20260820k" />
|
||||
<link rel="stylesheet" href="assets/css/theme-spicymedia.css?v=20260820k" />
|
||||
</head>
|
||||
<body>
|
||||
<div id="site-header"></div>
|
||||
@@ -126,7 +126,7 @@
|
||||
</main>
|
||||
|
||||
<div id="site-footer"></div>
|
||||
<script src="assets/js/i18n-kontakt.js?v=20260820j"></script>
|
||||
<script src="assets/js/main.js?v=20260820j"></script>
|
||||
<script src="assets/js/i18n-kontakt.js?v=20260820k"></script>
|
||||
<script src="assets/js/main.js?v=20260820k"></script>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
+6
-6
@@ -19,10 +19,10 @@
|
||||
<meta name="twitter:description" content="Alle Social-Media- und Musik-Links von DogFather an einem Ort — plus QR-Codes." />
|
||||
<meta name="twitter:image" content="https://dogfather-universe.com/assets/img/og-cover.jpg" />
|
||||
|
||||
<link rel="stylesheet" href="assets/css/main.css?v=20260820j" />
|
||||
<link rel="stylesheet" href="assets/css/theme-dogfather.css?v=20260820j" />
|
||||
<link rel="stylesheet" href="assets/css/theme-hasidog.css?v=20260820j" />
|
||||
<link rel="stylesheet" href="assets/css/theme-spicymedia.css?v=20260820j" />
|
||||
<link rel="stylesheet" href="assets/css/main.css?v=20260820k" />
|
||||
<link rel="stylesheet" href="assets/css/theme-dogfather.css?v=20260820k" />
|
||||
<link rel="stylesheet" href="assets/css/theme-hasidog.css?v=20260820k" />
|
||||
<link rel="stylesheet" href="assets/css/theme-spicymedia.css?v=20260820k" />
|
||||
<style>
|
||||
/* Rabattcodes: an das Supporter-Abo gekoppelt (Nutzer-Feedback
|
||||
03.08.2026: "verbinde es mit dem Abo... wenn die Leute drauf drücken
|
||||
@@ -140,8 +140,8 @@
|
||||
</main>
|
||||
|
||||
<div id="site-footer"></div>
|
||||
<script src="assets/js/i18n-links.js?v=20260820j"></script>
|
||||
<script src="assets/js/main.js?v=20260820j"></script>
|
||||
<script src="assets/js/i18n-links.js?v=20260820k"></script>
|
||||
<script src="assets/js/main.js?v=20260820k"></script>
|
||||
<script>
|
||||
(function () {
|
||||
document.addEventListener("DOMContentLoaded", function () {
|
||||
|
||||
+6
-6
@@ -19,10 +19,10 @@
|
||||
<meta name="twitter:description" content="DogFather als Manager bei der Spicy Media Agency: Scouts, Creator und faire Zusammenarbeit." />
|
||||
<meta name="twitter:image" content="https://dogfather-universe.com/assets/img/og-cover.jpg" />
|
||||
|
||||
<link rel="stylesheet" href="assets/css/main.css?v=20260820j" />
|
||||
<link rel="stylesheet" href="assets/css/theme-dogfather.css?v=20260820j" />
|
||||
<link rel="stylesheet" href="assets/css/theme-hasidog.css?v=20260820j" />
|
||||
<link rel="stylesheet" href="assets/css/theme-spicymedia.css?v=20260820j" />
|
||||
<link rel="stylesheet" href="assets/css/main.css?v=20260820k" />
|
||||
<link rel="stylesheet" href="assets/css/theme-dogfather.css?v=20260820k" />
|
||||
<link rel="stylesheet" href="assets/css/theme-hasidog.css?v=20260820k" />
|
||||
<link rel="stylesheet" href="assets/css/theme-spicymedia.css?v=20260820k" />
|
||||
</head>
|
||||
<body class="mit-hintergrund" style="--page-bg:url('/assets/img/bg-manager.jpg');--page-bg-mobile:url('/assets/img/bg-manager-mobile.jpg');">
|
||||
<div id="site-header"></div>
|
||||
@@ -126,7 +126,7 @@
|
||||
</main>
|
||||
|
||||
<div id="site-footer"></div>
|
||||
<script src="assets/js/i18n-manager.js?v=20260820j"></script>
|
||||
<script src="assets/js/main.js?v=20260820j"></script>
|
||||
<script src="assets/js/i18n-manager.js?v=20260820k"></script>
|
||||
<script src="assets/js/main.js?v=20260820k"></script>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
+9
-9
@@ -19,10 +19,10 @@
|
||||
<meta name="twitter:description" content="Fotos und Videos rund um Husky Casper an einem Ort." />
|
||||
<meta name="twitter:image" content="https://dogfather-universe.com/assets/img/og-cover.jpg" />
|
||||
|
||||
<link rel="stylesheet" href="assets/css/main.css?v=20260820j" />
|
||||
<link rel="stylesheet" href="assets/css/theme-dogfather.css?v=20260820j" />
|
||||
<link rel="stylesheet" href="assets/css/theme-hasidog.css?v=20260820j" />
|
||||
<link rel="stylesheet" href="assets/css/theme-spicymedia.css?v=20260820j" />
|
||||
<link rel="stylesheet" href="assets/css/main.css?v=20260820k" />
|
||||
<link rel="stylesheet" href="assets/css/theme-dogfather.css?v=20260820k" />
|
||||
<link rel="stylesheet" href="assets/css/theme-hasidog.css?v=20260820k" />
|
||||
<link rel="stylesheet" href="assets/css/theme-spicymedia.css?v=20260820k" />
|
||||
</head>
|
||||
<body class="mit-hintergrund" data-welt="casper" style="--page-bg:url('/assets/img/bg-casper.jpg');--page-bg-mobile:url('/assets/img/bg-casper-mobile.jpg');">
|
||||
<div id="site-header"></div>
|
||||
@@ -59,10 +59,10 @@
|
||||
</div>
|
||||
|
||||
<div id="site-footer"></div>
|
||||
<script src="assets/js/data-media.js?v=20260820j"></script>
|
||||
<script src="assets/js/data-videos.js?v=20260820j"></script>
|
||||
<script src="assets/js/medien-welt.js?v=20260820j"></script>
|
||||
<script src="assets/js/i18n-medien-casper.js?v=20260820j"></script>
|
||||
<script src="assets/js/main.js?v=20260820j"></script>
|
||||
<script src="assets/js/data-media.js?v=20260820k"></script>
|
||||
<script src="assets/js/data-videos.js?v=20260820k"></script>
|
||||
<script src="assets/js/medien-welt.js?v=20260820k"></script>
|
||||
<script src="assets/js/i18n-medien-casper.js?v=20260820k"></script>
|
||||
<script src="assets/js/main.js?v=20260820k"></script>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
+10
-10
@@ -19,10 +19,10 @@
|
||||
<meta name="twitter:description" content="Fotos, Videos und Events aus DogFathers Welt an einem Ort." />
|
||||
<meta name="twitter:image" content="https://dogfather-universe.com/assets/img/og-cover.jpg" />
|
||||
|
||||
<link rel="stylesheet" href="assets/css/main.css?v=20260820j" />
|
||||
<link rel="stylesheet" href="assets/css/theme-dogfather.css?v=20260820j" />
|
||||
<link rel="stylesheet" href="assets/css/theme-hasidog.css?v=20260820j" />
|
||||
<link rel="stylesheet" href="assets/css/theme-spicymedia.css?v=20260820j" />
|
||||
<link rel="stylesheet" href="assets/css/main.css?v=20260820k" />
|
||||
<link rel="stylesheet" href="assets/css/theme-dogfather.css?v=20260820k" />
|
||||
<link rel="stylesheet" href="assets/css/theme-hasidog.css?v=20260820k" />
|
||||
<link rel="stylesheet" href="assets/css/theme-spicymedia.css?v=20260820k" />
|
||||
</head>
|
||||
<body class="mit-hintergrund" data-welt="dogfather" style="--page-bg:url('/assets/img/bg-dogfather.jpg');--page-bg-mobile:url('/assets/img/bg-dogfather-mobile.jpg');">
|
||||
<div id="site-header"></div>
|
||||
@@ -65,11 +65,11 @@
|
||||
</div>
|
||||
|
||||
<div id="site-footer"></div>
|
||||
<script src="assets/js/data-media.js?v=20260820j"></script>
|
||||
<script src="assets/js/data-videos.js?v=20260820j"></script>
|
||||
<script src="assets/js/data-aktionen.js?v=20260820j"></script>
|
||||
<script src="assets/js/medien-welt.js?v=20260820j"></script>
|
||||
<script src="assets/js/i18n-medien-dogfather.js?v=20260820j"></script>
|
||||
<script src="assets/js/main.js?v=20260820j"></script>
|
||||
<script src="assets/js/data-media.js?v=20260820k"></script>
|
||||
<script src="assets/js/data-videos.js?v=20260820k"></script>
|
||||
<script src="assets/js/data-aktionen.js?v=20260820k"></script>
|
||||
<script src="assets/js/medien-welt.js?v=20260820k"></script>
|
||||
<script src="assets/js/i18n-medien-dogfather.js?v=20260820k"></script>
|
||||
<script src="assets/js/main.js?v=20260820k"></script>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
+10
-10
@@ -19,10 +19,10 @@
|
||||
<meta name="twitter:description" content="Fotos, Videos und Events aus der HasiDog-Welt an einem Ort." />
|
||||
<meta name="twitter:image" content="https://dogfather-universe.com/assets/img/og-cover.jpg" />
|
||||
|
||||
<link rel="stylesheet" href="assets/css/main.css?v=20260820j" />
|
||||
<link rel="stylesheet" href="assets/css/theme-dogfather.css?v=20260820j" />
|
||||
<link rel="stylesheet" href="assets/css/theme-hasidog.css?v=20260820j" />
|
||||
<link rel="stylesheet" href="assets/css/theme-spicymedia.css?v=20260820j" />
|
||||
<link rel="stylesheet" href="assets/css/main.css?v=20260820k" />
|
||||
<link rel="stylesheet" href="assets/css/theme-dogfather.css?v=20260820k" />
|
||||
<link rel="stylesheet" href="assets/css/theme-hasidog.css?v=20260820k" />
|
||||
<link rel="stylesheet" href="assets/css/theme-spicymedia.css?v=20260820k" />
|
||||
</head>
|
||||
<body class="mit-hintergrund" data-welt="hasidog" style="--page-bg:url('/assets/img/bg-hasidog.jpg');--page-bg-mobile:url('/assets/img/bg-hasidog-mobile.jpg');">
|
||||
<div id="site-header"></div>
|
||||
@@ -65,11 +65,11 @@
|
||||
</div>
|
||||
|
||||
<div id="site-footer"></div>
|
||||
<script src="assets/js/data-media.js?v=20260820j"></script>
|
||||
<script src="assets/js/data-videos.js?v=20260820j"></script>
|
||||
<script src="assets/js/data-aktionen.js?v=20260820j"></script>
|
||||
<script src="assets/js/medien-welt.js?v=20260820j"></script>
|
||||
<script src="assets/js/i18n-medien-hasidog.js?v=20260820j"></script>
|
||||
<script src="assets/js/main.js?v=20260820j"></script>
|
||||
<script src="assets/js/data-media.js?v=20260820k"></script>
|
||||
<script src="assets/js/data-videos.js?v=20260820k"></script>
|
||||
<script src="assets/js/data-aktionen.js?v=20260820k"></script>
|
||||
<script src="assets/js/medien-welt.js?v=20260820k"></script>
|
||||
<script src="assets/js/i18n-medien-hasidog.js?v=20260820k"></script>
|
||||
<script src="assets/js/main.js?v=20260820k"></script>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
+9
-9
@@ -16,10 +16,10 @@
|
||||
<meta name="twitter:card" content="summary_large_image" />
|
||||
<meta name="robots" content="noindex" /> <!-- generische URL, echte Team-Seiten verlinken hierher -->
|
||||
|
||||
<link rel="stylesheet" href="assets/css/main.css?v=20260820j" />
|
||||
<link rel="stylesheet" href="assets/css/theme-dogfather.css?v=20260820j" />
|
||||
<link rel="stylesheet" href="assets/css/theme-hasidog.css?v=20260820j" />
|
||||
<link rel="stylesheet" href="assets/css/theme-spicymedia.css?v=20260820j" />
|
||||
<link rel="stylesheet" href="assets/css/main.css?v=20260820k" />
|
||||
<link rel="stylesheet" href="assets/css/theme-dogfather.css?v=20260820k" />
|
||||
<link rel="stylesheet" href="assets/css/theme-hasidog.css?v=20260820k" />
|
||||
<link rel="stylesheet" href="assets/css/theme-spicymedia.css?v=20260820k" />
|
||||
<style>
|
||||
.profil-photo { aspect-ratio: 4/5; max-width: 420px; margin: 0 auto; }
|
||||
.profil-photo img { width: 100%; height: 100%; object-fit: cover; display: block; }
|
||||
@@ -130,11 +130,11 @@
|
||||
</main>
|
||||
|
||||
<div id="site-footer"></div>
|
||||
<script src="assets/js/data-modis.js?v=20260820j"></script>
|
||||
<script src="assets/js/data-scouts.js?v=20260820j"></script>
|
||||
<script src="assets/js/data-creator.js?v=20260820j"></script>
|
||||
<script src="assets/js/i18n-profil.js?v=20260820j"></script>
|
||||
<script src="assets/js/main.js?v=20260820j"></script>
|
||||
<script src="assets/js/data-modis.js?v=20260820k"></script>
|
||||
<script src="assets/js/data-scouts.js?v=20260820k"></script>
|
||||
<script src="assets/js/data-creator.js?v=20260820k"></script>
|
||||
<script src="assets/js/i18n-profil.js?v=20260820k"></script>
|
||||
<script src="assets/js/main.js?v=20260820k"></script>
|
||||
<script>
|
||||
document.addEventListener("DOMContentLoaded", () => {
|
||||
const slug = new URLSearchParams(location.search).get("p") || "";
|
||||
|
||||
+6
-6
@@ -19,10 +19,10 @@
|
||||
<meta name="twitter:description" content="Rabattcodes von Dogis Partnern für Team Dogi — spar bares Geld bei ausgewählten Marken." />
|
||||
<meta name="twitter:image" content="https://dogfather-universe.com/assets/img/og-cover.jpg" />
|
||||
|
||||
<link rel="stylesheet" href="assets/css/main.css?v=20260820j" />
|
||||
<link rel="stylesheet" href="assets/css/theme-dogfather.css?v=20260820j" />
|
||||
<link rel="stylesheet" href="assets/css/theme-hasidog.css?v=20260820j" />
|
||||
<link rel="stylesheet" href="assets/css/theme-spicymedia.css?v=20260820j" />
|
||||
<link rel="stylesheet" href="assets/css/main.css?v=20260820k" />
|
||||
<link rel="stylesheet" href="assets/css/theme-dogfather.css?v=20260820k" />
|
||||
<link rel="stylesheet" href="assets/css/theme-hasidog.css?v=20260820k" />
|
||||
<link rel="stylesheet" href="assets/css/theme-spicymedia.css?v=20260820k" />
|
||||
<style>
|
||||
/* Leere-Zustand-Kachel im "Gutschein"-Look: gestrichelter Rand wie ein
|
||||
ausgeschnittener Coupon, damit auch OHNE echte Codes schon klar ist,
|
||||
@@ -131,8 +131,8 @@
|
||||
</main>
|
||||
|
||||
<div id="site-footer"></div>
|
||||
<script src="assets/js/i18n-rabattcodes.js?v=20260820j"></script>
|
||||
<script src="assets/js/main.js?v=20260820j"></script>
|
||||
<script src="assets/js/i18n-rabattcodes.js?v=20260820k"></script>
|
||||
<script src="assets/js/main.js?v=20260820k"></script>
|
||||
<script>
|
||||
(function () {
|
||||
document.addEventListener("DOMContentLoaded", function () {
|
||||
|
||||
@@ -29,6 +29,7 @@ const MIGRATION_FILES = [
|
||||
"0003_supporter_abo.sql",
|
||||
"0004_supporter_oauth_providers.sql",
|
||||
"0005_supporter_polls.sql",
|
||||
"0006_testimonials.sql",
|
||||
];
|
||||
|
||||
function alreadyApplied(name) {
|
||||
|
||||
@@ -65,6 +65,9 @@ import {
|
||||
} from "./routes/supporter-admin.js";
|
||||
import { getEventsPublic, getEventsAdmin, saveEvent, uploadEventImage } from "./routes/events.js";
|
||||
import { getSpecialEventPublic, getSpecialEventAdmin, saveSpecialEvent } from "./routes/special-event.js";
|
||||
import {
|
||||
submitTestimonial, getApprovedTestimonialsPublic, getTestimonialsAdmin, setTestimonialStatus, deleteTestimonial,
|
||||
} from "./routes/testimonials.js";
|
||||
import {
|
||||
createPoll, listPolls, closePoll, reopenPoll, deletePoll, getActivePollPublic, voteOnPoll,
|
||||
} from "./routes/polls.js";
|
||||
@@ -159,6 +162,8 @@ app.get("/live-status", wrap(handleLiveStatusGet));
|
||||
app.post("/auth/login", wrap(login));
|
||||
app.get("/events-of-year", wrap(getEventsPublic));
|
||||
app.get("/special-event", wrap(getSpecialEventPublic));
|
||||
app.post("/testimonials/submit", wrap(submitTestimonial));
|
||||
app.get("/testimonials", wrap(getApprovedTestimonialsPublic));
|
||||
|
||||
app.post("/supporter/register", wrap(registerSupporter));
|
||||
app.post("/supporter/verify-email", wrap(verifySupporterEmail));
|
||||
@@ -200,6 +205,11 @@ app.post("/admin/events/upload-image", requireTeamSession, bildUpload.single("im
|
||||
app.post("/admin/special-event", requireTeamSession, withSession(getSpecialEventAdmin));
|
||||
app.post("/admin/special-event/save", requireTeamSession, withSession(saveSpecialEvent));
|
||||
|
||||
// "Stimmen"-Warteschlange (20.08.2026)
|
||||
app.post("/admin/testimonials/list", requireTeamSession, withSession(getTestimonialsAdmin));
|
||||
app.post("/admin/testimonials/status", requireTeamSession, withSession(setTestimonialStatus));
|
||||
app.post("/admin/testimonials/delete", requireTeamSession, withSession(deleteTestimonial));
|
||||
|
||||
app.post("/admin/roles/list", requireTeamSession, withSession(listRoles));
|
||||
app.post("/admin/roles/create", requireTeamSession, withSession(createRole));
|
||||
app.post("/admin/roles/update", requireTeamSession, withSession(updateRole));
|
||||
|
||||
@@ -48,6 +48,7 @@ export const PERMISSIONS = {
|
||||
POLLS_MANAGE: "Supporter-Abstimmungen erstellen & auswerten",
|
||||
|
||||
EVENTS_MANAGE: "Startseite: \"Event des Jahres\" verwalten",
|
||||
TESTIMONIALS_MANAGE: "Stimmen/Testimonials verwalten",
|
||||
};
|
||||
|
||||
export const PERMISSION_GROUPS = [
|
||||
@@ -59,6 +60,7 @@ export const PERMISSION_GROUPS = [
|
||||
{ label: "Einstellungen", keys: ["SETTINGS_EDIT_FORMS", "SETTINGS_EDIT_STATUS_TYPES", "SETTINGS_VIEW_STATS", "SETTINGS_VIEW_ACTIVITY_LOG", "SETTINGS_EDIT_GENERAL"] },
|
||||
{ label: "DogiCrew 🩵", keys: ["SUPPORTER_VIEW", "SUPPORTER_MANAGE_PREMIUMS", "SUPPORTER_MANAGE_COLLECTIONS", "POLLS_MANAGE"] },
|
||||
{ label: "Startseite", keys: ["EVENTS_MANAGE"] },
|
||||
{ label: "Stimmen", keys: ["TESTIMONIALS_MANAGE"] },
|
||||
];
|
||||
|
||||
export const OWNER_ONLY_ACTIONS = [
|
||||
|
||||
@@ -0,0 +1,118 @@
|
||||
/* =====================================================================
|
||||
routes/testimonials.js — "Stimmen" auf stimmen.html (20.08.2026,
|
||||
Nutzer-Wunsch: "eine richtig geile kachel ... wo die leute mit namen
|
||||
und tiktok namen mir eine nachricht schreiben können die ich in die
|
||||
verwaltung kriege, und dann kann ich die ausgewählten über die
|
||||
verwaltungsseite auf die website hinzufügen").
|
||||
|
||||
Öffentlich: jede/jeder darf eine Nachricht einreichen (kein Login
|
||||
nötig, absichtlich niedrigschwellig -- "mega persönlich zu den Fans").
|
||||
Landet als Entwurf ("eingereicht"), wird NIE automatisch veröffentlicht.
|
||||
Nur Dogi/VanVan (TESTIMONIALS_MANAGE) sehen die Warteschlange und
|
||||
entscheiden pro Eintrag: freigeben (-> erscheint auf stimmen.html),
|
||||
ablehnen oder löschen. ===================================================================== */
|
||||
import { db } from "../db.js";
|
||||
import { generateId, nowIso } from "../lib/crypto.js";
|
||||
import { hasPermission } from "../lib/permissions.js";
|
||||
import { json } from "../lib/http.js";
|
||||
import { logAction } from "../lib/audit.js";
|
||||
|
||||
function can(session, key) {
|
||||
if (!session) return false;
|
||||
if (session.isOwner) return true;
|
||||
return hasPermission(session.permissions, session.overrides, key);
|
||||
}
|
||||
|
||||
function forbidden(res) {
|
||||
return json(res, { ok: false, error: "Keine Berechtigung." }, 403);
|
||||
}
|
||||
|
||||
function oeffentlichesFeld(row) {
|
||||
return {
|
||||
id: row.id,
|
||||
name: row.name,
|
||||
tiktokUsername: row.tiktok_username,
|
||||
nachricht: row.nachricht,
|
||||
};
|
||||
}
|
||||
|
||||
function vollesFeld(row) {
|
||||
return {
|
||||
id: row.id,
|
||||
name: row.name,
|
||||
tiktokUsername: row.tiktok_username,
|
||||
nachricht: row.nachricht,
|
||||
status: row.status,
|
||||
createdAt: row.created_at,
|
||||
entschiedenAm: row.entschieden_am,
|
||||
entschiedenVon: row.entschieden_von,
|
||||
};
|
||||
}
|
||||
|
||||
/** Öffentlich, keine Session: eine Nachricht einreichen. Bewusst simpel
|
||||
gehalten (kein E-Mail-Nachweis, keine Registrierung) -- die einzige
|
||||
Schutzschicht gegen Missbrauch ist die manuelle Freigabe durch Dogi,
|
||||
kein Eintrag wird je ungesehen veröffentlicht. */
|
||||
export async function submitTestimonial(req, res) {
|
||||
const body = req.body || {};
|
||||
const name = String(body.name || "").trim().slice(0, 60);
|
||||
const tiktokUsername = String(body.tiktokUsername || "").trim().replace(/^@/, "").slice(0, 40) || null;
|
||||
const nachricht = String(body.nachricht || "").trim().slice(0, 500);
|
||||
|
||||
if (!name) return json(res, { ok: false, error: "Bitte gib deinen Namen an." }, 400);
|
||||
if (!nachricht) return json(res, { ok: false, error: "Bitte schreib eine Nachricht." }, 400);
|
||||
if (nachricht.length < 10) return json(res, { ok: false, error: "Die Nachricht ist noch sehr kurz -- magst du noch etwas mehr schreiben?" }, 400);
|
||||
|
||||
const id = generateId();
|
||||
db.prepare(
|
||||
`INSERT INTO testimonials (id, name, tiktok_username, nachricht, status, created_at) VALUES (?, ?, ?, ?, 'eingereicht', ?)`
|
||||
).run(id, name, tiktokUsername, nachricht, nowIso());
|
||||
|
||||
logAction("anonym", "testimonial.submitted", id, { name });
|
||||
return json(res, { ok: true });
|
||||
}
|
||||
|
||||
/** Öffentlich, keine Session: nur freigegebene Stimmen, neueste zuerst. */
|
||||
export async function getApprovedTestimonialsPublic(req, res) {
|
||||
const rows = db
|
||||
.prepare(`SELECT * FROM testimonials WHERE status = 'freigegeben' ORDER BY entschieden_am DESC LIMIT 60`)
|
||||
.all();
|
||||
return json(res, { ok: true, testimonials: rows.map(oeffentlichesFeld) });
|
||||
}
|
||||
|
||||
/** Für die Verwaltung: ALLE Einreichungen, neueste zuerst -- offene zuerst
|
||||
einsortiert, damit die Warteschlange sofort ins Auge fällt. */
|
||||
export async function getTestimonialsAdmin(req, res, session) {
|
||||
if (!can(session, "TESTIMONIALS_MANAGE")) return forbidden(res);
|
||||
const rows = db
|
||||
.prepare(
|
||||
`SELECT * FROM testimonials
|
||||
ORDER BY CASE status WHEN 'eingereicht' THEN 0 ELSE 1 END, created_at DESC`
|
||||
)
|
||||
.all();
|
||||
return json(res, { ok: true, testimonials: rows.map(vollesFeld) });
|
||||
}
|
||||
|
||||
export async function setTestimonialStatus(req, res, session) {
|
||||
if (!can(session, "TESTIMONIALS_MANAGE")) return forbidden(res);
|
||||
const id = String(req.body?.id || "");
|
||||
const status = String(req.body?.status || "");
|
||||
if (!["freigegeben", "abgelehnt", "eingereicht"].includes(status)) {
|
||||
return json(res, { ok: false, error: "Ungültiger Status." }, 400);
|
||||
}
|
||||
const info = db
|
||||
.prepare(`UPDATE testimonials SET status = ?, entschieden_am = ?, entschieden_von = ? WHERE id = ?`)
|
||||
.run(status, nowIso(), session.actor, id);
|
||||
if (info.changes === 0) return json(res, { ok: false, error: "Nicht gefunden." }, 404);
|
||||
logAction(session.actor, "testimonial.status_changed", id, { status });
|
||||
return json(res, { ok: true });
|
||||
}
|
||||
|
||||
export async function deleteTestimonial(req, res, session) {
|
||||
if (!can(session, "TESTIMONIALS_MANAGE")) return forbidden(res);
|
||||
const id = String(req.body?.id || "");
|
||||
const info = db.prepare(`DELETE FROM testimonials WHERE id = ?`).run(id);
|
||||
if (info.changes === 0) return json(res, { ok: false, error: "Nicht gefunden." }, 404);
|
||||
logAction(session.actor, "testimonial.deleted", id, null);
|
||||
return json(res, { ok: true });
|
||||
}
|
||||
+48
-17
@@ -19,10 +19,10 @@
|
||||
<meta name="twitter:description" content="Was die Community und das Team über DogFather sagen." />
|
||||
<meta name="twitter:image" content="https://dogfather-universe.com/assets/img/og-cover.jpg" />
|
||||
|
||||
<link rel="stylesheet" href="assets/css/main.css?v=20260820j" />
|
||||
<link rel="stylesheet" href="assets/css/theme-dogfather.css?v=20260820j" />
|
||||
<link rel="stylesheet" href="assets/css/theme-hasidog.css?v=20260820j" />
|
||||
<link rel="stylesheet" href="assets/css/theme-spicymedia.css?v=20260820j" />
|
||||
<link rel="stylesheet" href="assets/css/main.css?v=20260820k" />
|
||||
<link rel="stylesheet" href="assets/css/theme-dogfather.css?v=20260820k" />
|
||||
<link rel="stylesheet" href="assets/css/theme-hasidog.css?v=20260820k" />
|
||||
<link rel="stylesheet" href="assets/css/theme-spicymedia.css?v=20260820k" />
|
||||
</head>
|
||||
<body class="mit-hintergrund" style="--page-bg:url('/assets/img/bg-stimmen.jpg');--page-bg-mobile:url('/assets/img/bg-stimmen-mobile.jpg');">
|
||||
<div id="site-header"></div>
|
||||
@@ -37,27 +37,58 @@
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- ===== Eigene Stimme einreichen (20.08.2026, Nutzer-Wunsch: "eine
|
||||
richtig geile kachel ... wo die leute mit namen und tiktok namen
|
||||
mir eine nachricht schreiben können", "das wird mega persoenlich
|
||||
zu den fans"). Landet in der Verwaltung, wird NIE automatisch
|
||||
veroeffentlicht -- siehe server-internal/routes/testimonials.js. -->
|
||||
<section class="section-tight">
|
||||
<div class="container grid grid-3">
|
||||
<div class="card">
|
||||
<p class="quote" data-i18n="st_quote_community">„Hier steht bald eine echte Stimme aus der Community. 🩵"</p>
|
||||
<p class="small muted" data-i18n="st_label_community">— Community-Mitglied</p>
|
||||
<div class="container">
|
||||
<div class="stimmen-einreichen-kachel" id="stimmen-formular-box">
|
||||
<span class="stimmen-einreichen-glanz" aria-hidden="true"></span>
|
||||
<div class="stimmen-einreichen-kopf">
|
||||
<span class="stimmen-einreichen-herz" aria-hidden="true">💌</span>
|
||||
<h2 data-i18n="st_form_titel">Deine Stimme zählt</h2>
|
||||
<p class="small" data-i18n="st_form_text">
|
||||
Was bedeutet dir Team Dogi? Schreib uns deine Geschichte — mit etwas Glück liest DogFather sie
|
||||
persönlich und stellt sie hier auf die Seite.
|
||||
</p>
|
||||
</div>
|
||||
<div class="card">
|
||||
<p class="quote" data-i18n="st_quote_team">„Hier steht bald eine echte Stimme aus dem Team. 🩵"</p>
|
||||
<p class="small muted" data-i18n="st_label_team">— Team-Mitglied</p>
|
||||
<form id="stimmen-formular">
|
||||
<div class="field">
|
||||
<label for="st-name" data-i18n="st_form_name_label">Dein Name</label>
|
||||
<input type="text" id="st-name" maxlength="60" required />
|
||||
</div>
|
||||
<div class="card">
|
||||
<p class="quote" data-i18n="st_quote_scout">„Hier steht bald eine echte Stimme von Scouts & Creatorn. 🩵"</p>
|
||||
<p class="small muted" data-i18n="st_label_scout">— Scout/Creator</p>
|
||||
<div class="field">
|
||||
<label for="st-tiktok" data-i18n="st_form_tiktok_label">TikTok-Name (optional)</label>
|
||||
<input type="text" id="st-tiktok" maxlength="40" placeholder="@dein-name" />
|
||||
</div>
|
||||
<div class="field">
|
||||
<label for="st-nachricht" data-i18n="st_form_nachricht_label">Deine Nachricht</label>
|
||||
<textarea id="st-nachricht" maxlength="500" rows="4" required data-i18n-attr="placeholder:st_form_nachricht_placeholder" placeholder="Erzähl uns, was dir Team Dogi bedeutet …"></textarea>
|
||||
</div>
|
||||
<button type="submit" class="btn btn-primary" style="width:100%;" data-i18n="st_form_absenden">💙 Nachricht einreichen</button>
|
||||
</form>
|
||||
<p class="stimmen-erfolg" id="stimmen-erfolg" data-i18n="st_form_erfolg">Danke dir! Deine Nachricht wurde eingereicht und wird von Dogi persönlich geprüft. 🩵</p>
|
||||
<p class="stimmen-fehler" id="stimmen-fehler"></p>
|
||||
</div>
|
||||
</div>
|
||||
<!-- TODO: Echte Testimonials von Community, Team, Scouts und Creator sammeln (mit Einverständnis zur Veröffentlichung). Bis dahin bleiben hier Platzhalter stehen. -->
|
||||
</section>
|
||||
|
||||
<!-- ===== Echte, freigegebene Stimmen -- bleibt komplett unsichtbar, bis
|
||||
mindestens eine Nachricht in der Verwaltung freigegeben wurde (kein
|
||||
Platzhaltertext mehr). ===== -->
|
||||
<section class="section-tight" id="stimmen-echte-root" hidden>
|
||||
<div class="container">
|
||||
<h2 class="text-center" data-i18n="st_echte_titel">Das sagt die Community</h2>
|
||||
<div class="grid grid-3" id="stimmen-echte-grid" style="margin-top:1.6rem;"></div>
|
||||
</div>
|
||||
</section>
|
||||
</main>
|
||||
|
||||
<div id="site-footer"></div>
|
||||
<script src="assets/js/i18n-stimmen.js?v=20260820j"></script>
|
||||
<script src="assets/js/main.js?v=20260820j"></script>
|
||||
<script src="assets/js/i18n-stimmen.js?v=20260820k"></script>
|
||||
<script src="assets/js/main.js?v=20260820k"></script>
|
||||
<script src="assets/js/stimmen.js?v=20260820k"></script>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
+6
-6
@@ -19,10 +19,10 @@
|
||||
<meta name="twitter:description" content="Die persönliche Geschichte hinter DogFather — Filipe, Streamer, Manager bei Spicy Media und Hundepapa von Husky Casper." />
|
||||
<meta name="twitter:image" content="https://dogfather-universe.com/assets/img/og-cover.jpg" />
|
||||
|
||||
<link rel="stylesheet" href="assets/css/main.css?v=20260820j" />
|
||||
<link rel="stylesheet" href="assets/css/theme-dogfather.css?v=20260820j" />
|
||||
<link rel="stylesheet" href="assets/css/theme-hasidog.css?v=20260820j" />
|
||||
<link rel="stylesheet" href="assets/css/theme-spicymedia.css?v=20260820j" />
|
||||
<link rel="stylesheet" href="assets/css/main.css?v=20260820k" />
|
||||
<link rel="stylesheet" href="assets/css/theme-dogfather.css?v=20260820k" />
|
||||
<link rel="stylesheet" href="assets/css/theme-hasidog.css?v=20260820k" />
|
||||
<link rel="stylesheet" href="assets/css/theme-spicymedia.css?v=20260820k" />
|
||||
</head>
|
||||
<body class="mit-hintergrund" style="--page-bg:url('/assets/img/bg-streamer.jpg');--page-bg-mobile:url('/assets/img/bg-streamer-mobile.jpg');">
|
||||
<div id="site-header"></div>
|
||||
@@ -189,7 +189,7 @@
|
||||
</main>
|
||||
|
||||
<div id="site-footer"></div>
|
||||
<script src="assets/js/i18n-streamer.js?v=20260820j"></script>
|
||||
<script src="assets/js/main.js?v=20260820j"></script>
|
||||
<script src="assets/js/i18n-streamer.js?v=20260820k"></script>
|
||||
<script src="assets/js/main.js?v=20260820k"></script>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
+9
-9
@@ -19,17 +19,17 @@
|
||||
<meta name="twitter:description" content="Wann geht DogFather live? Streamplan und Livebereich." />
|
||||
<meta name="twitter:image" content="https://dogfather-universe.com/assets/img/og-cover.jpg" />
|
||||
|
||||
<link rel="stylesheet" href="assets/css/main.css?v=20260820j" />
|
||||
<link rel="stylesheet" href="assets/css/theme-dogfather.css?v=20260820j" />
|
||||
<link rel="stylesheet" href="assets/css/theme-hasidog.css?v=20260820j" />
|
||||
<link rel="stylesheet" href="assets/css/theme-spicymedia.css?v=20260820j" />
|
||||
<link rel="stylesheet" href="assets/css/main.css?v=20260820k" />
|
||||
<link rel="stylesheet" href="assets/css/theme-dogfather.css?v=20260820k" />
|
||||
<link rel="stylesheet" href="assets/css/theme-hasidog.css?v=20260820k" />
|
||||
<link rel="stylesheet" href="assets/css/theme-spicymedia.css?v=20260820k" />
|
||||
</head>
|
||||
<body class="mit-hintergrund" style="--page-bg:url('/assets/img/bg-streamplan.jpg?v=20260820j');--page-bg-mobile:url('/assets/img/bg-streamplan-mobile.jpg?v=20260820j');">
|
||||
<body class="mit-hintergrund" style="--page-bg:url('/assets/img/bg-streamplan.jpg?v=20260820k');--page-bg-mobile:url('/assets/img/bg-streamplan-mobile.jpg?v=20260820k');">
|
||||
<div id="site-header"></div>
|
||||
|
||||
<main>
|
||||
<section class="hero page-bg">
|
||||
<div class="hero-banner"><img src="/assets/img/bg-streamplan.jpg?v=20260820j" alt="" loading="eager" /></div>
|
||||
<div class="hero-banner"><img src="/assets/img/bg-streamplan.jpg?v=20260820k" alt="" loading="eager" /></div>
|
||||
<div class="container">
|
||||
<span class="eyebrow" data-i18n="sp_eyebrow">Welt 1 — DogFather</span>
|
||||
<h1 data-i18n="sp_h1">Streamplan & Livebereich</h1>
|
||||
@@ -126,8 +126,8 @@
|
||||
</main>
|
||||
|
||||
<div id="site-footer"></div>
|
||||
<script src="assets/js/i18n-streamplan.js?v=20260820j"></script>
|
||||
<script src="assets/js/main.js?v=20260820j"></script>
|
||||
<script src="assets/js/streamplan.js?v=20260820j"></script>
|
||||
<script src="assets/js/i18n-streamplan.js?v=20260820k"></script>
|
||||
<script src="assets/js/main.js?v=20260820k"></script>
|
||||
<script src="assets/js/streamplan.js?v=20260820k"></script>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
+7
-7
@@ -9,10 +9,10 @@
|
||||
<link rel="manifest" href="/manifest.json" />
|
||||
<meta name="theme-color" content="#0b0d10" />
|
||||
<meta name="robots" content="noindex" />
|
||||
<link rel="stylesheet" href="assets/css/main.css?v=20260820j" />
|
||||
<link rel="stylesheet" href="assets/css/theme-dogfather.css?v=20260820j" />
|
||||
<link rel="stylesheet" href="assets/css/theme-hasidog.css?v=20260820j" />
|
||||
<link rel="stylesheet" href="assets/css/theme-spicymedia.css?v=20260820j" />
|
||||
<link rel="stylesheet" href="assets/css/main.css?v=20260820k" />
|
||||
<link rel="stylesheet" href="assets/css/theme-dogfather.css?v=20260820k" />
|
||||
<link rel="stylesheet" href="assets/css/theme-hasidog.css?v=20260820k" />
|
||||
<link rel="stylesheet" href="assets/css/theme-spicymedia.css?v=20260820k" />
|
||||
<style>
|
||||
/* =====================================================================
|
||||
Supporter-Bereich — komplett neu gestaltet (Nutzer-Feedback
|
||||
@@ -416,9 +416,9 @@
|
||||
</main>
|
||||
|
||||
<div id="site-footer"></div>
|
||||
<script src="assets/js/i18n-supporter.js?v=20260820j"></script>
|
||||
<script src="assets/js/supporter.js?v=20260820j"></script>
|
||||
<script src="assets/js/main.js?v=20260820j"></script>
|
||||
<script src="assets/js/i18n-supporter.js?v=20260820k"></script>
|
||||
<script src="assets/js/supporter.js?v=20260820k"></script>
|
||||
<script src="assets/js/main.js?v=20260820k"></script>
|
||||
<script>
|
||||
(function () {
|
||||
const S = window.DogiSupporter;
|
||||
|
||||
+7
-7
@@ -19,10 +19,10 @@
|
||||
<meta name="twitter:description" content="VanVan, Diene, Funny, Miss und Marina — das Modi-/Core-Team von Team Dogi." />
|
||||
<meta name="twitter:image" content="https://dogfather-universe.com/assets/img/og-cover.jpg" />
|
||||
|
||||
<link rel="stylesheet" href="assets/css/main.css?v=20260820j" />
|
||||
<link rel="stylesheet" href="assets/css/theme-dogfather.css?v=20260820j" />
|
||||
<link rel="stylesheet" href="assets/css/theme-hasidog.css?v=20260820j" />
|
||||
<link rel="stylesheet" href="assets/css/theme-spicymedia.css?v=20260820j" />
|
||||
<link rel="stylesheet" href="assets/css/main.css?v=20260820k" />
|
||||
<link rel="stylesheet" href="assets/css/theme-dogfather.css?v=20260820k" />
|
||||
<link rel="stylesheet" href="assets/css/theme-hasidog.css?v=20260820k" />
|
||||
<link rel="stylesheet" href="assets/css/theme-spicymedia.css?v=20260820k" />
|
||||
</head>
|
||||
<body class="mit-hintergrund" style="--page-bg:url('/assets/img/bg-dogfather.jpg');--page-bg-mobile:url('/assets/img/bg-dogfather-mobile.jpg');">
|
||||
<div id="site-header"></div>
|
||||
@@ -66,9 +66,9 @@
|
||||
</main>
|
||||
|
||||
<div id="site-footer"></div>
|
||||
<script src="assets/js/data-modis.js?v=20260820j"></script>
|
||||
<script src="assets/js/i18n-team-modis.js?v=20260820j"></script>
|
||||
<script src="assets/js/main.js?v=20260820j"></script>
|
||||
<script src="assets/js/data-modis.js?v=20260820k"></script>
|
||||
<script src="assets/js/i18n-team-modis.js?v=20260820k"></script>
|
||||
<script src="assets/js/main.js?v=20260820k"></script>
|
||||
<script>
|
||||
document.addEventListener("DOMContentLoaded", () => {
|
||||
const grid = document.getElementById("modis-grid");
|
||||
|
||||
+7
-7
@@ -19,10 +19,10 @@
|
||||
<meta name="twitter:description" content="Die Scouts von Team Dogi bei Spicy Media." />
|
||||
<meta name="twitter:image" content="https://dogfather-universe.com/assets/img/og-cover.jpg" />
|
||||
|
||||
<link rel="stylesheet" href="assets/css/main.css?v=20260820j" />
|
||||
<link rel="stylesheet" href="assets/css/theme-dogfather.css?v=20260820j" />
|
||||
<link rel="stylesheet" href="assets/css/theme-hasidog.css?v=20260820j" />
|
||||
<link rel="stylesheet" href="assets/css/theme-spicymedia.css?v=20260820j" />
|
||||
<link rel="stylesheet" href="assets/css/main.css?v=20260820k" />
|
||||
<link rel="stylesheet" href="assets/css/theme-dogfather.css?v=20260820k" />
|
||||
<link rel="stylesheet" href="assets/css/theme-hasidog.css?v=20260820k" />
|
||||
<link rel="stylesheet" href="assets/css/theme-spicymedia.css?v=20260820k" />
|
||||
</head>
|
||||
<body class="mit-hintergrund" style="--page-bg:url('/assets/img/bg-scouts.jpg');--page-bg-mobile:url('/assets/img/bg-scouts-mobile.jpg');">
|
||||
<div id="site-header"></div>
|
||||
@@ -49,9 +49,9 @@
|
||||
</main>
|
||||
|
||||
<div id="site-footer"></div>
|
||||
<script src="assets/js/data-scouts.js?v=20260820j"></script>
|
||||
<script src="assets/js/i18n-team-scouts.js?v=20260820j"></script>
|
||||
<script src="assets/js/main.js?v=20260820j"></script>
|
||||
<script src="assets/js/data-scouts.js?v=20260820k"></script>
|
||||
<script src="assets/js/i18n-team-scouts.js?v=20260820k"></script>
|
||||
<script src="assets/js/main.js?v=20260820k"></script>
|
||||
<script>
|
||||
document.addEventListener("DOMContentLoaded", () => {
|
||||
const grid = document.getElementById("scouts-grid");
|
||||
|
||||
+6
-6
@@ -19,10 +19,10 @@
|
||||
<meta name="twitter:description" content="Eine persönliche Widmung von Dogi an Tili — seine TikTok-Muddi, die erste und bis heute einzige Streamerin, die ihm all die Zeit treu geblieben ist." />
|
||||
<meta name="twitter:image" content="https://dogfather-universe.com/assets/img/og-cover.jpg" />
|
||||
|
||||
<link rel="stylesheet" href="assets/css/main.css?v=20260820j" />
|
||||
<link rel="stylesheet" href="assets/css/theme-dogfather.css?v=20260820j" />
|
||||
<link rel="stylesheet" href="assets/css/theme-hasidog.css?v=20260820j" />
|
||||
<link rel="stylesheet" href="assets/css/theme-spicymedia.css?v=20260820j" />
|
||||
<link rel="stylesheet" href="assets/css/main.css?v=20260820k" />
|
||||
<link rel="stylesheet" href="assets/css/theme-dogfather.css?v=20260820k" />
|
||||
<link rel="stylesheet" href="assets/css/theme-hasidog.css?v=20260820k" />
|
||||
<link rel="stylesheet" href="assets/css/theme-spicymedia.css?v=20260820k" />
|
||||
<style>
|
||||
/* Gleiche Anpassung wie vanvan.html: der site-weit etwas größere
|
||||
Fließtext wirkt bei einem so langen persönlichen Brief zu wuchtig —
|
||||
@@ -153,7 +153,7 @@
|
||||
</main>
|
||||
|
||||
<div id="site-footer"></div>
|
||||
<script src="assets/js/i18n-tt-muddi.js?v=20260820j"></script>
|
||||
<script src="assets/js/main.js?v=20260820j"></script>
|
||||
<script src="assets/js/i18n-tt-muddi.js?v=20260820k"></script>
|
||||
<script src="assets/js/main.js?v=20260820k"></script>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
+6
-6
@@ -19,10 +19,10 @@
|
||||
<meta name="twitter:description" content="VanVan erzählt ihre Geschichte: wie sie zu Team Dogi kam, wer sie ist und was DogFather und Team Dogi ihr bedeuten." />
|
||||
<meta name="twitter:image" content="https://dogfather-universe.com/assets/img/og-cover.jpg" />
|
||||
|
||||
<link rel="stylesheet" href="assets/css/main.css?v=20260820j" />
|
||||
<link rel="stylesheet" href="assets/css/theme-dogfather.css?v=20260820j" />
|
||||
<link rel="stylesheet" href="assets/css/theme-hasidog.css?v=20260820j" />
|
||||
<link rel="stylesheet" href="assets/css/theme-spicymedia.css?v=20260820j" />
|
||||
<link rel="stylesheet" href="assets/css/main.css?v=20260820k" />
|
||||
<link rel="stylesheet" href="assets/css/theme-dogfather.css?v=20260820k" />
|
||||
<link rel="stylesheet" href="assets/css/theme-hasidog.css?v=20260820k" />
|
||||
<link rel="stylesheet" href="assets/css/theme-spicymedia.css?v=20260820k" />
|
||||
<style>
|
||||
/* Auf Wunsch: der site-weit vergrößerte Fließtext wirkte auf VanVans
|
||||
eigener Seite zu groß (viel Text am Stück) — hier gezielt wieder
|
||||
@@ -175,7 +175,7 @@
|
||||
</main>
|
||||
|
||||
<div id="site-footer"></div>
|
||||
<script src="assets/js/i18n-vanvan.js?v=20260820j"></script>
|
||||
<script src="assets/js/main.js?v=20260820j"></script>
|
||||
<script src="assets/js/i18n-vanvan.js?v=20260820k"></script>
|
||||
<script src="assets/js/main.js?v=20260820k"></script>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
+77
-6
@@ -19,10 +19,10 @@
|
||||
<link rel="apple-touch-icon" href="assets/img/apple-touch-icon-admin.png" />
|
||||
<meta name="apple-mobile-web-app-title" content="DogiCrew-Verwaltung" />
|
||||
<meta name="theme-color" content="#0e0a16" />
|
||||
<link rel="stylesheet" href="assets/css/main.css?v=20260820j" />
|
||||
<link rel="stylesheet" href="assets/css/theme-dogfather.css?v=20260820j" />
|
||||
<link rel="stylesheet" href="assets/css/theme-hasidog.css?v=20260820j" />
|
||||
<link rel="stylesheet" href="assets/css/theme-spicymedia.css?v=20260820j" />
|
||||
<link rel="stylesheet" href="assets/css/main.css?v=20260820k" />
|
||||
<link rel="stylesheet" href="assets/css/theme-dogfather.css?v=20260820k" />
|
||||
<link rel="stylesheet" href="assets/css/theme-hasidog.css?v=20260820k" />
|
||||
<link rel="stylesheet" href="assets/css/theme-spicymedia.css?v=20260820k" />
|
||||
<style>
|
||||
/* =====================================================================
|
||||
verwaltung.html — Dogis privater Command-Center-Bereich.
|
||||
@@ -844,6 +844,20 @@
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- ===== "Stimmen" verwalten (20.08.2026) — Nutzer-Wunsch: "die
|
||||
ausgewählten über die verwaltungsseite auf die website hinzufügen".
|
||||
Eingereichte Nachrichten von stimmen.html landen hier zur Prüfung,
|
||||
werden NIE automatisch veröffentlicht. ===== -->
|
||||
<section class="section-tight" id="testimonials-root" style="display:none;padding-top:0;">
|
||||
<div class="container">
|
||||
<h2 class="text-center" style="margin-bottom:.3rem;">💬 Stimmen verwalten</h2>
|
||||
<p class="small muted text-center" style="max-width:60ch;margin:0 auto 1.4rem;">
|
||||
Einreichungen von der Stimmen-Seite. Erst nach "Freigeben" erscheinen sie öffentlich.
|
||||
</p>
|
||||
<div id="testimonials-liste"></div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- ===== Supporter-Liste ===== -->
|
||||
<section class="section-tight" id="supp-root" style="display:none;padding-top:0;">
|
||||
<div class="container" id="liste-ansicht">
|
||||
@@ -1018,8 +1032,8 @@
|
||||
</main>
|
||||
|
||||
<div id="site-footer"></div>
|
||||
<script src="assets/js/main.js?v=20260820j"></script>
|
||||
<script src="assets/js/admin-auth.js?v=20260820j"></script>
|
||||
<script src="assets/js/main.js?v=20260820k"></script>
|
||||
<script src="assets/js/admin-auth.js?v=20260820k"></script>
|
||||
<script>
|
||||
(function () {
|
||||
const API_BASE_URL = "https://postfach.dogfather-universe.com";
|
||||
@@ -1095,6 +1109,10 @@
|
||||
document.getElementById("special-event-root").style.display = "";
|
||||
await ladeUndRenderSpecialEvent();
|
||||
}
|
||||
if (window.AdminAuth.sessionHasPermission(session, "TESTIMONIALS_MANAGE")) {
|
||||
document.getElementById("testimonials-root").style.display = "";
|
||||
await ladeUndRenderTestimonials();
|
||||
}
|
||||
const pollsBox = document.getElementById("box-abstimmungen");
|
||||
if (window.AdminAuth.sessionHasPermission(session, "POLLS_MANAGE")) {
|
||||
pollsBox.style.display = "";
|
||||
@@ -1113,6 +1131,7 @@
|
||||
document.getElementById("supp-root").style.display = "none";
|
||||
document.getElementById("events-root").style.display = "none";
|
||||
document.getElementById("special-event-root").style.display = "none";
|
||||
document.getElementById("testimonials-root").style.display = "none";
|
||||
document.getElementById("box-abstimmungen").style.display = "none";
|
||||
}
|
||||
|
||||
@@ -1477,6 +1496,58 @@
|
||||
});
|
||||
}
|
||||
|
||||
/* ===== "Stimmen" — Verwaltung (20.08.2026) =====
|
||||
Offene Einreichungen zuerst (siehe getTestimonialsAdmin-Sortierung),
|
||||
jede Karte mit Freigeben/Ablehnen/Löschen — je nach aktuellem Status
|
||||
stehen nur die sinnvollen Aktionen zur Verfügung. */
|
||||
const TESTIMONIAL_STATUS_LABELS = { eingereicht: "🟡 Neu", freigegeben: "🟢 Freigegeben", abgelehnt: "⚪ Abgelehnt" };
|
||||
|
||||
function testimonialKarteHtml(t) {
|
||||
return `<div class="card vw-card vw-shine-card" style="margin-bottom:1rem;padding:1.3rem 1.4rem;" data-testimonial-id="${t.id}">
|
||||
<div style="display:flex;justify-content:space-between;align-items:flex-start;gap:1rem;flex-wrap:wrap;">
|
||||
<div>
|
||||
<strong>${escapeHtml(t.name)}</strong>${t.tiktokUsername ? ` <span class="small" style="color:var(--text-muted);">@${escapeHtml(t.tiktokUsername)}</span>` : ""}
|
||||
<div class="small" style="color:var(--text-muted);">${TESTIMONIAL_STATUS_LABELS[t.status] || t.status} · eingereicht am ${fmtDatum(t.createdAt)}</div>
|
||||
</div>
|
||||
<div class="btn-row" style="gap:.5rem;">
|
||||
${t.status !== "freigegeben" ? `<button type="button" class="vw-refresh-btn testimonial-freigeben-btn">Freigeben</button>` : ""}
|
||||
${t.status !== "abgelehnt" ? `<button type="button" class="vw-refresh-btn testimonial-ablehnen-btn">Ablehnen</button>` : ""}
|
||||
<button type="button" class="vw-refresh-btn testimonial-loeschen-btn" style="color:#ff8a8a;">Löschen</button>
|
||||
</div>
|
||||
</div>
|
||||
<p style="margin:.8rem 0 0;">${escapeHtml(t.nachricht)}</p>
|
||||
</div>`;
|
||||
}
|
||||
|
||||
async function ladeUndRenderTestimonials() {
|
||||
const listeEl = document.getElementById("testimonials-liste");
|
||||
const res = await window.AdminAuth.apiCall(API_BASE_URL, "/admin/testimonials/list", {});
|
||||
if (!res.ok) {
|
||||
listeEl.innerHTML = `<p class="vw-leer">Stimmen konnten nicht geladen werden.</p>`;
|
||||
return;
|
||||
}
|
||||
const liste = res.testimonials || [];
|
||||
listeEl.innerHTML = liste.length ? liste.map(testimonialKarteHtml).join("") : `<p class="vw-leer">Noch keine Einreichungen.</p>`;
|
||||
|
||||
const aktion = async (btn, status) => {
|
||||
const id = btn.closest("[data-testimonial-id]").dataset.testimonialId;
|
||||
btn.disabled = true;
|
||||
await window.AdminAuth.apiCall(API_BASE_URL, "/admin/testimonials/status", { id, status });
|
||||
await ladeUndRenderTestimonials();
|
||||
};
|
||||
listeEl.querySelectorAll(".testimonial-freigeben-btn").forEach((btn) => btn.addEventListener("click", () => aktion(btn, "freigegeben")));
|
||||
listeEl.querySelectorAll(".testimonial-ablehnen-btn").forEach((btn) => btn.addEventListener("click", () => aktion(btn, "abgelehnt")));
|
||||
listeEl.querySelectorAll(".testimonial-loeschen-btn").forEach((btn) => {
|
||||
btn.addEventListener("click", async () => {
|
||||
if (!confirm("Diese Einreichung unwiderruflich löschen?")) return;
|
||||
const id = btn.closest("[data-testimonial-id]").dataset.testimonialId;
|
||||
btn.disabled = true;
|
||||
await window.AdminAuth.apiCall(API_BASE_URL, "/admin/testimonials/delete", { id });
|
||||
await ladeUndRenderTestimonials();
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
// Nutzer-Wunsch 04.08.2026 (6. Runde): zwei getrennte Kisten/Formulare
|
||||
// (Dogi + VanVan) statt einem gemeinsamen Feld — beide rufen denselben
|
||||
// Login-Endpunkt auf, die Fehlermeldung erscheint jeweils nur in der
|
||||
|
||||
+6
-6
@@ -19,10 +19,10 @@
|
||||
<meta name="twitter:description" content="Die Werte, nach denen DogFather als Manager bei Spicy Media arbeitet." />
|
||||
<meta name="twitter:image" content="https://dogfather-universe.com/assets/img/og-cover.jpg" />
|
||||
|
||||
<link rel="stylesheet" href="assets/css/main.css?v=20260820j" />
|
||||
<link rel="stylesheet" href="assets/css/theme-dogfather.css?v=20260820j" />
|
||||
<link rel="stylesheet" href="assets/css/theme-hasidog.css?v=20260820j" />
|
||||
<link rel="stylesheet" href="assets/css/theme-spicymedia.css?v=20260820j" />
|
||||
<link rel="stylesheet" href="assets/css/main.css?v=20260820k" />
|
||||
<link rel="stylesheet" href="assets/css/theme-dogfather.css?v=20260820k" />
|
||||
<link rel="stylesheet" href="assets/css/theme-hasidog.css?v=20260820k" />
|
||||
<link rel="stylesheet" href="assets/css/theme-spicymedia.css?v=20260820k" />
|
||||
</head>
|
||||
<body class="mit-hintergrund" style="--page-bg:url('/assets/img/bg-werte.jpg');--page-bg-mobile:url('/assets/img/bg-werte-mobile.jpg');">
|
||||
<div id="site-header"></div>
|
||||
@@ -113,7 +113,7 @@
|
||||
</main>
|
||||
|
||||
<div id="site-footer"></div>
|
||||
<script src="assets/js/i18n-werte.js?v=20260820j"></script>
|
||||
<script src="assets/js/main.js?v=20260820j"></script>
|
||||
<script src="assets/js/i18n-werte.js?v=20260820k"></script>
|
||||
<script src="assets/js/main.js?v=20260820k"></script>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
+6
-6
@@ -19,10 +19,10 @@
|
||||
<meta name="twitter:description" content="Die Geschichte von DogFather in einer interaktiven Zeitleiste." />
|
||||
<meta name="twitter:image" content="https://dogfather-universe.com/assets/img/og-cover.jpg" />
|
||||
|
||||
<link rel="stylesheet" href="assets/css/main.css?v=20260820j" />
|
||||
<link rel="stylesheet" href="assets/css/theme-dogfather.css?v=20260820j" />
|
||||
<link rel="stylesheet" href="assets/css/theme-hasidog.css?v=20260820j" />
|
||||
<link rel="stylesheet" href="assets/css/theme-spicymedia.css?v=20260820j" />
|
||||
<link rel="stylesheet" href="assets/css/main.css?v=20260820k" />
|
||||
<link rel="stylesheet" href="assets/css/theme-dogfather.css?v=20260820k" />
|
||||
<link rel="stylesheet" href="assets/css/theme-hasidog.css?v=20260820k" />
|
||||
<link rel="stylesheet" href="assets/css/theme-spicymedia.css?v=20260820k" />
|
||||
</head>
|
||||
<body class="mit-hintergrund" style="--page-bg:url('/assets/img/bg-zeitreise.jpg');--page-bg-mobile:url('/assets/img/bg-zeitreise-mobile.jpg');">
|
||||
<div id="site-header"></div>
|
||||
@@ -219,8 +219,8 @@
|
||||
</main>
|
||||
|
||||
<div id="site-footer"></div>
|
||||
<script src="assets/js/i18n-zeitreise.js?v=20260820j"></script>
|
||||
<script src="assets/js/main.js?v=20260820j"></script>
|
||||
<script src="assets/js/i18n-zeitreise.js?v=20260820k"></script>
|
||||
<script src="assets/js/main.js?v=20260820k"></script>
|
||||
<script>
|
||||
/* Wachstums-Reveal: Äste blenden sich beim Scrollen sanft ein — passt
|
||||
zum "wachsenden Baum"-Konzept dieser Seite. Rein page-lokal, betrifft
|
||||
|
||||
Reference in New Issue
Block a user