Add atom-theme visual design (header/footer, theme system, restyled pages)
Faithful port of AtomCMS's "atom" theme look (light, Habbo-retro, golden #eeb425 / amber #f59e0b accents, white cards, 12px radii, Nunito-first stack): - globals.css: full token set + components (.site-header/.nav-item, .btn-*, .card, .hero, currency pills, article cards, inputs, tables, footer). - SiteHeader (brand + nav + currency pills + auth box, staff-aware) + SiteFooter; layout wraps header/content/footer. Removed the bare SiteNav. - Restyled home (hero + article cards), login (centered card). - siteSettings now falls back to DEFAULTS on missing key OR DB error, so pages render without a DB; DATABASE_CONNECT_TIMEOUT_MS env + pool acquireTimeout make the no-DB fallback fast. Verified in a real browser (computed styles): header white/sticky, golden logo gradient, uppercase nav, amber primary button @12px radius, golden outline, hero gradient — all correct. tsc exit 0, vitest 48/48, next build exit 0.
This commit is contained in:
1 parent
5f8b465451
commit
9a79acebe7
10 files changed
+490
-109
No files matched your search
@@ -0,0 +1,18 @@
|
||||
import { siteSettings } from "@/lib/services/site-settings";
|
||||
|
||||
export async function SiteFooter() {
|
||||
const hotelName = await siteSettings.get("hotel_name", "Atom");
|
||||
const year = new Date().getFullYear();
|
||||
|
||||
return (
|
||||
<footer className="site-footer">
|
||||
<div className="inner">
|
||||
<span>
|
||||
© {year} {hotelName}
|
||||
</span>
|
||||
<span className="spacer" />
|
||||
<span className="muted">AtomCMS → Next.js conversion</span>
|
||||
</div>
|
||||
</footer>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,88 @@
|
||||
import Link from "next/link";
|
||||
import { auth } from "@/lib/auth";
|
||||
import { prisma } from "@/lib/prisma";
|
||||
import { siteSettings } from "@/lib/services/site-settings";
|
||||
|
||||
export async function SiteHeader() {
|
||||
const [hotelName, session] = await Promise.all([
|
||||
siteSettings.get("hotel_name", "Atom"),
|
||||
auth(),
|
||||
]);
|
||||
|
||||
let credits = 0;
|
||||
let duckets = 0;
|
||||
let diamonds = 0;
|
||||
if (session?.user?.id) {
|
||||
try {
|
||||
const id = Number(session.user.id);
|
||||
const [user, currencies] = await Promise.all([
|
||||
prisma.user.findUnique({ where: { id }, select: { credits: true } }),
|
||||
prisma.usersCurrency.findMany({ where: { userId: id } }),
|
||||
]);
|
||||
credits = user?.credits ?? 0;
|
||||
for (const c of currencies) {
|
||||
if (c.type === 0) duckets = c.amount;
|
||||
if (c.type === 5) diamonds = c.amount;
|
||||
}
|
||||
} catch {
|
||||
// DB unavailable — render the header without live balances.
|
||||
}
|
||||
}
|
||||
|
||||
const isStaff = (session?.user?.rank ?? 0) >= 7;
|
||||
|
||||
return (
|
||||
<header className="site-header">
|
||||
<div className="bar">
|
||||
<Link href="/" className="brand">
|
||||
<span className="dot" />
|
||||
{hotelName}
|
||||
</Link>
|
||||
<nav className="nav">
|
||||
<Link className="nav-item" href="/">
|
||||
Home
|
||||
</Link>
|
||||
<Link className="nav-item" href="/news">
|
||||
News
|
||||
</Link>
|
||||
{session?.user ? (
|
||||
<Link className="nav-item" href="/client">
|
||||
Hotel
|
||||
</Link>
|
||||
) : null}
|
||||
{isStaff ? (
|
||||
<Link className="nav-item" href="/admin">
|
||||
Admin
|
||||
</Link>
|
||||
) : null}
|
||||
</nav>
|
||||
<span className="spacer" />
|
||||
{session?.user ? (
|
||||
<>
|
||||
<div className="currencies">
|
||||
<span className="currency">
|
||||
<span className="coin credits">cr</span>
|
||||
{credits}
|
||||
</span>
|
||||
<span className="currency">
|
||||
<span className="coin duckets">d</span>
|
||||
{duckets}
|
||||
</span>
|
||||
<span className="currency">
|
||||
<span className="coin diamonds">◆</span>
|
||||
{diamonds}
|
||||
</span>
|
||||
</div>
|
||||
<Link className="nav-item" href={`/u/${session.user.name}`}>
|
||||
{session.user.name}
|
||||
</Link>
|
||||
</>
|
||||
) : (
|
||||
<Link className="btn btn-outline" href="/login">
|
||||
Login
|
||||
</Link>
|
||||
)}
|
||||
</div>
|
||||
</header>
|
||||
);
|
||||
}
|
||||
@@ -1,30 +0,0 @@
|
||||
import Link from "next/link";
|
||||
import { auth } from "@/lib/auth";
|
||||
import { siteSettings } from "@/lib/services/site-settings";
|
||||
|
||||
export async function SiteNav() {
|
||||
const [hotelName, session] = await Promise.all([
|
||||
siteSettings.get("hotel_name", "Atom"),
|
||||
auth(),
|
||||
]);
|
||||
|
||||
return (
|
||||
<nav className="site-nav">
|
||||
<div className="container">
|
||||
<Link href="/" className="brand">
|
||||
{hotelName ?? "Atom"}
|
||||
</Link>
|
||||
<Link href="/news">News</Link>
|
||||
<span className="spacer" />
|
||||
{session?.user ? (
|
||||
<>
|
||||
<Link href={`/u/${session.user.name}`}>{session.user.name}</Link>
|
||||
<Link href="/client">Hotel</Link>
|
||||
</>
|
||||
) : (
|
||||
<Link href="/login">Login</Link>
|
||||
)}
|
||||
</div>
|
||||
</nav>
|
||||
);
|
||||
}
|
||||
Reference in new issue
Block a user