import { AfterContentChecked, Component, EventEmitter, Input, Output, OnChanges, OnDestroy, ViewChild, inject, } from '@angular/core'; import { CommonModule, DecimalPipe, DatePipe } from '@angular/common'; import { UntypedFormBuilder, UntypedFormGroup, FormsModule, ReactiveFormsModule } from '@angular/forms'; import { Router, RouterLink } from '@angular/router'; import { MatButtonModule } from '@angular/material/button'; import { MatCardModule } from '@angular/material/card'; import { MatDialog, MatDialogModule } from '@angular/material/dialog'; import { MatFormFieldModule } from '@angular/material/form-field'; import { MatIconModule } from '@angular/material/icon'; import { MatInputModule } from '@angular/material/input'; import { MatMenuModule } from '@angular/material/menu'; import { MatNativeDateModule } from '@angular/material/core'; import { MatPaginator, MatPaginatorIntl, MatPaginatorModule } from '@angular/material/paginator'; import { MatProgressBarModule } from '@angular/material/progress-bar'; import { MatProgressSpinnerModule } from '@angular/material/progress-spinner'; import { MatSort, MatSortModule } from '@angular/material/sort'; import { MatTableDataSource, MatTableModule } from '@angular/material/table'; import { MatSnackBar, MatSnackBarHorizontalPosition, MatSnackBarModule, MatSnackBarVerticalPosition, } from '@angular/material/snack-bar'; import { Observable, Subscription } from 'rxjs'; import { take } from 'rxjs/operators'; import { NgxSkeletonLoaderModule } from 'ngx-skeleton-loader'; import { FrenchPaginator } from '@components/shared/french-paginator.component'; import { ColumnDefinition, Errors, Jump, JumpByDay, JumpFile, JumpFileType, JumpList, JumpListConfig, jumpColumns, } from '@models'; import { JumpsService, JumpTableService } from '@services'; import { JumpAddDialogComponent, JumpDeleteDialogComponent, JumpEditDialogComponent, JumpViewDialogComponent, } from '@components/logbook/dialogs'; @Component({ imports: [ CommonModule, DecimalPipe, DatePipe, RouterLink, FormsModule, ReactiveFormsModule, NgxSkeletonLoaderModule, MatButtonModule, MatCardModule, MatDialogModule, MatFormFieldModule, MatIconModule, MatInputModule, MatNativeDateModule, MatMenuModule, MatPaginatorModule, MatProgressBarModule, MatProgressSpinnerModule, MatSnackBarModule, MatSortModule, MatTableModule, JumpAddDialogComponent, JumpDeleteDialogComponent, JumpEditDialogComponent, JumpViewDialogComponent, ], selector: 'app-jump-table', styleUrl: './jump-table.component.scss', templateUrl: './jump-table.component.html', providers: [{ provide: MatPaginatorIntl, useClass: FrenchPaginator }], }) export class JumpTableComponent implements OnChanges, OnDestroy, AfterContentChecked { private router = inject(Router); private _jumpsService = inject(JumpsService); private _jumpTableService = inject(JumpTableService); private dialog = inject(MatDialog); private fb = inject(UntypedFormBuilder); private snackBar = inject(MatSnackBar); private _jumps: Subscription = new Subscription(); private _subscriptions: Array = []; //private _jump: Subscription = new Subscription(); private _query: JumpListConfig = { type: 'all', filters: {} }; //private _lastjump: Jump = {} as Jump; private _currentPage = 1; public errors!: Errors; public jump: Jump = {} as Jump; public jumps!: MatTableDataSource; public jumpsCount = 0; /* public displayedColumns: string[] = [ 'numero', 'date', 'lieu', 'oaci', 'aeronef', 'imat', 'hauteur', 'voile', 'taille', 'categorie', 'module', 'participants', 'programme', 'accessoires', 'zone', 'files', 'actions' ];*/ public displayedColumns: string[] = [ 'numero', 'date', 'lieu', 'aeronef', 'hauteur', 'voile', 'categorie', 'participants', 'zone', 'accessoires', 'files', 'actions', ]; //public displayedColumns: string[] = jumpColumns.map((col) => col.key) public columnsSchema: Array = jumpColumns; public searchForm: UntypedFormGroup; public loading = false; public isSubmitting = false; public isDeleting = false; public tableRefresh = false; public jumpRefresh = false; //public valid: any = {}; @ViewChild(MatSort) sort!: MatSort; @ViewChild(MatPaginator) paginator!: MatPaginator; constructor() { this._resetErrors(); this.searchForm = this.fb.group({ //numero: ['', Validators.required] numero: '', }); } @Input() title = ''; @Input() limit = 0; @Input() showAdd = false; @Input() isUser = false; @Input() set config(config: JumpListConfig) { if (config) { this._query = config; this._currentPage = 1; } } get config(): JumpListConfig { return this._query; } @Output() countChange: EventEmitter = new EventEmitter(); ngOnChanges() { this.loading = true; this._query = this.config; this.runQuery(); //console.log('ngOnChanges event in jump-table component'); } ngOnDestroy() { this._jumps.unsubscribe(); this._subscriptions.forEach((subscription) => { subscription.unsubscribe(); }); } ngAfterContentChecked() { if (this.jumpRefresh != this._jumpTableService.jumpRefresh) { this.jumpRefresh = this._jumpTableService.jumpRefresh; this.runQuery(); //console.log('table has been refreshed in jump-table component'); } } runQuery() { /* const jump$: Observable = this._jumpsService.getLastJump(); this._jump = jump$.subscribe((jump) => { this._lastjump = jump; }); */ if (this.limit) { this._query.filters.limit = this.limit; this._query.filters.offset = this.limit * (this._currentPage - 1); } else { this._query.filters.limit = 0; this._query.filters.offset = 0; } /* this._query.filters.numeroRangeStart = 1; this._query.filters.numeroRangeEnd = 0; this._query.filters.tailleRangeStart = 135; this._query.filters.tailleRangeEnd = 230; this._query.filters.participantsRangeStart = 1; this._query.filters.participantsRangeEnd = 10; this._query.filters.hauteurRangeStart = 0; this._query.filters.hauteurRangeEnd = 8000; */ const jumps$: Observable = this._jumpsService.query(this._query); this._jumps = jumps$.subscribe({ next: (data: JumpList) => { //this.updateJumps(data.jumps); //this.linkSkydiverIdJumps(); //this.getKmlJumpFiles(data.jumps); this.loading = false; this.jumps = new MatTableDataSource(data.jumps); this.jumpsCount = data.jumpsCount; this.countChange.emit(this.jumpsCount); this.paginator.length = data.jumpsCount; this.jumps.paginator = this.paginator; this.jumps.sort = this.sort; this._resetErrors(); }, error: (err) => { this.errors = err; }, }); } linkSkydiverIdJumps() { const config: JumpListConfig = { type: 'all', filters: {} } as JumpListConfig; config.filters.limit = 200; config.filters.offset = 0; config.filters.dateRangeStart = '2024-01-01 00:00:00'; //const year = new Date().getFullYear(); //config.filters.dateRangeEnd = `${year}-12-31 23:59:59`; config.filters.dateRangeEnd = '2024-12-31 23:59:59'; //console.log(config.filters); const subscription: Subscription = this._jumpsService .getAllByDay(config) .pipe(take(1)) .subscribe({ next: (days) => { this._resetErrors(); //console.log('getAllByDay : ', days); days.forEach((day) => { this.loadSkydiverIdJumps(day, config); }); }, error: (err) => { this.errors = err; }, }); this._subscriptions.push(subscription); } loadSkydiverIdJumps(day: JumpByDay, config: JumpListConfig) { config.filters.dateRangeStart = `${day.jumps[0].date!.substring(0, 10)} 00:00:00`; config.filters.dateRangeEnd = `${day.jumps[0].date!.substring(0, 10)} 23:59:59`; const subscription: Subscription = this._jumpsService .getAllFromSkydiverIdApi(config) .pipe(take(1)) .subscribe({ next: (data) => { if (data.items.length) { const diff = day.count - data.items.length; if (diff < 0) { console.error( `${diff} ${diff > 1 ? 'données' : 'donnée'} altimètre de trop le ${day.jumps[0].date!.substring(0, 10)}`, ); } else { if (diff > 0) { console.error( `${diff}/${day.count} ${diff > 1 ? 'données altimètre manquantes' : 'donnée altimètre manquante'} le ${day.jumps[0].date!.substring(0, 10)}`, day.jumps, data.items, ); } console.log( `${day.count} ${day.count > 1 ? 'sauts' : 'saut'} le ${data.items[0].date!.substring(0, 10)}`, day.jumps.sort((a, b) => b.numero! - a.numero!), data.items, ); data.items.map((jump, index) => { const file: JumpFile = { name: jump.name, path: `/Volumes/Storage/Skydive/X2_Logs/${day.jumps[index].date!.substring(0, 4)}/`, type: JumpFileType.CSV, } as JumpFile; this._subscriptions.push( this._jumpsService .saveX2Data(day.jumps[index].slug!, file, jump) .pipe(take(1)) .subscribe({ next: (jump) => { this._resetErrors(); console.log( `Le fichier ${file.name} a été ajouté au saut n°${jump.numero} !`, 'OK', ); }, error: (err) => { this.errors = err; }, }), ); return jump; }); } //console.log(`${day.count} ${day.count > 1 ? 'sauts' : 'saut'} le ${day.jumps[0].date!.substring(0, 10)}`); //console.log(`${data.items.length} ${data.items.length > 1 ? 'sauts' : 'saut'} le ${data.items[0].date!.substring(0, 10)}`, data); } else { console.info('Aucune données altimètre pour le ', day.jumps[0].date!.substring(0, 10)); } }, error: (err) => { console.error('getAllFromSkydiverIdApi error'); this.errors = err; }, }); this._subscriptions.push(subscription); } updateJumps(jumps: Array) { jumps.map((jump) => { jump.files = []; if (jump.dossier !== '' && jump.video !== '') { const file: JumpFile = { name: jump.video!, path: jump.dossier!, type: JumpFileType.VIDEO, } as JumpFile; this._subscriptions.push( this._jumpsService .saveFile(jump.slug, file) .pipe(take(1)) .subscribe({ next: (file) => { this._resetErrors(); jump.files.push(file); //console.log(`Le fichier ${file.name} a été ajouté au saut n°${jump.numero} !`, 'OK'); }, error: (err) => { this.errors = err; }, }), ); } return jump; }); } getKmlJumpFiles(jumps: Array) { jumps.map((jump) => { if (jump.x2data !== null) { //console.log(`Le fichier ${jump.x2data.name} existe pour le saut n°${jump.numero} !`, `https://skydiver.id/api/jump/${jump.x2data.id}/kml`); const file: JumpFile = { name: jump.x2data.name.substring(0, -4) + '.kml', path: `/X2_Kmls/${jump.date.substring(0, 4)}/`, type: JumpFileType.KML, } as JumpFile; this._subscriptions.push( this._jumpsService .saveKml(jump.slug, file) .pipe(take(1)) .subscribe({ next: (file) => { this._resetErrors(); console.log( `Le fichier ${file.path}${file.name} a été ajouté au saut n°${jump.numero} !`, 'OK', ); }, error: (err) => { this.errors = err; }, }), ); } return jump; }); } deleteJump(slug: string) { this.isDeleting = true; this._subscriptions.push( this._jumpsService .destroy(slug) .pipe(take(1)) .subscribe(() => { this.router.navigateByUrl('/'); }), ); } openDeleteDialog(jump: Jump): void { const dialogRef = this.dialog.open(JumpDeleteDialogComponent, { width: '60vw', data: jump, }); this._subscriptions.push( dialogRef .afterClosed() .pipe(take(1)) .subscribe((result) => { if (result != undefined) { this._onDelete(result.slug); } }), ); } openEditDialog(jump: Jump): void { const dialogRef = this.dialog.open(JumpEditDialogComponent, { width: '70vw', data: jump, }); this._subscriptions.push( dialogRef .afterClosed() .pipe(take(1)) .subscribe((result) => { if (result != undefined) { this._onEdit(result); } }), ); } openViewDialog(jump: Jump): void { const dialogRef = this.dialog.open(JumpViewDialogComponent, { width: '60vw', data: jump, }); dialogRef.afterClosed(); } getErrorMessage() { if (this.searchForm.controls['numero'].errors !== null) { return 'Vous devez indiquer une valeur'; } return ''; } submitForm() { if (this.searchForm.valid) { this._query.filters.numero = this.searchForm.value.numero; this._resetErrors(); this.runQuery(); } } private _onDelete(slug: string): void { try { this.isDeleting = true; this._subscriptions.push( this._jumpsService .destroy(slug) .pipe(take(1)) .subscribe({ next: () => { this._resetErrors(); this._openSnackBar(`Le saut a été supprimé !`, 'OK'); this.runQuery(); this.tableRefresh = !this.tableRefresh; this._jumpTableService.updateTableRefresh(this.tableRefresh); }, error: (err) => { this.errors = err; }, }), ); } catch (error) { console.error(error); } } private _onEdit(jump: Jump): void { try { this.isSubmitting = true; this._subscriptions.push( this._jumpsService .update(jump) .pipe(take(1)) .subscribe({ next: (jump) => { this._resetErrors(); this._openSnackBar(`Le saut n°${jump.numero} a été mis à jour !`, 'OK'); this.runQuery(); this.tableRefresh = !this.tableRefresh; this._jumpTableService.updateTableRefresh(this.tableRefresh); }, error: (err) => { this.errors = err; this.isSubmitting = false; }, }), ); } catch (error) { console.error(error); } } private _openSnackBar(content: string, title: string) { this.snackBar.open(content, title, { horizontalPosition: 'end', verticalPosition: 'bottom', duration: 4000, }); } private _resetErrors(): void { this.errors = { errors: {} }; } /* disableSubmit(id: number) { if (this.valid[id]) { return Object.values(this.valid[id]).some((item) => item === false) } return false } inputHandler(e: any, id: number, key: string) { if (!this.valid[id]) { this.valid[id] = {} } this.valid[id][key] = e.target.validity.valid } */ /* isAllSelected() { return this.jumps.data.every((item) => item.isSelected) } isAnySelected() { return this.jumps.data.some((item) => item.isSelected) } selectAll(event: any) { this.jumps.data = this.jumps.data.map((item) => ({ ...item, isSelected: event.checked, })) } */ }