ソースを参照

user trips images

master
Daniel 1年前
コミット
a3dc2d1e74
38個のファイルの変更619行の追加183行の削除
  1. +1
    -1
      angular/openapi.json
  2. +206
    -12
      angular/openapi.yaml
  3. +1
    -0
      angular/src/app/_components/_abstract/abstract-data-form-component.ts
  4. +28
    -4
      angular/src/app/_forms/apiForms.ts
  5. +29
    -0
      angular/src/app/_helpers/app-helper.service.ts
  6. +4
    -4
      angular/src/app/_views/location/location-form/location-form.component.html
  7. +6
    -1
      angular/src/app/_views/location/location-form/location-form.component.ts
  8. +3
    -3
      angular/src/app/_views/trip/trip-detail/trip-detail.component.html
  9. +7
    -11
      angular/src/app/_views/trip/trip-detail/trip-detail.component.ts
  10. +12
    -12
      angular/src/app/_views/trip/trip-form/trip-form.component.html
  11. +7
    -1
      angular/src/app/_views/trip/trip-form/trip-form.component.ts
  12. +1
    -1
      angular/src/app/_views/user-trip/user-trip-detail/user-trip-detail.component.html
  13. +17
    -7
      angular/src/app/_views/user-trip/user-trip-form/user-trip-form.component.html
  14. +116
    -30
      angular/src/app/_views/user-trip/user-trip-form/user-trip-form.component.ts
  15. +4
    -4
      angular/src/app/_views/vessel/vessel-form/vessel-form.component.html
  16. +7
    -1
      angular/src/app/_views/vessel/vessel-form/vessel-form.component.ts
  17. +2
    -2
      angular/src/app/core/api/v1/model/location.ts
  18. +2
    -1
      angular/src/app/core/api/v1/model/locationJsonld.ts
  19. +6
    -5
      angular/src/app/core/api/v1/model/trip.ts
  20. +6
    -3
      angular/src/app/core/api/v1/model/tripJsonld.ts
  21. +2
    -0
      angular/src/app/core/api/v1/model/tripLocation.ts
  22. +2
    -0
      angular/src/app/core/api/v1/model/tripLocationJsonld.ts
  23. +6
    -6
      angular/src/app/core/api/v1/model/userTrip.ts
  24. +2
    -0
      angular/src/app/core/api/v1/model/userTripEvent.ts
  25. +2
    -0
      angular/src/app/core/api/v1/model/userTripEventJsonld.ts
  26. +6
    -3
      angular/src/app/core/api/v1/model/userTripJsonld.ts
  27. +2
    -2
      angular/src/app/core/api/v1/model/vessel.ts
  28. +2
    -1
      angular/src/app/core/api/v1/model/vesselJsonld.ts
  29. +7
    -2
      httpdocs/src/ApiResource/LocationApi.php
  30. +55
    -42
      httpdocs/src/ApiResource/TripApi.php
  31. +9
    -0
      httpdocs/src/ApiResource/TripLocationApi.php
  32. +18
    -8
      httpdocs/src/ApiResource/UserTripApi.php
  33. +9
    -0
      httpdocs/src/ApiResource/UserTripEventApi.php
  34. +7
    -2
      httpdocs/src/ApiResource/VesselApi.php
  35. +1
    -1
      httpdocs/src/Mapper/LocationEntityToApiMapper.php
  36. +13
    -8
      httpdocs/src/Mapper/UserTripApiToEntityMapper.php
  37. +4
    -4
      httpdocs/src/Mapper/UserTripEntityToApiMapper.php
  38. +7
    -1
      httpdocs/src/State/EntityClassDtoStateProcessor.php

+ 1
- 1
angular/openapi.json
ファイル差分が大きすぎるため省略します
ファイルの表示


+ 206
- 12
angular/openapi.yaml ファイルの表示

@@ -2238,7 +2238,16 @@ components:
- integer
- 'null'
zone:
$ref: '#/components/schemas/Zone'
readOnly: true
type: string
format: iri-reference
example: 'https://example.com/'
zoneIri:
type:
- string
- 'null'
format: iri-reference
example: 'https://example.com/'
name:
type: string
code:
@@ -2256,6 +2265,7 @@ components:
- 'null'
format: date-time
required:
- zoneIri
- name
- code
Location.jsonld:
@@ -2292,7 +2302,14 @@ components:
- integer
- 'null'
zone:
readOnly: true
$ref: '#/components/schemas/Zone.jsonld'
zoneIri:
type:
- string
- 'null'
format: iri-reference
example: 'https://example.com/'
name:
type: string
code:
@@ -2310,6 +2327,7 @@ components:
- 'null'
format: date-time
required:
- zoneIri
- name
- code
MediaObject:
@@ -2467,7 +2485,16 @@ components:
- integer
- 'null'
vessel:
$ref: '#/components/schemas/Vessel'
readOnly: true
type: string
format: iri-reference
example: 'https://example.com/'
vesselIri:
type:
- string
- 'null'
format: iri-reference
example: 'https://example.com/'
pilotageReference:
readOnly: true
type:
@@ -2482,9 +2509,27 @@ components:
- string
- 'null'
startLocation:
$ref: '#/components/schemas/Location'
readOnly: true
type: string
format: iri-reference
example: 'https://example.com/'
startLocationIri:
type:
- string
- 'null'
format: iri-reference
example: 'https://example.com/'
endLocation:
$ref: '#/components/schemas/Location'
readOnly: true
type: string
format: iri-reference
example: 'https://example.com/'
endLocationIri:
type:
- string
- 'null'
format: iri-reference
example: 'https://example.com/'
startDate:
type: string
format: date-time
@@ -2502,6 +2547,9 @@ components:
- 'null'
format: date-time
required:
- vesselIri
- startLocationIri
- endLocationIri
- startDate
- endDate
Trip.jsonld:
@@ -2538,7 +2586,14 @@ components:
- integer
- 'null'
vessel:
readOnly: true
$ref: '#/components/schemas/Vessel.jsonld'
vesselIri:
type:
- string
- 'null'
format: iri-reference
example: 'https://example.com/'
pilotageReference:
readOnly: true
type:
@@ -2553,9 +2608,23 @@ components:
- string
- 'null'
startLocation:
readOnly: true
$ref: '#/components/schemas/Location.jsonld'
startLocationIri:
type:
- string
- 'null'
format: iri-reference
example: 'https://example.com/'
endLocation:
readOnly: true
$ref: '#/components/schemas/Location.jsonld'
endLocationIri:
type:
- string
- 'null'
format: iri-reference
example: 'https://example.com/'
startDate:
type: string
format: date-time
@@ -2573,6 +2642,9 @@ components:
- 'null'
format: date-time
required:
- vesselIri
- startLocationIri
- endLocationIri
- startDate
- endDate
TripLocation:
@@ -2587,8 +2659,20 @@ components:
- 'null'
trip:
$ref: '#/components/schemas/Trip'
tripIri:
type:
- string
- 'null'
format: iri-reference
example: 'https://example.com/'
location:
$ref: '#/components/schemas/Location'
locationIri:
type:
- string
- 'null'
format: iri-reference
example: 'https://example.com/'
isArrival:
type: boolean
date:
@@ -2601,6 +2685,8 @@ components:
- 'null'
format: date-time
required:
- tripIri
- locationIri
- date
TripLocation.jsonld:
type: object
@@ -2637,8 +2723,20 @@ components:
- 'null'
trip:
$ref: '#/components/schemas/Trip.jsonld'
tripIri:
type:
- string
- 'null'
format: iri-reference
example: 'https://example.com/'
location:
$ref: '#/components/schemas/Location.jsonld'
locationIri:
type:
- string
- 'null'
format: iri-reference
example: 'https://example.com/'
isArrival:
type: boolean
date:
@@ -2651,6 +2749,8 @@ components:
- 'null'
format: date-time
required:
- tripIri
- locationIri
- date
User:
type: object
@@ -2804,9 +2904,27 @@ components:
- integer
- 'null'
trip:
$ref: '#/components/schemas/Trip'
readOnly: true
type: string
format: iri-reference
example: 'https://example.com/'
tripIri:
type:
- string
- 'null'
format: iri-reference
example: 'https://example.com/'
user:
$ref: '#/components/schemas/User'
readOnly: true
type: string
format: iri-reference
example: 'https://example.com/'
userIri:
type:
- string
- 'null'
format: iri-reference
example: 'https://example.com/'
captainName:
type:
- string
@@ -2814,7 +2932,16 @@ components:
completed:
type: boolean
signature:
$ref: '#/components/schemas/MediaObject'
readOnly: true
type: string
format: iri-reference
example: 'https://example.com/'
signatureIri:
type:
- string
- 'null'
format: iri-reference
example: 'https://example.com/'
signatureUrl:
readOnly: true
type:
@@ -2832,8 +2959,8 @@ components:
- 'null'
format: date-time
required:
- trip
- user
- tripIri
- userIri
- completed
UserTrip.jsonld:
type: object
@@ -2869,9 +2996,23 @@ components:
- integer
- 'null'
trip:
readOnly: true
$ref: '#/components/schemas/Trip.jsonld'
tripIri:
type:
- string
- 'null'
format: iri-reference
example: 'https://example.com/'
user:
readOnly: true
$ref: '#/components/schemas/User.jsonld'
userIri:
type:
- string
- 'null'
format: iri-reference
example: 'https://example.com/'
captainName:
type:
- string
@@ -2879,7 +3020,14 @@ components:
completed:
type: boolean
signature:
readOnly: true
$ref: '#/components/schemas/MediaObject.jsonld'
signatureIri:
type:
- string
- 'null'
format: iri-reference
example: 'https://example.com/'
signatureUrl:
readOnly: true
type:
@@ -2897,8 +3045,8 @@ components:
- 'null'
format: date-time
required:
- trip
- user
- tripIri
- userIri
- completed
UserTripEvent:
type: object
@@ -2913,11 +3061,23 @@ components:
userTrip:
readOnly: true
$ref: '#/components/schemas/UserTrip'
userTripIri:
type:
- string
- 'null'
format: iri-reference
example: 'https://example.com/'
event:
readOnly: true
type: string
format: iri-reference
example: 'https://example.com/'
eventIri:
type:
- string
- 'null'
format: iri-reference
example: 'https://example.com/'
date:
type: string
format: date-time
@@ -2932,6 +3092,8 @@ components:
- 'null'
format: date-time
required:
- userTripIri
- eventIri
- date
UserTripEvent.jsonld:
type: object
@@ -2969,9 +3131,21 @@ components:
userTrip:
readOnly: true
$ref: '#/components/schemas/UserTrip.jsonld'
userTripIri:
type:
- string
- 'null'
format: iri-reference
example: 'https://example.com/'
event:
readOnly: true
$ref: '#/components/schemas/Event.jsonld'
eventIri:
type:
- string
- 'null'
format: iri-reference
example: 'https://example.com/'
date:
type: string
format: date-time
@@ -2986,6 +3160,8 @@ components:
- 'null'
format: date-time
required:
- userTripIri
- eventIri
- date
Vessel:
type: object
@@ -3002,7 +3178,16 @@ components:
code:
type: string
company:
$ref: '#/components/schemas/ShippingCompany'
readOnly: true
type: string
format: iri-reference
example: 'https://example.com/'
companyIri:
type:
- string
- 'null'
format: iri-reference
example: 'https://example.com/'
createdAt:
readOnly: true
type:
@@ -3012,6 +3197,7 @@ components:
required:
- name
- code
- companyIri
Vessel.jsonld:
type: object
description: ''
@@ -3050,7 +3236,14 @@ components:
code:
type: string
company:
readOnly: true
$ref: '#/components/schemas/ShippingCompany.jsonld'
companyIri:
type:
- string
- 'null'
format: iri-reference
example: 'https://example.com/'
createdAt:
readOnly: true
type:
@@ -3060,6 +3253,7 @@ components:
required:
- name
- code
- companyIri
Zone:
type: object
description: ''


+ 1
- 0
angular/src/app/_components/_abstract/abstract-data-form-component.ts ファイルの表示

@@ -64,6 +64,7 @@ export abstract class AbstractDataFormComponent<T extends { [key: string]: any }
if (request$ !== undefined) {
request$.subscribe({
next: (response) => {
this.data = response;
this.submit.emit({
status: ModalStatus.Submitted,
data: response


+ 28
- 4
angular/src/app/_forms/apiForms.ts ファイルの表示

@@ -12,6 +12,7 @@ export const eventJsonldForm = new FormGroup({
export const locationForm = new FormGroup({
dbId: new FormControl(null, []),
zone: new FormControl(null, []),
zoneIri: new FormControl(null, [Validators.required]),
name: new FormControl(null, [Validators.required]),
code: new FormControl(null, [Validators.required]),
isZone: new FormControl(null, []),
@@ -23,6 +24,7 @@ export const locationForm = new FormGroup({
export const locationJsonldForm = new FormGroup({
dbId: new FormControl(null, []),
zone: new FormControl(null, []),
zoneIri: new FormControl(null, [Validators.required]),
name: new FormControl(null, [Validators.required]),
code: new FormControl(null, [Validators.required]),
isZone: new FormControl(null, []),
@@ -63,11 +65,14 @@ export const shippingCompanyJsonldForm = new FormGroup({
export const tripForm = new FormGroup({
dbId: new FormControl(null, []),
vessel: new FormControl(null, []),
vesselIri: new FormControl(null, [Validators.required]),
pilotageReference: new FormControl(null, []),
customerReference: new FormControl(null, []),
captainName: new FormControl(null, []),
startLocation: new FormControl(null, []),
startLocationIri: new FormControl(null, [Validators.required]),
endLocation: new FormControl(null, []),
endLocationIri: new FormControl(null, [Validators.required]),
startDate: new FormControl(null, [Validators.required]),
endDate: new FormControl(null, [Validators.required]),
note: new FormControl(null, []),
@@ -77,11 +82,14 @@ export const tripForm = new FormGroup({
export const tripJsonldForm = new FormGroup({
dbId: new FormControl(null, []),
vessel: new FormControl(null, []),
vesselIri: new FormControl(null, [Validators.required]),
pilotageReference: new FormControl(null, []),
customerReference: new FormControl(null, []),
captainName: new FormControl(null, []),
startLocation: new FormControl(null, []),
startLocationIri: new FormControl(null, [Validators.required]),
endLocation: new FormControl(null, []),
endLocationIri: new FormControl(null, [Validators.required]),
startDate: new FormControl(null, [Validators.required]),
endDate: new FormControl(null, [Validators.required]),
note: new FormControl(null, []),
@@ -91,7 +99,9 @@ export const tripJsonldForm = new FormGroup({
export const tripLocationForm = new FormGroup({
dbId: new FormControl(null, []),
trip: new FormControl(null, []),
tripIri: new FormControl(null, [Validators.required]),
location: new FormControl(null, []),
locationIri: new FormControl(null, [Validators.required]),
isArrival: new FormControl(null, []),
date: new FormControl(null, [Validators.required]),
createdAt: new FormControl(null, [])
@@ -100,7 +110,9 @@ export const tripLocationForm = new FormGroup({
export const tripLocationJsonldForm = new FormGroup({
dbId: new FormControl(null, []),
trip: new FormControl(null, []),
tripIri: new FormControl(null, [Validators.required]),
location: new FormControl(null, []),
locationIri: new FormControl(null, [Validators.required]),
isArrival: new FormControl(null, []),
date: new FormControl(null, [Validators.required]),
createdAt: new FormControl(null, [])
@@ -138,11 +150,14 @@ export const userJsonldForm = new FormGroup({

export const userTripForm = new FormGroup({
dbId: new FormControl(null, []),
trip: new FormControl(null, [Validators.required]),
user: new FormControl(null, [Validators.required]),
trip: new FormControl(null, []),
tripIri: new FormControl(null, [Validators.required]),
user: new FormControl(null, []),
userIri: new FormControl(null, [Validators.required]),
captainName: new FormControl(null, []),
completed: new FormControl(null, [Validators.required]),
signature: new FormControl(null, []),
signatureIri: new FormControl(null, []),
signatureUrl: new FormControl(null, []),
completedDate: new FormControl(null, []),
createdAt: new FormControl(null, [])
@@ -150,11 +165,14 @@ export const userTripForm = new FormGroup({

export const userTripJsonldForm = new FormGroup({
dbId: new FormControl(null, []),
trip: new FormControl(null, [Validators.required]),
user: new FormControl(null, [Validators.required]),
trip: new FormControl(null, []),
tripIri: new FormControl(null, [Validators.required]),
user: new FormControl(null, []),
userIri: new FormControl(null, [Validators.required]),
captainName: new FormControl(null, []),
completed: new FormControl(null, [Validators.required]),
signature: new FormControl(null, []),
signatureIri: new FormControl(null, []),
signatureUrl: new FormControl(null, []),
completedDate: new FormControl(null, []),
createdAt: new FormControl(null, [])
@@ -163,7 +181,9 @@ export const userTripJsonldForm = new FormGroup({
export const userTripEventForm = new FormGroup({
dbId: new FormControl(null, []),
userTrip: new FormControl(null, []),
userTripIri: new FormControl(null, [Validators.required]),
event: new FormControl(null, []),
eventIri: new FormControl(null, [Validators.required]),
date: new FormControl(null, [Validators.required]),
note: new FormControl(null, []),
createdAt: new FormControl(null, [])
@@ -172,7 +192,9 @@ export const userTripEventForm = new FormGroup({
export const userTripEventJsonldForm = new FormGroup({
dbId: new FormControl(null, []),
userTrip: new FormControl(null, []),
userTripIri: new FormControl(null, [Validators.required]),
event: new FormControl(null, []),
eventIri: new FormControl(null, [Validators.required]),
date: new FormControl(null, [Validators.required]),
note: new FormControl(null, []),
createdAt: new FormControl(null, [])
@@ -183,6 +205,7 @@ export const vesselForm = new FormGroup({
name: new FormControl(null, [Validators.required]),
code: new FormControl(null, [Validators.required]),
company: new FormControl(null, []),
companyIri: new FormControl(null, [Validators.required]),
createdAt: new FormControl(null, [])
});

@@ -191,6 +214,7 @@ export const vesselJsonldForm = new FormGroup({
name: new FormControl(null, [Validators.required]),
code: new FormControl(null, [Validators.required]),
company: new FormControl(null, []),
companyIri: new FormControl(null, [Validators.required]),
createdAt: new FormControl(null, [])
});



+ 29
- 0
angular/src/app/_helpers/app-helper.service.ts ファイルの表示

@@ -84,4 +84,33 @@ export class AppHelperService {
return JSON.stringify(obj, null, 2); // Das `null, 2` formatiert das JSON mit Einrückungen
}

public convertJsonldToJson<T extends object>(jsonldObj: any): T {

// Erstelle ein neues Objekt ohne die JSON-LD spezifischen Eigenschaften
const result: any = {};

// Kopiere alle Eigenschaften außer context, id und type
for (const key in jsonldObj) {
if (
jsonldObj.hasOwnProperty(key) &&
key !== 'context' &&
key !== 'id' &&
key !== 'type'
) {
// Wenn der Wert ein Objekt ist und JSON-LD Metadaten enthält, konvertiere es rekursiv
if (
typeof jsonldObj[key] === 'object' &&
jsonldObj[key] !== null &&
(jsonldObj[key].context || jsonldObj[key].id || jsonldObj[key].type)
) {
result[key] = this.convertJsonldToJson(jsonldObj[key]);
} else {
result[key] = jsonldObj[key];
}
}
}

return result as T;
}

}

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

@@ -13,18 +13,18 @@
<input type="text" class="form-control" id="code" formControlName="code" required/>
</div>
<div class="mb-3">
<label for="zone" class="form-label">{{ 'model.zone' | translate }}:</label>
<label for="zoneIri" class="form-label">{{ 'model.zone' | translate }}:</label>
<app-search-select #zoneSearchSelect
[formId]="'zone'"
[formId]="'zoneIri'"
[formLabelLangKey]="'model.zone'"
[documentForm]="form"
[getDataFunction]="getZones"
[displayedDataField]="'name'"
[listColDefinitions]="zoneColDefinitions"
[dataSet]="data?.zone"
[dataSet]="data?.zoneIri"
>
</app-search-select>
<input id="zone" type="hidden" formControlName="zone"/>
<input id="zoneIri" type="hidden" formControlName="zoneIri"/>
</div>

<div class="col-12 col-sm-6 col-md-4 col-lg-3 mb-3 switch-widget">


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

@@ -8,6 +8,7 @@ import { ListGetDataFunctionType } from "@app/_components/list/list-get-data-fun
import { TranslateService } from "@ngx-translate/core";
import { Router } from "@angular/router";
import { ROUTE_BASE_DATA } from "@app/app-routing.module";
import {AppHelperService} from "@app/_helpers/app-helper.service";

@Component({
selector: 'app-location-form',
@@ -21,13 +22,17 @@ export class LocationFormComponent extends AbstractDataFormComponent<LocationJso
constructor(
private locationService: LocationService,
private zoneService: ZoneService,
private appHelperService: AppHelperService,
translateService: TranslateService,
router: Router
) {
super(
locationForm,
(data: LocationJsonld) => this.locationService.locationsPost(data),
(id: string | number, data: LocationJsonld) => this.locationService.locationsIdPatch(id.toString(), data),
(id: string | number, data: LocationJsonld) => this.locationService.locationsIdPatch(
id.toString(),
this.appHelperService.convertJsonldToJson(data)
),
(id: string | number) => this.locationService.locationsIdDelete(id.toString()),
translateService,
router


+ 3
- 3
angular/src/app/_views/trip/trip-detail/trip-detail.component.html ファイルの表示

@@ -22,13 +22,13 @@
<div class="col-12 col-md-4 mb-3">
<label [for]="'location_' + i" class="form-label">Location*:</label>
<app-search-select
[formId]="'location'"
[formId]="'locationIri'"
[formLabelLangKey]="'model.location'"
[documentForm]="locationForms[i]"
[getDataFunction]="getLocations"
[displayedDataField]="'name'"
[listColDefinitions]="locationColDefinitions"
[dataSet]="tripLocation.location"
[dataSet]="tripLocation.locationIri"
>
</app-search-select>
</div>
@@ -104,7 +104,7 @@
[getDataFunction]="getUsers"
[displayedDataField]="'fullName'"
[listColDefinitions]="userColDefinitions"
[dataSet]="userTrip.user"
[dataSet]="userTrip.userIri"
(change)="onUserSelectChange(i)"
>
</app-search-select>


+ 7
- 11
angular/src/app/_views/trip/trip-detail/trip-detail.component.ts ファイルの表示

@@ -111,7 +111,7 @@ export class TripDetailComponent implements OnInit, AfterViewInit {
// IDs aus bestehenden UserTrips (mit gültiger ID)
this.userTrips
.filter(ut => ut.user && ut.user.id)
.forEach(ut => assignedUserIds.push(this.appHelperService.extractId(ut.user.id!)));
.forEach(ut => assignedUserIds.push(this.appHelperService.extractId(ut.user?.id!)));

// IDs aus aktuellen Formularwerten (auch für noch nicht gespeicherte Einträge)
this.userForms.forEach(form => {
@@ -120,7 +120,6 @@ export class TripDetailComponent implements OnInit, AfterViewInit {
assignedUserIds.push(this.appHelperService.extractId(userValue));
}
});
console.log(assignedUserIds);

// Filtere Benutzer, die bereits ausgewählt sind (gespeichert oder nicht)
return this.userService.usersGetCollection(page, pageSize, undefined, term).pipe(
@@ -191,11 +190,10 @@ export class TripDetailComponent implements OnInit, AfterViewInit {
addNewTripLocation() {
// Create a new empty trip location
const newTripLocation: TripLocationJsonld = {
trip: {
'id': this.trip.id // Ensure we send the trip ID in the correct format
} as TripJsonld,
tripIri: this.trip.id!,
date: new Date().toISOString(),
isArrival: false
isArrival: false,
locationIri: null
};

this.tripLocations.push(newTripLocation);
@@ -321,7 +319,7 @@ export class TripDetailComponent implements OnInit, AfterViewInit {
// Update existing
return firstValueFrom(this.tripLocationService.tripLocationsIdPatch(
this.appHelperService.extractId(tripLocation.id),
tripLocation
this.appHelperService.convertJsonldToJson(tripLocation)
));
} else {
// Create new
@@ -348,9 +346,7 @@ export class TripDetailComponent implements OnInit, AfterViewInit {
if (userFormValue) {
// If just an ID was set, create a proper user object
if (typeof userFormValue === 'string') {
userTrip.user = {
'id': userFormValue
} as UserJsonld;
userTrip.userIri = userFormValue;
}
}
});
@@ -370,7 +366,7 @@ export class TripDetailComponent implements OnInit, AfterViewInit {
// Update existing
return firstValueFrom(this.userTripService.userTripsIdPatch(
this.appHelperService.extractId(userTrip.id),
userTrip
this.appHelperService.convertJsonldToJson(userTrip)
));
} else {
// Create new


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

@@ -7,46 +7,46 @@
<div class="spt-form">
<form [formGroup]="tripForm" (ngSubmit)="onSubmit()">
<div class="mb-3">
<label for="vessel" class="form-label">{{ 'trip.vessel' | translate }}*:</label>
<label for="vesselIri" class="form-label">{{ 'trip.vessel' | translate }}*:</label>
<app-search-select #vesselSearchSelect
[formId]="'vessel'"
[formId]="'vesselIri'"
[formLabelLangKey]="'model.vessel'"
[documentForm]="form"
[getDataFunction]="getVessels"
[displayedDataField]="'name'"
[listColDefinitions]="vesselColDefinitions"
[dataSet]="data?.vessel"
[dataSet]="data?.vesselIri"
>
</app-search-select>
<input id="vessel" type="hidden" formControlName="vessel" required/>
<input id="vesselIri" type="hidden" formControlName="vesselIri" required/>
</div>
<div class="mb-3">
<label for="startLocation" class="form-label">{{ 'trip.start_location' | translate }}*:</label>
<label for="startLocationIri" class="form-label">{{ 'trip.start_location' | translate }}*:</label>
<app-search-select #startLocationSearchSelect
[formId]="'startLocation'"
[formId]="'startLocationIri'"
[formLabelLangKey]="'model.location'"
[documentForm]="form"
[getDataFunction]="getLocations"
[displayedDataField]="'name'"
[listColDefinitions]="locationColDefinitions"
[dataSet]="data?.startLocation"
[dataSet]="data?.startLocationIri"
>
</app-search-select>
<input id="startLocation" type="hidden" formControlName="startLocation" required/>
<input id="startLocationIri" type="hidden" formControlName="startLocationIri" required/>
</div>
<div class="mb-3">
<label for="endLocation" class="form-label">{{ 'trip.end_location' | translate }}*:</label>
<label for="endLocationIri" class="form-label">{{ 'trip.end_location' | translate }}*:</label>
<app-search-select #endLocationSearchSelect
[formId]="'endLocation'"
[formId]="'endLocationIri'"
[formLabelLangKey]="'model.location'"
[documentForm]="form"
[getDataFunction]="getLocations"
[displayedDataField]="'name'"
[listColDefinitions]="locationColDefinitions"
[dataSet]="data?.endLocation"
[dataSet]="data?.endLocationIri"
>
</app-search-select>
<input id="endLocation" type="hidden" formControlName="endLocation" required/>
<input id="endLocationIri" type="hidden" formControlName="endLocationIri" required/>
</div>

<div class="mb-3">


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

@@ -15,6 +15,7 @@ import {Router} from "@angular/router";
import {ROUTE_BASE_DATA} from "@app/app-routing.module";
import {ListColDefinition} from "@app/_components/list/list-col-definition";
import {ListGetDataFunctionType} from "@app/_components/list/list-get-data-function-type";
import {AppHelperService} from "@app/_helpers/app-helper.service";

@Component({
selector: 'app-trip-form',
@@ -32,13 +33,18 @@ export class TripFormComponent extends AbstractDataFormComponent<TripJsonld> {
private tripService: TripService,
private vesselService: VesselService,
private locationService: LocationService,
private appHelperService: AppHelperService,
translateService: TranslateService,
router: Router
) {
super(
tripForm,
(data: TripJsonld) => this.tripService.tripsPost(data),
(id: string | number, data: TripJsonld) => this.tripService.tripsIdPatch(id.toString(), data),
(id: string | number, data: TripJsonld) =>
this.tripService.tripsIdPatch(
id.toString(),
this.appHelperService.convertJsonldToJson(data)
),
(id: string | number) => this.tripService.tripsIdDelete(id.toString()),
translateService,
router


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

@@ -2,7 +2,7 @@
<div class="spt-container">
<div class="spt-headline d-flex justify-content-between align-items-start">
<h2>{{ ('basic.edit') | translate }} {{ 'model.user_trip' | translate }}
{{ userTrip.trip.pilotageReference }} ({{ userTrip.user.fullName }})</h2>
{{ userTrip.trip?.pilotageReference }} ({{ userTrip.user?.fullName }})</h2>
</div>
</div>
<mat-tab-group>


+ 17
- 7
angular/src/app/_views/user-trip/user-trip-form/user-trip-form.component.html ファイルの表示

@@ -3,9 +3,9 @@
@if (data !== undefined) {
<form [formGroup]="userTripForm" (ngSubmit)="onSubmit()">

<input type="hidden" formControlName="trip" />
<input type="hidden" formControlName="user" />
<input type="hidden" formControlName="signature" />
<input type="hidden" formControlName="tripIri" />
<input type="hidden" formControlName="userIri" />
<input type="hidden" formControlName="signatureIri" />
<div class="mb-3">
<label for="captainName" class="form-label">{{ 'trip.captain_name' | translate }}:</label>
<input type="text" class="form-control" id="captainName" formControlName="captainName"/>
@@ -21,13 +21,22 @@
<!-- Neues File-Upload-Feld für MediaObject -->
<div class="mb-3">
<label for="mediaFile" class="form-label">{{ 'user_trip.signature' | translate }}:</label>
<input type="file" class="form-control" id="mediaFile" (change)="onFileSelected($event)"/>

<!-- File-Input ist deaktiviert, wenn ein Bild existiert und nicht zum Löschen markiert ist -->
<input type="file" class="form-control" id="mediaFile"
[disabled]="data.signatureUrl && showSignatureImage"
(change)="onFileSelected($event)"/>

@if (selectedFile) {
<small class="text-muted">{{ selectedFile.name }}</small>
}
@if (data.signatureUrl) {
<div class="mt-1">

@if (data.signatureUrl && showSignatureImage) {
<div class="mt-1 d-flex align-items-start gap-2">
<img [src]="data.signatureUrl" alt="Signatur" class="img-fluid" />
<button type="button" class="btn btn-sm btn-danger" (click)="markSignatureForRemoval()">
{{ 'basic.remove' | translate }}
</button>
</div>
}
</div>
@@ -37,7 +46,8 @@
</div>

<div class="flex gap-2">
<button type="submit" class="btn btn-primary" [disabled]="form.invalid" (click)="onSubmit()">
<!-- <button type="submit" class="btn btn-primary" [disabled]="form.invalid" (click)="onSubmit()">-->
<button type="submit" class="btn btn-primary" (click)="onSubmit()">
{{ 'basic.save' | translate }}
</button>



+ 116
- 30
angular/src/app/_views/user-trip/user-trip-form/user-trip-form.component.ts ファイルの表示

@@ -1,21 +1,18 @@
import { Component } from '@angular/core';
import {
AbstractDataFormComponent,
FormMode,
FormSubmitEvent
AbstractDataFormComponent, FormSubmitEvent,
} from "@app/_components/_abstract/abstract-data-form-component";
import {
LocationService, MediaObjectJsonld, MediaObjectService, TripJsonld, UserTrip,
MediaObjectService,
UserTripJsonld,
UserTripService,
VesselService
} from "@app/core/api/v1";
import { tripForm, userTripForm } from "@app/_forms/apiForms";
import { userTripForm } from "@app/_forms/apiForms";
import { TranslateService } from "@ngx-translate/core";
import { Router } from "@angular/router";
import { ROUTE_USER_TRIPS } from "@app/app-routing.module";
import { ModalStatus } from "@app/_helpers/modal.states";
import { firstValueFrom } from 'rxjs';
import {AppHelperService} from "@app/_helpers/app-helper.service";

@Component({
selector: 'app-user-trip-form',
@@ -26,17 +23,24 @@ export class UserTripFormComponent extends AbstractDataFormComponent<UserTripJso

protected readonly userTripForm = userTripForm;
selectedFile: File | null = null;
signatureToDelete: string | null = null;
showSignatureImage: boolean = true;

constructor(
private userTripService: UserTripService,
private mediaObjectService: MediaObjectService,
private appHelperService: AppHelperService,
translateService: TranslateService,
router: Router
) {
super(
userTripForm,
undefined,
(id: string | number, data: UserTripJsonld) => this.userTripService.userTripsIdPatch(id.toString(), data),
(id: string | number, data: UserTripJsonld) =>
this.userTripService.userTripsIdPatch(
id.toString(),
this.appHelperService.convertJsonldToJson(data)
),
(id: string | number) => this.userTripService.userTripsIdDelete(id.toString()),
translateService,
router
@@ -47,18 +51,24 @@ export class UserTripFormComponent extends AbstractDataFormComponent<UserTripJso
override ngOnInit(): void {
super.ngOnInit();

// Debug-Ausgabe für Formularvalidierung
this.form.statusChanges.subscribe(status => {
console.log('Form status:', status);
console.log('Form errors:', this.form.errors);

Object.keys(this.form.controls).forEach(key => {
const control = this.form.get(key);
if (control?.invalid) {
console.log(`Control '${key}' is invalid:`, control.errors);
}
});
this.submit.subscribe((event: FormSubmitEvent<UserTripJsonld>) => {
if (event.status === ModalStatus.Submitted) {
this.updateImageStatus();
}
});

// Debug-Ausgabe für Formularvalidierung
// this.form.statusChanges.subscribe(status => {
// console.log('Form status:', status);
// console.log('Form errors:', this.form.errors);
//
// Object.keys(this.form.controls).forEach(key => {
// const control = this.form.get(key);
// if (control?.invalid) {
// console.log(`Control '${key}' is invalid:`, control.errors);
// }
// });
// });
}

onFileSelected(event: Event): void {
@@ -73,29 +83,105 @@ export class UserTripFormComponent extends AbstractDataFormComponent<UserTripJso
return url.split('/').pop() || '';
}

markSignatureForRemoval(): void {
// Speichere die aktuelle signatureIri, um sie später zu löschen
this.signatureToDelete = this.form.get('signatureIri')?.value;

// Aktualisiere den Formularwert
this.form.patchValue({
signatureIri: null
});

// Verstecke das Bild in der UI
this.showSignatureImage = false;

// Falls eine Datei zum Hochladen ausgewählt wurde, entferne diese auch
this.selectedFile = null;
}

updateImageStatus(): void {
// Wenn das Formular erfolgreich gespeichert wurde, wird diese Methode aufgerufen

// Prüfen, ob es ein Bild gibt oder nicht
if (this.data?.signatureUrl) {
// Es gibt ein Bild, also zeige es an
this.showSignatureImage = true;
} else {
// Es gibt kein Bild, also verstecke es
this.showSignatureImage = false;
}

// Zurücksetzen der temporären Variablen
this.signatureToDelete = null;
this.selectedFile = null;
}

override onSubmit(): void {
if (!this.form.valid) {
// console.log('Form is invalid:', this.form.errors);
// Object.keys(this.form.controls).forEach(key => {
// const control = this.form.get(key);
// if (control?.invalid) {
// console.log(`Control '${key}' is invalid:`, control.errors);
// }
// });
return;
}

if (!this.selectedFile) {
const currentValue = this.form.get('signature')?.value;
if (currentValue === null) {
this.form.patchValue({
signature: undefined
// Drei Fälle:
// 1. Ein neues Bild wurde ausgewählt
// 2. Ein bestehendes Bild soll gelöscht werden
// 3. Keine Änderungen am Bild

if (this.selectedFile && this.signatureToDelete) {
// Fall 1a: Ein neues Bild wurde ausgewählt UND ein altes soll gelöscht werden
// Zuerst das alte Bild löschen, dann das neue hochladen
if (this.data?.signature?.dbId) {
this.mediaObjectService.mediaObjectsIdDelete(this.data?.signature?.dbId?.toString()).subscribe({
next: () => {
// Altes Bild wurde gelöscht, jetzt neues hochladen
this.uploadNewFile();
},
error: (error) => {
console.error('Error deleting signature:', error);
// Trotz Fehler neues Bild hochladen
this.uploadNewFile();
}
});
}

} else if (this.selectedFile) {
// Fall 1b: Nur ein neues Bild wurde ausgewählt (kein altes vorhanden)
this.uploadNewFile();
} else if (this.signatureToDelete && this.data?.signature?.dbId) {
// Fall 2: Nur ein bestehendes Bild soll gelöscht werden
this.mediaObjectService.mediaObjectsIdDelete(this.data?.signature?.dbId?.toString()).subscribe({
next: () => {
this.signatureToDelete = null;
super.onSubmit();
},
error: (error) => {
console.error('Error deleting signature:', error);
super.onSubmit();
}
});
} else {
// Fall 3: Keine Änderungen am Bild
super.onSubmit();
return;
}
}

private uploadNewFile(): void {
if (!this.selectedFile) return;

// 1. Handle file upload if a file is selected
this.mediaObjectService.mediaObjectsPost(this.selectedFile).subscribe({
next: (mediaObject) => {
// 2. Update the form data with the new mediaObject
console.log(mediaObject.id);
// Aktualisiere die Formulardaten mit dem neuen mediaObject
this.form.patchValue({
signature: mediaObject.id
signatureIri: mediaObject.id
});

// 3. Call the parent method to handle the standard save process
// Rufe die übergeordnete Methode auf, um den Standard-Speicherprozess zu verarbeiten
super.onSubmit();
},
error: (error) => {


+ 4
- 4
angular/src/app/_views/vessel/vessel-form/vessel-form.component.html ファイルの表示

@@ -23,17 +23,17 @@
</div>

<div class="mb-3">
<label for="company" class="form-label">{{ 'model.shipping_company' | translate }}:</label>
<label for="companyIri" class="form-label">{{ 'model.shipping_company' | translate }}:</label>
<app-search-select #shippingCompanySearchSelect
[formId]="'company'"
[formId]="'companyIri'"
[formLabelLangKey]="'model.shipping_company'"
[documentForm]="form"
[getDataFunction]="getShippingCompanies"
[displayedDataField]="'name'"
[listColDefinitions]="shippingCompanyColDefinitions"
[dataSet]="data?.company">
[dataSet]="data?.companyIri">
</app-search-select>
<input id="company" type="hidden" formControlName="company"/>
<input id="companyIri" type="hidden" formControlName="companyIri"/>
</div>

<div class="flex gap-2">


+ 7
- 1
angular/src/app/_views/vessel/vessel-form/vessel-form.component.ts ファイルの表示

@@ -8,6 +8,7 @@ import { AbstractDataFormComponent } from "@app/_components/_abstract/abstract-d
import { TranslateService } from "@ngx-translate/core";
import { Router } from "@angular/router";
import { ROUTE_BASE_DATA } from "@app/app-routing.module";
import {AppHelperService} from "@app/_helpers/app-helper.service";

@Component({
selector: 'app-vessel-form',
@@ -20,13 +21,18 @@ export class VesselFormComponent extends AbstractDataFormComponent<VesselJsonld>
constructor(
private vesselService: VesselService,
private shippingCompanyService: ShippingCompanyService,
private appHelperService: AppHelperService,
translateService: TranslateService,
router: Router
) {
super(
vesselForm,
(data: VesselJsonld) => this.vesselService.vesselsPost(data),
(id: string | number, data: VesselJsonld) => this.vesselService.vesselsIdPatch(id.toString(), data),
(id: string | number, data: VesselJsonld) =>
this.vesselService.vesselsIdPatch(
id.toString(),
this.appHelperService.convertJsonldToJson(data)
),
(id: string | number) => this.vesselService.vesselsIdDelete(id.toString()),
translateService,
router


+ 2
- 2
angular/src/app/core/api/v1/model/location.ts ファイルの表示

@@ -9,7 +9,6 @@
* https://openapi-generator.tech
* Do not edit the class manually.
*/
import { Zone } from './zone';


/**
@@ -17,7 +16,8 @@ import { Zone } from './zone';
*/
export interface Location {
readonly dbId?: number | null;
zone?: Zone;
readonly zone?: string;
zoneIri: string | null;
name: string;
code: string;
isZone?: boolean;


+ 2
- 1
angular/src/app/core/api/v1/model/locationJsonld.ts ファイルの表示

@@ -21,7 +21,8 @@ export interface LocationJsonld {
readonly id?: string;
readonly type?: string;
readonly dbId?: number | null;
zone?: ZoneJsonld;
readonly zone?: ZoneJsonld;
zoneIri: string | null;
name: string;
code: string;
isZone?: boolean;


+ 6
- 5
angular/src/app/core/api/v1/model/trip.ts ファイルの表示

@@ -9,8 +9,6 @@
* https://openapi-generator.tech
* Do not edit the class manually.
*/
import { Vessel } from './vessel';
import { Location } from './location';


/**
@@ -18,12 +16,15 @@ import { Location } from './location';
*/
export interface Trip {
readonly dbId?: number | null;
vessel?: Vessel;
readonly vessel?: string;
vesselIri: string | null;
readonly pilotageReference?: string | null;
customerReference?: string | null;
captainName?: string | null;
startLocation?: Location;
endLocation?: Location;
readonly startLocation?: string;
startLocationIri: string | null;
readonly endLocation?: string;
endLocationIri: string | null;
startDate: string;
endDate: string;
note?: string | null;


+ 6
- 3
angular/src/app/core/api/v1/model/tripJsonld.ts ファイルの表示

@@ -22,12 +22,15 @@ export interface TripJsonld {
readonly id?: string;
readonly type?: string;
readonly dbId?: number | null;
vessel?: VesselJsonld;
readonly vessel?: VesselJsonld;
vesselIri: string | null;
readonly pilotageReference?: string | null;
customerReference?: string | null;
captainName?: string | null;
startLocation?: LocationJsonld;
endLocation?: LocationJsonld;
readonly startLocation?: LocationJsonld;
startLocationIri: string | null;
readonly endLocation?: LocationJsonld;
endLocationIri: string | null;
startDate: string;
endDate: string;
note?: string | null;


+ 2
- 0
angular/src/app/core/api/v1/model/tripLocation.ts ファイルの表示

@@ -19,7 +19,9 @@ import { Location } from './location';
export interface TripLocation {
readonly dbId?: number | null;
trip?: Trip;
tripIri: string | null;
location?: Location;
locationIri: string | null;
isArrival?: boolean;
date: string;
readonly createdAt?: string | null;


+ 2
- 0
angular/src/app/core/api/v1/model/tripLocationJsonld.ts ファイルの表示

@@ -23,7 +23,9 @@ export interface TripLocationJsonld {
readonly type?: string;
readonly dbId?: number | null;
trip?: TripJsonld;
tripIri: string | null;
location?: LocationJsonld;
locationIri: string | null;
isArrival?: boolean;
date: string;
readonly createdAt?: string | null;


+ 6
- 6
angular/src/app/core/api/v1/model/userTrip.ts ファイルの表示

@@ -9,9 +9,6 @@
* https://openapi-generator.tech
* Do not edit the class manually.
*/
import { Trip } from './trip';
import { User } from './user';
import { MediaObject } from './mediaObject';


/**
@@ -19,11 +16,14 @@ import { MediaObject } from './mediaObject';
*/
export interface UserTrip {
readonly dbId?: number | null;
trip: Trip;
user: User;
readonly trip?: string;
tripIri: string | null;
readonly user?: string;
userIri: string | null;
captainName?: string | null;
completed: boolean;
signature?: MediaObject;
readonly signature?: string;
signatureIri?: string | null;
readonly signatureUrl?: string | null;
completedDate?: string | null;
readonly createdAt?: string | null;


+ 2
- 0
angular/src/app/core/api/v1/model/userTripEvent.ts ファイルの表示

@@ -18,7 +18,9 @@ import { UserTrip } from './userTrip';
export interface UserTripEvent {
readonly dbId?: number | null;
readonly userTrip?: UserTrip;
userTripIri: string | null;
readonly event?: string;
eventIri: string | null;
date: string;
note?: string | null;
readonly createdAt?: string | null;


+ 2
- 0
angular/src/app/core/api/v1/model/userTripEventJsonld.ts ファイルの表示

@@ -23,7 +23,9 @@ export interface UserTripEventJsonld {
readonly type?: string;
readonly dbId?: number | null;
readonly userTrip?: UserTripJsonld;
userTripIri: string | null;
readonly event?: EventJsonld;
eventIri: string | null;
date: string;
note?: string | null;
readonly createdAt?: string | null;


+ 6
- 3
angular/src/app/core/api/v1/model/userTripJsonld.ts ファイルの表示

@@ -23,11 +23,14 @@ export interface UserTripJsonld {
readonly id?: string;
readonly type?: string;
readonly dbId?: number | null;
trip: TripJsonld;
user: UserJsonld;
readonly trip?: TripJsonld;
tripIri: string | null;
readonly user?: UserJsonld;
userIri: string | null;
captainName?: string | null;
completed: boolean;
signature?: MediaObjectJsonld;
readonly signature?: MediaObjectJsonld;
signatureIri?: string | null;
readonly signatureUrl?: string | null;
completedDate?: string | null;
readonly createdAt?: string | null;


+ 2
- 2
angular/src/app/core/api/v1/model/vessel.ts ファイルの表示

@@ -9,7 +9,6 @@
* https://openapi-generator.tech
* Do not edit the class manually.
*/
import { ShippingCompany } from './shippingCompany';


/**
@@ -19,7 +18,8 @@ export interface Vessel {
readonly dbId?: number | null;
name: string;
code: string;
company?: ShippingCompany;
readonly company?: string;
companyIri: string | null;
readonly createdAt?: string | null;
}


+ 2
- 1
angular/src/app/core/api/v1/model/vesselJsonld.ts ファイルの表示

@@ -23,7 +23,8 @@ export interface VesselJsonld {
readonly dbId?: number | null;
name: string;
code: string;
company?: ShippingCompanyJsonld;
readonly company?: ShippingCompanyJsonld;
companyIri: string | null;
readonly createdAt?: string | null;
}


+ 7
- 2
httpdocs/src/ApiResource/LocationApi.php ファイルの表示

@@ -19,6 +19,7 @@ use App\State\EntityClassDtoStateProcessor;
use App\State\EntityToDtoStateProvider;
use Symfony\Component\Validator\Constraints as Assert;
use Symfony\Component\PropertyInfo\Type;
use Symfony\Component\Validator\Constraints\NotBlank;

#[ApiResource(
shortName: 'Location',
@@ -61,9 +62,9 @@ class LocationApi
* @var ZoneApi
*/
#[ApiProperty(
writable: true,
writable: false,
readableLink: true,
writableLink: true,
writableLink: false,
builtinTypes: [
new Type(
'object',
@@ -73,6 +74,10 @@ class LocationApi
)]
public ?ZoneApi $zone = null;

#[NotBlank]
#[ApiProperty(writable: true)]
public ?ZoneApi $zoneIri = null;

#[Assert\NotBlank]
public string $name;



+ 55
- 42
httpdocs/src/ApiResource/TripApi.php ファイルの表示

@@ -19,6 +19,7 @@ use App\State\EntityClassDtoStateProcessor;
use App\State\EntityToDtoStateProvider;
use Symfony\Component\Validator\Constraints as Assert;
use Symfony\Component\PropertyInfo\Type;
use Symfony\Component\Validator\Constraints\NotBlank;

#[ApiResource(
shortName: 'Trip',
@@ -57,22 +58,26 @@ class TripApi
#[ApiProperty(writable: false)]
public ?int $dbId = null;

// /**
// * @var VesselApi
// */
// #[ApiProperty(
// writable: true,
// readableLink: true,
// writableLink: false,
// builtinTypes: [
// new Type(
// 'object',
// class: VesselApi::class,
// )
// ]
// )]
/**
* @var VesselApi
*/
#[ApiProperty(
writable: false,
readableLink: true,
writableLink: false,
builtinTypes: [
new Type(
'object',
class: VesselApi::class,
)
]
)]
public ?VesselApi $vessel = null;

#[NotBlank]
#[ApiProperty(writable: true)]
public ?VesselApi $vesselIri = null;

#[ApiProperty(writable: false)]
public ?string $pilotageReference = null;

@@ -80,38 +85,46 @@ class TripApi

public ?string $captainName = null;

// /**
// * @var LocationApi
// */
// #[ApiProperty(
// writable: true,
// readableLink: true,
// writableLink: false,
// builtinTypes: [
// new Type(
// 'object',
// class: LocationApi::class,
// )
// ]
// )]
/**
* @var LocationApi
*/
#[ApiProperty(
writable: false,
readableLink: true,
writableLink: false,
builtinTypes: [
new Type(
'object',
class: LocationApi::class,
)
]
)]
public ?LocationApi $startLocation = null;

// /**
// * @var LocationApi
// */
// #[ApiProperty(
// writable: true,
// readableLink: true,
// writableLink: false,
// builtinTypes: [
// new Type(
// 'object',
// class: LocationApi::class,
// )
// ]
// )]
#[NotBlank]
#[ApiProperty(writable: true)]
public ?LocationApi $startLocationIri = null;

/**
* @var LocationApi
*/
#[ApiProperty(
writable: false,
readableLink: true,
writableLink: false,
builtinTypes: [
new Type(
'object',
class: LocationApi::class,
)
]
)]
public ?LocationApi $endLocation = null;

#[NotBlank]
#[ApiProperty(writable: true)]
public ?LocationApi $endLocationIri = null;

#[Assert\NotBlank]
public \DateTimeImmutable $startDate;



+ 9
- 0
httpdocs/src/ApiResource/TripLocationApi.php ファイルの表示

@@ -19,6 +19,7 @@ use App\State\EntityClassDtoStateProcessor;
use App\State\EntityToDtoStateProvider;
use Symfony\Component\Validator\Constraints as Assert;
use Symfony\Component\PropertyInfo\Type;
use Symfony\Component\Validator\Constraints\NotBlank;

#[ApiResource(
shortName: 'TripLocation',
@@ -73,6 +74,10 @@ class TripLocationApi
)]
public ?TripApi $trip = null;

#[NotBlank]
#[ApiProperty(writable: true)]
public ?TripApi $tripIri = null;

/**
* @var LocationApi
*/
@@ -89,6 +94,10 @@ class TripLocationApi
)]
public ?LocationApi $location = null;

#[NotBlank]
#[ApiProperty(writable: true)]
public ?LocationApi $locationIri = null;

public bool $isArrival;

#[Assert\NotBlank]


+ 18
- 8
httpdocs/src/ApiResource/UserTripApi.php ファイルの表示

@@ -20,6 +20,7 @@ use App\State\EntityClassDtoStateProcessor;
use App\State\EntityToDtoStateProvider;
use Symfony\Component\Validator\Constraints as Assert;
use Symfony\Component\PropertyInfo\Type;
use Symfony\Component\Validator\Constraints\NotBlank;

#[ApiResource(
shortName: 'UserTrip',
@@ -63,9 +64,9 @@ class UserTripApi
* @var TripApi
*/
#[ApiProperty(
writable: true,
writable: false,
readableLink: true,
writableLink: true,
writableLink: false,
builtinTypes: [
new Type(
'object',
@@ -73,16 +74,19 @@ class UserTripApi
)
]
)]
#[Assert\NotBlank]
public ?TripApi $trip = null;

#[NotBlank]
#[ApiProperty(writable: true)]
public ?TripApi $tripIri = null;

/**
* @var UserApi
*/
#[ApiProperty(
writable: true,
writable: false,
readableLink: true,
writableLink: true,
writableLink: false,
builtinTypes: [
new Type(
'object',
@@ -90,9 +94,12 @@ class UserTripApi
)
]
)]
#[Assert\NotBlank]
public ?UserApi $user = null;

#[NotBlank]
#[ApiProperty(writable: true)]
public ?UserApi $userIri = null;

public ?string $captainName = null;

#[Assert\NotNull]
@@ -102,9 +109,9 @@ class UserTripApi
* @var MediaObjectApi
*/
#[ApiProperty(
writable: true,
writable: false,
readableLink: true,
writableLink: true,
writableLink: false,
builtinTypes: [
new Type(
'object',
@@ -114,6 +121,9 @@ class UserTripApi
)]
public ?MediaObjectApi $signature = null;

#[ApiProperty(writable: true)]
public ?MediaObjectApi $signatureIri = null;

#[ApiProperty(writable: false)]
public ?string $signatureUrl = null;



+ 9
- 0
httpdocs/src/ApiResource/UserTripEventApi.php ファイルの表示

@@ -15,6 +15,7 @@ use App\State\EntityClassDtoStateProcessor;
use App\State\EntityToDtoStateProvider;
use Symfony\Component\Validator\Constraints as Assert;
use Symfony\Component\PropertyInfo\Type;
use Symfony\Component\Validator\Constraints\NotBlank;

#[ApiResource(
shortName: 'UserTripEvent',
@@ -61,6 +62,10 @@ class UserTripEventApi
)]
public ?UserTripApi $userTrip = null;

#[NotBlank]
#[ApiProperty(writable: true)]
public ?UserTripApi $userTripIri = null;

/**
* @var EventApi
*/
@@ -77,6 +82,10 @@ class UserTripEventApi
)]
public ?EventApi $event = null;

#[NotBlank]
#[ApiProperty(writable: true)]
public ?EventApi $eventIri = null;

#[Assert\NotBlank]
public \DateTimeImmutable $date;



+ 7
- 2
httpdocs/src/ApiResource/VesselApi.php ファイルの表示

@@ -19,6 +19,7 @@ use App\State\EntityClassDtoStateProcessor;
use App\State\EntityToDtoStateProvider;
use Symfony\Component\Validator\Constraints as Assert;
use Symfony\Component\PropertyInfo\Type;
use Symfony\Component\Validator\Constraints\NotBlank;

#[ApiResource(
shortName: 'Vessel',
@@ -67,9 +68,9 @@ class VesselApi
* @var ShippingCompanyApi
*/
#[ApiProperty(
writable: true,
writable: false,
readableLink: true,
writableLink: true,
writableLink: false,
builtinTypes: [
new Type(
'object',
@@ -79,6 +80,10 @@ class VesselApi
)]
public ?ShippingCompanyApi $company = null;

#[NotBlank]
#[ApiProperty(writable: true)]
public ?ShippingCompanyApi $companyIri = null;

#[ApiProperty(writable: false)]
public ?\DateTimeImmutable $createdAt = null;
}

+ 1
- 1
httpdocs/src/Mapper/LocationEntityToApiMapper.php ファイルの表示

@@ -43,7 +43,7 @@ class LocationEntityToApiMapper implements MapperInterface
$dto->isPort = $entity->isPort();
$dto->createdAt = $entity->getCreatedAt();

$dto->zone = $this->microMapper->map($entity->getZone(), ZoneApi::class, [
$dto->zoneIri = $dto->zone = $this->microMapper->map($entity->getZone(), ZoneApi::class, [
MicroMapperInterface::MAX_DEPTH => 1,
]);



+ 13
- 8
httpdocs/src/Mapper/UserTripApiToEntityMapper.php ファイルの表示

@@ -4,6 +4,7 @@ namespace App\Mapper;

use App\ApiResource\UserTripApi;
use App\Entity\UserTrip;
use App\Repository\MediaObjectRepository;
use App\Repository\UserTripRepository;
use App\Repository\TripRepository;
use App\Repository\UserRepository;
@@ -18,6 +19,7 @@ class UserTripApiToEntityMapper implements MapperInterface
private UserTripRepository $repository,
private TripRepository $tripRepository,
private UserRepository $userRepository,
private MediaObjectRepository $mediaObjectRepository
) {
}

@@ -64,16 +66,19 @@ class UserTripApiToEntityMapper implements MapperInterface

$entity->setCaptainName($dto->captainName);

if ($dto->completed && !$entity->isCompleted()) {
if ($entity->getSignature() === null) {
if ($dto->signatureUrl === null) {
if ($dto->completed && $entity->getCompletedDate() === null) {
$entity->setCompletedDate(new \DateTimeImmutable());
}
$entity->setCompleted($dto->completed);

}
if ($dto->signatureIri) {
$signature = $this->mediaObjectRepository->find($dto->signatureIri->id);
if (!$signature) {
throw new \Exception('Signature not found');
}

$entity->setCompleted($dto->completed);
$entity->setCompletedDate(new \DateTimeImmutable());
$entity->setSignature($signature);
} else {
$entity->setSignature(null);
}

return $entity;


+ 4
- 4
httpdocs/src/Mapper/UserTripEntityToApiMapper.php ファイルの表示

@@ -46,17 +46,17 @@ class UserTripEntityToApiMapper implements MapperInterface
$dto->completedDate = $entity->getCompletedDate();
$dto->createdAt = $entity->getCreatedAt();

$dto->trip = $this->microMapper->map($entity->getTrip(), TripApi::class, [
$dto->tripIri = $dto->trip = $this->microMapper->map($entity->getTrip(), TripApi::class, [
MicroMapperInterface::MAX_DEPTH => 1,
]);

$dto->user = $this->microMapper->map($entity->getUser(), UserApi::class, [
$dto->userIri = $dto->user = $this->microMapper->map($entity->getUser(), UserApi::class, [
MicroMapperInterface::MAX_DEPTH => 1,
]);

$dto->signature = null;
$dto->signatureIri = $dto->signature = null;
if ($entity->getSignature() !== null) {
$dto->signature = $this->microMapper->map($entity->getSignature(), MediaObjectApi::class, [
$dto->signatureIri = $dto->signature = $this->microMapper->map($entity->getSignature(), MediaObjectApi::class, [
MicroMapperInterface::MAX_DEPTH => 1,
]);
}


+ 7
- 1
httpdocs/src/State/EntityClassDtoStateProcessor.php ファイルの表示

@@ -46,11 +46,17 @@ class EntityClassDtoStateProcessor implements ProcessorInterface
$this->persistProcessor->process($entity, $operation, $uriVariables, $context);
$data->id = $entity->getId();

return $data;
$resourceClass = $operation->getClass();
return $this->mapEntityToDto($entity, $resourceClass);
}

private function mapDtoToEntity(object $dto, string $entityClass): object
{
return $this->microMapper->map($dto, $entityClass);
}

private function mapEntityToDto(object $entity, string $resourceClass): object
{
return $this->microMapper->map($entity, $resourceClass);
}
}

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