Decouple admin from public max-w-7xl via (site) route group.
Local Build and Deploy / deploy (push) Successful in 54s

Public chrome and width live only under (site). /admin and /client inherit the root layout alone, so housekeeping width is no longer tied to the public template.

Co-authored-by: Cursor <[email protected]>
This commit is contained in:
SimoandCursor committed 2026-07-15 21:12:52 +02:00
1 parent a1dc7ff0a7
commit 367f69a439
60 files changed
+29 -30

No files matched your search

+165
View File
@@ -0,0 +1,165 @@
import Link from "next/link";
import { notFound } from "next/navigation";
import { getTranslations } from "next-intl/server";
import { ContentCard } from "@/components/public/ui";
import { prisma } from "@/lib/prisma";
import { sanitize } from "@/lib/sanitize";
import { siteSettings } from "@/lib/services/site-settings";
export const revalidate = 300;
// Asset path matches AtomCMS's Blade view: asset('/assets/images/help-center/<image_url>').
const HELP_IMAGE_BASE = "/assets/images/help-center";
type HelpCategory = {
id: bigint;
name: string;
content: string;
position: number;
imageUrl: string | null;
buttonText: string | null;
buttonUrl: string | null;
buttonColor: string;
buttonBorderColor: string;
smallBox: boolean;
};
type HelpCategoryLink = {
id: bigint;
name: string;
position: number;
};
// Faithful to AtomCMS: name / content / button_text carry a `:hotel` placeholder
// replaced with the configured hotel name before display.
function withHotel(text: string | null | undefined, hotelName: string): string {
return (text ?? "").split(":hotel").join(hotelName);
}
export default async function HelpCategoryPage({
params,
}: {
params: Promise<{ category: string }>;
}) {
const t = await getTranslations("pages.helpCategory");
const { category } = await params;
let categoryId: bigint;
try {
categoryId = BigInt(category);
} catch {
notFound();
}
let cat: HelpCategory | null = null;
try {
cat = await prisma.websiteHelpCenterCategories.findUnique({
where: { id: categoryId },
select: {
id: true,
name: true,
content: true,
position: true,
imageUrl: true,
buttonText: true,
buttonUrl: true,
buttonColor: true,
buttonBorderColor: true,
smallBox: true,
},
});
} catch {
cat = null;
}
if (!cat) notFound();
const hotelName = (await siteSettings.get("hotel_name", "Atom")) ?? "Atom";
// Sibling help topics, so the reader can jump between categories. Isolated
// query: a DB hiccup degrades this list to empty rather than 500-ing.
let siblings: HelpCategoryLink[] = [];
try {
siblings = await prisma.websiteHelpCenterCategories.findMany({
where: { id: { not: categoryId } },
orderBy: { position: "asc" },
select: { id: true, name: true, position: true },
take: 50,
});
} catch {
siblings = [];
}
const title = withHotel(cat.name, hotelName);
const content = withHotel(cat.content, hotelName);
const buttonText = withHotel(cat.buttonText, hotelName);
const imageSrc = cat.imageUrl ? `${HELP_IMAGE_BASE}/${cat.imageUrl}` : "";
const hasButton = Boolean(cat.buttonText && cat.buttonText.trim() !== "");
return (
<main style={{ display: "grid", gap: "1.5rem" }}>
<p style={{ margin: 0 }}>
<Link href="/help">{t("back")}</Link>
</p>
<ContentCard icon="❓" title={title} subtitle={t("topicSubtitle")}>
{imageSrc ? (
// eslint-disable-next-line @next/next/no-img-element
<img
src={imageSrc}
alt=""
style={{
float: "right",
maxWidth: 160,
height: "auto",
margin: "0 0 0.75rem 1rem",
}}
/>
) : null}
{/* content is author-supplied HTML — sanitised server-side. */}
{/* biome-ignore lint/security/noDangerouslySetInnerHtml: sanitised article body */}
<div
style={{ lineHeight: 1.7 }}
dangerouslySetInnerHTML={{ __html: sanitize(content) }}
/>
{hasButton ? (
<div style={{ clear: "both", marginTop: "1rem" }}>
<a
href={cat.buttonUrl ?? "#"}
className="btn"
style={{
backgroundColor: cat.buttonColor,
border: `2px solid ${cat.buttonBorderColor}`,
}}
>
{buttonText}
</a>
</div>
) : null}
</ContentCard>
{siblings.length > 0 ? (
<ContentCard
icon="🧭"
title={t("moreTitle")}
subtitle={t("moreSubtitle")}
>
<div className="card-grid sm-2 lg-3">
{siblings.map((s) => (
<Link
key={String(s.id)}
href={`/help/${String(s.id)}`}
className="card hover"
style={{ display: "block", color: "inherit" }}
>
<strong>{withHotel(s.name, hotelName)}</strong>
</Link>
))}
</div>
</ContentCard>
) : null}
</main>
);
}