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:
@@ -1,5 +1,5 @@
|
||||
import Dexie, { type Table } from 'dexie'
|
||||
import type { Process, Activity, GatewayConfig, Resource, Simulation } from '@/domain/types'
|
||||
import type { Process, Activity, GatewayConfig, Resource, Simulation, ProcessGroup, AppSettings } from '@/domain/types'
|
||||
|
||||
export class ProcessCostDb extends Dexie {
|
||||
processes!: Table<Process>
|
||||
@@ -7,6 +7,8 @@ export class ProcessCostDb extends Dexie {
|
||||
gateways!: Table<GatewayConfig>
|
||||
resources!: Table<Resource>
|
||||
simulations!: Table<Simulation>
|
||||
groups!: Table<ProcessGroup> // 🆕 Sprint 3
|
||||
settings!: Table<AppSettings> // 🆕 Sprint 3
|
||||
|
||||
constructor() {
|
||||
super('ProcessCostPlatform')
|
||||
@@ -79,6 +81,44 @@ export class ProcessCostDb extends Dexie {
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
// Sprint 3: agrega tabla de grupos (Process Library), tabla de settings,
|
||||
// campos groupId/tags en Process, y automatedAssignedResources en Activity.
|
||||
this.version(4)
|
||||
.stores({
|
||||
processes: 'id, name, updatedAt, groupId', // groupId indexado para queries por grupo
|
||||
activities: 'id, processId, bpmnElementId',
|
||||
gateways: 'id, processId, bpmnElementId',
|
||||
resources: 'id, processId, name',
|
||||
simulations: 'id, processId, executedAt',
|
||||
groups: 'id, name, updatedAt', // nueva tabla
|
||||
settings: 'id', // nueva tabla (singleton)
|
||||
})
|
||||
.upgrade(async (tx) => {
|
||||
// Process: agregar groupId (null) y tags ([])
|
||||
await tx.table('processes').toCollection().modify((proc) => {
|
||||
if (proc.groupId === undefined) proc.groupId = null
|
||||
if (!Array.isArray(proc.tags)) proc.tags = []
|
||||
})
|
||||
// Activity: agregar automatedAssignedResources ([])
|
||||
await tx.table('activities').toCollection().modify((act) => {
|
||||
if (!Array.isArray(act.automatedAssignedResources)) {
|
||||
act.automatedAssignedResources = []
|
||||
}
|
||||
})
|
||||
// Settings: crear singleton con defaults (put es idempotente por clave primaria)
|
||||
await tx.table('settings').put({ id: 'singleton', groupLabel: 'Cliente' })
|
||||
})
|
||||
|
||||
// Belt-and-suspenders: garantizar singleton en fresh installs y entornos de test.
|
||||
// El upgrade también lo crea; este handler cubre el caso en que la tabla sea nueva
|
||||
// pero el upgrade no haya persistido el put (conocido con fake-indexeddb).
|
||||
this.on('ready', async () => {
|
||||
const existing = await this.settings.get('singleton')
|
||||
if (!existing) {
|
||||
await this.settings.put({ id: 'singleton', groupLabel: 'Cliente' })
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -17,7 +17,7 @@
|
||||
*/
|
||||
|
||||
import { db } from './db'
|
||||
import type { Process, Activity, GatewayConfig, Resource, Simulation } from '@/domain/types'
|
||||
import type { Process, Activity, GatewayConfig, Resource, Simulation, ProcessGroup, AppSettings } from '@/domain/types'
|
||||
|
||||
// ─── Process ─────────────────────────────────────────────────────────────────
|
||||
|
||||
@@ -125,13 +125,56 @@ export const simulationRepo = {
|
||||
},
|
||||
|
||||
async getLatestByProcess(processId: string): Promise<Simulation | undefined> {
|
||||
return db.simulations
|
||||
// Ordena por executedAt descendente para retornar la simulación más reciente.
|
||||
// No se usa .last() porque el primary key es UUID (no ordenado cronológicamente).
|
||||
const all = await db.simulations
|
||||
.where('processId')
|
||||
.equals(processId)
|
||||
.last()
|
||||
.toArray()
|
||||
if (!all.length) return undefined
|
||||
return all.reduce((latest, sim) => sim.executedAt > latest.executedAt ? sim : latest)
|
||||
},
|
||||
|
||||
async getByProcess(processId: string): Promise<Simulation[]> {
|
||||
return db.simulations.where('processId').equals(processId).toArray()
|
||||
},
|
||||
}
|
||||
|
||||
// ─── ProcessGroup ─────────────────────────────────────────────────────────────
|
||||
|
||||
/** @internal Solo llamar desde src/store/library-store.ts */
|
||||
export const groupRepo = {
|
||||
async save(group: ProcessGroup): Promise<void> {
|
||||
await db.groups.put(group)
|
||||
},
|
||||
|
||||
async getAll(): Promise<ProcessGroup[]> {
|
||||
return db.groups.orderBy('name').toArray()
|
||||
},
|
||||
|
||||
async getById(id: string): Promise<ProcessGroup | undefined> {
|
||||
return db.groups.get(id)
|
||||
},
|
||||
|
||||
async delete(id: string): Promise<void> {
|
||||
// Procesos del grupo pasan a "Sin clasificar" (groupId = null)
|
||||
await db.transaction('rw', [db.groups, db.processes], async () => {
|
||||
await db.processes.where('groupId').equals(id).modify({ groupId: null })
|
||||
await db.groups.delete(id)
|
||||
})
|
||||
},
|
||||
}
|
||||
|
||||
// ─── AppSettings ──────────────────────────────────────────────────────────────
|
||||
|
||||
/** @internal Solo llamar desde src/store/library-store.ts */
|
||||
export const settingsRepo = {
|
||||
async get(): Promise<AppSettings> {
|
||||
const s = await db.settings.get('singleton')
|
||||
return s ?? { id: 'singleton', groupLabel: 'Cliente' }
|
||||
},
|
||||
|
||||
async update(updates: Partial<Omit<AppSettings, 'id'>>): Promise<void> {
|
||||
await db.settings.update('singleton', updates)
|
||||
},
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user