ComparisonTable.tsx 5,62 ko
Newer Older
/**
 * Comparison table component - displays analytical vs simulation comparison.
 */
import type { NetworkComparison, QueueComparison } from '../../types/simulation';

interface ComparisonTableProps {
  comparison: NetworkComparison;
}

interface ComparisonRowProps {
  label: string;
  analytical: number;
  simulation: number;
  diffPercent: number;
  decimals?: number;
}

function ComparisonRow({
  label,
  analytical,
  simulation,
  diffPercent,
  decimals = 4,
}: ComparisonRowProps) {
  const absDiff = Math.abs(diffPercent);
  const isGoodMatch = absDiff < 5; // Less than 5% difference is good
  const isOkMatch = absDiff < 10; // Less than 10% is acceptable

  return (
    <tr className="border-b border-gray-200">
      <td className="py-2 px-3 text-sm text-gray-900">{label}</td>
      <td className="py-2 px-3 text-sm font-mono text-right text-gray-700">
        {analytical.toFixed(decimals)}
      </td>
      <td className="py-2 px-3 text-sm font-mono text-right text-gray-700">
        {simulation.toFixed(decimals)}
      </td>
      <td
        className={`py-2 px-3 text-sm font-mono text-right font-medium ${
          isGoodMatch
            ? 'text-green-600'
            : isOkMatch
            ? 'text-yellow-600'
            : 'text-red-600'
        }`}
      >
        {diffPercent >= 0 ? '+' : ''}
        {diffPercent.toFixed(2)}%
      </td>
    </tr>
  );
}

function QueueComparisonSection({
  queueName,
  queueComparison,
}: {
  queueName: string;
  queueComparison: QueueComparison;
}) {
  return (
    <>
      <tr className="bg-gray-100">
        <td colSpan={4} className="py-2 px-3 text-sm font-semibold text-gray-700">
          {queueName}
        </td>
      </tr>
      <ComparisonRow
        label="  Utilisation (ρ)"
        analytical={queueComparison.utilization.analytical}
        simulation={queueComparison.utilization.simulation}
        diffPercent={queueComparison.utilization.difference_percent}
      />
      <ComparisonRow
        label="  Nombre moyen (L)"
        analytical={queueComparison.average_customers.analytical}
        simulation={queueComparison.average_customers.simulation}
        diffPercent={queueComparison.average_customers.difference_percent}
      />
      <ComparisonRow
        label="  Temps moyen (W)"
        analytical={queueComparison.average_time.analytical}
        simulation={queueComparison.average_time.simulation}
        diffPercent={queueComparison.average_time.difference_percent}
      />
      <ComparisonRow
        label="  Temps d'attente (Wq)"
        analytical={queueComparison.average_wait.analytical}
        simulation={queueComparison.average_wait.simulation}
        diffPercent={queueComparison.average_wait.difference_percent}
      />
    </>
  );
}

export default function ComparisonTable({ comparison }: ComparisonTableProps) {
  return (
    <div className="overflow-x-auto">
      <table className="min-w-full divide-y divide-gray-300 bg-white border border-gray-200 rounded-lg">
        <thead className="bg-gray-50">
          <tr>
            <th className="py-3 px-3 text-left text-xs font-semibold text-gray-700 uppercase tracking-wider">
              Métrique
            </th>
            <th className="py-3 px-3 text-right text-xs font-semibold text-gray-700 uppercase tracking-wider">
              Analytique
            </th>
            <th className="py-3 px-3 text-right text-xs font-semibold text-gray-700 uppercase tracking-wider">
              Simulation
            </th>
            <th className="py-3 px-3 text-right text-xs font-semibold text-gray-700 uppercase tracking-wider">
              Différence
            </th>
          </tr>
        </thead>
        <tbody>
          {/* System totals */}
          <tr className="bg-blue-50">
            <td colSpan={4} className="py-2 px-3 text-sm font-bold text-blue-900">
              Système total
            </td>
          </tr>
          <ComparisonRow
            label="  L total"
            analytical={comparison.total_L.analytical}
            simulation={comparison.total_L.simulation}
            diffPercent={comparison.total_L.difference_percent}
          />
          <ComparisonRow
            label="  W total"
            analytical={comparison.total_W.analytical}
            simulation={comparison.total_W.simulation}
            diffPercent={comparison.total_W.difference_percent}
          />

          {/* Coordinator */}
          <QueueComparisonSection
            queueName="Coordinateur"
            queueComparison={comparison.coordinator}
          />

          {/* Servers */}
          {Object.entries(comparison.servers).map(([serverId, serverComparison]) => (
            <QueueComparisonSection
              key={serverId}
              queueName={
                serverId.replace('_', ' ').charAt(0).toUpperCase() +
                serverId.replace('_', ' ').slice(1)
              }
              queueComparison={serverComparison}
            />
          ))}
        </tbody>
      </table>

      {/* Legend */}
      <div className="mt-3 flex items-center justify-end space-x-4 text-xs">
        <div className="flex items-center">
          <div className="w-3 h-3 bg-green-600 rounded mr-1"></div>
          <span className="text-gray-600">&lt; 5% (excellent)</span>
        </div>
        <div className="flex items-center">
          <div className="w-3 h-3 bg-yellow-600 rounded mr-1"></div>
          <span className="text-gray-600">5-10% (acceptable)</span>
        </div>
        <div className="flex items-center">
          <div className="w-3 h-3 bg-red-600 rounded mr-1"></div>
          <span className="text-gray-600">&gt; 10% (important)</span>
        </div>
      </div>
    </div>
  );
}