inventschario/sketches/004-configuracion/index.html
pikaos ee849eb9a8 feat: roadmap y diseño inicial — iteración 1 antes de feedback del usuario
Inventario de dispositivos electrónicos no prestables para administración
pública. Esta primera iteración contiene el diseño y la planificación
antes de contactar con el usuario para su validación.

Contenido:
- especificaciones.md: requisitos detallados del programa
- docs/arquitectura.md: propuesta de arquitectura con alternativas
- docs/roadmap-iteracion-1.md: plan de desarrollo iteración 1
- docs/arquitectura-diagrama.html: diagrama visual de arquitectura
- sketches/: mockups de las 4 pantallas principales
  - 001-dashboard: vista resumen
  - 002-inventario: tabla de dispositivos
  - 003-formulario: alta/edición de dispositivos
  - 004-configuracion: ajustes generales

Stack propuesto: Python + Flask + SQLite + Alpine.js
Distribución: PyInstaller → un único .exe autocontenido
Entorno: Windows 11 Pro unida a dominio, sin privilegios de admin
2026-08-03 01:34:56 +01:00

496 lines
24 KiB
HTML
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<!DOCTYPE html>
<html lang="es">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Inventschario — Configuración</title>
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&display=swap" rel="stylesheet">
<style>
* { box-sizing: border-box; margin: 0; padding: 0; }
body {
font-family: 'Inter', -apple-system, BlinkMacSystemFont, sans-serif;
color: #1a1a1a; background: #f7f7f5; line-height: 1.5;
display: flex; min-height: 100vh;
}
.sidebar {
width: 240px; background: #fff; border-right: 1px solid #e5e5e3;
padding: 20px 0; flex-shrink: 0; display: flex; flex-direction: column;
}
.sidebar-logo { padding: 0 20px 20px; border-bottom: 1px solid #e5e5e3; margin-bottom: 8px; }
.sidebar-logo h1 { font-size: 16px; font-weight: 700; }
.sidebar-logo span { font-size: 11px; color: #999; display: block; margin-top: 2px; }
.nav-section { padding: 8px 12px; }
.nav-label { font-size: 11px; font-weight: 600; color: #999; text-transform: uppercase; letter-spacing: 0.5px; padding: 8px 8px 4px; }
.nav-item {
display: flex; align-items: center; gap: 10px; padding: 8px 12px;
border-radius: 6px; font-size: 14px; color: #666; cursor: pointer;
text-decoration: none; transition: background 0.15s, color 0.15s;
}
.nav-item:hover { background: #f0f0ee; color: #1a1a1a; }
.nav-item.active { background: #f0f0ee; color: #1a1a1a; font-weight: 500; }
.nav-item svg { width: 18px; height: 18px; flex-shrink: 0; }
.nav-spacer { flex: 1; }
.nav-footer { padding: 12px 20px; border-top: 1px solid #e5e5e3; font-size: 12px; color: #999; }
.main { flex: 1; display: flex; flex-direction: column; overflow: hidden; }
.topbar {
background: #fff; border-bottom: 1px solid #e5e5e3;
padding: 16px 32px; display: flex; align-items: center; justify-content: space-between;
}
.topbar h2 { font-size: 18px; font-weight: 600; }
.btn {
padding: 8px 16px; border-radius: 6px; font-size: 13px; font-weight: 500;
cursor: pointer; border: 1px solid #e5e5e3; background: #fff; color: #1a1a1a;
transition: background 0.15s; font-family: inherit;
}
.btn:hover { background: #f0f0ee; }
.btn-primary { background: #1a1a1a; color: #fff; border-color: #1a1a1a; }
.btn-primary:hover { background: #333; }
.btn-danger { color: #dc2626; border-color: #fca5a5; }
.btn-danger:hover { background: #fef2f2; }
.btn-sm { padding: 4px 10px; font-size: 12px; }
.content { padding: 32px; overflow-y: auto; flex: 1; max-width: 800px; }
/* Settings sections */
.settings-section {
background: #fff; border: 1px solid #e5e5e3; border-radius: 8px;
margin-bottom: 24px; overflow: hidden;
}
.settings-header {
padding: 16px 20px; border-bottom: 1px solid #e5e5e3;
display: flex; align-items: center; justify-content: space-between;
}
.settings-header h3 { font-size: 14px; font-weight: 600; }
.settings-header p { font-size: 12px; color: #999; }
.settings-body { padding: 20px; }
.setting-row {
display: flex; align-items: center; justify-content: space-between;
padding: 12px 0; border-bottom: 1px solid #f0f0ee;
}
.setting-row:last-child { border-bottom: none; }
.setting-info { flex: 1; }
.setting-label { font-size: 13px; font-weight: 500; }
.setting-desc { font-size: 12px; color: #999; margin-top: 2px; }
.setting-control { margin-left: 20px; }
.form-input, .form-select {
padding: 6px 10px; border: 1px solid #e5e5e3; border-radius: 4px;
font-size: 13px; font-family: inherit; background: #fff;
}
.form-input:focus, .form-select:focus {
outline: none; border-color: #1a1a1a; box-shadow: 0 0 0 1px #1a1a1a;
}
/* Toggle switch */
.toggle {
width: 40px; height: 22px; background: #e5e5e3; border-radius: 11px;
position: relative; cursor: pointer; transition: background 0.2s;
border: none; padding: 0;
}
.toggle.active { background: #166534; }
.toggle::after {
content: ''; width: 18px; height: 18px; background: #fff;
border-radius: 50%; position: absolute; top: 2px; left: 2px;
transition: transform 0.2s; box-shadow: 0 1px 3px rgba(0,0,0,0.1);
}
.toggle.active::after { transform: translateX(18px); }
/* Field labels editor */
.label-editor { margin-top: 8px; }
.label-row {
display: flex; align-items: center; gap: 12px; padding: 8px 0;
border-bottom: 1px solid #f0f0ee;
}
.label-row:last-child { border-bottom: none; }
.label-key {
font-size: 12px; color: #999; font-family: monospace;
min-width: 160px;
}
.label-input {
flex: 1; padding: 6px 10px; border: 1px solid #e5e5e3; border-radius: 4px;
font-size: 13px; font-family: inherit;
}
.label-input:focus { outline: none; border-color: #1a1a1a; box-shadow: 0 0 0 1px #1a1a1a; }
/* Backup list */
.backup-list { margin-top: 12px; }
.backup-item {
display: flex; align-items: center; justify-content: space-between;
padding: 10px 12px; border: 1px solid #f0f0ee; border-radius: 6px;
margin-bottom: 8px; font-size: 13px;
}
.backup-info { display: flex; align-items: center; gap: 12px; }
.backup-icon { font-size: 16px; }
.backup-name { font-weight: 500; }
.backup-meta { font-size: 11px; color: #999; }
.backup-actions { display: flex; gap: 4px; }
/* Tabs */
.tabs {
display: flex; gap: 0; border-bottom: 1px solid #e5e5e3; margin-bottom: 20px;
}
.tab {
padding: 10px 16px; font-size: 13px; font-weight: 500; color: #666;
cursor: pointer; border-bottom: 2px solid transparent; transition: all 0.15s;
background: none; border-top: none; border-left: none; border-right: none;
font-family: inherit;
}
.tab:hover { color: #1a1a1a; }
.tab.active { color: #1a1a1a; border-bottom-color: #1a1a1a; }
*:focus-visible { outline: 2px solid #1a1a1a; outline-offset: 2px; }
</style>
</head>
<body>
<!-- Sidebar -->
<nav class="sidebar" role="navigation" aria-label="Menú principal">
<div class="sidebar-logo"><h1>Inventschario</h1><span>Inventario Electrónico</span></div>
<div class="nav-section">
<div class="nav-label">Principal</div>
<a class="nav-item" href="#dashboard">
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><rect x="3" y="3" width="7" height="7" rx="1"/><rect x="14" y="3" width="7" height="7" rx="1"/><rect x="3" y="14" width="7" height="7" rx="1"/><rect x="14" y="14" width="7" height="7" rx="1"/></svg>
Dashboard
</a>
<a class="nav-item" href="#inventory">
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2"/><rect x="9" y="3" width="6" height="4" rx="1"/></svg>
Inventario
</a>
<a class="nav-item" href="#add">
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><circle cx="12" cy="12" r="10"/><line x1="12" y1="8" x2="12" y2="16"/><line x1="8" y1="12" x2="16" y2="12"/></svg>
Agregar Dispositivo
</a>
</div>
<div class="nav-section">
<div class="nav-label">Herramientas</div>
<a class="nav-item" href="#import">
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M21 15v4a2 2 0 01-2 2H5a2 2 0 01-2-2v-4"/><polyline points="17 8 12 3 7 8"/><line x1="12" y1="3" x2="12" y2="15"/></svg>
Importar / Exportar
</a>
<a class="nav-item" href="#backups">
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M19 21H5a2 2 0 01-2-2V5a2 2 0 012-2h11l5 5v11a2 2 0 01-2 2z"/><polyline points="17 21 17 13 7 13 7 21"/><polyline points="7 3 7 8 15 8"/></svg>
Copias de Seguridad
</a>
<a class="nav-item active" href="#settings" aria-current="page">
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><circle cx="12" cy="12" r="3"/><path d="M19.4 15a1.65 1.65 0 00.33 1.82l.06.06a2 2 0 010 2.83 2 2 0 01-2.83 0l-.06-.06a1.65 1.65 0 00-1.82-.33 1.65 1.65 0 00-1 1.51V21a2 2 0 01-4 0v-.09A1.65 1.65 0 009 19.4a1.65 1.65 0 00-1.82.33l-.06.06a2 2 0 01-2.83-2.83l.06-.06A1.65 1.65 0 004.68 15a1.65 1.65 0 00-1.51-1H3a2 2 0 010-4h.09A1.65 1.65 0 004.6 9a1.65 1.65 0 00-.33-1.82l-.06-.06a2 2 0 012.83-2.83l.06.06A1.65 1.65 0 009 4.68a1.65 1.65 0 001-1.51V3a2 2 0 014 0v.09a1.65 1.65 0 001 1.51 1.65 1.65 0 001.82-.33l.06-.06a2 2 0 012.83 2.83l-.06.06a1.65 1.65 0 00-.33 1.82V9c.26.604.852.997 1.51 1H21a2 2 0 010 4h-.09a1.65 1.65 0 00-1.51 1z"/></svg>
Configuración
</a>
</div>
<div class="nav-spacer"></div>
<div class="nav-footer">v1.0.0 — 2026-08-03</div>
</nav>
<!-- Main Content -->
<main class="main" role="main">
<header class="topbar">
<h2>Configuración</h2>
<div class="topbar-actions">
<button class="btn btn-primary">Guardar Cambios</button>
</div>
</header>
<div class="content">
<!-- Tabs -->
<div class="tabs" role="tablist">
<button class="tab active" role="tab" aria-selected="true">General</button>
<button class="tab" role="tab">Campos</button>
<button class="tab" role="tab">Copias de Seguridad</button>
<button class="tab" role="tab">Importar / Exportar</button>
</div>
<!-- General Settings -->
<div class="settings-section">
<div class="settings-header">
<div>
<h3>Información General</h3>
<p>Datos de la institución y configuración básica</p>
</div>
</div>
<div class="settings-body">
<div class="setting-row">
<div class="setting-info">
<div class="setting-label">Nombre de la Institución</div>
<div class="setting-desc">Aparece en el encabezado de exportaciones e informes</div>
</div>
<div class="setting-control">
<input class="form-input" type="text" value="Ayuntamiento de Ejemplo" style="width: 280px;">
</div>
</div>
<div class="setting-row">
<div class="setting-info">
<div class="setting-label">Prefijo de Número de Catálogo</div>
<div class="setting-desc">Prefijo para la generación automática de números de inventario</div>
</div>
<div class="setting-control">
<input class="form-input" type="text" value="INV-" style="width: 120px;">
</div>
</div>
<div class="setting-row">
<div class="setting-info">
<div class="setting-label">Separador CSV por Defecto</div>
<div class="setting-desc">Separador utilizado al exportar e importar archivos CSV</div>
</div>
<div class="setting-control">
<select class="form-select">
<option value=";">Punto y coma (;)</option>
<option value=",">Coma (,)</option>
<option value="\t">Tabulador</option>
</select>
</div>
</div>
</div>
</div>
<!-- Field Labels -->
<div class="settings-section">
<div class="settings-header">
<div>
<h3>Etiquetas de Campos</h3>
<p>Personaliza los nombres que aparecen en la interfaz. El nombre interno en la base de datos no cambia.</p>
</div>
</div>
<div class="settings-body">
<div class="label-editor">
<div class="label-row">
<span class="label-key">serial_number</span>
<input class="label-input" type="text" value="Número de Serie">
</div>
<div class="label-row">
<span class="label-key">service_tag</span>
<input class="label-input" type="text" value="Service Tag">
</div>
<div class="label-row">
<span class="label-key">catalog_number</span>
<input class="label-input" type="text" value="Número de Catálogo Interno">
</div>
<div class="label-row">
<span class="label-key">additional_registry</span>
<input class="label-input" type="text" value="Registro Adicional">
</div>
<div class="label-row">
<span class="label-key">device_name</span>
<input class="label-input" type="text" value="Nombre / Descripción">
</div>
<div class="label-row">
<span class="label-key">device_type</span>
<input class="label-input" type="text" value="Tipo de Dispositivo">
</div>
<div class="label-row">
<span class="label-key">brand</span>
<input class="label-input" type="text" value="Marca">
</div>
<div class="label-row">
<span class="label-key">model</span>
<input class="label-input" type="text" value="Modelo">
</div>
<div class="label-row">
<span class="label-key">purchase_date</span>
<input class="label-input" type="text" value="Fecha de Compra">
</div>
<div class="label-row">
<span class="label-key">warranty_expiry</span>
<input class="label-input" type="text" value="Fin de Garantía">
</div>
<div class="label-row">
<span class="label-key">purchase_price</span>
<input class="label-input" type="text" value="Precio de Compra (€)">
</div>
<div class="label-row">
<span class="label-key">assigned_location</span>
<input class="label-input" type="text" value="Ubicación Asignada">
</div>
<div class="label-row">
<span class="label-key">assigned_user</span>
<input class="label-input" type="text" value="Usuario Asignado">
</div>
<div class="label-row">
<span class="label-key">department</span>
<input class="label-input" type="text" value="Departamento">
</div>
<div class="label-row">
<span class="label-key">status</span>
<input class="label-input" type="text" value="Estado">
</div>
<div class="label-row">
<span class="label-key">condition</span>
<input class="label-input" type="text" value="Estado Físico">
</div>
</div>
</div>
</div>
<!-- Backup Settings -->
<div class="settings-section">
<div class="settings-header">
<div>
<h3>Copias de Seguridad</h3>
<p>Configuración del backup automático y gestión de copias</p>
</div>
</div>
<div class="settings-body">
<div class="setting-row">
<div class="setting-info">
<div class="setting-label">Backup Automático</div>
<div class="setting-desc">Crear una copia de seguridad de la base de datos cada día</div>
</div>
<div class="setting-control">
<button class="toggle active" role="switch" aria-checked="true" aria-label="Activar backup automático"></button>
</div>
</div>
<div class="setting-row">
<div class="setting-info">
<div class="setting-label">Hora del Backup</div>
<div class="setting-desc">Hora a la que se ejecuta el backup diario</div>
</div>
<div class="setting-control">
<input class="form-input" type="time" value="02:00">
</div>
</div>
<div class="setting-row">
<div class="setting-info">
<div class="setting-label">Días de Retención</div>
<div class="setting-desc">Número de días antes de sugerir la eliminación de backups antiguos</div>
</div>
<div class="setting-control">
<input class="form-input" type="number" value="30" min="7" max="365" style="width: 80px;">
<span style="font-size: 12px; color: #999; margin-left: 4px;">días</span>
</div>
</div>
<div class="setting-row">
<div class="setting-info">
<div class="setting-label">Máximo de Backups</div>
<div class="setting-desc">Número máximo de copias de seguridad a conservar</div>
</div>
<div class="setting-control">
<input class="form-input" type="number" value="90" min="10" max="365" style="width: 80px;">
</div>
</div>
<!-- Recent backups -->
<div style="margin-top: 20px; padding-top: 16px; border-top: 1px solid #e5e5e3;">
<div style="display: flex; justify-content: space-between; align-items: center; margin-bottom: 12px;">
<span style="font-size: 13px; font-weight: 500;">Copias Recientes</span>
<button class="btn btn-sm">Crear Backup Manual</button>
</div>
<div class="backup-list">
<div class="backup-item">
<div class="backup-info">
<span class="backup-icon" aria-hidden="true">💾</span>
<div>
<div class="backup-name">inventschario_20260803_020000.db.gz</div>
<div class="backup-meta">3 de agosto 2026, 02:00 — 2.4 MB — Automático</div>
</div>
</div>
<div class="backup-actions">
<button class="btn btn-sm" aria-label="Restaurar este backup">Restaurar</button>
<button class="btn btn-sm btn-danger" aria-label="Eliminar este backup">Eliminar</button>
</div>
</div>
<div class="backup-item">
<div class="backup-info">
<span class="backup-icon" aria-hidden="true">💾</span>
<div>
<div class="backup-name">inventschario_20260802_020000.db.gz</div>
<div class="backup-meta">2 de agosto 2026, 02:00 — 2.3 MB — Automático</div>
</div>
</div>
<div class="backup-actions">
<button class="btn btn-sm">Restaurar</button>
<button class="btn btn-sm btn-danger">Eliminar</button>
</div>
</div>
<div class="backup-item">
<div class="backup-info">
<span class="backup-icon" aria-hidden="true">💾</span>
<div>
<div class="backup-name">inventschario_20260801_143022.db.gz</div>
<div class="backup-meta">1 de agosto 2026, 14:30 — 2.3 MB — <strong style="color: #92400e;">Manual</strong></div>
</div>
</div>
<div class="backup-actions">
<button class="btn btn-sm">Restaurar</button>
<button class="btn btn-sm btn-danger">Eliminar</button>
</div>
</div>
</div>
</div>
</div>
</div>
<!-- Locations & Departments -->
<div class="settings-section">
<div class="settings-header">
<div>
<h3>Ubicaciones y Departamentos</h3>
<p>Gestiona las ubicaciones físicas y departamentos de la institución</p>
</div>
</div>
<div class="settings-body">
<div style="display: grid; grid-template-columns: 1fr 1fr; gap: 24px;">
<!-- Locations -->
<div>
<div style="display: flex; justify-content: space-between; align-items: center; margin-bottom: 12px;">
<span style="font-size: 13px; font-weight: 500;">Ubicaciones</span>
<button class="btn btn-sm">+ Añadir</button>
</div>
<div style="border: 1px solid #e5e5e3; border-radius: 6px; overflow: hidden;">
<div style="padding: 8px 12px; border-bottom: 1px solid #f0f0ee; font-size: 13px; display: flex; justify-content: space-between;">
<span>Planta 0 — Recepción</span>
<button class="btn btn-sm btn-danger" style="padding: 2px 6px; font-size: 11px;">×</button>
</div>
<div style="padding: 8px 12px; border-bottom: 1px solid #f0f0ee; font-size: 13px; display: flex; justify-content: space-between;">
<span>Planta 1 — Open Space</span>
<button class="btn btn-sm btn-danger" style="padding: 2px 6px; font-size: 11px;">×</button>
</div>
<div style="padding: 8px 12px; border-bottom: 1px solid #f0f0ee; font-size: 13px; display: flex; justify-content: space-between;">
<span>Planta 1 — Reprografía</span>
<button class="btn btn-sm btn-danger" style="padding: 2px 6px; font-size: 11px;">×</button>
</div>
<div style="padding: 8px 12px; border-bottom: 1px solid #f0f0ee; font-size: 13px; display: flex; justify-content: space-between;">
<span>Planta 2 — Desp. Dirección</span>
<button class="btn btn-sm btn-danger" style="padding: 2px 6px; font-size: 11px;">×</button>
</div>
<div style="padding: 8px 12px; font-size: 13px; display: flex; justify-content: space-between;">
<span>Almacén</span>
<button class="btn btn-sm btn-danger" style="padding: 2px 6px; font-size: 11px;">×</button>
</div>
</div>
</div>
<!-- Departments -->
<div>
<div style="display: flex; justify-content: space-between; align-items: center; margin-bottom: 12px;">
<span style="font-size: 13px; font-weight: 500;">Departamentos</span>
<button class="btn btn-sm">+ Añadir</button>
</div>
<div style="border: 1px solid #e5e5e3; border-radius: 6px; overflow: hidden;">
<div style="padding: 8px 12px; border-bottom: 1px solid #f0f0ee; font-size: 13px; display: flex; justify-content: space-between;">
<span>Informática</span>
<button class="btn btn-sm btn-danger" style="padding: 2px 6px; font-size: 11px;">×</button>
</div>
<div style="padding: 8px 12px; border-bottom: 1px solid #f0f0ee; font-size: 13px; display: flex; justify-content: space-between;">
<span>Administración</span>
<button class="btn btn-sm btn-danger" style="padding: 2px 6px; font-size: 11px;">×</button>
</div>
<div style="padding: 8px 12px; border-bottom: 1px solid #f0f0ee; font-size: 13px; display: flex; justify-content: space-between;">
<span>Recursos Humanos</span>
<button class="btn btn-sm btn-danger" style="padding: 2px 6px; font-size: 11px;">×</button>
</div>
<div style="padding: 8px 12px; border-bottom: 1px solid #f0f0ee; font-size: 13px; display: flex; justify-content: space-between;">
<span>Contabilidad</span>
<button class="btn btn-sm btn-danger" style="padding: 2px 6px; font-size: 11px;">×</button>
</div>
<div style="padding: 8px 12px; font-size: 13px; display: flex; justify-content: space-between;">
<span>Dirección</span>
<button class="btn btn-sm btn-danger" style="padding: 2px 6px; font-size: 11px;">×</button>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</main>
</body>
</html>