1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
|
<!-- app/views/recipes/show.html.erb -->
<%# Controls header %>
<div class="card shadow my-4">
<div class="card-header d-flex flex-wrap gap-2 justify-content-between align-items-center">
<h5 class="mb-0">Ferti© Recipe</h5>
<div class="d-flex flex-wrap gap-2 align-items-center">
<%= form_with url: ferti_recipe_path, method: :get, local: true, class: "d-flex flex-wrap gap-2 align-items-center", id: "recipe-form" do %>
<div class="d-flex align-items-center gap-2">
<label class="mb-0 small text-nowrap" for="volume_select">Volume</label>
<select id="volume_select" name="volume" class="form-select form-select-sm">
<% options = [["100 000 L", 100_000], ["200 000 L", 200_000], ["300 000 L", 300_000]] %>
<% current_volume = (params[:volume].presence || 100_000).to_i %>
<% options.each do |label, val| %>
<option value="<%= val %>" <%= 'selected' if val == current_volume %>><%= label %></option>
<% end %>
</select>
</div>
<div class="vr" style="height: 24px;"></div>
<div class="d-flex align-items-center gap-2">
<label class="mb-0 small text-nowrap" for="portions_input">Portions</label>
<input id="portions_input" type="number" min="2" max="10" step="1"
class="form-control form-control-sm" value="<%= params[:portions].presence || 2 %>">
</div>
<% end %>
<%= link_to "Back", root_path, class: "btn btn-sm btn-secondary" %>
</div>
</div>
<div class="table-responsive">
<table class="table table-sm align-middle mb-0" id="recipe-table">
<thead class="table-light">
<tr>
<th>Product</th>
<th class="text-muted">Fertilizer</th>
<th class="text-end">Qty (kg)</th>
<th class="text-end">Per portion (kg)</th>
</tr>
</thead>
<tbody>
<% @recipe.each do |component, kg| %>
<% prod_name = commercial_name_for(component) %>
<tr data-total-kg="<%= kg %>">
<td><strong><%= prod_name %></strong></td>
<td class="text-muted"><small><%= component.name %></small></td>
<td class="text-end fw-semibold"><%= fmt_kg(kg) %></td>
<td class="text-end per-portion-cell"><%= fmt_kg(kg / (params[:portions].presence || 2).to_f) %></td>
</tr>
<% end %>
<% if @recipe.blank? %>
<tr><td colspan="4" class="text-center text-muted">No supplementation required.</td></tr>
<% end %>
</tbody>
</table>
</div>
<div class="card-footer small text-muted">
Recipe based on latest measurement: <%= @latest.measured_on %>
</div>
</div>
<%# Tiny vanilla JS: auto-submit on volume change; live per-portion update %>
<script>
(function() {
const form = document.getElementById('recipe-form');
const volumeSelect = document.getElementById('volume_select');
const portionsInput = document.getElementById('portions_input');
const rows = document.querySelectorAll('#recipe-table tbody tr[data-total-kg]');
function clampPortions(n) {
n = parseInt(n || 2, 10);
if (isNaN(n)) n = 2;
return Math.min(10, Math.max(2, n));
}
function updatePerPortion() {
const n = clampPortions(portionsInput.value);
portionsInput.value = n;
rows.forEach(row => {
const total = parseFloat(row.getAttribute('data-total-kg') || '0');
const cell = row.querySelector('.per-portion-cell');
const per = (n > 0) ? (total / n) : 0;
cell.textContent = formatKg(per);
});
}
function formatKg(v) {
// match Rails number_with_precision(... precision: 2, strip zeros-ish)
const s = (Math.round(v * 100) / 100).toFixed(2);
return s.replace(/\.00$/, '').replace(/(\.\d)0$/, '$1');
}
volumeSelect.addEventListener('change', () => {
// submit with selected volume, keeping portions as a query param
const portions = clampPortions(portionsInput.value);
const url = new URL(form.action, window.location.origin);
url.searchParams.set('volume', volumeSelect.value);
url.searchParams.set('portions', portions);
window.location.assign(url.toString());
});
portionsInput.addEventListener('input', updatePerPortion);
updatePerPortion();
})();
</script>
|