Add record access strategy

This commit is contained in:
Francisco Gaona
2026-01-05 07:48:22 +01:00
parent 838a010fb2
commit 16907aadf8
97 changed files with 11350 additions and 208 deletions

View File

@@ -17,7 +17,7 @@ import {
SidebarRail,
} from '@/components/ui/sidebar'
import { Collapsible, CollapsibleContent, CollapsibleTrigger } from '@/components/ui/collapsible'
import { LayoutGrid, Boxes, Settings, Home, ChevronRight, Database, Layers, LogOut } from 'lucide-vue-next'
import { LayoutGrid, Boxes, Settings, Home, ChevronRight, Database, Layers, LogOut, Users, Globe, Building } from 'lucide-vue-next'
const { logout } = useAuth()
const { api } = useApi()
@@ -26,12 +26,31 @@ const handleLogout = async () => {
await logout()
}
// Check if user is central admin (by checking if we're on a central subdomain)
// Use ref instead of computed to avoid hydration mismatch
const isCentralAdmin = ref(false)
// Fetch objects and group by app
const apps = ref<any[]>([])
const topLevelObjects = ref<any[]>([])
const loading = ref(true)
onMounted(async () => {
// Set isCentralAdmin first
if (process.client) {
const hostname = window.location.hostname
const parts = hostname.split('.')
const subdomain = parts.length >= 2 ? parts[0] : null
const centralSubdomains = ['central', 'admin']
isCentralAdmin.value = subdomain ? centralSubdomains.includes(subdomain) : false
}
// Don't fetch tenant objects if we're on a central subdomain
if (isCentralAdmin.value) {
loading.value = false
return
}
try {
const response = await api.get('/setup/objects')
const allObjects = response.data || response || []
@@ -86,6 +105,49 @@ const staticMenuItems = [
url: '/setup/objects',
icon: Boxes,
},
{
title: 'Users',
url: '/setup/users',
icon: Users,
},
{
title: 'Roles',
url: '/setup/roles',
icon: Layers,
},
],
},
]
const centralAdminMenuItems: Array<{
title: string
icon: any
url?: string
items?: Array<{
title: string
url: string
icon: any
}>
}> = [
{
title: 'Central Admin',
icon: Settings,
items: [
{
title: 'Tenants',
url: '/central/tenants',
icon: Building,
},
{
title: 'Domains',
url: '/central/domains',
icon: Globe,
},
{
title: 'Admin Users',
url: '/central/users',
icon: Users,
},
],
},
]
@@ -160,6 +222,53 @@ const staticMenuItems = [
</SidebarGroupContent>
</SidebarGroup>
<!-- Central Admin Menu Items (only visible to central admins) -->
<SidebarGroup v-if="isCentralAdmin">
<SidebarGroupLabel>Central Administration</SidebarGroupLabel>
<SidebarGroupContent>
<SidebarMenu>
<template v-for="item in centralAdminMenuItems" :key="item.title">
<!-- Simple menu item -->
<SidebarMenuItem v-if="!item.items">
<SidebarMenuButton as-child>
<NuxtLink :to="item.url">
<component :is="item.icon" />
<span>{{ item.title }}</span>
</NuxtLink>
</SidebarMenuButton>
</SidebarMenuItem>
<!-- Collapsible menu item with submenu -->
<Collapsible v-else-if="item.items" as-child :default-open="true" class="group/collapsible">
<SidebarMenuItem>
<CollapsibleTrigger as-child>
<SidebarMenuButton :tooltip="item.title">
<component :is="item.icon" />
<span>{{ item.title }}</span>
<ChevronRight
class="ml-auto transition-transform duration-200 group-data-[state=open]/collapsible:rotate-90"
/>
</SidebarMenuButton>
</CollapsibleTrigger>
<CollapsibleContent>
<SidebarMenuSub>
<SidebarMenuSubItem v-for="subItem in item.items" :key="subItem.title">
<SidebarMenuSubButton as-child>
<NuxtLink :to="subItem.url">
<component v-if="subItem.icon" :is="subItem.icon" />
<span>{{ subItem.title }}</span>
</NuxtLink>
</SidebarMenuSubButton>
</SidebarMenuSubItem>
</SidebarMenuSub>
</CollapsibleContent>
</SidebarMenuItem>
</Collapsible>
</template>
</SidebarMenu>
</SidebarGroupContent>
</SidebarGroup>
<!-- Top-level Objects (no app) -->
<SidebarGroup v-if="!loading && topLevelObjects.length > 0">
<SidebarGroupLabel>Objects</SidebarGroupLabel>