- ProvServidor model: add Integraciones []Submodules (many2many:prov_servidor_submodules) - GetAllProvServidor: preload Integraciones - SetProvServidorIntegraciones: new model func to replace associations - New endpoint PUT /app/provservidor/:id/integraciones - GetSubmodules: support ?limit= query param - prov_servidor.html: show integrations as badges in table, view modal, and edit modal with checkboxes to select one or more existing submodules Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
752 lines
24 KiB
Go
Executable File
752 lines
24 KiB
Go
Executable File
package controllers
|
|
|
|
import (
|
|
"fmt"
|
|
"math"
|
|
"os"
|
|
"strconv"
|
|
"strings"
|
|
|
|
"github.com/gofiber/fiber/v2" //nolint:goimports
|
|
"github.com/sujit-baniya/fiber-boilerplate/pkg/models"
|
|
)
|
|
|
|
// RENDER
|
|
func Submodules(c *fiber.Ctx) error {
|
|
data := fiber.Map{
|
|
"user": c.Locals("user").(map[string]interface{}),
|
|
"modules": c.Locals("userModules"),
|
|
}
|
|
|
|
// Renderiza la vista "modules" con el layout "layouts/landing"
|
|
if err := c.Render("submodules", data, "layouts/main"); err != nil {
|
|
return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{
|
|
"message": "Error rendering template",
|
|
"error": err.Error(),
|
|
})
|
|
}
|
|
return nil
|
|
}
|
|
|
|
//CONSULTAR TODOS LOS SUBMODULOS
|
|
|
|
func GetSubmodules(c *fiber.Ctx) error {
|
|
// Obtener parámetros de consulta para la paginación
|
|
pageStr := c.Query("page", "1") // Por defecto, la página es 1
|
|
page, err := strconv.Atoi(pageStr)
|
|
if err != nil || page < 1 {
|
|
page = 1
|
|
}
|
|
|
|
searchQuery := c.Query("search", "") // Obtener el término de búsqueda
|
|
limitStr := c.Query("limit", "10")
|
|
limit, _ := strconv.Atoi(limitStr)
|
|
if limit <= 0 {
|
|
limit = 10
|
|
}
|
|
offset := (page - 1) * limit
|
|
|
|
// Llama a AllModules con el término de búsqueda
|
|
submodules, total, err := models.AllSubmodules(limit, offset, searchQuery)
|
|
if err != nil {
|
|
return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{
|
|
"message": "Error retrieving modules",
|
|
"error": err.Error(),
|
|
})
|
|
}
|
|
|
|
// Llama a AllRoles sin el término de búsqueda para obtener todos los roles
|
|
modules, err := models.AllModulesSelect() // Pasamos 0 para limit y offset para obtener todos los roles
|
|
if err != nil {
|
|
return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{
|
|
"message": "Error retrieving roles",
|
|
"error": err.Error(),
|
|
})
|
|
}
|
|
|
|
// Calcular el total de páginas
|
|
totalPages := int(math.Ceil(float64(total) / float64(limit)))
|
|
|
|
// Envía los módulos como respuesta JSON junto con la información de paginación
|
|
return c.JSON(fiber.Map{
|
|
"submodules": submodules,
|
|
"modules": modules,
|
|
"total": total, // Total de módulos disponibles
|
|
"totalPages": totalPages, // Total de páginas
|
|
"page": page, // Página actual
|
|
"limit": limit, // Límites por página
|
|
})
|
|
}
|
|
|
|
// ACTUALIZAR
|
|
func UpdateSubmodule(c *fiber.Ctx) error {
|
|
var m models.Submodules
|
|
uid, err := strconv.ParseUint(c.Params("id"), 10, 32)
|
|
if err != nil {
|
|
return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{
|
|
"message": "ID inválido",
|
|
"error": true,
|
|
})
|
|
}
|
|
|
|
// Analiza el cuerpo de la solicitud
|
|
if err := c.BodyParser(&m); err != nil {
|
|
return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{
|
|
"message": "Error al analizar el cuerpo de la solicitud",
|
|
"error": true,
|
|
})
|
|
}
|
|
|
|
m.ID = uint(uid)
|
|
|
|
// Actualiza el módulo en la base de datos
|
|
if err := models.UpdateSubmodule(m); err != nil {
|
|
return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{
|
|
"message": err.Error(),
|
|
"error": true,
|
|
})
|
|
}
|
|
|
|
return c.Status(fiber.StatusOK).JSON(fiber.Map{
|
|
"message": "Registro actualizado con éxito",
|
|
"error": false,
|
|
"submodule": m,
|
|
})
|
|
}
|
|
|
|
// CREAR SUBMODULO
|
|
func CreateSubmodule(c *fiber.Ctx) error {
|
|
var m models.Submodules
|
|
|
|
// Analiza el cuerpo de la solicitud
|
|
if err := c.BodyParser(&m); err != nil {
|
|
return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{
|
|
"message": "Error al analizar el cuerpo de la solicitud",
|
|
"error": true,
|
|
})
|
|
}
|
|
|
|
// Crea el nuevo módulo en la base de datos
|
|
if err := models.CreateSubmodule(m); err != nil {
|
|
return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{
|
|
"message": err.Error(),
|
|
"error": true,
|
|
})
|
|
}
|
|
|
|
// Crear el archivo del modelo basado en el nombre del submódulo (title)
|
|
title := m.Title
|
|
fileName := fmt.Sprintf("C:/laragon/www/gases/fiber-boilerplate/pkg/models/%s.go", strings.ToLower(title))
|
|
controllerFileName := fmt.Sprintf("C:/laragon/www/gases/fiber-boilerplate/rest/controllers/%s_controller.go", strings.ToLower(title))
|
|
routeFileName := "C:/laragon/www/gases/fiber-boilerplate/rest/routes/user.go"
|
|
viewFileName := fmt.Sprintf("C:/laragon/www/gases/fiber-boilerplate/resources/views/%s.html", strings.ToLower(title)) // Ruta del archivo de vista
|
|
|
|
// Generar el contenido del archivo
|
|
content := generateModelFileContent(title)
|
|
|
|
// Crear y escribir el archivo de modelo
|
|
if err := os.WriteFile(fileName, []byte(content), 0644); err != nil {
|
|
fmt.Printf("Error al crear el archivo de modelo: %s\n", err)
|
|
return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{
|
|
"message": "Error al crear el archivo de modelo",
|
|
"error": true,
|
|
})
|
|
}
|
|
fmt.Printf("Archivo de modelo creado: %s\n", fileName)
|
|
|
|
// Generar el contenido del archivo de controlador
|
|
controllerContent := generateControllerFileContent(title)
|
|
|
|
// Crear y escribir el archivo de controlador
|
|
if err := os.WriteFile(controllerFileName, []byte(controllerContent), 0644); err != nil {
|
|
fmt.Printf("Error al crear el archivo de controlador: %s\n", err)
|
|
return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{
|
|
"message": "Error al crear el archivo de controlador",
|
|
"error": true,
|
|
})
|
|
}
|
|
fmt.Printf("Archivo de controlador creado: %s\n", controllerFileName)
|
|
|
|
// Generar el contenido del archivo de vista
|
|
viewContent := generateViewFileContent(title)
|
|
|
|
// Crear y escribir el archivo de vista
|
|
if err := os.WriteFile(viewFileName, []byte(viewContent), 0644); err != nil {
|
|
fmt.Printf("Error al crear el archivo de vista: %s\n", err)
|
|
return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{
|
|
"message": "Error al crear el archivo de vista",
|
|
"error": true,
|
|
})
|
|
}
|
|
fmt.Printf("Archivo de vista creado: %s\n", viewFileName)
|
|
|
|
// Añadir las rutas al archivo user.go
|
|
routeContent := generateRouteFileContent(title)
|
|
|
|
// Abrir el archivo user.go en modo append y añadir las nuevas rutas
|
|
if err := appendToFile(routeFileName, routeContent); err != nil {
|
|
fmt.Printf("Error al añadir las rutas al archivo user.go: %s\n", err)
|
|
return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{
|
|
"message": "Error al añadir las rutas al archivo user.go",
|
|
"error": true,
|
|
})
|
|
}
|
|
fmt.Printf("Rutas añadidas al archivo user.go: %s\n", routeFileName)
|
|
|
|
return c.Status(fiber.StatusCreated).JSON(fiber.Map{
|
|
"message": "Registro creado con éxito",
|
|
"error": false,
|
|
"submodule": m,
|
|
})
|
|
}
|
|
|
|
// Función auxiliar para pluralizar el nombre si es necesario
|
|
func pluralize(word string) string {
|
|
// Simple heuristic: if the word ends in "s", don't pluralize
|
|
if strings.HasSuffix(word, "s") {
|
|
return word
|
|
}
|
|
return word + "s"
|
|
}
|
|
|
|
// Función que genera el contenido del archivo del modelo
|
|
func generateModelFileContent(title string) string {
|
|
pluralTitle := pluralize(title)
|
|
return fmt.Sprintf(`package models
|
|
|
|
import (
|
|
"time"
|
|
|
|
"gorm.io/gorm"
|
|
"github.com/sujit-baniya/fiber-boilerplate/app"
|
|
)
|
|
|
|
type %s struct {
|
|
gorm.Model // Embedding gorm.Model to inherit fields like ID, CreatedAt, UpdatedAt, DeletedAt
|
|
ID uint gorm:"primarykey"
|
|
ModifiedAt time.Time json:"modified_at" gorm:"column:modified_at"
|
|
Description string json:"description" gorm:"column:description"
|
|
Title string json:"title" gorm:"column:title"
|
|
}
|
|
|
|
// TableName overrides the table name used by %s to %s
|
|
func (%s) TableName() string {
|
|
return "%s"
|
|
}
|
|
|
|
// All%s recupera todos los registros con búsqueda
|
|
func All%s(limit, offset int, search string) ([]%s, int64, error) {
|
|
var items []%s
|
|
var total int64
|
|
db := app.Http.Database.DB.Model(&%s{})
|
|
|
|
// Filtrar por término de búsqueda si se proporciona
|
|
if search != "" {
|
|
db = db.Where("title LIKE ? OR description LIKE ?", "%%"+search+"%%", "%%"+search+"%%")
|
|
}
|
|
|
|
// Obtener el total de registros
|
|
if err := db.Count(&total).Error; err != nil {
|
|
return nil, 0, err
|
|
}
|
|
|
|
// Obtener los registros con paginación
|
|
if err := db.Order("created_at DESC").Limit(limit).Offset(offset).Find(&items).Error; err != nil {
|
|
return nil, 0, err
|
|
}
|
|
|
|
return items, total, nil
|
|
}
|
|
|
|
// All%sSelect recupera todos los registros sin paginación
|
|
func All%sSelect() ([]%s, error) {
|
|
var items []%s
|
|
|
|
// Realizar la consulta para obtener todos los registros
|
|
if err := app.Http.Database.DB.Model(&%s{}).Find(&items).Error; err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
return items, nil
|
|
}
|
|
|
|
// Create%s creates a new record
|
|
func Create%s(item %s) error {
|
|
if err := app.Http.Database.DB.Create(&item).Error; err != nil {
|
|
return err
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// Update%s updates an existing record
|
|
func Update%s(item %s) error {
|
|
if err := app.Http.Database.DB.Save(&item).Error; err != nil {
|
|
return err
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// Delete%s deletes a record by its ID
|
|
func Delete%s (id uint) error {
|
|
if err := app.Http.Database.DB.Delete(&%s{}, id).Error; err != nil {
|
|
return err
|
|
}
|
|
return nil
|
|
}
|
|
`,
|
|
title, // Nombre de la estructura (singular)
|
|
title, title, // TableName usa el nombre en singular para la estructura y la función
|
|
title, pluralTitle, // Nombre de la tabla (en plural)
|
|
pluralTitle, pluralTitle, title, // Función All para paginación, plural y singular
|
|
title, title, // Modelo usado en la consulta
|
|
pluralTitle, pluralTitle, title, // AllSelect sin paginación
|
|
title, title, title, // Crear, actualizar, borrar registros
|
|
title, title, title, title) // Funciones de creación, actualización y eliminación
|
|
}
|
|
|
|
// Función para generar el contenido del archivo de controlador
|
|
func generateControllerFileContent(title string) string {
|
|
lowerTitle := strings.ToLower(title)
|
|
|
|
// Contenido del controlador reemplazando "Title" con el nombre del submódulo
|
|
return fmt.Sprintf(`package controllers
|
|
|
|
import (
|
|
"github.com/gofiber/fiber/v2"
|
|
"github.com/sujit-baniya/fiber-boilerplate/pkg/models"
|
|
"strconv"
|
|
"math"
|
|
)
|
|
|
|
|
|
// RENDER
|
|
func %s(c *fiber.Ctx) error {
|
|
// Renderiza la vista con el layout layouts/main
|
|
if err := c.Render("%s", nil, "layouts/main"); err != nil {
|
|
return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{
|
|
"message": "Error rendering template",
|
|
"error": err.Error(),
|
|
})
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// CONSULTAR TODOS LOS REGISTROS
|
|
func Get%s(c *fiber.Ctx) error {
|
|
// Obtener parámetros de consulta para la paginación
|
|
pageStr := c.Query("page", "1") // Por defecto, la página es 1
|
|
page, err := strconv.Atoi(pageStr)
|
|
if err != nil || page < 1 {
|
|
page = 1
|
|
}
|
|
|
|
searchQuery := c.Query("search", "") // Obtener el término de búsqueda
|
|
limit := 10 // Número de registros por página
|
|
offset := (page - 1) * limit
|
|
|
|
// Llama a AllModules con el término de búsqueda
|
|
records, total, err := models.All%s(limit, offset, searchQuery)
|
|
if err != nil {
|
|
return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{
|
|
"message": "Error retrieving records",
|
|
"error": err.Error(),
|
|
})
|
|
}
|
|
|
|
// Calcular el total de páginas
|
|
totalPages := int(math.Ceil(float64(total) / float64(limit)))
|
|
|
|
// Envía los registros como respuesta JSON junto con la información de paginación
|
|
return c.JSON(fiber.Map{
|
|
"registros": records,
|
|
"total": total, // Total de registros disponibles
|
|
"totalPages": totalPages, // Total de páginas
|
|
"page": page, // Página actual
|
|
"limit": limit, // Límites por página
|
|
})
|
|
}
|
|
|
|
// CREAR
|
|
func Create%s(c *fiber.Ctx) error {
|
|
var m models.%s
|
|
|
|
// Analiza el cuerpo de la solicitud
|
|
if err := c.BodyParser(&m); err != nil {
|
|
return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{
|
|
"message": "Error al analizar el cuerpo de la solicitud",
|
|
"error": true,
|
|
})
|
|
}
|
|
|
|
// Crea el nuevo registro en la base de datos
|
|
if err := models.Create%s(m); err != nil {
|
|
return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{
|
|
"message": err.Error(),
|
|
"error": true,
|
|
})
|
|
}
|
|
|
|
return c.Status(fiber.StatusCreated).JSON(fiber.Map{
|
|
"message": "Registro creado con éxito",
|
|
"error": false,
|
|
"registro": m,
|
|
})
|
|
}
|
|
|
|
// ACTUALIZAR
|
|
func Update%s(c *fiber.Ctx) error {
|
|
var m models.%s
|
|
uid, err := strconv.ParseUint(c.Params("id"), 10, 32)
|
|
if err != nil {
|
|
return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{
|
|
"message": "ID inválido",
|
|
"error": true,
|
|
})
|
|
}
|
|
|
|
// Analiza el cuerpo de la solicitud
|
|
if err := c.BodyParser(&m); err != nil {
|
|
return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{
|
|
"message": "Error al analizar el cuerpo de la solicitud",
|
|
"error": true,
|
|
})
|
|
}
|
|
|
|
m.ID = uint(uid)
|
|
|
|
// Actualiza el módulo en la base de datos
|
|
if err := models.Update%s(m); err != nil {
|
|
return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{
|
|
"message": err.Error(),
|
|
"error": true,
|
|
})
|
|
}
|
|
|
|
return c.Status(fiber.StatusOK).JSON(fiber.Map{
|
|
"message": "Registro actualizado con éxito",
|
|
"error": false,
|
|
"registro": m,
|
|
})
|
|
}
|
|
|
|
// Delete%s elimina un %s por su ID
|
|
func Delete%s(c *fiber.Ctx) error {
|
|
uid, err := strconv.ParseUint(c.Params("id"), 10, 32)
|
|
if err != nil {
|
|
return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{
|
|
"message": "ID inválido",
|
|
"error": true,
|
|
})
|
|
}
|
|
|
|
// Lógica para eliminar %s
|
|
if err := models.Delete%s(uint(uid)); err != nil {
|
|
return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{
|
|
"message": err.Error(),
|
|
"error": true,
|
|
})
|
|
}
|
|
|
|
return c.Status(fiber.StatusOK).JSON(fiber.Map{
|
|
"message": "%s eliminado con éxito",
|
|
"error": false,
|
|
})
|
|
}
|
|
`, title, lowerTitle, title, title, title, title, title, title, title, lowerTitle, title, title, lowerTitle, title)
|
|
}
|
|
|
|
// Función para generar el contenido de rutas para el nuevo submódulo
|
|
func generateRouteFileContent(title string) string {
|
|
lowerTitle := strings.ToLower(title)
|
|
|
|
// Bloque de rutas personalizado
|
|
return fmt.Sprintf(`
|
|
// Rutas para %s
|
|
account.Get("/%s", controllers.Get%s) // Renderizar la vista de %s
|
|
account.Get("/load%s", controllers.Get%s) // Obtener todos los %s
|
|
account.Post("/%s", controllers.Create%s) // Crear un nuevo %s
|
|
account.Put("/%s/:id", controllers.Update%s) // Actualizar un %s existente
|
|
account.Delete("/%s/:id", controllers.Delete%s) // Eliminar un %s
|
|
`, lowerTitle, lowerTitle, title, lowerTitle, lowerTitle, title, lowerTitle, lowerTitle, title, lowerTitle, lowerTitle, title, lowerTitle, lowerTitle, title)
|
|
}
|
|
|
|
// Función para añadir contenido a un archivo existente
|
|
func appendToFile(fileName, content string) error {
|
|
f, err := os.OpenFile(fileName, os.O_APPEND|os.O_WRONLY, 0644)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
defer f.Close()
|
|
|
|
if _, err := f.WriteString(content); err != nil {
|
|
return err
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
// generateViewFileContent genera el contenido del archivo de vista para un submódulo
|
|
func generateViewFileContent(title string) string {
|
|
return fmt.Sprintf(`
|
|
<div x-data="app" class="bg-white rounded-lg shadow">
|
|
|
|
<div x-show="loading" class="fixed inset-0 bg-gray-800 bg-opacity-75 flex z-50 justify-center items-center">
|
|
<img src="../img/loading.gif" alt="Cargando..." class="w-16 h-16" />
|
|
</div>
|
|
|
|
|
|
<div class="container mx-auto p-6 w-full">
|
|
<div class="justify-between items-center w-full md:flex">
|
|
<div>
|
|
<h1 class="text-2xl font-bold mb-2">Lista de actualizaciones de datos de usuario</h1>
|
|
<p class="mb-4 text-sm">Gestión de actualizaciones de datos de usuario.</p>
|
|
</div>
|
|
|
|
<div class="mb-4 md:mb-0 relative">
|
|
<input type="text" placeholder="Buscar..." class="border border-gray-300 rounded p-2 w-full"
|
|
x-model="search" @input.debounce.500ms="loadData()" />
|
|
</div>
|
|
|
|
<div class="flex itmems-center justify-end mb-4 md:mb-0">
|
|
<button @click="exportToExcel()" class="bg-[#8eb02f] text-white px-4 py-2 rounded text-sm">Exportar a
|
|
Excel</button>
|
|
</div>
|
|
</div>
|
|
|
|
|
|
<div class="overflow-x-auto">
|
|
<table class="table-auto w-full">
|
|
<thead class="text-sm text-left py-4 border-b border-gray-300">
|
|
<tr class="text-left font-semibold border-collapse">
|
|
<th class="py-2 px-4 border-b">Código</th>
|
|
<th class="py-2 px-4 border-b">Nombre</th>
|
|
<th class="py-2 px-4 border-b"></th>
|
|
</tr>
|
|
</thead>
|
|
<tbody class="text-gray-500 select-none text-sm">
|
|
<template x-for="data in datos" :key="data.id">
|
|
<tr class="hover:bg-gray-100">
|
|
|
|
<td class="py-3 text-xs px-4 border-b" x-text="data.codigo"></td>
|
|
<td class="py-3 text-xs px-4 border-b" x-text="data.name"></td>
|
|
<td class="py-3 text-xs px-4 border-b">
|
|
<div class="flex items-center">
|
|
<div :class="data.revisado ? 'bg-green-500' : 'bg-gray-400'"
|
|
class="w-3 h-3 rounded-full mr-2"></div>
|
|
<span x-text="data.revisado ? 'Revisado' : 'No Revisado'"></span>
|
|
</div>
|
|
</td>
|
|
<td class="py-3 text-xs px-4 border-b"
|
|
x-text="data.created_at ? new Date(data.created_at).toLocaleDateString('es-ES', { year: 'numeric', month: 'long', day: 'numeric' }) : 'Fecha no disponible'">
|
|
</td>
|
|
<td class="py-3 text-xs px-4 border-b">
|
|
<div class="flex items-center gap-2">
|
|
<button @click="openViewModal(data)" title="Ver">
|
|
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24"
|
|
stroke-width="1.5" stroke="currentColor" class="w-5 text-[#8eb02f]">
|
|
<path stroke-linecap="round" stroke-linejoin="round"
|
|
d="M2.036 12.322a1.012 1.012 0 0 1 0-.639C3.423 7.51 7.36 4.5 12 4.5c4.638 0 8.573 3.007 9.963 7.178.07.207.07.431 0 .639C20.577 16.49 16.64 19.5 12 19.5c-4.638 0-8.573-3.007-9.963-7.178Z" />
|
|
<path stroke-linecap="round" stroke-linejoin="round"
|
|
d="M15 12a3 3 0 1 1-6 0 3 3 0 0 1 6 0Z" />
|
|
</svg>
|
|
</button>
|
|
</div>
|
|
</td>
|
|
</tr>
|
|
</template>
|
|
</tbody>
|
|
</table>
|
|
</div>
|
|
|
|
<div class="flex justify-start items-center gap-1 p-4">
|
|
<button @click="page = Math.max(1, page - 1); loadData()" :disabled="page === 1"
|
|
class="px-4 py-2 bg-gray-300 rounded disabled:opacity-50">
|
|
Anterior
|
|
</button>
|
|
<template x-for="pageNum in paginatedPages" :key="pageNum">
|
|
<button @click="goToPage(pageNum)" class="px-4 py-2 bg-gray-300 rounded "
|
|
:class="{ 'bg-blue-500 text-white': pageNum === page }" x-text="pageNum"></button>
|
|
</template>
|
|
<button @click="page += 1; loadData()" :disabled="datos.length < limit" :disabled="page === totalPages"
|
|
class="px-4 py-2 bg-gray-300 rounded disabled:opacity-50">
|
|
Siguiente
|
|
</button>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Modal Ver -->
|
|
<div x-show="viewModal"
|
|
class="fixed inset-0 overflow-auto bg-gray-800 bg-opacity-75 flex justify-center items-center z-50">
|
|
<div class="bg-white rounded-lg shadow-lg p-6 w-11/12 max-w-lg">
|
|
<h2 class="text-lg font-bold mb-4">Ver actualizacion de datos de usuario</h2>
|
|
|
|
<div class="w-full mb-4">
|
|
<label class="block mb-2">Tipo de usuario:</label>
|
|
<input type="text" x-model="selectedItem.tipo_usuario" disabled
|
|
class="border border-gray-300 rounded p-2 w-full" placeholder="Ingresa el tipo de usuario" />
|
|
|
|
</div>
|
|
|
|
<div class="flex flex-col sm:flex-row justify-between gap-4 mb-4">
|
|
<div class="w-full">
|
|
<label class="block mb-2">Tipo de persona:</label>
|
|
<input type="text" x-model="selectedItem.tipo_persona" disabled
|
|
class="border border-gray-300 rounded p-2 w-full" placeholder="Ingresa el tipo de persona" />
|
|
</div>
|
|
<div class="w-full">
|
|
<label class="block mb-2">Nombre:</label>
|
|
<input type="text" x-model="selectedItem.name" disabled
|
|
class="border border-gray-300 rounded p-2 w-full" placeholder="Ingresa el nombre" />
|
|
</div>
|
|
</div>
|
|
|
|
|
|
<div class="flex justify-end gap-2">
|
|
<button @click="viewModal = false" class="px-4 py-2 bg-gray-300 rounded">Cerrar</button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
|
|
|
|
</div>
|
|
|
|
<script>
|
|
function app() {
|
|
return {
|
|
datos: [],
|
|
loading: false,
|
|
limit: 10,
|
|
page: 1,
|
|
search: '',
|
|
totalPages: 0,
|
|
paginatedPages: [],
|
|
|
|
|
|
// modales
|
|
viewModal: false,
|
|
|
|
|
|
item: {
|
|
|
|
},
|
|
|
|
selectedItem: {
|
|
id: '',
|
|
tipo_persona: '',
|
|
name: '',
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
loadData() {
|
|
if (this.search !== '') {
|
|
this.page = 1;
|
|
this.limit = 100;
|
|
} else {
|
|
this.limit = 10;
|
|
}
|
|
this.loading = true;
|
|
axios.get('/app/loadactualizaciondatosusuarios', {
|
|
params: {
|
|
page: this.page,
|
|
limit: this.limit,
|
|
search: this.search
|
|
}
|
|
}).then(response => {
|
|
|
|
this.datos = response.data.registros.map(data => ({
|
|
id: data.ID,
|
|
tipo_persona: data.tipo_persona,
|
|
name: data.name,
|
|
created_at: data.CreatedAt,
|
|
}));
|
|
this.totalPages = response.data.totalPages;
|
|
this.calculatePaginatedPages();
|
|
}).catch(error => {
|
|
console.error('Error al obtener datos:', error);
|
|
}).finally(() => {
|
|
this.loading = false;
|
|
});
|
|
},
|
|
|
|
calculatePaginatedPages() {
|
|
const maxVisiblePages = 5;
|
|
const pages = [];
|
|
const startPage = Math.max(1, this.page - Math.floor(maxVisiblePages / 2));
|
|
const endPage = Math.min(this.totalPages, startPage + maxVisiblePages - 1);
|
|
|
|
for (let i = startPage; i <= endPage; i++) {
|
|
pages.push(i);
|
|
}
|
|
|
|
this.paginatedPages = pages;
|
|
},
|
|
|
|
goToPage(page) {
|
|
this.page = page;
|
|
this.loadData();
|
|
},
|
|
|
|
init() {
|
|
this.loadData();
|
|
},
|
|
|
|
openViewModal(data) {
|
|
|
|
this.selectedItem.id = data.id;
|
|
this.selectedItem.tipo_persona = data.tipo_persona;
|
|
this.selectedItem.name = data.name;
|
|
|
|
|
|
this.viewModal = true;
|
|
},
|
|
|
|
|
|
|
|
exportToExcel() {
|
|
axios.get('/app/export/actualizacion-datos-usuarios', {
|
|
responseType: 'blob', // Importante para manejar el archivo binario
|
|
}).then(response => {
|
|
const url = window.URL.createObjectURL(new Blob([response.data]));
|
|
const link = document.createElement('a');
|
|
link.href = url;
|
|
link.setAttribute('download', 'actualizacion_datos_usuarios.xlsx'); // Nombre del archivo
|
|
document.body.appendChild(link);
|
|
link.click();
|
|
}).catch(error => {
|
|
console.error('Error al exportar datos:', error);
|
|
});
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
</script>
|
|
`)
|
|
}
|
|
|
|
// ELIMINAR
|
|
func DeleteSubmodule(c *fiber.Ctx) error {
|
|
uid, err := strconv.ParseUint(c.Params("id"), 10, 32)
|
|
if err != nil {
|
|
return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{
|
|
"message": "Invalid ID",
|
|
"error": true,
|
|
})
|
|
}
|
|
|
|
if err := models.DeleteSumodule(uint(uid)); err != nil { // Ensure you have a function for deleting a module
|
|
return c.JSON(fiber.Map{
|
|
"message": err.Error(),
|
|
"error": true,
|
|
})
|
|
}
|
|
return c.JSON(fiber.Map{
|
|
"message": "Registro eliminado correctamente",
|
|
"error": false,
|
|
})
|
|
}
|