Basic signup for events
This commit is contained in:
@@ -0,0 +1,54 @@
|
||||
<?php
|
||||
|
||||
namespace App\Domains\Event\Actions\CertificateOfConductionCheck;
|
||||
|
||||
use App\Enumerations\EfzStatus;
|
||||
use Illuminate\Support\Facades\Http;
|
||||
|
||||
class CertificateOfConductionCheckCommand {
|
||||
function __construct(public CertificateOfConductionCheckRequest $request)
|
||||
{}
|
||||
|
||||
public function execute() : CertificateOfConductionCheckResponse {
|
||||
$response = new CertificateOfConductionCheckResponse();
|
||||
|
||||
|
||||
|
||||
$localGroup = str_replace('Stamm ', '', $this->request->participant->localGroup()->first()->name);
|
||||
|
||||
$apiResponse = Http::acceptJson()
|
||||
->asJson()
|
||||
->withoutVerifying()
|
||||
->post(env('COC_CHECK_URL'), [
|
||||
'firstName' => trim($this->request->participant->firstname),
|
||||
'lastName' => $this->request->participant->lastname,
|
||||
'nickname' => $this->request->participant->nickname,
|
||||
'address' => trim($this->request->participant->address_1 . ' ' . $this->request->participant->address_2),
|
||||
'zip' => $this->request->participant->zip,
|
||||
'city' => $this->request->participant->city,
|
||||
'birthday' => $this->request->participant->birthday->format('Y-m-d'),
|
||||
'email' => $this->request->participant->email_1,
|
||||
'localGroup' => $localGroup,
|
||||
'checkForDate' => $this->request->participant->departure_date->format('Y-m-d'),
|
||||
]);
|
||||
|
||||
if (! $apiResponse->successful()) {
|
||||
return $response;
|
||||
}
|
||||
|
||||
$responseParsed = $apiResponse->json();
|
||||
|
||||
if (($responseParsed['status'] ?? null) !== '200/ok') {
|
||||
return $response;
|
||||
}
|
||||
|
||||
$response->status = match ($responseParsed['efzStatus']) {
|
||||
'NOT_REQUIRED' => EfzStatus::EFZ_STATUS_NOT_REQUIRED,
|
||||
'CHECKED_VALID' => EfzStatus::EFZ_STATUS_CHECKED_VALID,
|
||||
'CHECKED_INVALID' => EfzStatus::EFZ_STATUS_CHECKED_INVALID,
|
||||
default => EfzStatus::EFZ_STATUS_NOT_CHECKED
|
||||
};
|
||||
|
||||
return $response;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
<?php
|
||||
|
||||
namespace App\Domains\Event\Actions\CertificateOfConductionCheck;
|
||||
|
||||
use App\Models\EventParticipant;
|
||||
|
||||
class CertificateOfConductionCheckRequest {
|
||||
function __construct(public EventParticipant $participant)
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
<?php
|
||||
|
||||
namespace App\Domains\Event\Actions\CertificateOfConductionCheck;
|
||||
|
||||
use App\Enumerations\EfzStatus;
|
||||
|
||||
class CertificateOfConductionCheckResponse {
|
||||
public string $status;
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
$this->status = EfzStatus::EFZ_STATUS_NOT_CHECKED;
|
||||
}
|
||||
}
|
||||
@@ -10,7 +10,7 @@ class SetParticipationFeesCommand {
|
||||
$this->request = $request;
|
||||
}
|
||||
|
||||
public function excetute() : SetParticipationFeesResponse {
|
||||
public function execute() : SetParticipationFeesResponse {
|
||||
$response = new SetParticipationFeesResponse();
|
||||
$this->cleanBefore();
|
||||
|
||||
@@ -19,7 +19,9 @@ class SetParticipationFeesCommand {
|
||||
'type' => $this->request->participationFeeFirst['type'],
|
||||
'name' => $this->request->participationFeeFirst['name'],
|
||||
'description' => $this->request->participationFeeFirst['description'],
|
||||
'amount' => $this->request->participationFeeFirst['amount']->getAmount()
|
||||
'amount_standard' => $this->request->participationFeeFirst['amount_standard']->getAmount(),
|
||||
'amount_reduced' => null !== $this->request->participationFeeFirst['amount_reduced'] ? $this->request->participationFeeFirst['amount_reduced']->getAmount() : null,
|
||||
'amount_solidarity' => null !== $this->request->participationFeeFirst['amount_solidarity'] ? $this->request->participationFeeFirst['amount_solidarity']->getAmount() : null,
|
||||
]))->save();
|
||||
|
||||
if ($this->request->participationFeeSecond !== null) {
|
||||
@@ -28,7 +30,9 @@ class SetParticipationFeesCommand {
|
||||
'type' => $this->request->participationFeeSecond['type'],
|
||||
'name' => $this->request->participationFeeSecond['name'],
|
||||
'description' => $this->request->participationFeeSecond['description'],
|
||||
'amount' => $this->request->participationFeeSecond['amount']->getAmount()
|
||||
'amount_standard' => $this->request->participationFeeSecond['amount_standard']->getAmount(),
|
||||
'amount_reduced' => null !== $this->request->participationFeeSecond['amount_reduced'] ? $this->request->participationFeeSecond['amount_reduced']->getAmount() : null,
|
||||
'amount_solidarity' => null !== $this->request->participationFeeSecond['amount_solidarity'] ? $this->request->participationFeeSecond['amount_solidarity']->getAmount() : null,
|
||||
]))->save();
|
||||
}
|
||||
|
||||
@@ -38,7 +42,9 @@ class SetParticipationFeesCommand {
|
||||
'type' => $this->request->participationFeeThird['type'],
|
||||
'name' => $this->request->participationFeeThird['name'],
|
||||
'description' => $this->request->participationFeeThird['description'],
|
||||
'amount' => $this->request->participationFeeThird['amount']->getAmount()
|
||||
'amount_standard' => $this->request->participationFeeThird['amount_standard']->getAmount(),
|
||||
'amount_reduced' => null !== $this->request->participationFeeThird['amount_reduced'] ? $this->request->participationFeeThird['amount_reduced']->getAmount() : null,
|
||||
'amount_solidarity' => null !== $this->request->participationFeeThird['amount_solidarity'] ? $this->request->participationFeeThird['amount_solidarity']->getAmount() : null,
|
||||
]))->save();
|
||||
}
|
||||
|
||||
@@ -48,7 +54,9 @@ class SetParticipationFeesCommand {
|
||||
'type' => $this->request->participationFeeFourth['type'],
|
||||
'name' => $this->request->participationFeeFourth['name'],
|
||||
'description' => $this->request->participationFeeFourth['description'],
|
||||
'amount' => $this->request->participationFeeFourth['amount']->getAmount()
|
||||
'amount_standard' => $this->request->participationFeeFourth['amount_standard']->getAmount(),
|
||||
'amount_reduced' => null !== $this->request->participationFeeFourth['amount_reduced'] ? $this->request->participationFeeFourth['amount_reduced']->getAmount() : null,
|
||||
'amount_solidarity' => null !== $this->request->participationFeeFourth['amount_solidarity'] ? $this->request->participationFeeFourth['amount_solidarity']->getAmount() : null,
|
||||
]))->save();
|
||||
}
|
||||
|
||||
|
||||
65
app/Domains/Event/Actions/SignUp/SignUpCommand.php
Normal file
65
app/Domains/Event/Actions/SignUp/SignUpCommand.php
Normal file
@@ -0,0 +1,65 @@
|
||||
<?php
|
||||
|
||||
namespace App\Domains\Event\Actions\SignUp;
|
||||
|
||||
use App\Enumerations\EfzStatus;
|
||||
use App\ValueObjects\Age;
|
||||
use Illuminate\Support\Str;
|
||||
|
||||
class SignUpCommand {
|
||||
public function __construct(public SignUpRequest $request) {
|
||||
}
|
||||
|
||||
public function execute() : SignUpResponse {
|
||||
$response = new SignUpResponse();
|
||||
|
||||
$participantAge = new Age($this->request->birthday);
|
||||
|
||||
$response->participant = $this->request->event->participants()->create(
|
||||
[
|
||||
'tenant' => $this->request->event->tenant,
|
||||
'user_id' => $this->request->user_id,
|
||||
'identifier' => Str::random(10),
|
||||
'firstname' => $this->request->firstname,
|
||||
'lastname' => $this->request->lastname,
|
||||
'nickname' => $this->request->nickname,
|
||||
'participation_type' => $this->request->participationType,
|
||||
'local_group' => $this->request->localGroup->slug,
|
||||
'birthday' => $this->request->birthday,
|
||||
'address_1' => $this->request->address_1,
|
||||
'address_2' => $this->request->address_2,
|
||||
'postcode' => $this->request->postcode,
|
||||
'city' => $this->request->city,
|
||||
'email_1' => $this->request->email_1,
|
||||
'email_2' => $this->request->email_2,
|
||||
'phone_1' => $this->request->phone_1,
|
||||
'phone_2' => $this->request->phone_2,
|
||||
'contact_person' => $this->request->contact_person,
|
||||
'allergies' => $this->request->allergies,
|
||||
'intolerances' => $this->request->intolerances,
|
||||
'medications' => $this->request->medications,
|
||||
'tetanus_vaccination' => $this->request->tetanus_vaccination,
|
||||
'eating_habit' => $this->request->eating_habit,
|
||||
'swimming_permission' => $participantAge->isfullAged() ? 'SWIMMING_PERMISSION_ALLOWED' : $this->request->swimming_permission,
|
||||
'first_aid_permission' => $participantAge->isfullAged() ? 'FIRST_AID_PERMISSION_ALLOWED' : $this->request->first_aid_permission,
|
||||
'foto_socialmedia' => $this->request->foto_socialmedia,
|
||||
'foto_print' => $this->request->foto_print,
|
||||
'foto_webseite' => $this->request->foto_webseite,
|
||||
'foto_partner' => $this->request->foto_partner,
|
||||
'foto_intern' => $this->request->foto_intern,
|
||||
'arrival_date' => $this->request->arrival,
|
||||
'departure_date' => $this->request->departure,
|
||||
'arrival_eating' => $this->request->arrival_eating,
|
||||
'departure_eating' => $this->request->departure_eating,
|
||||
'notes' => $this->request->notes,
|
||||
'amount' => $this->request->amount,
|
||||
'payment_purpose' => $this->request->event->name . ' - Beitrag ' . $this->request->firstname . ' ' . $this->request->lastname,
|
||||
'efz_status' => $participantAge->isfullAged() ? EfzStatus::EFZ_STATUS_NOT_CHECKED : EfzStatus::EFZ_STATUS_NOT_REQUIRED,
|
||||
]
|
||||
);
|
||||
|
||||
$response->success = true;
|
||||
return $response;
|
||||
}
|
||||
|
||||
}
|
||||
50
app/Domains/Event/Actions/SignUp/SignUpRequest.php
Normal file
50
app/Domains/Event/Actions/SignUp/SignUpRequest.php
Normal file
@@ -0,0 +1,50 @@
|
||||
<?php
|
||||
|
||||
namespace App\Domains\Event\Actions\SignUp;
|
||||
|
||||
use App\Enumerations\ParticipationType;
|
||||
use App\Models\Event;
|
||||
use App\Models\Tenant;
|
||||
use App\ValueObjects\Amount;
|
||||
use DateTime;
|
||||
|
||||
class SignUpRequest {
|
||||
function __construct(
|
||||
public Event $event,
|
||||
public ?int $user_id,
|
||||
public string $firstname,
|
||||
public string $lastname,
|
||||
public ?string $nickname,
|
||||
public string $participationType,
|
||||
public Tenant $localGroup,
|
||||
public DateTime $birthday,
|
||||
public string $address_1,
|
||||
public string $address_2,
|
||||
public string $postcode,
|
||||
public string $city,
|
||||
public string $email_1,
|
||||
public ?string $phone_1,
|
||||
public ?string $email_2,
|
||||
public ?string $phone_2,
|
||||
public ?string $contact_person,
|
||||
public ?string $allergies,
|
||||
public ?string $intolerances,
|
||||
public ?string $medications,
|
||||
public ?DateTime $tetanus_vaccination,
|
||||
public string $eating_habit,
|
||||
public ?string $swimming_permission,
|
||||
public ?string $first_aid_permission,
|
||||
public bool $foto_socialmedia,
|
||||
public bool $foto_print,
|
||||
public bool $foto_webseite,
|
||||
public bool $foto_partner,
|
||||
public bool $foto_intern,
|
||||
public DateTime $arrival,
|
||||
public DateTime $departure,
|
||||
public int $arrival_eating,
|
||||
public int $departure_eating,
|
||||
public ?string $notes,
|
||||
public Amount $amount
|
||||
) {
|
||||
}
|
||||
}
|
||||
16
app/Domains/Event/Actions/SignUp/SignUpResponse.php
Normal file
16
app/Domains/Event/Actions/SignUp/SignUpResponse.php
Normal file
@@ -0,0 +1,16 @@
|
||||
<?php
|
||||
|
||||
namespace App\Domains\Event\Actions\SignUp;
|
||||
|
||||
use App\Models\EventParticipant;
|
||||
|
||||
class SignUpResponse {
|
||||
public bool $success;
|
||||
public ?EventParticipant $participant;
|
||||
|
||||
public function __construct() {
|
||||
$this->success = false;
|
||||
$this->participant = null;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -20,6 +20,8 @@ class UpdateEventCommand {
|
||||
$this->request->event->alcoholics_age = $this->request->alcoholicsAge;
|
||||
$this->request->event->support_per_person = $this->request->supportPerPerson;
|
||||
$this->request->event->support_flat = $this->request->flatSupport;
|
||||
$this->request->event->send_weekly_report = $this->request->sendWeeklyReports;
|
||||
$this->request->event->registration_allowed = $this->request->registrationAllowed;
|
||||
$this->request->event->save();
|
||||
|
||||
$this->request->event->resetAllowedEatingHabits();
|
||||
|
||||
22
app/Domains/Event/Controllers/AvailableEventsController.php
Normal file
22
app/Domains/Event/Controllers/AvailableEventsController.php
Normal file
@@ -0,0 +1,22 @@
|
||||
<?php
|
||||
|
||||
namespace App\Domains\Event\Controllers;
|
||||
|
||||
use App\Providers\InertiaProvider;
|
||||
use App\Scopes\CommonController;
|
||||
use Illuminate\Http\Request;
|
||||
use Inertia\Response;
|
||||
|
||||
class AvailableEventsController extends CommonController
|
||||
{
|
||||
public function __invoke(Request $request) : Response {
|
||||
$events = [];
|
||||
foreach ($this->events->getAvailable(false) as $event) {
|
||||
$events[] = $event->toResource()->toArray($request);
|
||||
};
|
||||
|
||||
|
||||
$inertiaProvider = new InertiaProvider('Event/ListAvailable', ['events' => $events]);
|
||||
return $inertiaProvider->render();
|
||||
}
|
||||
}
|
||||
@@ -87,7 +87,7 @@ class CreateController extends CommonController {
|
||||
if ($wasSuccessful) {
|
||||
return response()->json([
|
||||
'status' => 'success',
|
||||
'event' => new EventResource($costUnitUpdateRequest->event)->toArray()
|
||||
'event' => new EventResource($costUnitUpdateRequest->event)->toArray($request)
|
||||
]);
|
||||
} else {
|
||||
return response()->json([
|
||||
|
||||
@@ -23,9 +23,9 @@ class DetailsController extends CommonController {
|
||||
return new InertiaProvider('Event/Details', ['event' => $event])->render();
|
||||
}
|
||||
|
||||
public function summary(int $eventId) : JsonResponse {
|
||||
public function summary(int $eventId, Request $request) : JsonResponse {
|
||||
$event = $this->events->getById($eventId);
|
||||
return response()->json(['event' => new EventResource($event)->toArray()]);
|
||||
return response()->json(['event' => $event->toResource()->toArray($request)]);
|
||||
}
|
||||
|
||||
public function updateCommonSettings(int $eventId, Request $request) : JsonResponse {
|
||||
@@ -78,7 +78,9 @@ class DetailsController extends CommonController {
|
||||
'type' => ParticipationType::PARTICIPATION_TYPE_PARTICIPANT,
|
||||
'name' => 'Teilnehmer',
|
||||
'description' => $request->input('pft_1_description'),
|
||||
'amount' => Amount::fromString($request->input('pft_1_amount'))
|
||||
'amount_standard' => Amount::fromString($request->input('pft_1_amount_standard')),
|
||||
'amount_reduced' => null !== $request->input('pft_1_amount_reduced') ? Amount::fromString($request->input('pft_1_amount_reduced')) : null,
|
||||
'amount_solidarity' => null !== $request->input('pft_1_amount_solidarity') ? Amount::fromString($request->input('pft_1_amount_solidarity')) : null
|
||||
];
|
||||
|
||||
$participationFeeRequest = new SetParticipationFeesRequest($event, $participationFeeFirst);
|
||||
@@ -88,7 +90,9 @@ class DetailsController extends CommonController {
|
||||
'type' => ParticipationType::PARTICIPATION_TYPE_TEAM,
|
||||
'name' => $event->participation_fee_type === ParticipationFeeType::PARTICIPATION_FEE_TYPE_FIXED ? 'Kernteam' : 'Solidaritätsbeitrag',
|
||||
'description' => $request->input('pft_2_description'),
|
||||
'amount' => Amount::fromString($request->input('pft_2_amount'))
|
||||
'amount_standard' => Amount::fromString($request->input('pft_2_amount_standard')),
|
||||
'amount_reduced' => null !== $request->input('pft_2_amount_reduced') ? Amount::fromString($request->input('pft_2_amount_reduced')) : null,
|
||||
'amount_solidarity' => null !== $request->input('pft_2_amount_solidarity') ? Amount::fromString($request->input('pft_2_amount_solidarity')) : null
|
||||
];
|
||||
}
|
||||
|
||||
@@ -97,7 +101,9 @@ class DetailsController extends CommonController {
|
||||
'type' => ParticipationType::PARTICIPATION_TYPE_VOLUNTEER,
|
||||
'name' => $event->participation_fee_type === ParticipationFeeType::PARTICIPATION_FEE_TYPE_FIXED ? 'Unterstützende' : 'Reduzierter Beitrag',
|
||||
'description' => $event->participation_fee_type !== ParticipationFeeType::PARTICIPATION_FEE_TYPE_SOLIDARITY ? $request->input('pft_3_description') : 'Nach Verfügbarkeit',
|
||||
'amount' => Amount::fromString($request->input('pft_3_amount'))
|
||||
'amount_standard' => Amount::fromString($request->input('pft_3_amount_standard')),
|
||||
'amount_reduced' => null !== $request->input('pft_3_amount_reduced') ? Amount::fromString($request->input('pft_3_amount_reduced')) : null,
|
||||
'amount_solidarity' => null !== $request->input('pft_3_amount_solidarity') ? Amount::fromString($request->input('pft_3_amount_solidarity')) : null
|
||||
];
|
||||
}
|
||||
|
||||
@@ -106,12 +112,14 @@ class DetailsController extends CommonController {
|
||||
'type' => ParticipationType::PARTICIPATION_TYPE_OTHER,
|
||||
'name' => 'Sonstige',
|
||||
'description' => $request->input('pft_4_description'),
|
||||
'amount' => Amount::fromString($request->input('pft_4_amount'))
|
||||
'amount_standard' => Amount::fromString($request->input('pft_4_amount_standard')),
|
||||
'amount_reduced' => null !== $request->input('pft_4_amount_reduced') ? Amount::fromString($request->input('pft_4_amount_reduced')) : null,
|
||||
'amount_solidarity' => null !== $request->input('pft_4_amount_solidarity') ? Amount::fromString($request->input('pft_4_amount_solidarity')) : null
|
||||
];
|
||||
}
|
||||
|
||||
$participationFeeCommand = new SetParticipationFeesCommand($participationFeeRequest);
|
||||
$response = $participationFeeCommand->excetute();
|
||||
$response = $participationFeeCommand->execute();
|
||||
|
||||
return response()->json(['status' => $response->success ? 'success' : 'error']);
|
||||
}
|
||||
|
||||
171
app/Domains/Event/Controllers/SignupController.php
Normal file
171
app/Domains/Event/Controllers/SignupController.php
Normal file
@@ -0,0 +1,171 @@
|
||||
<?php
|
||||
|
||||
namespace App\Domains\Event\Controllers;
|
||||
|
||||
use App\Domains\Event\Actions\CertificateOfConductionCheck\CertificateOfConductionCheckCommand;
|
||||
use App\Domains\Event\Actions\CertificateOfConductionCheck\CertificateOfConductionCheckRequest;
|
||||
use App\Domains\Event\Actions\SignUp\SignUpCommand;
|
||||
use App\Domains\Event\Actions\SignUp\SignUpRequest;
|
||||
use App\Models\Tenant;
|
||||
use App\Models\User;
|
||||
use App\Providers\InertiaProvider;
|
||||
use App\Resources\EventResource;
|
||||
use App\Resources\UserResource;
|
||||
use App\Scopes\CommonController;
|
||||
use App\ValueObjects\Amount;
|
||||
use http\Env\Response;
|
||||
use Illuminate\Http\JsonResponse;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Http\Resources\Json\JsonResource;
|
||||
|
||||
class SignupController extends CommonController {
|
||||
public function __invoke(int $eventId, Request $request) {
|
||||
$availableEvents = [];
|
||||
foreach ($this->events->getAvailable(false) as $event) {
|
||||
$availableEvents[] = $event->toResource()->toArray($request);
|
||||
};
|
||||
|
||||
$event = $this->events->getById($eventId, false)?->toResource()->toArray($request);
|
||||
|
||||
$participantData = [
|
||||
'firstname' => '',
|
||||
'lastname' => '',
|
||||
];
|
||||
|
||||
if (auth()->check()) {
|
||||
$user = new UserResource(auth()->user())->toArray($request);
|
||||
|
||||
$participantData = [
|
||||
'id' => $user['id'],
|
||||
'firstname' => $user['firstname'],
|
||||
'lastname' => $user['lastname'],
|
||||
'nickname' => $user['nickname'],
|
||||
'email' => $user['email'],
|
||||
'phone' => $user['phone'],
|
||||
'postcode' => $user['postcode'],
|
||||
'city' => $user['city'],
|
||||
'address_1' => $user['address_1'],
|
||||
'address_2' => $user['address_2'],
|
||||
'birthday' => $user['birthday'],
|
||||
'localGroup' => $user['localGroup'],
|
||||
'allergies' => $user['allergies'],
|
||||
'intolerances' => $user['intolerances'],
|
||||
'eating_habit' => $user['eating_habits'],
|
||||
'medications' => $user['medications'],
|
||||
'tetanusVaccination' => $user['tetanus_vaccination'],
|
||||
];
|
||||
|
||||
}
|
||||
|
||||
|
||||
$inertiaProvider = new InertiaProvider('Event/Signup', [
|
||||
'event' => $event,
|
||||
'availableEvents' => $availableEvents,
|
||||
'localGroups' => $event['contributingLocalGroups'],
|
||||
'participantData' => $participantData,
|
||||
]);
|
||||
return $inertiaProvider->render();
|
||||
}
|
||||
|
||||
public function signUp(int $eventId, Request $request) {
|
||||
$event = $this->events->getById($eventId, false);
|
||||
$eventResource = $event->toResource();
|
||||
$registrationData = $request->input('registration_data');
|
||||
|
||||
$arrival = \DateTime::createFromFormat('Y-m-d', $registrationData['arrival']);
|
||||
$departure = \DateTime::createFromFormat('Y-m-d', $registrationData['departure']);
|
||||
$tetanusVaccination = $registrationData['tetanusVaccination'] ? \DateTime::createFromFormat('Y-m-d', $registrationData['tetanusVaccination']) : null;
|
||||
|
||||
// Steps:
|
||||
// 1. Check, if bereits angemeldet
|
||||
|
||||
|
||||
//
|
||||
$amount = $eventResource->calculateAmount(
|
||||
$registrationData['participationType'],
|
||||
$registrationData['beitrag'],
|
||||
$arrival,
|
||||
$departure
|
||||
);
|
||||
|
||||
$signupRequest = new SignUpRequest(
|
||||
$event,$registrationData['userId'],
|
||||
$registrationData['vorname'],
|
||||
$registrationData['nachname'],
|
||||
$registrationData['pfadiname'],
|
||||
$registrationData['participationType'],
|
||||
Tenant::findOrFail($registrationData['localGroup']),
|
||||
\DateTime::createFromFormat('Y-m-d', $registrationData['geburtsdatum']),
|
||||
$registrationData['address1'],
|
||||
$registrationData['address2'],
|
||||
$registrationData['plz'],
|
||||
$registrationData['ort'],
|
||||
$registrationData['email_1'],
|
||||
$registrationData['telefon_1'],
|
||||
$registrationData['email_2'],
|
||||
$registrationData['telefon_2'],
|
||||
$registrationData['ansprechpartner'],
|
||||
$registrationData['allergien'],
|
||||
$registrationData['intolerances'],
|
||||
$registrationData['medikamente'],
|
||||
$tetanusVaccination,
|
||||
$registrationData['essgewohnheit'],
|
||||
$registrationData['badeerlaubnis'],
|
||||
$registrationData['first_aid'],
|
||||
$registrationData['foto']['socialmedia'],
|
||||
$registrationData['foto']['print'],
|
||||
$registrationData['foto']['webseite'],
|
||||
$registrationData['foto']['partner'],
|
||||
$registrationData['foto']['intern'],
|
||||
$arrival,
|
||||
$departure,
|
||||
$registrationData['anreise_essen'],
|
||||
$registrationData['abreise_essen'],
|
||||
$registrationData['anmerkungen'],
|
||||
$amount
|
||||
);
|
||||
|
||||
$signupCommand = new SignUpCommand($signupRequest);
|
||||
$signupResponse = $signupCommand->execute();
|
||||
|
||||
// 4. Addons registrieren
|
||||
|
||||
|
||||
$certificateOfConductionCheckRequest = new CertificateOfConductionCheckRequest($signupResponse->participant);
|
||||
$certificateOfConductionCheckCommand = new CertificateOfConductionCheckCommand($certificateOfConductionCheckRequest);
|
||||
$certificateOfConductionCheckResponse = $certificateOfConductionCheckCommand->execute();
|
||||
|
||||
$signupResponse->participant->efz_status = $certificateOfConductionCheckResponse->status;
|
||||
$signupResponse->participant->save();
|
||||
|
||||
// 6. E-Mail senden & Bestätigung senden
|
||||
|
||||
|
||||
|
||||
return response()->json(
|
||||
[
|
||||
'participant' => $signupResponse->participant->toResource()->toArray($request),
|
||||
'status' => 'success',
|
||||
]
|
||||
);
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
dd($eventId, $registrationData, $amount);
|
||||
}
|
||||
|
||||
public function calculateAmount(int $eventId, Request $request, bool $forDisplay = true) : JsonResponse | float {
|
||||
$event = $this->events->getById($eventId, false)->toResource();
|
||||
|
||||
return response()->json(['amount' =>
|
||||
$event->calculateAmount(
|
||||
$request->input('participationType'),
|
||||
$request->input('beitrag'),
|
||||
\DateTime::createFromFormat('Y-m-d', $request->input('arrival')),
|
||||
\DateTime::createFromFormat('Y-m-d', $request->input('departure'))
|
||||
)->toString()
|
||||
]);
|
||||
}
|
||||
}
|
||||
@@ -2,6 +2,7 @@
|
||||
|
||||
use App\Domains\Event\Controllers\CreateController;
|
||||
use App\Domains\Event\Controllers\DetailsController;
|
||||
use App\Domains\Event\Controllers\SignupController;
|
||||
use App\Middleware\IdentifyTenant;
|
||||
use Illuminate\Support\Facades\Route;
|
||||
|
||||
@@ -9,6 +10,9 @@ Route::prefix('api/v1')
|
||||
->group(function () {
|
||||
Route::middleware(IdentifyTenant::class)->group(function () {
|
||||
Route::prefix('event')->group(function () {
|
||||
Route::post('{eventId}/calculate-amount', [SignupController::class, 'calculateAmount']);
|
||||
Route::post('{eventId}/signup', [SignupController::class, 'signUp']);
|
||||
|
||||
Route::middleware(['auth'])->group(function () {
|
||||
Route::post('/create', [CreateController::class, 'doCreate']);
|
||||
|
||||
|
||||
@@ -1,12 +1,19 @@
|
||||
<?php
|
||||
|
||||
use App\Domains\Event\Controllers\AvailableEventsController;
|
||||
use App\Domains\Event\Controllers\CreateController;
|
||||
use App\Domains\Event\Controllers\DetailsController;
|
||||
use App\Domains\Event\Controllers\SignupController;
|
||||
use App\Middleware\IdentifyTenant;
|
||||
use Illuminate\Support\Facades\Route;
|
||||
|
||||
Route::middleware(IdentifyTenant::class)->group(function () {
|
||||
Route::prefix('event')->group(function () {
|
||||
Route::get('/available-events', AvailableEventsController::class);
|
||||
|
||||
|
||||
Route::get('/{eventId}/signup', SignupController::class);
|
||||
|
||||
Route::middleware(['auth'])->group(function () {
|
||||
Route::get('/new', CreateController::class);
|
||||
Route::get('/details/{eventId}', DetailsController::class);
|
||||
|
||||
102
app/Domains/Event/Views/Partials/AvailableEvents.vue
Normal file
102
app/Domains/Event/Views/Partials/AvailableEvents.vue
Normal file
@@ -0,0 +1,102 @@
|
||||
<script setup>
|
||||
|
||||
import ShadowedBox from "../../../../Views/Components/ShadowedBox.vue";
|
||||
|
||||
const props = defineProps({
|
||||
events: Array,
|
||||
})
|
||||
|
||||
console.log(props.events)
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div style="width: 95%; margin: 20px auto;">
|
||||
|
||||
<div v-if="props.events.length === 0" style="text-align: center; color: #6b7280; padding: 40px 0;">
|
||||
Aktuell sind keine Veranstaltungen verfügbar.
|
||||
</div>
|
||||
|
||||
<shadowed-box
|
||||
v-for="event in props.events"
|
||||
:key="event.id"
|
||||
style="padding: 24px; margin-bottom: 20px;"
|
||||
>
|
||||
<div style="display: flex; justify-content: space-between; align-items: flex-start; flex-wrap: wrap; gap: 12px;">
|
||||
<div>
|
||||
<h2 style="margin: 0 0 4px 0; font-size: 1.25rem;">{{ event.name }}</h2>
|
||||
<span style="color: #6b7280; font-size: 0.9rem;">{{ event.postalCode }} {{ event.location }}</span>
|
||||
</div>
|
||||
<span
|
||||
v-if="event.registrationAllowed"
|
||||
style="background: #d1fae5; color: #065f46; padding: 4px 12px; border-radius: 999px; font-size: 0.8rem; font-weight: 600; white-space: nowrap;"
|
||||
>
|
||||
Anmeldung offen
|
||||
</span>
|
||||
<span
|
||||
v-else
|
||||
style="background: #fee2e2; color: #991b1b; padding: 4px 12px; border-radius: 999px; font-size: 0.8rem; font-weight: 600; white-space: nowrap;"
|
||||
>
|
||||
Anmeldung geschlossen
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<hr style="margin: 16px 0; border: none; border-top: 1px solid #e5e7eb;" />
|
||||
|
||||
<table style="width: 100%; border-collapse: collapse;">
|
||||
<tr>
|
||||
<th style="text-align: left; padding: 6px 12px 6px 0; width: 220px; color: #374151; font-weight: 600;">Zeitraum</th>
|
||||
<td style="padding: 6px 0; color: #111827;">{{ event.eventBegin }} – {{ event.eventEnd }} ({{ event.duration }} Tage)</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th style="text-align: left; padding: 6px 12px 6px 0; width: 220px; color: #374151; font-weight: 600;">Veranstaltungsort</th>
|
||||
<td style="padding: 6px 0; color: #111827;">{{ event.postalCode }} {{ event.location }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th style="text-align: left; padding: 6px 12px 6px 0; color: #374151; font-weight: 600;">Frühbuchen bis</th>
|
||||
<td style="padding: 6px 0; color: #111827;">{{ event.earlyBirdEnd.formatted }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th style="text-align: left; padding: 6px 12px 6px 0; color: #374151; font-weight: 600;">Anmeldeschluss</th>
|
||||
<td style="padding: 6px 0; color: #111827;">{{ event.registrationFinalEnd.formatted }}</td>
|
||||
</tr>
|
||||
<tr v-if="event.email">
|
||||
<th style="text-align: left; padding: 6px 12px 6px 0; color: #374151; font-weight: 600;">Kontakt</th>
|
||||
<td style="padding: 6px 0;">
|
||||
<a :href="'mailto:' + event.email" style="color: #2563eb;">{{ event.email }}</a>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
<div style="margin-top: 20px; display: flex; justify-content: flex-end;">
|
||||
<a
|
||||
:href="'/event/' + event.id + '/signup'"
|
||||
style="
|
||||
display: inline-block;
|
||||
padding: 10px 24px;
|
||||
background-color: #2563eb;
|
||||
color: white;
|
||||
border-radius: 8px;
|
||||
text-decoration: none;
|
||||
font-weight: 600;
|
||||
font-size: 0.95rem;
|
||||
opacity: 1;
|
||||
transition: background-color 0.2s;
|
||||
"
|
||||
:style="{ opacity: event.registrationAllowed ? '1' : '0.5', pointerEvents: event.registrationAllowed ? 'auto' : 'none' }"
|
||||
>
|
||||
Zur Anmeldung →
|
||||
</a>
|
||||
</div>
|
||||
</shadowed-box>
|
||||
|
||||
</div><div style="width: 95%; margin: 20px auto;">
|
||||
|
||||
<div v-if="props.events.length === 0" style="text-align: center; color: #6b7280; padding: 40px 0;">
|
||||
Aktuell sind keine Veranstaltungen verfügbar.
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
|
||||
</style>
|
||||
@@ -16,19 +16,27 @@
|
||||
const errors = reactive({})
|
||||
const formData = reactive({
|
||||
"pft_1_active": true,
|
||||
"pft_1_amount": props.event.participationFee_1.amount,
|
||||
"pft_1_amount_standard": props.event.participationFee_1.amount_standard_edit,
|
||||
"pft_1_amount_reduced": props.event.participationFee_1.amount_reduced_edit,
|
||||
"pft_1_amount_solidarity": props.event.participationFee_1.amount_solidarity_edit,
|
||||
"pft_1_description": props.event.participationFee_1.description,
|
||||
|
||||
"pft_2_active": props.event.participationFee_2.active,
|
||||
"pft_2_amount": props.event.participationFee_2.amount,
|
||||
"pft_2_amount_standard": props.event.participationFee_2.amount_standard_edit,
|
||||
"pft_2_amount_reduced": props.event.participationFee_2.amount_reduced_edit,
|
||||
"pft_2_amount_solidarity": props.event.participationFee_2.amount_solidarity_edit,
|
||||
"pft_2_description": props.event.participationFee_2.description,
|
||||
|
||||
"pft_3_active": props.event.participationFee_3.active,
|
||||
"pft_3_amount": props.event.participationFee_3.amount,
|
||||
"pft_3_amount_standard": props.event.participationFee_3.amount_standard_edit,
|
||||
"pft_3_amount_reduced": props.event.participationFee_3.amount_reduced_edit,
|
||||
"pft_3_amount_solidarity": props.event.participationFee_3.amount_solidarity_edit,
|
||||
"pft_3_description": props.event.participationFee_3.description,
|
||||
|
||||
"pft_4_active": props.event.participationFee_4.active,
|
||||
"pft_4_amount": props.event.participationFee_4.amount,
|
||||
"pft_4_amount_standard": props.event.participationFee_4.amount_standard_edit,
|
||||
"pft_4_amount_reduced": props.event.participationFee_4.amount_reduced_edit,
|
||||
"pft_4_amount_solidarity": props.event.participationFee_4.amount_solidarity_edit,
|
||||
"pft_4_description": props.event.participationFee_4.description,
|
||||
|
||||
'maxAmount': props.event.maxAmount,
|
||||
@@ -38,17 +46,17 @@
|
||||
function validateInput() {
|
||||
var noErrors = true;
|
||||
|
||||
if (formData.pft_1_description === '' && !props.event.solidarityPayment) {
|
||||
if (formData.pft_1_description === '') {
|
||||
errors.pft_1_description = 'Eine Beschreibung für diese Gruppe ist erforderlich';
|
||||
noErrors = false;
|
||||
}
|
||||
|
||||
if (formData.pft_2_description === '' && formData.pft_2_active && !props.event.solidarityPayment) {
|
||||
if (formData.pft_2_description === '' && formData.pft_2_active) {
|
||||
errors.pft_2_description = 'Eine Beschreibung für diese Gruppe ist erforderlich';
|
||||
noErrors = false;
|
||||
}
|
||||
|
||||
if (formData.pft_3_description === '' && formData.pft_3_active && !props.event.solidarityPayment) {
|
||||
if (formData.pft_3_description === '' && formData.pft_3_active) {
|
||||
errors.pft_3_description = 'Eine Beschreibung für diese Gruppe ist erforderlich';
|
||||
noErrors = false;
|
||||
}
|
||||
@@ -73,19 +81,27 @@
|
||||
body: {
|
||||
event_id: props.event.id,
|
||||
pft_1_active: formData.pft_1_active,
|
||||
pft_1_amount: formData.pft_1_amount,
|
||||
pft_1_amount_standard: formData.pft_1_amount_standard,
|
||||
pft_1_amount_reduced: formData.pft_1_amount_reduced,
|
||||
pft_1_amount_solidarity: formData.pft_1_amount_solidarity,
|
||||
pft_1_description: formData.pft_1_description,
|
||||
|
||||
pft_2_active: formData.pft_2_active,
|
||||
pft_2_amount: formData.pft_2_amount,
|
||||
pft_2_amount_standard: formData.pft_2_amount_standard,
|
||||
pft_2_amount_reduced: formData.pft_2_amount_reduced,
|
||||
pft_2_amount_solidarity: formData.pft_2_amount_solidarity,
|
||||
pft_2_description: formData.pft_2_description,
|
||||
|
||||
pft_3_active: formData.pft_3_active,
|
||||
pft_3_amount: formData.pft_3_amount,
|
||||
pft_3_amount_standard: formData.pft_3_amount_standard,
|
||||
pft_3_amount_reduced: formData.pft_3_amount_reduced,
|
||||
pft_3_amount_solidarity: formData.pft_3_amount_solidarity,
|
||||
pft_3_description: formData.pft_3_description,
|
||||
|
||||
pft_4_active: formData.pft_4_active,
|
||||
pft_4_amount: formData.pft_4_amount,
|
||||
pft_4_amount_standard: formData.pft_4_amount_standard,
|
||||
pft_4_amount_reduced: formData.pft_4_amount_reduced,
|
||||
pft_4_amount_solidarity: formData.pft_4_amount_solidarity,
|
||||
pft_4_description: formData.pft_4_description,
|
||||
|
||||
maxAmount: formData.maxAmount,
|
||||
@@ -114,29 +130,41 @@
|
||||
<template>
|
||||
<table style="width: 100%;">
|
||||
<tr>
|
||||
<td>Aktiv</td>
|
||||
<td>Preisgruppe</td>
|
||||
<td>Betrag</td>
|
||||
<td>Beschreibung</td>
|
||||
<td><h4>Aktiv</h4></td>
|
||||
<td><h4>Preisgruppe</h4></td>
|
||||
<td v-if="!props.event.solidarityPayment"><h4>Betrag</h4></td>
|
||||
<td v-else><h4>Regulärer Beitrag</h4></td>
|
||||
<td v-if="props.event.solidarityPayment"><h4>Reduzierter Beitrag</h4></td>
|
||||
<td v-if="props.event.solidarityPayment"><h4>Solidaritätsbeitrag</h4></td>
|
||||
<td><h4>Beschreibung</h4></td>
|
||||
</tr>
|
||||
<tr style="height: 65px; vertical-align: top">
|
||||
<td>
|
||||
<input type="checkbox" v-model="formData.participationFeeType_1" checked disabled/>
|
||||
</td>
|
||||
<td v-if="props.event.solidarityPayment">
|
||||
Regulärer Beitrag
|
||||
</td>
|
||||
<td v-else>
|
||||
<td>
|
||||
Teilnehmende
|
||||
</td>
|
||||
<td>
|
||||
<AmountInput v-model="formData.pft_1_amount" class="width-small" @blur="recalculateMaxAmount(formData.pft_1_amount)" />
|
||||
<label v-if="props.event.payPerDay"> Euro / Tag</label>
|
||||
<label v-else> Euro Gesamt</label>
|
||||
<AmountInput v-model="formData.pft_1_amount_standard" class="width-small" @blur="recalculateMaxAmount(formData.pft_1_amount_standard)" />
|
||||
<label style="font-size: 10pt;" v-if="props.event.payPerDay"> Euro / Tag</label>
|
||||
<label style="font-size: 10pt;" v-else> Euro Gesamt</label>
|
||||
</td>
|
||||
|
||||
<td v-if="props.event.solidarityPayment">
|
||||
<AmountInput v-model="formData.pft_1_amount_reduced" class="width-small" @blur="recalculateMaxAmount(formData.pft_1_amount_reduced)" />
|
||||
<label style="font-size: 10pt;" v-if="props.event.payPerDay"> Euro / Tag</label>
|
||||
<label style="font-size: 10pt;" v-else> Euro Gesamt</label>
|
||||
</td>
|
||||
|
||||
<td v-if="props.event.solidarityPayment">
|
||||
<AmountInput v-model="formData.pft_1_amount_solidarity" class="width-small" @blur="recalculateMaxAmount(formData.pft_1_amount_solidarity)" />
|
||||
<label style="font-size: 10pt;" v-if="props.event.payPerDay"> Euro / Tag</label>
|
||||
<label style="font-size: 10pt;" v-else> Euro Gesamt</label>
|
||||
</td>
|
||||
|
||||
<td>
|
||||
<input v-if="!props.event.solidarityPayment" type="text" v-model="formData.pft_1_description" style="width: 300px;" />
|
||||
<label v-else></label>
|
||||
<input type="text" v-model="formData.pft_1_description" style="width: 300px;" />
|
||||
<ErrorText :message="errors.pft_1_description" />
|
||||
</td>
|
||||
</tr>
|
||||
@@ -145,24 +173,32 @@
|
||||
<td>
|
||||
<input id="use_pft_2" type="checkbox" v-model="formData.pft_2_active" :checked="formData.pft_2_active" />
|
||||
</td>
|
||||
<td v-if="props.event.solidarityPayment">
|
||||
<label for="use_pft_2" style="cursor: default">
|
||||
Solidaritätsbeitrag
|
||||
</label>
|
||||
</td>
|
||||
<td v-else>
|
||||
<td>
|
||||
<label for="use_pft_2" style="cursor: default">
|
||||
Kernteam
|
||||
</label>
|
||||
</td>
|
||||
<td v-if="formData.pft_2_active">
|
||||
<AmountInput v-model="formData.pft_2_amount" class="width-small" @blur="recalculateMaxAmount(formData.pft_2_amount)" />
|
||||
<label v-if="props.event.payPerDay"> Euro / Tag</label>
|
||||
<label v-else> Euro Gesamt</label>
|
||||
<AmountInput v-model="formData.pft_2_amount_standard" class="width-small" @blur="recalculateMaxAmount(formData.pft_2_amount_standard)" />
|
||||
<label style="font-size: 10pt;" v-if="props.event.payPerDay"> Euro / Tag</label>
|
||||
<label style="font-size: 10pt;" v-else> Euro Gesamt</label>
|
||||
</td>
|
||||
|
||||
<td v-if="props.event.solidarityPayment && formData.pft_2_active">
|
||||
<AmountInput v-model="formData.pft_2_amount_reduced" class="width-small" @blur="recalculateMaxAmount(formData.pft_2_amount_reduced)" />
|
||||
<label style="font-size: 10pt;" v-if="props.event.payPerDay"> Euro / Tag</label>
|
||||
<label style="font-size: 10pt;" v-else> Euro Gesamt</label>
|
||||
</td>
|
||||
|
||||
<td v-if="props.event.solidarityPayment && formData.pft_2_active">
|
||||
<AmountInput v-model="formData.pft_2_amount_solidarity" class="width-small" @blur="recalculateMaxAmount(formData.pft_2_amount_solidarity)" />
|
||||
<label style="font-size: 10pt;" v-if="props.event.payPerDay"> Euro / Tag</label>
|
||||
<label style="font-size: 10pt;" v-else> Euro Gesamt</label>
|
||||
</td>
|
||||
|
||||
|
||||
<td v-if="formData.pft_2_active">
|
||||
<input v-if="!props.event.solidarityPayment" type="text" v-model="formData.pft_2_description" style="width: 300px;" />
|
||||
<label v-else></label>
|
||||
<input type="text" v-model="formData.pft_2_description" style="width: 300px;" />
|
||||
<ErrorText :message="errors.pft_2_description" />
|
||||
</td>
|
||||
</tr>
|
||||
@@ -171,29 +207,36 @@
|
||||
<td>
|
||||
<input id="use_pft_3" type="checkbox" v-model="formData.pft_3_active" :checked="formData.pft_3_active" />
|
||||
</td>
|
||||
<td v-if="props.event.solidarityPayment">
|
||||
<label for="use_pft_3" style="cursor: default">
|
||||
Reduzierter Beitrag
|
||||
</label>
|
||||
</td>
|
||||
<td v-else>
|
||||
<td>
|
||||
<label for="use_pft_3" style="cursor: default">
|
||||
Unterstützende
|
||||
</label>
|
||||
</td>
|
||||
<td v-if="formData.pft_3_active">
|
||||
<AmountInput v-model="formData.pft_3_amount" class="width-small" @blur="recalculateMaxAmount(formData.pft_3_amount)" />
|
||||
<label v-if="props.event.payPerDay"> Euro / Tag</label>
|
||||
<label v-else> Euro Gesamt</label>
|
||||
<AmountInput v-model="formData.pft_3_amount_standard" class="width-small" @blur="recalculateMaxAmount(formData.pft_3_amount_standard)" />
|
||||
<label style="font-size: 10pt;" v-if="props.event.payPerDay"> Euro / Tag</label>
|
||||
<label style="font-size: 10pt;" v-else> Euro Gesamt</label>
|
||||
</td>
|
||||
|
||||
<td v-if="props.event.solidarityPayment && formData.pft_3_active">
|
||||
<AmountInput v-model="formData.pft_3_amount_reduced" class="width-small" @blur="recalculateMaxAmount(formData.pft_3_amount_reduced)" />
|
||||
<label style="font-size: 10pt;" v-if="props.event.payPerDay"> Euro / Tag</label>
|
||||
<label style="font-size: 10pt;" v-else> Euro Gesamt</label>
|
||||
</td>
|
||||
|
||||
<td v-if="props.event.solidarityPayment && formData.pft_3_active">
|
||||
<AmountInput v-model="formData.pft_3_amount_solidarity" class="width-small" @blur="recalculateMaxAmount(formData.pft_3_amount_solidarity)" />
|
||||
<label style="font-size: 10pt;" v-if="props.event.payPerDay"> Euro / Tag</label>
|
||||
<label style="font-size: 10pt;" v-else> Euro Gesamt</label>
|
||||
</td>
|
||||
|
||||
<td v-if="formData.pft_3_active">
|
||||
<input v-if="!props.event.solidarityPayment" type="text" v-model="formData.pft_3_description" style="width: 300px;" />
|
||||
<label v-else>Nach Verfügbarkeit</label>
|
||||
<input type="text" v-model="formData.pft_3_description" style="width: 300px;" />
|
||||
<ErrorText :message="errors.pft_3_description" />
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
<tr style="height: 65px; vertical-align: top;" v-if="!props.event.solidarityPayment">
|
||||
<tr style="height: 65px; vertical-align: top;">
|
||||
<td>
|
||||
<input id="use_pft_4" type="checkbox" v-model="formData.pft_4_active" :checked="formData.pft_4_active" />
|
||||
</td>
|
||||
@@ -203,10 +246,23 @@
|
||||
</label>
|
||||
</td>
|
||||
<td v-if="formData.pft_4_active">
|
||||
<AmountInput v-model="formData.pft_4_amount" class="width-small" @blur="recalculateMaxAmount(formData.pft_4_amount)" />
|
||||
<label v-if="props.event.payPerDay"> Euro / Tag</label>
|
||||
<label v-else> Euro Gesamt</label>
|
||||
<AmountInput v-model="formData.pft_4_amount_standard" class="width-small" @blur="recalculateMaxAmount(formData.pft_4_amount_standard)" />
|
||||
<label style="font-size: 10pt;" v-if="props.event.payPerDay"> Euro / Tag</label>
|
||||
<label style="font-size: 10pt;" v-else> Euro Gesamt</label>
|
||||
</td>
|
||||
|
||||
<td v-if="props.event.solidarityPayment && formData.pft_4_active">
|
||||
<AmountInput v-model="formData.pft_4_amount_reduced" class="width-small" @blur="recalculateMaxAmount(formData.pft_4_amount_reduced)" />
|
||||
<label style="font-size: 10pt;" v-if="props.event.payPerDay"> Euro / Tag</label>
|
||||
<label style="font-size: 10pt;" v-else> Euro Gesamt</label>
|
||||
</td>
|
||||
|
||||
<td v-if="props.event.solidarityPayment && formData.pft_4_active">
|
||||
<AmountInput v-model="formData.pft_4_amount_solidarity" class="width-small" @blur="recalculateMaxAmount(formData.pft_4_amount_solidarity)" />
|
||||
<label style="font-size: 10pt;" v-if="props.event.payPerDay"> Euro / Tag</label>
|
||||
<label style="font-size: 10pt;" v-else> Euro Gesamt</label>
|
||||
</td>
|
||||
|
||||
<td v-if="formData.pft_4_active">
|
||||
<input type="text" v-model="formData.pft_4_description" style="width: 300px;" />
|
||||
<ErrorText :message="errors.pft_4_description" />
|
||||
|
||||
@@ -43,7 +43,11 @@ const steps = [
|
||||
<template>
|
||||
<div>
|
||||
<!-- Nach Submit -->
|
||||
<SubmitSuccess v-if="submitResult?.type === 'success'" :data="submitResult.data" />
|
||||
<SubmitSuccess
|
||||
v-if="submitResult?.status === 'success'"
|
||||
:participant="submitResult?.participant"
|
||||
:event="event"
|
||||
/>
|
||||
<SubmitAlreadyExists v-else-if="submitResult?.type === 'exists'" :data="submitResult.data" />
|
||||
|
||||
<template v-else>
|
||||
|
||||
@@ -0,0 +1,12 @@
|
||||
<script setup>
|
||||
defineProps({ data: Object })
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div style="padding: 20px 0;">
|
||||
<h3>{{ data.nicename }}</h3>
|
||||
<p>{{ data.text_1 }}</p>
|
||||
<p>{{ data.text_2 }}</p>
|
||||
<a :href="data.email_link" style="color: #2563eb;">{{ data.email_text }}</a>
|
||||
</div>
|
||||
</template>
|
||||
@@ -0,0 +1,60 @@
|
||||
<script setup>
|
||||
const props = defineProps({
|
||||
participant: Object,
|
||||
event: Object,
|
||||
})
|
||||
|
||||
console.log(props.event)
|
||||
console.log(props.participant)
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div style="padding: 20px 0;">
|
||||
<h3>Hallo {{ props.participant.nicename }},</h3>
|
||||
<p>Vielen Dank für dein Interesse an der Veranstaltung {{event.name}}<br />Wir haben folgende Daten erhalten:</p>
|
||||
|
||||
<table class="form-table" style="margin-bottom: 20px;">
|
||||
<tr><td>Anreise:</td><td>{{ props.participant.arrival }}</td></tr>
|
||||
<tr><td>Abreise:</td><td>{{ props.participant.departure }}</td></tr>
|
||||
<tr><td>Teilnahmegruppe:</td><td>{{ props.participant.participation_group }}</td></tr>
|
||||
</table>
|
||||
|
||||
<div v-if="props.participant.efz_status === 'NOT_CHECKED'" style="font-weight: bold; color: #b45309; margin-bottom: 20px;">
|
||||
Dein erweitertes Führungszeugnis konnte nicht automatisch geprüft werden. Bitte kontaktiere die Aktionsleitung.
|
||||
</div>
|
||||
<div v-else-if="props.participant.efz_status === 'CHECKED_INVALID'" style="font-weight: bold; color: #dc2626; margin-bottom: 20px;">
|
||||
Du hast noch kein erweitertes Führungszeugnis hinterlegt. Bitte reiche es umgehend ein.
|
||||
</div>
|
||||
|
||||
<template v-if="props.participant.needs_payment">
|
||||
<table class="form-table" style="margin-bottom: 16px;">
|
||||
<tr>
|
||||
<td>Kontoinhaber:</td><td>{{ props.event.accountOwner }}</td>
|
||||
<td rowspan="4" style="vertical-align: top; padding-left: 20px;" v-if="props.participant.identifier !== ''">
|
||||
<img :src="'/print-girocode/' + props.participant.identifier" alt="GiroCode" style="max-width: 180px;" />
|
||||
<span style="width: 180px; text-align: center; display: block; font-size: 0.8rem; color: #6b7280; margin-top: 4px;">Giro-Code</span>
|
||||
</td>
|
||||
</tr>
|
||||
<tr><td>IBAN:</td><td>{{ props.event.accountIban }}</td></tr>
|
||||
<tr><td>Verwendungszweck:</td><td>{{ props.participant.payment_purpose }}</td></tr>
|
||||
<tr><td>Betrag:</td><td><strong>{{ props.participant.amount_left_string }}</strong></td></tr>
|
||||
</table>
|
||||
|
||||
<p>
|
||||
Bitte beachte, dass deine Anmeldung erst nach Zahlungseiongang vollständig ist.<br />
|
||||
Wenn dieser nicht bis zum {{ props.event.registrationFinalEnd.formatted }} erfolgt, kann deine Anmeldung storniert werden.<br /><br />
|
||||
Solltest du den Beitrag bis zu diesem Datum nicht oder nur teilweise überweisen können, kontaktiere bitte die Aktionsleitung, damit wir eine gemeinsame Lösiung finden können.
|
||||
</p>
|
||||
|
||||
|
||||
</template>
|
||||
<p v-else>
|
||||
Du musst keinen Beitrag überweisen. Deine Anmeldung ist bestätigt.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
Du erhältst innerhalb von 2 Stunden eine E-Mail mit weiteren Informationen.<br />
|
||||
Kontakt: <a :href="'mailto:' + props.event.email" style="color: #2563eb;">{{ props.event.email }}</a>
|
||||
</p>
|
||||
</div>
|
||||
</template>
|
||||
@@ -0,0 +1,98 @@
|
||||
import { ref, reactive, computed } from 'vue'
|
||||
import axios from 'axios'
|
||||
|
||||
export function useSignupForm(event, participantData) {
|
||||
const currentStep = ref(1)
|
||||
const submitting = ref(false)
|
||||
const summaryLoading = ref(false)
|
||||
const submitResult = ref(null) // null | { type: 'success'|'exists', data: {} }
|
||||
|
||||
const selectedAddons = reactive({})
|
||||
console.log(participantData)
|
||||
|
||||
const formData = reactive({
|
||||
eatingHabit: 'EATING_HABIT_VEGAN',
|
||||
userId: participantData.id,
|
||||
eventId: event.id,
|
||||
vorname: participantData.firstname ?? '',
|
||||
nachname: participantData.lastname ?? '',
|
||||
pfadiname: participantData.nickname ?? '',
|
||||
localGroup: participantData.localGroup ?? '-1',
|
||||
geburtsdatum: participantData.birthday ?? '',
|
||||
address1: participantData.address_1 ?? '',
|
||||
address2: participantData.address_2 ?? '',
|
||||
plz: participantData.postcode ?? '',
|
||||
ort: participantData.city ?? '',
|
||||
telefon_1: participantData.phone ?? '',
|
||||
email_1: participantData.email ?? '',
|
||||
participationType: '',
|
||||
ansprechpartner: '',
|
||||
telefon_2: '',
|
||||
email_2: '',
|
||||
badeerlaubnis: '-1',
|
||||
first_aid: '-1',
|
||||
participant_group: '',
|
||||
beitrag: 'regular',
|
||||
arrival: event.eventBeginInternal?.split('T')[0] ?? '',
|
||||
departure: event.eventEndInternal?.split('T')[0] ?? '',
|
||||
anreise_essen: '1',
|
||||
abreise_essen: '2',
|
||||
foto: { socialmedia: false, print: false, webseite: false, partner: false, intern: false },
|
||||
allergien: participantData.allergies ?? '',
|
||||
intolerances: participantData.intolerances ?? '',
|
||||
medikamente: participantData.medications ?? '',
|
||||
tetanusVaccination: participantData.tetanusVaccination ?? '',
|
||||
essgewohnheit: 'vegetarian',
|
||||
anmerkungen: '',
|
||||
summary_information_correct: false,
|
||||
summary_accept_terms: false,
|
||||
legal_accepted: false,
|
||||
payment: false,
|
||||
})
|
||||
|
||||
const summaryAmount = ref('')
|
||||
|
||||
const goToStep = async (step) => {
|
||||
if (step === 9) {
|
||||
summaryLoading.value = true
|
||||
summaryAmount.value = ''
|
||||
try {
|
||||
const res = await axios.post('/api/v1/event/' + event.id + '/calculate-amount', {
|
||||
arrival: formData.arrival,
|
||||
departure: formData.departure,
|
||||
event_id: event.id,
|
||||
participation_group: formData.participant_group,
|
||||
selected_amount: formData.beitrag,
|
||||
addons: selectedAddons,
|
||||
participationType: formData.participationType,
|
||||
beitrag: formData.beitrag,
|
||||
})
|
||||
summaryAmount.value = res.data.amount
|
||||
} finally {
|
||||
summaryLoading.value = false
|
||||
}
|
||||
}
|
||||
currentStep.value = step
|
||||
}
|
||||
|
||||
const submit = async () => {
|
||||
if (!formData.summary_information_correct || !formData.summary_accept_terms || !formData.legal_accepted || !formData.payment) {
|
||||
return
|
||||
}
|
||||
submitting.value = true
|
||||
try {
|
||||
const res = await axios.post('/api/v1/event/'+ event.id + '/signup', {
|
||||
addons: selectedAddons,
|
||||
registration_data: { ...formData },
|
||||
})
|
||||
submitResult.value = {
|
||||
status: res.data.status,
|
||||
participant: res.data.participant,
|
||||
}
|
||||
} finally {
|
||||
submitting.value = false
|
||||
}
|
||||
}
|
||||
|
||||
return { currentStep, goToStep, formData, selectedAddons, submit, submitting, submitResult, summaryLoading, summaryAmount }
|
||||
}
|
||||
@@ -0,0 +1,45 @@
|
||||
<script setup>
|
||||
const props = defineProps({ formData: Object, event: Object, selectedAddons: Object })
|
||||
const emit = defineEmits(['next', 'back'])
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div>
|
||||
<!-- Solidarbeitrag-Auswahl -->
|
||||
<div v-if="event.solidarityPayment" style="margin-bottom: 20px;">
|
||||
<h3>Beitrag</h3>
|
||||
<label v-if="event.participationFee_1?.active" style="display: block; margin-bottom: 8px;">
|
||||
<input type="radio" v-model="formData.beitrag" value="reduced" />
|
||||
{{ event.participationFee_1.name }} ({{ event.participationFee_1.amount }} €)
|
||||
</label>
|
||||
<label style="display: block; margin-bottom: 8px;">
|
||||
<input type="radio" v-model="formData.beitrag" value="regular" />
|
||||
{{ event.participationFee_2?.name ?? 'Regulärer Beitrag' }} ({{ event.participationFee_2?.amount }} €)
|
||||
</label>
|
||||
<label v-if="event.participationFee_3?.active" style="display: block; margin-bottom: 8px;">
|
||||
<input type="radio" v-model="formData.beitrag" value="social" />
|
||||
{{ event.participationFee_3.name }} ({{ event.participationFee_3.amount }} €)
|
||||
</label>
|
||||
</div>
|
||||
|
||||
<!-- Addons -->
|
||||
<div v-if="event.addons?.length > 0">
|
||||
<h3>Zusatzoptionen</h3>
|
||||
<div v-for="addon in event.addons" :key="addon.id" style="margin-bottom: 16px; padding: 12px; background: #f8fafc; border-radius: 8px;">
|
||||
<label style="display: flex; gap: 12px; cursor: pointer;">
|
||||
<input type="checkbox" v-model="selectedAddons[addon.id]" style="margin-top: 4px;" />
|
||||
<span>
|
||||
<strong>{{ addon.name }}</strong>
|
||||
<span style="display: block; color: #6b7280; font-size: 0.875rem;">Betrag: {{ addon.amount }}</span>
|
||||
<span style="display: block; color: #374151; font-size: 0.875rem; margin-top: 4px;">{{ addon.description }}</span>
|
||||
</span>
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="btn-row">
|
||||
<button type="button" class="btn-secondary" @click="emit('back', 5)">← Zurück</button>
|
||||
<button type="button" class="btn-primary" @click="emit('next', 7)">Weiter →</button>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
103
app/Domains/Event/Views/Partials/SignUpForm/steps/StepAge.vue
Normal file
103
app/Domains/Event/Views/Partials/SignUpForm/steps/StepAge.vue
Normal file
@@ -0,0 +1,103 @@
|
||||
|
||||
<script setup>
|
||||
defineProps({ event: Object })
|
||||
const emit = defineEmits(['next'])
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div>
|
||||
<h3 style="margin: 0 0 6px 0; color: #111827;">Wer nimmt teil?</h3>
|
||||
<p style="margin: 0 0 24px 0; color: #6b7280; font-size: 0.95rem;">Bitte wähle deine Altersgruppe aus.</p>
|
||||
|
||||
<div style="display: flex; gap: 20px; flex-wrap: wrap;">
|
||||
|
||||
<!-- Kind / Jugendliche:r -->
|
||||
<div class="age-card" @click="emit('next', 2)">
|
||||
<div class="age-card__badge">
|
||||
<img :src="'/images/children.png'" alt="Abzeichen Kind" class="age-card__img" onerror="this.style.display='none'" />
|
||||
<div class="age-card__badge-fallback">👦</div>
|
||||
</div>
|
||||
<div class="age-card__body">
|
||||
<h4 class="age-card__title">Mein Kind anmelden:</h4>
|
||||
<p class="age-card__desc">Mein Kind ist <strong>jünger als {{ event.alcoholicsAge }} Jahre.</strong></p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Erwachsene:r -->
|
||||
<div class="age-card" @click="emit('next', 3)">
|
||||
<div class="age-card__badge">
|
||||
<img :src="'/images/adults.png'" alt="Abzeichen Erwachsene" class="age-card__img" onerror="this.style.display='none'" />
|
||||
<div class="age-card__badge-fallback">🧑</div>
|
||||
</div>
|
||||
<div class="age-card__body">
|
||||
<h4 class="age-card__title">Mich selbst anmelden</h4>
|
||||
<p class="age-card__desc">Ich bin <strong>{{ event.alcoholicsAge }} Jahre oder älter</strong>.</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
.age-card {
|
||||
flex: 1;
|
||||
min-width: 220px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
background: #f8fafc;
|
||||
border: 2px solid #e5e7eb;
|
||||
border-radius: 12px;
|
||||
padding: 28px 20px;
|
||||
cursor: pointer;
|
||||
transition: border-color 0.15s, box-shadow 0.15s, transform 0.1s;
|
||||
text-align: center;
|
||||
}
|
||||
.age-card:hover {
|
||||
border-color: #2563eb;
|
||||
box-shadow: 0 4px 16px rgba(37, 99, 235, 0.12);
|
||||
transform: translateY(-2px);
|
||||
}
|
||||
|
||||
.age-card__badge {
|
||||
position: relative;
|
||||
width: 350px;
|
||||
height: 200px;
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
.age-card__img {
|
||||
width: 350px;
|
||||
height: 200px;
|
||||
object-fit: contain;
|
||||
}
|
||||
.age-card__badge-fallback {
|
||||
position: absolute;
|
||||
inset: 0;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
font-size: 3rem;
|
||||
background: #e0f2fe;
|
||||
border-radius: 50%;
|
||||
}
|
||||
/* Fallback ausblenden wenn Bild geladen ist */
|
||||
.age-card__img:not([style*="display:none"]) + .age-card__badge-fallback {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.age-card__body { display: flex; flex-direction: column; align-items: center; gap: 6px; }
|
||||
.age-card__title { margin: 0; font-size: 1.1rem; font-weight: 700; color: #111827; }
|
||||
.age-card__desc { margin: 0; font-size: 0.9rem; color: #374151; }
|
||||
.age-card__hint { margin: 0; font-size: 0.8rem; color: #6b7280; }
|
||||
.age-card__cta {
|
||||
margin-top: 10px;
|
||||
display: inline-block;
|
||||
padding: 6px 18px;
|
||||
background: #2563eb;
|
||||
color: white;
|
||||
border-radius: 999px;
|
||||
font-size: 0.85rem;
|
||||
font-weight: 600;
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,52 @@
|
||||
<script setup>
|
||||
const props = defineProps({ formData: Object, event: Object })
|
||||
const emit = defineEmits(['next', 'back'])
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div>
|
||||
<h3>Allergien & Ernährung</h3>
|
||||
<table class="form-table">
|
||||
<tr><td>Allergien:</td><td><input type="text" v-model="props.formData.allergien" /></td></tr>
|
||||
<tr>
|
||||
<td>
|
||||
Letzte Teranus-Impfung:
|
||||
<span style="display: block; font-size: 0.8rem; color: #6b7280; margin-top: 4px;">Lass das Feld frei, wenn die Information nicht vorliegt oder du diese nicht mitteilen willst</span>
|
||||
</td><td><input type="date" v-model="props.formData.tetanusVaccination" /></td></tr>
|
||||
|
||||
|
||||
|
||||
|
||||
<tr><td>Unverträglichkeiten:</td><td><input type="text" v-model="props.formData.intolerances" /></td></tr>
|
||||
<tr>
|
||||
<td>
|
||||
Medikamente:<br />
|
||||
<span style="display: block; font-size: 0.8rem; color: #6b7280; margin-top: 4px;">Bitte in ausreichender Menge mitbringen</span>
|
||||
</td>
|
||||
<td>
|
||||
<input type="text" v-model="props.formData.medikamente" />
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Ernährungsweise:</td>
|
||||
<td>
|
||||
<select v-model="props.formData.eatingHabit">
|
||||
<option
|
||||
v-for="eatingHabit in props.event.eatingHabits"
|
||||
:value="eatingHabit.data.slug">{{eatingHabit.data.name}}</option>
|
||||
</select>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Anmerkungen:</td>
|
||||
<td><textarea rows="5" v-model="props.formData.anmerkungen" style="width: 100%;"></textarea></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td colspan="2" class="btn-row">
|
||||
<button type="button" class="btn-secondary" @click="emit('back', 7)">← Zurück</button>
|
||||
<button type="button" class="btn-primary" @click="emit('next', 9)">Weiter →</button>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</div>
|
||||
</template>
|
||||
@@ -0,0 +1,81 @@
|
||||
<script setup>
|
||||
const props = defineProps({ formData: Object, event: Object })
|
||||
const emit = defineEmits(['next', 'back'])
|
||||
|
||||
const next = () => {
|
||||
const arrival = new Date(props.formData.arrival)
|
||||
arrival.setHours(0,0,0,0);
|
||||
const departure = new Date(props.formData.departure)
|
||||
const eventStart = new Date(props.event.eventBeginInternal)
|
||||
const eventEnd = new Date(props.event.eventEndInternal)
|
||||
|
||||
arrival.setHours(0,0,0,0);
|
||||
departure.setHours(0,0,0,0);
|
||||
eventStart.setHours(0,0,0,0);
|
||||
eventEnd.setHours(0,0,0,0);
|
||||
|
||||
|
||||
if (arrival < eventStart) {
|
||||
alert('Bitte gültige Anreise angeben innerhalb des Veranstaltungszeitraums wählen.')
|
||||
return
|
||||
}
|
||||
|
||||
if (arrival > eventEnd) {
|
||||
alert('Bitte gültige Abreise angeben innerhalb des Veranstaltungszeitraums wählen.')
|
||||
return
|
||||
}
|
||||
|
||||
if (departure < arrival) {
|
||||
alert('Abreise kann niht vor der Anreise liegen. Bitte korrigieren.')
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
|
||||
const hasAddons = (props.event.addons?.length > 0) || props.event.solidarityPayment
|
||||
emit('next', 5)
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
const back = () => emit('back', 3)
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div>
|
||||
<h3>An- und Abreise</h3>
|
||||
<table class="form-table">
|
||||
<tr>
|
||||
<td>Anreise:</td>
|
||||
<td>
|
||||
<input type="date" v-model="formData.arrival" /><br />
|
||||
<select v-model="formData.anreise_essen" style="margin-top: 6px;">
|
||||
<option value="1">Vor dem Abendessen</option>
|
||||
<option value="2">Vor dem Mittagessen</option>
|
||||
<option value="3">Vor dem Frühstück</option>
|
||||
<option value="4">Keine Mahlzeit</option>
|
||||
</select>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Abreise:</td>
|
||||
<td>
|
||||
<input type="date" v-model="formData.departure" /><br />
|
||||
<select v-model="formData.abreise_essen" style="margin-top: 6px;">
|
||||
<option value="1">Nach dem Frühstück</option>
|
||||
<option value="2">Nach dem Mittagessen</option>
|
||||
<option value="3">Nach dem Abendessen</option>
|
||||
<option value="4">Keine Mahlzeit</option>
|
||||
</select>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td colspan="2" class="btn-row">
|
||||
<button type="button" class="btn-secondary" @click="back">← Zurück</button>
|
||||
<button type="button" class="btn-primary" @click="next">Weiter →</button>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</div>
|
||||
</template>
|
||||
@@ -0,0 +1,125 @@
|
||||
<script setup>
|
||||
import ErrorText from "../../../../../../Views/Components/ErrorText.vue";
|
||||
import {reactive} from "vue";
|
||||
|
||||
const props = defineProps({ formData: Object, event: Object })
|
||||
const emit = defineEmits(['next', 'back'])
|
||||
|
||||
const errors = reactive({
|
||||
ansprechpartner: '',
|
||||
telefon_2: '',
|
||||
email_2: '',
|
||||
badeerlaubnis: '',
|
||||
first_aid: '',
|
||||
})
|
||||
|
||||
const next = () => {
|
||||
errors.ansprechpartner = ''
|
||||
errors.telefon_2 = ''
|
||||
errors.email_2 = ''
|
||||
errors.badeerlaubnis = ''
|
||||
errors.first_aid = ''
|
||||
|
||||
let hasError = false
|
||||
|
||||
if (!props.formData.ansprechpartner) {
|
||||
errors.ansprechpartner = 'Bitte eine Kontaktperson angeben.'
|
||||
hasError = true
|
||||
}
|
||||
|
||||
if (!props.formData.telefon_2) {
|
||||
errors.telefon_2 = 'Bitte eine Telefonnummer angeben.'
|
||||
hasError = true
|
||||
}
|
||||
|
||||
if (!props.formData.email_2) {
|
||||
errors.email_2 = 'Bitte eine E-Mail-Adresse angeben.'
|
||||
hasError = true
|
||||
}
|
||||
|
||||
if (props.formData.badeerlaubnis === '-1') {
|
||||
errors.badeerlaubnis = 'Bitte triff eine Entscheidung. Bist du dir unsicher, kontaktiere bitte die Aktionsleitung'
|
||||
hasError = true
|
||||
}
|
||||
|
||||
if (props.formData.first_aid === '-1') {
|
||||
errors.first_aid = 'Bitte triff eine Entscheidung. Bist du dir unsicher, kontaktiere bitte die Aktionsleitung.'
|
||||
hasError = true
|
||||
}
|
||||
|
||||
if (hasError) {
|
||||
return
|
||||
}
|
||||
|
||||
emit('next', 3)
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div>
|
||||
<h3>Kontaktperson</h3>
|
||||
<table class="form-table">
|
||||
<tr>
|
||||
<td>Name (Nachname, Vorname):</td>
|
||||
<td>
|
||||
<input type="text" v-model="formData.ansprechpartner" />
|
||||
<ErrorText :message="errors.ansprechpartner" />
|
||||
</td>
|
||||
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Telefon:</td>
|
||||
<td>
|
||||
<input type="text" v-model="formData.telefon_2" />
|
||||
<ErrorText :message="errors.telefon_2" />
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>E-Mail:</td>
|
||||
<td>
|
||||
<input type="text" v-model="formData.email_2" />
|
||||
<ErrorText :message="errors.email_2" />
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Badeerlaubnis:</td>
|
||||
<td>
|
||||
<select v-model="formData.badeerlaubnis">
|
||||
<option value="-1">Bitte wählen</option>
|
||||
|
||||
<option
|
||||
v-for="swimmingPermission in props.event.swimmingPermissions"
|
||||
:value="swimmingPermission.slug">{{swimmingPermission.name}}</option>
|
||||
</select>
|
||||
<ErrorText :message="errors.badeerlaubnis" />
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Erweiterte Erste Hilfe erlaubt:*</td>
|
||||
<td>
|
||||
<select v-model="formData.first_aid">
|
||||
<option value="-1">Bitte wählen</option>
|
||||
|
||||
<option
|
||||
v-for="firstAidPermission in props.event.firstAidPermissions"
|
||||
:value="firstAidPermission.slug">{{firstAidPermission.name}}</option>
|
||||
</select><br />
|
||||
<span style="font-size: 0.8rem; color: #6b7280;">
|
||||
Nicht dringend-notwendige Erste-Hilfe-Maßnahmen, beinhaltet das Entfernen von Zecken und Splittern sowie das Kleben von Pflastern.
|
||||
</span>
|
||||
<ErrorText :message="errors.first_aid" />
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td></td>
|
||||
<td>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td colspan="2" class="btn-row">
|
||||
<button type="button" class="btn-primary" @click="next">Weiter →</button>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</div>
|
||||
</template>
|
||||
@@ -0,0 +1,124 @@
|
||||
<script setup>
|
||||
import {reactive} from "vue";
|
||||
|
||||
const props = defineProps({ formData: Object, localGroups: Array })
|
||||
const emit = defineEmits(['next', 'back'])
|
||||
|
||||
const errors = reactive({
|
||||
vorname: '',
|
||||
nachname: '',
|
||||
geburtsdatum: '',
|
||||
localGroup: '',
|
||||
address1: '',
|
||||
plz: '',
|
||||
ort: '',
|
||||
telefon_1: '',
|
||||
email_1: '',
|
||||
})
|
||||
|
||||
const next = () => {
|
||||
errors.vorname = ''
|
||||
errors.nachname = ''
|
||||
errors.geburtsdatum = ''
|
||||
errors.localGroup = ''
|
||||
errors.address1 = ''
|
||||
errors.plz = ''
|
||||
errors.ort = ''
|
||||
errors.telefon_1 = ''
|
||||
errors.email_1 = ''
|
||||
|
||||
let hasError = false
|
||||
|
||||
if (!props.formData.vorname) {
|
||||
errors.vorname = 'Bitte den Vornamen angeben.'
|
||||
hasError = true
|
||||
}
|
||||
|
||||
if (!props.formData.nachname) {
|
||||
errors.nachname = 'Bitte den Nachnamen angeben.'
|
||||
hasError = true
|
||||
}
|
||||
|
||||
if (!props.formData.geburtsdatum) {
|
||||
errors.geburtsdatum = 'Bitte das Geburtsdatum angeben.'
|
||||
hasError = true
|
||||
}
|
||||
|
||||
if (props.formData.localGroup === '-1') {
|
||||
errors.localGroup = 'Bitte den Stamm auswählen.'
|
||||
hasError = true
|
||||
}
|
||||
|
||||
if (!props.formData.address1) {
|
||||
errors.address1 = 'Bitte die Adresse angeben.'
|
||||
hasError = true
|
||||
}
|
||||
|
||||
if (!props.formData.plz) {
|
||||
errors.plz = 'Bitte die Postleitzahl angeben.'
|
||||
hasError = true
|
||||
}
|
||||
|
||||
if (!props.formData.ort) {
|
||||
errors.ort = 'Bitte den Ort angeben.'
|
||||
hasError = true
|
||||
}
|
||||
|
||||
if (!props.formData.email_1) {
|
||||
errors.email_1 = 'Bitte eine E-Mail-Adresse angeben.'
|
||||
hasError = true
|
||||
}
|
||||
|
||||
if (hasError) {
|
||||
return
|
||||
}
|
||||
emit('next', 4)
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div>
|
||||
<h3>Persönliche Daten</h3>
|
||||
<table class="form-table">
|
||||
<tr><td>Vorname:</td><td><input type="text" v-model="props.formData.vorname" /></td></tr>
|
||||
<tr><td>Nachname:</td><td><input type="text" v-model="props.formData.nachname" /></td></tr>
|
||||
<tr><td>Pfadiname:</td><td><input type="text" v-model="props.formData.pfadiname" /></td></tr>
|
||||
<tr>
|
||||
<td>Stamm:</td>
|
||||
<td>
|
||||
<select v-model="props.formData.localGroup">
|
||||
<option value="-1">Bitte wählen</option>
|
||||
<option v-for="lg in localGroups" :key="lg.id" :value="lg.id">{{ lg.name }}</option>
|
||||
</select>
|
||||
</td>
|
||||
</tr>
|
||||
<tr><td>Geburtsdatum:</td><td><input type="date" v-model="props.formData.geburtsdatum" /></td></tr>
|
||||
<tr>
|
||||
<td>Adresse:</td>
|
||||
<td>
|
||||
<input type="text" v-model="props.formData.address1" />
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td></td>
|
||||
<td>
|
||||
<input type="text" v-model="props.formData.address2" />
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>PLZ, Ort:</td>
|
||||
<td>
|
||||
<input maxlength="5" type="text" v-model="props.formData.plz" style="width: 100px; margin-right: 8px;" />
|
||||
<input type="text" v-model="props.formData.ort" style="width: calc(100% - 110px);" />
|
||||
</td>
|
||||
</tr>
|
||||
<tr><td>Telefon:</td><td><input type="text" v-model="props.formData.telefon_1" /></td></tr>
|
||||
<tr><td>E-Mail:</td><td><input type="text" v-model="props.formData.email_1" /></td></tr>
|
||||
<tr>
|
||||
<td colspan="2" class="btn-row">
|
||||
<button type="button" class="btn-primary" @click="next">Weiter →</button>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</div>
|
||||
</template>
|
||||
@@ -0,0 +1,33 @@
|
||||
<script setup>
|
||||
const props = defineProps({ formData: Object, event: Object })
|
||||
const emit = defineEmits(['next', 'back'])
|
||||
|
||||
const acceptAll = () => {
|
||||
Object.keys(props.formData.foto).forEach(k => props.formData.foto[k] = true)
|
||||
emit('next', 8)
|
||||
}
|
||||
|
||||
const back = () => {
|
||||
const hasAddons = (props.event.addons?.length > 0) || props.event.solidarityPayment
|
||||
emit('back', hasAddons ? 6 : 5)
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div>
|
||||
<h3>Fotoerlaubnis</h3>
|
||||
<div v-for="[key, label] in [['socialmedia','Social Media'],['print','Printmedien'],['webseite','Webseite'],['partner','Partnerorganisationen'],['intern','Interne Zwecke']]"
|
||||
:key="key"
|
||||
style="margin-bottom: 10px;">
|
||||
<label style="display: flex; gap: 10px; cursor: pointer;">
|
||||
<input type="checkbox" v-model="formData.foto[key]" />
|
||||
{{ label }}
|
||||
</label>
|
||||
</div>
|
||||
<div class="btn-row">
|
||||
<button type="button" class="btn-secondary" @click="back">← Zurück</button>
|
||||
<button type="button" class="btn-primary" style="background: #059669;" @click="acceptAll">Alle akzeptieren & weiter</button>
|
||||
<button type="button" class="btn-primary" @click="emit('next', 8)">Weiter →</button>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
@@ -0,0 +1,130 @@
|
||||
<script setup>
|
||||
import { watch } from "vue";
|
||||
|
||||
const props = defineProps({ formData: Object, event: Object })
|
||||
const emit = defineEmits(['next', 'back'])
|
||||
|
||||
watch(
|
||||
() => props.formData.participationType,
|
||||
(value) => {
|
||||
if (!value) {
|
||||
props.formData.beitrag = 'standard'
|
||||
return
|
||||
}
|
||||
|
||||
props.formData.beitrag = 'standard'
|
||||
}
|
||||
)
|
||||
|
||||
const nextStep = () => {
|
||||
const hasAddons = (props.event.addons?.length ?? 0) > 0
|
||||
emit('next', hasAddons ? 6 : 7)
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div>
|
||||
<h3 v-if="event.solidarityPayment">Solidarbeitrag – Teilnahmegruppe</h3>
|
||||
<h3 v-else>Ich nehme teil als ...</h3>
|
||||
|
||||
<table style="width: 100%;">
|
||||
<tr
|
||||
v-for="participationType in props.event.participationTypes"
|
||||
:key="participationType.type.slug"
|
||||
style="vertical-align: top;"
|
||||
>
|
||||
<td style="width: 50px; padding-top: 6px;">
|
||||
<input
|
||||
:id="participationType.type.slug"
|
||||
v-model="props.formData.participationType"
|
||||
type="radio"
|
||||
:value="participationType.type.slug"
|
||||
/>
|
||||
</td>
|
||||
<td style="padding-bottom: 16px;">
|
||||
<label :for="participationType.type.slug" style="line-height: 1.5; font-weight: 600; cursor: pointer;">
|
||||
{{ participationType.type.name }}
|
||||
</label><br />
|
||||
|
||||
<label
|
||||
:for="participationType.type.slug"
|
||||
style="line-height: 1.5; padding-left: 15px; font-style: italic; color: #606060; cursor: pointer;"
|
||||
>
|
||||
{{ participationType.description }}
|
||||
</label>
|
||||
|
||||
<div
|
||||
v-if="props.formData.participationType === participationType.type.slug"
|
||||
style="margin-top: 10px; margin-left: 15px; padding: 12px 14px; background: #f8fafc; border-left: 3px solid #2563eb; border-radius: 6px;"
|
||||
>
|
||||
<template
|
||||
v-if="participationType.amount_reduced !== null || participationType.amount_solidarity !== null"
|
||||
>
|
||||
<div style="margin-bottom: 8px; font-size: 0.95rem; font-weight: 600; color: #374151;">
|
||||
Beitrag auswählen
|
||||
</div>
|
||||
|
||||
<label style="display: block; margin-bottom: 8px; cursor: pointer;">
|
||||
<input type="radio" v-model="props.formData.beitrag" value="standard" />
|
||||
Standardbeitrag
|
||||
<span style="color: #606060;">
|
||||
({{ participationType.amount_standard.readable }}
|
||||
<template v-if="props.event.payPerDay">/ Tag</template>
|
||||
<template v-else>Gesamt</template>)
|
||||
</span>
|
||||
</label>
|
||||
|
||||
<label
|
||||
v-if="participationType.amount_reduced !== null"
|
||||
style="display: block; margin-bottom: 8px; cursor: pointer;"
|
||||
>
|
||||
<input type="radio" v-model="props.formData.beitrag" value="reduced" />
|
||||
Reduzierter Beitrag
|
||||
<span style="color: #606060;">
|
||||
({{ participationType.amount_reduced.readable }}
|
||||
<template v-if="props.event.payPerDay">/ Tag</template>
|
||||
<template v-else>Gesamt</template>)
|
||||
</span>
|
||||
</label>
|
||||
|
||||
<label
|
||||
v-if="participationType.amount_solidarity !== null"
|
||||
style="display: block; margin-bottom: 0; cursor: pointer;"
|
||||
>
|
||||
<input type="radio" v-model="props.formData.beitrag" value="solidarity" />
|
||||
Solidaritätsbeitrag
|
||||
<span style="color: #606060;">
|
||||
({{ participationType.amount_solidarity.readable }}
|
||||
<template v-if="props.event.payPerDay">/ Tag</template>
|
||||
<template v-else>Gesamt</template>)
|
||||
</span>
|
||||
</label>
|
||||
</template>
|
||||
|
||||
<template v-else>
|
||||
<div style="font-size: 0.9rem; color: #606060;">
|
||||
Standardbeitrag:
|
||||
<strong>{{ participationType.amount_standard.readable }}</strong>
|
||||
<template v-if="props.event.payPerDay">/ Tag</template>
|
||||
<template v-else>Gesamt</template>
|
||||
</div>
|
||||
</template>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
<div class="btn-row">
|
||||
<button type="button" class="btn-secondary" @click="emit('back', 3)">← Zurück</button>
|
||||
<button
|
||||
type="button"
|
||||
v-if="props.formData.participationType"
|
||||
class="btn-primary"
|
||||
@click="nextStep"
|
||||
>
|
||||
Weiter →
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@@ -0,0 +1,63 @@
|
||||
<script setup>
|
||||
import {format, parseISO} from "date-fns";
|
||||
|
||||
const props = defineProps({
|
||||
formData: Object,
|
||||
event: Object,
|
||||
summaryAmount: String,
|
||||
summaryLoading: Boolean,
|
||||
submitting: Boolean,
|
||||
})
|
||||
const emit = defineEmits(['back', 'submit'])
|
||||
|
||||
function formatDate(dateString) {
|
||||
if (!dateString) return ''
|
||||
return format(parseISO(dateString), 'dd.MM.yyyy')
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div>
|
||||
<h3>Zusammenfassung</h3>
|
||||
|
||||
<div v-if="summaryLoading" style="color: #6b7280; padding: 20px 0;">Wird geladen…</div>
|
||||
<div v-else>
|
||||
<table class="form-table" style="margin-bottom: 20px;">
|
||||
<tr><td>Veranstaltung:</td><td><strong>{{ event.name }}</strong></td></tr>
|
||||
<tr><td>Anreise:</td><td>{{ formatDate(formData.arrival) }}</td></tr>
|
||||
<tr><td>Abreise:</td><td>{{ formatDate(formData.departure) }}</td></tr>
|
||||
</table>
|
||||
|
||||
<div style="display: flex; flex-direction: column; gap: 10px; margin-bottom: 20px;">
|
||||
<label style="display: flex; gap: 10px; cursor: pointer;">
|
||||
<input type="checkbox" v-model="formData.summary_information_correct" />
|
||||
Ich bestätige, dass alle Angaben korrekt sind.
|
||||
</label>
|
||||
<label style="display: flex; gap: 10px; cursor: pointer;">
|
||||
<input type="checkbox" v-model="formData.summary_accept_terms" />
|
||||
Ich akzeptiere die Teilnahmebedingungen.
|
||||
</label>
|
||||
<label style="display: flex; gap: 10px; cursor: pointer;">
|
||||
<input type="checkbox" v-model="formData.legal_accepted" />
|
||||
Ich stimme der Datenschutzerklärung zu.
|
||||
</label>
|
||||
<label style="display: flex; gap: 10px; cursor: pointer;">
|
||||
<input type="checkbox" v-model="formData.payment" />
|
||||
Ich bestätige, den Betrag von <strong>{{ summaryAmount }}</strong> zu überweisen.
|
||||
</label>
|
||||
</div>
|
||||
|
||||
<div class="btn-row">
|
||||
<button type="button" class="btn-secondary" @click="emit('back', 8)">← Zurück</button>
|
||||
<button
|
||||
type="submit"
|
||||
class="btn-primary"
|
||||
:disabled="!formData.summary_information_correct || !formData.summary_accept_terms || !formData.legal_accepted || !formData.payment || submitting"
|
||||
style="background: #059669;"
|
||||
>
|
||||
{{ submitting ? 'Wird gesendet…' : 'Jetzt anmelden ✓' }}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
Reference in New Issue
Block a user