ソースを参照

time check location trip entries

master
Daniel 10ヶ月前
コミット
9d6ae8c564
6個のファイルの変更196行の追加69行の削除
  1. +58
    -53
      angular/src/app/_components/datetime-picker/datetime-picker.component.ts
  2. +11
    -2
      angular/src/app/_components/list/list.component.ts
  3. +1
    -1
      angular/src/app/_views/trip/trip-location-form/trip-location-form.component.html
  4. +120
    -8
      angular/src/app/_views/trip/trip-location-form/trip-location-form.component.ts
  5. +3
    -3
      angular/src/app/_views/trip/trip-location-list/trip-location-list.component.ts
  6. +3
    -2
      angular/src/assets/i18n/en.json

+ 58
- 53
angular/src/app/_components/datetime-picker/datetime-picker.component.ts ファイルの表示

@@ -2,66 +2,68 @@ import {Component, EventEmitter, Input, OnInit, Output} from '@angular/core';
import {FormBuilder, FormGroup} from "@angular/forms";

@Component({
selector: 'app-datetime-picker',
templateUrl: './datetime-picker.component.html',
styleUrl: './datetime-picker.component.scss'
selector: 'app-datetime-picker',
templateUrl: './datetime-picker.component.html',
styleUrl: './datetime-picker.component.scss'
})
export class DatetimePickerComponent implements OnInit {
@Input() label: string = 'Date and Time';
@Input() inputId: string = 'myId';
@Input() initialValue: string | null = null;
@Input() readonly: boolean = false;
@Input() showSeconds: boolean = false;
@Output() dateTimeChange = new EventEmitter<string | null>();
@Input() label: string = 'Date and Time';
@Input() inputId: string = 'myId';
@Input() initialValue: string | null = null;
@Input() readonly: boolean = false;
@Input() showSeconds: boolean = false;
@Output() dateTimeChange = new EventEmitter<string | null>();

form: FormGroup;
form: FormGroup;

constructor(private fb: FormBuilder) {
this.form = this.fb.group({
date: [''],
time: ['']
});
}

ngOnInit() {
if (this.initialValue) {
const date = new Date(this.initialValue);
this.form.patchValue({
date: this.formatDate(date),
time: this.formatTime(date)
});
constructor(private fb: FormBuilder) {
this.form = this.fb.group({
date: [''],
time: ['']
});
}

if (this.readonly) {
this.form.disable();
}
ngOnInit() {
if (this.initialValue) {
// Datum als UTC interpretieren
const date = new Date(this.initialValue);
this.form.patchValue({
date: this.formatDate(date),
time: this.formatTime(date)
});
}

this.form.valueChanges.subscribe(() => {
if (!this.readonly) {
this.emitDateTime();
}
});
}
if (this.readonly) {
this.form.disable();
}

private formatDate(date: Date): string {
return date.toLocaleDateString('en-CA');
}
this.form.valueChanges.subscribe(() => {
if (!this.readonly) {
this.emitDateTime();
}
});
}

private formatDate(date: Date): string {
// ISO String verwenden und nur den Datumsteil extrahieren (YYYY-MM-DD)
return date.toISOString().split('T')[0];
}

private formatTime(date: Date): string {
// Zeit aus ISO String extrahieren
const timeString = date.toISOString().split('T')[1];

if (this.showSeconds) {
return date.toLocaleTimeString('en-GB', { hour12: false });
// Rückgabe mit Sekunden (HH:MM:SS)
return timeString.substring(0, 8);
} else {
// Nur Stunden und Minuten zurückgeben
return date.toLocaleTimeString('en-GB', {
hour12: false,
hour: '2-digit',
minute: '2-digit'
});
// Nur Stunden und Minuten zurückgeben (HH:MM)
return timeString.substring(0, 5);
}
}

private emitDateTime() {
const { date, time } = this.form.value;
const {date, time} = this.form.value;
if (date && time) {
const [year, month, day] = date.split('-');

@@ -74,21 +76,24 @@ export class DatetimePickerComponent implements OnInit {
seconds = '00';
}

// Datum in UTC erstellen
const dateTime = new Date(
Number(year),
Number(month) - 1,
Number(day),
Number(hours),
Number(minutes),
Number(seconds || 0)
Date.UTC(
Number(year),
Number(month) - 1,
Number(day),
Number(hours),
Number(minutes),
Number(seconds || 0)
)
);

// Format the date to match the loaded format
const formattedDate = dateTime.toLocaleString('sv-SE', { timeZone: 'Europe/Berlin' }).replace(' ', 'T') + '+02:00';
// Als ISO-String formatieren, der automatisch in UTC ist
const formattedDate = dateTime.toISOString();

this.dateTimeChange.emit(formattedDate);
} else {
this.dateTimeChange.emit(null);
}
}
}
}

+ 11
- 2
angular/src/app/_components/list/list.component.ts ファイルの表示

@@ -405,7 +405,6 @@ export class ListComponent implements OnInit, AfterViewInit, OnDestroy {
}

public onFilterChanged(filterData: {filters: any, activeCount: number}) {
console.log(filterData);
const filterJson = JSON.stringify(filterData.filters);
const currentFilterJson = JSON.stringify(this.filterObj);

@@ -417,6 +416,12 @@ export class ListComponent implements OnInit, AfterViewInit, OnDestroy {
}

public onCreateData() {
// Important: Reset the data property to prevent using previous edit data
if (this.dataFormComponentData) {
this.dataFormComponentData = {...this.dataFormComponentData};
delete this.dataFormComponentData.data;
}

this.appHelperService.openModal(
this.dataFormComponent,
this.dataFormComponentData,
@@ -425,7 +430,11 @@ export class ListComponent implements OnInit, AfterViewInit, OnDestroy {
}

public onEditData(element: any) {
this.dataFormComponentData.data = element;
// Create a new object instead of modifying the existing one
this.dataFormComponentData = {
...this.dataFormComponentData,
data: element
};
this.appHelperService.openModal(
this.dataFormComponent,
this.dataFormComponentData,


+ 1
- 1
angular/src/app/_views/trip/trip-location-form/trip-location-form.component.html ファイルの表示

@@ -5,7 +5,7 @@
</div>
} @else {
<div class="spt-headline d-flex justify-content-between align-items-start">
<h2>{{ ('basic.edit') | translate }} {{ 'model.trip_location' | translate }}: {{ data?.location?.name }}</h2>
<h2>{{ ('basic.edit') | translate }} {{ 'model.trip_location' | translate }}</h2>
</div>
}
<div class="spt-form">


+ 120
- 8
angular/src/app/_views/trip/trip-location-form/trip-location-form.component.ts ファイルの表示

@@ -1,4 +1,4 @@
import {Component, Input} from '@angular/core';
import {Component} from '@angular/core';
import {AbstractDataFormComponent} from "@app/_components/_abstract/abstract-data-form-component";
import {
LocationService, TripJsonld,
@@ -21,11 +21,13 @@ import {SearchSelectComponent} from "@app/_components/search-select/search-selec
})
export class TripLocationFormComponent extends AbstractDataFormComponent<TripLocationJsonld> {

protected trip!: TripJsonld;
protected arrivalDateTime?: string;
protected departureDateTime?: string;
protected readonly tripLocationForm = tripLocationForm;
protected readonly SearchSelectComponent = SearchSelectComponent;
protected locationColDefinitions: ListColDefinition[];
protected trip!: TripJsonld;
protected newArrivalDateTime?: string;
protected newDepartureDateTime?: string;
protected tripLocationEntries: TripLocationJsonld[];

constructor(
private tripLocationService: TripLocationService,
@@ -49,7 +51,7 @@ export class TripLocationFormComponent extends AbstractDataFormComponent<TripLoc
translateService,
router
);
this.tripLocationEntries = [];
this.redirectAfterDelete = '/' + ROUTE_BASE_DATA;
this.locationColDefinitions = SearchSelectComponent.getDefaultColDefLocations();
}
@@ -58,8 +60,11 @@ export class TripLocationFormComponent extends AbstractDataFormComponent<TripLoc
super.ngOnInit();
this.form.get('tripIri')?.setValue(this.trip.id);
if (!this.isEditMode()) {
this.form.get('arrivalDateTime')?.setValue(this.arrivalDateTime);
this.form.get('departureDateTime')?.setValue(this.departureDateTime);
this.form.get('newArrivalDateTime')?.setValue(this.newArrivalDateTime);
this.form.get('newDepartureDateTime')?.setValue(this.newDepartureDateTime);
} else {
this.form.get('newArrivalDateTime')?.setValue(this.data?.arrivalDateTime);
this.form.get('newDepartureDateTime')?.setValue(this.data?.departureDateTime);
}
}

@@ -72,8 +77,115 @@ export class TripLocationFormComponent extends AbstractDataFormComponent<TripLoc
}

override onSubmit() {
const formData = this.form.value as TripLocationJsonld;

if (this.hasDateOverlap(formData)) {
this.translateService?.get('trip_location.overlap_error').subscribe((error: string) => {
alert(error);
});
return;
}

super.onSubmit();
}

protected readonly tripLocationForm = tripLocationForm;
private hasDateOverlap(formData: TripLocationJsonld): boolean {
// Wenn kein Datum gesetzt ist, kann keine Überlappung stattfinden
if (!formData.arrivalDateTime && !formData.departureDateTime) {
return false;
}

// Wenn wir im Edit-Modus sind, entferne den aktuellen Eintrag aus der Überprüfung
const entriesToCheck = this.isEditMode()
? this.tripLocationEntries.filter(entry => entry.id !== this.data?.id)
: this.tripLocationEntries;

for (const entry of entriesToCheck) {
// Fall 1: Nur ein Datum ist gesetzt in formData
if (formData.arrivalDateTime && !formData.departureDateTime) {
// Arrival date darf nicht mit anderen arrival dates übereinstimmen
if (entry.arrivalDateTime === formData.arrivalDateTime) {
return true;
}

// Überprüfe, ob arrival innerhalb eines existierenden Zeitraums liegt
// ABER: arrival darf gleich mit einem departure sein
if (entry.arrivalDateTime && entry.departureDateTime) {
const arrivalDate = new Date(formData.arrivalDateTime);
const entryArrival = new Date(entry.arrivalDateTime);
const entryDeparture = new Date(entry.departureDateTime);

// Liegt innerhalb des Zeitraums, aber ist NICHT gleich dem departure
if (arrivalDate > entryArrival && arrivalDate < entryDeparture) {
return true;
}
}
}
else if (!formData.arrivalDateTime && formData.departureDateTime) {
// Departure date darf nicht mit anderen departure dates übereinstimmen
if (entry.departureDateTime === formData.departureDateTime) {
return true;
}

// Überprüfe, ob departure innerhalb eines existierenden Zeitraums liegt
// ABER: departure darf gleich mit einem arrival sein
if (entry.arrivalDateTime && entry.departureDateTime) {
const departureDate = new Date(formData.departureDateTime);
const entryArrival = new Date(entry.arrivalDateTime);
const entryDeparture = new Date(entry.departureDateTime);

// Liegt innerhalb des Zeitraums, aber ist NICHT gleich dem arrival
if (departureDate > entryArrival && departureDate < entryDeparture) {
return true;
}
}
}
// Fall 2: Beide Daten sind gesetzt in formData, überprüfe auf Überlappung
else if (formData.arrivalDateTime && formData.departureDateTime) {
const formArrival = new Date(formData.arrivalDateTime);
const formDeparture = new Date(formData.departureDateTime);

// Wenn der andere Eintrag beide Daten hat
if (entry.arrivalDateTime && entry.departureDateTime) {
const entryArrival = new Date(entry.arrivalDateTime);
const entryDeparture = new Date(entry.departureDateTime);

// Überprüfung auf Überlappung der Zeiträume, erlaubt grenzen zu teilen
// Formular-Zeitraum darf vor oder nach dem Entry-Zeitraum liegen,
// oder die Grenzen dürfen gleich sein (departure gleich arrival oder umgekehrt)
if (
// Überlappung, aber nicht nur am Rand
(formArrival < entryDeparture && formDeparture > entryArrival) &&
// Erlaubt: formDeparture === entryArrival ODER formArrival === entryDeparture
!(formDeparture.getTime() === entryArrival.getTime() ||
formArrival.getTime() === entryDeparture.getTime())
) {
return true;
}
}
// Wenn der andere Eintrag nur arrival hat
else if (entry.arrivalDateTime && !entry.departureDateTime) {
const entryArrival = new Date(entry.arrivalDateTime);

// Das arrival des anderen Eintrags darf nicht innerhalb des neuen Zeitraums liegen
// ABER: Es darf am Rand liegen (gleich mit formDeparture)
if (entryArrival > formArrival && entryArrival < formDeparture) {
return true;
}
}
// Wenn der andere Eintrag nur departure hat
else if (!entry.arrivalDateTime && entry.departureDateTime) {
const entryDeparture = new Date(entry.departureDateTime);

// Das departure des anderen Eintrags darf nicht innerhalb des neuen Zeitraums liegen
// ABER: Es darf am Rand liegen (gleich mit formArrival)
if (entryDeparture > formArrival && entryDeparture < formDeparture) {
return true;
}
}
}
}

return false;
}
}

+ 3
- 3
angular/src/app/_views/trip/trip-location-list/trip-location-list.component.ts ファイルの表示

@@ -100,7 +100,6 @@ export class TripLocationListComponent {
map(response => {
// Set default arrival and departure time before return
let arrivalDateTime = this.appHelperService.getDateTimeWithoutTimezone();
console.log(arrivalDateTime);
let departureDateTime = this.appHelperService.getDateTimeWithoutTimezone(new Date(new Date().setHours(new Date().getHours() + 1)));
if (response.member && response.member.length > 0) {
const lastEntry = response.member[response.member.length - 1];
@@ -116,8 +115,9 @@ export class TripLocationListComponent {
}
this.dataFormComponentData = {
trip: this.trip,
arrivalDateTime: arrivalDateTime,
departureDateTime: departureDateTime
newArrivalDateTime: arrivalDateTime,
newDepartureDateTime: departureDateTime,
tripLocationEntries: response.member
};

return response;


+ 3
- 2
angular/src/assets/i18n/en.json ファイルの表示

@@ -44,7 +44,7 @@
},
"common": {
"code": "Code",
"created_at": "Created at",
"created_at": "Created at (UTC)",
"date": "Date",
"name": "Name",
"note": "Note",
@@ -107,7 +107,8 @@
},
"trip_location": {
"arrival_date_time": "ETA",
"departure_date_time": "ETD"
"departure_date_time": "ETD",
"overlap_error": "These dates and times overlap with other entries, please check!!"
},
"user_trip": {
"approved": "Approved",


読み込み中…
キャンセル
保存