feat(settings): manage public header banner in admin
This commit is contained in:
@@ -57,9 +57,11 @@ export {
|
||||
registerPostCrudAuditHook,
|
||||
updatePost,
|
||||
} from "./posts"
|
||||
export type { PublicHeaderBanner } from "./settings"
|
||||
export type { PublicHeaderBanner, PublicHeaderBannerConfig } from "./settings"
|
||||
export {
|
||||
getPublicHeaderBanner,
|
||||
getPublicHeaderBannerConfig,
|
||||
isAdminSelfRegistrationEnabled,
|
||||
setAdminSelfRegistrationEnabled,
|
||||
setPublicHeaderBannerConfig,
|
||||
} from "./settings"
|
||||
|
||||
92
packages/db/src/settings.test.ts
Normal file
92
packages/db/src/settings.test.ts
Normal file
@@ -0,0 +1,92 @@
|
||||
import { beforeEach, describe, expect, it, vi } from "vitest"
|
||||
|
||||
const { mockDb } = vi.hoisted(() => ({
|
||||
mockDb: {
|
||||
systemSetting: {
|
||||
findUnique: vi.fn(),
|
||||
upsert: vi.fn(),
|
||||
},
|
||||
},
|
||||
}))
|
||||
|
||||
vi.mock("./client", () => ({
|
||||
db: mockDb,
|
||||
}))
|
||||
|
||||
import {
|
||||
getPublicHeaderBanner,
|
||||
getPublicHeaderBannerConfig,
|
||||
isAdminSelfRegistrationEnabled,
|
||||
setPublicHeaderBannerConfig,
|
||||
} from "./settings"
|
||||
|
||||
describe("settings service", () => {
|
||||
const previousEnv = process.env.CMS_ADMIN_SELF_REGISTRATION_ENABLED
|
||||
|
||||
beforeEach(() => {
|
||||
mockDb.systemSetting.findUnique.mockReset()
|
||||
mockDb.systemSetting.upsert.mockReset()
|
||||
process.env.CMS_ADMIN_SELF_REGISTRATION_ENABLED = previousEnv
|
||||
})
|
||||
|
||||
it("falls back to env flag when registration setting is missing", async () => {
|
||||
process.env.CMS_ADMIN_SELF_REGISTRATION_ENABLED = "true"
|
||||
mockDb.systemSetting.findUnique.mockResolvedValue(null)
|
||||
|
||||
const enabled = await isAdminSelfRegistrationEnabled()
|
||||
|
||||
expect(enabled).toBe(true)
|
||||
})
|
||||
|
||||
it("reads active public header banner payload", async () => {
|
||||
mockDb.systemSetting.findUnique.mockResolvedValue({
|
||||
value: JSON.stringify({
|
||||
enabled: true,
|
||||
message: "Commissions open",
|
||||
ctaLabel: "Book now",
|
||||
ctaHref: "/contact",
|
||||
}),
|
||||
})
|
||||
|
||||
const banner = await getPublicHeaderBanner()
|
||||
|
||||
expect(banner).toEqual({
|
||||
message: "Commissions open",
|
||||
ctaLabel: "Book now",
|
||||
ctaHref: "/contact",
|
||||
})
|
||||
})
|
||||
|
||||
it("returns a disabled default config for invalid data", async () => {
|
||||
mockDb.systemSetting.findUnique.mockResolvedValue({
|
||||
value: "not-json",
|
||||
})
|
||||
|
||||
const config = await getPublicHeaderBannerConfig()
|
||||
|
||||
expect(config).toEqual({
|
||||
enabled: false,
|
||||
message: "",
|
||||
ctaLabel: null,
|
||||
ctaHref: null,
|
||||
})
|
||||
})
|
||||
|
||||
it("writes banner config to system settings", async () => {
|
||||
mockDb.systemSetting.upsert.mockResolvedValue({})
|
||||
|
||||
await setPublicHeaderBannerConfig({
|
||||
enabled: true,
|
||||
message: "Holiday schedule",
|
||||
ctaLabel: "Details",
|
||||
ctaHref: "/news",
|
||||
})
|
||||
|
||||
expect(mockDb.systemSetting.upsert).toHaveBeenCalledTimes(1)
|
||||
expect(mockDb.systemSetting.upsert.mock.calls[0]?.[0]).toMatchObject({
|
||||
where: {
|
||||
key: "public.header_banner",
|
||||
},
|
||||
})
|
||||
})
|
||||
})
|
||||
@@ -16,6 +16,13 @@ export type PublicHeaderBanner = {
|
||||
ctaHref?: string
|
||||
}
|
||||
|
||||
export type PublicHeaderBannerConfig = {
|
||||
enabled: boolean
|
||||
message: string
|
||||
ctaLabel: string | null
|
||||
ctaHref: string | null
|
||||
}
|
||||
|
||||
function resolveEnvFallback(): boolean {
|
||||
return process.env.CMS_ADMIN_SELF_REGISTRATION_ENABLED === "true"
|
||||
}
|
||||
@@ -114,3 +121,69 @@ export async function getPublicHeaderBanner(): Promise<PublicHeaderBanner | null
|
||||
return null
|
||||
}
|
||||
}
|
||||
|
||||
export async function getPublicHeaderBannerConfig(): Promise<PublicHeaderBannerConfig> {
|
||||
try {
|
||||
const setting = await db.systemSetting.findUnique({
|
||||
where: { key: PUBLIC_HEADER_BANNER_KEY },
|
||||
select: { value: true },
|
||||
})
|
||||
|
||||
if (!setting) {
|
||||
return {
|
||||
enabled: false,
|
||||
message: "",
|
||||
ctaLabel: null,
|
||||
ctaHref: null,
|
||||
}
|
||||
}
|
||||
|
||||
const parsed = parsePublicHeaderBanner(setting.value)
|
||||
|
||||
if (!parsed) {
|
||||
return {
|
||||
enabled: false,
|
||||
message: "",
|
||||
ctaLabel: null,
|
||||
ctaHref: null,
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
enabled: parsed.enabled,
|
||||
message: parsed.message,
|
||||
ctaLabel: parsed.ctaLabel ?? null,
|
||||
ctaHref: parsed.ctaHref ?? null,
|
||||
}
|
||||
} catch {
|
||||
return {
|
||||
enabled: false,
|
||||
message: "",
|
||||
ctaLabel: null,
|
||||
ctaHref: null,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export async function setPublicHeaderBannerConfig(input: PublicHeaderBannerConfig): Promise<void> {
|
||||
await db.systemSetting.upsert({
|
||||
where: { key: PUBLIC_HEADER_BANNER_KEY },
|
||||
create: {
|
||||
key: PUBLIC_HEADER_BANNER_KEY,
|
||||
value: JSON.stringify({
|
||||
enabled: input.enabled,
|
||||
message: input.message,
|
||||
ctaLabel: input.ctaLabel ?? undefined,
|
||||
ctaHref: input.ctaHref ?? undefined,
|
||||
}),
|
||||
},
|
||||
update: {
|
||||
value: JSON.stringify({
|
||||
enabled: input.enabled,
|
||||
message: input.message,
|
||||
ctaLabel: input.ctaLabel ?? undefined,
|
||||
ctaHref: input.ctaHref ?? undefined,
|
||||
}),
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user