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
+2
-1
@@ -13,7 +13,8 @@ function createPrismaClient(): PrismaClient {
|
||||
password: decodeURIComponent(url.password),
|
||||
database: url.pathname.replace(/^\//, ""),
|
||||
connectionLimit: env.DATABASE_POOL_SIZE,
|
||||
connectTimeout: 10_000,
|
||||
connectTimeout: env.DATABASE_CONNECT_TIMEOUT_MS,
|
||||
acquireTimeout: env.DATABASE_CONNECT_TIMEOUT_MS,
|
||||
idleTimeout: env.DATABASE_IDLE_TIMEOUT_MS,
|
||||
});
|
||||
return new PrismaClient({
|
||||
|
||||
@@ -1,33 +1,50 @@
|
||||
import { prisma } from "@/lib/prisma";
|
||||
|
||||
// Fallback values used when a setting row is missing OR the DB is unreachable,
|
||||
// so the app still renders (e.g. local dev without a DATABASE_URL).
|
||||
const DEFAULTS: Record<string, string> = {
|
||||
hotel_name: "Atom",
|
||||
habbo_imaging_url: "https://www.habbo.com/habbo-imaging/avatarimage",
|
||||
logo_url: "",
|
||||
nitro_client_url: "",
|
||||
};
|
||||
|
||||
/**
|
||||
* DB-driven CMS config, mirroring AtomCMS's `setting()` / habbo-next's
|
||||
* `siteSettings`. Loads the whole website_settings table into a key→value map,
|
||||
* cached in-process. Booleans are stored as the strings '1' / '0'.
|
||||
* cached in-process, with graceful fallback to DEFAULTS. Booleans are stored as
|
||||
* the strings '1' / '0'.
|
||||
*/
|
||||
class SiteSettings {
|
||||
private cache: Map<string, string> | null = null;
|
||||
|
||||
private async load(): Promise<Map<string, string>> {
|
||||
if (this.cache === null) {
|
||||
const rows = await prisma.websiteSetting.findMany({
|
||||
select: { key: true, value: true },
|
||||
});
|
||||
this.cache = new Map(rows.map((r) => [r.key, r.value]));
|
||||
try {
|
||||
const rows = await prisma.websiteSetting.findMany({
|
||||
select: { key: true, value: true },
|
||||
});
|
||||
this.cache = new Map(rows.map((r) => [r.key, r.value]));
|
||||
} catch {
|
||||
// DB unavailable — serve defaults without caching so we retry later.
|
||||
return new Map(Object.entries(DEFAULTS));
|
||||
}
|
||||
}
|
||||
return this.cache;
|
||||
}
|
||||
|
||||
async get(key: string, fallback: string | null = null): Promise<string | null> {
|
||||
const map = await this.load();
|
||||
return map.has(key) ? (map.get(key) as string) : fallback;
|
||||
if (map.has(key)) return map.get(key) as string;
|
||||
if (key in DEFAULTS) return DEFAULTS[key] as string;
|
||||
return fallback;
|
||||
}
|
||||
|
||||
async getBool(key: string, fallback = false): Promise<boolean> {
|
||||
const map = await this.load();
|
||||
if (!map.has(key)) return fallback;
|
||||
const v = (map.get(key) as string).toLowerCase();
|
||||
return v === "1" || v === "true";
|
||||
const v = await this.get(key, null);
|
||||
if (v === null) return fallback;
|
||||
const s = v.toLowerCase();
|
||||
return s === "1" || s === "true";
|
||||
}
|
||||
|
||||
/** Invalidate the in-process cache after a settings write. */
|
||||
|
||||
Reference in new issue
Block a user