Personal data and password change

This commit is contained in:
2026-04-26 01:15:58 +02:00
parent f4ea07d82c
commit 5bcdc2fb5d
20 changed files with 620 additions and 26 deletions

View File

@@ -0,0 +1,23 @@
<?php
namespace App\Domains\Dashboard\Actions\UpdatePersonalData;
use App\Repositories\UserRepository;
class UpdatePersonalDataCommand
{
public function __construct(
private readonly UpdatePersonalDataRequest $request,
private readonly UserRepository $users
) {}
public function execute(): UpdatePersonalDataResponse
{
$this->users->updatePersonalData($this->request);
$response = new UpdatePersonalDataResponse();
$response->success = true;
return $response;
}
}

View File

@@ -0,0 +1,29 @@
<?php
namespace App\Domains\Dashboard\Actions\UpdatePersonalData;
use App\Models\User;
class UpdatePersonalDataRequest
{
public function __construct(
public readonly User $user,
public readonly ?string $nickname,
public readonly ?string $email,
public readonly ?string $phone,
public readonly ?string $address1,
public readonly ?string $address2,
public readonly ?string $postcode,
public readonly ?string $city,
public readonly ?string $birthday,
public readonly ?string $tetanusVaccination,
public readonly ?string $medications,
public readonly ?string $allergies,
public readonly ?string $intolerances,
public readonly ?string $eatingHabits,
public readonly ?string $swimmingPermission,
public readonly ?string $firstAidPermission,
public readonly ?string $bankAccountOwner,
public readonly ?string $bankAccountIban,
) {}
}

View File

@@ -0,0 +1,8 @@
<?php
namespace App\Domains\Dashboard\Actions\UpdatePersonalData;
class UpdatePersonalDataResponse
{
public bool $success;
}

View File

@@ -0,0 +1,14 @@
<?php
namespace App\Domains\Dashboard\Controllers;
use App\Providers\InertiaProvider;
use App\Scopes\CommonController;
class MessagesController extends CommonController {
public function __invoke() {
$inertiaProvider = new InertiaProvider('Dashboard/Messages', []);
return $inertiaProvider->render();
}
}

View File

@@ -0,0 +1,45 @@
<?php
namespace App\Domains\Dashboard\Controllers;
use App\Providers\InertiaProvider;
use App\Scopes\CommonController;
class PersonalDataController extends CommonController
{
public function __invoke()
{
if (!$this->checkAuth()) {
return redirect()->intended('/login');
}
$user = auth()->user();
$data = $this->users->getPersonalData($user);
$inertiaProvider = new InertiaProvider('Dashboard/PersonalData', [
'personalData' => [
'firstname' => $data['firstname'],
'lastname' => $data['lastname'],
'birthday' => $data['birthday'],
'nickname' => $data['nickname'],
'email' => $data['email'],
'phone' => $data['phone'],
'address1' => $data['address_1'],
'address2' => $data['address_2'],
'postcode' => $data['postcode'],
'city' => $data['city'],
'medications' => $data['medications'],
'allergies' => $data['allergies'],
'intolerances' => $data['intolerances'],
'eatingHabits' => $data['eating_habits'],
'swimmingPermission' => $data['swimming_permission'],
'firstAidPermission' => $data['first_aid_permission'],
'bankAccountOwner' => $data['bank_account_owner'],
'bankAccountIban' => $data['bank_account_iban'],
'tetanusVaccination' => $data['tetanus_vaccination'],
],
]);
return $inertiaProvider->render();
}
}

View File

@@ -0,0 +1,43 @@
<?php
namespace App\Domains\Dashboard\Controllers;
use App\Domains\Dashboard\Actions\UpdatePersonalData\UpdatePersonalDataCommand;
use App\Domains\Dashboard\Actions\UpdatePersonalData\UpdatePersonalDataRequest;
use App\Scopes\CommonController;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request;
class StorePersonalDataController extends CommonController
{
public function __invoke(Request $request): JsonResponse
{
$user = auth()->user();
$actionRequest = new UpdatePersonalDataRequest(
user: $user,
nickname: $request->input('nickname'),
email: $request->input('email'),
phone: $request->input('phone'),
address1: $request->input('address1'),
address2: $request->input('address2'),
postcode: $request->input('postcode'),
city: $request->input('city'),
medications: $request->input('medications'),
allergies: $request->input('allergies'),
intolerances: $request->input('intolerances'),
eatingHabits: $request->input('eatingHabits'),
swimmingPermission: $request->input('swimmingPermission'),
firstAidPermission: $request->input('firstAidPermission'),
bankAccountOwner: $request->input('bankAccountOwner'),
bankAccountIban: $request->input('bankAccountIban'),
birthday: $request->input('birthday'),
tetanusVaccination: $request->input('tetanusVaccination'),
);
$command = new UpdatePersonalDataCommand($actionRequest, $this->users);
$command->execute();
return response()->json(['success' => true, 'message' => 'Deine Daten wurden erfolgreich gespeichert.']);
}
}

View File

@@ -1,6 +1,7 @@
<?php
use App\Domains\Dashboard\Controllers\DashboardController;
use App\Domains\Dashboard\Controllers\StorePersonalDataController;
use App\Middleware\IdentifyTenant;
use Illuminate\Support\Facades\Route;
@@ -11,7 +12,10 @@ Route::middleware(IdentifyTenant::class)->group(function () {
Route::get('/open-cost-units', [DashboardController::class, 'getOpenCostUnits']);
Route::get('/upcoming-events', [DashboardController::class, 'getUpcomingEvents']);
Route::get('/my-participations', [DashboardController::class, 'getMyParticipations']);
Route::post('/personal-data', StorePersonalDataController::class);
});
});
});

View File

@@ -1 +1,16 @@
<?php
use App\Domains\Dashboard\Controllers\DashboardController;
use App\Domains\Dashboard\Controllers\MessagesController;
use App\Domains\Dashboard\Controllers\PersonalDataController;
use App\Middleware\IdentifyTenant;
use Illuminate\Support\Facades\Route;
Route::middleware(IdentifyTenant::class)->group(function () {
Route::middleware(['auth'])->group(function () {
Route::get('/personal-data', PersonalDataController::class);
Route::get('/messages', MessagesController::class);
});
});

View File

@@ -0,0 +1,25 @@
<script setup>
import AppLayout from "../../../../resources/js/layouts/AppLayout.vue";
import ShadowedBox from "../../../Views/Components/ShadowedBox.vue";
</script>
<template>
<AppLayout title='Meine Nachrichten'>
<shadowed-box style="width: 95%; margin: 20px auto; padding: 20px; overflow-x: hidden;">
Diese Funktion steht aktuell nicht zur Verfügung.<br />
Bitte versuche es später noch einmal.
</shadowed-box>
</AppLayout>
</template>
<style scoped>
textarea {
width: 100%;
padding: 6px 10px;
border: 1px solid #d1d5db;
border-radius: 6px;
font-size: 0.95rem;
box-sizing: border-box;
resize: vertical;
}
</style>

View File

@@ -27,8 +27,8 @@ function navigateTo(url) {
{{participation.arrivalDateReadable}} - {{participation.departureDateReadable}}
</td>
<td>
<Icon name="euro-sign" style="padding: 5px; font-size: 11pt; color: #ffffff; margin-right: 5px;" :class="participation.needs_payment ? 'bg-red' : 'bg-green'" />
<Icon name="award" style="padding: 5px; font-size: 11pt; color: #ffffff; margin-right: 5px;" :class="participation.cocColor" />
<Icon name="euro-sign" style="padding: 2px; font-size: 10pt; color: #ffffff; margin-right: 5px;" :class="participation.needs_payment ? 'bg-red' : 'bg-green'" />
<Icon name="award" style="padding: 2px; font-size: 10pt; color: #ffffff; margin-right: 5px;" :class="participation.cocColor" />
</td>
</tr>
</table>

View File

@@ -0,0 +1,186 @@
<script setup>
import { reactive, ref } from 'vue'
import { request } from '../../../../resources/js/components/HttpClient.js'
import AppLayout from "../../../../resources/js/layouts/AppLayout.vue";
import ShadowedBox from "../../../Views/Components/ShadowedBox.vue";
import {toast} from "vue3-toastify";
const props = defineProps({
personalData: Object,
})
const form = reactive({
nickname: props.personalData.nickname ?? '',
email: props.personalData.email ?? '',
phone: props.personalData.phone ?? '',
address1: props.personalData.address1 ?? '',
address2: props.personalData.address2 ?? '',
postcode: props.personalData.postcode ?? '',
city: props.personalData.city ?? '',
birthday: props.personalData.birthday ?? '',
tetanusVaccination: props.personalData.tetanusVaccination ?? '',
medications: props.personalData.medications ?? '',
allergies: props.personalData.allergies ?? '',
intolerances: props.personalData.intolerances ?? '',
eatingHabits: props.personalData.eatingHabits ?? '',
swimmingPermission: props.personalData.swimmingPermission ?? '',
firstAidPermission: props.personalData.firstAidPermission ?? '',
bankAccountOwner: props.personalData.bankAccountOwner ?? '',
bankAccountIban: props.personalData.bankAccountIban ?? '',
})
const saving = ref(false)
const successMessage = ref('')
const errorMessage = ref('')
const submit = async () => {
saving.value = true
successMessage.value = ''
errorMessage.value = ''
const result = await request('/api/v1/dashboard/personal-data', {
method: 'POST',
body: { ...form },
})
saving.value = false
if (result?.success) {
toast.success(result.message)
} else {
toast.error(result.message)
}
}
</script>
<template>
<AppLayout title='Persönliche Daten'>
<shadowed-box style="width: 95%; margin: 20px auto; padding: 20px; overflow-x: hidden;">
<div class="max-w-2xl mx-auto p-6">
<form @submit.prevent="submit">
<table class="form-table" style="width: 90%; margin: 10px;">
<!-- Nicht veränderbare Felder -->
<tr>
<td style="width: 200px; padding: 5px;">Vorname:</td>
<td><span class="text-gray-700">{{ personalData.firstname }}</span></td>
</tr>
<tr>
<td style="width: 200px; padding: 5px;">Nachname:</td>
<td><span class="text-gray-700">{{ personalData.lastname }}</span></td>
</tr>
<!-- Veränderbare Felder -->
<tr>
<td>Pfadiname:</td>
<td><input type="text" v-model="form.nickname" /></td>
</tr>
<tr>
<td>E-Mail:</td>
<td><input type="email" v-model="form.email" /></td>
</tr>
<tr>
<td>Telefon:</td>
<td><input type="text" v-model="form.phone" /></td>
</tr>
<tr>
<td>Straße / Hausnummer:</td>
<td><input type="text" v-model="form.address1" /></td>
</tr>
<tr>
<td>Adresszusatz:</td>
<td><input type="text" v-model="form.address2" /></td>
</tr>
<tr>
<td>PLZ:</td>
<td><input type="text" v-model="form.postcode" /></td>
</tr>
<tr>
<td>Ort:</td>
<td><input type="text" v-model="form.city" /></td>
</tr>
<tr>
<td style="width: 200px; padding: 5px;">Geburtsdatum:</td>
<td><input type="date" v-model="form.birthday" /></td>
</tr>
<tr>
<td>Medikamente:</td>
<td><input type="text" v-model="form.medications" /></td>
</tr>
<tr>
<td>Allergien:</td>
<td><input type="text" v-model="form.allergies" /></td>
</tr>
<tr>
<td>Unverträglichkeiten:</td>
<td><input type="text" v-model="form.intolerances" /></td>
</tr>
<tr>
<td>Letzte Tetanus-Impfung:</td>
<td><input type="date" v-model="form.tetanusVaccination" /></td>
</tr>
<tr>
<td>Ernährungsgewohnheiten:</td>
<td>
<select v-model="form.eatingHabits">
<option value="EATING_HABIT_VEGAN">Vegan</option>
<option value="EATING_HABIT_VEGETARIAN">Vegetarisch</option>
<option value="EATING_HABIT_OMNIVOR">Omnivor</option>
</select>
</td>
</tr>
<tr>
<td>Badeerlaubnis:</td>
<td>
<select v-model="form.swimmingPermission">
<option value="SWIMMING_PERMISSION_ALLOWED">Erteilt, kann schwimmen</option>
<option value="SWIMMING_PERMISSION_LIMITED">Erteilt, kann nicht schwimmen</option>
<option value="SWIMMING_PERMISSION_DENIED">Nicht erteilt</option>
</select>
</td>
</tr>
<tr>
<td>Erste-Hilfe-Erlaubnis:</td>
<td>
<select v-model="form.firstAidPermission">
<option value="FIRST_AID_PERMISSION_ALLOWED">Erweiterte Erste Hilfe erlaubt</option>
<option value="FIRST_AID_PERMISSION_DENIED">Erweiterte Erste Hilfe verweigert</option>
</select>
</td>
</tr>
<tr>
<td>Kontoinhaber:</td>
<td><input type="text" v-model="form.bankAccountOwner" /></td>
</tr>
<tr>
<td>IBAN:</td>
<td><input type="text" v-model="form.bankAccountIban" /></td>
</tr>
<tr>
<td colspan="2" class="btn-row" style="padding-top: 20px;">
<button type="submit" class="button" :disabled="saving">
{{ saving ? 'Wird gespeichert…' : 'Speichern' }}
</button>
</td>
</tr>
</table>
</form>
</div>
</shadowed-box>
</AppLayout>
</template>
<style scoped>
textarea {
width: 100%;
padding: 6px 10px;
border: 1px solid #d1d5db;
border-radius: 6px;
font-size: 0.95rem;
box-sizing: border-box;
resize: vertical;
}
</style>