135 lines
6.3 KiB
JavaScript
135 lines
6.3 KiB
JavaScript
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) =>
|
||
({
|
||
"&": "&",
|
||
"<": "<",
|
||
">": ">",
|
||
'"': """,
|
||
"'": "'",
|
||
})[character],
|
||
);
|
||
}
|
||
|
||
function contactName(person) {
|
||
return (
|
||
(typeof person.spitzname === "string" && person.spitzname.trim()) ||
|
||
person.vorname ||
|
||
person.name.trim().split(/\s+/)[0]
|
||
);
|
||
}
|
||
|
||
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="package-card advisor-card">
|
||
<div class="advisor-gallery" data-current-index="0" role="region" aria-roledescription="Karussell" aria-label="Bilder von ${escapeHtml(person.name)}">
|
||
<div class="advisor-gallery__frame">
|
||
<div class="advisor-gallery__track">
|
||
${(person.bilder || [])
|
||
.map(
|
||
(bild, index) => `
|
||
<figure class="advisor-gallery__slide" aria-label="${index + 1} von ${person.bilder.length}: ${escapeHtml(bild.typ)}" aria-hidden="${index !== 0}">
|
||
<img class="advisor-gallery__image" src="${escapeHtml(bild.quelle)}" alt="${escapeHtml(person.name)} – ${escapeHtml(bild.typ)}" loading="${index === 0 ? "eager" : "lazy"}" />
|
||
</figure>`,
|
||
)
|
||
.join("")}
|
||
</div>
|
||
<button class="advisor-gallery__arrow advisor-gallery__arrow--previous" type="button" data-gallery-direction="previous" aria-label="Vorheriges Bild von ${escapeHtml(person.name)}">‹</button>
|
||
<button class="advisor-gallery__arrow advisor-gallery__arrow--next" type="button" data-gallery-direction="next" aria-label="Nächstes Bild von ${escapeHtml(person.name)}">›</button>
|
||
</div>
|
||
<div class="advisor-gallery__pagination" aria-label="Bildposition">
|
||
${(person.bilder || [])
|
||
.map(
|
||
(bild, index) => `
|
||
<button class="advisor-gallery__dot" type="button" data-gallery-index="${index}" aria-label="Bild ${index + 1}: ${escapeHtml(bild.typ)}" aria-current="${index === 0}"></button>`,
|
||
)
|
||
.join("")}
|
||
</div>
|
||
</div>
|
||
<h3 class="package-title">${escapeHtml(person.name)}</h3>
|
||
<p class="package-type advisor-role">${escapeHtml(person.titel || "Ernährungsberatung")}</p>
|
||
<div class="package-details-box">
|
||
<div class="detail-item">
|
||
<h4 class="detail-title">Kurzbiografie</h4>
|
||
<p class="detail-text">${escapeHtml(person.biographie)}</p>
|
||
</div>
|
||
<div class="detail-item">
|
||
<h4 class="detail-title">Qualifikationen</h4>
|
||
<ul class="detail-list">${(person.qualifikationen || []).map((qualification) => `<li>${escapeHtml(qualification)}</li>`).join("")}</ul>
|
||
</div>
|
||
<div class="detail-item">
|
||
<h4 class="detail-title">Schwerpunkte</h4>
|
||
<ul class="detail-list">${(person.schwerpunkte || []).map((focus) => `<li>${escapeHtml(focus)}</li>`).join("")}</ul>
|
||
</div>
|
||
</div>
|
||
<button class="package-btn advisor-contact" type="button" data-berater-id="${escapeHtml(person.id)}" aria-haspopup="dialog">${escapeHtml(contactName(person))} kontaktieren</button>
|
||
</article>`,
|
||
)
|
||
.join("");
|
||
}
|
||
}
|
||
|
||
document.addEventListener("click", async (event) => {
|
||
const galleryControl = event.target.closest(
|
||
"button[data-gallery-direction], button[data-gallery-index]",
|
||
);
|
||
if (galleryControl) {
|
||
const gallery = galleryControl.closest(".advisor-gallery");
|
||
const slides = [...gallery.querySelectorAll(".advisor-gallery__slide")];
|
||
const currentIndex = Number(gallery.dataset.currentIndex || 0);
|
||
const requestedIndex = galleryControl.hasAttribute(
|
||
"data-gallery-direction",
|
||
)
|
||
? currentIndex +
|
||
(galleryControl.dataset.galleryDirection === "next" ? 1 : -1)
|
||
: Number(galleryControl.dataset.galleryIndex);
|
||
const nextIndex = (requestedIndex + slides.length) % slides.length;
|
||
|
||
gallery.dataset.currentIndex = String(nextIndex);
|
||
gallery.querySelector(".advisor-gallery__track").style.transform =
|
||
`translateX(-${nextIndex * 100}%)`;
|
||
slides.forEach((slide, index) => {
|
||
slide.setAttribute("aria-hidden", String(index !== nextIndex));
|
||
});
|
||
gallery.querySelectorAll("[data-gallery-index]").forEach((button) => {
|
||
button.setAttribute(
|
||
"aria-current",
|
||
String(Number(button.dataset.galleryIndex) === nextIndex),
|
||
);
|
||
});
|
||
return;
|
||
}
|
||
|
||
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;
|
||
}
|
||
});
|