File size: 2,713 Bytes
9b906ea
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
import { useTranslation } from "react-i18next";
import { AcpConflictWarnings } from "#/components/features/settings/acp-conflict-warnings";
import { AcpAuthStatusBanner } from "#/components/features/settings/acp-auth-status-banner";
import { AcpSecretField } from "#/components/features/settings/acp-secret-field";
import { Typography } from "#/ui/typography";
import { I18nKey } from "#/i18n/declaration";
import { useAcpAuthStatus } from "#/hooks/query/use-acp-auth-status";
import { getAcpProviderDisplayName } from "#/constants/acp-providers";
import type { AcpCredentialForm } from "#/hooks/use-acp-credential-form";

/**
 * Settings → Agent credentials section for a built-in ACP provider: renders the
 * same fields the onboarding step collects (and the same "already signed in"
 * auth banner), so credentials can be added or rotated after onboarding. The
 * form state and the save are owned by the parent (Settings → Agent) so the
 * page has a single Save button for both agent settings and credentials.
 * Renders nothing for providers without credential fields.
 */
export function AcpCredentialsSection({
  form,
  providerKey,
}: {
  form: AcpCredentialForm;
  providerKey: string;
}) {
  const { t } = useTranslation("openhands");
  const {
    fields,
    values,
    setValue,
    secretExists,
    conflicts,
    credentialsConfigured,
  } = form;
  const { status: authStatus, isChecking } = useAcpAuthStatus(providerKey);
  const providerName = getAcpProviderDisplayName(providerKey) ?? providerKey;

  if (fields.length === 0) return null;

  return (
    <div className="flex flex-col gap-4">
      <div className="flex flex-col gap-1">
        <Typography.Text className="text-sm font-medium text-white">
          {t(I18nKey.SETTINGS$ACP_CREDENTIALS_TITLE)}
        </Typography.Text>
        <Typography.Text className="text-xs text-[#717888]">
          {t(I18nKey.SETTINGS$ACP_CREDENTIALS_DESCRIPTION)}
        </Typography.Text>
      </div>

      <AcpAuthStatusBanner
        status={authStatus}
        isChecking={isChecking}
        credentialsConfigured={credentialsConfigured}
        providerName={providerName}
        testIdPrefix="settings-acp-auth"
      />

      <div className="flex flex-col gap-5">
        {fields.map((field) => (
          <AcpSecretField
            key={field.name}
            field={field}
            value={values[field.name] ?? ""}
            onChange={(value) => setValue(field.name, value)}
            alreadySet={secretExists(field.name)}
            testId={`settings-acp-secret-${field.name}`}
            showOptionalTag
          />
        ))}
      </div>

      <AcpConflictWarnings conflicts={conflicts} />
    </div>
  );
}