Compare commits

..

53 Commits
2.0.2 ... 2.1.2

Author SHA1 Message Date
vabene1111
163c2a53b6 fixed space overview 2025-09-09 07:55:31 +02:00
vabene1111
aba45657c3 fixed vite config 2025-09-08 08:38:53 +02:00
vabene1111
6cedde7b2d plugin and hosted fixes
# Conflicts:
#	vue3/src/locales/de.json
2025-09-08 08:33:05 +02:00
vabene1111
44baa8322c Merge branch 'develop' 2025-09-04 22:24:18 +02:00
vabene1111
0fbb95438a added auto meal planner back 2025-09-04 22:23:50 +02:00
vabene1111
c56dd9563c fixed accidentally closing meal plan dialog when opened from recipe context menu 2025-09-04 21:41:51 +02:00
vabene1111
0008b7c975 fixed servings scaler missing on mobile 2025-09-04 21:38:21 +02:00
vabene1111
524f086cc5 added merged steps overview 2025-09-04 21:35:28 +02:00
vabene1111
8550387e0c added ability to delete external recipe file 2025-09-04 21:09:34 +02:00
vabene1111
1618f8df79 fixed meal plan data loading 2025-09-04 20:51:54 +02:00
vabene1111
22dfb2a410 Merge pull request #3998 from Valinor/WEBP-Support
Support WEBP format in image processing #3997
2025-09-04 20:49:21 +02:00
Valinor
6973c65142 Support WEBP format in image processing
Add support for WEBP file format in image processing.
2025-09-01 15:45:26 +02:00
vabene1111
a01f86a14e migrated comments, improved recipe activity, added editor 2025-08-31 12:32:12 +02:00
vabene1111
9704268fdc added proper query binding to ModelListPager 2025-08-31 09:42:27 +02:00
vabene1111
84cc4c1165 food create serializer case insensitive 2025-08-31 09:23:27 +02:00
vabene1111
5cb70becb8 ingredient parser case insenstiive 2025-08-31 09:23:18 +02:00
vabene1111
5f99abf459 food and unit plurals in shopping 2025-08-30 11:20:02 +02:00
vabene1111
4a8ddce391 added fuzzy filtering to UnitConversionApi 2025-08-30 11:08:16 +02:00
vabene1111
9a14a87c27 import log view improvement 2025-08-30 08:39:31 +02:00
vabene1111
c01634f9bd remove search links from unauthenticated recipe view 2025-08-30 08:31:15 +02:00
vabene1111
f055df3b4d fixed original text for pasted ingredients 2025-08-30 08:29:07 +02:00
vabene1111
a83f474d70 note 2025-08-30 08:08:54 +02:00
vabene1111
63d358df36 indicate private reciesp 2025-08-29 13:08:16 +02:00
vabene1111
e70548fcc0 added split/merge steps to recipe view 2025-08-28 18:20:26 +02:00
vabene1111
17b03905e6 half increment rating display 2025-08-28 17:47:43 +02:00
vabene1111
90403e6a13 Merge pull request #3960 from dertasiu/develop
Allow video file types to be uploaded
2025-08-28 17:45:40 +02:00
vabene1111
db400cae25 Merge pull request #3956 from c0mputerguru/devcontainer-updatevue3
Update devcontainer to work with new vue3 UI.
2025-08-28 17:45:03 +02:00
vabene1111
0f8eee4e0f Merge branch 'develop' of https://github.com/vabene1111/recipes into develop 2025-08-28 07:55:55 +02:00
vabene1111
1f532f6276 quick fix 2025-08-28 07:55:47 +02:00
c0mputerguru
b32715e493 Update documentation for vscode devcontainer about either starting vite or collecting static files prior to starting django. 2025-08-23 16:50:57 +00:00
c0mputerguru
0d19e12118 Remove dependencies from devcontainer tasks and have django run in debug mode. 2025-08-23 16:41:36 +00:00
dertasiu
96e5213fa6 Allow video files to be uploaded 2025-08-23 11:02:03 +02:00
vabene1111
44c567d20b Merge branch 'develop' 2025-08-23 09:07:19 +02:00
vabene1111
3c920593cf hide AI import when disabeld 2025-08-23 09:07:12 +02:00
vabene1111
1d90f8b6f1 updated docs and fixed links 2025-08-23 08:49:10 +02:00
vabene1111
6b1217ec35 first draft of a hirarchy editor 2025-08-22 16:58:12 +02:00
c0mputerguru
a71564a424 Update devcontainer to work with new vue3 UI.
Fixes #3925
2025-08-21 22:41:13 +00:00
vabene1111
76c2e144fc fix windows issue 2025-08-21 18:20:00 +02:00
vabene1111
981353380c plugin python script 2025-08-21 18:12:45 +02:00
vabene1111
96a520b1af install JS dependencies 2025-08-21 17:48:14 +02:00
vabene1111
05f537dc6b shouldn't be necessary but apperently is 2025-08-21 17:43:42 +02:00
vabene1111
948d8da3b1 fixed wrong import 2025-08-21 17:36:24 +02:00
vabene1111
f8e4b39d88 testing .. 2025-08-21 17:12:51 +02:00
vabene1111
6c498f7dac playing with plugin building 2025-08-21 17:10:13 +02:00
vabene1111
d25702b717 added recipe batch editing dialog 2025-08-21 15:50:13 +02:00
vabene1111
aca18fcbe0 moved open data plugin to its own repo again 2025-08-21 15:50:01 +02:00
vabene1111
98b57d2854 foundations of recipe batch editing 2025-08-20 22:55:38 +02:00
vabene1111
5e1c804fd1 batch merge view 2025-08-20 22:20:23 +02:00
vabene1111
a30deb4bae added batch delete dialog 2025-08-20 21:47:43 +02:00
vabene1111
45a567856a added badge to serach page 2025-08-20 20:49:36 +02:00
vabene1111
7065d96f90 links in recipe created at/on and updated 2025-08-20 20:36:57 +02:00
vabene1111
f8cd42dec9 table select box 2025-08-20 17:31:04 +02:00
vabene1111
8d736c0f88 some small tweaks 2025-08-20 17:00:11 +02:00
121 changed files with 5496 additions and 1316 deletions

View File

@@ -1,12 +1,7 @@
FROM python:3.10-alpine3.18
FROM python:3.13-alpine3.22
#Install all dependencies.
RUN apk add --no-cache postgresql-libs postgresql-client gettext zlib libjpeg libwebp libxml2-dev libxslt-dev openldap git yarn
# Fix libxml error from xmlsec https://github.com/xmlsec/python-xmlsec/issues/257#issuecomment-1738620862
RUN echo "https://dl-cdn.alpinelinux.org/alpine/v3.15/community/" | tee -a /etc/apk/repositories
RUN echo "https://dl-cdn.alpinelinux.org/alpine/v3.15/main" | tee -a /etc/apk/repositories
RUN apk add --no-cache libxml2-dev=2.9.14-r2 xmlsec-dev=1.2.33-r0
RUN apk add --no-cache postgresql-libs postgresql-client gettext zlib libjpeg libwebp libxml2-dev libxslt-dev openldap git yarn libgcc libstdc++ nginx tini envsubst nodejs npm
#Print all logs without buffering it.
ENV PYTHONUNBUFFERED 1
@@ -24,8 +19,10 @@ RUN \
if [ `apk --print-arch` = "armv7" ]; then \
printf "[global]\nextra-index-url=https://www.piwheels.org/simple\n" > /etc/pip.conf ; \
fi
RUN apk add --no-cache --virtual .build-deps gcc musl-dev postgresql-dev zlib-dev jpeg-dev libwebp-dev openssl-dev libffi-dev cargo openldap-dev python3-dev && \
echo -n "INPUT ( libldap.so )" > /usr/lib/libldap_r.so && \
pip3 --disable-pip-version-check --no-cache-dir install -r /tmp/pip-tmp/requirements.txt && \
rm -rf /tmp/pip-tmp && \
RUN apk add --no-cache --virtual .build-deps gcc musl-dev postgresql-dev zlib-dev jpeg-dev libwebp-dev openssl-dev libffi-dev cargo openldap-dev python3-dev xmlsec-dev xmlsec build-base g++ curl rust && \
python -m pip install --upgrade pip && \
pip debug -v && \
pip install wheel==0.45.1 && \
pip install setuptools_rust==1.10.2 && \
pip install -r /tmp/pip-tmp/requirements.txt --no-cache-dir &&\
apk --purge del .build-deps

1
.gitignore vendored
View File

@@ -91,3 +91,4 @@ cookbook/static/vue3
vue3/node_modules
cookbook/tests/other/docs/reports/tests/tests.html
cookbook/tests/other/docs/reports/tests/pytest.xml
vue3/src/plugins

62
.vscode/tasks.json vendored
View File

@@ -14,28 +14,16 @@
},
{
"label": "Setup Dev Server",
"dependsOn": ["Run Migrations", "Yarn Build"]
"dependsOn": ["Run Migrations"]
},
{
"label": "Run Dev Server",
"type": "shell",
"type": "shell",
"dependsOn": ["Setup Dev Server"],
"command": "python3 manage.py runserver"
"command": "DEBUG=1 python3 manage.py runserver"
},
{
"label": "Yarn Install",
"dependsOn": ["Yarn Install - Vue", "Yarn Install - Vue3"]
},
{
"label": "Yarn Install - Vue",
"type": "shell",
"command": "yarn install --force",
"options": {
"cwd": "${workspaceFolder}/vue"
}
},
{
"label": "Yarn Install - Vue3",
"type": "shell",
"command": "yarn install --force",
"options": {
@@ -44,18 +32,6 @@
},
{
"label": "Generate API",
"dependsOn": ["Generate API - Vue", "Generate API - Vue3"]
},
{
"label": "Generate API - Vue",
"type": "shell",
"command": "openapi-generator-cli generate -g typescript-axios -i http://127.0.0.1:8000/openapi/",
"options": {
"cwd": "${workspaceFolder}/vue/src/utils/openapi"
}
},
{
"label": "Generate API - Vue3",
"type": "shell",
"command": "openapi-generator-cli generate -g typescript-fetch -i http://127.0.0.1:8000/openapi/",
"options": {
@@ -63,43 +39,19 @@
}
},
{
"label": "Yarn Serve",
"label": "Yarn Dev",
"type": "shell",
"command": "yarn serve",
"dependsOn": ["Yarn Install - Vue"],
"options": {
"cwd": "${workspaceFolder}/vue"
}
},
{
"label": "Vite Serve",
"type": "shell",
"command": "vite",
"dependsOn": ["Yarn Install - Vue3"],
"command": "yarn dev",
"dependsOn": ["Yarn Install"],
"options": {
"cwd": "${workspaceFolder}/vue3"
}
},
{
"label": "Yarn Build",
"dependsOn": ["Yarn Build - Vue", "Vite Build - Vue3"],
"group": "build"
},
{
"label": "Yarn Build - Vue",
"type": "shell",
"command": "yarn build",
"dependsOn": ["Yarn Install - Vue"],
"options": {
"cwd": "${workspaceFolder}/vue"
},
"group": "build"
},
{
"label": "Vite Build - Vue3",
"type": "shell",
"command": "vite build",
"dependsOn": ["Yarn Install - Vue3"],
"dependsOn": ["Yarn Install"],
"options": {
"cwd": "${workspaceFolder}/vue3"
},

View File

@@ -1,7 +1,7 @@
FROM python:3.13-alpine3.22
#Install all dependencies.
RUN apk add --no-cache postgresql-libs postgresql-client gettext zlib libjpeg libwebp libxml2-dev libxslt-dev openldap git libgcc libstdc++ nginx tini envsubst
RUN apk add --no-cache postgresql-libs postgresql-client gettext zlib libjpeg libwebp libxml2-dev libxslt-dev openldap git libgcc libstdc++ nginx tini envsubst nodejs npm
#Print all logs without buffering it.
ENV PYTHONUNBUFFERED=1 \

View File

@@ -10,6 +10,8 @@ GUNICORN_WORKERS="${GUNICORN_WORKERS:-3}"
GUNICORN_THREADS="${GUNICORN_THREADS:-2}"
GUNICORN_LOG_LEVEL="${GUNICORN_LOG_LEVEL:-'info'}"
PLUGINS_BUILD="${PLUGINS_BUILD:-0}"
if [ "${TANDOOR_PORT}" -eq 80 ]; then
echo "TANDOOR_PORT set to 8080 because 80 is now taken by the integrated nginx"
TANDOOR_PORT=8080
@@ -82,9 +84,13 @@ echo "Database is ready"
echo "Migrating database"
python manage.py migrate
if [ "${PLUGINS_BUILD}" -eq 1 ]; then
echo "Running yarn build at startup because PLUGINS_BUILD is enabled"
python plugin.py
fi
echo "Collecting static files, this may take a while..."
python manage.py collectstatic --noinput

View File

@@ -37,7 +37,7 @@ def get_filetype(name):
def is_file_type_allowed(filename, image_only=False):
is_file_allowed = False
allowed_file_types = ['.pdf', '.docx', '.xlsx', '.css']
allowed_file_types = ['.pdf', '.docx', '.xlsx', '.css', '.mp4', '.mov']
allowed_image_types = ['.png', '.jpg', '.jpeg', '.gif', '.webp']
check_list = allowed_image_types
if not image_only:
@@ -77,6 +77,8 @@ def handle_image(request, image_object, filetype):
file_format = 'JPEG'
if filetype == '.png':
file_format = 'PNG'
if filetype == '.webp':
file_format = 'WEBP'
if (image_object.size / 1000) > 500: # if larger than 500 kb compress
if filetype == '.jpeg' or filetype == '.jpg':

View File

@@ -0,0 +1,34 @@
# Generated by Django 4.2.22 on 2025-08-31 09:11
from django.db import migrations
from django_scopes import scopes_disabled
def migrate_comments(apps, schema_editor):
with scopes_disabled():
Comment = apps.get_model('cookbook', 'Comment')
CookLog = apps.get_model('cookbook', 'CookLog')
cook_logs = []
for c in Comment.objects.all():
cook_logs.append(CookLog(
recipe=c.recipe,
created_by=c.created_by,
created_at=c.created_at,
comment=c.text,
space=c.recipe.space,
))
CookLog.objects.bulk_create(cook_logs, unique_fields=('recipe', 'comment', 'created_at', 'created_by'))
class Migration(migrations.Migration):
dependencies = [
('cookbook', '0222_alter_shoppinglistrecipe_created_by_and_more'),
]
operations = [
migrations.RunPython(migrate_comments),
]

View File

@@ -592,7 +592,7 @@ class KeywordSerializer(UniqueFieldsMixin, ExtendedRecipeMixin):
fields = (
'id', 'name', 'label', 'description', 'image', 'parent', 'numchild', 'numrecipe', 'created_at',
'updated_at', 'full_name')
read_only_fields = ('id', 'label', 'numchild', 'parent', 'image')
read_only_fields = ('id', 'label', 'numchild', 'numrecipe', 'parent', 'image')
class UnitSerializer(UniqueFieldsMixin, ExtendedRecipeMixin, OpenDataModelMixin):
@@ -787,7 +787,7 @@ class FoodSerializer(UniqueFieldsMixin, WritableNestedModelSerializer, ExtendedR
if plural_name := validated_data.pop('plural_name', None):
plural_name = plural_name.strip()
if food := Food.objects.filter(Q(name=name) | Q(plural_name=name)).first():
if food := Food.objects.filter(Q(name__iexact=name) | Q(plural_name__iexact=name)).first():
return food
space = validated_data.pop('space', self.context['request'].space)
@@ -1038,7 +1038,7 @@ class RecipeOverviewSerializer(RecipeBaseSerializer):
fields = (
'id', 'name', 'description', 'image', 'keywords', 'working_time',
'waiting_time', 'created_by', 'created_at', 'updated_at',
'internal', 'servings', 'servings_text', 'rating', 'last_cooked', 'new', 'recent'
'internal', 'private','servings', 'servings_text', 'rating', 'last_cooked', 'new', 'recent'
)
# TODO having these readonly fields makes "RecipeOverview.ts" (API Client) not generate the RecipeOverviewToJSON second else block which leads to errors when using the api
# TODO find a solution (custom schema?) to have these fields readonly (to save performance) and generate a proper client (two serializers would probably do the trick)
@@ -1112,6 +1112,28 @@ class RecipeImportSerializer(SpacedModelSerializer):
fields = '__all__'
class RecipeBatchUpdateSerializer(serializers.Serializer):
recipes = serializers.ListField(child=serializers.IntegerField())
keywords_add = serializers.ListField(child=serializers.IntegerField())
keywords_remove = serializers.ListField(child=serializers.IntegerField())
keywords_set = serializers.ListField(child=serializers.IntegerField())
keywords_remove_all = serializers.BooleanField(default=False)
working_time = serializers.IntegerField(required=False, allow_null=True)
waiting_time = serializers.IntegerField(required=False, allow_null=True)
servings = serializers.IntegerField(required=False, allow_null=True)
servings_text = serializers.CharField(required=False, allow_null=True, allow_blank=True)
private = serializers.BooleanField(required=False, allow_null=True)
shared_add = serializers.ListField(child=serializers.IntegerField())
shared_remove = serializers.ListField(child=serializers.IntegerField())
shared_set = serializers.ListField(child=serializers.IntegerField())
shared_remove_all = serializers.BooleanField(default=False)
show_ingredient_overview = serializers.BooleanField(required=False, allow_null=True)
clear_description = serializers.BooleanField(required=False, allow_null=True)
class CustomFilterSerializer(SpacedModelSerializer, WritableNestedModelSerializer):
shared = UserSerializer(many=True, required=False)
@@ -1223,8 +1245,8 @@ class MealPlanSerializer(SpacedModelSerializer, WritableNestedModelSerializer):
class AutoMealPlanSerializer(serializers.Serializer):
start_date = serializers.DateField()
end_date = serializers.DateField()
start_date = serializers.DateTimeField()
end_date = serializers.DateTimeField()
meal_type_id = serializers.IntegerField()
keyword_ids = serializers.ListField()
servings = CustomDecimalField()
@@ -1480,7 +1502,7 @@ class InviteLinkSerializer(WritableNestedModelSerializer):
fields = (
'id', 'uuid', 'email', 'group', 'valid_until', 'used_by', 'reusable', 'internal_note', 'created_by',
'created_at',)
read_only_fields = ('id', 'uuid', 'used_by' ,'created_by', 'created_at',)
read_only_fields = ('id', 'uuid', 'used_by', 'created_by', 'created_at',)
# CORS, REST and Scopes aren't currently working
@@ -1770,6 +1792,7 @@ class AiImportSerializer(serializers.Serializer):
text = serializers.CharField(allow_null=True, allow_blank=True)
recipe_id = serializers.CharField(allow_null=True, allow_blank=True)
class ExportRequestSerializer(serializers.Serializer):
type = serializers.CharField()
all = serializers.BooleanField(default=False)

View File

@@ -51,11 +51,6 @@
{# {% endif %}#}
<p class="card-text"><small
class="text-muted">{% trans 'Owner' %}: {{ us.space.created_by }}</small>
{% if us.space.created_by != us.user %}
<p class="card-text"><small
class="text-muted"><a
href="{% url 'delete_user_space' us.pk %}">{% trans 'Leave Space' %}</a></small>
{% endif %}
<!--TODO add direct link to management page -->
</p>
</div>

View File

@@ -53,6 +53,17 @@
{% endblocktrans %}
{% endif %}
<h3 class="mt-5">{% trans 'Plugins' %}</h3>
Clone the plugin using git into the <code>recipe/plugin/</code> folder (Docker mount <code>/opt/recipe/recipes/plugins</code> to the host system and clone into it).
<table class="table table-bordered">
{% for p in plugins %}
<tr>
<td><a href="{{ p.github }}">{{ p.name }}</a> <br/>{{ p.base_path }}</td>
<td><a href="{% url 'view_plugin_update' %}?module={{ p.module }}" class="btn btn-primary">Git Pull</a></td>
</tr>
{% endfor %}
</table>
<h4 class="mt-3">{% trans 'Media Serving' %} <span class="badge text-bg-{% if gunicorn_media %}danger{% else %}success{% endif %}">{% if gunicorn_media %}
{% trans 'Warning' %}{% else %}{% trans 'Ok' %}{% endif %}</span></h4>
{% if gunicorn_media %}

View File

@@ -80,8 +80,9 @@ urlpatterns = [
path('switch-space/<int:space_id>', views.switch_space, name='view_switch_space'),
path('no-perm/', views.no_perm, name='view_no_perm'),
path('invite/<slug:token>', views.invite_link, name='view_invite'),
path('system/', views.system, name='view_system'),
path('system/', views.system, name='view_system'),
path('plugin/update/', views.plugin_update, name='view_plugin_update'),
path('abuse/<slug:token>', views.report_share_abuse, name='view_report_share_abuse'),
@@ -125,10 +126,6 @@ urlpatterns = [
]
if DEBUG:
urlpatterns.append(path('test/', views.test, name='view_test'))
urlpatterns.append(path('test2/', views.test2, name='view_test2'))
# catchall view for new frontend
urlpatterns += [
path('', views.index, name='index'),

View File

@@ -110,7 +110,7 @@ from cookbook.serializer import (AccessTokenSerializer, AutomationSerializer, Au
UserSerializer, UserSpaceSerializer, ViewLogSerializer,
LocalizationSerializer, ServerSettingsSerializer, RecipeFromSourceResponseSerializer, ShoppingListEntryBulkCreateSerializer, FdcQuerySerializer,
AiImportSerializer, ImportOpenDataSerializer, ImportOpenDataMetaDataSerializer, ImportOpenDataResponseSerializer, ExportRequestSerializer,
RecipeImportSerializer, ConnectorConfigSerializer, SearchPreferenceSerializer, SearchFieldsSerializer
RecipeImportSerializer, ConnectorConfigSerializer, SearchPreferenceSerializer, SearchFieldsSerializer, RecipeBatchUpdateSerializer
)
from cookbook.version_info import TANDOOR_VERSION
from cookbook.views.import_export import get_integration
@@ -411,6 +411,7 @@ class MergeMixin(ViewSetMixin):
description='Return first level children of {obj} with ID [int]. Integer 0 will return root {obj}s.',
type=int),
OpenApiParameter(name='tree', description='Return all self and children of {obj} with ID [int].', type=int),
OpenApiParameter(name='root_tree', description='Return all items belonging to the tree of the given {obj} id', type=int),
]),
move=extend_schema(parameters=[
OpenApiParameter(name="parent", description='The ID of the desired parent of the {obj}.', type=OpenApiTypes.INT,
@@ -423,6 +424,7 @@ class TreeMixin(MergeMixin, FuzzyFilterMixin):
def get_queryset(self):
root = self.request.query_params.get('root', None)
tree = self.request.query_params.get('tree', None)
root_tree = self.request.query_params.get('root_tree', None)
if root:
if root.isnumeric():
@@ -441,10 +443,21 @@ class TreeMixin(MergeMixin, FuzzyFilterMixin):
self.queryset = self.model.objects.get(id=int(tree)).get_descendants_and_self()
except self.model.DoesNotExist:
self.queryset = self.model.objects.none()
elif root_tree:
if root_tree.isnumeric():
try:
self.queryset = self.model.objects.get(id=int(root_tree)).get_root().get_descendants_and_self()
except self.model.DoesNotExist:
self.queryset = self.model.objects.none()
else:
return self.annotate_recipe(queryset=super().get_queryset(), request=self.request,
serializer=self.serializer_class, tree=True)
self.queryset = self.queryset.filter(space=self.request.space).order_by(Lower('name').asc())
self.queryset = self.queryset.filter(space=self.request.space)
# only order if not root_tree or tree mde because in these modes the sorting is relevant for the client
if not root_tree and not tree:
self.queryset = self.queryset.order_by(Lower('name').asc())
return self.annotate_recipe(queryset=self.queryset, request=self.request, serializer=self.serializer_class,
tree=True)
@@ -1359,9 +1372,103 @@ class RecipeViewSet(LoggingMixin, viewsets.ModelViewSet):
return Response(self.serializer_class(qs, many=True).data)
@decorators.action(detail=False, methods=['PUT'], serializer_class=RecipeBatchUpdateSerializer)
def batch_update(self, request):
serializer = self.serializer_class(data=request.data, partial=True)
if serializer.is_valid():
recipes = Recipe.objects.filter(id__in=serializer.validated_data['recipes'], space=self.request.space)
safe_recipe_ids = Recipe.objects.filter(id__in=serializer.validated_data['recipes'], space=self.request.space).values_list('id', flat=True)
if 'keywords_add' in serializer.validated_data:
keyword_relations = []
for r in recipes:
for k in serializer.validated_data['keywords_add']:
keyword_relations.append(Recipe.keywords.through(recipe_id=r.pk, keyword_id=k))
Recipe.keywords.through.objects.bulk_create(keyword_relations, ignore_conflicts=True, unique_fields=('recipe_id', 'keyword_id',))
if 'keywords_remove' in serializer.validated_data:
for k in serializer.validated_data['keywords_remove']:
Recipe.keywords.through.objects.filter(recipe_id__in=safe_recipe_ids, keyword_id=k).delete()
if 'keywords_set' in serializer.validated_data and len(serializer.validated_data['keywords_set']) > 0:
keyword_relations = []
Recipe.keywords.through.objects.filter(recipe_id__in=safe_recipe_ids).delete()
for r in recipes:
for k in serializer.validated_data['keywords_set']:
keyword_relations.append(Recipe.keywords.through(recipe_id=r.pk, keyword_id=k))
Recipe.keywords.through.objects.bulk_create(keyword_relations, ignore_conflicts=True, unique_fields=('recipe_id', 'keyword_id',))
if 'keywords_remove_all' in serializer.validated_data and serializer.validated_data['keywords_remove_all']:
Recipe.keywords.through.objects.filter(recipe_id__in=safe_recipe_ids).delete()
if 'working_time' in serializer.validated_data:
recipes.update(working_time=serializer.validated_data['working_time'])
if 'waiting_time' in serializer.validated_data:
recipes.update(waiting_time=serializer.validated_data['waiting_time'])
if 'servings' in serializer.validated_data:
recipes.update(servings=serializer.validated_data['servings'])
if 'servings_text' in serializer.validated_data:
recipes.update(servings_text=serializer.validated_data['servings_text'])
if 'private' in serializer.validated_data and serializer.validated_data['private'] is not None:
recipes.update(private=serializer.validated_data['private'])
if 'shared_add' in serializer.validated_data:
shared_relation = []
for r in recipes:
for u in serializer.validated_data['shared_add']:
shared_relation.append(Recipe.shared.through(recipe_id=r.pk, user_id=u))
Recipe.shared.through.objects.bulk_create(shared_relation, ignore_conflicts=True, unique_fields=('recipe_id', 'user_id',))
if 'shared_remove' in serializer.validated_data:
for s in serializer.validated_data['shared_remove']:
Recipe.shared.through.objects.filter(recipe_id__in=safe_recipe_ids, user_id=s).delete()
if 'shared_set' in serializer.validated_data and len(serializer.validated_data['shared_set']) > 0:
shared_relation = []
Recipe.shared.through.objects.filter(recipe_id__in=safe_recipe_ids).delete()
for r in recipes:
for u in serializer.validated_data['shared_set']:
shared_relation.append(Recipe.shared.through(recipe_id=r.pk, user_id=u))
Recipe.shared.through.objects.bulk_create(shared_relation, ignore_conflicts=True, unique_fields=('recipe_id', 'user_id',))
if 'shared_remove_all' in serializer.validated_data and serializer.validated_data['shared_remove_all']:
Recipe.shared.through.objects.filter(recipe_id__in=safe_recipe_ids).delete()
if 'clear_description' in serializer.validated_data and serializer.validated_data['clear_description']:
recipes.update(description='')
if 'show_ingredient_overview' in serializer.validated_data and serializer.validated_data['show_ingredient_overview'] is not None:
recipes.update(show_ingredient_overview=serializer.validated_data['show_ingredient_overview'])
return Response({}, 200)
return Response(serializer.errors, 400)
@extend_schema(responses=RecipeSerializer(many=False))
@decorators.action(detail=True, pagination_class=None, methods=['PATCH'], serializer_class=RecipeSerializer)
def delete_external(self, request, pk):
obj = self.get_object()
if obj.get_space() != request.space and has_group_permission(request.user, ['user']):
raise PermissionDenied(detail='You do not have the required permission to perform this action', code=403)
if obj.storage:
get_recipe_provider(obj).delete_file(obj)
obj.storage = None
obj.file_path = ''
obj.file_uid = ''
obj.save()
return Response(self.serializer_class(obj, many=False, context={'request': request}).data)
@extend_schema_view(list=extend_schema(
parameters=[OpenApiParameter(name='food_id', description='ID of food to filter for', type=int), ]))
parameters=[OpenApiParameter(name='food_id', description='ID of food to filter for', type=int),
OpenApiParameter(name='query', description='query that looks into food, base unit or converted unit by name', type=str), ]))
class UnitConversionViewSet(LoggingMixin, viewsets.ModelViewSet):
queryset = UnitConversion.objects
serializer_class = UnitConversionSerializer
@@ -1373,6 +1480,10 @@ class UnitConversionViewSet(LoggingMixin, viewsets.ModelViewSet):
if food_id is not None:
self.queryset = self.queryset.filter(food_id=food_id)
query = self.request.query_params.get('query', None)
if query is not None:
self.queryset = self.queryset.filter(Q(food__name__icontains=query) | Q(base_unit__name__icontains=query) | Q(converted_unit__name__icontains=query))
return self.queryset.filter(space=self.request.space)
@@ -2415,7 +2526,7 @@ def meal_plans_to_ical(queryset, filename):
request=inline_serializer(name="IngredientStringSerializer", fields={'text': CharField()}),
responses=inline_serializer(name="ParsedIngredientSerializer",
fields={'amount': IntegerField(), 'unit': CharField(), 'food': CharField(),
'note': CharField()})
'note': CharField(), 'original_text': CharField()})
)
@api_view(['POST'])
@permission_classes([CustomIsUser & CustomTokenHasReadWriteScope])
@@ -2425,13 +2536,20 @@ def ingredient_from_string(request):
ingredient_parser = IngredientParser(request, False)
amount, unit, food, note = ingredient_parser.parse(text)
ingredient = {'amount': amount, 'unit': None, 'food': None, 'note': note}
ingredient = {'amount': amount, 'unit': None, 'food': None, 'note': note, 'original_text': text}
if food:
food, created = Food.objects.get_or_create(space=request.space, name=food)
ingredient['food'] = {'name': food.name, 'id': food.id}
if food_obj := Food.objects.filter(space=request.space).filter(Q(name=food) | Q(plural_name=food)).first():
ingredient['food'] = {'name': food_obj.name, 'id': food_obj.id}
else:
food_obj = Food.objects.create(space=request.space, name=food)
ingredient['food'] = {'name': food_obj.name, 'id': food_obj.id}
if unit:
unit, created = Unit.objects.get_or_create(space=request.space, name=unit)
if unit_obj := Unit.objects.filter(space=request.space).filter(Q(name=unit) | Q(plural_name=unit)).first():
ingredient['food'] = {'name': unit_obj.name, 'id': unit_obj.id}
else:
unit_obj = Unit.objects.create(space=request.space, name=unit)
ingredient['food'] = {'name': unit_obj.name, 'id': unit_obj.id}
ingredient['unit'] = {'name': unit.name, 'id': unit.id}
return JsonResponse(ingredient, status=200)

View File

@@ -1,5 +1,6 @@
import os
import re
import subprocess
from datetime import datetime, timedelta
from io import StringIO
from uuid import UUID
@@ -13,7 +14,7 @@ from django.contrib.auth.decorators import login_required
from django.contrib.auth.models import Group
from django.contrib.auth.password_validation import validate_password
from django.core.cache import caches
from django.core.exceptions import ValidationError
from django.core.exceptions import ValidationError, PermissionDenied, BadRequest
from django.core.management import call_command
from django.db import models
from django.http import HttpResponseRedirect, JsonResponse, HttpResponse
@@ -238,11 +239,12 @@ def system(request):
for x in r.zrange('api:space-request-count', 0, 20, withscores=True, desc=True):
s = x[0].decode('utf-8')
space_stats = [Space.objects.get(pk=s).name, x[1]]
for i in range(0, 6):
d = (date.today() - timedelta(days=i)).isoformat()
space_stats.append(r.zscore(f'api:space-request-count:{d}', s))
api_space_stats.append(space_stats)
if space := Space.objects.filter(pk=s).first():
space_stats = [space.name, x[1]]
for i in range(0, 6):
d = (date.today() - timedelta(days=i)).isoformat()
space_stats.append(r.zscore(f'api:space-request-count:{d}', s))
api_space_stats.append(space_stats)
cache_response = caches['default'].get(f'system_view_test_cache_entry', None)
if not cache_response:
@@ -266,6 +268,22 @@ def system(request):
})
def plugin_update(request):
if not request.user.is_superuser:
raise PermissionDenied
if not 'module' in request.GET:
raise BadRequest
for p in PLUGINS:
if p['module'] == request.GET['module']:
update_response = subprocess.check_output(['git', 'pull'], cwd=p['base_path'])
print(update_response)
return HttpResponseRedirect(reverse('view_system'))
return HttpResponseRedirect(reverse('view_system'))
def setup(request):
with scopes_disabled():
if User.objects.count() > 0 or 'django.contrib.auth.backends.RemoteUserBackend' in settings.AUTHENTICATION_BACKENDS:

View File

@@ -11,34 +11,14 @@ If you like this application and want it to give back, there are many ways to co
If you know any foreign languages you can:
Improve the translations for any of the existing languages.
Add a new language to the long list of existing translations.
- Armenian
- Bulgarian
- Catalan
- Czech
- Danish
- Dutch
- English
- French
- German
- Hungarian
- Italian
- Latvian
- Norwegian
- Polish
- Russian
- Spanish
- Swedish
See [here](/docs/contribute/translations) for further information on how to contribute translation to Tandoor.
See [here](/contribute/translations/) for further information on how to contribute translation to Tandoor.
## Issues and Feature Requests
The most basic but also very important way of contributing is reporting issues and commenting on ideas and feature requests
The most basic but also crucial way of contributing is reporting issues and commenting on ideas and feature requests
over at [GitHub issues](https://github.com/vabene1111/recipes/issues).
Without feedback improvement can't happen, so don't hesitate to say what you want to say.
Without feedback, improvement can't happen, so don't hesitate to say what you want to say.
## Documentation
@@ -46,12 +26,12 @@ Helping improve the documentation for Tandoor is one of the easiest ways to give
You can write guides on how to install and configure Tandoor expanding our repository of non-standard configuations.
Or you can write how-to guides using some of Tandoor's advanced features such as authentication or automation.
See [here](/docs/contribute/documentation) for more information on how to add documentation to Tandoor.
See [here](/contribute/documentation/) for more information on how to add documentation to Tandoor.
## Contributing Code
For the truly ambitious, you can help write code to fix issues, add additional features, or write your own scripts using
Tandoor's extensive API and share your work with the community.
Before writing any code, please make sure that you review [contribution guidelines](/docs/contribute/guidelines) and
[VSCode](/docs/contribute/vscode) or [PyCharm](/docs/contribute/pycharm) specific configurations.
Before writing any code, please make sure that you review [contribution guidelines](/contribute/guidelines/) and
[VSCode](/contribute/vscode) or [PyCharm](/contribute/pycharm) specific configurations.

View File

@@ -32,10 +32,10 @@ To contribute to the project you are required to use the following packages with
## Testing
Django uses pytest-django to implement a full suite of testing. If you make any functional changes, please implment the appropriate
Django uses pytest-django to implement a full suite of testing. If you make any functional changes, please implement the appropriate
tests.
Tandoor is also actively soliciting contribors willing to setup vue3 testing. If you have knowledge in this area it would be greatly appreciated.
Tandoor is also actively soliciting contributors willing to setup vue3 testing. If you have knowledge in this area it would be greatly appreciated.
## API Client
@@ -44,9 +44,7 @@ Tandoor is also actively soliciting contribors willing to setup vue3 testing. If
The OpenAPI Generator is a Java project. You must have the java binary executable available on your PATH for this to work.
Tandoor uses [django-rest-framework](https://www.django-rest-framework.org/) for API implementation. Making contributions that impact the API requires an understanding of
Viewsets and Serializers.
Also double check that your changes are actively reflected in the schema so that client apis are generated accurately.
ViewSets and Serializers.
The API Client is generated automatically from the OpenAPI interface provided by the Django REST framework.
For this [openapi-generator](https://github.com/OpenAPITools/openapi-generator) is used.
@@ -55,17 +53,9 @@ Install it using your desired setup method. (For example, using `npm install @op
### Vue
Navigate to `vue/src/utils/openapi`.
Generate the schema using `openapi-generator-cli generate -g typescript-axios -i http://127.0.0.1:8000/openapi/`. (Replace your dev server url if required.)
### Vue3
Navigate to `vue3/src/openapi`.
Generate the schema using `openapi-generator-cli generate -g typescript-fetch -i http://127.0.0.1:8000/openapi/`. (Replace your dev server url if required.)
Generate the schema using the `generate_api_client.py` script in the main directory.
## Install and Configuration
Instructions for [VSCode](/docs/contribute/vscode)
Instructions for [PyCharm](/docs/contribute/pycharm)
Instructions for [VSCode](/contribute/vscode)
Instructions for [PyCharm](/contribute/pycharm)

View File

@@ -1,7 +1,5 @@
<!-- prettier-ignore-start -->
!!! info "Development Setup"
The dev setup is a little messy as this application combines the best (at least in my opinion) of both Django and Vue.js.
<!-- prettier-ignore-end -->
### Devcontainer Setup
@@ -32,17 +30,15 @@ populated from default values.
### Vue.js
<!-- prettier-ignore-start -->
!!! warning "Feature Freeze"
With the exception of bug fixes, no changes will be accepted on the legacy `vue` front-end.
<!-- prettier-ignore-end -->
!!! danger "Development Setup"
The vite dev server **must** be started before the django runserver command is run or else django will **not** recognize it and try to fallback to the build files.
Most new frontend pages are build using [Vue.js](https://vuejs.org/).
The frontend is build using [Vue.js](https://vuejs.org/).
In order to work on these pages, you will have to install a Javascript package manager of your choice. The following examples use yarn.
In the `vue` folder run `yarn install` followed by `yarn build` to install and build the legacy front-end.
In the `vue3` folder run `yarn install` followed by `yarn build` to install and build the new front-end.
1. go to the `vue3` and run `yarn install` to install the dependencies
2. run `yarn serve` to start the dev server that allows hot reloading and easy and quick development
After that you can use `yarn serve` from the `vue3` folder to start the development server, and proceed to test your changes.
If you do not wish to work on those pages, but instead want the application to work properly during development, run `yarn build` to build the frontend pages once.
If you do not wish to work on those pages, but instead want the application to work properly during development, run `yarn build` to build the frontend pages once. After that you
might need to run `python manage.py collectstatic` to setup the static files.

View File

@@ -16,8 +16,6 @@ Maintained by [Aimo](https://github.com/aimok04/kitshn)
- Website: [https://kitshn.app/](https://kitshn.app/)
- Appstores: [Apple](https://apps.apple.com/us/app/kitshn-for-tandoor/id6740168361), [Android](https://play.google.com/store/apps/details?id=de.kitshn.android)
### Untare (discontinued)
Maintained by [phantomate](https://github.com/phantomate/Untare)

View File

@@ -33,17 +33,26 @@ VS Marketplace Link: https://marketplace.visualstudio.com/items?itemName=esbenp.
<!-- prettier-ignore -->
!!! note
In order to debug vue yarn and vite servers must be started before starting the django server.
In order to hot reload vue, the `yarn dev` server must be started before starting the django server.
There are a number of built in tasks that are available. Here are a few of the key ones:
- `Setup Dev Server` - Runs all the prerequisite steps so that the dev server can be run inside VSCode.
- `Setup Tests` - Runs all prerequisites so tests can be run inside VSCode.
Once these are run, you should be able to run/debug a django server in VSCode as well as run/debug tests directly through VSCode.
There are also a few other tasks specified in case you have specific development needs:
Once these are run, there are 2 options. If you want to run a vue3 server in a hot reload mode for quick development of the frontend, you should run a development vue server:
- `Yarn Dev` - Runs development Vue.js vite server not connected to VSCode. Useful if you want to make Vue changes and see them in realtime.
If not, you need to build and copy the frontend to the django server. If you make changes to the frontend, you need to re-run this and restart the django server:
- `Collect Static Files` - Builds and collects the vue3 frontend so that it can be served via the django server.
Once either of those steps are done, you can start the django server:
- `Run Dev Server` - Runs a django development server not connected to VSCode.
There are also a few other tasks specified in case you have specific development needs:
- `Run all pytests` - Runs all the pytests outside of VSCode.
- `Yarn Serve` - Runs development Vue.js server not connected to VSCode. Useful if you want to make Vue changes and see them in realtime.
- `Serve Documentation` - Runs a documentation server. Useful if you want to see how changes to documentation show up.

21
plugin.py Normal file
View File

@@ -0,0 +1,21 @@
import os
import subprocess
import traceback
BASE_DIR = os.path.dirname(os.path.abspath(__file__))
#TODO clean existing links for when plugins are uninstalled or not necessary because it will just be empty links?
PLUGINS_DIRECTORY = os.path.join(BASE_DIR, 'recipes', 'plugins')
if os.path.isdir(PLUGINS_DIRECTORY):
for d in os.listdir(PLUGINS_DIRECTORY):
if d != '__pycache__':
try:
subprocess.run(['python', 'setup_repo.py'], shell=(os.name == 'nt'), cwd=os.path.join(BASE_DIR, 'recipes', 'plugins', d))
except Exception:
traceback.print_exc()
print(f'ERROR failed to link plugin {d}')
subprocess.run(['npm', 'install', '--global', 'yarn'], shell=(os.name == 'nt'), cwd=os.path.join(BASE_DIR, 'vue3'))
subprocess.run(['yarn', 'install'], shell=(os.name == 'nt'), cwd=os.path.join(BASE_DIR, 'vue3'))
subprocess.run(['yarn', 'build'], shell=(os.name == 'nt'), cwd=os.path.join(BASE_DIR, 'vue3'))

View File

@@ -221,10 +221,7 @@ try:
'module': f'recipes.plugins.{d}',
'base_path': os.path.join(BASE_DIR, 'recipes', 'plugins', d),
'base_url': plugin_class.base_url,
'bundle_name': plugin_class.bundle_name if hasattr(plugin_class, 'bundle_name') else '',
'api_router_name': plugin_class.api_router_name if hasattr(plugin_class, 'api_router_name') else '',
'nav_main': plugin_class.nav_main if hasattr(plugin_class, 'nav_main') else '',
'nav_dropdown': plugin_class.nav_dropdown if hasattr(plugin_class, 'nav_dropdown') else '',
}
PLUGINS.append(plugin_config)
print(f'PLUGIN {d} loaded')
@@ -534,28 +531,6 @@ if REDIS_HOST:
# Vue webpack settings
VUE_DIR = os.path.join(BASE_DIR, 'vue')
WEBPACK_LOADER = {
'DEFAULT': {
'CACHE': not DEBUG,
'BUNDLE_DIR_NAME': 'vue/', # must end with slash
'STATS_FILE': os.path.join(VUE_DIR, 'webpack-stats.json'),
'POLL_INTERVAL': 0.1,
'TIMEOUT': None,
'IGNORE': [r'.+\.hot-update.js', r'.+\.map'],
},
}
for p in PLUGINS:
if p['bundle_name'] != '':
WEBPACK_LOADER[p['bundle_name']] = {
'CACHE': not DEBUG,
'BUNDLE_DIR_NAME': 'vue/', # must end with slash
'STATS_FILE': os.path.join(p["base_path"], 'vue', 'webpack-stats.json'),
'POLL_INTERVAL': 0.1,
'TIMEOUT': None,
'IGNORE': [r'.+\.hot-update.js', r'.+\.map'],
}
DJANGO_VITE = {
"default": {
"dev_mode": False,

View File

@@ -10,6 +10,7 @@
},
"dependencies": {
"@types/luxon": "^3.7.1",
"@types/sortablejs": "^1.15.8",
"@vueform/multiselect": "^2.6.11",
"@vueuse/core": "^13.6.0",
"@vueuse/router": "^13.6.0",
@@ -22,7 +23,6 @@
"vue-router": "^4.5.0",
"vue-simple-calendar": "7.1.0",
"vuedraggable": "^4.1.0",
"@types/sortablejs": "^1.15.8",
"vuetify": "^3.9.3"
},
"devDependencies": {
@@ -32,20 +32,21 @@
"@types/node": "^24.0.8",
"@vitejs/plugin-vue": "^6.0.0",
"@vue/tsconfig": "^0.7.0",
"esbuild-register": "^3.6.0",
"jsdom": "^26.1.0",
"typescript": "^5.8.3",
"vite": "6.3.5",
"vite-plugin-pwa": "^1.0.2",
"workbox-core": "^7.3.0",
"workbox-build": "^7.3.0",
"workbox-window": "^7.3.0",
"vite-plugin-vuetify": "^2.1.1",
"vue-tsc": "^2.2.8",
"workbox-background-sync": "^7.3.0",
"workbox-build": "^7.3.0",
"workbox-core": "^7.3.0",
"workbox-expiration": "^7.3.0",
"workbox-navigation-preload": "^7.3.0",
"workbox-precaching": "^7.3.0",
"workbox-routing": "^7.3.0",
"workbox-strategies": "^7.3.0",
"vite-plugin-vuetify": "^2.1.1",
"vue-tsc": "^2.2.8"
"workbox-window": "^7.3.0"
}
}

View File

@@ -0,0 +1,114 @@
<template>
<v-dialog max-width="600px" :activator="props.activator" v-model="dialog">
<v-card :loading="loading">
<v-closable-card-title v-model="dialog" :title="$t('Auto_Planner')" icon="fa-solid fa-calendar-plus"></v-closable-card-title>
<v-card-text>
<v-form>
<model-select model="MealType" v-model="autoMealPlan.mealTypeId" :object="false"></model-select>
<model-select model="Keyword" v-model="autoMealPlan.keywordIds" mode="tags" :object="false"></model-select>
<v-number-input :label="$t('Servings')" v-model="autoMealPlan.servings"></v-number-input>
<v-date-input :label="$t('Date')"
multiple="range"
v-model="dateRangeValue"
:first-day-of-week="useUserPreferenceStore().deviceSettings.mealplan_startingDayOfWeek"
:show-week="useUserPreferenceStore().deviceSettings.mealplan_displayWeekNumbers"
prepend-icon=""
prepend-inner-icon="$calendar"
></v-date-input>
<model-select model="User" v-model="autoMealPlan.shared" mode="tags"></model-select>
<v-checkbox v-model="autoMealPlan.addshopping" :label="$t('AddToShopping')" hide-details></v-checkbox>
</v-form>
</v-card-text>
<v-card-actions>
<v-btn @click="dialog = false">{{ $t('Cancel') }}</v-btn>
<v-btn color="create" prepend-icon="fa-solid fa-person-running" @click="doAutoPlan()" :loading="loading">{{ $t('Create') }}</v-btn>
</v-card-actions>
</v-card>
</v-dialog>
</template>
<script setup lang="ts">
import {useI18n} from "vue-i18n";
import ModelSelect from "@/components/inputs/ModelSelect.vue";
import {ApiApi, AutoMealPlan} from "@/openapi";
import {onMounted, ref} from "vue";
import VClosableCardTitle from "@/components/dialogs/VClosableCardTitle.vue";
import {VDateInput} from 'vuetify/labs/VDateInput'
import {DateTime} from "luxon";
import {useUserPreferenceStore} from "@/stores/UserPreferenceStore.ts";
import {ErrorMessageType, PreparedMessage, useMessageStore} from "@/stores/MessageStore.ts";
import {useMealPlanStore} from "@/stores/MealPlanStore.ts";
const emit = defineEmits(['change'])
const props = defineProps({
activator: {type: String, default: 'parent'},
})
const {t} = useI18n()
const dialog = defineModel<boolean>({default: false})
const loading = ref(false)
const dateRangeValue = ref([] as Date[])
const autoMealPlan = ref({} as AutoMealPlan)
onMounted(() => {
initializeRequest()
})
/**
* load default values for auto plan creation
*/
function initializeRequest() {
autoMealPlan.value = {
servings: 1,
startDate: DateTime.now().toJSDate(),
endDate: DateTime.now().plus({day: 7}).toJSDate(),
shared: useUserPreferenceStore().userSettings.planShare,
addshopping: useUserPreferenceStore().userSettings.mealplanAutoaddShopping,
} as AutoMealPlan
dateRangeValue.value = []
let currentDate = DateTime.fromJSDate(autoMealPlan.value.startDate).plus({day: 1}).toJSDate()
while (currentDate <= autoMealPlan.value.endDate) {
dateRangeValue.value.push(currentDate)
currentDate = DateTime.fromJSDate(currentDate).plus({day: 1}).toJSDate()
}
}
/**
* perform auto plan creation
*/
function doAutoPlan() {
let api = new ApiApi()
loading.value = true
autoMealPlan.value.startDate = dateRangeValue.value[0]
autoMealPlan.value.endDate = dateRangeValue.value[dateRangeValue.value.length - 1]
console.log('requesting auto plan from ', autoMealPlan.value.startDate, ' to ', autoMealPlan.value.endDate)
api.apiAutoPlanCreate({autoMealPlan: autoMealPlan.value}).then(r => {
dialog.value = false
useMealPlanStore().refreshLastUpdatedPeriod()
initializeRequest()
useMessageStore().addPreparedMessage(PreparedMessage.CREATE_SUCCESS)
}).catch(err => {
useMessageStore().addError(ErrorMessageType.CREATE_ERROR, err)
}).finally(() => {
loading.value = false
})
}
</script>
<style scoped>
</style>

View File

@@ -0,0 +1,98 @@
<template>
<v-dialog max-width="600px" :activator="props.activator" v-model="dialog">
<v-card :loading="loading">
<v-closable-card-title
:title="$t('delete_title', {type: $t(genericModel.model.localizationKey)})"
:sub-title="genericModel.getLabel(props.source)"
:icon="genericModel.model.icon"
v-model="dialog"
></v-closable-card-title>
<v-divider></v-divider>
<v-card-text>
{{ $t('BatchDeleteConfirm') }}
<v-list>
<v-list-item border v-for="item in itemsToDelete">
{{ genericModel.getLabel(item) }}
<template #append>
<v-icon icon="fa-solid fa-xmark" color="error" variant="tonal" v-if="failedItems.includes(item)"></v-icon>
<v-icon icon="fa-solid fa-check" color="success" variant="tonal" v-else-if="updatedItems.includes(item)"></v-icon>
<v-icon icon="fa-solid fa-circle-notch fa-spin" variant="tonal" color="info" v-else-if="loading"></v-icon>
<v-btn icon="fa-solid fa-up-right-from-square" :to="{name: 'IngredientEditorPage', query: {food_id: item.id}}"
v-if="genericModel.model.name == 'Food' && failedItems.includes(item)" size="small"></v-btn>
<v-btn icon="fa-solid fa-up-right-from-square" :to="{name: 'IngredientEditorPage', query: {unit_id: item.id}}"
v-if="genericModel.model.name == 'Unit' && failedItems.includes(item)" size="small"></v-btn>
</template>
</v-list-item>
</v-list>
<p class="font-italic text-disabled">{{$t('BatchDeleteHelp')}}</p>
</v-card-text>
<v-card-actions>
<v-btn :disabled="loading" @click="dialog = false">{{ $t('Cancel') }}</v-btn>
<v-btn color="error" @click="deleteAll()" :loading="loading">{{ $t('Delete_All') }}</v-btn>
</v-card-actions>
</v-card>
</v-dialog>
</template>
<script setup lang="ts">
import {onMounted, PropType, ref, watch} from "vue";
import {EditorSupportedModels, EditorSupportedTypes, getGenericModelFromString} from "@/types/Models.ts";
import VClosableCardTitle from "@/components/dialogs/VClosableCardTitle.vue";
import {useI18n} from "vue-i18n";
const emit = defineEmits(['change'])
const props = defineProps({
model: {type: String as PropType<EditorSupportedModels>, required: true},
items: {type: Array as PropType<Array<EditorSupportedTypes>>, required: true},
activator: {type: String, default: 'parent'},
})
const {t} = useI18n()
const dialog = defineModel<boolean>({default: false})
const loading = ref(false)
const genericModel = getGenericModelFromString(props.model, t)
const itemsToDelete = ref<EditorSupportedTypes[]>([])
const failedItems = ref<EditorSupportedTypes[]>([])
const updatedItems = ref<EditorSupportedTypes[]>([])
watch(dialog, (newValue, oldValue) => {
if(!oldValue && newValue){
itemsToDelete.value = JSON.parse(JSON.stringify(props.items))
}
})
/**
* loop through the items and delete them
*/
function deleteAll() {
let promises: Promise<any>[] = []
loading.value = true
itemsToDelete.value.forEach(item => {
promises.push(genericModel.destroy(item.id!).then((r: any) => {
updatedItems.value.push(item)
}).catch((err: any) => {
failedItems.value.push(item)
}))
})
Promise.allSettled(promises).then(() => {
loading.value = false
emit('change')
})
}
</script>
<style scoped>
</style>

View File

@@ -0,0 +1,166 @@
<template>
<v-dialog max-width="1200px" :activator="props.activator" v-model="dialog">
<v-card :loading="loading">
<v-closable-card-title
:title="$t('BatchEdit')"
:sub-title="$t('BatchEditUpdatingItemsCount', {type: $t('Recipes'), count: updateItems.length})"
:icon="TRecipe.icon"
v-model="dialog"
></v-closable-card-title>
<v-divider></v-divider>
<v-card-text>
<v-form>
<v-row>
<v-col cols="12" md="6">
<v-card :title="$t('Keywords')" :prepend-icon="TKeyword.icon" variant="plain">
<v-card-text>
<model-select model="Keyword" v-model="batchUpdateRequest.recipeBatchUpdate.keywordsAdd" :object="false" allow-create mode="tags">
<template #prepend>
<v-icon icon="fa-solid fa-add"></v-icon>
</template>
</model-select>
<model-select model="Keyword" v-model="batchUpdateRequest.recipeBatchUpdate.keywordsRemove" :object="false" allow-create mode="tags">
<template #prepend>
<v-icon icon="fa-solid fa-minus"></v-icon>
</template>
</model-select>
<model-select model="Keyword" v-model="batchUpdateRequest.recipeBatchUpdate.keywordsSet" :object="false" allow-create mode="tags">
<template #prepend>
<v-icon icon="fa-solid fa-equals"></v-icon>
</template>
</model-select>
<v-checkbox :label="$t('RemoveAllType', {type: $t('Keywords')})" hide-details v-model="batchUpdateRequest.recipeBatchUpdate.keywordsRemoveAll"></v-checkbox>
</v-card-text>
</v-card>
<v-card :title="$t('Private_Recipe')" :subtitle="$t('Private_Recipe_Help')" prepend-icon="fa-solid fa-eye-slash" variant="plain">
<v-card-text>
<v-select :items="boolUpdateOptions" :label="$t('Private_Recipe')" clearable v-model="batchUpdateRequest.recipeBatchUpdate._private"></v-select>
<model-select model="User" v-model="batchUpdateRequest.recipeBatchUpdate.sharedAdd" :object="false" allow-create mode="tags">
<template #prepend>
<v-icon icon="fa-solid fa-add"></v-icon>
</template>
</model-select>
<model-select model="User" v-model="batchUpdateRequest.recipeBatchUpdate.sharedRemove" :object="false" allow-create mode="tags">
<template #prepend>
<v-icon icon="fa-solid fa-minus"></v-icon>
</template>
</model-select>
<model-select model="User" v-model="batchUpdateRequest.recipeBatchUpdate.sharedSet" :object="false" allow-create mode="tags">
<template #prepend>
<v-icon icon="fa-solid fa-equals"></v-icon>
</template>
</model-select>
<v-checkbox :label="$t('RemoveAllType', {type: $t('Users')})" hide-details v-model="batchUpdateRequest.recipeBatchUpdate.sharedRemoveAll"></v-checkbox>
</v-card-text>
</v-card>
</v-col>
<v-col cols="12" md="6">
<v-card :title="$t('Miscellaneous')" prepend-icon="fa-solid fa-list" variant="plain">
<v-card-text>
<v-number-input :label="$t('WorkingTime')" v-model="batchUpdateRequest.recipeBatchUpdate.workingTime" :step="5">
</v-number-input>
<v-number-input :label="$t('WaitingTime')" v-model="batchUpdateRequest.recipeBatchUpdate.waitingTime" :step="5">
</v-number-input>
<v-number-input :label="$t('Serving')" v-model="batchUpdateRequest.recipeBatchUpdate.servings">
</v-number-input>
<v-text-field :label="$t('ServingsText')" v-model="batchUpdateRequest.recipeBatchUpdate.servingsText" @update:model-value="updateServings = true">
<template #append>
<v-checkbox v-model="updateServings" hide-details></v-checkbox>
</template>
</v-text-field>
<v-select :items="boolUpdateOptions" :label="$t('show_ingredient_overview')" clearable v-model="batchUpdateRequest.recipeBatchUpdate.showIngredientOverview"></v-select>
<v-checkbox hide-details :label="$t('DeleteSomething', {item: $t('Description')})" v-model="batchUpdateRequest.recipeBatchUpdate.clearDescription"></v-checkbox>
</v-card-text>
</v-card>
</v-col>
</v-row>
</v-form>
</v-card-text>
<v-card-actions>
<v-btn :disabled="loading" @click="dialog = false">{{ $t('Cancel') }}</v-btn>
<v-btn color="warning" :loading="loading" @click="batchUpdateRecipes()" :disabled="updateItems.length < 1">{{ $t('Update') }}</v-btn>
</v-card-actions>
</v-card>
</v-dialog>
</template>
<script setup lang="ts">
import {onMounted, PropType, ref, watch} from "vue";
import {EditorSupportedModels, EditorSupportedTypes, getGenericModelFromString, TKeyword, TRecipe} from "@/types/Models.ts";
import VClosableCardTitle from "@/components/dialogs/VClosableCardTitle.vue";
import {useI18n} from "vue-i18n";
import {ApiApi, ApiRecipeBatchUpdateUpdateRequest, Recipe, RecipeOverview} from "@/openapi";
import {ErrorMessageType, useMessageStore} from "@/stores/MessageStore.ts";
import ModelSelect from "@/components/inputs/ModelSelect.vue";
const emit = defineEmits(['change'])
const props = defineProps({
items: {type: Array as PropType<Array<RecipeOverview>>, required: true},
activator: {type: String, default: 'parent'},
})
const {t} = useI18n()
const dialog = defineModel<boolean>({default: false})
const loading = ref(false)
const updateItems = ref([] as RecipeOverview[])
const batchUpdateRequest = ref({recipeBatchUpdate: {servingsText: ''}} as ApiRecipeBatchUpdateUpdateRequest)
const updateServings = ref(false)
const boolUpdateOptions = ref([
{value: true, title: t('Yes')},
{value: false, title: t('No')},
])
/**
* copy prop when dialog opens so that items remain when parent is updated after change is emitted
*/
watch(dialog, (newValue, oldValue) => {
if (!oldValue && newValue && props.items != undefined) {
batchUpdateRequest.value.recipeBatchUpdate.recipes = props.items.flatMap(r => r.id!)
updateItems.value = JSON.parse(JSON.stringify(props.items))
}
})
/**
* perform batch request to update recipes
*/
function batchUpdateRecipes() {
let api = new ApiApi()
loading.value = true
// prevent accidentally clearing the field with extra checkbox
if (!updateServings.value) {
batchUpdateRequest.value.recipeBatchUpdate.servingsText = undefined
}
api.apiRecipeBatchUpdateUpdate(batchUpdateRequest.value).then(r => {
}).catch(err => {
useMessageStore().addError(ErrorMessageType.UPDATE_ERROR, err)
}).finally(() => {
emit('change')
loading.value = false
})
}
</script>
<style scoped>
</style>

View File

@@ -3,26 +3,38 @@
<v-card :loading="loading">
<v-closable-card-title
:title="$t('merge_title', {type: $t(genericModel.model.localizationKey)})"
:sub-title="genericModel.getLabel(props.source)"
:sub-title="sourceNames"
:icon="genericModel.model.icon"
v-model="dialog"
></v-closable-card-title>
<v-divider></v-divider>
<v-card-text>
{{ $t('merge_selection', {source: genericModel.getLabel(props.source), type: $t(genericModel.model.localizationKey)}) }}
{{ $t('merge_selection', {source: sourceNames, type: $t(genericModel.model.localizationKey)}) }}
<model-select :model="props.model" v-model="target" allow-create></model-select>
<v-row>
<v-col class="text-center">
<v-card color="warning" variant="tonal">
<v-card-title>{{ genericModel.getLabel(props.source) }}</v-card-title>
</v-card>
<v-icon icon="fa-solid fa-arrow-down" class="mt-4 mb-4"></v-icon>
<v-card color="success" variant="tonal">
<v-card-title v-if="!target">?</v-card-title>
<v-card-title v-else>{{ genericModel.getLabel(target) }}</v-card-title>
</v-card>
<v-col>
<v-list>
<v-list-item border v-for="item in sourceItems">
{{ genericModel.getLabel(item) }}
<template #append>
<v-icon icon="fa-solid fa-xmark" color="error" variant="tonal" v-if="failedItems.includes(item)"></v-icon>
<v-icon icon="fa-solid fa-check" color="success" variant="tonal" v-else-if="updatedItems.includes(item)"></v-icon>
<v-icon icon="fa-solid fa-circle-notch fa-spin" variant="tonal" color="info" v-else-if="loading"></v-icon>
</template>
</v-list-item>
<v-list-item class="text-center">
<v-icon icon="fa-solid fa-arrow-down" class="mt-4 mb-4"></v-icon>
</v-list-item>
<v-list-item class="text-center" border>
<span v-if="!target">?</span>
<span v-else>{{ genericModel.getLabel(target) }}</span>
</v-list-item>
</v-list>
</v-col>
</v-row>
@@ -30,7 +42,7 @@
</v-card-text>
<v-card-actions>
<v-btn :disabled="loading">{{ $t('Cancel') }}</v-btn>
<v-btn :disabled="loading" @click="dialog = false">{{ $t('Cancel') }}</v-btn>
<v-btn color="warning" @click="mergeModel()" :loading="loading" :disabled="!target">{{ $t('Merge') }}</v-btn>
</v-card-actions>
</v-card>
@@ -40,7 +52,7 @@
<script setup lang="ts">
import ModelSelect from "@/components/inputs/ModelSelect.vue";
import {PropType, ref} from "vue";
import {computed, PropType, ref, watch} from "vue";
import {EditorSupportedModels, EditorSupportedTypes, getGenericModelFromString} from "@/types/Models";
import {ErrorMessageType, PreparedMessage, useMessageStore} from "@/stores/MessageStore";
import {useI18n} from "vue-i18n";
@@ -51,7 +63,7 @@ const emit = defineEmits(['change'])
const props = defineProps({
model: {type: String as PropType<EditorSupportedModels>, required: true},
source: {type: {} as PropType<EditorSupportedTypes>, required: true},
source: {type: Array as PropType<Array<EditorSupportedTypes>>, required: true},
activator: {type: String, default: 'parent'},
})
@@ -64,41 +76,62 @@ const automate = ref(false)
const genericModel = getGenericModelFromString(props.model, t)
const target = ref<null | EditorSupportedTypes>(null)
const sourceItems = ref<EditorSupportedTypes[]>([])
const failedItems = ref<EditorSupportedTypes[]>([])
const updatedItems = ref<EditorSupportedTypes[]>([])
watch(dialog, (newValue, oldValue) => {
if (!oldValue && newValue) {
sourceItems.value = JSON.parse(JSON.stringify(props.source))
}
})
/**
* generate comma seperated list of item names that act as the source
*/
const sourceNames = computed(() => {
if (sourceItems.value) {
return sourceItems.value.map(i => genericModel.getLabel(i)).join(', ')
}
return ''
})
/**
* merge source into selected target
*/
function mergeModel() {
let api = new ApiApi()
let promises: Promise<any>[] = []
if (target.value != null) {
loading.value = true
genericModel.merge(props.source, target.value).then(r => {
useMessageStore().addPreparedMessage(PreparedMessage.UPDATE_SUCCESS)
emit('change', target.value)
sourceItems.value.forEach(sourceItem => {
promises.push(genericModel.merge(sourceItem, target.value).then(r => {
if (automate.value && target.value != null && Object.hasOwn(props.source, 'name') && Object.hasOwn(target.value, 'name')) {
let automation = {
name: `${t('Merge') } ${props.source.name} -> ${target.value.name}`.substring(0,128),
param1: props.source.name,
param2: target.value.name,
type: genericModel.model.mergeAutomation
} as Automation
api.apiAutomationCreate({automation: automation}).catch(err => {
useMessageStore().addError(ErrorMessageType.UPDATE_ERROR, err)
}).finally(() => {
loading.value = false
dialog.value = false
})
}
}).catch(err => {
useMessageStore().addError(ErrorMessageType.UPDATE_ERROR, err)
}).finally(() => {
if (!automate.value) {
loading.value = false
dialog.value = false
}
updatedItems.value.push(sourceItem)
if (automate.value && target.value != null && Object.hasOwn(sourceItem, 'name') && Object.hasOwn(sourceItem, 'name')) {
let automation = {
name: `${t('Merge')} ${sourceItem.name} -> ${target.value.name}`.substring(0, 128),
param1: sourceItem.name,
param2: target.value.name,
type: genericModel.model.mergeAutomation
} as Automation
promises.push(api.apiAutomationCreate({automation: automation}).catch(err => {
useMessageStore().addError(ErrorMessageType.UPDATE_ERROR, err)
}))
}
}).catch(err => {
updatedItems.value.push(sourceItem)
}))
})
Promise.allSettled(promises).then(() => {
loading.value = false
emit('change')
})
}
}

View File

@@ -10,7 +10,7 @@
<v-row>
<v-col>
<v-textarea :model-value="importLog.msg"></v-textarea>
<v-textarea :model-value="importLog.msg" max-rows="25" auto-grow></v-textarea>
</v-col>
</v-row>

View File

@@ -3,7 +3,7 @@
<slot name="prepend"></slot>
<v-chip class="me-1 mb-1" :label="props.label" :color="props.color" :size="props.size" :variant="props.variant" v-for="k in keywords"
:to="{name: 'SearchPage', query: {keywords: k.id}}"> {{ k.label }}
:to="useUserPreferenceStore().isAuthenticated ? {name: 'SearchPage', query: {keywords: k.id}} : undefined"> {{ k.label }}
</v-chip>
<slot name="append"></slot>
@@ -15,6 +15,7 @@
import {Keyword, KeywordLabel} from "@/openapi";
import {computed, PropType} from "vue";
import {useUserPreferenceStore} from "@/stores/UserPreferenceStore.ts";
const props = defineProps({
keywords: Array as PropType<Array<Keyword> | Array<KeywordLabel> | undefined>,

View File

@@ -131,9 +131,9 @@ function refreshVisiblePeriod(startDateUnknown: boolean) {
// load backwards to as on initial
if (startDateUnknown) {
useMealPlanStore().refreshFromAPI(DateTime.fromJSDate(calendarDate.value).minus({days: days}).toJSDate(), DateTime.now().plus({days: days}).toJSDate())
useMealPlanStore().refreshFromAPI(DateTime.fromJSDate(calendarDate.value).minus({days: days}).toJSDate(), DateTime.fromJSDate(calendarDate.value).plus({days: days}).toJSDate())
} else {
useMealPlanStore().refreshFromAPI(calendarDate.value, DateTime.now().plus({days: days}).toJSDate())
useMealPlanStore().refreshFromAPI(calendarDate.value, DateTime.fromJSDate(calendarDate.value).plus({days: days}).toJSDate())
}
}

View File

@@ -13,12 +13,17 @@
<template v-if="route.name == 'MealPlanPage'">
<v-divider></v-divider>
<v-list-item prepend-icon="fa-solid fa-calendar-plus" link>
{{$t('Auto_Planner')}}
<auto-plan-dialog></auto-plan-dialog>
</v-list-item>
<v-list-subheader>{{$t('Settings')}}</v-list-subheader>
<v-list-item>
<meal-plan-device-settings></meal-plan-device-settings>
</v-list-item>
</template>
</template>
<script setup lang="ts">
@@ -27,6 +32,7 @@ import {useRoute} from "vue-router";
import {getListModels} from "@/types/Models";
import {useUserPreferenceStore} from "@/stores/UserPreferenceStore";
import MealPlanDeviceSettings from "@/components/settings/MealPlanDeviceSettings.vue";
import AutoPlanDialog from "@/components/dialogs/AutoPlanDialog.vue";
const route = useRoute()

View File

@@ -0,0 +1,27 @@
<template>
<i class="fa-solid fa-lock"></i>
<span v-if="props.showText" class="ms-1 me-1">{{ $t('Private_Recipe') }}</span>
<v-chip class="me-1 mb-1" :color="props.color" :size="props.size" :variant="props.variant" v-for="u in users" :key="u.id" prepend-icon="fa-solid fa-share-nodes"> {{ u.displayName }}
</v-chip>
</template>
<script setup lang="ts">
import {User} from "@/openapi";
import {PropType} from "vue";
import {useUserPreferenceStore} from "@/stores/UserPreferenceStore.ts";
const props = defineProps({
showText: {type: Boolean, default: true},
users: {type: [] as PropType<Array<User>>, required: false},
size: {type: String, default: 'x-small'},
color: {type: String, default: ''},
variant: {type: String as PropType<NonNullable<"tonal" | "flat" | "text" | "elevated" | "outlined" | "plain"> | undefined>, default: 'tonal'},
})
</script>
<style scoped>
</style>

View File

@@ -1,40 +1,11 @@
<template>
<v-card class="mt-1" v-if="cookLogs.length > 0">
<v-card-title>{{ $t('Activity') }}</v-card-title>
<v-card-text>
<v-list>
<v-list-item v-for="c in cookLogs.sort((a,b) => a.createdAt! > b.createdAt! ? 1 : -1)" :key="c.id">
<template #prepend>
<v-avatar color="primary">V</v-avatar>
</template>
<v-list-item-title class="font-weight-bold">{{ c.createdBy.displayName }}
<v-rating density="comfortable" size="x-small" color="tandoor" class="float-right" v-model="c.rating" readonly v-if="c.rating != undefined"></v-rating>
</v-list-item-title>
{{ c.comment }}
<p v-if="c.servings != null && c.servings > 0">
{{ c.servings }}
<span v-if="recipe.servingsText != ''">{{ recipe.servingsText }}</span>
<span v-else-if="c.servings == 1">{{ $t('Serving') }}</span>
<span v-else>{{ $t('Servings') }}</span>
</p>
<p class="text-disabled">
{{ DateTime.fromJSDate(c.createdAt).toLocaleString(DateTime.DATETIME_SHORT) }}
</p>
</v-list-item>
</v-list>
</v-card-text>
</v-card>
<v-card class="mt-1 d-print-none">
<v-card class="mt-1 d-print-none" v-if="useUserPreferenceStore().isAuthenticated" :loading="loading">
<v-card-text>
<v-textarea :label="$t('Comment')" rows="2" v-model="newCookLog.comment"></v-textarea>
<v-row de>
<v-row dense>
<v-col cols="12" md="4">
<v-label>{{$t('Rating')}}</v-label><br/>
<v-label>{{ $t('Rating') }}</v-label>
<br/>
<v-rating v-model="newCookLog.rating" clearable hover density="compact"></v-rating>
</v-col>
<v-col cols="12" md="4">
@@ -52,6 +23,48 @@
</v-card-actions>
</v-card>
<v-card class="mt-1" v-if="cookLogs.length > 0" :loading="loading">
<v-card-title>{{ $t('Activity') }}</v-card-title>
<v-card-text>
<v-list>
<v-list-item class="border-t-sm" v-for="c in cookLogs" :key="c.id" :link="c.createdBy.id == useUserPreferenceStore().userSettings?.user.id">
<template #prepend>
<v-avatar color="primary">{{ c.createdBy.displayName.charAt(0) }}</v-avatar>
</template>
<v-list-item-title class="font-weight-bold">
{{ c.createdBy.displayName }}
</v-list-item-title>
<v-list-item-subtitle>{{ c.comment }}</v-list-item-subtitle>
<v-list-item-subtitle class="font-italic mt-1" v-if="c.servings != null && c.servings > 0">
{{ c.servings }}
<span v-if="recipe.servingsText != ''">{{ recipe.servingsText }}</span>
<span v-else-if="c.servings == 1">{{ $t('Serving') }}</span>
<span v-else>{{ $t('Servings') }}</span>
</v-list-item-subtitle>
<template #append>
<v-list-item-action class="flex-column align-end">
<v-rating density="comfortable" size="x-small" color="tandoor" v-model="c.rating" half-increments readonly
v-if="c.rating != undefined"></v-rating>
<v-spacer></v-spacer>
<v-tooltip location="top" :text="DateTime.fromJSDate(c.createdAt).toLocaleString(DateTime.DATETIME_MED)" v-if="c.createdAt != undefined">
<template v-slot:activator="{ props }">
<span v-bind="props">{{ DateTime.fromJSDate(c.createdAt).toRelative({style: 'narrow'}) }}</span>
</template>
</v-tooltip>
</v-list-item-action>
</template>
<model-edit-dialog model="CookLog" :item="c" v-if="c.createdBy.id == useUserPreferenceStore().userSettings?.user.id" @save="recLoadCookLog(props.recipe.id)" @delete="recLoadCookLog(props.recipe.id)"></model-edit-dialog>
</v-list-item>
</v-list>
</v-card-text>
</v-card>
</template>
@@ -62,6 +75,8 @@ import {ApiApi, CookLog, Recipe} from "@/openapi";
import {DateTime} from "luxon";
import {ErrorMessageType, useMessageStore} from "@/stores/MessageStore";
import {VDateInput} from 'vuetify/labs/VDateInput'
import {useUserPreferenceStore} from "@/stores/UserPreferenceStore.ts";
import ModelEditDialog from "@/components/dialogs/ModelEditDialog.vue";
const props = defineProps({
recipe: {
@@ -73,21 +88,31 @@ const props = defineProps({
const newCookLog = ref({} as CookLog);
const cookLogs = ref([] as CookLog[])
const loading = ref(false)
onMounted(() => {
refreshActivity()
recLoadCookLog(props.recipe.id)
resetForm()
})
/**
* load cook logs from database for given recipe
* recursively load cook logs from database for given recipe
*/
function refreshActivity() {
function recLoadCookLog(recipeId: number, page: number = 1) {
const api = new ApiApi()
api.apiCookLogList({recipe: props.recipe.id}).then(r => {
// TODO pagination
loading.value = true
if(page == 1){
cookLogs.value = []
}
api.apiCookLogList({recipe: props.recipe.id, page: page}).then(r => {
if (r.results) {
cookLogs.value = r.results
cookLogs.value = cookLogs.value.concat(r.results)
if (r.next) {
recLoadCookLog(recipeId, page + 1)
} else {
cookLogs.value = cookLogs.value.sort((a, b) => a.createdAt! > b.createdAt! ? 1 : -1)
loading.value = false
}
}
})
}

View File

@@ -13,14 +13,18 @@
</div>
<div class="mt-1">
<!-- <v-btn icon="fas fa-ellipsis-v" size="small" variant="plain"></v-btn>-->
<recipe-context-menu :recipe="props.recipe" size="small"></recipe-context-menu>
<recipe-context-menu :recipe="props.recipe" size="small" v-if="props.showMenu"></recipe-context-menu>
</div>
</div>
<!-- <p class="text-disabled">{{ props.recipe.createdBy.displayName}}</p>-->
<keywords-component variant="outlined" :keywords="props.recipe.keywords" :max-keywords="3">
<keywords-component variant="outlined" :keywords="props.recipe.keywords" :max-keywords="3" v-if="props.showKeywords">
<template #prepend>
<v-chip class="mb-1 me-1" size="x-small" label variant="outlined" v-if="recipe._private">
<private-recipe-badge :show-text="false"></private-recipe-badge>
</v-chip>
<v-chip class="mb-1 me-1" size="x-small" label variant="outlined" color="info"
v-if="!props.recipe.internal">
v-if="props.recipe.internal == false">
{{ $t('External') }}
</v-chip>
<v-chip class="mb-1 me-1" size="x-small" prepend-icon="far fa-clock" label variant="outlined"
@@ -32,7 +36,7 @@
</div>
<v-card :to="`/recipe/${props.recipe.id}`" :style="{'height': props.height}" v-if="false">
<v-card :to="{name: 'RecipeViewPage', params: {id: props.recipe.id}}" :style="{'height': props.height}" v-if="false">
<v-tooltip
class="align-center justify-center"
location="top center" origin="overlap"
@@ -100,14 +104,16 @@ import {Recipe, RecipeOverview} from "@/openapi";
import RecipeContextMenu from "@/components/inputs/RecipeContextMenu.vue";
import RecipeImage from "@/components/display/RecipeImage.vue";
import {useRouter} from "vue-router";
import PrivateRecipeBadge from "@/components/display/PrivateRecipeBadge.vue";
const props = defineProps({
recipe: {type: {} as PropType<Recipe | RecipeOverview>, required: true,},
loading: {type: Boolean, required: false},
show_keywords: {type: Boolean, required: false},
showKeywords: {type: Boolean, default: true, required: false},
show_description: {type: Boolean, required: false},
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}
})
const router = useRouter()

View File

@@ -28,6 +28,7 @@
<recipe-context-menu :recipe="recipe" v-if="useUserPreferenceStore().isAuthenticated"></recipe-context-menu>
</v-sheet>
<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>
<v-rating v-model="recipe.rating" size="x-small" v-if="recipe.rating" half-increments readonly></v-rating>
<v-sheet class="ps-2 text-disabled">
{{ recipe.description }}
@@ -35,8 +36,7 @@
</v-card>
</v-card>
<!-- only display values if not all are default (e.g. for external recipes) -->
<v-card class="mt-1" v-if="recipe.workingTime != 0 || recipe.waitingTime != 0 || recipe.servings != 1">
<v-card class="mt-1">
<v-container>
<v-row class="text-center text-body-2">
<v-col class="pt-1 pb-1">
@@ -85,6 +85,8 @@
<i>{{ recipe.description }}</i>
</p>
<private-recipe-badge :users="recipe.shared" v-if="recipe._private"></private-recipe-badge>
<v-rating v-model="recipe.rating" size="x-small" v-if="recipe.rating" readonly></v-rating>
<keywords-component variant="flat" class="mt-4" :keywords="recipe.keywords"></keywords-component>
@@ -146,7 +148,8 @@
variant="outlined"
:title="$t('CreatedBy')"
:subtitle="recipe.createdBy.displayName"
prepend-icon="fa-solid fa-user">
prepend-icon="fa-solid fa-user"
:to="(useUserPreferenceStore().isAuthenticated) ? {name: 'SearchPage', query: {createdby: recipe.createdBy.id!}}: undefined">
</v-card>
</v-col>
<v-col cols="12" md="3">
@@ -154,7 +157,8 @@
variant="outlined"
:title="$t('Created')"
:subtitle="DateTime.fromJSDate(recipe.createdAt).toLocaleString(DateTime.DATETIME_MED)"
prepend-icon="$create">
prepend-icon="$create"
:to="(useUserPreferenceStore().isAuthenticated) ? {name: 'SearchPage', query: {createdon: DateTime.fromJSDate(recipe.createdAt).toISODate()}} : undefined">
</v-card>
</v-col>
<v-col cols="12" md="3">
@@ -162,7 +166,8 @@
variant="outlined"
:title="$t('Updated')"
:subtitle="DateTime.fromJSDate(recipe.updatedAt).toLocaleString(DateTime.DATETIME_MED)"
prepend-icon="$edit">
prepend-icon="$edit"
:to="(useUserPreferenceStore().isAuthenticated) ? {name: 'SearchPage', query: {updatedon: DateTime.fromJSDate(recipe.updatedAt).toISODate()}}: undefined">
</v-card>
</v-col>
<v-col cols="12" md="3" v-if="recipe.sourceUrl">
@@ -201,6 +206,7 @@ import PropertyView from "@/components/display/PropertyView.vue";
import {useUserPreferenceStore} from "@/stores/UserPreferenceStore.ts";
import {ErrorMessageType, useMessageStore} from "@/stores/MessageStore.ts";
import {useFileApi} from "@/composables/useFileApi.ts";
import PrivateRecipeBadge from "@/components/display/PrivateRecipeBadge.vue";
const {request, release} = useWakeLock()
const {doAiImport, fileApiLoading} = useFileApi()
@@ -252,7 +258,7 @@ function aiConvertRecipe() {
recipe.value.servingsText = r.recipe.servingsText
recipe.value.workingTime = r.recipe.workingTime
recipe.value.waitingTime = r.recipe.waitingTime
servings.value = r.recipe.servings
loading.value = true

View File

@@ -15,17 +15,16 @@
<i class="fas fa-check text-success fa-fw" v-if="a.checked"></i>
<i class="fas fa-clock-rotate-left text-info fa-fw" v-if="a.delayed"></i> <b>
<span :class="{'text-disabled': a.checked || a.delayed}" class="text-no-wrap">
{{ $n(a.amount) }}
<span v-if="a.unit">{{ a.unit.name }}</span>
<span v-if="amounts.length > 1 || (amounts.length == 1 && a.amount != 1)">{{ $n(a.amount) }}</span>
<span class="ms-1" v-if="a.unit">{{ pluralString(a.unit, a.amount) }}</span>
</span>
</b>
</span>
<br/>
</span>
</div>
<div class="d-flex flex-column flex-grow-1 align-self-center">
{{ shoppingListFood.food.name }} <br/>
{{ pluralString(shoppingListFood.food, (amounts.length > 1 || (amounts.length == 1 && amounts[0].amount > 1) ? 2 : 1)) }} <br/>
<span v-if="infoRow"><small class="text-disabled">{{ infoRow }}</small></span>
</div>
</div>
@@ -59,6 +58,7 @@ import {ErrorMessageType, useMessageStore} from "@/stores/MessageStore";
import {IShoppingListFood, ShoppingLineAmount} from "@/types/Shopping";
import {isDelayed, isEntryVisible, isShoppingListFoodDelayed, isShoppingListFoodVisible} from "@/utils/logic_utils";
import ShoppingLineItemDialog from "@/components/dialogs/ShoppingLineItemDialog.vue";
import {pluralString} from "@/utils/model_utils.ts";
const emit = defineEmits(['clicked'])

View File

@@ -123,6 +123,8 @@
</template>
</v-list>
<!-- TODO remove once append to body for model select is working properly -->
<v-spacer style="margin-top: 120px;"></v-spacer>
</v-col>
</v-row>
@@ -180,7 +182,8 @@
<template #append>
<v-btn icon="$create" color="create" :disabled="manualAddRecipe == undefined">
<v-icon icon="$create"></v-icon>
<add-to-shopping-dialog :recipe="manualAddRecipe" v-if="manualAddRecipe != undefined" @created="useShoppingStore().refreshFromAPI(); manualAddRecipe = undefined"></add-to-shopping-dialog>
<add-to-shopping-dialog :recipe="manualAddRecipe" v-if="manualAddRecipe != undefined"
@created="useShoppingStore().refreshFromAPI(); manualAddRecipe = undefined"></add-to-shopping-dialog>
</v-btn>
</template>
</ModelSelect>

View File

@@ -1,10 +1,19 @@
<template>
<v-expansion-panels>
<v-expansion-panel>
<v-expansion-panel-title><i class="far fa-list-alt fa-fw me-2"></i> {{ $t('StepsOverview') }}</v-expansion-panel-title>
<v-expansion-panel-title>
<i class="far fa-list-alt fa-fw me-2"></i> {{ $t('StepsOverview') }}
</v-expansion-panel-title>
<v-expansion-panel-text>
<v-container>
<v-row v-for="(s, i) in props.steps">
<v-row>
<v-col>
<v-btn-toggle density="compact" v-model="useUserPreferenceStore().deviceSettings.recipe_mergeStepOverview" border divided>
<v-btn :value="false" prepend-icon="fa-solid fa-folder-tree">{{ $t('Structured') }}</v-btn>
<v-btn :value="true" prepend-icon="fa-solid fa-arrows-to-circle">{{ $t('Summary') }}</v-btn>
</v-btn-toggle>
</v-col>
</v-row>
<v-row v-for="(s, i) in props.steps" v-if="!useUserPreferenceStore().deviceSettings.recipe_mergeStepOverview">
<v-col class="pa-1" cols="12" md="6">
<b v-if="s.showAsHeader">{{ i + 1 }}. {{ s.name }} </b>
<ingredients-table v-model="s.ingredients" :ingredient-factor="props.ingredientFactor"></ingredients-table>
@@ -21,7 +30,13 @@
</template>
</v-col>
</v-row>
</v-container>
<v-row v-if="useUserPreferenceStore().deviceSettings.recipe_mergeStepOverview">
<v-col class="pa-1" cols="12" md="6">
<ingredients-table v-model="mergedIngredients" :ingredient-factor="props.ingredientFactor" :show-checkbox="false"></ingredients-table>
</v-col>
</v-row>
</v-expansion-panel-text>
</v-expansion-panel>
@@ -30,10 +45,10 @@
</template>
<script setup lang="ts">
import {PropType} from 'vue'
import {Step} from "@/openapi";
import {computed, PropType, ref} from 'vue'
import {Ingredient, Step} from "@/openapi";
import IngredientsTable from "@/components/display/IngredientsTable.vue";
import StepView from "@/components/display/StepView.vue";
import {useUserPreferenceStore} from "@/stores/UserPreferenceStore.ts";
const props = defineProps({
steps: {
@@ -46,6 +61,70 @@ const props = defineProps({
},
})
const showMergedIngredients = ref(false)
const mergedIngredients = computed(() => {
// Function to collect all ingredients from recipe steps
const getAllIngredients = () => {
const ingredients: Array<Ingredient> = [];
// Add ingredients from steps
props.steps.forEach(step => {
step.ingredients.forEach(ingredient => {
if (ingredient.food && !ingredient.isHeader && !ingredient.noAmount) {
ingredients.push(ingredient);
}
});
// Add ingredients from step recipes if they exist
if (step.stepRecipeData) {
step.stepRecipeData.steps?.forEach((subStep: Step) => {
subStep.ingredients.forEach((ingredient: Ingredient) => {
if (ingredient.food && !ingredient.isHeader && !ingredient.noAmount) {
ingredients.push(ingredient);
}
});
});
}
});
return ingredients;
};
// Get all ingredients
const allIngredients = getAllIngredients();
// Create a map to group and sum ingredients by food and unit
const groupedIngredients = new Map<string, Ingredient>();
allIngredients.forEach(ingredient => {
if (!ingredient.food || !ingredient.unit) return;
// Create a unique key for food-unit combination
const key = `${ingredient.food.id}-${ingredient.unit.id}`;
if (groupedIngredients.has(key)) {
// If this food-unit combination already exists, sum the amounts
const existingIngredient = groupedIngredients.get(key)!;
existingIngredient.amount += ingredient.amount;
} else {
// Create a new entry with the adjusted amount
const clonedIngredient = {...ingredient};
groupedIngredients.set(key, clonedIngredient);
}
});
// Convert the map back to an array
const result = Array.from(groupedIngredients.values());
// Sort alphabetically by food name
return result.sort((a, b) => {
const foodNameA = a.food?.name.toLowerCase() || '';
const foodNameB = b.food?.name.toLowerCase() || '';
return foodNameA.localeCompare(foodNameB);
});
})
</script>

View File

@@ -0,0 +1,178 @@
<template>
<v-row justify="space-between" dense>
<v-col cols="6">
<v-card :loading="loading" variant="outlined">
<v-card-text>
<v-treeview
v-model:activated="activeObjs"
return-object
activatable
rounded
indent-lines
hide-actions
density="compact"
open-all
item-title="name"
:items="objTree"
:disabled="loading">
<template v-slot:append="{ item, depth, isFirst, isLast }">
<v-icon icon="fa-solid fa-location-crosshairs" v-if="item.id == editingObj.id!"></v-icon>
</template>
</v-treeview>
</v-card-text>
</v-card>
</v-col>
<v-col cols="6">
<v-card v-if="activeObjs.length == 1" :title="activeObjs[0].name" :prepend-icon="genericModel.model.icon" variant="outlined">
<v-card-text>
<v-label>{{$t('AddChild')}}</v-label>
<model-select :model="genericModel.model.name" v-model="addChildObj" allow-create>
<template #append>
<v-btn color="save" icon="$save" :disabled="addChildObj == undefined" @click="moveObject(addChildObj,activeObjs[0].id!); addChildObj = undefined"></v-btn>
</template>
</model-select>
<v-label>{{$t('Parent')}}</v-label>
<model-select :model="genericModel.model.name" v-model="setParentObj" allow-create>
<template #append>
<v-btn color="save" icon="$save" :disabled="setParentObj == undefined" @click="moveObject(activeObjs[0], setParentObj.id!); setParentObj = undefined"></v-btn>
</template>
</model-select>
<v-btn @click="moveObject(activeObjs[0],0)" class="mt-2" color="warning" prepend-icon="fa-solid fa-link-slash" block>
{{$t('RemoveParent')}}
</v-btn>
<v-btn block prepend-icon="$edit" color="info" class="mt-4"
:to="{name: 'ModelEditPage' , params: {model: genericModel.model.name, id: activeObjs[0].id }}"
v-if="activeObjs[0].id != editingObj.id!">
{{ $t('Edit') }}
</v-btn>
<v-btn block prepend-icon="$search" color="success" class="mt-4 mb-10"
:to="searchLink" target="_blank"
v-if="searchLink">
{{ $t('Recipes') }}
</v-btn>
</v-card-text>
</v-card>
</v-col>
</v-row>
</template>
<script setup lang="ts">
import {computed, onMounted, PropType, ref} from "vue";
import ModelSelect from "@/components/inputs/ModelSelect.vue";
import {ErrorMessageType, useMessageStore} from "@/stores/MessageStore.ts";
import {EditorSupportedModels, EditorSupportedTypes, getGenericModelFromString} from "@/types/Models.ts";
import {useI18n} from "vue-i18n";
type Tree<T> = T & { children: Tree<T>[] };
const props = defineProps({
model: {type: String as PropType<EditorSupportedModels>, required: true},
})
const editingObj = defineModel<EditorSupportedTypes>({required: true})
const t = useI18n()
/**
* compute tree structure based on object list
*/
const objTree = computed(() => {
return buildTreeDFS(objList.value)
})
/**
* link to search for recipes using the selected object
*/
const searchLink = computed(() => {
if (activeObjs.value.length == 1) {
if (props.model == 'Keyword') {
return {name: 'SearchPage', query: {keywords: activeObjs.value[0].id!}}
} else if (props.model == 'Food') {
return {name: 'SearchPage', query: {keywords: activeObjs.value[0].id!}}
}
}
return undefined
})
const loading = ref(false)
const objList = ref([] as EditorSupportedTypes[])
const activeObjs = ref([] as EditorSupportedTypes[])
const addChildObj = ref<undefined | EditorSupportedTypes>(undefined)
const setParentObj = ref<undefined | EditorSupportedTypes>(undefined)
const genericModel = ref(getGenericModelFromString(props.model, t))
onMounted(() => {
recLoadObjectTree(editingObj.value.id!, 1)
})
/**
* recursively load all objects belonging to the selected objects tree
* @param objId base object id to look for tree
* @param page page to load
*/
function recLoadObjectTree(objId: number, page: number) {
loading.value = true
genericModel.value.list({rootTree: objId, pageSize: 100}).then(response => {
objList.value = objList.value.concat(response.results)
if (response.next) {
recLoadObjectTree(objId, page + 1)
} else {
if (activeObjs.value.length == 0) {
activeObjs.value = [objTree.value.find(x => x.id! == editingObj.value.id!)]
}
loading.value = false
}
})
}
/**
* move the given obj to the desired parent and update in local data cache
* @param obj object to change parent for
* @param parentId parent id to change the object to or 0 to remove parent
*/
function moveObject(obj: EditorSupportedTypes, parentId: number) {
loading.value = true
genericModel.value.move(obj, parentId).then((r: any) => {
objList.value = []
recLoadObjectTree(editingObj.value.id!, 1)
}).catch((err: any) => {
loading.value = false
useMessageStore().addError(ErrorMessageType.UPDATE_ERROR, err)
})
}
/**
* Recursively build a tree datastructures from a DFS ordered list of objects
*/
function buildTreeDFS<T extends { id: number; parent: number | null }>(items: T[]): Tree<T>[] {
let index = 0;
function buildSubtree(parentId: number | null): Tree<T>[] {
const children: Tree<T>[] = [];
while (index < items.length && items[index].parent === parentId) {
const item = items[index++];
const node: Tree<T> = {
...item,
children: buildSubtree(item.id) // recurse immediately, consumes children in DFS order
};
children.push(node);
}
return children;
}
return buildSubtree(null); // start from roots (parent === null)
}
</script>
<style scoped>
</style>

View File

@@ -6,9 +6,8 @@
<v-list-item :to="{ name: 'ModelEditPage', params: {model: 'recipe', id: recipe.id} }" prepend-icon="$edit">
{{ $t('Edit') }}
</v-list-item>
<v-list-item prepend-icon="$mealplan" link>
<v-list-item prepend-icon="$mealplan" @click="mealPlanDialog = true">
{{ $t('Add_to_Plan') }}
<model-edit-dialog model="MealPlan" :itemDefaults="{recipe: recipe, servings: recipe.servings}"></model-edit-dialog>
</v-list-item>
<v-list-item prepend-icon="$shopping" link>
{{ $t('Add_to_Shopping') }}
@@ -30,11 +29,12 @@
</v-menu>
</v-btn>
<model-edit-dialog model="MealPlan" :itemDefaults="{recipe: recipe, servings: recipe.servings}" :close-after-create="false" :close-after-save="false" v-model="mealPlanDialog"></model-edit-dialog>
</template>
<script setup lang="ts">
import {nextTick, PropType} from 'vue'
import {nextTick, PropType, ref} from 'vue'
import {Recipe, RecipeFlat, RecipeOverview} from "@/openapi";
import ModelEditDialog from "@/components/dialogs/ModelEditDialog.vue";
import RecipeShareDialog from "@/components/dialogs/RecipeShareDialog.vue";
@@ -46,6 +46,8 @@ const props = defineProps({
size: {type: String, default: 'medium'},
})
const mealPlanDialog = ref(false)
function openPrintView() {
print()
}

View File

@@ -61,7 +61,7 @@
<div v-if="!mobile">
<vue-draggable v-model="step.ingredients" handle=".drag-handle" :on-sort="sortIngredients" :empty-insert-threshold="25" group="ingredients">
<v-row v-for="(ingredient, index) in step.ingredients" :key="ingredient.id" dense>
<v-col cols="12" class="pa-0 ma-0 text-center text-disabled">
<v-col cols="12" class="pa-0 ma-0 text-center text-disabled" v-if="ingredient.originalText">
<v-icon icon="$import" size="x-small"></v-icon>
{{ ingredient.originalText }}
</v-col>
@@ -306,6 +306,7 @@ function parseAndInsertIngredients() {
r.forEach(i => {
console.log(i)
step.value.ingredients.push({
originalText: i.value.originalText,
amount: i.value.amount,
food: i.value.food,
unit: i.value.unit,

View File

@@ -12,11 +12,22 @@
<v-card-text>
<v-form :disabled="loading">
<v-textarea :label="$t('Comment')" rows="2" v-model="editingObj.comment"></v-textarea>
<v-row dense>
<v-col cols="12" md="4">
<v-label>{{ $t('Rating') }}</v-label>
<br/>
<v-rating v-model="editingObj.rating" clearable hover density="compact"></v-rating>
</v-col>
<v-col cols="12" md="4">
<v-text-field :label="$t('Name')" v-model="editingObj.name"></v-text-field>
<v-text-field :label="$t('Code')" v-model="editingObj.code"></v-text-field>
<v-number-input :label="$t('Servings')" v-model="editingObj.servings" :precision="2"></v-number-input>
</v-col>
<v-col cols="12" md="4">
<v-date-input :label="$t('Date')" v-model="editingObj.createdAt"></v-date-input>
<v-textarea :label="$t('Comment')" v-model="editingObj.comment"></v-textarea>
</v-col>
</v-row>
</v-form>
</v-card-text>
@@ -27,19 +38,20 @@
<script setup lang="ts">
import {onMounted, PropType, watch} from "vue";
import {OpenDataFood, OpenDataVersion} from "@/openapi";
import {CookLog} from "@/openapi";
import ModelEditorBase from "@/components/model_editors/ModelEditorBase.vue";
import {useModelEditorFunctions} from "@/composables/useModelEditorFunctions";
const props = defineProps({
item: {type: {} as PropType<OpenDataVersion>, required: false, default: null},
item: {type: {} as PropType<CookLog>, required: false, default: null},
itemId: {type: [Number, String], required: false, default: undefined},
itemDefaults: {type: {} as PropType<OpenDataVersion>, required: false, default: {} as OpenDataVersion},
itemDefaults: {type: {} as PropType<CookLog>, required: false, default: {} as CookLog},
dialog: {type: Boolean, default: false}
})
const emit = defineEmits(['create', 'save', 'delete', 'close', 'changedState'])
const {setupState, deleteObject, saveObject, isUpdate, editingObjName, loading, editingObj, editingObjChanged, modelClass} = useModelEditorFunctions<OpenDataVersion>('OpenDataVersion', emit)
const {setupState, deleteObject, saveObject, isUpdate, editingObjName, loading, editingObj, editingObjChanged, modelClass} = useModelEditorFunctions<CookLog>('CookLog', emit)
/**
* watch prop changes and re-initialize editor
@@ -58,7 +70,7 @@ onMounted(() => {
/**
* component specific state setup logic
*/
function initializeEditor(){
function initializeEditor() {
setupState(props.item, props.itemId, {itemDefaults: props.itemDefaults})
}

View File

@@ -15,6 +15,7 @@
<v-tab value="food">{{ $t('Food') }}</v-tab>
<v-tab value="properties">{{ $t('Properties') }}</v-tab>
<v-tab value="conversions">{{ $t('Conversion') }}</v-tab>
<v-tab value="hierarchy">{{ $t('Hierarchy') }}</v-tab>
<v-tab value="misc">{{ $t('Miscellaneous') }}</v-tab>
</v-tabs>
</v-card-text>
@@ -83,7 +84,7 @@
</v-col>
<v-col md="6">
<!-- TODO fix card overflow invisible, overflow-visible class is not working -->
<model-select v-model="uc.baseUnit" model="Unit" hide-details></model-select>
<model-select v-model="uc.baseUnit" model="Unit" hide-details></model-select>
</v-col>
</v-row>
<v-row dense>
@@ -97,7 +98,7 @@
</v-col>
<v-col md="6">
<!-- TODO fix card overflow invisible, overflow-visible class is not working -->
<model-select v-model="uc.convertedUnit" model="Unit"></model-select>
<model-select v-model="uc.convertedUnit" model="Unit"></model-select>
</v-col>
</v-row>
</v-card-text>
@@ -105,7 +106,19 @@
</v-card>
</v-form>
<!-- TODO remove once append to body for model select is working properly -->
<v-spacer style="margin-top: 60px;"></v-spacer>
<v-spacer style="margin-top: 60px;"></v-spacer>
</v-tabs-window-item>
<v-tabs-window-item value="hierarchy">
<hierarchy-editor v-model="editingObj" :model="modelClass.model.name"></hierarchy-editor>
<v-checkbox :label="$t('substitute_siblings')" :hint="$t('substitute_siblings_help')" v-model="editingObj.substituteSiblings" persistent-hint></v-checkbox>
<v-checkbox :label="$t('substitute_children')" :hint="$t('substitute_children_help')" v-model="editingObj.substituteChildren" persistent-hint></v-checkbox>
<ModelSelect model="FoodInheritField" v-model="editingObj.inheritFields" :label="$t('InheritFields')" :hint="$t('InheritFields_help')"
mode="tags"></ModelSelect>
<ModelSelect model="FoodInheritField" v-model="editingObj.childInheritFields" :label="$t('ChildInheritFields')" :hint="$t('ChildInheritFields_help')"
mode="tags"></ModelSelect>
</v-tabs-window-item>
<v-tabs-window-item value="misc">
@@ -117,14 +130,6 @@
<v-divider class="mt-2 mb-2"></v-divider>
<ModelSelect model="Food" v-model="editingObj.substitute" :label="$t('Substitutes')" :hint="$t('substitute_help')" mode="tags"></ModelSelect>
<v-checkbox :label="$t('substitute_siblings')" :hint="$t('substitute_siblings_help')" v-model="editingObj.substituteSiblings" persistent-hint></v-checkbox>
<v-checkbox :label="$t('substitute_children')" :hint="$t('substitute_children_help')" v-model="editingObj.substituteChildren" persistent-hint></v-checkbox>
<ModelSelect model="FoodInheritField" v-model="editingObj.inheritFields" :label="$t('InheritFields')" :hint="$t('InheritFields_help')"
mode="tags"></ModelSelect>
<ModelSelect model="FoodInheritField" v-model="editingObj.childInheritFields" :label="$t('ChildInheritFields')" :hint="$t('ChildInheritFields_help')"
mode="tags"></ModelSelect>
<!-- TODO re-add reset inheritance button/api call /function (previously annotated field on food -->
<v-text-field :label="$t('Open_Data_Slug')" :hint="$t('open_data_help_text')" persistent-hint v-model="editingObj.openDataSlug" disabled></v-text-field>
</v-form>
@@ -154,6 +159,7 @@ import {useUserPreferenceStore} from "@/stores/UserPreferenceStore";
import FdcSearchDialog from "@/components/dialogs/FdcSearchDialog.vue";
import {openFdcPage} from "@/utils/fdc.ts";
import {DateTime} from "luxon";
import HierarchyEditor from "@/components/inputs/HierarchyEditor.vue";
const props = defineProps({
@@ -212,7 +218,7 @@ onMounted(() => {
/**
* component specific state setup logic
*/
function initializeEditor(){
function initializeEditor() {
setupState(props.item, props.itemId, {
newItemFunction: () => {
editingObj.value.propertiesFoodAmount = 100

View File

@@ -9,13 +9,29 @@
:is-changed="editingObjChanged"
:model-class="modelClass"
:object-name="editingObjName()">
<v-card-text class="pa-0">
<v-tabs v-model="tab" :disabled="loading" grow>
<v-tab value="keyword">{{ $t('Keyword') }}</v-tab>
<v-tab value="hierarchy">{{ $t('Hierarchy') }}</v-tab>
</v-tabs>
</v-card-text>
<v-card-text>
<v-form :disabled="loading">
<v-tabs-window v-model="tab">
<v-tabs-window-item value="keyword">
<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"></v-textarea>
<v-text-field :label="$t('Name')" v-model="editingObj.name"></v-text-field>
<v-textarea :label="$t('Description')" v-model="editingObj.description"></v-textarea>
</v-form>
</v-form>
</v-tabs-window-item>
<v-tabs-window-item value="hierarchy">
<hierarchy-editor v-model="editingObj" :model="modelClass.model.name"></hierarchy-editor>
</v-tabs-window-item>
</v-tabs-window>
</v-card-text>
</model-editor-base>
@@ -23,10 +39,11 @@
<script setup lang="ts">
import {onMounted, PropType, watch} from "vue";
import {onMounted, PropType, ref, watch} from "vue";
import {Keyword} from "@/openapi";
import ModelEditorBase from "@/components/model_editors/ModelEditorBase.vue";
import {useModelEditorFunctions} from "@/composables/useModelEditorFunctions";
import HierarchyEditor from "@/components/inputs/HierarchyEditor.vue";
const props = defineProps({
item: {type: {} as PropType<Keyword>, required: false, default: null},
@@ -38,6 +55,8 @@ const props = defineProps({
const emit = defineEmits(['create', 'save', 'delete', 'close', 'changedState'])
const {setupState, deleteObject, saveObject, isUpdate, editingObjName, loading, editingObj, editingObjChanged, modelClass} = useModelEditorFunctions<Keyword>('Keyword', emit)
const tab = ref("keyword")
/**
* watch prop changes and re-initialize editor
* required to embed editor directly into pages and be able to change item from the outside
@@ -55,7 +74,7 @@ onMounted(() => {
/**
* component specific state setup logic
*/
function initializeEditor(){
function initializeEditor() {
setupState(props.item, props.itemId, {itemDefaults: props.itemDefaults})
}

View File

@@ -63,8 +63,8 @@
</v-col>
</v-row>
<!-- <closable-help-alert :text="$t('RecipeStepsHelp')" :action-text="$t('Steps')" @click="tab='steps'"></closable-help-alert>-->
<v-btn @click="tab='steps'" class="float-right" variant="tonal" append-icon="fa-solid fa-arrow-right">{{$t('Steps')}} </v-btn>
<!-- <closable-help-alert :text="$t('RecipeStepsHelp')" :action-text="$t('Steps')" @click="tab='steps'"></closable-help-alert>-->
<v-btn @click="tab='steps'" class="float-right" variant="tonal" append-icon="fa-solid fa-arrow-right">{{ $t('Steps') }}</v-btn>
</v-form>
</v-tabs-window-item>
@@ -77,12 +77,19 @@
</v-row>
<v-row>
<v-col class="text-center">
<v-btn-group density="compact">
<v-btn-group density="compact" divided border>
<v-btn color="success" prepend-icon="fa-solid fa-plus" @click="addStep()">{{ $t('Add_Step') }}</v-btn>
<v-btn color="warning" @click="dialogStepManager = true">
<v-btn color="warning" @click="dialogStepManager = true" :disabled="editingObj.steps.length < 2">
<v-icon icon="fa-solid fa-arrow-down-1-9"></v-icon>
</v-btn>
<v-btn prepend-icon="fa-solid fa-maximize" @click="handleSplitAllSteps" :disabled="editingObj.steps.length < 1"><span
v-if="!mobile">{{ $t('Split') }}</span></v-btn>
<v-btn prepend-icon="fa-solid fa-minimize" @click="handleMergeAllSteps" :disabled="editingObj.steps.length < 2"><span
v-if="!mobile">{{ $t('Merge') }}</span></v-btn>
</v-btn-group>
</v-col>
</v-row>
@@ -100,17 +107,26 @@
v-model="editingObj.showIngredientOverview"></v-checkbox>
<v-text-field :label="$t('Imported_From')" v-model="editingObj.sourceUrl"></v-text-field>
<v-checkbox :label="$t('Private_Recipe')" persistent-hint v-model="editingObj._private"></v-checkbox>
<model-select mode="tags" model="User" :label="$t('Private_Recipe')" :hint="$t('Private_Recipe_Help')" persistent-hint v-model="editingObj.shared"
<v-checkbox :label="$t('Private_Recipe')" persistent-hint :hint="$t('Private_Recipe_Help')" v-model="editingObj._private"></v-checkbox>
<model-select mode="tags" model="User" :label="$t('Share')" persistent-hint v-model="editingObj.shared"
append-to-body v-if="editingObj._private"></model-select>
<div class="mt-2" v-if="editingObj.filePath">
{{ $t('ExternalRecipe') }}
<v-text-field readonly v-model="editingObj.filePath"></v-text-field>
<v-btn prepend-icon="$delete" color="error" :loading="loading">{{ $t('delete_title', {type: $t('ExternalRecipe')}) }}
<delete-confirm-dialog :object-name="editingObj.filePath" :model-name="$t('ExternalRecipe')" @delete="deleteExternalFile()"></delete-confirm-dialog>
</v-btn>
</div>
</v-form>
</v-tabs-window-item>
</v-tabs-window>
</v-card-text>
<v-card-text v-if="isSpaceAtRecipeLimit(useUserPreferenceStore().activeSpace)">
<v-alert color="warning" icon="fa-solid fa-triangle-exclamation">
{{$t('SpaceLimitReached')}}
{{ $t('SpaceLimitReached') }}
<v-btn color="success" variant="flat" :to="{name: 'SpaceSettings'}">{{ $t('SpaceSettings') }}</v-btn>
</v-alert>
</v-card-text>
@@ -138,7 +154,7 @@
<script setup lang="ts">
import {onMounted, PropType, ref, shallowRef, watch} from "vue";
import {Ingredient, Recipe, Step} from "@/openapi";
import {ApiApi, Ingredient, Recipe, Step} from "@/openapi";
import ModelEditorBase from "@/components/model_editors/ModelEditorBase.vue";
import {useModelEditorFunctions} from "@/composables/useModelEditorFunctions";
import ModelSelect from "@/components/inputs/ModelSelect.vue";
@@ -151,7 +167,9 @@ import ClosableHelpAlert from "@/components/display/ClosableHelpAlert.vue";
import {useDisplay} from "vuetify";
import {isSpaceAtRecipeLimit} from "@/utils/logic_utils";
import {useUserPreferenceStore} from "@/stores/UserPreferenceStore";
import SpaceSettings from "@/components/settings/SpaceSettings.vue";
import {mergeAllSteps, splitAllSteps} from "@/utils/step_utils.ts";
import DeleteConfirmDialog from "@/components/dialogs/DeleteConfirmDialog.vue";
import {ErrorMessageType, useMessageStore} from "@/stores/MessageStore.ts";
const props = defineProps({
@@ -188,7 +206,7 @@ onMounted(() => {
/**
* component specific state setup logic
*/
function initializeEditor(){
function initializeEditor() {
setupState(props.item, props.itemId, {
newItemFunction: () => {
editingObj.value.steps = [] as Step[]
@@ -249,6 +267,33 @@ function deleteStepAtIndex(index: number) {
editingObj.value.steps.splice(index, 1)
}
function handleMergeAllSteps(): void {
if (editingObj.value.steps) {
mergeAllSteps(editingObj.value.steps)
}
}
function handleSplitAllSteps(): void {
if (editingObj.value.steps) {
splitAllSteps(editingObj.value.steps, '\n')
}
}
/**
* deletes the external file for the recipe
*/
function deleteExternalFile() {
let api = new ApiApi()
loading.value = true
api.apiRecipeDeleteExternalPartialUpdate({id: editingObj.value.id!, patchedRecipe: editingObj.value}).then(r => {
editingObj.value = r
}).catch(err => {
useMessageStore().addError(ErrorMessageType.DELETE_ERROR, err)
}).finally(() => {
loading.value = false
})
}
</script>
<style scoped>

View File

@@ -25,7 +25,9 @@ export function useNavigation() {
TANDOOR_PLUGINS.forEach(plugin => {
plugin.navigationDrawer.forEach(navEntry => {
let navEntryCopy = Object.assign({}, navEntry)
navEntryCopy.title = t(navEntryCopy.title)
if ('title' in navEntryCopy) {
navEntryCopy.title = t(navEntryCopy.title)
}
navigation.push(navEntryCopy)
})
})
@@ -44,7 +46,9 @@ export function useNavigation() {
TANDOOR_PLUGINS.forEach(plugin => {
plugin.bottomNavigation.forEach(navEntry => {
let navEntryCopy = Object.assign({}, navEntry)
navEntryCopy.title = t(navEntryCopy.title)
if ('title' in navEntryCopy) {
navEntryCopy.title = t(navEntryCopy.title)
}
navigation.push(navEntryCopy)
})
})
@@ -80,7 +84,9 @@ export function useNavigation() {
TANDOOR_PLUGINS.forEach(plugin => {
plugin.userNavigation.forEach(navEntry => {
let navEntryCopy = Object.assign({}, navEntry)
navEntryCopy.title = t(navEntryCopy.title)
if ('title' in navEntryCopy) {
navEntryCopy.title = t(navEntryCopy.title)
}
navigation.push(navEntryCopy)
})
})

View File

@@ -2,6 +2,7 @@
"API_Browser": "",
"API_Documentation": "",
"Add": "",
"AddChild": "",
"AddFoodToShopping": "",
"AddToShopping": "",
"Add_Servings_to_Shopping": "",
@@ -19,6 +20,10 @@
"Auto_Planner": "",
"Automate": "",
"Automation": "",
"BatchDeleteConfirm": "",
"BatchDeleteHelp": "",
"BatchEdit": "",
"BatchEditUpdatingItemsCount": "",
"Bookmarklet": "",
"Books": "",
"CREATE_ERROR": "",
@@ -61,11 +66,13 @@
"DelayUntil": "",
"Delete": "",
"DeleteShoppingConfirm": "",
"DeleteSomething": "",
"Delete_Food": "",
"Delete_Keyword": "",
"Description": "",
"Disable_Amount": "",
"Documentation": "",
"DontChange": "",
"Download": "",
"Drag_Here_To_Delete": "",
"Edit": "",
@@ -105,6 +112,7 @@
"Hide_Keywords": "",
"Hide_Recipes": "",
"Hide_as_header": "",
"Hierarchy": "",
"Icon": "",
"IgnoreAccents": "",
"IgnoreAccentsHelp": "",
@@ -144,6 +152,7 @@
"Log_Recipe_Cooking": "",
"Make_Header": "",
"Make_Ingredient": "",
"ManageSubscription": "",
"Manage_Books": "",
"Meal_Plan": "",
"Meal_Plan_Days": "",
@@ -151,6 +160,7 @@
"Meal_Type_Required": "",
"Meal_Types": "",
"Merge": "",
"MergeAutomateHelp": "",
"Merge_Keyword": "",
"Message": "",
"MissingProperties": "",
@@ -175,6 +185,7 @@
"New_Unit": "",
"Next_Day": "",
"Next_Period": "",
"No": "",
"NoCategory": "",
"NoUnit": "",
"No_ID": "",
@@ -208,6 +219,8 @@
"Previous_Day": "",
"Previous_Period": "",
"Print": "",
"Private": "",
"Private_Recipe_Help": "",
"Protected": "",
"Proteins": "",
"Quick actions": "",
@@ -222,7 +235,9 @@
"Recipes": "",
"Recipes_In_Import": "",
"Recipes_per_page": "",
"RemoveAllType": "",
"RemoveFoodFromShopping": "",
"RemoveParent": "",
"Remove_nutrition_recipe": "",
"Reset": "",
"Reset_Search": "",
@@ -253,6 +268,7 @@
"Single": "",
"Size": "",
"Sort_by_new": "",
"Space": "",
"Starting_Day": "",
"StartsWith": "",
"StartsWithHelp": "",
@@ -292,6 +308,7 @@
"Valid Until": "",
"View": "",
"View_Recipes": "",
"Visibility": "",
"Waiting": "",
"Warning": "",
"Warning_Delete_Supermarket_Category": "",
@@ -299,6 +316,7 @@
"Week": "",
"Week_Numbers": "",
"Year": "",
"Yes": "",
"add_keyword": "",
"additional_options": "",
"advanced": "",

View File

@@ -2,6 +2,7 @@
"API_Browser": "",
"API_Documentation": "",
"Add": "Добави",
"AddChild": "",
"AddFoodToShopping": "Добавете {food} към списъка си за пазаруване",
"AddToShopping": "Добавяне към списъка за пазаруване",
"Add_Servings_to_Shopping": "Добавете {servings} порции към Пазаруване",
@@ -19,6 +20,10 @@
"Auto_Planner": "Автоматичен плановик",
"Automate": "Автоматизация",
"Automation": "Автоматизация",
"BatchDeleteConfirm": "",
"BatchDeleteHelp": "",
"BatchEdit": "",
"BatchEditUpdatingItemsCount": "",
"Bookmarklet": "Книжен пазар",
"Books": "Книги",
"CREATE_ERROR": "",
@@ -58,11 +63,13 @@
"DelayUntil": "Забавяне до",
"Delete": "Изтрий",
"DeleteShoppingConfirm": "Сигурни ли сте, че искате да премахнете цялата {food} от списъка за пазаруване?",
"DeleteSomething": "",
"Delete_Food": "Изтриване на храна",
"Delete_Keyword": "Изтриване на ключова дума",
"Description": "Описание",
"Disable_Amount": "Деактивиране на сумата",
"Documentation": "Документация",
"DontChange": "",
"Download": "Изтегляне",
"Drag_Here_To_Delete": "Плъзнете тук, за да изтриете",
"Edit": "Редактиране",
@@ -102,6 +109,7 @@
"Hide_Keywords": "Скриване на ключова дума",
"Hide_Recipes": "Скриване на рецепти",
"Hide_as_header": "Скриване като заглавка",
"Hierarchy": "",
"Icon": "Икона",
"IgnoreAccents": "",
"IgnoreAccentsHelp": "",
@@ -139,6 +147,7 @@
"Log_Recipe_Cooking": "Дневник на Рецепта за готвене",
"Make_Header": "Направете заглавие",
"Make_Ingredient": "Направете съставка",
"ManageSubscription": "",
"Manage_Books": "Управление на Книги",
"Meal_Plan": "План на хранене",
"Meal_Plan_Days": "Бъдещи планове за хранене",
@@ -146,6 +155,7 @@
"Meal_Type_Required": "Изисква се вид хранене",
"Meal_Types": "Видове хранене",
"Merge": "Обединяване",
"MergeAutomateHelp": "",
"Merge_Keyword": "Обединяване на ключова дума",
"MissingProperties": "",
"Month": "Месец",
@@ -168,6 +178,7 @@
"New_Unit": "Нова единица",
"Next_Day": "Следващия ден",
"Next_Period": "Следващ период",
"No": "",
"NoCategory": "Няма избрана категория.",
"NoUnit": "",
"No_ID": "Идентификатора не е намерен, не може да се изтрие.",
@@ -201,6 +212,8 @@
"Previous_Day": "Предишен ден",
"Previous_Period": "Предишен период",
"Print": "Печат",
"Private": "",
"Private_Recipe_Help": "",
"Protected": "Защитен",
"Proteins": "Протеини (белтъчини)",
"Quick actions": "Бързи действия",
@@ -215,7 +228,9 @@
"Recipes": "Рецепти",
"Recipes_In_Import": "Рецепти във вашия файл за импортиране",
"Recipes_per_page": "Рецепти на страница",
"RemoveAllType": "",
"RemoveFoodFromShopping": "Премахнете {food} от списъка си за пазаруване",
"RemoveParent": "",
"Remove_nutrition_recipe": "Изтрийте хранителните стойности от рецептата",
"Reset": "Нулиране",
"Reset_Search": "Нулиране на търсенето",
@@ -246,6 +261,7 @@
"Single": "Единичен",
"Size": "Размер",
"Sort_by_new": "Сортиране по ново",
"Space": "",
"Starting_Day": "Начален ден от седмицата",
"StartsWith": "",
"StartsWithHelp": "",
@@ -283,6 +299,7 @@
"User": "потребител",
"View": "Изглед",
"View_Recipes": "Вижте рецепти",
"Visibility": "",
"Waiting": "Очакване",
"Warning": "Внимание",
"Warning_Delete_Supermarket_Category": "Изтриването на категория супермаркет ще изтрие и всички връзки с храни. Сигурен ли си?",
@@ -290,6 +307,7 @@
"Week": "Седмица",
"Week_Numbers": "Номера на седмиците",
"Year": "Година",
"Yes": "",
"add_keyword": "Добавяне на ключова дума",
"additional_options": "Допълнителни настройки",
"advanced": "Разширено",

View File

@@ -4,6 +4,7 @@
"API_Documentation": "",
"Account": "Compte",
"Add": "Afegir",
"AddChild": "",
"AddFoodToShopping": "Afegeix {food} a la llista de la compra",
"AddToShopping": "Afegir a la llista de la compra",
"Add_Servings_to_Shopping": "Afegir {servings} racions a la compra",
@@ -26,6 +27,10 @@
"Automate": "Automatitzar",
"Automation": "Automatizació",
"Back": "Enrere",
"BatchDeleteConfirm": "",
"BatchDeleteHelp": "",
"BatchEdit": "",
"BatchEditUpdatingItemsCount": "",
"Bookmarklet": "Marcadors",
"Books": "Llibres",
"CREATE_ERROR": "",
@@ -90,6 +95,7 @@
"DelayUntil": "Endarrerir fins",
"Delete": "Eliminar",
"DeleteShoppingConfirm": "Segur que vols eliminar tot el/la {food} de la llista de la compra?",
"DeleteSomething": "",
"Delete_All": "Eliminar tot",
"Delete_Food": "Eliminar Aliment",
"Delete_Keyword": "Esborreu paraula clau",
@@ -99,6 +105,7 @@
"Disable_Amount": "Deshabiliteu quantitat",
"Disabled": "Desactivat",
"Documentation": "Documentació",
"DontChange": "",
"Download": "Descarregar",
"Drag_Here_To_Delete": "Arrossega aquí per a eliminar",
"Edit": "Editar",
@@ -146,6 +153,7 @@
"Hide_Keywords": "Amagueu paraula clau",
"Hide_Recipes": "Amagueu receptes",
"Hide_as_header": "Amagueu com a títol",
"Hierarchy": "",
"Hour": "Hora",
"Hours": "Hores",
"Icon": "Icona",
@@ -194,6 +202,7 @@
"Logo": "Logotip",
"Make_Header": "Establiu capçalera",
"Make_Ingredient": "Establiu ingredient",
"ManageSubscription": "",
"Manage_Books": "Gestioneu els llibres",
"Manage_Emails": "Administrar correus",
"Meal_Plan": "Pla d'àpats",
@@ -202,6 +211,7 @@
"Meal_Type_Required": "El tipus és obligatori",
"Meal_Types": "Tipus de menjars",
"Merge": "Unificar",
"MergeAutomateHelp": "",
"Merge_Keyword": "Fusioneu paraula clau",
"Message": "Missatge",
"MissingProperties": "",
@@ -232,6 +242,7 @@
"New_Unit": "Nova unitat",
"Next_Day": "Següent dia",
"Next_Period": "Període següent",
"No": "",
"NoCategory": "No s'ha seleccionat categoria.",
"NoMoreUndo": "No hi ha canvis per desar.",
"NoUnit": "",
@@ -272,6 +283,7 @@
"Previous_Day": "Dia Anterior",
"Previous_Period": "Període anterior",
"Print": "Imprimir",
"Private": "",
"Private_Recipe": "Recepta privada",
"Private_Recipe_Help": "Només tu i la gent amb qui l'has compartit podran veure aquesta recepta.",
"Properties": "Propietats",
@@ -293,7 +305,9 @@
"Recipes": "Receptes",
"Recipes_In_Import": "Receptes al fitxer d'importació",
"Recipes_per_page": "Receptes per pàgina",
"RemoveAllType": "",
"RemoveFoodFromShopping": "Elimina {food} de la llista de la compra",
"RemoveParent": "",
"Remove_nutrition_recipe": "Esborreu nutrició de la recepta",
"Reset": "Restablir",
"Reset_Search": "Reinicieu la cerca",
@@ -332,6 +346,7 @@
"Size": "Mida",
"Social_Authentication": "Identificació amb Xarxes Socials",
"Sort_by_new": "Ordenar a partir del més nou",
"Space": "",
"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.",
"StartDate": "Data d'inici",
@@ -390,6 +405,7 @@
"Valid Until": "Vàlid fins",
"View": "Mostrar",
"View_Recipes": "Mostreu les receptes",
"Visibility": "",
"Waiting": "Esperant",
"Warning": "Advertència",
"Warning_Delete_Supermarket_Category": "Si suprimiu una categoria de supermercat, també se suprimiran totes les relacions alimentàries. N'estàs segur?",
@@ -398,6 +414,7 @@
"Week_Numbers": "Números de la setmana",
"Welcome": "Benvingut/da",
"Year": "Any",
"Yes": "",
"add_keyword": "Afegir Paraula Clau",
"additional_options": "Opcions addicionals",
"advanced": "Avançat",

View File

@@ -4,6 +4,7 @@
"API_Documentation": "",
"Account": "Účet",
"Add": "Přidat",
"AddChild": "",
"AddFoodToShopping": "Přidat {food} na váš nákupní seznam",
"AddToShopping": "Přidat do nákupního seznamu",
"Add_Servings_to_Shopping": "Přidat {servings} porce na nákupní seznam",
@@ -26,6 +27,10 @@
"Automate": "Automatizovat",
"Automation": "Automatizace",
"Back": "Zpět",
"BatchDeleteConfirm": "",
"BatchDeleteHelp": "",
"BatchEdit": "",
"BatchEditUpdatingItemsCount": "",
"Bookmarklet": "Skript v záložce",
"Books": "Kuchařky",
"CREATE_ERROR": "",
@@ -89,6 +94,7 @@
"DelayUntil": "Odložit do",
"Delete": "Smazat",
"DeleteShoppingConfirm": "Jste si jistí, že chcete odstranit všechno {food} z nákupního seznamu?",
"DeleteSomething": "",
"Delete_All": "Smazat vše",
"Delete_Food": "Smazat potravinu",
"Delete_Keyword": "Smazat štítek",
@@ -98,6 +104,7 @@
"Disable_Amount": "Skrýt množství",
"Disabled": "Deaktivované",
"Documentation": "Dokumentace",
"DontChange": "",
"Download": "Stáhnout",
"Drag_Here_To_Delete": "Přesunutím sem smazat",
"Edit": "Upravit",
@@ -145,6 +152,7 @@
"Hide_Keywords": "Skrýt štítek",
"Hide_Recipes": "Skrýt recept",
"Hide_as_header": "Skryj jako nadpis",
"Hierarchy": "",
"Hour": "Hodina",
"Hours": "Hodiny",
"Icon": "Ikona",
@@ -192,6 +200,7 @@
"Logo": "Logo",
"Make_Header": "Použij jako nadpis",
"Make_Ingredient": "Použij jako ingredienci",
"ManageSubscription": "",
"Manage_Books": "Spravovat kuchařky",
"Manage_Emails": "Spravovat emaily",
"Meal_Plan": "Jídelníček",
@@ -200,6 +209,7 @@
"Meal_Type_Required": "Druh jídla je povinný",
"Meal_Types": "Druhy jídel",
"Merge": "Spojit",
"MergeAutomateHelp": "",
"Merge_Keyword": "Sloučit štítek",
"Message": "Zpráva",
"MissingProperties": "",
@@ -230,6 +240,7 @@
"New_Unit": "Nová jednotka",
"Next_Day": "Následující den",
"Next_Period": "Další období",
"No": "",
"NoCategory": "Není vybrána žádná kategorie.",
"NoUnit": "",
"No_ID": "ID nenalezeno, odstranění není možné.",
@@ -269,6 +280,7 @@
"Previous_Day": "Předchozí den",
"Previous_Period": "Předchozí období",
"Print": "Tisk",
"Private": "",
"Private_Recipe": "Soukromý recept",
"Private_Recipe_Help": "Recept můžete zobrazit pouze vy a lidé, se kterými jej sdílíte.",
"Properties": "Nutriční vlastnosti",
@@ -290,7 +302,9 @@
"Recipes": "Recepty",
"Recipes_In_Import": "Receptů v importním souboru",
"Recipes_per_page": "Receptů na stránku",
"RemoveAllType": "",
"RemoveFoodFromShopping": "Odstranit {food} z nákupního seznamu",
"RemoveParent": "",
"Remove_nutrition_recipe": "Smazat nutriční hodnoty",
"Reset": "Resetovat",
"Reset_Search": "Zrušit filtry vyhledávání",
@@ -327,6 +341,7 @@
"Size": "Velikost",
"Social_Authentication": "Přihlašování pomocí účtů sociálních sítí",
"Sort_by_new": "Seřadit od nejnovějšího",
"Space": "",
"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.",
"StartDate": "Počáteční datum",
@@ -382,6 +397,7 @@
"Valid Until": "Platné do",
"View": "Zobrazit",
"View_Recipes": "Zobrazit recepty",
"Visibility": "",
"Waiting": "Čekající",
"Warning": "Varování",
"Warning_Delete_Supermarket_Category": "Vymazáním kategorie obchodu dojde k odstranění všech vazeb na potraviny. Jste si jistí?",
@@ -390,6 +406,7 @@
"Week_Numbers": "Číslo týdne",
"Welcome": "Vítejte",
"Year": "Rok",
"Yes": "",
"add_keyword": "Přidat štítek",
"additional_options": "Rozšířené možnosti",
"advanced": "Pokročilé",

View File

@@ -4,6 +4,7 @@
"API_Documentation": "",
"Account": "Bruger",
"Add": "Tilføj",
"AddChild": "",
"AddFoodToShopping": "Tilføj {food} til indkøbsliste",
"AddToShopping": "Tilføj til indkøbsliste",
"Add_Servings_to_Shopping": "Tilføj {servings} serveringer til indkøb",
@@ -26,6 +27,10 @@
"Automate": "Automatiser",
"Automation": "Automatisering",
"Back": "Tilbage",
"BatchDeleteConfirm": "",
"BatchDeleteHelp": "",
"BatchEdit": "",
"BatchEditUpdatingItemsCount": "",
"Bookmarklet": "Bogmærke",
"Books": "Bøger",
"CREATE_ERROR": "",
@@ -90,6 +95,7 @@
"DelayUntil": "Udskyd indtil",
"Delete": "Slet",
"DeleteShoppingConfirm": "Er du sikker på at du vil fjerne {food} fra indkøbsliste?",
"DeleteSomething": "",
"Delete_All": "Slet alle",
"Delete_Food": "Slet mad",
"Delete_Keyword": "Slet nøgleord",
@@ -99,6 +105,7 @@
"Disable_Amount": "Deaktiver antal",
"Disabled": "Slået fra",
"Documentation": "Dokumentation",
"DontChange": "",
"Download": "Download",
"Drag_Here_To_Delete": "Træk herhen for at slette",
"Edit": "Rediger",
@@ -146,6 +153,7 @@
"Hide_Keywords": "Skjul nøgleord",
"Hide_Recipes": "Skjul opskrifter",
"Hide_as_header": "Skjul som rubrik",
"Hierarchy": "",
"Hour": "Time",
"Hours": "Timer",
"Icon": "Ikon",
@@ -194,6 +202,7 @@
"Logo": "Logo",
"Make_Header": "Opret rubrik",
"Make_Ingredient": "Opret ingredient",
"ManageSubscription": "",
"Manage_Books": "Administrer bøger",
"Manage_Emails": "Håndter Emails",
"Meal_Plan": "Madplan",
@@ -202,6 +211,7 @@
"Meal_Type_Required": "Måltidstype påkrævet",
"Meal_Types": "Måltidstyper",
"Merge": "Sammenflet",
"MergeAutomateHelp": "",
"Merge_Keyword": "Sammenflet nøgleord",
"Message": "Besked",
"MissingProperties": "",
@@ -232,6 +242,7 @@
"New_Unit": "Ny enhed",
"Next_Day": "Næste dag",
"Next_Period": "Næste periode",
"No": "",
"NoCategory": "Ingen kategori valgt.",
"NoMoreUndo": "Ingen ændringer at fortryde.",
"NoUnit": "",
@@ -272,6 +283,7 @@
"Previous_Day": "Forgående dag",
"Previous_Period": "Forgående periode",
"Print": "Udskriv",
"Private": "",
"Private_Recipe": "Privat opskrift",
"Private_Recipe_Help": "Opskriften er kun synlig for dig, og dem som den er delt med.",
"Properties": "Egenskaber",
@@ -293,7 +305,9 @@
"Recipes": "Opskrifter",
"Recipes_In_Import": "Opskrifter i din importerede fil",
"Recipes_per_page": "Opskrifter pr. side",
"RemoveAllType": "",
"RemoveFoodFromShopping": "Fjern {food} fra indkøbsliste",
"RemoveParent": "",
"Remove_nutrition_recipe": "Fjern næringsindhold fra opskrift",
"Reset": "Nulstil",
"Reset_Search": "Nulstil søgning",
@@ -332,6 +346,7 @@
"Size": "Størrelse",
"Social_Authentication": "Social authenticering",
"Sort_by_new": "Sorter efter nylige",
"Space": "",
"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.",
"StartDate": "Startdato",
@@ -390,6 +405,7 @@
"Valid Until": "Gyldig indtil",
"View": "Vis",
"View_Recipes": "Vis opskrifter",
"Visibility": "",
"Waiting": "Vente",
"Warning": "Advarsel",
"Warning_Delete_Supermarket_Category": "At slette en supermarkedskategori vil også slette alle relationer til mad. Er du sikker?",
@@ -398,6 +414,7 @@
"Week_Numbers": "Ugenumre",
"Welcome": "Velkommen",
"Year": "År",
"Yes": "",
"add_keyword": "Tilføj nøgleord",
"additional_options": "Yderligere indstillinger",
"advanced": "Avanceret",

View File

@@ -2,6 +2,9 @@
"AI": "AI",
"AIImportSubtitle": "Verwende AI um Fotos von Rezepten zu importieren.",
"API": "API",
"APIKey": "API Schlüssel",
"Summary": "Zusammenfassung",
"Structured": "Strukturiert",
"API_Browser": "API Browser",
"API_Documentation": "API Dokumentation",
"AccessTokenHelp": "Zugriffsschlüssel für die REST Schnittstelle.",
@@ -11,6 +14,7 @@
"Activity": "Aktivität",
"Add": "Hinzufügen",
"AddAll": "Alle Hinzufügen",
"AddChild": "Kind hinzufügen",
"AddFilter": "Filter Hinzufügen",
"AddFoodToShopping": "Fügen Sie {food} zur Einkaufsliste hinzu",
"AddMany": "Mehrere Hinzufügen",
@@ -46,6 +50,10 @@
"BaseUnit": "Basiseinheit",
"BaseUnitHelp": "Optionale Standardeinheit zur automatischen Umrechnung",
"Basics": "Grundlagen",
"BatchDeleteConfirm": "Möchtest du alle angezeigten Objekte löschen? Dies kann nicht rückgängig gemacht werden!",
"BatchDeleteHelp": "Wenn ein Objekt nicht gelöscht werden kann, wird es noch irgendwo verwendet. ",
"BatchEdit": "Massenbearbeitung",
"BatchEditUpdatingItemsCount": "Bearbeite {count} {type}",
"Book": "Buch",
"Bookmarklet": "Lesezeichen",
"BookmarkletHelp1": "Schiebe den Knopf in deine Lesezeichenleiste",
@@ -132,6 +140,7 @@
"Delete": "Löschen",
"DeleteConfirmQuestion": "Sind Sie sicher das Sie dieses Objekt löschen wollen?",
"DeleteShoppingConfirm": "Möchten Sie wirklich alle {food} von der Einkaufsliste entfernen?",
"DeleteSomething": "{item} löschen",
"Delete_All": "Alles löschen",
"Delete_Food": "Lebensmittel löschen",
"Delete_Keyword": "Schlagwort löschen",
@@ -144,6 +153,7 @@
"Disable_Amount": "Menge deaktivieren",
"Disabled": "Deaktiviert",
"Documentation": "Dokumentation",
"DontChange": "Nicht ändern",
"Down": "Runter",
"Download": "Herunterladen",
"DragToUpload": "Drag & Drop oder Klicken zum Auswählen",
@@ -212,6 +222,7 @@
"Hide_Keywords": "Schlagwort verstecken",
"Hide_Recipes": "Rezepte verstecken",
"Hide_as_header": "Keine Überschrift",
"Hierarchy": "Hierarchie",
"History": "Verlauf",
"HostedFreeVersion": "Sie verwenden die kostenlose Testversion von Tandoor",
"Hour": "Stunde",
@@ -274,7 +285,7 @@
"Logout": "Ausloggen",
"Make_Header": "In Überschrift wandeln",
"Make_Ingredient": "In Zutat umwandeln",
"ManageSubscription": "Tarfi verwalten",
"ManageSubscription": "Tarif verwalten",
"Manage_Books": "Bücher verwalten",
"Manage_Emails": "E-Mails verwalten",
"MealPlanHelp": "Ein Speiseplan ist ein Eintrag im Kalender zur Planung von Mahlzeiten. Er muss entweder ein Rezept oder einen Titel erhalten und kann mit der Einkaufsliste verknüpft werden. ",
@@ -286,13 +297,14 @@
"Meal_Type_Required": "Mahlzeitentyp ist erforderlich",
"Meal_Types": "Mahlzeiten",
"Merge": "Zusammenführen",
"MergeAutomateHelp": "Erstelle eine Automatisierung ,die auch zukünftig erstellte Objekte mit diesem Namen durch das gewählte Objekt ersetzt.",
"MergeAutomateHelp": "Erstelle eine Automatisierung, die auch zukünftig erstellte Objekte mit diesem Namen durch das gewählte Objekt ersetzt.",
"Merge_Keyword": "Schlagworte zusammenführen",
"Message": "Nachricht",
"Messages": "Nachrichten",
"Miscellaneous": "Sonstige",
"MissingConversion": "Fehlende Umrechnung",
"MissingProperties": "Fehlende Eigenschaften",
"Model": "Modell",
"ModelSelectResultsHelp": "Für mehr Ergebnisse suchen",
"Monday": "Montag",
"Month": "Monat",
@@ -325,6 +337,7 @@
"Next": "Weiter",
"Next_Day": "Tag vor",
"Next_Period": "nächster Zeitraum",
"No": "Nein",
"NoCategory": "Ohne Kategorie",
"NoMoreUndo": "Rückgängig: Keine Änderungen",
"NoUnit": "Keine Einheit",
@@ -377,8 +390,9 @@
"Previous_Day": "Tag zurück",
"Previous_Period": "voriger Zeitraum",
"Print": "Drucken",
"Private": "Privat",
"Private_Recipe": "Privates Rezept",
"Private_Recipe_Help": "Dieses Rezept ist nur für dich und Personen mit denen du es geteilt hast sichtbar.",
"Private_Recipe_Help": "Private Rezepte sind nur für dich und Personen mit denen du Sie geteilt hast sichtbar.",
"Profile": "Profil",
"Properties": "Eigenschaften",
"PropertiesFoodHelp": "Eigenschaften können für Rezepte und Lebensmittel erfasst werden. Eigenschaften von Lebensmitteln werden automatisch entsprechend der im Rezept enthaltenen Menge berechnet.",
@@ -411,7 +425,9 @@
"Recipes_In_Import": "Rezepte in deiner importierten Datei",
"Recipes_per_page": "Rezepte pro Seite",
"Remove": "Entfernen",
"RemoveAllType": "Alle {type} entfernen",
"RemoveFoodFromShopping": "{food} von der Einkaufsliste löschen",
"RemoveParent": "Eltern entfernen",
"Remove_nutrition_recipe": "Nährwerte aus Rezept löschen",
"Reset": "Zurücksetzen",
"ResetHelp": "Hilfe Zurücksetzen",
@@ -473,6 +489,7 @@
"Source": "Quelle",
"SourceImportHelp": "Importiere JSON im schema.org/recipe format oder eine HTML Seite mit json+ld Rezept bzw. microdata.",
"SourceImportSubtitle": "Importiere JSON oder HTML manuell.",
"Space": "Space",
"SpaceLimitExceeded": "Dein Space hat ein Limit überschritten, manche Funktionen wurden eingeschränkt.",
"SpaceLimitReached": "Dieser Space hat ein Limit erreicht. Es können keine neuen Objekte von diesem Typ angelegt werden.",
"SpaceMemberHelp": "Füge Benutzer hinzu indem du Einladungen erstellst und Sie an die gewünschte Person sendest.",
@@ -498,10 +515,12 @@
"Storage": "Externer Speicher",
"StorageHelp": "Externe Speicherorte an denen Rezepte als Dateien (Foto/PDF) abgelegt und mit Tandor syncronisiert werden können.",
"StoragePasswordTokenHelp": "Das hinterlegte Passwort/Token kann nicht angezeigt werden. Es wird nur aktualisiert wenn etwas neues in das Feld eingegeben wird. ",
"Structured": "Strukturiert",
"SubstituteOnHand": "Du hast eine Alternative vorrätig.",
"Substitutes": "Alternativen",
"Success": "Erfolgreich",
"SuccessClipboard": "Einkaufsliste wurde in die Zwischenablage kopiert",
"Summary": "Zusammenfassung",
"Sunday": "Sonntag",
"Supermarket": "Supermarkt",
"SupermarketCategoriesOnly": "Nur Supermarktkategorien",
@@ -575,6 +594,7 @@
"ViewLogHelp": "Verlauf angesehener Rezepte. ",
"View_Recipes": "Rezepte Ansehen",
"Viewed": "Angesehen",
"Visibility": "Sichtbarkeit",
"Waiting": "Wartezeit",
"WaitingTime": "Wartezeit",
"WarnPageLeave": "Deine Änderungen wurden noch nicht gespeichert und gehen verloren. Seite wirklich verlassen?",
@@ -588,6 +608,7 @@
"Welcome": "Willkommen",
"WorkingTime": "Arbeitszeit",
"Year": "Jahr",
"Yes": "Ja",
"YourSpaces": "Deine Spaces",
"active": "aktiv",
"add_keyword": "Stichwort hinzufügen",

View File

@@ -4,6 +4,7 @@
"API_Documentation": "",
"Account": "Λογαριασμός",
"Add": "Προσθήκη",
"AddChild": "",
"AddFoodToShopping": "Προσθήκη του φαγητού {food} στη λίστα αγορών σας",
"AddToShopping": "Προσθήκη στη λίστα αγορών",
"Add_Servings_to_Shopping": "Προσθήκη {servings} μερίδων στις αγορές",
@@ -26,6 +27,10 @@
"Automate": "Αυτοματοποίηση",
"Automation": "Αυτοματισμός",
"Back": "Πίσω",
"BatchDeleteConfirm": "",
"BatchDeleteHelp": "",
"BatchEdit": "",
"BatchEditUpdatingItemsCount": "",
"Bookmarklet": "Bookmarklet",
"Books": "Βιβλία",
"CREATE_ERROR": "",
@@ -90,6 +95,7 @@
"DelayUntil": "Καθυστέρηση μέχρι",
"Delete": "Διαγραφή",
"DeleteShoppingConfirm": "Θέλετε σίγουρα να αφαιρέσετε τα {food} από τη λίστα αγορών;",
"DeleteSomething": "",
"Delete_All": "Διαγραφή όλων",
"Delete_Food": "Διαγραφή φαγητού",
"Delete_Keyword": "Διαγραφή λέξης-κλειδί",
@@ -99,6 +105,7 @@
"Disable_Amount": "Απενεργοποίηση ποσότητας",
"Disabled": "Απενεροποιημένο",
"Documentation": "Τεκμηρίωση",
"DontChange": "",
"Download": "Λήψη",
"Drag_Here_To_Delete": "Σύρετε εδώ για διαγραφή",
"Edit": "Τροποποίηση",
@@ -146,6 +153,7 @@
"Hide_Keywords": "Απόκρυψη λέξης-κλειδί",
"Hide_Recipes": "Απόκρυψη συνταγών",
"Hide_as_header": "Απόκρυψη ως κεφαλίδα",
"Hierarchy": "",
"Hour": "Ώρα",
"Hours": "Ώρες",
"Icon": "Εικονίδιο",
@@ -194,6 +202,7 @@
"Logo": "Λογότυπο",
"Make_Header": "Δημιουργία κεφαλίδας",
"Make_Ingredient": "Δημιουργία υλικού",
"ManageSubscription": "",
"Manage_Books": "Διαχείριση βιβλίων",
"Manage_Emails": "Διαχείριση email",
"Meal_Plan": "Πρόγραμμα γευμάτων",
@@ -202,6 +211,7 @@
"Meal_Type_Required": "Το είδος του γεύματος είναι απαραίτητο",
"Meal_Types": "Είδη γευμάτων",
"Merge": "Συγχώνευση",
"MergeAutomateHelp": "",
"Merge_Keyword": "Συγχώνευση λέξης-κλειδί",
"Message": "Μήνυμα",
"MissingProperties": "",
@@ -232,6 +242,7 @@
"New_Unit": "Νέα μονάδα μέτρησης",
"Next_Day": "Επόμενη μέρα",
"Next_Period": "Επόμενη περίοδος",
"No": "",
"NoCategory": "Δεν έχει επιλεγεί κατηγορία.",
"NoMoreUndo": "Δεν υπάρχουν αλλαγές για ανέρεση.",
"NoUnit": "",
@@ -272,6 +283,7 @@
"Previous_Day": "Προηγούμενη μέρα",
"Previous_Period": "Προηγούμενη περίοδος",
"Print": "Εκτύπωση",
"Private": "",
"Private_Recipe": "Ιδιωτική συνταγή",
"Private_Recipe_Help": "Η συνταγή είναι ορατή μόνο σε εσάς και στα άτομα με τα οποία την μοιράζεστε.",
"Properties": "Ιδιότητες",
@@ -293,7 +305,9 @@
"Recipes": "Συνταγές",
"Recipes_In_Import": "Συνταγές στο αρχείο εισαγωγής",
"Recipes_per_page": "Συνταγές ανά σελίδα",
"RemoveAllType": "",
"RemoveFoodFromShopping": "Αφαίρεση του φαγητού {food} από τη λίστα αγορών σας",
"RemoveParent": "",
"Remove_nutrition_recipe": "Αφαίρεση διατροφικής αξίας από τη συνταγή",
"Reset": "Επαναφορά",
"Reset_Search": "Επαναφορά αναζήτησης",
@@ -332,6 +346,7 @@
"Size": "Μέγεθος",
"Social_Authentication": "Ταυτοποίηση μέσω κοινωνικών δικτύων",
"Sort_by_new": "Ταξινόμηση κατά νέο",
"Space": "",
"Space_Cosmetic_Settings": "Ορισμένες ρυθμίσεις εμφάνισης μπορούν να αλλάξουν από τους διαχειριστές του χώρου και θα παρακάμψουν τις ρυθμίσεις πελάτη για αυτόν τον χώρο.",
"Split_All_Steps": "Διαχωρισμός όλων των γραμμών σε χωριστά βήματα.",
"StartDate": "Ημερομηνία Έναρξης",
@@ -390,6 +405,7 @@
"Valid Until": "Ισχύει έως",
"View": "Προβολή",
"View_Recipes": "Προβολή συνταγών",
"Visibility": "",
"Waiting": "Αναμονή",
"Warning": "Προειδοποίηση",
"Warning_Delete_Supermarket_Category": "Η διαγραφή μιας κατηγορίας supermarket θα διαγράψει και όλες τις σχέσεις της με φαγητά. Είστε σίγουροι;",
@@ -398,6 +414,7 @@
"Week_Numbers": "Αριθμοί εδομάδων",
"Welcome": "Καλώς ήρθατε",
"Year": "Έτος",
"Yes": "",
"add_keyword": "Προσθήκη λέξης-κλειδί",
"additional_options": "Επιπλέον επιλογές",
"advanced": "Για προχωρημένους",

View File

@@ -11,6 +11,7 @@
"Activity": "Activity",
"Add": "Add",
"AddAll": "Add all",
"AddChild": "Add child",
"AddFilter": "Add Filter",
"AddFoodToShopping": "Add {food} to your shopping list",
"AddMany": "Add Many",
@@ -44,6 +45,10 @@
"BaseUnit": "Base Unit",
"BaseUnitHelp": "Standard unit for automatic unit conversion",
"Basics": "Basics",
"BatchDeleteConfirm": "Do you want to delete all shown items? This cannot be undone!",
"BatchDeleteHelp": "If an item cannot be deleted it is used somewhere. ",
"BatchEdit": "Batch Edit",
"BatchEditUpdatingItemsCount": "Editing {count} {type}",
"Book": "Book",
"Bookmarklet": "Bookmarklet",
"BookmarkletHelp1": "Drag the following button to your bookmarks bar",
@@ -130,6 +135,7 @@
"Delete": "Delete",
"DeleteConfirmQuestion": "Are you sure you want to delete this object?",
"DeleteShoppingConfirm": "Are you sure that you want to remove all {food} from the shopping list?",
"DeleteSomething": "Delete {item}",
"Delete_All": "Delete all",
"Delete_Food": "Delete Food",
"Delete_Keyword": "Delete Keyword",
@@ -142,6 +148,7 @@
"Disable_Amount": "Disable Amount",
"Disabled": "Disabled",
"Documentation": "Documentation",
"DontChange": "Don't change",
"Down": "Down",
"Download": "Download",
"DragToUpload": "Drag and Drop or click to select",
@@ -210,6 +217,7 @@
"Hide_Keywords": "Hide Keyword",
"Hide_Recipes": "Hide Recipes",
"Hide_as_header": "Hide as header",
"Hierarchy": "Hierarchy",
"History": "History",
"HostedFreeVersion": "You are using the free version of Tandoor",
"Hour": "Hour",
@@ -291,6 +299,7 @@
"Miscellaneous": "Miscellaneous",
"MissingConversion": "Missing Conversion",
"MissingProperties": "Missing Properties",
"Model": "Model",
"ModelSelectResultsHelp": "Search for more results",
"Monday": "Monday",
"Month": "Month",
@@ -323,6 +332,7 @@
"Next": "Next",
"Next_Day": "Next Day",
"Next_Period": "Next Period",
"No": "No",
"NoCategory": "No Category",
"NoMoreUndo": "No changes to be undone.",
"NoUnit": "No Unit",
@@ -375,8 +385,9 @@
"Previous_Day": "Previous Day",
"Previous_Period": "Previous Period",
"Print": "Print",
"Private": "Private",
"Private_Recipe": "Private Recipe",
"Private_Recipe_Help": "Recipe is only shown to you and people its shared with.",
"Private_Recipe_Help": "Private recipes are only shown to you and people they are shared with.",
"Profile": "Profile",
"Properties": "Properties",
"PropertiesFoodHelp": "Properties can be added to Recipes and Foods. Properties on Foods are automatically calculated based on their amount in the recipe.",
@@ -409,7 +420,9 @@
"Recipes_In_Import": "Recipes in your import file",
"Recipes_per_page": "Recipes per Page",
"Remove": "Remove",
"RemoveAllType": "Remove all {type}",
"RemoveFoodFromShopping": "Remove {food} from your shopping list",
"RemoveParent": "Remove parent",
"Remove_nutrition_recipe": "Delete nutrition from recipe",
"Reset": "Reset",
"ResetHelp": "Reset Help",
@@ -471,6 +484,7 @@
"Source": "Source",
"SourceImportHelp": "Import JSON in schema.org/recipe format or html pages with json+ld recipe or microdata.",
"SourceImportSubtitle": "Import JSON or HTML manually.",
"Space": "Space",
"SpaceLimitExceeded": "Your space has surpassed one of its limits, some functions might be restricted.",
"SpaceLimitReached": "This Space has reached a limit. No more objects of this type can be created.",
"SpaceMemberHelp": "Add users to your space by creating an Invite Link and sending it to the person you want to add.",
@@ -496,10 +510,12 @@
"Storage": "External Storage",
"StorageHelp": "External storage locations where recipe files (image/pdf) can be stored and synced with Tandoor.",
"StoragePasswordTokenHelp": "The stored password/token will never be displayed. It is only changed if something new is entered into the field. ",
"Structured": "Structured",
"SubstituteOnHand": "You have a substitute on hand.",
"Substitutes": "Substitutes",
"Success": "Success",
"SuccessClipboard": "Shopping list copied to clipboard",
"Summary": "Summary",
"Sunday": "Sunday",
"Supermarket": "Supermarket",
"SupermarketCategoriesOnly": "Supermarket Categories Only",
@@ -573,6 +589,7 @@
"ViewLogHelp": "History of viewed recipes. ",
"View_Recipes": "View Recipes",
"Viewed": "Viewed",
"Visibility": "Visibility",
"Waiting": "Waiting",
"WaitingTime": "Waiting Time",
"WarnPageLeave": "There are unsaved changes that will get lost. Leave page anyway?",
@@ -586,6 +603,7 @@
"Welcome": "Welcome",
"WorkingTime": "Working time",
"Year": "Year",
"Yes": "Yes",
"YourSpaces": "Your Spaces",
"active": "active",
"add_keyword": "Add Keyword",

View File

@@ -11,6 +11,7 @@
"Activity": "Actividad",
"Add": "Añadir",
"AddAll": "Agregar todo",
"AddChild": "",
"AddFilter": "Agregar filtro",
"AddFoodToShopping": "Añadir {food} a la lista de compras",
"AddMany": "Agregar muchos",
@@ -43,6 +44,10 @@
"BaseUnit": "Unidad base",
"BaseUnitHelp": "Unidad estándar para la conversión automática de unidades",
"Basics": "Básicos",
"BatchDeleteConfirm": "",
"BatchDeleteHelp": "",
"BatchEdit": "",
"BatchEditUpdatingItemsCount": "",
"Book": "Libro",
"Bookmarklet": "Marcador ejecutable",
"BookmarkletHelp1": "Arrastra el siguiente botón a tu barra de marcadores",
@@ -128,6 +133,7 @@
"Delete": "Eliminar",
"DeleteConfirmQuestion": "¿Confirmas querer eliminar este objeto?",
"DeleteShoppingConfirm": "¿Confirmas que quieres eliminar todo el {food} de la lista de compras?",
"DeleteSomething": "",
"Delete_All": "Eliminar todo",
"Delete_Food": "Eliminar alimento",
"Delete_Keyword": "Eliminar palabra clave",
@@ -140,6 +146,7 @@
"Disable_Amount": "Deshabilitar cantidad",
"Disabled": "Deshabilitado",
"Documentation": "Documentación",
"DontChange": "",
"Down": "Abajo",
"Download": "Descargar",
"DragToUpload": "Arrastra y suelta o haz clic para seleccionar",
@@ -204,6 +211,7 @@
"Hide_Keywords": "Esconder palabra clave",
"Hide_Recipes": "Esconder recetas",
"Hide_as_header": "Esconder como titulo",
"Hierarchy": "",
"History": "Historial",
"HostedFreeVersion": "Estás usando la versión gratuita de Tandoor",
"Hour": "Hora",
@@ -315,6 +323,7 @@
"Next": "Siguiente",
"Next_Day": "Siguiente Día",
"Next_Period": "Siguiente Período",
"No": "",
"NoCategory": "No se ha seleccionado categoría.",
"NoMoreUndo": "No hay cambios que deshacer.",
"NoUnit": "",
@@ -364,6 +373,7 @@
"Previous_Day": "Día Anterior",
"Previous_Period": "Período Anterior",
"Print": "Imprimir",
"Private": "",
"Private_Recipe": "Receta Privada",
"Private_Recipe_Help": "La receta solo podrás verla tu y la gente con la que esta compartida.",
"Profile": "Perfil",
@@ -398,7 +408,9 @@
"Recipes_In_Import": "Recetas en tu fichero de importación",
"Recipes_per_page": "Recetas por página",
"Remove": "Remover",
"RemoveAllType": "",
"RemoveFoodFromShopping": "Eliminar {food} de la lista de la compra",
"RemoveParent": "",
"Remove_nutrition_recipe": "Borrar nutrición de la canasta",
"Reset": "Restablecer",
"ResetHelp": "Reiniciar ayuda",
@@ -455,6 +467,7 @@
"Sort_by_new": "Ordenar por novedades",
"SourceImportHelp": "Importar JSON en formato schema.org/recipe o páginas HTML con recetas en formato JSON+LD o microdatos.",
"SourceImportSubtitle": "Importar JSON o HTML manualmente.",
"Space": "",
"SpaceLimitExceeded": "Tu espacio ha sobrepasado uno de sus límites, algunas funciones podrían estar restringidas.",
"SpaceLimitReached": "Este espacio ha alcanzado un límite. No se pueden crear más objetos de este tipo.",
"SpaceMemberHelp": "Agrega usuarios a tu espacio creando un enlace de invitación y enviándolo a la persona que quieras agregar.",
@@ -553,6 +566,7 @@
"ViewLogHelp": "Historial de recetas visualizadas. ",
"View_Recipes": "Mostrar recetas",
"Viewed": "Visualizada",
"Visibility": "",
"Waiting": "Esperando",
"WaitingTime": "Tiempo de espera",
"WarnPageLeave": "Hay cambios sin guardar que se perderán. ¿Salir de la página de todos modos?",
@@ -566,6 +580,7 @@
"Welcome": "Bienvenido/a",
"WorkingTime": "Tiempo de trabajo",
"Year": "Año",
"Yes": "",
"YourSpaces": "Tus espacios",
"active": "activo",
"add_keyword": "Añadir Palabra Clave",

View File

@@ -4,6 +4,7 @@
"API_Documentation": "",
"Account": "Tili",
"Add": "Lisää",
"AddChild": "",
"AddFoodToShopping": "Lisää {food} ostoslistaan",
"AddToShopping": "Lisää ostoslistalle",
"Add_Servings_to_Shopping": "Lisää {servings} Annoksia Ostoksiin",
@@ -27,6 +28,10 @@
"Automate": "Automatisoi",
"Automation": "Automaatio",
"Back": "Takaisin",
"BatchDeleteConfirm": "",
"BatchDeleteHelp": "",
"BatchEdit": "",
"BatchEditUpdatingItemsCount": "",
"Bookmarklet": "Kirjamerkki",
"Books": "Kirjat",
"CREATE_ERROR": "",
@@ -87,6 +92,7 @@
"DelayUntil": "Viive asti",
"Delete": "Poista",
"DeleteShoppingConfirm": "Oletko varma, että haluat poistaa kaikki {food} ostoslistalta?",
"DeleteSomething": "",
"Delete_All": "Poista kaikki",
"Delete_Food": "Poista ruoka",
"Delete_Keyword": "Poista avainsana",
@@ -96,6 +102,7 @@
"Disable_Amount": "Poista Määrä käytöstä",
"Disabled": "Ei käytössä",
"Documentation": "Dokumentaatio",
"DontChange": "",
"Download": "Lataa",
"Drag_Here_To_Delete": "Vedä tänne poistaaksesi",
"Edit": "Muokkaa",
@@ -143,6 +150,7 @@
"Hide_Keywords": "Piilota Avainsana",
"Hide_Recipes": "Piilota Reseptit",
"Hide_as_header": "Piilota otsikko",
"Hierarchy": "",
"Hour": "Tunti",
"Hours": "Tuntia",
"Icon": "Kuvake",
@@ -188,6 +196,7 @@
"Logo": "Logo",
"Make_Header": "Valmista Otsikko",
"Make_Ingredient": "Valmista Ainesosa",
"ManageSubscription": "",
"Manage_Books": "Hallinnoi kirjoja",
"Manage_Emails": "Hallinnoi sähköposteja",
"Meal_Plan": "Ateriasuunnitelma",
@@ -196,6 +205,7 @@
"Meal_Type_Required": "Ateriatyyppi pakollinen",
"Meal_Types": "Ateriatyypit",
"Merge": "Yhdistä",
"MergeAutomateHelp": "",
"Merge_Keyword": "Yhdistä Avainsana",
"Message": "Viesti",
"MissingProperties": "",
@@ -221,6 +231,7 @@
"New_Unit": "Uusi Yksikkö",
"Next_Day": "Seuraava Päivä",
"Next_Period": "Seuraava Jakso",
"No": "",
"NoCategory": "Luokkaa ei ole valittu.",
"NoMoreUndo": "Ei peruttavia muutoksia.",
"NoUnit": "",
@@ -261,6 +272,7 @@
"Previous_Day": "Edellinen Päivä",
"Previous_Period": "Edellinen Jakso",
"Print": "Tulosta",
"Private": "",
"Private_Recipe": "Yksityinen Resepti",
"Private_Recipe_Help": "Resepti näytetään vain sinulle ja ihmisille, joiden kanssa se jaetaan.",
"Properties": "Ominaisuudet",
@@ -282,7 +294,9 @@
"Recipes": "Reseptit",
"Recipes_In_Import": "Reseptit tuonti tiedostossasi",
"Recipes_per_page": "Reseptejä sivulla",
"RemoveAllType": "",
"RemoveFoodFromShopping": "Poista {food} ostoslistalta",
"RemoveParent": "",
"Remove_nutrition_recipe": "Poista ravintoaine reseptistä",
"Reset": "Nollaa",
"Reset_Search": "Nollaa haku",
@@ -320,6 +334,7 @@
"Size": "Koko",
"Social_Authentication": "Sosiaalinen Todennus",
"Sort_by_new": "Lajittele uusien mukaan",
"Space": "",
"Split_All_Steps": "Jaa kaikki rivit erillisiin vaiheisiin.",
"StartDate": "Aloituspäivä",
"Starting_Day": "Viikon aloituspäivä",
@@ -371,6 +386,7 @@
"Valid Until": "Voimassa Asti",
"View": "Katso",
"View_Recipes": "Näytä Reseptit",
"Visibility": "",
"Waiting": "Odottaa",
"Warning": "Varoitus",
"Website": "Verkkosivusto",
@@ -378,6 +394,7 @@
"Week_Numbers": "Viikkonumerot",
"Welcome": "Tervetuloa",
"Year": "Vuosi",
"Yes": "",
"add_keyword": "Lisää Avainsana",
"additional_options": "Lisäasetukset",
"advanced": "Edistynyt",

View File

@@ -11,6 +11,7 @@
"Activity": "Activité",
"Add": "Ajouter",
"AddAll": "Tout ajouter",
"AddChild": "",
"AddFilter": "Ajouter un filtre",
"AddFoodToShopping": "Ajouter laliment {food} à votre liste de courses",
"AddMany": "Ajouter plusieurs",
@@ -45,6 +46,10 @@
"BaseUnit": "Unité de base",
"BaseUnitHelp": "Unité standard pour la conversion automatique des unités",
"Basics": "Les bases",
"BatchDeleteConfirm": "",
"BatchDeleteHelp": "",
"BatchEdit": "",
"BatchEditUpdatingItemsCount": "",
"Book": "Livre",
"Bookmarklet": "Signet",
"BookmarkletHelp1": "Faites glisser le bouton suivant dans votre barre de signets",
@@ -131,6 +136,7 @@
"Delete": "Supprimer",
"DeleteConfirmQuestion": "Voulez-vous vraiment supprimer cet objet ?",
"DeleteShoppingConfirm": "Êtes-vous sûr(e) de vouloir supprimer tous les aliments {food} de votre liste de courses?",
"DeleteSomething": "",
"Delete_All": "Supprimer tout",
"Delete_Food": "Supprimer laliment",
"Delete_Keyword": "Supprimer le mot-clé",
@@ -143,6 +149,7 @@
"Disable_Amount": "Désactiver la quantité",
"Disabled": "Désactivé",
"Documentation": "Documentation",
"DontChange": "",
"Down": "Bas",
"Download": "Télécharger",
"DragToUpload": "Déplacer ou cliquer pour sélectionner",
@@ -211,6 +218,7 @@
"Hide_Keywords": "Cacher le mot-clé",
"Hide_Recipes": "Cacher les recettes",
"Hide_as_header": "Cacher comme en-tête",
"Hierarchy": "",
"History": "Historique",
"HostedFreeVersion": "Vous utilisez la version gratuite de Tandoor",
"Hour": "Heure",
@@ -322,6 +330,7 @@
"Next": "Suivant",
"Next_Day": "Prochain jour",
"Next_Period": "Prochaine période",
"No": "",
"NoCategory": "Pas de catégorie sélectionnée.",
"NoMoreUndo": "Aucun changement à annuler.",
"NoUnit": "Pas d'unité",
@@ -374,6 +383,7 @@
"Previous_Day": "Jour précédent",
"Previous_Period": "Période précédente",
"Print": "Imprimer",
"Private": "",
"Private_Recipe": "Recette privée",
"Private_Recipe_Help": "La recette est uniquement visible par vous et les gens avec qui elle est partagée.",
"Profile": "Profile",
@@ -408,7 +418,9 @@
"Recipes_In_Import": "Recettes dans votre fichier dimportation",
"Recipes_per_page": "Nombre de recettes par page",
"Remove": "Enlever",
"RemoveAllType": "",
"RemoveFoodFromShopping": "Supprimer laliment {food} de votre liste de courses",
"RemoveParent": "",
"Remove_nutrition_recipe": "Supprimer les valeurs nutritionelles de la recette",
"Reset": "Réinitialiser",
"ResetHelp": "Aide à la réinitialisation",
@@ -470,6 +482,7 @@
"Source": "Source",
"SourceImportHelp": "Importez du JSON au format schema.org/recipe ou des pages HTML avec une recette json+ld ou des microdonnées.",
"SourceImportSubtitle": "Importez en JSON ou HTML manuellement.",
"Space": "",
"SpaceLimitExceeded": "Votre groupe a dépassé une de ses limites, certaines fonctions pourraient être restreintes.",
"SpaceLimitReached": "Ce groupe a atteint sa limite. Aucun nouvel objet de ce type ne peut être créé.",
"SpaceMemberHelp": "Ajoutez des utilisateurs à votre espace en créant un lien d'invitation et en l'envoyant à la personne que vous souhaitez ajouter.",
@@ -572,6 +585,7 @@
"ViewLogHelp": "Historique des recettes consultées. ",
"View_Recipes": "Voir les recettes",
"Viewed": "Vue",
"Visibility": "",
"Waiting": "Attente",
"WaitingTime": "Temps d'attente",
"WarnPageLeave": "Certaines modifications non enregistrées seront perdues. Voulez-vous quand même quitter la page ?",
@@ -585,6 +599,7 @@
"Welcome": "Bienvenue",
"WorkingTime": "Temps de préparation",
"Year": "Année",
"Yes": "",
"YourSpaces": "Vos groupes",
"active": "actif",
"add_keyword": "Ajouter un Mot Clé",

View File

@@ -4,6 +4,7 @@
"API_Documentation": "",
"Account": "חשבון",
"Add": "הוספה",
"AddChild": "",
"AddFoodToShopping": "הוסף {מזון} לרשימת הקניות",
"AddToShopping": "הוסף לרשימת קניות",
"Add_Servings_to_Shopping": "הוסף{מנה}מנות לקנייה",
@@ -26,6 +27,10 @@
"Automate": "אוטומט",
"Automation": "אוטומטציה",
"Back": "חזור",
"BatchDeleteConfirm": "",
"BatchDeleteHelp": "",
"BatchEdit": "",
"BatchEditUpdatingItemsCount": "",
"Bookmarklet": "סימניה",
"Books": "ספרים",
"CREATE_ERROR": "",
@@ -90,6 +95,7 @@
"DelayUntil": "השהה עד",
"Delete": "מחק",
"DeleteShoppingConfirm": "האם אתה בטוח שברצונך להסיר את כל ה{מזון} מרשימת הקניות ?",
"DeleteSomething": "",
"Delete_All": "מחק הכל",
"Delete_Food": "מחק אוכל",
"Delete_Keyword": "מחר מילת מפתח",
@@ -99,6 +105,7 @@
"Disable_Amount": "אל תאפשר כמות",
"Disabled": "מושבת",
"Documentation": "תיעוד",
"DontChange": "",
"Download": "הורדה",
"Drag_Here_To_Delete": "משוך לכאן למחיקה",
"Edit": "ערוך",
@@ -146,6 +153,7 @@
"Hide_Keywords": "הסתרת מילת מפתח",
"Hide_Recipes": "הסתרת מתכונים",
"Hide_as_header": "הסתר בתור כותרת",
"Hierarchy": "",
"Hour": "שעה",
"Hours": "שעות",
"Icon": "צלמית",
@@ -194,6 +202,7 @@
"Logo": "לוגו",
"Make_Header": "הפוך לכותרת",
"Make_Ingredient": "הפוך למרכיב",
"ManageSubscription": "",
"Manage_Books": "נהל ספרים",
"Manage_Emails": "נהל כתובות דואר אלקטרוני",
"Meal_Plan": "תוכנית ארוחה",
@@ -202,6 +211,7 @@
"Meal_Type_Required": "סוג אוכל נדרש",
"Meal_Types": "סוגי אוכל",
"Merge": "איחוד",
"MergeAutomateHelp": "",
"Merge_Keyword": "איחוד מילת מפתח",
"Message": "הודעה",
"MissingProperties": "",
@@ -232,6 +242,7 @@
"New_Unit": "יחידה חדשה",
"Next_Day": "היום הבא",
"Next_Period": "התקופה הבאה",
"No": "",
"NoCategory": "לא נבחרה קטגוריה.",
"NoMoreUndo": "אין עוד שינויים לשחזור.",
"NoUnit": "",
@@ -272,6 +283,7 @@
"Previous_Day": "יום קודם",
"Previous_Period": "תקופה קודמת",
"Print": "הדפסה",
"Private": "",
"Private_Recipe": "מתכון פרטי",
"Private_Recipe_Help": "המתכון מוצג רק לך ולאנשים ששותפו.",
"Properties": "ערכים",
@@ -293,7 +305,9 @@
"Recipes": "מתכונים",
"Recipes_In_Import": "מתכון בקובץ הייבוא",
"Recipes_per_page": "מתכונים בכל דף",
"RemoveAllType": "",
"RemoveFoodFromShopping": "הסר {מזון} מרשימת הקניות",
"RemoveParent": "",
"Remove_nutrition_recipe": "מחר ערכים תזונתיים מהמתכון",
"Reset": "אפס",
"Reset_Search": "אפס חיפוש",
@@ -332,6 +346,7 @@
"Size": "גודל",
"Social_Authentication": "אימות חברתי",
"Sort_by_new": "סדר ע\"י חדש",
"Space": "",
"Space_Cosmetic_Settings": "חלק מהגדרות הקוסמטיות יכולות להיות מעודכנות על ידי מנהל המרחב וידרסו את הגדרות הקליינט עבור מרחב זה.",
"Split_All_Steps": "פצל את כל השורות לצעדים נפרדים.",
"StartDate": "תאריך התחלה",
@@ -390,6 +405,7 @@
"Valid Until": "פעיל עד",
"View": "תצוגה",
"View_Recipes": "הצג מתכונים",
"Visibility": "",
"Waiting": "המתנה",
"Warning": "אזהרה",
"Warning_Delete_Supermarket_Category": "מחיקת קטגורית סופרמרקט תמחוק גם את המאכלים הקשורים. האם אתה בטוח ?",
@@ -398,6 +414,7 @@
"Week_Numbers": "מספר השבוע",
"Welcome": "ברוכים הבאים",
"Year": "שנה",
"Yes": "",
"add_keyword": "הוסף מילת מפתח",
"additional_options": "אפשרויות נוספות",
"advanced": "מתקדם",

View File

@@ -4,6 +4,7 @@
"API_Documentation": "",
"Account": "Korisnički račun",
"Add": "Dodaj",
"AddChild": "",
"AddFoodToShopping": "Dodaj {food} na svoj popis za kupovinu",
"AddToShopping": "Dodaj na popis za kupovinu",
"Add_Servings_to_Shopping": "Dodaj {servings} obroka u Kupovinu",
@@ -26,6 +27,10 @@
"Automate": "Automatiziraj",
"Automation": "Automatizacija",
"Back": "Nazad",
"BatchDeleteConfirm": "",
"BatchDeleteHelp": "",
"BatchEdit": "",
"BatchEditUpdatingItemsCount": "",
"Bookmarklet": "Knjižna oznaka",
"Books": "Knjige",
"CREATE_ERROR": "",
@@ -90,6 +95,7 @@
"DelayUntil": "Odgodi do",
"Delete": "Obriši",
"DeleteShoppingConfirm": "Jesi li siguran da želiš ukloniti svu {food} s popisa za kupnju?",
"DeleteSomething": "",
"Delete_All": "Obriši sve",
"Delete_Food": "Obriši namirnicu",
"Delete_Keyword": "Obriši ključnu riječ",
@@ -99,6 +105,7 @@
"Disable_Amount": "Onemogući količinu",
"Disabled": "Onemogućeno",
"Documentation": "Dokumentacija",
"DontChange": "",
"Download": "Preuzimanje",
"Drag_Here_To_Delete": "Povuci ovdje za brisanje",
"Edit": "Uredi",
@@ -146,6 +153,7 @@
"Hide_Keywords": "Sakrij ključnu riječ",
"Hide_Recipes": "Sakrij Recepte",
"Hide_as_header": "Sakrij kao zaglavlje",
"Hierarchy": "",
"Hour": "Sat",
"Hours": "Sati",
"Icon": "Ikona",
@@ -194,6 +202,7 @@
"Logo": "Logotip",
"Make_Header": "Napravi zaglavlje",
"Make_Ingredient": "Napravi sastojak",
"ManageSubscription": "",
"Manage_Books": "Upravljaj knjigama",
"Manage_Emails": "Upravljanje e-poštom",
"Meal_Plan": "Plan obroka",
@@ -202,6 +211,7 @@
"Meal_Type_Required": "Potreban je tip obroka",
"Meal_Types": "Tipovi obroka",
"Merge": "Spoji",
"MergeAutomateHelp": "",
"Merge_Keyword": "Spoji ključnu riječ",
"Message": "Poruka",
"MissingProperties": "",
@@ -232,6 +242,7 @@
"New_Unit": "Nova jedinica",
"Next_Day": "Sljedeći dan",
"Next_Period": "Slijedeće razdoblje",
"No": "",
"NoCategory": "Nije odabrana kategorija.",
"NoMoreUndo": "Nema promjena koje se mogu poništiti.",
"NoUnit": "",
@@ -272,6 +283,7 @@
"Previous_Day": "Prethodni dan",
"Previous_Period": "Prethodno razdoblje",
"Print": "Ispis",
"Private": "",
"Private_Recipe": "Privatni Recept",
"Private_Recipe_Help": "Recept se prikazuje samo vama i osobama s kojima se dijeli.",
"Properties": "Svojstva",
@@ -293,7 +305,9 @@
"Recipes": "Recepti",
"Recipes_In_Import": "Recepti u vašoj datoteci za uvoz",
"Recipes_per_page": "Recepata po stranici",
"RemoveAllType": "",
"RemoveFoodFromShopping": "Ukloni {food} sa svog popisa za kupovinu",
"RemoveParent": "",
"Remove_nutrition_recipe": "Izbrišite hranjive sastojke iz recepta",
"Reset": "Ponovo postavi",
"Reset_Search": "Poništi pretragu",
@@ -332,6 +346,7 @@
"Size": "Veličina",
"Social_Authentication": "Autentifikacija putem društvenih mreža",
"Sort_by_new": "Poredaj po novom",
"Space": "",
"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.",
"StartDate": "Početni datum",
@@ -390,6 +405,7 @@
"Valid Until": "Vrijedi do",
"View": "Pogled",
"View_Recipes": "Pogledajte recepte",
"Visibility": "",
"Waiting": "Čekanje",
"Warning": "Upozorenje",
"Warning_Delete_Supermarket_Category": "Brisanje kategorije supermarketa također će izbrisati sve odnose na namirnice. Jeste li sigurni?",
@@ -398,6 +414,7 @@
"Week_Numbers": "Brojevi tjedana",
"Welcome": "Dobrodošli",
"Year": "Godina",
"Yes": "",
"add_keyword": "Dodaj ključnu riječ",
"additional_options": "Dodatne mogućnosti",
"advanced": "Napredno",

View File

@@ -4,6 +4,7 @@
"API_Documentation": "",
"Account": "Fiók",
"Add": "Hozzáadás",
"AddChild": "",
"AddFoodToShopping": "{food} hozzáadása bevásárlólistához",
"AddToShopping": "Hozzáadás a bevásárlólistához",
"Add_Servings_to_Shopping": "",
@@ -26,6 +27,10 @@
"Automate": "Automatizálás",
"Automation": "Automatizálás",
"Back": "Vissza",
"BatchDeleteConfirm": "",
"BatchDeleteHelp": "",
"BatchEdit": "",
"BatchEditUpdatingItemsCount": "",
"Bookmarklet": "Könyvjelző",
"Books": "Könyvek",
"CREATE_ERROR": "",
@@ -79,6 +84,7 @@
"DelayUntil": "",
"Delete": "Törlés",
"DeleteShoppingConfirm": "Biztos, hogy az összes {food}-t el akarja távolítani a bevásárlólistáról?",
"DeleteSomething": "",
"Delete_Food": "Alapanyag törlése",
"Delete_Keyword": "Kulcsszó törlése",
"Description": "Megnevezés",
@@ -87,6 +93,7 @@
"Disable_Amount": "Összeg kikapcsolása",
"Disabled": "Kikapcsolva",
"Documentation": "Dokumentáció",
"DontChange": "",
"Download": "Letöltés",
"Drag_Here_To_Delete": "Törléshez húzza ide",
"Edit": "Szerkesztés",
@@ -129,6 +136,7 @@
"Hide_Keywords": "Kulcsszó elrejtése",
"Hide_Recipes": "Receptek elrejtése",
"Hide_as_header": "Fejlécként elrejtve",
"Hierarchy": "",
"Hour": "Óra",
"Hours": "Óra",
"Icon": "Ikon",
@@ -175,6 +183,7 @@
"Log_Recipe_Cooking": "Főzés naplózása",
"Make_Header": "Átalakítás címsorra",
"Make_Ingredient": "Összetevő létrehozása",
"ManageSubscription": "",
"Manage_Books": "Könyvek kezelése",
"Manage_Emails": "Levelezés kezelése",
"Meal_Plan": "Menüterv",
@@ -183,6 +192,7 @@
"Meal_Type_Required": "Étkezés megadása kötelező",
"Meal_Types": "Étkezések",
"Merge": "Összefűzés",
"MergeAutomateHelp": "",
"Merge_Keyword": "Kulcsszó összevonása",
"Message": "Üzenet",
"MissingProperties": "",
@@ -210,6 +220,7 @@
"New_Unit": "Új mennyiségi egység",
"Next_Day": "Következő nap",
"Next_Period": "Következő periódus",
"No": "",
"NoCategory": "Nincs kategória kiválasztva.",
"NoUnit": "",
"No_ID": "Azonosító nem található, ezért nem törölhető.",
@@ -248,6 +259,7 @@
"Previous_Day": "Előző nap",
"Previous_Period": "Előző periódus",
"Print": "Nyomtatás",
"Private": "",
"Private_Recipe": "Privát recept",
"Private_Recipe_Help": "A recept csak Önnek és azoknak az embereknek jelenik meg, akikkel megosztotta.",
"Properties": "Tulajdonságok",
@@ -266,7 +278,9 @@
"Recipes": "Receptek",
"Recipes_In_Import": "",
"Recipes_per_page": "Receptek oldalanként",
"RemoveAllType": "",
"RemoveFoodFromShopping": "{food} eltávolítása bevásárlólistáról",
"RemoveParent": "",
"Remove_nutrition_recipe": "Tápértékadatok törlése a receptből",
"Reset": "Visszaállítás",
"Reset_Search": "Keresés alaphelyzetbe állítása",
@@ -299,6 +313,7 @@
"Single": "Egyetlen",
"Size": "Méret",
"Sort_by_new": "Rendezés legújabbak szerint",
"Space": "",
"Split_All_Steps": "Ossza fel az összes sort különálló lépésekbe.",
"StartDate": "Kezdés dátuma",
"Starting_Day": "A hét kezdőnapja",
@@ -350,6 +365,7 @@
"Valid Until": "Érvényes",
"View": "Nézet",
"View_Recipes": "Receptek megjelenítése",
"Visibility": "",
"Waiting": "Várakozás",
"Warning": "Figyelmeztetés",
"Warning_Delete_Supermarket_Category": "Egy szupermarket-kategória törlése az alapanyagokkal való összes kapcsolatot is törli. Biztos vagy benne?",
@@ -358,6 +374,7 @@
"Week_Numbers": "",
"Welcome": "Üdvözöljük",
"Year": "Év",
"Yes": "",
"add_keyword": "Kulcsszó hozzáadása",
"additional_options": "További lehetőségek",
"advanced": "Haladó",

View File

@@ -2,6 +2,7 @@
"API_Browser": "",
"API_Documentation": "",
"Add": "",
"AddChild": "",
"Add_nutrition_recipe": "Ավելացնել սննդայնություն բաղադրատոմսին",
"Add_to_Book": "",
"Add_to_Plan": "Ավելացնել պլանին",
@@ -9,6 +10,10 @@
"Advanced Search Settings": "Ընդլայնված փնտրման կարգավորումներ",
"Apply": "",
"Automate": "Ավտոմատացնել",
"BatchDeleteConfirm": "",
"BatchDeleteHelp": "",
"BatchEdit": "",
"BatchEditUpdatingItemsCount": "",
"Books": "",
"CREATE_ERROR": "",
"Calories": "",
@@ -25,9 +30,11 @@
"DELETE_ERROR": "",
"Date": "",
"Delete": "",
"DeleteSomething": "",
"Delete_Food": "Ջնջել սննդամթերքը",
"Delete_Keyword": "Ջնջել բանալի բառը",
"Description": "Նկարագրություն",
"DontChange": "",
"Download": "Ներբեռնել",
"Edit": "",
"Edit_Food": "Խմբագրել սննդամթերքը",
@@ -52,6 +59,7 @@
"Hide_Keywords": "Թաքցնել բանալի բառը",
"Hide_Recipes": "Թաքցնել բաղադրատոմսերը",
"Hide_as_header": "Թաքցնել որպես խորագիր",
"Hierarchy": "",
"IgnoreAccents": "",
"IgnoreAccentsHelp": "",
"Import": "Ներմուծել",
@@ -63,9 +71,11 @@
"Load_More": "",
"Log_Cooking": "Գրանցել եփելը",
"Log_Recipe_Cooking": "Գրանցել բաղադրատոմսի օգտագործում",
"ManageSubscription": "",
"Manage_Books": "Կարգավորել Գրքերը",
"Meal_Plan": "Ճաշացուցակ",
"Merge": "Միացնել",
"MergeAutomateHelp": "",
"Merge_Keyword": "Միացնել բանալի բառը",
"MissingProperties": "",
"Move": "Տեղափոխել",
@@ -76,6 +86,7 @@
"New_Food": "Նոր սննդամթերք",
"New_Keyword": "Նոր բանալի բառ",
"New_Recipe": "Նոր բաղադրատոմս",
"No": "",
"NoUnit": "",
"No_Results": "Արդյունքներ չկան",
"Nutrition": "",
@@ -88,6 +99,8 @@
"PrecisionSearchHelp": "",
"Preparation": "",
"Print": "Տպել",
"Private": "",
"Private_Recipe_Help": "",
"Proteins": "",
"Rating": "",
"Recently_Viewed": "Վերջերս դիտած",
@@ -96,6 +109,8 @@
"Recipe_Image": "Բաղադրատոմսի նկար",
"Recipes": "Բաղադրատոմսեր",
"Recipes_per_page": "Բաղադրատոմս էջում",
"RemoveAllType": "",
"RemoveParent": "",
"Remove_nutrition_recipe": "Հեռացնել բաղադրատոմսի սննդայնությունը",
"Reset_Search": "Զրոյացնել որոնումը",
"Save": "",
@@ -115,6 +130,7 @@
"Show_as_header": "Ցույց տալ որպես խորագիր",
"Size": "",
"Sort_by_new": "Տեսակավորել ըստ նորերի",
"Space": "",
"StartsWith": "",
"StartsWithHelp": "",
"Step": "",
@@ -132,7 +148,9 @@
"Use_Plural_Unit_Simple": "",
"View": "Դիտել",
"View_Recipes": "Դիտել բաղադրատոմսերը",
"Visibility": "",
"Waiting": "",
"Yes": "",
"all_fields_optional": "Բոլոր տողերը կամավոր են և կարող են մնալ դատարկ։",
"and": "և",
"confirm_delete": "Համոզվա՞ծ եք, որ ուզում եք ջնջել այս {օբյեկտը}։",

View File

@@ -4,6 +4,7 @@
"API_Documentation": "",
"Account": "",
"Add": "Tambahkan",
"AddChild": "",
"AddFoodToShopping": "",
"AddToShopping": "",
"Add_Servings_to_Shopping": "",
@@ -21,6 +22,10 @@
"Auto_Planner": "",
"Automate": "",
"Automation": "Automatis",
"BatchDeleteConfirm": "",
"BatchDeleteHelp": "",
"BatchEdit": "",
"BatchEditUpdatingItemsCount": "",
"Bookmarklet": "",
"Books": "Buku",
"CREATE_ERROR": "",
@@ -70,6 +75,7 @@
"DelayUntil": "",
"Delete": "Menghapus",
"DeleteShoppingConfirm": "",
"DeleteSomething": "",
"Delete_Food": "",
"Delete_Keyword": "Hapus Kata Kunci",
"Description": "",
@@ -77,6 +83,7 @@
"Disable_Amount": "Nonaktifkan Jumlah",
"Disabled": "",
"Documentation": "",
"DontChange": "",
"Download": "Unduh",
"Drag_Here_To_Delete": "",
"Edit": "Sunting",
@@ -117,6 +124,7 @@
"Hide_Keywords": "Sembunyikan Kata Kunci",
"Hide_Recipes": "Sembunyikan Resep",
"Hide_as_header": "Sembunyikan sebagai tajuk",
"Hierarchy": "",
"Hour": "",
"Hours": "",
"Icon": "",
@@ -160,6 +168,7 @@
"Log_Recipe_Cooking": "Log Resep Memasak",
"Make_Header": "Buat Header",
"Make_Ingredient": "Buat bahan",
"ManageSubscription": "",
"Manage_Books": "Kelola Buku",
"Manage_Emails": "",
"Meal_Plan": "rencana makan",
@@ -168,6 +177,7 @@
"Meal_Type_Required": "",
"Meal_Types": "",
"Merge": "Menggabungkan",
"MergeAutomateHelp": "",
"Merge_Keyword": "Gabungkan Kata Kunci",
"Message": "",
"MissingProperties": "",
@@ -194,6 +204,7 @@
"New_Unit": "",
"Next_Day": "",
"Next_Period": "",
"No": "",
"NoCategory": "",
"NoUnit": "",
"No_ID": "",
@@ -226,6 +237,7 @@
"Previous_Day": "",
"Previous_Period": "",
"Print": "Mencetak",
"Private": "",
"Private_Recipe": "Resep Pribadi",
"Private_Recipe_Help": "Resep hanya diperlihatkan kepada Anda dan orang-orang yang dibagikan resep tersebut.",
"Protected": "Terlindung",
@@ -242,7 +254,9 @@
"Recipes": "Resep",
"Recipes_In_Import": "",
"Recipes_per_page": "Resep per Halaman",
"RemoveAllType": "",
"RemoveFoodFromShopping": "",
"RemoveParent": "",
"Remove_nutrition_recipe": "Hapus nutrisi dari resep",
"Reset": "",
"Reset_Search": "Setel Ulang Pencarian",
@@ -276,6 +290,7 @@
"Size": "Ukuran",
"Social_Authentication": "",
"Sort_by_new": "Urutkan berdasarkan baru",
"Space": "",
"Starting_Day": "",
"StartsWith": "",
"StartsWithHelp": "",
@@ -318,6 +333,7 @@
"Valid Until": "",
"View": "Melihat",
"View_Recipes": "Lihat Resep",
"Visibility": "",
"Waiting": "Menunggu",
"Warning": "",
"Warning_Delete_Supermarket_Category": "",
@@ -325,6 +341,7 @@
"Week": "",
"Week_Numbers": "",
"Year": "",
"Yes": "",
"add_keyword": "",
"additional_options": "",
"advanced": "",

View File

@@ -4,6 +4,7 @@
"API_Documentation": "",
"Account": "",
"Add": "",
"AddChild": "",
"AddFoodToShopping": "",
"AddToShopping": "",
"Add_Servings_to_Shopping": "",
@@ -26,6 +27,10 @@
"Automate": "",
"Automation": "",
"Back": "",
"BatchDeleteConfirm": "",
"BatchDeleteHelp": "",
"BatchEdit": "",
"BatchEditUpdatingItemsCount": "",
"Bookmarklet": "",
"Books": "",
"CREATE_ERROR": "",
@@ -89,6 +94,7 @@
"DelayUntil": "",
"Delete": "",
"DeleteShoppingConfirm": "",
"DeleteSomething": "",
"Delete_All": "",
"Delete_Food": "",
"Delete_Keyword": "",
@@ -98,6 +104,7 @@
"Disable_Amount": "",
"Disabled": "",
"Documentation": "",
"DontChange": "",
"Download": "",
"Drag_Here_To_Delete": "",
"Edit": "",
@@ -145,6 +152,7 @@
"Hide_Keywords": "",
"Hide_Recipes": "",
"Hide_as_header": "",
"Hierarchy": "",
"Hour": "",
"Hours": "",
"Icon": "",
@@ -193,6 +201,7 @@
"Logo": "",
"Make_Header": "",
"Make_Ingredient": "",
"ManageSubscription": "",
"Manage_Books": "",
"Manage_Emails": "",
"Meal_Plan": "",
@@ -201,6 +210,7 @@
"Meal_Type_Required": "",
"Meal_Types": "",
"Merge": "",
"MergeAutomateHelp": "",
"Merge_Keyword": "",
"Message": "",
"MissingProperties": "",
@@ -231,6 +241,7 @@
"New_Unit": "",
"Next_Day": "",
"Next_Period": "",
"No": "",
"NoCategory": "",
"NoMoreUndo": "",
"NoUnit": "",
@@ -271,6 +282,7 @@
"Previous_Day": "",
"Previous_Period": "",
"Print": "",
"Private": "",
"Private_Recipe": "",
"Private_Recipe_Help": "",
"Properties": "",
@@ -292,7 +304,9 @@
"Recipes": "",
"Recipes_In_Import": "",
"Recipes_per_page": "",
"RemoveAllType": "",
"RemoveFoodFromShopping": "",
"RemoveParent": "",
"Remove_nutrition_recipe": "",
"Reset": "",
"Reset_Search": "",
@@ -330,6 +344,7 @@
"Size": "",
"Social_Authentication": "",
"Sort_by_new": "",
"Space": "",
"Space_Cosmetic_Settings": "",
"Split_All_Steps": "",
"StartDate": "",
@@ -388,6 +403,7 @@
"Valid Until": "",
"View": "",
"View_Recipes": "",
"Visibility": "",
"Waiting": "",
"Warning": "",
"Warning_Delete_Supermarket_Category": "",
@@ -396,6 +412,7 @@
"Week_Numbers": "",
"Welcome": "",
"Year": "",
"Yes": "",
"add_keyword": "",
"additional_options": "",
"advanced": "",

View File

@@ -11,6 +11,7 @@
"Activity": "Attività",
"Add": "Aggiungi",
"AddAll": "Aggiungi tutto",
"AddChild": "",
"AddFilter": "Aggiungi filtro",
"AddFoodToShopping": "Aggiungi {food} alla tua lista della spesa",
"AddMany": "Aggiungi molti",
@@ -45,6 +46,10 @@
"BaseUnit": "Unità di base",
"BaseUnitHelp": "Unità standard per la conversione automatica di unità",
"Basics": "Informazioni di base",
"BatchDeleteConfirm": "",
"BatchDeleteHelp": "",
"BatchEdit": "",
"BatchEditUpdatingItemsCount": "",
"Book": "Libro",
"Bookmarklet": "Segnalibro",
"BookmarkletHelp1": "Trascina il pulsante seguente nella barra dei tuoi segnalibri",
@@ -131,6 +136,7 @@
"Delete": "Elimina",
"DeleteConfirmQuestion": "Sei sicuro di voler eliminare questo oggetto?",
"DeleteShoppingConfirm": "Sei sicuro di voler rimuovere tutto {food} dalla lista della spesa?",
"DeleteSomething": "",
"Delete_All": "Elimina tutti",
"Delete_Food": "Elimina alimento",
"Delete_Keyword": "Elimina parola chiave",
@@ -143,6 +149,7 @@
"Disable_Amount": "Disabilita quantità",
"Disabled": "Disabilitato",
"Documentation": "Documentazione",
"DontChange": "",
"Down": "Giù",
"Download": "Scarica",
"DragToUpload": "Trascina e rilascia o fai clic per selezionare",
@@ -211,6 +218,7 @@
"Hide_Keywords": "Nascondi parola chiave",
"Hide_Recipes": "Nascondi ricette",
"Hide_as_header": "Nascondi come intestazione",
"Hierarchy": "",
"History": "Cronologia",
"HostedFreeVersion": "Stai utilizzando la versione gratuita di Tandoor",
"Hour": "Ora",
@@ -324,6 +332,7 @@
"Next": "Successivo",
"Next_Day": "Giorno successivo",
"Next_Period": "Periodo successivo",
"No": "",
"NoCategory": "Nessuna categoria selezionata.",
"NoMoreUndo": "Nessuna modifica da annullare.",
"NoUnit": "Nessuna unità",
@@ -376,6 +385,7 @@
"Previous_Day": "Giorno precedente",
"Previous_Period": "Periodo precedente",
"Print": "Stampa",
"Private": "",
"Private_Recipe": "Ricetta privata",
"Private_Recipe_Help": "La ricetta viene mostrata solo a te e a chi l'hai condivisa.",
"Profile": "Profilo",
@@ -410,7 +420,9 @@
"Recipes_In_Import": "Ricette nel tuo file di importazione",
"Recipes_per_page": "Ricette per pagina",
"Remove": "Rimuovi",
"RemoveAllType": "",
"RemoveFoodFromShopping": "Rimuovi {food} dalla tua lista della spesa",
"RemoveParent": "",
"Remove_nutrition_recipe": "Elimina nutrienti dalla ricetta",
"Reset": "Azzera",
"ResetHelp": "Ripristina aiuto",
@@ -472,6 +484,7 @@
"Source": "Fonte",
"SourceImportHelp": "Importa JSON nel formato schema.org/recipe o pagine HTML con ricetta json+ld o microdati.",
"SourceImportSubtitle": "Importa manualmente JSON o HTML.",
"Space": "",
"SpaceLimitExceeded": "Il tuo spazio ha superato uno dei suoi limiti, alcune funzioni potrebbero essere limitate.",
"SpaceLimitReached": "Questo spazio ha raggiunto il limite. Non è possibile creare altri oggetti di questo tipo.",
"SpaceMemberHelp": "Aggiungi utenti al tuo spazio creando un collegamento di invito e inviandolo alla persona che desideri aggiungere.",
@@ -574,6 +587,7 @@
"ViewLogHelp": "Cronologia delle ricette visualizzate. ",
"View_Recipes": "Mostra ricette",
"Viewed": "Visualizzata",
"Visibility": "",
"Waiting": "Attesa",
"WaitingTime": "Tempo di attesa",
"WarnPageLeave": "Ci sono modifiche non salvate che andranno perse. Vuoi comunque abbandonare la pagina?",
@@ -587,6 +601,7 @@
"Welcome": "Benvenuto",
"WorkingTime": "Orario lavorativo",
"Year": "Anno",
"Yes": "",
"YourSpaces": "I tuoi spazi",
"active": "attivo",
"add_keyword": "Aggiungi parola chiave",

View File

@@ -4,6 +4,7 @@
"API_Documentation": "",
"Account": "",
"Add": "",
"AddChild": "",
"AddFoodToShopping": "",
"AddToShopping": "",
"Add_Servings_to_Shopping": "",
@@ -26,6 +27,10 @@
"Automate": "",
"Automation": "",
"Back": "",
"BatchDeleteConfirm": "",
"BatchDeleteHelp": "",
"BatchEdit": "",
"BatchEditUpdatingItemsCount": "",
"Bookmarklet": "",
"Books": "",
"CREATE_ERROR": "",
@@ -81,6 +86,7 @@
"DelayUntil": "",
"Delete": "",
"DeleteShoppingConfirm": "",
"DeleteSomething": "",
"Delete_Food": "",
"Delete_Keyword": "Ištrinti raktažodį",
"Description": "",
@@ -89,6 +95,7 @@
"Disable_Amount": "Išjungti sumą",
"Disabled": "",
"Documentation": "",
"DontChange": "",
"Download": "",
"Drag_Here_To_Delete": "",
"Edit": "",
@@ -131,6 +138,7 @@
"Hide_Keywords": "Paslėpti raktažodį",
"Hide_Recipes": "Paslėpti receptus",
"Hide_as_header": "Slėpti kaip antraštę",
"Hierarchy": "",
"Hour": "",
"Hours": "",
"Icon": "",
@@ -177,6 +185,7 @@
"Log_Recipe_Cooking": "Užregistruoti recepto pagaminimą",
"Make_Header": "Padaryti antraštę",
"Make_Ingredient": "Padaryti ingredientą",
"ManageSubscription": "",
"Manage_Books": "Tvarkyti knygas",
"Manage_Emails": "",
"Meal_Plan": "Maisto planas",
@@ -185,6 +194,7 @@
"Meal_Type_Required": "",
"Meal_Types": "",
"Merge": "",
"MergeAutomateHelp": "",
"Merge_Keyword": "Sujungti raktažodį",
"Message": "",
"MissingProperties": "",
@@ -213,6 +223,7 @@
"New_Unit": "",
"Next_Day": "",
"Next_Period": "",
"No": "",
"NoCategory": "",
"NoUnit": "",
"No_ID": "",
@@ -252,6 +263,7 @@
"Previous_Day": "",
"Previous_Period": "",
"Print": "",
"Private": "",
"Private_Recipe": "",
"Private_Recipe_Help": "",
"Properties": "",
@@ -270,7 +282,9 @@
"Recipes": "",
"Recipes_In_Import": "",
"Recipes_per_page": "Receptų skaičius per puslapį",
"RemoveAllType": "",
"RemoveFoodFromShopping": "",
"RemoveParent": "",
"Remove_nutrition_recipe": "Ištrinti mitybos informaciją iš recepto",
"Reset": "",
"Reset_Search": "Iš naujo nustatyti paiešką",
@@ -304,6 +318,7 @@
"Size": "",
"Social_Authentication": "",
"Sort_by_new": "Rūšiuoti pagal naujumą",
"Space": "",
"Split_All_Steps": "",
"StartDate": "",
"Starting_Day": "",
@@ -358,6 +373,7 @@
"Valid Until": "",
"View": "",
"View_Recipes": "Žiūrėti receptus",
"Visibility": "",
"Waiting": "",
"Warning": "",
"Warning_Delete_Supermarket_Category": "",
@@ -366,6 +382,7 @@
"Week_Numbers": "",
"Welcome": "",
"Year": "",
"Yes": "",
"add_keyword": "",
"additional_options": "",
"advanced": "",

View File

@@ -4,6 +4,7 @@
"API_Documentation": "",
"Account": "",
"Add": "",
"AddChild": "",
"AddFoodToShopping": "",
"AddToShopping": "",
"Add_Servings_to_Shopping": "",
@@ -26,6 +27,10 @@
"Automate": "",
"Automation": "",
"Back": "",
"BatchDeleteConfirm": "",
"BatchDeleteHelp": "",
"BatchEdit": "",
"BatchEditUpdatingItemsCount": "",
"Bookmarklet": "",
"Books": "",
"CREATE_ERROR": "",
@@ -90,6 +95,7 @@
"DelayUntil": "",
"Delete": "",
"DeleteShoppingConfirm": "",
"DeleteSomething": "",
"Delete_All": "",
"Delete_Food": "",
"Delete_Keyword": "",
@@ -99,6 +105,7 @@
"Disable_Amount": "",
"Disabled": "",
"Documentation": "",
"DontChange": "",
"Download": "",
"Drag_Here_To_Delete": "",
"Edit": "",
@@ -146,6 +153,7 @@
"Hide_Keywords": "",
"Hide_Recipes": "",
"Hide_as_header": "",
"Hierarchy": "",
"Hour": "",
"Hours": "",
"Icon": "",
@@ -194,6 +202,7 @@
"Logo": "",
"Make_Header": "",
"Make_Ingredient": "",
"ManageSubscription": "",
"Manage_Books": "",
"Manage_Emails": "",
"Meal_Plan": "",
@@ -202,6 +211,7 @@
"Meal_Type_Required": "",
"Meal_Types": "",
"Merge": "",
"MergeAutomateHelp": "",
"Merge_Keyword": "",
"Message": "",
"MissingProperties": "",
@@ -232,6 +242,7 @@
"New_Unit": "",
"Next_Day": "",
"Next_Period": "",
"No": "",
"NoCategory": "",
"NoMoreUndo": "",
"NoUnit": "",
@@ -272,6 +283,7 @@
"Previous_Day": "",
"Previous_Period": "",
"Print": "",
"Private": "",
"Private_Recipe": "",
"Private_Recipe_Help": "",
"Properties": "",
@@ -293,7 +305,9 @@
"Recipes": "",
"Recipes_In_Import": "",
"Recipes_per_page": "",
"RemoveAllType": "",
"RemoveFoodFromShopping": "",
"RemoveParent": "",
"Remove_nutrition_recipe": "",
"Reset": "",
"Reset_Search": "",
@@ -332,6 +346,7 @@
"Size": "",
"Social_Authentication": "",
"Sort_by_new": "",
"Space": "",
"Space_Cosmetic_Settings": "",
"Split_All_Steps": "",
"StartDate": "",
@@ -390,6 +405,7 @@
"Valid Until": "",
"View": "",
"View_Recipes": "",
"Visibility": "",
"Waiting": "",
"Warning": "",
"Warning_Delete_Supermarket_Category": "",
@@ -398,6 +414,7 @@
"Week_Numbers": "",
"Welcome": "",
"Year": "",
"Yes": "",
"add_keyword": "",
"additional_options": "",
"advanced": "",

View File

@@ -4,6 +4,7 @@
"API_Documentation": "",
"Account": "",
"Add": "Legg til",
"AddChild": "",
"AddFoodToShopping": "Legg til {food] i handlelisten din",
"AddToShopping": "Legg til i handleliste",
"Add_Servings_to_Shopping": "Legg til {servings} serveringer i handlelisten",
@@ -25,6 +26,10 @@
"Auto_Sort_Help": "Flytt alle ingredienser til det mest passende steget.",
"Automate": "Automatiser",
"Automation": "Automatiser",
"BatchDeleteConfirm": "",
"BatchDeleteHelp": "",
"BatchEdit": "",
"BatchEditUpdatingItemsCount": "",
"Bookmarklet": "",
"Books": "Bøker",
"CREATE_ERROR": "",
@@ -86,6 +91,7 @@
"DelayUntil": "Forsink til",
"Delete": "Slett",
"DeleteShoppingConfirm": "Er du sikker på at du fjerne alle {food} fra handlelisten?",
"DeleteSomething": "",
"Delete_All": "Slett alle",
"Delete_Food": "Slett Matrett",
"Delete_Keyword": "Slett nøkkelord",
@@ -95,6 +101,7 @@
"Disable_Amount": "Deaktiver mengde",
"Disabled": "",
"Documentation": "",
"DontChange": "",
"Download": "Last ned",
"Drag_Here_To_Delete": "Dra her for å slette",
"Edit": "Rediger",
@@ -137,6 +144,7 @@
"Hide_Keywords": "Skjul nøkkelord",
"Hide_Recipes": "Skjul oppskrifter",
"Hide_as_header": "Skjul overskrift",
"Hierarchy": "",
"Hour": "Time",
"Hours": "Timer",
"Icon": "Ikon",
@@ -184,6 +192,7 @@
"Log_Recipe_Cooking": "Logg oppskriftsbruk",
"Make_Header": "Bruk som overskrift",
"Make_Ingredient": "Bruk som ingrediens",
"ManageSubscription": "",
"Manage_Books": "Administrer bøker",
"Manage_Emails": "Administrer e-poster",
"Meal_Plan": "Måltidsplan",
@@ -192,6 +201,7 @@
"Meal_Type_Required": "Måltidstype er nødvendig",
"Meal_Types": "Måltidstyper",
"Merge": "Slå sammen",
"MergeAutomateHelp": "",
"Merge_Keyword": "Slå sammen nøkkelord",
"Message": "Melding",
"MissingProperties": "",
@@ -218,6 +228,7 @@
"New_Unit": "Ny Enhet",
"Next_Day": "Neste dag",
"Next_Period": "Neste periode",
"No": "",
"NoCategory": "Ingen kategori valgt.",
"NoMoreUndo": "Ingen endringer å angre.",
"NoUnit": "",
@@ -257,6 +268,7 @@
"Previous_Day": "Forrige dag",
"Previous_Period": "Forrige periode",
"Print": "Skriv ut",
"Private": "",
"Private_Recipe": "Privat Oppskrift",
"Private_Recipe_Help": "Oppskriften er bare vist til deg og dem du har delt den med.",
"Properties": "Egenskaper",
@@ -277,7 +289,9 @@
"Recipes": "Oppskrift",
"Recipes_In_Import": "",
"Recipes_per_page": "Oppskrifter per side",
"RemoveAllType": "",
"RemoveFoodFromShopping": "Fjern {food} fra handelisten din",
"RemoveParent": "",
"Remove_nutrition_recipe": "Fjern næringsinnhold fra oppskrift",
"Reset": "",
"Reset_Search": "Nullstill søk",
@@ -314,6 +328,7 @@
"Size": "Størrelse",
"Social_Authentication": "",
"Sort_by_new": "Sorter etter nyest",
"Space": "",
"Split_All_Steps": "",
"StartDate": "Startdato",
"Starting_Day": "Dag uken skal state på",
@@ -367,6 +382,7 @@
"Valid Until": "",
"View": "Visning",
"View_Recipes": "Vis oppskrifter",
"Visibility": "",
"Waiting": "Venter",
"Warning": "Advarsel",
"Warning_Delete_Supermarket_Category": "",
@@ -375,6 +391,7 @@
"Week_Numbers": "Ukenummer",
"Welcome": "Velkommen",
"Year": "År",
"Yes": "",
"add_keyword": "",
"additional_options": "",
"advanced": "Avansert",

View File

@@ -11,6 +11,7 @@
"Activity": "Activiteit",
"Add": "Voeg toe",
"AddAll": "Voeg alles toe",
"AddChild": "",
"AddFilter": "Voeg filter toe",
"AddFoodToShopping": "Voeg {food} toe aan je boodschappenlijst",
"AddMany": "Voeg meerdere toe",
@@ -46,6 +47,10 @@
"BaseUnit": "Basiseenheid",
"BaseUnitHelp": "Standaardeenheid om automatische eenheden om te rekenen",
"Basics": "Basisprincipes",
"BatchDeleteConfirm": "",
"BatchDeleteHelp": "",
"BatchEdit": "",
"BatchEditUpdatingItemsCount": "",
"Book": "Boek",
"Bookmarklet": "Bladwijzer",
"BookmarkletHelp1": "Sleep de onderstaande knop naar je bladwijzerbalk",
@@ -132,6 +137,7 @@
"Delete": "Verwijder",
"DeleteConfirmQuestion": "Weet je zeker dat je dit object wilt verwijderen?",
"DeleteShoppingConfirm": "Weet je zeker dat je {food} van de boodschappenlijst wil verwijderen?",
"DeleteSomething": "",
"Delete_All": "Verwijder allen",
"Delete_Food": "Verwijder voedingsmiddel",
"Delete_Keyword": "Verwijder trefwoord",
@@ -144,6 +150,7 @@
"Disable_Amount": "Schakel hoeveelheid uit",
"Disabled": "Gedeactiveerd",
"Documentation": "Documentatie",
"DontChange": "",
"Down": "Omlaag",
"Download": "Download",
"DragToUpload": "Slepen en neerzetten of klik om te selecteren",
@@ -212,6 +219,7 @@
"Hide_Keywords": "Verberg trefwoord",
"Hide_Recipes": "Verberg recepten",
"Hide_as_header": "Verberg als koptekst",
"Hierarchy": "",
"History": "Geschiedenis",
"HostedFreeVersion": "Je gebruikt de gratis versie van Tandoor",
"Hour": "Uur",
@@ -325,6 +333,7 @@
"Next": "Volgende",
"Next_Day": "Volgende dag",
"Next_Period": "Volgende periode",
"No": "",
"NoCategory": "Geen categorie geselecteerd",
"NoMoreUndo": "Geen veranderingen om ongedaan te maken.",
"NoUnit": "Geen eenheid",
@@ -377,6 +386,7 @@
"Previous_Day": "Vorige dag",
"Previous_Period": "Vorige periode",
"Print": "Afdrukken",
"Private": "",
"Private_Recipe": "Privé recept",
"Private_Recipe_Help": "Recept is alleen zichtbaar voor jou en de mensen waar je het mee gedeeld hebt.",
"Profile": "Profiel",
@@ -411,7 +421,9 @@
"Recipes_In_Import": "Recepten in je importbestand",
"Recipes_per_page": "Recepten per pagina",
"Remove": "Verwijder",
"RemoveAllType": "",
"RemoveFoodFromShopping": "Verwijder {food} van je boodschappenlijst",
"RemoveParent": "",
"Remove_nutrition_recipe": "Verwijder voedingswaarde van recept",
"Reset": "Herstel",
"ResetHelp": "Hulp herstellen",
@@ -473,6 +485,7 @@
"Source": "Bron",
"SourceImportHelp": "Importeer JSON in schema.org/recipe-formaat of html-paginas met json+ld-recepten of microdata.",
"SourceImportSubtitle": "Importeer handmatig JSON of HTML.",
"Space": "",
"SpaceLimitExceeded": "Je ruimte heeft een limiet overschreden, sommige functies zijn mogelijk beperkt.",
"SpaceLimitReached": "Deze ruimte heeft een limiet bereikt. Er kunnen geen objecten van dit type meer worden aangemaakt.",
"SpaceMemberHelp": "Voeg gebruikers toe aan je ruimte door een uitnodigingslink aan te maken en naar de persoon te sturen die je wilt toevoegen.",
@@ -575,6 +588,7 @@
"ViewLogHelp": "Geschiedenis van bekeken recepten. ",
"View_Recipes": "Bekijk Recepten",
"Viewed": "Bekeken",
"Visibility": "",
"Waiting": "Wachten",
"WaitingTime": "Wachttijd",
"WarnPageLeave": "Er zijn niet-opgeslagen wijzigingen die verloren zullen gaan. Pagina toch verlaten?",
@@ -588,6 +602,7 @@
"Welcome": "Welkom",
"WorkingTime": "Bereidingstijd",
"Year": "Jaar",
"Yes": "",
"YourSpaces": "Jouw ruimtes",
"active": "actief",
"add_keyword": "Voeg trefwoord toe",

View File

@@ -9,6 +9,7 @@
"Activity": "Aktywność",
"Add": "Dodaj",
"AddAll": "Dodaj wszystkie",
"AddChild": "",
"AddFilter": "Dodaj filtr",
"AddFoodToShopping": "Dodaj {food} do swojej listy zakupów",
"AddMany": "Dodaj wiele",
@@ -43,6 +44,10 @@
"BaseUnit": "Podstawowa jednostka",
"BaseUnitHelp": "Standardowa jednostka dla automatyczne konwersji jednostek",
"Basics": "Podstawy",
"BatchDeleteConfirm": "",
"BatchDeleteHelp": "",
"BatchEdit": "",
"BatchEditUpdatingItemsCount": "",
"Book": "Książka",
"Bookmarklet": "Skryptozakładka",
"BookmarkletHelp1": "Przeciągnij następujący przycisk do twojego paska zakładek",
@@ -116,6 +121,7 @@
"DelayUntil": "Opóźnij do",
"Delete": "Usuń",
"DeleteShoppingConfirm": "Czy na pewno chcesz usunąć wszystkie {food} z listy zakupów?",
"DeleteSomething": "",
"Delete_All": "Usuń wszystko",
"Delete_Food": "Usuń żywność",
"Delete_Keyword": "Usuń słowo kluczowe",
@@ -125,6 +131,7 @@
"Disable_Amount": "Wyłącz ilość",
"Disabled": "Wyłączone",
"Documentation": "Dokumentacja",
"DontChange": "",
"Download": "Pobieranie",
"Drag_Here_To_Delete": "Przeciągnij tutaj, aby usunąć",
"Edit": "Edytuj",
@@ -172,6 +179,7 @@
"Hide_Keywords": "Ukryj słowo kluczowe",
"Hide_Recipes": "Ukryj przepisy",
"Hide_as_header": "Ukryj jako nagłówek",
"Hierarchy": "",
"Hour": "Godzina",
"Hours": "Godziny",
"Icon": "Ikona",
@@ -220,6 +228,7 @@
"Logo": "Logo",
"Make_Header": "Utwórz nagłówek",
"Make_Ingredient": "Utwórz składnik",
"ManageSubscription": "",
"Manage_Books": "Zarządzaj książkami",
"Manage_Emails": "Zarządzaj e-mailami",
"Meal_Plan": "Plan posiłków",
@@ -228,6 +237,7 @@
"Meal_Type_Required": "Rodzaj posiłku jest wymagany",
"Meal_Types": "Rodzaje posiłków",
"Merge": "Scal",
"MergeAutomateHelp": "",
"Merge_Keyword": "Scal słowa kluczowe",
"Message": "Wiadomość",
"MissingProperties": "",
@@ -258,6 +268,7 @@
"New_Unit": "Nowa jednostka",
"Next_Day": "Następny dzień",
"Next_Period": "Następny okres",
"No": "",
"NoCategory": "Nie wybrano kategorii.",
"NoMoreUndo": "Brak zmian do wycofania.",
"NoUnit": "",
@@ -298,6 +309,7 @@
"Previous_Day": "Poprzedni dzień",
"Previous_Period": "Poprzedni okres",
"Print": "Drukuj",
"Private": "",
"Private_Recipe": "Prywatny przepis",
"Private_Recipe_Help": "Przepis jest widoczny tylko dla Ciebie i dla osób, którym jest udostępniany.",
"Properties": "Właściwości",
@@ -319,7 +331,9 @@
"Recipes": "Przepisy",
"Recipes_In_Import": "Przepisy w pliku importu",
"Recipes_per_page": "Przepisy na stronę",
"RemoveAllType": "",
"RemoveFoodFromShopping": "Usuń {food} z listy zakupów",
"RemoveParent": "",
"Remove_nutrition_recipe": "Usuń wartości odżywcze z przepisu",
"Reset": "Resetowanie",
"Reset_Search": "Resetuj wyszukiwanie",
@@ -358,6 +372,7 @@
"Size": "Rozmiar",
"Social_Authentication": "Uwierzytelnianie społecznościowe",
"Sort_by_new": "Sortuj według nowych",
"Space": "",
"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.",
"StartDate": "Data początkowa",
@@ -416,6 +431,7 @@
"Valid Until": "Ważne do",
"View": "Pogląd",
"View_Recipes": "Przeglądaj przepisy",
"Visibility": "",
"Waiting": "Oczekiwanie",
"Warning": "Ostrzeżenie",
"Warning_Delete_Supermarket_Category": "Usunięcie kategorii supermarketu spowoduje również usunięcie wszystkich relacji z żywnością. Jesteś pewny?",
@@ -424,6 +440,7 @@
"Week_Numbers": "Numery tygodni",
"Welcome": "Witamy",
"Year": "Rok",
"Yes": "",
"add_keyword": "Dodaj słowo kluczowe",
"additional_options": "Opcje dodatkowe",
"advanced": "Zaawansowany",

View File

@@ -2,6 +2,7 @@
"API_Browser": "",
"API_Documentation": "",
"Add": "Adicionar",
"AddChild": "",
"AddFoodToShopping": "Adicionar {food} à sua lista de compras",
"AddToShopping": "Adicionar á lista de compras",
"Add_Servings_to_Shopping": "Adicionar {servings} doses ás compras",
@@ -21,6 +22,10 @@
"Auto_Sort_Help": "Mover todos os ingredientes para o passo mais indicado.",
"Automate": "Automatizar",
"Automation": "Automação",
"BatchDeleteConfirm": "",
"BatchDeleteHelp": "",
"BatchEdit": "",
"BatchEditUpdatingItemsCount": "",
"Books": "Livros",
"CREATE_ERROR": "",
"Calculator": "Calculadora",
@@ -70,12 +75,14 @@
"DelayUntil": "",
"Delete": "Apagar",
"DeleteShoppingConfirm": "Tem a certeza que pretende remover toda {food} da sua lista de compras?",
"DeleteSomething": "",
"Delete_All": "Apagar todos",
"Delete_Food": "Eliminar comida",
"Delete_Keyword": "Eliminar Palavra Chave",
"Description": "Descrição",
"Description_Replace": "Substituir descrição",
"Disable_Amount": "Desativar quantidade",
"DontChange": "",
"Download": "Transferência",
"Drag_Here_To_Delete": "Arraste para aqui para eliminar",
"Edit": "Editar",
@@ -117,6 +124,7 @@
"Hide_Keywords": "Esconder palavra-chave",
"Hide_Recipes": "Esconder Receitas",
"Hide_as_header": "Esconder como cabeçalho",
"Hierarchy": "",
"Icon": "Ícone",
"IgnoreAccents": "",
"IgnoreAccentsHelp": "",
@@ -151,6 +159,7 @@
"Log_Recipe_Cooking": "Registrar Receitas de Culinária",
"Make_Header": "Tornar cabeçalho",
"Make_Ingredient": "Fazer ingrediente",
"ManageSubscription": "",
"Manage_Books": "Gerenciar Livros",
"Meal_Plan": "Plano de Refeição",
"Meal_Plan_Days": "Planos de alimentação futuros",
@@ -158,6 +167,7 @@
"Meal_Type_Required": "Tipo de refeição é necessário",
"Meal_Types": "Tipos de refeições",
"Merge": "Juntar",
"MergeAutomateHelp": "",
"Merge_Keyword": "Unir palavra-chave",
"MissingProperties": "",
"Month": "Mês",
@@ -178,6 +188,7 @@
"New_Unit": "Nova Unidade",
"Next_Day": "Dia seguinte",
"Next_Period": "Próximo período",
"No": "",
"NoCategory": "Nenhuma categoria selecionada.",
"NoMoreUndo": "Nenhuma alteração para ser desfeita.",
"NoUnit": "",
@@ -215,6 +226,7 @@
"Previous_Day": "Dia anterior",
"Previous_Period": "Período anterior",
"Print": "Imprimir",
"Private": "",
"Private_Recipe": "Receita Privada",
"Private_Recipe_Help": "A receita só é mostrada ás pessoas com que foi partilhada.",
"Properties": "Propriedades",
@@ -235,7 +247,9 @@
"Recipe_Image": "Imagem da Receita",
"Recipes": "Receitas",
"Recipes_per_page": "Receitas por página",
"RemoveAllType": "",
"RemoveFoodFromShopping": "Remover {food} da sua lista de compras",
"RemoveParent": "",
"Remove_nutrition_recipe": "Remover valor nutricional da receita",
"Reset": "Reiniciar",
"Reset_Search": "Repor Pesquisa",
@@ -265,6 +279,7 @@
"Show_as_header": "Mostrar como cabeçalho",
"Size": "Tamanho",
"Sort_by_new": "Ordenar por mais recente",
"Space": "",
"StartDate": "Data de início",
"Starting_Day": "Dia de início da semana",
"StartsWith": "",
@@ -308,12 +323,14 @@
"User": "Utilizador",
"View": "Vista",
"View_Recipes": "Ver Receitas",
"Visibility": "",
"Waiting": "Em espera",
"Warning": "Aviso",
"Week": "Semana",
"Week_Numbers": "Números das semanas",
"Welcome": "Bem-vindo",
"Year": "Ano",
"Yes": "",
"add_keyword": "Adicionar Palavra Chave",
"advanced": "",
"advanced_search_settings": "Configurações Avançadas de Pesquisa",

View File

@@ -11,6 +11,7 @@
"Activity": "Atividade",
"Add": "Adicionar",
"AddAll": "Adicionar todos",
"AddChild": "",
"AddFilter": "Adicionar Filtro",
"AddFoodToShopping": "Incluir {food} na sua lista de compras",
"AddMany": "Adicionar muitos",
@@ -44,6 +45,10 @@
"BaseUnit": "Unidade Base",
"BaseUnitHelp": "Unidade padrão para conversão de unidades",
"Basics": "Básicos",
"BatchDeleteConfirm": "",
"BatchDeleteHelp": "",
"BatchEdit": "",
"BatchEditUpdatingItemsCount": "",
"Book": "Livro",
"Bookmarklet": "Marcador",
"BookmarkletHelp1": "Arraste o seguinte botão para sua barra de favoritos",
@@ -130,6 +135,7 @@
"Delete": "Deletar",
"DeleteConfirmQuestion": "Tem certeza que quer excluir esse objeto?",
"DeleteShoppingConfirm": "Tem certeza que deseja remover todas {food} de sua lista de compras?",
"DeleteSomething": "",
"Delete_All": "Excluir tudo",
"Delete_Food": "Deletar Comida",
"Delete_Keyword": "Deletar palavra-chave",
@@ -142,6 +148,7 @@
"Disable_Amount": "Desabilitar Quantidade",
"Disabled": "Desabilitado",
"Documentation": "Documentação",
"DontChange": "",
"Down": "Abaixo",
"Download": "Baixar",
"DragToUpload": "Clique e arraste ou clique para selecionar",
@@ -210,6 +217,7 @@
"Hide_Keywords": "Esconder palavra-chave",
"Hide_Recipes": "Esconder Receitas",
"Hide_as_header": "Esconder cabeçalho",
"Hierarchy": "",
"History": "Histórico",
"HostedFreeVersion": "Você está utilizando a versão gratuita do Tandoor",
"Hour": "Hora",
@@ -283,6 +291,7 @@
"Meal_Type_Required": "Tipo de comida é obrigatório",
"Meal_Types": "Tipos de Comida",
"Merge": "Mesclar",
"MergeAutomateHelp": "",
"Merge_Keyword": "Mesclar palavra-chave",
"Message": "Mensagem",
"MissingProperties": "",
@@ -313,6 +322,7 @@
"New_Unit": "Nova Unidade",
"Next_Day": "Próximo Dia",
"Next_Period": "Próximo Período",
"No": "",
"NoCategory": "Nenhuma categoria selecionada.",
"NoMoreUndo": "Nenhuma alteração para desfazer.",
"NoUnit": "",
@@ -350,6 +360,7 @@
"Previous_Day": "Dia Anterior",
"Previous_Period": "Período Anterior",
"Print": "Imprimir",
"Private": "",
"Private_Recipe": "Receita privada",
"Private_Recipe_Help": "Receita é visível somente para você e para pessoas compartilhadas.",
"Properties": "Propriedades",
@@ -371,7 +382,9 @@
"Recipes": "Receitas",
"Recipes_In_Import": "Receitas no seu arquivo de importação",
"Recipes_per_page": "Receitas por página",
"RemoveAllType": "",
"RemoveFoodFromShopping": "Remover {food} da sua lista de compras",
"RemoveParent": "",
"Remove_nutrition_recipe": "Deletar dados nutricionais da receita",
"Reset": "Reiniciar",
"Reset_Search": "Resetar Busca",
@@ -407,6 +420,7 @@
"Size": "Tamanho",
"Social_Authentication": "Autenticação social",
"Sort_by_new": "Ordenar por novos",
"Space": "",
"Space_Cosmetic_Settings": "Algumas configurações cosméticas podem ser alteradas pelos administradores do espaço e substituirão as configurações do cliente para esse espaço.",
"Split_All_Steps": "Divida todas as linhas em etapas separadas.",
"StartDate": "Data Início",
@@ -461,6 +475,7 @@
"Valid Until": "Válido Até",
"View": "Visualizar",
"View_Recipes": "Ver Receitas",
"Visibility": "",
"Waiting": "Espera",
"Warning": "Alerta",
"Warning_Delete_Supermarket_Category": "Excluir uma categoria de supermercado também excluirá todas as relações com alimentos. Tem certeza?",
@@ -469,6 +484,7 @@
"Week_Numbers": "Números da Semana",
"Welcome": "Bem vindo",
"Year": "Ano",
"Yes": "",
"add_keyword": "Incluir Palavra-Chave",
"additional_options": "Opções Adicionais",
"advanced": "Avançado",

View File

@@ -4,6 +4,7 @@
"API_Documentation": "",
"Account": "Cont",
"Add": "Adaugă",
"AddChild": "",
"AddFoodToShopping": "Adăugă {food} în lista de cumpărături",
"AddToShopping": "Adaugă la lista de cumpărături",
"Add_Servings_to_Shopping": "Adăugă {servings} porții la cumpărături",
@@ -25,6 +26,10 @@
"Auto_Sort_Help": "Mutați toate ingredientele la cel mai potrivit pas.",
"Automate": "Automatizat",
"Automation": "Automatizare",
"BatchDeleteConfirm": "",
"BatchDeleteHelp": "",
"BatchEdit": "",
"BatchEditUpdatingItemsCount": "",
"Bookmarklet": "Marcaj",
"Books": "Cărți",
"CREATE_ERROR": "",
@@ -76,6 +81,7 @@
"DelayUntil": "Amână până la",
"Delete": "Șterge",
"DeleteShoppingConfirm": "Sunteți sigur că doriți să eliminați toate {food} din lista de cumpărături?",
"DeleteSomething": "",
"Delete_Food": "Ștergere mâncare",
"Delete_Keyword": "Ștergere cuvânt cheie",
"Description": "Descriere",
@@ -84,6 +90,7 @@
"Disable_Amount": "Dezactivare cantitate",
"Disabled": "Dezactivat",
"Documentation": "Documentație",
"DontChange": "",
"Download": "Descarcă",
"Drag_Here_To_Delete": "Mută aici pentru a șterge",
"Edit": "Editează",
@@ -124,6 +131,7 @@
"Hide_Keywords": "Ascunde cuvânt cheie",
"Hide_Recipes": "Ascunde rețetele",
"Hide_as_header": "Ascunderea ca antet",
"Hierarchy": "",
"Hour": "Oră",
"Hours": "Ore",
"Icon": "Iconiță",
@@ -169,6 +177,7 @@
"Log_Recipe_Cooking": "Jurnalul rețetelor de pregătire",
"Make_Header": "Creare antet",
"Make_Ingredient": "Create ingredient",
"ManageSubscription": "",
"Manage_Books": "Gestionarea cărților",
"Manage_Emails": "Gestionarea e-mailurilor",
"Meal_Plan": "Plan de alimentare",
@@ -177,6 +186,7 @@
"Meal_Type_Required": "Tipul mesei este necesar",
"Meal_Types": "Tipuri de mese",
"Merge": "Unire",
"MergeAutomateHelp": "",
"Merge_Keyword": "Unește cuvânt cheie",
"Message": "Mesaj",
"MissingProperties": "",
@@ -203,6 +213,7 @@
"New_Unit": "Unitate nouă",
"Next_Day": "Ziua următoare",
"Next_Period": "Perioada următoare",
"No": "",
"NoCategory": "Nicio categorie selectată.",
"NoUnit": "",
"No_ID": "ID-ul nu a fost găsit, nu se poate șterge.",
@@ -238,6 +249,7 @@
"Previous_Day": "Ziua precedentă",
"Previous_Period": "Perioada precedentă",
"Print": "Tipărește",
"Private": "",
"Private_Recipe": "Rețetă privată",
"Private_Recipe_Help": "Rețeta este arătată doar ție și oamenilor cu care este împărtășită.",
"Protected": "Protejat",
@@ -254,7 +266,9 @@
"Recipes": "Rețete",
"Recipes_In_Import": "Rețete în fișierul de import",
"Recipes_per_page": "Rețete pe pagină",
"RemoveAllType": "",
"RemoveFoodFromShopping": "Șterge {food} din lista de cumpărături",
"RemoveParent": "",
"Remove_nutrition_recipe": "Ștergere a nutriției din rețetă",
"Reset": "Resetare",
"Reset_Search": "Resetarea căutării",
@@ -288,6 +302,7 @@
"Size": "Marime",
"Social_Authentication": "Autentificare socială",
"Sort_by_new": "Sortare după nou",
"Space": "",
"Split_All_Steps": "Împărțiți toate rândurile în pași separați.",
"Starting_Day": "Ziua de început a săptămânii",
"StartsWith": "",
@@ -337,6 +352,7 @@
"Valid Until": "Valabil până la",
"View": "Vizualizare",
"View_Recipes": "Vizionare rețete",
"Visibility": "",
"Waiting": "Așteptare",
"Warning": "Atenționare",
"Warning_Delete_Supermarket_Category": "Ștergerea unei categorii de supermarketuri va șterge, de asemenea, toate relațiile cu alimentele. Sunteți sigur?",
@@ -344,6 +360,7 @@
"Week": "Săptămână",
"Week_Numbers": "Numerele săptămânii",
"Year": "An",
"Yes": "",
"add_keyword": "Adăugare cuvânt cheie",
"additional_options": "Opțiuni suplimentare",
"advanced": "Avansat",

View File

@@ -11,6 +11,7 @@
"Activity": "Активность",
"Add": "Добавить",
"AddAll": "Добавить все",
"AddChild": "",
"AddFilter": "Добавить фильтр",
"AddFoodToShopping": "Добавить {food} в ваш список покупок",
"AddMany": "Добавить несколько",
@@ -45,6 +46,10 @@
"BaseUnit": "Базовая единица измерения",
"BaseUnitHelp": "Стандартная единица для автоконвертации",
"Basics": "Основные понятия",
"BatchDeleteConfirm": "",
"BatchDeleteHelp": "",
"BatchEdit": "",
"BatchEditUpdatingItemsCount": "",
"Book": "Книга",
"Bookmarklet": "Букмарклет",
"BookmarkletHelp1": "Перетащите эту кнопку в панель закладок",
@@ -131,6 +136,7 @@
"Delete": "Удалить",
"DeleteConfirmQuestion": "Вы уверены, что хотите удалить этот объект?",
"DeleteShoppingConfirm": "Вы уверены, что хотите удалить все {food} из вашего списка покупок?",
"DeleteSomething": "",
"Delete_All": "Удалить всё",
"Delete_Food": "Удалить элемент",
"Delete_Keyword": "Удалить ключевое слово",
@@ -143,6 +149,7 @@
"Disable_Amount": "Деактивировать количество",
"Disabled": "Отключено",
"Documentation": "Документация",
"DontChange": "",
"Down": "Вниз",
"Download": "Загрузить",
"DragToUpload": "Перетащите сюда или нажмите для выбора",
@@ -211,6 +218,7 @@
"Hide_Keywords": "Скрыть ключевое слово",
"Hide_Recipes": "Скрыть рецепт",
"Hide_as_header": "Скрыть заголовок",
"Hierarchy": "",
"History": "История",
"HostedFreeVersion": "Текущая версия: бесплатная",
"Hour": "Час",
@@ -323,6 +331,7 @@
"Next": "Следующий",
"Next_Day": "Следующий день",
"Next_Period": "Следующий период",
"No": "",
"NoCategory": "Категория не выбрана.",
"NoMoreUndo": "Нет изменений, которые можно было бы отменить.",
"No_ID": "ID не найден, удаление не возможно.",
@@ -374,6 +383,7 @@
"Previous_Day": "Предыдущий день",
"Previous_Period": "Предыдущий период",
"Print": "Распечатать",
"Private": "",
"Private_Recipe": "Приватный Рецепт",
"Private_Recipe_Help": "Рецепт виден только вам и людям, с которыми им поделились.",
"Profile": "Профиль",
@@ -408,7 +418,9 @@
"Recipes_In_Import": "Рецепты в вашем файле импорта",
"Recipes_per_page": "Рецептов на странице",
"Remove": "Удалить",
"RemoveAllType": "",
"RemoveFoodFromShopping": "Удалить {food} из вашего списка покупок",
"RemoveParent": "",
"Remove_nutrition_recipe": "Уберите питательные вещества из рецепта",
"Reset": "Сбросить",
"ResetHelp": "Сбросить подсказки",
@@ -470,6 +482,7 @@
"Source": "Источник",
"SourceImportHelp": "Импортируйте JSON в формате schema.org/recipe или HTML-страницы с рецептами в формате JSON-LD или микроданных.",
"SourceImportSubtitle": "Импортировать JSON или HTML вручную.",
"Space": "",
"SpaceLimitExceeded": "Ваше пространство превысило один из лимитов, некоторые функции могут быть ограничены.",
"SpaceLimitReached": "В этом пространстве достигнут лимит. Новые объекты данного типа создавать нельзя.",
"SpaceMemberHelp": "Для добавления пользователей создайте пригласительную ссылку и передайте её человеку, которого хотите пригласить.",
@@ -572,6 +585,7 @@
"ViewLogHelp": "История просмотренных рецептов. ",
"View_Recipes": "Просмотр рецепта",
"Viewed": "Просмотрено",
"Visibility": "",
"Waiting": "Ожидание",
"WaitingTime": "Время ожидания",
"WarnPageLeave": "Есть несохраненные изменения, которые будут потеряны. Всё равно покинуть страницу?",
@@ -585,6 +599,7 @@
"Welcome": "Добро пожаловать",
"WorkingTime": "Время работы",
"Year": "Год",
"Yes": "",
"YourSpaces": "Ваши пространства",
"active": "активно",
"add_keyword": "Добавить ключевое слово",

View File

@@ -11,6 +11,7 @@
"Activity": "Aktivnost",
"Add": "Dodaj",
"AddAll": "Dodaj vse",
"AddChild": "",
"AddFilter": "Dodaj filter",
"AddFoodToShopping": "Dodaj {food} v nakupovalni listek",
"AddMany": "Dodaj veliko",
@@ -45,6 +46,10 @@
"BaseUnit": "Osnovna enota",
"BaseUnitHelp": "Standardna enota za samodejno pretvorbo enot",
"Basics": "Osnove",
"BatchDeleteConfirm": "",
"BatchDeleteHelp": "",
"BatchEdit": "",
"BatchEditUpdatingItemsCount": "",
"Book": "Knjiga",
"Bookmarklet": "Zaznamek",
"BookmarkletHelp1": "Povlecite naslednji gumb v vrstico z zaznamki",
@@ -131,6 +136,7 @@
"Delete": "Izbriši",
"DeleteConfirmQuestion": "Ali ste prepričani, da želite izbrisati ta objekt?",
"DeleteShoppingConfirm": "Si prepričan/a, da želiš odstraniti VSO {food} iz nakupovalnega listka?",
"DeleteSomething": "",
"Delete_All": "Izbriši vse",
"Delete_Food": "Izbriši hrano",
"Delete_Keyword": "Izbriši ključno besedo",
@@ -143,6 +149,7 @@
"Disable_Amount": "Onemogoči količino",
"Disabled": "Onemogočeno",
"Documentation": "Dokumentacija",
"DontChange": "",
"Down": "Navzdol",
"Download": "Prenesi",
"DragToUpload": "Povlecite in spustite ali kliknite za izbiro",
@@ -211,6 +218,7 @@
"Hide_Keywords": "Skrij ključno besedo",
"Hide_Recipes": "Skrij recept",
"Hide_as_header": "Skrij kot glavo",
"Hierarchy": "",
"History": "Zgodovina",
"HostedFreeVersion": "Uporabljate brezplačno različico Tandoorja",
"Hour": "Ura",
@@ -324,6 +332,7 @@
"Next": "Naprej",
"Next_Day": "Naslednji Dan",
"Next_Period": "Naslednje obdobje",
"No": "",
"NoCategory": "Brez kategorije",
"NoMoreUndo": "Ni sprememb, ki bi jih bilo mogoče razveljaviti.",
"NoUnit": "Brez enote",
@@ -376,6 +385,7 @@
"Previous_Day": "Prejšnji Dan",
"Previous_Period": "Prejšnje obdobje",
"Print": "Natisni",
"Private": "",
"Private_Recipe": "Zasebni Recept",
"Private_Recipe_Help": "Recept je prikazan samo vam in osebam, s katerimi ga delite.",
"Profile": "Profil",
@@ -410,7 +420,9 @@
"Recipes_In_Import": "Recepti v vaši uvozni datoteki",
"Recipes_per_page": "Receptov na stran",
"Remove": "Odstrani",
"RemoveAllType": "",
"RemoveFoodFromShopping": "Odstrani {food} iz nakupovalnega listka",
"RemoveParent": "",
"Remove_nutrition_recipe": "Receptu izbriši hranilno vrednost",
"Reset": "Ponastavi",
"ResetHelp": "Pomoč pri ponastavitvi",
@@ -472,6 +484,7 @@
"Source": "Vir",
"SourceImportHelp": "Uvozite JSON v formatu schema.org/recipe ali na straneh html z receptom json+ld ali mikropodatki.",
"SourceImportSubtitle": "Ročno uvozite JSON ali HTML.",
"Space": "",
"SpaceLimitExceeded": "Vaš prostor je presegel eno od svojih omejitev, nekatere funkcije so morda omejene.",
"SpaceLimitReached": "Ta prostor je dosegel omejitev. Te vrste predmetov ni mogoče ustvariti več.",
"SpaceMemberHelp": "Dodajte uporabnike v svoj prostor tako, da ustvarite povezavo za povabilo in jo pošljete osebi, ki jo želite dodati.",
@@ -574,6 +587,7 @@
"ViewLogHelp": "Zgodovina ogledanih receptov. ",
"View_Recipes": "Preglej recepte",
"Viewed": "Ogledano",
"Visibility": "",
"Waiting": "Čakanje",
"WaitingTime": "Čakalni čas",
"WarnPageLeave": "Nekatere spremembe niso shranjene in bodo izgubljene. Želite vseeno zapustiti stran?",
@@ -587,6 +601,7 @@
"Welcome": "Dobrodošli",
"WorkingTime": "Delovni čas",
"Year": "Leto",
"Yes": "",
"YourSpaces": "Vaši prostori",
"active": "aktiven",
"add_keyword": "Dodaj ključno besedo",

View File

@@ -10,6 +10,7 @@
"Activity": "Aktivitet",
"Add": "Lägg till",
"AddAll": "Lägg till alla",
"AddChild": "",
"AddFilter": "Lägg till filter",
"AddFoodToShopping": "Lägg till {food} på din inköpslista",
"AddMany": "Lägg till flera",
@@ -44,6 +45,10 @@
"BaseUnit": "Basenhet",
"BaseUnitHelp": "Standardenhet för automatisk enhetsomvandling",
"Basics": "Grunderna",
"BatchDeleteConfirm": "",
"BatchDeleteHelp": "",
"BatchEdit": "",
"BatchEditUpdatingItemsCount": "",
"Book": "Bok",
"Bookmarklet": "Bokmärke",
"BookmarkletHelp1": "Dra följande knapp till ditt bokmärkesfält",
@@ -127,6 +132,7 @@
"DelayUntil": "Fördröjning till",
"Delete": "Radera",
"DeleteShoppingConfirm": "Är du säker på att du vill ta bort all {food} från inköpslistan?",
"DeleteSomething": "",
"Delete_All": "Radera alla",
"Delete_Food": "Ta bort livsmedel",
"Delete_Keyword": "Ta bort nyckelord",
@@ -136,6 +142,7 @@
"Disable_Amount": "Inaktivera belopp",
"Disabled": "Inaktiverad",
"Documentation": "Dokumentation",
"DontChange": "",
"Download": "Ladda ned",
"Drag_Here_To_Delete": "Dra hit för att radera",
"Edit": "Redigera",
@@ -183,6 +190,7 @@
"Hide_Keywords": "Dölj nyckelord",
"Hide_Recipes": "Dölj recept",
"Hide_as_header": "Göm som rubrik",
"Hierarchy": "",
"Hour": "Timme",
"Hours": "Timmar",
"Icon": "Ikon",
@@ -231,6 +239,7 @@
"Logo": "Logga",
"Make_Header": "Skapa rubrik",
"Make_Ingredient": "Skapa ingrediens",
"ManageSubscription": "",
"Manage_Books": "Hantera böcker",
"Manage_Emails": "Hantera mejladresser",
"Meal_Plan": "Måltidsplanering",
@@ -239,6 +248,7 @@
"Meal_Type_Required": "Måltidstyp är obligatorisk",
"Meal_Types": "Måltidstyper",
"Merge": "Slå samman",
"MergeAutomateHelp": "",
"Merge_Keyword": "Slå samman nyckelord",
"Message": "Meddelande",
"MissingProperties": "",
@@ -269,6 +279,7 @@
"New_Unit": "Ny enhet",
"Next_Day": "Nästa dag",
"Next_Period": "Nästa period",
"No": "",
"NoCategory": "Ingen kategori vald.",
"NoMoreUndo": "Inga ändringar att ångra.",
"NoUnit": "",
@@ -309,6 +320,7 @@
"Previous_Day": "Föregående dag",
"Previous_Period": "Föregående period",
"Print": "Skriv ut",
"Private": "",
"Private_Recipe": "Privat Recept",
"Private_Recipe_Help": "Receptet visas bara för dig och personer som det delas med.",
"Properties": "Egenskaper",
@@ -330,7 +342,9 @@
"Recipes": "Recept",
"Recipes_In_Import": "Recept i din importfil",
"Recipes_per_page": "Recept per sida",
"RemoveAllType": "",
"RemoveFoodFromShopping": "Ta bort {mat} från din inköpslista",
"RemoveParent": "",
"Remove_nutrition_recipe": "Ta bort näring från receptet",
"Reset": "Återställ",
"Reset_Search": "Rensa sök",
@@ -369,6 +383,7 @@
"Size": "Storlek",
"Social_Authentication": "Social autentisering",
"Sort_by_new": "Sortera efter ny",
"Space": "",
"Space_Cosmetic_Settings": "Vissa kosmetiska inställningar kan ändras av hushålls-administratörer och skriver över klientinställningar för det hushållet.",
"Split_All_Steps": "Dela upp alla rader i separata steg.",
"StartDate": "Startdatum",
@@ -427,6 +442,7 @@
"Valid Until": "Giltig till",
"View": "Visa",
"View_Recipes": "Visa recept",
"Visibility": "",
"Waiting": "Väntan",
"Warning": "Varning",
"Warning_Delete_Supermarket_Category": "Om du tar bort en mataffärskategori raderas också alla relationer till livsmedel. Är du säker?",
@@ -435,6 +451,7 @@
"Week_Numbers": "Veckonummer",
"Welcome": "Välkommen",
"Year": "År",
"Yes": "",
"add_keyword": "Lägg till nyckelord",
"additional_options": "Ytterligare alternativ",
"advanced": "Avancerat",

View File

@@ -4,6 +4,7 @@
"API_Documentation": "",
"Account": "Hesap",
"Add": "Ekle",
"AddChild": "",
"AddFoodToShopping": "{food}'ı alışveriş listenize ekleyin",
"AddToShopping": "Alışveriş listesine ekle",
"Add_Servings_to_Shopping": "Alışverişe {servings} Porsiyon Ekle",
@@ -26,6 +27,10 @@
"Automate": "Otomatikleştir",
"Automation": "Otomasyon",
"Back": "Geri",
"BatchDeleteConfirm": "",
"BatchDeleteHelp": "",
"BatchEdit": "",
"BatchEditUpdatingItemsCount": "",
"Bookmarklet": "Yer İmi",
"Books": "Kitaplar",
"CREATE_ERROR": "",
@@ -90,6 +95,7 @@
"DelayUntil": "Şu Zamana Kadar Geciktir",
"Delete": "Sil",
"DeleteShoppingConfirm": "Tüm {food} alışveriş listesinden kaldırmak istediğinizden emin misiniz?",
"DeleteSomething": "",
"Delete_All": "Tümünü sil",
"Delete_Food": "Yiyeceği Sil",
"Delete_Keyword": "Anahtar Kelimeyi Sil",
@@ -99,6 +105,7 @@
"Disable_Amount": "Tutarı Devre Dışı Bırak",
"Disabled": "Devre Dışı",
"Documentation": "Dokümantasyon",
"DontChange": "",
"Download": "İndir",
"Drag_Here_To_Delete": "Silmek için buraya sürükleyin",
"Edit": "Düzenle",
@@ -146,6 +153,7 @@
"Hide_Keywords": "Anahtar Kelimeyi Gizle",
"Hide_Recipes": "Tarifleri Gizle",
"Hide_as_header": "Başlık olarak gizle",
"Hierarchy": "",
"Hour": "Saat",
"Hours": "Saatler",
"Icon": "Simge",
@@ -194,6 +202,7 @@
"Logo": "Logo",
"Make_Header": "Başlık Oluştur",
"Make_Ingredient": "Malzeme Oluştur",
"ManageSubscription": "",
"Manage_Books": "Kitapları Yönet",
"Manage_Emails": "E-postaları Yönet",
"Meal_Plan": "Yemek Planı",
@@ -202,6 +211,7 @@
"Meal_Type_Required": "Yemek türü gereklidir",
"Meal_Types": "Yemek türleri",
"Merge": "Birleştir",
"MergeAutomateHelp": "",
"Merge_Keyword": "Anahtar Kelimeyi Birleştir",
"Message": "Mesaj",
"MissingProperties": "",
@@ -232,6 +242,7 @@
"New_Unit": "Yeni Birim",
"Next_Day": "Sonraki Gün",
"Next_Period": "Sonraki Dönem",
"No": "",
"NoCategory": "Hiçbir kategori seçilmedi.",
"NoMoreUndo": "Yapılacak değişiklik yok.",
"NoUnit": "",
@@ -272,6 +283,7 @@
"Previous_Day": "Önceki Gün",
"Previous_Period": "Önceki Dönem",
"Print": "Yazdır",
"Private": "",
"Private_Recipe": "Özel Tarif",
"Private_Recipe_Help": "Tarif yalnızca size ve paylaştığınız kişilere gösterilir.",
"Properties": "Özellikler",
@@ -293,7 +305,9 @@
"Recipes": "Tarifler",
"Recipes_In_Import": "İçe aktarma dosyanızdaki tarifler",
"Recipes_per_page": "Sayfa Başına Tarif",
"RemoveAllType": "",
"RemoveFoodFromShopping": "{food}'ı alışveriş listenizden çıkarın",
"RemoveParent": "",
"Remove_nutrition_recipe": "Tariften besin değeri sil",
"Reset": "Sıfırla",
"Reset_Search": "Aramayı Sıfırla",
@@ -332,6 +346,7 @@
"Size": "Boyut",
"Social_Authentication": "Sosyal Kimlik Doğrulama",
"Sort_by_new": "Yeniye göre sırala",
"Space": "",
"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.",
"StartDate": "Başlangıç Tarihi",
@@ -390,6 +405,7 @@
"Valid Until": "Geçerlilik Tarihi",
"View": "Görüntüle",
"View_Recipes": "Tarifleri Görüntüle",
"Visibility": "",
"Waiting": "Bekleniyor",
"Warning": "Uyarı",
"Warning_Delete_Supermarket_Category": "Bir market kategorisinin silinmesi, gıdalarla olan tüm ilişkileri de silecektir. Emin misiniz?",
@@ -398,6 +414,7 @@
"Week_Numbers": "Hafta numaraları",
"Welcome": "Hoşgeldiniz",
"Year": "Yıl",
"Yes": "",
"add_keyword": "Anahtar Kelime Ekle",
"additional_options": "Ek Seçenekler",
"advanced": "Gelişmiş",

View File

@@ -2,6 +2,7 @@
"API_Browser": "",
"API_Documentation": "",
"Add": "Додати",
"AddChild": "",
"AddFoodToShopping": "Додати {food} до вашого списку покупок",
"AddToShopping": "Додати до списку покупок",
"Add_Servings_to_Shopping": "Додати {servings} Порції до Покупок",
@@ -23,6 +24,10 @@
"Auto_Sort_Help": "Перемістити всі інгредієнти до більш підходящого кроку.",
"Automate": "Автоматично",
"Automation": "Автоматизація",
"BatchDeleteConfirm": "",
"BatchDeleteHelp": "",
"BatchEdit": "",
"BatchEditUpdatingItemsCount": "",
"Bookmarklet": "",
"Books": "Книги",
"CREATE_ERROR": "",
@@ -77,6 +82,7 @@
"DelayUntil": "",
"Delete": "Видалити",
"DeleteShoppingConfirm": "Ви впевнені, що хочете видалити {food} з вашого списку покупок?",
"DeleteSomething": "",
"Delete_All": "Видалити усе",
"Delete_Food": "Видалити Їжу",
"Delete_Keyword": "Видалити Ключове слово",
@@ -84,6 +90,7 @@
"Description_Replace": "Замінити Опис",
"Disable_Amount": "Виключити Кількість",
"Documentation": "",
"DontChange": "",
"Download": "Скачати",
"Drag_Here_To_Delete": "Перемістіть сюди, щоб видалити",
"Edit": "Редагувати",
@@ -127,6 +134,7 @@
"Hide_Keywords": "Сховати Ключове слово",
"Hide_Recipes": "Сховати Рецепти",
"Hide_as_header": "Приховати як заголовок",
"Hierarchy": "",
"Icon": "Іконка",
"IgnoreAccents": "",
"IgnoreAccentsHelp": "",
@@ -168,6 +176,7 @@
"Log_Recipe_Cooking": "Журнал приготування",
"Make_Header": "Створити Заголовок",
"Make_Ingredient": "Створити Інгрідієнт",
"ManageSubscription": "",
"Manage_Books": "Управління Книжкою",
"Meal_Plan": "План Харчування",
"Meal_Plan_Days": "Майбутній план харчування",
@@ -175,6 +184,7 @@
"Meal_Type_Required": "Тип страви є обов'язковим",
"Meal_Types": "Типи страви",
"Merge": "Об'єднати",
"MergeAutomateHelp": "",
"Merge_Keyword": "Об'єднати Ключове слово",
"MissingProperties": "",
"Month": "Місяць",
@@ -198,6 +208,7 @@
"New_Unit": "Нова Одиниця",
"Next_Day": "Наступний День",
"Next_Period": "Наступний період",
"No": "",
"NoCategory": "Жодна категорія не вибрана.",
"NoMoreUndo": "Відсутні зміни для скасування.",
"NoUnit": "",
@@ -236,6 +247,7 @@
"Previous_Day": "Попередній День",
"Previous_Period": "Попередній Період",
"Print": "Друкувати",
"Private": "",
"Private_Recipe": "Приватний Рецепт",
"Private_Recipe_Help": "Рецепт показаний тільки Вам і тими з ким ви поділилися їм.",
"Properties": "Властивості",
@@ -257,7 +269,9 @@
"Recipes": "Рецепти",
"Recipes_In_Import": "",
"Recipes_per_page": "Кількість Рецептів на Сторінку",
"RemoveAllType": "",
"RemoveFoodFromShopping": "Видалити {food} з вашого списку покупок",
"RemoveParent": "",
"Remove_nutrition_recipe": "Видалити харчову цінність з рецепта",
"Reset": "",
"Reset_Search": "Скинути Пошук",
@@ -289,6 +303,7 @@
"Single": "",
"Size": "Розмір",
"Sort_by_new": "Сортувати за новими",
"Space": "",
"StartDate": "Початкова дата",
"Starting_Day": "Початковий день тижня",
"StartsWith": "",
@@ -333,6 +348,7 @@
"User": "",
"View": "Перегляд",
"View_Recipes": "Подивитися Рецепт",
"Visibility": "",
"Waiting": "Очікування",
"Warning": "Застереження",
"Warning_Delete_Supermarket_Category": "",
@@ -341,6 +357,7 @@
"Week_Numbers": "Номер тижня",
"Welcome": "Вітаємо",
"Year": "Рік",
"Yes": "",
"add_keyword": "",
"additional_options": "",
"advanced": "",

View File

@@ -4,6 +4,7 @@
"API_Documentation": "",
"Account": "账户",
"Add": "添加",
"AddChild": "",
"AddFoodToShopping": "添加 {food} 到购物清单",
"AddToShopping": "添加到购物清单",
"Add_Servings_to_Shopping": "添加 {servings} 份到购物",
@@ -26,6 +27,10 @@
"Automate": "自动化",
"Automation": "自动化",
"Back": "后退",
"BatchDeleteConfirm": "",
"BatchDeleteHelp": "",
"BatchEdit": "",
"BatchEditUpdatingItemsCount": "",
"Bookmarklet": "书签",
"Books": "烹饪手册",
"CREATE_ERROR": "",
@@ -90,6 +95,7 @@
"DelayUntil": "推迟到",
"Delete": "删除",
"DeleteShoppingConfirm": "确定要移除购物清单中所有 {food} 吗?",
"DeleteSomething": "",
"Delete_All": "全部删除",
"Delete_Food": "删除食物",
"Delete_Keyword": "删除关键词",
@@ -99,6 +105,7 @@
"Disable_Amount": "禁用金额",
"Disabled": "禁用",
"Documentation": "文档",
"DontChange": "",
"Download": "下载",
"Drag_Here_To_Delete": "拖动此处可删除",
"Edit": "编辑",
@@ -146,6 +153,7 @@
"Hide_Keywords": "隐藏关键词",
"Hide_Recipes": "隐藏食谱",
"Hide_as_header": "隐藏标题",
"Hierarchy": "",
"Hour": "小数",
"Hours": "小时",
"Icon": "图标",
@@ -194,6 +202,7 @@
"Logo": "徽标",
"Make_Header": "显示注意事项",
"Make_Ingredient": "制作食材",
"ManageSubscription": "",
"Manage_Books": "烹饪手册管理",
"Manage_Emails": "管理电子邮件",
"Meal_Plan": "用餐计划",
@@ -202,6 +211,7 @@
"Meal_Type_Required": "用餐类型是必需的",
"Meal_Types": "用餐类型",
"Merge": "合并",
"MergeAutomateHelp": "",
"Merge_Keyword": "合并关键词",
"Message": "信息",
"MissingProperties": "",
@@ -232,6 +242,7 @@
"New_Unit": "新建单位",
"Next_Day": "第二天",
"Next_Period": "下期",
"No": "",
"NoCategory": "未选择分类。",
"NoMoreUndo": "没有可撤消的更改。",
"NoUnit": "",
@@ -272,6 +283,7 @@
"Previous_Day": "前一天",
"Previous_Period": "上期",
"Print": "打印",
"Private": "",
"Private_Recipe": "私人食谱",
"Private_Recipe_Help": "食谱只有你和共享的人会显示。",
"Properties": "属性",
@@ -293,7 +305,9 @@
"Recipes": "食谱",
"Recipes_In_Import": "从文件中导入食谱",
"Recipes_per_page": "每页食谱数量",
"RemoveAllType": "",
"RemoveFoodFromShopping": "从购物清单中移除 {food}",
"RemoveParent": "",
"Remove_nutrition_recipe": "从食谱中删除营养信息",
"Reset": "重置",
"Reset_Search": "重置搜索",
@@ -332,6 +346,7 @@
"Size": "大小",
"Social_Authentication": "社交认证",
"Sort_by_new": "按新旧排序",
"Space": "",
"Space_Cosmetic_Settings": "空间管理员可以更改某些装饰设置,并将覆盖该空间的客户端设置。",
"Split_All_Steps": "将所有行拆分为单独的步骤。",
"StartDate": "开始日期",
@@ -390,6 +405,7 @@
"Valid Until": "有效期限",
"View": "查看",
"View_Recipes": "查看食谱",
"Visibility": "",
"Waiting": "等待",
"Warning": "警告",
"Warning_Delete_Supermarket_Category": "删除超市类别也会删除与食品的所有关系。 你确定吗?",
@@ -398,6 +414,7 @@
"Week_Numbers": "周数",
"Welcome": "欢迎",
"Year": "年",
"Yes": "",
"add_keyword": "添加关键字",
"additional_options": "附加选项",
"advanced": "高级",

View File

@@ -11,6 +11,7 @@
"Activity": "活動",
"Add": "新增",
"AddAll": "增加全部",
"AddChild": "",
"AddFilter": "增加過濾器",
"AddFoodToShopping": "添加食物到購物",
"AddMany": "增加多個",
@@ -44,6 +45,10 @@
"BaseUnit": "基礎單位",
"BaseUnitHelp": "自動單位轉換的標準單位",
"Basics": "基礎",
"BatchDeleteConfirm": "",
"BatchDeleteHelp": "",
"BatchEdit": "",
"BatchEditUpdatingItemsCount": "",
"Book": "書籍",
"Bookmarklet": "書籤小工具",
"BookmarkletHelp1": "將以下按鈕拖到您的書籤欄中",
@@ -130,6 +135,7 @@
"Delete": "刪除",
"DeleteConfirmQuestion": "您確定要刪除此物件嗎?",
"DeleteShoppingConfirm": "確定要移除購物清單中所有 {food} 嗎?",
"DeleteSomething": "",
"Delete_All": "刪除全部",
"Delete_Food": "刪除食物",
"Delete_Keyword": "刪除關鍵字",
@@ -142,6 +148,7 @@
"Disable_Amount": "停用數量",
"Disabled": "已停用",
"Documentation": "文件",
"DontChange": "",
"Down": "下",
"Download": "下載",
"DragToUpload": "拖放或點擊選擇",
@@ -210,6 +217,7 @@
"Hide_Keywords": "隱藏關鍵字",
"Hide_Recipes": "隱藏食譜",
"Hide_as_header": "隱藏為標題",
"Hierarchy": "",
"History": "歷史記錄",
"HostedFreeVersion": "您正在使用 Tandoor 的免費版本",
"Hour": "小時",
@@ -323,6 +331,7 @@
"Next": "下一個",
"Next_Day": "下一天",
"Next_Period": "下一期間",
"No": "",
"NoCategory": "無分類",
"NoMoreUndo": "沒有可撤消的更改。",
"NoUnit": "無單位",
@@ -375,6 +384,7 @@
"Previous_Day": "前一天",
"Previous_Period": "上一期間",
"Print": "列印",
"Private": "",
"Private_Recipe": "私人食譜",
"Private_Recipe_Help": "食譜只有你和共享的人會顯示。",
"Profile": "個人資料",
@@ -409,7 +419,9 @@
"Recipes_In_Import": "匯入檔中的食譜",
"Recipes_per_page": "每頁中食譜",
"Remove": "移除",
"RemoveAllType": "",
"RemoveFoodFromShopping": "從購物清單中移除 {food}",
"RemoveParent": "",
"Remove_nutrition_recipe": "從食譜中刪除營養資訊",
"Reset": "重置",
"ResetHelp": "重置說明",
@@ -471,6 +483,7 @@
"Source": "來源",
"SourceImportHelp": "匯入 schema.org/recipe 格式的 JSON 或包含 json+ld 食譜或微資料的 HTML 頁面。",
"SourceImportSubtitle": "手動匯入 JSON 或 HTML。",
"Space": "",
"SpaceLimitExceeded": "您的空間已超過其中一個限制,某些功能可能會受到限制。",
"SpaceLimitReached": "此空間已達到限制。無法再建立此類型的物件。",
"SpaceMemberHelp": "透過建立邀請連結並發送給您要新增的人來將使用者新增到您的空間。",
@@ -573,6 +586,7 @@
"ViewLogHelp": "已檢視食譜的歷史記錄。 ",
"View_Recipes": "查看食譜",
"Viewed": "已檢視",
"Visibility": "",
"Waiting": "等待",
"WaitingTime": "等待時間",
"WarnPageLeave": "有未儲存的變更將會遺失。仍要離開頁面嗎?",
@@ -586,6 +600,7 @@
"Welcome": "歡迎",
"WorkingTime": "製作時間",
"Year": "年",
"Yes": "",
"YourSpaces": "您的空間",
"active": "啟用",
"add_keyword": "添加關鍵字",

View File

@@ -3,6 +3,7 @@ apis/ApiTokenAuthApi.ts
apis/index.ts
index.ts
models/AccessToken.ts
models/AlignmentEnum.ts
models/AuthToken.ts
models/AutoMealPlan.ts
models/Automation.ts
@@ -16,6 +17,11 @@ models/CookLog.ts
models/CustomFilter.ts
models/DefaultPageEnum.ts
models/DeleteEnum.ts
models/EnterpriseKeyword.ts
models/EnterpriseSocialEmbed.ts
models/EnterpriseSocialEmbedTypeEnum.ts
models/EnterpriseSocialRecipeSearch.ts
models/EnterpriseSpace.ts
models/ExportLog.ts
models/ExportRequest.ts
models/FdcQuery.ts
@@ -56,6 +62,9 @@ models/PaginatedBookmarkletImportListList.ts
models/PaginatedConnectorConfigList.ts
models/PaginatedCookLogList.ts
models/PaginatedCustomFilterList.ts
models/PaginatedEnterpriseSocialEmbedList.ts
models/PaginatedEnterpriseSocialRecipeSearchList.ts
models/PaginatedEnterpriseSpaceList.ts
models/PaginatedExportLogList.ts
models/PaginatedFoodList.ts
models/PaginatedImportLogList.ts
@@ -64,6 +73,13 @@ models/PaginatedInviteLinkList.ts
models/PaginatedKeywordList.ts
models/PaginatedMealPlanList.ts
models/PaginatedMealTypeList.ts
models/PaginatedOpenDataCategoryList.ts
models/PaginatedOpenDataConversionList.ts
models/PaginatedOpenDataFoodList.ts
models/PaginatedOpenDataPropertyList.ts
models/PaginatedOpenDataStoreList.ts
models/PaginatedOpenDataUnitList.ts
models/PaginatedOpenDataVersionList.ts
models/PaginatedPropertyList.ts
models/PaginatedPropertyTypeList.ts
models/PaginatedRecipeBookEntryList.ts
@@ -92,6 +108,8 @@ models/PatchedBookmarkletImport.ts
models/PatchedConnectorConfig.ts
models/PatchedCookLog.ts
models/PatchedCustomFilter.ts
models/PatchedEnterpriseSocialEmbed.ts
models/PatchedEnterpriseSpace.ts
models/PatchedExportLog.ts
models/PatchedFood.ts
models/PatchedImportLog.ts
@@ -132,6 +150,7 @@ models/PatchedViewLog.ts
models/Property.ts
models/PropertyType.ts
models/Recipe.ts
models/RecipeBatchUpdate.ts
models/RecipeBook.ts
models/RecipeBookEntry.ts
models/RecipeFlat.ts

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,52 @@
/* 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.
*/
/**
* * `start` - start
* * `center` - center
* * `end` - end
* @export
*/
export const AlignmentEnum = {
Start: 'start',
Center: 'center',
End: 'end'
} as const;
export type AlignmentEnum = typeof AlignmentEnum[keyof typeof AlignmentEnum];
export function instanceOfAlignmentEnum(value: any): boolean {
for (const key in AlignmentEnum) {
if (Object.prototype.hasOwnProperty.call(AlignmentEnum, key)) {
if (AlignmentEnum[key] === value) {
return true;
}
}
}
return false;
}
export function AlignmentEnumFromJSON(json: any): AlignmentEnum {
return AlignmentEnumFromJSONTyped(json, false);
}
export function AlignmentEnumFromJSONTyped(json: any, ignoreDiscriminator: boolean): AlignmentEnum {
return json as AlignmentEnum;
}
export function AlignmentEnumToJSON(value?: AlignmentEnum | null): any {
return value as any;
}

View File

@@ -109,8 +109,8 @@ export function AutoMealPlanToJSON(value?: AutoMealPlan | null): any {
}
return {
'start_date': ((value['startDate']).toISOString().substring(0,10)),
'end_date': ((value['endDate']).toISOString().substring(0,10)),
'start_date': ((value['startDate']).toISOString()),
'end_date': ((value['endDate']).toISOString()),
'meal_type_id': value['mealTypeId'],
'keyword_ids': value['keywordIds'],
'servings': value['servings'],

View File

@@ -14,43 +14,47 @@
/**
* * `G` - g
* * `KG` - kg
* * `ML` - ml
* * `L` - l
* * `OUNCE` - ounce
* * `POUND` - pound
* * `FLUID_OUNCE` - fluid_ounce
* * `TSP` - tsp
* * `TBSP` - tbsp
* * `CUP` - cup
* * `PINT` - pint
* * `QUART` - quart
* * `GALLON` - gallon
* * `IMPERIAL_FLUID_OUNCE` - imperial fluid ounce
* * `IMPERIAL_PINT` - imperial pint
* * `IMPERIAL_QUART` - imperial quart
* * `IMPERIAL_GALLON` - imperial gallon
* * `g` - g
* * `kg` - kg
* * `ounce` - ounce
* * `pound` - pound
* * `ml` - ml
* * `l` - l
* * `fluid_ounce` - fluid_ounce
* * `pint` - pint
* * `quart` - quart
* * `gallon` - gallon
* * `tbsp` - tbsp
* * `tsp` - tsp
* * `us_cup` - US Cup
* * `imperial_fluid_ounce` - imperial fluid ounce
* * `imperial_pint` - imperial pint
* * `imperial_quart` - imperial quart
* * `imperial_gallon` - imperial gallon
* * `imperial_tbsp` - imperial tbsp
* * `imperial_tsp` - imperial tsp
* @export
*/
export const BaseUnitEnum = {
G: 'G',
Kg: 'KG',
Ml: 'ML',
L: 'L',
Ounce: 'OUNCE',
Pound: 'POUND',
FluidOunce: 'FLUID_OUNCE',
Tsp: 'TSP',
Tbsp: 'TBSP',
Cup: 'CUP',
Pint: 'PINT',
Quart: 'QUART',
Gallon: 'GALLON',
ImperialFluidOunce: 'IMPERIAL_FLUID_OUNCE',
ImperialPint: 'IMPERIAL_PINT',
ImperialQuart: 'IMPERIAL_QUART',
ImperialGallon: 'IMPERIAL_GALLON'
G: 'g',
Kg: 'kg',
Ounce: 'ounce',
Pound: 'pound',
Ml: 'ml',
L: 'l',
FluidOunce: 'fluid_ounce',
Pint: 'pint',
Quart: 'quart',
Gallon: 'gallon',
Tbsp: 'tbsp',
Tsp: 'tsp',
UsCup: 'us_cup',
ImperialFluidOunce: 'imperial_fluid_ounce',
ImperialPint: 'imperial_pint',
ImperialQuart: 'imperial_quart',
ImperialGallon: 'imperial_gallon',
ImperialTbsp: 'imperial_tbsp',
ImperialTsp: 'imperial_tsp'
} as const;
export type BaseUnitEnum = typeof BaseUnitEnum[keyof typeof BaseUnitEnum];

View File

@@ -0,0 +1,85 @@
/* 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';
/**
*
* @export
* @interface EnterpriseKeyword
*/
export interface EnterpriseKeyword {
/**
*
* @type {number}
* @memberof EnterpriseKeyword
*/
id?: number;
/**
*
* @type {string}
* @memberof EnterpriseKeyword
*/
name: string;
/**
*
* @type {string}
* @memberof EnterpriseKeyword
*/
readonly label: string;
/**
*
* @type {string}
* @memberof EnterpriseKeyword
*/
description?: string;
}
/**
* Check if a given object implements the EnterpriseKeyword interface.
*/
export function instanceOfEnterpriseKeyword(value: object): value is EnterpriseKeyword {
if (!('name' in value) || value['name'] === undefined) return false;
if (!('label' in value) || value['label'] === undefined) return false;
return true;
}
export function EnterpriseKeywordFromJSON(json: any): EnterpriseKeyword {
return EnterpriseKeywordFromJSONTyped(json, false);
}
export function EnterpriseKeywordFromJSONTyped(json: any, ignoreDiscriminator: boolean): EnterpriseKeyword {
if (json == null) {
return json;
}
return {
'id': json['id'] == null ? undefined : json['id'],
'name': json['name'],
'label': json['label'],
'description': json['description'] == null ? undefined : json['description'],
};
}
export function EnterpriseKeywordToJSON(value?: Omit<EnterpriseKeyword, 'label'> | null): any {
if (value == null) {
return value;
}
return {
'id': value['id'],
'name': value['name'],
'description': value['description'],
};
}

View File

@@ -0,0 +1,138 @@
/* 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 { AlignmentEnum } from './AlignmentEnum';
import {
AlignmentEnumFromJSON,
AlignmentEnumFromJSONTyped,
AlignmentEnumToJSON,
} from './AlignmentEnum';
import type { EnterpriseSocialEmbedTypeEnum } from './EnterpriseSocialEmbedTypeEnum';
import {
EnterpriseSocialEmbedTypeEnumFromJSON,
EnterpriseSocialEmbedTypeEnumFromJSONTyped,
EnterpriseSocialEmbedTypeEnumToJSON,
} from './EnterpriseSocialEmbedTypeEnum';
import type { Keyword } from './Keyword';
import {
KeywordFromJSON,
KeywordFromJSONTyped,
KeywordToJSON,
} from './Keyword';
/**
* Adds nested create feature
* @export
* @interface EnterpriseSocialEmbed
*/
export interface EnterpriseSocialEmbed {
/**
*
* @type {number}
* @memberof EnterpriseSocialEmbed
*/
id?: number;
/**
*
* @type {string}
* @memberof EnterpriseSocialEmbed
*/
name: string;
/**
*
* @type {EnterpriseSocialEmbedTypeEnum}
* @memberof EnterpriseSocialEmbed
*/
type: EnterpriseSocialEmbedTypeEnum;
/**
*
* @type {Array<Keyword>}
* @memberof EnterpriseSocialEmbed
*/
keywords: Array<Keyword>;
/**
*
* @type {AlignmentEnum}
* @memberof EnterpriseSocialEmbed
*/
alignment?: AlignmentEnum;
/**
*
* @type {string}
* @memberof EnterpriseSocialEmbed
*/
backgroundColor?: string;
/**
*
* @type {string}
* @memberof EnterpriseSocialEmbed
*/
accentColor?: string;
/**
*
* @type {string}
* @memberof EnterpriseSocialEmbed
*/
uuid?: string;
}
/**
* Check if a given object implements the EnterpriseSocialEmbed interface.
*/
export function instanceOfEnterpriseSocialEmbed(value: object): value is EnterpriseSocialEmbed {
if (!('name' in value) || value['name'] === undefined) return false;
if (!('type' in value) || value['type'] === undefined) return false;
if (!('keywords' in value) || value['keywords'] === undefined) return false;
return true;
}
export function EnterpriseSocialEmbedFromJSON(json: any): EnterpriseSocialEmbed {
return EnterpriseSocialEmbedFromJSONTyped(json, false);
}
export function EnterpriseSocialEmbedFromJSONTyped(json: any, ignoreDiscriminator: boolean): EnterpriseSocialEmbed {
if (json == null) {
return json;
}
return {
'id': json['id'] == null ? undefined : json['id'],
'name': json['name'],
'type': EnterpriseSocialEmbedTypeEnumFromJSON(json['type']),
'keywords': ((json['keywords'] as Array<any>).map(KeywordFromJSON)),
'alignment': json['alignment'] == null ? undefined : AlignmentEnumFromJSON(json['alignment']),
'backgroundColor': json['background_color'] == null ? undefined : json['background_color'],
'accentColor': json['accent_color'] == null ? undefined : json['accent_color'],
'uuid': json['uuid'] == null ? undefined : json['uuid'],
};
}
export function EnterpriseSocialEmbedToJSON(value?: EnterpriseSocialEmbed | null): any {
if (value == null) {
return value;
}
return {
'id': value['id'],
'name': value['name'],
'type': EnterpriseSocialEmbedTypeEnumToJSON(value['type']),
'keywords': ((value['keywords'] as Array<any>).map(KeywordToJSON)),
'alignment': AlignmentEnumToJSON(value['alignment']),
'background_color': value['backgroundColor'],
'accent_color': value['accentColor'],
'uuid': value['uuid'],
};
}

View File

@@ -0,0 +1,56 @@
/* 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.
*/
/**
* * `RECIPE` - Recipe
* * `RECIPE_KEYWORD` - Recipe Keyword
* * `MEAL_PLAN` - Meal Plan
* * `SHOPPING` - Shopping
* * `BOOK` - Book
* @export
*/
export const EnterpriseSocialEmbedTypeEnum = {
Recipe: 'RECIPE',
RecipeKeyword: 'RECIPE_KEYWORD',
MealPlan: 'MEAL_PLAN',
Shopping: 'SHOPPING',
Book: 'BOOK'
} as const;
export type EnterpriseSocialEmbedTypeEnum = typeof EnterpriseSocialEmbedTypeEnum[keyof typeof EnterpriseSocialEmbedTypeEnum];
export function instanceOfEnterpriseSocialEmbedTypeEnum(value: any): boolean {
for (const key in EnterpriseSocialEmbedTypeEnum) {
if (Object.prototype.hasOwnProperty.call(EnterpriseSocialEmbedTypeEnum, key)) {
if (EnterpriseSocialEmbedTypeEnum[key] === value) {
return true;
}
}
}
return false;
}
export function EnterpriseSocialEmbedTypeEnumFromJSON(json: any): EnterpriseSocialEmbedTypeEnum {
return EnterpriseSocialEmbedTypeEnumFromJSONTyped(json, false);
}
export function EnterpriseSocialEmbedTypeEnumFromJSONTyped(json: any, ignoreDiscriminator: boolean): EnterpriseSocialEmbedTypeEnum {
return json as EnterpriseSocialEmbedTypeEnum;
}
export function EnterpriseSocialEmbedTypeEnumToJSON(value?: EnterpriseSocialEmbedTypeEnum | null): any {
return value as any;
}

View File

@@ -0,0 +1,91 @@
/* 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 { EnterpriseKeyword } from './EnterpriseKeyword';
import {
EnterpriseKeywordFromJSON,
EnterpriseKeywordFromJSONTyped,
EnterpriseKeywordToJSON,
} from './EnterpriseKeyword';
/**
*
* @export
* @interface EnterpriseSocialRecipeSearch
*/
export interface EnterpriseSocialRecipeSearch {
/**
*
* @type {number}
* @memberof EnterpriseSocialRecipeSearch
*/
id?: number;
/**
*
* @type {string}
* @memberof EnterpriseSocialRecipeSearch
*/
readonly name: string;
/**
*
* @type {string}
* @memberof EnterpriseSocialRecipeSearch
*/
readonly image: string | null;
/**
*
* @type {Array<EnterpriseKeyword>}
* @memberof EnterpriseSocialRecipeSearch
*/
readonly keywords: Array<EnterpriseKeyword>;
}
/**
* Check if a given object implements the EnterpriseSocialRecipeSearch interface.
*/
export function instanceOfEnterpriseSocialRecipeSearch(value: object): value is EnterpriseSocialRecipeSearch {
if (!('name' in value) || value['name'] === undefined) return false;
if (!('image' in value) || value['image'] === undefined) return false;
if (!('keywords' in value) || value['keywords'] === undefined) return false;
return true;
}
export function EnterpriseSocialRecipeSearchFromJSON(json: any): EnterpriseSocialRecipeSearch {
return EnterpriseSocialRecipeSearchFromJSONTyped(json, false);
}
export function EnterpriseSocialRecipeSearchFromJSONTyped(json: any, ignoreDiscriminator: boolean): EnterpriseSocialRecipeSearch {
if (json == null) {
return json;
}
return {
'id': json['id'] == null ? undefined : json['id'],
'name': json['name'],
'image': json['image'],
'keywords': ((json['keywords'] as Array<any>).map(EnterpriseKeywordFromJSON)),
};
}
export function EnterpriseSocialRecipeSearchToJSON(value?: Omit<EnterpriseSocialRecipeSearch, 'name'|'image'|'keywords'> | null): any {
if (value == null) {
return value;
}
return {
'id': value['id'],
};
}

View File

@@ -0,0 +1,70 @@
/* 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';
/**
*
* @export
* @interface EnterpriseSpace
*/
export interface EnterpriseSpace {
/**
*
* @type {number}
* @memberof EnterpriseSpace
*/
space: number;
/**
*
* @type {string}
* @memberof EnterpriseSpace
*/
licensedModules: string;
}
/**
* Check if a given object implements the EnterpriseSpace interface.
*/
export function instanceOfEnterpriseSpace(value: object): value is EnterpriseSpace {
if (!('space' in value) || value['space'] === undefined) return false;
if (!('licensedModules' in value) || value['licensedModules'] === undefined) return false;
return true;
}
export function EnterpriseSpaceFromJSON(json: any): EnterpriseSpace {
return EnterpriseSpaceFromJSONTyped(json, false);
}
export function EnterpriseSpaceFromJSONTyped(json: any, ignoreDiscriminator: boolean): EnterpriseSpace {
if (json == null) {
return json;
}
return {
'space': json['space'],
'licensedModules': json['licensed_modules'],
};
}
export function EnterpriseSpaceToJSON(value?: EnterpriseSpace | null): any {
if (value == null) {
return value;
}
return {
'space': value['space'],
'licensed_modules': value['licensedModules'],
};
}

View File

@@ -164,10 +164,10 @@ export interface OpenDataFood {
propertiesSource?: string;
/**
*
* @type {string}
* @type {number}
* @memberof OpenDataFood
*/
fdcId: string;
fdcId?: number;
/**
*
* @type {string}
@@ -193,7 +193,6 @@ export function instanceOfOpenDataFood(value: object): value is OpenDataFood {
if (!('storeCategory' in value) || value['storeCategory'] === undefined) return false;
if (!('properties' in value) || value['properties'] === undefined) return false;
if (!('propertiesFoodUnit' in value) || value['propertiesFoodUnit'] === undefined) return false;
if (!('fdcId' in value) || value['fdcId'] === undefined) return false;
if (!('createdBy' in value) || value['createdBy'] === undefined) return false;
return true;
}
@@ -222,7 +221,7 @@ export function OpenDataFoodFromJSONTyped(json: any, ignoreDiscriminator: boolea
'propertiesFoodAmount': json['properties_food_amount'] == null ? undefined : json['properties_food_amount'],
'propertiesFoodUnit': OpenDataUnitFromJSON(json['properties_food_unit']),
'propertiesSource': json['properties_source'] == null ? undefined : json['properties_source'],
'fdcId': json['fdc_id'],
'fdcId': json['fdc_id'] == null ? undefined : json['fdc_id'],
'comment': json['comment'] == null ? undefined : json['comment'],
'createdBy': json['created_by'],
};

View 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 { EnterpriseSocialEmbed } from './EnterpriseSocialEmbed';
import {
EnterpriseSocialEmbedFromJSON,
EnterpriseSocialEmbedFromJSONTyped,
EnterpriseSocialEmbedToJSON,
} from './EnterpriseSocialEmbed';
/**
*
* @export
* @interface PaginatedEnterpriseSocialEmbedList
*/
export interface PaginatedEnterpriseSocialEmbedList {
/**
*
* @type {number}
* @memberof PaginatedEnterpriseSocialEmbedList
*/
count: number;
/**
*
* @type {string}
* @memberof PaginatedEnterpriseSocialEmbedList
*/
next?: string;
/**
*
* @type {string}
* @memberof PaginatedEnterpriseSocialEmbedList
*/
previous?: string;
/**
*
* @type {Array<EnterpriseSocialEmbed>}
* @memberof PaginatedEnterpriseSocialEmbedList
*/
results: Array<EnterpriseSocialEmbed>;
/**
*
* @type {Date}
* @memberof PaginatedEnterpriseSocialEmbedList
*/
timestamp?: Date;
}
/**
* Check if a given object implements the PaginatedEnterpriseSocialEmbedList interface.
*/
export function instanceOfPaginatedEnterpriseSocialEmbedList(value: object): value is PaginatedEnterpriseSocialEmbedList {
if (!('count' in value) || value['count'] === undefined) return false;
if (!('results' in value) || value['results'] === undefined) return false;
return true;
}
export function PaginatedEnterpriseSocialEmbedListFromJSON(json: any): PaginatedEnterpriseSocialEmbedList {
return PaginatedEnterpriseSocialEmbedListFromJSONTyped(json, false);
}
export function PaginatedEnterpriseSocialEmbedListFromJSONTyped(json: any, ignoreDiscriminator: boolean): PaginatedEnterpriseSocialEmbedList {
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(EnterpriseSocialEmbedFromJSON)),
'timestamp': json['timestamp'] == null ? undefined : (new Date(json['timestamp'])),
};
}
export function PaginatedEnterpriseSocialEmbedListToJSON(value?: PaginatedEnterpriseSocialEmbedList | null): any {
if (value == null) {
return value;
}
return {
'count': value['count'],
'next': value['next'],
'previous': value['previous'],
'results': ((value['results'] as Array<any>).map(EnterpriseSocialEmbedToJSON)),
'timestamp': value['timestamp'] == null ? undefined : ((value['timestamp']).toISOString()),
};
}

View 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 { EnterpriseSocialRecipeSearch } from './EnterpriseSocialRecipeSearch';
import {
EnterpriseSocialRecipeSearchFromJSON,
EnterpriseSocialRecipeSearchFromJSONTyped,
EnterpriseSocialRecipeSearchToJSON,
} from './EnterpriseSocialRecipeSearch';
/**
*
* @export
* @interface PaginatedEnterpriseSocialRecipeSearchList
*/
export interface PaginatedEnterpriseSocialRecipeSearchList {
/**
*
* @type {number}
* @memberof PaginatedEnterpriseSocialRecipeSearchList
*/
count: number;
/**
*
* @type {string}
* @memberof PaginatedEnterpriseSocialRecipeSearchList
*/
next?: string;
/**
*
* @type {string}
* @memberof PaginatedEnterpriseSocialRecipeSearchList
*/
previous?: string;
/**
*
* @type {Array<EnterpriseSocialRecipeSearch>}
* @memberof PaginatedEnterpriseSocialRecipeSearchList
*/
results: Array<EnterpriseSocialRecipeSearch>;
/**
*
* @type {Date}
* @memberof PaginatedEnterpriseSocialRecipeSearchList
*/
timestamp?: Date;
}
/**
* Check if a given object implements the PaginatedEnterpriseSocialRecipeSearchList interface.
*/
export function instanceOfPaginatedEnterpriseSocialRecipeSearchList(value: object): value is PaginatedEnterpriseSocialRecipeSearchList {
if (!('count' in value) || value['count'] === undefined) return false;
if (!('results' in value) || value['results'] === undefined) return false;
return true;
}
export function PaginatedEnterpriseSocialRecipeSearchListFromJSON(json: any): PaginatedEnterpriseSocialRecipeSearchList {
return PaginatedEnterpriseSocialRecipeSearchListFromJSONTyped(json, false);
}
export function PaginatedEnterpriseSocialRecipeSearchListFromJSONTyped(json: any, ignoreDiscriminator: boolean): PaginatedEnterpriseSocialRecipeSearchList {
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(EnterpriseSocialRecipeSearchFromJSON)),
'timestamp': json['timestamp'] == null ? undefined : (new Date(json['timestamp'])),
};
}
export function PaginatedEnterpriseSocialRecipeSearchListToJSON(value?: PaginatedEnterpriseSocialRecipeSearchList | null): any {
if (value == null) {
return value;
}
return {
'count': value['count'],
'next': value['next'],
'previous': value['previous'],
'results': ((value['results'] as Array<any>).map(EnterpriseSocialRecipeSearchToJSON)),
'timestamp': value['timestamp'] == null ? undefined : ((value['timestamp']).toISOString()),
};
}

View 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 { EnterpriseSpace } from './EnterpriseSpace';
import {
EnterpriseSpaceFromJSON,
EnterpriseSpaceFromJSONTyped,
EnterpriseSpaceToJSON,
} from './EnterpriseSpace';
/**
*
* @export
* @interface PaginatedEnterpriseSpaceList
*/
export interface PaginatedEnterpriseSpaceList {
/**
*
* @type {number}
* @memberof PaginatedEnterpriseSpaceList
*/
count: number;
/**
*
* @type {string}
* @memberof PaginatedEnterpriseSpaceList
*/
next?: string;
/**
*
* @type {string}
* @memberof PaginatedEnterpriseSpaceList
*/
previous?: string;
/**
*
* @type {Array<EnterpriseSpace>}
* @memberof PaginatedEnterpriseSpaceList
*/
results: Array<EnterpriseSpace>;
/**
*
* @type {Date}
* @memberof PaginatedEnterpriseSpaceList
*/
timestamp?: Date;
}
/**
* Check if a given object implements the PaginatedEnterpriseSpaceList interface.
*/
export function instanceOfPaginatedEnterpriseSpaceList(value: object): value is PaginatedEnterpriseSpaceList {
if (!('count' in value) || value['count'] === undefined) return false;
if (!('results' in value) || value['results'] === undefined) return false;
return true;
}
export function PaginatedEnterpriseSpaceListFromJSON(json: any): PaginatedEnterpriseSpaceList {
return PaginatedEnterpriseSpaceListFromJSONTyped(json, false);
}
export function PaginatedEnterpriseSpaceListFromJSONTyped(json: any, ignoreDiscriminator: boolean): PaginatedEnterpriseSpaceList {
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(EnterpriseSpaceFromJSON)),
'timestamp': json['timestamp'] == null ? undefined : (new Date(json['timestamp'])),
};
}
export function PaginatedEnterpriseSpaceListToJSON(value?: PaginatedEnterpriseSpaceList | null): any {
if (value == null) {
return value;
}
return {
'count': value['count'],
'next': value['next'],
'previous': value['previous'],
'results': ((value['results'] as Array<any>).map(EnterpriseSpaceToJSON)),
'timestamp': value['timestamp'] == null ? undefined : ((value['timestamp']).toISOString()),
};
}

View File

@@ -43,6 +43,12 @@ export interface ParsedIngredient {
* @memberof ParsedIngredient
*/
note: string;
/**
*
* @type {string}
* @memberof ParsedIngredient
*/
originalText: string;
}
/**
@@ -53,6 +59,7 @@ export function instanceOfParsedIngredient(value: object): value is ParsedIngred
if (!('unit' in value) || value['unit'] === undefined) return false;
if (!('food' in value) || value['food'] === undefined) return false;
if (!('note' in value) || value['note'] === undefined) return false;
if (!('originalText' in value) || value['originalText'] === undefined) return false;
return true;
}
@@ -70,6 +77,7 @@ export function ParsedIngredientFromJSONTyped(json: any, ignoreDiscriminator: bo
'unit': json['unit'],
'food': json['food'],
'note': json['note'],
'originalText': json['original_text'],
};
}
@@ -83,6 +91,7 @@ export function ParsedIngredientToJSON(value?: ParsedIngredient | null): any {
'unit': value['unit'],
'food': value['food'],
'note': value['note'],
'original_text': value['originalText'],
};
}

View File

@@ -0,0 +1,135 @@
/* 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 { AlignmentEnum } from './AlignmentEnum';
import {
AlignmentEnumFromJSON,
AlignmentEnumFromJSONTyped,
AlignmentEnumToJSON,
} from './AlignmentEnum';
import type { EnterpriseSocialEmbedTypeEnum } from './EnterpriseSocialEmbedTypeEnum';
import {
EnterpriseSocialEmbedTypeEnumFromJSON,
EnterpriseSocialEmbedTypeEnumFromJSONTyped,
EnterpriseSocialEmbedTypeEnumToJSON,
} from './EnterpriseSocialEmbedTypeEnum';
import type { Keyword } from './Keyword';
import {
KeywordFromJSON,
KeywordFromJSONTyped,
KeywordToJSON,
} from './Keyword';
/**
* Adds nested create feature
* @export
* @interface PatchedEnterpriseSocialEmbed
*/
export interface PatchedEnterpriseSocialEmbed {
/**
*
* @type {number}
* @memberof PatchedEnterpriseSocialEmbed
*/
id?: number;
/**
*
* @type {string}
* @memberof PatchedEnterpriseSocialEmbed
*/
name?: string;
/**
*
* @type {EnterpriseSocialEmbedTypeEnum}
* @memberof PatchedEnterpriseSocialEmbed
*/
type?: EnterpriseSocialEmbedTypeEnum;
/**
*
* @type {Array<Keyword>}
* @memberof PatchedEnterpriseSocialEmbed
*/
keywords?: Array<Keyword>;
/**
*
* @type {AlignmentEnum}
* @memberof PatchedEnterpriseSocialEmbed
*/
alignment?: AlignmentEnum;
/**
*
* @type {string}
* @memberof PatchedEnterpriseSocialEmbed
*/
backgroundColor?: string;
/**
*
* @type {string}
* @memberof PatchedEnterpriseSocialEmbed
*/
accentColor?: string;
/**
*
* @type {string}
* @memberof PatchedEnterpriseSocialEmbed
*/
uuid?: string;
}
/**
* Check if a given object implements the PatchedEnterpriseSocialEmbed interface.
*/
export function instanceOfPatchedEnterpriseSocialEmbed(value: object): value is PatchedEnterpriseSocialEmbed {
return true;
}
export function PatchedEnterpriseSocialEmbedFromJSON(json: any): PatchedEnterpriseSocialEmbed {
return PatchedEnterpriseSocialEmbedFromJSONTyped(json, false);
}
export function PatchedEnterpriseSocialEmbedFromJSONTyped(json: any, ignoreDiscriminator: boolean): PatchedEnterpriseSocialEmbed {
if (json == null) {
return json;
}
return {
'id': json['id'] == null ? undefined : json['id'],
'name': json['name'] == null ? undefined : json['name'],
'type': json['type'] == null ? undefined : EnterpriseSocialEmbedTypeEnumFromJSON(json['type']),
'keywords': json['keywords'] == null ? undefined : ((json['keywords'] as Array<any>).map(KeywordFromJSON)),
'alignment': json['alignment'] == null ? undefined : AlignmentEnumFromJSON(json['alignment']),
'backgroundColor': json['background_color'] == null ? undefined : json['background_color'],
'accentColor': json['accent_color'] == null ? undefined : json['accent_color'],
'uuid': json['uuid'] == null ? undefined : json['uuid'],
};
}
export function PatchedEnterpriseSocialEmbedToJSON(value?: PatchedEnterpriseSocialEmbed | null): any {
if (value == null) {
return value;
}
return {
'id': value['id'],
'name': value['name'],
'type': EnterpriseSocialEmbedTypeEnumToJSON(value['type']),
'keywords': value['keywords'] == null ? undefined : ((value['keywords'] as Array<any>).map(KeywordToJSON)),
'alignment': AlignmentEnumToJSON(value['alignment']),
'background_color': value['backgroundColor'],
'accent_color': value['accentColor'],
'uuid': value['uuid'],
};
}

View File

@@ -0,0 +1,68 @@
/* 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';
/**
*
* @export
* @interface PatchedEnterpriseSpace
*/
export interface PatchedEnterpriseSpace {
/**
*
* @type {number}
* @memberof PatchedEnterpriseSpace
*/
space?: number;
/**
*
* @type {string}
* @memberof PatchedEnterpriseSpace
*/
licensedModules?: string;
}
/**
* Check if a given object implements the PatchedEnterpriseSpace interface.
*/
export function instanceOfPatchedEnterpriseSpace(value: object): value is PatchedEnterpriseSpace {
return true;
}
export function PatchedEnterpriseSpaceFromJSON(json: any): PatchedEnterpriseSpace {
return PatchedEnterpriseSpaceFromJSONTyped(json, false);
}
export function PatchedEnterpriseSpaceFromJSONTyped(json: any, ignoreDiscriminator: boolean): PatchedEnterpriseSpace {
if (json == null) {
return json;
}
return {
'space': json['space'] == null ? undefined : json['space'],
'licensedModules': json['licensed_modules'] == null ? undefined : json['licensed_modules'],
};
}
export function PatchedEnterpriseSpaceToJSON(value?: PatchedEnterpriseSpace | null): any {
if (value == null) {
return value;
}
return {
'space': value['space'],
'licensed_modules': value['licensedModules'],
};
}

View File

@@ -164,10 +164,10 @@ export interface PatchedOpenDataFood {
propertiesSource?: string;
/**
*
* @type {string}
* @type {number}
* @memberof PatchedOpenDataFood
*/
fdcId?: string;
fdcId?: number;
/**
*
* @type {string}

View File

@@ -0,0 +1,187 @@
/* 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';
/**
*
* @export
* @interface RecipeBatchUpdate
*/
export interface RecipeBatchUpdate {
/**
*
* @type {Array<number>}
* @memberof RecipeBatchUpdate
*/
recipes: Array<number>;
/**
*
* @type {Array<number>}
* @memberof RecipeBatchUpdate
*/
keywordsAdd: Array<number>;
/**
*
* @type {Array<number>}
* @memberof RecipeBatchUpdate
*/
keywordsRemove: Array<number>;
/**
*
* @type {Array<number>}
* @memberof RecipeBatchUpdate
*/
keywordsSet: Array<number>;
/**
*
* @type {boolean}
* @memberof RecipeBatchUpdate
*/
keywordsRemoveAll?: boolean;
/**
*
* @type {number}
* @memberof RecipeBatchUpdate
*/
workingTime?: number;
/**
*
* @type {number}
* @memberof RecipeBatchUpdate
*/
waitingTime?: number;
/**
*
* @type {number}
* @memberof RecipeBatchUpdate
*/
servings?: number;
/**
*
* @type {string}
* @memberof RecipeBatchUpdate
*/
servingsText?: string;
/**
*
* @type {boolean}
* @memberof RecipeBatchUpdate
*/
_private?: boolean;
/**
*
* @type {Array<number>}
* @memberof RecipeBatchUpdate
*/
sharedAdd: Array<number>;
/**
*
* @type {Array<number>}
* @memberof RecipeBatchUpdate
*/
sharedRemove: Array<number>;
/**
*
* @type {Array<number>}
* @memberof RecipeBatchUpdate
*/
sharedSet: Array<number>;
/**
*
* @type {boolean}
* @memberof RecipeBatchUpdate
*/
sharedRemoveAll?: boolean;
/**
*
* @type {boolean}
* @memberof RecipeBatchUpdate
*/
showIngredientOverview?: boolean;
/**
*
* @type {boolean}
* @memberof RecipeBatchUpdate
*/
clearDescription?: boolean;
}
/**
* Check if a given object implements the RecipeBatchUpdate interface.
*/
export function instanceOfRecipeBatchUpdate(value: object): value is RecipeBatchUpdate {
if (!('recipes' in value) || value['recipes'] === undefined) return false;
if (!('keywordsAdd' in value) || value['keywordsAdd'] === undefined) return false;
if (!('keywordsRemove' in value) || value['keywordsRemove'] === undefined) return false;
if (!('keywordsSet' in value) || value['keywordsSet'] === undefined) return false;
if (!('sharedAdd' in value) || value['sharedAdd'] === undefined) return false;
if (!('sharedRemove' in value) || value['sharedRemove'] === undefined) return false;
if (!('sharedSet' in value) || value['sharedSet'] === undefined) return false;
return true;
}
export function RecipeBatchUpdateFromJSON(json: any): RecipeBatchUpdate {
return RecipeBatchUpdateFromJSONTyped(json, false);
}
export function RecipeBatchUpdateFromJSONTyped(json: any, ignoreDiscriminator: boolean): RecipeBatchUpdate {
if (json == null) {
return json;
}
return {
'recipes': json['recipes'],
'keywordsAdd': json['keywords_add'],
'keywordsRemove': json['keywords_remove'],
'keywordsSet': json['keywords_set'],
'keywordsRemoveAll': json['keywords_remove_all'] == null ? undefined : json['keywords_remove_all'],
'workingTime': json['working_time'] == null ? undefined : json['working_time'],
'waitingTime': json['waiting_time'] == null ? undefined : json['waiting_time'],
'servings': json['servings'] == null ? undefined : json['servings'],
'servingsText': json['servings_text'] == null ? undefined : json['servings_text'],
'_private': json['private'] == null ? undefined : json['private'],
'sharedAdd': json['shared_add'],
'sharedRemove': json['shared_remove'],
'sharedSet': json['shared_set'],
'sharedRemoveAll': json['shared_remove_all'] == null ? undefined : json['shared_remove_all'],
'showIngredientOverview': json['show_ingredient_overview'] == null ? undefined : json['show_ingredient_overview'],
'clearDescription': json['clear_description'] == null ? undefined : json['clear_description'],
};
}
export function RecipeBatchUpdateToJSON(value?: RecipeBatchUpdate | null): any {
if (value == null) {
return value;
}
return {
'recipes': value['recipes'],
'keywords_add': value['keywordsAdd'],
'keywords_remove': value['keywordsRemove'],
'keywords_set': value['keywordsSet'],
'keywords_remove_all': value['keywordsRemoveAll'],
'working_time': value['workingTime'],
'waiting_time': value['waitingTime'],
'servings': value['servings'],
'servings_text': value['servingsText'],
'private': value['_private'],
'shared_add': value['sharedAdd'],
'shared_remove': value['sharedRemove'],
'shared_set': value['sharedSet'],
'shared_remove_all': value['sharedRemoveAll'],
'show_ingredient_overview': value['showIngredientOverview'],
'clear_description': value['clearDescription'],
};
}

View File

@@ -98,6 +98,12 @@ export interface RecipeOverview {
* @memberof RecipeOverview
*/
readonly internal: boolean;
/**
*
* @type {boolean}
* @memberof RecipeOverview
*/
_private?: boolean;
/**
*
* @type {number}
@@ -179,6 +185,7 @@ export function RecipeOverviewFromJSONTyped(json: any, ignoreDiscriminator: bool
'createdAt': (new Date(json['created_at'])),
'updatedAt': (new Date(json['updated_at'])),
'internal': json['internal'],
'_private': json['private'] == null ? undefined : json['private'],
'servings': json['servings'],
'servingsText': json['servings_text'],
'rating': json['rating'],
@@ -197,6 +204,7 @@ export function RecipeOverviewToJSON(value?: Omit<RecipeOverview, 'image'|'keywo
'id': value['id'],
'name': value['name'],
'description': value['description'],
'private': value['_private'],
};
}

View File

@@ -1,6 +1,7 @@
/* tslint:disable */
/* eslint-disable */
export * from './AccessToken';
export * from './AlignmentEnum';
export * from './AuthToken';
export * from './AutoMealPlan';
export * from './Automation';
@@ -14,6 +15,11 @@ export * from './CookLog';
export * from './CustomFilter';
export * from './DefaultPageEnum';
export * from './DeleteEnum';
export * from './EnterpriseKeyword';
export * from './EnterpriseSocialEmbed';
export * from './EnterpriseSocialEmbedTypeEnum';
export * from './EnterpriseSocialRecipeSearch';
export * from './EnterpriseSpace';
export * from './ExportLog';
export * from './ExportRequest';
export * from './FdcQuery';
@@ -54,6 +60,9 @@ export * from './PaginatedBookmarkletImportListList';
export * from './PaginatedConnectorConfigList';
export * from './PaginatedCookLogList';
export * from './PaginatedCustomFilterList';
export * from './PaginatedEnterpriseSocialEmbedList';
export * from './PaginatedEnterpriseSocialRecipeSearchList';
export * from './PaginatedEnterpriseSpaceList';
export * from './PaginatedExportLogList';
export * from './PaginatedFoodList';
export * from './PaginatedImportLogList';
@@ -62,6 +71,13 @@ export * from './PaginatedInviteLinkList';
export * from './PaginatedKeywordList';
export * from './PaginatedMealPlanList';
export * from './PaginatedMealTypeList';
export * from './PaginatedOpenDataCategoryList';
export * from './PaginatedOpenDataConversionList';
export * from './PaginatedOpenDataFoodList';
export * from './PaginatedOpenDataPropertyList';
export * from './PaginatedOpenDataStoreList';
export * from './PaginatedOpenDataUnitList';
export * from './PaginatedOpenDataVersionList';
export * from './PaginatedPropertyList';
export * from './PaginatedPropertyTypeList';
export * from './PaginatedRecipeBookEntryList';
@@ -90,6 +106,8 @@ export * from './PatchedBookmarkletImport';
export * from './PatchedConnectorConfig';
export * from './PatchedCookLog';
export * from './PatchedCustomFilter';
export * from './PatchedEnterpriseSocialEmbed';
export * from './PatchedEnterpriseSpace';
export * from './PatchedExportLog';
export * from './PatchedFood';
export * from './PatchedImportLog';
@@ -130,6 +148,7 @@ export * from './PatchedViewLog';
export * from './Property';
export * from './PropertyType';
export * from './Recipe';
export * from './RecipeBatchUpdate';
export * from './RecipeBook';
export * from './RecipeBookEntry';
export * from './RecipeFlat';

View File

@@ -35,6 +35,10 @@
<database-model-col model="MealType"></database-model-col>
</v-row>
<template v-for="p in TANDOOR_PLUGINS" :key="p.name">
<component :is="p.databasePageComponent" v-if="p.databasePageComponent"></component>
</template>
<v-row>
<v-col>
<h2>{{ $t('Miscellaneous') }}</h2>
@@ -47,8 +51,8 @@
<database-model-col model="CustomFilter"></database-model-col>
<database-model-col model="CookLog"></database-model-col>
<database-model-col model="ViewLog"></database-model-col>
<database-link-col :to="{name: 'IngredientEditorPage'}"
<database-link-col :to="{name: 'IngredientEditorPage'}"
prepend-icon="fa-solid fa-table-list"
:title="$t('Ingredient Editor')"
:subtitle="$t('IngredientEditorHelp')">
@@ -77,6 +81,7 @@
import DatabaseModelCol from "@/components/display/DatabaseModelCol.vue";
import DatabaseLinkCol from "@/components/display/DatabaseLinkCol.vue";
import {TANDOOR_PLUGINS} from "@/types/Plugins.ts";
</script>

View File

@@ -9,6 +9,7 @@
</v-card>
</v-col>
</v-row>
<v-row dense>
<v-col>
<v-card :prepend-icon="genericModel.model.icon" :title="$t(genericModel.model.localizationKey)">
@@ -21,9 +22,9 @@
<v-btn class="float-right" icon="$create" color="create" v-if="!genericModel.model.disableCreate">
<i class="fa-solid fa-plus"></i>
<model-edit-dialog :close-after-create="false" :model="model"
@create="loadItems({page: tablePage, itemsPerPage: useUserPreferenceStore().deviceSettings.general_tableItemsPerPage, search: searchQuery})"
@save="loadItems({page: tablePage, itemsPerPage: useUserPreferenceStore().deviceSettings.general_tableItemsPerPage, search: searchQuery})"
@delete="loadItems({page: tablePage, itemsPerPage: useUserPreferenceStore().deviceSettings.general_tableItemsPerPage, search: searchQuery})"></model-edit-dialog>
@create="loadItems({page: page})"
@save="loadItems({page: page })"
@delete="loadItems({page: page})"></model-edit-dialog>
</v-btn>
</template>
<v-card-actions v-if="genericModel.model.name == 'RecipeImport'">
@@ -34,20 +35,38 @@
</v-row>
<v-row>
<v-col>
<v-text-field prepend-inner-icon="$search" :label="$t('Search')" v-model="searchQuery" clearable></v-text-field>
<v-text-field prepend-inner-icon="$search" :label="$t('Search')" v-model="query" clearable></v-text-field>
<v-data-table-server
v-model="selectedItems"
return-object
@update:options="loadItems"
:items="items"
:items-length="itemCount"
:loading="loading"
:search="searchQuery"
:search="query"
:headers="genericModel.getTableHeaders()"
:items-per-page-options="itemsPerPageOptions"
:show-select="tableShowSelect"
:page="tablePage"
:items-per-page="useUserPreferenceStore().deviceSettings.general_tableItemsPerPage"
:show-select="!genericModel.model.disableDelete || genericModel.model.isMerge"
:page="page"
:items-per-page="pageSize"
disable-sort
>
<template v-slot:header.action v-if="selectedItems.length > 0">
<v-btn icon="fa-solid fa-ellipsis-v" variant="plain" color="info">
<v-icon icon="fa-solid fa-ellipsis-v"></v-icon>
<v-menu activator="parent" close-on-content-click>
<v-list density="compact" class="pt-1 pb-1" activatable>
<v-list-item prepend-icon="fa-solid fa-arrows-to-dot" @click="batchMergeDialog = true" v-if="genericModel.model.isMerge">
{{ $t('Merge') }}
</v-list-item>
<v-list-item prepend-icon="$delete" @click="batchDeleteDialog = true" v-if="!genericModel.model.disableDelete">
{{ $t('Delete_All') }}
</v-list-item>
</v-list>
</v-menu>
</v-btn>
</template>
<template v-slot:item.action="{ item }">
<v-btn class="float-right" icon="$menu" variant="plain">
<v-icon icon="$menu"></v-icon>
@@ -59,8 +78,8 @@
</v-list-item>
<v-list-item prepend-icon="fa-solid fa-arrows-to-dot" v-if="genericModel.model.isMerge" link>
{{ $t('Merge') }}
<model-merge-dialog :model="model" :source="item"
@change="loadItems({page: tablePage, itemsPerPage: useUserPreferenceStore().deviceSettings.general_tableItemsPerPage, search: searchQuery})"></model-merge-dialog>
<model-merge-dialog :model="model" :source="[item]"
@change="loadItems({page: page, itemsPerPage: pageSize, search: query})"></model-merge-dialog>
</v-list-item>
<v-list-item prepend-icon="fa-solid fa-table-list" :to="{name: 'IngredientEditorPage', query: {food_id: item.id}}"
v-if="genericModel.model.name == 'Food'">
@@ -84,6 +103,13 @@
</v-data-table-server>
</v-col>
</v-row>
<batch-delete-dialog :items="selectedItems" :model="props.model" v-model="batchDeleteDialog" activator="model"
@change="loadItems({page: page, itemsPerPage: pageSize, search: query})"></batch-delete-dialog>
<model-merge-dialog :model="model" :source="selectedItems" v-model="batchMergeDialog" activator="model"
@change="loadItems({page: page, itemsPerPage: pageSize, search: query})"></model-merge-dialog>
</v-container>
</template>
@@ -93,15 +119,19 @@
import {onBeforeMount, PropType, ref, watch} from "vue";
import {ErrorMessageType, useMessageStore} from "@/stores/MessageStore";
import {useI18n} from "vue-i18n";
import {EditorSupportedModels, GenericModel, getGenericModelFromString, Model,} from "@/types/Models";
import {EditorSupportedModels, EditorSupportedTypes, GenericModel, getGenericModelFromString, Model,} from "@/types/Models";
import ModelEditDialog from "@/components/dialogs/ModelEditDialog.vue";
import {useRoute, useRouter} from "vue-router";
import {useUserPreferenceStore} from "@/stores/UserPreferenceStore";
import ModelMergeDialog from "@/components/dialogs/ModelMergeDialog.vue";
import {VDataTableUpdateOptions} from "@/vuetify";
import SyncDialog from "@/components/dialogs/SyncDialog.vue";
import {ApiApi, RecipeImport} from "@/openapi";
import {ApiApi, ApiRecipeListRequest, RecipeImport} from "@/openapi";
import {useTitle} from "@vueuse/core";
import RecipeShareDialog from "@/components/dialogs/RecipeShareDialog.vue";
import AddToShoppingDialog from "@/components/dialogs/AddToShoppingDialog.vue";
import BatchDeleteDialog from "@/components/dialogs/BatchDeleteDialog.vue";
import {useRouteQuery} from "@vueuse/router";
const {t} = useI18n()
const router = useRouter()
@@ -122,32 +152,27 @@ const itemsPerPageOptions = [
{value: 50, title: '50'},
]
const tablePage = ref(1)
const tableShowSelect = ref(false) // TODO enable once mass edit functions are implemented
const query = useRouteQuery('query', "")
const page = useRouteQuery('page', 1, {transform: Number})
const pageSize = useRouteQuery('pageSize', useUserPreferenceStore().deviceSettings.general_tableItemsPerPage, {transform: Number})
const selectedItems = ref([] as EditorSupportedTypes[])
const batchDeleteDialog = ref(false)
const batchMergeDialog = ref(false)
// data
const loading = ref(false);
const items = ref([] as Array<any>)
const itemCount = ref(0)
const searchQuery = ref('')
const genericModel = ref({} as GenericModel)
/**
* watch route changes (trough navigation) and set table page accordingly
*/
watch(() => route.query.page, () => {
if (!loading.value && typeof route.query.page == "string" && !isNaN(parseInt(route.query.page))) {
tablePage.value = parseInt(route.query.page)
}
})
// when navigating to ModelListPage from ModelListPage with a different model lifecycle hooks are not called so watch for change here
watch(() => props.model, (newValue, oldValue) => {
if (newValue != oldValue) {
genericModel.value = getGenericModelFromString(props.model, t)
tablePage.value = 1
loadItems({page: 1, itemsPerPage: useUserPreferenceStore().deviceSettings.general_tableItemsPerPage, search: searchQuery.value})
loadItems({page: 1})
}
})
@@ -163,10 +188,6 @@ onBeforeMount(() => {
}
title.value = t(genericModel.value.model.localizationKey)
if (typeof route.query.page == "string" && !isNaN(parseInt(route.query.page))) {
tablePage.value = parseInt(route.query.page)
}
})
/**
@@ -175,22 +196,14 @@ onBeforeMount(() => {
* @param options
*/
function loadItems(options: VDataTableUpdateOptions) {
loading.value = true
selectedItems.value = []
window.scrollTo({top: 0, behavior: 'smooth'})
if (tablePage.value != options.page) {
tablePage.value = options.page
}
if (route.query.page == undefined) {
router.replace({name: 'ModelListPage', params: {model: props.model}, query: {page: options.page}})
} else {
router.push({name: 'ModelListPage', params: {model: props.model}, query: {page: options.page}})
}
page.value = options.page
pageSize.value = options.itemsPerPage
useUserPreferenceStore().deviceSettings.general_tableItemsPerPage = options.itemsPerPage
genericModel.value.list({page: options.page, pageSize: options.itemsPerPage, query: options.search}).then((r: any) => {
genericModel.value.list({ query: query.value, page: options.page, pageSize: pageSize.value }).then((r: any) => {
items.value = r.results
itemCount.value = r.count
}).catch((err: any) => {
@@ -200,16 +213,6 @@ function loadItems(options: VDataTableUpdateOptions) {
})
}
/**
* change models and reset page/scroll
* @param m
*/
function changeModel(m: Model) {
tablePage.value = 1
router.push({name: 'ModelListPage', params: {model: m.name.toLowerCase()}, query: {page: 1}})
window.scrollTo({top: 0, behavior: 'smooth'})
}
// model specific functions
/**
@@ -219,7 +222,7 @@ function changeModel(m: Model) {
function importRecipe(item: RecipeImport) {
let api = new ApiApi()
api.apiRecipeImportImportRecipeCreate({id: item.id!, recipeImport: item}).then(r => {
loadItems({page: 1, itemsPerPage: useUserPreferenceStore().deviceSettings.general_tableItemsPerPage, search: searchQuery.value})
loadItems({page: 1})
}).catch(err => {
useMessageStore().addError(ErrorMessageType.CREATE_ERROR, err)
})
@@ -232,7 +235,7 @@ function importAllRecipes() {
let api = new ApiApi()
api.apiRecipeImportImportAllCreate({recipeImport: {} as RecipeImport}).then(r => {
loadItems({page: 1, itemsPerPage: useUserPreferenceStore().deviceSettings.general_tableItemsPerPage, search: searchQuery.value})
loadItems({page: 1})
}).catch(err => {
useMessageStore().addError(ErrorMessageType.CREATE_ERROR, err)
})

Some files were not shown because too many files have changed in this diff Show More