refactor(post-events): read event data from resolved calendarItem
Replace direct post.isEvent/eventDate/eventLocation reads with getPostEventInfo() helper that derives info from the resolved calendarItem reference. Loaders now resolve "calendarItem" and request that field instead of legacy flat properties. Also regenerate iconify mdi subset (map-marker-outline, download, google, calendar-search, refresh). Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
+75
-35
@@ -121,6 +121,44 @@ export function filterPostsByTag(
|
||||
});
|
||||
}
|
||||
|
||||
export type PostEventLocation = {
|
||||
text?: string;
|
||||
lat?: number;
|
||||
lng?: number;
|
||||
};
|
||||
|
||||
export type PostEventInfo = {
|
||||
isEvent: boolean;
|
||||
eventDate: string | null;
|
||||
eventLocation: PostEventLocation | null;
|
||||
};
|
||||
|
||||
/**
|
||||
* Liest event-Daten aus dem aufgelösten calendarItem-Reference des Posts.
|
||||
* Voraussetzung: post mit resolve=["all"] oder explizit "calendarItem" geladen,
|
||||
* sodass `post.calendarItem` als Objekt mit terminZeit/location vorliegt.
|
||||
*/
|
||||
export function getPostEventInfo(
|
||||
post: PostEntry | null | undefined,
|
||||
): PostEventInfo {
|
||||
const ci = (post as { calendarItem?: unknown } | null | undefined)?.calendarItem;
|
||||
if (!ci || typeof ci !== "object") {
|
||||
return { isEvent: false, eventDate: null, eventLocation: null };
|
||||
}
|
||||
const obj = ci as {
|
||||
terminZeit?: string;
|
||||
location?: PostEventLocation;
|
||||
};
|
||||
if (!obj.terminZeit) {
|
||||
return { isEvent: false, eventDate: null, eventLocation: null };
|
||||
}
|
||||
return {
|
||||
isEvent: true,
|
||||
eventDate: obj.terminZeit,
|
||||
eventLocation: obj.location ?? null,
|
||||
};
|
||||
}
|
||||
|
||||
/** URL aus post.postImage (RustyCMS img mit src, oder legacy file.url). */
|
||||
export function getPostImageUrl(
|
||||
postImage: PostEntry["postImage"],
|
||||
@@ -200,32 +238,33 @@ export type PostSearchIndexEntry = {
|
||||
eventDate: string | null;
|
||||
};
|
||||
|
||||
type PostWithEventIndex = PostEntry & {
|
||||
isEvent?: boolean;
|
||||
eventDate?: string;
|
||||
type PostWithImageIndex = PostEntry & {
|
||||
_resolvedImageUrl?: string;
|
||||
};
|
||||
|
||||
export function buildPostSearchIndex(posts: PostEntry[]): PostSearchIndexEntry[] {
|
||||
return posts.map((p) => ({
|
||||
slug: p.slug ?? p._slug ?? "",
|
||||
headline: p.headline ?? "",
|
||||
excerpt: p.excerpt ?? "",
|
||||
subheadline: p.subheadline ?? "",
|
||||
tags: (p.postTag ?? [])
|
||||
.map((t) =>
|
||||
typeof t === "string"
|
||||
? t
|
||||
: (t as { name?: string; _slug?: string }).name ??
|
||||
(t as { _slug?: string })._slug ??
|
||||
"",
|
||||
)
|
||||
.filter(Boolean),
|
||||
image: (p as PostWithEventIndex)._resolvedImageUrl ?? null,
|
||||
created: p.created ?? null,
|
||||
isEvent: Boolean((p as PostWithEventIndex).isEvent),
|
||||
eventDate: (p as PostWithEventIndex).eventDate ?? null,
|
||||
}));
|
||||
return posts.map((p) => {
|
||||
const ev = getPostEventInfo(p);
|
||||
return {
|
||||
slug: p.slug ?? p._slug ?? "",
|
||||
headline: p.headline ?? "",
|
||||
excerpt: p.excerpt ?? "",
|
||||
subheadline: p.subheadline ?? "",
|
||||
tags: (p.postTag ?? [])
|
||||
.map((t) =>
|
||||
typeof t === "string"
|
||||
? t
|
||||
: (t as { name?: string; _slug?: string }).name ??
|
||||
(t as { _slug?: string })._slug ??
|
||||
"",
|
||||
)
|
||||
.filter(Boolean),
|
||||
image: (p as PostWithImageIndex)._resolvedImageUrl ?? null,
|
||||
created: p.created ?? null,
|
||||
isEvent: ev.isEvent,
|
||||
eventDate: ev.eventDate,
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
export type LoadPostsResult = {
|
||||
@@ -260,7 +299,7 @@ export async function loadPostsList(opts: {
|
||||
// (post → postImage → img-Sidecar; post → postTag → name/icon/color).
|
||||
[posts, tags] = await Promise.all([
|
||||
getPosts({
|
||||
resolve: ["postImage", "postTag"],
|
||||
resolve: ["postImage", "postTag", "calendarItem"],
|
||||
depth: 2,
|
||||
fields: [
|
||||
"_slug",
|
||||
@@ -270,8 +309,7 @@ export async function loadPostsList(opts: {
|
||||
"subheadline",
|
||||
"excerpt",
|
||||
"created",
|
||||
"isEvent",
|
||||
"eventDate",
|
||||
"calendarItem",
|
||||
"hideFromListing",
|
||||
"postImage.*",
|
||||
"postTag.name",
|
||||
@@ -342,16 +380,18 @@ export async function loadPostsList(opts: {
|
||||
|
||||
export function selectUpcomingEvents(posts: PostEntry[], max = 4): PostEntry[] {
|
||||
const now = Date.now();
|
||||
return (posts as PostWithEventIndex[])
|
||||
.filter(
|
||||
(p) => p.isEvent && p.eventDate && new Date(p.eventDate).getTime() >= now,
|
||||
)
|
||||
.sort(
|
||||
(a, b) =>
|
||||
new Date(a.eventDate ?? 0).getTime() -
|
||||
new Date(b.eventDate ?? 0).getTime(),
|
||||
)
|
||||
.slice(0, max) as PostEntry[];
|
||||
type Scored = { post: PostEntry; ts: number };
|
||||
const scored: Scored[] = [];
|
||||
for (const p of posts) {
|
||||
const ev = getPostEventInfo(p);
|
||||
if (!ev.isEvent || !ev.eventDate) continue;
|
||||
const ts = new Date(ev.eventDate).getTime();
|
||||
if (ts >= now) scored.push({ post: p, ts });
|
||||
}
|
||||
return scored
|
||||
.sort((a, b) => a.ts - b.ts)
|
||||
.slice(0, max)
|
||||
.map((s) => s.post);
|
||||
}
|
||||
|
||||
const POST_BASE = "/post";
|
||||
|
||||
Reference in New Issue
Block a user