Initial commit: ANOUMA website with Payload CMS, WebRTC meetings, and booking system
- 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
This commit is contained in:
@@ -0,0 +1,114 @@
|
||||
import Image from "next/image";
|
||||
import type { ImageMood } from "@/lib/angebote";
|
||||
|
||||
/**
|
||||
* Renders a photo when `src` is provided, otherwise falls back to a warm,
|
||||
* organic gradient placeholder in the given mood. Pages should always pass
|
||||
* `src` once real photography exists — no other prop changes are needed.
|
||||
*/
|
||||
|
||||
const moodGradients: Record<ImageMood, string> = {
|
||||
plum: "linear-gradient(135deg, #a97070 0%, #66505f 55%, #453238 100%)",
|
||||
dustyrose: "linear-gradient(135deg, #ecc8be 0%, #a97070 55%, #8b616d 100%)",
|
||||
moss: "linear-gradient(135deg, #d4c1a5 0%, #89937c 55%, #4f5b45 100%)",
|
||||
caramel: "linear-gradient(135deg, #efd5c8 0%, #d4c1a5 55%, #a47c60 100%)",
|
||||
mauve: "linear-gradient(135deg, #e5c4bc 0%, #8b6f7d 55%, #66505f 100%)",
|
||||
sand: "linear-gradient(135deg, #f2e2d6 0%, #e8dcc8 55%, #a89580 100%)",
|
||||
rose: "linear-gradient(135deg, #f0e0d3 0%, #e0b4b1 55%, #d19ca1 100%)",
|
||||
peach: "linear-gradient(135deg, #f2e2d6 0%, #ecc8be 55%, #e0b4b1 100%)",
|
||||
};
|
||||
|
||||
const moodIcons: Record<ImageMood, React.ReactNode> = {
|
||||
plum: (
|
||||
<path d="M32 4c8 6 12 14 12 22 0 9-6.5 16-12 16S20 35 20 26c0-8 4-16 12-22Zm0 60V38" />
|
||||
),
|
||||
dustyrose: <path d="M32 8c9 8 18 18 18 30a18 18 0 1 1-36 0c0-12 9-22 18-30Z" />,
|
||||
moss: (
|
||||
<path d="M32 60V24M32 24c-10 0-18-8-18-18 10 0 18 8 18 18Zm0 0c0-10 8-18 18-18 0 10-8 18-18 18Z" />
|
||||
),
|
||||
caramel: (
|
||||
<path d="M32 6c6 10 14 20 14 30a14 14 0 1 1-28 0c0-10 8-20 14-30Z" />
|
||||
),
|
||||
mauve: <path d="M8 32c8-14 16-20 24-20s16 6 24 20c-8 14-16 20-24 20S16 46 8 32Z" />,
|
||||
sand: (
|
||||
<path d="M6 24c8-6 12-6 20 0s12 6 20 0M6 40c8-6 12-6 20 0s12 6 20 0" />
|
||||
),
|
||||
rose: (
|
||||
<path d="M32 34a10 10 0 1 0 0-20 10 10 0 0 0 0 20Zm0 0a10 10 0 1 1 0 20 10 10 0 0 1 0-20Zm-14-10a10 10 0 1 1 14 10 10 10 0 0 1-14-10Zm28 0a10 10 0 1 0-14 10 10 10 0 0 0 14-10Z" />
|
||||
),
|
||||
peach: <path d="M8 40c6-20 18-32 24-32s18 12 24 32c-8 8-16 12-24 12s-16-4-24-12Z" />,
|
||||
};
|
||||
|
||||
type ImagePlaceholderProps = {
|
||||
mood: ImageMood;
|
||||
label: string;
|
||||
src?: string;
|
||||
alt?: string;
|
||||
className?: string;
|
||||
shape?: "blob" | "soft";
|
||||
priority?: boolean;
|
||||
};
|
||||
|
||||
export function ImagePlaceholder({
|
||||
mood,
|
||||
label,
|
||||
src,
|
||||
alt,
|
||||
className = "",
|
||||
shape = "blob",
|
||||
priority = false,
|
||||
}: ImagePlaceholderProps) {
|
||||
const radius =
|
||||
shape === "blob"
|
||||
? "rounded-[62%_38%_53%_47%/45%_42%_58%_55%]"
|
||||
: "rounded-[2.5rem]";
|
||||
|
||||
if (src) {
|
||||
return (
|
||||
<div
|
||||
className={`relative overflow-hidden ${radius} ${className}`}
|
||||
aria-hidden={alt ? undefined : true}
|
||||
>
|
||||
<Image
|
||||
src={src}
|
||||
alt={alt ?? ""}
|
||||
fill
|
||||
priority={priority}
|
||||
className="object-cover"
|
||||
sizes="(min-width: 1024px) 50vw, 100vw"
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<div
|
||||
className={`relative overflow-hidden ${radius} ${className}`}
|
||||
style={{ background: moodGradients[mood] }}
|
||||
role="img"
|
||||
aria-label={label}
|
||||
>
|
||||
<svg
|
||||
className="absolute inset-0 h-full w-full opacity-[0.14] mix-blend-overlay"
|
||||
aria-hidden="true"
|
||||
>
|
||||
<filter id={`grain-${mood}`}>
|
||||
<feTurbulence type="fractalNoise" baseFrequency="0.85" numOctaves="2" stitchTiles="stitch" />
|
||||
</filter>
|
||||
<rect width="100%" height="100%" filter={`url(#grain-${mood})`} />
|
||||
</svg>
|
||||
<svg
|
||||
viewBox="0 0 64 64"
|
||||
className="absolute left-1/2 top-1/2 h-16 w-16 -translate-x-1/2 -translate-y-1/2 text-white/40 sm:h-20 sm:w-20"
|
||||
fill="none"
|
||||
stroke="currentColor"
|
||||
strokeWidth="1.25"
|
||||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
aria-hidden="true"
|
||||
>
|
||||
{moodIcons[mood]}
|
||||
</svg>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user