Extends the branding service with a hiddenNavHrefs: string[] field so forks can hide sidebar entries (e.g. /salary, /customers) without patching DashboardNav.tsx. Configurable via NEXT_PUBLIC_BRANDING_HIDDEN_NAV as a comma-separated list. Default is [] — vanilla gnubok unchanged. Routes remain reachable; this is a nav-visibility switch only. Settings sidebar is intentionally out of scope. Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
101 lines
3.5 KiB
TypeScript
101 lines
3.5 KiB
TypeScript
/**
|
|
* Branding Service
|
|
*
|
|
* Provides whitelabel-friendly branding values (app name, support emails,
|
|
* asset paths, theme colors) with three-tier resolution:
|
|
*
|
|
* defaults < env vars < extension override
|
|
*
|
|
* If nothing is set, gnubok defaults are returned — production behaviour
|
|
* is unchanged. A whitelabel sets env vars (NEXT_PUBLIC_BRANDING_* for
|
|
* client-readable, BRANDING_* for server-only) or registers a branding
|
|
* extension via registerBrandingService().
|
|
*
|
|
* See WHITELABEL.md for the full env var reference and fork checklist.
|
|
*/
|
|
|
|
export interface BrandingConfig {
|
|
// Identity
|
|
appName: string
|
|
appDescription: string
|
|
legalEntity: string
|
|
|
|
// Contact
|
|
supportEmail: string
|
|
privacyEmail: string
|
|
securityEmail: string
|
|
|
|
// URLs
|
|
appUrl: string
|
|
|
|
// Asset paths
|
|
logoPath: string
|
|
faviconPath: string
|
|
appleTouchIconPath: string
|
|
pwaIconBasePath: string
|
|
|
|
// Colors
|
|
themeColor: string
|
|
manifestThemeColor: string
|
|
manifestBackgroundColor: string
|
|
|
|
// Navigation
|
|
hiddenNavHrefs: string[]
|
|
}
|
|
|
|
const DEFAULT_BRANDING: BrandingConfig = {
|
|
appName: 'Gnubok',
|
|
appDescription: 'Ekonomihantering',
|
|
legalEntity: 'Arcim',
|
|
supportEmail: 'support@gnubok.se',
|
|
privacyEmail: 'privacy@gnubok.se',
|
|
securityEmail: 'security@arcim.io',
|
|
appUrl: process.env.NEXT_PUBLIC_APP_URL || 'https://app.gnubok.se',
|
|
logoPath: '/gnubokiceon-removebg-preview.png',
|
|
faviconPath: '/favicon.ico',
|
|
appleTouchIconPath: '/icons/icon-192.png',
|
|
pwaIconBasePath: '/icons',
|
|
themeColor: '#304D83',
|
|
manifestThemeColor: '#1a1a1a',
|
|
manifestBackgroundColor: '#ffffff',
|
|
hiddenNavHrefs: [],
|
|
}
|
|
|
|
let _override: Partial<BrandingConfig> = {}
|
|
|
|
export function registerBrandingService(partial: Partial<BrandingConfig>): void {
|
|
_override = { ...partial }
|
|
}
|
|
|
|
export function getBranding(): BrandingConfig {
|
|
return {
|
|
...DEFAULT_BRANDING,
|
|
...readEnvOverrides(),
|
|
..._override,
|
|
}
|
|
}
|
|
|
|
function readEnvOverrides(): Partial<BrandingConfig> {
|
|
const env = process.env
|
|
const o: Partial<BrandingConfig> = {}
|
|
if (env.NEXT_PUBLIC_BRANDING_APP_NAME) o.appName = env.NEXT_PUBLIC_BRANDING_APP_NAME
|
|
if (env.NEXT_PUBLIC_BRANDING_APP_DESCRIPTION) o.appDescription = env.NEXT_PUBLIC_BRANDING_APP_DESCRIPTION
|
|
if (env.BRANDING_LEGAL_ENTITY) o.legalEntity = env.BRANDING_LEGAL_ENTITY
|
|
if (env.BRANDING_SUPPORT_EMAIL) o.supportEmail = env.BRANDING_SUPPORT_EMAIL
|
|
if (env.BRANDING_PRIVACY_EMAIL) o.privacyEmail = env.BRANDING_PRIVACY_EMAIL
|
|
if (env.BRANDING_SECURITY_EMAIL) o.securityEmail = env.BRANDING_SECURITY_EMAIL
|
|
if (env.NEXT_PUBLIC_APP_URL) o.appUrl = env.NEXT_PUBLIC_APP_URL
|
|
if (env.NEXT_PUBLIC_BRANDING_LOGO_PATH) o.logoPath = env.NEXT_PUBLIC_BRANDING_LOGO_PATH
|
|
if (env.NEXT_PUBLIC_BRANDING_FAVICON_PATH) o.faviconPath = env.NEXT_PUBLIC_BRANDING_FAVICON_PATH
|
|
if (env.NEXT_PUBLIC_BRANDING_APPLE_ICON_PATH) o.appleTouchIconPath = env.NEXT_PUBLIC_BRANDING_APPLE_ICON_PATH
|
|
if (env.NEXT_PUBLIC_BRANDING_PWA_ICON_BASE) o.pwaIconBasePath = env.NEXT_PUBLIC_BRANDING_PWA_ICON_BASE
|
|
if (env.NEXT_PUBLIC_BRANDING_THEME_COLOR) o.themeColor = env.NEXT_PUBLIC_BRANDING_THEME_COLOR
|
|
if (env.NEXT_PUBLIC_BRANDING_MANIFEST_THEME_COLOR) o.manifestThemeColor = env.NEXT_PUBLIC_BRANDING_MANIFEST_THEME_COLOR
|
|
if (env.NEXT_PUBLIC_BRANDING_MANIFEST_BG_COLOR) o.manifestBackgroundColor = env.NEXT_PUBLIC_BRANDING_MANIFEST_BG_COLOR
|
|
if (env.NEXT_PUBLIC_BRANDING_HIDDEN_NAV) {
|
|
const hrefs = env.NEXT_PUBLIC_BRANDING_HIDDEN_NAV.split(',').map(s => s.trim()).filter(Boolean)
|
|
if (hrefs.length > 0) o.hiddenNavHrefs = hrefs
|
|
}
|
|
return o
|
|
}
|