Afiq a2a81bd3bb Refactor RBAC Management and Dashboard Integration
- Removed the RBAC Management section from the navigation structure.
- Updated the dashboard page title to "RBAC Management" and modified the breadcrumb to reflect the new path.
- Simplified the dashboard component by removing unused data and integrating new tab functionalities for managing applications, groups, roles, and permissions.
- Enhanced the create group and create role pages to include application assignment and simplified form states.
- Updated user creation page to include application assignment and filtered group/role options based on the selected application.
- Deleted the obsolete rbac-permission page to streamline the project structure.
2025-05-31 17:30:11 +08:00

317 lines
9.6 KiB
Vue

<script setup>
definePageMeta({
title: "Create Group",
middleware: ["auth"],
requiresAuth: true,
breadcrumb: [
{ name: "Dashboard", path: "/dashboard" },
{ name: "Groups", path: "/groups" },
{ name: "Create Group", path: "/groups/create", type: "current" }
]
});
import { ref, reactive, computed, onMounted } from 'vue'
// Form state - SIMPLIFIED
const groupForm = reactive({
name: '',
description: '',
parentGroup: '',
roles: [], // Groups contain roles, not permissions
application: '', // Groups belong to applications
isActive: true
})
// Available options
const availableParentGroups = ref([
{ id: '1', name: 'IT Department' },
{ id: '2', name: 'HR Department' },
{ id: '3', name: 'Finance Department' },
{ id: '4', name: 'Management' }
])
const availableRoles = ref([
{ id: '1', name: 'Administrator', description: 'Full system access' },
{ id: '2', name: 'Manager', description: 'Department management access' },
{ id: '3', name: 'Editor', description: 'Content editing access' },
{ id: '4', name: 'Viewer', description: 'Read-only access' }
])
const availableApplications = ref([
{ id: '1', name: 'Main Application', description: 'Primary business application' },
{ id: '2', name: 'HR System', description: 'Human Resources Management' },
{ id: '3', name: 'Finance System', description: 'Financial Management' }
])
// Validation state
const errors = ref({})
const isLoading = ref(false)
// Computed
const isFormValid = computed(() => {
return groupForm.name &&
groupForm.description &&
groupForm.application &&
groupForm.name.length >= 3
})
const parentGroupOptions = computed(() =>
availableParentGroups.value.map(group => ({
label: group.name,
value: group.id
}))
)
const applicationOptions = computed(() =>
availableApplications.value.map(app => ({
label: app.name,
value: app.id
}))
)
// Methods
const validateForm = () => {
errors.value = {}
if (!groupForm.name) {
errors.value.name = 'Group name is required'
} else if (groupForm.name.length < 3) {
errors.value.name = 'Group name must be at least 3 characters'
}
if (!groupForm.description) {
errors.value.description = 'Description is required'
}
if (!groupForm.application) {
errors.value.application = 'Application is required'
}
return Object.keys(errors.value).length === 0
}
const createGroup = async () => {
if (!validateForm()) {
return
}
isLoading.value = true
try {
// Prepare group data - SIMPLIFIED
const groupData = {
name: groupForm.name,
description: groupForm.description,
parentGroup: groupForm.parentGroup,
roles: groupForm.roles,
application: groupForm.application,
isActive: groupForm.isActive
}
// API call to create group
const response = await $fetch('/api/groups/create', {
method: 'POST',
body: groupData
})
if (response.success) {
await navigateTo('/groups')
}
} catch (error) {
console.error('Failed to create group:', error)
} finally {
isLoading.value = false
}
}
const resetForm = () => {
Object.assign(groupForm, {
name: '',
description: '',
parentGroup: '',
roles: [],
application: '',
isActive: true
})
errors.value = {}
}
// Initialize
onMounted(() => {
// Load available data
})
</script>
<template>
<div>
<LayoutsBreadcrumb />
<!-- Header -->
<div class="mb-6">
<div class="flex items-center justify-between">
<div>
<h1 class="text-2xl font-bold text-gray-900 dark:text-white">Create New Group</h1>
<p class="text-gray-600 dark:text-gray-400">Groups are collections of roles for organizing users</p>
</div>
<div class="flex space-x-3">
<rs-button @click="resetForm" variant="primary-outline">
<Icon name="ph:arrow-clockwise" class="w-4 h-4 mr-2" />
Reset Form
</rs-button>
<rs-button @click="createGroup" :disabled="!isFormValid || isLoading">
<Icon name="ph:users-three" class="w-4 h-4 mr-2" />
{{ isLoading ? 'Creating...' : 'Create Group' }}
</rs-button>
</div>
</div>
</div>
<FormKit type="form" @submit="createGroup">
<div class="grid grid-cols-1 lg:grid-cols-3 gap-6">
<!-- Main Form -->
<div class="lg:col-span-2 space-y-6">
<!-- Basic Information -->
<rs-card>
<template #header>
<h3 class="text-lg font-medium text-gray-900 dark:text-white">Basic Information</h3>
</template>
<template #body>
<div class="space-y-4">
<div class="grid grid-cols-1 md:grid-cols-2 gap-4">
<FormKit
v-model="groupForm.name"
type="text"
label="Group Name"
placeholder="e.g., Development Team"
validation="required|length:3"
validation-visibility="dirty"
:validation-messages="{
required: 'Group name is required',
length: 'Group name must be at least 3 characters'
}"
/>
<FormKit
v-model="groupForm.application"
type="select"
label="Application"
placeholder="Select application"
:options="applicationOptions"
validation="required"
validation-visibility="dirty"
help="Which application this group belongs to"
:validation-messages="{
required: 'Application is required'
}"
/>
</div>
<FormKit
v-model="groupForm.description"
type="textarea"
label="Description"
placeholder="Describe the purpose of this group"
validation="required"
validation-visibility="dirty"
rows="3"
:validation-messages="{
required: 'Description is required'
}"
/>
<FormKit
v-model="groupForm.parentGroup"
type="select"
label="Parent Group (Optional)"
placeholder="Select parent group"
:options="parentGroupOptions"
help="Create this as a sub-group under an existing group"
/>
<FormKit
v-model="groupForm.isActive"
type="checkbox"
label="Active Group"
help="Group can be assigned to users when active"
/>
</div>
</template>
</rs-card>
</div>
<!-- Sidebar -->
<div class="space-y-6">
<!-- Roles Assignment -->
<rs-card>
<template #header>
<h3 class="text-lg font-medium text-gray-900 dark:text-white">Roles in Group</h3>
</template>
<template #body>
<div class="space-y-3">
<p class="text-sm text-gray-600 dark:text-gray-400 mb-4">
Select roles that belong to this group. Users in this group will inherit these roles.
</p>
<FormKit
v-for="role in availableRoles"
:key="role.id"
v-model="groupForm.roles"
type="checkbox"
:value="role.id"
:label="role.name"
:help="role.description"
:classes="{
wrapper: 'mb-2',
label: '!text-sm',
help: '!text-xs'
}"
/>
</div>
</template>
</rs-card>
<!-- Preview -->
<rs-card>
<template #header>
<h3 class="text-lg font-medium text-gray-900 dark:text-white">Preview</h3>
</template>
<template #body>
<div class="space-y-3">
<div>
<span class="text-sm font-medium text-gray-700 dark:text-gray-300">Name:</span>
<p class="text-sm text-gray-900 dark:text-white">{{ groupForm.name || 'Not set' }}</p>
</div>
<div>
<span class="text-sm font-medium text-gray-700 dark:text-gray-300">Application:</span>
<p class="text-sm text-gray-900 dark:text-white">
{{ availableApplications.find(a => a.id === groupForm.application)?.name || 'Not selected' }}
</p>
</div>
<div>
<span class="text-sm font-medium text-gray-700 dark:text-gray-300">Roles:</span>
<p class="text-sm text-gray-900 dark:text-white">
{{ groupForm.roles.length }} role(s) selected
</p>
</div>
<div class="flex space-x-2">
<rs-badge :variant="groupForm.isActive ? 'success' : 'secondary'">
{{ groupForm.isActive ? 'Active' : 'Inactive' }}
</rs-badge>
</div>
</div>
</template>
</rs-card>
</div>
</div>
</FormKit>
</div>
</template>
<style scoped>
/* Custom styles */
</style>