- Next.js 16 App Router site with the ANOUMA design system - Payload CMS (PostgreSQL) for offers, events, posts and page content - WebRTC video-call system with custom signaling server - SMTP email reminders and booking-request notifications - Customer accounts, calendar-based availability, and booking workflow
67 lines
2.0 KiB
TypeScript
67 lines
2.0 KiB
TypeScript
import { OFFER_CATEGORIES } from "@/collections/Offers";
|
|
import type { Offer } from "@/payload-types";
|
|
|
|
export type ImageMood =
|
|
| "plum"
|
|
| "dustyrose"
|
|
| "moss"
|
|
| "caramel"
|
|
| "mauve"
|
|
| "sand"
|
|
| "rose"
|
|
| "peach";
|
|
|
|
// Purely a presentation fallback for offers/events without an uploaded photo
|
|
// yet — content itself always comes from the CMS.
|
|
const moodBySlug: Record<string, ImageMood> = {
|
|
prozessbegleitung: "plum",
|
|
"doula-begleitung": "dustyrose",
|
|
erdenkinder: "moss",
|
|
maedchenkreis: "caramel",
|
|
"singen-im-kreis": "sand",
|
|
"singen-fuer-schwangere": "peach",
|
|
"mama-baby-singkreis": "rose",
|
|
};
|
|
|
|
const moodByCategory: Record<string, ImageMood> = {
|
|
prozessbegleitung: "plum",
|
|
"doula-begleitung": "dustyrose",
|
|
kindergruppen: "moss",
|
|
singkreise: "rose",
|
|
};
|
|
|
|
export function moodForOffer(offer: Pick<Offer, "slug" | "category">): ImageMood {
|
|
return (offer.slug ? moodBySlug[offer.slug] : undefined) ?? moodByCategory[offer.category] ?? "mauve";
|
|
}
|
|
|
|
export type OfferCategoryGroup = {
|
|
category: (typeof OFFER_CATEGORIES)[number]["value"];
|
|
label: string;
|
|
href: string;
|
|
offers: Offer[];
|
|
};
|
|
|
|
const categoryHref: Record<string, string> = {
|
|
prozessbegleitung: "/angebote/prozessbegleitung",
|
|
"doula-begleitung": "/angebote/doula-begleitung",
|
|
kindergruppen: "/angebote#kindergruppen",
|
|
singkreise: "/angebote/singkreise",
|
|
};
|
|
|
|
/**
|
|
* Groups the flat `offers` collection into the four nav/overview sections.
|
|
* Private offers (e.g. "Einzelbegleitung") are deliberately excluded — they
|
|
* stay reachable only via their own direct booking link.
|
|
*/
|
|
export function groupOffersByCategory(offers: Offer[]): OfferCategoryGroup[] {
|
|
const publicOffers = offers.filter((offer) => offer.visibility !== "private");
|
|
return OFFER_CATEGORIES.map(({ value, label }) => ({
|
|
category: value,
|
|
label,
|
|
href: categoryHref[value] ?? "/angebote",
|
|
offers: publicOffers
|
|
.filter((offer) => offer.category === value)
|
|
.sort((a, b) => (a.order ?? 0) - (b.order ?? 0)),
|
|
})).filter((group) => group.offers.length > 0);
|
|
}
|