ADD new frontend 2

This commit is contained in:
Urtzi Alfaro
2025-08-28 17:15:29 +02:00
parent 0fd273cfce
commit 9ea6794923
195 changed files with 178 additions and 56461 deletions

View File

@@ -83,12 +83,12 @@ const DashboardCard = forwardRef<HTMLDivElement, DashboardCardProps>(({
};
const variantStyles = {
metric: 'bg-gradient-to-br from-white to-blue-50 border-[var(--color-info)]/20 hover:border-blue-300',
chart: 'bg-white border-[var(--border-primary)] hover:border-[var(--border-secondary)]',
list: 'bg-white border-[var(--border-primary)] hover:border-[var(--border-secondary)]',
activity: 'bg-gradient-to-br from-white to-green-50 border-green-200 hover:border-green-300',
status: 'bg-gradient-to-br from-white to-purple-50 border-purple-200 hover:border-purple-300',
action: 'bg-gradient-to-br from-white to-amber-50 border-amber-200 hover:border-amber-300 hover:shadow-lg'
metric: 'bg-[var(--bg-primary)] border-[var(--border-primary)] hover:border-[var(--border-secondary)]',
chart: 'bg-[var(--bg-primary)] border-[var(--border-primary)] hover:border-[var(--border-secondary)]',
list: 'bg-[var(--bg-primary)] border-[var(--border-primary)] hover:border-[var(--border-secondary)]',
activity: 'bg-[var(--bg-primary)] border-[var(--border-primary)] hover:border-[var(--border-secondary)]',
status: 'bg-[var(--bg-primary)] border-[var(--border-primary)] hover:border-[var(--border-secondary)]',
action: 'bg-[var(--bg-primary)] border-[var(--border-primary)] hover:border-[var(--border-secondary)] hover:shadow-lg'
};
const cardClasses = clsx(
@@ -97,7 +97,7 @@ const DashboardCard = forwardRef<HTMLDivElement, DashboardCardProps>(({
{
'cursor-pointer transform hover:-translate-y-1': interactive || onClick,
'opacity-50': isLoading,
'border-red-300 bg-red-50': hasError,
'border-[var(--color-error)]/30 bg-[var(--color-error)]/5': hasError,
},
className
);
@@ -142,7 +142,7 @@ const DashboardCard = forwardRef<HTMLDivElement, DashboardCardProps>(({
const renderErrorContent = () => (
<div className="text-center py-8">
<div className="text-red-500 mb-4">
<div className="text-[var(--color-error)] mb-4">
<svg
className="w-12 h-12 mx-auto"
fill="none"

View File

@@ -208,19 +208,20 @@ export const Header = forwardRef<HeaderRef, HeaderProps>(({
'flex items-center justify-between px-4 lg:px-6',
'transition-all duration-300 ease-in-out',
'backdrop-blur-sm bg-[var(--bg-primary)]/95',
'z-[var(--z-fixed)]',
className
)}
role="banner"
aria-label="Navegación principal"
>
{/* Left section */}
<div className="flex items-center gap-4 flex-1 min-w-0">
<div className="flex items-center gap-4 flex-1 min-w-0 h-full">
{/* Mobile menu button */}
<Button
variant="ghost"
size="sm"
onClick={onMenuClick}
className="lg:hidden p-2"
className="lg:hidden w-10 h-10 p-0 flex items-center justify-center"
aria-label="Abrir menú de navegación"
>
<Menu className="h-5 w-5" />
@@ -230,12 +231,13 @@ export const Header = forwardRef<HeaderRef, HeaderProps>(({
<div className="flex items-center gap-3 min-w-0">
{logo || (
<>
<div className="w-8 h-8 bg-gradient-to-br from-[var(--color-primary)] to-[var(--color-primary-dark)] rounded-lg flex items-center justify-center text-white font-bold text-sm">
<div className="w-8 h-8 bg-gradient-to-br from-[var(--color-primary)] to-[var(--color-primary-dark)] rounded-lg flex items-center justify-center text-white font-bold text-sm flex-shrink-0">
PI
</div>
<h1 className={clsx(
'font-semibold text-[var(--text-primary)] transition-opacity duration-300',
'hidden sm:block',
'hidden sm:block text-lg leading-tight',
'self-center',
sidebarCollapsed ? 'lg:block' : 'lg:hidden xl:block'
)}>
Panadería IA
@@ -251,7 +253,9 @@ export const Header = forwardRef<HeaderRef, HeaderProps>(({
className="hidden md:flex items-center flex-1 max-w-md mx-4"
>
<div className="relative w-full">
<Search className="absolute left-3 top-1/2 transform -translate-y-1/2 h-4 w-4 text-[var(--text-tertiary)]" />
<div className="absolute left-3 top-0 bottom-0 flex items-center pointer-events-none">
<Search className="h-4 w-4 text-[var(--text-tertiary)]" />
</div>
<input
ref={searchInputRef}
type="text"
@@ -261,28 +265,32 @@ export const Header = forwardRef<HeaderRef, HeaderProps>(({
onBlur={() => setIsSearchFocused(false)}
placeholder={searchPlaceholder}
className={clsx(
'w-full pl-10 pr-10 py-2 text-sm',
'w-full pl-10 pr-12 py-2.5 text-sm',
'bg-[var(--bg-secondary)] border border-[var(--border-primary)]',
'rounded-lg transition-colors duration-200',
'focus:outline-none focus:ring-2 focus:ring-[var(--color-primary)]/20',
'focus:border-[var(--color-primary)]',
'placeholder:text-[var(--text-tertiary)]'
'placeholder:text-[var(--text-tertiary)]',
'h-9'
)}
aria-label="Buscar en la aplicación"
/>
{searchValue && (
{searchValue ? (
<button
type="button"
onClick={clearSearch}
className="absolute right-3 top-1/2 transform -translate-y-1/2 p-1 hover:bg-[var(--bg-tertiary)] rounded-full transition-colors"
className="absolute right-3 top-0 bottom-0 flex items-center p-1 hover:bg-[var(--bg-tertiary)] rounded-full transition-colors"
aria-label="Limpiar búsqueda"
>
<X className="h-3 w-3 text-[var(--text-tertiary)]" />
</button>
) : (
<div className="absolute right-3 top-0 bottom-0 flex items-center pointer-events-none">
<kbd className="hidden lg:inline-flex items-center justify-center h-5 px-1.5 text-xs text-[var(--text-tertiary)] font-mono bg-[var(--bg-tertiary)] rounded border border-[var(--border-primary)]">
K
</kbd>
</div>
)}
<kbd className="absolute right-3 top-1/2 transform -translate-y-1/2 hidden lg:inline-flex items-center gap-1 text-xs text-[var(--text-tertiary)] font-mono">
K
</kbd>
</div>
</form>
)}
@@ -290,14 +298,14 @@ export const Header = forwardRef<HeaderRef, HeaderProps>(({
{/* Right section */}
{isAuthenticated && (
<div className="flex items-center gap-2">
<div className="flex items-center gap-1 h-full">
{/* Mobile search */}
{showSearch && (
<Button
variant="ghost"
size="sm"
onClick={focusSearch}
className="md:hidden p-2"
className="md:hidden w-10 h-10 p-0 flex items-center justify-center"
aria-label="Buscar"
>
<Search className="h-5 w-5" />
@@ -311,7 +319,7 @@ export const Header = forwardRef<HeaderRef, HeaderProps>(({
variant="ghost"
size="sm"
onClick={() => setIsThemeMenuOpen(!isThemeMenuOpen)}
className="p-2"
className="w-10 h-10 p-0 flex items-center justify-center"
aria-label={`Tema actual: ${theme}`}
aria-expanded={isThemeMenuOpen}
aria-haspopup="true"
@@ -354,7 +362,7 @@ export const Header = forwardRef<HeaderRef, HeaderProps>(({
variant="ghost"
size="sm"
onClick={onNotificationClick}
className="p-2 relative"
className="w-10 h-10 p-0 flex items-center justify-center relative"
aria-label={`Notificaciones${notificationCount > 0 ? ` (${notificationCount})` : ''}`}
>
<Bell className="h-5 w-5" />
@@ -378,7 +386,7 @@ export const Header = forwardRef<HeaderRef, HeaderProps>(({
variant="ghost"
size="sm"
onClick={toggleUserMenu}
className="flex items-center gap-2 pl-2 pr-3 py-1 h-auto"
className="flex items-center gap-2 pl-2 pr-2 py-1.5 h-10 min-w-0"
aria-label="Menú de usuario"
aria-expanded={isUserMenuOpen}
aria-haspopup="true"
@@ -388,11 +396,12 @@ export const Header = forwardRef<HeaderRef, HeaderProps>(({
alt={user.full_name}
fallback={user.full_name}
size="sm"
className="flex-shrink-0"
/>
<span className="hidden sm:block text-sm font-medium text-[var(--text-primary)] truncate max-w-[120px]">
{user.full_name}
</span>
<ChevronDown className="h-4 w-4 text-[var(--text-tertiary)]" />
<ChevronDown className="h-4 w-4 text-[var(--text-tertiary)] flex-shrink-0" />
</Button>
{isUserMenuOpen && (

View File

@@ -203,35 +203,141 @@ export const routesConfig: RouteConfig[] = [
cache: true,
},
},
// Inventory Management
// Operations Section
{
path: '/app/operations/inventory',
name: 'Inventory',
component: 'InventoryPage',
title: 'Inventario',
icon: 'inventory',
requiresAuth: true,
showInNavigation: true,
},
// Production Management
{
path: '/app/operations/production',
name: 'Production',
component: 'ProductionPage',
title: 'Producción',
path: '/app/operations',
name: 'Operations',
component: 'OperationsPage',
title: 'Operaciones',
icon: 'production',
requiresAuth: true,
showInNavigation: true,
children: [
{
path: '/app/operations/production',
name: 'Production',
component: 'ProductionPage',
title: 'Producción',
icon: 'production',
requiresAuth: true,
showInNavigation: true,
showInBreadcrumbs: true,
},
{
path: '/app/operations/orders',
name: 'Orders',
component: 'OrdersPage',
title: 'Pedidos',
icon: 'orders',
requiresAuth: true,
showInNavigation: true,
showInBreadcrumbs: true,
},
{
path: '/app/operations/inventory',
name: 'Inventory',
component: 'InventoryPage',
title: 'Inventario',
icon: 'inventory',
requiresAuth: true,
showInNavigation: true,
showInBreadcrumbs: true,
},
{
path: '/app/operations/recipes',
name: 'Recipes',
component: 'RecipesPage',
title: 'Recetas',
icon: 'production',
requiresAuth: true,
showInNavigation: true,
showInBreadcrumbs: true,
},
{
path: '/app/operations/procurement',
name: 'Procurement',
component: 'ProcurementPage',
title: 'Compras',
icon: 'procurement',
requiresAuth: true,
showInNavigation: true,
showInBreadcrumbs: true,
},
{
path: '/app/operations/pos',
name: 'POS',
component: 'POSPage',
title: 'Punto de Venta',
icon: 'pos',
requiresAuth: true,
showInNavigation: true,
showInBreadcrumbs: true,
},
],
},
// Settings
// Analytics Section
{
path: ROUTES.SETTINGS,
path: '/app/analytics',
name: 'Analytics',
component: 'AnalyticsPage',
title: 'Analytics',
icon: 'sales',
requiresAuth: true,
requiredRoles: ['admin', 'manager'],
showInNavigation: true,
children: [
{
path: '/app/analytics/forecasting',
name: 'Forecasting',
component: 'ForecastingPage',
title: 'Pronósticos',
icon: 'forecasting',
requiresAuth: true,
requiredRoles: ['admin', 'manager'],
showInNavigation: true,
showInBreadcrumbs: true,
},
{
path: '/app/analytics/sales',
name: 'SalesAnalytics',
component: 'SalesAnalyticsPage',
title: 'Análisis de Ventas',
icon: 'sales',
requiresAuth: true,
requiredRoles: ['admin', 'manager'],
showInNavigation: true,
showInBreadcrumbs: true,
},
{
path: '/app/analytics/performance',
name: 'PerformanceAnalytics',
component: 'PerformanceAnalyticsPage',
title: 'Análisis de Rendimiento',
icon: 'sales',
requiresAuth: true,
requiredRoles: ['admin', 'manager'],
showInNavigation: true,
showInBreadcrumbs: true,
},
{
path: '/app/analytics/ai-insights',
name: 'AIInsights',
component: 'AIInsightsPage',
title: 'Insights de IA',
icon: 'forecasting',
requiresAuth: true,
requiredRoles: ['admin', 'manager'],
showInNavigation: true,
showInBreadcrumbs: true,
},
],
},
// Settings Section
{
path: '/app/settings',
name: 'Settings',
component: 'SettingsPage',
title: 'Configuración',
@@ -246,16 +352,7 @@ export const routesConfig: RouteConfig[] = [
title: 'Configuración de Panadería',
icon: 'settings',
requiresAuth: true,
showInNavigation: true,
showInBreadcrumbs: true,
},
{
path: '/app/settings/system',
name: 'SystemSettings',
component: 'SystemSettingsPage',
title: 'Configuración del Sistema',
icon: 'settings',
requiresAuth: true,
requiredRoles: ['admin'],
showInNavigation: true,
showInBreadcrumbs: true,
},
@@ -266,6 +363,18 @@ export const routesConfig: RouteConfig[] = [
title: 'Gestión de Equipo',
icon: 'settings',
requiresAuth: true,
requiredRoles: ['admin', 'manager'],
showInNavigation: true,
showInBreadcrumbs: true,
},
{
path: '/app/settings/system',
name: 'SystemSettings',
component: 'SystemSettingsPage',
title: 'Configuración del Sistema',
icon: 'settings',
requiresAuth: true,
requiredRoles: ['admin'],
showInNavigation: true,
showInBreadcrumbs: true,
},
@@ -276,88 +385,13 @@ export const routesConfig: RouteConfig[] = [
title: 'Entrenamiento',
icon: 'training',
requiresAuth: true,
requiredRoles: ['admin', 'manager'],
showInNavigation: true,
showInBreadcrumbs: true,
},
],
},
// Individual Operations Pages
{
path: '/app/operations/recipes',
name: 'Recipes',
component: 'RecipesPage',
title: 'Recetas',
icon: 'production',
requiresAuth: true,
showInNavigation: true,
},
{
path: '/app/operations/orders',
name: 'Orders',
component: 'OrdersPage',
title: 'Pedidos',
icon: 'orders',
requiresAuth: true,
showInNavigation: true,
},
{
path: '/app/operations/procurement',
name: 'Procurement',
component: 'ProcurementPage',
title: 'Compras',
icon: 'procurement',
requiresAuth: true,
showInNavigation: true,
},
{
path: '/app/operations/pos',
name: 'POS',
component: 'POSPage',
title: 'Punto de Venta',
icon: 'pos',
requiresAuth: true,
showInNavigation: true,
},
// Individual Analytics Pages
{
path: '/app/analytics/forecasting',
name: 'Forecasting',
component: 'ForecastingPage',
title: 'Pronósticos',
icon: 'forecasting',
requiresAuth: true,
showInNavigation: true,
},
{
path: '/app/analytics/sales',
name: 'SalesAnalytics',
component: 'SalesAnalyticsPage',
title: 'Análisis de Ventas',
icon: 'sales',
requiresAuth: true,
showInNavigation: true,
},
{
path: '/app/analytics/ai-insights',
name: 'AIInsights',
component: 'AIInsightsPage',
title: 'Insights de IA',
icon: 'forecasting',
requiresAuth: true,
showInNavigation: true,
},
{
path: '/app/analytics/performance',
name: 'PerformanceAnalytics',
component: 'PerformanceAnalyticsPage',
title: 'Análisis de Rendimiento',
icon: 'sales',
requiresAuth: true,
showInNavigation: true,
},
// Communications Section
{
path: '/app/communications',