| @@ -4183,10 +4183,6 @@ components: | |||||
| type: | type: | ||||
| - string | - string | ||||
| - 'null' | - 'null' | ||||
| customerReference: | |||||
| type: | |||||
| - string | |||||
| - 'null' | |||||
| startLocation: | startLocation: | ||||
| readOnly: true | readOnly: true | ||||
| type: string | type: string | ||||
| @@ -4280,10 +4276,6 @@ components: | |||||
| type: | type: | ||||
| - string | - string | ||||
| - 'null' | - 'null' | ||||
| customerReference: | |||||
| type: | |||||
| - string | |||||
| - 'null' | |||||
| startLocation: | startLocation: | ||||
| readOnly: true | readOnly: true | ||||
| $ref: '#/components/schemas/Location.jsonld' | $ref: '#/components/schemas/Location.jsonld' | ||||
| @@ -4352,14 +4344,15 @@ components: | |||||
| - 'null' | - 'null' | ||||
| format: iri-reference | format: iri-reference | ||||
| example: 'https://example.com/' | example: 'https://example.com/' | ||||
| isArrival: | |||||
| type: boolean | |||||
| isTransit: | |||||
| type: boolean | |||||
| isDeparture: | |||||
| type: boolean | |||||
| date: | |||||
| type: string | |||||
| arrivalDateTime: | |||||
| type: | |||||
| - string | |||||
| - 'null' | |||||
| format: date-time | |||||
| departureDateTime: | |||||
| type: | |||||
| - string | |||||
| - 'null' | |||||
| format: date-time | format: date-time | ||||
| createdAt: | createdAt: | ||||
| readOnly: true | readOnly: true | ||||
| @@ -4370,7 +4363,6 @@ components: | |||||
| required: | required: | ||||
| - tripIri | - tripIri | ||||
| - locationIri | - locationIri | ||||
| - date | |||||
| TripLocation.jsonld: | TripLocation.jsonld: | ||||
| type: object | type: object | ||||
| description: '' | description: '' | ||||
| @@ -4420,14 +4412,15 @@ components: | |||||
| - 'null' | - 'null' | ||||
| format: iri-reference | format: iri-reference | ||||
| example: 'https://example.com/' | example: 'https://example.com/' | ||||
| isArrival: | |||||
| type: boolean | |||||
| isTransit: | |||||
| type: boolean | |||||
| isDeparture: | |||||
| type: boolean | |||||
| date: | |||||
| type: string | |||||
| arrivalDateTime: | |||||
| type: | |||||
| - string | |||||
| - 'null' | |||||
| format: date-time | |||||
| departureDateTime: | |||||
| type: | |||||
| - string | |||||
| - 'null' | |||||
| format: date-time | format: date-time | ||||
| createdAt: | createdAt: | ||||
| readOnly: true | readOnly: true | ||||
| @@ -4438,7 +4431,6 @@ components: | |||||
| required: | required: | ||||
| - tripIri | - tripIri | ||||
| - locationIri | - locationIri | ||||
| - date | |||||
| User: | User: | ||||
| type: object | type: object | ||||
| description: '' | description: '' | ||||
| @@ -1,4 +1,4 @@ | |||||
| import { Directive, EventEmitter, Input, Output, OnInit } from '@angular/core'; | |||||
| import {Directive, EventEmitter, Input, Output, OnInit, AfterViewInit} from '@angular/core'; | |||||
| import { FormGroup } from '@angular/forms'; | import { FormGroup } from '@angular/forms'; | ||||
| import { Observable } from 'rxjs'; | import { Observable } from 'rxjs'; | ||||
| import { Router } from '@angular/router'; | import { Router } from '@angular/router'; | ||||
| @@ -20,7 +20,7 @@ export interface FormSubmitEvent<T> { | |||||
| } | } | ||||
| @Directive() | @Directive() | ||||
| export abstract class AbstractDataFormComponent<T extends { [key: string]: any }> implements OnInit { | |||||
| export abstract class AbstractDataFormComponent<T extends { [key: string]: any }> implements OnInit, AfterViewInit { | |||||
| @Input() data?: T; | @Input() data?: T; | ||||
| @Input() mode: FormMode = FormMode.Create; | @Input() mode: FormMode = FormMode.Create; | ||||
| @Input() id?: string | number; | @Input() id?: string | number; | ||||
| @@ -51,6 +51,9 @@ export abstract class AbstractDataFormComponent<T extends { [key: string]: any } | |||||
| } | } | ||||
| } | } | ||||
| ngAfterViewInit() { | |||||
| } | |||||
| protected getNewDataSet(): T { | protected getNewDataSet(): T { | ||||
| return {} as T; | return {} as T; | ||||
| } | } | ||||
| @@ -5,6 +5,6 @@ | |||||
| </div> | </div> | ||||
| <div class="col-6"> | <div class="col-6"> | ||||
| <label for="{{ inputId }}-time">{{ label }} ({{ 'basic.time' | translate }}):</label> | <label for="{{ inputId }}-time">{{ label }} ({{ 'basic.time' | translate }}):</label> | ||||
| <input type="time" id="{{ inputId }}-time" class="form-control" formControlName="time" step="1" [readonly]="readonly"> | |||||
| <input type="time" id="{{ inputId }}-time" class="form-control" formControlName="time" [step]="showSeconds ? 1 : 60" [readonly]="readonly"> | |||||
| </div> | </div> | ||||
| </div> | </div> | ||||
| @@ -11,6 +11,7 @@ export class DatetimePickerComponent implements OnInit { | |||||
| @Input() inputId: string = 'myId'; | @Input() inputId: string = 'myId'; | ||||
| @Input() initialValue: string | null = null; | @Input() initialValue: string | null = null; | ||||
| @Input() readonly: boolean = false; | @Input() readonly: boolean = false; | ||||
| @Input() showSeconds: boolean = false; | |||||
| @Output() dateTimeChange = new EventEmitter<string | null>(); | @Output() dateTimeChange = new EventEmitter<string | null>(); | ||||
| form: FormGroup; | form: FormGroup; | ||||
| @@ -46,25 +47,48 @@ export class DatetimePickerComponent implements OnInit { | |||||
| return date.toLocaleDateString('en-CA'); | return date.toLocaleDateString('en-CA'); | ||||
| } | } | ||||
| private formatTime(date: Date): string { | |||||
| return date.toLocaleTimeString('en-GB', { hour12: false }); | |||||
| } | |||||
| private formatTime(date: Date): string { | |||||
| if (this.showSeconds) { | |||||
| return date.toLocaleTimeString('en-GB', { hour12: false }); | |||||
| } else { | |||||
| // Nur Stunden und Minuten zurückgeben | |||||
| return date.toLocaleTimeString('en-GB', { | |||||
| hour12: false, | |||||
| hour: '2-digit', | |||||
| minute: '2-digit' | |||||
| }); | |||||
| } | |||||
| } | |||||
| private emitDateTime() { | |||||
| const { date, time } = this.form.value; | |||||
| if (date && time) { | |||||
| const [year, month, day] = date.split('-'); | |||||
| private emitDateTime() { | |||||
| const { date, time } = this.form.value; | |||||
| if (date && time) { | |||||
| const [year, month, day] = date.split('-'); | |||||
| const [hours, minutes, seconds] = time.split(':'); | |||||
| const dateTime = new Date(Number(year), Number(month) - 1, Number(day), Number(hours), Number(minutes), Number(seconds)); | |||||
| // Parse Zeit je nach Format (mit oder ohne Sekunden) | |||||
| let hours, minutes, seconds; | |||||
| if (this.showSeconds && time.split(':').length > 2) { | |||||
| [hours, minutes, seconds] = time.split(':'); | |||||
| } else { | |||||
| [hours, minutes] = time.split(':'); | |||||
| seconds = '00'; | |||||
| } | |||||
| // Format the date to match the loaded format | |||||
| const formattedDate = dateTime.toLocaleString('sv-SE', { timeZone: 'Europe/Berlin' }).replace(' ', 'T') + '+02:00'; | |||||
| const dateTime = new Date( | |||||
| Number(year), | |||||
| Number(month) - 1, | |||||
| Number(day), | |||||
| Number(hours), | |||||
| Number(minutes), | |||||
| Number(seconds || 0) | |||||
| ); | |||||
| //console.log('Emitting datetime:', formattedDate); | |||||
| this.dateTimeChange.emit(formattedDate); | |||||
| } else { | |||||
| //console.log('Emitting null datetime'); | |||||
| this.dateTimeChange.emit(null); | |||||
| // Format the date to match the loaded format | |||||
| const formattedDate = dateTime.toLocaleString('sv-SE', { timeZone: 'Europe/Berlin' }).replace(' ', 'T') + '+02:00'; | |||||
| this.dateTimeChange.emit(formattedDate); | |||||
| } else { | |||||
| this.dateTimeChange.emit(null); | |||||
| } | |||||
| } | } | ||||
| } | |||||
| } | } | ||||
| @@ -29,6 +29,7 @@ export class ListComponent implements OnInit, AfterViewInit, OnDestroy { | |||||
| @Input() public onRowSelectedFunction!: Function; | @Input() public onRowSelectedFunction!: Function; | ||||
| @Input() public onUpdateBooleanStateFunction!: ListUpdateElementFunctionType; | @Input() public onUpdateBooleanStateFunction!: ListUpdateElementFunctionType; | ||||
| @Input() public dataFormComponent!: Type<AbstractDataFormComponent<any>>; | @Input() public dataFormComponent!: Type<AbstractDataFormComponent<any>>; | ||||
| @Input() public dataFormComponentData?: any; | |||||
| @Input() public searchable: boolean; | @Input() public searchable: boolean; | ||||
| @Input() public showDetailButton: boolean; | @Input() public showDetailButton: boolean; | ||||
| @Input() public showPosition: boolean; | @Input() public showPosition: boolean; | ||||
| @@ -122,6 +123,9 @@ export class ListComponent implements OnInit, AfterViewInit, OnDestroy { | |||||
| this.setupAutoRefresh(); | this.setupAutoRefresh(); | ||||
| } | } | ||||
| ngAfterViewInit(): void { | |||||
| } | |||||
| private setupAutoRefresh(): void { | private setupAutoRefresh(): void { | ||||
| this.clearAutoRefresh(); | this.clearAutoRefresh(); | ||||
| if (this.refreshIntervalSeconds && this.refreshIntervalSeconds > 0) { | if (this.refreshIntervalSeconds && this.refreshIntervalSeconds > 0) { | ||||
| @@ -205,9 +209,6 @@ export class ListComponent implements OnInit, AfterViewInit, OnDestroy { | |||||
| return visibility; | return visibility; | ||||
| } | } | ||||
| ngAfterViewInit(): void { | |||||
| } | |||||
| getData = (): void => { | getData = (): void => { | ||||
| this.getDataFunction( | this.getDataFunction( | ||||
| this.pagingComponent.getPageIndex(), | this.pagingComponent.getPageIndex(), | ||||
| @@ -392,7 +393,7 @@ export class ListComponent implements OnInit, AfterViewInit, OnDestroy { | |||||
| public onCreateData() { | public onCreateData() { | ||||
| this.appHelperService.openModal( | this.appHelperService.openModal( | ||||
| this.dataFormComponent, | this.dataFormComponent, | ||||
| null, | |||||
| this.dataFormComponentData, | |||||
| this.getData | this.getData | ||||
| ); | ); | ||||
| } | } | ||||
| @@ -99,7 +99,6 @@ export const tripForm = new FormGroup({ | |||||
| vessel: new FormControl(null, []), | vessel: new FormControl(null, []), | ||||
| vesselIri: new FormControl(null, [Validators.required]), | vesselIri: new FormControl(null, [Validators.required]), | ||||
| pilotageReference: new FormControl(null, []), | pilotageReference: new FormControl(null, []), | ||||
| customerReference: new FormControl(null, []), | |||||
| startLocation: new FormControl(null, []), | startLocation: new FormControl(null, []), | ||||
| startLocationIri: new FormControl(null, [Validators.required]), | startLocationIri: new FormControl(null, [Validators.required]), | ||||
| endLocation: new FormControl(null, []), | endLocation: new FormControl(null, []), | ||||
| @@ -116,7 +115,6 @@ export const tripJsonldForm = new FormGroup({ | |||||
| vessel: new FormControl(null, []), | vessel: new FormControl(null, []), | ||||
| vesselIri: new FormControl(null, [Validators.required]), | vesselIri: new FormControl(null, [Validators.required]), | ||||
| pilotageReference: new FormControl(null, []), | pilotageReference: new FormControl(null, []), | ||||
| customerReference: new FormControl(null, []), | |||||
| startLocation: new FormControl(null, []), | startLocation: new FormControl(null, []), | ||||
| startLocationIri: new FormControl(null, [Validators.required]), | startLocationIri: new FormControl(null, [Validators.required]), | ||||
| endLocation: new FormControl(null, []), | endLocation: new FormControl(null, []), | ||||
| @@ -134,10 +132,8 @@ export const tripLocationForm = new FormGroup({ | |||||
| tripIri: new FormControl(null, [Validators.required]), | tripIri: new FormControl(null, [Validators.required]), | ||||
| location: new FormControl(null, []), | location: new FormControl(null, []), | ||||
| locationIri: new FormControl(null, [Validators.required]), | locationIri: new FormControl(null, [Validators.required]), | ||||
| isArrival: new FormControl(null, []), | |||||
| isTransit: new FormControl(null, []), | |||||
| isDeparture: new FormControl(null, []), | |||||
| date: new FormControl(null, [Validators.required]), | |||||
| arrivalDateTime: new FormControl(null, []), | |||||
| departureDateTime: new FormControl(null, []), | |||||
| createdAt: new FormControl(null, []) | createdAt: new FormControl(null, []) | ||||
| }); | }); | ||||
| @@ -147,10 +143,8 @@ export const tripLocationJsonldForm = new FormGroup({ | |||||
| tripIri: new FormControl(null, [Validators.required]), | tripIri: new FormControl(null, [Validators.required]), | ||||
| location: new FormControl(null, []), | location: new FormControl(null, []), | ||||
| locationIri: new FormControl(null, [Validators.required]), | locationIri: new FormControl(null, [Validators.required]), | ||||
| isArrival: new FormControl(null, []), | |||||
| isTransit: new FormControl(null, []), | |||||
| isDeparture: new FormControl(null, []), | |||||
| date: new FormControl(null, [Validators.required]), | |||||
| arrivalDateTime: new FormControl(null, []), | |||||
| departureDateTime: new FormControl(null, []), | |||||
| createdAt: new FormControl(null, []) | createdAt: new FormControl(null, []) | ||||
| }); | }); | ||||
| @@ -47,6 +47,7 @@ export class AppHelperService { | |||||
| public openModal(component: any, data: any, callback?: (callbackParam?: any) => void, callbackParam?: any): Promise<ModalStatus> { | public openModal(component: any, data: any, callback?: (callbackParam?: any) => void, callbackParam?: any): Promise<ModalStatus> { | ||||
| const modalRef = this.modalService.open(component); | const modalRef = this.modalService.open(component); | ||||
| console.log(data); | |||||
| for (const key in data) { | for (const key in data) { | ||||
| modalRef.componentInstance[key] = data[key]; | modalRef.componentInstance[key] = data[key]; | ||||
| } | } | ||||
| @@ -16,106 +16,10 @@ | |||||
| <mat-tab label="{{ 'trip.itinerary' | translate }}"> | <mat-tab label="{{ 'trip.itinerary' | translate }}"> | ||||
| <div> | <div> | ||||
| <h4 class="mb-4">{{ 'trip.itinerary_locations' | translate }}</h4> | <h4 class="mb-4">{{ 'trip.itinerary_locations' | translate }}</h4> | ||||
| <div *ngFor="let tripLocation of tripLocations; let i = index" class="p-2 mb-2 changing-list"> | |||||
| <div class="row"> | |||||
| <div class="col-12 col-md-4 mb-1"> | |||||
| <label [for]="'location_' + i" class="form-label">Location*:</label> | |||||
| <app-search-select | |||||
| [formId]="'location'" | |||||
| [formLabelLangKey]="'model.location'" | |||||
| [documentForm]="locationForms[i]" | |||||
| [getDataFunction]="getLocations" | |||||
| [displayedDataField]="'name'" | |||||
| [listColDefinitions]="locationColDefinitions" | |||||
| [dataSet]="tripLocation.location" | |||||
| > | |||||
| </app-search-select> | |||||
| </div> | |||||
| <div class="col-12 col-md-2 mb-1"> | |||||
| <label class="form-label">trip.date (Date):</label> | |||||
| <div> | |||||
| <input | |||||
| type="date" | |||||
| class="form-control" | |||||
| [value]="formatDateForInput(tripLocation.date)" | |||||
| (change)="onDateInputChange($event, i)" | |||||
| /> | |||||
| </div> | |||||
| </div> | |||||
| <div class="col-12 col-md-2 mb-1"> | |||||
| <label class="form-label">trip.date (Time):</label> | |||||
| <div> | |||||
| <input | |||||
| type="time" | |||||
| class="form-control" | |||||
| [value]="formatTimeForInput(tripLocation.date)" | |||||
| (change)="onTimeInputChange($event, i)" | |||||
| /> | |||||
| </div> | |||||
| </div> | |||||
| <div class="col-12 col-md-2 mb-1 d-flex align-items-end"> | |||||
| <div class="form-check"> | |||||
| <input | |||||
| class="form-check-input" | |||||
| type="checkbox" | |||||
| [id]="'isArrival_' + i" | |||||
| [checked]="tripLocation.isArrival" | |||||
| (change)="onIsArrivalChange($event, i)" | |||||
| > | |||||
| <label class="form-check-label" [for]="'isArrival_' + i"> | |||||
| {{ 'trip.is_arrival' | translate }} | |||||
| </label> | |||||
| </div> | |||||
| <div class="form-check"> | |||||
| <input | |||||
| class="form-check-input" | |||||
| type="checkbox" | |||||
| [id]="'isTransit_' + i" | |||||
| [checked]="tripLocation.isTransit" | |||||
| (change)="onIsTransitChange($event, i)" | |||||
| > | |||||
| <label class="form-check-label" [for]="'isTransit_' + i"> | |||||
| {{ 'trip.is_transit' | translate }} | |||||
| </label> | |||||
| </div> | |||||
| <div class="form-check"> | |||||
| <input | |||||
| class="form-check-input" | |||||
| type="checkbox" | |||||
| [id]="'isDeparture_' + i" | |||||
| [checked]="tripLocation.isDeparture" | |||||
| (change)="onIsDepartureChange($event, i)" | |||||
| > | |||||
| <label class="form-check-label" [for]="'isDeparture_' + i"> | |||||
| {{ 'trip.is_departure' | translate }} | |||||
| </label> | |||||
| </div> | |||||
| </div> | |||||
| <div class="col-12 col-md-2 mb-1 d-flex align-items-end"> | |||||
| <button type="button" class="btn btn-danger mb-3" (click)="removeTripLocation(i)">X</button> | |||||
| </div> | |||||
| </div> | |||||
| </div> | |||||
| <div class="row"> | |||||
| <div class="col-12 mb-3"> | |||||
| <button type="button" class="btn btn-primary" (click)="addNewTripLocation()">+</button> | |||||
| </div> | |||||
| </div> | |||||
| <div class="row"> | |||||
| <div class="col-12 mb-3"> | |||||
| <button type="button" class="btn btn-primary" (click)="saveAllTripLocations()"> | |||||
| {{ 'basic.save' | translate }} | |||||
| </button> | |||||
| </div> | |||||
| </div> | |||||
| <app-trip-location-list | |||||
| [trip]="trip" | |||||
| > | |||||
| </app-trip-location-list> | |||||
| </div> | </div> | ||||
| </mat-tab> | </mat-tab> | ||||
| <mat-tab label="{{ 'trip.assigned_users' | translate }}"> | <mat-tab label="{{ 'trip.assigned_users' | translate }}"> | ||||
| @@ -137,10 +41,6 @@ | |||||
| > | > | ||||
| </app-search-select> | </app-search-select> | ||||
| </div> | </div> | ||||
| <div class="col-12 col-md-2 d-flex align-items-end"> | |||||
| <button type="button" class="btn btn-danger mb-3" (click)="removeUserTrip(i)">X</button> | |||||
| </div> | |||||
| </div> | </div> | ||||
| </div> | </div> | ||||
| @@ -168,74 +168,6 @@ export class TripDetailComponent implements OnInit, AfterViewInit { | |||||
| } | } | ||||
| } | } | ||||
| formatDateForInput(dateString: string): string { | |||||
| if (!dateString) return ''; | |||||
| const date = new Date(dateString); | |||||
| return date.toISOString().split('T')[0]; | |||||
| } | |||||
| formatTimeForInput(dateString: string): string { | |||||
| if (!dateString) return ''; | |||||
| const date = new Date(dateString); | |||||
| return date.toTimeString().slice(0, 5); | |||||
| } | |||||
| onDateInputChange(event: Event, index: number) { | |||||
| const input = event.target as HTMLInputElement; | |||||
| const currentDate = new Date(this.tripLocations[index].date || new Date()); | |||||
| const [year, month, day] = input.value.split('-').map(Number); | |||||
| currentDate.setFullYear(year, month - 1, day); | |||||
| this.tripLocations[index].date = currentDate.toISOString(); | |||||
| } | |||||
| onTimeInputChange(event: Event, index: number) { | |||||
| const input = event.target as HTMLInputElement; | |||||
| const currentDate = new Date(this.tripLocations[index].date || new Date()); | |||||
| const [hours, minutes] = input.value.split(':').map(Number); | |||||
| currentDate.setHours(hours, minutes); | |||||
| this.tripLocations[index].date = currentDate.toISOString(); | |||||
| } | |||||
| onIsArrivalChange(event: Event, index: number) { | |||||
| const checkbox = event.target as HTMLInputElement; | |||||
| this.tripLocations[index].isArrival = checkbox.checked; | |||||
| } | |||||
| onIsTransitChange(event: Event, index: number) { | |||||
| const checkbox = event.target as HTMLInputElement; | |||||
| this.tripLocations[index].isTransit = checkbox.checked; | |||||
| } | |||||
| onIsDepartureChange(event: Event, index: number) { | |||||
| const checkbox = event.target as HTMLInputElement; | |||||
| this.tripLocations[index].isDeparture = checkbox.checked; | |||||
| } | |||||
| addNewTripLocation() { | |||||
| // Create a new empty trip location | |||||
| const newTripLocation: TripLocationJsonld = { | |||||
| tripIri: this.trip.id!, | |||||
| date: new Date().toISOString(), | |||||
| isArrival: false, | |||||
| locationIri: null | |||||
| }; | |||||
| this.tripLocations.push(newTripLocation); | |||||
| this.locationForms.push(this.createLocationForm(null)); | |||||
| // Force update in the next event loop to properly initialize the search-select | |||||
| setTimeout(() => { | |||||
| if (this.searchSelects) { | |||||
| const lastSelect = this.searchSelects.last; | |||||
| if (lastSelect) { | |||||
| lastSelect.ngAfterViewInit(); | |||||
| } | |||||
| } | |||||
| }); | |||||
| } | |||||
| addNewUserTrip() { | addNewUserTrip() { | ||||
| // Erstelle ein unvollständiges Objekt (ohne user-Property) | // Erstelle ein unvollständiges Objekt (ohne user-Property) | ||||
| const newUserTrip: UserTripJsonld = { | const newUserTrip: UserTripJsonld = { | ||||
| @@ -303,110 +235,6 @@ export class TripDetailComponent implements OnInit, AfterViewInit { | |||||
| }) | }) | ||||
| } | } | ||||
| removeTripLocation(index: number): void { | |||||
| this.translateService.get('basic.delete_confirm').subscribe((confirmMessage: string) => { | |||||
| if (!confirm(confirmMessage)) { | |||||
| return; | |||||
| } | |||||
| // Just remove from arrays; deletion happens later in saveAllTripLocations() | |||||
| this.tripLocations.splice(index, 1); | |||||
| this.locationForms.splice(index, 1); | |||||
| }); | |||||
| } | |||||
| removeUserTrip(index: number) { | |||||
| this.translateService.get('basic.delete_confirm').subscribe((confirmMessage: string) => { | |||||
| if (!confirm(confirmMessage)) { | |||||
| return; | |||||
| } | |||||
| // Nur aus dem Array entfernen, tatsächliches Löschen erfolgt beim Speichern | |||||
| this.userTrips.splice(index, 1); | |||||
| this.userForms.splice(index, 1); | |||||
| }); | |||||
| } | |||||
| saveAllTripLocations(): void { | |||||
| // A) DELETE: find which original dbIds are missing → delete those | |||||
| const originalDbIds = this.originalTripLocations | |||||
| .map(o => o.dbId) | |||||
| // narrow type to number (filter out null/undefined) | |||||
| .filter((id): id is number => id != null); | |||||
| const currentDbIds = this.tripLocations | |||||
| .map(t => t.dbId) | |||||
| .filter((id): id is number => id != null); | |||||
| const dbIdsToDelete = originalDbIds.filter(id => !currentDbIds.includes(id)); | |||||
| dbIdsToDelete.forEach(dbId => { | |||||
| this.tripLocationService | |||||
| .tripLocationsIdDelete(dbId.toString()) | |||||
| .subscribe({ | |||||
| next: () => console.log(`TripLocation ${dbId} deleted.`), | |||||
| error: err => console.error(`Error deleting TripLocation ${dbId}:`, err) | |||||
| }); | |||||
| }); | |||||
| // B) UPSERT: create or update remaining entries | |||||
| this.tripLocations.forEach((tripLocation, index) => { | |||||
| const form = this.locationForms[index]; | |||||
| const locationVal = form.get('location')?.value; | |||||
| const dbId = tripLocation.dbId; | |||||
| const isNew = dbId == null; // no dbId → brand new | |||||
| // only for existing entries compare against snapshot | |||||
| let locationChanged = false; | |||||
| let modelChanged = false; | |||||
| if (!isNew) { | |||||
| const original = this.originalTripLocations[index]; | |||||
| locationChanged = locationVal !== original.locationIri; | |||||
| modelChanged = ( | |||||
| tripLocation.date !== original.date || | |||||
| tripLocation.isArrival !== original.isArrival || | |||||
| tripLocation.isTransit !== original.isTransit || | |||||
| tripLocation.isDeparture !== original.isDeparture | |||||
| ); | |||||
| } | |||||
| // skip if neither new nor changed | |||||
| if (!(isNew || locationChanged || modelChanged)) { | |||||
| return; | |||||
| } | |||||
| // validate a location was chosen | |||||
| if (!locationVal) { | |||||
| console.error(`Location missing for entry #${index}`); | |||||
| return; | |||||
| } | |||||
| if (typeof locationVal === 'string') { | |||||
| tripLocation.locationIri = locationVal; | |||||
| } | |||||
| // always ensure the parent trip IRI is set | |||||
| tripLocation.tripIri = this.trip?.id ?? null; | |||||
| // choose correct API call | |||||
| const save$ = isNew | |||||
| ? this.tripLocationService.tripLocationsPost(tripLocation) | |||||
| : this.tripLocationService.tripLocationsIdPatch( | |||||
| dbId!.toString(), | |||||
| this.appHelperService.convertJsonldToJson(tripLocation) | |||||
| ); | |||||
| // execute save and log result | |||||
| save$.subscribe({ | |||||
| next: saved => { | |||||
| console.log(`TripLocation #${index} ${isNew ? 'created' : 'updated'}.`); | |||||
| }, | |||||
| error: err => { | |||||
| console.error(`Error saving TripLocation #${index}:`, err); | |||||
| } | |||||
| }); | |||||
| }); | |||||
| // C) Finally: reset original snapshot to current state | |||||
| this.originalTripLocations = JSON.parse(JSON.stringify(this.tripLocations)); | |||||
| } | |||||
| saveAllUserTrips() { | saveAllUserTrips() { | ||||
| // Aktuelle IDs speichern, um später gelöschte Einträge zu identifizieren | // Aktuelle IDs speichern, um später gelöschte Einträge zu identifizieren | ||||
| let originalUserTripIds: string[] = []; | let originalUserTripIds: string[] = []; | ||||
| @@ -49,10 +49,6 @@ | |||||
| </app-search-select> | </app-search-select> | ||||
| <input id="endLocationIri" type="hidden" formControlName="endLocationIri" required/> | <input id="endLocationIri" type="hidden" formControlName="endLocationIri" required/> | ||||
| </div> | </div> | ||||
| <div class="col-12 col-lg-6 mb-3"> | |||||
| <label for="customerReference" class="form-label">{{ 'trip.customer_reference' | translate }}:</label> | |||||
| <input type="text" class="form-control" id="customerReference" formControlName="customerReference"/> | |||||
| </div> | |||||
| <div class="col-12 col-lg-6 mb-3"> | <div class="col-12 col-lg-6 mb-3"> | ||||
| <app-datetime-picker | <app-datetime-picker | ||||
| [label]="'trip.start_date' | translate" | [label]="'trip.start_date' | translate" | ||||
| @@ -65,7 +61,7 @@ | |||||
| <div class="col-12 col-lg-6 mb-3"> | <div class="col-12 col-lg-6 mb-3"> | ||||
| <app-datetime-picker | <app-datetime-picker | ||||
| [label]="'trip.end_date' | translate" | [label]="'trip.end_date' | translate" | ||||
| [inputId]="'startDate'" | |||||
| [inputId]="'endDate'" | |||||
| [initialValue]="tripForm.get('endDate')?.value ?? null" | [initialValue]="tripForm.get('endDate')?.value ?? null" | ||||
| (dateTimeChange)="onDateChange($event, 'endDate')" | (dateTimeChange)="onDateChange($event, 'endDate')" | ||||
| ></app-datetime-picker> | ></app-datetime-picker> | ||||
| @@ -32,14 +32,6 @@ export class TripListComponent { | |||||
| sortable: true, | sortable: true, | ||||
| filterType: FilterBarComponent.FILTER_TYPE_TEXT, | filterType: FilterBarComponent.FILTER_TYPE_TEXT, | ||||
| } as ListColDefinition, | } as ListColDefinition, | ||||
| { | |||||
| name: 'customerReference', | |||||
| text: 'trip.customer_reference', | |||||
| type: ListComponent.COLUMN_TYPE_TEXT, | |||||
| field: 'customerReference', | |||||
| sortable: true, | |||||
| filterType: FilterBarComponent.FILTER_TYPE_TEXT, | |||||
| } as ListColDefinition, | |||||
| { | { | ||||
| name: 'vessel', | name: 'vessel', | ||||
| text: 'trip.vessel', | text: 'trip.vessel', | ||||
| @@ -0,0 +1,57 @@ | |||||
| <div class="spt-container"> | |||||
| @if (!isEditMode()) { | |||||
| <div class="spt-headline d-flex justify-content-between align-items-start"> | |||||
| <h2>{{ ('basic.new') | translate }} {{ 'model.trip_location' | translate }}</h2> | |||||
| </div> | |||||
| } | |||||
| <div class="spt-form"> | |||||
| <form [formGroup]="tripLocationForm" (ngSubmit)="onSubmit()"> | |||||
| <div class="row"> | |||||
| <div class="col-12 mb-3"> | |||||
| <label for="locationIri" class="form-label">{{ 'trip.start_location' | translate }}*:</label> | |||||
| <app-search-select #locationSearchSelect | |||||
| [formId]="'locationIri'" | |||||
| [formLabelLangKey]="'model.location'" | |||||
| [documentForm]="form" | |||||
| [getDataFunction]="getLocations" | |||||
| [displayedDataField]="'name'" | |||||
| [listColDefinitions]="locationColDefinitions" | |||||
| [dataSet]="data?.location" | |||||
| > | |||||
| </app-search-select> | |||||
| <input id="locationIri" type="hidden" formControlName="locationIri" required/> | |||||
| </div> | |||||
| <div class="col-12 col-lg-6 mb-3"> | |||||
| <app-datetime-picker | |||||
| [label]="'trip_location.arrival_date_time' | translate" | |||||
| [inputId]="'arrivalDateTime'" | |||||
| [initialValue]="tripLocationForm.get('arrivalDateTime')?.value ?? null" | |||||
| (dateTimeChange)="onDateChange($event, 'arrivalDateTime')" | |||||
| ></app-datetime-picker> | |||||
| </div> | |||||
| <div class="col-12 col-lg-6 mb-3"> | |||||
| <app-datetime-picker | |||||
| [label]="'trip_location.departure_date_time' | translate" | |||||
| [inputId]="'departureDateTime'" | |||||
| [initialValue]="tripLocationForm.get('departureDateTime')?.value ?? null" | |||||
| (dateTimeChange)="onDateChange($event, 'departureDateTime')" | |||||
| ></app-datetime-picker> | |||||
| </div> | |||||
| </div> | |||||
| <div class="row"> | |||||
| <div class="col-12 mb-3"> | |||||
| <button type="submit" class="btn btn-primary" [disabled]="form.invalid"> | |||||
| {{ 'basic.save' | translate }} | |||||
| </button> | |||||
| @if (isEditMode()) { | |||||
| <button type="button" class="ms-3 btn btn-primary" (click)="onDelete()"> | |||||
| {{ 'basic.delete' | translate }} {{ 'model.trip' | translate }} | |||||
| </button> | |||||
| } | |||||
| </div> | |||||
| </div> | |||||
| </form> | |||||
| </div> | |||||
| </div> | |||||
| @@ -0,0 +1,23 @@ | |||||
| import { ComponentFixture, TestBed } from '@angular/core/testing'; | |||||
| import { TripLocationFormComponent } from './trip-location-form.component'; | |||||
| describe('TripLocationFormComponent', () => { | |||||
| let component: TripLocationFormComponent; | |||||
| let fixture: ComponentFixture<TripLocationFormComponent>; | |||||
| beforeEach(async () => { | |||||
| await TestBed.configureTestingModule({ | |||||
| declarations: [TripLocationFormComponent] | |||||
| }) | |||||
| .compileComponents(); | |||||
| fixture = TestBed.createComponent(TripLocationFormComponent); | |||||
| component = fixture.componentInstance; | |||||
| fixture.detectChanges(); | |||||
| }); | |||||
| it('should create', () => { | |||||
| expect(component).toBeTruthy(); | |||||
| }); | |||||
| }); | |||||
| @@ -0,0 +1,72 @@ | |||||
| import {Component, Input} from '@angular/core'; | |||||
| import {AbstractDataFormComponent} from "@app/_components/_abstract/abstract-data-form-component"; | |||||
| import { | |||||
| LocationService, TripJsonld, | |||||
| TripLocationJsonld, | |||||
| TripLocationService, | |||||
| } from "@app/core/api/v1"; | |||||
| import {AppHelperService} from "@app/_helpers/app-helper.service"; | |||||
| import {TranslateService} from "@ngx-translate/core"; | |||||
| import {Router} from "@angular/router"; | |||||
| import {ROUTE_BASE_DATA} from "@app/app-routing.module"; | |||||
| import {tripLocationForm} from "@app/_forms/apiForms"; | |||||
| import {ListGetDataFunctionType} from "@app/_components/list/list-get-data-function-type"; | |||||
| import {ListColDefinition} from "@app/_components/list/list-col-definition"; | |||||
| import {SearchSelectComponent} from "@app/_components/search-select/search-select.component"; | |||||
| @Component({ | |||||
| selector: 'app-trip-location-form', | |||||
| templateUrl: './trip-location-form.component.html', | |||||
| styleUrl: './trip-location-form.component.scss' | |||||
| }) | |||||
| export class TripLocationFormComponent extends AbstractDataFormComponent<TripLocationJsonld> { | |||||
| @Input() public trip!: TripJsonld; | |||||
| @Input() public arrivalDateTime?: string; | |||||
| @Input() public departureDateTime?: string; | |||||
| protected readonly SearchSelectComponent = SearchSelectComponent; | |||||
| protected locationColDefinitions: ListColDefinition[]; | |||||
| constructor( | |||||
| private tripLocationService: TripLocationService, | |||||
| private locationService: LocationService, | |||||
| private appHelperService: AppHelperService, | |||||
| translateService: TranslateService, | |||||
| router: Router | |||||
| ) { | |||||
| super( | |||||
| tripLocationForm, | |||||
| (data: TripLocationJsonld) => { | |||||
| return this.tripLocationService.tripLocationsPost(data); | |||||
| }, | |||||
| (id: string | number, data: TripLocationJsonld) => | |||||
| this.tripLocationService.tripLocationsIdPatch( | |||||
| id.toString(), | |||||
| this.appHelperService.convertJsonldToJson(data) | |||||
| ), | |||||
| (id: string | number) => this.tripLocationService.tripLocationsIdDelete(id.toString()), | |||||
| translateService, | |||||
| router | |||||
| ); | |||||
| this.redirectAfterDelete = '/' + ROUTE_BASE_DATA; | |||||
| this.locationColDefinitions = SearchSelectComponent.getDefaultColDefLocations(); | |||||
| } | |||||
| override ngOnInit() { | |||||
| super.ngOnInit(); | |||||
| this.form.get('tripIri')?.setValue(this.trip.id); | |||||
| this.form.get('arrivalDateTime')?.setValue(this.arrivalDateTime); | |||||
| this.form.get('departureDateTime')?.setValue(this.departureDateTime); | |||||
| } | |||||
| getLocations: ListGetDataFunctionType = (index: number, pageSize: number, term?: string) => { | |||||
| return this.locationService.locationsGetCollection( | |||||
| index, | |||||
| pageSize, | |||||
| term | |||||
| ); | |||||
| } | |||||
| protected readonly tripLocationForm = tripLocationForm; | |||||
| } | |||||
| @@ -0,0 +1,9 @@ | |||||
| <div class="spt-container"> | |||||
| <app-list #listComponent | |||||
| [listId]="'tripLocationList'" | |||||
| [getDataFunction]="getData" | |||||
| [listColDefinitions]="listColDefinitions" | |||||
| [dataFormComponent]="tripLocationFormComponent" | |||||
| [dataFormComponentData]="dataFormComponentData" | |||||
| ></app-list> | |||||
| </div> | |||||
| @@ -0,0 +1,23 @@ | |||||
| import { ComponentFixture, TestBed } from '@angular/core/testing'; | |||||
| import { TripLocationListComponent } from './trip-location-list.component'; | |||||
| describe('TripLocationListComponent', () => { | |||||
| let component: TripLocationListComponent; | |||||
| let fixture: ComponentFixture<TripLocationListComponent>; | |||||
| beforeEach(async () => { | |||||
| await TestBed.configureTestingModule({ | |||||
| declarations: [TripLocationListComponent] | |||||
| }) | |||||
| .compileComponents(); | |||||
| fixture = TestBed.createComponent(TripLocationListComponent); | |||||
| component = fixture.componentInstance; | |||||
| fixture.detectChanges(); | |||||
| }); | |||||
| it('should create', () => { | |||||
| expect(component).toBeTruthy(); | |||||
| }); | |||||
| }); | |||||
| @@ -0,0 +1,127 @@ | |||||
| import {Component, Input, Type, ViewChild} from '@angular/core'; | |||||
| import {ListComponent} from "@app/_components/list/list.component"; | |||||
| import {ListColDefinition} from "@app/_components/list/list-col-definition"; | |||||
| import {TripJsonld, TripLocationService} from "@app/core/api/v1"; | |||||
| import {AppHelperService} from "@app/_helpers/app-helper.service"; | |||||
| import {FilterBarComponent} from "@app/_components/filter-bar/filter-bar.component"; | |||||
| import {ListGetDataFunctionType} from "@app/_components/list/list-get-data-function-type"; | |||||
| import {TripLocationFormComponent} from "@app/_views/trip/trip-location-form/trip-location-form.component"; | |||||
| import {map, tap} from "rxjs/operators"; | |||||
| @Component({ | |||||
| selector: 'app-trip-location-list', | |||||
| templateUrl: './trip-location-list.component.html', | |||||
| styleUrl: './trip-location-list.component.scss' | |||||
| }) | |||||
| export class TripLocationListComponent { | |||||
| @Input() public trip!: TripJsonld; | |||||
| @ViewChild("listComponent", {static: false}) listComponent!: ListComponent; | |||||
| protected tripLocationFormComponent = TripLocationFormComponent; | |||||
| protected listColDefinitions!: ListColDefinition[]; | |||||
| protected dataFormComponentData: any; | |||||
| constructor( | |||||
| private tripLocationService: TripLocationService, | |||||
| protected appHelperService: AppHelperService, | |||||
| ) { | |||||
| this.listColDefinitions = [ | |||||
| { | |||||
| name: 'location', | |||||
| text: 'model.location', | |||||
| type: ListComponent.COLUMN_TYPE_TEXT, | |||||
| subResource: 'location', | |||||
| field: 'name', | |||||
| sortable: true, | |||||
| filterType: FilterBarComponent.FILTER_TYPE_TEXT, | |||||
| } as ListColDefinition, | |||||
| { | |||||
| name: 'zone', | |||||
| text: 'model.zone', | |||||
| type: ListComponent.COLUMN_TYPE_TEXT, | |||||
| subResource: 'location', | |||||
| field: 'zoneName', | |||||
| sortable: true, | |||||
| filterType: FilterBarComponent.FILTER_TYPE_TEXT, | |||||
| } as ListColDefinition, | |||||
| { | |||||
| name: 'arrivalDateTime', | |||||
| text: 'trip_location.arrival_date_time', | |||||
| type: ListComponent.COLUMN_TYPE_DATE, | |||||
| field: 'arrivalDateTime', | |||||
| sortable: true, | |||||
| filterType: FilterBarComponent.FILTER_TYPE_DATE, | |||||
| } as ListColDefinition, | |||||
| { | |||||
| name: 'departureDateTime', | |||||
| text: 'trip_location.departure_date_time', | |||||
| type: ListComponent.COLUMN_TYPE_DATE, | |||||
| field: 'departureDateTime', | |||||
| sortable: true, | |||||
| filterType: FilterBarComponent.FILTER_TYPE_DATE, | |||||
| } as ListColDefinition, | |||||
| { | |||||
| name: 'createdAt', | |||||
| text: 'common.created_at', | |||||
| type: ListComponent.COLUMN_TYPE_DATE, | |||||
| field: 'createdAt', | |||||
| sortable: true, | |||||
| filterType: FilterBarComponent.FILTER_TYPE_DATE, | |||||
| } as ListColDefinition, | |||||
| ]; | |||||
| } | |||||
| ngOnInit() { | |||||
| this.dataFormComponentData = { | |||||
| trip: this.trip, | |||||
| arrivalDateTime: new Date().toISOString(), | |||||
| departureDateTime: new Date(new Date().setHours(new Date().getHours() + 1)) | |||||
| }; | |||||
| } | |||||
| ngAfterViewInit(): void { | |||||
| this.listComponent.getData(); | |||||
| } | |||||
| getData: ListGetDataFunctionType = ( | |||||
| index: number, | |||||
| pageSize: number, | |||||
| term?: string, | |||||
| ) => { | |||||
| return this.tripLocationService.tripLocationsGetCollection( | |||||
| index, | |||||
| pageSize, | |||||
| this.trip.id, | |||||
| undefined, | |||||
| this.listComponent.getFilterJsonString(), | |||||
| this.listComponent.getSortingJsonString() | |||||
| ).pipe( | |||||
| map(response => { | |||||
| // Set default arrival and departure time before return | |||||
| let arrivalDateTime = new Date().toISOString(); | |||||
| let departureDateTime = new Date(new Date().setHours(new Date().getHours() + 1)).toISOString(); | |||||
| if (response.member && response.member.length > 0) { | |||||
| const lastEntry = response.member[response.member.length - 1]; | |||||
| if (lastEntry.departureDateTime) { | |||||
| const departureDate = new Date(lastEntry.departureDateTime); | |||||
| departureDate.setDate(departureDate.getDate() + 1); | |||||
| arrivalDateTime = departureDate.toISOString(); | |||||
| const arrivalDate = new Date(arrivalDateTime); | |||||
| arrivalDate.setHours(arrivalDate.getHours() + 1); | |||||
| departureDateTime = arrivalDate.toISOString(); | |||||
| } | |||||
| } | |||||
| this.dataFormComponentData = { | |||||
| trip: this.trip, | |||||
| arrivalDateTime: arrivalDateTime, | |||||
| departureDateTime: departureDateTime | |||||
| }; | |||||
| return response; | |||||
| }) | |||||
| ); | |||||
| } | |||||
| } | |||||
| @@ -71,6 +71,8 @@ import { UserTripEventComponent } from './_views/user-trip-event/user-trip-event | |||||
| import { UserTripEventListComponent } from './_views/user-trip-event/user-trip-event-list/user-trip-event-list.component'; | import { UserTripEventListComponent } from './_views/user-trip-event/user-trip-event-list/user-trip-event-list.component'; | ||||
| import { UserFormComponent } from './_views/user/user-form/user-form.component'; | import { UserFormComponent } from './_views/user/user-form/user-form.component'; | ||||
| import { ImageUploadComponent } from './_components/image-upload/image-upload.component'; | import { ImageUploadComponent } from './_components/image-upload/image-upload.component'; | ||||
| import { TripLocationListComponent } from './_views/trip/trip-location-list/trip-location-list.component'; | |||||
| import { TripLocationFormComponent } from './_views/trip/trip-location-form/trip-location-form.component'; | |||||
| registerLocaleData(localeDe, 'de-DE'); | registerLocaleData(localeDe, 'de-DE'); | ||||
| @@ -167,6 +169,8 @@ export function HttpLoaderFactory(http: HttpClient) { | |||||
| UserTripEventListComponent, | UserTripEventListComponent, | ||||
| UserFormComponent, | UserFormComponent, | ||||
| ImageUploadComponent, | ImageUploadComponent, | ||||
| TripLocationListComponent, | |||||
| TripLocationFormComponent, | |||||
| ], | ], | ||||
| providers: [ | providers: [ | ||||
| {provide: HTTP_INTERCEPTORS, useClass: JwtInterceptor, multi: true}, | {provide: HTTP_INTERCEPTORS, useClass: JwtInterceptor, multi: true}, | ||||
| @@ -19,7 +19,6 @@ export interface Trip { | |||||
| readonly vessel?: string; | readonly vessel?: string; | ||||
| vesselIri: string | null; | vesselIri: string | null; | ||||
| readonly pilotageReference?: string | null; | readonly pilotageReference?: string | null; | ||||
| customerReference?: string | null; | |||||
| readonly startLocation?: string; | readonly startLocation?: string; | ||||
| startLocationIri: string | null; | startLocationIri: string | null; | ||||
| readonly endLocation?: string; | readonly endLocation?: string; | ||||
| @@ -24,7 +24,6 @@ export interface TripJsonld { | |||||
| readonly vessel?: VesselJsonld; | readonly vessel?: VesselJsonld; | ||||
| vesselIri: string | null; | vesselIri: string | null; | ||||
| readonly pilotageReference?: string | null; | readonly pilotageReference?: string | null; | ||||
| customerReference?: string | null; | |||||
| readonly startLocation?: LocationJsonld; | readonly startLocation?: LocationJsonld; | ||||
| startLocationIri: string | null; | startLocationIri: string | null; | ||||
| readonly endLocation?: LocationJsonld; | readonly endLocation?: LocationJsonld; | ||||
| @@ -22,10 +22,8 @@ export interface TripLocation { | |||||
| tripIri: string | null; | tripIri: string | null; | ||||
| location?: Location; | location?: Location; | ||||
| locationIri: string | null; | locationIri: string | null; | ||||
| isArrival?: boolean; | |||||
| isTransit?: boolean; | |||||
| isDeparture?: boolean; | |||||
| date: string; | |||||
| arrivalDateTime?: string | null; | |||||
| departureDateTime?: string | null; | |||||
| readonly createdAt?: string | null; | readonly createdAt?: string | null; | ||||
| } | } | ||||
| @@ -25,10 +25,8 @@ export interface TripLocationJsonld { | |||||
| tripIri: string | null; | tripIri: string | null; | ||||
| location?: LocationJsonld; | location?: LocationJsonld; | ||||
| locationIri: string | null; | locationIri: string | null; | ||||
| isArrival?: boolean; | |||||
| isTransit?: boolean; | |||||
| isDeparture?: boolean; | |||||
| date: string; | |||||
| arrivalDateTime?: string | null; | |||||
| departureDateTime?: string | null; | |||||
| readonly createdAt?: string | null; | readonly createdAt?: string | null; | ||||
| } | } | ||||
| @@ -43,6 +43,11 @@ | |||||
| "events": "Events", | "events": "Events", | ||||
| "completed": "Completed" | "completed": "Completed" | ||||
| }, | }, | ||||
| "trip_location": | |||||
| { | |||||
| "arrival_date_time": "ETA", | |||||
| "departure_date_time": "ETD" | |||||
| }, | |||||
| "user_trip": | "user_trip": | ||||
| { | { | ||||
| "view": "Pilotage", | "view": "Pilotage", | ||||
| @@ -67,6 +72,7 @@ | |||||
| "vessel": "Vessel", | "vessel": "Vessel", | ||||
| "shipping_company": "Shipping Company", | "shipping_company": "Shipping Company", | ||||
| "trip": "Trip", | "trip": "Trip", | ||||
| "trip_location": "Itinerary location", | |||||
| "user": "User", | "user": "User", | ||||
| "user_trip": "Pilotage", | "user_trip": "Pilotage", | ||||
| "event": "Event" | "event": "Event" | ||||
| @@ -0,0 +1,41 @@ | |||||
| <?php | |||||
| declare(strict_types=1); | |||||
| namespace DoctrineMigrations; | |||||
| use Doctrine\DBAL\Schema\Schema; | |||||
| use Doctrine\Migrations\AbstractMigration; | |||||
| /** | |||||
| * Auto-generated Migration: Please modify to your needs! | |||||
| */ | |||||
| final class Version20250507154018 extends AbstractMigration | |||||
| { | |||||
| public function getDescription(): string | |||||
| { | |||||
| return ''; | |||||
| } | |||||
| public function up(Schema $schema): void | |||||
| { | |||||
| // this up() migration is auto-generated, please modify it to your needs | |||||
| $this->addSql(<<<'SQL' | |||||
| ALTER TABLE trip DROP customer_reference | |||||
| SQL); | |||||
| $this->addSql(<<<'SQL' | |||||
| ALTER TABLE trip_location ADD arrival_date_time DATETIME DEFAULT NULL COMMENT '(DC2Type:datetime_immutable)', ADD departure_date_time DATETIME DEFAULT NULL COMMENT '(DC2Type:datetime_immutable)', DROP is_arrival, DROP date, DROP is_transit, DROP is_departure | |||||
| SQL); | |||||
| } | |||||
| public function down(Schema $schema): void | |||||
| { | |||||
| // this down() migration is auto-generated, please modify it to your needs | |||||
| $this->addSql(<<<'SQL' | |||||
| ALTER TABLE trip_location ADD is_arrival TINYINT(1) NOT NULL, ADD date DATETIME NOT NULL COMMENT '(DC2Type:datetime_immutable)', ADD is_transit TINYINT(1) NOT NULL, ADD is_departure TINYINT(1) NOT NULL, DROP arrival_date_time, DROP departure_date_time | |||||
| SQL); | |||||
| $this->addSql(<<<'SQL' | |||||
| ALTER TABLE trip ADD customer_reference VARCHAR(255) DEFAULT NULL | |||||
| SQL); | |||||
| } | |||||
| } | |||||
| @@ -81,8 +81,6 @@ class TripApi | |||||
| #[ApiProperty(writable: false)] | #[ApiProperty(writable: false)] | ||||
| public ?string $pilotageReference = null; | public ?string $pilotageReference = null; | ||||
| public ?string $customerReference = null; | |||||
| /** | /** | ||||
| * @var LocationApi | * @var LocationApi | ||||
| */ | */ | ||||
| @@ -40,7 +40,7 @@ use Symfony\Component\Validator\Constraints\NotBlank; | |||||
| security: 'is_granted("ROLE_ADMIN")' | security: 'is_granted("ROLE_ADMIN")' | ||||
| ) | ) | ||||
| ], | ], | ||||
| order: ['date' => 'ASC'], | |||||
| order: ['arrivalDateTime' => 'ASC', 'departureDateTime' => 'ASC'], | |||||
| security: 'is_granted("ROLE_USER")', | security: 'is_granted("ROLE_USER")', | ||||
| provider: EntityToDtoStateProvider::class, | provider: EntityToDtoStateProvider::class, | ||||
| processor: EntityClassDtoStateProcessor::class, | processor: EntityClassDtoStateProcessor::class, | ||||
| @@ -62,9 +62,9 @@ class TripLocationApi | |||||
| * @var TripApi | * @var TripApi | ||||
| */ | */ | ||||
| #[ApiProperty( | #[ApiProperty( | ||||
| writable: true, | |||||
| writable: false, | |||||
| readableLink: true, | readableLink: true, | ||||
| writableLink: true, | |||||
| writableLink: false, | |||||
| builtinTypes: [ | builtinTypes: [ | ||||
| new Type( | new Type( | ||||
| 'object', | 'object', | ||||
| @@ -82,9 +82,9 @@ class TripLocationApi | |||||
| * @var LocationApi | * @var LocationApi | ||||
| */ | */ | ||||
| #[ApiProperty( | #[ApiProperty( | ||||
| writable: true, | |||||
| writable: false, | |||||
| readableLink: true, | readableLink: true, | ||||
| writableLink: true, | |||||
| writableLink: false, | |||||
| builtinTypes: [ | builtinTypes: [ | ||||
| new Type( | new Type( | ||||
| 'object', | 'object', | ||||
| @@ -98,14 +98,9 @@ class TripLocationApi | |||||
| #[ApiProperty(writable: true)] | #[ApiProperty(writable: true)] | ||||
| public ?LocationApi $locationIri = null; | public ?LocationApi $locationIri = null; | ||||
| public bool $isArrival = false; | |||||
| public ?\DateTimeImmutable $arrivalDateTime = null; | |||||
| public bool $isTransit = false; | |||||
| public bool $isDeparture = false; | |||||
| #[Assert\NotBlank] | |||||
| public \DateTimeImmutable $date; | |||||
| public ?\DateTimeImmutable $departureDateTime = null; | |||||
| #[ApiProperty(writable: false)] | #[ApiProperty(writable: false)] | ||||
| public ?\DateTimeImmutable $createdAt = null; | public ?\DateTimeImmutable $createdAt = null; | ||||
| @@ -22,9 +22,6 @@ class Trip | |||||
| #[ORM\JoinColumn(nullable: false)] | #[ORM\JoinColumn(nullable: false)] | ||||
| private Vessel $vessel; | private Vessel $vessel; | ||||
| #[ORM\Column(length: 255, nullable: true)] | |||||
| private ?string $customerReference = null; | |||||
| #[ORM\ManyToOne(targetEntity: Location::class)] | #[ORM\ManyToOne(targetEntity: Location::class)] | ||||
| #[ORM\JoinColumn(name: 'start_location_id', nullable: false)] | #[ORM\JoinColumn(name: 'start_location_id', nullable: false)] | ||||
| private Location $startLocation; | private Location $startLocation; | ||||
| @@ -24,26 +24,19 @@ class TripLocation | |||||
| #[ORM\JoinColumn(nullable: false)] | #[ORM\JoinColumn(nullable: false)] | ||||
| private Location $location; | private Location $location; | ||||
| #[ORM\Column(nullable: false)] | |||||
| private bool $isArrival = true; | |||||
| #[ORM\Column(nullable: true)] | |||||
| private ?DateTimeImmutable $arrivalDateTime = null; | |||||
| #[ORM\Column(nullable: false)] | |||||
| private bool $isTransit = true; | |||||
| #[ORM\Column(nullable: false)] | |||||
| private bool $isDeparture = true; | |||||
| #[ORM\Column] | |||||
| private DateTimeImmutable $date; | |||||
| #[ORM\Column(nullable: true)] | |||||
| private ?DateTimeImmutable $departureDateTime = null; | |||||
| #[ORM\Column] | #[ORM\Column] | ||||
| private DateTimeImmutable $createdAt; | private DateTimeImmutable $createdAt; | ||||
| public function __construct(Trip $trip, Location $location, DateTimeImmutable $date) | |||||
| public function __construct(Trip $trip, Location $location) | |||||
| { | { | ||||
| $this->trip = $trip; | $this->trip = $trip; | ||||
| $this->location = $location; | $this->location = $location; | ||||
| $this->date = $date; | |||||
| $this->createdAt = new DateTimeImmutable(); | $this->createdAt = new DateTimeImmutable(); | ||||
| } | } | ||||
| @@ -74,45 +67,24 @@ class TripLocation | |||||
| return $this; | return $this; | ||||
| } | } | ||||
| public function getDate(): DateTimeImmutable | |||||
| { | |||||
| return $this->date; | |||||
| } | |||||
| public function setDate(DateTimeImmutable $date): self | |||||
| { | |||||
| $this->date = $date; | |||||
| return $this; | |||||
| } | |||||
| public function isArrival(): bool | |||||
| { | |||||
| return $this->isArrival; | |||||
| } | |||||
| public function setIsArrival(bool $isArrival): void | |||||
| { | |||||
| $this->isArrival = $isArrival; | |||||
| } | |||||
| public function isTransit(): bool | |||||
| public function getArrivalDateTime(): ?DateTimeImmutable | |||||
| { | { | ||||
| return $this->isTransit; | |||||
| return $this->arrivalDateTime; | |||||
| } | } | ||||
| public function setIsTransit(bool $isTransit): void | |||||
| public function setArrivalDateTime(?DateTimeImmutable $arrivalDateTime): void | |||||
| { | { | ||||
| $this->isTransit = $isTransit; | |||||
| $this->arrivalDateTime = $arrivalDateTime; | |||||
| } | } | ||||
| public function isDeparture(): bool | |||||
| public function getDepartureDateTime(): ?DateTimeImmutable | |||||
| { | { | ||||
| return $this->isDeparture; | |||||
| return $this->departureDateTime; | |||||
| } | } | ||||
| public function setIsDeparture(bool $isDeparture): void | |||||
| public function setDepartureDateTime(?DateTimeImmutable $departureDateTime): void | |||||
| { | { | ||||
| $this->isDeparture = $isDeparture; | |||||
| $this->departureDateTime = $departureDateTime; | |||||
| } | } | ||||
| public function getCreatedAt(): DateTimeImmutable | public function getCreatedAt(): DateTimeImmutable | ||||
| @@ -73,7 +73,6 @@ class TripApiToEntityMapper implements MapperInterface | |||||
| assert($dto instanceof TripApi); | assert($dto instanceof TripApi); | ||||
| assert($entity instanceof Trip); | assert($entity instanceof Trip); | ||||
| $entity->setCustomerReference($dto->customerReference); | |||||
| $entity->setStartDate($dto->startDate); | $entity->setStartDate($dto->startDate); | ||||
| $entity->setEndDate($dto->endDate); | $entity->setEndDate($dto->endDate); | ||||
| $entity->setNote($dto->note); | $entity->setNote($dto->note); | ||||
| @@ -38,7 +38,6 @@ class TripEntityToApiMapper implements MapperInterface | |||||
| $dto->dbId = $entity->getId(); | $dto->dbId = $entity->getId(); | ||||
| $dto->pilotageReference = "P-" . $entity->getId() . "-" . $entity->getStartDate()->format('Y'); | $dto->pilotageReference = "P-" . $entity->getId() . "-" . $entity->getStartDate()->format('Y'); | ||||
| $dto->customerReference = $entity->getCustomerReference(); | |||||
| $dto->startDate = $entity->getStartDate(); | $dto->startDate = $entity->getStartDate(); | ||||
| $dto->endDate = $entity->getEndDate(); | $dto->endDate = $entity->getEndDate(); | ||||
| $dto->note = $entity->getNote(); | $dto->note = $entity->getNote(); | ||||
| @@ -49,7 +49,7 @@ class TripLocationApiToEntityMapper implements MapperInterface | |||||
| throw new \Exception('Location not found'); | throw new \Exception('Location not found'); | ||||
| } | } | ||||
| return new TripLocation($trip, $location, $dto->date); | |||||
| return new TripLocation($trip, $location); | |||||
| } | } | ||||
| public function populate(object $from, object $to, array $context): object | public function populate(object $from, object $to, array $context): object | ||||
| @@ -67,10 +67,8 @@ class TripLocationApiToEntityMapper implements MapperInterface | |||||
| $entity->setLocation($location); | $entity->setLocation($location); | ||||
| } | } | ||||
| $entity->setIsArrival($dto->isArrival); | |||||
| $entity->setIsTransit($dto->isTransit); | |||||
| $entity->setIsDeparture($dto->isDeparture); | |||||
| $entity->setDate($dto->date); | |||||
| $entity->setArrivalDateTime($dto->arrivalDateTime); | |||||
| $entity->setDepartureDateTime($dto->departureDateTime); | |||||
| return $entity; | return $entity; | ||||
| } | } | ||||
| @@ -37,10 +37,8 @@ class TripLocationEntityToApiMapper implements MapperInterface | |||||
| assert($dto instanceof TripLocationApi); | assert($dto instanceof TripLocationApi); | ||||
| $dto->dbId = $entity->getId(); | $dto->dbId = $entity->getId(); | ||||
| $dto->date = $entity->getDate(); | |||||
| $dto->isArrival = $entity->isArrival(); | |||||
| $dto->isTransit = $entity->isTransit(); | |||||
| $dto->isDeparture = $entity->isDeparture(); | |||||
| $dto->arrivalDateTime = $entity->getArrivalDateTime(); | |||||
| $dto->departureDateTime = $entity->getDepartureDateTime(); | |||||
| $dto->createdAt = $entity->getCreatedAt(); | $dto->createdAt = $entity->getCreatedAt(); | ||||
| $dto->tripIri = $dto->trip = $this->microMapper->map($entity->getTrip(), TripApi::class, [ | $dto->tripIri = $dto->trip = $this->microMapper->map($entity->getTrip(), TripApi::class, [ | ||||