mirror of
https://github.com/TandoorRecipes/recipes.git
synced 2025-12-25 11:19:39 -05:00
Compare commits
6 Commits
feature/sh
...
master
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
fc40b5220b | ||
|
|
c0d172574d | ||
|
|
84fd3055ea | ||
|
|
d22b5a4a39 | ||
|
|
602f0a8bf0 | ||
|
|
856f417d1b |
@@ -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),
|
||||
),
|
||||
]
|
||||
@@ -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'),
|
||||
),
|
||||
]
|
||||
@@ -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),
|
||||
),
|
||||
]
|
||||
@@ -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)
|
||||
|
||||
@@ -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):
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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')))
|
||||
|
||||
@@ -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
|
||||
*/
|
||||
|
||||
@@ -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>
|
||||
@@ -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>
|
||||
|
||||
@@ -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>
|
||||
@@ -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>
|
||||
|
||||
|
||||
@@ -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>
|
||||
@@ -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"
|
||||
|
||||
@@ -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: {}}},
|
||||
|
||||
@@ -23,7 +23,6 @@
|
||||
"AiModelHelp": "",
|
||||
"AiProvider": "",
|
||||
"AiProviderHelp": "",
|
||||
"All": "",
|
||||
"App": "",
|
||||
"Apply": "",
|
||||
"Are_You_Sure": "",
|
||||
|
||||
@@ -23,7 +23,6 @@
|
||||
"AiModelHelp": "",
|
||||
"AiProvider": "",
|
||||
"AiProviderHelp": "",
|
||||
"All": "",
|
||||
"App": "Приложение",
|
||||
"Apply": "",
|
||||
"Are_You_Sure": "Сигурен ли си?",
|
||||
|
||||
@@ -26,7 +26,6 @@
|
||||
"AiProvider": "",
|
||||
"AiProviderHelp": "",
|
||||
"Alignment": "Alineació",
|
||||
"All": "",
|
||||
"Amount": "Quantitat",
|
||||
"App": "Aplicació",
|
||||
"Apply": "",
|
||||
|
||||
@@ -26,7 +26,6 @@
|
||||
"AiProvider": "",
|
||||
"AiProviderHelp": "",
|
||||
"Alignment": "Zarovnání",
|
||||
"All": "",
|
||||
"Amount": "Množství",
|
||||
"App": "Aplikace",
|
||||
"Apply": "",
|
||||
|
||||
@@ -26,7 +26,6 @@
|
||||
"AiProvider": "",
|
||||
"AiProviderHelp": "",
|
||||
"Alignment": "Justering",
|
||||
"All": "",
|
||||
"Amount": "Mængde",
|
||||
"App": "App",
|
||||
"Apply": "",
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -26,7 +26,6 @@
|
||||
"AiProvider": "",
|
||||
"AiProviderHelp": "",
|
||||
"Alignment": "Ευθυγράμμιση",
|
||||
"All": "",
|
||||
"Amount": "Ποσότητα",
|
||||
"App": "Εφαρμογή",
|
||||
"Apply": "",
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -36,7 +36,6 @@
|
||||
"AiProvider": "",
|
||||
"AiProviderHelp": "",
|
||||
"Alignment": "Alineación",
|
||||
"All": "",
|
||||
"AllRecipes": "Todas las recetas",
|
||||
"Amount": "Cantidad",
|
||||
"App": "Aplicación",
|
||||
|
||||
@@ -27,7 +27,6 @@
|
||||
"AiProvider": "",
|
||||
"AiProviderHelp": "",
|
||||
"Alignment": "Tasaus",
|
||||
"All": "",
|
||||
"Amount": "Määrä",
|
||||
"App": "Applikaatio",
|
||||
"Apply": "",
|
||||
|
||||
@@ -37,7 +37,6 @@
|
||||
"AiProvider": "",
|
||||
"AiProviderHelp": "",
|
||||
"Alignment": "Alignement",
|
||||
"All": "",
|
||||
"AllRecipes": "Toutes les recettes",
|
||||
"Amount": "Quantité",
|
||||
"App": "Appli",
|
||||
|
||||
@@ -26,7 +26,6 @@
|
||||
"AiProvider": "",
|
||||
"AiProviderHelp": "",
|
||||
"Alignment": "יישור",
|
||||
"All": "",
|
||||
"Amount": "כמות",
|
||||
"App": "אפליקציה",
|
||||
"Apply": "",
|
||||
|
||||
@@ -26,7 +26,6 @@
|
||||
"AiProvider": "",
|
||||
"AiProviderHelp": "",
|
||||
"Alignment": "Poravnanje",
|
||||
"All": "",
|
||||
"Amount": "Količina",
|
||||
"App": "Aplikacija",
|
||||
"Apply": "",
|
||||
|
||||
@@ -26,7 +26,6 @@
|
||||
"AiProvider": "",
|
||||
"AiProviderHelp": "",
|
||||
"Alignment": "Igazítás",
|
||||
"All": "",
|
||||
"Amount": "Összeg",
|
||||
"App": "Applikáció",
|
||||
"Apply": "",
|
||||
|
||||
@@ -17,7 +17,6 @@
|
||||
"AiModelHelp": "",
|
||||
"AiProvider": "",
|
||||
"AiProviderHelp": "",
|
||||
"All": "",
|
||||
"Apply": "",
|
||||
"Automate": "Ավտոմատացնել",
|
||||
"BatchDeleteConfirm": "",
|
||||
|
||||
@@ -25,7 +25,6 @@
|
||||
"AiModelHelp": "",
|
||||
"AiProvider": "",
|
||||
"AiProviderHelp": "",
|
||||
"All": "",
|
||||
"App": "",
|
||||
"Apply": "",
|
||||
"Are_You_Sure": "",
|
||||
|
||||
@@ -26,7 +26,6 @@
|
||||
"AiProvider": "",
|
||||
"AiProviderHelp": "",
|
||||
"Alignment": "",
|
||||
"All": "",
|
||||
"Amount": "",
|
||||
"App": "",
|
||||
"Apply": "",
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -36,7 +36,6 @@
|
||||
"AiProvider": "",
|
||||
"AiProviderHelp": "",
|
||||
"Alignment": "",
|
||||
"All": "",
|
||||
"AllRecipes": "",
|
||||
"Amount": "",
|
||||
"App": "",
|
||||
|
||||
@@ -26,7 +26,6 @@
|
||||
"AiProvider": "",
|
||||
"AiProviderHelp": "",
|
||||
"Alignment": "",
|
||||
"All": "",
|
||||
"Amount": "Suma",
|
||||
"App": "",
|
||||
"Apply": "",
|
||||
|
||||
@@ -26,7 +26,6 @@
|
||||
"AiProvider": "",
|
||||
"AiProviderHelp": "",
|
||||
"Alignment": "",
|
||||
"All": "",
|
||||
"Amount": "",
|
||||
"App": "",
|
||||
"Apply": "",
|
||||
|
||||
@@ -26,7 +26,6 @@
|
||||
"AiProvider": "",
|
||||
"AiProviderHelp": "",
|
||||
"Alignment": "Justering",
|
||||
"All": "",
|
||||
"Amount": "Mengde",
|
||||
"App": "App",
|
||||
"Apply": "",
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -35,7 +35,6 @@
|
||||
"AiProvider": "",
|
||||
"AiProviderHelp": "",
|
||||
"Alignment": "Wyrównanie",
|
||||
"All": "",
|
||||
"AllRecipes": "Wszystkie przepisy",
|
||||
"Amount": "Ilość",
|
||||
"App": "Aplikacja",
|
||||
|
||||
@@ -19,7 +19,6 @@
|
||||
"Added_on": "Adicionado a",
|
||||
"Advanced": "Avançado",
|
||||
"Alignment": "Alinhamento",
|
||||
"All": "",
|
||||
"Amount": "Quantidade",
|
||||
"Apply": "",
|
||||
"Auto_Planner": "",
|
||||
|
||||
@@ -36,7 +36,6 @@
|
||||
"AiProvider": "",
|
||||
"AiProviderHelp": "",
|
||||
"Alignment": "Alinhamento",
|
||||
"All": "",
|
||||
"AllRecipes": "Todas Receitas",
|
||||
"Amount": "Quantidade",
|
||||
"App": "Aplicação",
|
||||
|
||||
@@ -26,7 +26,6 @@
|
||||
"AiModelHelp": "",
|
||||
"AiProvider": "",
|
||||
"AiProviderHelp": "",
|
||||
"All": "",
|
||||
"Amount": "Cantitate",
|
||||
"App": "Aplicație",
|
||||
"Apply": "",
|
||||
|
||||
@@ -37,7 +37,6 @@
|
||||
"AiProvider": "",
|
||||
"AiProviderHelp": "",
|
||||
"Alignment": "Выравнивание",
|
||||
"All": "",
|
||||
"AllRecipes": "Все рецепты",
|
||||
"Amount": "Количество",
|
||||
"App": "Приложение",
|
||||
|
||||
@@ -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
@@ -26,7 +26,6 @@
|
||||
"AiProvider": "",
|
||||
"AiProviderHelp": "",
|
||||
"Alignment": "Hizalama",
|
||||
"All": "",
|
||||
"Amount": "Miktar",
|
||||
"App": "Uygulama",
|
||||
"Apply": "",
|
||||
|
||||
@@ -37,7 +37,6 @@
|
||||
"AiProvider": "Постачальник ШІ",
|
||||
"AiProviderHelp": "Ви можете налаштувати декількох постачальників штучного інтелекту відповідно до своїх уподобань. Їх навіть можна налаштувати для роботи в декількох просторах.",
|
||||
"Alignment": "Вирівнювання",
|
||||
"All": "",
|
||||
"AllRecipes": "Всі рецепти",
|
||||
"Amount": "Кількість",
|
||||
"App": "Додаток",
|
||||
|
||||
@@ -26,7 +26,6 @@
|
||||
"AiProvider": "",
|
||||
"AiProviderHelp": "",
|
||||
"Alignment": "校准",
|
||||
"All": "",
|
||||
"Amount": "数量",
|
||||
"App": "应用",
|
||||
"Apply": "",
|
||||
|
||||
@@ -36,7 +36,6 @@
|
||||
"AiProvider": "",
|
||||
"AiProviderHelp": "",
|
||||
"Alignment": "對齊",
|
||||
"All": "",
|
||||
"AllRecipes": "所有食譜",
|
||||
"Amount": "數量",
|
||||
"App": "應用程式",
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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/
|
||||
*/
|
||||
|
||||
@@ -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)),
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -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()),
|
||||
};
|
||||
}
|
||||
|
||||
@@ -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)),
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -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'],
|
||||
};
|
||||
}
|
||||
|
||||
@@ -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'],
|
||||
|
||||
@@ -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']),
|
||||
};
|
||||
|
||||
@@ -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'],
|
||||
};
|
||||
}
|
||||
|
||||
@@ -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'],
|
||||
|
||||
@@ -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'],
|
||||
};
|
||||
}
|
||||
|
||||
@@ -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'],
|
||||
|
||||
@@ -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']),
|
||||
};
|
||||
|
||||
@@ -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'],
|
||||
};
|
||||
}
|
||||
|
||||
@@ -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'],
|
||||
|
||||
@@ -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';
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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',
|
||||
|
||||
@@ -23,6 +23,7 @@ export interface IShoppingList {
|
||||
export interface IShoppingListCategory {
|
||||
name: string,
|
||||
foods: Map<number, IShoppingListFood>,
|
||||
stats: ShoppingListStats,
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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 ----------------------
|
||||
|
||||
Reference in New Issue
Block a user