import type { MetadataRoute } from "next"; import { getBranding } from "@/lib/business/branding"; /** * Next.js's manifest file convention — auto-served at /manifest.webmanifest * with the tag injected automatically. Dynamic as of * Phase 8: name/description/colors reflect the installed business (see * src/lib/business/branding.ts) rather than the internal product name. * * `force-dynamic` is required, not optional — without it, Next has no * signal that this route depends on request-time data (a plain DB read * doesn't count as one of Next's dynamic-usage triggers) and statically * renders it once at build time. Since the Docker image is built before * the installation wizard ever runs, that bakes in the pre-install * neutral fallback ("Book an Appointment"/"Booking") permanently — the * real business name/icons would never appear no matter how the Business * row changes afterward. * * Each of the four manifest icons falls back independently to the app's * own pre-generated, correctly-sized/maskable PWA icon when a business * hasn't uploaded its own — a business can set some without all four (see * the "App Icon & Home Screen" super-admin settings screen). This is * fallback rule #2 ("business name + a neutral generated icon") applied * per-field, rather than risking a broken/non-square install icon. */ export const dynamic = "force-dynamic"; export default async function manifest(): Promise { const branding = await getBranding(); return { name: branding.name, short_name: branding.shortName, description: branding.description || "Book your next appointment in seconds.", start_url: "/home", scope: "/", display: "standalone", background_color: branding.backgroundColor, theme_color: branding.themeColor, orientation: "portrait-primary", icons: [ { src: branding.icon192Url || "/icons/icon-192.png", sizes: "192x192", type: "image/png", purpose: "any", }, { src: branding.icon512Url || "/icons/icon-512.png", sizes: "512x512", type: "image/png", purpose: "any", }, { src: branding.maskableIcon192Url || "/icons/icon-maskable-192.png", sizes: "192x192", type: "image/png", purpose: "maskable", }, { src: branding.maskableIcon512Url || "/icons/icon-maskable-512.png", sizes: "512x512", type: "image/png", purpose: "maskable", }, ], }; }