Skip to content

Commit 9638cc3

Browse files
authored
Use shared secret picker for Google Discovery (#450)
1 parent a99aceb commit 9638cc3

1 file changed

Lines changed: 8 additions & 123 deletions

File tree

‎packages/plugins/google-discovery/src/react/AddGoogleDiscoverySource.tsx‎

Lines changed: 8 additions & 123 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,12 @@
11
import { useCallback, useEffect, useMemo, useRef, useState } from "react";
22
import { 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";
55
import { 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";
77
import { 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";
1010
import { Badge } from "@executor/react/components/badge";
1111
import { Button } from "@executor/react/components/button";
1212
import {
@@ -42,7 +42,6 @@ import {
4242
import { FilterTabs } from "@executor/react/components/filter-tabs";
4343
import { FloatActions } from "@executor/react/components/float-actions";
4444
import { Input } from "@executor/react/components/input";
45-
import { Label } from "@executor/react/components/label";
4645
import { RadioGroup, RadioGroupItem } from "@executor/react/components/radio-group";
4746
import { IOSSpinner, Spinner } from "@executor/react/components/spinner";
4847
import { addGoogleDiscoverySource, probeGoogleDiscovery } from "./atoms";
@@ -63,146 +62,34 @@ const GOOGLE_EXTRA_AUTHORIZATION_PARAMS = {
6362

6463
type 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

16169
function 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

Comments
 (0)