Files
dezky/apps/website/composables/useLanding.ts
T
Ronni Baslund 554cb99f2c feat(website): Umami analytics (conversion + CTA events)
Add the Umami tracker (cookieless, no consent banner) in the document head,
limited to the production hostnames via data-domains so dev traffic doesn't
pollute the stats. Pageviews are auto-tracked per page and locale.

Custom events on the key funnel:
- demo-request (demo form submit, with teamSize)
- partner-application (partner form submit, with type)
- book-demo (every "Book a demo" CTA click) via data-umami-event
- login (clicks through to the app)

Also fix the mobile nav menu links, which weren't localized (would drop
Danish visitors back to English).
2026-06-06 21:14:42 +02:00

66 lines
2.6 KiB
TypeScript

import { computed } from 'vue'
import { COPY, type Lang } from '~/utils/landingCopy'
import { makeTheme } from '~/utils/landingTokens'
// Locale is owned by @nuxtjs/i18n (URL-based: English at /, Danish at /da, with
// cookie-remembered browser detection). useLang exposes it as the 'da' | 'en'
// the COPY object expects; useCopy maps to the matching translations. `dark` is
// unused machinery from the design's Tweaks panel (the site is light-only).
export const useLang = () => {
const { locale } = useI18n()
return computed<Lang>(() => (locale.value === 'da' ? 'da' : 'en'))
}
export const useDark = () => useState<boolean>('dz-dark', () => false)
export const useTheme = () => {
const dark = useDark()
return computed(() => makeTheme(dark.value))
}
export const useCopy = () => {
const { locale } = useI18n()
return computed(() => COPY[locale.value === 'da' ? 'da' : 'en'])
}
// Setup-only. Returns a click handler that switches to the other locale's
// localized route (flips the URL, e.g. / <-> /da).
export function useLangToggle() {
const { locale } = useI18n()
const switchLocalePath = useSwitchLocalePath()
return () => navigateTo(switchLocalePath(locale.value === 'da' ? 'en' : 'da'))
}
// Setup-only. Localizes an internal href, preserving any #hash. Page links get
// the locale prefix (/about -> /da/about in Danish); section anchors resolve
// against the current locale's home (/#suite -> /da#suite). Bare #hash returns
// unchanged (same-page anchor).
export function useLocalizeHref() {
const localePath = useLocalePath()
return (href: string) => {
if (href.startsWith('#')) return href
const i = href.indexOf('#')
const path = i === -1 ? href : href.slice(0, i)
const hash = i === -1 ? '' : href.slice(i)
return localePath(path || '/') + hash
}
}
// Fire a custom Umami event. No-op on the server and when the tracker isn't
// active (e.g. localhost dev, where data-domains limits it to dezky.eu).
export function track(event: string, data?: Record<string, unknown>) {
if (!import.meta.client) return
const u = (window as unknown as { umami?: { track: (e: string, d?: unknown) => void } }).umami
u?.track(event, data)
}
// Smooth-scroll to an in-page anchor, accounting for the sticky 72px nav.
// Non-anchor / placeholder links (#) are ignored.
export function scrollToAnchor(hash: string) {
if (!hash || hash === '#' || !hash.startsWith('#')) return
const el = document.getElementById(hash.slice(1))
if (!el) return
const top = el.getBoundingClientRect().top + window.scrollY - 72
window.scrollTo({ top, behavior: 'smooth' })
history.replaceState(null, '', hash)
}