Signup for events implemented

This commit is contained in:
2026-03-22 00:06:03 +01:00
parent b8341890d3
commit 405591d6dd
13 changed files with 428 additions and 24 deletions

View File

@@ -7,8 +7,6 @@ use App\Providers\InertiaProvider;
use App\Scopes\CommonController;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Js;
class DashboardController extends CommonController {
public function __invoke(Request $request) {

View File

@@ -6,17 +6,16 @@ use App\Domains\Event\Actions\CertificateOfConductionCheck\CertificateOfConducti
use App\Domains\Event\Actions\CertificateOfConductionCheck\CertificateOfConductionCheckRequest;
use App\Domains\Event\Actions\SignUp\SignUpCommand;
use App\Domains\Event\Actions\SignUp\SignUpRequest;
use App\Mail\EventSignUpSuccessfull;
use App\Models\Tenant;
use App\Models\User;
use App\Providers\DoubleCheckEventRegistrationProvider;
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 DateTime;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request;
use Illuminate\Http\Resources\Json\JsonResource;
use Illuminate\Support\Facades\Mail;
class SignupController extends CommonController {
public function __invoke(int $eventId, Request $request) {
@@ -76,11 +75,17 @@ class SignupController extends CommonController {
$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
$doubleCheckEventRegistrationProvider = new DoubleCheckEventRegistrationProvider(
$event,
$registrationData['vorname'],
$registrationData['nachname'],
$registrationData['email_1'],
DateTime::createFromFormat('Y-m-d', $registrationData['geburtsdatum']));
if ($doubleCheckEventRegistrationProvider->isRegistered()) {
return response()->json(['status' => 'exists']);
}
//
$amount = $eventResource->calculateAmount(
$registrationData['participationType'],
$registrationData['beitrag'],
@@ -138,9 +143,15 @@ class SignupController extends CommonController {
$signupResponse->participant->efz_status = $certificateOfConductionCheckResponse->status;
$signupResponse->participant->save();
// 6. E-Mail senden & Bestätigung senden
Mail::to($signupResponse->participant->email_1)->send(new EventSignUpSuccessfull(
participant: $signupResponse->participant,
));
if ($signupResponse->participant->email_2 !== null) {
Mail::to($signupResponse->participant->email_2)->send(new EventSignUpSuccessfull(
participant: $signupResponse->participant,
));
}
return response()->json(
[

View File

@@ -18,8 +18,6 @@ const props = defineProps({
localGroups: Array,
})
console.log(props.participantData);
const emit = defineEmits(['registrationDone'])
const {
@@ -48,7 +46,7 @@ const steps = [
:participant="submitResult?.participant"
:event="event"
/>
<SubmitAlreadyExists v-else-if="submitResult?.type === 'exists'" :data="submitResult.data" />
<SubmitAlreadyExists v-else-if="submitResult?.status === 'exists'" :event="event" />
<template v-else>
<!-- Fortschrittsleiste (ab Step 2) -->

View File

@@ -1,12 +1,18 @@
<script setup>
defineProps({ data: Object })
const props = defineProps({
event: 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>
<h3>Registrierung nicht möglich</h3>
<p>
Leider konnte deine Anmeldung nicht ausgeführt werden, da du bereits für die Veranstaltung {{props.event.name}} angemeldet bist.
</p>
<p>
Falls du bereits angemeldet warst und abgemeldet wurdest, oder andere Fragen hast, kontaktiere die Veranstaltungsleitung:
<a href="mailto:{{props.event.email}}">{{props.event.email}}</a>
</p>
</div>
</template>

View File

@@ -16,7 +16,7 @@ console.log(props.participant)
<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>
<tr><td>Teilnahmegruppe:</td><td>{{ props.participant.participationType }}</td></tr>
</table>
<div v-if="props.participant.efz_status === 'NOT_CHECKED'" style="font-weight: bold; color: #b45309; margin-bottom: 20px;">

View File

@@ -14,6 +14,35 @@ function formatDate(dateString) {
if (!dateString) return ''
return format(parseISO(dateString), 'dd.MM.yyyy')
}
function participationGroup() {
if (props.formData.participationType === 'team') {
return 'Kernteam';
}
if (props.formData.participationType === 'participant') {
return 'Teilnehmende';
}
if (props.formData.participationType === 'volunteer') {
return 'Unterstützende';
}
return 'Sonstige';
}
function eatingHabit() {
if (props.formData.eatingHabit === 'EATING_HABIT_VEGAN') {
return 'Vegan';
}
if (props.formData.eatingHabit === 'EATING_HABIT_VEGETARIAN') {
return 'Vegetarisch';
}
return 'Omnivor';
}
</script>
<template>
@@ -23,6 +52,65 @@ function formatDate(dateString) {
<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>Dein Name:</td>
<td>{{props.formData.vorname}} {{props.formData.vorname}}</td>
</tr>
<tr>
<td>Deine E-Mail:</td>
<td>{{props.formData.email_1}}</td>
</tr>
<tr v-if="props.formData.ansprechpartner !== ''">
<td>Name deiner Kontaktperson:</td>
<td>{{props.formData.ansprechpartner}}</td>
</tr>
<tr v-if="props.formData.email_2 !== ''">
<td>E-Mail-Adresse deiner Kontaktperson:</td>
<td>{{props.formData.email_2}}</td>
</tr>
<tr v-if="props.formData.telefon_2 !== ''">
<td>Telefonnummer deiner Kontaktperson:</td>
<td>{{props.formData.telefon_2}}</td>
</tr>
<tr>
<td>Teilnahmegruppe:</td>
<td>{{ participationGroup() }}</td>
</tr>
<tr>
<td>Foto-Erlaubnis:</td>
<td>
<strong>Social Media:</strong> {{props.formData.foto.socialmedia ? 'Ja' : 'Nein'}},
<strong>Printmedien:</strong> {{props.formData.foto.print ? 'Ja' : 'Nein'}},
<strong>Webseite:</strong> {{props.formData.foto.webseite ? 'Ja' : 'Nein'}},
<strong>Partnerorganisationen:</strong> {{props.formData.foto.partner ? 'Ja' : 'Nein'}},
<strong>Interne Zwecke:</strong> {{props.formData.foto.intern ? 'Ja' : 'Nein'}}
</td>
</tr>
<tr>
<td>Allergien:</td>
<td>{{props.formData.allergien}}</td>
</tr>
<tr>
<td>Lebensmittelunverträglichkeiten:</td>
<td>{{props.formData.intolerances}}</td>
</tr>
<tr>
<td>Ernährungsweise:</td>
<td>{{eatingHabit()}}</td>
</tr>
<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>

View File

@@ -0,0 +1,97 @@
<?php
namespace App\Mail;
use App\Enumerations\EfzStatus;
use App\Models\EventParticipant;
use App\Resources\EventParticipantResource;
use Illuminate\Bus\Queueable;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Http\Request;
use Illuminate\Mail\Mailable;
use Illuminate\Mail\Mailables\Attachment;
use Illuminate\Mail\Mailables\Content;
use Illuminate\Mail\Mailables\Envelope;
use Illuminate\Queue\SerializesModels;
class EventSignUpSuccessfull extends Mailable
{
use Queueable, SerializesModels;
/**
* Create a new message instance.
*/
public function __construct(
private EventParticipant $participant,
)
{
//
}
/**
* Get the message envelope.
*/
public function envelope(): Envelope
{
$participant = $this->participant->toResource()->toArray(new Request());
$subject = sprintf(
$participant['needs_payment'] ? 'Teilnahme- & Zahlungsinformationen %1$s %2$s' : 'Anmeldebestätigung %1$s %2$s',
'für die Veranstaltung',
$this->participant->event()->first()->name
);
return new Envelope(
subject: $subject,
);
}
/**
* Get the message content definition.
*/
public function content(): Content
{
$event = $this->participant->event()->first()->toResource()->toArray(new Request());
$participant = $this->participant->toResource()->toArray(new Request());
$girocodeProvider = new \App\Providers\GiroCodeProvider(
$event['accountOwner'],
$event['accountIban'],
(float) $participant['amount_left_value'],
$participant['payment_purpose']
);
$girocodeBinary = (string)$girocodeProvider->create();
return new Content(
view: 'emails.events.signup_complete',
with: [
'participationType' => $participant['participationType'],
'name' => $participant['nicename'],
'eventTitle' => $event['name'],
'eventEmail' => $event['email'],
'arrival' => $participant['arrival'],
'departure' => $participant['departure'],
'amount' => $participant['amount_left_string'],
'paymentFinalDate' => $event['registrationFinalEnd']['formatted'],
'paymentRequired' => $participant['needs_payment'],
'accountOwner' => $event['accountOwner'],
'accountIban' => $event['accountIban'],
'paymentPurpose' => $participant['payment_purpose'],
'girocodeBinary' => $girocodeBinary,
'efzStatus' => $participant['efz_status']
],
);
}
/**
* Get the attachments for the message.
*
* @return array<int, Attachment>
*/
public function attachments(): array
{
return [];
}
}

View File

@@ -0,0 +1,30 @@
<?php
namespace App\Providers;
use App\Models\Event;
use DateTime;
class DoubleCheckEventRegistrationProvider {
function __construct(
private Event $event,
private string $firstname,
private string $lastname,
private string $email,
private DateTime $birthday,
) {}
public function isRegistered() : bool
{
$checkconditions = array(
'firstname' => $this->firstname,
'lastname' => $this->lastname,
'email_1' => $this->email,
'birthday' => $this->birthday->format('Y-m-d'),
);
return $this->event->participants()->where($checkconditions)->exists();
}
}

View File

@@ -2,6 +2,7 @@
namespace App\Resources;
use App\Enumerations\ParticipationType;
use App\Models\EventParticipant;
use Illuminate\Http\Resources\Json\JsonResource;
@@ -16,16 +17,23 @@ class EventParticipantResource extends JsonResource
{
$event = $this->resource->event;
$amountLeft = $this->resource->amount;
if ($this->resource->amount_paid !== null) {
$amountLeft->subtractAmount($this->resource->amount_paid);
}
return array_merge(
$this->resource->toArray(),
[
'participationType' => ParticipationType::where(['slug' => $this->resource->participation_type])->first()->name,
'needs_payment' => $this->resource->amount->getAmount() > 0 && $event->pay_direct,
'nicename' => $this->resource->getNicename(),
'arrival' => $this->resource->arrival_date->format('d.m.Y'),
'departure' => $this->resource->departure_date->format('d.m.Y'),
'amount_left_string' => $this->resource->amount->toString(),
'amount_left_string' => $amountLeft->toString(),
'amount_left_value' => $amountLeft->getAmount(),
'email_1' => $this->resource->email_1,
]
);