import React from "react"; import { FiServer, FiGlobe, FiKey, FiLock, FiPlus, FiTrash2, FiSettings, FiLink, } from "react-icons/fi"; import { SiDocker } from "react-icons/si"; import { FiPackage, FiUploadCloud } from "react-icons/fi"; type DeployType = "docker" | "binary" | "deploy"; type AuthMethod = "key" | "password"; interface ExtraField { key: string; value: string; } export interface SSHAgentConfig { agentLabel: string; user: string; ip: string; port: number; authMethod: AuthMethod; sshKey?: string; password?: string; extraFields: ExtraField[]; deployType: DeployType; } interface SSHAgentFormProps { index: number; config: SSHAgentConfig; onChange: (index: number, config: SSHAgentConfig) => void; onRemove: (index: number) => void; canRemove: boolean; } const DEPLOY_OPTIONS: { value: DeployType; label: string; icon: React.ReactNode; }[] = [ { value: "docker", label: "Docker", icon: }, { value: "binary", label: "Binary", icon: }, ]; const inputBaseStyle: React.CSSProperties = { width: "100%", padding: "10px 12px", border: "1px solid var(--border)", borderRadius: "8px", backgroundColor: "var(--input-bg)", color: "var(--text-primary)", fontSize: "14px", transition: "border-color 0.2s, box-shadow 0.2s", }; const labelStyle: React.CSSProperties = { display: "block", marginBottom: "8px", color: "var(--text-secondary)", fontSize: "14px", fontWeight: 500, }; export const SSHAgentForm: React.FC = ({ index, config, onChange, onRemove, canRemove, }) => { const handleChange = (field: keyof SSHAgentConfig, value: unknown) => { onChange(index, { ...config, [field]: value }); }; const handleExtraFieldChange = ( fieldIndex: number, field: keyof ExtraField, value: string, ) => { const newExtraFields = [...config.extraFields]; newExtraFields[fieldIndex] = { ...newExtraFields[fieldIndex], [field]: value, }; handleChange("extraFields", newExtraFields); }; const addExtraField = () => { handleChange("extraFields", [ ...config.extraFields, { key: "", value: "" }, ]); }; const removeExtraField = (fieldIndex: number) => { const newExtraFields = config.extraFields.filter( (_, i) => i !== fieldIndex, ); handleChange("extraFields", newExtraFields); }; const handleFocus = ( e: React.FocusEvent< HTMLInputElement | HTMLTextAreaElement | HTMLSelectElement >, ) => { e.currentTarget.style.borderColor = "var(--border-focus)"; e.currentTarget.style.boxShadow = `0 0 0 3px var(--border-focus)30`; }; const handleBlur = ( e: React.FocusEvent< HTMLInputElement | HTMLTextAreaElement | HTMLSelectElement >, ) => { e.currentTarget.style.borderColor = "var(--border)"; e.currentTarget.style.boxShadow = "none"; }; return ( {/* Header */} SSH сервер #{index + 1} {canRemove && ( onRemove(index)} className="flex items-center gap-2 px-4 py-2 rounded-lg transition-all" style={{ background: "var(--error-bg)", color: "var(--error-text)", border: "1px solid var(--error-border)", cursor: "pointer", fontSize: "14px", fontWeight: 500, }} onMouseEnter={(e) => { e.currentTarget.style.background = "var(--error-text)"; e.currentTarget.style.color = "#fff"; }} onMouseLeave={(e) => { e.currentTarget.style.background = "var(--error-bg)"; e.currentTarget.style.color = "var(--error-text)"; }} > Удалить )} {/* Agent Label */} Метка агента * handleChange("agentLabel", e.target.value)} required style={inputBaseStyle} onFocus={handleFocus} onBlur={handleBlur} placeholder="production-server-1" /> {/* User, IP и Port */} Пользователь * handleChange("user", e.target.value)} required style={inputBaseStyle} onFocus={handleFocus} onBlur={handleBlur} placeholder="username" /> IP адрес * handleChange("ip", e.target.value)} required style={inputBaseStyle} onFocus={handleFocus} onBlur={handleBlur} placeholder="192.168.1.1" /> Порт * handleChange("port", parseInt(e.target.value) || 22) } required min={1} max={65535} style={inputBaseStyle} onFocus={handleFocus} onBlur={handleBlur} placeholder="22" /> {/* Метод аутентификации */} Метод аутентификации * {(["key", "password"] as const).map((method) => ( handleChange("authMethod", method)} className="flex-1 py-2.5 px-4 rounded-lg border transition-all font-medium" style={{ backgroundColor: config.authMethod === method ? "var(--accent)" : "var(--input-bg)", color: config.authMethod === method ? "var(--accent-text)" : "var(--text-primary)", borderColor: config.authMethod === method ? "var(--accent)" : "var(--border)", cursor: "pointer", fontSize: "14px", }} > {method === "key" ? "SSH ключ" : "Пароль"} ))} {/* SSH Key или Password */} {config.authMethod === "key" ? ( SSH ключ * handleChange("sshKey", e.target.value)} required rows={4} style={{ ...inputBaseStyle, fontFamily: "ui-monospace, SFMono-Regular, monospace", resize: "vertical", }} onFocus={handleFocus} onBlur={handleBlur} placeholder="-----BEGIN OPENSSH PRIVATE KEY-----" /> ) : ( Пароль * handleChange("password", e.target.value)} required style={inputBaseStyle} onFocus={handleFocus} onBlur={handleBlur} placeholder="••••••••" /> )} {/* Дополнительные поля */} Дополнительные параметры { e.currentTarget.style.opacity = "0.85"; }} onMouseLeave={(e) => { e.currentTarget.style.opacity = "1"; }} > Добавить {config.extraFields.length === 0 && ( Нет дополнительных параметров )} {config.extraFields.map((extra, fieldIndex) => ( handleExtraFieldChange(fieldIndex, "key", e.target.value) } style={inputBaseStyle} onFocus={handleFocus} onBlur={handleBlur} placeholder="Параметр" /> handleExtraFieldChange(fieldIndex, "value", e.target.value) } style={inputBaseStyle} onFocus={handleFocus} onBlur={handleBlur} placeholder="Значение" /> removeExtraField(fieldIndex)} className="flex items-center justify-center rounded-lg border transition-all" style={{ background: "var(--error-bg)", color: "var(--error-text)", borderColor: "var(--error-border)", cursor: "pointer", fontSize: "18px", padding: "8px 12px", }} onMouseEnter={(e) => { e.currentTarget.style.background = "var(--error-text)"; e.currentTarget.style.color = "#fff"; }} onMouseLeave={(e) => { e.currentTarget.style.background = "var(--error-bg)"; e.currentTarget.style.color = "var(--error-text)"; }} > × ))} {/* Тип развертывания */} Тип развертывания * {DEPLOY_OPTIONS.map((option) => ( handleChange("deployType", option.value)} className="flex items-center justify-center gap-2 py-3 px-4 rounded-lg border transition-all font-medium" style={{ backgroundColor: config.deployType === option.value ? "var(--accent)" : "var(--input-bg)", color: config.deployType === option.value ? "var(--accent-text)" : "var(--text-primary)", borderColor: config.deployType === option.value ? "var(--accent)" : "var(--border)", cursor: "pointer", fontSize: "14px", }} > {option.icon} {option.label} ))} ); };
Нет дополнительных параметров