Migrate CMS from Sanity to self-hosted Directus, add Impressum + Datenschutz
- Replace src/lib/sanity.ts with src/lib/directus.ts (REST API client) - Update all 9 pages to use Directus field names and imageUrl() - Add Impressum (§5 TMG) and Datenschutz (DSGVO) pages - Update .env.example for Directus URL + token Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -1,5 +1,5 @@
|
||||
# Sanity-Projekt-ID — aus sanity.io/manage
|
||||
PUBLIC_SANITY_PROJECT_ID=xxxxxxxx
|
||||
# Directus CMS URL
|
||||
DIRECTUS_URL=https://cms.kitafreunde-regenbogen.de
|
||||
|
||||
# Dataset — in der Regel "production"
|
||||
PUBLIC_SANITY_DATASET=production
|
||||
# Directus API token (read-only, used at build time)
|
||||
DIRECTUS_TOKEN=your-token-here
|
||||
|
||||
79
src/lib/directus.ts
Normal file
79
src/lib/directus.ts
Normal file
@@ -0,0 +1,79 @@
|
||||
const DIRECTUS_URL = import.meta.env.DIRECTUS_URL ?? "https://cms.kitafreunde-regenbogen.de";
|
||||
const DIRECTUS_TOKEN = import.meta.env.DIRECTUS_TOKEN ?? "";
|
||||
|
||||
async function fetchDirectus<T>(path: string): Promise<T | null> {
|
||||
try {
|
||||
const res = await fetch(`${DIRECTUS_URL}${path}`, {
|
||||
headers: DIRECTUS_TOKEN ? { Authorization: `Bearer ${DIRECTUS_TOKEN}` } : {},
|
||||
});
|
||||
if (!res.ok) return null;
|
||||
const json = await res.json();
|
||||
return json.data ?? null;
|
||||
} catch {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
export function imageUrl(fileId: string | null | undefined, width = 800): string {
|
||||
if (!fileId) return "";
|
||||
return `${DIRECTUS_URL}/assets/${fileId}?width=${width}&format=webp`;
|
||||
}
|
||||
|
||||
export async function getSettings() {
|
||||
return fetchDirectus<{
|
||||
contact_email: string; contact_phone: string;
|
||||
iban: string; bic: string; bank_name: string;
|
||||
member_fee_active: number; member_fee_supporting: number; member_fee_company: number;
|
||||
stats_members: number; stats_funded: number;
|
||||
social_instagram: string; social_facebook: string;
|
||||
}>("/items/site_settings");
|
||||
}
|
||||
|
||||
export async function getFeaturedProjects() {
|
||||
const data = await fetchDirectus<any[]>(
|
||||
"/items/projects?filter[featured][_eq]=true&filter[status][_neq]=archived&sort=-date&limit=6"
|
||||
);
|
||||
return data ?? [];
|
||||
}
|
||||
|
||||
export async function getAllProjects() {
|
||||
const data = await fetchDirectus<any[]>(
|
||||
"/items/projects?filter[status][_neq]=archived&sort[]=sort&sort[]=-date"
|
||||
);
|
||||
return data ?? [];
|
||||
}
|
||||
|
||||
export async function getProject(slug: string) {
|
||||
const data = await fetchDirectus<any[]>(
|
||||
`/items/projects?filter[slug][_eq]=${encodeURIComponent(slug)}&limit=1`
|
||||
);
|
||||
return data?.[0] ?? null;
|
||||
}
|
||||
|
||||
export async function getLatestPosts(limit = 6) {
|
||||
const data = await fetchDirectus<any[]>(
|
||||
`/items/posts?filter[status][_eq]=published&sort=-published_at&limit=${limit}`
|
||||
);
|
||||
return data ?? [];
|
||||
}
|
||||
|
||||
export async function getAllPosts() {
|
||||
const data = await fetchDirectus<any[]>(
|
||||
"/items/posts?filter[status][_eq]=published&sort=-published_at"
|
||||
);
|
||||
return data ?? [];
|
||||
}
|
||||
|
||||
export async function getPost(slug: string) {
|
||||
const data = await fetchDirectus<any[]>(
|
||||
`/items/posts?filter[slug][_eq]=${encodeURIComponent(slug)}&limit=1`
|
||||
);
|
||||
return data?.[0] ?? null;
|
||||
}
|
||||
|
||||
export async function getTeam() {
|
||||
const data = await fetchDirectus<any[]>(
|
||||
"/items/team_members?sort[]=sort&sort[]=name"
|
||||
);
|
||||
return data ?? [];
|
||||
}
|
||||
@@ -1,11 +1,10 @@
|
||||
---
|
||||
import Layout from "../../layouts/Layout.astro";
|
||||
import { getAllPosts, getPost } from "../../lib/sanity";
|
||||
import { toHTML } from "@portabletext/to-html";
|
||||
import { getAllPosts, getPost, imageUrl } from "../../lib/directus";
|
||||
|
||||
export async function getStaticPaths() {
|
||||
const posts = await getAllPosts();
|
||||
return posts.map((p: any) => ({ params: { slug: p.slug.current } }));
|
||||
return posts.map((p: any) => ({ params: { slug: p.slug } }));
|
||||
}
|
||||
|
||||
const { slug } = Astro.params;
|
||||
@@ -13,8 +12,8 @@ const post = await getPost(slug!);
|
||||
|
||||
if (!post) return Astro.redirect("/aktuelles");
|
||||
|
||||
const bodyHtml = post.body ? toHTML(post.body) : "";
|
||||
const dateFormatted = new Date(post.publishedAt).toLocaleDateString("de-DE", {
|
||||
const bodyHtml = post.body ?? "";
|
||||
const dateFormatted = new Date(post.published_at).toLocaleDateString("de-DE", {
|
||||
day: "numeric", month: "long", year: "numeric",
|
||||
});
|
||||
---
|
||||
@@ -22,22 +21,22 @@ const dateFormatted = new Date(post.publishedAt).toLocaleDateString("de-DE", {
|
||||
<Layout
|
||||
title={post.title}
|
||||
description={post.excerpt}
|
||||
ogImage={post.coverImage?.asset?.url}
|
||||
ogImage={post.cover_image ? imageUrl(post.cover_image) : undefined}
|
||||
>
|
||||
<div class="max-w-6xl mx-auto px-4 pt-6">
|
||||
<a href="/aktuelles" class="text-sm text-[var(--color-text-muted)] hover:text-[var(--color-primary)]">← Alle Beiträge</a>
|
||||
</div>
|
||||
|
||||
{post.coverImage?.asset?.url && (
|
||||
{post.cover_image && (
|
||||
<div class="max-w-6xl mx-auto px-4 mt-6">
|
||||
<div class="aspect-video rounded-3xl overflow-hidden">
|
||||
<img src={`${post.coverImage.asset.url}?w=1200&auto=format`} alt={post.title} class="w-full h-full object-cover" />
|
||||
<img src={imageUrl(post.cover_image, 1200)} alt={post.title} class="w-full h-full object-cover" />
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
<article class="section max-w-3xl">
|
||||
<time class="text-sm text-[var(--color-text-muted)]" datetime={post.publishedAt}>{dateFormatted}</time>
|
||||
<time class="text-sm text-[var(--color-text-muted)]" datetime={post.published_at}>{dateFormatted}</time>
|
||||
<h1 class="font-brand text-4xl md:text-5xl text-[var(--color-text)] mt-3 mb-6 leading-tight">{post.title}</h1>
|
||||
{post.excerpt && <p class="text-xl text-[var(--color-text-muted)] mb-8 leading-relaxed">{post.excerpt}</p>}
|
||||
{bodyHtml && (
|
||||
|
||||
@@ -1,14 +1,13 @@
|
||||
---
|
||||
import Layout from "../../layouts/Layout.astro";
|
||||
import { getAllPosts } from "../../lib/sanity";
|
||||
import { getAllPosts, imageUrl } from "../../lib/directus";
|
||||
|
||||
const posts = await getAllPosts();
|
||||
|
||||
const categoryLabel: Record<string, string> = {
|
||||
news: "Vereinsnews",
|
||||
projects: "Projekte",
|
||||
events: "Veranstaltungen",
|
||||
press: "Presse",
|
||||
event: "Veranstaltung",
|
||||
report: "Bericht",
|
||||
};
|
||||
---
|
||||
|
||||
@@ -29,10 +28,10 @@ const categoryLabel: Record<string, string> = {
|
||||
) : (
|
||||
<div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6">
|
||||
{posts.map((post: any) => (
|
||||
<a href={`/aktuelles/${post.slug.current}`} class="card group">
|
||||
{post.coverImage?.asset?.url ? (
|
||||
<a href={`/aktuelles/${post.slug}`} class="card group">
|
||||
{post.cover_image ? (
|
||||
<div class="aspect-video rounded-xl overflow-hidden mb-4 bg-[var(--color-surface-alt)]">
|
||||
<img src={`${post.coverImage.asset.url}?w=600&auto=format`} alt={post.title} class="w-full h-full object-cover group-hover:scale-105 transition-transform duration-300" />
|
||||
<img src={imageUrl(post.cover_image, 600)} alt={post.title} class="w-full h-full object-cover group-hover:scale-105 transition-transform duration-300" />
|
||||
</div>
|
||||
) : (
|
||||
<div class="aspect-video rounded-xl mb-4 bg-[var(--color-surface-alt)] flex items-center justify-center text-4xl">📰</div>
|
||||
@@ -43,8 +42,8 @@ const categoryLabel: Record<string, string> = {
|
||||
{categoryLabel[post.category] ?? post.category}
|
||||
</span>
|
||||
)}
|
||||
<time class="text-xs text-[var(--color-text-muted)]" datetime={post.publishedAt}>
|
||||
{new Date(post.publishedAt).toLocaleDateString("de-DE", { day: "numeric", month: "long", year: "numeric" })}
|
||||
<time class="text-xs text-[var(--color-text-muted)]" datetime={post.published_at}>
|
||||
{new Date(post.published_at).toLocaleDateString("de-DE", { day: "numeric", month: "long", year: "numeric" })}
|
||||
</time>
|
||||
</div>
|
||||
<h3 class="font-bold leading-snug group-hover:text-[var(--color-primary)] transition-colors">{post.title}</h3>
|
||||
|
||||
166
src/pages/datenschutz.astro
Normal file
166
src/pages/datenschutz.astro
Normal file
@@ -0,0 +1,166 @@
|
||||
---
|
||||
import Layout from "../layouts/Layout.astro";
|
||||
import { getSettings } from "../lib/directus";
|
||||
|
||||
const settings = await getSettings();
|
||||
---
|
||||
|
||||
<Layout
|
||||
title="Datenschutz"
|
||||
description="Datenschutzerklärung des Kitafreunde Regenbogen e.V. gemäß DSGVO."
|
||||
>
|
||||
<section class="section pt-16 max-w-3xl">
|
||||
<p class="text-sm font-semibold tracking-widest uppercase text-[var(--color-primary)] mb-3">Rechtliches</p>
|
||||
<h1 class="font-brand text-5xl text-[var(--color-text)] mb-10">Datenschutz</h1>
|
||||
|
||||
<div class="space-y-8 text-[var(--color-text-muted)] text-sm leading-relaxed">
|
||||
|
||||
<div class="card">
|
||||
<h2 class="font-bold text-[var(--color-text)] mb-3">1. Verantwortliche Stelle</h2>
|
||||
<p>
|
||||
Verantwortlich im Sinne der Datenschutz-Grundverordnung (DSGVO) und anderer
|
||||
datenschutzrechtlicher Bestimmungen ist:
|
||||
</p>
|
||||
<div class="mt-3">
|
||||
<p class="font-semibold text-[var(--color-text)]">Kitafreunde Regenbogen e.V.</p>
|
||||
<p>c/o Integrationskindertagesstätte Regenbogen</p>
|
||||
<p>Keilerstraße 23 · 13503 Berlin</p>
|
||||
{settings?.contact_email && (
|
||||
<p class="mt-1">E-Mail: <a href={`mailto:${settings.contact_email}`} class="text-[var(--color-primary)] hover:underline">{settings.contact_email}</a></p>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="card">
|
||||
<h2 class="font-bold text-[var(--color-text)] mb-3">2. Allgemeines zur Datenverarbeitung</h2>
|
||||
<p>
|
||||
Wir nehmen den Schutz deiner persönlichen Daten sehr ernst. Diese Website erhebt
|
||||
und verarbeitet personenbezogene Daten nur im technisch notwendigen Umfang und
|
||||
ausschließlich auf der Grundlage der gesetzlichen Bestimmungen (DSGVO, TMG).
|
||||
</p>
|
||||
<p class="mt-3">
|
||||
Wir verwenden <strong class="text-[var(--color-text)]">keine Tracking- oder Analysetools,
|
||||
keine Werbenetzwerke und keine Social-Media-Plugins</strong>, die automatisch Daten
|
||||
erheben. Es werden keine Cookies gesetzt.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div class="card">
|
||||
<h2 class="font-bold text-[var(--color-text)] mb-3">3. Hosting</h2>
|
||||
<p>
|
||||
Diese Website wird auf Servern der Hetzner Online GmbH (Industriestr. 25, 91710 Gunzenhausen)
|
||||
in Deutschland betrieben. Hetzner ist als Auftragsverarbeiter nach Art. 28 DSGVO vertraglich
|
||||
gebunden. Beim Aufruf der Website werden durch den Webserver automatisch folgende Daten
|
||||
in Server-Logfiles gespeichert:
|
||||
</p>
|
||||
<ul class="list-disc pl-5 mt-3 space-y-1">
|
||||
<li>IP-Adresse des anfragenden Rechners (anonymisiert nach kurzer Zeit)</li>
|
||||
<li>Datum und Uhrzeit des Zugriffs</li>
|
||||
<li>Aufgerufene Seite / Datei</li>
|
||||
<li>HTTP-Statuscode</li>
|
||||
<li>Übertragene Datenmenge</li>
|
||||
<li>Referrer-URL</li>
|
||||
<li>Browser und Betriebssystem (User-Agent)</li>
|
||||
</ul>
|
||||
<p class="mt-3">
|
||||
Rechtsgrundlage ist Art. 6 Abs. 1 lit. f DSGVO (berechtigtes Interesse am sicheren
|
||||
und fehlerfreien Betrieb der Website). Die Daten werden nicht mit anderen Datenquellen
|
||||
zusammengeführt und nach spätestens 7 Tagen gelöscht.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div class="card">
|
||||
<h2 class="font-bold text-[var(--color-text)] mb-3">4. Google Fonts</h2>
|
||||
<p>
|
||||
Diese Website lädt Schriftarten (Pacifico, Inter) von Google Fonts
|
||||
(<code class="text-xs bg-[var(--color-surface)] px-1 rounded">fonts.googleapis.com</code>).
|
||||
Dabei wird deine IP-Adresse an Server von Google LLC in den USA übermittelt.
|
||||
Rechtsgrundlage ist Art. 6 Abs. 1 lit. f DSGVO; unser berechtigtes Interesse
|
||||
liegt in der einheitlichen Darstellung der Website.
|
||||
</p>
|
||||
<p class="mt-3">
|
||||
Google ist unter dem EU-US Data Privacy Framework zertifiziert. Weitere
|
||||
Informationen: <a href="https://policies.google.com/privacy" target="_blank"
|
||||
rel="noopener noreferrer" class="text-[var(--color-primary)] hover:underline">
|
||||
policies.google.com/privacy</a>.
|
||||
</p>
|
||||
<p class="mt-3 text-xs italic">
|
||||
Hinweis: Wir planen, alle Schriftarten künftig lokal zu betreiben,
|
||||
sodass keine Daten mehr an Google übertragen werden.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div class="card">
|
||||
<h2 class="font-bold text-[var(--color-text)] mb-3">5. Kontaktaufnahme per E-Mail</h2>
|
||||
<p>
|
||||
Wenn du uns per E-Mail kontaktierst, werden deine Angaben (E-Mail-Adresse,
|
||||
ggf. Name und Nachrichteninhalt) zum Zweck der Bearbeitung deiner Anfrage
|
||||
und für den Fall von Anschlussfragen bei uns gespeichert.
|
||||
</p>
|
||||
<p class="mt-3">
|
||||
Rechtsgrundlage ist Art. 6 Abs. 1 lit. b DSGVO (Vertragsanbahnung oder
|
||||
berechtigtes Interesse). Die Daten werden nicht an Dritte weitergegeben
|
||||
und nach Abschluss der Kommunikation gelöscht, sofern keine gesetzlichen
|
||||
Aufbewahrungspflichten entgegenstehen.
|
||||
</p>
|
||||
<p class="mt-3">
|
||||
Das Kontaktformular auf dieser Website funktioniert als mailto-Link — es werden
|
||||
keine Formulardaten auf unseren Servern gespeichert oder verarbeitet.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div class="card">
|
||||
<h2 class="font-bold text-[var(--color-text)] mb-3">6. Mitgliedschaft</h2>
|
||||
<p>
|
||||
Wenn du Mitglied des Kitafreunde Regenbogen e.V. wirst, verarbeiten wir die
|
||||
in der Beitrittserklärung angegebenen Daten (Name, Anschrift, E-Mail, ggf.
|
||||
IBAN für den Beitragseinzug) zur Verwaltung der Mitgliedschaft.
|
||||
</p>
|
||||
<p class="mt-3">
|
||||
Rechtsgrundlage ist Art. 6 Abs. 1 lit. b DSGVO (Vertragserfüllung) sowie
|
||||
§ 26 BDSG (Vereinsmitgliedschaft). Die Daten werden für die Dauer der
|
||||
Mitgliedschaft und anschließend entsprechend den gesetzlichen
|
||||
Aufbewahrungsfristen gespeichert.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div class="card">
|
||||
<h2 class="font-bold text-[var(--color-text)] mb-3">7. Deine Rechte</h2>
|
||||
<p>Du hast jederzeit das Recht auf:</p>
|
||||
<ul class="list-disc pl-5 mt-3 space-y-1">
|
||||
<li><strong class="text-[var(--color-text)]">Auskunft</strong> über die zu deiner Person gespeicherten Daten (Art. 15 DSGVO)</li>
|
||||
<li><strong class="text-[var(--color-text)]">Berichtigung</strong> unrichtiger Daten (Art. 16 DSGVO)</li>
|
||||
<li><strong class="text-[var(--color-text)]">Löschung</strong> deiner Daten (Art. 17 DSGVO)</li>
|
||||
<li><strong class="text-[var(--color-text)]">Einschränkung</strong> der Verarbeitung (Art. 18 DSGVO)</li>
|
||||
<li><strong class="text-[var(--color-text)]">Datenübertragbarkeit</strong> (Art. 20 DSGVO)</li>
|
||||
<li><strong class="text-[var(--color-text)]">Widerspruch</strong> gegen die Verarbeitung (Art. 21 DSGVO)</li>
|
||||
</ul>
|
||||
<p class="mt-3">
|
||||
Zur Ausübung deiner Rechte wende dich an:
|
||||
{settings?.contact_email ? (
|
||||
<a href={`mailto:${settings.contact_email}`} class="text-[var(--color-primary)] hover:underline">{settings.contact_email}</a>
|
||||
) : (
|
||||
<span class="text-[var(--color-primary)] font-medium">[E-Mail eintragen]</span>
|
||||
)}
|
||||
</p>
|
||||
<p class="mt-3">
|
||||
Du hast außerdem das Recht, dich bei der zuständigen Datenschutz-Aufsichtsbehörde
|
||||
zu beschweren. In Berlin ist das die Berliner Beauftragte für Datenschutz und
|
||||
Informationsfreiheit (<a href="https://www.datenschutz-berlin.de" target="_blank"
|
||||
rel="noopener noreferrer" class="text-[var(--color-primary)] hover:underline">
|
||||
datenschutz-berlin.de</a>).
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div class="card">
|
||||
<h2 class="font-bold text-[var(--color-text)] mb-3">8. Aktualität dieser Erklärung</h2>
|
||||
<p>
|
||||
Diese Datenschutzerklärung ist aktuell gültig und hat den Stand Juni 2026.
|
||||
Durch die Weiterentwicklung unserer Website oder aufgrund geänderter gesetzlicher
|
||||
Vorgaben kann es notwendig werden, diese Datenschutzerklärung anzupassen.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</section>
|
||||
</Layout>
|
||||
80
src/pages/impressum.astro
Normal file
80
src/pages/impressum.astro
Normal file
@@ -0,0 +1,80 @@
|
||||
---
|
||||
import Layout from "../layouts/Layout.astro";
|
||||
import { getSettings } from "../lib/directus";
|
||||
|
||||
const settings = await getSettings();
|
||||
---
|
||||
|
||||
<Layout
|
||||
title="Impressum"
|
||||
description="Impressum des Kitafreunde Regenbogen e.V. — Angaben gemäß § 5 TMG."
|
||||
>
|
||||
<section class="section pt-16 max-w-3xl">
|
||||
<p class="text-sm font-semibold tracking-widest uppercase text-[var(--color-primary)] mb-3">Rechtliches</p>
|
||||
<h1 class="font-brand text-5xl text-[var(--color-text)] mb-10">Impressum</h1>
|
||||
|
||||
<div class="space-y-8 text-[var(--color-text-muted)] text-sm leading-relaxed">
|
||||
|
||||
<div class="card">
|
||||
<h2 class="font-bold text-[var(--color-text)] mb-3">Angaben gemäß § 5 TMG</h2>
|
||||
<p class="font-semibold text-[var(--color-text)]">Kitafreunde Regenbogen e.V.</p>
|
||||
<p>c/o Integrationskindertagesstätte Regenbogen</p>
|
||||
<p>Keilerstraße 23</p>
|
||||
<p>13503 Berlin</p>
|
||||
</div>
|
||||
|
||||
<div class="card">
|
||||
<h2 class="font-bold text-[var(--color-text)] mb-3">Vereinsregister</h2>
|
||||
<p>Registergericht: Amtsgericht Charlottenburg</p>
|
||||
<p>Vereinsregisternummer: <span class="text-[var(--color-primary)] font-medium">[VR-Nummer eintragen]</span></p>
|
||||
</div>
|
||||
|
||||
<div class="card">
|
||||
<h2 class="font-bold text-[var(--color-text)] mb-3">Vertreten durch den Vorstand</h2>
|
||||
<p>1. Vorsitzende/r: <span class="text-[var(--color-primary)] font-medium">[Name eintragen]</span></p>
|
||||
<p>Stellvertretung: <span class="text-[var(--color-primary)] font-medium">[Name eintragen]</span></p>
|
||||
<p>Kassenwart/in: <span class="text-[var(--color-primary)] font-medium">[Name eintragen]</span></p>
|
||||
</div>
|
||||
|
||||
<div class="card">
|
||||
<h2 class="font-bold text-[var(--color-text)] mb-3">Kontakt</h2>
|
||||
{settings?.contact_email ? (
|
||||
<p>E-Mail: <a href={`mailto:${settings.contact_email}`} class="text-[var(--color-primary)] hover:underline">{settings.contact_email}</a></p>
|
||||
) : (
|
||||
<p>E-Mail: <span class="text-[var(--color-primary)] font-medium">[E-Mail eintragen]</span></p>
|
||||
)}
|
||||
{settings?.contact_phone && (
|
||||
<p>Telefon: {settings.contact_phone}</p>
|
||||
)}
|
||||
</div>
|
||||
|
||||
<div class="card">
|
||||
<h2 class="font-bold text-[var(--color-text)] mb-3">Gemeinnützigkeit</h2>
|
||||
<p>
|
||||
Der Kitafreunde Regenbogen e.V. ist als gemeinnützig anerkannt. Der letzte
|
||||
Freistellungsbescheid wurde erteilt vom Finanzamt <span class="text-[var(--color-primary)] font-medium">[Finanzamt eintragen]</span>,
|
||||
Steuernummer <span class="text-[var(--color-primary)] font-medium">[Steuernummer eintragen]</span>.
|
||||
</p>
|
||||
<p class="mt-2">Spenden sind steuerlich absetzbar.</p>
|
||||
</div>
|
||||
|
||||
<div class="card">
|
||||
<h2 class="font-bold text-[var(--color-text)] mb-3">Inhaltlich verantwortlich</h2>
|
||||
<p>Inhaltlich verantwortlich gemäß § 18 Abs. 2 MStV:</p>
|
||||
<p class="mt-1">
|
||||
<span class="text-[var(--color-primary)] font-medium">[Vorstandsvorsitzende/r, Name und Anschrift]</span>
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div class="card">
|
||||
<h2 class="font-bold text-[var(--color-text)] mb-3">Haftungshinweis</h2>
|
||||
<p>
|
||||
Trotz sorgfältiger inhaltlicher Kontrolle übernehmen wir keine Haftung für die Inhalte
|
||||
externer Links. Für den Inhalt der verlinkten Seiten sind ausschließlich deren Betreiber
|
||||
verantwortlich.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</section>
|
||||
</Layout>
|
||||
@@ -1,12 +1,10 @@
|
||||
---
|
||||
import Layout from "../layouts/Layout.astro";
|
||||
import { getSettings, getFeaturedProjects, getLatestPosts } from "../lib/sanity";
|
||||
import { getSettings, getFeaturedProjects, getLatestPosts, imageUrl } from "../lib/directus";
|
||||
|
||||
const settings = await getSettings();
|
||||
const featuredProjects = await getFeaturedProjects();
|
||||
const latestPosts = await getLatestPosts(3);
|
||||
|
||||
const stats = settings?.stats;
|
||||
---
|
||||
|
||||
<Layout
|
||||
@@ -83,25 +81,19 @@ const stats = settings?.stats;
|
||||
</section>
|
||||
|
||||
<!-- Zahlen -->
|
||||
{stats && (stats.members || stats.fundsPerYear || stats.projectsTotal) && (
|
||||
{settings && (settings.stats_members || settings.stats_funded) && (
|
||||
<section class="bg-[var(--color-primary)] text-white">
|
||||
<div class="max-w-6xl mx-auto px-4 py-16 grid grid-cols-1 md:grid-cols-3 gap-8 text-center">
|
||||
{stats.members && (
|
||||
<div class="max-w-6xl mx-auto px-4 py-16 grid grid-cols-1 md:grid-cols-2 gap-8 text-center">
|
||||
{settings.stats_members && (
|
||||
<div>
|
||||
<p class="font-brand text-5xl mb-2">{stats.members}</p>
|
||||
<p class="font-brand text-5xl mb-2">{settings.stats_members}</p>
|
||||
<p class="text-white/80">Mitglieder unterstützen den Verein</p>
|
||||
</div>
|
||||
)}
|
||||
{stats.fundsPerYear && (
|
||||
{settings.stats_funded && (
|
||||
<div>
|
||||
<p class="font-brand text-5xl mb-2">{stats.fundsPerYear.toLocaleString("de")} €</p>
|
||||
<p class="text-white/80">jährlich für die Kinder</p>
|
||||
</div>
|
||||
)}
|
||||
{stats.projectsTotal && (
|
||||
<div>
|
||||
<p class="font-brand text-5xl mb-2">{stats.projectsTotal}</p>
|
||||
<p class="text-white/80">Projekte seit Vereinsgründung</p>
|
||||
<p class="font-brand text-5xl mb-2">{settings.stats_funded.toLocaleString("de")} €</p>
|
||||
<p class="text-white/80">geförderte Projekte</p>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
@@ -115,10 +107,10 @@ const stats = settings?.stats;
|
||||
<p class="text-[var(--color-text-muted)] mb-8">Was wir gerade machen und was geplant ist.</p>
|
||||
<div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6">
|
||||
{featuredProjects.map((p: any) => (
|
||||
<a href={`/projekte/${p.slug.current}`} class="card group">
|
||||
{p.image?.asset && (
|
||||
<a href={`/projekte/${p.slug}`} class="card group">
|
||||
{p.image && (
|
||||
<div class="aspect-video bg-[var(--color-surface-alt)] rounded-xl mb-4 overflow-hidden">
|
||||
<img src={p.image.asset.url} alt={p.title} class="w-full h-full object-cover group-hover:scale-105 transition-transform duration-300" />
|
||||
<img src={imageUrl(p.image, 600)} alt={p.title} class="w-full h-full object-cover group-hover:scale-105 transition-transform duration-300" />
|
||||
</div>
|
||||
)}
|
||||
<p class="text-xs text-[var(--color-text-muted)] mb-1">{p.date}</p>
|
||||
@@ -139,9 +131,9 @@ const stats = settings?.stats;
|
||||
<h2 class="text-3xl font-bold mb-8">Aktuelles</h2>
|
||||
<div class="grid grid-cols-1 md:grid-cols-3 gap-6">
|
||||
{latestPosts.map((post: any) => (
|
||||
<a href={`/aktuelles/${post.slug.current}`} class="card group">
|
||||
<a href={`/aktuelles/${post.slug}`} class="card group">
|
||||
<p class="text-xs text-[var(--color-text-muted)] mb-2">
|
||||
{new Date(post.publishedAt).toLocaleDateString("de-DE", { day: "numeric", month: "long", year: "numeric" })}
|
||||
{new Date(post.published_at).toLocaleDateString("de-DE", { day: "numeric", month: "long", year: "numeric" })}
|
||||
</p>
|
||||
<h3 class="font-bold leading-snug group-hover:text-[var(--color-primary)] transition-colors">{post.title}</h3>
|
||||
{post.excerpt && <p class="text-sm text-[var(--color-text-muted)] mt-2 line-clamp-3">{post.excerpt}</p>}
|
||||
|
||||
@@ -1,10 +1,8 @@
|
||||
---
|
||||
import Layout from "../layouts/Layout.astro";
|
||||
import { getSettings } from "../lib/sanity";
|
||||
import { getSettings } from "../lib/directus";
|
||||
|
||||
const settings = await getSettings();
|
||||
const contact = settings?.contact;
|
||||
const social = settings?.social;
|
||||
---
|
||||
|
||||
<Layout title="Kontakt" description="Kontakt zum Kitafreunde Regenbogen e.V. — schreib uns, werde Mitglied oder bring eine Idee ein.">
|
||||
@@ -23,11 +21,11 @@ const social = settings?.social;
|
||||
13503 Berlin
|
||||
</p>
|
||||
</div>
|
||||
{contact?.email && (
|
||||
{settings?.contact_email && (
|
||||
<div class="card">
|
||||
<h3 class="font-bold mb-2 text-sm uppercase tracking-wide text-[var(--color-text-muted)]">E-Mail</h3>
|
||||
<a href={`mailto:${contact.email}`} class="text-[var(--color-primary)] font-medium text-lg">
|
||||
{contact.email}
|
||||
<a href={`mailto:${settings.contact_email}`} class="text-[var(--color-primary)] font-medium text-lg">
|
||||
{settings.contact_email}
|
||||
</a>
|
||||
</div>
|
||||
)}
|
||||
@@ -38,15 +36,15 @@ const social = settings?.social;
|
||||
</p>
|
||||
<a href="/mitglied-werden" class="btn-primary text-sm">Zur Mitgliedschaftsseite →</a>
|
||||
</div>
|
||||
{(social?.instagram || social?.facebook) && (
|
||||
{(settings?.social_instagram || settings?.social_facebook) && (
|
||||
<div class="card">
|
||||
<h3 class="font-bold mb-3">Social Media</h3>
|
||||
<div class="flex gap-3">
|
||||
{social.instagram && (
|
||||
<a href={social.instagram} target="_blank" rel="noopener" class="btn-secondary text-sm">Instagram</a>
|
||||
{settings.social_instagram && (
|
||||
<a href={`https://instagram.com/${settings.social_instagram}`} target="_blank" rel="noopener" class="btn-secondary text-sm">Instagram</a>
|
||||
)}
|
||||
{social.facebook && (
|
||||
<a href={social.facebook} target="_blank" rel="noopener" class="btn-secondary text-sm">Facebook</a>
|
||||
{settings.social_facebook && (
|
||||
<a href={settings.social_facebook} target="_blank" rel="noopener" class="btn-secondary text-sm">Facebook</a>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
@@ -60,7 +58,7 @@ const social = settings?.social;
|
||||
Dieses Formular öffnet deinen E-Mail-Client.
|
||||
</p>
|
||||
<form
|
||||
action={`mailto:${contact?.email ?? "info@kitafreunde-regenbogen.de"}`}
|
||||
action={`mailto:${settings?.contact_email ?? "info@kitafreunde-regenbogen.de"}`}
|
||||
method="get"
|
||||
enctype="text/plain"
|
||||
class="space-y-4"
|
||||
|
||||
@@ -1,10 +1,8 @@
|
||||
---
|
||||
import Layout from "../layouts/Layout.astro";
|
||||
import { getSettings } from "../lib/sanity";
|
||||
import { getSettings } from "../lib/directus";
|
||||
|
||||
const settings = await getSettings();
|
||||
const fees = settings?.memberFees;
|
||||
const bank = settings?.bank;
|
||||
---
|
||||
|
||||
<Layout
|
||||
@@ -39,9 +37,9 @@ const bank = settings?.bank;
|
||||
<div class="card border-2 border-[var(--color-primary)]">
|
||||
<p class="text-sm font-semibold text-[var(--color-primary)] uppercase tracking-wide mb-2">Aktives Mitglied</p>
|
||||
<p class="font-brand text-4xl text-[var(--color-text)] mb-1">
|
||||
ab {fees?.active ?? 12} €<span class="text-lg font-normal">/Jahr</span>
|
||||
ab {settings?.member_fee_active ?? 12} €<span class="text-lg font-normal">/Jahr</span>
|
||||
</p>
|
||||
<p class="text-sm text-[var(--color-text-muted)] mb-4">zzgl. einmalig {fees?.admissionFee ?? 5} € Aufnahmegebühr</p>
|
||||
<p class="text-sm text-[var(--color-text-muted)] mb-4">zzgl. einmalig 5 € Aufnahmegebühr</p>
|
||||
<ul class="text-sm text-[var(--color-text-muted)] space-y-2 mb-6">
|
||||
<li class="flex gap-2"><span class="text-[var(--color-rb-green)] font-bold">✓</span> Stimmrecht auf der Mitgliederversammlung</li>
|
||||
<li class="flex gap-2"><span class="text-[var(--color-rb-green)] font-bold">✓</span> Mitgestaltung von Projekten und Zielen</li>
|
||||
@@ -53,7 +51,7 @@ const bank = settings?.bank;
|
||||
<div class="card">
|
||||
<p class="text-sm font-semibold text-[var(--color-accent)] uppercase tracking-wide mb-2">Fördermitglied</p>
|
||||
<p class="font-brand text-4xl text-[var(--color-text)] mb-1">
|
||||
ab {fees?.supporting ?? 6} €<span class="text-lg font-normal">/Jahr</span>
|
||||
ab {settings?.member_fee_supporting ?? 6} €<span class="text-lg font-normal">/Jahr</span>
|
||||
</p>
|
||||
<p class="text-sm text-[var(--color-text-muted)] mb-4">keine Aufnahmegebühr</p>
|
||||
<ul class="text-sm text-[var(--color-text-muted)] space-y-2 mb-6">
|
||||
@@ -83,7 +81,7 @@ const bank = settings?.bank;
|
||||
<div class="grid grid-cols-1 md:grid-cols-3 gap-8">
|
||||
{[
|
||||
{ step: "1", title: "Beitrittserklärung ausfüllen", text: "Als PDF ausdrucken und unterschreiben — oder im Kitabüro abholen." },
|
||||
{ step: "2", title: "Beitrag überweisen", text: `Jahresbeitrag + einmalige Aufnahmegebühr (${fees?.admissionFee ?? 5} €) auf unser Vereinskonto.` },
|
||||
{ step: "2", title: "Beitrag überweisen", text: "Jahresbeitrag + einmalige Aufnahmegebühr (5 €) auf unser Vereinskonto." },
|
||||
{ step: "3", title: "Fertig — du bist dabei.", text: "Willkommen bei den Kitafreunden. Wir freuen uns." },
|
||||
].map(({ step, title, text }) => (
|
||||
<div class="text-center">
|
||||
@@ -101,15 +99,15 @@ const bank = settings?.bank;
|
||||
</section>
|
||||
|
||||
<!-- Bank -->
|
||||
{bank?.iban && (
|
||||
{settings?.iban && (
|
||||
<section class="section">
|
||||
<div class="card max-w-xl">
|
||||
<h3 class="font-bold mb-4">Bankverbindung</h3>
|
||||
<dl class="text-sm space-y-2 text-[var(--color-text-muted)]">
|
||||
<div class="flex gap-2"><dt class="font-medium text-[var(--color-text)] w-32">Empfänger</dt><dd>{bank.accountHolder}</dd></div>
|
||||
<div class="flex gap-2"><dt class="font-medium text-[var(--color-text)] w-32">IBAN</dt><dd class="font-mono">{bank.iban}</dd></div>
|
||||
{bank.bic && <div class="flex gap-2"><dt class="font-medium text-[var(--color-text)] w-32">BIC</dt><dd class="font-mono">{bank.bic}</dd></div>}
|
||||
{bank.bank && <div class="flex gap-2"><dt class="font-medium text-[var(--color-text)] w-32">Bank</dt><dd>{bank.bank}</dd></div>}
|
||||
<div class="flex gap-2"><dt class="font-medium text-[var(--color-text)] w-32">Empfänger</dt><dd>Kitafreunde Regenbogen e.V.</dd></div>
|
||||
<div class="flex gap-2"><dt class="font-medium text-[var(--color-text)] w-32">IBAN</dt><dd class="font-mono">{settings.iban}</dd></div>
|
||||
{settings.bic && <div class="flex gap-2"><dt class="font-medium text-[var(--color-text)] w-32">BIC</dt><dd class="font-mono">{settings.bic}</dd></div>}
|
||||
{settings.bank_name && <div class="flex gap-2"><dt class="font-medium text-[var(--color-text)] w-32">Bank</dt><dd>{settings.bank_name}</dd></div>}
|
||||
<div class="flex gap-2"><dt class="font-medium text-[var(--color-text)] w-32">Verwendung</dt><dd>Mitgliedsbeitrag [Dein Name]</dd></div>
|
||||
</dl>
|
||||
</div>
|
||||
|
||||
@@ -1,9 +1,8 @@
|
||||
---
|
||||
import Layout from "../layouts/Layout.astro";
|
||||
import { getSettings } from "../lib/sanity";
|
||||
import { getSettings } from "../lib/directus";
|
||||
|
||||
const settings = await getSettings();
|
||||
const contact = settings?.contact;
|
||||
---
|
||||
|
||||
<Layout title="Presse" description="Pressekontakt und Informationen zum Kitafreunde Regenbogen e.V., Förderverein der Integrationskita Regenbogen in Berlin-Tegel.">
|
||||
@@ -14,12 +13,12 @@ const contact = settings?.contact;
|
||||
<div class="grid grid-cols-1 md:grid-cols-2 gap-12 mt-12">
|
||||
<div>
|
||||
<h2 class="text-xl font-bold mb-4">Pressekontakt</h2>
|
||||
{contact?.email ? (
|
||||
{settings?.contact_email ? (
|
||||
<div class="card">
|
||||
<p class="font-semibold mb-1">Kitafreunde Regenbogen e.V.</p>
|
||||
<p class="text-sm text-[var(--color-text-muted)] mb-3">Keilerstraße 23 · 13503 Berlin</p>
|
||||
<a href={`mailto:${contact.email}?subject=Presseanfrage`} class="text-[var(--color-primary)] font-medium">
|
||||
{contact.email}
|
||||
<a href={`mailto:${settings.contact_email}?subject=Presseanfrage`} class="text-[var(--color-primary)] font-medium">
|
||||
{settings.contact_email}
|
||||
</a>
|
||||
</div>
|
||||
) : (
|
||||
|
||||
@@ -1,11 +1,10 @@
|
||||
---
|
||||
import Layout from "../../layouts/Layout.astro";
|
||||
import { getAllProjects, getProject } from "../../lib/sanity";
|
||||
import { toHTML } from "@portabletext/to-html";
|
||||
import { getAllProjects, getProject, imageUrl } from "../../lib/directus";
|
||||
|
||||
export async function getStaticPaths() {
|
||||
const projects = await getAllProjects();
|
||||
return projects.map((p: any) => ({ params: { slug: p.slug.current } }));
|
||||
return projects.map((p: any) => ({ params: { slug: p.slug } }));
|
||||
}
|
||||
|
||||
const { slug } = Astro.params;
|
||||
@@ -13,23 +12,23 @@ const project = await getProject(slug!);
|
||||
|
||||
if (!project) return Astro.redirect("/projekte");
|
||||
|
||||
const bodyHtml = project.body ? toHTML(project.body) : "";
|
||||
const bodyHtml = project.body ?? "";
|
||||
---
|
||||
|
||||
<Layout
|
||||
title={project.title}
|
||||
description={project.summary}
|
||||
ogImage={project.image?.asset?.url}
|
||||
ogImage={project.image ? imageUrl(project.image) : undefined}
|
||||
>
|
||||
<!-- Breadcrumb -->
|
||||
<div class="max-w-6xl mx-auto px-4 pt-6">
|
||||
<a href="/projekte" class="text-sm text-[var(--color-text-muted)] hover:text-[var(--color-primary)]">← Alle Projekte</a>
|
||||
</div>
|
||||
|
||||
{project.image?.asset?.url && (
|
||||
{project.image && (
|
||||
<div class="max-w-6xl mx-auto px-4 mt-6">
|
||||
<div class="aspect-video rounded-3xl overflow-hidden">
|
||||
<img src={`${project.image.asset.url}?w=1200&auto=format`} alt={project.title} class="w-full h-full object-cover" />
|
||||
<img src={imageUrl(project.image, 1200)} alt={project.title} class="w-full h-full object-cover" />
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
@@ -38,7 +37,7 @@ const bodyHtml = project.body ? toHTML(project.body) : "";
|
||||
<div class="max-w-3xl">
|
||||
<div class="flex items-center gap-3 mb-4 text-sm text-[var(--color-text-muted)]">
|
||||
{project.date && <span>📅 {project.date}</span>}
|
||||
{project.targetGroup && <span>👥 {project.targetGroup}</span>}
|
||||
{project.target_group && <span>👥 {project.target_group}</span>}
|
||||
</div>
|
||||
<h1 class="font-brand text-4xl md:text-5xl text-[var(--color-text)] mb-6">{project.title}</h1>
|
||||
<p class="text-xl text-[var(--color-text-muted)] leading-relaxed mb-8">{project.summary}</p>
|
||||
|
||||
@@ -1,17 +1,17 @@
|
||||
---
|
||||
import Layout from "../../layouts/Layout.astro";
|
||||
import { getAllProjects } from "../../lib/sanity";
|
||||
import { getAllProjects, imageUrl } from "../../lib/directus";
|
||||
|
||||
const projects = await getAllProjects();
|
||||
|
||||
const statusLabel: Record<string, string> = {
|
||||
planned: "Geplant",
|
||||
active: "Laufend",
|
||||
running: "Laufend",
|
||||
done: "Abgeschlossen",
|
||||
};
|
||||
const statusColor: Record<string, string> = {
|
||||
planned: "var(--color-rb-blue)",
|
||||
active: "var(--color-rb-green)",
|
||||
running: "var(--color-rb-green)",
|
||||
done: "var(--color-text-muted)",
|
||||
};
|
||||
---
|
||||
@@ -41,10 +41,10 @@ const statusColor: Record<string, string> = {
|
||||
<section class="section pt-0">
|
||||
<div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6">
|
||||
{projects.map((p: any) => (
|
||||
<a href={`/projekte/${p.slug.current}`} class="card group">
|
||||
{p.image?.asset?.url ? (
|
||||
<a href={`/projekte/${p.slug}`} class="card group">
|
||||
{p.image ? (
|
||||
<div class="aspect-video rounded-xl overflow-hidden mb-4 bg-[var(--color-surface-alt)]">
|
||||
<img src={`${p.image.asset.url}?w=600&auto=format`} alt={p.title} class="w-full h-full object-cover group-hover:scale-105 transition-transform duration-300" />
|
||||
<img src={imageUrl(p.image, 600)} alt={p.title} class="w-full h-full object-cover group-hover:scale-105 transition-transform duration-300" />
|
||||
</div>
|
||||
) : (
|
||||
<div class="aspect-video rounded-xl mb-4 bg-[var(--color-surface-alt)] flex items-center justify-center text-4xl">🎭</div>
|
||||
@@ -58,8 +58,8 @@ const statusColor: Record<string, string> = {
|
||||
</div>
|
||||
<h3 class="font-bold text-lg mb-2 group-hover:text-[var(--color-primary)] transition-colors">{p.title}</h3>
|
||||
<p class="text-sm text-[var(--color-text-muted)] line-clamp-3">{p.summary}</p>
|
||||
{p.targetGroup && (
|
||||
<p class="text-xs text-[var(--color-text-muted)] mt-3">👥 {p.targetGroup}</p>
|
||||
{p.target_group && (
|
||||
<p class="text-xs text-[var(--color-text-muted)] mt-3">👥 {p.target_group}</p>
|
||||
)}
|
||||
</a>
|
||||
))}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
---
|
||||
import Layout from "../layouts/Layout.astro";
|
||||
import { getTeam } from "../lib/sanity";
|
||||
import { getTeam, imageUrl } from "../lib/directus";
|
||||
|
||||
const team = await getTeam();
|
||||
|
||||
@@ -96,8 +96,8 @@ const roleLabel: Record<string, string> = {
|
||||
{team.map((member: any) => (
|
||||
<div class="text-center">
|
||||
<div class="w-20 h-20 mx-auto mb-3 rounded-full bg-[var(--color-primary-light)] flex items-center justify-center text-white text-2xl overflow-hidden">
|
||||
{member.photo?.asset
|
||||
? <img src={member.photo.asset.url} alt={member.name} class="w-full h-full object-cover" />
|
||||
{member.photo
|
||||
? <img src={imageUrl(member.photo, 80)} alt={member.name} class="w-full h-full object-cover" />
|
||||
: "👤"}
|
||||
</div>
|
||||
<p class="font-semibold">{member.name}</p>
|
||||
|
||||
@@ -1,10 +1,8 @@
|
||||
---
|
||||
import Layout from "../layouts/Layout.astro";
|
||||
import { getSettings } from "../lib/sanity";
|
||||
import { getSettings } from "../lib/directus";
|
||||
|
||||
const settings = await getSettings();
|
||||
const bank = settings?.bank;
|
||||
const contact = settings?.contact;
|
||||
---
|
||||
|
||||
<Layout
|
||||
@@ -35,11 +33,11 @@ const contact = settings?.contact;
|
||||
Überweise direkt auf unser Vereinskonto. Kein Formular, kein Aufwand.
|
||||
Spendenquittung auf Wunsch.
|
||||
</p>
|
||||
{bank?.iban ? (
|
||||
{settings?.iban ? (
|
||||
<div class="bg-[var(--color-surface-alt)] rounded-xl p-4 text-sm space-y-1">
|
||||
<p><span class="font-medium">Empfänger:</span> {bank.accountHolder}</p>
|
||||
<p><span class="font-medium">IBAN:</span> <span class="font-mono">{bank.iban}</span></p>
|
||||
{bank.bic && <p><span class="font-medium">BIC:</span> <span class="font-mono">{bank.bic}</span></p>}
|
||||
<p><span class="font-medium">Empfänger:</span> Kitafreunde Regenbogen e.V.</p>
|
||||
<p><span class="font-medium">IBAN:</span> <span class="font-mono">{settings.iban}</span></p>
|
||||
{settings.bic && <p><span class="font-medium">BIC:</span> <span class="font-mono">{settings.bic}</span></p>}
|
||||
<p><span class="font-medium">Verwendung:</span> Spende Kitafreunde Regenbogen</p>
|
||||
</div>
|
||||
) : (
|
||||
@@ -67,9 +65,9 @@ const contact = settings?.contact;
|
||||
Spielzeug, Bücher, Materialien, Werkzeug, Pflanzen für den Garten —
|
||||
frag uns, was gerade gebraucht wird. Wir stimmen mit der Kita ab.
|
||||
</p>
|
||||
{contact?.email && (
|
||||
<a href={`mailto:${contact.email}?subject=Sachspende`} class="btn-secondary text-sm">
|
||||
{contact.email}
|
||||
{settings?.contact_email && (
|
||||
<a href={`mailto:${settings.contact_email}?subject=Sachspende`} class="btn-secondary text-sm">
|
||||
{settings.contact_email}
|
||||
</a>
|
||||
)}
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user