feat: complete Epic 1 — team management & permission system

- Story 1.1: Permission enum, config, AuthorizesPermissions & HasWorkspaceScope traits, member→worker migration
- Story 1.2: Team page with member list, invitation system with queued email
- Story 1.3: Role assignment (Manager/Worker) and member removal with activity logging
- Story 1.4: Owner-only permission toggle matrix for Managers (manage team, view logs, configure portal)
- Story 1.5: Role-based access enforcement — Workers see only assigned declarations/clients, sidebar scoping
- Story 1.6: Workspace switcher dropdown for multi-workspace users with session-based switching
- 83 new/modified files, 182 tests passing with zero regressions

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-03-18 00:12:50 +00:00
parent 5dffd2d063
commit c89d1879bf
83 changed files with 5850 additions and 314 deletions

View File

@@ -8,6 +8,7 @@ import {
HelpCircle,
LayoutGrid,
Users,
UsersRound,
} from 'lucide-vue-next';
import { computed } from 'vue';
import NavFooter from '@/components/NavFooter.vue';
@@ -23,11 +24,17 @@ import {
SidebarMenuItem,
} from '@/components/ui/sidebar';
import { dashboard } from '@/routes';
import { index as clientsIndex } from '@/routes/clients';
import { index as declarationsIndex } from '@/routes/declarations';
import { index as teamIndex } from '@/routes/team';
import type { NavItem } from '@/types';
import AppLogo from './AppLogo.vue';
import WorkspaceSwitcher from './WorkspaceSwitcher.vue';
const page = usePage();
const workspaceRole = computed(() => page.props.auth?.workspaceRole);
const isWorker = computed(() => workspaceRole.value === 'worker');
const mainNavItems = computed<NavItem[]>(() => {
const items: NavItem[] = [
{
@@ -37,18 +44,31 @@ const mainNavItems = computed<NavItem[]>(() => {
},
];
if (page.props.auth?.currentWorkspace) {
items.push(
{
title: 'Clients',
href: '/clients',
icon: Briefcase,
},
{
title: 'Déclarations',
href: '/declarations',
if (isWorker.value) {
items.push({
title: 'Mes déclarations',
href: declarationsIndex.url(),
icon: FileStack,
},
);
});
} else {
items.push(
{
title: 'Clients',
href: clientsIndex.url(),
icon: Briefcase,
},
{
title: 'Déclarations',
href: declarationsIndex.url(),
icon: FileStack,
},
{
title: 'Équipe',
href: teamIndex.url(),
icon: UsersRound,
},
);
}
}
return items;
});