Added Targets index
This commit is contained in:
parent
ffcc9553d5
commit
cf3bbb205d
@ -2,17 +2,21 @@ class TargetsController < ApplicationController
|
|||||||
layout 'body_tracking'
|
layout 'body_tracking'
|
||||||
menu_item :body_trackers
|
menu_item :body_trackers
|
||||||
helper :body_trackers
|
helper :body_trackers
|
||||||
|
helper_method :current_goal
|
||||||
|
|
||||||
include Concerns::Finders
|
include Concerns::Finders
|
||||||
|
|
||||||
before_action :find_project_by_project_id, only: [:index, :new, :create]
|
before_action :find_project_by_project_id, only: [:index, :new, :create]
|
||||||
|
before_action :find_quantity_by_quantity_id, only: [:toggle_exposure]
|
||||||
|
before_action :find_goal, only: [:toggle_exposure]
|
||||||
|
before_action :set_view_params
|
||||||
|
|
||||||
def index
|
def index
|
||||||
prepare_targets
|
prepare_targets
|
||||||
end
|
end
|
||||||
|
|
||||||
def new
|
def new
|
||||||
target = (@goal || @project.goals.binding).targets.new
|
target = current_goal.targets.new
|
||||||
target.arity.times { target.thresholds.new }
|
target.arity.times { target.thresholds.new }
|
||||||
@targets = [target]
|
@targets = [target]
|
||||||
end
|
end
|
||||||
@ -22,9 +26,15 @@ class TargetsController < ApplicationController
|
|||||||
@targets = goal.targets.build(targets_params[:targets]) do |target|
|
@targets = goal.targets.build(targets_params[:targets]) do |target|
|
||||||
target.effective_from = params[:target][:effective_from]
|
target.effective_from = params[:target][:effective_from]
|
||||||
end
|
end
|
||||||
|
# FIXME: add goal exposures before save and require (in model) goal.target_exposures to
|
||||||
|
# be present (same for measurement/food?)
|
||||||
|
|
||||||
# :save only after build, to re-display values in case records are invalid
|
# :save only after build, to re-display values in case records are invalid
|
||||||
if goal.save && Target.transaction { @targets.all?(&:save) }
|
if goal.save && Target.transaction { @targets.all?(&:save) }
|
||||||
|
if goal.target_exposures.empty?
|
||||||
|
goal.quantities << @targets.map { |t| t.thresholds.first.quantity }.first(6)
|
||||||
|
end
|
||||||
|
|
||||||
flash[:notice] = 'Created new target(s)'
|
flash[:notice] = 'Created new target(s)'
|
||||||
prepare_targets
|
prepare_targets
|
||||||
else
|
else
|
||||||
@ -36,6 +46,27 @@ class TargetsController < ApplicationController
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def edit
|
||||||
|
end
|
||||||
|
|
||||||
|
def update
|
||||||
|
end
|
||||||
|
|
||||||
|
def destroy
|
||||||
|
end
|
||||||
|
|
||||||
|
def reapply
|
||||||
|
end
|
||||||
|
|
||||||
|
def toggle_exposure
|
||||||
|
current_goal.target_exposures.toggle!(@quantity)
|
||||||
|
prepare_targets
|
||||||
|
end
|
||||||
|
|
||||||
|
def current_goal
|
||||||
|
@goal || @project.goals.binding
|
||||||
|
end
|
||||||
|
|
||||||
private
|
private
|
||||||
|
|
||||||
def goal_params
|
def goal_params
|
||||||
@ -59,6 +90,22 @@ class TargetsController < ApplicationController
|
|||||||
end
|
end
|
||||||
|
|
||||||
def prepare_targets
|
def prepare_targets
|
||||||
@targets = @project.targets.includes(:item, :thresholds)
|
@quantities = current_goal.quantities.includes(:formula)
|
||||||
|
|
||||||
|
@targets_by_date = Hash.new { |h,k| h[k] = {} }
|
||||||
|
@project.targets.includes(:item, thresholds: [:quantity]).reject(&:new_record?)
|
||||||
|
.each { |t| @targets_by_date[t.effective_from][t.thresholds.first.quantity] = t }
|
||||||
|
end
|
||||||
|
|
||||||
|
def set_view_params
|
||||||
|
@view_params = case params[:view]
|
||||||
|
when 'by_effective_from'
|
||||||
|
{view: :by_effective_from, effective_from: @effective_from}
|
||||||
|
when 'by_item_quantity'
|
||||||
|
{view: :by_item_quantity, item: nil, quantity: @quantity}
|
||||||
|
else
|
||||||
|
{view: :by_scope, scope: :all}
|
||||||
|
end
|
||||||
|
@view_params[goal_id] = @goal.id if @goal
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
@ -14,11 +14,15 @@ module BodyTrackersHelper
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def format_time(t)
|
def format_date(d)
|
||||||
t.strftime("%R") if t
|
d&.strftime("%F")
|
||||||
end
|
end
|
||||||
|
|
||||||
def toggle_exposure_options(enabled, domain)
|
def format_time(t)
|
||||||
|
t&.strftime("%R")
|
||||||
|
end
|
||||||
|
|
||||||
|
def toggle_exposure_options(enabled, domain = :all)
|
||||||
enabled = enabled.map { |q| [q.name, q.id] }
|
enabled = enabled.map { |q| [q.name, q.id] }
|
||||||
enabled_ids = enabled.map(&:last)
|
enabled_ids = enabled.map(&:last)
|
||||||
|
|
||||||
@ -49,8 +53,7 @@ module BodyTrackersHelper
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
# TODO: rename to quantities_table_header
|
def quantities_table_header(quantities)
|
||||||
def table_header_spec(quantities)
|
|
||||||
# spec: table of rows (tr), where each row is a hash of cells (td) (hash keeps items
|
# spec: table of rows (tr), where each row is a hash of cells (td) (hash keeps items
|
||||||
# ordered the way they were added). Hash values determine cell property:
|
# ordered the way they were added). Hash values determine cell property:
|
||||||
# * int > 0 - quantity name-labelled cell with 'int' size colspan
|
# * int > 0 - quantity name-labelled cell with 'int' size colspan
|
||||||
|
@ -3,11 +3,11 @@ module TargetsHelper
|
|||||||
Target::CONDITIONS
|
Target::CONDITIONS
|
||||||
end
|
end
|
||||||
|
|
||||||
def action_links(m)
|
def action_links(d)
|
||||||
link_to(l(:button_retake), retake_measurement_path(m, @view_params),
|
link_to(l(:button_reapply), reapply_project_targets_path(@project, d, @view_params),
|
||||||
{remote: true, class: "icon icon-reload"}) +
|
{remote: true, class: "icon icon-reload"}) +
|
||||||
link_to(l(:button_edit), edit_measurement_path(m, @view_params),
|
link_to(l(:button_edit), edit_target_path(@project, d, @view_params),
|
||||||
{remote: true, class: "icon icon-edit"}) +
|
{remote: true, class: "icon icon-edit"}) +
|
||||||
delete_link(measurement_path(m), {remote: true, data: {}})
|
delete_link(target_path(d), {remote: true, data: {}})
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
@ -1,6 +1,10 @@
|
|||||||
class Goal < ActiveRecord::Base
|
class Goal < ActiveRecord::Base
|
||||||
belongs_to :project, required: true
|
belongs_to :project, required: true
|
||||||
has_many :targets, inverse_of: :goal, dependent: :destroy
|
has_many :targets, -> { order "effective_from DESC" }, inverse_of: :goal,
|
||||||
|
dependent: :destroy, extend: BodyTracking::ItemsWithQuantities
|
||||||
|
has_many :target_exposures, as: :view, dependent: :destroy,
|
||||||
|
class_name: 'Exposure', extend: BodyTracking::TogglableExposures
|
||||||
|
has_many :quantities, -> { order "lft" }, through: :target_exposures
|
||||||
|
|
||||||
validates :name, presence: true, uniqueness: {scope: :project_id}
|
validates :name, presence: true, uniqueness: {scope: :project_id}
|
||||||
|
|
||||||
|
@ -16,7 +16,7 @@ class Target < ActiveRecord::Base
|
|||||||
validates :condition, inclusion: {in: CONDITIONS}
|
validates :condition, inclusion: {in: CONDITIONS}
|
||||||
validates :scope, inclusion: {in: [:ingredient, :meal, :day],
|
validates :scope, inclusion: {in: [:ingredient, :meal, :day],
|
||||||
if: -> { thresholds.first.quantity.domain == :diet }}
|
if: -> { thresholds.first.quantity.domain == :diet }}
|
||||||
validates :effective_from, presence: {unless: :is_binding?}, absence: {if: :is_binding?}
|
validates :effective_from, presence: {if: :is_binding?}, absence: {unless: :is_binding?}
|
||||||
|
|
||||||
after_initialize do
|
after_initialize do
|
||||||
if new_record?
|
if new_record?
|
||||||
@ -30,4 +30,8 @@ class Target < ActiveRecord::Base
|
|||||||
def arity
|
def arity
|
||||||
BigDecimal.method(condition).arity
|
BigDecimal.method(condition).arity
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def to_s
|
||||||
|
"#{condition} #{thresholds.first.value} [#{thresholds.first.unit.shortname}]"
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
27
app/views/body_trackers/_quantity_table_header.html.erb
Normal file
27
app/views/body_trackers/_quantity_table_header.html.erb
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
<% total_width = 4 + @quantities.length %>
|
||||||
|
<% header = quantities_table_header(@quantities) %>
|
||||||
|
<% header.each_with_index do |row, i| %>
|
||||||
|
<tr class="header">
|
||||||
|
<% if i == 0 %>
|
||||||
|
<td colspan="2" rowspan="<%= 1 + header.length %>"
|
||||||
|
style="width:<%= 3 * 100/total_width %>%; border: none;"></td>
|
||||||
|
<td class="quantityhead" rowspan="<%= header.length %>"
|
||||||
|
style="width:<%= 100/total_width %>%;"><%= l(:field_amount) %></td>
|
||||||
|
<% end %>
|
||||||
|
|
||||||
|
<% row.each do |q, span| %>
|
||||||
|
<td class="<%= span ? 'quantityhead' : 'quantityheadempty' %>"
|
||||||
|
<%= "colspan=#{span}" if span && span > 0 %>
|
||||||
|
<%= "rowspan=#{-span}" if span && span < 0 %>
|
||||||
|
style="width: <%= (span && span > 0 ? span : 1) * 100/total_width %>%;"
|
||||||
|
title="<%= q.description %>">
|
||||||
|
<%= q.name if span %>
|
||||||
|
</td>
|
||||||
|
<% end %>
|
||||||
|
|
||||||
|
<% if i == 0 %>
|
||||||
|
<td rowspan="<%= 1 + header.length %>"
|
||||||
|
style="width:<%= 100/total_width %>%; border: none;"></td>
|
||||||
|
<% end %>
|
||||||
|
</tr>
|
||||||
|
<% end %>
|
@ -8,7 +8,7 @@
|
|||||||
<table id="meals" class="list odd-even">
|
<table id="meals" class="list odd-even">
|
||||||
<tbody>
|
<tbody>
|
||||||
<% total_width = 4 + @quantities.length %>
|
<% total_width = 4 + @quantities.length %>
|
||||||
<% header = table_header_spec(@quantities) %>
|
<% header = quantities_table_header(@quantities) %>
|
||||||
<% @meals_by_date.reverse_each do |date, meals| %>
|
<% @meals_by_date.reverse_each do |date, meals| %>
|
||||||
<% header.each_with_index do |row, i| %>
|
<% header.each_with_index do |row, i| %>
|
||||||
<tr class="header">
|
<tr class="header">
|
||||||
|
@ -6,7 +6,7 @@
|
|||||||
<% if goal.persisted? %>
|
<% if goal.persisted? %>
|
||||||
<p><%= render partial: 'goals/show_form', locals: {goal: goal} %></p>
|
<p><%= render partial: 'goals/show_form', locals: {goal: goal} %></p>
|
||||||
<%#= t '.effective_from' %>
|
<%#= t '.effective_from' %>
|
||||||
<p><%= f.date_field :effective_from, disabled: goal.is_binding? %></p>
|
<p><%= f.date_field :effective_from, disabled: !goal.is_binding? %></p>
|
||||||
<% else %>
|
<% else %>
|
||||||
<%= render partial: 'goals/form', locals: {goal: goal} %>
|
<%= render partial: 'goals/form', locals: {goal: goal} %>
|
||||||
<% end %>
|
<% end %>
|
||||||
|
@ -1,32 +1,87 @@
|
|||||||
<% if @targets.any? { |t| t.persisted? } %>
|
<%#= render partial: 'measurements/filters',
|
||||||
<table class="list">
|
locals: {url: filter_project_measurements_path(@project, @view_params)} %>
|
||||||
<thead>
|
|
||||||
<tr>
|
<% if @targets_by_date.any? %>
|
||||||
<th style="width:5%"><%= l(:field_taken_at_date) %></th>
|
<%= render partial: 'targets/options' %>
|
||||||
<th><%= l(:field_name) %></th>
|
|
||||||
<th><%= l(:field_notes) %></th>
|
<% formulas = @quantities.map { |q| q.formula } %>
|
||||||
<th><%= l(:field_source) %></th>
|
<%# formulas.unshift(@filter_q.formula) if @filter_q %>
|
||||||
<th style="width:5%"><%= l(:field_action) %></th>
|
<%= error_messages_for *formulas %>
|
||||||
</tr>
|
|
||||||
</thead>
|
<table id="targets" class="list odd-even">
|
||||||
<tbody>
|
<tbody>
|
||||||
<% @measurements.each do |m| %>
|
<% total_width = 3 + @quantities.length %>
|
||||||
<% next if m.new_record? %>
|
<% header = quantities_table_header(@quantities) %>
|
||||||
<tr id="measurement-<%= m.id %>" class="primary measurement">
|
<% header.each_with_index do |row, i| %>
|
||||||
<td class="date unwrappable"><%= format_datetime(m) %></td>
|
<tr class="header">
|
||||||
<td class="name">
|
<% if i == 0 %>
|
||||||
<div style="float:left;">
|
<td class="quantityhead" rowspan="<%= header.length %>"
|
||||||
<%= link_to m.routine.name, readouts_measurement_routine_path(m.routine) %>
|
style="width:<%= 2 * 100/total_width %>%"><%= l(:field_effective_from) %></td>
|
||||||
</div>
|
<% end %>
|
||||||
<div style="float:right;">
|
|
||||||
<small><%= " (#{pluralize(m.readouts.size, 'readout')})" %></small>
|
<% row.each do |q, span| %>
|
||||||
</div>
|
<td class="<%= span ? 'quantityhead' : 'quantityheadempty' %>"
|
||||||
</td>
|
<%= "colspan=#{span}" if span && span > 0 %>
|
||||||
<td class="notes ellipsible"><%= m.notes %></td>
|
<%= "rowspan=#{-span}" if span && span < 0 %>
|
||||||
<td class="source"><%= m.source.name if m.source.present? %></td>
|
style="width: <%= (span && span > 0 ? span : 1) * 100/total_width %>%;"
|
||||||
<td class="action unwrappable"><%= action_links(m) %></td>
|
title="<%= q.description %>">
|
||||||
|
<%= q.name if span %>
|
||||||
|
</td>
|
||||||
|
<% end %>
|
||||||
|
|
||||||
|
<% if i == 0 %>
|
||||||
|
<td rowspan="<%= header.length %>"
|
||||||
|
style="width:<%= 100/total_width %>%;border:none;"><%= l(:field_action) %></td>
|
||||||
|
<% end %>
|
||||||
</tr>
|
</tr>
|
||||||
<% end %>
|
<% end %>
|
||||||
|
|
||||||
|
<% @targets_by_date.each do |date, targets| %>
|
||||||
|
<% row_class = "date #{cycle('odd', 'even')}" %>
|
||||||
|
<tr id="date-<%= date.strftime('%Y%m%d') %>" class="primary <%= row_class %>">
|
||||||
|
<td class="date unwrappable" style="cursor: pointer;"
|
||||||
|
onclick="$(this).closest('tr').toggle();
|
||||||
|
$(this).closest('tr').nextUntil('tr.primary', '.date').toggle();
|
||||||
|
return false;">
|
||||||
|
<span class="icon icon-bullet-closed"><%= format_date(date) %></span>
|
||||||
|
</td>
|
||||||
|
<% @quantities.each do |q| %>
|
||||||
|
<td class="primary value ellipsible"><%= targets[q] %></td>
|
||||||
|
<% end %>
|
||||||
|
<td class="action unwrappable"><%= action_links(date) %></td>
|
||||||
|
</tr>
|
||||||
|
|
||||||
|
<tr class="<%= row_class %>" style="display:none">
|
||||||
|
<% rows = @quantities.empty? ? 1 : (targets.length - 1) / @quantities.length + 1 %>
|
||||||
|
<td rowspan="<%= rows %>" class="date unwrappable" style="cursor: pointer;"
|
||||||
|
onclick="$(this).closest('tr').prev('tr.primary').toggle();
|
||||||
|
$(this).closest('tr').prev('tr.primary')
|
||||||
|
.nextUntil('tr.primary', '.date').toggle();
|
||||||
|
return false;">
|
||||||
|
<span class="icon icon-bullet-open"><%= format_date(date) %></span>
|
||||||
|
</td>
|
||||||
|
<% @quantities.each do |q| %>
|
||||||
|
<td class="primary quantity ellipsible">
|
||||||
|
<%= q.name %><p class="value"><%= targets.delete(q) %></p>
|
||||||
|
</td>
|
||||||
|
<% end %>
|
||||||
|
<td rowspan="<%= rows %>" class="action unwrappable"><%= action_links(date) %></td>
|
||||||
|
</tr>
|
||||||
|
|
||||||
|
<% targets.each_slice(@quantities.length) do |extras| %>
|
||||||
|
<tr class="extra <%= row_class %>" style="display:none">
|
||||||
|
<% extras.each do |q, t| %>
|
||||||
|
<td class="extra quantity ellipsible">
|
||||||
|
<%= q.name %><p class="value"><%= t %></p>
|
||||||
|
</td>
|
||||||
|
<% end %>
|
||||||
|
<% if @quantities.length > extras.length %>
|
||||||
|
<td class="space" colspan="<%= @quantities.length - extras.length %>"></td>
|
||||||
|
<% end %>
|
||||||
|
</tr>
|
||||||
|
<% end %>
|
||||||
|
|
||||||
|
<% end %>
|
||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
<% else %>
|
<% else %>
|
||||||
|
33
app/views/targets/_oldindex.html.erb
Normal file
33
app/views/targets/_oldindex.html.erb
Normal file
@ -0,0 +1,33 @@
|
|||||||
|
<% if @targets.any? %>
|
||||||
|
<table class="list">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th style="width:5%"><%= l(:field_effective_from) %></th>
|
||||||
|
<th><%= l(:field_name) %></th>
|
||||||
|
<th><%= l(:field_notes) %></th>
|
||||||
|
<th><%= l(:field_source) %></th>
|
||||||
|
<th style="width:5%"><%= l(:field_action) %></th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
<% @targets.select(&:persisted?).each do |t| %>
|
||||||
|
<tr id="target-<%= t.id %>" class="primary target">
|
||||||
|
<td class="date unwrappable"><%= format_date(t.effective_from) %></td>
|
||||||
|
<td class="name">
|
||||||
|
<div style="float:left;">
|
||||||
|
<%= link_to m.routine.name, readouts_measurement_routine_path(m.routine) %>
|
||||||
|
</div>
|
||||||
|
<div style="float:right;">
|
||||||
|
<small><%= " (#{pluralize(m.readouts.size, 'readout')})" %></small>
|
||||||
|
</div>
|
||||||
|
</td>
|
||||||
|
<td class="notes ellipsible"><%= m.notes %></td>
|
||||||
|
<td class="source"><%= m.source.name if m.source.present? %></td>
|
||||||
|
<td class="action unwrappable"><%= action_links(m) %></td>
|
||||||
|
</tr>
|
||||||
|
<% end %>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
<% else %>
|
||||||
|
<p class="nodata"><%= l(:label_no_data) %></p>
|
||||||
|
<% end %>
|
18
app/views/targets/_options.html.erb
Normal file
18
app/views/targets/_options.html.erb
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
<fieldset id="options" class="collapsible">
|
||||||
|
<legend onclick="toggleFieldset(this);"><%= l(:label_options) %></legend>
|
||||||
|
<div>
|
||||||
|
<%= form_tag toggle_exposure_goal_path(current_goal, @view_params),
|
||||||
|
id: 'toggle-exposure-form', name: 'toggle-exposure-form',
|
||||||
|
method: :post, remote: true do %>
|
||||||
|
|
||||||
|
<table>
|
||||||
|
<tr>
|
||||||
|
<td style="width:100%"></td>
|
||||||
|
<td><%= select_tag 'quantity_id',
|
||||||
|
toggle_exposure_options(current_goal.quantities) %></td>
|
||||||
|
<td><%= submit_tag l(:button_add) %></td>
|
||||||
|
</tr>
|
||||||
|
</table>
|
||||||
|
<% end %>
|
||||||
|
</div>
|
||||||
|
</fieldset>
|
@ -1,7 +1,7 @@
|
|||||||
$('#new-targets').empty();
|
$('#new-targets').empty();
|
||||||
<% case @view_params[:view] %>
|
<% case @view_params[:view] %>
|
||||||
<% when :by_date %>
|
<% when :by_date %>
|
||||||
$('#targets').html('<%= j render partial: 'targets/by_date' %>');
|
$('#targets').html('<%= j render partial: 'targets/by_effective_date' %>');
|
||||||
<% else %>
|
<% else %>
|
||||||
$('#targets').html('<%= j render partial: 'targets/index' %>');
|
$('#targets').html('<%= j render partial: 'targets/index' %>');
|
||||||
<% end %>
|
<% end %>
|
||||||
|
@ -22,6 +22,7 @@ en:
|
|||||||
field_formula: 'Formula'
|
field_formula: 'Formula'
|
||||||
field_code: 'Formula'
|
field_code: 'Formula'
|
||||||
field_shortname: 'Short name'
|
field_shortname: 'Short name'
|
||||||
|
button_reapply: 'Reapply'
|
||||||
button_eat: 'Eat'
|
button_eat: 'Eat'
|
||||||
button_notes: 'Notes'
|
button_notes: 'Notes'
|
||||||
button_retake: 'Retake'
|
button_retake: 'Retake'
|
||||||
|
@ -7,8 +7,16 @@ resources :projects, shallow: true do
|
|||||||
post 'defaults'
|
post 'defaults'
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
resources :goals, only: [:show, :edit]
|
resources :goals, only: [:show, :edit] do
|
||||||
resources :targets, except: [:show]
|
member do
|
||||||
|
post 'toggle_exposure', to: 'goals#toggle_exposure'
|
||||||
|
end
|
||||||
|
end
|
||||||
|
resources :targets, except: [:show] do
|
||||||
|
collection do
|
||||||
|
post 'reapply/:date', to: 'targets#reapply', as: :reapply
|
||||||
|
end
|
||||||
|
end
|
||||||
resources :ingredients, only: [] do
|
resources :ingredients, only: [] do
|
||||||
post 'adjust/:adjustment', to: 'meals#adjust', as: :adjust, on: :member
|
post 'adjust/:adjustment', to: 'meals#adjust', as: :adjust, on: :member
|
||||||
end
|
end
|
||||||
|
2
init.rb
2
init.rb
@ -27,7 +27,7 @@ Redmine::Plugin.register :body_tracking do
|
|||||||
permission :manage_common, {
|
permission :manage_common, {
|
||||||
body_trackers: [:defaults],
|
body_trackers: [:defaults],
|
||||||
goals: [:edit],
|
goals: [:edit],
|
||||||
targets: [:new, :create, :edit, :update, :destroy],
|
targets: [:new, :create, :edit, :update, :destroy, :reapply, :toggle_exposure],
|
||||||
meals: [:new, :create, :edit, :update, :destroy, :edit_notes, :update_notes,
|
meals: [:new, :create, :edit, :update, :destroy, :edit_notes, :update_notes,
|
||||||
:toggle_eaten, :toggle_exposure, :adjust],
|
:toggle_eaten, :toggle_exposure, :adjust],
|
||||||
measurement_routines: [:edit],
|
measurement_routines: [:edit],
|
||||||
|
Reference in New Issue
Block a user