ADD new frontend
This commit is contained in:
352
frontend/src/pages/app/DashboardPage.tsx
Normal file
352
frontend/src/pages/app/DashboardPage.tsx
Normal file
@@ -0,0 +1,352 @@
|
||||
import React from 'react';
|
||||
import { TrendingUp, TrendingDown, AlertTriangle, CheckCircle, Clock, DollarSign } from 'lucide-react';
|
||||
import { Card, Badge } from '../../components/ui';
|
||||
import { PageHeader } from '../../components/layout';
|
||||
import { DashboardCard, KPIWidget, QuickActions, RecentActivity, ActivityType, ActivityStatus } from '../../components/domain/dashboard';
|
||||
|
||||
const DashboardPage: React.FC = () => {
|
||||
const kpiData = [
|
||||
{
|
||||
title: 'Ventas Hoy',
|
||||
value: {
|
||||
current: 1247,
|
||||
previous: 1112,
|
||||
format: 'currency' as const,
|
||||
prefix: '€'
|
||||
},
|
||||
trend: {
|
||||
direction: 'up' as const,
|
||||
value: 12,
|
||||
isPositive: true,
|
||||
comparisonPeriod: 'vs ayer'
|
||||
},
|
||||
icon: <DollarSign className="w-5 h-5" />,
|
||||
},
|
||||
{
|
||||
title: 'Órdenes Pendientes',
|
||||
value: {
|
||||
current: 23,
|
||||
previous: 24,
|
||||
format: 'number' as const
|
||||
},
|
||||
trend: {
|
||||
direction: 'down' as const,
|
||||
value: 4.2,
|
||||
isPositive: false,
|
||||
comparisonPeriod: 'vs ayer'
|
||||
},
|
||||
icon: <Clock className="w-5 h-5" />,
|
||||
},
|
||||
{
|
||||
title: 'Productos Vendidos',
|
||||
value: {
|
||||
current: 156,
|
||||
previous: 144,
|
||||
format: 'number' as const
|
||||
},
|
||||
trend: {
|
||||
direction: 'up' as const,
|
||||
value: 8.3,
|
||||
isPositive: true,
|
||||
comparisonPeriod: 'vs ayer'
|
||||
},
|
||||
icon: <CheckCircle className="w-5 h-5" />,
|
||||
},
|
||||
{
|
||||
title: 'Stock Crítico',
|
||||
value: {
|
||||
current: 4,
|
||||
previous: 2,
|
||||
format: 'number' as const
|
||||
},
|
||||
trend: {
|
||||
direction: 'up' as const,
|
||||
value: 100,
|
||||
isPositive: false,
|
||||
comparisonPeriod: 'vs ayer'
|
||||
},
|
||||
status: 'warning' as const,
|
||||
icon: <AlertTriangle className="w-5 h-5" />,
|
||||
},
|
||||
];
|
||||
|
||||
const quickActions = [
|
||||
{
|
||||
id: 'production',
|
||||
title: 'Nueva Orden de Producción',
|
||||
description: 'Crear nueva orden de producción',
|
||||
icon: <TrendingUp className="w-6 h-6" />,
|
||||
onClick: () => window.location.href = '/app/operations/production',
|
||||
href: '/app/operations/production'
|
||||
},
|
||||
{
|
||||
id: 'inventory',
|
||||
title: 'Gestionar Inventario',
|
||||
description: 'Administrar stock de productos',
|
||||
icon: <CheckCircle className="w-6 h-6" />,
|
||||
onClick: () => window.location.href = '/app/operations/inventory',
|
||||
href: '/app/operations/inventory'
|
||||
},
|
||||
{
|
||||
id: 'sales',
|
||||
title: 'Ver Ventas',
|
||||
description: 'Analizar ventas y reportes',
|
||||
icon: <DollarSign className="w-6 h-6" />,
|
||||
onClick: () => window.location.href = '/app/analytics/sales',
|
||||
href: '/app/analytics/sales'
|
||||
},
|
||||
{
|
||||
id: 'settings',
|
||||
title: 'Configuración',
|
||||
description: 'Ajustar configuración del sistema',
|
||||
icon: <AlertTriangle className="w-6 h-6" />,
|
||||
onClick: () => window.location.href = '/app/settings',
|
||||
href: '/app/settings'
|
||||
},
|
||||
];
|
||||
|
||||
const recentActivities = [
|
||||
{
|
||||
id: '1',
|
||||
type: ActivityType.PRODUCTION,
|
||||
title: 'Orden de producción completada',
|
||||
description: 'Pan de Molde Integral - 20 unidades',
|
||||
timestamp: new Date(Date.now() - 2 * 60 * 60 * 1000).toISOString(),
|
||||
status: ActivityStatus.SUCCESS,
|
||||
},
|
||||
{
|
||||
id: '2',
|
||||
type: ActivityType.INVENTORY,
|
||||
title: 'Stock bajo detectado',
|
||||
description: 'Levadura fresca necesita reposición',
|
||||
timestamp: new Date(Date.now() - 3 * 60 * 60 * 1000).toISOString(),
|
||||
status: ActivityStatus.WARNING,
|
||||
},
|
||||
{
|
||||
id: '3',
|
||||
type: ActivityType.SALES,
|
||||
title: 'Venta registrada',
|
||||
description: '€45.50 - Croissants y café',
|
||||
timestamp: new Date(Date.now() - 4 * 60 * 60 * 1000).toISOString(),
|
||||
status: ActivityStatus.INFO,
|
||||
},
|
||||
];
|
||||
|
||||
const productionStatus = {
|
||||
today: {
|
||||
target: 150,
|
||||
completed: 95,
|
||||
inProgress: 18,
|
||||
pending: 37,
|
||||
},
|
||||
efficiency: 85,
|
||||
};
|
||||
|
||||
const salesData = {
|
||||
today: 1247,
|
||||
yesterday: 1112,
|
||||
thisWeek: 8934,
|
||||
thisMonth: 35678,
|
||||
};
|
||||
|
||||
const inventoryAlerts = [
|
||||
{ item: 'Levadura Fresca', current: 2, min: 5, status: 'critical' },
|
||||
{ item: 'Harina Integral', current: 8, min: 10, status: 'low' },
|
||||
{ item: 'Mantequilla', current: 15, min: 20, status: 'low' },
|
||||
];
|
||||
|
||||
const topProducts = [
|
||||
{ name: 'Pan de Molde', sold: 45, revenue: 202.50 },
|
||||
{ name: 'Croissants', sold: 32, revenue: 192.00 },
|
||||
{ name: 'Baguettes', sold: 28, revenue: 84.00 },
|
||||
{ name: 'Magdalenas', sold: 24, revenue: 72.00 },
|
||||
];
|
||||
|
||||
|
||||
return (
|
||||
<div className="space-y-6">
|
||||
<PageHeader
|
||||
title="Panel de Control"
|
||||
description="Vista general de tu panadería"
|
||||
/>
|
||||
|
||||
{/* KPI Cards */}
|
||||
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-6">
|
||||
{kpiData.map((kpi, index) => (
|
||||
<KPIWidget
|
||||
key={index}
|
||||
title={kpi.title}
|
||||
value={kpi.value}
|
||||
trend={kpi.trend}
|
||||
icon={kpi.icon}
|
||||
status={kpi.status}
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
|
||||
<div className="grid grid-cols-1 lg:grid-cols-3 gap-6">
|
||||
{/* Production Status */}
|
||||
<Card className="p-6">
|
||||
<h3 className="text-lg font-semibold text-[var(--text-primary)] mb-4">Estado de Producción</h3>
|
||||
|
||||
<div className="space-y-4">
|
||||
<div className="flex justify-between items-center">
|
||||
<span className="text-sm text-[var(--text-secondary)]">Progreso del Día</span>
|
||||
<span className="text-sm font-medium">
|
||||
{productionStatus.today.completed} / {productionStatus.today.target}
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<div className="w-full bg-[var(--bg-quaternary)] rounded-full h-2">
|
||||
<div
|
||||
className="bg-[var(--color-info)] h-2 rounded-full"
|
||||
style={{
|
||||
width: `${(productionStatus.today.completed / productionStatus.today.target) * 100}%`
|
||||
}}
|
||||
></div>
|
||||
</div>
|
||||
|
||||
<div className="grid grid-cols-3 gap-4 mt-4">
|
||||
<div className="text-center">
|
||||
<p className="text-2xl font-bold text-[var(--color-success)]">{productionStatus.today.completed}</p>
|
||||
<p className="text-xs text-[var(--text-secondary)]">Completado</p>
|
||||
</div>
|
||||
<div className="text-center">
|
||||
<p className="text-2xl font-bold text-[var(--color-info)]">{productionStatus.today.inProgress}</p>
|
||||
<p className="text-xs text-[var(--text-secondary)]">En Proceso</p>
|
||||
</div>
|
||||
<div className="text-center">
|
||||
<p className="text-2xl font-bold text-[var(--color-primary)]">{productionStatus.today.pending}</p>
|
||||
<p className="text-xs text-[var(--text-secondary)]">Pendiente</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="mt-4 pt-4 border-t">
|
||||
<div className="flex justify-between items-center">
|
||||
<span className="text-sm text-[var(--text-secondary)]">Eficiencia</span>
|
||||
<span className="text-sm font-medium text-purple-600">{productionStatus.efficiency}%</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</Card>
|
||||
|
||||
{/* Sales Summary */}
|
||||
<Card className="p-6">
|
||||
<h3 className="text-lg font-semibold text-[var(--text-primary)] mb-4">Resumen de Ventas</h3>
|
||||
|
||||
<div className="space-y-4">
|
||||
<div className="flex justify-between items-center">
|
||||
<span className="text-sm text-[var(--text-secondary)]">Hoy</span>
|
||||
<span className="text-lg font-semibold text-[var(--color-success)]">€{salesData.today.toLocaleString()}</span>
|
||||
</div>
|
||||
|
||||
<div className="flex justify-between items-center">
|
||||
<span className="text-sm text-[var(--text-secondary)]">Ayer</span>
|
||||
<div className="flex items-center">
|
||||
<span className="text-sm font-medium mr-2">€{salesData.yesterday.toLocaleString()}</span>
|
||||
{salesData.today > salesData.yesterday ? (
|
||||
<TrendingUp className="h-4 w-4 text-[var(--color-success)]" />
|
||||
) : (
|
||||
<TrendingDown className="h-4 w-4 text-[var(--color-error)]" />
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="flex justify-between items-center">
|
||||
<span className="text-sm text-[var(--text-secondary)]">Esta Semana</span>
|
||||
<span className="text-sm font-medium">€{salesData.thisWeek.toLocaleString()}</span>
|
||||
</div>
|
||||
|
||||
<div className="flex justify-between items-center">
|
||||
<span className="text-sm text-[var(--text-secondary)]">Este Mes</span>
|
||||
<span className="text-sm font-medium">€{salesData.thisMonth.toLocaleString()}</span>
|
||||
</div>
|
||||
|
||||
<div className="mt-4 pt-4 border-t">
|
||||
<div className="text-center">
|
||||
<p className="text-xs text-[var(--text-secondary)]">Crecimiento vs ayer</p>
|
||||
<p className="text-lg font-semibold text-[var(--color-success)]">
|
||||
+{(((salesData.today - salesData.yesterday) / salesData.yesterday) * 100).toFixed(1)}%
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</Card>
|
||||
|
||||
{/* Inventory Alerts */}
|
||||
<Card className="p-6">
|
||||
<h3 className="text-lg font-semibold text-[var(--text-primary)] mb-4">Alertas de Inventario</h3>
|
||||
|
||||
<div className="space-y-3">
|
||||
{inventoryAlerts.map((alert, index) => (
|
||||
<div key={index} className="flex items-center justify-between p-3 bg-[var(--color-error)]/10 rounded-lg">
|
||||
<div>
|
||||
<p className="text-sm font-medium text-[var(--text-primary)]">{alert.item}</p>
|
||||
<p className="text-xs text-[var(--text-secondary)]">Stock: {alert.current} / Mín: {alert.min}</p>
|
||||
</div>
|
||||
<Badge variant={alert.status === 'critical' ? 'red' : 'yellow'}>
|
||||
{alert.status === 'critical' ? 'Crítico' : 'Bajo'}
|
||||
</Badge>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
|
||||
<div className="mt-4 pt-4 border-t">
|
||||
<button className="w-full text-sm text-[var(--color-info)] hover:text-[var(--color-info)] font-medium">
|
||||
Ver Todo el Inventario →
|
||||
</button>
|
||||
</div>
|
||||
</Card>
|
||||
</div>
|
||||
|
||||
<div className="grid grid-cols-1 lg:grid-cols-2 gap-6">
|
||||
{/* Top Products */}
|
||||
<Card className="p-6">
|
||||
<h3 className="text-lg font-semibold text-[var(--text-primary)] mb-4">Productos Más Vendidos</h3>
|
||||
|
||||
<div className="space-y-3">
|
||||
{topProducts.map((product, index) => (
|
||||
<div key={index} className="flex items-center justify-between">
|
||||
<div className="flex items-center">
|
||||
<span className="text-sm font-medium text-[var(--text-secondary)] w-6">{index + 1}.</span>
|
||||
<span className="text-sm text-[var(--text-primary)]">{product.name}</span>
|
||||
</div>
|
||||
<div className="text-right">
|
||||
<p className="text-sm font-medium text-[var(--text-primary)]">{product.sold} unidades</p>
|
||||
<p className="text-xs text-[var(--color-success)]">€{product.revenue.toFixed(2)}</p>
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
|
||||
<div className="mt-4 pt-4 border-t">
|
||||
<button className="w-full text-sm text-[var(--color-info)] hover:text-[var(--color-info)] font-medium">
|
||||
Ver Análisis Completo →
|
||||
</button>
|
||||
</div>
|
||||
</Card>
|
||||
|
||||
{/* Recent Activity */}
|
||||
<Card className="p-6">
|
||||
<h3 className="text-lg font-semibold text-[var(--text-primary)] mb-4">Actividad Reciente</h3>
|
||||
|
||||
<RecentActivity activities={recentActivities} />
|
||||
|
||||
<div className="mt-4 pt-4 border-t">
|
||||
<button className="w-full text-sm text-[var(--color-info)] hover:text-[var(--color-info)] font-medium">
|
||||
Ver Toda la Actividad →
|
||||
</button>
|
||||
</div>
|
||||
</Card>
|
||||
</div>
|
||||
|
||||
{/* Quick Actions */}
|
||||
<Card className="p-6">
|
||||
<h3 className="text-lg font-semibold text-[var(--text-primary)] mb-4">Acciones Rápidas</h3>
|
||||
<QuickActions actions={quickActions} />
|
||||
</Card>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default DashboardPage;
|
||||
Reference in New Issue
Block a user