Commit 8472f529 authored by RenanMontenegro3's avatar RenanMontenegro3

feat: ajustes em componentes de aceite, blacklist, navio, bercos e csv

parent 8533bdd0
import { TestBed } from '@angular/core/testing';
import { CsvService } from './csv.service';
describe('CsvService', () => {
let service: CsvService;
beforeEach(() => {
TestBed.configureTestingModule({});
service = TestBed.inject(CsvService);
});
it('should be created', () => {
expect(service).toBeTruthy();
});
});
import { Injectable } from '@angular/core';
@Injectable({
providedIn: 'root'
})
export class CsvService {
public saveDataInCSV(data: Array<any>): string {
console.log(data)
if (!Array.isArray(data) || data.length === 0) {
console.warn('Nenhum dado disponível para exportação.');
return '';
}
let propertyNames = Object.keys(data[0]);
console.log(Object.keys(data[0]))
let rowWithPropertyNames = Object.keys(data[0]).join(';') + '\n';
let csvContent = rowWithPropertyNames;
let rows: string[] = [];
data.forEach((item) => {
let values: string[] = [];
propertyNames.forEach((key) => {
let val: any = item[key];
if (val !== undefined && val !== null) {
val = new String(val);
} else {
val = '';
}
values.push(val);
});
rows.push(values.join(';'));
});
csvContent += rows.join('\n');
return csvContent;
}
public importDataFromCSV(csvText: string): Array<any> {
const propertyNames = csvText.slice(0, csvText.indexOf('\n')).split(',');
const dataRows = csvText.slice(csvText.indexOf('\n') + 1).split('\n');
let dataArray: any[] = [];
dataRows.forEach((row) => {
let values = row.split(',');
let obj: any = new Object();
for (let index = 0; index < propertyNames.length; index++) {
const propertyName: string = propertyNames[index];
let val: any = values[index];
if (val === '') {
val = null;
}
obj[propertyName] = val;
}
dataArray.push(obj);
});
return dataArray;
}
}
......@@ -46,75 +46,61 @@
<div class="row">
<mat-form-field class="field medium" appearance="outline">
<mat-label>Calado de Entrada</mat-label>
<input matInput type="text" [currencyMask]="{ prefix: '',thousands: '.', decimal: ',' }" formControlName="calado_entrada">
<input matInput type="text" [currencyMask]="{ prefix: '',thousands: '.', decimal: ',' }"
formControlName="calado_entrada">
<mat-error *ngIf="empForm.get('calado_entrada')?.invalid">{{ getErrorMessage('calado_entrada') }}</mat-error>
</mat-form-field>
<mat-form-field class="field medium" appearance="outline">
<mat-label>Calado de Saída</mat-label>
<input matInput type="text" [currencyMask]="{ prefix: '',thousands: '.', decimal: ',' }" formControlName="calado_saida">
<input matInput type="text" [currencyMask]="{ prefix: '',thousands: '.', decimal: ',' }"
formControlName="calado_saida">
<mat-error *ngIf="empForm.get('calado_saida')?.invalid">{{ getErrorMessage('calado_saida') }}</mat-error>
</mat-form-field>
</div>
<div class="row">
<mat-form-field class="field large" appearance="outline">
<mat-label>DWT (Atual)</mat-label>
<input matInput type="text" [currencyMask]="{ prefix: '',thousands: '.', decimal: ',' }"
formControlName="dwt">
<input matInput type="text" [currencyMask]="{ prefix: '',thousands: '.', decimal: ',' }" formControlName="dwt">
<mat-error *ngIf="empForm.get('dwt')?.invalid">{{ getErrorMessage('dwt') }}</mat-error>
</mat-form-field>
<mat-form-field class="field medium" appearance="outline">
<mat-label>Pontal (m)</mat-label>
<input matInput type="text" [currencyMask]="{ prefix: '',thousands: '.', decimal: ',' }" formControlName="pontal">
<input matInput type="text" [currencyMask]="{ prefix: '',thousands: '.', decimal: ',' }"
formControlName="pontal">
<mat-error *ngIf="empForm.get('pontal')?.invalid">{{ getErrorMessage('pontal') }}</mat-error>
</mat-form-field>
<mat-form-field class="field small" appearance="outline">
<mat-label>Ponte Mfold (m)</mat-label>
<input matInput type="text" [currencyMask]="{ prefix: '',thousands: '.', decimal: ',' }" formControlName="ponte_mfold">
<mat-error *ngIf="empForm.get('ponte_mfold')?.invalid">{{ getErrorMessage('ponte_mfold') }}</mat-error>
</mat-form-field>
</div>
<div class="row">
<mat-form-field class="field large" appearance="outline">
<mat-label>Mfold Quilha (m)</mat-label>
<input matInput type="text" [currencyMask]="{ prefix: '',thousands: '.', decimal: ',' }" formControlName="mfold_quilha">
<mat-error *ngIf="empForm.get('mfold_quilha')?.invalid">{{ getErrorMessage('mfold_quilha') }}</mat-error>
</mat-form-field>
<mat-form-field class="field medium" appearance="outline">
<mat-label>Categoria</mat-label>
<mat-select formControlName="categoria">
<mat-select formControlName="categoria" (selectionChange)="onCategoriaChange($event.value)">
<mat-option *ngFor="let cat of categoria" [value]="cat.id">
{{ cat.nome }}
</mat-option>
</mat-select>
<mat-error *ngIf="empForm.get('categoria')?.invalid">{{ getErrorMessage('categoria') }}</mat-error>
</mat-form-field>
<!-- Campo Mfold Quilha (m) só aparece se for Granel Líquido -->
<mat-form-field class="field large" appearance="outline" *ngIf="selectedCategoriaNome === 'Granel Líquido'">
<mat-label>Mfold Quilha (m)</mat-label>
<input matInput type="text" [currencyMask]="{ prefix: '',thousands: '.', decimal: ',' }"
formControlName="mfold_quilha">
<mat-error *ngIf="empForm.get('mfold_quilha')?.invalid">{{ getErrorMessage('mfold_quilha') }}</mat-error>
</mat-form-field>
<mat-form-field class="field small" appearance="outline">
<mat-label>Flag</mat-label>
<mat-select formControlName="flag">
<mat-select-trigger>
<img
*ngIf="selectedCountry"
[src]="selectedCountry.flagUrl"
width="20"
height="15"
alt="{{ selectedCountry.name }} flag"
style="margin-right: 8px"
/>
<img *ngIf="selectedCountry" [src]="selectedCountry.flagUrl" width="20" height="15"
alt="{{ selectedCountry.name }} flag" style="margin-right: 8px" />
{{ selectedCountry?.name }}
</mat-select-trigger>
<mat-option
*ngFor="let country of countries"
[value]="country.id"
(onSelectionChange)="onCountrySelected(country)"
>
<img
[src]="country.flagUrl"
width="20"
height="15"
alt="{{ country.name }} flag"
style="margin-right: 8px"
/>
<mat-option *ngFor="let country of countries" [value]="country.id"
(onSelectionChange)="onCountrySelected(country)">
<img [src]="country.flagUrl" width="20" height="15" alt="{{ country.name }} flag"
style="margin-right: 8px" />
{{ country.name }}
</mat-option>
</mat-select>
......@@ -123,24 +109,26 @@
</mat-error>
</mat-form-field>
</div>
<div class="row">
<mat-form-field class="field small" appearance="outline" *ngIf="selectedCategoriaNome === 'Granel Líquido'">
<mat-label>Ponte Mfold (m)</mat-label>
<input matInput type="text" [currencyMask]="{ prefix: '',thousands: '.', decimal: ',' }"
formControlName="ponte_mfold">
<mat-error *ngIf="empForm.get('ponte_mfold')?.invalid">{{ getErrorMessage('ponte_mfold') }}</mat-error>
</mat-form-field>
</div>
<div class="row">
<mat-form-field class="field full-width" appearance="outline">
<mat-label>Observação</mat-label>
<input matInput type="text" formControlName="obs">
<mat-error *ngIf="empForm.get('obs')?.invalid">{{ getErrorMessage('obs') }}</mat-error>
</mat-form-field>
</div>
<div class="row file-upload-container">
<mat-label style="color:cadetblue">Adicione o documente Q88 da embarcação</mat-label>
<input
type="file"
class="file-input"
(change)="onFileSelected($event)"
#fileUpload
style="display: none"
/>
<input type="file" class="file-input" (change)="onFileSelected($event)" #fileUpload style="display: none" />
<div class="file-upload-content">
<div class="file-info">
......@@ -148,12 +136,7 @@
</div>
<div class="upload-button-wrapper">
<button
mat-mini-fab
color="primary"
class="upload-btn"
(click)="fileUpload.click()"
>
<button mat-mini-fab color="primary" class="upload-btn" (click)="fileUpload.click()">
<mat-icon>attach_file</mat-icon>
</button>
</div>
......@@ -161,17 +144,25 @@
<div class="uploaded-image-container" *ngIf="imageUrl">
<img [src]="imageUrl" alt="Uploaded Image" class="uploaded-image" />
<button
mat-mini-fab
color="warn"
class="remove-image-btn"
(click)="removeImage()"
>
<button mat-mini-fab color="warn" class="remove-image-btn" (click)="removeImage()">
<mat-icon>close</mat-icon>
</button>
</div>
</div>
<div class="row">
<mat-form-field class="field full-width" appearance="outline">
<mat-label>Berços</mat-label>
<mat-select formControlName="bercosSelecionados" multiple>
<mat-option *ngFor="let berco of bercosDisponiveis" [value]="berco.nome">
Berço {{ berco.nome }}
</mat-option>
</mat-select>
<mat-hint>Selecione um ou mais berços</mat-hint>
</mat-form-field>
</div>
</div>
<div mat-dialog-actions class="action">
<button mat-raised-button type="button" color="warn" [mat-dialog-close]="false">Cancelar</button>
<button mat-raised-button color="primary" type="submit">Salvar</button>
......
......@@ -20,6 +20,7 @@ import { Observable, catchError, distinctUntilChanged, map, of, switchMap, tap }
import { HttpClient } from '@angular/common/http';
import { ToastrService } from 'ngx-toastr';
import { NgxCurrencyDirective } from 'ngx-currency';
import { BercosService } from '../../../services/bercos/bercos.service';
@Component({
selector: 'app-aceite-add',
......@@ -36,7 +37,7 @@ import { NgxCurrencyDirective } from 'ngx-currency';
MatSelectModule,
MatRadioModule,
MatIconModule,
MatSnackBarModule,NgxCurrencyDirective
MatSnackBarModule, NgxCurrencyDirective
],
templateUrl: './aceite-add.component.html',
styleUrls: ['./aceite-add.component.scss']
......@@ -51,7 +52,7 @@ export class AceiteAddComponent implements OnInit {
{ id: '2', nome: 'Granel Líquido' },
{ id: '3', nome: 'Carga Geral' }
];
constructor(
private _fb: FormBuilder,
private _empService: AceiteService,
......@@ -61,11 +62,12 @@ export class AceiteAddComponent implements OnInit {
private _dialog: MatDialog,
private http: HttpClient,
private toastService: ToastrService,
private _bercoService: BercosService // Injetando o serviço de berços
) {
this.empForm = this._fb.group({
imo: [data ? data.imo : '', Validators.required, this.ImoValidator()],
mmsi: [data ? data.mmsi : '', ],
mmsi: [data ? data.mmsi : '',],
nome: [data ? data.nome : '', Validators.required],
loa: [data ? data.loa : '', Validators.required],
boca: [data ? data.boca : '', Validators.required],
......@@ -76,8 +78,21 @@ export class AceiteAddComponent implements OnInit {
categoria: [data ? data.categoria : '', Validators.required],
flag: [data ? data.flag : '', Validators.required],
obs: [data ? data.obs : ''],
calado_entrada:[ '', Validators.required],
calado_saida:['', Validators.required],
calado_entrada: ['', Validators.required],
calado_saida: ['', Validators.required],
bercosSelecionados: ['']
,
});
this.empForm.get('categoria')?.valueChanges.subscribe(catId => {
const cat = this.categoria.find((c: any) => c.id === catId);
if (cat && cat.nome === 'Granel Líquido') {
this.empForm.get('mfold_quilha')?.setValidators([Validators.required]);
this.empForm.get('ponte_mfold')?.setValidators([Validators.required]);
} else {
this.empForm.get('mfold_quilha')?.clearValidators();
this.empForm.get('ponte_mfold')?.clearValidators();
}
this.empForm.get('mfold_quilha')?.updateValueAndValidity();
});
}
......@@ -86,6 +101,8 @@ export class AceiteAddComponent implements OnInit {
event.preventDefault();
}
}
countries = [
{ id: 1, name: 'Afeganistão', flagUrl: 'https://flagcdn.com/w20/af.png' },
{ id: 2, name: 'África do Sul', flagUrl: 'https://flagcdn.com/w20/za.png' },
......@@ -427,11 +444,24 @@ export class AceiteAddComponent implements OnInit {
onCountrySelected(country: { id: number; name: string; flagUrl: string }) {
this.selectedCountry = country;
}
bercosDisponiveis: any[] = [];
ngOnInit(): void {
if (this.data) {
this.empForm.patchValue(this.data);
}
this._bercoService.getBercosListSemPaginacao().subscribe({
next: (res) => {
console.log(res)
// Ajuste para consumir do _embedded.bercoResponseList
this.bercosDisponiveis = res && res._embedded && res._embedded.bercoResponseList
? res._embedded.bercoResponseList
: [];
console.log(this.bercosDisponiveis);
},
error: () => {
this.bercosDisponiveis = [];
}
});
this.empForm.get('imo')?.valueChanges
.pipe(
distinctUntilChanged(),
......@@ -457,12 +487,12 @@ export class AceiteAddComponent implements OnInit {
if (input.files && input.files.length > 0) {
const file = input.files[0];
const allowedTypes = ['application/pdf', 'application/msword', 'application/vnd.openxmlformats-officedocument.wordprocessingml.document'];
if (!allowedTypes.includes(file.type)) {
this.toastService.error('Apenas arquivos PDF ou Word são permitidos.');
return;
}
this.selectedFile = file;
this.fileName = file.name;
const reader = new FileReader();
......@@ -470,7 +500,8 @@ export class AceiteAddComponent implements OnInit {
this.imageUrl = (e.target?.result as string) || '';
};
reader.readAsDataURL(file);
}}
}
}
removeImage() {
this.imageUrl = null;
this.fileName = '';
......@@ -492,7 +523,12 @@ export class AceiteAddComponent implements OnInit {
);
};
}
selectedCategoriaNome: string = '';
onCategoriaChange(catId: any) {
const cat = this.categoria.find((c: any) => c.id === catId);
this.selectedCategoriaNome = cat ? cat.nome : '';
}
getErrorMessage(formControlName: string): string {
if (this.empForm.get(formControlName)?.hasError('required')) {
return 'Campo obrigatório';
......@@ -516,7 +552,7 @@ export class AceiteAddComponent implements OnInit {
if (this.empForm.valid) {
if (this.selectedFile) {
this._empService.addAceite(this.empForm.value,this.selectedFile).subscribe({
this._empService.addAceite(this.empForm.value, this.selectedFile).subscribe({
next: () => {
this._dialogRef.close(true);
this.toastService.success('Aceite cadastrado com sucesso!');
......@@ -526,7 +562,7 @@ export class AceiteAddComponent implements OnInit {
},
});
} else {
this._empService.addAceite(this.empForm.value,null).subscribe({
this._empService.addAceite(this.empForm.value, null).subscribe({
next: () => {
this._dialogRef.close(true);
this.toastService.success('Aceite cadastrado com sucesso!');
......@@ -534,7 +570,8 @@ export class AceiteAddComponent implements OnInit {
error: (err: any) => {
console.error(err);
},
}); }
});
}
}
}
}
......
<div mat-dialog-title>
<h1>{{ "Buscar Aceite" }}</h1>
</div>
<form [formGroup]="empForm" (ngSubmit)="onFormSubmit()">
<div mat-dialog-content class="content">
<div class="row">
<!-- IMO -->
<mat-form-field class="field full-width" appearance="outline">
<mat-label>IMO</mat-label>
<input
matInput
type="number"
(keydown)="preventNegative($event)"
min="0"
formControlName="imo"
/>
<mat-error *ngIf="empForm.get('imo')?.invalid">{{
getErrorMessage("imo")
<h1>{{ "Buscar Aceite" }}</h1>
</div>
<form [formGroup]="empForm" (ngSubmit)="onFormSubmit()">
<div mat-dialog-content class="content">
<div class="row">
<!-- IMO -->
<mat-form-field class="field full-width" appearance="outline">
<mat-label>IMO</mat-label>
<input matInput type="number" (keydown)="preventNegative($event)" min="0" formControlName="imo" />
<mat-error *ngIf="empForm.get('imo')?.invalid">{{
getErrorMessage("imo")
}}</mat-error>
</mat-form-field>
</div>
<!--
</mat-form-field>
</div>
<!--
<div class="row">
<mat-form-field class="field medium" appearance="outline">
<mat-label>Categoria</mat-label>
......@@ -33,7 +27,7 @@
getErrorMessage("categoria")
}}</mat-error>
</mat-form-field>
<mat-form-field class="field small" appearance="outline">
<mat-label>Flag</mat-label>
<mat-select formControlName="flag">
......@@ -71,17 +65,22 @@
</div>
-->
<div class="row">
<mat-form-field class="field medium" appearance="outline">
<mat-label>Data de Início</mat-label>
<input matInput type="date" formControlName="dataInicio" />
</mat-form-field>
<mat-form-field class="field medium" appearance="outline">
<mat-label>Data de Fim</mat-label>
<input matInput type="date" formControlName="dataFim" />
</mat-form-field>
</div>
<div mat-dialog-actions class="action">
<button
mat-raised-button
type="button"
color="warn"
[mat-dialog-close]="false"
>
Cancelar
</button>
<button mat-raised-button color="primary" type="submit">Buscar</button>
</div>
</form>
\ No newline at end of file
</div>
<div mat-dialog-actions class="action">
<button mat-raised-button type="button" color="warn" [mat-dialog-close]="false">
Cancelar
</button>
<button mat-raised-button color="primary" type="submit">Buscar</button>
</div>
</form>
......@@ -47,7 +47,7 @@ export class AceiteSearchComponent implements OnInit {
empForm: FormGroup;
selectedFile: File | null = null;
categoria: number[] = [1, 2,3];
categoria: number[] = [1, 2, 3];
constructor(
private _fb: FormBuilder,
......@@ -60,8 +60,9 @@ export class AceiteSearchComponent implements OnInit {
) {
this.empForm = this._fb.group({
imo: [''],
categoria: [''],
flag: [''],
dataInicio: [''],
dataFim: [''],
// ...outros campos...
});
}
......@@ -82,12 +83,12 @@ export class AceiteSearchComponent implements OnInit {
if (input.files && input.files.length > 0) {
const file = input.files[0];
const allowedTypes = ['application/pdf', 'application/msword', 'application/vnd.openxmlformats-officedocument.wordprocessingml.document'];
if (!allowedTypes.includes(file.type)) {
this.toastService.error('Apenas arquivos PDF ou Word são permitidos.');
return;
}
this.selectedFile = file;
this.fileName = file.name;
const reader = new FileReader();
......@@ -95,7 +96,8 @@ export class AceiteSearchComponent implements OnInit {
this.imageUrl = (e.target?.result as string) || '';
};
reader.readAsDataURL(file);
}}
}
}
getErrorMessage(formControlName: string): string {
if (this.empForm.get(formControlName)?.hasError('required')) {
......@@ -451,8 +453,8 @@ export class AceiteSearchComponent implements OnInit {
if (this.empForm.valid) {
const searchCriteria = {
imo: this.empForm.value.imo,
categoria: this.empForm.value.categoria,
flag: this.empForm.value.flag,
dataInicio: this.empForm.value.dataInicio,
dataFim: this.empForm.value.dataFim,
};
this._empService.searchAceite(searchCriteria).subscribe({
......
......@@ -3,7 +3,14 @@
[secondaryButtonColor]="'accent'" [secondaryButtonAction]="openSearchBercoForm.bind(this)">
</app-add-search>
<!-- Botão Exportar Excel Verde -->
<div class="main-body">
<button mat-raised-button color="primary" style="background-color: #43a047; color: #fff; margin: 12px 0;" (click)="saveDataInCSV('aceites')">
<mat-icon>download</mat-icon>
Exportar Excel
</button>
<div class="table-container" style="overflow-x: auto">
<table mat-table [dataSource]="dataSource" matSort>
<!-- ID Column -->
......@@ -95,4 +102,4 @@
[disabled]="paginator.pageIndex === paginator.getNumberOfPages() - 1">
Última Página
</button>
</div>
\ No newline at end of file
</div>
......@@ -24,10 +24,11 @@
<span *ngSwitchCase="'Y'" [ngStyle]="{ color: 'green' }">Navio Aceito</span>
<span *ngSwitchCase="'NE'" [ngStyle]="{ color: 'red' }">Aceite Negado</span>
<span *ngSwitchCase="'N'" [ngStyle]="{ color: 'orange' }">Em processamento</span>
</ng-container>
</ng-container>
</p>
</div>
</mat-card-content>
</mat-card>
<ng-container *ngIf="data.path">
......@@ -61,23 +62,33 @@
Status: <span [ngStyle]="{ color: 'green' }">Navio Aceito</span>
</p>
@if(data.restricoes){
<p class="field full-width" appearance="outline">
{{data.restricoes}}
</p>
<p class="field full-width" appearance="outline">
{{data.restricoes}}
</p>
}@else{
<p class="field full-width" appearance="outline">
O navio é compatível com os seguintes berços:
</p>
<div *ngIf="data?.bercos?.length > 0">
<div *ngFor="let berco of data.bercos">
{{ berco.nome }}
</div>
<p class="field full-width" appearance="outline">
O navio é compatível com os seguintes berços:
</p>
<div *ngIf="data?.bercos?.length > 0">
<div *ngFor="let berco of data.bercos">
{{ berco.nome }}
</div>
</div>
}
<p *ngIf="data.bercosSelecionados?.length > 0 " class="field full-width" appearance="outline">
<strong>Preferências de Berços do Usuário:</strong>
<span *ngIf="data.bercosSelecionados?.length > 0; else semPreferencias">
<span *ngFor="let berco of data.bercosSelecionados; let last = last">
{{ berco.nome || berco }}
<span *ngIf="!last">, </span>
</span>
</span>
<ng-template #semPreferencias>
Nenhuma preferência informada.
</ng-template>
</p>
</div>
<div *ngSwitchCase="'NE'">
<p class="field full-width" appearance="outline">
Status: <span [ngStyle]="{ color: 'red' }">Aceite Negado</span>
......
......@@ -8,7 +8,12 @@
[secondaryButtonAction]="openSearchBercoForm.bind(this)"
>
</app-add-search>
<div class="main-body">
<button mat-raised-button color="primary" style="background-color: #43a047; color: #fff; margin: 12px 0;" (click)="saveDataInCSV('aceites')">
<mat-icon>download</mat-icon>
Exportar Excel
</button>
<div class="table-container">
<table mat-table [dataSource]="dataSource" matSort>
<!-- ID Column -->
......
......@@ -27,6 +27,7 @@ import { BercoSearchComponent } from '../berco-search/berco-search.component';
import { JwtDecoderService } from '../../../jwt-decoder.service';
import { AuthService } from '../../../auth.service';
import { jwtDecode } from 'jwt-decode';
import { CsvService } from '../../../csv.service';
interface BercoData {
id: number;
......@@ -88,17 +89,19 @@ export class BercosComponent implements OnInit {
@ViewChild(MatSort) sort!: MatSort;
user: any;
authTokenPresent = false;
constructor(
private _dialog: MatDialog,
private _bercoService: BercosService,
private _coreService: CoreService,
private jwtDecoderService: JwtDecoderService,
private authService: AuthService,
private cdr: ChangeDetectorRef
) {}
private cdr: ChangeDetectorRef,
private _csvService: CsvService
) { }
ngOnInit(): void { this.validationToken();
ngOnInit(): void {
this.validationToken();
}
ngAfterViewInit(): void {
......@@ -130,29 +133,29 @@ export class BercosComponent implements OnInit {
if (storedData) {
const bercos = JSON.parse(storedData); // Converte o JSON em um objeto
console.log('Dados recuperados do sessionStorage:', bercos);
// Atualiza os dados no MatTableDataSource
this.dataSource.data = bercos;
// Reassocia paginator e sort
if (this.paginator && this.sort) {
this.dataSource.paginator = this.paginator;
this.dataSource.sort = this.sort;
}
// Atualiza a visualização
this.cdr.detectChanges();
// Remove dados do sessionStorage após o uso
sessionStorage.removeItem('bercosSearch');
console.log('bercosSearch removido do sessionStorage.');
return; // Retorna aqui, pois já usamos os dados do sessionStorage
} else {
console.warn('Nenhum dado encontrado no sessionStorage.');
}
}
// Caso os dados não estejam no sessionStorage, chama a API
const pageIndex = event ? event.pageIndex + 1 : this.currentPage - 1;
......@@ -174,7 +177,7 @@ export class BercosComponent implements OnInit {
},
});
}
applyFilter(event: Event) {
const filterValue = (event.target as HTMLInputElement).value;
......@@ -209,13 +212,14 @@ export class BercosComponent implements OnInit {
}
openAddBercoForm() {
this._dialog.open(BercoAddComponent,{autoFocus: true, // Foca no primeiro campo interativo do formulário
this._dialog.open(BercoAddComponent, {
autoFocus: true, // Foca no primeiro campo interativo do formulário
disableClose: true, // Impede que o diálogo feche ao clicar fora
width: '500px', // Define a largura do diálogo});
});
this.cdr.detectChanges();
}
});
this.cdr.detectChanges();
}
openSearchBercoForm() {
this._dialog.open(BercoSearchComponent);
if (typeof sessionStorage !== 'undefined') {
......@@ -223,7 +227,7 @@ export class BercosComponent implements OnInit {
if (storedData) {
const dataSource = JSON.parse(storedData); // Converte o JSON em um objeto
console.log('Dados recuperados do sessionStorage:', dataSource);
this.dataSource= dataSource;
this.dataSource = dataSource;
this.cdr.detectChanges();
// Atualiza a tabela com os dados
} else {
......@@ -241,4 +245,75 @@ export class BercosComponent implements OnInit {
width: '800px', // Define a largura do diálogo
});
}
public saveDataInCSV(name: string): void {
this._bercoService.getAllAceitesSemPaginacao().subscribe({
next: (aceites: any[]) => {
console.log('✅ Aceites recebidos:', aceites); // LOGA TODOS OS ACEITES ORIGINAIS
const data = aceites.map(a => {
const userRaw = a.user || '';
let idUsuario = '';
let email = '';
console.log('🔍 Usuário bruto:', userRaw); // LOGA STRING DO USUÁRIO
// Extrai ID e email do campo "Usuário"
const match = userRaw.match(/User\(id=(\d+),\s*email=([^)]+)\)/);
if (match) {
idUsuario = match[1];
email = match[2];
} else if (userRaw.match(/User\(id=(\d+)\)/)) {
idUsuario = userRaw.match(/User\(id=(\d+)\)/)[1];
}
// Categoria
let categoriaLabel = 'Não informado';
switch (+a.categoria) {
case 1:
categoriaLabel = 'Granel Sólido';
break;
case 2:
categoriaLabel = 'Granel Líquido';
break;
case 3:
categoriaLabel = 'Carga Geral';
break;
}
const row = {
Nome: a.nome,
DWT: a.dwt || '',
'Boca Máx': a.boca_max || '',
'Calado Máx': a.calado_max || '',
'Categoria': categoriaLabel,
Profundidade: a.profundidade || '',
Largura: a.largura || '',
'LOA Máx': a.loa_max || '',
'Comprimento Estrutural': a.compri_estrutural || '',
'Comprimento Útil': a.compri_util || '',
};
return row;
});
console.log('📋 Dados finais para CSV:', data); // LOGA O ARRAY COMPLETO FINAL
const csvContent = this._csvService.saveDataInCSV(data);
console.log('📝 Conteúdo CSV:', csvContent); // LOGA O TEXTO CSV GERADO
const hiddenElement = document.createElement('a');
hiddenElement.href = 'data:text/csv;charset=utf-8,' + encodeURI(csvContent);
hiddenElement.target = '_blank';
hiddenElement.download = name + '.csv';
hiddenElement.click();
},
error: (err) => {
this._coreService.openSnackBar('Erro ao exportar CSV');
console.error('❌ Erro ao exportar CSV:', err);
}
});
}
}
<app-add-search
[title]="'Black List'"
[primaryButtonText]="'Adicionar Navio'"
[primaryButtonColor]="'primary'"
[primaryButtonAction]="openAddEmpForm.bind(this)"
>
<app-add-search [title]="'Black List'" [primaryButtonText]="'Adicionar Navio'" [primaryButtonColor]="'primary'"
[primaryButtonAction]="openAddEmpForm.bind(this)">
</app-add-search>
<!-- Botão Exportar Excel Verde -->
<div class="main-body">
<button mat-raised-button color="primary" style="background-color: #43a047; color: #fff; margin: 12px 0;"
(click)="saveDataInCSV('aceites')">
<mat-icon>download</mat-icon>
Exportar Excel
</button>
<div class="table-container" style="overflow-x: auto">
<table mat-table [dataSource]="dataSource" matSort>
<!-- ID Column -->
......@@ -42,29 +44,35 @@
<!-- Flag Column -->
<ng-container matColumnDef="flag">
<th mat-header-cell *matHeaderCellDef mat-sort-header>Flag</th>
<td mat-cell *matCellDef="let row">{{ row.flag || "Null" }}</td>
</ng-container>
<th mat-header-cell *matHeaderCellDef>Flag</th>
<td mat-cell *matCellDef="let row">
<ng-container *ngIf="countries[+row.flag]; else flagNotAvailable">
<img [src]="countries[+row.flag].flagUrl" width="20" height="15"
alt="{{ countries[+row.flag].name }} flag"
style="margin-right: 8px" />
<span>{{ countries[+row.flag]?.name }}</span>
</ng-container>
<ng-template #flagNotAvailable>Não informado</ng-template>
</td>
</ng-container>
<!-- Action Column -->
<ng-container matColumnDef="action">
<th mat-header-cell *matHeaderCellDef>Action</th>
<td mat-cell *matCellDef="let row" style="white-space: nowrap">
<button mat-icon-button (click)="openEditForm(row)" *ngIf="user.role === 'COMPANY'"
mat-icon-button
color="accent"
>
<button mat-icon-button (click)="openEditForm(row)" *ngIf="user.role === 'COMPANY'" mat-icon-button
color="accent">
<mat-icon style="color: coral">edit</mat-icon>
</button>
<button mat-icon-button color="primary" (click)="getAceiteById(row)">
<mat-icon>visibility</mat-icon>
</button>
<button mat-icon-button color="warn" (click)="deleteEmployee(row.id)" *ngIf="user.role === 'COMPANY'"
mat-icon-button
color="accent">
mat-icon-button color="accent">
<mat-icon>delete</mat-icon>
</button>
</td>
</ng-container>
......@@ -73,29 +81,17 @@
</table>
</div>
<!-- Add paginator reference #paginator="matPaginator" -->
<mat-paginator
#paginator
[length]="totalItems"
[pageSize]="pageSize"
(page)="loadData($event)"
>
</mat-paginator>
<!-- Add paginator reference #paginator="matPaginator" -->
<mat-paginator #paginator [length]="totalItems" [pageSize]="pageSize" (page)="loadData($event)">
</mat-paginator>
<!-- Buttons for navigating to the first and last page -->
<button
mat-button
(click)="paginator.firstPage()"
[disabled]="paginator.pageIndex === 0"
>
Primeira Página
</button>
<!-- Buttons for navigating to the first and last page -->
<button mat-button (click)="paginator.firstPage()" [disabled]="paginator.pageIndex === 0">
Primeira Página
</button>
<button
mat-button
(click)="paginator.lastPage()"
[disabled]="paginator.pageIndex === paginator.getNumberOfPages() - 1"
>
Última Página
</button>
<button mat-button (click)="paginator.lastPage()"
[disabled]="paginator.pageIndex === paginator.getNumberOfPages() - 1">
Última Página
</button>
</div>
import { ComponentFixture, TestBed } from '@angular/core/testing';
import { SearchBlackComponent } from './search-black.component';
describe('SearchBlackComponent', () => {
let component: SearchBlackComponent;
let fixture: ComponentFixture<SearchBlackComponent>;
beforeEach(async () => {
await TestBed.configureTestingModule({
imports: [SearchBlackComponent]
})
.compileComponents();
fixture = TestBed.createComponent(SearchBlackComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
});
import { Component } from '@angular/core';
@Component({
selector: 'app-search-black',
standalone: true,
imports: [],
templateUrl: './search-black.component.html',
styleUrl: './search-black.component.scss'
})
export class SearchBlackComponent {
}
......@@ -35,18 +35,24 @@
</div>
-->
<div class="column" style="flex: 1 1 30%; margin-bottom: 10px;">
<p><strong>Mfold Quilha:</strong> {{ navioInfo.mfold_quilha || 'Não informado' }}</p>
<p><strong>Mfold Quilha:</strong>
{{ navioInfo.mfold_quilha !== null && navioInfo.mfold_quilha !== undefined ? navioInfo.mfold_quilha : 'Não informado' }}
</p>
</div>
<div class="column" style="flex: 1 1 30%; margin-bottom: 10px;">
<p><strong>Categoria:</strong>
{{ navioInfo.categoria === 1 ? 'Granel Sólido' : (navioInfo.categoria === 2 ? 'Granel Líquido' : (navioInfo.categoria === 3 ? 'Carga Geral' : 'Não informado')) }}
{{
+navioInfo.categoria === 1 ? 'Granel Sólido' :
(+navioInfo.categoria === 2 ? 'Granel Líquido' :
(+navioInfo.categoria === 3 ? 'Carga Geral' : 'Não informado'))
}}
</p>
</div>
<div class="column" style="flex: 1 1 30%; margin-bottom: 10px;">
<p><strong>Flag:</strong>
<ng-container *ngIf="navioInfo.flag; else flagNotAvailable">
<img [src]="countries[navioInfo.flag].flagUrl" width="20" height="15" alt="{{ countries[navioInfo.flag].name }} flag" style="margin-right: 8px" />
<p>{{ countries[navioInfo.flag]?.name }}</p>
<ng-container *ngIf="countries[+navioInfo.flag]; else flagNotAvailable">
<img [src]="countries[+navioInfo.flag].flagUrl" width="20" height="15" alt="{{ countries[+navioInfo.flag].name }} flag" style="margin-right: 8px" />
<span>{{ countries[+navioInfo.flag]?.name }}</span>
</ng-container>
<ng-template #flagNotAvailable>Não informado</ng-template>
</p>
......
......@@ -46,7 +46,10 @@ export class VisualizacaoBlackListComponent implements OnInit {
// Adicione a lógica para salvar as informações de entrada
}
getFlagUrl(flagId: number): string | null {
const country = this.countries.find(c => c.id === flagId);
return country ? country.flagUrl : null;
}
countries = [
{ id: 1, name: 'Afeganistão', flagUrl: 'https://flagcdn.com/w20/af.png' },
{ id: 2, name: 'África do Sul', flagUrl: 'https://flagcdn.com/w20/za.png' },
......
......@@ -16,6 +16,7 @@ import { MatDialogModule } from '@angular/material/dialog';
import { NaviosComponent } from '../navios/navios.component';
import { ToastrService } from 'ngx-toastr';
import { NgxCurrencyDirective } from 'ngx-currency';
import { BercosService } from '../../../services/bercos/bercos.service';
@Component({
selector: 'app-navio-add',
......@@ -55,7 +56,8 @@ export class NavioAddComponent implements OnInit {
private _empService: NavioService,
private _dialogRef: MatDialogRef<NavioAddComponent>,
@Inject(MAT_DIALOG_DATA) public data: any,
private toastService: ToastrService
private toastService: ToastrService,
private _bercoService: BercosService, // Injetando o serviço de berços
) {
this.empForm = this._fb.group({
imo: ['', Validators.required],
......@@ -71,6 +73,24 @@ export class NavioAddComponent implements OnInit {
flag: ['', Validators.required],
obs: [''],
});
this.empForm.get('categoria')?.valueChanges.subscribe(catId => {
const cat = this.categoria.find((c: any) => c.id === catId);
if (cat && cat.nome === 'Granel Líquido') {
this.empForm.get('mfold_quilha')?.setValidators([Validators.required]);
this.empForm.get('ponte_mfold')?.setValidators([Validators.required]);
} else {
this.empForm.get('mfold_quilha')?.clearValidators();
this.empForm.get('ponte_mfold')?.clearValidators();
}
this.empForm.get('mfold_quilha')?.updateValueAndValidity();
this.empForm.get('ponte_mfold')?.updateValueAndValidity();
});
}
selectedCategoriaNome: string = '';
onCategoriaChange(catId: any) {
const cat = this.categoria.find((c: any) => c.id === catId);
this.selectedCategoriaNome = cat ? cat.nome : '';
}
countries = [
......@@ -422,8 +442,17 @@ export class NavioAddComponent implements OnInit {
event.preventDefault();
}
}
bercosDisponiveis: any[] = [];
ngOnInit(): void {
this._bercoService.getBercosListSemPaginacao().subscribe({
next: (res) => {
this.bercosDisponiveis = Array.isArray(res) ? res : [];
},
error: () => {
this.bercosDisponiveis = [];
}
});
if (this.data) {
this.empForm.patchValue(this.data.empForm);
}
......@@ -434,12 +463,12 @@ export class NavioAddComponent implements OnInit {
if (input.files && input.files.length > 0) {
const file = input.files[0];
const allowedTypes = ['application/pdf', 'application/msword', 'application/vnd.openxmlformats-officedocument.wordprocessingml.document'];
if (!allowedTypes.includes(file.type)) {
this.toastService.error('Apenas arquivos PDF ou Word são permitidos.');
return;
}
this.selectedFile = file;
this.fileName = file.name;
const reader = new FileReader();
......@@ -483,4 +512,4 @@ export class NavioAddComponent implements OnInit {
}
}
}
}
\ No newline at end of file
}
......@@ -64,7 +64,7 @@
getErrorMessage("pontal")
}}</mat-error>
</mat-form-field>
<mat-form-field class="field small" appearance="outline">
<mat-form-field class="field small" appearance="outline" *ngIf="selectedCategoriaNome === 'Granel Líquido'">
<mat-label>Ponte Mfold (m)</mat-label>
<input matInput type="text" [currencyMask]="{ prefix: ' ', thousands: '.', decimal: ',' }"
formControlName="ponte_mfold" />
......@@ -75,7 +75,7 @@
</div>
<div class="row">
<mat-form-field class="field large" appearance="outline">
<mat-form-field class="field large" appearance="outline" *ngIf="selectedCategoriaNome === 'Granel Líquido'">
<mat-label>Mfold Quilha (m)</mat-label>
<input matInput type="text" [currencyMask]="{ prefix: ' ', thousands: '.', decimal: ',' }"
formControlName="mfold_quilha" />
......@@ -86,14 +86,12 @@
<mat-form-field class="field medium" appearance="outline">
<mat-label>Categoria</mat-label>
<mat-select formControlName="categoria">
<mat-option [value]="1">Granel Sólido</mat-option>
<mat-option [value]="2">Granel Líquido</mat-option>
<mat-option [value]="3">Carga Geral</mat-option>
<mat-select formControlName="categoria" (selectionChange)="onCategoriaChange($event.value)">
<mat-option *ngFor="let cat of categoria" [value]="cat.id">
{{ cat.nome }}
</mat-option>
</mat-select>
<mat-error *ngIf="empForm.get('categoria')?.invalid">
{{ getErrorMessage("categoria") }}
</mat-error>
<mat-error *ngIf="empForm.get('categoria')?.invalid">{{ getErrorMessage('categoria') }}</mat-error>
</mat-form-field>
<mat-form-field class="field small" appearance="outline">
<mat-label>Flag</mat-label>
......@@ -169,4 +167,4 @@
</button>
<button mat-raised-button color="primary" type="submit">Salvar</button>
</div>
</form>
\ No newline at end of file
</form>
......@@ -40,12 +40,17 @@ import { NgxCurrencyDirective } from 'ngx-currency';
styleUrl: './navio-edit.component.scss',
})
export class NavioEditComponent implements OnInit {
empForm: FormGroup;
fileName = '';
imageUrl: string | ArrayBuffer | null = '';
selectedFile: File | null = null;
categoria: number[] = [1, 2];
categoria = [
{ id: 1, nome: 'Granel Sólido' },
{ id: 2, nome: 'Granel Líquido' },
{ id: 3, nome: 'Carga Geral' }
];
constructor(
private _fb: FormBuilder,
private _empService: NavioService,
......@@ -68,9 +73,22 @@ export class NavioEditComponent implements OnInit {
flag: [Number(this.data?.flag) || '', Validators.required],
obs: [this.data?.obs || ''],
});
this.empForm.get('categoria')?.valueChanges.subscribe(catId => {
const cat = this.categoria.find((c: any) => c.id === catId);
if (cat && cat.nome === 'Granel Líquido') {
this.empForm.get('mfold_quilha')?.setValidators([Validators.required]);
} else {
this.empForm.get('mfold_quilha')?.clearValidators();
}
this.empForm.get('mfold_quilha')?.updateValueAndValidity();
});
}
selectedCategoriaNome: string = '';
onCategoriaChange(catId: any) {
const cat = this.categoria.find((c: any) => c.id === catId);
this.selectedCategoriaNome = cat ? cat.nome : '';
}
countries = [
{ id: 1, name: 'Afeganistão', flagUrl: 'https://flagcdn.com/w20/af.png' },
{ id: 2, name: 'África do Sul', flagUrl: 'https://flagcdn.com/w20/za.png' },
......@@ -413,6 +431,7 @@ export class NavioEditComponent implements OnInit {
onCountrySelected(country: { name: string; flagUrl: string }) {
this.selectedCountry = country;
}
preventNegative(event: KeyboardEvent): void {
if (event.key === '-' || event.key === '+' || event.key === 'e') {
event.preventDefault();
......@@ -438,12 +457,12 @@ export class NavioEditComponent implements OnInit {
if (input.files && input.files.length > 0) {
const file = input.files[0];
const allowedTypes = ['application/pdf', 'application/msword', 'application/vnd.openxmlformats-officedocument.wordprocessingml.document'];
if (!allowedTypes.includes(file.type)) {
this.toastService.error('Apenas arquivos PDF ou Word são permitidos.');
return;
}
this.selectedFile = file;
this.fileName = file.name;
const reader = new FileReader();
......
......@@ -20,6 +20,17 @@
}}</mat-error>
</mat-form-field>
</div>
<div class="row">
<mat-form-field class="field medium" appearance="outline">
<mat-label>Data de Início</mat-label>
<input matInput type="date" formControlName="dataInicio" />
</mat-form-field>
<mat-form-field class="field medium" appearance="outline">
<mat-label>Data de Fim</mat-label>
<input matInput type="date" formControlName="dataFim" />
</mat-form-field>
</div>
<!--
<mat-form-field class="field medium" appearance="outline">
<mat-label>Categoria</mat-label>
......@@ -76,4 +87,4 @@
</button>
<button mat-raised-button color="primary" type="submit">Buscar</button>
</div>
</form>
\ No newline at end of file
</form>
......@@ -59,8 +59,8 @@ export class NavioSearchComponent implements OnInit {
) {
this.empForm = this._fb.group({
imo: [''],
categoria: [''],
flag: [''],
dataInicio: [''],
dataFim: [''],
});
}
countries = [
......@@ -421,12 +421,12 @@ export class NavioSearchComponent implements OnInit {
if (input.files && input.files.length > 0) {
const file = input.files[0];
const allowedTypes = ['application/pdf', 'application/msword', 'application/vnd.openxmlformats-officedocument.wordprocessingml.document'];
if (!allowedTypes.includes(file.type)) {
this.toastService.error('Apenas arquivos PDF ou Word são permitidos.');
return;
}
this.selectedFile = file;
this.fileName = file.name;
const reader = new FileReader();
......@@ -450,10 +450,10 @@ export class NavioSearchComponent implements OnInit {
if (this.empForm.valid) {
const searchCriteria = {
imo: this.empForm.value.imo,
categoria: this.empForm.value.categoria,
flag: this.empForm.value.flag,
dataInicio: this.empForm.value.dataInicio,
dataFim: this.empForm.value.dataFim,
};
console.log('Search Criteria:', searchCriteria);
this._empService.searchNavios(searchCriteria).subscribe({
next: (val: any) => {
this.toastService.success('Busca realizada com sucesso');
......
......@@ -10,6 +10,10 @@
</app-add-search>
<div class="main-body">
<button mat-raised-button color="primary" style="background-color: #43a047; color: #fff; margin: 12px 0;" (click)="saveDataInCSV('aceites')">
<mat-icon>download</mat-icon>
Exportar Excel
</button>
<div class="table-container" style="overflow-x: auto">
<table mat-table [dataSource]="dataSource" matSort>
<!-- ID Column -->
......@@ -118,4 +122,4 @@
>
Última Página
</button>
</div>
\ No newline at end of file
</div>
......@@ -30,6 +30,7 @@ import { AuthService } from '../../../auth.service';
import { jwtDecode } from 'jwt-decode';
import { BlackListAddComponent } from '../../crud-blackList/black-list-add/black-list-add.component';
import { ToastrService } from 'ngx-toastr';
import { CsvService } from '../../../csv.service';
interface NavioData {
id: number;
imo: number;
......@@ -101,9 +102,10 @@ export class NaviosComponent implements OnInit {
private _coreService: CoreService,
private cdr: ChangeDetectorRef,
private toastService: ToastrService,
private _csvService: CsvService,
) {}
) { }
ngOnInit(): void {
this.validationToken();
}
......@@ -134,36 +136,36 @@ export class NaviosComponent implements OnInit {
loadData(event?: PageEvent): void {
const pageIndex = event ? event.pageIndex + 1 : this.currentPage - 1;
// Verifica se há dados no sessionStorage
if (typeof sessionStorage !== 'undefined') {
const storedData = sessionStorage.getItem('naviosSearch');
if (storedData) {
const navios = JSON.parse(storedData); // Converte o JSON em um objeto
console.log('Dados recuperados do sessionStorage:', navios);
// Atualiza os dados na tabela
this.dataSource.data = navios;
// Atualiza o total de itens para paginação (opcional)
this.totalItems = navios.length;
// Configurações do paginator e sort
if (this.paginator && this.sort) {
this.dataSource.paginator = this.paginator;
this.dataSource.sort = this.sort;
}
// Remove dados do sessionStorage após o uso
sessionStorage.removeItem('naviosSearch');
console.log('naviosSearch removido do sessionStorage.');
return; // Retorna para evitar chamada à API
} else {
console.warn('Nenhum dado encontrado no sessionStorage.');
}
}
// Caso os dados não estejam no sessionStorage, chama a API
this._navioService.getEmployeeList(pageIndex, this.pageSize).subscribe({
next: (res: any) => {
......@@ -558,8 +560,8 @@ export class NaviosComponent implements OnInit {
this.cdr.detectChanges();
}
addBlackList(data:any){
addBlackList(data: any) {
this._dialog.open(BlackListAddComponent, {
data,
autoFocus: true, // Foca no primeiro campo interativo do formulário
......@@ -570,7 +572,7 @@ export class NaviosComponent implements OnInit {
}
openAddEmpForm() {
this._dialog.open(NavioAddComponent,{
this._dialog.open(NavioAddComponent, {
autoFocus: true, // Foca no primeiro campo interativo do formulário
disableClose: true, // Impede que o diálogo feche ao clicar fora
width: '600px', // Define a largura do diálogo
......@@ -583,23 +585,23 @@ export class NaviosComponent implements OnInit {
this._dialog.open(NavioSearchComponent, {
width: '600px', // Ajuste o tamanho conforme necessário
panelClass: 'custom-dialog-container'
}); if (typeof sessionStorage !== 'undefined') {
const storedData = sessionStorage.getItem('naviosSearch');
if (storedData) {
const dataSource = JSON.parse(storedData); // Converte o JSON em um objeto
console.log('Dados recuperados do sessionStorage:', dataSource);
this.dataSource.data = dataSource;
this.cdr.detectChanges();
// Atualiza a tabela com os dados
}); if (typeof sessionStorage !== 'undefined') {
const storedData = sessionStorage.getItem('naviosSearch');
if (storedData) {
const dataSource = JSON.parse(storedData); // Converte o JSON em um objeto
console.log('Dados recuperados do sessionStorage:', dataSource);
this.dataSource.data = dataSource;
this.cdr.detectChanges();
// Atualiza a tabela com os dados
} else {
console.warn('Nenhum dado encontrado no sessionStorage.');
}
} else {
console.warn('Nenhum dado encontrado no sessionStorage.');
console.error('sessionStorage não está disponível.');
}
} else {
console.error('sessionStorage não está disponível.');
}
}
getAceiteById(data: any) {
this._dialog.open(VisualizacaoNavioComponent, {
......@@ -608,4 +610,73 @@ export class NaviosComponent implements OnInit {
width: '600px', // Define a largura do diálogo
});
}
public saveDataInCSV(name: string): void {
this._navioService.getAllAceitesSemPaginacao().subscribe({
next: (aceites: any[]) => {
const data = aceites.map(a => {
const userRaw = a.user || '';
let idUsuario = '';
let email = '';
// Extrai ID e email do campo "Usuário"
const match = userRaw.match(/User\(id=(\d+),\s*email=([^)]+)\)/);
if (match) {
idUsuario = match[1];
email = match[2];
} else if (userRaw.match(/User\(id=(\d+)\)/)) {
idUsuario = userRaw.match(/User\(id=(\d+)\)/)[1];
}
// Categoria
let categoriaLabel = 'Não informado';
switch (+a.categoria) {
case 1:
categoriaLabel = 'Granel Sólido';
break;
case 2:
categoriaLabel = 'Granel Líquido';
break;
case 3:
categoriaLabel = 'Carga Geral';
break;
}
// Flag
let flagLabel = '';
if (a.flag && !isNaN(+a.flag)) {
const flagObj = this.countries.find(c => c.id === +a.flag);
flagLabel = flagObj ? flagObj.name : a.flag;
} else {
flagLabel = a.flag || 'Não informado';
}
return {
Nome: a.nome,
IMO: a.imo,
'ID Usuário': idUsuario,
Email: email,
Categoria: categoriaLabel,
Flag: flagLabel,
Observações: a.obs,
Pontal: a.pontal,
'Moldagem Quilha': a.mfold_quilha,
};
});
const csvContent = this._csvService.saveDataInCSV(data);
const hiddenElement = document.createElement('a');
hiddenElement.href = 'data:text/csv;charset=utf-8,' + encodeURI(csvContent);
hiddenElement.target = '_blank';
hiddenElement.download = name + '.csv';
hiddenElement.click();
},
error: (err) => {
this._coreService.openSnackBar('Erro ao exportar CSV');
console.error(err);
}
});
}
}
......@@ -36,12 +36,16 @@
<p><strong>Mfold Quilha:</strong> {{ data.mfold_quilha || 'Não informado' }}</p>
</div>
<div class="column" style="flex: 1 1 30%; margin-bottom: 10px;">
<p><strong>Categoria:</strong>
{{ data.categoria === 1 ? 'Granel Sólido' : (data.categoria === 2 ? 'Granel Líquido' : (data.categoria === 3 ? 'Carga Geral' : 'Não informado')) }}
<p><strong>Categoria:</strong>
{{
+data.categoria === 1 ? 'Granel Sólido' :
(+data.categoria === 2 ? 'Granel Líquido' :
(+data.categoria === 3 ? 'Carga Geral' : 'Não informado'))
}}
</p>
</div>
<div class="column" style="flex: 1 1 30%; margin-bottom: 10px;">
<p><strong>Flag:</strong>
<p><strong>Flag:</strong>
<ng-container *ngIf="data.flag; else flagNotAvailable">
<img [src]="countries[data.flag].flagUrl" width="20" height="15" alt="{{ countries[data.flag].name }} flag" style="margin-right: 8px" />
<p>{{ countries[data.flag]?.name }}</p>
......
<div class="grid-container">
<h1 class="mat-h1">Dashboard</h1>
<!-- Botão para Baixar Dados em CSV -->
<button mat-raised-button color="primary" style="background-color: #43a047; color: #fff; margin-bottom: 16px;"
(click)="saveDataInCSV('dashboard-dados')">
<mat-icon>download</mat-icon>
Baixar Dados CSV
</button>
<!-- Grid para os Gráficos -->
<mat-grid-list [cols]="isSmallScreen ? 1 : 3" gutterSize="16px">
<mat-grid-tile>
......
......@@ -9,7 +9,7 @@ import { ChartModule } from 'primeng/chart';
import { HttpClientModule } from '@angular/common/http'; // Import necessário para requisições HTTP
import { NavioSearchComponent } from '../crud-navios/navio-search/navio-search.component';
import { NavioService } from '../../services/navio.service';
import { CsvService } from '../../csv.service';
@Component({
selector: 'app-dashboard',
templateUrl: './dashboard.component.html',
......@@ -43,7 +43,9 @@ export class DashboardComponent implements OnInit {
onResize() {
this.isSmallScreen = window.innerWidth < 768;
}
constructor(private navioService: NavioService) {}
constructor(private navioService: NavioService,
private _csvService: CsvService // Injetando o serviço CSV
) {}
ngOnInit(): void {
// Consumindo o serviço para obter os dados do dashboard
......@@ -82,7 +84,7 @@ export class DashboardComponent implements OnInit {
{
data: [response.statusCounts.Y, response.statusCounts.NE, response.totalBlackLists],
backgroundColor: ['#36A2EB', '#FF6384', '#FFCE56']
}
}
]
};
......@@ -93,4 +95,27 @@ export class DashboardComponent implements OnInit {
};
});
}
public saveDataInCSV(name: string): void {
// organize o data com base na response e deixe nesse formato {"titulo": "valor"}
const data: Array<any> = [
{ "Indicador": "Total de Navios", "Valor": this.totalNavios },
{ "Indicador": "Total de Usuários", "Valor": this.totalUsuarios },
{ "Indicador": "Total de Berços", "Valor": this.totalBercos },
{ "Indicador": "Total de Aceites", "Valor": this.totalAceites },
{ "Indicador": "Total Aceitos", "Valor": this.dataDoughnut?.datasets[0]?.data[0] ?? 0 },
{ "Indicador": "Total Não Aceitos", "Valor": this.dataDoughnut?.datasets[0]?.data[1] ?? 0 },
{ "Indicador": "Total Não Especificados", "Valor": this.dataBar?.datasets[0]?.data[2] ?? 0 },
{ "Indicador": "Navios na Black-list", "Valor": this.dataDoughnut?.datasets[0]?.data[2] ?? 0 }
];
console.log(data);
let csvContent = this._csvService.saveDataInCSV(data);
var hiddenElement = document.createElement('a');
hiddenElement.href = 'data:text/csv;charset=utf-8,' + encodeURI(csvContent);
hiddenElement.target = '_blank';
hiddenElement.download = name + '.csv';
hiddenElement.click();
}
}
......@@ -26,9 +26,9 @@
<td mat-cell *matCellDef="let row">
{{
row.role === 'COMPANY'
? 'ADMIN'
? 'Agente do Porto'
: row.role === 'CANDIDATE'
? 'USUÁRIO'
? 'Agente do Navio'
: 'Desconhecido'
}}
</td>
......@@ -81,4 +81,4 @@
>
Última Página
</button>
</div>
\ No newline at end of file
</div>
......@@ -86,4 +86,9 @@ export class BlackListService {
return throwError(error);
}));
}
getAllBlackListSemPaginacao(): Observable<any[]> {
return this._http.get<any>(`${this.apiUrl}/sem-paginacao`).pipe(
map((res: any) => res._embedded?.blackListResponseList || [])
);
}
}
......@@ -19,7 +19,14 @@ export class AceiteService {
constructor(private _http: HttpClient) { }
getAllAceitesSemPaginacao(): Observable<any[]> {
return this._http.get<any>(`${this.apiUrl}/sem-paginacao`).pipe(
map((res: any) => {
// Ajuste conforme a estrutura do seu backend
return res._embedded?.acceptResponseList || [];
})
);
}
addAceite(data: any, foto: File | null): Observable<any> {
console.log(JSON.stringify(JSON.stringify(data)));
console.log(foto);
......@@ -83,13 +90,12 @@ export class AceiteService {
if (data.imo) {
params = params.set('imo', data.imo);
}
if (data.status) {
params = params.set('status', data.status);
}
if (data.nome) {
params = params.set('nome', data.nome);
}
if (data.dataInicio) {
params = params.set('dataInicio', data.dataInicio);
}
if (data.dataFim) {
params = params.set('dataFim', data.dataFim);
}
// Faz a requisição GET com os parâmetros
return this._http.get<any>(`${this.apiUrl}/custom`, { params }).pipe(
tap((res: any) => {
......
......@@ -79,7 +79,7 @@ export class BercosService {
}
getBercosListSemPaginacao(): Observable<any> {
return this._http.get<any>(this.apiUrl).pipe(
return this._http.get<any>(`${this.apiUrl}/sem-paginacao`).pipe(
tap((res: any) => {
console.log('Lista completa de berços:', res);
}),
......@@ -112,4 +112,12 @@ export class BercosService {
})
);
}
getAllAceitesSemPaginacao(): Observable<any[]> {
return this._http.get<any>(`${this.apiUrl}/sem-paginacao`).pipe(
map((res: any) => {
// Ajuste conforme a estrutura do seu backend
return res._embedded?.bercoResponseList || [];
})
);
}
}
......@@ -5,7 +5,7 @@ import {
HttpParams,
} from '@angular/common/http';
import { Observable, throwError } from 'rxjs';
import { catchError, tap } from 'rxjs/operators';
import { catchError, map, tap } from 'rxjs/operators';
import { environment } from '../../environments/.env'; // Importa o environment
@Injectable({
......@@ -15,7 +15,7 @@ export class NavioService {
users: any[] = [];
private apiUrl = environment.API_URL + '/api/vessels';
private apiUrlDash = environment.API_URL + '/statistics/all';
constructor(private _http: HttpClient) {}
constructor(private _http: HttpClient) { }
addEmployee1(data: any, foto: File): Observable<any> {
console.log(JSON.stringify(JSON.stringify(data)));
......@@ -72,11 +72,11 @@ export class NavioService {
if (data.imo) {
params = params.set('imo', data.imo);
}
if (data.status) {
params = params.set('status', data.status);
if (data.dataInicio) {
params = params.set('dataInicio', data.dataInicio);
}
if (data.nome) {
params = params.set('nome', data.nome);
if (data.dataFim) {
params = params.set('dataFim', data.dataFim);
}
// Faz a requisição GET com os parâmetros
......@@ -172,6 +172,13 @@ export class NavioService {
})
);
}
getAllAceitesSemPaginacao(): Observable<any[]> {
return this._http.get<any>(`${this.apiUrl}/sem-paginacao`).pipe(
map((res: any) => res._embedded?.vesselResponseList || [])
);
}
}
import { HttpClient, HttpParams } from '@angular/common/http';
import { Injectable } from '@angular/core';
import { catchError, Observable, tap, throwError } from 'rxjs';
import { catchError, map, Observable, tap, throwError } from 'rxjs';
import { environment } from '../../../environments/.env'; // Importa o environment
@Injectable({
......@@ -42,4 +42,12 @@ export class UserService {
})
);
}
getAllAceitesSemPaginacao(): Observable<any[]> {
return this._http.get<any>(`${this.apiUrl}/sem-paginacao`).pipe(
map((res: any) => {
// Ajuste conforme a estrutura do seu backend
return res._embedded?.acceptResponseList || [];
})
);
}
}
......@@ -12,4 +12,4 @@
</button>
}
</div>
</div>
\ No newline at end of file
</div>
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment