Skip to content

Plataforma técnica · Sevastopol

System Config View Island

Islands Sevastopol Administración

SystemConfigViewIsland opera los parámetros del sistema por tenant — feature flags, límites, defaults — sobre /api/admin/system-config. Lista, filtra por clave/categoría/estado y permite editar valor/descripción/categoría/tipo_dato/activa. Sin alta ni eliminación desde la UI: los registros vienen sembrados por el bootstrap del tenant y solo se mantienen.

  • Directorysevastopol/src/components/islands/admin/
    • SystemConfigViewIsland.tsx — componente único, internamente SystemConfigView
  • Directorysevastopol/src/config/
    • headers.ts HEADERS_SYSTEM_CONFIG (estructura de columnas)
SignalTipoUso
tenantsTenant[]Catálogo del selector (/api/tenant).
selectedTenantIdstringTenant activo, sincronizado con localStorage.
configsSystemConfig[]Lista completa del tenant seleccionado.
qClave / qCategoriastringFiltros sub-string.
qActivestring"", "true", "false".
showModal / editingConfig / formDataediciónModal de edición de un registro.
isSubmitting / isLoadingbloqueosUI.
const filteredConfigs = createMemo(() => {
let list = [...configs()];
if (qClave()) list = list.filter(c => c.clave.toLowerCase().includes(qClave().toLowerCase()));
if (qCategoria()) list = list.filter(c => (c.categoria || '').toLowerCase().includes(qCategoria().toLowerCase()));
if (qActive() === "true") list = list.filter(c => c.activa);
if (qActive() === "false") list = list.filter(c => !c.activa);
return list.sort((a, b) => {
const catCompare = (a.categoria || '').localeCompare(b.categoria || '');
return catCompare !== 0 ? catCompare : a.clave.localeCompare(b.clave);
});
});

Orden estable: primero por categoría, luego por clave. Categorías nulas quedan al inicio.

Tres StatCard en el header:

MétricaCálculo
Totalconfigs.length
Activasconfigs.filter(c => c.activa).length
Categoríasnew Set(configs.map(c => c.categoria).filter(Boolean)).size

Las columnas vienen de HEADERS_SYSTEM_CONFIG (configuración compartida) con tres render overrides:

ColumnaRender
claveMonoespaciado en negrita + categoría como subtítulo.
valorTexto truncado con title={r.valor} (tooltip al pasar el cursor).
activa<BoolBadge> con etiqueta “Activa” o “Inactiva”.

5 campos. La clave no es editable — se muestra como cabecera del modal en una caja gris destacada:

<div class="bg-zinc-50 dark:bg-zinc-900 p-3 rounded-lg">
<div class="text-sm font-medium">Clave:</div>
<div class="font-mono text-lg font-bold">{editingConfig()!.clave}</div>
</div>
Campo editableTipoNotas
valor<Input>El valor real del parámetro.
descripcion<Input>Descripción humana.
categoria<Input>Grupo libre (no enum).
tipo_dato<Input>Pista de tipo (STRING, INT, BOOL…).
activa<Checkbox>Bandera de activación.

El payload (UpdateSystemConfigDTO) se envía completo en cada PUT:

const r = await authenticatedFetch(`${API_BASE}/${editingConfig()!.id}?tenant_id=${selectedTenantId()}`, {
method: "PUT",
headers: { "Content-Type": "application/json" },
body: JSON.stringify(payload),
});
MétodoRutaOperación
GET/api/tenantCatálogo de tenants para el selector (legacy).
GET/api/admin/system-config?tenant_id=...Lista parámetros del tenant.
PUT/api/admin/system-config/:id?tenant_id=...Actualiza un parámetro.

Sin POST ni DELETE — la administración del schema (alta/baja de parámetros) está fuera del frontend.

ReglaMotivo
Selector legacy + localStorage en vez de useActiveTenantEsta island predates el hook compartido; persistencia mediante localStorage mantiene la selección entre recargas.
Clave inmutable en el modalLa clave es referenciada por código del sistema; cambiarla rompería lookups.
Orden categoría → claveAgrupa visualmente parámetros relacionados; reduce búsqueda.
Sin alta ni eliminaciónEl catálogo de parámetros es responsabilidad del bootstrap; la UI solo edita valores.
Truncar valor con tooltip completoValores largos (URLs, JSON) no rompen el layout pero siguen siendo legibles al pasar el cursor.
Modal con widthClass="w-[600px]"Más ancho que el modal estándar para acomodar valores y descripciones largas.