templates/timesheet/index.html.twig line 1

Open in your IDE?
  1. {# templates/timesheet/index.html.twig #}
  2. {% extends 'admin/baseAdmin.html.twig' %}
  3. {% block title %}Feuille d'heures - Recherche{% endblock %}
  4. {% block stylesheets %}
  5.     {{ parent() }}
  6.     <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap-select@1.13.14/dist/css/bootstrap-select.min.css">
  7.     <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
  8.     <style>
  9.         :root {
  10.             --primary-color: #4361ee;
  11.             --primary-light: #eef2ff;
  12.             --secondary-color: #3a0ca3;
  13.             --success-color: #06d6a0;
  14.             --warning-color: #ffd166;
  15.             --info-color: #118ab2;
  16.             --dark-color: #2b2d42;
  17.             --light-color: #f8f9fa;
  18.         }
  19.         
  20.         body {
  21.             background-color: #f5f7fb;
  22.         }
  23.         
  24.         .page-header {
  25.             background: linear-gradient(135deg, var(--primary-color), var(--secondary-color));
  26.             color: white;
  27.             padding: 2rem 0;
  28.             margin-bottom: 2rem;
  29.             border-radius: 0 0 15px 15px;
  30.             box-shadow: 0 4px 20px rgba(67, 97, 238, 0.15);
  31.         }
  32.         
  33.         .page-header h1 {
  34.             font-weight: 700;
  35.             margin-bottom: 0.5rem;
  36.             text-shadow: 0 2px 4px rgba(0,0,0,0.1);
  37.         }
  38.         
  39.         .page-header .subtitle {
  40.             opacity: 0.9;
  41.             font-size: 1.1rem;
  42.         }
  43.         
  44.         .card {
  45.             border: none;
  46.             border-radius: 12px;
  47.             box-shadow: 0 5px 15px rgba(0, 0, 0, 0.05);
  48.             transition: transform 0.3s ease, box-shadow 0.3s ease;
  49.             overflow: hidden;
  50.         }
  51.         
  52.         .card:hover {
  53.             box-shadow: 0 8px 25px rgba(0, 0, 0, 0.1);
  54.         }
  55.         
  56.         .card-header {
  57.             background: linear-gradient(to right, var(--primary-light), white);
  58.             border-bottom: 2px solid var(--primary-light);
  59.             padding: 1.25rem 1.5rem;
  60.             font-weight: 600;
  61.             color: var(--dark-color);
  62.             position: relative;
  63.         }
  64.         
  65.         .card-header::before {
  66.             content: '';
  67.             position: absolute;
  68.             left: 0;
  69.             top: 0;
  70.             height: 100%;
  71.             width: 4px;
  72.             background: var(--primary-color);
  73.             border-radius: 0 3px 3px 0;
  74.         }
  75.         
  76.         .card-header h5 {
  77.             margin: 0;
  78.             font-weight: 700;
  79.             font-size: 1.1rem;
  80.         }
  81.         
  82.         .form-section {
  83.             padding: 1.5rem;
  84.         }
  85.         
  86.         .form-label {
  87.             font-weight: 600;
  88.             color: var(--dark-color);
  89.             margin-bottom: 0.5rem;
  90.             display: flex;
  91.             align-items: center;
  92.         }
  93.         
  94.         .form-label i {
  95.             margin-right: 8px;
  96.             color: var(--primary-color);
  97.         }
  98.         
  99.         .form-control {
  100.             border: 2px solid #e2e8f0;
  101.             border-radius: 8px;
  102.             padding: 0.75rem 1rem;
  103.             font-size: 0.95rem;
  104.             transition: all 0.3s ease;
  105.             background-color: white;
  106.         }
  107.         
  108.         .form-control:focus {
  109.             border-color: var(--primary-color);
  110.             box-shadow: 0 0 0 3px rgba(67, 97, 238, 0.1);
  111.             background-color: white;
  112.         }
  113.         
  114.         .input-group-text {
  115.             background-color: var(--primary-light);
  116.             border: 2px solid #e2e8f0;
  117.             border-right: none;
  118.             color: var(--primary-color);
  119.             font-weight: 600;
  120.         }
  121.         
  122.         .btn {
  123.             border-radius: 8px;
  124.             padding: 0.75rem 1.5rem;
  125.             font-weight: 600;
  126.             transition: all 0.3s ease;
  127.             border: none;
  128.             display: inline-flex;
  129.             align-items: center;
  130.             justify-content: center;
  131.         }
  132.         
  133.         .btn-primary {
  134.             background: linear-gradient(135deg, var(--primary-color), #5a6ff0);
  135.             color: white;
  136.             box-shadow: 0 4px 12px rgba(67, 97, 238, 0.25);
  137.         }
  138.         
  139.         .btn-primary:hover {
  140.             background: linear-gradient(135deg, #3a56e9, #4a5fed);
  141.             transform: translateY(-2px);
  142.             box-shadow: 0 6px 18px rgba(67, 97, 238, 0.35);
  143.         }
  144.         
  145.         .btn-success {
  146.             background: linear-gradient(135deg, var(--success-color), #1bdecb);
  147.             color: white;
  148.             box-shadow: 0 4px 12px rgba(6, 214, 160, 0.25);
  149.         }
  150.         
  151.         .btn-success:hover {
  152.             background: linear-gradient(135deg, #05c896, #16d9c5);
  153.             transform: translateY(-2px);
  154.             box-shadow: 0 6px 18px rgba(6, 214, 160, 0.35);
  155.         }
  156.         
  157.         /* Table styles */
  158.         .results-card {
  159.             border-radius: 12px;
  160.             overflow: hidden;
  161.             box-shadow: 0 5px 20px rgba(0, 0, 0, 0.08);
  162.             margin-bottom: 2rem;
  163.         }
  164.         
  165.         .table-container {
  166.             max-height: 600px;
  167.             overflow-y: auto;
  168.             border-radius: 8px;
  169.         }
  170.         
  171.         .table {
  172.             margin-bottom: 0;
  173.             font-size: 0.9rem;
  174.         }
  175.         
  176.         .table thead {
  177.             background: linear-gradient(to right, var(--primary-color), var(--secondary-color));
  178.             color: white;
  179.             position: sticky;
  180.             top: 0;
  181.             z-index: 10;
  182.         }
  183.         
  184.         .table th {
  185.             border: none;
  186.             padding: 1rem 0.75rem;
  187.             font-weight: 600;
  188.             text-transform: uppercase;
  189.             font-size: 0.8rem;
  190.             letter-spacing: 0.5px;
  191.             white-space: nowrap;
  192.         }
  193.         
  194.         .table td {
  195.             padding: 0.85rem 0.75rem;
  196.             vertical-align: middle;
  197.             border-bottom: 1px solid #f1f5f9;
  198.             transition: background-color 0.2s ease;
  199.         }
  200.         
  201.         .table-striped tbody tr:nth-of-type(odd) {
  202.             background-color: rgba(67, 97, 238, 0.02);
  203.         }
  204.         
  205.         .table-hover tbody tr:hover {
  206.             background-color: rgba(67, 97, 238, 0.08);
  207.         }
  208.         
  209.         .total-row {
  210.             background: linear-gradient(to right, #f8fafc, #e2e8f0) !important;
  211.             font-weight: 700;
  212.         }
  213.         
  214.         .total-row td {
  215.             border-top: 2px solid var(--primary-color);
  216.             border-bottom: none;
  217.             padding: 1rem 0.75rem;
  218.         }
  219.         
  220.         /* Cell styles */
  221.         .date-cell {
  222.             font-weight: 600;
  223.             color: var(--dark-color);
  224.             min-width: 100px;
  225.         }
  226.         
  227.         .date-cell .weekday {
  228.             display: block;
  229.             font-size: 0.75rem;
  230.             color: #64748b;
  231.             font-weight: normal;
  232.             margin-top: 2px;
  233.         }
  234.         
  235.         .time-cell {
  236.             text-align: center;
  237.             font-family: 'Courier New', 'SF Mono', monospace;
  238.             font-weight: 600;
  239.             background-color: var(--primary-light);
  240.             border-radius: 6px;
  241.             padding: 0.4rem 0.6rem;
  242.             min-width: 75px;
  243.             color: var(--primary-color);
  244.             border: 1px solid rgba(67, 97, 238, 0.1);
  245.         }
  246.         
  247.         .number-cell {
  248.             text-align: right;
  249.             font-family: 'Courier New', 'SF Mono', monospace;
  250.             font-weight: 600;
  251.             min-width: 85px;
  252.             color: #334155;
  253.         }
  254.         
  255.         .highlight-cell {
  256.             background-color: rgba(255, 209, 102, 0.15);
  257.             border: 1px solid rgba(255, 209, 102, 0.2);
  258.         }
  259.         
  260.         /* Vacation badges */
  261.         .vacation-badge {
  262.             display: inline-flex;
  263.             align-items: center;
  264.             justify-content: center;
  265.             width: 28px;
  266.             height: 28px;
  267.             border-radius: 50%;
  268.             font-weight: 700;
  269.             font-size: 0.85rem;
  270.             box-shadow: 0 3px 8px rgba(0,0,0,0.1);
  271.             transition: transform 0.2s ease, box-shadow 0.2s ease;
  272.         }
  273.         
  274.         .vacation-badge:hover {
  275.             transform: scale(1.1);
  276.             box-shadow: 0 5px 12px rgba(0,0,0,0.15);
  277.         }
  278.         
  279.         .vacation-1 { 
  280.             background: linear-gradient(135deg, #a5b4fc, #818cf8);
  281.             color: white;
  282.         }
  283.         .vacation-2 { 
  284.             background: linear-gradient(135deg, #34d399, #10b981);
  285.             color: white;
  286.         }
  287.         .vacation-3 { 
  288.             background: linear-gradient(135deg, #fbbf24, #f59e0b);
  289.             color: white;
  290.         }
  291.         .vacation-4 { 
  292.             background: linear-gradient(135deg, #f87171, #ef4444);
  293.             color: white;
  294.         }
  295.         
  296.         /* Stats cards */
  297.         .stats-grid {
  298.             display: grid;
  299.             grid-template-columns: repeat(auto-fit, minmax(220px, 1fr));
  300.             gap: 1.5rem;
  301.             margin-top: 2rem;
  302.         }
  303.         
  304.         .stat-card {
  305.             background: white;
  306.             border-radius: 12px;
  307.             padding: 1.5rem;
  308.             box-shadow: 0 5px 15px rgba(0, 0, 0, 0.05);
  309.             transition: all 0.3s ease;
  310.             border-top: 4px solid;
  311.             position: relative;
  312.             overflow: hidden;
  313.         }
  314.         
  315.         .stat-card::before {
  316.             content: '';
  317.             position: absolute;
  318.             top: 0;
  319.             left: 0;
  320.             right: 0;
  321.             height: 4px;
  322.             background: linear-gradient(90deg, transparent, rgba(255,255,255,0.5), transparent);
  323.         }
  324.         
  325.         .stat-card:hover {
  326.             transform: translateY(-5px);
  327.             box-shadow: 0 10px 25px rgba(0, 0, 0, 0.1);
  328.         }
  329.         
  330.         .stat-icon {
  331.             width: 48px;
  332.             height: 48px;
  333.             border-radius: 12px;
  334.             display: flex;
  335.             align-items: center;
  336.             justify-content: center;
  337.             margin-bottom: 1rem;
  338.             font-size: 1.5rem;
  339.         }
  340.         
  341.         .stat-title {
  342.             font-size: 0.85rem;
  343.             font-weight: 600;
  344.             color: #64748b;
  345.             text-transform: uppercase;
  346.             letter-spacing: 0.5px;
  347.             margin-bottom: 0.5rem;
  348.         }
  349.         
  350.         .stat-value {
  351.             font-size: 1.8rem;
  352.             font-weight: 700;
  353.             margin-bottom: 0.25rem;
  354.         }
  355.         
  356.         .stat-subtitle {
  357.             font-size: 0.85rem;
  358.             color: #94a3b8;
  359.         }
  360.         
  361.         .work-hours .stat-icon { background: rgba(67, 97, 238, 0.1); color: var(--primary-color); }
  362.         .work-hours { border-top-color: var(--primary-color); }
  363.         
  364.         .paid-hours .stat-icon { background: rgba(6, 214, 160, 0.1); color: var(--success-color); }
  365.         .paid-hours { border-top-color: var(--success-color); }
  366.         
  367.         .vacations .stat-icon { background: rgba(255, 209, 102, 0.1); color: #f59e0b; }
  368.         .vacations { border-top-color: #f59e0b; }
  369.         
  370.         .delta .stat-icon { background: rgba(17, 138, 178, 0.1); color: var(--info-color); }
  371.         .delta { border-top-color: var(--info-color); }
  372.         
  373.         /* Alert */
  374.         .alert {
  375.             border-radius: 10px;
  376.             border: none;
  377.             padding: 1.25rem 1.5rem;
  378.             box-shadow: 0 4px 12px rgba(0,0,0,0.05);
  379.         }
  380.         
  381.         .alert-warning {
  382.             background: linear-gradient(135deg, #fffbeb, #fef3c7);
  383.             border-left: 4px solid #f59e0b;
  384.             color: #92400e;
  385.         }
  386.         
  387.         /* Footer */
  388.         .card-footer {
  389.             background-color: #f8fafc;
  390.             border-top: 1px solid #e2e8f0;
  391.             padding: 1rem 1.5rem;
  392.             font-size: 0.85rem;
  393.             color: #64748b;
  394.         }
  395.         
  396.         /* Action buttons */
  397.         .action-buttons {
  398.             display: flex;
  399.             gap: 0.75rem;
  400.             align-items: center;
  401.         }
  402.         
  403.         .action-btn {
  404.             width: 40px;
  405.             height: 40px;
  406.             border-radius: 8px;
  407.             display: flex;
  408.             align-items: center;
  409.             justify-content: center;
  410.             background: var(--primary-light);
  411.             color: var(--primary-color);
  412.             border: none;
  413.             transition: all 0.3s ease;
  414.         }
  415.         
  416.         .action-btn:hover {
  417.             background: var(--primary-color);
  418.             color: white;
  419.             transform: translateY(-2px);
  420.             box-shadow: 0 4px 12px rgba(67, 97, 238, 0.2);
  421.         }
  422.         
  423.         /* Results count badge */
  424.         .results-badge {
  425.             background: linear-gradient(135deg, var(--success-color), #1bdecb);
  426.             color: white;
  427.             padding: 0.5rem 1rem;
  428.             border-radius: 20px;
  429.             font-weight: 600;
  430.             font-size: 0.9rem;
  431.             box-shadow: 0 3px 10px rgba(6, 214, 160, 0.2);
  432.         }
  433.         
  434.         /* Delta styling */
  435.         .delta-positive {
  436.             color: #10b981;
  437.             font-weight: 700;
  438.             background-color: rgba(16, 185, 129, 0.1);
  439.             border-radius: 4px;
  440.             padding: 2px 6px;
  441.         }
  442.         
  443.         .delta-negative {
  444.             color: #ef4444;
  445.             font-weight: 700;
  446.             background-color: rgba(239, 68, 68, 0.1);
  447.             border-radius: 4px;
  448.             padding: 2px 6px;
  449.         }
  450.         
  451.         /* Responsive */
  452.         @media (max-width: 768px) {
  453.             .stats-grid {
  454.                 grid-template-columns: 1fr;
  455.             }
  456.             
  457.             .table-container {
  458.                 font-size: 0.8rem;
  459.             }
  460.             
  461.             .table th, .table td {
  462.                 padding: 0.6rem 0.5rem;
  463.             }
  464.             
  465.             .time-cell, .number-cell {
  466.                 min-width: 60px;
  467.             }
  468.             
  469.             .action-buttons {
  470.                 flex-wrap: wrap;
  471.             }
  472.         }
  473.         
  474.         /* Animation */
  475.         @keyframes fadeIn {
  476.             from { opacity: 0; transform: translateY(10px); }
  477.             to { opacity: 1; transform: translateY(0); }
  478.         }
  479.         
  480.         .fade-in {
  481.             animation: fadeIn 0.5s ease forwards;
  482.         }
  483.         
  484.         /* Print styles */
  485.         @media print {
  486.             .page-header, .btn, .action-buttons, .card-footer {
  487.                 display: none;
  488.             }
  489.             
  490.             .table thead {
  491.                 background: #f1f5f9 !important;
  492.                 color: var(--dark-color) !important;
  493.             }
  494.         }
  495.     </style>
  496. {% endblock %}
  497. {% block body %}
  498. <!-- Page Header -->
  499. <div class="page-header">
  500.     <div class="container-fluid">
  501.         <div class="row align-items-center">
  502.             <div class="col-md-8">
  503.                 <h1><i class="fas fa-chart-line me-2"></i>Feuille d'heures</h1>
  504.                 <p class="subtitle mb-0">Analyse complète des heures travaillées et payées</p>
  505.             </div>
  506.             <div class="col-md-4 text-end">
  507.                 <span class="results-badge">
  508.                     <i class="fas fa-calendar-alt me-1"></i>
  509.                     {{ "now"|date('F Y')|capitalize }}
  510.                 </span>
  511.             </div>
  512.         </div>
  513.     </div>
  514. </div>
  515. <div class="container-fluid pb-5">
  516.     <!-- Search Form -->
  517.     <div class="card fade-in mb-4">
  518.         <div class="card-header">
  519.             <h5><i class="fas fa-search me-2"></i>Recherche avancée</h5>
  520.         </div>
  521.         <div class="form-section">
  522.             {{ form_start(form, {'attr': {'class': 'row g-4', 'id': 'search-form'}}) }}
  523.                 <div class="col-lg-3 col-md-6">
  524.                     {{ form_label(form.nom_client) }}
  525.                     <div class="input-group">
  526.                         <span class="input-group-text"><i class="fas fa-user"></i></span>
  527.                         {{ form_widget(form.nom_client, {'attr': {
  528.                             'class': 'form-control',
  529.                             'list': 'conducteurs-list',
  530.                             'placeholder': 'Sélectionnez un conducteur'
  531.                         }}) }}
  532.                     </div>
  533.                     <datalist id="conducteurs-list">
  534.                         {% for conducteur in conducteurs %}
  535.                             <option value="{{ conducteur }}">
  536.                         {% endfor %}
  537.                     </datalist>
  538.                 </div>
  539.                 
  540.                 <div class="col-lg-3 col-md-6">
  541.                     {{ form_label(form.immatriculation) }}
  542.                     <div class="input-group">
  543.                         <span class="input-group-text"><i class="fas fa-car"></i></span>
  544.                         {{ form_widget(form.immatriculation, {'attr': {
  545.                             'class': 'form-control',
  546.                             'list': 'immatriculations-list',
  547.                             'placeholder': 'Toutes les immatriculations'
  548.                         }}) }}
  549.                     </div>
  550.                     <datalist id="immatriculations-list">
  551.                         {% for immat in immatriculations %}
  552.                             <option value="{{ immat }}">
  553.                         {% endfor %}
  554.                     </datalist>
  555.                 </div>
  556.                 
  557.                 <div class="col-lg-3 col-md-6">
  558.                     {{ form_label(form.agence) }}
  559.                     <div class="input-group">
  560.                         <span class="input-group-text"><i class="fas fa-building"></i></span>
  561.                         {{ form_widget(form.agence, {'attr': {
  562.                             'class': 'form-control',
  563.                             'list': 'agences-list',
  564.                             'placeholder': 'Toutes les agences'
  565.                         }}) }}
  566.                     </div>
  567.                     <datalist id="agences-list">
  568.                         {% for agence in agences %}
  569.                             <option value="{{ agence }}">
  570.                         {% endfor %}
  571.                     </datalist>
  572.                 </div>
  573.                 
  574.                 <div class="col-lg-3 col-md-6">
  575.                     {{ form_label(form.date_debut) }}
  576.                     <div class="input-group">
  577.                         <span class="input-group-text"><i class="fas fa-calendar-day"></i></span>
  578.                         {{ form_widget(form.date_debut, {'attr': {
  579.                             'class': 'form-control'
  580.                         }}) }}
  581.                     </div>
  582.                 </div>
  583.                 
  584.                 <div class="col-lg-3 col-md-6">
  585.                     {{ form_label(form.date_fin) }}
  586.                     <div class="input-group">
  587.                         <span class="input-group-text"><i class="fas fa-calendar-week"></i></span>
  588.                         {{ form_widget(form.date_fin, {'attr': {
  589.                             'class': 'form-control'
  590.                         }}) }}
  591.                     </div>
  592.                 </div>
  593.                 
  594.                 <div class="col-12 mt-2">
  595.                     <div class="d-flex flex-wrap align-items-center justify-content-between">
  596.                         <div class="action-buttons">
  597.                             {{ form_widget(form.rechercher, {'attr': {
  598.                                 'class': 'btn btn-primary',
  599.                                 'value': '🔍 Rechercher'
  600.                             }}) }}
  601.                             
  602.                             <button type="submit" 
  603.                                     name="export" 
  604.                                     class="btn btn-success"
  605.                                     formaction="{{ export_url }}"
  606.                                     formnovalidate="true">
  607.                                 <i class="fas fa-file-excel me-2"></i> Exporter
  608.                             </button>
  609.                             
  610.                             {# <button type="button" class="action-btn" id="print-btn" title="Imprimer">
  611.                                 <i class="fas fa-print"></i>
  612.                             </button> #}
  613.                             
  614.                             <button type="button" class="action-btn" id="help-btn" title="Aide">
  615.                                 <i class="fas fa-question-circle"></i>
  616.                             </button>
  617.                         </div>
  618.                         
  619.                         <a href="javascript:void(0)" class="text-primary" id="reset-form">
  620.                             <i class="fas fa-redo-alt me-1"></i> Réinitialiser
  621.                         </a>
  622.                     </div>
  623.                 </div>
  624.                 
  625.                 {# Champs cachés pour l'export #}
  626.                 <input type="hidden" name="immatriculation" value="{{ search_params.immatriculation ?? '' }}">
  627.                 <input type="hidden" name="nom_client" value="{{ search_params.nom_client ?? '' }}">
  628.                 <input type="hidden" name="agence" value="{{ search_params.agence ?? '' }}">
  629.                 <input type="hidden" name="date_debut" value="{% if search_params.date_debut is defined and search_params.date_debut %}{{ search_params.date_debut|date('Y-m-d') }}{% endif %}">
  630.                 <input type="hidden" name="date_fin" value="{% if search_params.date_fin is defined and search_params.date_fin %}{{ search_params.date_fin|date('Y-m-d') }}{% endif %}">
  631.             {{ form_end(form) }}
  632.         </div>
  633.     </div>
  634.     
  635.     <!-- Results -->
  636.     {% if results is not empty %}
  637.         <div class="results-card fade-in" style="animation-delay: 0.1s">
  638.             <div class="card-header d-flex justify-content-between align-items-center">
  639.                 <h5><i class="fas fa-list-check me-2"></i>Résultats de la recherche</h5>
  640.                 <div class="d-flex align-items-center">
  641.                     <span class="me-3" style="color: var(--primary-color); font-weight: 600;">
  642.                         <i class="fas fa-calendar-check me-1"></i>
  643.                         {{ results|length }} jour{{ results|length > 1 ? 's' : '' }}
  644.                     </span>
  645.                     <div class="action-buttons">
  646.                         {# <button type="button" class="action-btn" id="export-pdf" title="Exporter PDF">
  647.                             <i class="fas fa-file-pdf"></i>
  648.                         </button>
  649.                         <button type="button" class="action-btn" id="copy-data" title="Copier les données">
  650.                             <i class="fas fa-copy"></i>
  651.                         </button> #}
  652.                     </div>
  653.                 </div>
  654.             </div>
  655.             
  656.             <div class="table-container">
  657.                 <table class="table table-hover table-striped">
  658.                     <thead>
  659.                         <tr>
  660.                             <th>Date</th>
  661.                             <th>Conducteur</th>
  662.                             <th>Véhicule</th>
  663.                             <th colspan="2" class="text-center">Tranche 1</th>
  664.                             <th colspan="2" class="text-center">Tranche 2</th>
  665.                             <th colspan="2" class="text-center">Tranche 3</th>
  666.                             <th colspan="2" class="text-center">Tranche 4</th>
  667.                             <th>Total</th>
  668.                             <th>Centièmes</th>
  669.                             <th>Vac.</th>
  670.                             <th>Forfait</th>
  671.                             <th>Déduc.</th>
  672.                             <th>Δ Delta</th>
  673.                             <th>Payé</th>
  674.                             <th>Centièmes payés</th>
  675.                         </tr>
  676.                     </thead>
  677.                     <tbody>
  678.                         {% for day in results %}
  679.                             <tr>
  680.                                 <td class="date-cell">
  681.                                     {{ day.date|date('d/m/Y') }}
  682.                                     <span class="weekday">{{ day.date|date('D')|trans }}</span>
  683.                                 </td>
  684.                                 <td><strong>{{ day.nom_client }}</strong></td>
  685.                                 <td><span class="badge bg-secondary">{{ day.immatriculation }}</span></td>
  686.                                 
  687.                                 {# Tranches horaires #}
  688.                                 {% for tranche in day.tranches %}
  689.                                     <td class="time-cell">{{ tranche.debut ?? '-' }}</td>
  690.                                     <td class="time-cell">{{ tranche.fin ?? '-' }}</td>
  691.                                 {% endfor %}
  692.                                 
  693.                                 {# Si moins de 4 tranches, ajouter des cellules vides #}
  694.                                 {% for i in day.tranches|length..3 %}
  695.                                     <td class="time-cell text-muted">-</td>
  696.                                     <td class="time-cell text-muted">-</td>
  697.                                 {% endfor %}
  698.                                 
  699.                                 {# Calculs #}
  700.                                 <td class="time-cell highlight-cell"><strong>{{ day.total_worked }}</strong></td>
  701.                                 <td class="number-cell">{{ day.total_centièmes|number_format(2, ',', ' ') }}</td>
  702.                                 <td class="text-center">
  703.                                     <span class="vacation-badge vacation-{{ day.nb_vacations }}" 
  704.                                           title="{{ day.nb_vacations }} vacation(s)">
  705.                                         {{ day.nb_vacations }}
  706.                                     </span>
  707.                                 </td>
  708.                                 <td class="time-cell">{{ day.forfait }}</td>
  709.                                 <td class="time-cell">{{ day.deduction }}</td>
  710.                                 <td class="time-cell">
  711.                                     <span class="{% if day.delta|replace({':': ''})|default(0) > 0 %}delta-positive{% else %}delta-negative{% endif %}">
  712.                                         {{ day.delta }}
  713.                                     </span>
  714.                                 </td>
  715.                                 <td class="time-cell highlight-cell"><strong>{{ day.paid_hours }}</strong></td>
  716.                                 <td class="number-cell">{{ day.paid_centièmes|number_format(2, ',', ' ') }}</td>
  717.                             </tr>
  718.                         {% endfor %}
  719.                         
  720.                         {# Ligne des totaux #}
  721.                         {% if totals is not empty %}
  722.                             <tr class="total-row">
  723.                                 <td colspan="11" class="text-end fw-bold">TOTAUX :</td>
  724.                                 <td class="time-cell fw-bold">{{ totals.total_worked }}</td>
  725.                                 <td class="number-cell fw-bold">{{ totals.total_centièmes|number_format(2, ',', ' ') }}</td>
  726.                                 <td class="text-center fw-bold">{{ totals.total_vacations }}</td>
  727.                                 <td class="time-cell fw-bold">{{ totals.total_forfait }}</td>
  728.                                 <td class="time-cell fw-bold">{{ totals.total_deduction }}</td>
  729.                                 <td class="time-cell fw-bold">
  730.                                     <span class="{% if totals.total_delta|replace({':': ''})|default(0) > 0 %}delta-positive{% else %}delta-negative{% endif %}">
  731.                                         {{ totals.total_delta }}
  732.                                     </span>
  733.                                 </td>
  734.                                 <td class="time-cell fw-bold">{{ totals.total_paid }}</td>
  735.                                 <td class="number-cell fw-bold">{{ totals.total_paid_centièmes|number_format(2, ',', ' ') }}</td>
  736.                             </tr>
  737.                         {% endif %}
  738.                     </tbody>
  739.                 </table>
  740.             </div>
  741.             
  742.             <div class="card-footer">
  743.                 <div class="row align-items-center">
  744.                     <div class="col-md-8">
  745.                         <small class="text-muted">
  746.                             <i class="fas fa-circle-info me-1"></i>
  747.                             Système de calcul avancé • Δ Delta = Différence heures travaillées/payées
  748.                         </small>
  749.                     </div>
  750.                     <div class="col-md-4 text-end">
  751.                         <small class="text-muted">
  752.                             <i class="fas fa-clock me-1"></i>
  753.                             Généré le {{ "now"|date('d/m/Y à H:i') }}
  754.                         </small>
  755.                     </div>
  756.                 </div>
  757.             </div>
  758.         </div>
  759.         
  760.         <!-- Stats Cards -->
  761.         <div class="stats-grid fade-in" style="animation-delay: 0.2s">
  762.             <div class="stat-card work-hours">
  763.                 <div class="stat-icon">
  764.                     <i class="fas fa-clock"></i>
  765.                 </div>
  766.                 <div class="stat-title">Heures travaillées</div>
  767.                 <div class="stat-value text-primary">{{ totals.total_worked }}</div>
  768.                 <div class="stat-subtitle">{{ totals.total_centièmes|number_format(2, ',', ' ') }} centièmes</div>
  769.             </div>
  770.             
  771.             <div class="stat-card paid-hours">
  772.                 <div class="stat-icon">
  773.                     <i class="fas fa-money-bill-wave"></i>
  774.                 </div>
  775.                 <div class="stat-title">Heures payées</div>
  776.                 <div class="stat-value text-success">{{ totals.total_paid }}</div>
  777.                 <div class="stat-subtitle">{{ totals.total_paid_centièmes|number_format(2, ',', ' ') }} centièmes</div>
  778.             </div>
  779.             
  780.             <div class="stat-card vacations">
  781.                 <div class="stat-icon">
  782.                     <i class="fas fa-calendar-alt"></i>
  783.                 </div>
  784.                 <div class="stat-title">Total vacations</div>
  785.                 <div class="stat-value" style="color: #f59e0b;">{{ totals.total_vacations }}</div>
  786.                 <div class="stat-subtitle">Réparties sur {{ results|length }} jour{{ results|length > 1 ? 's' : '' }}</div>
  787.             </div>
  788.             
  789.             <div class="stat-card delta">
  790.                 <div class="stat-icon">
  791.                     <i class="fas fa-balance-scale"></i>
  792.                 </div>
  793.                 <div class="stat-title">Balance Δ</div>
  794.                 <div class="stat-value {% if totals.total_delta|replace({':': ''})|default(0) > 0 %}text-success{% else %}text-danger{% endif %}">
  795.                     {{ totals.total_delta }}
  796.                 </div>
  797.                 <div class="stat-subtitle">
  798.                     {% if totals.total_delta|replace({':': ''})|default(0) > 0 %}
  799.                         <i class="fas fa-arrow-up me-1"></i> Excédent favorable
  800.                     {% elseif totals.total_delta|replace({':': ''})|default(0) < 0 %}
  801.                         <i class="fas fa-arrow-down me-1"></i> Déficit à combler
  802.                     {% else %}
  803.                         <i class="fas fa-equals me-1"></i> Équilibre parfait
  804.                     {% endif %}
  805.                 </div>
  806.             </div>
  807.         </div>
  808.         
  809.         <!-- Summary Note -->
  810.         <div class="alert alert-info mt-4 fade-in" style="animation-delay: 0.3s">
  811.             <div class="d-flex">
  812.                 <i class="fas fa-chart-pie me-3 fs-4" style="color: var(--info-color);"></i>
  813.                 <div>
  814.                     <h6 class="alert-heading mb-2">Synthèse de l'analyse</h6>
  815.                     <p class="mb-0">
  816.                         L'analyse révèle un taux d'efficacité de 
  817.                         <strong>
  818.                             {% if totals.total_worked|replace({':': ''})|default(0) > 0 %}
  819.                                 {{ ((totals.total_paid|replace({':': ''}) / totals.total_worked|replace({':': ''})) * 100)|number_format(1) }}%
  820.                             {% else %}
  821.                                 0%
  822.                             {% endif %}
  823.                         </strong> 
  824.                         entre les heures travaillées et payées. 
  825.                         {% if totals.total_delta|replace({':': ''})|default(0) > 0 %}
  826.                             <span class="text-success">Excellent résultat avec un excédent favorable.</span>
  827.                         {% elseif totals.total_delta|replace({':': ''})|default(0) < 0 %}
  828.                             <span class="text-danger">Attention au déficit qui nécessite une attention particulière.</span>
  829.                         {% else %}
  830.                             <span class="text-info">Équilibre parfait entre travail et rémunération.</span>
  831.                         {% endif %}
  832.                     </p>
  833.                 </div>
  834.             </div>
  835.         </div>
  836.         
  837.     {% elseif form_submitted %}
  838.         <div class="alert alert-warning fade-in">
  839.             <div class="d-flex align-items-center">
  840.                 <i class="fas fa-search-minus me-3 fs-4"></i>
  841.                 <div>
  842.                     <h6 class="alert-heading mb-1">Aucun résultat trouvé</h6>
  843.                     <p class="mb-0">Modifiez vos critères de recherche ou élargissez la période pour obtenir des résultats.</p>
  844.                 </div>
  845.             </div>
  846.         </div>
  847.         
  848.         <div class="text-center mt-4">
  849.             <button class="btn btn-outline-primary" id="show-all-data">
  850.                 <i class="fas fa-eye me-2"></i>Afficher toutes les données
  851.             </button>
  852.         </div>
  853.     {% endif %}
  854. </div>
  855. {% endblock %}
  856. {% block javascripts %}
  857.     {{ parent() }}
  858.     <script src="https://cdn.jsdelivr.net/npm/bootstrap-select@1.13.14/dist/js/bootstrap-select.min.js"></script>
  859.     <script>
  860.         document.addEventListener('DOMContentLoaded', function() {
  861.             // Réinitialisation du formulaire
  862.             document.getElementById('reset-form').addEventListener('click', function(e) {
  863.                 e.preventDefault();
  864.                 document.getElementById('search-form').reset();
  865.                 // Mettre les dates par défaut
  866.                 const today = new Date();
  867.                 const firstDay = new Date(today.getFullYear(), today.getMonth(), 1);
  868.                 
  869.                 document.querySelector('#{{ form.date_debut.vars.id }}').value = 
  870.                     firstDay.toISOString().split('T')[0];
  871.                 document.querySelector('#{{ form.date_fin.vars.id }}').value = 
  872.                     today.toISOString().split('T')[0];
  873.             });
  874.             
  875.             // Afficher toutes les données
  876.             const showAllBtn = document.getElementById('show-all-data');
  877.             if (showAllBtn) {
  878.                 showAllBtn.addEventListener('click', function() {
  879.                     document.getElementById('search-form').reset();
  880.                     document.getElementById('search-form').submit();
  881.                 });
  882.             }
  883.             
  884.             // Impression
  885.             document.getElementById('print-btn').addEventListener('click', function() {
  886.                 window.print();
  887.             });
  888.             
  889.             // Copie des données
  890.             document.getElementById('copy-data').addEventListener('click', function() {
  891.                 const table = document.querySelector('.table');
  892.                 let text = '';
  893.                 
  894.                 // En-têtes
  895.                 const headers = table.querySelectorAll('thead th');
  896.                 const headerText = Array.from(headers).map(th => th.textContent.trim()).join('\t');
  897.                 text += headerText + '\n';
  898.                 
  899.                 // Données
  900.                 const rows = table.querySelectorAll('tbody tr');
  901.                 rows.forEach(row => {
  902.                     const cells = row.querySelectorAll('td');
  903.                     const rowText = Array.from(cells).map(cell => cell.textContent.trim()).join('\t');
  904.                     text += rowText + '\n';
  905.                 });
  906.                 
  907.                 // Copie dans le presse-papier
  908.                 navigator.clipboard.writeText(text).then(() => {
  909.                     const originalHTML = this.innerHTML;
  910.                     this.innerHTML = '<i class="fas fa-check"></i>';
  911.                     this.style.background = 'var(--success-color)';
  912.                     this.style.color = 'white';
  913.                     
  914.                     setTimeout(() => {
  915.                         this.innerHTML = originalHTML;
  916.                         this.style.background = '';
  917.                         this.style.color = '';
  918.                     }, 2000);
  919.                 });
  920.             });
  921.             
  922.             // Bouton d'aide
  923.             document.getElementById('help-btn').addEventListener('click', function() {
  924.                 alert("ℹ️ AIDE\n\n• Utilisez les filtres pour affiner votre recherche\n• Le delta (Δ) montre la différence entre heures travaillées et payées\n• Exportez les données en Excel pour analyse approfondie\n• Les vacations sont colorées pour une lecture rapide");
  925.             });
  926.             
  927.             // Export PDF (simulé)
  928.             document.getElementById('export-pdf').addEventListener('click', function() {
  929.                 alert("📄 La fonction d'export PDF sera bientôt disponible !");
  930.             });
  931.             
  932.             // Mise à jour des champs cachés pour l'export
  933.             document.getElementById('search-form').addEventListener('submit', function() {
  934.                 this.querySelector('input[name="immatriculation"]').value = 
  935.                     this.querySelector('#{{ form.immatriculation.vars.id }}').value;
  936.                 this.querySelector('input[name="nom_client"]').value = 
  937.                     this.querySelector('#{{ form.nom_client.vars.id }}').value;
  938.                 this.querySelector('input[name="agence"]').value = 
  939.                     this.querySelector('#{{ form.agence.vars.id }}').value;
  940.                 this.querySelector('input[name="date_debut"]').value = 
  941.                     this.querySelector('#{{ form.date_debut.vars.id }}').value;
  942.                 this.querySelector('input[name="date_fin"]').value = 
  943.                     this.querySelector('#{{ form.date_fin.vars.id }}').value;
  944.             });
  945.             
  946.             // Animation des cartes au survol
  947.             const statCards = document.querySelectorAll('.stat-card');
  948.             statCards.forEach(card => {
  949.                 card.addEventListener('mouseenter', function() {
  950.                     this.style.transform = 'translateY(-8px) scale(1.02)';
  951.                 });
  952.                 
  953.                 card.addEventListener('mouseleave', function() {
  954.                     this.style.transform = 'translateY(0) scale(1)';
  955.                 });
  956.             });
  957.             
  958.             // Animation des badges de vacations
  959.             const vacationBadges = document.querySelectorAll('.vacation-badge');
  960.             vacationBadges.forEach(badge => {
  961.                 badge.addEventListener('mouseenter', function() {
  962.                     this.style.transform = 'scale(1.15) rotate(5deg)';
  963.                 });
  964.                 
  965.                 badge.addEventListener('mouseleave', function() {
  966.                     this.style.transform = 'scale(1) rotate(0)';
  967.                 });
  968.             });
  969.             
  970.             // Effet de focus sur les champs de formulaire
  971.             const formInputs = document.querySelectorAll('.form-control');
  972.             formInputs.forEach(input => {
  973.                 input.addEventListener('focus', function() {
  974.                     this.parentElement.style.transform = 'translateY(-2px)';
  975.                 });
  976.                 
  977.                 input.addEventListener('blur', function() {
  978.                     this.parentElement.style.transform = 'translateY(0)';
  979.                 });
  980.             });
  981.         });
  982.     </script>
  983. {% endblock %}