templates/upload/review.html.twig line 1

Open in your IDE?
  1. {# templates/upload/review.html.twig #}
  2. {% extends 'admin/baseAdmin.html.twig' %}
  3. {% block title %}Validation IA - Relecture & Correction{% endblock %}
  4. {% block stylesheets %}
  5.     {{ parent() }}
  6.     <style>
  7.         :root {
  8.             --ai-primary: linear-gradient(135deg, #1e40af, #3b82f6);
  9.             --ai-success: linear-gradient(135deg, #10b981, #34d399);
  10.             --ai-warning: linear-gradient(135deg, #f59e0b, #fbbf24);
  11.             --ai-danger: linear-gradient(135deg, #dc2626, #ef4444);
  12.             --ai-info: linear-gradient(135deg, #0ea5e9, #38bdf8);
  13.             --ai-processing: linear-gradient(135deg, #8b5cf6, #a78bfa);
  14.         }
  15.         
  16.         .review-container {
  17.             background: linear-gradient(180deg, #f8fafc 0%, #f1f5f9 100%);
  18.             min-height: 100vh;
  19.         }
  20.         
  21.         .header-gradient {
  22.             background: var(--ai-primary);
  23.             color: white;
  24.             border-radius: 20px;
  25.             padding: 2rem 2.5rem;
  26.             margin-bottom: 2rem;
  27.             position: relative;
  28.             overflow: hidden;
  29.         }
  30.         
  31.         .header-gradient::before {
  32.             content: '';
  33.             position: absolute;
  34.             top: -50%;
  35.             right: -20%;
  36.             width: 300px;
  37.             height: 300px;
  38.             background: rgba(255, 255, 255, 0.1);
  39.             border-radius: 50%;
  40.             filter: blur(40px);
  41.         }
  42.         
  43.         .ai-badge {
  44.             background: linear-gradient(135deg, #8b5cf6, #a78bfa);
  45.             color: white;
  46.             padding: 0.5rem 1rem;
  47.             border-radius: 50px;
  48.             font-weight: 700;
  49.             font-size: 0.85rem;
  50.             display: inline-flex;
  51.             align-items: center;
  52.             gap: 0.5rem;
  53.         }
  54.         
  55.         .card-ai {
  56.             border: none;
  57.             border-radius: 20px;
  58.             box-shadow: 0 10px 25px -5px rgba(0, 0, 0, 0.08);
  59.             background: white;
  60.             overflow: hidden;
  61.             transition: transform 0.3s cubic-bezier(0.4, 0, 0.2, 1);
  62.         }
  63.         
  64.         .card-ai:hover {
  65.             transform: translateY(-5px);
  66.             box-shadow: 0 20px 40px -10px rgba(0, 0, 0, 0.12);
  67.         }
  68.         
  69.         .card-header-ai {
  70.             background: var(--ai-primary);
  71.             color: white;
  72.             border: none;
  73.             padding: 1.5rem 2rem;
  74.             border-radius: 20px 20px 0 0 !important;
  75.         }
  76.         
  77.         .card-header-secondary {
  78.             background: linear-gradient(135deg, #374151, #4b5563);
  79.             color: white;
  80.             border: none;
  81.             padding: 1.5rem 2rem;
  82.             border-radius: 20px 20px 0 0 !important;
  83.         }
  84.         
  85.         .stat-card-ai {
  86.             background: white;
  87.             border-radius: 16px;
  88.             padding: 1.5rem;
  89.             box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.05);
  90.             border-left: 5px solid;
  91.             height: 100%;
  92.             position: relative;
  93.             overflow: hidden;
  94.         }
  95.         
  96.         .stat-card-ai::before {
  97.             content: '';
  98.             position: absolute;
  99.             top: 0;
  100.             left: 0;
  101.             right: 0;
  102.             height: 3px;
  103.             background: inherit;
  104.             opacity: 0.3;
  105.         }
  106.         
  107.         .stat-card-ai.primary { border-left-color: #3b82f6; }
  108.         .stat-card-ai.success { border-left-color: #10b981; }
  109.         .stat-card-ai.warning { border-left-color: #f59e0b; }
  110.         .stat-card-ai.info { border-left-color: #0ea5e9; }
  111.         
  112.         .stat-number-ai {
  113.             font-size: 2.5rem;
  114.             font-weight: 900;
  115.             line-height: 1;
  116.             margin-bottom: 0.5rem;
  117.         }
  118.         
  119.         .stat-number-ai.primary { color: #3b82f6; }
  120.         .stat-number-ai.success { color: #10b981; }
  121.         .stat-number-ai.warning { color: #f59e0b; }
  122.         .stat-number-ai.info { color: #0ea5e9; }
  123.         
  124.         .stat-icon-ai {
  125.             position: absolute;
  126.             right: 1.5rem;
  127.             top: 1.5rem;
  128.             font-size: 2rem;
  129.             opacity: 0.2;
  130.         }
  131.         
  132.         .document-preview-container-ai {
  133.             border: 3px solid white;
  134.             border-radius: 16px;
  135.             overflow: hidden;
  136.             background: white;
  137.             height: 280px;
  138.             display: flex;
  139.             align-items: center;
  140.             justify-content: center;
  141.             cursor: pointer;
  142.             transition: all 0.4s cubic-bezier(0.4, 0, 0.2, 1);
  143.             box-shadow: 0 10px 30px -5px rgba(0, 0, 0, 0.1);
  144.             position: relative;
  145.         }
  146.         
  147.         .document-preview-container-ai:hover {
  148.             transform: translateY(-8px) scale(1.02);
  149.             box-shadow: 0 25px 50px -12px rgba(59, 130, 246, 0.25);
  150.             border-color: #3b82f6;
  151.         }
  152.         
  153.         .document-preview-container-ai:hover::after {
  154.             content: '🔍 Cliquez pour agrandir';
  155.             position: absolute;
  156.             bottom: 0;
  157.             left: 0;
  158.             right: 0;
  159.             background: rgba(59, 130, 246, 0.9);
  160.             color: white;
  161.             text-align: center;
  162.             padding: 0.5rem;
  163.             font-size: 0.85rem;
  164.             font-weight: 600;
  165.         }
  166.         
  167.         .document-preview-ai {
  168.             max-height: 100%;
  169.             max-width: 100%;
  170.             object-fit: contain;
  171.             transition: transform 0.4s ease;
  172.         }
  173.         
  174.         .document-preview-container-ai:hover .document-preview-ai {
  175.             transform: scale(1.05);
  176.         }
  177.         
  178.         .pdf-icon-ai {
  179.             font-size: 5rem;
  180.             color: #dc2626;
  181.             filter: drop-shadow(0 10px 15px rgba(220, 38, 38, 0.2));
  182.             transition: all 0.4s ease;
  183.         }
  184.         
  185.         .document-preview-container-ai:hover .pdf-icon-ai {
  186.             transform: rotate(-5deg) scale(1.1);
  187.             filter: drop-shadow(0 15px 25px rgba(220, 38, 38, 0.3));
  188.         }
  189.         
  190.         .ai-process-steps {
  191.             background: linear-gradient(135deg, #f0f9ff, #e0f2fe);
  192.             border-radius: 16px;
  193.             padding: 2rem;
  194.             border-left: 5px solid #0ea5e9;
  195.             position: relative;
  196.             overflow: hidden;
  197.         }
  198.         
  199.         .ai-process-steps::before {
  200.             content: 'IA';
  201.             position: absolute;
  202.             top: 1rem;
  203.             right: 1rem;
  204.             font-size: 3rem;
  205.             font-weight: 900;
  206.             color: rgba(14, 165, 233, 0.1);
  207.         }
  208.         
  209.         .process-step {
  210.             display: flex;
  211.             align-items: flex-start;
  212.             gap: 1rem;
  213.             margin-bottom: 1.5rem;
  214.             padding: 1rem;
  215.             background: white;
  216.             border-radius: 12px;
  217.             box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.05);
  218.             transition: transform 0.2s ease;
  219.         }
  220.         
  221.         .process-step:hover {
  222.             transform: translateX(5px);
  223.         }
  224.         
  225.         .step-number {
  226.             width: 36px;
  227.             height: 36px;
  228.             background: var(--ai-primary);
  229.             color: white;
  230.             border-radius: 50%;
  231.             display: flex;
  232.             align-items: center;
  233.             justify-content: center;
  234.             font-weight: 900;
  235.             flex-shrink: 0;
  236.         }
  237.         
  238.         .table-container-ai {
  239.             border-radius: 16px;
  240.             overflow: hidden;
  241.             box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.08);
  242.         }
  243.         
  244.         .table-header-ai {
  245.             background: linear-gradient(135deg, #374151, #4b5563);
  246.             color: white;
  247.             border: none;
  248.             position: sticky;
  249.             top: 0;
  250.             z-index: 10;
  251.         }
  252.         
  253.         .table-header-ai th {
  254.             border: none;
  255.             font-weight: 700;
  256.             padding: 1.25rem 1rem;
  257.             text-transform: uppercase;
  258.             letter-spacing: 0.5px;
  259.             font-size: 0.85rem;
  260.         }
  261.         
  262.         .table-subheader-ai {
  263.             background: #f3f4f6;
  264.             border-bottom: 2px solid #e5e7eb;
  265.         }
  266.         
  267.         .table-subheader-ai th {
  268.             font-size: 0.8rem;
  269.             color: #6b7280;
  270.             font-weight: 600;
  271.             padding: 0.75rem 1rem;
  272.         }
  273.         
  274.         .table-row-ai {
  275.             background: white;
  276.             border-bottom: 1px solid #f3f4f6;
  277.             transition: all 0.2s ease;
  278.         }
  279.         
  280.         .table-row-ai:hover {
  281.             background: #f9fafb;
  282.             transform: translateY(-1px);
  283.             box-shadow: 0 2px 4px rgba(0, 0, 0, 0.05);
  284.             z-index: 1;
  285.             position: relative;
  286.         }
  287.         
  288.         .table-cell-ai {
  289.             padding: 1rem;
  290.             vertical-align: middle;
  291.             border: none;
  292.         }
  293.         
  294.         .form-control-ai {
  295.             border: 2px solid #e5e7eb;
  296.             border-radius: 12px;
  297.             padding: 0.75rem 1rem;
  298.             font-size: 0.95rem;
  299.             transition: all 0.2s ease;
  300.             background: white;
  301.         }
  302.         
  303.         .form-control-ai:focus {
  304.             border-color: #3b82f6;
  305.             box-shadow: 0 0 0 3px rgba(59, 130, 246, 0.1);
  306.             transform: translateY(-1px);
  307.         }
  308.         
  309.         .time-cell-ai {
  310.             background: #f8fafc;
  311.             min-width: 100px;
  312.         }
  313.         
  314.         .confidence-indicator {
  315.             display: inline-flex;
  316.             align-items: center;
  317.             gap: 0.5rem;
  318.             padding: 0.4rem 0.8rem;
  319.             border-radius: 50px;
  320.             font-size: 0.8rem;
  321.             font-weight: 700;
  322.         }
  323.         
  324.         .confidence-high { background: #d1fae5; color: #065f46; }
  325.         .confidence-medium { background: #fef3c7; color: #92400e; }
  326.         .confidence-low { background: #fee2e2; color: #991b1b; }
  327.         
  328.         .btn-ai-primary {
  329.             background: var(--ai-primary);
  330.             color: white;
  331.             border: none;
  332.             padding: 0.875rem 2rem;
  333.             border-radius: 12px;
  334.             font-weight: 700;
  335.             display: inline-flex;
  336.             align-items: center;
  337.             gap: 0.75rem;
  338.             transition: all 0.3s ease;
  339.         }
  340.         
  341.         .btn-ai-primary:hover {
  342.             transform: translateY(-2px);
  343.             box-shadow: 0 10px 25px -5px rgba(59, 130, 246, 0.3);
  344.             color: white;
  345.         }
  346.         
  347.         .btn-ai-success {
  348.             background: var(--ai-success);
  349.             color: white;
  350.             border: none;
  351.             padding: 0.875rem 2rem;
  352.             border-radius: 12px;
  353.             font-weight: 700;
  354.             display: inline-flex;
  355.             align-items: center;
  356.             gap: 0.75rem;
  357.             transition: all 0.3s ease;
  358.         }
  359.         
  360.         .btn-ai-success:hover {
  361.             transform: translateY(-2px);
  362.             box-shadow: 0 10px 25px -5px rgba(16, 185, 129, 0.3);
  363.             color: white;
  364.         }
  365.         
  366.         .btn-ai-outline {
  367.             border: 2px solid #e5e7eb;
  368.             background: white;
  369.             color: #374151;
  370.             padding: 0.875rem 2rem;
  371.             border-radius: 12px;
  372.             font-weight: 700;
  373.             display: inline-flex;
  374.             align-items: center;
  375.             gap: 0.75rem;
  376.             transition: all 0.3s ease;
  377.         }
  378.         
  379.         .btn-ai-outline:hover {
  380.             border-color: #3b82f6;
  381.             color: #3b82f6;
  382.             transform: translateY(-2px);
  383.             box-shadow: 0 10px 25px -5px rgba(0, 0, 0, 0.1);
  384.         }
  385.         
  386.         .ai-validation-alert {
  387.             background: linear-gradient(135deg, #fef3c7, #fde68a);
  388.             border: none;
  389.             border-left: 5px solid #f59e0b;
  390.             border-radius: 12px;
  391.             padding: 1.5rem;
  392.         }
  393.         
  394.         .extraction-highlight {
  395.             background: linear-gradient(120deg, #dbeafe 0%, #dbeafe 100%);
  396.             animation: highlightPulse 2s ease-in-out infinite;
  397.             border-radius: 8px;
  398.             padding: 2px 4px;
  399.             font-weight: 600;
  400.         }
  401.         
  402.         @keyframes highlightPulse {
  403.             0%, 100% { opacity: 1; }
  404.             50% { opacity: 0.8; }
  405.         }
  406.         
  407.         .modal-fullscreen-ai .modal-content {
  408.             border-radius: 0;
  409.             background: #1f2937;
  410.         }
  411.         
  412.         .modal-header-ai {
  413.             background: #374151;
  414.             border-bottom: 1px solid #4b5563;
  415.         }
  416.         
  417.         .modal-title-ai {
  418.             color: white;
  419.             font-weight: 700;
  420.         }
  421.         
  422.         .ai-correction-tag {
  423.             position: absolute;
  424.             top: -10px;
  425.             right: 20px;
  426.             background: #dc2626;
  427.             color: white;
  428.             padding: 0.5rem 1rem;
  429.             border-radius: 0 0 10px 10px;
  430.             font-size: 0.75rem;
  431.             font-weight: 700;
  432.             z-index: 10;
  433.         }
  434.         
  435.         @media (max-width: 768px) {
  436.             .header-gradient {
  437.                 padding: 1.5rem;
  438.                 border-radius: 16px;
  439.             }
  440.             
  441.             .stat-number-ai {
  442.                 font-size: 2rem;
  443.             }
  444.             
  445.             .document-preview-container-ai {
  446.                 height: 220px;
  447.             }
  448.             
  449.             .table-responsive {
  450.                 font-size: 0.85rem;
  451.             }
  452.         }
  453.     </style>
  454. {% endblock %}
  455. {% block body %}
  456.     <div class="container-fluid py-4 review-container">
  457.         
  458.         <!-- En-tête avec IA en vedette -->
  459.         <div class="header-gradient">
  460.             <div class="row align-items-center">
  461.                 <div class="col-md-8">
  462.                     <div class="d-flex align-items-center gap-3 mb-3">
  463.                         <h1 class="h2 fw-bold mb-0">🔄 Validation IA</h1>
  464.                         <span class="ai-badge">
  465.                             <i class="fas fa-robot"></i>
  466.                             Extraction automatisée
  467.                         </span>
  468.                         {# {% if declaration.extractionConfidence %}
  469.                             <span class="confidence-indicator confidence-{{ declaration.extractionConfidence > 80 ? 'high' : declaration.extractionConfidence > 60 ? 'medium' : 'low' }}">
  470.                                 <i class="fas fa-{{ declaration.extractionConfidence > 80 ? 'chart-line' : declaration.extractionConfidence > 60 ? 'chart-bar' : 'exclamation-triangle' }}"></i>
  471.                                 Fiabilité : {{ declaration.extractionConfidence }}%
  472.                             </span>
  473.                         {% endif %} #}
  474.                     </div>
  475.                     <p class="mb-0 opacity-90 fs-5">Relecture & correction des données extraites automatiquement par notre IA</p>
  476.                 </div>
  477.                 <div class="col-md-4 text-md-end mt-3 mt-md-0">
  478.                     <div class="d-flex flex-column flex-md-row gap-2 justify-content-md-end">
  479.                         <span class="info-badge   bg-opacity-20 text-white d-inline-flex align-items-center">
  480.                             <i class="fas fa-file me-2"></i>
  481.                             {{ batch.originalFilename|length > 25 ? batch.originalFilename[:25] ~ '...' : batch.originalFilename }}
  482.                         </span>
  483.                         <a href="{{ path('upload_list') }}" class="btn btn-light btn-sm px-3">
  484.                             <i class="fas fa-arrow-left me-2"></i>Retour
  485.                         </a>
  486.                     </div>
  487.                 </div>
  488.             </div>
  489.         </div>
  490.         <!-- Cartes statistiques IA -->
  491.         <div class="row mb-5">
  492.             <div class="col-xl-3 col-md-6 mb-4">
  493.                 <div class="stat-card-ai primary">
  494.                     <i class="fas fa-list-ul stat-icon-ai primary"></i>
  495.                     <div class="stat-number-ai primary">{{ ligneCount }}</div>
  496.                     <div class="stat-label text-muted mb-2">Lignes détectées</div>
  497.                     <div class="small text-success">
  498.                         <i class="fas fa-check-circle me-1"></i>
  499.                         {{ declaration.correctLines|default(ligneCount) }} correctes
  500.                     </div>
  501.                 </div>
  502.             </div>
  503.             
  504.             <div class="col-xl-3 col-md-6 mb-4">
  505.                 <div class="stat-card-ai success">
  506.                     <i class="fas fa-table stat-icon-ai success"></i>
  507.                     <div class="stat-number-ai success">{{ declaration.tablesDetected|default('1') }}</div>
  508.                     <div class="stat-label text-muted mb-2">Structures analysées</div>
  509.                     <div class="small text-info">
  510.                         <i class="fas fa-brain me-1"></i>
  511.                         Reconnaissance IA
  512.                     </div>
  513.                 </div>
  514.             </div>
  515.             
  516.             <div class="col-xl-3 col-md-6 mb-4">
  517.                 <div class="stat-card-ai warning">
  518.                     <i class="fas fa-edit stat-icon-ai warning"></i>
  519.                     <div class="stat-number-ai warning">{{ declaration.lignes|length }}</div>
  520.                     <div class="stat-label text-muted mb-2">Lignes à vérifier</div>
  521.                     <div class="small text-warning">
  522.                         <i class="fas fa-clock me-1"></i>
  523.                         {{ declaration.pendingCorrections|default('0') }} corrections
  524.                     </div>
  525.                 </div>
  526.             </div>
  527.             
  528.             <div class="col-xl-3 col-md-6 mb-4">
  529.                 <div class="stat-card-ai info">
  530.                     <i class="fas fa-bolt stat-icon-ai info"></i>
  531.                     <div class="stat-number-ai info">{{ declaration.processingTime|default('15') }}s</div>
  532.                     <div class="stat-label text-muted mb-2">Temps de traitement</div>
  533.                     <div class="small text-primary">
  534.                         <i class="fas fa-rocket me-1"></i>
  535.                         Analyse ultra-rapide
  536.                     </div>
  537.                 </div>
  538.             </div>
  539.         </div>
  540.         <!-- Document et processus IA -->
  541.         <div class="row mb-5">
  542.             <div class="col-lg-5 mb-4">
  543.                 <div class="card-ai h-100">
  544.                     <div class="card-header-ai">
  545.                         <h5 class="mb-0">
  546.                             <i class="fas fa-file-image me-2"></i>
  547.                             Document original
  548.                         </h5>
  549.                     </div>
  550.                     <div class="card-body p-4">
  551.                         <!-- Aperçu interactif -->
  552.                         <div class="document-preview-container-ai mb-4" 
  553.                             data-bs-toggle="modal" 
  554.                             data-bs-target="#documentModal"
  555.                             title="Cliquez pour visualiser en haute résolution">
  556.                             {% if batch.extension in ['jpg','jpeg','png','heic','webp','gif','bmp'] %}
  557.                                 <img src="{{ asset('uploads/' ~ batch.storedFilename) }}" 
  558.                                     class="document-preview-ai"
  559.                                     alt="Document original analysé par IA"
  560.                                     id="documentPreviewAi">
  561.                             {% else %}
  562.                                 <div class="text-center">
  563.                                     <i class="fas fa-file-pdf pdf-icon-ai"></i>
  564.                                     <p class="text-dark mt-3 mb-1 fw-semibold">Document PDF analysé</p>
  565.                                     <small class="text-muted">Cliquez pour explorer</small>
  566.                                 </div>
  567.                             {% endif %}
  568.                         </div>
  569.                         
  570.                         <!-- Métriques du document -->
  571.                         <div class="row g-3">
  572.                             <div class="col-6">
  573.                                 <div class="bg-light rounded p-3 text-center">
  574.                                     <div class="text-muted small mb-1">Format</div>
  575.                                     <div class="fw-bold text-dark">{{ batch.extension|upper }}</div>
  576.                                 </div>
  577.                             </div>
  578.                             <div class="col-6">
  579.                                 <div class="bg-light rounded p-3 text-center">
  580.                                     <div class="text-muted small mb-1">Taille</div>
  581.                                     <div class="fw-bold text-dark">{{ (batch.fileSize / 1024 / 1024)|number_format(2) }} Mo</div>
  582.                                 </div>
  583.                             </div>
  584.                             <div class="col-6">
  585.                                 <div class="bg-light rounded p-3 text-center">
  586.                                     <div class="text-muted small mb-1">Date</div>
  587.                                     <div class="fw-bold text-dark">{{ batch.uploadedAt|date('d/m/Y') }}</div>
  588.                                 </div>
  589.                             </div>
  590.                             <div class="col-6">
  591.                                 <div class="bg-light rounded p-3 text-center">
  592.                                     <div class="text-muted small mb-1">Heure</div>
  593.                                     <div class="fw-bold text-dark">{{ batch.uploadedAt|date('H:i') }}</div>
  594.                                 </div>
  595.                             </div>
  596.                         </div>
  597.                         
  598.                         <!-- Actions document -->
  599.                         <div class="d-flex gap-2 mt-4">
  600.                             <a href="{{ asset('uploads/' ~ batch.storedFilename) }}" 
  601.                             class="btn btn-ai-outline flex-grow-1"
  602.                             target="_blank"
  603.                             download="{{ batch.originalFilename }}">
  604.                                 <i class="fas fa-download"></i>
  605.                                 Télécharger
  606.                             </a>
  607.                             <button type="button" 
  608.                                     class="btn btn-ai-primary"
  609.                                     data-bs-toggle="modal" 
  610.                                     data-bs-target="#documentModal">
  611.                                 <i class="fas fa-expand"></i>
  612.                                 Plein écran
  613.                             </button>
  614.                         </div>
  615.                     </div>
  616.                 </div>
  617.             </div>
  618.             
  619.             <div class="col-lg-7 mb-4">
  620.                 <div class="card-ai h-100">
  621.                     <div class="card-header-ai">
  622.                         <h5 class="mb-0">
  623.                             <i class="fas fa-brain me-2"></i>
  624.                             Processus d'extraction IA
  625.                         </h5>
  626.                     </div>
  627.                     <div class="card-body p-4">
  628.                         <div class="ai-process-steps mb-4">
  629.                             <h6 class="text-dark mb-4 fw-bold">
  630.                                 <i class="fas fa-microchip me-2 text-info"></i>
  631.                                 Comment notre IA a analysé votre document :
  632.                             </h6>
  633.                             
  634.                             <div class="process-step">
  635.                                 <div class="step-number">1</div>
  636.                                 <div>
  637.                                     <h6 class="text-dark mb-1 fw-semibold">OCR Avancé</h6>
  638.                                     <p class="text-muted mb-0 small">Reconnaissance optique des caractères pour extraire le texte des images</p>
  639.                                 </div>
  640.                             </div>
  641.                             
  642.                             <div class="process-step">
  643.                                 <div class="step-number">2</div>
  644.                                 <div>
  645.                                     <h6 class="text-dark mb-1 fw-semibold">Écriture manuscrite</h6>
  646.                                     <p class="text-muted mb-0 small">Reconnaissance intelligente de l'écriture manuscrite avec 95% de précision</p>
  647.                                 </div>
  648.                             </div>
  649.                             
  650.                             <div class="process-step">
  651.                                 <div class="step-number">3</div>
  652.                                 <div>
  653.                                     <h6 class="text-dark mb-1 fw-semibold">Structure des tableaux</h6>
  654.                                     <p class="text-muted mb-0 small">Détection automatique de la structure et des relations entre cellules</p>
  655.                                 </div>
  656.                             </div>
  657.                             
  658.                             <div class="process-step">
  659.                                 <div class="step-number">4</div>
  660.                                 <div>
  661.                                     <h6 class="text-dark mb-1 fw-semibold">Validation intelligente</h6>
  662.                                     <p class="text-muted mb-0 small">Vérification cohérence des données et détection des anomalies</p>
  663.                                 </div>
  664.                             </div>
  665.                         </div>
  666.                         
  667.                         <!-- Indicateur de performance IA -->
  668.                         {# {% if declaration.extractionStats %}
  669.                             <div class="border rounded p-3 mb-3">
  670.                                 <div class="d-flex justify-content-between align-items-center mb-2">
  671.                                     <span class="text-dark fw-semibold">
  672.                                         <i class="fas fa-chart-bar me-2 text-primary"></i>
  673.                                         Performance de l'extraction
  674.                                     </span>
  675.                                     <span class="badge bg-primary">{{ declaration.extractionConfidence|default('85') }}%</span>
  676.                                 </div>
  677.                                 <div class="progress" style="height: 8px;">
  678.                                     <div class="progress-bar bg-primary" style="width: {{ declaration.extractionConfidence|default('85') }}%"></div>
  679.                                 </div>
  680.                                 <div class="d-flex justify-content-between mt-2">
  681.                                     <small class="text-muted">{{ declaration.extractionStats.words|default('0') }} mots détectés</small>
  682.                                     <small class="text-muted">{{ declaration.extractionStats.tables|default('0') }} tableaux analysés</small>
  683.                                 </div>
  684.                             </div>
  685.                         {% endif %} #}
  686.                         
  687.                         <!-- Alerte validation -->
  688.                         <div class="ai-validation-alert">
  689.                             <div class="d-flex">
  690.                                 <div class="flex-shrink-0">
  691.                                     <i class="fas fa-user-check text-warning fa-2x"></i>
  692.                                 </div>
  693.                                 <div class="flex-grow-1 ms-3">
  694.                                     <h6 class="alert-heading fw-bold mb-2">Validation humaine requise</h6>
  695.                                     <p class="mb-0 small">Notre IA a extrait les données avec précision, mais votre expertise est essentielle pour valider et corriger si nécessaire.</p>
  696.                                 </div>
  697.                             </div>
  698.                         </div>
  699.                     </div>
  700.                 </div>
  701.             </div>
  702.         </div>
  703.         <!-- FORMULAIRE PRINCIPAL -->
  704.         <form method="post" action="{{ path('upload_review', {id: batch.id}) }}" id="reviewFormAi" class="position-relative">
  705.             <div class="ai-correction-tag">
  706.                 <i class="fas fa-edit me-1"></i>Mode correction
  707.             </div>
  708.             <!-- Informations conducteur -->
  709.             <div class="card-ai mb-4">
  710.                 <div class="card-header-secondary">
  711.                     <h5 class="mb-0">
  712.                         <i class="fas fa-user-tie me-2"></i>
  713.                         Informations du conducteur
  714.                         <small class="opacity-75 ms-2 fw-normal">(Extraites automatiquement)</small>
  715.                     </h5>
  716.                 </div>
  717.                 <div class="card-body p-4">
  718.                     <div class="row g-3">
  719.                         <div class="col-md-6">
  720.                             <label class="form-label fw-bold text-dark mb-2 d-flex align-items-center">
  721.                                 Nom du conducteur
  722.                                 <span class="extraction-highlight ms-2">IA</span>
  723.                             </label>
  724.                             <input type="text" name="nom_client" class="form-control-ai" 
  725.                                 value="{{ declaration.nomClient }}" 
  726.                                 placeholder="Nom et prénom du conducteur" 
  727.                                 required>
  728.                             <small class="text-muted mt-1 d-block">
  729.                                 <i class="fas fa-robot me-1"></i>
  730.                                 Détecté automatiquement depuis le document
  731.                             </small>
  732.                         </div>
  733.                         <div class="col-md-3">
  734.                             <label class="form-label fw-bold text-dark mb-2">Immatriculation</label>
  735.                             <input type="text" name="immatriculation" class="form-control-ai" 
  736.                                 value="{{ declaration.immatriculation }}"
  737.                                 placeholder="AA-123-BB">
  738.                         </div>
  739.                         <div class="col-md-3">
  740.                             <label class="form-label fw-bold text-dark mb-2">N° licence</label>
  741.                             <input type="text" name="numero_licence" class="form-control-ai" 
  742.                                 value="{{ declaration.numeroLicence }}"
  743.                                 placeholder="N° licence">
  744.                         </div>
  745.                         
  746.                         <div class="col-md-4">
  747.                             <label class="form-label fw-bold text-dark mb-2">Agence</label>
  748.                             <input type="text" name="agence" class="form-control-ai" 
  749.                                 value="{{ declaration.agence }}"
  750.                                 placeholder="Agence rattachée">
  751.                         </div>
  752.                         <div class="col-md-4">
  753.                             <label class="form-label fw-bold text-dark mb-2">Date début</label>
  754.                             <input type="date" name="date_debut" class="form-control-ai" 
  755.                                 value="{{ declaration.dateDebut ? declaration.dateDebut|date('Y-m-d') : '' }}">
  756.                         </div>
  757.                         <div class="col-md-4">
  758.                             <label class="form-label fw-bold text-dark mb-2">Date fin</label>
  759.                             <input type="date" name="date_fin" class="form-control-ai" 
  760.                                 value="{{ declaration.dateFin ? declaration.dateFin|date('Y-m-d') : '' }}">
  761.                         </div>
  762.                     </div>
  763.                 </div>
  764.             </div>
  765.             <!-- Tableau des lignes journalières -->
  766.             <div class="card-ai mb-4">
  767.                 <div class="card-header-secondary d-flex justify-content-between align-items-center">
  768.                     <div>
  769.                         <h5 class="mb-0">
  770.                             <i class="fas fa-calendar-alt me-2"></i>
  771.                             Lignes journalières extraites
  772.                             <span class="badge bg-primary ms-2">{{ ligneCount }} lignes</span>
  773.                         </h5>
  774.                         <p class="mb-0 small text-light opacity-75 mt-1">
  775.                             <i class="fas fa-magic me-1"></i>
  776.                             Tableau restructuré automatiquement par l'IA
  777.                         </p>
  778.                     </div>
  779.                     <button type="submit" name="add_line" value="1" class="btn btn-light">
  780.                         <i class="fas fa-plus-circle me-2"></i>
  781.                         Ajouter ligne
  782.                     </button>
  783.                 </div>
  784.                 <div class="card-body p-0">
  785.                     <div class="table-container-ai">
  786.                         <div class="table-responsive">
  787.                             <table class="table table-bordered mb-0">
  788.                                 <thead>
  789.                                     <tr class="table-header-ai">
  790.                                         <th width="130" class="text-center">📅 Date</th>
  791.                                         <th width="160">📍 Départ</th>
  792.                                         <th width="100" class="text-center">🚗 KM départ</th>
  793.                                         <th colspan="2" class="text-center">🌅 Matin</th>
  794.                                         <th colspan="2" class="text-center">☀️ Midi</th>
  795.                                         <th colspan="2" class="text-center">🌙 Soir</th>
  796.                                         <th colspan="2" class="text-center">⏰ Autre</th>
  797.                                         <th width="160">📍 Arrivée</th>
  798.                                         <th width="100" class="text-center">🚗 KM arrivée</th>
  799.                                         <th width="130" class="text-center">➕ Temps annexes</th>
  800.                                         <th width="130" class="text-center">⏱️ Total</th>
  801.                                         <th width="200">💬 Commentaires</th>
  802.                                         <th width="80" class="text-center">⚙️ Actions</th>
  803.                                     </tr>
  804.                                     <tr class="table-subheader-ai">
  805.                                         <th></th><th></th><th></th>
  806.                                         <th class="text-center"><small>Début</small></th>
  807.                                         <th class="text-center"><small>Fin</small></th>
  808.                                         <th class="text-center"><small>Début</small></th>
  809.                                         <th class="text-center"><small>Fin</small></th>
  810.                                         <th class="text-center"><small>Début</small></th>
  811.                                         <th class="text-center"><small>Fin</small></th>
  812.                                         <th class="text-center"><small>Début</small></th>
  813.                                         <th class="text-center"><small>Fin</small></th>
  814.                                         <th></th><th></th><th></th><th></th><th></th>
  815.                                     </tr>
  816.                                 </thead>
  817.                                 <tbody>
  818.                                     {% for ligne in declaration.lignes %}
  819.                                     <tr class="table-row-ai">
  820.                                         <td class="table-cell-ai">
  821.                                             <input type="date" name="lignes[{{ loop.index0 }}][date]" 
  822.                                                 class="form-control-ai" 
  823.                                                 value="{{ ligne.dateJour|date('Y-m-d') }}" 
  824.                                                 required>
  825.                                         </td>
  826.                                         <td class="table-cell-ai">
  827.                                             <input type="text" name="lignes[{{ loop.index0 }}][lieu_depart]" 
  828.                                                 class="form-control-ai" 
  829.                                                 value="{{ ligne.lieuDepart }}"
  830.                                                 placeholder="Lieu de départ">
  831.                                         </td>
  832.                                         <td class="table-cell-ai">
  833.                                             <input type="number" name="lignes[{{ loop.index0 }}][km_depart]" 
  834.                                                 class="form-control-ai text-center" 
  835.                                                 value="{{ ligne.kmDepart }}"
  836.                                                 step="0.1"
  837.                                                 placeholder="0.0">
  838.                                         </td>
  839.                                         
  840.                                         <!-- Matin -->
  841.                                         <td class="table-cell-ai time-cell-ai">
  842.                                             <input type="time" name="lignes[{{ loop.index0 }}][tranche_1_matin_debut]" 
  843.                                                 class="form-control-ai" 
  844.                                                 value="{{ ligne.tranche1MatinDebut }}">
  845.                                         </td>
  846.                                         <td class="table-cell-ai time-cell-ai">
  847.                                             <input type="time" name="lignes[{{ loop.index0 }}][tranche_1_matin_fin]" 
  848.                                                 class="form-control-ai" 
  849.                                                 value="{{ ligne.tranche1MatinFin }}">
  850.                                         </td>
  851.                                         
  852.                                         <!-- Midi -->
  853.                                         <td class="table-cell-ai time-cell-ai">
  854.                                             <input type="time" name="lignes[{{ loop.index0 }}][tranche_2_midi_debut]" 
  855.                                                 class="form-control-ai" 
  856.                                                 value="{{ ligne.tranche2MidiDebut }}">
  857.                                         </td>
  858.                                         <td class="table-cell-ai time-cell-ai">
  859.                                             <input type="time" name="lignes[{{ loop.index0 }}][tranche_2_midi_fin]" 
  860.                                                 class="form-control-ai" 
  861.                                                 value="{{ ligne.tranche2MidiFin }}">
  862.                                         </td>
  863.                                         
  864.                                         <!-- Soir -->
  865.                                         <td class="table-cell-ai time-cell-ai">
  866.                                             <input type="time" name="lignes[{{ loop.index0 }}][tranche_3_soir_debut]" 
  867.                                                 class="form-control-ai" 
  868.                                                 value="{{ ligne.tranche3SoirDebut }}">
  869.                                         </td>
  870.                                         <td class="table-cell-ai time-cell-ai">
  871.                                             <input type="time" name="lignes[{{ loop.index0 }}][tranche_3_soir_fin]" 
  872.                                                 class="form-control-ai" 
  873.                                                 value="{{ ligne.tranche3SoirFin }}">
  874.                                         </td>
  875.                                         
  876.                                         <!-- Autre -->
  877.                                         <td class="table-cell-ai time-cell-ai">
  878.                                             <input type="time" name="lignes[{{ loop.index0 }}][tranche_4_autre_debut]" 
  879.                                                 class="form-control-ai" 
  880.                                                 value="{{ ligne.tranche4AutreDebut }}">
  881.                                         </td>
  882.                                         <td class="table-cell-ai time-cell-ai">
  883.                                             <input type="time" name="lignes[{{ loop.index0 }}][tranche_4_autre_fin]" 
  884.                                                 class="form-control-ai" 
  885.                                                 value="{{ ligne.tranche4AutreFin }}">
  886.                                         </td>
  887.                                         
  888.                                         <td class="table-cell-ai">
  889.                                             <input type="text" name="lignes[{{ loop.index0 }}][lieu_arrivee]" 
  890.                                                 class="form-control-ai" 
  891.                                                 value="{{ ligne.lieuArrivee }}"
  892.                                                 placeholder="Lieu d'arrivée">
  893.                                         </td>
  894.                                         <td class="table-cell-ai">
  895.                                             <input type="number" name="lignes[{{ loop.index0 }}][km_arrivee]" 
  896.                                                 class="form-control-ai text-center" 
  897.                                                 value="{{ ligne.kmArrivee }}"
  898.                                                 step="0.1"
  899.                                                 placeholder="0.0">
  900.                                         </td>
  901.                                         <td class="table-cell-ai">
  902.                                             <input type="text" name="lignes[{{ loop.index0 }}][temps_annexes]" 
  903.                                                 class="form-control-ai text-center" 
  904.                                                 value="{{ ligne.tempsAnnexes }}"
  905.                                                 placeholder="00:00">
  906.                                         </td>
  907.                                         <td class="table-cell-ai">
  908.                                             <input type="text" name="lignes[{{ loop.index0 }}][temps_total]" 
  909.                                                 class="form-control-ai text-center fw-bold" 
  910.                                                 value="{{ ligne.tempsTotal }}"
  911.                                                 placeholder="00:00">
  912.                                         </td>
  913.                                         <td class="table-cell-ai">
  914.                                             <textarea name="lignes[{{ loop.index0 }}][commentaires]" 
  915.                                                     class="form-control-ai" 
  916.                                                     rows="1"
  917.                                                     placeholder="Notes éventuelles...">{{ ligne.commentaires }}</textarea>
  918.                                         </td>
  919.                                         <td class="table-cell-ai text-center">
  920.                                             {% if ligneCount > 1 %}
  921.                                                 <button type="submit" name="delete_line" value="{{ ligne.id }}" 
  922.                                                         class="btn btn-outline-danger btn-sm" 
  923.                                                         onclick="return confirmAI(this)"
  924.                                                         title="Supprimer cette ligne"
  925.                                                         data-line-date="{{ ligne.dateJour|date('d/m/Y') }}">
  926.                                                     <i class="fas fa-trash-alt"></i>
  927.                                                 </button>
  928.                                             {% endif %}
  929.                                         </td>
  930.                                     </tr>
  931.                                     {% endfor %}
  932.                                 </tbody>
  933.                             </table>
  934.                         </div>
  935.                     </div>
  936.                 </div>
  937.             </div>
  938.             <!-- Actions finales -->
  939.             <div class="card-ai">
  940.                 <div class="card-body p-4">
  941.                     <div class="row align-items-center">
  942.                         <div class="col-md-6 mb-3 mb-md-0">
  943.                             <div class="d-flex align-items-center gap-2">
  944.                                 <div class="flex-shrink-0">
  945.                                     <i class="fas fa-shield-alt fa-2x text-primary opacity-75"></i>
  946.                                 </div>
  947.                                 <div class="flex-grow-1">
  948.                                     <h6 class="mb-1 fw-bold">Validation sécurisée</h6>
  949.                                     <p class="mb-0 small text-muted">Toutes les modifications sont tracées et sécurisées</p>
  950.                                 </div>
  951.                             </div>
  952.                         </div>
  953.                         
  954.                         <div class="col-md-6">
  955.                             <div class="d-flex flex-column flex-md-row gap-3 justify-content-md-end">
  956.                                 <a href="{{ path('upload_list') }}" class="btn btn-ai-outline">
  957.                                     <i class="fas fa-arrow-left me-2"></i>
  958.                                     Retour
  959.                                 </a>
  960.                                 
  961.                                 <button type="submit" name="save_draft" value="1" class="btn btn-ai-primary">
  962.                                     <i class="fas fa-save me-2"></i>
  963.                                     Sauvegarder brouillon
  964.                                 </button>
  965.                                 
  966.                                 <button type="button" 
  967.                                         onclick="validateFinalAI()" 
  968.                                         class="btn btn-ai-success">
  969.                                     <i class="fas fa-check-double me-2"></i>
  970.                                     Valider définitivement
  971.                                 </button>
  972.                             </div>
  973.                         </div>
  974.                     </div>
  975.                 </div>
  976.             </div>
  977.         </form>
  978.     </div>
  979.     <!-- Modal document plein écran -->
  980.     <div class="modal fade modal-fullscreen-ai" id="documentModal" tabindex="-1" aria-hidden="true">
  981.         <div class="modal-dialog modal-fullscreen">
  982.             <div class="modal-content">
  983.                 <div class="modal-header modal-header-ai">
  984.                     <h5 class="modal-title modal-title-ai">
  985.                         <i class="fas fa-file-{% if batch.extension == 'pdf' %}pdf{% else %}image{% endif %} me-2"></i>
  986.                         {{ batch.originalFilename }}
  987.                     </h5>
  988.                     <button type="button" class="btn-close btn-close-white" data-bs-dismiss="modal" aria-label="Close"></button>
  989.                 </div>
  990.                 <div class="modal-body p-0">
  991.                     {% if batch.extension == 'pdf' %}
  992.                         <embed src="{{ asset('uploads/' ~ batch.storedFilename) }}" 
  993.                             type="application/pdf" 
  994.                             class="w-100 h-100">
  995.                     {% else %}
  996.                         <div class="d-flex align-items-center justify-content-center w-100 h-100">
  997.                             <img src="{{ asset('uploads/' ~ batch.storedFilename) }}" 
  998.                                 alt="{{ batch.originalFilename }}"
  999.                                 class="img-fluid"
  1000.                                 style="max-height: 90vh; object-fit: contain;">
  1001.                         </div>
  1002.                     {% endif %}
  1003.                     
  1004.                     <div class="preview-actions">
  1005.                         <a href="{{ asset('uploads/' ~ batch.storedFilename) }}" 
  1006.                         class="btn btn-primary" 
  1007.                         target="_blank"
  1008.                         download="{{ batch.originalFilename }}">
  1009.                             <i class="fas fa-download me-1"></i>
  1010.                             Télécharger
  1011.                         </a>
  1012.                         <button type="button" class="btn btn-secondary" data-bs-dismiss="modal">
  1013.                             <i class="fas fa-times me-1"></i>
  1014.                             Fermer
  1015.                         </button>
  1016.                     </div>
  1017.                 </div>
  1018.             </div>
  1019.         </div>
  1020.     </div>
  1021. {% endblock %}
  1022. {% block javascripts %}
  1023.     {{ parent() }}
  1024.     <script>
  1025.         // Fonction de validation finale améliorée
  1026.         function validateFinalAI() {
  1027.             const form = document.getElementById('reviewFormAi');
  1028.             const validateBtn = form.querySelector('button[name="validate_all"]') || 
  1029.                                form.querySelector('[onclick*="validateFinalAI"]');
  1030.             
  1031.             // Vérifier les champs obligatoires
  1032.             const requiredFields = form.querySelectorAll('[required]');
  1033.             const emptyFields = [];
  1034.             
  1035.             requiredFields.forEach(field => {
  1036.                 if (!field.value.trim()) {
  1037.                     emptyFields.push(field);
  1038.                     field.classList.add('is-invalid');
  1039.                 }
  1040.             });
  1041.             
  1042.             if (emptyFields.length > 0) {
  1043.                 // Afficher une notification moderne
  1044.                 if (typeof Swal !== 'undefined') {
  1045.                     Swal.fire({
  1046.                         icon: 'warning',
  1047.                         title: 'Champs manquants',
  1048.                         html: `
  1049.                             <div class="text-start">
  1050.                                 <p>Veuillez remplir les <strong>${emptyFields.length}</strong> champ(s) obligatoire(s) :</p>
  1051.                                 <ul class="mb-0">
  1052.                                     ${emptyFields.slice(0, 3).map(field => 
  1053.                                         `<li>${field.previousElementSibling?.textContent || 'Champ requis'}</li>`
  1054.                                     ).join('')}
  1055.                                     ${emptyFields.length > 3 ? '<li>... et ' + (emptyFields.length - 3) + ' autre(s)</li>' : ''}
  1056.                                 </ul>
  1057.                             </div>
  1058.                         `,
  1059.                         confirmButtonText: 'Corriger',
  1060.                         confirmButtonColor: '#3b82f6'
  1061.                     }).then(() => {
  1062.                         emptyFields[0].scrollIntoView({ behavior: 'smooth', block: 'center' });
  1063.                         emptyFields[0].focus();
  1064.                     });
  1065.                 } else {
  1066.                     alert(`Veuillez remplir ${emptyFields.length} champ(s) obligatoire(s) avant de valider.`);
  1067.                     emptyFields[0].focus();
  1068.                 }
  1069.                 return false;
  1070.             }
  1071.             
  1072.             // Confirmation avec statistiques
  1073.             const lineCount = {{ ligneCount }};
  1074.             const totalHours = calculateTotalHours();
  1075.             
  1076.             if (typeof Swal !== 'undefined') {
  1077.                 Swal.fire({
  1078.                     title: '✅ Validation définitive',
  1079.                     html: `
  1080.                         <div class="text-start">
  1081.                             <p>Êtes-vous sûr de vouloir valider définitivement cette déclaration ?</p>
  1082.                             <div class="alert alert-light border mt-3">
  1083.                                 <div class="row">
  1084.                                     <div class="col-6">
  1085.                                         <small class="text-muted">Lignes :</small><br>
  1086.                                         <strong>${lineCount}</strong>
  1087.                                     </div>
  1088.                                     <div class="col-6">
  1089.                                         <small class="text-muted">Période :</small><br>
  1090.                                         <strong>${form.querySelector('[name="date_debut"]').value} → ${form.querySelector('[name="date_fin"]').value}</strong>
  1091.                                     </div>
  1092.                                 </div>
  1093.                             </div>
  1094.                             <p class="text-danger small mt-3">
  1095.                                 <i class="fas fa-exclamation-triangle me-1"></i>
  1096.                                 Cette action est irréversible.
  1097.                             </p>
  1098.                         </div>
  1099.                     `,
  1100.                     icon: 'question',
  1101.                     showCancelButton: true,
  1102.                     confirmButtonText: 'Oui, valider',
  1103.                     cancelButtonText: 'Annuler',
  1104.                     confirmButtonColor: '#10b981',
  1105.                     cancelButtonColor: '#6b7280',
  1106.                     reverseButtons: true
  1107.                 }).then((result) => {
  1108.                     if (result.isConfirmed) {
  1109.                         // Créer un bouton temporaire pour la validation
  1110.                         const tempInput = document.createElement('input');
  1111.                         tempInput.type = 'hidden';
  1112.                         tempInput.name = 'validate_all';
  1113.                         tempInput.value = '1';
  1114.                         form.appendChild(tempInput);
  1115.                         
  1116.                         // Afficher un loader
  1117.                         Swal.fire({
  1118.                             title: 'Validation en cours...',
  1119.                             text: 'Enregistrement des données validées',
  1120.                             icon: 'info',
  1121.                             showConfirmButton: false,
  1122.                             allowOutsideClick: false,
  1123.                             didOpen: () => {
  1124.                                 Swal.showLoading();
  1125.                                 // Soumettre après un délai
  1126.                                 setTimeout(() => form.submit(), 1000);
  1127.                             }
  1128.                         });
  1129.                     }
  1130.                 });
  1131.             } else {
  1132.                 if (confirm(`Valider définitivement ${lineCount} lignes ? Cette action est irréversible.`)) {
  1133.                     const tempInput = document.createElement('input');
  1134.                     tempInput.type = 'hidden';
  1135.                     tempInput.name = 'validate_all';
  1136.                     tempInput.value = '1';
  1137.                     form.appendChild(tempInput);
  1138.                     form.submit();
  1139.                 }
  1140.             }
  1141.         }
  1142.         
  1143.         // Calculer le total des heures
  1144.         function calculateTotalHours() {
  1145.             const timeInputs = document.querySelectorAll('input[type="time"]');
  1146.             let totalMinutes = 0;
  1147.             
  1148.             timeInputs.forEach(input => {
  1149.                 if (input.value) {
  1150.                     const [hours, minutes] = input.value.split(':').map(Number);
  1151.                     totalMinutes += hours * 60 + minutes;
  1152.                 }
  1153.             });
  1154.             
  1155.             const hours = Math.floor(totalMinutes / 60);
  1156.             const minutes = totalMinutes % 60;
  1157.             return `${hours}h${minutes.toString().padStart(2, '0')}`;
  1158.         }
  1159.         
  1160.         // Confirmation améliorée pour la suppression
  1161.         function confirmAI(button) {
  1162.             const lineDate = button.dataset.lineDate || 'cette ligne';
  1163.             
  1164.             if (typeof Swal !== 'undefined') {
  1165.                 Swal.fire({
  1166.                     title: 'Supprimer la ligne ?',
  1167.                     html: `
  1168.                         <div class="text-start">
  1169.                             <p>Vous allez supprimer la ligne du <strong>${lineDate}</strong>.</p>
  1170.                             <p class="text-danger small">
  1171.                                 <i class="fas fa-exclamation-triangle me-1"></i>
  1172.                                 Cette action ne peut pas être annulée.
  1173.                             </p>
  1174.                         </div>
  1175.                     `,
  1176.                     icon: 'warning',
  1177.                     showCancelButton: true,
  1178.                     confirmButtonText: 'Oui, supprimer',
  1179.                     cancelButtonText: 'Annuler',
  1180.                     confirmButtonColor: '#dc2626',
  1181.                     cancelButtonColor: '#6b7280',
  1182.                     reverseButtons: true
  1183.                 }).then((result) => {
  1184.                     if (result.isConfirmed) {
  1185.                         // Soumettre le formulaire
  1186.                         button.form.submit();
  1187.                     }
  1188.                 });
  1189.                 return false;
  1190.             } else {
  1191.                 return confirm(`Supprimer la ligne du ${lineDate} ?`);
  1192.             }
  1193.         }
  1194.         
  1195.         // Amélioration de l'UX des champs
  1196.         document.addEventListener('DOMContentLoaded', function() {
  1197.             const form = document.getElementById('reviewFormAi');
  1198.             
  1199.             // Highlight automatique des champs corrigés
  1200.             const inputs = form.querySelectorAll('input, textarea');
  1201.             inputs.forEach(input => {
  1202.                 const originalValue = input.value;
  1203.                 
  1204.                 input.addEventListener('change', function() {
  1205.                     if (this.value !== originalValue) {
  1206.                         this.style.borderColor = '#10b981';
  1207.                         this.style.boxShadow = '0 0 0 3px rgba(16, 185, 129, 0.1)';
  1208.                         
  1209.                         // Ajouter un petit badge "modifié"
  1210.                         const parent = this.parentElement;
  1211.                         if (!parent.querySelector('.modified-badge')) {
  1212.                             const badge = document.createElement('span');
  1213.                             badge.className = 'modified-badge position-absolute';
  1214.                             badge.innerHTML = '<i class="fas fa-pencil-alt"></i>';
  1215.                             badge.style.top = '5px';
  1216.                             badge.style.right = '5px';
  1217.                             badge.style.color = '#10b981';
  1218.                             badge.style.fontSize = '0.7rem';
  1219.                             badge.style.zIndex = '5';
  1220.                             parent.style.position = 'relative';
  1221.                             parent.appendChild(badge);
  1222.                         }
  1223.                     }
  1224.                 });
  1225.             });
  1226.             
  1227.             // Navigation clavier améliorée
  1228.             const tableInputs = form.querySelectorAll('.table-container-ai input, .table-container-ai textarea');
  1229.             tableInputs.forEach((input, index) => {
  1230.                 input.addEventListener('keydown', function(e) {
  1231.                     if (e.key === 'Enter' && !e.shiftKey && this.tagName !== 'TEXTAREA') {
  1232.                         e.preventDefault();
  1233.                         const nextIndex = index + (e.ctrlKey ? 10 : 1); // Ctrl+Entrée pour sauter 10 champs
  1234.                         if (nextIndex < tableInputs.length) {
  1235.                             tableInputs[nextIndex].focus();
  1236.                         }
  1237.                     } else if (e.key === 'Tab' && !e.shiftKey) {
  1238.                         if (index === tableInputs.length - 1) {
  1239.                             e.preventDefault();
  1240.                             form.querySelector('button[name="save_draft"]').focus();
  1241.                         }
  1242.                     }
  1243.                 });
  1244.             });
  1245.             
  1246.             // Calcul automatique du temps total
  1247.             function setupTimeCalculation() {
  1248.                 const timeCells = form.querySelectorAll('.time-cell-ai input[type="time"]');
  1249.                 timeCells.forEach(cell => {
  1250.                     cell.addEventListener('change', function() {
  1251.                         const row = this.closest('tr');
  1252.                         const timeInputs = row.querySelectorAll('input[type="time"]');
  1253.                         let totalMinutes = 0;
  1254.                         
  1255.                         // Calculer le temps total entre début et fin pour chaque tranche
  1256.                         for (let i = 0; i < timeInputs.length; i += 2) {
  1257.                             const start = timeInputs[i].value;
  1258.                             const end = timeInputs[i + 1]?.value;
  1259.                             
  1260.                             if (start && end) {
  1261.                                 const [startHours, startMinutes] = start.split(':').map(Number);
  1262.                                 const [endHours, endMinutes] = end.split(':').map(Number);
  1263.                                 const diffMinutes = (endHours * 60 + endMinutes) - (startHours * 60 + startMinutes);
  1264.                                 if (diffMinutes > 0) {
  1265.                                     totalMinutes += diffMinutes;
  1266.                                 }
  1267.                             }
  1268.                         }
  1269.                         
  1270.                         // Mettre à jour le champ temps total
  1271.                         if (totalMinutes > 0) {
  1272.                             const hours = Math.floor(totalMinutes / 60);
  1273.                             const minutes = totalMinutes % 60;
  1274.                             const totalCell = row.querySelector('input[name$="[temps_total]"]');
  1275.                             if (totalCell) {
  1276.                                 totalCell.value = `${hours.toString().padStart(2, '0')}:${minutes.toString().padStart(2, '0')}`;
  1277.                             }
  1278.                         }
  1279.                     });
  1280.                 });
  1281.             }
  1282.             
  1283.             setupTimeCalculation();
  1284.             
  1285.             // Auto-complétion pour les lieux récurrents
  1286.             const locationInputs = form.querySelectorAll('input[placeholder*="Lieu"]');
  1287.             const commonLocations = ['Dépôt', 'Garage', 'Siège', 'Client', 'Chantier'];
  1288.             
  1289.             locationInputs.forEach(input => {
  1290.                 const datalist = document.createElement('datalist');
  1291.                 datalist.id = `locations-${Math.random().toString(36).substr(2, 9)}`;
  1292.                 
  1293.                 commonLocations.forEach(location => {
  1294.                     const option = document.createElement('option');
  1295.                     option.value = location;
  1296.                     datalist.appendChild(option);
  1297.                 });
  1298.                 
  1299.                 document.body.appendChild(datalist);
  1300.                 input.setAttribute('list', datalist.id);
  1301.                 input.setAttribute('autocomplete', 'on');
  1302.             });
  1303.             
  1304.             // Modal amélioré
  1305.             const documentModal = document.getElementById('documentModal');
  1306.             if (documentModal) {
  1307.                 documentModal.addEventListener('shown.bs.modal', function() {
  1308.                     // Zoom interactif pour les images
  1309.                     const img = this.querySelector('img');
  1310.                     if (img) {
  1311.                         img.style.cursor = 'zoom-in';
  1312.                         let isZoomed = false;
  1313.                         
  1314.                         img.addEventListener('click', function() {
  1315.                             if (!isZoomed) {
  1316.                                 this.style.transform = 'scale(1.5)';
  1317.                                 this.style.cursor = 'zoom-out';
  1318.                             } else {
  1319.                                 this.style.transform = 'scale(1)';
  1320.                                 this.style.cursor = 'zoom-in';
  1321.                             }
  1322.                             isZoomed = !isZoomed;
  1323.                         });
  1324.                     }
  1325.                 });
  1326.             }
  1327.             
  1328.             // Afficher un message de bienvenue pour la première visite
  1329.             if (!localStorage.getItem('reviewTutorialShown')) {
  1330.                 setTimeout(() => {
  1331.                     if (typeof Swal !== 'undefined') {
  1332.                         Swal.fire({
  1333.                             title: '👋 Bienvenue dans l\'interface de relecture IA',
  1334.                             html: `
  1335.                                 <div class="text-start">
  1336.                                     <p>Notre intelligence artificielle a extrait automatiquement les données de votre document.</p>
  1337.                                     <div class="alert alert-info mt-3">
  1338.                                         <h6><i class="fas fa-lightbulb me-2"></i>Conseils :</h6>
  1339.                                         <ul class="mb-0 small">
  1340.                                             <li>Vérifiez les champs mis en évidence</li>
  1341.                                             <li>Comparez avec le document original</li>
  1342.                                             <li>Utilisez Tab/Entrée pour naviguer rapidement</li>
  1343.                                         </ul>
  1344.                                     </div>
  1345.                                 </div>
  1346.                             `,
  1347.                             icon: 'info',
  1348.                             confirmButtonText: 'Commencer',
  1349.                             confirmButtonColor: '#3b82f6'
  1350.                         });
  1351.                         localStorage.setItem('reviewTutorialShown', 'true');
  1352.                     }
  1353.                 }, 1000);
  1354.             }
  1355.         });
  1356.     </script>
  1357. {% endblock %}