As we continued development in Sprint 1 of Lura, today I focused on creating one of the most critical components of any multi-tenant application: the navigation system — specifically, a dynamic sidebar that changes based on the active workspace and user role.
🔍 Why It Matters
In a multi-tenant platform like Lura, where each workspace represents a legal team or organization, the UI must help users:
- Quickly switch between workspaces
- Navigate through cases and documents within a workspace
- Only see what they're allowed to access (based on their role)
This means the sidebar isn't just visual — it's deeply tied to the data model, auth state, and role-based access control (RBAC).
🧠 Key Features I Built Today:
- Dynamic Sidebar Structure Each time a user logs in, they’re routed to their default workspace dashboard. The sidebar fetches:
- The list of workspaces they belong to
- The cases within the selected workspace
- Routes like dashboard, calendar, documents, and chatbot (if allowed)
This was achieved using:
- useEffect to fetch data after auth loads
- useRouter from Next.js to sync the URL
- Context providers to manage global workspace state
// Simplified Sidebar logic
const { currentWorkspace } = useWorkspaceContext();
const { data: cases } = useCases(currentWorkspace.id);
return (
<aside className="sidebar">
<Link href={`/workspace/${currentWorkspace.id}/dashboard`}>Dashboard</Link>
{cases.map(c => (
<Link key={c.id} href={`/workspace/${currentWorkspace.id}/case/${c.id}`}>
{c.title}
</Link>
))}
</aside>
);
- Workspace Switching Dropdown Users with access to multiple workspaces can switch between them from a dropdown. When a new workspace is selected:
- The workspaceContext is updated
- The sidebar re-renders with the new workspace's content
- The app re-routes to the workspace dashboard
I also made sure that the sidebar remains consistent across pages, using a layout wrapper component in _app.tsx.
- Role-Aware Links Admins can see links to manage users and delete cases/documents. Lawyers can't. This was done via a reusable permission checker
const can = (role: string, action: string) => {
const rules = {
lawyer: ['read', 'create'],
admin: ['read', 'create', 'delete'],
superadmin: ['*']
};
return rules[role]?.includes(action) || rules[role]?.includes('*');
};
✅ Key Takeaways
- Navigation systems are more than UI — they reflect access, identity, and structure.
- Using context and custom hooks helped reduce prop drilling and made switching workspaces seamless.
- By planning the sidebar early, we reduced the chance of layout bugs and inconsistencies.
❓Question:
How do you handle dynamic layouts in your multi-role or multi-tenant apps?
Top comments (0)