npm package: @seniorlogistica/yms-components
Description: Projeto criado para componentes reutilizaveis entre os frontends do portal de agendamento.
Author: Rodrigo Cardoso
Latest version: 2.6.2

# Portal Agendamento Components

Projeto criado para componentes reutilizaveis entre os frontends do portal
de agendamento.

### Instalação

` npm i --save @seniorlogistica/yms-components `

## Tópicos
- [Refresh Button](#refresh-button)
- [Decorators para Salvar Filtro aplicado ao navegar entre telas](#decorators)
    - [Premissas para Utilização](#decorators-premisses)
    - [Implementação Básica](#decorators-basic)
    - [Salvar Filtros](#decorators-save)
    - [Aplicar Filtros ao carregar Tela](#decorators-applyOnLoad)
    - [Atualização do Estado do Filtro](#decorators-update)
    - [Limpeza do Estado do Filtro](#decorators-clear)
- [Componente de seleção de plantas](#plantComponent)
    - [Direcionamento e validação das rotas](#plantComponent-routes)
    - [Obtendo o valor da planta vinculada a sessão](#plantComponent-value)
    - [Internacionalização](#plantComponent-i18n)
- [Componente de visualização dos anexos da agenda](#agendaAnexoComponent)
    - [instalando o componente](#agendaAnexoComponent-using)
    - [Utilizando o componente na tela](#agendaAnexoComponent-component)
    - [Utilizando o componente como modal](#agendaAnexoComponent-dialog)

# Refresh Button
<a name='refresh-button'></a>
O componente refresh-button necessíta de uma chave com o nome "update_grid" no arquivo de translate

# Decorators para Salvar Filtro aplicado ao navegar entre telas
<a name='decorators'></a>
Para facilitar o salvamento e recarregamento de filtros aplicados ao component <p-table> nos projetos de frontend foi criado um serviço que utiliza o sessionStorage do browser para armazenar o filtro e também foram criados decorators para aplicar e manter o estado dos filtros de maneira simplificada.
Lembrando que o salvamento é feito propositalmente no sessionStorage do browser para que esta informação esteja acessível somente à janela onde esta foi criada, isto é, ao aplicar e realizado o salvamento de um filtro em uma janela do browser, este não estará disponível em outras instâncias, ficando restrita apenas à janela que foi criada.

## Premissas para Utilização
<a name='decorators-premisses'></a>
Para a utilização desta funcionalidade de salvamento deve-se respeitar algumas premissas.
- **yms-components**: Deve-se obrigatóriamente estar utilizando o pacote yms-components
- **Aliado à estrutura gerada pelo gerador**: Deve utilizar aliado ao <p-table> gerado pelo gerado de frontends nas telas de listagem bem como os métodos de aplicação de filtros e refresh criados pelo @seniorsistemas/frontend-generator.

## Implementação Básica
<a name='decorators-basic'></a>

- **Injetar o Component p-table**: Derverá ser injetado o component HTML <p-table> para ser acessível via typescript do component.

``` html
<p-table #componentTable [value]="componentGridData" [columns]="componentGridColumns" dataKey="id" [lazy]="true"
    [scrollable]="true" [resizableColumns]="true" sortMode="single" [paginator]="true"
    [totalRecords]="componentTotalRecords" rows="10" [rowsPerPageOptions]="[10, 20, 50, 100]"
    [(selection)]="componentSelection" (onLazyLoad)="componentGridChange($event)" [loading]="componentGridLoading"
    [sortOrder]="1" >
```

``` typescript
import { ViewChild } from "@angular/core";
import { Table } from "primeng/table";

...

@Component({
    templateUrl: "./component-list.component.html",
    providers: [ConfirmationService],
})
export class ComponentListComponent implements OnInit, OnDestroy {
    ...
    @ViewChild('componentTable') 
    public componentTable: Table;
    ...
}
```

- **Injetar o Serviço SaveScreenFilterService**: Injetar o serviço SaveScreenFilterService para que seja fornecido com item da configuração dos decorators

``` typescript
import { SaveScreenFilterService } from "@seniorlogistica/yms-components";

...

@Component({
    templateUrl: "./component-list.component.html",
    providers: [ConfirmationService],
})
export class ComponentListComponent implements OnInit, OnDestroy {
    ...
    constructor(
        private saveScreenFilterService:SaveScreenFilterService
    ) {}
}
```
- **Criar o objeto SaveFilterConfig**: No método ngOnInit criar o objeto SaveFilterConfig que é necessário para o funcionamento dos decorators.

``` typescript
import { SaveFilterConfig } from "@seniorlogistica/yms-components";

...

@Component({
    templateUrl: "./component-list.component.html",
    providers: [ConfirmationService],
})
export class ComponentListComponent implements OnInit, OnDestroy {
    //Declaração como Field
    public saveFilterConfig: SaveFilterConfig;
    ...
    
    public ngOnInit() {
        this.saveFilterConfig = {
            environment: environment, //importação dos dados do projeto
            formFilter: this.conponentFiltersForm, //formGroup que agrega os filtros permitidos na listagem
            saveScreenFilterService: this.saveScreenFilterService, //Injeção do serviço que realiza o salvamento
            searchMethodName: "componentSearch", //Método que é chamado a aplicar os filtros
            tableRef: this.componentTable, //@ViewChild do component p-table
            tableCurrentListParams: "componentCurrentListParams"// Objeto ListParams que faz parte do componente e guarda os tokens de consulta
        };
    }

}
```

- **Interface SaveFilterInfoProvider**: Implementar a interface *SaveFilterInfoProvider* no componente para que as informações necessárias ao salvamento/aplicação filtros estejam disponíveis através da chamada do método  getSaveFilterConfig() para que os decorators funcionem adequadamente.

``` typescript
import { SaveFilterInfoProvider, SaveFilterConfig } from "@seniorlogistica/yms-components";

...

@Component({
    templateUrl: "./component-list.component.html",
    providers: [ConfirmationService],
})
export class ComponentListComponent implements OnInit, OnDestroy, SaveFilterInfoProvider {
    ...

    getSaveFilterConfig(): SaveFilterConfig {
        return this.saveFilterConfig;
    }
}
```

## Salvar Filtros
<a name='decorators-save'></a>
No método onde é aplicado os filtros na tela de listagem deve se adicionado o decorator @StoreFilters() para que seja armazenado o filtro ao ser executado qualquer filtro no grid.

``` typescript
import { StoreFilters } from "@seniorlogistica/yms-components";

...

@Component({
    templateUrl: "./component-list.component.html",
    providers: [ConfirmationService],
})
export class ComponentListComponent implements OnInit, OnDestroy, SaveFilterInfoProvider {
    ...
    @StoreFilters()//Decorator responsável por salvar o filtro aplicado
    public componentSearch(table: any) {
        if (!this.componentFiltersForm.valid) {
            return this.validateAllFormFields(this.componentFiltersForm);
        }

        const filterData = this.componentFiltersForm.getRawValue();
        this.componentFiltersPanelCollapsed = true;
        this.componentResetGrid(table, { filterData });
    }
    ...
}
```

## Aplicar Filtros ao carregar Tela
<a name='decorators-applyOnLoad'></a>
No método ngOnInit que é executado ao inicializar a tela deve ser adicionado o decorator @ApplyFilterOnInit() ao método ngOnInit() e também adicionar @ApplyFilterOnGridChange() ao método que esteja vinculado ao evento (onLazyLoad) do p-table para que ao abrir a tela e existir um filtro já armazenado, este seja aplicado

``` typescript
import { ApplyFilterOnInit, ApplyFilterOnGridChange } from "@seniorlogistica/yms-components";

...

@Component({
    templateUrl: "./component-list.component.html",
    providers: [ConfirmationService],
})
export class ComponentListComponent implements OnInit, OnDestroy, SaveFilterInfoProvider {
    ...
     @ApplyFilterOnInit()//Decorator que é responsável por localizar filtro salvo e aplicá-lo na abertura da tela
    public ngOnInit() {
       ...
    }
    
     @ApplyFilterOnGridChange()
    public componentGridChange(event: LazyLoadEvent) {
        ...
    }
    ...
}
```

## Atualização do Estado do Filtro
<a name='decorators-update'></a>
Como é possível remover o filtro token a token que foi aplicado é necessário aplicar o decorator @UpdateStoredFilters() no método de remoção dos tokens para que seja atualizado o filtro salvo assim que removido um token.

``` typescript
import { UpdateStoredFilters } from "@seniorlogistica/yms-components";

...

@Component({
    templateUrl: "./component-list.component.html",
    providers: [ConfirmationService],
})
export class ComponentListComponent implements OnInit, OnDestroy, SaveFilterInfoProvider {
    ...
    @UpdateStoredFilters()//Decoretor responsável por realizar a atualização do filtro salvo
    public componentRemoveToken(table: any, token: CustomIToken) {
        if (token.id == "status") {
            this.componentFiltersForm
                .get(token.id)
                .setValue(this.componentFiltersForm
                              .get(token.id)
                              .value.filter((val: any) => val != token.value));
        } else {
            this.componentFiltersForm.get(token.id).setValue(undefined);
        }        

        const filterData = this.componentFiltersForm.getRawValue();
        this.componentResetGrid(table, { filterData });
    }
    ...
}
```

## Limpeza do Estado do Filtro
<a name='decorators-clear'></a>
Para executar a remoção do filtro salvo ao limpar todos os filtros da tela é necessário aplicar o decorator @ClearStoredFilters() no método de limpeza dos filtros na tela de listagem.

``` typescript
import { ClearStoredFilters } from "@seniorlogistica/yms-components";

...

@Component({
    templateUrl: "./component-list.component.html",
    providers: [ConfirmationService],
})
export class ComponentListComponent implements OnInit, OnDestroy, SaveFilterInfoProvider {
    ...
    @ClearStoredFilters()
    public componentClear(table: any) {
        this.componentFiltersForm.reset();
        const filterData = this.componentFiltersForm.getRawValue();
        this.componentResetGrid(table, { filterData });
    }
    ...
}
```

<a name="plantComponent"></a>
# Componente de seleção de plantas

O componente de seleção de plantas foi criado para centralizar o vínculo da planta à sessão. Para isso, foram desenvolvidas regras que devem ser utilizadas para a utilização desse módulo.

<a name="plantComponent-routes"></a>
## Direcionamento e validação das rotas

Para direcionar qualquer rota para o componente de seleção de planta, é necessário dois passos. Importar o módulo `SelectPlantModule` no módulo que será utilizado e empacotar todas as rotas dentro de uma validação `canActivate` utilizando o serviço `ValidatePlant` que deve estar no array de providers do módulo.
Segue exemplo:

``` typescript
export const routes: Routes = [
    {
        path: "",
        canActivate: [ ValidatePlant ],
        children: [
            /* 
            * Rotas da aplicação
            */
        ]
    }
];

@NgModule({
    imports: [
        // Módulo da aplicação
        RouterModule.forChild(routes),
        SelectPlantModule
    ],
    providers: [
        ValidatePlant
    ]
})
export class AppModule {}
```
Dessa maneira, qualquer caminho que direcione para alguma das rotas da aplicação, primeiramente vai ser validado se a planta já foi selecionada ou não.

<a name="plantComponent-value"></a>
### Obtendo o valor da planta vinculada a sessão

Para saber qual planta foi selecionada pelo usuário, é necessário a utilização do serviço `SelectPlantService`. Para isso, pode-se injetar o serviço dentro de um componente e após isso, chamar o método `getPlant()`. Segue exemplo.

``` typescript
@Component({
    // Configuração do componente
})
export class MyComponent {
    constructor(private selectPlantService: SelectPlantService) {
        let planta = selectPlantService.getPlant();
    }
}
```

Também é possível verificar se a planta foi selecionada chamando o método `isPresent()` do serviço `SelectPlantService`.

<a name="plantComponent-i18n"></a>
### Internacionalização

Para que a tradução funcione, é necessário que algumas chaves sejam adicionadas no arquivo de internacionalização. Segue as chaves com um valor de exemplo já setado.

```js
{
    "nothing_found": "Nada encontrado",
    "filter": "Filtrar",
    "clear": "Limpar",
    "select": "Selecionar",
    "continue": "Continuar",
    "select_plant": "Selecionar planta",
    "yms.components.select_plant_title": "Selecione a planta",
    "yms.components.select_plant_search_title": "Selecione a planta",
    "yms.components.select_plant.plant_name": "Nome",
    "yms.components.select_plant_empty_state": "Selecione a planta",
    "yms.components.select_plant_empty_state_description": "É necessário selecionar a planta para acessar a Gestão de Pátio",
    "yms.components.select_plant_has_plant": "Planta já selecionada",
    "yms.components.select_plant_has_plant_description": "Sua sessão já possui uma planta selecionada"
}
``` 
<a name="agendaAnexoComponent"></a>
# Componente de exibição de anexos da agenda

Esse componente deve ser utilizado para exibir os anexos do agendamento. Ele dá a opção de fazer o download de um anexo.
Além de poder visualizar os anexos dentro de um componente específico ou dentro de um `DynamicDialog`

<a name="agendaAnexoComponent-using"></a>
### Utilizando os componentes

Para utilizar os componentes, deve ser importado o modulo `AgendaAnexosModule`. Segue exemplo:

``` typescript
@NgModule({
    ...,
    imports: [
        ...,
        AgendaAnexosModule
    ]
})
export class MyModule {}
```

<a name="agendaAnexoComponent-component"></a>
#### Exibindo o componente diretamente

Para exibir os anexos diretamente na tela, utilize o componente `agenda-anexos` no HTML. O componente espera 2 argumentos: `anexos` e `agendaId`

```html
<div>
    <h1> Agenda </h1>
    <agenda-anexos [anexos]="myAgenda.anexo" [agendaId]="myAgenda.id"></agenda-anexos>
</div>
```

<a name="agendaAnexoComponent-dialog"></a>
#### Abrindo modal para visualização dos componentes

A biblioteca já disponibiliza um componente para visualizar os anexos em um modal. Para isso faça os seguintes passos:

- Em seu módulo importe o seguinte módulo da biblioteca do primeng: `DynamicDialogModule`
- Em seu componente adicione `DialogService` nos providers e injete ele no construtor
```typescript
@Component({
    ...,
    providers: [DialogService]
})
export class MyComponent {
    constructor(private dialogService: DialogService) {
        
    }
}
```
- Para abrir o modal, utilie o método `open` do objeto `dialogService` passando o componente `AgendaAnexosDialogComponent` como primeiro argumento.
- No segundo argumento, devem ser passadas as configurações do modal. Na configuração `data` devem ser passados os seguintes campos: `anexos` e `agendaId`
```typescript
this.dialogService.open(AgendaAnexosDialogComponent, {
    header: "myTitle",
    width: "600px",
    data: {
        anexos: myAgenda.anexo,
        agendaId: myAgenda.id
    }
})
```