Add React frontend and Sinbad2IA LLM integration.
Introduce a full Vite/React UI for exams, auth, materials, images, generation, and export. Adapt backend for Sinbad2IA chat API, bcrypt passwords, CORS on port 5173, and schema migrations.
This commit is contained in:
@@ -0,0 +1,79 @@
|
||||
import { useEffect } from "react";
|
||||
import { createPortal } from "react-dom";
|
||||
import Button from "./Button";
|
||||
import Icon from "./Icon";
|
||||
|
||||
export default function Modal({ open, onClose, title, children, footer, large }) {
|
||||
useEffect(() => {
|
||||
if (!open) return;
|
||||
const onKey = (e) => e.key === "Escape" && onClose?.();
|
||||
window.addEventListener("keydown", onKey);
|
||||
const prevOverflow = document.body.style.overflow;
|
||||
document.body.style.overflow = "hidden";
|
||||
return () => {
|
||||
window.removeEventListener("keydown", onKey);
|
||||
document.body.style.overflow = prevOverflow;
|
||||
};
|
||||
}, [open, onClose]);
|
||||
|
||||
if (!open) return null;
|
||||
|
||||
return createPortal(
|
||||
<div className="modal-overlay" onMouseDown={onClose}>
|
||||
<div
|
||||
className={`modal ${large ? "modal-lg" : ""}`}
|
||||
onMouseDown={(e) => e.stopPropagation()}
|
||||
>
|
||||
{title && (
|
||||
<div className="modal-head">
|
||||
<h3>{title}</h3>
|
||||
<span style={{ flex: 1 }} />
|
||||
<button className="toast-close" type="button" aria-label="Cerrar" onClick={onClose}>
|
||||
<Icon name="close" size={16} />
|
||||
</button>
|
||||
</div>
|
||||
)}
|
||||
<div className="modal-body">{children}</div>
|
||||
{footer && <div className="modal-foot">{footer}</div>}
|
||||
</div>
|
||||
</div>,
|
||||
document.body
|
||||
);
|
||||
}
|
||||
|
||||
export function ConfirmDialog({
|
||||
open,
|
||||
title = "¿Confirmar?",
|
||||
message,
|
||||
confirmLabel = "Confirmar",
|
||||
danger,
|
||||
loading,
|
||||
onConfirm,
|
||||
onClose,
|
||||
}) {
|
||||
return (
|
||||
<Modal
|
||||
open={open}
|
||||
onClose={onClose}
|
||||
title={title}
|
||||
footer={
|
||||
<>
|
||||
<Button variant="ghost" onClick={onClose} disabled={loading}>
|
||||
Cancelar
|
||||
</Button>
|
||||
<Button
|
||||
variant={danger ? "danger" : "primary"}
|
||||
onClick={onConfirm}
|
||||
loading={loading}
|
||||
>
|
||||
{confirmLabel}
|
||||
</Button>
|
||||
</>
|
||||
}
|
||||
>
|
||||
<p className="text-soft" style={{ margin: 0 }}>
|
||||
{message}
|
||||
</p>
|
||||
</Modal>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user