logs
This commit is contained in:
parent
72fffcfbca
commit
e6a9e00f82
10
app/assets/javascripts/logs.js
Normal file
10
app/assets/javascripts/logs.js
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
(function($) {
|
||||||
|
|
||||||
|
$(document).on("turbolinks:load", function() {
|
||||||
|
$(".log-form input.datepicker").datepicker({autoclose: true, todayBtn: "linked", format: "yyyy-mm-dd"});
|
||||||
|
$(".log-form input.rating").starRating();
|
||||||
|
|
||||||
|
$(".log-table input.rating").starRating({readOnly: true});
|
||||||
|
});
|
||||||
|
|
||||||
|
})(jQuery);
|
@ -3,6 +3,8 @@
|
|||||||
$(document).on("turbolinks:load", function() {
|
$(document).on("turbolinks:load", function() {
|
||||||
$(".recipe-view ul.ingredients").checkable();
|
$(".recipe-view ul.ingredients").checkable();
|
||||||
$(".recipe-view ol.steps").checkable();
|
$(".recipe-view ol.steps").checkable();
|
||||||
|
|
||||||
|
$(".recipe-table input.rating").starRating({readOnly: true, interval: 0.25, size: '20px'});
|
||||||
});
|
});
|
||||||
|
|
||||||
})(jQuery);
|
})(jQuery);
|
||||||
|
@ -1,76 +1,115 @@
|
|||||||
|
|
||||||
(function ($) {
|
(function ($) {
|
||||||
|
|
||||||
var pluginName = "starRating";
|
var pluginName = "starRating";
|
||||||
var defaultOptions = {
|
var defaultOptions = {
|
||||||
starCount: 5
|
starCount: 5,
|
||||||
};
|
readOnly: false,
|
||||||
|
interval: 1,
|
||||||
|
size: '30px'
|
||||||
|
};
|
||||||
|
|
||||||
var methods = {
|
var methods = {
|
||||||
initialize: function(opts) {
|
initialize: function(opts) {
|
||||||
return this.each(function() {
|
return this.each(function() {
|
||||||
var options = _.extend({}, defaultOptions, opts);
|
var $input = $(this);
|
||||||
|
|
||||||
var $input = $(this);
|
if ($input.data(pluginName.toLowerCase()) === true) {
|
||||||
options.$input = $input;
|
if (console && console.log) {
|
||||||
|
console.log("star rating has already been initialized; skipping...");
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
var $widget = $("<span />").addClass("star-rating");
|
$input.attr("data-" + pluginName.toLowerCase(), "true");
|
||||||
|
|
||||||
for (var x = 1; x <= options.starCount; x++) {
|
var options = _.extend({}, defaultOptions, opts);
|
||||||
$widget.append(
|
|
||||||
$("<span />").addClass("star").data("rating", x)
|
var $widget = $("<span />").addClass("star-rating").css({'font-size': options.size});
|
||||||
);
|
var $emptySet = $("<span />").addClass("empty-set").appendTo($widget);
|
||||||
|
var $filledSet = $("<span />").addClass("filled-set").appendTo($widget);
|
||||||
|
|
||||||
|
options.$input = $input;
|
||||||
|
options.$emptySet = $emptySet;
|
||||||
|
|
||||||
|
for (var x = 1; x <= options.starCount; x++) {
|
||||||
|
$emptySet.append(
|
||||||
|
$("<span />").addClass("star empty")
|
||||||
|
);
|
||||||
|
|
||||||
|
$filledSet.append(
|
||||||
|
$("<span />").addClass("star full")
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
$widget.data(pluginName + ".options", options);
|
||||||
|
|
||||||
|
$input.data(pluginName, true).hide().after($widget);
|
||||||
|
|
||||||
|
privateMethods.updateStars($widget);
|
||||||
|
|
||||||
|
if (!options.readOnly) {
|
||||||
|
$widget
|
||||||
|
.on("click." + pluginName, function(e) {
|
||||||
|
var value = privateMethods.calculateRating($widget, e.pageX);
|
||||||
|
privateMethods.setValue($widget, value);
|
||||||
|
})
|
||||||
|
.on("mousemove." + pluginName, function(e) {
|
||||||
|
var value = privateMethods.calculateRating($widget, e.pageX);
|
||||||
|
privateMethods.updateStars($widget, value);
|
||||||
|
})
|
||||||
|
.on("mouseleave." + pluginName, function (e) {
|
||||||
|
privateMethods.updateStars($widget);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
$input
|
||||||
|
.on("change." + pluginName, function() {
|
||||||
|
privateMethods.updateStars($widget);
|
||||||
|
});
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
};
|
||||||
|
|
||||||
$widget.data(pluginName + ".options", options);
|
var privateMethods = {
|
||||||
|
updateStars: function($widget, value) {
|
||||||
|
var options = $widget.data(pluginName + ".options");
|
||||||
|
value = (value == null ? (parseFloat(options.$input.val() || 0)) : value);
|
||||||
|
$widget.find(".filled-set").css({width: privateMethods.calculateWidth($widget, value)});
|
||||||
|
},
|
||||||
|
|
||||||
$input.hide().after($widget);
|
setValue: function($widget, value) {
|
||||||
|
var options = $widget.data(pluginName + ".options");
|
||||||
|
options.$input.val(value);
|
||||||
|
privateMethods.updateStars($widget);
|
||||||
|
},
|
||||||
|
|
||||||
privateMethods.updateStars($widget);
|
calculateWidth: function($widget, value) {
|
||||||
|
var options = $widget.data(pluginName + ".options");
|
||||||
|
var width = options.$emptySet.width();
|
||||||
|
return ((value / options.starCount) * 100).toString() + "%";
|
||||||
|
},
|
||||||
|
|
||||||
$widget.on("click." + pluginName, "span.star", function(e) {
|
// Given a screen X coordinate, calculates the nearest valid value for this rating widget
|
||||||
var value = $(this).data("rating");
|
calculateRating: function($widget, screenX) {
|
||||||
privateMethods.setValue($widget, value);
|
var options = $widget.data(pluginName + ".options");
|
||||||
});
|
var offset = options.$emptySet.offset();
|
||||||
|
var width = options.$emptySet.width();
|
||||||
|
var ratio = (screenX - offset.left) / width;
|
||||||
|
ratio = Math.max(0, Math.min(1, ratio));
|
||||||
|
|
||||||
$input.on("change." + pluginName, function() {
|
return Math.ceil(options.starCount * (1 / options.interval) * ratio) / (1 / options.interval);
|
||||||
privateMethods.updateStars($widget);
|
}
|
||||||
});
|
};
|
||||||
});
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
var privateMethods = {
|
$.fn[pluginName] = function(method) {
|
||||||
updateStars: function($widget) {
|
if (methods[method]) {
|
||||||
var options = $widget.data(pluginName + ".options");
|
return methods[method].apply(this, Array.prototype.slice.call(arguments, 1));
|
||||||
var value = parseInt(options.$input.val());
|
} else if (typeof method === 'object' || ! method) {
|
||||||
|
return methods.initialize.apply(this, arguments);
|
||||||
$widget.find(".star").each(function(idx, elem) {
|
|
||||||
var $star = $(elem);
|
|
||||||
$star.removeClass("empty full");
|
|
||||||
if ($star.data("rating") <= value) {
|
|
||||||
$star.addClass("full");
|
|
||||||
} else {
|
} else {
|
||||||
$star.addClass("empty");
|
$.error('Method ' + method + ' does not exist on jQuery.' + pluginName);
|
||||||
}
|
}
|
||||||
});
|
};
|
||||||
},
|
|
||||||
|
|
||||||
setValue: function($widget, value) {
|
|
||||||
var options = $widget.data(pluginName + ".options");
|
|
||||||
options.$input.val(value);
|
|
||||||
privateMethods.updateStars($widget);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
$.fn[pluginName] = function(method) {
|
|
||||||
if (methods[method]) {
|
|
||||||
return methods[method].apply(this, Array.prototype.slice.call(arguments, 1));
|
|
||||||
} else if (typeof method === 'object' || ! method) {
|
|
||||||
return methods.initialize.apply(this, arguments);
|
|
||||||
} else {
|
|
||||||
$.error('Method ' + method + ' does not exist on jQuery.' + pluginName);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
})(jQuery);
|
})(jQuery);
|
@ -2,10 +2,23 @@
|
|||||||
|
|
||||||
span.star-rating {
|
span.star-rating {
|
||||||
|
|
||||||
display: block;
|
display: inline-block;
|
||||||
font-size: 30px;
|
|
||||||
color: gold;
|
color: gold;
|
||||||
cursor: default;
|
cursor: default;
|
||||||
|
position: relative;
|
||||||
|
white-space: nowrap;
|
||||||
|
|
||||||
|
.empty-set {
|
||||||
|
color: gray;
|
||||||
|
opacity: 0.5;
|
||||||
|
}
|
||||||
|
|
||||||
|
.filled-set {
|
||||||
|
overflow-x: hidden;
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
}
|
||||||
|
|
||||||
span.star {
|
span.star {
|
||||||
|
|
||||||
|
@ -3,14 +3,11 @@ class LogsController < ApplicationController
|
|||||||
before_action :ensure_valid_user
|
before_action :ensure_valid_user
|
||||||
|
|
||||||
before_action :set_log, only: [:show, :edit, :update, :destroy]
|
before_action :set_log, only: [:show, :edit, :update, :destroy]
|
||||||
before_action :set_recipe, only: [:index, :new, :create]
|
before_action :set_recipe, only: [:new, :create]
|
||||||
before_action :require_recipe, only: [:new, :create]
|
before_action :require_recipe, only: [:new, :create]
|
||||||
|
|
||||||
def index
|
def index
|
||||||
@logs = Log.for_user(current_user)
|
@logs = Log.for_user(current_user).order(:date)
|
||||||
if @recipe
|
|
||||||
@logs = @logs.for_recipe(@recipe)
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|
||||||
def show
|
def show
|
||||||
@ -41,8 +38,9 @@ class LogsController < ApplicationController
|
|||||||
|
|
||||||
def create
|
def create
|
||||||
@log = Log.new
|
@log = Log.new
|
||||||
@log.recipe = @recipe.log_copy(current_user)
|
|
||||||
@log.assign_attributes(log_params)
|
@log.assign_attributes(log_params)
|
||||||
|
@log.recipe.is_log = true
|
||||||
|
@log.recipe.user = current_user
|
||||||
@log.user = current_user
|
@log.user = current_user
|
||||||
@log.source_recipe = @recipe
|
@log.source_recipe = @recipe
|
||||||
|
|
||||||
@ -79,7 +77,7 @@ class LogsController < ApplicationController
|
|||||||
end
|
end
|
||||||
|
|
||||||
def log_params
|
def log_params
|
||||||
params.require(:log).permit(:date, :rating, recipe_attributes: [:name, :description, :source, :yields, :total_time, :active_time, recipe_ingredients_attributes: [:name, :ingredient_id, :quantity, :units, :preparation, :sort_order, :id, :_destroy], recipe_steps_attributes: [:step, :sort_order, :id, :_destroy]])
|
params.require(:log).permit(:date, :rating, :notes, recipe_attributes: [:name, :description, :source, :yields, :total_time, :active_time, recipe_ingredients_attributes: [:name, :ingredient_id, :quantity, :units, :preparation, :sort_order, :id, :_destroy], recipe_steps_attributes: [:step, :sort_order, :id, :_destroy]])
|
||||||
end
|
end
|
||||||
|
|
||||||
end
|
end
|
@ -1,9 +1,11 @@
|
|||||||
class LogDecorator < BaseDecorator
|
class LogDecorator < BaseDecorator
|
||||||
|
|
||||||
|
def recipe
|
||||||
|
RecipeDecorator.decorate(wrapped.recipe, h)
|
||||||
|
end
|
||||||
|
|
||||||
def date
|
def date
|
||||||
v = super
|
v = super
|
||||||
puts v
|
|
||||||
puts '================'
|
|
||||||
if v && v.respond_to?(:strftime)
|
if v && v.respond_to?(:strftime)
|
||||||
v.strftime("%Y-%m-%d")
|
v.strftime("%Y-%m-%d")
|
||||||
else
|
else
|
||||||
|
@ -24,4 +24,8 @@ class RecipeDecorator < BaseDecorator
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def average_rating
|
||||||
|
@average_rating ||= (Log.for_recipe(wrapped).for_user(self.user).where('rating IS NOT NULL').average(:rating) || 0)
|
||||||
|
end
|
||||||
|
|
||||||
end
|
end
|
@ -21,6 +21,7 @@ module ApplicationHelper
|
|||||||
nav = [
|
nav = [
|
||||||
nav_item('Recipes', recipes_path, 'recipes'),
|
nav_item('Recipes', recipes_path, 'recipes'),
|
||||||
nav_item('Ingredients', ingredients_path, 'ingredients'),
|
nav_item('Ingredients', ingredients_path, 'ingredients'),
|
||||||
|
nav_item('Logs', logs_path, 'logs'),
|
||||||
nav_item('Calculator', calculator_path, 'calculator'),
|
nav_item('Calculator', calculator_path, 'calculator'),
|
||||||
nav_item('About', about_path, 'home')
|
nav_item('About', about_path, 'home')
|
||||||
]
|
]
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
<% @log = decorate(@log, LogDecorator) %>
|
<% @log = decorate(@log, LogDecorator) %>
|
||||||
|
|
||||||
<%= form_for([@recipe, @log]) do |f| %>
|
<%= form_for([@recipe, @log], html: {class: 'log-form'}) do |f| %>
|
||||||
|
|
||||||
<%= render partial: 'shared/error_list', locals: {model: @log} %>
|
<%= render partial: 'shared/error_list', locals: {model: @log} %>
|
||||||
|
|
||||||
@ -44,10 +44,3 @@
|
|||||||
|
|
||||||
<% end %>
|
<% end %>
|
||||||
|
|
||||||
<script type="text/javascript">
|
|
||||||
|
|
||||||
$(document).on("turbolinks:load", function() {
|
|
||||||
$("input.datepicker").datepicker({autoclose: true, todayBtn: "linked", format: "yyyy-mm-dd"});
|
|
||||||
$("input.rating").starRating();
|
|
||||||
});
|
|
||||||
</script>
|
|
@ -0,0 +1,40 @@
|
|||||||
|
<div class="row">
|
||||||
|
<div class="col-xs-12">
|
||||||
|
|
||||||
|
<div class="page-header">
|
||||||
|
<h1>Log Entries</h1>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<% if @logs.empty? %>
|
||||||
|
<p>No Entries</p>
|
||||||
|
<% else %>
|
||||||
|
|
||||||
|
<div class="table-responsive">
|
||||||
|
<table class="log-table table table-striped table-hover">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th>Recipe</th>
|
||||||
|
<th>Date</th>
|
||||||
|
<th>Rating</th>
|
||||||
|
<th>Notes</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
|
||||||
|
<tbody>
|
||||||
|
<% decorate(@logs, LogDecorator).each do |log| %>
|
||||||
|
<tr>
|
||||||
|
<td><%= link_to log.recipe.short_name, log %></td>
|
||||||
|
<td><%= log.date %></td>
|
||||||
|
<td><input type="hidden" class="rating" value="<%= log.rating %>" /></td>
|
||||||
|
<td><%= %></td>
|
||||||
|
</tr>
|
||||||
|
<% end %>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<% end %>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
@ -10,10 +10,11 @@
|
|||||||
<% else %>
|
<% else %>
|
||||||
|
|
||||||
<div class="table-responsive">
|
<div class="table-responsive">
|
||||||
<table class="table table-striped table-hover">
|
<table class="recipe-table table table-striped table-hover">
|
||||||
<thead>
|
<thead>
|
||||||
<tr>
|
<tr>
|
||||||
<th>Name</th>
|
<th>Name</th>
|
||||||
|
<th>Rating</th>
|
||||||
<th>Yields</th>
|
<th>Yields</th>
|
||||||
<th>Time</th>
|
<th>Time</th>
|
||||||
<th>Created</th>
|
<th>Created</th>
|
||||||
@ -27,6 +28,13 @@
|
|||||||
<% decorate(@recipes, RecipeDecorator).each do |recipe| %>
|
<% decorate(@recipes, RecipeDecorator).each do |recipe| %>
|
||||||
<tr>
|
<tr>
|
||||||
<td><%= link_to recipe.short_name, recipe %></td>
|
<td><%= link_to recipe.short_name, recipe %></td>
|
||||||
|
<td>
|
||||||
|
<% if recipe.average_rating > 0 %>
|
||||||
|
<input type="hidden" class="rating" value="<%= recipe.average_rating %>" />
|
||||||
|
<% else %>
|
||||||
|
--
|
||||||
|
<% end %>
|
||||||
|
</td>
|
||||||
<td><%= recipe.yields %></td>
|
<td><%= recipe.yields %></td>
|
||||||
<td><%= recipe_time(recipe) %></td>
|
<td><%= recipe_time(recipe) %></td>
|
||||||
<td><%= timestamp(recipe.created_at) %></td>
|
<td><%= timestamp(recipe.created_at) %></td>
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
<% if model.errors.any? %>
|
<% if model.errors.any? %>
|
||||||
<div id="error_explanation" class="alert alert-danger">
|
<div id="error_explanation" class="alert alert-danger">
|
||||||
<h4><%= pluralize(model.errors.count, 'error') %> prohibited this <%= model.class.model_name.human %> from being saved:</h4>
|
<h4><%= pluralize(model.errors.count, 'error') %> prohibited this <%= model.model_name.human %> from being saved:</h4>
|
||||||
|
|
||||||
<ul>
|
<ul>
|
||||||
<% model.errors.full_messages.each do |msg| %>
|
<% model.errors.full_messages.each do |msg| %>
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
Rails.application.routes.draw do
|
Rails.application.routes.draw do
|
||||||
|
|
||||||
resources :recipes do
|
resources :recipes do
|
||||||
resources :logs, only: [:index, :new, :create]
|
resources :logs, only: [:new, :create]
|
||||||
end
|
end
|
||||||
|
|
||||||
resources :logs, except: [:new, :create]
|
resources :logs, except: [:new, :create]
|
||||||
|
Loading…
Reference in New Issue
Block a user