= new Set([
"nightscout",
]);
-export function ConnectionsPanel() {
+export function ConnectionsPanel({
+ callbackUrls,
+}: {
+ /**
+ * The per-provider OAuth callback URLs, resolved on the server per request
+ * (`getIntegrationCallbackUrls`) and threaded down from the route. A client
+ * module cannot derive them: `NEXT_PUBLIC_APP_URL` is inlined at build
+ * time and absent from the published image.
+ */
+ callbackUrls: IntegrationCallbackUrls;
+}) {
const { t } = useTranslations();
const { isAuthenticated } = useAuth();
const router = useRouter();
@@ -328,28 +339,46 @@ export function ConnectionsPanel() {
{/* Per-provider anchors so deep-links (onboarding "more sources" chips,
`/settings/integrations#oura`) scroll straight to the right card. */}
-
+
-
+
-
+
-
+
-
+
-
+
diff --git a/src/components/settings/integrations/fitbit-card.tsx b/src/components/settings/integrations/fitbit-card.tsx
index 98e7c4056..2d16a8f7a 100644
--- a/src/components/settings/integrations/fitbit-card.tsx
+++ b/src/components/settings/integrations/fitbit-card.tsx
@@ -68,13 +68,16 @@ import {
CallbackMismatchNotice,
IntegrationCardDescription,
IntegrationRedirectGuide,
- integrationCallbackUrl,
} from "./setup-guide-link";
+import type { IntegrationCallbackUrls } from "@/lib/integrations/callback-urls";
export function FitbitCard({
viewModel,
+ callbackUrl,
}: {
viewModel: IntegrationStatusViewModel | undefined;
+ /** Server-resolved OAuth callback URL; see `getIntegrationCallbackUrls`. */
+ callbackUrl: IntegrationCallbackUrls["fitbit"];
}) {
const { t } = useTranslations();
const describeSyncOutcome = useSyncOutcomeMessage();
@@ -314,6 +317,7 @@ export function FitbitCard({
)}
);
diff --git a/src/components/settings/integrations/google-health-card.tsx b/src/components/settings/integrations/google-health-card.tsx
index 99b127409..86ea06dc3 100644
--- a/src/components/settings/integrations/google-health-card.tsx
+++ b/src/components/settings/integrations/google-health-card.tsx
@@ -83,13 +83,16 @@ import {
import {
CallbackMismatchNotice,
IntegrationCardDescription,
- integrationCallbackUrl,
} from "./setup-guide-link";
+import type { IntegrationCallbackUrls } from "@/lib/integrations/callback-urls";
export function GoogleHealthCard({
viewModel,
+ callbackUrl,
}: {
viewModel: IntegrationStatusViewModel | undefined;
+ /** Server-resolved OAuth callback URL; see `getIntegrationCallbackUrls`. */
+ callbackUrl: IntegrationCallbackUrls["google-health"];
}) {
const { t } = useTranslations();
const describeSyncOutcome = useSyncOutcomeMessage();
@@ -764,7 +767,7 @@ export function GoogleHealthCard({
);
diff --git a/src/components/settings/integrations/oauth-provider-card.tsx b/src/components/settings/integrations/oauth-provider-card.tsx
index 147fcc79a..4b8d2e5b7 100644
--- a/src/components/settings/integrations/oauth-provider-card.tsx
+++ b/src/components/settings/integrations/oauth-provider-card.tsx
@@ -69,8 +69,8 @@ import {
IntegrationCardDescription,
IntegrationRedirectGuide,
type IntegrationDocsProvider,
- integrationCallbackUrl,
} from "./setup-guide-link";
+import type { IntegrationCallbackUrls } from "@/lib/integrations/callback-urls";
export interface OAuthProviderStatus {
connected: boolean;
@@ -123,6 +123,8 @@ export interface OAuthProviderCardProps {
* exist.
*/
viewModel?: OAuthProviderStatus;
+ /** Server-resolved OAuth callback URL; see `getIntegrationCallbackUrls`. */
+ callbackUrl: IntegrationCallbackUrls[OAuthProviderCardProps["provider"]];
}
export function OAuthProviderCard({
@@ -133,6 +135,7 @@ export function OAuthProviderCard({
dataHref,
credentials = false,
viewModel,
+ callbackUrl,
}: OAuthProviderCardProps) {
const { t } = useTranslations();
const describeSyncOutcome = useSyncOutcomeMessage();
@@ -341,6 +344,7 @@ export function OAuthProviderCard({
)}