feat(permissions) : add role-based UI guards and readonly mode for viewers

- 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>
This commit is contained in:
Matthieu
2026-02-26 13:36:42 +01:00
parent 6bed715b7f
commit cc70fe2b29
46 changed files with 946 additions and 423 deletions

View File

@@ -26,6 +26,7 @@
:initial-data="initialData"
:lock-category="true"
:saving="saving"
:readonly="!canEdit"
:disable-submit="isSubmitBlocked"
:disable-submit-message="submitBlockMessage"
:restricted-mode="isRestrictedMode"
@@ -47,6 +48,7 @@ import { useCategoryEditGuard } from '~/composables/useCategoryEditGuard'
import { useComponentTypes } from '~/composables/useComponentTypes'
import { useToast } from '~/composables/useToast'
const { canEdit } = usePermissions()
const route = useRoute()
const router = useRouter()
const { showError, showSuccess } = useToast()
@@ -128,6 +130,7 @@ const handleCancel = () => {
}
const handleSubmit = async (payload: Parameters<typeof updateModelType>[1]) => {
if (!canEdit.value) return
if (guardSubmitOrNotify()) {
return
}

View File

@@ -20,6 +20,7 @@
initial-category="COMPONENT"
:lock-category="true"
:saving="saving"
:readonly="!canEdit"
@submit="handleSubmit"
@cancel="handleCancel"
/>
@@ -35,6 +36,8 @@ import { createModelType } from '~/services/modelTypes'
import { invalidateEntityTypeCache } from '~/composables/useEntityTypes'
import { useToast } from '~/composables/useToast'
const { canEdit } = usePermissions()
useHead(() => ({
title: 'Nouvelle catégorie de composant',
}))
@@ -50,6 +53,7 @@ const handleCancel = () => {
}
const handleSubmit = async (payload: Parameters<typeof createModelType>[0]) => {
if (!canEdit.value) return
saving.value = true
try {
const enrichedPayload = {