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 @@ ...@@ -46,75 +46,61 @@
<div class="row"> <div class="row">
<mat-form-field class="field medium" appearance="outline"> <mat-form-field class="field medium" appearance="outline">
<mat-label>Calado de Entrada</mat-label> <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-error *ngIf="empForm.get('calado_entrada')?.invalid">{{ getErrorMessage('calado_entrada') }}</mat-error>
</mat-form-field> </mat-form-field>
<mat-form-field class="field medium" appearance="outline"> <mat-form-field class="field medium" appearance="outline">
<mat-label>Calado de Saída</mat-label> <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-error *ngIf="empForm.get('calado_saida')?.invalid">{{ getErrorMessage('calado_saida') }}</mat-error>
</mat-form-field> </mat-form-field>
</div> </div>
<div class="row"> <div class="row">
<mat-form-field class="field large" appearance="outline"> <mat-form-field class="field large" appearance="outline">
<mat-label>DWT (Atual)</mat-label> <mat-label>DWT (Atual)</mat-label>
<input matInput type="text" [currencyMask]="{ prefix: '',thousands: '.', decimal: ',' }" <input matInput type="text" [currencyMask]="{ prefix: '',thousands: '.', decimal: ',' }" formControlName="dwt">
formControlName="dwt">
<mat-error *ngIf="empForm.get('dwt')?.invalid">{{ getErrorMessage('dwt') }}</mat-error> <mat-error *ngIf="empForm.get('dwt')?.invalid">{{ getErrorMessage('dwt') }}</mat-error>
</mat-form-field> </mat-form-field>
<mat-form-field class="field medium" appearance="outline"> <mat-form-field class="field medium" appearance="outline">
<mat-label>Pontal (m)</mat-label> <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-error *ngIf="empForm.get('pontal')?.invalid">{{ getErrorMessage('pontal') }}</mat-error>
</mat-form-field> </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>
<div class="row"> <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-form-field class="field medium" appearance="outline">
<mat-label>Categoria</mat-label> <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"> <mat-option *ngFor="let cat of categoria" [value]="cat.id">
{{ cat.nome }} {{ cat.nome }}
</mat-option> </mat-option>
</mat-select> </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>
<!-- 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-form-field class="field small" appearance="outline">
<mat-label>Flag</mat-label> <mat-label>Flag</mat-label>
<mat-select formControlName="flag"> <mat-select formControlName="flag">
<mat-select-trigger> <mat-select-trigger>
<img <img *ngIf="selectedCountry" [src]="selectedCountry.flagUrl" width="20" height="15"
*ngIf="selectedCountry" alt="{{ selectedCountry.name }} flag" style="margin-right: 8px" />
[src]="selectedCountry.flagUrl"
width="20"
height="15"
alt="{{ selectedCountry.name }} flag"
style="margin-right: 8px"
/>
{{ selectedCountry?.name }} {{ selectedCountry?.name }}
</mat-select-trigger> </mat-select-trigger>
<mat-option <mat-option *ngFor="let country of countries" [value]="country.id"
*ngFor="let country of countries" (onSelectionChange)="onCountrySelected(country)">
[value]="country.id" <img [src]="country.flagUrl" width="20" height="15" alt="{{ country.name }} flag"
(onSelectionChange)="onCountrySelected(country)" style="margin-right: 8px" />
>
<img
[src]="country.flagUrl"
width="20"
height="15"
alt="{{ country.name }} flag"
style="margin-right: 8px"
/>
{{ country.name }} {{ country.name }}
</mat-option> </mat-option>
</mat-select> </mat-select>
...@@ -123,24 +109,26 @@ ...@@ -123,24 +109,26 @@
</mat-error> </mat-error>
</mat-form-field> </mat-form-field>
</div> </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"> <div class="row">
<mat-form-field class="field full-width" appearance="outline"> <mat-form-field class="field full-width" appearance="outline">
<mat-label>Observação</mat-label> <mat-label>Observação</mat-label>
<input matInput type="text" formControlName="obs"> <input matInput type="text" formControlName="obs">
<mat-error *ngIf="empForm.get('obs')?.invalid">{{ getErrorMessage('obs') }}</mat-error> <mat-error *ngIf="empForm.get('obs')?.invalid">{{ getErrorMessage('obs') }}</mat-error>
</mat-form-field> </mat-form-field>
</div> </div>
<div class="row file-upload-container"> <div class="row file-upload-container">
<mat-label style="color:cadetblue">Adicione o documente Q88 da embarcação</mat-label> <mat-label style="color:cadetblue">Adicione o documente Q88 da embarcação</mat-label>
<input <input type="file" class="file-input" (change)="onFileSelected($event)" #fileUpload style="display: none" />
type="file"
class="file-input"
(change)="onFileSelected($event)"
#fileUpload
style="display: none"
/>
<div class="file-upload-content"> <div class="file-upload-content">
<div class="file-info"> <div class="file-info">
...@@ -148,12 +136,7 @@ ...@@ -148,12 +136,7 @@
</div> </div>
<div class="upload-button-wrapper"> <div class="upload-button-wrapper">
<button <button mat-mini-fab color="primary" class="upload-btn" (click)="fileUpload.click()">
mat-mini-fab
color="primary"
class="upload-btn"
(click)="fileUpload.click()"
>
<mat-icon>attach_file</mat-icon> <mat-icon>attach_file</mat-icon>
</button> </button>
</div> </div>
...@@ -161,17 +144,25 @@ ...@@ -161,17 +144,25 @@
<div class="uploaded-image-container" *ngIf="imageUrl"> <div class="uploaded-image-container" *ngIf="imageUrl">
<img [src]="imageUrl" alt="Uploaded Image" class="uploaded-image" /> <img [src]="imageUrl" alt="Uploaded Image" class="uploaded-image" />
<button <button mat-mini-fab color="warn" class="remove-image-btn" (click)="removeImage()">
mat-mini-fab
color="warn"
class="remove-image-btn"
(click)="removeImage()"
>
<mat-icon>close</mat-icon> <mat-icon>close</mat-icon>
</button> </button>
</div> </div>
</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>
<div mat-dialog-actions class="action"> <div mat-dialog-actions class="action">
<button mat-raised-button type="button" color="warn" [mat-dialog-close]="false">Cancelar</button> <button mat-raised-button type="button" color="warn" [mat-dialog-close]="false">Cancelar</button>
<button mat-raised-button color="primary" type="submit">Salvar</button> <button mat-raised-button color="primary" type="submit">Salvar</button>
......
...@@ -20,6 +20,7 @@ import { Observable, catchError, distinctUntilChanged, map, of, switchMap, tap } ...@@ -20,6 +20,7 @@ import { Observable, catchError, distinctUntilChanged, map, of, switchMap, tap }
import { HttpClient } from '@angular/common/http'; import { HttpClient } from '@angular/common/http';
import { ToastrService } from 'ngx-toastr'; import { ToastrService } from 'ngx-toastr';
import { NgxCurrencyDirective } from 'ngx-currency'; import { NgxCurrencyDirective } from 'ngx-currency';
import { BercosService } from '../../../services/bercos/bercos.service';
@Component({ @Component({
selector: 'app-aceite-add', selector: 'app-aceite-add',
...@@ -36,7 +37,7 @@ import { NgxCurrencyDirective } from 'ngx-currency'; ...@@ -36,7 +37,7 @@ import { NgxCurrencyDirective } from 'ngx-currency';
MatSelectModule, MatSelectModule,
MatRadioModule, MatRadioModule,
MatIconModule, MatIconModule,
MatSnackBarModule,NgxCurrencyDirective MatSnackBarModule, NgxCurrencyDirective
], ],
templateUrl: './aceite-add.component.html', templateUrl: './aceite-add.component.html',
styleUrls: ['./aceite-add.component.scss'] styleUrls: ['./aceite-add.component.scss']
...@@ -51,7 +52,7 @@ export class AceiteAddComponent implements OnInit { ...@@ -51,7 +52,7 @@ export class AceiteAddComponent implements OnInit {
{ id: '2', nome: 'Granel Líquido' }, { id: '2', nome: 'Granel Líquido' },
{ id: '3', nome: 'Carga Geral' } { id: '3', nome: 'Carga Geral' }
]; ];
constructor( constructor(
private _fb: FormBuilder, private _fb: FormBuilder,
private _empService: AceiteService, private _empService: AceiteService,
...@@ -61,11 +62,12 @@ export class AceiteAddComponent implements OnInit { ...@@ -61,11 +62,12 @@ export class AceiteAddComponent implements OnInit {
private _dialog: MatDialog, private _dialog: MatDialog,
private http: HttpClient, private http: HttpClient,
private toastService: ToastrService, private toastService: ToastrService,
private _bercoService: BercosService // Injetando o serviço de berços
) { ) {
this.empForm = this._fb.group({ this.empForm = this._fb.group({
imo: [data ? data.imo : '', Validators.required, this.ImoValidator()], imo: [data ? data.imo : '', Validators.required, this.ImoValidator()],
mmsi: [data ? data.mmsi : '', ], mmsi: [data ? data.mmsi : '',],
nome: [data ? data.nome : '', Validators.required], nome: [data ? data.nome : '', Validators.required],
loa: [data ? data.loa : '', Validators.required], loa: [data ? data.loa : '', Validators.required],
boca: [data ? data.boca : '', Validators.required], boca: [data ? data.boca : '', Validators.required],
...@@ -76,8 +78,21 @@ export class AceiteAddComponent implements OnInit { ...@@ -76,8 +78,21 @@ export class AceiteAddComponent implements OnInit {
categoria: [data ? data.categoria : '', Validators.required], categoria: [data ? data.categoria : '', Validators.required],
flag: [data ? data.flag : '', Validators.required], flag: [data ? data.flag : '', Validators.required],
obs: [data ? data.obs : ''], obs: [data ? data.obs : ''],
calado_entrada:[ '', Validators.required], calado_entrada: ['', Validators.required],
calado_saida:['', 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 { ...@@ -86,6 +101,8 @@ export class AceiteAddComponent implements OnInit {
event.preventDefault(); event.preventDefault();
} }
} }
countries = [ countries = [
{ id: 1, name: 'Afeganistão', flagUrl: 'https://flagcdn.com/w20/af.png' }, { 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' }, { id: 2, name: 'África do Sul', flagUrl: 'https://flagcdn.com/w20/za.png' },
...@@ -427,11 +444,24 @@ export class AceiteAddComponent implements OnInit { ...@@ -427,11 +444,24 @@ export class AceiteAddComponent implements OnInit {
onCountrySelected(country: { id: number; name: string; flagUrl: string }) { onCountrySelected(country: { id: number; name: string; flagUrl: string }) {
this.selectedCountry = country; this.selectedCountry = country;
} }
bercosDisponiveis: any[] = [];
ngOnInit(): void { ngOnInit(): void {
if (this.data) { if (this.data) {
this.empForm.patchValue(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 this.empForm.get('imo')?.valueChanges
.pipe( .pipe(
distinctUntilChanged(), distinctUntilChanged(),
...@@ -457,12 +487,12 @@ export class AceiteAddComponent implements OnInit { ...@@ -457,12 +487,12 @@ export class AceiteAddComponent implements OnInit {
if (input.files && input.files.length > 0) { if (input.files && input.files.length > 0) {
const file = input.files[0]; const file = input.files[0];
const allowedTypes = ['application/pdf', 'application/msword', 'application/vnd.openxmlformats-officedocument.wordprocessingml.document']; const allowedTypes = ['application/pdf', 'application/msword', 'application/vnd.openxmlformats-officedocument.wordprocessingml.document'];
if (!allowedTypes.includes(file.type)) { if (!allowedTypes.includes(file.type)) {
this.toastService.error('Apenas arquivos PDF ou Word são permitidos.'); this.toastService.error('Apenas arquivos PDF ou Word são permitidos.');
return; return;
} }
this.selectedFile = file; this.selectedFile = file;
this.fileName = file.name; this.fileName = file.name;
const reader = new FileReader(); const reader = new FileReader();
...@@ -470,7 +500,8 @@ export class AceiteAddComponent implements OnInit { ...@@ -470,7 +500,8 @@ export class AceiteAddComponent implements OnInit {
this.imageUrl = (e.target?.result as string) || ''; this.imageUrl = (e.target?.result as string) || '';
}; };
reader.readAsDataURL(file); reader.readAsDataURL(file);
}} }
}
removeImage() { removeImage() {
this.imageUrl = null; this.imageUrl = null;
this.fileName = ''; this.fileName = '';
...@@ -492,7 +523,12 @@ export class AceiteAddComponent implements OnInit { ...@@ -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 { getErrorMessage(formControlName: string): string {
if (this.empForm.get(formControlName)?.hasError('required')) { if (this.empForm.get(formControlName)?.hasError('required')) {
return 'Campo obrigatório'; return 'Campo obrigatório';
...@@ -516,7 +552,7 @@ export class AceiteAddComponent implements OnInit { ...@@ -516,7 +552,7 @@ export class AceiteAddComponent implements OnInit {
if (this.empForm.valid) { if (this.empForm.valid) {
if (this.selectedFile) { if (this.selectedFile) {
this._empService.addAceite(this.empForm.value,this.selectedFile).subscribe({ this._empService.addAceite(this.empForm.value, this.selectedFile).subscribe({
next: () => { next: () => {
this._dialogRef.close(true); this._dialogRef.close(true);
this.toastService.success('Aceite cadastrado com sucesso!'); this.toastService.success('Aceite cadastrado com sucesso!');
...@@ -526,7 +562,7 @@ export class AceiteAddComponent implements OnInit { ...@@ -526,7 +562,7 @@ export class AceiteAddComponent implements OnInit {
}, },
}); });
} else { } else {
this._empService.addAceite(this.empForm.value,null).subscribe({ this._empService.addAceite(this.empForm.value, null).subscribe({
next: () => { next: () => {
this._dialogRef.close(true); this._dialogRef.close(true);
this.toastService.success('Aceite cadastrado com sucesso!'); this.toastService.success('Aceite cadastrado com sucesso!');
...@@ -534,7 +570,8 @@ export class AceiteAddComponent implements OnInit { ...@@ -534,7 +570,8 @@ export class AceiteAddComponent implements OnInit {
error: (err: any) => { error: (err: any) => {
console.error(err); console.error(err);
}, },
}); } });
}
} }
} }
} }
......
<div mat-dialog-title> <div mat-dialog-title>
<h1>{{ "Buscar Aceite" }}</h1> <h1>{{ "Buscar Aceite" }}</h1>
</div> </div>
<form [formGroup]="empForm" (ngSubmit)="onFormSubmit()"> <form [formGroup]="empForm" (ngSubmit)="onFormSubmit()">
<div mat-dialog-content class="content"> <div mat-dialog-content class="content">
<div class="row"> <div class="row">
<!-- IMO --> <!-- IMO -->
<mat-form-field class="field full-width" appearance="outline"> <mat-form-field class="field full-width" appearance="outline">
<mat-label>IMO</mat-label> <mat-label>IMO</mat-label>
<input <input matInput type="number" (keydown)="preventNegative($event)" min="0" formControlName="imo" />
matInput <mat-error *ngIf="empForm.get('imo')?.invalid">{{
type="number" getErrorMessage("imo")
(keydown)="preventNegative($event)"
min="0"
formControlName="imo"
/>
<mat-error *ngIf="empForm.get('imo')?.invalid">{{
getErrorMessage("imo")
}}</mat-error> }}</mat-error>
</mat-form-field> </mat-form-field>
</div> </div>
<!-- <!--
<div class="row"> <div class="row">
<mat-form-field class="field medium" appearance="outline"> <mat-form-field class="field medium" appearance="outline">
<mat-label>Categoria</mat-label> <mat-label>Categoria</mat-label>
...@@ -33,7 +27,7 @@ ...@@ -33,7 +27,7 @@
getErrorMessage("categoria") getErrorMessage("categoria")
}}</mat-error> }}</mat-error>
</mat-form-field> </mat-form-field>
<mat-form-field class="field small" appearance="outline"> <mat-form-field class="field small" appearance="outline">
<mat-label>Flag</mat-label> <mat-label>Flag</mat-label>
<mat-select formControlName="flag"> <mat-select formControlName="flag">
...@@ -71,17 +65,22 @@ ...@@ -71,17 +65,22 @@
</div> </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>
<div mat-dialog-actions class="action"> </div>
<button <div mat-dialog-actions class="action">
mat-raised-button <button mat-raised-button type="button" color="warn" [mat-dialog-close]="false">
type="button" Cancelar
color="warn" </button>
[mat-dialog-close]="false" <button mat-raised-button color="primary" type="submit">Buscar</button>
> </div>
Cancelar </form>
</button>
<button mat-raised-button color="primary" type="submit">Buscar</button>
</div>
</form>
\ No newline at end of file
...@@ -47,7 +47,7 @@ export class AceiteSearchComponent implements OnInit { ...@@ -47,7 +47,7 @@ export class AceiteSearchComponent implements OnInit {
empForm: FormGroup; empForm: FormGroup;
selectedFile: File | null = null; selectedFile: File | null = null;
categoria: number[] = [1, 2,3]; categoria: number[] = [1, 2, 3];
constructor( constructor(
private _fb: FormBuilder, private _fb: FormBuilder,
...@@ -60,8 +60,9 @@ export class AceiteSearchComponent implements OnInit { ...@@ -60,8 +60,9 @@ export class AceiteSearchComponent implements OnInit {
) { ) {
this.empForm = this._fb.group({ this.empForm = this._fb.group({
imo: [''], imo: [''],
categoria: [''], dataInicio: [''],
flag: [''], dataFim: [''],
// ...outros campos...
}); });
} }
...@@ -82,12 +83,12 @@ export class AceiteSearchComponent implements OnInit { ...@@ -82,12 +83,12 @@ export class AceiteSearchComponent implements OnInit {
if (input.files && input.files.length > 0) { if (input.files && input.files.length > 0) {
const file = input.files[0]; const file = input.files[0];
const allowedTypes = ['application/pdf', 'application/msword', 'application/vnd.openxmlformats-officedocument.wordprocessingml.document']; const allowedTypes = ['application/pdf', 'application/msword', 'application/vnd.openxmlformats-officedocument.wordprocessingml.document'];
if (!allowedTypes.includes(file.type)) { if (!allowedTypes.includes(file.type)) {
this.toastService.error('Apenas arquivos PDF ou Word são permitidos.'); this.toastService.error('Apenas arquivos PDF ou Word são permitidos.');
return; return;
} }
this.selectedFile = file; this.selectedFile = file;
this.fileName = file.name; this.fileName = file.name;
const reader = new FileReader(); const reader = new FileReader();
...@@ -95,7 +96,8 @@ export class AceiteSearchComponent implements OnInit { ...@@ -95,7 +96,8 @@ export class AceiteSearchComponent implements OnInit {
this.imageUrl = (e.target?.result as string) || ''; this.imageUrl = (e.target?.result as string) || '';
}; };
reader.readAsDataURL(file); reader.readAsDataURL(file);
}} }
}
getErrorMessage(formControlName: string): string { getErrorMessage(formControlName: string): string {
if (this.empForm.get(formControlName)?.hasError('required')) { if (this.empForm.get(formControlName)?.hasError('required')) {
...@@ -451,8 +453,8 @@ export class AceiteSearchComponent implements OnInit { ...@@ -451,8 +453,8 @@ export class AceiteSearchComponent implements OnInit {
if (this.empForm.valid) { if (this.empForm.valid) {
const searchCriteria = { const searchCriteria = {
imo: this.empForm.value.imo, imo: this.empForm.value.imo,
categoria: this.empForm.value.categoria, dataInicio: this.empForm.value.dataInicio,
flag: this.empForm.value.flag, dataFim: this.empForm.value.dataFim,
}; };
this._empService.searchAceite(searchCriteria).subscribe({ this._empService.searchAceite(searchCriteria).subscribe({
......
...@@ -3,7 +3,14 @@ ...@@ -3,7 +3,14 @@
[secondaryButtonColor]="'accent'" [secondaryButtonAction]="openSearchBercoForm.bind(this)"> [secondaryButtonColor]="'accent'" [secondaryButtonAction]="openSearchBercoForm.bind(this)">
</app-add-search> </app-add-search>
<!-- Botão Exportar Excel Verde -->
<div class="main-body"> <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"> <div class="table-container" style="overflow-x: auto">
<table mat-table [dataSource]="dataSource" matSort> <table mat-table [dataSource]="dataSource" matSort>
<!-- ID Column --> <!-- ID Column -->
...@@ -95,4 +102,4 @@ ...@@ -95,4 +102,4 @@
[disabled]="paginator.pageIndex === paginator.getNumberOfPages() - 1"> [disabled]="paginator.pageIndex === paginator.getNumberOfPages() - 1">
Última Página Última Página
</button> </button>
</div> </div>
\ No newline at end of file
...@@ -24,10 +24,11 @@ ...@@ -24,10 +24,11 @@
<span *ngSwitchCase="'Y'" [ngStyle]="{ color: 'green' }">Navio Aceito</span> <span *ngSwitchCase="'Y'" [ngStyle]="{ color: 'green' }">Navio Aceito</span>
<span *ngSwitchCase="'NE'" [ngStyle]="{ color: 'red' }">Aceite Negado</span> <span *ngSwitchCase="'NE'" [ngStyle]="{ color: 'red' }">Aceite Negado</span>
<span *ngSwitchCase="'N'" [ngStyle]="{ color: 'orange' }">Em processamento</span> <span *ngSwitchCase="'N'" [ngStyle]="{ color: 'orange' }">Em processamento</span>
</ng-container> </ng-container>
</p> </p>
</div> </div>
</mat-card-content> </mat-card-content>
</mat-card> </mat-card>
<ng-container *ngIf="data.path"> <ng-container *ngIf="data.path">
...@@ -61,23 +62,33 @@ ...@@ -61,23 +62,33 @@
Status: <span [ngStyle]="{ color: 'green' }">Navio Aceito</span> Status: <span [ngStyle]="{ color: 'green' }">Navio Aceito</span>
</p> </p>
@if(data.restricoes){ @if(data.restricoes){
<p class="field full-width" appearance="outline"> <p class="field full-width" appearance="outline">
{{data.restricoes}} {{data.restricoes}}
</p> </p>
}@else{ }@else{
<p class="field full-width" appearance="outline"> <p class="field full-width" appearance="outline">
O navio é compatível com os seguintes berços: O navio é compatível com os seguintes berços:
</p> </p>
<div *ngIf="data?.bercos?.length > 0"> <div *ngIf="data?.bercos?.length > 0">
<div *ngFor="let berco of data.bercos"> <div *ngFor="let berco of data.bercos">
{{ berco.nome }} {{ berco.nome }}
</div>
</div> </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>
<div *ngSwitchCase="'NE'"> <div *ngSwitchCase="'NE'">
<p class="field full-width" appearance="outline"> <p class="field full-width" appearance="outline">
Status: <span [ngStyle]="{ color: 'red' }">Aceite Negado</span> Status: <span [ngStyle]="{ color: 'red' }">Aceite Negado</span>
......
...@@ -8,7 +8,12 @@ ...@@ -8,7 +8,12 @@
[secondaryButtonAction]="openSearchBercoForm.bind(this)" [secondaryButtonAction]="openSearchBercoForm.bind(this)"
> >
</app-add-search> </app-add-search>
<div class="main-body"> <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"> <div class="table-container">
<table mat-table [dataSource]="dataSource" matSort> <table mat-table [dataSource]="dataSource" matSort>
<!-- ID Column --> <!-- ID Column -->
......
...@@ -27,6 +27,7 @@ import { BercoSearchComponent } from '../berco-search/berco-search.component'; ...@@ -27,6 +27,7 @@ import { BercoSearchComponent } from '../berco-search/berco-search.component';
import { JwtDecoderService } from '../../../jwt-decoder.service'; import { JwtDecoderService } from '../../../jwt-decoder.service';
import { AuthService } from '../../../auth.service'; import { AuthService } from '../../../auth.service';
import { jwtDecode } from 'jwt-decode'; import { jwtDecode } from 'jwt-decode';
import { CsvService } from '../../../csv.service';
interface BercoData { interface BercoData {
id: number; id: number;
...@@ -88,17 +89,19 @@ export class BercosComponent implements OnInit { ...@@ -88,17 +89,19 @@ export class BercosComponent implements OnInit {
@ViewChild(MatSort) sort!: MatSort; @ViewChild(MatSort) sort!: MatSort;
user: any; user: any;
authTokenPresent = false; authTokenPresent = false;
constructor( constructor(
private _dialog: MatDialog, private _dialog: MatDialog,
private _bercoService: BercosService, private _bercoService: BercosService,
private _coreService: CoreService, private _coreService: CoreService,
private jwtDecoderService: JwtDecoderService, private jwtDecoderService: JwtDecoderService,
private authService: AuthService, private authService: AuthService,
private cdr: ChangeDetectorRef private cdr: ChangeDetectorRef,
) {} private _csvService: CsvService
) { }
ngOnInit(): void { this.validationToken(); ngOnInit(): void {
this.validationToken();
} }
ngAfterViewInit(): void { ngAfterViewInit(): void {
...@@ -130,29 +133,29 @@ export class BercosComponent implements OnInit { ...@@ -130,29 +133,29 @@ export class BercosComponent implements OnInit {
if (storedData) { if (storedData) {
const bercos = JSON.parse(storedData); // Converte o JSON em um objeto const bercos = JSON.parse(storedData); // Converte o JSON em um objeto
console.log('Dados recuperados do sessionStorage:', bercos); console.log('Dados recuperados do sessionStorage:', bercos);
// Atualiza os dados no MatTableDataSource // Atualiza os dados no MatTableDataSource
this.dataSource.data = bercos; this.dataSource.data = bercos;
// Reassocia paginator e sort // Reassocia paginator e sort
if (this.paginator && this.sort) { if (this.paginator && this.sort) {
this.dataSource.paginator = this.paginator; this.dataSource.paginator = this.paginator;
this.dataSource.sort = this.sort; this.dataSource.sort = this.sort;
} }
// Atualiza a visualização // Atualiza a visualização
this.cdr.detectChanges(); this.cdr.detectChanges();
// Remove dados do sessionStorage após o uso // Remove dados do sessionStorage após o uso
sessionStorage.removeItem('bercosSearch'); sessionStorage.removeItem('bercosSearch');
console.log('bercosSearch removido do sessionStorage.'); console.log('bercosSearch removido do sessionStorage.');
return; // Retorna aqui, pois já usamos os dados do sessionStorage return; // Retorna aqui, pois já usamos os dados do sessionStorage
} else { } else {
console.warn('Nenhum dado encontrado no sessionStorage.'); console.warn('Nenhum dado encontrado no sessionStorage.');
} }
} }
// Caso os dados não estejam no sessionStorage, chama a API // Caso os dados não estejam no sessionStorage, chama a API
const pageIndex = event ? event.pageIndex + 1 : this.currentPage - 1; const pageIndex = event ? event.pageIndex + 1 : this.currentPage - 1;
...@@ -174,7 +177,7 @@ export class BercosComponent implements OnInit { ...@@ -174,7 +177,7 @@ export class BercosComponent implements OnInit {
}, },
}); });
} }
applyFilter(event: Event) { applyFilter(event: Event) {
const filterValue = (event.target as HTMLInputElement).value; const filterValue = (event.target as HTMLInputElement).value;
...@@ -209,13 +212,14 @@ export class BercosComponent implements OnInit { ...@@ -209,13 +212,14 @@ export class BercosComponent implements OnInit {
} }
openAddBercoForm() { 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 disableClose: true, // Impede que o diálogo feche ao clicar fora
width: '500px', // Define a largura do diálogo}); width: '500px', // Define a largura do diálogo});
}); });
this.cdr.detectChanges(); this.cdr.detectChanges();
} }
openSearchBercoForm() { openSearchBercoForm() {
this._dialog.open(BercoSearchComponent); this._dialog.open(BercoSearchComponent);
if (typeof sessionStorage !== 'undefined') { if (typeof sessionStorage !== 'undefined') {
...@@ -223,7 +227,7 @@ export class BercosComponent implements OnInit { ...@@ -223,7 +227,7 @@ export class BercosComponent implements OnInit {
if (storedData) { if (storedData) {
const dataSource = JSON.parse(storedData); // Converte o JSON em um objeto const dataSource = JSON.parse(storedData); // Converte o JSON em um objeto
console.log('Dados recuperados do sessionStorage:', dataSource); console.log('Dados recuperados do sessionStorage:', dataSource);
this.dataSource= dataSource; this.dataSource = dataSource;
this.cdr.detectChanges(); this.cdr.detectChanges();
// Atualiza a tabela com os dados // Atualiza a tabela com os dados
} else { } else {
...@@ -241,4 +245,75 @@ export class BercosComponent implements OnInit { ...@@ -241,4 +245,75 @@ export class BercosComponent implements OnInit {
width: '800px', // Define a largura do diálogo 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 <app-add-search [title]="'Black List'" [primaryButtonText]="'Adicionar Navio'" [primaryButtonColor]="'primary'"
[title]="'Black List'" [primaryButtonAction]="openAddEmpForm.bind(this)">
[primaryButtonText]="'Adicionar Navio'"
[primaryButtonColor]="'primary'"
[primaryButtonAction]="openAddEmpForm.bind(this)"
>
</app-add-search> </app-add-search>
<!-- Botão Exportar Excel Verde -->
<div class="main-body"> <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"> <div class="table-container" style="overflow-x: auto">
<table mat-table [dataSource]="dataSource" matSort> <table mat-table [dataSource]="dataSource" matSort>
<!-- ID Column --> <!-- ID Column -->
...@@ -42,29 +44,35 @@ ...@@ -42,29 +44,35 @@
<!-- Flag Column --> <!-- Flag Column -->
<ng-container matColumnDef="flag"> <ng-container matColumnDef="flag">
<th mat-header-cell *matHeaderCellDef mat-sort-header>Flag</th> <th mat-header-cell *matHeaderCellDef>Flag</th>
<td mat-cell *matCellDef="let row">{{ row.flag || "Null" }}</td> <td mat-cell *matCellDef="let row">
</ng-container> <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 --> <!-- Action Column -->
<ng-container matColumnDef="action"> <ng-container matColumnDef="action">
<th mat-header-cell *matHeaderCellDef>Action</th> <th mat-header-cell *matHeaderCellDef>Action</th>
<td mat-cell *matCellDef="let row" style="white-space: nowrap"> <td mat-cell *matCellDef="let row" style="white-space: nowrap">
<button mat-icon-button (click)="openEditForm(row)" *ngIf="user.role === 'COMPANY'" <button mat-icon-button (click)="openEditForm(row)" *ngIf="user.role === 'COMPANY'" mat-icon-button
mat-icon-button color="accent">
color="accent"
>
<mat-icon style="color: coral">edit</mat-icon> <mat-icon style="color: coral">edit</mat-icon>
</button> </button>
<button mat-icon-button color="primary" (click)="getAceiteById(row)"> <button mat-icon-button color="primary" (click)="getAceiteById(row)">
<mat-icon>visibility</mat-icon> <mat-icon>visibility</mat-icon>
</button> </button>
<button mat-icon-button color="warn" (click)="deleteEmployee(row.id)" *ngIf="user.role === 'COMPANY'" <button mat-icon-button color="warn" (click)="deleteEmployee(row.id)" *ngIf="user.role === 'COMPANY'"
mat-icon-button mat-icon-button color="accent">
color="accent">
<mat-icon>delete</mat-icon> <mat-icon>delete</mat-icon>
</button> </button>
</td> </td>
</ng-container> </ng-container>
...@@ -73,29 +81,17 @@ ...@@ -73,29 +81,17 @@
</table> </table>
</div> </div>
<!-- Add paginator reference #paginator="matPaginator" --> <!-- Add paginator reference #paginator="matPaginator" -->
<mat-paginator <mat-paginator #paginator [length]="totalItems" [pageSize]="pageSize" (page)="loadData($event)">
#paginator </mat-paginator>
[length]="totalItems"
[pageSize]="pageSize"
(page)="loadData($event)"
>
</mat-paginator>
<!-- Buttons for navigating to the first and last page --> <!-- Buttons for navigating to the first and last page -->
<button <button mat-button (click)="paginator.firstPage()" [disabled]="paginator.pageIndex === 0">
mat-button Primeira Página
(click)="paginator.firstPage()" </button>
[disabled]="paginator.pageIndex === 0"
>
Primeira Página
</button>
<button <button mat-button (click)="paginator.lastPage()"
mat-button [disabled]="paginator.pageIndex === paginator.getNumberOfPages() - 1">
(click)="paginator.lastPage()" Última Página
[disabled]="paginator.pageIndex === paginator.getNumberOfPages() - 1" </button>
>
Última Página
</button>
</div> </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 @@ ...@@ -35,18 +35,24 @@
</div> </div>
--> -->
<div class="column" style="flex: 1 1 30%; margin-bottom: 10px;"> <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>
<div class="column" style="flex: 1 1 30%; margin-bottom: 10px;"> <div class="column" style="flex: 1 1 30%; margin-bottom: 10px;">
<p><strong>Categoria:</strong> <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> </p>
</div> </div>
<div class="column" style="flex: 1 1 30%; margin-bottom: 10px;"> <div class="column" style="flex: 1 1 30%; margin-bottom: 10px;">
<p><strong>Flag:</strong> <p><strong>Flag:</strong>
<ng-container *ngIf="navioInfo.flag; else flagNotAvailable"> <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" /> <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> <span>{{ countries[+navioInfo.flag]?.name }}</span>
</ng-container> </ng-container>
<ng-template #flagNotAvailable>Não informado</ng-template> <ng-template #flagNotAvailable>Não informado</ng-template>
</p> </p>
......
...@@ -46,7 +46,10 @@ export class VisualizacaoBlackListComponent implements OnInit { ...@@ -46,7 +46,10 @@ export class VisualizacaoBlackListComponent implements OnInit {
// Adicione a lógica para salvar as informações de entrada // 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 = [ countries = [
{ id: 1, name: 'Afeganistão', flagUrl: 'https://flagcdn.com/w20/af.png' }, { 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' }, { id: 2, name: 'África do Sul', flagUrl: 'https://flagcdn.com/w20/za.png' },
......
...@@ -16,6 +16,7 @@ import { MatDialogModule } from '@angular/material/dialog'; ...@@ -16,6 +16,7 @@ import { MatDialogModule } from '@angular/material/dialog';
import { NaviosComponent } from '../navios/navios.component'; import { NaviosComponent } from '../navios/navios.component';
import { ToastrService } from 'ngx-toastr'; import { ToastrService } from 'ngx-toastr';
import { NgxCurrencyDirective } from 'ngx-currency'; import { NgxCurrencyDirective } from 'ngx-currency';
import { BercosService } from '../../../services/bercos/bercos.service';
@Component({ @Component({
selector: 'app-navio-add', selector: 'app-navio-add',
...@@ -55,7 +56,8 @@ export class NavioAddComponent implements OnInit { ...@@ -55,7 +56,8 @@ export class NavioAddComponent implements OnInit {
private _empService: NavioService, private _empService: NavioService,
private _dialogRef: MatDialogRef<NavioAddComponent>, private _dialogRef: MatDialogRef<NavioAddComponent>,
@Inject(MAT_DIALOG_DATA) public data: any, @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({ this.empForm = this._fb.group({
imo: ['', Validators.required], imo: ['', Validators.required],
...@@ -71,6 +73,24 @@ export class NavioAddComponent implements OnInit { ...@@ -71,6 +73,24 @@ export class NavioAddComponent implements OnInit {
flag: ['', Validators.required], flag: ['', Validators.required],
obs: [''], 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 = [ countries = [
...@@ -422,8 +442,17 @@ export class NavioAddComponent implements OnInit { ...@@ -422,8 +442,17 @@ export class NavioAddComponent implements OnInit {
event.preventDefault(); event.preventDefault();
} }
} }
bercosDisponiveis: any[] = [];
ngOnInit(): void { ngOnInit(): void {
this._bercoService.getBercosListSemPaginacao().subscribe({
next: (res) => {
this.bercosDisponiveis = Array.isArray(res) ? res : [];
},
error: () => {
this.bercosDisponiveis = [];
}
});
if (this.data) { if (this.data) {
this.empForm.patchValue(this.data.empForm); this.empForm.patchValue(this.data.empForm);
} }
...@@ -434,12 +463,12 @@ export class NavioAddComponent implements OnInit { ...@@ -434,12 +463,12 @@ export class NavioAddComponent implements OnInit {
if (input.files && input.files.length > 0) { if (input.files && input.files.length > 0) {
const file = input.files[0]; const file = input.files[0];
const allowedTypes = ['application/pdf', 'application/msword', 'application/vnd.openxmlformats-officedocument.wordprocessingml.document']; const allowedTypes = ['application/pdf', 'application/msword', 'application/vnd.openxmlformats-officedocument.wordprocessingml.document'];
if (!allowedTypes.includes(file.type)) { if (!allowedTypes.includes(file.type)) {
this.toastService.error('Apenas arquivos PDF ou Word são permitidos.'); this.toastService.error('Apenas arquivos PDF ou Word são permitidos.');
return; return;
} }
this.selectedFile = file; this.selectedFile = file;
this.fileName = file.name; this.fileName = file.name;
const reader = new FileReader(); const reader = new FileReader();
...@@ -483,4 +512,4 @@ export class NavioAddComponent implements OnInit { ...@@ -483,4 +512,4 @@ export class NavioAddComponent implements OnInit {
} }
} }
} }
} }
\ No newline at end of file
...@@ -64,7 +64,7 @@ ...@@ -64,7 +64,7 @@
getErrorMessage("pontal") getErrorMessage("pontal")
}}</mat-error> }}</mat-error>
</mat-form-field> </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> <mat-label>Ponte Mfold (m)</mat-label>
<input matInput type="text" [currencyMask]="{ prefix: ' ', thousands: '.', decimal: ',' }" <input matInput type="text" [currencyMask]="{ prefix: ' ', thousands: '.', decimal: ',' }"
formControlName="ponte_mfold" /> formControlName="ponte_mfold" />
...@@ -75,7 +75,7 @@ ...@@ -75,7 +75,7 @@
</div> </div>
<div class="row"> <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> <mat-label>Mfold Quilha (m)</mat-label>
<input matInput type="text" [currencyMask]="{ prefix: ' ', thousands: '.', decimal: ',' }" <input matInput type="text" [currencyMask]="{ prefix: ' ', thousands: '.', decimal: ',' }"
formControlName="mfold_quilha" /> formControlName="mfold_quilha" />
...@@ -86,14 +86,12 @@ ...@@ -86,14 +86,12 @@
<mat-form-field class="field medium" appearance="outline"> <mat-form-field class="field medium" appearance="outline">
<mat-label>Categoria</mat-label> <mat-label>Categoria</mat-label>
<mat-select formControlName="categoria"> <mat-select formControlName="categoria" (selectionChange)="onCategoriaChange($event.value)">
<mat-option [value]="1">Granel Sólido</mat-option> <mat-option *ngFor="let cat of categoria" [value]="cat.id">
<mat-option [value]="2">Granel Líquido</mat-option> {{ cat.nome }}
<mat-option [value]="3">Carga Geral</mat-option> </mat-option>
</mat-select> </mat-select>
<mat-error *ngIf="empForm.get('categoria')?.invalid"> <mat-error *ngIf="empForm.get('categoria')?.invalid">{{ getErrorMessage('categoria') }}</mat-error>
{{ getErrorMessage("categoria") }}
</mat-error>
</mat-form-field> </mat-form-field>
<mat-form-field class="field small" appearance="outline"> <mat-form-field class="field small" appearance="outline">
<mat-label>Flag</mat-label> <mat-label>Flag</mat-label>
...@@ -169,4 +167,4 @@ ...@@ -169,4 +167,4 @@
</button> </button>
<button mat-raised-button color="primary" type="submit">Salvar</button> <button mat-raised-button color="primary" type="submit">Salvar</button>
</div> </div>
</form> </form>
\ No newline at end of file
...@@ -40,12 +40,17 @@ import { NgxCurrencyDirective } from 'ngx-currency'; ...@@ -40,12 +40,17 @@ import { NgxCurrencyDirective } from 'ngx-currency';
styleUrl: './navio-edit.component.scss', styleUrl: './navio-edit.component.scss',
}) })
export class NavioEditComponent implements OnInit { export class NavioEditComponent implements OnInit {
empForm: FormGroup; empForm: FormGroup;
fileName = ''; fileName = '';
imageUrl: string | ArrayBuffer | null = ''; imageUrl: string | ArrayBuffer | null = '';
selectedFile: File | null = 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( constructor(
private _fb: FormBuilder, private _fb: FormBuilder,
private _empService: NavioService, private _empService: NavioService,
...@@ -68,9 +73,22 @@ export class NavioEditComponent implements OnInit { ...@@ -68,9 +73,22 @@ export class NavioEditComponent implements OnInit {
flag: [Number(this.data?.flag) || '', Validators.required], flag: [Number(this.data?.flag) || '', Validators.required],
obs: [this.data?.obs || ''], 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 = [ countries = [
{ id: 1, name: 'Afeganistão', flagUrl: 'https://flagcdn.com/w20/af.png' }, { 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' }, { id: 2, name: 'África do Sul', flagUrl: 'https://flagcdn.com/w20/za.png' },
...@@ -413,6 +431,7 @@ export class NavioEditComponent implements OnInit { ...@@ -413,6 +431,7 @@ export class NavioEditComponent implements OnInit {
onCountrySelected(country: { name: string; flagUrl: string }) { onCountrySelected(country: { name: string; flagUrl: string }) {
this.selectedCountry = country; this.selectedCountry = country;
} }
preventNegative(event: KeyboardEvent): void { preventNegative(event: KeyboardEvent): void {
if (event.key === '-' || event.key === '+' || event.key === 'e') { if (event.key === '-' || event.key === '+' || event.key === 'e') {
event.preventDefault(); event.preventDefault();
...@@ -438,12 +457,12 @@ export class NavioEditComponent implements OnInit { ...@@ -438,12 +457,12 @@ export class NavioEditComponent implements OnInit {
if (input.files && input.files.length > 0) { if (input.files && input.files.length > 0) {
const file = input.files[0]; const file = input.files[0];
const allowedTypes = ['application/pdf', 'application/msword', 'application/vnd.openxmlformats-officedocument.wordprocessingml.document']; const allowedTypes = ['application/pdf', 'application/msword', 'application/vnd.openxmlformats-officedocument.wordprocessingml.document'];
if (!allowedTypes.includes(file.type)) { if (!allowedTypes.includes(file.type)) {
this.toastService.error('Apenas arquivos PDF ou Word são permitidos.'); this.toastService.error('Apenas arquivos PDF ou Word são permitidos.');
return; return;
} }
this.selectedFile = file; this.selectedFile = file;
this.fileName = file.name; this.fileName = file.name;
const reader = new FileReader(); const reader = new FileReader();
......
...@@ -20,6 +20,17 @@ ...@@ -20,6 +20,17 @@
}}</mat-error> }}</mat-error>
</mat-form-field> </mat-form-field>
</div> </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-form-field class="field medium" appearance="outline">
<mat-label>Categoria</mat-label> <mat-label>Categoria</mat-label>
...@@ -76,4 +87,4 @@ ...@@ -76,4 +87,4 @@
</button> </button>
<button mat-raised-button color="primary" type="submit">Buscar</button> <button mat-raised-button color="primary" type="submit">Buscar</button>
</div> </div>
</form> </form>
\ No newline at end of file
...@@ -59,8 +59,8 @@ export class NavioSearchComponent implements OnInit { ...@@ -59,8 +59,8 @@ export class NavioSearchComponent implements OnInit {
) { ) {
this.empForm = this._fb.group({ this.empForm = this._fb.group({
imo: [''], imo: [''],
categoria: [''], dataInicio: [''],
flag: [''], dataFim: [''],
}); });
} }
countries = [ countries = [
...@@ -421,12 +421,12 @@ export class NavioSearchComponent implements OnInit { ...@@ -421,12 +421,12 @@ export class NavioSearchComponent implements OnInit {
if (input.files && input.files.length > 0) { if (input.files && input.files.length > 0) {
const file = input.files[0]; const file = input.files[0];
const allowedTypes = ['application/pdf', 'application/msword', 'application/vnd.openxmlformats-officedocument.wordprocessingml.document']; const allowedTypes = ['application/pdf', 'application/msword', 'application/vnd.openxmlformats-officedocument.wordprocessingml.document'];
if (!allowedTypes.includes(file.type)) { if (!allowedTypes.includes(file.type)) {
this.toastService.error('Apenas arquivos PDF ou Word são permitidos.'); this.toastService.error('Apenas arquivos PDF ou Word são permitidos.');
return; return;
} }
this.selectedFile = file; this.selectedFile = file;
this.fileName = file.name; this.fileName = file.name;
const reader = new FileReader(); const reader = new FileReader();
...@@ -450,10 +450,10 @@ export class NavioSearchComponent implements OnInit { ...@@ -450,10 +450,10 @@ export class NavioSearchComponent implements OnInit {
if (this.empForm.valid) { if (this.empForm.valid) {
const searchCriteria = { const searchCriteria = {
imo: this.empForm.value.imo, imo: this.empForm.value.imo,
categoria: this.empForm.value.categoria, dataInicio: this.empForm.value.dataInicio,
flag: this.empForm.value.flag, dataFim: this.empForm.value.dataFim,
}; };
console.log('Search Criteria:', searchCriteria);
this._empService.searchNavios(searchCriteria).subscribe({ this._empService.searchNavios(searchCriteria).subscribe({
next: (val: any) => { next: (val: any) => {
this.toastService.success('Busca realizada com sucesso'); this.toastService.success('Busca realizada com sucesso');
......
...@@ -10,6 +10,10 @@ ...@@ -10,6 +10,10 @@
</app-add-search> </app-add-search>
<div class="main-body"> <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"> <div class="table-container" style="overflow-x: auto">
<table mat-table [dataSource]="dataSource" matSort> <table mat-table [dataSource]="dataSource" matSort>
<!-- ID Column --> <!-- ID Column -->
...@@ -118,4 +122,4 @@ ...@@ -118,4 +122,4 @@
> >
Última Página Última Página
</button> </button>
</div> </div>
\ No newline at end of file
...@@ -30,6 +30,7 @@ import { AuthService } from '../../../auth.service'; ...@@ -30,6 +30,7 @@ import { AuthService } from '../../../auth.service';
import { jwtDecode } from 'jwt-decode'; import { jwtDecode } from 'jwt-decode';
import { BlackListAddComponent } from '../../crud-blackList/black-list-add/black-list-add.component'; import { BlackListAddComponent } from '../../crud-blackList/black-list-add/black-list-add.component';
import { ToastrService } from 'ngx-toastr'; import { ToastrService } from 'ngx-toastr';
import { CsvService } from '../../../csv.service';
interface NavioData { interface NavioData {
id: number; id: number;
imo: number; imo: number;
...@@ -101,9 +102,10 @@ export class NaviosComponent implements OnInit { ...@@ -101,9 +102,10 @@ export class NaviosComponent implements OnInit {
private _coreService: CoreService, private _coreService: CoreService,
private cdr: ChangeDetectorRef, private cdr: ChangeDetectorRef,
private toastService: ToastrService, private toastService: ToastrService,
private _csvService: CsvService,
) {} ) { }
ngOnInit(): void { ngOnInit(): void {
this.validationToken(); this.validationToken();
} }
...@@ -134,36 +136,36 @@ export class NaviosComponent implements OnInit { ...@@ -134,36 +136,36 @@ export class NaviosComponent implements OnInit {
loadData(event?: PageEvent): void { loadData(event?: PageEvent): void {
const pageIndex = event ? event.pageIndex + 1 : this.currentPage - 1; const pageIndex = event ? event.pageIndex + 1 : this.currentPage - 1;
// Verifica se há dados no sessionStorage // Verifica se há dados no sessionStorage
if (typeof sessionStorage !== 'undefined') { if (typeof sessionStorage !== 'undefined') {
const storedData = sessionStorage.getItem('naviosSearch'); const storedData = sessionStorage.getItem('naviosSearch');
if (storedData) { if (storedData) {
const navios = JSON.parse(storedData); // Converte o JSON em um objeto const navios = JSON.parse(storedData); // Converte o JSON em um objeto
console.log('Dados recuperados do sessionStorage:', navios); console.log('Dados recuperados do sessionStorage:', navios);
// Atualiza os dados na tabela // Atualiza os dados na tabela
this.dataSource.data = navios; this.dataSource.data = navios;
// Atualiza o total de itens para paginação (opcional) // Atualiza o total de itens para paginação (opcional)
this.totalItems = navios.length; this.totalItems = navios.length;
// Configurações do paginator e sort // Configurações do paginator e sort
if (this.paginator && this.sort) { if (this.paginator && this.sort) {
this.dataSource.paginator = this.paginator; this.dataSource.paginator = this.paginator;
this.dataSource.sort = this.sort; this.dataSource.sort = this.sort;
} }
// Remove dados do sessionStorage após o uso // Remove dados do sessionStorage após o uso
sessionStorage.removeItem('naviosSearch'); sessionStorage.removeItem('naviosSearch');
console.log('naviosSearch removido do sessionStorage.'); console.log('naviosSearch removido do sessionStorage.');
return; // Retorna para evitar chamada à API return; // Retorna para evitar chamada à API
} else { } else {
console.warn('Nenhum dado encontrado no sessionStorage.'); console.warn('Nenhum dado encontrado no sessionStorage.');
} }
} }
// Caso os dados não estejam no sessionStorage, chama a API // Caso os dados não estejam no sessionStorage, chama a API
this._navioService.getEmployeeList(pageIndex, this.pageSize).subscribe({ this._navioService.getEmployeeList(pageIndex, this.pageSize).subscribe({
next: (res: any) => { next: (res: any) => {
...@@ -558,8 +560,8 @@ export class NaviosComponent implements OnInit { ...@@ -558,8 +560,8 @@ export class NaviosComponent implements OnInit {
this.cdr.detectChanges(); this.cdr.detectChanges();
} }
addBlackList(data:any){ addBlackList(data: any) {
this._dialog.open(BlackListAddComponent, { this._dialog.open(BlackListAddComponent, {
data, data,
autoFocus: true, // Foca no primeiro campo interativo do formulário autoFocus: true, // Foca no primeiro campo interativo do formulário
...@@ -570,7 +572,7 @@ export class NaviosComponent implements OnInit { ...@@ -570,7 +572,7 @@ export class NaviosComponent implements OnInit {
} }
openAddEmpForm() { openAddEmpForm() {
this._dialog.open(NavioAddComponent,{ this._dialog.open(NavioAddComponent, {
autoFocus: true, // Foca no primeiro campo interativo do formulário autoFocus: true, // Foca no primeiro campo interativo do formulário
disableClose: true, // Impede que o diálogo feche ao clicar fora disableClose: true, // Impede que o diálogo feche ao clicar fora
width: '600px', // Define a largura do diálogo width: '600px', // Define a largura do diálogo
...@@ -583,23 +585,23 @@ export class NaviosComponent implements OnInit { ...@@ -583,23 +585,23 @@ export class NaviosComponent implements OnInit {
this._dialog.open(NavioSearchComponent, { this._dialog.open(NavioSearchComponent, {
width: '600px', // Ajuste o tamanho conforme necessário width: '600px', // Ajuste o tamanho conforme necessário
panelClass: 'custom-dialog-container' panelClass: 'custom-dialog-container'
}); if (typeof sessionStorage !== 'undefined') { }); if (typeof sessionStorage !== 'undefined') {
const storedData = sessionStorage.getItem('naviosSearch'); const storedData = sessionStorage.getItem('naviosSearch');
if (storedData) { if (storedData) {
const dataSource = JSON.parse(storedData); // Converte o JSON em um objeto const dataSource = JSON.parse(storedData); // Converte o JSON em um objeto
console.log('Dados recuperados do sessionStorage:', dataSource); console.log('Dados recuperados do sessionStorage:', dataSource);
this.dataSource.data = dataSource; this.dataSource.data = dataSource;
this.cdr.detectChanges(); this.cdr.detectChanges();
// Atualiza a tabela com os dados // Atualiza a tabela com os dados
} else {
console.warn('Nenhum dado encontrado no sessionStorage.');
}
} else { } 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) { getAceiteById(data: any) {
this._dialog.open(VisualizacaoNavioComponent, { this._dialog.open(VisualizacaoNavioComponent, {
...@@ -608,4 +610,73 @@ export class NaviosComponent implements OnInit { ...@@ -608,4 +610,73 @@ export class NaviosComponent implements OnInit {
width: '600px', // Define a largura do diálogo 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 @@ ...@@ -36,12 +36,16 @@
<p><strong>Mfold Quilha:</strong> {{ data.mfold_quilha || 'Não informado' }}</p> <p><strong>Mfold Quilha:</strong> {{ data.mfold_quilha || 'Não informado' }}</p>
</div> </div>
<div class="column" style="flex: 1 1 30%; margin-bottom: 10px;"> <div class="column" style="flex: 1 1 30%; margin-bottom: 10px;">
<p><strong>Categoria:</strong> <p><strong>Categoria:</strong>
{{ data.categoria === 1 ? 'Granel Sólido' : (data.categoria === 2 ? 'Granel Líquido' : (data.categoria === 3 ? 'Carga Geral' : 'Não informado')) }} {{
+data.categoria === 1 ? 'Granel Sólido' :
(+data.categoria === 2 ? 'Granel Líquido' :
(+data.categoria === 3 ? 'Carga Geral' : 'Não informado'))
}}
</p> </p>
</div> </div>
<div class="column" style="flex: 1 1 30%; margin-bottom: 10px;"> <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"> <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" /> <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> <p>{{ countries[data.flag]?.name }}</p>
......
<div class="grid-container"> <div class="grid-container">
<h1 class="mat-h1">Dashboard</h1> <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 --> <!-- Grid para os Gráficos -->
<mat-grid-list [cols]="isSmallScreen ? 1 : 3" gutterSize="16px"> <mat-grid-list [cols]="isSmallScreen ? 1 : 3" gutterSize="16px">
<mat-grid-tile> <mat-grid-tile>
......
...@@ -9,7 +9,7 @@ import { ChartModule } from 'primeng/chart'; ...@@ -9,7 +9,7 @@ import { ChartModule } from 'primeng/chart';
import { HttpClientModule } from '@angular/common/http'; // Import necessário para requisições HTTP import { HttpClientModule } from '@angular/common/http'; // Import necessário para requisições HTTP
import { NavioSearchComponent } from '../crud-navios/navio-search/navio-search.component'; import { NavioSearchComponent } from '../crud-navios/navio-search/navio-search.component';
import { NavioService } from '../../services/navio.service'; import { NavioService } from '../../services/navio.service';
import { CsvService } from '../../csv.service';
@Component({ @Component({
selector: 'app-dashboard', selector: 'app-dashboard',
templateUrl: './dashboard.component.html', templateUrl: './dashboard.component.html',
...@@ -43,7 +43,9 @@ export class DashboardComponent implements OnInit { ...@@ -43,7 +43,9 @@ export class DashboardComponent implements OnInit {
onResize() { onResize() {
this.isSmallScreen = window.innerWidth < 768; this.isSmallScreen = window.innerWidth < 768;
} }
constructor(private navioService: NavioService) {} constructor(private navioService: NavioService,
private _csvService: CsvService // Injetando o serviço CSV
) {}
ngOnInit(): void { ngOnInit(): void {
// Consumindo o serviço para obter os dados do dashboard // Consumindo o serviço para obter os dados do dashboard
...@@ -82,7 +84,7 @@ export class DashboardComponent implements OnInit { ...@@ -82,7 +84,7 @@ export class DashboardComponent implements OnInit {
{ {
data: [response.statusCounts.Y, response.statusCounts.NE, response.totalBlackLists], data: [response.statusCounts.Y, response.statusCounts.NE, response.totalBlackLists],
backgroundColor: ['#36A2EB', '#FF6384', '#FFCE56'] backgroundColor: ['#36A2EB', '#FF6384', '#FFCE56']
} }
] ]
}; };
...@@ -93,4 +95,27 @@ export class DashboardComponent implements OnInit { ...@@ -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 @@ ...@@ -26,9 +26,9 @@
<td mat-cell *matCellDef="let row"> <td mat-cell *matCellDef="let row">
{{ {{
row.role === 'COMPANY' row.role === 'COMPANY'
? 'ADMIN' ? 'Agente do Porto'
: row.role === 'CANDIDATE' : row.role === 'CANDIDATE'
? 'USUÁRIO' ? 'Agente do Navio'
: 'Desconhecido' : 'Desconhecido'
}} }}
</td> </td>
...@@ -81,4 +81,4 @@ ...@@ -81,4 +81,4 @@
> >
Última Página Última Página
</button> </button>
</div> </div>
\ No newline at end of file
...@@ -86,4 +86,9 @@ export class BlackListService { ...@@ -86,4 +86,9 @@ export class BlackListService {
return throwError(error); 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 { ...@@ -19,7 +19,14 @@ export class AceiteService {
constructor(private _http: HttpClient) { } 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> { addAceite(data: any, foto: File | null): Observable<any> {
console.log(JSON.stringify(JSON.stringify(data))); console.log(JSON.stringify(JSON.stringify(data)));
console.log(foto); console.log(foto);
...@@ -83,13 +90,12 @@ export class AceiteService { ...@@ -83,13 +90,12 @@ export class AceiteService {
if (data.imo) { if (data.imo) {
params = params.set('imo', data.imo); params = params.set('imo', data.imo);
} }
if (data.status) { if (data.dataInicio) {
params = params.set('status', data.status); params = params.set('dataInicio', data.dataInicio);
} }
if (data.nome) { if (data.dataFim) {
params = params.set('nome', data.nome); params = params.set('dataFim', data.dataFim);
} }
// Faz a requisição GET com os parâmetros // Faz a requisição GET com os parâmetros
return this._http.get<any>(`${this.apiUrl}/custom`, { params }).pipe( return this._http.get<any>(`${this.apiUrl}/custom`, { params }).pipe(
tap((res: any) => { tap((res: any) => {
......
...@@ -79,7 +79,7 @@ export class BercosService { ...@@ -79,7 +79,7 @@ export class BercosService {
} }
getBercosListSemPaginacao(): Observable<any> { getBercosListSemPaginacao(): Observable<any> {
return this._http.get<any>(this.apiUrl).pipe( return this._http.get<any>(`${this.apiUrl}/sem-paginacao`).pipe(
tap((res: any) => { tap((res: any) => {
console.log('Lista completa de berços:', res); console.log('Lista completa de berços:', res);
}), }),
...@@ -112,4 +112,12 @@ export class BercosService { ...@@ -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 { ...@@ -5,7 +5,7 @@ import {
HttpParams, HttpParams,
} from '@angular/common/http'; } from '@angular/common/http';
import { Observable, throwError } from 'rxjs'; 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 import { environment } from '../../environments/.env'; // Importa o environment
@Injectable({ @Injectable({
...@@ -15,7 +15,7 @@ export class NavioService { ...@@ -15,7 +15,7 @@ export class NavioService {
users: any[] = []; users: any[] = [];
private apiUrl = environment.API_URL + '/api/vessels'; private apiUrl = environment.API_URL + '/api/vessels';
private apiUrlDash = environment.API_URL + '/statistics/all'; private apiUrlDash = environment.API_URL + '/statistics/all';
constructor(private _http: HttpClient) {} constructor(private _http: HttpClient) { }
addEmployee1(data: any, foto: File): Observable<any> { addEmployee1(data: any, foto: File): Observable<any> {
console.log(JSON.stringify(JSON.stringify(data))); console.log(JSON.stringify(JSON.stringify(data)));
...@@ -72,11 +72,11 @@ export class NavioService { ...@@ -72,11 +72,11 @@ export class NavioService {
if (data.imo) { if (data.imo) {
params = params.set('imo', data.imo); params = params.set('imo', data.imo);
} }
if (data.status) { if (data.dataInicio) {
params = params.set('status', data.status); params = params.set('dataInicio', data.dataInicio);
} }
if (data.nome) { if (data.dataFim) {
params = params.set('nome', data.nome); params = params.set('dataFim', data.dataFim);
} }
// Faz a requisição GET com os parâmetros // Faz a requisição GET com os parâmetros
...@@ -172,6 +172,13 @@ export class NavioService { ...@@ -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 { HttpClient, HttpParams } from '@angular/common/http';
import { Injectable } from '@angular/core'; 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 import { environment } from '../../../environments/.env'; // Importa o environment
@Injectable({ @Injectable({
...@@ -42,4 +42,12 @@ export class UserService { ...@@ -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 @@ ...@@ -12,4 +12,4 @@
</button> </button>
} }
</div> </div>
</div> </div>
\ No newline at end of file
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