feat: full site build — Project/Melody schema (Option A), admin CRUD, public sections, uploads, email+SMTP, internal analytics, legal pages, docs

This commit is contained in:
2026-08-05 20:53:40 +02:00
parent d87f3033c6
commit c26f41511f
122 changed files with 15068 additions and 41 deletions
+20
View File
@@ -0,0 +1,20 @@
import Image from "next/image";
import Link from "next/link";
import type { Locale } from "@/lib/i18n";
import { getMelodyDirectoryCopy, getMelodyPath } from "@/lib/melody-content";
import AudioPlayer from "@/components/public/audio-player";
export default function MelodyDetail({ locale, melody }: { locale: Locale; melody: { id: string; slug: string; titleAr: string; titleEn: string; descAr: string | null; descEn: string | null; audioFile: string; coverImage: string | null; isDownloadable: boolean; category: { nameAr: string; nameEn: string } } }) {
const title = locale === "ar" ? melody.titleAr : melody.titleEn;
const description = locale === "ar" ? melody.descAr : melody.descEn;
const category = locale === "ar" ? melody.category.nameAr : melody.category.nameEn;
const copy = getMelodyDirectoryCopy(locale);
return (
<article className="mx-auto w-full max-w-5xl space-y-10 px-4 py-10 sm:px-6 lg:px-8">
<Link href={getMelodyPath(locale)} className="text-sm font-medium text-brand-2 hover:underline"> {copy.back}</Link>
<header className="max-w-3xl space-y-4"><p className="text-sm font-semibold uppercase tracking-[0.18em] text-brand-2">{category}</p><h1 className="text-4xl font-semibold tracking-tight sm:text-6xl">{title}</h1>{description ? <p className="text-xl leading-8 text-muted-foreground">{description}</p> : null}</header>
{melody.coverImage ? <div className="relative aspect-[16/7] overflow-hidden rounded-2xl border border-border bg-muted"><Image src={melody.coverImage} alt={title} fill unoptimized sizes="100vw" className="object-cover" priority /></div> : null}
<div className="max-w-2xl"><AudioPlayer src={melody.audioFile} title={title} locale={locale} downloadable={melody.isDownloadable} analytics={{ entityId: melody.id }} /></div>
</article>
);
}