modificaciones de mejora

This commit is contained in:
Lizandro Guarnizo
2026-06-11 18:06:21 -05:00
parent fa0991eed7
commit 89021f2e77
3 changed files with 104 additions and 19 deletions
+29 -6
View File
@@ -26,25 +26,48 @@ func (OssApi) TableName() string {
return "oss_api"
}
// GetAllQrVcard obtiene todos los registros de QrVcard con paginación y búsqueda
func GetAllOssApi(limit, offset int, search string) ([]OssApi, int64, error) {
// GetAllOssApi obtiene todos los registros de OssApi con paginación, búsqueda, filtros y orden
func GetAllOssApi(limit, offset int, search, provider, estado, sortBy, sortDir, dateFrom, dateTo string) ([]OssApi, int64, error) {
var items []OssApi
var total int64
db := app.Http.Database.DB.Model(&OssApi{})
// Si se pasa un término de búsqueda, lo aplicamos en la consulta
if search != "" {
db = db.Where("name LIKE ?", "%"+search+"%")
}
if provider != "" {
db = db.Where("provider = ?", provider)
}
if estado == "activo" {
db = db.Where("is_active = ?", true)
} else if estado == "inactivo" {
db = db.Where("is_active = ?", false)
}
if dateFrom != "" {
db = db.Where("created_at >= ?", dateFrom)
}
if dateTo != "" {
db = db.Where("created_at <= ?", dateTo+" 23:59:59")
}
// Contamos el total de registros
if err := db.Count(&total).Error; err != nil {
log.Printf("Error counting OssApi: %v", err)
return nil, 0, err
}
// Obtenemos los registros con paginación
if err := db.Order("id DESC").Limit(limit).Offset(offset).Find(&items).Error; err != nil {
allowedSorts := map[string]bool{
"id": true, "name": true, "provider": true,
"endpoint": true, "bucket_name": true, "region": true,
"is_active": true, "created_at": true,
}
if !allowedSorts[sortBy] {
sortBy = "id"
}
if sortDir != "asc" {
sortDir = "desc"
}
if err := db.Order(sortBy+" "+sortDir).Limit(limit).Offset(offset).Find(&items).Error; err != nil {
log.Printf("Error retrieving OssApi: %v", err)
return nil, 0, err
}
+65 -11
View File
@@ -56,12 +56,28 @@
<!-- Alerta de error -->
<div x-show="errorMsg" class="mb-4 p-3 bg-red-50 border border-red-200 rounded-lg text-sm text-red-700" x-text="errorMsg"></div>
<!-- Barra de búsqueda (solo tab config) -->
<div x-show="activeTab === 'config'" class="flex flex-col sm:flex-row gap-3 mb-5">
<!-- Filtros (solo tab config) -->
<div x-show="activeTab === 'config'" class="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-6 gap-3 mb-5">
<input x-model="search" @keyup.enter="load()" type="text" placeholder="Buscar por nombre..."
class="border border-gray-300 rounded-lg px-3 py-2 text-sm flex-1 focus:outline-none focus:ring-1 focus:ring-[#8eb02f]" />
class="border border-gray-300 rounded-lg px-3 py-2 text-sm focus:outline-none focus:ring-1 focus:ring-[#8eb02f]" />
<select x-model="filtroProvider" @change="load()"
class="border border-gray-300 rounded-lg px-3 py-2 text-sm focus:outline-none focus:ring-1 focus:ring-[#8eb02f]">
<option value="">Todos los proveedores</option>
<option value="s3">S3 / MinIO</option>
<option value="alibaba">Alibaba Cloud</option>
</select>
<select x-model="filtroEstado" @change="load()"
class="border border-gray-300 rounded-lg px-3 py-2 text-sm focus:outline-none focus:ring-1 focus:ring-[#8eb02f]">
<option value="">Todos los estados</option>
<option value="activo">Activo</option>
<option value="inactivo">Inactivo</option>
</select>
<input x-model="dateFrom" @change="load()" type="date"
class="border border-gray-300 rounded-lg px-3 py-2 text-sm focus:outline-none focus:ring-1 focus:ring-[#8eb02f]" />
<input x-model="dateTo" @change="load()" type="date"
class="border border-gray-300 rounded-lg px-3 py-2 text-sm focus:outline-none focus:ring-1 focus:ring-[#8eb02f]" />
<button @click="load()" :disabled="loading"
class="flex items-center gap-2 px-4 py-2 rounded-lg border border-gray-300 text-sm hover:bg-gray-50 transition disabled:opacity-40">
class="flex items-center justify-center gap-2 px-4 py-2 rounded-lg border border-gray-300 text-sm hover:bg-gray-50 transition disabled:opacity-40">
<svg xmlns="http://www.w3.org/2000/svg" class="h-4 w-4" :class="loading && 'animate-spin'" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2">
<path stroke-linecap="round" stroke-linejoin="round" d="M16.023 9.348h4.992v-.001M2.985 19.644v-4.992m0 0h4.992m-4.993 0 3.181 3.183a8.25 8.25 0 0 0 13.803-3.7M4.031 9.865a8.25 8.25 0 0 1 13.803-3.7l3.181 3.182m0-4.991v4.99"/>
</svg>
@@ -74,12 +90,24 @@
<table class="table-auto w-full text-sm">
<thead class="border-b border-gray-200 text-left text-xs font-semibold text-gray-500 uppercase">
<tr>
<th class="py-2 px-3">Nombre</th>
<th class="py-2 px-3">Proveedor</th>
<th class="py-2 px-3">Endpoint</th>
<th class="py-2 px-3">Bucket</th>
<th class="py-2 px-3">Región</th>
<th class="py-2 px-3">Estado</th>
<th class="py-2 px-3 cursor-pointer select-none hover:text-gray-700" @click="sort('name')">
<span class="inline-flex items-center gap-1">Nombre <template x-if="sortBy === 'name'"><span x-text="sortDir === 'asc' ? '↑' : '↓'"></span></template></span>
</th>
<th class="py-2 px-3 cursor-pointer select-none hover:text-gray-700" @click="sort('provider')">
<span class="inline-flex items-center gap-1">Proveedor <template x-if="sortBy === 'provider'"><span x-text="sortDir === 'asc' ? '↑' : '↓'"></span></template></span>
</th>
<th class="py-2 px-3 cursor-pointer select-none hover:text-gray-700" @click="sort('endpoint')">
<span class="inline-flex items-center gap-1">Endpoint <template x-if="sortBy === 'endpoint'"><span x-text="sortDir === 'asc' ? '↑' : '↓'"></span></template></span>
</th>
<th class="py-2 px-3 cursor-pointer select-none hover:text-gray-700" @click="sort('bucket_name')">
<span class="inline-flex items-center gap-1">Bucket <template x-if="sortBy === 'bucket_name'"><span x-text="sortDir === 'asc' ? '↑' : '↓'"></span></template></span>
</th>
<th class="py-2 px-3 cursor-pointer select-none hover:text-gray-700" @click="sort('region')">
<span class="inline-flex items-center gap-1">Región <template x-if="sortBy === 'region'"><span x-text="sortDir === 'asc' ? '↑' : '↓'"></span></template></span>
</th>
<th class="py-2 px-3 cursor-pointer select-none hover:text-gray-700" @click="sort('is_active')">
<span class="inline-flex items-center gap-1">Estado <template x-if="sortBy === 'is_active'"><span x-text="sortDir === 'asc' ? '↑' : '↓'"></span></template></span>
</th>
<th class="py-2 px-3 text-right">Acciones</th>
</tr>
</thead>
@@ -374,6 +402,12 @@ function ossApiApp() {
totalPages: 1,
page: 1,
search: '',
filtroProvider: '',
filtroEstado: '',
dateFrom: '',
dateTo: '',
sortBy: 'id',
sortDir: 'desc',
loading: false,
errorMsg: '',
showModal: false,
@@ -406,7 +440,16 @@ function ossApiApp() {
this.loading = true;
this.errorMsg = '';
try {
const params = new URLSearchParams({ page: this.page, search: this.search });
const params = new URLSearchParams({
page: this.page,
search: this.search,
provider: this.filtroProvider,
estado: this.filtroEstado,
date_from: this.dateFrom,
date_to: this.dateTo,
sort_by: this.sortBy,
sort_dir: this.sortDir,
});
const res = await fetch(`/app/loadossapi?${params}`);
const data = await res.json();
if (!res.ok) throw new Error(data.error || 'Error al cargar');
@@ -420,6 +463,17 @@ function ossApiApp() {
}
},
sort(col) {
if (this.sortBy === col) {
this.sortDir = this.sortDir === 'asc' ? 'desc' : 'asc';
} else {
this.sortBy = col;
this.sortDir = 'asc';
}
this.page = 1;
this.load();
},
openAdd() {
this.editItem = null;
this.form = { name: '', provider: 'alibaba', endpoint: '', access_key_id: '', access_key_secret: '', bucket_name: '', region: '', is_active: true, notes: '' };
+10 -2
View File
@@ -44,9 +44,17 @@ func GetOssApiConfigs(c *fiber.Ctx) error {
}
limit := 20
offset := (page - 1) * limit
search := c.Query("search", "")
items, total, err := models.GetAllOssApi(limit, offset, search)
items, total, err := models.GetAllOssApi(
limit, offset,
c.Query("search", ""),
c.Query("provider", ""),
c.Query("estado", ""),
c.Query("sort_by", "id"),
c.Query("sort_dir", "desc"),
c.Query("date_from", ""),
c.Query("date_to", ""),
)
if err != nil {
return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{"error": err.Error()})
}