Tag editor; ingredient editor
This commit is contained in:
parent
a07f037b8f
commit
c13b3a09bd
@ -2,7 +2,7 @@ class IngredientsController < ApplicationController
|
|||||||
|
|
||||||
before_action :set_ingredient, only: [:show, :edit, :update, :destroy]
|
before_action :set_ingredient, only: [:show, :edit, :update, :destroy]
|
||||||
|
|
||||||
before_action :ensure_valid_user, except: [:index]
|
before_action :ensure_valid_user, except: [:index, :show]
|
||||||
|
|
||||||
# GET /ingredients
|
# GET /ingredients
|
||||||
# GET /ingredients.json
|
# GET /ingredients.json
|
||||||
@ -39,7 +39,7 @@ class IngredientsController < ApplicationController
|
|||||||
respond_to do |format|
|
respond_to do |format|
|
||||||
if @ingredient.save
|
if @ingredient.save
|
||||||
format.html { redirect_to ingredients_path, notice: 'Ingredient was successfully created.' }
|
format.html { redirect_to ingredients_path, notice: 'Ingredient was successfully created.' }
|
||||||
format.json { render :show, status: :created, location: @ingredient }
|
format.json { render json: { success: true }, status: :created, location: @ingredient }
|
||||||
else
|
else
|
||||||
format.html { render :new }
|
format.html { render :new }
|
||||||
format.json { render json: @ingredient.errors, status: :unprocessable_entity }
|
format.json { render json: @ingredient.errors, status: :unprocessable_entity }
|
||||||
@ -58,7 +58,7 @@ class IngredientsController < ApplicationController
|
|||||||
respond_to do |format|
|
respond_to do |format|
|
||||||
if @ingredient.save
|
if @ingredient.save
|
||||||
format.html { redirect_to ingredients_path, notice: 'Ingredient was successfully updated.' }
|
format.html { redirect_to ingredients_path, notice: 'Ingredient was successfully updated.' }
|
||||||
format.json { render :show, status: :ok, location: @ingredient }
|
format.json { render json: { success: true }, status: :ok, location: @ingredient }
|
||||||
else
|
else
|
||||||
format.html { render :edit }
|
format.html { render :edit }
|
||||||
format.json { render json: @ingredient.errors, status: :unprocessable_entity }
|
format.json { render json: @ingredient.errors, status: :unprocessable_entity }
|
||||||
@ -92,9 +92,7 @@ class IngredientsController < ApplicationController
|
|||||||
@ingredient.set_usda_food(UsdaFood.find_by_ndbn(@ingredient.ndbn))
|
@ingredient.set_usda_food(UsdaFood.find_by_ndbn(@ingredient.ndbn))
|
||||||
end
|
end
|
||||||
|
|
||||||
respond_to do |format|
|
render :show
|
||||||
format.js {}
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|
||||||
def prefetch
|
def prefetch
|
||||||
@ -134,7 +132,7 @@ class IngredientsController < 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 ingredient_params
|
def ingredient_params
|
||||||
params.require(:ingredient).permit(:name, :notes, :ndbn, :density, :water, :protein, :lipids, :carbohydrates, :kcal, :fiber, :sugar, :calcium, :sodium, :vit_k, :ingredient_units_attributes => [:name, :gram_weight, :id, :_destroy])
|
params.require(:ingredient).permit(:name, :notes, :ndbn, :density, :water, :protein, :lipids, :carbohydrates, :kcal, :fiber, :sugar, :calcium, :sodium, :vit_k, :ash, :iron, :magnesium, :phosphorus, :potassium, :zinc, :copper, :manganese, :vit_c, :vit_b6, :vit_b12, :vit_a, :vit_e, :vit_d, :cholesterol, :ingredient_units_attributes => [:name, :gram_weight, :id, :_destroy])
|
||||||
end
|
end
|
||||||
|
|
||||||
def conversion_params
|
def conversion_params
|
||||||
|
@ -60,8 +60,6 @@ class RecipesController < ApplicationController
|
|||||||
if @recipe.update(recipe_params)
|
if @recipe.update(recipe_params)
|
||||||
render json: { success: true }
|
render json: { success: true }
|
||||||
else
|
else
|
||||||
puts '===='
|
|
||||||
puts @recipe.recipe_ingredients.map { |ri| ri.ingredient_id.class }.join("|")
|
|
||||||
render json: @recipe.errors, status: :unprocessable_entity
|
render json: @recipe.errors, status: :unprocessable_entity
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
94
app/javascript/components/AppDateTime.vue
Normal file
94
app/javascript/components/AppDateTime.vue
Normal file
@ -0,0 +1,94 @@
|
|||||||
|
<template>
|
||||||
|
<span :title="fullString">{{ friendlyString }}</span>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
|
||||||
|
export default {
|
||||||
|
props: {
|
||||||
|
dateTime: {
|
||||||
|
required: true,
|
||||||
|
type: [Date, String]
|
||||||
|
},
|
||||||
|
|
||||||
|
showDate: {
|
||||||
|
required: false,
|
||||||
|
type: Boolean,
|
||||||
|
default: true
|
||||||
|
},
|
||||||
|
|
||||||
|
showTime: {
|
||||||
|
required: false,
|
||||||
|
type: Boolean,
|
||||||
|
default: true
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
computed: {
|
||||||
|
dateObj() {
|
||||||
|
if (this.dateTime instanceof Date) {
|
||||||
|
return this.dateTime;
|
||||||
|
} else if (this.dateTime === null || this.dateTime.length === 0) {
|
||||||
|
return null;
|
||||||
|
} else {
|
||||||
|
return new Date(this.dateTime);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
friendlyString() {
|
||||||
|
const parts = [];
|
||||||
|
if (this.showDate) {
|
||||||
|
parts.push(this.formatDate());
|
||||||
|
}
|
||||||
|
if (this.showTime) {
|
||||||
|
parts.push(this.formatTime(true));
|
||||||
|
}
|
||||||
|
return parts.join(" ");
|
||||||
|
},
|
||||||
|
|
||||||
|
fullString() {
|
||||||
|
return this.formatDate() + " " + this.formatTime(false);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
methods: {
|
||||||
|
formatDate() {
|
||||||
|
if (this.dateObj) {
|
||||||
|
return [this.dateObj.getMonth() + 1, this.dateObj.getDate(), this.dateObj.getFullYear() % 100].join("/");
|
||||||
|
} else {
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
formatTime(use12hour) {
|
||||||
|
if (this.dateObj) {
|
||||||
|
let h = this.dateObj.getHours();
|
||||||
|
const m = this.dateObj.getMinutes().toString().padStart(2, "0");
|
||||||
|
let meridiem = "";
|
||||||
|
|
||||||
|
if (use12hour) {
|
||||||
|
meridiem = " am";
|
||||||
|
|
||||||
|
if (h === 0) {
|
||||||
|
h = 12;
|
||||||
|
} else if (h > 12) {
|
||||||
|
h = h - 12;
|
||||||
|
meridiem = " pm";
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
h = h.toString().padStart(2, "0");
|
||||||
|
}
|
||||||
|
|
||||||
|
return h + ":" + m + " " + meridiem;
|
||||||
|
|
||||||
|
} else {
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
</style>
|
@ -10,9 +10,12 @@
|
|||||||
import CaretTop from "open-iconic/svg/caret-top";
|
import CaretTop from "open-iconic/svg/caret-top";
|
||||||
import Check from "open-iconic/svg/check";
|
import Check from "open-iconic/svg/check";
|
||||||
import CircleCheck from "open-iconic/svg/circle-check.svg";
|
import CircleCheck from "open-iconic/svg/circle-check.svg";
|
||||||
|
import LinkBroken from "open-iconic/svg/link-broken";
|
||||||
|
import LinkIntact from "open-iconic/svg/link-intact";
|
||||||
import LockLocked from "open-iconic/svg/lock-locked";
|
import LockLocked from "open-iconic/svg/lock-locked";
|
||||||
import LockUnlocked from "open-iconic/svg/lock-unlocked";
|
import LockUnlocked from "open-iconic/svg/lock-unlocked";
|
||||||
import Person from "open-iconic/svg/person";
|
import Person from "open-iconic/svg/person";
|
||||||
|
import Pencil from "open-iconic/svg/pencil";
|
||||||
import Star from "open-iconic/svg/star";
|
import Star from "open-iconic/svg/star";
|
||||||
import X from "open-iconic/svg/x";
|
import X from "open-iconic/svg/x";
|
||||||
|
|
||||||
@ -21,8 +24,11 @@
|
|||||||
'caret-top': CaretTop,
|
'caret-top': CaretTop,
|
||||||
check: Check,
|
check: Check,
|
||||||
'circle-check': CircleCheck,
|
'circle-check': CircleCheck,
|
||||||
|
'link-broken': LinkBroken,
|
||||||
|
'link-intact': LinkIntact,
|
||||||
'lock-locked': LockLocked,
|
'lock-locked': LockLocked,
|
||||||
'lock-unlocked': LockUnlocked,
|
'lock-unlocked': LockUnlocked,
|
||||||
|
pencil: Pencil,
|
||||||
person: Person,
|
person: Person,
|
||||||
star: Star,
|
star: Star,
|
||||||
x: X
|
x: X
|
||||||
@ -85,24 +91,29 @@
|
|||||||
fill: currentColor;
|
fill: currentColor;
|
||||||
|
|
||||||
&.sm {
|
&.sm {
|
||||||
|
width: 0.6em;
|
||||||
|
height: 0.6em;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.md {
|
||||||
width: 1em;
|
width: 1em;
|
||||||
height: 1em;
|
height: 1em;
|
||||||
}
|
}
|
||||||
|
|
||||||
&.md {
|
&.lg {
|
||||||
width: 1.33em;
|
width: 1.33em;
|
||||||
height: 1.33em;
|
height: 1.33em;
|
||||||
}
|
}
|
||||||
|
|
||||||
&.lg {
|
&.xl {
|
||||||
width: 2em;
|
width: 2em;
|
||||||
height: 2em;
|
height: 2em;
|
||||||
}
|
}
|
||||||
|
|
||||||
&.xl {
|
/*&.xl {*/
|
||||||
width: 3em;
|
/*width: 3em;*/
|
||||||
height: 3em;
|
/*height: 3em;*/
|
||||||
}
|
/*}*/
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
100
app/javascript/components/AppPager.vue
Normal file
100
app/javascript/components/AppPager.vue
Normal file
@ -0,0 +1,100 @@
|
|||||||
|
<template>
|
||||||
|
<nav v-show="totalPages > 1" class="pagination" role="navigation" :aria-label="pagedItemName + ' page navigation'">
|
||||||
|
<a class="pagination-previous" :title="isFirstPage ? 'This is the first page' : ''" :disabled="isFirstPage" @click.prevent="changePage(currentPage - 1)">Previous</a>
|
||||||
|
<a class="pagination-next" :title="isLastPage ? 'This is the last page' : ''" :disabled="isLastPage" @click.prevent="changePage(currentPage + 1)">Next page</a>
|
||||||
|
<ul class="pagination-list">
|
||||||
|
<li v-for="page in pageItems" :key="page">
|
||||||
|
<a v-if="page > 0" class="pagination-link" :class="{'is-current': page === currentPage}" href="#" @click.prevent="changePage(page)">{{page}}</a>
|
||||||
|
<span v-else class="pagination-ellipsis">…</span>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</nav>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
|
||||||
|
export default {
|
||||||
|
props: {
|
||||||
|
pagedItemName: {
|
||||||
|
required: false,
|
||||||
|
type: String,
|
||||||
|
default: ''
|
||||||
|
},
|
||||||
|
|
||||||
|
currentPage: {
|
||||||
|
required: true,
|
||||||
|
type: Number
|
||||||
|
},
|
||||||
|
|
||||||
|
totalPages: {
|
||||||
|
required: true,
|
||||||
|
type: Number
|
||||||
|
},
|
||||||
|
|
||||||
|
pageWindow: {
|
||||||
|
required: false,
|
||||||
|
type: Number,
|
||||||
|
default: 4
|
||||||
|
},
|
||||||
|
|
||||||
|
pageOuterWindow: {
|
||||||
|
required: false,
|
||||||
|
type: Number,
|
||||||
|
default: 1
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
computed: {
|
||||||
|
pageItems() {
|
||||||
|
const items = new Set();
|
||||||
|
|
||||||
|
for (let x = 0; x < this.pageOuterWindow; x++) {
|
||||||
|
items.add(x + 1);
|
||||||
|
items.add(this.totalPages - x);
|
||||||
|
}
|
||||||
|
|
||||||
|
const start = this.currentPage - Math.ceil(this.pageWindow / 2);
|
||||||
|
const end = this.currentPage + Math.floor(this.pageWindow / 2);
|
||||||
|
|
||||||
|
for (let x = start; x <= end; x++) {
|
||||||
|
items.add(x);
|
||||||
|
}
|
||||||
|
|
||||||
|
let emptySpace = -1;
|
||||||
|
const finalList = [];
|
||||||
|
|
||||||
|
[...items.values()].filter(p => p > 0 && p <= this.totalPages).sort((a, b) => a - b).forEach((p, idx, list) => {
|
||||||
|
finalList.push(p);
|
||||||
|
if (list[idx + 1] && list[idx + 1] !== p + 1) {
|
||||||
|
finalList.push(emptySpace--);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return finalList;
|
||||||
|
},
|
||||||
|
|
||||||
|
isLastPage() {
|
||||||
|
return this.currentPage === this.totalPages;
|
||||||
|
},
|
||||||
|
|
||||||
|
isFirstPage() {
|
||||||
|
return this.currentPage === 1;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
methods: {
|
||||||
|
changePage(idx) {
|
||||||
|
this.$emit("changePage", idx);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
|
||||||
|
ul.pagination {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
</style>
|
64
app/javascript/components/AppTagEditor.vue
Normal file
64
app/javascript/components/AppTagEditor.vue
Normal file
@ -0,0 +1,64 @@
|
|||||||
|
<template>
|
||||||
|
<div class="tag-editor control">
|
||||||
|
<input type="text" class="input" v-model="tagText">
|
||||||
|
<div class="tags">
|
||||||
|
<span v-for="t in value" :key="t" class="tag">{{t}}</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
|
||||||
|
export default {
|
||||||
|
props: {
|
||||||
|
value: {
|
||||||
|
required: true,
|
||||||
|
type: Array
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
// data() {
|
||||||
|
// return {
|
||||||
|
// valueCopy: []
|
||||||
|
// };
|
||||||
|
// },
|
||||||
|
|
||||||
|
computed: {
|
||||||
|
tagText: {
|
||||||
|
get: function() {
|
||||||
|
return this.value.join(" ");
|
||||||
|
},
|
||||||
|
set: function(str) {
|
||||||
|
const newTags = [...new Set(str.toString().split(/\s+/).filter(t => t.length > 0))];
|
||||||
|
if (!this.arraysEqual(newTags, this.value)) {
|
||||||
|
this.$emit("input", newTags);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
watch: {
|
||||||
|
// value(newVal) {
|
||||||
|
// console.log(newVal);
|
||||||
|
// if (!this.arraysEqual(newVal, this.valueCopy)) {
|
||||||
|
// console.log("diff");
|
||||||
|
// this.valueCopy = newVal;
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
},
|
||||||
|
|
||||||
|
methods: {
|
||||||
|
arraysEqual(arr1, arr2) {
|
||||||
|
if(arr1.length !== arr2.length)
|
||||||
|
return false;
|
||||||
|
for(let i = arr1.length; i--;) {
|
||||||
|
if(arr1[i] !== arr2[i])
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
</script>
|
@ -9,10 +9,28 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="field">
|
|
||||||
<label class="label is-small-mobile">Nutrient Databank Number</label>
|
<label class="label is-small-mobile">Nutrient Databank Number</label>
|
||||||
|
<div class="field has-addons">
|
||||||
<div class="control">
|
<div class="control">
|
||||||
<input type="text" class="input is-small-mobile" v-model="ingredient.ndbn">
|
<button type="button" class="button" :class="{'is-primary': hasNdbn}"><app-icon :icon="hasNdbn ? 'link-intact' : 'link-broken'" size="sm"></app-icon><span>{{ingredient.ndbn}}</span></button>
|
||||||
|
</div>
|
||||||
|
<div class="control is-expanded">
|
||||||
|
<app-autocomplete
|
||||||
|
:inputClass="'is-small-mobile'"
|
||||||
|
ref="autocomplete"
|
||||||
|
v-model="ingredient.usda_food_name"
|
||||||
|
:minLength="2"
|
||||||
|
valueAttribute="name"
|
||||||
|
labelAttribute="description"
|
||||||
|
placeholder=""
|
||||||
|
@optionSelected="searchItemSelected"
|
||||||
|
:onGetOptions="updateSearchItems"
|
||||||
|
>
|
||||||
|
</app-autocomplete>
|
||||||
|
</div>
|
||||||
|
<div v-if="hasNdbn" class="control">
|
||||||
|
<button type="button" class="button is-danger" @click="removeNdbn">X</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@ -23,13 +41,6 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<fieldset>
|
|
||||||
<legent>Ingredient Units</legent>
|
|
||||||
|
|
||||||
<button class="button" type="button">Add Unit</button>
|
|
||||||
|
|
||||||
</fieldset>
|
|
||||||
|
|
||||||
<div class="field">
|
<div class="field">
|
||||||
<label class="label is-small-mobile">Notes</label>
|
<label class="label is-small-mobile">Notes</label>
|
||||||
<div class="control">
|
<div class="control">
|
||||||
@ -37,31 +48,96 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<fieldset>
|
<div class="columns">
|
||||||
<legend>Nutrition per 100 grams</legend>
|
<div class="column">
|
||||||
|
<div class="message">
|
||||||
|
<div class="message-header">
|
||||||
|
Custom Units
|
||||||
|
</div>
|
||||||
|
|
||||||
<div class="columns is-mobile is-multiline">
|
<div class="message-body">
|
||||||
<div v-for="(nutrient, name) in nutrients" :key="name" class="column is-half-mobile is-one-third-tablet">
|
<button class="button" type="button" @click="addUnit">Add Unit</button>
|
||||||
<label class="label is-small-mobile">{{nutrient.label}}</label>
|
|
||||||
<div class="field has-addons">
|
<table class="table">
|
||||||
<div class="control">
|
<tr>
|
||||||
<input type="text" class="input is-small-mobile" v-model="ingredient[name]">
|
<th>Name</th>
|
||||||
</div>
|
<th>Grams</th>
|
||||||
<div class="control">
|
<th></th>
|
||||||
<button type="button" class="unit-label button is-static is-small-mobile">{{nutrient.unit}}</button>
|
</tr>
|
||||||
</div>
|
<tr v-for="unit in visibleIngredientUnits" :key="unit.id">
|
||||||
|
<td>
|
||||||
|
<div class="control">
|
||||||
|
<input type="text" class="input is-small-mobile" v-model="unit.name">
|
||||||
|
</div>
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
<div class="control">
|
||||||
|
<input type="text" class="input is-small-mobile" v-model="unit.gram_weight">
|
||||||
|
</div>
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
<button type="button" class="button is-danger" @click="removeUnit(unit)">X</button>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</table>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
</fieldset>
|
<div class="column">
|
||||||
|
<div class="message">
|
||||||
|
<div class="message-header">
|
||||||
|
NDBN Units
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="message-body">
|
||||||
|
|
||||||
|
<table class="table">
|
||||||
|
<tr>
|
||||||
|
<th>Name</th>
|
||||||
|
<th>Grams</th>
|
||||||
|
</tr>
|
||||||
|
<tr v-for="unit in ingredient.ndbn_units">
|
||||||
|
<td>{{unit.description}}</td>
|
||||||
|
<td>{{unit.gram_weight}}</td>
|
||||||
|
</tr>
|
||||||
|
</table>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="message">
|
||||||
|
<div class="message-header">
|
||||||
|
Nutrition per 100 grams
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="message-body">
|
||||||
|
<div class="columns is-mobile is-multiline">
|
||||||
|
<div v-for="(nutrient, name) in nutrients" :key="name" class="column is-half-mobile is-one-third-tablet">
|
||||||
|
<label class="label is-small-mobile">{{nutrient.label}}</label>
|
||||||
|
<div class="field has-addons">
|
||||||
|
<div class="control is-expanded">
|
||||||
|
<input type="text" class="input is-small-mobile" :disabled="hasNdbn" v-model="ingredient[name]">
|
||||||
|
</div>
|
||||||
|
<div class="control">
|
||||||
|
<button type="button" tabindex="-1" class="unit-label button is-static is-small-mobile">{{nutrient.unit}}</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
|
|
||||||
|
import api from "../lib/Api";
|
||||||
import AppAutocomplete from "./AppAutocomplete";
|
import AppAutocomplete from "./AppAutocomplete";
|
||||||
|
import AppIcon from "./AppIcon";
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
props: {
|
props: {
|
||||||
@ -109,8 +185,67 @@
|
|||||||
};
|
};
|
||||||
},
|
},
|
||||||
|
|
||||||
|
computed: {
|
||||||
|
visibleIngredientUnits() {
|
||||||
|
return this.ingredient.ingredient_units.filter(iu => iu._destroy !== true);
|
||||||
|
},
|
||||||
|
|
||||||
|
hasNdbn() {
|
||||||
|
return this.ingredient.ndbn !== null;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
methods: {
|
||||||
|
addUnit() {
|
||||||
|
this.ingredient.ingredient_units.push({
|
||||||
|
id: null,
|
||||||
|
name: null,
|
||||||
|
gram_weight: null
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
removeUnit(unit) {
|
||||||
|
if (unit.id) {
|
||||||
|
unit._destroy = true;
|
||||||
|
} else {
|
||||||
|
const idx = this.ingredient.ingredient_units.findIndex(i => i === unit);
|
||||||
|
this.ingredient.ingredient_units.splice(idx, 1);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
removeNdbn() {
|
||||||
|
this.ingredient.ndbn = null;
|
||||||
|
this.ingredient.usda_food_name = null;
|
||||||
|
this.ingredient.ndbn_units = [];
|
||||||
|
},
|
||||||
|
|
||||||
|
updateSearchItems(text) {
|
||||||
|
return api.getUsdaFoodSearch(text)
|
||||||
|
.then(data => data.map(f => {
|
||||||
|
return {
|
||||||
|
name: f.name,
|
||||||
|
ndbn: f.ndbn,
|
||||||
|
description: ["#", f.ndbn, ", Cal:", f.kcal, ", Carbs:", f.carbohydrates, ", Fat:", f.lipid, ", Protein:", f.protein].join("")
|
||||||
|
}
|
||||||
|
}));
|
||||||
|
},
|
||||||
|
|
||||||
|
searchItemSelected(food) {
|
||||||
|
this.ingredient.ndbn = food.ndbn;
|
||||||
|
this.ingredient.usda_food_name = food.name;
|
||||||
|
this.ingredient.ndbn_units = [];
|
||||||
|
|
||||||
|
this.loadResource(
|
||||||
|
api.postIngredientSelectNdbn(this.ingredient)
|
||||||
|
.then(i => Object.assign(this.ingredient, i))
|
||||||
|
);
|
||||||
|
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
components: {
|
components: {
|
||||||
AppAutocomplete
|
AppAutocomplete,
|
||||||
|
AppIcon
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -31,9 +31,7 @@
|
|||||||
|
|
||||||
<div class="field">
|
<div class="field">
|
||||||
<label class="label is-small-mobile">Tags</label>
|
<label class="label is-small-mobile">Tags</label>
|
||||||
<div class="control">
|
<app-tag-editor v-model="recipe.tags"></app-tag-editor>
|
||||||
<input class="input is-small-mobile" type="text" placeholder="tags">
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="columns">
|
<div class="columns">
|
||||||
@ -93,6 +91,7 @@
|
|||||||
import api from "../lib/Api";
|
import api from "../lib/Api";
|
||||||
|
|
||||||
import RecipeEditIngredientEditor from "./RecipeEditIngredientEditor";
|
import RecipeEditIngredientEditor from "./RecipeEditIngredientEditor";
|
||||||
|
import AppTagEditor from "./AppTagEditor";
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
props: {
|
props: {
|
||||||
@ -143,6 +142,7 @@
|
|||||||
},
|
},
|
||||||
|
|
||||||
components: {
|
components: {
|
||||||
|
AppTagEditor,
|
||||||
RecipeEditIngredientEditor
|
RecipeEditIngredientEditor
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -37,7 +37,9 @@
|
|||||||
</div>
|
</div>
|
||||||
<div class="column is-narrow">
|
<div class="column is-narrow">
|
||||||
<span class="label is-small-mobile" v-if="showLabels"> </span>
|
<span class="label is-small-mobile" v-if="showLabels"> </span>
|
||||||
<button type="button" class="button is-danger is-small-mobile" @click="deleteIngredient(ingredient)">X</button>
|
<button type="button" class="button is-danger is-small-mobile" @click="deleteIngredient(ingredient)">
|
||||||
|
<app-icon icon="x"></app-icon>
|
||||||
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
@ -45,6 +47,7 @@
|
|||||||
<script>
|
<script>
|
||||||
|
|
||||||
import AppAutocomplete from "./AppAutocomplete";
|
import AppAutocomplete from "./AppAutocomplete";
|
||||||
|
import AppIcon from "./AppIcon";
|
||||||
import api from "../lib/Api";
|
import api from "../lib/Api";
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
@ -92,7 +95,8 @@
|
|||||||
},
|
},
|
||||||
|
|
||||||
components: {
|
components: {
|
||||||
AppAutocomplete
|
AppAutocomplete,
|
||||||
|
AppIcon
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -7,7 +7,7 @@
|
|||||||
<ingredient-show :ingredient="ingredient"></ingredient-show>
|
<ingredient-show :ingredient="ingredient"></ingredient-show>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<router-link class="button" :to="{name: 'edit_ingredient', params: { id: ingredientId }}">Edit</router-link>
|
<router-link v-if="isLoggedIn" class="button" :to="{name: 'edit_ingredient', params: { id: ingredientId }}">Edit</router-link>
|
||||||
<router-link class="button" to="/ingredients">Back</router-link>
|
<router-link class="button" to="/ingredients">Back</router-link>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
@ -20,7 +20,36 @@
|
|||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
ingredient: {
|
ingredient: {
|
||||||
name: null
|
name: null,
|
||||||
|
notes: null,
|
||||||
|
ndbn: null,
|
||||||
|
density: null,
|
||||||
|
water: null,
|
||||||
|
ash: null,
|
||||||
|
protein: null,
|
||||||
|
kcal: null,
|
||||||
|
fiber: null,
|
||||||
|
sugar: null,
|
||||||
|
carbohydrates: null,
|
||||||
|
calcium: null,
|
||||||
|
iron: null,
|
||||||
|
magnesium: null,
|
||||||
|
phosphorus: null,
|
||||||
|
potassium: null,
|
||||||
|
sodium: null,
|
||||||
|
zinc: null,
|
||||||
|
copper: null,
|
||||||
|
manganese: null,
|
||||||
|
vit_c: null,
|
||||||
|
vit_b6: null,
|
||||||
|
vit_b12: null,
|
||||||
|
vit_a: null,
|
||||||
|
vit_e: null,
|
||||||
|
vit_d: null,
|
||||||
|
vit_k: null,
|
||||||
|
cholesterol: null,
|
||||||
|
lipids: null,
|
||||||
|
ingredient_units: []
|
||||||
},
|
},
|
||||||
validationErrors: null
|
validationErrors: null
|
||||||
}
|
}
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
<template>
|
<template>
|
||||||
<div>
|
<div>
|
||||||
|
|
||||||
<div v-if="recipe === null">
|
<div v-if="ingredient === null">
|
||||||
Loading...
|
Loading...
|
||||||
</div>
|
</div>
|
||||||
<div v-else>
|
<div v-else>
|
||||||
|
@ -2,7 +2,9 @@
|
|||||||
<div>
|
<div>
|
||||||
<h1 class="title">Ingredients</h1>
|
<h1 class="title">Ingredients</h1>
|
||||||
|
|
||||||
<router-link :to="{name: 'new_ingredient'}" class="button is-primary">Create Ingredient</router-link>
|
<router-link v-if="isLoggedIn" :to="{name: 'new_ingredient'}" class="button is-primary">Create Ingredient</router-link>
|
||||||
|
|
||||||
|
<app-pager :current-page="currentPage" :total-pages="totalPages" paged-item-name="ingredient" @changePage="changePage"></app-pager>
|
||||||
|
|
||||||
<table class="table">
|
<table class="table">
|
||||||
<thead>
|
<thead>
|
||||||
@ -10,7 +12,7 @@
|
|||||||
<th>Name</th>
|
<th>Name</th>
|
||||||
<th>USDA</th>
|
<th>USDA</th>
|
||||||
<th>KCal per 100g</th>
|
<th>KCal per 100g</th>
|
||||||
<th>Density</th>
|
<th>Density (oz/cup)</th>
|
||||||
<th></th>
|
<th></th>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
@ -31,12 +33,19 @@
|
|||||||
<td>{{i.kcal}}</td>
|
<td>{{i.kcal}}</td>
|
||||||
<td>{{i.density}}</td>
|
<td>{{i.density}}</td>
|
||||||
<td>
|
<td>
|
||||||
<button type="button" class="button is-danger">X</button>
|
<router-link v-if="isLoggedIn" class="button" :to="{name: 'edit_ingredient', params: { id: i.id } }">
|
||||||
|
<app-icon icon="pencil"></app-icon>
|
||||||
|
</router-link>
|
||||||
|
<button v-if="isLoggedIn" type="button" class="button is-danger">
|
||||||
|
<app-icon icon="x"></app-icon>
|
||||||
|
</button>
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
|
|
||||||
|
<app-pager :current-page="currentPage" :total-pages="totalPages" paged-item-name="ingredient" @changePage="changePage"></app-pager>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
@ -45,6 +54,7 @@
|
|||||||
import api from "../lib/Api";
|
import api from "../lib/Api";
|
||||||
import debounce from "lodash/debounce";
|
import debounce from "lodash/debounce";
|
||||||
import AppIcon from "./AppIcon";
|
import AppIcon from "./AppIcon";
|
||||||
|
import AppPager from "./AppPager";
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
data() {
|
data() {
|
||||||
@ -65,10 +75,27 @@
|
|||||||
} else {
|
} else {
|
||||||
return [];
|
return [];
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
totalPages() {
|
||||||
|
if (this.ingredientData) {
|
||||||
|
return this.ingredientData.total_pages
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
},
|
||||||
|
|
||||||
|
currentPage() {
|
||||||
|
if (this.ingredientData) {
|
||||||
|
return this.ingredientData.current_page
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
methods: {
|
methods: {
|
||||||
|
changePage(idx) {
|
||||||
|
this.search.page = idx;
|
||||||
|
},
|
||||||
|
|
||||||
getList: debounce(function() {
|
getList: debounce(function() {
|
||||||
this.loadResource(
|
this.loadResource(
|
||||||
@ -89,7 +116,8 @@
|
|||||||
},
|
},
|
||||||
|
|
||||||
components: {
|
components: {
|
||||||
AppIcon
|
AppIcon,
|
||||||
|
AppPager
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -7,7 +7,7 @@
|
|||||||
<recipe-show :recipe="recipe"></recipe-show>
|
<recipe-show :recipe="recipe"></recipe-show>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<router-link class="button" :to="{name: 'edit_recipe', params: { id: recipeId }}">Edit</router-link>
|
<router-link v-if="isLoggedIn" class="button" :to="{name: 'edit_recipe', params: { id: recipeId }}">Edit</router-link>
|
||||||
<router-link class="button" to="/">Back</router-link>
|
<router-link class="button" to="/">Back</router-link>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
@ -2,7 +2,9 @@
|
|||||||
<div>
|
<div>
|
||||||
<h1 class="title">Recipes</h1>
|
<h1 class="title">Recipes</h1>
|
||||||
|
|
||||||
<router-link :to="{name: 'new_recipe'}" class="button is-primary">Create Recipe</router-link>
|
<router-link v-if="isLoggedIn" :to="{name: 'new_recipe'}" class="button is-primary">Create Recipe</router-link>
|
||||||
|
|
||||||
|
<app-pager :current-page="currentPage" :total-pages="totalPages" paged-item-name="recipe" @changePage="changePage"></app-pager>
|
||||||
|
|
||||||
<table class="table">
|
<table class="table">
|
||||||
<thead>
|
<thead>
|
||||||
@ -48,15 +50,21 @@
|
|||||||
</td>
|
</td>
|
||||||
<td>{{ r.yields }}</td>
|
<td>{{ r.yields }}</td>
|
||||||
<td>{{ formatRecipeTime(r.total_time, r.active_time) }}</td>
|
<td>{{ formatRecipeTime(r.total_time, r.active_time) }}</td>
|
||||||
<td>{{ formatDate(r.updated_at) }}</td>
|
<td><app-date-time :date-time="r.updated_at" :show-time="false"></app-date-time></td>
|
||||||
<td>
|
<td>
|
||||||
<router-link :to="{name: 'edit_recipe', params: { id: r.id } }" class="button">Edit</router-link>
|
<router-link v-if="isLoggedIn" :to="{name: 'edit_recipe', params: { id: r.id } }" class="button">
|
||||||
<button type="button" class="button">Delete</button>
|
<app-icon icon="pencil" size="md"></app-icon>
|
||||||
|
</router-link>
|
||||||
|
<button v-if="isLoggedIn" type="button" class="button is-danger">
|
||||||
|
<app-icon icon="x" size="md"></app-icon>
|
||||||
|
</button>
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
|
|
||||||
|
<app-pager :current-page="currentPage" :total-pages="totalPages" paged-item-name="recipe" @changePage="changePage"></app-pager>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
@ -64,7 +72,9 @@
|
|||||||
|
|
||||||
import api from "../lib/Api";
|
import api from "../lib/Api";
|
||||||
import debounce from "lodash/debounce";
|
import debounce from "lodash/debounce";
|
||||||
|
import AppDateTime from "./AppDateTime";
|
||||||
import AppIcon from "./AppIcon";
|
import AppIcon from "./AppIcon";
|
||||||
|
import AppPager from "./AppPager";
|
||||||
import AppRating from "./AppRating";
|
import AppRating from "./AppRating";
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
@ -100,10 +110,28 @@
|
|||||||
{name: 'total_time', label: 'Time', sort: true},
|
{name: 'total_time', label: 'Time', sort: true},
|
||||||
{name: 'created_at', label: 'Created', sort: true}
|
{name: 'created_at', label: 'Created', sort: true}
|
||||||
]
|
]
|
||||||
|
},
|
||||||
|
|
||||||
|
totalPages() {
|
||||||
|
if (this.recipeData) {
|
||||||
|
return this.recipeData.total_pages;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
},
|
||||||
|
|
||||||
|
currentPage() {
|
||||||
|
if (this.recipeData) {
|
||||||
|
return this.recipeData.current_page;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
methods: {
|
methods: {
|
||||||
|
changePage(idx) {
|
||||||
|
this.search.page = idx;
|
||||||
|
},
|
||||||
|
|
||||||
setSort(col) {
|
setSort(col) {
|
||||||
if (this.search.sortColumn === col) {
|
if (this.search.sortColumn === col) {
|
||||||
this.search.sortDirection = this.search.sortDirection === "desc" ? "asc" : "desc";
|
this.search.sortDirection = this.search.sortDirection === "desc" ? "asc" : "desc";
|
||||||
@ -120,15 +148,6 @@
|
|||||||
);
|
);
|
||||||
}, 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) {
|
formatRecipeTime(total, active) {
|
||||||
let str = "";
|
let str = "";
|
||||||
|
|
||||||
@ -160,7 +179,9 @@
|
|||||||
|
|
||||||
components: {
|
components: {
|
||||||
AppRating,
|
AppRating,
|
||||||
AppIcon
|
AppIcon,
|
||||||
|
AppDateTime,
|
||||||
|
AppPager
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -181,6 +181,76 @@ class Api {
|
|||||||
return this.get("/ingredients/" + id);
|
return this.get("/ingredients/" + id);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
buildIngredientParams(ingredient) {
|
||||||
|
return {
|
||||||
|
ingredient: {
|
||||||
|
name: ingredient.name,
|
||||||
|
notes: ingredient.notes,
|
||||||
|
ndbn: ingredient.ndbn,
|
||||||
|
density: ingredient.density,
|
||||||
|
|
||||||
|
water: ingredient.water,
|
||||||
|
ash: ingredient.ash,
|
||||||
|
protein: ingredient.protein,
|
||||||
|
kcal: ingredient.kcal,
|
||||||
|
fiber: ingredient.fiber,
|
||||||
|
sugar: ingredient.sugar,
|
||||||
|
carbohydrates: ingredient.carbohydrates,
|
||||||
|
calcium: ingredient.calcium,
|
||||||
|
iron: ingredient.iron,
|
||||||
|
magnesium: ingredient.magnesium,
|
||||||
|
phosphorus: ingredient.phosphorus,
|
||||||
|
potassium: ingredient.potassium,
|
||||||
|
sodium: ingredient.sodium,
|
||||||
|
zinc: ingredient.zinc,
|
||||||
|
copper: ingredient.copper,
|
||||||
|
manganese: ingredient.manganese,
|
||||||
|
vit_c: ingredient.vit_c,
|
||||||
|
vit_b6: ingredient.vit_b6,
|
||||||
|
vit_b12: ingredient.vit_b12,
|
||||||
|
vit_a: ingredient.vit_a,
|
||||||
|
vit_e: ingredient.vit_e,
|
||||||
|
vit_d: ingredient.vit_d,
|
||||||
|
vit_k: ingredient.vit_k,
|
||||||
|
cholesterol: ingredient.cholesterol,
|
||||||
|
lipids: ingredient.lipids,
|
||||||
|
|
||||||
|
|
||||||
|
ingredient_units_attributes: ingredient.ingredient_units.map(iu => {
|
||||||
|
if (iu._destroy) {
|
||||||
|
return {
|
||||||
|
id: iu.id,
|
||||||
|
_destroy: true
|
||||||
|
};
|
||||||
|
} else {
|
||||||
|
return {
|
||||||
|
id: iu.id,
|
||||||
|
name: iu.name,
|
||||||
|
gram_weight: iu.gram_weight
|
||||||
|
};
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
postIngredient(ingredient) {
|
||||||
|
return this.post("/ingredients/", this.buildIngredientParams(ingredient));
|
||||||
|
}
|
||||||
|
|
||||||
|
patchIngredient(ingredient) {
|
||||||
|
return this.patch("/ingredients/" + ingredient.id, this.buildIngredientParams(ingredient));
|
||||||
|
}
|
||||||
|
|
||||||
|
postIngredientSelectNdbn(ingredient) {
|
||||||
|
const url = ingredient.id ? "/ingredients/" + ingredient.id + "/select_ndbn" : "/ingredients/select_ndbn";
|
||||||
|
return this.post(url, this.buildIngredientParams(ingredient));
|
||||||
|
}
|
||||||
|
|
||||||
|
getUsdaFoodSearch(query) {
|
||||||
|
return this.get("/ingredients/usda_food_search", {query: query});
|
||||||
|
}
|
||||||
|
|
||||||
postLogin(username, password) {
|
postLogin(username, password) {
|
||||||
const params = {
|
const params = {
|
||||||
username: username,
|
username: username,
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
|
|
||||||
// Bluma default overrides
|
// Bluma default overrides
|
||||||
$green: #79A736;
|
$green: #79A736;
|
||||||
|
$red: #FF0000;
|
||||||
$primary: $green;
|
$primary: $green;
|
||||||
|
|
||||||
$modal-content-width: 750px;
|
$modal-content-width: 750px;
|
||||||
|
@ -6,6 +6,7 @@
|
|||||||
@import "~bulma/sass/components/level";
|
@import "~bulma/sass/components/level";
|
||||||
@import "~bulma/sass/components/message";
|
@import "~bulma/sass/components/message";
|
||||||
@import "~bulma/sass/components/modal";
|
@import "~bulma/sass/components/modal";
|
||||||
|
@import "~bulma/sass/components/pagination";
|
||||||
@import "~bulma/sass/elements/_all";
|
@import "~bulma/sass/elements/_all";
|
||||||
@import "~bulma/sass/grid/columns";
|
@import "~bulma/sass/grid/columns";
|
||||||
@import "~bulma/sass/layout/section";
|
@import "~bulma/sass/layout/section";
|
||||||
|
@ -3,6 +3,7 @@ json.extract! @ingredient,
|
|||||||
:id,
|
:id,
|
||||||
:name,
|
:name,
|
||||||
:ndbn,
|
:ndbn,
|
||||||
|
:usda_food_name,
|
||||||
:notes,
|
:notes,
|
||||||
:density,
|
:density,
|
||||||
:water,
|
:water,
|
||||||
@ -30,3 +31,16 @@ json.extract! @ingredient,
|
|||||||
:vit_k,
|
:vit_k,
|
||||||
:cholesterol,
|
:cholesterol,
|
||||||
:lipids
|
:lipids
|
||||||
|
|
||||||
|
if @ingredient.ndbn.present?
|
||||||
|
json.ndbn_units @ingredient.usda_food.usda_food_weights do |fw|
|
||||||
|
json.extract! fw, :amount, :description, :gram_weight
|
||||||
|
end
|
||||||
|
else
|
||||||
|
json.ndbn_units []
|
||||||
|
end
|
||||||
|
|
||||||
|
json.ingredient_units @ingredient.ingredient_units do |iu|
|
||||||
|
json.extract! iu, :id, :name, :gram_weight
|
||||||
|
json._destroy false
|
||||||
|
end
|
@ -1,7 +1,8 @@
|
|||||||
|
|
||||||
json.array! @foods do |f|
|
json.array! @foods do |f|
|
||||||
|
|
||||||
json.extract! f, :ndbn
|
json.extract! f, :ndbn, :kcal, :carbohydrates, :lipid, :protein
|
||||||
json.name f.long_description
|
json.name f.long_description
|
||||||
|
|
||||||
|
|
||||||
end
|
end
|
@ -1,30 +1,36 @@
|
|||||||
Arguments:
|
Arguments:
|
||||||
/home/dan/.nvm/versions/node/v8.7.0/bin/node /home/dan/.nvm/versions/node/v8.7.0/bin/yarn add svg-router
|
/Users/delbert/.nvm/versions/node/v8.9.0/bin/node /usr/local/Cellar/yarn/1.5.1_1/libexec/bin/yarn.js check --integrity
|
||||||
|
|
||||||
PATH:
|
PATH:
|
||||||
/home/dan/.rvm/gems/ruby-2.5.1/bin:/home/dan/.rvm/gems/ruby-2.5.1@global/bin:/home/dan/.rvm/rubies/ruby-2.5.1/bin:/home/dan/.rvm/bin:/home/dan/.cargo/bin:/home/dan/.nvm/versions/node/v8.7.0/bin:/home/dan/.cargo/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games:/usr/local/games
|
/Users/delbert/.rvm/gems/ruby-2.4.3/bin:/Users/delbert/.rvm/gems/ruby-2.4.3@global/bin:/Users/delbert/.rvm/rubies/ruby-2.4.3/bin:/Users/delbert/.rvm/bin:/Users/delbert/.cargo/bin:/Users/delbert/.nvm/versions/node/v8.9.0/bin:/usr/local/bin:/usr/local/sbin:/usr/bin:/bin:/usr/sbin:/sbin:/opt/X11/bin
|
||||||
|
|
||||||
Yarn version:
|
Yarn version:
|
||||||
1.5.1
|
1.5.1
|
||||||
|
|
||||||
Node version:
|
Node version:
|
||||||
8.7.0
|
8.9.0
|
||||||
|
|
||||||
Platform:
|
Platform:
|
||||||
linux x64
|
darwin x64
|
||||||
|
|
||||||
npm manifest:
|
npm manifest:
|
||||||
{
|
{
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@rails/webpacker": "^3.4.1",
|
"@rails/webpacker": "^3.4.1",
|
||||||
|
"autosize": "^4.0.1",
|
||||||
"bulma": "^0.6.2",
|
"bulma": "^0.6.2",
|
||||||
"caniuse-lite": "^1.0.30000815",
|
"caniuse-lite": "^1.0.30000815",
|
||||||
"css-loader": "^0.28.11",
|
"css-loader": "^0.28.11",
|
||||||
|
"lodash": "^4.17.5",
|
||||||
|
"open-iconic": "^1.1.1",
|
||||||
|
"svg-loader": "^0.0.2",
|
||||||
"vue": "^2.5.16",
|
"vue": "^2.5.16",
|
||||||
"vue-loader": "^14.2.2",
|
"vue-loader": "^14.2.2",
|
||||||
|
"vue-progressbar": "^0.7.4",
|
||||||
"vue-router": "^3.0.1",
|
"vue-router": "^3.0.1",
|
||||||
"vue-template-compiler": "^2.5.16",
|
"vue-template-compiler": "^2.5.16",
|
||||||
"vuex": "^3.0.1",
|
"vuex": "^3.0.1",
|
||||||
|
"vuex-router-sync": "^5.0.0",
|
||||||
"webpack": "^3.11.0"
|
"webpack": "^3.11.0"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
@ -318,6 +324,10 @@ Lockfile:
|
|||||||
postcss "^6.0.17"
|
postcss "^6.0.17"
|
||||||
postcss-value-parser "^3.2.3"
|
postcss-value-parser "^3.2.3"
|
||||||
|
|
||||||
|
autosize@^4.0.1:
|
||||||
|
version "4.0.1"
|
||||||
|
resolved "https://registry.yarnpkg.com/autosize/-/autosize-4.0.1.tgz#4e2f89d00e290dd98e1f95555a8ccb91e9c7a41a"
|
||||||
|
|
||||||
aws-sign2@~0.6.0:
|
aws-sign2@~0.6.0:
|
||||||
version "0.6.0"
|
version "0.6.0"
|
||||||
resolved "https://registry.yarnpkg.com/aws-sign2/-/aws-sign2-0.6.0.tgz#14342dd38dbcc94d0e5b87d763cd63612c0e794f"
|
resolved "https://registry.yarnpkg.com/aws-sign2/-/aws-sign2-0.6.0.tgz#14342dd38dbcc94d0e5b87d763cd63612c0e794f"
|
||||||
@ -3312,7 +3322,7 @@ Lockfile:
|
|||||||
version "4.5.0"
|
version "4.5.0"
|
||||||
resolved "https://registry.yarnpkg.com/lodash.uniq/-/lodash.uniq-4.5.0.tgz#d0225373aeb652adc1bc82e4945339a842754773"
|
resolved "https://registry.yarnpkg.com/lodash.uniq/-/lodash.uniq-4.5.0.tgz#d0225373aeb652adc1bc82e4945339a842754773"
|
||||||
|
|
||||||
"lodash@>=3.5 <5", lodash@^4.0.0, lodash@^4.14.0, lodash@^4.17.2, lodash@^4.17.4, lodash@~4.17.4:
|
"lodash@>=3.5 <5", lodash@^4.0.0, lodash@^4.14.0, lodash@^4.17.2, lodash@^4.17.4, lodash@^4.17.5, lodash@~4.17.4:
|
||||||
version "4.17.5"
|
version "4.17.5"
|
||||||
resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.5.tgz#99a92d65c0272debe8c96b6057bc8fbfa3bed511"
|
resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.5.tgz#99a92d65c0272debe8c96b6057bc8fbfa3bed511"
|
||||||
|
|
||||||
@ -3817,6 +3827,10 @@ Lockfile:
|
|||||||
version "3.0.5"
|
version "3.0.5"
|
||||||
resolved "https://registry.yarnpkg.com/onecolor/-/onecolor-3.0.5.tgz#36eff32201379efdf1180fb445e51a8e2425f9f6"
|
resolved "https://registry.yarnpkg.com/onecolor/-/onecolor-3.0.5.tgz#36eff32201379efdf1180fb445e51a8e2425f9f6"
|
||||||
|
|
||||||
|
open-iconic@^1.1.1:
|
||||||
|
version "1.1.1"
|
||||||
|
resolved "https://registry.yarnpkg.com/open-iconic/-/open-iconic-1.1.1.tgz#9dcfc8c7cd3c61cdb4a236b1a347894c97adc0c6"
|
||||||
|
|
||||||
opn@^5.1.0:
|
opn@^5.1.0:
|
||||||
version "5.3.0"
|
version "5.3.0"
|
||||||
resolved "https://registry.yarnpkg.com/opn/-/opn-5.3.0.tgz#64871565c863875f052cfdf53d3e3cb5adb53b1c"
|
resolved "https://registry.yarnpkg.com/opn/-/opn-5.3.0.tgz#64871565c863875f052cfdf53d3e3cb5adb53b1c"
|
||||||
@ -5554,6 +5568,10 @@ Lockfile:
|
|||||||
dependencies:
|
dependencies:
|
||||||
has-flag "^3.0.0"
|
has-flag "^3.0.0"
|
||||||
|
|
||||||
|
svg-loader@^0.0.2:
|
||||||
|
version "0.0.2"
|
||||||
|
resolved "https://registry.yarnpkg.com/svg-loader/-/svg-loader-0.0.2.tgz#601ab2fdaa1dadae3ca9975b550de92a07e1d92b"
|
||||||
|
|
||||||
svgo@^0.7.0:
|
svgo@^0.7.0:
|
||||||
version "0.7.2"
|
version "0.7.2"
|
||||||
resolved "https://registry.yarnpkg.com/svgo/-/svgo-0.7.2.tgz#9f5772413952135c6fefbf40afe6a4faa88b4bb5"
|
resolved "https://registry.yarnpkg.com/svgo/-/svgo-0.7.2.tgz#9f5772413952135c6fefbf40afe6a4faa88b4bb5"
|
||||||
@ -5903,6 +5921,10 @@ Lockfile:
|
|||||||
vue-style-loader "^4.0.1"
|
vue-style-loader "^4.0.1"
|
||||||
vue-template-es2015-compiler "^1.6.0"
|
vue-template-es2015-compiler "^1.6.0"
|
||||||
|
|
||||||
|
vue-progressbar@^0.7.4:
|
||||||
|
version "0.7.4"
|
||||||
|
resolved "https://registry.yarnpkg.com/vue-progressbar/-/vue-progressbar-0.7.4.tgz#435dd9cb3707e29a1b18063afed44aeff371e606"
|
||||||
|
|
||||||
vue-router@^3.0.1:
|
vue-router@^3.0.1:
|
||||||
version "3.0.1"
|
version "3.0.1"
|
||||||
resolved "https://registry.yarnpkg.com/vue-router/-/vue-router-3.0.1.tgz#d9b05ad9c7420ba0f626d6500d693e60092cc1e9"
|
resolved "https://registry.yarnpkg.com/vue-router/-/vue-router-3.0.1.tgz#d9b05ad9c7420ba0f626d6500d693e60092cc1e9"
|
||||||
@ -5929,6 +5951,10 @@ Lockfile:
|
|||||||
version "2.5.16"
|
version "2.5.16"
|
||||||
resolved "https://registry.yarnpkg.com/vue/-/vue-2.5.16.tgz#07edb75e8412aaeed871ebafa99f4672584a0085"
|
resolved "https://registry.yarnpkg.com/vue/-/vue-2.5.16.tgz#07edb75e8412aaeed871ebafa99f4672584a0085"
|
||||||
|
|
||||||
|
vuex-router-sync@^5.0.0:
|
||||||
|
version "5.0.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/vuex-router-sync/-/vuex-router-sync-5.0.0.tgz#1a225c17a1dd9e2f74af0a1b2c62072e9492b305"
|
||||||
|
|
||||||
vuex@^3.0.1:
|
vuex@^3.0.1:
|
||||||
version "3.0.1"
|
version "3.0.1"
|
||||||
resolved "https://registry.yarnpkg.com/vuex/-/vuex-3.0.1.tgz#e761352ebe0af537d4bb755a9b9dc4be3df7efd2"
|
resolved "https://registry.yarnpkg.com/vuex/-/vuex-3.0.1.tgz#e761352ebe0af537d4bb755a9b9dc4be3df7efd2"
|
||||||
@ -6188,11 +6214,10 @@ Lockfile:
|
|||||||
window-size "0.1.0"
|
window-size "0.1.0"
|
||||||
|
|
||||||
Trace:
|
Trace:
|
||||||
Error: Couldn't find package "svg-router" on the "npm" registry.
|
Error: Found 1 errors.
|
||||||
at new MessageError (/home/dan/.nvm/versions/node/v8.7.0/lib/node_modules/yarn/lib/cli.js:186:110)
|
at new MessageError (/usr/local/Cellar/yarn/1.5.1_1/libexec/lib/cli.js:186:110)
|
||||||
at NpmResolver.<anonymous> (/home/dan/.nvm/versions/node/v8.7.0/lib/node_modules/yarn/lib/cli.js:51625:15)
|
at /usr/local/Cellar/yarn/1.5.1_1/libexec/lib/cli.js:59470:13
|
||||||
at Generator.next (<anonymous>)
|
at Generator.next (<anonymous>)
|
||||||
at step (/home/dan/.nvm/versions/node/v8.7.0/lib/node_modules/yarn/lib/cli.js:98:30)
|
at step (/usr/local/Cellar/yarn/1.5.1_1/libexec/lib/cli.js:98:30)
|
||||||
at /home/dan/.nvm/versions/node/v8.7.0/lib/node_modules/yarn/lib/cli.js:109:13
|
at /usr/local/Cellar/yarn/1.5.1_1/libexec/lib/cli.js:109:13
|
||||||
at <anonymous>
|
at <anonymous>
|
||||||
at process._tickCallback (internal/process/next_tick.js:188:7)
|
|
||||||
|
Loading…
Reference in New Issue
Block a user