From 075665c588989ed0decdfb20d83f32b33eed4639 Mon Sep 17 00:00:00 2001 From: Marius Peter Date: Fri, 29 Aug 2025 14:22:37 +0200 Subject: Properly implement bed and raft management logic. --- app/controllers/beds_controller.rb | 61 ++++++++++++ app/controllers/crops_controller.rb | 34 +++---- app/controllers/rafts_controller.rb | 53 +++------- app/helpers/beds_helper.rb | 2 + app/models/bed.rb | 1 + app/models/raft.rb | 2 +- app/views/application/_navbar.html.erb | 12 +-- app/views/beds/_bed.html.erb | 2 + app/views/beds/_form.html.erb | 28 ++++++ app/views/beds/edit.html.erb | 11 +++ app/views/beds/index.html.erb | 54 +++++++++++ .../dashboard/_nutrient_measurements.html.erb | 22 +++++ app/views/dashboard/_raft_allocation.html.erb | 4 +- app/views/dashboard/_recent_measurements.html.erb | 22 ----- app/views/dashboard/index.html.erb | 2 +- app/views/rafts/_form.html.erb | 23 +++++ app/views/rafts/edit.html.erb | 5 + app/views/rafts/editor.html.erb | 108 --------------------- 18 files changed, 244 insertions(+), 202 deletions(-) create mode 100644 app/controllers/beds_controller.rb create mode 100644 app/helpers/beds_helper.rb create mode 100644 app/views/beds/_bed.html.erb create mode 100644 app/views/beds/_form.html.erb create mode 100644 app/views/beds/edit.html.erb create mode 100644 app/views/beds/index.html.erb create mode 100644 app/views/dashboard/_nutrient_measurements.html.erb delete mode 100644 app/views/dashboard/_recent_measurements.html.erb create mode 100644 app/views/rafts/_form.html.erb create mode 100644 app/views/rafts/edit.html.erb delete mode 100644 app/views/rafts/editor.html.erb (limited to 'app') diff --git a/app/controllers/beds_controller.rb b/app/controllers/beds_controller.rb new file mode 100644 index 0000000..c213978 --- /dev/null +++ b/app/controllers/beds_controller.rb @@ -0,0 +1,61 @@ +class BedsController < ApplicationController + before_action :set_bed, only: %i[ edit update ] + before_action :get_crops, only: %i[ index edit update ] + + def index + @beds = Bed.all + end + + def edit + end + + def update + if @bed.update(bed_params) + redirect_to beds_path, notice: "Bed #{@bed.id} successfully updated." + else + render :edit, status: :unprocessable_entity + end + end + + def bulk_assign_crops + crop = Crop.find(params[:crop_id]) + Raft.update_all(crop_id: crop.id) + redirect_back fallback_location: root_path, notice: "All rafts set to #{crop.name}." + end + + + def reset_seed_crops + # mirrors seed logic + tomatoes = Crop.find_by!(name: :tomatoes) + chives = Crop.find_by!(name: :chives) + lettuce = Crop.find_by!(name: :lettuce) + + Bed.includes(:rafts).find_each do |bed| + default_crop = + case bed.location + when 1..3 then tomatoes + when 4..7 then chives + else lettuce + end + bed.rafts.update_all(crop_id: default_crop.id) + end + redirect_back fallback_location: root_path, notice: "Raft crops reset to default seed layout." + end + + private + + def set_bed + @bed = Bed.find(params[:id]) + end + + def get_crops + @crops = Crop.order(:name) + end + + def bed_params + params.require(:bed).permit( + :location, + rafts_attributes: %i[id crop_id] + ) + end +end diff --git a/app/controllers/crops_controller.rb b/app/controllers/crops_controller.rb index 9619852..951d380 100644 --- a/app/controllers/crops_controller.rb +++ b/app/controllers/crops_controller.rb @@ -1,40 +1,30 @@ class CropsController < ApplicationController before_action :set_crop, only: %i[ show edit update destroy ] - # GET /crops or /crops.json def index @crops = Crop.all end - # GET /crops/1 or /crops/1.json def show end - # GET /crops/new def new @crop = Crop.new end - # GET /crops/1/edit def edit end - # POST /crops or /crops.json def create @crop = Crop.new(crop_params) - respond_to do |format| - if @crop.save - format.html { redirect_to @crop, notice: "Crop was successfully created." } - format.json { render :show, status: :created, location: @crop } - else - format.html { render :new, status: :unprocessable_entity } - format.json { render json: @crop.errors, status: :unprocessable_entity } - end + if @crop.save + redirect_to @crop, notice: "Crop was successfully created." + else + render :new, status: :unprocessable_entity end end - # PATCH/PUT /crops/1 or /crops/1.json def update respond_to do |format| if @crop.update(crop_params) @@ -47,7 +37,6 @@ class CropsController < ApplicationController end end - # DELETE /crops/1 or /crops/1.json def destroy @crop.destroy! @@ -58,13 +47,12 @@ class CropsController < ApplicationController end private - # Use callbacks to share common setup or constraints between actions. - def set_crop - @crop = Crop.find(params.expect(:id)) - end - # Only allow a list of trusted parameters through. - def crop_params - params.expect(crop: [ :name, :crop_type, :nno3, :p, :k, :ca, :mg, :s, :na, :cl, :si, :fe, :zn, :b, :mn, :cu, :mo, :nnh4 ]) - end + def set_crop + @crop = Crop.find(params.expect(:id)) + end + + def crop_params + params.expect(crop: [ :name, :crop_type, :nno3, :p, :k, :ca, :mg, :s, :na, :cl, :si, :fe, :zn, :b, :mn, :cu, :mo, :nnh4 ]) + end end diff --git a/app/controllers/rafts_controller.rb b/app/controllers/rafts_controller.rb index 96d99fd..c1c8a72 100644 --- a/app/controllers/rafts_controller.rb +++ b/app/controllers/rafts_controller.rb @@ -1,54 +1,29 @@ class RaftsController < ApplicationController - before_action :set_collections, only: [ :editor ] + before_action :set_raft, only: %i[ edit update ] + before_action :set_crop, only: %i[ edit update ] - def index - redirect_to editor_rafts_path + def edit end - def editor - # @beds, @crops set in before_action - end - - # 1) Assign ALL rafts - def assign_all - crop_id = normalized_crop_id(params[:crop_id]) - Raft.update_all(crop_id: crop_id) # nil ok - redirect_to editor_rafts_path, notice: "All rafts updated." - end - - # 2) Assign all rafts in ONE bed - def assign_bed - bed = Bed.find(params.require(:bed_id)) - crop_id = normalized_crop_id(params[:crop_id]) - bed.rafts.update_all(crop_id: crop_id) - redirect_to editor_rafts_path, notice: "Bed ##{bed.location} updated." - end - - # 3) Assign ONE raft - def assign_one - raft = Raft.find(params[:id]) - val = params[:crop_id].to_s - - if val.blank? || val.casecmp("null").zero? - # Skip validations; write NULL directly - raft.update_column(:crop_id, nil) + def update + if @raft.update(raft_params) + redirect_to beds_path, notice: "Raft #{@raft.id} successfully updated." else - raft.update!(crop_id: val) + render :edit, status: :unprocessable_entity end - - redirect_to editor_rafts_path(anchor: "bed-#{raft.bed_id}"), notice: "Raft updated." end private - def set_collections - @beds = Bed.order(:location) + def set_raft + @raft = Raft.find(params[:id]) + end + + def set_crop @crops = Crop.order(:name) end - # Accept "", "null" → nil to allow clearing - def normalized_crop_id(val) - return nil if val.blank? || val.to_s.downcase == "null" - val + def raft_params + params.require(:raft).permit(:location, :bed_id, :crop_id) end end diff --git a/app/helpers/beds_helper.rb b/app/helpers/beds_helper.rb new file mode 100644 index 0000000..2f3e2cb --- /dev/null +++ b/app/helpers/beds_helper.rb @@ -0,0 +1,2 @@ +module BedsHelper +end diff --git a/app/models/bed.rb b/app/models/bed.rb index 33eafd2..d41afe6 100644 --- a/app/models/bed.rb +++ b/app/models/bed.rb @@ -1,4 +1,5 @@ class Bed < ApplicationRecord has_many :rafts, -> { order(:location) }, dependent: :destroy + accepts_nested_attributes_for :rafts validates :location, presence: true, uniqueness: true end diff --git a/app/models/raft.rb b/app/models/raft.rb index af52700..3fe5928 100644 --- a/app/models/raft.rb +++ b/app/models/raft.rb @@ -1,5 +1,5 @@ class Raft < ApplicationRecord belongs_to :bed - belongs_to :crop + belongs_to :crop, optional: true validates :location, presence: true, uniqueness: { scope: :bed_id } end diff --git a/app/views/application/_navbar.html.erb b/app/views/application/_navbar.html.erb index 9fa250a..89dfe3d 100644 --- a/app/views/application/_navbar.html.erb +++ b/app/views/application/_navbar.html.erb @@ -3,12 +3,12 @@ FAPG diff --git a/app/views/beds/_bed.html.erb b/app/views/beds/_bed.html.erb new file mode 100644 index 0000000..6961f9b --- /dev/null +++ b/app/views/beds/_bed.html.erb @@ -0,0 +1,2 @@ +
+
diff --git a/app/views/beds/_form.html.erb b/app/views/beds/_form.html.erb new file mode 100644 index 0000000..e3a5c35 --- /dev/null +++ b/app/views/beds/_form.html.erb @@ -0,0 +1,28 @@ +<%= form_with(model: bed) do |form| %> + <% if bed.errors.any? %> +
+

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

+ +
+ <% end %> + +
+ <%= form.fields_for :rafts do |raft| %> +
+ Raft <%= raft.object.location %> + <%= raft.collection_select :crop_id, @crops, :id, :name, + { include_blank: "Unassigned" }, + { class: "form-select" } %> +
+ <%= raft.hidden_field :id %> + <% end %> +
+ +
+ <%= form.submit class: "btn btn-primary" %> +
+<% end %> diff --git a/app/views/beds/edit.html.erb b/app/views/beds/edit.html.erb new file mode 100644 index 0000000..6887586 --- /dev/null +++ b/app/views/beds/edit.html.erb @@ -0,0 +1,11 @@ +<% content_for :title, "Editing bed #{@bed.location}" %> + +

Editing bed <%= @bed.location %>

+ +<%= render "form", bed: @bed %> + +
+ +
+ <%= link_to "Back to beds", beds_path, class: "btn btn-secondary" %> +
diff --git a/app/views/beds/index.html.erb b/app/views/beds/index.html.erb new file mode 100644 index 0000000..e645dc5 --- /dev/null +++ b/app/views/beds/index.html.erb @@ -0,0 +1,54 @@ +<% content_for :title, "Crop Allocation" %> + +

Beds and Rafts

+ +<%= link_to "Back to dashboard", root_path, class: "btn btn-outline-secondary my-3" %> + + +

+ Click on a bed row or an individual raft to update the corresponding crop. +

+ +<%= form_with url: bulk_assign_crops_beds_path, + method: :patch, + local: true do %> +
+ + +
+<% end %> + +
+ + + + + <% max_cols = @beds.map { |b| b.rafts.count }.max %> + <% (1..max_cols).each do |i| %> + + <% end %> + + + + <% @beds.each do |bed| %> + + + <% bed.rafts.each do |raft| %> + + <% end %> + + <% end %> + +
BedR<%= i %>
<%= link_to bed.location, edit_bed_path(bed), class: "btn btn-outline-secondary" %> + <%= link_to (raft&.crop&.name || "—"), edit_raft_path(raft), class: "btn btn-sm" %> +
+
+ +<%= button_to "Reset crop allocation", reset_seed_crops_beds_path, + method: :post, + form: { data: { turbo: false } }, + class: "btn btn-outline-danger" %> diff --git a/app/views/dashboard/_nutrient_measurements.html.erb b/app/views/dashboard/_nutrient_measurements.html.erb new file mode 100644 index 0000000..bc63a60 --- /dev/null +++ b/app/views/dashboard/_nutrient_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/_raft_allocation.html.erb b/app/views/dashboard/_raft_allocation.html.erb index ef95cdd..a2128ea 100644 --- a/app/views/dashboard/_raft_allocation.html.erb +++ b/app/views/dashboard/_raft_allocation.html.erb @@ -1,7 +1,7 @@
-
Raft Allocation
- <%= link_to "Edit raft allocation", editor_rafts_path, class: "btn btn-sm btn-primary" %> +
Crop Allocation
+ <%= link_to "Edit allocation", beds_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 deleted file mode 100644 index bc63a60..0000000 --- a/app/views/dashboard/_recent_measurements.html.erb +++ /dev/null @@ -1,22 +0,0 @@ -
-
-
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/index.html.erb b/app/views/dashboard/index.html.erb index 2902ada..b1b2d87 100644 --- a/app/views/dashboard/index.html.erb +++ b/app/views/dashboard/index.html.erb @@ -4,4 +4,4 @@ <%= render "target_table" %> -<%= render "recent_measurements" %> +<%= render "nutrient_measurements" %> diff --git a/app/views/rafts/_form.html.erb b/app/views/rafts/_form.html.erb new file mode 100644 index 0000000..7b5d382 --- /dev/null +++ b/app/views/rafts/_form.html.erb @@ -0,0 +1,23 @@ +<%= form_with(model: raft) do |form| %> + <% if raft.errors.any? %> +
+

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

+ +
    + <% raft.errors.each do |error| %> +
  • <%= error.full_message %>
  • + <% end %> +
+
+ <% end %> + +
+ + <%= form.collection_select :crop_id, @crops, :id, :name, + { include_blank: "Unassigned" }, { class: "form-select" } %> +
+ +
+ <%= form.submit class: "btn btn-primary" %> +
+<% end %> diff --git a/app/views/rafts/edit.html.erb b/app/views/rafts/edit.html.erb new file mode 100644 index 0000000..bf975dc --- /dev/null +++ b/app/views/rafts/edit.html.erb @@ -0,0 +1,5 @@ +<% content_for :title, "Editing bed #{@raft.bed.location}, raft #{@raft.location}" %> + +

Editing bed <%= @raft.bed.location %>, raft <%= @raft.location %>

+ +<%= render "form", raft: @raft %> diff --git a/app/views/rafts/editor.html.erb b/app/views/rafts/editor.html.erb deleted file mode 100644 index cde56d8..0000000 --- a/app/views/rafts/editor.html.erb +++ /dev/null @@ -1,108 +0,0 @@ -
-

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 %> -- cgit v1.2.3