Spotify-Links, Live-Status-Feature (grüner Punkt) mit Owner-Toggle
This commit is contained in:
@@ -425,6 +425,29 @@ footer.site-footer {
|
|||||||
font-size: .8rem; color: var(--text-muted);
|
font-size: .8rem; color: var(--text-muted);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* ---------- Live-Status-Punkt ---------- */
|
||||||
|
.live-dot {
|
||||||
|
display: inline-block;
|
||||||
|
width: 10px;
|
||||||
|
height: 10px;
|
||||||
|
border-radius: 50%;
|
||||||
|
background: #6b7280; /* grau = aktuell offline */
|
||||||
|
margin-right: .45rem;
|
||||||
|
vertical-align: middle;
|
||||||
|
}
|
||||||
|
.live-dot.is-live {
|
||||||
|
background: #22c55e; /* grün = live */
|
||||||
|
animation: live-pulse 1.6s infinite;
|
||||||
|
}
|
||||||
|
@keyframes live-pulse {
|
||||||
|
0% { box-shadow: 0 0 0 0 rgba(34, 197, 94, .55); }
|
||||||
|
70% { box-shadow: 0 0 0 10px rgba(34, 197, 94, 0); }
|
||||||
|
100% { box-shadow: 0 0 0 0 rgba(34, 197, 94, 0); }
|
||||||
|
}
|
||||||
|
@media (prefers-reduced-motion: reduce) {
|
||||||
|
.live-dot.is-live { animation: none; }
|
||||||
|
}
|
||||||
|
|
||||||
/* ---------- Badges / Hierarchy ---------- */
|
/* ---------- Badges / Hierarchy ---------- */
|
||||||
.badge { display: inline-block; font-size: .7rem; text-transform: uppercase; letter-spacing: .06em; padding: .2rem .6rem; border-radius: 999px; background: var(--bg-alt); border: 1px solid var(--border); color: var(--text-muted); }
|
.badge { display: inline-block; font-size: .7rem; text-transform: uppercase; letter-spacing: .06em; padding: .2rem .6rem; border-radius: 999px; background: var(--bg-alt); border: 1px solid var(--border); color: var(--text-muted); }
|
||||||
.badge.rank-1 { border-color: var(--accent); color: var(--accent); }
|
.badge.rank-1 { border-color: var(--accent); color: var(--accent); }
|
||||||
|
|||||||
@@ -24,6 +24,8 @@
|
|||||||
POST /admin/roles/list { ownerCode } -> Helfer-Codes auflisten (nur Owner)
|
POST /admin/roles/list { ownerCode } -> Helfer-Codes auflisten (nur Owner)
|
||||||
POST /admin/roles/create { ownerCode, role, label } -> neuen Helfer-Code erzeugen (nur Owner)
|
POST /admin/roles/create { ownerCode, role, label } -> neuen Helfer-Code erzeugen (nur Owner)
|
||||||
POST /admin/roles/revoke { ownerCode, keyId } -> Helfer-Code entziehen (nur Owner)
|
POST /admin/roles/revoke { ownerCode, keyId } -> Helfer-Code entziehen (nur Owner)
|
||||||
|
GET /live-status -> öffentlich: {live, updatedAt}
|
||||||
|
POST /live-status/set { ownerCode, live } -> Live-Status umschalten (nur Owner)
|
||||||
GET /health -> einfacher Check ob der Worker läuft
|
GET /health -> einfacher Check ob der Worker läuft
|
||||||
|
|
||||||
Secrets/Bindings (siehe wrangler.toml + README.md):
|
Secrets/Bindings (siehe wrangler.toml + README.md):
|
||||||
@@ -33,6 +35,7 @@
|
|||||||
|
|
||||||
const ALLOWED_TYPES = ["creator", "scout", "kooperation"];
|
const ALLOWED_TYPES = ["creator", "scout", "kooperation"];
|
||||||
const ROLE_KEY_PREFIX = "role:";
|
const ROLE_KEY_PREFIX = "role:";
|
||||||
|
const LIVE_STATUS_KEY = "meta:live";
|
||||||
|
|
||||||
function corsHeaders() {
|
function corsHeaders() {
|
||||||
// TODO: sobald die Domain final steht, statt "*" die echte Domain eintragen
|
// TODO: sobald die Domain final steht, statt "*" die echte Domain eintragen
|
||||||
@@ -40,7 +43,7 @@ function corsHeaders() {
|
|||||||
// an die API senden kann.
|
// an die API senden kann.
|
||||||
return {
|
return {
|
||||||
"Access-Control-Allow-Origin": "*",
|
"Access-Control-Allow-Origin": "*",
|
||||||
"Access-Control-Allow-Methods": "POST, OPTIONS",
|
"Access-Control-Allow-Methods": "GET, POST, OPTIONS",
|
||||||
"Access-Control-Allow-Headers": "Content-Type",
|
"Access-Control-Allow-Headers": "Content-Type",
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@@ -215,6 +218,30 @@ async function handleRolesRevoke(request, env) {
|
|||||||
return json({ ok: true });
|
return json({ ok: true });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async function handleLiveStatusGet(env) {
|
||||||
|
const raw = await env.BEWERBUNGEN.get(LIVE_STATUS_KEY);
|
||||||
|
const data = raw ? JSON.parse(raw) : { live: false, updatedAt: null };
|
||||||
|
return json({ ok: true, ...data });
|
||||||
|
}
|
||||||
|
|
||||||
|
async function handleLiveStatusSet(request, env) {
|
||||||
|
let body;
|
||||||
|
try {
|
||||||
|
body = await request.json();
|
||||||
|
} catch {
|
||||||
|
return json({ ok: false, error: "Ungültiges JSON." }, 400);
|
||||||
|
}
|
||||||
|
if (!(await requireOwner(body, env))) {
|
||||||
|
return json({ ok: false, error: "Nur Dogi darf den Live-Status ändern." }, 401);
|
||||||
|
}
|
||||||
|
|
||||||
|
const live = !!(body && body.live);
|
||||||
|
const record = { live, updatedAt: new Date().toISOString() };
|
||||||
|
await env.BEWERBUNGEN.put(LIVE_STATUS_KEY, JSON.stringify(record));
|
||||||
|
|
||||||
|
return json({ ok: true, ...record });
|
||||||
|
}
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
async fetch(request, env) {
|
async fetch(request, env) {
|
||||||
const url = new URL(request.url);
|
const url = new URL(request.url);
|
||||||
@@ -247,6 +274,14 @@ export default {
|
|||||||
return handleRolesRevoke(request, env);
|
return handleRolesRevoke(request, env);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (url.pathname === "/live-status" && request.method === "GET") {
|
||||||
|
return handleLiveStatusGet(env);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (url.pathname === "/live-status/set" && request.method === "POST") {
|
||||||
|
return handleLiveStatusSet(request, env);
|
||||||
|
}
|
||||||
|
|
||||||
return json({ ok: false, error: "Not found." }, 404);
|
return json({ ok: false, error: "Not found." }, 404);
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|||||||
+1
-1
@@ -82,7 +82,7 @@
|
|||||||
<div class="container">
|
<div class="container">
|
||||||
<h2 class="text-center">Nebenprojekte & Leidenschaften</h2>
|
<h2 class="text-center">Nebenprojekte & Leidenschaften</h2>
|
||||||
<div class="grid grid-3">
|
<div class="grid grid-3">
|
||||||
<div class="card"><h3>🎵 Musik-Produzent</h3><p>Eigene Songs unter dem Namen „Hasidog" auf Spotify.</p></div>
|
<div class="card"><h3>🎵 Musik-Produzent</h3><p>Eigene Songs unter dem Namen „Hasidog" auf <a href="https://open.spotify.com/intl-de/artist/2INiE1VVJGUQdEURsltuJU" target="_blank" rel="noopener">Spotify</a>.</p></div>
|
||||||
<div class="card"><h3>🧸 Merchandise-Creator</h3><p>Tassen, Decken, Popsockets, Schlüsselanhänger, Sticker.</p></div>
|
<div class="card"><h3>🧸 Merchandise-Creator</h3><p>Tassen, Decken, Popsockets, Schlüsselanhänger, Sticker.</p></div>
|
||||||
<div class="card"><h3>📖 Autor</h3><p>Ein Buch über Hasidog & Casper.</p></div>
|
<div class="card"><h3>📖 Autor</h3><p>Ein Buch über Hasidog & Casper.</p></div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
+2
-2
@@ -41,7 +41,7 @@
|
|||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<h2>Was ist HasiDog?</h2>
|
<h2>Was ist HasiDog?</h2>
|
||||||
<p>HasiDog ist mehr als ein Spitzname — es ist die verspielte, familiäre Marke rund um Dogfathers Community. Unter dem Namen „Hasidog" erscheint auch die eigene Musik auf Spotify.</p>
|
<p>HasiDog ist mehr als ein Spitzname — es ist die verspielte, familiäre Marke rund um Dogfathers Community. Unter dem Namen „Hasidog" erscheint auch die eigene Musik auf <a href="https://open.spotify.com/intl-de/artist/2INiE1VVJGUQdEURsltuJU" target="_blank" rel="noopener">Spotify</a>.</p>
|
||||||
<div class="btn-row">
|
<div class="btn-row">
|
||||||
<a class="btn btn-primary" href="casper.html">Zur Casper-Seite 🐾</a>
|
<a class="btn btn-primary" href="casper.html">Zur Casper-Seite 🐾</a>
|
||||||
<a class="btn btn-outline" href="community.html">Zur Community</a>
|
<a class="btn btn-outline" href="community.html">Zur Community</a>
|
||||||
@@ -54,7 +54,7 @@
|
|||||||
<div class="container">
|
<div class="container">
|
||||||
<h2 class="text-center">Die HasiDog-Familie</h2>
|
<h2 class="text-center">Die HasiDog-Familie</h2>
|
||||||
<div class="grid grid-3">
|
<div class="grid grid-3">
|
||||||
<div class="card text-center"><h3>🎵 Musik</h3><p>Songs unter dem Namen „Hasidog" auf Spotify.</p></div>
|
<div class="card text-center"><h3>🎵 Musik</h3><p>Songs unter dem Namen „Hasidog" auf <a href="https://open.spotify.com/intl-de/artist/2INiE1VVJGUQdEURsltuJU" target="_blank" rel="noopener">Spotify</a>.</p></div>
|
||||||
<div class="card text-center"><h3>🐕 Casper</h3><p>Der Husky, der zum Gesicht der HasiDog-Welt wurde — eigene Seite: <a href="casper.html">Casper</a>.</p></div>
|
<div class="card text-center"><h3>🐕 Casper</h3><p>Der Husky, der zum Gesicht der HasiDog-Welt wurde — eigene Seite: <a href="casper.html">Casper</a>.</p></div>
|
||||||
<div class="card text-center"><h3>📖 Buch</h3><p>„Hasidog & Casper" — die Geschichte in Buchform.</p></div>
|
<div class="card text-center"><h3>📖 Buch</h3><p>„Hasidog & Casper" — die Geschichte in Buchform.</p></div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
+24
-1
@@ -34,7 +34,7 @@
|
|||||||
<p class="lead worlds">Drei Welten, eine Familie: Team Dogi.</p>
|
<p class="lead worlds">Drei Welten, eine Familie: Team Dogi.</p>
|
||||||
|
|
||||||
<div class="card" style="max-width:420px;margin:1.4rem auto 0;padding:1rem 1.4rem;">
|
<div class="card" style="max-width:420px;margin:1.4rem auto 0;padding:1rem 1.4rem;">
|
||||||
<span class="badge rank-1">🔴 Live-Status</span>
|
<span class="badge rank-1"><span class="live-dot" id="live-dot"></span><span id="live-text">Aktuell offline</span></span>
|
||||||
<p class="small" style="margin:.5rem 0 0;">Dogfather geht <strong>jeden Tag ab 20 Uhr</strong> live — oft schon tagsüber, zu wechselnden Zeiten. <a href="streamplan.html">Streamplan ansehen →</a></p>
|
<p class="small" style="margin:.5rem 0 0;">Dogfather geht <strong>jeden Tag ab 20 Uhr</strong> live — oft schon tagsüber, zu wechselnden Zeiten. <a href="streamplan.html">Streamplan ansehen →</a></p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@@ -167,7 +167,30 @@
|
|||||||
<script src="assets/js/data-aktionen.js"></script>
|
<script src="assets/js/data-aktionen.js"></script>
|
||||||
<script src="assets/js/main.js"></script>
|
<script src="assets/js/main.js"></script>
|
||||||
<script>
|
<script>
|
||||||
|
// Gleiche Worker-URL wie in assets/js/forms.js / postfach.html eintragen,
|
||||||
|
// sobald der Cloudflare Worker deployed ist.
|
||||||
|
const API_BASE_URL = "";
|
||||||
|
|
||||||
document.addEventListener("DOMContentLoaded", () => {
|
document.addEventListener("DOMContentLoaded", () => {
|
||||||
|
// Live-Status: grüner Punkt, wenn Dogfather gerade live ist
|
||||||
|
const liveDot = document.getElementById("live-dot");
|
||||||
|
const liveText = document.getElementById("live-text");
|
||||||
|
if (API_BASE_URL && liveDot && liveText) {
|
||||||
|
fetch(`${API_BASE_URL}/live-status`)
|
||||||
|
.then((res) => res.json())
|
||||||
|
.then((data) => {
|
||||||
|
if (data && data.live) {
|
||||||
|
liveDot.classList.add("is-live");
|
||||||
|
liveText.textContent = "Live jetzt!";
|
||||||
|
} else {
|
||||||
|
liveText.textContent = "Aktuell offline";
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.catch(() => {
|
||||||
|
liveText.textContent = "Aktuell offline";
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
// Stats: Team-Größe aus den Datenobjekten berechnen
|
// Stats: Team-Größe aus den Datenobjekten berechnen
|
||||||
const teamCount = (window.MODIS || []).length + (window.SCOUTS || []).length;
|
const teamCount = (window.MODIS || []).length + (window.SCOUTS || []).length;
|
||||||
const statTeam = document.getElementById("stat-team");
|
const statTeam = document.getElementById("stat-team");
|
||||||
|
|||||||
+5
-5
@@ -41,13 +41,13 @@
|
|||||||
<h3>🎥 TikTok</h3>
|
<h3>🎥 TikTok</h3>
|
||||||
<p>@dogfather0804</p>
|
<p>@dogfather0804</p>
|
||||||
</a>
|
</a>
|
||||||
<a class="card text-center" href="#" target="_blank" rel="noopener">
|
<a class="card text-center" href="https://open.spotify.com/intl-de/artist/2INiE1VVJGUQdEURsltuJU" target="_blank" rel="noopener">
|
||||||
<h3>🎵 Spotify — Hasidog</h3>
|
<h3>🎵 Spotify — Hasidog</h3>
|
||||||
<p class="small muted">TODO: Spotify-Link ergänzen</p>
|
<p>Zum Artist-Profil</p>
|
||||||
</a>
|
</a>
|
||||||
<a class="card text-center" href="#" target="_blank" rel="noopener">
|
<a class="card text-center" href="https://open.spotify.com/intl-de/artist/4J9PcKZBkh6V8dVY3CGliw" target="_blank" rel="noopener">
|
||||||
<h3>🎵 Spotify — Van-Van</h3>
|
<h3>🎵 Spotify — Van-Van</h3>
|
||||||
<p class="small muted">TODO: Spotify-Link ergänzen</p>
|
<p>Zum Artist-Profil</p>
|
||||||
</a>
|
</a>
|
||||||
<a class="card text-center" href="#" target="_blank" rel="noopener">
|
<a class="card text-center" href="#" target="_blank" rel="noopener">
|
||||||
<h3>💬 Discord</h3>
|
<h3>💬 Discord</h3>
|
||||||
@@ -83,7 +83,7 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="todo-note">
|
<div class="todo-note">
|
||||||
<strong>TODO:</strong> Vorhandenen QR-Code <code>scout-qr-1785422377936.png</code> nach <code>assets/qr/scout-bewerbung-qr.png</code> kopieren, sobald die Datei bereitsteht. Restliche Links (Spotify, Discord, Shop) mit echten URLs befüllen.
|
<strong>TODO:</strong> Vorhandenen QR-Code <code>scout-qr-1785422377936.png</code> nach <code>assets/qr/scout-bewerbung-qr.png</code> kopieren, sobald die Datei bereitsteht. Restliche Links (Discord, Shop) mit echten URLs befüllen — Spotify ist bereits verlinkt.
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
|
|||||||
@@ -64,6 +64,21 @@
|
|||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
|
<!-- ===== Live-Status umschalten (NUR für Dogi/Owner sichtbar) ===== -->
|
||||||
|
<section class="section-tight" id="live-toggle-section" style="display:none;">
|
||||||
|
<div class="container text-center">
|
||||||
|
<h2>Live-Status</h2>
|
||||||
|
<p class="lead" style="max-width:50ch;margin:0 auto;">Steuert den grünen Punkt auf der Startseite.</p>
|
||||||
|
<div class="card" style="max-width:360px;margin:1rem auto 0;">
|
||||||
|
<span class="badge rank-1"><span class="live-dot" id="admin-live-dot"></span><span id="admin-live-text">Aktuell offline</span></span>
|
||||||
|
<div class="btn-row" style="justify-content:center;margin-top:1rem;">
|
||||||
|
<button class="btn btn-primary" id="go-live-btn" type="button">🟢 Live schalten</button>
|
||||||
|
<button class="btn btn-outline" id="go-offline-btn" type="button">Offline schalten</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
<!-- ===== Rollen-Verwaltung (NUR für Dogi/Owner sichtbar) ===== -->
|
<!-- ===== Rollen-Verwaltung (NUR für Dogi/Owner sichtbar) ===== -->
|
||||||
<section id="admin-section" style="display:none;">
|
<section id="admin-section" style="display:none;">
|
||||||
<div class="container">
|
<div class="container">
|
||||||
@@ -122,6 +137,11 @@ document.addEventListener("DOMContentLoaded", () => {
|
|||||||
const roleCreateStatus = document.getElementById("role-create-status");
|
const roleCreateStatus = document.getElementById("role-create-status");
|
||||||
const newCodeReveal = document.getElementById("new-code-reveal");
|
const newCodeReveal = document.getElementById("new-code-reveal");
|
||||||
const rolesList = document.getElementById("roles-list");
|
const rolesList = document.getElementById("roles-list");
|
||||||
|
const liveToggleSection = document.getElementById("live-toggle-section");
|
||||||
|
const adminLiveDot = document.getElementById("admin-live-dot");
|
||||||
|
const adminLiveText = document.getElementById("admin-live-text");
|
||||||
|
const goLiveBtn = document.getElementById("go-live-btn");
|
||||||
|
const goOfflineBtn = document.getElementById("go-offline-btn");
|
||||||
|
|
||||||
if (!API_BASE_URL) {
|
if (!API_BASE_URL) {
|
||||||
notConfigured.style.display = "block";
|
notConfigured.style.display = "block";
|
||||||
@@ -178,9 +198,12 @@ document.addEventListener("DOMContentLoaded", () => {
|
|||||||
|
|
||||||
if (data.isOwner) {
|
if (data.isOwner) {
|
||||||
adminSection.style.display = "block";
|
adminSection.style.display = "block";
|
||||||
|
liveToggleSection.style.display = "block";
|
||||||
loadRoles();
|
loadRoles();
|
||||||
|
loadLiveStatus();
|
||||||
} else {
|
} else {
|
||||||
adminSection.style.display = "none";
|
adminSection.style.display = "none";
|
||||||
|
liveToggleSection.style.display = "none";
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
} catch {
|
} catch {
|
||||||
@@ -234,6 +257,38 @@ document.addEventListener("DOMContentLoaded", () => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function renderLiveStatus(live) {
|
||||||
|
adminLiveDot.classList.toggle("is-live", !!live);
|
||||||
|
adminLiveText.textContent = live ? "Live jetzt!" : "Aktuell offline";
|
||||||
|
}
|
||||||
|
|
||||||
|
async function loadLiveStatus() {
|
||||||
|
try {
|
||||||
|
const res = await fetch(`${API_BASE_URL}/live-status`);
|
||||||
|
const data = await res.json();
|
||||||
|
if (res.ok && data.ok) renderLiveStatus(data.live);
|
||||||
|
} catch {
|
||||||
|
// still, keine Blockade der restlichen Seite
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function setLiveStatus(live) {
|
||||||
|
try {
|
||||||
|
const res = await fetch(`${API_BASE_URL}/live-status/set`, {
|
||||||
|
method: "POST",
|
||||||
|
headers: { "Content-Type": "application/json" },
|
||||||
|
body: JSON.stringify({ ownerCode: session.code, live }),
|
||||||
|
});
|
||||||
|
const data = await res.json();
|
||||||
|
if (res.ok && data.ok) renderLiveStatus(data.live);
|
||||||
|
} catch {
|
||||||
|
// keine Blockade
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
goLiveBtn.addEventListener("click", () => setLiveStatus(true));
|
||||||
|
goOfflineBtn.addEventListener("click", () => setLiveStatus(false));
|
||||||
|
|
||||||
form.addEventListener("submit", async (e) => {
|
form.addEventListener("submit", async (e) => {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
if (!API_BASE_URL) {
|
if (!API_BASE_URL) {
|
||||||
@@ -255,6 +310,7 @@ document.addEventListener("DOMContentLoaded", () => {
|
|||||||
sessionBar.style.display = "none";
|
sessionBar.style.display = "none";
|
||||||
inboxSection.style.display = "none";
|
inboxSection.style.display = "none";
|
||||||
adminSection.style.display = "none";
|
adminSection.style.display = "none";
|
||||||
|
liveToggleSection.style.display = "none";
|
||||||
status.textContent = "";
|
status.textContent = "";
|
||||||
status.className = "form-status";
|
status.className = "form-status";
|
||||||
});
|
});
|
||||||
|
|||||||
+2
-2
@@ -42,7 +42,7 @@
|
|||||||
<div>
|
<div>
|
||||||
<h2>Wer ist VanVan?</h2>
|
<h2>Wer ist VanVan?</h2>
|
||||||
<p>VanVan ist die rechte Hand von Dogfather — und seine beste Freundin. Sie bekommt täglich die Neuigkeiten, Aufgaben und Infos direkt von ihm und verteilt bzw. delegiert sie im Team, oder organisiert gemeinsam mit Diene.</p>
|
<p>VanVan ist die rechte Hand von Dogfather — und seine beste Freundin. Sie bekommt täglich die Neuigkeiten, Aufgaben und Infos direkt von ihm und verteilt bzw. delegiert sie im Team, oder organisiert gemeinsam mit Diene.</p>
|
||||||
<p>Neben ihrer Rolle im Team ist VanVan auch Musik-Produzentin — ihre Songs erscheinen unter dem Namen <strong>Van-Van</strong> auf Spotify.</p>
|
<p>Neben ihrer Rolle im Team ist VanVan auch Musik-Produzentin — ihre Songs erscheinen unter dem Namen <strong>Van-Van</strong> auf <a href="https://open.spotify.com/intl-de/artist/4J9PcKZBkh6V8dVY3CGliw" target="_blank" rel="noopener">Spotify</a>.</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
@@ -53,7 +53,7 @@
|
|||||||
<div class="grid grid-3">
|
<div class="grid grid-3">
|
||||||
<div class="card text-center"><h3>🩵 Vertrauen</h3><p>Die engste Vertraute von Dogfather im Team.</p></div>
|
<div class="card text-center"><h3>🩵 Vertrauen</h3><p>Die engste Vertraute von Dogfather im Team.</p></div>
|
||||||
<div class="card text-center"><h3>🗂️ Organisation</h3><p>Behält den Überblick und delegiert an das richtige Teammitglied.</p></div>
|
<div class="card text-center"><h3>🗂️ Organisation</h3><p>Behält den Überblick und delegiert an das richtige Teammitglied.</p></div>
|
||||||
<div class="card text-center"><h3>🎵 Musik</h3><p>Produziert eigene Songs als „Van-Van" auf Spotify.</p></div>
|
<div class="card text-center"><h3>🎵 Musik</h3><p>Produziert eigene Songs als „Van-Van" auf <a href="https://open.spotify.com/intl-de/artist/4J9PcKZBkh6V8dVY3CGliw" target="_blank" rel="noopener">Spotify</a>.</p></div>
|
||||||
</div>
|
</div>
|
||||||
<div class="todo-note">
|
<div class="todo-note">
|
||||||
<strong>TODO:</strong> Persönliches Zitat, Story und Social-Links von VanVan noch einholen (siehe Masterplan-Fragenkatalog, Block Team).
|
<strong>TODO:</strong> Persönliches Zitat, Story und Social-Links von VanVan noch einholen (siehe Masterplan-Fragenkatalog, Block Team).
|
||||||
|
|||||||
Reference in New Issue
Block a user