- 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.
217 lines
5.7 KiB
Vue
217 lines
5.7 KiB
Vue
<template>
|
|
<div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-6">
|
|
<rs-card v-for="stat in stats" :key="stat.id">
|
|
<template #body>
|
|
<div class="flex items-center">
|
|
<div class="flex-1">
|
|
<p class="text-sm font-medium text-gray-600 dark:text-gray-400">{{ stat.label }}</p>
|
|
<p class="text-2xl font-semibold text-gray-900 dark:text-white">
|
|
{{ formatValue(stat.value, stat.type) }}
|
|
</p>
|
|
<div v-if="stat.trend" class="flex items-center mt-1">
|
|
<Icon
|
|
:name="stat.trend.direction === 'up' ? 'ph:trend-up' : 'ph:trend-down'"
|
|
class="w-4 h-4 mr-1"
|
|
:class="stat.trend.direction === 'up' ? 'text-green-500' : 'text-red-500'"
|
|
/>
|
|
<span
|
|
class="text-xs font-medium"
|
|
:class="stat.trend.direction === 'up' ? 'text-green-600' : 'text-red-600'"
|
|
>
|
|
{{ stat.trend.value }}{{ stat.trend.type === 'percentage' ? '%' : '' }}
|
|
</span>
|
|
<span class="text-xs text-gray-500 dark:text-gray-400 ml-1">vs last month</span>
|
|
</div>
|
|
</div>
|
|
<div class="flex-shrink-0">
|
|
<div
|
|
class="p-3 rounded-lg transition-colors"
|
|
:class="stat.iconBg"
|
|
>
|
|
<Icon
|
|
:name="stat.icon"
|
|
class="w-6 h-6"
|
|
:class="stat.iconColor"
|
|
/>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Progress bar for certain stats -->
|
|
<div v-if="stat.progress" class="mt-3">
|
|
<div class="flex items-center justify-between text-xs text-gray-600 dark:text-gray-400 mb-1">
|
|
<span>{{ stat.progress.label }}</span>
|
|
<span>{{ stat.progress.current }} / {{ stat.progress.total }}</span>
|
|
</div>
|
|
<div class="w-full bg-gray-200 dark:bg-gray-700 rounded-full h-1.5">
|
|
<div
|
|
class="h-1.5 rounded-full transition-all duration-300"
|
|
:class="stat.progress.color"
|
|
:style="{ width: (stat.progress.current / stat.progress.total * 100) + '%' }"
|
|
></div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Quick action button -->
|
|
<div v-if="stat.action" class="mt-3">
|
|
<button
|
|
@click="$emit('stat-action', stat.action.type)"
|
|
class="w-full text-xs font-medium text-primary hover:text-primary/80 transition-colors"
|
|
>
|
|
{{ stat.action.label }}
|
|
</button>
|
|
</div>
|
|
</template>
|
|
</rs-card>
|
|
</div>
|
|
</template>
|
|
|
|
<script setup>
|
|
const props = defineProps({
|
|
totalGroups: {
|
|
type: Number,
|
|
default: 0
|
|
},
|
|
totalRoles: {
|
|
type: Number,
|
|
default: 0
|
|
},
|
|
totalUsers: {
|
|
type: Number,
|
|
default: 0
|
|
},
|
|
totalResources: {
|
|
type: Number,
|
|
default: 0
|
|
},
|
|
activePermissions: {
|
|
type: Number,
|
|
default: 0
|
|
},
|
|
totalPermissions: {
|
|
type: Number,
|
|
default: 0
|
|
},
|
|
lastSyncTime: {
|
|
type: String,
|
|
default: null
|
|
},
|
|
trends: {
|
|
type: Object,
|
|
default: () => ({})
|
|
}
|
|
})
|
|
|
|
const emit = defineEmits(['stat-action'])
|
|
|
|
const stats = computed(() => [
|
|
{
|
|
id: 'groups',
|
|
label: 'Total Groups',
|
|
value: props.totalGroups,
|
|
type: 'number',
|
|
icon: 'ph:users-three',
|
|
iconColor: 'text-blue-600 dark:text-blue-400',
|
|
iconBg: 'bg-blue-100 dark:bg-blue-900/30',
|
|
trend: props.trends.groups ? {
|
|
direction: props.trends.groups >= 0 ? 'up' : 'down',
|
|
value: Math.abs(props.trends.groups),
|
|
type: 'number'
|
|
} : null,
|
|
action: {
|
|
type: 'sync-groups',
|
|
label: 'Sync from Authentik'
|
|
}
|
|
},
|
|
{
|
|
id: 'roles',
|
|
label: 'Total Roles',
|
|
value: props.totalRoles,
|
|
type: 'number',
|
|
icon: 'ph:shield-check',
|
|
iconColor: 'text-green-600 dark:text-green-400',
|
|
iconBg: 'bg-green-100 dark:bg-green-900/30',
|
|
trend: props.trends.roles ? {
|
|
direction: props.trends.roles >= 0 ? 'up' : 'down',
|
|
value: Math.abs(props.trends.roles),
|
|
type: 'number'
|
|
} : null,
|
|
action: {
|
|
type: 'manage-roles',
|
|
label: 'Manage Roles'
|
|
}
|
|
},
|
|
{
|
|
id: 'users',
|
|
label: 'Total Users',
|
|
value: props.totalUsers,
|
|
type: 'number',
|
|
icon: 'ph:user',
|
|
iconColor: 'text-purple-600 dark:text-purple-400',
|
|
iconBg: 'bg-purple-100 dark:bg-purple-900/30',
|
|
trend: props.trends.users ? {
|
|
direction: props.trends.users >= 0 ? 'up' : 'down',
|
|
value: Math.abs(props.trends.users),
|
|
type: 'percentage'
|
|
} : null,
|
|
progress: {
|
|
label: 'Active Users',
|
|
current: Math.floor(props.totalUsers * 0.85), // 85% active users
|
|
total: props.totalUsers,
|
|
color: 'bg-purple-500'
|
|
}
|
|
},
|
|
{
|
|
id: 'resources',
|
|
label: 'Resources',
|
|
value: props.totalResources,
|
|
type: 'number',
|
|
icon: 'ph:package',
|
|
iconColor: 'text-orange-600 dark:text-orange-400',
|
|
iconBg: 'bg-orange-100 dark:bg-orange-900/30',
|
|
progress: {
|
|
label: 'Configured',
|
|
current: props.activePermissions,
|
|
total: props.totalPermissions,
|
|
color: 'bg-orange-500'
|
|
},
|
|
action: {
|
|
type: 'manage-permissions',
|
|
label: 'Configure Permissions'
|
|
}
|
|
}
|
|
])
|
|
|
|
const formatValue = (value, type) => {
|
|
if (type === 'number') {
|
|
return value.toLocaleString()
|
|
}
|
|
if (type === 'percentage') {
|
|
return value + '%'
|
|
}
|
|
return value
|
|
}
|
|
</script>
|
|
|
|
<style scoped>
|
|
/* Add subtle hover effects */
|
|
.rs-card:hover {
|
|
@apply transform scale-[1.02] transition-transform duration-200;
|
|
}
|
|
|
|
.rs-card:hover .bg-blue-100 {
|
|
@apply bg-blue-200;
|
|
}
|
|
|
|
.rs-card:hover .bg-green-100 {
|
|
@apply bg-green-200;
|
|
}
|
|
|
|
.rs-card:hover .bg-purple-100 {
|
|
@apply bg-purple-200;
|
|
}
|
|
|
|
.rs-card:hover .bg-orange-100 {
|
|
@apply bg-orange-200;
|
|
}
|
|
</style> |