New Frontend
This commit is contained in:
64
frontend/src/hooks/useAuth.ts
Normal file
64
frontend/src/hooks/useAuth.ts
Normal file
@@ -0,0 +1,64 @@
|
||||
import { useCallback } from 'react';
|
||||
import { useDispatch } from 'react-redux';
|
||||
import { loginSuccess, logout } from '../store/slices/authSlice';
|
||||
import { setCurrentTenant } from '../store/slices/tenantSlice';
|
||||
|
||||
interface User {
|
||||
id: string;
|
||||
email: string;
|
||||
fullName: string;
|
||||
role: string;
|
||||
isOnboardingComplete: boolean;
|
||||
tenant_id?: string;
|
||||
}
|
||||
|
||||
export const useAuth = () => {
|
||||
const dispatch = useDispatch();
|
||||
|
||||
const initializeAuth = useCallback(async () => {
|
||||
try {
|
||||
// Check for stored auth token
|
||||
const token = localStorage.getItem('auth_token');
|
||||
const userData = localStorage.getItem('user_data');
|
||||
const selectedTenantId = localStorage.getItem('selectedTenantId');
|
||||
|
||||
if (token && userData) {
|
||||
const user: User = JSON.parse(userData);
|
||||
|
||||
// Set user in auth state
|
||||
dispatch(loginSuccess({ user, token }));
|
||||
|
||||
// If there's a selected tenant, try to load it
|
||||
if (selectedTenantId) {
|
||||
// This would normally fetch tenant data from API
|
||||
// For now, we'll just set a placeholder
|
||||
const tenantData = {
|
||||
id: selectedTenantId,
|
||||
name: 'Mi Panadería',
|
||||
business_type: 'individual',
|
||||
address: 'Dirección de ejemplo'
|
||||
};
|
||||
dispatch(setCurrentTenant(tenantData));
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Failed to initialize auth:', error);
|
||||
// Clear invalid tokens
|
||||
localStorage.removeItem('auth_token');
|
||||
localStorage.removeItem('user_data');
|
||||
localStorage.removeItem('selectedTenantId');
|
||||
}
|
||||
}, [dispatch]);
|
||||
|
||||
const handleLogout = useCallback(() => {
|
||||
dispatch(logout());
|
||||
localStorage.removeItem('auth_token');
|
||||
localStorage.removeItem('user_data');
|
||||
localStorage.removeItem('selectedTenantId');
|
||||
}, [dispatch]);
|
||||
|
||||
return {
|
||||
initializeAuth,
|
||||
handleLogout
|
||||
};
|
||||
};
|
||||
56
frontend/src/hooks/useBakeryType.ts
Normal file
56
frontend/src/hooks/useBakeryType.ts
Normal file
@@ -0,0 +1,56 @@
|
||||
import { useSelector } from 'react-redux';
|
||||
import { RootState } from '../store';
|
||||
|
||||
export type BakeryType = 'individual' | 'central_workshop';
|
||||
|
||||
interface BakeryTypeConfig {
|
||||
bakeryType: BakeryType;
|
||||
isIndividual: boolean;
|
||||
isCentral: boolean;
|
||||
getLabel: () => string;
|
||||
getDescription: () => string;
|
||||
getInventoryLabel: () => string;
|
||||
getProductionLabel: () => string;
|
||||
getSupplierLabel: () => string;
|
||||
}
|
||||
|
||||
export const useBakeryType = (): BakeryTypeConfig => {
|
||||
const { currentTenant } = useSelector((state: RootState) => state.tenant);
|
||||
|
||||
const bakeryType: BakeryType = currentTenant?.business_type || 'individual';
|
||||
const isIndividual = bakeryType === 'individual';
|
||||
const isCentral = bakeryType === 'central_workshop';
|
||||
|
||||
const getLabel = (): string => {
|
||||
return isIndividual ? 'Panadería Individual' : 'Obrador Central';
|
||||
};
|
||||
|
||||
const getDescription = (): string => {
|
||||
return isIndividual
|
||||
? 'Panadería con producción in-situ usando ingredientes frescos'
|
||||
: 'Obrador central que distribuye productos semi-terminados o terminados';
|
||||
};
|
||||
|
||||
const getInventoryLabel = (): string => {
|
||||
return isIndividual ? 'Ingredientes' : 'Productos';
|
||||
};
|
||||
|
||||
const getProductionLabel = (): string => {
|
||||
return isIndividual ? 'Producción' : 'Distribución';
|
||||
};
|
||||
|
||||
const getSupplierLabel = (): string => {
|
||||
return isIndividual ? 'Proveedores de Ingredientes' : 'Proveedores de Productos';
|
||||
};
|
||||
|
||||
return {
|
||||
bakeryType,
|
||||
isIndividual,
|
||||
isCentral,
|
||||
getLabel,
|
||||
getDescription,
|
||||
getInventoryLabel,
|
||||
getProductionLabel,
|
||||
getSupplierLabel
|
||||
};
|
||||
};
|
||||
112
frontend/src/hooks/usePermissions.ts
Normal file
112
frontend/src/hooks/usePermissions.ts
Normal file
@@ -0,0 +1,112 @@
|
||||
import { useSelector } from 'react-redux';
|
||||
import { RootState } from '../store';
|
||||
|
||||
export type UserRole = 'owner' | 'admin' | 'manager' | 'worker';
|
||||
|
||||
interface Permission {
|
||||
action: string;
|
||||
resource: string;
|
||||
}
|
||||
|
||||
interface PermissionsConfig {
|
||||
userRole: UserRole;
|
||||
hasRole: (role: UserRole | UserRole[]) => boolean;
|
||||
hasPermission: (permission: string) => boolean;
|
||||
canManageUsers: boolean;
|
||||
canManageTenants: boolean;
|
||||
canViewAnalytics: boolean;
|
||||
canEditRecipes: boolean;
|
||||
canViewFinancials: boolean;
|
||||
canManageSettings: boolean;
|
||||
}
|
||||
|
||||
// Define role hierarchy (higher index = more permissions)
|
||||
const ROLE_HIERARCHY: UserRole[] = ['worker', 'manager', 'admin', 'owner'];
|
||||
|
||||
// Define permissions for each role
|
||||
const ROLE_PERMISSIONS: Record<UserRole, string[]> = {
|
||||
worker: [
|
||||
'view:inventory',
|
||||
'view:production',
|
||||
'view:orders',
|
||||
'update:production_status',
|
||||
'view:recipes_basic'
|
||||
],
|
||||
manager: [
|
||||
'view:inventory',
|
||||
'view:production',
|
||||
'view:orders',
|
||||
'view:sales',
|
||||
'update:production_status',
|
||||
'update:inventory',
|
||||
'create:orders',
|
||||
'view:recipes_basic',
|
||||
'view:analytics_basic',
|
||||
'view:reports_operational'
|
||||
],
|
||||
admin: [
|
||||
'view:inventory',
|
||||
'view:production',
|
||||
'view:orders',
|
||||
'view:sales',
|
||||
'view:analytics',
|
||||
'view:financials',
|
||||
'update:production_status',
|
||||
'update:inventory',
|
||||
'create:orders',
|
||||
'manage:recipes',
|
||||
'manage:users',
|
||||
'view:reports_all',
|
||||
'manage:settings_tenant'
|
||||
],
|
||||
owner: [
|
||||
'*' // All permissions
|
||||
]
|
||||
};
|
||||
|
||||
export const usePermissions = (): PermissionsConfig => {
|
||||
const { user } = useSelector((state: RootState) => state.auth);
|
||||
|
||||
const userRole: UserRole = (user?.role as UserRole) || 'worker';
|
||||
|
||||
const hasRole = (role: UserRole | UserRole[]): boolean => {
|
||||
if (Array.isArray(role)) {
|
||||
return role.includes(userRole);
|
||||
}
|
||||
|
||||
const userRoleIndex = ROLE_HIERARCHY.indexOf(userRole);
|
||||
const requiredRoleIndex = ROLE_HIERARCHY.indexOf(role);
|
||||
|
||||
return userRoleIndex >= requiredRoleIndex;
|
||||
};
|
||||
|
||||
const hasPermission = (permission: string): boolean => {
|
||||
const userPermissions = ROLE_PERMISSIONS[userRole] || [];
|
||||
|
||||
// Owner has all permissions
|
||||
if (userPermissions.includes('*')) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return userPermissions.includes(permission);
|
||||
};
|
||||
|
||||
const canManageUsers = hasPermission('manage:users');
|
||||
const canManageTenants = hasRole(['admin', 'owner']);
|
||||
const canViewAnalytics = hasPermission('view:analytics') || hasPermission('view:analytics_basic');
|
||||
const canEditRecipes = hasPermission('manage:recipes');
|
||||
const canViewFinancials = hasPermission('view:financials');
|
||||
const canManageSettings = hasRole(['admin', 'owner']);
|
||||
|
||||
return {
|
||||
userRole,
|
||||
hasRole,
|
||||
hasPermission,
|
||||
canManageUsers,
|
||||
canManageTenants,
|
||||
canViewAnalytics,
|
||||
canEditRecipes,
|
||||
canViewFinancials,
|
||||
canManageSettings
|
||||
};
|
||||
};
|
||||
Reference in New Issue
Block a user