WIP
This commit is contained in:
parent
0559143f0e
commit
b56c2429c7
@ -47,7 +47,7 @@ class SpaceFilterSerializer(serializers.ListSerializer):
|
||||
|
||||
def to_representation(self, data):
|
||||
if (type(data) == QuerySet and data.query.is_sliced):
|
||||
# if query is sliced or if is a MP_NodeQuerySet it came from api request not nested serializer
|
||||
# if query is sliced it came from api request not nested serializer
|
||||
return super().to_representation(data)
|
||||
if self.child.Meta.model == User:
|
||||
data = data.filter(userpreference__space=self.context['request'].space)
|
||||
@ -212,8 +212,8 @@ class KeywordSerializer(UniqueFieldsMixin, serializers.ModelSerializer):
|
||||
|
||||
def get_image(self, obj):
|
||||
recipes = obj.recipe_set.all().filter(space=obj.space).exclude(image__isnull=True).exclude(image__exact='')
|
||||
if len(recipes) == 0:
|
||||
recipes = Recipe.objects.filter(keywords__in=obj.get_tree(), space=obj.space).exclude(image__isnull=True).exclude(image__exact='') # if no recipes found - check whole tree
|
||||
if len(recipes) == 0 and obj.has_children():
|
||||
recipes = Recipe.objects.filter(keywords__in=obj.get_descendants(), space=obj.space).exclude(image__isnull=True).exclude(image__exact='') # if no recipes found - check whole tree
|
||||
if len(recipes) != 0:
|
||||
return random.choice(recipes).image.url
|
||||
else:
|
||||
@ -297,10 +297,9 @@ class FoodSerializer(UniqueFieldsMixin, WritableNestedModelSerializer):
|
||||
return obj.recipe.image.url
|
||||
# if food is not also a recipe, look for recipe images that use the food
|
||||
recipes = Recipe.objects.filter(steps__ingredients__food=obj, space=obj.space).exclude(image__isnull=True).exclude(image__exact='')
|
||||
|
||||
# if no recipes found - check whole tree
|
||||
if len(recipes) == 0:
|
||||
recipes = Recipe.objects.filter(steps__ingredients__food__in=obj.get_tree(), space=obj.space).exclude(image__isnull=True).exclude(image__exact='')
|
||||
if len(recipes) == 0 and obj.has_children():
|
||||
recipes = Recipe.objects.filter(steps__ingredients__food__in=obj.get_descendants(), space=obj.space).exclude(image__isnull=True).exclude(image__exact='')
|
||||
|
||||
if len(recipes) != 0:
|
||||
return random.choice(recipes).image.url
|
||||
|
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@ -135,10 +135,6 @@ urlpatterns = [
|
||||
name='web_manifest'),
|
||||
]
|
||||
|
||||
# generic_models = (
|
||||
# Recipe, RecipeImport, Storage, RecipeBook, MealPlan, SyncLog, Sync,
|
||||
# Comment, RecipeBookEntry, Keyword, Food, ShoppingList, InviteLink
|
||||
# )
|
||||
generic_models = (
|
||||
Recipe, RecipeImport, Storage, RecipeBook, MealPlan, SyncLog, Sync,
|
||||
Comment, RecipeBookEntry, ShoppingList, InviteLink
|
||||
|
@ -80,7 +80,7 @@
|
||||
spinner="waveDots">
|
||||
</infinite-loading>
|
||||
</div>
|
||||
<!-- right side keyword cards -->
|
||||
<!-- right side food cards -->
|
||||
<div class="col col-md mh-100 overflow-auto " v-if="show_split">
|
||||
<food-card
|
||||
v-for="f in foods2"
|
||||
@ -108,51 +108,56 @@
|
||||
<!-- TODO Modals can probably be made generic and moved to component -->
|
||||
<!-- edit modal -->
|
||||
<b-modal class="modal"
|
||||
:id="'id_modal_keyword_edit'"
|
||||
@shown="prepareEmoji"
|
||||
:title="this.$t('Edit_Keyword')"
|
||||
:id="'id_modal_food_edit'"
|
||||
:title="this.$t('Edit_Food')"
|
||||
:ok-title="this.$t('Save')"
|
||||
:cancel-title="this.$t('Cancel')"
|
||||
@ok="saveKeyword">
|
||||
@ok="saveFood">
|
||||
<form>
|
||||
<label for="id_keyword_name_edit">{{ this.$t('Name') }}</label>
|
||||
<input class="form-control" type="text" id="id_keyword_name_edit" v-model="this_item.name">
|
||||
<label for="id_keyword_description_edit">{{ this.$t('Description') }}</label>
|
||||
<input class="form-control" type="text" id="id_keyword_description_edit" v-model="this_item.description">
|
||||
<label for="id_keyword_icon_edit">{{ this.$t('Icon') }}</label>
|
||||
<twemoji-textarea
|
||||
id="id_keyword_icon_edit"
|
||||
ref="_edit"
|
||||
:emojiData="emojiDataAll"
|
||||
:emojiGroups="emojiGroups"
|
||||
triggerType="hover"
|
||||
recentEmojisFeat="true"
|
||||
recentEmojisStorage="local"
|
||||
@contentChanged="setIcon"
|
||||
/>
|
||||
<label for="id_food_name_edit">{{ this.$t('Name') }}</label>
|
||||
<input class="form-control" type="text" id="id_food_name_edit" v-model="this_item.name">
|
||||
<label for="id_food_description_edit">{{ this.$t('Description') }}</label>
|
||||
<input class="form-control" type="text" id="id_food_description_edit" v-model="this_item.description">
|
||||
<label for="id_food_recipe_edit">{{ this.$t('Recipe') }}</label>
|
||||
<generic-multiselect
|
||||
@change="this_item.recipe=$event.val"
|
||||
label="name"
|
||||
:initial_selection="this_item.recipe"
|
||||
search_function="listRecipes"
|
||||
:multiple="false"
|
||||
:sticky_options="[{'id': null,'name': $t('None')}]"
|
||||
style="flex-grow: 1; flex-shrink: 1; flex-basis: 0"
|
||||
:placeholder="this.$t('Search')">
|
||||
</generic-multiselect>
|
||||
<div class="form-group form-check">
|
||||
<input type="checkbox" class="form-check-input" id="id_food_ignore_edit" v-model="this_item.ignore_shopping">
|
||||
<label class="form-check-label" for="id_food_ignore_edit">{{ this.$t('Ignore_Shopping') }}</label>
|
||||
</div>
|
||||
<label for="id_food_category_edit">{{ this.$t('Shopping_Category') }}</label>
|
||||
<input class="form-control" type="text" id="id_food_category_edit" >
|
||||
</form>
|
||||
</b-modal>
|
||||
<!-- delete modal -->
|
||||
<b-modal class="modal"
|
||||
:id="'id_modal_keyword_delete'"
|
||||
:title="this.$t('Delete_Keyword')"
|
||||
:id="'id_modal_food_delete'"
|
||||
:title="this.$t('Delete_Food')"
|
||||
:ok-title="this.$t('Delete')"
|
||||
:cancel-title="this.$t('Cancel')"
|
||||
@ok="delKeyword(this_item.id)">
|
||||
@ok="delFood(this_item.id)">
|
||||
{{this.$t("delete_confimation", {'kw': this_item.name})}} {{this_item.name}}
|
||||
</b-modal>
|
||||
<!-- move modal -->
|
||||
<b-modal class="modal"
|
||||
:id="'id_modal_keyword_move'"
|
||||
:title="this.$t('Move_Keyword')"
|
||||
:id="'id_modal_food_move'"
|
||||
:title="this.$t('Move_Food')"
|
||||
:ok-title="this.$t('Move')"
|
||||
:cancel-title="this.$t('Cancel')"
|
||||
@ok="moveKeyword(this_item.id, this_item.target.id)">
|
||||
@ok="moveFood(this_item.id, this_item.target.id)">
|
||||
{{ this.$t("move_selection", {'child': this_item.name}) }}
|
||||
<generic-multiselect
|
||||
@change="this_item.target=$event.val"
|
||||
label="name"
|
||||
search_function="listKeywords"
|
||||
search_function="listFood"
|
||||
:multiple="false"
|
||||
:sticky_options="[{'id': 0,'name': $t('Root')}]"
|
||||
:tree_api="true"
|
||||
@ -162,16 +167,16 @@
|
||||
</b-modal>
|
||||
<!-- merge modal -->
|
||||
<b-modal class="modal"
|
||||
:id="'id_modal_keyword_merge'"
|
||||
:title="this.$t('Merge_Keyword')"
|
||||
:id="'id_modal_food_merge'"
|
||||
:title="this.$t('Merge_Food')"
|
||||
:ok-title="this.$t('Merge')"
|
||||
:cancel-title="this.$t('Cancel')"
|
||||
@ok="mergeKeyword(this_item.id, this_item.target.id)">
|
||||
{{ this.$t("merge_selection", {'source': this_item.name, 'type': this.$t('keyword')}) }}
|
||||
@ok="mergeFood(this_item.id, this_item.target.id)">
|
||||
{{ this.$t("merge_selection", {'source': this_item.name, 'type': this.$t('food')}) }}
|
||||
<generic-multiselect
|
||||
@change="this_item.target=$event.val"
|
||||
label="name"
|
||||
search_function="listKeywords"
|
||||
search_function="listFoods"
|
||||
:multiple="false"
|
||||
:tree_api="true"
|
||||
style="flex-grow: 1; flex-shrink: 1; flex-basis: 0"
|
||||
@ -193,36 +198,19 @@ import {BootstrapVue} from 'bootstrap-vue'
|
||||
import 'bootstrap-vue/dist/bootstrap-vue.css'
|
||||
import _debounce from 'lodash/debounce'
|
||||
|
||||
import {ResolveUrlMixin} from "@/utils/utils";
|
||||
import {ToastMixin} from "@/utils/utils";
|
||||
|
||||
import {ApiApiFactory} from "@/utils/openapi/api.ts";
|
||||
import FoodCard from "@/components/FoodCard";
|
||||
import GenericMultiselect from "@/components/GenericMultiselect";
|
||||
import InfiniteLoading from 'vue-infinite-loading';
|
||||
|
||||
// would move with modals if made generic
|
||||
import {TwemojiTextarea} from '@kevinfaguiar/vue-twemoji-picker';
|
||||
// TODO add localization
|
||||
import EmojiAllData from '@kevinfaguiar/vue-twemoji-picker/emoji-data/en/emoji-all-groups.json';
|
||||
import EmojiGroups from '@kevinfaguiar/vue-twemoji-picker/emoji-data/emoji-groups.json';
|
||||
// end move with generic modals
|
||||
|
||||
Vue.use(BootstrapVue)
|
||||
|
||||
export default {
|
||||
name: 'FoodListView',
|
||||
mixins: [ResolveUrlMixin],
|
||||
components: {TwemojiTextarea, FoodCard, GenericMultiselect, InfiniteLoading},
|
||||
computed: {
|
||||
// move with generic modals
|
||||
emojiDataAll() {
|
||||
return EmojiAllData;
|
||||
},
|
||||
emojiGroups() {
|
||||
return EmojiGroups;
|
||||
}
|
||||
// end move with generic modals
|
||||
},
|
||||
mixins: [ToastMixin],
|
||||
components: {FoodCard, GenericMultiselect, InfiniteLoading},
|
||||
data() {
|
||||
return {
|
||||
foods: [],
|
||||
@ -241,7 +229,9 @@ export default {
|
||||
'id': -1,
|
||||
'name': '',
|
||||
'description': '',
|
||||
'icon': '',
|
||||
'recipe': null,
|
||||
'ignore_shopping': '',
|
||||
'supermarket_category': null,
|
||||
'target': {
|
||||
'id': -1,
|
||||
'name': ''
|
||||
@ -262,15 +252,6 @@ export default {
|
||||
}, 700)
|
||||
},
|
||||
methods: {
|
||||
makeToast: function (title, message, variant = null) {
|
||||
//TODO remove duplicate function in favor of central one
|
||||
this.$bvToast.toast(message, {
|
||||
title: title,
|
||||
variant: variant,
|
||||
toaster: 'b-toaster-top-center',
|
||||
solid: true
|
||||
})
|
||||
},
|
||||
resetSearch: function () {
|
||||
if (this.search_input !== '') {
|
||||
this.search_input = ''
|
||||
@ -295,26 +276,26 @@ export default {
|
||||
|
||||
if (e.action == 'delete') {
|
||||
this.this_item = source
|
||||
this.$bvModal.show('id_modal_keyword_delete')
|
||||
this.$bvModal.show('id_modal_food_delete')
|
||||
} else if (e.action == 'new') {
|
||||
this.this_item = {}
|
||||
this.$bvModal.show('id_modal_keyword_edit')
|
||||
this.$bvModal.show('id_modal_food_edit')
|
||||
} else if (e.action == 'edit') {
|
||||
this.this_item = source
|
||||
this.$bvModal.show('id_modal_keyword_edit')
|
||||
this.$bvModal.show('id_modal_food_edit')
|
||||
} else if (e.action === 'move') {
|
||||
this.this_item = source
|
||||
if (target == null) {
|
||||
this.$bvModal.show('id_modal_keyword_move')
|
||||
this.$bvModal.show('id_modal_food_move')
|
||||
} else {
|
||||
this.moveKeyword(source.id, target.id)
|
||||
this.moveFood(source.id, target.id)
|
||||
}
|
||||
} else if (e.action === 'merge') {
|
||||
this.this_item = source
|
||||
if (target == null) {
|
||||
this.$bvModal.show('id_modal_keyword_merge')
|
||||
this.$bvModal.show('id_modal_food_merge')
|
||||
} else {
|
||||
this.mergeKeyword(e.source.id, e.target.id)
|
||||
this.mergeFood(e.source.id, e.target.id)
|
||||
}
|
||||
} else if (e.action === 'get-children') {
|
||||
if (source.expanded) {
|
||||
@ -335,13 +316,14 @@ export default {
|
||||
},
|
||||
saveFood: function () {
|
||||
let apiClient = new ApiApiFactory()
|
||||
let food = {
|
||||
name: this.this_item.name,
|
||||
description: this.this_item.description,
|
||||
icon: this.this_item.icon,
|
||||
}
|
||||
console.log(this.this_item, !this.this_item.id)
|
||||
// let food = {
|
||||
// name: this.this_item.name,
|
||||
// description: this.this_item.description,
|
||||
// icon: this.this_item.icon,
|
||||
// }
|
||||
if (!this.this_item.id) { // if there is no item id assume its a new item
|
||||
apiClient.createFood(food).then(result => {
|
||||
apiClient.createFood(this.this_item).then(result => {
|
||||
// place all new foods at the top of the list - could sort instead
|
||||
this.foods = [result.data].concat(this.foods)
|
||||
// this creates a deep copy to make sure that columns stay independent
|
||||
@ -356,7 +338,7 @@ export default {
|
||||
this.this_item = {}
|
||||
})
|
||||
} else {
|
||||
apiClient.partialUpdateFood(this.this_item.id, food).then(result => {
|
||||
apiClient.partialUpdateFood(this.this_item.id, this.this_item).then(result => {
|
||||
this.refreshCard(this.this_item.id)
|
||||
this.this_item={}
|
||||
}).catch((err) => {
|
||||
@ -427,9 +409,9 @@ export default {
|
||||
|
||||
apiClient.listFoods(query, root, tree, page, pageSize).then(result => {
|
||||
if (col == 'left') {
|
||||
parent = this.findFood(this.keywords, food.id)
|
||||
parent = this.findFood(this.foods, food.id)
|
||||
} else if (col == 'right'){
|
||||
parent = this.findFood(this.keywords2, food.id)
|
||||
parent = this.findFood(this.foods2, food.id)
|
||||
}
|
||||
if (parent) {
|
||||
Vue.set(parent, 'children', result.data.results)
|
||||
|
@ -194,7 +194,7 @@ import {BootstrapVue} from 'bootstrap-vue'
|
||||
import 'bootstrap-vue/dist/bootstrap-vue.css'
|
||||
import _debounce from 'lodash/debounce'
|
||||
|
||||
import {ResolveUrlMixin} from "@/utils/utils";
|
||||
import {ToastMixin} from "@/utils/utils";
|
||||
|
||||
import {ApiApiFactory} from "@/utils/openapi/api.ts";
|
||||
import KeywordCard from "@/components/KeywordCard";
|
||||
@ -212,7 +212,7 @@ Vue.use(BootstrapVue)
|
||||
|
||||
export default {
|
||||
name: 'KeywordListView',
|
||||
mixins: [ResolveUrlMixin],
|
||||
mixins: [ToastMixin],
|
||||
components: {TwemojiTextarea, KeywordCard, GenericMultiselect, InfiniteLoading},
|
||||
computed: {
|
||||
// move with generic modals
|
||||
@ -263,15 +263,6 @@ export default {
|
||||
}, 700)
|
||||
},
|
||||
methods: {
|
||||
makeToast: function (title, message, variant = null) {
|
||||
//TODO remove duplicate function in favor of central one
|
||||
this.$bvToast.toast(message, {
|
||||
title: title,
|
||||
variant: variant,
|
||||
toaster: 'b-toaster-top-center',
|
||||
solid: true
|
||||
})
|
||||
},
|
||||
resetSearch: function () {
|
||||
if (this.search_input !== '') {
|
||||
this.search_input = ''
|
||||
|
@ -65,6 +65,11 @@ export default {
|
||||
apiClient[this.search_function](query, root, tree, page, pageSize).then(result => {
|
||||
this.objects = this.sticky_options.concat(result.data.results)
|
||||
})
|
||||
} else if (this.search_function === 'listRecipes') {
|
||||
apiClient[this.search_function](query, undefined, undefined, undefined, undefined, undefined,
|
||||
undefined, undefined, undefined, undefined, undefined, 25, undefined).then(result => {
|
||||
this.objects = this.sticky_options.concat(result.data.results)
|
||||
})
|
||||
} else {
|
||||
apiClient[this.search_function]({query: {query: query, limit: this.limit}}).then(result => {
|
||||
this.objects = this.sticky_options.concat(result.data)
|
||||
|
@ -102,5 +102,8 @@
|
||||
"merge_selection": "Replace all occurences of {source} with the selected {type}.",
|
||||
"Advanced Search Settings": "Advanced Search Settings",
|
||||
"Download": "Download",
|
||||
"Root": "Root"
|
||||
"Root": "Root",
|
||||
"Ignore_Shopping": "Ignore Shopping",
|
||||
"Shopping_Category": "Shopping Category",
|
||||
"Edit_Food": "Edit Food"
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user