feat: implement role-based access control (RBAC) management system with Keycloak integration and dynamic page permission configuration
This commit is contained in:
No files matched your search
+27
-21
@@ -129,6 +129,7 @@
|
||||
prepend-icon="mdi-shield-edit-outline"
|
||||
class="text-capitalize rounded-lg mr-2"
|
||||
@click="editPermissions(item)"
|
||||
v-permission="'canEdit'"
|
||||
>
|
||||
Atur Akses
|
||||
</v-btn>
|
||||
@@ -165,8 +166,8 @@
|
||||
<div class="pa-6 pt-0">
|
||||
<EditHakAkses
|
||||
v-if="editedEntity"
|
||||
:pages="editedEntity.pages"
|
||||
@update:pages="editedEntity.pages = $event"
|
||||
:hakAksesMenu="editedEntity.hakAksesMenu || []"
|
||||
@update:hakAksesMenu="editedEntity.hakAksesMenu = $event"
|
||||
/>
|
||||
</div>
|
||||
</v-card-text>
|
||||
@@ -240,7 +241,7 @@
|
||||
<script setup lang="ts">
|
||||
import { ref, computed, onMounted } from 'vue';
|
||||
import EditHakAkses from '@/components/HakAkses/EditHakAkses.vue';
|
||||
import { useNavItemsStore } from '~/stores/navItems1';
|
||||
import { useNavItemsStore, defaultNavItems } from '~/stores/navItems1';
|
||||
import type { HakAkses } from '~/types/setting';
|
||||
|
||||
definePageMeta({
|
||||
@@ -321,6 +322,8 @@ const loadData = async () => {
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
|
||||
// Computed list based on active tab
|
||||
const displayItems = computed(() => {
|
||||
let list = [];
|
||||
@@ -328,15 +331,15 @@ const displayItems = computed(() => {
|
||||
else if (activeTab.value === 'groups') list = keycloakEntities.value.groups;
|
||||
else list = keycloakEntities.value.users;
|
||||
|
||||
// Get all valid paths from current navigation store
|
||||
const allNavPaths = new Set<string>();
|
||||
const extractPaths = (items: any[]) => {
|
||||
// Get all valid menuKeys from current navigation store
|
||||
const allNavKeys = new Set<string>();
|
||||
const extractKeys = (items: any[]) => {
|
||||
items.forEach((item: any) => {
|
||||
if (item.path) allNavPaths.add(item.path);
|
||||
if (item.children) extractPaths(item.children);
|
||||
if (item.menuKey) allNavKeys.add(item.menuKey);
|
||||
if (item.children) extractKeys(item.children);
|
||||
});
|
||||
};
|
||||
extractPaths(navItemsStore.getNavItems);
|
||||
extractKeys(defaultNavItems);
|
||||
|
||||
return list.map((entity: any) => {
|
||||
// Find existing permission mapping
|
||||
@@ -344,18 +347,15 @@ const displayItems = computed(() => {
|
||||
const lookupKey = entity.type === 'user' ? entity.username : entity.name;
|
||||
const mapping = hakAksesList.value.find(h => h.namaHakAkses === lookupKey);
|
||||
|
||||
if (!mapping) return { ...entity, pages: [], validPagesCount: 0, status: 'tidak aktif', id_mapping: null };
|
||||
if (!mapping) return { ...entity, hakAksesMenu: [], validPagesCount: 0, status: 'tidak aktif', id_mapping: null };
|
||||
|
||||
// Count only valid pages that exist in navigation
|
||||
const validPages = (mapping.pages || []).filter(p => {
|
||||
const path = typeof p === 'string' ? p : (p as any)?.path;
|
||||
return path && allNavPaths.has(path);
|
||||
});
|
||||
// Count only valid pages that exist in navigation and have canAccess
|
||||
const validMenus = (mapping.hakAksesMenu || []).filter((m: any) => m.menuKey && allNavKeys.has(m.menuKey) && m.canAccess);
|
||||
|
||||
return {
|
||||
...entity,
|
||||
pages: mapping.pages, // Keep original for editing
|
||||
validPagesCount: validPages.length,
|
||||
hakAksesMenu: mapping.hakAksesMenu, // Keep original for editing
|
||||
validPagesCount: validMenus.length,
|
||||
status: mapping.status,
|
||||
id_mapping: mapping.id
|
||||
};
|
||||
@@ -401,10 +401,14 @@ const savePermissions = async () => {
|
||||
loading.value = true;
|
||||
try {
|
||||
const payload = {
|
||||
id: editedEntity.value.id_mapping, // If null, backend create new
|
||||
namaHakAkses: editedEntity.value.name,
|
||||
id: editedEntity.value.id_mapping || undefined, // Zod optional() expects undefined, not null
|
||||
namaHakAkses: editedEntity.value.type === 'user' ? editedEntity.value.username : editedEntity.value.name,
|
||||
role: editedEntity.value.type === 'role' ? editedEntity.value.name : undefined,
|
||||
group: editedEntity.value.type === 'group' ? editedEntity.value.name : undefined,
|
||||
namaTipeUser: editedEntity.value.type === 'user' ? editedEntity.value.username : undefined,
|
||||
isGroupBased: editedEntity.value.type === 'group',
|
||||
status: 'aktif',
|
||||
pages: editedEntity.value.pages
|
||||
hakAksesMenu: editedEntity.value.hakAksesMenu || []
|
||||
};
|
||||
|
||||
const response = await $fetch<{ success: boolean, message: string }>('/api/hak-akses', {
|
||||
@@ -417,10 +421,12 @@ const savePermissions = async () => {
|
||||
await loadData();
|
||||
await navItemsStore.refreshNavItems();
|
||||
showEditDialog.value = false;
|
||||
} else {
|
||||
showSnackbar(response.message || 'Gagal menyimpan perubahan', 'error');
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Error saving:', error);
|
||||
showSnackbar('Gagal menyimpan perubahan', 'error');
|
||||
showSnackbar('Terjadi kesalahan pada server saat menyimpan perubahan', 'error');
|
||||
} finally {
|
||||
loading.value = false;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user