New Frontend

This commit is contained in:
Urtzi Alfaro
2025-08-16 20:13:40 +02:00
parent 23c5f50111
commit 8914786973
35 changed files with 4223 additions and 538 deletions

View 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
};
};

View 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
};
};

View 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
};
};