import { useEffect, useState } from "react"; import { useMutation, useQueryClient } from "@tanstack/react-query"; import type { CompanySecret, UserSecretDefinition } from "@paperclipai/shared"; import { Dialog, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogTitle, } from "@/components/ui/dialog"; import { Button } from "@/components/ui/button"; import { Input } from "@/components/ui/input"; import { Textarea } from "@/components/ui/textarea"; import { secretsApi } from "../../api/secrets"; import { ApiError } from "../../api/client"; import { queryKeys } from "../../lib/queryKeys"; import { useToastActions } from "../../context/ToastContext"; import { UserSecretChip } from "./user-secret-presentation"; /** * Shared "set my value" dialog for a user-secret definition. Used both from the * Secrets → My secrets tab and from the missing-required-secret warning surfaces * (task run / issue failure), so a user can satisfy a required secret from either * place with identical behavior. */ export function SetMyUserSecretDialog({ companyId, definition, existingSecret, open, onOpenChange, onSaved, }: { companyId: string; definition: UserSecretDefinition | null; existingSecret?: CompanySecret | null; open: boolean; onOpenChange: (open: boolean) => void; onSaved?: (secret: CompanySecret) => void; }) { const queryClient = useQueryClient(); const { pushToast } = useToastActions(); const [value, setValue] = useState(""); const [externalRef, setExternalRef] = useState(""); const [error, setError] = useState(null); const isExternal = definition?.managedMode === "external_reference"; useEffect(() => { if (open) { setValue(""); setExternalRef(""); setError(null); } }, [open, definition?.id]); const save = useMutation({ mutationFn: async () => { if (!definition) throw new Error("No definition selected"); const payload = isExternal ? { externalRef: externalRef.trim() } : { value: value.trim() }; if (existingSecret) { // A stored value already exists → rotate it in place. return secretsApi.rotateMyUserSecret(companyId, existingSecret.id, payload); } return secretsApi.createMyUserSecret(companyId, { definitionId: definition.id, definitionKey: definition.key, ...payload, }); }, onSuccess: (secret) => { queryClient.invalidateQueries({ queryKey: queryKeys.secrets.myUserSecrets(companyId) }); queryClient.invalidateQueries({ queryKey: queryKeys.secrets.userDefinitions(companyId) }); pushToast({ title: existingSecret ? "Value updated" : "Value saved", body: definition?.name, tone: "success", }); onSaved?.(secret); onOpenChange(false); }, onError: (err) => { setError( err instanceof ApiError ? err.message : err instanceof Error ? err.message : "Failed to save value", ); }, }); const canSave = isExternal ? externalRef.trim().length > 0 : value.trim().length > 0; return ( {existingSecret ? "Update your value" : "Set your value"} {definition ? ( <> This value is yours only. It is used when you are the user responsible for a run that needs {definition.key}. ) : null} {definition ? (
{definition.name}
{definition.description ? (

{definition.description}

) : null} {definition.usageGuidance ? (

{definition.usageGuidance}

) : null}
{isExternal ? (
setExternalRef(event.target.value)} placeholder="provider reference or ARN" className="font-mono text-sm" autoFocus />

Points at your own credential in the configured provider. Paperclip stores the reference, not the value.

) : (