GUI for Participant management
This commit is contained in:
@@ -0,0 +1,21 @@
|
||||
<?php
|
||||
|
||||
namespace App\Domains\Event\Actions\ManualCertificateOfConductionCheck;
|
||||
|
||||
use App\Enumerations\EfzStatus;
|
||||
use Illuminate\Support\Facades\Http;
|
||||
|
||||
class ManualCertificateOfConductionCheckCommand {
|
||||
function __construct(public ManualCertificateOfConductionCheckRequest $request)
|
||||
{}
|
||||
|
||||
public function execute() : ManualCertificateOfConductionCheckResponse {
|
||||
$response = new ManualCertificateOfConductionCheckResponse();
|
||||
|
||||
|
||||
$this->request->participant->efz_status = EfzStatus::EFZ_STATUS_CHECKED_VALID;
|
||||
$this->request->participant->save();
|
||||
$response->success = true;
|
||||
return $response;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
<?php
|
||||
|
||||
namespace App\Domains\Event\Actions\ManualCertificateOfConductionCheck;
|
||||
|
||||
use App\Models\EventParticipant;
|
||||
|
||||
class ManualCertificateOfConductionCheckRequest {
|
||||
function __construct(public EventParticipant $participant)
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
<?php
|
||||
|
||||
namespace App\Domains\Event\Actions\ManualCertificateOfConductionCheck;
|
||||
|
||||
use App\Enumerations\EfzStatus;
|
||||
|
||||
class ManualCertificateOfConductionCheckResponse {
|
||||
public bool $success;
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
$this->success = false;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
<?php
|
||||
|
||||
namespace App\Domains\Event\Actions\ParticipantPayment;
|
||||
|
||||
class ParticipantPaymentCommand {
|
||||
public function __construct(public ParticipantPaymentRequest $request) {
|
||||
}
|
||||
|
||||
public function execute() : ParticipantPaymentResponse {
|
||||
$response = new ParticipantPaymentResponse();
|
||||
|
||||
$this->request->participant->amount_paid = $this->request->amountPaid;
|
||||
$this->request->participant->save();
|
||||
|
||||
$response->success = true;
|
||||
|
||||
return $response;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
<?php
|
||||
|
||||
namespace App\Domains\Event\Actions\ParticipantPayment;
|
||||
|
||||
use App\Models\EventParticipant;
|
||||
use App\ValueObjects\Amount;
|
||||
|
||||
class ParticipantPaymentRequest {
|
||||
public EventParticipant $participant;
|
||||
public Amount $amountPaid;
|
||||
|
||||
public function __construct(EventParticipant $participant, Amount $amountPaid) {
|
||||
$this->participant = $participant;
|
||||
$this->amountPaid = $amountPaid;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
<?php
|
||||
|
||||
namespace App\Domains\Event\Actions\ParticipantPayment;
|
||||
|
||||
class ParticipantPaymentResponse {
|
||||
public bool $success = false;
|
||||
}
|
||||
@@ -19,6 +19,7 @@ use App\ValueObjects\Amount;
|
||||
use Illuminate\Http\JsonResponse;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Http\Response;
|
||||
use function Symfony\Component\String\b;
|
||||
|
||||
class DetailsController extends CommonController {
|
||||
public function __invoke(int $eventId) {
|
||||
@@ -170,4 +171,25 @@ class DetailsController extends CommonController {
|
||||
'Content-Disposition' => 'attachment; filename="' . $listType . '.csv"',
|
||||
]);
|
||||
}
|
||||
|
||||
public function listParticipants(string $eventId, string $listType, Request $request) : JsonResponse {
|
||||
$event = $this->events->getByIdentifier($eventId);
|
||||
switch ($listType) {
|
||||
case 'by-local-group':
|
||||
$participants = $this->eventParticipants->groupByLocalGroup($event, $request);
|
||||
break;
|
||||
case 'by-participation-group':
|
||||
$participants = $this->eventParticipants->groupByParticipationType($event, $request);
|
||||
break;
|
||||
case 'signed-off':
|
||||
break;
|
||||
default:
|
||||
$participants = ['Alle Teilnehmenden' => $this->eventParticipants->getForList($event, $request)];
|
||||
}
|
||||
|
||||
return response()->json([
|
||||
'participants' => $participants,
|
||||
'event' => $event->toResource()->toArray($request)
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
40
app/Domains/Event/Controllers/ParticipantController.php
Normal file
40
app/Domains/Event/Controllers/ParticipantController.php
Normal file
@@ -0,0 +1,40 @@
|
||||
<?php
|
||||
|
||||
namespace App\Domains\Event\Controllers;
|
||||
|
||||
use App\Domains\Event\Actions\ManualCertificateOfConductionCheck\ManualCertificateOfConductionCheckCommand;
|
||||
use App\Domains\Event\Actions\ManualCertificateOfConductionCheck\ManualCertificateOfConductionCheckRequest;
|
||||
use App\Domains\Event\Actions\ParticipantPayment\ParticipantPaymentCommand;
|
||||
use App\Domains\Event\Actions\ParticipantPayment\ParticipantPaymentRequest;
|
||||
use App\Models\EventParticipant;
|
||||
use App\Scopes\CommonController;
|
||||
use Illuminate\Support\Facades\Request;
|
||||
|
||||
class ParticipantController extends CommonController {
|
||||
public function paymentComplete(string $participantIdentifier, Request $request) {
|
||||
$participant = $this->eventParticipants->getByIdentifier($participantIdentifier, $this->events, false);
|
||||
|
||||
$paymentRequest = new ParticipantPaymentRequest($participant, $participant->amount);
|
||||
|
||||
$paymentCommand = new ParticipantPaymentCommand($paymentRequest);
|
||||
$paymentResponse = $paymentCommand->execute();
|
||||
|
||||
return response()->json([
|
||||
'status' => $paymentResponse->success ? 'success' : 'error',
|
||||
'message' => $paymentResponse->success ? 'Die Zahlung wurde erfolgreich gebucht.' : 'Beim Buchen der Zahlung ist ein Fehler aufgetreten.'
|
||||
]);
|
||||
}
|
||||
|
||||
public function markCocExisting(string $participantIdentifier, Request $request) {
|
||||
$participant = $this->eventParticipants->getByIdentifier($participantIdentifier, $this->events, false);
|
||||
|
||||
$cocRequest = new ManualCertificateOfConductionCheckRequest($participant);
|
||||
$cocCommand = new ManualCertificateOfConductionCheckCommand($cocRequest);
|
||||
$cocResponse = $cocCommand->execute();
|
||||
|
||||
return response()->json([
|
||||
'status' => $cocResponse->success ? 'success' : 'error',
|
||||
'message' => $cocResponse->success ? 'Das eFZ wurde als gültig hinterlegt' : 'Beim Aktualisieren des eFZ-Status ist ein Fehler aufgetreten.'
|
||||
]);
|
||||
}
|
||||
}
|
||||
@@ -2,6 +2,7 @@
|
||||
|
||||
use App\Domains\Event\Controllers\CreateController;
|
||||
use App\Domains\Event\Controllers\DetailsController;
|
||||
use App\Domains\Event\Controllers\ParticipantController;
|
||||
use App\Domains\Event\Controllers\SignupController;
|
||||
use App\Middleware\IdentifyTenant;
|
||||
use Illuminate\Support\Facades\Route;
|
||||
@@ -19,7 +20,7 @@ Route::prefix('api/v1')
|
||||
Route::prefix('/details/{eventId}') ->group(function () {
|
||||
Route::get('/summary', [DetailsController::class, 'summary']);
|
||||
|
||||
|
||||
Route::get('/participants/{listType}', [DetailsController::class, 'listParticipants']);
|
||||
|
||||
|
||||
Route::post('/event-managers', [DetailsController::class, 'updateEventManagers']);
|
||||
@@ -28,6 +29,10 @@ Route::prefix('api/v1')
|
||||
});
|
||||
|
||||
|
||||
Route::prefix('/participant/{participantIdentifier}')->group(function () {
|
||||
Route::post('/payment-complete', [ParticipantController::class, 'paymentComplete']);
|
||||
Route::post('/mark-coc-existing', [ParticipantController::class, 'markCocExisting']);
|
||||
});
|
||||
|
||||
});
|
||||
});
|
||||
|
||||
@@ -3,7 +3,7 @@ import {reactive, inject, onMounted} from 'vue';
|
||||
import AppLayout from "../../../../resources/js/layouts/AppLayout.vue";
|
||||
import ShadowedBox from "../../../Views/Components/ShadowedBox.vue";
|
||||
import TabbedPage from "../../../Views/Components/TabbedPage.vue";
|
||||
import ListCostUnits from "../../CostUnit/Views/Partials/ListCostUnits.vue";
|
||||
import ParticipantsList from "./Partials/ParticipantsList.vue";
|
||||
import Overview from "./Partials/Overview.vue";
|
||||
|
||||
const props = defineProps({
|
||||
@@ -18,27 +18,27 @@ const tabs = [
|
||||
},
|
||||
{
|
||||
title: 'Alle Teilnehmendenden',
|
||||
component: ListCostUnits,
|
||||
endpoint: "/api/v1/cost-unit/open/current-running-jobs",
|
||||
component: ParticipantsList,
|
||||
endpoint: "/api/v1/event/details/" + props.event.identifier + '/participants/all',
|
||||
},
|
||||
{
|
||||
title: 'Teilis nach Stamm',
|
||||
component: ListCostUnits,
|
||||
endpoint: "/api/v1/cost-unit/open/closed-cost-units",
|
||||
component: ParticipantsList,
|
||||
endpoint: "/api/v1/event/details/" + props.event.identifier + '/participants/by-local-group',
|
||||
},
|
||||
{
|
||||
title: 'Teilis nach Teili-Gruppe',
|
||||
component: ListCostUnits,
|
||||
endpoint: "/api/v1/cost-unit/open/archived-cost-units",
|
||||
component: ParticipantsList,
|
||||
endpoint: "/api/v1/event/details/" + props.event.identifier + '/participants/by-participation-group',
|
||||
},
|
||||
{
|
||||
title: 'Abgemeldete Teilis',
|
||||
component: ListCostUnits,
|
||||
endpoint: "/api/v1/cost-unit/open/archived-cost-units",
|
||||
component: ParticipantsList,
|
||||
endpoint: "/api/v1/event/details/" + props.event.identifier + '/participants/signed-off',
|
||||
},
|
||||
{
|
||||
title: 'Zusätze',
|
||||
component: ListCostUnits,
|
||||
component: ParticipantsList,
|
||||
endpoint: "/api/v1/cost-unit/open/archived-cost-units",
|
||||
},
|
||||
]
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<script setup>
|
||||
import {onMounted, reactive, ref} from "vue";
|
||||
import {onMounted, reactive, ref} from "vue";
|
||||
import ParticipationFees from "./ParticipationFees.vue";
|
||||
import ParticipationSummary from "./ParticipationSummary.vue";
|
||||
import CommonSettings from "./CommonSettings.vue";
|
||||
|
||||
@@ -1,31 +1,116 @@
|
||||
<script setup>
|
||||
import { reactive, watch } from "vue";
|
||||
import AmountInput from "../../../../Views/Components/AmountInput.vue";
|
||||
|
||||
const props = defineProps({
|
||||
const props = defineProps({
|
||||
editMode: Boolean,
|
||||
participant: Object,
|
||||
})
|
||||
event: Object,
|
||||
});
|
||||
|
||||
const emit = defineEmits(['closeParticipantDetails', 'markCocExisting','paymentComplete', 'cancelParticipation'])
|
||||
console.log(props.participant)
|
||||
|
||||
function markCocExisting(participant) {
|
||||
emit('markCocExisting', participant)
|
||||
close()
|
||||
}
|
||||
const emit = defineEmits([
|
||||
'closeParticipantDetails',
|
||||
'markCocExisting',
|
||||
'paymentComplete',
|
||||
'cancelParticipation',
|
||||
'editParticipant',
|
||||
'saveParticipant',
|
||||
]);
|
||||
|
||||
function paymentComplete(participant) {
|
||||
emit('paymentComplete', participant)
|
||||
close()
|
||||
}
|
||||
const form = reactive({
|
||||
firstname: '',
|
||||
lastname: '',
|
||||
nickname: '',
|
||||
address_1: '',
|
||||
address_2: '',
|
||||
postcode: '',
|
||||
city: '',
|
||||
localgroup: '',
|
||||
birthday: '',
|
||||
email_1: '',
|
||||
phone_1: '',
|
||||
contact_person: '',
|
||||
email_2: '',
|
||||
phone_2: '',
|
||||
arrival: '',
|
||||
departure: '',
|
||||
participationType: '',
|
||||
eatingHabit: '',
|
||||
allergies: '',
|
||||
intolerances: '',
|
||||
medications: '',
|
||||
extendedFirstAid: '',
|
||||
swimmingPermission: '',
|
||||
tetanusVaccination: '',
|
||||
notes: '',
|
||||
amountPaid: '',
|
||||
amountExpected: '',
|
||||
cocStatus: '',
|
||||
});
|
||||
|
||||
function close() {
|
||||
emit('closeParticipantDetails')
|
||||
watch(
|
||||
() => props.participant,
|
||||
(participant) => {
|
||||
form.firstname = participant?.firstname ?? '';
|
||||
form.lastname = participant?.lastname ?? '';
|
||||
form.nickname = participant?.nickname ?? '';
|
||||
form.address_1 = participant?.address_1 ?? '';
|
||||
form.address_2 = participant?.address_2 ?? '';
|
||||
form.postcode = participant?.postcode ?? '';
|
||||
form.city = participant?.city ?? '';
|
||||
form.localgroup = participant?.local_group ?? '';
|
||||
form.birthday = participant?.birthdayDate ?? '';
|
||||
form.email_1 = participant?.email_1 ?? '';
|
||||
form.phone_1 = participant?.phone_1 ?? '';
|
||||
form.contact_person = participant?.contact_person ?? '';
|
||||
form.email_2 = participant?.email_2 ?? '';
|
||||
form.phone_2 = participant?.phone_2 ?? '';
|
||||
form.arrival = participant?.arrival ?? '';
|
||||
form.departure = participant?.departure ?? '';
|
||||
form.participationType = participant?.participation_type ?? '';
|
||||
form.eatingHabit = participant?.eating_habit ?? '';
|
||||
form.allergies = participant?.allergies ?? '';
|
||||
form.intolerances = participant?.intolerances ?? '';
|
||||
form.medications = participant?.medications ?? '';
|
||||
form.extendedFirstAid = participant?.first_aid_permission ?? '';
|
||||
form.swimmingPermission = participant?.swimming_permission ?? '';
|
||||
form.tetanusVaccination = participant?.tetanus_vaccination ?? '';
|
||||
form.notes = participant?.notes ?? '';
|
||||
form.amountPaid = participant?.amountPaid.short ?? '';
|
||||
form.amountExpected = participant?.amountExpected.short ?? '';
|
||||
form.cocStatus = participant?.efz_status ?? '';
|
||||
},
|
||||
{ immediate: true }
|
||||
);
|
||||
|
||||
}
|
||||
function markCocExisting(participant) {
|
||||
emit('markCocExisting', participant);
|
||||
close();
|
||||
}
|
||||
|
||||
function cancelParticipation(participant) {
|
||||
emit('cancelParticipation', participant)
|
||||
close()
|
||||
}
|
||||
function paymentComplete(participant) {
|
||||
emit('paymentComplete', participant);
|
||||
close();
|
||||
}
|
||||
|
||||
function close() {
|
||||
emit('closeParticipantDetails');
|
||||
}
|
||||
|
||||
function cancelParticipation(participant) {
|
||||
emit('cancelParticipation', participant);
|
||||
close();
|
||||
}
|
||||
|
||||
function enableEditMode() {
|
||||
emit('editParticipant');
|
||||
}
|
||||
|
||||
function saveParticipant() {
|
||||
emit('saveParticipant', { ...form });
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
@@ -40,7 +125,11 @@
|
||||
<th>Name</th>
|
||||
<td>
|
||||
<span v-if="!props.editMode">
|
||||
{{props.participant.firstname}} {{props.participant.lastname}}
|
||||
{{ props.participant.firstname }} {{ props.participant.lastname }}
|
||||
</span>
|
||||
<span v-else>
|
||||
<input v-model="form.firstname" type="text" placeholder="Vorname" />
|
||||
<input v-model="form.lastname" type="text" placeholder="Nachname" />
|
||||
</span>
|
||||
</td>
|
||||
</tr>
|
||||
@@ -48,7 +137,8 @@
|
||||
<tr>
|
||||
<th>Pfadiname</th>
|
||||
<td>
|
||||
<span v-if="!props.editMode">{{props.participant.nickname}}</span>
|
||||
<span v-if="!props.editMode">{{ props.participant.nickname }}</span>
|
||||
<input v-else v-model="form.nickname" type="text" />
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
@@ -56,25 +146,35 @@
|
||||
<th>Anschrift</th>
|
||||
<td>
|
||||
<span v-if="!props.editMode">
|
||||
{{props.participant.address_1}}<br />
|
||||
{{props.participant.address_2}}<br />
|
||||
{{props.participant.postcode}}
|
||||
{{props.participant.city}}
|
||||
{{ props.participant.address_1 }}<br />
|
||||
{{ props.participant.address_2 }}<br />
|
||||
{{ props.participant.postcode }}
|
||||
{{ props.participant.city }}
|
||||
</span>
|
||||
<span v-else>
|
||||
<input v-model="form.address_1" type="text" placeholder="Adresse 1" />
|
||||
<input v-model="form.address_2" type="text" placeholder="Adresse 2" />
|
||||
<input v-model="form.postcode" type="text" placeholder="PLZ" />
|
||||
<input v-model="form.city" type="text" placeholder="Ort" />
|
||||
</span>
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<th>Stamm</th>
|
||||
<td>
|
||||
<span v-if="!props.editMode">{{props.participant.localgroup}}</span>
|
||||
<span v-if="!props.editMode">{{ props.participant.localgroup }}</span>
|
||||
<select v-else v-model="form.localgroup">
|
||||
<option v-for="group in props.event.contributingLocalGroups" :key="group.id" :value="group.slug">{{ group.name }}</option>
|
||||
</select>
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<th>Geburtsdatum</th>
|
||||
<td>
|
||||
<span v-if="!props.editMode">
|
||||
{{props.participant.birthday}}
|
||||
</span>
|
||||
<span v-if="!props.editMode">{{ props.participant.birthday }}</span>
|
||||
<input v-else v-model="form.birthday" type="date" />
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
@@ -86,41 +186,45 @@
|
||||
<tr>
|
||||
<th>E-Mail</th>
|
||||
<td>
|
||||
<span v-if="!props.editMode">{{props.participant.email_1}}</span>
|
||||
<span v-if="!props.editMode">{{ props.participant.email_1 }}</span>
|
||||
<input v-else v-model="form.email_1" type="email" />
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<th>Telefon</th>
|
||||
<td>
|
||||
<span v-if="!props.editMode">{{props.participant.phone_1}}</span>
|
||||
<span v-if="!props.editMode">{{ props.participant.phone_1 }}</span>
|
||||
<input v-else v-model="form.phone_1" type="text" />
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<th>Ansprechperson</th>
|
||||
<td>
|
||||
<span v-if="!props.editMode">{{props.participant.contact_person}}</span>
|
||||
<span v-if="!props.editMode">{{ props.participant.contact_person }}</span>
|
||||
<input v-else v-model="form.contact_person" type="text" />
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<th>Ansprechperson E-Mail</th>
|
||||
<td>
|
||||
<span v-if="!props.editMode">{{props.participant.email_2}}</span>
|
||||
<span v-if="!props.editMode">{{ props.participant.email_2 }}</span>
|
||||
<input v-else v-model="form.email_2" type="email" />
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<th>Ansprechperson Telefon</th>
|
||||
<td>
|
||||
<span v-if="!props.editMode">{{props.participant.phone_2}}</span>
|
||||
<span v-if="!props.editMode">{{ props.participant.phone_2 }}</span>
|
||||
<input v-else v-model="form.phone_2" type="text" />
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="participationData">
|
||||
<div>
|
||||
@@ -129,97 +233,138 @@
|
||||
<tr>
|
||||
<th>Anreise</th>
|
||||
<td>
|
||||
<span v-if="!props.editMode">{{props.participant.arrival}}</span>
|
||||
<span v-if="!props.editMode">{{ props.participant.arrival }}</span>
|
||||
<input v-else v-model="form.arrival" type="text" />
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>Abreise</th>
|
||||
<td>
|
||||
<span v-if="!props.editMode">{{props.participant.departure}}</span>
|
||||
<span v-if="!props.editMode">{{ props.participant.departure }}</span>
|
||||
<input v-else v-model="form.departure" type="text" />
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>Teilnahmegruppe</th>
|
||||
<td>
|
||||
<span v-if="!props.editMode">{{props.participant.participationType}}</span>
|
||||
<span v-if="!props.editMode">{{ props.participant.participationType }}</span>
|
||||
<select v-else v-model="form.participationType">
|
||||
<option
|
||||
v-for="participationType in event.participationTypes"
|
||||
:value="participationType.type.slug"
|
||||
>
|
||||
{{ participationType.type.name }}
|
||||
</option>
|
||||
</select>
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<th>Ernährung</th>
|
||||
<td>
|
||||
<span v-if="!props.editMode">{{props.participant.eatingHabit}}</span>
|
||||
<span v-if="!props.editMode">{{ props.participant.eatingHabit }}</span>
|
||||
<select v-else v-model="form.eatingHabit">
|
||||
<option
|
||||
v-for="eatingHabit in event.eatingHabits"
|
||||
:value="eatingHabit.slug"
|
||||
>
|
||||
{{ eatingHabit.name }}
|
||||
</option>
|
||||
</select>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>eFZ-Status</th>
|
||||
<td>
|
||||
<span v-if="!props.editMode">{{props.participant.efzStatusReadable}}</span>
|
||||
<span v-if="!props.editMode">{{ props.participant.efzStatusReadable }}</span>
|
||||
<select v-else v-model="form.cocStatus">
|
||||
<option value="not_checked">Nicht geprüft</option>
|
||||
<option value="not_required">Nicht erforderlich</option>
|
||||
<option value="checked_valid">Geprüft und Vorhanden</option>
|
||||
<option value="checked_invalid">Nicht vorhanden</option>
|
||||
</select>
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<th>Beitrag</th>
|
||||
<td>
|
||||
<span v-if="!props.editMode">{{props.participant.amountPaid.readable}} / {{props.participant.amountExpected.readable}}</span>
|
||||
<span v-if="!props.editMode">{{ props.participant.amountPaid.readable }} / {{ props.participant.amountExpected.readable }}</span>
|
||||
<span v-else>
|
||||
<AmountInput v-model="form.amountPaid" style="width:74px" /> Euro
|
||||
/
|
||||
<AmountInput v-model="form.amountExpected" style="width: 74px" /> Euro
|
||||
</span>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<h3>Medizinische Informationen</h3>
|
||||
<table>
|
||||
<tr>
|
||||
<th>Allergien</th>
|
||||
<td>
|
||||
<span v-if="!props.editMode">{{props.participant.allergies}}</span>
|
||||
<span v-if="!props.editMode">{{ props.participant.allergies }}</span>
|
||||
<input type="text" v-else v-model="form.allergies" />
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>Unverträglichkeiten</th>
|
||||
<td>
|
||||
<span v-if="!props.editMode">{{props.participant.intolerances}}</span>
|
||||
<span v-if="!props.editMode">{{ props.participant.intolerances }}</span>
|
||||
<input type="text" v-else v-model="form.intolerances" />
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<th>Medikamente</th>
|
||||
<td>
|
||||
<span v-if="!props.editMode">{{props.participant.medications}}</span>
|
||||
<span v-if="!props.editMode">{{ props.participant.medications }}</span>
|
||||
<input type="text" v-else v-model="form.medications" />
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<th>Erweiterte Erste Hilfe</th>
|
||||
<td>
|
||||
<span v-if="!props.editMode">{{props.participant.extendedFirstAid}}</span>
|
||||
<span v-if="!props.editMode">{{ props.participant.extendedFirstAid }}</span>
|
||||
<select v-else v-model="form.extendedFirstAid">
|
||||
<option value="FIRST_AID_PERMISSION_ALLOWED">Erlaubt</option>
|
||||
<option value="FIRST_AID_PERMISSION_DENIED">Verweigert</option>
|
||||
</select>
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<th>Badeerlaubnis</th>
|
||||
<td>
|
||||
<span v-if="!props.editMode">{{props.participant.swimmingPermission}}</span>
|
||||
<span v-if="!props.editMode">{{ props.participant.swimmingPermission }}</span>
|
||||
<select v-else v-model="form.swimmingPermission">
|
||||
<option value="SWIMMING_PERMISSION_ALLOWED">Vorhanden, kann schwimmen</option>
|
||||
<option value="SWIMMING_PERMISSION_LIMITED">Vorhanden, kann nicht schwimmen</option>
|
||||
<option value="SWIMMING_PERMISSION_DENIED">Verweigert</option>
|
||||
</select>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>Letzte Tetanus-Impfung</th>
|
||||
<td>
|
||||
<span v-if="!props.editMode">{{props.participant.tetanusVaccination}}</span>
|
||||
<span v-if="!props.editMode">{{ props.participant.tetanusVaccination }}</span>
|
||||
<input v-else v-model="form.tetanusVaccination" type="date" />
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<th>Anmerkungen</th>
|
||||
<td>
|
||||
<span v-if="!props.editMode">{{props.participant.notes}}</span>
|
||||
<span v-if="!props.editMode">{{ props.participant.notes }}</span>
|
||||
<textarea v-else v-model="form.notes"></textarea>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<button v-if="!props.editMode" class="button" @click="enableEditMode">Bearbeiten</button>
|
||||
<button v-else class="button" @click="saveParticipant">Speichern</button>
|
||||
|
||||
<button class="button">Bearbeiten</button>
|
||||
<button class="button" @click="paymentComplete(props.participant)">Zahlung vollständig</button>
|
||||
<button class="button" @click="markCocExisting(props.participant)">eFZ liegt vor</button>
|
||||
<button class="button" @click="cancelParticipation(props.participant)">Abmelden</button>
|
||||
@@ -231,6 +376,7 @@
|
||||
.participationData {
|
||||
display: flex;
|
||||
margin-bottom: 20px;
|
||||
gap: 20px;
|
||||
}
|
||||
|
||||
.participationData div {
|
||||
@@ -238,4 +384,19 @@
|
||||
vertical-align: top;
|
||||
}
|
||||
|
||||
input[type="text"],
|
||||
input[type="email"],
|
||||
input[type="date"],
|
||||
textarea
|
||||
{
|
||||
width: 250px
|
||||
}
|
||||
|
||||
textarea {
|
||||
height: 100px;
|
||||
}
|
||||
|
||||
select {
|
||||
width: 262px;
|
||||
}
|
||||
</style>
|
||||
|
||||
432
app/Domains/Event/Views/Partials/ParticipantsList.vue
Normal file
432
app/Domains/Event/Views/Partials/ParticipantsList.vue
Normal file
@@ -0,0 +1,432 @@
|
||||
<script setup>
|
||||
import { computed, reactive, ref } from "vue";
|
||||
import Modal from "../../../../Views/Components/Modal.vue";
|
||||
import ParticipantData from "./ParticipantData.vue";
|
||||
import {toast} from "vue3-toastify";
|
||||
import {useAjax} from "../../../../../resources/js/components/ajaxHandler.js";
|
||||
import {format, getDay, getMonth, getYear} from "date-fns";
|
||||
|
||||
const props = defineProps({
|
||||
data: {
|
||||
type: Object,
|
||||
default: () => ({
|
||||
localGroups: {},
|
||||
participants: {},
|
||||
event: {},
|
||||
}),
|
||||
},
|
||||
});
|
||||
|
||||
const today = format(new Date(), "yyyy-MM-dd");
|
||||
|
||||
const { request } = useAjax();
|
||||
|
||||
const searchTerms = reactive({});
|
||||
const selectedStatuses = reactive({});
|
||||
|
||||
const event = computed(() => props.data?.event ?? {});
|
||||
const participantGroups = computed(() => props.data?.participants ?? {});
|
||||
const showParticipantDetails = ref(false);
|
||||
const showParticipant = ref(null);
|
||||
const editMode = ref(false);
|
||||
|
||||
const openCancelDialog = ref(false);
|
||||
const openPartialPaymentDialogSwitch = ref(false);
|
||||
|
||||
defineEmits(['showParticipantDetails', 'markCocExisting', 'paymentComplete'])
|
||||
|
||||
function openParticipantDetails(input) {
|
||||
showParticipantDetails.value = true;
|
||||
showParticipant.value = input;
|
||||
editMode.value = false;
|
||||
}
|
||||
|
||||
async function saveParticipant(formData) {
|
||||
if (!showParticipant.value?.identifier) {
|
||||
return;
|
||||
}
|
||||
|
||||
const data = await request('/api/v1/event/participant/' + showParticipant.value.identifier + '/update', {
|
||||
method: "POST",
|
||||
body: JSON.stringify(formData),
|
||||
});
|
||||
|
||||
if (data.status === 'success') {
|
||||
toast.success(data.message ?? 'Änderungen gespeichert');
|
||||
editMode.value = false;
|
||||
} else {
|
||||
toast.error(data.message ?? 'Speichern fehlgeschlagen');
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
const getGroupEntries = computed(() => {
|
||||
return Object.entries(participantGroups.value ?? {});
|
||||
});
|
||||
|
||||
const getAgeCounts = (participants) => {
|
||||
const buckets = {
|
||||
'0-5': 0,
|
||||
'6-11': 0,
|
||||
'12-15': 0,
|
||||
'16-17': 0,
|
||||
'18-27': 0,
|
||||
'27+': 0,
|
||||
};
|
||||
|
||||
(participants ?? []).forEach((participant) => {
|
||||
const age = Number(participant?.age);
|
||||
|
||||
if (!Number.isFinite(age)) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (age >= 0 && age <= 5) {
|
||||
buckets['0-5']++;
|
||||
} else if (age <= 11) {
|
||||
buckets['6-11']++;
|
||||
} else if (age <= 15) {
|
||||
buckets['12-15']++;
|
||||
} else if (age <= 17) {
|
||||
buckets['16-17']++;
|
||||
} else if (age <= 27) {
|
||||
buckets['18-27']++;
|
||||
} else {
|
||||
buckets['27+']++;
|
||||
}
|
||||
});
|
||||
|
||||
return buckets;
|
||||
};
|
||||
|
||||
const getSearchText = (participant) => {
|
||||
return [
|
||||
participant?.firstname,
|
||||
participant?.lastname,
|
||||
participant?.nickname,
|
||||
participant?.email_1,
|
||||
participant?.email_2,
|
||||
participant?.phone_1,
|
||||
participant?.phone_2,
|
||||
participant?.local_group_string,
|
||||
participant?.participation_type_string,
|
||||
participant?.efz_status_string,
|
||||
participant?.amount_string,
|
||||
]
|
||||
.filter(Boolean)
|
||||
.join(" ")
|
||||
.toLowerCase();
|
||||
};
|
||||
|
||||
const getFilteredParticipants = (groupKey, participants) => {
|
||||
const searchTerm = (searchTerms[groupKey] ?? "").trim().toLowerCase();
|
||||
|
||||
return (participants ?? []).filter((participant) => {
|
||||
const matchesSearch =
|
||||
!searchTerm ||
|
||||
getSearchText(participant).includes(searchTerm);
|
||||
|
||||
|
||||
return matchesSearch;
|
||||
});
|
||||
};
|
||||
|
||||
const getRowClass = (participant) => {
|
||||
if (participant?.unregistered_at) {
|
||||
return "bg-gray-50 text-gray-500";
|
||||
}
|
||||
|
||||
if (
|
||||
Number(participant?.amount?.amount ?? participant?.amount ?? 0) !==
|
||||
Number(participant?.amount_paid?.amount ?? participant?.amount_paid ?? 0)
|
||||
) {
|
||||
return "bg-red-50";
|
||||
}
|
||||
|
||||
return "";
|
||||
};
|
||||
|
||||
const ensureGroupState = (groupKey) => {
|
||||
if (searchTerms[groupKey] === undefined) {
|
||||
searchTerms[groupKey] = "";
|
||||
}
|
||||
|
||||
if (selectedStatuses[groupKey] === undefined) {
|
||||
selectedStatuses[groupKey] = "all";
|
||||
}
|
||||
};
|
||||
|
||||
async function paymentComplete(participant) {
|
||||
const data = await request('/api/v1/event/participant/' + participant.identifier + '/payment-complete', {
|
||||
method: "POST",
|
||||
});
|
||||
|
||||
if (data.status === 'success') {
|
||||
toast.success(data.message);
|
||||
document.getElementById('participant-' + participant.identifier + '-payment').removeAttribute('class');
|
||||
document.getElementById('participant-' + participant.identifier + '-paid').innerText = participant.amountExpected.readable;
|
||||
document.getElementById('participant-' + participant.identifier + '-actions').style.display='none';
|
||||
} else {
|
||||
toast.error(data.message);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
async function markCocExisting(participant) {
|
||||
const data = await request('/api/v1/event/participant/' + participant.identifier + '/mark-coc-existing', {
|
||||
method: "POST",
|
||||
});
|
||||
|
||||
if (data.status === 'success') {
|
||||
toast.success(data.message);
|
||||
document.getElementById('participant-' + participant.identifier + '-coc-status').innerText = 'Gültig';
|
||||
document.getElementById('participant-' + participant.identifier + '-coc-action').style.display='none';
|
||||
document.getElementById('participant-' + participant.identifier + '-name').removeAttribute('class');
|
||||
} else {
|
||||
toast.error(data.message);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
function openCancelParticipationDialog(participant) {
|
||||
openCancelDialog.value = true;
|
||||
showParticipant = participant;
|
||||
|
||||
}
|
||||
|
||||
async function execCancelParticipation() {
|
||||
openCancelDialog.value = false;
|
||||
toast.success('Abmeldung erfolgreich')
|
||||
|
||||
}
|
||||
|
||||
function openPartialPaymentDialog(participant) {
|
||||
openPartialPaymentDialogSwitch.value = true;
|
||||
showParticipant = participant;
|
||||
|
||||
}
|
||||
|
||||
async function execPartialPayment() {
|
||||
openPartialPaymentDialogSwitch.value = false;
|
||||
toast.success('Teilzahlung erfolgreich')
|
||||
}
|
||||
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<h2>{{ event?.name ?? "Veranstaltung" }}</h2>
|
||||
<div :key="groupKey">
|
||||
<div>
|
||||
<table style="width: 95%; margin: 20px auto; border-collapse: collapse;" v-for="[groupKey, participants] in getGroupEntries">
|
||||
<thead>
|
||||
<tr>
|
||||
<th colspan="4" style="background: linear-gradient(to bottom, #fff, #f6f7f7); font-weight: bold">
|
||||
{{ groupKey }} ({{ participants.length }} Personen)
|
||||
</th>
|
||||
</tr>
|
||||
<tr style="background: linear-gradient(to bottom, #fff, #f6f7f7);">
|
||||
<th>Name</th>
|
||||
<th>Beitrag</th>
|
||||
<th>E-Mail-Adresse</th>
|
||||
<th>Telefon</th>
|
||||
</tr>
|
||||
</thead>
|
||||
|
||||
<tbody>
|
||||
<template
|
||||
v-for="participant in getFilteredParticipants(groupKey, participants)"
|
||||
:key="participant.id"
|
||||
>
|
||||
<tr :class="getRowClass(participant)" :id="'participant-' + participant.identifier">
|
||||
<td :id="'participant-' + participant.identifier +'-name'"
|
||||
style="width: 300px;"
|
||||
:class="participant.efz_status === 'checked_invalid' ? 'efz-invalid' :
|
||||
participant.efz_status === 'not_checked' ? 'efz-not-checked' : ''">
|
||||
<div v-html="participant.fullname" /><br />
|
||||
Geburtsdatum: {{ participant.birthday }}<br />
|
||||
Alter: {{ participant.age }} Jahre<br />
|
||||
|
||||
|
||||
<span>eFZ-Status: <label :id="'participant-' + participant.identifier +'-coc-status'">{{ participant.efzStatusReadable }}</label></span>
|
||||
<span :id="'participant-' + participant.identifier +'-coc-action'" v-if="participant.efz_status !== 'checked_valid' && participant.efz_status !== 'not_required'" class="link" style="color: #3cb62e; font-size: 11pt;" @click="markCocExisting(participant)">Vorhanden?</span>
|
||||
</td>
|
||||
|
||||
<td :id="'participant-' + participant.identifier +'-payment'" :class="participant.amount_left_value != 0 && !unregistered_at ? 'not-paid' : ''" style="width: 275px; '">
|
||||
Gezahlt: <label :id="'participant-' + participant.identifier + '-paid'">{{ participant?.amountPaid.readable }}</label> /<br />
|
||||
Gesamt: {{ participant?.amountExpected.readable }}
|
||||
<br /><br />
|
||||
<span v-if="participant.amount_left_value != 0 && !unregistered_at" :id="'participant-' + participant.identifier + '-actions'">
|
||||
<span class="link" style="font-size:10pt;" @click="paymentComplete(participant)">Zahlung buchen</span>
|
||||
<span class="link" style="font-size:10pt;" @click="openPartialPaymentDialog(participant)">Teilzahlung buchen</span>
|
||||
</span>
|
||||
</td>
|
||||
|
||||
<td>
|
||||
{{ participant?.email_1 ?? "-" }}
|
||||
<div v-if="participant?.email_2 && participant.email_2 !== participant.email_1" class="text-xs text-gray-500">
|
||||
{{ participant.email_2 }}
|
||||
</div>
|
||||
|
||||
</td>
|
||||
|
||||
<td>
|
||||
{{ participant?.phone_1 ?? "-" }}
|
||||
<div v-if="participant?.phone_2 && participant.phone_2 !== participant.phone_1" class="text-xs text-gray-500">
|
||||
{{ participant.phone_2 }}
|
||||
</div>
|
||||
</td>
|
||||
|
||||
|
||||
</tr>
|
||||
|
||||
<tr class="participant-meta-row">
|
||||
<td colspan="5" style="height: 15px !important; font-size: 9pt; background-color: #ffffff; border-top-style: none;">
|
||||
{{ participant?.localgroup ?? "-" }} |
|
||||
<strong> Anreise: </strong>{{ participant?.arrival ?? "-" }} |
|
||||
<strong> Abreise: </strong>{{ participant?.departure ?? "-" }} |
|
||||
<strong> Angemeldet am: </strong>{{ participant?.registerDate ?? "-" }} |
|
||||
<span class="link" @click="openParticipantDetails(participant)">Details</span> |
|
||||
<span class="link">E-Mail senden</span> |
|
||||
<span @click="openCancelParticipationDialog(participant)" v-if="!unregistered_at" class="link" style="color: #da7070;">Abmelden</span>
|
||||
<span v-else class="link" style="color: #3cb62e;">Wieder anmelden</span>
|
||||
</td>
|
||||
</tr>
|
||||
</template>
|
||||
|
||||
<tr>
|
||||
<td colspan="3" style="font-weight: normal;">
|
||||
0 - 5 Jahre: <strong>{{ getAgeCounts(participants)['0-5'] ?? 0 }}</strong> |
|
||||
6-11 Jahre: <strong>{{ getAgeCounts(participants)['6-11'] ?? 0 }}</strong> |
|
||||
12-15 Jahre: <strong>{{ getAgeCounts(participants)['12-15'] ?? 0 }}</strong> |
|
||||
16 - 17 Jahre: <strong>{{ getAgeCounts(participants)['16-17'] ?? 0 }}</strong> |
|
||||
18 - 27 Jahre: <strong>{{ getAgeCounts(participants)['18-27'] ?? 0 }}</strong> |
|
||||
27 Jahre und älter: <strong>{{ getAgeCounts(participants)['27+'] ?? 0 }}</strong>
|
||||
</td>
|
||||
<td>
|
||||
E-Mail an Gruppe senden
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<Modal
|
||||
:show="showParticipantDetails"
|
||||
title="Anmeldedetails ansehen"
|
||||
@close="showParticipantDetails = false;toast.success('HALLO');"
|
||||
>
|
||||
<ParticipantData
|
||||
@cancelParticipation="openCancelParticipationDialog"
|
||||
:participant="showParticipant"
|
||||
:editMode="editMode"
|
||||
:event="event"
|
||||
@editParticipant="editMode = true"
|
||||
@saveParticipant="saveParticipant"
|
||||
@paymentComplete="paymentComplete"
|
||||
@markCocExisting="markCocExisting"
|
||||
@closeParticipantDetails="showParticipantDetails = false" />
|
||||
</Modal>
|
||||
|
||||
<Modal
|
||||
:show="openCancelDialog"
|
||||
title="Anmeldung stornieren"
|
||||
@close="openCancelDialog = false"
|
||||
>
|
||||
Datum der Abmeldung:
|
||||
<input type="date" style="margin-top: 10px;" id="cancel_date" :value="today" />
|
||||
<br /><br />
|
||||
<button class="button" @click="execCancelParticipation()">Abmeldung durchführen</button>
|
||||
</Modal>
|
||||
|
||||
<Modal
|
||||
:show="openPartialPaymentDialogSwitch"
|
||||
title="Teilbetragszahlung"
|
||||
@close="openPartialPaymentDialogSwitch = false"
|
||||
>
|
||||
Gesamtbetrag der Zahlung:
|
||||
<input type="text" style="margin-top: 10px; width: 150px !important;" id="partial_payment_amount" /> Euro
|
||||
<br /><br />
|
||||
<button class="button" @click="execPartialPayment()">Teilbetrag buchen</button>
|
||||
</Modal>
|
||||
|
||||
|
||||
|
||||
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
table {
|
||||
margin-bottom: 60px !important;
|
||||
}
|
||||
|
||||
tr {
|
||||
vertical-align: top;
|
||||
}
|
||||
|
||||
tr td {
|
||||
height: 80px;
|
||||
padding: 10px;
|
||||
padding-top: 20px;
|
||||
font-size: 11pt;
|
||||
line-height: 1.5;
|
||||
}
|
||||
|
||||
tr th {
|
||||
height: 40px;
|
||||
padding-left: 10px;
|
||||
vertical-align: middle;
|
||||
}
|
||||
|
||||
tr th:after {
|
||||
content: "";
|
||||
|
||||
}
|
||||
|
||||
tr:nth-child(even) {
|
||||
background-color: #f9fafb;
|
||||
border-style: solid;
|
||||
border-width: 0 1px;
|
||||
border-color: #e5e7eb;
|
||||
}
|
||||
|
||||
|
||||
|
||||
tr:nth-child(odd) {
|
||||
background-color: #ffffff;
|
||||
border-style: solid;
|
||||
border-width: 5px 1px 0 1px;
|
||||
border-color: #e5e7eb;
|
||||
}
|
||||
|
||||
tr:first-child {
|
||||
border-width: 1px 1px 0 1px;
|
||||
}
|
||||
|
||||
tr:last-child {
|
||||
border-width: 0 1px 1px 1px;
|
||||
}
|
||||
|
||||
tr:last-child td {
|
||||
background: linear-gradient(to bottom, #fff, #f6f7f7); font-weight: bold;
|
||||
height: 30px;
|
||||
}
|
||||
|
||||
.button {
|
||||
display: block;
|
||||
font-size: 10pt;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
.not-paid {
|
||||
color: #ff0000; background-color: #ffe6e6;
|
||||
}
|
||||
|
||||
.efz-invalid {
|
||||
color: #ff0000; background-color: #ffe6e6;
|
||||
}
|
||||
|
||||
.efz-not-checked {
|
||||
color: #8D914BFF; background-color: #F4E99EFF;
|
||||
}
|
||||
</style>
|
||||
@@ -140,11 +140,18 @@ class EventParticipant extends InstancedModel
|
||||
return sprintf('%1$s %2$s', $this->firstname, $this->lastname);
|
||||
}
|
||||
|
||||
public function getFullname() : string {
|
||||
return sprintf('%1$1s %2$s %3$s',
|
||||
public function getFullName() : string {
|
||||
if ($this->nickname === null) {
|
||||
return sprintf('%1$s %2$s', $this->firstname, $this->lastname)
|
||||
|>trim(...);
|
||||
}
|
||||
|
||||
|
||||
return sprintf('%1$1s%2$s(%3$s %4$s)',
|
||||
$this->nickname,
|
||||
'<br />',
|
||||
$this->firstname,
|
||||
$this->lastname,
|
||||
$this->nickname !== null ? '(' . $this->nickname . ')' : '',
|
||||
)
|
||||
|>trim(...);
|
||||
}
|
||||
|
||||
@@ -4,6 +4,7 @@ namespace App\Repositories;
|
||||
|
||||
use App\Enumerations\EatingHabit;
|
||||
use App\Models\Event;
|
||||
use App\Models\EventParticipant;
|
||||
use Illuminate\Http\Request;
|
||||
|
||||
class EventParticipantRepository {
|
||||
@@ -16,6 +17,41 @@ class EventParticipantRepository {
|
||||
return $participants;
|
||||
}
|
||||
|
||||
public function getByIdentifier(string $identifier, EventRepository $eventRepository, bool $withPermissionCheck = true) : ?EventParticipant {
|
||||
$participant = EventParticipant::where('identifier', $identifier)->first();
|
||||
if ($participant === null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if ($withPermissionCheck) {
|
||||
$event = $eventRepository->getById($participant->event_id);
|
||||
if ($event === null) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
return $participant;
|
||||
}
|
||||
|
||||
public function groupByLocalGroup(Event $event, Request $request) : array {
|
||||
$allParticipants = $this->getForList($event, $request);
|
||||
$participants = [];
|
||||
foreach ($allParticipants as $participant) {
|
||||
$participants[$participant['localgroup']][] = $participant;
|
||||
}
|
||||
|
||||
return $participants;
|
||||
}
|
||||
|
||||
public function groupByParticipationType(Event $event, Request $request) : array {
|
||||
$allParticipants = $this->getForList($event, $request);
|
||||
$participants = [];
|
||||
foreach ($allParticipants as $participant) {
|
||||
$participants[$participant['participationType']][] = $participant;
|
||||
}
|
||||
|
||||
return $participants;
|
||||
}
|
||||
|
||||
public function getParticipantsWithIntolerances(Event $event, Request $request) : array {
|
||||
$participants = [];
|
||||
foreach ($event->participants()->whereNotNull('intolerances')->whereNot('intolerances' , '=', '')->get() as $participant) {
|
||||
|
||||
@@ -2,6 +2,8 @@
|
||||
|
||||
namespace App\Resources;
|
||||
|
||||
use App\Enumerations\EatingHabit;
|
||||
use App\Enumerations\EfzStatus;
|
||||
use App\Enumerations\ParticipationType;
|
||||
use App\Models\EventParticipant;
|
||||
use App\ValueObjects\Age;
|
||||
@@ -18,7 +20,7 @@ class EventParticipantResource extends JsonResource
|
||||
{
|
||||
$event = $this->resource->event;
|
||||
|
||||
$amountLeft = $this->resource->amount;
|
||||
$amountLeft = clone $this->resource->amount;
|
||||
if ($this->resource->amount_paid !== null) {
|
||||
$amountLeft->subtractAmount($this->resource->amount_paid);
|
||||
}
|
||||
@@ -32,14 +34,20 @@ class EventParticipantResource extends JsonResource
|
||||
$presenceDaysSupport = $presenceDaysSupport - 1;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
return array_merge(
|
||||
$this->resource->toArray(),
|
||||
[
|
||||
'birthdayDate' => $this->resource->birthday->format('Y-m-d'),
|
||||
'registerDate' => $this->resource->created_at->format('d.m.Y'),
|
||||
'fullname' => $this->resource->getFullName(),
|
||||
'age' => new Age($this->resource->birthday)->getAge(),
|
||||
'localgroup' => $this->resource->localGroup()->first()->name,
|
||||
'swimmingPermission' => $this->resource->swimmingPermission()->first()->short,
|
||||
'extendedFirstAid' => $this->resource->firstAidPermission()->first()->name,
|
||||
'tetanusVaccination' => $this->resource->tetanus_vaccination?->format('d.m.Y'),
|
||||
'tetanusVaccination' => $this->resource->tetanus_vaccination?->format('d.m.Y') ?? 'Unbekannt',
|
||||
'presenceDays' => ['real' => $presenceDays, 'support' => $presenceDaysSupport],
|
||||
'participationType' => ParticipationType::where(['slug' => $this->resource->participation_type])->first()->name,
|
||||
'needs_payment' => $this->resource->amount->getAmount() > 0 && $event->pay_direct,
|
||||
@@ -49,14 +57,21 @@ class EventParticipantResource extends JsonResource
|
||||
'amount_left_string' => $amountLeft->toString(),
|
||||
'amount_left_value' => $amountLeft->getAmount(),
|
||||
'email_1' => $this->resource->email_1,
|
||||
'amountPaid' => ['value' => $this->resource->amount_paid, 'readable' => $this->resource->amount_paid?->toString() ?? '0,00 Euro'],
|
||||
'amountExpected' => ['value' => $this->resource->amount, 'readable' => $this->resource->amount?->toString() ?? '0,00 Euro'],
|
||||
'amountPaid' => ['value' => $this->resource->amount_paid, 'readable' => $this->resource->amount_paid?->toString() ?? '0,00 Euro', 'short' => $this->resource->amount_paid?->getFormattedAmount() ?? '0,00'],
|
||||
'amountExpected' => ['value' => $this->resource->amount, 'readable' => $this->resource->amount?->toString() ?? '0,00 Euro', 'short' => $this->resource->amount?->getFormattedAmount() ?? '0,00'],
|
||||
'alcoholicsAllowed' => new Age($this->resource->birthday)->getAge() >= $event->alcoholics_age,
|
||||
'localGroupPostcode' => $this->resource->localGroup()->first()->postcode,
|
||||
'localGroupCity' => $this->resource->localGroup()->first()->city,
|
||||
'state' => config('postCode.map.' . $this->resource->postcode),
|
||||
'localGroupState' => config('postCode.map.' . $this->resource->localGroup()->first()->postcode),
|
||||
'birthday' => $this->resource->birthday->format('d.m.Y'),
|
||||
'eatingHabit' => EatingHabit::where('slug', $this->resource->eating_habit)->first()->name,
|
||||
'efzStatusReadable' => match($this->resource->efz_status) {
|
||||
EfzStatus::EFZ_STATUS_CHECKED_VALID => 'Gültig',
|
||||
EfzStatus::EFZ_STATUS_CHECKED_INVALID => 'Ungültig',
|
||||
EfzStatus::EFZ_STATUS_NOT_CHECKED => 'Nicht geprüft',
|
||||
EfzStatus::EFZ_STATUS_NOT_REQUIRED => 'Nicht erforderlich',
|
||||
},
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
@@ -17,6 +17,7 @@ class LocalGroupResource extends JsonResource {
|
||||
'email' => $this->resource->email,
|
||||
'city' => $this->resource->city,
|
||||
'postalcode' => $this->resource->postcode,
|
||||
'slug' => $this->resource->slug,
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
@@ -112,7 +112,7 @@ onUnmounted(() => {
|
||||
background: white;
|
||||
border-radius: 12px;
|
||||
|
||||
max-width: 600px;
|
||||
max-width: 1200px;
|
||||
width: 90%;
|
||||
box-shadow: 0 10px 30px rgba(0, 0, 0, 0.2);
|
||||
outline: none;
|
||||
|
||||
@@ -29,7 +29,7 @@ h1, h2, h3, h4, h5, h6 {
|
||||
|
||||
.main {
|
||||
margin: 0 auto;
|
||||
box-shadow: 20px 54px 15px rgba(0, 0, 0, 0.1);
|
||||
box-shadow: 20px 20px 15px rgba(0, 0, 0, 0.1);
|
||||
border-radius: 0 10px 0 0;
|
||||
flex: 1;
|
||||
display: flex;
|
||||
|
||||
@@ -15,6 +15,7 @@ input[type="email"],
|
||||
input[type="password"],
|
||||
input[type="date"],
|
||||
input[type="number"],
|
||||
textarea,
|
||||
select {
|
||||
width: 100%;
|
||||
font-size: 13pt;
|
||||
@@ -34,6 +35,7 @@ input[type="email"]:focus,
|
||||
input[type="password"]:focus,
|
||||
input[type="date"]:focus,
|
||||
input[type="number"]:focus,
|
||||
textarea:focus,
|
||||
select:focus {
|
||||
outline: none;
|
||||
border-color: #1d4899;
|
||||
@@ -46,6 +48,7 @@ table {
|
||||
}
|
||||
|
||||
input[type="button"],
|
||||
.button,
|
||||
input[type="submit"] {
|
||||
cursor: pointer;
|
||||
background-color: #ffffff;
|
||||
@@ -55,6 +58,7 @@ input[type="submit"] {
|
||||
}
|
||||
|
||||
input[type="button"]:hover,
|
||||
.button:hover,
|
||||
input[type="submit"]:hover,
|
||||
.deny-button:hover,
|
||||
.accept-button:hover,
|
||||
|
||||
Reference in New Issue
Block a user