| @@ -1,29 +1,41 @@ | |||
| import { Directive, EventEmitter, Input, Output, OnInit } from '@angular/core'; | |||
| import { FormGroup } from '@angular/forms'; | |||
| import { ModalStatus } from '@app/_helpers/modal.states'; | |||
| import { Observable } from 'rxjs'; | |||
| import { Router } from '@angular/router'; | |||
| import { TranslateService } from '@ngx-translate/core'; | |||
| import {ModalStatus} from "@app/_helpers/modal.states"; | |||
| export type SaveFunction<T> = (item: T) => Observable<T>; | |||
| export type UpdateFunction<T> = (id: string | number, item: T) => Observable<T>; | |||
| export type DeleteFunction = (id: string | number) => Observable<any>; | |||
| export enum FormMode { | |||
| Create = 'create', | |||
| Edit = 'edit' | |||
| } | |||
| export interface FormSubmitEvent<T> { | |||
| status: ModalStatus; | |||
| data: T | null; | |||
| } | |||
| @Directive() | |||
| export abstract class AbstractDataFormComponent<T extends { [key: string]: any }> implements OnInit { | |||
| @Input() data?: T; | |||
| @Input() mode: FormMode = FormMode.Create; | |||
| @Input() id?: string | number; | |||
| @Output() submit: EventEmitter<ModalStatus> = new EventEmitter<ModalStatus>(); | |||
| @Input() redirectAfterDelete?: string; | |||
| @Output() submit = new EventEmitter<FormSubmitEvent<T>>(); | |||
| @Output() deleted: EventEmitter<void> = new EventEmitter<void>(); | |||
| protected form!: FormGroup; | |||
| constructor( | |||
| protected formConfig: FormGroup, | |||
| protected createFn: SaveFunction<T>, | |||
| protected updateFn: UpdateFunction<T> | |||
| protected updateFn: UpdateFunction<T>, | |||
| protected deleteFn?: DeleteFunction, | |||
| protected translateService?: TranslateService, | |||
| protected router?: Router | |||
| ) { | |||
| this.form = formConfig; | |||
| } | |||
| @@ -32,12 +44,14 @@ export abstract class AbstractDataFormComponent<T extends { [key: string]: any } | |||
| if (this.data) { | |||
| this.form.patchValue(this.data); | |||
| } else if (this.mode === FormMode.Create) { | |||
| this.data = this.getInitialData(); | |||
| this.data = this.getNewDataSet(); | |||
| this.form.patchValue(this.data); | |||
| } | |||
| } | |||
| protected abstract getInitialData(): T; | |||
| protected getNewDataSet(): T { | |||
| return {} as T; | |||
| } | |||
| onSubmit(): void { | |||
| if (!this.form.valid) return; | |||
| @@ -48,12 +62,40 @@ export abstract class AbstractDataFormComponent<T extends { [key: string]: any } | |||
| : this.updateFn(this.id!, formData); | |||
| request$.subscribe({ | |||
| next: () => { | |||
| // this.form.reset(); | |||
| this.submit.emit(ModalStatus.Submitted); | |||
| next: (response) => { | |||
| this.submit.emit({ | |||
| status: ModalStatus.Submitted, | |||
| data: response | |||
| }); | |||
| }, | |||
| error: (error) => { | |||
| console.error('Error saving data:', error); | |||
| this.submit.emit({ | |||
| status: ModalStatus.Cancelled, // Statt Error verwenden wir Cancelled | |||
| data: null | |||
| }); | |||
| } | |||
| }); | |||
| } | |||
| onDelete(): void { | |||
| if (!this.isEditMode() || !this.deleteFn || !this.id || !this.translateService) { | |||
| return; | |||
| } | |||
| this.translateService.get('basic.delete_confirm').subscribe((confirmMessage: string) => { | |||
| if (confirm(confirmMessage)) { | |||
| this.deleteFn!(this.id!).subscribe({ | |||
| next: () => { | |||
| this.deleted.emit(); | |||
| if (this.redirectAfterDelete && this.router) { | |||
| this.router.navigate([this.redirectAfterDelete]); | |||
| } | |||
| }, | |||
| error: (error) => { | |||
| console.error('Error deleting data:', error); | |||
| } | |||
| }); | |||
| } | |||
| }); | |||
| } | |||
| @@ -118,14 +118,14 @@ | |||
| class="btn btn-primary spt-icon-details" | |||
| data-type="user-tool" | |||
| data-action="edit" | |||
| target="_blank" | |||
| [target]="detailLinkNewTab ? '_blank' : '_self'" | |||
| [routerLink]="[appHelperService.getLink(element, column.url)]"> | |||
| </a> | |||
| <a *ngIf="getCustomDetailLinkFunction" | |||
| class="btn btn-primary spt-icon-details" | |||
| data-type="user-tool" | |||
| data-action="edit" | |||
| target="_blank" | |||
| [target]="detailLinkNewTab ? '_blank' : '_self'" | |||
| [routerLink]="['/' + getCustomDetailLinkFunction(element)]"> | |||
| </a> | |||
| </ng-container> | |||
| @@ -38,6 +38,7 @@ export class ListComponent implements OnInit, AfterViewInit, OnDestroy { | |||
| @Input() public displayOptions!: { [key: string]: string }; | |||
| @Input() public defaultDisplayOption!: string; | |||
| @Input() public refreshIntervalSeconds?: number; | |||
| @Input() public detailLinkNewTab?: boolean; | |||
| @ViewChild(MatSort) sort; | |||
| @ViewChild("pagingComponent", {static: false}) protected pagingComponent!: PagingComponent; | |||
| @ViewChild("filterBarComponent", {static: false}) protected filterBarComponent!: FilterBarComponent; | |||
| @@ -90,7 +91,7 @@ export class ListComponent implements OnInit, AfterViewInit, OnDestroy { | |||
| this.hidePageSize = false; | |||
| this.dataSource = new MatTableDataSource<any>(); | |||
| this.filterConfig = null; | |||
| this.detailLinkNewTab = false; | |||
| } | |||
| ngOnInit(): void { | |||
| @@ -2,6 +2,7 @@ import {DomSanitizer, SafeHtml} from "@angular/platform-browser"; | |||
| import {Injectable} from "@angular/core"; | |||
| import {NgbModal, NgbModalOptions} from "@ng-bootstrap/ng-bootstrap"; | |||
| import {ModalStatus} from "@app/_helpers/modal.states"; | |||
| import {FormSubmitEvent} from "@app/_components/_abstract/abstract-data-form-component"; | |||
| @Injectable({providedIn: 'root'}) | |||
| export class AppHelperService { | |||
| @@ -50,8 +51,8 @@ export class AppHelperService { | |||
| modalRef.componentInstance[key] = data[key]; | |||
| } | |||
| return modalRef.componentInstance.submit.subscribe((modalStatus: ModalStatus) => { | |||
| if (modalStatus === ModalStatus.Submitted) { | |||
| return modalRef.componentInstance.submit.subscribe((event: FormSubmitEvent<any>) => { | |||
| if (event.status === ModalStatus.Submitted) { | |||
| modalRef.dismiss(); | |||
| if (callback) { | |||
| callback(callbackParam); | |||
| @@ -4,13 +4,7 @@ | |||
| [data]="location" | |||
| [mode]="FormMode.Edit" | |||
| [id]="appHelperService.extractId(location.id!)" | |||
| (submit)="onFormSubmit()" | |||
| (submit)="onFormUpdate($event)" | |||
| ></app-location-form> | |||
| <div class="mt-4 flex gap-2"> | |||
| <button (click)="apiDeleteLocation()" class="spt-button spt-button-danger"> | |||
| {{ 'basic.delete' | translate }} {{ 'model.location' | translate }} | |||
| </button> | |||
| </div> | |||
| </div> | |||
| } | |||
| } | |||
| @@ -1,10 +1,9 @@ | |||
| import {Component, OnInit} from '@angular/core'; | |||
| import {LocationJsonld, LocationService} from "@app/core/api/v1"; | |||
| import {AppHelperService} from "@app/_helpers/app-helper.service"; | |||
| import {ActivatedRoute, Router} from "@angular/router"; | |||
| import {ROUTE_LOCATIONS} from "@app/app-routing.module"; | |||
| import {TranslateService} from "@ngx-translate/core"; | |||
| import {FormMode} from "@app/_components/_abstract/abstract-data-form-component"; | |||
| import { Component, OnInit } from '@angular/core'; | |||
| import { LocationJsonld, LocationService } from "@app/core/api/v1"; | |||
| import { AppHelperService } from "@app/_helpers/app-helper.service"; | |||
| import { ActivatedRoute } from "@angular/router"; | |||
| import { FormMode, FormSubmitEvent } from "@app/_components/_abstract/abstract-data-form-component"; | |||
| import {ModalStatus} from "@app/_helpers/modal.states"; | |||
| @Component({ | |||
| selector: 'app-location-detail', | |||
| @@ -17,39 +16,22 @@ export class LocationDetailComponent implements OnInit { | |||
| constructor( | |||
| private locationService: LocationService, | |||
| protected appHelperService: AppHelperService, | |||
| protected translateService: TranslateService, | |||
| private route: ActivatedRoute, | |||
| protected router: Router | |||
| private route: ActivatedRoute | |||
| ) {} | |||
| ngOnInit() { | |||
| this.route.params.subscribe(params => { | |||
| this.apiGetLocationData(params['id']); | |||
| this.locationService.locationsIdGet(params['id']).subscribe( | |||
| data => { | |||
| this.location = data; | |||
| } | |||
| ); | |||
| }); | |||
| } | |||
| apiGetLocationData(locationId: string) { | |||
| this.locationService.locationsIdGet(locationId).subscribe( | |||
| data => { | |||
| console.log(data); | |||
| this.location = data; | |||
| } | |||
| ); | |||
| onFormUpdate(event: FormSubmitEvent<LocationJsonld>) { | |||
| if (event.status === ModalStatus.Submitted && event.data) { | |||
| this.location = event.data; | |||
| } | |||
| } | |||
| apiDeleteLocation() { | |||
| this.translateService.get('basic.delete_confirm').subscribe((confirmMessage: string) => { | |||
| if (confirm(confirmMessage)) { | |||
| this.locationService.locationsIdDelete( | |||
| this.appHelperService.extractId(this.location.id!) | |||
| ).subscribe(() => { | |||
| this.router.navigate(['/' + ROUTE_LOCATIONS]); | |||
| }); | |||
| } | |||
| }); | |||
| } | |||
| onFormSubmit() { | |||
| this.apiGetLocationData(this.appHelperService.extractId(this.location.id!)); | |||
| } | |||
| } | |||
| } | |||
| @@ -23,8 +23,17 @@ | |||
| </app-search-select> | |||
| <input id="zone" type="hidden" formControlName="zone"/> | |||
| </div> | |||
| <button type="submit" class="btn btn-primary" [disabled]="form.invalid"> | |||
| {{ (isEditMode() ? 'basic.save' : 'basic.create') | translate }} | |||
| </button> | |||
| <div class="flex gap-2"> | |||
| <button type="submit" class="btn btn-primary" [disabled]="form.invalid"> | |||
| {{ 'basic.save' | translate }} | |||
| </button> | |||
| @if (isEditMode()) { | |||
| <button type="button" class="spt-button spt-button-danger" (click)="onDelete()"> | |||
| {{ 'basic.delete' | translate }} {{ 'model.location' | translate }} | |||
| </button> | |||
| } | |||
| </div> | |||
| </form> | |||
| </div> | |||
| @@ -1,10 +1,13 @@ | |||
| import { Component } from '@angular/core'; | |||
| import {AbstractDataFormComponent} from "@app/_components/_abstract/abstract-data-form-component"; | |||
| import {LocationJsonld, LocationService, ZoneService} from "@app/core/api/v1"; | |||
| import {SearchSelectComponent} from "@app/_components/search-select/search-select.component"; | |||
| import {ListColDefinition} from "@app/_components/list/list-col-definition"; | |||
| import {locationForm} from "@app/_forms/apiForms"; | |||
| import {ListGetDataFunctionType} from "@app/_components/list/list-get-data-function-type"; | |||
| import { AbstractDataFormComponent } from "@app/_components/_abstract/abstract-data-form-component"; | |||
| import { LocationJsonld, LocationService, ZoneService } from "@app/core/api/v1"; | |||
| import { SearchSelectComponent } from "@app/_components/search-select/search-select.component"; | |||
| import { ListColDefinition } from "@app/_components/list/list-col-definition"; | |||
| import { locationForm } from "@app/_forms/apiForms"; | |||
| import { ListGetDataFunctionType } from "@app/_components/list/list-get-data-function-type"; | |||
| import { TranslateService } from "@ngx-translate/core"; | |||
| import { Router } from "@angular/router"; | |||
| import { ROUTE_BASE_DATA } from "@app/app-routing.module"; | |||
| @Component({ | |||
| selector: 'app-location-form', | |||
| @@ -16,22 +19,24 @@ export class LocationFormComponent extends AbstractDataFormComponent<LocationJso | |||
| constructor( | |||
| private locationService: LocationService, | |||
| private zoneService: ZoneService | |||
| private zoneService: ZoneService, | |||
| translateService: TranslateService, | |||
| router: Router | |||
| ) { | |||
| super( | |||
| locationForm, | |||
| (data: LocationJsonld) => locationService.locationsPost(data), | |||
| (id: string | number, data: LocationJsonld) => locationService.locationsIdPatch(id.toString(), data) | |||
| locationForm, | |||
| (data: LocationJsonld) => this.locationService.locationsPost(data), | |||
| (id: string | number, data: LocationJsonld) => this.locationService.locationsIdPatch(id.toString(), data), | |||
| (id: string | number) => this.locationService.locationsIdDelete(id.toString()), | |||
| translateService, | |||
| router | |||
| ); | |||
| this.redirectAfterDelete = '/' + ROUTE_BASE_DATA; | |||
| this.zoneColDefinitions = SearchSelectComponent.getDefaultColDefZones(); | |||
| } | |||
| protected override getInitialData(): LocationJsonld { | |||
| return {} as LocationJsonld; | |||
| } | |||
| getZones: ListGetDataFunctionType = (index: number, pageSize: number, term?: string) => { | |||
| return this.zoneService.zonesGetCollection(index, pageSize, term); | |||
| } | |||
| } | |||
| } | |||
| @@ -20,6 +20,6 @@ | |||
| <button type="submit" | |||
| class="btn btn-primary" | |||
| [disabled]="form.invalid"> | |||
| {{ (isEditMode() ? 'basic.save' : 'basic.create') | translate }} | |||
| {{ 'basic.save' | translate }} | |||
| </button> | |||
| </form> | |||
| @@ -18,7 +18,7 @@ export class ShippingCompanyFormComponent extends AbstractDataFormComponent<Ship | |||
| ); | |||
| } | |||
| protected override getInitialData(): ShippingCompanyJsonld { | |||
| protected override getNewDataSet(): ShippingCompanyJsonld { | |||
| return {} as ShippingCompanyJsonld; | |||
| } | |||
| } | |||
| @@ -32,6 +32,6 @@ | |||
| <button type="submit" | |||
| class="btn btn-primary" | |||
| [disabled]="form.invalid"> | |||
| {{ (isEditMode() ? 'basic.save' : 'basic.create') | translate }} | |||
| {{ 'basic.save' | translate }} | |||
| </button> | |||
| </form> | |||
| @@ -27,7 +27,7 @@ export class VesselFormComponent extends AbstractDataFormComponent<VesselJsonld> | |||
| this.shippingCompanyColDefinitions = SearchSelectComponent.getDefaultColDefShippingCompanies(); | |||
| } | |||
| protected override getInitialData(): VesselJsonld { | |||
| protected override getNewDataSet(): VesselJsonld { | |||
| return {} as VesselJsonld; | |||
| } | |||
| @@ -1,4 +1,4 @@ | |||
| <h2>{{ (isEditMode() ? 'basic.edit' : 'basic.new') | translate }} {{ 'model.location' | translate }}</h2> | |||
| <h2>{{ (isEditMode() ? 'basic.edit' : 'basic.new') | translate }} {{ 'model.zone' | translate }}</h2> | |||
| <div class="spt-form"> | |||
| <form [formGroup]="form" (ngSubmit)="onSubmit()"> | |||
| <div class="mb-3"> | |||
| @@ -10,7 +10,7 @@ | |||
| <input type="text" class="form-control" id="code" formControlName="code" required/> | |||
| </div> | |||
| <button type="submit" class="btn btn-primary" [disabled]="form.invalid"> | |||
| {{ (isEditMode() ? 'basic.save' : 'basic.create') | translate }} | |||
| {{ 'basic.save' | translate }} | |||
| </button> | |||
| </form> | |||
| </div> | |||
| @@ -22,8 +22,5 @@ export class ZoneFormComponent extends AbstractDataFormComponent<ZoneJsonld> { | |||
| ); | |||
| } | |||
| protected override getInitialData(): ZoneJsonld { | |||
| return {} as ZoneJsonld; | |||
| } | |||
| } | |||