From 52b044d6a4278c229992404ad5801769c2d13363 Mon Sep 17 00:00:00 2001 From: Marius Peter Date: Sun, 24 Aug 2025 20:29:54 +0200 Subject: First commit. Vive le Castel Peter ! --- app/views/application/_copyright_footer.html.erb | 10 ++ app/views/application/_navbar.html.erb | 33 +++++++ app/views/crops/_crop.html.erb | 92 ++++++++++++++++++ app/views/crops/_form.html.erb | 107 ++++++++++++++++++++ app/views/crops/edit.html.erb | 12 +++ app/views/crops/index.html.erb | 16 +++ app/views/crops/new.html.erb | 11 +++ app/views/crops/show.html.erb | 10 ++ app/views/dashboard/_raft_allocation.html.erb | 9 ++ app/views/dashboard/_recent_measurements.html.erb | 22 +++++ app/views/dashboard/_target_table.html.erb | 40 ++++++++ app/views/dashboard/index.html.erb | 7 ++ .../_fertilizer_product.html.erb | 12 +++ app/views/fertilizer_products/_form.html.erb | 27 ++++++ app/views/fertilizer_products/edit.html.erb | 12 +++ app/views/fertilizer_products/index.html.erb | 16 +++ app/views/fertilizer_products/new.html.erb | 11 +++ app/views/fertilizer_products/show.html.erb | 10 ++ app/views/layouts/application.html.erb | 46 +++++++++ app/views/layouts/mailer.html.erb | 13 +++ app/views/layouts/mailer.text.erb | 1 + app/views/nutrient_measurement/index.html.erb | 27 ++++++ app/views/pwa/manifest.json.erb | 22 +++++ app/views/pwa/service-worker.js | 26 +++++ app/views/rafts/editor.html.erb | 108 +++++++++++++++++++++ app/views/recipes/_table.html.erb | 32 ++++++ app/views/recipes/show.html.erb | 108 +++++++++++++++++++++ 27 files changed, 840 insertions(+) create mode 100644 app/views/application/_copyright_footer.html.erb create mode 100644 app/views/application/_navbar.html.erb create mode 100644 app/views/crops/_crop.html.erb create mode 100644 app/views/crops/_form.html.erb create mode 100644 app/views/crops/edit.html.erb create mode 100644 app/views/crops/index.html.erb create mode 100644 app/views/crops/new.html.erb create mode 100644 app/views/crops/show.html.erb create mode 100644 app/views/dashboard/_raft_allocation.html.erb create mode 100644 app/views/dashboard/_recent_measurements.html.erb create mode 100644 app/views/dashboard/_target_table.html.erb create mode 100644 app/views/dashboard/index.html.erb create mode 100644 app/views/fertilizer_products/_fertilizer_product.html.erb create mode 100644 app/views/fertilizer_products/_form.html.erb create mode 100644 app/views/fertilizer_products/edit.html.erb create mode 100644 app/views/fertilizer_products/index.html.erb create mode 100644 app/views/fertilizer_products/new.html.erb create mode 100644 app/views/fertilizer_products/show.html.erb create mode 100644 app/views/layouts/application.html.erb create mode 100644 app/views/layouts/mailer.html.erb create mode 100644 app/views/layouts/mailer.text.erb create mode 100644 app/views/nutrient_measurement/index.html.erb create mode 100644 app/views/pwa/manifest.json.erb create mode 100644 app/views/pwa/service-worker.js create mode 100644 app/views/rafts/editor.html.erb create mode 100644 app/views/recipes/_table.html.erb create mode 100644 app/views/recipes/show.html.erb (limited to 'app/views') diff --git a/app/views/application/_copyright_footer.html.erb b/app/views/application/_copyright_footer.html.erb new file mode 100644 index 0000000..4a6d240 --- /dev/null +++ b/app/views/application/_copyright_footer.html.erb @@ -0,0 +1,10 @@ + diff --git a/app/views/application/_navbar.html.erb b/app/views/application/_navbar.html.erb new file mode 100644 index 0000000..9fa250a --- /dev/null +++ b/app/views/application/_navbar.html.erb @@ -0,0 +1,33 @@ + diff --git a/app/views/crops/_crop.html.erb b/app/views/crops/_crop.html.erb new file mode 100644 index 0000000..e9b9b4d --- /dev/null +++ b/app/views/crops/_crop.html.erb @@ -0,0 +1,92 @@ +
+

+ Name: + <%= crop.name %> +

+ +

+ Crop type: + <%= crop.crop_type %> +

+ +

+ Nno3: + <%= crop.nno3 %> +

+ +

+ P: + <%= crop.p %> +

+ +

+ K: + <%= crop.k %> +

+ +

+ Ca: + <%= crop.ca %> +

+ +

+ Mg: + <%= crop.mg %> +

+ +

+ S: + <%= crop.s %> +

+ +

+ Na: + <%= crop.na %> +

+ +

+ Cl: + <%= crop.cl %> +

+ +

+ Si: + <%= crop.si %> +

+ +

+ Fe: + <%= crop.fe %> +

+ +

+ Zn: + <%= crop.zn %> +

+ +

+ B: + <%= crop.b %> +

+ +

+ Mn: + <%= crop.mn %> +

+ +

+ Cu: + <%= crop.cu %> +

+ +

+ Mo: + <%= crop.mo %> +

+ +

+ Nnh4: + <%= crop.nnh4 %> +

+ +
diff --git a/app/views/crops/_form.html.erb b/app/views/crops/_form.html.erb new file mode 100644 index 0000000..90226b6 --- /dev/null +++ b/app/views/crops/_form.html.erb @@ -0,0 +1,107 @@ +<%= form_with(model: crop) do |form| %> + <% if crop.errors.any? %> +
+

<%= pluralize(crop.errors.count, "error") %> prohibited this crop from being saved:

+ + +
+ <% end %> + +
+ <%= form.label :name, style: "display: block" %> + <%= form.text_field :name %> +
+ +
+ <%= form.label :crop_type, style: "display: block" %> + <%= form.number_field :crop_type %> +
+ +
+ <%= form.label :nno3, style: "display: block" %> + <%= form.text_field :nno3 %> +
+ +
+ <%= form.label :p, style: "display: block" %> + <%= form.text_field :p %> +
+ +
+ <%= form.label :k, style: "display: block" %> + <%= form.text_field :k %> +
+ +
+ <%= form.label :ca, style: "display: block" %> + <%= form.text_field :ca %> +
+ +
+ <%= form.label :mg, style: "display: block" %> + <%= form.text_field :mg %> +
+ +
+ <%= form.label :s, style: "display: block" %> + <%= form.text_field :s %> +
+ +
+ <%= form.label :na, style: "display: block" %> + <%= form.text_field :na %> +
+ +
+ <%= form.label :cl, style: "display: block" %> + <%= form.text_field :cl %> +
+ +
+ <%= form.label :si, style: "display: block" %> + <%= form.text_field :si %> +
+ +
+ <%= form.label :fe, style: "display: block" %> + <%= form.text_field :fe %> +
+ +
+ <%= form.label :zn, style: "display: block" %> + <%= form.text_field :zn %> +
+ +
+ <%= form.label :b, style: "display: block" %> + <%= form.text_field :b %> +
+ +
+ <%= form.label :mn, style: "display: block" %> + <%= form.text_field :mn %> +
+ +
+ <%= form.label :cu, style: "display: block" %> + <%= form.text_field :cu %> +
+ +
+ <%= form.label :mo, style: "display: block" %> + <%= form.text_field :mo %> +
+ +
+ <%= form.label :nnh4, style: "display: block" %> + <%= form.text_field :nnh4 %> +
+ +
+ <%= form.submit %> +
+<% end %> diff --git a/app/views/crops/edit.html.erb b/app/views/crops/edit.html.erb new file mode 100644 index 0000000..54c616c --- /dev/null +++ b/app/views/crops/edit.html.erb @@ -0,0 +1,12 @@ +<% content_for :title, "Editing crop" %> + +

Editing crop

+ +<%= render "form", crop: @crop %> + +
+ +
+ <%= link_to "Show this crop", @crop %> | + <%= link_to "Back to crops", crops_path %> +
diff --git a/app/views/crops/index.html.erb b/app/views/crops/index.html.erb new file mode 100644 index 0000000..bae09fa --- /dev/null +++ b/app/views/crops/index.html.erb @@ -0,0 +1,16 @@ +

<%= notice %>

+ +<% content_for :title, "Crops" %> + +

Crops

+ +
+ <% @crops.each do |crop| %> + <%= render crop %> +

+ <%= link_to "Show this crop", crop %> +

+ <% end %> +
+ +<%= link_to "New crop", new_crop_path %> diff --git a/app/views/crops/new.html.erb b/app/views/crops/new.html.erb new file mode 100644 index 0000000..4ef4da5 --- /dev/null +++ b/app/views/crops/new.html.erb @@ -0,0 +1,11 @@ +<% content_for :title, "New crop" %> + +

New crop

+ +<%= render "form", crop: @crop %> + +
+ +
+ <%= link_to "Back to crops", crops_path %> +
diff --git a/app/views/crops/show.html.erb b/app/views/crops/show.html.erb new file mode 100644 index 0000000..971f097 --- /dev/null +++ b/app/views/crops/show.html.erb @@ -0,0 +1,10 @@ +

<%= notice %>

+ +<%= render @crop %> + +
+ <%= link_to "Edit this crop", edit_crop_path(@crop) %> | + <%= link_to "Back to crops", crops_path %> + + <%= button_to "Destroy this crop", @crop, method: :delete %> +
diff --git a/app/views/dashboard/_raft_allocation.html.erb b/app/views/dashboard/_raft_allocation.html.erb new file mode 100644 index 0000000..ef95cdd --- /dev/null +++ b/app/views/dashboard/_raft_allocation.html.erb @@ -0,0 +1,9 @@ +
+
+
Raft Allocation
+ <%= link_to "Edit raft allocation", editor_rafts_path, class: "btn btn-sm btn-primary" %> +
+ + <%= bar_chart @raft_data, stacked: true %> +
+ diff --git a/app/views/dashboard/_recent_measurements.html.erb b/app/views/dashboard/_recent_measurements.html.erb new file mode 100644 index 0000000..bc63a60 --- /dev/null +++ b/app/views/dashboard/_recent_measurements.html.erb @@ -0,0 +1,22 @@ +
+
+
Nutrient Measurements
+
+ <%#= 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" %> +
+
+ +
+
+ <%= line_chart @npk_measurement_data, + title: "NPK", + ytitle: "Concentration (mg/L)" %> +
+
+ <%= line_chart @ammonium_measurement_data, + title: "Ammonium", + ytitle: "Concentration (mg/L)" %> +
+
+
diff --git a/app/views/dashboard/_target_table.html.erb b/app/views/dashboard/_target_table.html.erb new file mode 100644 index 0000000..b8f7e66 --- /dev/null +++ b/app/views/dashboard/_target_table.html.erb @@ -0,0 +1,40 @@ +
+
+
Target nutrient concentrations
+ <%= link_to "Get Ferti© recipe", ferti_recipe_path, class: "btn btn-sm btn-primary" %> +
+ +
+ + + + + + + + + + + <% NutrientsHelper::NUTRIENTS.each do |n| %> + <% latest = @latest_measurement[n] || 0 %> + <% target = @target[n] || 0 %> + <% delta = target - latest %> + + + + + + + <% end %> + +
Nutrient Latest (mg/L) + Target (mg/L)Δ %
<%= n.upcase %><%= fmt(latest) %><%= fmt(target) %> + + <%= fmt(delta) %>% + +
+
+ +
diff --git a/app/views/dashboard/index.html.erb b/app/views/dashboard/index.html.erb new file mode 100644 index 0000000..2902ada --- /dev/null +++ b/app/views/dashboard/index.html.erb @@ -0,0 +1,7 @@ +

Ferti

+ +<%= render "raft_allocation" %> + +<%= render "target_table" %> + +<%= render "recent_measurements" %> diff --git a/app/views/fertilizer_products/_fertilizer_product.html.erb b/app/views/fertilizer_products/_fertilizer_product.html.erb new file mode 100644 index 0000000..d84a03d --- /dev/null +++ b/app/views/fertilizer_products/_fertilizer_product.html.erb @@ -0,0 +1,12 @@ +
+

+ Name: + <%= fertilizer_product.name %> +

+ +

+ Purity: + <%= fertilizer_product.purity %> +

+ +
diff --git a/app/views/fertilizer_products/_form.html.erb b/app/views/fertilizer_products/_form.html.erb new file mode 100644 index 0000000..517fe09 --- /dev/null +++ b/app/views/fertilizer_products/_form.html.erb @@ -0,0 +1,27 @@ +<%= form_with(model: fertilizer_product) do |form| %> + <% if fertilizer_product.errors.any? %> +
+

<%= pluralize(fertilizer_product.errors.count, "error") %> prohibited this fertilizer_product from being saved:

+ + +
+ <% end %> + +
+ <%= form.label :name, style: "display: block" %> + <%= form.text_field :name %> +
+ +
+ <%= form.label :purity, style: "display: block" %> + <%= form.text_field :purity %> +
+ +
+ <%= form.submit %> +
+<% end %> diff --git a/app/views/fertilizer_products/edit.html.erb b/app/views/fertilizer_products/edit.html.erb new file mode 100644 index 0000000..aa88dad --- /dev/null +++ b/app/views/fertilizer_products/edit.html.erb @@ -0,0 +1,12 @@ +<% content_for :title, "Editing fertilizer product" %> + +

Editing fertilizer product

+ +<%= render "form", fertilizer_product: @fertilizer_product %> + +
+ +
+ <%= link_to "Show this fertilizer product", @fertilizer_product %> | + <%= link_to "Back to fertilizer products", fertilizer_products_path %> +
diff --git a/app/views/fertilizer_products/index.html.erb b/app/views/fertilizer_products/index.html.erb new file mode 100644 index 0000000..f624ffc --- /dev/null +++ b/app/views/fertilizer_products/index.html.erb @@ -0,0 +1,16 @@ +

<%= notice %>

+ +<% content_for :title, "Fertilizer products" %> + +

Fertilizer products

+ +
+ <% @fertilizer_products.each do |fertilizer_product| %> + <%= render fertilizer_product %> +

+ <%= link_to "Show this fertilizer product", fertilizer_product %> +

+ <% end %> +
+ +<%= link_to "New fertilizer product", new_fertilizer_product_path %> diff --git a/app/views/fertilizer_products/new.html.erb b/app/views/fertilizer_products/new.html.erb new file mode 100644 index 0000000..81aad79 --- /dev/null +++ b/app/views/fertilizer_products/new.html.erb @@ -0,0 +1,11 @@ +<% content_for :title, "New fertilizer product" %> + +

New fertilizer product

+ +<%= render "form", fertilizer_product: @fertilizer_product %> + +
+ +
+ <%= link_to "Back to fertilizer products", fertilizer_products_path %> +
diff --git a/app/views/fertilizer_products/show.html.erb b/app/views/fertilizer_products/show.html.erb new file mode 100644 index 0000000..ea97041 --- /dev/null +++ b/app/views/fertilizer_products/show.html.erb @@ -0,0 +1,10 @@ +

<%= notice %>

+ +<%= render @fertilizer_product %> + +
+ <%= link_to "Edit this fertilizer product", edit_fertilizer_product_path(@fertilizer_product) %> | + <%= link_to "Back to fertilizer products", fertilizer_products_path %> + + <%= button_to "Destroy this fertilizer product", @fertilizer_product, method: :delete %> +
diff --git a/app/views/layouts/application.html.erb b/app/views/layouts/application.html.erb new file mode 100644 index 0000000..bf61c9d --- /dev/null +++ b/app/views/layouts/application.html.erb @@ -0,0 +1,46 @@ + + + + <%= content_for(:title) || "Ferti" %> + + + + <%= csrf_meta_tags %> + <%= csp_meta_tag %> + + <%= yield :head %> + + <%# Enable PWA manifest for installable apps (make sure to enable in config/routes.rb too!) %> + <%#= tag.link rel: "manifest", href: pwa_manifest_path(format: :json) %> + + + + + + <%# Includes all stylesheet files in app/assets/stylesheets %> + <%= stylesheet_link_tag :app, "data-turbo-track": "reload" %> + + + + + + + + + + <%= javascript_importmap_tags %> + + + + <%= render "application/navbar" %> + +
+ <%= yield %> +
+ + <%= render "application/copyright_footer" %> + + + + + diff --git a/app/views/layouts/mailer.html.erb b/app/views/layouts/mailer.html.erb new file mode 100644 index 0000000..3aac900 --- /dev/null +++ b/app/views/layouts/mailer.html.erb @@ -0,0 +1,13 @@ + + + + + + + + + <%= yield %> + + diff --git a/app/views/layouts/mailer.text.erb b/app/views/layouts/mailer.text.erb new file mode 100644 index 0000000..37f0bdd --- /dev/null +++ b/app/views/layouts/mailer.text.erb @@ -0,0 +1 @@ +<%= yield %> diff --git a/app/views/nutrient_measurement/index.html.erb b/app/views/nutrient_measurement/index.html.erb new file mode 100644 index 0000000..d9f522e --- /dev/null +++ b/app/views/nutrient_measurement/index.html.erb @@ -0,0 +1,27 @@ +

NutrientMeasurement#index

+

Find me in app/views/nutrient_measurement/index.html.erb

+ +
+ + + + + + + + + + + + <% @measurements.each do |m| %> + + + + + + + + <% end %> + +
DateN (total)PKNH₄‑N
<%= l(m.measured_on) %><%= fmt2(total_n(m)) %><%= fmt2(m.p) %><%= fmt2(m.k) %><%= fmt2(m.nnh4) %>
+
diff --git a/app/views/pwa/manifest.json.erb b/app/views/pwa/manifest.json.erb new file mode 100644 index 0000000..f8908f0 --- /dev/null +++ b/app/views/pwa/manifest.json.erb @@ -0,0 +1,22 @@ +{ + "name": "Ferti", + "icons": [ + { + "src": "/icon.png", + "type": "image/png", + "sizes": "512x512" + }, + { + "src": "/icon.png", + "type": "image/png", + "sizes": "512x512", + "purpose": "maskable" + } + ], + "start_url": "/", + "display": "standalone", + "scope": "/", + "description": "Ferti.", + "theme_color": "red", + "background_color": "red" +} diff --git a/app/views/pwa/service-worker.js b/app/views/pwa/service-worker.js new file mode 100644 index 0000000..b3a13fb --- /dev/null +++ b/app/views/pwa/service-worker.js @@ -0,0 +1,26 @@ +// Add a service worker for processing Web Push notifications: +// +// self.addEventListener("push", async (event) => { +// const { title, options } = await event.data.json() +// event.waitUntil(self.registration.showNotification(title, options)) +// }) +// +// self.addEventListener("notificationclick", function(event) { +// event.notification.close() +// event.waitUntil( +// clients.matchAll({ type: "window" }).then((clientList) => { +// for (let i = 0; i < clientList.length; i++) { +// let client = clientList[i] +// let clientPath = (new URL(client.url)).pathname +// +// if (clientPath == event.notification.data.path && "focus" in client) { +// return client.focus() +// } +// } +// +// if (clients.openWindow) { +// return clients.openWindow(event.notification.data.path) +// } +// }) +// ) +// }) diff --git a/app/views/rafts/editor.html.erb b/app/views/rafts/editor.html.erb new file mode 100644 index 0000000..cde56d8 --- /dev/null +++ b/app/views/rafts/editor.html.erb @@ -0,0 +1,108 @@ +
+

Rafts editor

+ <%= link_to "Back to dashboard", root_path, class: "btn btn-outline-secondary" %> +
+ + +
+
Assign ALL rafts
+
+ <%= form_with url: assign_all_rafts_path, method: :patch, class: "row g-2", data: { turbo: false } do %> +
+ +
+
+ +
+ <% end %> +
+
+ +
+ + + + + <% max_cols = @beds.map { |b| b.rafts.count }.max %> + <% (1..max_cols).each do |i| %> + + <% end %> + + + + <% @beds.each do |bed| %> + + + <% bed.rafts.order(:location).each do |raft| %> + + <% end %> + + <% end %> + +
BedR<%= i %>
<%= bed.location %> + <% if raft.crop %> + <%= raft.crop.name %> + <% else %> + — + <% end %> +
+
+ + +<% @beds.each do |bed| %> +
+
+ Bed #<%= bed.location %> + <%= form_with url: assign_bed_rafts_path, method: :patch, class: "d-flex gap-2 align-items-center", data: { turbo: false } do %> + + + + <% end %> +
+
+
+ + + + + + + + + + <% bed.rafts.order(:location).each do |raft| %> + + + + + + <% end %> + +
RaftCropActions
<%= raft.location %> + <%= form_with url: assign_one_raft_path(raft), method: :patch, class: "d-flex gap-2", data: { turbo: false } do %> + + + <% end %> + + <%= button_to "Clear", assign_one_raft_path(raft, crop_id: ""), method: :patch, + form: { data: { turbo: false } }, class: "btn btn-outline-secondary btn-sm" %> +
+
+
+
+<% end %> diff --git a/app/views/recipes/_table.html.erb b/app/views/recipes/_table.html.erb new file mode 100644 index 0000000..a42a0b9 --- /dev/null +++ b/app/views/recipes/_table.html.erb @@ -0,0 +1,32 @@ +
+ + + + + + + + + + + <% pcount = [[portions.to_i, 2].max, 10].min %> + <% recipe&.each do |component, kg| %> + <% prod_name = commercial_name_for(component) %> + + + + + + + <% end %> + <% if recipe.blank? %> + + <% end %> + +
Fertilizer productComponentQty (kg)Per portion (kg)
<%= prod_name %><%= component.name %><%= fmt_kg(kg) %><%= fmt_kg(kg / pcount.to_f) %>
No supplementation required.
+
+ + diff --git a/app/views/recipes/show.html.erb b/app/views/recipes/show.html.erb new file mode 100644 index 0000000..180fdae --- /dev/null +++ b/app/views/recipes/show.html.erb @@ -0,0 +1,108 @@ + +<%# Controls header %> +
+
+
Ferti© Recipe
+ +
+ <%= form_with url: ferti_recipe_path, method: :get, local: true, class: "d-flex flex-wrap gap-2 align-items-center", id: "recipe-form" do %> +
+ + +
+ +
+ +
+ + +
+ <% end %> + + <%= link_to "Back", root_path, class: "btn btn-sm btn-secondary" %> +
+
+ +
+ + + + + + + + + + + <% @recipe.each do |component, kg| %> + <% prod_name = commercial_name_for(component) %> + + + + + + + <% end %> + <% if @recipe.blank? %> + + <% end %> + +
ProductFertilizerQty (kg)Per portion (kg)
<%= prod_name %><%= component.name %><%= fmt_kg(kg) %><%= fmt_kg(kg / (params[:portions].presence || 2).to_f) %>
No supplementation required.
+
+ + +
+ +<%# Tiny vanilla JS: auto-submit on volume change; live per-portion update %> + -- cgit v1.2.3