feat: implement admin user management interface and routes for CRUD operations and password resets

This commit is contained in:
jeremy bayse
2026-04-14 18:17:33 +02:00
parent e68108a2b1
commit 4a137fc511
3 changed files with 27 additions and 1 deletions

View File

@@ -94,4 +94,18 @@ class UserController extends Controller
return back()->with('success', 'Administrateur supprimé.'); return back()->with('success', 'Administrateur supprimé.');
} }
public function resetPassword(User $user)
{
if (!auth()->user()->isSuperAdmin()) {
abort(403, 'Unauthorized action.');
}
$password = Str::random(10);
$user->update([
'password' => Hash::make($password)
]);
return back()->with('success', 'Nouveau mot de passe généré pour ' . $user->name . ' : ' . $password);
}
} }

View File

@@ -1,8 +1,10 @@
<script setup> <script setup>
import { Head, useForm, Link } from '@inertiajs/vue3'; import { Head, useForm, Link, usePage } from '@inertiajs/vue3';
import { ref } from 'vue'; import { ref } from 'vue';
import AdminLayout from '@/Layouts/AdminLayout.vue'; import AdminLayout from '@/Layouts/AdminLayout.vue';
const page = usePage();
const props = defineProps({ const props = defineProps({
users: Array, users: Array,
tenants: Array tenants: Array
@@ -51,6 +53,12 @@ const deleteUser = (user) => {
} }
}; };
const resetPassword = (user) => {
if (confirm(`Êtes-vous sûr de vouloir réinitialiser le mot de passe de ${user.name} ? Un nouveau mot de passe sera généré aléatoirement.`)) {
form.post(route('admin.users.reset-password', user.id));
}
};
const cancel = () => { const cancel = () => {
isCreating.value = false; isCreating.value = false;
editingUser.value = null; editingUser.value = null;
@@ -136,6 +144,9 @@ const cancel = () => {
{{ user.tenant ? user.tenant.name : (user.role === 'super_admin' ? 'Toutes les structures' : 'Aucun rattachement') }} {{ user.tenant ? user.tenant.name : (user.role === 'super_admin' ? 'Toutes les structures' : 'Aucun rattachement') }}
</td> </td>
<td class="py-3 px-6 text-right space-x-2"> <td class="py-3 px-6 text-right space-x-2">
<button v-if="page.props.auth.user.role === 'super_admin'" @click="resetPassword(user)" class="text-orange-600 hover:text-orange-900 px-3 py-1 rounded bg-orange-50 hover:bg-orange-100 transition-colors" title="Réinitialiser le mot de passe">
MDP
</button>
<button @click="editUser(user)" class="text-indigo-600 hover:text-indigo-900 px-3 py-1 rounded bg-indigo-50 hover:bg-indigo-100 transition-colors">Modifier</button> <button @click="editUser(user)" class="text-indigo-600 hover:text-indigo-900 px-3 py-1 rounded bg-indigo-50 hover:bg-indigo-100 transition-colors">Modifier</button>
<button @click="deleteUser(user)" class="text-red-600 hover:text-red-900 px-3 py-1 rounded bg-red-50 hover:bg-red-100 transition-colors">Supprimer</button> <button @click="deleteUser(user)" class="text-red-600 hover:text-red-900 px-3 py-1 rounded bg-red-50 hover:bg-red-100 transition-colors">Supprimer</button>
</td> </td>

View File

@@ -86,6 +86,7 @@ Route::middleware('auth')->group(function () {
Route::resource('quizzes.questions', \App\Http\Controllers\QuestionController::class)->only(['store', 'update', 'destroy']); Route::resource('quizzes.questions', \App\Http\Controllers\QuestionController::class)->only(['store', 'update', 'destroy']);
Route::resource('tenants', \App\Http\Controllers\TenantController::class)->only(['index', 'store', 'update', 'destroy']); Route::resource('tenants', \App\Http\Controllers\TenantController::class)->only(['index', 'store', 'update', 'destroy']);
Route::resource('users', \App\Http\Controllers\UserController::class)->except(['show', 'create', 'edit']); Route::resource('users', \App\Http\Controllers\UserController::class)->except(['show', 'create', 'edit']);
Route::post('/users/{user}/reset-password', [\App\Http\Controllers\UserController::class, 'resetPassword'])->name('users.reset-password');
Route::get('/backup', [\App\Http\Controllers\BackupController::class, 'download'])->name('backup'); Route::get('/backup', [\App\Http\Controllers\BackupController::class, 'download'])->name('backup');
Route::delete('/attempts/{attempt}', [\App\Http\Controllers\AttemptController::class, 'destroy'])->name('attempts.destroy'); Route::delete('/attempts/{attempt}', [\App\Http\Controllers\AttemptController::class, 'destroy'])->name('attempts.destroy');
Route::patch('/answers/{answer}/score', [\App\Http\Controllers\AttemptController::class, 'updateAnswerScore'])->name('answers.update-score'); Route::patch('/answers/{answer}/score', [\App\Http\Controllers\AttemptController::class, 'updateAnswerScore'])->name('answers.update-score');