Verwaltungsseite perfektioniert: Team-Verwaltung für Modis/Scouts/Creator/Manager
Nutzer-Wunsch 21.08.2026: "überall wo Modis sind oder Scouts oder Manager, oder Creator, will ich dass ich die easy über meine Verwaltungsseite hinzufüge und die automatisch in der Website hinzugefügt werden ... auch mit Fotos und TikTok Link." Backend (server-internal, braucht Filipes manuellen Deploy): - Neue Tabelle team_members (Migration 0007) für alle vier Kategorien gemeinsam -- ergänzt, überschreibt NIE die von Hand gepflegten Einträge in data-modis.js/data-scouts.js/data-creator.js. - routes/team.js: öffentliches Lesen (nur aktive Mitglieder, optional nach Kategorie gefiltert), TEAM_MANAGE-geschütztes Anlegen/Bearbeiten/ Löschen, Foto-Upload (gleiches Muster wie "Event des Jahres"), automatische Übersetzung von Rolle/Bio/Zitat wie bei anderen admin-gepflegten Texten. Slug global eindeutig (mit automatischer Kollisionsauflösung), TikTok-Kurzname wird zu voller URL ergänzt. 27 Tests in isolierter Testumgebung geprüft (kein better-sqlite3 lokal kompilierbar). Frontend: - window.dogiTeamLaden() in main.js: holt admin-gepflegte Mitglieder einer (oder aller) Kategorien und bringt sie in exakt dieselbe Form wie die bestehenden data-*.js-Einträge. - team-scouts.html/team-modis.html/creator.html hängen das Ergebnis einfach an ihre bestehenden Arrays an und rendern erneut -- exakt derselbe Look wie die schon bestehenden Profile (Foto/Avatar-Initiale, Rolle, Zitat, TikTok-Button). creator.html hatte bisher ein "return" bei leerem CREATORS-Array, wodurch admin-Creator NIE geladen worden wären -- gefixt. team-modis.html berücksichtigt Rang/Rang-Bezeichnung für die Pyramide. - Neue Sektion "Weitere Manager" auf manager.html, komplett unsichtbar bis der erste Manager angelegt wird (data-manager.js neu, wie data-creator.js aktuell leer). - profil.html sucht jetzt kategorieübergreifend auch in admin-gepflegten Profilen, inkl. korrektem Theme/Zurück-Link auch für Manager. Verwaltung: neue Sektion "Team verwalten" (TEAM_MANAGE-Berechtigung) -- ein Formular für alle vier Kategorien mit Foto-Sofort-Upload, TikTok-Feld, Bio/Zitat, bedingten Rang-Feldern (nur Modi), Kategorie- Filterleiste und Bearbeiten/Löschen pro Eintrag. Beim Testen mit Playwright einen echten Syntaxfehler gefunden und gefixt (ASCII-Anführungszeichen statt schließendem „" in einem Statustext), der das GESAMTE Verwaltungs-Skript und damit die komplette Seite lahmgelegt hätte. Cache-Busting-Version auf 20260821j erhöht. Co-Authored-By: Claude Sonnet 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="twitter:image" content="https://dogfather-universe.com/assets/img/og-cover.jpg" />
|
||||||
<meta name="robots" content="noindex" />
|
<meta name="robots" content="noindex" />
|
||||||
<title>Seite nicht gefunden — DOGFATHER UNIVERSE</title>
|
<title>Seite nicht gefunden — DOGFATHER UNIVERSE</title>
|
||||||
<link rel="stylesheet" href="assets/css/main.css?v=20260821i" />
|
<link rel="stylesheet" href="assets/css/main.css?v=20260821j" />
|
||||||
<link rel="stylesheet" href="assets/css/theme-dogfather.css?v=20260821i" />
|
<link rel="stylesheet" href="assets/css/theme-dogfather.css?v=20260821j" />
|
||||||
<link rel="stylesheet" href="assets/css/theme-hasidog.css?v=20260821i" />
|
<link rel="stylesheet" href="assets/css/theme-hasidog.css?v=20260821j" />
|
||||||
<link rel="stylesheet" href="assets/css/theme-spicymedia.css?v=20260821i" />
|
<link rel="stylesheet" href="assets/css/theme-spicymedia.css?v=20260821j" />
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<div id="site-header"></div>
|
<div id="site-header"></div>
|
||||||
@@ -41,7 +41,7 @@
|
|||||||
</main>
|
</main>
|
||||||
|
|
||||||
<div id="site-footer"></div>
|
<div id="site-footer"></div>
|
||||||
<script src="assets/js/i18n-404.js?v=20260821i"></script>
|
<script src="assets/js/i18n-404.js?v=20260821j"></script>
|
||||||
<script src="assets/js/main.js?v=20260821i"></script>
|
<script src="assets/js/main.js?v=20260821j"></script>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</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: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" />
|
<meta name="twitter:image" content="https://dogfather-universe.com/assets/img/og-cover.jpg" />
|
||||||
|
|
||||||
<link rel="stylesheet" href="assets/css/main.css?v=20260821i" />
|
<link rel="stylesheet" href="assets/css/main.css?v=20260821j" />
|
||||||
<link rel="stylesheet" href="assets/css/theme-dogfather.css?v=20260821i" />
|
<link rel="stylesheet" href="assets/css/theme-dogfather.css?v=20260821j" />
|
||||||
<link rel="stylesheet" href="assets/css/theme-hasidog.css?v=20260821i" />
|
<link rel="stylesheet" href="assets/css/theme-hasidog.css?v=20260821j" />
|
||||||
<link rel="stylesheet" href="assets/css/theme-spicymedia.css?v=20260821i" />
|
<link rel="stylesheet" href="assets/css/theme-spicymedia.css?v=20260821j" />
|
||||||
<style>
|
<style>
|
||||||
/* =====================================================================
|
/* =====================================================================
|
||||||
Preis-/Anmelde-Kiste — komplett neu gestaltet (Nutzer-Feedback
|
Preis-/Anmelde-Kiste — komplett neu gestaltet (Nutzer-Feedback
|
||||||
@@ -454,9 +454,9 @@
|
|||||||
</main>
|
</main>
|
||||||
|
|
||||||
<div id="site-footer"></div>
|
<div id="site-footer"></div>
|
||||||
<script src="assets/js/i18n-abonnieren.js?v=20260821i"></script>
|
<script src="assets/js/i18n-abonnieren.js?v=20260821j"></script>
|
||||||
<script src="assets/js/supporter.js?v=20260821i"></script>
|
<script src="assets/js/supporter.js?v=20260821j"></script>
|
||||||
<script src="assets/js/main.js?v=20260821i"></script>
|
<script src="assets/js/main.js?v=20260821j"></script>
|
||||||
<script>
|
<script>
|
||||||
(function () {
|
(function () {
|
||||||
const S = window.DogiSupporter;
|
const S = window.DogiSupporter;
|
||||||
|
|||||||
@@ -20,10 +20,10 @@
|
|||||||
<meta name="twitter:image" content="https://dogfather-universe.com/assets/img/og-cover.jpg" />
|
<meta name="twitter:image" content="https://dogfather-universe.com/assets/img/og-cover.jpg" />
|
||||||
<meta name="robots" content="noindex" />
|
<meta name="robots" content="noindex" />
|
||||||
|
|
||||||
<link rel="stylesheet" href="assets/css/main.css?v=20260821i" />
|
<link rel="stylesheet" href="assets/css/main.css?v=20260821j" />
|
||||||
<link rel="stylesheet" href="assets/css/theme-dogfather.css?v=20260821i" />
|
<link rel="stylesheet" href="assets/css/theme-dogfather.css?v=20260821j" />
|
||||||
<link rel="stylesheet" href="assets/css/theme-hasidog.css?v=20260821i" />
|
<link rel="stylesheet" href="assets/css/theme-hasidog.css?v=20260821j" />
|
||||||
<link rel="stylesheet" href="assets/css/theme-spicymedia.css?v=20260821i" />
|
<link rel="stylesheet" href="assets/css/theme-spicymedia.css?v=20260821j" />
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<div id="site-header"></div>
|
<div id="site-header"></div>
|
||||||
@@ -134,6 +134,6 @@
|
|||||||
</main>
|
</main>
|
||||||
|
|
||||||
<div id="site-footer"></div>
|
<div id="site-footer"></div>
|
||||||
<script src="assets/js/main.js?v=20260821i"></script>
|
<script src="assets/js/main.js?v=20260821j"></script>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|||||||
+7
-7
@@ -19,10 +19,10 @@
|
|||||||
<meta name="twitter:description" content="Detailansicht einer Aktion oder eines Projekts von Team Dogi." />
|
<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" />
|
<meta name="twitter:image" content="https://dogfather-universe.com/assets/img/og-cover.jpg" />
|
||||||
|
|
||||||
<link rel="stylesheet" href="assets/css/main.css?v=20260821i" />
|
<link rel="stylesheet" href="assets/css/main.css?v=20260821j" />
|
||||||
<link rel="stylesheet" href="assets/css/theme-dogfather.css?v=20260821i" />
|
<link rel="stylesheet" href="assets/css/theme-dogfather.css?v=20260821j" />
|
||||||
<link rel="stylesheet" href="assets/css/theme-hasidog.css?v=20260821i" />
|
<link rel="stylesheet" href="assets/css/theme-hasidog.css?v=20260821j" />
|
||||||
<link rel="stylesheet" href="assets/css/theme-spicymedia.css?v=20260821i" />
|
<link rel="stylesheet" href="assets/css/theme-spicymedia.css?v=20260821j" />
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<div id="site-header"></div>
|
<div id="site-header"></div>
|
||||||
@@ -51,9 +51,9 @@
|
|||||||
</main>
|
</main>
|
||||||
|
|
||||||
<div id="site-footer"></div>
|
<div id="site-footer"></div>
|
||||||
<script src="assets/js/data-aktionen.js?v=20260821i"></script>
|
<script src="assets/js/data-aktionen.js?v=20260821j"></script>
|
||||||
<script src="assets/js/i18n-aktion-detail.js?v=20260821i"></script>
|
<script src="assets/js/i18n-aktion-detail.js?v=20260821j"></script>
|
||||||
<script src="assets/js/main.js?v=20260821i"></script>
|
<script src="assets/js/main.js?v=20260821j"></script>
|
||||||
<script>
|
<script>
|
||||||
document.addEventListener("DOMContentLoaded", () => {
|
document.addEventListener("DOMContentLoaded", () => {
|
||||||
const params = new URLSearchParams(location.search);
|
const params = new URLSearchParams(location.search);
|
||||||
|
|||||||
@@ -0,0 +1,18 @@
|
|||||||
|
/* =====================================================================
|
||||||
|
data-manager.js — Manager-Datenobjekt (21.08.2026)
|
||||||
|
|
||||||
|
DogFather (Filipe) selbst hat weiterhin seine eigene, feste Seite
|
||||||
|
(manager.html) — dieses Array ist für WEITERE Manager gedacht, die
|
||||||
|
künftig dazukommen könnten (siehe zeitreise.html, "Aufbau eines
|
||||||
|
eigenen Creator-Systems": "Eine langfristige Struktur für Creator,
|
||||||
|
Scouts und zukünftige Manager"). Noch niemand final geklärt, deshalb
|
||||||
|
bewusst leer — genau wie data-creator.js, bis echte Namen feststehen.
|
||||||
|
Einfach nach dem Muster in data-scouts.js Objekte ins Array einfügen,
|
||||||
|
sobald es so weit ist — manager.html liest automatisch aus.
|
||||||
|
|
||||||
|
Felder: siehe data-scouts.js (identisches Muster).
|
||||||
|
===================================================================== */
|
||||||
|
|
||||||
|
const MANAGERS = [];
|
||||||
|
|
||||||
|
if (typeof window !== "undefined") window.MANAGERS = MANAGERS;
|
||||||
@@ -39,6 +39,13 @@ window.I18N_PAGE = {
|
|||||||
de: "Profil ansehen", "de-CH": "Profil aluege",
|
de: "Profil ansehen", "de-CH": "Profil aluege",
|
||||||
en: "View profile", fr: "Voir le profil", pt: "Ver perfil",
|
en: "View profile", fr: "Voir le profil", pt: "Ver perfil",
|
||||||
},
|
},
|
||||||
|
/* 21.08.2026: Fallback-Kategorie für über die Verwaltung angelegte
|
||||||
|
Creator ohne eigenes Genre (die Verwaltung fragt aktuell keine
|
||||||
|
Genre-Kategorie ab) -- verhindert einen "undefined"-Filterknopf. */
|
||||||
|
crt_kategorie_sonstige: {
|
||||||
|
de: "Sonstige", "de-CH": "Anders",
|
||||||
|
en: "Other", fr: "Autre", pt: "Outro",
|
||||||
|
},
|
||||||
crt_bewerben_btn: {
|
crt_bewerben_btn: {
|
||||||
de: "Als Creator bewerben", "de-CH": "Als Creator bewärbe",
|
de: "Als Creator bewerben", "de-CH": "Als Creator bewärbe",
|
||||||
en: "Apply as a creator", fr: "Postuler comme créateur", pt: "Candidatar-se como creator",
|
en: "Apply as a creator", fr: "Postuler comme créateur", pt: "Candidatar-se como creator",
|
||||||
|
|||||||
@@ -177,4 +177,16 @@ window.I18N_PAGE = {
|
|||||||
fr: "Les scouts trouvent et accompagnent de nouveaux talents pour Dogi.",
|
fr: "Les scouts trouvent et accompagnent de nouveaux talents pour Dogi.",
|
||||||
pt: "Os scouts encontram e acompanham novos talentos para o Dogi.",
|
pt: "Os scouts encontram e acompanham novos talentos para o Dogi.",
|
||||||
},
|
},
|
||||||
|
/* 21.08.2026: Abschnitt für weitere, über die Verwaltung angelegte Manager
|
||||||
|
(siehe zeitreise.html, "Aufbau eines eigenen Creator-Systems" ->
|
||||||
|
"zukünftige Manager"). Bleibt komplett unsichtbar, solange keiner
|
||||||
|
eingetragen ist -- siehe renderWeitereManager() weiter unten. */
|
||||||
|
mgr_weitere_h2: {
|
||||||
|
de: "Weitere Manager", "de-CH": "Witeri Manager",
|
||||||
|
en: "More Managers", fr: "Autres managers", pt: "Outros managers",
|
||||||
|
},
|
||||||
|
mgr_weitere_profil_btn: {
|
||||||
|
de: "Profil ansehen", "de-CH": "Profil aluege",
|
||||||
|
en: "View profile", fr: "Voir le profil", pt: "Ver perfil",
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -30,6 +30,10 @@ window.I18N_PAGE = {
|
|||||||
de: "Manager DogFather — Creator", "de-CH": "Manager DogFather — Creator",
|
de: "Manager DogFather — Creator", "de-CH": "Manager DogFather — Creator",
|
||||||
en: "Manager DogFather — Creator", fr: "Manager DogFather — Creator", pt: "Manager DogFather — Creator",
|
en: "Manager DogFather — Creator", fr: "Manager DogFather — Creator", pt: "Manager DogFather — Creator",
|
||||||
},
|
},
|
||||||
|
pr_eyebrow_manager: {
|
||||||
|
de: "Manager DogFather — Spicy Media", "de-CH": "Manager DogFather — Spicy Media",
|
||||||
|
en: "Manager DogFather — Spicy Media", fr: "Manager DogFather — Spicy Media", pt: "Manager DogFather — Spicy Media",
|
||||||
|
},
|
||||||
pr_back_modis: {
|
pr_back_modis: {
|
||||||
de: "← Zurück zum Modi-Team", "de-CH": "← Zrugg zum Modi-Team",
|
de: "← Zurück zum Modi-Team", "de-CH": "← Zrugg zum Modi-Team",
|
||||||
en: "← Back to the Modi Team", fr: "← Retour à l'équipe Modi", pt: "← Voltar à equipe Modi",
|
en: "← Back to the Modi Team", fr: "← Retour à l'équipe Modi", pt: "← Voltar à equipe Modi",
|
||||||
@@ -42,6 +46,10 @@ window.I18N_PAGE = {
|
|||||||
de: "← Zurück zu den Creatorn", "de-CH": "← Zrugg zu de Creator",
|
de: "← Zurück zu den Creatorn", "de-CH": "← Zrugg zu de Creator",
|
||||||
en: "← Back to the Creators", fr: "← Retour aux Creators", pt: "← Voltar aos Creators",
|
en: "← Back to the Creators", fr: "← Retour aux Creators", pt: "← Voltar aos Creators",
|
||||||
},
|
},
|
||||||
|
pr_back_manager: {
|
||||||
|
de: "← Zurück zu Manager DogFather", "de-CH": "← Zrugg zu Manager DogFather",
|
||||||
|
en: "← Back to Manager DogFather", fr: "← Retour à Manager DogFather", pt: "← Voltar a Manager DogFather",
|
||||||
|
},
|
||||||
pr_badge_prefix: {
|
pr_badge_prefix: {
|
||||||
de: "Stufe", "de-CH": "Stuefe",
|
de: "Stufe", "de-CH": "Stuefe",
|
||||||
en: "Level", fr: "Niveau", pt: "Nível",
|
en: "Level", fr: "Niveau", pt: "Nível",
|
||||||
|
|||||||
@@ -1154,6 +1154,57 @@
|
|||||||
return feld[sprache] || feld.de || "";
|
return feld[sprache] || feld.de || "";
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/* =====================================================================
|
||||||
|
dogiTeamLaden() — 21.08.2026, Nutzer-Wunsch: "überall wo Modis sind
|
||||||
|
oder Scouts oder Manager, oder Creator, will ich dass ich die easy
|
||||||
|
über meine Verwaltungsseite hinzufüge und die automatisch in der
|
||||||
|
Website hinzugefügt werden ... auch mit Fotos und TikTok Link."
|
||||||
|
|
||||||
|
Holt die über die Verwaltung angelegten Team-Mitglieder einer
|
||||||
|
Kategorie ("modi" | "scout" | "creator" | "manager") vom Server und
|
||||||
|
bringt sie in GENAU dieselbe Objektform wie die von Hand gepflegten
|
||||||
|
Einträge in data-modis.js/data-scouts.js/data-creator.js/
|
||||||
|
data-manager.js (name/slug/role/photo/bio/quote/social/...) — die
|
||||||
|
aufrufende Seite hängt das Ergebnis einfach hinten an ihr bestehendes
|
||||||
|
window.MODIS/SCOUTS/CREATORS/MANAGERS-Array an und ruft ihre eigene,
|
||||||
|
unveränderte Render-Funktion erneut auf. Kein kategorie-Parameter
|
||||||
|
liefert ALLE Kategorien zusammen (genutzt von profil.html, das vorab
|
||||||
|
nicht weiß, zu welcher Kategorie ein slug gehört).
|
||||||
|
|
||||||
|
Schlägt die Anfrage fehl (Server nicht erreichbar, Netz weg): leeres
|
||||||
|
Array statt Fehler -- die von Hand gepflegten Einträge bleiben davon
|
||||||
|
komplett unberührt sichtbar, nur admin-gepflegte kommen dann halt
|
||||||
|
(vorübergehend) nicht hinzu. */
|
||||||
|
const TEAM_API_BASIS = "https://postfach.dogfather-universe.com";
|
||||||
|
window.dogiTeamLaden = async function (kategorie) {
|
||||||
|
try {
|
||||||
|
const url = kategorie ? `${TEAM_API_BASIS}/team?kategorie=${encodeURIComponent(kategorie)}` : `${TEAM_API_BASIS}/team`;
|
||||||
|
const res = await fetch(url, { cache: "no-store" });
|
||||||
|
const data = await res.json();
|
||||||
|
if (!data || !data.ok || !Array.isArray(data.mitglieder)) return [];
|
||||||
|
return data.mitglieder.map((m) => ({
|
||||||
|
name: m.name,
|
||||||
|
slug: m.slug,
|
||||||
|
kategorie: m.kategorie,
|
||||||
|
role: m.rolle || "",
|
||||||
|
photo: m.foto || "",
|
||||||
|
avatar: m.foto || "",
|
||||||
|
bio: m.bio || "",
|
||||||
|
quote: m.zitat || "",
|
||||||
|
social: m.tiktok ? { tiktok: m.tiktok } : {},
|
||||||
|
status: "aktiv", // /team liefert ohnehin nur aktive Mitglieder
|
||||||
|
rank: m.rang || undefined,
|
||||||
|
rankLabel: m.rangLabel || undefined,
|
||||||
|
// Markiert admin-gepflegte Einträge (z.B. für profil.html, das
|
||||||
|
// sonst nicht zwischen fest einprogrammierten und dynamischen
|
||||||
|
// Personen unterscheiden könnte).
|
||||||
|
istAdminGepflegt: true,
|
||||||
|
}));
|
||||||
|
} catch {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
/* ===================================================================
|
/* ===================================================================
|
||||||
PWA: Service-Worker-Registrierung
|
PWA: Service-Worker-Registrierung
|
||||||
Voraussetzung dafür, dass Chrome/Edge auf dem Desktop "App
|
Voraussetzung dafür, dass Chrome/Edge auf dem Desktop "App
|
||||||
|
|||||||
@@ -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: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" />
|
<meta name="twitter:image" content="https://dogfather-universe.com/assets/img/og-cover.jpg" />
|
||||||
|
|
||||||
<link rel="stylesheet" href="assets/css/main.css?v=20260821i" />
|
<link rel="stylesheet" href="assets/css/main.css?v=20260821j" />
|
||||||
<link rel="stylesheet" href="assets/css/theme-dogfather.css?v=20260821i" />
|
<link rel="stylesheet" href="assets/css/theme-dogfather.css?v=20260821j" />
|
||||||
<link rel="stylesheet" href="assets/css/theme-hasidog.css?v=20260821i" />
|
<link rel="stylesheet" href="assets/css/theme-hasidog.css?v=20260821j" />
|
||||||
<link rel="stylesheet" href="assets/css/theme-spicymedia.css?v=20260821i" />
|
<link rel="stylesheet" href="assets/css/theme-spicymedia.css?v=20260821j" />
|
||||||
</head>
|
</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');">
|
<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>
|
<div id="site-header"></div>
|
||||||
@@ -70,8 +70,8 @@
|
|||||||
</main>
|
</main>
|
||||||
|
|
||||||
<div id="site-footer"></div>
|
<div id="site-footer"></div>
|
||||||
<script src="assets/js/forms.js?v=20260821i"></script>
|
<script src="assets/js/forms.js?v=20260821j"></script>
|
||||||
<script src="assets/js/i18n-bewerben-creator.js?v=20260821i"></script>
|
<script src="assets/js/i18n-bewerben-creator.js?v=20260821j"></script>
|
||||||
<script src="assets/js/main.js?v=20260821i"></script>
|
<script src="assets/js/main.js?v=20260821j"></script>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|||||||
@@ -22,10 +22,10 @@
|
|||||||
<meta name="twitter:description" content="Kooperationsanfrage an Manager DogFather / Spicy Media." />
|
<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" />
|
<meta name="twitter:image" content="https://dogfather-universe.com/assets/img/og-cover.jpg" />
|
||||||
|
|
||||||
<link rel="stylesheet" href="assets/css/main.css?v=20260821i" />
|
<link rel="stylesheet" href="assets/css/main.css?v=20260821j" />
|
||||||
<link rel="stylesheet" href="assets/css/theme-dogfather.css?v=20260821i" />
|
<link rel="stylesheet" href="assets/css/theme-dogfather.css?v=20260821j" />
|
||||||
<link rel="stylesheet" href="assets/css/theme-hasidog.css?v=20260821i" />
|
<link rel="stylesheet" href="assets/css/theme-hasidog.css?v=20260821j" />
|
||||||
<link rel="stylesheet" href="assets/css/theme-spicymedia.css?v=20260821i" />
|
<link rel="stylesheet" href="assets/css/theme-spicymedia.css?v=20260821j" />
|
||||||
</head>
|
</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');">
|
<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>
|
<div id="site-header"></div>
|
||||||
@@ -68,8 +68,8 @@
|
|||||||
</main>
|
</main>
|
||||||
|
|
||||||
<div id="site-footer"></div>
|
<div id="site-footer"></div>
|
||||||
<script src="assets/js/forms.js?v=20260821i"></script>
|
<script src="assets/js/forms.js?v=20260821j"></script>
|
||||||
<script src="assets/js/i18n-bewerben-kooperation.js?v=20260821i"></script>
|
<script src="assets/js/i18n-bewerben-kooperation.js?v=20260821j"></script>
|
||||||
<script src="assets/js/main.js?v=20260821i"></script>
|
<script src="assets/js/main.js?v=20260821j"></script>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</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: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" />
|
<meta name="twitter:image" content="https://dogfather-universe.com/assets/img/og-cover.jpg" />
|
||||||
|
|
||||||
<link rel="stylesheet" href="assets/css/main.css?v=20260821i" />
|
<link rel="stylesheet" href="assets/css/main.css?v=20260821j" />
|
||||||
<link rel="stylesheet" href="assets/css/theme-dogfather.css?v=20260821i" />
|
<link rel="stylesheet" href="assets/css/theme-dogfather.css?v=20260821j" />
|
||||||
<link rel="stylesheet" href="assets/css/theme-hasidog.css?v=20260821i" />
|
<link rel="stylesheet" href="assets/css/theme-hasidog.css?v=20260821j" />
|
||||||
<link rel="stylesheet" href="assets/css/theme-spicymedia.css?v=20260821i" />
|
<link rel="stylesheet" href="assets/css/theme-spicymedia.css?v=20260821j" />
|
||||||
</head>
|
</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');">
|
<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>
|
<div id="site-header"></div>
|
||||||
@@ -79,8 +79,8 @@
|
|||||||
</main>
|
</main>
|
||||||
|
|
||||||
<div id="site-footer"></div>
|
<div id="site-footer"></div>
|
||||||
<script src="assets/js/forms.js?v=20260821i"></script>
|
<script src="assets/js/forms.js?v=20260821j"></script>
|
||||||
<script src="assets/js/i18n-bewerben-modi.js?v=20260821i"></script>
|
<script src="assets/js/i18n-bewerben-modi.js?v=20260821j"></script>
|
||||||
<script src="assets/js/main.js?v=20260821i"></script>
|
<script src="assets/js/main.js?v=20260821j"></script>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</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: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" />
|
<meta name="twitter:image" content="https://dogfather-universe.com/assets/img/og-cover.jpg" />
|
||||||
|
|
||||||
<link rel="stylesheet" href="assets/css/main.css?v=20260821i" />
|
<link rel="stylesheet" href="assets/css/main.css?v=20260821j" />
|
||||||
<link rel="stylesheet" href="assets/css/theme-dogfather.css?v=20260821i" />
|
<link rel="stylesheet" href="assets/css/theme-dogfather.css?v=20260821j" />
|
||||||
<link rel="stylesheet" href="assets/css/theme-hasidog.css?v=20260821i" />
|
<link rel="stylesheet" href="assets/css/theme-hasidog.css?v=20260821j" />
|
||||||
<link rel="stylesheet" href="assets/css/theme-spicymedia.css?v=20260821i" />
|
<link rel="stylesheet" href="assets/css/theme-spicymedia.css?v=20260821j" />
|
||||||
</head>
|
</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');">
|
<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>
|
<div id="site-header"></div>
|
||||||
@@ -71,8 +71,8 @@
|
|||||||
</main>
|
</main>
|
||||||
|
|
||||||
<div id="site-footer"></div>
|
<div id="site-footer"></div>
|
||||||
<script src="assets/js/forms.js?v=20260821i"></script>
|
<script src="assets/js/forms.js?v=20260821j"></script>
|
||||||
<script src="assets/js/i18n-bewerben-scout.js?v=20260821i"></script>
|
<script src="assets/js/i18n-bewerben-scout.js?v=20260821j"></script>
|
||||||
<script src="assets/js/main.js?v=20260821i"></script>
|
<script src="assets/js/main.js?v=20260821j"></script>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</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: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" />
|
<meta name="twitter:image" content="https://dogfather-universe.com/assets/img/og-cover.jpg" />
|
||||||
|
|
||||||
<link rel="stylesheet" href="assets/css/main.css?v=20260821i" />
|
<link rel="stylesheet" href="assets/css/main.css?v=20260821j" />
|
||||||
<link rel="stylesheet" href="assets/css/theme-dogfather.css?v=20260821i" />
|
<link rel="stylesheet" href="assets/css/theme-dogfather.css?v=20260821j" />
|
||||||
<link rel="stylesheet" href="assets/css/theme-hasidog.css?v=20260821i" />
|
<link rel="stylesheet" href="assets/css/theme-hasidog.css?v=20260821j" />
|
||||||
<link rel="stylesheet" href="assets/css/theme-spicymedia.css?v=20260821i" />
|
<link rel="stylesheet" href="assets/css/theme-spicymedia.css?v=20260821j" />
|
||||||
</head>
|
</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');">
|
<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>
|
<div id="site-header"></div>
|
||||||
@@ -85,7 +85,7 @@
|
|||||||
</main>
|
</main>
|
||||||
|
|
||||||
<div id="site-footer"></div>
|
<div id="site-footer"></div>
|
||||||
<script src="assets/js/i18n-bewerben.js?v=20260821i"></script>
|
<script src="assets/js/i18n-bewerben.js?v=20260821j"></script>
|
||||||
<script src="assets/js/main.js?v=20260821i"></script>
|
<script src="assets/js/main.js?v=20260821j"></script>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</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: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" />
|
<meta name="twitter:image" content="https://dogfather-universe.com/assets/img/og-cover.jpg" />
|
||||||
|
|
||||||
<link rel="stylesheet" href="assets/css/main.css?v=20260821i" />
|
<link rel="stylesheet" href="assets/css/main.css?v=20260821j" />
|
||||||
<link rel="stylesheet" href="assets/css/theme-dogfather.css?v=20260821i" />
|
<link rel="stylesheet" href="assets/css/theme-dogfather.css?v=20260821j" />
|
||||||
<link rel="stylesheet" href="assets/css/theme-hasidog.css?v=20260821i" />
|
<link rel="stylesheet" href="assets/css/theme-hasidog.css?v=20260821j" />
|
||||||
<link rel="stylesheet" href="assets/css/theme-spicymedia.css?v=20260821i" />
|
<link rel="stylesheet" href="assets/css/theme-spicymedia.css?v=20260821j" />
|
||||||
</head>
|
</head>
|
||||||
<body class="mit-hintergrund" style="--page-bg:url('/assets/img/bg-casper.jpg');--page-bg-mobile:url('/assets/img/bg-casper-mobile.jpg');">
|
<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>
|
<div id="site-header"></div>
|
||||||
@@ -85,7 +85,7 @@
|
|||||||
</main>
|
</main>
|
||||||
|
|
||||||
<div id="site-footer"></div>
|
<div id="site-footer"></div>
|
||||||
<script src="assets/js/i18n-casper.js?v=20260821i"></script>
|
<script src="assets/js/i18n-casper.js?v=20260821j"></script>
|
||||||
<script src="assets/js/main.js?v=20260821i"></script>
|
<script src="assets/js/main.js?v=20260821j"></script>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|||||||
@@ -0,0 +1,44 @@
|
|||||||
|
-- =====================================================================
|
||||||
|
-- Team-Mitglieder (21.08.2026) — Nutzer-Wunsch: "überall wo Modis sind
|
||||||
|
-- oder Scouts oder Manager, oder Creator, will ich dass ich die easy über
|
||||||
|
-- meine Verwaltungsseite hinzufüge und die automatisch in der Website
|
||||||
|
-- hinzugefügt werden ... auch mit Fotos und TikTok Link."
|
||||||
|
--
|
||||||
|
-- Eine gemeinsame Tabelle für alle vier Kategorien (statt vier eigenen),
|
||||||
|
-- weil sie strukturell identisch sind (Name/Rolle/Foto/TikTok/Bio/Zitat) --
|
||||||
|
-- "kategorie" unterscheidet, wo jemand öffentlich erscheint. rang/
|
||||||
|
-- rang_label sind nur für Modis relevant (Pyramiden-Hierarchie auf
|
||||||
|
-- team-modis.html), bei den anderen Kategorien bleiben sie einfach leer.
|
||||||
|
--
|
||||||
|
-- Ergänzt die weiterhin bestehenden, von Hand gepflegten Einträge in
|
||||||
|
-- assets/js/data-modis.js/data-scouts.js/data-creator.js/data-manager.js
|
||||||
|
-- (z.B. VanVan, Patrick, Bananenstift) -- die Website mischt beide Quellen
|
||||||
|
-- zu einer gemeinsamen Liste (siehe window.dogiTeamLaden() in main.js).
|
||||||
|
-- Ein Fehler bei einer Kategorie kann so nie die schon bestehenden,
|
||||||
|
-- fest einprogrammierten Profile verschwinden lassen.
|
||||||
|
--
|
||||||
|
-- slug ist GLOBAL eindeutig (nicht nur pro Kategorie), weil profil.html
|
||||||
|
-- über alle Kategorien hinweg nach genau einem slug sucht.
|
||||||
|
-- =====================================================================
|
||||||
|
|
||||||
|
CREATE TABLE IF NOT EXISTS team_members (
|
||||||
|
id TEXT PRIMARY KEY,
|
||||||
|
kategorie TEXT NOT NULL, -- modi | scout | creator | manager
|
||||||
|
name TEXT NOT NULL,
|
||||||
|
slug TEXT NOT NULL UNIQUE,
|
||||||
|
rolle_json TEXT NOT NULL, -- {de, "de-CH", en, fr, pt}
|
||||||
|
foto_url TEXT,
|
||||||
|
tiktok_url TEXT,
|
||||||
|
bio_json TEXT, -- {de, "de-CH", en, fr, pt} oder NULL
|
||||||
|
zitat_json TEXT, -- {de, "de-CH", en, fr, pt} oder NULL
|
||||||
|
rang INTEGER, -- nur Modi-Pyramide, sonst NULL
|
||||||
|
rang_label_json TEXT, -- nur Modi-Pyramide, sonst NULL
|
||||||
|
reihenfolge INTEGER NOT NULL DEFAULT 0,
|
||||||
|
status TEXT NOT NULL DEFAULT 'aktiv', -- aktiv | pausiert
|
||||||
|
erstellt_am TEXT NOT NULL,
|
||||||
|
geaendert_am TEXT NOT NULL,
|
||||||
|
erstellt_von TEXT,
|
||||||
|
geaendert_von TEXT
|
||||||
|
);
|
||||||
|
|
||||||
|
CREATE INDEX IF NOT EXISTS idx_team_members_kategorie ON team_members (kategorie, status, reihenfolge);
|
||||||
+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: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" />
|
<meta name="twitter:image" content="https://dogfather-universe.com/assets/img/og-cover.jpg" />
|
||||||
|
|
||||||
<link rel="stylesheet" href="assets/css/main.css?v=20260821i" />
|
<link rel="stylesheet" href="assets/css/main.css?v=20260821j" />
|
||||||
<link rel="stylesheet" href="assets/css/theme-dogfather.css?v=20260821i" />
|
<link rel="stylesheet" href="assets/css/theme-dogfather.css?v=20260821j" />
|
||||||
<link rel="stylesheet" href="assets/css/theme-hasidog.css?v=20260821i" />
|
<link rel="stylesheet" href="assets/css/theme-hasidog.css?v=20260821j" />
|
||||||
<link rel="stylesheet" href="assets/css/theme-spicymedia.css?v=20260821i" />
|
<link rel="stylesheet" href="assets/css/theme-spicymedia.css?v=20260821j" />
|
||||||
</head>
|
</head>
|
||||||
<body class="mit-hintergrund" style="--page-bg:url('/assets/img/bg-community.jpg');--page-bg-mobile:url('/assets/img/bg-community-mobile.jpg');">
|
<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>
|
<div id="site-header"></div>
|
||||||
@@ -84,7 +84,7 @@
|
|||||||
</main>
|
</main>
|
||||||
|
|
||||||
<div id="site-footer"></div>
|
<div id="site-footer"></div>
|
||||||
<script src="assets/js/i18n-community.js?v=20260821i"></script>
|
<script src="assets/js/i18n-community.js?v=20260821j"></script>
|
||||||
<script src="assets/js/main.js?v=20260821i"></script>
|
<script src="assets/js/main.js?v=20260821j"></script>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|||||||
+27
-9
@@ -19,10 +19,10 @@
|
|||||||
<meta name="twitter:description" content="Content Creator, die Teil von DogFather / Spicy Media sind." />
|
<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" />
|
<meta name="twitter:image" content="https://dogfather-universe.com/assets/img/og-cover.jpg" />
|
||||||
|
|
||||||
<link rel="stylesheet" href="assets/css/main.css?v=20260821i" />
|
<link rel="stylesheet" href="assets/css/main.css?v=20260821j" />
|
||||||
<link rel="stylesheet" href="assets/css/theme-dogfather.css?v=20260821i" />
|
<link rel="stylesheet" href="assets/css/theme-dogfather.css?v=20260821j" />
|
||||||
<link rel="stylesheet" href="assets/css/theme-hasidog.css?v=20260821i" />
|
<link rel="stylesheet" href="assets/css/theme-hasidog.css?v=20260821j" />
|
||||||
<link rel="stylesheet" href="assets/css/theme-spicymedia.css?v=20260821i" />
|
<link rel="stylesheet" href="assets/css/theme-spicymedia.css?v=20260821j" />
|
||||||
</head>
|
</head>
|
||||||
<body class="mit-hintergrund" style="--page-bg:url('/assets/img/bg-creator.jpg');--page-bg-mobile:url('/assets/img/bg-creator-mobile.jpg');">
|
<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>
|
<div id="site-header"></div>
|
||||||
@@ -49,6 +49,7 @@
|
|||||||
<span data-i18n="crt_empty">Noch keine Creator-Profile veröffentlicht — bald verfügbar. 🩵</span>
|
<span data-i18n="crt_empty">Noch keine Creator-Profile veröffentlicht — bald verfügbar. 🩵</span>
|
||||||
<span data-i18n="crt_filter_alle">alle</span>
|
<span data-i18n="crt_filter_alle">alle</span>
|
||||||
<span data-i18n="crt_profil_btn">Profil ansehen</span>
|
<span data-i18n="crt_profil_btn">Profil ansehen</span>
|
||||||
|
<span data-i18n="crt_kategorie_sonstige">Sonstige</span>
|
||||||
</div>
|
</div>
|
||||||
<!-- TODO: Noch keine Creator-Profile final geklärt. Sobald Namen/Infos feststehen, in assets/js/data-creator.js ergänzen — diese Seite rendert automatisch. -->
|
<!-- TODO: Noch keine Creator-Profile final geklärt. Sobald Namen/Infos feststehen, in assets/js/data-creator.js ergänzen — diese Seite rendert automatisch. -->
|
||||||
<div class="btn-row" style="justify-content:center;">
|
<div class="btn-row" style="justify-content:center;">
|
||||||
@@ -59,14 +60,26 @@
|
|||||||
</main>
|
</main>
|
||||||
|
|
||||||
<div id="site-footer"></div>
|
<div id="site-footer"></div>
|
||||||
<script src="assets/js/data-creator.js?v=20260821i"></script>
|
<script src="assets/js/data-creator.js?v=20260821j"></script>
|
||||||
<script src="assets/js/i18n-creator.js?v=20260821i"></script>
|
<script src="assets/js/i18n-creator.js?v=20260821j"></script>
|
||||||
<script src="assets/js/main.js?v=20260821i"></script>
|
<script src="assets/js/main.js?v=20260821j"></script>
|
||||||
<script>
|
<script>
|
||||||
document.addEventListener("DOMContentLoaded", () => {
|
document.addEventListener("DOMContentLoaded", async () => {
|
||||||
const grid = document.getElementById("creator-grid");
|
const grid = document.getElementById("creator-grid");
|
||||||
const filterBar = document.getElementById("creator-filters");
|
const filterBar = document.getElementById("creator-filters");
|
||||||
const list = window.CREATORS || [];
|
let list = window.CREATORS || [];
|
||||||
|
|
||||||
|
// 21.08.2026: über die Verwaltung angelegte Creator ergänzen, BEVOR
|
||||||
|
// entschieden wird, ob die Seite "leer" ist (Nutzer-Wunsch "überall wo
|
||||||
|
// ... Creator ... sind, will ich dass ich die easy über meine
|
||||||
|
// Verwaltungsseite hinzufüge"). Vorher gab es direkt darunter ein
|
||||||
|
// "return", sobald window.CREATORS leer war (bisher der Normalfall, da
|
||||||
|
// noch keine echten Creator-Profile feststehen) -- dadurch wären
|
||||||
|
// admin-gepflegte Creator NIE geladen worden.
|
||||||
|
if (window.dogiTeamLaden) {
|
||||||
|
const neue = await window.dogiTeamLaden("creator");
|
||||||
|
if (neue.length) list = [...list, ...neue];
|
||||||
|
}
|
||||||
|
|
||||||
// Texte fürs JS-generierte Grid/Filter kommen aus den versteckten
|
// Texte fürs JS-generierte Grid/Filter kommen aus den versteckten
|
||||||
// data-i18n-Elementen oben — main.js übersetzt sie ganz normal (auch bei
|
// data-i18n-Elementen oben — main.js übersetzt sie ganz normal (auch bei
|
||||||
@@ -88,6 +101,11 @@ document.addEventListener("DOMContentLoaded", () => {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 21.08.2026: über die Verwaltung angelegte Creator haben (noch) kein
|
||||||
|
// eigenes Genre-Feld (category) -- ohne diesen Fallback würde die
|
||||||
|
// Filterleiste einen kaputten "undefined"-Knopf zeigen.
|
||||||
|
list = list.map((c) => (c.category ? c : { ...c, category: uebersetzen("crt_kategorie_sonstige") }));
|
||||||
|
|
||||||
const categories = ["alle", ...new Set(list.map((c) => c.category))];
|
const categories = ["alle", ...new Set(list.map((c) => c.category))];
|
||||||
let aktiverFilter = "alle";
|
let aktiverFilter = "alle";
|
||||||
|
|
||||||
|
|||||||
+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: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" />
|
<meta name="twitter:image" content="https://dogfather-universe.com/assets/img/og-cover.jpg" />
|
||||||
|
|
||||||
<link rel="stylesheet" href="assets/css/main.css?v=20260821i" />
|
<link rel="stylesheet" href="assets/css/main.css?v=20260821j" />
|
||||||
<link rel="stylesheet" href="assets/css/theme-dogfather.css?v=20260821i" />
|
<link rel="stylesheet" href="assets/css/theme-dogfather.css?v=20260821j" />
|
||||||
<link rel="stylesheet" href="assets/css/theme-hasidog.css?v=20260821i" />
|
<link rel="stylesheet" href="assets/css/theme-hasidog.css?v=20260821j" />
|
||||||
<link rel="stylesheet" href="assets/css/theme-spicymedia.css?v=20260821i" />
|
<link rel="stylesheet" href="assets/css/theme-spicymedia.css?v=20260821j" />
|
||||||
</head>
|
</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');">
|
<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>
|
<div id="site-header"></div>
|
||||||
@@ -158,7 +158,7 @@
|
|||||||
</main>
|
</main>
|
||||||
|
|
||||||
<div id="site-footer"></div>
|
<div id="site-footer"></div>
|
||||||
<script src="assets/js/i18n-dogfather.js?v=20260821i"></script>
|
<script src="assets/js/i18n-dogfather.js?v=20260821j"></script>
|
||||||
<script src="assets/js/main.js?v=20260821i"></script>
|
<script src="assets/js/main.js?v=20260821j"></script>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|||||||
@@ -26,8 +26,8 @@
|
|||||||
<meta name="apple-mobile-web-app-capable" content="yes" />
|
<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-status-bar-style" content="black-translucent" />
|
||||||
<meta name="apple-mobile-web-app-title" content="DogFather" />
|
<meta name="apple-mobile-web-app-title" content="DogFather" />
|
||||||
<link rel="stylesheet" href="assets/css/main.css?v=20260821i" />
|
<link rel="stylesheet" href="assets/css/main.css?v=20260821j" />
|
||||||
<link rel="stylesheet" href="assets/css/theme-dogfather.css?v=20260821i" />
|
<link rel="stylesheet" href="assets/css/theme-dogfather.css?v=20260821j" />
|
||||||
<style>
|
<style>
|
||||||
/* =====================================================================
|
/* =====================================================================
|
||||||
Zugangsseite — bewusst kein braves Standard-Login-Formular, sondern
|
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: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" />
|
<meta name="twitter:image" content="https://dogfather-universe.com/assets/img/og-cover.jpg" />
|
||||||
|
|
||||||
<link rel="stylesheet" href="assets/css/main.css?v=20260821i" />
|
<link rel="stylesheet" href="assets/css/main.css?v=20260821j" />
|
||||||
<link rel="stylesheet" href="assets/css/theme-dogfather.css?v=20260821i" />
|
<link rel="stylesheet" href="assets/css/theme-dogfather.css?v=20260821j" />
|
||||||
<link rel="stylesheet" href="assets/css/theme-hasidog.css?v=20260821i" />
|
<link rel="stylesheet" href="assets/css/theme-hasidog.css?v=20260821j" />
|
||||||
<link rel="stylesheet" href="assets/css/theme-spicymedia.css?v=20260821i" />
|
<link rel="stylesheet" href="assets/css/theme-spicymedia.css?v=20260821j" />
|
||||||
</head>
|
</head>
|
||||||
<body class="mit-hintergrund" style="--page-bg:url('/assets/img/bg-hasidog.jpg');--page-bg-mobile:url('/assets/img/bg-hasidog-mobile.jpg');">
|
<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>
|
<div id="site-header"></div>
|
||||||
@@ -186,7 +186,7 @@
|
|||||||
</main>
|
</main>
|
||||||
|
|
||||||
<div id="site-footer"></div>
|
<div id="site-footer"></div>
|
||||||
<script src="assets/js/i18n-hasidog.js?v=20260821i"></script>
|
<script src="assets/js/i18n-hasidog.js?v=20260821j"></script>
|
||||||
<script src="assets/js/main.js?v=20260821i"></script>
|
<script src="assets/js/main.js?v=20260821j"></script>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|||||||
+8
-8
@@ -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: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" />
|
<meta name="twitter:image" content="https://dogfather-universe.com/assets/img/og-cover.jpg" />
|
||||||
|
|
||||||
<link rel="stylesheet" href="assets/css/main.css?v=20260821i" />
|
<link rel="stylesheet" href="assets/css/main.css?v=20260821j" />
|
||||||
<link rel="stylesheet" href="assets/css/theme-dogfather.css?v=20260821i" />
|
<link rel="stylesheet" href="assets/css/theme-dogfather.css?v=20260821j" />
|
||||||
<link rel="stylesheet" href="assets/css/theme-hasidog.css?v=20260821i" />
|
<link rel="stylesheet" href="assets/css/theme-hasidog.css?v=20260821j" />
|
||||||
<link rel="stylesheet" href="assets/css/theme-spicymedia.css?v=20260821i" />
|
<link rel="stylesheet" href="assets/css/theme-spicymedia.css?v=20260821j" />
|
||||||
</head>
|
</head>
|
||||||
<body class="start-seite">
|
<body class="start-seite">
|
||||||
<div id="site-header"></div>
|
<div id="site-header"></div>
|
||||||
@@ -287,10 +287,10 @@
|
|||||||
</main>
|
</main>
|
||||||
|
|
||||||
<div id="site-footer"></div>
|
<div id="site-footer"></div>
|
||||||
<script src="assets/js/data-modis.js?v=20260821i"></script>
|
<script src="assets/js/data-modis.js?v=20260821j"></script>
|
||||||
<script src="assets/js/data-scouts.js?v=20260821i"></script>
|
<script src="assets/js/data-scouts.js?v=20260821j"></script>
|
||||||
<script src="assets/js/i18n-index.js?v=20260821i"></script>
|
<script src="assets/js/i18n-index.js?v=20260821j"></script>
|
||||||
<script src="assets/js/main.js?v=20260821i"></script>
|
<script src="assets/js/main.js?v=20260821j"></script>
|
||||||
<script>
|
<script>
|
||||||
// Gleiche URL wie in assets/js/forms.js / postfach.html — läuft seit
|
// Gleiche URL wie in assets/js/forms.js / postfach.html — läuft seit
|
||||||
// 05.08.2026 auf dem eigenen Server statt auf Cloudflare Workers.
|
// 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:description" content="Kontakt, Impressum und Datenschutz von DOGFATHER UNIVERSE." />
|
||||||
<meta name="twitter:image" content="https://dogfather-universe.com/assets/img/og-cover.jpg" />
|
<meta name="twitter:image" content="https://dogfather-universe.com/assets/img/og-cover.jpg" />
|
||||||
|
|
||||||
<link rel="stylesheet" href="assets/css/main.css?v=20260821i" />
|
<link rel="stylesheet" href="assets/css/main.css?v=20260821j" />
|
||||||
<link rel="stylesheet" href="assets/css/theme-dogfather.css?v=20260821i" />
|
<link rel="stylesheet" href="assets/css/theme-dogfather.css?v=20260821j" />
|
||||||
<link rel="stylesheet" href="assets/css/theme-hasidog.css?v=20260821i" />
|
<link rel="stylesheet" href="assets/css/theme-hasidog.css?v=20260821j" />
|
||||||
<link rel="stylesheet" href="assets/css/theme-spicymedia.css?v=20260821i" />
|
<link rel="stylesheet" href="assets/css/theme-spicymedia.css?v=20260821j" />
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<div id="site-header"></div>
|
<div id="site-header"></div>
|
||||||
@@ -126,7 +126,7 @@
|
|||||||
</main>
|
</main>
|
||||||
|
|
||||||
<div id="site-footer"></div>
|
<div id="site-footer"></div>
|
||||||
<script src="assets/js/i18n-kontakt.js?v=20260821i"></script>
|
<script src="assets/js/i18n-kontakt.js?v=20260821j"></script>
|
||||||
<script src="assets/js/main.js?v=20260821i"></script>
|
<script src="assets/js/main.js?v=20260821j"></script>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</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: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" />
|
<meta name="twitter:image" content="https://dogfather-universe.com/assets/img/og-cover.jpg" />
|
||||||
|
|
||||||
<link rel="stylesheet" href="assets/css/main.css?v=20260821i" />
|
<link rel="stylesheet" href="assets/css/main.css?v=20260821j" />
|
||||||
<link rel="stylesheet" href="assets/css/theme-dogfather.css?v=20260821i" />
|
<link rel="stylesheet" href="assets/css/theme-dogfather.css?v=20260821j" />
|
||||||
<link rel="stylesheet" href="assets/css/theme-hasidog.css?v=20260821i" />
|
<link rel="stylesheet" href="assets/css/theme-hasidog.css?v=20260821j" />
|
||||||
<link rel="stylesheet" href="assets/css/theme-spicymedia.css?v=20260821i" />
|
<link rel="stylesheet" href="assets/css/theme-spicymedia.css?v=20260821j" />
|
||||||
<style>
|
<style>
|
||||||
/* Rabattcodes: an das Supporter-Abo gekoppelt (Nutzer-Feedback
|
/* Rabattcodes: an das Supporter-Abo gekoppelt (Nutzer-Feedback
|
||||||
03.08.2026: "verbinde es mit dem Abo... wenn die Leute drauf drücken
|
03.08.2026: "verbinde es mit dem Abo... wenn die Leute drauf drücken
|
||||||
@@ -140,8 +140,8 @@
|
|||||||
</main>
|
</main>
|
||||||
|
|
||||||
<div id="site-footer"></div>
|
<div id="site-footer"></div>
|
||||||
<script src="assets/js/i18n-links.js?v=20260821i"></script>
|
<script src="assets/js/i18n-links.js?v=20260821j"></script>
|
||||||
<script src="assets/js/main.js?v=20260821i"></script>
|
<script src="assets/js/main.js?v=20260821j"></script>
|
||||||
<script>
|
<script>
|
||||||
(function () {
|
(function () {
|
||||||
document.addEventListener("DOMContentLoaded", function () {
|
document.addEventListener("DOMContentLoaded", function () {
|
||||||
|
|||||||
+64
-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: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" />
|
<meta name="twitter:image" content="https://dogfather-universe.com/assets/img/og-cover.jpg" />
|
||||||
|
|
||||||
<link rel="stylesheet" href="assets/css/main.css?v=20260821i" />
|
<link rel="stylesheet" href="assets/css/main.css?v=20260821j" />
|
||||||
<link rel="stylesheet" href="assets/css/theme-dogfather.css?v=20260821i" />
|
<link rel="stylesheet" href="assets/css/theme-dogfather.css?v=20260821j" />
|
||||||
<link rel="stylesheet" href="assets/css/theme-hasidog.css?v=20260821i" />
|
<link rel="stylesheet" href="assets/css/theme-hasidog.css?v=20260821j" />
|
||||||
<link rel="stylesheet" href="assets/css/theme-spicymedia.css?v=20260821i" />
|
<link rel="stylesheet" href="assets/css/theme-spicymedia.css?v=20260821j" />
|
||||||
</head>
|
</head>
|
||||||
<body class="mit-hintergrund" style="--page-bg:url('/assets/img/bg-manager.jpg');--page-bg-mobile:url('/assets/img/bg-manager-mobile.jpg');">
|
<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>
|
<div id="site-header"></div>
|
||||||
@@ -96,6 +96,19 @@
|
|||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
|
<!-- "Weitere Manager" (21.08.2026, Nutzer-Wunsch: "überall wo ... Manager
|
||||||
|
... sind, will ich dass ich die easy über meine Verwaltungsseite
|
||||||
|
hinzufüge"). Aktuell gibt es nur DogFather selbst als Manager -- diese
|
||||||
|
Sektion bleibt deshalb komplett unsichtbar (hidden), bis über die
|
||||||
|
Verwaltung mindestens ein weiterer Manager angelegt wurde. Gleiches
|
||||||
|
flaches Karten-Raster wie team-scouts.html. -->
|
||||||
|
<section class="section-tight" id="weitere-manager-root" hidden>
|
||||||
|
<div class="container">
|
||||||
|
<h2 class="text-center" data-i18n="mgr_weitere_h2">Weitere Manager</h2>
|
||||||
|
<div id="weitere-manager-grid" class="grid grid-4"></div>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
<section>
|
<section>
|
||||||
<div class="container">
|
<div class="container">
|
||||||
<h2 class="text-center" data-i18n="mgr_team_h2">Willst du dazugehören?</h2>
|
<h2 class="text-center" data-i18n="mgr_team_h2">Willst du dazugehören?</h2>
|
||||||
@@ -126,7 +139,52 @@
|
|||||||
</main>
|
</main>
|
||||||
|
|
||||||
<div id="site-footer"></div>
|
<div id="site-footer"></div>
|
||||||
<script src="assets/js/i18n-manager.js?v=20260821i"></script>
|
<script src="assets/js/data-manager.js?v=20260821j"></script>
|
||||||
<script src="assets/js/main.js?v=20260821i"></script>
|
<script src="assets/js/i18n-manager.js?v=20260821j"></script>
|
||||||
|
<script src="assets/js/main.js?v=20260821j"></script>
|
||||||
|
<script>
|
||||||
|
// "Weitere Manager" (21.08.2026) -- siehe Kommentar bei #weitere-manager-root
|
||||||
|
// oben. Aktuell IMMER window.MANAGERS === [] (siehe data-manager.js), Inhalt
|
||||||
|
// kommt also ausschließlich über die Verwaltung (dogiTeamLaden). Bleibt
|
||||||
|
// komplett unsichtbar, solange niemand eingetragen ist.
|
||||||
|
document.addEventListener("DOMContentLoaded", () => {
|
||||||
|
const root = document.getElementById("weitere-manager-root");
|
||||||
|
const grid = document.getElementById("weitere-manager-grid");
|
||||||
|
if (!root || !grid) return;
|
||||||
|
|
||||||
|
const feld = (x) => (window.dogiFeld ? window.dogiFeld(x) : x || "");
|
||||||
|
const t = (schluessel) => (window.dogiUebersetzen ? window.dogiUebersetzen(schluessel) : schluessel);
|
||||||
|
|
||||||
|
const renderWeitereManager = () => {
|
||||||
|
const liste = window.MANAGERS || [];
|
||||||
|
if (!liste.length) {
|
||||||
|
root.hidden = true;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
grid.innerHTML = liste.map((m) => {
|
||||||
|
const quoteText = feld(m.quote);
|
||||||
|
return `
|
||||||
|
<a class="profile-card" href="profil.html?p=${m.slug}">
|
||||||
|
<div class="avatar">${(m.avatar || m.photo) ? `<img src="${m.avatar || m.photo}" alt="${m.name}" loading="lazy">` : `<span class="avatar-initial">${m.name.charAt(0).toUpperCase()}</span>`}</div>
|
||||||
|
<h3>${m.name}</h3>
|
||||||
|
<div class="role">${feld(m.role)}</div>
|
||||||
|
${quoteText ? `<p class="quote">„${quoteText}"</p>` : ""}
|
||||||
|
<span class="btn btn-outline" style="margin-top:.7rem;pointer-events:none;">${t("mgr_weitere_profil_btn")}</span>
|
||||||
|
</a>`;
|
||||||
|
}).join("");
|
||||||
|
root.hidden = false;
|
||||||
|
};
|
||||||
|
|
||||||
|
document.addEventListener("dogi-sprache-geaendert", renderWeitereManager);
|
||||||
|
|
||||||
|
if (window.dogiTeamLaden) {
|
||||||
|
window.dogiTeamLaden("manager").then((neue) => {
|
||||||
|
if (!neue.length) return;
|
||||||
|
window.MANAGERS = [...(window.MANAGERS || []), ...neue];
|
||||||
|
renderWeitereManager();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
</script>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</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: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" />
|
<meta name="twitter:image" content="https://dogfather-universe.com/assets/img/og-cover.jpg" />
|
||||||
|
|
||||||
<link rel="stylesheet" href="assets/css/main.css?v=20260821i" />
|
<link rel="stylesheet" href="assets/css/main.css?v=20260821j" />
|
||||||
<link rel="stylesheet" href="assets/css/theme-dogfather.css?v=20260821i" />
|
<link rel="stylesheet" href="assets/css/theme-dogfather.css?v=20260821j" />
|
||||||
<link rel="stylesheet" href="assets/css/theme-hasidog.css?v=20260821i" />
|
<link rel="stylesheet" href="assets/css/theme-hasidog.css?v=20260821j" />
|
||||||
<link rel="stylesheet" href="assets/css/theme-spicymedia.css?v=20260821i" />
|
<link rel="stylesheet" href="assets/css/theme-spicymedia.css?v=20260821j" />
|
||||||
</head>
|
</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');">
|
<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>
|
<div id="site-header"></div>
|
||||||
@@ -59,10 +59,10 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div id="site-footer"></div>
|
<div id="site-footer"></div>
|
||||||
<script src="assets/js/data-media.js?v=20260821i"></script>
|
<script src="assets/js/data-media.js?v=20260821j"></script>
|
||||||
<script src="assets/js/data-videos.js?v=20260821i"></script>
|
<script src="assets/js/data-videos.js?v=20260821j"></script>
|
||||||
<script src="assets/js/medien-welt.js?v=20260821i"></script>
|
<script src="assets/js/medien-welt.js?v=20260821j"></script>
|
||||||
<script src="assets/js/i18n-medien-casper.js?v=20260821i"></script>
|
<script src="assets/js/i18n-medien-casper.js?v=20260821j"></script>
|
||||||
<script src="assets/js/main.js?v=20260821i"></script>
|
<script src="assets/js/main.js?v=20260821j"></script>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</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: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" />
|
<meta name="twitter:image" content="https://dogfather-universe.com/assets/img/og-cover.jpg" />
|
||||||
|
|
||||||
<link rel="stylesheet" href="assets/css/main.css?v=20260821i" />
|
<link rel="stylesheet" href="assets/css/main.css?v=20260821j" />
|
||||||
<link rel="stylesheet" href="assets/css/theme-dogfather.css?v=20260821i" />
|
<link rel="stylesheet" href="assets/css/theme-dogfather.css?v=20260821j" />
|
||||||
<link rel="stylesheet" href="assets/css/theme-hasidog.css?v=20260821i" />
|
<link rel="stylesheet" href="assets/css/theme-hasidog.css?v=20260821j" />
|
||||||
<link rel="stylesheet" href="assets/css/theme-spicymedia.css?v=20260821i" />
|
<link rel="stylesheet" href="assets/css/theme-spicymedia.css?v=20260821j" />
|
||||||
</head>
|
</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');">
|
<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>
|
<div id="site-header"></div>
|
||||||
@@ -65,11 +65,11 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div id="site-footer"></div>
|
<div id="site-footer"></div>
|
||||||
<script src="assets/js/data-media.js?v=20260821i"></script>
|
<script src="assets/js/data-media.js?v=20260821j"></script>
|
||||||
<script src="assets/js/data-videos.js?v=20260821i"></script>
|
<script src="assets/js/data-videos.js?v=20260821j"></script>
|
||||||
<script src="assets/js/data-aktionen.js?v=20260821i"></script>
|
<script src="assets/js/data-aktionen.js?v=20260821j"></script>
|
||||||
<script src="assets/js/medien-welt.js?v=20260821i"></script>
|
<script src="assets/js/medien-welt.js?v=20260821j"></script>
|
||||||
<script src="assets/js/i18n-medien-dogfather.js?v=20260821i"></script>
|
<script src="assets/js/i18n-medien-dogfather.js?v=20260821j"></script>
|
||||||
<script src="assets/js/main.js?v=20260821i"></script>
|
<script src="assets/js/main.js?v=20260821j"></script>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</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: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" />
|
<meta name="twitter:image" content="https://dogfather-universe.com/assets/img/og-cover.jpg" />
|
||||||
|
|
||||||
<link rel="stylesheet" href="assets/css/main.css?v=20260821i" />
|
<link rel="stylesheet" href="assets/css/main.css?v=20260821j" />
|
||||||
<link rel="stylesheet" href="assets/css/theme-dogfather.css?v=20260821i" />
|
<link rel="stylesheet" href="assets/css/theme-dogfather.css?v=20260821j" />
|
||||||
<link rel="stylesheet" href="assets/css/theme-hasidog.css?v=20260821i" />
|
<link rel="stylesheet" href="assets/css/theme-hasidog.css?v=20260821j" />
|
||||||
<link rel="stylesheet" href="assets/css/theme-spicymedia.css?v=20260821i" />
|
<link rel="stylesheet" href="assets/css/theme-spicymedia.css?v=20260821j" />
|
||||||
</head>
|
</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');">
|
<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>
|
<div id="site-header"></div>
|
||||||
@@ -65,11 +65,11 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div id="site-footer"></div>
|
<div id="site-footer"></div>
|
||||||
<script src="assets/js/data-media.js?v=20260821i"></script>
|
<script src="assets/js/data-media.js?v=20260821j"></script>
|
||||||
<script src="assets/js/data-videos.js?v=20260821i"></script>
|
<script src="assets/js/data-videos.js?v=20260821j"></script>
|
||||||
<script src="assets/js/data-aktionen.js?v=20260821i"></script>
|
<script src="assets/js/data-aktionen.js?v=20260821j"></script>
|
||||||
<script src="assets/js/medien-welt.js?v=20260821i"></script>
|
<script src="assets/js/medien-welt.js?v=20260821j"></script>
|
||||||
<script src="assets/js/i18n-medien-hasidog.js?v=20260821i"></script>
|
<script src="assets/js/i18n-medien-hasidog.js?v=20260821j"></script>
|
||||||
<script src="assets/js/main.js?v=20260821i"></script>
|
<script src="assets/js/main.js?v=20260821j"></script>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|||||||
+33
-15
@@ -16,10 +16,10 @@
|
|||||||
<meta name="twitter:card" content="summary_large_image" />
|
<meta name="twitter:card" content="summary_large_image" />
|
||||||
<meta name="robots" content="noindex" /> <!-- generische URL, echte Team-Seiten verlinken hierher -->
|
<meta name="robots" content="noindex" /> <!-- generische URL, echte Team-Seiten verlinken hierher -->
|
||||||
|
|
||||||
<link rel="stylesheet" href="assets/css/main.css?v=20260821i" />
|
<link rel="stylesheet" href="assets/css/main.css?v=20260821j" />
|
||||||
<link rel="stylesheet" href="assets/css/theme-dogfather.css?v=20260821i" />
|
<link rel="stylesheet" href="assets/css/theme-dogfather.css?v=20260821j" />
|
||||||
<link rel="stylesheet" href="assets/css/theme-hasidog.css?v=20260821i" />
|
<link rel="stylesheet" href="assets/css/theme-hasidog.css?v=20260821j" />
|
||||||
<link rel="stylesheet" href="assets/css/theme-spicymedia.css?v=20260821i" />
|
<link rel="stylesheet" href="assets/css/theme-spicymedia.css?v=20260821j" />
|
||||||
<style>
|
<style>
|
||||||
.profil-photo { aspect-ratio: 4/5; max-width: 420px; margin: 0 auto; }
|
.profil-photo { aspect-ratio: 4/5; max-width: 420px; margin: 0 auto; }
|
||||||
.profil-photo img { width: 100%; height: 100%; object-fit: cover; display: block; }
|
.profil-photo img { width: 100%; height: 100%; object-fit: cover; display: block; }
|
||||||
@@ -130,18 +130,25 @@
|
|||||||
</main>
|
</main>
|
||||||
|
|
||||||
<div id="site-footer"></div>
|
<div id="site-footer"></div>
|
||||||
<script src="assets/js/data-modis.js?v=20260821i"></script>
|
<script src="assets/js/data-modis.js?v=20260821j"></script>
|
||||||
<script src="assets/js/data-scouts.js?v=20260821i"></script>
|
<script src="assets/js/data-scouts.js?v=20260821j"></script>
|
||||||
<script src="assets/js/data-creator.js?v=20260821i"></script>
|
<script src="assets/js/data-creator.js?v=20260821j"></script>
|
||||||
<script src="assets/js/i18n-profil.js?v=20260821i"></script>
|
<script src="assets/js/data-manager.js?v=20260821j"></script>
|
||||||
<script src="assets/js/main.js?v=20260821i"></script>
|
<script src="assets/js/i18n-profil.js?v=20260821j"></script>
|
||||||
|
<script src="assets/js/main.js?v=20260821j"></script>
|
||||||
<script>
|
<script>
|
||||||
document.addEventListener("DOMContentLoaded", () => {
|
document.addEventListener("DOMContentLoaded", async () => {
|
||||||
const slug = new URLSearchParams(location.search).get("p") || "";
|
const slug = new URLSearchParams(location.search).get("p") || "";
|
||||||
const modis = window.MODIS || [];
|
const modis = window.MODIS || [];
|
||||||
const scouts = window.SCOUTS || [];
|
const scouts = window.SCOUTS || [];
|
||||||
const creators = window.CREATORS || [];
|
const creators = window.CREATORS || [];
|
||||||
const alle = [...modis, ...scouts, ...creators];
|
const managers = window.MANAGERS || [];
|
||||||
|
// 21.08.2026: über die Verwaltung angelegte Mitglieder ALLER Kategorien
|
||||||
|
// dazuholen -- profil.html weiß vorab nicht, zu welcher Kategorie ein
|
||||||
|
// slug gehört, deshalb kommt hier (ohne Kategorie-Filter) einfach alles
|
||||||
|
// zusammen zurück und wird unten normal mitgesucht.
|
||||||
|
const admins = window.dogiTeamLaden ? await window.dogiTeamLaden() : [];
|
||||||
|
const alle = [...modis, ...scouts, ...creators, ...managers, ...admins];
|
||||||
const person = alle.find((p) => p.slug === slug);
|
const person = alle.find((p) => p.slug === slug);
|
||||||
|
|
||||||
// Für alle Rahmentexte, die rein per JS gesetzt werden (Name/Rolle/Bio
|
// Für alle Rahmentexte, die rein per JS gesetzt werden (Name/Rolle/Bio
|
||||||
@@ -170,16 +177,22 @@ document.addEventListener("DOMContentLoaded", () => {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const istScout = scouts.includes(person);
|
// 21.08.2026: admin-gepflegte Personen (aus dogiTeamLaden) stecken NICHT
|
||||||
const istCreator = creators.includes(person);
|
// in den statischen modis/scouts/creators/managers-Arrays selbst (die
|
||||||
const welt = istScout || istCreator ? "spicymedia" : "dogfather";
|
// reference-Prüfung .includes(person) würde für sie also fälschlich
|
||||||
|
// "false" liefern) -- deshalb zusätzlich über das von dogiTeamLaden
|
||||||
|
// gesetzte person.kategorie-Feld prüfen.
|
||||||
|
const istScout = scouts.includes(person) || person.kategorie === "scout";
|
||||||
|
const istCreator = creators.includes(person) || person.kategorie === "creator";
|
||||||
|
const istManager = managers.includes(person) || person.kategorie === "manager";
|
||||||
|
const welt = istScout || istCreator || istManager ? "spicymedia" : "dogfather";
|
||||||
document.documentElement.setAttribute("data-theme", welt);
|
document.documentElement.setAttribute("data-theme", welt);
|
||||||
|
|
||||||
// Passendes Weltmotiv nur noch als weichgezeichnete Stimmung ganz im
|
// Passendes Weltmotiv nur noch als weichgezeichnete Stimmung ganz im
|
||||||
// Hintergrund (body.mit-hintergrund) — bewusst KEIN scharfes Banner-Bild
|
// Hintergrund (body.mit-hintergrund) — bewusst KEIN scharfes Banner-Bild
|
||||||
// mehr oben im Kopfbereich, damit das Profil der Person (Foto/Name/Bio)
|
// mehr oben im Kopfbereich, damit das Profil der Person (Foto/Name/Bio)
|
||||||
// sofort beim Öffnen der Seite im Fokus steht, ohne Banner davor.
|
// sofort beim Öffnen der Seite im Fokus steht, ohne Banner davor.
|
||||||
const bgBasis = istScout || istCreator ? "bg-scouts" : "bg-dogfather";
|
const bgBasis = istScout || istCreator || istManager ? "bg-scouts" : "bg-dogfather";
|
||||||
document.body.style.setProperty("--page-bg", `url('/assets/img/${bgBasis}.jpg')`);
|
document.body.style.setProperty("--page-bg", `url('/assets/img/${bgBasis}.jpg')`);
|
||||||
document.body.style.setProperty("--page-bg-mobile", `url('/assets/img/${bgBasis}-mobile.jpg')`);
|
document.body.style.setProperty("--page-bg-mobile", `url('/assets/img/${bgBasis}-mobile.jpg')`);
|
||||||
|
|
||||||
@@ -241,6 +254,8 @@ document.addEventListener("DOMContentLoaded", () => {
|
|||||||
? t("pr_eyebrow_scout")
|
? t("pr_eyebrow_scout")
|
||||||
: istCreator
|
: istCreator
|
||||||
? t("pr_eyebrow_creator")
|
? t("pr_eyebrow_creator")
|
||||||
|
: istManager
|
||||||
|
? t("pr_eyebrow_manager")
|
||||||
: t("pr_eyebrow_default");
|
: t("pr_eyebrow_default");
|
||||||
|
|
||||||
document.getElementById("profil-hero-name").textContent = person.name;
|
document.getElementById("profil-hero-name").textContent = person.name;
|
||||||
@@ -286,6 +301,9 @@ document.addEventListener("DOMContentLoaded", () => {
|
|||||||
} else if (istCreator) {
|
} else if (istCreator) {
|
||||||
zurueck.href = "creator.html";
|
zurueck.href = "creator.html";
|
||||||
zurueck.textContent = t("pr_back_creator");
|
zurueck.textContent = t("pr_back_creator");
|
||||||
|
} else if (istManager) {
|
||||||
|
zurueck.href = "manager.html";
|
||||||
|
zurueck.textContent = t("pr_back_manager");
|
||||||
} else {
|
} else {
|
||||||
zurueck.href = "team-modis.html";
|
zurueck.href = "team-modis.html";
|
||||||
zurueck.textContent = t("pr_back_modis");
|
zurueck.textContent = t("pr_back_modis");
|
||||||
|
|||||||
+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: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" />
|
<meta name="twitter:image" content="https://dogfather-universe.com/assets/img/og-cover.jpg" />
|
||||||
|
|
||||||
<link rel="stylesheet" href="assets/css/main.css?v=20260821i" />
|
<link rel="stylesheet" href="assets/css/main.css?v=20260821j" />
|
||||||
<link rel="stylesheet" href="assets/css/theme-dogfather.css?v=20260821i" />
|
<link rel="stylesheet" href="assets/css/theme-dogfather.css?v=20260821j" />
|
||||||
<link rel="stylesheet" href="assets/css/theme-hasidog.css?v=20260821i" />
|
<link rel="stylesheet" href="assets/css/theme-hasidog.css?v=20260821j" />
|
||||||
<link rel="stylesheet" href="assets/css/theme-spicymedia.css?v=20260821i" />
|
<link rel="stylesheet" href="assets/css/theme-spicymedia.css?v=20260821j" />
|
||||||
<style>
|
<style>
|
||||||
/* Leere-Zustand-Kachel im "Gutschein"-Look: gestrichelter Rand wie ein
|
/* Leere-Zustand-Kachel im "Gutschein"-Look: gestrichelter Rand wie ein
|
||||||
ausgeschnittener Coupon, damit auch OHNE echte Codes schon klar ist,
|
ausgeschnittener Coupon, damit auch OHNE echte Codes schon klar ist,
|
||||||
@@ -131,8 +131,8 @@
|
|||||||
</main>
|
</main>
|
||||||
|
|
||||||
<div id="site-footer"></div>
|
<div id="site-footer"></div>
|
||||||
<script src="assets/js/i18n-rabattcodes.js?v=20260821i"></script>
|
<script src="assets/js/i18n-rabattcodes.js?v=20260821j"></script>
|
||||||
<script src="assets/js/main.js?v=20260821i"></script>
|
<script src="assets/js/main.js?v=20260821j"></script>
|
||||||
<script>
|
<script>
|
||||||
(function () {
|
(function () {
|
||||||
document.addEventListener("DOMContentLoaded", function () {
|
document.addEventListener("DOMContentLoaded", function () {
|
||||||
|
|||||||
@@ -30,6 +30,7 @@ const MIGRATION_FILES = [
|
|||||||
"0004_supporter_oauth_providers.sql",
|
"0004_supporter_oauth_providers.sql",
|
||||||
"0005_supporter_polls.sql",
|
"0005_supporter_polls.sql",
|
||||||
"0006_testimonials.sql",
|
"0006_testimonials.sql",
|
||||||
|
"0007_team_members.sql",
|
||||||
];
|
];
|
||||||
|
|
||||||
function alreadyApplied(name) {
|
function alreadyApplied(name) {
|
||||||
|
|||||||
@@ -71,6 +71,9 @@ import {
|
|||||||
import {
|
import {
|
||||||
createPoll, listPolls, closePoll, reopenPoll, deletePoll, getActivePollPublic, voteOnPoll,
|
createPoll, listPolls, closePoll, reopenPoll, deletePoll, getActivePollPublic, voteOnPoll,
|
||||||
} from "./routes/polls.js";
|
} from "./routes/polls.js";
|
||||||
|
import {
|
||||||
|
getTeamPublic, getTeamAdmin, saveTeamMember, deleteTeamMember, uploadTeamPhoto,
|
||||||
|
} from "./routes/team.js";
|
||||||
|
|
||||||
initDb();
|
initDb();
|
||||||
|
|
||||||
@@ -86,6 +89,10 @@ app.disable("x-powered-by"); // verrät sonst unnötig den eingesetzten Technik-
|
|||||||
const UPLOADS_BASIS_DIR = process.env.UPLOADS_DIR || "/var/lib/dogfather-internal/uploads";
|
const UPLOADS_BASIS_DIR = process.env.UPLOADS_DIR || "/var/lib/dogfather-internal/uploads";
|
||||||
const EVENTS_UPLOADS_DIR = `${UPLOADS_BASIS_DIR}/events`;
|
const EVENTS_UPLOADS_DIR = `${UPLOADS_BASIS_DIR}/events`;
|
||||||
mkdirSync(EVENTS_UPLOADS_DIR, { recursive: true });
|
mkdirSync(EVENTS_UPLOADS_DIR, { recursive: true });
|
||||||
|
// Team-Fotos (21.08.2026, "Verwaltungsseite perfektionieren"-Auftrag) --
|
||||||
|
// eigener Unterordner, gleiches Muster wie bei den Event-Bildern oben.
|
||||||
|
const TEAM_UPLOADS_DIR = `${UPLOADS_BASIS_DIR}/team`;
|
||||||
|
mkdirSync(TEAM_UPLOADS_DIR, { recursive: true });
|
||||||
|
|
||||||
const ERLAUBTE_BILD_TYPEN = new Set(["image/jpeg", "image/png", "image/webp"]);
|
const ERLAUBTE_BILD_TYPEN = new Set(["image/jpeg", "image/png", "image/webp"]);
|
||||||
const bildUpload = multer({
|
const bildUpload = multer({
|
||||||
@@ -99,6 +106,14 @@ const bildUpload = multer({
|
|||||||
limits: { fileSize: 5 * 1024 * 1024 }, // 5 MB
|
limits: { fileSize: 5 * 1024 * 1024 }, // 5 MB
|
||||||
fileFilter: (req, file, cb) => cb(null, ERLAUBTE_BILD_TYPEN.has(file.mimetype)),
|
fileFilter: (req, file, cb) => cb(null, ERLAUBTE_BILD_TYPEN.has(file.mimetype)),
|
||||||
});
|
});
|
||||||
|
const teamBildUpload = multer({
|
||||||
|
storage: multer.diskStorage({
|
||||||
|
destination: (req, file, cb) => cb(null, TEAM_UPLOADS_DIR),
|
||||||
|
filename: (req, file, cb) => cb(null, `${crypto.randomUUID()}${extname(file.originalname).toLowerCase()}`),
|
||||||
|
}),
|
||||||
|
limits: { fileSize: 5 * 1024 * 1024 }, // 5 MB
|
||||||
|
fileFilter: (req, file, cb) => cb(null, ERLAUBTE_BILD_TYPEN.has(file.mimetype)),
|
||||||
|
});
|
||||||
|
|
||||||
app.use((req, res, next) => {
|
app.use((req, res, next) => {
|
||||||
res.setHeader("X-Content-Type-Options", "nosniff");
|
res.setHeader("X-Content-Type-Options", "nosniff");
|
||||||
@@ -164,6 +179,7 @@ app.get("/events-of-year", wrap(getEventsPublic));
|
|||||||
app.get("/special-event", wrap(getSpecialEventPublic));
|
app.get("/special-event", wrap(getSpecialEventPublic));
|
||||||
app.post("/testimonials/submit", wrap(submitTestimonial));
|
app.post("/testimonials/submit", wrap(submitTestimonial));
|
||||||
app.get("/testimonials", wrap(getApprovedTestimonialsPublic));
|
app.get("/testimonials", wrap(getApprovedTestimonialsPublic));
|
||||||
|
app.get("/team", wrap(getTeamPublic));
|
||||||
|
|
||||||
app.post("/supporter/register", wrap(registerSupporter));
|
app.post("/supporter/register", wrap(registerSupporter));
|
||||||
app.post("/supporter/verify-email", wrap(verifySupporterEmail));
|
app.post("/supporter/verify-email", wrap(verifySupporterEmail));
|
||||||
@@ -210,6 +226,12 @@ app.post("/admin/testimonials/list", requireTeamSession, withSession(getTestimon
|
|||||||
app.post("/admin/testimonials/status", requireTeamSession, withSession(setTestimonialStatus));
|
app.post("/admin/testimonials/status", requireTeamSession, withSession(setTestimonialStatus));
|
||||||
app.post("/admin/testimonials/delete", requireTeamSession, withSession(deleteTestimonial));
|
app.post("/admin/testimonials/delete", requireTeamSession, withSession(deleteTestimonial));
|
||||||
|
|
||||||
|
// Team-Mitglieder (Modis/Scouts/Creator/Manager) — 21.08.2026
|
||||||
|
app.post("/admin/team/list", requireTeamSession, withSession(getTeamAdmin));
|
||||||
|
app.post("/admin/team/save", requireTeamSession, withSession(saveTeamMember));
|
||||||
|
app.post("/admin/team/delete", requireTeamSession, withSession(deleteTeamMember));
|
||||||
|
app.post("/admin/team/upload-photo", requireTeamSession, teamBildUpload.single("image"), withSession(uploadTeamPhoto));
|
||||||
|
|
||||||
app.post("/admin/roles/list", requireTeamSession, withSession(listRoles));
|
app.post("/admin/roles/list", requireTeamSession, withSession(listRoles));
|
||||||
app.post("/admin/roles/create", requireTeamSession, withSession(createRole));
|
app.post("/admin/roles/create", requireTeamSession, withSession(createRole));
|
||||||
app.post("/admin/roles/update", requireTeamSession, withSession(updateRole));
|
app.post("/admin/roles/update", requireTeamSession, withSession(updateRole));
|
||||||
|
|||||||
@@ -49,6 +49,7 @@ export const PERMISSIONS = {
|
|||||||
|
|
||||||
EVENTS_MANAGE: "Startseite: \"Event des Jahres\" verwalten",
|
EVENTS_MANAGE: "Startseite: \"Event des Jahres\" verwalten",
|
||||||
TESTIMONIALS_MANAGE: "Stimmen/Testimonials verwalten",
|
TESTIMONIALS_MANAGE: "Stimmen/Testimonials verwalten",
|
||||||
|
TEAM_MANAGE: "Team-Mitglieder verwalten (Modis/Scouts/Creator/Manager)",
|
||||||
};
|
};
|
||||||
|
|
||||||
export const PERMISSION_GROUPS = [
|
export const PERMISSION_GROUPS = [
|
||||||
@@ -61,6 +62,7 @@ export const PERMISSION_GROUPS = [
|
|||||||
{ label: "DogiCrew 🩵", keys: ["SUPPORTER_VIEW", "SUPPORTER_MANAGE_PREMIUMS", "SUPPORTER_MANAGE_COLLECTIONS", "POLLS_MANAGE"] },
|
{ label: "DogiCrew 🩵", keys: ["SUPPORTER_VIEW", "SUPPORTER_MANAGE_PREMIUMS", "SUPPORTER_MANAGE_COLLECTIONS", "POLLS_MANAGE"] },
|
||||||
{ label: "Startseite", keys: ["EVENTS_MANAGE"] },
|
{ label: "Startseite", keys: ["EVENTS_MANAGE"] },
|
||||||
{ label: "Stimmen", keys: ["TESTIMONIALS_MANAGE"] },
|
{ label: "Stimmen", keys: ["TESTIMONIALS_MANAGE"] },
|
||||||
|
{ label: "Team", keys: ["TEAM_MANAGE"] },
|
||||||
];
|
];
|
||||||
|
|
||||||
export const OWNER_ONLY_ACTIONS = [
|
export const OWNER_ONLY_ACTIONS = [
|
||||||
|
|||||||
@@ -0,0 +1,228 @@
|
|||||||
|
/* =====================================================================
|
||||||
|
routes/team.js — Team-Mitglieder-Verwaltung (21.08.2026, Nutzer-Wunsch:
|
||||||
|
"überall wo Modis sind oder Scouts oder Manager, oder Creator, will ich
|
||||||
|
dass ich die easy über meine Verwaltungsseite hinzufüge und die
|
||||||
|
automatisch in der Website hinzugefügt werden ... auch mit Fotos und
|
||||||
|
TikTok Link").
|
||||||
|
|
||||||
|
Ergänzt (überschreibt NICHT) die von Hand gepflegten Einträge in
|
||||||
|
assets/js/data-modis.js/data-scouts.js/data-creator.js/data-manager.js.
|
||||||
|
Die Website mischt beide Quellen zu einer gemeinsamen Liste (siehe
|
||||||
|
window.dogiTeamLaden() in main.js) -- ein Fehler hier kann die
|
||||||
|
bestehenden, fest einprogrammierten Profile (VanVan, Patrick, ...) nie
|
||||||
|
verschwinden lassen.
|
||||||
|
|
||||||
|
Öffentlicher Lesezugriff (getTeamPublic) ohne Session, nur AKTIVE
|
||||||
|
Mitglieder -- Schreibzugriff nur mit TEAM_MANAGE-Berechtigung.
|
||||||
|
===================================================================== */
|
||||||
|
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";
|
||||||
|
import { uebersetzeAlleSprachen } from "../lib/translate.js";
|
||||||
|
|
||||||
|
const KATEGORIEN = ["modi", "scout", "creator", "manager"];
|
||||||
|
|
||||||
|
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 parseJsonFeld(text) {
|
||||||
|
if (!text) return null;
|
||||||
|
try {
|
||||||
|
return JSON.parse(text);
|
||||||
|
} catch {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Slug aus dem Namen ableiten (Kleinbuchstaben, Umlaute vereinfacht,
|
||||||
|
alles außer a-z0-9 wird zu "-"), falls die Verwaltung keinen eigenen
|
||||||
|
Slug mitschickt -- selbes Muster wie an anderen Stellen der Seite. */
|
||||||
|
function slugAusName(name) {
|
||||||
|
return String(name || "")
|
||||||
|
.toLowerCase()
|
||||||
|
.replace(/ä/g, "ae").replace(/ö/g, "oe").replace(/ü/g, "ue").replace(/ß/g, "ss")
|
||||||
|
.replace(/[^a-z0-9]+/g, "-")
|
||||||
|
.replace(/^-+|-+$/g, "")
|
||||||
|
.slice(0, 60);
|
||||||
|
}
|
||||||
|
|
||||||
|
function oeffentlichesFeld(row) {
|
||||||
|
return {
|
||||||
|
id: row.id,
|
||||||
|
kategorie: row.kategorie,
|
||||||
|
name: row.name,
|
||||||
|
slug: row.slug,
|
||||||
|
rolle: parseJsonFeld(row.rolle_json),
|
||||||
|
foto: row.foto_url || "",
|
||||||
|
tiktok: row.tiktok_url || "",
|
||||||
|
bio: parseJsonFeld(row.bio_json),
|
||||||
|
zitat: parseJsonFeld(row.zitat_json),
|
||||||
|
rang: row.rang,
|
||||||
|
rangLabel: parseJsonFeld(row.rang_label_json),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
function vollesFeld(row) {
|
||||||
|
return {
|
||||||
|
...oeffentlichesFeld(row),
|
||||||
|
status: row.status,
|
||||||
|
reihenfolge: row.reihenfolge,
|
||||||
|
erstelltAm: row.erstellt_am,
|
||||||
|
geaendertAm: row.geaendert_am,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Öffentlich, keine Session: nur AKTIVE Mitglieder. Ohne ?kategorie=
|
||||||
|
kommen alle vier Kategorien zusammen zurück (genutzt von profil.html,
|
||||||
|
das vorab nicht weiß, zu welcher Kategorie ein slug gehört). */
|
||||||
|
export async function getTeamPublic(req, res) {
|
||||||
|
const kategorie = String(req.query?.kategorie || "").trim();
|
||||||
|
let rows;
|
||||||
|
if (kategorie && KATEGORIEN.includes(kategorie)) {
|
||||||
|
rows = db
|
||||||
|
.prepare(`SELECT * FROM team_members WHERE kategorie = ? AND status = 'aktiv' ORDER BY reihenfolge ASC, erstellt_am ASC`)
|
||||||
|
.all(kategorie);
|
||||||
|
} else {
|
||||||
|
rows = db
|
||||||
|
.prepare(`SELECT * FROM team_members WHERE status = 'aktiv' ORDER BY reihenfolge ASC, erstellt_am ASC`)
|
||||||
|
.all();
|
||||||
|
}
|
||||||
|
return json(res, { ok: true, mitglieder: rows.map(oeffentlichesFeld) });
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Für die Verwaltung: ALLE Mitglieder aller Kategorien/Status. */
|
||||||
|
export async function getTeamAdmin(req, res, session) {
|
||||||
|
if (!can(session, "TEAM_MANAGE")) return forbidden(res);
|
||||||
|
const rows = db.prepare(`SELECT * FROM team_members ORDER BY kategorie ASC, reihenfolge ASC, erstellt_am ASC`).all();
|
||||||
|
return json(res, { ok: true, mitglieder: rows.map(vollesFeld) });
|
||||||
|
}
|
||||||
|
|
||||||
|
/** NUR Foto-Upload, kein JSON-Body (multipart, siehe index.js multer-Middleware). */
|
||||||
|
export async function uploadTeamPhoto(req, res, session) {
|
||||||
|
if (!can(session, "TEAM_MANAGE")) return forbidden(res);
|
||||||
|
if (!req.file) return json(res, { ok: false, error: "Keine Bilddatei erhalten." }, 400);
|
||||||
|
const url = `/uploads/team/${req.file.filename}`;
|
||||||
|
logAction(session.actor, "team.photo_uploaded", req.file.filename, null);
|
||||||
|
return json(res, { ok: true, url });
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function saveTeamMember(req, res, session) {
|
||||||
|
if (!can(session, "TEAM_MANAGE")) return forbidden(res);
|
||||||
|
const body = req.body || {};
|
||||||
|
const id = String(body.id || "").trim() || null;
|
||||||
|
|
||||||
|
const kategorie = String(body.kategorie || "").trim();
|
||||||
|
if (!KATEGORIEN.includes(kategorie)) {
|
||||||
|
return json(res, { ok: false, error: "Ungültige Kategorie." }, 400);
|
||||||
|
}
|
||||||
|
const name = String(body.name || "").trim().slice(0, 60);
|
||||||
|
if (!name) return json(res, { ok: false, error: "Name ist Pflicht." }, 400);
|
||||||
|
|
||||||
|
const rolleDe = String(body.rolle || "").trim().slice(0, 120);
|
||||||
|
if (!rolleDe) return json(res, { ok: false, error: "Rolle ist Pflicht." }, 400);
|
||||||
|
|
||||||
|
let slug = String(body.slug || "").trim().toLowerCase().replace(/[^a-z0-9-]/g, "").slice(0, 60) || slugAusName(name);
|
||||||
|
if (!slug) return json(res, { ok: false, error: "Konnte keinen gültigen Slug ableiten -- bitte Namen prüfen." }, 400);
|
||||||
|
|
||||||
|
// Slug muss GLOBAL eindeutig sein (profil.html sucht kategorieübergreifend).
|
||||||
|
// Kollidiert er mit einem ANDEREN Eintrag (nicht dem gerade bearbeiteten),
|
||||||
|
// wird er automatisch mit "-2", "-3", ... eindeutig gemacht statt hart
|
||||||
|
// abzulehnen -- Dogi/VanVan sollen nicht wegen eines Namensdopplers
|
||||||
|
// (z.B. zwei Personen mit Spitzname "Foxy") hängen bleiben.
|
||||||
|
const slugBelegt = (kandidat) => {
|
||||||
|
const row = db.prepare(`SELECT id FROM team_members WHERE slug = ?`).get(kandidat);
|
||||||
|
return row && row.id !== id;
|
||||||
|
};
|
||||||
|
if (slugBelegt(slug)) {
|
||||||
|
let zaehler = 2;
|
||||||
|
while (slugBelegt(`${slug}-${zaehler}`)) zaehler++;
|
||||||
|
slug = `${slug}-${zaehler}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
const tiktokRoh = String(body.tiktok || "").trim().slice(0, 200);
|
||||||
|
let tiktokUrl = null;
|
||||||
|
if (tiktokRoh) {
|
||||||
|
if (/^https?:\/\//i.test(tiktokRoh)) {
|
||||||
|
tiktokUrl = tiktokRoh;
|
||||||
|
} else {
|
||||||
|
// Bequemlichkeit: reiner Benutzername (mit oder ohne @) wird
|
||||||
|
// automatisch zu einer vollen TikTok-URL ergänzt.
|
||||||
|
tiktokUrl = `https://www.tiktok.com/@${tiktokRoh.replace(/^@/, "")}`;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const bioDe = String(body.bio || "").trim().slice(0, 800);
|
||||||
|
const zitatDe = String(body.zitat || "").trim().slice(0, 200);
|
||||||
|
const fotoUrl = String(body.foto || "").trim().slice(0, 300) || null;
|
||||||
|
|
||||||
|
const rang = body.rang !== undefined && body.rang !== null && body.rang !== ""
|
||||||
|
? Math.max(1, Math.min(99, Number(body.rang) || 0)) : null;
|
||||||
|
const rangLabelDe = String(body.rangLabel || "").trim().slice(0, 40);
|
||||||
|
|
||||||
|
const reihenfolge = Number.isFinite(Number(body.reihenfolge)) ? Number(body.reihenfolge) : 0;
|
||||||
|
const status = body.status === "pausiert" ? "pausiert" : "aktiv";
|
||||||
|
|
||||||
|
const [rolle, bio, zitat, rangLabel] = await Promise.all([
|
||||||
|
uebersetzeAlleSprachen(rolleDe),
|
||||||
|
bioDe ? uebersetzeAlleSprachen(bioDe) : Promise.resolve(null),
|
||||||
|
zitatDe ? uebersetzeAlleSprachen(zitatDe) : Promise.resolve(null),
|
||||||
|
rangLabelDe ? uebersetzeAlleSprachen(rangLabelDe) : Promise.resolve(null),
|
||||||
|
]);
|
||||||
|
|
||||||
|
const jetzt = nowIso();
|
||||||
|
|
||||||
|
if (id) {
|
||||||
|
const info = db
|
||||||
|
.prepare(
|
||||||
|
`UPDATE team_members SET
|
||||||
|
kategorie = ?, name = ?, slug = ?, rolle_json = ?, foto_url = ?, tiktok_url = ?,
|
||||||
|
bio_json = ?, zitat_json = ?, rang = ?, rang_label_json = ?, reihenfolge = ?,
|
||||||
|
status = ?, geaendert_am = ?, geaendert_von = ?
|
||||||
|
WHERE id = ?`
|
||||||
|
)
|
||||||
|
.run(
|
||||||
|
kategorie, name, slug, JSON.stringify(rolle), fotoUrl, tiktokUrl,
|
||||||
|
bio ? JSON.stringify(bio) : null, zitat ? JSON.stringify(zitat) : null,
|
||||||
|
rang, rangLabel ? JSON.stringify(rangLabel) : null, reihenfolge,
|
||||||
|
status, jetzt, session.actor, id
|
||||||
|
);
|
||||||
|
if (info.changes === 0) return json(res, { ok: false, error: "Nicht gefunden." }, 404);
|
||||||
|
logAction(session.actor, "team.updated", id, { name, kategorie });
|
||||||
|
const row = db.prepare(`SELECT * FROM team_members WHERE id = ?`).get(id);
|
||||||
|
return json(res, { ok: true, mitglied: vollesFeld(row) });
|
||||||
|
}
|
||||||
|
|
||||||
|
const neueId = generateId();
|
||||||
|
db.prepare(
|
||||||
|
`INSERT INTO team_members
|
||||||
|
(id, kategorie, name, slug, rolle_json, foto_url, tiktok_url, bio_json, zitat_json,
|
||||||
|
rang, rang_label_json, reihenfolge, status, erstellt_am, geaendert_am, erstellt_von, geaendert_von)
|
||||||
|
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`
|
||||||
|
).run(
|
||||||
|
neueId, kategorie, name, slug, JSON.stringify(rolle), fotoUrl, tiktokUrl,
|
||||||
|
bio ? JSON.stringify(bio) : null, zitat ? JSON.stringify(zitat) : null,
|
||||||
|
rang, rangLabel ? JSON.stringify(rangLabel) : null, reihenfolge,
|
||||||
|
status, jetzt, jetzt, session.actor, session.actor
|
||||||
|
);
|
||||||
|
logAction(session.actor, "team.created", neueId, { name, kategorie });
|
||||||
|
const row = db.prepare(`SELECT * FROM team_members WHERE id = ?`).get(neueId);
|
||||||
|
return json(res, { ok: true, mitglied: vollesFeld(row) });
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function deleteTeamMember(req, res, session) {
|
||||||
|
if (!can(session, "TEAM_MANAGE")) return forbidden(res);
|
||||||
|
const id = String(req.body?.id || "");
|
||||||
|
const info = db.prepare(`DELETE FROM team_members WHERE id = ?`).run(id);
|
||||||
|
if (info.changes === 0) return json(res, { ok: false, error: "Nicht gefunden." }, 404);
|
||||||
|
logAction(session.actor, "team.deleted", id, null);
|
||||||
|
return json(res, { ok: true });
|
||||||
|
}
|
||||||
+7
-7
@@ -19,10 +19,10 @@
|
|||||||
<meta name="twitter:description" content="Was die Community und das Team über DogFather sagen." />
|
<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" />
|
<meta name="twitter:image" content="https://dogfather-universe.com/assets/img/og-cover.jpg" />
|
||||||
|
|
||||||
<link rel="stylesheet" href="assets/css/main.css?v=20260821i" />
|
<link rel="stylesheet" href="assets/css/main.css?v=20260821j" />
|
||||||
<link rel="stylesheet" href="assets/css/theme-dogfather.css?v=20260821i" />
|
<link rel="stylesheet" href="assets/css/theme-dogfather.css?v=20260821j" />
|
||||||
<link rel="stylesheet" href="assets/css/theme-hasidog.css?v=20260821i" />
|
<link rel="stylesheet" href="assets/css/theme-hasidog.css?v=20260821j" />
|
||||||
<link rel="stylesheet" href="assets/css/theme-spicymedia.css?v=20260821i" />
|
<link rel="stylesheet" href="assets/css/theme-spicymedia.css?v=20260821j" />
|
||||||
</head>
|
</head>
|
||||||
<body class="mit-hintergrund" style="--page-bg:url('/assets/img/bg-stimmen.jpg');--page-bg-mobile:url('/assets/img/bg-stimmen-mobile.jpg');">
|
<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>
|
<div id="site-header"></div>
|
||||||
@@ -87,8 +87,8 @@
|
|||||||
</main>
|
</main>
|
||||||
|
|
||||||
<div id="site-footer"></div>
|
<div id="site-footer"></div>
|
||||||
<script src="assets/js/i18n-stimmen.js?v=20260821i"></script>
|
<script src="assets/js/i18n-stimmen.js?v=20260821j"></script>
|
||||||
<script src="assets/js/main.js?v=20260821i"></script>
|
<script src="assets/js/main.js?v=20260821j"></script>
|
||||||
<script src="assets/js/stimmen.js?v=20260821i"></script>
|
<script src="assets/js/stimmen.js?v=20260821j"></script>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</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: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" />
|
<meta name="twitter:image" content="https://dogfather-universe.com/assets/img/og-cover.jpg" />
|
||||||
|
|
||||||
<link rel="stylesheet" href="assets/css/main.css?v=20260821i" />
|
<link rel="stylesheet" href="assets/css/main.css?v=20260821j" />
|
||||||
<link rel="stylesheet" href="assets/css/theme-dogfather.css?v=20260821i" />
|
<link rel="stylesheet" href="assets/css/theme-dogfather.css?v=20260821j" />
|
||||||
<link rel="stylesheet" href="assets/css/theme-hasidog.css?v=20260821i" />
|
<link rel="stylesheet" href="assets/css/theme-hasidog.css?v=20260821j" />
|
||||||
<link rel="stylesheet" href="assets/css/theme-spicymedia.css?v=20260821i" />
|
<link rel="stylesheet" href="assets/css/theme-spicymedia.css?v=20260821j" />
|
||||||
</head>
|
</head>
|
||||||
<body class="mit-hintergrund" style="--page-bg:url('/assets/img/bg-streamer.jpg');--page-bg-mobile:url('/assets/img/bg-streamer-mobile.jpg');">
|
<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>
|
<div id="site-header"></div>
|
||||||
@@ -217,7 +217,7 @@
|
|||||||
</main>
|
</main>
|
||||||
|
|
||||||
<div id="site-footer"></div>
|
<div id="site-footer"></div>
|
||||||
<script src="assets/js/i18n-streamer.js?v=20260821i"></script>
|
<script src="assets/js/i18n-streamer.js?v=20260821j"></script>
|
||||||
<script src="assets/js/main.js?v=20260821i"></script>
|
<script src="assets/js/main.js?v=20260821j"></script>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|||||||
+9
-9
@@ -19,17 +19,17 @@
|
|||||||
<meta name="twitter:description" content="Wann geht DogFather live? Streamplan und Livebereich." />
|
<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" />
|
<meta name="twitter:image" content="https://dogfather-universe.com/assets/img/og-cover.jpg" />
|
||||||
|
|
||||||
<link rel="stylesheet" href="assets/css/main.css?v=20260821i" />
|
<link rel="stylesheet" href="assets/css/main.css?v=20260821j" />
|
||||||
<link rel="stylesheet" href="assets/css/theme-dogfather.css?v=20260821i" />
|
<link rel="stylesheet" href="assets/css/theme-dogfather.css?v=20260821j" />
|
||||||
<link rel="stylesheet" href="assets/css/theme-hasidog.css?v=20260821i" />
|
<link rel="stylesheet" href="assets/css/theme-hasidog.css?v=20260821j" />
|
||||||
<link rel="stylesheet" href="assets/css/theme-spicymedia.css?v=20260821i" />
|
<link rel="stylesheet" href="assets/css/theme-spicymedia.css?v=20260821j" />
|
||||||
</head>
|
</head>
|
||||||
<body class="mit-hintergrund" style="--page-bg:url('/assets/img/bg-streamplan.jpg?v=20260821i');--page-bg-mobile:url('/assets/img/bg-streamplan-mobile.jpg?v=20260821i');">
|
<body class="mit-hintergrund" style="--page-bg:url('/assets/img/bg-streamplan.jpg?v=20260821j');--page-bg-mobile:url('/assets/img/bg-streamplan-mobile.jpg?v=20260821j');">
|
||||||
<div id="site-header"></div>
|
<div id="site-header"></div>
|
||||||
|
|
||||||
<main>
|
<main>
|
||||||
<section class="hero page-bg">
|
<section class="hero page-bg">
|
||||||
<div class="hero-banner"><img src="/assets/img/bg-streamplan.jpg?v=20260821i" alt="" loading="eager" /></div>
|
<div class="hero-banner"><img src="/assets/img/bg-streamplan.jpg?v=20260821j" alt="" loading="eager" /></div>
|
||||||
<div class="container">
|
<div class="container">
|
||||||
<span class="eyebrow" data-i18n="sp_eyebrow">Welt 1 — DogFather</span>
|
<span class="eyebrow" data-i18n="sp_eyebrow">Welt 1 — DogFather</span>
|
||||||
<h1 data-i18n="sp_h1">Streamplan & Livebereich</h1>
|
<h1 data-i18n="sp_h1">Streamplan & Livebereich</h1>
|
||||||
@@ -126,8 +126,8 @@
|
|||||||
</main>
|
</main>
|
||||||
|
|
||||||
<div id="site-footer"></div>
|
<div id="site-footer"></div>
|
||||||
<script src="assets/js/i18n-streamplan.js?v=20260821i"></script>
|
<script src="assets/js/i18n-streamplan.js?v=20260821j"></script>
|
||||||
<script src="assets/js/main.js?v=20260821i"></script>
|
<script src="assets/js/main.js?v=20260821j"></script>
|
||||||
<script src="assets/js/streamplan.js?v=20260821i"></script>
|
<script src="assets/js/streamplan.js?v=20260821j"></script>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|||||||
+7
-7
@@ -9,10 +9,10 @@
|
|||||||
<link rel="manifest" href="/manifest.json" />
|
<link rel="manifest" href="/manifest.json" />
|
||||||
<meta name="theme-color" content="#0b0d10" />
|
<meta name="theme-color" content="#0b0d10" />
|
||||||
<meta name="robots" content="noindex" />
|
<meta name="robots" content="noindex" />
|
||||||
<link rel="stylesheet" href="assets/css/main.css?v=20260821i" />
|
<link rel="stylesheet" href="assets/css/main.css?v=20260821j" />
|
||||||
<link rel="stylesheet" href="assets/css/theme-dogfather.css?v=20260821i" />
|
<link rel="stylesheet" href="assets/css/theme-dogfather.css?v=20260821j" />
|
||||||
<link rel="stylesheet" href="assets/css/theme-hasidog.css?v=20260821i" />
|
<link rel="stylesheet" href="assets/css/theme-hasidog.css?v=20260821j" />
|
||||||
<link rel="stylesheet" href="assets/css/theme-spicymedia.css?v=20260821i" />
|
<link rel="stylesheet" href="assets/css/theme-spicymedia.css?v=20260821j" />
|
||||||
<style>
|
<style>
|
||||||
/* =====================================================================
|
/* =====================================================================
|
||||||
Supporter-Bereich — komplett neu gestaltet (Nutzer-Feedback
|
Supporter-Bereich — komplett neu gestaltet (Nutzer-Feedback
|
||||||
@@ -416,9 +416,9 @@
|
|||||||
</main>
|
</main>
|
||||||
|
|
||||||
<div id="site-footer"></div>
|
<div id="site-footer"></div>
|
||||||
<script src="assets/js/i18n-supporter.js?v=20260821i"></script>
|
<script src="assets/js/i18n-supporter.js?v=20260821j"></script>
|
||||||
<script src="assets/js/supporter.js?v=20260821i"></script>
|
<script src="assets/js/supporter.js?v=20260821j"></script>
|
||||||
<script src="assets/js/main.js?v=20260821i"></script>
|
<script src="assets/js/main.js?v=20260821j"></script>
|
||||||
<script>
|
<script>
|
||||||
(function () {
|
(function () {
|
||||||
const S = window.DogiSupporter;
|
const S = window.DogiSupporter;
|
||||||
|
|||||||
+30
-11
@@ -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: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" />
|
<meta name="twitter:image" content="https://dogfather-universe.com/assets/img/og-cover.jpg" />
|
||||||
|
|
||||||
<link rel="stylesheet" href="assets/css/main.css?v=20260821i" />
|
<link rel="stylesheet" href="assets/css/main.css?v=20260821j" />
|
||||||
<link rel="stylesheet" href="assets/css/theme-dogfather.css?v=20260821i" />
|
<link rel="stylesheet" href="assets/css/theme-dogfather.css?v=20260821j" />
|
||||||
<link rel="stylesheet" href="assets/css/theme-hasidog.css?v=20260821i" />
|
<link rel="stylesheet" href="assets/css/theme-hasidog.css?v=20260821j" />
|
||||||
<link rel="stylesheet" href="assets/css/theme-spicymedia.css?v=20260821i" />
|
<link rel="stylesheet" href="assets/css/theme-spicymedia.css?v=20260821j" />
|
||||||
</head>
|
</head>
|
||||||
<body class="mit-hintergrund" style="--page-bg:url('/assets/img/bg-dogfather.jpg');--page-bg-mobile:url('/assets/img/bg-dogfather-mobile.jpg');">
|
<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>
|
<div id="site-header"></div>
|
||||||
@@ -72,24 +72,31 @@
|
|||||||
</main>
|
</main>
|
||||||
|
|
||||||
<div id="site-footer"></div>
|
<div id="site-footer"></div>
|
||||||
<script src="assets/js/data-modis.js?v=20260821i"></script>
|
<script src="assets/js/data-modis.js?v=20260821j"></script>
|
||||||
<script src="assets/js/i18n-team-modis.js?v=20260821i"></script>
|
<script src="assets/js/i18n-team-modis.js?v=20260821j"></script>
|
||||||
<script src="assets/js/main.js?v=20260821i"></script>
|
<script src="assets/js/main.js?v=20260821j"></script>
|
||||||
<script>
|
<script>
|
||||||
document.addEventListener("DOMContentLoaded", () => {
|
document.addEventListener("DOMContentLoaded", () => {
|
||||||
const grid = document.getElementById("modis-grid");
|
const grid = document.getElementById("modis-grid");
|
||||||
if (!grid || !window.MODIS) return;
|
if (!grid || !window.MODIS) return;
|
||||||
const rest = window.MODIS.filter((m) => m.name !== "VanVan").sort((a, b) => a.rank - b.rank);
|
|
||||||
|
|
||||||
const feld = (x) => (window.dogiFeld ? window.dogiFeld(x) : x || "");
|
const feld = (x) => (window.dogiFeld ? window.dogiFeld(x) : x || "");
|
||||||
const t = (schluessel) => (window.dogiUebersetzen ? window.dogiUebersetzen(schluessel) : schluessel);
|
const t = (schluessel) => (window.dogiUebersetzen ? window.dogiUebersetzen(schluessel) : schluessel);
|
||||||
|
|
||||||
// Pyramide: eine Ebene pro eindeutigem Rang, oben schmal/groß, nach
|
|
||||||
// unten breiter/kompakter — genau wie ein Organigramm.
|
|
||||||
const raenge = [...new Set(rest.map((m) => m.rank))];
|
|
||||||
const MAX_STUFEN = 5; // fünf definierte Größenstufen im CSS
|
const MAX_STUFEN = 5; // fünf definierte Größenstufen im CSS
|
||||||
|
|
||||||
|
// 21.08.2026: rest/raenge werden jetzt INNERHALB von renderModis() frisch aus
|
||||||
|
// window.MODIS berechnet (statt einmalig beim Setup) -- nötig, damit über die
|
||||||
|
// Verwaltung nachträglich angehängte Modis (siehe dogiTeamLaden weiter unten)
|
||||||
|
// beim erneuten Aufruf von renderModis() tatsächlich mit auftauchen. Ein
|
||||||
|
// admin-gepflegter Modi ohne eigenen Rang fällt dabei (?? 999) ganz ans Ende
|
||||||
|
// der Pyramide, statt die Sortierung der bestehenden Ränge durcheinander zu
|
||||||
|
// bringen.
|
||||||
const renderModis = () => {
|
const renderModis = () => {
|
||||||
|
const rest = window.MODIS.filter((m) => m.name !== "VanVan").sort((a, b) => (a.rank ?? 999) - (b.rank ?? 999));
|
||||||
|
// Pyramide: eine Ebene pro eindeutigem Rang, oben schmal/groß, nach
|
||||||
|
// unten breiter/kompakter — genau wie ein Organigramm.
|
||||||
|
const raenge = [...new Set(rest.map((m) => m.rank))];
|
||||||
grid.innerHTML = raenge
|
grid.innerHTML = raenge
|
||||||
.map((rang, i) => {
|
.map((rang, i) => {
|
||||||
const stufe = Math.min(i + 1, MAX_STUFEN);
|
const stufe = Math.min(i + 1, MAX_STUFEN);
|
||||||
@@ -125,6 +132,18 @@ document.addEventListener("DOMContentLoaded", () => {
|
|||||||
};
|
};
|
||||||
renderModis();
|
renderModis();
|
||||||
document.addEventListener("dogi-sprache-geaendert", renderModis);
|
document.addEventListener("dogi-sprache-geaendert", renderModis);
|
||||||
|
|
||||||
|
// 21.08.2026: über die Verwaltung angelegte Modis ergänzen (Nutzer-Wunsch
|
||||||
|
// "überall wo Modis sind ... will ich dass ich die easy über meine
|
||||||
|
// Verwaltungsseite hinzufüge"). Rang/Rang-Bezeichnung kommen mit, damit sie
|
||||||
|
// sich sauber in die bestehende Pyramide einordnen.
|
||||||
|
if (window.dogiTeamLaden) {
|
||||||
|
window.dogiTeamLaden("modi").then((neue) => {
|
||||||
|
if (!neue.length) return;
|
||||||
|
window.MODIS = [...window.MODIS, ...neue];
|
||||||
|
renderModis();
|
||||||
|
});
|
||||||
|
}
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
</body>
|
</body>
|
||||||
|
|||||||
+20
-7
@@ -19,10 +19,10 @@
|
|||||||
<meta name="twitter:description" content="Die Scouts von Team Dogi bei Spicy Media." />
|
<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" />
|
<meta name="twitter:image" content="https://dogfather-universe.com/assets/img/og-cover.jpg" />
|
||||||
|
|
||||||
<link rel="stylesheet" href="assets/css/main.css?v=20260821i" />
|
<link rel="stylesheet" href="assets/css/main.css?v=20260821j" />
|
||||||
<link rel="stylesheet" href="assets/css/theme-dogfather.css?v=20260821i" />
|
<link rel="stylesheet" href="assets/css/theme-dogfather.css?v=20260821j" />
|
||||||
<link rel="stylesheet" href="assets/css/theme-hasidog.css?v=20260821i" />
|
<link rel="stylesheet" href="assets/css/theme-hasidog.css?v=20260821j" />
|
||||||
<link rel="stylesheet" href="assets/css/theme-spicymedia.css?v=20260821i" />
|
<link rel="stylesheet" href="assets/css/theme-spicymedia.css?v=20260821j" />
|
||||||
</head>
|
</head>
|
||||||
<body class="mit-hintergrund" style="--page-bg:url('/assets/img/bg-scouts.jpg');--page-bg-mobile:url('/assets/img/bg-scouts-mobile.jpg');">
|
<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>
|
<div id="site-header"></div>
|
||||||
@@ -49,9 +49,9 @@
|
|||||||
</main>
|
</main>
|
||||||
|
|
||||||
<div id="site-footer"></div>
|
<div id="site-footer"></div>
|
||||||
<script src="assets/js/data-scouts.js?v=20260821i"></script>
|
<script src="assets/js/data-scouts.js?v=20260821j"></script>
|
||||||
<script src="assets/js/i18n-team-scouts.js?v=20260821i"></script>
|
<script src="assets/js/i18n-team-scouts.js?v=20260821j"></script>
|
||||||
<script src="assets/js/main.js?v=20260821i"></script>
|
<script src="assets/js/main.js?v=20260821j"></script>
|
||||||
<script>
|
<script>
|
||||||
document.addEventListener("DOMContentLoaded", () => {
|
document.addEventListener("DOMContentLoaded", () => {
|
||||||
const grid = document.getElementById("scouts-grid");
|
const grid = document.getElementById("scouts-grid");
|
||||||
@@ -76,6 +76,19 @@ document.addEventListener("DOMContentLoaded", () => {
|
|||||||
};
|
};
|
||||||
renderScouts();
|
renderScouts();
|
||||||
document.addEventListener("dogi-sprache-geaendert", renderScouts);
|
document.addEventListener("dogi-sprache-geaendert", renderScouts);
|
||||||
|
|
||||||
|
// 21.08.2026: über die Verwaltung angelegte Scouts ergänzen (Nutzer-Wunsch
|
||||||
|
// "überall wo ... Scouts ... sind, will ich dass ich die easy über meine
|
||||||
|
// Verwaltungsseite hinzufüge"). Fügt sich einfach hinten an die von Hand
|
||||||
|
// gepflegte Liste an und rendert danach einmal neu -- die fest
|
||||||
|
// einprogrammierten Scouts (Patrick, Bananenstift) bleiben unverändert.
|
||||||
|
if (window.dogiTeamLaden) {
|
||||||
|
window.dogiTeamLaden("scout").then((neue) => {
|
||||||
|
if (!neue.length) return;
|
||||||
|
window.SCOUTS = [...window.SCOUTS, ...neue];
|
||||||
|
renderScouts();
|
||||||
|
});
|
||||||
|
}
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
</body>
|
</body>
|
||||||
|
|||||||
+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: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" />
|
<meta name="twitter:image" content="https://dogfather-universe.com/assets/img/og-cover.jpg" />
|
||||||
|
|
||||||
<link rel="stylesheet" href="assets/css/main.css?v=20260821i" />
|
<link rel="stylesheet" href="assets/css/main.css?v=20260821j" />
|
||||||
<link rel="stylesheet" href="assets/css/theme-dogfather.css?v=20260821i" />
|
<link rel="stylesheet" href="assets/css/theme-dogfather.css?v=20260821j" />
|
||||||
<link rel="stylesheet" href="assets/css/theme-hasidog.css?v=20260821i" />
|
<link rel="stylesheet" href="assets/css/theme-hasidog.css?v=20260821j" />
|
||||||
<link rel="stylesheet" href="assets/css/theme-spicymedia.css?v=20260821i" />
|
<link rel="stylesheet" href="assets/css/theme-spicymedia.css?v=20260821j" />
|
||||||
<style>
|
<style>
|
||||||
/* Gleiche Anpassung wie vanvan.html: der site-weit etwas größere
|
/* Gleiche Anpassung wie vanvan.html: der site-weit etwas größere
|
||||||
Fließtext wirkt bei einem so langen persönlichen Brief zu wuchtig —
|
Fließtext wirkt bei einem so langen persönlichen Brief zu wuchtig —
|
||||||
@@ -153,7 +153,7 @@
|
|||||||
</main>
|
</main>
|
||||||
|
|
||||||
<div id="site-footer"></div>
|
<div id="site-footer"></div>
|
||||||
<script src="assets/js/i18n-tt-muddi.js?v=20260821i"></script>
|
<script src="assets/js/i18n-tt-muddi.js?v=20260821j"></script>
|
||||||
<script src="assets/js/main.js?v=20260821i"></script>
|
<script src="assets/js/main.js?v=20260821j"></script>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</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: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" />
|
<meta name="twitter:image" content="https://dogfather-universe.com/assets/img/og-cover.jpg" />
|
||||||
|
|
||||||
<link rel="stylesheet" href="assets/css/main.css?v=20260821i" />
|
<link rel="stylesheet" href="assets/css/main.css?v=20260821j" />
|
||||||
<link rel="stylesheet" href="assets/css/theme-dogfather.css?v=20260821i" />
|
<link rel="stylesheet" href="assets/css/theme-dogfather.css?v=20260821j" />
|
||||||
<link rel="stylesheet" href="assets/css/theme-hasidog.css?v=20260821i" />
|
<link rel="stylesheet" href="assets/css/theme-hasidog.css?v=20260821j" />
|
||||||
<link rel="stylesheet" href="assets/css/theme-spicymedia.css?v=20260821i" />
|
<link rel="stylesheet" href="assets/css/theme-spicymedia.css?v=20260821j" />
|
||||||
<style>
|
<style>
|
||||||
/* Auf Wunsch: der site-weit vergrößerte Fließtext wirkte auf VanVans
|
/* Auf Wunsch: der site-weit vergrößerte Fließtext wirkte auf VanVans
|
||||||
eigener Seite zu groß (viel Text am Stück) — hier gezielt wieder
|
eigener Seite zu groß (viel Text am Stück) — hier gezielt wieder
|
||||||
@@ -175,7 +175,7 @@
|
|||||||
</main>
|
</main>
|
||||||
|
|
||||||
<div id="site-footer"></div>
|
<div id="site-footer"></div>
|
||||||
<script src="assets/js/i18n-vanvan.js?v=20260821i"></script>
|
<script src="assets/js/i18n-vanvan.js?v=20260821j"></script>
|
||||||
<script src="assets/js/main.js?v=20260821i"></script>
|
<script src="assets/js/main.js?v=20260821j"></script>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|||||||
+317
-6
@@ -19,10 +19,10 @@
|
|||||||
<link rel="apple-touch-icon" href="assets/img/apple-touch-icon-admin.png" />
|
<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="apple-mobile-web-app-title" content="DogiCrew-Verwaltung" />
|
||||||
<meta name="theme-color" content="#0e0a16" />
|
<meta name="theme-color" content="#0e0a16" />
|
||||||
<link rel="stylesheet" href="assets/css/main.css?v=20260821i" />
|
<link rel="stylesheet" href="assets/css/main.css?v=20260821j" />
|
||||||
<link rel="stylesheet" href="assets/css/theme-dogfather.css?v=20260821i" />
|
<link rel="stylesheet" href="assets/css/theme-dogfather.css?v=20260821j" />
|
||||||
<link rel="stylesheet" href="assets/css/theme-hasidog.css?v=20260821i" />
|
<link rel="stylesheet" href="assets/css/theme-hasidog.css?v=20260821j" />
|
||||||
<link rel="stylesheet" href="assets/css/theme-spicymedia.css?v=20260821i" />
|
<link rel="stylesheet" href="assets/css/theme-spicymedia.css?v=20260821j" />
|
||||||
<style>
|
<style>
|
||||||
/* =====================================================================
|
/* =====================================================================
|
||||||
verwaltung.html — Dogis privater Command-Center-Bereich.
|
verwaltung.html — Dogis privater Command-Center-Bereich.
|
||||||
@@ -858,6 +858,109 @@
|
|||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
|
<!-- ===== "Team verwalten" (21.08.2026) — Nutzer-Wunsch: "überall wo
|
||||||
|
Modis sind oder Scouts oder Manager, oder Creator, will ich dass ich
|
||||||
|
die easy über meine Verwaltungsseite hinzufüge und die automatisch
|
||||||
|
in der Website hinzugefügt werden ... auch mit Fotos und TikTok
|
||||||
|
Link". Ergänzt (überschreibt NICHT) die von Hand gepflegten
|
||||||
|
Einträge in assets/js/data-modis.js/data-scouts.js/data-creator.js/
|
||||||
|
data-manager.js -- neue Mitglieder erscheinen automatisch auf
|
||||||
|
team-modis.html/team-scouts.html/creator.html/manager.html UND auf
|
||||||
|
profil.html, exakt im selben Look wie die schon bestehenden (siehe
|
||||||
|
window.dogiTeamLaden() in main.js). Rolle/Bio/Zitat werden wie
|
||||||
|
"Event des Jahres" nur auf Deutsch eingetippt und beim Speichern
|
||||||
|
automatisch übersetzt. -->
|
||||||
|
<section class="section-tight" id="team-root" style="display:none;padding-top:0;">
|
||||||
|
<div class="container">
|
||||||
|
<h2 class="text-center" style="margin-bottom:.3rem;">🧑🤝🧑 Team verwalten</h2>
|
||||||
|
<p class="small muted text-center" style="max-width:64ch;margin:0 auto 1.4rem;">
|
||||||
|
Modis, Scouts, Creator und Manager — neue Mitglieder erscheinen automatisch auf der
|
||||||
|
passenden Seite (mit eigenem Profil, Foto und TikTok-Button), genau wie die schon
|
||||||
|
bestehenden. Rolle/Vorstellung/Zitat bitte auf Deutsch eintippen, der Rest wird beim
|
||||||
|
Speichern automatisch übersetzt.
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<div class="card vw-card vw-shine-card vw-noise" style="max-width:640px;margin:0 auto 2rem;padding:1.5rem;">
|
||||||
|
<h3 id="team-form-titel" style="margin-top:0;">Neues Mitglied</h3>
|
||||||
|
<input type="hidden" id="team-id" value="" />
|
||||||
|
<div class="field">
|
||||||
|
<label for="team-kategorie">Kategorie</label>
|
||||||
|
<select id="team-kategorie">
|
||||||
|
<option value="modi">Modi (Team Dogi)</option>
|
||||||
|
<option value="scout">Scout (Spicy Media)</option>
|
||||||
|
<option value="creator">Creator (Spicy Media)</option>
|
||||||
|
<option value="manager">Manager (Spicy Media)</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
<div class="field">
|
||||||
|
<label for="team-name">Name</label>
|
||||||
|
<input type="text" id="team-name" maxlength="60" placeholder="z. B. Foxy" />
|
||||||
|
</div>
|
||||||
|
<div class="field">
|
||||||
|
<label for="team-rolle">Rolle (Deutsch)</label>
|
||||||
|
<input type="text" id="team-rolle" maxlength="120" placeholder="z. B. Scout (Manager) — Rechte Hand" />
|
||||||
|
</div>
|
||||||
|
<div style="margin-bottom:1rem;text-align:center;">
|
||||||
|
<img id="team-foto-vorschau" alt="" style="width:140px;height:140px;object-fit:cover;border-radius:50%;background:var(--bg-alt);display:none;" />
|
||||||
|
</div>
|
||||||
|
<div class="field">
|
||||||
|
<label for="team-foto-input">Foto (optional, max. 5 MB, JPG/PNG/WebP)</label>
|
||||||
|
<input type="file" id="team-foto-input" accept="image/jpeg,image/png,image/webp" />
|
||||||
|
</div>
|
||||||
|
<div class="field">
|
||||||
|
<label for="team-tiktok">TikTok (Benutzername oder Link, optional)</label>
|
||||||
|
<input type="text" id="team-tiktok" maxlength="200" placeholder="z. B. @foxy123 oder https://tiktok.com/@foxy123" />
|
||||||
|
</div>
|
||||||
|
<div class="field">
|
||||||
|
<label for="team-bio">Kurze Vorstellung (Deutsch, optional)</label>
|
||||||
|
<textarea id="team-bio" maxlength="800" rows="3"></textarea>
|
||||||
|
</div>
|
||||||
|
<div class="field">
|
||||||
|
<label for="team-zitat">Zitat/Motto (Deutsch, optional)</label>
|
||||||
|
<input type="text" id="team-zitat" maxlength="200" />
|
||||||
|
</div>
|
||||||
|
<div id="team-rang-felder" style="display:none;">
|
||||||
|
<div class="grid grid-2" style="gap:.8rem;">
|
||||||
|
<div class="field">
|
||||||
|
<label for="team-rang">Rang (Pyramiden-Stufe)</label>
|
||||||
|
<input type="number" id="team-rang" min="1" max="99" />
|
||||||
|
</div>
|
||||||
|
<div class="field">
|
||||||
|
<label for="team-rang-label">Rang-Bezeichnung (Deutsch)</label>
|
||||||
|
<input type="text" id="team-rang-label" maxlength="40" placeholder="z. B. Rechte Hand" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<p class="small muted" style="margin:.3rem 0 1rem;">
|
||||||
|
Bestimmt die Ebene in der Modi-Pyramide — ohne Rang landet die Person automatisch
|
||||||
|
ganz unten.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
<div class="field">
|
||||||
|
<label for="team-reihenfolge">Reihenfolge (kleinere Zahl erscheint zuerst)</label>
|
||||||
|
<input type="number" id="team-reihenfolge" value="0" />
|
||||||
|
</div>
|
||||||
|
<label style="display:flex;align-items:center;gap:.6rem;margin:1rem 0;cursor:pointer;">
|
||||||
|
<input type="checkbox" id="team-aktiv" checked style="width:1.2rem;height:1.2rem;" />
|
||||||
|
<span>Aktiv — öffentlich sichtbar</span>
|
||||||
|
</label>
|
||||||
|
<div class="btn-row">
|
||||||
|
<button type="button" class="btn btn-primary" id="team-speichern" style="flex:1;">Speichern</button>
|
||||||
|
<button type="button" class="btn btn-outline" id="team-abbrechen" style="display:none;">Abbrechen</button>
|
||||||
|
</div>
|
||||||
|
<p class="small" id="team-status" style="margin-top:.6rem;"></p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="btn-row" style="justify-content:center;margin-bottom:1.4rem;" id="team-filter-bar">
|
||||||
|
<button type="button" class="filter-btn active" data-team-filter="alle">Alle</button>
|
||||||
|
<button type="button" class="filter-btn" data-team-filter="modi">Modi</button>
|
||||||
|
<button type="button" class="filter-btn" data-team-filter="scout">Scouts</button>
|
||||||
|
<button type="button" class="filter-btn" data-team-filter="creator">Creator</button>
|
||||||
|
<button type="button" class="filter-btn" data-team-filter="manager">Manager</button>
|
||||||
|
</div>
|
||||||
|
<div id="team-liste"></div>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
<!-- ===== Supporter-Liste ===== -->
|
<!-- ===== Supporter-Liste ===== -->
|
||||||
<section class="section-tight" id="supp-root" style="display:none;padding-top:0;">
|
<section class="section-tight" id="supp-root" style="display:none;padding-top:0;">
|
||||||
<div class="container" id="liste-ansicht">
|
<div class="container" id="liste-ansicht">
|
||||||
@@ -1032,8 +1135,8 @@
|
|||||||
</main>
|
</main>
|
||||||
|
|
||||||
<div id="site-footer"></div>
|
<div id="site-footer"></div>
|
||||||
<script src="assets/js/main.js?v=20260821i"></script>
|
<script src="assets/js/main.js?v=20260821j"></script>
|
||||||
<script src="assets/js/admin-auth.js?v=20260821i"></script>
|
<script src="assets/js/admin-auth.js?v=20260821j"></script>
|
||||||
<script>
|
<script>
|
||||||
(function () {
|
(function () {
|
||||||
const API_BASE_URL = "https://postfach.dogfather-universe.com";
|
const API_BASE_URL = "https://postfach.dogfather-universe.com";
|
||||||
@@ -1113,6 +1216,10 @@
|
|||||||
document.getElementById("testimonials-root").style.display = "";
|
document.getElementById("testimonials-root").style.display = "";
|
||||||
await ladeUndRenderTestimonials();
|
await ladeUndRenderTestimonials();
|
||||||
}
|
}
|
||||||
|
if (window.AdminAuth.sessionHasPermission(session, "TEAM_MANAGE")) {
|
||||||
|
document.getElementById("team-root").style.display = "";
|
||||||
|
await ladeUndRenderTeam();
|
||||||
|
}
|
||||||
const pollsBox = document.getElementById("box-abstimmungen");
|
const pollsBox = document.getElementById("box-abstimmungen");
|
||||||
if (window.AdminAuth.sessionHasPermission(session, "POLLS_MANAGE")) {
|
if (window.AdminAuth.sessionHasPermission(session, "POLLS_MANAGE")) {
|
||||||
pollsBox.style.display = "";
|
pollsBox.style.display = "";
|
||||||
@@ -1132,6 +1239,7 @@
|
|||||||
document.getElementById("events-root").style.display = "none";
|
document.getElementById("events-root").style.display = "none";
|
||||||
document.getElementById("special-event-root").style.display = "none";
|
document.getElementById("special-event-root").style.display = "none";
|
||||||
document.getElementById("testimonials-root").style.display = "none";
|
document.getElementById("testimonials-root").style.display = "none";
|
||||||
|
document.getElementById("team-root").style.display = "none";
|
||||||
document.getElementById("box-abstimmungen").style.display = "none";
|
document.getElementById("box-abstimmungen").style.display = "none";
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1548,6 +1656,209 @@
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* ===== "Team verwalten" — Modis/Scouts/Creator/Manager (21.08.2026) =====
|
||||||
|
Ein Formular für alle vier Kategorien (identische Felder), darunter
|
||||||
|
eine nach Kategorie filterbare Liste aller bereits angelegten
|
||||||
|
Mitglieder mit Bearbeiten/Löschen. Rolle/Bio/Zitat werden beim
|
||||||
|
Speichern serverseitig automatisch übersetzt (siehe routes/team.js) --
|
||||||
|
genau wie bei "Event des Jahres". Ein hochgeladenes Foto landet SOFORT
|
||||||
|
bei Auswahl auf dem Server (nicht erst bei "Speichern"), gleiches
|
||||||
|
Muster wie bei den Event-Bildern weiter oben. */
|
||||||
|
const TEAM_KATEGORIE_LABELS = { modi: "Modi", scout: "Scout", creator: "Creator", manager: "Manager" };
|
||||||
|
let teamHochgeladenesFoto = null;
|
||||||
|
let teamAktiverFilter = "alle";
|
||||||
|
|
||||||
|
function teamKarteHtml(m) {
|
||||||
|
const fotoHtml = m.foto
|
||||||
|
? `<img src="${m.foto}" alt="${escapeHtml(m.name)}" style="width:56px;height:56px;border-radius:50%;object-fit:cover;flex-shrink:0;">`
|
||||||
|
: `<span style="width:56px;height:56px;border-radius:50%;background:var(--bg-alt);display:flex;align-items:center;justify-content:center;font-weight:700;flex-shrink:0;">${escapeHtml((m.name || "?").charAt(0).toUpperCase())}</span>`;
|
||||||
|
return `<div class="card vw-card vw-shine-card" style="margin-bottom:1rem;padding:1.1rem 1.3rem;display:flex;gap:1rem;align-items:center;flex-wrap:wrap;" data-team-id="${m.id}">
|
||||||
|
${fotoHtml}
|
||||||
|
<div style="flex:1;min-width:180px;">
|
||||||
|
<strong>${escapeHtml(m.name)}</strong>
|
||||||
|
<span class="vw-overview-chip">${TEAM_KATEGORIE_LABELS[m.kategorie] || m.kategorie}</span>
|
||||||
|
<span class="vw-overview-chip" style="${m.status === "pausiert" ? "opacity:.6;" : ""}">${m.status === "pausiert" ? "⚪ Pausiert" : "🟢 Aktiv"}</span>
|
||||||
|
<div class="small" style="color:var(--text-muted);">${escapeHtml(m.rolle?.de || "")}</div>
|
||||||
|
${m.tiktok ? `<a href="${escapeHtml(m.tiktok)}" target="_blank" rel="noopener" class="small">♪ TikTok</a>` : ""}
|
||||||
|
</div>
|
||||||
|
<div class="btn-row" style="gap:.5rem;">
|
||||||
|
<button type="button" class="vw-refresh-btn team-bearbeiten-btn">Bearbeiten</button>
|
||||||
|
<button type="button" class="vw-refresh-btn team-loeschen-btn" style="color:#ff8a8a;">Löschen</button>
|
||||||
|
</div>
|
||||||
|
</div>`;
|
||||||
|
}
|
||||||
|
|
||||||
|
let teamAlleMitglieder = [];
|
||||||
|
|
||||||
|
function teamListeZeichnen() {
|
||||||
|
const listeEl = document.getElementById("team-liste");
|
||||||
|
const gefiltert = teamAktiverFilter === "alle" ? teamAlleMitglieder : teamAlleMitglieder.filter((m) => m.kategorie === teamAktiverFilter);
|
||||||
|
listeEl.innerHTML = gefiltert.length ? gefiltert.map(teamKarteHtml).join("") : `<p class="vw-leer">Noch keine Mitglieder in dieser Kategorie.</p>`;
|
||||||
|
|
||||||
|
listeEl.querySelectorAll(".team-bearbeiten-btn").forEach((btn) => {
|
||||||
|
btn.addEventListener("click", () => {
|
||||||
|
const id = btn.closest("[data-team-id]").dataset.teamId;
|
||||||
|
const m = teamAlleMitglieder.find((x) => x.id === id);
|
||||||
|
if (m) teamFormularBefuellen(m);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
listeEl.querySelectorAll(".team-loeschen-btn").forEach((btn) => {
|
||||||
|
btn.addEventListener("click", async () => {
|
||||||
|
const id = btn.closest("[data-team-id]").dataset.teamId;
|
||||||
|
const m = teamAlleMitglieder.find((x) => x.id === id);
|
||||||
|
if (!confirm(`${m?.name || "Dieses Mitglied"} unwiderruflich löschen?`)) return;
|
||||||
|
btn.disabled = true;
|
||||||
|
await window.AdminAuth.apiCall(API_BASE_URL, "/admin/team/delete", { id });
|
||||||
|
await ladeUndRenderTeam();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
async function ladeUndRenderTeam() {
|
||||||
|
const res = await window.AdminAuth.apiCall(API_BASE_URL, "/admin/team/list", {});
|
||||||
|
teamAlleMitglieder = res.ok ? (res.mitglieder || []) : [];
|
||||||
|
teamListeZeichnen();
|
||||||
|
}
|
||||||
|
|
||||||
|
function teamFormularZuruecksetzen() {
|
||||||
|
document.getElementById("team-id").value = "";
|
||||||
|
document.getElementById("team-form-titel").textContent = "Neues Mitglied";
|
||||||
|
document.getElementById("team-kategorie").value = "modi";
|
||||||
|
document.getElementById("team-name").value = "";
|
||||||
|
document.getElementById("team-rolle").value = "";
|
||||||
|
document.getElementById("team-tiktok").value = "";
|
||||||
|
document.getElementById("team-bio").value = "";
|
||||||
|
document.getElementById("team-zitat").value = "";
|
||||||
|
document.getElementById("team-rang").value = "";
|
||||||
|
document.getElementById("team-rang-label").value = "";
|
||||||
|
document.getElementById("team-reihenfolge").value = "0";
|
||||||
|
document.getElementById("team-aktiv").checked = true;
|
||||||
|
document.getElementById("team-foto-vorschau").style.display = "none";
|
||||||
|
document.getElementById("team-foto-input").value = "";
|
||||||
|
document.getElementById("team-abbrechen").style.display = "none";
|
||||||
|
document.getElementById("team-status").textContent = "";
|
||||||
|
teamHochgeladenesFoto = null;
|
||||||
|
teamRangFelderAnpassen();
|
||||||
|
}
|
||||||
|
|
||||||
|
function teamFormularBefuellen(m) {
|
||||||
|
document.getElementById("team-id").value = m.id;
|
||||||
|
document.getElementById("team-form-titel").textContent = `„${m.name}“ bearbeiten`;
|
||||||
|
document.getElementById("team-kategorie").value = m.kategorie;
|
||||||
|
document.getElementById("team-name").value = m.name;
|
||||||
|
document.getElementById("team-rolle").value = m.rolle?.de || "";
|
||||||
|
document.getElementById("team-tiktok").value = m.tiktok || "";
|
||||||
|
document.getElementById("team-bio").value = m.bio?.de || "";
|
||||||
|
document.getElementById("team-zitat").value = m.zitat?.de || "";
|
||||||
|
document.getElementById("team-rang").value = m.rang || "";
|
||||||
|
document.getElementById("team-rang-label").value = m.rangLabel?.de || "";
|
||||||
|
document.getElementById("team-reihenfolge").value = m.reihenfolge ?? 0;
|
||||||
|
document.getElementById("team-aktiv").checked = m.status !== "pausiert";
|
||||||
|
teamHochgeladenesFoto = m.foto || null;
|
||||||
|
const vorschau = document.getElementById("team-foto-vorschau");
|
||||||
|
if (teamHochgeladenesFoto) {
|
||||||
|
vorschau.src = teamHochgeladenesFoto;
|
||||||
|
vorschau.style.display = "";
|
||||||
|
} else {
|
||||||
|
vorschau.style.display = "none";
|
||||||
|
}
|
||||||
|
document.getElementById("team-abbrechen").style.display = "";
|
||||||
|
document.getElementById("team-status").textContent = "";
|
||||||
|
teamRangFelderAnpassen();
|
||||||
|
document.getElementById("team-root").scrollIntoView({ behavior: "smooth", block: "start" });
|
||||||
|
}
|
||||||
|
|
||||||
|
function teamRangFelderAnpassen() {
|
||||||
|
const istModi = document.getElementById("team-kategorie").value === "modi";
|
||||||
|
document.getElementById("team-rang-felder").style.display = istModi ? "" : "none";
|
||||||
|
}
|
||||||
|
document.getElementById("team-kategorie").addEventListener("change", teamRangFelderAnpassen);
|
||||||
|
document.getElementById("team-abbrechen").addEventListener("click", teamFormularZuruecksetzen);
|
||||||
|
|
||||||
|
document.getElementById("team-foto-input").addEventListener("change", async () => {
|
||||||
|
const input = document.getElementById("team-foto-input");
|
||||||
|
const datei = input.files?.[0];
|
||||||
|
if (!datei) return;
|
||||||
|
const statusEl = document.getElementById("team-status");
|
||||||
|
statusEl.textContent = "Foto wird hochgeladen …";
|
||||||
|
statusEl.style.color = "";
|
||||||
|
const form = new FormData();
|
||||||
|
form.append("image", datei);
|
||||||
|
const session = window.AdminAuth.getSession();
|
||||||
|
try {
|
||||||
|
const res = await fetch(`${API_BASE_URL}/admin/team/upload-photo`, {
|
||||||
|
method: "POST",
|
||||||
|
headers: session?.token ? { Authorization: `Bearer ${session.token}` } : {},
|
||||||
|
body: form,
|
||||||
|
});
|
||||||
|
const data = await res.json();
|
||||||
|
if (!data.ok) {
|
||||||
|
statusEl.textContent = data.error || "Foto-Upload fehlgeschlagen.";
|
||||||
|
statusEl.style.color = "#ff6b6b";
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
teamHochgeladenesFoto = data.url;
|
||||||
|
const vorschau = document.getElementById("team-foto-vorschau");
|
||||||
|
vorschau.src = data.url;
|
||||||
|
vorschau.style.display = "";
|
||||||
|
statusEl.textContent = "Foto hochgeladen — jetzt noch auf „Speichern“ klicken.";
|
||||||
|
} catch {
|
||||||
|
statusEl.textContent = "Foto-Upload fehlgeschlagen (keine Verbindung).";
|
||||||
|
statusEl.style.color = "#ff6b6b";
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
document.getElementById("team-speichern").addEventListener("click", async () => {
|
||||||
|
const statusEl = document.getElementById("team-status");
|
||||||
|
const name = document.getElementById("team-name").value.trim();
|
||||||
|
const rolle = document.getElementById("team-rolle").value.trim();
|
||||||
|
if (!name || !rolle) {
|
||||||
|
statusEl.textContent = "Name und Rolle sind Pflicht.";
|
||||||
|
statusEl.style.color = "#ff6b6b";
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const btn = document.getElementById("team-speichern");
|
||||||
|
btn.disabled = true;
|
||||||
|
statusEl.textContent = "Wird gespeichert und übersetzt …";
|
||||||
|
statusEl.style.color = "";
|
||||||
|
const id = document.getElementById("team-id").value || undefined;
|
||||||
|
const res = await window.AdminAuth.apiCall(API_BASE_URL, "/admin/team/save", {
|
||||||
|
id,
|
||||||
|
kategorie: document.getElementById("team-kategorie").value,
|
||||||
|
name,
|
||||||
|
rolle,
|
||||||
|
foto: teamHochgeladenesFoto,
|
||||||
|
tiktok: document.getElementById("team-tiktok").value.trim(),
|
||||||
|
bio: document.getElementById("team-bio").value.trim(),
|
||||||
|
zitat: document.getElementById("team-zitat").value.trim(),
|
||||||
|
rang: document.getElementById("team-rang").value,
|
||||||
|
rangLabel: document.getElementById("team-rang-label").value.trim(),
|
||||||
|
reihenfolge: document.getElementById("team-reihenfolge").value,
|
||||||
|
status: document.getElementById("team-aktiv").checked ? "aktiv" : "pausiert",
|
||||||
|
});
|
||||||
|
btn.disabled = false;
|
||||||
|
if (!res.ok) {
|
||||||
|
statusEl.textContent = res.error || "Speichern fehlgeschlagen.";
|
||||||
|
statusEl.style.color = "#ff6b6b";
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
statusEl.textContent = "✅ Gespeichert.";
|
||||||
|
statusEl.style.color = "#baf7d6";
|
||||||
|
teamFormularZuruecksetzen();
|
||||||
|
await ladeUndRenderTeam();
|
||||||
|
});
|
||||||
|
|
||||||
|
document.getElementById("team-filter-bar").addEventListener("click", (e) => {
|
||||||
|
const btn = e.target.closest(".filter-btn");
|
||||||
|
if (!btn) return;
|
||||||
|
teamAktiverFilter = btn.dataset.teamFilter;
|
||||||
|
document.querySelectorAll("#team-filter-bar .filter-btn").forEach((b) => b.classList.remove("active"));
|
||||||
|
btn.classList.add("active");
|
||||||
|
teamListeZeichnen();
|
||||||
|
});
|
||||||
|
|
||||||
|
teamRangFelderAnpassen();
|
||||||
|
|
||||||
// Nutzer-Wunsch 04.08.2026 (6. Runde): zwei getrennte Kisten/Formulare
|
// Nutzer-Wunsch 04.08.2026 (6. Runde): zwei getrennte Kisten/Formulare
|
||||||
// (Dogi + VanVan) statt einem gemeinsamen Feld — beide rufen denselben
|
// (Dogi + VanVan) statt einem gemeinsamen Feld — beide rufen denselben
|
||||||
// Login-Endpunkt auf, die Fehlermeldung erscheint jeweils nur in der
|
// 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: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" />
|
<meta name="twitter:image" content="https://dogfather-universe.com/assets/img/og-cover.jpg" />
|
||||||
|
|
||||||
<link rel="stylesheet" href="assets/css/main.css?v=20260821i" />
|
<link rel="stylesheet" href="assets/css/main.css?v=20260821j" />
|
||||||
<link rel="stylesheet" href="assets/css/theme-dogfather.css?v=20260821i" />
|
<link rel="stylesheet" href="assets/css/theme-dogfather.css?v=20260821j" />
|
||||||
<link rel="stylesheet" href="assets/css/theme-hasidog.css?v=20260821i" />
|
<link rel="stylesheet" href="assets/css/theme-hasidog.css?v=20260821j" />
|
||||||
<link rel="stylesheet" href="assets/css/theme-spicymedia.css?v=20260821i" />
|
<link rel="stylesheet" href="assets/css/theme-spicymedia.css?v=20260821j" />
|
||||||
</head>
|
</head>
|
||||||
<body class="mit-hintergrund" style="--page-bg:url('/assets/img/bg-werte.jpg');--page-bg-mobile:url('/assets/img/bg-werte-mobile.jpg');">
|
<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>
|
<div id="site-header"></div>
|
||||||
@@ -113,7 +113,7 @@
|
|||||||
</main>
|
</main>
|
||||||
|
|
||||||
<div id="site-footer"></div>
|
<div id="site-footer"></div>
|
||||||
<script src="assets/js/i18n-werte.js?v=20260821i"></script>
|
<script src="assets/js/i18n-werte.js?v=20260821j"></script>
|
||||||
<script src="assets/js/main.js?v=20260821i"></script>
|
<script src="assets/js/main.js?v=20260821j"></script>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|||||||
+6
-6
@@ -19,10 +19,10 @@
|
|||||||
<meta name="twitter:description" content="Die Geschichte von DogFather in einer interaktiven Zeitleiste." />
|
<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" />
|
<meta name="twitter:image" content="https://dogfather-universe.com/assets/img/og-cover.jpg" />
|
||||||
|
|
||||||
<link rel="stylesheet" href="assets/css/main.css?v=20260821i" />
|
<link rel="stylesheet" href="assets/css/main.css?v=20260821j" />
|
||||||
<link rel="stylesheet" href="assets/css/theme-dogfather.css?v=20260821i" />
|
<link rel="stylesheet" href="assets/css/theme-dogfather.css?v=20260821j" />
|
||||||
<link rel="stylesheet" href="assets/css/theme-hasidog.css?v=20260821i" />
|
<link rel="stylesheet" href="assets/css/theme-hasidog.css?v=20260821j" />
|
||||||
<link rel="stylesheet" href="assets/css/theme-spicymedia.css?v=20260821i" />
|
<link rel="stylesheet" href="assets/css/theme-spicymedia.css?v=20260821j" />
|
||||||
</head>
|
</head>
|
||||||
<body class="mit-hintergrund" style="--page-bg:url('/assets/img/bg-zeitreise.jpg');--page-bg-mobile:url('/assets/img/bg-zeitreise-mobile.jpg');">
|
<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>
|
<div id="site-header"></div>
|
||||||
@@ -237,8 +237,8 @@
|
|||||||
</main>
|
</main>
|
||||||
|
|
||||||
<div id="site-footer"></div>
|
<div id="site-footer"></div>
|
||||||
<script src="assets/js/i18n-zeitreise.js?v=20260821i"></script>
|
<script src="assets/js/i18n-zeitreise.js?v=20260821j"></script>
|
||||||
<script src="assets/js/main.js?v=20260821i"></script>
|
<script src="assets/js/main.js?v=20260821j"></script>
|
||||||
<script>
|
<script>
|
||||||
/* Wachstums-Reveal: Äste blenden sich beim Scrollen sanft ein — passt
|
/* Wachstums-Reveal: Äste blenden sich beim Scrollen sanft ein — passt
|
||||||
zum "wachsenden Baum"-Konzept dieser Seite. Rein page-lokal, betrifft
|
zum "wachsenden Baum"-Konzept dieser Seite. Rein page-lokal, betrifft
|
||||||
|
|||||||
Reference in New Issue
Block a user