ADD new frontend

This commit is contained in:
Urtzi Alfaro
2025-08-28 10:41:04 +02:00
parent 9c247a5f99
commit 0fd273cfce
492 changed files with 114979 additions and 1632 deletions

View File

@@ -0,0 +1,263 @@
import { create } from 'zustand';
import { persist, createJSONStorage } from 'zustand/middleware';
export interface User {
id: string;
email: string;
name: string;
role: 'admin' | 'manager' | 'baker' | 'staff';
permissions: string[];
tenantId: string;
tenantName: string;
avatar?: string;
lastLogin?: string;
preferences?: {
language: string;
timezone: string;
theme: 'light' | 'dark';
notifications: boolean;
};
}
export interface AuthState {
// State
user: User | null;
token: string | null;
refreshToken: string | null;
isAuthenticated: boolean;
isLoading: boolean;
error: string | null;
// Actions
login: (email: string, password: string) => Promise<void>;
logout: () => void;
refreshAuth: () => Promise<void>;
updateUser: (updates: Partial<User>) => void;
clearError: () => void;
setLoading: (loading: boolean) => void;
// Permission helpers
hasPermission: (permission: string) => boolean;
hasRole: (role: string) => boolean;
canAccess: (resource: string, action: string) => boolean;
}
// Mock API functions (replace with actual API calls)
const mockLogin = async (email: string, password: string): Promise<{ user: User; token: string; refreshToken: string }> => {
// Simulate API delay
await new Promise(resolve => setTimeout(resolve, 1000));
if (email === 'admin@bakery.com' && password === 'admin') {
return {
user: {
id: '1',
email: 'admin@bakery.com',
name: 'Admin User',
role: 'admin',
permissions: ['*'],
tenantId: 'tenant-1',
tenantName: 'Panadería San Miguel',
avatar: undefined,
lastLogin: new Date().toISOString(),
preferences: {
language: 'es',
timezone: 'Europe/Madrid',
theme: 'light',
notifications: true,
},
},
token: 'mock-jwt-token',
refreshToken: 'mock-refresh-token',
};
}
throw new Error('Credenciales inválidas');
};
const mockRefreshToken = async (refreshToken: string): Promise<{ token: string; refreshToken: string }> => {
await new Promise(resolve => setTimeout(resolve, 500));
if (refreshToken === 'mock-refresh-token') {
return {
token: 'new-mock-jwt-token',
refreshToken: 'new-mock-refresh-token',
};
}
throw new Error('Invalid refresh token');
};
export const useAuthStore = create<AuthState>()(
persist(
(set, get) => ({
// Initial state
user: null,
token: null,
refreshToken: null,
isAuthenticated: false,
isLoading: false,
error: null,
// Actions
login: async (email: string, password: string) => {
try {
set({ isLoading: true, error: null });
const response = await mockLogin(email, password);
set({
user: response.user,
token: response.token,
refreshToken: response.refreshToken,
isAuthenticated: true,
isLoading: false,
error: null,
});
} catch (error) {
set({
user: null,
token: null,
refreshToken: null,
isAuthenticated: false,
isLoading: false,
error: error instanceof Error ? error.message : 'Error de autenticación',
});
throw error;
}
},
logout: () => {
set({
user: null,
token: null,
refreshToken: null,
isAuthenticated: false,
isLoading: false,
error: null,
});
},
refreshAuth: async () => {
try {
const { refreshToken } = get();
if (!refreshToken) {
throw new Error('No refresh token available');
}
set({ isLoading: true });
const response = await mockRefreshToken(refreshToken);
set({
token: response.token,
refreshToken: response.refreshToken,
isLoading: false,
error: null,
});
} catch (error) {
set({
user: null,
token: null,
refreshToken: null,
isAuthenticated: false,
isLoading: false,
error: error instanceof Error ? error.message : 'Error al renovar sesión',
});
throw error;
}
},
updateUser: (updates: Partial<User>) => {
const { user } = get();
if (user) {
set({
user: { ...user, ...updates },
});
}
},
clearError: () => {
set({ error: null });
},
setLoading: (loading: boolean) => {
set({ isLoading: loading });
},
// Permission helpers
hasPermission: (permission: string): boolean => {
const { user } = get();
if (!user) return false;
// Admin has all permissions
if (user.permissions.includes('*')) return true;
return user.permissions.includes(permission);
},
hasRole: (role: string): boolean => {
const { user } = get();
return user?.role === role;
},
canAccess: (resource: string, action: string): boolean => {
const { user, hasPermission } = get();
if (!user) return false;
// Check specific permission
if (hasPermission(`${resource}:${action}`)) return true;
// Check wildcard permissions
if (hasPermission(`${resource}:*`)) return true;
if (hasPermission('*')) return true;
// Role-based access fallback
switch (user.role) {
case 'admin':
return true;
case 'manager':
return ['inventory', 'production', 'sales', 'reports'].includes(resource);
case 'baker':
return ['production', 'inventory'].includes(resource) &&
['read', 'update'].includes(action);
case 'staff':
return ['inventory', 'sales'].includes(resource) &&
action === 'read';
default:
return false;
}
},
}),
{
name: 'auth-storage',
storage: createJSONStorage(() => localStorage),
partialize: (state) => ({
user: state.user,
token: state.token,
refreshToken: state.refreshToken,
isAuthenticated: state.isAuthenticated,
}),
}
)
);
// Selectors for common use cases
export const useAuthUser = () => useAuthStore((state) => state.user);
export const useIsAuthenticated = () => useAuthStore((state) => state.isAuthenticated);
export const useAuthLoading = () => useAuthStore((state) => state.isLoading);
export const useAuthError = () => useAuthStore((state) => state.error);
export const usePermissions = () => useAuthStore((state) => ({
hasPermission: state.hasPermission,
hasRole: state.hasRole,
canAccess: state.canAccess,
}));
// Hook for auth actions
export const useAuthActions = () => useAuthStore((state) => ({
login: state.login,
logout: state.logout,
refreshAuth: state.refreshAuth,
updateUser: state.updateUser,
clearError: state.clearError,
setLoading: state.setLoading,
}));