first ideas of property editor
This commit is contained in:
parent
7d9fcac0c7
commit
b275c53e5a
@ -651,7 +651,7 @@ class IngredientSimpleSerializer(WritableNestedModelSerializer):
|
|||||||
uch = UnitConversionHelper(self.context['request'].space)
|
uch = UnitConversionHelper(self.context['request'].space)
|
||||||
conversions = []
|
conversions = []
|
||||||
for c in uch.get_conversions(obj):
|
for c in uch.get_conversions(obj):
|
||||||
conversions.append({'food': c.food.name, 'unit': c.unit.name , 'amount': c.amount}) # TODO do formatting in helper
|
conversions.append({'food': c.food.name, 'unit': c.unit.name, 'amount': c.amount}) # TODO do formatting in helper
|
||||||
return conversions
|
return conversions
|
||||||
else:
|
else:
|
||||||
return []
|
return []
|
||||||
@ -726,7 +726,7 @@ class StepRecipeSerializer(WritableNestedModelSerializer):
|
|||||||
class UnitConversionSerializer(WritableNestedModelSerializer):
|
class UnitConversionSerializer(WritableNestedModelSerializer):
|
||||||
base_unit = UnitSerializer()
|
base_unit = UnitSerializer()
|
||||||
converted_unit = UnitSerializer()
|
converted_unit = UnitSerializer()
|
||||||
food = FoodSerializer(allow_null=True)
|
food = FoodSerializer(allow_null=True, required=False)
|
||||||
base_amount = CustomDecimalField()
|
base_amount = CustomDecimalField()
|
||||||
converted_amount = CustomDecimalField()
|
converted_amount = CustomDecimalField()
|
||||||
|
|
||||||
@ -740,7 +740,7 @@ class UnitConversionSerializer(WritableNestedModelSerializer):
|
|||||||
fields = ('id', 'base_amount', 'base_unit', 'converted_amount', 'converted_unit', 'food')
|
fields = ('id', 'base_amount', 'base_unit', 'converted_amount', 'converted_unit', 'food')
|
||||||
|
|
||||||
|
|
||||||
class NutritionTypeSerializer(serializers.ModelSerializer):
|
class FoodPropertyTypeSerializer(serializers.ModelSerializer):
|
||||||
def create(self, validated_data):
|
def create(self, validated_data):
|
||||||
validated_data['space'] = self.context['request'].space
|
validated_data['space'] = self.context['request'].space
|
||||||
return super().create(validated_data)
|
return super().create(validated_data)
|
||||||
@ -750,6 +750,22 @@ class NutritionTypeSerializer(serializers.ModelSerializer):
|
|||||||
fields = ('id', 'name', 'icon', 'unit', 'description')
|
fields = ('id', 'name', 'icon', 'unit', 'description')
|
||||||
|
|
||||||
|
|
||||||
|
class FoodPropertySerializer(UniqueFieldsMixin, WritableNestedModelSerializer):
|
||||||
|
property_type = FoodPropertyTypeSerializer()
|
||||||
|
food = FoodSimpleSerializer()
|
||||||
|
unit = UnitSerializer()
|
||||||
|
# TODO prevent updates
|
||||||
|
|
||||||
|
def create(self, validated_data):
|
||||||
|
validated_data['space'] = self.context['request'].space
|
||||||
|
return super().create(validated_data)
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
model = FoodProperty
|
||||||
|
fields = ('id', 'food_amount', 'food_unit', 'food', 'property_amount', 'property_type')
|
||||||
|
read_only_fields = ('id',)
|
||||||
|
|
||||||
|
|
||||||
class NutritionInformationSerializer(serializers.ModelSerializer):
|
class NutritionInformationSerializer(serializers.ModelSerializer):
|
||||||
carbohydrates = CustomDecimalField()
|
carbohydrates = CustomDecimalField()
|
||||||
fats = CustomDecimalField()
|
fats = CustomDecimalField()
|
||||||
|
@ -269,6 +269,33 @@
|
|||||||
</div>
|
</div>
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="col-4">
|
||||||
|
<a href="{% url 'list_food_property_type' %}" class="p-0 p-md-1">
|
||||||
|
<div class="card p-0 no-gutters border-0">
|
||||||
|
<div class="card-body text-center p-0 no-gutters">
|
||||||
|
<i class="fas fa-database fa-2x"></i>
|
||||||
|
</div>
|
||||||
|
<div class="card-body text-break text-center p-0 no-gutters text-muted menu-dropdown-text">
|
||||||
|
{% trans 'Food Properties' %}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="row m-0 mt-2 mt-md-0">
|
||||||
|
<div class="col-4">
|
||||||
|
<a href="{% url 'list_unit_conversion' %}" class="p-0 p-md-1">
|
||||||
|
<div class="card p-0 no-gutters border-0">
|
||||||
|
<div class="card-body text-center p-0 no-gutters">
|
||||||
|
<i class="fas fa-exchange-alt fa-2x"></i>
|
||||||
|
</div>
|
||||||
|
<div class="card-body text-break text-center p-0 no-gutters text-muted menu-dropdown-text">
|
||||||
|
{% trans 'Unit Conversions' %}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</li>
|
</li>
|
||||||
|
@ -92,7 +92,7 @@ from cookbook.serializer import (AutomationSerializer, BookmarkletImportListSeri
|
|||||||
SyncLogSerializer, SyncSerializer, UnitSerializer,
|
SyncLogSerializer, SyncSerializer, UnitSerializer,
|
||||||
UserFileSerializer, UserSerializer, UserPreferenceSerializer,
|
UserFileSerializer, UserSerializer, UserPreferenceSerializer,
|
||||||
UserSpaceSerializer, ViewLogSerializer, AccessTokenSerializer, FoodSimpleSerializer,
|
UserSpaceSerializer, ViewLogSerializer, AccessTokenSerializer, FoodSimpleSerializer,
|
||||||
RecipeExportSerializer, UnitConversionSerializer, NutritionTypeSerializer)
|
RecipeExportSerializer, UnitConversionSerializer, FoodPropertyTypeSerializer)
|
||||||
from cookbook.views.import_export import get_integration
|
from cookbook.views.import_export import get_integration
|
||||||
from recipes import settings
|
from recipes import settings
|
||||||
|
|
||||||
@ -971,7 +971,7 @@ class UnitConversionViewSet(viewsets.ModelViewSet):
|
|||||||
|
|
||||||
class NutritionTypeViewSet(viewsets.ModelViewSet):
|
class NutritionTypeViewSet(viewsets.ModelViewSet):
|
||||||
queryset = FoodPropertyType.objects
|
queryset = FoodPropertyType.objects
|
||||||
serializer_class = NutritionTypeSerializer
|
serializer_class = FoodPropertyTypeSerializer
|
||||||
permission_classes = [CustomIsUser & CustomTokenHasReadWriteScope]
|
permission_classes = [CustomIsUser & CustomTokenHasReadWriteScope]
|
||||||
|
|
||||||
def get_queryset(self):
|
def get_queryset(self):
|
||||||
|
@ -1,43 +1,133 @@
|
|||||||
<template>
|
<template>
|
||||||
|
|
||||||
<div id="app">
|
<div id="app">
|
||||||
|
<div class="row" v-if="food">
|
||||||
|
<div class="col-12">
|
||||||
<div class="row" v-if="recipe !== undefined">
|
<h2>{{ food.name }} <small class="text-muted" v-if="food.plural_name">{{ food.plural_name }}</small>
|
||||||
<div class="col-6">
|
</h2>
|
||||||
<div class="card">
|
|
||||||
<table class="table table-bordered table-sm">
|
|
||||||
|
|
||||||
<tr v-for="p in recipe.food_properties" v-bind:key="`id_${p.id}`">
|
|
||||||
<td>
|
|
||||||
<button class="btn btn-danger btn-sm"
|
|
||||||
@click="selected_property = p"><i
|
|
||||||
class="fas fa-exclamation-triangle"></i>
|
|
||||||
</button>
|
|
||||||
{{ p.icon }} {{ p.name }}
|
|
||||||
</td>
|
|
||||||
<td>{{ p.total_value }} {{ p.unit }}</td>
|
|
||||||
</tr>
|
|
||||||
|
|
||||||
|
|
||||||
</table>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<b-modal id="id_modal_property_overview" title="Property Overview" v-model="show_modal" @hidden="selected_property = undefined">
|
<div class="row">
|
||||||
<template v-if="selected_property !== undefined">
|
<div class="col-12">
|
||||||
{{ selected_property.description }}
|
<b-form v-if="food">
|
||||||
<table class="table table-bordered">
|
<b-form-group :label="$t('Name')" description="">
|
||||||
<tr v-for="f in selected_property.food_values" v-bind:key="`id_${selected_property.id}_food_${f.id}`">
|
<b-form-input v-model="food.name"></b-form-input>
|
||||||
<td>{{ f.food }}</td>
|
</b-form-group>
|
||||||
<td>{{ f.value }} {{ selected_property.unit }}</td>
|
<b-form-group :label="$t('Plural')" description="">
|
||||||
</tr>
|
<b-form-input v-model="food.plural_name"></b-form-input>
|
||||||
</table>
|
</b-form-group>
|
||||||
</template>
|
|
||||||
|
<!-- Food properties -->
|
||||||
|
|
||||||
|
<h5><i class="fas fa-database"></i> {{ $t('Properties') }} <small class="text-muted">{{ food.name }}</small></h5>
|
||||||
|
<table class="table table-bordered" v-if="food_properties">
|
||||||
|
<tr v-for="fp in food_properties" v-bind:key="fp.id">
|
||||||
|
<td><input v-model="fp.property_amount" type="number"> {{ fp.property_type.unit }}</td>
|
||||||
|
<td><b> {{ fp.property_type.name }} </b></td>
|
||||||
|
<td> /</td>
|
||||||
|
<td><input v-model="fp.food_amount" type="number"> </td>
|
||||||
|
<td>
|
||||||
|
<generic-multiselect
|
||||||
|
@change="fp.food_unit = $event.val;"
|
||||||
|
:model="Models.UNIT"
|
||||||
|
:initial_selection="fp.food_unit"
|
||||||
|
label="name"
|
||||||
|
:multiple="false"
|
||||||
|
:placeholder="$t('Unit')"
|
||||||
|
></generic-multiselect>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</table>
|
||||||
|
|
||||||
|
|
||||||
</b-modal>
|
<!-- Unit conversion -->
|
||||||
|
|
||||||
|
<!-- ADVANCED FEATURES somehow hide this stuff -->
|
||||||
|
|
||||||
|
<b-form-group :label="$t('Recipe')" :description="$t('food_recipe_help')">
|
||||||
|
<generic-multiselect
|
||||||
|
@change="food.recipe = $event.val;"
|
||||||
|
:model="Models.RECIPE"
|
||||||
|
:initial_selection="food.recipe"
|
||||||
|
label="name"
|
||||||
|
:multiple="false"
|
||||||
|
:placeholder="$t('Recipe')"
|
||||||
|
></generic-multiselect>
|
||||||
|
</b-form-group>
|
||||||
|
|
||||||
|
<b-form-group :description="$t('OnHand_help')">
|
||||||
|
<b-form-checkbox v-model="food.food_onhand">{{ $t('OnHand') }}</b-form-checkbox>
|
||||||
|
</b-form-group>
|
||||||
|
|
||||||
|
<b-form-group :description="$t('ignore_shopping_help')">
|
||||||
|
<b-form-checkbox v-model="food.ignore_shopping">{{ $t('Ignore_Shopping') }}</b-form-checkbox>
|
||||||
|
</b-form-group>
|
||||||
|
|
||||||
|
<b-form-group :label="$t('Shopping_Category')" :description="$t('shopping_category_help')">
|
||||||
|
<generic-multiselect
|
||||||
|
@change="food.supermarket_category = $event.val;"
|
||||||
|
:model="Models.SHOPPING_CATEGORY"
|
||||||
|
:initial_selection="food.supermarket_category"
|
||||||
|
label="name"
|
||||||
|
:multiple="false"
|
||||||
|
:allow_create="true"
|
||||||
|
:placeholder="$t('Shopping_Category')"
|
||||||
|
></generic-multiselect>
|
||||||
|
</b-form-group>
|
||||||
|
|
||||||
|
<hr/>
|
||||||
|
<!-- todo add conditions if false disable dont hide -->
|
||||||
|
<b-form-group :label="$t('Substitutes')" :description="$t('substitute_help')">
|
||||||
|
<generic-multiselect
|
||||||
|
@change="food.substitute = $event.val;"
|
||||||
|
:model="Models.FOOD"
|
||||||
|
:initial_selection="food.substitute"
|
||||||
|
label="name"
|
||||||
|
:multiple="false"
|
||||||
|
:placeholder="$t('Substitutes')"
|
||||||
|
></generic-multiselect>
|
||||||
|
</b-form-group>
|
||||||
|
|
||||||
|
<b-form-group :description="$t('substitute_siblings_help')">
|
||||||
|
<b-form-checkbox v-model="food.substitute_siblings">{{
|
||||||
|
$t('substitute_siblings')
|
||||||
|
}}
|
||||||
|
</b-form-checkbox>
|
||||||
|
</b-form-group>
|
||||||
|
|
||||||
|
<b-form-group :label="$t('InheritFields')" :description="$t('InheritFields_help')">
|
||||||
|
<generic-multiselect
|
||||||
|
@change="food.inherit_fields = $event.val;"
|
||||||
|
:model="Models.FOOD_INHERIT_FIELDS"
|
||||||
|
:initial_selection="food.inherit_fields"
|
||||||
|
label="name"
|
||||||
|
:multiple="false"
|
||||||
|
:placeholder="$t('InheritFields')"
|
||||||
|
></generic-multiselect>
|
||||||
|
</b-form-group>
|
||||||
|
|
||||||
|
<b-form-group :label="$t('ChildInheritFields')" :description="$t('ChildInheritFields_help')">
|
||||||
|
<generic-multiselect
|
||||||
|
@change="food.child_inherit_fields = $event.val;"
|
||||||
|
:model="Models.FOOD_INHERIT_FIELDS"
|
||||||
|
:initial_selection="food.child_inherit_fields"
|
||||||
|
label="name"
|
||||||
|
:multiple="false"
|
||||||
|
:placeholder="$t('ChildInheritFields')"
|
||||||
|
></generic-multiselect>
|
||||||
|
</b-form-group>
|
||||||
|
|
||||||
|
<!-- TODO change to a button -->
|
||||||
|
<b-form-group :description="$t('reset_children_help')">
|
||||||
|
<b-form-checkbox v-model="food.reset_inherit">{{ $t('reset_children') }}</b-form-checkbox>
|
||||||
|
</b-form-group>
|
||||||
|
|
||||||
|
<b-button variant="primary" @click="updateFood">{{ $t('Save') }}</b-button>
|
||||||
|
</b-form>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
@ -49,8 +139,9 @@ import {BootstrapVue} from "bootstrap-vue"
|
|||||||
|
|
||||||
import "bootstrap-vue/dist/bootstrap-vue.css"
|
import "bootstrap-vue/dist/bootstrap-vue.css"
|
||||||
import {ApiApiFactory} from "@/utils/openapi/api";
|
import {ApiApiFactory} from "@/utils/openapi/api";
|
||||||
|
import RecipeCard from "@/components/RecipeCard.vue";
|
||||||
import {ApiMixin} from "@/utils/utils";
|
import GenericMultiselect from "@/components/GenericMultiselect.vue";
|
||||||
|
import {ApiMixin, StandardToasts} from "@/utils/utils";
|
||||||
|
|
||||||
|
|
||||||
Vue.use(BootstrapVue)
|
Vue.use(BootstrapVue)
|
||||||
@ -59,31 +150,48 @@ Vue.use(BootstrapVue)
|
|||||||
export default {
|
export default {
|
||||||
name: "TestView",
|
name: "TestView",
|
||||||
mixins: [ApiMixin],
|
mixins: [ApiMixin],
|
||||||
components: {},
|
components: {
|
||||||
|
GenericMultiselect
|
||||||
|
},
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
recipe: undefined,
|
food: undefined,
|
||||||
selected_property: undefined,
|
food_properties: [],
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
computed: {
|
|
||||||
show_modal: function () {
|
|
||||||
return this.selected_property !== undefined
|
|
||||||
},
|
|
||||||
},
|
|
||||||
mounted() {
|
mounted() {
|
||||||
this.$i18n.locale = window.CUSTOM_LOCALE
|
this.$i18n.locale = window.CUSTOM_LOCALE
|
||||||
let apiClient = new ApiApiFactory()
|
let apiClient = new ApiApiFactory()
|
||||||
apiClient.retrieveRecipe('1').then((r) => {
|
apiClient.retrieveFood('1').then((r) => {
|
||||||
this.recipe = r.data
|
this.food = r.data
|
||||||
})
|
})
|
||||||
|
|
||||||
|
apiClient.listFoodPropertyTypes().then((r) => {
|
||||||
|
r.data.forEach((fp) => {
|
||||||
|
this.food_properties.push({
|
||||||
|
'food_amount': 0,
|
||||||
|
'food_unit': null,
|
||||||
|
'food': this.food,
|
||||||
|
'property_amount': 0,
|
||||||
|
'property_type': fp,
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
updateFood: function () {
|
||||||
|
let apiClient = new ApiApiFactory()
|
||||||
|
apiClient.updateFood(this.food.id, this.food).then((r) => {
|
||||||
|
this.food = r.data
|
||||||
|
StandardToasts.makeStandardToast(this, StandardToasts.SUCCESS_UPDATE)
|
||||||
|
}).catch(err => {
|
||||||
|
StandardToasts.makeStandardToast(this, StandardToasts.FAIL_UPDATE, err)
|
||||||
|
})
|
||||||
|
}
|
||||||
},
|
},
|
||||||
methods: {},
|
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style>
|
<style>
|
||||||
|
|
||||||
|
|
||||||
</style>
|
</style>
|
||||||
|
@ -2,8 +2,8 @@
|
|||||||
<div>
|
<div>
|
||||||
|
|
||||||
|
|
||||||
<div class="card" v-if="recipe !== undefined">
|
<div class="card p-2" v-if="recipe !== undefined" >
|
||||||
<h5>Properties</h5>
|
<h5><i class="fas fa-database"></i> {{$t('Properties')}}</h5>
|
||||||
<table class="table table-bordered table-sm">
|
<table class="table table-bordered table-sm">
|
||||||
|
|
||||||
<tr v-for="p in recipe.food_properties" v-bind:key="`id_${p.id}`">
|
<tr v-for="p in recipe.food_properties" v-bind:key="`id_${p.id}`">
|
||||||
|
@ -168,6 +168,7 @@
|
|||||||
"create_title": "New {type}",
|
"create_title": "New {type}",
|
||||||
"edit_title": "Edit {type}",
|
"edit_title": "Edit {type}",
|
||||||
"Name": "Name",
|
"Name": "Name",
|
||||||
|
"Properties": "Properties",
|
||||||
"Type": "Type",
|
"Type": "Type",
|
||||||
"Description": "Description",
|
"Description": "Description",
|
||||||
"Recipe": "Recipe",
|
"Recipe": "Recipe",
|
||||||
|
@ -595,7 +595,7 @@ export class Models {
|
|||||||
form_field: true,
|
form_field: true,
|
||||||
type: "text",
|
type: "text",
|
||||||
field: "converted_amount",
|
field: "converted_amount",
|
||||||
label: "base_amount",
|
label: "converted_amount",
|
||||||
placeholder: "",
|
placeholder: "",
|
||||||
},
|
},
|
||||||
converted_unit: {
|
converted_unit: {
|
||||||
|
Loading…
Reference in New Issue
Block a user