npm package: @marxa/carrier
Description: This library was generated with [Angular CLI](https://github.com/angular/angular-cli) version 11.2.14.
Author: Jorge Guzman
Latest version: 12.2210.4

# Carrier

This library was generated with [Angular CLI](https://github.com/angular/angular-cli) version 11.2.14.

## Install
### As Node Dependency
1. Log in https://marxa.jfrog.io/ and login. In the account menu navigate to **Set me up**. And remain the configurations snippet and Copy it.
2. If you user VSCode, in a terminal run `code ~/.npmrc`, otherwise, navigate to `~/.npmrc` in your file explorer.
3. Paste the code of the instructions from JFrog adding the prefix `@marxa:` in every artifactory regist. Example:
    ```bash
    _auth = <PASSWORD> (converted to base 64)
    email = youremail@email.com
    always-auth = true
    @marxa:registry = https://marxa.jfrog.io/artifactory/api/npm/mx-library-npm/
    ```
3. Import the dependencies
    ```bash
    npm i -s @marxa/carrier
    ```

### As submodule
1. Ensure you have permission to clone this repo
2. Open a terminal in your project root
3. Run 
    ```bash
    git submodule init
    git submodule add -b develop https://github.com/Marxa-Digital/mx-carrier libs/marxa/carrier
    ```
4. Configure the `angular.json` file, including the below block code in `projects`
    ```json
    {
      "projects":{
        "@marxa/carrier": {
          "projectType": "library",
          "root": "projects/marxa/carrier",
          "sourceRoot": "projects/marxa/carrier/src",
          "prefix": "mx",
          "architect": {
            "build": {
              "builder": "@angular-devkit/build-angular:ng-packagr",
              "options": {
                "project": "projects/marxa/carrier/ng-package.json"
              },
              "configurations": {
                "production": {
                  "tsConfig": "projects/marxa/carrier/tsconfig.lib.prod.json"
                },
                "development": {
                  "tsConfig": "projects/marxa/carrier/tsconfig.lib.json"
                }
              },
              "defaultConfiguration": "production"
            },
            "test": {
              "builder": "@angular-devkit/build-angular:karma",
              "options": {
                "main": "projects/marxa/carrier/src/test.ts",
                "tsConfig": "projects/marxa/carrier/tsconfig.spec.json",
                "karmaConfig": "projects/marxa/carrier/karma.conf.js"
              }
            }
          }
        }
      }
    }
    ```
5. Confugure the `tsconfig.json` file including the below block code in `paths`
    ```json
    {
      "compilerOptions": {
        ...
        "paths":{
          "@marxa/carrier": [
            "dist/marxa/carrier/marxa-carrier",
            "dist/marxa/carrier"
          ],
        }
      }
    }
    ```
6. Run the next command
    ```bash
    cd libs/marxa/carrier && start cmd /k ng build @marxa/carrier --watch
    ```
7. Install the library locally running the next command
    ```bash
    npm i "./dist/marxa/carrier"
    ```
------------------

## MxTableImporter
Table Importer is a helper to import CSV files with big data. It provides the result as a big object or a backup file.

### Usage
1. In your module where you wanna use it, import the MxCarrierModule
    ```ts
    @NgModule({
      imports: [MxCarrierModule],
    })
    export class MyModule {}
    ```
2. In the component where you want to call the importer, use the selector
    my-component.html
    ```html
    <mx-table-importer
      label="Importar tabla"
      color="primary"
      [config]="configuration"
    ></mx-table-importer>
    ```
    my-component.ts
    ```ts
    public configuration: MxTableImporterConfig = new MxTableImporterConfig(
      "Requerido", // requiredLabel
      ".", // decimalSeparator
      ",", // valuesSeparator
      "\n", // listSeparator
      false, // hideInstructions
      false, // hideAdvices
      true, // allowRenameColumns
      "array" // resultType
    )
    ```

    It will print a button to call the importer.
  
3. Instead, you can use the selector only and hide the button if you want call from the service.
    
      my-component.html
      ```html
      <mx-table-importer [hide]="true"></mx-table-importer>
      ```
      my-component.ts
      ```ts
        constructor(private _importer: MxTableImporter){}

        openImporter(): void {
          this._importer.openImporterPanel$.next();
        }
      ```
4. Configure the columns of the table that will be expected.
    ```ts
    constructor(private _importer: MxTableImporter){
      this._importer.columns = [
        {
          name: "name",
          textDesc: "The name of the",
          htmlDesc: "The name of the",
          required: true,
          typedAs: ColumnCustomType.NUMBER
        }
      ]
    }
    ```

5. If you decide create a file, you can configure it.
    ```ts
    private dateStringOption: Intl.DateTimeFormatOptions = {
      weekday: "long",
      year: "numeric",
      month: "long",
      day: "numeric",
      hour: "numeric",
      minute: "numeric",
    };

    public dateInTitle = {
      locale: "es-MX",
      options: this.dateStringOption,
    };

    constructor(private _importer: MxTableImporter){
      this._importer.exportFileConfig = new ExportAsCSVConfig(
        true, // download
        'imported data', // name
        this.dateInTitle, // dateInTitle
      )
    }
    ```

6. Can configure the content of the instructions
    ```ts
    constructor(private _importer: MxTableImporter){
      this._importer.tableImporterContent = new MxTableImporterContent(
        ...
      )
    }
    ```

------------------

## MxStorage

Para usar la librería tienes que agregar el módulo de la librería al `imports` del módulo de tu proyecto de Angular donde vayas a usarlo.
```ts
import { AngularFireModule } from "@angular/fire";
import { MxStorageModule } from '@marxa/storage';

@NgModule({
  declarations: [],
  imports: [
    MxStorageModule,
    //No se te olvide inicializar tu app de AngularFire
    AngularFireModule.initializeApp(environment.firebaseConfig) 
  ],
})
export class AppModule {}
```


### Files Picker
Esta librería cuenta con un **Files Picker** donde el cliente puede dar click o arrastrar los archivos para agregarlos y luego subirlos a **Firebase Storage**. Este componente tiene la especial colaboración de [**NgxDropzone**](https://www.npmjs.com/package/ngx-dropzone)

| ❗  Antes de empezar  |
|----------------------------------|
En tu proyecto de firebase, si no has creado un valde (*bucket*) pa' cargar todas las cosas, ve a la sección y pícale al botón de **Comenzar**

#### **Ejemplos**

#### **Uso básico**
```html
<mx-files-picker
[maxFileSize]="2097152"
path="cosas"
prefixName="marxa-"
[metadata]="{autor: 'yomero'}"
toggleButtonLabel="Cargar archivos"
[multiple]="false"
dropzoneLabel="Toca o arrastra un archivo hasta aquí"
uploadButtonLabel="Subir"
color="primary"
></mx-files-picker>
```

#### **Con modal**
En veces, necesitas que el diseño no te estorbe. Para esas ocasiones puedes usar el modo **Modal** el cual tendrá el mismo efecto.

```html
<mx-files-picker
color="primary"
[openModal]="true"
></mx-files-picker>
```

#### **Sin botón de subida**
Cuando necesites tener control de la subida de los datos, puedes quitar el botón disparador de subida y usar el **Service** que te permita ejecutar el disparador y tener control observable del evento.

```html
<mx-files-picker
color="primary"
[uploadButton]="false"
></mx-files-picker>
```

```ts
@Component({
  selector: 'app-root',
  templateUrl: './app.component.html',
  styleUrls: ['./app.component.scss']
})
export class AppComponent implements OnDestroy {

  constructor(
    private _storage: MxStorage
  ) { }

  onUpload(){
    this._storage.upload().subscribe( (files:iUploadedFile[]) => {
      console.log(files)
    })
  }
}

```

| ❗  Importante  |
|----------------------------|
En caso de que quieras acceder a los archivos listos para subirse puedes acceder a través del servicio 

### **Configuración**
Ahora se muestra la tabla de opciones con las que cuenta el **Files Picker**.

| **Atributo** | **Descripción** |
|--------------|-----------------|
| @Input() path: ``string`` | Permite crear rutas o carpetas donde se guardarán los archivos |
| @Input() prefixName: ``string`` | Agrega un prefijo al nombre del archivo trepado |
| @Input() metadata?: ``Object`` | **(Opcional)** Si lo agregas, debe ser un objeto con la metadata que quieras o necesites |
| @Input() multiple: ``boolean`` | **Default: false** Permite cargar varios archivos a la vez en el Dropzone |
| @Input() maxFileSize?: ``number`` | **(Opcional)** Evita que se suban archivos muy pesados agregando los bytes que quieres limitar. Ej. 2Mb  = 2097152b |
| @Input() showDropzone: ``boolean`` = false | **Default: false** Muestra el dropzone desde un principio y omite el botón de mostrar dropzone |
| @Input() uploadButton: ``boolean`` | **Default: true** Muestra u oculta el botón disparador de subida de archivos |
| @Input() uploadStatus: ``boolean`` | **Default: true** Muestr u oculta la barra de status del archivo subido |
| @Input() toggleButtonLabel: ``string`` | **Default: 'Subir archivos'** Mensaje que se muestra en el botón que muestra el Dropzone |
| @Input() uploadButtonLabel: ``string`` | **Default: 'Subir'** Mensaje que se muestra en el botón que sube los archivos |
| @Input() dropzoneLabel: ``string`` | **Default: 'Arrastra los archivos o toca aquí'** Mensaje que se muestra dentro del Dropzone |
| @Input() disable: ``boolean`` | **Default: false** Deshabilita el botón de subida de archivos. Perfecto para validar si no se han subido archivos | 
| @Input() color: ``'primary'``, ``'accent'`` o ``'warn'`` | **Default: 'primary'** Basado en **Angular Material Color Palette** muestra el botón de subida de archivos depende el color seleccionado |
| @Input() openModal: ``boolean`` | **Default: false** Define si se abrirá el dropzone en el lugar del botón o en un modal |
| @Output() uploadComplete: ``iUploadedFile[]`` | Informa cuando terminó de subir todos los archivos y entrga un array con la información de archivos subidos. |
| @Output() onLoaded: ``void`` | Informa cuando los archivos fueron cargados en el dropzone |


### **Import File**
Este componente importa los datos de un archivo CSV a la base de datos de **Firestore** agregando cada fila como un documento de la colección.

| ❗  Antes de empezar  |
|----------------------------------|
Si quieres que esto funcione. Debes tener inicializada tu base de datos de **Firestore** en proyecto.

| ⚠️ CUIDADO          |
|----------------------------|
Subir los registros de un CSV te costará 1 registro de escritura de la **Firestore** por cada 500 filas en tu documento. Peeeero...
Si mandas llamar la colección que subiste te costará **la cantidad total** de la colección de registros de lectura de la **Firestore** de los cuales 14k son gratuitos, de esa cantidad en adelante **puede costarte mucho dinero** estar llamando esta colección de documentos.


#### **Ejemplo de uso**
Supongamos que vas a subir una lista de productos a una E-commerce. 


```html
<mx-import-file
[requiredColumns]="needColumns"
[importCol]="'productos'"
[idField]="'referencia'"
[renameColumns]="false"
></mx-import-file>
```


- Probablemente vas a necesitar validar que el archivo contenga las columnas necesarias. Para ello puedes usar `requiredColumns`.
- Probablemente vas a necesitar subirlos a una colección en específico. Para ellos, uso `importCol`.
- Probablemente necesites que una de las columnas sea usada como id de documento de **Firestore**, para ellos puedes usar `idField`. OJO: Los campos de la columna que selecciones serán transfromados a `lowerCase`, se cambiarán los espacios, comas, puntos, diagonales, arrobas y guiones medios por guiones bajos (`_`) y se omitirán comillas
- Si necesitas que las columnas cambien de nombre. Puedes usar el editor de columnas


| :eye: CUIDADO          |
|------------------------|
Los archivos deben subirse en CSV, sin filas de encabezado o títulos. La primera fila debe ser la de los nombres de las columnas.


### Exportar
Para exportar colecciones de **Firestore**a un archivo CSV, sigue los siguientes pasos:
1. Has una consulta de tu colección en **Firestore** y guárdala en un array.
2. Usa el método `downloadList`

```ts
import { AngularFirestore } from '@angular/fire/firestore';
import { MxStorage } from '@marxa/storage';

@Component({
  selector: 'app-root',
  templateUrl: './app.component.html',
  styleUrls: ['./app.component.scss']
})
export class AppComponent implements OnDestroy {

  constructor(
    private _afs: AngularFirestore,
    private _storage: MxStorage
  ) { }

  async onDownload(){
    let list: any[] = [];
    const collection = await this._afs.collection('collection').get()
    
    // Si tu lista no es muy larga puedes usar el básico forEach
    collection.forEach(item => {
      list.push(item.data())
    })

    // De lo contrario, MxStorage cuenta con un iterador asíncrono
    await this._storage.asynForEach(list, (item: any) => {
      list.push(item.data())
    })

    this._storage.downloadList(list)
    // Esto automáticamente descargará el archivo en tu dispositivo
  }
}

```

### MODELOS MxStorage

#### *iUploadInfo* 
Información del archivo cargado a **Firebase Storage**

| **Propiedad** | **Descripción** |
|---------------|-----------------|
| fileName?: `string` | **Optional**  Muestra el nombre con el cual fue cargado el archivo |
| uploaded?: ``Date`` | firebase.firestore.Timestamp | **Optional** Se registra como tipo `Date` pero si se carga a **Firestore** retorna como `firebase.Timestamp` |
| metadata?: ``any`` | **Optional** El objeto cargado como metadata |
| format?: ``string`` | **Optional** Formato del archivo cargado. Ejemplo: 'image/jpg' |


#### *iUploadedFile* 
**Se extienda a `iUploadInfo`** Resultado del archivo cargado a **Firebase Storage**

|**Propiedad**|**Descripción**|
|--------------|-----------------|
| url?: ``string`` | La url con la cual acceder al archivo en **Storage**
| uploadedState?: `number` o `true` | No'más sirve para el momento de estar cargando |


### MxStorage SERVICIO

#### *Propiedades*
|**Propiedad**|**Descripción**|
|--------------|-----------------|
| files: ``any[]`` | Los archivos que han de subirse |
| recordsLength: ``number`` | La cantidad de filas del documento CSV que se importará a **Firestore** |
| headerMap: ``Map<string, string>`` | Map de las columnas del documento CSV que se importará a **Firestore**. El primer `string` corresponde al valor original y el segundo al que se ha de transformar |
| mergeImport: ``boolean`` | **Default: true** Controla si al subir filas, el valor es `true` y el documento tiene el mismo ID, los datos del documento no se borrarán, sólo se actualizarán los campos existentes en el documento. Si no existen los creará. De lo contrario, si es `false` se borrarán los campos anteriores y se sustituirán por los nuevos. |
| RawHeaderList(): `Observable<string[]>` | Método `get` que provee un observable de la lista de nombres de las columnas del archivo CSV que se cargará. Retorna una observable con la lista de nombres de las columnas

#### **Observables**
|**Observable**|**Descripción**|
|--------------|-----------------|
| fileUploadedStatus$:`` Subject<iUploadedFile>`` | Notifica el status del archivo que se está subiendo en el momento |
| upload$:`` Subject<void>`` | Notifica cuando cuando se ha solicitado subir los archivos |
| uploadComplete$:`` Subject<iUploadedFile[]>`` | Notifica cuando los archivos se han cargado y los muestra en un array |
| clearDropzone$:`` Subject<void>`` | Le notifica al dropzone que debe cerrarse |
| closeImportDialog$:`` Subject<void>`` | Le notifica al modal que debe cerrarse |
| closeSpinner$:`` Subject<void>`` | Le notifica al spinner que debe cerrarse |
| importState$: ``BehaviorSubject<string>`` | Notifica el estado de la importación |
| recordsReaded$: ``BehaviorSubject<number>`` | Notifica cuantos registros se han cargado a **Firestore** |

#### *Métodos*
##### `upload()` 
Ejecuta el ciclo de subir los archivos en `MxStorage.files: any[]`


##### `uploadFile(file, path, prefixName?, metadata?): Observable<iUploadedFile>` 
Sube un único archivo y retorna un observable con el archivo cargado.

| Parámetro | Descripción|
|-----------|------------|
| file: `any` | Archivo nativo |
| path: `string` | La ruta donde será almacenada dentro del bucket de **Firebase Storage**
| prefixName: `string | null` | (Opcional) Se agrega al nombre del archivo nativo para ser guardado así. |
| metadata: `any` | (Opcional) Si este parámetro se asigna se agrega a la metadata del archivo. DEBE SER OBJETO |

#####  `deleteFiles(files, path?): Promise<void> ` 
Método para eliminar archivos del **Storage** returna una promsea vacía 

| Parámetro | Descripción|
|-----------|------------|
| files: `iUploadInfo[]` | Arreglo de archivos en la forma de interface `iUploadInfo` |
| path: `string` | (Opcional) Si no se especifica, se toma de la interface `iUploadInfo`, Ruta del archivo a eliminarse |

##### `asyncForEach(array, callback): Promise<void>` 
Método para iterar arreglos de manera asíncrona. Retorna una promsea vacía 

| Parámetro | Descripción|
|-----------|------------|
| array: `any[]` | Un arreglo cualquiera |
| callbarck: `any` | Función que se realizará por cada elemento del array, Proporciona las propiedades `value` y `index` |


##### `downloadList(list, filename): Promise<void>` 
Método para descargar un array de objetos en formato CSV. Los objetos anidados obtendrán el nombre del objeto padre.hijo. Sólo permite 3 niveles de objetos anidados.

| Parámetro | Descripción|
|-----------|------------|
| list: `any[]` | Cualquier array de objetos |
| filename: `string` | Nombre del archivo al descargarse |


##### `getRawValue(value): Object` 
Método que extrae objetos anidados a un sólo objeto. Sólo permite 3 niveles de objetos anidados.

| Parámetro | Descripción|
|-----------|------------|
| value: `any` | Objeto cualquiera |


##### `importFile(collection?, idField?, mergeImport?): Promise<void>`
Método para importar archivos CSV a **Firestore**. Retorna una promsea vacía

| Parámetro | Descripción|
|-----------|------------|
| collection: `string` | (Opcional) El nombre de la colección a la que se importará. Si no se provee este parámetro se creará una colección llamada 'imports'.|
| idField: `string` | (Opcional) Nombre de la columna del archivo que servirá para marcar como id cada documento en la colección. **NOTA:** Si el id del documento resultante existe en la colección, el documento se actualizará. El id será transformado a lowerCase, cambiando espacios, puntos, comas, diagonales, arrobas y guiones medios por guiones bajos (`_`) y eliminado las comillas. |
| mergeImport: `boolean` | (Opcional, Default: `true`) Controla si al subir filas, el valor es `true` y el documento tiene el mismo ID, los datos del documento no se borrarán, sólo se actualizarán los campos existentes en el documento. Si no existen los creará. De lo contrario, si es `false` se borrarán los campos anteriores y se sustituirán por los nuevos. |



## Contacto
Email: jguzman@marxadigital.com