- | Sin registros |
+ Sin registros |
@@ -186,6 +210,37 @@
+
+
Infraestructura (opcional)
+
+
+
+
+
+
+
+
+
Sin BD para este servidor
+
+
+
+
+
@@ -349,9 +404,10 @@ document.addEventListener('alpine:init', () => {
bienvenidaModal: false, bienvenidaReglaId: '', bienvenidaContratoId: null,
historialModal: false, historialTitulo: '', historialTimeline: [], historialLoading: false,
clientes: [], servicios: [], reglas: [], reglasBienvenida: [],
+ servidoresList: [], conxDbList: [], aiConfigList: [],
selectedId: null,
verificandoID: null,
- form: { cliente_id:'', servicio_ids:[], fecha_inicio:'', fecha_vencimiento:'', precio_acordado:0, moneda:'COP', estado:'activo', auto_renovar:false, notas:'' },
+ form: { cliente_id:'', servicio_ids:[], fecha_inicio:'', fecha_vencimiento:'', precio_acordado:0, moneda:'COP', estado:'activo', auto_renovar:false, notas:'', servidor_id:'', conx_db_id:'', ai_config_id:'' },
toast: { show: false, msg: '', type: 'ok' },
async init() {
@@ -369,10 +425,12 @@ document.addEventListener('alpine:init', () => {
async loadSelects() {
try {
- const [c, s, r] = await Promise.all([
+ const [c, s, r, sv, ai] = await Promise.all([
axios.get('/app/api/clientes/select'),
axios.get('/app/api/servicios/select'),
axios.get('/app/api/reglas-notificacion'),
+ axios.get('/app/loadservidorselect'),
+ axios.get('/app/api/ai-config/select'),
]);
this.clientes = Array.isArray(c.data) ? c.data : (c.data.registros || []);
this.servicios = Array.isArray(s.data) ? s.data : (s.data.registros || []);
@@ -380,11 +438,24 @@ document.addEventListener('alpine:init', () => {
this.reglas = todasReglas.filter(x => x.activo);
this.reglasBienvenida = todasReglas.filter(x => x.activo && x.tipo_evento === 'bienvenida');
if (this.reglasBienvenida.length > 0) this.bienvenidaReglaId = this.reglasBienvenida[0].ID;
+ this.servidoresList = sv.data.registros || [];
+ this.aiConfigList = ai.data.registros || [];
} catch(e) {
this.showToast('Error cargando listas: ' + (e.response?.data?.error || e.message), 'error');
}
},
+ async onServidorChange() {
+ this.form.conx_db_id = '';
+ if (!this.form.servidor_id) { this.conxDbList = []; return; }
+ try {
+ const res = await axios.get('/app/api/conxdb/select', { params: { servidor_id: this.form.servidor_id } });
+ this.conxDbList = res.data.registros || [];
+ } catch(e) {
+ this.conxDbList = [];
+ }
+ },
+
get precioSugerido() {
return this.servicios
.filter(s => (this.form.servicio_ids || []).includes(s.ID))
@@ -400,7 +471,7 @@ document.addEventListener('alpine:init', () => {
this.form.precio_acordado = this.precioSugerido;
},
- openEdit(d) {
+ async openEdit(d) {
this.form = {
cliente_id: d.cliente_id,
servicio_ids: (d.servicios || []).map(s => s.ID),
@@ -410,16 +481,24 @@ document.addEventListener('alpine:init', () => {
moneda: d.moneda || 'COP',
estado: d.estado,
auto_renovar: d.auto_renovar,
- notas: d.notas
+ notas: d.notas,
+ servidor_id: d.servidor_id || '',
+ conx_db_id: d.conx_db_id || '',
+ ai_config_id: d.ai_config_id || ''
};
this.selectedId = d.ID;
this.editModal = true;
+ // Cargar BD del servidor seleccionado
+ if (d.servidor_id) {
+ await this.onServidorChange();
+ }
},
openDelete(d) { this.selectedId = d.ID; this.deleteModal = true; },
closeModals() {
this.addModal = this.editModal = this.deleteModal = false;
this.selectedId = null;
- this.form = { cliente_id:'', servicio_ids:[], fecha_inicio:'', fecha_vencimiento:'', precio_acordado:0, moneda:'COP', estado:'activo', auto_renovar:false, notas:'' };
+ this.conxDbList = [];
+ this.form = { cliente_id:'', servicio_ids:[], fecha_inicio:'', fecha_vencimiento:'', precio_acordado:0, moneda:'COP', estado:'activo', auto_renovar:false, notas:'', servidor_id:'', conx_db_id:'', ai_config_id:'' };
},
async save() {
@@ -430,6 +509,9 @@ document.addEventListener('alpine:init', () => {
cliente_id: parseInt(this.form.cliente_id) || 0,
servicio_id: parseInt(this.form.servicio_id) || 0,
precio_acordado: parseFloat(this.form.precio_acordado) || 0,
+ servidor_id: this.form.servidor_id ? parseInt(this.form.servidor_id) : null,
+ conx_db_id: this.form.conx_db_id ? parseInt(this.form.conx_db_id) : null,
+ ai_config_id: this.form.ai_config_id ? parseInt(this.form.ai_config_id) : null,
};
if (this.editModal) {
await axios.put(`/app/api/contratos/${this.selectedId}`, payload);
diff --git a/rest/controllers/ai_config_controller.go b/rest/controllers/ai_config_controller.go
index 3d23495..36e0cc0 100644
--- a/rest/controllers/ai_config_controller.go
+++ b/rest/controllers/ai_config_controller.go
@@ -155,6 +155,15 @@ func UpdateAiConfigHandler(c *fiber.Ctx) error {
return c.JSON(fiber.Map{"ok": true})
}
+// GetAiConfigSelect devuelve lista simple para selects
+func GetAiConfigSelect(c *fiber.Ctx) error {
+ items, err := models.GetAiConfigSelect()
+ if err != nil {
+ return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{"error": err.Error()})
+ }
+ return c.JSON(fiber.Map{"registros": items})
+}
+
// DeleteAiConfigHandler elimina una configuración de IA.
func DeleteAiConfigHandler(c *fiber.Ctx) error {
id, err := strconv.ParseUint(c.Params("id"), 10, 64)
diff --git a/rest/controllers/contrato_controller.go b/rest/controllers/contrato_controller.go
index 0483b77..ea05070 100644
--- a/rest/controllers/contrato_controller.go
+++ b/rest/controllers/contrato_controller.go
@@ -62,13 +62,16 @@ func GetContratos(c *fiber.Ctx) error {
func CreateContrato(c *fiber.Ctx) error {
type Input struct {
- ClienteID uint `json:"cliente_id"`
- ServicioIDs []uint `json:"servicio_ids"`
- FechaInicio string `json:"fecha_inicio"`
- FechaVencimiento string `json:"fecha_vencimiento"`
+ ClienteID uint `json:"cliente_id"`
+ ServicioIDs []uint `json:"servicio_ids"`
+ FechaInicio string `json:"fecha_inicio"`
+ FechaVencimiento string `json:"fecha_vencimiento"`
PrecioAcordado float64 `json:"precio_acordado"`
- AutoRenovar bool `json:"auto_renovar"`
- Notas string `json:"notas"`
+ AutoRenovar bool `json:"auto_renovar"`
+ Notas string `json:"notas"`
+ ServidorID *uint `json:"servidor_id"`
+ ConxDbID *uint `json:"conx_db_id"`
+ AiConfigID *uint `json:"ai_config_id"`
}
var inp Input
if err := c.BodyParser(&inp); err != nil {
@@ -86,10 +89,13 @@ func CreateContrato(c *fiber.Ctx) error {
ClienteID: inp.ClienteID,
FechaInicio: fi,
FechaVencimiento: fv,
- PrecioAcordado: inp.PrecioAcordado,
+ PrecioAcordado: float64(inp.PrecioAcordado),
AutoRenovar: inp.AutoRenovar,
Notas: inp.Notas,
Estado: "activo",
+ ServidorID: inp.ServidorID,
+ ConxDbID: inp.ConxDbID,
+ AiConfigID: inp.AiConfigID,
}
if err := models.CreateContrato(m, inp.ServicioIDs); err != nil {
return c.Status(500).JSON(fiber.Map{"error": err.Error()})
@@ -115,10 +121,13 @@ func UpdateContrato(c *fiber.Ctx) error {
PrecioAcordado float64 `json:"precio_acordado"`
AutoRenovar bool `json:"auto_renovar"`
Notas string `json:"notas"`
+ ServidorID *uint `json:"servidor_id"`
+ ConxDbID *uint `json:"conx_db_id"`
+ AiConfigID *uint `json:"ai_config_id"`
}
var inp Input
if err := c.BodyParser(&inp); err != nil {
- return c.Status(400).JSON(fiber.Map{"error": err.Error()})
+ return c.Status(400).JSON(fiber.Map{"error": "ID inválido"})
}
existing, err := models.GetContratoByID(uint(id))
if err != nil {
@@ -136,6 +145,9 @@ func UpdateContrato(c *fiber.Ctx) error {
existing.PrecioAcordado = inp.PrecioAcordado
existing.AutoRenovar = inp.AutoRenovar
existing.Notas = inp.Notas
+ existing.ServidorID = inp.ServidorID
+ existing.ConxDbID = inp.ConxDbID
+ existing.AiConfigID = inp.AiConfigID
if err := models.UpdateContrato(*existing, inp.ServicioIDs); err != nil {
return c.Status(500).JSON(fiber.Map{"error": err.Error()})
}
diff --git a/rest/controllers/conx_db_controller.go b/rest/controllers/conx_db_controller.go
index 1910ddd..816d0f6 100755
--- a/rest/controllers/conx_db_controller.go
+++ b/rest/controllers/conx_db_controller.go
@@ -125,3 +125,12 @@ func DeleteConxDb(c *fiber.Ctx) error {
"message": "conx ssh eliminado exitosamente",
})
}
+
+func GetConxDbSelect(c *fiber.Ctx) error {
+ servidorID := c.Query("servidor_id")
+ items, err := models.GetConxDbSelect(servidorID)
+ if err != nil {
+ return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{"error": err.Error()})
+ }
+ return c.JSON(fiber.Map{"registros": items})
+}
diff --git a/rest/routes/user.go b/rest/routes/user.go
index 03954de..2f77cbe 100755
--- a/rest/routes/user.go
+++ b/rest/routes/user.go
@@ -65,6 +65,7 @@ func UserRoutes(app fiber.Router) {
// Rutas de conexiones db
protected.Get("/conexion_db", middlewares.MenuMiddleware, controllers.ConxDb) // Renderizar la vista
protected.Get("/loadconexiondb", controllers.GetConxDb) // Obtener
+ protected.Get("/api/conxdb/select", controllers.GetConxDbSelect) // Select (filtrado por ?servidor_id=)
protected.Post("/conexiondb", controllers.CreateConxDb) // Crear
protected.Put("/conexiondb/:id", controllers.UpdateConxDb) // Actualizar
protected.Delete("/conexiondb/:id", controllers.DeleteConxDb) // Eliminar
@@ -284,6 +285,7 @@ func UserRoutes(app fiber.Router) {
// ─── Configuraciones de IA (Qwen, OpenAI, etc.) ─────────────────────────
protected.Get("/ai-config", middlewares.MenuMiddleware, controllers.AiConfigIndex)
protected.Get("/ai-config/list", controllers.GetAiConfigs)
+ protected.Get("/api/ai-config/select", controllers.GetAiConfigSelect)
protected.Post("/ai-config", controllers.CreateAiConfigHandler)
protected.Put("/ai-config/:id", controllers.UpdateAiConfigHandler)
protected.Delete("/ai-config/:id", controllers.DeleteAiConfigHandler)