Renamed Ingredient -> Food
Ingredient is now part of composition (meal/recipe/dish)
This commit is contained in:
parent
c3010a70e8
commit
8e8160c41a
@ -1,9 +1,9 @@
|
||||
module Concerns::Finders
|
||||
private
|
||||
|
||||
def find_ingredient
|
||||
@ingredient = Ingredient.find(params[:id])
|
||||
@project = @ingredient.project
|
||||
def find_food
|
||||
@food = Food.find(params[:id])
|
||||
@project = @food.project
|
||||
rescue ActiveRecord::RecordNotFound
|
||||
render_404
|
||||
end
|
||||
|
@ -1,4 +1,4 @@
|
||||
class IngredientsController < ApplicationController
|
||||
class FoodsController < ApplicationController
|
||||
require 'csv'
|
||||
|
||||
layout 'body_tracking'
|
||||
@ -11,25 +11,25 @@ class IngredientsController < ApplicationController
|
||||
before_action :find_project_by_project_id,
|
||||
only: [:index, :new, :create, :nutrients, :filter, :import]
|
||||
before_action :find_quantity_by_quantity_id, only: [:toggle_column]
|
||||
before_action :find_ingredient, only: [:edit, :update, :destroy, :toggle]
|
||||
before_action :find_food, only: [:edit, :update, :destroy, :toggle]
|
||||
before_action :authorize
|
||||
|
||||
def index
|
||||
prepare_ingredients
|
||||
prepare_foods
|
||||
end
|
||||
|
||||
def new
|
||||
@ingredient = @project.ingredients.new
|
||||
@ingredient.nutrients.new(unit: @ingredient.ref_unit)
|
||||
@food = @project.foods.new
|
||||
@food.nutrients.new(unit: @food.ref_unit)
|
||||
end
|
||||
|
||||
def create
|
||||
@ingredient = @project.ingredients.new(ingredient_params)
|
||||
if @ingredient.save
|
||||
flash[:notice] = 'Created new ingredient'
|
||||
@food = @project.foods.new(food_params)
|
||||
if @food.save
|
||||
flash[:notice] = 'Created new food'
|
||||
prepare_items
|
||||
else
|
||||
@ingredient.nutrients.new(unit: @ingredient.ref_unit) if @ingredient.nutrients.empty?
|
||||
@food.nutrients.new(unit: @food.ref_unit) if @food.nutrients.empty?
|
||||
render :new
|
||||
end
|
||||
end
|
||||
@ -38,8 +38,8 @@ class IngredientsController < ApplicationController
|
||||
end
|
||||
|
||||
def update
|
||||
if @ingredient.update(ingredient_params)
|
||||
flash[:notice] = 'Updated ingredient'
|
||||
if @food.update(food_params)
|
||||
flash[:notice] = 'Updated food'
|
||||
prepare_items
|
||||
render :index
|
||||
else
|
||||
@ -48,13 +48,13 @@ class IngredientsController < ApplicationController
|
||||
end
|
||||
|
||||
def destroy
|
||||
if @ingredient.destroy
|
||||
flash[:notice] = 'Deleted ingredient'
|
||||
if @food.destroy
|
||||
flash[:notice] = 'Deleted food'
|
||||
end
|
||||
end
|
||||
|
||||
def toggle
|
||||
@ingredient.toggle_hidden!
|
||||
@food.toggle_hidden!
|
||||
prepare_items
|
||||
end
|
||||
|
||||
@ -68,7 +68,7 @@ class IngredientsController < ApplicationController
|
||||
end
|
||||
|
||||
def filter
|
||||
session[:i_filters] = params.permit(:name, :visibility, formula: [:code, :zero_nil])
|
||||
session[:f_filters] = params.permit(:name, :visibility, formula: [:code, :zero_nil])
|
||||
prepare_items
|
||||
render :index
|
||||
end
|
||||
@ -80,7 +80,7 @@ class IngredientsController < ApplicationController
|
||||
quantities = @project.quantities.diet.map { |q| [q.name, q] }.to_h
|
||||
units = @project.units.map { |u| [u.shortname, u] }.to_h
|
||||
sources = @project.sources.map { |s| [s.name, s] }.to_h
|
||||
ingredients_params = []
|
||||
foods_params = []
|
||||
column_units = {}
|
||||
|
||||
CSV.foreach(params[:file].path, headers: true).with_index(2) do |row, line|
|
||||
@ -92,7 +92,7 @@ class IngredientsController < ApplicationController
|
||||
warnings << "Line #{line}: unknown source name #{r['Source']}"
|
||||
end
|
||||
|
||||
i_params = {
|
||||
f_params = {
|
||||
name: r.delete('Name'),
|
||||
notes: r.delete('Notes'),
|
||||
ref_amount: 100.0,
|
||||
@ -138,12 +138,12 @@ class IngredientsController < ApplicationController
|
||||
|
||||
next if quantities[quantity].blank?
|
||||
if quantity == 'Reference'
|
||||
i_params.update({
|
||||
f_params.update({
|
||||
ref_amount: amount.to_d,
|
||||
ref_unit: unit
|
||||
})
|
||||
else
|
||||
i_params[:nutrients_attributes] << {
|
||||
f_params[:nutrients_attributes] << {
|
||||
quantity: quantities[quantity],
|
||||
amount: amount.to_d,
|
||||
unit: unit
|
||||
@ -151,20 +151,20 @@ class IngredientsController < ApplicationController
|
||||
end
|
||||
end
|
||||
|
||||
ingredients_params << i_params
|
||||
foods_params << f_params
|
||||
end
|
||||
else
|
||||
warnings << 'No file selected'
|
||||
end
|
||||
|
||||
if warnings.empty?
|
||||
ingredients = @project.ingredients.create(ingredients_params)
|
||||
flash[:notice] = "Imported #{ingredients.map(&:persisted?).count(true)} out of" \
|
||||
" #{ingredients_params.length} ingredients"
|
||||
skipped = ingredients.select { |i| !i.persisted? }
|
||||
foods = @project.foods.create(foods_params)
|
||||
flash[:notice] = "Imported #{foods.map(&:persisted?).count(true)} out of" \
|
||||
" #{foods_params.length} foods"
|
||||
skipped = foods.select { |f| !f.persisted? }
|
||||
if skipped.length > 0
|
||||
skipped_desc = skipped.map { |i| "#{i.name} - #{i.errors.full_messages.join(', ')}" }
|
||||
flash[:warning] = "Ingredients skipped due to errors:<br>" \
|
||||
skipped_desc = skipped.map { |f| "#{f.name} - #{f.errors.full_messages.join(', ')}" }
|
||||
flash[:warning] = "Foods skipped due to errors:<br>" \
|
||||
" #{skipped_desc.join('<br>').truncate(1024)}"
|
||||
end
|
||||
else
|
||||
@ -177,11 +177,11 @@ class IngredientsController < ApplicationController
|
||||
private
|
||||
|
||||
def init_session_filters
|
||||
session[:i_filters] ||= {formula: {}}
|
||||
session[:f_filters] ||= {formula: {}}
|
||||
end
|
||||
|
||||
def ingredient_params
|
||||
params.require(:ingredient).permit(
|
||||
def food_params
|
||||
params.require(:food).permit(
|
||||
:name,
|
||||
:notes,
|
||||
:ref_amount,
|
||||
@ -201,18 +201,18 @@ class IngredientsController < ApplicationController
|
||||
end
|
||||
|
||||
def prepare_items
|
||||
params[:view] == 'index' ? prepare_ingredients : prepare_nutrients
|
||||
params[:view] == 'index' ? prepare_foods : prepare_nutrients
|
||||
end
|
||||
|
||||
def prepare_ingredients
|
||||
@ingredients, @formula_q = @project.ingredients
|
||||
def prepare_foods
|
||||
@foods, @formula_q = @project.foods
|
||||
.includes(:ref_unit, :source)
|
||||
.filter(session[:i_filters])
|
||||
.filter(session[:f_filters])
|
||||
end
|
||||
|
||||
def prepare_nutrients
|
||||
@quantities = @project.nutrient_quantities.includes(:formula)
|
||||
@ingredients, @requested_n, @extra_n, @formula_q = @project.ingredients
|
||||
.filter(session[:i_filters], @quantities)
|
||||
@foods, @requested_n, @extra_n, @formula_q = @project.foods
|
||||
.filter(session[:f_filters], @quantities)
|
||||
end
|
||||
end
|
@ -16,6 +16,6 @@ class MealsController < ApplicationController
|
||||
private
|
||||
|
||||
def prepare_meals
|
||||
@meals = @project.meals.includes(:ingredients)
|
||||
@meals = @project.meals.includes(:foods)
|
||||
end
|
||||
end
|
||||
|
@ -1,4 +1,4 @@
|
||||
module IngredientsHelper
|
||||
module FoodsHelper
|
||||
def quantity_options
|
||||
nested_set_options(@project.quantities.diet) do |q|
|
||||
raw("#{' ' * q.level}#{q.name}")
|
||||
@ -28,14 +28,14 @@ module IngredientsHelper
|
||||
|
||||
def group_options
|
||||
translations = t('.groups')
|
||||
Ingredient.groups.map do |k,v|
|
||||
Food.groups.map do |k,v|
|
||||
[translations[k.to_sym], k]
|
||||
end
|
||||
end
|
||||
|
||||
def action_links(i, view)
|
||||
link_to(l(:button_edit), edit_ingredient_path(i, view: view),
|
||||
def action_links(f, view)
|
||||
link_to(l(:button_edit), edit_food_path(f, view: view),
|
||||
{remote: true, class: "icon icon-edit"}) +
|
||||
delete_link(ingredient_path(i), {remote: true, data: {}})
|
||||
delete_link(food_path(f), {remote: true, data: {}})
|
||||
end
|
||||
end
|
@ -1,4 +1,4 @@
|
||||
class Ingredient < ActiveRecord::Base
|
||||
class Food < ActiveRecord::Base
|
||||
enum group: {
|
||||
other: 0,
|
||||
dish: 1,
|
||||
@ -16,7 +16,7 @@ class Ingredient < ActiveRecord::Base
|
||||
belongs_to :ref_unit, class_name: 'Unit', required: true
|
||||
belongs_to :source, required: false
|
||||
|
||||
has_many :nutrients, inverse_of: :ingredient, dependent: :destroy, validate: true
|
||||
has_many :nutrients, inverse_of: :food, dependent: :destroy, validate: true
|
||||
validates :nutrients, presence: true
|
||||
accepts_nested_attributes_for :nutrients, allow_destroy: true, reject_if: proc { |attrs|
|
||||
attrs['quantity_id'].blank? && attrs['amount'].blank?
|
@ -0,0 +1,6 @@
|
||||
class Meal < ActiveRecord::Base
|
||||
belongs_to :project, required: true
|
||||
|
||||
has_many :ingredients, as: :composition, dependent: :destroy
|
||||
has_many :foods, through: :ingredients
|
||||
end
|
@ -1,8 +1,8 @@
|
||||
class Nutrient < ActiveRecord::Base
|
||||
belongs_to :ingredient, inverse_of: :nutrients, required: true
|
||||
belongs_to :food, inverse_of: :nutrients, required: true
|
||||
belongs_to :quantity, required: true
|
||||
belongs_to :unit, required: true
|
||||
|
||||
validates :quantity, uniqueness: {scope: :ingredient_id}
|
||||
validates :quantity, uniqueness: {scope: :food_id}
|
||||
validates :amount, numericality: {greater_than_or_equal_to: 0.0}
|
||||
end
|
||||
|
6
app/views/foods/_contextual.html.erb
Normal file
6
app/views/foods/_contextual.html.erb
Normal file
@ -0,0 +1,6 @@
|
||||
<% if User.current.allowed_to?(:manage_common, @project) %>
|
||||
<%= link_to t(".link_import_foods"), '#', class: 'icon icon-multiple',
|
||||
onclick: '$("#import-foods").show(); $("#filename").focus(); return false;' %>
|
||||
<%= link_to t(".link_new_food"), new_project_food_path(@project, view: view),
|
||||
{remote: true, class: 'icon icon-add'} %>
|
||||
<% end %>
|
@ -1,9 +1,9 @@
|
||||
<%= labelled_form_for @ingredient,
|
||||
url: ingredient_path(@ingredient, view: view),
|
||||
<%= labelled_form_for @food,
|
||||
url: food_path(@food, view: view),
|
||||
method: :patch, remote: true,
|
||||
html: {id: 'ingredient-edit-form', name: 'ingredient-edit-form'} do |f| %>
|
||||
html: {id: 'food-edit-form', name: 'food-edit-form'} do |f| %>
|
||||
|
||||
<%= render partial: 'ingredients/form', locals: {f: f} %>
|
||||
<%= render partial: 'foods/form', locals: {f: f} %>
|
||||
|
||||
<div class="tabular">
|
||||
<p>
|
@ -9,14 +9,14 @@
|
||||
<table class="filter">
|
||||
<tr>
|
||||
<td>
|
||||
<%= text_field_tag 'name', session[:i_filters][:name], placeholder: 'name' %>
|
||||
<%= text_field_tag 'name', session[:f_filters][:name], placeholder: 'name' %>
|
||||
</td>
|
||||
<td>
|
||||
<%= select_tag 'visibility', visibility_options(session[:i_filters][:visibility]),
|
||||
<%= select_tag 'visibility', visibility_options(session[:f_filters][:visibility]),
|
||||
prompt: t('.visibility_prompt'), onchange: '$("#filters-form").submit();' %>
|
||||
</td>
|
||||
<td style="width:100%;">
|
||||
<%= text_field_tag 'formula[code]', session[:i_filters][:formula][:code],
|
||||
<%= text_field_tag 'formula[code]', session[:f_filters][:formula][:code],
|
||||
placeholder: 'conditional expression including nutrients', size: 40,
|
||||
style: 'box-sizing:border-box; width:100%;' %>
|
||||
</td>
|
@ -1,4 +1,4 @@
|
||||
<%= error_messages_for @ingredient %>
|
||||
<%= error_messages_for @food %>
|
||||
|
||||
<div class="box tabular">
|
||||
<p><%= f.text_field :name, size: 40, required: true %></p>
|
||||
@ -21,7 +21,7 @@
|
||||
<p><%= f.text_field :source_ident, size: 25, required: false %></p>
|
||||
</div>
|
||||
</div>
|
||||
<% @ingredient.nutrients.each_with_index do |n, index| %>
|
||||
<% @food.nutrients.each_with_index do |n, index| %>
|
||||
<%= f.fields_for 'nutrients_attributes', n, index: '' do |ff| %>
|
||||
<p class="nutrient">
|
||||
<%= ff.hidden_field :id %>
|
||||
@ -32,7 +32,7 @@
|
||||
<%= ff.hidden_field :_destroy %>
|
||||
<%= link_to t(".button_delete_nutrient"), '#',
|
||||
class: 'icon icon-del',
|
||||
style: (@ingredient.nutrients.length > 1 ? "" : "display:none"),
|
||||
style: (@food.nutrients.length > 1 ? "" : "display:none"),
|
||||
onclick: "deleteNutrient(); return false;" %>
|
||||
</p>
|
||||
<% end %>
|
@ -1,7 +1,7 @@
|
||||
<div id="import-ingredients" style="display:none;">
|
||||
<h2><%= t ".heading_import_ingredients" %></h2>
|
||||
<div id="import-foods" style="display:none;">
|
||||
<h2><%= t ".heading_import_foods" %></h2>
|
||||
|
||||
<%= form_tag import_project_ingredients_path(@project),
|
||||
<%= form_tag import_project_foods_path(@project),
|
||||
id: 'import-form', name: 'import-form', multipart: true do %>
|
||||
|
||||
<div class="box tabular">
|
||||
@ -14,8 +14,7 @@
|
||||
</p>
|
||||
</div>
|
||||
<%= submit_tag l(:button_import) %>
|
||||
<%= link_to l(:button_cancel), "#",
|
||||
onclick: '$("#import-ingredients").hide(); return false;' %>
|
||||
<%= link_to l(:button_cancel), "#", onclick: '$("#import-foods").hide(); return false;' %>
|
||||
<% end %>
|
||||
<hr>
|
||||
</div>
|
@ -1,7 +1,7 @@
|
||||
<%= render partial: 'ingredients/filters',
|
||||
locals: {url: filter_project_ingredients_path(@project, view: :index)} %>
|
||||
<%= render partial: 'foods/filters',
|
||||
locals: {url: filter_project_foods_path(@project, view: :index)} %>
|
||||
|
||||
<% if @ingredients.any? { |i| i.persisted? } %>
|
||||
<% if @foods.any? { |f| f.persisted? } %>
|
||||
<%= error_messages_for @formula_q.formula if @formula_q %>
|
||||
|
||||
<table class="list">
|
||||
@ -16,26 +16,26 @@
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<% @ingredients.each do |i| %>
|
||||
<% next if i.new_record? %>
|
||||
<tr id="ingredient-<%= i.id %>"
|
||||
class="ingredient primary<%= ' hidden' if i.hidden %>">
|
||||
<% @foods.each do |f| %>
|
||||
<% next if f.new_record? %>
|
||||
<tr id="food-<%= f.id %>"
|
||||
class="food primary<%= ' hidden' if f.hidden %>">
|
||||
<td class="name ellipsible">
|
||||
<%= link_to '', toggle_ingredient_path(i), {
|
||||
<%= link_to '', toggle_food_path(f), {
|
||||
remote: true,
|
||||
method: :post,
|
||||
class: "icon icon-eye"
|
||||
} %>
|
||||
<%= i.name %>
|
||||
<%= f.name %>
|
||||
</td>
|
||||
<td class="notes ellipsible"><%= i.notes %></td>
|
||||
<td class="reference value"><%= i.ref_amount %> [<%= i.ref_unit.shortname %>]</td>
|
||||
<td class="group"><%= i.group %></td>
|
||||
<td class="notes ellipsible"><%= f.notes %></td>
|
||||
<td class="reference value"><%= f.ref_amount %> [<%= f.ref_unit.shortname %>]</td>
|
||||
<td class="group"><%= f.group %></td>
|
||||
<td class="source">
|
||||
<%= i.source.name if i.source.present? %>
|
||||
<%= ", #{i.source_ident}" if i.source_ident.present? %>
|
||||
<%= f.source.name if f.source.present? %>
|
||||
<%= ", #{f.source_ident}" if f.source_ident.present? %>
|
||||
</td>
|
||||
<td class="action unwrappable"><%= action_links(i, :index) %></td>
|
||||
<td class="action unwrappable"><%= action_links(f, :index) %></td>
|
||||
</tr>
|
||||
<% end %>
|
||||
</tbody>
|
18
app/views/foods/_new_form.html.erb
Normal file
18
app/views/foods/_new_form.html.erb
Normal file
@ -0,0 +1,18 @@
|
||||
<h2><%= t ".heading_new_food" %></h2>
|
||||
|
||||
<%= labelled_form_for @food,
|
||||
url: project_foods_path(@project, view: view),
|
||||
remote: true,
|
||||
html: {id: 'new-food-form', name: 'new-food-form'} do |f| %>
|
||||
|
||||
<%= render partial: 'foods/form', locals: {f: f} %>
|
||||
|
||||
<div class="tabular">
|
||||
<p>
|
||||
<%= submit_tag l(:button_create) %>
|
||||
<%= link_to l(:button_cancel), "#",
|
||||
onclick: '$("#new-food").empty(); return false;' %>
|
||||
</p>
|
||||
</div>
|
||||
<% end %>
|
||||
<hr>
|
@ -1,8 +1,8 @@
|
||||
<%= render partial: 'ingredients/filters',
|
||||
locals: {url: filter_project_ingredients_path(@project, view: :nutrients)} %>
|
||||
<%= render partial: 'foods/filters',
|
||||
locals: {url: filter_project_foods_path(@project, view: :nutrients)} %>
|
||||
|
||||
<% if @ingredients.any? %>
|
||||
<%= render partial: 'ingredients/options' %>
|
||||
<% if @foods.any? %>
|
||||
<%= render partial: 'foods/options' %>
|
||||
|
||||
<% formulas = @quantities.map { |q| q.formula } %>
|
||||
<% formulas.unshift(@formula_q.formula) if @formula_q %>
|
||||
@ -17,7 +17,7 @@
|
||||
<th style="width:<%= 100/total_width %>%" class="closable ellipsible">
|
||||
<div style="float:right;position:relative;">
|
||||
<%= link_to '',
|
||||
toggle_column_project_ingredients_path(@project, quantity_id: q.id),
|
||||
toggle_column_project_foods_path(@project, quantity_id: q.id),
|
||||
{class: "icon icon-close", method: :post, remote: true} %>
|
||||
</div>
|
||||
<%= q.name %>
|
||||
@ -28,17 +28,17 @@
|
||||
</thead>
|
||||
|
||||
<tbody>
|
||||
<% @ingredients.each_with_index do |i, index| %>
|
||||
<% row_class = "ingredient#{' hidden' if i.hidden} #{cycle('odd', 'even')}" %>
|
||||
<tr id="ingredient-<%= i.id %>" class="primary <%= row_class %>">
|
||||
<% @foods.each_with_index do |f, index| %>
|
||||
<% row_class = "food#{' hidden' if f.hidden} #{cycle('odd', 'even')}" %>
|
||||
<tr id="food-<%= f.id %>" class="primary <%= row_class %>">
|
||||
<td class="name ellipsible" style="cursor: pointer;"
|
||||
onclick="$(this).closest('tr').toggle(); $(this).closest('tr').nextUntil('tr.primary', '.ingredient').toggle(); return false;">
|
||||
<span class="icon icon-bullet-closed"><%= i.name %></span>
|
||||
onclick="$(this).closest('tr').toggle(); $(this).closest('tr').nextUntil('tr.primary', '.food').toggle(); return false;">
|
||||
<span class="icon icon-bullet-closed"><%= f.name %></span>
|
||||
</td>
|
||||
<% @requested_n[index].each do |*, value| %>
|
||||
<td class="primary value ellipsible"><%= format_value(value) %></td>
|
||||
<% end %>
|
||||
<td class="action unwrappable"><%= action_links(i, :nutrients) %></td>
|
||||
<td class="action unwrappable"><%= action_links(f, :nutrients) %></td>
|
||||
</tr>
|
||||
|
||||
<tr class="<%= row_class %>" style="display:none">
|
||||
@ -48,8 +48,8 @@
|
||||
rows = 1
|
||||
end %>
|
||||
<td rowspan="<%= rows %>" class="name ellipsible" style="cursor: pointer;"
|
||||
onclick="$(this).closest('tr').prev('tr.primary').toggle(); $(this).closest('tr').prev('tr.primary').nextUntil('tr.primary', '.ingredient').toggle(); return false;">
|
||||
<span class="icon icon-bullet-open"><%= i.name %></span>
|
||||
onclick="$(this).closest('tr').prev('tr.primary').toggle(); $(this).closest('tr').prev('tr.primary').nextUntil('tr.primary', '.food').toggle(); return false;">
|
||||
<span class="icon icon-bullet-open"><%= f.name %></span>
|
||||
</td>
|
||||
<% @requested_n[index].each do |q, value| %>
|
||||
<td class="primary quantity ellipsible">
|
||||
@ -58,7 +58,7 @@
|
||||
</td>
|
||||
<% end %>
|
||||
<td rowspan="<%= rows %>" class="action unwrappable">
|
||||
<%= action_links(i, :nutrients) %>
|
||||
<%= action_links(f, :nutrients) %>
|
||||
</td>
|
||||
</tr>
|
||||
|
@ -1,7 +1,7 @@
|
||||
<fieldset id="options" class="collapsible">
|
||||
<legend onclick="toggleFieldset(this);"><%= l(:label_options) %></legend>
|
||||
<div>
|
||||
<%= form_tag toggle_column_project_ingredients_path(@project),
|
||||
<%= form_tag toggle_column_project_foods_path(@project),
|
||||
id: 'toggle-column-form', name: 'toggle-column-form',
|
||||
method: :post, remote: true do %>
|
||||
|
6
app/views/foods/create.js.erb
Normal file
6
app/views/foods/create.js.erb
Normal file
@ -0,0 +1,6 @@
|
||||
$('#new-food').empty();
|
||||
<% if params[:view] == 'index' %>
|
||||
$('#foods').html('<%= j render partial: 'foods/index' %>');
|
||||
<% else %>
|
||||
$('#nutrients').html('<%= j render partial: 'foods/nutrients' %>');
|
||||
<% end %>
|
3
app/views/foods/destroy.js.erb
Normal file
3
app/views/foods/destroy.js.erb
Normal file
@ -0,0 +1,3 @@
|
||||
<% if @food.destroyed? %>
|
||||
$('tr[id=food-<%= @food.id %>]').nextUntil('tr.primary').addBack().remove();
|
||||
<% end %>
|
8
app/views/foods/edit.js.erb
Normal file
8
app/views/foods/edit.js.erb
Normal file
@ -0,0 +1,8 @@
|
||||
$('tr[id=food-<%= @food.id %>]').nextUntil('tr.primary', ':not(.food)')
|
||||
.remove();
|
||||
var columns = $('table > thead > tr > th').length;
|
||||
$('tr[id=food-<%= @food.id %>]').nextUntil('tr.primary').addBack().last().after(
|
||||
'<tr><td class="form" colspan="'+columns+'"><div id="edit-food">' +
|
||||
'<%= j render partial: 'foods/edit_form', locals: {view: params[:view]} %>' +
|
||||
'</div></td></tr>'
|
||||
);
|
15
app/views/foods/index.html.erb
Normal file
15
app/views/foods/index.html.erb
Normal file
@ -0,0 +1,15 @@
|
||||
<div class="contextual">
|
||||
<%= link_to t(".heading_nutrient_view"), nutrients_project_foods_path(@project),
|
||||
class: 'icon icon-stats' %>
|
||||
<%= render partial: 'foods/contextual', locals: {view: :index} %>
|
||||
</div>
|
||||
|
||||
<%= render partial: 'foods/import' %>
|
||||
|
||||
<div id="new-food">
|
||||
</div>
|
||||
|
||||
<h2><%= t ".heading" %></h2>
|
||||
<div id='foods'>
|
||||
<%= render partial: 'foods/index' %>
|
||||
</div>
|
5
app/views/foods/index.js.erb
Normal file
5
app/views/foods/index.js.erb
Normal file
@ -0,0 +1,5 @@
|
||||
<% if params[:view] == 'index' %>
|
||||
$('#foods').html('<%= j render partial: 'foods/index' %>');
|
||||
<% else %>
|
||||
$('#nutrients').html('<%= j render partial: 'foods/nutrients' %>');
|
||||
<% end %>
|
2
app/views/foods/new.js.erb
Normal file
2
app/views/foods/new.js.erb
Normal file
@ -0,0 +1,2 @@
|
||||
$('#new-food')
|
||||
.html('<%= j render partial: 'foods/new_form', locals: {view: params[:view]} %>');
|
15
app/views/foods/nutrients.html.erb
Normal file
15
app/views/foods/nutrients.html.erb
Normal file
@ -0,0 +1,15 @@
|
||||
<div class="contextual">
|
||||
<%= link_to t(".heading_food_list"), project_foods_path(@project),
|
||||
class: 'icon icon-list' %>
|
||||
<%= render partial: 'foods/contextual', locals: {view: :nutrients} %>
|
||||
</div>
|
||||
|
||||
<%= render partial: 'foods/import' %>
|
||||
|
||||
<div id="new-food">
|
||||
</div>
|
||||
|
||||
<h2><%= t ".heading" %></h2>
|
||||
<div id='nutrients'>
|
||||
<%= render partial: 'foods/nutrients' %>
|
||||
</div>
|
1
app/views/foods/toggle.js.erb
Normal file
1
app/views/foods/toggle.js.erb
Normal file
@ -0,0 +1 @@
|
||||
$('#foods').html('<%= j render partial: 'foods/index' %>');
|
1
app/views/foods/toggle_column.js.erb
Normal file
1
app/views/foods/toggle_column.js.erb
Normal file
@ -0,0 +1 @@
|
||||
$('#nutrients').html('<%= j render partial: 'foods/nutrients' %>');
|
@ -1,6 +0,0 @@
|
||||
<% if User.current.allowed_to?(:manage_common, @project) %>
|
||||
<%= link_to t(".link_import_ingredients"), '#', class: 'icon icon-multiple',
|
||||
onclick: '$("#import-ingredients").show(); $("#filename").focus(); return false;' %>
|
||||
<%= link_to t(".link_new_ingredient"), new_project_ingredient_path(@project, view: view),
|
||||
{remote: true, class: 'icon icon-add'} %>
|
||||
<% end %>
|
@ -1,18 +0,0 @@
|
||||
<h2><%= t ".heading_new_ingredient" %></h2>
|
||||
|
||||
<%= labelled_form_for @ingredient,
|
||||
url: project_ingredients_path(@project, view: view),
|
||||
remote: true,
|
||||
html: {id: 'new-ingredient-form', name: 'new-ingredient-form'} do |f| %>
|
||||
|
||||
<%= render partial: 'ingredients/form', locals: {f: f} %>
|
||||
|
||||
<div class="tabular">
|
||||
<p>
|
||||
<%= submit_tag l(:button_create) %>
|
||||
<%= link_to l(:button_cancel), "#",
|
||||
onclick: '$("#new-ingredient").empty(); return false;' %>
|
||||
</p>
|
||||
</div>
|
||||
<% end %>
|
||||
<hr>
|
@ -1,6 +0,0 @@
|
||||
$('#new-ingredient').empty();
|
||||
<% if params[:view] == 'index' %>
|
||||
$('#ingredients').html('<%= j render partial: 'ingredients/index' %>');
|
||||
<% else %>
|
||||
$('#nutrients').html('<%= j render partial: 'ingredients/nutrients' %>');
|
||||
<% end %>
|
@ -1,3 +0,0 @@
|
||||
<% if @ingredient.destroyed? %>
|
||||
$('tr[id=ingredient-<%= @ingredient.id %>]').nextUntil('tr.primary').addBack().remove();
|
||||
<% end %>
|
@ -1,8 +0,0 @@
|
||||
$('tr[id=ingredient-<%= @ingredient.id %>]').nextUntil('tr.primary', ':not(.ingredient)')
|
||||
.remove();
|
||||
var columns = $('table > thead > tr > th').length;
|
||||
$('tr[id=ingredient-<%= @ingredient.id %>]').nextUntil('tr.primary').addBack().last().after(
|
||||
'<tr><td class="form" colspan="'+columns+'"><div id="edit-ingredient">' +
|
||||
'<%= j render partial: 'ingredients/edit_form', locals: {view: params[:view]} %>' +
|
||||
'</div></td></tr>'
|
||||
);
|
@ -1,15 +0,0 @@
|
||||
<div class="contextual">
|
||||
<%= link_to t(".heading_nutrient_view"), nutrients_project_ingredients_path(@project),
|
||||
class: 'icon icon-stats' %>
|
||||
<%= render partial: 'ingredients/contextual', locals: {view: :index} %>
|
||||
</div>
|
||||
|
||||
<%= render partial: 'ingredients/import' %>
|
||||
|
||||
<div id="new-ingredient">
|
||||
</div>
|
||||
|
||||
<h2><%= t ".heading" %></h2>
|
||||
<div id='ingredients'>
|
||||
<%= render partial: 'ingredients/index' %>
|
||||
</div>
|
@ -1,5 +0,0 @@
|
||||
<% if params[:view] == 'index' %>
|
||||
$('#ingredients').html('<%= j render partial: 'ingredients/index' %>');
|
||||
<% else %>
|
||||
$('#nutrients').html('<%= j render partial: 'ingredients/nutrients' %>');
|
||||
<% end %>
|
@ -1,2 +0,0 @@
|
||||
$('#new-ingredient')
|
||||
.html('<%= j render partial: 'ingredients/new_form', locals: {view: params[:view]} %>');
|
@ -1,15 +0,0 @@
|
||||
<div class="contextual">
|
||||
<%= link_to t(".heading_ingredient_list"), project_ingredients_path(@project),
|
||||
class: 'icon icon-list' %>
|
||||
<%= render partial: 'ingredients/contextual', locals: {view: :nutrients} %>
|
||||
</div>
|
||||
|
||||
<%= render partial: 'ingredients/import' %>
|
||||
|
||||
<div id="new-ingredient">
|
||||
</div>
|
||||
|
||||
<h2><%= t ".heading" %></h2>
|
||||
<div id='nutrients'>
|
||||
<%= render partial: 'ingredients/nutrients' %>
|
||||
</div>
|
@ -1 +0,0 @@
|
||||
$('#ingredients').html('<%= j render partial: 'ingredients/index' %>');
|
@ -1 +0,0 @@
|
||||
$('#nutrients').html('<%= j render partial: 'ingredients/nutrients' %>');
|
@ -12,9 +12,9 @@
|
||||
<ul>
|
||||
<li><%= link_to t(".link_meals"), project_meals_path(@project) %></li>
|
||||
<li>
|
||||
<%= link_to t(".link_ingredients"), project_ingredients_path(@project) %>
|
||||
<%= link_to t(".link_foods"), project_foods_path(@project) %>
|
||||
/
|
||||
<%= link_to t(".link_nutrients"), nutrients_project_ingredients_path(@project) %>
|
||||
<%= link_to t(".link_nutrients"), nutrients_project_foods_path(@project) %>
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
|
@ -1,6 +1,6 @@
|
||||
table.list tr.quantity.primary td.name {font-weight: bold;}
|
||||
|
||||
table.list tr.ingredient.hidden {opacity: 0.4}
|
||||
table.list tr.food.hidden {opacity: 0.4}
|
||||
|
||||
table.list .date,
|
||||
table.list .name,
|
||||
|
@ -27,10 +27,10 @@ en:
|
||||
readouts:
|
||||
duplicated_quantity_unit_pair: 'you can define each quantity/unit pair only
|
||||
once per measurement'
|
||||
ingredient:
|
||||
food:
|
||||
attributes:
|
||||
nutrients:
|
||||
duplicated_quantity: 'you can define each quantity only once per ingredient'
|
||||
duplicated_quantity: 'you can define each nutrient quantity only once per food'
|
||||
quantity:
|
||||
attributes:
|
||||
parent:
|
||||
@ -57,7 +57,7 @@ en:
|
||||
link_summary: 'Summary'
|
||||
link_meals: 'Meals'
|
||||
link_measurements: 'Measurements'
|
||||
link_ingredients: 'Ingredients'
|
||||
link_foods: 'Foods'
|
||||
link_nutrients: 'Nutrients'
|
||||
link_sources: 'Data sources'
|
||||
link_quantities: 'Quantities'
|
||||
@ -79,18 +79,18 @@ en:
|
||||
heading_new_measurement: 'New measurement'
|
||||
index:
|
||||
heading: 'Measurements'
|
||||
ingredients:
|
||||
foods:
|
||||
contextual:
|
||||
link_import_ingredients: 'Import'
|
||||
link_new_ingredient: 'New ingredient'
|
||||
link_import_foods: 'Import'
|
||||
link_new_food: 'New food'
|
||||
filters:
|
||||
visibility_prompt: 'all'
|
||||
import:
|
||||
heading_import_ingredients: 'Import'
|
||||
heading_import_foods: 'Import'
|
||||
label_import_select_csv_file: 'Select CSV file'
|
||||
import_hints: 'CSV file has to include header with column names. Recognized column
|
||||
names are:
|
||||
(1) ingredient attributes, case sensitive:
|
||||
(1) food attributes, case sensitive:
|
||||
"Name" - required, "Notes" - optional, "Reference" - defaults to 100[g],
|
||||
"Group" - defaults to "other", "Source" - optional, "SourceIdent" - optional,
|
||||
(2) quantities'' names with unit short name in square brackets.
|
||||
@ -105,13 +105,13 @@ en:
|
||||
other: 'other'
|
||||
meat: 'meat'
|
||||
new_form:
|
||||
heading_new_ingredient: 'New ingredient'
|
||||
heading_new_food: 'New food'
|
||||
index:
|
||||
heading: 'Ingredients'
|
||||
heading: 'Foods'
|
||||
heading_nutrient_view: 'Nutrient view'
|
||||
nutrients:
|
||||
heading: 'Nutrients'
|
||||
heading_ingredient_list: 'Ingredient list'
|
||||
heading_food_list: 'Food list'
|
||||
sources:
|
||||
index:
|
||||
heading: 'Data sources'
|
||||
|
@ -22,7 +22,7 @@ resources :projects, shallow: true do
|
||||
get 'filter'
|
||||
end
|
||||
end
|
||||
resources :ingredients, only: [:index, :new, :create, :edit, :update, :destroy] do
|
||||
resources :foods, only: [:index, :new, :create, :edit, :update, :destroy] do
|
||||
post 'toggle', on: :member
|
||||
collection do
|
||||
get 'nutrients'
|
||||
|
@ -39,7 +39,7 @@ class CreateSchema < ActiveRecord::Migration
|
||||
t.timestamps null: false
|
||||
end
|
||||
|
||||
create_table :ingredients do |t|
|
||||
create_table :foods do |t|
|
||||
t.references :project
|
||||
t.string :name
|
||||
t.text :notes
|
||||
@ -54,7 +54,7 @@ class CreateSchema < ActiveRecord::Migration
|
||||
end
|
||||
|
||||
create_table :nutrients do |t|
|
||||
t.references :ingredient
|
||||
t.references :food
|
||||
t.references :quantity
|
||||
t.decimal :amount, precision: 12, scale: 6
|
||||
t.references :unit
|
||||
@ -91,9 +91,9 @@ class CreateSchema < ActiveRecord::Migration
|
||||
t.timestamps null: false
|
||||
end
|
||||
|
||||
create_table :compositions_ingredients do |t|
|
||||
create_table :ingredients do |t|
|
||||
t.references :composition, polymorphic: true
|
||||
t.references :ingredient
|
||||
t.references :food
|
||||
t.references :part_of
|
||||
t.decimal :ready_ratio, precision: 12, scale: 6
|
||||
t.decimal :amount, precision: 12, scale: 6
|
||||
|
4
init.rb
4
init.rb
@ -16,7 +16,7 @@ Redmine::Plugin.register :body_tracking do
|
||||
meals: [:index],
|
||||
measurement_routines: [:show],
|
||||
measurements: [:index, :readouts, :filter],
|
||||
ingredients: [:index, :nutrients, :filter],
|
||||
foods: [:index, :nutrients, :filter],
|
||||
sources: [:index],
|
||||
quantities: [:index, :parents, :filter],
|
||||
units: [:index],
|
||||
@ -26,7 +26,7 @@ Redmine::Plugin.register :body_tracking do
|
||||
meals: [:new, :create, :edit, :update, :destroy],
|
||||
measurement_routines: [:edit],
|
||||
measurements: [:new, :create, :edit, :update, :destroy, :retake, :toggle_column],
|
||||
ingredients: [:new, :create, :edit, :update, :destroy, :toggle, :toggle_column,
|
||||
foods: [:new, :create, :edit, :update, :destroy, :toggle, :toggle_column,
|
||||
:import],
|
||||
sources: [:create, :destroy],
|
||||
quantities: [:new, :create, :edit, :update, :destroy, :move, :new_child, :create_child],
|
||||
|
@ -1,10 +1,10 @@
|
||||
module BodyTracking
|
||||
module ItemsWithQuantities
|
||||
RELATIONS = {
|
||||
'Ingredient' => {
|
||||
'Food' => {
|
||||
domain: :diet,
|
||||
subitem_class: Nutrient,
|
||||
foreign_key: :ingredient_id,
|
||||
foreign_key: :food_id,
|
||||
value_field: :amount
|
||||
},
|
||||
'Measurement' => {
|
||||
|
@ -5,7 +5,7 @@ module BodyTracking::ProjectPatch
|
||||
has_many :measurement_routines, dependent: :destroy
|
||||
has_many :measurements, -> { order "taken_at DESC" }, dependent: :destroy,
|
||||
extend: BodyTracking::ItemsWithQuantities, through: :measurement_routines
|
||||
has_many :ingredients, -> { order "name" }, dependent: :destroy,
|
||||
has_many :foods, -> { order "name" }, dependent: :destroy,
|
||||
extend: BodyTracking::ItemsWithQuantities
|
||||
|
||||
has_many :sources, dependent: :destroy
|
||||
|
@ -1,6 +1,6 @@
|
||||
require File.expand_path('../../test_helper', __FILE__)
|
||||
|
||||
class IngredientsControllerTest < ActionController::TestCase
|
||||
class FoodsControllerTest < ActionController::TestCase
|
||||
# Replace this with your real tests.
|
||||
def test_truth
|
||||
assert true
|
@ -1,6 +1,6 @@
|
||||
require File.expand_path('../../test_helper', __FILE__)
|
||||
|
||||
class IngredientTest < ActiveSupport::TestCase
|
||||
class FoodTest < ActiveSupport::TestCase
|
||||
|
||||
# Replace this with your real tests.
|
||||
def test_truth
|
Reference in New Issue
Block a user