- Add usePermissions composable (isAdmin, canEdit, canView) - Password-protected profile login with modal on profiles page - Disable all form fields for ROLE_VIEWER across edit/create pages - Show navigation buttons (Modifier/Consulter) for all roles, hide delete for viewers - Add readonly prop to ModelTypeForm for category pages - Disable modal fields (sites, constructeurs) for viewers - Guard /admin routes in middleware Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
246 lines
8.0 KiB
Vue
246 lines
8.0 KiB
Vue
<template>
|
|
<div class="container mx-auto p-6 max-w-6xl">
|
|
<div class="flex items-center justify-between mb-6">
|
|
<h1 class="text-2xl font-bold">
|
|
Administration des profils
|
|
</h1>
|
|
<button class="btn btn-primary btn-sm" @click="showCreateDialog = true">
|
|
Nouveau profil
|
|
</button>
|
|
</div>
|
|
|
|
<div v-if="loading" class="flex justify-center py-12">
|
|
<span class="loading loading-spinner loading-lg" />
|
|
</div>
|
|
|
|
<div v-else-if="profiles.length" class="overflow-x-auto">
|
|
<table class="table table-zebra w-full">
|
|
<thead>
|
|
<tr>
|
|
<th>Nom</th>
|
|
<th>Email</th>
|
|
<th>Role</th>
|
|
<th>Mot de passe</th>
|
|
<th>Statut</th>
|
|
<th>Actions</th>
|
|
</tr>
|
|
</thead>
|
|
<tbody>
|
|
<tr v-for="profile in profiles" :key="profile.id">
|
|
<td class="font-medium">
|
|
{{ profile.firstName }} {{ profile.lastName }}
|
|
</td>
|
|
<td class="text-sm text-base-content/70">
|
|
{{ profile.email || '-' }}
|
|
</td>
|
|
<td>
|
|
<select
|
|
class="select select-bordered select-xs"
|
|
:value="primaryRole(profile)"
|
|
@change="handleRoleChange(profile.id, $event.target.value)"
|
|
>
|
|
<option value="ROLE_ADMIN">
|
|
Admin
|
|
</option>
|
|
<option value="ROLE_GESTIONNAIRE">
|
|
Gestionnaire
|
|
</option>
|
|
<option value="ROLE_VIEWER">
|
|
Viewer
|
|
</option>
|
|
</select>
|
|
</td>
|
|
<td>
|
|
<span v-if="profile.hasPassword" class="badge badge-success badge-sm">Oui</span>
|
|
<span v-else class="badge badge-ghost badge-sm">Non</span>
|
|
<button
|
|
class="btn btn-ghost btn-xs ml-1"
|
|
@click="openPasswordDialog(profile.id)"
|
|
>
|
|
{{ profile.hasPassword ? 'Changer' : 'Definir' }}
|
|
</button>
|
|
</td>
|
|
<td>
|
|
<span
|
|
class="badge badge-sm"
|
|
:class="profile.isActive ? 'badge-success' : 'badge-error'"
|
|
>
|
|
{{ profile.isActive ? 'Actif' : 'Inactif' }}
|
|
</span>
|
|
</td>
|
|
<td>
|
|
<button
|
|
v-if="profile.isActive"
|
|
class="btn btn-ghost btn-xs text-error"
|
|
@click="handleDeactivate(profile.id)"
|
|
>
|
|
Desactiver
|
|
</button>
|
|
</td>
|
|
</tr>
|
|
</tbody>
|
|
</table>
|
|
</div>
|
|
|
|
<div v-else class="text-center py-12 text-base-content/60">
|
|
Aucun profil.
|
|
</div>
|
|
|
|
<!-- Create Profile Dialog -->
|
|
<dialog ref="createDialog" class="modal" :open="showCreateDialog || undefined">
|
|
<div class="modal-box">
|
|
<h3 class="font-bold text-lg mb-4">
|
|
Nouveau profil
|
|
</h3>
|
|
<form @submit.prevent="handleCreate">
|
|
<div class="form-control mb-3">
|
|
<label class="label"><span class="label-text">Prenom</span></label>
|
|
<input v-model="createForm.firstName" type="text" class="input input-bordered" required>
|
|
</div>
|
|
<div class="form-control mb-3">
|
|
<label class="label"><span class="label-text">Nom</span></label>
|
|
<input v-model="createForm.lastName" type="text" class="input input-bordered" required>
|
|
</div>
|
|
<div class="form-control mb-3">
|
|
<label class="label"><span class="label-text">Email</span></label>
|
|
<input v-model="createForm.email" type="email" class="input input-bordered">
|
|
</div>
|
|
<div class="form-control mb-3">
|
|
<label class="label"><span class="label-text">Mot de passe</span></label>
|
|
<input v-model="createForm.password" type="password" class="input input-bordered">
|
|
</div>
|
|
<div class="form-control mb-3">
|
|
<label class="label"><span class="label-text">Role</span></label>
|
|
<select v-model="createForm.role" class="select select-bordered">
|
|
<option value="ROLE_ADMIN">
|
|
Admin
|
|
</option>
|
|
<option value="ROLE_GESTIONNAIRE">
|
|
Gestionnaire
|
|
</option>
|
|
<option value="ROLE_VIEWER">
|
|
Viewer
|
|
</option>
|
|
</select>
|
|
</div>
|
|
<div class="modal-action">
|
|
<button type="button" class="btn btn-ghost" @click="showCreateDialog = false">
|
|
Annuler
|
|
</button>
|
|
<button type="submit" class="btn btn-primary" :disabled="creating">
|
|
<span v-if="creating" class="loading loading-spinner loading-xs" />
|
|
Creer
|
|
</button>
|
|
</div>
|
|
</form>
|
|
</div>
|
|
<form method="dialog" class="modal-backdrop">
|
|
<button type="button" @click="showCreateDialog = false">
|
|
close
|
|
</button>
|
|
</form>
|
|
</dialog>
|
|
|
|
<!-- Set Password Dialog -->
|
|
<dialog ref="passwordDialog" class="modal" :open="showPasswordDialog || undefined">
|
|
<div class="modal-box">
|
|
<h3 class="font-bold text-lg mb-4">
|
|
Definir le mot de passe
|
|
</h3>
|
|
<form @submit.prevent="handleSetPassword">
|
|
<div class="form-control mb-3">
|
|
<label class="label"><span class="label-text">Nouveau mot de passe</span></label>
|
|
<input v-model="newPassword" type="password" class="input input-bordered" required>
|
|
</div>
|
|
<div class="modal-action">
|
|
<button type="button" class="btn btn-ghost" @click="showPasswordDialog = false">
|
|
Annuler
|
|
</button>
|
|
<button type="submit" class="btn btn-primary" :disabled="settingPassword">
|
|
<span v-if="settingPassword" class="loading loading-spinner loading-xs" />
|
|
Valider
|
|
</button>
|
|
</div>
|
|
</form>
|
|
</div>
|
|
<form method="dialog" class="modal-backdrop">
|
|
<button type="button" @click="showPasswordDialog = false">
|
|
close
|
|
</button>
|
|
</form>
|
|
</dialog>
|
|
</div>
|
|
</template>
|
|
|
|
<script setup>
|
|
import { ref, onMounted } from 'vue'
|
|
import { useAdminProfiles } from '#imports'
|
|
|
|
const { profiles, loading, fetchAll, createProfile, updateRole, setPassword, deactivateProfile } = useAdminProfiles()
|
|
|
|
const showCreateDialog = ref(false)
|
|
const showPasswordDialog = ref(false)
|
|
const creating = ref(false)
|
|
const settingPassword = ref(false)
|
|
const passwordProfileId = ref(null)
|
|
const newPassword = ref('')
|
|
|
|
const createForm = ref({
|
|
firstName: '',
|
|
lastName: '',
|
|
email: '',
|
|
password: '',
|
|
role: 'ROLE_VIEWER',
|
|
})
|
|
|
|
const primaryRole = (profile) => {
|
|
const roles = profile.roles || []
|
|
if (roles.includes('ROLE_ADMIN')) { return 'ROLE_ADMIN' }
|
|
if (roles.includes('ROLE_GESTIONNAIRE')) { return 'ROLE_GESTIONNAIRE' }
|
|
return 'ROLE_VIEWER'
|
|
}
|
|
|
|
const handleCreate = async () => {
|
|
creating.value = true
|
|
try {
|
|
const data = { ...createForm.value }
|
|
if (!data.email) { delete data.email }
|
|
if (!data.password) { delete data.password }
|
|
await createProfile(data)
|
|
showCreateDialog.value = false
|
|
createForm.value = { firstName: '', lastName: '', email: '', password: '', role: 'ROLE_VIEWER' }
|
|
} finally {
|
|
creating.value = false
|
|
}
|
|
}
|
|
|
|
const handleRoleChange = async (profileId, role) => {
|
|
await updateRole(profileId, role)
|
|
}
|
|
|
|
const openPasswordDialog = (profileId) => {
|
|
passwordProfileId.value = profileId
|
|
newPassword.value = ''
|
|
showPasswordDialog.value = true
|
|
}
|
|
|
|
const handleSetPassword = async () => {
|
|
if (!passwordProfileId.value) { return }
|
|
settingPassword.value = true
|
|
try {
|
|
await setPassword(passwordProfileId.value, newPassword.value)
|
|
showPasswordDialog.value = false
|
|
} finally {
|
|
settingPassword.value = false
|
|
}
|
|
}
|
|
|
|
const handleDeactivate = async (profileId) => {
|
|
await deactivateProfile(profileId)
|
|
}
|
|
|
|
onMounted(() => {
|
|
fetchAll()
|
|
})
|
|
</script>
|