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

@@ -1,4 +1,4 @@
import { useNavigate } from '@tanstack/react-router'
import { useNavigate, useSearch } from '@tanstack/react-router'
import { useCallback, useState } from 'react'
import { UploadCloud, FileText, Loader2, FlaskConical } from 'lucide-react'
import { v4 as uuidv4 } from 'uuid'
@@ -9,6 +9,7 @@ import type { Process, Activity, GatewayConfig } from '@/domain/types'
import { bpmnTypeToGatewayType } from '@/domain/types'
import { useToast } from '@/components/ui/use-toast'
import { AppFooter } from '@/components/AppFooter'
import { AppHeader } from '@/components/AppHeader'
const SAMPLE_PROCESSES = [
{
@@ -38,6 +39,10 @@ export function ImportPage() {
const [isProcessing, setIsProcessing] = useState(false)
const [loadingSampleId, setLoadingSampleId] = useState<string | null>(null)
// groupId viene de la URL cuando el usuario importa desde dentro de un grupo
const search = useSearch({ from: '/import' })
const targetGroupId = (search as { groupId?: string }).groupId ?? null
const processFile = useCallback(async (file: File) => {
if (!file.name.endsWith('.bpmn') && !file.name.endsWith('.xml')) {
toast({
@@ -69,6 +74,8 @@ export function ImportPage() {
automationInvestment: 0,
createdAt: now,
updatedAt: now,
groupId: targetGroupId,
tags: [],
}
const activityElements = extractActivityElements(graph)
@@ -84,6 +91,7 @@ export function ImportPage() {
automatable: false,
automatedCostFixed: 0,
automatedTimeMinutes: 0,
automatedAssignedResources: [],
}))
const gatewayElements = extractGatewayElements(graph)
@@ -167,7 +175,9 @@ export function ImportPage() {
}
return (
<div className="min-h-screen bg-gradient-to-br from-slate-50 to-slate-100 flex flex-col items-center justify-center p-6">
<div className="min-h-screen bg-gradient-to-br from-slate-50 to-slate-100 flex flex-col">
<AppHeader />
<div className="flex-1 flex flex-col items-center justify-center p-6">
{/* Header */}
<div className="text-center mb-10">
<div className="inline-flex items-center gap-2 text-primary text-sm font-medium mb-4 bg-primary/10 px-3 py-1 rounded-full">
@@ -278,6 +288,7 @@ export function ImportPage() {
</p>
<AppFooter />
</div>
</div>
)
}