diff --git a/app/javascript/components/AppIcon.vue b/app/javascript/components/AppIcon.vue index c528fe3..3d8d972 100644 --- a/app/javascript/components/AppIcon.vue +++ b/app/javascript/components/AppIcon.vue @@ -13,6 +13,7 @@ import LockLocked from "open-iconic/svg/lock-locked"; import LockUnlocked from "open-iconic/svg/lock-unlocked"; import Person from "open-iconic/svg/person"; + import Star from "open-iconic/svg/star"; import X from "open-iconic/svg/x"; const iconMap = { @@ -23,6 +24,7 @@ 'lock-locked': LockLocked, 'lock-unlocked': LockUnlocked, person: Person, + star: Star, x: X }; diff --git a/app/javascript/components/AppRating.vue b/app/javascript/components/AppRating.vue new file mode 100644 index 0000000..2461991 --- /dev/null +++ b/app/javascript/components/AppRating.vue @@ -0,0 +1,99 @@ + + + + + \ No newline at end of file diff --git a/app/javascript/components/IngredientEdit.vue b/app/javascript/components/IngredientEdit.vue index 0ba7b56..8eeeef7 100644 --- a/app/javascript/components/IngredientEdit.vue +++ b/app/javascript/components/IngredientEdit.vue @@ -1,11 +1,68 @@ \ No newline at end of file diff --git a/app/javascript/components/RecipeEditIngredientEditor.vue b/app/javascript/components/RecipeEditIngredientEditor.vue index b7c7f61..ef93351 100644 --- a/app/javascript/components/RecipeEditIngredientEditor.vue +++ b/app/javascript/components/RecipeEditIngredientEditor.vue @@ -64,7 +64,7 @@ return []; } - const regex = /^(?:([\d\/.]+(?:\s+[\d\/]+)?)\s+)?(?:([\w-]+)(?:\s+of)?\s+)?([^,]*)(?:,\s*(.*))?$/i; + const regex = /^(?:([\d\/.]+(?:\s+[\d\/]+)?)\s+)?(?:([\w-]+)(?:\s+of)?\s+)?([^,|]+?|.+\|)(?:,\s*([^|]*?))?(?:\s*\[(\d+)\]\s*)?$/i; const magicFunc = function(str) { if (str === "-") { @@ -80,22 +80,11 @@ for (let line of lines) { if (line.length === 0) { continue; } - const barIndex = line.lastIndexOf("|"); - let afterBar = null; - - if (barIndex >= 0) { - afterBar = line.slice(barIndex + 1); - line = line.slice(0, barIndex); - } - const match = line.match(regex); if (match) { - let 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; - } + const matchedName = match[3].replace(/\|\s*$/, ""); + let item = {quantity: magicFunc(match[1]), units: magicFunc(match[2]), name: magicFunc(matchedName), preparation: magicFunc(match[4]), id: match[5] || null}; parsed.push(item); } else { parsed.push(null); @@ -111,15 +100,20 @@ }, methods: { - addIngredient() { - this.ingredients.push({ + createIngredient() { + return { + id: null, quantity: null, units: null, name: null, preparation: null, ingredient_id: null, sort_order: Math.max([0].concat(this.ingredients.map(i => i.sort_order))) + 5 - }); + }; + }, + + addIngredient() { + this.ingredients.push(this.createIngredient()); }, deleteIngredient(ingredient) { @@ -140,8 +134,9 @@ text.push( item.quantity + " " + (item.units || "-") + " " + - item.name + - (item.preparation ? (", " + item.preparation) : "") + (item.name.indexOf(",") >= 0 ? item.name + "|" : item.name) + + (item.preparation ? (", " + item.preparation) : "") + + (item.id ? (" [" + item.id + "]") : "") ); } @@ -153,6 +148,47 @@ }, saveBulkEditing() { + const parsed = this.bulkIngredientPreview.filter(i => i !== null); + const existing = [...this.ingredients]; + const newList = []; + + for (let parsedIngredient of parsed) { + let newIngredient = null; + + if (parsedIngredient.id !== null) { + let intId = parseInt(parsedIngredient.id); + let exIdx = existing.findIndex(i => i.id === intId); + if (exIdx >= 0) { + let ex = existing[exIdx]; + if (ex.name === parsedIngredient.name) { + newIngredient = ex; + existing.splice(exIdx, 1); + } + } + } + + if (newIngredient === null) { + newIngredient = this.createIngredient(); + } + + newIngredient.quantity = parsedIngredient.quantity; + newIngredient.units = parsedIngredient.units; + newIngredient.name = parsedIngredient.name; + newIngredient.preparation = parsedIngredient.preparation; + newList.push(newIngredient); + } + + for (let oldExisting of existing.filter(i => i.id !== null)) { + newList.push({id: oldExisting.id, _destroy: true}); + } + + this.ingredients.splice(0); + let sortIdx = 0; + for (let n of newList) { + n.sort_order = sortIdx++; + this.ingredients.push(n); + } + this.isBulkEditing = false; } }, diff --git a/app/javascript/components/TheRecipeList.vue b/app/javascript/components/TheRecipeList.vue index 54af7ab..8c81b18 100644 --- a/app/javascript/components/TheRecipeList.vue +++ b/app/javascript/components/TheRecipeList.vue @@ -42,10 +42,13 @@ {{tag}} - {{r.rating}} - {{r.yields}} - {{r.total_time}} ({{r.active_time}}) - {{r.created_at}} + + + -- + + {{ r.yields }} + {{ formatRecipeTime(r.total_time, r.active_time) }} + {{ formatDate(r.updated_at) }} Edit @@ -62,6 +65,7 @@ import api from "../lib/Api"; import debounce from "lodash/debounce"; import AppIcon from "./AppIcon"; + import AppRating from "./AppRating"; export default { data() { @@ -114,7 +118,34 @@ api.getRecipeList(this.search.page, this.search.per, this.search.sortColumn, this.search.sortDirection, this.search.name, this.search.tags) .then(data => this.recipeData = data) ); - }, 500, {leading: true, trailing: true}) + }, 500, {leading: true, trailing: true}), + + formatDate(dateStr) { + if (dateStr && dateStr.length) { + const date = new Date(dateStr); + return [date.getMonth() + 1, date.getDate(), date.getFullYear() % 100].join("/"); + } else { + return ""; + } + }, + + formatRecipeTime(total, active) { + let str = ""; + + if (total && total > 0) { + str += total; + } + + if (active && active > 0) { + if (str.length) { + str += " (" + active + ")"; + } else { + str += active; + } + } + + return str; + } }, created() { @@ -128,6 +159,7 @@ }, components: { + AppRating, AppIcon } } diff --git a/app/views/ingredients/show.json.jbuilder b/app/views/ingredients/show.json.jbuilder index 6532b71..906e04d 100644 --- a/app/views/ingredients/show.json.jbuilder +++ b/app/views/ingredients/show.json.jbuilder @@ -1,2 +1,32 @@ -json.extract! @ingredient, :id, :name, :ndbn, :density +json.extract! @ingredient, + :id, + :name, + :ndbn, + :notes, + :density, + :water, + :ash, + :protein, + :kcal, + :fiber, + :sugar, + :carbohydrates, + :calcium, + :iron, + :magnesium, + :phosphorus, + :potassium, + :sodium, + :zinc, + :copper, + :manganese, + :vit_c, + :vit_b6, + :vit_b12, + :vit_a, + :vit_e, + :vit_d, + :vit_k, + :cholesterol, + :lipids