mirror of
https://github.com/TandoorRecipes/recipes.git
synced 2025-12-27 12:09:10 -05:00
Compare commits
47 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
c78b7a6928 | ||
|
|
7a2ccc075c | ||
|
|
237054c23e | ||
|
|
ac1d641bd5 | ||
|
|
3545b6e98a | ||
|
|
d3a56e00ea | ||
|
|
e9f8578c25 | ||
|
|
dccfc436be | ||
|
|
1e85c8587b | ||
|
|
b8f92ab054 | ||
|
|
766ed31f8e | ||
|
|
cad78e115d | ||
|
|
b599c4f6a9 | ||
|
|
439539f56d | ||
|
|
237bcb92c9 | ||
|
|
ce02a23dbb | ||
|
|
8e81512735 | ||
|
|
c69f0394a8 | ||
|
|
d7ca9e05de | ||
|
|
64534ff810 | ||
|
|
d0164a6c28 | ||
|
|
0f898ddf4a | ||
|
|
e903382034 | ||
|
|
0d225450da | ||
|
|
c077a64484 | ||
|
|
6c16094b42 | ||
|
|
5aa80746f9 | ||
|
|
cc64717818 | ||
|
|
6acd892116 | ||
|
|
3955408aa4 | ||
|
|
3de2468df3 | ||
|
|
b1d983fbc3 | ||
|
|
5f443d2593 | ||
|
|
436158f596 | ||
|
|
dcc56fc138 | ||
|
|
0eef10079b | ||
|
|
2b839dfb19 | ||
|
|
491b678d6e | ||
|
|
151dce006d | ||
|
|
d4f538b4aa | ||
|
|
a727439c57 | ||
|
|
f779107749 | ||
|
|
4a5c8f41fa | ||
|
|
bf458e22e8 | ||
|
|
9b8088fca2 | ||
|
|
68435aa335 | ||
|
|
afe5465044 |
8
.github/workflows/build-docker.yml
vendored
8
.github/workflows/build-docker.yml
vendored
@@ -17,15 +17,9 @@ jobs:
|
||||
# Standard build config
|
||||
- name: Standard
|
||||
dockerfile: Dockerfile
|
||||
platforms: linux/amd64,linux/arm64
|
||||
platforms: linux/amd64,linux/arm64,linux/arm/v7
|
||||
suffix: ""
|
||||
continue-on-error: false
|
||||
# Raspi build config
|
||||
- name: Raspi
|
||||
dockerfile: Dockerfile-raspi
|
||||
platforms: linux/arm/v7
|
||||
suffix: "-raspi"
|
||||
continue-on-error: true
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
FROM python:3.10-alpine3.15
|
||||
FROM python:3.10-alpine3.18
|
||||
|
||||
#Install all dependencies.
|
||||
RUN apk add --no-cache postgresql-libs postgresql-client gettext zlib libjpeg libwebp libxml2-dev libxslt-dev py-cryptography openldap
|
||||
RUN apk add --no-cache postgresql-libs postgresql-client gettext zlib libjpeg libwebp libxml2-dev libxslt-dev openldap
|
||||
|
||||
#Print all logs without buffering it.
|
||||
ENV PYTHONUNBUFFERED 1
|
||||
@@ -15,6 +15,10 @@ WORKDIR /opt/recipes
|
||||
|
||||
COPY requirements.txt ./
|
||||
|
||||
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 git && \
|
||||
echo -n "INPUT ( libldap.so )" > /usr/lib/libldap_r.so && \
|
||||
python -m venv venv && \
|
||||
|
||||
@@ -1,33 +0,0 @@
|
||||
# builds of cryptography for raspberry pi (or better arm v7) fail for some
|
||||
FROM python:3.9-alpine3.15
|
||||
|
||||
#Install all dependencies.
|
||||
RUN apk add --no-cache postgresql-libs postgresql-client gettext zlib libjpeg libwebp libxml2-dev libxslt-dev py-cryptography openldap gcompat
|
||||
|
||||
#Print all logs without buffering it.
|
||||
ENV PYTHONUNBUFFERED 1
|
||||
|
||||
#This port will be used by gunicorn.
|
||||
EXPOSE 8080
|
||||
|
||||
#Create app dir and install requirements.
|
||||
RUN mkdir /opt/recipes
|
||||
WORKDIR /opt/recipes
|
||||
|
||||
COPY requirements.txt ./
|
||||
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 zlib-dev jpeg-dev libwebp-dev python3-dev git && \
|
||||
echo -n "INPUT ( libldap.so )" > /usr/lib/libldap_r.so && \
|
||||
python -m venv venv && \
|
||||
/opt/recipes/venv/bin/python -m pip install --upgrade pip && \
|
||||
venv/bin/pip install wheel==0.37.1 && \
|
||||
venv/bin/pip install -r requirements.txt --no-cache-dir --no-binary=Pillow && \
|
||||
apk --purge del .build-deps
|
||||
|
||||
#Copy project and execute it.
|
||||
COPY . ./
|
||||
RUN chmod +x boot.sh
|
||||
ENTRYPOINT ["/opt/recipes/boot.sh"]
|
||||
@@ -39,6 +39,8 @@ def delete_space_action(modeladmin, request, queryset):
|
||||
class SpaceAdmin(admin.ModelAdmin):
|
||||
list_display = ('name', 'created_by', 'max_recipes', 'max_users', 'max_file_storage_mb', 'allow_sharing')
|
||||
search_fields = ('name', 'created_by__username')
|
||||
autocomplete_fields = ('created_by',)
|
||||
filter_horizontal = ('food_inherit',)
|
||||
list_filter = ('max_recipes', 'max_users', 'max_file_storage_mb', 'allow_sharing')
|
||||
date_hierarchy = 'created_at'
|
||||
actions = [delete_space_action]
|
||||
@@ -50,6 +52,8 @@ admin.site.register(Space, SpaceAdmin)
|
||||
class UserSpaceAdmin(admin.ModelAdmin):
|
||||
list_display = ('user', 'space',)
|
||||
search_fields = ('user__username', 'space__name',)
|
||||
filter_horizontal = ('groups',)
|
||||
autocomplete_fields = ('user', 'space',)
|
||||
|
||||
|
||||
admin.site.register(UserSpace, UserSpaceAdmin)
|
||||
@@ -60,6 +64,7 @@ class UserPreferenceAdmin(admin.ModelAdmin):
|
||||
search_fields = ('user__username',)
|
||||
list_filter = ('theme', 'nav_color', 'default_page',)
|
||||
date_hierarchy = 'created_at'
|
||||
filter_horizontal = ('plan_share', 'shopping_share',)
|
||||
|
||||
@staticmethod
|
||||
def name(obj):
|
||||
|
||||
@@ -434,3 +434,10 @@ def switch_user_active_space(user, space):
|
||||
return us
|
||||
except ObjectDoesNotExist:
|
||||
return None
|
||||
|
||||
|
||||
class IsReadOnlyDRF(permissions.BasePermission):
|
||||
message = 'You cannot interact with this object as it is not owned by you!'
|
||||
|
||||
def has_permission(self, request, view):
|
||||
return request.method in SAFE_METHODS
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import json
|
||||
import traceback
|
||||
from io import BytesIO, StringIO
|
||||
from re import match
|
||||
from zipfile import ZipFile
|
||||
@@ -19,7 +20,10 @@ class Default(Integration):
|
||||
recipe = self.decode_recipe(recipe_string)
|
||||
images = list(filter(lambda v: match('image.*', v), recipe_zip.namelist()))
|
||||
if images:
|
||||
self.import_recipe_image(recipe, BytesIO(recipe_zip.read(images[0])), filetype=get_filetype(images[0]))
|
||||
try:
|
||||
self.import_recipe_image(recipe, BytesIO(recipe_zip.read(images[0])), filetype=get_filetype(images[0]))
|
||||
except AttributeError as e:
|
||||
traceback.print_exc()
|
||||
return recipe
|
||||
|
||||
def decode_recipe(self, string):
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,18 @@
|
||||
# Generated by Django 4.1.9 on 2023-06-26 13:28
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('cookbook', '0193_space_internal_note'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AlterField(
|
||||
model_name='food',
|
||||
name='properties_food_amount',
|
||||
field=models.DecimalField(blank=True, decimal_places=2, default=100, max_digits=16),
|
||||
),
|
||||
]
|
||||
@@ -0,0 +1,34 @@
|
||||
# Generated by Django 4.1.9 on 2023-06-30 20:34
|
||||
|
||||
from django.db import migrations, models
|
||||
import django.db.models.deletion
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('cookbook', '0194_alter_food_properties_food_amount'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AddField(
|
||||
model_name='invitelink',
|
||||
name='internal_note',
|
||||
field=models.TextField(blank=True, null=True),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='userspace',
|
||||
name='internal_note',
|
||||
field=models.TextField(blank=True, null=True),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='userspace',
|
||||
name='invite_link',
|
||||
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.PROTECT, to='cookbook.invitelink'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='userpreference',
|
||||
name='theme',
|
||||
field=models.CharField(choices=[('TANDOOR', 'Tandoor'), ('BOOTSTRAP', 'Bootstrap'), ('DARKLY', 'Darkly'), ('FLATLY', 'Flatly'), ('SUPERHERO', 'Superhero'), ('TANDOOR_DARK', 'Tandoor Dark (INCOMPLETE)')], default='TANDOOR', max_length=128),
|
||||
),
|
||||
]
|
||||
@@ -331,6 +331,7 @@ class UserPreference(models.Model, PermissionModelMixin):
|
||||
FLATLY = 'FLATLY'
|
||||
SUPERHERO = 'SUPERHERO'
|
||||
TANDOOR = 'TANDOOR'
|
||||
TANDOOR_DARK = 'TANDOOR_DARK'
|
||||
|
||||
THEMES = (
|
||||
(TANDOOR, 'Tandoor'),
|
||||
@@ -338,6 +339,7 @@ class UserPreference(models.Model, PermissionModelMixin):
|
||||
(DARKLY, 'Darkly'),
|
||||
(FLATLY, 'Flatly'),
|
||||
(SUPERHERO, 'Superhero'),
|
||||
(TANDOOR_DARK, 'Tandoor Dark (INCOMPLETE)'),
|
||||
)
|
||||
|
||||
# Nav colors
|
||||
@@ -413,6 +415,9 @@ class UserSpace(models.Model, PermissionModelMixin):
|
||||
# that having more than one active space should just break certain parts of the application and not leak any data
|
||||
active = models.BooleanField(default=False)
|
||||
|
||||
invite_link = models.ForeignKey("InviteLink", on_delete=models.PROTECT, null=True, blank=True)
|
||||
internal_note = models.TextField(blank=True, null=True)
|
||||
|
||||
created_at = models.DateTimeField(auto_now_add=True)
|
||||
updated_at = models.DateTimeField(auto_now=True)
|
||||
|
||||
@@ -585,7 +590,7 @@ class Food(ExportModelOperationsMixin('food'), TreeModel, PermissionModelMixin):
|
||||
child_inherit_fields = models.ManyToManyField(FoodInheritField, blank=True, related_name='child_inherit')
|
||||
|
||||
properties = models.ManyToManyField("Property", blank=True, through='FoodProperty')
|
||||
properties_food_amount = models.IntegerField(default=100, blank=True)
|
||||
properties_food_amount = models.DecimalField(default=100, max_digits=16, decimal_places=2, blank=True)
|
||||
properties_food_unit = models.ForeignKey(Unit, on_delete=models.PROTECT, blank=True, null=True)
|
||||
|
||||
preferred_unit = models.ForeignKey(Unit, on_delete=models.SET_NULL, null=True, blank=True, default=None, related_name='preferred_unit')
|
||||
@@ -717,25 +722,6 @@ class Ingredient(ExportModelOperationsMixin('ingredient'), models.Model, Permiss
|
||||
space = models.ForeignKey(Space, on_delete=models.CASCADE)
|
||||
objects = ScopedManager(space='space')
|
||||
|
||||
def __str__(self):
|
||||
food = ""
|
||||
unit = ""
|
||||
if self.always_use_plural_food and self.food.plural_name not in (None, "") and not self.no_amount:
|
||||
food = self.food.plural_name
|
||||
else:
|
||||
if self.amount > 1 and self.food.plural_name not in (None, "") and not self.no_amount:
|
||||
food = self.food.plural_name
|
||||
else:
|
||||
food = str(self.food)
|
||||
if self.always_use_plural_unit and self.unit.plural_name not in (None, "") and not self.no_amount:
|
||||
unit = self.unit.plural_name
|
||||
else:
|
||||
if self.amount > 1 and self.unit is not None and self.unit.plural_name not in (None, "") and not self.no_amount:
|
||||
unit = self.unit.plural_name
|
||||
else:
|
||||
unit = str(self.unit)
|
||||
return str(self.amount) + ' ' + str(unit) + ' ' + str(food)
|
||||
|
||||
class Meta:
|
||||
ordering = ['order', 'pk']
|
||||
indexes = (
|
||||
@@ -1142,6 +1128,8 @@ class InviteLink(ExportModelOperationsMixin('invite_link'), models.Model, Permis
|
||||
created_by = models.ForeignKey(User, on_delete=models.CASCADE)
|
||||
created_at = models.DateTimeField(auto_now_add=True)
|
||||
|
||||
internal_note = models.TextField(blank=True, null=True)
|
||||
|
||||
space = models.ForeignKey(Space, on_delete=models.CASCADE)
|
||||
objects = ScopedManager(space='space')
|
||||
|
||||
|
||||
@@ -78,6 +78,19 @@ class ExtendedRecipeMixin(serializers.ModelSerializer):
|
||||
return path
|
||||
|
||||
|
||||
class OpenDataModelMixin(serializers.ModelSerializer):
|
||||
|
||||
def create(self, validated_data):
|
||||
if 'open_data_slug' in validated_data and validated_data['open_data_slug'] is not None and validated_data['open_data_slug'].strip() == '':
|
||||
validated_data['open_data_slug'] = None
|
||||
return super().create(validated_data)
|
||||
|
||||
def update(self, instance, validated_data):
|
||||
if 'open_data_slug' in validated_data and validated_data['open_data_slug'] is not None and validated_data['open_data_slug'].strip() == '':
|
||||
validated_data['open_data_slug'] = None
|
||||
return super().update(instance, validated_data)
|
||||
|
||||
|
||||
class CustomDecimalField(serializers.Field):
|
||||
"""
|
||||
Custom decimal field to normalize useless decimal places
|
||||
@@ -96,7 +109,7 @@ class CustomDecimalField(serializers.Field):
|
||||
if data == '':
|
||||
return 0
|
||||
try:
|
||||
return float(data.replace(',', ''))
|
||||
return float(data.replace(',', '.'))
|
||||
except ValueError:
|
||||
raise ValidationError('A valid number is required')
|
||||
|
||||
@@ -309,8 +322,8 @@ class UserSpaceSerializer(WritableNestedModelSerializer):
|
||||
|
||||
class Meta:
|
||||
model = UserSpace
|
||||
fields = ('id', 'user', 'space', 'groups', 'active', 'created_at', 'updated_at',)
|
||||
read_only_fields = ('id', 'created_at', 'updated_at', 'space')
|
||||
fields = ('id', 'user', 'space', 'groups', 'active', 'internal_note', 'invite_link', 'created_at', 'updated_at',)
|
||||
read_only_fields = ('id', 'invite_link', 'created_at', 'updated_at', 'space')
|
||||
|
||||
|
||||
class SpacedModelSerializer(serializers.ModelSerializer):
|
||||
@@ -440,7 +453,7 @@ class KeywordSerializer(UniqueFieldsMixin, ExtendedRecipeMixin):
|
||||
read_only_fields = ('id', 'label', 'numchild', 'parent', 'image')
|
||||
|
||||
|
||||
class UnitSerializer(UniqueFieldsMixin, ExtendedRecipeMixin):
|
||||
class UnitSerializer(UniqueFieldsMixin, ExtendedRecipeMixin, OpenDataModelMixin):
|
||||
recipe_filter = 'steps__ingredients__unit'
|
||||
|
||||
def create(self, validated_data):
|
||||
@@ -469,7 +482,7 @@ class UnitSerializer(UniqueFieldsMixin, ExtendedRecipeMixin):
|
||||
read_only_fields = ('id', 'numrecipe', 'image')
|
||||
|
||||
|
||||
class SupermarketCategorySerializer(UniqueFieldsMixin, WritableNestedModelSerializer):
|
||||
class SupermarketCategorySerializer(UniqueFieldsMixin, WritableNestedModelSerializer, OpenDataModelMixin):
|
||||
|
||||
def create(self, validated_data):
|
||||
name = validated_data.pop('name').strip()
|
||||
@@ -493,7 +506,7 @@ class SupermarketCategoryRelationSerializer(WritableNestedModelSerializer):
|
||||
fields = ('id', 'category', 'supermarket', 'order')
|
||||
|
||||
|
||||
class SupermarketSerializer(UniqueFieldsMixin, SpacedModelSerializer):
|
||||
class SupermarketSerializer(UniqueFieldsMixin, SpacedModelSerializer, OpenDataModelMixin):
|
||||
category_to_supermarket = SupermarketCategoryRelationSerializer(many=True, read_only=True)
|
||||
|
||||
class Meta:
|
||||
@@ -501,11 +514,13 @@ class SupermarketSerializer(UniqueFieldsMixin, SpacedModelSerializer):
|
||||
fields = ('id', 'name', 'description', 'category_to_supermarket', 'open_data_slug')
|
||||
|
||||
|
||||
class PropertyTypeSerializer(serializers.ModelSerializer):
|
||||
class PropertyTypeSerializer(OpenDataModelMixin, WritableNestedModelSerializer, UniqueFieldsMixin):
|
||||
id = serializers.IntegerField(required=False)
|
||||
|
||||
def create(self, validated_data):
|
||||
validated_data['space'] = self.context['request'].space
|
||||
|
||||
if property_type := PropertyType.objects.filter(Q(name=validated_data['name'])).first():
|
||||
if property_type := PropertyType.objects.filter(Q(name=validated_data['name'])).filter(space=self.context['request'].space).first():
|
||||
return property_type
|
||||
|
||||
return super().create(validated_data)
|
||||
@@ -519,8 +534,6 @@ class PropertySerializer(UniqueFieldsMixin, WritableNestedModelSerializer):
|
||||
property_type = PropertyTypeSerializer()
|
||||
property_amount = CustomDecimalField()
|
||||
|
||||
# TODO prevent updates
|
||||
|
||||
def create(self, validated_data):
|
||||
validated_data['space'] = self.context['request'].space
|
||||
return super().create(validated_data)
|
||||
@@ -528,7 +541,6 @@ class PropertySerializer(UniqueFieldsMixin, WritableNestedModelSerializer):
|
||||
class Meta:
|
||||
model = Property
|
||||
fields = ('id', 'property_amount', 'property_type')
|
||||
read_only_fields = ('id',)
|
||||
|
||||
|
||||
class RecipeSimpleSerializer(WritableNestedModelSerializer):
|
||||
@@ -556,7 +568,7 @@ class FoodSimpleSerializer(serializers.ModelSerializer):
|
||||
fields = ('id', 'name', 'plural_name')
|
||||
|
||||
|
||||
class FoodSerializer(UniqueFieldsMixin, WritableNestedModelSerializer, ExtendedRecipeMixin):
|
||||
class FoodSerializer(UniqueFieldsMixin, WritableNestedModelSerializer, ExtendedRecipeMixin, OpenDataModelMixin):
|
||||
supermarket_category = SupermarketCategorySerializer(allow_null=True, required=False)
|
||||
recipe = RecipeSimpleSerializer(allow_null=True, required=False)
|
||||
# shopping = serializers.SerializerMethodField('get_shopping_status')
|
||||
@@ -569,6 +581,7 @@ class FoodSerializer(UniqueFieldsMixin, WritableNestedModelSerializer, ExtendedR
|
||||
|
||||
properties = PropertySerializer(many=True, allow_null=True, required=False)
|
||||
properties_food_unit = UnitSerializer(allow_null=True, required=False)
|
||||
properties_food_amount = CustomDecimalField(required=False)
|
||||
|
||||
recipe_filter = 'steps__ingredients__food'
|
||||
images = ['recipe__image']
|
||||
@@ -636,8 +649,15 @@ class FoodSerializer(UniqueFieldsMixin, WritableNestedModelSerializer, ExtendedR
|
||||
if properties_food_unit := validated_data.pop('properties_food_unit', None):
|
||||
properties_food_unit = Unit.objects.filter(name=properties_food_unit['name']).first()
|
||||
|
||||
properties = validated_data.pop('properties', None)
|
||||
|
||||
obj, created = Food.objects.get_or_create(name=name, plural_name=plural_name, space=space, properties_food_unit=properties_food_unit,
|
||||
defaults=validated_data)
|
||||
|
||||
if properties and len(properties) > 0:
|
||||
for p in properties:
|
||||
obj.properties.add(Property.objects.create(property_type_id=p['property_type']['id'], property_amount=p['property_amount'], space=space))
|
||||
|
||||
return obj
|
||||
|
||||
def update(self, instance, validated_data):
|
||||
@@ -764,7 +784,7 @@ class StepRecipeSerializer(WritableNestedModelSerializer):
|
||||
)
|
||||
|
||||
|
||||
class UnitConversionSerializer(WritableNestedModelSerializer):
|
||||
class UnitConversionSerializer(WritableNestedModelSerializer, OpenDataModelMixin):
|
||||
name = serializers.SerializerMethodField('get_conversion_name')
|
||||
base_unit = UnitSerializer()
|
||||
converted_unit = UnitSerializer()
|
||||
@@ -1225,7 +1245,7 @@ class InviteLinkSerializer(WritableNestedModelSerializer):
|
||||
class Meta:
|
||||
model = InviteLink
|
||||
fields = (
|
||||
'id', 'uuid', 'email', 'group', 'valid_until', 'used_by', 'reusable', 'created_by', 'created_at',)
|
||||
'id', 'uuid', 'email', 'group', 'valid_until', 'used_by', 'reusable', 'internal_note', 'created_by', 'created_at',)
|
||||
read_only_fields = ('id', 'uuid', 'created_by', 'created_at',)
|
||||
|
||||
|
||||
@@ -1342,7 +1362,7 @@ class RecipeExportSerializer(WritableNestedModelSerializer):
|
||||
model = Recipe
|
||||
fields = (
|
||||
'name', 'description', 'keywords', 'steps', 'working_time',
|
||||
'waiting_time', 'internal', 'nutrition', 'servings', 'servings_text',
|
||||
'waiting_time', 'internal', 'nutrition', 'servings', 'servings_text', 'source_url',
|
||||
)
|
||||
|
||||
def create(self, validated_data):
|
||||
|
||||
10486
cookbook/static/themes/tandoor_dark.min.css
vendored
Normal file
10486
cookbook/static/themes/tandoor_dark.min.css
vendored
Normal file
File diff suppressed because it is too large
Load Diff
@@ -73,7 +73,7 @@
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<nav class="navbar navbar-expand-lg navbar-dark bg-{% nav_color request %}"
|
||||
<nav class="navbar navbar-expand-lg {% nav_color request %}"
|
||||
id="id_main_nav"
|
||||
style="{% sticky_nav request %}">
|
||||
|
||||
|
||||
@@ -16,6 +16,7 @@ def theme_url(request):
|
||||
UserPreference.DARKLY: 'themes/darkly.min.css',
|
||||
UserPreference.SUPERHERO: 'themes/superhero.min.css',
|
||||
UserPreference.TANDOOR: 'themes/tandoor.min.css',
|
||||
UserPreference.TANDOOR_DARK: 'themes/tandoor_dark.min.css',
|
||||
}
|
||||
if request.user.userpreference.theme in themes:
|
||||
return static(themes[request.user.userpreference.theme])
|
||||
@@ -26,8 +27,12 @@ def theme_url(request):
|
||||
@register.simple_tag
|
||||
def nav_color(request):
|
||||
if not request.user.is_authenticated:
|
||||
return 'primary'
|
||||
return request.user.userpreference.nav_color.lower()
|
||||
return 'navbar-light bg-primary'
|
||||
|
||||
if request.user.userpreference.nav_color.lower() in ['light', 'warning', 'info', 'success']:
|
||||
return f'navbar-light bg-{request.user.userpreference.nav_color.lower()}'
|
||||
else:
|
||||
return f'navbar-dark bg-{request.user.userpreference.nav_color.lower()}'
|
||||
|
||||
|
||||
@register.simple_tag
|
||||
|
||||
@@ -252,7 +252,7 @@ class MergeMixin(ViewSetMixin):
|
||||
|
||||
try:
|
||||
if isinstance(source, Food):
|
||||
source.properties.through.objects.all().delete()
|
||||
source.properties.remove()
|
||||
|
||||
for link in [field for field in source._meta.get_fields() if issubclass(type(field), ForeignObjectRel)]:
|
||||
linkManager = getattr(source, link.get_accessor_name())
|
||||
@@ -421,6 +421,10 @@ class UserSpaceViewSet(viewsets.ModelViewSet):
|
||||
return super().destroy(request, *args, **kwargs)
|
||||
|
||||
def get_queryset(self):
|
||||
internal_note = self.request.query_params.get('internal_note', None)
|
||||
if internal_note is not None:
|
||||
self.queryset = self.queryset.filter(internal_note=internal_note)
|
||||
|
||||
if is_space_owner(self.request.user, self.request.space):
|
||||
return self.queryset.filter(space=self.request.space)
|
||||
else:
|
||||
@@ -1047,6 +1051,21 @@ class ShoppingListEntryViewSet(viewsets.ModelViewSet):
|
||||
Q(created_by=self.request.user)
|
||||
| Q(shoppinglist__shared=self.request.user)
|
||||
| Q(created_by__in=list(self.request.user.get_shopping_share()))
|
||||
).prefetch_related(
|
||||
'created_by',
|
||||
'food',
|
||||
'food__properties',
|
||||
'food__properties__property_type',
|
||||
'food__inherit_fields',
|
||||
'food__supermarket_category',
|
||||
'food__onhand_users',
|
||||
'food__substitute',
|
||||
'food__child_inherit_fields',
|
||||
|
||||
'unit',
|
||||
'list_recipe',
|
||||
'list_recipe__mealplan',
|
||||
'list_recipe__mealplan__recipe',
|
||||
).distinct().all()
|
||||
|
||||
if pk := self.request.query_params.getlist('id', []):
|
||||
@@ -1165,6 +1184,11 @@ class InviteLinkViewSet(viewsets.ModelViewSet, StandardFilterMixin):
|
||||
permission_classes = [CustomIsSpaceOwner & CustomIsAdmin & CustomTokenHasReadWriteScope]
|
||||
|
||||
def get_queryset(self):
|
||||
|
||||
internal_note = self.request.query_params.get('internal_note', None)
|
||||
if internal_note is not None:
|
||||
self.queryset = self.queryset.filter(internal_note=internal_note)
|
||||
|
||||
if is_space_owner(self.request.user, self.request.space):
|
||||
self.queryset = self.queryset.filter(space=self.request.space).all()
|
||||
return super().get_queryset()
|
||||
@@ -1308,8 +1332,12 @@ def recipe_from_source(request):
|
||||
}, status=status.HTTP_400_BAD_REQUEST)
|
||||
else:
|
||||
try:
|
||||
json.loads(data)
|
||||
data = "<script type='application/ld+json'>" + data + "</script>"
|
||||
data_json = json.loads(data)
|
||||
if '@context' not in data_json:
|
||||
data_json['@context'] = 'https://schema.org'
|
||||
if '@type' not in data_json:
|
||||
data_json['@type'] = 'Recipe'
|
||||
data = "<script type='application/ld+json'>" + json.dumps(data_json) + "</script>"
|
||||
except JSONDecodeError:
|
||||
pass
|
||||
scrape = text_scraper(text=data, url=url)
|
||||
|
||||
@@ -370,7 +370,7 @@ def invite_link(request, token):
|
||||
link.used_by = request.user
|
||||
link.save()
|
||||
|
||||
user_space = UserSpace.objects.create(user=request.user, space=link.space, active=False)
|
||||
user_space = UserSpace.objects.create(user=request.user, space=link.space, internal_note=link.internal_note, invite_link=link, active=False)
|
||||
|
||||
if request.user.userspace_set.count() == 1:
|
||||
user_space.active = True
|
||||
|
||||
@@ -2,7 +2,7 @@ version: "2.4"
|
||||
services:
|
||||
db_recipes:
|
||||
restart: always
|
||||
image: postgres:11-alpine
|
||||
image: postgres:15-alpine
|
||||
volumes:
|
||||
- ${POSTGRES_DATA_DIR:-./postgresql}:/var/lib/postgresql/data
|
||||
env_file:
|
||||
@@ -22,6 +22,7 @@ services:
|
||||
- ./.env
|
||||
volumes:
|
||||
- staticfiles:/opt/recipes/staticfiles
|
||||
# Do not make this a bind mount, see https://docs.tandoor.dev/install/docker/#volumes-vs-bind-mounts
|
||||
- nginx_config:/opt/recipes/nginx/conf.d
|
||||
- ${MEDIA_FILES_DIR:-./mediafiles}:/opt/recipes/mediafiles
|
||||
depends_on:
|
||||
@@ -41,6 +42,7 @@ services:
|
||||
depends_on:
|
||||
- web_recipes
|
||||
volumes:
|
||||
# Do not make this a bind mount, see https://docs.tandoor.dev/install/docker/#volumes-vs-bind-mounts
|
||||
- nginx_config:/etc/nginx/conf.d:ro
|
||||
- staticfiles:/static:ro
|
||||
- ${MEDIA_FILES_DIR:-./mediafiles}:/media:ro
|
||||
|
||||
@@ -2,7 +2,7 @@ version: "3"
|
||||
services:
|
||||
db_recipes:
|
||||
restart: always
|
||||
image: postgres:11-alpine
|
||||
image: postgres:15-alpine
|
||||
volumes:
|
||||
- ./postgresql:/var/lib/postgresql/data
|
||||
env_file:
|
||||
@@ -17,6 +17,7 @@ services:
|
||||
- ./.env
|
||||
volumes:
|
||||
- staticfiles:/opt/recipes/staticfiles
|
||||
# Do not make this a bind mount, see https://docs.tandoor.dev/install/docker/#volumes-vs-bind-mounts
|
||||
- nginx_config:/opt/recipes/nginx/conf.d
|
||||
- ./mediafiles:/opt/recipes/mediafiles
|
||||
depends_on:
|
||||
@@ -32,6 +33,7 @@ services:
|
||||
depends_on:
|
||||
- web_recipes
|
||||
volumes:
|
||||
# Do not make this a bind mount, see https://docs.tandoor.dev/install/docker/#volumes-vs-bind-mounts
|
||||
- nginx_config:/etc/nginx/conf.d:ro
|
||||
- staticfiles:/static:ro
|
||||
- ./mediafiles:/media:ro
|
||||
|
||||
@@ -2,7 +2,7 @@ version: "3"
|
||||
services:
|
||||
db_recipes:
|
||||
restart: always
|
||||
image: postgres:11-alpine
|
||||
image: postgres:15-alpine
|
||||
volumes:
|
||||
- ./postgresql:/var/lib/postgresql/data
|
||||
env_file:
|
||||
@@ -15,6 +15,7 @@ services:
|
||||
- ./.env
|
||||
volumes:
|
||||
- staticfiles:/opt/recipes/staticfiles
|
||||
# Do not make this a bind mount, see https://docs.tandoor.dev/install/docker/#volumes-vs-bind-mounts
|
||||
- nginx_config:/opt/recipes/nginx/conf.d
|
||||
- ./mediafiles:/opt/recipes/mediafiles
|
||||
depends_on:
|
||||
@@ -30,6 +31,7 @@ services:
|
||||
depends_on:
|
||||
- web_recipes
|
||||
volumes:
|
||||
# Do not make this a bind mount, see https://docs.tandoor.dev/install/docker/#volumes-vs-bind-mounts
|
||||
- nginx_config:/etc/nginx/conf.d:ro
|
||||
- staticfiles:/static:ro
|
||||
- ./mediafiles:/media:ro
|
||||
|
||||
@@ -2,7 +2,7 @@ version: "3"
|
||||
services:
|
||||
db_recipes:
|
||||
restart: always
|
||||
image: postgres:11-alpine
|
||||
image: postgres:15-alpine
|
||||
volumes:
|
||||
- ./postgresql:/var/lib/postgresql/data
|
||||
env_file:
|
||||
@@ -17,6 +17,7 @@ services:
|
||||
- ./.env
|
||||
volumes:
|
||||
- staticfiles:/opt/recipes/staticfiles
|
||||
# Do not make this a bind mount, see https://docs.tandoor.dev/install/docker/#volumes-vs-bind-mounts
|
||||
- nginx_config:/opt/recipes/nginx/conf.d
|
||||
- ./mediafiles:/opt/recipes/mediafiles
|
||||
depends_on:
|
||||
@@ -30,6 +31,7 @@ services:
|
||||
env_file:
|
||||
- ./.env
|
||||
volumes:
|
||||
# Do not make this a bind mount, see https://docs.tandoor.dev/install/docker/#volumes-vs-bind-mounts
|
||||
- nginx_config:/etc/nginx/conf.d:ro
|
||||
- staticfiles:/static:ro
|
||||
- ./mediafiles:/media:ro
|
||||
|
||||
@@ -69,7 +69,7 @@ services:
|
||||
db_recipes:
|
||||
restart: always
|
||||
container_name: db_recipes
|
||||
image: postgres:11-alpine
|
||||
image: postgres:15-alpine
|
||||
volumes:
|
||||
- ./recipes/db:/var/lib/postgresql/data
|
||||
env_file:
|
||||
|
||||
133
docs/install/truenas_portainer.md
Normal file
133
docs/install/truenas_portainer.md
Normal file
@@ -0,0 +1,133 @@
|
||||
!!! info "Community Contributed"
|
||||
This guide was contributed by the community and is neither officially supported, nor updated or tested.
|
||||
|
||||
This guide is to assist those installing Tandoor Recipes on Truenas Core using Docker and or Portainer
|
||||
|
||||
Docker install instructions adapted from [PhasedLogix IT Services's guide](https://getmethegeek.com/blog/2021-01-07-add-docker-capabilities-to-truenas-core/). Portainer install instructions adopted from the [Portainer Official Documentation](https://docs.portainer.io/start/install-ce/server/docker/linux). Tandoor installation on Portainer provided by users `Szeraax` and `TransatlanticFoe` on Discord (Thank you two!)
|
||||
|
||||
## **Instructions**
|
||||
|
||||
Basic guide to setup Docker and Portainer TrueNAS Core.
|
||||
|
||||
### 1. Login to TrueNAS through your browser
|
||||
- Go to the Virtual Machines Menu
|
||||

|
||||
- Click next to dedicate resources to the VM (see below image of authors setup, you may need to change resources to fit your needs)
|
||||

|
||||
- Hit next to go to disk setup
|
||||
-You want to create a new disk, here are the settings you should use
|
||||
-Disk Type: AHCI
|
||||
-Zvol location: tank/vm (Or wherever you have your VM memory located at)
|
||||
-Size: Atleast 30 gigs
|
||||

|
||||
-Hit next to go to network interface (The defaults are fine but make sure you select the right network adapter)
|
||||
-Hit next to go to installation
|
||||
-Navigate to your ubuntu ISO file (The original author and this author used Ubuntu Server. This OS uses less resources than some other OS's and can be ran Headless with either VNC or SSH access. You can use other OS's, but this guide was written with Ubuntu Server)
|
||||
-Hit next, then submit, you have made the virtual machine!
|
||||
-Open the virtual machine then hit VNC to open ubuntu
|
||||

|
||||
-Once its up choose your language and go through the installer
|
||||
-Once you are done with the setup we want to SSH into the ubuntu VM to setup docker
|
||||
-Open powershell and type SSH "user"@(ip) (replace "user" with the user you setup in the OS installation)
|
||||
-Enter your Password if requested
|
||||
-Close the VNC Console
|
||||
-Go back into the SSH console and get ready to type some commands. Type these commands in order:
|
||||
`sudo apt update`
|
||||
`sudo apt install apt-transport-https ca-certificates curl software-properties-common`
|
||||
`y` (If prompted with a question)
|
||||
`curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo apt-key add -`
|
||||
`sudo add-apt-repository "deb [arch=amd64] https://download.docker.com/linux/ubuntu focal stable"`
|
||||
`sudo apt update`
|
||||
`apt-cache policy docker-ce`
|
||||
-To make it so you don’t have to use sudo for every docker command run this command
|
||||
`sudo usermod -aG docker ${USER}`
|
||||
`su - ${USER}`
|
||||
|
||||
### 2. Install Portainer
|
||||
!!! Note: By default, Portainer Server will expose the UI over port 9443 and expose a TCP tunnel server over port 8000. The latter is optional and is only required if you plan to use the Edge compute features with Edge agents.
|
||||
|
||||
-First, create the volume that Portainer Server will use to store its database:
|
||||
`docker volume create portainer_data`
|
||||
-Then, download and install the Portainer Server container:
|
||||
`docker run -d -p 8000:8000 -p 9443:9443 --name portainer --restart=always -v /var/run/docker.sock:/var/run/docker.sock -v portainer_data:/data portainer/portainer-ce:latest`
|
||||
-Portainer Server has now been installed. You can check to see whether the Portainer Server container has started by running `docker ps`
|
||||
-Now that the installation is complete, you can log into your Portainer Server instance by opening a web browser and going to:
|
||||
`https://localhost:9443`
|
||||
-Replace `localhost` with the relevant IP address or FQDN if needed, and adjust the port if you changed it earlier.
|
||||
-You will be presented with the initial setup page for Portainer Server.
|
||||
-Create your first user
|
||||
-Your first user will be an administrator. The username defaults to admin but you can change it if you prefer. The password must be at least 12 characters long and meet the listed password requirements.
|
||||
-Connect Portainer to your environments.
|
||||
-Once the admin user has been created, the "Environment Wizard" will automatically launch. The wizard will help get you started with Portainer.
|
||||
-Select "Get Started" to use the Enviroment Portainer is running in
|
||||

|
||||
|
||||
### 3. Install Tandoor Recipies VIA Portainer Web Editor
|
||||
-From the menu select Stacks, click Add stack, give the stack a descriptive name then select Web editor.
|
||||

|
||||
-Use the below code and input it into the Web Editor:
|
||||
|
||||
`version: "3"
|
||||
services:
|
||||
db_recipes:
|
||||
restart: always
|
||||
image: postgres:15-alpine
|
||||
volumes:
|
||||
- ./postgresql:/var/lib/postgresql/data
|
||||
env_file:
|
||||
- stack.env
|
||||
|
||||
web_recipes:
|
||||
# image: vabene1111/recipes:latest
|
||||
image: vabene1111/recipes:beta
|
||||
env_file:
|
||||
- stack.env
|
||||
volumes:
|
||||
- staticfiles:/opt/recipes/staticfiles
|
||||
# Do not make this a bind mount, see https://docs.tandoor.dev/install/docker/#volumes-vs-bind-mounts
|
||||
- nginx_config:/opt/recipes/nginx/conf.d
|
||||
- ./mediafiles:/opt/recipes/mediafiles
|
||||
depends_on:
|
||||
- db_recipes
|
||||
|
||||
nginx_recipes:
|
||||
image: nginx:mainline-alpine
|
||||
restart: always
|
||||
ports:
|
||||
- 12008:80
|
||||
env_file:
|
||||
- stack.env
|
||||
depends_on:
|
||||
- web_recipes
|
||||
volumes:
|
||||
# Do not make this a bind mount, see https://docs.tandoor.dev/install/docker/#volumes-vs-bind-mounts
|
||||
- nginx_config:/etc/nginx/conf.d:ro
|
||||
- staticfiles:/static
|
||||
- ./mediafiles:/media
|
||||
|
||||
volumes:
|
||||
nginx_config:
|
||||
staticfiles:`
|
||||
|
||||
-Download the .env template from [HERE](https://raw.githubusercontent.com/vabene1111/recipes/develop/.env.template) and load this file by pressing the "Load Variables from .env File" button:
|
||||

|
||||
|
||||
-You will need to change the following variables:
|
||||
-`SECRET_KEY` needs to be replaced with a new key. This can be generated from websites like [Djecrety](https://djecrety.ir/)
|
||||
-`TIMEZONE` needs to be replaced with the appropriate code for your timezone. Accepted values can be found at [TimezoneDB](https://timezonedb.com/time-zones)
|
||||
-`POSTGRES_USER` and `POSTGRES_PASSWORD` needs to be replaced with your username and password from [PostgreSQL](https://www.postgresql.org/) !!!NOTE Do not sign in using social media. You need to sign up using Email and Password.
|
||||
-After those veriables are changed, you may press the `Deploy the Stack` button at the bottom of the page. This will create the needed containers to run Tandoor Recipes.
|
||||
|
||||
### 4. Login and Setup your new server!
|
||||
- You need to access your Tandoor Server through its Webpage: `https://localhost:xxxx` replacing `localhost` with the IP of the VM running Docker and `xxxx` with the port you chose in the Web Editor for `nginx_recipes` above. In this case, `12008`.
|
||||
!!! While the containers are starting and doing whatever they need to do, you might still get HTTP errors e.g. 500 or 502. Just be patient and try again in a moment
|
||||
-You will now need to set up the Tandoor Server through the WebGUI.
|
||||
@@ -33,6 +33,7 @@ nav:
|
||||
- Synology: install/synology.md
|
||||
- Kubernetes: install/kubernetes.md
|
||||
- KubeSail or PiBox: install/kubesail.md
|
||||
- TrueNAS Portainer: install/truenas_portainer.md
|
||||
- WSL: install/wsl.md
|
||||
- Manual: install/manual.md
|
||||
- Other setups: install/other.md
|
||||
|
||||
@@ -128,34 +128,40 @@ INSTALLED_APPS = [
|
||||
'treebeard',
|
||||
]
|
||||
|
||||
PLUGINS_DIRECTORY = os.path.join(BASE_DIR, 'recipes', 'plugins')
|
||||
PLUGINS = []
|
||||
try:
|
||||
for d in os.listdir(os.path.join(BASE_DIR, 'recipes', 'plugins')):
|
||||
if d != '__pycache__':
|
||||
try:
|
||||
apps_path = f'recipes.plugins.{d}.apps'
|
||||
__import__(apps_path)
|
||||
app_config_classname = dir(sys.modules[apps_path])[1]
|
||||
plugin_module = f'recipes.plugins.{d}.apps.{app_config_classname}'
|
||||
if plugin_module not in INSTALLED_APPS:
|
||||
INSTALLED_APPS.append(plugin_module)
|
||||
plugin_class = getattr(
|
||||
sys.modules[apps_path], app_config_classname)
|
||||
plugin_config = {
|
||||
'name': plugin_class.verbose_name if hasattr(plugin_class, 'verbose_name') else plugin_class.name,
|
||||
'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)
|
||||
except Exception:
|
||||
if DEBUG:
|
||||
traceback.print_exc()
|
||||
print(f'ERROR failed to initialize plugin {d}')
|
||||
if os.path.isdir(PLUGINS_DIRECTORY):
|
||||
for d in os.listdir(PLUGINS_DIRECTORY):
|
||||
if d != '__pycache__':
|
||||
try:
|
||||
apps_path = f'recipes.plugins.{d}.apps'
|
||||
__import__(apps_path)
|
||||
app_config_classname = dir(sys.modules[apps_path])[1]
|
||||
plugin_module = f'recipes.plugins.{d}.apps.{app_config_classname}'
|
||||
plugin_class = getattr(sys.modules[apps_path], app_config_classname)
|
||||
plugin_disabled = False
|
||||
if hasattr(plugin_class, 'disabled'):
|
||||
plugin_disabled = plugin_class.disabled
|
||||
if plugin_module not in INSTALLED_APPS and not plugin_disabled:
|
||||
INSTALLED_APPS.append(plugin_module)
|
||||
|
||||
plugin_config = {
|
||||
'name': plugin_class.verbose_name if hasattr(plugin_class, 'verbose_name') else plugin_class.name,
|
||||
'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')
|
||||
except Exception:
|
||||
if DEBUG:
|
||||
traceback.print_exc()
|
||||
print(f'ERROR failed to initialize plugin {d}')
|
||||
except Exception:
|
||||
if DEBUG:
|
||||
print('ERROR failed to initialize plugins')
|
||||
@@ -522,4 +528,4 @@ DEFAULT_FROM_EMAIL = os.getenv('DEFAULT_FROM_EMAIL', 'webmaster@localhost')
|
||||
ACCOUNT_EMAIL_SUBJECT_PREFIX = os.getenv(
|
||||
'ACCOUNT_EMAIL_SUBJECT_PREFIX', '[Tandoor Recipes] ') # allauth sender prefix
|
||||
|
||||
mimetypes.add_type("text/javascript", ".js", True)
|
||||
mimetypes.add_type("text/javascript", ".js", True)
|
||||
|
||||
@@ -1254,23 +1254,28 @@ export default {
|
||||
ing_list.forEach((ing) => {
|
||||
if (ing.trim() !== "") {
|
||||
promises.push(this.genericPostAPI("api_ingredient_from_string", {text: ing}).then((result) => {
|
||||
|
||||
let unit = null
|
||||
if (result.data.unit !== "" && result.data.unit !== null) {
|
||||
unit = {name: result.data.unit}
|
||||
}
|
||||
parsed_ing_list.push({
|
||||
let new_ingredient = {
|
||||
amount: result.data.amount,
|
||||
unit: unit,
|
||||
food: {name: result.data.food},
|
||||
note: result.data.note,
|
||||
original_text: ing,
|
||||
})
|
||||
}
|
||||
console.log(ing, new_ingredient)
|
||||
parsed_ing_list.push(new_ingredient)
|
||||
}))
|
||||
}
|
||||
})
|
||||
Promise.allSettled(promises).then(() => {
|
||||
ing_list.forEach(ing => {
|
||||
step.ingredients.push(parsed_ing_list.find(x => x.original_text === ing))
|
||||
if(ing.trim() !== ""){
|
||||
step.ingredients.push(parsed_ing_list.find(x => x.original_text === ing))
|
||||
}
|
||||
})
|
||||
})
|
||||
},
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<template>
|
||||
<div id="app">
|
||||
<recipe-view-component></recipe-view-component>
|
||||
<div id="app" v-if="recipe_id !== undefined">
|
||||
<recipe-view-component :recipe_id="recipe_id"></recipe-view-component>
|
||||
|
||||
<bottom-navigation-bar></bottom-navigation-bar>
|
||||
</div>
|
||||
@@ -12,6 +12,7 @@ import {BootstrapVue} from "bootstrap-vue"
|
||||
import "bootstrap-vue/dist/bootstrap-vue.css"
|
||||
|
||||
import RecipeViewComponent from "@/components/RecipeViewComponent.vue";
|
||||
import BottomNavigationBar from "@/components/BottomNavigationBar.vue";
|
||||
|
||||
Vue.use(BootstrapVue)
|
||||
|
||||
@@ -19,13 +20,15 @@ export default {
|
||||
name: "RecipeView",
|
||||
mixins: [],
|
||||
components: {
|
||||
RecipeViewComponent
|
||||
RecipeViewComponent,
|
||||
BottomNavigationBar
|
||||
},
|
||||
computed: {},
|
||||
data() {
|
||||
return {}
|
||||
return {
|
||||
recipe_id: window.RECIPE_ID
|
||||
}
|
||||
},
|
||||
|
||||
mounted() {
|
||||
this.$i18n.locale = window.CUSTOM_LOCALE
|
||||
},
|
||||
|
||||
@@ -71,8 +71,8 @@
|
||||
}}</span></span>
|
||||
</td>
|
||||
<td>
|
||||
<button class="btn btn-danger btn-small" @click="deleteProperty(fp)"><i
|
||||
class="fas fa-trash-alt"></i></button>
|
||||
<a class="btn btn-danger btn-small" @click="deleteProperty(fp)"><i
|
||||
class="fas fa-trash-alt"></i></a>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
@@ -293,7 +293,9 @@ export default {
|
||||
if (this.item1.id !== undefined) {
|
||||
pf = apiClient.retrieveFood(this.item1.id).then((r) => {
|
||||
this.food = r.data
|
||||
this.food.properties_food_unit = {name: 'g'}
|
||||
if (this.food.properties_food_unit === null) {
|
||||
this.food.properties_food_unit = {name: 'g'}
|
||||
}
|
||||
}).catch(err => {
|
||||
StandardToasts.makeStandardToast(this, StandardToasts.FAIL_FETCH, err)
|
||||
})
|
||||
|
||||
@@ -1,8 +1,14 @@
|
||||
<template>
|
||||
<div v-if="recipe.keywords.length > 0">
|
||||
<span :key="k.id" v-for="k in recipe.keywords.slice(0,keyword_splice).filter((kk) => { return kk.show || kk.show === undefined })" class="pl-1">
|
||||
<a :href="`${resolveDjangoUrl('view_search')}?keyword=${k.id}`"><b-badge pill variant="light"
|
||||
class="font-weight-normal">{{ k.label }}</b-badge></a>
|
||||
<template v-if="enable_keyword_links">
|
||||
<a :href="`${resolveDjangoUrl('view_search')}?keyword=${k.id}`">
|
||||
<b-badge pill variant="light" class="font-weight-normal">{{ k.label }}</b-badge>
|
||||
</a>
|
||||
</template>
|
||||
<template v-else>
|
||||
<b-badge pill variant="light" class="font-weight-normal">{{ k.label }}</b-badge>
|
||||
</template>
|
||||
|
||||
</span>
|
||||
</div>
|
||||
@@ -18,10 +24,11 @@ export default {
|
||||
props: {
|
||||
recipe: Object,
|
||||
limit: Number,
|
||||
enable_keyword_links: {type: Boolean, default: true}
|
||||
},
|
||||
computed: {
|
||||
keyword_splice: function (){
|
||||
if(this.limit){
|
||||
keyword_splice: function () {
|
||||
if (this.limit) {
|
||||
return this.limit
|
||||
}
|
||||
return this.recipe.keywords.lenght
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<template>
|
||||
<div>
|
||||
<b-form-group v-bind:label="label" class="mb-3">
|
||||
<b-form-group v-bind:label="field_label" class="mb-3">
|
||||
<b-form-select v-model="new_value" :placeholder="placeholder" :options="translatedOptions"></b-form-select>
|
||||
</b-form-group>
|
||||
</div>
|
||||
@@ -10,12 +10,13 @@
|
||||
export default {
|
||||
name: "ChoiceInput",
|
||||
props: {
|
||||
field: { type: String, default: "You Forgot To Set Field Name" },
|
||||
label: { type: String, default: "Text Field" },
|
||||
value: { type: String, default: "" },
|
||||
field: {type: String, default: "You Forgot To Set Field Name"},
|
||||
label: {type: String, default: "Text Field"},
|
||||
value: {type: String, default: ""},
|
||||
options: [],
|
||||
placeholder: { type: String, default: "You Should Add Placeholder Text" },
|
||||
show_merge: { type: Boolean, default: false },
|
||||
placeholder: {type: String, default: "You Should Add Placeholder Text"},
|
||||
show_merge: {type: Boolean, default: false},
|
||||
optional: {type: Boolean, default: false},
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
@@ -31,9 +32,16 @@ export default {
|
||||
},
|
||||
},
|
||||
computed: {
|
||||
field_label: function () {
|
||||
if (this.optional) {
|
||||
return this.label
|
||||
} else {
|
||||
return this.label + '*'
|
||||
}
|
||||
},
|
||||
translatedOptions() {
|
||||
return this.options.map((x) => {
|
||||
return { ...x, text: this.$t(x.text) }
|
||||
return {...x, text: this.$t(x.text)}
|
||||
})
|
||||
},
|
||||
},
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<template>
|
||||
<div>
|
||||
<b-form-group v-bind:label="label" class="mb-3">
|
||||
<b-form-group v-bind:label="field_label" class="mb-3">
|
||||
<b-form-input v-model="new_value" type="date" ></b-form-input>
|
||||
<em v-if="help" class="small text-muted">{{ help }}</em>
|
||||
<small v-if="subtitle" class="text-muted">{{ subtitle }}</small>
|
||||
@@ -17,6 +17,16 @@ export default {
|
||||
value: { type: String, default: "" },
|
||||
help: { type: String, default: undefined },
|
||||
subtitle: { type: String, default: undefined },
|
||||
optional: {type: Boolean, default: false},
|
||||
},
|
||||
computed: {
|
||||
field_label: function () {
|
||||
if (this.optional) {
|
||||
return this.label
|
||||
} else {
|
||||
return this.label + '*'
|
||||
}
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
<template>
|
||||
<div>
|
||||
<b-form-group
|
||||
v-bind:label="label"
|
||||
v-bind:label="field_label"
|
||||
class="mb-3">
|
||||
|
||||
<input class="form-control" v-model="new_value">
|
||||
@@ -27,6 +27,7 @@ export default {
|
||||
field: {type: String, default: 'You Forgot To Set Field Name'},
|
||||
label: {type: String, default: ''},
|
||||
value: {type: String, default: ''},
|
||||
optional: {type: Boolean, default: false},
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
@@ -36,6 +37,15 @@ export default {
|
||||
emojisOutput: ""
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
field_label: function () {
|
||||
if (this.optional) {
|
||||
return this.label
|
||||
} else {
|
||||
return this.label + '*'
|
||||
}
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
'new_value': function () {
|
||||
this.$root.$emit('change', this.field, this.new_value ?? null)
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
<template>
|
||||
<div>
|
||||
<b-form-group
|
||||
v-bind:label="label"
|
||||
v-bind:label="field_label"
|
||||
class="mb-3">
|
||||
<b-form-file
|
||||
v-model="new_value"
|
||||
@@ -30,7 +30,17 @@ export default {
|
||||
value: {type: String, default: ''},
|
||||
placeholder: {type: String, default: ''},
|
||||
show_merge: {type: Boolean, default: false},
|
||||
optional: {type: Boolean, default: false},
|
||||
},
|
||||
computed: {
|
||||
field_label: function () {
|
||||
if (this.optional) {
|
||||
return this.label
|
||||
} else {
|
||||
return this.label + '*'
|
||||
}
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
new_value: undefined,
|
||||
|
||||
@@ -11,15 +11,16 @@
|
||||
</template>
|
||||
<div v-for="(f, i) in form.fields" v-bind:key="i">
|
||||
<p v-if="visibleCondition(f, 'instruction')">{{ f.label }}</p>
|
||||
<lookup-input v-if="visibleCondition(f, 'lookup')" :form="f" :model="listModel(f.list)" @change="storeValue" :help="showHelp && f.help"/>
|
||||
<lookup-input v-if="visibleCondition(f, 'lookup')" :form="f" :model="listModel(f.list)" @change="storeValue" :help="showHelp && f.help" :optional="f.optional"/>
|
||||
<checkbox-input class="mb-3" v-if="visibleCondition(f, 'checkbox')" :label="f.label" :value="f.value" :field="f.field" :help="showHelp && f.help"/>
|
||||
<text-input v-if="visibleCondition(f, 'text')" :label="f.label" :value="f.value" :field="f.field" :placeholder="f.placeholder" :help="showHelp && f.help" :subtitle="f.subtitle" :disabled="f.disabled"/>
|
||||
<choice-input v-if="visibleCondition(f, 'choice')" :label="f.label" :value="f.value" :field="f.field" :options="f.options" :placeholder="f.placeholder"/>
|
||||
<emoji-input v-if="visibleCondition(f, 'emoji')" :label="f.label" :value="f.value" :field="f.field" @change="storeValue"/>
|
||||
<file-input v-if="visibleCondition(f, 'file')" :label="f.label" :value="f.value" :field="f.field" @change="storeValue"/>
|
||||
<small-text v-if="visibleCondition(f, 'smalltext')" :value="f.value"/>
|
||||
<date-input v-if="visibleCondition(f, 'date')" :label="f.label" :value="f.value" :field="f.field" :help="showHelp && f.help" :subtitle="f.subtitle"/>
|
||||
<number-input v-if="visibleCondition(f, 'number')" :label="f.label" :value="f.value" :field="f.field" :placeholder="f.placeholder" :help="showHelp && f.help" :subtitle="f.subtitle"/>
|
||||
<text-input v-if="visibleCondition(f, 'text')" :label="f.label" :value="f.value" :field="f.field" :placeholder="f.placeholder" :help="showHelp && f.help" :subtitle="f.subtitle" :disabled="f.disabled" :optional="f.optional"/>
|
||||
<text-area-input v-if="visibleCondition(f, 'textarea')" :label="f.label" :value="f.value" :field="f.field" :placeholder="f.placeholder" :help="showHelp && f.help" :subtitle="f.subtitle" :disabled="f.disabled" :optional="f.optional"/>
|
||||
<choice-input v-if="visibleCondition(f, 'choice')" :label="f.label" :value="f.value" :field="f.field" :options="f.options" :placeholder="f.placeholder" :optional="f.optional"/>
|
||||
<emoji-input v-if="visibleCondition(f, 'emoji')" :label="f.label" :value="f.value" :field="f.field" @change="storeValue" :optional="f.optional"/>
|
||||
<file-input v-if="visibleCondition(f, 'file')" :label="f.label" :value="f.value" :field="f.field" @change="storeValue" :optional="f.optional"/>
|
||||
<small-text v-if="visibleCondition(f, 'smalltext')" :value="f.value" />
|
||||
<date-input v-if="visibleCondition(f, 'date')" :label="f.label" :value="f.value" :field="f.field" :help="showHelp && f.help" :subtitle="f.subtitle" :optional="f.optional"/>
|
||||
<number-input v-if="visibleCondition(f, 'number')" :label="f.label" :value="f.value" :field="f.field" :placeholder="f.placeholder" :help="showHelp && f.help" :subtitle="f.subtitle" :optional="f.optional"/>
|
||||
</div>
|
||||
<template v-slot:modal-footer>
|
||||
<div class="row w-100">
|
||||
@@ -57,6 +58,7 @@ import FileInput from "@/components/Modals/FileInput"
|
||||
import SmallText from "@/components/Modals/SmallText"
|
||||
import HelpBadge from "@/components/Badges/Help"
|
||||
import NumberInput from "@/components/Modals/NumberInput.vue";
|
||||
import TextAreaInput from "@/components/Modals/TextAreaInput.vue";
|
||||
|
||||
export default {
|
||||
name: "GenericModalForm",
|
||||
@@ -70,7 +72,8 @@ export default {
|
||||
SmallText,
|
||||
HelpBadge,
|
||||
DateInput,
|
||||
NumberInput
|
||||
NumberInput,
|
||||
TextAreaInput
|
||||
},
|
||||
mixins: [ApiMixin, ToastMixin],
|
||||
props: {
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
<div>
|
||||
<b-form-group :class="class_list">
|
||||
<template #label v-if="show_label">
|
||||
{{ form.label }}
|
||||
{{ field_label }}
|
||||
</template>
|
||||
<generic-multiselect
|
||||
@change="new_value = $event.val"
|
||||
@@ -27,11 +27,11 @@
|
||||
|
||||
<script>
|
||||
import GenericMultiselect from "@/components/GenericMultiselect"
|
||||
import { StandardToasts, ApiMixin } from "@/utils/utils"
|
||||
import {StandardToasts, ApiMixin} from "@/utils/utils"
|
||||
|
||||
export default {
|
||||
name: "LookupInput",
|
||||
components: { GenericMultiselect },
|
||||
components: {GenericMultiselect},
|
||||
mixins: [ApiMixin],
|
||||
props: {
|
||||
form: {
|
||||
@@ -46,11 +46,13 @@ export default {
|
||||
return undefined
|
||||
},
|
||||
},
|
||||
class_list: { type: String, default: "mb-3" },
|
||||
show_label: { type: Boolean, default: true },
|
||||
clear: { type: Number },
|
||||
help: { type: String, default: undefined },
|
||||
class_list: {type: String, default: "mb-3"},
|
||||
show_label: {type: Boolean, default: true},
|
||||
clear: {type: Number},
|
||||
help: {type: String, default: undefined},
|
||||
optional: {type: Boolean, default: false},
|
||||
},
|
||||
|
||||
data() {
|
||||
return {
|
||||
new_value: undefined,
|
||||
@@ -67,7 +69,7 @@ export default {
|
||||
this.label = this.form?.label ?? ""
|
||||
this.sticky_options = this.form?.sticky_options ?? []
|
||||
this.sticky_options = this.sticky_options.map((x) => {
|
||||
return { ...x, name: this.$t(x.name) }
|
||||
return {...x, name: this.$t(x.name)}
|
||||
})
|
||||
this.list_label = this.form?.list_label ?? undefined
|
||||
if (this.list_label?.includes("::")) {
|
||||
@@ -75,6 +77,13 @@ export default {
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
field_label: function () {
|
||||
if (this.optional) {
|
||||
return this.form.label
|
||||
} else {
|
||||
return this.form.label + '*'
|
||||
}
|
||||
},
|
||||
modelName() {
|
||||
return this.$t(this?.model?.name) ?? this.$t("Search")
|
||||
},
|
||||
@@ -124,7 +133,7 @@ export default {
|
||||
let item = undefined
|
||||
let label = this.form.list_label.split("::")
|
||||
itemlist.forEach((x) => {
|
||||
item = { ...x }
|
||||
item = {...x}
|
||||
for (const [k, v] of Object.entries(x)) {
|
||||
if (k == label[0]) {
|
||||
item["id"] = v.id
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<template>
|
||||
<div>
|
||||
<b-form-group v-bind:label="label" class="mb-3">
|
||||
<b-form-group v-bind:label="field_label" class="mb-3">
|
||||
<b-form-input v-model="new_value" type="number" :placeholder="placeholder"></b-form-input>
|
||||
<em v-if="help" class="small text-muted">{{ help }}</em>
|
||||
<small v-if="subtitle" class="text-muted">{{ subtitle }}</small>
|
||||
@@ -18,6 +18,16 @@ export default {
|
||||
placeholder: { type: Number, default: 0 },
|
||||
help: { type: String, default: undefined },
|
||||
subtitle: { type: String, default: undefined },
|
||||
optional: {type: Boolean, default: false},
|
||||
},
|
||||
computed: {
|
||||
field_label: function () {
|
||||
if (this.optional) {
|
||||
return this.label
|
||||
} else {
|
||||
return this.label + '*'
|
||||
}
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
|
||||
48
vue/src/components/Modals/TextAreaInput.vue
Normal file
48
vue/src/components/Modals/TextAreaInput.vue
Normal file
@@ -0,0 +1,48 @@
|
||||
<template>
|
||||
<div>
|
||||
<b-form-group v-bind:label="field_label" class="mb-3">
|
||||
<b-form-textarea v-model="new_value" type="text" :placeholder="placeholder" :disabled="disabled"></b-form-textarea>
|
||||
<em v-if="help" class="small text-muted">{{ help }}</em>
|
||||
<small v-if="subtitle" class="text-muted">{{ subtitle }}</small>
|
||||
</b-form-group>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: "TextAreaInput",
|
||||
props: {
|
||||
field: {type: String, default: "You Forgot To Set Field Name"},
|
||||
label: {type: String, default: "Text Field"},
|
||||
value: {type: String, default: ""},
|
||||
placeholder: {type: String, default: "You Should Add Placeholder Text"},
|
||||
help: {type: String, default: undefined},
|
||||
subtitle: {type: String, default: undefined},
|
||||
disabled: {type: Boolean, default: false},
|
||||
optional: {type: Boolean, default: false},
|
||||
},
|
||||
computed: {
|
||||
field_label: function () {
|
||||
if (this.optional) {
|
||||
return this.label
|
||||
} else {
|
||||
return this.label + '*'
|
||||
}
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
new_value: undefined,
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
this.new_value = this.value
|
||||
},
|
||||
watch: {
|
||||
new_value: function () {
|
||||
this.$root.$emit("change", this.field, this.new_value)
|
||||
},
|
||||
},
|
||||
methods: {},
|
||||
}
|
||||
</script>
|
||||
@@ -1,6 +1,6 @@
|
||||
<template>
|
||||
<div>
|
||||
<b-form-group v-bind:label="label" class="mb-3">
|
||||
<b-form-group v-bind:label="field_label" class="mb-3">
|
||||
<b-form-input v-model="new_value" type="text" :placeholder="placeholder" :disabled="disabled"></b-form-input>
|
||||
<em v-if="help" class="small text-muted">{{ help }}</em>
|
||||
<small v-if="subtitle" class="text-muted">{{ subtitle }}</small>
|
||||
@@ -18,13 +18,23 @@ export default {
|
||||
placeholder: { type: String, default: "You Should Add Placeholder Text" },
|
||||
help: { type: String, default: undefined },
|
||||
subtitle: { type: String, default: undefined },
|
||||
disabled: { type: Boolean, default: false }
|
||||
disabled: { type: Boolean, default: false },
|
||||
optional: {type: Boolean, default: false},
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
new_value: undefined,
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
field_label: function () {
|
||||
if (this.optional) {
|
||||
return this.label
|
||||
} else {
|
||||
return this.label + '*'
|
||||
}
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
this.new_value = this.value
|
||||
},
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
<div>
|
||||
|
||||
|
||||
<div class="card p-4 pb-2" v-if="recipe !== undefined">
|
||||
<div class="card p-4 pb-2" v-if="recipe !== undefined && property_list.length > 0">
|
||||
<b-row>
|
||||
<b-col>
|
||||
<h5><i class="fas fa-database"></i> {{ $t('Properties') }}</h5>
|
||||
|
||||
@@ -21,7 +21,7 @@
|
||||
<template v-else>
|
||||
<b-card no-body v-hover v-if="recipe" style="height: 100%">
|
||||
|
||||
<a :href="this.recipe.id !== undefined ? resolveDjangoUrl('view_recipe', this.recipe.id) : null">
|
||||
<a :href="recipe_link">
|
||||
<div class="content">
|
||||
<div class="content-overlay" v-if="recipe.description !== null && recipe.description !== ''"></div>
|
||||
<b-card-img-lazy style="height: 15vh; object-fit: cover" class="" :src="recipe_image"
|
||||
@@ -50,7 +50,7 @@
|
||||
<b-card-body class="p-2 pl-3 pr-3">
|
||||
<div class="d-flex flex-row">
|
||||
<div class="flex-grow-1">
|
||||
<a :href="this.recipe.id !== undefined ? resolveDjangoUrl('view_recipe', this.recipe.id) : null" class="text-body font-weight-bold two-row-text">
|
||||
<a :href="recipe_link" class="text-body font-weight-bold two-row-text">
|
||||
<template v-if="recipe !== null">{{ recipe.name }}</template>
|
||||
<template v-else>{{ meal_plan.title }}</template>
|
||||
</a>
|
||||
@@ -71,7 +71,7 @@
|
||||
|
||||
<p class="mt-1 mb-1">
|
||||
<last-cooked :recipe="recipe"></last-cooked>
|
||||
<keywords-component :recipe="recipe" :limit="3"
|
||||
<keywords-component :recipe="recipe" :limit="3" :enable_keyword_links="enable_keyword_links"
|
||||
style="margin-top: 4px; position: relative; z-index: 3;"></keywords-component>
|
||||
</p>
|
||||
<transition name="fade" mode="in-out">
|
||||
@@ -152,6 +152,8 @@ export default {
|
||||
detailed: {type: Boolean, default: true},
|
||||
show_context_menu: {type: Boolean, default: true},
|
||||
context_disabled_options: Object,
|
||||
open_recipe_on_click: {type: Boolean, default: true},
|
||||
enable_keyword_links: {type: Boolean, default: true},
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
@@ -178,6 +180,13 @@ export default {
|
||||
waiting_time: function () {
|
||||
return calculateHourMinuteSplit(this.recipe.waiting_time)
|
||||
},
|
||||
recipe_link: function (){
|
||||
if(this.open_recipe_on_click){
|
||||
return this.recipe.id !== undefined ? resolveDjangoUrl('view_recipe', this.recipe.id) : null
|
||||
} else {
|
||||
return "#"
|
||||
}
|
||||
}
|
||||
},
|
||||
methods: {},
|
||||
directives: {
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
</template>
|
||||
|
||||
<div v-if="!loading" style="padding-bottom: 60px">
|
||||
<RecipeSwitcher ref="ref_recipe_switcher" @switch="quickSwitch($event)"/>
|
||||
<RecipeSwitcher ref="ref_recipe_switcher" @switch="quickSwitch($event)" v-if="show_recipe_switcher"/>
|
||||
<div class="row">
|
||||
<div class="col-12" style="text-align: center">
|
||||
<h3>{{ recipe.name }}</h3>
|
||||
@@ -27,7 +27,7 @@
|
||||
</div>
|
||||
|
||||
<div style="text-align: center">
|
||||
<keywords-component :recipe="recipe"></keywords-component>
|
||||
<keywords-component :recipe="recipe" :enable_keyword_links="enable_keyword_links"></keywords-component>
|
||||
</div>
|
||||
|
||||
<hr/>
|
||||
@@ -77,7 +77,7 @@
|
||||
|
||||
<div class="col col-md-2 col-2 mt-2 mt-md-0 text-right">
|
||||
<recipe-context-menu v-bind:recipe="recipe" :servings="servings"
|
||||
:disabled_options="{print:false}"></recipe-context-menu>
|
||||
:disabled_options="{print:false}" v-if="show_context_menu"></recipe-context-menu>
|
||||
</div>
|
||||
</div>
|
||||
<hr/>
|
||||
@@ -234,13 +234,20 @@ export default {
|
||||
ingredient_height: '250',
|
||||
}
|
||||
},
|
||||
props: {
|
||||
recipe_id: Number,
|
||||
show_context_menu: {type: Boolean, default: true},
|
||||
enable_keyword_links: {type: Boolean, default: true},
|
||||
show_recipe_switcher: {type: Boolean, default: true},
|
||||
//show_comments: {type: Boolean, default: true},
|
||||
},
|
||||
watch: {
|
||||
servings(newVal, oldVal) {
|
||||
this.servings_cache[this.recipe.id] = this.servings
|
||||
},
|
||||
},
|
||||
mounted() {
|
||||
this.loadRecipe(window.RECIPE_ID)
|
||||
this.loadRecipe(this.recipe_id)
|
||||
this.$i18n.locale = window.CUSTOM_LOCALE
|
||||
this.requestWakeLock()
|
||||
window.addEventListener('resize', this.handleResize);
|
||||
|
||||
@@ -50,6 +50,7 @@
|
||||
<b-form-select-option value="DARKLY">Darkly</b-form-select-option>
|
||||
<b-form-select-option value="FLATLY">Flatly</b-form-select-option>
|
||||
<b-form-select-option value="SUPERHERO">Superhero</b-form-select-option>
|
||||
<b-form-select-option value="TANDOOR_DARK">Tandoor Dark (INCOMPLETE)</b-form-select-option>
|
||||
</b-form-select>
|
||||
|
||||
</b-form-group>
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
{
|
||||
"warning_feature_beta": "",
|
||||
"warning_feature_beta": "Tato funkce je momentálně ve fázi Beta (testování). Očekávejte, prosím, chyby. V budoucnosti může dojít i ke ztrátě dat.",
|
||||
"err_fetching_resource": "",
|
||||
"err_creating_resource": "",
|
||||
"err_updating_resource": "",
|
||||
@@ -13,395 +13,395 @@
|
||||
"success_deleting_resource": "",
|
||||
"success_moving_resource": "",
|
||||
"success_merging_resource": "",
|
||||
"file_upload_disabled": "",
|
||||
"warning_space_delete": "",
|
||||
"file_upload_disabled": "Nahrávání souborů není povoleno pro Váš prostor.",
|
||||
"warning_space_delete": "Můžete smazat váš prostor včetně všech receptů, nákupních seznamů, jídelníčků a všeho ostatního, co jste vytvořili. Tuto akci nemůžete vzít zpět! Jste si jisti, že chcete pokračovat?",
|
||||
"food_inherit_info": "",
|
||||
"facet_count_info": "",
|
||||
"step_time_minutes": "",
|
||||
"confirm_delete": "",
|
||||
"import_running": "",
|
||||
"all_fields_optional": "",
|
||||
"convert_internal": "",
|
||||
"show_only_internal": "",
|
||||
"show_split_screen": "",
|
||||
"facet_count_info": "Zobraz počet receptů u filtrů vyhledávání.",
|
||||
"step_time_minutes": "Nastavte čas v minutách",
|
||||
"confirm_delete": "Jste si jisti že chcete odstranit tento {objekt}?",
|
||||
"import_running": "Probíhá import, čekejte prosím!",
|
||||
"all_fields_optional": "Všechna pole jsou nepviná a mohou být ponechána prázdná.",
|
||||
"convert_internal": "Převést na interní recept",
|
||||
"show_only_internal": "Zobrazit pouze interní recepty",
|
||||
"show_split_screen": "Rozdělené zobrazení",
|
||||
"Log_Recipe_Cooking": "",
|
||||
"External_Recipe_Image": "",
|
||||
"Add_to_Shopping": "",
|
||||
"Add_to_Plan": "",
|
||||
"Step_start_time": "",
|
||||
"Sort_by_new": "",
|
||||
"Table_of_Contents": "",
|
||||
"External_Recipe_Image": "Externí obrázek receptu",
|
||||
"Add_to_Shopping": "Přidat k nákupu",
|
||||
"Add_to_Plan": "Přidat do jídelníčku",
|
||||
"Step_start_time": "Nastav počáteční čas",
|
||||
"Sort_by_new": "Seřadit od nejnovějšího",
|
||||
"Table_of_Contents": "Obsah",
|
||||
"Recipes_per_page": "Receptů na stránku",
|
||||
"Show_as_header": "",
|
||||
"Hide_as_header": "",
|
||||
"Show_as_header": "Nastav jako nadpis",
|
||||
"Hide_as_header": "Skryj jako nadpis",
|
||||
"Add_nutrition_recipe": "Přidat nutriční hodnoty",
|
||||
"Remove_nutrition_recipe": "Smazat nutriční hodnoty",
|
||||
"Copy_template_reference": "",
|
||||
"Save_and_View": "Uložit & Zobrazit",
|
||||
"Manage_Books": "",
|
||||
"Manage_Books": "Spravovat kuchařky",
|
||||
"Meal_Plan": "Jídelníček",
|
||||
"Select_Book": "",
|
||||
"Select_Book": "Vyber kuchařku",
|
||||
"Select_File": "Vybrat soubor",
|
||||
"Recipe_Image": "",
|
||||
"Recipe_Image": "Obrázek k receptu",
|
||||
"Import_finished": "Import dokončen",
|
||||
"View_Recipes": "Zobrazit recepty",
|
||||
"Log_Cooking": "",
|
||||
"New_Recipe": "Nový recept",
|
||||
"Url_Import": "",
|
||||
"Reset_Search": "",
|
||||
"Recently_Viewed": "",
|
||||
"Load_More": "",
|
||||
"New_Keyword": "",
|
||||
"Delete_Keyword": "",
|
||||
"Edit_Keyword": "",
|
||||
"Url_Import": "Import pomocí URL odkazu",
|
||||
"Reset_Search": "Zrušit filtry vyhledávání",
|
||||
"Recently_Viewed": "Naposledy prohlížené",
|
||||
"Load_More": "Načíst další",
|
||||
"New_Keyword": "Nové klíčové slovo",
|
||||
"Delete_Keyword": "Smazat klíčové slovo",
|
||||
"Edit_Keyword": "Upravit klíčové slovo",
|
||||
"Edit_Recipe": "Upravit recept",
|
||||
"Move_Keyword": "",
|
||||
"Merge_Keyword": "",
|
||||
"Hide_Keywords": "",
|
||||
"Hide_Recipes": "",
|
||||
"Move_Keyword": "Přesunout klíčové slovo",
|
||||
"Merge_Keyword": "Sloučit klíčové slovo",
|
||||
"Hide_Keywords": "Skrýt klíčové slovo",
|
||||
"Hide_Recipes": "Skrýt recept",
|
||||
"Move_Up": "Nahoru",
|
||||
"Move_Down": "Dolů",
|
||||
"Step_Name": "Název kroku",
|
||||
"Step_Type": "",
|
||||
"Make_Header": "",
|
||||
"Make_Ingredient": "",
|
||||
"Step_Type": "Druh kroku",
|
||||
"Make_Header": "Použij jako nadpis",
|
||||
"Make_Ingredient": "Použij jako ingredienci",
|
||||
"Amount": "Množství",
|
||||
"Enable_Amount": "Zobrazit množství",
|
||||
"Disable_Amount": "Skrýt množství",
|
||||
"Ingredient Editor": "Editace ingrediencí",
|
||||
"Description_Replace": "",
|
||||
"Instruction_Replace": "",
|
||||
"Auto_Sort": "",
|
||||
"Auto_Sort_Help": "",
|
||||
"Private_Recipe": "",
|
||||
"Private_Recipe_Help": "",
|
||||
"reusable_help_text": "",
|
||||
"Add_Step": "",
|
||||
"Keywords": "",
|
||||
"Books": "",
|
||||
"Description_Replace": "Nahraď popis",
|
||||
"Instruction_Replace": "Nahraď instrukce",
|
||||
"Auto_Sort": "Automatické řazení",
|
||||
"Auto_Sort_Help": "Přiřadit všechny ingredience k nejlépe vyhovujícímu kroku.",
|
||||
"Private_Recipe": "Soukromý recept",
|
||||
"Private_Recipe_Help": "Recept můžete zobrazit pouze vy a lidé, se kterými jej sdílíte.",
|
||||
"reusable_help_text": "Má-li pozvánka platit pro více než jednoho uživatele.",
|
||||
"Add_Step": "Přidat krok",
|
||||
"Keywords": "Klíčová slova",
|
||||
"Books": "Kuchařky",
|
||||
"Proteins": "Proteiny",
|
||||
"Fats": "Tuky",
|
||||
"Carbohydrates": "",
|
||||
"Calories": "",
|
||||
"Energy": "",
|
||||
"Carbohydrates": "Sacharidy",
|
||||
"Calories": "Kalorie",
|
||||
"Energy": "Energie",
|
||||
"Nutrition": "",
|
||||
"Date": "",
|
||||
"Share": "",
|
||||
"Automation": "",
|
||||
"Parameter": "",
|
||||
"Export": "",
|
||||
"Copy": "",
|
||||
"Rating": "",
|
||||
"Close": "",
|
||||
"Cancel": "",
|
||||
"Link": "",
|
||||
"Add": "",
|
||||
"New": "",
|
||||
"Note": "",
|
||||
"Success": "",
|
||||
"Failure": "",
|
||||
"Protected": "",
|
||||
"Ingredients": "",
|
||||
"Date": "Datum",
|
||||
"Share": "Sdílet",
|
||||
"Automation": "Automatizace",
|
||||
"Parameter": "Parametr",
|
||||
"Export": "Export",
|
||||
"Copy": "Kopírovat",
|
||||
"Rating": "Hodnocení",
|
||||
"Close": "Zavřít",
|
||||
"Cancel": "Zrušit",
|
||||
"Link": "Odkaz",
|
||||
"Add": "Přidat",
|
||||
"New": "Nový",
|
||||
"Note": "Poznámka",
|
||||
"Success": "Úspěch",
|
||||
"Failure": "Selhání",
|
||||
"Protected": "Chráněný",
|
||||
"Ingredients": "Ingredience",
|
||||
"Supermarket": "",
|
||||
"Categories": "",
|
||||
"Category": "",
|
||||
"Selected": "",
|
||||
"min": "",
|
||||
"Servings": "",
|
||||
"Categories": "Kategorie",
|
||||
"Category": "Kategorie",
|
||||
"Selected": "Vybrané",
|
||||
"min": "min",
|
||||
"Servings": "Porce",
|
||||
"Waiting": "",
|
||||
"Preparation": "",
|
||||
"External": "",
|
||||
"Size": "",
|
||||
"Files": "",
|
||||
"File": "",
|
||||
"Edit": "",
|
||||
"Image": "",
|
||||
"Delete": "",
|
||||
"Open": "",
|
||||
"Ok": "",
|
||||
"Save": "",
|
||||
"Step": "",
|
||||
"Search": "",
|
||||
"Import": "",
|
||||
"Print": "",
|
||||
"Settings": "",
|
||||
"or": "",
|
||||
"and": "",
|
||||
"Information": "",
|
||||
"Download": "",
|
||||
"Create": "",
|
||||
"Search Settings": "",
|
||||
"View": "",
|
||||
"Recipes": "",
|
||||
"Move": "",
|
||||
"Merge": "",
|
||||
"Parent": "",
|
||||
"Copy Link": "",
|
||||
"Copy Token": "",
|
||||
"delete_confirmation": "",
|
||||
"move_confirmation": "",
|
||||
"merge_confirmation": "",
|
||||
"create_rule": "",
|
||||
"move_selection": "",
|
||||
"merge_selection": "",
|
||||
"Preparation": "Příprava",
|
||||
"External": "Externí",
|
||||
"Size": "Velikost",
|
||||
"Files": "Soubory",
|
||||
"File": "Soubor",
|
||||
"Edit": "Upravit",
|
||||
"Image": "Obrázek",
|
||||
"Delete": "Smazat",
|
||||
"Open": "Otevřít",
|
||||
"Ok": "Ok",
|
||||
"Save": "Uložit",
|
||||
"Step": "Krok",
|
||||
"Search": "Hledat",
|
||||
"Import": "Import",
|
||||
"Print": "Tisk",
|
||||
"Settings": "Nastavení",
|
||||
"or": "nebo",
|
||||
"and": "a",
|
||||
"Information": "Informace",
|
||||
"Download": "Stáhnout",
|
||||
"Create": "Vytvořit",
|
||||
"Search Settings": "Nastavení vyhledávání",
|
||||
"View": "Zobrazit",
|
||||
"Recipes": "Recepty",
|
||||
"Move": "Přesunout",
|
||||
"Merge": "Spojit",
|
||||
"Parent": "Nadřazená",
|
||||
"Copy Link": "Kopírovat odkaz",
|
||||
"Copy Token": "Kopírovat token",
|
||||
"delete_confirmation": "Jste si jistí, že chcete smazat {source}?",
|
||||
"move_confirmation": "Přesunout <i>{child}</i> do nadřazené <i>{parent}</i>",
|
||||
"merge_confirmation": "Nahradit <i>{source}</i> za <i>{target}</i>",
|
||||
"create_rule": "a vytvořit automatizaci",
|
||||
"move_selection": "Vybrat nadřazený {type} kam {source} přesunout.",
|
||||
"merge_selection": "Nahradit všechny výskyty {source} za vybraný {type}.",
|
||||
"Root": "",
|
||||
"Ignore_Shopping": "",
|
||||
"Shopping_Category": "",
|
||||
"Shopping_Categories": "",
|
||||
"Edit_Food": "",
|
||||
"Move_Food": "",
|
||||
"New_Food": "",
|
||||
"Hide_Food": "",
|
||||
"Food_Alias": "",
|
||||
"Unit_Alias": "",
|
||||
"Keyword_Alias": "",
|
||||
"Delete_Food": "",
|
||||
"No_ID": "",
|
||||
"Meal_Plan_Days": "",
|
||||
"merge_title": "",
|
||||
"move_title": "",
|
||||
"Food": "",
|
||||
"Original_Text": "",
|
||||
"Recipe_Book": "",
|
||||
"Ignore_Shopping": "Ignorovat nákupní seznam",
|
||||
"Shopping_Category": "Kategorie nákupního seznamu",
|
||||
"Shopping_Categories": "Kategorie nákupního seznamu",
|
||||
"Edit_Food": "Upravit jídlo",
|
||||
"Move_Food": "Přesunout jídlo",
|
||||
"New_Food": "Nové jídlo",
|
||||
"Hide_Food": "Skrýt jídlo",
|
||||
"Food_Alias": "Přezdívka jídla",
|
||||
"Unit_Alias": "Přezdívka jednotky",
|
||||
"Keyword_Alias": "Přezdívka klíčového slova",
|
||||
"Delete_Food": "Smazat jídlo",
|
||||
"No_ID": "ID nenalezeno, odstranění není možné.",
|
||||
"Meal_Plan_Days": "Budoucí jídelníčky",
|
||||
"merge_title": "Sloučit {type}",
|
||||
"move_title": "Přesunout {type}",
|
||||
"Food": "Jídlo",
|
||||
"Original_Text": "Původní text",
|
||||
"Recipe_Book": "Kuchařka",
|
||||
"del_confirmation_tree": "",
|
||||
"delete_title": "",
|
||||
"create_title": "",
|
||||
"edit_title": "",
|
||||
"Name": "",
|
||||
"Type": "",
|
||||
"Description": "",
|
||||
"Recipe": "",
|
||||
"delete_title": "Smazat {type}",
|
||||
"create_title": "Nový {type}",
|
||||
"edit_title": "Upravit {type}",
|
||||
"Name": "Jméno",
|
||||
"Type": "Typ",
|
||||
"Description": "Popis",
|
||||
"Recipe": "Recept",
|
||||
"tree_root": "",
|
||||
"Icon": "",
|
||||
"Unit": "",
|
||||
"Decimals": "",
|
||||
"Default_Unit": "",
|
||||
"No_Results": "",
|
||||
"New_Unit": "",
|
||||
"Create_New_Shopping Category": "",
|
||||
"Create_New_Food": "",
|
||||
"Create_New_Keyword": "",
|
||||
"Create_New_Unit": "",
|
||||
"Create_New_Meal_Type": "",
|
||||
"Create_New_Shopping_Category": "",
|
||||
"and_up": "",
|
||||
"and_down": "",
|
||||
"Instructions": "",
|
||||
"Unrated": "",
|
||||
"Automate": "",
|
||||
"Empty": "",
|
||||
"Key_Ctrl": "",
|
||||
"Key_Shift": "",
|
||||
"Time": "",
|
||||
"Text": "",
|
||||
"Shopping_list": "",
|
||||
"Added_by": "",
|
||||
"Icon": "Ikona",
|
||||
"Unit": "Jednotka",
|
||||
"Decimals": "Desetinná místa",
|
||||
"Default_Unit": "Výchozí jednotka",
|
||||
"No_Results": "Žádné výsledky",
|
||||
"New_Unit": "Nová jednotka",
|
||||
"Create_New_Shopping Category": "Vytvořit novou nákupní kategorii",
|
||||
"Create_New_Food": "Přidat nové jídlo",
|
||||
"Create_New_Keyword": "Přidat nové klíčové slovo",
|
||||
"Create_New_Unit": "Přidat novou jednotku",
|
||||
"Create_New_Meal_Type": "Přidat nový druh jídla",
|
||||
"Create_New_Shopping_Category": "Přidat novou nákupní kategorii",
|
||||
"and_up": "a nahoru",
|
||||
"and_down": "a dolů",
|
||||
"Instructions": "Instrukce",
|
||||
"Unrated": "Nehodnocené",
|
||||
"Automate": "Automatizovat",
|
||||
"Empty": "Prázdné",
|
||||
"Key_Ctrl": "Ctrl",
|
||||
"Key_Shift": "Shift",
|
||||
"Time": "Čas",
|
||||
"Text": "Text",
|
||||
"Shopping_list": "Nákupní seznam",
|
||||
"Added_by": "Přidáno uživatelem",
|
||||
"Added_on": "",
|
||||
"AddToShopping": "",
|
||||
"IngredientInShopping": "",
|
||||
"NotInShopping": "",
|
||||
"OnHand": "",
|
||||
"FoodOnHand": "",
|
||||
"FoodNotOnHand": "",
|
||||
"Undefined": "",
|
||||
"Create_Meal_Plan_Entry": "",
|
||||
"Edit_Meal_Plan_Entry": "",
|
||||
"Title": "",
|
||||
"Week": "",
|
||||
"Month": "",
|
||||
"Year": "",
|
||||
"Planner": "",
|
||||
"Planner_Settings": "",
|
||||
"Period": "",
|
||||
"Plan_Period_To_Show": "",
|
||||
"Periods": "",
|
||||
"Plan_Show_How_Many_Periods": "",
|
||||
"Starting_Day": "",
|
||||
"Meal_Types": "",
|
||||
"Meal_Type": "",
|
||||
"New_Entry": "",
|
||||
"Clone": "",
|
||||
"Drag_Here_To_Delete": "",
|
||||
"Meal_Type_Required": "",
|
||||
"Title_or_Recipe_Required": "",
|
||||
"Color": "",
|
||||
"New_Meal_Type": "",
|
||||
"Use_Fractions": "",
|
||||
"Use_Fractions_Help": "",
|
||||
"AddFoodToShopping": "",
|
||||
"RemoveFoodFromShopping": "",
|
||||
"DeleteShoppingConfirm": "",
|
||||
"AddToShopping": "Přidat do nákupního seznamu",
|
||||
"IngredientInShopping": "Tato ingredience je na vašem nákupním seznamu.",
|
||||
"NotInShopping": "{food} není na vašem nákupním seznamu.",
|
||||
"OnHand": "Momentálně k dispozici",
|
||||
"FoodOnHand": "{food} máte k dispozici.",
|
||||
"FoodNotOnHand": "Nemáte {food} k dispozici.",
|
||||
"Undefined": "Neurčeno",
|
||||
"Create_Meal_Plan_Entry": "Vytvořit položku v jídelníčku",
|
||||
"Edit_Meal_Plan_Entry": "Upravit položku v jídelníčku",
|
||||
"Title": "Název",
|
||||
"Week": "Týden",
|
||||
"Month": "Měsíc",
|
||||
"Year": "Rok",
|
||||
"Planner": "Plánovač",
|
||||
"Planner_Settings": "Nastavení plánovače",
|
||||
"Period": "Období",
|
||||
"Plan_Period_To_Show": "Zobrazit týdny, měsíce nebo roky",
|
||||
"Periods": "Období",
|
||||
"Plan_Show_How_Many_Periods": "Kolik období bude zobrazeno",
|
||||
"Starting_Day": "První den v týdnu",
|
||||
"Meal_Types": "Druhy jídel",
|
||||
"Meal_Type": "Druh jídla",
|
||||
"New_Entry": "Nová položka",
|
||||
"Clone": "Klonovat",
|
||||
"Drag_Here_To_Delete": "Přesunutím sem smazat",
|
||||
"Meal_Type_Required": "Druh jídla je povinný",
|
||||
"Title_or_Recipe_Required": "Je požadován název nebo výběr receptu",
|
||||
"Color": "Barva",
|
||||
"New_Meal_Type": "Nový druh jídla",
|
||||
"Use_Fractions": "Použít zlomky",
|
||||
"Use_Fractions_Help": "Automaticky převézt desetinná čísla na zlomky při prohlížení repetu.",
|
||||
"AddFoodToShopping": "Přidat {food} na váš nákupní seznam",
|
||||
"RemoveFoodFromShopping": "Odstranit {food} z nákupního seznamu",
|
||||
"DeleteShoppingConfirm": "Jste si jistí, že chcete odstranit všechno {food} z nákupního seznamu?",
|
||||
"IgnoredFood": "",
|
||||
"Add_Servings_to_Shopping": "",
|
||||
"Week_Numbers": "",
|
||||
"Show_Week_Numbers": "",
|
||||
"Export_As_ICal": "",
|
||||
"Export_To_ICal": "",
|
||||
"Cannot_Add_Notes_To_Shopping": "",
|
||||
"Added_To_Shopping_List": "",
|
||||
"Shopping_List_Empty": "",
|
||||
"Next_Period": "",
|
||||
"Previous_Period": "",
|
||||
"Current_Period": "",
|
||||
"Next_Day": "",
|
||||
"Previous_Day": "",
|
||||
"Add_Servings_to_Shopping": "Přidat {servings} porce na nákupní seznam",
|
||||
"Week_Numbers": "Číslo týdne",
|
||||
"Show_Week_Numbers": "Zobrazit čísla týdnů?",
|
||||
"Export_As_ICal": "Exportovat současný úsek do formátu iCal",
|
||||
"Export_To_ICal": "Export ovat .ics",
|
||||
"Cannot_Add_Notes_To_Shopping": "Poznámky nemohou být přidány na nákupní seznam",
|
||||
"Added_To_Shopping_List": "Přidáno na nákupní seznam",
|
||||
"Shopping_List_Empty": "Váš nákupní seznam je momentálně prázdný, můžete přidat položky pomocí kontextového menu záznamu v jídelníčku (pravým kliknutím na kartu nebo levým kliknutím na ikonu menu)",
|
||||
"Next_Period": "Další období",
|
||||
"Previous_Period": "Předchozí období",
|
||||
"Current_Period": "Současné období",
|
||||
"Next_Day": "Následující den",
|
||||
"Previous_Day": "Předchozí den",
|
||||
"Inherit": "",
|
||||
"InheritFields": "",
|
||||
"FoodInherit": "",
|
||||
"ShowUncategorizedFood": "",
|
||||
"GroupBy": "",
|
||||
"Language": "",
|
||||
"Theme": "",
|
||||
"ShowUncategorizedFood": "Zobrazit nedefinované",
|
||||
"GroupBy": "Seskupit podle",
|
||||
"Language": "Jazyk",
|
||||
"Theme": "Téma",
|
||||
"SupermarketCategoriesOnly": "",
|
||||
"MoveCategory": "",
|
||||
"CountMore": "",
|
||||
"IgnoreThis": "",
|
||||
"DelayFor": "",
|
||||
"Warning": "",
|
||||
"NoCategory": "",
|
||||
"MoveCategory": "Předunout do: ",
|
||||
"CountMore": "...+{count} víc",
|
||||
"IgnoreThis": "Nikdy automaticky nepřídávat {food} na nákupní seznam",
|
||||
"DelayFor": "Odložit na {hours} hodin",
|
||||
"Warning": "Varování",
|
||||
"NoCategory": "Není vybrána žádná kategorie.",
|
||||
"InheritWarning": "",
|
||||
"ShowDelayed": "",
|
||||
"Completed": "",
|
||||
"OfflineAlert": "",
|
||||
"shopping_share": "",
|
||||
"shopping_auto_sync": "",
|
||||
"one_url_per_line": "",
|
||||
"mealplan_autoadd_shopping": "",
|
||||
"mealplan_autoexclude_onhand": "",
|
||||
"mealplan_autoinclude_related": "",
|
||||
"default_delay": "",
|
||||
"plan_share_desc": "",
|
||||
"shopping_share_desc": "",
|
||||
"shopping_auto_sync_desc": "",
|
||||
"mealplan_autoadd_shopping_desc": "",
|
||||
"mealplan_autoexclude_onhand_desc": "",
|
||||
"mealplan_autoinclude_related_desc": "",
|
||||
"default_delay_desc": "",
|
||||
"ShowDelayed": "Zobrazit odložené položky",
|
||||
"Completed": "Dokončeno",
|
||||
"OfflineAlert": "Jste offline, nákupní seznam nemusí být synchronizován.",
|
||||
"shopping_share": "Sdílet nákupní seznam",
|
||||
"shopping_auto_sync": "Automatická synchronizace",
|
||||
"one_url_per_line": "Jeden URL odkaz na řádek",
|
||||
"mealplan_autoadd_shopping": "Automaticky přidat jídelníček",
|
||||
"mealplan_autoexclude_onhand": "Nezahrnovat jídlo k dispozici",
|
||||
"mealplan_autoinclude_related": "Přidat podobné recepty",
|
||||
"default_delay": "Výchozí doba odložení v hodinách",
|
||||
"plan_share_desc": "Nové položky v jídelníčku budou automaticky sdíleny s vybranými uživateli.",
|
||||
"shopping_share_desc": "Uživatelé uvidí všechny položky na vašem nákupním seznamu. Abyste viděli položky na jejich seznamu, musí si přidat vás.",
|
||||
"shopping_auto_sync_desc": "Nastavením 0 dojde k vypnutí automatické synchronizace. Při prohlížení nákupního seznamu je vždy po uplynutí nastaveného počtu vteřin aktualizován o změny, které mohli provést jiní uživatelé. To je užitečné, pokud nakupujete ve více lidech, ale může používat více dat.",
|
||||
"mealplan_autoadd_shopping_desc": "Automaticky podle jídelníčku přidat ingredience na nákupní seznam.",
|
||||
"mealplan_autoexclude_onhand_desc": "Nepřidávat ingredience, které jsou k dispozici, na nákupní seznam, když je vytvořen podle jídelníčku (manuálně nebo automaticky).",
|
||||
"mealplan_autoinclude_related_desc": "Když je nákupní seznam vytvořen podle jídelníčku, přidat i položky z přidružených receptů.",
|
||||
"default_delay_desc": "Výchozí odložení položek v nákupním seznamu v hodinách.",
|
||||
"filter_to_supermarket": "",
|
||||
"Coming_Soon": "",
|
||||
"Auto_Planner": "",
|
||||
"New_Cookbook": "",
|
||||
"Hide_Keyword": "",
|
||||
"Hour": "",
|
||||
"Hours": "",
|
||||
"Day": "",
|
||||
"Days": "",
|
||||
"Second": "",
|
||||
"Seconds": "",
|
||||
"Clear": "",
|
||||
"Users": "",
|
||||
"Invites": "",
|
||||
"err_move_self": "",
|
||||
"nothing": "",
|
||||
"err_merge_self": "",
|
||||
"show_sql": "",
|
||||
"filter_to_supermarket_desc": "",
|
||||
"CategoryName": "",
|
||||
"SupermarketName": "",
|
||||
"CategoryInstruction": "",
|
||||
"Coming_Soon": "Již brzy",
|
||||
"Auto_Planner": "Automatický plánovač",
|
||||
"New_Cookbook": "Nová kuchařka",
|
||||
"Hide_Keyword": "Skrýt klíčová slova",
|
||||
"Hour": "Hodina",
|
||||
"Hours": "Hodiny",
|
||||
"Day": "Den",
|
||||
"Days": "Dny",
|
||||
"Second": "Vteřina",
|
||||
"Seconds": "Vteřiny",
|
||||
"Clear": "Vyčistit",
|
||||
"Users": "Uživatelé",
|
||||
"Invites": "Pozvánky",
|
||||
"err_move_self": "Není možné přesunout jednu položku do sebe samé",
|
||||
"nothing": "Není co dělat",
|
||||
"err_merge_self": "Není možné sloučit položku samu se sebou",
|
||||
"show_sql": "Zobrazit SQL",
|
||||
"filter_to_supermarket_desc": "Standartně zobrazovat položky v nákupním seznamu pouze pro vybraný obchod.",
|
||||
"CategoryName": "Název kategorie",
|
||||
"SupermarketName": "Název obchodu",
|
||||
"CategoryInstruction": "Přetáhnutím kategorií změníte pořadí, ve kterém se zobrazují v nákupním seznamu.",
|
||||
"shopping_recent_days_desc": "",
|
||||
"shopping_recent_days": "",
|
||||
"download_pdf": "",
|
||||
"download_csv": "",
|
||||
"csv_delim_help": "",
|
||||
"csv_delim_label": "",
|
||||
"SuccessClipboard": "",
|
||||
"copy_to_clipboard": "",
|
||||
"shopping_recent_days": "Nedávné dny",
|
||||
"download_pdf": "Stáhnout PDF",
|
||||
"download_csv": "Stáhnout CSV",
|
||||
"csv_delim_help": "Který znak bude použit pro oddělení záznamů v CSV.",
|
||||
"csv_delim_label": "Oddělovač záznamů v CSV",
|
||||
"SuccessClipboard": "Nákupní seznam byl zkopírován do schránky",
|
||||
"copy_to_clipboard": "Zkopírovat do schránky",
|
||||
"csv_prefix_help": "",
|
||||
"csv_prefix_label": "",
|
||||
"copy_markdown_table": "",
|
||||
"in_shopping": "",
|
||||
"DelayUntil": "",
|
||||
"Pin": "",
|
||||
"Unpin": "",
|
||||
"PinnedConfirmation": "",
|
||||
"UnpinnedConfirmation": "",
|
||||
"mark_complete": "",
|
||||
"QuickEntry": "",
|
||||
"shopping_add_onhand_desc": "",
|
||||
"shopping_add_onhand": "",
|
||||
"related_recipes": "",
|
||||
"today_recipes": "",
|
||||
"sql_debug": "",
|
||||
"remember_search": "",
|
||||
"remember_hours": "",
|
||||
"tree_select": "",
|
||||
"OnHand_help": "",
|
||||
"ignore_shopping_help": "",
|
||||
"shopping_category_help": "",
|
||||
"copy_markdown_table": "Kopírovat jako Markdown tabulku",
|
||||
"in_shopping": "V nákupním seznamu",
|
||||
"DelayUntil": "Odložit do",
|
||||
"Pin": "Připnout",
|
||||
"Unpin": "Odepnout",
|
||||
"PinnedConfirmation": "{recipe} byl připnut.",
|
||||
"UnpinnedConfirmation": "{recipe} byl odepnut.",
|
||||
"mark_complete": "Označit jako hotové",
|
||||
"QuickEntry": "Rychlý záznam",
|
||||
"shopping_add_onhand_desc": "Označit potravinu jako \"K dispozici\" když je odškrtnuta na nákupním seznamu.",
|
||||
"shopping_add_onhand": "Automaticky K dispozici",
|
||||
"related_recipes": "Související recepty",
|
||||
"today_recipes": "Dnešní recepty",
|
||||
"sql_debug": "SQL Debug",
|
||||
"remember_search": "Pamatovat vyhledávání",
|
||||
"remember_hours": "Kolik hodin pamatovat",
|
||||
"tree_select": "Použít stromový výběr",
|
||||
"OnHand_help": "Potravina je v inventáři a nebude automaticky přidána na nákupní seznam. Status \"k dipozici\" je sdílen s nakupujícími uživateli.",
|
||||
"ignore_shopping_help": "Nikdy nepřidávat potravinu na nákupní seznam (např. voda)",
|
||||
"shopping_category_help": "Obchody mohou být seřazeny a třízeny pomocí nákupních kategorií podle rozvržení uliček a regálů.",
|
||||
"food_recipe_help": "",
|
||||
"Foods": "",
|
||||
"Account": "",
|
||||
"Cosmetic": "",
|
||||
"Foods": "Potraviny",
|
||||
"Account": "Účet",
|
||||
"Cosmetic": "Zobrazení",
|
||||
"API": "API",
|
||||
"enable_expert": "",
|
||||
"expert_mode": "",
|
||||
"simple_mode": "",
|
||||
"advanced": "",
|
||||
"fields": "",
|
||||
"show_keywords": "",
|
||||
"show_foods": "",
|
||||
"show_books": "",
|
||||
"show_rating": "",
|
||||
"show_units": "",
|
||||
"show_filters": "",
|
||||
"not": "",
|
||||
"save_filter": "",
|
||||
"filter_name": "",
|
||||
"left_handed": "",
|
||||
"left_handed_help": "",
|
||||
"Custom Filter": "",
|
||||
"shared_with": "",
|
||||
"sort_by": "",
|
||||
"enable_expert": "Povolit Expertní režim",
|
||||
"expert_mode": "Expertní režim",
|
||||
"simple_mode": "Jednoduchý režim",
|
||||
"advanced": "Pokročilé",
|
||||
"fields": "Pole",
|
||||
"show_keywords": "Zobrazit klíčová slova",
|
||||
"show_foods": "Zobrazit potraviny",
|
||||
"show_books": "Zobrazit kuchařky",
|
||||
"show_rating": "Zobrazit hodnocení",
|
||||
"show_units": "Zobrazit jednotky",
|
||||
"show_filters": "Zobrazit filtry",
|
||||
"not": "ne",
|
||||
"save_filter": "Uložit filtr",
|
||||
"filter_name": "Název filtru",
|
||||
"left_handed": "Režim pro leváky",
|
||||
"left_handed_help": "Optimalizuje uživatelské prostředí pro levou ruku.",
|
||||
"Custom Filter": "Uživatelský filtr",
|
||||
"shared_with": "Sdíleno s",
|
||||
"sort_by": "Seřadit podle",
|
||||
"asc": "Vzestupně",
|
||||
"desc": "Sestupně",
|
||||
"date_viewed": "",
|
||||
"last_cooked": "",
|
||||
"times_cooked": "",
|
||||
"date_created": "",
|
||||
"show_sortby": "",
|
||||
"date_viewed": "Poslední prohlížené",
|
||||
"last_cooked": "Naposledy vařeno",
|
||||
"times_cooked": "Kolkrát vařeno",
|
||||
"date_created": "Datum vytvoření",
|
||||
"show_sortby": "Zobrazit Seřazeno podle",
|
||||
"search_rank": "",
|
||||
"make_now": "",
|
||||
"recipe_filter": "Filtrovat recepty",
|
||||
"book_filter_help": "",
|
||||
"review_shopping": "",
|
||||
"book_filter_help": "Zahrnout i recepty z filtru stejně jako manuálně přidané.",
|
||||
"review_shopping": "Zkontrolovat nákupní položky před uložením",
|
||||
"view_recipe": "Zobrazit recept",
|
||||
"copy_to_new": "",
|
||||
"copy_to_new": "Zkopírovat do nového receptu",
|
||||
"recipe_name": "Název receptu",
|
||||
"paste_ingredients_placeholder": "",
|
||||
"paste_ingredients": "",
|
||||
"ingredient_list": "",
|
||||
"explain": "",
|
||||
"paste_ingredients_placeholder": "Zde vložit seznam ingrediencí.",
|
||||
"paste_ingredients": "Vložit ingredince",
|
||||
"ingredient_list": "Seznam ingrediencí",
|
||||
"explain": "Vysvětlit",
|
||||
"filter": "Filtr",
|
||||
"Website": "Web",
|
||||
"App": "Aplikace",
|
||||
"Message": "",
|
||||
"Bookmarklet": "",
|
||||
"Sticky_Nav": "",
|
||||
"Sticky_Nav_Help": "",
|
||||
"Nav_Color": "",
|
||||
"Nav_Color_Help": "",
|
||||
"Message": "Zpráva",
|
||||
"Bookmarklet": "Bookmarklet",
|
||||
"Sticky_Nav": "Připnout navigační panel",
|
||||
"Sticky_Nav_Help": "Vždy zobrazit navigační panel na vrchu stránky.",
|
||||
"Nav_Color": "Barva navigačního panelu",
|
||||
"Nav_Color_Help": "Zmenit barvu navigačního panelu.",
|
||||
"Use_Kj": "Používat kJ místo kcal",
|
||||
"Comments_setting": "Zobrazit komentáře",
|
||||
"click_image_import": "Vyberte obrázek, který chcete přiřadit k tomuto receptu",
|
||||
"no_more_images_found": "Žádné další obrázky na zadaném odkazu.",
|
||||
"import_duplicates": "",
|
||||
"paste_json": "",
|
||||
"Click_To_Edit": "",
|
||||
"search_no_recipes": "",
|
||||
"search_import_help_text": "",
|
||||
"search_create_help_text": "",
|
||||
"warning_duplicate_filter": "",
|
||||
"import_duplicates": "Aby bylo zamezeno duplicitním receptům, recepty se stejným jménem jako již existující jsou ignorovány. Pokud chcete přidat všechno, zaškrtněte toto políčko.",
|
||||
"paste_json": "Sem zkopírujte zdroj ve formátu json nebo html.",
|
||||
"Click_To_Edit": "Kliknutím editovat",
|
||||
"search_no_recipes": "Nebyly nealezeny žádné recepty!",
|
||||
"search_import_help_text": "Importovat recept z externí webové stránky nebo aplikace.",
|
||||
"search_create_help_text": "Vytvořit nový recept přímo v Tandoor.",
|
||||
"warning_duplicate_filter": "Varování: Kvůli technickým omezení může použití několika filtrů se stejnou kombinací (a/nebo/ne) přinést neočekávaný výsledek.",
|
||||
"reset_children": "",
|
||||
"reset_children_help": "",
|
||||
"reset_food_inheritance": "",
|
||||
"reset_food_inheritance_info": "",
|
||||
"substitute_help": "",
|
||||
"substitute_siblings_help": "",
|
||||
"substitute_children_help": "",
|
||||
"substitute_help": "Při hledání podle ingrediencí, které jsou k dispozici, jsou zvažovány náhrady.",
|
||||
"substitute_siblings_help": "Všechny potraviny, které sdílejí nadřazenou položku jsou považovány za náhrady.",
|
||||
"substitute_children_help": "Všechny potraviny, které jsou podřízeny této, jsou považovány za náhražky.",
|
||||
"substitute_siblings": "",
|
||||
"substitute_children": "",
|
||||
"SubstituteOnHand": "",
|
||||
"SubstituteOnHand": "Máte k dispozici náhradu.",
|
||||
"ChildInheritFields": "",
|
||||
"ChildInheritFields_help": "",
|
||||
"InheritFields_help": "",
|
||||
@@ -478,5 +478,21 @@
|
||||
"Use_Plural_Food_Simple": "",
|
||||
"plural_usage_info": "",
|
||||
"Create Recipe": "Vytvořit recept",
|
||||
"Import Recipe": "Importovat recept"
|
||||
"Import Recipe": "Importovat recept",
|
||||
"per_serving": "na porci",
|
||||
"open_data_help_text": "Projekt Tandoor Open Data nabízí komunitou poskytnutá data pro Tandoor. Toto pole je automaticky vyplněno při importu a může být později upraveno.",
|
||||
"Data_Import_Info": "Rozšiřte svůj prostor o seznamy jídel, jednotek a další spravované komunitou, a vylepšete tak svoji sbírku receptů.",
|
||||
"Update_Existing_Data": "Aktualizovat existující data",
|
||||
"Use_Metric": "Používat metrické jednotky",
|
||||
"Learn_More": "Zjistit víc",
|
||||
"converted_unit": "Převedená jendotka",
|
||||
"converted_amount": "Převedené množství",
|
||||
"base_unit": "Základní jednotka",
|
||||
"base_amount": "Základní množství",
|
||||
"Datatype": "Datový typ",
|
||||
"Number of Objects": "Počet Objektů",
|
||||
"Property": "Vlastnost",
|
||||
"Conversion": "Převod",
|
||||
"Properties": "Vlastnosti",
|
||||
"recipe_property_info": "Můžete také přidávat vlastnosti k Vašim jídlům. Hodnoty budou automaticky přepočteny na základě Vašeho receptu!"
|
||||
}
|
||||
|
||||
@@ -296,7 +296,7 @@
|
||||
"OnHand_help": "Lebensmittel ist \"Vorrätig\" und wird nicht automatisch zur Einkaufsliste hinzugefügt. Der Status \"Vorrätig\" wird mit den Benutzern der Einkaufsliste geteilt.",
|
||||
"shopping_category_help": "Einkaufsläden können nach Produktkategorie entsprechend der Anordnung der Regalreihen sortiert werden.",
|
||||
"Foods": "Lebensmittel",
|
||||
"food_recipe_help": "Wird ein Rezept hier verknüpft, wird diese Verknüpfung in allen anderen Rezepten übernommen, die dieses Lebensmittel beinhaltet",
|
||||
"food_recipe_help": "Wird ein Rezept hier verknüpft, wird diese Verknüpfung in allen anderen Rezepten übernommen, die dieses Lebensmittel beinhalten",
|
||||
"review_shopping": "Überprüfe die Einkaufsliste vor dem Speichern",
|
||||
"view_recipe": "Rezept anschauen",
|
||||
"Planned": "Geplant",
|
||||
@@ -362,7 +362,7 @@
|
||||
"and_down": "& Runter",
|
||||
"enable_expert": "Expertenmodus aktivieren",
|
||||
"filter_name": "Name des Filters",
|
||||
"shared_with": "Geteilt mit",
|
||||
"shared_with": "geteilt mit",
|
||||
"asc": "Aufsteigend",
|
||||
"desc": "Absteigend",
|
||||
"book_filter_help": "Schließt zusätzlich zu den manuell hinzugefügten Rezepten, alle Rezepte die dem Filter entsprechen ein.",
|
||||
@@ -408,7 +408,7 @@
|
||||
"New_Supermarket": "Erstelle einen neuen Supermarkt",
|
||||
"New_Supermarket_Category": "Erstelle eine neue Supermarktkategorie",
|
||||
"warning_space_delete": "Du kannst deinen Space inklusive all deiner Rezepte, Shoppinglisten, Essensplänen und allem anderen, das du erstellt hast löschen. Dieser Schritt kann nicht rückgängig gemacht werden! Bist du sicher, dass du das tun möchtest?",
|
||||
"Copy Link": "Kopiere den Link in die Zwischenablage",
|
||||
"Copy Link": "Link Kopieren",
|
||||
"Users": "Benutzer",
|
||||
"facet_count_info": "Zeige die Anzahl der Rezepte auf den Suchfiltern.",
|
||||
"Copy Token": "Kopiere Token",
|
||||
@@ -460,9 +460,9 @@
|
||||
"Comments_setting": "Kommentare anzeigen",
|
||||
"reset_food_inheritance": "Vererbung zurücksetzen",
|
||||
"food_inherit_info": "Datenfelder des Lebensmittels, die standardmäßig vererbt werden sollen.",
|
||||
"Are_You_Sure": "Bist du dir sicher?",
|
||||
"Are_You_Sure": "Sind sie sicher?",
|
||||
"Plural": "Plural",
|
||||
"plural_short": "pl.",
|
||||
"plural_short": "Plural",
|
||||
"Use_Plural_Unit_Always": "Pluralform der Maßeinheit immer verwenden",
|
||||
"Use_Plural_Unit_Simple": "Pluralform der Maßeinheit dynamisch anpassen",
|
||||
"Use_Plural_Food_Always": "Pluralform des Essens immer verwenden",
|
||||
@@ -498,5 +498,6 @@
|
||||
"Number of Objects": "Anzahl von Objekten",
|
||||
"Property": "Eigenschaft",
|
||||
"Conversion": "Umrechnung",
|
||||
"Properties": "Eigenschaften"
|
||||
"Properties": "Eigenschaften",
|
||||
"total": "gesamt"
|
||||
}
|
||||
|
||||
501
vue/src/locales/el.json
Normal file
501
vue/src/locales/el.json
Normal file
@@ -0,0 +1,501 @@
|
||||
{
|
||||
"warning_feature_beta": "Αυτή η λειτουργία βρίσκεται αυτήν τη στιγμή σε κατάσταση BETA (δοκιμαστική). Παρακαλούμε να αναμένετε σφάλματα και πιθανές αλλαγές που μπορεί να προκαλέσουν απώλεια δεδομένων που σχετίζονται με τις διάφορες λειτουργίες στο μέλλον.",
|
||||
"err_fetching_resource": "",
|
||||
"err_creating_resource": "",
|
||||
"err_updating_resource": "",
|
||||
"err_deleting_resource": "",
|
||||
"err_deleting_protected_resource": "",
|
||||
"err_moving_resource": "",
|
||||
"err_merging_resource": "",
|
||||
"success_fetching_resource": "",
|
||||
"success_creating_resource": "",
|
||||
"success_updating_resource": "",
|
||||
"success_deleting_resource": "",
|
||||
"success_moving_resource": "",
|
||||
"success_merging_resource": "",
|
||||
"file_upload_disabled": "",
|
||||
"recipe_property_info": "",
|
||||
"warning_space_delete": "",
|
||||
"food_inherit_info": "",
|
||||
"facet_count_info": "",
|
||||
"step_time_minutes": "",
|
||||
"confirm_delete": "",
|
||||
"import_running": "",
|
||||
"all_fields_optional": "",
|
||||
"convert_internal": "",
|
||||
"show_only_internal": "",
|
||||
"show_split_screen": "",
|
||||
"Log_Recipe_Cooking": "",
|
||||
"External_Recipe_Image": "",
|
||||
"Add_to_Shopping": "",
|
||||
"Add_to_Plan": "",
|
||||
"Step_start_time": "",
|
||||
"Sort_by_new": "",
|
||||
"Table_of_Contents": "",
|
||||
"Recipes_per_page": "",
|
||||
"Show_as_header": "",
|
||||
"Hide_as_header": "",
|
||||
"Add_nutrition_recipe": "",
|
||||
"Remove_nutrition_recipe": "",
|
||||
"Copy_template_reference": "",
|
||||
"per_serving": "",
|
||||
"Save_and_View": "",
|
||||
"Manage_Books": "",
|
||||
"Meal_Plan": "",
|
||||
"Select_Book": "",
|
||||
"Select_File": "",
|
||||
"Recipe_Image": "",
|
||||
"Import_finished": "",
|
||||
"View_Recipes": "",
|
||||
"Log_Cooking": "",
|
||||
"New_Recipe": "",
|
||||
"Url_Import": "",
|
||||
"Reset_Search": "",
|
||||
"Recently_Viewed": "",
|
||||
"Load_More": "",
|
||||
"New_Keyword": "",
|
||||
"Delete_Keyword": "",
|
||||
"Edit_Keyword": "",
|
||||
"Edit_Recipe": "",
|
||||
"Move_Keyword": "",
|
||||
"Merge_Keyword": "",
|
||||
"Hide_Keywords": "",
|
||||
"Hide_Recipes": "",
|
||||
"Move_Up": "",
|
||||
"Move_Down": "",
|
||||
"Step_Name": "",
|
||||
"Step_Type": "",
|
||||
"Make_Header": "",
|
||||
"Make_Ingredient": "",
|
||||
"Amount": "",
|
||||
"Enable_Amount": "",
|
||||
"Disable_Amount": "",
|
||||
"Ingredient Editor": "",
|
||||
"Description_Replace": "",
|
||||
"Instruction_Replace": "",
|
||||
"Auto_Sort": "",
|
||||
"Auto_Sort_Help": "",
|
||||
"Private_Recipe": "",
|
||||
"Private_Recipe_Help": "",
|
||||
"reusable_help_text": "",
|
||||
"open_data_help_text": "",
|
||||
"Open_Data_Slug": "",
|
||||
"Open_Data_Import": "",
|
||||
"Data_Import_Info": "",
|
||||
"Update_Existing_Data": "",
|
||||
"Use_Metric": "",
|
||||
"Learn_More": "",
|
||||
"converted_unit": "",
|
||||
"converted_amount": "",
|
||||
"base_unit": "",
|
||||
"base_amount": "",
|
||||
"Datatype": "",
|
||||
"Number of Objects": "",
|
||||
"Add_Step": "",
|
||||
"Keywords": "Λέξεις κλειδιά",
|
||||
"Books": "Βιβλία",
|
||||
"Proteins": "",
|
||||
"Fats": "",
|
||||
"Carbohydrates": "",
|
||||
"Calories": "",
|
||||
"Energy": "",
|
||||
"Nutrition": "",
|
||||
"Date": "",
|
||||
"Share": "",
|
||||
"Automation": "",
|
||||
"Parameter": "",
|
||||
"Export": "",
|
||||
"Copy": "",
|
||||
"Rating": "",
|
||||
"Close": "",
|
||||
"Cancel": "",
|
||||
"Link": "",
|
||||
"Add": "",
|
||||
"New": "",
|
||||
"Note": "",
|
||||
"Success": "",
|
||||
"Failure": "",
|
||||
"Protected": "",
|
||||
"Ingredients": "",
|
||||
"Supermarket": "",
|
||||
"Categories": "",
|
||||
"Category": "",
|
||||
"Selected": "",
|
||||
"min": "",
|
||||
"Servings": "Μερίδες",
|
||||
"Waiting": "",
|
||||
"Preparation": "",
|
||||
"External": "",
|
||||
"Size": "",
|
||||
"Files": "",
|
||||
"File": "",
|
||||
"Edit": "",
|
||||
"Image": "",
|
||||
"Delete": "",
|
||||
"Open": "",
|
||||
"Ok": "",
|
||||
"Save": "",
|
||||
"Step": "",
|
||||
"Search": "",
|
||||
"Import": "",
|
||||
"Print": "",
|
||||
"Settings": "Ρυθμίσεις",
|
||||
"or": "",
|
||||
"and": "",
|
||||
"Information": "",
|
||||
"Download": "",
|
||||
"Create": "",
|
||||
"Search Settings": "",
|
||||
"View": "",
|
||||
"Recipes": "",
|
||||
"Move": "",
|
||||
"Merge": "",
|
||||
"Parent": "",
|
||||
"Copy Link": "",
|
||||
"Copy Token": "",
|
||||
"delete_confirmation": "",
|
||||
"move_confirmation": "",
|
||||
"merge_confirmation": "",
|
||||
"create_rule": "",
|
||||
"move_selection": "",
|
||||
"merge_selection": "",
|
||||
"Root": "",
|
||||
"Ignore_Shopping": "",
|
||||
"Shopping_Category": "",
|
||||
"Shopping_Categories": "",
|
||||
"Edit_Food": "",
|
||||
"Move_Food": "",
|
||||
"New_Food": "",
|
||||
"Hide_Food": "",
|
||||
"Food_Alias": "",
|
||||
"Unit_Alias": "",
|
||||
"Keyword_Alias": "",
|
||||
"Delete_Food": "",
|
||||
"No_ID": "",
|
||||
"Meal_Plan_Days": "",
|
||||
"merge_title": "",
|
||||
"move_title": "",
|
||||
"Food": "Φαγητό",
|
||||
"Property": "",
|
||||
"Conversion": "",
|
||||
"Original_Text": "",
|
||||
"Recipe_Book": "",
|
||||
"del_confirmation_tree": "",
|
||||
"delete_title": "",
|
||||
"create_title": "",
|
||||
"edit_title": "",
|
||||
"Name": "",
|
||||
"Properties": "",
|
||||
"Type": "",
|
||||
"Description": "",
|
||||
"Recipe": "",
|
||||
"tree_root": "",
|
||||
"Icon": "",
|
||||
"Unit": "",
|
||||
"Decimals": "",
|
||||
"Default_Unit": "",
|
||||
"No_Results": "",
|
||||
"New_Unit": "",
|
||||
"Create_New_Shopping Category": "",
|
||||
"Create_New_Food": "",
|
||||
"Create_New_Keyword": "",
|
||||
"Create_New_Unit": "",
|
||||
"Create_New_Meal_Type": "",
|
||||
"Create_New_Shopping_Category": "",
|
||||
"and_up": "",
|
||||
"and_down": "",
|
||||
"Instructions": "",
|
||||
"Unrated": "",
|
||||
"Automate": "",
|
||||
"Empty": "",
|
||||
"Key_Ctrl": "",
|
||||
"Key_Shift": "",
|
||||
"Time": "",
|
||||
"Text": "",
|
||||
"Shopping_list": "",
|
||||
"Added_by": "",
|
||||
"Added_on": "",
|
||||
"AddToShopping": "",
|
||||
"IngredientInShopping": "",
|
||||
"NotInShopping": "",
|
||||
"OnHand": "",
|
||||
"FoodOnHand": "",
|
||||
"FoodNotOnHand": "",
|
||||
"Undefined": "",
|
||||
"Create_Meal_Plan_Entry": "",
|
||||
"Edit_Meal_Plan_Entry": "",
|
||||
"Title": "",
|
||||
"Week": "",
|
||||
"Month": "",
|
||||
"Year": "",
|
||||
"Planner": "",
|
||||
"Planner_Settings": "",
|
||||
"Period": "",
|
||||
"Plan_Period_To_Show": "",
|
||||
"Periods": "",
|
||||
"Plan_Show_How_Many_Periods": "",
|
||||
"Starting_Day": "",
|
||||
"Meal_Types": "",
|
||||
"Meal_Type": "",
|
||||
"New_Entry": "",
|
||||
"Clone": "",
|
||||
"Drag_Here_To_Delete": "",
|
||||
"Meal_Type_Required": "",
|
||||
"Title_or_Recipe_Required": "",
|
||||
"Color": "",
|
||||
"New_Meal_Type": "",
|
||||
"Use_Fractions": "",
|
||||
"Use_Fractions_Help": "",
|
||||
"AddFoodToShopping": "",
|
||||
"RemoveFoodFromShopping": "",
|
||||
"DeleteShoppingConfirm": "",
|
||||
"IgnoredFood": "",
|
||||
"Add_Servings_to_Shopping": "",
|
||||
"Week_Numbers": "",
|
||||
"Show_Week_Numbers": "",
|
||||
"Export_As_ICal": "",
|
||||
"Export_To_ICal": "",
|
||||
"Cannot_Add_Notes_To_Shopping": "",
|
||||
"Added_To_Shopping_List": "",
|
||||
"Shopping_List_Empty": "",
|
||||
"Next_Period": "",
|
||||
"Previous_Period": "",
|
||||
"Current_Period": "",
|
||||
"Next_Day": "",
|
||||
"Previous_Day": "",
|
||||
"Inherit": "",
|
||||
"InheritFields": "",
|
||||
"FoodInherit": "",
|
||||
"ShowUncategorizedFood": "",
|
||||
"GroupBy": "",
|
||||
"Language": "",
|
||||
"Theme": "",
|
||||
"SupermarketCategoriesOnly": "",
|
||||
"MoveCategory": "",
|
||||
"CountMore": "",
|
||||
"IgnoreThis": "",
|
||||
"DelayFor": "",
|
||||
"Warning": "",
|
||||
"NoCategory": "",
|
||||
"InheritWarning": "",
|
||||
"ShowDelayed": "",
|
||||
"Completed": "",
|
||||
"OfflineAlert": "",
|
||||
"shopping_share": "",
|
||||
"shopping_auto_sync": "",
|
||||
"one_url_per_line": "",
|
||||
"mealplan_autoadd_shopping": "",
|
||||
"mealplan_autoexclude_onhand": "",
|
||||
"mealplan_autoinclude_related": "",
|
||||
"default_delay": "",
|
||||
"plan_share_desc": "",
|
||||
"shopping_share_desc": "",
|
||||
"shopping_auto_sync_desc": "",
|
||||
"mealplan_autoadd_shopping_desc": "",
|
||||
"mealplan_autoexclude_onhand_desc": "",
|
||||
"mealplan_autoinclude_related_desc": "",
|
||||
"default_delay_desc": "",
|
||||
"filter_to_supermarket": "",
|
||||
"Coming_Soon": "",
|
||||
"Auto_Planner": "",
|
||||
"New_Cookbook": "",
|
||||
"Hide_Keyword": "",
|
||||
"Hour": "",
|
||||
"Hours": "",
|
||||
"Day": "",
|
||||
"Days": "",
|
||||
"Second": "",
|
||||
"Seconds": "",
|
||||
"Clear": "",
|
||||
"Users": "",
|
||||
"Invites": "",
|
||||
"err_move_self": "",
|
||||
"nothing": "",
|
||||
"err_merge_self": "",
|
||||
"show_sql": "",
|
||||
"filter_to_supermarket_desc": "",
|
||||
"CategoryName": "",
|
||||
"SupermarketName": "",
|
||||
"CategoryInstruction": "",
|
||||
"shopping_recent_days_desc": "",
|
||||
"shopping_recent_days": "",
|
||||
"download_pdf": "",
|
||||
"download_csv": "",
|
||||
"csv_delim_help": "",
|
||||
"csv_delim_label": "",
|
||||
"SuccessClipboard": "",
|
||||
"copy_to_clipboard": "",
|
||||
"csv_prefix_help": "",
|
||||
"csv_prefix_label": "",
|
||||
"copy_markdown_table": "",
|
||||
"in_shopping": "",
|
||||
"DelayUntil": "",
|
||||
"Pin": "",
|
||||
"Unpin": "",
|
||||
"PinnedConfirmation": "",
|
||||
"UnpinnedConfirmation": "",
|
||||
"mark_complete": "",
|
||||
"QuickEntry": "",
|
||||
"shopping_add_onhand_desc": "",
|
||||
"shopping_add_onhand": "",
|
||||
"related_recipes": "",
|
||||
"today_recipes": "",
|
||||
"sql_debug": "",
|
||||
"remember_search": "",
|
||||
"remember_hours": "",
|
||||
"tree_select": "",
|
||||
"OnHand_help": "",
|
||||
"ignore_shopping_help": "",
|
||||
"shopping_category_help": "",
|
||||
"food_recipe_help": "",
|
||||
"Foods": "",
|
||||
"Account": "",
|
||||
"Cosmetic": "",
|
||||
"API": "",
|
||||
"enable_expert": "",
|
||||
"expert_mode": "",
|
||||
"simple_mode": "",
|
||||
"advanced": "",
|
||||
"fields": "",
|
||||
"show_keywords": "",
|
||||
"show_foods": "",
|
||||
"show_books": "",
|
||||
"show_rating": "",
|
||||
"show_units": "",
|
||||
"show_filters": "",
|
||||
"not": "",
|
||||
"save_filter": "",
|
||||
"filter_name": "",
|
||||
"left_handed": "",
|
||||
"left_handed_help": "",
|
||||
"Custom Filter": "",
|
||||
"shared_with": "",
|
||||
"sort_by": "",
|
||||
"asc": "",
|
||||
"desc": "",
|
||||
"date_viewed": "",
|
||||
"last_cooked": "",
|
||||
"times_cooked": "",
|
||||
"date_created": "",
|
||||
"show_sortby": "",
|
||||
"search_rank": "",
|
||||
"make_now": "",
|
||||
"recipe_filter": "",
|
||||
"book_filter_help": "",
|
||||
"review_shopping": "",
|
||||
"view_recipe": "",
|
||||
"copy_to_new": "",
|
||||
"recipe_name": "",
|
||||
"paste_ingredients_placeholder": "",
|
||||
"paste_ingredients": "",
|
||||
"ingredient_list": "",
|
||||
"explain": "",
|
||||
"filter": "",
|
||||
"Website": "",
|
||||
"App": "",
|
||||
"Message": "",
|
||||
"Bookmarklet": "",
|
||||
"Sticky_Nav": "",
|
||||
"Sticky_Nav_Help": "",
|
||||
"Nav_Color": "",
|
||||
"Nav_Color_Help": "",
|
||||
"Use_Kj": "",
|
||||
"Comments_setting": "",
|
||||
"click_image_import": "",
|
||||
"no_more_images_found": "",
|
||||
"import_duplicates": "",
|
||||
"paste_json": "",
|
||||
"Click_To_Edit": "",
|
||||
"search_no_recipes": "",
|
||||
"search_import_help_text": "",
|
||||
"search_create_help_text": "",
|
||||
"warning_duplicate_filter": "",
|
||||
"reset_children": "",
|
||||
"reset_children_help": "",
|
||||
"reset_food_inheritance": "",
|
||||
"reset_food_inheritance_info": "",
|
||||
"substitute_help": "",
|
||||
"substitute_siblings_help": "",
|
||||
"substitute_children_help": "",
|
||||
"substitute_siblings": "",
|
||||
"substitute_children": "",
|
||||
"SubstituteOnHand": "",
|
||||
"ChildInheritFields": "",
|
||||
"ChildInheritFields_help": "",
|
||||
"InheritFields_help": "",
|
||||
"show_ingredient_overview": "",
|
||||
"Ingredient Overview": "",
|
||||
"last_viewed": "",
|
||||
"created_on": "",
|
||||
"updatedon": "",
|
||||
"Imported_From": "",
|
||||
"advanced_search_settings": "",
|
||||
"nothing_planned_today": "",
|
||||
"no_pinned_recipes": "",
|
||||
"Planned": "",
|
||||
"Pinned": "",
|
||||
"Imported": "",
|
||||
"Quick actions": "",
|
||||
"Ratings": "",
|
||||
"Internal": "",
|
||||
"Units": "",
|
||||
"Manage_Emails": "",
|
||||
"Change_Password": "",
|
||||
"Social_Authentication": "",
|
||||
"Random Recipes": "",
|
||||
"parameter_count": "",
|
||||
"select_keyword": "",
|
||||
"add_keyword": "",
|
||||
"select_file": "",
|
||||
"select_recipe": "",
|
||||
"select_unit": "",
|
||||
"select_food": "",
|
||||
"remove_selection": "",
|
||||
"empty_list": "",
|
||||
"Select": "",
|
||||
"Supermarkets": "",
|
||||
"User": "",
|
||||
"Username": "",
|
||||
"First_name": "",
|
||||
"Last_name": "",
|
||||
"Keyword": "Λέξη κλειδί",
|
||||
"Advanced": "",
|
||||
"Page": "",
|
||||
"Single": "",
|
||||
"Multiple": "",
|
||||
"Reset": "",
|
||||
"Disabled": "",
|
||||
"Disable": "",
|
||||
"Options": "",
|
||||
"Create Food": "",
|
||||
"create_food_desc": "",
|
||||
"additional_options": "",
|
||||
"Importer_Help": "",
|
||||
"Documentation": "",
|
||||
"Select_App_To_Import": "",
|
||||
"Import_Supported": "",
|
||||
"Export_Supported": "",
|
||||
"Import_Not_Yet_Supported": "",
|
||||
"Export_Not_Yet_Supported": "",
|
||||
"Import_Result_Info": "",
|
||||
"Recipes_In_Import": "",
|
||||
"Toggle": "",
|
||||
"total": "",
|
||||
"Import_Error": "",
|
||||
"Warning_Delete_Supermarket_Category": "",
|
||||
"New_Supermarket": "",
|
||||
"New_Supermarket_Category": "",
|
||||
"Are_You_Sure": "",
|
||||
"Valid Until": "",
|
||||
"Split_All_Steps": "",
|
||||
"Combine_All_Steps": "",
|
||||
"Plural": "",
|
||||
"plural_short": "",
|
||||
"Use_Plural_Unit_Always": "",
|
||||
"Use_Plural_Unit_Simple": "",
|
||||
"Use_Plural_Food_Always": "",
|
||||
"Use_Plural_Food_Simple": "",
|
||||
"plural_usage_info": "",
|
||||
"Create Recipe": "",
|
||||
"Import Recipe": ""
|
||||
}
|
||||
@@ -258,12 +258,14 @@ export class Models {
|
||||
field: "description",
|
||||
label: "Description",
|
||||
placeholder: "",
|
||||
optional: true,
|
||||
},
|
||||
icon: {
|
||||
form_field: true,
|
||||
type: "emoji",
|
||||
field: "icon",
|
||||
label: "Icon",
|
||||
optional: true,
|
||||
},
|
||||
full_name: {
|
||||
form_field: true,
|
||||
@@ -295,6 +297,7 @@ export class Models {
|
||||
field: "plural_name",
|
||||
label: "Plural name",
|
||||
placeholder: "",
|
||||
optional: true,
|
||||
},
|
||||
description: {
|
||||
form_field: true,
|
||||
@@ -302,6 +305,7 @@ export class Models {
|
||||
field: "description",
|
||||
label: "Description",
|
||||
placeholder: "",
|
||||
optional: true,
|
||||
},
|
||||
open_data_slug: {
|
||||
form_field: true,
|
||||
@@ -310,6 +314,7 @@ export class Models {
|
||||
disabled: true,
|
||||
label: "Open_Data_Slug",
|
||||
help_text: "open_data_help_text",
|
||||
optional: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -364,12 +369,14 @@ export class Models {
|
||||
field: "description",
|
||||
label: "Description",
|
||||
placeholder: "",
|
||||
optional: true,
|
||||
},
|
||||
icon: {
|
||||
form_field: true,
|
||||
type: "emoji",
|
||||
field: "icon",
|
||||
label: "Icon",
|
||||
optional: true,
|
||||
},
|
||||
filter: {
|
||||
form_field: true,
|
||||
@@ -377,6 +384,7 @@ export class Models {
|
||||
field: "filter",
|
||||
label: "Custom Filter",
|
||||
list: "CUSTOM_FILTER",
|
||||
optional: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -401,6 +409,7 @@ export class Models {
|
||||
field: "description",
|
||||
label: "Description",
|
||||
placeholder: "",
|
||||
optional: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -451,6 +460,7 @@ export class Models {
|
||||
field: "description",
|
||||
label: "Description",
|
||||
placeholder: "",
|
||||
optional: true,
|
||||
},
|
||||
categories: {
|
||||
form_field: true,
|
||||
@@ -461,6 +471,7 @@ export class Models {
|
||||
field: "category_to_supermarket",
|
||||
label: "Categories", // form.label always translated in utils.getForm()
|
||||
placeholder: "",
|
||||
optional: true,
|
||||
},
|
||||
open_data_slug: {
|
||||
form_field: true,
|
||||
@@ -469,6 +480,7 @@ export class Models {
|
||||
disabled: true,
|
||||
label: "Open_Data_Slug",
|
||||
help_text: "open_data_help_text",
|
||||
optional: true,
|
||||
},
|
||||
},
|
||||
config: {
|
||||
@@ -507,6 +519,7 @@ export class Models {
|
||||
field: "description",
|
||||
label: "Description",
|
||||
placeholder: "",
|
||||
optional: true,
|
||||
},
|
||||
type: {
|
||||
form_field: true,
|
||||
@@ -652,6 +665,7 @@ export class Models {
|
||||
disabled: true,
|
||||
label: "Open_Data_Slug",
|
||||
help_text: "open_data_help_text",
|
||||
optional: true,
|
||||
},
|
||||
|
||||
},
|
||||
@@ -685,6 +699,7 @@ export class Models {
|
||||
field: "icon",
|
||||
label: "Icon",
|
||||
placeholder: "",
|
||||
optional: true,
|
||||
},
|
||||
unit: {
|
||||
form_field: true,
|
||||
@@ -692,6 +707,7 @@ export class Models {
|
||||
field: "unit",
|
||||
label: "Unit",
|
||||
placeholder: "",
|
||||
optional: true,
|
||||
},
|
||||
description: {
|
||||
form_field: true,
|
||||
@@ -699,6 +715,7 @@ export class Models {
|
||||
field: "description",
|
||||
label: "Description",
|
||||
placeholder: "",
|
||||
optional: true,
|
||||
},
|
||||
open_data_slug: {
|
||||
form_field: true,
|
||||
@@ -707,6 +724,7 @@ export class Models {
|
||||
disabled: true,
|
||||
label: "Open_Data_Slug",
|
||||
help_text: "open_data_help_text",
|
||||
optional: true,
|
||||
},
|
||||
},
|
||||
|
||||
@@ -853,15 +871,8 @@ export class Models {
|
||||
apiName: "InviteLink",
|
||||
paginated: false,
|
||||
create: {
|
||||
params: [["email", "group", "valid_until", "reusable"]],
|
||||
params: [["email", "group", "valid_until", "reusable", "internal_note"]],
|
||||
form: {
|
||||
email: {
|
||||
form_field: true,
|
||||
type: "text",
|
||||
field: "email",
|
||||
label: "Email",
|
||||
placeholder: "",
|
||||
},
|
||||
group: {
|
||||
form_field: true,
|
||||
type: "lookup",
|
||||
@@ -878,6 +889,14 @@ export class Models {
|
||||
label: "Valid Until",
|
||||
placeholder: "",
|
||||
},
|
||||
email: {
|
||||
form_field: true,
|
||||
type: "text",
|
||||
field: "email",
|
||||
label: "Email",
|
||||
placeholder: "",
|
||||
optional: true,
|
||||
},
|
||||
reusable: {
|
||||
form_field: true,
|
||||
type: "checkbox",
|
||||
@@ -886,6 +905,15 @@ export class Models {
|
||||
help_text: "reusable_help_text",
|
||||
placeholder: "",
|
||||
},
|
||||
internal_note: {
|
||||
form_field: true,
|
||||
type: "textarea",
|
||||
field: "internal_note",
|
||||
label: "Note",
|
||||
placeholder: "",
|
||||
optional: true,
|
||||
},
|
||||
form_function: "InviteLinkDefaultValid",
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -14,6 +14,7 @@ import {BToast} from "bootstrap-vue"
|
||||
// * */
|
||||
import Vue from "vue"
|
||||
import {Actions, Models} from "./models"
|
||||
import moment from "moment";
|
||||
|
||||
export const ToastMixin = {
|
||||
name: "ToastMixin",
|
||||
@@ -721,6 +722,10 @@ export const formFunctions = {
|
||||
form.fields.filter((x) => x.field === "inherit_fields")[0].value = getUserPreference("food_inherit_default")
|
||||
return form
|
||||
},
|
||||
InviteLinkDefaultValid: function (form){
|
||||
form.fields.filter((x) => x.field === "valid_until")[0].value = moment().add(7, "days").format('yyyy-MM-DD')
|
||||
return form
|
||||
},
|
||||
AutomationOrderDefault: function (form) {
|
||||
if (form.fields.filter((x) => x.field === "order")[0].value === undefined) {
|
||||
form.fields.filter((x) => x.field === "order")[0].value = 1000
|
||||
|
||||
Reference in New Issue
Block a user