ADD new frontend 2
This commit is contained in:
@@ -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"
|
||||
|
||||
@@ -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 && (
|
||||
|
||||
@@ -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',
|
||||
|
||||
Reference in New Issue
Block a user