242 lines
6.3 KiB
Vue
242 lines
6.3 KiB
Vue
<template>
|
|
<div>
|
|
<h1 class="title">{{action}} {{food.name || "[Unnamed Food]"}}</h1>
|
|
|
|
<app-validation-errors :errors="validationErrors"></app-validation-errors>
|
|
|
|
<div class="field">
|
|
<label class="label is-small-mobile">Name</label>
|
|
<div class="control">
|
|
<input type="text" class="input is-small-mobile" v-model="food.name">
|
|
</div>
|
|
</div>
|
|
|
|
|
|
<label class="label is-small-mobile">Nutrient Databank Number</label>
|
|
<div class="field has-addons">
|
|
<div class="control">
|
|
<button type="button" class="button" :class="{'is-primary': hasNdbn}"><app-icon :icon="hasNdbn ? 'link-intact' : 'link-broken'" size="sm"></app-icon><span>{{food.ndbn}}</span></button>
|
|
</div>
|
|
<div class="control is-expanded">
|
|
<app-autocomplete
|
|
:inputClass="'is-small-mobile'"
|
|
ref="autocomplete"
|
|
v-model="food.usda_food_name"
|
|
:minLength="2"
|
|
valueAttribute="name"
|
|
labelAttribute="description"
|
|
key-attribute="ndbn"
|
|
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 class="field">
|
|
<label class="label is-small-mobile">Density</label>
|
|
<div class="control">
|
|
<input type="text" class="input is-small-mobile" v-model="food.density">
|
|
</div>
|
|
</div>
|
|
|
|
<div class="field">
|
|
<label class="label is-small-mobile">Notes</label>
|
|
<div class="control">
|
|
<textarea type="text" class="textarea is-small-mobile" v-model="food.notes"></textarea>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="columns">
|
|
<div class="column">
|
|
<div class="message">
|
|
<div class="message-header">
|
|
Custom Units
|
|
</div>
|
|
|
|
<div class="message-body">
|
|
<button class="button" type="button" @click="addUnit">Add Unit</button>
|
|
|
|
<table class="table">
|
|
<tr>
|
|
<th>Name</th>
|
|
<th>Grams</th>
|
|
<th></th>
|
|
</tr>
|
|
<tr v-for="unit in visibleFoodUnits" :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 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 food.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="food[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>
|
|
</template>
|
|
|
|
<script>
|
|
|
|
import api from "../lib/Api";
|
|
import { mapState } from "vuex";
|
|
|
|
export default {
|
|
props: {
|
|
food: {
|
|
required: true,
|
|
type: Object
|
|
},
|
|
validationErrors: {
|
|
required: false,
|
|
type: Object,
|
|
default: {}
|
|
},
|
|
action: {
|
|
required: false,
|
|
type: String,
|
|
default: "Editing"
|
|
}
|
|
},
|
|
|
|
data() {
|
|
return {
|
|
|
|
};
|
|
},
|
|
|
|
computed: {
|
|
...mapState({
|
|
nutrients: 'nutrientList'
|
|
}),
|
|
|
|
visibleFoodUnits() {
|
|
return this.food.food_units.filter(iu => iu._destroy !== true);
|
|
},
|
|
|
|
hasNdbn() {
|
|
return this.food.ndbn !== null;
|
|
}
|
|
},
|
|
|
|
methods: {
|
|
addUnit() {
|
|
this.food.food_units.push({
|
|
id: null,
|
|
name: null,
|
|
gram_weight: null
|
|
});
|
|
},
|
|
|
|
removeUnit(unit) {
|
|
if (unit.id) {
|
|
unit._destroy = true;
|
|
} else {
|
|
const idx = this.food.food_units.findIndex(i => i === unit);
|
|
this.food.food_units.splice(idx, 1);
|
|
}
|
|
},
|
|
|
|
removeNdbn() {
|
|
this.food.ndbn = null;
|
|
this.food.usda_food_name = null;
|
|
this.food.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.food.ndbn = food.ndbn;
|
|
this.food.usda_food_name = food.name;
|
|
this.food.ndbn_units = [];
|
|
|
|
this.loadResource(
|
|
api.postIngredientSelectNdbn(this.food)
|
|
.then(i => Object.assign(this.food, i))
|
|
);
|
|
|
|
},
|
|
},
|
|
|
|
components: {
|
|
}
|
|
}
|
|
|
|
</script>
|
|
|
|
<style lang="scss" scoped>
|
|
|
|
.unit-label {
|
|
width: 3em;
|
|
}
|
|
|
|
</style> |