import { useState } from "react"; import { useAtomSet } from "@effect-atom/atom-react"; import { useScope } from "@executor/react/api/scope-context"; import { sourceWriteKeys } from "@executor/react/api/reactivity-keys"; import { usePendingSources } from "@executor/react/api/optimistic"; import { HeadersList } from "@executor/react/plugins/headers-list"; import { type HeaderState } from "@executor/react/plugins/secret-header-auth"; import { displayNameFromUrl, slugifyNamespace, SourceIdentityFields, useSourceIdentity, } from "@executor/react/plugins/source-identity"; import { useSecretPickerSecrets } from "@executor/react/plugins/use-secret-picker-secrets"; import { Button } from "@executor/react/components/button"; import { CardStack, CardStackContent, CardStackEntryField, } from "@executor/react/components/card-stack"; import { FieldLabel } from "@executor/react/components/field"; import { FloatActions } from "@executor/react/components/float-actions"; import { Input } from "@executor/react/components/input"; import { Spinner } from "@executor/react/components/spinner"; import { addGraphqlSource } from "./atoms"; import type { HeaderValue } from "../sdk/types"; const initialHeader = (): HeaderState => ({ name: "Authorization", prefix: "Bearer ", presetKey: "bearer", secretId: null, }); export default function AddGraphqlSource(props: { onComplete: () => void; onCancel: () => void; initialUrl?: string; }) { const [endpoint, setEndpoint] = useState(props.initialUrl ?? ""); const identity = useSourceIdentity({ fallbackName: displayNameFromUrl(endpoint) ?? "", }); const [headers, setHeaders] = useState([initialHeader()]); const [adding, setAdding] = useState(false); const [addError, setAddError] = useState(null); const scopeId = useScope(); const doAdd = useAtomSet(addGraphqlSource, { mode: "promise" }); const { beginAdd } = usePendingSources(); const secretList = useSecretPickerSecrets(); const headersValid = headers.every((header) => header.name.trim() && header.secretId); const canAdd = endpoint.trim().length > 0 && (headers.length === 0 || headersValid); const handleAdd = async () => { setAdding(true); setAddError(null); const headerMap: Record = {}; for (const header of headers) { const name = header.name.trim(); if (name && header.secretId) { headerMap[name] = { secretId: header.secretId, ...(header.prefix ? { prefix: header.prefix } : {}), }; } } const trimmedEndpoint = endpoint.trim(); const namespace = slugifyNamespace(identity.namespace) || slugifyNamespace(displayNameFromUrl(trimmedEndpoint) ?? "") || "graphql"; const displayName = identity.name.trim() || displayNameFromUrl(trimmedEndpoint) || namespace; const placeholder = beginAdd({ id: namespace, name: displayName, kind: "graphql", url: trimmedEndpoint || undefined, }); try { await doAdd({ path: { scopeId }, payload: { endpoint: trimmedEndpoint, name: identity.name.trim() || undefined, namespace: slugifyNamespace(identity.namespace) || undefined, ...(Object.keys(headerMap).length > 0 ? { headers: headerMap } : {}), }, reactivityKeys: sourceWriteKeys, }); props.onComplete(); } catch (e) { setAddError(e instanceof Error ? e.message : "Failed to add source"); setAdding(false); } finally { placeholder.done(); } }; return (

Add GraphQL Source

setEndpoint((e.target as HTMLInputElement).value)} placeholder="https://api.example.com/graphql" className="font-mono text-sm" />
Headers
{/* Error */} {addError && (

{addError}

)}
); }