Files
neo/frontend/pages/setup/roles.vue
Francisco Gaona 88f656c3f5 WIP - permissions
2025-12-28 05:43:03 +01:00

186 lines
5.6 KiB
Vue

<template>
<div class="min-h-screen bg-background">
<NuxtLayout name="default">
<main class="container mx-auto px-4 py-8">
<div class="mb-6 flex items-center justify-between">
<div>
<h1 class="text-3xl font-bold">Roles & Permissions</h1>
<p class="text-muted-foreground">Manage user roles and their permissions across objects</p>
</div>
<Button @click="showCreateDialog = true">
<Plus class="w-4 h-4 mr-2" />
New Role
</Button>
</div>
<div v-if="loading" class="text-center py-12">Loading roles...</div>
<div v-else class="space-y-4">
<Card
v-for="role in roles"
:key="role.id"
class="cursor-pointer hover:border-primary transition-colors"
@click="handleSelectRole(role)"
>
<CardHeader>
<div class="flex items-center justify-between">
<div>
<CardTitle>{{ role.name }}</CardTitle>
<CardDescription v-if="role.description">
{{ role.description }}
</CardDescription>
</div>
<Button
variant="ghost"
size="sm"
@click.stop="handleDeleteRole(role.id)"
>
<Trash2 class="w-4 h-4" />
</Button>
</div>
</CardHeader>
</Card>
<div v-if="roles.length === 0" class="text-center py-12 text-muted-foreground">
No roles yet. Create one to get started.
</div>
</div>
<!-- Create Role Dialog -->
<Dialog v-model:open="showCreateDialog">
<DialogContent>
<DialogHeader>
<DialogTitle>Create New Role</DialogTitle>
<DialogDescription>
Define a new role for your organization
</DialogDescription>
</DialogHeader>
<div class="space-y-4 py-4">
<div class="space-y-2">
<Label>Role Name</Label>
<Input v-model="newRole.name" placeholder="e.g., Account Manager" />
</div>
<div class="space-y-2">
<Label>Description</Label>
<Input v-model="newRole.description" placeholder="Optional description" />
</div>
</div>
<DialogFooter>
<Button variant="outline" @click="showCreateDialog = false">Cancel</Button>
<Button @click="handleCreateRole" :disabled="!newRole.name || creating">
{{ creating ? 'Creating...' : 'Create' }}
</Button>
</DialogFooter>
</DialogContent>
</Dialog>
<!-- Role Permissions Editor Dialog -->
<Dialog v-model:open="showPermissionsDialog">
<DialogContent class="max-w-4xl max-h-[80vh] overflow-y-auto">
<DialogHeader>
<DialogTitle>Manage Permissions: {{ selectedRole?.name }}</DialogTitle>
<DialogDescription>
Configure what this role can do with each object
</DialogDescription>
</DialogHeader>
<RolePermissionsEditor
v-if="selectedRole"
:role="selectedRole"
@saved="handlePermissionsSaved"
/>
</DialogContent>
</Dialog>
</main>
</NuxtLayout>
</div>
</template>
<script setup lang="ts">
import { Plus, Trash2 } from 'lucide-vue-next'
import { Button } from '@/components/ui/button'
import { Card, CardHeader, CardTitle, CardDescription } from '@/components/ui/card'
import {
Dialog,
DialogContent,
DialogDescription,
DialogFooter,
DialogHeader,
DialogTitle,
} from '@/components/ui/dialog'
import { Input } from '@/components/ui/input'
import { Label } from '@/components/ui/label'
import RolePermissionsEditor from '@/components/RolePermissionsEditor.vue'
const { api } = useApi()
const { toast } = useToast()
const roles = ref<any[]>([])
const loading = ref(true)
const creating = ref(false)
const showCreateDialog = ref(false)
const showPermissionsDialog = ref(false)
const selectedRole = ref<any>(null)
const newRole = ref({
name: '',
description: '',
})
const fetchRoles = async () => {
try {
loading.value = true
roles.value = await api.get('/roles')
} catch (e: any) {
console.error('Error fetching roles:', e)
toast.error('Failed to load roles')
} finally {
loading.value = false
}
}
const handleCreateRole = async () => {
try {
creating.value = true
const created = await api.post('/roles', newRole.value)
roles.value.push(created)
toast.success('Role created successfully')
showCreateDialog.value = false
newRole.value = { name: '', description: '' }
} catch (e: any) {
console.error('Error creating role:', e)
toast.error('Failed to create role')
} finally {
creating.value = false
}
}
const handleSelectRole = (role: any) => {
selectedRole.value = role
showPermissionsDialog.value = true
}
const handleDeleteRole = async (roleId: string) => {
if (!confirm('Are you sure you want to delete this role?')) return
try {
await api.delete(`/roles/${roleId}`)
roles.value = roles.value.filter(r => r.id !== roleId)
toast.success('Role deleted successfully')
} catch (e: any) {
console.error('Error deleting role:', e)
toast.error('Failed to delete role')
}
}
const handlePermissionsSaved = () => {
showPermissionsDialog.value = false
toast.success('Permissions saved successfully')
}
onMounted(() => {
fetchRoles()
})
</script>