PROCUREDATA

DOCUMENTO TÉCNICO - PROCUREDATA v3.2

Plataforma de Soberanía de Datos para Cadenas de Suministro

Versión: 3.2 (Registration + Onboarding System)
Fecha de Actualización: 13 Enero 2026
Clasificación: Documentación Técnica Interna
Estado: Production-Ready ✅ | EDC · Gaia-X ✅ | Onboarding ✅


Índice

  1. Changelog desde v3.1
  2. Visión General del Sistema
  3. Arquitectura de la Plataforma
  4. Componentes del Espacio de Datos (Gaia-X)
  5. Catálogo de Componentes Técnicos
  6. Hooks Personalizados
  7. Interfaces y Páginas Principales
  8. Personas de Usuario (Roles Técnicos)
  9. Modelo de Gobernanza Técnica
  10. Seguridad y Auditoría
  11. Casos de Uso Principales
  12. Mejoras de UX v3.1
  13. API de negocio
  14. Sistema de Registro y Onboarding (v3.2)
  15. Guía de Desarrollo
  16. Estado de Auditoría
  17. Anexos
  18. Historial de Versiones

1. Changelog desde v3.1

v3.2 - 13 Enero 2026

📝 Sistema de Registro y Onboarding

  • Tabla registration_requests: Nueva tabla para gestión de solicitudes de adhesión
    • Estados: pending, under_review, approved, rejected, needs_info
    • Validación de duplicados por tax_id
    • Campos para datos fiscales, representante legal, intenciones de uso
  • API de negocio submit-registration: Procesamiento de solicitudes de registro con:
    • Validación de payload con esquemas Zod
    • Detección de duplicados por CIF/NIF
    • Inserción en tabla registration_requests
    • Trigger de email de bienvenida
  • API de negocio send-welcome-email: Emails diferenciados por rol (Buyer/Supplier)
    • Plantilla Supplier: "Activa tu visibilidad" - enfoque en certificaciones
    • Plantilla Buyer: "Verificación de Seguridad" - enfoque en KYB
    • Soporte multiidioma: ES, EN, FR, PT, DE, IT, NL
    • Integración con Resend API
  • Hook useRegistration: Gestión de estado de envío de formularios con persistencia localStorage
  • Página /register: Formulario multi-paso con validación Zod en tiempo real

🔐 Sistema de Acceso Diferenciado

  • Acceso con cuenta: Usuarios no autenticados, que son llevados al acceso (/auth)
  • Modo Pending Setup: Usuarios registrados en proceso de onboarding
  • Modo Active: Usuarios con organización verificada y datos reales
  • Hook useUserAccessMode: Determina el modo de acceso del usuario actual

🌐 Internacionalización Completa

  • Nuevas traducciones en 7 idiomas (ES, EN, FR, PT, DE, IT, NL) para:
    • Página de registro (register.json)
    • Emails transaccionales
    • Onboarding dashboard
    • Mensajes de validación y errores

📊 Documentación Técnica v3.2

  • Documento Técnico: Actualizado a v3.2 con changelog, nuevos endpoints de la API
  • Capacidades Enterprise: Añadidas 2 nuevas capacidades (Onboarding KYB, Control de Acceso)
  • Documentos Explicativos: Actualizado Doc 12 con flujo de registro

v3.1 - 05 Enero 2026

🔗 Integración EDC y Gaia-X completa

  • ConnectorStatusWidget: Nuevo widget en Dashboard mostrando:
    • Pago en EUROe (en estudio)
    • Dirección did:ethr de la organización
    • DID verificado (did:ethr:0x7ecc:...)
    • Link a /evidencias
  • AuthContext híbrido: Fusión de autenticación Keycloak + wallet de la organización
  • Hook useOrgWallet: Gestión dedicada de conexión/desconexión wallet con auto-reconnect
  • Servicio edcConnectorService: Singleton para todas las operaciones con el conector EDC

📊 Capacidades Realtime

  • ActivityFeed: Suscripción a approval_history vía la API de ProcureData
  • Invalidación automática de queries con queryClient.invalidateQueries
  • Cleanup correcto con notifications.unsubscribe()

🎨 Mejoras de UX

  • Requests.tsx: Estados de carga individuales por botón (processingId)
  • Opportunities.tsx: AlertDialog de confirmación antes de propuestas
  • Auth.tsx: Validación Zod para email y contraseña
  • SettingsPreferences.tsx: Skeleton de carga durante fetch de preferencias
  • EmptyState component: Estados vacíos consistentes con iconos y acciones

🔒 Seguridad y Privacidad

  • usePrivacyPreferences hook: Persistencia de preferencias de privacidad
  • Updates optimistas con rollback en error
  • Campos: profile_visible, show_access_history, access_alerts, anonymous_research

📖 Documentación Interactiva

  • Página /architecture: 4 tabs interactivos con diagramas Mermaid renderizados
  • Componente MermaidDiagram.tsx: Renderizado de diagramas con soporte dark mode
  • Dependencia mermaid@11.12.2: Visualización de flujos y arquitectura

🛠 Infraestructura

  • Limpieza de logs de desarrollo en producción
  • Tipos de identidad en src/types/identity.types.ts
  • Extensión global de Wallet local (navegador)

2. Visión General del Sistema

2.1 Propósito

PROCUREDATA es una plataforma de soberanía de datos diseñada para facilitar el intercambio seguro y trazable de información empresarial entre organizaciones de la cadena de suministro. No almacena datos; gestiona acuerdos soberanos entre quienes tienen los datos y quienes los necesitan.

La plataforma implementa los principios del ecosistema Gaia-X y el estándar IDSA (International Data Spaces Association) para garantizar:

  • Soberanía de Datos: Los propietarios de los datos mantienen control total sobre quién accede a su información y bajo qué condiciones.
  • Trazabilidad Inmutable: Cada acceso y transacción queda registrado en el registro de evidencias firmado, creando un historial auditable.
  • Interoperabilidad: Compatibilidad con sistemas ERP existentes (SAP, Oracle, Salesforce) mediante conectores estandarizados.
  • Cumplimiento Normativo: Diseñado para satisfacer requisitos GDPR, CSRD, y regulaciones sectoriales.

2.2 Modelo Tripartito de Roles

PROCUREDATA implementa un modelo de tres roles fundamentales basado en el estándar IDSA:

1. Solicita dato
2. Autoriza uso legal
3. Libera acceso técnico

Consumer/Comprador

Provider/Proveedor

Data Holder/Custodio

RolResponsabilidadEjemplo en Cadena de Suministro
Data ConsumerSolicita acceso a datos para un propósito específicoEmpresa compradora que necesita validar proveedores
Data Subject (Provider)Propietario original de los datos, decide sobre su usoProveedor cuyos datos fiscales se solicitan
Data HolderCustodio técnico que almacena y entrega los datosAgencia tributaria, cámara de comercio, certificadora

2.3 Arquitectura: API de negocio + conectores EDC

PROCUREDATA utiliza una arquitectura híbrida que combina:

  • Capa Web2 (Cloud AI/PostgreSQL): Almacenamiento tradicional para datos de usuario.s, lógica de negocio, y APIs RESTful.
  • Capa de confianza (EDC, DIDs y registro de evidencias): Registro de evidencias firmado de las transacciones, verificación de identidad (DID) y facturación tradicional (EUROe en estudio).
┌─────────────────────────────────────────────────────────────────┐
│                        USUARIO FINAL                            │
│        (Navegador Web + wallet de la organización)              │
└─────────────────────────────────────────────────────────────────┘
                              │
                              ▼
┌─────────────────────────────────────────────────────────────────┐
│                   FRONTEND (aplicación web)                     │
│  ┌──────────────┐  ┌──────────────┐  ┌──────────────┐          │
│  │  Keycloak    │  │ Wallet org.  │  │ Catálogo y   │          │
│  │  (sesión)    │  │  (did:ethr)  │  │ solicitudes  │          │
│  └──────────────┘  └──────────────┘  └──────────────┘          │
└─────────────────────────────────────────────────────────────────┘
                              │
              ┌───────────────┴───────────────┐
              ▼                               ▼
┌─────────────────────────┐     ┌─────────────────────────┐
│   PLATAFORMA (portal)   │     │     CONECTORES EDC      │
│   (MyDataSpace)         │     │ (uno por organización)  │
│  ┌─────────────────┐    │     │  ┌─────────────────┐    │
│  │   PostgreSQL    │    │     │  │  Control Plane  │    │
│  │(API de negocio) │    │     │  │  (DSP · ODRL)   │    │
│  ├─────────────────┤    │     │  ├─────────────────┤    │
│  │    Keycloak     │    │     │  │   Data Plane    │    │
│  │  (OIDC · JWT)   │    │     │  │ (transferencia) │    │
│  ├─────────────────┤    │     │  ├─────────────────┤    │
│  │ Notario Gaia-X  │    │     │  │ Identidad DCP   │    │
│  │ y emisor de VCs │    │     │  │ (did:web · VC)  │    │
│  ├─────────────────┤    │     │  ├─────────────────┤    │
│  │   Registro de   │    │     │  │ Catálogo DCAT-AP│    │
│  │   evidencias    │    │     │  │   (federado)    │    │
│  └─────────────────┘    │     │  └─────────────────┘    │
└─────────────────────────┘     └─────────────────────────┘

2.4 Estado Actual

CaracterísticaEstado
Production-Ready✅
EDC · Gaia-X✅
Auditoría de seguridadCompletada
Acceso con cuenta propia✅ (datos sintéticos)
Tour guiado interactivo6 pasos

3. Arquitectura de la Plataforma

3.1 Stack Tecnológico Completo

Frontend

TecnologíaVersiónPropósito
React18.3.1Biblioteca de UI con hooks y concurrent mode
ViteLatestBundler y dev server con HMR instantáneo
TypeScript5.xTipado estático para seguridad en desarrollo
Tailwind CSS3.xUtilidades CSS con sistema de diseño tokenizado
Shadcn/uiLatest49 componentes accesibles basados en Radix UI
Framer Motion12.23.24Animaciones declarativas y transiciones
Lucide React0.462.0Iconografía SVG consistente
Recharts2.15.4Visualización de datos y gráficos
Mermaid11.12.2Diagramas y visualizaciones (NUEVO v3.1)

Estado y Data Fetching

TecnologíaVersiónPropósito
TanStack Query5.83.0Cache, sincronización y estado servidor
React ContextBuilt-inEstado global para auth y preferencias
React Hook Form7.61.1Gestión de formularios performante
Zod3.25.76Validación de esquemas en runtime

Backend (Cloud AI)

TecnologíaVersiónPropósito
PostgreSQL15.xBase de datos relacional con aislamiento por organización (28 tablas)
KeycloakLatestAutenticación JWT con múltiples providers
API de negocioDenoServerless functions para lógica backend
RealtimeWebSocketsSuscripciones a cambios en tiempo real
StorageS3-compatibleAlmacenamiento de archivos

Capa de confianza (Trust Layer)

TecnologíaVersiónPropósito
Ethers.js6.16.0Interacción con conectores EDC (DSP)
Conectores EDC (OVH, UE)Chain 0x7ECC (32460)Conectores EDC del ecosistema MyDataSpace
MetaMask (importar wallet)LatestWallets compatibles (importación)
EUROe (en estudio)ERC-20 (en estudio)Stablecoin para pagos en plataforma
DID (did:ethr)W3C StandardIdentificadores descentralizados

Utilidades

TecnologíaVersiónPropósito
date-fns3.6.0Manipulación de fechas
jsPDF3.0.4Generación de documentos PDF
React Joyride2.9.3Tours guiados de onboarding
Sonner1.7.4Sistema de notificaciones toast

3.2 Diagrama de Arquitectura de Alto Nivel

Layer Trust - EDC · Gaia-X

Portal ProcureData - API y Keycloak

Middleware Layer

Frontend Layer

React + Vite + Tailwind

Shadcn/ui Components

TanStack Query + Context

useAuth Context

Wallet de la organización

React Query Client

Keycloak

PostgreSQL 28 Tables

API de negocio

Notificaciones

Conectores EDC (OVH, UE)

DID Registry

EUROe (en estudio)

Registro de evidencias

3.3 Flujo de Datos Principal

Registro de evidenciasDatabaseHolderSubject (Provider)ConsumerRegistro de evidenciasDatabaseHolderSubject (Provider)ConsumerCrea transacción (initiated)Notifica solicitudAprueba (pending_holder)Notifica para liberaciónLibera datos (completed)Notariza hash de accesoDatos disponiblesPrueba verificable

3.4 Máquina de Estados de Transacciones

Consumer envía

Subject aprueba

Subject rechaza

Holder aprueba

Holder rechaza

Consumer cancela

initiated

pending_subject

pending_holder

denied_subject

completed

denied_holder

cancelled


4. Componentes del Espacio de Datos (Gaia-X)

4.1 Identidad Soberana (Self-Sovereign Identity - SSI)

PROCUREDATA implementa Identificadores Descentralizados (DIDs) siguiendo el estándar W3C DID Core 1.0, específicamente el método did:ethr para generar la wallet de la organización (did:ethr).

Generación de DID

El servicio edcConnectorService genera DIDs automáticamente al conectar una wallet:

typescript
// src/services/pontusX.ts
generateDID(address: string): string {
  // Formato estándar: did:ethr:<chainId>:<ethereumAddress>
  return `did:ethr:${PONTUSX_NETWORK_CONFIG.chainId}:${address.toLowerCase()}`;
}

// Resultado ejemplo:
// did:ethr:0x7ecc:0x742d35cc6634c0532925a3b844bc9e7595f8fe00

Estructura del DID

ComponenteValorDescripción
SchemedidPrefijo estándar W3C
MethodethrMétodo Ethereum DID
Network0x7eccDID del espacio (clave de firma de evidencias)
Identifier0x742d...Dirección Ethereum de la wallet

Tipos TypeScript para SSI

typescript
// src/types/web3.types.ts
export interface DecentralizedIdentifier {
  did: string;           // Formato: did:ethr:0x7ecc:0x...
  address: string;       // Dirección Ethereum 0x...
  chainId: string;       // DID en Hex (ej: 0x7ecc)
  verified: boolean;     // Estado de verificación local
}

export interface VerifiableCredential {
  id: string;
  type: string[];
  issuer: string;
  issuanceDate: string;
  expirationDate?: string;
  credentialSubject: {
    id: string;
    [key: string]: unknown;
  };
  proof?: {
    type: string;
    created: string;
    verificationMethod: string;
    proofPurpose: string;
    proofValue: string;
  };
}

4.2 Conector de Datos (Data Connector)

El servicio EdcConnectorService actúa como el conector de datos que facilita la comunicación entre la aplicación y el conector EDC.

Clase IdentityService Completa

typescript
// src/services/pontusX.ts
import { ethers } from 'ethers';
import type { WalletState } from '@/types/web3.types';

export const PONTUSX_NETWORK_CONFIG = {
  chainId: '0x7ecc',           // 32460 en decimal
  chainName: 'Eclipse EDC Testnet',
  nativeCurrency: {
    name: 'Eclipse EDC Token',
    symbol: 'GX',
    decimals: 18,
  },
  rpcUrls: ['https://rpc.dev.pontus-x.eu'],
  blockExplorerUrls: ['https://procuredata.ai/evidencias/'],
};

const EUROE_CONTRACT_ADDRESS = "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48";

const ERC20_ABI = [
  "function balanceOf(address owner) view returns (uint256)",
  "function decimals() view returns (uint8)",
  "function symbol() view returns (string)",
  "function transfer(address to, uint amount) returns (bool)"
];

class Eclipse EDCXService {
  private provider: ethers.BrowserProvider | null = null;
  private signer: ethers.JsonRpcSigner | null = null;
  private euroeContract: ethers.Contract | null = null;

  constructor() {
    if (typeof window !== 'undefined' && window.ethereum) {
      this.provider = new ethers.BrowserProvider(window.ethereum);
    }
  }

  // Métodos principales
  isWeb3Available(): boolean
  async connectWallet(): Promise<WalletState>
  disconnect(): WalletState
  async switchNetwork(): Promise<void>
  async getEUROeBalance(address: string): Promise<string>
  generateDID(address: string): string
  async signMessage(message: string): Promise<string>
  async revokeAccess(did: string, resourceId: string): Promise<string>
  onAccountsChanged(callback: (accounts: string[]) => void): void
  onChainChanged(callback: (chainId: string) => void): void
  removeListeners(): void
}

// Exportar una instancia Singleton
export const pontusXService = new Eclipse EDCXService();

Métodos del Servicio

MétodoDescripciónRetorno
hasOrgWallet()Detecta wallet instaladaboolean
connectWallet()Conecta + switch red + balancesWalletState
disconnect()Limpia estado de walletWalletState
switchNetwork()No requiere cambio de redvoid
getInvoiceStatus(invoiceId)Lee el estado de los acuerdos de contrato EDCstring
generateDID(address)Genera did:ethr:chainId:addressstring
signMessage(message)Firma SIWE para verificaciónstring
revokeAccess(did, resourceId)Ejecuta la revocación en el conector EDCstring (txHash)

4.3 Catálogo Federado

El catálogo de PROCUREDATA implementa la especificación DCAT (Data Catalog Vocabulary) adaptada para espacios de datos industriales.

Vista Materializada: marketplace_listings

sql
CREATE VIEW marketplace_listings AS
SELECT 
  da.id AS asset_id,
  dp.id AS product_id,
  dp.name AS product_name,
  dp.description AS product_description,
  dp.category,
  dp.version,
  da.price,
  da.currency,
  da.pricing_model,
  da.billing_period,
  o.id AS provider_id,
  o.name AS provider_name,
  o.kyb_verified,
  o.seller_category,
  esg.energy_renewable_percent,
  -- Badges calculados
  (esg.energy_renewable_percent >= 80) AS has_green_badge,
  -- Métricas de reputación
  (SELECT AVG(rating) FROM organization_reviews WHERE target_org_id = o.id) AS reputation_score,
  (SELECT COUNT(*) FROM organization_reviews WHERE target_org_id = o.id) AS review_count
FROM data_assets da
JOIN data_products dp ON da.product_id = dp.id
JOIN organizations o ON da.subject_org_id = o.id
LEFT JOIN esg_reports esg ON o.id = esg.organization_id
WHERE da.is_public_marketplace = true AND da.status = 'active';

Funcionalidades del Catálogo

FuncionalidadImplementación
Vista Grid/ListaToggle con estado local
Filtro por categoríaDropdown con enum de categorías
Filtro por precioSlider con rango min/max
Badges ESGhas_green_badge de la vista
Badge KYBkyb_verified de organizations
ComparaciónCheckbox multi-selección
WishlistTabla user_wishlist

4.4 Contratos de Uso (Usage Policies - ODRL)

PROCUREDATA genera políticas de uso siguiendo el estándar ODRL (Open Digital Rights Language) almacenadas en la tabla data_policies.

Estructura de Política ODRL

json
{
  "@context": "http://www.w3.org/ns/odrl/2/",
  "@type": "Agreement",
  "uid": "policy:procuredata:550e8400-e29b...",
  "profile": "http://example.org/gaiax-profile",
  "permission": [{
    "target": "asset:procuredata:33333333-...",
    "action": "use",
    "constraint": [{
      "leftOperand": "dateTime",
      "operator": "lteq",
      "rightOperand": "2026-04-05T00:00:00Z"
    }],
    "duty": [{
      "action": "compensate",
      "constraint": [{
        "leftOperand": "payAmount",
        "operator": "eq",
        "rightOperand": { "@value": "500", "@type": "xsd:decimal" },
        "unit": "EUR"
      }]
    }]
  }],
  "prohibition": [{
    "target": "asset:procuredata:33333333-...",
    "action": "transfer"
  }]
}

Tabla data_policies

sql
CREATE TABLE data_policies (
  id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
  transaction_id UUID REFERENCES data_transactions(id),
  odrl_policy_json JSONB NOT NULL,
  generated_at TIMESTAMPTZ DEFAULT now()
);

5. Catálogo de Componentes Técnicos

5.1 Componentes Core/EDC

ComponenteArchivoResponsabilidad TécnicaVersión
LoginFormsrc/components/WalletButton.tsxGestión completa de sesión: acceso con Keycloak, visualización de perfil (DID, credenciales verificables), dropdown con acciones. Usa useOrgWallet hook.v3.0
ConnectorStatusWidgetsrc/components/IdentityStatusWidget.tsxWidget de dashboard que muestra estado de wallet, balances, y DID verificado. Tres estados: sin wallet detectada, no conectada, conectada.v3.1
RevokeAccessButtonsrc/components/RevokeAccessButton.tsxKill-switch para revocación de acceso a recursos. AlertDialog de confirmación con advertencia de irreversibilidad. Llama a edcService.revokeAccess().v3.0

Estados del IdentityStatusWidget

  1. Sin identidad descentralizada: Muestra botón para importar la wallet en MetaMask
  2. No conectado: Muestra botón "Conectar Wallet"
  3. Conectado: Muestra:
    • EUROe (en estudio)
    • Identidad did:ethr
    • DID truncado con botón copiar
    • Link a /evidencias

5.2 Componentes Feature/Audit

ComponenteArchivoResponsabilidad Técnica
DataLineageEvidencesrc/components/DataLineageEvidence.tsxTimeline visual de eventos de trazabilidad. Muestra hashes del registro de evidencias, timestamps, actores. Botón para copiar hash y link a la verificación pública en /evidencias.
DataLineagesrc/components/DataLineage.tsxVisualización de lineage de datos desde PostgreSQL. Complementa el registro de evidencias con la trazabilidad firmada de cada intercambio.
SmartContractViewersrc/components/SmartContractViewer.tsxVisor de acuerdos de contrato EDC y su política ODRL. Permite inspeccionar permisos y obligaciones.

5.3 Componentes Feature/Commerce

ComponenteArchivoResponsabilidad Técnica
Facturaciónsrc/components/PaymentGateway.tsxPasarela de pagos con 3 tabs: Tarjeta (Stripe), Wallet (EUROe, en estudio), Transferencia. Integración con edcConnectorService para pagos crypto.
OrderSummarysrc/components/OrderSummary.tsxResumen de pedido con desglose de precios, impuestos, y total. Soporta múltiples monedas.

5.4 Componentes Core/UX

ComponenteArchivoResponsabilidad TécnicaVersión
NotificationsBellsrc/components/NotificationsBell.tsxConsumidor de notificaciones en tiempo real de la API. Muestra contador de no leídas, dropdown con lista, mark as read.v3.0
ActivityFeedsrc/components/ActivityFeed.tsxFeed de actividad con suscripción Realtime a approval_history. Invalidación automática de queries. Cleanup correcto con removeChannel().v3.1
EmptyStatesrc/components/EmptyState.tsxEstados vacíos consistentes con iconos y acciones. Reutilizable en múltiples páginas.v3.1
DemoBannersrc/components/DemoBanner.tsxAviso para cuentas pendientes de aprobación, con los siguientes pasos.v3.0
DemoToursrc/components/DemoTour.tsxTour guiado con React Joyride. Steps configurables por página.v3.0
CommandMenusrc/components/CommandMenu.tsxPaleta de comandos estilo Spotlight/Alfred. Navegación rápida y acciones.v3.0

5.5 Componentes UI/Layout

ComponenteArchivoResponsabilidad Técnica
AppLayoutsrc/components/AppLayout.tsxLayout principal con Sidebar, Header, y área de contenido. Gestiona responsive.
AppSidebarsrc/components/AppSidebar.tsxNavegación lateral con secciones colapsables. Integra OrganizationSwitcher.
DynamicBreadcrumbssrc/components/DynamicBreadcrumbs.tsxMigas de pan generadas dinámicamente desde la ruta actual.
ThemeTogglesrc/components/ThemeToggle.tsxSwitch de tema claro/oscuro con persistencia en localStorage.
ProtectedRoutesrc/components/ProtectedRoute.tsxHOC que verifica autenticación y redirige a /auth si no hay sesión.
MermaidDiagramsrc/components/MermaidDiagram.tsxRenderizado de diagramas Mermaid con soporte dark mode. NUEVO v3.1

5.6 Componentes Feature/Data

ComponenteArchivoResponsabilidad Técnica
ESGDataViewsrc/components/ESGDataView.tsxVisualización de datos ESG: emisiones scope 1/2, mix energético, certificaciones. Gráficos con Recharts.
IoTDataViewsrc/components/IoTDataView.tsxRenderizado de datos de telemetría IoT. Series temporales y alertas.
ArrayDataViewsrc/components/ArrayDataView.tsxVista genérica para arrays de datos. Tabla paginada con sorting.
GenericJSONViewsrc/components/GenericJSONView.tsxVisor de JSON con syntax highlighting y collapse/expand.

5.7 Componentes Feature/Collaboration

ComponenteArchivoResponsabilidad Técnica
NegotiationChatsrc/components/NegotiationChat.tsxChat embebido para negociación de contratos. Mensajes almacenados en transaction_messages.
TeamManagementsrc/components/TeamManagement.tsxGestión de miembros del equipo y roles. CRUD sobre user_roles.
AIConciergesrc/components/AIConcierge.tsxAsistente virtual con IA para guiar usuarios. Integración con Cloud AI Gateway.

5.8 Componentes Shadcn/UI (49 componentes)

Directorio: src/components/ui/

ComponentePropósito
accordion, alert, alert-dialogContenedores de información expandibles
avatar, badge, buttonElementos de identidad e interacción
calendar, card, carouselVisualización de datos
checkbox, collapsible, commandControles de formulario
context-menu, dialog, drawerMenús y modales
dropdown-menu, form, hover-cardInteracciones complejas
input, input-otp, labelCampos de entrada
menubar, navigation-menuNavegación
pagination, popover, progressNavegación y feedback
radio-group, resizable, scroll-areaControles avanzados
select, separator, sheetSelección y layout
sidebar, skeleton, sliderLayout y estados de carga
sonner, switch, tableNotificaciones, toggles, tablas
tabs, textarea, toast, toasterOrganización y notificaciones
toggle, toggle-group, tooltipInteracciones menores

6. Hooks Personalizados

6.1 useAuth - Autenticación Híbrida

typescript
// src/hooks/useAuth.tsx
interface AuthContextType {
  // API ProcureData Auth
  user: User | null;
  session: Session | null;
  loading: boolean;
  signUp: (email: string, password: string) => Promise<AuthResponse>;
  signIn: (email: string, password: string) => Promise<AuthResponse>;
  signOut: () => Promise<void>;
  
  // Web3 Identity (NUEVO v3.1)
  walletAddress: string | null;
  did: string | null;
  isWeb3Connected: boolean;
  connectWallet: (silent?: boolean) => Promise<void>;
  disconnectWallet: () => void;
}

Flujo de Autenticación

Wallet de la organizaciónPostgreSQLAuth PageUsuarioWallet de la organizaciónPostgreSQLAuth PageUsuarioopt[Conexión EDC]Introduce email/passwordsignIn(email, password)Session tokenRedirect a DashboardClick "Iniciar sesión"Fichero cifrado de la walletaddress, chainIdGenera DIDSesión iniciada

6.2 useWeb3Wallet - Gestión de Wallet (NUEVO v3.1)

typescript
// src/hooks/useWeb3Wallet.tsx
import { useState, useEffect, useCallback } from 'react';
import { pontusXService } from '@/services/pontusX';
import type { WalletState } from '@/types/web3.types';
import { toast } from 'sonner';

const INITIAL_STATE: WalletState = {
  address: null,
  chainId: null,
  balance: null,
  euroeBalance: null,
  did: null,
  isConnected: false
};

export const useWeb3Wallet = () => {
  const [wallet, setWallet] = useState<WalletState>(INITIAL_STATE);
  const [isConnecting, setIsConnecting] = useState(true);
  const [hasWeb3, setHasWeb3] = useState(false);

  // Check Web3 availability on mount
  useEffect(() => {
    setHasWeb3(pontusXService.isWeb3Available());
  }, []);

  const connect = useCallback(async (silent = false) => {
    if (!silent) setIsConnecting(true);
    try {
      const state = await pontusXService.connectWallet();
      setWallet(state);
      if (!silent) {
        toast.success("Billetera Conectada", {
          description: `Cuenta: ${state.address?.slice(0, 6)}...${state.address?.slice(-4)}`
        });
      }
    } catch (error: unknown) {
      const message = error instanceof Error ? error.message : "Error desconocido";
      console.error("Connection failed:", error);
      if (!silent) {
        toast.error("Error al conectar", { description: message });
      }
    } finally {
      setIsConnecting(false);
    }
  }, []);

  const disconnect = useCallback(() => {
    pontusXService.disconnect();
    setWallet(INITIAL_STATE);
    toast.info("Desconectado", {
      description: "Has cerrado la sesión de tu wallet."
    });
  }, []);

  // Auto-connection and event listeners
  useEffect(() => {
    const checkConnection = async () => {
      if (window.ethereum) {
        try {
          const accounts = await window.ethereum.request({ 
            method: 'eth_accounts' 
          }) as string[];
          if (accounts.length > 0) {
            await connect(true); // Silently reconnect
          }
        } catch (err) {
          console.error("Error checking accounts:", err);
        }
      }
      setIsConnecting(false);
    };

    checkConnection();

    // Setup MetaMask event listeners
    if (window.ethereum) {
      const handleAccountsChanged = (accounts: unknown) => {
        const accountList = accounts as string[];
        if (accountList.length > 0) {
          connect(true);
        } else {
          setWallet(INITIAL_STATE);
          toast.info("Desconexión detectada en MetaMask");
        }
      };

      const handleChainChanged = () => {
        window.location.reload();
      };

      window.ethereum.on('accountsChanged', handleAccountsChanged);
      window.ethereum.on('chainChanged', handleChainChanged);

      return () => {
        window.ethereum?.removeListener('accountsChanged', handleAccountsChanged);
        window.ethereum?.removeListener('chainChanged', handleChainChanged);
      };
    }
  }, [connect]);

  return { 
    wallet, 
    isConnecting, 
    hasWeb3, 
    connect: () => connect(false), 
    disconnect 
  };
};

Retorno del Hook

PropiedadTipoDescripción
walletWalletStateEstado completo de la wallet
isConnectingbooleanIndicador de conexión en progreso
hasWalletbooleanSi la organización tiene wallet
connect() => Promise<void>Función para conectar wallet
disconnect() => voidFunción para desconectar

6.3 usePrivacyPreferences - Persistencia Optimista (NUEVO v3.1)

typescript
// src/hooks/usePrivacyPreferences.tsx
interface UserPreferences {
  profile_visible: boolean;
  show_access_history: boolean;
  access_alerts: boolean;
  anonymous_research: boolean;
}

const DEFAULT_PREFERENCES: UserPreferences = {
  profile_visible: true,
  show_access_history: true,
  access_alerts: true,
  anonymous_research: false,
};

export function usePrivacyPreferences() {
  const { user } = useAuth();
  const [preferences, setPreferences] = useState<UserPreferences>(DEFAULT_PREFERENCES);
  const [loading, setLoading] = useState(true);

  // Load preferences
  useEffect(() => {
    if (!user?.id) return;
    
    const loadPreferences = async () => {
      const { data } = await supabase
        .from('privacy_preferences')
        .select('*')
        .eq('user_id', user.id)
        .single();
      
      if (data) setPreferences(data);
      setLoading(false);
    };

    loadPreferences();
  }, [user?.id]);

  // Update with optimistic rollback
  const updatePreference = useCallback(async (
    key: keyof UserPreferences, 
    value: boolean
  ) => {
    const previousValue = preferences[key];
    
    // Optimistic update
    setPreferences(prev => ({ ...prev, [key]: value }));

    try {
      const { error } = await supabase
        .from('privacy_preferences')
        .update({ [key]: value, updated_at: new Date().toISOString() })
        .eq('user_id', user?.id);

      if (error) throw error;
      toast.success('Preferencia actualizada');
    } catch (error) {
      // Rollback on error
      setPreferences(prev => ({ ...prev, [key]: previousValue }));
      toast.error('Error al actualizar preferencia');
    }
  }, [user?.id, preferences]);

  return { preferences, loading, updatePreference };
}

Campos de Preferencias

CampoTipoDescripción
profile_visiblebooleanPerfil visible en marketplace
show_access_historybooleanMostrar historial de accesos
access_alertsbooleanNotificaciones de acceso a datos
anonymous_researchbooleanPermitir uso anónimo para investigación

6.4 useOrganizationContext - Multi-tenant

typescript
// src/hooks/useOrganizationContext.tsx
interface OrganizationContextType {
  currentOrg: Organization | null;
  organizations: Organization[];
  switchOrganization: (orgId: string) => void;
  isLoading: boolean;
}

6.5 useNotifications - Sistema de Notificaciones

typescript
// src/hooks/useNotifications.tsx
interface UseNotificationsReturn {
  notifications: Notification[];
  unreadCount: number;
  markAsRead: (id: string) => Promise<void>;
  markAllAsRead: () => Promise<void>;
  isLoading: boolean;
}

6.6 useOrgSector - Sector de Organización

typescript
// src/hooks/useOrgSector.tsx
// Determina el sector de la organización actual para renderizar iconos y UI contextual

7. Interfaces y Páginas Principales

7.1 Mapa de Rutas Completo (27 rutas)

Rutas Públicas

RutaComponenteDescripción
/Landing.tsxPágina de marketing con hero, features, success stories
/authAuth.tsxLogin/Registro con validación Zod
/guideGuide.tsxGuía del ecosistema
/architectureArchitecture.tsxDocumentación técnica interactiva con 4 tabs y Mermaid (v3.1)
/whitepaperInteractiveWhitepaper.tsxTech Whitepaper interactivo

Rutas Protegidas (AppLayout)

RutaComponenteDescripción
/dashboardDashboard.tsxCentro de mando + ConnectorWidget + ActivityFeed
/catalogCatalog.tsxMarketplace federado
/catalog/product/:idProductDetail.tsxDetalle de producto con muestra y solicitud
/requestsRequests.tsxLista de transacciones con estados y loading states
/requests/newRequestWizard.tsxWizard de 5 pasos para crear solicitud
/dataData.tsxLista de activos de datos adquiridos
/data/view/:idDataView.tsxVista de consumo con DataLineage y exportación
/opportunitiesOpportunities.tsxOportunidades de mercado con AlertDialog
/servicesServices.tsxCatálogo de servicios de valor añadido
/reportsReports.tsxGeneración y descarga de informes
/sustainabilitySustainability.tsxDashboard ESG con métricas ambientales
/innovationInnovationLab.tsxConceptos de innovación y análisis predictivo
/analyticsSellerAnalytics.tsxAnalytics para vendedores de datos
/notificationsNotifications.tsxCentro de notificaciones con historial
/settingsSettings.tsxHub de configuración
/settings/organizationSettingsOrganization.tsxConfiguración de organización + Team
/settings/preferencesSettingsPreferences.tsxPreferencias de privacidad con hook optimista (v3.1)
/settings/webhooksWebhookSettings.tsxConfiguración de webhooks
/settings/erp-configERPConfig.tsxIntegración con sistemas ERP
/settings/auditAuditLogs.tsxLogs de auditoría con filtros
*NotFound.tsxPágina 404 con navegación

7.2 Páginas Clave Detalladas

Dashboard (/dashboard)

Propósito: Centro de mando principal con KPIs y accesos rápidos.

Componentes v3.1:

  • ConnectorStatusWidget - Estado de wallet y balances
  • ActivityFeed - Feed con Realtime subscriptions
  • DashboardStats - Tarjetas de estadísticas

Architecture (/architecture) - NUEVO v3.1

Propósito: Documentación técnica interactiva.

Estructura:

  • 4 tabs: Base de Datos, Seguridad y aislamiento, Tech Stack, Flujos de Datos
  • Diagramas Mermaid renderizados con soporte dark mode
  • Cards explicativas con código de ejemplo

DataView (/data/:id)

Propósito: Visualización de datos adquiridos con auditoría integrada.

Componentes utilizados:

  • ESGDataView / IoTDataView / GenericJSONView (según tipo de datos)
  • DataLineageEvidence (trazabilidad)
  • RevokeAccessButton (revocación)

Funcionalidades:

typescript
// Exportación a ERP
const handleExportERP = async () => {
  await supabase.functions.invoke('erp-data-uploader', {
    body: { transactionId, configId: selectedConfig }
  });
};

// Descarga CSV
const handleDownloadCSV = () => {
  const csv = convertToCSV(data);
  downloadFile(csv, `data-${transactionId}.csv`);
};

RequestWizard (/requests/new)

Propósito: Flujo guiado de 5 pasos para crear solicitudes de datos.

Pasos del wizard:

  1. Selección de Producto: Grid de productos disponibles
  2. Propósito y Justificación: Formulario con validación Zod
  3. Duración de Acceso: Selector de días (30/60/90/180/365)
  4. Revisión de Política: Vista previa de ODRL generado
  5. Confirmación y Pago: PaymentGateway integrado

Persistencia: Auto-guardado en localStorage para recuperar sesión.

SettingsPreferences (/settings/preferences)

Propósito: Panel de control de privacidad del usuario.

Hook utilizado: usePrivacyPreferences (v3.1)

typescript
const { preferences, updatePreference, loading } = usePrivacyPreferences();

// Toggle con persistencia optimista
<Switch
  checked={preferences.profile_visible}
  onCheckedChange={(checked) => updatePreference('profile_visible', checked)}
/>

8. Personas de Usuario (Roles Técnicos)

8.1 Sistema de Roles

PROCUREDATA implementa un sistema de roles basado en la tabla user_roles con aislamiento por organización en la API.

Enum de Roles

sql
CREATE TYPE public.app_role AS ENUM (
  'admin',          -- Control total de la organización
  'approver',       -- Puede aprobar/rechazar transacciones
  'viewer',         -- Solo lectura
  'api_configurator' -- Gestión de integraciones ERP
);

Tabla user_roles

sql
CREATE TABLE public.user_roles (
  id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
  user_id UUID REFERENCES auth.users(id) ON DELETE CASCADE,
  organization_id UUID REFERENCES organizations(id),
  role app_role NOT NULL,
  created_at TIMESTAMPTZ DEFAULT now(),
  UNIQUE (user_id, organization_id, role)
);

Función de Verificación

sql
CREATE FUNCTION public.has_role(_user_id UUID, _organization_id UUID, _role app_role)
RETURNS BOOLEAN
LANGUAGE SQL STABLE SECURITY DEFINER
SET search_path = public
AS $$
  SELECT EXISTS (
    SELECT 1 FROM public.user_roles
    WHERE user_id = _user_id
      AND organization_id = _organization_id
      AND role = _role
  )
$$;

8.2 Roles por Tipo de Organización

Tipo de OrgRol TípicoPermisos Principales
Consumerviewer, approverVer catálogo, crear solicitudes, aprobar pagos
Provider (Subject)admin, approverGestionar activos, aprobar/rechazar solicitudes
Data Holderadmin, api_configuratorCustodia de datos, configurar ERPs, segunda aprobación

8.3 Flujos por Rol

Data Consumer

Catalog

ProductDetail

RequestWizard

Facturación

Requests - Pendiente

DataView - Tras aprobación

Data Provider (Subject)

Aprobar

Rechazar

Requests

Evaluar solicitud

pre_approve

denied_subject

Esperar Holder

Data Holder

Aprobar

Rechazar

Requests - pending_holder

Evaluar aprobación Subject

approved

denied_holder

Liberar datos


9. Modelo de Gobernanza Técnica

9.1 Políticas de Acceso

Flujo de Aprobación Multi-Etapa

Consumer crea solicitud

Enviado a Subject

Subject pre-aprueba

Subject rechaza

Holder aprueba

Holder rechaza

Datos entregados

Consumer cancela

initiated

pending_subject

pending_holder

denied_subject

approved

denied_holder

completed

cancelled

Estados de Transacción

sql
CREATE TYPE public.transaction_status AS ENUM (
  'initiated',       -- Solicitud creada
  'pending_subject', -- Esperando aprobación del propietario
  'pending_holder',  -- Esperando aprobación del custodio
  'approved',        -- Aprobada por todos
  'denied_subject',  -- Rechazada por propietario
  'denied_holder',   -- Rechazada por custodio
  'completed',       -- Datos entregados
  'cancelled'        -- Cancelada por solicitante
);

Tabla approval_history

sql
CREATE TABLE public.approval_history (
  id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
  transaction_id UUID REFERENCES data_transactions(id),
  actor_org_id UUID REFERENCES organizations(id),
  actor_user_id UUID,
  action approval_action NOT NULL, -- 'pre_approve' | 'approve' | 'deny' | 'cancel'
  notes TEXT,
  created_at TIMESTAMPTZ DEFAULT now()
);

9.2 Mecanismo de Consentimiento

El hook usePrivacyPreferences gestiona las preferencias de privacidad con persistencia optimista. Ver Sección 6.3.

9.3 Revocación de Acceso

Componente RevokeAccessButton

typescript
// src/components/RevokeAccessButton.tsx
const handleRevoke = async () => {
  if (!wallet.isConnected) {
    await connect();
    return;
  }

  setIsRevoking(true);
  try {
    const txHash = await pontusXService.revokeAccess(wallet.did!, resourceId);
    toast.success('Acceso revocado', {
      description: `Transacción: ${txHash.slice(0, 10)}...`
    });
    onRevoked?.(txHash);
  } catch (error) {
    toast.error('Error al revocar acceso');
  } finally {
    setIsRevoking(false);
    setDialogOpen(false);
  }
};

Flujo de Revocación

  1. Usuario hace click en "Revocar Acceso"
  2. AlertDialog solicita confirmación explícita
  3. Si wallet no conectada, se solicita conexión
  4. edcService.revokeAccess(did, resourceId) ejecuta transacción
  5. Hash de transacción se registra para auditoría
  6. UI se actualiza reflejando revocación

10. Seguridad y Auditoría

10.1 Autenticación Dual

PROCUREDATA implementa un modelo de autenticación dual que combina:

Keycloak (cuentas propias)

typescript
// src/hooks/useAuth.tsx
const signIn = async (email: string, password: string) => {
  const { data, error } = await supabase.auth.signInWithPassword({
    email,
    password,
  });
  if (error) throw error;
  return data;
};

// JWT almacenado en localStorage por API ProcureData SDK
// Refresh automático antes de expiración

Cuenta Keycloak (OIDC)

typescript
// src/services/pontusX.ts
async signMessage(message: string): Promise<string> {
  if (!this.signer) throw new Error('Wallet not connected');
  
  // Sign-In With Ethereum (SIWE) message
  const siweMessage = `
    PROCUREDATA Authentication
    
    I authorize this application to access my wallet.
    
    Nonce: ${Date.now()}
    Message: ${message}
  `;
  
  return await this.signer.signMessage(siweMessage);
}

Validación Zod (Auth.tsx) - NUEVO v3.1

typescript
const authSchema = z.object({
  email: z
    .string()
    .trim()
    .min(1, "El email es obligatorio")
    .email("Introduce un email válido")
    .max(255, "Email demasiado largo"),
  password: z
    .string()
    .min(6, "La contraseña debe tener al menos 6 caracteres")
    .max(72, "Contraseña demasiado larga"),
});

10.2 Aislamiento por organización

Políticas Implementadas

sql
-- Ejemplo: Transacciones visibles solo para organizaciones involucradas
CREATE POLICY "Users can view transactions of their organization"
ON public.data_transactions
FOR SELECT
USING (
  consumer_org_id = public.get_user_organization(auth.uid())
  OR subject_org_id = public.get_user_organization(auth.uid())
  OR holder_org_id = public.get_user_organization(auth.uid())
);

-- Ejemplo: Solo admins pueden modificar configuraciones
CREATE POLICY "Only admins can update organization settings"
ON public.organizations
FOR UPDATE
USING (
  public.has_role(auth.uid(), id, 'admin')
);

Funciones Security Definer

sql
CREATE FUNCTION public.get_user_organization(_user_id UUID)
RETURNS UUID
LANGUAGE SQL STABLE SECURITY DEFINER
SET search_path = public
AS $$
  SELECT organization_id
  FROM public.user_profiles
  WHERE user_id = _user_id
  LIMIT 1
$$;

10.3 Trazabilidad End-to-End

EvidenciasEclipse EDCPostgreSQLFrontendUsuarioEvidenciasEclipse EDCPostgreSQLFrontendUsuarioAcción (aprobar/revocar)INSERT approval_historyOK + timestampRegistrar hash de eventotx_hashUPDATE metadata contx_hashOKConfirmación con linkVerificar transacciónDetalles inmutables

10.4 Audit Logs

sql
CREATE TABLE public.audit_logs (
  id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
  organization_id UUID REFERENCES organizations(id),
  actor_id UUID,
  actor_email TEXT,
  action TEXT NOT NULL,        -- 'create' | 'update' | 'delete' | 'access'
  resource TEXT,               -- 'transaction' | 'asset' | 'organization'
  details JSONB,               -- Payload completo del evento
  ip_address TEXT,
  created_at TIMESTAMPTZ DEFAULT now()
);

-- RLS: Solo admins pueden ver logs de su organización
ALTER TABLE audit_logs ENABLE ROW LEVEL SECURITY;

CREATE POLICY "Admins can view audit logs"
ON audit_logs FOR SELECT
USING (public.has_role(auth.uid(), organization_id, 'admin'));

11. Casos de Uso Principales

11.1 Caso 1: Onboarding con identidad descentralizada

Objetivo: Usuario conecta su wallet y obtiene identidad verificable.

Eclipse EDCWallet (fichero cifrado)edcConnectorServiceLoginFormUsuarioEclipse EDCWallet (fichero cifrado)edcConnectorServiceLoginFormUsuarioalt[Red no configurada]Click "Iniciar sesión"hasOrgWallet()true (sesión válida)connect()eth_requestAccountsPopup de autorizaciónAprobar conexión[address]signIn(email,7ECC)Error: chain unknownpassword)ig)Popup agregar redAprobarChain switchedgetBalance(address)GX balancebilling.getStatus(orgId)Estado de facturacióngenerateDID(address-)Sesión completaUI actualizada con perfil

11.2 Caso 2: Intercambio de Datos con Facturación

Objetivo: Consumer adquiere acceso a datos con un acuerdo de contrato EDC y facturación tradicional.

Eclipse EDCPostgreSQLFacturaciónRequestWizardProductDetailCatalogConsumerEclipse EDCPostgreSQLFacturaciónRequestWizardProductDetailCatalogConsumerNavegar catálogoSELECT marketplace_listingsLista de productosClick en productoNavigate(/catalog/:id)SELECT data_assets WHERE id=?Detalle + sample_data"Solicitar Datos"Navigate(/requests/new?asset=i-d)Completa wizard (5 pasos)Genera políticaODRLEnviar solicitudRender resumenTab "Identidad"Verificar confirmación del cobroBalance suficiente"Enviar solicitud"billing.confirmPayment(tx, referencia)tx_hashINSERT data_transactionsINSERT data_policiesUPDATE payment_status ='completed'transaction_idConfirmación + Redirect a /requests

11.3 Caso 3: Auditoría Forense

Objetivo: Verificar la integridad de un acceso a datos mediante el registro de evidencias.

Verificación de evidenciasPostgreSQLDataLineageDataViewAuditorVerificación de evidenciasPostgreSQLDataLineageDataViewAuditorSecuencia, fecha,hash previo, firmaHash coincide =Datos noalteradosAccede a /data/:transactionIdSELECT data_transactions WHERE id=?transaction + metadataRender DataLineageEvidenceSELECT approval_historyWHERE transaction_id=?Lista de eventosTimeline visual con hashesClick en hash de eventocopyToClipboard(evidence_-hash)"Hash copiado""Ver en /evidencias"Open(/evidencias/hash)Detalles de transacciónVerificar integridad

Verificación de integridad:

typescript
// Componente DataLineageRegistro de evidencias
const verifyIntegrity = (event: LineageEvent) => {
  // 1. Hash registrado en API ProcureData
  const storedHash = event.metadata.tx_hash;
  
  // 2. Hash verificable en registro de evidencias
  const explorerUrl = `${PONTUSX_NETWORK_CONFIG.blockExplorerUrls[0]}tx/${storedHash}`;
  
  // 3. Si el hash existe en el explorer, el evento es inmutable
  return {
    isVerified: true,
    explorerUrl,
    blockNumber: event.metadata.block_number,
    timestamp: event.metadata.confirmed_at,
  };
};

12. Mejoras de UX v3.1

12.1 Loading States Individuales (Requests.tsx)

typescript
const [processingId, setProcessingId] = useState<string | null>(null);

const handleApprove = async (transactionId: string) => {
  setProcessingId(transactionId);
  try {
    await supabase.from('data_transactions').update({ ... });
    toast.success('Transacción aprobada');
  } finally {
    setProcessingId(null);
  }
};

// En el botón:
<Button
  onClick={() => handleApprove(transaction.id)}
  disabled={processingId !== null}
>
  {processingId === transaction.id ? (
    <Loader2 className="h-4 w-4 animate-spin" />
  ) : (
    <CheckCircle className="h-4 w-4" />
  )}
</Button>

12.2 Confirmación con AlertDialog (Opportunities.tsx)

tsx
<AlertDialog>
  <AlertDialogTrigger asChild>
    <Button>Proponer mis Datos</Button>
  </AlertDialogTrigger>
  <AlertDialogContent>
    <AlertDialogHeader>
      <AlertDialogTitle>Confirmar Propuesta</AlertDialogTitle>
      <AlertDialogDescription>
        ¿Estás seguro? Los datos serán compartidos según el contrato.
      </AlertDialogDescription>
    </AlertDialogHeader>
    <AlertDialogFooter>
      <AlertDialogCancel>Cancelar</AlertDialogCancel>
      <AlertDialogAction onClick={() => handleProposal(opp.id)}>
        Confirmar
      </AlertDialogAction>
    </AlertDialogFooter>
  </AlertDialogContent>
</AlertDialog>

12.3 Skeleton Loaders (SettingsPreferences.tsx)

tsx
{loading ? (
  <div className="space-y-4">
    <Skeleton className="h-12 w-full" />
    <Skeleton className="h-12 w-full" />
    <Skeleton className="h-12 w-full" />
  </div>
) : (
  <div className="space-y-4">
    {/* Contenido real */}
  </div>
)}

12.4 Validación Zod (Auth.tsx)

typescript
const authSchema = z.object({
  email: z
    .string()
    .trim()
    .min(1, "El email es obligatorio")
    .email("Introduce un email válido")
    .max(255, "Email demasiado largo"),
  password: z
    .string()
    .min(6, "La contraseña debe tener al menos 6 caracteres")
    .max(72, "Contraseña demasiado larga"),
});

// Uso con React Hook Form
const form = useForm({
  resolver: zodResolver(authSchema),
});

12.5 Estados Vacíos Consistentes (EmptyState)

tsx
// src/components/EmptyState.tsx
interface EmptyStateProps {
  icon?: LucideIcon;
  title: string;
  description?: string;
  action?: {
    label: string;
    onClick: () => void;
  };
}

<EmptyState
  icon={FileText}
  title="No hay transacciones"
  description="Cuando realices solicitudes de datos, aparecerán aquí."
  action={{
    label: "Explorar Catálogo",
    onClick: () => navigate('/catalog')
  }}
/>

13. API de negocio

13.1 erp-api-tester

Propósito: Test de conectividad a APIs ERP externas

Ruta: POST /functions/v1/erp-api-tester

typescript
// Request
{
  apiUrl: string;
  apiKey: string;
  method: 'GET' | 'POST';
}

// Response
{
  success: boolean;
  responseTime: number;
  statusCode: number;
}

13.2 erp-data-uploader

Propósito: Envío de datos estructurados a sistemas ERP

Ruta: POST /functions/v1/erp-data-uploader

typescript
// Request
{
  configId: string;
  transactionId: string;
  data: object;
}

// Response
{
  success: boolean;
  externalId: string;
}

13.3 notification-handler

Propósito: Emails transaccionales via Resend

Ruta: POST /functions/v1/notification-handler

typescript
// Request
{
  type: 'pre_approved' | 'approved' | 'denied' | 'completed';
  transactionId: string;
  recipientEmail: string;
}

// Response
{
  success: boolean;
  messageId: string;
}

14. Guía de Desarrollo

14.1 Tokens de Diseño (index.css)

css
:root {
  --background: 0 0% 100%;
  --foreground: 222.2 84% 4.9%;
  --primary: 221.2 83.2% 53.3%;
  --primary-foreground: 210 40% 98%;
  --secondary: 210 40% 96.1%;
  --muted: 210 40% 96.1%;
  --muted-foreground: 215.4 16.3% 46.9%;
  --accent: 210 40% 96.1%;
  --destructive: 0 84.2% 60.2%;
  --border: 214.3 31.8% 91.4%;
  --radius: 0.5rem;
}

.dark {
  --background: 222.2 84% 4.9%;
  --foreground: 210 40% 98%;
  --primary: 217.2 91.2% 59.8%;
  /* ... */
}

14.2 Uso Correcto de Colores

tsx
// ✅ CORRECTO: Usar tokens semánticos
<div className="bg-background text-foreground" />
<Button className="bg-primary text-primary-foreground" />
<Badge variant="destructive" />
<Card className="border-border" />

// ❌ INCORRECTO: Colores hardcoded
<div className="bg-white text-black" />
<Button className="bg-blue-600" />
<div style={{ color: '#333' }} />

14.3 Archivos NO Editables

Los siguientes archivos son autogenerados y NO deben modificarse manualmente:

ArchivoRazón
src/integrations/api/client.tsCliente de la API autogenerado
src/integrations/api/types.tsTipos de base de datos autogenerados
api/config.tomlConfiguración de la API
db/migrations/*Historial de migraciones
.envVariables de entorno (no commitear)

14.4 Convenciones de Código

AspectoConvención
ComponentesPascalCase (UserProfile.tsx)
HookscamelCase con prefijo use (useAuth.tsx)
ServicioscamelCase (edcConnector.ts)
TiposPascalCase (WalletState)
Archivos CSSkebab-case si necesario
ImportsAbsolute paths con @/

14.5 Estructura de Imports

typescript
// 1. React y librerías externas
import { useState, useEffect } from 'react';
import { useQuery } from '@tanstack/react-query';

// 2. Componentes UI
import { Button } from '@/components/ui/button';
import { Card } from '@/components/ui/card';

// 3. Componentes custom
import { WalletButton } from '@/components/WalletButton';

// 4. Hooks
import { useAuth } from '@/hooks/useAuth';

// 5. Servicios y utilidades
import { supabase } from '@/integrations/supabase/client';
import { pontusXService } from '@/services/pontusX';

// 6. Tipos
import type { WalletState } from '@/types/web3.types';

15. Estado de Auditoría

15.1 Resumen de Tareas

PrioridadCompletadasTotal%
🔴 Crítica66100%
🟠 Alta3650%
🟢 Mejora41040%

15.2 Críticos Completados ✅

  • Limpieza de logs de desarrollo en producción
  • Loading states en acciones destructivas (processingId)
  • Validación Zod en formularios críticos (Auth.tsx)
  • Confirmación AlertDialog antes de acciones irreversibles
  • Skeleton loaders para estados de carga
  • Identidad descentralizada en el contexto de autenticación con Keycloak

15.3 Alta Prioridad Completados ✅

  • Hook usePrivacyPreferences con persistencia optimista
  • Componente ConnectorStatusWidget en Dashboard
  • Componente EmptyState reutilizable

15.4 Pendientes Alta Prioridad

  • Integración EDC (Eclipse Dataspace Connector)
  • SSI Wallet completa (requiere infraestructura Gaia-X)
  • Indicadores visuales de precio en tarjetas de Catálogo

15.5 Mejoras Completadas ✅

  • Página /architecture con tabs interactivos
  • Componente MermaidDiagram.tsx con dark mode
  • ActivityFeed con Realtime subscriptions
  • Cleanup correcto de channels WebSocket

16. Anexos

16.1 Esquema de Base de Datos Completo (28 tablas)

Organizaciones y Usuarios

TablaColumnas ClaveDescripción
organizationsid, name, type, sector, kyb_verified, did, wallet_addressEntidades del sistema
user_profilesuser_id, organization_id, full_name, positionPerfiles de usuario
user_rolesuser_id, organization_id, roleRoles por organización
privacy_preferencesuser_id, profile_visible, access_alertsPreferencias de privacidad

Catálogo de Datos

TablaColumnas ClaveDescripción
data_productsid, name, category, schema_definition, versionPlantillas de productos
data_assetsid, product_id, holder_org_id, subject_org_id, priceActivos disponibles
catalog_metadataasset_id, tags, categories, visibilityMetadatos de catálogo

Transacciones

TablaColumnas ClaveDescripción
data_transactionsid, asset_id, consumer_org_id, subject_org_id, holder_org_id, statusTransacciones
approval_historytransaction_id, actor_org_id, action, notesHistorial de aprobaciones (Realtime)
data_policiestransaction_id, odrl_policy_jsonContratos ODRL
data_payloadstransaction_id, schema_type, data_contentContenido de datos
transaction_messagestransaction_id, sender_org_id, contentChat de negociación

Finanzas

TablaColumnas ClaveDescripción
walletsorganization_id, balance, currency, addressBilleteras
access_logsfrom_wallet_id, to_wallet_id, amount, typeMovimientos

Sistema y Seguridad

TablaColumnas ClaveDescripción
notificationsuser_id, type, title, message, is_readSistema Realtime
login_attemptsemail, ip_address, success, attempted_atRate Limiting
audit_logsactor_id, action, resource, detailsTrazabilidad
export_logstransaction_id, format, exported_byAuditoría exportaciones

Otros

TablaDescripción
esg_reportsReportes de sostenibilidad
marketplace_opportunitiesDemandas de datos
erp_configurationsIntegraciones ERP
supplier_dataDatos estructurados de proveedores
value_servicesServicios adicionales
organization_reviewsReseñas entre organizaciones
success_storiesCasos de éxito
innovation_lab_conceptsConceptos del Innovation Lab
user_wishlistLista de deseos de usuarios

16.2 Enums del Sistema

sql
-- Tipos de organización
CREATE TYPE organization_type AS ENUM ('consumer', 'provider', 'data_holder');

-- Roles de usuario
CREATE TYPE app_role AS ENUM ('admin', 'approver', 'viewer', 'api_configurator');

-- Estados de transacción
CREATE TYPE transaction_status AS ENUM (
  'initiated', 'pending_subject', 'pending_holder',
  'approved', 'denied_subject', 'denied_holder',
  'completed', 'cancelled'
);

-- Acciones de aprobación
CREATE TYPE approval_action AS ENUM ('pre_approve', 'approve', 'deny', 'cancel');

-- Tipos de configuración ERP
CREATE TYPE erp_config_type AS ENUM ('download', 'upload');

-- Métodos de autenticación ERP
CREATE TYPE auth_method AS ENUM ('bearer', 'api_key', 'oauth', 'basic');

16.3 Variables de Entorno

VariableÁmbitoDescripción
VITE_API_URLFrontendURL de la API de negocio
VITE_API_BASE_URLFrontendAnon key pública
VITE_API_BASE_URLFrontendID del proyecto
API_URLAPI de negocioURL interna
PROCUREDATA_API_URLAPI de negocioAnon key
DATABASE_URLAPI de negocioService role (privilegiado)
RESEND_API_KEYAPI de negocioAPI key para emails

16.4 Estructura del Proyecto

procuredata/
├── docs/                          # Documentación técnica
│   └── DOCUMENTO_TECNICO.md       # Este archivo (v3.1)
│
├── src/
│   ├── components/                # 40+ componentes custom
│   │   ├── ui/                    # 49 componentes Shadcn/UI
│   │   ├── AppLayout.tsx
│   │   ├── AppSidebar.tsx
│   │   ├── Web3StatusWidget.tsx   # NUEVO v3.1
│   │   ├── WalletButton.tsx
│   │   ├── ActivityFeed.tsx       # MEJORADO v3.1
│   │   ├── EmptyState.tsx         # NUEVO v3.1
│   │   ├── MermaidDiagram.tsx     # NUEVO v3.1
│   │   └── ...
│   │
│   ├── pages/                     # 27 páginas
│   │   ├── Landing.tsx
│   │   ├── Auth.tsx               # Validación Zod v3.1
│   │   ├── Dashboard.tsx          # + Web3Widget v3.1
│   │   ├── Architecture.tsx       # Tabs interactivos v3.1
│   │   └── ...
│   │
│   ├── hooks/                     # 8 hooks personalizados
│   │   ├── useAuth.tsx
│   │   ├── useWeb3Wallet.tsx      # NUEVO v3.1
│   │   ├── usePrivacyPreferences.tsx  # NUEVO v3.1
│   │   └── ...
│   │
│   ├── services/
│   │   └── pontusX.ts             # Servicio Web3
│   │
│   ├── types/
│   │   ├── database.extensions.ts
│   │   └── web3.types.ts
│   │
│   └── integrations/
│       └── supabase/
│           ├── client.ts          # NO EDITAR
│           └── types.ts           # NO EDITAR
│
├── supabase/
│   ├── functions/                 # 3 API de negocio
│   │   ├── erp-api-tester/
│   │   ├── erp-data-uploader/
│   │   └── notification-handler/
│   │
│   ├── migrations/                # NO EDITAR
│   └── config.toml                # NO EDITAR
│
└── public/                        # Assets estáticos

11. Catálogo de Casos de Uso Detallado

Esta sección documenta los 10 casos de uso industriales principales que demuestran el valor práctico de PROCUREDATA en la cadena de suministro.

11.1 Onboarding KYB (Know Your Business)

AspectoDetalle
ActorProveedor Nuevo
El ProblemaLas empresas pierden meses verificando la identidad de nuevos proveedores mediante procesos manuales, con riesgo de fraude por suplantación de identidad corporativa.
La Solución PROCUREDATAEl proveedor genera la wallet de su organización en el navegador, que crea automáticamente un DID (did:ethr:0x7ecc:...) y la plataforma valida su credencial verificable (MembershipCredential) con DCP.
Flujo TécnicoWallet Generation → DID (did:ethr) → Gaia-X Compliance Lookup → Credential Verification → Profile Created
ValorElimina el fraude de suplantación de identidad, reduce el onboarding de semanas a minutos.

11.2 Intercambio de Huella de Carbono (Scope 3)

AspectoDetalle
ActorManufacturera (Consumer) y Proveedores (Providers)
El ProblemaLas empresas no pueden cumplir con reportes CSRD/ESG porque dependen de datos de emisiones de sus proveedores que son difíciles de verificar, abriendo la puerta al "Greenwashing".
La Solución PROCUREDATALa manufacturera solicita datos de emisiones. Los proveedores suben su reporte ESG, se registra su hash en el registro de evidencias y se comparte el acceso con políticas ODRL.
Flujo TécnicoConsumer Request → Subject Approval → ESG Data Upload → Hash Notarization in ProcureData Evidence Log → Controlled Data Access
ValorAuditoría inmutable para reportes CSRD/ESG, eliminando el Greenwashing.

11.3 Compraventa de Datos con facturación (Marketplace)

AspectoDetalle
ActorData Broker
El ProblemaLos pagos transfronterizos por datos tienen fricción bancaria, tiempos de liquidación de días, y altas comisiones.
La Solución PROCUREDATAEl broker publica un dataset de "Tendencias de Mercado" en el catálogo. Un comprador lo contrata con facturación tradicional. El acuerdo de contrato EDC libera el acceso automáticamente cuando el custodio confirma el cobro.
Flujo TécnicoAsset Published → Invoice Payment → Contract Agreement (ODRL) → Data Transfer (DSP) → Data Download
ValorLiquidación instantánea, sin fricción bancaria internacional, comisiones mínimas.

11.4 Revocación de Emergencia (Kill-Switch)

AspectoDetalle
ActorDirector de Seguridad
El ProblemaCuando se detecta una brecha en un socio comercial, los datos ya compartidos siguen siendo accesibles durante días mientras se tramita la revocación manual.
La Solución PROCUREDATAEl Director usa el componente RevokeAccessButton para ejecutar una revocación instantánea. El conector EDC termina el acuerdo de contrato y bloquea nuevas transferencias en tiempo real.
Flujo TécnicoBreach Detection → RevokeAccessButton Click → EDC Agreement Termination → Access Token Invalidation → Audit Log Entry
ValorSoberanía real sobre los datos, mitigación de daños inmediata ante brechas de seguridad.

11.5 Pasaporte Digital de Producto (DPP)

AspectoDetalle
ActorMarca de Ropa
El ProblemaLa normativa UE exige trazabilidad completa de productos, pero agregar datos de múltiples proveedores (tela, tinte, confección) es complejo y propenso a errores.
La Solución PROCUREDATALa marca crea un activo digital agregando datos de 5 proveedores en un solo "Pasaporte Digital". Cada contribución queda registrada con su DID y timestamp.
Flujo TécnicoCreate DPP Asset → Invite Suppliers → Each Supplier Signs Data → Aggregate to Single Asset → Publish DPP → Consumer QR Scan
ValorCumplimiento con normativa UE, transparencia total para el consumidor final.

11.6 Entrenamiento de IA Privado (Compute-to-Data)

AspectoDetalle
ActorStartup de IA y Hospital/Farmacéutica
El ProblemaLos hospitales no pueden monetizar sus datos porque compartir datos de pacientes viola GDPR y regulaciones de privacidad médica.
La Solución PROCUREDATALa IA "viaja" a los datos del hospital mediante Compute-to-Data. El modelo se entrena en un sandbox aislado sin que los datos salgan nunca de la infraestructura del hospital.
Flujo TécnicoAI Model Upload → Sandbox Provisioning → Secure Execution Environment → Training on Local Data → Model Results Export (no raw data)
ValorMonetización de datos sensibles sin perder privacidad, cumplimiento GDPR.

11.7 Gestión de Recalls (Retirada de Productos)

AspectoDetalle
ActorFabricante de Automóviles
El ProblemaIdentificar qué lotes de piezas defectuosas se instalaron en qué vehículos puede tomar semanas con sistemas tradicionales, poniendo vidas en riesgo.
La Solución PROCUREDATAEl fabricante usa el componente DataLineage para rastrear en segundos la procedencia del lote defectuoso y en qué coches se instaló cada pieza.
Flujo TécnicoDefect Detection → DataLineage Query → Evidence Log Trace → Supplier Lot Identification → Affected Vehicle List → Recall Initiation
ValorRespuesta rápida ante crisis de calidad, salvando vidas y reduciendo costos de recall.

11.8 Financiación de Cadena de Suministro (DeFi)

AspectoDetalle
ActorPequeño Agricultor
El ProblemaLos pequeños productores no pueden acceder a crédito porque los bancos solo evalúan balances financieros, no el historial de cumplimiento de entregas.
La Solución PROCUREDATAEl agricultor comparte su historial de entregas exitosas (verificado en el registro de evidencias) con un banco o entidad financiera para obtener un crédito con mejor tasa basado en su reputación verificable.
Flujo TécnicoRequest Credit → Share Verifiable Delivery History → Bank/DeFi Protocol Verification → Reputation Score Calculation → Credit Approval with Better Rate
ValorAcceso a capital basado en reputación verificable, no solo en balance financiero.

11.9 Auditoría de Cadena de Frío (IoT)

AspectoDetalle
ActorLogística de Alimentos
El ProblemaLas disputas sobre quién rompió la cadena de frío en un envío de alimentos son imposibles de resolver sin registros inmutables.
La Solución PROCUREDATASensores IoT suben lecturas de temperatura cada hora a través de la API de negocio. Si se rompe la cadena de frío, el evento queda registrado inmutablemente con timestamp y ubicación GPS.
Flujo TécnicoIoT Sensor Reading → API Processing → Threshold Check → Evidence Log Notarization → Alert if Breach → Immutable Audit Trail
ValorResolución indiscutible de disputas sobre responsabilidad en la cadena de frío.

11.10 Negociación Dinámica de Licencias (ODRL)

AspectoDetalle
ActorCentro de Investigación
El ProblemaLas licencias de datos tradicionales son "todo o nada" - no permiten restricciones temporales, geográficas o de uso específico sin contratos complejos.
La Solución PROCUREDATAEl investigador usa el componente NegotiationChat para negociar un contrato específico: "Acceso solo por 30 días, solo para uso académico, solo en UE". La política ODRL se genera automáticamente.
Flujo TécnicoOpen NegotiationChat → Define Constraints → ODRL Policy Generation → Counter-offer Loop → Agreement → EDC Contract Agreement → Time-Limited Access
ValorFlexibilidad contractual automatizada que va más allá del modelo "comprar para siempre".

14. Sistema de Registro y Onboarding (v3.2)

14.1 Flujo de Registro de Nuevos Usuarios

Resend EmailDatabaseAPI de negocio/registerUsuarioResend EmailDatabaseAPI de negocio/registerUsuarioalt[Ya existe][Nuevo registro]Completa formulariomulti-pasoValidación Zod enclientePOST /submit-registrationValidación payloadCheck duplicados (tax_id)Registro existenteError 409 Conflict"CIF ya registrado"No existeINSERT registration_requestsrequest_idsend-welcome-emailEmail enviadoSuccess 201Pantalla de confirmación

14.2 Tabla registration_requests

sql
CREATE TABLE public.registration_requests (
  id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
  legal_name TEXT NOT NULL,
  tax_id TEXT NOT NULL,
  sector TEXT NOT NULL,
  size TEXT NOT NULL,
  country TEXT NOT NULL,
  address TEXT NOT NULL,
  role TEXT NOT NULL,  -- 'buyer' | 'supplier'
  
  -- Representante legal
  representative_name TEXT NOT NULL,
  representative_email TEXT NOT NULL,
  representative_phone TEXT,
  representative_position TEXT,
  
  -- Intenciones de uso
  intention_data_types TEXT[],
  intention_has_erp TEXT,
  erp_type TEXT,
  product_category TEXT,
  
  -- Aceptación legal
  accepted_terms BOOLEAN DEFAULT false,
  accepted_gdpr BOOLEAN DEFAULT false,
  accepted_conduct BOOLEAN DEFAULT false,
  
  -- Estado del registro
  status registration_status DEFAULT 'pending',
  created_organization_id UUID REFERENCES organizations(id),
  reviewed_by TEXT,
  reviewed_at TIMESTAMPTZ,
  reviewer_notes TEXT,
  
  -- Auditoría
  ip_address TEXT,
  user_agent TEXT,
  created_at TIMESTAMPTZ DEFAULT now(),
  updated_at TIMESTAMPTZ DEFAULT now()
);

-- Enum de estados
CREATE TYPE registration_status AS ENUM (
  'pending',        -- Recién enviado
  'under_review',   -- En revisión por admin
  'approved',       -- Aprobado, organización creada
  'rejected',       -- Rechazado
  'needs_info'      -- Requiere información adicional
);

14.3 Endpoint de la API: submit-registration

Ruta: POST /functions/v1/submit-registration

typescript
// Payload esperado
interface RegistrationPayload {
  organization: {
    legalName: string;
    taxId: string;
    sector: string;
    size: string;
    country: string;
    address: string;
  };
  representative: {
    fullName: string;
    email: string;
    phone?: string;
    position?: string;
  };
  role: 'buyer' | 'supplier';
  intention: {
    dataTypes: string[];
    hasErp: string;
    erpType?: string;
    productCategory?: string;
  };
  legal: {
    acceptedTerms: boolean;
    acceptedGdpr: boolean;
    acceptedConduct: boolean;
  };
  metadata?: {
    ipAddress?: string;
    userAgent?: string;
    locale?: string;
  };
}

// Response
{
  success: true,
  message: "Registration submitted successfully",
  requestId: "uuid",
  status: "pending"
}

14.4 Servicio de la API: send-welcome-email

Ruta: POST /functions/v1/send-welcome-email

typescript
// Payload
interface WelcomeEmailPayload {
  role: 'buyer' | 'supplier';
  recipientEmail: string;
  recipientName: string;
  companyName: string;
  requestId: string;
  locale?: string;  // 'es' | 'en' | 'fr' | 'pt' | 'de' | 'it' | 'nl'
}

// Lógica de selección de plantilla
if (role === 'supplier') {
  // Plantilla A: "Activa tu visibilidad"
  // - Icono: Ámbar (alerta)
  // - CTA: "Completar mi perfil ahora"
  // - Enfoque: Subir certificaciones, ser visible
} else {
  // Plantilla B: "Verificación de Seguridad"
  // - Icono: Azul (escudo)
  // - CTA: "Iniciar verificación de entidad"
  // - Enfoque: KYB, conectar ERP
}

14.5 Sistema de Acceso Diferenciado

No

Sí

No

Sí

Usuario accede a /catalog

¿Autenticado?

Redirección a /auth
Inicio de sesión
Formulario de acceso

¿Tiene organización
¿Cuenta aprobada?

OnboardingDashboard
Empty State
Guía paso a paso

AppLayout
Datos REALES
Funcionalidad completa

14.6 Hook useUserAccessMode

typescript
// src/hooks/useUserAccessMode.ts
export type AccessMode = 'signed_out' | 'pending_setup' | 'active';

export function useUserAccessMode(): {
  mode: AccessMode;
  isLoading: boolean;
} {
  const { user } = useAuth();
  const { currentOrganization, isLoading } = useOrganizationContext();
  
  if (!user) return { mode: 'signed_out', isLoading: false };
  if (isLoading) return { mode: 'signed_out', isLoading: true };
  if (!currentOrganization || currentOrganization.is_demo) {
    return { mode: 'pending_setup', isLoading: false };
  }
  return { mode: 'active', isLoading: false };
}

15. Guía de Desarrollo

15.1 Tokens de Diseño (index.css)

css
:root {
  --background: 0 0% 100%;
  --foreground: 222.2 84% 4.9%;
  --primary: 221.2 83.2% 53.3%;
  --primary-foreground: 210 40% 98%;
  --secondary: 210 40% 96.1%;
  --muted: 210 40% 96.1%;
  --muted-foreground: 215.4 16.3% 46.9%;
  --accent: 210 40% 96.1%;
  --destructive: 0 84.2% 60.2%;
  --border: 214.3 31.8% 91.4%;
  --radius: 0.5rem;
}

.dark {
  --background: 222.2 84% 4.9%;
  --foreground: 210 40% 98%;
  --primary: 217.2 91.2% 59.8%;
  /* ... */
}

15.2 Uso Correcto de Colores

tsx
// ✅ CORRECTO: Usar tokens semánticos
<div className="bg-background text-foreground" />
<Button className="bg-primary text-primary-foreground" />
<Badge variant="destructive" />
<Card className="border-border" />

// ❌ INCORRECTO: Colores hardcoded
<div className="bg-white text-black" />
<Button className="bg-blue-600" />
<div style={{ color: '#333' }} />

15.3 Archivos NO Editables

Los siguientes archivos son autogenerados y NO deben modificarse manualmente:

ArchivoRazón
src/integrations/api/client.tsCliente de la API autogenerado
src/integrations/api/types.tsTipos de base de datos autogenerados
api/config.tomlConfiguración de la API
db/migrations/*Historial de migraciones
.envVariables de entorno (no commitear)

15.4 Convenciones de Código

AspectoConvención
ComponentesPascalCase (UserProfile.tsx)
HookscamelCase con prefijo use (useAuth.tsx)
ServicioscamelCase (edcConnector.ts)
TiposPascalCase (WalletState)
Archivos CSSkebab-case si necesario
ImportsAbsolute paths con @/

15.5 Estructura de Imports

typescript
// 1. React y librerías externas
import { useState, useEffect } from 'react';
import { useQuery } from '@tanstack/react-query';

// 2. Componentes UI
import { Button } from '@/components/ui/button';
import { Card } from '@/components/ui/card';

// 3. Componentes custom
import { WalletButton } from '@/components/WalletButton';

// 4. Hooks
import { useAuth } from '@/hooks/useAuth';

// 5. Servicios y utilidades
import { supabase } from '@/integrations/supabase/client';
import { pontusXService } from '@/services/pontusX';

// 6. Tipos
import type { WalletState } from '@/types/web3.types';

16. Estado de Auditoría

16.1 Resumen de Tareas

PrioridadCompletadasTotal%
🔴 Crítica88100%
🟠 Alta5862.5%
🟢 Mejora61250%

16.2 Críticos Completados ✅

  • Limpieza de logs de desarrollo en producción
  • Loading states en acciones destructivas (processingId)
  • Validación Zod en formularios críticos (Auth.tsx, Register.tsx)
  • Confirmación AlertDialog antes de acciones irreversibles
  • Skeleton loaders para estados de carga
  • Identidad descentralizada en el contexto de autenticación con Keycloak
  • Sistema de registro con validación de duplicados
  • Emails de bienvenida diferenciados por rol

16.3 Alta Prioridad Completados ✅

  • Hook usePrivacyPreferences con persistencia optimista
  • Componente ConnectorStatusWidget en Dashboard
  • Componente EmptyState reutilizable
  • API de negocio submit-registration
  • API de negocio send-welcome-email

16.4 Pendientes Alta Prioridad

  • Integración EDC (Eclipse Dataspace Connector)
  • SSI Wallet completa (requiere infraestructura Gaia-X)
  • Indicadores visuales de precio en tarjetas de Catálogo

16.5 Mejoras Completadas ✅

  • Página /architecture con tabs interactivos
  • Componente MermaidDiagram.tsx con dark mode
  • ActivityFeed con Realtime subscriptions
  • Cleanup correcto de channels WebSocket
  • Página /register con formulario multi-paso
  • Traducciones de registro en 7 idiomas

17. Anexos

17.1 Esquema de Base de Datos Completo (29 tablas)

Organizaciones y Usuarios

TablaColumnas ClaveDescripción
organizationsid, name, type, sector, kyb_verified, did, wallet_addressEntidades del sistema
user_profilesuser_id, organization_id, full_name, positionPerfiles de usuario
user_rolesuser_id, organization_id, roleRoles por organización
privacy_preferencesuser_id, profile_visible, access_alertsPreferencias de privacidad
registration_requeststax_id, status, role, representative_emailNUEVO v3.2: Solicitudes de registro

Catálogo de Datos

TablaColumnas ClaveDescripción
data_productsid, name, category, schema_definition, versionPlantillas de productos
data_assetsid, product_id, holder_org_id, subject_org_id, priceActivos disponibles
catalog_metadataasset_id, tags, categories, visibilityMetadatos de catálogo

Transacciones

TablaColumnas ClaveDescripción
data_transactionsid, asset_id, consumer_org_id, subject_org_id, holder_org_id, statusTransacciones
approval_historytransaction_id, actor_org_id, action, notesHistorial de aprobaciones (Realtime)
data_policiestransaction_id, odrl_policy_jsonContratos ODRL
data_payloadstransaction_id, schema_type, data_contentContenido de datos
transaction_messagestransaction_id, sender_org_id, contentChat de negociación

Finanzas

TablaColumnas ClaveDescripción
walletsorganization_id, balance, currency, addressBilleteras
access_logsfrom_wallet_id, to_wallet_id, amount, typeMovimientos

Sistema y Seguridad

TablaColumnas ClaveDescripción
notificationsuser_id, type, title, message, is_readSistema Realtime
login_attemptsemail, ip_address, success, attempted_atRate Limiting
audit_logsactor_id, action, resource, detailsTrazabilidad
export_logstransaction_id, format, exported_byAuditoría exportaciones

Otros

TablaDescripción
esg_reportsReportes de sostenibilidad
marketplace_opportunitiesDemandas de datos
erp_configurationsIntegraciones ERP
supplier_dataDatos estructurados de proveedores
value_servicesServicios adicionales
organization_reviewsReseñas entre organizaciones
success_storiesCasos de éxito
innovation_lab_conceptsConceptos del Innovation Lab
user_wishlistLista de deseos de usuarios
ai_feedbackFeedback para entrenamiento de ARIA

17.2 Enums del Sistema

sql
-- Tipos de organización
CREATE TYPE organization_type AS ENUM ('consumer', 'provider', 'data_holder');

-- Roles de usuario
CREATE TYPE app_role AS ENUM ('admin', 'approver', 'viewer', 'api_configurator');

-- Estados de transacción
CREATE TYPE transaction_status AS ENUM (
  'initiated', 'pending_subject', 'pending_holder',
  'approved', 'denied_subject', 'denied_holder',
  'completed', 'cancelled'
);

-- Acciones de aprobación
CREATE TYPE approval_action AS ENUM ('pre_approve', 'approve', 'deny', 'cancel');

-- Estados de registro (NUEVO v3.2)
CREATE TYPE registration_status AS ENUM (
  'pending', 'under_review', 'approved', 'rejected', 'needs_info'
);

-- Tipos de configuración ERP
CREATE TYPE erp_config_type AS ENUM ('download', 'upload');

-- Métodos de autenticación ERP
CREATE TYPE auth_method AS ENUM ('bearer', 'api_key', 'oauth', 'basic');

17.3 Variables de Entorno

VariableÁmbitoDescripción
VITE_API_URLFrontendURL de la API de negocio
VITE_API_BASE_URLFrontendAnon key pública
VITE_API_BASE_URLFrontendID del proyecto
API_URLAPI de negocioURL interna
PROCUREDATA_API_URLAPI de negocioAnon key
DATABASE_URLAPI de negocioService role (privilegiado)
RESEND_API_KEYAPI de negocioAPI key para emails

17.4 Estructura del Proyecto

procuredata/
├── docs/                          # Documentación técnica
│   └── DOCUMENTO_TECNICO.md       # Este archivo (v3.2)
│
├── src/
│   ├── components/                # 45+ componentes custom
│   │   ├── ui/                    # 49 componentes Shadcn/UI
│   │   ├── AppLayout.tsx
│   │   ├── AppSidebar.tsx
│   │   ├── Web3StatusWidget.tsx   # v3.1
│   │   ├── WalletButton.tsx
│   │   ├── ActivityFeed.tsx       # v3.1
│   │   ├── EmptyState.tsx         # v3.1
│   │   ├── MermaidDiagram.tsx     # v3.1
│   │   └── ...
│   │
│   ├── pages/                     # 28 páginas
│   │   ├── Landing.tsx
│   │   ├── Auth.tsx               # Validación Zod v3.1
│   │   ├── Register.tsx           # NUEVO v3.2
│   │   ├── Dashboard.tsx          # + Web3Widget v3.1
│   │   ├── Architecture.tsx       # Tabs interactivos v3.1
│   │   └── ...
│   │
│   ├── hooks/                     # 10 hooks personalizados
│   │   ├── useAuth.tsx
│   │   ├── useWeb3Wallet.tsx      # v3.1
│   │   ├── usePrivacyPreferences.tsx  # v3.1
│   │   ├── useRegistration.tsx    # NUEVO v3.2
│   │   ├── useUserAccessMode.tsx  # NUEVO v3.2
│   │   └── ...
│   │
│   ├── services/
│   │   └── pontusX.ts             # Servicio Web3
│   │
│   ├── types/
│   │   ├── database.extensions.ts
│   │   └── web3.types.ts
│   │
│   └── integrations/
│       └── supabase/
│           ├── client.ts          # NO EDITAR
│           └── types.ts           # NO EDITAR
│
├── supabase/
│   ├── functions/                 # 5 API de negocio
│   │   ├── erp-api-tester/
│   │   ├── erp-data-uploader/
│   │   ├── notification-handler/
│   │   ├── submit-registration/   # NUEVO v3.2
│   │   └── send-welcome-email/    # NUEVO v3.2
│   │
│   ├── migrations/                # NO EDITAR
│   └── config.toml                # NO EDITAR
│
└── public/                        # Assets estáticos

18. Historial de Versiones

VersiónFechaCambios Principales
3.213 Enero 2026Sistema de registro multi-paso, emails de bienvenida diferenciados (Buyer/Supplier), acceso según el estado de la cuenta (pendiente/activa), 2 nuevos endpoints de la API, traducciones en 7 idiomas, documentación actualizada
3.105 Enero 2026WalletStatusWidget, useOrgWallet, usePrivacyPreferences, ActivityFeed Realtime, EmptyState, MermaidDiagram, página Architecture interactiva, validación Zod, loading states individuales, AlertDialog confirmaciones
3.0Diciembre 2025Conectores EDC, SSI con DIDs y credenciales verificables, facturación, wallet de organización
2.5Diciembre 2025Realtime notifications, mejoras UX básicas
2.0Noviembre 2025Modelo tripartito Consumer/Subject/Holder, políticas ODRL, Innovation Lab
1.0Octubre 2025MVP inicial con catálogo básico y autenticación

Referencias


Documento generado por PROCUREDATA Technical Documentation System
Última actualización: 13 Enero 2026
Versión: 3.2 (Registration + Onboarding System)