/* Tab: Accesos compartidos — gestor de credenciales cifradas. Las contraseñas se guardan cifradas en el servidor (Fernet) y solo se descargan bajo petición explícita (botón "ver"). Nunca viajan en el listado. */ const SHARED_FIELD_MAX = 4096; function TagInput({ tags, onChange }) { const [draft, setDraft] = React.useState(""); function commit() { const t = draft.trim(); if (t && !tags.includes(t) && tags.length < 20) onChange([...tags, t]); setDraft(""); } return (
{tags.map((t) => ( {t} ))} setDraft(e.target.value)} onKeyDown={(e) => { if (e.key === "Enter" || e.key === ",") { e.preventDefault(); commit(); } else if (e.key === "Backspace" && !draft && tags.length) onChange(tags.slice(0, -1)); }} onBlur={commit} placeholder={tags.length ? "" : "Añade etiquetas y pulsa Enter"} style={{ flex: 1, minWidth: 120, border: "none", outline: "none", background: "transparent", color: "var(--text)", fontSize: 13 }} />
); } function SharedAccessModal({ initial, onClose, onSaved }) { const editing = !!initial; const [appName, setAppName] = React.useState(initial?.app_name || ""); const [account, setAccount] = React.useState(initial?.account || ""); const [password, setPassword] = React.useState(""); const [passwordTouched, setPasswordTouched] = React.useState(!editing); const [showPw, setShowPw] = React.useState(false); const [tags, setTags] = React.useState(initial?.tags || []); const [notes, setNotes] = React.useState(initial?.notes || ""); const [loading, setLoading] = React.useState(false); const [error, setError] = React.useState(""); async function handleSubmit(e) { e.preventDefault(); if (!appName.trim()) { setError("La aplicación es obligatoria"); return; } setLoading(true); setError(""); const payload = { app_name: appName.trim(), account: account.trim(), tags, notes: notes.trim(), }; // Solo mandamos password si el usuario lo escribió/cambió. if (passwordTouched) payload.password = password; try { const url = editing ? `/api/shared-accesses/${initial.id}` : "/api/shared-accesses"; const r = await fetch(url, { method: editing ? "PUT" : "POST", credentials: "same-origin", headers: { "Content-Type": "application/json" }, body: JSON.stringify(payload), }); const d = await r.json(); if (r.ok && d.success) { onSaved(d.access); onClose(); } else setError(d.detail || "No se pudo guardar"); } catch { setError("Error de conexión"); } finally { setLoading(false); } } return (
e.stopPropagation()}>

{editing ? "Editar acceso" : "Nuevo acceso"}

setAppName(e.target.value)} placeholder="Google Ads, Meta Business…" autoFocus/>
setAccount(e.target.value)} placeholder="equipo@empresa.com" autoComplete="off"/>
{editing && !passwordTouched ? ( ) : (
setPassword(e.target.value)} placeholder="••••••••" style={{ paddingRight: 38 }}/>
)} {editing && passwordTouched && (
Déjalo vacío para eliminar la contraseña guardada.
)}