Personal data and password change
This commit is contained in:
@@ -28,6 +28,8 @@ Pfad: `app/Domains/{Domain}/Actions/{ActionName}/`
|
|||||||
- `$this->users` → `UserRepository`
|
- `$this->users` → `UserRepository`
|
||||||
- `$this->tenant` → aktueller `Tenant`
|
- `$this->tenant` → aktueller `Tenant`
|
||||||
|
|
||||||
|
- Die Controller besitzen ausschließlich eine __invoke() - Funktion
|
||||||
|
- Für die Speichern-Actions werden separate Controller-Klassen erstellt (z. B. `StoreEventParticipantController`)
|
||||||
---
|
---
|
||||||
|
|
||||||
## Repositories
|
## Repositories
|
||||||
@@ -69,3 +71,11 @@ Pfad: `app/Domains/{Domain}/Actions/{ActionName}/`
|
|||||||
- Attachments werden über `Attachment::fromData(fn () => $content, $filename)->withMime(...)` angehängt
|
- Attachments werden über `Attachment::fromData(fn () => $content, $filename)->withMime(...)` angehängt
|
||||||
- Werden Daten sowohl in `content()` als auch in `attachments()` benötigt, wird eine **private Hilfsmethode mit Lazy-Caching** verwendet (einmaliges Berechnen, Ergebnis in private Property speichern)
|
- Werden Daten sowohl in `content()` als auch in `attachments()` benötigt, wird eine **private Hilfsmethode mit Lazy-Caching** verwendet (einmaliges Berechnen, Ergebnis in private Property speichern)
|
||||||
- Blade-Templates referenzieren Mail-Attachments per `cid:`-Link: `<a href="cid:{{ $filename }}">...</a>`
|
- Blade-Templates referenzieren Mail-Attachments per `cid:`-Link: `<a href="cid:{{ $filename }}">...</a>`
|
||||||
|
|
||||||
|
## Actions
|
||||||
|
- Die Actions sind in `app/Domains/{Domain}/Actions/{ActionName}/`-Verzeichnissen organisiert
|
||||||
|
- Jede Action besitzt einen Request, einen Command und einen Response
|
||||||
|
- Der Request besitzt auschließlich einen Konstruktor, der die notwendigen Parameter annimmt
|
||||||
|
- Die Response-Klasse enthält ausschließlich die notwendigen Daten für die Antwort
|
||||||
|
- Die Action-Klasse enthält die Logik für die Verarbeitung der Anfrage und die Generierung der Antwort
|
||||||
|
- Die Logik wird in einer execute() - Funktion innerhalb des Commands impplementiert. Private Funktionen, für ausgelagerte Prozesse sind zulässig, wenn der Code damit lesbarer wird.
|
||||||
|
|||||||
@@ -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;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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,
|
||||||
|
) {}
|
||||||
|
}
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Domains\Dashboard\Actions\UpdatePersonalData;
|
||||||
|
|
||||||
|
class UpdatePersonalDataResponse
|
||||||
|
{
|
||||||
|
public bool $success;
|
||||||
|
}
|
||||||
14
app/Domains/Dashboard/Controllers/MessagesController.php
Normal file
14
app/Domains/Dashboard/Controllers/MessagesController.php
Normal 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();
|
||||||
|
}
|
||||||
|
}
|
||||||
45
app/Domains/Dashboard/Controllers/PersonalDataController.php
Normal file
45
app/Domains/Dashboard/Controllers/PersonalDataController.php
Normal 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();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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.']);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,6 +1,7 @@
|
|||||||
<?php
|
<?php
|
||||||
|
|
||||||
use App\Domains\Dashboard\Controllers\DashboardController;
|
use App\Domains\Dashboard\Controllers\DashboardController;
|
||||||
|
use App\Domains\Dashboard\Controllers\StorePersonalDataController;
|
||||||
use App\Middleware\IdentifyTenant;
|
use App\Middleware\IdentifyTenant;
|
||||||
use Illuminate\Support\Facades\Route;
|
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('/open-cost-units', [DashboardController::class, 'getOpenCostUnits']);
|
||||||
Route::get('/upcoming-events', [DashboardController::class, 'getUpcomingEvents']);
|
Route::get('/upcoming-events', [DashboardController::class, 'getUpcomingEvents']);
|
||||||
Route::get('/my-participations', [DashboardController::class, 'getMyParticipations']);
|
Route::get('/my-participations', [DashboardController::class, 'getMyParticipations']);
|
||||||
|
Route::post('/personal-data', StorePersonalDataController::class);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -1 +1,16 @@
|
|||||||
<?php
|
<?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);
|
||||||
|
|
||||||
|
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|||||||
25
app/Domains/Dashboard/Views/Messages.vue
Normal file
25
app/Domains/Dashboard/Views/Messages.vue
Normal 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>
|
||||||
@@ -27,8 +27,8 @@ function navigateTo(url) {
|
|||||||
{{participation.arrivalDateReadable}} - {{participation.departureDateReadable}}
|
{{participation.arrivalDateReadable}} - {{participation.departureDateReadable}}
|
||||||
</td>
|
</td>
|
||||||
<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="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: 5px; font-size: 11pt; color: #ffffff; margin-right: 5px;" :class="participation.cocColor" />
|
<Icon name="award" style="padding: 2px; font-size: 10pt; color: #ffffff; margin-right: 5px;" :class="participation.cocColor" />
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
</table>
|
</table>
|
||||||
|
|||||||
186
app/Domains/Dashboard/Views/PersonalData.vue
Normal file
186
app/Domains/Dashboard/Views/PersonalData.vue
Normal 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>
|
||||||
24
app/Domains/UserManagement/Controllers/ProfileController.php
Normal file
24
app/Domains/UserManagement/Controllers/ProfileController.php
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Domains\UserManagement\Controllers;
|
||||||
|
|
||||||
|
use App\Providers\InertiaProvider;
|
||||||
|
use App\Scopes\CommonController;
|
||||||
|
|
||||||
|
class ProfileController extends CommonController
|
||||||
|
{
|
||||||
|
public function __invoke()
|
||||||
|
{
|
||||||
|
if (!$this->checkAuth()) {
|
||||||
|
return redirect()->intended('/login');
|
||||||
|
}
|
||||||
|
|
||||||
|
$user = auth()->user();
|
||||||
|
|
||||||
|
$inertiaProvider = new InertiaProvider('UserManagement/Profile', [
|
||||||
|
'username' => $user->username,
|
||||||
|
]);
|
||||||
|
|
||||||
|
return $inertiaProvider->render();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,37 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Domains\UserManagement\Controllers;
|
||||||
|
|
||||||
|
use App\Domains\UserManagement\Actions\UserChangePassword\UserChangePasswordCommand;
|
||||||
|
use App\Domains\UserManagement\Actions\UserChangePassword\UserChangePasswordRequest;
|
||||||
|
use App\Scopes\CommonController;
|
||||||
|
use Illuminate\Http\JsonResponse;
|
||||||
|
use Illuminate\Http\Request;
|
||||||
|
|
||||||
|
class StoreProfileController extends CommonController
|
||||||
|
{
|
||||||
|
public function __invoke(Request $request): JsonResponse
|
||||||
|
{
|
||||||
|
if (!$this->checkAuth()) {
|
||||||
|
return response()->json(['success' => false, 'message' => 'Unauthorized'], 401);
|
||||||
|
}
|
||||||
|
|
||||||
|
$password = $request->input('password');
|
||||||
|
$passwordConfirmation = $request->input('password_confirmation');
|
||||||
|
|
||||||
|
if (empty($password)) {
|
||||||
|
return response()->json(['success' => false, 'message' => 'Bitte ein Passwort eingeben.'], 422);
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($password !== $passwordConfirmation) {
|
||||||
|
return response()->json(['success' => false, 'message' => 'Die Passwörter stimmen nicht überein.'], 422);
|
||||||
|
}
|
||||||
|
|
||||||
|
$actionRequest = new UserChangePasswordRequest(auth()->user(), $password);
|
||||||
|
$command = new UserChangePasswordCommand($actionRequest);
|
||||||
|
$command->execute();
|
||||||
|
|
||||||
|
auth()->logout();
|
||||||
|
return response()->json(['success' => true, 'message' => 'Dein Passwort wurde erfolgreich geändert.']);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -3,13 +3,15 @@
|
|||||||
use App\Domains\UserManagement\Controllers\EmailVerificationController;
|
use App\Domains\UserManagement\Controllers\EmailVerificationController;
|
||||||
use App\Domains\UserManagement\Controllers\RegistrationController;
|
use App\Domains\UserManagement\Controllers\RegistrationController;
|
||||||
use App\Domains\UserManagement\Controllers\ResetPasswordController;
|
use App\Domains\UserManagement\Controllers\ResetPasswordController;
|
||||||
|
use App\Domains\UserManagement\Controllers\StoreProfileController;
|
||||||
use App\Middleware\IdentifyTenant;
|
use App\Middleware\IdentifyTenant;
|
||||||
use App\Providers\GlobalDataProvider;
|
use App\Providers\GlobalDataProvider;
|
||||||
use Illuminate\Support\Facades\Route;
|
use Illuminate\Support\Facades\Route;
|
||||||
use Inertia\Inertia;
|
use Inertia\Inertia;
|
||||||
|
|
||||||
Route::prefix('v1')
|
Route::prefix('/api/v1')
|
||||||
->group(function () {
|
->group(function () {
|
||||||
Route::middleware(IdentifyTenant::class)->group(function () {
|
Route::middleware(IdentifyTenant::class)->group(function () {
|
||||||
|
Route::post('/profile', StoreProfileController::class);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -3,6 +3,7 @@
|
|||||||
use App\Domains\UserManagement\Controllers\EmailVerificationController;
|
use App\Domains\UserManagement\Controllers\EmailVerificationController;
|
||||||
use App\Domains\UserManagement\Controllers\LoginController;
|
use App\Domains\UserManagement\Controllers\LoginController;
|
||||||
use App\Domains\UserManagement\Controllers\LogOutController;
|
use App\Domains\UserManagement\Controllers\LogOutController;
|
||||||
|
use App\Domains\UserManagement\Controllers\ProfileController;
|
||||||
use App\Domains\UserManagement\Controllers\RegistrationController;
|
use App\Domains\UserManagement\Controllers\RegistrationController;
|
||||||
use App\Domains\UserManagement\Controllers\ResetPasswordController;
|
use App\Domains\UserManagement\Controllers\ResetPasswordController;
|
||||||
use App\Middleware\IdentifyTenant;
|
use App\Middleware\IdentifyTenant;
|
||||||
@@ -20,6 +21,8 @@ Route::middleware(IdentifyTenant::class)->group(function () {
|
|||||||
|
|
||||||
Route::middleware(['auth'])->group(function () {
|
Route::middleware(['auth'])->group(function () {
|
||||||
Route::post('/logout', [LogoutController::class, 'logout']);
|
Route::post('/logout', [LogoutController::class, 'logout']);
|
||||||
|
Route::get('/profile', ProfileController::class);
|
||||||
|
|
||||||
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
100
app/Domains/UserManagement/Views/Profile.vue
Normal file
100
app/Domains/UserManagement/Views/Profile.vue
Normal file
@@ -0,0 +1,100 @@
|
|||||||
|
<script setup>
|
||||||
|
import { ref } from 'vue'
|
||||||
|
import AppLayout from '../../../../resources/js/layouts/AppLayout.vue'
|
||||||
|
import ShadowedBox from '../../../Views/Components/ShadowedBox.vue'
|
||||||
|
import { request } from '../../../../resources/js/components/HttpClient.js'
|
||||||
|
import {toast} from "vue3-toastify";
|
||||||
|
|
||||||
|
const props = defineProps({
|
||||||
|
username: String,
|
||||||
|
})
|
||||||
|
|
||||||
|
const password = ref('')
|
||||||
|
const passwordConfirmation = ref('')
|
||||||
|
const saving = ref(false)
|
||||||
|
const successMessage = ref('')
|
||||||
|
const errorMessage = ref('')
|
||||||
|
|
||||||
|
const submit = async () => {
|
||||||
|
if (!password.value) {
|
||||||
|
toast.error('Bitte gib ein neues Passwort ein')
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (password.value !== passwordConfirmation.value) {
|
||||||
|
toast.error('Die Wiederholung des Passworts stimmt nicht mit dem Passwort überein')
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
saving.value = true
|
||||||
|
successMessage.value = ''
|
||||||
|
errorMessage.value = ''
|
||||||
|
|
||||||
|
const result = await request('/api/v1/profile', {
|
||||||
|
method: 'POST',
|
||||||
|
body: {
|
||||||
|
password: password.value,
|
||||||
|
password_confirmation: passwordConfirmation.value,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
saving.value = false
|
||||||
|
|
||||||
|
if (result?.success) {
|
||||||
|
toast.success(result.message)
|
||||||
|
password.value = ''
|
||||||
|
passwordConfirmation.value = ''
|
||||||
|
} else {
|
||||||
|
toast.error(result?.message ?? 'Beim Speichern ist ein Fehler aufgetreten.')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<AppLayout title="Profil">
|
||||||
|
<shadowed-box style="width: 95%; margin: 20px auto; padding: 20px; overflow-x: hidden;">
|
||||||
|
<h2>Mein Profil</h2>
|
||||||
|
<form @submit.prevent="submit">
|
||||||
|
<table class="form-table" style="width: 90%; margin: auto;">
|
||||||
|
<tr>
|
||||||
|
<td style="width: 250px;">Anmeldename:</td>
|
||||||
|
<td>
|
||||||
|
<span>{{ username }}</span>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>Neues Passwort:</td>
|
||||||
|
<td>
|
||||||
|
<input type="password" v-model="password" autocomplete="new-password" />
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>Passwort wiederholen:</td>
|
||||||
|
<td>
|
||||||
|
<input type="password" v-model="passwordConfirmation" autocomplete="new-password" />
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td colspan="2" class="btn-row">
|
||||||
|
<button type="submit" class="button" :disabled="saving">
|
||||||
|
{{ saving ? 'Wird gespeichert…' : 'Passwort ändern' }}
|
||||||
|
</button>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</table>
|
||||||
|
</form>
|
||||||
|
</shadowed-box>
|
||||||
|
</AppLayout>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
.feedback {
|
||||||
|
padding: 10px 14px;
|
||||||
|
border-radius: 6px;
|
||||||
|
margin-bottom: 16px;
|
||||||
|
font-weight: 500;
|
||||||
|
}
|
||||||
|
.feedback.success { background: #f0fdf4; color: #15803d; border: 1px solid #bbf7d0; }
|
||||||
|
.feedback.error { background: #fef2f2; color: #b91c1c; border: 1px solid #fecaca; }
|
||||||
|
</style>
|
||||||
@@ -2,6 +2,7 @@
|
|||||||
|
|
||||||
namespace App\Repositories;
|
namespace App\Repositories;
|
||||||
|
|
||||||
|
use App\Domains\Dashboard\Actions\UpdatePersonalData\UpdatePersonalDataRequest;
|
||||||
use App\Models\User;
|
use App\Models\User;
|
||||||
use DateTime;
|
use DateTime;
|
||||||
|
|
||||||
@@ -47,4 +48,51 @@ class UserRepository {
|
|||||||
|
|
||||||
return $return;
|
return $return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function getPersonalData(User $user): array
|
||||||
|
{
|
||||||
|
return [
|
||||||
|
'firstname' => $user->firstname ?? '',
|
||||||
|
'lastname' => $user->lastname ?? '',
|
||||||
|
'birthday' => $user->birthday ?? null,
|
||||||
|
'nickname' => $user->nickname ?? null,
|
||||||
|
'email' => $user->email ?? null,
|
||||||
|
'phone' => $user->phone ?? null,
|
||||||
|
'address_1' => $user->address_1 ?? null,
|
||||||
|
'address_2' => $user->address_2 ?? null,
|
||||||
|
'postcode' => $user->postcode ?? null,
|
||||||
|
'city' => $user->city ?? null,
|
||||||
|
'medications' => $user->medications ?? null,
|
||||||
|
'allergies' => $user->allergies ?? null,
|
||||||
|
'intolerances' => $user->intolerances ?? null,
|
||||||
|
'tetanus_vaccination' => $user->tetanus_vaccination ?? null,
|
||||||
|
'eating_habits' => $user->eating_habits ?? null,
|
||||||
|
'swimming_permission' => $user->swimming_permission ?? null,
|
||||||
|
'first_aid_permission' => $user->first_aid_permission ?? null,
|
||||||
|
'bank_account_owner' => $user->bank_account_owner ?? null,
|
||||||
|
'bank_account_iban' => $user->bank_account_iban ?? null,
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
public function updatePersonalData(UpdatePersonalDataRequest $request): void
|
||||||
|
{
|
||||||
|
$request->user->update([
|
||||||
|
'nickname' => $request->nickname,
|
||||||
|
'email' => $request->email,
|
||||||
|
'phone' => $request->phone,
|
||||||
|
'address_1' => $request->address1,
|
||||||
|
'address_2' => $request->address2,
|
||||||
|
'postcode' => $request->postcode,
|
||||||
|
'city' => $request->city,
|
||||||
|
'medications' => $request->medications,
|
||||||
|
'allergies' => $request->allergies,
|
||||||
|
'tetanus_vaccination' => $request->tetanusVaccination,
|
||||||
|
'intolerances' => $request->intolerances,
|
||||||
|
'eating_habits' => $request->eatingHabits,
|
||||||
|
'swimming_permission' => $request->swimmingPermission,
|
||||||
|
'first_aid_permission' => $request->firstAidPermission,
|
||||||
|
'bank_account_owner' => $request->bankAccountOwner,
|
||||||
|
'bank_account_iban' => $request->bankAccountIban,
|
||||||
|
]);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,14 +1,10 @@
|
|||||||
<?php
|
<?php
|
||||||
|
|
||||||
require __DIR__.'/../app/Domains/UserManagement/Routes/api.php';
|
|
||||||
|
|
||||||
|
|
||||||
use App\Domains\UserManagement\Controllers\EmailVerificationController;
|
use App\Domains\UserManagement\Controllers\EmailVerificationController;
|
||||||
use App\Domains\UserManagement\Controllers\RegistrationController;
|
use App\Domains\UserManagement\Controllers\RegistrationController;
|
||||||
use App\Domains\UserManagement\Controllers\ResetPasswordController;
|
use App\Domains\UserManagement\Controllers\ResetPasswordController;
|
||||||
use App\Middleware\IdentifyTenant;
|
use App\Middleware\IdentifyTenant;
|
||||||
use Illuminate\Support\Facades\Route;
|
use Illuminate\Support\Facades\Route;
|
||||||
use Inertia\Inertia;
|
|
||||||
|
|
||||||
Route::prefix('v1')
|
Route::prefix('v1')
|
||||||
->group(function () {
|
->group(function () {
|
||||||
|
|||||||
@@ -14,6 +14,7 @@ use Illuminate\Support\Facades\Route;
|
|||||||
require_once __DIR__ . '/../app/Domains/Dashboard/Routes/web.php';
|
require_once __DIR__ . '/../app/Domains/Dashboard/Routes/web.php';
|
||||||
require_once __DIR__ . '/../app/Domains/Dashboard/Routes/api.php';
|
require_once __DIR__ . '/../app/Domains/Dashboard/Routes/api.php';
|
||||||
require_once __DIR__ . '/../app/Domains/UserManagement/Routes/web.php';
|
require_once __DIR__ . '/../app/Domains/UserManagement/Routes/web.php';
|
||||||
|
require_once __DIR__ . '/../app/Domains/UserManagement/Routes/api.php';
|
||||||
require_once __DIR__ . '/../app/Domains/CostUnit/Routes/web.php';
|
require_once __DIR__ . '/../app/Domains/CostUnit/Routes/web.php';
|
||||||
require_once __DIR__ . '/../app/Domains/CostUnit/Routes/api.php';
|
require_once __DIR__ . '/../app/Domains/CostUnit/Routes/api.php';
|
||||||
require_once __DIR__ . '/../app/Domains/Invoice/Routes/web.php';
|
require_once __DIR__ . '/../app/Domains/Invoice/Routes/web.php';
|
||||||
@@ -38,25 +39,6 @@ Route::middleware(IdentifyTenant::class)->group(function () {
|
|||||||
Route::get('/retrieve-event-setting-data', [GlobalDataProvider::class, 'getEventSettingData']);
|
Route::get('/retrieve-event-setting-data', [GlobalDataProvider::class, 'getEventSettingData']);
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
Route::middleware(['auth'])->group(function () {
|
|
||||||
|
|
||||||
Route::get('/messages', fn () => inertia('Messages'));
|
|
||||||
|
|
||||||
});
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
Route::get('/messages', [TestRenderInertiaProvider::class, 'index']);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user