1- import { Anchor , Badge , Button , Center , Group , Select , Stack , Text } from '@mantine/core'
2- import React , { useState } from 'react'
1+ import { Alert , Badge , Box , Button , Center , Group , Select , Stack , Text } from '@mantine/core'
2+ import React from 'react'
33import { useSettings } from '../../../hooks/useSettings'
44import type { FlagAuthState } from './useFlagAuth'
5+ import { useInspectedPageOverrides } from './useInspectedPageOverrides'
56import { FLAG_SITES } from './oauth'
67
78export function ConnectScreen ( { auth } : { auth : FlagAuthState } ) {
8- const [ advancedOpen , setAdvancedOpen ] = useState ( false )
9-
109 return (
1110 < Center h = "100%" className = "dd-privacy-allow" >
1211 < Stack align = "center" gap = "md" maw = { 460 } px = "md" >
12+ < DisconnectedOverridesNotice />
1313 < Text size = "xl" fw = { 600 } ta = "center" >
1414 Authenticate with Datadog to access your feature flags
1515 </ Text >
16+ { /* Pick the site before signing in: it selects which Datadog OAuth server + FFE API the flow
17+ talks to (see FLAG_SITES), so it must be set before the Sign in button runs that flow. */ }
18+ < Box w = "100%" >
19+ { /* Locked while signing in: the chosen site is baked into the OAuth flow already running, so
20+ switching mid-flow would point the resulting token at a different environment. */ }
21+ < SiteField disabled = { auth . connecting } />
22+ </ Box >
1623 < Button color = "violet" onClick = { auth . connect } loading = { auth . connecting } >
1724 Sign in to Datadog
1825 </ Button >
@@ -21,47 +28,92 @@ export function ConnectScreen({ auth }: { auth: FlagAuthState }) {
2128 { auth . error }
2229 </ Text >
2330 ) }
24-
25- < Anchor component = "button" type = "button" size = "xs" c = "dimmed" onClick = { ( ) => setAdvancedOpen ( ( open ) => ! open ) } >
26- { advancedOpen ? '− Hide advanced' : 'Advanced: site' }
27- </ Anchor >
28- { advancedOpen && (
29- < Stack gap = "sm" style = { { width : '100%' } } >
30- < SiteField />
31- </ Stack >
31+ { /* A revocation that failed leaves the grant live at Datadog while this panel is signed out,
32+ so the notice belongs on this screen — it's the one the user lands on after disconnecting. */ }
33+ { auth . warning && (
34+ < Text c = "orange" size = "xs" ta = "center" >
35+ { auth . warning } You can revoke it from Datadog under Organization Settings → Authorized Applications.
36+ </ Text >
3237 ) }
3338 </ Stack >
3439 </ Center >
3540 )
3641}
3742
43+ /**
44+ * Surfaces overrides already stored on the inspected page while signed out — otherwise this screen is
45+ * all that renders, so an override left from an earlier session keeps affecting the page with nothing
46+ * to explain it. Informational only; everything that mutates overrides lives on the connected tab.
47+ *
48+ * Mounted only while disconnected, so its navigation listeners never run alongside the connected
49+ * tab's own instance of this hook.
50+ */
51+ function DisconnectedOverridesNotice ( ) {
52+ const { status, overrides } = useInspectedPageOverrides ( )
53+ const count = Object . keys ( overrides ) . length
54+
55+ if ( status !== 'ready' || count === 0 ) {
56+ return null
57+ }
58+
59+ return (
60+ // Masked despite the surrounding dd-privacy-allow: only a count renders today, but flag keys are
61+ // customer data, so anything added here should stay out of the extension's own Session Replay.
62+ < Alert
63+ color = "orange"
64+ w = "100%"
65+ data-dd-privacy = "mask"
66+ title = { `${ count } override${ count === 1 ? '' : 's' } active on this page` }
67+ >
68+ < Text size = "xs" >
69+ These are stored in the page and keep applying while you are signed out. Sign in to view and remove them.
70+ </ Text >
71+ </ Alert >
72+ )
73+ }
74+
3875export function ConnectionHeader ( { auth } : { auth : FlagAuthState } ) {
3976 return (
4077 < Stack gap = { 4 } >
41- < Group justify = "space-between" >
42- < Group gap = "xs" >
43- < Badge color = "green" variant = "light" >
44- Connected via OAuth
45- </ Badge >
46- < Text c = "dimmed" size = "xs" >
47- { auth . site }
48- </ Text >
49- </ Group >
50- < Button size = "compact-xs" variant = "subtle" color = "gray" onClick = { auth . disconnect } >
78+ { /* The badge opts out of Mantine's default uppercasing: "datad0g.com" and "datadoghq.com"
79+ differ by a zero vs an "o", so caps destroy the one glyph telling staging from production.
80+ Disconnect sits at the far end — it revokes the grant, so a misclick costs a full re-auth. */ }
81+ < Group gap = "xs" align = "center" justify = "space-between" wrap = "nowrap" >
82+ < Badge color = "green" variant = "light" tt = "none" >
83+ Connected: { siteLabel ( auth . site ) }
84+ </ Badge >
85+ < Button
86+ size = "compact-xs"
87+ variant = "light"
88+ color = "red"
89+ onClick = { auth . disconnect }
90+ loading = { auth . disconnecting }
91+ // Disconnect revokes the grant at Datadog before clearing the local session, so guard
92+ // against a second click re-running it against tokens the first click already revoked.
93+ disabled = { auth . disconnecting }
94+ >
5195 Disconnect
5296 </ Button >
5397 </ Group >
54- { /* Surface disconnect failures here too — otherwise a failed Disconnect looks like a no-op. */ }
98+ { /* Surface disconnect failures here too — otherwise a failed Disconnect looks like a no-op.
99+ (A revoke-succeeded-but-grant-live warning can't appear here: it always accompanies a
100+ successful local sign-out, which flips to the ConnectScreen where the notice lives.) */ }
55101 { auth . error && (
56- < Text c = "red" size = "xs" ta = "right" >
102+ < Text c = "red" size = "xs" >
57103 { auth . error }
58104 </ Text >
59105 ) }
60106 </ Stack >
61107 )
62108}
63109
64- function SiteField ( ) {
110+ // Falls back to the raw site so a stale or hand-edited setting still renders something meaningful
111+ // (getFlagsApiHost is the one that treats an unknown site as an error).
112+ function siteLabel ( site : string ) : string {
113+ return FLAG_SITES . find ( ( entry ) => entry . site === site ) ?. label ?? site
114+ }
115+
116+ function SiteField ( { disabled } : { disabled ?: boolean } ) {
65117 const [ { flagsSite } , setSetting ] = useSettings ( )
66118
67119 return (
@@ -72,6 +124,7 @@ function SiteField() {
72124 value = { flagsSite }
73125 onChange = { ( value ) => value && setSetting ( 'flagsSite' , value ) }
74126 allowDeselect = { false }
127+ disabled = { disabled }
75128 size = "xs"
76129 />
77130 )
0 commit comments