refactor(directory) : gate report actions via RBAC permissions + guard report deletion
- replace hardcoded ROLE_ADMIN check with usePermissions().can('directory.{clients,prospects}.manage')
- rename misleading isAdmin prop to canManage in CommercialReportTab and ReportDocumentList
- add busy guard on delete confirmation modal to prevent duplicate DELETE on double-click
This commit is contained in:
@@ -6,7 +6,7 @@
|
||||
<span v-if="reports.length">{{ $t('directory.reports.count', { n: reports.length }, reports.length) }}</span>
|
||||
</p>
|
||||
<MalioButton
|
||||
v-if="isAdmin"
|
||||
v-if="canManage"
|
||||
icon-name="mdi:plus"
|
||||
icon-position="left"
|
||||
button-class="w-auto px-4"
|
||||
@@ -24,7 +24,7 @@
|
||||
<p class="font-medium text-neutral-600">{{ $t('directory.reports.empty') }}</p>
|
||||
<p class="max-w-sm text-sm text-neutral-400">{{ $t('directory.reports.emptyHint') }}</p>
|
||||
<MalioButton
|
||||
v-if="isAdmin"
|
||||
v-if="canManage"
|
||||
variant="tertiary"
|
||||
icon-name="mdi:plus"
|
||||
icon-position="left"
|
||||
@@ -70,7 +70,7 @@
|
||||
<span v-if="report.author"> · {{ report.author.username }}</span>
|
||||
</p>
|
||||
</div>
|
||||
<div v-if="isAdmin" class="flex shrink-0 gap-1">
|
||||
<div v-if="canManage" class="flex shrink-0 gap-1">
|
||||
<MalioButtonIcon
|
||||
icon="mdi:pencil-outline"
|
||||
variant="ghost"
|
||||
@@ -97,7 +97,7 @@
|
||||
|
||||
<!-- Documents joints -->
|
||||
<div
|
||||
v-if="(report.documents?.length ?? 0) || isAdmin"
|
||||
v-if="(report.documents?.length ?? 0) || canManage"
|
||||
class="mt-3 border-t border-neutral-100 pt-3"
|
||||
>
|
||||
<p class="mb-2 text-xs font-medium uppercase tracking-wide text-neutral-400">
|
||||
@@ -107,11 +107,11 @@
|
||||
<ReportDocumentList
|
||||
v-if="report.documents?.length"
|
||||
:documents="report.documents"
|
||||
:is-admin="isAdmin"
|
||||
:can-manage="canManage"
|
||||
@delete="(docId) => removeDocument(docId)"
|
||||
/>
|
||||
<ReportDocumentUpload
|
||||
v-if="isAdmin"
|
||||
v-if="canManage"
|
||||
:report-id="report.id"
|
||||
@uploaded="reload"
|
||||
/>
|
||||
@@ -129,6 +129,7 @@
|
||||
/>
|
||||
<ConfirmDeleteReportModal
|
||||
v-model="confirmOpen"
|
||||
:busy="deleting"
|
||||
@confirm="confirmDelete"
|
||||
/>
|
||||
</div>
|
||||
@@ -141,7 +142,7 @@ import { useReportDocumentService } from '~/modules/directory/services/report-do
|
||||
|
||||
const props = defineProps<{
|
||||
owner: { client?: string, prospect?: string }
|
||||
isAdmin: boolean
|
||||
canManage: boolean
|
||||
}>()
|
||||
|
||||
const reportService = useCommercialReportService()
|
||||
@@ -155,6 +156,7 @@ const editing = ref<CommercialReport | null>(null)
|
||||
|
||||
const confirmOpen = ref(false)
|
||||
const pendingDelete = ref<CommercialReport | null>(null)
|
||||
const deleting = ref(false)
|
||||
|
||||
// Le plus récent en haut (l'API ne garantit pas l'ordre).
|
||||
const sortedReports = computed(() =>
|
||||
@@ -208,11 +210,16 @@ function askDelete(report: CommercialReport): void {
|
||||
}
|
||||
|
||||
async function confirmDelete(): Promise<void> {
|
||||
if (!pendingDelete.value) return
|
||||
await reportService.remove(pendingDelete.value.id)
|
||||
confirmOpen.value = false
|
||||
pendingDelete.value = null
|
||||
await reload()
|
||||
if (!pendingDelete.value || deleting.value) return
|
||||
deleting.value = true
|
||||
try {
|
||||
await reportService.remove(pendingDelete.value.id)
|
||||
confirmOpen.value = false
|
||||
pendingDelete.value = null
|
||||
await reload()
|
||||
} finally {
|
||||
deleting.value = false
|
||||
}
|
||||
}
|
||||
|
||||
async function removeDocument(id: number): Promise<void> {
|
||||
|
||||
Reference in New Issue
Block a user