proyectos.html 5.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142
  1. {% extends "base.html" %}
  2. {% block content %}
  3. <div class="row">
  4. <div class="col-12">
  5. <div class="d-flex justify-content-between align-items-center mb-4">
  6. <h2>Simulaciones Guardadas</h2>
  7. <a href="{{ url_for('main.index') }}" class="btn btn-secondary">Volver al Inicio</a>
  8. </div>
  9. <!-- Filter Form -->
  10. <div class="card mb-4">
  11. <div class="card-body">
  12. <form action="{{ url_for('main.proyectos') }}" method="GET" class="row g-3 align-items-end">
  13. <div class="col-md-4">
  14. <label for="id_casa" class="form-label">Filtrar por Casa</label>
  15. <select name="id_casa" class="form-select" onchange="this.form.submit()">
  16. <option value="">Todas las casas</option>
  17. {% for casa in casas %}
  18. <option value="{{ casa.id_casa }}" {% if selected_casa == casa.id_casa %}selected{% endif %}>{{ casa.nombre }}</option>
  19. {% endfor %}
  20. </select>
  21. </div>
  22. <div class="col-md-2">
  23. <a href="{{ url_for('main.proyectos') }}" class="btn btn-outline-secondary">Limpiar Filtro</a>
  24. </div>
  25. </form>
  26. </div>
  27. </div>
  28. {% if proyectos %}
  29. <div class="card mb-4">
  30. <div class="card-header">
  31. Comparativa de Costos Mensuales: Sin Solar vs Con Solar
  32. </div>
  33. <div class="card-body">
  34. <canvas id="savingsChart" width="400" height="100"></canvas>
  35. </div>
  36. </div>
  37. <div class="table-responsive">
  38. <table class="table table-striped table-hover">
  39. <thead>
  40. <tr>
  41. <th>Fecha</th>
  42. <th>Cliente</th>
  43. <th>Ciudad</th>
  44. <th>Casa</th>
  45. <th>Panel</th>
  46. <th>Cantidad</th>
  47. <th>Energía (kWh/mes)</th>
  48. <th>Ahorro ($/mes)</th>
  49. </tr>
  50. </thead>
  51. <tbody>
  52. {% for proyecto in proyectos %}
  53. <tr>
  54. <td>{{ proyecto.fecha_referencia.strftime('%Y-%m-%d') }}</td>
  55. <td>{{ proyecto.nombre_cliente }}</td>
  56. <td>{{ proyecto.ciudad.nombre_ciudad }}</td>
  57. <td>{{ proyecto.casa.nombre if proyecto.casa else 'N/A' }}</td>
  58. <td>{{ proyecto.panel.marca }} {{ proyecto.panel.modelo }}</td>
  59. <td>{{ proyecto.cantidad_paneles }}</td>
  60. <td>{{ "%.2f"|format(proyecto.energia_estimada_mensual) }}</td>
  61. <td>{{ "%.2f"|format(proyecto.ahorro_estimado) if proyecto.ahorro_estimado else '-' }}</td>
  62. </tr>
  63. {% endfor %}
  64. </tbody>
  65. </table>
  66. </div>
  67. {% else %}
  68. <div class="alert alert-info">
  69. No hay simulaciones guardadas todavía.
  70. </div>
  71. {% endif %}
  72. </div>
  73. </div>
  74. {% endblock %}
  75. {% block scripts %}
  76. <script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
  77. <script>
  78. const ctx = document.getElementById('savingsChart').getContext('2d');
  79. const savingsChart = new Chart(ctx, {
  80. type: 'bar',
  81. data: {
  82. labels: {{ labels | tojson }},
  83. datasets: [
  84. {
  85. label: 'Costo Sin Solar ($)',
  86. data: {{ costo_sin_solar | tojson }},
  87. backgroundColor: 'rgba(220, 53, 69, 0.6)',
  88. borderColor: 'rgba(220, 53, 69, 1)',
  89. borderWidth: 1
  90. },
  91. {
  92. label: 'Costo Con Solar ($)',
  93. data: {{ costo_con_solar | tojson }},
  94. backgroundColor: 'rgba(25, 135, 84, 0.6)',
  95. borderColor: 'rgba(25, 135, 84, 1)',
  96. borderWidth: 1
  97. }
  98. ]
  99. },
  100. options: {
  101. scales: {
  102. y: {
  103. beginAtZero: true,
  104. title: {
  105. display: true,
  106. text: 'Costo Mensual ($)'
  107. }
  108. }
  109. },
  110. plugins: {
  111. tooltip: {
  112. callbacks: {
  113. footer: function(tooltipItems) {
  114. let sinSolar = 0;
  115. let conSolar = 0;
  116. tooltipItems.forEach(function(tooltipItem) {
  117. if (tooltipItem.datasetIndex === 0) {
  118. sinSolar = tooltipItem.raw;
  119. } else if (tooltipItem.datasetIndex === 1) {
  120. conSolar = tooltipItem.raw;
  121. }
  122. });
  123. // Nota: Como el tooltip muestra un item a la vez por defecto en modo 'nearest',
  124. // esto funciona mejor si el modo es 'index'.
  125. return '';
  126. }
  127. }
  128. }
  129. },
  130. interaction: {
  131. mode: 'index',
  132. intersect: false,
  133. }
  134. }
  135. });
  136. </script>
  137. {% endblock %}