calendar working
All checks were successful
/ deploy (push) Successful in 41s

This commit is contained in:
Loyyd 2026-06-11 17:13:37 +02:00
parent 2c41a374ec
commit 6387b4d92a
5 changed files with 633 additions and 500 deletions

View file

@ -9,41 +9,317 @@ import mainHtml from "../content/pages/events.html?raw";
bodyClass="archive post-type-archive post-type-archive-tribe_events custom-background tribe-events-page-template tribe-no-js tribe-filter-live metaslider-plugin parabola-image-three caption-light meta-light parabola_triagles parabola-menu-left"
pathname="/events.html"
canonical="/events.html"
robots="noindex, follow, max-image-preview:large"
>
<Fragment slot="head">
<link rel="stylesheet" id="tec-variables-skeleton-css" href="/assets/vendor/events-calendar/common/src/resources/css/variables-skeleton.min.css?ver=6.3.2" type="text/css" media="all" />
<link rel="stylesheet" id="tec-variables-full-css" href="/assets/vendor/events-calendar/common/src/resources/css/variables-full.min.css?ver=6.3.2" type="text/css" media="all" />
<link rel="stylesheet" id="tribe-common-skeleton-style-css" href="/assets/vendor/events-calendar/common/src/resources/css/common-skeleton.min.css?ver=6.3.2" type="text/css" media="all" />
<link rel="stylesheet" id="tribe-common-full-style-css" href="/assets/vendor/events-calendar/common/src/resources/css/common-full.min.css?ver=6.3.2" type="text/css" media="all" />
<link rel="stylesheet" id="tribe-events-views-v2-bootstrap-datepicker-styles-css" href="/assets/vendor/events-calendar/vendor/bootstrap-datepicker/css/bootstrap-datepicker.standalone.min.css?ver=6.8.2.1" type="text/css" media="all" />
<link rel="stylesheet" id="tribe-tooltipster-css-css" href="/assets/vendor/events-calendar/common/vendor/tooltipster/tooltipster.bundle.min.css?ver=6.3.2" type="text/css" media="all" />
<link rel="stylesheet" id="tribe-events-views-v2-skeleton-css" href="/assets/vendor/events-calendar/src/resources/css/views-skeleton.min.css?ver=6.8.2.1" type="text/css" media="all" />
<link rel="stylesheet" id="tribe-events-views-v2-full-css" href="/assets/vendor/events-calendar/src/resources/css/views-full.min.css?ver=6.8.2.1" type="text/css" media="all" />
<link rel="stylesheet" id="tribe-events-views-v2-print-css" href="/assets/vendor/events-calendar/src/resources/css/views-print.min.css?ver=6.8.2.1" type="text/css" media="print" />
<script is:inline type="text/javascript" src="/assets/vendor/events-calendar/common/src/resources/js/tribe-common.min.js?ver=6.3.2" id="tribe-common-js"></script>
<script is:inline type="text/javascript" src="/assets/vendor/events-calendar/src/resources/js/views/breakpoints.min.js?ver=6.8.2.1" id="tribe-events-views-v2-breakpoints-js"></script>
</Fragment>
<Fragment set:html={mainHtml} />
<Fragment slot="scripts">
<script is:inline type="text/javascript" src="/assets/vendor/events-calendar/src/resources/js/views/viewport.min.js?ver=6.8.2.1" id="tribe-events-views-v2-viewport-js"></script>
<script is:inline type="text/javascript" src="/assets/vendor/events-calendar/src/resources/js/views/accordion.min.js?ver=6.8.2.1" id="tribe-events-views-v2-accordion-js"></script>
<script is:inline type="text/javascript" src="/assets/vendor/events-calendar/src/resources/js/views/view-selector.min.js?ver=6.8.2.1" id="tribe-events-views-v2-view-selector-js"></script>
<script is:inline type="text/javascript" src="/assets/vendor/events-calendar/vendor/bootstrap-datepicker/js/bootstrap-datepicker.min.js?ver=6.8.2.1" id="tribe-events-views-v2-bootstrap-datepicker-js"></script>
<script is:inline type="text/javascript" src="/assets/vendor/events-calendar/src/resources/js/views/datepicker.min.js?ver=6.8.2.1" id="tribe-events-views-v2-datepicker-js"></script>
<script is:inline type="text/javascript" src="/assets/vendor/events-calendar/src/resources/js/views/ical-links.min.js?ver=6.8.2.1" id="tribe-events-views-v2-ical-links-js"></script>
<script is:inline type="text/javascript" src="/assets/vendor/events-calendar/src/resources/js/views/events-bar-inputs.min.js?ver=6.8.2.1" id="tribe-events-views-v2-events-bar-inputs-js"></script>
<script is:inline type="text/javascript" src="/assets/vendor/events-calendar/src/resources/js/views/events-bar.min.js?ver=6.8.2.1" id="tribe-events-views-v2-events-bar-js"></script>
<script is:inline type="text/javascript" src="/assets/vendor/events-calendar/common/vendor/tooltipster/tooltipster.bundle.min.js?ver=6.3.2" id="tribe-tooltipster-js"></script>
<script is:inline type="text/javascript" src="/assets/vendor/events-calendar/src/resources/js/views/tooltip.min.js?ver=6.8.2.1" id="tribe-events-views-v2-tooltip-js"></script>
<script is:inline type="text/javascript" src="/assets/vendor/events-calendar/src/resources/js/views/navigation-scroll.min.js?ver=6.8.2.1" id="tribe-events-views-v2-navigation-scroll-js"></script>
<script is:inline type="text/javascript" src="/assets/vendor/events-calendar/src/resources/js/views/multiday-events.min.js?ver=6.8.2.1" id="tribe-events-views-v2-multiday-events-js"></script>
<script is:inline type="text/javascript" src="/assets/vendor/events-calendar/src/resources/js/views/month-mobile-events.min.js?ver=6.8.2.1" id="tribe-events-views-v2-month-mobile-events-js"></script>
<script is:inline type="text/javascript" src="/assets/vendor/events-calendar/src/resources/js/views/month-grid.min.js?ver=6.8.2.1" id="tribe-events-views-v2-month-grid-js"></script>
<script is:inline type="text/javascript" src="/assets/vendor/events-calendar/common/src/resources/js/utils/query-string.min.js?ver=6.3.2" id="tribe-query-string-js"></script>
<script is:inline src="/assets/vendor/events-calendar/common/src/resources/js/underscore-before.js"></script>
<script is:inline type="text/javascript" src="/assets/vendor/underscore.min.js?ver=1.13.4" id="underscore-js"></script>
<script is:inline src="/assets/vendor/events-calendar/common/src/resources/js/underscore-after.js"></script>
<script is:inline defer type="text/javascript" src="/assets/vendor/events-calendar/src/resources/js/views/manager.min.js?ver=6.8.2.1" id="tribe-events-views-v2-manager-js"></script>
</Fragment>
<script is:inline slot="scripts">
(() => {
const calendar = document.querySelector("[data-calendar]");
if (!calendar) {
return;
}
const recurringEvents = [
{
id: "sunday-service",
title: "Sunday Service",
startTime: "11:00",
endTime: "12:00",
location: "19 North Great Georges St., Dublin 1, Ireland",
details: "Weekly Sunday Service video upload and community worship.",
url: "/services.html",
weekday: 0,
},
];
const monthTitle = calendar.querySelector("[data-calendar-panel-title]");
const grid = calendar.querySelector("[data-calendar-grid]");
const list = calendar.querySelector("[data-calendar-list]");
const title = calendar.querySelector("#calendar-month-title");
const search = calendar.querySelector("[data-calendar-search]");
const today = new Date();
let visibleMonth = new Date(today.getFullYear(), today.getMonth(), 1);
let selectedDate = toDateKey(today);
const monthFormatter = new Intl.DateTimeFormat("en-IE", { month: "long", year: "numeric" });
const dayFormatter = new Intl.DateTimeFormat("en-IE", { weekday: "long", day: "numeric", month: "long", year: "numeric" });
function pad(value) {
return String(value).padStart(2, "0");
}
function toDateKey(date) {
return `${date.getFullYear()}-${pad(date.getMonth() + 1)}-${pad(date.getDate())}`;
}
function fromDateKey(key) {
const [year, month, day] = key.split("-").map(Number);
return new Date(year, month - 1, day);
}
function addDays(date, days) {
const next = new Date(date);
next.setDate(next.getDate() + days);
return next;
}
function isSameMonth(date, month) {
return date.getFullYear() === month.getFullYear() && date.getMonth() === month.getMonth();
}
function monthStartGridDate(month) {
const first = new Date(month.getFullYear(), month.getMonth(), 1);
const mondayIndex = (first.getDay() + 6) % 7;
return addDays(first, -mondayIndex);
}
function monthEndGridDate(month) {
const last = new Date(month.getFullYear(), month.getMonth() + 1, 0);
const sundayIndex = (7 - last.getDay()) % 7;
return addDays(last, sundayIndex);
}
function eventMatchesQuery(event) {
const query = String(search?.value || "").trim().toLowerCase();
if (!query) {
return true;
}
return [event.title, event.location, event.details]
.join(" ")
.toLowerCase()
.includes(query);
}
function eventsBetween(start, end) {
const events = [];
for (let date = new Date(start); date <= end; date = addDays(date, 1)) {
for (const recurringEvent of recurringEvents) {
if (date.getDay() !== recurringEvent.weekday) {
continue;
}
const dateKey = toDateKey(date);
events.push({
...recurringEvent,
id: `${recurringEvent.id}-${dateKey}`,
date: dateKey,
});
}
}
return events.filter(eventMatchesQuery);
}
function formatEventTime(event) {
return `${event.startTime} - ${event.endTime}`;
}
function renderGrid() {
const start = monthStartGridDate(visibleMonth);
const end = monthEndGridDate(visibleMonth);
const events = eventsBetween(start, end);
const eventsByDate = new Map();
for (const event of events) {
const dayEvents = eventsByDate.get(event.date) || [];
dayEvents.push(event);
eventsByDate.set(event.date, dayEvents);
}
title.textContent = monthFormatter.format(visibleMonth);
grid.innerHTML = "";
for (let date = new Date(start); date <= end; date = addDays(date, 1)) {
const dateKey = toDateKey(date);
const dayEvents = eventsByDate.get(dateKey) || [];
const cell = document.createElement("button");
cell.type = "button";
cell.className = "calendar-day";
cell.dataset.date = dateKey;
cell.setAttribute("role", "gridcell");
cell.setAttribute("aria-label", `${dayFormatter.format(date)}${dayEvents.length ? `, ${dayEvents.length} event` : ""}`);
if (!isSameMonth(date, visibleMonth)) {
cell.classList.add("calendar-day-muted");
}
if (dateKey === toDateKey(today)) {
cell.classList.add("calendar-day-today");
}
if (dateKey === selectedDate) {
cell.classList.add("calendar-day-selected");
cell.setAttribute("aria-selected", "true");
}
const number = document.createElement("span");
number.className = "calendar-day-number";
number.textContent = String(date.getDate());
cell.append(number);
const eventStack = document.createElement("span");
eventStack.className = "calendar-day-events";
for (const event of dayEvents.slice(0, 2)) {
const pill = document.createElement("span");
pill.className = "calendar-event-pill";
pill.textContent = event.title;
eventStack.append(pill);
}
if (dayEvents.length > 2) {
const more = document.createElement("span");
more.className = "calendar-event-more";
more.textContent = `+${dayEvents.length - 2} more`;
eventStack.append(more);
}
cell.append(eventStack);
cell.addEventListener("click", () => {
selectedDate = dateKey;
render();
});
grid.append(cell);
}
}
function renderList() {
const selected = fromDateKey(selectedDate);
const monthStart = new Date(visibleMonth.getFullYear(), visibleMonth.getMonth(), 1);
const monthEnd = new Date(visibleMonth.getFullYear(), visibleMonth.getMonth() + 1, 0);
const selectedEvents = eventsBetween(selected, selected);
const upcomingEvents = eventsBetween(monthStart, monthEnd)
.filter((event) => event.date >= toDateKey(today))
.slice(0, 8);
const events = selectedEvents.length ? selectedEvents : upcomingEvents;
monthTitle.textContent = selectedEvents.length
? dayFormatter.format(selected)
: "Upcoming Events";
list.innerHTML = "";
if (!events.length) {
const empty = document.createElement("p");
empty.className = "calendar-empty";
empty.textContent = "No matching events for this view.";
list.append(empty);
return;
}
for (const event of events) {
const item = document.createElement("article");
item.className = "calendar-event-card";
const eventDate = fromDateKey(event.date);
const date = document.createElement("p");
date.className = "calendar-event-date";
date.textContent = dayFormatter.format(eventDate);
const heading = document.createElement("h4");
heading.textContent = event.title;
const time = document.createElement("p");
time.textContent = formatEventTime(event);
const location = document.createElement("p");
location.textContent = event.location;
const details = document.createElement("p");
details.textContent = event.details;
const link = document.createElement("a");
link.href = event.url;
link.textContent = "Sunday Services";
item.append(date, heading, time, location, details, link);
list.append(item);
}
}
function icsDate(dateKey, time) {
const [year, month, day] = dateKey.split("-");
const [hours, minutes] = time.split(":");
return `${year}${month}${day}T${hours}${minutes}00`;
}
function escapeIcs(value) {
return String(value)
.replace(/\\/g, "\\\\")
.replace(/;/g, "\\;")
.replace(/,/g, "\\,")
.replace(/\n/g, "\\n");
}
function exportCalendar() {
const start = new Date(today.getFullYear(), today.getMonth(), 1);
const end = new Date(today.getFullYear() + 1, today.getMonth() + 1, 0);
const events = eventsBetween(start, end);
const stamp = new Date().toISOString().replace(/[-:]/g, "").replace(/\.\d{3}Z$/, "Z");
const lines = [
"BEGIN:VCALENDAR",
"VERSION:2.0",
"PRODID:-//FFWPU Ireland//Events Calendar//EN",
"CALSCALE:GREGORIAN",
"METHOD:PUBLISH",
];
for (const event of events) {
lines.push(
"BEGIN:VEVENT",
`UID:${event.id}@familyfed.ie`,
`DTSTAMP:${stamp}`,
`DTSTART:${icsDate(event.date, event.startTime)}`,
`DTEND:${icsDate(event.date, event.endTime)}`,
`SUMMARY:${escapeIcs(event.title)}`,
`DESCRIPTION:${escapeIcs(event.details)}`,
`LOCATION:${escapeIcs(event.location)}`,
`URL:https://familyfed.bcgen.ie${event.url}`,
"END:VEVENT",
);
}
lines.push("END:VCALENDAR");
const blob = new Blob([lines.join("\r\n")], { type: "text/calendar;charset=utf-8" });
const url = URL.createObjectURL(blob);
const link = document.createElement("a");
link.href = url;
link.download = "familyfed-events.ics";
document.body.append(link);
link.click();
link.remove();
URL.revokeObjectURL(url);
}
function render() {
renderGrid();
renderList();
}
calendar.querySelector("[data-calendar-prev]")?.addEventListener("click", () => {
visibleMonth = new Date(visibleMonth.getFullYear(), visibleMonth.getMonth() - 1, 1);
selectedDate = toDateKey(new Date(visibleMonth.getFullYear(), visibleMonth.getMonth(), 1));
render();
});
calendar.querySelector("[data-calendar-next]")?.addEventListener("click", () => {
visibleMonth = new Date(visibleMonth.getFullYear(), visibleMonth.getMonth() + 1, 1);
selectedDate = toDateKey(new Date(visibleMonth.getFullYear(), visibleMonth.getMonth(), 1));
render();
});
calendar.querySelector("[data-calendar-today]")?.addEventListener("click", () => {
visibleMonth = new Date(today.getFullYear(), today.getMonth(), 1);
selectedDate = toDateKey(today);
render();
});
calendar.querySelector("[data-calendar-export]")?.addEventListener("click", exportCalendar);
search?.addEventListener("input", render);
render();
})();
</script>
</SiteLayout>