Afiq f05dd42c16 Enhance README and implement RBAC system with Authentik integration
- Updated README.md to reflect the new project name and provide an overview of the Role-Based Access Control (RBAC) system.
- Added new components for RBAC management, including:
  - PermissionExample.vue: Demonstrates permission-based navigation.
  - GroupCard.vue: Displays group information and assigned roles.
  - PermissionMatrix.vue: Visual representation of permissions across roles and resources.
  - RoleTemplates.vue: Quick role templates for applying pre-configured permissions.
  - StatsCards.vue: Displays statistics related to users, groups, and roles.
- Introduced useRbacPermissions.js for managing permission checks.
- Created docker-compose.yml for PostgreSQL and Redis services.
- Developed comprehensive documentation for application management and Authentik integration.
- Added multiple pages for managing applications, groups, roles, and users, including bulk operations and templates.
- Updated navigation structure to include new RBAC management paths.
2025-05-31 15:58:41 +08:00

162 lines
5.1 KiB
Vue

<template>
<rs-card>
<template #header>
<div class="flex items-center justify-between">
<div>
<h3 class="text-lg font-medium text-gray-900 dark:text-white">{{ group.name }}</h3>
<p class="text-sm text-gray-600 dark:text-gray-400">{{ group.description }}</p>
</div>
<div class="flex items-center space-x-2">
<rs-badge variant="info">{{ group.userCount }} users</rs-badge>
<rs-badge
:variant="group.authentikSynced ? 'success' : 'warning'"
class="text-xs"
>
{{ group.authentikSynced ? 'Synced' : 'Manual' }}
</rs-badge>
</div>
</div>
</template>
<template #body>
<div class="space-y-4">
<!-- Role Assignment Section -->
<div>
<div class="flex items-center justify-between mb-3">
<h4 class="text-sm font-medium text-gray-900 dark:text-white">Assigned Roles</h4>
<span class="text-xs text-gray-500 dark:text-gray-400">
{{ assignedRoleCount }} of {{ availableRoles.length }} roles
</span>
</div>
<div class="space-y-2">
<label v-for="role in availableRoles" :key="role.id" class="flex items-center group">
<input
type="checkbox"
:checked="isRoleAssigned(role.id)"
@change="handleRoleToggle(role.id, $event)"
class="h-4 w-4 text-primary focus:ring-primary border-gray-300 rounded transition-colors"
/>
<div class="ml-3 flex-1 flex items-center justify-between">
<div>
<span class="text-sm text-gray-900 dark:text-white">{{ role.name }}</span>
<p class="text-xs text-gray-500 dark:text-gray-400">{{ role.description }}</p>
</div>
<div class="flex items-center space-x-2">
<rs-badge variant="secondary" class="text-xs">{{ role.userCount }}</rs-badge>
<button
v-if="isRoleAssigned(role.id)"
@click="$emit('view-role-details', role.id)"
class="opacity-0 group-hover:opacity-100 text-primary hover:text-primary/80 text-xs transition-opacity"
>
View Details
</button>
</div>
</div>
</label>
</div>
</div>
<!-- Quick Actions -->
<div class="pt-4 border-t border-gray-200 dark:border-gray-700">
<div class="flex items-center justify-between">
<div class="flex space-x-2">
<rs-button
@click="$emit('apply-template', 'admin')"
variant="primary-outline"
size="sm"
class="text-xs"
>
Admin Template
</rs-button>
<rs-button
@click="$emit('apply-template', 'viewer')"
variant="primary-outline"
size="sm"
class="text-xs"
>
Viewer Template
</rs-button>
</div>
<rs-button
@click="$emit('manage-users')"
variant="primary-outline"
size="sm"
class="text-xs"
>
<Icon name="ph:users" class="w-3 h-3 mr-1" />
Manage Users
</rs-button>
</div>
</div>
<!-- Group Metadata -->
<div class="pt-3 border-t border-gray-200 dark:border-gray-700">
<div class="grid grid-cols-2 gap-4 text-xs text-gray-500 dark:text-gray-400">
<div>
<span class="font-medium">Authentik UUID:</span>
<p class="font-mono mt-1 break-all">{{ group.authentikUUID }}</p>
</div>
<div>
<span class="font-medium">Last Sync:</span>
<p class="mt-1">{{ formatDate(group.lastSync) }}</p>
</div>
</div>
</div>
</div>
</template>
</rs-card>
</template>
<script setup>
import { computed } from 'vue'
const props = defineProps({
group: {
type: Object,
required: true
},
availableRoles: {
type: Array,
required: true
},
assignedRoles: {
type: Array,
default: () => []
}
})
const emit = defineEmits([
'role-changed',
'apply-template',
'manage-users',
'view-role-details'
])
const assignedRoleCount = computed(() => props.assignedRoles.length)
const isRoleAssigned = (roleId) => {
return props.assignedRoles.includes(roleId)
}
const handleRoleToggle = (roleId, event) => {
const assigned = event.target.checked
emit('role-changed', { groupId: props.group.id, roleId, assigned })
}
const formatDate = (dateString) => {
if (!dateString) return 'Never'
return new Date(dateString).toLocaleDateString('en-US', {
month: 'short',
day: 'numeric',
hour: '2-digit',
minute: '2-digit'
})
}
</script>
<style scoped>
.group:hover .opacity-0 {
@apply opacity-100;
}
</style>