| @@ -2786,6 +2786,8 @@ components: | |||||
| type: | type: | ||||
| - string | - string | ||||
| - 'null' | - 'null' | ||||
| completed: | |||||
| type: boolean | |||||
| signatureUrl: | signatureUrl: | ||||
| readOnly: true | readOnly: true | ||||
| type: | type: | ||||
| @@ -2846,6 +2848,8 @@ components: | |||||
| type: | type: | ||||
| - string | - string | ||||
| - 'null' | - 'null' | ||||
| completed: | |||||
| type: boolean | |||||
| signatureUrl: | signatureUrl: | ||||
| readOnly: true | readOnly: true | ||||
| type: | type: | ||||
| @@ -133,6 +133,7 @@ export const userTripForm = new FormGroup({ | |||||
| trip: new FormControl(null, [Validators.required]), | trip: new FormControl(null, [Validators.required]), | ||||
| user: new FormControl(null, [Validators.required]), | user: new FormControl(null, [Validators.required]), | ||||
| captainName: new FormControl(null, []), | captainName: new FormControl(null, []), | ||||
| completed: new FormControl(null, []), | |||||
| signatureUrl: new FormControl(null, []), | signatureUrl: new FormControl(null, []), | ||||
| completedDate: new FormControl(null, []), | completedDate: new FormControl(null, []), | ||||
| createdAt: new FormControl(null, []) | createdAt: new FormControl(null, []) | ||||
| @@ -143,6 +144,7 @@ export const userTripJsonldForm = new FormGroup({ | |||||
| trip: new FormControl(null, [Validators.required]), | trip: new FormControl(null, [Validators.required]), | ||||
| user: new FormControl(null, [Validators.required]), | user: new FormControl(null, [Validators.required]), | ||||
| captainName: new FormControl(null, []), | captainName: new FormControl(null, []), | ||||
| completed: new FormControl(null, []), | |||||
| signatureUrl: new FormControl(null, []), | signatureUrl: new FormControl(null, []), | ||||
| completedDate: new FormControl(null, []), | completedDate: new FormControl(null, []), | ||||
| createdAt: new FormControl(null, []) | createdAt: new FormControl(null, []) | ||||
| @@ -1,7 +1,7 @@ | |||||
| @if (trip) { | @if (trip) { | ||||
| <div class="spt-container"> | <div class="spt-container"> | ||||
| <div class="spt-headline d-flex justify-content-between align-items-start"> | <div class="spt-headline d-flex justify-content-between align-items-start"> | ||||
| <h2>{{ ('basic.edit') | translate }} {{ 'model.trip' | translate }}</h2> | |||||
| <h2>{{ ('basic.edit') | translate }} {{ 'model.trip' | translate }} {{ trip.pilotageReference }}</h2> | |||||
| </div> | </div> | ||||
| </div> | </div> | ||||
| <mat-tab-group> | <mat-tab-group> | ||||
| @@ -91,7 +91,7 @@ | |||||
| </mat-tab> | </mat-tab> | ||||
| <mat-tab label="{{ 'trip.assigned_users' | translate }}"> | <mat-tab label="{{ 'trip.assigned_users' | translate }}"> | ||||
| <div> | <div> | ||||
| <h4 class="mb-4">{{ 'trip.user_assignments' | translate }}</h4> | |||||
| <h4 class="mb-4">{{ 'trip.assigned_users' | translate }}</h4> | |||||
| <div *ngFor="let userTrip of userTrips; let i = index" class="mb-4"> | <div *ngFor="let userTrip of userTrips; let i = index" class="mb-4"> | ||||
| <div class="row"> | <div class="row"> | ||||
| @@ -117,16 +117,10 @@ export class TripDetailComponent implements OnInit, AfterViewInit { | |||||
| this.userForms.forEach(form => { | this.userForms.forEach(form => { | ||||
| const userValue = form.get('user')?.value; | const userValue = form.get('user')?.value; | ||||
| if (userValue) { | if (userValue) { | ||||
| // Wenn es ein Objekt ist (z.B. vollständiges UserJsonld-Objekt) | |||||
| if (typeof userValue === 'object' && userValue.id) { | |||||
| assignedUserIds.push(this.appHelperService.extractId(userValue.id)); | |||||
| } | |||||
| // Wenn es nur die ID als String ist | |||||
| else if (typeof userValue === 'string') { | |||||
| assignedUserIds.push(userValue); | |||||
| } | |||||
| assignedUserIds.push(this.appHelperService.extractId(userValue)); | |||||
| } | } | ||||
| }); | }); | ||||
| console.log(assignedUserIds); | |||||
| // Filtere Benutzer, die bereits ausgewählt sind (gespeichert oder nicht) | // Filtere Benutzer, die bereits ausgewählt sind (gespeichert oder nicht) | ||||
| return this.userService.usersGetCollection(page, pageSize, undefined, term).pipe( | return this.userService.usersGetCollection(page, pageSize, undefined, term).pipe( | ||||
| @@ -224,7 +218,7 @@ export class TripDetailComponent implements OnInit, AfterViewInit { | |||||
| trip: { | trip: { | ||||
| 'id': this.trip.id | 'id': this.trip.id | ||||
| } as TripJsonld, | } as TripJsonld, | ||||
| user: null | |||||
| user: undefined | |||||
| }; | }; | ||||
| // Füge es als UserTripJsonld hinzu | // Füge es als UserTripJsonld hinzu | ||||
| @@ -1 +1,9 @@ | |||||
| <p>user-trip-detail works!</p> | |||||
| @if (userTrip) { | |||||
| <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> | |||||
| </div> | |||||
| </div> | |||||
| } | |||||
| @@ -1,4 +1,15 @@ | |||||
| import { Component } from '@angular/core'; | import { Component } from '@angular/core'; | ||||
| import { | |||||
| LocationService, | |||||
| TripJsonld, | |||||
| TripLocationService, | |||||
| TripService, UserService, | |||||
| UserTripJsonld, | |||||
| UserTripService | |||||
| } from "@app/core/api/v1"; | |||||
| import {AppHelperService} from "@app/_helpers/app-helper.service"; | |||||
| import {ActivatedRoute} from "@angular/router"; | |||||
| import {FormBuilder} from "@angular/forms"; | |||||
| @Component({ | @Component({ | ||||
| selector: 'app-user-trip-detail', | selector: 'app-user-trip-detail', | ||||
| @@ -6,5 +17,26 @@ import { Component } from '@angular/core'; | |||||
| styleUrl: './user-trip-detail.component.scss' | styleUrl: './user-trip-detail.component.scss' | ||||
| }) | }) | ||||
| export class UserTripDetailComponent { | export class UserTripDetailComponent { | ||||
| protected userTrip!: UserTripJsonld; | |||||
| constructor( | |||||
| private tripService: TripService, | |||||
| private tripLocationService: TripLocationService, | |||||
| private locationService: LocationService, | |||||
| private userTripService: UserTripService, | |||||
| private userService: UserService, | |||||
| protected appHelperService: AppHelperService, | |||||
| private route: ActivatedRoute, | |||||
| private fb: FormBuilder | |||||
| ) {} | |||||
| ngOnInit() { | |||||
| this.route.params.subscribe(params => { | |||||
| this.userTripService.userTripsIdGet(params['id']).subscribe( | |||||
| data => { | |||||
| this.userTrip = data; | |||||
| } | |||||
| ); | |||||
| }); | |||||
| } | |||||
| } | } | ||||
| @@ -44,7 +44,7 @@ export class UserTripListComponent { | |||||
| name: 'userFirstname', | name: 'userFirstname', | ||||
| text: 'users.firstname', | text: 'users.firstname', | ||||
| type: ListComponent.COLUMN_TYPE_TEXT_BOLD, | type: ListComponent.COLUMN_TYPE_TEXT_BOLD, | ||||
| field: 'firstname', | |||||
| field: 'firstName', | |||||
| subResource: 'user', | subResource: 'user', | ||||
| sortable: true, | sortable: true, | ||||
| filterType: FilterBarComponent.FILTER_TYPE_TEXT, | filterType: FilterBarComponent.FILTER_TYPE_TEXT, | ||||
| @@ -53,7 +53,7 @@ export class UserTripListComponent { | |||||
| name: 'userLastname', | name: 'userLastname', | ||||
| text: 'users.lastname', | text: 'users.lastname', | ||||
| type: ListComponent.COLUMN_TYPE_TEXT_BOLD, | type: ListComponent.COLUMN_TYPE_TEXT_BOLD, | ||||
| field: 'lastname', | |||||
| field: 'lastName', | |||||
| subResource: 'user', | subResource: 'user', | ||||
| sortable: true, | sortable: true, | ||||
| filterType: FilterBarComponent.FILTER_TYPE_TEXT, | filterType: FilterBarComponent.FILTER_TYPE_TEXT, | ||||
| @@ -21,6 +21,7 @@ export interface UserTrip { | |||||
| trip: Trip; | trip: Trip; | ||||
| user: User; | user: User; | ||||
| captainName?: string | null; | captainName?: string | null; | ||||
| completed?: boolean; | |||||
| readonly signatureUrl?: string | null; | readonly signatureUrl?: string | null; | ||||
| completedDate?: string | null; | completedDate?: string | null; | ||||
| readonly createdAt?: string | null; | readonly createdAt?: string | null; | ||||
| @@ -25,6 +25,7 @@ export interface UserTripJsonld { | |||||
| trip: TripJsonld; | trip: TripJsonld; | ||||
| user: UserJsonld; | user: UserJsonld; | ||||
| captainName?: string | null; | captainName?: string | null; | ||||
| completed?: boolean; | |||||
| readonly signatureUrl?: string | null; | readonly signatureUrl?: string | null; | ||||
| completedDate?: string | null; | completedDate?: string | null; | ||||
| readonly createdAt?: string | null; | readonly createdAt?: string | null; | ||||
| @@ -33,7 +33,8 @@ | |||||
| "add_itinerary_location": "Add itinerary location", | "add_itinerary_location": "Add itinerary location", | ||||
| "remove_itinerary_location": "Remove itinerary location", | "remove_itinerary_location": "Remove itinerary location", | ||||
| "save_itinerary": "Save itinerary", | "save_itinerary": "Save itinerary", | ||||
| "assigned_users": "Assigned Users (User Trips)" | |||||
| "assigned_users": "Assigned Users (User Trips)", | |||||
| "save_user_assignments": "Save user assignments" | |||||
| }, | }, | ||||
| "user_trip": | "user_trip": | ||||
| { | { | ||||
| @@ -52,7 +53,9 @@ | |||||
| "zone": "Zone", | "zone": "Zone", | ||||
| "vessel": "Vessel", | "vessel": "Vessel", | ||||
| "shipping_company": "Shipping Company", | "shipping_company": "Shipping Company", | ||||
| "trip": "Trip" | |||||
| "trip": "Trip", | |||||
| "user": "User", | |||||
| "user_trip": "User Trip" | |||||
| }, | }, | ||||
| "location": | "location": | ||||
| { | { | ||||
| @@ -0,0 +1,31 @@ | |||||
| <?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 Version20250312165234 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('ALTER TABLE user_trip ADD completed TINYINT(1) NOT NULL'); | |||||
| } | |||||
| public function down(Schema $schema): void | |||||
| { | |||||
| // this down() migration is auto-generated, please modify it to your needs | |||||
| $this->addSql('ALTER TABLE user_trip DROP completed'); | |||||
| } | |||||
| } | |||||
| @@ -95,6 +95,25 @@ class UserTripApi | |||||
| public ?string $captainName = null; | public ?string $captainName = null; | ||||
| public bool $completed; | |||||
| /** | |||||
| * @var MediaObjectApi | |||||
| */ | |||||
| #[ApiProperty( | |||||
| writable: true, | |||||
| readableLink: true, | |||||
| writableLink: true, | |||||
| builtinTypes: [ | |||||
| new Type( | |||||
| 'object', | |||||
| class: MediaObjectApi::class, | |||||
| ) | |||||
| ] | |||||
| )] | |||||
| #[Assert\NotBlank] | |||||
| public ?MediaObjectApi $mediaObject = null; | |||||
| #[ApiProperty(writable: false)] | #[ApiProperty(writable: false)] | ||||
| public ?string $signatureUrl = null; | public ?string $signatureUrl = null; | ||||
| @@ -34,6 +34,9 @@ class UserTrip | |||||
| #[ORM\JoinColumn(nullable: true, onDelete: "SET NULL")] | #[ORM\JoinColumn(nullable: true, onDelete: "SET NULL")] | ||||
| private ?MediaObject $signature = null; | private ?MediaObject $signature = null; | ||||
| #[ORM\Column(nullable: false)] | |||||
| private bool $completed = false; | |||||
| #[ORM\Column(nullable: true)] | #[ORM\Column(nullable: true)] | ||||
| private ?DateTimeImmutable $completedDate; | private ?DateTimeImmutable $completedDate; | ||||
| @@ -49,6 +52,7 @@ class UserTrip | |||||
| ) { | ) { | ||||
| $this->trip = $trip; | $this->trip = $trip; | ||||
| $this->user = $user; | $this->user = $user; | ||||
| $this->completed = false; | |||||
| $this->createdAt = new DateTimeImmutable(); | $this->createdAt = new DateTimeImmutable(); | ||||
| $this->userTripEvents = new ArrayCollection(); | $this->userTripEvents = new ArrayCollection(); | ||||
| } | } | ||||
| @@ -110,6 +114,16 @@ class UserTrip | |||||
| $this->completedDate = $completedDate; | $this->completedDate = $completedDate; | ||||
| } | } | ||||
| public function isCompleted(): bool | |||||
| { | |||||
| return $this->completed; | |||||
| } | |||||
| public function setCompleted(bool $completed): void | |||||
| { | |||||
| $this->completed = $completed; | |||||
| } | |||||
| public function getUserTripEvents(): Collection | public function getUserTripEvents(): Collection | ||||
| { | { | ||||
| return $this->userTripEvents; | return $this->userTripEvents; | ||||
| @@ -63,7 +63,18 @@ class UserTripApiToEntityMapper implements MapperInterface | |||||
| assert($entity instanceof UserTrip); | assert($entity instanceof UserTrip); | ||||
| $entity->setCaptainName($dto->captainName); | $entity->setCaptainName($dto->captainName); | ||||
| $entity->setCompletedDate($dto->completedDate); | |||||
| if ($dto->completed && !$entity->isCompleted()) { | |||||
| if ($entity->getSignature() === null) { | |||||
| if ($dto->signatureUrl === null) { | |||||
| } | |||||
| } | |||||
| $entity->setCompleted($dto->completed); | |||||
| $entity->setCompletedDate(new \DateTimeImmutable()); | |||||
| } | |||||
| return $entity; | return $entity; | ||||
| } | } | ||||