Improve the frontend 4
This commit is contained in:
@@ -12,11 +12,21 @@ import {
|
||||
Truck,
|
||||
Calendar
|
||||
} from 'lucide-react';
|
||||
import {
|
||||
LineChart,
|
||||
Line,
|
||||
XAxis,
|
||||
YAxis,
|
||||
CartesianGrid,
|
||||
Tooltip,
|
||||
ResponsiveContainer,
|
||||
Legend
|
||||
} from 'recharts';
|
||||
import { PageHeader } from '../../../components/layout';
|
||||
import { Card, StatsGrid, Button, Tabs } from '../../../components/ui';
|
||||
import { useSubscription } from '../../../api/hooks/subscription';
|
||||
import { useCurrentTenant } from '../../../stores/tenant.store';
|
||||
import { useProcurementDashboard } from '../../../api/hooks/orders';
|
||||
import { useProcurementDashboard, useProcurementTrends } from '../../../api/hooks/procurement';
|
||||
import { formatters } from '../../../components/ui/Stats/StatsPresets';
|
||||
|
||||
const ProcurementAnalyticsPage: React.FC = () => {
|
||||
@@ -27,6 +37,7 @@ const ProcurementAnalyticsPage: React.FC = () => {
|
||||
const [activeTab, setActiveTab] = useState('overview');
|
||||
|
||||
const { data: dashboard, isLoading: dashboardLoading } = useProcurementDashboard(tenantId);
|
||||
const { data: trends, isLoading: trendsLoading } = useProcurementTrends(tenantId, 7);
|
||||
|
||||
// Check if user has access to advanced analytics (professional/enterprise)
|
||||
const hasAdvancedAccess = canAccessAnalytics('advanced');
|
||||
@@ -162,32 +173,32 @@ const ProcurementAnalyticsPage: React.FC = () => {
|
||||
<>
|
||||
{/* Overview Tab */}
|
||||
<div className="grid grid-cols-1 md:grid-cols-2 gap-6">
|
||||
{/* Plan Status Distribution */}
|
||||
<Card>
|
||||
<div className="p-6">
|
||||
<h3 className="text-lg font-medium text-[var(--text-primary)] mb-4">
|
||||
Distribución de Estados de Planes
|
||||
</h3>
|
||||
<div className="space-y-3">
|
||||
{dashboard?.plan_status_distribution?.map((status: any) => (
|
||||
<div key={status.status} className="flex items-center justify-between">
|
||||
<span className="text-[var(--text-secondary)]">{status.status}</span>
|
||||
<div className="flex items-center gap-2">
|
||||
<div className="w-32 h-2 bg-[var(--bg-tertiary)] rounded-full overflow-hidden">
|
||||
<div
|
||||
className="h-full bg-[var(--color-primary)]"
|
||||
style={{ width: `${(status.count / (dashboard?.summary?.total_plans || 1)) * 100}%` }}
|
||||
/>
|
||||
</div>
|
||||
<span className="text-sm font-medium text-[var(--text-primary)] w-8 text-right">
|
||||
{status.count}
|
||||
</span>
|
||||
{/* Plan Status Distribution */}
|
||||
<Card>
|
||||
<div className="p-6">
|
||||
<h3 className="text-lg font-medium text-[var(--text-primary)] mb-4">
|
||||
Distribución de Estados de Planes
|
||||
</h3>
|
||||
<div className="space-y-3">
|
||||
{dashboard?.plan_status_distribution?.map((status: any) => (
|
||||
<div key={status.status} className="flex items-center justify-between">
|
||||
<span className="text-[var(--text-secondary)]">{status.status}</span>
|
||||
<div className="flex items-center gap-2">
|
||||
<div className="w-32 h-2 bg-[var(--bg-tertiary)] rounded-full overflow-hidden">
|
||||
<div
|
||||
className="h-full bg-[var(--color-primary)]"
|
||||
style={{ width: `${(status.count / (dashboard?.summary?.total_plans || 1)) * 100}%` }}
|
||||
/>
|
||||
</div>
|
||||
<span className="text-sm font-medium text-[var(--text-primary)] w-8 text-right">
|
||||
{status.count}
|
||||
</span>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</Card>
|
||||
</div>
|
||||
</Card>
|
||||
|
||||
{/* Critical Requirements */}
|
||||
<Card>
|
||||
@@ -302,15 +313,63 @@ const ProcurementAnalyticsPage: React.FC = () => {
|
||||
</Card>
|
||||
</div>
|
||||
|
||||
{/* Performance Trend Chart Placeholder */}
|
||||
{/* Performance Trend Chart */}
|
||||
<Card>
|
||||
<div className="p-6">
|
||||
<h3 className="text-lg font-medium text-[var(--text-primary)] mb-4">
|
||||
Tendencias de Rendimiento
|
||||
Tendencias de Rendimiento (Últimos 7 días)
|
||||
</h3>
|
||||
<div className="h-64 flex items-center justify-center text-[var(--text-tertiary)]">
|
||||
Gráfico de tendencias - Próximamente
|
||||
</div>
|
||||
{trendsLoading ? (
|
||||
<div className="h-64 flex items-center justify-center">
|
||||
<div className="animate-spin rounded-full h-8 w-8 border-b-2 border-[var(--color-primary)]"></div>
|
||||
</div>
|
||||
) : trends && trends.performance_trend && trends.performance_trend.length > 0 ? (
|
||||
<ResponsiveContainer width="100%" height={300}>
|
||||
<LineChart data={trends.performance_trend}>
|
||||
<CartesianGrid strokeDasharray="3 3" stroke="var(--border-primary)" />
|
||||
<XAxis
|
||||
dataKey="date"
|
||||
stroke="var(--text-tertiary)"
|
||||
tick={{ fill: 'var(--text-secondary)' }}
|
||||
/>
|
||||
<YAxis
|
||||
stroke="var(--text-tertiary)"
|
||||
tick={{ fill: 'var(--text-secondary)' }}
|
||||
tickFormatter={(value) => `${(value * 100).toFixed(0)}%`}
|
||||
/>
|
||||
<Tooltip
|
||||
contentStyle={{
|
||||
backgroundColor: 'var(--bg-primary)',
|
||||
border: '1px solid var(--border-primary)',
|
||||
borderRadius: '8px'
|
||||
}}
|
||||
formatter={(value: any) => `${(value * 100).toFixed(1)}%`}
|
||||
labelStyle={{ color: 'var(--text-primary)' }}
|
||||
/>
|
||||
<Legend />
|
||||
<Line
|
||||
type="monotone"
|
||||
dataKey="fulfillment_rate"
|
||||
stroke="var(--color-success)"
|
||||
strokeWidth={2}
|
||||
name="Tasa de Cumplimiento"
|
||||
dot={{ fill: 'var(--color-success)' }}
|
||||
/>
|
||||
<Line
|
||||
type="monotone"
|
||||
dataKey="on_time_rate"
|
||||
stroke="var(--color-info)"
|
||||
strokeWidth={2}
|
||||
name="Entregas a Tiempo"
|
||||
dot={{ fill: 'var(--color-info)' }}
|
||||
/>
|
||||
</LineChart>
|
||||
</ResponsiveContainer>
|
||||
) : (
|
||||
<div className="h-64 flex items-center justify-center text-[var(--text-tertiary)]">
|
||||
No hay datos de tendencias disponibles
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</Card>
|
||||
</>
|
||||
@@ -459,11 +518,51 @@ const ProcurementAnalyticsPage: React.FC = () => {
|
||||
<Card>
|
||||
<div className="p-6">
|
||||
<h3 className="text-lg font-medium text-[var(--text-primary)] mb-4">
|
||||
Tendencia de Calidad
|
||||
Tendencia de Calidad (Últimos 7 días)
|
||||
</h3>
|
||||
<div className="h-48 flex items-center justify-center text-[var(--text-tertiary)]">
|
||||
Gráfico de tendencia de calidad - Próximamente
|
||||
</div>
|
||||
{trendsLoading ? (
|
||||
<div className="h-48 flex items-center justify-center">
|
||||
<div className="animate-spin rounded-full h-8 w-8 border-b-2 border-[var(--color-primary)]"></div>
|
||||
</div>
|
||||
) : trends && trends.quality_trend && trends.quality_trend.length > 0 ? (
|
||||
<ResponsiveContainer width="100%" height={200}>
|
||||
<LineChart data={trends.quality_trend}>
|
||||
<CartesianGrid strokeDasharray="3 3" stroke="var(--border-primary)" />
|
||||
<XAxis
|
||||
dataKey="date"
|
||||
stroke="var(--text-tertiary)"
|
||||
tick={{ fill: 'var(--text-secondary)' }}
|
||||
/>
|
||||
<YAxis
|
||||
stroke="var(--text-tertiary)"
|
||||
tick={{ fill: 'var(--text-secondary)' }}
|
||||
domain={[0, 10]}
|
||||
ticks={[0, 2, 4, 6, 8, 10]}
|
||||
/>
|
||||
<Tooltip
|
||||
contentStyle={{
|
||||
backgroundColor: 'var(--bg-primary)',
|
||||
border: '1px solid var(--border-primary)',
|
||||
borderRadius: '8px'
|
||||
}}
|
||||
formatter={(value: any) => `${value.toFixed(1)} / 10`}
|
||||
labelStyle={{ color: 'var(--text-primary)' }}
|
||||
/>
|
||||
<Line
|
||||
type="monotone"
|
||||
dataKey="quality_score"
|
||||
stroke="var(--color-warning)"
|
||||
strokeWidth={2}
|
||||
name="Puntuación de Calidad"
|
||||
dot={{ fill: 'var(--color-warning)' }}
|
||||
/>
|
||||
</LineChart>
|
||||
</ResponsiveContainer>
|
||||
) : (
|
||||
<div className="h-48 flex items-center justify-center text-[var(--text-tertiary)]">
|
||||
No hay datos de calidad disponibles
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</Card>
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user