Added ingredient prep to recipe_ingredient; update to bulk editing

This commit is contained in:
Dan Elbert 2016-01-30 20:29:35 -06:00
parent 0cc568344e
commit 422c77b131
9 changed files with 137 additions and 61 deletions

View File

@ -53,7 +53,6 @@
$editors.each(function(idx, elem) { $editors.each(function(idx, elem) {
var $editor = $(elem); var $editor = $(elem);
var $ingredientId = $editor.find("input.ingredient_id"); var $ingredientId = $editor.find("input.ingredient_id");
var $customDensity = $editor.find("input.custom_density");
var $group = $editor.find("div.typeahead-group"); var $group = $editor.find("div.typeahead-group");
$editor.find(".ingredient-typeahead").typeahead({}, $editor.find(".ingredient-typeahead").typeahead({},
@ -66,7 +65,6 @@
}); });
if ($ingredientId.val().length) { if ($ingredientId.val().length) {
$customDensity.prop('disabled', true);
$group.addClass("has-success"); $group.addClass("has-success");
} }
}); });
@ -75,12 +73,10 @@
function ingredientItemPicked($typeahead, datum) { function ingredientItemPicked($typeahead, datum) {
var $container = $typeahead.closest(".nested-fields"); var $container = $typeahead.closest(".nested-fields");
var $ingredientId = $container.find("input.ingredient_id"); var $ingredientId = $container.find("input.ingredient_id");
var $customDensity = $container.find("input.custom_density");
var $group = $container.find("div.typeahead-group"); var $group = $container.find("div.typeahead-group");
$ingredientId.val(datum.id); $ingredientId.val(datum.id);
$typeahead.typeahead('val', datum.name); $typeahead.typeahead('val', datum.name);
$customDensity.val(datum.density).prop('disabled', true);
$group.addClass("has-success"); $group.addClass("has-success");
} }
@ -103,22 +99,41 @@
} }
} }
function addIngredient(quantity, units, name, ingredient_id) { function addIngredient(item) {
$("#ingredient-list").one("cocoon:before-insert", function(e, $container) { $("#ingredient-list").one("cocoon:before-insert", function(e, $container) {
var $ingredientId = $container.find("input.ingredient_id"); var $ingredientId = $container.find("input.ingredient_id");
var $name = $container.find("input.custom_name"); var $name = $container.find("input.ingredient-typeahead.tt-input");
var $quantity = $container.find("input.quantity"); var $quantity = $container.find("input.quantity");
var $units = $container.find("input.units"); var $units = $container.find("input.units");
var $preparation = $container.find("input.preparation");
$ingredientId.typeahead("val", name); $name.typeahead("val", item.name);
$name.val(name); $ingredientId.val(item.ingredient_id);
$units.val(units); $units.val(item.units);
$quantity.val(quantity); $quantity.val(item.quantity);
$preparation.val(item.preparation);
}); });
$("#addIngredientButton").trigger("click"); $("#addIngredientButton").trigger("click");
} }
function getIngredients() {
var data = [];
$("#ingredient-list .ingredient-editor").each(function() {
var $container = $(this);
var $ingredientId = $container.find("input.ingredient_id");
var $name = $container.find("input.ingredient-typeahead.tt-input");
var $quantity = $container.find("input.quantity");
var $units = $container.find("input.units");
var $preparation = $container.find("input.preparation");
data.push({ingredient_id: $ingredientId.val(), name: $name.typeahead("val"), quantity: $quantity.val(), units: $units.val(), preparation: $preparation.val()});
});
return data;
}
function addStep(step) { function addStep(step) {
$("#step-list").one("cocoon:before-insert", function(e, $container) { $("#step-list").one("cocoon:before-insert", function(e, $container) {
var $step = $container.find("textarea.step"); var $step = $container.find("textarea.step");
@ -226,14 +241,7 @@
var $ingredientBulkList = $("#ingredient_bulk_parsed_list"); var $ingredientBulkList = $("#ingredient_bulk_parsed_list");
autosize($ingredientBulkInput); autosize($ingredientBulkInput);
$bulkIngredientsModal var parseBulkIngredients = function() {
.on('show.bs.modal', function (event) {
$ingredientBulkInput.val('');
$ingredientBulkList.empty();
autosize.update($ingredientBulkInput);
});
$ingredientBulkInput.on('keyup', function() {
var data = $ingredientBulkInput.val(); var data = $ingredientBulkInput.val();
$ingredientBulkList.empty(); $ingredientBulkList.empty();
@ -242,7 +250,7 @@
var lines = data.replace("\r", "").split("\n"); var lines = data.replace("\r", "").split("\n");
var regex = /^(?:([\d\/.]+(?:\s+[\d\/]+)?)\s+)?(?:([\w-]+)(?:\s+of)?\s+)?(\w[\w ,\-\(\)\/'"]*)$/i; var regex = /^(?:([\d\/.]+(?:\s+[\d\/]+)?)\s+)?(?:([\w-]+)(?:\s+of)?\s+)?([^,]*)(?:,\s*(.*))?$/i;
var magicFunc = function(str) { var magicFunc = function(str) {
if (str == "-") { if (str == "-") {
@ -256,10 +264,23 @@
var line = lines[x].trim(); var line = lines[x].trim();
if (line.length == 0) { continue; } if (line.length == 0) { continue; }
var barIndex = line.lastIndexOf("|");
var afterBar = null;
if (barIndex >= 0) {
afterBar = line.slice(barIndex + 1);
line = line.slice(0, barIndex);
}
var match = line.match(regex); var match = line.match(regex);
if (match) { if (match) {
parsed.push({quantity: magicFunc(match[1]), units: magicFunc(match[2]), name: magicFunc(match[3])}); item = {quantity: magicFunc(match[1]), units: magicFunc(match[2]), name: magicFunc(match[3]), preparation: magicFunc(match[4])};
if (afterBar) {
item.name = item.name + ", " + item.preparation;
item.preparation = afterBar;
}
parsed.push(item);
} else { } else {
parsed.push(null); parsed.push(null);
} }
@ -275,14 +296,44 @@
.append($("<td />").addClass("quantity").text(item.quantity)) .append($("<td />").addClass("quantity").text(item.quantity))
.append($("<td />").addClass("units").text(item.units)) .append($("<td />").addClass("units").text(item.units))
.append($("<td />").addClass("name").text(item.name)) .append($("<td />").addClass("name").text(item.name))
.append($("<td />").addClass("preparation").text(item.preparation))
); );
} else { } else {
$ingredientBulkList.append( $ingredientBulkList.append(
$("<tr />") $("<tr />")
.append($("<td />").attr("colspan", "3").text("<Cannot Parse>")) .append($("<td />").attr("colspan", "4").text("<Cannot Parse>"))
); );
} }
} }
};
$bulkIngredientsModal
.on('show.bs.modal', function (event) {
var data = getIngredients();
var x;
var text = [];
for (x = 0; x < data.length; x++) {
var item = data[x];
text.push(
item.quantity + " " +
(item.units || "-") + " " +
item.name +
(item.preparation ? (", " + item.preparation) : "")
);
}
$ingredientBulkInput.val(text.join("\n"));
setTimeout(function() {
parseBulkIngredients();
autosize.update($ingredientBulkInput);
}, 250);
});
$ingredientBulkInput.on('keyup', function() {
parseBulkIngredients();
}); });
$("#bulkIngredientAddSubmit").on("click", function() { $("#bulkIngredientAddSubmit").on("click", function() {
@ -293,7 +344,7 @@
for (x = 0; x < parsed.length; x++) { for (x = 0; x < parsed.length; x++) {
var item = parsed[x]; var item = parsed[x];
if (item) { if (item) {
addIngredient(item.quantity, item.units, item.name, item.ingredient_id) addIngredient(item)
} }
} }
} }

View File

@ -27,6 +27,7 @@
div.ingredient-editor { div.ingredient-editor {
@include editor; @include editor;
} }
div.step-editor { div.step-editor {
@ -50,6 +51,19 @@ div#ingredient-list, div#step-list {
padding-bottom: 15px; padding-bottom: 15px;
} }
div#ingredient-list {
@media (min-width: $screen-md-min) {
.ingredient-editor .control-label {
display: none;
}
.ingredient-editor:first-child .control-label {
display: inline-block;
}
}
}
div.recipe-view { div.recipe-view {
.source { .source {

View File

@ -24,6 +24,7 @@ class RecipesController < ApplicationController
# GET /recipes/new # GET /recipes/new
def new def new
@recipe = Recipe.new @recipe = Recipe.new
@recipe.recipe_ingredients << RecipeIngredient.new
end end
# GET /recipes/1/edit # GET /recipes/1/edit
@ -75,6 +76,6 @@ class RecipesController < ApplicationController
# Never trust parameters from the scary internet, only allow the white list through. # Never trust parameters from the scary internet, only allow the white list through.
def recipe_params def recipe_params
params.require(:recipe).permit(:name, :description, :source, :yields, :total_time, :active_time, recipe_ingredients_attributes: [:custom_name, :custom_density, :ingredient_id, :quantity, :units, :sort_order, :id, :_destroy], recipe_steps_attributes: [:step, :sort_order, :id, :_destroy]) params.require(:recipe).permit(: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

View File

@ -6,7 +6,7 @@ class RecipeIngredient < ActiveRecord::Base
validates :sort_order, presence: true validates :sort_order, presence: true
validates :custom_density, density: true, allow_blank: true validates :custom_density, density: true, allow_blank: true
def custom_name def name
if self.ingredient_id.present? if self.ingredient_id.present?
self.ingredient.name self.ingredient.name
else else
@ -14,23 +14,15 @@ class RecipeIngredient < ActiveRecord::Base
end end
end end
def custom_density
if self.ingredient_id.present?
self.ingredient.density
else
super
end
end
def display_name def display_name
if quantity.present? && units.present? if quantity.present? && units.present?
"#{quantity} #{units} of #{custom_name}" "#{quantity} #{units} #{name}"
elsif quantity.present? elsif quantity.present?
"#{quantity} #{custom_name}" "#{quantity} #{name}"
elsif units.present? elsif units.present?
"#{units} #{custom_name}" "#{units} #{name}"
else else
custom_name name
end end
end end

View File

@ -47,7 +47,7 @@
<h3>Ingredients</h3> <h3>Ingredients</h3>
<button type="button" class="btn btn-primary" data-toggle="modal" data-target="#bulk_ingredients_modal">Bulk Add</button> <button type="button" class="btn btn-primary" data-toggle="modal" data-target="#bulk_ingredients_modal">Bulk Edit</button>
<div id="ingredient-list"> <div id="ingredient-list">
<%= f.fields_for :recipe_ingredients do |ri_form| %> <%= f.fields_for :recipe_ingredients do |ri_form| %>
<%= render partial: 'recipes/editor/ingredient', locals: { f: ri_form } %> <%= render partial: 'recipes/editor/ingredient', locals: { f: ri_form } %>
@ -58,7 +58,7 @@
<h3>Steps</h3> <h3>Steps</h3>
<button type="button" class="btn btn-primary" data-toggle="modal" data-target="#bulk_steps_modal">Bulk Add</button> <button type="button" class="btn btn-primary" data-toggle="modal" data-target="#bulk_steps_modal">Bulk Edit</button>
<div id="step-list"> <div id="step-list">
<%= f.fields_for :recipe_steps do |rs_form| %> <%= f.fields_for :recipe_steps do |rs_form| %>
<%= render partial: 'recipes/editor/step', locals: { f: rs_form } %> <%= render partial: 'recipes/editor/step', locals: { f: rs_form } %>

View File

@ -11,19 +11,20 @@
<div class="row"> <div class="row">
<div class="col-xs-6"> <div class="col-xs-6">
<div class="form-group"> <div class="form-group form-group-lg">
<label for="ingredient_bulk_input" class="control-label">Raw Input</label> <label for="ingredient_bulk_input" class="control-label">Raw Input</label>
<textarea id="ingredient_bulk_input" class="form-control"></textarea> <textarea id="ingredient_bulk_input" class="form-control"></textarea>
</div> </div>
</div> </div>
<div class="col-xs-6"> <div class="col-xs-6">
<table class="table"> <table class="table table-condensed table-bordered">
<thead> <thead>
<tr> <tr>
<th>Quantity</th> <th>#</th>
<th>Unit</th> <th>Unit</th>
<th>Name</th> <th>Name</th>
<th>Prep</th>
</tr> </tr>
</thead> </thead>
<tbody id="ingredient_bulk_parsed_list"> <tbody id="ingredient_bulk_parsed_list">

View File

@ -6,20 +6,6 @@
<div class="row"> <div class="row">
<div class="col-xs-12 col-md-7">
<div class="form-group form-group-sm typeahead-group">
<%= f.label :custom_name, "Name", class: "control-label" %>
<div class="input-group input-group-sm">
<%= f.text_field :custom_name, class: 'form-control ingredient-typeahead custom_name' %>
<span class="input-group-btn">
<button tabindex="-1" class="btn btn-info ingredient_convert_btn" type="button" data-toggle="modal" data-target="#convert_modal">
<span class="glyphicon glyphicon-transfer"></span>
</button>
</span>
</div>
</div>
</div>
<div class="col-xs-6 col-sm-6 col-md-2"> <div class="col-xs-6 col-sm-6 col-md-2">
<div class="form-group form-group-sm"> <div class="form-group form-group-sm">
<%= f.label :quantity, class: "control-label" %> <%= f.label :quantity, class: "control-label" %>
@ -30,10 +16,32 @@
<div class="col-xs-6 col-sm-6 col-md-3"> <div class="col-xs-6 col-sm-6 col-md-3">
<div class="form-group form-group-sm"> <div class="form-group form-group-sm">
<%= f.label :units, class: "control-label" %> <%= f.label :units, class: "control-label" %>
<div class="input-group input-group-sm">
<%= f.text_field :units, class: 'form-control units' %> <%= f.text_field :units, class: 'form-control units' %>
<span class="input-group-btn">
<button tabindex="-1" class="btn btn-info ingredient_convert_btn" type="button" data-toggle="modal" data-target="#convert_modal">
<span class="glyphicon glyphicon-transfer"></span>
</button>
</span>
</div> </div>
</div> </div>
</div> </div>
<div class="col-xs-6 col-md-3">
<div class="form-group form-group-sm typeahead-group">
<%= f.label :name, class: "control-label" %>
<%= f.text_field :name, class: 'form-control ingredient-typeahead custom' %>
</div>
</div>
<div class="col-xs-6 col-md-4">
<div class="form-group form-group-sm">
<%= f.label :preparation, class: "control-label" %>
<%= f.text_field :preparation, class: 'form-control preparation' %>
</div>
</div>
</div>
</div> </div>
<div class="col-xs-2 col-sm-1"> <div class="col-xs-2 col-sm-1">

View File

@ -0,0 +1,9 @@
class ChangeIngredients < ActiveRecord::Migration
def change
change_table :recipe_ingredients do |t|
t.remove :custom_density
t.rename :custom_name, :name
t.text :preparation
end
end
end

View File

@ -11,7 +11,7 @@
# #
# It's strongly recommended that you check this file into your version control system. # It's strongly recommended that you check this file into your version control system.
ActiveRecord::Schema.define(version: 20160124231837) do ActiveRecord::Schema.define(version: 20160130231838) do
create_table "ingredients", force: :cascade do |t| create_table "ingredients", force: :cascade do |t|
t.string "name" t.string "name"
@ -33,13 +33,13 @@ ActiveRecord::Schema.define(version: 20160124231837) do
create_table "recipe_ingredients", force: :cascade do |t| create_table "recipe_ingredients", force: :cascade do |t|
t.integer "ingredient_id" t.integer "ingredient_id"
t.integer "recipe_id" t.integer "recipe_id"
t.string "custom_name" t.string "name"
t.string "custom_density"
t.integer "sort_order" t.integer "sort_order"
t.string "quantity" t.string "quantity"
t.string "units" t.string "units"
t.datetime "created_at", null: false t.datetime "created_at", null: false
t.datetime "updated_at", null: false t.datetime "updated_at", null: false
t.text "preparation"
end end
add_index "recipe_ingredients", ["recipe_id"], name: "index_recipe_ingredients_on_recipe_id" add_index "recipe_ingredients", ["recipe_id"], name: "index_recipe_ingredients_on_recipe_id"