playing with delete confirm view

This commit is contained in:
vabene1111
2025-09-20 19:04:51 +02:00
parent b037d90220
commit b8a403b7c1
43 changed files with 3231 additions and 161 deletions

View File

@@ -1718,7 +1718,7 @@ class FdcQuerySerializer(serializers.Serializer):
foods = FdcQueryFoodsSerializer(many=True)
class GenericModelSerializer(serializers.Serializer):
class GenericModelReferenceSerializer(serializers.Serializer):
id = serializers.IntegerField()
model = serializers.CharField()
name = serializers.CharField()

View File

@@ -114,7 +114,7 @@ from cookbook.serializer import (AccessTokenSerializer, AutomationSerializer, Au
LocalizationSerializer, ServerSettingsSerializer, RecipeFromSourceResponseSerializer, ShoppingListEntryBulkCreateSerializer, FdcQuerySerializer,
AiImportSerializer, ImportOpenDataSerializer, ImportOpenDataMetaDataSerializer, ImportOpenDataResponseSerializer, ExportRequestSerializer,
RecipeImportSerializer, ConnectorConfigSerializer, SearchPreferenceSerializer, SearchFieldsSerializer, RecipeBatchUpdateSerializer,
AiProviderSerializer, AiLogSerializer, FoodBatchUpdateSerializer, GenericModelSerializer
AiProviderSerializer, AiLogSerializer, FoodBatchUpdateSerializer, GenericModelReferenceSerializer
)
from cookbook.version_info import TANDOOR_VERSION
from cookbook.views.import_export import get_integration
@@ -515,6 +515,134 @@ class TreeMixin(MergeMixin, FuzzyFilterMixin):
return Response(content, status=status.HTTP_400_BAD_REQUEST)
def paginate(func):
"""
pagination decorator for custom ViewSet actions
"""
@wraps(func)
def inner(self, *args, **kwargs):
queryset = func(self, *args, **kwargs)
assert isinstance(queryset, (list, QuerySet))
page = self.paginate_queryset(queryset)
if page is not None:
serializer = self.get_serializer(page, many=True)
return self.get_paginated_response(serializer.data)
serializer = self.get_serializer(queryset, many=True)
return Response(serializer.data)
return inner
class DeleteRelationMixing:
"""
mixin to add custom API function for model delete dependency checking
"""
@staticmethod
def collect(obj):
# collector.nested() nested seems to not include protecting but does include cascading
# collector.protected: objects that raise Protected or Restricted error when deleting unit
# collector.field_updates: fields that get updated when deleting the unit
# collector.model_objs: collects the objects that should be deleted together with the selected unit
collector = NestedObjects(using=DEFAULT_DB_ALIAS)
collector.collect([obj])
return collector
@extend_schema(responses=GenericModelReferenceSerializer(many=True))
@decorators.action(detail=True, methods=['GET'], serializer_class=GenericModelReferenceSerializer)
@paginate
def protecting(self, request, pk):
"""
get a paginated list of objects that are protecting the selected object form being deleted
"""
obj = self.queryset.filter(pk=pk, space=request.space).first()
if obj:
CACHE_KEY = f'DELETE_COLLECTOR_{request.space.pk}_PROTECTING_{obj.__class__.__name__}_{obj.pk}'
if c := caches['default'].get(CACHE_KEY, None):
return c
collector = self.collect(obj)
protected_objects = []
for o in collector.protected:
protected_objects.append({
'id': o.pk,
'model': o.__class__.__name__,
'name': str(o),
})
caches['default'].set(CACHE_KEY, protected_objects, 60)
return protected_objects
else:
return []
@extend_schema(responses=GenericModelReferenceSerializer(many=True))
@decorators.action(detail=True, methods=['GET'], serializer_class=GenericModelReferenceSerializer)
@paginate
def cascading(self, request, pk):
"""
get a paginated list of objects that will be cascaded (deleted) when deleting the selected object
"""
obj = self.queryset.filter(pk=pk, space=request.space).first()
if obj:
CACHE_KEY = f'DELETE_COLLECTOR_{request.space.pk}_CASCADING_{obj.__class__.__name__}_{obj.pk}'
if c := caches['default'].get(CACHE_KEY, None):
return c
collector = self.collect(obj)
cascading_objects = []
for model, objs in collector.model_objs.items():
for o in objs:
cascading_objects.append({
'id': o.pk,
'model': o.__class__.__name__,
'name': str(o),
})
caches['default'].set(CACHE_KEY, cascading_objects, 60)
return cascading_objects
else:
return []
@extend_schema(responses=GenericModelReferenceSerializer(many=True))
@decorators.action(detail=True, methods=['GET'], serializer_class=GenericModelReferenceSerializer)
@paginate
def nulling(self, request, pk):
"""
get a paginated list of objects where the selected object will be removed whe its deleted
"""
obj = self.queryset.filter(pk=pk, space=request.space).first()
if obj:
CACHE_KEY = f'DELETE_COLLECTOR_{request.space.pk}_NULLING_{obj.__class__.__name__}_{obj.pk}'
if c := caches['default'].get(CACHE_KEY, None):
return c
collector = self.collect(obj)
nulling_objects = []
# field_updates is a dict of relations that will be updated and querysets of items affected
for key, value in collector.field_updates.items():
# iterate over each queryset for relation
for qs in value:
# itereate over each object in queryset of relation
for o in qs:
nulling_objects.append(
{
'id': o.pk,
'model': o.__class__.__name__,
'name': str(o),
}
)
caches['default'].set(CACHE_KEY, nulling_objects, 60)
return nulling_objects
else:
return []
@extend_schema_view(list=extend_schema(parameters=[
OpenApiParameter(name='filter_list', description='User IDs, repeat for multiple', type=str, many=True),
]))
@@ -637,7 +765,7 @@ class SearchPreferenceViewSet(LoggingMixin, viewsets.ModelViewSet):
return self.queryset.filter(user=self.request.user)
class AiProviderViewSet(LoggingMixin, viewsets.ModelViewSet):
class AiProviderViewSet(LoggingMixin, viewsets.ModelViewSet, DeleteRelationMixing):
queryset = AiProvider.objects
serializer_class = AiProviderSerializer
permission_classes = [CustomAiProviderPermission & CustomTokenHasReadWriteScope]
@@ -660,7 +788,7 @@ class AiLogViewSet(LoggingMixin, viewsets.ModelViewSet):
return self.queryset.filter(space=self.request.space)
class StorageViewSet(LoggingMixin, viewsets.ModelViewSet):
class StorageViewSet(LoggingMixin, viewsets.ModelViewSet, DeleteRelationMixing):
# TODO handle delete protect error and adjust test
queryset = Storage.objects
serializer_class = StorageSerializer
@@ -671,7 +799,7 @@ class StorageViewSet(LoggingMixin, viewsets.ModelViewSet):
return self.queryset.filter(space=self.request.space)
class SyncViewSet(LoggingMixin, viewsets.ModelViewSet):
class SyncViewSet(LoggingMixin, viewsets.ModelViewSet, DeleteRelationMixing):
queryset = Sync.objects
serializer_class = SyncSerializer
permission_classes = [CustomIsAdmin & CustomTokenHasReadWriteScope]
@@ -732,7 +860,7 @@ class RecipeImportViewSet(LoggingMixin, viewsets.ModelViewSet):
return Response({'msg': 'ok'}, status=status.HTTP_200_OK)
class ConnectorConfigViewSet(LoggingMixin, viewsets.ModelViewSet):
class ConnectorConfigViewSet(LoggingMixin, viewsets.ModelViewSet, DeleteRelationMixing):
queryset = ConnectorConfig.objects
serializer_class = ConnectorConfigSerializer
permission_classes = [CustomIsAdmin & CustomTokenHasReadWriteScope]
@@ -742,7 +870,7 @@ class ConnectorConfigViewSet(LoggingMixin, viewsets.ModelViewSet):
return self.queryset.filter(space=self.request.space)
class SupermarketViewSet(LoggingMixin, StandardFilterModelViewSet):
class SupermarketViewSet(LoggingMixin, StandardFilterModelViewSet, DeleteRelationMixing):
queryset = Supermarket.objects
serializer_class = SupermarketSerializer
permission_classes = [CustomIsUser & CustomTokenHasReadWriteScope]
@@ -754,7 +882,7 @@ class SupermarketViewSet(LoggingMixin, StandardFilterModelViewSet):
# TODO does supermarket category have settings to support fuzzy filtering and/or merge?
class SupermarketCategoryViewSet(LoggingMixin, FuzzyFilterMixin, MergeMixin):
class SupermarketCategoryViewSet(LoggingMixin, FuzzyFilterMixin, MergeMixin, DeleteRelationMixing):
queryset = SupermarketCategory.objects
model = SupermarketCategory
serializer_class = SupermarketCategorySerializer
@@ -777,7 +905,7 @@ class SupermarketCategoryRelationViewSet(LoggingMixin, StandardFilterModelViewSe
return super().get_queryset()
class KeywordViewSet(LoggingMixin, TreeMixin):
class KeywordViewSet(LoggingMixin, TreeMixin, DeleteRelationMixing):
queryset = Keyword.objects
model = Keyword
serializer_class = KeywordSerializer
@@ -785,120 +913,6 @@ class KeywordViewSet(LoggingMixin, TreeMixin):
pagination_class = DefaultPagination
def paginate(func):
@wraps(func)
def inner(self, *args, **kwargs):
queryset = func(self, *args, **kwargs)
assert isinstance(queryset, (list, QuerySet))
page = self.paginate_queryset(queryset)
if page is not None:
serializer = self.get_serializer(page, many=True)
return self.get_paginated_response(serializer.data)
serializer = self.get_serializer(queryset, many=True)
return Response(serializer.data)
return inner
class DeleteRelationMixing:
# for units
# foodproperty_unit = PROTECT
# Ingredient = SET NULL
# UnitConversion = CASCADE
# print(collector.nested()) # nested seems to not include protecting but does include cascading
# protected: objects that raise Protected or Restricted error when deleting unit
# field_updates: fields that get updated when deleting the unit
# model objs: collects the objects that should be deleted together with the selected unit
collector = None
def collect(self, obj):
# TODO individual collector cache keys
# TODO does this even make sense with multiple workers because of cache misses?
if c := caches['default'].get('DELETING_COLLECTOR_TEMP_CACHE_NAME', None):
print('collector from cache')
return c
collector = NestedObjects(using=DEFAULT_DB_ALIAS)
collector.collect([obj])
self.collector = collector
caches['default'].set('DELETING_COLLECTOR_TEMP_CACHE_NAME', collector, 15)
print('new collector')
return collector
@extend_schema(responses=GenericModelSerializer(many=True))
@decorators.action(detail=True, methods=['GET'], serializer_class=GenericModelSerializer, pagination_class=DefaultPagination)
@paginate
def protecting(self, request, pk):
obj = self.queryset.filter(pk=pk, space=request.space).first()
if obj:
collector = self.collect(obj)
protected_objects = []
for o in collector.protected:
protected_objects.append({
'id': o.pk,
'model': o.__class__.__name__,
'name': str(o),
})
return protected_objects
else:
return []
@extend_schema(responses=GenericModelSerializer(many=True))
@decorators.action(detail=True, methods=['GET'], serializer_class=GenericModelSerializer, pagination_class=DefaultPagination)
@paginate
def cascading(self, request, pk):
obj = self.queryset.filter(pk=pk, space=request.space).first()
if obj:
collector = self.collect(obj)
cascading_objects = []
for model, objs in collector.model_objs.items():
for o in objs:
cascading_objects.append({
'id': o.pk,
'model': o.__class__.__name__,
'name': str(o),
})
return cascading_objects
else:
return []
@extend_schema(responses=GenericModelSerializer(many=True))
@decorators.action(detail=True, methods=['GET'], serializer_class=GenericModelSerializer, pagination_class=DefaultPagination)
@paginate
def nulling(self, request, pk):
obj = self.queryset.filter(pk=pk, space=request.space).first()
if obj:
collector = self.collect(obj)
updating_objects = []
# field_updates is a dict of relations that will be updated and querysets of items affected
for key, value in collector.field_updates.items():
# iterate over each queryset for relation
for qs in value:
# itereate over each object in queryset of relation
for o in qs:
updating_objects.append(
{
'id': o.pk,
'model': o.__class__.__name__,
'name': str(o),
}
)
return updating_objects
else:
return []
class UnitViewSet(LoggingMixin, MergeMixin, FuzzyFilterMixin, DeleteRelationMixing):
queryset = Unit.objects
model = Unit
@@ -919,7 +933,7 @@ class FoodInheritFieldViewSet(LoggingMixin, viewsets.ReadOnlyModelViewSet):
return super().get_queryset()
class FoodViewSet(LoggingMixin, TreeMixin):
class FoodViewSet(LoggingMixin, TreeMixin, DeleteRelationMixing):
queryset = Food.objects
model = Food
serializer_class = FoodSerializer
@@ -1167,7 +1181,7 @@ class FoodViewSet(LoggingMixin, TreeMixin):
OpenApiParameter(name='order_direction', description='Order ascending or descending', type=str,
enum=['asc', 'desc']),
]))
class RecipeBookViewSet(LoggingMixin, StandardFilterModelViewSet):
class RecipeBookViewSet(LoggingMixin, StandardFilterModelViewSet, DeleteRelationMixing):
queryset = RecipeBook.objects
serializer_class = RecipeBookSerializer
permission_classes = [(CustomIsOwner | CustomIsShared) & CustomTokenHasReadWriteScope]
@@ -1321,7 +1335,7 @@ class AutoPlanViewSet(LoggingMixin, mixins.CreateModelMixin, viewsets.GenericVie
return Response(serializer.errors, 400)
class MealTypeViewSet(LoggingMixin, viewsets.ModelViewSet):
class MealTypeViewSet(LoggingMixin, viewsets.ModelViewSet, DeleteRelationMixing):
"""
returns list of meal types created by the
requesting user ordered by the order field.
@@ -1471,7 +1485,7 @@ class RecipePagination(PageNumberPagination):
OpenApiParameter(name='filter', description=_('ID of a custom filter. Returns all recipes matched by that filter.'), type=int),
OpenApiParameter(name='makenow', description=_('Filter recipes that can be made with OnHand food. [''true''/''<b>false</b>'']'), type=bool),
]))
class RecipeViewSet(LoggingMixin, viewsets.ModelViewSet):
class RecipeViewSet(LoggingMixin, viewsets.ModelViewSet, DeleteRelationMixing):
queryset = Recipe.objects
serializer_class = RecipeSerializer
# TODO split read and write permission for meal plan guest
@@ -1753,7 +1767,7 @@ class UnitConversionViewSet(LoggingMixin, viewsets.ModelViewSet):
enum=[m[0] for m in PropertyType.CHOICES])
]
))
class PropertyTypeViewSet(LoggingMixin, viewsets.ModelViewSet):
class PropertyTypeViewSet(LoggingMixin, viewsets.ModelViewSet, DeleteRelationMixing):
queryset = PropertyType.objects
serializer_class = PropertyTypeSerializer
permission_classes = [CustomIsUser & CustomTokenHasReadWriteScope]
@@ -1984,7 +1998,7 @@ class BookmarkletImportViewSet(LoggingMixin, viewsets.ModelViewSet):
return self.queryset.filter(space=self.request.space).all()
class UserFileViewSet(LoggingMixin, StandardFilterModelViewSet):
class UserFileViewSet(LoggingMixin, StandardFilterModelViewSet, DeleteRelationMixing):
queryset = UserFile.objects
serializer_class = UserFileSerializer
permission_classes = [CustomIsUser & CustomTokenHasReadWriteScope]

View File

@@ -46,8 +46,8 @@ let routes = [
{path: '/view/recipe/:id', redirect: {name: 'RecipeViewPage'}}, // old Tandoor v1 url pattern
{path: '/list/:model?', component: () => import("@/pages/ModelListPage.vue"), props: true, name: 'ModelListPage'},
{path: '/edit/:model/:id?', component: () => import("@/pages/ModelEditPage.vue"), props: true, name: 'ModelEditPage'},
{path: '/delete/:model/:id?', component: () => import("@/pages/ModelDeletePage.vue"), props: true, name: 'ModelDeletePage'},
{path: '/edit/:model/:id?', component: () => import("@/pages/ModelEditPage.vue"), props: true, name: 'ModelEditPage', meta: {title: 'Edit'}},
{path: '/delete/:model/:id?', component: () => import("@/pages/ModelDeletePage.vue"), props: true, name: 'ModelDeletePage', meta: {title: 'Delete'}},
{path: '/database', component: () => import("@/pages/DatabasePage.vue"), props: true, name: 'DatabasePage', meta: {title: 'Database'}},
{path: '/ingredient-editor', component: () => import("@/pages/IngredientEditorPage.vue"), name: 'IngredientEditorPage', meta: {title: 'Ingredient Editor'}},

View File

@@ -32,6 +32,8 @@
"BatchDeleteHelp": "",
"BatchEdit": "",
"BatchEditUpdatingItemsCount": "",
"Blocking": "",
"BlockingHelp": "",
"Bookmarklet": "",
"Books": "",
"CREATE_ERROR": "",
@@ -39,10 +41,13 @@
"Cancel": "",
"Cannot_Add_Notes_To_Shopping": "",
"Carbohydrates": "",
"Cascading": "",
"CascadingHelp": "",
"Categories": "",
"Category": "",
"CategoryInstruction": "",
"CategoryName": "",
"Changing": "",
"ChildInheritFields": "",
"ChildInheritFields_help": "",
"Clear": "",
@@ -216,6 +221,7 @@
"No_Results": "",
"NotInShopping": "",
"Note": "",
"NullingHelp": "",
"Nutrition": "",
"NutritionsPerServing": "",
"NutritionsPerServingHelp": "",
@@ -261,6 +267,7 @@
"Recipes": "",
"Recipes_In_Import": "",
"Recipes_per_page": "",
"Refresh": "",
"RemoveAllType": "",
"RemoveFoodFromShopping": "",
"RemoveParent": "",

View File

@@ -32,6 +32,8 @@
"BatchDeleteHelp": "",
"BatchEdit": "",
"BatchEditUpdatingItemsCount": "",
"Blocking": "",
"BlockingHelp": "",
"Bookmarklet": "Книжен пазар",
"Books": "Книги",
"CREATE_ERROR": "",
@@ -39,10 +41,13 @@
"Cancel": "Откажи",
"Cannot_Add_Notes_To_Shopping": "Бележки не могат да се добавят към списъка за пазаруване",
"Carbohydrates": "Въглехидрати",
"Cascading": "",
"CascadingHelp": "",
"Categories": "Категории",
"Category": "Категория",
"CategoryInstruction": "Плъзнете категориите, за да промените категориите за поръчки, които се появяват в списъка за пазаруване.",
"CategoryName": "Име на категория",
"Changing": "",
"ChildInheritFields": "Последователи наследяват полета",
"ChildInheritFields_help": "Последователите ще наследят тези полета по подразбиране.",
"Clear": "Изчистване",
@@ -209,6 +214,7 @@
"No_Results": "Няма резултати",
"NotInShopping": "{food} не е в списъка ви за пазаруване.",
"Note": "Бележка",
"NullingHelp": "",
"Nutrition": "Хранителни стойности",
"NutritionsPerServing": "",
"NutritionsPerServingHelp": "",
@@ -254,6 +260,7 @@
"Recipes": "Рецепти",
"Recipes_In_Import": "Рецепти във вашия файл за импортиране",
"Recipes_per_page": "Рецепти на страница",
"Refresh": "",
"RemoveAllType": "",
"RemoveFoodFromShopping": "Премахнете {food} от списъка си за пазаруване",
"RemoveParent": "",

View File

@@ -39,6 +39,8 @@
"BatchDeleteHelp": "",
"BatchEdit": "",
"BatchEditUpdatingItemsCount": "",
"Blocking": "",
"BlockingHelp": "",
"Bookmarklet": "Marcadors",
"Books": "Llibres",
"CREATE_ERROR": "",
@@ -47,11 +49,14 @@
"Cancel": "Cancelar",
"Cannot_Add_Notes_To_Shopping": "Les notes no poden afegir-se a la llista de la compra",
"Carbohydrates": "Carbohidrats",
"Cascading": "",
"CascadingHelp": "",
"Categories": "Categories",
"Category": "Categoria",
"CategoryInstruction": "Arrossega les categories per canviar l'ordre que apareixen les categories a la llista de compres.",
"CategoryName": "Nom Categoria",
"Change_Password": "Canviar contrasenya",
"Changing": "",
"ChildInheritFields": "Camps Heretats dels Fills",
"ChildInheritFields_help": "Els fills heretaran aquests camps per defecte.",
"Choose_Category": "Escull Categoria",
@@ -274,6 +279,7 @@
"No_Results": "No hi ha resultats",
"NotInShopping": "{food} no està a la teva llista de la compra.",
"Note": "Nota",
"NullingHelp": "",
"Number of Objects": "Nombre d'Objectes",
"Nutrition": "Valors nutricionals",
"NutritionsPerServing": "",
@@ -331,6 +337,7 @@
"Recipes": "Receptes",
"Recipes_In_Import": "Receptes al fitxer d'importació",
"Recipes_per_page": "Receptes per pàgina",
"Refresh": "",
"RemoveAllType": "",
"RemoveFoodFromShopping": "Elimina {food} de la llista de la compra",
"RemoveParent": "",

View File

@@ -39,6 +39,8 @@
"BatchDeleteHelp": "",
"BatchEdit": "",
"BatchEditUpdatingItemsCount": "",
"Blocking": "",
"BlockingHelp": "",
"Bookmarklet": "Skript v záložce",
"Books": "Kuchařky",
"CREATE_ERROR": "",
@@ -47,11 +49,14 @@
"Cancel": "Zrušit",
"Cannot_Add_Notes_To_Shopping": "Poznámky nemohou být přidány na nákupní seznam",
"Carbohydrates": "Sacharidy",
"Cascading": "",
"CascadingHelp": "",
"Categories": "Kategorie",
"Category": "Kategorie",
"CategoryInstruction": "Přetáhnutím kategorií změníte pořadí, ve kterém se zobrazují v nákupním seznamu.",
"CategoryName": "Název kategorie",
"Change_Password": "Změna hesla",
"Changing": "",
"ChildInheritFields": "Propisovaná pole podřízených",
"ChildInheritFields_help": "Podřízeným se budou standardně propisovat tato pole.",
"Choose_Category": "Vyberte kategorii",
@@ -271,6 +276,7 @@
"No_Results": "Žádné výsledky",
"NotInShopping": "{food} není na vašem nákupním seznamu.",
"Note": "Poznámka",
"NullingHelp": "",
"Number of Objects": "Počet Objektů",
"Nutrition": "Výživové hodnoty",
"NutritionsPerServing": "",
@@ -328,6 +334,7 @@
"Recipes": "Recepty",
"Recipes_In_Import": "Receptů v importním souboru",
"Recipes_per_page": "Receptů na stránku",
"Refresh": "",
"RemoveAllType": "",
"RemoveFoodFromShopping": "Odstranit {food} z nákupního seznamu",
"RemoveParent": "",

View File

@@ -39,6 +39,8 @@
"BatchDeleteHelp": "",
"BatchEdit": "",
"BatchEditUpdatingItemsCount": "",
"Blocking": "",
"BlockingHelp": "",
"Bookmarklet": "Bogmærke",
"Books": "Bøger",
"CREATE_ERROR": "",
@@ -47,11 +49,14 @@
"Cancel": "Annuller",
"Cannot_Add_Notes_To_Shopping": "Noter kan ikke tilføjes til indkøbslisten",
"Carbohydrates": "Kulhydrater",
"Cascading": "",
"CascadingHelp": "",
"Categories": "Kategorier",
"Category": "Kategori",
"CategoryInstruction": "Træk rundt på kategorier, for at ændre på rækkefølgen de opstår i på indkøbslisten.",
"CategoryName": "Kategorinavn",
"Change_Password": "Skift kodeord",
"Changing": "",
"ChildInheritFields": "Barn nedarvningsfelter",
"ChildInheritFields_help": "Børn nedarvede disse felter som standard.",
"Choose_Category": "Vælg kategori",
@@ -274,6 +279,7 @@
"No_Results": "Ingen resultater",
"NotInShopping": "{food} er ikke i din indkøbsliste.",
"Note": "Note",
"NullingHelp": "",
"Number of Objects": "Antal objekter",
"Nutrition": "Næring",
"NutritionsPerServing": "",
@@ -331,6 +337,7 @@
"Recipes": "Opskrifter",
"Recipes_In_Import": "Opskrifter i din importerede fil",
"Recipes_per_page": "Opskrifter pr. side",
"Refresh": "",
"RemoveAllType": "",
"RemoveFoodFromShopping": "Fjern {food} fra indkøbsliste",
"RemoveParent": "",

View File

@@ -60,6 +60,8 @@
"BatchDeleteHelp": "Wenn ein Objekt nicht gelöscht werden kann, wird es noch irgendwo verwendet. ",
"BatchEdit": "Massenbearbeitung",
"BatchEditUpdatingItemsCount": "Bearbeite {count} {type}",
"Blocking": "Blockierend",
"BlockingHelp": "Die folgenden Objekte verhindern das löschen der ausgewählten {type}",
"Book": "Buch",
"Bookmarklet": "Lesezeichen",
"BookmarkletHelp1": "Schiebe den Knopf in deine Lesezeichenleiste",
@@ -74,11 +76,14 @@
"Cannot_Add_Notes_To_Shopping": "Notizen können nicht auf die Einkaufsliste gesetzt werden",
"Carbohydrates": "Kohlenhydrate",
"Cards": "Karten",
"Cascading": "Kaskadierend",
"CascadingHelp": "Die folgenden Objekte werden gelöscht wenn die ausgewählte {type} gelöscht wird.",
"Categories": "Kategorien",
"Category": "Kategorie",
"CategoryInstruction": "Ziehen Sie Kategorien, um die Reihenfolge zu ändern, in der die Kategorien in der Einkaufsliste erscheinen.",
"CategoryName": "Kategorienname",
"Change_Password": "Kennwort ändern",
"Changing": "Ändern",
"ChildInheritFields": "Kindelemente erben Felder",
"ChildInheritFields_help": "Kindelemente erben diese Felder standardmäßig.",
"Choose_Category": "Kategorie Auswählen",
@@ -369,6 +374,7 @@
"NotFoundHelp": "Die gesuchte Seite konnte nicht gefunden werden.",
"NotInShopping": "{food} befindet sich nicht auf Ihrer Einkaufsliste.",
"Note": "Notiz",
"NullingHelp": "Die ausgewählte {type} wird aus den folgenden Objekten entfernt wenn Sie gelöscht wird. ",
"Number of Objects": "Anzahl von Objekten",
"Nutrition": "Nährwerte",
"NutritionsPerServing": "Nährwerte pro Portion",
@@ -448,6 +454,7 @@
"Recipes": "Rezepte",
"Recipes_In_Import": "Rezepte in deiner importierten Datei",
"Recipes_per_page": "Rezepte pro Seite",
"Refresh": "Aktualisieren",
"Remove": "Entfernen",
"RemoveAllType": "Alle {type} entfernen",
"RemoveFoodFromShopping": "{food} von der Einkaufsliste löschen",

View File

@@ -39,6 +39,8 @@
"BatchDeleteHelp": "",
"BatchEdit": "",
"BatchEditUpdatingItemsCount": "",
"Blocking": "",
"BlockingHelp": "",
"Bookmarklet": "Bookmarklet",
"Books": "Βιβλία",
"CREATE_ERROR": "",
@@ -47,11 +49,14 @@
"Cancel": "Ακύρωση",
"Cannot_Add_Notes_To_Shopping": "Δεν είναι δυνατή η προσθήκη σημειώσεων στη λίστα αγορών",
"Carbohydrates": "Υδατάνθρακες",
"Cascading": "",
"CascadingHelp": "",
"Categories": "Κατηγορίες",
"Category": "Κατηγορία",
"CategoryInstruction": "Σύρετε κατηγορίες για να αλλάξετε τη σειρά με την οποία εμφανίζονται στη λίστα αγορών.",
"CategoryName": "Όνομα κατηγορίας",
"Change_Password": "Αλλαγή κωδικού πρόσβασης",
"Changing": "",
"ChildInheritFields": "Τα παιδιά κληρονομούν τα πεδία",
"ChildInheritFields_help": "Τα παιδιά θα κληρονομούν αυτά τα πεδία από προεπιλογή.",
"Choose_Category": "Επιλογή κατηγορίας",
@@ -274,6 +279,7 @@
"No_Results": "Δεν υπάρχουν αποτελέσματα",
"NotInShopping": "Το φαγητό { food} δεν είναι στη λίστα αγορών σας.",
"Note": "Σημείωση",
"NullingHelp": "",
"Number of Objects": "Αριθμός αντικειμένων",
"Nutrition": "Διατροφική αξία",
"NutritionsPerServing": "",
@@ -331,6 +337,7 @@
"Recipes": "Συνταγές",
"Recipes_In_Import": "Συνταγές στο αρχείο εισαγωγής",
"Recipes_per_page": "Συνταγές ανά σελίδα",
"Refresh": "",
"RemoveAllType": "",
"RemoveFoodFromShopping": "Αφαίρεση του φαγητού {food} από τη λίστα αγορών σας",
"RemoveParent": "",

View File

@@ -58,6 +58,8 @@
"BatchDeleteHelp": "If an item cannot be deleted it is used somewhere. ",
"BatchEdit": "Batch Edit",
"BatchEditUpdatingItemsCount": "Editing {count} {type}",
"Blocking": "Blocking",
"BlockingHelp": "The following objects are preventing you from deleting the selected {type}.",
"Book": "Book",
"Bookmarklet": "Bookmarklet",
"BookmarkletHelp1": "Drag the following button to your bookmarks bar",
@@ -72,11 +74,14 @@
"Cannot_Add_Notes_To_Shopping": "Notes cannot be added to the shopping list",
"Carbohydrates": "Carbohydrates",
"Cards": "Cards",
"Cascading": "Cascading",
"CascadingHelp": "The following objects will be deleted when you delete the selected {type}",
"Categories": "Categories",
"Category": "Category",
"CategoryInstruction": "Drag categories to change the order categories appear in shopping list.",
"CategoryName": "Category Name",
"Change_Password": "Change Password",
"Changing": "Changing",
"ChildInheritFields": "Children Inherit Fields",
"ChildInheritFields_help": "Children will inherit these fields by default.",
"Choose_Category": "Choose Category",
@@ -367,6 +372,7 @@
"NotFoundHelp": "The page or object you are looking for could not be found.",
"NotInShopping": "{food} is not in your shopping list.",
"Note": "Note",
"NullingHelp": "The selected {type} will be removed from the following objects when it is deleted.",
"Number of Objects": "Number of Objects",
"Nutrition": "Nutrition",
"NutritionsPerServing": "Nutritions per Serving",
@@ -446,6 +452,7 @@
"Recipes": "Recipes",
"Recipes_In_Import": "Recipes in your import file",
"Recipes_per_page": "Recipes per Page",
"Refresh": "Refresh",
"Remove": "Remove",
"RemoveAllType": "Remove all {type}",
"RemoveFoodFromShopping": "Remove {food} from your shopping list",

View File

@@ -56,6 +56,8 @@
"BatchDeleteHelp": "",
"BatchEdit": "",
"BatchEditUpdatingItemsCount": "",
"Blocking": "",
"BlockingHelp": "",
"Book": "Libro",
"Bookmarklet": "Marcador ejecutable",
"BookmarkletHelp1": "Arrastra el siguiente botón a tu barra de marcadores",
@@ -70,11 +72,14 @@
"Cannot_Add_Notes_To_Shopping": "Las notas no pueden añadirse a la lista de compras",
"Carbohydrates": "Carbohidratos",
"Cards": "Tarjetas",
"Cascading": "",
"CascadingHelp": "",
"Categories": "Categorías",
"Category": "Categoría",
"CategoryInstruction": "Arrastra las categorías para cambiar el orden en que aparecen en la lista de compras.",
"CategoryName": "Nombre de la categoría",
"Change_Password": "Cambiar contraseña",
"Changing": "",
"ChildInheritFields": "Los hijos heredan campos",
"ChildInheritFields_help": "Los elementos hijos heredarán estos campos de forma predeterminada.",
"Choose_Category": "Escoger categoría",
@@ -357,6 +362,7 @@
"NotFoundHelp": "La página o el objeto que buscas no pudo ser encontrado.",
"NotInShopping": "{food} no esta en tu lista de la compra.",
"Note": "Nota",
"NullingHelp": "",
"Number of Objects": "Número de Objetos",
"Nutrition": "Nutrición",
"NutritionsPerServing": "",
@@ -433,6 +439,7 @@
"Recipes": "Recetas",
"Recipes_In_Import": "Recetas en tu fichero de importación",
"Recipes_per_page": "Recetas por página",
"Refresh": "",
"Remove": "Remover",
"RemoveAllType": "",
"RemoveFoodFromShopping": "Eliminar {food} de la lista de la compra",

View File

@@ -40,6 +40,8 @@
"BatchDeleteHelp": "",
"BatchEdit": "",
"BatchEditUpdatingItemsCount": "",
"Blocking": "",
"BlockingHelp": "",
"Bookmarklet": "Kirjamerkki",
"Books": "Kirjat",
"CREATE_ERROR": "",
@@ -48,11 +50,14 @@
"Cancel": "Peruuta",
"Cannot_Add_Notes_To_Shopping": "Lisätietoja ei voida lisätä ostoslistaan",
"Carbohydrates": "Hiilihydraatit",
"Cascading": "",
"CascadingHelp": "",
"Categories": "Luokat",
"Category": "Luokka",
"CategoryInstruction": "Vedä luokkia muuttaaksesi luokkien järjestystä, jotka näkyvät ostoslistassa.",
"CategoryName": "Kategorian Nimi",
"Change_Password": "Vaihda Salasana",
"Changing": "",
"Choose_Category": "Valitse Kategoria",
"Clear": "Pyyhi",
"Click_To_Edit": "Muokkaa napsauttamalla",
@@ -263,6 +268,7 @@
"No_Results": "Ei Tuloksia",
"NotInShopping": "{food} ei ole ostoslistalla.",
"Note": "Lisätiedot",
"NullingHelp": "",
"Number of Objects": "Objektien määrä",
"Nutrition": "Ravitsemus",
"NutritionsPerServing": "",
@@ -320,6 +326,7 @@
"Recipes": "Reseptit",
"Recipes_In_Import": "Reseptit tuonti tiedostossasi",
"Recipes_per_page": "Reseptejä sivulla",
"Refresh": "",
"RemoveAllType": "",
"RemoveFoodFromShopping": "Poista {food} ostoslistalta",
"RemoveParent": "",

View File

@@ -58,6 +58,8 @@
"BatchDeleteHelp": "",
"BatchEdit": "",
"BatchEditUpdatingItemsCount": "",
"Blocking": "",
"BlockingHelp": "",
"Book": "Livre",
"Bookmarklet": "Signet",
"BookmarkletHelp1": "Faites glisser le bouton suivant dans votre barre de signets",
@@ -72,11 +74,14 @@
"Cannot_Add_Notes_To_Shopping": "Les notes ne peuvent pas être ajoutées à la liste de courses",
"Carbohydrates": "Glucides",
"Cards": "Cartes",
"Cascading": "",
"CascadingHelp": "",
"Categories": "Catégories",
"Category": "Catégorie",
"CategoryInstruction": "Faites glisser les catégories pour modifier l'ordre dans lequel elles apparaissent dans la liste des courses.",
"CategoryName": "Intitulé de la catégorie",
"Change_Password": "Modifier le mot de passe",
"Changing": "",
"ChildInheritFields": "Les enfants héritent des champs",
"ChildInheritFields_help": "Les enfants hériteront de ces champs par défaut.",
"Choose_Category": "Choisir une catégorie",
@@ -364,6 +369,7 @@
"NotFoundHelp": "La page ou l'objet que vous recherchez n'a pas pu être trouvé.",
"NotInShopping": "Laliment {food} nest pas dans votre liste de courses.",
"Note": "Notes",
"NullingHelp": "",
"Number of Objects": "Nombre d'objets",
"Nutrition": "Valeurs nutritionnelles",
"NutritionsPerServing": "",
@@ -443,6 +449,7 @@
"Recipes": "Recettes",
"Recipes_In_Import": "Recettes dans votre fichier dimportation",
"Recipes_per_page": "Nombre de recettes par page",
"Refresh": "",
"Remove": "Enlever",
"RemoveAllType": "",
"RemoveFoodFromShopping": "Supprimer laliment {food} de votre liste de courses",

View File

@@ -39,6 +39,8 @@
"BatchDeleteHelp": "",
"BatchEdit": "",
"BatchEditUpdatingItemsCount": "",
"Blocking": "",
"BlockingHelp": "",
"Bookmarklet": "סימניה",
"Books": "ספרים",
"CREATE_ERROR": "",
@@ -47,11 +49,14 @@
"Cancel": "ביטול",
"Cannot_Add_Notes_To_Shopping": "לא ניתן להוסיף הערות לרשימת הקניות",
"Carbohydrates": "פחמימות",
"Cascading": "",
"CascadingHelp": "",
"Categories": "קטגוריות",
"Category": "קטגוריה",
"CategoryInstruction": "גרור קטגוריות לשינוי הסדר שבו הן מופיעות ברשימת הקניות.",
"CategoryName": "שם קטגוריה",
"Change_Password": "החלפת סיסמא",
"Changing": "",
"ChildInheritFields": "שדות ילדים ירושה.",
"ChildInheritFields_help": "ילדים ירשו את השדות האלו כברירת מחדל.",
"Choose_Category": "בחר קטגוריה",
@@ -274,6 +279,7 @@
"No_Results": "אין תוצאות",
"NotInShopping": "{food} אינו רשימת הקניות.",
"Note": "הערה",
"NullingHelp": "",
"Number of Objects": "מספר אובייקטים",
"Nutrition": "תזונה",
"NutritionsPerServing": "",
@@ -331,6 +337,7 @@
"Recipes": "מתכונים",
"Recipes_In_Import": "מתכון בקובץ הייבוא",
"Recipes_per_page": "מתכונים בכל דף",
"Refresh": "",
"RemoveAllType": "",
"RemoveFoodFromShopping": "הסר {מזון} מרשימת הקניות",
"RemoveParent": "",

View File

@@ -39,6 +39,8 @@
"BatchDeleteHelp": "",
"BatchEdit": "",
"BatchEditUpdatingItemsCount": "",
"Blocking": "",
"BlockingHelp": "",
"Bookmarklet": "Knjižna oznaka",
"Books": "Knjige",
"CREATE_ERROR": "",
@@ -47,11 +49,14 @@
"Cancel": "Otkaži",
"Cannot_Add_Notes_To_Shopping": "Bilješke se ne mogu dodati na popis za kupovinu",
"Carbohydrates": "Ugljikohidrati",
"Cascading": "",
"CascadingHelp": "",
"Categories": "Kategorije",
"Category": "Kategorija",
"CategoryInstruction": "Povuci kategorije kako bi promijenio redoslijed kategorijea narudžbi koje se pojavljuju na popisu za kupnju.",
"CategoryName": "Naziv kategorije",
"Change_Password": "Promjena lozinke",
"Changing": "",
"ChildInheritFields": "Djeca nasljeđuju polja",
"ChildInheritFields_help": "Djeca će prema zadanim postavkama naslijediti ova polja.",
"Choose_Category": "Odaberi kategoriju",
@@ -274,6 +279,7 @@
"No_Results": "Nema rezultata",
"NotInShopping": "{food} nije na vašem popisu za kupovinu.",
"Note": "Bilješka",
"NullingHelp": "",
"Number of Objects": "Broj objekata",
"Nutrition": "Nutritivna vrijednost",
"NutritionsPerServing": "",
@@ -331,6 +337,7 @@
"Recipes": "Recepti",
"Recipes_In_Import": "Recepti u vašoj datoteci za uvoz",
"Recipes_per_page": "Recepata po stranici",
"Refresh": "",
"RemoveAllType": "",
"RemoveFoodFromShopping": "Ukloni {food} sa svog popisa za kupovinu",
"RemoveParent": "",

View File

@@ -39,6 +39,8 @@
"BatchDeleteHelp": "",
"BatchEdit": "",
"BatchEditUpdatingItemsCount": "",
"Blocking": "",
"BlockingHelp": "",
"Bookmarklet": "Könyvjelző",
"Books": "Könyvek",
"CREATE_ERROR": "",
@@ -46,11 +48,14 @@
"Cancel": "Mégsem",
"Cannot_Add_Notes_To_Shopping": "A bevásárlólistához nem adható hozzá megjegyzés",
"Carbohydrates": "Szénhidrátok",
"Cascading": "",
"CascadingHelp": "",
"Categories": "Kategóriák",
"Category": "Kategória",
"CategoryInstruction": "A kategóriákat mozgatva megváltoztathatja a kategóriák sorrendjét a bevásárlólistán.",
"CategoryName": "Kategória neve",
"Change_Password": "Jelszó módosítása",
"Changing": "",
"ChildInheritFields": "",
"ChildInheritFields_help": "",
"Choose_Category": "Kategória kiválasztása",
@@ -251,6 +256,7 @@
"No_Results": "Nincsenek találatok",
"NotInShopping": "{food} nincs a bevásárlólistáján.",
"Note": "Megjegyzés",
"NullingHelp": "",
"Number of Objects": "Objektumok száma",
"Nutrition": "Tápérték",
"NutritionsPerServing": "",
@@ -304,6 +310,7 @@
"Recipes": "Receptek",
"Recipes_In_Import": "",
"Recipes_per_page": "Receptek oldalanként",
"Refresh": "",
"RemoveAllType": "",
"RemoveFoodFromShopping": "{food} eltávolítása bevásárlólistáról",
"RemoveParent": "",

View File

@@ -22,13 +22,18 @@
"BatchDeleteHelp": "",
"BatchEdit": "",
"BatchEditUpdatingItemsCount": "",
"Blocking": "",
"BlockingHelp": "",
"Books": "",
"CREATE_ERROR": "",
"Calories": "",
"Cancel": "",
"Carbohydrates": "",
"Cascading": "",
"CascadingHelp": "",
"Categories": "",
"Category": "",
"Changing": "",
"Close": "",
"ConvertUsingAI": "",
"Copy": "",
@@ -114,6 +119,7 @@
"No": "",
"NoUnit": "",
"No_Results": "Արդյունքներ չկան",
"NullingHelp": "",
"Nutrition": "",
"NutritionsPerServing": "",
"NutritionsPerServingHelp": "",
@@ -136,6 +142,7 @@
"Recipe_Image": "Բաղադրատոմսի նկար",
"Recipes": "Բաղադրատոմսեր",
"Recipes_per_page": "Բաղադրատոմս էջում",
"Refresh": "",
"RemoveAllType": "",
"RemoveParent": "",
"Remove_nutrition_recipe": "Հեռացնել բաղադրատոմսի սննդայնությունը",

View File

@@ -34,6 +34,8 @@
"BatchDeleteHelp": "",
"BatchEdit": "",
"BatchEditUpdatingItemsCount": "",
"Blocking": "",
"BlockingHelp": "",
"Bookmarklet": "",
"Books": "Buku",
"CREATE_ERROR": "",
@@ -41,11 +43,14 @@
"Cancel": "Batal",
"Cannot_Add_Notes_To_Shopping": "",
"Carbohydrates": "Karbohidrat",
"Cascading": "",
"CascadingHelp": "",
"Categories": "Kategori",
"Category": "Kategori",
"CategoryInstruction": "",
"CategoryName": "",
"Change_Password": "",
"Changing": "",
"ChildInheritFields": "",
"ChildInheritFields_help": "",
"Clear": "",
@@ -235,6 +240,7 @@
"No_Results": "",
"NotInShopping": "",
"Note": "Catatan",
"NullingHelp": "",
"Nutrition": "Nutrisi",
"NutritionsPerServing": "",
"NutritionsPerServingHelp": "",
@@ -280,6 +286,7 @@
"Recipes": "Resep",
"Recipes_In_Import": "",
"Recipes_per_page": "Resep per Halaman",
"Refresh": "",
"RemoveAllType": "",
"RemoveFoodFromShopping": "",
"RemoveParent": "",

View File

@@ -39,6 +39,8 @@
"BatchDeleteHelp": "",
"BatchEdit": "",
"BatchEditUpdatingItemsCount": "",
"Blocking": "",
"BlockingHelp": "",
"Bookmarklet": "",
"Books": "",
"CREATE_ERROR": "",
@@ -47,11 +49,14 @@
"Cancel": "",
"Cannot_Add_Notes_To_Shopping": "",
"Carbohydrates": "",
"Cascading": "",
"CascadingHelp": "",
"Categories": "",
"Category": "",
"CategoryInstruction": "",
"CategoryName": "",
"Change_Password": "",
"Changing": "",
"ChildInheritFields": "",
"ChildInheritFields_help": "",
"Choose_Category": "",
@@ -273,6 +278,7 @@
"No_Results": "",
"NotInShopping": "",
"Note": "",
"NullingHelp": "",
"Number of Objects": "",
"Nutrition": "",
"NutritionsPerServing": "",
@@ -330,6 +336,7 @@
"Recipes": "",
"Recipes_In_Import": "",
"Recipes_per_page": "",
"Refresh": "",
"RemoveAllType": "",
"RemoveFoodFromShopping": "",
"RemoveParent": "",

View File

@@ -58,6 +58,8 @@
"BatchDeleteHelp": "",
"BatchEdit": "",
"BatchEditUpdatingItemsCount": "",
"Blocking": "",
"BlockingHelp": "",
"Book": "Libro",
"Bookmarklet": "Segnalibro",
"BookmarkletHelp1": "Trascina il pulsante seguente nella barra dei tuoi segnalibri",
@@ -72,11 +74,14 @@
"Cannot_Add_Notes_To_Shopping": "Le note non possono essere aggiunte alla lista della spesa",
"Carbohydrates": "Carboidrati",
"Cards": "Schede",
"Cascading": "",
"CascadingHelp": "",
"Categories": "Categorie",
"Category": "Categoria",
"CategoryInstruction": "Trascina le categorie per cambiare l'ordine in cui appaiono nella lista della spesa.",
"CategoryName": "Nome categoria",
"Change_Password": "Cambia password",
"Changing": "",
"ChildInheritFields": "Figli ereditano i campi",
"ChildInheritFields_help": "In modo predefinito, i figli erediteranno questi campi.",
"Choose_Category": "Scegli categoria",
@@ -366,6 +371,7 @@
"NotFoundHelp": "La pagina o l'oggetto che stai cercando non è stato trovato.",
"NotInShopping": "{food} non è nella tua lista della spesa.",
"Note": "Nota",
"NullingHelp": "",
"Number of Objects": "Numero di oggetti",
"Nutrition": "Nutrienti",
"NutritionsPerServing": "",
@@ -445,6 +451,7 @@
"Recipes": "Ricette",
"Recipes_In_Import": "Ricette nel tuo file di importazione",
"Recipes_per_page": "Ricette per pagina",
"Refresh": "",
"Remove": "Rimuovi",
"RemoveAllType": "",
"RemoveFoodFromShopping": "Rimuovi {food} dalla tua lista della spesa",

View File

@@ -39,6 +39,8 @@
"BatchDeleteHelp": "",
"BatchEdit": "",
"BatchEditUpdatingItemsCount": "",
"Blocking": "",
"BlockingHelp": "",
"Bookmarklet": "",
"Books": "",
"CREATE_ERROR": "",
@@ -46,11 +48,14 @@
"Cancel": "",
"Cannot_Add_Notes_To_Shopping": "",
"Carbohydrates": "",
"Cascading": "",
"CascadingHelp": "",
"Categories": "",
"Category": "",
"CategoryInstruction": "",
"CategoryName": "",
"Change_Password": "",
"Changing": "",
"ChildInheritFields": "",
"ChildInheritFields_help": "",
"Choose_Category": "",
@@ -254,6 +259,7 @@
"No_Results": "",
"NotInShopping": "",
"Note": "",
"NullingHelp": "",
"Number of Objects": "",
"Nutrition": "",
"NutritionsPerServing": "",
@@ -308,6 +314,7 @@
"Recipes": "",
"Recipes_In_Import": "",
"Recipes_per_page": "Receptų skaičius per puslapį",
"Refresh": "",
"RemoveAllType": "",
"RemoveFoodFromShopping": "",
"RemoveParent": "",

View File

@@ -39,6 +39,8 @@
"BatchDeleteHelp": "",
"BatchEdit": "",
"BatchEditUpdatingItemsCount": "",
"Blocking": "",
"BlockingHelp": "",
"Bookmarklet": "",
"Books": "",
"CREATE_ERROR": "",
@@ -47,11 +49,14 @@
"Cancel": "",
"Cannot_Add_Notes_To_Shopping": "",
"Carbohydrates": "",
"Cascading": "",
"CascadingHelp": "",
"Categories": "",
"Category": "",
"CategoryInstruction": "",
"CategoryName": "",
"Change_Password": "",
"Changing": "",
"ChildInheritFields": "",
"ChildInheritFields_help": "",
"Choose_Category": "",
@@ -274,6 +279,7 @@
"No_Results": "",
"NotInShopping": "",
"Note": "",
"NullingHelp": "",
"Number of Objects": "",
"Nutrition": "",
"NutritionsPerServing": "",
@@ -331,6 +337,7 @@
"Recipes": "",
"Recipes_In_Import": "",
"Recipes_per_page": "",
"Refresh": "",
"RemoveAllType": "",
"RemoveFoodFromShopping": "",
"RemoveParent": "",

View File

@@ -38,6 +38,8 @@
"BatchDeleteHelp": "",
"BatchEdit": "",
"BatchEditUpdatingItemsCount": "",
"Blocking": "",
"BlockingHelp": "",
"Bookmarklet": "",
"Books": "Bøker",
"CREATE_ERROR": "",
@@ -46,11 +48,14 @@
"Cancel": "Avbryt",
"Cannot_Add_Notes_To_Shopping": "Notater kan ikke legges til i handlelisten",
"Carbohydrates": "Karbohydrater",
"Cascading": "",
"CascadingHelp": "",
"Categories": "Kategorier",
"Category": "Kategori",
"CategoryInstruction": "Dra kategorier for å endre på rekkefølgen de vises i handlelisten.",
"CategoryName": "Kategori navn",
"Change_Password": "Endre passord",
"Changing": "",
"ChildInheritFields": "",
"ChildInheritFields_help": "",
"Clear": "Fjern",
@@ -260,6 +265,7 @@
"No_Results": "Ingen resultat",
"NotInShopping": "{food} er ikke i handlelisten din.",
"Note": "Merk",
"NullingHelp": "",
"Number of Objects": "Antall objekter",
"Nutrition": "Næringsinnhold",
"NutritionsPerServing": "",
@@ -315,6 +321,7 @@
"Recipes": "Oppskrift",
"Recipes_In_Import": "",
"Recipes_per_page": "Oppskrifter per side",
"Refresh": "",
"RemoveAllType": "",
"RemoveFoodFromShopping": "Fjern {food} fra handelisten din",
"RemoveParent": "",

View File

@@ -59,6 +59,8 @@
"BatchDeleteHelp": "",
"BatchEdit": "",
"BatchEditUpdatingItemsCount": "",
"Blocking": "",
"BlockingHelp": "",
"Book": "Boek",
"Bookmarklet": "Bladwijzer",
"BookmarkletHelp1": "Sleep de onderstaande knop naar je bladwijzerbalk",
@@ -73,11 +75,14 @@
"Cannot_Add_Notes_To_Shopping": "Notities kunnen niet aan de boodschappenlijst toegevoegd worden",
"Carbohydrates": "Koolhydraten",
"Cards": "Kaarten",
"Cascading": "",
"CascadingHelp": "",
"Categories": "Categorieën",
"Category": "Categorie",
"CategoryInstruction": "Versleep categorieën om de volgorde waarin ze in de boodschappenlijst getoond worden aan te passen.",
"CategoryName": "Categorienaam",
"Change_Password": "Wachtwoord veranderen",
"Changing": "",
"ChildInheritFields": "Afgeleiden Erven Velden",
"ChildInheritFields_help": "Afgeleiden zullen deze velden standaard overnemen.",
"Choose_Category": "Kies categorie",
@@ -367,6 +372,7 @@
"NotFoundHelp": "De pagina of het object dat je zoekt, is niet gevonden.",
"NotInShopping": "{food} staat niet op je boodschappenlijst.",
"Note": "Notitie",
"NullingHelp": "",
"Number of Objects": "Aantal objecten",
"Nutrition": "Voedingswaarde",
"NutritionsPerServing": "",
@@ -446,6 +452,7 @@
"Recipes": "Recepten",
"Recipes_In_Import": "Recepten in je importbestand",
"Recipes_per_page": "Recepten per pagina",
"Refresh": "",
"Remove": "Verwijder",
"RemoveAllType": "",
"RemoveFoodFromShopping": "Verwijder {food} van je boodschappenlijst",

View File

@@ -56,6 +56,8 @@
"BatchDeleteHelp": "",
"BatchEdit": "",
"BatchEditUpdatingItemsCount": "",
"Blocking": "",
"BlockingHelp": "",
"Book": "Książka",
"Bookmarklet": "Skryptozakładka",
"BookmarkletHelp1": "Przeciągnij następujący przycisk do twojego paska zakładek",
@@ -70,11 +72,14 @@
"Cannot_Add_Notes_To_Shopping": "Notatki nie mogą być dodawane do listy zakupów",
"Carbohydrates": "Węglowodany",
"Cards": "Karty",
"Cascading": "",
"CascadingHelp": "",
"Categories": "Kategorie",
"Category": "Kategorie",
"CategoryInstruction": "Przeciągnij kategorie, aby zmienić kolejność w jakiej kategorie pojawiają się na liście zakupów.",
"CategoryName": "Nazwa kategorii",
"Change_Password": "Zmień hasło",
"Changing": "",
"ChildInheritFields": "Potomne dziedziczą pola",
"ChildInheritFields_help": "Potomne domyślnie odziedziczą te pola.",
"Choose_Category": "Wybierz kategorię",
@@ -300,6 +305,7 @@
"No_Results": "Brak wyników",
"NotInShopping": "{food} nie ma na Twojej liście zakupów.",
"Note": "Notatka",
"NullingHelp": "",
"Number of Objects": "Ilość obiektów",
"Nutrition": "Odżywianie",
"NutritionsPerServing": "",
@@ -357,6 +363,7 @@
"Recipes": "Przepisy",
"Recipes_In_Import": "Przepisy w pliku importu",
"Recipes_per_page": "Przepisy na stronę",
"Refresh": "",
"RemoveAllType": "",
"RemoveFoodFromShopping": "Usuń {food} z listy zakupów",
"RemoveParent": "",

View File

@@ -34,6 +34,8 @@
"BatchDeleteHelp": "",
"BatchEdit": "",
"BatchEditUpdatingItemsCount": "",
"Blocking": "",
"BlockingHelp": "",
"Books": "Livros",
"CREATE_ERROR": "",
"Calculator": "Calculadora",
@@ -41,10 +43,13 @@
"Cancel": "Cancelar",
"Cannot_Add_Notes_To_Shopping": "Notas não podem ser adicionadas à lista de compras",
"Carbohydrates": "Carboidratos",
"Cascading": "",
"CascadingHelp": "",
"Categories": "Categorias",
"Category": "Categoria",
"CategoryInstruction": "",
"CategoryName": "",
"Changing": "",
"ChildInheritFields": "",
"ChildInheritFields_help": "",
"Clear": "",
@@ -220,6 +225,7 @@
"No_Results": "Sem resultados",
"NotInShopping": "{food} não está na sua lista de compras.",
"Note": "Nota",
"NullingHelp": "",
"Number of Objects": "Número de objetos",
"Nutrition": "Nutrição",
"NutritionsPerServing": "",
@@ -273,6 +279,7 @@
"Recipe_Image": "Imagem da Receita",
"Recipes": "Receitas",
"Recipes_per_page": "Receitas por página",
"Refresh": "",
"RemoveAllType": "",
"RemoveFoodFromShopping": "Remover {food} da sua lista de compras",
"RemoveParent": "",

View File

@@ -57,6 +57,8 @@
"BatchDeleteHelp": "",
"BatchEdit": "",
"BatchEditUpdatingItemsCount": "",
"Blocking": "",
"BlockingHelp": "",
"Book": "Livro",
"Bookmarklet": "Marcador",
"BookmarkletHelp1": "Arraste o seguinte botão para sua barra de favoritos",
@@ -71,11 +73,14 @@
"Cannot_Add_Notes_To_Shopping": "Notas não podem sem adicionadas na lista de compras",
"Carbohydrates": "Carboidratos",
"Cards": "Cartões",
"Cascading": "",
"CascadingHelp": "",
"Categories": "Categorias",
"Category": "Categoria",
"CategoryInstruction": "Arraste as categorias para alterar a ordem em que as categorias de pedidos aparecem na lista de compras.",
"CategoryName": "Nome da Categoria",
"Change_Password": "Alterar Senha",
"Changing": "",
"ChildInheritFields": "Campos de Filhos Herdados",
"ChildInheritFields_help": "Os filhos herdarão esses campos por padrão.",
"Choose_Category": "Selecionar Categoria",
@@ -354,6 +359,7 @@
"No_Results": "Sem Resultados",
"NotInShopping": "{food} não está na sua lista de compras.",
"Note": "Nota",
"NullingHelp": "",
"Number of Objects": "Número de Objetos",
"Nutrition": "Nutrição",
"NutritionsPerServing": "",
@@ -408,6 +414,7 @@
"Recipes": "Receitas",
"Recipes_In_Import": "Receitas no seu arquivo de importação",
"Recipes_per_page": "Receitas por página",
"Refresh": "",
"RemoveAllType": "",
"RemoveFoodFromShopping": "Remover {food} da sua lista de compras",
"RemoveParent": "",

View File

@@ -38,6 +38,8 @@
"BatchDeleteHelp": "",
"BatchEdit": "",
"BatchEditUpdatingItemsCount": "",
"Blocking": "",
"BlockingHelp": "",
"Bookmarklet": "Marcaj",
"Books": "Cărți",
"CREATE_ERROR": "",
@@ -45,11 +47,14 @@
"Cancel": "Anulează",
"Cannot_Add_Notes_To_Shopping": "Notele nu pot fi adăugate la lista de cumpărături",
"Carbohydrates": "Carbohidrați",
"Cascading": "",
"CascadingHelp": "",
"Categories": "Categorii",
"Category": "Categorie",
"CategoryInstruction": "Trageți categoriile pentru a schimba categoriile de comenzi care apar în lista de cumpărături.",
"CategoryName": "Nume categorie",
"Change_Password": "Schimbați parola",
"Changing": "",
"ChildInheritFields": "Copiii moștenesc câmpurile",
"ChildInheritFields_help": "Copiii vor moșteni aceste câmpuri în mod implicit.",
"Clear": "Curățare",
@@ -244,6 +249,7 @@
"No_Results": "Fără rezultate",
"NotInShopping": "{food} nu se află în lista de cumpărături.",
"Note": "Notă",
"NullingHelp": "",
"Nutrition": "Nutriție",
"NutritionsPerServing": "",
"NutritionsPerServingHelp": "",
@@ -292,6 +298,7 @@
"Recipes": "Rețete",
"Recipes_In_Import": "Rețete în fișierul de import",
"Recipes_per_page": "Rețete pe pagină",
"Refresh": "",
"RemoveAllType": "",
"RemoveFoodFromShopping": "Șterge {food} din lista de cumpărături",
"RemoveParent": "",

View File

@@ -58,6 +58,8 @@
"BatchDeleteHelp": "",
"BatchEdit": "",
"BatchEditUpdatingItemsCount": "",
"Blocking": "",
"BlockingHelp": "",
"Book": "Книга",
"Bookmarklet": "Букмарклет",
"BookmarkletHelp1": "Перетащите эту кнопку в панель закладок",
@@ -72,11 +74,14 @@
"Cannot_Add_Notes_To_Shopping": "Нельзя добавить записи в список покупок",
"Carbohydrates": "Углеводы",
"Cards": "Карточки",
"Cascading": "",
"CascadingHelp": "",
"Categories": "Категории",
"Category": "Категория",
"CategoryInstruction": "Перетаскивайте категории, чтобы изменить порядок отображения категорий в списке покупок.",
"CategoryName": "Название категории",
"Change_Password": "Изменить пароль",
"Changing": "",
"ChildInheritFields": "Поля наследуются дочерними элементами",
"ChildInheritFields_help": "По умолчанию дочерние объекты унаследуют эти поля.",
"Choose_Category": "Выбрать категорию",
@@ -364,6 +369,7 @@
"NotFoundHelp": "Не удалось найти страницу или объект.",
"NotInShopping": "{food} отсутствует в вашем списке покупок.",
"Note": "Заметка",
"NullingHelp": "",
"Number of Objects": "Количество (шт.)",
"Nutrition": "Питательность",
"NutritionsPerServing": "",
@@ -443,6 +449,7 @@
"Recipes": "Рецепты",
"Recipes_In_Import": "Рецепты в вашем файле импорта",
"Recipes_per_page": "Рецептов на странице",
"Refresh": "",
"Remove": "Удалить",
"RemoveAllType": "",
"RemoveFoodFromShopping": "Удалить {food} из вашего списка покупок",

View File

@@ -58,6 +58,8 @@
"BatchDeleteHelp": "",
"BatchEdit": "",
"BatchEditUpdatingItemsCount": "",
"Blocking": "",
"BlockingHelp": "",
"Book": "Knjiga",
"Bookmarklet": "Zaznamek",
"BookmarkletHelp1": "Povlecite naslednji gumb v vrstico z zaznamki",
@@ -72,11 +74,14 @@
"Cannot_Add_Notes_To_Shopping": "Opombe ne moreš dodati v nakupovalni listek",
"Carbohydrates": "Ogljikovi hidrati",
"Cards": "Karte",
"Cascading": "",
"CascadingHelp": "",
"Categories": "Kategorije",
"Category": "Kategorija",
"CategoryInstruction": "Povleci kategorije za spremembo vrstnega reda v nakupovalnem listku.",
"CategoryName": "Ime kategorije",
"Change_Password": "Spremeni geslo",
"Changing": "",
"ChildInheritFields": "Otroci podedujejo polja",
"ChildInheritFields_help": "Otroci bodo privzeto podedovali ta polja.",
"Choose_Category": "Izberi kategorijo",
@@ -366,6 +371,7 @@
"NotFoundHelp": "Strani ali predmeta, ki ga iščete, ni bilo mogoče najti.",
"NotInShopping": "{food} ni v tvojem nakupovalnem listku.",
"Note": "Opomba",
"NullingHelp": "",
"Number of Objects": "Število predmetov",
"Nutrition": "Prehrana",
"NutritionsPerServing": "",
@@ -445,6 +451,7 @@
"Recipes": "Recepti",
"Recipes_In_Import": "Recepti v vaši uvozni datoteki",
"Recipes_per_page": "Receptov na stran",
"Refresh": "",
"Remove": "Odstrani",
"RemoveAllType": "",
"RemoveFoodFromShopping": "Odstrani {food} iz nakupovalnega listka",

View File

@@ -57,6 +57,8 @@
"BatchDeleteHelp": "",
"BatchEdit": "",
"BatchEditUpdatingItemsCount": "",
"Blocking": "",
"BlockingHelp": "",
"Book": "Bok",
"Bookmarklet": "Bokmärke",
"BookmarkletHelp1": "Dra följande knapp till ditt bokmärkesfält",
@@ -71,11 +73,14 @@
"Cannot_Add_Notes_To_Shopping": "Anteckningar kan inte läggas till inköpslistan",
"Carbohydrates": "Kolhydrater",
"Cards": "Kort",
"Cascading": "",
"CascadingHelp": "",
"Categories": "Kategorier",
"Category": "Kategori",
"CategoryInstruction": "Dra kategorier för att ändra den ordning som kategorierna visas i inköpslistan.",
"CategoryName": "Kategorinamn",
"Change_Password": "Ändra lösenord",
"Changing": "",
"ChildInheritFields": "Underordnade ärver fält",
"ChildInheritFields_help": "Underordnade kommer att ärva dessa fält som standard.",
"Choose_Category": "Välj kategori",
@@ -311,6 +316,7 @@
"No_Results": "Inget resultat",
"NotInShopping": "{food} finns inte i din inköpslista.",
"Note": "Anteckning",
"NullingHelp": "",
"Number of Objects": "Antal objekt",
"Nutrition": "Näringsinnehåll",
"NutritionsPerServing": "",
@@ -368,6 +374,7 @@
"Recipes": "Recept",
"Recipes_In_Import": "Recept i din importfil",
"Recipes_per_page": "Recept per sida",
"Refresh": "",
"RemoveAllType": "",
"RemoveFoodFromShopping": "Ta bort {mat} från din inköpslista",
"RemoveParent": "",

View File

@@ -39,6 +39,8 @@
"BatchDeleteHelp": "",
"BatchEdit": "",
"BatchEditUpdatingItemsCount": "",
"Blocking": "",
"BlockingHelp": "",
"Bookmarklet": "Yer İmi",
"Books": "Kitaplar",
"CREATE_ERROR": "",
@@ -47,11 +49,14 @@
"Cancel": "İptal",
"Cannot_Add_Notes_To_Shopping": "Alışveriş listesine notlar eklenemez",
"Carbohydrates": "Karbonhidratlar",
"Cascading": "",
"CascadingHelp": "",
"Categories": "Kategoriler",
"Category": "Kategori",
"CategoryInstruction": "Alışveriş listesinde görünen sipariş kategorilerini değiştirmek için kategorileri sürükleyin.",
"CategoryName": "Kategori Adı",
"Change_Password": "Parolayı Değiştir",
"Changing": "",
"ChildInheritFields": "Alt Öğeler Alanları Devralır",
"ChildInheritFields_help": "Alt öğeler varsayılan olarak bu alanları devralır.",
"Choose_Category": "Kategori Seç",
@@ -274,6 +279,7 @@
"No_Results": "Sonuç Yok",
"NotInShopping": "{food} alışveriş listenizde yok.",
"Note": "Not",
"NullingHelp": "",
"Number of Objects": "Nesne Sayısı",
"Nutrition": "Besin Değeri",
"NutritionsPerServing": "",
@@ -331,6 +337,7 @@
"Recipes": "Tarifler",
"Recipes_In_Import": "İçe aktarma dosyanızdaki tarifler",
"Recipes_per_page": "Sayfa Başına Tarif",
"Refresh": "",
"RemoveAllType": "",
"RemoveFoodFromShopping": "{food}'ı alışveriş listenizden çıkarın",
"RemoveParent": "",

View File

@@ -36,6 +36,8 @@
"BatchDeleteHelp": "",
"BatchEdit": "",
"BatchEditUpdatingItemsCount": "",
"Blocking": "",
"BlockingHelp": "",
"Bookmarklet": "",
"Books": "Книги",
"CREATE_ERROR": "",
@@ -44,10 +46,13 @@
"Cancel": "Відмінити",
"Cannot_Add_Notes_To_Shopping": "Нотатки не можуть бути доданими до списку покупок",
"Carbohydrates": "Вуглеводи",
"Cascading": "",
"CascadingHelp": "",
"Categories": "Категорії",
"Category": "Категорія",
"CategoryInstruction": "",
"CategoryName": "",
"Changing": "",
"ChildInheritFields": "",
"ChildInheritFields_help": "",
"Clear": "",
@@ -240,6 +245,7 @@
"No_Results": "Немає Результату",
"NotInShopping": "{food} немає в вашому списку покупок.",
"Note": "Нотатка",
"NullingHelp": "",
"Number of Objects": "Кількість Об'єктів",
"Nutrition": "Харчова цінність",
"NutritionsPerServing": "",
@@ -295,6 +301,7 @@
"Recipes": "Рецепти",
"Recipes_In_Import": "",
"Recipes_per_page": "Кількість Рецептів на Сторінку",
"Refresh": "",
"RemoveAllType": "",
"RemoveFoodFromShopping": "Видалити {food} з вашого списку покупок",
"RemoveParent": "",

View File

@@ -39,6 +39,8 @@
"BatchDeleteHelp": "",
"BatchEdit": "",
"BatchEditUpdatingItemsCount": "",
"Blocking": "",
"BlockingHelp": "",
"Bookmarklet": "书签",
"Books": "烹饪手册",
"CREATE_ERROR": "",
@@ -47,11 +49,14 @@
"Cancel": "取消",
"Cannot_Add_Notes_To_Shopping": "无法将笔记添加到购物清单",
"Carbohydrates": "碳水化合物",
"Cascading": "",
"CascadingHelp": "",
"Categories": "分类",
"Category": "分类",
"CategoryInstruction": "拖动类别可更改出现在购物清单中的订单类别。",
"CategoryName": "分类名",
"Change_Password": "更改密码",
"Changing": "",
"ChildInheritFields": "子级继承字段",
"ChildInheritFields_help": "默认情况下,子项将继承这些字段。",
"Choose_Category": "选择类别",
@@ -274,6 +279,7 @@
"No_Results": "没有结果",
"NotInShopping": "购物清单中没有 {food}。",
"Note": "笔记",
"NullingHelp": "",
"Number of Objects": "对象数量",
"Nutrition": "营养",
"NutritionsPerServing": "",
@@ -331,6 +337,7 @@
"Recipes": "食谱",
"Recipes_In_Import": "从文件中导入食谱",
"Recipes_per_page": "每页食谱数量",
"Refresh": "",
"RemoveAllType": "",
"RemoveFoodFromShopping": "从购物清单中移除 {food}",
"RemoveParent": "",

View File

@@ -57,6 +57,8 @@
"BatchDeleteHelp": "",
"BatchEdit": "",
"BatchEditUpdatingItemsCount": "",
"Blocking": "",
"BlockingHelp": "",
"Book": "書籍",
"Bookmarklet": "書籤小工具",
"BookmarkletHelp1": "將以下按鈕拖到您的書籤欄中",
@@ -71,11 +73,14 @@
"Cannot_Add_Notes_To_Shopping": "無法添加備註到購物",
"Carbohydrates": "碳水化合物",
"Cards": "卡片",
"Cascading": "",
"CascadingHelp": "",
"Categories": "分類",
"Category": "類別",
"CategoryInstruction": "拖動類別可更改出現在購物清單中的訂單類別。",
"CategoryName": "分類名稱",
"Change_Password": "更改密碼",
"Changing": "",
"ChildInheritFields": "子項繼承欄位",
"ChildInheritFields_help": "預設情況下,子項將繼承這些欄位。",
"Choose_Category": "選擇分類",
@@ -365,6 +370,7 @@
"NotFoundHelp": "找不到您要尋找的頁面或物件。",
"NotInShopping": "購物清單中沒有 {food}。",
"Note": "備註",
"NullingHelp": "",
"Number of Objects": "對象數量",
"Nutrition": "營養",
"NutritionsPerServing": "",
@@ -444,6 +450,7 @@
"Recipes": "食譜",
"Recipes_In_Import": "匯入檔中的食譜",
"Recipes_per_page": "每頁中食譜",
"Refresh": "",
"Remove": "移除",
"RemoveAllType": "",
"RemoveFoodFromShopping": "從購物清單中移除 {food}",

View File

@@ -33,7 +33,7 @@ models/FoodBatchUpdate.ts
models/FoodInheritField.ts
models/FoodShoppingUpdate.ts
models/FoodSimple.ts
models/GenericModel.ts
models/GenericModelReference.ts
models/Group.ts
models/ImportLog.ts
models/ImportOpenData.ts
@@ -73,7 +73,7 @@ models/PaginatedEnterpriseSocialRecipeSearchList.ts
models/PaginatedEnterpriseSpaceList.ts
models/PaginatedExportLogList.ts
models/PaginatedFoodList.ts
models/PaginatedGenericModelList.ts
models/PaginatedGenericModelReferenceList.ts
models/PaginatedImportLogList.ts
models/PaginatedIngredientList.ts
models/PaginatedInviteLinkList.ts

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,78 @@
/* tslint:disable */
/* eslint-disable */
/**
* Tandoor
* Tandoor API Docs
*
* The version of the OpenAPI document: 0.0.0
*
*
* NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech).
* https://openapi-generator.tech
* Do not edit the class manually.
*/
import { mapValues } from '../runtime';
/**
*
* @export
* @interface GenericModelReference
*/
export interface GenericModelReference {
/**
*
* @type {number}
* @memberof GenericModelReference
*/
id?: number;
/**
*
* @type {string}
* @memberof GenericModelReference
*/
model: string;
/**
*
* @type {string}
* @memberof GenericModelReference
*/
name: string;
}
/**
* Check if a given object implements the GenericModelReference interface.
*/
export function instanceOfGenericModelReference(value: object): value is GenericModelReference {
if (!('model' in value) || value['model'] === undefined) return false;
if (!('name' in value) || value['name'] === undefined) return false;
return true;
}
export function GenericModelReferenceFromJSON(json: any): GenericModelReference {
return GenericModelReferenceFromJSONTyped(json, false);
}
export function GenericModelReferenceFromJSONTyped(json: any, ignoreDiscriminator: boolean): GenericModelReference {
if (json == null) {
return json;
}
return {
'id': json['id'] == null ? undefined : json['id'],
'model': json['model'],
'name': json['name'],
};
}
export function GenericModelReferenceToJSON(value?: GenericModelReference | null): any {
if (value == null) {
return value;
}
return {
'id': value['id'],
'model': value['model'],
'name': value['name'],
};
}

View File

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

View File

@@ -31,7 +31,7 @@ export * from './FoodBatchUpdate';
export * from './FoodInheritField';
export * from './FoodShoppingUpdate';
export * from './FoodSimple';
export * from './GenericModel';
export * from './GenericModelReference';
export * from './Group';
export * from './ImportLog';
export * from './ImportOpenData';
@@ -71,7 +71,7 @@ export * from './PaginatedEnterpriseSocialRecipeSearchList';
export * from './PaginatedEnterpriseSpaceList';
export * from './PaginatedExportLogList';
export * from './PaginatedFoodList';
export * from './PaginatedGenericModelList';
export * from './PaginatedGenericModelReferenceList';
export * from './PaginatedImportLogList';
export * from './PaginatedIngredientList';
export * from './PaginatedInviteLinkList';

View File

@@ -1,19 +1,161 @@
<template>
{{ props.model }}
{{ props.id }}
{{ editingObj }}
{{ protectingObjects }}
<v-container>
<v-row>
<v-col>
<v-card>
<v-card-text class="pt-2 pb-2">
<v-btn variant="flat" @click="router.go(-1)" prepend-icon="fa-solid fa-arrow-left">{{ $t('Back') }}</v-btn>
<v-btn variant="flat" @click="reloadAll()"
:loading="protectingObjectsLoading||cascadingObjectsLoading||nullingObjectsLoading" class="float-right" prepend-icon="fa-solid fa-arrows-rotate">
{{ $t('Refresh') }}
</v-btn>
</v-card-text>
</v-card>
</v-col>
</v-row>
<v-row dense>
<v-col>
<v-card>
<v-tabs v-model="tab" grow>
<v-tab value="protecting">
{{ $t('Blocking') }}
<template #append>
<v-chip size="small">{{ protectingObjectsCount }}</v-chip>
</template>
</v-tab>
<v-tab value="cascading">
{{ $t('Cascading') }}
<template #append>
<v-chip size="small">{{ cascadingObjectsCount }}</v-chip>
</template>
</v-tab>
<v-tab value="nulling">
{{ $t('Changing') }}
<template #append>
<v-chip size="small">{{ nullingObjectsCount }}</v-chip>
</template>
</v-tab>
</v-tabs>
</v-card>
<v-tabs-window v-model="tab">
<v-tabs-window-item value="protecting">
<v-card :title="$t('Blocking')">
<v-card-text>
{{ $t('BlockingHelp', {type: $t(genericModel.model.localizationKey)}) }}
<v-data-table-server
density="compact"
:headers="tableHeaders"
:loading="protectingObjectsLoading"
:items-length="protectingObjectsCount"
:items="protectingObjects"
@update:options="loadProtected"
>
<template #item.model="{item}">
{{ $t(item.model) }}
</template>
<template #item.actions="{item}">
<v-btn icon="$delete" variant="plain" size="small" target="_blank"
:to="{name: 'ModelDeletePage', params: {model: item.model, id: item.id}}"></v-btn>
<v-btn icon="$edit" variant="plain" size="small" target="_blank"
:to="{name: 'ModelEditPage', params: {model: item.model, id: item.id}}"></v-btn>
</template>
</v-data-table-server>
</v-card-text>
</v-card>
</v-tabs-window-item>
<v-tabs-window-item value="cascading">
<v-card>
<v-card-text>
{{ $t('CascadingHelp', {type: $t(genericModel.model.localizationKey)}) }}
<v-data-table-server
density="compact"
:headers="tableHeaders"
:loading="cascadingObjectsLoading"
:items-length="cascadingObjectsCount"
:items="cascadingObjects"
@update:options="loadCascading"
>
<template #item.model="{item}">
{{ $t(item.model) }}
</template>
<template #item.actions="{item}">
<v-btn icon="$delete" variant="plain" size="small" target="_blank"
:to="{name: 'ModelDeletePage', params: {model: item.model, id: item.id}}"></v-btn>
<v-btn icon="$edit" variant="plain" size="small" target="_blank"
:to="{name: 'ModelEditPage', params: {model: item.model, id: item.id}}"></v-btn>
</template>
</v-data-table-server>
</v-card-text>
</v-card>
</v-tabs-window-item>
<v-tabs-window-item value="nulling">
<v-card>
<v-card-text>
{{ $t('NullingHelp', {type: $t(genericModel.model.localizationKey)}) }}
<v-data-table-server
density="compact"
:headers="tableHeaders"
:loading="nullingObjectsLoading"
:items-length="nullingObjectsCount"
:items="nullingObjects"
:items-per-page="pageSize"
@update:options="loadNulling"
>
<template #item.model="{item}">
{{ $t(item.model) }}
</template>
<template #item.actions="{item}">
<v-btn icon="$delete" variant="plain" size="small" target="_blank"
:to="{name: 'ModelDeletePage', params: {model: item.model, id: item.id}}"></v-btn>
<v-btn icon="$edit" variant="plain" size="small" target="_blank"
:to="{name: 'ModelEditPage', params: {model: item.model, id: item.id}}"></v-btn>
</template>
</v-data-table-server>
</v-card-text>
</v-card>
</v-tabs-window-item>
</v-tabs-window>
</v-col>
</v-row>
<v-row dense>
<v-col>
<v-card class="border-error border-sm border-opacity-100">
<v-card-title>{{ $t('Delete') }}</v-card-title>
<v-card-text>
{{ $t('delete_confirmation', {source: `${$t(genericModel.model.localizationKey)} ${genericModel.getLabel(editingObj)}`}) }}
</v-card-text>
<v-card-actions>
<v-btn color="delete" prepend-icon="$delete" :disabled="protectingObjectsCount > 0" @click="deleteObject()" :loading="deleteLoading">{{ $t('Delete') }}</v-btn>
</v-card-actions>
</v-card>
</v-col>
</v-row>
</v-container>
</template>
<script setup lang="ts">
import {onBeforeMount, onMounted, PropType, ref, watch} from "vue";
import {onBeforeMount, onMounted, PropType, ref} from "vue";
import {EditorSupportedModels, GenericModel, getGenericModelFromString} from "@/types/Models.ts";
import {useTitle} from "@vueuse/core";
import {useI18n} from "vue-i18n";
import {ApiApi} from "@/openapi";
import {GenericModelReference} from "@/openapi";
import {VDataTableUpdateOptions} from "@/vuetify.ts";
import {ErrorMessageType, useMessageStore} from "@/stores/MessageStore.ts";
import {useRouter} from "vue-router";
import {VDataTableHeaders} from "vuetify/components";
import {useUserPreferenceStore} from "@/stores/UserPreferenceStore.ts";
const router = useRouter()
const title = useTitle()
const {t} = useI18n()
@@ -22,10 +164,31 @@ const props = defineProps({
id: {type: String, required: true},
})
const tableHeaders = [
{title: 'ID', key: 'id',},
{title: t('Model'), key: 'model',},
{title: t('Name'), key: 'name',},
{title: t('Actions'), key: 'actions', align: 'end'},
] as VDataTableHeaders[]
const genericModel = ref({} as GenericModel)
const editingObj = ref({} as EditorSupportedModels)
const tab = ref('protecting')
const deleteLoading = ref(false)
const protectingObjects = ref([] as GenericModel[])
const pageSize = ref(useUserPreferenceStore().deviceSettings.general_tableItemsPerPage)
const protectingObjects = ref([] as GenericModelReference[])
const protectingObjectsCount = ref(0)
const protectingObjectsLoading = ref(false)
const cascadingObjects = ref([] as GenericModelReference[])
const cascadingObjectsCount = ref(0)
const cascadingObjectsLoading = ref(false)
const nullingObjects = ref([] as GenericModelReference[])
const nullingObjectsCount = ref(0)
const nullingObjectsLoading = ref(false)
/**
* select model class before mount because template renders (and requests item load) before onMounted is called
@@ -37,30 +200,80 @@ onBeforeMount(() => {
console.error('Invalid model passed to ModelListPage, loading Food instead')
genericModel.value = getGenericModelFromString('Food', t)
}
title.value = t(genericModel.value.model.localizationKey)
})
onMounted(() => {
loadObject()
loadProtected()
reloadAll()
})
function loadObject() {
genericModel.value.retrieve(Number(props.id!)).then(obj => {
genericModel.value.retrieve(Number(props.id)).then(obj => {
editingObj.value = obj
title.value = t('DeleteSomething', {item: `${t(genericModel.value.model.localizationKey)} ${genericModel.value.getLabel(editingObj.value)}`})
}).catch(err => {
useMessageStore().addError(ErrorMessageType.FETCH_ERROR, err)
})
}
function loadProtected() {
let api = new ApiApi()
api.apiUnitProtectingList({id: props.id}).then(r => {
protectingObjects.value = r.results
function deleteObject() {
deleteLoading.value = true
genericModel.value.destroy(Number(props.id)).then(() => {
router.push({name: 'ModelListPage', params: {model: props.model}})
}).catch(err => {
useMessageStore().addError(ErrorMessageType.DELETE_ERROR, err)
}).finally(() => {
deleteLoading.value = false
})
}
function reloadAll() {
loadProtected({page: 1, itemsPerPage: pageSize.value})
loadCascading({page: 1, itemsPerPage: pageSize.value})
loadNulling({page: 1, itemsPerPage: pageSize.value})
}
function loadProtected(options: VDataTableUpdateOptions) {
protectingObjectsLoading.value = true
genericModel.value.getDeleteProtecting({id: Number(props.id), page: options.page, pageSize: options.itemsPerPage}).then(r => {
protectingObjects.value = r.results
protectingObjectsCount.value = r.count
}).catch(err => {
useMessageStore().addError(ErrorMessageType.FETCH_ERROR, err)
}).finally(() => {
protectingObjectsLoading.value = false
})
}
function loadCascading(options: VDataTableUpdateOptions) {
cascadingObjectsLoading.value = true
genericModel.value.getDeleteCascading({id: Number(props.id), page: options.page, pageSize: options.itemsPerPage}).then(r => {
cascadingObjects.value = r.results
cascadingObjectsCount.value = r.count
}).catch(err => {
useMessageStore().addError(ErrorMessageType.FETCH_ERROR, err)
}).finally(() => {
cascadingObjectsLoading.value = false
})
}
function loadNulling(options: VDataTableUpdateOptions) {
nullingObjectsLoading.value = true
genericModel.value.getDeleteNulling({id: Number(props.id), page: options.page, pageSize: options.itemsPerPage}).then(r => {
nullingObjects.value = r.results
nullingObjectsCount.value = r.count
}).catch(err => {
useMessageStore().addError(ErrorMessageType.FETCH_ERROR, err)
}).finally(() => {
nullingObjectsLoading.value = false
})
}
</script>
<style scoped>

View File

@@ -67,6 +67,15 @@ type GenericListRequestParameter = {
query: string,
}
/**
* common list parameters shared by all generic models
*/
type DeleteRelationRequestParameter = {
page: number,
pageSize: number,
id: number,
}
/**
* if a model is shown in a table, these are the table headers
* structure similar to the VDataTableHeaders but simplified and
@@ -948,7 +957,7 @@ export class GenericModel {
} else {
return this.api[`api${this.model.name}List`](genericListRequestParameter)
}
};
}
/**
* create a new instance of the given model
@@ -1048,6 +1057,33 @@ export class GenericModel {
}
}
/**
* query the protecting list endpoint
* @param deleteRelationRequestParameter parameters
* @return promise of request
*/
getDeleteProtecting(deleteRelationRequestParameter: DeleteRelationRequestParameter) {
return this.api[`api${this.model.name}ProtectingList`](deleteRelationRequestParameter)
};
/**
* query the cascading list endpoint
* @param deleteRelationRequestParameter parameters
* @return promise of request
*/
getDeleteCascading(deleteRelationRequestParameter: DeleteRelationRequestParameter) {
return this.api[`api${this.model.name}CascadingList`](deleteRelationRequestParameter)
};
/**
* query the nulling list endpoint
* @param deleteRelationRequestParameter parameters
* @return promise of request
*/
getDeleteNulling(deleteRelationRequestParameter: DeleteRelationRequestParameter) {
return this.api[`api${this.model.name}NullingList`](deleteRelationRequestParameter)
};
/**
* gets a label for a specific object instance using the model toStringKeys property
* @param obj obj to get label for