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,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;