diff options
Diffstat (limited to 'app/views')
-rw-r--r-- | app/views/dashboard/_nutrient_measurements.html.erb | 22 | ||||
-rw-r--r-- | app/views/dashboard/_nutrient_measurements_table.html.erb | 18 | ||||
-rw-r--r-- | app/views/dashboard/_nutrient_target_table.html.erb | 74 | ||||
-rw-r--r-- | app/views/dashboard/_raft_allocation.html.erb | 2 | ||||
-rw-r--r-- | app/views/dashboard/_target_table.html.erb | 3 | ||||
-rw-r--r-- | app/views/dashboard/index.html.erb | 8 | ||||
-rw-r--r-- | app/views/layouts/application.html.erb | 5 | ||||
-rw-r--r-- | app/views/nutrient_measurement/index.html.erb | 27 | ||||
-rw-r--r-- | app/views/nutrient_measurements/_form.html.erb | 49 | ||||
-rw-r--r-- | app/views/nutrient_measurements/index.html.erb | 41 | ||||
-rw-r--r-- | app/views/nutrient_measurements/new.html.erb | 11 | ||||
-rw-r--r-- | app/views/targets/create.html.erb | 2 | ||||
-rw-r--r-- | app/views/targets/edit.html.erb | 2 | ||||
-rw-r--r-- | app/views/targets/index.html.erb | 68 | ||||
-rw-r--r-- | app/views/targets/new.html.erb | 85 | ||||
-rw-r--r-- | app/views/targets/show.html.erb | 3 | ||||
-rw-r--r-- | app/views/targets/update.html.erb | 2 |
17 files changed, 364 insertions, 58 deletions
diff --git a/app/views/dashboard/_nutrient_measurements.html.erb b/app/views/dashboard/_nutrient_measurements.html.erb deleted file mode 100644 index bc63a60..0000000 --- a/app/views/dashboard/_nutrient_measurements.html.erb +++ /dev/null @@ -1,22 +0,0 @@ -<div class="card shadow mb-4"> - <div class="card-header d-flex justify-content-between align-items-center"> - <h5 class="mb-0">Nutrient Measurements</h5> - <div class="btn-group"> - <%#= link_to "Add new measurement", editor_rafts_path, class: "btn btn-sm btn-primary" %> - <%#= link_to "View all", editor_rafts_path, class: "btn btn-sm btn-secondary" %> - </div> - </div> - - <div class="card-body p-0"> - <div class="container mb-3"> - <%= line_chart @npk_measurement_data, - title: "NPK", - ytitle: "Concentration (mg/L)" %> - </div> - <div class="container mb-3"> - <%= line_chart @ammonium_measurement_data, - title: "Ammonium", - ytitle: "Concentration (mg/L)" %> - </div> - </div> -</div> diff --git a/app/views/dashboard/_nutrient_measurements_table.html.erb b/app/views/dashboard/_nutrient_measurements_table.html.erb new file mode 100644 index 0000000..82ea6ff --- /dev/null +++ b/app/views/dashboard/_nutrient_measurements_table.html.erb @@ -0,0 +1,18 @@ +<div class="card shadow my-3"> + <div class="card-header d-flex justify-content-between align-items-center"> + <h4 class="mb-0">Relevé des Nutriments</h4> + <div class="btn-group"> + <%= link_to "Ajouter un relevé", new_nutrient_measurement_path, class: "btn btn-sm btn-primary" %> + <%= link_to "Liste des relevés", nutrient_measurements_path, class: "btn btn-sm btn-secondary" %> + </div> + </div> + + <div class="card-body"> + <%= line_chart @npk_measurement_data, + title: "NPK", + ytitle: "Concentration (mg/L)" %> + <%= line_chart @ammonium_measurement_data, + title: "Ammonium", + ytitle: "Concentration (mg/L)" %> + </div> +</div> diff --git a/app/views/dashboard/_nutrient_target_table.html.erb b/app/views/dashboard/_nutrient_target_table.html.erb new file mode 100644 index 0000000..7cf294c --- /dev/null +++ b/app/views/dashboard/_nutrient_target_table.html.erb @@ -0,0 +1,74 @@ +<div class="card shadow my-3"> + <div class="card-header d-flex justify-content-between align-items-center"> + <h4 class="mb-0">Complémentation</h4> + <div class="btn-group"> + <%= link_to "Nouvelle cible", new_target_path, class: "btn btn-sm btn-primary" %> + <%= link_to "Voir la recette", root_path, class: "btn btn-sm btn-secondary" %> + </div> + </div> + + <div class="card-body p-0"> + <div class="table-responsive"> + <table class="table table-sm table-striped table-hover align-middle mb-0"> + <thead class="table-light"> + <tr> + <th>Nutriment</th> + <th class="text-end">Relevé</th> + <th class="text-end">Cible</th> + <th class="text-end">Delta</th> + </tr> + </thead> + <tbody> + <% wr = @weighted || {} %> + <% lm = @latest_measurements || {} %> + + <% keys = (wr.keys + lm.keys).map(&:to_s).uniq.sort %> + <% keys.each do |nut| %> + <% measured = lm[nut] %> + <% target = wr[nut] %> + <% delta = (measured.to_f - target.to_f) if measured || target %> + <tr> + <td class="fw-semibold"><%= nut.upcase %></td> + + <td class="text-end"> + <% if measured.nil? %> + <span class="text-muted">—</span> + <% else %> + <%= number_with_precision(measured, precision: 2) %> + <% end %> + </td> + + <td class="text-end"> + <% if target.nil? %> + <span class="text-muted">—</span> + <% else %> + <%= number_with_precision(target, precision: 2) %> + <% end %> + </td> + + <td class="text-end"> + <% if measured.nil? && target.nil? %> + <span class="text-muted">—</span> + <% else %> + <% badge = + if delta.nil? + "text-bg-secondary" + elsif delta.abs <= 0.01 + "text-bg-success" + elsif delta > 0 + "text-bg-warning" + else + "text-bg-danger" + end %> + <span class="badge <%= badge %>"> + <%= number_with_precision(delta.to_f, precision: 2) %> + </span> + <% end %> + </td> + </tr> + <% end %> + </tbody> + </table> + </div> + </div> +</div> diff --git a/app/views/dashboard/_raft_allocation.html.erb b/app/views/dashboard/_raft_allocation.html.erb index a2128ea..1c9ef9a 100644 --- a/app/views/dashboard/_raft_allocation.html.erb +++ b/app/views/dashboard/_raft_allocation.html.erb @@ -1,7 +1,7 @@ <div class="card shadow mb-4"> <div class="card-header d-flex justify-content-between align-items-center"> <h5 class="mb-0">Crop Allocation</h5> - <%= link_to "Edit allocation", beds_path, class: "btn btn-sm btn-primary" %> + <%#= link_to "Edit allocation", beds_path, class: "btn btn-sm btn-primary" %> </div> <%= bar_chart @raft_data, stacked: true %> diff --git a/app/views/dashboard/_target_table.html.erb b/app/views/dashboard/_target_table.html.erb index b8f7e66..b1553dc 100644 --- a/app/views/dashboard/_target_table.html.erb +++ b/app/views/dashboard/_target_table.html.erb @@ -9,8 +9,7 @@ <thead class="table-light"> <tr> <th scope="col" class="text-nowrap">Nutrient</th> - <th scope="col" class="text-end"> Latest (mg/L) - </th> + <th scope="col" class="text-end"> Latest (mg/L)</th> <th scope="col" class="text-end">Target (mg/L)</th> <th scope="col" class="text-end">Δ %</th> </tr> diff --git a/app/views/dashboard/index.html.erb b/app/views/dashboard/index.html.erb index b1b2d87..a954e4c 100644 --- a/app/views/dashboard/index.html.erb +++ b/app/views/dashboard/index.html.erb @@ -1,7 +1,9 @@ <h1 class="display-1">Ferti</h1> -<%= render "raft_allocation" %> +<%= render "nutrient_target_table", nutrient_profiles: @nutrient_profiles %> -<%= render "target_table" %> +<%#= render "raft_allocation" %> -<%= render "nutrient_measurements" %> +<%#= render "target_table" %> + +<%= render "nutrient_measurements_table" %> diff --git a/app/views/layouts/application.html.erb b/app/views/layouts/application.html.erb index bf61c9d..dc61670 100644 --- a/app/views/layouts/application.html.erb +++ b/app/views/layouts/application.html.erb @@ -14,8 +14,8 @@ <%#= tag.link rel: "manifest", href: pwa_manifest_path(format: :json) %> <link rel="icon" href="/icon.png" type="image/png"> - <link rel="icon" href="/icon.svg" type="image/svg+xml"> - <link rel="apple-touch-icon" href="/icon.png"> + <!-- <link rel="icon" href="/icon.svg" type="image/svg+xml"> --> + <!-- <link rel="apple-touch-icon" href="/icon.png"> --> <%# Includes all stylesheet files in app/assets/stylesheets %> <%= stylesheet_link_tag :app, "data-turbo-track": "reload" %> @@ -25,7 +25,6 @@ <script src="https://cdn.jsdelivr.net/npm/chart.js"></script> <script src="https://cdn.jsdelivr.net/npm/chartkick@5"></script> - <script src="https://cdn.jsdelivr.net/npm/chartjs-adapter-date-fns/dist/chartjs-adapter-date-fns.bundle.min.js"></script> <%= javascript_importmap_tags %> diff --git a/app/views/nutrient_measurement/index.html.erb b/app/views/nutrient_measurement/index.html.erb deleted file mode 100644 index d9f522e..0000000 --- a/app/views/nutrient_measurement/index.html.erb +++ /dev/null @@ -1,27 +0,0 @@ -<h1>NutrientMeasurement#index</h1> -<p>Find me in app/views/nutrient_measurement/index.html.erb</p> - -<div class="table-responsive"> - <table class="table table-sm table-striped table-hover align-middle table-nutrient mb-0"> - <thead class="table-light"> - <tr> - <th>Date</th> - <th class="numeric" title="Total N = NO₃‑N + NH₄‑N">N (total)</th> - <th class="numeric">P</th> - <th class="numeric">K</th> - <th class="numeric" title="Ammonia nitrogen">NH₄‑N</th> - </tr> - </thead> - <tbody> - <% @measurements.each do |m| %> - <tr> - <td><%= l(m.measured_on) %></td> - <td class="numeric"><%= fmt2(total_n(m)) %></td> - <td class="numeric"><%= fmt2(m.p) %></td> - <td class="numeric"><%= fmt2(m.k) %></td> - <td class="numeric"><%= fmt2(m.nnh4) %></td> - </tr> - <% end %> - </tbody> - </table> -</div> diff --git a/app/views/nutrient_measurements/_form.html.erb b/app/views/nutrient_measurements/_form.html.erb new file mode 100644 index 0000000..9689c57 --- /dev/null +++ b/app/views/nutrient_measurements/_form.html.erb @@ -0,0 +1,49 @@ +<%= form_with(model: nutrient_measurement) do |form| %> + <% if nutrient_measurement.errors.any? %> + <div class="alert alert-danger"> + <p class="mb-1"><strong><%= pluralize(nutrient_measurement.errors.count, "erreur") %></strong> empêchent l’enregistrement :</p> + <ul class="mb-0"> + <% nutrient_measurement.errors.full_messages.each do |msg| %> + <li><%= msg %></li> + <% end %> + </ul> + </div> + <% end %> + + <div class="mb-3"> + <%= form.label :measured_on, "Date du relevé", class: "form-label" %> + <%= form.date_field :measured_on, class: "form-control", required: true %> + </div> + + <h2 class="h6 mt-4 mb-2">Concentrations de nutriments — laisser vide si non mesuré</h2> + + <div class="row g-2"> + <% # You can reorganize into macros/micros if you prefer %> + <% labels = { + nno3: "Nitrate (N-NO₃)", p: "Phosphore (P)", k: "Potassium (K)", + ca: "Calcium (Ca)", mg: "Magnésium (Mg)", s: "Soufre (S)", + na: "Sodium (Na)", cl: "Chlore (Cl)", si: "Silicium (Si)", + fe: "Fer (Fe)", zn: "Zinc (Zn)", b: "Bore (B)", + mn: "Manganèse (Mn)", cu: "Cuivre (Cu)", mo: "Molybdène (Mo)", + nnh4: "Ammonium (N-NH₄)" + } %> + + <% NutrientMeasurement::NUTRIENT_FIELDS.each do |field| %> + <div class="col-6 col-md-3"> + <div class="input-group"> + <%= form.number_field field, + class: "form-control", + placeholder: "—", + step: "0.01", + min: "0" %> + <span class="input-group-text">mg/L</span> + </div> + <label class="form-label d-block small text-muted mt-1"><%= labels[field] %></label> + </div> + <% end %> + </div> + + <div> + <%= form.submit "Ajouter le relevé", class: "btn btn-primary" %> + </div> +<% end %> diff --git a/app/views/nutrient_measurements/index.html.erb b/app/views/nutrient_measurements/index.html.erb new file mode 100644 index 0000000..c01d0cc --- /dev/null +++ b/app/views/nutrient_measurements/index.html.erb @@ -0,0 +1,41 @@ +<% content_for :title, "Liste des Relevé" %> + +<h1 class="display-1">Liste des Relevés</h1> + +<div class="d-flex justify-content-between align-items-center mb-3"> + <div class="btn-group"> + <%= link_to "Nouvelle mesure", new_nutrient_measurement_path, class: "btn btn-primary" %> + <%= link_to "Retour", root_path, class: "btn btn-outline-secondary" %> + </div> +</div> + +<div class="table-responsive"> + <table class="table table-sm table-striped table-hover align-middle table-nutrient mb-0"> + <thead class="table-light"> + <tr> + <th>Date</th> + <th class="text-end" title="Total N = NO₃-N + NH₄-N">N (total)</th> + <th class="text-end">P</th> + <th class="text-end">K</th> + <th class="text-end" title="Ammonia nitrogen">NH₄-N</th> + </tr> + </thead> + <tbody> + <% @nutrient_measurements.each do |m| %> + <tr> + <td><%= l(m.measured_on) %></td> + <td class="text-end"> + <%= number_with_precision(m.nno3.to_f + m.nnh4.to_f, precision: 2) if m.nno3 || m.nnh4 %> + </td> + <td class="text-end"><%= number_with_precision(m.p, precision: 2) if m.p %></td> + <td class="text-end"><%= number_with_precision(m.k, precision: 2) if m.k %></td> + <td class="text-end"><%= number_with_precision(m.nnh4, precision: 2) if m.nnh4 %></td> + </tr> + <% end %> + </tbody> + </table> +</div> + +<%= line_chart @npk_measurement_data, + title: "NPK", + ytitle: "Concentration (mg/L)" %> diff --git a/app/views/nutrient_measurements/new.html.erb b/app/views/nutrient_measurements/new.html.erb new file mode 100644 index 0000000..82a913e --- /dev/null +++ b/app/views/nutrient_measurements/new.html.erb @@ -0,0 +1,11 @@ +<% content_for :title, "Ajouter un Relevé" %> + +<h1 class="display-1">Ajouter un Relevé</h1> + +<%= render "form", nutrient_measurement: @nutrient_measurement %> + +<br> + +<div> + <%= link_to "Retour", root_path, class: "btn btn-secondary" %> +</div> diff --git a/app/views/targets/create.html.erb b/app/views/targets/create.html.erb new file mode 100644 index 0000000..51e2782 --- /dev/null +++ b/app/views/targets/create.html.erb @@ -0,0 +1,2 @@ +<h1>Targets#create</h1> +<p>Find me in app/views/targets/create.html.erb</p> diff --git a/app/views/targets/edit.html.erb b/app/views/targets/edit.html.erb new file mode 100644 index 0000000..849bf7f --- /dev/null +++ b/app/views/targets/edit.html.erb @@ -0,0 +1,2 @@ +<h1>Targets#edit</h1> +<p>Find me in app/views/targets/edit.html.erb</p> diff --git a/app/views/targets/index.html.erb b/app/views/targets/index.html.erb new file mode 100644 index 0000000..b552038 --- /dev/null +++ b/app/views/targets/index.html.erb @@ -0,0 +1,68 @@ +<h1 class="display-1">Cibles</h1> + +<div class="btn-group my-3"> + <%= link_to "Nouvelle Cible", new_target_path, class: "btn btn-primary" %> +</div> + +<div class="table-responsive"> + <table class="table table-sm table-striped table-hover align-middle mb-0"> + <thead class="table-light"> + <tr> + <th>Nom</th> + <th>Répartition</tr> + <th class="text-end" style="width: 140px;">Total %</th> + <th class="text-nowrap" style="width: 190px;">Créé le</th> + <th class="text-end" style="width: 180px;">Actions</th> + </tr> + </thead> + <tbody> + <% if @targets.present? %> + <% @targets.each do |t| %> + <% sum_pct = t.target_allocations.sum { |a| a.percentage.to_f } %> + <% badge_class = (sum_pct - 100.0).abs <= 0.01 ? "bg-success" : "bg-danger" %> + <tr> + <td class="fw-semibold"> + <%= link_to t.name.presence || "Objectif ##{t.id}", t %> + </td> + <td> + <% if t.target_allocations.empty? %> + <span class="text-muted">Aucune répartition définie</span> + <% else %> + <ul class="list-unstyled mb-0 d-flex flex-wrap gap-2"> + <% t.target_allocations.each do |a| %> + <li class="badge text-bg-light border"> + <%= a.nutrient_profile&.name || "Profil ##{a.nutrient_profile_id}" %> + — <%= number_with_precision(a.percentage.to_f, precision: 2) %>% + </li> + <% end %> + </ul> + <% end %> + </td> + <td class="text-end"> + <span class="badge <%= badge_class %>"> + <%= number_with_precision(sum_pct, precision: 2) %>% + </span> + </td> + <td class="text-nowrap"> + <%= l(t.created_at, format: :short) %> + </td> + <td class="text-end text-nowrap"> + <%= link_to "Voir", t, class: "btn btn-outline-secondary btn-sm" %> + <%= link_to "Modifier", edit_target_path(t), class: "btn btn-outline-primary btn-sm" %> + <%# FIXME: Doesn't work. %> + <%= link_to "Supprimer", t, class: "btn btn-outline-danger btn-sm", + data: { turbo_method: :delete, turbo_confirm: "Supprimer cet objectif ?" } %> + </td> + </tr> + <% end %> + <% else %> + <tr> + <td colspan="5" class="text-center py-4 text-muted"> + Aucun objectif pour le moment. + <%= link_to "Créer le premier", new_target_path %>. + </td> + </tr> + <% end %> + </tbody> + </table> +</div> diff --git a/app/views/targets/new.html.erb b/app/views/targets/new.html.erb new file mode 100644 index 0000000..42cb7bd --- /dev/null +++ b/app/views/targets/new.html.erb @@ -0,0 +1,85 @@ +<% content_for :title, "Ajouter une Cible" %> + +<h1 class="display-1">Ajouter une Cible</h1> + +<%= form_with(model: @target) do |f| %> + <div class="card shadow-sm"> + <div class="card-header"> + <%= f.text_field :name, class: "form-control", placeholder: "Nom de la cible" %> + </div> + + <div class="card-body p-0"> + <div class="table-responsive"> + <table class="table table-hover align-middle mb-0"> + <thead class="table-light"> + <tr> + <th>Profil</th> + <th class="text-end" style="width: 180px;">Proportion</th> + </tr> + </thead> + <tbody id="alloc-table-body"> + <%= f.fields_for :target_allocations do |af| %> + <% np = af.object.nutrient_profile %> + <tr> + <td> + <%= af.hidden_field :nutrient_profile_id %> + <strong><%= np&.name.capitalize || "Profil ##{af.object.nutrient_profile_id}" %></strong> + </td> + <td class="text-end"> + <div class="input-group input-group-sm" style="max-width: 160px; margin-left:auto;"> + <%= af.number_field :percentage, + in: 0..100, step: 0.5, + class: "form-control text-end alloc-input", + placeholder: "0.0", + data: { action: "input->alloc#sum" } %> + <span class="input-group-text">%</span> + </div> + </td> + </tr> + <% end %> + </tbody> + <tfoot> + <tr> + <td class="small text-muted">Ajustez chaque pourcentage pour totaliser 100%.</td> + <td class="text-end"> + <span class="badge bg-secondary" id="alloc-total">Total : 0%</span> + </td> + </tr> + </tfoot> + </table> + </div> + </div> + + <div class="card-footer d-flex gap-2 justify-content-end"> + <div class="btn-group"> + <%= f.submit "Enregistrer l’objectif", class: "btn btn-primary", id: "submit-btn" %> + <%= link_to "Annuler", targets_path, class: "btn btn-secondary" %> + </div> + </div> + </div> +<% end %> + +<script> + // Lightweight client-side sum check (no Stimulus required). + document.addEventListener("turbo:load", initAllocSum); + document.addEventListener("DOMContentLoaded", initAllocSum); + + function initAllocSum() { + const inputs = document.querySelectorAll(".alloc-input"); + const totalBadge = document.getElementById("alloc-total"); + const submitBtn = document.getElementById("submit-btn"); + if (!inputs.length || !totalBadge) return; + + function updateTotal() { + let sum = 0; + inputs.forEach(i => sum += parseFloat(i.value || "0")); + const rounded = Math.round(sum * 100) / 100; + totalBadge.textContent = `Total : ${rounded}%`; + totalBadge.className = "badge " + (Math.abs(rounded - 100) < 0.01 ? "bg-success" : "bg-danger"); + if (submitBtn) submitBtn.disabled = !(Math.abs(rounded - 100) < 0.01); + } + + inputs.forEach(i => i.addEventListener("input", updateTotal)); + updateTotal(); + } +</script> diff --git a/app/views/targets/show.html.erb b/app/views/targets/show.html.erb new file mode 100644 index 0000000..1525609 --- /dev/null +++ b/app/views/targets/show.html.erb @@ -0,0 +1,3 @@ +<h1>Targets#show</h1> + +<%# TODO: add table comparing this target with the most recent measurement. %> diff --git a/app/views/targets/update.html.erb b/app/views/targets/update.html.erb new file mode 100644 index 0000000..a39287c --- /dev/null +++ b/app/views/targets/update.html.erb @@ -0,0 +1,2 @@ +<h1>Targets#update</h1> +<p>Find me in app/views/targets/update.html.erb</p> |