Beraterkarten sowie Daten erstellt und in index.html eingebunden

feature/beraterprofile
Majd Ibrahim 2026-09-29 14:39:25 +00:00
parent 55e4904482
commit 370d54fb39
4 changed files with 188 additions and 0 deletions

View File

@ -920,6 +920,21 @@
</section>
<!-- User Story: Rezensionen -->
<section class="section advisor-section" id="beraterteam" aria-labelledby="advisor-heading">
<div class="container">
<div class="section-heading">
<div>
<p class="eyebrow">PERSÖNLICH FÜR DICH DA</p>
<h2 id="advisor-heading">Lerne deine Ernährungsberater kennen.</h2>
</div>
<p class="section-intro">Hinter einer guten Ernährungsstrategie stehen Menschen, die zuhören und ihre Erfahrung in deinen Alltag und dein Training einbringen.</p>
</div>
<div class="advisor-grid" id="advisor-profiles" aria-live="polite">
<p class="advisor-loading">Beraterprofile werden geladen …</p>
</div>
</div>
</section>
<section class="section review-section" id="erfahrungen">
<div class="container review-layout">
<div class="review-copy">
@ -1118,6 +1133,7 @@
type="module"
src="/src/components/navigationsbar/navigation.js"
></script>
<script type="module" src="/src/features/berater/berater-entry.js"></script>
<script>
const form = document.getElementById("reviewForm");
const status = document.getElementById("formStatus");

View File

@ -0,0 +1,51 @@
import berater from "../../shared/berater-daten.json";
import "./berater.css";
const container = document.querySelector("#advisor-profiles");
function escapeHtml(value) {
return String(value).replace(/[&<>"']/g, (character) => ({
"&": "&amp;",
"<": "&lt;",
">": "&gt;",
'"': "&quot;",
"'": "&#039;",
})[character]);
}
if (container) {
if (!Array.isArray(berater) || berater.length === 0) {
container.innerHTML = '<p class="advisor-loading">Aktuell sind keine Beraterprofile verfügbar.</p>';
} else {
container.innerHTML = berater.map((person) => `
<article class="advisor-card">
<div class="advisor-card__topline"></div>
<h3>${escapeHtml(person.name)}</h3>
<p class="advisor-card__role">${escapeHtml(person.titel || "Ernährungsberatung")}</p>
<div class="advisor-card__details">
<h4>Kurzbiografie</h4>
<p>${escapeHtml(person.biographie)}</p>
<h4>Qualifikationen</h4>
<ul>${(person.qualifikationen || []).map((qualification) => `<li>${escapeHtml(qualification)}</li>`).join("")}</ul>
<h4>Schwerpunkte</h4>
<ul>${(person.schwerpunkte || []).map((focus) => `<li>${escapeHtml(focus)}</li>`).join("")}</ul>
</div>
<button class="advisor-contact" type="button" data-berater-id="${escapeHtml(person.id)}" aria-haspopup="dialog">Berater kontaktieren</button>
</article>
`).join("");
}
}
document.addEventListener("click", async (event) => {
const button = event.target.closest("button[data-berater-id]");
if (!button) return;
try {
const { openContactDialog } = await import(/* @vite-ignore */ "../contact/contact-dialog.js");
openContactDialog({ advisorId: button.dataset.beraterId });
} catch (error) {
console.error("Das Kontaktformular ist derzeit nicht verfügbar.", error);
button.insertAdjacentHTML("afterend", '<p class="advisor-contact-error" role="status">Das Kontaktformular ist momentan nicht verfügbar.</p>');
button.disabled = true;
}
});

View File

@ -0,0 +1,109 @@
.advisor-section {
background: linear-gradient(180deg, rgba(255, 255, 255, 0.018), rgba(255, 255, 255, 0.035));
}
.advisor-grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(min(100%, 290px), 1fr));
gap: 20px;
}
.advisor-loading {
grid-column: 1 / -1;
color: var(--muted);
}
.advisor-card {
display: flex;
flex-direction: column;
padding: 30px;
border: 1px solid rgba(248, 250, 252, 0.12);
border-radius: 28px;
background: linear-gradient(145deg, rgba(255, 255, 255, 0.075), rgba(255, 255, 255, 0.025));
}
.advisor-card__topline {
height: 3px;
margin: -30px -30px 26px;
border-radius: 28px 28px 0 0;
background: #10b981;
opacity: 0.75;
}
.advisor-card h3 {
color: #fff;
font-family: Inter, system-ui, sans-serif;
font-size: 1.8rem;
}
.advisor-card__role {
margin-top: 8px;
color: #10b981;
font-size: 0.78rem;
font-weight: 700;
letter-spacing: 0.14em;
text-transform: uppercase;
}
.advisor-card__details {
flex: 1;
margin-top: 24px;
padding: 22px;
border: 1px solid rgba(248, 250, 252, 0.1);
border-radius: 22px;
background: rgba(255, 255, 255, 0.045);
}
.advisor-card__details h4 {
margin: 0 0 8px;
color: #fff;
font-size: 0.94rem;
}
.advisor-card__details p,
.advisor-card__details li {
color: var(--muted);
font-size: 0.92rem;
line-height: 1.8;
}
.advisor-card__details h4:not(:first-child) {
margin-top: 18px;
}
.advisor-card__details ul {
margin: 0;
padding-left: 18px;
}
.advisor-contact {
min-height: 58px;
margin-top: 24px;
padding: 10px 22px;
border: 1px solid rgba(148, 163, 184, 0.25);
border-radius: 999px;
color: #fff;
background: rgba(255, 255, 255, 0.025);
font: inherit;
font-weight: 700;
cursor: pointer;
transition: background 0.2s ease, border-color 0.2s ease, transform 0.2s ease;
}
.advisor-contact:hover,
.advisor-contact:focus-visible {
transform: translateY(-1px);
border-color: rgba(16, 185, 129, 0.55);
background: rgba(255, 255, 255, 0.1);
}
.advisor-contact:focus-visible {
outline: 3px solid rgba(16, 185, 129, 0.45);
outline-offset: 3px;
}
.advisor-contact-error {
margin-top: 10px;
color: #fca5a5;
font-size: 0.82rem;
}

View File

@ -0,0 +1,12 @@
[
{
"id": "anna-mueller",
"name": "Anna Müller",
"email": "anna.mueller@tri-hub.de",
"titel": "Ernährungsberaterin",
"biographie": "Anna begleitet Ausdauerathletinnen und -athleten dabei, eine alltagstaugliche Ernährungsweise zu entwickeln. Im Mittelpunkt stehen individuelle Bedürfnisse, praktikable Routinen und eine gute Versorgung rund um Training und Wettkampf.",
"qualifikationen": ["Ernährungsberatung", "Sporternährung"],
"schwerpunkte": ["Sporternährung", "Wettkampfverpflegung", "Hydration"],
"bildquelle": ""
}
]