mirror of
https://github.com/TandoorRecipes/recipes.git
synced 2025-12-30 21:49:50 -05:00
Compare commits
47 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
28e554d04b | ||
|
|
5040caf91c | ||
|
|
c5fcfd07a7 | ||
|
|
a1a172e223 | ||
|
|
0039654d40 | ||
|
|
17de37b9fc | ||
|
|
d0856ce3b7 | ||
|
|
24426c2b7e | ||
|
|
5380b7d697 | ||
|
|
1d0488fbb0 | ||
|
|
2213346297 | ||
|
|
e1a9938c0b | ||
|
|
126f21842f | ||
|
|
9dfc9e1020 | ||
|
|
84898b09f2 | ||
|
|
d4ded02c2a | ||
|
|
8db294255e | ||
|
|
c99d13e2e7 | ||
|
|
5608f80246 | ||
|
|
59040f4cdf | ||
|
|
af476480c1 | ||
|
|
9307e61c1a | ||
|
|
8f5593d5ca | ||
|
|
f4eded5b03 | ||
|
|
3d9c51053a | ||
|
|
0046233b6f | ||
|
|
78ede7b601 | ||
|
|
7e7e133604 | ||
|
|
b0ec569a00 | ||
|
|
7674084ae0 | ||
|
|
798e2ac48b | ||
|
|
714d4a32a9 | ||
|
|
66b5097872 | ||
|
|
c1d4fed142 | ||
|
|
1cc0806729 | ||
|
|
09487a0e94 | ||
|
|
89e58edcad | ||
|
|
887d7fe9f0 | ||
|
|
14696e3ce8 | ||
|
|
dd56bb4b35 | ||
|
|
8ec0ba9541 | ||
|
|
c105c9190e | ||
|
|
9c1700adb9 | ||
|
|
b43a87a7e3 | ||
|
|
01e78baecf | ||
|
|
0ee241524d | ||
|
|
e0196f17da |
@@ -56,7 +56,7 @@ class FoodPropertyHelper:
|
|||||||
if p.property_type == pt and p.property_amount is not None:
|
if p.property_type == pt and p.property_amount is not None:
|
||||||
has_property_value = True
|
has_property_value = True
|
||||||
for c in conversions:
|
for c in conversions:
|
||||||
if c.unit == i.food.properties_food_unit:
|
if c.unit == i.food.properties_food_unit and i.food.properties_food_amount != 0:
|
||||||
found_property = True
|
found_property = True
|
||||||
computed_properties[pt.id]['total_value'] += (c.amount / i.food.properties_food_amount) * p.property_amount
|
computed_properties[pt.id]['total_value'] += (c.amount / i.food.properties_food_amount) * p.property_amount
|
||||||
computed_properties[pt.id]['food_values'] = self.add_or_create(
|
computed_properties[pt.id]['food_values'] = self.add_or_create(
|
||||||
|
|||||||
@@ -324,9 +324,9 @@ class RecipeSearch():
|
|||||||
self._queryset = self._queryset.annotate(recent=Coalesce(Max(Case(When(pk__in=num_recent_recipes.values('recipe'), then='viewlog__pk'))), Value(0)))
|
self._queryset = self._queryset.annotate(recent=Coalesce(Max(Case(When(pk__in=num_recent_recipes.values('recipe'), then='viewlog__pk'))), Value(0)))
|
||||||
|
|
||||||
def _favorite_recipes(self):
|
def _favorite_recipes(self):
|
||||||
if self._sort_includes('favorite') or self._timescooked or self._timescooked_gte or self._timescooked_lte:
|
if self._sort_includes('favorite') or self._timescooked is not None or self._timescooked_gte is not None or self._timescooked_lte is not None:
|
||||||
less_than = self._timescooked_lte and not self._sort_includes('-favorite')
|
less_than = self._timescooked_lte and not self._sort_includes('-favorite')
|
||||||
if less_than or self._timescooked == 0:
|
if less_than:
|
||||||
default = 1000
|
default = 1000
|
||||||
else:
|
else:
|
||||||
default = 0
|
default = 0
|
||||||
@@ -338,11 +338,11 @@ class RecipeSearch():
|
|||||||
)
|
)
|
||||||
self._queryset = self._queryset.annotate(favorite=Coalesce(Subquery(favorite_recipes), default))
|
self._queryset = self._queryset.annotate(favorite=Coalesce(Subquery(favorite_recipes), default))
|
||||||
|
|
||||||
if self._timescooked:
|
if self._timescooked is not None:
|
||||||
self._queryset = self._queryset.filter(favorite=self._timescooked)
|
self._queryset = self._queryset.filter(favorite=self._timescooked)
|
||||||
elif self._timescooked_lte:
|
elif self._timescooked_lte is not None:
|
||||||
self._queryset = self._queryset.filter(favorite__lte=int(self._timescooked_lte)).exclude(favorite=0)
|
self._queryset = self._queryset.filter(favorite__lte=int(self._timescooked_lte)).exclude(favorite=0)
|
||||||
elif self._timescooked_gte:
|
elif self._timescooked_gte is not None:
|
||||||
self._queryset = self._queryset.filter(favorite__gte=int(self._timescooked_gte))
|
self._queryset = self._queryset.filter(favorite__gte=int(self._timescooked_gte))
|
||||||
|
|
||||||
def keyword_filters(self, **kwargs):
|
def keyword_filters(self, **kwargs):
|
||||||
|
|||||||
@@ -75,7 +75,8 @@ class RecipeShoppingEditor():
|
|||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def get_shopping_list_recipe(id, user, space):
|
def get_shopping_list_recipe(id, user, space):
|
||||||
return ShoppingListRecipe.objects.filter(id=id).filter(entries__space=space).filter(
|
# TODO this sucks since it wont find SLR's that no longer have any entries
|
||||||
|
return ShoppingListRecipe.objects.filter(id=id, space=space).filter(
|
||||||
Q(entries__created_by=user)
|
Q(entries__created_by=user)
|
||||||
| Q(entries__created_by__in=list(user.get_shopping_share()))
|
| Q(entries__created_by__in=list(user.get_shopping_share()))
|
||||||
).prefetch_related('entries').first()
|
).prefetch_related('entries').first()
|
||||||
@@ -136,7 +137,8 @@ class RecipeShoppingEditor():
|
|||||||
self.servings = servings
|
self.servings = servings
|
||||||
|
|
||||||
self._delete_ingredients(ingredients=ingredients)
|
self._delete_ingredients(ingredients=ingredients)
|
||||||
if self.servings != self._shopping_list_recipe.servings:
|
# need to check if there is a SLR because its possible it cant be found if all entries are deleted
|
||||||
|
if self._shopping_list_recipe and self.servings != self._shopping_list_recipe.servings:
|
||||||
self.edit_servings()
|
self.edit_servings()
|
||||||
self._add_ingredients(ingredients=ingredients)
|
self._add_ingredients(ingredients=ingredients)
|
||||||
return True
|
return True
|
||||||
@@ -175,8 +177,9 @@ class RecipeShoppingEditor():
|
|||||||
existing = self._shopping_list_recipe.entries.filter(ingredient__in=ingredients).values_list('ingredient__pk', flat=True)
|
existing = self._shopping_list_recipe.entries.filter(ingredient__in=ingredients).values_list('ingredient__pk', flat=True)
|
||||||
add_ingredients = ingredients.exclude(id__in=existing)
|
add_ingredients = ingredients.exclude(id__in=existing)
|
||||||
|
|
||||||
|
entries = []
|
||||||
for i in [x for x in add_ingredients if x.food]:
|
for i in [x for x in add_ingredients if x.food]:
|
||||||
ShoppingListEntry.objects.create(
|
entry = ShoppingListEntry(
|
||||||
list_recipe=self._shopping_list_recipe,
|
list_recipe=self._shopping_list_recipe,
|
||||||
food=i.food,
|
food=i.food,
|
||||||
unit=i.unit,
|
unit=i.unit,
|
||||||
@@ -185,6 +188,12 @@ class RecipeShoppingEditor():
|
|||||||
created_by=self.created_by,
|
created_by=self.created_by,
|
||||||
space=self.space,
|
space=self.space,
|
||||||
)
|
)
|
||||||
|
entries.append(entry)
|
||||||
|
|
||||||
|
ShoppingListEntry.objects.bulk_create(entries)
|
||||||
|
for e in entries:
|
||||||
|
if e.food.shopping_lists.count() > 0:
|
||||||
|
e.shopping_lists.set(e.food.shopping_lists.all())
|
||||||
|
|
||||||
# deletes shopping list entries not in ingredients list
|
# deletes shopping list entries not in ingredients list
|
||||||
def _delete_ingredients(self, ingredients=None):
|
def _delete_ingredients(self, ingredients=None):
|
||||||
|
|||||||
@@ -96,14 +96,20 @@ class Mealie1(Integration):
|
|||||||
self.import_log.msg += f"Ignoring {r['name']} because a recipe with this name already exists.\n"
|
self.import_log.msg += f"Ignoring {r['name']} because a recipe with this name already exists.\n"
|
||||||
self.import_log.save()
|
self.import_log.save()
|
||||||
else:
|
else:
|
||||||
|
servings = 1
|
||||||
|
try:
|
||||||
|
servings = r['recipe_servings'] if r['recipe_servings'] and r['recipe_servings'] != 0 else 1
|
||||||
|
except KeyError:
|
||||||
|
pass
|
||||||
|
|
||||||
recipe = Recipe.objects.create(
|
recipe = Recipe.objects.create(
|
||||||
waiting_time=parse_time(r['perform_time']),
|
waiting_time=parse_time(r['perform_time']),
|
||||||
working_time=parse_time(r['prep_time']),
|
working_time=parse_time(r['prep_time']),
|
||||||
description=r['description'][:512],
|
description=r['description'][:512],
|
||||||
name=r['name'],
|
name=r['name'],
|
||||||
source_url=r['org_url'],
|
source_url=r['org_url'],
|
||||||
servings=r['recipe_servings'] if r['recipe_servings'] and r['recipe_servings'] != 0 else 1,
|
servings=servings,
|
||||||
servings_text=r['recipe_yield'].strip() if r['recipe_yield'] else "",
|
servings_text=r['recipe_yield'].strip()[:32] if r['recipe_yield'] else "",
|
||||||
internal=True,
|
internal=True,
|
||||||
created_at=r['created_at'],
|
created_at=r['created_at'],
|
||||||
space=self.request.space,
|
space=self.request.space,
|
||||||
@@ -131,7 +137,7 @@ class Mealie1(Integration):
|
|||||||
step_id_dict = {}
|
step_id_dict = {}
|
||||||
for s in mealie_database['recipe_instructions']:
|
for s in mealie_database['recipe_instructions']:
|
||||||
if s['recipe_id'] in recipes_dict:
|
if s['recipe_id'] in recipes_dict:
|
||||||
step = Step.objects.create(instruction=(s['text'] if s['text'] else "") + (f" \n {s['summary']}" if s['summary'] else ""),
|
step = Step.objects.create(instruction=(s['text'] if s['text'] else "") + (f" \n {s['summary']}" if 'summary' in s and s['summary'] else ""),
|
||||||
order=s['position'],
|
order=s['position'],
|
||||||
name=s['title'],
|
name=s['title'],
|
||||||
space=self.request.space)
|
space=self.request.space)
|
||||||
@@ -153,7 +159,7 @@ class Mealie1(Integration):
|
|||||||
for n in mealie_database['notes']:
|
for n in mealie_database['notes']:
|
||||||
if n['recipe_id'] in recipes_dict:
|
if n['recipe_id'] in recipes_dict:
|
||||||
step = Step.objects.create(instruction=n['text'],
|
step = Step.objects.create(instruction=n['text'],
|
||||||
name=n['title'],
|
name=n['title'][:128] if n['title'] else "",
|
||||||
order=100,
|
order=100,
|
||||||
space=self.request.space)
|
space=self.request.space)
|
||||||
steps_relation.append(Recipe.steps.through(recipe_id=recipes_dict[n['recipe_id']], step_id=step.pk))
|
steps_relation.append(Recipe.steps.through(recipe_id=recipes_dict[n['recipe_id']], step_id=step.pk))
|
||||||
@@ -191,7 +197,7 @@ class Mealie1(Integration):
|
|||||||
space=self.request.space,
|
space=self.request.space,
|
||||||
)
|
)
|
||||||
ingredients_relation.append(Step.ingredients.through(step_id=get_step_id(i, first_step_of_recipe_dict, step_id_dict,recipe_ingredient_ref_link_dict), ingredient_id=ingredient.pk))
|
ingredients_relation.append(Step.ingredients.through(step_id=get_step_id(i, first_step_of_recipe_dict, step_id_dict,recipe_ingredient_ref_link_dict), ingredient_id=ingredient.pk))
|
||||||
elif i['note'].strip():
|
elif i['note'] and i['note'].strip():
|
||||||
amount, unit, food, note = ingredient_parser.parse(i['note'].strip())
|
amount, unit, food, note = ingredient_parser.parse(i['note'].strip())
|
||||||
f = ingredient_parser.get_food(food)
|
f = ingredient_parser.get_food(food)
|
||||||
u = ingredient_parser.get_unit(unit)
|
u = ingredient_parser.get_unit(unit)
|
||||||
@@ -243,7 +249,7 @@ class Mealie1(Integration):
|
|||||||
for r in mealie_database['recipe_nutrition']:
|
for r in mealie_database['recipe_nutrition']:
|
||||||
if r['recipe_id'] in recipes_dict:
|
if r['recipe_id'] in recipes_dict:
|
||||||
for key in property_types_dict:
|
for key in property_types_dict:
|
||||||
if r[key]:
|
if key in r and r[key]:
|
||||||
properties_relation.append(
|
properties_relation.append(
|
||||||
Property(property_type_id=property_types_dict[key].pk,
|
Property(property_type_id=property_types_dict[key].pk,
|
||||||
property_amount=Decimal(str(r[key])) / (
|
property_amount=Decimal(str(r[key])) / (
|
||||||
|
|||||||
@@ -63,7 +63,15 @@ class MealMaster(Integration):
|
|||||||
current_recipe = ''
|
current_recipe = ''
|
||||||
|
|
||||||
for fl in file.readlines():
|
for fl in file.readlines():
|
||||||
line = fl.decode("windows-1250")
|
line = ""
|
||||||
|
try:
|
||||||
|
line = fl.decode("UTF-8")
|
||||||
|
except UnicodeDecodeError:
|
||||||
|
try:
|
||||||
|
line = fl.decode("windows-1250")
|
||||||
|
except Exception as e:
|
||||||
|
line = "ERROR DECODING LINE"
|
||||||
|
|
||||||
if (line.startswith('MMMMM') or line.startswith('-----')) and 'meal-master' in line.lower():
|
if (line.startswith('MMMMM') or line.startswith('-----')) and 'meal-master' in line.lower():
|
||||||
if current_recipe != '':
|
if current_recipe != '':
|
||||||
recipe_list.append(current_recipe)
|
recipe_list.append(current_recipe)
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ msgstr ""
|
|||||||
"Project-Id-Version: PACKAGE VERSION\n"
|
"Project-Id-Version: PACKAGE VERSION\n"
|
||||||
"Report-Msgid-Bugs-To: \n"
|
"Report-Msgid-Bugs-To: \n"
|
||||||
"POT-Creation-Date: 2025-09-22 20:15+0200\n"
|
"POT-Creation-Date: 2025-09-22 20:15+0200\n"
|
||||||
"PO-Revision-Date: 2025-11-15 12:08+0000\n"
|
"PO-Revision-Date: 2025-12-01 06:08+0000\n"
|
||||||
"Last-Translator: \"Matjaž T.\" <matjaz@moj-svet.si>\n"
|
"Last-Translator: \"Matjaž T.\" <matjaz@moj-svet.si>\n"
|
||||||
"Language-Team: Slovenian <http://translate.tandoor.dev/projects/tandoor/"
|
"Language-Team: Slovenian <http://translate.tandoor.dev/projects/tandoor/"
|
||||||
"recipes-backend/sl/>\n"
|
"recipes-backend/sl/>\n"
|
||||||
@@ -2129,9 +2129,8 @@ msgid ""
|
|||||||
"please consult the django documentation on how to reset passwords."
|
"please consult the django documentation on how to reset passwords."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
"Nastavitveno stran lahko uporabite samo za ustvarjanje prvega "
|
"Nastavitveno stran lahko uporabite samo za ustvarjanje prvega "
|
||||||
"uporabnika! \n"
|
"uporabnika! Če ste pozabili poverilnice superuporabnika, "
|
||||||
" Če ste pozabili svoje poverilnice superuporabnika, si oglejte "
|
"si oglejte dokumentacijo django za ponastavitev gesel."
|
||||||
"dokumentacijo django o tem, kako ponastaviti gesla."
|
|
||||||
|
|
||||||
#: .\cookbook\views\views.py:304
|
#: .\cookbook\views\views.py:304
|
||||||
msgid "Passwords dont match!"
|
msgid "Passwords dont match!"
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ msgstr ""
|
|||||||
"Project-Id-Version: PACKAGE VERSION\n"
|
"Project-Id-Version: PACKAGE VERSION\n"
|
||||||
"Report-Msgid-Bugs-To: \n"
|
"Report-Msgid-Bugs-To: \n"
|
||||||
"POT-Creation-Date: 2024-08-01 15:04+0200\n"
|
"POT-Creation-Date: 2024-08-01 15:04+0200\n"
|
||||||
"PO-Revision-Date: 2025-11-18 07:01+0000\n"
|
"PO-Revision-Date: 2025-12-02 08:03+0000\n"
|
||||||
"Last-Translator: SerhiiOS <serhios@users.noreply.translate.tandoor.dev>\n"
|
"Last-Translator: SerhiiOS <serhios@users.noreply.translate.tandoor.dev>\n"
|
||||||
"Language-Team: Ukrainian <http://translate.tandoor.dev/projects/tandoor/"
|
"Language-Team: Ukrainian <http://translate.tandoor.dev/projects/tandoor/"
|
||||||
"recipes-backend/uk/>\n"
|
"recipes-backend/uk/>\n"
|
||||||
@@ -468,7 +468,7 @@ msgstr "Пошук"
|
|||||||
#: .\cookbook\models.py:455 .\cookbook\templates\base.html:114
|
#: .\cookbook\models.py:455 .\cookbook\templates\base.html:114
|
||||||
#: .\cookbook\templates\meal_plan.html:7
|
#: .\cookbook\templates\meal_plan.html:7
|
||||||
msgid "Meal-Plan"
|
msgid "Meal-Plan"
|
||||||
msgstr "План харчування"
|
msgstr "Меню"
|
||||||
|
|
||||||
#: .\cookbook\models.py:456 .\cookbook\templates\base.html:122
|
#: .\cookbook\models.py:456 .\cookbook\templates\base.html:122
|
||||||
#: .\cookbook\views\views.py:459
|
#: .\cookbook\views\views.py:459
|
||||||
@@ -1085,7 +1085,7 @@ msgstr "Ви використовуєте безкоштовну версію Ta
|
|||||||
|
|
||||||
#: .\cookbook\templates\base.html:407
|
#: .\cookbook\templates\base.html:407
|
||||||
msgid "Upgrade Now"
|
msgid "Upgrade Now"
|
||||||
msgstr "Оновити зараз"
|
msgstr "Оновити Зараз"
|
||||||
|
|
||||||
#: .\cookbook\templates\batch\edit.html:6
|
#: .\cookbook\templates\batch\edit.html:6
|
||||||
msgid "Batch edit Category"
|
msgid "Batch edit Category"
|
||||||
@@ -1447,7 +1447,7 @@ msgstr "Таблиця"
|
|||||||
#: .\cookbook\templates\markdown_info.html:155
|
#: .\cookbook\templates\markdown_info.html:155
|
||||||
#: .\cookbook\templates\markdown_info.html:172
|
#: .\cookbook\templates\markdown_info.html:172
|
||||||
msgid "Header"
|
msgid "Header"
|
||||||
msgstr "Заголовок"
|
msgstr "Шапка"
|
||||||
|
|
||||||
#: .\cookbook\templates\markdown_info.html:157
|
#: .\cookbook\templates\markdown_info.html:157
|
||||||
#: .\cookbook\templates\markdown_info.html:178
|
#: .\cookbook\templates\markdown_info.html:178
|
||||||
@@ -2639,7 +2639,7 @@ msgstr "Конфігурація коннектора для бекенду"
|
|||||||
|
|
||||||
#: .\cookbook\views\lists.py:91
|
#: .\cookbook\views\lists.py:91
|
||||||
msgid "Invite Links"
|
msgid "Invite Links"
|
||||||
msgstr "Посилання для запрошення"
|
msgstr "Посилання для запрошеннь"
|
||||||
|
|
||||||
#: .\cookbook\views\lists.py:154
|
#: .\cookbook\views\lists.py:154
|
||||||
msgid "Supermarkets"
|
msgid "Supermarkets"
|
||||||
@@ -2790,7 +2790,7 @@ msgstr ""
|
|||||||
|
|
||||||
#: .\cookbook\views\views.py:451
|
#: .\cookbook\views\views.py:451
|
||||||
msgid "Manage recipes, shopping list, meal plans and more."
|
msgid "Manage recipes, shopping list, meal plans and more."
|
||||||
msgstr "Керуйте рецептами, списком покупок, планами харчування тощо."
|
msgstr "Керуйте рецептами, списком покупок, меню тощо."
|
||||||
|
|
||||||
#: .\cookbook\views\views.py:458
|
#: .\cookbook\views\views.py:458
|
||||||
msgid "Plan"
|
msgid "Plan"
|
||||||
|
|||||||
29
cookbook/migrations/0232_shoppinglist.py
Normal file
29
cookbook/migrations/0232_shoppinglist.py
Normal file
@@ -0,0 +1,29 @@
|
|||||||
|
# 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),
|
||||||
|
),
|
||||||
|
]
|
||||||
@@ -0,0 +1,28 @@
|
|||||||
|
# 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'),
|
||||||
|
),
|
||||||
|
]
|
||||||
@@ -0,0 +1,22 @@
|
|||||||
|
# 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,6 +551,7 @@ class UserPreference(models.Model, PermissionModelMixin):
|
|||||||
show_step_ingredients = models.BooleanField(default=True)
|
show_step_ingredients = models.BooleanField(default=True)
|
||||||
default_delay = models.DecimalField(default=4, max_digits=8, decimal_places=4)
|
default_delay = models.DecimalField(default=4, max_digits=8, decimal_places=4)
|
||||||
shopping_recent_days = models.PositiveIntegerField(default=7)
|
shopping_recent_days = models.PositiveIntegerField(default=7)
|
||||||
|
shopping_update_food_lists = models.BooleanField(default=True)
|
||||||
csv_delim = models.CharField(max_length=2, default=",")
|
csv_delim = models.CharField(max_length=2, default=",")
|
||||||
csv_prefix = models.CharField(max_length=10, blank=True, )
|
csv_prefix = models.CharField(max_length=10, blank=True, )
|
||||||
|
|
||||||
@@ -666,6 +667,7 @@ class Supermarket(models.Model, PermissionModelMixin):
|
|||||||
name = models.CharField(max_length=128, validators=[MinLengthValidator(1)])
|
name = models.CharField(max_length=128, validators=[MinLengthValidator(1)])
|
||||||
description = models.TextField(blank=True, null=True)
|
description = models.TextField(blank=True, null=True)
|
||||||
categories = models.ManyToManyField(SupermarketCategory, through='SupermarketCategoryRelation')
|
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)
|
open_data_slug = models.CharField(max_length=128, null=True, blank=True, default=None)
|
||||||
|
|
||||||
space = models.ForeignKey(Space, on_delete=models.CASCADE)
|
space = models.ForeignKey(Space, on_delete=models.CASCADE)
|
||||||
@@ -780,6 +782,7 @@ class Food(ExportModelOperationsMixin('food'), TreeModel, PermissionModelMixin):
|
|||||||
recipe = models.ForeignKey('Recipe', null=True, blank=True, on_delete=models.SET_NULL)
|
recipe = models.ForeignKey('Recipe', null=True, blank=True, on_delete=models.SET_NULL)
|
||||||
url = models.CharField(max_length=1024, blank=True, null=True, default='')
|
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
|
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
|
ignore_shopping = models.BooleanField(default=False) # inherited field
|
||||||
onhand_users = models.ManyToManyField(User, blank=True)
|
onhand_users = models.ManyToManyField(User, blank=True)
|
||||||
description = models.TextField(default='', blank=True)
|
description = models.TextField(default='', blank=True)
|
||||||
@@ -943,8 +946,8 @@ class Ingredient(ExportModelOperationsMixin('ingredient'), models.Model, Permiss
|
|||||||
space = models.ForeignKey(Space, on_delete=models.CASCADE)
|
space = models.ForeignKey(Space, on_delete=models.CASCADE)
|
||||||
objects = ScopedManager(space='space')
|
objects = ScopedManager(space='space')
|
||||||
|
|
||||||
def __str__(self):
|
# def __str__(self):
|
||||||
return f'{self.pk}: {self.amount} ' + (self.food.name if self.food else ' ') + (self.unit.name if self.unit else '')
|
# return f'{self.pk}: {self.amount} ' + (self.food.name if self.food else ' ') + (self.unit.name if self.unit else '')
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
ordering = ['order', 'pk']
|
ordering = ['order', 'pk']
|
||||||
@@ -1157,7 +1160,7 @@ class Comment(ExportModelOperationsMixin('comment'), models.Model, PermissionMod
|
|||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
return self.text
|
return self.text
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
ordering = ('pk',)
|
ordering = ('pk',)
|
||||||
|
|
||||||
@@ -1297,14 +1300,30 @@ class ShoppingListRecipe(ExportModelOperationsMixin('shopping_list_recipe'), mod
|
|||||||
|
|
||||||
objects = ScopedManager(space='space')
|
objects = ScopedManager(space='space')
|
||||||
|
|
||||||
def __str__(self):
|
# def __str__(self):
|
||||||
return f'Shopping list recipe {self.id} - {self.recipe}'
|
# return f'Shopping list recipe {self.id} - {self.recipe}'
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
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')
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
ordering = ('pk',)
|
ordering = ('pk',)
|
||||||
|
|
||||||
|
|
||||||
class ShoppingListEntry(ExportModelOperationsMixin('shopping_list_entry'), models.Model, PermissionModelMixin):
|
class ShoppingListEntry(ExportModelOperationsMixin('shopping_list_entry'), models.Model, PermissionModelMixin):
|
||||||
|
shopping_lists = models.ManyToManyField(ShoppingList, blank=True)
|
||||||
list_recipe = models.ForeignKey(ShoppingListRecipe, on_delete=models.CASCADE, null=True, blank=True, related_name='entries')
|
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')
|
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)
|
unit = models.ForeignKey(Unit, on_delete=models.SET_NULL, null=True, blank=True)
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
import traceback
|
||||||
import uuid
|
import uuid
|
||||||
from datetime import datetime, timedelta
|
from datetime import datetime, timedelta
|
||||||
from decimal import Decimal
|
from decimal import Decimal
|
||||||
@@ -37,7 +38,7 @@ from cookbook.models import (Automation, BookmarkletImport, Comment, CookLog, Cu
|
|||||||
ShareLink, ShoppingListEntry, ShoppingListRecipe, Space,
|
ShareLink, ShoppingListEntry, ShoppingListRecipe, Space,
|
||||||
Step, Storage, Supermarket, SupermarketCategory,
|
Step, Storage, Supermarket, SupermarketCategory,
|
||||||
SupermarketCategoryRelation, Sync, SyncLog, Unit, UnitConversion,
|
SupermarketCategoryRelation, Sync, SyncLog, Unit, UnitConversion,
|
||||||
UserFile, UserPreference, UserSpace, ViewLog, ConnectorConfig, SearchPreference, SearchFields, AiLog, AiProvider)
|
UserFile, UserPreference, UserSpace, ViewLog, ConnectorConfig, SearchPreference, SearchFields, AiLog, AiProvider, ShoppingList)
|
||||||
from cookbook.templatetags.custom_tags import markdown
|
from cookbook.templatetags.custom_tags import markdown
|
||||||
from recipes.settings import AWS_ENABLED, MEDIA_URL, EMAIL_HOST
|
from recipes.settings import AWS_ENABLED, MEDIA_URL, EMAIL_HOST
|
||||||
|
|
||||||
@@ -186,11 +187,37 @@ class SpaceFilterSerializer(serializers.ListSerializer):
|
|||||||
if isinstance(self.context['request'].user, AnonymousUser):
|
if isinstance(self.context['request'].user, AnonymousUser):
|
||||||
data = []
|
data = []
|
||||||
else:
|
else:
|
||||||
data = data.filter(userspace__space=self.context['request'].user.get_active_space()).all()
|
iterable = data.all() if hasattr(data, 'all') else data
|
||||||
|
if isinstance(iterable, list) or (isinstance(iterable, QuerySet) and getattr(iterable, '_result_cache', None) is not None):
|
||||||
|
try:
|
||||||
|
new_data = []
|
||||||
|
for u in iterable:
|
||||||
|
for us in u.userspace_set.all():
|
||||||
|
if us.space.id == self.context['request'].space.id:
|
||||||
|
new_data.append(u)
|
||||||
|
data = new_data
|
||||||
|
except Exception:
|
||||||
|
traceback.print_exc()
|
||||||
|
data = data.filter(userspace__space=self.context['request'].user.get_active_space()).all()
|
||||||
|
else:
|
||||||
|
if hasattr(self.context['request'], 'space'):
|
||||||
|
data = data.filter(userspace__space=self.context['request'].space).all()
|
||||||
|
else:
|
||||||
|
# not sure why but this branch can be hit (just normal page load, need to see why)
|
||||||
|
data = data.filter(userspace__space=self.context['request'].user.get_active_space()).all()
|
||||||
elif isinstance(data, list):
|
elif isinstance(data, list):
|
||||||
data = [d for d in data if getattr(d, self.child.Meta.model.get_space_key()[0]) == self.context['request'].space]
|
data = [d for d in data if getattr(d, self.child.Meta.model.get_space_key()[0]) == self.context['request'].space]
|
||||||
else:
|
else:
|
||||||
data = data.filter(**{'__'.join(self.child.Meta.model.get_space_key()): self.context['request'].space})
|
iterable = data.all() if hasattr(data, 'all') else data
|
||||||
|
if isinstance(iterable, list) or (isinstance(iterable, QuerySet) and getattr(iterable, '_result_cache', None) is not None):
|
||||||
|
keys = self.child.Meta.model.get_space_key()
|
||||||
|
if keys == ('space',):
|
||||||
|
data = [d for d in iterable if getattr(d, 'space_id') == self.context['request'].space.id]
|
||||||
|
else:
|
||||||
|
# use cached results here too, just dont have time to test this now, probably obj.get_space()
|
||||||
|
data = data.filter(**{'__'.join(self.child.Meta.model.get_space_key()): self.context['request'].space})
|
||||||
|
else:
|
||||||
|
data = data.filter(**{'__'.join(self.child.Meta.model.get_space_key()): self.context['request'].space})
|
||||||
return super().to_representation(data)
|
return super().to_representation(data)
|
||||||
|
|
||||||
|
|
||||||
@@ -484,6 +511,20 @@ class SpacedModelSerializer(serializers.ModelSerializer):
|
|||||||
return super().create(validated_data)
|
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):
|
class MealTypeSerializer(SpacedModelSerializer, WritableNestedModelSerializer):
|
||||||
|
|
||||||
def create(self, validated_data):
|
def create(self, validated_data):
|
||||||
@@ -533,7 +574,7 @@ class UserPreferenceSerializer(WritableNestedModelSerializer):
|
|||||||
'ingredient_decimals', 'comments', 'shopping_auto_sync', 'mealplan_autoadd_shopping',
|
'ingredient_decimals', 'comments', 'shopping_auto_sync', 'mealplan_autoadd_shopping',
|
||||||
'food_inherit_default', 'default_delay',
|
'food_inherit_default', 'default_delay',
|
||||||
'mealplan_autoinclude_related', 'mealplan_autoexclude_onhand', 'shopping_share', 'shopping_recent_days',
|
'mealplan_autoinclude_related', 'mealplan_autoexclude_onhand', 'shopping_share', 'shopping_recent_days',
|
||||||
'csv_delim', 'csv_prefix',
|
'csv_delim', 'csv_prefix', 'shopping_update_food_lists',
|
||||||
'filter_to_supermarket', 'shopping_add_onhand', 'left_handed', 'show_step_ingredients',
|
'filter_to_supermarket', 'shopping_add_onhand', 'left_handed', 'show_step_ingredients',
|
||||||
'food_children_exist'
|
'food_children_exist'
|
||||||
)
|
)
|
||||||
@@ -648,7 +689,7 @@ class KeywordLabelSerializer(serializers.ModelSerializer):
|
|||||||
|
|
||||||
@extend_schema_field(str)
|
@extend_schema_field(str)
|
||||||
def get_label(self, obj):
|
def get_label(self, obj):
|
||||||
return str(obj)
|
return obj.name
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
list_serializer_class = SpaceFilterSerializer
|
list_serializer_class = SpaceFilterSerializer
|
||||||
@@ -665,7 +706,7 @@ class KeywordSerializer(UniqueFieldsMixin, ExtendedRecipeMixin):
|
|||||||
|
|
||||||
@extend_schema_field(str)
|
@extend_schema_field(str)
|
||||||
def get_label(self, obj):
|
def get_label(self, obj):
|
||||||
return str(obj)
|
return obj.name
|
||||||
|
|
||||||
def create(self, validated_data):
|
def create(self, validated_data):
|
||||||
# since multi select tags dont have id's
|
# since multi select tags dont have id's
|
||||||
@@ -740,8 +781,9 @@ class SupermarketCategoryRelationSerializer(WritableNestedModelSerializer):
|
|||||||
fields = ('id', 'category', 'supermarket', 'order')
|
fields = ('id', 'category', 'supermarket', 'order')
|
||||||
|
|
||||||
|
|
||||||
class SupermarketSerializer(UniqueFieldsMixin, SpacedModelSerializer, OpenDataModelMixin):
|
class SupermarketSerializer(UniqueFieldsMixin, SpacedModelSerializer, WritableNestedModelSerializer, OpenDataModelMixin):
|
||||||
category_to_supermarket = SupermarketCategoryRelationSerializer(many=True, read_only=True)
|
category_to_supermarket = SupermarketCategoryRelationSerializer(many=True, read_only=True)
|
||||||
|
shopping_lists = ShoppingListSerializer(many=True, required=False)
|
||||||
|
|
||||||
def create(self, validated_data):
|
def create(self, validated_data):
|
||||||
validated_data['name'] = validated_data['name'].strip()
|
validated_data['name'] = validated_data['name'].strip()
|
||||||
@@ -752,7 +794,7 @@ class SupermarketSerializer(UniqueFieldsMixin, SpacedModelSerializer, OpenDataMo
|
|||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
model = Supermarket
|
model = Supermarket
|
||||||
fields = ('id', 'name', 'description', 'category_to_supermarket', 'open_data_slug')
|
fields = ('id', 'name', 'description', 'shopping_lists', 'category_to_supermarket', 'open_data_slug')
|
||||||
|
|
||||||
|
|
||||||
class PropertyTypeSerializer(OpenDataModelMixin, WritableNestedModelSerializer, UniqueFieldsMixin):
|
class PropertyTypeSerializer(OpenDataModelMixin, WritableNestedModelSerializer, UniqueFieldsMixin):
|
||||||
@@ -836,7 +878,7 @@ class FoodSerializer(UniqueFieldsMixin, WritableNestedModelSerializer, ExtendedR
|
|||||||
substitute_onhand = serializers.SerializerMethodField('get_substitute_onhand')
|
substitute_onhand = serializers.SerializerMethodField('get_substitute_onhand')
|
||||||
substitute = FoodSimpleSerializer(many=True, allow_null=True, required=False)
|
substitute = FoodSimpleSerializer(many=True, allow_null=True, required=False)
|
||||||
parent = IntegerField(read_only=True)
|
parent = IntegerField(read_only=True)
|
||||||
|
shopping_lists = ShoppingListSerializer(many=True, required=False)
|
||||||
properties = PropertySerializer(many=True, allow_null=True, required=False)
|
properties = PropertySerializer(many=True, allow_null=True, required=False)
|
||||||
properties_food_unit = UnitSerializer(allow_null=True, required=False)
|
properties_food_unit = UnitSerializer(allow_null=True, required=False)
|
||||||
properties_food_amount = CustomDecimalField(required=False)
|
properties_food_amount = CustomDecimalField(required=False)
|
||||||
@@ -947,7 +989,7 @@ class FoodSerializer(UniqueFieldsMixin, WritableNestedModelSerializer, ExtendedR
|
|||||||
fields = (
|
fields = (
|
||||||
'id', 'name', 'plural_name', 'description', 'shopping', 'recipe', 'url', 'properties', 'properties_food_amount', 'properties_food_unit', 'fdc_id',
|
'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',
|
'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',
|
'substitute', 'substitute_siblings', 'substitute_children', 'substitute_onhand', 'child_inherit_fields', 'open_data_slug', 'shopping_lists',
|
||||||
)
|
)
|
||||||
read_only_fields = ('id', 'numchild', 'parent', 'image', 'numrecipe')
|
read_only_fields = ('id', 'numchild', 'parent', 'image', 'numrecipe')
|
||||||
|
|
||||||
@@ -1327,7 +1369,7 @@ class MealPlanSerializer(SpacedModelSerializer, WritableNestedModelSerializer):
|
|||||||
|
|
||||||
@extend_schema_field(bool)
|
@extend_schema_field(bool)
|
||||||
def in_shopping(self, obj):
|
def in_shopping(self, obj):
|
||||||
return ShoppingListRecipe.objects.filter(mealplan=obj.id).exists()
|
return obj.shoppinglistrecipe_set.count() > 0
|
||||||
|
|
||||||
def create(self, validated_data):
|
def create(self, validated_data):
|
||||||
validated_data['created_by'] = self.context['request'].user
|
validated_data['created_by'] = self.context['request'].user
|
||||||
@@ -1393,13 +1435,23 @@ class ShoppingListRecipeSerializer(serializers.ModelSerializer):
|
|||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
model = ShoppingListRecipe
|
model = ShoppingListRecipe
|
||||||
fields = ('id', 'name', 'recipe', 'recipe_data', 'mealplan', 'meal_plan_data', 'servings', 'created_by',)
|
fields = ('id', 'name', 'recipe', 'recipe_data', 'meal_plan_data', 'mealplan', 'servings', 'created_by',)
|
||||||
read_only_fields = ('id', 'created_by',)
|
read_only_fields = ('id', 'created_by',)
|
||||||
|
|
||||||
|
|
||||||
|
class FoodShoppingSerializer(serializers.ModelSerializer):
|
||||||
|
supermarket_category = SupermarketCategorySerializer(read_only=True)
|
||||||
|
shopping_lists = ShoppingListSerializer(read_only=True, many=True)
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
model = Food
|
||||||
|
fields = ('id', 'name', 'plural_name', 'supermarket_category', 'shopping_lists')
|
||||||
|
|
||||||
|
|
||||||
class ShoppingListEntrySerializer(WritableNestedModelSerializer):
|
class ShoppingListEntrySerializer(WritableNestedModelSerializer):
|
||||||
food = FoodSerializer(allow_null=True)
|
food = FoodShoppingSerializer(allow_null=True)
|
||||||
unit = UnitSerializer(allow_null=True, required=False)
|
unit = UnitSerializer(allow_null=True, required=False)
|
||||||
|
shopping_lists = ShoppingListSerializer(many=True, required=False)
|
||||||
list_recipe_data = ShoppingListRecipeSerializer(source='list_recipe', read_only=True)
|
list_recipe_data = ShoppingListRecipeSerializer(source='list_recipe', read_only=True)
|
||||||
amount = CustomDecimalField()
|
amount = CustomDecimalField()
|
||||||
created_by = UserSerializer(read_only=True)
|
created_by = UserSerializer(read_only=True)
|
||||||
@@ -1448,7 +1500,13 @@ class ShoppingListEntrySerializer(WritableNestedModelSerializer):
|
|||||||
created_by=self.context['request'].user)
|
created_by=self.context['request'].user)
|
||||||
del validated_data['mealplan_id']
|
del validated_data['mealplan_id']
|
||||||
|
|
||||||
return super().create(validated_data)
|
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
|
||||||
|
|
||||||
def update(self, instance, validated_data):
|
def update(self, instance, validated_data):
|
||||||
user = self.context['request'].user
|
user = self.context['request'].user
|
||||||
@@ -1468,7 +1526,7 @@ class ShoppingListEntrySerializer(WritableNestedModelSerializer):
|
|||||||
class Meta:
|
class Meta:
|
||||||
model = ShoppingListEntry
|
model = ShoppingListEntry
|
||||||
fields = (
|
fields = (
|
||||||
'id', 'list_recipe', 'food', 'unit', 'amount', 'order', 'checked', 'ingredient',
|
'id', 'list_recipe', 'shopping_lists', 'food', 'unit', 'amount', 'order', 'checked', 'ingredient',
|
||||||
'list_recipe_data', 'created_by', 'created_at', 'updated_at', 'completed_at', 'delay_until', 'mealplan_id'
|
'list_recipe_data', 'created_by', 'created_at', 'updated_at', 'completed_at', 'delay_until', 'mealplan_id'
|
||||||
)
|
)
|
||||||
read_only_fields = ('id', 'created_by', 'created_at')
|
read_only_fields = ('id', 'created_by', 'created_at')
|
||||||
@@ -1729,6 +1787,7 @@ class GenericModelReferenceSerializer(serializers.Serializer):
|
|||||||
model = serializers.CharField()
|
model = serializers.CharField()
|
||||||
name = serializers.CharField()
|
name = serializers.CharField()
|
||||||
|
|
||||||
|
|
||||||
# Export/Import Serializers
|
# Export/Import Serializers
|
||||||
|
|
||||||
class KeywordExportSerializer(KeywordSerializer):
|
class KeywordExportSerializer(KeywordSerializer):
|
||||||
|
|||||||
@@ -40,6 +40,7 @@ router.register(r'recipe-book-entry', api.RecipeBookEntryViewSet)
|
|||||||
router.register(r'unit-conversion', api.UnitConversionViewSet)
|
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-type', api.PropertyTypeViewSet) # NOTE: if regenerating the legacy API these need renamed to food-property
|
||||||
router.register(r'property', api.PropertyViewSet)
|
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-entry', api.ShoppingListEntryViewSet)
|
||||||
router.register(r'shopping-list-recipe', api.ShoppingListRecipeViewSet)
|
router.register(r'shopping-list-recipe', api.ShoppingListRecipeViewSet)
|
||||||
router.register(r'space', api.SpaceViewSet)
|
router.register(r'space', api.SpaceViewSet)
|
||||||
|
|||||||
@@ -88,7 +88,7 @@ from cookbook.models import (Automation, BookmarkletImport, ConnectorConfig, Coo
|
|||||||
RecipeBookEntry, ShareLink, ShoppingListEntry,
|
RecipeBookEntry, ShareLink, ShoppingListEntry,
|
||||||
ShoppingListRecipe, Space, Step, Storage, Supermarket, SupermarketCategory,
|
ShoppingListRecipe, Space, Step, Storage, Supermarket, SupermarketCategory,
|
||||||
SupermarketCategoryRelation, Sync, SyncLog, Unit, UnitConversion,
|
SupermarketCategoryRelation, Sync, SyncLog, Unit, UnitConversion,
|
||||||
UserFile, UserPreference, UserSpace, ViewLog, RecipeImport, SearchPreference, SearchFields, AiLog, AiProvider
|
UserFile, UserPreference, UserSpace, ViewLog, RecipeImport, SearchPreference, SearchFields, AiLog, AiProvider, ShoppingList
|
||||||
)
|
)
|
||||||
from cookbook.provider.dropbox import Dropbox
|
from cookbook.provider.dropbox import Dropbox
|
||||||
from cookbook.provider.local import Local
|
from cookbook.provider.local import Local
|
||||||
@@ -114,7 +114,7 @@ from cookbook.serializer import (AccessTokenSerializer, AutomationSerializer, Au
|
|||||||
LocalizationSerializer, ServerSettingsSerializer, RecipeFromSourceResponseSerializer, ShoppingListEntryBulkCreateSerializer, FdcQuerySerializer,
|
LocalizationSerializer, ServerSettingsSerializer, RecipeFromSourceResponseSerializer, ShoppingListEntryBulkCreateSerializer, FdcQuerySerializer,
|
||||||
AiImportSerializer, ImportOpenDataSerializer, ImportOpenDataMetaDataSerializer, ImportOpenDataResponseSerializer, ExportRequestSerializer,
|
AiImportSerializer, ImportOpenDataSerializer, ImportOpenDataMetaDataSerializer, ImportOpenDataResponseSerializer, ExportRequestSerializer,
|
||||||
RecipeImportSerializer, ConnectorConfigSerializer, SearchPreferenceSerializer, SearchFieldsSerializer, RecipeBatchUpdateSerializer,
|
RecipeImportSerializer, ConnectorConfigSerializer, SearchPreferenceSerializer, SearchFieldsSerializer, RecipeBatchUpdateSerializer,
|
||||||
AiProviderSerializer, AiLogSerializer, FoodBatchUpdateSerializer, GenericModelReferenceSerializer
|
AiProviderSerializer, AiLogSerializer, FoodBatchUpdateSerializer, GenericModelReferenceSerializer, ShoppingListSerializer
|
||||||
)
|
)
|
||||||
from cookbook.version_info import TANDOOR_VERSION
|
from cookbook.version_info import TANDOOR_VERSION
|
||||||
from cookbook.views.import_export import get_integration
|
from cookbook.views.import_export import get_integration
|
||||||
@@ -307,7 +307,8 @@ class FuzzyFilterMixin(viewsets.ModelViewSet, ExtendedRecipeMixin):
|
|||||||
filter = Q(name__icontains=query)
|
filter = Q(name__icontains=query)
|
||||||
if self.request.user.is_authenticated:
|
if self.request.user.is_authenticated:
|
||||||
if any([self.model.__name__.lower() in x for x in
|
if any([self.model.__name__.lower() in x for x in
|
||||||
self.request.user.searchpreference.unaccent.values_list('field', flat=True)]):
|
self.request.user.searchpreference.unaccent.values_list('field', flat=True)]) and (
|
||||||
|
settings.DATABASES['default']['ENGINE'] == 'django.db.backends.postgresql'):
|
||||||
filter |= Q(name__unaccent__icontains=query)
|
filter |= Q(name__unaccent__icontains=query)
|
||||||
|
|
||||||
self.queryset = (
|
self.queryset = (
|
||||||
@@ -1991,25 +1992,39 @@ class ShoppingListRecipeViewSet(LoggingMixin, viewsets.ModelViewSet):
|
|||||||
if serializer.is_valid():
|
if serializer.is_valid():
|
||||||
entries = []
|
entries = []
|
||||||
for e in serializer.validated_data['entries']:
|
for e in serializer.validated_data['entries']:
|
||||||
entries.append(
|
entry = ShoppingListEntry(
|
||||||
ShoppingListEntry(
|
list_recipe_id=obj.pk,
|
||||||
list_recipe_id=obj.pk,
|
amount=e['amount'],
|
||||||
amount=e['amount'],
|
unit_id=e['unit_id'],
|
||||||
unit_id=e['unit_id'],
|
food_id=e['food_id'],
|
||||||
food_id=e['food_id'],
|
ingredient_id=e['ingredient_id'],
|
||||||
ingredient_id=e['ingredient_id'],
|
created_by_id=request.user.id,
|
||||||
created_by_id=request.user.id,
|
space_id=request.space.id,
|
||||||
space_id=request.space.id,
|
|
||||||
)
|
|
||||||
)
|
)
|
||||||
|
entries.append(entry)
|
||||||
|
|
||||||
ShoppingListEntry.objects.bulk_create(entries)
|
ShoppingListEntry.objects.bulk_create(entries)
|
||||||
|
for e in entries:
|
||||||
|
if e.food.shopping_lists.count() > 0:
|
||||||
|
e.shopping_lists.set(e.food.shopping_lists.all())
|
||||||
|
|
||||||
ConnectorManager.add_work(ActionType.CREATED, *entries)
|
ConnectorManager.add_work(ActionType.CREATED, *entries)
|
||||||
return Response(serializer.validated_data)
|
return Response(serializer.validated_data)
|
||||||
else:
|
else:
|
||||||
return Response(serializer.errors, 400)
|
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=[
|
@extend_schema_view(list=extend_schema(parameters=[
|
||||||
OpenApiParameter(name='updated_after',
|
OpenApiParameter(name='updated_after',
|
||||||
description=_('Returns only elements updated after the given timestamp in ISO 8601 format.'),
|
description=_('Returns only elements updated after the given timestamp in ISO 8601 format.'),
|
||||||
@@ -2029,19 +2044,23 @@ class ShoppingListEntryViewSet(LoggingMixin, viewsets.ModelViewSet):
|
|||||||
def get_queryset(self):
|
def get_queryset(self):
|
||||||
self.queryset = self.queryset.filter(space=self.request.space)
|
self.queryset = self.queryset.filter(space=self.request.space)
|
||||||
|
|
||||||
|
# select_related("list_recipe")
|
||||||
self.queryset = self.queryset.filter(
|
self.queryset = self.queryset.filter(
|
||||||
Q(created_by=self.request.user)
|
Q(created_by=self.request.user)
|
||||||
| Q(created_by__in=list(self.request.user.get_shopping_share()))).prefetch_related('created_by', 'food',
|
| Q(created_by__in=list(self.request.user.get_shopping_share()))).prefetch_related('created_by',
|
||||||
'food__properties',
|
'food',
|
||||||
'food__properties__property_type',
|
'food__shopping_lists',
|
||||||
'food__inherit_fields',
|
'shopping_lists',
|
||||||
'food__supermarket_category',
|
'unit',
|
||||||
'food__onhand_users',
|
'list_recipe',
|
||||||
'food__substitute',
|
'list_recipe__recipe__keywords',
|
||||||
'food__child_inherit_fields',
|
'list_recipe__recipe__created_by',
|
||||||
'unit', 'list_recipe',
|
|
||||||
'list_recipe__mealplan',
|
'list_recipe__mealplan',
|
||||||
|
'list_recipe__mealplan__shared',
|
||||||
|
'list_recipe__mealplan__shared__userspace_set',
|
||||||
|
'list_recipe__mealplan__shoppinglistrecipe_set',
|
||||||
'list_recipe__mealplan__recipe',
|
'list_recipe__mealplan__recipe',
|
||||||
|
'list_recipe__mealplan__recipe__keywords',
|
||||||
).distinct().all()
|
).distinct().all()
|
||||||
|
|
||||||
updated_after = self.request.query_params.get('updated_after', None)
|
updated_after = self.request.query_params.get('updated_after', None)
|
||||||
@@ -3056,11 +3075,20 @@ def meal_plans_to_ical(queryset, filename):
|
|||||||
for p in queryset:
|
for p in queryset:
|
||||||
event = Event()
|
event = Event()
|
||||||
event['uid'] = p.id
|
event['uid'] = p.id
|
||||||
event.add('dtstart', p.from_date)
|
|
||||||
|
start_date_time = p.from_date
|
||||||
|
end_date_time = p.from_date
|
||||||
|
|
||||||
if p.to_date:
|
if p.to_date:
|
||||||
event.add('dtend', p.to_date)
|
end_date_time = p.to_date
|
||||||
else:
|
|
||||||
event.add('dtend', p.from_date)
|
if p.meal_type.time:
|
||||||
|
start_date_time = datetime.datetime.combine(p.from_date, p.meal_type.time)
|
||||||
|
end_date_time = datetime.datetime.combine(p.to_date, p.meal_type.time) + datetime.timedelta(minutes=60)
|
||||||
|
|
||||||
|
event.add('dtstart', start_date_time)
|
||||||
|
event.add('dtend', end_date_time)
|
||||||
|
|
||||||
event['summary'] = f'{p.meal_type.name}: {p.get_label()}'
|
event['summary'] = f'{p.meal_type.name}: {p.get_label()}'
|
||||||
event['description'] = p.note
|
event['description'] = p.note
|
||||||
cal.add_component(event)
|
cal.add_component(event)
|
||||||
|
|||||||
@@ -43,7 +43,10 @@ def index(request, path=None, resource=None):
|
|||||||
return HttpResponseRedirect(reverse_lazy('view_setup'))
|
return HttpResponseRedirect(reverse_lazy('view_setup'))
|
||||||
|
|
||||||
if 'signup_token' in request.session:
|
if 'signup_token' in request.session:
|
||||||
return HttpResponseRedirect(reverse('view_invite', args=[request.session.pop('signup_token', '')]))
|
value = request.session['signup_token']
|
||||||
|
del request.session['signup_token']
|
||||||
|
request.session.modified = True
|
||||||
|
return HttpResponseRedirect(reverse('view_invite', args=[value]))
|
||||||
|
|
||||||
if request.user.is_authenticated or re.search(r'/recipe/\d+/', request.path[:512]) and request.GET.get('share'):
|
if request.user.is_authenticated or re.search(r'/recipe/\d+/', request.path[:512]) and request.GET.get('share'):
|
||||||
return render(request, 'frontend/tandoor.html', {})
|
return render(request, 'frontend/tandoor.html', {})
|
||||||
|
|||||||
@@ -675,4 +675,4 @@ DISABLE_EXTERNAL_CONNECTORS = extract_bool('DISABLE_EXTERNAL_CONNECTORS', False)
|
|||||||
EXTERNAL_CONNECTORS_QUEUE_SIZE = int(os.getenv('EXTERNAL_CONNECTORS_QUEUE_SIZE', 100))
|
EXTERNAL_CONNECTORS_QUEUE_SIZE = int(os.getenv('EXTERNAL_CONNECTORS_QUEUE_SIZE', 100))
|
||||||
|
|
||||||
mimetypes.add_type("text/javascript", ".js", True)
|
mimetypes.add_type("text/javascript", ".js", True)
|
||||||
mimetypes.add_type("text/javascript", ".mjs", True)
|
mimetypes.add_type("text/javascript", ".mjs", True)
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
Django==5.2.8
|
Django==5.2.9
|
||||||
cryptography===45.0.5
|
cryptography===45.0.5
|
||||||
django-annoying==0.10.6
|
django-annoying==0.10.6
|
||||||
django-cleanup==9.0.0
|
django-cleanup==9.0.0
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
<template>
|
<template>
|
||||||
|
|
||||||
<v-dialog v-model="dialog" activator="parent" style="max-width: 75vw;">
|
<v-dialog v-model="dialog" :activator="props.activator" style="max-width: 75vw;">
|
||||||
<v-card>
|
<v-card>
|
||||||
|
|
||||||
<v-closable-card-title :title="$t('Export')" v-model="dialog"></v-closable-card-title>
|
<v-closable-card-title :title="$t('Export')" v-model="dialog"></v-closable-card-title>
|
||||||
@@ -48,7 +48,7 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
|
|
||||||
import VClosableCardTitle from "@/components/dialogs/VClosableCardTitle.vue";
|
import VClosableCardTitle from "@/components/dialogs/VClosableCardTitle.vue";
|
||||||
import {computed, ref} from "vue";
|
import {computed, PropType, ref, shallowRef} from "vue";
|
||||||
import {useShoppingStore} from "@/stores/ShoppingStore.ts";
|
import {useShoppingStore} from "@/stores/ShoppingStore.ts";
|
||||||
import {isEntryVisible, isShoppingCategoryVisible, isShoppingListFoodVisible} from "@/utils/logic_utils.ts";
|
import {isEntryVisible, isShoppingCategoryVisible, isShoppingListFoodVisible} from "@/utils/logic_utils.ts";
|
||||||
import {useI18n} from "vue-i18n";
|
import {useI18n} from "vue-i18n";
|
||||||
@@ -56,10 +56,15 @@ import {useUserPreferenceStore} from "@/stores/UserPreferenceStore.ts";
|
|||||||
import {ShoppingListEntry} from "@/openapi";
|
import {ShoppingListEntry} from "@/openapi";
|
||||||
import BtnCopy from "@/components/buttons/BtnCopy.vue";
|
import BtnCopy from "@/components/buttons/BtnCopy.vue";
|
||||||
import {useClipboard} from "@vueuse/core";
|
import {useClipboard} from "@vueuse/core";
|
||||||
|
import {EditorSupportedModels, getGenericModelFromString} from "@/types/Models.ts";
|
||||||
|
|
||||||
const {t} = useI18n()
|
const {t} = useI18n()
|
||||||
const {copy} = useClipboard()
|
const {copy} = useClipboard()
|
||||||
|
|
||||||
|
const props = defineProps({
|
||||||
|
activator: {default: 'parent'},
|
||||||
|
})
|
||||||
|
|
||||||
const dialog = defineModel<boolean>()
|
const dialog = defineModel<boolean>()
|
||||||
const mode = ref<'md_list' | 'md_table' | 'csv'>('md_list')
|
const mode = ref<'md_list' | 'md_table' | 'csv'>('md_list')
|
||||||
|
|
||||||
@@ -78,7 +83,7 @@ const exportText = computed(() => {
|
|||||||
|
|
||||||
textArray.push(formatHeader())
|
textArray.push(formatHeader())
|
||||||
|
|
||||||
useShoppingStore().getEntriesByGroup.forEach(category => {
|
useShoppingStore().entriesByGroup.forEach(category => {
|
||||||
if (isShoppingCategoryVisible(category)) {
|
if (isShoppingCategoryVisible(category)) {
|
||||||
if (category.name === useShoppingStore().UNDEFINED_CATEGORY) {
|
if (category.name === useShoppingStore().UNDEFINED_CATEGORY) {
|
||||||
textArray.push(formatCategory(t('NoCategory')))
|
textArray.push(formatCategory(t('NoCategory')))
|
||||||
|
|||||||
@@ -8,6 +8,9 @@
|
|||||||
<v-label>{{ $t('Choose_Category') }}</v-label>
|
<v-label>{{ $t('Choose_Category') }}</v-label>
|
||||||
<model-select model="SupermarketCategory" @update:modelValue="categoryUpdate" allow-create></model-select>
|
<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-row>
|
||||||
<v-col class="pr-0">
|
<v-col class="pr-0">
|
||||||
<v-btn height="80px" color="info" density="compact" size="small" block stacked
|
<v-btn height="80px" color="info" density="compact" size="small" block stacked
|
||||||
@@ -76,6 +79,9 @@
|
|||||||
<v-list-item-subtitle v-if="isDelayed(e)" class="text-info font-weight-bold">
|
<v-list-item-subtitle v-if="isDelayed(e)" class="text-info font-weight-bold">
|
||||||
{{ $t('PostponedUntil') }} {{ DateTime.fromJSDate(e.delayUntil!).toLocaleString(DateTime.DATETIME_SHORT) }}
|
{{ $t('PostponedUntil') }} {{ DateTime.fromJSDate(e.delayUntil!).toLocaleString(DateTime.DATETIME_SHORT) }}
|
||||||
</v-list-item-subtitle>
|
</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-group divided border>
|
||||||
<v-btn icon="" @click="e.amount = e.amount / 2; updateEntryAmount(e)" v-if="!e.ingredient">
|
<v-btn icon="" @click="e.amount = e.amount / 2; updateEntryAmount(e)" v-if="!e.ingredient">
|
||||||
@@ -122,8 +128,8 @@
|
|||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
|
|
||||||
import {computed} from "vue";
|
import {computed, ref} from "vue";
|
||||||
import {ApiApi, PatchedShoppingListEntry, ShoppingListEntry, SupermarketCategory} from "@/openapi";
|
import {ApiApi, PatchedShoppingListEntry, ShoppingList, ShoppingListEntry, SupermarketCategory} from "@/openapi";
|
||||||
import ModelSelect from "@/components/inputs/ModelSelect.vue";
|
import ModelSelect from "@/components/inputs/ModelSelect.vue";
|
||||||
import {IShoppingListFood} from "@/types/Shopping";
|
import {IShoppingListFood} from "@/types/Shopping";
|
||||||
import VClosableCardTitle from "@/components/dialogs/VClosableCardTitle.vue";
|
import VClosableCardTitle from "@/components/dialogs/VClosableCardTitle.vue";
|
||||||
@@ -133,12 +139,16 @@ import ModelEditDialog from "@/components/dialogs/ModelEditDialog.vue";
|
|||||||
import {useShoppingStore} from "@/stores/ShoppingStore";
|
import {useShoppingStore} from "@/stores/ShoppingStore";
|
||||||
import {isDelayed, isShoppingListFoodDelayed} from "@/utils/logic_utils";
|
import {isDelayed, isShoppingListFoodDelayed} from "@/utils/logic_utils";
|
||||||
import {ErrorMessageType, PreparedMessage, useMessageStore} from "@/stores/MessageStore";
|
import {ErrorMessageType, PreparedMessage, useMessageStore} from "@/stores/MessageStore";
|
||||||
|
import ShoppingListsBar from "@/components/display/ShoppingListsBar.vue";
|
||||||
|
import {useUserPreferenceStore} from "@/stores/UserPreferenceStore.ts";
|
||||||
|
|
||||||
const {mobile} = useDisplay()
|
const {mobile} = useDisplay()
|
||||||
|
|
||||||
const showDialog = defineModel<Boolean>()
|
const showDialog = defineModel<Boolean>()
|
||||||
const shoppingListFood = defineModel<IShoppingListFood>('shoppingListFood', {required: true})
|
const shoppingListFood = defineModel<IShoppingListFood>('shoppingListFood', {required: true})
|
||||||
|
|
||||||
|
const shoppingListUpdateLoading = ref(false)
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* returns a flat list of entries for the given shopping list food
|
* returns a flat list of entries for the given shopping list food
|
||||||
*/
|
*/
|
||||||
@@ -164,6 +174,8 @@ const isShoppingLineDelayed = computed(() => {
|
|||||||
function categoryUpdate(category: SupermarketCategory) {
|
function categoryUpdate(category: SupermarketCategory) {
|
||||||
const api = new ApiApi()
|
const api = new ApiApi()
|
||||||
shoppingListFood.value.food.supermarketCategory = category
|
shoppingListFood.value.food.supermarketCategory = category
|
||||||
|
shoppingListFood.value.entries.forEach(e => e.food.supermarketCategory = category)
|
||||||
|
useShoppingStore().updateEntriesStructure()
|
||||||
api.apiFoodUpdate({id: shoppingListFood.value.food.id, food: shoppingListFood.value.food}).then(r => {
|
api.apiFoodUpdate({id: shoppingListFood.value.food.id, food: shoppingListFood.value.food}).then(r => {
|
||||||
useMessageStore().addPreparedMessage(PreparedMessage.UPDATE_SUCCESS)
|
useMessageStore().addPreparedMessage(PreparedMessage.UPDATE_SUCCESS)
|
||||||
}).catch(err => {
|
}).catch(err => {
|
||||||
@@ -171,6 +183,35 @@ 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
|
* add new entry for currently selected food type
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -10,15 +10,15 @@
|
|||||||
<!-- <v-text-field density="compact" variant="outlined" class="pt-2 pb-2" :label="$t('Search')" hide-details clearable></v-text-field>-->
|
<!-- <v-text-field density="compact" variant="outlined" class="pt-2 pb-2" :label="$t('Search')" hide-details clearable></v-text-field>-->
|
||||||
<!-- </v-list-item>-->
|
<!-- </v-list-item>-->
|
||||||
<!-- <v-divider></v-divider>-->
|
<!-- <v-divider></v-divider>-->
|
||||||
<v-list-item link title="Start" @click="window = 'start'" prepend-icon="fa-solid fa-house"></v-list-item>
|
<v-list-item link :title="$t('Start')" @click="window = 'start'" prepend-icon="fa-solid fa-house"></v-list-item>
|
||||||
<v-list-item link title="Space" @click="window = 'space'" prepend-icon="fa-solid fa-database"></v-list-item>
|
<v-list-item link :title="$t('Space')" @click="window = 'space'" prepend-icon="fa-solid fa-database"></v-list-item>
|
||||||
<v-list-item link :title="$t('Recipes')" @click="window = 'recipes'" prepend-icon="$recipes"></v-list-item>
|
<v-list-item link :title="$t('Recipes')" @click="window = 'recipes'" prepend-icon="$recipes"></v-list-item>
|
||||||
<v-list-item link :title="$t('Import')" @click="window = 'import'" prepend-icon="$import"></v-list-item>
|
<v-list-item link :title="$t('Import')" @click="window = 'import'" prepend-icon="$import"></v-list-item>
|
||||||
<v-list-item link :title="$t('AI')" @click="window = 'ai'" prepend-icon="$ai"></v-list-item>
|
<v-list-item link :title="$t('AI')" @click="window = 'ai'" prepend-icon="$ai"></v-list-item>
|
||||||
<v-list-item link :title="$t('Unit')" @click="window = 'unit'" prepend-icon="fa-solid fa-scale-balanced"></v-list-item>
|
<v-list-item link :title="$t('Unit')" @click="window = 'unit'" prepend-icon="fa-solid fa-scale-balanced"></v-list-item>
|
||||||
<v-list-item link :title="$t('Food')" @click="window = 'food'" prepend-icon="fa-solid fa-carrot"></v-list-item>
|
<v-list-item link :title="$t('Food')" @click="window = 'food'" prepend-icon="fa-solid fa-carrot"></v-list-item>
|
||||||
<v-list-item link :title="$t('Keyword')" @click="window = 'keyword'" prepend-icon="fa-solid fa-tags"></v-list-item>
|
<v-list-item link :title="$t('Keyword')" @click="window = 'keyword'" prepend-icon="fa-solid fa-tags"></v-list-item>
|
||||||
<v-list-item link title="Recipe Structure" @click="window = 'recipe_structure'" prepend-icon="fa-solid fa-diagram-project"></v-list-item>
|
<v-list-item link :title="$t('Recipe Structure')" @click="window = 'recipe_structure'" prepend-icon="fa-solid fa-diagram-project"></v-list-item>
|
||||||
<v-list-item link :title="$t('Properties')" @click="window = 'properties'" prepend-icon="fa-solid fa-database"></v-list-item>
|
<v-list-item link :title="$t('Properties')" @click="window = 'properties'" prepend-icon="fa-solid fa-database"></v-list-item>
|
||||||
<v-list-item link :title="$t('Search')" @click="window = 'recipe_search'" prepend-icon="$search"></v-list-item>
|
<v-list-item link :title="$t('Search')" @click="window = 'recipe_search'" prepend-icon="$search"></v-list-item>
|
||||||
<v-list-item link :title="$t('SavedSearch')" @click="window = 'search_filter'" prepend-icon="fa-solid fa-sd-card"></v-list-item>
|
<v-list-item link :title="$t('SavedSearch')" @click="window = 'search_filter'" prepend-icon="fa-solid fa-sd-card"></v-list-item>
|
||||||
@@ -31,6 +31,8 @@
|
|||||||
|
|
||||||
<v-main>
|
<v-main>
|
||||||
<v-container>
|
<v-container>
|
||||||
|
<v-select v-model="window" :items="mobileMenuItems" class="d-block d-lg-none"> </v-select>
|
||||||
|
|
||||||
<v-window v-model="window">
|
<v-window v-model="window">
|
||||||
<v-window-item value="start">
|
<v-window-item value="start">
|
||||||
<h2>Welcome to Tandoor 2</h2>
|
<h2>Welcome to Tandoor 2</h2>
|
||||||
@@ -46,7 +48,8 @@
|
|||||||
<v-btn class="mt-2 ms-2" color="info" href="https://github.com/TandoorRecipes/recipes" target="_blank" prepend-icon="fa-solid fa-code-branch">GitHub
|
<v-btn class="mt-2 ms-2" color="info" href="https://github.com/TandoorRecipes/recipes" target="_blank" prepend-icon="fa-solid fa-code-branch">GitHub
|
||||||
</v-btn>
|
</v-btn>
|
||||||
|
|
||||||
<v-alert class="mt-3" border="start" variant="tonal" color="success" v-if="(!useUserPreferenceStore().serverSettings.hosted && !useUserPreferenceStore().activeSpace.demo)">
|
<v-alert class="mt-3" border="start" variant="tonal" color="success"
|
||||||
|
v-if="(!useUserPreferenceStore().serverSettings.hosted && !useUserPreferenceStore().activeSpace.demo)">
|
||||||
<v-alert-title>Did you know?</v-alert-title>
|
<v-alert-title>Did you know?</v-alert-title>
|
||||||
Tandoor is Open Source and available to anyone for free to host on their own server. Thousands of hours have been spend
|
Tandoor is Open Source and available to anyone for free to host on their own server. Thousands of hours have been spend
|
||||||
making Tandoor what it is today. You can help make Tandoor even better by contributing or helping financing the effort.
|
making Tandoor what it is today. You can help make Tandoor even better by contributing or helping financing the effort.
|
||||||
@@ -60,10 +63,12 @@
|
|||||||
|
|
||||||
</v-window-item>
|
</v-window-item>
|
||||||
<v-window-item value="space">
|
<v-window-item value="space">
|
||||||
<p class="mt-3">All your data is stored in a Space where you can invite other people to collaborate on your recipe database. Typcially the members of a space
|
<p class="mt-3">All your data is stored in a Space where you can invite other people to collaborate on your recipe database. Typcially the members of a
|
||||||
|
space
|
||||||
belong to one family/household/organization.</p>
|
belong to one family/household/organization.</p>
|
||||||
|
|
||||||
<p class="mt-3">While everyone can access all recipes by default, Books, Shopping Lists and Mealplans are not shared by default. You can share them with other
|
<p class="mt-3">While everyone can access all recipes by default, Books, Shopping Lists and Mealplans are not shared by default. You can share them with
|
||||||
|
other
|
||||||
members of your space
|
members of your space
|
||||||
using the settings.
|
using the settings.
|
||||||
</p>
|
</p>
|
||||||
@@ -77,19 +82,24 @@
|
|||||||
|
|
||||||
</v-window-item>
|
</v-window-item>
|
||||||
<v-window-item value="recipes">
|
<v-window-item value="recipes">
|
||||||
<p class="mt-3">Recipes are the foundation of your Tandoor space. A Recipe has one or more steps that contain ingredients, instructions and other information.
|
<p class="mt-3">Recipes are the foundation of your Tandoor space. A Recipe has one or more steps that contain ingredients, instructions and other
|
||||||
|
information.
|
||||||
Ingredients in turn consist of an amount, a unit and a food, allowing recipes to be scaled, nutrition's to be calculated and shopping to be organized.
|
Ingredients in turn consist of an amount, a unit and a food, allowing recipes to be scaled, nutrition's to be calculated and shopping to be organized.
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
<p class="mt-3">Besides manually creating them you can also import them from various different places.
|
<p class="mt-3">Besides manually creating them you can also import them from various different places.
|
||||||
</p>
|
</p>
|
||||||
<p class="mt-3">Recipes, by default, are visible to all members of your space. Setting them to private means only you can see it. After setting it to private you
|
<p class="mt-3">Recipes, by default, are visible to all members of your space. Setting them to private means only you can see it. After setting it to
|
||||||
|
private you
|
||||||
can manually specify the people who should be able to view the recipe.
|
can manually specify the people who should be able to view the recipe.
|
||||||
You can also create a share link for the recipe to share it with everyone that has access to the link.
|
You can also create a share link for the recipe to share it with everyone that has access to the link.
|
||||||
</p>
|
</p>
|
||||||
<p class="mt-3"></p>
|
<p class="mt-3"></p>
|
||||||
|
|
||||||
<v-btn color="primary" variant="tonal" prepend-icon="$create" class="me-2" :to="{name: 'ModelEditPage', params: {model: 'Recipe'}}">{{ $t('Create') }}</v-btn>
|
<v-btn color="primary" variant="tonal" prepend-icon="$create" class="me-2" :to="{name: 'ModelEditPage', params: {model: 'Recipe'}}">{{
|
||||||
|
$t('Create')
|
||||||
|
}}
|
||||||
|
</v-btn>
|
||||||
<v-btn color="primary" variant="tonal" prepend-icon="$search" class="me-2" :to="{name: 'SearchPage'}">{{ $t('Search') }}</v-btn>
|
<v-btn color="primary" variant="tonal" prepend-icon="$search" class="me-2" :to="{name: 'SearchPage'}">{{ $t('Search') }}</v-btn>
|
||||||
|
|
||||||
</v-window-item>
|
</v-window-item>
|
||||||
@@ -119,7 +129,8 @@
|
|||||||
</p>
|
</p>
|
||||||
|
|
||||||
<p class="mt-3" v-if="useUserPreferenceStore().serverSettings.hosted">
|
<p class="mt-3" v-if="useUserPreferenceStore().serverSettings.hosted">
|
||||||
To prevent accidental AI cost you can review your AI usage using the AI Log. The Server Administrator can also set AI usage limits for your space (either monthly or using a balance).
|
To prevent accidental AI cost you can review your AI usage using the AI Log. The Server Administrator can also set AI usage limits for your space
|
||||||
|
(either monthly or using a balance).
|
||||||
</p>
|
</p>
|
||||||
<p class="mt-3" v-if="!useUserPreferenceStore().serverSettings.hosted">
|
<p class="mt-3" v-if="!useUserPreferenceStore().serverSettings.hosted">
|
||||||
Depending on your subscription you will have different AI Credits available for your space every month. Additionally you might have a Credit balance
|
Depending on your subscription you will have different AI Credits available for your space every month. Additionally you might have a Credit balance
|
||||||
@@ -153,7 +164,8 @@
|
|||||||
<v-btn color="primary" variant="tonal" prepend-icon="fa-solid fa-scale-balanced" class="me-2" :to="{name: 'ModelListPage', params: {model: 'Unit'}}">
|
<v-btn color="primary" variant="tonal" prepend-icon="fa-solid fa-scale-balanced" class="me-2" :to="{name: 'ModelListPage', params: {model: 'Unit'}}">
|
||||||
{{ $t('Unit') }}
|
{{ $t('Unit') }}
|
||||||
</v-btn>
|
</v-btn>
|
||||||
<v-btn color="primary" variant="tonal" prepend-icon="fa-solid fa-exchange-alt" class="me-2" :to="{name: 'ModelListPage', params: {model: 'UnitConversion'}}">
|
<v-btn color="primary" variant="tonal" prepend-icon="fa-solid fa-exchange-alt" class="me-2"
|
||||||
|
:to="{name: 'ModelListPage', params: {model: 'UnitConversion'}}">
|
||||||
{{ $t('Conversion') }}
|
{{ $t('Conversion') }}
|
||||||
</v-btn>
|
</v-btn>
|
||||||
|
|
||||||
@@ -223,7 +235,8 @@
|
|||||||
calculate the property amount if a Food is given in a different unit (e.g. 1kg or 1 cup).
|
calculate the property amount if a Food is given in a different unit (e.g. 1kg or 1 cup).
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
<v-btn color="primary" variant="tonal" prepend-icon="fa-solid fa-database" class="me-2 mt-2 mb-2" :to="{name: 'ModelListPage', params: {model: 'PropertyType'}}">
|
<v-btn color="primary" variant="tonal" prepend-icon="fa-solid fa-database" class="me-2 mt-2 mb-2"
|
||||||
|
:to="{name: 'ModelListPage', params: {model: 'PropertyType'}}">
|
||||||
{{ $t('Property') }}
|
{{ $t('Property') }}
|
||||||
</v-btn>
|
</v-btn>
|
||||||
<h3>Editor</h3>
|
<h3>Editor</h3>
|
||||||
@@ -294,7 +307,8 @@
|
|||||||
</p>
|
</p>
|
||||||
|
|
||||||
<p class="mt-3">
|
<p class="mt-3">
|
||||||
You can assign Supermarket Categories to your Foods, either trough the Food Editor or directly by clicking on a Shopping List Entry, to automatically sort the list
|
You can assign Supermarket Categories to your Foods, either trough the Food Editor or directly by clicking on a Shopping List Entry, to automatically
|
||||||
|
sort the list
|
||||||
according to the Category Order defined in the Supermarket.
|
according to the Category Order defined in the Supermarket.
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
@@ -333,7 +347,8 @@
|
|||||||
|
|
||||||
<p class="mt-3">
|
<p class="mt-3">
|
||||||
When selecting a Recipe in a Meal Plan you can automatically add its ingredients to the shopping list. You can also manually add more entries trough the
|
When selecting a Recipe in a Meal Plan you can automatically add its ingredients to the shopping list. You can also manually add more entries trough the
|
||||||
shopping tab in the Meal Plan editor. When deleting a Meal Plan all Shopping List Entries associated with that Meal Plan are deleted as well. When changing the
|
shopping tab in the Meal Plan editor. When deleting a Meal Plan all Shopping List Entries associated with that Meal Plan are deleted as well. When
|
||||||
|
changing the
|
||||||
number of servings in a Meal Plan the Servings of the connected Recipe in the Shopping list are automatically changed as well.
|
number of servings in a Meal Plan the Servings of the connected Recipe in the Shopping list are automatically changed as well.
|
||||||
|
|
||||||
</p>
|
</p>
|
||||||
@@ -368,10 +383,30 @@
|
|||||||
|
|
||||||
import {ref} from "vue";
|
import {ref} from "vue";
|
||||||
import {useUserPreferenceStore} from "@/stores/UserPreferenceStore.ts";
|
import {useUserPreferenceStore} from "@/stores/UserPreferenceStore.ts";
|
||||||
|
import {useI18n} from "vue-i18n";
|
||||||
|
|
||||||
|
const {t} = useI18n()
|
||||||
const drawer = defineModel()
|
const drawer = defineModel()
|
||||||
const window = ref('start')
|
const window = ref('start')
|
||||||
|
|
||||||
|
const mobileMenuItems = ref([
|
||||||
|
{title: t('Start'), props: {prependIcon: 'fa-solid fa-house'}, value: 'start'},
|
||||||
|
{title: t('Space'), props: {prependIcon: 'fa-solid fa-database'}, value: 'space'},
|
||||||
|
{title: t('Recipes'), props: {prependIcon: '$recipes'}, value: 'recipes'},
|
||||||
|
{title: t('Import'), props: {prependIcon: '$import'}, value: 'import'},
|
||||||
|
{title: t('AI'), props: {prependIcon: '$ai'}, value: 'ai'},
|
||||||
|
{title: t('Unit'), props: {prependIcon: 'fa-solid fa-scale-balanced'}, value: 'unit'},
|
||||||
|
{title: t('Food'), props: {prependIcon: 'fa-solid fa-carrot'}, value: 'food'},
|
||||||
|
{title: t('Keyword'), props: {prependIcon: 'fa-solid fa-tags'}, value: 'keyword'},
|
||||||
|
{title: t('RecipeStructure'), props: {prependIcon: 'fa-solid fa-diagram-project'}, value: 'recipe_structure'},
|
||||||
|
{title: t('Properties'), props: {prependIcon: 'fa-solid fa-database'}, value: 'properties'},
|
||||||
|
{title: t('Search'), props: {prependIcon: '$search'}, value: 'recipe_search'},
|
||||||
|
{title: t('SavedSearch'), props: {prependIcon: 'fa-solid fa-sd-card'}, value: 'search_filter'},
|
||||||
|
{title: t('Books'), props: {prependIcon: '$books'}, value: 'books'},
|
||||||
|
{title: t('Shopping'), props: {prependIcon: '$shopping'}, value: 'shopping'},
|
||||||
|
{title: t('Meal_Plan'), props: {prependIcon: '$mealplan'}, value: 'meal_plan'}
|
||||||
|
])
|
||||||
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -146,7 +146,11 @@ onMounted(() => {
|
|||||||
|
|
||||||
function clickMealPlan(plan: MealPlan) {
|
function clickMealPlan(plan: MealPlan) {
|
||||||
if (plan.recipe) {
|
if (plan.recipe) {
|
||||||
router.push({name: 'RecipeViewPage', params: {id: plan.recipe.id}})
|
router.push({
|
||||||
|
name: 'RecipeViewPage',
|
||||||
|
params: { id: String(plan.recipe.id) }, // keep id in params
|
||||||
|
query: { servings: String(plan.servings ?? '') } // pass servings as query
|
||||||
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -37,15 +37,15 @@
|
|||||||
|
|
||||||
<v-dialog max-width="900px" v-model="dialog">
|
<v-dialog max-width="900px" v-model="dialog">
|
||||||
<v-card v-if="dialogProperty" :loading="loading">
|
<v-card v-if="dialogProperty" :loading="loading">
|
||||||
<v-closable-card-title :title="`${dialogProperty.propertyAmountTotal} ${dialogProperty.unit} ${dialogProperty.name}`" :sub-title="$t('total')" icon="$properties"
|
<v-closable-card-title :title="`${dialogProperty.propertyAmountTotal} ${(dialogProperty.unit != null) ? dialogProperty.unit : ''} ${dialogProperty.name}`" :sub-title="$t('total')" icon="$properties"
|
||||||
v-model="dialog"></v-closable-card-title>
|
v-model="dialog"></v-closable-card-title>
|
||||||
<v-card-text>
|
<v-card-text>
|
||||||
<v-list>
|
<v-list>
|
||||||
<v-list-item border v-for="fv in dialogProperty.foodValues" :key="`${dialogProperty.id}_${fv.id}`">
|
<v-list-item border v-for="fv in dialogProperty.foodValues" :key="`${dialogProperty.id}_${fv.id}`">
|
||||||
<template #prepend>
|
<template #prepend>
|
||||||
<v-progress-circular size="55" width="5" :model-value="(fv.value/dialogProperty.propertyAmountTotal)*100"
|
<v-progress-circular size="55" width="5" :model-value="(fv.value* props.ingredientFactor/dialogProperty.propertyAmountTotal)*100"
|
||||||
:color="colorScale((fv.value/dialogProperty.propertyAmountTotal)*100)" v-if="fv.value != null && dialogProperty.propertyAmountTotal > 0">
|
:color="colorScale((fv.value* props.ingredientFactor/dialogProperty.propertyAmountTotal)*100)" v-if="fv.value != null && dialogProperty.propertyAmountTotal > 0">
|
||||||
{{ Math.round((fv.value / dialogProperty.propertyAmountTotal) * 100) }}%
|
{{ Math.round((fv.value* props.ingredientFactor / dialogProperty.propertyAmountTotal) * 100) }}%
|
||||||
</v-progress-circular>
|
</v-progress-circular>
|
||||||
<v-progress-circular size="55" width="5" v-if="fv.value == null">?</v-progress-circular>
|
<v-progress-circular size="55" width="5" v-if="fv.value == null">?</v-progress-circular>
|
||||||
</template>
|
</template>
|
||||||
@@ -59,7 +59,7 @@
|
|||||||
<model-edit-dialog model="UnitConversion" @create="refreshRecipe()"
|
<model-edit-dialog model="UnitConversion" @create="refreshRecipe()"
|
||||||
:item-defaults="{baseAmount: 1, baseUnit: fv.missing_conversion.base_unit, convertedUnit: fv.missing_conversion.converted_unit, food: fv.food}"></model-edit-dialog>
|
:item-defaults="{baseAmount: 1, baseUnit: fv.missing_conversion.base_unit, convertedUnit: fv.missing_conversion.converted_unit, food: fv.food}"></model-edit-dialog>
|
||||||
</v-chip>
|
</v-chip>
|
||||||
<v-chip v-else-if="fv.value != undefined">{{ $n(fv.value) }} {{ dialogProperty.unit }}</v-chip>
|
<v-chip v-else-if="fv.value != undefined">{{ $n(fv.value * props.ingredientFactor) }} {{ dialogProperty.unit }}</v-chip>
|
||||||
<v-chip color="warning" prepend-icon="$edit" class="cursor-pointer" :to="{name: 'ModelEditPage', params: {model: 'Recipe', id: recipe.id}}" v-else-if="fv.missing_unit">
|
<v-chip color="warning" prepend-icon="$edit" class="cursor-pointer" :to="{name: 'ModelEditPage', params: {model: 'Recipe', id: recipe.id}}" v-else-if="fv.missing_unit">
|
||||||
{{ $t('NoUnit') }}
|
{{ $t('NoUnit') }}
|
||||||
</v-chip>
|
</v-chip>
|
||||||
@@ -101,7 +101,10 @@ type PropertyWrapper = {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const props = defineProps({
|
const props = defineProps({
|
||||||
servings: {type: Number, required: true,},
|
ingredientFactor: {
|
||||||
|
type: Number,
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
})
|
})
|
||||||
|
|
||||||
const recipe = defineModel<Recipe>({required: true})
|
const recipe = defineModel<Recipe>({required: true})
|
||||||
@@ -143,7 +146,7 @@ const propertyList = computed(() => {
|
|||||||
description: rp.propertyType.description,
|
description: rp.propertyType.description,
|
||||||
foodValues: [],
|
foodValues: [],
|
||||||
propertyAmountPerServing: rp.propertyAmount,
|
propertyAmountPerServing: rp.propertyAmount,
|
||||||
propertyAmountTotal: rp.propertyAmount * recipe.value.servings * (props.servings / recipe.value.servings),
|
propertyAmountTotal: rp.propertyAmount * recipe.value.servings * props.ingredientFactor,
|
||||||
missingValue: false,
|
missingValue: false,
|
||||||
unit: rp.propertyType.unit,
|
unit: rp.propertyType.unit,
|
||||||
type: rp.propertyType,
|
type: rp.propertyType,
|
||||||
@@ -161,7 +164,7 @@ const propertyList = computed(() => {
|
|||||||
icon: fp.icon,
|
icon: fp.icon,
|
||||||
foodValues: fp.food_values,
|
foodValues: fp.food_values,
|
||||||
propertyAmountPerServing: fp.total_value / recipe.value.servings,
|
propertyAmountPerServing: fp.total_value / recipe.value.servings,
|
||||||
propertyAmountTotal: fp.total_value * (props.servings / recipe.value.servings),
|
propertyAmountTotal: fp.total_value * props.ingredientFactor,
|
||||||
missingValue: fp.missing_value,
|
missingValue: fp.missing_value,
|
||||||
unit: fp.unit,
|
unit: fp.unit,
|
||||||
type: fp,
|
type: fp,
|
||||||
|
|||||||
@@ -69,7 +69,7 @@
|
|||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
|
|
||||||
import {onMounted, PropType, ref} from "vue";
|
import {onMounted, PropType, ref, watch} from "vue";
|
||||||
import {ApiApi, CookLog, Recipe} from "@/openapi";
|
import {ApiApi, CookLog, Recipe} from "@/openapi";
|
||||||
import {DateTime} from "luxon";
|
import {DateTime} from "luxon";
|
||||||
import {ErrorMessageType, useMessageStore} from "@/stores/MessageStore";
|
import {ErrorMessageType, useMessageStore} from "@/stores/MessageStore";
|
||||||
@@ -82,6 +82,10 @@ const props = defineProps({
|
|||||||
type: Object as PropType<Recipe>,
|
type: Object as PropType<Recipe>,
|
||||||
required: true
|
required: true
|
||||||
},
|
},
|
||||||
|
servings: {
|
||||||
|
type: Number,
|
||||||
|
required: true
|
||||||
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
const newCookLog = ref({} as CookLog);
|
const newCookLog = ref({} as CookLog);
|
||||||
@@ -121,7 +125,7 @@ function recLoadCookLog(recipeId: number, page: number = 1) {
|
|||||||
*/
|
*/
|
||||||
function resetForm() {
|
function resetForm() {
|
||||||
newCookLog.value = {} as CookLog
|
newCookLog.value = {} as CookLog
|
||||||
newCookLog.value.servings = props.recipe.servings
|
newCookLog.value.servings = props.servings
|
||||||
newCookLog.value.createdAt = new Date()
|
newCookLog.value.createdAt = new Date()
|
||||||
newCookLog.value.recipe = props.recipe.id!
|
newCookLog.value.recipe = props.recipe.id!
|
||||||
}
|
}
|
||||||
@@ -140,6 +144,13 @@ function saveCookLog() {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* watch for changes in servings prop and update the servings input field
|
||||||
|
*/
|
||||||
|
watch(() => props.servings, (newVal) => {
|
||||||
|
newCookLog.value.servings = newVal
|
||||||
|
})
|
||||||
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style scoped>
|
<style scoped>
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
<template>
|
<template>
|
||||||
<template v-if="!props.loading">
|
<template v-if="!props.loading">
|
||||||
|
|
||||||
<router-link :to="{name: 'RecipeViewPage', params: {id: props.recipe.id}}" :target="linkTarget">
|
<router-link :to="dest" :target="linkTarget">
|
||||||
<recipe-image :style="{height: props.height}" :recipe="props.recipe" rounded="lg" class="mr-3 ml-3">
|
<recipe-image :style="{height: props.height}" :recipe="props.recipe" rounded="lg" class="mr-3 ml-3">
|
||||||
|
|
||||||
</recipe-image>
|
</recipe-image>
|
||||||
@@ -36,7 +36,7 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
||||||
<v-card :to="{name: 'RecipeViewPage', params: {id: props.recipe.id}}" :style="{'height': props.height}" v-if="false">
|
<v-card :to="dest" :style="{'height': props.height}" v-if="false">
|
||||||
<v-tooltip
|
<v-tooltip
|
||||||
class="align-center justify-center"
|
class="align-center justify-center"
|
||||||
location="top center" origin="overlap"
|
location="top center" origin="overlap"
|
||||||
@@ -97,7 +97,7 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import {PropType} from 'vue'
|
import {computed, PropType} from 'vue'
|
||||||
import KeywordsComponent from "@/components/display/KeywordsBar.vue";
|
import KeywordsComponent from "@/components/display/KeywordsBar.vue";
|
||||||
import {Recipe, RecipeOverview} from "@/openapi";
|
import {Recipe, RecipeOverview} from "@/openapi";
|
||||||
|
|
||||||
@@ -113,20 +113,29 @@ const props = defineProps({
|
|||||||
show_description: {type: Boolean, required: false},
|
show_description: {type: Boolean, required: false},
|
||||||
height: {type: String, required: false, default: '15vh'},
|
height: {type: String, required: false, default: '15vh'},
|
||||||
linkTarget: {type: String, required: false, default: ''},
|
linkTarget: {type: String, required: false, default: ''},
|
||||||
showMenu: {type: Boolean, default: true, required: false}
|
showMenu: {type: Boolean, default: true, required: false},
|
||||||
|
servings: {type: Number, required: false},
|
||||||
})
|
})
|
||||||
|
|
||||||
const router = useRouter()
|
const router = useRouter()
|
||||||
|
|
||||||
|
const dest = computed(() => {
|
||||||
|
const route: any = { name: 'RecipeViewPage', params: { id: props.recipe.id } };
|
||||||
|
if (props.servings !== undefined) {
|
||||||
|
route.query = { servings: String(props.servings) };
|
||||||
|
}
|
||||||
|
return route;
|
||||||
|
})
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* open the recipe either in the same tab or in a new tab depending on the link target prop
|
* open the recipe either in the same tab or in a new tab depending on the link target prop
|
||||||
*/
|
*/
|
||||||
function openRecipe() {
|
function openRecipe() {
|
||||||
if (props.linkTarget != '') {
|
if (props.linkTarget != '') {
|
||||||
const routeData = router.resolve({name: 'RecipeViewPage', params: {id: props.recipe.id}});
|
const routeData = router.resolve(dest.value);
|
||||||
window.open(routeData.href, props.linkTarget);
|
window.open(routeData.href, props.linkTarget);
|
||||||
} else {
|
} else {
|
||||||
router.push({name: 'RecipeViewPage', params: {id: props.recipe.id}})
|
router.push(dest.value);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -10,7 +10,7 @@
|
|||||||
|
|
||||||
<template v-if="recipe.name != undefined">
|
<template v-if="recipe.name != undefined">
|
||||||
|
|
||||||
<template class="d-block d-lg-none">
|
<template class="d-block d-lg-none d-print-none">
|
||||||
|
|
||||||
<!-- mobile layout -->
|
<!-- mobile layout -->
|
||||||
<v-card class="rounded-0">
|
<v-card class="rounded-0">
|
||||||
@@ -25,7 +25,7 @@
|
|||||||
<span class="ps-2 text-h5 flex-grow-1 pa-1" :class="{'text-truncate': !showFullRecipeName}" @click="showFullRecipeName = !showFullRecipeName">
|
<span class="ps-2 text-h5 flex-grow-1 pa-1" :class="{'text-truncate': !showFullRecipeName}" @click="showFullRecipeName = !showFullRecipeName">
|
||||||
{{ recipe.name }}
|
{{ recipe.name }}
|
||||||
</span>
|
</span>
|
||||||
<recipe-context-menu :recipe="recipe" v-if="useUserPreferenceStore().isAuthenticated"></recipe-context-menu>
|
<recipe-context-menu :recipe="recipe" :servings="servings" v-if="useUserPreferenceStore().isAuthenticated"></recipe-context-menu>
|
||||||
</v-sheet>
|
</v-sheet>
|
||||||
<keywords-component variant="flat" class="ms-1" :keywords="recipe.keywords"></keywords-component>
|
<keywords-component variant="flat" class="ms-1" :keywords="recipe.keywords"></keywords-component>
|
||||||
<private-recipe-badge :users="recipe.shared" v-if="recipe._private"></private-recipe-badge>
|
<private-recipe-badge :users="recipe.shared" v-if="recipe._private"></private-recipe-badge>
|
||||||
@@ -61,7 +61,7 @@
|
|||||||
</v-card>
|
</v-card>
|
||||||
</template>
|
</template>
|
||||||
<!-- Desktop horizontal layout -->
|
<!-- Desktop horizontal layout -->
|
||||||
<template class="d-none d-lg-block">
|
<template class="d-none d-lg-block d-print-block">
|
||||||
<v-row dense>
|
<v-row dense>
|
||||||
<v-col cols="8">
|
<v-col cols="8">
|
||||||
<recipe-image
|
<recipe-image
|
||||||
@@ -75,7 +75,7 @@
|
|||||||
<v-card-text class="flex-grow-1">
|
<v-card-text class="flex-grow-1">
|
||||||
<div class="d-flex">
|
<div class="d-flex">
|
||||||
<h1 class="flex-column flex-grow-1">{{ recipe.name }}</h1>
|
<h1 class="flex-column flex-grow-1">{{ recipe.name }}</h1>
|
||||||
<recipe-context-menu :recipe="recipe" v-if="useUserPreferenceStore().isAuthenticated"
|
<recipe-context-menu :recipe="recipe" :servings="servings" v-if="useUserPreferenceStore().isAuthenticated"
|
||||||
class="flex-column mb-auto mt-2 float-right"></recipe-context-menu>
|
class="flex-column mb-auto mt-2 float-right"></recipe-context-menu>
|
||||||
</div>
|
</div>
|
||||||
<p>
|
<p>
|
||||||
@@ -118,7 +118,7 @@
|
|||||||
</v-row>
|
</v-row>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<template v-if="recipe.filePath">
|
<template v-if="recipe.filePath && !useUserPreferenceStore().isPrintMode">
|
||||||
<external-recipe-viewer class="mt-2" :recipe="recipe"></external-recipe-viewer>
|
<external-recipe-viewer class="mt-2" :recipe="recipe"></external-recipe-viewer>
|
||||||
|
|
||||||
<v-card :title="$t('AI')" prepend-icon="$ai" :loading="fileApiLoading || loading" :disabled="fileApiLoading || loading || !useUserPreferenceStore().activeSpace.aiEnabled"
|
<v-card :title="$t('AI')" prepend-icon="$ai" :loading="fileApiLoading || loading" :disabled="fileApiLoading || loading || !useUserPreferenceStore().activeSpace.aiEnabled"
|
||||||
@@ -144,7 +144,7 @@
|
|||||||
<step-view v-model="recipe.steps[index]" :step-number="index+1" :ingredientFactor="ingredientFactor"></step-view>
|
<step-view v-model="recipe.steps[index]" :step-number="index+1" :ingredientFactor="ingredientFactor"></step-view>
|
||||||
</v-card>
|
</v-card>
|
||||||
|
|
||||||
<property-view v-model="recipe" :servings="servings"></property-view>
|
<property-view v-model="recipe" :ingredientFactor="ingredientFactor"></property-view>
|
||||||
|
|
||||||
<v-card class="mt-2">
|
<v-card class="mt-2">
|
||||||
<v-card-text>
|
<v-card-text>
|
||||||
@@ -190,7 +190,7 @@
|
|||||||
</v-card-text>
|
</v-card-text>
|
||||||
</v-card>
|
</v-card>
|
||||||
|
|
||||||
<recipe-activity :recipe="recipe" v-if="useUserPreferenceStore().userSettings.comments"></recipe-activity>
|
<recipe-activity :recipe="recipe" :servings="servings" v-if="useUserPreferenceStore().userSettings.comments"></recipe-activity>
|
||||||
</template>
|
</template>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
@@ -220,8 +220,11 @@ const {doAiImport, fileApiLoading} = useFileApi()
|
|||||||
|
|
||||||
const loading = ref(false)
|
const loading = ref(false)
|
||||||
const recipe = defineModel<Recipe>({required: true})
|
const recipe = defineModel<Recipe>({required: true})
|
||||||
|
const props = defineProps<{
|
||||||
|
servings: {type: Number, required: false},
|
||||||
|
}>()
|
||||||
|
|
||||||
const servings = ref(1)
|
const servings = ref(props.servings ?? recipe.value.servings ?? 1)
|
||||||
const showFullRecipeName = ref(false)
|
const showFullRecipeName = ref(false)
|
||||||
|
|
||||||
const selectedAiProvider = ref<undefined | AiProvider>(useUserPreferenceStore().activeSpace.aiDefaultProvider)
|
const selectedAiProvider = ref<undefined | AiProvider>(useUserPreferenceStore().activeSpace.aiDefaultProvider)
|
||||||
@@ -236,11 +239,13 @@ const ingredientFactor = computed(() => {
|
|||||||
/**
|
/**
|
||||||
* change servings when recipe servings are changed
|
* change servings when recipe servings are changed
|
||||||
*/
|
*/
|
||||||
watch(() => recipe.value.servings, () => {
|
if (props.servings === undefined) {
|
||||||
if (recipe.value.servings) {
|
watch(() => recipe.value.servings, () => {
|
||||||
servings.value = recipe.value.servings
|
if (recipe.value.servings) {
|
||||||
}
|
servings.value = recipe.value.servings
|
||||||
})
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
//keep screen on while viewing a recipe
|
//keep screen on while viewing a recipe
|
||||||
|
|||||||
@@ -1,11 +1,17 @@
|
|||||||
<template>
|
<template>
|
||||||
<v-list-item class="swipe-container border-t-sm mt-0 mb-0 pt-0 pb-0 pe-0 pa-0" :id="itemContainerId" @touchend="handleSwipe()" @click="dialog = true;"
|
<v-list-item class="swipe-container border-t-sm mt-0 mb-0 pt-0 pb-0 pe-0 pa-0 shopping-border"
|
||||||
v-if="isShoppingListFoodVisible(props.shoppingListFood, useUserPreferenceStore().deviceSettings)"
|
:id="itemContainerId"
|
||||||
|
@touchend="handleSwipe()"
|
||||||
|
@click="dialog = true;"
|
||||||
|
:value="shoppingListFood"
|
||||||
>
|
>
|
||||||
<!-- <div class="swipe-action" :class="{'bg-success': !isChecked , 'bg-warning': isChecked }">-->
|
<!-- <div class="swipe-action" :class="{'bg-success': !isChecked , 'bg-warning': isChecked }">-->
|
||||||
<!-- <i class="swipe-icon fa-fw fas" :class="{'fa-check': !isChecked , 'fa-cart-plus': isChecked }"></i>-->
|
<!-- <i class="swipe-icon fa-fw fas" :class="{'fa-check': !isChecked , 'fa-cart-plus': isChecked }"></i>-->
|
||||||
<!-- </div>-->
|
<!-- </div>-->
|
||||||
|
|
||||||
|
<div class="color-marker-container">
|
||||||
|
<span :style="{background: sl.color}" v-for="sl in shoppingList"></span>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div class="flex-grow-1 p-2">
|
<div class="flex-grow-1 p-2">
|
||||||
<div class="d-flex">
|
<div class="d-flex">
|
||||||
@@ -31,13 +37,18 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
<template v-slot:[selectBtnSlot]="{ isSelected, select }" v-if="selectEnabled">
|
||||||
|
<v-list-item-action class="ps-3 pe-3" start>
|
||||||
|
<v-checkbox-btn :model-value="isSelected" @update:model-value="select" @click.native.stop=""></v-checkbox-btn>
|
||||||
|
</v-list-item-action>
|
||||||
|
</template>
|
||||||
|
|
||||||
<template v-slot:[checkBtnSlot]>
|
<template v-slot:[checkBtnSlot]>
|
||||||
<div class="ps-3 pe-3" @click.native.stop="useShoppingStore().setEntriesCheckedState(entries, !isChecked, true);">
|
<div class="ps-3 pe-3" @click.native.stop="useShoppingStore().setEntriesCheckedState(entries, !isChecked, true);">
|
||||||
<v-btn color="success" size="large"
|
<v-btn color="success" size="large"
|
||||||
:class="{'btn-success': !isChecked, 'btn-warning': isChecked}" :icon="actionButtonIcon" variant="plain">
|
:class="{'btn-success': !isChecked, 'btn-warning': isChecked}" :icon="actionButtonIcon" variant="plain">
|
||||||
</v-btn>
|
</v-btn>
|
||||||
</div>
|
</div>
|
||||||
<!-- <i class="d-print-none fa-fw fas" :class="{'fa-check': !isChecked , 'fa-cart-plus': isChecked }"></i>-->
|
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<!-- <div class="swipe-action bg-primary justify-content-end">-->
|
<!-- <div class="swipe-action bg-primary justify-content-end">-->
|
||||||
@@ -56,20 +67,23 @@ import {computed, PropType, ref} from "vue";
|
|||||||
import {DateTime} from "luxon";
|
import {DateTime} from "luxon";
|
||||||
import {useShoppingStore} from "@/stores/ShoppingStore.js";
|
import {useShoppingStore} from "@/stores/ShoppingStore.js";
|
||||||
import {useUserPreferenceStore} from "@/stores/UserPreferenceStore.js";
|
import {useUserPreferenceStore} from "@/stores/UserPreferenceStore.js";
|
||||||
import {ApiApi, Food, ShoppingListEntry} from '@/openapi'
|
import {ApiApi, Food, ShoppingList, ShoppingListEntry} from '@/openapi'
|
||||||
import {ErrorMessageType, useMessageStore} from "@/stores/MessageStore";
|
import {ErrorMessageType, useMessageStore} from "@/stores/MessageStore";
|
||||||
import {IShoppingListFood, ShoppingLineAmount} from "@/types/Shopping";
|
import {IShoppingListFood, ShoppingLineAmount} from "@/types/Shopping";
|
||||||
import {isDelayed, isEntryVisible, isShoppingListFoodDelayed, isShoppingListFoodVisible} from "@/utils/logic_utils";
|
import {isDelayed, isEntryVisible, isShoppingListFoodDelayed, isShoppingListFoodVisible} from "@/utils/logic_utils";
|
||||||
import ShoppingLineItemDialog from "@/components/dialogs/ShoppingLineItemDialog.vue";
|
import ShoppingLineItemDialog from "@/components/dialogs/ShoppingLineItemDialog.vue";
|
||||||
import {pluralString} from "@/utils/model_utils.ts";
|
import {pluralString} from "@/utils/model_utils.ts";
|
||||||
|
import ShoppingListsBar from "@/components/display/ShoppingListsBar.vue";
|
||||||
|
|
||||||
const emit = defineEmits(['clicked'])
|
const emit = defineEmits(['clicked'])
|
||||||
|
|
||||||
const props = defineProps({
|
const props = defineProps({
|
||||||
shoppingListFood: {type: {} as PropType<IShoppingListFood>, required: true},
|
shoppingListFood: {type: {} as PropType<IShoppingListFood>, required: true},
|
||||||
hideInfoRow: {type: Boolean, default: false}
|
hideInfoRow: {type: Boolean, default: false},
|
||||||
|
selectEnabled: {type: Boolean, default: false}
|
||||||
})
|
})
|
||||||
const checkBtnSlot = ref(useUserPreferenceStore().userSettings.leftHanded ? 'prepend' : 'append')
|
const checkBtnSlot = ref(useUserPreferenceStore().userSettings.leftHanded ? 'prepend' : 'append')
|
||||||
|
const selectBtnSlot = ref(useUserPreferenceStore().userSettings.leftHanded ? 'append' : 'prepend')
|
||||||
|
|
||||||
const dialog = ref(false)
|
const dialog = ref(false)
|
||||||
|
|
||||||
@@ -82,9 +96,7 @@ const entries = computed(() => {
|
|||||||
*/
|
*/
|
||||||
const itemContainerId = computed(() => {
|
const itemContainerId = computed(() => {
|
||||||
let id = 'id_sli_'
|
let id = 'id_sli_'
|
||||||
for (let i in entries.value) {
|
entries.value.forEach(e => id += e.id + '_')
|
||||||
id += i + '_'
|
|
||||||
}
|
|
||||||
return id
|
return id
|
||||||
})
|
})
|
||||||
|
|
||||||
@@ -112,6 +124,22 @@ 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
|
* calculate the amounts for the given line
|
||||||
* can combine 1 to n entries with the same unit
|
* can combine 1 to n entries with the same unit
|
||||||
@@ -123,34 +151,34 @@ const amounts = computed((): ShoppingLineAmount[] => {
|
|||||||
for (let i in entries.value) {
|
for (let i in entries.value) {
|
||||||
let e = entries.value[i]
|
let e = entries.value[i]
|
||||||
|
|
||||||
if (isEntryVisible(e, useUserPreferenceStore().deviceSettings)) {
|
|
||||||
let unit = -1
|
|
||||||
if (e.unit !== undefined && e.unit !== null) {
|
|
||||||
unit = e.unit.id!
|
|
||||||
}
|
|
||||||
|
|
||||||
if (e.amount > 0) {
|
let unit = -1
|
||||||
|
if (e.unit !== undefined && e.unit !== null) {
|
||||||
|
unit = e.unit.id!
|
||||||
|
}
|
||||||
|
|
||||||
let uaMerged = false
|
if (e.amount > 0) {
|
||||||
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) {
|
let uaMerged = false
|
||||||
unitAmounts.push({
|
unitAmounts.forEach(ua => {
|
||||||
key: `${unit}_${e.checked}_${isDelayed(e)}`,
|
if (((ua.unit == null && e.unit == null) || (ua.unit != null && ua.unit.id! == unit)) && ua.checked == e.checked && ua.delayed == isDelayed(e)) {
|
||||||
amount: e.amount,
|
ua.amount += e.amount
|
||||||
unit: e.unit,
|
uaMerged = true
|
||||||
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
|
return unitAmounts
|
||||||
})
|
})
|
||||||
|
|
||||||
@@ -171,29 +199,28 @@ const infoRow = computed(() => {
|
|||||||
for (let i in entries.value) {
|
for (let i in entries.value) {
|
||||||
let e = entries.value[i]
|
let e = entries.value[i]
|
||||||
|
|
||||||
if (isEntryVisible(e, useUserPreferenceStore().deviceSettings)) {
|
|
||||||
|
|
||||||
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.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 (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.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) {
|
if (useUserPreferenceStore().deviceSettings.shopping_item_info_created_by && authors.length > 0) {
|
||||||
@@ -247,4 +274,22 @@ function handleSwipe() {
|
|||||||
|
|
||||||
<style>
|
<style>
|
||||||
/* TODO swipe system classes removed because not working (visually, touch detection was working), retrieve from old ShoppingLineItem VCS */
|
/* TODO swipe system classes removed because not working (visually, touch detection was working), retrieve from old ShoppingLineItem VCS */
|
||||||
|
|
||||||
|
|
||||||
|
/* 2. Container to wrap the color bars and place them to the far left */
|
||||||
|
.color-marker-container {
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
height: 100%;
|
||||||
|
width: 3px;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
}
|
||||||
|
|
||||||
|
.color-marker-container span {
|
||||||
|
width: 100%;
|
||||||
|
flex-grow: 1;
|
||||||
|
}
|
||||||
|
|
||||||
</style>
|
</style>
|
||||||
@@ -2,7 +2,7 @@
|
|||||||
<v-tabs v-model="currentTab">
|
<v-tabs v-model="currentTab">
|
||||||
<v-tab value="shopping"><i class="fas fa-fw"
|
<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="{'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().stats.countUnchecked }})</span></v-tab>
|
class="d-none d-md-block ms-1">{{ $t('Shopping_list') }} ({{ useShoppingStore().totalFoods }})</span></v-tab>
|
||||||
<v-tab value="recipes"><i class="fas fa-book fa-fw"></i> <span class="d-none d-md-block ms-1">{{
|
<v-tab value="recipes"><i class="fas fa-book fa-fw"></i> <span class="d-none d-md-block ms-1">{{
|
||||||
$t('Recipes')
|
$t('Recipes')
|
||||||
}} ({{ useShoppingStore().getAssociatedRecipes().length }})</span></v-tab>
|
}} ({{ useShoppingStore().getAssociatedRecipes().length }})</span></v-tab>
|
||||||
@@ -25,9 +25,13 @@
|
|||||||
|
|
||||||
<v-list density="compact">
|
<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="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-divider></v-divider>
|
||||||
<v-list-item>
|
<v-list-item>
|
||||||
<v-select hide-details :items="groupingOptionsItems" v-model="useUserPreferenceStore().deviceSettings.shopping_selected_grouping" :label="$t('GroupBy')">
|
<v-select hide-details :items="groupingOptionsItems" v-model="useUserPreferenceStore().deviceSettings.shopping_selected_grouping"
|
||||||
|
:label="$t('GroupBy')">
|
||||||
</v-select>
|
</v-select>
|
||||||
</v-list-item>
|
</v-list-item>
|
||||||
<v-list-item v-if="useUserPreferenceStore().deviceSettings.shopping_selected_grouping == ShoppingGroupingOptions.CATEGORY">
|
<v-list-item v-if="useUserPreferenceStore().deviceSettings.shopping_selected_grouping == ShoppingGroupingOptions.CATEGORY">
|
||||||
@@ -65,29 +69,94 @@
|
|||||||
</v-list>
|
</v-list>
|
||||||
</v-menu>
|
</v-menu>
|
||||||
|
|
||||||
<v-btn height="100%" rounded="0" variant="plain">
|
<!-- <v-btn height="100%" rounded="0" variant="plain">-->
|
||||||
<i class="fa-solid fa-download"></i>
|
<!-- <i class="fa-solid fa-download"></i>-->
|
||||||
<shopping-export-dialog></shopping-export-dialog>
|
<!-- <shopping-export-dialog></shopping-export-dialog>-->
|
||||||
</v-btn>
|
<!-- </v-btn>-->
|
||||||
|
|
||||||
<v-btn height="100%" rounded="0" variant="plain" @click="useShoppingStore().undoChange()">
|
<!-- <v-btn height="100%" rounded="0" variant="plain" @click="useShoppingStore().undoChange()">-->
|
||||||
<i class="fa-solid fa-arrow-rotate-left"></i>
|
<!-- <i class="fa-solid fa-arrow-rotate-left"></i>-->
|
||||||
</v-btn>
|
<!-- </v-btn>-->
|
||||||
|
|
||||||
</v-tabs>
|
</v-tabs>
|
||||||
|
|
||||||
|
<shopping-export-dialog v-model="exportDialog" activator="model"></shopping-export-dialog>
|
||||||
|
|
||||||
<v-window v-model="currentTab">
|
<v-window v-model="currentTab">
|
||||||
<v-window-item value="shopping">
|
<v-window-item value="shopping">
|
||||||
<v-container>
|
<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-row class="pa-0" dense>
|
||||||
<v-col class="pa-0">
|
<v-col class="pa-0">
|
||||||
<v-chip-group v-model="useUserPreferenceStore().deviceSettings.shopping_selected_supermarket" v-if="supermarkets.length > 0">
|
<v-chip label density="compact" variant="outlined" style="max-width: 50%;" :prepend-icon="TSupermarket.icon" append-icon="fa-solid fa-caret-down">
|
||||||
<v-chip v-for="s in supermarkets" :value="s" :key="s.id" label density="compact" variant="outlined" color="primary">{{ s.name }}</v-chip>
|
<span v-if="useUserPreferenceStore().deviceSettings.shopping_selected_supermarket != null">
|
||||||
</v-chip-group>
|
{{ 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" class="ms-1" variant="outlined"
|
||||||
|
:color="(useUserPreferenceStore().deviceSettings.shopping_selected_shopping_list.length == 0 ? '' : 'secondary')" :prepend-icon="TShoppingList.icon"
|
||||||
|
append-icon="fa-solid fa-caret-down">
|
||||||
|
<template v-if="useUserPreferenceStore().deviceSettings.shopping_selected_shopping_list.filter(sl => sl != -1).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" select-strategy="leaf">
|
||||||
|
<v-list-item @click="useUserPreferenceStore().deviceSettings.shopping_selected_shopping_list = [] ">
|
||||||
|
{{ $t('All') }}
|
||||||
|
</v-list-item>
|
||||||
|
<v-list-item :value="-1" @click="useUserPreferenceStore().deviceSettings.shopping_selected_shopping_list = [-1];">
|
||||||
|
<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>
|
||||||
|
{{ $t('None') }}
|
||||||
|
</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-col>
|
</v-col>
|
||||||
</v-row>
|
</v-row>
|
||||||
|
|
||||||
<v-row>
|
<v-row class="mt-0">
|
||||||
<v-col>
|
<v-col>
|
||||||
<v-alert v-if="useShoppingStore().hasFailedItems()" color="warning" class="mb-2">
|
<v-alert v-if="useShoppingStore().hasFailedItems()" color="warning" class="mb-2">
|
||||||
<template #prepend>
|
<template #prepend>
|
||||||
@@ -107,19 +176,18 @@
|
|||||||
<v-skeleton-loader type="list-item"></v-skeleton-loader>
|
<v-skeleton-loader type="list-item"></v-skeleton-loader>
|
||||||
<v-skeleton-loader type="list-item"></v-skeleton-loader>
|
<v-skeleton-loader type="list-item"></v-skeleton-loader>
|
||||||
</v-list>
|
</v-list>
|
||||||
<v-list class="mt-3" density="compact" v-else>
|
<v-list class="mt-3" density="compact" v-model:selected="selectedLines" select-strategy="leaf" v-else>
|
||||||
<template v-for="category in useShoppingStore().getEntriesByGroup" :key="category.name">
|
<template v-for="category in useShoppingStore().entriesByGroup" :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>
|
|
||||||
|
|
||||||
<template v-for="[i, value] in category.foods" :key="value.food.id">
|
<v-list-subheader v-if="category.name === useShoppingStore().UNDEFINED_CATEGORY"><i>{{ $t('NoCategory') }}</i></v-list-subheader>
|
||||||
<shopping-line-item :shopping-list-food="value"></shopping-line-item>
|
<v-list-subheader v-else>{{ category.name }}</v-list-subheader>
|
||||||
</template>
|
<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>
|
||||||
|
|
||||||
</template>
|
</template>
|
||||||
</v-list>
|
</v-list>
|
||||||
|
|
||||||
@@ -246,14 +314,14 @@
|
|||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
|
|
||||||
import {computed, onMounted, ref} from "vue";
|
import {computed, onMounted, ref, shallowRef, toRef, watch} from "vue";
|
||||||
import {useShoppingStore} from "@/stores/ShoppingStore";
|
import {useShoppingStore} from "@/stores/ShoppingStore";
|
||||||
import {ApiApi, Recipe, ResponseError, ShoppingListEntry, ShoppingListRecipe, Supermarket} from "@/openapi";
|
import {ApiApi, Recipe, ResponseError, ShoppingList, ShoppingListEntry, ShoppingListRecipe, Supermarket} from "@/openapi";
|
||||||
import {ErrorMessageType, PreparedMessage, useMessageStore} from "@/stores/MessageStore";
|
import {ErrorMessageType, PreparedMessage, useMessageStore} from "@/stores/MessageStore";
|
||||||
import ShoppingLineItem from "@/components/display/ShoppingLineItem.vue";
|
import ShoppingLineItem from "@/components/display/ShoppingLineItem.vue";
|
||||||
import {useUserPreferenceStore} from "@/stores/UserPreferenceStore";
|
import {useUserPreferenceStore} from "@/stores/UserPreferenceStore";
|
||||||
import ModelSelect from "@/components/inputs/ModelSelect.vue";
|
import ModelSelect from "@/components/inputs/ModelSelect.vue";
|
||||||
import {ShoppingGroupingOptions} from "@/types/Shopping";
|
import {IShoppingListFood, ShoppingGroupingOptions} from "@/types/Shopping";
|
||||||
import {useI18n} from "vue-i18n";
|
import {useI18n} from "vue-i18n";
|
||||||
import NumberScalerDialog from "@/components/inputs/NumberScalerDialog.vue";
|
import NumberScalerDialog from "@/components/inputs/NumberScalerDialog.vue";
|
||||||
import SupermarketEditor from "@/components/model_editors/SupermarketEditor.vue";
|
import SupermarketEditor from "@/components/model_editors/SupermarketEditor.vue";
|
||||||
@@ -265,13 +333,18 @@ import {onBeforeRouteLeave} from "vue-router";
|
|||||||
import {isShoppingCategoryVisible} from "@/utils/logic_utils.ts";
|
import {isShoppingCategoryVisible} from "@/utils/logic_utils.ts";
|
||||||
import ShoppingExportDialog from "@/components/dialogs/ShoppingExportDialog.vue";
|
import ShoppingExportDialog from "@/components/dialogs/ShoppingExportDialog.vue";
|
||||||
import AddToShoppingDialog from "@/components/dialogs/AddToShoppingDialog.vue";
|
import AddToShoppingDialog from "@/components/dialogs/AddToShoppingDialog.vue";
|
||||||
|
import {TShoppingList, TSupermarket} from "@/types/Models.ts";
|
||||||
|
|
||||||
const {t} = useI18n()
|
const {t} = useI18n()
|
||||||
|
|
||||||
|
const exportDialog = ref(false)
|
||||||
const currentTab = ref("shopping")
|
const currentTab = ref("shopping")
|
||||||
const supermarkets = ref([] as Supermarket[])
|
const supermarkets = ref([] as Supermarket[])
|
||||||
|
const shoppingLists = ref([] as ShoppingList[])
|
||||||
const manualAddRecipe = ref<undefined | Recipe>(undefined)
|
const manualAddRecipe = ref<undefined | Recipe>(undefined)
|
||||||
|
|
||||||
|
const selectedLines = shallowRef([] as IShoppingListFood[])
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* VSelect items for shopping list grouping options with localized names
|
* VSelect items for shopping list grouping options with localized names
|
||||||
*/
|
*/
|
||||||
@@ -283,6 +356,10 @@ const groupingOptionsItems = computed(() => {
|
|||||||
return items
|
return items
|
||||||
})
|
})
|
||||||
|
|
||||||
|
watch(() => useUserPreferenceStore().deviceSettings, () => {
|
||||||
|
useShoppingStore().updateEntriesStructure()
|
||||||
|
}, {deep: true})
|
||||||
|
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
addEventListener("visibilitychange", (event) => {
|
addEventListener("visibilitychange", (event) => {
|
||||||
useShoppingStore().autoSyncHasFocus = (document.visibilityState === 'visible')
|
useShoppingStore().autoSyncHasFocus = (document.visibilityState === 'visible')
|
||||||
@@ -304,6 +381,7 @@ onMounted(() => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
loadSupermarkets()
|
loadSupermarkets()
|
||||||
|
loadShoppingLists()
|
||||||
})
|
})
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -378,6 +456,20 @@ function loadSupermarkets() {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* load a list of supermarkets
|
||||||
|
*/
|
||||||
|
function loadShoppingLists() {
|
||||||
|
let api = new ApiApi()
|
||||||
|
|
||||||
|
api.apiShoppingListList().then(r => {
|
||||||
|
shoppingLists.value = r.results
|
||||||
|
// TODO either recursive or add a "favorite" attribute to supermarkets for them to display at all
|
||||||
|
}).catch(err => {
|
||||||
|
useMessageStore().addError(ErrorMessageType.FETCH_ERROR, err)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style scoped>
|
<style scoped>
|
||||||
|
|||||||
27
vue3/src/components/display/ShoppingListsBar.vue
Normal file
27
vue3/src/components/display/ShoppingListsBar.vue
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
<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>
|
||||||
@@ -22,10 +22,10 @@
|
|||||||
<timer :seconds="step.time != undefined ? step.time*60 : 0" @stop="timerRunning = false" v-if="timerRunning"></timer>
|
<timer :seconds="step.time != undefined ? step.time*60 : 0" @stop="timerRunning = false" v-if="timerRunning"></timer>
|
||||||
<v-card-text v-if="step.ingredients.length > 0 || step.instruction != ''">
|
<v-card-text v-if="step.ingredients.length > 0 || step.instruction != ''">
|
||||||
<v-row>
|
<v-row>
|
||||||
<v-col cols="12" md="6" v-if="step.ingredients.length > 0 && (step.showIngredientsTable || step.show_ingredients_table)">
|
<v-col :cols="(useUserPreferenceStore().isPrintMode) ? 6 : 12" md="6" v-if="step.ingredients.length > 0 && (step.showIngredientsTable || step.show_ingredients_table)">
|
||||||
<ingredients-table v-model="step.ingredients" :ingredient-factor="ingredientFactor"></ingredients-table>
|
<ingredients-table v-model="step.ingredients" :ingredient-factor="ingredientFactor"></ingredients-table>
|
||||||
</v-col>
|
</v-col>
|
||||||
<v-col cols="12" md="6" class="markdown-body">
|
<v-col :cols="(useUserPreferenceStore().isPrintMode) ? 6 : 12" md="6" class="markdown-body">
|
||||||
<instructions :instructions_html="step.instructionsMarkdown" :ingredient_factor="ingredientFactor"
|
<instructions :instructions_html="step.instructionsMarkdown" :ingredient_factor="ingredientFactor"
|
||||||
v-if="step.instructionsMarkdown != undefined"></instructions>
|
v-if="step.instructionsMarkdown != undefined"></instructions>
|
||||||
<!-- sub recipes dont have a correct schema, thus they use different variable naming -->
|
<!-- sub recipes dont have a correct schema, thus they use different variable naming -->
|
||||||
@@ -62,6 +62,7 @@ import {Step} from "@/openapi";
|
|||||||
|
|
||||||
import Instructions from "@/components/display/Instructions.vue";
|
import Instructions from "@/components/display/Instructions.vue";
|
||||||
import Timer from "@/components/display/Timer.vue";
|
import Timer from "@/components/display/Timer.vue";
|
||||||
|
import {useUserPreferenceStore} from "@/stores/UserPreferenceStore.ts";
|
||||||
|
|
||||||
const step = defineModel<Step>({required: true})
|
const step = defineModel<Step>({required: true})
|
||||||
|
|
||||||
|
|||||||
@@ -26,7 +26,7 @@
|
|||||||
<v-progress-circular v-if="duplicateLoading" indeterminate size="small"></v-progress-circular>
|
<v-progress-circular v-if="duplicateLoading" indeterminate size="small"></v-progress-circular>
|
||||||
</template>
|
</template>
|
||||||
</v-list-item>
|
</v-list-item>
|
||||||
<v-list-item :to="{ name: 'RecipeViewPage', params: { id: recipe.id}, query: {print: 'true'} }" :active="false" target="_blank" prepend-icon="fa-solid fa-print">
|
<v-list-item :to="{ name: 'RecipeViewPage', params: { id: recipe.id}, query: {print: 'true', servings: props.servings} }" :active="false" target="_blank" prepend-icon="fa-solid fa-print">
|
||||||
{{ $t('Print') }}
|
{{ $t('Print') }}
|
||||||
</v-list-item>
|
</v-list-item>
|
||||||
</v-list>
|
</v-list>
|
||||||
@@ -47,22 +47,21 @@ import AddToShoppingDialog from "@/components/dialogs/AddToShoppingDialog.vue";
|
|||||||
import {ErrorMessageType, useMessageStore} from "@/stores/MessageStore.ts";
|
import {ErrorMessageType, useMessageStore} from "@/stores/MessageStore.ts";
|
||||||
import {useRouter} from "vue-router";
|
import {useRouter} from "vue-router";
|
||||||
import {useFileApi} from "@/composables/useFileApi.ts";
|
import {useFileApi} from "@/composables/useFileApi.ts";
|
||||||
|
import {useI18n} from "vue-i18n";
|
||||||
|
|
||||||
const router = useRouter()
|
const router = useRouter()
|
||||||
|
const {t} = useI18n()
|
||||||
const {updateRecipeImage} = useFileApi()
|
const {updateRecipeImage} = useFileApi()
|
||||||
|
|
||||||
const props = defineProps({
|
const props = defineProps({
|
||||||
recipe: {type: Object as PropType<Recipe | RecipeOverview>, required: true},
|
recipe: {type: Object as PropType<Recipe | RecipeOverview>, required: true},
|
||||||
|
servings: {type: Number, default: undefined},
|
||||||
size: {type: String, default: 'medium'},
|
size: {type: String, default: 'medium'},
|
||||||
})
|
})
|
||||||
|
|
||||||
const mealPlanDialog = ref(false)
|
const mealPlanDialog = ref(false)
|
||||||
const duplicateLoading = ref(false)
|
const duplicateLoading = ref(false)
|
||||||
|
|
||||||
function openPrintView() {
|
|
||||||
print()
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* create a duplicate of the recipe by pulling its current data and creating a new recipe with the same data
|
* create a duplicate of the recipe by pulling its current data and creating a new recipe with the same data
|
||||||
*/
|
*/
|
||||||
@@ -70,7 +69,27 @@ function duplicateRecipe() {
|
|||||||
let api = new ApiApi()
|
let api = new ApiApi()
|
||||||
duplicateLoading.value = true
|
duplicateLoading.value = true
|
||||||
api.apiRecipeRetrieve({id: props.recipe.id!}).then(originalRecipe => {
|
api.apiRecipeRetrieve({id: props.recipe.id!}).then(originalRecipe => {
|
||||||
api.apiRecipeCreate({recipe: originalRecipe}).then(newRecipe => {
|
|
||||||
|
let recipe = {...originalRecipe, ...{id: undefined, name: originalRecipe.name + `(${t('Copy')})`}}
|
||||||
|
recipe.steps = recipe.steps.map((step) => {
|
||||||
|
return {
|
||||||
|
...step,
|
||||||
|
...{
|
||||||
|
id: undefined,
|
||||||
|
ingredients: step.ingredients.map((ingredient) => {
|
||||||
|
return {...ingredient, ...{id: undefined}}
|
||||||
|
}),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
if (recipe.properties != null) {
|
||||||
|
recipe.properties = recipe.properties.map((p) => {
|
||||||
|
return {...p, ...{id: undefined}}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
api.apiRecipeCreate({recipe: recipe}).then(newRecipe => {
|
||||||
|
|
||||||
if (originalRecipe.image) {
|
if (originalRecipe.image) {
|
||||||
updateRecipeImage(newRecipe.id!, null, originalRecipe.image).then(r => {
|
updateRecipeImage(newRecipe.id!, null, originalRecipe.image).then(r => {
|
||||||
|
|||||||
@@ -283,8 +283,9 @@ function parseAndInsertIngredients() {
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
Promise.allSettled(promises).then(r => {
|
Promise.allSettled(promises).then(r => {
|
||||||
|
step.value.ingredients = step.value.ingredients.filter(i => i.food != null || i.note != null || i.amount != 0)
|
||||||
|
|
||||||
r.forEach(i => {
|
r.forEach(i => {
|
||||||
console.log(i)
|
|
||||||
step.value.ingredients.push({
|
step.value.ingredients.push({
|
||||||
originalText: i.value.originalText,
|
originalText: i.value.originalText,
|
||||||
amount: i.value.amount,
|
amount: i.value.amount,
|
||||||
|
|||||||
@@ -30,6 +30,7 @@
|
|||||||
<v-textarea :label="$t('Description')" v-model="editingObj.description"></v-textarea>
|
<v-textarea :label="$t('Description')" v-model="editingObj.description"></v-textarea>
|
||||||
<!-- TODO fix card overflow invisible, overflow-visible class is not working -->
|
<!-- 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('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-form>
|
||||||
</v-tabs-window-item>
|
</v-tabs-window-item>
|
||||||
|
|
||||||
|
|||||||
@@ -29,7 +29,7 @@
|
|||||||
@update:modelValue="editingObj.servings = editingObj.recipe ? editingObj.recipe.servings : 1"></ModelSelect>
|
@update:modelValue="editingObj.servings = editingObj.recipe ? editingObj.recipe.servings : 1"></ModelSelect>
|
||||||
<!-- <v-number-input label="Days" control-variant="split" :min="1"></v-number-input>-->
|
<!-- <v-number-input label="Days" control-variant="split" :min="1"></v-number-input>-->
|
||||||
<!--TODO create days input with +/- synced to date -->
|
<!--TODO create days input with +/- synced to date -->
|
||||||
<recipe-card :recipe="editingObj.recipe" v-if="editingObj && editingObj.recipe" link-target="_blank"></recipe-card>
|
<recipe-card :recipe="editingObj.recipe" :servings="editingObj.servings" v-if="editingObj && editingObj.recipe" link-target="_blank"></recipe-card>
|
||||||
<v-btn prepend-icon="$shopping" color="create" class="mt-1" v-if="!editingObj.shopping && editingObj.recipe && isUpdate()">
|
<v-btn prepend-icon="$shopping" color="create" class="mt-1" v-if="!editingObj.shopping && editingObj.recipe && isUpdate()">
|
||||||
{{$t('Add')}}
|
{{$t('Add')}}
|
||||||
<add-to-shopping-dialog :recipe="editingObj.recipe" :meal-plan="editingObj" @created="loadShoppingListEntries(); editingObj.shopping = true;"></add-to-shopping-dialog>
|
<add-to-shopping-dialog :recipe="editingObj.recipe" :meal-plan="editingObj" @created="loadShoppingListEntries(); editingObj.shopping = true;"></add-to-shopping-dialog>
|
||||||
|
|||||||
68
vue3/src/components/model_editors/ShoppingListEditor.vue
Normal file
68
vue3/src/components/model_editors/ShoppingListEditor.vue
Normal file
@@ -0,0 +1,68 @@
|
|||||||
|
<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,6 +27,7 @@
|
|||||||
<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('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('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('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
|
<v-number-input
|
||||||
class="mt-2"
|
class="mt-2"
|
||||||
|
|||||||
@@ -16,7 +16,7 @@ export function useNavigation() {
|
|||||||
{component: VListItem, prependIcon: '$recipes', title: 'Home', to: {name: 'StartPage', params: {}}},
|
{component: VListItem, prependIcon: '$recipes', title: 'Home', to: {name: 'StartPage', params: {}}},
|
||||||
{component: VListItem, prependIcon: '$search', title: t('Search'), to: {name: 'SearchPage', 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: '$mealplan', title: t('Meal_Plan'), to: {name: 'MealPlanPage', params: {}}},
|
||||||
{component: VListItem, prependIcon: '$shopping', title: t('Shopping_list'), to: {name: 'ShoppingListPage', params: {}}},
|
{component: VListItem, prependIcon: '$shopping', title: t('Shopping'), to: {name: 'ShoppingListPage', params: {}}},
|
||||||
{component: VListItem, prependIcon: 'fas fa-globe', title: t('Import'), to: {name: 'RecipeImportPage', 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: '$books', title: t('Books'), to: {name: 'BooksPage', params: {}}},
|
||||||
{component: VListItem, prependIcon: 'fa-solid fa-folder-tree', title: t('Database'), to: {name: 'DatabasePage', params: {}}},
|
{component: VListItem, prependIcon: 'fa-solid fa-folder-tree', title: t('Database'), to: {name: 'DatabasePage', params: {}}},
|
||||||
|
|||||||
@@ -23,6 +23,7 @@
|
|||||||
"AiModelHelp": "",
|
"AiModelHelp": "",
|
||||||
"AiProvider": "",
|
"AiProvider": "",
|
||||||
"AiProviderHelp": "",
|
"AiProviderHelp": "",
|
||||||
|
"All": "",
|
||||||
"App": "",
|
"App": "",
|
||||||
"Apply": "",
|
"Apply": "",
|
||||||
"Are_You_Sure": "",
|
"Are_You_Sure": "",
|
||||||
@@ -267,6 +268,7 @@
|
|||||||
"Ratings": "",
|
"Ratings": "",
|
||||||
"Recently_Viewed": "",
|
"Recently_Viewed": "",
|
||||||
"Recipe": "",
|
"Recipe": "",
|
||||||
|
"RecipeStructure": "",
|
||||||
"Recipe_Book": "",
|
"Recipe_Book": "",
|
||||||
"Recipe_Image": "",
|
"Recipe_Image": "",
|
||||||
"Recipes": "",
|
"Recipes": "",
|
||||||
@@ -314,6 +316,7 @@
|
|||||||
"SpaceMembersHelp": "",
|
"SpaceMembersHelp": "",
|
||||||
"SpaceName": "",
|
"SpaceName": "",
|
||||||
"SpacePrivateObjectsHelp": "",
|
"SpacePrivateObjectsHelp": "",
|
||||||
|
"Start": "",
|
||||||
"Starting_Day": "",
|
"Starting_Day": "",
|
||||||
"StartsWith": "",
|
"StartsWith": "",
|
||||||
"StartsWithHelp": "",
|
"StartsWithHelp": "",
|
||||||
|
|||||||
@@ -23,6 +23,7 @@
|
|||||||
"AiModelHelp": "",
|
"AiModelHelp": "",
|
||||||
"AiProvider": "",
|
"AiProvider": "",
|
||||||
"AiProviderHelp": "",
|
"AiProviderHelp": "",
|
||||||
|
"All": "",
|
||||||
"App": "Приложение",
|
"App": "Приложение",
|
||||||
"Apply": "",
|
"Apply": "",
|
||||||
"Are_You_Sure": "Сигурен ли си?",
|
"Are_You_Sure": "Сигурен ли си?",
|
||||||
@@ -260,6 +261,7 @@
|
|||||||
"Ratings": "Рейтинги",
|
"Ratings": "Рейтинги",
|
||||||
"Recently_Viewed": "Наскоро разгледани",
|
"Recently_Viewed": "Наскоро разгледани",
|
||||||
"Recipe": "Рецепта",
|
"Recipe": "Рецепта",
|
||||||
|
"RecipeStructure": "",
|
||||||
"Recipe_Book": "Книга с рецепти",
|
"Recipe_Book": "Книга с рецепти",
|
||||||
"Recipe_Image": "Изображение на рецептата",
|
"Recipe_Image": "Изображение на рецептата",
|
||||||
"Recipes": "Рецепти",
|
"Recipes": "Рецепти",
|
||||||
@@ -307,6 +309,7 @@
|
|||||||
"SpaceMembersHelp": "",
|
"SpaceMembersHelp": "",
|
||||||
"SpaceName": "",
|
"SpaceName": "",
|
||||||
"SpacePrivateObjectsHelp": "",
|
"SpacePrivateObjectsHelp": "",
|
||||||
|
"Start": "",
|
||||||
"Starting_Day": "Начален ден от седмицата",
|
"Starting_Day": "Начален ден от седмицата",
|
||||||
"StartsWith": "",
|
"StartsWith": "",
|
||||||
"StartsWithHelp": "",
|
"StartsWithHelp": "",
|
||||||
|
|||||||
@@ -26,6 +26,7 @@
|
|||||||
"AiProvider": "",
|
"AiProvider": "",
|
||||||
"AiProviderHelp": "",
|
"AiProviderHelp": "",
|
||||||
"Alignment": "Alineació",
|
"Alignment": "Alineació",
|
||||||
|
"All": "",
|
||||||
"Amount": "Quantitat",
|
"Amount": "Quantitat",
|
||||||
"App": "Aplicació",
|
"App": "Aplicació",
|
||||||
"Apply": "",
|
"Apply": "",
|
||||||
@@ -337,6 +338,7 @@
|
|||||||
"Ratings": "Avaluació",
|
"Ratings": "Avaluació",
|
||||||
"Recently_Viewed": "Vistos recentment",
|
"Recently_Viewed": "Vistos recentment",
|
||||||
"Recipe": "Recepta",
|
"Recipe": "Recepta",
|
||||||
|
"RecipeStructure": "",
|
||||||
"Recipe_Book": "Llibre de receptes",
|
"Recipe_Book": "Llibre de receptes",
|
||||||
"Recipe_Image": "Imatge de la recepta",
|
"Recipe_Image": "Imatge de la recepta",
|
||||||
"Recipes": "Receptes",
|
"Recipes": "Receptes",
|
||||||
@@ -394,6 +396,7 @@
|
|||||||
"SpacePrivateObjectsHelp": "",
|
"SpacePrivateObjectsHelp": "",
|
||||||
"Space_Cosmetic_Settings": "Un administrador de l'espai podria canviar algunes configuracions estètiques i tindrien prioritat sobre la configuració dels usuaris per a aquest espai.",
|
"Space_Cosmetic_Settings": "Un administrador de l'espai podria canviar algunes configuracions estètiques i tindrien prioritat sobre la configuració dels usuaris per a aquest espai.",
|
||||||
"Split_All_Steps": "Dividir totes les files en passos separats.",
|
"Split_All_Steps": "Dividir totes les files en passos separats.",
|
||||||
|
"Start": "",
|
||||||
"StartDate": "Data d'inici",
|
"StartDate": "Data d'inici",
|
||||||
"Starting_Day": "Dia d'inici de la setmana",
|
"Starting_Day": "Dia d'inici de la setmana",
|
||||||
"StartsWith": "",
|
"StartsWith": "",
|
||||||
|
|||||||
@@ -26,6 +26,7 @@
|
|||||||
"AiProvider": "",
|
"AiProvider": "",
|
||||||
"AiProviderHelp": "",
|
"AiProviderHelp": "",
|
||||||
"Alignment": "Zarovnání",
|
"Alignment": "Zarovnání",
|
||||||
|
"All": "",
|
||||||
"Amount": "Množství",
|
"Amount": "Množství",
|
||||||
"App": "Aplikace",
|
"App": "Aplikace",
|
||||||
"Apply": "",
|
"Apply": "",
|
||||||
@@ -334,6 +335,7 @@
|
|||||||
"Ratings": "Hodnocení",
|
"Ratings": "Hodnocení",
|
||||||
"Recently_Viewed": "Naposledy prohlížené",
|
"Recently_Viewed": "Naposledy prohlížené",
|
||||||
"Recipe": "Recept",
|
"Recipe": "Recept",
|
||||||
|
"RecipeStructure": "",
|
||||||
"Recipe_Book": "Kuchařka",
|
"Recipe_Book": "Kuchařka",
|
||||||
"Recipe_Image": "Obrázek k receptu",
|
"Recipe_Image": "Obrázek k receptu",
|
||||||
"Recipes": "Recepty",
|
"Recipes": "Recepty",
|
||||||
@@ -389,6 +391,7 @@
|
|||||||
"SpacePrivateObjectsHelp": "",
|
"SpacePrivateObjectsHelp": "",
|
||||||
"Space_Cosmetic_Settings": "Některá kosmetická nastavení mohou měnit správci prostoru a budou mít přednost před nastavením klienta pro daný prostor.",
|
"Space_Cosmetic_Settings": "Některá kosmetická nastavení mohou měnit správci prostoru a budou mít přednost před nastavením klienta pro daný prostor.",
|
||||||
"Split_All_Steps": "Rozdělit každý řádek do samostatného kroku.",
|
"Split_All_Steps": "Rozdělit každý řádek do samostatného kroku.",
|
||||||
|
"Start": "",
|
||||||
"StartDate": "Počáteční datum",
|
"StartDate": "Počáteční datum",
|
||||||
"Starting_Day": "První den v týdnu",
|
"Starting_Day": "První den v týdnu",
|
||||||
"StartsWith": "",
|
"StartsWith": "",
|
||||||
|
|||||||
@@ -26,6 +26,7 @@
|
|||||||
"AiProvider": "",
|
"AiProvider": "",
|
||||||
"AiProviderHelp": "",
|
"AiProviderHelp": "",
|
||||||
"Alignment": "Justering",
|
"Alignment": "Justering",
|
||||||
|
"All": "",
|
||||||
"Amount": "Mængde",
|
"Amount": "Mængde",
|
||||||
"App": "App",
|
"App": "App",
|
||||||
"Apply": "",
|
"Apply": "",
|
||||||
@@ -337,6 +338,7 @@
|
|||||||
"Ratings": "Bedømmelser",
|
"Ratings": "Bedømmelser",
|
||||||
"Recently_Viewed": "Vist for nylig",
|
"Recently_Viewed": "Vist for nylig",
|
||||||
"Recipe": "Opskrift",
|
"Recipe": "Opskrift",
|
||||||
|
"RecipeStructure": "",
|
||||||
"Recipe_Book": "Opskriftsbog",
|
"Recipe_Book": "Opskriftsbog",
|
||||||
"Recipe_Image": "Opskriftsbillede",
|
"Recipe_Image": "Opskriftsbillede",
|
||||||
"Recipes": "Opskrifter",
|
"Recipes": "Opskrifter",
|
||||||
@@ -394,6 +396,7 @@
|
|||||||
"SpacePrivateObjectsHelp": "",
|
"SpacePrivateObjectsHelp": "",
|
||||||
"Space_Cosmetic_Settings": "Visse kosmetiske indstillinger kan ændres af område-administratorer og vil overskrive klient-indstillinger for pågældende område.",
|
"Space_Cosmetic_Settings": "Visse kosmetiske indstillinger kan ændres af område-administratorer og vil overskrive klient-indstillinger for pågældende område.",
|
||||||
"Split_All_Steps": "Opdel rækker i separate trin.",
|
"Split_All_Steps": "Opdel rækker i separate trin.",
|
||||||
|
"Start": "",
|
||||||
"StartDate": "Startdato",
|
"StartDate": "Startdato",
|
||||||
"Starting_Day": "Første dag på ugen",
|
"Starting_Day": "Første dag på ugen",
|
||||||
"StartsWith": "",
|
"StartsWith": "",
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@@ -26,6 +26,7 @@
|
|||||||
"AiProvider": "",
|
"AiProvider": "",
|
||||||
"AiProviderHelp": "",
|
"AiProviderHelp": "",
|
||||||
"Alignment": "Ευθυγράμμιση",
|
"Alignment": "Ευθυγράμμιση",
|
||||||
|
"All": "",
|
||||||
"Amount": "Ποσότητα",
|
"Amount": "Ποσότητα",
|
||||||
"App": "Εφαρμογή",
|
"App": "Εφαρμογή",
|
||||||
"Apply": "",
|
"Apply": "",
|
||||||
@@ -337,6 +338,7 @@
|
|||||||
"Ratings": "Βαθμολογίες",
|
"Ratings": "Βαθμολογίες",
|
||||||
"Recently_Viewed": "Προβλήθηκαν πρόσφατα",
|
"Recently_Viewed": "Προβλήθηκαν πρόσφατα",
|
||||||
"Recipe": "Συνταγή",
|
"Recipe": "Συνταγή",
|
||||||
|
"RecipeStructure": "",
|
||||||
"Recipe_Book": "Βιβλίο συνταγών",
|
"Recipe_Book": "Βιβλίο συνταγών",
|
||||||
"Recipe_Image": "Εικόνα συνταγής",
|
"Recipe_Image": "Εικόνα συνταγής",
|
||||||
"Recipes": "Συνταγές",
|
"Recipes": "Συνταγές",
|
||||||
@@ -394,6 +396,7 @@
|
|||||||
"SpacePrivateObjectsHelp": "",
|
"SpacePrivateObjectsHelp": "",
|
||||||
"Space_Cosmetic_Settings": "Ορισμένες ρυθμίσεις εμφάνισης μπορούν να αλλάξουν από τους διαχειριστές του χώρου και θα παρακάμψουν τις ρυθμίσεις πελάτη για αυτόν τον χώρο.",
|
"Space_Cosmetic_Settings": "Ορισμένες ρυθμίσεις εμφάνισης μπορούν να αλλάξουν από τους διαχειριστές του χώρου και θα παρακάμψουν τις ρυθμίσεις πελάτη για αυτόν τον χώρο.",
|
||||||
"Split_All_Steps": "Διαχωρισμός όλων των γραμμών σε χωριστά βήματα.",
|
"Split_All_Steps": "Διαχωρισμός όλων των γραμμών σε χωριστά βήματα.",
|
||||||
|
"Start": "",
|
||||||
"StartDate": "Ημερομηνία Έναρξης",
|
"StartDate": "Ημερομηνία Έναρξης",
|
||||||
"Starting_Day": "Πρώτη μέρα της εβδομάδας",
|
"Starting_Day": "Πρώτη μέρα της εβδομάδας",
|
||||||
"StartsWith": "",
|
"StartsWith": "",
|
||||||
|
|||||||
@@ -37,6 +37,7 @@
|
|||||||
"AiProvider": "AI Provider",
|
"AiProvider": "AI Provider",
|
||||||
"AiProviderHelp": "You can configure multiple AI providers according to your preferences. They can even be configured to work across multiple spaces.",
|
"AiProviderHelp": "You can configure multiple AI providers according to your preferences. They can even be configured to work across multiple spaces.",
|
||||||
"Alignment": "Alignment",
|
"Alignment": "Alignment",
|
||||||
|
"All": "All",
|
||||||
"AllRecipes": "All Recipes",
|
"AllRecipes": "All Recipes",
|
||||||
"Amount": "Amount",
|
"Amount": "Amount",
|
||||||
"App": "App",
|
"App": "App",
|
||||||
@@ -149,6 +150,7 @@
|
|||||||
"Decimals": "Decimals",
|
"Decimals": "Decimals",
|
||||||
"Default": "Default",
|
"Default": "Default",
|
||||||
"DefaultPage": "Default Page",
|
"DefaultPage": "Default Page",
|
||||||
|
"DefaultShoppingListHelp": "Default List when this Food is added to the Shoppinglist.",
|
||||||
"Default_Unit": "Default Unit",
|
"Default_Unit": "Default Unit",
|
||||||
"DelayFor": "Delay for {hours} hours",
|
"DelayFor": "Delay for {hours} hours",
|
||||||
"DelayUntil": "Delay Until",
|
"DelayUntil": "Delay Until",
|
||||||
@@ -373,6 +375,7 @@
|
|||||||
"NoUnit": "No Unit",
|
"NoUnit": "No Unit",
|
||||||
"No_ID": "ID not found, cannot delete.",
|
"No_ID": "ID not found, cannot delete.",
|
||||||
"No_Results": "No Results",
|
"No_Results": "No Results",
|
||||||
|
"None": "None",
|
||||||
"NotFound": "Not found",
|
"NotFound": "Not found",
|
||||||
"NotFoundHelp": "The page or object you are looking for could not be found.",
|
"NotFoundHelp": "The page or object you are looking for could not be found.",
|
||||||
"NotInShopping": "{food} is not in your shopping list.",
|
"NotInShopping": "{food} is not in your shopping list.",
|
||||||
@@ -452,6 +455,7 @@
|
|||||||
"RecipeBookHelp": "Recipebooks contain recipe book entries or can be automatically populated by using saved search filters. ",
|
"RecipeBookHelp": "Recipebooks contain recipe book entries or can be automatically populated by using saved search filters. ",
|
||||||
"RecipeHelp": "Recipes are the foundation of Tandoor and consist of general information and steps, made up of ingredients, instructions and more. ",
|
"RecipeHelp": "Recipes are the foundation of Tandoor and consist of general information and steps, made up of ingredients, instructions and more. ",
|
||||||
"RecipeStepsHelp": "Ingredients, Instructions and more can be edited in the tab Steps.",
|
"RecipeStepsHelp": "Ingredients, Instructions and more can be edited in the tab Steps.",
|
||||||
|
"RecipeStructure": "Recipe Structure",
|
||||||
"Recipe_Book": "Recipe Book",
|
"Recipe_Book": "Recipe Book",
|
||||||
"Recipe_Image": "Recipe Image",
|
"Recipe_Image": "Recipe Image",
|
||||||
"Recipes": "Recipes",
|
"Recipes": "Recipes",
|
||||||
@@ -500,14 +504,17 @@
|
|||||||
"Share": "Share",
|
"Share": "Share",
|
||||||
"ShopLater": "Shop later",
|
"ShopLater": "Shop later",
|
||||||
"ShopNow": "Shop now",
|
"ShopNow": "Shop now",
|
||||||
|
"Shopping": "Shopping",
|
||||||
"ShoppingBackgroundSyncWarning": "Bad network, waiting to sync ...",
|
"ShoppingBackgroundSyncWarning": "Bad network, waiting to sync ...",
|
||||||
|
"ShoppingList": "Shoppinglist",
|
||||||
"ShoppingListEntry": "Shoppinglist Entry",
|
"ShoppingListEntry": "Shoppinglist Entry",
|
||||||
"ShoppingListEntryHelp": "Shopping list entries can be created manually or trough recipes and meal plans.",
|
"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",
|
"ShoppingListRecipe": "Shoppinglist Recipe",
|
||||||
"Shopping_Categories": "Shopping Categories",
|
"Shopping_Categories": "Shopping Categories",
|
||||||
"Shopping_Category": "Shopping Category",
|
"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_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. Potato/100 Potatoes/100 g Potatoes",
|
"Shopping_input_placeholder": "e.g. 100 g Potatoes",
|
||||||
"Shopping_list": "Shopping List",
|
"Shopping_list": "Shopping List",
|
||||||
"ShowDelayed": "Show delayed items",
|
"ShowDelayed": "Show delayed items",
|
||||||
"ShowIngredients": "Show Ingredients",
|
"ShowIngredients": "Show Ingredients",
|
||||||
@@ -539,6 +546,7 @@
|
|||||||
"Space_Cosmetic_Settings": "Some cosmetic settings can be changed by space administrators and will override client settings for that space.",
|
"Space_Cosmetic_Settings": "Some cosmetic settings can be changed by space administrators and will override client settings for that space.",
|
||||||
"Split": "Split",
|
"Split": "Split",
|
||||||
"Split_All_Steps": "Split all rows into separate steps.",
|
"Split_All_Steps": "Split all rows into separate steps.",
|
||||||
|
"Start": "Start",
|
||||||
"StartDate": "Start Date",
|
"StartDate": "Start Date",
|
||||||
"Starting_Day": "Starting day of the week",
|
"Starting_Day": "Starting day of the week",
|
||||||
"StartsWith": "Starts with",
|
"StartsWith": "Starts with",
|
||||||
@@ -608,6 +616,8 @@
|
|||||||
"Unrated": "Unrated",
|
"Unrated": "Unrated",
|
||||||
"Up": "Up",
|
"Up": "Up",
|
||||||
"Update": "Update",
|
"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",
|
"Update_Existing_Data": "Update Existing Data",
|
||||||
"Updated": "Updated",
|
"Updated": "Updated",
|
||||||
"UpgradeNow": "Upgrade now",
|
"UpgradeNow": "Upgrade now",
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@@ -27,6 +27,7 @@
|
|||||||
"AiProvider": "",
|
"AiProvider": "",
|
||||||
"AiProviderHelp": "",
|
"AiProviderHelp": "",
|
||||||
"Alignment": "Tasaus",
|
"Alignment": "Tasaus",
|
||||||
|
"All": "",
|
||||||
"Amount": "Määrä",
|
"Amount": "Määrä",
|
||||||
"App": "Applikaatio",
|
"App": "Applikaatio",
|
||||||
"Apply": "",
|
"Apply": "",
|
||||||
@@ -326,6 +327,7 @@
|
|||||||
"Ratings": "Luokitukset",
|
"Ratings": "Luokitukset",
|
||||||
"Recently_Viewed": "Äskettäin katsotut",
|
"Recently_Viewed": "Äskettäin katsotut",
|
||||||
"Recipe": "Resepti",
|
"Recipe": "Resepti",
|
||||||
|
"RecipeStructure": "",
|
||||||
"Recipe_Book": "Keittokirja",
|
"Recipe_Book": "Keittokirja",
|
||||||
"Recipe_Image": "Reseptin Kuva",
|
"Recipe_Image": "Reseptin Kuva",
|
||||||
"Recipes": "Reseptit",
|
"Recipes": "Reseptit",
|
||||||
@@ -381,6 +383,7 @@
|
|||||||
"SpaceName": "",
|
"SpaceName": "",
|
||||||
"SpacePrivateObjectsHelp": "",
|
"SpacePrivateObjectsHelp": "",
|
||||||
"Split_All_Steps": "Jaa kaikki rivit erillisiin vaiheisiin.",
|
"Split_All_Steps": "Jaa kaikki rivit erillisiin vaiheisiin.",
|
||||||
|
"Start": "",
|
||||||
"StartDate": "Aloituspäivä",
|
"StartDate": "Aloituspäivä",
|
||||||
"Starting_Day": "Viikon aloituspäivä",
|
"Starting_Day": "Viikon aloituspäivä",
|
||||||
"StartsWith": "",
|
"StartsWith": "",
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@@ -26,6 +26,7 @@
|
|||||||
"AiProvider": "",
|
"AiProvider": "",
|
||||||
"AiProviderHelp": "",
|
"AiProviderHelp": "",
|
||||||
"Alignment": "יישור",
|
"Alignment": "יישור",
|
||||||
|
"All": "",
|
||||||
"Amount": "כמות",
|
"Amount": "כמות",
|
||||||
"App": "אפליקציה",
|
"App": "אפליקציה",
|
||||||
"Apply": "",
|
"Apply": "",
|
||||||
@@ -337,6 +338,7 @@
|
|||||||
"Ratings": "דירוג",
|
"Ratings": "דירוג",
|
||||||
"Recently_Viewed": "נצפו לאחרונה",
|
"Recently_Viewed": "נצפו לאחרונה",
|
||||||
"Recipe": "מתכון",
|
"Recipe": "מתכון",
|
||||||
|
"RecipeStructure": "",
|
||||||
"Recipe_Book": "ספר מתכון",
|
"Recipe_Book": "ספר מתכון",
|
||||||
"Recipe_Image": "תמונת מתכון",
|
"Recipe_Image": "תמונת מתכון",
|
||||||
"Recipes": "מתכונים",
|
"Recipes": "מתכונים",
|
||||||
@@ -394,6 +396,7 @@
|
|||||||
"SpacePrivateObjectsHelp": "",
|
"SpacePrivateObjectsHelp": "",
|
||||||
"Space_Cosmetic_Settings": "חלק מהגדרות הקוסמטיות יכולות להיות מעודכנות על ידי מנהל המרחב וידרסו את הגדרות הקליינט עבור מרחב זה.",
|
"Space_Cosmetic_Settings": "חלק מהגדרות הקוסמטיות יכולות להיות מעודכנות על ידי מנהל המרחב וידרסו את הגדרות הקליינט עבור מרחב זה.",
|
||||||
"Split_All_Steps": "פצל את כל השורות לצעדים נפרדים.",
|
"Split_All_Steps": "פצל את כל השורות לצעדים נפרדים.",
|
||||||
|
"Start": "",
|
||||||
"StartDate": "תאריך התחלה",
|
"StartDate": "תאריך התחלה",
|
||||||
"Starting_Day": "יום תחילת השבוע",
|
"Starting_Day": "יום תחילת השבוע",
|
||||||
"StartsWith": "",
|
"StartsWith": "",
|
||||||
|
|||||||
@@ -26,6 +26,7 @@
|
|||||||
"AiProvider": "",
|
"AiProvider": "",
|
||||||
"AiProviderHelp": "",
|
"AiProviderHelp": "",
|
||||||
"Alignment": "Poravnanje",
|
"Alignment": "Poravnanje",
|
||||||
|
"All": "",
|
||||||
"Amount": "Količina",
|
"Amount": "Količina",
|
||||||
"App": "Aplikacija",
|
"App": "Aplikacija",
|
||||||
"Apply": "",
|
"Apply": "",
|
||||||
@@ -337,6 +338,7 @@
|
|||||||
"Ratings": "Ocjene",
|
"Ratings": "Ocjene",
|
||||||
"Recently_Viewed": "Nedavno pogledano",
|
"Recently_Viewed": "Nedavno pogledano",
|
||||||
"Recipe": "Recept",
|
"Recipe": "Recept",
|
||||||
|
"RecipeStructure": "",
|
||||||
"Recipe_Book": "Knjiga recepata",
|
"Recipe_Book": "Knjiga recepata",
|
||||||
"Recipe_Image": "Slika recepta",
|
"Recipe_Image": "Slika recepta",
|
||||||
"Recipes": "Recepti",
|
"Recipes": "Recepti",
|
||||||
@@ -394,6 +396,7 @@
|
|||||||
"SpacePrivateObjectsHelp": "",
|
"SpacePrivateObjectsHelp": "",
|
||||||
"Space_Cosmetic_Settings": "Neke kozmetičke postavke mogu promijeniti administratori prostora i one će poništiti postavke klijenta za taj prostor.",
|
"Space_Cosmetic_Settings": "Neke kozmetičke postavke mogu promijeniti administratori prostora i one će poništiti postavke klijenta za taj prostor.",
|
||||||
"Split_All_Steps": "Podijeli sve retke u zasebne korake.",
|
"Split_All_Steps": "Podijeli sve retke u zasebne korake.",
|
||||||
|
"Start": "",
|
||||||
"StartDate": "Početni datum",
|
"StartDate": "Početni datum",
|
||||||
"Starting_Day": "Početni dan u tjednu",
|
"Starting_Day": "Početni dan u tjednu",
|
||||||
"StartsWith": "",
|
"StartsWith": "",
|
||||||
|
|||||||
@@ -26,6 +26,7 @@
|
|||||||
"AiProvider": "",
|
"AiProvider": "",
|
||||||
"AiProviderHelp": "",
|
"AiProviderHelp": "",
|
||||||
"Alignment": "Igazítás",
|
"Alignment": "Igazítás",
|
||||||
|
"All": "",
|
||||||
"Amount": "Összeg",
|
"Amount": "Összeg",
|
||||||
"App": "Applikáció",
|
"App": "Applikáció",
|
||||||
"Apply": "",
|
"Apply": "",
|
||||||
@@ -310,6 +311,7 @@
|
|||||||
"Ratings": "Értékelések",
|
"Ratings": "Értékelések",
|
||||||
"Recently_Viewed": "Nemrég megtekintett",
|
"Recently_Viewed": "Nemrég megtekintett",
|
||||||
"Recipe": "Recept",
|
"Recipe": "Recept",
|
||||||
|
"RecipeStructure": "",
|
||||||
"Recipe_Book": "Szakácskönyv",
|
"Recipe_Book": "Szakácskönyv",
|
||||||
"Recipe_Image": "Receptkép",
|
"Recipe_Image": "Receptkép",
|
||||||
"Recipes": "Receptek",
|
"Recipes": "Receptek",
|
||||||
@@ -360,6 +362,7 @@
|
|||||||
"SpaceName": "",
|
"SpaceName": "",
|
||||||
"SpacePrivateObjectsHelp": "",
|
"SpacePrivateObjectsHelp": "",
|
||||||
"Split_All_Steps": "Ossza fel az összes sort különálló lépésekbe.",
|
"Split_All_Steps": "Ossza fel az összes sort különálló lépésekbe.",
|
||||||
|
"Start": "",
|
||||||
"StartDate": "Kezdés dátuma",
|
"StartDate": "Kezdés dátuma",
|
||||||
"Starting_Day": "A hét kezdőnapja",
|
"Starting_Day": "A hét kezdőnapja",
|
||||||
"StartsWith": "",
|
"StartsWith": "",
|
||||||
|
|||||||
@@ -17,6 +17,7 @@
|
|||||||
"AiModelHelp": "",
|
"AiModelHelp": "",
|
||||||
"AiProvider": "",
|
"AiProvider": "",
|
||||||
"AiProviderHelp": "",
|
"AiProviderHelp": "",
|
||||||
|
"All": "",
|
||||||
"Apply": "",
|
"Apply": "",
|
||||||
"Automate": "Ավտոմատացնել",
|
"Automate": "Ավտոմատացնել",
|
||||||
"BatchDeleteConfirm": "",
|
"BatchDeleteConfirm": "",
|
||||||
@@ -143,6 +144,7 @@
|
|||||||
"Rating": "",
|
"Rating": "",
|
||||||
"Recently_Viewed": "Վերջերս դիտած",
|
"Recently_Viewed": "Վերջերս դիտած",
|
||||||
"Recipe": "Բաղադրատոմս",
|
"Recipe": "Բաղադրատոմս",
|
||||||
|
"RecipeStructure": "",
|
||||||
"Recipe_Book": "Բաղադրատոմսերի գիրք",
|
"Recipe_Book": "Բաղադրատոմսերի գիրք",
|
||||||
"Recipe_Image": "Բաղադրատոմսի նկար",
|
"Recipe_Image": "Բաղադրատոմսի նկար",
|
||||||
"Recipes": "Բաղադրատոմսեր",
|
"Recipes": "Բաղադրատոմսեր",
|
||||||
@@ -177,6 +179,7 @@
|
|||||||
"SpaceMembersHelp": "",
|
"SpaceMembersHelp": "",
|
||||||
"SpaceName": "",
|
"SpaceName": "",
|
||||||
"SpacePrivateObjectsHelp": "",
|
"SpacePrivateObjectsHelp": "",
|
||||||
|
"Start": "",
|
||||||
"StartsWith": "",
|
"StartsWith": "",
|
||||||
"StartsWithHelp": "",
|
"StartsWithHelp": "",
|
||||||
"Step": "",
|
"Step": "",
|
||||||
|
|||||||
@@ -25,6 +25,7 @@
|
|||||||
"AiModelHelp": "",
|
"AiModelHelp": "",
|
||||||
"AiProvider": "",
|
"AiProvider": "",
|
||||||
"AiProviderHelp": "",
|
"AiProviderHelp": "",
|
||||||
|
"All": "",
|
||||||
"App": "",
|
"App": "",
|
||||||
"Apply": "",
|
"Apply": "",
|
||||||
"Are_You_Sure": "",
|
"Are_You_Sure": "",
|
||||||
@@ -286,6 +287,7 @@
|
|||||||
"Ratings": "",
|
"Ratings": "",
|
||||||
"Recently_Viewed": "baru saja dilihat",
|
"Recently_Viewed": "baru saja dilihat",
|
||||||
"Recipe": "",
|
"Recipe": "",
|
||||||
|
"RecipeStructure": "",
|
||||||
"Recipe_Book": "",
|
"Recipe_Book": "",
|
||||||
"Recipe_Image": "Gambar Resep",
|
"Recipe_Image": "Gambar Resep",
|
||||||
"Recipes": "Resep",
|
"Recipes": "Resep",
|
||||||
@@ -336,6 +338,7 @@
|
|||||||
"SpaceMembersHelp": "",
|
"SpaceMembersHelp": "",
|
||||||
"SpaceName": "",
|
"SpaceName": "",
|
||||||
"SpacePrivateObjectsHelp": "",
|
"SpacePrivateObjectsHelp": "",
|
||||||
|
"Start": "",
|
||||||
"Starting_Day": "",
|
"Starting_Day": "",
|
||||||
"StartsWith": "",
|
"StartsWith": "",
|
||||||
"StartsWithHelp": "",
|
"StartsWithHelp": "",
|
||||||
|
|||||||
@@ -26,6 +26,7 @@
|
|||||||
"AiProvider": "",
|
"AiProvider": "",
|
||||||
"AiProviderHelp": "",
|
"AiProviderHelp": "",
|
||||||
"Alignment": "",
|
"Alignment": "",
|
||||||
|
"All": "",
|
||||||
"Amount": "",
|
"Amount": "",
|
||||||
"App": "",
|
"App": "",
|
||||||
"Apply": "",
|
"Apply": "",
|
||||||
@@ -336,6 +337,7 @@
|
|||||||
"Ratings": "",
|
"Ratings": "",
|
||||||
"Recently_Viewed": "",
|
"Recently_Viewed": "",
|
||||||
"Recipe": "",
|
"Recipe": "",
|
||||||
|
"RecipeStructure": "",
|
||||||
"Recipe_Book": "",
|
"Recipe_Book": "",
|
||||||
"Recipe_Image": "",
|
"Recipe_Image": "",
|
||||||
"Recipes": "",
|
"Recipes": "",
|
||||||
@@ -392,6 +394,7 @@
|
|||||||
"SpacePrivateObjectsHelp": "",
|
"SpacePrivateObjectsHelp": "",
|
||||||
"Space_Cosmetic_Settings": "",
|
"Space_Cosmetic_Settings": "",
|
||||||
"Split_All_Steps": "",
|
"Split_All_Steps": "",
|
||||||
|
"Start": "",
|
||||||
"StartDate": "",
|
"StartDate": "",
|
||||||
"Starting_Day": "",
|
"Starting_Day": "",
|
||||||
"StartsWith": "",
|
"StartsWith": "",
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -26,6 +26,7 @@
|
|||||||
"AiProvider": "",
|
"AiProvider": "",
|
||||||
"AiProviderHelp": "",
|
"AiProviderHelp": "",
|
||||||
"Alignment": "",
|
"Alignment": "",
|
||||||
|
"All": "",
|
||||||
"Amount": "Suma",
|
"Amount": "Suma",
|
||||||
"App": "",
|
"App": "",
|
||||||
"Apply": "",
|
"Apply": "",
|
||||||
@@ -314,6 +315,7 @@
|
|||||||
"Ratings": "",
|
"Ratings": "",
|
||||||
"Recently_Viewed": "Neseniai Žiūrėta",
|
"Recently_Viewed": "Neseniai Žiūrėta",
|
||||||
"Recipe": "",
|
"Recipe": "",
|
||||||
|
"RecipeStructure": "",
|
||||||
"Recipe_Book": "",
|
"Recipe_Book": "",
|
||||||
"Recipe_Image": "Recepto nuotrauka",
|
"Recipe_Image": "Recepto nuotrauka",
|
||||||
"Recipes": "",
|
"Recipes": "",
|
||||||
@@ -365,6 +367,7 @@
|
|||||||
"SpaceName": "",
|
"SpaceName": "",
|
||||||
"SpacePrivateObjectsHelp": "",
|
"SpacePrivateObjectsHelp": "",
|
||||||
"Split_All_Steps": "",
|
"Split_All_Steps": "",
|
||||||
|
"Start": "",
|
||||||
"StartDate": "",
|
"StartDate": "",
|
||||||
"Starting_Day": "",
|
"Starting_Day": "",
|
||||||
"StartsWith": "",
|
"StartsWith": "",
|
||||||
|
|||||||
@@ -26,6 +26,7 @@
|
|||||||
"AiProvider": "",
|
"AiProvider": "",
|
||||||
"AiProviderHelp": "",
|
"AiProviderHelp": "",
|
||||||
"Alignment": "",
|
"Alignment": "",
|
||||||
|
"All": "",
|
||||||
"Amount": "",
|
"Amount": "",
|
||||||
"App": "",
|
"App": "",
|
||||||
"Apply": "",
|
"Apply": "",
|
||||||
@@ -337,6 +338,7 @@
|
|||||||
"Ratings": "",
|
"Ratings": "",
|
||||||
"Recently_Viewed": "",
|
"Recently_Viewed": "",
|
||||||
"Recipe": "",
|
"Recipe": "",
|
||||||
|
"RecipeStructure": "",
|
||||||
"Recipe_Book": "",
|
"Recipe_Book": "",
|
||||||
"Recipe_Image": "",
|
"Recipe_Image": "",
|
||||||
"Recipes": "",
|
"Recipes": "",
|
||||||
@@ -394,6 +396,7 @@
|
|||||||
"SpacePrivateObjectsHelp": "",
|
"SpacePrivateObjectsHelp": "",
|
||||||
"Space_Cosmetic_Settings": "",
|
"Space_Cosmetic_Settings": "",
|
||||||
"Split_All_Steps": "",
|
"Split_All_Steps": "",
|
||||||
|
"Start": "",
|
||||||
"StartDate": "",
|
"StartDate": "",
|
||||||
"Starting_Day": "",
|
"Starting_Day": "",
|
||||||
"StartsWith": "",
|
"StartsWith": "",
|
||||||
|
|||||||
@@ -26,6 +26,7 @@
|
|||||||
"AiProvider": "",
|
"AiProvider": "",
|
||||||
"AiProviderHelp": "",
|
"AiProviderHelp": "",
|
||||||
"Alignment": "Justering",
|
"Alignment": "Justering",
|
||||||
|
"All": "",
|
||||||
"Amount": "Mengde",
|
"Amount": "Mengde",
|
||||||
"App": "App",
|
"App": "App",
|
||||||
"Apply": "",
|
"Apply": "",
|
||||||
@@ -321,6 +322,7 @@
|
|||||||
"Ratings": "",
|
"Ratings": "",
|
||||||
"Recently_Viewed": "Nylig vist",
|
"Recently_Viewed": "Nylig vist",
|
||||||
"Recipe": "Oppskrift",
|
"Recipe": "Oppskrift",
|
||||||
|
"RecipeStructure": "",
|
||||||
"Recipe_Book": "Oppskriftsbok",
|
"Recipe_Book": "Oppskriftsbok",
|
||||||
"Recipe_Image": "Oppskriftsbilde",
|
"Recipe_Image": "Oppskriftsbilde",
|
||||||
"Recipes": "Oppskrift",
|
"Recipes": "Oppskrift",
|
||||||
@@ -375,6 +377,7 @@
|
|||||||
"SpaceName": "",
|
"SpaceName": "",
|
||||||
"SpacePrivateObjectsHelp": "",
|
"SpacePrivateObjectsHelp": "",
|
||||||
"Split_All_Steps": "",
|
"Split_All_Steps": "",
|
||||||
|
"Start": "",
|
||||||
"StartDate": "Startdato",
|
"StartDate": "Startdato",
|
||||||
"Starting_Day": "Dag uken skal state på",
|
"Starting_Day": "Dag uken skal state på",
|
||||||
"StartsWith": "",
|
"StartsWith": "",
|
||||||
|
|||||||
@@ -3,6 +3,7 @@
|
|||||||
"AIImportSubtitle": "Gebruik Al om afbeeldingen van recepten te importeren.",
|
"AIImportSubtitle": "Gebruik Al om afbeeldingen van recepten te importeren.",
|
||||||
"AISettingsHostedHelp": "Je kunt AI-functies inschakelen of beschikbare credits aanpassen door je abonnement te beheren.",
|
"AISettingsHostedHelp": "Je kunt AI-functies inschakelen of beschikbare credits aanpassen door je abonnement te beheren.",
|
||||||
"API": "API",
|
"API": "API",
|
||||||
|
"APIKey": "API-sleutel",
|
||||||
"API_Browser": "API-browser",
|
"API_Browser": "API-browser",
|
||||||
"API_Documentation": "API-documentatie",
|
"API_Documentation": "API-documentatie",
|
||||||
"AccessTokenHelp": "Toegangssleutels voor de REST API.",
|
"AccessTokenHelp": "Toegangssleutels voor de REST API.",
|
||||||
@@ -55,7 +56,7 @@
|
|||||||
"BaseUnit": "Basiseenheid",
|
"BaseUnit": "Basiseenheid",
|
||||||
"BaseUnitHelp": "Standaardeenheid om automatische eenheden om te rekenen",
|
"BaseUnitHelp": "Standaardeenheid om automatische eenheden om te rekenen",
|
||||||
"Basics": "Basisprincipes",
|
"Basics": "Basisprincipes",
|
||||||
"BatchDeleteConfirm": "Wil je alle getoonde items verwijderen? Dit kan niet ongedaan worden gemaakt!",
|
"BatchDeleteConfirm": "Wil je alle getoonde items verwijderen? Dit kan niet ongedaan worden gemaakt! WAARSCHUWING: Het is mogelijk dat hierdoor objecten worden verwijderd die ook elders worden gebruikt. ",
|
||||||
"BatchDeleteHelp": "Als een item niet verwijderd kan worden, wordt het ergens gebruikt. ",
|
"BatchDeleteHelp": "Als een item niet verwijderd kan worden, wordt het ergens gebruikt. ",
|
||||||
"BatchEdit": "Batchbewerking",
|
"BatchEdit": "Batchbewerking",
|
||||||
"BatchEditUpdatingItemsCount": "{count} {type} bewerken",
|
"BatchEditUpdatingItemsCount": "{count} {type} bewerken",
|
||||||
@@ -329,6 +330,7 @@
|
|||||||
"Miscellaneous": "Diversen",
|
"Miscellaneous": "Diversen",
|
||||||
"MissingConversion": "Ontbrekende conversie",
|
"MissingConversion": "Ontbrekende conversie",
|
||||||
"MissingProperties": "Ontbrekende eigenschappen",
|
"MissingProperties": "Ontbrekende eigenschappen",
|
||||||
|
"Model": "Model",
|
||||||
"ModelSelectResultsHelp": "Zoek naar meer resultaten",
|
"ModelSelectResultsHelp": "Zoek naar meer resultaten",
|
||||||
"Monday": "Maandag",
|
"Monday": "Maandag",
|
||||||
"Month": "Maand",
|
"Month": "Maand",
|
||||||
@@ -448,6 +450,7 @@
|
|||||||
"RecipeBookHelp": "Receptboeken bevatten receptenboekitems of kunnen automatisch gevuld worden met behulp van opgeslagen zoekfilters. ",
|
"RecipeBookHelp": "Receptboeken bevatten receptenboekitems of kunnen automatisch gevuld worden met behulp van opgeslagen zoekfilters. ",
|
||||||
"RecipeHelp": "Recepten vormen de basis van Tandoor en bestaan uit algemene informatie en stappen, opgebouwd uit ingrediënten, instructies en meer. ",
|
"RecipeHelp": "Recepten vormen de basis van Tandoor en bestaan uit algemene informatie en stappen, opgebouwd uit ingrediënten, instructies en meer. ",
|
||||||
"RecipeStepsHelp": "Ingrediënten, instructies en meer kun je bewerken in het tabblad stappen.",
|
"RecipeStepsHelp": "Ingrediënten, instructies en meer kun je bewerken in het tabblad stappen.",
|
||||||
|
"RecipeStructure": "Receptstructuur",
|
||||||
"Recipe_Book": "Kookboek",
|
"Recipe_Book": "Kookboek",
|
||||||
"Recipe_Image": "Receptafbeelding",
|
"Recipe_Image": "Receptafbeelding",
|
||||||
"Recipes": "Recepten",
|
"Recipes": "Recepten",
|
||||||
@@ -534,6 +537,7 @@
|
|||||||
"Space_Cosmetic_Settings": "Sommige weergave-instellingen kunnen worden geforceerd door de administrator van de ruimte en zullen de persoonlijke instellingen voor die ruimte overschrijven.",
|
"Space_Cosmetic_Settings": "Sommige weergave-instellingen kunnen worden geforceerd door de administrator van de ruimte en zullen de persoonlijke instellingen voor die ruimte overschrijven.",
|
||||||
"Split": "Splitsen",
|
"Split": "Splitsen",
|
||||||
"Split_All_Steps": "Splits alle rijen in aparte stappen.",
|
"Split_All_Steps": "Splits alle rijen in aparte stappen.",
|
||||||
|
"Start": "Start",
|
||||||
"StartDate": "Startdatum",
|
"StartDate": "Startdatum",
|
||||||
"Starting_Day": "Eerste dag van de week",
|
"Starting_Day": "Eerste dag van de week",
|
||||||
"StartsWith": "Begint met",
|
"StartsWith": "Begint met",
|
||||||
@@ -550,10 +554,12 @@
|
|||||||
"Storage": "Externe opslag",
|
"Storage": "Externe opslag",
|
||||||
"StorageHelp": "Extern opgeslagen locaties waar receptenbestanden (afbeelding/pdf) kunnen worden opgeslagen en gesynchroniseerd met Tandoor.",
|
"StorageHelp": "Extern opgeslagen locaties waar receptenbestanden (afbeelding/pdf) kunnen worden opgeslagen en gesynchroniseerd met Tandoor.",
|
||||||
"StoragePasswordTokenHelp": "Het opgeslagen wachtwoord/token wordt nooit weergegeven. Dit wordt alleen gewijzigd als er iets nieuws in het veld wordt ingevoerd. ",
|
"StoragePasswordTokenHelp": "Het opgeslagen wachtwoord/token wordt nooit weergegeven. Dit wordt alleen gewijzigd als er iets nieuws in het veld wordt ingevoerd. ",
|
||||||
|
"Structured": "Gestructureerd",
|
||||||
"SubstituteOnHand": "Je hebt een alternatief op voorraad.",
|
"SubstituteOnHand": "Je hebt een alternatief op voorraad.",
|
||||||
"Substitutes": "Alternatieven",
|
"Substitutes": "Alternatieven",
|
||||||
"Success": "Succes",
|
"Success": "Succes",
|
||||||
"SuccessClipboard": "Boodschappenlijst is gekopieerd naar klembord",
|
"SuccessClipboard": "Boodschappenlijst is gekopieerd naar klembord",
|
||||||
|
"Summary": "Samenvatting",
|
||||||
"Sunday": "Zondag",
|
"Sunday": "Zondag",
|
||||||
"Supermarket": "Supermarkt",
|
"Supermarket": "Supermarkt",
|
||||||
"SupermarketCategoriesOnly": "Alleen supermarkt categorieën",
|
"SupermarketCategoriesOnly": "Alleen supermarkt categorieën",
|
||||||
@@ -864,8 +870,12 @@
|
|||||||
"warning_duplicate_filter": "Waarschuwing: door technische beperkingen kan het hebben van meerdere filters of dezelfde combinatie (en/of/niet) tot onverwachte resultaten leiden.",
|
"warning_duplicate_filter": "Waarschuwing: door technische beperkingen kan het hebben van meerdere filters of dezelfde combinatie (en/of/niet) tot onverwachte resultaten leiden.",
|
||||||
"warning_feature_beta": "Deze functie zit op dit moment in de BETA (test) fase. Verwacht hier bugs en toekomstige wijzigingen die tot het verlies van data kunnen leiden bij het gebruik.",
|
"warning_feature_beta": "Deze functie zit op dit moment in de BETA (test) fase. Verwacht hier bugs en toekomstige wijzigingen die tot het verlies van data kunnen leiden bij het gebruik.",
|
||||||
"warning_space_delete": "Je kunt jouw ruimte verwijderen inclusief alle recepten, boodschappenlijstjes, maaltijdplannen en alles wat je verder aangemaakt hebt. Dit kan niet ongedaan worden gemaakt! Weet je het zeker?",
|
"warning_space_delete": "Je kunt jouw ruimte verwijderen inclusief alle recepten, boodschappenlijstjes, maaltijdplannen en alles wat je verder aangemaakt hebt. Dit kan niet ongedaan worden gemaakt! Weet je het zeker?",
|
||||||
"Summary": "Samenvatting",
|
"AboutTandoor": "Tandoor is een open source platform om recepten, maaltijdplannen, boodschappenlijstjes en meer te beheren.",
|
||||||
"APIKey": "API-sleutel",
|
"ImportIntoTandoorHelp": "Om dit recept in je eigen Tandoor-collectie te importeren, volg je de volgende stappen.",
|
||||||
"Model": "Model",
|
"SelfHosted": "Zelfgehost",
|
||||||
"Structured": "Gestructureerd"
|
"CreateAccount": "Maak account",
|
||||||
|
"Shopping": "Boodschappen",
|
||||||
|
"ShoppingList": "Boodschappenlijst",
|
||||||
|
"ShoppingListHelp": "Hiermee kun je items op verschillende lijsten plaatsen. Dit kan worden gebruikt voor verschillende supermarkten, speciale aanbiedingen of evenementen. ",
|
||||||
|
"Finish": "Eind"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -35,6 +35,7 @@
|
|||||||
"AiProvider": "",
|
"AiProvider": "",
|
||||||
"AiProviderHelp": "",
|
"AiProviderHelp": "",
|
||||||
"Alignment": "Wyrównanie",
|
"Alignment": "Wyrównanie",
|
||||||
|
"All": "",
|
||||||
"AllRecipes": "Wszystkie przepisy",
|
"AllRecipes": "Wszystkie przepisy",
|
||||||
"Amount": "Ilość",
|
"Amount": "Ilość",
|
||||||
"App": "Aplikacja",
|
"App": "Aplikacja",
|
||||||
@@ -363,6 +364,7 @@
|
|||||||
"Ratings": "Oceny",
|
"Ratings": "Oceny",
|
||||||
"Recently_Viewed": "Ostatnio oglądane",
|
"Recently_Viewed": "Ostatnio oglądane",
|
||||||
"Recipe": "Przepis",
|
"Recipe": "Przepis",
|
||||||
|
"RecipeStructure": "",
|
||||||
"Recipe_Book": "Książka z przepisami",
|
"Recipe_Book": "Książka z przepisami",
|
||||||
"Recipe_Image": "Obrazek dla przepisu",
|
"Recipe_Image": "Obrazek dla przepisu",
|
||||||
"Recipes": "Przepisy",
|
"Recipes": "Przepisy",
|
||||||
@@ -420,6 +422,7 @@
|
|||||||
"SpacePrivateObjectsHelp": "",
|
"SpacePrivateObjectsHelp": "",
|
||||||
"Space_Cosmetic_Settings": "Administratorzy przestrzeni mogą zmienić niektóre ustawienia kosmetyczne, które zastąpią ustawienia klienta dla tej przestrzeni.",
|
"Space_Cosmetic_Settings": "Administratorzy przestrzeni mogą zmienić niektóre ustawienia kosmetyczne, które zastąpią ustawienia klienta dla tej przestrzeni.",
|
||||||
"Split_All_Steps": "Traktuj każdy wiersz jako osobne kroki.",
|
"Split_All_Steps": "Traktuj każdy wiersz jako osobne kroki.",
|
||||||
|
"Start": "",
|
||||||
"StartDate": "Data początkowa",
|
"StartDate": "Data początkowa",
|
||||||
"Starting_Day": "Dzień rozpoczęcia tygodnia",
|
"Starting_Day": "Dzień rozpoczęcia tygodnia",
|
||||||
"StartsWith": "",
|
"StartsWith": "",
|
||||||
|
|||||||
@@ -19,6 +19,7 @@
|
|||||||
"Added_on": "Adicionado a",
|
"Added_on": "Adicionado a",
|
||||||
"Advanced": "Avançado",
|
"Advanced": "Avançado",
|
||||||
"Alignment": "Alinhamento",
|
"Alignment": "Alinhamento",
|
||||||
|
"All": "",
|
||||||
"Amount": "Quantidade",
|
"Amount": "Quantidade",
|
||||||
"Apply": "",
|
"Apply": "",
|
||||||
"Auto_Planner": "",
|
"Auto_Planner": "",
|
||||||
@@ -236,6 +237,7 @@
|
|||||||
"Ratings": "Avaliações",
|
"Ratings": "Avaliações",
|
||||||
"Recently_Viewed": "Vistos Recentemente",
|
"Recently_Viewed": "Vistos Recentemente",
|
||||||
"Recipe": "Receita",
|
"Recipe": "Receita",
|
||||||
|
"RecipeStructure": "",
|
||||||
"Recipe_Book": "Livro de Receitas",
|
"Recipe_Book": "Livro de Receitas",
|
||||||
"Recipe_Image": "Imagem da Receita",
|
"Recipe_Image": "Imagem da Receita",
|
||||||
"Recipes": "Receitas",
|
"Recipes": "Receitas",
|
||||||
@@ -271,6 +273,7 @@
|
|||||||
"Show_as_header": "Mostrar como cabeçalho",
|
"Show_as_header": "Mostrar como cabeçalho",
|
||||||
"Size": "Tamanho",
|
"Size": "Tamanho",
|
||||||
"Sort_by_new": "Ordenar por mais recente",
|
"Sort_by_new": "Ordenar por mais recente",
|
||||||
|
"Start": "",
|
||||||
"StartDate": "Data de início",
|
"StartDate": "Data de início",
|
||||||
"Starting_Day": "Dia de início da semana",
|
"Starting_Day": "Dia de início da semana",
|
||||||
"StartsWith": "",
|
"StartsWith": "",
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@@ -26,6 +26,7 @@
|
|||||||
"AiModelHelp": "",
|
"AiModelHelp": "",
|
||||||
"AiProvider": "",
|
"AiProvider": "",
|
||||||
"AiProviderHelp": "",
|
"AiProviderHelp": "",
|
||||||
|
"All": "",
|
||||||
"Amount": "Cantitate",
|
"Amount": "Cantitate",
|
||||||
"App": "Aplicație",
|
"App": "Aplicație",
|
||||||
"Apply": "",
|
"Apply": "",
|
||||||
@@ -298,6 +299,7 @@
|
|||||||
"Ratings": "Evaluări",
|
"Ratings": "Evaluări",
|
||||||
"Recently_Viewed": "Vizualizate recent",
|
"Recently_Viewed": "Vizualizate recent",
|
||||||
"Recipe": "Rețetă",
|
"Recipe": "Rețetă",
|
||||||
|
"RecipeStructure": "",
|
||||||
"Recipe_Book": "Carte de rețete",
|
"Recipe_Book": "Carte de rețete",
|
||||||
"Recipe_Image": "Imagine a rețetei",
|
"Recipe_Image": "Imagine a rețetei",
|
||||||
"Recipes": "Rețete",
|
"Recipes": "Rețete",
|
||||||
@@ -349,6 +351,7 @@
|
|||||||
"SpaceName": "",
|
"SpaceName": "",
|
||||||
"SpacePrivateObjectsHelp": "",
|
"SpacePrivateObjectsHelp": "",
|
||||||
"Split_All_Steps": "Împărțiți toate rândurile în pași separați.",
|
"Split_All_Steps": "Împărțiți toate rândurile în pași separați.",
|
||||||
|
"Start": "",
|
||||||
"Starting_Day": "Ziua de început a săptămânii",
|
"Starting_Day": "Ziua de început a săptămânii",
|
||||||
"StartsWith": "",
|
"StartsWith": "",
|
||||||
"StartsWithHelp": "",
|
"StartsWithHelp": "",
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@@ -3,8 +3,10 @@
|
|||||||
"AIImportSubtitle": "Uporabite umetno inteligenco za uvoz slik receptov.",
|
"AIImportSubtitle": "Uporabite umetno inteligenco za uvoz slik receptov.",
|
||||||
"AISettingsHostedHelp": "Funkcije umetne inteligence lahko omogočite ali spremenite razpoložljive kredite z upravljanjem naročnine.",
|
"AISettingsHostedHelp": "Funkcije umetne inteligence lahko omogočite ali spremenite razpoložljive kredite z upravljanjem naročnine.",
|
||||||
"API": "API",
|
"API": "API",
|
||||||
|
"APIKey": "API ključ",
|
||||||
"API_Browser": "API brskalnik",
|
"API_Browser": "API brskalnik",
|
||||||
"API_Documentation": "API dokumentacija",
|
"API_Documentation": "API dokumentacija",
|
||||||
|
"AboutTandoor": "Tandoor je odprtokodna platforma za upravljanje receptov, načrtov obrokov, nakupovalnih seznamov in še več.",
|
||||||
"AccessTokenHelp": "Dostopni ključi za REST API.",
|
"AccessTokenHelp": "Dostopni ključi za REST API.",
|
||||||
"Access_Token": "Dostopni žeton",
|
"Access_Token": "Dostopni žeton",
|
||||||
"Account": "Račun",
|
"Account": "Račun",
|
||||||
@@ -115,6 +117,7 @@
|
|||||||
"Create": "Ustvari",
|
"Create": "Ustvari",
|
||||||
"Create Food": "Ustvari živilo",
|
"Create Food": "Ustvari živilo",
|
||||||
"Create Recipe": "Ustvari recept",
|
"Create Recipe": "Ustvari recept",
|
||||||
|
"CreateAccount": "Ustvari račun",
|
||||||
"CreateFirstRecipe": "Ustvarite svoj prvi recept z urejevalnikom receptov.",
|
"CreateFirstRecipe": "Ustvarite svoj prvi recept z urejevalnikom receptov.",
|
||||||
"CreateInvitation": "Ustvari povabilo",
|
"CreateInvitation": "Ustvari povabilo",
|
||||||
"Create_Meal_Plan_Entry": "Ustvari vnos za načrtovan obrok",
|
"Create_Meal_Plan_Entry": "Ustvari vnos za načrtovan obrok",
|
||||||
@@ -208,6 +211,7 @@
|
|||||||
"Fats": "Maščobe",
|
"Fats": "Maščobe",
|
||||||
"File": "Datoteka",
|
"File": "Datoteka",
|
||||||
"Files": "Datoteke",
|
"Files": "Datoteke",
|
||||||
|
"Finish": "Končaj",
|
||||||
"FinishedAt": "Končano ob",
|
"FinishedAt": "Končano ob",
|
||||||
"First": "Prvi",
|
"First": "Prvi",
|
||||||
"First_name": "Ime",
|
"First_name": "Ime",
|
||||||
@@ -256,6 +260,7 @@
|
|||||||
"ImportAll": "Uvozi vse",
|
"ImportAll": "Uvozi vse",
|
||||||
"ImportFirstRecipe": "Uvozite svoj prvi recept z enega od tisočih spletnih mest ali pa uporabite enega od drugih uvoznikov za uvoz obstoječe zbirke, dokumentov ali seznamov URL-jev.",
|
"ImportFirstRecipe": "Uvozite svoj prvi recept z enega od tisočih spletnih mest ali pa uporabite enega od drugih uvoznikov za uvoz obstoječe zbirke, dokumentov ali seznamov URL-jev.",
|
||||||
"ImportIntoTandoor": "Uvozi v Tandoor",
|
"ImportIntoTandoor": "Uvozi v Tandoor",
|
||||||
|
"ImportIntoTandoorHelp": "Če želite uvoziti ta recept v svojo zbirko Tandoor, sledite naslednjim korakom.",
|
||||||
"ImportMealPlans": "Uvozi načrte prehrane",
|
"ImportMealPlans": "Uvozi načrte prehrane",
|
||||||
"ImportShoppingList": "Uvozi nakupovalne sezname",
|
"ImportShoppingList": "Uvozi nakupovalne sezname",
|
||||||
"Import_Error": "Med uvozom je prišlo do napake. Za ogled razširite podrobnosti na dnu strani.",
|
"Import_Error": "Med uvozom je prišlo do napake. Za ogled razširite podrobnosti na dnu strani.",
|
||||||
@@ -328,6 +333,7 @@
|
|||||||
"Miscellaneous": "Razno",
|
"Miscellaneous": "Razno",
|
||||||
"MissingConversion": "Manjkajoča konverzija",
|
"MissingConversion": "Manjkajoča konverzija",
|
||||||
"MissingProperties": "Manjkajoče lastnosti",
|
"MissingProperties": "Manjkajoče lastnosti",
|
||||||
|
"Model": "Model",
|
||||||
"ModelSelectResultsHelp": "Išči več rezultatov",
|
"ModelSelectResultsHelp": "Išči več rezultatov",
|
||||||
"Monday": "Ponedeljek",
|
"Monday": "Ponedeljek",
|
||||||
"Month": "Mesec",
|
"Month": "Mesec",
|
||||||
@@ -447,6 +453,7 @@
|
|||||||
"RecipeBookHelp": "Knjige receptov vsebujejo vnose v knjige receptov ali pa se lahko samodejno izpolnijo z uporabo shranjenih iskalnih filtrov. ",
|
"RecipeBookHelp": "Knjige receptov vsebujejo vnose v knjige receptov ali pa se lahko samodejno izpolnijo z uporabo shranjenih iskalnih filtrov. ",
|
||||||
"RecipeHelp": "Recepti so osnova Tandoorja in so sestavljeni iz splošnih informacij in korakov, sestavljenih iz sestavin, navodil in še več. ",
|
"RecipeHelp": "Recepti so osnova Tandoorja in so sestavljeni iz splošnih informacij in korakov, sestavljenih iz sestavin, navodil in še več. ",
|
||||||
"RecipeStepsHelp": "Sestavine, navodila in drugo lahko urejate v zavihku Koraki.",
|
"RecipeStepsHelp": "Sestavine, navodila in drugo lahko urejate v zavihku Koraki.",
|
||||||
|
"RecipeStructure": "Struktura recepta",
|
||||||
"Recipe_Book": "Knjiga receptov",
|
"Recipe_Book": "Knjiga receptov",
|
||||||
"Recipe_Image": "Slika recepta",
|
"Recipe_Image": "Slika recepta",
|
||||||
"Recipes": "Recepti",
|
"Recipes": "Recepti",
|
||||||
@@ -486,6 +493,7 @@
|
|||||||
"Select_File": "Izberi datoteko",
|
"Select_File": "Izberi datoteko",
|
||||||
"Selected": "Izbrano",
|
"Selected": "Izbrano",
|
||||||
"SelectedCategories": "Izbrane kategorije",
|
"SelectedCategories": "Izbrane kategorije",
|
||||||
|
"SelfHosted": "Samostojno gostovanje",
|
||||||
"Serving": "Serviranje",
|
"Serving": "Serviranje",
|
||||||
"Servings": "Porcije",
|
"Servings": "Porcije",
|
||||||
"ServingsText": "Besedilo o porcijah",
|
"ServingsText": "Besedilo o porcijah",
|
||||||
@@ -533,6 +541,7 @@
|
|||||||
"Space_Cosmetic_Settings": "Nekatere kozmetične nastavitve lahko spremenijo skrbniki prostora in bodo preglasile nastavitve odjemalca za ta prostor.",
|
"Space_Cosmetic_Settings": "Nekatere kozmetične nastavitve lahko spremenijo skrbniki prostora in bodo preglasile nastavitve odjemalca za ta prostor.",
|
||||||
"Split": "Razdelitev",
|
"Split": "Razdelitev",
|
||||||
"Split_All_Steps": "Vse vrstice razdelite na ločene korake.",
|
"Split_All_Steps": "Vse vrstice razdelite na ločene korake.",
|
||||||
|
"Start": "Začni",
|
||||||
"StartDate": "Začetni datum",
|
"StartDate": "Začetni datum",
|
||||||
"Starting_Day": "Začetni dan v tednu",
|
"Starting_Day": "Začetni dan v tednu",
|
||||||
"StartsWith": "Začne se s/z",
|
"StartsWith": "Začne se s/z",
|
||||||
@@ -549,10 +558,12 @@
|
|||||||
"Storage": "Zunanji pomnilnik",
|
"Storage": "Zunanji pomnilnik",
|
||||||
"StorageHelp": "Zunanje lokacije za shranjevanje, kjer je mogoče shraniti datoteke z recepti (slike/pdf) in sinhronizirati s Tandoorjem.",
|
"StorageHelp": "Zunanje lokacije za shranjevanje, kjer je mogoče shraniti datoteke z recepti (slike/pdf) in sinhronizirati s Tandoorjem.",
|
||||||
"StoragePasswordTokenHelp": "Shranjeno geslo/žeton ne bo nikoli prikazano. Spremeni se le, če v polje vnesete nekaj novega. ",
|
"StoragePasswordTokenHelp": "Shranjeno geslo/žeton ne bo nikoli prikazano. Spremeni se le, če v polje vnesete nekaj novega. ",
|
||||||
|
"Structured": "Strukturirano",
|
||||||
"SubstituteOnHand": "Pri roki imate nadomestek.",
|
"SubstituteOnHand": "Pri roki imate nadomestek.",
|
||||||
"Substitutes": "Nadomestki",
|
"Substitutes": "Nadomestki",
|
||||||
"Success": "Uspešno",
|
"Success": "Uspešno",
|
||||||
"SuccessClipboard": "Nakupovalni listek je kopiran v odložišče",
|
"SuccessClipboard": "Nakupovalni listek je kopiran v odložišče",
|
||||||
|
"Summary": "Povzetek",
|
||||||
"Sunday": "Nedelja",
|
"Sunday": "Nedelja",
|
||||||
"Supermarket": "Trgovina",
|
"Supermarket": "Trgovina",
|
||||||
"SupermarketCategoriesOnly": "Prikaži samo trgovinske kategorije",
|
"SupermarketCategoriesOnly": "Prikaži samo trgovinske kategorije",
|
||||||
@@ -862,13 +873,7 @@
|
|||||||
"warning_duplicate_filter": "Opozorilo: Zaradi tehničnih omejitev lahko uporaba več filtrov iste kombinacije (in/ali/ne) prinese nepričakovane rezultate.",
|
"warning_duplicate_filter": "Opozorilo: Zaradi tehničnih omejitev lahko uporaba več filtrov iste kombinacije (in/ali/ne) prinese nepričakovane rezultate.",
|
||||||
"warning_feature_beta": "Ta funkcija je trenutno v stanju BETA (testiranje). Pri uporabi te funkcije pričakujte napake in morebitne prelomne spremembe v prihodnosti (morda izgubite podatke, povezane s to funkcijo).",
|
"warning_feature_beta": "Ta funkcija je trenutno v stanju BETA (testiranje). Pri uporabi te funkcije pričakujte napake in morebitne prelomne spremembe v prihodnosti (morda izgubite podatke, povezane s to funkcijo).",
|
||||||
"warning_space_delete": "Izbrišete lahko svoj prostor, vključno z vsemi recepti, nakupovalnimi seznami, načrti obrokov in vsem drugim, kar ste ustvarili. Tega ni mogoče preklicati! Ste prepričani, da želite to storiti?",
|
"warning_space_delete": "Izbrišete lahko svoj prostor, vključno z vsemi recepti, nakupovalnimi seznami, načrti obrokov in vsem drugim, kar ste ustvarili. Tega ni mogoče preklicati! Ste prepričani, da želite to storiti?",
|
||||||
"APIKey": "API ključ",
|
"Shopping": "Nakupovanje",
|
||||||
"Model": "Model",
|
"ShoppingList": "Nakupovalni seznam",
|
||||||
"Structured": "Strukturirano",
|
"ShoppingListHelp": "Omogoča dodajanje vnosov na različne sezname. Uporablja se lahko za različne supermarkete, posebne ponudbe ali dogodke. "
|
||||||
"Summary": "Povzetek",
|
|
||||||
"AboutTandoor": "Tandoor je odprtokodna platforma za upravljanje receptov, načrtov obrokov, nakupovalnih seznamov in še več.",
|
|
||||||
"CreateAccount": "Ustvari račun",
|
|
||||||
"Finish": "Končaj",
|
|
||||||
"ImportIntoTandoorHelp": "Če želite uvoziti ta recept v svojo zbirko Tandoor, sledite naslednjim korakom.",
|
|
||||||
"SelfHosted": "Samostojno gostovanje"
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -36,6 +36,7 @@
|
|||||||
"AiProvider": "",
|
"AiProvider": "",
|
||||||
"AiProviderHelp": "",
|
"AiProviderHelp": "",
|
||||||
"Alignment": "Orientering",
|
"Alignment": "Orientering",
|
||||||
|
"All": "",
|
||||||
"AllRecipes": "Alla recept",
|
"AllRecipes": "Alla recept",
|
||||||
"Amount": "Mängd",
|
"Amount": "Mängd",
|
||||||
"App": "App",
|
"App": "App",
|
||||||
@@ -150,11 +151,13 @@
|
|||||||
"DelayFor": "Fördröjning på {hours} timmar",
|
"DelayFor": "Fördröjning på {hours} timmar",
|
||||||
"DelayUntil": "Fördröjning till",
|
"DelayUntil": "Fördröjning till",
|
||||||
"Delete": "Radera",
|
"Delete": "Radera",
|
||||||
|
"DeleteConfirmQuestion": "Är du säker på att du vill ta bort detta objekt?",
|
||||||
"DeleteShoppingConfirm": "Är du säker på att du vill ta bort all {food} från inköpslistan?",
|
"DeleteShoppingConfirm": "Är du säker på att du vill ta bort all {food} från inköpslistan?",
|
||||||
"DeleteSomething": "",
|
"DeleteSomething": "",
|
||||||
"Delete_All": "Radera alla",
|
"Delete_All": "Radera alla",
|
||||||
"Delete_Food": "Ta bort livsmedel",
|
"Delete_Food": "Ta bort livsmedel",
|
||||||
"Delete_Keyword": "Ta bort nyckelord",
|
"Delete_Keyword": "Ta bort nyckelord",
|
||||||
|
"Deleted": "Borttagen",
|
||||||
"Description": "Beskrivning",
|
"Description": "Beskrivning",
|
||||||
"Description_Replace": "Ersätt beskrivning",
|
"Description_Replace": "Ersätt beskrivning",
|
||||||
"Disable": "Inaktivera",
|
"Disable": "Inaktivera",
|
||||||
|
|||||||
@@ -26,6 +26,7 @@
|
|||||||
"AiProvider": "",
|
"AiProvider": "",
|
||||||
"AiProviderHelp": "",
|
"AiProviderHelp": "",
|
||||||
"Alignment": "Hizalama",
|
"Alignment": "Hizalama",
|
||||||
|
"All": "",
|
||||||
"Amount": "Miktar",
|
"Amount": "Miktar",
|
||||||
"App": "Uygulama",
|
"App": "Uygulama",
|
||||||
"Apply": "",
|
"Apply": "",
|
||||||
@@ -337,6 +338,7 @@
|
|||||||
"Ratings": "Derecelendirmeler",
|
"Ratings": "Derecelendirmeler",
|
||||||
"Recently_Viewed": "Son Görüntülenen",
|
"Recently_Viewed": "Son Görüntülenen",
|
||||||
"Recipe": "Tarif",
|
"Recipe": "Tarif",
|
||||||
|
"RecipeStructure": "",
|
||||||
"Recipe_Book": "Yemek Tarifi Kitabı",
|
"Recipe_Book": "Yemek Tarifi Kitabı",
|
||||||
"Recipe_Image": "Tarif Resmi",
|
"Recipe_Image": "Tarif Resmi",
|
||||||
"Recipes": "Tarifler",
|
"Recipes": "Tarifler",
|
||||||
@@ -394,6 +396,7 @@
|
|||||||
"SpacePrivateObjectsHelp": "",
|
"SpacePrivateObjectsHelp": "",
|
||||||
"Space_Cosmetic_Settings": "Bazı kozmetik ayarlar alan yöneticileri tarafından değiştirilebilir ve o alanın istemci ayarlarını geçersiz kılar.",
|
"Space_Cosmetic_Settings": "Bazı kozmetik ayarlar alan yöneticileri tarafından değiştirilebilir ve o alanın istemci ayarlarını geçersiz kılar.",
|
||||||
"Split_All_Steps": "Tüm satırları ayrı adımlara bölün.",
|
"Split_All_Steps": "Tüm satırları ayrı adımlara bölün.",
|
||||||
|
"Start": "",
|
||||||
"StartDate": "Başlangıç Tarihi",
|
"StartDate": "Başlangıç Tarihi",
|
||||||
"Starting_Day": "Haftanın başlangıç günü",
|
"Starting_Day": "Haftanın başlangıç günü",
|
||||||
"StartsWith": "",
|
"StartsWith": "",
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@@ -26,6 +26,7 @@
|
|||||||
"AiProvider": "",
|
"AiProvider": "",
|
||||||
"AiProviderHelp": "",
|
"AiProviderHelp": "",
|
||||||
"Alignment": "校准",
|
"Alignment": "校准",
|
||||||
|
"All": "",
|
||||||
"Amount": "数量",
|
"Amount": "数量",
|
||||||
"App": "应用",
|
"App": "应用",
|
||||||
"Apply": "",
|
"Apply": "",
|
||||||
@@ -337,6 +338,7 @@
|
|||||||
"Ratings": "等级",
|
"Ratings": "等级",
|
||||||
"Recently_Viewed": "最近浏览",
|
"Recently_Viewed": "最近浏览",
|
||||||
"Recipe": "食谱",
|
"Recipe": "食谱",
|
||||||
|
"RecipeStructure": "",
|
||||||
"Recipe_Book": "食谱书",
|
"Recipe_Book": "食谱书",
|
||||||
"Recipe_Image": "食谱图像",
|
"Recipe_Image": "食谱图像",
|
||||||
"Recipes": "食谱",
|
"Recipes": "食谱",
|
||||||
@@ -394,6 +396,7 @@
|
|||||||
"SpacePrivateObjectsHelp": "",
|
"SpacePrivateObjectsHelp": "",
|
||||||
"Space_Cosmetic_Settings": "空间管理员可以更改某些装饰设置,并将覆盖该空间的客户端设置。",
|
"Space_Cosmetic_Settings": "空间管理员可以更改某些装饰设置,并将覆盖该空间的客户端设置。",
|
||||||
"Split_All_Steps": "将所有行拆分为单独的步骤。",
|
"Split_All_Steps": "将所有行拆分为单独的步骤。",
|
||||||
|
"Start": "",
|
||||||
"StartDate": "开始日期",
|
"StartDate": "开始日期",
|
||||||
"Starting_Day": "一周中的第一天",
|
"Starting_Day": "一周中的第一天",
|
||||||
"StartsWith": "",
|
"StartsWith": "",
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@@ -31,6 +31,7 @@ models/FdcQueryFoods.ts
|
|||||||
models/Food.ts
|
models/Food.ts
|
||||||
models/FoodBatchUpdate.ts
|
models/FoodBatchUpdate.ts
|
||||||
models/FoodInheritField.ts
|
models/FoodInheritField.ts
|
||||||
|
models/FoodShopping.ts
|
||||||
models/FoodShoppingUpdate.ts
|
models/FoodShoppingUpdate.ts
|
||||||
models/FoodSimple.ts
|
models/FoodSimple.ts
|
||||||
models/GenericModelReference.ts
|
models/GenericModelReference.ts
|
||||||
@@ -94,6 +95,7 @@ models/PaginatedRecipeBookList.ts
|
|||||||
models/PaginatedRecipeImportList.ts
|
models/PaginatedRecipeImportList.ts
|
||||||
models/PaginatedRecipeOverviewList.ts
|
models/PaginatedRecipeOverviewList.ts
|
||||||
models/PaginatedShoppingListEntryList.ts
|
models/PaginatedShoppingListEntryList.ts
|
||||||
|
models/PaginatedShoppingListList.ts
|
||||||
models/PaginatedShoppingListRecipeList.ts
|
models/PaginatedShoppingListRecipeList.ts
|
||||||
models/PaginatedSpaceList.ts
|
models/PaginatedSpaceList.ts
|
||||||
models/PaginatedStepList.ts
|
models/PaginatedStepList.ts
|
||||||
@@ -140,6 +142,7 @@ models/PatchedRecipeBook.ts
|
|||||||
models/PatchedRecipeBookEntry.ts
|
models/PatchedRecipeBookEntry.ts
|
||||||
models/PatchedRecipeImport.ts
|
models/PatchedRecipeImport.ts
|
||||||
models/PatchedSearchPreference.ts
|
models/PatchedSearchPreference.ts
|
||||||
|
models/PatchedShoppingList.ts
|
||||||
models/PatchedShoppingListEntry.ts
|
models/PatchedShoppingListEntry.ts
|
||||||
models/PatchedShoppingListRecipe.ts
|
models/PatchedShoppingListRecipe.ts
|
||||||
models/PatchedSpace.ts
|
models/PatchedSpace.ts
|
||||||
@@ -174,6 +177,7 @@ models/SearchFields.ts
|
|||||||
models/SearchPreference.ts
|
models/SearchPreference.ts
|
||||||
models/ServerSettings.ts
|
models/ServerSettings.ts
|
||||||
models/ShareLink.ts
|
models/ShareLink.ts
|
||||||
|
models/ShoppingList.ts
|
||||||
models/ShoppingListEntry.ts
|
models/ShoppingListEntry.ts
|
||||||
models/ShoppingListEntryBulk.ts
|
models/ShoppingListEntryBulk.ts
|
||||||
models/ShoppingListEntryBulkCreate.ts
|
models/ShoppingListEntryBulkCreate.ts
|
||||||
|
|||||||
@@ -85,6 +85,7 @@ import type {
|
|||||||
PaginatedRecipeImportList,
|
PaginatedRecipeImportList,
|
||||||
PaginatedRecipeOverviewList,
|
PaginatedRecipeOverviewList,
|
||||||
PaginatedShoppingListEntryList,
|
PaginatedShoppingListEntryList,
|
||||||
|
PaginatedShoppingListList,
|
||||||
PaginatedShoppingListRecipeList,
|
PaginatedShoppingListRecipeList,
|
||||||
PaginatedSpaceList,
|
PaginatedSpaceList,
|
||||||
PaginatedStepList,
|
PaginatedStepList,
|
||||||
@@ -131,6 +132,7 @@ import type {
|
|||||||
PatchedRecipeBookEntry,
|
PatchedRecipeBookEntry,
|
||||||
PatchedRecipeImport,
|
PatchedRecipeImport,
|
||||||
PatchedSearchPreference,
|
PatchedSearchPreference,
|
||||||
|
PatchedShoppingList,
|
||||||
PatchedShoppingListEntry,
|
PatchedShoppingListEntry,
|
||||||
PatchedShoppingListRecipe,
|
PatchedShoppingListRecipe,
|
||||||
PatchedSpace,
|
PatchedSpace,
|
||||||
@@ -163,6 +165,7 @@ import type {
|
|||||||
SearchPreference,
|
SearchPreference,
|
||||||
ServerSettings,
|
ServerSettings,
|
||||||
ShareLink,
|
ShareLink,
|
||||||
|
ShoppingList,
|
||||||
ShoppingListEntry,
|
ShoppingListEntry,
|
||||||
ShoppingListEntryBulk,
|
ShoppingListEntryBulk,
|
||||||
ShoppingListEntryBulkCreate,
|
ShoppingListEntryBulkCreate,
|
||||||
@@ -324,6 +327,8 @@ import {
|
|||||||
PaginatedRecipeOverviewListToJSON,
|
PaginatedRecipeOverviewListToJSON,
|
||||||
PaginatedShoppingListEntryListFromJSON,
|
PaginatedShoppingListEntryListFromJSON,
|
||||||
PaginatedShoppingListEntryListToJSON,
|
PaginatedShoppingListEntryListToJSON,
|
||||||
|
PaginatedShoppingListListFromJSON,
|
||||||
|
PaginatedShoppingListListToJSON,
|
||||||
PaginatedShoppingListRecipeListFromJSON,
|
PaginatedShoppingListRecipeListFromJSON,
|
||||||
PaginatedShoppingListRecipeListToJSON,
|
PaginatedShoppingListRecipeListToJSON,
|
||||||
PaginatedSpaceListFromJSON,
|
PaginatedSpaceListFromJSON,
|
||||||
@@ -416,6 +421,8 @@ import {
|
|||||||
PatchedRecipeImportToJSON,
|
PatchedRecipeImportToJSON,
|
||||||
PatchedSearchPreferenceFromJSON,
|
PatchedSearchPreferenceFromJSON,
|
||||||
PatchedSearchPreferenceToJSON,
|
PatchedSearchPreferenceToJSON,
|
||||||
|
PatchedShoppingListFromJSON,
|
||||||
|
PatchedShoppingListToJSON,
|
||||||
PatchedShoppingListEntryFromJSON,
|
PatchedShoppingListEntryFromJSON,
|
||||||
PatchedShoppingListEntryToJSON,
|
PatchedShoppingListEntryToJSON,
|
||||||
PatchedShoppingListRecipeFromJSON,
|
PatchedShoppingListRecipeFromJSON,
|
||||||
@@ -480,6 +487,8 @@ import {
|
|||||||
ServerSettingsToJSON,
|
ServerSettingsToJSON,
|
||||||
ShareLinkFromJSON,
|
ShareLinkFromJSON,
|
||||||
ShareLinkToJSON,
|
ShareLinkToJSON,
|
||||||
|
ShoppingListFromJSON,
|
||||||
|
ShoppingListToJSON,
|
||||||
ShoppingListEntryFromJSON,
|
ShoppingListEntryFromJSON,
|
||||||
ShoppingListEntryToJSON,
|
ShoppingListEntryToJSON,
|
||||||
ShoppingListEntryBulkFromJSON,
|
ShoppingListEntryBulkFromJSON,
|
||||||
@@ -1955,6 +1964,21 @@ export interface ApiShareLinkRetrieveRequest {
|
|||||||
id: number;
|
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 {
|
export interface ApiShoppingListEntryBulkCreateRequest {
|
||||||
shoppingListEntryBulk: Omit<ShoppingListEntryBulk, 'timestamp'>;
|
shoppingListEntryBulk: Omit<ShoppingListEntryBulk, 'timestamp'>;
|
||||||
}
|
}
|
||||||
@@ -1988,6 +2012,30 @@ export interface ApiShoppingListEntryUpdateRequest {
|
|||||||
shoppingListEntry: Omit<ShoppingListEntry, 'listRecipeData'|'createdBy'|'createdAt'|'updatedAt'>;
|
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 {
|
export interface ApiShoppingListRecipeBulkCreateEntriesCreateRequest {
|
||||||
id: number;
|
id: number;
|
||||||
shoppingListEntryBulkCreate: ShoppingListEntryBulkCreate;
|
shoppingListEntryBulkCreate: ShoppingListEntryBulkCreate;
|
||||||
@@ -2021,6 +2069,15 @@ export interface ApiShoppingListRecipeUpdateRequest {
|
|||||||
shoppingListRecipe: Omit<ShoppingListRecipe, 'recipeData'|'mealPlanData'|'createdBy'>;
|
shoppingListRecipe: Omit<ShoppingListRecipe, 'recipeData'|'mealPlanData'|'createdBy'>;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface ApiShoppingListRetrieveRequest {
|
||||||
|
id: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface ApiShoppingListUpdateRequest {
|
||||||
|
id: number;
|
||||||
|
shoppingList?: ShoppingList;
|
||||||
|
}
|
||||||
|
|
||||||
export interface ApiSpaceCreateRequest {
|
export interface ApiSpaceCreateRequest {
|
||||||
space?: Omit<Space, 'createdBy'|'createdAt'|'maxRecipes'|'maxFileStorageMb'|'maxUsers'|'allowSharing'|'demo'|'userCount'|'recipeCount'|'fileSizeMb'|'aiMonthlyCreditsUsed'>;
|
space?: Omit<Space, 'createdBy'|'createdAt'|'maxRecipes'|'maxFileStorageMb'|'maxUsers'|'allowSharing'|'demo'|'userCount'|'recipeCount'|'fileSizeMb'|'aiMonthlyCreditsUsed'>;
|
||||||
}
|
}
|
||||||
@@ -14551,6 +14608,124 @@ export class ApiApi extends runtime.BaseAPI {
|
|||||||
return await response.value();
|
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
|
* 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
|
||||||
*/
|
*/
|
||||||
@@ -14837,6 +15012,182 @@ export class ApiApi extends runtime.BaseAPI {
|
|||||||
return await response.value();
|
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/
|
* logs request counts to redis cache total/per user/
|
||||||
*/
|
*/
|
||||||
@@ -15126,6 +15477,83 @@ export class ApiApi extends runtime.BaseAPI {
|
|||||||
return await response.value();
|
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/
|
* logs request counts to redis cache total/per user/
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -13,6 +13,12 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
import { mapValues } from '../runtime';
|
import { mapValues } from '../runtime';
|
||||||
|
import type { ShoppingList } from './ShoppingList';
|
||||||
|
import {
|
||||||
|
ShoppingListFromJSON,
|
||||||
|
ShoppingListFromJSONTyped,
|
||||||
|
ShoppingListToJSON,
|
||||||
|
} from './ShoppingList';
|
||||||
import type { SupermarketCategory } from './SupermarketCategory';
|
import type { SupermarketCategory } from './SupermarketCategory';
|
||||||
import {
|
import {
|
||||||
SupermarketCategoryFromJSON,
|
SupermarketCategoryFromJSON,
|
||||||
@@ -235,6 +241,12 @@ export interface Food {
|
|||||||
* @memberof Food
|
* @memberof Food
|
||||||
*/
|
*/
|
||||||
openDataSlug?: string;
|
openDataSlug?: string;
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @type {Array<ShoppingList>}
|
||||||
|
* @memberof Food
|
||||||
|
*/
|
||||||
|
shoppingLists?: Array<ShoppingList>;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -284,6 +296,7 @@ export function FoodFromJSONTyped(json: any, ignoreDiscriminator: boolean): Food
|
|||||||
'substituteOnhand': json['substitute_onhand'],
|
'substituteOnhand': json['substitute_onhand'],
|
||||||
'childInheritFields': json['child_inherit_fields'] == null ? undefined : ((json['child_inherit_fields'] as Array<any>).map(FoodInheritFieldFromJSON)),
|
'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'],
|
'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)),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -312,6 +325,7 @@ export function FoodToJSON(value?: Omit<Food, 'shopping'|'parent'|'numchild'|'fu
|
|||||||
'substitute_children': value['substituteChildren'],
|
'substitute_children': value['substituteChildren'],
|
||||||
'child_inherit_fields': value['childInheritFields'] == null ? undefined : ((value['childInheritFields'] as Array<any>).map(FoodInheritFieldToJSON)),
|
'child_inherit_fields': value['childInheritFields'] == null ? undefined : ((value['childInheritFields'] as Array<any>).map(FoodInheritFieldToJSON)),
|
||||||
'open_data_slug': value['openDataSlug'],
|
'open_data_slug': value['openDataSlug'],
|
||||||
|
'shopping_lists': value['shoppingLists'] == null ? undefined : ((value['shoppingLists'] as Array<any>).map(ShoppingListToJSON)),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
106
vue3/src/openapi/models/FoodShopping.ts
Normal file
106
vue3/src/openapi/models/FoodShopping.ts
Normal file
@@ -0,0 +1,106 @@
|
|||||||
|
/* 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';
|
||||||
|
import type { SupermarketCategory } from './SupermarketCategory';
|
||||||
|
import {
|
||||||
|
SupermarketCategoryFromJSON,
|
||||||
|
SupermarketCategoryFromJSONTyped,
|
||||||
|
SupermarketCategoryToJSON,
|
||||||
|
} from './SupermarketCategory';
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @export
|
||||||
|
* @interface FoodShopping
|
||||||
|
*/
|
||||||
|
export interface FoodShopping {
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @type {number}
|
||||||
|
* @memberof FoodShopping
|
||||||
|
*/
|
||||||
|
id?: number;
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @type {string}
|
||||||
|
* @memberof FoodShopping
|
||||||
|
*/
|
||||||
|
name: string;
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @type {string}
|
||||||
|
* @memberof FoodShopping
|
||||||
|
*/
|
||||||
|
pluralName?: string;
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @type {SupermarketCategory}
|
||||||
|
* @memberof FoodShopping
|
||||||
|
*/
|
||||||
|
readonly supermarketCategory: SupermarketCategory;
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @type {Array<ShoppingList>}
|
||||||
|
* @memberof FoodShopping
|
||||||
|
*/
|
||||||
|
readonly shoppingLists: Array<ShoppingList>;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check if a given object implements the FoodShopping interface.
|
||||||
|
*/
|
||||||
|
export function instanceOfFoodShopping(value: object): value is FoodShopping {
|
||||||
|
if (!('name' in value) || value['name'] === undefined) return false;
|
||||||
|
if (!('supermarketCategory' in value) || value['supermarketCategory'] === undefined) return false;
|
||||||
|
if (!('shoppingLists' in value) || value['shoppingLists'] === undefined) return false;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function FoodShoppingFromJSON(json: any): FoodShopping {
|
||||||
|
return FoodShoppingFromJSONTyped(json, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function FoodShoppingFromJSONTyped(json: any, ignoreDiscriminator: boolean): FoodShopping {
|
||||||
|
if (json == null) {
|
||||||
|
return json;
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
|
||||||
|
'id': json['id'] == null ? undefined : json['id'],
|
||||||
|
'name': json['name'],
|
||||||
|
'pluralName': json['plural_name'] == null ? undefined : json['plural_name'],
|
||||||
|
'supermarketCategory': SupermarketCategoryFromJSON(json['supermarket_category']),
|
||||||
|
'shoppingLists': ((json['shopping_lists'] as Array<any>).map(ShoppingListFromJSON)),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export function FoodShoppingToJSON(value?: Omit<FoodShopping, 'supermarketCategory'|'shoppingLists'> | null): any {
|
||||||
|
if (value == null) {
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
|
||||||
|
'id': value['id'],
|
||||||
|
'name': value['name'],
|
||||||
|
'plural_name': value['pluralName'],
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
101
vue3/src/openapi/models/PaginatedShoppingListList.ts
Normal file
101
vue3/src/openapi/models/PaginatedShoppingListList.ts
Normal file
@@ -0,0 +1,101 @@
|
|||||||
|
/* 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,6 +13,12 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
import { mapValues } from '../runtime';
|
import { mapValues } from '../runtime';
|
||||||
|
import type { ShoppingList } from './ShoppingList';
|
||||||
|
import {
|
||||||
|
ShoppingListFromJSON,
|
||||||
|
ShoppingListFromJSONTyped,
|
||||||
|
ShoppingListToJSON,
|
||||||
|
} from './ShoppingList';
|
||||||
import type { SupermarketCategory } from './SupermarketCategory';
|
import type { SupermarketCategory } from './SupermarketCategory';
|
||||||
import {
|
import {
|
||||||
SupermarketCategoryFromJSON,
|
SupermarketCategoryFromJSON,
|
||||||
@@ -235,6 +241,12 @@ export interface PatchedFood {
|
|||||||
* @memberof PatchedFood
|
* @memberof PatchedFood
|
||||||
*/
|
*/
|
||||||
openDataSlug?: string;
|
openDataSlug?: string;
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @type {Array<ShoppingList>}
|
||||||
|
* @memberof PatchedFood
|
||||||
|
*/
|
||||||
|
shoppingLists?: Array<ShoppingList>;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -278,6 +290,7 @@ export function PatchedFoodFromJSONTyped(json: any, ignoreDiscriminator: boolean
|
|||||||
'substituteOnhand': json['substitute_onhand'] == null ? undefined : json['substitute_onhand'],
|
'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)),
|
'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'],
|
'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)),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -306,6 +319,7 @@ export function PatchedFoodToJSON(value?: Omit<PatchedFood, 'shopping'|'parent'|
|
|||||||
'substitute_children': value['substituteChildren'],
|
'substitute_children': value['substituteChildren'],
|
||||||
'child_inherit_fields': value['childInheritFields'] == null ? undefined : ((value['childInheritFields'] as Array<any>).map(FoodInheritFieldToJSON)),
|
'child_inherit_fields': value['childInheritFields'] == null ? undefined : ((value['childInheritFields'] as Array<any>).map(FoodInheritFieldToJSON)),
|
||||||
'open_data_slug': value['openDataSlug'],
|
'open_data_slug': value['openDataSlug'],
|
||||||
|
'shopping_lists': value['shoppingLists'] == null ? undefined : ((value['shoppingLists'] as Array<any>).map(ShoppingListToJSON)),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
84
vue3/src/openapi/models/PatchedShoppingList.ts
Normal file
84
vue3/src/openapi/models/PatchedShoppingList.ts
Normal file
@@ -0,0 +1,84 @@
|
|||||||
|
/* 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,6 +19,18 @@ import {
|
|||||||
UserFromJSONTyped,
|
UserFromJSONTyped,
|
||||||
UserToJSON,
|
UserToJSON,
|
||||||
} from './User';
|
} from './User';
|
||||||
|
import type { FoodShopping } from './FoodShopping';
|
||||||
|
import {
|
||||||
|
FoodShoppingFromJSON,
|
||||||
|
FoodShoppingFromJSONTyped,
|
||||||
|
FoodShoppingToJSON,
|
||||||
|
} from './FoodShopping';
|
||||||
|
import type { ShoppingList } from './ShoppingList';
|
||||||
|
import {
|
||||||
|
ShoppingListFromJSON,
|
||||||
|
ShoppingListFromJSONTyped,
|
||||||
|
ShoppingListToJSON,
|
||||||
|
} from './ShoppingList';
|
||||||
import type { ShoppingListRecipe } from './ShoppingListRecipe';
|
import type { ShoppingListRecipe } from './ShoppingListRecipe';
|
||||||
import {
|
import {
|
||||||
ShoppingListRecipeFromJSON,
|
ShoppingListRecipeFromJSON,
|
||||||
@@ -31,12 +43,6 @@ import {
|
|||||||
UnitFromJSONTyped,
|
UnitFromJSONTyped,
|
||||||
UnitToJSON,
|
UnitToJSON,
|
||||||
} from './Unit';
|
} from './Unit';
|
||||||
import type { Food } from './Food';
|
|
||||||
import {
|
|
||||||
FoodFromJSON,
|
|
||||||
FoodFromJSONTyped,
|
|
||||||
FoodToJSON,
|
|
||||||
} from './Food';
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Adds nested create feature
|
* Adds nested create feature
|
||||||
@@ -58,10 +64,16 @@ export interface PatchedShoppingListEntry {
|
|||||||
listRecipe?: number;
|
listRecipe?: number;
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
* @type {Food}
|
* @type {Array<ShoppingList>}
|
||||||
* @memberof PatchedShoppingListEntry
|
* @memberof PatchedShoppingListEntry
|
||||||
*/
|
*/
|
||||||
food?: Food;
|
shoppingLists?: Array<ShoppingList>;
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @type {FoodShopping}
|
||||||
|
* @memberof PatchedShoppingListEntry
|
||||||
|
*/
|
||||||
|
food?: FoodShopping;
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
* @type {Unit}
|
* @type {Unit}
|
||||||
@@ -155,7 +167,8 @@ export function PatchedShoppingListEntryFromJSONTyped(json: any, ignoreDiscrimin
|
|||||||
|
|
||||||
'id': json['id'] == null ? undefined : json['id'],
|
'id': json['id'] == null ? undefined : json['id'],
|
||||||
'listRecipe': json['list_recipe'] == null ? undefined : json['list_recipe'],
|
'listRecipe': json['list_recipe'] == null ? undefined : json['list_recipe'],
|
||||||
'food': json['food'] == null ? undefined : FoodFromJSON(json['food']),
|
'shoppingLists': json['shopping_lists'] == null ? undefined : ((json['shopping_lists'] as Array<any>).map(ShoppingListFromJSON)),
|
||||||
|
'food': json['food'] == null ? undefined : FoodShoppingFromJSON(json['food']),
|
||||||
'unit': json['unit'] == null ? undefined : UnitFromJSON(json['unit']),
|
'unit': json['unit'] == null ? undefined : UnitFromJSON(json['unit']),
|
||||||
'amount': json['amount'] == null ? undefined : json['amount'],
|
'amount': json['amount'] == null ? undefined : json['amount'],
|
||||||
'order': json['order'] == null ? undefined : json['order'],
|
'order': json['order'] == null ? undefined : json['order'],
|
||||||
@@ -179,7 +192,8 @@ export function PatchedShoppingListEntryToJSON(value?: Omit<PatchedShoppingListE
|
|||||||
|
|
||||||
'id': value['id'],
|
'id': value['id'],
|
||||||
'list_recipe': value['listRecipe'],
|
'list_recipe': value['listRecipe'],
|
||||||
'food': FoodToJSON(value['food']),
|
'shopping_lists': value['shoppingLists'] == null ? undefined : ((value['shoppingLists'] as Array<any>).map(ShoppingListToJSON)),
|
||||||
|
'food': FoodShoppingToJSON(value['food']),
|
||||||
'unit': UnitToJSON(value['unit']),
|
'unit': UnitToJSON(value['unit']),
|
||||||
'amount': value['amount'],
|
'amount': value['amount'],
|
||||||
'order': value['order'],
|
'order': value['order'],
|
||||||
|
|||||||
@@ -62,18 +62,18 @@ export interface PatchedShoppingListRecipe {
|
|||||||
* @memberof PatchedShoppingListRecipe
|
* @memberof PatchedShoppingListRecipe
|
||||||
*/
|
*/
|
||||||
readonly recipeData?: RecipeOverview;
|
readonly recipeData?: RecipeOverview;
|
||||||
/**
|
|
||||||
*
|
|
||||||
* @type {number}
|
|
||||||
* @memberof PatchedShoppingListRecipe
|
|
||||||
*/
|
|
||||||
mealplan?: number;
|
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
* @type {MealPlan}
|
* @type {MealPlan}
|
||||||
* @memberof PatchedShoppingListRecipe
|
* @memberof PatchedShoppingListRecipe
|
||||||
*/
|
*/
|
||||||
readonly mealPlanData?: MealPlan;
|
readonly mealPlanData?: MealPlan;
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @type {number}
|
||||||
|
* @memberof PatchedShoppingListRecipe
|
||||||
|
*/
|
||||||
|
mealplan?: number;
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
* @type {number}
|
* @type {number}
|
||||||
@@ -109,8 +109,8 @@ export function PatchedShoppingListRecipeFromJSONTyped(json: any, ignoreDiscrimi
|
|||||||
'name': json['name'] == null ? undefined : json['name'],
|
'name': json['name'] == null ? undefined : json['name'],
|
||||||
'recipe': json['recipe'] == null ? undefined : json['recipe'],
|
'recipe': json['recipe'] == null ? undefined : json['recipe'],
|
||||||
'recipeData': json['recipe_data'] == null ? undefined : RecipeOverviewFromJSON(json['recipe_data']),
|
'recipeData': json['recipe_data'] == null ? undefined : RecipeOverviewFromJSON(json['recipe_data']),
|
||||||
'mealplan': json['mealplan'] == null ? undefined : json['mealplan'],
|
|
||||||
'mealPlanData': json['meal_plan_data'] == null ? undefined : MealPlanFromJSON(json['meal_plan_data']),
|
'mealPlanData': json['meal_plan_data'] == null ? undefined : MealPlanFromJSON(json['meal_plan_data']),
|
||||||
|
'mealplan': json['mealplan'] == null ? undefined : json['mealplan'],
|
||||||
'servings': json['servings'] == null ? undefined : json['servings'],
|
'servings': json['servings'] == null ? undefined : json['servings'],
|
||||||
'createdBy': json['created_by'] == null ? undefined : UserFromJSON(json['created_by']),
|
'createdBy': json['created_by'] == null ? undefined : UserFromJSON(json['created_by']),
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -19,6 +19,12 @@ import {
|
|||||||
SupermarketCategoryRelationFromJSONTyped,
|
SupermarketCategoryRelationFromJSONTyped,
|
||||||
SupermarketCategoryRelationToJSON,
|
SupermarketCategoryRelationToJSON,
|
||||||
} from './SupermarketCategoryRelation';
|
} from './SupermarketCategoryRelation';
|
||||||
|
import type { ShoppingList } from './ShoppingList';
|
||||||
|
import {
|
||||||
|
ShoppingListFromJSON,
|
||||||
|
ShoppingListFromJSONTyped,
|
||||||
|
ShoppingListToJSON,
|
||||||
|
} from './ShoppingList';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Moves `UniqueValidator`'s from the validation stage to the save stage.
|
* Moves `UniqueValidator`'s from the validation stage to the save stage.
|
||||||
@@ -78,6 +84,12 @@ export interface PatchedSupermarket {
|
|||||||
* @memberof PatchedSupermarket
|
* @memberof PatchedSupermarket
|
||||||
*/
|
*/
|
||||||
description?: string;
|
description?: string;
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @type {Array<ShoppingList>}
|
||||||
|
* @memberof PatchedSupermarket
|
||||||
|
*/
|
||||||
|
shoppingLists?: Array<ShoppingList>;
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
* @type {Array<SupermarketCategoryRelation>}
|
* @type {Array<SupermarketCategoryRelation>}
|
||||||
@@ -112,6 +124,7 @@ export function PatchedSupermarketFromJSONTyped(json: any, ignoreDiscriminator:
|
|||||||
'id': json['id'] == null ? undefined : json['id'],
|
'id': json['id'] == null ? undefined : json['id'],
|
||||||
'name': json['name'] == null ? undefined : json['name'],
|
'name': json['name'] == null ? undefined : json['name'],
|
||||||
'description': json['description'] == null ? undefined : json['description'],
|
'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)),
|
'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'],
|
'openDataSlug': json['open_data_slug'] == null ? undefined : json['open_data_slug'],
|
||||||
};
|
};
|
||||||
@@ -126,6 +139,7 @@ export function PatchedSupermarketToJSON(value?: Omit<PatchedSupermarket, 'categ
|
|||||||
'id': value['id'],
|
'id': value['id'],
|
||||||
'name': value['name'],
|
'name': value['name'],
|
||||||
'description': value['description'],
|
'description': value['description'],
|
||||||
|
'shopping_lists': value['shoppingLists'] == null ? undefined : ((value['shoppingLists'] as Array<any>).map(ShoppingListToJSON)),
|
||||||
'open_data_slug': value['openDataSlug'],
|
'open_data_slug': value['openDataSlug'],
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -200,6 +200,12 @@ export interface PatchedUserPreference {
|
|||||||
* @memberof PatchedUserPreference
|
* @memberof PatchedUserPreference
|
||||||
*/
|
*/
|
||||||
csvPrefix?: string;
|
csvPrefix?: string;
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @type {boolean}
|
||||||
|
* @memberof PatchedUserPreference
|
||||||
|
*/
|
||||||
|
shoppingUpdateFoodLists?: boolean;
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
* @type {boolean}
|
* @type {boolean}
|
||||||
@@ -273,6 +279,7 @@ export function PatchedUserPreferenceFromJSONTyped(json: any, ignoreDiscriminato
|
|||||||
'shoppingRecentDays': json['shopping_recent_days'] == null ? undefined : json['shopping_recent_days'],
|
'shoppingRecentDays': json['shopping_recent_days'] == null ? undefined : json['shopping_recent_days'],
|
||||||
'csvDelim': json['csv_delim'] == null ? undefined : json['csv_delim'],
|
'csvDelim': json['csv_delim'] == null ? undefined : json['csv_delim'],
|
||||||
'csvPrefix': json['csv_prefix'] == null ? undefined : json['csv_prefix'],
|
'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'],
|
'filterToSupermarket': json['filter_to_supermarket'] == null ? undefined : json['filter_to_supermarket'],
|
||||||
'shoppingAddOnhand': json['shopping_add_onhand'] == null ? undefined : json['shopping_add_onhand'],
|
'shoppingAddOnhand': json['shopping_add_onhand'] == null ? undefined : json['shopping_add_onhand'],
|
||||||
'leftHanded': json['left_handed'] == null ? undefined : json['left_handed'],
|
'leftHanded': json['left_handed'] == null ? undefined : json['left_handed'],
|
||||||
@@ -309,6 +316,7 @@ export function PatchedUserPreferenceToJSON(value?: Omit<PatchedUserPreference,
|
|||||||
'shopping_recent_days': value['shoppingRecentDays'],
|
'shopping_recent_days': value['shoppingRecentDays'],
|
||||||
'csv_delim': value['csvDelim'],
|
'csv_delim': value['csvDelim'],
|
||||||
'csv_prefix': value['csvPrefix'],
|
'csv_prefix': value['csvPrefix'],
|
||||||
|
'shopping_update_food_lists': value['shoppingUpdateFoodLists'],
|
||||||
'filter_to_supermarket': value['filterToSupermarket'],
|
'filter_to_supermarket': value['filterToSupermarket'],
|
||||||
'shopping_add_onhand': value['shoppingAddOnhand'],
|
'shopping_add_onhand': value['shoppingAddOnhand'],
|
||||||
'left_handed': value['leftHanded'],
|
'left_handed': value['leftHanded'],
|
||||||
|
|||||||
84
vue3/src/openapi/models/ShoppingList.ts
Normal file
84
vue3/src/openapi/models/ShoppingList.ts
Normal file
@@ -0,0 +1,84 @@
|
|||||||
|
/* 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,6 +19,18 @@ import {
|
|||||||
UserFromJSONTyped,
|
UserFromJSONTyped,
|
||||||
UserToJSON,
|
UserToJSON,
|
||||||
} from './User';
|
} from './User';
|
||||||
|
import type { FoodShopping } from './FoodShopping';
|
||||||
|
import {
|
||||||
|
FoodShoppingFromJSON,
|
||||||
|
FoodShoppingFromJSONTyped,
|
||||||
|
FoodShoppingToJSON,
|
||||||
|
} from './FoodShopping';
|
||||||
|
import type { ShoppingList } from './ShoppingList';
|
||||||
|
import {
|
||||||
|
ShoppingListFromJSON,
|
||||||
|
ShoppingListFromJSONTyped,
|
||||||
|
ShoppingListToJSON,
|
||||||
|
} from './ShoppingList';
|
||||||
import type { ShoppingListRecipe } from './ShoppingListRecipe';
|
import type { ShoppingListRecipe } from './ShoppingListRecipe';
|
||||||
import {
|
import {
|
||||||
ShoppingListRecipeFromJSON,
|
ShoppingListRecipeFromJSON,
|
||||||
@@ -31,12 +43,6 @@ import {
|
|||||||
UnitFromJSONTyped,
|
UnitFromJSONTyped,
|
||||||
UnitToJSON,
|
UnitToJSON,
|
||||||
} from './Unit';
|
} from './Unit';
|
||||||
import type { Food } from './Food';
|
|
||||||
import {
|
|
||||||
FoodFromJSON,
|
|
||||||
FoodFromJSONTyped,
|
|
||||||
FoodToJSON,
|
|
||||||
} from './Food';
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Adds nested create feature
|
* Adds nested create feature
|
||||||
@@ -58,10 +64,16 @@ export interface ShoppingListEntry {
|
|||||||
listRecipe?: number;
|
listRecipe?: number;
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
* @type {Food}
|
* @type {Array<ShoppingList>}
|
||||||
* @memberof ShoppingListEntry
|
* @memberof ShoppingListEntry
|
||||||
*/
|
*/
|
||||||
food: Food | null;
|
shoppingLists?: Array<ShoppingList>;
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @type {FoodShopping}
|
||||||
|
* @memberof ShoppingListEntry
|
||||||
|
*/
|
||||||
|
food: FoodShopping | null;
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
* @type {Unit}
|
* @type {Unit}
|
||||||
@@ -161,7 +173,8 @@ export function ShoppingListEntryFromJSONTyped(json: any, ignoreDiscriminator: b
|
|||||||
|
|
||||||
'id': json['id'] == null ? undefined : json['id'],
|
'id': json['id'] == null ? undefined : json['id'],
|
||||||
'listRecipe': json['list_recipe'] == null ? undefined : json['list_recipe'],
|
'listRecipe': json['list_recipe'] == null ? undefined : json['list_recipe'],
|
||||||
'food': FoodFromJSON(json['food']),
|
'shoppingLists': json['shopping_lists'] == null ? undefined : ((json['shopping_lists'] as Array<any>).map(ShoppingListFromJSON)),
|
||||||
|
'food': FoodShoppingFromJSON(json['food']),
|
||||||
'unit': json['unit'] == null ? undefined : UnitFromJSON(json['unit']),
|
'unit': json['unit'] == null ? undefined : UnitFromJSON(json['unit']),
|
||||||
'amount': json['amount'],
|
'amount': json['amount'],
|
||||||
'order': json['order'] == null ? undefined : json['order'],
|
'order': json['order'] == null ? undefined : json['order'],
|
||||||
@@ -185,7 +198,8 @@ export function ShoppingListEntryToJSON(value?: Omit<ShoppingListEntry, 'listRec
|
|||||||
|
|
||||||
'id': value['id'],
|
'id': value['id'],
|
||||||
'list_recipe': value['listRecipe'],
|
'list_recipe': value['listRecipe'],
|
||||||
'food': FoodToJSON(value['food']),
|
'shopping_lists': value['shoppingLists'] == null ? undefined : ((value['shoppingLists'] as Array<any>).map(ShoppingListToJSON)),
|
||||||
|
'food': FoodShoppingToJSON(value['food']),
|
||||||
'unit': UnitToJSON(value['unit']),
|
'unit': UnitToJSON(value['unit']),
|
||||||
'amount': value['amount'],
|
'amount': value['amount'],
|
||||||
'order': value['order'],
|
'order': value['order'],
|
||||||
|
|||||||
@@ -62,18 +62,18 @@ export interface ShoppingListRecipe {
|
|||||||
* @memberof ShoppingListRecipe
|
* @memberof ShoppingListRecipe
|
||||||
*/
|
*/
|
||||||
readonly recipeData: RecipeOverview;
|
readonly recipeData: RecipeOverview;
|
||||||
/**
|
|
||||||
*
|
|
||||||
* @type {number}
|
|
||||||
* @memberof ShoppingListRecipe
|
|
||||||
*/
|
|
||||||
mealplan?: number;
|
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
* @type {MealPlan}
|
* @type {MealPlan}
|
||||||
* @memberof ShoppingListRecipe
|
* @memberof ShoppingListRecipe
|
||||||
*/
|
*/
|
||||||
readonly mealPlanData: MealPlan;
|
readonly mealPlanData: MealPlan;
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @type {number}
|
||||||
|
* @memberof ShoppingListRecipe
|
||||||
|
*/
|
||||||
|
mealplan?: number;
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
* @type {number}
|
* @type {number}
|
||||||
@@ -113,8 +113,8 @@ export function ShoppingListRecipeFromJSONTyped(json: any, ignoreDiscriminator:
|
|||||||
'name': json['name'] == null ? undefined : json['name'],
|
'name': json['name'] == null ? undefined : json['name'],
|
||||||
'recipe': json['recipe'] == null ? undefined : json['recipe'],
|
'recipe': json['recipe'] == null ? undefined : json['recipe'],
|
||||||
'recipeData': RecipeOverviewFromJSON(json['recipe_data']),
|
'recipeData': RecipeOverviewFromJSON(json['recipe_data']),
|
||||||
'mealplan': json['mealplan'] == null ? undefined : json['mealplan'],
|
|
||||||
'mealPlanData': MealPlanFromJSON(json['meal_plan_data']),
|
'mealPlanData': MealPlanFromJSON(json['meal_plan_data']),
|
||||||
|
'mealplan': json['mealplan'] == null ? undefined : json['mealplan'],
|
||||||
'servings': json['servings'],
|
'servings': json['servings'],
|
||||||
'createdBy': UserFromJSON(json['created_by']),
|
'createdBy': UserFromJSON(json['created_by']),
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -19,6 +19,12 @@ import {
|
|||||||
SupermarketCategoryRelationFromJSONTyped,
|
SupermarketCategoryRelationFromJSONTyped,
|
||||||
SupermarketCategoryRelationToJSON,
|
SupermarketCategoryRelationToJSON,
|
||||||
} from './SupermarketCategoryRelation';
|
} from './SupermarketCategoryRelation';
|
||||||
|
import type { ShoppingList } from './ShoppingList';
|
||||||
|
import {
|
||||||
|
ShoppingListFromJSON,
|
||||||
|
ShoppingListFromJSONTyped,
|
||||||
|
ShoppingListToJSON,
|
||||||
|
} from './ShoppingList';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Moves `UniqueValidator`'s from the validation stage to the save stage.
|
* Moves `UniqueValidator`'s from the validation stage to the save stage.
|
||||||
@@ -78,6 +84,12 @@ export interface Supermarket {
|
|||||||
* @memberof Supermarket
|
* @memberof Supermarket
|
||||||
*/
|
*/
|
||||||
description?: string;
|
description?: string;
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @type {Array<ShoppingList>}
|
||||||
|
* @memberof Supermarket
|
||||||
|
*/
|
||||||
|
shoppingLists?: Array<ShoppingList>;
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
* @type {Array<SupermarketCategoryRelation>}
|
* @type {Array<SupermarketCategoryRelation>}
|
||||||
@@ -114,6 +126,7 @@ export function SupermarketFromJSONTyped(json: any, ignoreDiscriminator: boolean
|
|||||||
'id': json['id'] == null ? undefined : json['id'],
|
'id': json['id'] == null ? undefined : json['id'],
|
||||||
'name': json['name'],
|
'name': json['name'],
|
||||||
'description': json['description'] == null ? undefined : json['description'],
|
'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)),
|
'categoryToSupermarket': ((json['category_to_supermarket'] as Array<any>).map(SupermarketCategoryRelationFromJSON)),
|
||||||
'openDataSlug': json['open_data_slug'] == null ? undefined : json['open_data_slug'],
|
'openDataSlug': json['open_data_slug'] == null ? undefined : json['open_data_slug'],
|
||||||
};
|
};
|
||||||
@@ -128,6 +141,7 @@ export function SupermarketToJSON(value?: Omit<Supermarket, 'categoryToSupermark
|
|||||||
'id': value['id'],
|
'id': value['id'],
|
||||||
'name': value['name'],
|
'name': value['name'],
|
||||||
'description': value['description'],
|
'description': value['description'],
|
||||||
|
'shopping_lists': value['shoppingLists'] == null ? undefined : ((value['shoppingLists'] as Array<any>).map(ShoppingListToJSON)),
|
||||||
'open_data_slug': value['openDataSlug'],
|
'open_data_slug': value['openDataSlug'],
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -200,6 +200,12 @@ export interface UserPreference {
|
|||||||
* @memberof UserPreference
|
* @memberof UserPreference
|
||||||
*/
|
*/
|
||||||
csvPrefix?: string;
|
csvPrefix?: string;
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @type {boolean}
|
||||||
|
* @memberof UserPreference
|
||||||
|
*/
|
||||||
|
shoppingUpdateFoodLists?: boolean;
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
* @type {boolean}
|
* @type {boolean}
|
||||||
@@ -276,6 +282,7 @@ export function UserPreferenceFromJSONTyped(json: any, ignoreDiscriminator: bool
|
|||||||
'shoppingRecentDays': json['shopping_recent_days'] == null ? undefined : json['shopping_recent_days'],
|
'shoppingRecentDays': json['shopping_recent_days'] == null ? undefined : json['shopping_recent_days'],
|
||||||
'csvDelim': json['csv_delim'] == null ? undefined : json['csv_delim'],
|
'csvDelim': json['csv_delim'] == null ? undefined : json['csv_delim'],
|
||||||
'csvPrefix': json['csv_prefix'] == null ? undefined : json['csv_prefix'],
|
'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'],
|
'filterToSupermarket': json['filter_to_supermarket'] == null ? undefined : json['filter_to_supermarket'],
|
||||||
'shoppingAddOnhand': json['shopping_add_onhand'] == null ? undefined : json['shopping_add_onhand'],
|
'shoppingAddOnhand': json['shopping_add_onhand'] == null ? undefined : json['shopping_add_onhand'],
|
||||||
'leftHanded': json['left_handed'] == null ? undefined : json['left_handed'],
|
'leftHanded': json['left_handed'] == null ? undefined : json['left_handed'],
|
||||||
@@ -312,6 +319,7 @@ export function UserPreferenceToJSON(value?: Omit<UserPreference, 'user'|'foodIn
|
|||||||
'shopping_recent_days': value['shoppingRecentDays'],
|
'shopping_recent_days': value['shoppingRecentDays'],
|
||||||
'csv_delim': value['csvDelim'],
|
'csv_delim': value['csvDelim'],
|
||||||
'csv_prefix': value['csvPrefix'],
|
'csv_prefix': value['csvPrefix'],
|
||||||
|
'shopping_update_food_lists': value['shoppingUpdateFoodLists'],
|
||||||
'filter_to_supermarket': value['filterToSupermarket'],
|
'filter_to_supermarket': value['filterToSupermarket'],
|
||||||
'shopping_add_onhand': value['shoppingAddOnhand'],
|
'shopping_add_onhand': value['shoppingAddOnhand'],
|
||||||
'left_handed': value['leftHanded'],
|
'left_handed': value['leftHanded'],
|
||||||
|
|||||||
@@ -29,6 +29,7 @@ export * from './FdcQueryFoods';
|
|||||||
export * from './Food';
|
export * from './Food';
|
||||||
export * from './FoodBatchUpdate';
|
export * from './FoodBatchUpdate';
|
||||||
export * from './FoodInheritField';
|
export * from './FoodInheritField';
|
||||||
|
export * from './FoodShopping';
|
||||||
export * from './FoodShoppingUpdate';
|
export * from './FoodShoppingUpdate';
|
||||||
export * from './FoodSimple';
|
export * from './FoodSimple';
|
||||||
export * from './GenericModelReference';
|
export * from './GenericModelReference';
|
||||||
@@ -92,6 +93,7 @@ export * from './PaginatedRecipeBookList';
|
|||||||
export * from './PaginatedRecipeImportList';
|
export * from './PaginatedRecipeImportList';
|
||||||
export * from './PaginatedRecipeOverviewList';
|
export * from './PaginatedRecipeOverviewList';
|
||||||
export * from './PaginatedShoppingListEntryList';
|
export * from './PaginatedShoppingListEntryList';
|
||||||
|
export * from './PaginatedShoppingListList';
|
||||||
export * from './PaginatedShoppingListRecipeList';
|
export * from './PaginatedShoppingListRecipeList';
|
||||||
export * from './PaginatedSpaceList';
|
export * from './PaginatedSpaceList';
|
||||||
export * from './PaginatedStepList';
|
export * from './PaginatedStepList';
|
||||||
@@ -138,6 +140,7 @@ export * from './PatchedRecipeBook';
|
|||||||
export * from './PatchedRecipeBookEntry';
|
export * from './PatchedRecipeBookEntry';
|
||||||
export * from './PatchedRecipeImport';
|
export * from './PatchedRecipeImport';
|
||||||
export * from './PatchedSearchPreference';
|
export * from './PatchedSearchPreference';
|
||||||
|
export * from './PatchedShoppingList';
|
||||||
export * from './PatchedShoppingListEntry';
|
export * from './PatchedShoppingListEntry';
|
||||||
export * from './PatchedShoppingListRecipe';
|
export * from './PatchedShoppingListRecipe';
|
||||||
export * from './PatchedSpace';
|
export * from './PatchedSpace';
|
||||||
@@ -172,6 +175,7 @@ export * from './SearchFields';
|
|||||||
export * from './SearchPreference';
|
export * from './SearchPreference';
|
||||||
export * from './ServerSettings';
|
export * from './ServerSettings';
|
||||||
export * from './ShareLink';
|
export * from './ShareLink';
|
||||||
|
export * from './ShoppingList';
|
||||||
export * from './ShoppingListEntry';
|
export * from './ShoppingListEntry';
|
||||||
export * from './ShoppingListEntryBulk';
|
export * from './ShoppingListEntryBulk';
|
||||||
export * from './ShoppingListEntryBulkCreate';
|
export * from './ShoppingListEntryBulkCreate';
|
||||||
|
|||||||
@@ -31,6 +31,7 @@
|
|||||||
</v-row>
|
</v-row>
|
||||||
<v-row dense>
|
<v-row dense>
|
||||||
<database-model-col model="Supermarket"></database-model-col>
|
<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="SupermarketCategory"></database-model-col>
|
||||||
<database-model-col model="MealType"></database-model-col>
|
<database-model-col model="MealType"></database-model-col>
|
||||||
</v-row>
|
</v-row>
|
||||||
|
|||||||
@@ -95,6 +95,9 @@
|
|||||||
<v-chip label v-if="item.id == useUserPreferenceStore().activeSpace.id!" color="success">{{ $t('Active') }}</v-chip>
|
<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>
|
<v-chip label v-else color="info" @click="useUserPreferenceStore().switchSpace(item)">{{ $t('Select') }}</v-chip>
|
||||||
</template>
|
</template>
|
||||||
|
<template v-slot:item.color="{ item }">
|
||||||
|
<v-chip label :color="item.color">{{ item.color }}</v-chip>
|
||||||
|
</template>
|
||||||
<template v-slot:item.action="{ item }">
|
<template v-slot:item.action="{ item }">
|
||||||
<v-btn class="float-right" icon="$menu" variant="plain">
|
<v-btn class="float-right" icon="$menu" variant="plain">
|
||||||
<v-icon icon="$menu"></v-icon>
|
<v-icon icon="$menu"></v-icon>
|
||||||
|
|||||||
@@ -606,7 +606,7 @@ function importFromUrlList() {
|
|||||||
setTimeout(importFromUrlList, 500)
|
setTimeout(importFromUrlList, 500)
|
||||||
})
|
})
|
||||||
}).catch(err => {
|
}).catch(err => {
|
||||||
|
setTimeout(importFromUrlList, 500)
|
||||||
}).finally(() => {
|
}).finally(() => {
|
||||||
loading.value = false
|
loading.value = false
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -2,7 +2,8 @@
|
|||||||
<v-container :class="{'ps-0 pe-0 pt-0': mobile}">
|
<v-container :class="{'ps-0 pe-0 pt-0': mobile}">
|
||||||
<v-defaults-provider :defaults="(useUserPreferenceStore().isPrintMode ? {VCard: {variant: 'flat'}} : {})">
|
<v-defaults-provider :defaults="(useUserPreferenceStore().isPrintMode ? {VCard: {variant: 'flat'}} : {})">
|
||||||
|
|
||||||
<recipe-view v-model="recipe"></recipe-view>
|
|
||||||
|
<recipe-view v-model="recipe" :servings="servings"></recipe-view>
|
||||||
|
|
||||||
<div class="mt-2" v-if="isShared && Object.keys(recipe).length > 0">
|
<div class="mt-2" v-if="isShared && Object.keys(recipe).length > 0">
|
||||||
<import-tandoor-dialog></import-tandoor-dialog>
|
<import-tandoor-dialog></import-tandoor-dialog>
|
||||||
@@ -35,6 +36,13 @@ const isShared = computed(() => {
|
|||||||
return params.share && typeof params.share == "string"
|
return params.share && typeof params.share == "string"
|
||||||
})
|
})
|
||||||
|
|
||||||
|
const servings = computed(() => {
|
||||||
|
const value = params.servings
|
||||||
|
if (!value) return undefined
|
||||||
|
const parsed = parseInt(value as string, 10)
|
||||||
|
return parsed > 0 ? parsed : undefined
|
||||||
|
})
|
||||||
|
|
||||||
const recipe = ref({} as Recipe)
|
const recipe = ref({} as Recipe)
|
||||||
|
|
||||||
watch(() => props.id, () => {
|
watch(() => props.id, () => {
|
||||||
|
|||||||
@@ -184,7 +184,7 @@ import RecipeCard from "@/components/display/RecipeCard.vue";
|
|||||||
import {useDisplay} from "vuetify";
|
import {useDisplay} from "vuetify";
|
||||||
import {useUserPreferenceStore} from "@/stores/UserPreferenceStore";
|
import {useUserPreferenceStore} from "@/stores/UserPreferenceStore";
|
||||||
import {useRouteQuery} from "@vueuse/router";
|
import {useRouteQuery} from "@vueuse/router";
|
||||||
import {routeQueryDateTransformer, stringToBool, toNumberArray} from "@/utils/utils";
|
import {numberOrUndefinedTransformer, routeQueryDateTransformer, stringToBool, toNumberArray} from "@/utils/utils";
|
||||||
import RandomIcon from "@/components/display/RandomIcon.vue";
|
import RandomIcon from "@/components/display/RandomIcon.vue";
|
||||||
import {VSelect, VTextField, VNumberInput} from "vuetify/components";
|
import {VSelect, VTextField, VNumberInput} from "vuetify/components";
|
||||||
import RatingField from "@/components/inputs/RatingField.vue";
|
import RatingField from "@/components/inputs/RatingField.vue";
|
||||||
@@ -759,27 +759,30 @@ const filters = ref({
|
|||||||
label: `${t('Rating')} (${t('exact')})`,
|
label: `${t('Rating')} (${t('exact')})`,
|
||||||
hint: '',
|
hint: '',
|
||||||
enabled: false,
|
enabled: false,
|
||||||
|
clearable: true,
|
||||||
default: undefined,
|
default: undefined,
|
||||||
is: RatingField,
|
is: RatingField,
|
||||||
modelValue: useRouteQuery('rating', undefined, {transform: Number}),
|
modelValue: useRouteQuery('rating', undefined, {transform: numberOrUndefinedTransformer}),
|
||||||
},
|
},
|
||||||
ratingGte: {
|
ratingGte: {
|
||||||
id: 'ratingGte',
|
id: 'ratingGte',
|
||||||
label: `${t('Rating')} (>=)`,
|
label: `${t('Rating')} (>=)`,
|
||||||
hint: '',
|
hint: '',
|
||||||
enabled: false,
|
enabled: false,
|
||||||
|
clearable: true,
|
||||||
default: undefined,
|
default: undefined,
|
||||||
is: RatingField,
|
is: RatingField,
|
||||||
modelValue: useRouteQuery('ratingGte', undefined, {transform: Number}),
|
modelValue: useRouteQuery('ratingGte', undefined, {transform: numberOrUndefinedTransformer}),
|
||||||
},
|
},
|
||||||
ratingLte: {
|
ratingLte: {
|
||||||
id: 'ratingLte',
|
id: 'ratingLte',
|
||||||
label: `${t('Rating')} (<=)`,
|
label: `${t('Rating')} (<=)`,
|
||||||
hint: '',
|
hint: '',
|
||||||
enabled: false,
|
enabled: false,
|
||||||
|
clearable: true,
|
||||||
default: undefined,
|
default: undefined,
|
||||||
is: RatingField,
|
is: RatingField,
|
||||||
modelValue: useRouteQuery('ratingLte', undefined, {transform: Number}),
|
modelValue: useRouteQuery('ratingLte', undefined, {transform: numberOrUndefinedTransformer}),
|
||||||
},
|
},
|
||||||
timescooked: {
|
timescooked: {
|
||||||
id: 'timescooked',
|
id: 'timescooked',
|
||||||
@@ -787,26 +790,29 @@ const filters = ref({
|
|||||||
hint: 'Recipes that were cooked at least X times',
|
hint: 'Recipes that were cooked at least X times',
|
||||||
enabled: false,
|
enabled: false,
|
||||||
default: undefined,
|
default: undefined,
|
||||||
|
clearable: true,
|
||||||
is: VNumberInput,
|
is: VNumberInput,
|
||||||
modelValue: useRouteQuery('timescooked', undefined, {transform: Number}),
|
modelValue: useRouteQuery('timescooked', undefined, {transform: numberOrUndefinedTransformer}),
|
||||||
},
|
},
|
||||||
timescookedGte: {
|
timescookedGte: {
|
||||||
id: 'timescookedGte',
|
id: 'timescookedGte',
|
||||||
label: `${t('times_cooked')} (>=)`,
|
label: `${t('times_cooked')} (>=)`,
|
||||||
hint: '',
|
hint: '',
|
||||||
enabled: false,
|
enabled: false,
|
||||||
|
clearable: true,
|
||||||
default: undefined,
|
default: undefined,
|
||||||
is: VNumberInput,
|
is: VNumberInput,
|
||||||
modelValue: useRouteQuery('timescookedGte', undefined, {transform: Number}),
|
modelValue: useRouteQuery('timescookedGte', undefined, {transform: numberOrUndefinedTransformer}),
|
||||||
},
|
},
|
||||||
timescookedLte: {
|
timescookedLte: {
|
||||||
id: 'timescookedLte',
|
id: 'timescookedLte',
|
||||||
label: `${t('times_cooked')} (<=)`,
|
label: `${t('times_cooked')} (<=)`,
|
||||||
hint: '',
|
hint: '',
|
||||||
enabled: false,
|
enabled: false,
|
||||||
|
clearable: true,
|
||||||
default: undefined,
|
default: undefined,
|
||||||
is: VNumberInput,
|
is: VNumberInput,
|
||||||
modelValue: useRouteQuery('timescookedLte', undefined, {transform: Number}),
|
modelValue: useRouteQuery('timescookedLte', undefined, {transform: numberOrUndefinedTransformer}),
|
||||||
},
|
},
|
||||||
makenow: {
|
makenow: {
|
||||||
id: 'makenow',
|
id: 'makenow',
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import {acceptHMRUpdate, defineStore} from "pinia"
|
import {acceptHMRUpdate, defineStore} from "pinia"
|
||||||
import {ApiApi, ApiShoppingListEntryListRequest, Food, Recipe, ShoppingListEntry, ShoppingListEntryBulk, ShoppingListRecipe, Supermarket, SupermarketCategory} from "@/openapi";
|
import {ApiApi, ApiShoppingListEntryListRequest, Food, Recipe, ShoppingListEntry, ShoppingListEntryBulk, ShoppingListRecipe, Supermarket, SupermarketCategory} from "@/openapi";
|
||||||
import {computed, ref} from "vue";
|
import {computed, ref, shallowRef, triggerRef} from "vue";
|
||||||
import {
|
import {
|
||||||
IShoppingExportEntry,
|
IShoppingExportEntry,
|
||||||
IShoppingList,
|
IShoppingList,
|
||||||
@@ -25,14 +25,6 @@ export const useShoppingStore = defineStore(_STORE_ID, () => {
|
|||||||
let supermarketCategories = ref([] as SupermarketCategory[])
|
let supermarketCategories = ref([] as SupermarketCategory[])
|
||||||
let supermarkets = ref([] as Supermarket[])
|
let supermarkets = ref([] as Supermarket[])
|
||||||
|
|
||||||
let stats = ref({
|
|
||||||
countChecked: 0,
|
|
||||||
countUnchecked: 0,
|
|
||||||
countCheckedFood: 0,
|
|
||||||
countUncheckedFood: 0,
|
|
||||||
countUncheckedDelayed: 0,
|
|
||||||
} as ShoppingListStats)
|
|
||||||
|
|
||||||
// internal
|
// internal
|
||||||
let currentlyUpdating = ref(false)
|
let currentlyUpdating = ref(false)
|
||||||
let initialized = ref(false)
|
let initialized = ref(false)
|
||||||
@@ -41,28 +33,25 @@ export const useShoppingStore = defineStore(_STORE_ID, () => {
|
|||||||
let autoSyncHasFocus = ref(true)
|
let autoSyncHasFocus = ref(true)
|
||||||
let autoSyncTimeoutId = ref(0)
|
let autoSyncTimeoutId = ref(0)
|
||||||
|
|
||||||
let undoStack = ref([] as ShoppingOperationHistoryEntry[])
|
let undoStack = shallowRef([] as ShoppingOperationHistoryEntry[])
|
||||||
let queueTimeoutId = ref(-1)
|
let queueTimeoutId = ref(-1)
|
||||||
let itemCheckSyncQueue = ref([] as IShoppingSyncQueueEntry[])
|
let itemCheckSyncQueue = shallowRef([] as IShoppingSyncQueueEntry[])
|
||||||
|
let syncQueueRunning = ref(false)
|
||||||
|
|
||||||
|
let entriesByGroup = shallowRef([] as IShoppingListCategory[])
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* build a multi-level data structure ready for display from shopping list entries
|
* build a multi-level data structure ready for display from shopping list entries
|
||||||
* group by selected grouping key
|
* group by selected grouping key
|
||||||
*/
|
*/
|
||||||
const getEntriesByGroup = computed(() => {
|
function updateEntriesStructure() {
|
||||||
stats.value = {
|
|
||||||
countChecked: 0,
|
|
||||||
countUnchecked: 0,
|
|
||||||
countCheckedFood: 0,
|
|
||||||
countUncheckedFood: 0,
|
|
||||||
countUncheckedDelayed: 0,
|
|
||||||
} as ShoppingListStats
|
|
||||||
|
|
||||||
let structure = {} as IShoppingList
|
let structure = {} as IShoppingList
|
||||||
structure.categories = new Map<string, IShoppingListCategory>
|
structure.categories = new Map<string, IShoppingListCategory>
|
||||||
|
|
||||||
if (useUserPreferenceStore().deviceSettings.shopping_selected_grouping === ShoppingGroupingOptions.CATEGORY && useUserPreferenceStore().deviceSettings.shopping_selected_supermarket != null) {
|
const deviceSettings = useUserPreferenceStore().deviceSettings
|
||||||
useUserPreferenceStore().deviceSettings.shopping_selected_supermarket.categoryToSupermarket.forEach(cTS => {
|
|
||||||
|
if (deviceSettings.shopping_selected_grouping === ShoppingGroupingOptions.CATEGORY && deviceSettings.shopping_selected_supermarket != null) {
|
||||||
|
deviceSettings.shopping_selected_supermarket.categoryToSupermarket.forEach(cTS => {
|
||||||
structure.categories.set(cTS.category.name, {'name': cTS.category.name, 'foods': new Map<number, IShoppingListFood>} as IShoppingListCategory)
|
structure.categories.set(cTS.category.name, {'name': cTS.category.name, 'foods': new Map<number, IShoppingListFood>} as IShoppingListCategory)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@@ -71,46 +60,9 @@ export const useShoppingStore = defineStore(_STORE_ID, () => {
|
|||||||
|
|
||||||
// build structure
|
// build structure
|
||||||
entries.value.forEach(shoppingListEntry => {
|
entries.value.forEach(shoppingListEntry => {
|
||||||
structure = updateEntryInStructure(structure, shoppingListEntry)
|
if (isEntryVisible(shoppingListEntry, deviceSettings)) {
|
||||||
})
|
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
|
// ordering
|
||||||
@@ -121,10 +73,27 @@ export const useShoppingStore = defineStore(_STORE_ID, () => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
structure.categories.forEach(category => {
|
structure.categories.forEach(category => {
|
||||||
orderedStructure.push(category)
|
if (category.foods.size > 0) {
|
||||||
|
orderedStructure.push(category)
|
||||||
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
return orderedStructure
|
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
|
||||||
})
|
})
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -135,7 +104,7 @@ export const useShoppingStore = defineStore(_STORE_ID, () => {
|
|||||||
function getFlatEntries() {
|
function getFlatEntries() {
|
||||||
let items: IShoppingExportEntry[] = []
|
let items: IShoppingExportEntry[] = []
|
||||||
|
|
||||||
getEntriesByGroup.value.forEach(shoppingListEntry => {
|
entriesByGroup.value.forEach(shoppingListEntry => {
|
||||||
shoppingListEntry.foods.forEach(food => {
|
shoppingListEntry.foods.forEach(food => {
|
||||||
food.entries.forEach(entry => {
|
food.entries.forEach(entry => {
|
||||||
items.push({
|
items.push({
|
||||||
@@ -175,7 +144,7 @@ export const useShoppingStore = defineStore(_STORE_ID, () => {
|
|||||||
function hasFailedItems() {
|
function hasFailedItems() {
|
||||||
for (let i in itemCheckSyncQueue.value) {
|
for (let i in itemCheckSyncQueue.value) {
|
||||||
if (itemCheckSyncQueue.value[i]['status'] === 'syncing_failed_before' || itemCheckSyncQueue.value[i]['status'] === 'waiting_failed_before') {
|
if (itemCheckSyncQueue.value[i]['status'] === 'syncing_failed_before' || itemCheckSyncQueue.value[i]['status'] === 'waiting_failed_before') {
|
||||||
return true
|
return !syncQueueRunning.value
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return false
|
return false
|
||||||
@@ -197,6 +166,7 @@ export const useShoppingStore = defineStore(_STORE_ID, () => {
|
|||||||
} else {
|
} else {
|
||||||
// only clear local entries when not given a meal plan to not accidentally filter the shopping list
|
// only clear local entries when not given a meal plan to not accidentally filter the shopping list
|
||||||
entries.value = new Map<number, ShoppingListEntry>
|
entries.value = new Map<number, ShoppingListEntry>
|
||||||
|
initialized.value = false
|
||||||
}
|
}
|
||||||
|
|
||||||
recLoadShoppingListEntries(requestParameters)
|
recLoadShoppingListEntries(requestParameters)
|
||||||
@@ -221,17 +191,30 @@ export const useShoppingStore = defineStore(_STORE_ID, () => {
|
|||||||
*/
|
*/
|
||||||
function recLoadShoppingListEntries(requestParameters: ApiShoppingListEntryListRequest) {
|
function recLoadShoppingListEntries(requestParameters: ApiShoppingListEntryListRequest) {
|
||||||
let api = new ApiApi()
|
let api = new ApiApi()
|
||||||
api.apiShoppingListEntryList(requestParameters).then((r) => {
|
return api.apiShoppingListEntryList(requestParameters).then((r) => {
|
||||||
|
let promises = [] as Promise<any>[]
|
||||||
|
let newMap = new Map<number, ShoppingListEntry>()
|
||||||
r.results.forEach((e) => {
|
r.results.forEach((e) => {
|
||||||
entries.value.set(e.id!, e)
|
newMap.set(e.id!, e)
|
||||||
})
|
})
|
||||||
if (r.next) {
|
// bulk assign to avoid unnecessary reactivity updates
|
||||||
requestParameters.page = requestParameters.page + 1
|
entries.value = new Map([...entries.value, ...newMap])
|
||||||
recLoadShoppingListEntries(requestParameters)
|
|
||||||
} else {
|
if (requestParameters.page == 1) {
|
||||||
currentlyUpdating.value = false
|
if (r.next) {
|
||||||
initialized.value = true
|
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
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
}).catch((err) => {
|
}).catch((err) => {
|
||||||
currentlyUpdating.value = false
|
currentlyUpdating.value = false
|
||||||
useMessageStore().addError(ErrorMessageType.FETCH_ERROR, err)
|
useMessageStore().addError(ErrorMessageType.FETCH_ERROR, err)
|
||||||
@@ -251,6 +234,9 @@ export const useShoppingStore = defineStore(_STORE_ID, () => {
|
|||||||
r.results.forEach((e) => {
|
r.results.forEach((e) => {
|
||||||
entries.value.set(e.id!, e)
|
entries.value.set(e.id!, e)
|
||||||
})
|
})
|
||||||
|
if(r.results.length > 0){
|
||||||
|
updateEntriesStructure()
|
||||||
|
}
|
||||||
currentlyUpdating.value = false
|
currentlyUpdating.value = false
|
||||||
}).catch((err: any) => {
|
}).catch((err: any) => {
|
||||||
currentlyUpdating.value = false
|
currentlyUpdating.value = false
|
||||||
@@ -267,6 +253,7 @@ export const useShoppingStore = defineStore(_STORE_ID, () => {
|
|||||||
const api = new ApiApi()
|
const api = new ApiApi()
|
||||||
return api.apiShoppingListEntryCreate({shoppingListEntry: object}).then((r) => {
|
return api.apiShoppingListEntryCreate({shoppingListEntry: object}).then((r) => {
|
||||||
entries.value.set(r.id!, r)
|
entries.value.set(r.id!, r)
|
||||||
|
updateEntriesStructure()
|
||||||
if (undo) {
|
if (undo) {
|
||||||
registerChange("CREATE", [r])
|
registerChange("CREATE", [r])
|
||||||
}
|
}
|
||||||
@@ -299,6 +286,7 @@ export const useShoppingStore = defineStore(_STORE_ID, () => {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* delete shopping list entry object from DB and store
|
* delete shopping list entry object from DB and store
|
||||||
* @param object entry object to delete
|
* @param object entry object to delete
|
||||||
@@ -308,6 +296,19 @@ export const useShoppingStore = defineStore(_STORE_ID, () => {
|
|||||||
const api = new ApiApi()
|
const api = new ApiApi()
|
||||||
return api.apiShoppingListEntryDestroy({id: object.id!}).then((r) => {
|
return api.apiShoppingListEntryDestroy({id: object.id!}).then((r) => {
|
||||||
entries.value.delete(object.id!)
|
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) {
|
if (undo) {
|
||||||
registerChange("DESTROY", [object])
|
registerChange("DESTROY", [object])
|
||||||
}
|
}
|
||||||
@@ -331,7 +332,32 @@ export const useShoppingStore = defineStore(_STORE_ID, () => {
|
|||||||
return recipes
|
return recipes
|
||||||
}
|
}
|
||||||
|
|
||||||
// convenience methods
|
|
||||||
|
/**
|
||||||
|
* 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
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* puts an entry into the appropriate group of the IShoppingList datastructure
|
* 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
|
* if a group does not yet exist and the sorting is not set to category with selected supermarket only, it will be created
|
||||||
@@ -339,23 +365,8 @@ export const useShoppingStore = defineStore(_STORE_ID, () => {
|
|||||||
* @param entry
|
* @param entry
|
||||||
*/
|
*/
|
||||||
function updateEntryInStructure(structure: IShoppingList, entry: ShoppingListEntry) {
|
function updateEntryInStructure(structure: IShoppingList, entry: ShoppingListEntry) {
|
||||||
let groupingKey = UNDEFINED_CATEGORY
|
|
||||||
|
|
||||||
let group = useUserPreferenceStore().deviceSettings.shopping_selected_grouping
|
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)) {
|
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)
|
structure.categories.set(groupingKey, {'name': groupingKey, 'foods': new Map<number, IShoppingListFood>} as IShoppingListCategory)
|
||||||
@@ -383,19 +394,18 @@ export const useShoppingStore = defineStore(_STORE_ID, () => {
|
|||||||
if (undo) {
|
if (undo) {
|
||||||
registerChange((checked ? 'CHECKED' : 'UNCHECKED'), entries)
|
registerChange((checked ? 'CHECKED' : 'UNCHECKED'), entries)
|
||||||
}
|
}
|
||||||
|
|
||||||
let entryIdList: number[] = []
|
let entryIdList: number[] = []
|
||||||
entries.forEach(entry => {
|
entries.forEach(entry => {
|
||||||
entry.checked = checked
|
entry.checked = checked
|
||||||
entryIdList.push(entry.id!)
|
entryIdList.push(entry.id!)
|
||||||
})
|
})
|
||||||
|
|
||||||
itemCheckSyncQueue.value.push({
|
itemCheckSyncQueue.value.push({
|
||||||
ids: entryIdList,
|
ids: entryIdList,
|
||||||
checked: checked,
|
checked: checked,
|
||||||
status: 'waiting',
|
status: 'waiting',
|
||||||
} as IShoppingSyncQueueEntry)
|
} as IShoppingSyncQueueEntry)
|
||||||
runSyncQueue(5)
|
|
||||||
|
runSyncQueue(100)
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -409,15 +419,17 @@ export const useShoppingStore = defineStore(_STORE_ID, () => {
|
|||||||
let api = new ApiApi()
|
let api = new ApiApi()
|
||||||
let promises: Promise<void>[] = []
|
let promises: Promise<void>[] = []
|
||||||
|
|
||||||
|
let updatedEntries = new Map<number, ShoppingListEntry>()
|
||||||
itemCheckSyncQueue.value.forEach((entry, index) => {
|
itemCheckSyncQueue.value.forEach((entry, index) => {
|
||||||
entry['status'] = ((entry['status'] === 'waiting') ? 'syncing' : 'syncing_failed_before')
|
entry['status'] = ((entry['status'] === 'waiting_failed_before') ? 'syncing_failed_before' : 'syncing')
|
||||||
|
syncQueueRunning.value = true
|
||||||
let p = api.apiShoppingListEntryBulkCreate({shoppingListEntryBulk: entry}, {}).then((r) => {
|
let p = api.apiShoppingListEntryBulkCreate({shoppingListEntryBulk: entry}, {}).then((r) => {
|
||||||
entry.ids.forEach(id => {
|
entry.ids.forEach(id => {
|
||||||
let e = entries.value.get(id)
|
let e = entries.value.get(id)
|
||||||
e.updatedAt = r.timestamp
|
if (e) {
|
||||||
e.checked = r.checked
|
e.updatedAt = r.timestamp
|
||||||
entries.value.set(id, e)
|
updatedEntries.set(id, e)
|
||||||
|
}
|
||||||
})
|
})
|
||||||
itemCheckSyncQueue.value.splice(index, 1)
|
itemCheckSyncQueue.value.splice(index, 1)
|
||||||
}).catch((err) => {
|
}).catch((err) => {
|
||||||
@@ -432,6 +444,10 @@ export const useShoppingStore = defineStore(_STORE_ID, () => {
|
|||||||
})
|
})
|
||||||
|
|
||||||
Promise.allSettled(promises).finally(() => {
|
Promise.allSettled(promises).finally(() => {
|
||||||
|
entries.value = new Map([...entries.value, ...updatedEntries])
|
||||||
|
syncQueueRunning.value = false
|
||||||
|
//TODO proper function to splice/update structure as needed
|
||||||
|
useShoppingStore().updateEntriesStructure()
|
||||||
if (itemCheckSyncQueue.value.length > 0) {
|
if (itemCheckSyncQueue.value.length > 0) {
|
||||||
runSyncQueue(500)
|
runSyncQueue(500)
|
||||||
}
|
}
|
||||||
@@ -579,7 +595,8 @@ export const useShoppingStore = defineStore(_STORE_ID, () => {
|
|||||||
entries,
|
entries,
|
||||||
supermarkets,
|
supermarkets,
|
||||||
supermarketCategories,
|
supermarketCategories,
|
||||||
getEntriesByGroup,
|
updateEntriesStructure,
|
||||||
|
entriesByGroup,
|
||||||
autoSyncTimeoutId,
|
autoSyncTimeoutId,
|
||||||
autoSyncHasFocus,
|
autoSyncHasFocus,
|
||||||
autoSyncLastTimestamp,
|
autoSyncLastTimestamp,
|
||||||
@@ -589,7 +606,7 @@ export const useShoppingStore = defineStore(_STORE_ID, () => {
|
|||||||
hasFailedItems,
|
hasFailedItems,
|
||||||
itemCheckSyncQueue,
|
itemCheckSyncQueue,
|
||||||
undoStack,
|
undoStack,
|
||||||
stats,
|
totalFoods,
|
||||||
refreshFromAPI,
|
refreshFromAPI,
|
||||||
autoSync,
|
autoSync,
|
||||||
createObject,
|
createObject,
|
||||||
|
|||||||
@@ -228,6 +228,7 @@ export const useUserPreferenceStore = defineStore('user_preference_store', () =>
|
|||||||
shopping_show_selected_supermarket_only: false,
|
shopping_show_selected_supermarket_only: false,
|
||||||
shopping_selected_grouping: ShoppingGroupingOptions.CATEGORY,
|
shopping_selected_grouping: ShoppingGroupingOptions.CATEGORY,
|
||||||
shopping_selected_supermarket: null,
|
shopping_selected_supermarket: null,
|
||||||
|
shopping_selected_shopping_list: [],
|
||||||
shopping_item_info_created_by: false,
|
shopping_item_info_created_by: false,
|
||||||
shopping_item_info_mealplan: true,
|
shopping_item_info_mealplan: true,
|
||||||
shopping_item_info_recipe: true,
|
shopping_item_info_recipe: true,
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ import {
|
|||||||
MealPlan,
|
MealPlan,
|
||||||
MealType,
|
MealType,
|
||||||
Property, PropertyType,
|
Property, PropertyType,
|
||||||
Recipe, RecipeBook, RecipeBookEntry, RecipeImport, SearchFields, ShoppingListEntry, Space,
|
Recipe, RecipeBook, RecipeBookEntry, RecipeImport, SearchFields, ShoppingList, ShoppingListEntry, Space,
|
||||||
Step,
|
Step,
|
||||||
Supermarket,
|
Supermarket,
|
||||||
SupermarketCategory, Sync, SyncLog,
|
SupermarketCategory, Sync, SyncLog,
|
||||||
@@ -144,6 +144,7 @@ export type EditorSupportedModels =
|
|||||||
| 'Automation'
|
| 'Automation'
|
||||||
| 'Keyword'
|
| 'Keyword'
|
||||||
| 'UserFile'
|
| 'UserFile'
|
||||||
|
| 'ShoppingList'
|
||||||
| 'ShoppingListEntry'
|
| 'ShoppingListEntry'
|
||||||
| 'User'
|
| 'User'
|
||||||
| 'RecipeBook'
|
| 'RecipeBook'
|
||||||
@@ -182,6 +183,7 @@ export type EditorSupportedTypes =
|
|||||||
| Automation
|
| Automation
|
||||||
| Keyword
|
| Keyword
|
||||||
| UserFile
|
| UserFile
|
||||||
|
| ShoppingList
|
||||||
| ShoppingListEntry
|
| ShoppingListEntry
|
||||||
| User
|
| User
|
||||||
| RecipeBook
|
| RecipeBook
|
||||||
@@ -484,6 +486,28 @@ export const TSupermarketCategory = {
|
|||||||
} as Model
|
} as Model
|
||||||
registerModel(TSupermarketCategory)
|
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 = {
|
export const TShoppingListEntry = {
|
||||||
name: 'ShoppingListEntry',
|
name: 'ShoppingListEntry',
|
||||||
localizationKey: 'ShoppingListEntry',
|
localizationKey: 'ShoppingListEntry',
|
||||||
|
|||||||
@@ -23,7 +23,6 @@ export interface IShoppingList {
|
|||||||
export interface IShoppingListCategory {
|
export interface IShoppingListCategory {
|
||||||
name: string,
|
name: string,
|
||||||
foods: Map<number, IShoppingListFood>,
|
foods: Map<number, IShoppingListFood>,
|
||||||
stats: ShoppingListStats,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import {Supermarket} from "@/openapi";
|
import {ShoppingList, Supermarket} from "@/openapi";
|
||||||
|
|
||||||
export type DeviceSettings = {
|
export type DeviceSettings = {
|
||||||
shopping_show_checked_entries: boolean
|
shopping_show_checked_entries: boolean
|
||||||
@@ -6,6 +6,7 @@ export type DeviceSettings = {
|
|||||||
shopping_show_selected_supermarket_only: boolean
|
shopping_show_selected_supermarket_only: boolean
|
||||||
shopping_selected_grouping: string
|
shopping_selected_grouping: string
|
||||||
shopping_selected_supermarket: Supermarket | null
|
shopping_selected_supermarket: Supermarket | null
|
||||||
|
shopping_selected_shopping_list: number[]
|
||||||
shopping_item_info_created_by: boolean
|
shopping_item_info_created_by: boolean
|
||||||
shopping_item_info_mealplan: boolean
|
shopping_item_info_mealplan: boolean
|
||||||
shopping_item_info_recipe: boolean
|
shopping_item_info_recipe: boolean
|
||||||
|
|||||||
@@ -18,6 +18,15 @@ export function isEntryVisible(entry: ShoppingListEntry, deviceSettings: DeviceS
|
|||||||
if (entry.checked && !deviceSettings.shopping_show_checked_entries) {
|
if (entry.checked && !deviceSettings.shopping_show_checked_entries) {
|
||||||
entryVisible = false
|
entryVisible = false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// if no list is selected show all entries
|
||||||
|
// if -1 is selected show entries without shopping lists
|
||||||
|
// otherwise check if at least one of the entries lists is selected
|
||||||
|
if(deviceSettings.shopping_selected_shopping_list.length > 0){
|
||||||
|
if(!(deviceSettings.shopping_selected_shopping_list.includes(-1) && entry.shoppingLists?.length == 0) && !deviceSettings.shopping_selected_shopping_list.some(sl => (entry.shoppingLists?.findIndex(eSl => eSl.id == sl) != -1))){
|
||||||
|
entryVisible = false
|
||||||
|
}
|
||||||
|
}
|
||||||
return entryVisible
|
return entryVisible
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -60,16 +69,15 @@ export function isShoppingListFoodDelayed(slf: IShoppingListFood) {
|
|||||||
* @param category
|
* @param category
|
||||||
*/
|
*/
|
||||||
export function isShoppingCategoryVisible(category: IShoppingListCategory) {
|
export function isShoppingCategoryVisible(category: IShoppingListCategory) {
|
||||||
let entryCount = category.stats.countUnchecked
|
console.log('checking if category is visible')
|
||||||
|
let categoryVisible = false
|
||||||
|
category.foods.forEach(food => {
|
||||||
|
if(isShoppingListFoodVisible(food, useUserPreferenceStore().deviceSettings)){
|
||||||
|
categoryVisible = true
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
if (useUserPreferenceStore().deviceSettings.shopping_show_checked_entries) {
|
return categoryVisible
|
||||||
entryCount += category.stats.countChecked
|
|
||||||
}
|
|
||||||
if (useUserPreferenceStore().deviceSettings.shopping_show_delayed_entries) {
|
|
||||||
entryCount += category.stats.countUncheckedDelayed
|
|
||||||
}
|
|
||||||
|
|
||||||
return entryCount > 0
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// -------------- SPACE RELATED ----------------------
|
// -------------- SPACE RELATED ----------------------
|
||||||
|
|||||||
@@ -79,4 +79,20 @@ export function stringToBool(param: string): boolean | undefined {
|
|||||||
export const routeQueryDateTransformer = {
|
export const routeQueryDateTransformer = {
|
||||||
get: (value: string | null | Date) => ((value == null) ? null : (new Date(value))),
|
get: (value: string | null | Date) => ((value == null) ? null : (new Date(value))),
|
||||||
set: (value: string | null | Date) => ((value == null) ? null : (DateTime.fromJSDate(new Date(value)).toISODate()))
|
set: (value: string | null | Date) => ((value == null) ? null : (DateTime.fromJSDate(new Date(value)).toISODate()))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* routeQueryParam transformer for boolean fields converting string bools to real bools
|
||||||
|
*/
|
||||||
|
export const boolOrUndefinedTransformer = {
|
||||||
|
get: (value: string | null | undefined) => ((value == null) ? undefined : value == 'true'),
|
||||||
|
set: (value: boolean | null | undefined) => ((value == null) ? undefined : value.toString())
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* routeQueryParam transformer for number fields converting string numbers to real numbers and allowing undefined for resettable parameters
|
||||||
|
*/
|
||||||
|
export const numberOrUndefinedTransformer = {
|
||||||
|
get: (value: string | null | undefined) => ((value == null) ? undefined : Number(value)),
|
||||||
|
set: (value: string | null | undefined) => ((value == null) ? undefined : value.toString())
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user