WIp - manage role permissions per object
This commit is contained in:
@@ -15,60 +15,135 @@
|
||||
No roles available. Create roles first to manage field-level permissions.
|
||||
</div>
|
||||
|
||||
<div v-else class="space-y-4">
|
||||
<div class="rounded-md border">
|
||||
<table class="w-full">
|
||||
<thead>
|
||||
<tr class="border-b bg-muted/50">
|
||||
<th class="p-3 text-left font-medium">Field</th>
|
||||
<th
|
||||
v-for="role in roles"
|
||||
:key="role.id"
|
||||
class="p-3 text-center font-medium border-l"
|
||||
:colspan="2"
|
||||
>
|
||||
{{ role.name }}
|
||||
</th>
|
||||
</tr>
|
||||
<tr class="border-b bg-muted/30">
|
||||
<th class="p-2 text-left text-xs font-medium text-muted-foreground"></th>
|
||||
<template v-for="role in roles" :key="`${role.id}-headers`">
|
||||
<th class="p-2 text-center text-xs font-medium text-muted-foreground border-l">Read</th>
|
||||
<th class="p-2 text-center text-xs font-medium text-muted-foreground">Edit</th>
|
||||
</template>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr
|
||||
v-for="field in fields"
|
||||
:key="field.id"
|
||||
class="border-b hover:bg-muted/30"
|
||||
>
|
||||
<td class="p-3">
|
||||
<div>
|
||||
<div class="font-medium">{{ field.label }}</div>
|
||||
<div class="text-xs text-muted-foreground">{{ field.apiName }}</div>
|
||||
</div>
|
||||
</td>
|
||||
<template v-for="role in roles" :key="`${field.id}-${role.id}`">
|
||||
<td class="p-3 text-center border-l">
|
||||
<div v-else class="space-y-6">
|
||||
<!-- Role Selector -->
|
||||
<div class="space-y-2">
|
||||
<Label>Select Role</Label>
|
||||
<Select v-model="selectedRoleId" @update:model-value="(value) => selectedRoleId = value">
|
||||
<SelectTrigger class="w-full">
|
||||
<SelectValue placeholder="Choose a role to configure permissions" />
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
<SelectItem v-for="role in roles" :key="role.id" :value="role.id">
|
||||
{{ role.name }}
|
||||
</SelectItem>
|
||||
</SelectContent>
|
||||
</Select>
|
||||
</div>
|
||||
|
||||
<!-- Object-Level Permissions -->
|
||||
<div v-if="selectedRoleId" class="space-y-2">
|
||||
<h3 class="text-sm font-medium">Object-Level Permissions</h3>
|
||||
<div class="rounded-md border">
|
||||
<table class="w-full">
|
||||
<thead>
|
||||
<tr class="border-b bg-muted/50">
|
||||
<th class="p-3 text-left font-medium">Permission</th>
|
||||
<th class="p-3 text-center font-medium">Enabled</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr class="border-b hover:bg-muted/30">
|
||||
<td class="p-3">Create</td>
|
||||
<td class="p-3 text-center">
|
||||
<Checkbox
|
||||
:model-value="hasPermission(field.id, role.id, 'read')"
|
||||
@update:model-value="(checked: boolean) => updatePermission(field.id, role.id, 'read', checked)"
|
||||
:model-value="objectPermissions.canCreate"
|
||||
@update:model-value="(checked: boolean) => updateObjectPermission('canCreate', checked)"
|
||||
/>
|
||||
</td>
|
||||
</tr>
|
||||
<tr class="border-b hover:bg-muted/30">
|
||||
<td class="p-3">Read</td>
|
||||
<td class="p-3 text-center">
|
||||
<Checkbox
|
||||
:model-value="objectPermissions.canRead"
|
||||
@update:model-value="(checked: boolean) => updateObjectPermission('canRead', checked)"
|
||||
/>
|
||||
</td>
|
||||
</tr>
|
||||
<tr class="border-b hover:bg-muted/30">
|
||||
<td class="p-3">Edit</td>
|
||||
<td class="p-3 text-center">
|
||||
<Checkbox
|
||||
:model-value="objectPermissions.canEdit"
|
||||
@update:model-value="(checked: boolean) => updateObjectPermission('canEdit', checked)"
|
||||
/>
|
||||
</td>
|
||||
</tr>
|
||||
<tr class="border-b hover:bg-muted/30">
|
||||
<td class="p-3">Delete</td>
|
||||
<td class="p-3 text-center">
|
||||
<Checkbox
|
||||
:model-value="objectPermissions.canDelete"
|
||||
@update:model-value="(checked: boolean) => updateObjectPermission('canDelete', checked)"
|
||||
/>
|
||||
</td>
|
||||
</tr>
|
||||
<tr class="border-b hover:bg-muted/30">
|
||||
<td class="p-3">View All</td>
|
||||
<td class="p-3 text-center">
|
||||
<Checkbox
|
||||
:model-value="objectPermissions.canViewAll"
|
||||
@update:model-value="(checked: boolean) => updateObjectPermission('canViewAll', checked)"
|
||||
/>
|
||||
</td>
|
||||
</tr>
|
||||
<tr class="hover:bg-muted/30">
|
||||
<td class="p-3">Modify All</td>
|
||||
<td class="p-3 text-center">
|
||||
<Checkbox
|
||||
:model-value="objectPermissions.canModifyAll"
|
||||
@update:model-value="(checked: boolean) => updateObjectPermission('canModifyAll', checked)"
|
||||
/>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Field-Level Permissions -->
|
||||
<div v-if="selectedRoleId" class="space-y-2">
|
||||
<h3 class="text-sm font-medium">Field-Level Permissions</h3>
|
||||
<div class="rounded-md border">
|
||||
<table class="w-full">
|
||||
<thead>
|
||||
<tr class="border-b bg-muted/50">
|
||||
<th class="p-3 text-left font-medium">Field</th>
|
||||
<th class="p-3 text-center font-medium">Read</th>
|
||||
<th class="p-3 text-center font-medium">Edit</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr
|
||||
v-for="field in fields"
|
||||
:key="field.id"
|
||||
class="border-b hover:bg-muted/30"
|
||||
>
|
||||
<td class="p-3">
|
||||
<div>
|
||||
<div class="font-medium">{{ field.label }}</div>
|
||||
<div class="text-xs text-muted-foreground">{{ field.apiName }}</div>
|
||||
</div>
|
||||
</td>
|
||||
<td class="p-3 text-center">
|
||||
<Checkbox
|
||||
:model-value="hasPermission(field.id, selectedRoleId, 'read')"
|
||||
@update:model-value="(checked: boolean) => updatePermission(field.id, selectedRoleId, 'read', checked)"
|
||||
:disabled="field.isSystem"
|
||||
/>
|
||||
</td>
|
||||
<td class="p-3 text-center">
|
||||
<Checkbox
|
||||
:model-value="hasPermission(field.id, role.id, 'edit')"
|
||||
@update:model-value="(checked: boolean) => updatePermission(field.id, role.id, 'edit', checked)"
|
||||
:disabled="field.isSystem || !hasPermission(field.id, role.id, 'read')"
|
||||
:model-value="hasPermission(field.id, selectedRoleId, 'edit')"
|
||||
@update:model-value="(checked: boolean) => updatePermission(field.id, selectedRoleId, 'edit', checked)"
|
||||
:disabled="field.isSystem || !hasPermission(field.id, selectedRoleId, 'read')"
|
||||
/>
|
||||
</td>
|
||||
</template>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="flex items-center gap-2 text-sm text-muted-foreground">
|
||||
@@ -86,13 +161,16 @@
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref, onMounted, computed } from 'vue';
|
||||
import { ref, onMounted, computed, watch } from 'vue';
|
||||
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '~/components/ui/card';
|
||||
import { Checkbox } from '~/components/ui/checkbox';
|
||||
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from '~/components/ui/select';
|
||||
import { Label } from '~/components/ui/label';
|
||||
import { Info } from 'lucide-vue-next';
|
||||
|
||||
const props = defineProps<{
|
||||
objectId: string;
|
||||
objectApiName: string;
|
||||
fields: any[];
|
||||
}>();
|
||||
|
||||
@@ -102,7 +180,16 @@ const { toast } = useToast();
|
||||
const loading = ref(true);
|
||||
const saving = ref(false);
|
||||
const roles = ref<any[]>([]);
|
||||
const selectedRoleId = ref<string>('');
|
||||
const permissions = ref<Map<string, Map<string, { canRead: boolean; canEdit: boolean }>>>(new Map());
|
||||
const objectPermissions = ref({
|
||||
canCreate: false,
|
||||
canRead: false,
|
||||
canEdit: false,
|
||||
canDelete: false,
|
||||
canViewAll: false,
|
||||
canModifyAll: false,
|
||||
});
|
||||
|
||||
// Load roles and permissions
|
||||
onMounted(async () => {
|
||||
@@ -149,8 +236,6 @@ const hasPermission = (fieldId: string, roleId: string, type: 'read' | 'edit'):
|
||||
};
|
||||
|
||||
const updatePermission = async (fieldId: string, roleId: string, type: 'read' | 'edit', checked: boolean) => {
|
||||
console.log('updatePermission called:', { fieldId, roleId, type, checked });
|
||||
|
||||
try {
|
||||
saving.value = true;
|
||||
|
||||
@@ -180,22 +265,14 @@ const updatePermission = async (fieldId: string, roleId: string, type: 'read' |
|
||||
}
|
||||
}
|
||||
|
||||
console.log('Saving permission:', {
|
||||
roleId,
|
||||
fieldDefinitionId: fieldId,
|
||||
canRead: perm.canRead,
|
||||
canEdit: perm.canEdit,
|
||||
});
|
||||
|
||||
// Save to backend
|
||||
const result = await api.put(`/setup/objects/${props.objectId}/field-permissions`, {
|
||||
await api.put(`/setup/objects/${props.objectId}/field-permissions`, {
|
||||
roleId,
|
||||
fieldDefinitionId: fieldId,
|
||||
canRead: perm.canRead,
|
||||
canEdit: perm.canEdit,
|
||||
});
|
||||
|
||||
console.log('Save result:', result);
|
||||
toast.success('Permission updated');
|
||||
|
||||
} catch (error: any) {
|
||||
@@ -216,4 +293,52 @@ const updatePermission = async (fieldId: string, roleId: string, type: 'read' |
|
||||
saving.value = false;
|
||||
}
|
||||
};
|
||||
|
||||
const updateObjectPermission = async (permission: string, checked: boolean) => {
|
||||
if (!selectedRoleId.value) return;
|
||||
|
||||
try {
|
||||
saving.value = true;
|
||||
|
||||
// Update local state
|
||||
(objectPermissions.value as any)[permission] = checked;
|
||||
|
||||
// Save to backend
|
||||
await api.put(`/setup/objects/${props.objectApiName}/permissions`, {
|
||||
roleId: selectedRoleId.value,
|
||||
...objectPermissions.value,
|
||||
});
|
||||
|
||||
toast.success('Object permission updated');
|
||||
} catch (error: any) {
|
||||
console.error('Failed to update object permission:', error);
|
||||
toast.error(error.message || 'Failed to update permission');
|
||||
|
||||
// Revert change
|
||||
(objectPermissions.value as any)[permission] = !checked;
|
||||
} finally {
|
||||
saving.value = false;
|
||||
}
|
||||
};
|
||||
|
||||
// Load object permissions when role changes
|
||||
watch(selectedRoleId, async (roleId) => {
|
||||
if (!roleId) return;
|
||||
|
||||
try {
|
||||
const response = await api.get(`/setup/objects/${props.objectApiName}/permissions/${roleId}`);
|
||||
if (response) {
|
||||
objectPermissions.value = {
|
||||
canCreate: Boolean(response.canCreate),
|
||||
canRead: Boolean(response.canRead),
|
||||
canEdit: Boolean(response.canEdit),
|
||||
canDelete: Boolean(response.canDelete),
|
||||
canViewAll: Boolean(response.canViewAll),
|
||||
canModifyAll: Boolean(response.canModifyAll),
|
||||
};
|
||||
}
|
||||
} catch (error: any) {
|
||||
console.error('Failed to load object permissions:', error);
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
Reference in New Issue
Block a user