Imporve the role based forntend protected roles
This commit is contained in:
@@ -1,5 +1,6 @@
|
||||
import { create } from 'zustand';
|
||||
import { persist, createJSONStorage } from 'zustand/middleware';
|
||||
import { GLOBAL_USER_ROLES, type GlobalUserRole } from '../types/roles';
|
||||
|
||||
export interface User {
|
||||
id: string;
|
||||
@@ -13,7 +14,7 @@ export interface User {
|
||||
language?: string;
|
||||
timezone?: string;
|
||||
tenant_id?: string;
|
||||
role?: string;
|
||||
role?: GlobalUserRole;
|
||||
}
|
||||
|
||||
export interface AuthState {
|
||||
@@ -191,15 +192,22 @@ export const useAuthStore = create<AuthState>()(
|
||||
set({ isLoading: loading });
|
||||
},
|
||||
|
||||
// Permission helpers - Simplified for backend compatibility
|
||||
// Permission helpers - Global user permissions only
|
||||
hasPermission: (_permission: string): boolean => {
|
||||
const { user } = get();
|
||||
if (!user || !user.is_active) return false;
|
||||
|
||||
// Admin has all permissions
|
||||
if (user.role === 'admin') return true;
|
||||
// Super admin and admin have all global permissions
|
||||
if (user.role === GLOBAL_USER_ROLES.SUPER_ADMIN || user.role === GLOBAL_USER_ROLES.ADMIN) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// Basic role-based permissions
|
||||
// Manager has limited permissions
|
||||
if (user.role === GLOBAL_USER_ROLES.MANAGER) {
|
||||
return ['user_management', 'system_settings'].includes(_permission);
|
||||
}
|
||||
|
||||
// Regular users have basic permissions
|
||||
return false;
|
||||
},
|
||||
|
||||
@@ -212,14 +220,15 @@ export const useAuthStore = create<AuthState>()(
|
||||
const { user } = get();
|
||||
if (!user || !user.is_active) return false;
|
||||
|
||||
// Role-based access control
|
||||
// Global role-based access control (system-wide)
|
||||
switch (user.role) {
|
||||
case 'admin':
|
||||
case GLOBAL_USER_ROLES.SUPER_ADMIN:
|
||||
case GLOBAL_USER_ROLES.ADMIN:
|
||||
return true;
|
||||
case 'manager':
|
||||
return ['inventory', 'production', 'sales', 'reports'].includes(resource);
|
||||
case 'user':
|
||||
return ['inventory', 'sales'].includes(resource) && action === 'read';
|
||||
case GLOBAL_USER_ROLES.MANAGER:
|
||||
return ['users', 'system'].includes(resource);
|
||||
case GLOBAL_USER_ROLES.USER:
|
||||
return action === 'read';
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -1,12 +1,14 @@
|
||||
import { create } from 'zustand';
|
||||
import { persist, createJSONStorage } from 'zustand/middleware';
|
||||
import { tenantService, type TenantResponse } from '../api';
|
||||
import { tenantService, type TenantResponse, type TenantAccessResponse } from '../api';
|
||||
import { useAuthUser } from './auth.store';
|
||||
import { TENANT_ROLES, GLOBAL_USER_ROLES } from '../types/roles';
|
||||
|
||||
export interface TenantState {
|
||||
// State
|
||||
currentTenant: TenantResponse | null;
|
||||
availableTenants: TenantResponse[] | null;
|
||||
currentTenantAccess: TenantAccessResponse | null;
|
||||
isLoading: boolean;
|
||||
error: string | null;
|
||||
|
||||
@@ -14,6 +16,7 @@ export interface TenantState {
|
||||
setCurrentTenant: (tenant: TenantResponse) => void;
|
||||
switchTenant: (tenantId: string) => Promise<boolean>;
|
||||
loadUserTenants: () => Promise<void>;
|
||||
loadCurrentTenantAccess: () => Promise<void>;
|
||||
clearTenants: () => void;
|
||||
clearError: () => void;
|
||||
setLoading: (loading: boolean) => void;
|
||||
@@ -29,14 +32,17 @@ export const useTenantStore = create<TenantState>()(
|
||||
// Initial state
|
||||
currentTenant: null,
|
||||
availableTenants: null,
|
||||
currentTenantAccess: null,
|
||||
isLoading: false,
|
||||
error: null,
|
||||
|
||||
// Actions
|
||||
setCurrentTenant: (tenant: TenantResponse) => {
|
||||
set({ currentTenant: tenant });
|
||||
set({ currentTenant: tenant, currentTenantAccess: null });
|
||||
// Update API client with new tenant ID
|
||||
tenantService.setCurrentTenant(tenant);
|
||||
// Load tenant access info
|
||||
get().loadCurrentTenantAccess();
|
||||
},
|
||||
|
||||
switchTenant: async (tenantId: string): Promise<boolean> => {
|
||||
@@ -116,10 +122,25 @@ export const useTenantStore = create<TenantState>()(
|
||||
}
|
||||
},
|
||||
|
||||
loadCurrentTenantAccess: async (): Promise<void> => {
|
||||
try {
|
||||
const { currentTenant } = get();
|
||||
if (!currentTenant) return;
|
||||
|
||||
const accessInfo = await tenantService.getCurrentUserTenantAccess(currentTenant.id);
|
||||
set({ currentTenantAccess: accessInfo });
|
||||
} catch (error) {
|
||||
// Don't set error state for access loading failures - just log
|
||||
console.warn('Failed to load tenant access:', error);
|
||||
set({ currentTenantAccess: null });
|
||||
}
|
||||
},
|
||||
|
||||
clearTenants: () => {
|
||||
set({
|
||||
currentTenant: null,
|
||||
availableTenants: null,
|
||||
currentTenantAccess: null,
|
||||
error: null,
|
||||
});
|
||||
tenantService.clearCurrentTenant();
|
||||
@@ -135,33 +156,34 @@ export const useTenantStore = create<TenantState>()(
|
||||
|
||||
// Permission helpers (migrated from BakeryContext)
|
||||
hasPermission: (permission: string): boolean => {
|
||||
const { currentTenant } = get();
|
||||
if (!currentTenant) return false;
|
||||
const { currentTenant, currentTenantAccess } = get();
|
||||
if (!currentTenant || !currentTenantAccess || !currentTenantAccess.has_access) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Get user to determine role within this tenant
|
||||
const authState = JSON.parse(localStorage.getItem('auth-storage') || '{}')?.state;
|
||||
const user = authState?.user;
|
||||
// Check if user has specific permission in their tenant permissions array
|
||||
if (currentTenantAccess.permissions?.includes(permission)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// Admin role has all permissions
|
||||
if (user?.role === 'admin') return true;
|
||||
// Check if user has broader permissions that include this one
|
||||
if (currentTenantAccess.permissions?.includes('*') ||
|
||||
currentTenantAccess.permissions?.includes('admin')) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// TODO: Implement proper tenant-based permissions
|
||||
// For now, use basic role-based permissions
|
||||
switch (user?.role) {
|
||||
case 'admin':
|
||||
// Role-based fallback for common permissions based on tenant role
|
||||
const tenantRole = currentTenantAccess.role;
|
||||
switch (tenantRole) {
|
||||
case TENANT_ROLES.OWNER:
|
||||
case TENANT_ROLES.ADMIN:
|
||||
return true;
|
||||
case 'manager':
|
||||
return ['inventory', 'production', 'sales', 'reports'].some(resource =>
|
||||
permission.startsWith(resource)
|
||||
);
|
||||
case 'baker':
|
||||
return ['production', 'inventory'].some(resource =>
|
||||
permission.startsWith(resource)
|
||||
) && !permission.includes(':delete');
|
||||
case 'staff':
|
||||
return ['inventory', 'sales'].some(resource =>
|
||||
permission.startsWith(resource)
|
||||
) && permission.includes(':read');
|
||||
case TENANT_ROLES.MEMBER:
|
||||
// Members can read and write but not delete or manage users
|
||||
return !permission.includes('delete') && !permission.includes('admin');
|
||||
case TENANT_ROLES.VIEWER:
|
||||
// Viewers can only read
|
||||
return permission.includes('read') || permission.includes('view');
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
@@ -185,6 +207,7 @@ export const useTenantStore = create<TenantState>()(
|
||||
partialize: (state) => ({
|
||||
currentTenant: state.currentTenant,
|
||||
availableTenants: state.availableTenants,
|
||||
currentTenantAccess: state.currentTenantAccess,
|
||||
}),
|
||||
onRehydrateStorage: () => (state) => {
|
||||
// Initialize API client with stored tenant when store rehydrates
|
||||
@@ -201,6 +224,7 @@ export const useTenantStore = create<TenantState>()(
|
||||
// Selectors for common use cases
|
||||
export const useCurrentTenant = () => useTenantStore((state) => state.currentTenant);
|
||||
export const useAvailableTenants = () => useTenantStore((state) => state.availableTenants);
|
||||
export const useCurrentTenantAccess = () => useTenantStore((state) => state.currentTenantAccess);
|
||||
export const useTenantLoading = () => useTenantStore((state) => state.isLoading);
|
||||
export const useTenantError = () => useTenantStore((state) => state.error);
|
||||
|
||||
@@ -209,6 +233,7 @@ export const useTenantActions = () => useTenantStore((state) => ({
|
||||
setCurrentTenant: state.setCurrentTenant,
|
||||
switchTenant: state.switchTenant,
|
||||
loadUserTenants: state.loadUserTenants,
|
||||
loadCurrentTenantAccess: state.loadCurrentTenantAccess,
|
||||
clearTenants: state.clearTenants,
|
||||
clearError: state.clearError,
|
||||
setLoading: state.setLoading,
|
||||
@@ -224,6 +249,7 @@ export const useTenantPermissions = () => useTenantStore((state) => ({
|
||||
export const useTenant = () => {
|
||||
const currentTenant = useCurrentTenant();
|
||||
const availableTenants = useAvailableTenants();
|
||||
const currentTenantAccess = useCurrentTenantAccess();
|
||||
const isLoading = useTenantLoading();
|
||||
const error = useTenantError();
|
||||
const actions = useTenantActions();
|
||||
@@ -232,6 +258,7 @@ export const useTenant = () => {
|
||||
return {
|
||||
currentTenant,
|
||||
availableTenants,
|
||||
currentTenantAccess,
|
||||
isLoading,
|
||||
error,
|
||||
...actions,
|
||||
|
||||
Reference in New Issue
Block a user