Compare commits

..

6 Commits

Author SHA1 Message Date
vabene1111
fc40b5220b Merge branch 'develop' 2025-11-24 20:40:07 +01:00
vabene1111
c0d172574d Merge branch 'develop' 2025-11-19 21:53:16 +01:00
vabene1111
84fd3055ea Merge branch 'develop' 2025-11-18 15:29:08 +01:00
vabene1111
d22b5a4a39 Merge branch 'develop' 2025-10-15 15:18:13 +02:00
vabene1111
602f0a8bf0 Merge branch 'master' of https://github.com/TandoorRecipes/recipes 2025-10-11 11:59:11 +02:00
vabene1111
856f417d1b Merge branch 'develop' 2025-10-08 07:57:59 +02:00
74 changed files with 952 additions and 2251 deletions

View File

@@ -1,29 +0,0 @@
# Generated by Django 5.2.7 on 2025-11-30 10:19
import cookbook.models
import django.db.models.deletion
import django_prometheus.models
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('cookbook', '0231_alter_aiprovider_options_alter_automation_options_and_more'),
]
operations = [
migrations.CreateModel(
name='ShoppingList',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('name', models.CharField(blank=True, default='', max_length=32)),
('description', models.TextField(blank=True)),
('color', models.CharField(blank=True, max_length=7, null=True)),
('created_at', models.DateTimeField(auto_now_add=True)),
('updated_at', models.DateTimeField(auto_now=True)),
('space', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='cookbook.space')),
],
bases=(django_prometheus.models.ExportModelOperationsMixin('shopping_list'), models.Model, cookbook.models.PermissionModelMixin),
),
]

View File

@@ -1,28 +0,0 @@
# Generated by Django 5.2.7 on 2025-11-30 14:00
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('cookbook', '0232_shoppinglist'),
]
operations = [
migrations.AddField(
model_name='food',
name='shopping_lists',
field=models.ManyToManyField(blank=True, to='cookbook.shoppinglist'),
),
migrations.AddField(
model_name='shoppinglistentry',
name='shopping_lists',
field=models.ManyToManyField(blank=True, to='cookbook.shoppinglist'),
),
migrations.AddField(
model_name='supermarket',
name='shopping_lists',
field=models.ManyToManyField(blank=True, to='cookbook.shoppinglist'),
),
]

View File

@@ -1,22 +0,0 @@
# Generated by Django 5.2.8 on 2025-12-03 16:02
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('cookbook', '0233_food_shopping_lists_shoppinglistentry_shopping_lists_and_more'),
]
operations = [
migrations.AlterModelOptions(
name='shoppinglist',
options={'ordering': ('pk',)},
),
migrations.AddField(
model_name='userpreference',
name='shopping_update_food_lists',
field=models.BooleanField(default=True),
),
]

View File

@@ -551,7 +551,6 @@ class UserPreference(models.Model, PermissionModelMixin):
show_step_ingredients = models.BooleanField(default=True)
default_delay = models.DecimalField(default=4, max_digits=8, decimal_places=4)
shopping_recent_days = models.PositiveIntegerField(default=7)
shopping_update_food_lists = models.BooleanField(default=True)
csv_delim = models.CharField(max_length=2, default=",")
csv_prefix = models.CharField(max_length=10, blank=True, )
@@ -667,7 +666,6 @@ class Supermarket(models.Model, PermissionModelMixin):
name = models.CharField(max_length=128, validators=[MinLengthValidator(1)])
description = models.TextField(blank=True, null=True)
categories = models.ManyToManyField(SupermarketCategory, through='SupermarketCategoryRelation')
shopping_lists = models.ManyToManyField("ShoppingList", blank=True)
open_data_slug = models.CharField(max_length=128, null=True, blank=True, default=None)
space = models.ForeignKey(Space, on_delete=models.CASCADE)
@@ -782,7 +780,6 @@ class Food(ExportModelOperationsMixin('food'), TreeModel, PermissionModelMixin):
recipe = models.ForeignKey('Recipe', null=True, blank=True, on_delete=models.SET_NULL)
url = models.CharField(max_length=1024, blank=True, null=True, default='')
supermarket_category = models.ForeignKey(SupermarketCategory, null=True, blank=True, on_delete=models.SET_NULL) # inherited field
shopping_lists = models.ManyToManyField("ShoppingList", blank=True)
ignore_shopping = models.BooleanField(default=False) # inherited field
onhand_users = models.ManyToManyField(User, blank=True)
description = models.TextField(default='', blank=True)
@@ -946,8 +943,8 @@ class Ingredient(ExportModelOperationsMixin('ingredient'), models.Model, Permiss
space = models.ForeignKey(Space, on_delete=models.CASCADE)
objects = ScopedManager(space='space')
# def __str__(self):
# return f'{self.pk}: {self.amount} ' + (self.food.name if self.food else ' ') + (self.unit.name if self.unit else '')
def __str__(self):
return f'{self.pk}: {self.amount} ' + (self.food.name if self.food else ' ') + (self.unit.name if self.unit else '')
class Meta:
ordering = ['order', 'pk']
@@ -1160,7 +1157,7 @@ class Comment(ExportModelOperationsMixin('comment'), models.Model, PermissionMod
def __str__(self):
return self.text
class Meta:
ordering = ('pk',)
@@ -1300,30 +1297,14 @@ class ShoppingListRecipe(ExportModelOperationsMixin('shopping_list_recipe'), mod
objects = ScopedManager(space='space')
# def __str__(self):
# return f'Shopping list recipe {self.id} - {self.recipe}'
class Meta:
ordering = ('pk',)
class ShoppingList(ExportModelOperationsMixin('shopping_list'), models.Model, PermissionModelMixin):
name = models.CharField(max_length=32, blank=True, default='')
description = models.TextField(blank=True)
color = models.CharField(max_length=7, blank=True, null=True)
created_at = models.DateTimeField(auto_now_add=True)
updated_at = models.DateTimeField(auto_now=True)
space = models.ForeignKey(Space, on_delete=models.CASCADE)
objects = ScopedManager(space='space')
def __str__(self):
return f'Shopping list recipe {self.id} - {self.recipe}'
class Meta:
ordering = ('pk',)
class ShoppingListEntry(ExportModelOperationsMixin('shopping_list_entry'), models.Model, PermissionModelMixin):
shopping_lists = models.ManyToManyField(ShoppingList, blank=True)
list_recipe = models.ForeignKey(ShoppingListRecipe, on_delete=models.CASCADE, null=True, blank=True, related_name='entries')
food = models.ForeignKey(Food, on_delete=models.CASCADE, related_name='shopping_entries')
unit = models.ForeignKey(Unit, on_delete=models.SET_NULL, null=True, blank=True)

View File

@@ -37,7 +37,7 @@ from cookbook.models import (Automation, BookmarkletImport, Comment, CookLog, Cu
ShareLink, ShoppingListEntry, ShoppingListRecipe, Space,
Step, Storage, Supermarket, SupermarketCategory,
SupermarketCategoryRelation, Sync, SyncLog, Unit, UnitConversion,
UserFile, UserPreference, UserSpace, ViewLog, ConnectorConfig, SearchPreference, SearchFields, AiLog, AiProvider, ShoppingList)
UserFile, UserPreference, UserSpace, ViewLog, ConnectorConfig, SearchPreference, SearchFields, AiLog, AiProvider)
from cookbook.templatetags.custom_tags import markdown
from recipes.settings import AWS_ENABLED, MEDIA_URL, EMAIL_HOST
@@ -186,29 +186,11 @@ class SpaceFilterSerializer(serializers.ListSerializer):
if isinstance(self.context['request'].user, AnonymousUser):
data = []
else:
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()
data = data.filter(userspace__space=self.context['request'].user.get_active_space()).all()
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]
else:
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})
data = data.filter(**{'__'.join(self.child.Meta.model.get_space_key()): self.context['request'].space})
return super().to_representation(data)
@@ -502,20 +484,6 @@ class SpacedModelSerializer(serializers.ModelSerializer):
return super().create(validated_data)
class ShoppingListSerializer(SpacedModelSerializer, WritableNestedModelSerializer):
def create(self, validated_data):
validated_data['name'] = validated_data['name'].strip()
space = validated_data.pop('space', self.context['request'].space)
obj, created = ShoppingList.objects.get_or_create(name__iexact=validated_data['name'], space=space, defaults=validated_data)
return obj
class Meta:
model = ShoppingList
fields = ('id', 'name', 'description', 'color',) # returning dates breaks breaks shopping list deviceSetting save due to date retrieved from local storage as string
read_only_fields = ('id',)
class MealTypeSerializer(SpacedModelSerializer, WritableNestedModelSerializer):
def create(self, validated_data):
@@ -565,7 +533,7 @@ class UserPreferenceSerializer(WritableNestedModelSerializer):
'ingredient_decimals', 'comments', 'shopping_auto_sync', 'mealplan_autoadd_shopping',
'food_inherit_default', 'default_delay',
'mealplan_autoinclude_related', 'mealplan_autoexclude_onhand', 'shopping_share', 'shopping_recent_days',
'csv_delim', 'csv_prefix', 'shopping_update_food_lists',
'csv_delim', 'csv_prefix',
'filter_to_supermarket', 'shopping_add_onhand', 'left_handed', 'show_step_ingredients',
'food_children_exist'
)
@@ -680,7 +648,7 @@ class KeywordLabelSerializer(serializers.ModelSerializer):
@extend_schema_field(str)
def get_label(self, obj):
return obj.name
return str(obj)
class Meta:
list_serializer_class = SpaceFilterSerializer
@@ -697,7 +665,7 @@ class KeywordSerializer(UniqueFieldsMixin, ExtendedRecipeMixin):
@extend_schema_field(str)
def get_label(self, obj):
return obj.name
return str(obj)
def create(self, validated_data):
# since multi select tags dont have id's
@@ -774,7 +742,6 @@ class SupermarketCategoryRelationSerializer(WritableNestedModelSerializer):
class SupermarketSerializer(UniqueFieldsMixin, SpacedModelSerializer, OpenDataModelMixin):
category_to_supermarket = SupermarketCategoryRelationSerializer(many=True, read_only=True)
shopping_lists = ShoppingListSerializer(many=True, required=False)
def create(self, validated_data):
validated_data['name'] = validated_data['name'].strip()
@@ -785,7 +752,7 @@ class SupermarketSerializer(UniqueFieldsMixin, SpacedModelSerializer, OpenDataMo
class Meta:
model = Supermarket
fields = ('id', 'name', 'description', 'shopping_lists', 'category_to_supermarket', 'open_data_slug')
fields = ('id', 'name', 'description', 'category_to_supermarket', 'open_data_slug')
class PropertyTypeSerializer(OpenDataModelMixin, WritableNestedModelSerializer, UniqueFieldsMixin):
@@ -869,7 +836,7 @@ class FoodSerializer(UniqueFieldsMixin, WritableNestedModelSerializer, ExtendedR
substitute_onhand = serializers.SerializerMethodField('get_substitute_onhand')
substitute = FoodSimpleSerializer(many=True, allow_null=True, required=False)
parent = IntegerField(read_only=True)
shopping_lists = ShoppingListSerializer(many=True, required=False)
properties = PropertySerializer(many=True, allow_null=True, required=False)
properties_food_unit = UnitSerializer(allow_null=True, required=False)
properties_food_amount = CustomDecimalField(required=False)
@@ -980,7 +947,7 @@ class FoodSerializer(UniqueFieldsMixin, WritableNestedModelSerializer, ExtendedR
fields = (
'id', 'name', 'plural_name', 'description', 'shopping', 'recipe', 'url', 'properties', 'properties_food_amount', 'properties_food_unit', 'fdc_id',
'food_onhand', 'supermarket_category', 'image', 'parent', 'numchild', 'numrecipe', 'inherit_fields', 'full_name', 'ignore_shopping',
'substitute', 'substitute_siblings', 'substitute_children', 'substitute_onhand', 'child_inherit_fields', 'open_data_slug', 'shopping_lists',
'substitute', 'substitute_siblings', 'substitute_children', 'substitute_onhand', 'child_inherit_fields', 'open_data_slug',
)
read_only_fields = ('id', 'numchild', 'parent', 'image', 'numrecipe')
@@ -1360,7 +1327,7 @@ class MealPlanSerializer(SpacedModelSerializer, WritableNestedModelSerializer):
@extend_schema_field(bool)
def in_shopping(self, obj):
return obj.shoppinglistrecipe_set.count() > 0
return ShoppingListRecipe.objects.filter(mealplan=obj.id).exists()
def create(self, validated_data):
validated_data['created_by'] = self.context['request'].user
@@ -1426,14 +1393,13 @@ class ShoppingListRecipeSerializer(serializers.ModelSerializer):
class Meta:
model = ShoppingListRecipe
fields = ('id', 'name', 'recipe', 'recipe_data', 'meal_plan_data', 'mealplan', 'servings', 'created_by',)
fields = ('id', 'name', 'recipe', 'recipe_data', 'mealplan', 'meal_plan_data', 'servings', 'created_by',)
read_only_fields = ('id', 'created_by',)
class ShoppingListEntrySerializer(WritableNestedModelSerializer):
food = FoodSimpleSerializer(allow_null=True)
food = FoodSerializer(allow_null=True)
unit = UnitSerializer(allow_null=True, required=False)
shopping_lists = ShoppingListSerializer(many=True, required=False)
list_recipe_data = ShoppingListRecipeSerializer(source='list_recipe', read_only=True)
amount = CustomDecimalField()
created_by = UserSerializer(read_only=True)
@@ -1482,13 +1448,7 @@ class ShoppingListEntrySerializer(WritableNestedModelSerializer):
created_by=self.context['request'].user)
del validated_data['mealplan_id']
obj = super().create(validated_data)
if self.context['request'].user.userpreference.shopping_update_food_lists:
obj.shopping_lists.clear()
obj.shopping_lists.set(obj.food.shopping_lists.all())
return obj
return super().create(validated_data)
def update(self, instance, validated_data):
user = self.context['request'].user
@@ -1508,7 +1468,7 @@ class ShoppingListEntrySerializer(WritableNestedModelSerializer):
class Meta:
model = ShoppingListEntry
fields = (
'id', 'list_recipe', 'shopping_lists', 'food', 'unit', 'amount', 'order', 'checked', 'ingredient',
'id', 'list_recipe', 'food', 'unit', 'amount', 'order', 'checked', 'ingredient',
'list_recipe_data', 'created_by', 'created_at', 'updated_at', 'completed_at', 'delay_until', 'mealplan_id'
)
read_only_fields = ('id', 'created_by', 'created_at')
@@ -1769,7 +1729,6 @@ class GenericModelReferenceSerializer(serializers.Serializer):
model = serializers.CharField()
name = serializers.CharField()
# Export/Import Serializers
class KeywordExportSerializer(KeywordSerializer):

View File

@@ -40,7 +40,6 @@ router.register(r'recipe-book-entry', api.RecipeBookEntryViewSet)
router.register(r'unit-conversion', api.UnitConversionViewSet)
router.register(r'property-type', api.PropertyTypeViewSet) # NOTE: if regenerating the legacy API these need renamed to food-property
router.register(r'property', api.PropertyViewSet)
router.register(r'shopping-list', api.ShoppingListViewSet)
router.register(r'shopping-list-entry', api.ShoppingListEntryViewSet)
router.register(r'shopping-list-recipe', api.ShoppingListRecipeViewSet)
router.register(r'space', api.SpaceViewSet)

View File

@@ -88,7 +88,7 @@ from cookbook.models import (Automation, BookmarkletImport, ConnectorConfig, Coo
RecipeBookEntry, ShareLink, ShoppingListEntry,
ShoppingListRecipe, Space, Step, Storage, Supermarket, SupermarketCategory,
SupermarketCategoryRelation, Sync, SyncLog, Unit, UnitConversion,
UserFile, UserPreference, UserSpace, ViewLog, RecipeImport, SearchPreference, SearchFields, AiLog, AiProvider, ShoppingList
UserFile, UserPreference, UserSpace, ViewLog, RecipeImport, SearchPreference, SearchFields, AiLog, AiProvider
)
from cookbook.provider.dropbox import Dropbox
from cookbook.provider.local import Local
@@ -114,7 +114,7 @@ from cookbook.serializer import (AccessTokenSerializer, AutomationSerializer, Au
LocalizationSerializer, ServerSettingsSerializer, RecipeFromSourceResponseSerializer, ShoppingListEntryBulkCreateSerializer, FdcQuerySerializer,
AiImportSerializer, ImportOpenDataSerializer, ImportOpenDataMetaDataSerializer, ImportOpenDataResponseSerializer, ExportRequestSerializer,
RecipeImportSerializer, ConnectorConfigSerializer, SearchPreferenceSerializer, SearchFieldsSerializer, RecipeBatchUpdateSerializer,
AiProviderSerializer, AiLogSerializer, FoodBatchUpdateSerializer, GenericModelReferenceSerializer, ShoppingListSerializer
AiProviderSerializer, AiLogSerializer, FoodBatchUpdateSerializer, GenericModelReferenceSerializer
)
from cookbook.version_info import TANDOOR_VERSION
from cookbook.views.import_export import get_integration
@@ -2011,17 +2011,6 @@ class ShoppingListRecipeViewSet(LoggingMixin, viewsets.ModelViewSet):
return Response(serializer.errors, 400)
class ShoppingListViewSet(LoggingMixin, viewsets.ModelViewSet, DeleteRelationMixing):
queryset = ShoppingList.objects
serializer_class = ShoppingListSerializer
permission_classes = [CustomIsUser & CustomTokenHasReadWriteScope]
pagination_class = DefaultPagination
def get_queryset(self):
queryset = self.queryset.filter(space=self.request.space).all()
return queryset
@extend_schema_view(list=extend_schema(parameters=[
OpenApiParameter(name='updated_after',
description=_('Returns only elements updated after the given timestamp in ISO 8601 format.'),
@@ -2043,18 +2032,17 @@ class ShoppingListEntryViewSet(LoggingMixin, viewsets.ModelViewSet):
self.queryset = self.queryset.filter(
Q(created_by=self.request.user)
| Q(created_by__in=list(self.request.user.get_shopping_share()))).prefetch_related('created_by',
'food',
'shopping_lists',
'unit',
'list_recipe',
'list_recipe__recipe__keywords',
'list_recipe__recipe__created_by',
| Q(created_by__in=list(self.request.user.get_shopping_share()))).prefetch_related('created_by', 'food',
'food__properties',
'food__properties__property_type',
'food__inherit_fields',
'food__supermarket_category',
'food__onhand_users',
'food__substitute',
'food__child_inherit_fields',
'unit', 'list_recipe',
'list_recipe__mealplan',
'list_recipe__mealplan__shared',
'list_recipe__mealplan__shoppinglistrecipe_set',
'list_recipe__mealplan__recipe',
'list_recipe__mealplan__recipe__keywords',
).distinct().all()
updated_after = self.request.query_params.get('updated_after', None)

View File

@@ -1,6 +1,6 @@
<template>
<v-dialog v-model="dialog" :activator="props.activator" style="max-width: 75vw;">
<v-dialog v-model="dialog" activator="parent" style="max-width: 75vw;">
<v-card>
<v-closable-card-title :title="$t('Export')" v-model="dialog"></v-closable-card-title>
@@ -48,7 +48,7 @@
<script setup lang="ts">
import VClosableCardTitle from "@/components/dialogs/VClosableCardTitle.vue";
import {computed, PropType, ref, shallowRef} from "vue";
import {computed, ref} from "vue";
import {useShoppingStore} from "@/stores/ShoppingStore.ts";
import {isEntryVisible, isShoppingCategoryVisible, isShoppingListFoodVisible} from "@/utils/logic_utils.ts";
import {useI18n} from "vue-i18n";
@@ -56,15 +56,10 @@ import {useUserPreferenceStore} from "@/stores/UserPreferenceStore.ts";
import {ShoppingListEntry} from "@/openapi";
import BtnCopy from "@/components/buttons/BtnCopy.vue";
import {useClipboard} from "@vueuse/core";
import {EditorSupportedModels, getGenericModelFromString} from "@/types/Models.ts";
const {t} = useI18n()
const {copy} = useClipboard()
const props = defineProps({
activator: {default: 'parent'},
})
const dialog = defineModel<boolean>()
const mode = ref<'md_list' | 'md_table' | 'csv'>('md_list')
@@ -83,7 +78,7 @@ const exportText = computed(() => {
textArray.push(formatHeader())
useShoppingStore().entriesByGroup.forEach(category => {
useShoppingStore().getEntriesByGroup.forEach(category => {
if (isShoppingCategoryVisible(category)) {
if (category.name === useShoppingStore().UNDEFINED_CATEGORY) {
textArray.push(formatCategory(t('NoCategory')))

View File

@@ -8,9 +8,6 @@
<v-label>{{ $t('Choose_Category') }}</v-label>
<model-select model="SupermarketCategory" @update:modelValue="categoryUpdate" allow-create></model-select>
<v-label>{{ $t('ShoppingList') }}</v-label>
<model-select model="ShoppingList" @update:modelValue="shoppingListUpdate" mode="tags" allow-create></model-select>
<v-row>
<v-col class="pr-0">
<v-btn height="80px" color="info" density="compact" size="small" block stacked
@@ -79,9 +76,6 @@
<v-list-item-subtitle v-if="isDelayed(e)" class="text-info font-weight-bold">
{{ $t('PostponedUntil') }} {{ DateTime.fromJSDate(e.delayUntil!).toLocaleString(DateTime.DATETIME_SHORT) }}
</v-list-item-subtitle>
<v-list-item-subtitle v-if="e.shoppingLists.length > 0" class="text-info font-weight-bold">
<shopping-lists-bar :shopping-lists="e.shoppingLists"></shopping-lists-bar>
</v-list-item-subtitle>
<v-btn-group divided border>
<v-btn icon="" @click="e.amount = e.amount / 2; updateEntryAmount(e)" v-if="!e.ingredient">
@@ -128,8 +122,8 @@
<script setup lang="ts">
import {computed, ref} from "vue";
import {ApiApi, PatchedShoppingListEntry, ShoppingList, ShoppingListEntry, SupermarketCategory} from "@/openapi";
import {computed} from "vue";
import {ApiApi, PatchedShoppingListEntry, ShoppingListEntry, SupermarketCategory} from "@/openapi";
import ModelSelect from "@/components/inputs/ModelSelect.vue";
import {IShoppingListFood} from "@/types/Shopping";
import VClosableCardTitle from "@/components/dialogs/VClosableCardTitle.vue";
@@ -139,16 +133,12 @@ import ModelEditDialog from "@/components/dialogs/ModelEditDialog.vue";
import {useShoppingStore} from "@/stores/ShoppingStore";
import {isDelayed, isShoppingListFoodDelayed} from "@/utils/logic_utils";
import {ErrorMessageType, PreparedMessage, useMessageStore} from "@/stores/MessageStore";
import ShoppingListsBar from "@/components/display/ShoppingListsBar.vue";
import {useUserPreferenceStore} from "@/stores/UserPreferenceStore.ts";
const {mobile} = useDisplay()
const showDialog = defineModel<Boolean>()
const shoppingListFood = defineModel<IShoppingListFood>('shoppingListFood', {required: true})
const shoppingListUpdateLoading = ref(false)
/**
* returns a flat list of entries for the given shopping list food
*/
@@ -181,35 +171,6 @@ function categoryUpdate(category: SupermarketCategory) {
})
}
/**
* change the shopping list for all entries
* @param shoppingLists
*/
function shoppingListUpdate(shoppingLists: ShoppingList[]) {
const api = new ApiApi()
const promises: Promise<any>[] = []
shoppingListUpdateLoading.value = true
shoppingListFood.value.entries.forEach(e => {
e.shoppingLists = shoppingLists
promises.push(api.apiShoppingListEntryUpdate({id: e.id, shoppingListEntry: e}).then(r => {
}).catch(err => {
useMessageStore().addError(ErrorMessageType.UPDATE_ERROR, err)
}))
})
if (useUserPreferenceStore().userSettings.shoppingUpdateFoodLists){
shoppingListFood.value.food.shoppingLists = shoppingLists
promises.push(api.apiFoodUpdate({id: shoppingListFood.value.food.id!, food: shoppingListFood.value.food}).then(r => {
}).catch(err => {
useMessageStore().addError(ErrorMessageType.UPDATE_ERROR, err)
}))
}
Promise.all(promises).finally(() => shoppingListUpdateLoading.value = false)
}
/**
* add new entry for currently selected food type
*/

View File

@@ -1,14 +1,11 @@
<template>
<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-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-if="isShoppingListFoodVisible(props.shoppingListFood, useUserPreferenceStore().deviceSettings)"
>
<!-- <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>-->
<!-- </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="d-flex">
@@ -59,13 +56,12 @@ import {computed, PropType, ref} from "vue";
import {DateTime} from "luxon";
import {useShoppingStore} from "@/stores/ShoppingStore.js";
import {useUserPreferenceStore} from "@/stores/UserPreferenceStore.js";
import {ApiApi, Food, ShoppingList, ShoppingListEntry} from '@/openapi'
import {ApiApi, Food, ShoppingListEntry} from '@/openapi'
import {ErrorMessageType, useMessageStore} from "@/stores/MessageStore";
import {IShoppingListFood, ShoppingLineAmount} from "@/types/Shopping";
import {isDelayed, isEntryVisible, isShoppingListFoodDelayed, isShoppingListFoodVisible} from "@/utils/logic_utils";
import ShoppingLineItemDialog from "@/components/dialogs/ShoppingLineItemDialog.vue";
import {pluralString} from "@/utils/model_utils.ts";
import ShoppingListsBar from "@/components/display/ShoppingListsBar.vue";
const emit = defineEmits(['clicked'])
@@ -86,7 +82,9 @@ const entries = computed(() => {
*/
const itemContainerId = computed(() => {
let id = 'id_sli_'
entries.value.forEach(e => id += e.id + '_')
for (let i in entries.value) {
id += i + '_'
}
return id
})
@@ -114,22 +112,6 @@ const actionButtonIcon = computed(() => {
})
const shoppingList = computed(() => {
const lists = [] as ShoppingList[]
entries.value.forEach(e => {
if (e.shoppingLists) {
e.shoppingLists.forEach(l => {
if (lists.findIndex(sl => sl.id == l.id) == -1) {
lists.push(l)
}
})
}
})
return lists
})
/**
* calculate the amounts for the given line
* can combine 1 to n entries with the same unit
@@ -141,34 +123,34 @@ const amounts = computed((): ShoppingLineAmount[] => {
for (let i in entries.value) {
let e = entries.value[i]
if (isEntryVisible(e, useUserPreferenceStore().deviceSettings)) {
let unit = -1
if (e.unit !== undefined && e.unit !== null) {
unit = e.unit.id!
}
let unit = -1
if (e.unit !== undefined && e.unit !== null) {
unit = e.unit.id!
}
if (e.amount > 0) {
if (e.amount > 0) {
let uaMerged = false
unitAmounts.forEach(ua => {
if (((ua.unit == null && e.unit == null) || (ua.unit != null && ua.unit.id! == unit)) && ua.checked == e.checked && ua.delayed == isDelayed(e)) {
ua.amount += e.amount
uaMerged = true
}
})
let uaMerged = false
unitAmounts.forEach(ua => {
if (((ua.unit == null && e.unit == null) || (ua.unit != null && ua.unit.id! == unit)) && ua.checked == e.checked && ua.delayed == isDelayed(e)) {
ua.amount += e.amount
uaMerged = true
if (!uaMerged) {
unitAmounts.push({
key: `${unit}_${e.checked}_${isDelayed(e)}`,
amount: e.amount,
unit: e.unit,
checked: e.checked,
delayed: isDelayed(e)
} as ShoppingLineAmount)
}
})
if (!uaMerged) {
unitAmounts.push({
key: `${unit}_${e.checked}_${isDelayed(e)}`,
amount: e.amount,
unit: e.unit,
checked: e.checked,
delayed: isDelayed(e)
} as ShoppingLineAmount)
}
}
}
return unitAmounts
})
@@ -189,28 +171,29 @@ const infoRow = computed(() => {
for (let i in entries.value) {
let e = entries.value[i]
if (isEntryVisible(e, useUserPreferenceStore().deviceSettings)) {
if (authors.indexOf(e.createdBy.displayName) === -1) {
authors.push(e.createdBy.displayName)
}
if (authors.indexOf(e.createdBy.displayName) === -1) {
authors.push(e.createdBy.displayName)
}
if (e.listRecipe != null) {
if (e.listRecipeData.recipe != null) {
let recipe_name = e.listRecipeData.recipeData.name
if (recipes.indexOf(recipe_name) === -1) {
recipes.push(recipe_name.substring(0, 14) + (recipe_name.length > 14 ? '..' : ''))
if (e.listRecipe != null) {
if (e.listRecipeData.recipe != null) {
let recipe_name = e.listRecipeData.recipeData.name
if (recipes.indexOf(recipe_name) === -1) {
recipes.push(recipe_name.substring(0, 14) + (recipe_name.length > 14 ? '..' : ''))
}
}
if (e.listRecipeData.mealplan != null) {
let meal_plan_entry = (e.listRecipeData.mealPlanData.mealType.name.substring(0, 8) || '') + (e.listRecipeData.mealPlanData.mealType.name.length > 8 ? '..' : '') + ' (' + DateTime.fromJSDate(e.listRecipeData.mealPlanData.fromDate).toLocaleString(DateTime.DATE_SHORT) + ')'
if (meal_pans.indexOf(meal_plan_entry) === -1) {
meal_pans.push(meal_plan_entry)
}
}
}
if (e.listRecipeData.mealplan != null) {
let meal_plan_entry = (e.listRecipeData.mealPlanData.mealType.name.substring(0, 8) || '') + (e.listRecipeData.mealPlanData.mealType.name.length > 8 ? '..' : '') + ' (' + DateTime.fromJSDate(e.listRecipeData.mealPlanData.fromDate).toLocaleString(DateTime.DATE_SHORT) + ')'
if (meal_pans.indexOf(meal_plan_entry) === -1) {
meal_pans.push(meal_plan_entry)
}
}
}
}
if (useUserPreferenceStore().deviceSettings.shopping_item_info_created_by && authors.length > 0) {
@@ -264,22 +247,4 @@ function handleSwipe() {
<style>
/* 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>

View File

@@ -2,7 +2,7 @@
<v-tabs v-model="currentTab">
<v-tab value="shopping"><i class="fas fa-fw"
:class="{'fa-circle-notch fa-spin':useShoppingStore().currentlyUpdating, 'fa-shopping-cart ': !useShoppingStore().currentlyUpdating}"></i> <span
class="d-none d-md-block ms-1">{{ $t('Shopping_list') }} ({{ useShoppingStore().totalFoods }})</span></v-tab>
class="d-none d-md-block ms-1">{{ $t('Shopping_list') }} ({{ useShoppingStore().stats.countUnchecked }})</span></v-tab>
<v-tab value="recipes"><i class="fas fa-book fa-fw"></i> <span class="d-none d-md-block ms-1">{{
$t('Recipes')
}} ({{ useShoppingStore().getAssociatedRecipes().length }})</span></v-tab>
@@ -25,13 +25,9 @@
<v-list density="compact">
<v-list-item @click="useShoppingStore().undoChange()" prepend-icon="fa-solid fa-arrow-rotate-left">{{ $t('Undo') }}</v-list-item>
<v-list-item @click="exportDialog = true" link prepend-icon="fa-solid fa-download">
{{ $t('Export') }}
</v-list-item>
<v-divider></v-divider>
<v-list-item>
<v-select hide-details :items="groupingOptionsItems" v-model="useUserPreferenceStore().deviceSettings.shopping_selected_grouping"
@update:modelValue="useShoppingStore().updateEntriesStructure()" :label="$t('GroupBy')">
<v-select hide-details :items="groupingOptionsItems" v-model="useUserPreferenceStore().deviceSettings.shopping_selected_grouping" :label="$t('GroupBy')">
</v-select>
</v-list-item>
<v-list-item v-if="useUserPreferenceStore().deviceSettings.shopping_selected_grouping == ShoppingGroupingOptions.CATEGORY">
@@ -69,83 +65,29 @@
</v-list>
</v-menu>
<!-- <v-btn height="100%" rounded="0" variant="plain">-->
<!-- <i class="fa-solid fa-download"></i>-->
<!-- <shopping-export-dialog></shopping-export-dialog>-->
<!-- </v-btn>-->
<v-btn height="100%" rounded="0" variant="plain">
<i class="fa-solid fa-download"></i>
<shopping-export-dialog></shopping-export-dialog>
</v-btn>
<!-- <v-btn height="100%" rounded="0" variant="plain" @click="useShoppingStore().undoChange()">-->
<!-- <i class="fa-solid fa-arrow-rotate-left"></i>-->
<!-- </v-btn>-->
<v-btn height="100%" rounded="0" variant="plain" @click="useShoppingStore().undoChange()">
<i class="fa-solid fa-arrow-rotate-left"></i>
</v-btn>
</v-tabs>
<shopping-export-dialog v-model="exportDialog" activator="model"></shopping-export-dialog>
<v-window v-model="currentTab">
<v-window-item value="shopping">
<v-container>
<!-- <v-row class="pa-0" dense>-->
<!-- <v-col class="pa-0">-->
<!-- <v-chip-group v-model="useUserPreferenceStore().deviceSettings.shopping_selected_supermarket" v-if="supermarkets.length > 0">-->
<!-- <v-chip v-for="s in supermarkets" :value="s" :key="s.id" label density="compact" variant="outlined" color="primary">{{ s.name }}</v-chip>-->
<!-- </v-chip-group>-->
<!-- </v-col>-->
<!-- </v-row>-->
<v-row class="pa-0" dense>
<v-col class="pa-0">
<v-chip label density="compact" variant="outlined" style="max-width: 50%;" :prepend-icon="TSupermarket.icon" append-icon="fa-solid fa-caret-down">
<span v-if="useUserPreferenceStore().deviceSettings.shopping_selected_supermarket != null">
{{ useUserPreferenceStore().deviceSettings.shopping_selected_supermarket.name }}
</span>
<span v-else>{{ $t('Supermarket') }}</span>
<v-menu activator="parent">
<v-list density="compact">
<v-list-item @click="useUserPreferenceStore().deviceSettings.shopping_selected_shopping_list = []; useShoppingStore().updateEntriesStructure()">
{{ $t('SelectNone') }}
</v-list-item>
<v-list-item v-for="s in supermarkets" :key="s.id" @click="useUserPreferenceStore().deviceSettings.shopping_selected_supermarket = s">
{{ s.name }}
</v-list-item>
<v-list-item prepend-icon="$create" :to="{name: 'ModelEditPage', params: {model: 'Supermarket'}}">
{{ $t('Create') }}
</v-list-item>
</v-list>
</v-menu>
</v-chip>
<v-chip label density="compact" variant="outlined" style="max-width: 50%;" :prepend-icon="TShoppingList.icon" append-icon="fa-solid fa-caret-down">
<template v-if="useUserPreferenceStore().deviceSettings.shopping_selected_shopping_list.length > 0">
{{ shoppingLists.filter(sl => useUserPreferenceStore().deviceSettings.shopping_selected_shopping_list.includes(sl.id)).flatMap(sl => sl.name).join(', ') }}
</template>
<template v-else>{{ $t('ShoppingList') }}</template>
<v-menu activator="parent" :close-on-content-click="false">
<v-list density="compact" v-model:selected="useUserPreferenceStore().deviceSettings.shopping_selected_shopping_list"
@update:selected="useShoppingStore().updateEntriesStructure()" select-strategy="leaf">
<v-list-item @click="useUserPreferenceStore().deviceSettings.shopping_selected_shopping_list = []; useShoppingStore().updateEntriesStructure()">
{{ $t('All') }}
</v-list-item>
<v-list-item v-for="s in shoppingLists" :key="s.id" :value="s.id">
<template v-slot:prepend="{ isSelected, select }">
<v-list-item-action start>
<v-checkbox-btn :model-value="isSelected" @update:model-value="select"></v-checkbox-btn>
</v-list-item-action>
</template>
{{ 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-chip-group v-model="useUserPreferenceStore().deviceSettings.shopping_selected_supermarket" v-if="supermarkets.length > 0">
<v-chip v-for="s in supermarkets" :value="s" :key="s.id" label density="compact" variant="outlined" color="primary">{{ s.name }}</v-chip>
</v-chip-group>
</v-col>
</v-row>
<v-row class="mt-0">
<v-row>
<v-col>
<v-alert v-if="useShoppingStore().hasFailedItems()" color="warning" class="mb-2">
<template #prepend>
@@ -166,17 +108,18 @@
<v-skeleton-loader type="list-item"></v-skeleton-loader>
</v-list>
<v-list class="mt-3" density="compact" v-else>
<template v-for="category in useShoppingStore().entriesByGroup" :key="category.name">
<template v-for="category in useShoppingStore().getEntriesByGroup" :key="category.name">
<template v-if="isShoppingCategoryVisible(category)">
<v-list-subheader v-if="category.name === useShoppingStore().UNDEFINED_CATEGORY"><i>{{ $t('NoCategory') }}</i></v-list-subheader>
<v-list-subheader v-else>{{ category.name }}</v-list-subheader>
<v-divider></v-divider>
<v-list-subheader v-if="category.name === useShoppingStore().UNDEFINED_CATEGORY"><i>{{ $t('NoCategory') }}</i></v-list-subheader>
<v-list-subheader v-else>{{ category.name }}</v-list-subheader>
<v-divider></v-divider>
<template v-for="[i, value] in category.foods" :key="value.food.id">
<shopping-line-item :shopping-list-food="value"></shopping-line-item>
</template>
<template v-for="[i, value] in category.foods" :key="value.food.id">
<shopping-line-item :shopping-list-food="value"></shopping-line-item>
</template>
</template>
</v-list>
@@ -303,9 +246,9 @@
<script setup lang="ts">
import {computed, onMounted, ref, toRef} from "vue";
import {computed, onMounted, ref} from "vue";
import {useShoppingStore} from "@/stores/ShoppingStore";
import {ApiApi, Recipe, ResponseError, ShoppingList, ShoppingListEntry, ShoppingListRecipe, Supermarket} from "@/openapi";
import {ApiApi, Recipe, ResponseError, ShoppingListEntry, ShoppingListRecipe, Supermarket} from "@/openapi";
import {ErrorMessageType, PreparedMessage, useMessageStore} from "@/stores/MessageStore";
import ShoppingLineItem from "@/components/display/ShoppingLineItem.vue";
import {useUserPreferenceStore} from "@/stores/UserPreferenceStore";
@@ -322,14 +265,11 @@ import {onBeforeRouteLeave} from "vue-router";
import {isShoppingCategoryVisible} from "@/utils/logic_utils.ts";
import ShoppingExportDialog from "@/components/dialogs/ShoppingExportDialog.vue";
import AddToShoppingDialog from "@/components/dialogs/AddToShoppingDialog.vue";
import {TShoppingList, TSupermarket} from "@/types/Models.ts";
const {t} = useI18n()
const exportDialog = ref(false)
const currentTab = ref("shopping")
const supermarkets = ref([] as Supermarket[])
const shoppingLists = ref([] as ShoppingList[])
const manualAddRecipe = ref<undefined | Recipe>(undefined)
/**
@@ -364,7 +304,6 @@ onMounted(() => {
}
loadSupermarkets()
loadShoppingLists()
})
/**
@@ -439,20 +378,6 @@ 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>
<style scoped>

View File

@@ -1,27 +0,0 @@
<template>
<div v-if="props.shoppingLists">
<slot name="prepend"></slot>
<v-chip class="me-1 mb-1" :color="shoppingList.color" :size="props.size" :variant="props.variant" label v-for="shoppingList in props.shoppingLists">
{{ shoppingList.name }}
</v-chip>
<slot name="append"></slot>
</div>
</template>
<script setup lang="ts">
import {Keyword, KeywordLabel, ShoppingList} from "@/openapi";
import {computed, PropType} from "vue";
import {useUserPreferenceStore} from "@/stores/UserPreferenceStore.ts";
const props = defineProps({
shoppingLists: Array as PropType<Array<ShoppingList> | undefined>,
size: {type: String, default: 'x-small'},
variant: {type: String as PropType<NonNullable<"tonal" | "flat" | "text" | "elevated" | "outlined" | "plain"> | undefined>, default: 'outlined'},
})
</script>

View File

@@ -30,7 +30,6 @@
<v-textarea :label="$t('Description')" v-model="editingObj.description"></v-textarea>
<!-- TODO fix card overflow invisible, overflow-visible class is not working -->
<model-select :label="$t('Category')" v-model="editingObj.supermarketCategory" model="SupermarketCategory" allow-create append-to-body></model-select>
<model-select :label="$t('ShoppingList')" :hint="$t('DefaultShoppingListHelp')" v-model="editingObj.shoppingLists" model="ShoppingList" mode="tags" allow-create append-to-body></model-select>
</v-form>
</v-tabs-window-item>

View File

@@ -1,68 +0,0 @@
<template>
<model-editor-base
:loading="loading"
:dialog="dialog"
@save="saveObject"
@delete="deleteObject"
@close="emit('close'); editingObjChanged = false"
:is-update="isUpdate()"
:is-changed="editingObjChanged"
:model-class="modelClass"
:object-name="editingObjName()"
:editing-object="editingObj">
<v-card-text>
<v-form :disabled="loading">
<v-text-field :label="$t('Name')" v-model="editingObj.name"></v-text-field>
<v-textarea :label="$t('Description')" v-model="editingObj.description" :rows="2" auto-grow></v-textarea>
<v-color-picker :label="$t('Color')" v-model="editingObj.color" mode="hex" :modes="['hex']" show-swatches
:swatches="[['#ddbf86'],['#b98766'],['#b55e4f'],['#82aa8b'],['#385f84']]"></v-color-picker>
</v-form>
</v-card-text>
</model-editor-base>
</template>
<script setup lang="ts">
import {onMounted, PropType, watch} from "vue";
import {ShoppingList, ShoppingListEntry} from "@/openapi";
import ModelEditorBase from "@/components/model_editors/ModelEditorBase.vue";
import {useModelEditorFunctions} from "@/composables/useModelEditorFunctions";
import ModelSelect from "@/components/inputs/ModelSelect.vue";
const props = defineProps({
item: {type: {} as PropType<ShoppingList>, required: false, default: null},
itemId: {type: [Number, String], required: false, default: undefined},
itemDefaults: {type: {} as PropType<ShoppingList>, required: false, default: {} as ShoppingList},
dialog: {type: Boolean, default: false}
})
const emit = defineEmits(['create', 'save', 'delete', 'close', 'changedState'])
const {setupState, deleteObject, saveObject, isUpdate, editingObjName, loading, editingObj, editingObjChanged, modelClass} = useModelEditorFunctions<ShoppingList>('ShoppingList', emit)
/**
* watch prop changes and re-initialize editor
* required to embed editor directly into pages and be able to change item from the outside
*/
watch([() => props.item, () => props.itemId], () => {
initializeEditor()
})
// object specific data (for selects/display)
onMounted(() => {
initializeEditor()
})
/**
* component specific state setup logic
*/
function initializeEditor(){
setupState(props.item, props.itemId, {itemDefaults: props.itemDefaults})
}
</script>
<style scoped>
</style>

View File

@@ -27,7 +27,6 @@
<v-checkbox :label="$t('mealplan_autoinclude_related')" :hint="$t('mealplan_autoinclude_related_desc')" persistent-hint v-model="useUserPreferenceStore().userSettings.mealplanAutoincludeRelated"></v-checkbox>
<v-checkbox :label="$t('shopping_add_onhand')" :hint="$t('shopping_add_onhand_desc')" persistent-hint v-model="useUserPreferenceStore().userSettings.shoppingAddOnhand"></v-checkbox>
<v-checkbox :label="$t('filter_to_supermarket')" :hint="$t('filter_to_supermarket_desc')" persistent-hint v-model="useUserPreferenceStore().userSettings.filterToSupermarket"></v-checkbox>
<v-checkbox :label="$t('UpdateFoodLists')" :hint="$t('UpdateFoodListsHelp')" persistent-hint v-model="useUserPreferenceStore().userSettings.shoppingUpdateFoodLists"></v-checkbox>
<v-number-input
class="mt-2"

View File

@@ -16,7 +16,7 @@ export function useNavigation() {
{component: VListItem, prependIcon: '$recipes', title: 'Home', to: {name: 'StartPage', params: {}}},
{component: VListItem, prependIcon: '$search', title: t('Search'), to: {name: 'SearchPage', params: {}}},
{component: VListItem, prependIcon: '$mealplan', title: t('Meal_Plan'), to: {name: 'MealPlanPage', params: {}}},
{component: VListItem, prependIcon: '$shopping', title: t('Shopping'), to: {name: 'ShoppingListPage', params: {}}},
{component: VListItem, prependIcon: '$shopping', title: t('Shopping_list'), to: {name: 'ShoppingListPage', params: {}}},
{component: VListItem, prependIcon: 'fas fa-globe', title: t('Import'), to: {name: 'RecipeImportPage', params: {}}},
{component: VListItem, prependIcon: '$books', title: t('Books'), to: {name: 'BooksPage', params: {}}},
{component: VListItem, prependIcon: 'fa-solid fa-folder-tree', title: t('Database'), to: {name: 'DatabasePage', params: {}}},

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -39,7 +39,6 @@
"AiProvider": "AI Anbieter",
"AiProviderHelp": "Je nach Präferenz können verschiedene AI Anbieter angelegt werden. Diese können auch Space übergreifend sein.",
"Alignment": "Ausrichtung",
"All": "Alle",
"AllRecipes": "Alle Rezepte",
"Amount": "Menge",
"App": "App",
@@ -152,7 +151,6 @@
"Decimals": "Nachkommastellen",
"Default": "Standard",
"DefaultPage": "Standardseite",
"DefaultShoppingListHelp": "Standard Liste wenn dieses Lebensmittel auf die Einkaufsliste gesetzt wird.",
"Default_Unit": "Standardeinheit",
"DelayFor": "Um {hours} Stunden verschieben",
"DelayUntil": "Verzögerung bis",
@@ -505,17 +503,14 @@
"Share": "Teilen",
"ShopLater": "Später kaufen",
"ShopNow": "Jetzt kaufen",
"Shopping": "Einkaufen",
"ShoppingBackgroundSyncWarning": "Schlechte Netzwerkverbindung, Warten auf Synchronisation ...",
"ShoppingList": "Einkaufsliste",
"ShoppingListEntry": "Einkaufslisten Eintrag",
"ShoppingListEntryHelp": "Einträge auf der Einkaufsliste können manuell oder automatisch durch Rezepte und Essenspläne erstellt werden.",
"ShoppingListHelp": "Erlaubt es Einträge auf verschiedene Listen zu setzten. Beispielsweise für verschiedene Supermärkte, Angebote oder Ereignisse. ",
"ShoppingListRecipe": "Einkaufslisten Rezepte",
"Shopping_Categories": "Einkaufskategorien",
"Shopping_Category": "Einkaufskategorie",
"Shopping_List_Empty": "Deine Einkaufsliste ist aktuell leer. Einträge können über das Kontextmenü hinzugefügt werden (Rechtsklick auf einen Eintrag oder Klick auf das Menü-Icon)",
"Shopping_input_placeholder": "z.B. 100 g Kartoffeln",
"Shopping_input_placeholder": "z.B. Kartoffeln/ 100 Kartoffeln/ 100 g Kartoffeln",
"Shopping_list": "Einkaufsliste",
"ShowDelayed": "Zeige verschobene Elemente",
"ShowIngredients": "Zutaten anzeigen",
@@ -617,8 +612,6 @@
"Unrated": "Unbewertet",
"Up": "Hoch",
"Update": "Aktualisieren",
"UpdateFoodLists": "Lebensmittel Einkaufslisten aktualisieren",
"UpdateFoodListsHelp": "Aktualisiert die Standardeinkaufslisten im Lebensmittel wenn diese beim Einkaufen geändert werden.",
"Update_Existing_Data": "Vorhandene Daten aktualisieren",
"Updated": "Aktualisiert",
"UpgradeNow": "Jetzt Upgraden",

View File

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

View File

@@ -37,7 +37,6 @@
"AiProvider": "AI Provider",
"AiProviderHelp": "You can configure multiple AI providers according to your preferences. They can even be configured to work across multiple spaces.",
"Alignment": "Alignment",
"All": "All",
"AllRecipes": "All Recipes",
"Amount": "Amount",
"App": "App",
@@ -150,7 +149,6 @@
"Decimals": "Decimals",
"Default": "Default",
"DefaultPage": "Default Page",
"DefaultShoppingListHelp": "Default List when this Food is added to the Shoppinglist.",
"Default_Unit": "Default Unit",
"DelayFor": "Delay for {hours} hours",
"DelayUntil": "Delay Until",
@@ -503,17 +501,14 @@
"Share": "Share",
"ShopLater": "Shop later",
"ShopNow": "Shop now",
"Shopping": "Shopping",
"ShoppingBackgroundSyncWarning": "Bad network, waiting to sync ...",
"ShoppingList": "Shoppinglist",
"ShoppingListEntry": "Shoppinglist Entry",
"ShoppingListEntryHelp": "Shopping list entries can be created manually or trough recipes and meal plans.",
"ShoppingListHelp": "Allows you to put entries on different lists. Can be used for different supermarkets, special offers or events. ",
"ShoppingListRecipe": "Shoppinglist Recipe",
"Shopping_Categories": "Shopping Categories",
"Shopping_Category": "Shopping Category",
"Shopping_List_Empty": "Your shopping list is currently empty, you can add items via the context menu of a meal plan entry (right click on the card or left click the menu icon)",
"Shopping_input_placeholder": "e.g. 100 g Potatoes",
"Shopping_input_placeholder": "e.g. Potato/100 Potatoes/100 g Potatoes",
"Shopping_list": "Shopping List",
"ShowDelayed": "Show delayed items",
"ShowIngredients": "Show Ingredients",
@@ -615,8 +610,6 @@
"Unrated": "Unrated",
"Up": "Up",
"Update": "Update",
"UpdateFoodLists": "Update Food Shoppinglists",
"UpdateFoodListsHelp": "Update the default shopping lists in the food when changing shopping lists during shopping.",
"Update_Existing_Data": "Update Existing Data",
"Updated": "Updated",
"UpgradeNow": "Upgrade now",

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -38,7 +38,6 @@
"AiProvider": "Fornitore AI",
"AiProviderHelp": "Puoi configurare più fornitori AI in base alle tue preferenze. Possono essere configurati anche per lavorare tra più spazi.",
"Alignment": "Allineamento",
"All": "",
"AllRecipes": "Tutte le ricette",
"Amount": "Quantità",
"App": "Applicazione",
@@ -454,7 +453,7 @@
"RecipeBookHelp": "I ricettari contengono voci di ricette oppure possono essere compilati automaticamente utilizzando filtri di ricerca salvati. ",
"RecipeHelp": "Le ricette sono la base del Tandoor e sono composte da informazioni generali e passaggi, oltre che da ingredienti, istruzioni e altro ancora. ",
"RecipeStepsHelp": "Ingredienti, istruzioni e altro possono essere modificati nella scheda Step.",
"RecipeStructure": "Struttura ricetta",
"RecipeStructure": "",
"Recipe_Book": "Libro di ricette",
"Recipe_Image": "Immagine ricetta",
"Recipes": "Ricette",
@@ -542,7 +541,7 @@
"Space_Cosmetic_Settings": "Alcune impostazioni cosmetiche possono essere modificate dagli amministratori dell'istanza e sovrascriveranno le impostazioni client per quell'istanza.",
"Split": "Dividi",
"Split_All_Steps": "Divide tutte le righe in step separati.",
"Start": "Avvia",
"Start": "",
"StartDate": "Data d'inizio",
"Starting_Day": "Giorno di inizio della settimana",
"StartsWith": "Inizia con",

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

File diff suppressed because it is too large Load Diff

View File

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

View File

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

View File

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

View File

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

View File

@@ -94,7 +94,6 @@ models/PaginatedRecipeBookList.ts
models/PaginatedRecipeImportList.ts
models/PaginatedRecipeOverviewList.ts
models/PaginatedShoppingListEntryList.ts
models/PaginatedShoppingListList.ts
models/PaginatedShoppingListRecipeList.ts
models/PaginatedSpaceList.ts
models/PaginatedStepList.ts
@@ -141,7 +140,6 @@ models/PatchedRecipeBook.ts
models/PatchedRecipeBookEntry.ts
models/PatchedRecipeImport.ts
models/PatchedSearchPreference.ts
models/PatchedShoppingList.ts
models/PatchedShoppingListEntry.ts
models/PatchedShoppingListRecipe.ts
models/PatchedSpace.ts
@@ -176,7 +174,6 @@ models/SearchFields.ts
models/SearchPreference.ts
models/ServerSettings.ts
models/ShareLink.ts
models/ShoppingList.ts
models/ShoppingListEntry.ts
models/ShoppingListEntryBulk.ts
models/ShoppingListEntryBulkCreate.ts

View File

@@ -85,7 +85,6 @@ import type {
PaginatedRecipeImportList,
PaginatedRecipeOverviewList,
PaginatedShoppingListEntryList,
PaginatedShoppingListList,
PaginatedShoppingListRecipeList,
PaginatedSpaceList,
PaginatedStepList,
@@ -132,7 +131,6 @@ import type {
PatchedRecipeBookEntry,
PatchedRecipeImport,
PatchedSearchPreference,
PatchedShoppingList,
PatchedShoppingListEntry,
PatchedShoppingListRecipe,
PatchedSpace,
@@ -165,7 +163,6 @@ import type {
SearchPreference,
ServerSettings,
ShareLink,
ShoppingList,
ShoppingListEntry,
ShoppingListEntryBulk,
ShoppingListEntryBulkCreate,
@@ -327,8 +324,6 @@ import {
PaginatedRecipeOverviewListToJSON,
PaginatedShoppingListEntryListFromJSON,
PaginatedShoppingListEntryListToJSON,
PaginatedShoppingListListFromJSON,
PaginatedShoppingListListToJSON,
PaginatedShoppingListRecipeListFromJSON,
PaginatedShoppingListRecipeListToJSON,
PaginatedSpaceListFromJSON,
@@ -421,8 +416,6 @@ import {
PatchedRecipeImportToJSON,
PatchedSearchPreferenceFromJSON,
PatchedSearchPreferenceToJSON,
PatchedShoppingListFromJSON,
PatchedShoppingListToJSON,
PatchedShoppingListEntryFromJSON,
PatchedShoppingListEntryToJSON,
PatchedShoppingListRecipeFromJSON,
@@ -487,8 +480,6 @@ import {
ServerSettingsToJSON,
ShareLinkFromJSON,
ShareLinkToJSON,
ShoppingListFromJSON,
ShoppingListToJSON,
ShoppingListEntryFromJSON,
ShoppingListEntryToJSON,
ShoppingListEntryBulkFromJSON,
@@ -1964,21 +1955,6 @@ export interface ApiShareLinkRetrieveRequest {
id: number;
}
export interface ApiShoppingListCascadingListRequest {
id: number;
cache?: boolean;
page?: number;
pageSize?: number;
}
export interface ApiShoppingListCreateRequest {
shoppingList?: ShoppingList;
}
export interface ApiShoppingListDestroyRequest {
id: number;
}
export interface ApiShoppingListEntryBulkCreateRequest {
shoppingListEntryBulk: Omit<ShoppingListEntryBulk, 'timestamp'>;
}
@@ -2012,30 +1988,6 @@ export interface ApiShoppingListEntryUpdateRequest {
shoppingListEntry: Omit<ShoppingListEntry, 'listRecipeData'|'createdBy'|'createdAt'|'updatedAt'>;
}
export interface ApiShoppingListListRequest {
page?: number;
pageSize?: number;
}
export interface ApiShoppingListNullingListRequest {
id: number;
cache?: boolean;
page?: number;
pageSize?: number;
}
export interface ApiShoppingListPartialUpdateRequest {
id: number;
patchedShoppingList?: PatchedShoppingList;
}
export interface ApiShoppingListProtectingListRequest {
id: number;
cache?: boolean;
page?: number;
pageSize?: number;
}
export interface ApiShoppingListRecipeBulkCreateEntriesCreateRequest {
id: number;
shoppingListEntryBulkCreate: ShoppingListEntryBulkCreate;
@@ -2069,15 +2021,6 @@ export interface ApiShoppingListRecipeUpdateRequest {
shoppingListRecipe: Omit<ShoppingListRecipe, 'recipeData'|'mealPlanData'|'createdBy'>;
}
export interface ApiShoppingListRetrieveRequest {
id: number;
}
export interface ApiShoppingListUpdateRequest {
id: number;
shoppingList?: ShoppingList;
}
export interface ApiSpaceCreateRequest {
space?: Omit<Space, 'createdBy'|'createdAt'|'maxRecipes'|'maxFileStorageMb'|'maxUsers'|'allowSharing'|'demo'|'userCount'|'recipeCount'|'fileSizeMb'|'aiMonthlyCreditsUsed'>;
}
@@ -14608,124 +14551,6 @@ export class ApiApi extends runtime.BaseAPI {
return await response.value();
}
/**
* get a paginated list of objects that will be cascaded (deleted) when deleting the selected object
*/
async apiShoppingListCascadingListRaw(requestParameters: ApiShoppingListCascadingListRequest, initOverrides?: RequestInit | runtime.InitOverrideFunction): Promise<runtime.ApiResponse<PaginatedGenericModelReferenceList>> {
if (requestParameters['id'] == null) {
throw new runtime.RequiredError(
'id',
'Required parameter "id" was null or undefined when calling apiShoppingListCascadingList().'
);
}
const queryParameters: any = {};
if (requestParameters['cache'] != null) {
queryParameters['cache'] = requestParameters['cache'];
}
if (requestParameters['page'] != null) {
queryParameters['page'] = requestParameters['page'];
}
if (requestParameters['pageSize'] != null) {
queryParameters['page_size'] = requestParameters['pageSize'];
}
const headerParameters: runtime.HTTPHeaders = {};
if (this.configuration && this.configuration.apiKey) {
headerParameters["Authorization"] = await this.configuration.apiKey("Authorization"); // ApiKeyAuth authentication
}
const response = await this.request({
path: `/api/shopping-list/{id}/cascading/`.replace(`{${"id"}}`, encodeURIComponent(String(requestParameters['id']))),
method: 'GET',
headers: headerParameters,
query: queryParameters,
}, initOverrides);
return new runtime.JSONApiResponse(response, (jsonValue) => PaginatedGenericModelReferenceListFromJSON(jsonValue));
}
/**
* get a paginated list of objects that will be cascaded (deleted) when deleting the selected object
*/
async apiShoppingListCascadingList(requestParameters: ApiShoppingListCascadingListRequest, initOverrides?: RequestInit | runtime.InitOverrideFunction): Promise<PaginatedGenericModelReferenceList> {
const response = await this.apiShoppingListCascadingListRaw(requestParameters, initOverrides);
return await response.value();
}
/**
* logs request counts to redis cache total/per user/
*/
async apiShoppingListCreateRaw(requestParameters: ApiShoppingListCreateRequest, initOverrides?: RequestInit | runtime.InitOverrideFunction): Promise<runtime.ApiResponse<ShoppingList>> {
const queryParameters: any = {};
const headerParameters: runtime.HTTPHeaders = {};
headerParameters['Content-Type'] = 'application/json';
if (this.configuration && this.configuration.apiKey) {
headerParameters["Authorization"] = await this.configuration.apiKey("Authorization"); // ApiKeyAuth authentication
}
const response = await this.request({
path: `/api/shopping-list/`,
method: 'POST',
headers: headerParameters,
query: queryParameters,
body: ShoppingListToJSON(requestParameters['shoppingList']),
}, initOverrides);
return new runtime.JSONApiResponse(response, (jsonValue) => ShoppingListFromJSON(jsonValue));
}
/**
* logs request counts to redis cache total/per user/
*/
async apiShoppingListCreate(requestParameters: ApiShoppingListCreateRequest = {}, initOverrides?: RequestInit | runtime.InitOverrideFunction): Promise<ShoppingList> {
const response = await this.apiShoppingListCreateRaw(requestParameters, initOverrides);
return await response.value();
}
/**
* logs request counts to redis cache total/per user/
*/
async apiShoppingListDestroyRaw(requestParameters: ApiShoppingListDestroyRequest, initOverrides?: RequestInit | runtime.InitOverrideFunction): Promise<runtime.ApiResponse<void>> {
if (requestParameters['id'] == null) {
throw new runtime.RequiredError(
'id',
'Required parameter "id" was null or undefined when calling apiShoppingListDestroy().'
);
}
const queryParameters: any = {};
const headerParameters: runtime.HTTPHeaders = {};
if (this.configuration && this.configuration.apiKey) {
headerParameters["Authorization"] = await this.configuration.apiKey("Authorization"); // ApiKeyAuth authentication
}
const response = await this.request({
path: `/api/shopping-list/{id}/`.replace(`{${"id"}}`, encodeURIComponent(String(requestParameters['id']))),
method: 'DELETE',
headers: headerParameters,
query: queryParameters,
}, initOverrides);
return new runtime.VoidApiResponse(response);
}
/**
* logs request counts to redis cache total/per user/
*/
async apiShoppingListDestroy(requestParameters: ApiShoppingListDestroyRequest, initOverrides?: RequestInit | runtime.InitOverrideFunction): Promise<void> {
await this.apiShoppingListDestroyRaw(requestParameters, initOverrides);
}
/**
* individual entries of a shopping list automatically filtered to only contain unchecked items that are not older than the shopping recent days setting to not bloat endpoint
*/
@@ -15012,182 +14837,6 @@ export class ApiApi extends runtime.BaseAPI {
return await response.value();
}
/**
* logs request counts to redis cache total/per user/
*/
async apiShoppingListListRaw(requestParameters: ApiShoppingListListRequest, initOverrides?: RequestInit | runtime.InitOverrideFunction): Promise<runtime.ApiResponse<PaginatedShoppingListList>> {
const queryParameters: any = {};
if (requestParameters['page'] != null) {
queryParameters['page'] = requestParameters['page'];
}
if (requestParameters['pageSize'] != null) {
queryParameters['page_size'] = requestParameters['pageSize'];
}
const headerParameters: runtime.HTTPHeaders = {};
if (this.configuration && this.configuration.apiKey) {
headerParameters["Authorization"] = await this.configuration.apiKey("Authorization"); // ApiKeyAuth authentication
}
const response = await this.request({
path: `/api/shopping-list/`,
method: 'GET',
headers: headerParameters,
query: queryParameters,
}, initOverrides);
return new runtime.JSONApiResponse(response, (jsonValue) => PaginatedShoppingListListFromJSON(jsonValue));
}
/**
* logs request counts to redis cache total/per user/
*/
async apiShoppingListList(requestParameters: ApiShoppingListListRequest = {}, initOverrides?: RequestInit | runtime.InitOverrideFunction): Promise<PaginatedShoppingListList> {
const response = await this.apiShoppingListListRaw(requestParameters, initOverrides);
return await response.value();
}
/**
* get a paginated list of objects where the selected object will be removed whe its deleted
*/
async apiShoppingListNullingListRaw(requestParameters: ApiShoppingListNullingListRequest, initOverrides?: RequestInit | runtime.InitOverrideFunction): Promise<runtime.ApiResponse<PaginatedGenericModelReferenceList>> {
if (requestParameters['id'] == null) {
throw new runtime.RequiredError(
'id',
'Required parameter "id" was null or undefined when calling apiShoppingListNullingList().'
);
}
const queryParameters: any = {};
if (requestParameters['cache'] != null) {
queryParameters['cache'] = requestParameters['cache'];
}
if (requestParameters['page'] != null) {
queryParameters['page'] = requestParameters['page'];
}
if (requestParameters['pageSize'] != null) {
queryParameters['page_size'] = requestParameters['pageSize'];
}
const headerParameters: runtime.HTTPHeaders = {};
if (this.configuration && this.configuration.apiKey) {
headerParameters["Authorization"] = await this.configuration.apiKey("Authorization"); // ApiKeyAuth authentication
}
const response = await this.request({
path: `/api/shopping-list/{id}/nulling/`.replace(`{${"id"}}`, encodeURIComponent(String(requestParameters['id']))),
method: 'GET',
headers: headerParameters,
query: queryParameters,
}, initOverrides);
return new runtime.JSONApiResponse(response, (jsonValue) => PaginatedGenericModelReferenceListFromJSON(jsonValue));
}
/**
* get a paginated list of objects where the selected object will be removed whe its deleted
*/
async apiShoppingListNullingList(requestParameters: ApiShoppingListNullingListRequest, initOverrides?: RequestInit | runtime.InitOverrideFunction): Promise<PaginatedGenericModelReferenceList> {
const response = await this.apiShoppingListNullingListRaw(requestParameters, initOverrides);
return await response.value();
}
/**
* logs request counts to redis cache total/per user/
*/
async apiShoppingListPartialUpdateRaw(requestParameters: ApiShoppingListPartialUpdateRequest, initOverrides?: RequestInit | runtime.InitOverrideFunction): Promise<runtime.ApiResponse<ShoppingList>> {
if (requestParameters['id'] == null) {
throw new runtime.RequiredError(
'id',
'Required parameter "id" was null or undefined when calling apiShoppingListPartialUpdate().'
);
}
const queryParameters: any = {};
const headerParameters: runtime.HTTPHeaders = {};
headerParameters['Content-Type'] = 'application/json';
if (this.configuration && this.configuration.apiKey) {
headerParameters["Authorization"] = await this.configuration.apiKey("Authorization"); // ApiKeyAuth authentication
}
const response = await this.request({
path: `/api/shopping-list/{id}/`.replace(`{${"id"}}`, encodeURIComponent(String(requestParameters['id']))),
method: 'PATCH',
headers: headerParameters,
query: queryParameters,
body: PatchedShoppingListToJSON(requestParameters['patchedShoppingList']),
}, initOverrides);
return new runtime.JSONApiResponse(response, (jsonValue) => ShoppingListFromJSON(jsonValue));
}
/**
* logs request counts to redis cache total/per user/
*/
async apiShoppingListPartialUpdate(requestParameters: ApiShoppingListPartialUpdateRequest, initOverrides?: RequestInit | runtime.InitOverrideFunction): Promise<ShoppingList> {
const response = await this.apiShoppingListPartialUpdateRaw(requestParameters, initOverrides);
return await response.value();
}
/**
* get a paginated list of objects that are protecting the selected object form being deleted
*/
async apiShoppingListProtectingListRaw(requestParameters: ApiShoppingListProtectingListRequest, initOverrides?: RequestInit | runtime.InitOverrideFunction): Promise<runtime.ApiResponse<PaginatedGenericModelReferenceList>> {
if (requestParameters['id'] == null) {
throw new runtime.RequiredError(
'id',
'Required parameter "id" was null or undefined when calling apiShoppingListProtectingList().'
);
}
const queryParameters: any = {};
if (requestParameters['cache'] != null) {
queryParameters['cache'] = requestParameters['cache'];
}
if (requestParameters['page'] != null) {
queryParameters['page'] = requestParameters['page'];
}
if (requestParameters['pageSize'] != null) {
queryParameters['page_size'] = requestParameters['pageSize'];
}
const headerParameters: runtime.HTTPHeaders = {};
if (this.configuration && this.configuration.apiKey) {
headerParameters["Authorization"] = await this.configuration.apiKey("Authorization"); // ApiKeyAuth authentication
}
const response = await this.request({
path: `/api/shopping-list/{id}/protecting/`.replace(`{${"id"}}`, encodeURIComponent(String(requestParameters['id']))),
method: 'GET',
headers: headerParameters,
query: queryParameters,
}, initOverrides);
return new runtime.JSONApiResponse(response, (jsonValue) => PaginatedGenericModelReferenceListFromJSON(jsonValue));
}
/**
* get a paginated list of objects that are protecting the selected object form being deleted
*/
async apiShoppingListProtectingList(requestParameters: ApiShoppingListProtectingListRequest, initOverrides?: RequestInit | runtime.InitOverrideFunction): Promise<PaginatedGenericModelReferenceList> {
const response = await this.apiShoppingListProtectingListRaw(requestParameters, initOverrides);
return await response.value();
}
/**
* logs request counts to redis cache total/per user/
*/
@@ -15477,83 +15126,6 @@ export class ApiApi extends runtime.BaseAPI {
return await response.value();
}
/**
* logs request counts to redis cache total/per user/
*/
async apiShoppingListRetrieveRaw(requestParameters: ApiShoppingListRetrieveRequest, initOverrides?: RequestInit | runtime.InitOverrideFunction): Promise<runtime.ApiResponse<ShoppingList>> {
if (requestParameters['id'] == null) {
throw new runtime.RequiredError(
'id',
'Required parameter "id" was null or undefined when calling apiShoppingListRetrieve().'
);
}
const queryParameters: any = {};
const headerParameters: runtime.HTTPHeaders = {};
if (this.configuration && this.configuration.apiKey) {
headerParameters["Authorization"] = await this.configuration.apiKey("Authorization"); // ApiKeyAuth authentication
}
const response = await this.request({
path: `/api/shopping-list/{id}/`.replace(`{${"id"}}`, encodeURIComponent(String(requestParameters['id']))),
method: 'GET',
headers: headerParameters,
query: queryParameters,
}, initOverrides);
return new runtime.JSONApiResponse(response, (jsonValue) => ShoppingListFromJSON(jsonValue));
}
/**
* logs request counts to redis cache total/per user/
*/
async apiShoppingListRetrieve(requestParameters: ApiShoppingListRetrieveRequest, initOverrides?: RequestInit | runtime.InitOverrideFunction): Promise<ShoppingList> {
const response = await this.apiShoppingListRetrieveRaw(requestParameters, initOverrides);
return await response.value();
}
/**
* logs request counts to redis cache total/per user/
*/
async apiShoppingListUpdateRaw(requestParameters: ApiShoppingListUpdateRequest, initOverrides?: RequestInit | runtime.InitOverrideFunction): Promise<runtime.ApiResponse<ShoppingList>> {
if (requestParameters['id'] == null) {
throw new runtime.RequiredError(
'id',
'Required parameter "id" was null or undefined when calling apiShoppingListUpdate().'
);
}
const queryParameters: any = {};
const headerParameters: runtime.HTTPHeaders = {};
headerParameters['Content-Type'] = 'application/json';
if (this.configuration && this.configuration.apiKey) {
headerParameters["Authorization"] = await this.configuration.apiKey("Authorization"); // ApiKeyAuth authentication
}
const response = await this.request({
path: `/api/shopping-list/{id}/`.replace(`{${"id"}}`, encodeURIComponent(String(requestParameters['id']))),
method: 'PUT',
headers: headerParameters,
query: queryParameters,
body: ShoppingListToJSON(requestParameters['shoppingList']),
}, initOverrides);
return new runtime.JSONApiResponse(response, (jsonValue) => ShoppingListFromJSON(jsonValue));
}
/**
* logs request counts to redis cache total/per user/
*/
async apiShoppingListUpdate(requestParameters: ApiShoppingListUpdateRequest, initOverrides?: RequestInit | runtime.InitOverrideFunction): Promise<ShoppingList> {
const response = await this.apiShoppingListUpdateRaw(requestParameters, initOverrides);
return await response.value();
}
/**
* logs request counts to redis cache total/per user/
*/

View File

@@ -13,12 +13,6 @@
*/
import { mapValues } from '../runtime';
import type { ShoppingList } from './ShoppingList';
import {
ShoppingListFromJSON,
ShoppingListFromJSONTyped,
ShoppingListToJSON,
} from './ShoppingList';
import type { SupermarketCategory } from './SupermarketCategory';
import {
SupermarketCategoryFromJSON,
@@ -241,12 +235,6 @@ export interface Food {
* @memberof Food
*/
openDataSlug?: string;
/**
*
* @type {Array<ShoppingList>}
* @memberof Food
*/
shoppingLists?: Array<ShoppingList>;
}
/**
@@ -296,7 +284,6 @@ export function FoodFromJSONTyped(json: any, ignoreDiscriminator: boolean): Food
'substituteOnhand': json['substitute_onhand'],
'childInheritFields': json['child_inherit_fields'] == null ? undefined : ((json['child_inherit_fields'] as Array<any>).map(FoodInheritFieldFromJSON)),
'openDataSlug': json['open_data_slug'] == null ? undefined : json['open_data_slug'],
'shoppingLists': json['shopping_lists'] == null ? undefined : ((json['shopping_lists'] as Array<any>).map(ShoppingListFromJSON)),
};
}
@@ -325,7 +312,6 @@ export function FoodToJSON(value?: Omit<Food, 'shopping'|'parent'|'numchild'|'fu
'substitute_children': value['substituteChildren'],
'child_inherit_fields': value['childInheritFields'] == null ? undefined : ((value['childInheritFields'] as Array<any>).map(FoodInheritFieldToJSON)),
'open_data_slug': value['openDataSlug'],
'shopping_lists': value['shoppingLists'] == null ? undefined : ((value['shoppingLists'] as Array<any>).map(ShoppingListToJSON)),
};
}

View File

@@ -1,101 +0,0 @@
/* tslint:disable */
/* eslint-disable */
/**
* Tandoor
* Tandoor API Docs
*
* The version of the OpenAPI document: 0.0.0
*
*
* NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech).
* https://openapi-generator.tech
* Do not edit the class manually.
*/
import { mapValues } from '../runtime';
import type { ShoppingList } from './ShoppingList';
import {
ShoppingListFromJSON,
ShoppingListFromJSONTyped,
ShoppingListToJSON,
} from './ShoppingList';
/**
*
* @export
* @interface PaginatedShoppingListList
*/
export interface PaginatedShoppingListList {
/**
*
* @type {number}
* @memberof PaginatedShoppingListList
*/
count: number;
/**
*
* @type {string}
* @memberof PaginatedShoppingListList
*/
next?: string;
/**
*
* @type {string}
* @memberof PaginatedShoppingListList
*/
previous?: string;
/**
*
* @type {Array<ShoppingList>}
* @memberof PaginatedShoppingListList
*/
results: Array<ShoppingList>;
/**
*
* @type {Date}
* @memberof PaginatedShoppingListList
*/
timestamp?: Date;
}
/**
* Check if a given object implements the PaginatedShoppingListList interface.
*/
export function instanceOfPaginatedShoppingListList(value: object): value is PaginatedShoppingListList {
if (!('count' in value) || value['count'] === undefined) return false;
if (!('results' in value) || value['results'] === undefined) return false;
return true;
}
export function PaginatedShoppingListListFromJSON(json: any): PaginatedShoppingListList {
return PaginatedShoppingListListFromJSONTyped(json, false);
}
export function PaginatedShoppingListListFromJSONTyped(json: any, ignoreDiscriminator: boolean): PaginatedShoppingListList {
if (json == null) {
return json;
}
return {
'count': json['count'],
'next': json['next'] == null ? undefined : json['next'],
'previous': json['previous'] == null ? undefined : json['previous'],
'results': ((json['results'] as Array<any>).map(ShoppingListFromJSON)),
'timestamp': json['timestamp'] == null ? undefined : (new Date(json['timestamp'])),
};
}
export function PaginatedShoppingListListToJSON(value?: PaginatedShoppingListList | null): any {
if (value == null) {
return value;
}
return {
'count': value['count'],
'next': value['next'],
'previous': value['previous'],
'results': ((value['results'] as Array<any>).map(ShoppingListToJSON)),
'timestamp': value['timestamp'] == null ? undefined : ((value['timestamp']).toISOString()),
};
}

View File

@@ -13,12 +13,6 @@
*/
import { mapValues } from '../runtime';
import type { ShoppingList } from './ShoppingList';
import {
ShoppingListFromJSON,
ShoppingListFromJSONTyped,
ShoppingListToJSON,
} from './ShoppingList';
import type { SupermarketCategory } from './SupermarketCategory';
import {
SupermarketCategoryFromJSON,
@@ -241,12 +235,6 @@ export interface PatchedFood {
* @memberof PatchedFood
*/
openDataSlug?: string;
/**
*
* @type {Array<ShoppingList>}
* @memberof PatchedFood
*/
shoppingLists?: Array<ShoppingList>;
}
/**
@@ -290,7 +278,6 @@ export function PatchedFoodFromJSONTyped(json: any, ignoreDiscriminator: boolean
'substituteOnhand': json['substitute_onhand'] == null ? undefined : json['substitute_onhand'],
'childInheritFields': json['child_inherit_fields'] == null ? undefined : ((json['child_inherit_fields'] as Array<any>).map(FoodInheritFieldFromJSON)),
'openDataSlug': json['open_data_slug'] == null ? undefined : json['open_data_slug'],
'shoppingLists': json['shopping_lists'] == null ? undefined : ((json['shopping_lists'] as Array<any>).map(ShoppingListFromJSON)),
};
}
@@ -319,7 +306,6 @@ export function PatchedFoodToJSON(value?: Omit<PatchedFood, 'shopping'|'parent'|
'substitute_children': value['substituteChildren'],
'child_inherit_fields': value['childInheritFields'] == null ? undefined : ((value['childInheritFields'] as Array<any>).map(FoodInheritFieldToJSON)),
'open_data_slug': value['openDataSlug'],
'shopping_lists': value['shoppingLists'] == null ? undefined : ((value['shoppingLists'] as Array<any>).map(ShoppingListToJSON)),
};
}

View File

@@ -1,84 +0,0 @@
/* tslint:disable */
/* eslint-disable */
/**
* Tandoor
* Tandoor API Docs
*
* The version of the OpenAPI document: 0.0.0
*
*
* NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech).
* https://openapi-generator.tech
* Do not edit the class manually.
*/
import { mapValues } from '../runtime';
/**
* Adds nested create feature
* @export
* @interface PatchedShoppingList
*/
export interface PatchedShoppingList {
/**
*
* @type {number}
* @memberof PatchedShoppingList
*/
id?: number;
/**
*
* @type {string}
* @memberof PatchedShoppingList
*/
name?: string;
/**
*
* @type {string}
* @memberof PatchedShoppingList
*/
description?: string;
/**
*
* @type {string}
* @memberof PatchedShoppingList
*/
color?: string;
}
/**
* Check if a given object implements the PatchedShoppingList interface.
*/
export function instanceOfPatchedShoppingList(value: object): value is PatchedShoppingList {
return true;
}
export function PatchedShoppingListFromJSON(json: any): PatchedShoppingList {
return PatchedShoppingListFromJSONTyped(json, false);
}
export function PatchedShoppingListFromJSONTyped(json: any, ignoreDiscriminator: boolean): PatchedShoppingList {
if (json == null) {
return json;
}
return {
'id': json['id'] == null ? undefined : json['id'],
'name': json['name'] == null ? undefined : json['name'],
'description': json['description'] == null ? undefined : json['description'],
'color': json['color'] == null ? undefined : json['color'],
};
}
export function PatchedShoppingListToJSON(value?: PatchedShoppingList | null): any {
if (value == null) {
return value;
}
return {
'id': value['id'],
'name': value['name'],
'description': value['description'],
'color': value['color'],
};
}

View File

@@ -19,12 +19,6 @@ import {
UserFromJSONTyped,
UserToJSON,
} from './User';
import type { ShoppingList } from './ShoppingList';
import {
ShoppingListFromJSON,
ShoppingListFromJSONTyped,
ShoppingListToJSON,
} from './ShoppingList';
import type { ShoppingListRecipe } from './ShoppingListRecipe';
import {
ShoppingListRecipeFromJSON,
@@ -37,12 +31,12 @@ import {
UnitFromJSONTyped,
UnitToJSON,
} from './Unit';
import type { FoodSimple } from './FoodSimple';
import type { Food } from './Food';
import {
FoodSimpleFromJSON,
FoodSimpleFromJSONTyped,
FoodSimpleToJSON,
} from './FoodSimple';
FoodFromJSON,
FoodFromJSONTyped,
FoodToJSON,
} from './Food';
/**
* Adds nested create feature
@@ -64,16 +58,10 @@ export interface PatchedShoppingListEntry {
listRecipe?: number;
/**
*
* @type {Array<ShoppingList>}
* @type {Food}
* @memberof PatchedShoppingListEntry
*/
shoppingLists?: Array<ShoppingList>;
/**
*
* @type {FoodSimple}
* @memberof PatchedShoppingListEntry
*/
food?: FoodSimple;
food?: Food;
/**
*
* @type {Unit}
@@ -167,8 +155,7 @@ export function PatchedShoppingListEntryFromJSONTyped(json: any, ignoreDiscrimin
'id': json['id'] == null ? undefined : json['id'],
'listRecipe': json['list_recipe'] == null ? undefined : json['list_recipe'],
'shoppingLists': json['shopping_lists'] == null ? undefined : ((json['shopping_lists'] as Array<any>).map(ShoppingListFromJSON)),
'food': json['food'] == null ? undefined : FoodSimpleFromJSON(json['food']),
'food': json['food'] == null ? undefined : FoodFromJSON(json['food']),
'unit': json['unit'] == null ? undefined : UnitFromJSON(json['unit']),
'amount': json['amount'] == null ? undefined : json['amount'],
'order': json['order'] == null ? undefined : json['order'],
@@ -192,8 +179,7 @@ export function PatchedShoppingListEntryToJSON(value?: Omit<PatchedShoppingListE
'id': value['id'],
'list_recipe': value['listRecipe'],
'shopping_lists': value['shoppingLists'] == null ? undefined : ((value['shoppingLists'] as Array<any>).map(ShoppingListToJSON)),
'food': FoodSimpleToJSON(value['food']),
'food': FoodToJSON(value['food']),
'unit': UnitToJSON(value['unit']),
'amount': value['amount'],
'order': value['order'],

View File

@@ -62,18 +62,18 @@ export interface PatchedShoppingListRecipe {
* @memberof PatchedShoppingListRecipe
*/
readonly recipeData?: RecipeOverview;
/**
*
* @type {MealPlan}
* @memberof PatchedShoppingListRecipe
*/
readonly mealPlanData?: MealPlan;
/**
*
* @type {number}
* @memberof PatchedShoppingListRecipe
*/
mealplan?: number;
/**
*
* @type {MealPlan}
* @memberof PatchedShoppingListRecipe
*/
readonly mealPlanData?: MealPlan;
/**
*
* @type {number}
@@ -109,8 +109,8 @@ export function PatchedShoppingListRecipeFromJSONTyped(json: any, ignoreDiscrimi
'name': json['name'] == null ? undefined : json['name'],
'recipe': json['recipe'] == null ? undefined : json['recipe'],
'recipeData': json['recipe_data'] == null ? undefined : RecipeOverviewFromJSON(json['recipe_data']),
'mealPlanData': json['meal_plan_data'] == null ? undefined : MealPlanFromJSON(json['meal_plan_data']),
'mealplan': json['mealplan'] == null ? undefined : json['mealplan'],
'mealPlanData': json['meal_plan_data'] == null ? undefined : MealPlanFromJSON(json['meal_plan_data']),
'servings': json['servings'] == null ? undefined : json['servings'],
'createdBy': json['created_by'] == null ? undefined : UserFromJSON(json['created_by']),
};

View File

@@ -19,12 +19,6 @@ import {
SupermarketCategoryRelationFromJSONTyped,
SupermarketCategoryRelationToJSON,
} from './SupermarketCategoryRelation';
import type { ShoppingList } from './ShoppingList';
import {
ShoppingListFromJSON,
ShoppingListFromJSONTyped,
ShoppingListToJSON,
} from './ShoppingList';
/**
* Moves `UniqueValidator`'s from the validation stage to the save stage.
@@ -84,12 +78,6 @@ export interface PatchedSupermarket {
* @memberof PatchedSupermarket
*/
description?: string;
/**
*
* @type {Array<ShoppingList>}
* @memberof PatchedSupermarket
*/
shoppingLists?: Array<ShoppingList>;
/**
*
* @type {Array<SupermarketCategoryRelation>}
@@ -124,7 +112,6 @@ export function PatchedSupermarketFromJSONTyped(json: any, ignoreDiscriminator:
'id': json['id'] == null ? undefined : json['id'],
'name': json['name'] == null ? undefined : json['name'],
'description': json['description'] == null ? undefined : json['description'],
'shoppingLists': json['shopping_lists'] == null ? undefined : ((json['shopping_lists'] as Array<any>).map(ShoppingListFromJSON)),
'categoryToSupermarket': json['category_to_supermarket'] == null ? undefined : ((json['category_to_supermarket'] as Array<any>).map(SupermarketCategoryRelationFromJSON)),
'openDataSlug': json['open_data_slug'] == null ? undefined : json['open_data_slug'],
};
@@ -139,7 +126,6 @@ export function PatchedSupermarketToJSON(value?: Omit<PatchedSupermarket, 'categ
'id': value['id'],
'name': value['name'],
'description': value['description'],
'shopping_lists': value['shoppingLists'] == null ? undefined : ((value['shoppingLists'] as Array<any>).map(ShoppingListToJSON)),
'open_data_slug': value['openDataSlug'],
};
}

View File

@@ -200,12 +200,6 @@ export interface PatchedUserPreference {
* @memberof PatchedUserPreference
*/
csvPrefix?: string;
/**
*
* @type {boolean}
* @memberof PatchedUserPreference
*/
shoppingUpdateFoodLists?: boolean;
/**
*
* @type {boolean}
@@ -279,7 +273,6 @@ export function PatchedUserPreferenceFromJSONTyped(json: any, ignoreDiscriminato
'shoppingRecentDays': json['shopping_recent_days'] == null ? undefined : json['shopping_recent_days'],
'csvDelim': json['csv_delim'] == null ? undefined : json['csv_delim'],
'csvPrefix': json['csv_prefix'] == null ? undefined : json['csv_prefix'],
'shoppingUpdateFoodLists': json['shopping_update_food_lists'] == null ? undefined : json['shopping_update_food_lists'],
'filterToSupermarket': json['filter_to_supermarket'] == null ? undefined : json['filter_to_supermarket'],
'shoppingAddOnhand': json['shopping_add_onhand'] == null ? undefined : json['shopping_add_onhand'],
'leftHanded': json['left_handed'] == null ? undefined : json['left_handed'],
@@ -316,7 +309,6 @@ export function PatchedUserPreferenceToJSON(value?: Omit<PatchedUserPreference,
'shopping_recent_days': value['shoppingRecentDays'],
'csv_delim': value['csvDelim'],
'csv_prefix': value['csvPrefix'],
'shopping_update_food_lists': value['shoppingUpdateFoodLists'],
'filter_to_supermarket': value['filterToSupermarket'],
'shopping_add_onhand': value['shoppingAddOnhand'],
'left_handed': value['leftHanded'],

View File

@@ -1,84 +0,0 @@
/* tslint:disable */
/* eslint-disable */
/**
* Tandoor
* Tandoor API Docs
*
* The version of the OpenAPI document: 0.0.0
*
*
* NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech).
* https://openapi-generator.tech
* Do not edit the class manually.
*/
import { mapValues } from '../runtime';
/**
* Adds nested create feature
* @export
* @interface ShoppingList
*/
export interface ShoppingList {
/**
*
* @type {number}
* @memberof ShoppingList
*/
id?: number;
/**
*
* @type {string}
* @memberof ShoppingList
*/
name?: string;
/**
*
* @type {string}
* @memberof ShoppingList
*/
description?: string;
/**
*
* @type {string}
* @memberof ShoppingList
*/
color?: string;
}
/**
* Check if a given object implements the ShoppingList interface.
*/
export function instanceOfShoppingList(value: object): value is ShoppingList {
return true;
}
export function ShoppingListFromJSON(json: any): ShoppingList {
return ShoppingListFromJSONTyped(json, false);
}
export function ShoppingListFromJSONTyped(json: any, ignoreDiscriminator: boolean): ShoppingList {
if (json == null) {
return json;
}
return {
'id': json['id'] == null ? undefined : json['id'],
'name': json['name'] == null ? undefined : json['name'],
'description': json['description'] == null ? undefined : json['description'],
'color': json['color'] == null ? undefined : json['color'],
};
}
export function ShoppingListToJSON(value?: ShoppingList | null): any {
if (value == null) {
return value;
}
return {
'id': value['id'],
'name': value['name'],
'description': value['description'],
'color': value['color'],
};
}

View File

@@ -19,12 +19,6 @@ import {
UserFromJSONTyped,
UserToJSON,
} from './User';
import type { ShoppingList } from './ShoppingList';
import {
ShoppingListFromJSON,
ShoppingListFromJSONTyped,
ShoppingListToJSON,
} from './ShoppingList';
import type { ShoppingListRecipe } from './ShoppingListRecipe';
import {
ShoppingListRecipeFromJSON,
@@ -37,12 +31,12 @@ import {
UnitFromJSONTyped,
UnitToJSON,
} from './Unit';
import type { FoodSimple } from './FoodSimple';
import type { Food } from './Food';
import {
FoodSimpleFromJSON,
FoodSimpleFromJSONTyped,
FoodSimpleToJSON,
} from './FoodSimple';
FoodFromJSON,
FoodFromJSONTyped,
FoodToJSON,
} from './Food';
/**
* Adds nested create feature
@@ -64,16 +58,10 @@ export interface ShoppingListEntry {
listRecipe?: number;
/**
*
* @type {Array<ShoppingList>}
* @type {Food}
* @memberof ShoppingListEntry
*/
shoppingLists?: Array<ShoppingList>;
/**
*
* @type {FoodSimple}
* @memberof ShoppingListEntry
*/
food: FoodSimple | null;
food: Food | null;
/**
*
* @type {Unit}
@@ -173,8 +161,7 @@ export function ShoppingListEntryFromJSONTyped(json: any, ignoreDiscriminator: b
'id': json['id'] == null ? undefined : json['id'],
'listRecipe': json['list_recipe'] == null ? undefined : json['list_recipe'],
'shoppingLists': json['shopping_lists'] == null ? undefined : ((json['shopping_lists'] as Array<any>).map(ShoppingListFromJSON)),
'food': FoodSimpleFromJSON(json['food']),
'food': FoodFromJSON(json['food']),
'unit': json['unit'] == null ? undefined : UnitFromJSON(json['unit']),
'amount': json['amount'],
'order': json['order'] == null ? undefined : json['order'],
@@ -198,8 +185,7 @@ export function ShoppingListEntryToJSON(value?: Omit<ShoppingListEntry, 'listRec
'id': value['id'],
'list_recipe': value['listRecipe'],
'shopping_lists': value['shoppingLists'] == null ? undefined : ((value['shoppingLists'] as Array<any>).map(ShoppingListToJSON)),
'food': FoodSimpleToJSON(value['food']),
'food': FoodToJSON(value['food']),
'unit': UnitToJSON(value['unit']),
'amount': value['amount'],
'order': value['order'],

View File

@@ -62,18 +62,18 @@ export interface ShoppingListRecipe {
* @memberof ShoppingListRecipe
*/
readonly recipeData: RecipeOverview;
/**
*
* @type {MealPlan}
* @memberof ShoppingListRecipe
*/
readonly mealPlanData: MealPlan;
/**
*
* @type {number}
* @memberof ShoppingListRecipe
*/
mealplan?: number;
/**
*
* @type {MealPlan}
* @memberof ShoppingListRecipe
*/
readonly mealPlanData: MealPlan;
/**
*
* @type {number}
@@ -113,8 +113,8 @@ export function ShoppingListRecipeFromJSONTyped(json: any, ignoreDiscriminator:
'name': json['name'] == null ? undefined : json['name'],
'recipe': json['recipe'] == null ? undefined : json['recipe'],
'recipeData': RecipeOverviewFromJSON(json['recipe_data']),
'mealPlanData': MealPlanFromJSON(json['meal_plan_data']),
'mealplan': json['mealplan'] == null ? undefined : json['mealplan'],
'mealPlanData': MealPlanFromJSON(json['meal_plan_data']),
'servings': json['servings'],
'createdBy': UserFromJSON(json['created_by']),
};

View File

@@ -19,12 +19,6 @@ import {
SupermarketCategoryRelationFromJSONTyped,
SupermarketCategoryRelationToJSON,
} from './SupermarketCategoryRelation';
import type { ShoppingList } from './ShoppingList';
import {
ShoppingListFromJSON,
ShoppingListFromJSONTyped,
ShoppingListToJSON,
} from './ShoppingList';
/**
* Moves `UniqueValidator`'s from the validation stage to the save stage.
@@ -84,12 +78,6 @@ export interface Supermarket {
* @memberof Supermarket
*/
description?: string;
/**
*
* @type {Array<ShoppingList>}
* @memberof Supermarket
*/
shoppingLists?: Array<ShoppingList>;
/**
*
* @type {Array<SupermarketCategoryRelation>}
@@ -126,7 +114,6 @@ export function SupermarketFromJSONTyped(json: any, ignoreDiscriminator: boolean
'id': json['id'] == null ? undefined : json['id'],
'name': json['name'],
'description': json['description'] == null ? undefined : json['description'],
'shoppingLists': json['shopping_lists'] == null ? undefined : ((json['shopping_lists'] as Array<any>).map(ShoppingListFromJSON)),
'categoryToSupermarket': ((json['category_to_supermarket'] as Array<any>).map(SupermarketCategoryRelationFromJSON)),
'openDataSlug': json['open_data_slug'] == null ? undefined : json['open_data_slug'],
};
@@ -141,7 +128,6 @@ export function SupermarketToJSON(value?: Omit<Supermarket, 'categoryToSupermark
'id': value['id'],
'name': value['name'],
'description': value['description'],
'shopping_lists': value['shoppingLists'] == null ? undefined : ((value['shoppingLists'] as Array<any>).map(ShoppingListToJSON)),
'open_data_slug': value['openDataSlug'],
};
}

View File

@@ -200,12 +200,6 @@ export interface UserPreference {
* @memberof UserPreference
*/
csvPrefix?: string;
/**
*
* @type {boolean}
* @memberof UserPreference
*/
shoppingUpdateFoodLists?: boolean;
/**
*
* @type {boolean}
@@ -282,7 +276,6 @@ export function UserPreferenceFromJSONTyped(json: any, ignoreDiscriminator: bool
'shoppingRecentDays': json['shopping_recent_days'] == null ? undefined : json['shopping_recent_days'],
'csvDelim': json['csv_delim'] == null ? undefined : json['csv_delim'],
'csvPrefix': json['csv_prefix'] == null ? undefined : json['csv_prefix'],
'shoppingUpdateFoodLists': json['shopping_update_food_lists'] == null ? undefined : json['shopping_update_food_lists'],
'filterToSupermarket': json['filter_to_supermarket'] == null ? undefined : json['filter_to_supermarket'],
'shoppingAddOnhand': json['shopping_add_onhand'] == null ? undefined : json['shopping_add_onhand'],
'leftHanded': json['left_handed'] == null ? undefined : json['left_handed'],
@@ -319,7 +312,6 @@ export function UserPreferenceToJSON(value?: Omit<UserPreference, 'user'|'foodIn
'shopping_recent_days': value['shoppingRecentDays'],
'csv_delim': value['csvDelim'],
'csv_prefix': value['csvPrefix'],
'shopping_update_food_lists': value['shoppingUpdateFoodLists'],
'filter_to_supermarket': value['filterToSupermarket'],
'shopping_add_onhand': value['shoppingAddOnhand'],
'left_handed': value['leftHanded'],

View File

@@ -92,7 +92,6 @@ export * from './PaginatedRecipeBookList';
export * from './PaginatedRecipeImportList';
export * from './PaginatedRecipeOverviewList';
export * from './PaginatedShoppingListEntryList';
export * from './PaginatedShoppingListList';
export * from './PaginatedShoppingListRecipeList';
export * from './PaginatedSpaceList';
export * from './PaginatedStepList';
@@ -139,7 +138,6 @@ export * from './PatchedRecipeBook';
export * from './PatchedRecipeBookEntry';
export * from './PatchedRecipeImport';
export * from './PatchedSearchPreference';
export * from './PatchedShoppingList';
export * from './PatchedShoppingListEntry';
export * from './PatchedShoppingListRecipe';
export * from './PatchedSpace';
@@ -174,7 +172,6 @@ export * from './SearchFields';
export * from './SearchPreference';
export * from './ServerSettings';
export * from './ShareLink';
export * from './ShoppingList';
export * from './ShoppingListEntry';
export * from './ShoppingListEntryBulk';
export * from './ShoppingListEntryBulkCreate';

View File

@@ -31,7 +31,6 @@
</v-row>
<v-row dense>
<database-model-col model="Supermarket"></database-model-col>
<database-model-col model="ShoppingList"></database-model-col>
<database-model-col model="SupermarketCategory"></database-model-col>
<database-model-col model="MealType"></database-model-col>
</v-row>

View File

@@ -95,9 +95,6 @@
<v-chip label v-if="item.id == useUserPreferenceStore().activeSpace.id!" color="success">{{ $t('Active') }}</v-chip>
<v-chip label v-else color="info" @click="useUserPreferenceStore().switchSpace(item)">{{ $t('Select') }}</v-chip>
</template>
<template v-slot:item.color="{ item }">
<v-chip label :color="item.color">{{ item.color }}</v-chip>
</template>
<template v-slot:item.action="{ item }">
<v-btn class="float-right" icon="$menu" variant="plain">
<v-icon icon="$menu"></v-icon>

View File

@@ -1,6 +1,6 @@
import {acceptHMRUpdate, defineStore} from "pinia"
import {ApiApi, ApiShoppingListEntryListRequest, Food, Recipe, ShoppingListEntry, ShoppingListEntryBulk, ShoppingListRecipe, Supermarket, SupermarketCategory} from "@/openapi";
import {computed, ref, shallowRef, triggerRef} from "vue";
import {computed, ref} from "vue";
import {
IShoppingExportEntry,
IShoppingList,
@@ -25,6 +25,14 @@ export const useShoppingStore = defineStore(_STORE_ID, () => {
let supermarketCategories = ref([] as SupermarketCategory[])
let supermarkets = ref([] as Supermarket[])
let stats = ref({
countChecked: 0,
countUnchecked: 0,
countCheckedFood: 0,
countUncheckedFood: 0,
countUncheckedDelayed: 0,
} as ShoppingListStats)
// internal
let currentlyUpdating = ref(false)
let initialized = ref(false)
@@ -33,25 +41,28 @@ export const useShoppingStore = defineStore(_STORE_ID, () => {
let autoSyncHasFocus = ref(true)
let autoSyncTimeoutId = ref(0)
let undoStack = shallowRef([] as ShoppingOperationHistoryEntry[])
let undoStack = ref([] as ShoppingOperationHistoryEntry[])
let queueTimeoutId = ref(-1)
let itemCheckSyncQueue = shallowRef([] as IShoppingSyncQueueEntry[])
let syncQueueRunning = ref(false)
let entriesByGroup = shallowRef([] as IShoppingListCategory[])
let itemCheckSyncQueue = ref([] as IShoppingSyncQueueEntry[])
/**
* build a multi-level data structure ready for display from shopping list entries
* group by selected grouping key
*/
function updateEntriesStructure() {
const getEntriesByGroup = computed(() => {
stats.value = {
countChecked: 0,
countUnchecked: 0,
countCheckedFood: 0,
countUncheckedFood: 0,
countUncheckedDelayed: 0,
} as ShoppingListStats
let structure = {} as IShoppingList
structure.categories = new Map<string, IShoppingListCategory>
const deviceSettings = useUserPreferenceStore().deviceSettings
if (deviceSettings.shopping_selected_grouping === ShoppingGroupingOptions.CATEGORY && deviceSettings.shopping_selected_supermarket != null) {
deviceSettings.shopping_selected_supermarket.categoryToSupermarket.forEach(cTS => {
if (useUserPreferenceStore().deviceSettings.shopping_selected_grouping === ShoppingGroupingOptions.CATEGORY && useUserPreferenceStore().deviceSettings.shopping_selected_supermarket != null) {
useUserPreferenceStore().deviceSettings.shopping_selected_supermarket.categoryToSupermarket.forEach(cTS => {
structure.categories.set(cTS.category.name, {'name': cTS.category.name, 'foods': new Map<number, IShoppingListFood>} as IShoppingListCategory)
})
}
@@ -60,41 +71,60 @@ export const useShoppingStore = defineStore(_STORE_ID, () => {
// build structure
entries.value.forEach(shoppingListEntry => {
if (isEntryVisible(shoppingListEntry, deviceSettings)) {
structure = updateEntryInStructure(structure, shoppingListEntry)
}
structure = updateEntryInStructure(structure, shoppingListEntry)
})
// statistics for UI conditions and display
structure.categories.forEach(category => {
let categoryStats = {
countChecked: 0,
countUnchecked: 0,
countCheckedFood: 0,
countUncheckedFood: 0,
countUncheckedDelayed: 0,
} as ShoppingListStats
category.foods.forEach(food => {
let food_checked = true
food.entries.forEach(entry => {
if (entry.checked) {
categoryStats.countChecked++
} else {
if (isDelayed(entry)) {
categoryStats.countUncheckedDelayed++
} else {
categoryStats.countUnchecked++
}
}
})
if (food_checked) {
categoryStats.countCheckedFood++
} else {
categoryStats.countUncheckedFood++
}
})
category.stats = categoryStats
stats.value.countChecked += categoryStats.countChecked
stats.value.countUnchecked += categoryStats.countUnchecked
stats.value.countCheckedFood += categoryStats.countCheckedFood
stats.value.countUncheckedFood += categoryStats.countUncheckedFood
})
// ordering
let undefinedCategoryGroup = structure.categories.get(UNDEFINED_CATEGORY)
if (undefinedCategoryGroup != null) {
totalFoods.value += undefinedCategoryGroup.foods.size
orderedStructure.push(undefinedCategoryGroup)
structure.categories.delete(UNDEFINED_CATEGORY)
}
structure.categories.forEach(category => {
if (category.foods.size > 0) {
orderedStructure.push(category)
}
orderedStructure.push(category)
})
entriesByGroup.value = orderedStructure
}
/**
* get the total number of foods in the shopping list
* since entries are always grouped by food, it makes no sense to display the entry count anywhere
*/
let totalFoods = computed(() => {
let count = 0
if (initialized.value) {
entriesByGroup.value.forEach(category => {
count += category.foods.size
})
}
return count
return orderedStructure
})
/**
@@ -105,7 +135,7 @@ export const useShoppingStore = defineStore(_STORE_ID, () => {
function getFlatEntries() {
let items: IShoppingExportEntry[] = []
entriesByGroup.value.forEach(shoppingListEntry => {
getEntriesByGroup.value.forEach(shoppingListEntry => {
shoppingListEntry.foods.forEach(food => {
food.entries.forEach(entry => {
items.push({
@@ -145,7 +175,7 @@ export const useShoppingStore = defineStore(_STORE_ID, () => {
function hasFailedItems() {
for (let i in itemCheckSyncQueue.value) {
if (itemCheckSyncQueue.value[i]['status'] === 'syncing_failed_before' || itemCheckSyncQueue.value[i]['status'] === 'waiting_failed_before') {
return !syncQueueRunning.value
return true
}
}
return false
@@ -167,7 +197,6 @@ export const useShoppingStore = defineStore(_STORE_ID, () => {
} else {
// only clear local entries when not given a meal plan to not accidentally filter the shopping list
entries.value = new Map<number, ShoppingListEntry>
initialized.value = false
}
recLoadShoppingListEntries(requestParameters)
@@ -192,30 +221,17 @@ export const useShoppingStore = defineStore(_STORE_ID, () => {
*/
function recLoadShoppingListEntries(requestParameters: ApiShoppingListEntryListRequest) {
let api = new ApiApi()
return api.apiShoppingListEntryList(requestParameters).then((r) => {
let promises = [] as Promise<any>[]
let newMap = new Map<number, ShoppingListEntry>()
api.apiShoppingListEntryList(requestParameters).then((r) => {
r.results.forEach((e) => {
newMap.set(e.id!, e)
entries.value.set(e.id!, e)
})
// bulk assign to avoid unnecessary reactivity updates
entries.value = new Map([...entries.value, ...newMap])
if (requestParameters.page == 1) {
if (r.next) {
while (Math.ceil(r.count / requestParameters.pageSize) > requestParameters.page) {
requestParameters.page = requestParameters.page + 1
promises.push(recLoadShoppingListEntries(requestParameters))
}
}
Promise.allSettled(promises).then(() => {
updateEntriesStructure()
currentlyUpdating.value = false
initialized.value = true
})
if (r.next) {
requestParameters.page = requestParameters.page + 1
recLoadShoppingListEntries(requestParameters)
} else {
currentlyUpdating.value = false
initialized.value = true
}
}).catch((err) => {
currentlyUpdating.value = false
useMessageStore().addError(ErrorMessageType.FETCH_ERROR, err)
@@ -251,7 +267,6 @@ export const useShoppingStore = defineStore(_STORE_ID, () => {
const api = new ApiApi()
return api.apiShoppingListEntryCreate({shoppingListEntry: object}).then((r) => {
entries.value.set(r.id!, r)
updateEntriesStructure()
if (undo) {
registerChange("CREATE", [r])
}
@@ -284,7 +299,6 @@ export const useShoppingStore = defineStore(_STORE_ID, () => {
})
}
/**
* delete shopping list entry object from DB and store
* @param object entry object to delete
@@ -294,19 +308,6 @@ export const useShoppingStore = defineStore(_STORE_ID, () => {
const api = new ApiApi()
return api.apiShoppingListEntryDestroy({id: object.id!}).then((r) => {
entries.value.delete(object.id!)
let categoryName = getEntryCategoryKey(object)
entriesByGroup.value.forEach(category => {
if (category.name == categoryName) {
category.foods.get(object.food!.id!)?.entries.delete(object.id!)
if(category.foods.get(object.food!.id!)?.entries.size == 0) {
category.foods.delete(object.food!.id!)
triggerRef(entriesByGroup)
}
}
})
if (undo) {
registerChange("DESTROY", [object])
}
@@ -330,32 +331,7 @@ export const useShoppingStore = defineStore(_STORE_ID, () => {
return recipes
}
/**
* get the key (name) of the IShoppingListCategory that an entry belongs to
* @param object
*/
function getEntryCategoryKey(object: ShoppingListEntry) {
let group = useUserPreferenceStore().deviceSettings.shopping_selected_grouping
let groupingKey = UNDEFINED_CATEGORY
if (group == ShoppingGroupingOptions.CATEGORY && object.food != null && object.food.supermarketCategory != null) {
groupingKey = object.food?.supermarketCategory?.name
} else if (group == ShoppingGroupingOptions.CREATED_BY) {
groupingKey = object.createdBy.displayName
} else if (group == ShoppingGroupingOptions.RECIPE && object.listRecipeData != null) {
if (object.listRecipeData.recipeData != null) {
groupingKey = object.listRecipeData.recipeData.name
if (object.listRecipeData.mealPlanData != null) {
groupingKey += ' - ' + object.listRecipeData.mealPlanData.mealType.name + ' - ' + DateTime.fromJSDate(object.listRecipeData.mealPlanData.fromDate).toLocaleString(DateTime.DATE_SHORT)
}
}
}
return groupingKey
}
// convenience methods
/**
* puts an entry into the appropriate group of the IShoppingList datastructure
* if a group does not yet exist and the sorting is not set to category with selected supermarket only, it will be created
@@ -363,8 +339,23 @@ export const useShoppingStore = defineStore(_STORE_ID, () => {
* @param entry
*/
function updateEntryInStructure(structure: IShoppingList, entry: ShoppingListEntry) {
let groupingKey = UNDEFINED_CATEGORY
let group = useUserPreferenceStore().deviceSettings.shopping_selected_grouping
let groupingKey = getEntryCategoryKey(entry)
if (group == ShoppingGroupingOptions.CATEGORY && entry.food != null && entry.food.supermarketCategory != null) {
groupingKey = entry.food?.supermarketCategory?.name
} else if (group == ShoppingGroupingOptions.CREATED_BY) {
groupingKey = entry.createdBy.displayName
} else if (group == ShoppingGroupingOptions.RECIPE && entry.listRecipeData != null) {
if (entry.listRecipeData.recipeData != null) {
groupingKey = entry.listRecipeData.recipeData.name
if (entry.listRecipeData.mealPlanData != null) {
groupingKey += ' - ' + entry.listRecipeData.mealPlanData.mealType.name + ' - ' + DateTime.fromJSDate(entry.listRecipeData.mealPlanData.fromDate).toLocaleString(DateTime.DATE_SHORT)
}
}
}
if (!structure.categories.has(groupingKey) && !(group == ShoppingGroupingOptions.CATEGORY && useUserPreferenceStore().deviceSettings.shopping_show_selected_supermarket_only)) {
structure.categories.set(groupingKey, {'name': groupingKey, 'foods': new Map<number, IShoppingListFood>} as IShoppingListCategory)
@@ -392,18 +383,19 @@ export const useShoppingStore = defineStore(_STORE_ID, () => {
if (undo) {
registerChange((checked ? 'CHECKED' : 'UNCHECKED'), entries)
}
let entryIdList: number[] = []
entries.forEach(entry => {
entry.checked = checked
entryIdList.push(entry.id!)
})
itemCheckSyncQueue.value.push({
ids: entryIdList,
checked: checked,
status: 'waiting',
} as IShoppingSyncQueueEntry)
runSyncQueue(100)
runSyncQueue(5)
}
/**
@@ -417,17 +409,15 @@ export const useShoppingStore = defineStore(_STORE_ID, () => {
let api = new ApiApi()
let promises: Promise<void>[] = []
let updatedEntries = new Map<number, ShoppingListEntry>()
itemCheckSyncQueue.value.forEach((entry, index) => {
entry['status'] = ((entry['status'] === 'waiting_failed_before') ? 'syncing_failed_before' : 'syncing')
syncQueueRunning.value = true
entry['status'] = ((entry['status'] === 'waiting') ? 'syncing' : 'syncing_failed_before')
let p = api.apiShoppingListEntryBulkCreate({shoppingListEntryBulk: entry}, {}).then((r) => {
entry.ids.forEach(id => {
let e = entries.value.get(id)
if (e) {
e.updatedAt = r.timestamp
updatedEntries.set(id, e)
}
e.updatedAt = r.timestamp
e.checked = r.checked
entries.value.set(id, e)
})
itemCheckSyncQueue.value.splice(index, 1)
}).catch((err) => {
@@ -442,8 +432,6 @@ export const useShoppingStore = defineStore(_STORE_ID, () => {
})
Promise.allSettled(promises).finally(() => {
entries.value = new Map([...entries.value, ...updatedEntries])
syncQueueRunning.value = false
if (itemCheckSyncQueue.value.length > 0) {
runSyncQueue(500)
}
@@ -591,8 +579,7 @@ export const useShoppingStore = defineStore(_STORE_ID, () => {
entries,
supermarkets,
supermarketCategories,
updateEntriesStructure,
entriesByGroup,
getEntriesByGroup,
autoSyncTimeoutId,
autoSyncHasFocus,
autoSyncLastTimestamp,
@@ -602,7 +589,7 @@ export const useShoppingStore = defineStore(_STORE_ID, () => {
hasFailedItems,
itemCheckSyncQueue,
undoStack,
totalFoods,
stats,
refreshFromAPI,
autoSync,
createObject,

View File

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

View File

@@ -7,7 +7,7 @@ import {
MealPlan,
MealType,
Property, PropertyType,
Recipe, RecipeBook, RecipeBookEntry, RecipeImport, SearchFields, ShoppingList, ShoppingListEntry, Space,
Recipe, RecipeBook, RecipeBookEntry, RecipeImport, SearchFields, ShoppingListEntry, Space,
Step,
Supermarket,
SupermarketCategory, Sync, SyncLog,
@@ -144,7 +144,6 @@ export type EditorSupportedModels =
| 'Automation'
| 'Keyword'
| 'UserFile'
| 'ShoppingList'
| 'ShoppingListEntry'
| 'User'
| 'RecipeBook'
@@ -183,7 +182,6 @@ export type EditorSupportedTypes =
| Automation
| Keyword
| UserFile
| ShoppingList
| ShoppingListEntry
| User
| RecipeBook
@@ -486,28 +484,6 @@ export const TSupermarketCategory = {
} as Model
registerModel(TSupermarketCategory)
export const TShoppingList = {
name: 'ShoppingList',
localizationKey: 'ShoppingList',
localizationKeyDescription: 'ShoppingListHelp',
icon: 'fa-solid fa-list-check',
editorComponent: defineAsyncComponent(() => import(`@/components/model_editors/ShoppingListEditor.vue`)),
disableListView: true,
isPaginated: true,
toStringKeys: ['name'],
tableHeaders: [
{title: 'Name', key: 'name'},
{title: 'Color', key: 'color'},
{title: 'Description', key: 'description'},
{title: 'Actions', key: 'action', align: 'end'},
]
} as Model
registerModel(TShoppingList)
export const TShoppingListEntry = {
name: 'ShoppingListEntry',
localizationKey: 'ShoppingListEntry',

View File

@@ -23,6 +23,7 @@ export interface IShoppingList {
export interface IShoppingListCategory {
name: string,
foods: Map<number, IShoppingListFood>,
stats: ShoppingListStats,
}
/**

View File

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

View File

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