diff --git a/apps/website/middleware/coming-soon.global.ts b/apps/website/middleware/coming-soon.global.ts new file mode 100644 index 0000000..7ffb03a --- /dev/null +++ b/apps/website/middleware/coming-soon.global.ts @@ -0,0 +1,25 @@ +// Holding-page gate. While NUXT_PUBLIC_COMING_SOON=true, every route is +// redirected to the locale-appropriate /coming-soon page so the unfinished +// site stays hidden. Flip the env to false (in Coolify) to launch — no code +// change. The team can preview the real site via ?preview=, which sets +// a short-lived cookie so subsequent navigation stays unlocked. +export default defineNuxtRouteMiddleware((to) => { + const config = useRuntimeConfig() + if (!config.public.comingSoon) return + + // Never redirect the holding page itself (avoids a loop). + if (to.path === '/coming-soon' || to.path === '/da/coming-soon') return + + const token = config.public.previewToken as string + const bypass = useCookie('dz_preview', { + maxAge: 60 * 60 * 24 * 7, sameSite: 'lax', path: '/', + }) + if (token && to.query.preview === token) { + bypass.value = '1' + return + } + if (bypass.value === '1') return + + const isDa = to.path === '/da' || to.path.startsWith('/da/') + return navigateTo(isDa ? '/da/coming-soon' : '/coming-soon') +}) diff --git a/apps/website/nuxt.config.ts b/apps/website/nuxt.config.ts index 87769ab..2472033 100644 --- a/apps/website/nuxt.config.ts +++ b/apps/website/nuxt.config.ts @@ -76,6 +76,12 @@ export default defineNuxtConfig({ runtimeConfig: { public: { siteUrl, + // Holding-page gate. Set NUXT_PUBLIC_COMING_SOON=true (Coolify env) to + // show the coming-soon page for every route; flip to false to launch. + // Preview the real site behind the gate with ?preview= + // (override the token via NUXT_PUBLIC_PREVIEW_TOKEN). + comingSoon: false, + previewToken: 'dezky-preview', }, }, diff --git a/apps/website/pages/coming-soon.vue b/apps/website/pages/coming-soon.vue new file mode 100644 index 0000000..58a3df6 --- /dev/null +++ b/apps/website/pages/coming-soon.vue @@ -0,0 +1,79 @@ + + + diff --git a/apps/website/utils/landingCopy.ts b/apps/website/utils/landingCopy.ts index 38e28e0..76a6dc4 100644 --- a/apps/website/utils/landingCopy.ts +++ b/apps/website/utils/landingCopy.ts @@ -6,6 +6,14 @@ export type HeadlinePart = string | { hl: string } export const COPY = { da: { + waitlist: { + eyebrow: 'snart klar', + heading: 'Vi er der næsten.', + body: 'dezky lægger sidste hånd på den suveræne arbejdsplads — mail, filer, video, chat og login, hostet i EU. Skriv dig op, så siger vi til, så snart vi går live.', + emailPh: 'din@e-mail.dk', + submit: 'Hold mig opdateret', + note: 'Ingen spam — kun én mail, når vi lancerer.', + }, nav: { product: 'produkt', security: 'sikkerhed', whitelabel: 'whitelabel', pricing: 'priser', docs: 'docs', login: 'log ind', cta: 'book en demo' }, hero: { eyebrow: '// suveræn produktivitet · v1.0', @@ -508,6 +516,14 @@ export const COPY = { }, }, en: { + waitlist: { + eyebrow: 'coming soon', + heading: 'We\'re almost there.', + body: 'dezky is putting the finishing touches on its sovereign workplace — mail, files, video, chat and sign-in, hosted in the EU. Leave your email and we\'ll let you know the moment we go live.', + emailPh: 'you@company.com', + submit: 'Notify me', + note: 'No spam — just one email when we launch.', + }, nav: { product: 'product', security: 'security', whitelabel: 'whitelabel', pricing: 'pricing', docs: 'docs', login: 'log in', cta: 'book a demo' }, hero: { eyebrow: '// sovereign productivity · v1.0',