"use client"; /** * v1.17.1 — shared "Setup guide" doc-link for every Settings → Integrations * card. Each card paints the same affordance: one discreet, external link to * the provider's setup runbook ("was eingeben, wo klicken"). Single-sourced * here so all six cards (WHOOP / Withings / Fitbit / Polar / Oura / Nightscout) * read as one family and the docs host lives in exactly one place. * * The runbooks themselves are authored separately; this link wires the * destination now so a user who is mid-setup always knows where to go. */ import { ExternalLink } from "lucide-react"; import { useTranslations } from "@/lib/i18n/context"; /** * Base for every provider setup runbook. The provider key is appended as a * path segment, e.g. `https://docs.healthlog.dev/integrations/whoop`. Kept as * a single constant so the host never drifts across cards. */ export const INTEGRATION_DOCS_BASE = "https://docs.healthlog.dev/integrations"; export type IntegrationDocsProvider = | "whoop" | "withings" | "fitbit" | "polar" | "oura" | "nightscout" // v1.27.0 — Google Health runbook (docs.healthlog.dev/integrations/google-health). | "google-health" // v1.28.x — Strava runbook (docs.healthlog.dev/integrations/strava). | "strava"; export function integrationDocsHref(provider: IntegrationDocsProvider): string { return `${INTEGRATION_DOCS_BASE}/${provider}`; } /** * The OAuth callback path every BYO-key provider registers against. Every * provider's `getRedirectUri()` (`src/lib/{provider}/client.ts`) derives the * same shape from `NEXT_PUBLIC_APP_URL` unless an operator overrides it with * an explicit `*_REDIRECT_URI` env var — the common case this card targets. */ export function integrationCallbackUrl( provider: IntegrationDocsProvider, ): string { const base = process.env.NEXT_PUBLIC_APP_URL ?? ""; return `${base}/api/${provider}/callback`; } /** * v1.29.x (UX audit H2) — compact 3-step BYO-developer-app guide for the * six providers that require a self-hoster to register their own vendor * app before an OAuth connect can ever succeed (Withings / WHOOP / Fitbit / * Polar / Oura / Strava). The external "Setup guide" link explains the * *why*; this inline callout gives the one value every vendor form asks * for and the app already knows — the callback URL — so it doesn't have to * be reconstructed by hand from the docs. Shown only while the user hasn't * saved their own credentials yet; it steps out of the way once configured. */ export function IntegrationRedirectGuide({ provider, providerLabel, }: { provider: IntegrationDocsProvider; providerLabel: string; }) { const { t } = useTranslations(); const callbackUrl = integrationCallbackUrl(provider); return (

{t("settings.integrationRedirectGuide.intro", { provider: providerLabel, })}

  1. {t("settings.integrationRedirectGuide.step1", { provider: providerLabel, })}
  2. {t("settings.integrationRedirectGuide.step2")}{" "} {callbackUrl}
  3. {t("settings.integrationRedirectGuide.step3")}
); } /** * The one description every integration card paints: ONE sentence, then the * setup-guide link inline at the end of it. * * It used to be two paragraphs stacked in the description slot — a white * primary sentence and a grey secondary one whose only job was to carry the * link. The secondary sentence was byte-identical across six of the eight * cards, which is the tell: it said nothing about the provider it sat under. * The link is the part that carried information, so the link is what stayed. */ export function IntegrationCardDescription({ i18nPrefix, provider, }: { i18nPrefix: string; provider: IntegrationDocsProvider; }) { const { t } = useTranslations(); return ( <> {t(`${i18nPrefix}Description`)}{" "} {t("settings.integrationSetupGuide")} ); }