import { useEffect, useState } from "react"; import { graphql, useMutation } from "react-relay"; import { Dialog } from "./Dialog.tsx"; import { Button } from "./Button.tsx"; import { Input } from "./Input.tsx"; import { Textarea } from "./Textarea.tsx"; import { FormControl } from "./FormControl.tsx"; import { CopyableField } from "./CopyableField.tsx"; import type { OAuthClientModalCreateMutation } from "../__generated__/OAuthClientModalCreateMutation.graphql.ts"; import type { OAuthClientModalUpdateMutation } from "../__generated__/OAuthClientModalUpdateMutation.graphql.ts"; interface OAuthClient { clientId: string; clientSecret?: string | null; clientName: string; redirectUris: ReadonlyArray; scope?: string | null; clientUri?: string | null; logoUri?: string | null; tosUri?: string | null; policyUri?: string | null; } interface OAuthClientModalProps { sliceUri: string; client?: OAuthClient | null; onClose: () => void; onSuccess: (clientId: string, clientSecret: string | null) => void; } export function OAuthClientModal({ sliceUri, client, onClose, onSuccess, }: OAuthClientModalProps) { const isEditMode = !!client; const [clientName, setClientName] = useState(""); const [redirectUris, setRedirectUris] = useState(""); const [scope, setScope] = useState(""); const [clientUri, setClientUri] = useState(""); const [logoUri, setLogoUri] = useState(""); const [tosUri, setTosUri] = useState(""); const [policyUri, setPolicyUri] = useState(""); const [error, setError] = useState(null); // Initialize form with client data in edit mode useEffect(() => { if (client) { setClientName(client.clientName); setRedirectUris(client.redirectUris.join("\n")); setScope(client.scope || ""); setClientUri(client.clientUri || ""); setLogoUri(client.logoUri || ""); setTosUri(client.tosUri || ""); setPolicyUri(client.policyUri || ""); } }, [client]); const [createOAuthClient, isCreating] = useMutation< OAuthClientModalCreateMutation >(graphql` mutation OAuthClientModalCreateMutation( $sliceUri: String! $clientName: String! $redirectUris: [String!]! $scope: String! $clientUri: String $logoUri: String $tosUri: String $policyUri: String ) { createOAuthClient( sliceUri: $sliceUri clientName: $clientName redirectUris: $redirectUris scope: $scope clientUri: $clientUri logoUri: $logoUri tosUri: $tosUri policyUri: $policyUri ) { clientId clientSecret } } `); const [updateOAuthClient, isUpdating] = useMutation< OAuthClientModalUpdateMutation >(graphql` mutation OAuthClientModalUpdateMutation( $clientId: String! $clientName: String $redirectUris: [String!] $scope: String $clientUri: String $logoUri: String $tosUri: String $policyUri: String ) { updateOAuthClient( clientId: $clientId clientName: $clientName redirectUris: $redirectUris scope: $scope clientUri: $clientUri logoUri: $logoUri tosUri: $tosUri policyUri: $policyUri ) { clientId } } `); const isSubmitting = isCreating || isUpdating; const handleSubmit = (e: React.FormEvent) => { e.preventDefault(); setError(null); // Parse redirect URIs const uris = redirectUris .split("\n") .map((uri) => uri.trim()) .filter((uri) => uri.length > 0); if (uris.length === 0) { setError("At least one redirect URI is required"); return; } // Validate redirect URIs for (const uri of uris) { if (!uri.startsWith("http://") && !uri.startsWith("https://")) { setError(`Invalid redirect URI: ${uri}. Must use HTTP or HTTPS.`); return; } } if (isEditMode && client) { // Update existing client updateOAuthClient({ variables: { clientId: client.clientId, clientName: clientName.trim() || null, redirectUris: uris, scope: scope.trim() || null, clientUri: clientUri.trim() || null, logoUri: logoUri.trim() || null, tosUri: tosUri.trim() || null, policyUri: policyUri.trim() || null, }, onCompleted: (response) => { onSuccess(response.updateOAuthClient.clientId, null); }, onError: (err) => { console.error("Failed to update OAuth client:", err); setError( err.message || "Failed to update OAuth client. Please try again.", ); }, }); } else { // Create new client createOAuthClient({ variables: { sliceUri, clientName, redirectUris: uris, scope: scope.trim(), clientUri: clientUri.trim() || null, logoUri: logoUri.trim() || null, tosUri: tosUri.trim() || null, policyUri: policyUri.trim() || null, }, onCompleted: (response) => { onSuccess( response.createOAuthClient.clientId, response.createOAuthClient.clientSecret ?? null, ); }, onError: (err) => { console.error("Failed to create OAuth client:", err); setError( err.message || "Failed to create OAuth client. Please try again.", ); }, }); } }; return ( {isEditMode && client && (
{client.clientSecret && ( )}
)}
setClientName(e.target.value)} placeholder="My Application" required disabled={isSubmitting} />