added unit conversion editor to food editor

This commit is contained in:
vabene1111 2023-05-29 17:16:22 +02:00
parent c0577abb89
commit 326549568f
3 changed files with 278 additions and 4160 deletions

View File

@ -816,30 +816,30 @@ class RecipeViewSet(viewsets.ModelViewSet):
if self.detail: # if detail request and not list, private condition is verified by permission class if self.detail: # if detail request and not list, private condition is verified by permission class
if not share: # filter for space only if not shared if not share: # filter for space only if not shared
self.queryset = self.queryset.filter(space=self.request.space).prefetch_related( self.queryset = self.queryset.filter(space=self.request.space).prefetch_related(
'keywords', 'keywords',
'shared', 'shared',
'properties', 'properties',
'properties__property_type', 'properties__property_type',
'steps', 'steps',
'steps__ingredients', 'steps__ingredients',
'steps__ingredients__step_set', 'steps__ingredients__step_set',
'steps__ingredients__step_set__recipe_set', 'steps__ingredients__step_set__recipe_set',
'steps__ingredients__food', 'steps__ingredients__food',
'steps__ingredients__food__properties', 'steps__ingredients__food__properties',
'steps__ingredients__food__properties__property_type', 'steps__ingredients__food__properties__property_type',
'steps__ingredients__food__inherit_fields', 'steps__ingredients__food__inherit_fields',
'steps__ingredients__food__supermarket_category', 'steps__ingredients__food__supermarket_category',
'steps__ingredients__food__onhand_users', 'steps__ingredients__food__onhand_users',
'steps__ingredients__food__substitute', 'steps__ingredients__food__substitute',
'steps__ingredients__food__child_inherit_fields', 'steps__ingredients__food__child_inherit_fields',
'steps__ingredients__unit', 'steps__ingredients__unit',
'steps__ingredients__unit__unit_conversion_base_relation', 'steps__ingredients__unit__unit_conversion_base_relation',
'steps__ingredients__unit__unit_conversion_base_relation__base_unit', 'steps__ingredients__unit__unit_conversion_base_relation__base_unit',
'steps__ingredients__unit__unit_conversion_converted_relation', 'steps__ingredients__unit__unit_conversion_converted_relation',
'steps__ingredients__unit__unit_conversion_converted_relation__converted_unit', 'steps__ingredients__unit__unit_conversion_converted_relation__converted_unit',
'cooklog_set', 'cooklog_set',
).select_related('nutrition') ).select_related('nutrition')
return super().get_queryset() return super().get_queryset()
@ -973,8 +973,16 @@ class UnitConversionViewSet(viewsets.ModelViewSet):
queryset = UnitConversion.objects queryset = UnitConversion.objects
serializer_class = UnitConversionSerializer serializer_class = UnitConversionSerializer
permission_classes = [CustomIsUser & CustomTokenHasReadWriteScope] permission_classes = [CustomIsUser & CustomTokenHasReadWriteScope]
query_params = [
QueryParam(name='food_id', description='ID of food to filter for', qtype='int'),
]
schema = QueryParamAutoSchema()
def get_queryset(self): def get_queryset(self):
food_id = self.request.query_params.get('food_id', None)
if food_id is not None:
self.queryset = self.queryset.filter(food_id=food_id)
return self.queryset.filter(space=self.request.space) return self.queryset.filter(space=self.request.space)

View File

@ -2,7 +2,7 @@
<div> <div>
<b-modal :id="id" size="xl" @hidden="cancelAction"> <b-modal :id="id" size="xl" @hidden="cancelAction" :body-class="`pr-3 pl-3`">
<template v-slot:modal-title> <template v-slot:modal-title>
<div class="row" v-if="food"> <div class="row" v-if="food">
@ -15,95 +15,147 @@
</div> </div>
</template> </template>
<div class="row"> <div>
<div class="col-12"> <b-tabs content-class="mt-3" v-if="food">
<b-form v-if="food"> <b-tab title="General" active>
<b-form-group :label="$t('Name')" description=""> <b-form>
<b-form-input v-model="food.name"></b-form-input> <b-form-group :label="$t('Name')" description="">
</b-form-group> <b-form-input v-model="food.name"></b-form-input>
<b-form-group :label="$t('Plural')" description=""> </b-form-group>
<b-form-input v-model="food.plural_name"></b-form-input> <b-form-group :label="$t('Plural')" description="">
</b-form-group> <b-form-input v-model="food.plural_name"></b-form-input>
</b-form-group>
<!-- Food properties --> <!-- Food properties -->
<h5><i class="fas fa-database"></i> {{ $t('Properties') }}</h5> <h5><i class="fas fa-database"></i> {{ $t('Properties') }}</h5>
<b-form-group :label="$t('Properties Food Amount')" description=""> <!-- TODO localize --> <b-form-group :label="$t('Properties Food Amount')" description=""> <!-- TODO localize -->
<b-form-input v-model="food.properties_food_amount"></b-form-input> <b-form-input v-model="food.properties_food_amount"></b-form-input>
</b-form-group> </b-form-group>
<b-form-group :label="$t('Properties Food Unit')" description=""> <!-- TODO localize --> <b-form-group :label="$t('Properties Food Unit')" description=""> <!-- TODO localize -->
<generic-multiselect <generic-multiselect
@change="food.properties_food_unit = $event.val;" @change="food.properties_food_unit = $event.val;"
:model="Models.UNIT" :model="Models.UNIT"
:initial_single_selection="food.properties_food_unit" :initial_single_selection="food.properties_food_unit"
label="name" label="name"
:multiple="false" :multiple="false"
:placeholder="$t('Unit')" :placeholder="$t('Unit')"
></generic-multiselect> ></generic-multiselect>
</b-form-group> </b-form-group>
<table class="table table-bordered"> <table class="table table-bordered">
<thead> <thead>
<tr> <tr>
<th> {{ $t('Property Amount') }}</th> <!-- TODO localize --> <th> {{ $t('Property Amount') }}</th> <!-- TODO localize -->
<th> {{ $t('Property Type') }}</th> <!-- TODO localize --> <th> {{ $t('Property Type') }}</th> <!-- TODO localize -->
<th></th> <th></th>
<th></th> <th></th>
</tr> </tr>
</thead> </thead>
<tr v-for="fp in food.properties" v-bind:key="fp.id"> <tr v-for="fp in food.properties" v-bind:key="fp.id">
<td><input v-model="fp.property_amount" type="number"> <span <td><input v-model="fp.property_amount" type="number"> <span
v-if="fp.property_type">{{ fp.property_type.unit }}</span></td> v-if="fp.property_type">{{ fp.property_type.unit }}</span></td>
<td> <td>
<generic-multiselect <generic-multiselect
@change="fp.property_type = $event.val" @change="fp.property_type = $event.val"
:initial_single_selection="fp.property_type" :initial_single_selection="fp.property_type"
label="name" :model="Models.PROPERTY_TYPE" label="name" :model="Models.PROPERTY_TYPE"
:multiple="false"/> :multiple="false"/>
</td> </td>
<td> / <span>{{ food.properties_food_amount }} <span <td> / <span>{{ food.properties_food_amount }} <span
v-if="food.properties_food_unit !== null">{{ v-if="food.properties_food_unit !== null">{{
food.properties_food_unit.name food.properties_food_unit.name
}}</span></span> }}</span></span>
</td> </td>
<td> <td>
<button class="btn btn-danger btn-small" @click="deleteProperty(fp)"><i <button class="btn btn-danger btn-small" @click="deleteProperty(fp)"><i
class="fas fa-trash-alt"></i></button> class="fas fa-trash-alt"></i></button>
</td> </td>
</tr> </tr>
</table> </table>
<div class="text-center"> <div class="text-center">
<b-button-group> <b-button-group>
<b-btn class="btn btn-success shadow-none" @click="addProperty()"><i <b-btn class="btn btn-success shadow-none" @click="addProperty()"><i
class="fa fa-plus"></i> class="fa fa-plus"></i>
</b-btn> </b-btn>
<b-btn class="btn btn-secondary shadow-none" @click="addAllProperties()"><i <b-btn class="btn btn-secondary shadow-none" @click="addAllProperties()"><i
class="fa fa-plus"> <i class="ml-1 fas fa-list"></i></i> class="fa fa-plus"> <i class="ml-1 fas fa-list"></i></i>
</b-btn> </b-btn>
</b-button-group> </b-button-group>
</div> </div>
<b-form-group :label="$t('Shopping_Category')" :description="$t('shopping_category_help')"> <b-form-group :label="$t('Shopping_Category')" :description="$t('shopping_category_help')">
<generic-multiselect <generic-multiselect
@change="food.supermarket_category = $event.val;" @change="food.supermarket_category = $event.val;"
:model="Models.SHOPPING_CATEGORY" :model="Models.SHOPPING_CATEGORY"
:initial_single_selection="food.supermarket_category" :initial_single_selection="food.supermarket_category"
label="name" label="name"
:multiple="false" :multiple="false"
:allow_create="true" :allow_create="true"
:placeholder="$t('Shopping_Category')" :placeholder="$t('Shopping_Category')"
></generic-multiselect> ></generic-multiselect>
</b-form-group> </b-form-group>
</b-form>
</b-tab>
<b-tab title="Conversions" @click="loadUnitConversions" v-if="this.food.id !== undefined">
<b-row v-for="uc in unit_conversions" :key="uc">
<b-col>
<span v-if="uc.id">
<b-btn class="btn btn-sm" variant="danger" @click="deleteUnitConversion(uc)"><i class="fas fa-trash-alt"></i></b-btn>
{{uc.base_amount}}
{{uc.base_unit.name}}
=
{{uc.converted_amount}}
{{uc.converted_unit.name}}
</span>
<b-form class="mt-1">
<b-input-group>
<b-input v-model="uc.base_amount" @change="uc.changed = true"></b-input>
<b-input-group-append>
<generic-multiselect
@change="uc.base_unit = $event.val; uc.changed = true"
:initial_single_selection="uc.base_unit"
label="name" :model="Models.UNIT"
:multiple="false"/>
</b-input-group-append>
</b-input-group>
<b-input-group>
<b-input v-model="uc.converted_amount" @change="uc.changed = true"></b-input>
<b-input-group-append>
<generic-multiselect
@change="uc.converted_unit = $event.val; uc.changed = true"
:initial_single_selection="uc.converted_unit"
label="name" :model="Models.UNIT"
:multiple="false"/>
</b-input-group-append>
</b-input-group>
</b-form>
</b-col>
<hr style="height: 1px"/>
</b-row>
<b-row>
<b-col class="text-center">
<b-btn variant="success" @click="addUnitConversion"><i class="fa fa-plus"></i></b-btn>
</b-col>
</b-row>
</b-tab>
<b-tab title="More">
<b-form>
<!-- Unit conversion -->
<!-- ADVANCED FEATURES somehow hide this stuff -->
<b-collapse id="collapse-advanced">
<b-form-group :label="$t('Recipe')" :description="$t('food_recipe_help')"> <b-form-group :label="$t('Recipe')" :description="$t('food_recipe_help')">
<generic-multiselect <generic-multiselect
@change="food.recipe = $event.val;" @change="food.recipe = $event.val;"
@ -176,16 +228,15 @@
}} }}
</b-form-checkbox> </b-form-checkbox>
</b-form-group> </b-form-group>
</b-collapse>
</b-form> </b-form>
</b-tab>
</div> </b-tabs>
</div> </div>
<template v-slot:modal-footer> <template v-slot:modal-footer>
<b-button variant="primary" @click="updateFood">{{ $t('Save') }}</b-button> <b-button variant="primary" @click="updateFood">{{ $t('Save') }}</b-button>
<b-button v-b-toggle.collapse-advanced class="m-1">{{ $t('Advanced') }}</b-button>
</template> </template>
</b-modal> </b-modal>
</div> </div>
@ -231,6 +282,7 @@ export default {
data() { data() {
return { return {
food: undefined, food: undefined,
unit_conversions: []
} }
}, },
mounted() { mounted() {
@ -286,6 +338,24 @@ export default {
StandardToasts.makeStandardToast(this, StandardToasts.FAIL_UPDATE, err) StandardToasts.makeStandardToast(this, StandardToasts.FAIL_UPDATE, err)
}) })
} }
this.unit_conversions.forEach(uc => {
if (uc.changed === true) {
if (uc.id === undefined) {
apiClient.createUnitConversion(uc).then(r => {
uc = r.data
}).catch(err => {
StandardToasts.makeStandardToast(this, StandardToasts.FAIL_CREATE, err, true)
})
} else {
apiClient.updateUnitConversion(uc.id, uc).then(r => {
uc = r.data
}).catch(err => {
StandardToasts.makeStandardToast(this, StandardToasts.FAIL_UPDATE, err, true)
})
}
}
})
}, },
addProperty: function () { addProperty: function () {
this.food.properties.push({property_type: null, property_amount: 0}) this.food.properties.push({property_type: null, property_amount: 0})
@ -306,6 +376,32 @@ export default {
cancelAction: function () { cancelAction: function () {
this.$emit("hidden", "") this.$emit("hidden", "")
}, },
loadUnitConversions: function () {
let apiClient = new ApiApiFactory()
apiClient.listUnitConversions(this.food.id).then(r => {
this.unit_conversions = r.data
})
},
addUnitConversion: function () {
this.unit_conversions.push(
{
food: this.food,
base_amount: 1,
base_unit: null,
converted_amount: 0,
converted_unit: null,
}
)
},
deleteUnitConversion: function (uc){
this.unit_conversions = this.unit_conversions.filter(u => u !== uc)
let apiClient = new ApiApiFactory()
apiClient.destroyUnitConversion(uc.id).then(r => {
StandardToasts.makeStandardToast(this, StandardToasts.SUCCESS_DELETE)
}).catch(err => {
StandardToasts.makeStandardToast(this, StandardToasts.FAIL_DELETE, err)
})
}
}, },
} }
</script> </script>

File diff suppressed because it is too large Load Diff