npm package: @infodocnpm/search-select
Description: Biblioteca de componentes UI para SGDEA (Sistema de Gestión Documental Electrónico de Archivo).
Latest version: 0.0.4

# search-select (npm)

Biblioteca de componentes UI para SGDEA (Sistema de Gestión Documental Electrónico de Archivo).

**Versión publicada:** `0.0.1`. **API actual (`public-api.ts`):** consulta el archivo en el repo; en ramas mínimas puede incluir solo *button*, *searchable-select* y *ui-components* placeholder. Los ejemplos de metadatos más abajo aplican si esas rutas existen en tu copia del código.

## Instalación

```bash
npm install search-select
```

## Documentación Completa

Para la documentación completa de instalación e integración de metadatos, consulta el archivo:

**`MANUAL_INSTALACION_METADATOS.md`** en la raíz del repositorio `sgdea-component-library`.

Este manual contiene:
- Guía paso a paso de instalación
- Configuración de rutas y secciones
- Integración en modo inline (recomendado)
- Ejemplos de código completos
- Validación de campos obligatorios
- Vinculación de metadatos con registros

## Componente: Metadata Crosscutting

Componente para gestionar y mostrar formularios de metadatos de manera inline o en modal.

**IMPORTANTE:** Consulta el `MANUAL_INSTALACION_METADATOS.md` para ver la documentación completa y actualizada.

### Modo Inline (Recomendado)

El componente puede renderizarse directamente dentro de un formulario sin necesidad de un modal.

#### 1. Importar el componente

```typescript
import { MetadataCrosscuttingComponent } from 'search-select';

@Component({
  selector: 'app-my-component',
  standalone: true,
  imports: [
    MetadataCrosscuttingComponent,
    // ... otros imports
  ],
  // ...
})
export class MyComponent {
  // ...
}
```

#### 2. Usar en el template

```html
<sgdea-metadata-crosscutting
  #metadataComponent
  [inlineMode]="true"
  [autoLoad]="true"
  [sectionFormId]="sectionFormId"
  [apiUrl]="metadataApiUrl"
  [createApiUrl]="metadataCreateApiUrl"
  [registerId]="null"
  [initialFormData]="initialMetadataData || null"
  (saveModal)="onMetadataSave($event)">
</sgdea-metadata-crosscutting>
```

#### 3. Configurar en el componente TypeScript

```typescript
import { ViewChild } from '@angular/core';
import { MetadataCrosscuttingComponent, MetadataFormData, MetadataModalFormStructure } from 'search-select';

export class MyComponent {
  @ViewChild('metadataComponent') metadataComponent?: MetadataCrosscuttingComponent;
  
  sectionFormId = 'your-section-form-id';
  metadataApiUrl = 'https://your-api.com/api/v1/metadata/template/get-all-metadata-for-modal';
  metadataCreateApiUrl = 'https://your-api.com/api/v1/metadata/value-metadata/create';
  initialMetadataData: MetadataFormData[] | null = null;

  // Obtener datos actuales del formulario
  getCurrentMetadataData(): MetadataFormData[] {
    if (this.metadataComponent) {
      return this.metadataComponent.getCurrentFormData();
    }
    return [];
  }

  // Guardar datos (opcional, si quieres guardar localmente)
  onMetadataSave(data: { formData: MetadataFormData[], formStructure: MetadataModalFormStructure | null }): void {
    // Guardar localmente si es necesario
    console.log('Metadata saved:', data);
  }
}
```

#### 4. Vincular metadatos después de crear un registro

```typescript
import { ValueMetadataCreateMapper, ValueMetadataCreateRequestDto } from 'search-select';
import { HttpClient } from '@angular/common/http';
import { forkJoin, Observable, of } from 'rxjs';
import { catchError, map } from 'rxjs/operators';

export class MyComponent {
  constructor(private http: HttpClient) {}

  // Después de crear tu registro (ej: préstamo, expediente, etc.)
  linkMetadataToRegister(registerId: string): void {
    const metadataData = this.metadataComponent?.getCurrentFormData() || [];
    
    if (metadataData.length === 0) {
      return; // No hay datos para vincular
    }

    const apiUrl = 'https://your-api.com/api/v1/metadata/value-metadata/create';
    const sectionFormId = this.sectionFormId;

    // Construir el request usando el mapper
    const requestPayload: ValueMetadataCreateRequestDto = ValueMetadataCreateMapper.toRequestDto(
      metadataData,
      sectionFormId,
      registerId,
      null // formStructure - opcional
    );

    // Enviar al backend
    this.http.post(apiUrl, requestPayload).subscribe({
      next: (response) => {
        console.log('Metadata vinculado exitosamente:', response);
      },
      error: (error) => {
        console.error('Error al vincular metadata:', error);
      }
    });
  }
}
```

### Modo Modal (Legacy)

El componente también puede mostrarse en un modal (modo legacy).

```html
<sgdea-metadata-crosscutting
  [isVisible]="isModalVisible"
  [modalTitle]="'Metadatos del Documento'"
  [sectionFormId]="sectionFormId"
  [apiUrl]="metadataApiUrl"
  [createApiUrl]="metadataCreateApiUrl"
  [registerId]="registerId"
  [initialFormData]="initialMetadataData"
  (closeModal)="closeModal()"
  (saveModal)="onMetadataSave($event)">
</sgdea-metadata-crosscutting>
```

### Propiedades del Componente

| Propiedad | Tipo | Descripción |
|-----------|------|-------------|
| `inlineMode` | `boolean` | Si es `true`, muestra el formulario inline (sin modal). Por defecto: `false` |
| `autoLoad` | `boolean` | Si es `true`, carga automáticamente los metadatos al inicializar. Por defecto: `false` |
| `sectionFormId` | `string \| null` | ID de la sección de formulario de metadatos |
| `apiUrl` | `string \| null` | URL completa del endpoint para obtener la estructura de metadatos |
| `createApiUrl` | `string \| null` | URL completa del endpoint para crear/vincular metadatos |
| `registerId` | `string \| null` | ID del registro al que se vincularán los metadatos (usado en modo modal) |
| `initialFormData` | `MetadataFormData[] \| null` | Datos iniciales para poblar el formulario |
| `isVisible` | `boolean` | Muestra/oculta el modal (solo en modo modal). Por defecto: `false` |
| `modalTitle` | `string` | Título del modal (solo en modo modal). Por defecto: `'Metadatos del Documento'` |

### Eventos

| Evento | Tipo | Descripción |
|--------|------|-------------|
| `saveModal` | `EventEmitter<{ formData: MetadataFormData[], formStructure: MetadataModalFormStructure \| null }>` | Se emite cuando se guarda el formulario |
| `closeModal` | `EventEmitter<void>` | Se emite cuando se cierra el modal (solo modo modal) |

### Métodos Públicos

#### `getCurrentFormData(): MetadataFormData[]`

Obtiene los datos actuales del formulario sin validar ni guardar. Útil para obtener los valores actuales antes de vincular al registro.

```typescript
const currentData = this.metadataComponent?.getCurrentFormData();
```

### Validación de Campos Obligatorios

Para validar que todos los campos obligatorios estén diligenciados antes de crear un registro:

```typescript
import { MetadataValidationService, ValidateMissingFieldsResponseDto } from 'search-select';

export class MyComponent {
  constructor(private metadataValidationService: MetadataValidationService) {}

  validateRequiredFields(): boolean {
    const currentData = this.metadataComponent?.getCurrentFormData() || [];
    const sectionFormId = this.sectionFormId;
    
    // Obtener secciones con campos obligatorios
    const apiUrl = 'https://your-api.com/api/v1/metadata/template/get-all-metadata-for-modal';
    
    this.metadataValidationService.validateRequiredFields(apiUrl, [sectionFormId])
      .subscribe({
        next: (response) => {
          if (response.sectionsWithRequiredFields.length > 0) {
            // Hay campos obligatorios, validar que estén diligenciados
            const metadataDataMap = new Map();
            metadataDataMap.set(sectionFormId, {
              formData: currentData,
              formStructure: null // Obtener de la estructura cargada
            });

            const validationResult = this.metadataValidationService.validateMissingRequiredFields(
              response.sectionsWithRequiredFields,
              metadataDataMap
            );

            if (validationResult.hasMissingFields) {
              console.error('Faltan campos obligatorios:', validationResult.missingSections);
              return false;
            }
          }
          return true;
        }
      });

    return false;
  }
}
```

### Ejemplo Completo de Integración

```typescript
import { Component, ViewChild } from '@angular/core';
import { MetadataCrosscuttingComponent, MetadataFormData, MetadataModalFormStructure, ValueMetadataCreateMapper, ValueMetadataCreateRequestDto } from 'search-select';
import { HttpClient } from '@angular/common/http';

@Component({
  selector: 'app-loan-form',
  standalone: true,
  imports: [MetadataCrosscuttingComponent],
  template: `
    <form>
      <!-- Campos del formulario principal -->
      
      <!-- Metadatos inline -->
      <sgdea-metadata-crosscutting
        #metadataComponent
        [inlineMode]="true"
        [autoLoad]="true"
        [sectionFormId]="'DETAIL_SECTION'"
        [apiUrl]="metadataApiUrl"
        [createApiUrl]="metadataCreateApiUrl"
        (saveModal)="onMetadataSave($event)">
      </sgdea-metadata-crosscutting>

      <button (click)="createLoan()">Crear Préstamo</button>
    </form>
  `
})
export class LoanFormComponent {
  @ViewChild('metadataComponent') metadataComponent?: MetadataCrosscuttingComponent;
  
  metadataApiUrl = 'https://api.example.com/api/v1/metadata/template/get-all-metadata-for-modal';
  metadataCreateApiUrl = 'https://api.example.com/api/v1/metadata/value-metadata/create';

  constructor(private http: HttpClient) {}

  onMetadataSave(data: { formData: MetadataFormData[], formStructure: MetadataModalFormStructure | null }): void {
    // Guardar localmente si es necesario
    console.log('Metadata guardado:', data);
  }

  createLoan(): void {
    // 1. Crear el préstamo
    this.http.post('https://api.example.com/api/v1/loans', loanData)
      .subscribe({
        next: (response: any) => {
          const loanId = response.idPrestamo;
          
          // 2. Vincular metadatos al préstamo
          this.linkMetadataToLoan(loanId);
        }
      });
  }

  linkMetadataToLoan(loanId: string): void {
    const metadataData = this.metadataComponent?.getCurrentFormData() || [];
    
    if (metadataData.length === 0) {
      return;
    }

    const requestPayload: ValueMetadataCreateRequestDto = ValueMetadataCreateMapper.toRequestDto(
      metadataData,
      'DETAIL_SECTION',
      loanId,
      null
    );

    this.http.post(this.metadataCreateApiUrl, requestPayload).subscribe({
      next: () => {
        console.log('Metadatos vinculados exitosamente');
      },
      error: (error) => {
        console.error('Error al vincular metadatos:', error);
      }
    });
  }
}
```

## Building

Para construir la librería localmente:

```bash
npm run build:lib
```

Los artefactos de compilación se guardarán en el directorio `dist/`.

### Publicar la Librería

Una vez construida la librería:

1. Navegar al directorio `dist`:
   ```bash
   cd dist/ui-components
   ```

2. Ejecutar `npm publish` para publicar la librería en el registro de npm:
   ```bash
   npm publish
   ```

## Running unit tests

Para ejecutar las pruebas unitarias:

```bash
npm test
```

## Recursos Adicionales

Para más información sobre Angular CLI, visita [Angular CLI Overview and Command Reference](https://angular.dev/tools/cli).