diff --git a/app/Domains/Admin/Controllers/UserDetailGetController.php b/app/Domains/Admin/Controllers/UserDetailGetController.php
new file mode 100644
index 0000000..9e013e7
--- /dev/null
+++ b/app/Domains/Admin/Controllers/UserDetailGetController.php
@@ -0,0 +1,35 @@
+toArray();
+
+ unset($userData['password'], $userData['remember_token'], $userData['activation_token'], $userData['activation_token_expires_at']);
+
+ $tenantNames = Tenant::pluck('name', 'slug');
+ $userData['nicename'] = $user->getNicename();
+ $userData['fullname'] = $user->getFullName();
+ $userData['local_group_name'] = $tenantNames[$user->local_group] ?? $user->local_group;
+
+ return response()->json([
+ 'user' => $userData,
+ 'isOwnUser' => auth()->id() === $user->id,
+ 'isLvTenant' => $this->tenant->slug === 'lv',
+ 'userRoles' => UserRole::all()->map(fn($role) => ['slug' => $role->slug, 'name' => $role->name]),
+ 'localGroups' => Tenant::where('is_active_local_group', true)->get()->map(fn($t) => ['slug' => $t->slug, 'name' => $t->name]),
+ ]);
+ }
+}
diff --git a/app/Domains/Admin/Controllers/UserListApiController.php b/app/Domains/Admin/Controllers/UserListApiController.php
new file mode 100644
index 0000000..e4aa5dc
--- /dev/null
+++ b/app/Domains/Admin/Controllers/UserListApiController.php
@@ -0,0 +1,40 @@
+tenant->slug === 'lv') {
+ $query->orderBy('lastname')->orderBy('firstname');
+ } else {
+ $query->where('local_group', $this->tenant->slug)
+ ->orderBy('lastname')->orderBy('firstname');
+ }
+
+ $users = $query->get()->map(function ($user) use ($tenantNames) {
+ return [
+ 'id' => $user->id,
+ 'firstname' => $user->firstname,
+ 'lastname' => $user->lastname,
+ 'nickname' => $user->nickname,
+ 'local_group' => $user->local_group,
+ 'local_group_name' => $tenantNames[$user->local_group] ?? $user->local_group,
+ 'active' => $user->active,
+ ];
+ });
+
+ return response()->json(['users' => $users]);
+ }
+}
diff --git a/app/Domains/Admin/Controllers/UserListPageController.php b/app/Domains/Admin/Controllers/UserListPageController.php
new file mode 100644
index 0000000..1ebc516
--- /dev/null
+++ b/app/Domains/Admin/Controllers/UserListPageController.php
@@ -0,0 +1,19 @@
+ $this->tenant->slug === 'lv',
+ ]);
+ return $inertiaProvider->render();
+ }
+}
diff --git a/app/Domains/Admin/Controllers/UserResetPasswordController.php b/app/Domains/Admin/Controllers/UserResetPasswordController.php
new file mode 100644
index 0000000..253034f
--- /dev/null
+++ b/app/Domains/Admin/Controllers/UserResetPasswordController.php
@@ -0,0 +1,32 @@
+email) {
+ return response()->json([
+ 'status' => 'error',
+ 'message' => 'Benutzer*in hat keine E-Mail-Adresse hinterlegt.',
+ ]);
+ }
+
+ $command = new GenerateActivationTokenCommand($user);
+ $command->execute();
+
+ return response()->json([
+ 'status' => 'success',
+ 'message' => 'Passwort-Reset-Mail wurde gesendet.',
+ ]);
+ }
+}
diff --git a/app/Domains/Admin/Controllers/UserToggleActiveController.php b/app/Domains/Admin/Controllers/UserToggleActiveController.php
new file mode 100644
index 0000000..4c5ffe5
--- /dev/null
+++ b/app/Domains/Admin/Controllers/UserToggleActiveController.php
@@ -0,0 +1,33 @@
+id() === $user->id) {
+ return response()->json([
+ 'status' => 'error',
+ 'message' => 'Du kannst dich nicht selbst deaktivieren.',
+ ]);
+ }
+
+ $user->update(['active' => !$user->active]);
+
+ $status = $user->active ? 'aktiviert' : 'deaktiviert';
+
+ return response()->json([
+ 'status' => 'success',
+ 'message' => 'Benutzer*in wurde ' . $status . '.',
+ 'active' => $user->active,
+ ]);
+ }
+}
diff --git a/app/Domains/Admin/Controllers/UserUpdateController.php b/app/Domains/Admin/Controllers/UserUpdateController.php
new file mode 100644
index 0000000..de85b97
--- /dev/null
+++ b/app/Domains/Admin/Controllers/UserUpdateController.php
@@ -0,0 +1,42 @@
+id() === $user->id;
+ $isLvTenant = $this->tenant->slug === 'lv';
+
+ $allowedFields = [
+ 'firstname', 'lastname', 'nickname', 'email', 'phone', 'birthday',
+ 'membership_id', 'address_1', 'address_2', 'postcode', 'city',
+ 'eating_habits', 'swimming_permission', 'first_aid_permission',
+ 'bank_account_owner', 'bank_account_iban',
+ 'medications', 'allergies', 'intolerances',
+ 'user_role_local_group',
+ ];
+
+ if ($isLvTenant) {
+ $allowedFields[] = 'local_group';
+ if (!$isOwnUser) {
+ $allowedFields[] = 'user_role_main';
+ }
+ }
+
+ $data = $request->only($allowedFields);
+ $user->update($data);
+
+ return response()->json([
+ 'status' => 'success',
+ 'message' => 'Benutzerdaten wurden gespeichert.',
+ ]);
+ }
+}
diff --git a/app/Domains/Admin/Routes/api.php b/app/Domains/Admin/Routes/api.php
index d31a421..2d1337d 100644
--- a/app/Domains/Admin/Routes/api.php
+++ b/app/Domains/Admin/Routes/api.php
@@ -1,5 +1,10 @@
Route::post('/gdpr', TenantGdprUpdateController::class);
});
+ Route::prefix('api/v1/admin/users')->group(function () {
+ Route::get('/list', UserListApiController::class);
+ Route::get('/{id}', UserDetailGetController::class);
+ Route::post('/{id}', UserUpdateController::class);
+ Route::post('/{id}/toggle-active', UserToggleActiveController::class);
+ Route::post('/{id}/reset-password', UserResetPasswordController::class);
+ });
+
Route::middleware(LvOnlyMiddleware::class)->group(function () {
Route::prefix('api/v1/admin/tenants')->group(function () {
Route::get('/list', TenantListApiController::class);
diff --git a/app/Domains/Admin/Routes/web.php b/app/Domains/Admin/Routes/web.php
index 9846e40..793962f 100644
--- a/app/Domains/Admin/Routes/web.php
+++ b/app/Domains/Admin/Routes/web.php
@@ -4,6 +4,7 @@ use App\Domains\Admin\Controllers\AdminDashboardController;
use App\Domains\Admin\Controllers\TenantEditPageController;
use App\Domains\Admin\Controllers\TenantListPageController;
use App\Domains\Admin\Controllers\TenantPageController;
+use App\Domains\Admin\Controllers\UserListPageController;
use App\Middleware\AdminRoleMiddleware;
use App\Middleware\IdentifyTenant;
use App\Middleware\LvOnlyMiddleware;
@@ -13,6 +14,7 @@ Route::middleware([IdentifyTenant::class, 'auth', AdminRoleMiddleware::class])->
Route::prefix('admin')->group(function () {
Route::get('/', AdminDashboardController::class);
Route::get('/tenant', TenantPageController::class);
+ Route::get('/users', UserListPageController::class);
Route::middleware(LvOnlyMiddleware::class)->group(function () {
Route::get('/tenants', TenantListPageController::class);
diff --git a/app/Domains/Admin/Views/Partials/UserDetail.vue b/app/Domains/Admin/Views/Partials/UserDetail.vue
new file mode 100644
index 0000000..2a0106d
--- /dev/null
+++ b/app/Domains/Admin/Views/Partials/UserDetail.vue
@@ -0,0 +1,331 @@
+
+
+
+
+
+
+
+
+ | Anmeldename: | {{ user.username }} |
+ | Vorname: | {{ form.firstname }} |
+ | Nachname: | {{ form.lastname }} |
+ | Nickname: | {{ form.nickname }} |
+ | E-Mail: | {{ form.email }} |
+ | Telefon: | {{ form.phone }} |
+ | Geburtstag: | {{ form.birthday }} |
+ | Mitgliedsnummer: | {{ form.membership_id }} |
+ | Adresse 1: | {{ form.address_1 }} |
+ | Adresse 2: | {{ form.address_2 }} |
+ | PLZ: | {{ form.postcode }} |
+ | Ort: | {{ form.city }} |
+ | Medikamente: | {{ form.medications }} |
+ | Allergien: | {{ form.allergies }} |
+ | Unverträglichkeiten: | {{ form.intolerances }} |
+ | Kontoinhaber: | {{ form.bank_account_owner }} |
+ | IBAN: | {{ form.bank_account_iban }} |
+ | Stamm: | {{ getGroupName(form.local_group) }} |
+ | Rolle (Stamm): | {{ getRoleName(form.user_role_local_group) }} |
+ | Rolle (LV): | {{ getRoleName(form.user_role_main) }} |
+ | Status: |
+
+ {{ user.active ? 'Aktiv' : 'Inaktiv' }}
+
+ |
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/app/Domains/Admin/Views/UserList.vue b/app/Domains/Admin/Views/UserList.vue
new file mode 100644
index 0000000..a325bf9
--- /dev/null
+++ b/app/Domains/Admin/Views/UserList.vue
@@ -0,0 +1,167 @@
+
+
+
+
+
+
+
+
+ | Nachname |
+ Vorname |
+ Pfadiname |
+ Stamm |
+ Status |
+
+
+
+
+ | {{ user.lastname }} |
+ {{ user.firstname }} |
+ {{ user.nickname }} |
+ {{ user.local_group_name }} |
+
+
+ {{ user.active ? 'Aktiv' : 'Inaktiv' }}
+
+ |
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/resources/js/layouts/AdminAppLayout.vue b/resources/js/layouts/AdminAppLayout.vue
index b4a1235..f0e4370 100644
--- a/resources/js/layouts/AdminAppLayout.vue
+++ b/resources/js/layouts/AdminAppLayout.vue
@@ -109,6 +109,9 @@ const props = defineProps({
Stämme
+
+ Benutzer*innen
+