import { useState, useEffect, useRef, useCallback } from "react"; import type { PortMapping } from "../../lib/types"; interface Props { portMappings: PortMapping[]; disabled: boolean; onSave: (mappings: PortMapping[]) => Promise; onClose: () => void; } export default function PortMappingsModal({ portMappings: initial, disabled, onSave, onClose }: Props) { const [mappings, setMappings] = useState(initial); const overlayRef = useRef(null); useEffect(() => { const handleKeyDown = (e: KeyboardEvent) => { if (e.key === "Escape") onClose(); }; document.addEventListener("keydown", handleKeyDown); return () => document.removeEventListener("keydown", handleKeyDown); }, [onClose]); const handleOverlayClick = useCallback( (e: React.MouseEvent) => { if (e.target === overlayRef.current) onClose(); }, [onClose], ); const updatePort = (index: number, field: "host_port" | "container_port", value: string) => { const updated = [...mappings]; const num = parseInt(value, 10); updated[index] = { ...updated[index], [field]: isNaN(num) ? 0 : num }; setMappings(updated); }; const updateProtocol = (index: number, value: string) => { const updated = [...mappings]; updated[index] = { ...updated[index], protocol: value }; setMappings(updated); }; const removeMapping = async (index: number) => { const updated = mappings.filter((_, i) => i !== index); setMappings(updated); try { await onSave(updated); } catch (err) { console.error("Failed to remove port mapping:", err); } }; const addMapping = async () => { const updated = [...mappings, { host_port: 0, container_port: 0, protocol: "tcp" }]; setMappings(updated); try { await onSave(updated); } catch (err) { console.error("Failed to add port mapping:", err); } }; const handleBlur = async () => { try { await onSave(mappings); } catch (err) { console.error("Failed to update port mappings:", err); } }; return (

Port Mappings

Map host ports to container ports. Services can be started after the container is running.

{disabled && (
Container must be stopped to change port mappings.
)}
{mappings.length === 0 && (

No port mappings configured.

)} {mappings.length > 0 && (
Host Port Container Port Protocol
)} {mappings.map((pm, i) => (
updatePort(i, "host_port", e.target.value)} onBlur={handleBlur} placeholder="8080" disabled={disabled} className="w-[30%] px-2 py-1.5 bg-[var(--bg-primary)] border border-[var(--border-color)] rounded text-sm text-[var(--text-primary)] focus:outline-none focus:border-[var(--accent)] disabled:opacity-50 font-mono" /> updatePort(i, "container_port", e.target.value)} onBlur={handleBlur} placeholder="8080" disabled={disabled} className="w-[30%] px-2 py-1.5 bg-[var(--bg-primary)] border border-[var(--border-color)] rounded text-sm text-[var(--text-primary)] focus:outline-none focus:border-[var(--accent)] disabled:opacity-50 font-mono" />
))}
); }