feat: MVP Fase 0 — Process Cost Platform v0.1.0

Plataforma completa de análisis de costos operativos basada en BPMN 2.0.

Funcionalidades incluidas:
- Import de archivos BPMN 2.0 por drag & drop + 3 procesos de ejemplo
- Visualización read-only del diagrama (bpmn-js)
- Configuración de actividades (costo, tiempo, recursos asignados)
- CRUD de recursos (rol, persona, sistema, equipo, insumo)
- Configuración global (moneda, overhead %, nombre, cliente)
- Motor de simulación determinístico agregado con propagación de gateways XOR/AND
- Reporte visual con mapa de calor en dos modos (relativo/absoluto)
- Gráficos: KPIs, top actividades, composición, costo por recurso
- Export PDF (jsPDF + html2canvas) y CSV (papaparse)
- Persistencia en IndexedDB (Dexie)
- 268 tests unitarios/integración + 6 E2E con Playwright
- Deploy: https://process-cost-platform.pages.dev

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-05-14 01:58:40 -03:00
commit bed161c92a
97 changed files with 19638 additions and 0 deletions

View File

@@ -0,0 +1,99 @@
import { create } from 'zustand'
import { processRepo, activityRepo, gatewayRepo, resourceRepo } from '@/persistence/repositories'
import type { Process, Activity, GatewayConfig, Resource } from '@/domain/types'
interface ProcessState {
currentProcess: Process | null
activities: Activity[]
gateways: GatewayConfig[]
resources: Resource[]
isLoading: boolean
error: string | null
// Acciones
loadProcess: (processId: string) => Promise<void>
setCurrentProcess: (process: Process) => Promise<void>
updateProcess: (updates: Partial<Process>) => Promise<void>
updateActivity: (activity: Activity) => Promise<void>
updateGateway: (gateway: GatewayConfig) => Promise<void>
addResource: (resource: Resource) => Promise<void>
updateResource: (resource: Resource) => Promise<void>
deleteResource: (resourceId: string) => Promise<void>
reset: () => void
}
export const useProcessStore = create<ProcessState>((set, get) => ({
currentProcess: null,
activities: [],
gateways: [],
resources: [],
isLoading: false,
error: null,
loadProcess: async (processId) => {
set({ isLoading: true, error: null })
try {
const [process, activities, gateways, resources] = await Promise.all([
processRepo.getById(processId),
activityRepo.getByProcess(processId),
gatewayRepo.getByProcess(processId),
resourceRepo.getByProcess(processId),
])
if (!process) throw new Error('Proceso no encontrado')
set({ currentProcess: process, activities, gateways, resources, isLoading: false })
} catch (err) {
set({ error: err instanceof Error ? err.message : 'Error desconocido', isLoading: false })
}
},
setCurrentProcess: async (process) => {
await processRepo.save(process)
const [activities, gateways, resources] = await Promise.all([
activityRepo.getByProcess(process.id),
gatewayRepo.getByProcess(process.id),
resourceRepo.getByProcess(process.id),
])
set({ currentProcess: process, activities, gateways, resources })
},
updateProcess: async (updates) => {
const current = get().currentProcess
if (!current) return
const updated: Process = { ...current, ...updates, updatedAt: Date.now() }
await processRepo.save(updated)
set({ currentProcess: updated })
},
updateActivity: async (activity) => {
await activityRepo.save(activity)
set((state) => ({
activities: state.activities.map((a) => (a.id === activity.id ? activity : a)),
}))
},
updateGateway: async (gateway) => {
await gatewayRepo.save(gateway)
set((state) => ({
gateways: state.gateways.map((g) => (g.id === gateway.id ? gateway : g)),
}))
},
addResource: async (resource) => {
await resourceRepo.save(resource)
set((state) => ({ resources: [...state.resources, resource] }))
},
updateResource: async (resource) => {
await resourceRepo.save(resource)
set((state) => ({
resources: state.resources.map((r) => (r.id === resource.id ? resource : r)),
}))
},
deleteResource: async (resourceId) => {
await resourceRepo.delete(resourceId)
set((state) => ({ resources: state.resources.filter((r) => r.id !== resourceId) }))
},
reset: () => set({ currentProcess: null, activities: [], gateways: [], resources: [], error: null }),
}))