lots of WIP stuff

This commit is contained in:
vabene1111
2025-12-01 18:39:40 +01:00
parent 9307e61c1a
commit 5608f80246
43 changed files with 1751 additions and 1616 deletions

View File

@@ -945,8 +945,8 @@ class Ingredient(ExportModelOperationsMixin('ingredient'), models.Model, Permiss
space = models.ForeignKey(Space, on_delete=models.CASCADE) space = models.ForeignKey(Space, on_delete=models.CASCADE)
objects = ScopedManager(space='space') objects = ScopedManager(space='space')
def __str__(self): # def __str__(self):
return f'{self.pk}: {self.amount} ' + (self.food.name if self.food else ' ') + (self.unit.name if self.unit else '') # return f'{self.pk}: {self.amount} ' + (self.food.name if self.food else ' ') + (self.unit.name if self.unit else '')
class Meta: class Meta:
ordering = ['order', 'pk'] ordering = ['order', 'pk']
@@ -1299,8 +1299,8 @@ class ShoppingListRecipe(ExportModelOperationsMixin('shopping_list_recipe'), mod
objects = ScopedManager(space='space') objects = ScopedManager(space='space')
def __str__(self): # def __str__(self):
return f'Shopping list recipe {self.id} - {self.recipe}' # return f'Shopping list recipe {self.id} - {self.recipe}'
class Meta: class Meta:
ordering = ('pk',) ordering = ('pk',)
@@ -1317,6 +1317,9 @@ class ShoppingList(ExportModelOperationsMixin('shopping_list'), models.Model, Pe
space = models.ForeignKey(Space, on_delete=models.CASCADE) space = models.ForeignKey(Space, on_delete=models.CASCADE)
objects = ScopedManager(space='space') objects = ScopedManager(space='space')
class Meta:
ordering = ('pk',)
class ShoppingListEntry(ExportModelOperationsMixin('shopping_list_entry'), models.Model, PermissionModelMixin): class ShoppingListEntry(ExportModelOperationsMixin('shopping_list_entry'), models.Model, PermissionModelMixin):
shopping_lists = models.ManyToManyField(ShoppingList, blank=True) shopping_lists = models.ManyToManyField(ShoppingList, blank=True)

View File

@@ -186,11 +186,29 @@ class SpaceFilterSerializer(serializers.ListSerializer):
if isinstance(self.context['request'].user, AnonymousUser): if isinstance(self.context['request'].user, AnonymousUser):
data = [] data = []
else: else:
data = data.filter(userspace__space=self.context['request'].user.get_active_space()).all() iterable = data.all() if hasattr(data, 'all') else data
if isinstance(iterable, list) or (isinstance(iterable, QuerySet) and getattr(iterable, '_result_cache', None) is not None):
data = [d for d in iterable if d.userspace.space.id == self.context['request'].space.id]
else:
if hasattr(self.context['request'], 'space'):
data = data.filter(userspace__space=self.context['request'].space).all()
else:
# not sure why but this branch can be hit (just normal page load, need to see why)
data = data.filter(userspace__space=self.context['request'].user.get_active_space()).all()
elif isinstance(data, list): elif isinstance(data, list):
data = [d for d in data if getattr(d, self.child.Meta.model.get_space_key()[0]) == self.context['request'].space] data = [d for d in data if getattr(d, self.child.Meta.model.get_space_key()[0]) == self.context['request'].space]
else: else:
data = data.filter(**{'__'.join(self.child.Meta.model.get_space_key()): self.context['request'].space}) iterable = data.all() if hasattr(data, 'all') else data
if isinstance(iterable, list) or (isinstance(iterable, QuerySet) and getattr(iterable, '_result_cache', None) is not None):
keys = self.child.Meta.model.get_space_key()
if keys == ('space',):
data = [d for d in iterable if getattr(d, 'space_id') == self.context['request'].space.id]
else:
# use cached results here too, just dont have time to test this now, probably obj.get_space()
data = data.filter(**{'__'.join(self.child.Meta.model.get_space_key()): self.context['request'].space})
else:
data = data.filter(**{'__'.join(self.child.Meta.model.get_space_key()): self.context['request'].space})
return super().to_representation(data) return super().to_representation(data)
@@ -648,7 +666,7 @@ class KeywordLabelSerializer(serializers.ModelSerializer):
@extend_schema_field(str) @extend_schema_field(str)
def get_label(self, obj): def get_label(self, obj):
return str(obj) return obj.name
class Meta: class Meta:
list_serializer_class = SpaceFilterSerializer list_serializer_class = SpaceFilterSerializer
@@ -665,7 +683,7 @@ class KeywordSerializer(UniqueFieldsMixin, ExtendedRecipeMixin):
@extend_schema_field(str) @extend_schema_field(str)
def get_label(self, obj): def get_label(self, obj):
return str(obj) return obj.name
def create(self, validated_data): def create(self, validated_data):
# since multi select tags dont have id's # since multi select tags dont have id's
@@ -1327,7 +1345,7 @@ class MealPlanSerializer(SpacedModelSerializer, WritableNestedModelSerializer):
@extend_schema_field(bool) @extend_schema_field(bool)
def in_shopping(self, obj): def in_shopping(self, obj):
return ShoppingListRecipe.objects.filter(mealplan=obj.id).exists() return obj.shoppinglistrecipe_set.count() > 0
def create(self, validated_data): def create(self, validated_data):
validated_data['created_by'] = self.context['request'].user validated_data['created_by'] = self.context['request'].user
@@ -1393,7 +1411,7 @@ class ShoppingListRecipeSerializer(serializers.ModelSerializer):
class Meta: class Meta:
model = ShoppingListRecipe model = ShoppingListRecipe
fields = ('id', 'name', 'recipe', 'recipe_data', 'mealplan', 'meal_plan_data', 'servings', 'created_by',) fields = ('id', 'name', 'recipe', 'recipe_data', 'meal_plan_data', 'mealplan', 'servings', 'created_by',)
read_only_fields = ('id', 'created_by',) read_only_fields = ('id', 'created_by',)
@@ -1412,7 +1430,7 @@ class ShoppingListSerializer(SpacedModelSerializer, WritableNestedModelSerialize
class ShoppingListEntrySerializer(WritableNestedModelSerializer): class ShoppingListEntrySerializer(WritableNestedModelSerializer):
food = FoodSerializer(allow_null=True) food = FoodSimpleSerializer(allow_null=True)
unit = UnitSerializer(allow_null=True, required=False) unit = UnitSerializer(allow_null=True, required=False)
shopping_lists = ShoppingListSerializer(many=True, required=False) shopping_lists = ShoppingListSerializer(many=True, required=False)
list_recipe_data = ShoppingListRecipeSerializer(source='list_recipe', read_only=True) list_recipe_data = ShoppingListRecipeSerializer(source='list_recipe', read_only=True)

View File

@@ -2043,17 +2043,18 @@ class ShoppingListEntryViewSet(LoggingMixin, viewsets.ModelViewSet):
self.queryset = self.queryset.filter( self.queryset = self.queryset.filter(
Q(created_by=self.request.user) Q(created_by=self.request.user)
| Q(created_by__in=list(self.request.user.get_shopping_share()))).prefetch_related('created_by', 'food', | Q(created_by__in=list(self.request.user.get_shopping_share()))).prefetch_related('created_by',
'food__properties', 'food',
'food__properties__property_type', 'shopping_lists',
'food__inherit_fields', 'unit',
'food__supermarket_category', 'list_recipe',
'food__onhand_users', 'list_recipe__recipe__keywords',
'food__substitute', 'list_recipe__recipe__created_by',
'food__child_inherit_fields',
'unit', 'list_recipe',
'list_recipe__mealplan', 'list_recipe__mealplan',
'list_recipe__mealplan__shared',
'list_recipe__mealplan__shoppinglistrecipe_set',
'list_recipe__mealplan__recipe', 'list_recipe__mealplan__recipe',
'list_recipe__mealplan__recipe__keywords',
).distinct().all() ).distinct().all()
updated_after = self.request.query_params.get('updated_after', None) updated_after = self.request.query_params.get('updated_after', None)

View File

@@ -1,14 +1,17 @@
<template> <template>
<v-list-item class="swipe-container border-t-sm mt-0 mb-0 pt-0 pb-0 pe-0 pa-0" :id="itemContainerId" @touchend="handleSwipe()" @click="dialog = true;" <v-list-item class="swipe-container border-t-sm mt-0 mb-0 pt-0 pb-0 pe-0 pa-0 shopping-border" :id="itemContainerId" @touchend="handleSwipe()" @click="dialog = true;"
v-if="isShoppingListFoodVisible(props.shoppingListFood, useUserPreferenceStore().deviceSettings)" v-if="isShoppingListFoodVisible(props.shoppingListFood, useUserPreferenceStore().deviceSettings)"
> >
<!-- <div class="swipe-action" :class="{'bg-success': !isChecked , 'bg-warning': isChecked }">--> <!-- <div class="swipe-action" :class="{'bg-success': !isChecked , 'bg-warning': isChecked }">-->
<!-- <i class="swipe-icon fa-fw fas" :class="{'fa-check': !isChecked , 'fa-cart-plus': isChecked }"></i>--> <!-- <i class="swipe-icon fa-fw fas" :class="{'fa-check': !isChecked , 'fa-cart-plus': isChecked }"></i>-->
<!-- </div>--> <!-- </div>-->
<div class="color-marker-container">
<span :style="{background: sl.color}" v-for="sl in shoppingList"></span>
</div>
<div class="flex-grow-1 p-2"> <div class="flex-grow-1 p-2" >
<div class="d-flex"> <div class="d-flex" >
<div class="d-flex flex-column pr-2 pl-4"> <div class="d-flex flex-column pr-2 pl-4">
<span v-for="a in amounts" v-bind:key="a.key"> <span v-for="a in amounts" v-bind:key="a.key">
<span> <span>
@@ -113,6 +116,17 @@ const actionButtonIcon = computed(() => {
}) })
const shoppingList = computed(() => {
const lists = new Set()
for (let entry of entries.value) {
if (entry.shoppingLists) {
entry.shoppingLists.forEach(l => lists.add(l))
}
}
return Array.from(lists)
})
/** /**
* calculate the amounts for the given line * calculate the amounts for the given line
* can combine 1 to n entries with the same unit * can combine 1 to n entries with the same unit
@@ -248,4 +262,22 @@ function handleSwipe() {
<style> <style>
/* TODO swipe system classes removed because not working (visually, touch detection was working), retrieve from old ShoppingLineItem VCS */ /* TODO swipe system classes removed because not working (visually, touch detection was working), retrieve from old ShoppingLineItem VCS */
/* 2. Container to wrap the color bars and place them to the far left */
.color-marker-container {
position: absolute;
top: 0;
left: 0;
height: 100%;
width: 3px;
display: flex;
flex-direction: column;
}
.color-marker-container span {
width: 100%;
flex-grow: 1;
}
</style> </style>

View File

@@ -89,7 +89,6 @@
<v-row class="pa-0" dense> <v-row class="pa-0" dense>
<v-col class="pa-0" cols="6"> <v-col class="pa-0" cols="6">
<v-chip label density="compact" variant="outlined" :prepend-icon="TSupermarket.icon" append-icon="fa-solid fa-caret-down"> <v-chip label density="compact" variant="outlined" :prepend-icon="TSupermarket.icon" append-icon="fa-solid fa-caret-down">
<template v-if="useUserPreferenceStore().deviceSettings.shopping_selected_supermarket != null"> <template v-if="useUserPreferenceStore().deviceSettings.shopping_selected_supermarket != null">
{{useUserPreferenceStore().deviceSettings.shopping_selected_supermarket.name}} {{useUserPreferenceStore().deviceSettings.shopping_selected_supermarket.name}}
@@ -101,11 +100,34 @@
<v-list-item v-for="s in supermarkets" :key="s.id" @click="useUserPreferenceStore().deviceSettings.shopping_selected_supermarket = s"> <v-list-item v-for="s in supermarkets" :key="s.id" @click="useUserPreferenceStore().deviceSettings.shopping_selected_supermarket = s">
{{ s.name }} {{ s.name }}
</v-list-item> </v-list-item>
<v-list-item prepend-icon="$create" :to="{name: 'ModelEditPage', params: {model: 'Supermarket'}}">
{{$t('Create')}}
</v-list-item>
</v-list> </v-list>
</v-menu> </v-menu>
</v-chip> </v-chip>
</v-col>
<v-col class="pa-0" cols="6">
<v-chip label density="compact" variant="outlined" :prepend-icon="TShoppingList.icon" append-icon="fa-solid fa-caret-down">
<template v-if="useUserPreferenceStore().deviceSettings.shopping_selected_shopping_list != null">
{{useUserPreferenceStore().deviceSettings.shopping_selected_shopping_list.name}}
</template>
<template v-else>{{ $t('ShoppingList') }}</template>
<v-menu activator="parent">
<v-list density="compact">
<v-list-item @click="useUserPreferenceStore().deviceSettings.shopping_selected_shopping_list = null">
{{$t('All')}}
</v-list-item>
<v-list-item v-for="s in shoppingLists" :key="s.id" @click="useUserPreferenceStore().deviceSettings.shopping_selected_shopping_list = s">
{{ s.name }}
</v-list-item>
<v-list-item prepend-icon="$create" :to="{name: 'ModelEditPage', params: {model: 'ShoppingList'}}">
{{$t('Create')}}
</v-list-item>
</v-list>
</v-menu>
</v-chip>
</v-col> </v-col>
</v-row> </v-row>
@@ -270,7 +292,7 @@
import {computed, onMounted, ref} from "vue"; import {computed, onMounted, ref} from "vue";
import {useShoppingStore} from "@/stores/ShoppingStore"; import {useShoppingStore} from "@/stores/ShoppingStore";
import {ApiApi, Recipe, ResponseError, ShoppingListEntry, ShoppingListRecipe, Supermarket} from "@/openapi"; import {ApiApi, Recipe, ResponseError, ShoppingList, ShoppingListEntry, ShoppingListRecipe, Supermarket} from "@/openapi";
import {ErrorMessageType, PreparedMessage, useMessageStore} from "@/stores/MessageStore"; import {ErrorMessageType, PreparedMessage, useMessageStore} from "@/stores/MessageStore";
import ShoppingLineItem from "@/components/display/ShoppingLineItem.vue"; import ShoppingLineItem from "@/components/display/ShoppingLineItem.vue";
import {useUserPreferenceStore} from "@/stores/UserPreferenceStore"; import {useUserPreferenceStore} from "@/stores/UserPreferenceStore";
@@ -287,12 +309,13 @@ import {onBeforeRouteLeave} from "vue-router";
import {isShoppingCategoryVisible} from "@/utils/logic_utils.ts"; import {isShoppingCategoryVisible} from "@/utils/logic_utils.ts";
import ShoppingExportDialog from "@/components/dialogs/ShoppingExportDialog.vue"; import ShoppingExportDialog from "@/components/dialogs/ShoppingExportDialog.vue";
import AddToShoppingDialog from "@/components/dialogs/AddToShoppingDialog.vue"; import AddToShoppingDialog from "@/components/dialogs/AddToShoppingDialog.vue";
import {TSupermarket} from "@/types/Models.ts"; import {TShoppingList, TSupermarket} from "@/types/Models.ts";
const {t} = useI18n() const {t} = useI18n()
const currentTab = ref("shopping") const currentTab = ref("shopping")
const supermarkets = ref([] as Supermarket[]) const supermarkets = ref([] as Supermarket[])
const shoppingLists = ref([] as ShoppingList[])
const manualAddRecipe = ref<undefined | Recipe>(undefined) const manualAddRecipe = ref<undefined | Recipe>(undefined)
/** /**
@@ -327,6 +350,7 @@ onMounted(() => {
} }
loadSupermarkets() loadSupermarkets()
loadShoppingLists()
}) })
/** /**
@@ -401,6 +425,20 @@ function loadSupermarkets() {
}) })
} }
/**
* load a list of supermarkets
*/
function loadShoppingLists() {
let api = new ApiApi()
api.apiShoppingListList().then(r => {
shoppingLists.value = r.results
// TODO either recursive or add a "favorite" attribute to supermarkets for them to display at all
}).catch(err => {
useMessageStore().addError(ErrorMessageType.FETCH_ERROR, err)
})
}
</script> </script>
<style scoped> <style scoped>

View File

@@ -23,6 +23,7 @@
"AiModelHelp": "", "AiModelHelp": "",
"AiProvider": "", "AiProvider": "",
"AiProviderHelp": "", "AiProviderHelp": "",
"All": "",
"App": "", "App": "",
"Apply": "", "Apply": "",
"Are_You_Sure": "", "Are_You_Sure": "",

View File

@@ -23,6 +23,7 @@
"AiModelHelp": "", "AiModelHelp": "",
"AiProvider": "", "AiProvider": "",
"AiProviderHelp": "", "AiProviderHelp": "",
"All": "",
"App": "Приложение", "App": "Приложение",
"Apply": "", "Apply": "",
"Are_You_Sure": "Сигурен ли си?", "Are_You_Sure": "Сигурен ли си?",

View File

@@ -26,6 +26,7 @@
"AiProvider": "", "AiProvider": "",
"AiProviderHelp": "", "AiProviderHelp": "",
"Alignment": "Alineació", "Alignment": "Alineació",
"All": "",
"Amount": "Quantitat", "Amount": "Quantitat",
"App": "Aplicació", "App": "Aplicació",
"Apply": "", "Apply": "",

View File

@@ -26,6 +26,7 @@
"AiProvider": "", "AiProvider": "",
"AiProviderHelp": "", "AiProviderHelp": "",
"Alignment": "Zarovnání", "Alignment": "Zarovnání",
"All": "",
"Amount": "Množství", "Amount": "Množství",
"App": "Aplikace", "App": "Aplikace",
"Apply": "", "Apply": "",

View File

@@ -26,6 +26,7 @@
"AiProvider": "", "AiProvider": "",
"AiProviderHelp": "", "AiProviderHelp": "",
"Alignment": "Justering", "Alignment": "Justering",
"All": "",
"Amount": "Mængde", "Amount": "Mængde",
"App": "App", "App": "App",
"Apply": "", "Apply": "",

View File

@@ -39,6 +39,7 @@
"AiProvider": "AI Anbieter", "AiProvider": "AI Anbieter",
"AiProviderHelp": "Je nach Präferenz können verschiedene AI Anbieter angelegt werden. Diese können auch Space übergreifend sein.", "AiProviderHelp": "Je nach Präferenz können verschiedene AI Anbieter angelegt werden. Diese können auch Space übergreifend sein.",
"Alignment": "Ausrichtung", "Alignment": "Ausrichtung",
"All": "Alle",
"AllRecipes": "Alle Rezepte", "AllRecipes": "Alle Rezepte",
"Amount": "Menge", "Amount": "Menge",
"App": "App", "App": "App",

View File

@@ -26,6 +26,7 @@
"AiProvider": "", "AiProvider": "",
"AiProviderHelp": "", "AiProviderHelp": "",
"Alignment": "Ευθυγράμμιση", "Alignment": "Ευθυγράμμιση",
"All": "",
"Amount": "Ποσότητα", "Amount": "Ποσότητα",
"App": "Εφαρμογή", "App": "Εφαρμογή",
"Apply": "", "Apply": "",

View File

@@ -37,6 +37,7 @@
"AiProvider": "AI Provider", "AiProvider": "AI Provider",
"AiProviderHelp": "You can configure multiple AI providers according to your preferences. They can even be configured to work across multiple spaces.", "AiProviderHelp": "You can configure multiple AI providers according to your preferences. They can even be configured to work across multiple spaces.",
"Alignment": "Alignment", "Alignment": "Alignment",
"All": "All",
"AllRecipes": "All Recipes", "AllRecipes": "All Recipes",
"Amount": "Amount", "Amount": "Amount",
"App": "App", "App": "App",

View File

@@ -36,6 +36,7 @@
"AiProvider": "", "AiProvider": "",
"AiProviderHelp": "", "AiProviderHelp": "",
"Alignment": "Alineación", "Alignment": "Alineación",
"All": "",
"AllRecipes": "Todas las recetas", "AllRecipes": "Todas las recetas",
"Amount": "Cantidad", "Amount": "Cantidad",
"App": "Aplicación", "App": "Aplicación",

View File

@@ -27,6 +27,7 @@
"AiProvider": "", "AiProvider": "",
"AiProviderHelp": "", "AiProviderHelp": "",
"Alignment": "Tasaus", "Alignment": "Tasaus",
"All": "",
"Amount": "Määrä", "Amount": "Määrä",
"App": "Applikaatio", "App": "Applikaatio",
"Apply": "", "Apply": "",

View File

@@ -37,6 +37,7 @@
"AiProvider": "", "AiProvider": "",
"AiProviderHelp": "", "AiProviderHelp": "",
"Alignment": "Alignement", "Alignment": "Alignement",
"All": "",
"AllRecipes": "Toutes les recettes", "AllRecipes": "Toutes les recettes",
"Amount": "Quantité", "Amount": "Quantité",
"App": "Appli", "App": "Appli",

View File

@@ -26,6 +26,7 @@
"AiProvider": "", "AiProvider": "",
"AiProviderHelp": "", "AiProviderHelp": "",
"Alignment": "יישור", "Alignment": "יישור",
"All": "",
"Amount": "כמות", "Amount": "כמות",
"App": "אפליקציה", "App": "אפליקציה",
"Apply": "", "Apply": "",

View File

@@ -26,6 +26,7 @@
"AiProvider": "", "AiProvider": "",
"AiProviderHelp": "", "AiProviderHelp": "",
"Alignment": "Poravnanje", "Alignment": "Poravnanje",
"All": "",
"Amount": "Količina", "Amount": "Količina",
"App": "Aplikacija", "App": "Aplikacija",
"Apply": "", "Apply": "",

View File

@@ -26,6 +26,7 @@
"AiProvider": "", "AiProvider": "",
"AiProviderHelp": "", "AiProviderHelp": "",
"Alignment": "Igazítás", "Alignment": "Igazítás",
"All": "",
"Amount": "Összeg", "Amount": "Összeg",
"App": "Applikáció", "App": "Applikáció",
"Apply": "", "Apply": "",

View File

@@ -17,6 +17,7 @@
"AiModelHelp": "", "AiModelHelp": "",
"AiProvider": "", "AiProvider": "",
"AiProviderHelp": "", "AiProviderHelp": "",
"All": "",
"Apply": "", "Apply": "",
"Automate": "Ավտոմատացնել", "Automate": "Ավտոմատացնել",
"BatchDeleteConfirm": "", "BatchDeleteConfirm": "",

View File

@@ -25,6 +25,7 @@
"AiModelHelp": "", "AiModelHelp": "",
"AiProvider": "", "AiProvider": "",
"AiProviderHelp": "", "AiProviderHelp": "",
"All": "",
"App": "", "App": "",
"Apply": "", "Apply": "",
"Are_You_Sure": "", "Are_You_Sure": "",

View File

@@ -26,6 +26,7 @@
"AiProvider": "", "AiProvider": "",
"AiProviderHelp": "", "AiProviderHelp": "",
"Alignment": "", "Alignment": "",
"All": "",
"Amount": "", "Amount": "",
"App": "", "App": "",
"Apply": "", "Apply": "",

File diff suppressed because it is too large Load Diff

View File

@@ -36,6 +36,7 @@
"AiProvider": "", "AiProvider": "",
"AiProviderHelp": "", "AiProviderHelp": "",
"Alignment": "", "Alignment": "",
"All": "",
"AllRecipes": "", "AllRecipes": "",
"Amount": "", "Amount": "",
"App": "", "App": "",

View File

@@ -26,6 +26,7 @@
"AiProvider": "", "AiProvider": "",
"AiProviderHelp": "", "AiProviderHelp": "",
"Alignment": "", "Alignment": "",
"All": "",
"Amount": "Suma", "Amount": "Suma",
"App": "", "App": "",
"Apply": "", "Apply": "",

View File

@@ -26,6 +26,7 @@
"AiProvider": "", "AiProvider": "",
"AiProviderHelp": "", "AiProviderHelp": "",
"Alignment": "", "Alignment": "",
"All": "",
"Amount": "", "Amount": "",
"App": "", "App": "",
"Apply": "", "Apply": "",

View File

@@ -26,6 +26,7 @@
"AiProvider": "", "AiProvider": "",
"AiProviderHelp": "", "AiProviderHelp": "",
"Alignment": "Justering", "Alignment": "Justering",
"All": "",
"Amount": "Mengde", "Amount": "Mengde",
"App": "App", "App": "App",
"Apply": "", "Apply": "",

View File

@@ -38,6 +38,7 @@
"AiProvider": "AI-provider", "AiProvider": "AI-provider",
"AiProviderHelp": "Je kunt meerdere AI-providers configureren naar jouw voorkeuren. Ze kunnen zelfs werken in meerdere ruimtes.", "AiProviderHelp": "Je kunt meerdere AI-providers configureren naar jouw voorkeuren. Ze kunnen zelfs werken in meerdere ruimtes.",
"Alignment": "Afstemming", "Alignment": "Afstemming",
"All": "",
"AllRecipes": "Alle recepten", "AllRecipes": "Alle recepten",
"Amount": "Hoeveelheid", "Amount": "Hoeveelheid",
"App": "App", "App": "App",

View File

@@ -35,6 +35,7 @@
"AiProvider": "", "AiProvider": "",
"AiProviderHelp": "", "AiProviderHelp": "",
"Alignment": "Wyrównanie", "Alignment": "Wyrównanie",
"All": "",
"AllRecipes": "Wszystkie przepisy", "AllRecipes": "Wszystkie przepisy",
"Amount": "Ilość", "Amount": "Ilość",
"App": "Aplikacja", "App": "Aplikacja",

View File

@@ -19,6 +19,7 @@
"Added_on": "Adicionado a", "Added_on": "Adicionado a",
"Advanced": "Avançado", "Advanced": "Avançado",
"Alignment": "Alinhamento", "Alignment": "Alinhamento",
"All": "",
"Amount": "Quantidade", "Amount": "Quantidade",
"Apply": "", "Apply": "",
"Auto_Planner": "", "Auto_Planner": "",

View File

@@ -36,6 +36,7 @@
"AiProvider": "", "AiProvider": "",
"AiProviderHelp": "", "AiProviderHelp": "",
"Alignment": "Alinhamento", "Alignment": "Alinhamento",
"All": "",
"AllRecipes": "Todas Receitas", "AllRecipes": "Todas Receitas",
"Amount": "Quantidade", "Amount": "Quantidade",
"App": "Aplicação", "App": "Aplicação",

View File

@@ -26,6 +26,7 @@
"AiModelHelp": "", "AiModelHelp": "",
"AiProvider": "", "AiProvider": "",
"AiProviderHelp": "", "AiProviderHelp": "",
"All": "",
"Amount": "Cantitate", "Amount": "Cantitate",
"App": "Aplicație", "App": "Aplicație",
"Apply": "", "Apply": "",

View File

@@ -37,6 +37,7 @@
"AiProvider": "", "AiProvider": "",
"AiProviderHelp": "", "AiProviderHelp": "",
"Alignment": "Выравнивание", "Alignment": "Выравнивание",
"All": "",
"AllRecipes": "Все рецепты", "AllRecipes": "Все рецепты",
"Amount": "Количество", "Amount": "Количество",
"App": "Приложение", "App": "Приложение",

View File

@@ -38,6 +38,7 @@
"AiProvider": "Ponudnik umetne inteligence", "AiProvider": "Ponudnik umetne inteligence",
"AiProviderHelp": "Več ponudnikov umetne inteligence lahko konfigurirate glede na svoje nastavitve. Konfigurirate jih lahko celo za delovanje v več prostorih.", "AiProviderHelp": "Več ponudnikov umetne inteligence lahko konfigurirate glede na svoje nastavitve. Konfigurirate jih lahko celo za delovanje v več prostorih.",
"Alignment": "Poravnava", "Alignment": "Poravnava",
"All": "",
"AllRecipes": "Vsi recepti", "AllRecipes": "Vsi recepti",
"Amount": "Količina", "Amount": "Količina",
"App": "Aplikacija", "App": "Aplikacija",

File diff suppressed because it is too large Load Diff

View File

@@ -26,6 +26,7 @@
"AiProvider": "", "AiProvider": "",
"AiProviderHelp": "", "AiProviderHelp": "",
"Alignment": "Hizalama", "Alignment": "Hizalama",
"All": "",
"Amount": "Miktar", "Amount": "Miktar",
"App": "Uygulama", "App": "Uygulama",
"Apply": "", "Apply": "",

View File

@@ -37,6 +37,7 @@
"AiProvider": "Постачальник ШІ", "AiProvider": "Постачальник ШІ",
"AiProviderHelp": "Ви можете налаштувати декількох постачальників штучного інтелекту відповідно до своїх уподобань. Їх навіть можна налаштувати для роботи в декількох просторах.", "AiProviderHelp": "Ви можете налаштувати декількох постачальників штучного інтелекту відповідно до своїх уподобань. Їх навіть можна налаштувати для роботи в декількох просторах.",
"Alignment": "Вирівнювання", "Alignment": "Вирівнювання",
"All": "",
"AllRecipes": "Всі рецепти", "AllRecipes": "Всі рецепти",
"Amount": "Кількість", "Amount": "Кількість",
"App": "Додаток", "App": "Додаток",

View File

@@ -26,6 +26,7 @@
"AiProvider": "", "AiProvider": "",
"AiProviderHelp": "", "AiProviderHelp": "",
"Alignment": "校准", "Alignment": "校准",
"All": "",
"Amount": "数量", "Amount": "数量",
"App": "应用", "App": "应用",
"Apply": "", "Apply": "",

View File

@@ -36,6 +36,7 @@
"AiProvider": "", "AiProvider": "",
"AiProviderHelp": "", "AiProviderHelp": "",
"Alignment": "對齊", "Alignment": "對齊",
"All": "",
"AllRecipes": "所有食譜", "AllRecipes": "所有食譜",
"Amount": "數量", "Amount": "數量",
"App": "應用程式", "App": "應用程式",

View File

@@ -50,6 +50,7 @@ export const useShoppingStore = defineStore(_STORE_ID, () => {
* group by selected grouping key * group by selected grouping key
*/ */
const getEntriesByGroup = computed(() => { const getEntriesByGroup = computed(() => {
console.log("--> getEntriesByGroup called")
stats.value = { stats.value = {
countChecked: 0, countChecked: 0,
countUnchecked: 0, countUnchecked: 0,
@@ -221,13 +222,16 @@ export const useShoppingStore = defineStore(_STORE_ID, () => {
*/ */
function recLoadShoppingListEntries(requestParameters: ApiShoppingListEntryListRequest) { function recLoadShoppingListEntries(requestParameters: ApiShoppingListEntryListRequest) {
let api = new ApiApi() let api = new ApiApi()
api.apiShoppingListEntryList(requestParameters).then((r) => { return api.apiShoppingListEntryList(requestParameters).then((r) => {
r.results.forEach((e) => { r.results.forEach((e) => {
entries.value.set(e.id!, e) entries.value.set(e.id!, e)
}) })
if (r.next) {
requestParameters.page = requestParameters.page + 1 if (requestParameters.page == 1 && r.next) {
recLoadShoppingListEntries(requestParameters) while (Math.ceil(r.count / requestParameters.pageSize) > requestParameters.page) {
requestParameters.page = requestParameters.page + 1
recLoadShoppingListEntries(requestParameters)
}
} else { } else {
currentlyUpdating.value = false currentlyUpdating.value = false
initialized.value = true initialized.value = true

View File

@@ -228,6 +228,7 @@ export const useUserPreferenceStore = defineStore('user_preference_store', () =>
shopping_show_selected_supermarket_only: false, shopping_show_selected_supermarket_only: false,
shopping_selected_grouping: ShoppingGroupingOptions.CATEGORY, shopping_selected_grouping: ShoppingGroupingOptions.CATEGORY,
shopping_selected_supermarket: null, shopping_selected_supermarket: null,
shopping_selected_shopping_list: null,
shopping_item_info_created_by: false, shopping_item_info_created_by: false,
shopping_item_info_mealplan: true, shopping_item_info_mealplan: true,
shopping_item_info_recipe: true, shopping_item_info_recipe: true,

View File

@@ -1,4 +1,4 @@
import {Supermarket} from "@/openapi"; import {ShoppingList, Supermarket} from "@/openapi";
export type DeviceSettings = { export type DeviceSettings = {
shopping_show_checked_entries: boolean shopping_show_checked_entries: boolean
@@ -6,6 +6,7 @@ export type DeviceSettings = {
shopping_show_selected_supermarket_only: boolean shopping_show_selected_supermarket_only: boolean
shopping_selected_grouping: string shopping_selected_grouping: string
shopping_selected_supermarket: Supermarket | null shopping_selected_supermarket: Supermarket | null
shopping_selected_shopping_list: ShoppingList | null
shopping_item_info_created_by: boolean shopping_item_info_created_by: boolean
shopping_item_info_mealplan: boolean shopping_item_info_mealplan: boolean
shopping_item_info_recipe: boolean shopping_item_info_recipe: boolean

View File

@@ -18,6 +18,10 @@ export function isEntryVisible(entry: ShoppingListEntry, deviceSettings: DeviceS
if (entry.checked && !deviceSettings.shopping_show_checked_entries) { if (entry.checked && !deviceSettings.shopping_show_checked_entries) {
entryVisible = false entryVisible = false
} }
if(deviceSettings.shopping_selected_shopping_list != null && entry.shoppingLists?.findIndex(sl => sl.id == deviceSettings.shopping_selected_shopping_list.id) == -1){
entryVisible = false
}
return entryVisible return entryVisible
} }
@@ -60,16 +64,15 @@ export function isShoppingListFoodDelayed(slf: IShoppingListFood) {
* @param category * @param category
*/ */
export function isShoppingCategoryVisible(category: IShoppingListCategory) { export function isShoppingCategoryVisible(category: IShoppingListCategory) {
let entryCount = category.stats.countUnchecked console.log('checking if category is visible')
let categoryVisible = false
category.foods.forEach(food => {
if(isShoppingListFoodVisible(food, useUserPreferenceStore().deviceSettings)){
categoryVisible = true
}
})
if (useUserPreferenceStore().deviceSettings.shopping_show_checked_entries) { return categoryVisible
entryCount += category.stats.countChecked
}
if (useUserPreferenceStore().deviceSettings.shopping_show_delayed_entries) {
entryCount += category.stats.countUncheckedDelayed
}
return entryCount > 0
} }
// -------------- SPACE RELATED ---------------------- // -------------- SPACE RELATED ----------------------