Add admin user management (create, edit, password change, delete)
Deploy to Production (witstore.wende.it) / deploy (push) Successful in 6s
Deploy to Production (witstore.wende.it) / deploy (push) Successful in 6s
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,43 @@
|
|||||||
|
APP_NAME="WIT-TEST-Shop"
|
||||||
|
APP_ENV=production
|
||||||
|
APP_KEY=
|
||||||
|
APP_DEBUG=false
|
||||||
|
APP_URL=https://witstore.wende.it
|
||||||
|
|
||||||
|
APP_LOCALE=de
|
||||||
|
APP_FALLBACK_LOCALE=de
|
||||||
|
APP_FAKER_LOCALE=de_DE
|
||||||
|
|
||||||
|
BCRYPT_ROUNDS=12
|
||||||
|
|
||||||
|
LOG_CHANNEL=stack
|
||||||
|
LOG_LEVEL=error
|
||||||
|
|
||||||
|
DB_CONNECTION=mysql
|
||||||
|
DB_HOST=127.0.0.1
|
||||||
|
DB_PORT=3306
|
||||||
|
DB_DATABASE=wendeIT_WITS
|
||||||
|
DB_USERNAME=wendeIT_WITSU
|
||||||
|
DB_PASSWORD=Sfn4r3z3U?krhzM?
|
||||||
|
|
||||||
|
SESSION_DRIVER=file
|
||||||
|
SESSION_LIFETIME=120
|
||||||
|
SESSION_ENCRYPT=false
|
||||||
|
SESSION_PATH=/
|
||||||
|
SESSION_DOMAIN=null
|
||||||
|
|
||||||
|
FILESYSTEM_DISK=public
|
||||||
|
QUEUE_CONNECTION=database
|
||||||
|
|
||||||
|
CACHE_STORE=file
|
||||||
|
|
||||||
|
MAIL_MAILER=smtp
|
||||||
|
MAIL_SCHEME=tls
|
||||||
|
MAIL_HOST=smtp.wende.it
|
||||||
|
MAIL_PORT=587
|
||||||
|
MAIL_USERNAME=shop@wende.it
|
||||||
|
MAIL_PASSWORD=
|
||||||
|
MAIL_FROM_ADDRESS="shop@wende.it"
|
||||||
|
MAIL_FROM_NAME="WIT-TEST-Shop"
|
||||||
|
|
||||||
|
VITE_APP_NAME="${APP_NAME}"
|
||||||
@@ -0,0 +1,82 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Http\Controllers\Admin;
|
||||||
|
|
||||||
|
use App\Http\Controllers\Controller;
|
||||||
|
use App\Models\User;
|
||||||
|
use Illuminate\Http\Request;
|
||||||
|
use Illuminate\Support\Facades\Hash;
|
||||||
|
|
||||||
|
class AdminUserController extends Controller
|
||||||
|
{
|
||||||
|
public function index()
|
||||||
|
{
|
||||||
|
$admins = User::whereIn('role', ['admin', 'super_admin'])->orderBy('name')->get();
|
||||||
|
return view('admin.admins.index', compact('admins'));
|
||||||
|
}
|
||||||
|
|
||||||
|
public function create()
|
||||||
|
{
|
||||||
|
return view('admin.admins.form', ['admin' => null]);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function store(Request $request)
|
||||||
|
{
|
||||||
|
$data = $request->validate([
|
||||||
|
'name' => 'required|string|max:255',
|
||||||
|
'email' => 'required|email|unique:users,email',
|
||||||
|
'role' => 'required|in:admin,super_admin',
|
||||||
|
'password' => 'required|min:8|confirmed',
|
||||||
|
]);
|
||||||
|
|
||||||
|
User::create([
|
||||||
|
'name' => $data['name'],
|
||||||
|
'email' => $data['email'],
|
||||||
|
'role' => $data['role'],
|
||||||
|
'password' => Hash::make($data['password']),
|
||||||
|
'approved' => true,
|
||||||
|
'email_verified_at' => now(),
|
||||||
|
]);
|
||||||
|
|
||||||
|
return redirect()->route('admin.admins.index')->with('success', 'Admin-Benutzer angelegt.');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function edit(User $admin)
|
||||||
|
{
|
||||||
|
abort_if(! $admin->isAdmin(), 404);
|
||||||
|
return view('admin.admins.form', compact('admin'));
|
||||||
|
}
|
||||||
|
|
||||||
|
public function update(Request $request, User $admin)
|
||||||
|
{
|
||||||
|
abort_if(! $admin->isAdmin(), 404);
|
||||||
|
|
||||||
|
$data = $request->validate([
|
||||||
|
'name' => 'required|string|max:255',
|
||||||
|
'email' => 'required|email|unique:users,email,' . $admin->id,
|
||||||
|
'role' => 'required|in:admin,super_admin',
|
||||||
|
'password' => 'nullable|min:8|confirmed',
|
||||||
|
]);
|
||||||
|
|
||||||
|
$admin->name = $data['name'];
|
||||||
|
$admin->email = $data['email'];
|
||||||
|
$admin->role = $data['role'];
|
||||||
|
|
||||||
|
if (!empty($data['password'])) {
|
||||||
|
$admin->password = Hash::make($data['password']);
|
||||||
|
}
|
||||||
|
|
||||||
|
$admin->save();
|
||||||
|
|
||||||
|
return redirect()->route('admin.admins.index')->with('success', 'Admin-Benutzer aktualisiert.');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function destroy(User $admin)
|
||||||
|
{
|
||||||
|
abort_if(! $admin->isAdmin(), 404);
|
||||||
|
abort_if($admin->id === auth()->id(), 403, 'Sie können sich nicht selbst löschen.');
|
||||||
|
|
||||||
|
$admin->delete();
|
||||||
|
return back()->with('success', 'Admin-Benutzer gelöscht.');
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,60 @@
|
|||||||
|
@extends('layouts.admin')
|
||||||
|
@section('title', $admin ? 'Admin bearbeiten' : 'Neuer Admin')
|
||||||
|
@section('content')
|
||||||
|
<div class="max-w-lg">
|
||||||
|
<h1 class="text-2xl font-bold mb-6">{{ $admin ? 'Admin bearbeiten' : 'Neuer Admin-Benutzer' }}</h1>
|
||||||
|
|
||||||
|
<form method="POST" action="{{ $admin ? route('admin.admins.update', $admin) : route('admin.admins.store') }}" class="bg-white border rounded-lg p-6 space-y-4">
|
||||||
|
@csrf
|
||||||
|
@if($admin) @method('PUT') @endif
|
||||||
|
|
||||||
|
@if($errors->any())
|
||||||
|
<div class="bg-red-50 border border-red-200 rounded p-3 text-sm text-red-700">
|
||||||
|
@foreach($errors->all() as $e)<div>• {{ $e }}</div>@endforeach
|
||||||
|
</div>
|
||||||
|
@endif
|
||||||
|
|
||||||
|
<div>
|
||||||
|
<label class="block text-sm font-medium mb-1">Name *</label>
|
||||||
|
<input type="text" name="name" value="{{ old('name', $admin?->name) }}"
|
||||||
|
class="w-full border rounded px-3 py-2 text-sm" required>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div>
|
||||||
|
<label class="block text-sm font-medium mb-1">E-Mail *</label>
|
||||||
|
<input type="email" name="email" value="{{ old('email', $admin?->email) }}"
|
||||||
|
class="w-full border rounded px-3 py-2 text-sm" required>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div>
|
||||||
|
<label class="block text-sm font-medium mb-1">Rolle *</label>
|
||||||
|
<select name="role" class="w-full border rounded px-3 py-2 text-sm">
|
||||||
|
<option value="admin" {{ old('role', $admin?->role) === 'admin' ? 'selected' : '' }}>Admin</option>
|
||||||
|
<option value="super_admin" {{ old('role', $admin?->role) === 'super_admin' ? 'selected' : '' }}>Super-Admin</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="border-t pt-4">
|
||||||
|
<label class="block text-sm font-medium mb-1">
|
||||||
|
{{ $admin ? 'Neues Passwort (leer lassen = unverändert)' : 'Passwort *' }}
|
||||||
|
</label>
|
||||||
|
<input type="password" name="password"
|
||||||
|
class="w-full border rounded px-3 py-2 text-sm"
|
||||||
|
{{ $admin ? '' : 'required' }}>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div>
|
||||||
|
<label class="block text-sm font-medium mb-1">Passwort bestätigen</label>
|
||||||
|
<input type="password" name="password_confirmation"
|
||||||
|
class="w-full border rounded px-3 py-2 text-sm">
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="flex gap-3 pt-2">
|
||||||
|
<button type="submit" class="bg-blue-600 text-white px-5 py-2 rounded-lg hover:bg-blue-700 text-sm font-medium">
|
||||||
|
{{ $admin ? 'Speichern' : 'Anlegen' }}
|
||||||
|
</button>
|
||||||
|
<a href="{{ route('admin.admins.index') }}" class="px-5 py-2 border rounded-lg text-sm hover:bg-gray-50">Abbrechen</a>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
@endsection
|
||||||
@@ -0,0 +1,57 @@
|
|||||||
|
@extends('layouts.admin')
|
||||||
|
@section('title', 'Admin-Benutzer')
|
||||||
|
@section('content')
|
||||||
|
<div class="flex items-center justify-between mb-6">
|
||||||
|
<h1 class="text-2xl font-bold">Admin-Benutzer</h1>
|
||||||
|
<a href="{{ route('admin.admins.create') }}" class="bg-blue-600 text-white px-4 py-2 rounded-lg hover:bg-blue-700 text-sm font-medium">
|
||||||
|
+ Neuer Admin
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
@if(session('success'))
|
||||||
|
<div class="bg-green-100 border border-green-300 text-green-800 px-4 py-3 rounded-lg mb-4">{{ session('success') }}</div>
|
||||||
|
@endif
|
||||||
|
|
||||||
|
<div class="bg-white rounded-lg border overflow-hidden">
|
||||||
|
<table class="w-full text-sm">
|
||||||
|
<thead class="bg-gray-50 border-b">
|
||||||
|
<tr>
|
||||||
|
<th class="text-left px-4 py-3 font-medium text-gray-600">Name</th>
|
||||||
|
<th class="text-left px-4 py-3 font-medium text-gray-600">E-Mail</th>
|
||||||
|
<th class="text-left px-4 py-3 font-medium text-gray-600">Rolle</th>
|
||||||
|
<th class="text-left px-4 py-3 font-medium text-gray-600">Erstellt</th>
|
||||||
|
<th class="px-4 py-3"></th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody class="divide-y">
|
||||||
|
@foreach($admins as $a)
|
||||||
|
<tr class="hover:bg-gray-50">
|
||||||
|
<td class="px-4 py-3 font-medium">
|
||||||
|
{{ $a->name }}
|
||||||
|
@if($a->id === auth()->id()) <span class="text-xs text-blue-500">(Sie)</span> @endif
|
||||||
|
</td>
|
||||||
|
<td class="px-4 py-3 text-gray-600">{{ $a->email }}</td>
|
||||||
|
<td class="px-4 py-3">
|
||||||
|
@if($a->role === 'super_admin')
|
||||||
|
<span class="bg-purple-100 text-purple-700 text-xs px-2 py-0.5 rounded-full">Super-Admin</span>
|
||||||
|
@else
|
||||||
|
<span class="bg-blue-100 text-blue-700 text-xs px-2 py-0.5 rounded-full">Admin</span>
|
||||||
|
@endif
|
||||||
|
</td>
|
||||||
|
<td class="px-4 py-3 text-gray-500">{{ $a->created_at->format('d.m.Y') }}</td>
|
||||||
|
<td class="px-4 py-3 text-right space-x-2">
|
||||||
|
<a href="{{ route('admin.admins.edit', $a) }}" class="text-blue-600 hover:underline text-xs">Bearbeiten</a>
|
||||||
|
@if($a->id !== auth()->id())
|
||||||
|
<form method="POST" action="{{ route('admin.admins.destroy', $a) }}" class="inline"
|
||||||
|
onsubmit="return confirm('{{ $a->name }} wirklich löschen?')">
|
||||||
|
@csrf @method('DELETE')
|
||||||
|
<button class="text-red-500 hover:underline text-xs">Löschen</button>
|
||||||
|
</form>
|
||||||
|
@endif
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
@endforeach
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
@endsection
|
||||||
@@ -28,6 +28,7 @@
|
|||||||
<a href="{{ route('admin.banners.index') }}" class="block px-3 py-2 rounded hover:bg-gray-700 {{ request()->routeIs('admin.banners.*') ? 'bg-gray-700' : '' }}">Banner</a>
|
<a href="{{ route('admin.banners.index') }}" class="block px-3 py-2 rounded hover:bg-gray-700 {{ request()->routeIs('admin.banners.*') ? 'bg-gray-700' : '' }}">Banner</a>
|
||||||
<a href="{{ route('admin.faqs.index') }}" class="block px-3 py-2 rounded hover:bg-gray-700 {{ request()->routeIs('admin.faqs.*') ? 'bg-gray-700' : '' }}">FAQ</a>
|
<a href="{{ route('admin.faqs.index') }}" class="block px-3 py-2 rounded hover:bg-gray-700 {{ request()->routeIs('admin.faqs.*') ? 'bg-gray-700' : '' }}">FAQ</a>
|
||||||
<div class="pt-2 pb-1 text-xs text-gray-400 uppercase tracking-wide">System</div>
|
<div class="pt-2 pb-1 text-xs text-gray-400 uppercase tracking-wide">System</div>
|
||||||
|
<a href="{{ route('admin.admins.index') }}" class="block px-3 py-2 rounded hover:bg-gray-700 {{ request()->routeIs('admin.admins.*') ? 'bg-gray-700' : '' }}">Admin-Benutzer</a>
|
||||||
<a href="{{ route('admin.settings.index') }}" class="block px-3 py-2 rounded hover:bg-gray-700 {{ request()->routeIs('admin.settings.*') ? 'bg-gray-700' : '' }}">Einstellungen</a>
|
<a href="{{ route('admin.settings.index') }}" class="block px-3 py-2 rounded hover:bg-gray-700 {{ request()->routeIs('admin.settings.*') ? 'bg-gray-700' : '' }}">Einstellungen</a>
|
||||||
</nav>
|
</nav>
|
||||||
<div class="p-4 border-t border-gray-700 text-xs text-gray-400">
|
<div class="p-4 border-t border-gray-700 text-xs text-gray-400">
|
||||||
|
|||||||
@@ -97,6 +97,14 @@ Route::middleware(['auth', 'admin'])->prefix('admin')->name('admin.')->group(fun
|
|||||||
Route::put('faqs/{faq}', [Admin\FaqController::class, 'update'])->name('faqs.update');
|
Route::put('faqs/{faq}', [Admin\FaqController::class, 'update'])->name('faqs.update');
|
||||||
Route::delete('faqs/{faq}', [Admin\FaqController::class, 'destroy'])->name('faqs.destroy');
|
Route::delete('faqs/{faq}', [Admin\FaqController::class, 'destroy'])->name('faqs.destroy');
|
||||||
|
|
||||||
|
// Admin-Benutzer
|
||||||
|
Route::get('admins', [Admin\AdminUserController::class, 'index'])->name('admins.index');
|
||||||
|
Route::get('admins/create', [Admin\AdminUserController::class, 'create'])->name('admins.create');
|
||||||
|
Route::post('admins', [Admin\AdminUserController::class, 'store'])->name('admins.store');
|
||||||
|
Route::get('admins/{admin}/edit', [Admin\AdminUserController::class, 'edit'])->name('admins.edit');
|
||||||
|
Route::put('admins/{admin}', [Admin\AdminUserController::class, 'update'])->name('admins.update');
|
||||||
|
Route::delete('admins/{admin}', [Admin\AdminUserController::class, 'destroy'])->name('admins.destroy');
|
||||||
|
|
||||||
// Einstellungen
|
// Einstellungen
|
||||||
Route::get('settings', [Admin\SettingsController::class, 'index'])->name('settings.index');
|
Route::get('settings', [Admin\SettingsController::class, 'index'])->name('settings.index');
|
||||||
Route::put('settings', [Admin\SettingsController::class, 'update'])->name('settings.update');
|
Route::put('settings', [Admin\SettingsController::class, 'update'])->name('settings.update');
|
||||||
|
|||||||
Reference in New Issue
Block a user