feat(sprint-3): biblioteca, recursos TO-BE, ROI mejorado e identidad de marca

Sprint 3 completo — 7 etapas:

- Etapa 1-2: biblioteca de procesos con agrupación por cliente (Dexie v4,
  LibraryPage, library-store, groupId/tags en Process)
- Etapa 3: modal de configuración global por proceso (horizonte, frecuencia,
  inversión, moneda) con validación y persistencia
- Etapa 4: panel de recursos rediseñado con soporte de unidades y minutos
  de utilización (utilizationMinutes + units)
- Etapa 5: recursos en escenario automatizado — motor de simulación,
  UI de edición en ActivityPanel, comparación AS-IS/TO-BE en reporte
- Etapa 6: PDF recursos automatizados, automatedAssignedResources requerido,
  561 tests pasando
- Etapa 7: identidad de marca — AppHeader gradiente naranja/magenta con logo
  InQuality en 5 páginas, favicon con logo real, logo/marca navegan a home

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-05-27 11:49:43 -03:00
parent d2746f07d1
commit 67e259e6ed
57 changed files with 4372 additions and 82 deletions

View File

@@ -204,7 +204,11 @@ export function runSimulation(input: SimulationInput): SimulationResult {
const resourceBreakdown: ActivitySimResult['resourceCostBreakdown'] = []
let resourceCostDirect = 0
for (const assignment of activity.assignedResources) {
const resourceAssignments = useAutomated
? (activity.automatedAssignedResources ?? [])
: activity.assignedResources
for (const assignment of resourceAssignments) {
const resource = resources.get(assignment.resourceId)
if (!resource) continue
const hoursUsed = (assignment.utilizationMinutes / 60) * assignment.units

View File

@@ -16,6 +16,8 @@ export interface Process {
automationInvestment: number // 🆕 Sprint 1 — inversión total del proyecto, default 0
createdAt: number
updatedAt: number
groupId: UUID | null // 🆕 Sprint 3 — null = proceso sin grupo asignado ("Sin clasificar")
tags: string[] // 🆕 Sprint 3 — tags libres para clasificación y búsqueda
}
export type ActivityType = 'task' | 'subprocess'
@@ -32,6 +34,7 @@ export interface Activity {
automatable: boolean // 🆕 Sprint 1 — si la actividad es candidata a automatización
automatedCostFixed: number // 🆕 Sprint 1 — costo recurrente por ejecución automatizada
automatedTimeMinutes: number // 🆕 Sprint 1 — tiempo de la actividad en escenario automatizado
automatedAssignedResources: ActivityResourceAssignment[] // 🆕 Sprint 3 — recursos del escenario automatizado (requerido desde Etapa 6)
}
export interface ActivityResourceAssignment {
@@ -74,6 +77,20 @@ export interface Simulation {
resultAutomated?: SimulationResult // 🆕 Sprint 1 — escenario automatizado (opcional por retrocompatibilidad)
}
// ─── Process Library ─────────────────────────────────────────────────────────
export interface ProcessGroup {
id: UUID // generado con crypto.randomUUID()
name: string
createdAt: number // timestamp Unix ms
updatedAt: number // timestamp Unix ms
}
export interface AppSettings {
id: 'singleton' // siempre un único registro por instalación
groupLabel: string // etiqueta configurable para el nivel de agrupación. Default: 'Cliente'
}
// ─── Motor de simulación: Input/Output ───────────────────────────────────────
// Escenario de simulación: actual (campos directCostFixed/executionTimeMinutes)