Plataforma técnica · Sevastopol
System Config View Island
Islands Sevastopol Administración
Propósito
Section titled “Propósito”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.
Ubicación
Section titled “Ubicación”Directorysevastopol/src/components/islands/admin/
- SystemConfigViewIsland.tsx — componente único, internamente
SystemConfigView
- SystemConfigViewIsland.tsx — componente único, internamente
Directorysevastopol/src/config/
- headers.ts —
HEADERS_SYSTEM_CONFIG(estructura de columnas)
- headers.ts —
Estado
Section titled “Estado”| Signal | Tipo | Uso |
|---|---|---|
tenants | Tenant[] | Catálogo del selector (/api/tenant). |
selectedTenantId | string | Tenant activo, sincronizado con localStorage. |
configs | SystemConfig[] | Lista completa del tenant seleccionado. |
qClave / qCategoria | string | Filtros sub-string. |
qActive | string | "", "true", "false". |
showModal / editingConfig / formData | edición | Modal de edición de un registro. |
isSubmitting / isLoading | bloqueos | UI. |
Filtros y ordenación
Section titled “Filtros y ordenación”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.
Estadísticas
Section titled “Estadísticas”Tres StatCard en el header:
| Métrica | Cálculo |
|---|---|
| Total | configs.length |
| Activas | configs.filter(c => c.activa).length |
| Categorías | new Set(configs.map(c => c.categoria).filter(Boolean)).size |
Las columnas vienen de HEADERS_SYSTEM_CONFIG (configuración compartida) con tres render overrides:
| Columna | Render |
|---|---|
clave | Monoespaciado en negrita + categoría como subtítulo. |
valor | Texto truncado con title={r.valor} (tooltip al pasar el cursor). |
activa | <BoolBadge> con etiqueta “Activa” o “Inactiva”. |
Modal de edición
Section titled “Modal de edición”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 editable | Tipo | Notas |
|---|---|---|
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),});Endpoints consumidos
Section titled “Endpoints consumidos”| Método | Ruta | Operación |
|---|---|---|
GET | /api/tenant | Catá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.
Reglas de UI
Section titled “Reglas de UI”| Regla | Motivo |
|---|---|
Selector legacy + localStorage en vez de useActiveTenant | Esta island predates el hook compartido; persistencia mediante localStorage mantiene la selección entre recargas. |
| Clave inmutable en el modal | La clave es referenciada por código del sistema; cambiarla rompería lookups. |
| Orden categoría → clave | Agrupa visualmente parámetros relacionados; reduce búsqueda. |
| Sin alta ni eliminación | El catálogo de parámetros es responsabilidad del bootstrap; la UI solo edita valores. |
| Truncar valor con tooltip completo | Valores 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. |