ResultsDisplay.tsx 14,1 ko
Newer Older
/**
 * Results display component - shows simulation results, analytical results, and comparison.
 */
import { useState } from 'react';
import { useSimulationStore } from '../../store/simulationStore';
import MetricsCard from './MetricsCard';
import ComparisonTable from './ComparisonTable';
import NetworkDiagram from '../visualization/NetworkDiagram';
import UtilizationChart from '../visualization/UtilizationChart';
import MetricsComparisonChart from '../visualization/MetricsComparisonChart';

export default function ResultsDisplay() {
  const {
    simulationResults,
    analyticalResults,
    comparisonResults,
    isLoading,
  } = useSimulationStore();

  const [activeTab, setActiveTab] = useState<'metrics' | 'visualizations' | 'comparison'>('metrics');

  if (isLoading) {
    return (
      <div className="flex items-center justify-center py-12">
        <div className="text-center">
          <svg
            className="animate-spin h-10 w-10 text-blue-600 mx-auto mb-4"
            fill="none"
            viewBox="0 0 24 24"
          >
            <circle
              className="opacity-25"
              cx="12"
              cy="12"
              r="10"
              stroke="currentColor"
              strokeWidth="4"
            />
            <path
              className="opacity-75"
              fill="currentColor"
              d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z"
            />
          </svg>
          <p className="text-gray-600">Calcul en cours...</p>
        </div>
      </div>
    );
  }

  if (!simulationResults && !analyticalResults) {
    return (
      <div className="text-center py-12">
        <svg
          className="mx-auto h-12 w-12 text-gray-400 mb-4"
          fill="none"
          viewBox="0 0 24 24"
          stroke="currentColor"
        >
          <path
            strokeLinecap="round"
            strokeLinejoin="round"
            strokeWidth={2}
            d="M9 19v-6a2 2 0 00-2-2H5a2 2 0 00-2 2v6a2 2 0 002 2h2a2 2 0 002-2zm0 0V9a2 2 0 012-2h2a2 2 0 012 2v10m-6 0a2 2 0 002 2h2a2 2 0 002-2m0 0V5a2 2 0 012-2h2a2 2 0 012 2v14a2 2 0 01-2 2h-2a2 2 0 01-2-2z"
          />
        </svg>
        <h3 className="text-lg font-medium text-gray-900 mb-2">
          Aucun résultat disponible
        </h3>
        <p className="text-gray-600">
          Lancez une simulation pour voir les résultats
        </p>
      </div>
    );
  }

  return (
    <div className="space-y-6">
      {/* Tabs */}
      <div className="border-b border-gray-200">
        <nav className="-mb-px flex space-x-8">
          <button
            onClick={() => setActiveTab('metrics')}
            className={`py-2 px-1 border-b-2 font-medium text-sm transition-colors ${
              activeTab === 'metrics'
                ? 'border-blue-500 text-blue-600'
                : 'border-transparent text-gray-500 hover:text-gray-700 hover:border-gray-300'
            }`}
          >
            Métriques
          </button>
          <button
            onClick={() => setActiveTab('visualizations')}
            className={`py-2 px-1 border-b-2 font-medium text-sm transition-colors ${
              activeTab === 'visualizations'
                ? 'border-blue-500 text-blue-600'
                : 'border-transparent text-gray-500 hover:text-gray-700 hover:border-gray-300'
            }`}
          >
            Visualisations
          </button>
          <button
            onClick={() => setActiveTab('comparison')}
            className={`py-2 px-1 border-b-2 font-medium text-sm transition-colors ${
              activeTab === 'comparison'
                ? 'border-blue-500 text-blue-600'
                : 'border-transparent text-gray-500 hover:text-gray-700 hover:border-gray-300'
            }`}
          >
            Comparaison
          </button>
        </nav>
      </div>

      {/* Stability indicator */}
      {(simulationResults || analyticalResults) && (
        <div
          className={`p-4 rounded-md border ${
            (simulationResults?.is_stable ?? analyticalResults?.is_stable)
              ? 'bg-green-50 border-green-200'
              : 'bg-red-50 border-red-200'
          }`}
        >
          <div className="flex items-center">
            <div
              className={`flex-shrink-0 w-3 h-3 rounded-full mr-3 ${
                (simulationResults?.is_stable ?? analyticalResults?.is_stable)
                  ? 'bg-green-500'
                  : 'bg-red-500'
              }`}
            />
            <div>
              <h3
                className={`text-sm font-medium ${
                  (simulationResults?.is_stable ?? analyticalResults?.is_stable)
                    ? 'text-green-900'
                    : 'text-red-900'
                }`}
              >
                {(simulationResults?.is_stable ?? analyticalResults?.is_stable)
                  ? 'Système stable'
                  : 'Système instable'}
              </h3>
              {!simulationResults?.is_stable && simulationResults?.stability_notes && (
                <p className="text-sm text-red-700 mt-1">
                  {simulationResults.stability_notes}
                </p>
              )}
              {!analyticalResults?.is_stable && analyticalResults?.instability_reason && (
                <p className="text-sm text-red-700 mt-1">
                  {analyticalResults.instability_reason}
                </p>
              )}
            </div>
          </div>
        </div>
      )}

      {/* Metrics Tab */}
      {activeTab === 'metrics' && (
        <div className="space-y-6">

      {/* Simulation results */}
      {simulationResults && (
        <div>
          <h3 className="text-base font-semibold text-gray-900 mb-3">
            Résultats de simulation
          </h3>
          <div className="grid grid-cols-1 md:grid-cols-2 gap-4 mb-4">
            <MetricsCard
              title="Total requêtes"
              value={simulationResults.total_requests_completed}
              unit="requêtes"
            />
            <MetricsCard
              title="Temps moyen dans le système"
              value={simulationResults.average_system_time}
              unit="unités de temps"
              decimals={2}
            />
          </div>

          {/* Coordinator stats */}
          <div className="mb-4">
            <h4 className="text-sm font-medium text-gray-700 mb-2">
              Coordinateur
            </h4>
            <div className="grid grid-cols-2 md:grid-cols-4 gap-3">
              <MetricsCard
                title="Utilisation (ρ)"
                value={simulationResults.coordinator_stats.utilization}
                decimals={4}
                compact
              />
              <MetricsCard
                title="Temps d'attente"
                value={simulationResults.coordinator_stats.average_wait_time}
                decimals={2}
                compact
              />
              <MetricsCard
                title="Temps de service"
                value={simulationResults.coordinator_stats.average_service_time}
                decimals={2}
                compact
              />
              <MetricsCard
                title="Temps système"
                value={simulationResults.coordinator_stats.average_system_time}
                decimals={2}
                compact
              />
            </div>
          </div>

          {/* Server stats */}
          {Object.entries(simulationResults.server_stats).map(([serverId, stats]) => (
            <div key={serverId} className="mb-4">
              <h4 className="text-sm font-medium text-gray-700 mb-2">
                {serverId.replace('_', ' ').charAt(0).toUpperCase() +
                  serverId.replace('_', ' ').slice(1)}
              </h4>
              <div className="grid grid-cols-2 md:grid-cols-4 gap-3">
                <MetricsCard
                  title="Utilisation (ρ)"
                  value={stats.utilization}
                  decimals={4}
                  compact
                />
                <MetricsCard
                  title="Temps d'attente"
                  value={stats.average_wait_time}
                  decimals={2}
                  compact
                />
                <MetricsCard
                  title="Temps de service"
                  value={stats.average_service_time}
                  decimals={2}
                  compact
                />
                <MetricsCard
                  title="Temps système"
                  value={stats.average_system_time}
                  decimals={2}
                  compact
                />
              </div>
            </div>
          ))}
        </div>
      )}

      {/* Analytical results */}
      {analyticalResults && (
        <div>
          <h3 className="text-base font-semibold text-gray-900 mb-3">
            Résultats analytiques (Théorème de Jackson)
          </h3>
          <div className="grid grid-cols-1 md:grid-cols-2 gap-4 mb-4">
            <MetricsCard
              title="Nombre moyen de clients (L)"
              value={analyticalResults.total_average_customers}
              decimals={4}
            />
            <MetricsCard
              title="Temps moyen (W)"
              value={analyticalResults.total_average_time}
              decimals={4}
              unit="unités de temps"
            />
          </div>

          {/* Coordinator analytics */}
          <div className="mb-4">
            <h4 className="text-sm font-medium text-gray-700 mb-2">
              Coordinateur
            </h4>
            <div className="grid grid-cols-2 md:grid-cols-4 gap-3">
              <MetricsCard
                title="Utilisation (ρ)"
                value={analyticalResults.coordinator.utilization}
                decimals={4}
                compact
              />
              <MetricsCard
                title="L"
                value={analyticalResults.coordinator.average_customers ?? 0}
                decimals={4}
                compact
              />
              <MetricsCard
                title="W"
                value={analyticalResults.coordinator.average_time ?? 0}
                decimals={4}
                compact
              />
              <MetricsCard
                title="Wq"
                value={analyticalResults.coordinator.average_wait_time ?? 0}
                decimals={4}
                compact
              />
            </div>
          </div>

          {/* Server analytics */}
          {Object.entries(analyticalResults.servers).map(([serverId, analytics]) => (
            <div key={serverId} className="mb-4">
              <h4 className="text-sm font-medium text-gray-700 mb-2">
                {serverId.replace('_', ' ').charAt(0).toUpperCase() +
                  serverId.replace('_', ' ').slice(1)}
              </h4>
              <div className="grid grid-cols-2 md:grid-cols-4 gap-3">
                <MetricsCard
                  title="Utilisation (ρ)"
                  value={analytics.utilization}
                  decimals={4}
                  compact
                />
                <MetricsCard
                  title="L"
                  value={analytics.average_customers ?? 0}
                  decimals={4}
                  compact
                />
                <MetricsCard
                  title="W"
                  value={analytics.average_time ?? 0}
                  decimals={4}
                  compact
                />
                <MetricsCard
                  title="Wq"
                  value={analytics.average_wait_time ?? 0}
                  decimals={4}
                  compact
                />
              </div>
            </div>
          ))}
        </div>
      )}
        </div>
      )}

      {/* Visualizations Tab */}
      {activeTab === 'visualizations' && (
        <div className="space-y-6">
          {/* Network diagram */}
          {config && (
            <div>
              <NetworkDiagram config={config} />
            </div>
          )}

          {/* Utilization chart */}
          {(simulationResults || analyticalResults) && (
            <div>
              <UtilizationChart
                simulationResults={simulationResults}
                analyticalResults={analyticalResults}
              />
            </div>
          )}

          {/* Metrics charts */}
          <div className="grid grid-cols-1 lg:grid-cols-2 gap-6">
            {analyticalResults && (
              <MetricsComparisonChart
                simulationResults={simulationResults}
                analyticalResults={analyticalResults}
                metric="L"
              />
            )}
            {(simulationResults || analyticalResults) && (
              <MetricsComparisonChart
                simulationResults={simulationResults}
                analyticalResults={analyticalResults}
                metric="W"
              />
            )}
          </div>
        </div>
      )}

      {/* Comparison Tab */}
      {activeTab === 'comparison' && (
        <div className="space-y-6">
          {comparisonResults ? (
            <>
              <div>
                <h3 className="text-base font-semibold text-gray-900 mb-3">
                  Comparaison Analytique vs Simulation
                </h3>
                <ComparisonTable comparison={comparisonResults} />
              </div>

              {/* Visualization of comparison */}
              <div className="grid grid-cols-1 lg:grid-cols-2 gap-6">
                <UtilizationChart
                  simulationResults={simulationResults}
                  analyticalResults={analyticalResults}
                />
                <MetricsComparisonChart
                  simulationResults={simulationResults}
                  analyticalResults={analyticalResults}
                  metric="W"
                />
              </div>
            </>
          ) : (
            <div className="text-center py-12">
              <p className="text-gray-600">
                Lancez une simulation pour voir la comparaison avec les résultats analytiques
              </p>
            </div>
          )}