import React, { type ReactNode } from "react"; import { Alert, Button, Checkbox, Form, Input, InputNumber, Select, Space, Switch, Table, Tag, Typography, } from "antd"; import { ApiOutlined, BgColorsOutlined, CloudOutlined, ClusterOutlined, CodeOutlined, DatabaseOutlined, FileTextOutlined, GatewayOutlined, LinkOutlined, SafetyCertificateOutlined, ThunderboltOutlined, } from "@ant-design/icons"; import { DB_ICON_TYPES, PRESET_ICON_COLORS, getDbDefaultColor, getDbIcon, getDbIconLabel, } from "../DatabaseIcons"; import ConnectionModalMongoSections from "../ConnectionModalMongoSections"; import ConnectionModalRedisSections from "../ConnectionModalRedisSections"; import { t } from "../../i18n"; import { supportsConnectionReadOnlyMode, } from "../../utils/connectionReadOnly"; import { getConnectionConfigLayoutKindLabel, getStoredSecretPlaceholder, } from "../../utils/connectionModalPresentation"; import { getCustomConnectionDriverHelp } from "../../utils/driverImportGuidance"; import { noAutoCapInputProps } from "../../utils/inputAutoCap"; import { JVM_EDITABLE_MODES, normalizeEditableJVMModes, } from "../../utils/jvmConnectionConfig"; import { resolveJVMModeMeta } from "../../utils/jvmRuntimePresentation"; import { getConnectionParamsPlaceholder, getUriPlaceholder, } from "./connectionModalUri"; import ConnectionModalNetworkSecuritySection from "./ConnectionModalNetworkSecuritySection"; import type { MongoMemberInfo } from "../../types"; const { Text } = Typography; const CLICKHOUSE_PROTOCOL_OPTIONS: Array<{ value: "auto" | "http" | "native"; label?: string; labelKey?: string; }> = [ { value: "auto", labelKey: "connection.modal.field.clickHouseProtocol.auto" }, { value: "http", label: "HTTP" }, { value: "native", label: "Native" }, ]; const OCEANBASE_PROTOCOL_OPTIONS: Array<{ value: "mysql" | "oracle"; label: string; }> = [ { value: "mysql", label: "MySQL" }, { value: "oracle", label: "Oracle" }, ]; const PRIMARY_USERNAME_OPTIONAL_TYPES = new Set([ "mongodb", "elasticsearch", "chroma", "qdrant", "rocketmq", "mqtt", "kafka", "rabbitmq", ]); type ConnectionModalStep2Props = Record; const ConnectionModalStep2: React.FC = (props) => { const { activeConfigSection, activeNetworkConfig, buildRedisDatabaseList, clearConnectionTestResultForChoice, connectionConfigLayout, createCustomDsnRule, createUriAwareRequiredRule, currentDriverSnapshot, currentDriverUnavailableReason, currentDriverUpdateReason, customIconColor, customIconType, darkMode, dbList, dbType, discoveringMembers, form, getConnectionOptionCardStyle, handleCopyURI, handleDiscoverMongoMembers, handleGenerateURI, handleJvmModeCardSelect, handleJvmModeToggle, handleParseURI, handleSelectCertificateFile, handleSelectDatabaseFile, handleSelectSSHKeyFile, initialValues, isCustom, isFileDb, isJVM, isKafka, isMQTT, isMySQLLike, isOceanBaseOracle, isRedis, isRocketMQ, isSSLType, jvmDiagnosticEnabled, jvmDiagnosticTransport, jvmEnvironment, jvmPreferredMode, jvmSectionCardStyle, kafkaTopology, modalInnerSectionStyle, modalMutedTextStyle, mongoAuthMechanism, mongoMembers, mongoReadPreference, mongoSrv, mongoTopology, mqttTopology, mysqlTopology, normalizeRedisDatabaseSelection, normalizedJvmAllowedModes, oceanBaseProtocol, onOpenDriverManager, primaryPasswordVisible, proxyType, redisDbList, redisTopology, renderChoiceCards, renderConfigSectionCard, renderJvmSectionHeader, renderStoredSecretControls, resolvedUriFeedbackMessage, rocketmqTopology, selectingCertificateField, selectingDbFile, selectingSSHKey, setActiveConfigSection, setActiveNetworkConfig, setChoiceFieldValue, setCustomIconColor, setCustomIconType, setDbType, setMongoMembers, setPrimaryPasswordVisible, setRedisDbList, setTestErrorLogOpen, setTestResult, setUriFeedback, setUseHttpTunnel, setUseProxy, setUseSSH, setUseSSL, sslHintText, sslMode, supportsConnectionParams, supportsSSLCAPath, supportsSSLClientCertificate, testResult, tunnelSectionStyle, unsupportedJvmModeMessage, uriFeedback, useHttpTunnel, useProxy, useSSH, useSSL, } = props; const [readOnlyProtectionExpanded, setReadOnlyProtectionExpanded] = React.useState(false); React.useEffect(() => { setReadOnlyProtectionExpanded(false); }, [dbType]); const renderStep2 = () => { const showConnectionReadOnlyField = supportsConnectionReadOnlyMode({ type: dbType, driver: form.getFieldValue("driver"), oceanBaseProtocol, }); const restrictDataEdit = Form.useWatch("restrictDataEdit", form) === true; const restrictStructureEdit = Form.useWatch("restrictStructureEdit", form) === true; const restrictScriptExecution = Form.useWatch("restrictScriptExecution", form) === true; const restrictDataImport = Form.useWatch("restrictDataImport", form) === true; const connectionProtectionEnabledCount = [ restrictDataEdit, restrictStructureEdit, restrictScriptExecution, restrictDataImport, ].filter(Boolean).length; const baseInfoSection = (
{t("connection.modal.config.basic.title")}
{t("connection.modal.config.basic.description")}
{renderConfigSectionCard({ sectionKey: "identity", icon: , badge: ( {getConnectionConfigLayoutKindLabel(connectionConfigLayout.kind)} ), children: ( ), })} {!isCustom && !isJVM && renderConfigSectionCard({ sectionKey: "uri", icon: , children: ( <> {supportsConnectionParams && ( )} {uriFeedback && ( setUriFeedback(null)} style={{ marginBottom: 16 }} /> )} {renderStoredSecretControls({ fieldName: "uri", clearKey: "opaqueURI", hasStoredSecret: initialValues?.hasOpaqueURI, clearLabel: t("connection.modal.uri.stored.clear"), description: t("connection.modal.uri.stored.description"), })} ), })} {isCustom ? ( <> {renderConfigSectionCard({ sectionKey: "customDriver", icon: , children: ( ), })} {renderConfigSectionCard({ sectionKey: "customDsn", icon: , children: ( <> {renderStoredSecretControls({ fieldName: "dsn", clearKey: "opaqueDSN", hasStoredSecret: initialValues?.hasOpaqueDSN, clearLabel: t("connection.modal.field.dsn.clearSaved"), description: t( "connection.modal.field.dsn.savedDescription", ), })} ), })} ) : isJVM ? ( <> {unsupportedJvmModeMessage && ( )}
{renderJvmSectionHeader( , t("connection.modal.jvm.target.title"), t("connection.modal.jvm.target.description"), )}
{t("connection.modal.jvm.environment.title")} {renderChoiceCards({ fieldName: "jvmEnvironment", value: String(jvmEnvironment), minWidth: 120, options: [ { value: "dev", label: t( "connection.modal.jvm.environment.dev.label", ), description: t( "connection.modal.jvm.environment.dev.description", ), }, { value: "uat", label: t( "connection.modal.jvm.environment.staging.label", ), description: t( "connection.modal.jvm.environment.staging.description", ), }, { value: "prod", label: t( "connection.modal.jvm.environment.prod.label", ), description: t( "connection.modal.jvm.environment.prod.description", ), }, ], })}
{t("connection.modal.jvm.readonlyPreferred")}
{renderJvmSectionHeader( , t("connection.modal.jvm.accessMode.title"), t("connection.modal.jvm.accessMode.description"), )}
{JVM_EDITABLE_MODES.map((mode) => { const meta = resolveJVMModeMeta(mode); const enabled = normalizedJvmAllowedModes.includes(mode); const preferred = jvmPreferredMode === mode; return (
handleJvmModeCardSelect(mode)} onKeyDown={(event) => { if (event.key === "Enter" || event.key === " ") { event.preventDefault(); handleJvmModeCardSelect(mode); } }} aria-pressed={enabled} style={{ textAlign: "left", padding: 14, borderRadius: 16, border: enabled ? darkMode ? "1px solid rgba(255,214,102,0.36)" : "1px solid rgba(22,119,255,0.34)" : darkMode ? "1px solid rgba(255,255,255,0.08)" : "1px solid rgba(16,24,40,0.08)", background: enabled ? darkMode ? "rgba(255,214,102,0.08)" : "rgba(22,119,255,0.06)" : darkMode ? "rgba(255,255,255,0.03)" : "rgba(16,24,40,0.03)", boxShadow: preferred ? darkMode ? "0 0 0 2px rgba(255,214,102,0.12)" : "0 0 0 2px rgba(22,119,255,0.10)" : "none", color: darkMode ? "#f5f7ff" : "#162033", cursor: "pointer", transition: "all 120ms ease", }} > {meta.label} {preferred ? ( {t("connection.modal.jvm.tag.preferred")} ) : null} {!enabled ? ( {t("connection.modal.jvm.tag.notEnabled")} ) : null}
{mode === "jmx" ? t("connection.modal.jvm.mode.jmx.description") : mode === "endpoint" ? t( "connection.modal.jvm.mode.endpoint.description", ) : t("connection.modal.jvm.mode.agent.description")}
); })}
{t("connection.modal.jvm.preferredSummary", { mode: resolveJVMModeMeta(String(jvmPreferredMode || "jmx")) .label, })}
{renderJvmSectionHeader( , "JMX", t("connection.modal.jvm.jmx.description"), {normalizedJvmAllowedModes.includes("jmx") ? t("connection.modal.jvm.tag.enabled") : t("connection.modal.jvm.tag.notEnabled")} , )}
{renderJvmSectionHeader( , "Endpoint", t("connection.modal.jvm.endpoint.description"), {normalizedJvmAllowedModes.includes("endpoint") ? t("connection.modal.jvm.tag.enabled") : t("connection.modal.jvm.tag.notEnabled")} , )}
{renderJvmSectionHeader( , "Agent", t("connection.modal.jvm.agent.description"), {normalizedJvmAllowedModes.includes("agent") ? t("connection.modal.jvm.tag.enabled") : t("connection.modal.jvm.tag.notEnabled")} , )}
{renderJvmSectionHeader( , t("connection.modal.jvm.diagnostic.title"), t("connection.modal.jvm.diagnostic.description"), , )} {jvmDiagnosticEnabled ? ( <>
{t("connection.modal.jvm.diagnostic.transport.label")} {renderChoiceCards({ fieldName: "jvmDiagnosticTransport", value: String(jvmDiagnosticTransport), options: [ { value: "agent-bridge", label: t( "connection.modal.jvm.diagnostic.transport.agent_bridge", ), description: t( "connection.modal.jvm.diagnostic.transport.agentBridge.description", ), }, { value: "arthas-tunnel", label: t( "connection.modal.jvm.diagnostic.transport.arthas_tunnel", ), description: t( "connection.modal.jvm.diagnostic.transport.arthasTunnel.description", ), }, ], })}
{[ { name: "jvmDiagnosticAllowObserveCommands", label: t( "connection.modal.jvm.diagnostic.command.observe.label", ), description: t( "connection.modal.jvm.diagnostic.command.observe.description", ), }, { name: "jvmDiagnosticAllowTraceCommands", label: t( "connection.modal.jvm.diagnostic.command.trace.label", ), description: t( "connection.modal.jvm.diagnostic.command.trace.description", ), }, { name: "jvmDiagnosticAllowMutatingCommands", label: t( "connection.modal.jvm.diagnostic.command.mutating.label", ), description: t( "connection.modal.jvm.diagnostic.command.mutating.description", ), }, ].map((item) => (
{item.label}
{item.description}
))}
) : (
{t("connection.modal.jvm.diagnostic.disabledHint")}
)}
) : ( <> {renderConfigSectionCard({ sectionKey: isFileDb ? "fileTarget" : "target", icon: isFileDb ? : , children: (
{isFileDb ? ( ) : ( Number(value) > 0, ), ]} style={{ marginBottom: 0 }} > )}
), })} {dbType === "clickhouse" && renderConfigSectionCard({ sectionKey: "connectionMode", icon: , children: ( { form.setFieldsValue({ mysqlTopology: "single" }); clearConnectionTestResultForChoice(); }} /> ), })} {(dbType === "postgres" || dbType === "kingbase" || dbType === "highgo" || dbType === "vastbase" || dbType === "opengauss" || dbType === "gaussdb" || dbType === "trino") && renderConfigSectionCard({ sectionKey: "service", icon: , children: ( ), })} {dbType === "kafka" && renderConfigSectionCard({ sectionKey: "service", icon: , children: ( ), })} {dbType === "rocketmq" && renderConfigSectionCard({ sectionKey: "service", icon: , children: ( ), })} {dbType === "mqtt" && renderConfigSectionCard({ sectionKey: "service", icon: , children: ( ), })} {dbType === "rabbitmq" && renderConfigSectionCard({ sectionKey: "service", icon: , children: ( ), })} {(dbType === "oracle" || isOceanBaseOracle) && renderConfigSectionCard({ sectionKey: "service", icon: , children: ( ), })} {showConnectionReadOnlyField && renderConfigSectionCard({ sectionKey: "readOnly", icon: , collapsible: true, expanded: readOnlyProtectionExpanded, onToggle: () => setReadOnlyProtectionExpanded((expanded) => !expanded), badge: ( 0 ? "red" : "default" } > {connectionProtectionEnabledCount > 0 ? t( "connection.modal.field.readOnly.status.enabledCount", { count: connectionProtectionEnabledCount, }, ) : t("connection.modal.field.readOnly.status.disabled")} ), children: (
0 ? darkMode ? "1px solid rgba(255,120,117,0.34)" : "1px solid rgba(245,34,45,0.18)" : darkMode ? "1px solid rgba(255,214,102,0.24)" : "1px solid rgba(250,173,20,0.18)", background: connectionProtectionEnabledCount > 0 ? darkMode ? "linear-gradient(180deg, rgba(255,120,117,0.12) 0%, rgba(255,120,117,0.05) 100%)" : "linear-gradient(180deg, rgba(255,245,245,0.96) 0%, rgba(255,240,240,0.92) 100%)" : darkMode ? "linear-gradient(180deg, rgba(255,214,102,0.10) 0%, rgba(255,214,102,0.04) 100%)" : "linear-gradient(180deg, rgba(255,251,230,0.98) 0%, rgba(255,247,214,0.94) 100%)", boxShadow: darkMode ? "inset 0 1px 0 rgba(255,255,255,0.04)" : "inset 0 1px 0 rgba(255,255,255,0.92)", }} >
{t("connection.modal.field.readOnly.label")}
{t("connection.modal.field.readOnly.help")}
{t("connection.modal.field.readOnly.compatibility")}
{[ { field: "restrictDataEdit", checked: restrictDataEdit, label: t( "connection.modal.field.readOnly.option.dataEdit.label", ), help: t( "connection.modal.field.readOnly.option.dataEdit.help", ), }, { field: "restrictStructureEdit", checked: restrictStructureEdit, label: t( "connection.modal.field.readOnly.option.structureEdit.label", ), help: t( "connection.modal.field.readOnly.option.structureEdit.help", ), }, { field: "restrictScriptExecution", checked: restrictScriptExecution, label: t( "connection.modal.field.readOnly.option.scriptExecution.label", ), help: t( "connection.modal.field.readOnly.option.scriptExecution.help", ), }, { field: "restrictDataImport", checked: restrictDataImport, label: t( "connection.modal.field.readOnly.option.dataImport.label", ), help: t( "connection.modal.field.readOnly.option.dataImport.help", ), }, ].map((item) => (
setChoiceFieldValue(item.field, !item.checked) } style={{ padding: 14, borderRadius: 14, border: item.checked ? darkMode ? "1px solid rgba(255,120,117,0.22)" : "1px solid rgba(245,34,45,0.14)" : darkMode ? "1px solid rgba(255,255,255,0.08)" : "1px solid rgba(5,5,5,0.08)", background: item.checked ? darkMode ? "rgba(255,120,117,0.08)" : "rgba(255,241,240,0.92)" : darkMode ? "rgba(255,255,255,0.02)" : "rgba(255,255,255,0.9)", cursor: "pointer", }} >
event.stopPropagation()} > clearConnectionTestResultForChoice() } style={{ marginInlineStart: 0, }} />
{item.label}
{item.help}
))}
{t("connection.modal.field.readOnly.summary.title")}
{connectionProtectionEnabledCount > 0 ? t( "connection.modal.field.readOnly.summary.selected", { count: connectionProtectionEnabledCount }, ) : t( "connection.modal.field.readOnly.summary.empty", )}
{t("connection.modal.field.readOnly.tip")}
), })} {isMySQLLike && renderConfigSectionCard({ sectionKey: "connectionMode", icon: , children: renderChoiceCards({ fieldName: "mysqlTopology", value: String(mysqlTopology), options: [ { value: "single", label: t("connection.modal.topology.single.label"), description: t( "connection.modal.topology.mysql.single.description", ), }, { value: "replica", label: t( "connection.modal.topology.mysql.replica.label", ), description: t( "connection.modal.topology.mysql.replica.description", ), }, ], }), })} {isKafka && renderConfigSectionCard({ sectionKey: "connectionMode", icon: , children: renderChoiceCards({ fieldName: "kafkaTopology", value: String(kafkaTopology), options: [ { value: "single", label: t("connection.modal.messageQueue.kafka.topology.single.label"), description: t( "connection.modal.messageQueue.kafka.topology.single.description", ), }, { value: "cluster", label: t("connection.modal.messageQueue.topology.cluster.label"), description: t( "connection.modal.messageQueue.kafka.topology.cluster.description", ), }, ], }), })} {isRocketMQ && renderConfigSectionCard({ sectionKey: "connectionMode", icon: , children: renderChoiceCards({ fieldName: "rocketmqTopology", value: String(rocketmqTopology), options: [ { value: "single", label: t("connection.modal.messageQueue.rocketmq.topology.single.label"), description: t( "connection.modal.messageQueue.rocketmq.topology.single.description", ), }, { value: "cluster", label: t("connection.modal.messageQueue.topology.cluster.label"), description: t( "connection.modal.messageQueue.rocketmq.topology.cluster.description", ), }, ], }), })} {isMQTT && renderConfigSectionCard({ sectionKey: "connectionMode", icon: , children: renderChoiceCards({ fieldName: "mqttTopology", value: String(mqttTopology), options: [ { value: "single", label: t("connection.modal.messageQueue.mqtt.topology.single.label"), description: t( "connection.modal.messageQueue.mqtt.topology.single.description", ), }, { value: "cluster", label: t("connection.modal.messageQueue.topology.cluster.label"), description: t( "connection.modal.messageQueue.mqtt.topology.cluster.description", ), }, ], }), })} {isKafka && kafkaTopology === "cluster" && renderConfigSectionCard({ sectionKey: "replica", icon: , children: ( ), })} {isMQTT && mqttTopology === "cluster" && renderConfigSectionCard({ sectionKey: "replica", icon: , children: (
{renderStoredSecretControls({ fieldName: "mysqlReplicaPassword", clearKey: "mysqlReplicaPassword", hasStoredSecret: initialValues?.hasMySQLReplicaPassword, clearLabel: t( "connection.modal.field.mysqlReplicaPassword.clear", ), description: t( "connection.modal.field.mysqlReplicaPassword.savedDescription", ), })} ), })} {dbType === "mongodb" && renderConfigSectionCard({ sectionKey: "connectionMode", icon: , children: renderChoiceCards({ fieldName: "mongoTopology", value: String(mongoTopology), options: [ { value: "single", label: t("connection.modal.topology.single.label"), description: t( "connection.modal.topology.mongodb.single.description", ), }, { value: "replica", label: t( "connection.modal.topology.mongodb.replica.label", ), description: t( "connection.modal.topology.mongodb.replica.description", ), }, ], }), })} {dbType === "mongodb" && renderConfigSectionCard({ sectionKey: "mongoDiscovery", icon: , children: ( <>
{[ { value: false, label: t( "connection.modal.mongo.discovery.standard.label", ), description: t( "connection.modal.mongo.discovery.standard.description", ), }, { value: true, label: t( "connection.modal.mongo.discovery.srv.label", ), description: t( "connection.modal.mongo.discovery.srv.description", ), }, ].map((option) => { const active = mongoSrv === option.value; return ( ); })}
{mongoSrv && useSSH && ( )} ), })} {dbType === "mongodb" && mongoTopology === "replica" && renderConfigSectionCard({ sectionKey: "replica", icon: , children: ( <>
{renderStoredSecretControls({ fieldName: "mongoReplicaPassword", clearKey: "mongoReplicaPassword", hasStoredSecret: initialValues?.hasMongoReplicaPassword, clearLabel: t( "connection.modal.field.mongoReplicaPassword.clear", ), description: t( "connection.modal.field.mongoReplicaPassword.savedDescription", ), })} {mongoMembers.length > 0 && ( record.host} pagination={false} dataSource={mongoMembers} style={{ marginBottom: 12 }} columns={[ { title: t("connection.modal.field.host.label"), dataIndex: "host", width: "48%", }, { title: t("connection.modal.mongo.member.role"), dataIndex: "role", width: "32%", render: ( value: string, record: MongoMemberInfo, ) => ( {value || record.state || t("common.unknown")} ), }, { title: t("connection.modal.mongo.member.health"), dataIndex: "healthy", width: "20%", render: (value: boolean) => ( {value ? t("connection.modal.mongo.member.healthy") : t( "connection.modal.mongo.member.unhealthy", )} ), }, ]} /> )} ), })} {dbType === "mongodb" && renderConfigSectionCard({ sectionKey: "mongoPolicy", icon: , children: (
{t("connection.modal.mongo.readPreference.label")} {renderChoiceCards({ fieldName: "mongoReadPreference", value: String(mongoReadPreference), minWidth: 130, options: [ { value: "primary", label: "primary", description: t( "connection.modal.mongo.readPreference.primary.description", ), }, { value: "primaryPreferred", label: "primaryPreferred", description: t( "connection.modal.mongo.readPreference.primaryPreferred.description", ), }, { value: "secondary", label: "secondary", description: t( "connection.modal.mongo.readPreference.secondary.description", ), }, { value: "secondaryPreferred", label: "secondaryPreferred", description: t( "connection.modal.mongo.readPreference.secondaryPreferred.description", ), }, { value: "nearest", label: "nearest", description: t( "connection.modal.mongo.readPreference.nearest.description", ), }, ], })}
), })} {isRedis && renderConfigSectionCard({ sectionKey: "connectionMode", icon: , children: ( <> {renderChoiceCards({ fieldName: "redisTopology", value: String(redisTopology), options: [ { value: "single", label: t("connection.modal.topology.single.label"), description: t( "connection.modal.topology.redis.single.description", ), }, { value: "cluster", label: t( "connection.modal.topology.redis.cluster.label", ), description: t( "connection.modal.topology.redis.cluster.description", ), }, ], })} {redisTopology === "cluster" && ( {redisDbList.map((db: number) => ( db{db} ))} ), })} {!isFileDb && !isRedis && renderConfigSectionCard({ sectionKey: "credentials", icon: , children: ( <>
{dbType === "mongodb" && (
{t( "connection.modal.mongo.authMechanism.label", )} {renderChoiceCards({ fieldName: "mongoAuthMechanism", value: String(mongoAuthMechanism), minWidth: 150, options: [ { value: "", label: t( "connection.modal.mongo.authMechanism.auto.label", ), description: t( "connection.modal.mongo.authMechanism.auto.description", ), }, { value: "NONE", label: t( "connection.modal.mongo.authMechanism.none.label", ), description: t( "connection.modal.mongo.authMechanism.none.description", ), }, { value: "SCRAM-SHA-1", label: "SCRAM-SHA-1", description: t( "connection.modal.mongo.authMechanism.scramSha1.description", ), }, { value: "SCRAM-SHA-256", label: "SCRAM-SHA-256", description: t( "connection.modal.mongo.authMechanism.scramSha256.description", ), }, { value: "MONGODB-AWS", label: "MONGODB-AWS", description: t( "connection.modal.mongo.authMechanism.aws.description", ), }, ], })}
)}
{dbType === "mongodb" && ( {t("connection.modal.field.savePassword")} )} ), })} {!isFileDb && !isRedis && !isKafka && renderConfigSectionCard({ sectionKey: "databaseScope", icon: , children: ( ), })} )} ); const networkSecuritySection = ( ); return (
{ if (testResult) { setTestResult(null); setTestErrorLogOpen(false); } if ( changed.uri !== undefined || changed.connectionParams !== undefined || changed.type !== undefined || changed.oceanBaseProtocol !== undefined ) { setUriFeedback(null); } if (changed.useSSL !== undefined) { setUseSSL(changed.useSSL); if (changed.useSSL) setActiveNetworkConfig("ssl"); } if (changed.useSSH !== undefined) { setUseSSH(changed.useSSH); if (changed.useSSH) setActiveNetworkConfig("ssh"); } if (changed.useProxy !== undefined) { const enabledProxy = !!changed.useProxy; setUseProxy(enabledProxy); if (enabledProxy) setActiveNetworkConfig("proxy"); if (enabledProxy && form.getFieldValue("useHttpTunnel")) { form.setFieldValue("useHttpTunnel", false); setUseHttpTunnel(false); } } if (changed.proxyType !== undefined) { const nextType = String( changed.proxyType || "socks5", ).toLowerCase(); if (nextType === "http") { const currentPort = Number(form.getFieldValue("proxyPort") || 0); if (!currentPort || currentPort === 1080) { form.setFieldValue("proxyPort", 8080); } } else { const currentPort = Number(form.getFieldValue("proxyPort") || 0); if (!currentPort || currentPort === 8080) { form.setFieldValue("proxyPort", 1080); } } } if (changed.useHttpTunnel !== undefined) { const enabledHttpTunnel = !!changed.useHttpTunnel; setUseHttpTunnel(enabledHttpTunnel); if (enabledHttpTunnel) setActiveNetworkConfig("httpTunnel"); if (enabledHttpTunnel && form.getFieldValue("useProxy")) { form.setFieldValue("useProxy", false); setUseProxy(false); } if (enabledHttpTunnel) { const currentPort = Number( form.getFieldValue("httpTunnelPort") || 0, ); if (!currentPort || currentPort <= 0) { form.setFieldValue("httpTunnelPort", 8080); } } } if (changed.type !== undefined) setDbType(changed.type); if (changed.jvmAllowedModes !== undefined) { const resolvedModes = normalizeEditableJVMModes( changed.jvmAllowedModes, ); const currentPreferredMode = String( form.getFieldValue("jvmPreferredMode") || "", ) .trim() .toLowerCase(); const resolvedPreferredMode = resolvedModes.find((mode) => mode === currentPreferredMode) || resolvedModes[0]; form.setFieldValue("jvmAllowedModes", resolvedModes); form.setFieldValue("jvmPreferredMode", resolvedPreferredMode); form.setFieldValue( "jvmEndpointEnabled", resolvedModes.includes("endpoint"), ); form.setFieldValue( "jvmAgentEnabled", resolvedModes.includes("agent"), ); } if (changed.redisTopology !== undefined) { const nextRedisTopology = String( changed.redisTopology || "single", ).toLowerCase(); const currentRedisPort = Number(form.getFieldValue("port") || 0); if ( nextRedisTopology === "sentinel" && (!currentRedisPort || currentRedisPort === 6379) ) { form.setFieldValue("port", 26379); } else if ( nextRedisTopology !== "sentinel" && currentRedisPort === 26379 ) { form.setFieldValue("port", 6379); } const supportedDbs = buildRedisDatabaseList( form.getFieldValue("redisDB"), form.getFieldValue("includeRedisDatabases"), ); setRedisDbList(supportedDbs); form.setFieldValue( "includeRedisDatabases", normalizeRedisDatabaseSelection( form.getFieldValue("includeRedisDatabases"), supportedDbs, ), ); } if ( changed.type !== undefined || changed.host !== undefined || changed.port !== undefined || changed.mongoHosts !== undefined || changed.mongoTopology !== undefined || changed.mongoSrv !== undefined ) { setMongoMembers([]); } }} > {currentDriverUnavailableReason && ( {currentDriverUnavailableReason} } /> )} {currentDriverUpdateReason && ( {currentDriverUpdateReason} } /> )} {(() => { const sectionItems: Array<{ key: "basic" | "network" | "appearance"; title: string; description: string; icon: React.ReactNode; }> = [ { key: "basic", title: t("connection.modal.config.basic.title"), description: isJVM ? t("connection.modal.config.basic.jvmNavDescription") : t("connection.modal.config.basic.navDescription"), icon: , }, ...(!isCustom && !isFileDb && !isJVM ? [ { key: "network" as const, title: t("connection.modal.network.title"), description: t( "connection.modal.network.navDescription", ), icon: , }, ] : []), { key: "appearance", title: t("connection.modal.appearance.title"), description: t("connection.modal.appearance.description"), icon: , }, ]; const resolvedSection = sectionItems.some( (item) => item.key === activeConfigSection, ) ? activeConfigSection : sectionItems[0]?.key || "basic"; const effectiveIconType = customIconType || dbType; const effectiveIconColor = customIconColor || getDbDefaultColor(effectiveIconType); const appearanceSection = (
{t("connection.modal.appearance.icon")}
{DB_ICON_TYPES.map((iconKey) => { const isActive = effectiveIconType === iconKey; return ( ); })}
{t("connection.modal.appearance.current", { name: getDbIconLabel(effectiveIconType), })}
{t("connection.modal.appearance.color")}
{PRESET_ICON_COLORS.map((presetColor) => { const isActive = effectiveIconColor === presetColor; return (
{t("connection.modal.appearance.preview")}
{getDbIcon(effectiveIconType, effectiveIconColor, 24)} {form.getFieldValue("name") || t("connection.modal.appearance.previewName")}
{(customIconType || customIconColor) && ( )}
); const currentSectionContent = resolvedSection === "basic" ? baseInfoSection : resolvedSection === "appearance" ? appearanceSection : networkSecuritySection; if (sectionItems.length <= 1) { return currentSectionContent; } return (
{t("connection.modal.config.sections")}
{sectionItems.map((item) => { const active = item.key === resolvedSection; return ( ); })}
{currentSectionContent}
); })()} ); }; return renderStep2(); }; export default ConnectionModalStep2;