11import { useCallback , useEffect , useMemo , useRef , useState } from "react" ;
22import { useAtomSet , useAtomValue , Result } from "@effect-atom/atom-react" ;
33
4- import { secretsAtom , setSecret } from "@executor/react/api/atoms" ;
4+ import { secretsAtom } from "@executor/react/api/atoms" ;
55import { usePendingSources } from "@executor/react/api/optimistic" ;
6- import { secretWriteKeys , sourceWriteKeys } from "@executor/react/api/reactivity-keys" ;
6+ import { sourceWriteKeys } from "@executor/react/api/reactivity-keys" ;
77import { useScope } from "@executor/react/api/scope-context" ;
8- import { SecretPicker , type SecretPickerSecret } from "@executor/react/plugins/secret-picker" ;
9- import { SecretId } from "@executor/sdk " ;
8+ import type { SecretPickerSecret } from "@executor/react/plugins/secret-picker" ;
9+ import { CreatableSecretPicker } from "@executor/react/plugins/secret-header-auth " ;
1010import { Badge } from "@executor/react/components/badge" ;
1111import { Button } from "@executor/react/components/button" ;
1212import {
@@ -42,7 +42,6 @@ import {
4242import { FilterTabs } from "@executor/react/components/filter-tabs" ;
4343import { FloatActions } from "@executor/react/components/float-actions" ;
4444import { Input } from "@executor/react/components/input" ;
45- import { Label } from "@executor/react/components/label" ;
4645import { RadioGroup , RadioGroupItem } from "@executor/react/components/radio-group" ;
4746import { IOSSpinner , Spinner } from "@executor/react/components/spinner" ;
4847import { addGoogleDiscoverySource , probeGoogleDiscovery } from "./atoms" ;
@@ -63,146 +62,34 @@ const GOOGLE_EXTRA_AUTHORIZATION_PARAMS = {
6362
6463type GoogleAuthKind = "none" | "oauth2" ;
6564
66- // ---------------------------------------------------------------------------
67- // Inline secret creation
68- // ---------------------------------------------------------------------------
69-
70- function InlineCreateSecret ( props : {
71- headerName : string ;
72- suggestedId : string ;
73- onCreated : ( secretId : string ) => void ;
74- onCancel : ( ) => void ;
75- } ) {
76- const [ secretId , setSecretIdValue ] = useState ( props . suggestedId ) ;
77- const [ secretName , setSecretName ] = useState ( props . headerName ) ;
78- const [ secretValue , setSecretValue ] = useState ( "" ) ;
79- const [ saving , setSaving ] = useState ( false ) ;
80- const [ error , setError ] = useState < string | null > ( null ) ;
81- const scopeId = useScope ( ) ;
82- const doSet = useAtomSet ( setSecret , { mode : "promise" } ) ;
83-
84- const handleSave = async ( ) => {
85- if ( ! secretId . trim ( ) || ! secretValue . trim ( ) ) return ;
86- setSaving ( true ) ;
87- setError ( null ) ;
88- try {
89- await doSet ( {
90- path : { scopeId } ,
91- payload : {
92- id : SecretId . make ( secretId . trim ( ) ) ,
93- name : secretName . trim ( ) || secretId . trim ( ) ,
94- value : secretValue . trim ( ) ,
95- } ,
96- reactivityKeys : [ ...secretWriteKeys ] ,
97- } ) ;
98- props . onCreated ( secretId . trim ( ) ) ;
99- } catch ( e ) {
100- setError ( e instanceof Error ? e . message : "Failed to save secret" ) ;
101- setSaving ( false ) ;
102- }
103- } ;
104-
105- return (
106- < div className = "rounded-lg border border-primary/20 bg-primary/[0.02] p-3 space-y-2.5" >
107- < p className = "text-[11px] font-semibold text-primary tracking-wide uppercase" > New secret</ p >
108- < div className = "grid grid-cols-2 gap-2" >
109- < div className = "space-y-1" >
110- < Label className = "text-[10px] uppercase tracking-wider text-muted-foreground" > ID</ Label >
111- < Input
112- value = { secretId }
113- onChange = { ( e ) => setSecretIdValue ( ( e . target as HTMLInputElement ) . value ) }
114- placeholder = "google-client-secret"
115- className = "h-8 text-xs font-mono"
116- />
117- </ div >
118- < div className = "space-y-1" >
119- < Label className = "text-[10px] uppercase tracking-wider text-muted-foreground" >
120- Label
121- </ Label >
122- < Input
123- value = { secretName }
124- onChange = { ( e ) => setSecretName ( ( e . target as HTMLInputElement ) . value ) }
125- placeholder = "Client Secret"
126- className = "h-8 text-xs"
127- />
128- </ div >
129- </ div >
130- < div className = "space-y-1" >
131- < Label className = "text-[10px] uppercase tracking-wider text-muted-foreground" > Value</ Label >
132- < Input
133- type = "password"
134- value = { secretValue }
135- onChange = { ( e ) => setSecretValue ( ( e . target as HTMLInputElement ) . value ) }
136- placeholder = "paste your client secret…"
137- className = "h-8 text-xs font-mono"
138- />
139- </ div >
140- { error && < p className = "text-[11px] text-destructive" > { error } </ p > }
141- < div className = "flex justify-end gap-1.5 pt-0.5" >
142- < Button variant = "outline" size = "xs" onClick = { props . onCancel } >
143- Cancel
144- </ Button >
145- < Button
146- size = "xs"
147- onClick = { handleSave }
148- disabled = { ! secretId . trim ( ) || ! secretValue . trim ( ) || saving }
149- >
150- { saving ? "Saving…" : "Create and use" }
151- </ Button >
152- </ div >
153- </ div >
154- ) ;
155- }
156-
15765// ---------------------------------------------------------------------------
15866// Client secret field with inline creation
15967// ---------------------------------------------------------------------------
16068
16169function SecretBackedField ( props : {
16270 label : string ;
16371 suggestedSecretId : string ;
164- headerName : string ;
16572 secretId : string | null ;
16673 onSelect : ( secretId : string | null ) => void ;
16774 secretList : readonly SecretPickerSecret [ ] ;
16875 placeholder : string ;
16976 clearable ?: boolean ;
17077} ) {
171- const [ creating , setCreating ] = useState ( false ) ;
17278 const { label, secretId, onSelect, secretList, placeholder, clearable = true } = props ;
17379
174- if ( creating ) {
175- return (
176- < div className = "space-y-2" >
177- < Label > { label } </ Label >
178- < InlineCreateSecret
179- headerName = { props . headerName }
180- suggestedId = { props . suggestedSecretId }
181- onCreated = { ( id ) => {
182- onSelect ( id ) ;
183- setCreating ( false ) ;
184- } }
185- onCancel = { ( ) => setCreating ( false ) }
186- />
187- </ div >
188- ) ;
189- }
190-
19180 return (
19281 < div className = "space-y-2" >
193- < Label > { label } </ Label >
19482 < div className = "flex items-center gap-2" >
19583 < div className = "flex-1 min-w-0" >
196- < SecretPicker
84+ < CreatableSecretPicker
19785 value = { secretId }
198- onSelect = { onSelect }
86+ onSelect = { ( id ) => onSelect ( id ) }
19987 secrets = { secretList }
20088 placeholder = { placeholder }
89+ suggestedId = { props . suggestedSecretId }
90+ secretLabel = { label }
20191 />
20292 </ div >
203- < Button variant = "outline" size = "sm" className = "shrink-0" onClick = { ( ) => setCreating ( true ) } >
204- + New
205- </ Button >
20693 { clearable && secretId && (
20794 < Button variant = "outline" onClick = { ( ) => onSelect ( null ) } >
20895 Clear
@@ -725,7 +612,6 @@ export default function AddGoogleDiscoverySource(props: {
725612 < div className = "space-y-3 rounded-xl border border-border bg-card px-4 py-4" >
726613 < SecretBackedField
727614 label = "OAuth Client ID"
728- headerName = "Client ID"
729615 suggestedSecretId = "google-oauth-client-id"
730616 secretId = { clientIdSecretId }
731617 onSelect = { setClientIdSecretId }
@@ -735,7 +621,6 @@ export default function AddGoogleDiscoverySource(props: {
735621 />
736622 < SecretBackedField
737623 label = "OAuth Client Secret"
738- headerName = "Client Secret"
739624 suggestedSecretId = "google-oauth-client-secret"
740625 secretId = { clientSecretSecretId }
741626 onSelect = { setClientSecretSecretId }
0 commit comments