Files
bakery-ia/frontend/src/pages/app/data/traffic/TrafficPage.tsx.backup
2025-08-28 10:41:04 +02:00

336 lines
13 KiB
Plaintext

import React, { useState } from 'react';
import { Users, Clock, TrendingUp, MapPin, Calendar, BarChart3, Download, Filter } from 'lucide-react';
import { Button, Card, Badge } from '../../../../components/ui';
import { PageHeader } from '../../../../components/layout';
const TrafficPage: React.FC = () => {
const [selectedPeriod, setSelectedPeriod] = useState('week');
const [selectedMetric, setSelectedMetric] = useState('visitors');
const trafficData = {
totalVisitors: 2847,
peakHour: '12:00',
averageVisitDuration: '23min',
busyDays: ['Viernes', 'Sábado'],
conversionRate: 68.4
};
const hourlyTraffic = [
{ hour: '07:00', visitors: 15, sales: 12, duration: '18min' },
{ hour: '08:00', visitors: 32, sales: 24, duration: '22min' },
{ hour: '09:00', visitors: 45, sales: 28, duration: '25min' },
{ hour: '10:00', visitors: 38, sales: 25, duration: '24min' },
{ hour: '11:00', visitors: 52, sales: 35, duration: '26min' },
{ hour: '12:00', visitors: 78, sales: 54, duration: '28min' },
{ hour: '13:00', visitors: 85, sales: 58, duration: '30min' },
{ hour: '14:00', visitors: 62, sales: 42, duration: '27min' },
{ hour: '15:00', visitors: 48, sales: 32, duration: '25min' },
{ hour: '16:00', visitors: 55, sales: 38, duration: '26min' },
{ hour: '17:00', visitors: 68, sales: 46, duration: '29min' },
{ hour: '18:00', visitors: 74, sales: 52, duration: '31min' },
{ hour: '19:00', visitors: 56, sales: 39, duration: '28min' },
{ hour: '20:00', visitors: 28, sales: 18, duration: '22min' }
];
const dailyTraffic = [
{ day: 'Lun', visitors: 245, sales: 168, conversion: 68.6, avgDuration: '22min' },
{ day: 'Mar', visitors: 289, sales: 195, conversion: 67.5, avgDuration: '24min' },
{ day: 'Mié', visitors: 321, sales: 218, conversion: 67.9, avgDuration: '25min' },
{ day: 'Jue', visitors: 356, sales: 242, conversion: 68.0, avgDuration: '26min' },
{ day: 'Vie', visitors: 445, sales: 312, conversion: 70.1, avgDuration: '28min' },
{ day: 'Sáb', visitors: 498, sales: 348, conversion: 69.9, avgDuration: '30min' },
{ day: 'Dom', visitors: 398, sales: 265, conversion: 66.6, avgDuration: '27min' }
];
const trafficSources = [
{ source: 'Pie', visitors: 1245, percentage: 43.7, trend: 5.2 },
{ source: 'Búsqueda Local', visitors: 687, percentage: 24.1, trend: 12.3 },
{ source: 'Recomendaciones', visitors: 423, percentage: 14.9, trend: -2.1 },
{ source: 'Redes Sociales', visitors: 298, percentage: 10.5, trend: 8.7 },
{ source: 'Publicidad', visitors: 194, percentage: 6.8, trend: 15.4 }
];
const customerSegments = [
{
segment: 'Regulares Matutinos',
count: 145,
percentage: 24.2,
peakHours: ['07:00-09:00'],
avgSpend: 12.50,
frequency: 'Diaria'
},
{
segment: 'Familia Fin de Semana',
count: 198,
percentage: 33.1,
peakHours: ['10:00-13:00'],
avgSpend: 28.90,
frequency: 'Semanal'
},
{
segment: 'Oficinistas Almuerzo',
count: 112,
percentage: 18.7,
peakHours: ['12:00-14:00'],
avgSpend: 8.75,
frequency: '2-3x semana'
},
{
segment: 'Clientes Ocasionales',
count: 143,
percentage: 23.9,
peakHours: ['16:00-19:00'],
avgSpend: 15.20,
frequency: 'Mensual'
}
];
const getTrendColor = (trend: number) => {
return trend >= 0 ? 'text-green-600' : 'text-red-600';
};
const getTrendIcon = (trend: number) => {
return trend >= 0 ? '↗' : '↘';
};
const maxVisitors = Math.max(...hourlyTraffic.map(h => h.visitors));
const maxDailyVisitors = Math.max(...dailyTraffic.map(d => d.visitors));
return (
<div className="p-6 space-y-6">
<PageHeader
title="Análisis de Tráfico"
description="Monitorea los patrones de visitas y flujo de clientes"
action={
<div className="flex space-x-2">
<Button variant="outline">
<Filter className="w-4 h-4 mr-2" />
Filtros
</Button>
<Button variant="outline">
<Download className="w-4 h-4 mr-2" />
Exportar
</Button>
</div>
}
/>
{/* Traffic Stats */}
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-5 gap-6">
<Card className="p-6">
<div className="flex items-center justify-between">
<div>
<p className="text-sm font-medium text-gray-600">Visitantes Totales</p>
<p className="text-3xl font-bold text-blue-600">{trafficData.totalVisitors.toLocaleString()}</p>
</div>
<div className="h-12 w-12 bg-blue-100 rounded-full flex items-center justify-center">
<Users className="h-6 w-6 text-blue-600" />
</div>
</div>
</Card>
<Card className="p-6">
<div className="flex items-center justify-between">
<div>
<p className="text-sm font-medium text-gray-600">Hora Pico</p>
<p className="text-3xl font-bold text-green-600">{trafficData.peakHour}</p>
</div>
<div className="h-12 w-12 bg-green-100 rounded-full flex items-center justify-center">
<Clock className="h-6 w-6 text-green-600" />
</div>
</div>
</Card>
<Card className="p-6">
<div className="flex items-center justify-between">
<div>
<p className="text-sm font-medium text-gray-600">Duración Promedio</p>
<p className="text-3xl font-bold text-purple-600">{trafficData.averageVisitDuration}</p>
</div>
<div className="h-12 w-12 bg-purple-100 rounded-full flex items-center justify-center">
<Clock className="h-6 w-6 text-purple-600" />
</div>
</div>
</Card>
<Card className="p-6">
<div className="flex items-center justify-between">
<div>
<p className="text-sm font-medium text-gray-600">Conversión</p>
<p className="text-3xl font-bold text-orange-600">{trafficData.conversionRate}%</p>
</div>
<div className="h-12 w-12 bg-orange-100 rounded-full flex items-center justify-center">
<TrendingUp className="h-6 w-6 text-orange-600" />
</div>
</div>
</Card>
<Card className="p-6">
<div className="flex items-center justify-between">
<div>
<p className="text-sm font-medium text-gray-600">Días Ocupados</p>
<p className="text-sm font-bold text-red-600">{trafficData.busyDays.join(', ')}</p>
</div>
<div className="h-12 w-12 bg-red-100 rounded-full flex items-center justify-center">
<Calendar className="h-6 w-6 text-red-600" />
</div>
</div>
</Card>
</div>
{/* Controls */}
<Card className="p-6">
<div className="flex flex-col sm:flex-row gap-4">
<div>
<label className="block text-sm font-medium text-gray-700 mb-2">Período</label>
<select
value={selectedPeriod}
onChange={(e) => setSelectedPeriod(e.target.value)}
className="px-3 py-2 border border-gray-300 rounded-md"
>
<option value="day">Hoy</option>
<option value="week">Esta Semana</option>
<option value="month">Este Mes</option>
<option value="year">Este Año</option>
</select>
</div>
<div>
<label className="block text-sm font-medium text-gray-700 mb-2">Métrica</label>
<select
value={selectedMetric}
onChange={(e) => setSelectedMetric(e.target.value)}
className="px-3 py-2 border border-gray-300 rounded-md"
>
<option value="visitors">Visitantes</option>
<option value="sales">Ventas</option>
<option value="duration">Duración</option>
<option value="conversion">Conversión</option>
</select>
</div>
</div>
</Card>
<div className="grid grid-cols-1 lg:grid-cols-2 gap-6">
{/* Hourly Traffic */}
<Card className="p-6">
<h3 className="text-lg font-semibold text-gray-900 mb-4">Tráfico por Hora</h3>
<div className="h-64 flex items-end space-x-1 justify-between">
{hourlyTraffic.map((data, index) => (
<div key={index} className="flex flex-col items-center flex-1">
<div className="text-xs text-gray-600 mb-1">{data.visitors}</div>
<div
className="w-full bg-blue-500 rounded-t"
style={{
height: `${(data.visitors / maxVisitors) * 200}px`,
minHeight: '4px'
}}
></div>
<span className="text-xs text-gray-500 mt-2 transform -rotate-45 origin-center">
{data.hour}
</span>
</div>
))}
</div>
</Card>
{/* Daily Traffic */}
<Card className="p-6">
<h3 className="text-lg font-semibold text-gray-900 mb-4">Tráfico Semanal</h3>
<div className="h-64 flex items-end space-x-2 justify-between">
{dailyTraffic.map((data, index) => (
<div key={index} className="flex flex-col items-center flex-1">
<div className="text-xs text-gray-600 mb-1">{data.visitors}</div>
<div
className="w-full bg-green-500 rounded-t"
style={{
height: `${(data.visitors / maxDailyVisitors) * 200}px`,
minHeight: '8px'
}}
></div>
<span className="text-sm text-gray-700 mt-2 font-medium">
{data.day}
</span>
<div className="text-xs text-gray-500 mt-1">
{data.conversion}%
</div>
</div>
))}
</div>
</Card>
{/* Traffic Sources */}
<Card className="p-6">
<h3 className="text-lg font-semibold text-gray-900 mb-4">Fuentes de Tráfico</h3>
<div className="space-y-3">
{trafficSources.map((source, index) => (
<div key={index} className="flex items-center justify-between p-3 bg-gray-50 rounded-lg">
<div className="flex items-center space-x-3">
<div className="w-3 h-3 bg-blue-500 rounded-full"></div>
<div>
<p className="text-sm font-medium text-gray-900">{source.source}</p>
<p className="text-xs text-gray-500">{source.visitors} visitantes</p>
</div>
</div>
<div className="text-right">
<p className="text-sm font-medium text-gray-900">{source.percentage}%</p>
<div className={`text-xs flex items-center ${getTrendColor(source.trend)}`}>
<span>{getTrendIcon(source.trend)} {Math.abs(source.trend).toFixed(1)}%</span>
</div>
</div>
</div>
))}
</div>
</Card>
{/* Customer Segments */}
<Card className="p-6">
<h3 className="text-lg font-semibold text-gray-900 mb-4">Segmentos de Clientes</h3>
<div className="space-y-4">
{customerSegments.map((segment, index) => (
<div key={index} className="border rounded-lg p-4">
<div className="flex items-center justify-between mb-2">
<h4 className="font-medium text-gray-900">{segment.segment}</h4>
<Badge variant="blue">{segment.percentage}%</Badge>
</div>
<div className="grid grid-cols-2 gap-4 text-sm">
<div>
<p className="text-gray-500">Clientes</p>
<p className="font-medium">{segment.count}</p>
</div>
<div>
<p className="text-gray-500">Gasto Promedio</p>
<p className="font-medium">€{segment.avgSpend}</p>
</div>
<div>
<p className="text-gray-500">Horario Pico</p>
<p className="font-medium">{segment.peakHours.join(', ')}</p>
</div>
<div>
<p className="text-gray-500">Frecuencia</p>
<p className="font-medium">{segment.frequency}</p>
</div>
</div>
</div>
))}
</div>
</Card>
</div>
{/* Traffic Heat Map placeholder */}
<Card className="p-6">
<h3 className="text-lg font-semibold text-gray-900 mb-4">Mapa de Calor - Zonas de la Panadería</h3>
<div className="h-64 bg-gray-100 rounded-lg flex items-center justify-center">
<div className="text-center">
<MapPin className="h-12 w-12 text-gray-400 mx-auto mb-4" />
<p className="text-gray-600">Visualización de zonas de mayor tráfico</p>
<p className="text-sm text-gray-500 mt-1">Entrada: 45% • Mostrador: 32% • Zona sentada: 23%</p>
</div>
</div>
</Card>
</div>
);
};
export default TrafficPage;