mirror of
https://github.com/TandoorRecipes/recipes.git
synced 2025-12-27 12:09:10 -05:00
Compare commits
48 Commits
beta
...
dependabot
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
823079f51d | ||
|
|
f4eded5b03 | ||
|
|
3d9c51053a | ||
|
|
0046233b6f | ||
|
|
78ede7b601 | ||
|
|
7e7e133604 | ||
|
|
b0ec569a00 | ||
|
|
7674084ae0 | ||
|
|
798e2ac48b | ||
|
|
714d4a32a9 | ||
|
|
66b5097872 | ||
|
|
c1d4fed142 | ||
|
|
1cc0806729 | ||
|
|
09487a0e94 | ||
|
|
89e58edcad | ||
|
|
887d7fe9f0 | ||
|
|
14696e3ce8 | ||
|
|
dd56bb4b35 | ||
|
|
8ec0ba9541 | ||
|
|
c105c9190e | ||
|
|
9c1700adb9 | ||
|
|
b43a87a7e3 | ||
|
|
01e78baecf | ||
|
|
0ee241524d | ||
|
|
2bd60a6f13 | ||
|
|
e0196f17da | ||
|
|
fe75052baa | ||
|
|
0a3b750294 | ||
|
|
fcd3918b5f | ||
|
|
e7aac06ca7 | ||
|
|
67e1f57723 | ||
|
|
10581329e8 | ||
|
|
553c06f291 | ||
|
|
e0cbfd824c | ||
|
|
a5a522d378 | ||
|
|
8502bb235b | ||
|
|
42f2ad624f | ||
|
|
3b95bf40da | ||
|
|
81a6837b06 | ||
|
|
a95e352250 | ||
|
|
a4ca66d287 | ||
|
|
ec30b81ae5 | ||
|
|
92211b1f51 | ||
|
|
8eeea42057 | ||
|
|
7e76a71ccc | ||
|
|
b397c94f0a | ||
|
|
9552564e59 | ||
|
|
4ecf323e4f |
2
.github/workflows/build-docker.yml
vendored
2
.github/workflows/build-docker.yml
vendored
@@ -21,7 +21,7 @@ jobs:
|
||||
suffix: ""
|
||||
continue-on-error: false
|
||||
steps:
|
||||
- uses: actions/checkout@v5
|
||||
- uses: actions/checkout@v6
|
||||
|
||||
- name: Get version number
|
||||
id: get_version
|
||||
|
||||
2
.github/workflows/ci.yml
vendored
2
.github/workflows/ci.yml
vendored
@@ -12,7 +12,7 @@ jobs:
|
||||
python-version: ["3.12"]
|
||||
node-version: ["22"]
|
||||
steps:
|
||||
- uses: actions/checkout@v5
|
||||
- uses: actions/checkout@v6
|
||||
- uses: awalsh128/cache-apt-pkgs-action@v1.6.0
|
||||
with:
|
||||
packages: libsasl2-dev python3-dev libxml2-dev libxmlsec1-dev libxslt-dev libxmlsec1-openssl libxslt-dev libldap2-dev libssl-dev gcc musl-dev postgresql-dev zlib-dev jpeg-dev libwebp-dev openssl-dev libffi-dev cargo openldap-dev python3-dev xmlsec-dev xmlsec build-base g++ curl
|
||||
|
||||
2
.github/workflows/codeql-analysis.yml
vendored
2
.github/workflows/codeql-analysis.yml
vendored
@@ -12,7 +12,7 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v5
|
||||
uses: actions/checkout@v6
|
||||
with:
|
||||
# We must fetch at least the immediate parents so that if this is
|
||||
# a pull request then we can checkout the head.
|
||||
|
||||
2
.github/workflows/docs.yml
vendored
2
.github/workflows/docs.yml
vendored
@@ -12,7 +12,7 @@ jobs:
|
||||
if: github.repository_owner == 'TandoorRecipes' && ${{ github.event.workflow_run.conclusion == 'success' }}
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v5
|
||||
- uses: actions/checkout@v6
|
||||
- uses: actions/setup-python@v6
|
||||
with:
|
||||
python-version: 3.x
|
||||
|
||||
@@ -56,7 +56,7 @@ class FoodPropertyHelper:
|
||||
if p.property_type == pt and p.property_amount is not None:
|
||||
has_property_value = True
|
||||
for c in conversions:
|
||||
if c.unit == i.food.properties_food_unit:
|
||||
if c.unit == i.food.properties_food_unit and i.food.properties_food_amount != 0:
|
||||
found_property = True
|
||||
computed_properties[pt.id]['total_value'] += (c.amount / i.food.properties_food_amount) * p.property_amount
|
||||
computed_properties[pt.id]['food_values'] = self.add_or_create(
|
||||
|
||||
@@ -324,9 +324,9 @@ class RecipeSearch():
|
||||
self._queryset = self._queryset.annotate(recent=Coalesce(Max(Case(When(pk__in=num_recent_recipes.values('recipe'), then='viewlog__pk'))), Value(0)))
|
||||
|
||||
def _favorite_recipes(self):
|
||||
if self._sort_includes('favorite') or self._timescooked or self._timescooked_gte or self._timescooked_lte:
|
||||
if self._sort_includes('favorite') or self._timescooked is not None or self._timescooked_gte is not None or self._timescooked_lte is not None:
|
||||
less_than = self._timescooked_lte and not self._sort_includes('-favorite')
|
||||
if less_than or self._timescooked == 0:
|
||||
if less_than:
|
||||
default = 1000
|
||||
else:
|
||||
default = 0
|
||||
@@ -338,11 +338,11 @@ class RecipeSearch():
|
||||
)
|
||||
self._queryset = self._queryset.annotate(favorite=Coalesce(Subquery(favorite_recipes), default))
|
||||
|
||||
if self._timescooked:
|
||||
if self._timescooked is not None:
|
||||
self._queryset = self._queryset.filter(favorite=self._timescooked)
|
||||
elif self._timescooked_lte:
|
||||
elif self._timescooked_lte is not None:
|
||||
self._queryset = self._queryset.filter(favorite__lte=int(self._timescooked_lte)).exclude(favorite=0)
|
||||
elif self._timescooked_gte:
|
||||
elif self._timescooked_gte is not None:
|
||||
self._queryset = self._queryset.filter(favorite__gte=int(self._timescooked_gte))
|
||||
|
||||
def keyword_filters(self, **kwargs):
|
||||
|
||||
@@ -75,7 +75,8 @@ class RecipeShoppingEditor():
|
||||
|
||||
@staticmethod
|
||||
def get_shopping_list_recipe(id, user, space):
|
||||
return ShoppingListRecipe.objects.filter(id=id).filter(entries__space=space).filter(
|
||||
# TODO this sucks since it wont find SLR's that no longer have any entries
|
||||
return ShoppingListRecipe.objects.filter(id=id, space=space).filter(
|
||||
Q(entries__created_by=user)
|
||||
| Q(entries__created_by__in=list(user.get_shopping_share()))
|
||||
).prefetch_related('entries').first()
|
||||
@@ -136,7 +137,8 @@ class RecipeShoppingEditor():
|
||||
self.servings = servings
|
||||
|
||||
self._delete_ingredients(ingredients=ingredients)
|
||||
if self.servings != self._shopping_list_recipe.servings:
|
||||
# need to check if there is a SLR because its possible it cant be found if all entries are deleted
|
||||
if self._shopping_list_recipe and self.servings != self._shopping_list_recipe.servings:
|
||||
self.edit_servings()
|
||||
self._add_ingredients(ingredients=ingredients)
|
||||
return True
|
||||
|
||||
@@ -96,14 +96,20 @@ class Mealie1(Integration):
|
||||
self.import_log.msg += f"Ignoring {r['name']} because a recipe with this name already exists.\n"
|
||||
self.import_log.save()
|
||||
else:
|
||||
servings = 1
|
||||
try:
|
||||
servings = r['recipe_servings'] if r['recipe_servings'] and r['recipe_servings'] != 0 else 1
|
||||
except KeyError:
|
||||
pass
|
||||
|
||||
recipe = Recipe.objects.create(
|
||||
waiting_time=parse_time(r['perform_time']),
|
||||
working_time=parse_time(r['prep_time']),
|
||||
description=r['description'][:512],
|
||||
name=r['name'],
|
||||
source_url=r['org_url'],
|
||||
servings=r['recipe_servings'] if r['recipe_servings'] and r['recipe_servings'] != 0 else 1,
|
||||
servings_text=r['recipe_yield'].strip() if r['recipe_yield'] else "",
|
||||
servings=servings,
|
||||
servings_text=r['recipe_yield'].strip()[:32] if r['recipe_yield'] else "",
|
||||
internal=True,
|
||||
created_at=r['created_at'],
|
||||
space=self.request.space,
|
||||
@@ -131,7 +137,7 @@ class Mealie1(Integration):
|
||||
step_id_dict = {}
|
||||
for s in mealie_database['recipe_instructions']:
|
||||
if s['recipe_id'] in recipes_dict:
|
||||
step = Step.objects.create(instruction=(s['text'] if s['text'] else "") + (f" \n {s['summary']}" if s['summary'] else ""),
|
||||
step = Step.objects.create(instruction=(s['text'] if s['text'] else "") + (f" \n {s['summary']}" if 'summary' in s and s['summary'] else ""),
|
||||
order=s['position'],
|
||||
name=s['title'],
|
||||
space=self.request.space)
|
||||
@@ -153,7 +159,7 @@ class Mealie1(Integration):
|
||||
for n in mealie_database['notes']:
|
||||
if n['recipe_id'] in recipes_dict:
|
||||
step = Step.objects.create(instruction=n['text'],
|
||||
name=n['title'],
|
||||
name=n['title'][:128] if n['title'] else "",
|
||||
order=100,
|
||||
space=self.request.space)
|
||||
steps_relation.append(Recipe.steps.through(recipe_id=recipes_dict[n['recipe_id']], step_id=step.pk))
|
||||
@@ -243,7 +249,7 @@ class Mealie1(Integration):
|
||||
for r in mealie_database['recipe_nutrition']:
|
||||
if r['recipe_id'] in recipes_dict:
|
||||
for key in property_types_dict:
|
||||
if r[key]:
|
||||
if key in r and r[key]:
|
||||
properties_relation.append(
|
||||
Property(property_type_id=property_types_dict[key].pk,
|
||||
property_amount=Decimal(str(r[key])) / (
|
||||
|
||||
@@ -63,7 +63,15 @@ class MealMaster(Integration):
|
||||
current_recipe = ''
|
||||
|
||||
for fl in file.readlines():
|
||||
line = fl.decode("windows-1250")
|
||||
line = ""
|
||||
try:
|
||||
line = fl.decode("UTF-8")
|
||||
except UnicodeDecodeError:
|
||||
try:
|
||||
line = fl.decode("windows-1250")
|
||||
except Exception as e:
|
||||
line = "ERROR DECODING LINE"
|
||||
|
||||
if (line.startswith('MMMMM') or line.startswith('-----')) and 'meal-master' in line.lower():
|
||||
if current_recipe != '':
|
||||
recipe_list.append(current_recipe)
|
||||
|
||||
@@ -12,7 +12,7 @@ msgstr ""
|
||||
"Project-Id-Version: PACKAGE VERSION\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2025-09-22 20:15+0200\n"
|
||||
"PO-Revision-Date: 2025-09-22 10:09+0000\n"
|
||||
"PO-Revision-Date: 2025-11-18 07:01+0000\n"
|
||||
"Last-Translator: Vincenzo Reale <smart2128vr@gmail.com>\n"
|
||||
"Language-Team: Italian <http://translate.tandoor.dev/projects/tandoor/"
|
||||
"recipes-backend/it/>\n"
|
||||
@@ -21,7 +21,7 @@ msgstr ""
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"Plural-Forms: nplurals=2; plural=n != 1;\n"
|
||||
"X-Generator: Weblate 5.13.1\n"
|
||||
"X-Generator: Weblate 5.13.3\n"
|
||||
|
||||
#: .\cookbook\forms.py:50
|
||||
msgid "Default"
|
||||
@@ -126,54 +126,52 @@ msgid "ferment"
|
||||
msgstr "fermentare"
|
||||
|
||||
#: .\cookbook\helper\recipe_url_import.py:325
|
||||
#, fuzzy
|
||||
#| msgid "Last cooked"
|
||||
msgid "slow cook"
|
||||
msgstr "Cucinato di recente"
|
||||
msgstr "cottura lenta"
|
||||
|
||||
#: .\cookbook\helper\recipe_url_import.py:326
|
||||
msgid "egg boiler"
|
||||
msgstr ""
|
||||
msgstr "bollitore per uova"
|
||||
|
||||
#: .\cookbook\helper\recipe_url_import.py:327
|
||||
msgid "kettle"
|
||||
msgstr ""
|
||||
msgstr "teiera"
|
||||
|
||||
#: .\cookbook\helper\recipe_url_import.py:328
|
||||
msgid "blend"
|
||||
msgstr ""
|
||||
msgstr "miscela"
|
||||
|
||||
#: .\cookbook\helper\recipe_url_import.py:329
|
||||
msgid "pre-clean"
|
||||
msgstr ""
|
||||
msgstr "pre-pulizia"
|
||||
|
||||
#: .\cookbook\helper\recipe_url_import.py:330
|
||||
msgid "high temperature"
|
||||
msgstr ""
|
||||
msgstr "alta temperatura"
|
||||
|
||||
#: .\cookbook\helper\recipe_url_import.py:331
|
||||
msgid "rice cooker"
|
||||
msgstr ""
|
||||
msgstr "cuociriso"
|
||||
|
||||
#: .\cookbook\helper\recipe_url_import.py:332
|
||||
msgid "caramelize"
|
||||
msgstr ""
|
||||
msgstr "caramellare"
|
||||
|
||||
#: .\cookbook\helper\recipe_url_import.py:333
|
||||
msgid "peeler"
|
||||
msgstr ""
|
||||
msgstr "pelapatate"
|
||||
|
||||
#: .\cookbook\helper\recipe_url_import.py:334
|
||||
msgid "slicer"
|
||||
msgstr ""
|
||||
msgstr "affettatrice"
|
||||
|
||||
#: .\cookbook\helper\recipe_url_import.py:335
|
||||
msgid "grater"
|
||||
msgstr ""
|
||||
msgstr "grattugia"
|
||||
|
||||
#: .\cookbook\helper\recipe_url_import.py:336
|
||||
msgid "spiralizer"
|
||||
msgstr ""
|
||||
msgstr "tagliaverdure a spirale"
|
||||
|
||||
#: .\cookbook\helper\recipe_url_import.py:337
|
||||
msgid "sous-vide"
|
||||
@@ -235,7 +233,7 @@ msgstr "Carboidrati"
|
||||
|
||||
#: .\cookbook\integration\mealie1.py:212
|
||||
msgid "Cholesterol"
|
||||
msgstr ""
|
||||
msgstr "Colesterolo"
|
||||
|
||||
#: .\cookbook\integration\mealie1.py:213
|
||||
#: .\cookbook\migrations\0190_auto_20230525_1506.py:17
|
||||
@@ -244,33 +242,31 @@ msgstr "Grassi"
|
||||
|
||||
#: .\cookbook\integration\mealie1.py:214
|
||||
msgid "Fiber"
|
||||
msgstr ""
|
||||
msgstr "Fibra"
|
||||
|
||||
#: .\cookbook\integration\mealie1.py:215
|
||||
#, fuzzy
|
||||
#| msgid "Proteins"
|
||||
msgid "Protein"
|
||||
msgstr "Proteine"
|
||||
msgstr "Proteina"
|
||||
|
||||
#: .\cookbook\integration\mealie1.py:216
|
||||
msgid "Saturated Fat"
|
||||
msgstr ""
|
||||
msgstr "Grasso saturo"
|
||||
|
||||
#: .\cookbook\integration\mealie1.py:217
|
||||
msgid "Sodium"
|
||||
msgstr ""
|
||||
msgstr "Sodio"
|
||||
|
||||
#: .\cookbook\integration\mealie1.py:218
|
||||
msgid "Sugar"
|
||||
msgstr ""
|
||||
msgstr "Zucchero"
|
||||
|
||||
#: .\cookbook\integration\mealie1.py:219
|
||||
msgid "Trans Fat"
|
||||
msgstr ""
|
||||
msgstr "Grasso trans"
|
||||
|
||||
#: .\cookbook\integration\mealie1.py:220
|
||||
msgid "Unsaturated Fat"
|
||||
msgstr ""
|
||||
msgstr "Grasso insaturo"
|
||||
|
||||
#: .\cookbook\integration\openeats.py:28
|
||||
msgid "Recipe source:"
|
||||
@@ -484,7 +480,7 @@ msgstr "Hai raggiungo il limite per il caricamento dei file."
|
||||
|
||||
#: .\cookbook\serializer.py:281
|
||||
msgid "The given file type is not allowed."
|
||||
msgstr ""
|
||||
msgstr "Il tipo di filo specificato non è consentito."
|
||||
|
||||
#: .\cookbook\serializer.py:421 .\cookbook\views\views.py:94
|
||||
msgid ""
|
||||
@@ -493,7 +489,7 @@ msgstr "Hai raggiunto il numero massimo di istanze di tua proprietà."
|
||||
|
||||
#: .\cookbook\serializer.py:434
|
||||
msgid "Space Name must be unique."
|
||||
msgstr ""
|
||||
msgstr "Il nome dello spazio deve essere univoco."
|
||||
|
||||
#: .\cookbook\serializer.py:469
|
||||
msgid "Cannot modify Space owner permission."
|
||||
@@ -847,10 +843,8 @@ msgid "We are sorry, but the sign up is currently closed."
|
||||
msgstr "Spiacenti, al momento le iscrizioni sono chiuse."
|
||||
|
||||
#: .\cookbook\templates\frontend\tandoor.html:15
|
||||
#, fuzzy
|
||||
#| msgid "Tandoor Recipes Invite"
|
||||
msgid "Tandoor Recipe Manager"
|
||||
msgstr "Invito per Tandoor Recipes"
|
||||
msgstr "Gestore delle ricette Tandoor"
|
||||
|
||||
#: .\cookbook\templates\index.html:28
|
||||
msgid "Search recipe ..."
|
||||
@@ -1442,8 +1436,6 @@ msgstr ""
|
||||
" %(site_name)s. Per finire, completa il modulo seguente:"
|
||||
|
||||
#: .\cookbook\templates\socialaccount\signup.html:32
|
||||
#, fuzzy
|
||||
#| msgid "I accept the follwoing"
|
||||
msgid "I accept the following"
|
||||
msgstr "Accetto i seguenti"
|
||||
|
||||
@@ -1529,15 +1521,6 @@ msgid "System"
|
||||
msgstr "Sistema"
|
||||
|
||||
#: .\cookbook\templates\system.html:24
|
||||
#, fuzzy
|
||||
#| msgid ""
|
||||
#| "\n"
|
||||
#| " Django Recipes is an open source free software application. It "
|
||||
#| "can be found on\n"
|
||||
#| " <a href=\"https://github.com/vabene1111/recipes\">GitHub</a>.\n"
|
||||
#| " Changelogs can be found <a href=\"https://github.com/vabene1111/"
|
||||
#| "recipes/releases\">here</a>.\n"
|
||||
#| " "
|
||||
msgid ""
|
||||
"\n"
|
||||
" Tandoor Recipes is an open source free software application. It can "
|
||||
@@ -1548,11 +1531,11 @@ msgid ""
|
||||
" "
|
||||
msgstr ""
|
||||
"\n"
|
||||
" Django Recipes è una applicazione gratuita e open source. È "
|
||||
" Tandoor Recipes è una applicazione gratuita e open source. È "
|
||||
"disponibile su\n"
|
||||
" <a href=\"https://github.com/vabene1111/recipes\">GitHub</a>.\n"
|
||||
" Puoi consultare le ultime novità <a href=\"https://github.com/"
|
||||
"vabene1111/recipes/releases\">qui</a>.\n"
|
||||
" <a href=\"https://github.com/TandoorRecipes/recipes\">GitHub</a>.\n"
|
||||
" Puoi consultare le ultime novità <a href="
|
||||
"\"https://github.com/TandoorRecipes/recipes/releases\">qui</a>.\n"
|
||||
" "
|
||||
|
||||
#: .\cookbook\templates\system.html:30
|
||||
@@ -1574,7 +1557,7 @@ msgstr ""
|
||||
|
||||
#: .\cookbook\templates\system.html:56
|
||||
msgid "Plugins"
|
||||
msgstr ""
|
||||
msgstr "Estensioni"
|
||||
|
||||
#: .\cookbook\templates\system.html:67
|
||||
msgid "Media Serving"
|
||||
@@ -1802,18 +1785,12 @@ msgid "{obj.name} was added to the shopping list."
|
||||
msgstr "{obj.name} è stato aggiunto alla lista della spesa."
|
||||
|
||||
#: .\cookbook\views\api.py:1239
|
||||
#, fuzzy
|
||||
#| msgid "Filter meal plans from date (inclusive) in the format of YYYY-MM-DD."
|
||||
msgid "Filter meal plans from date (inclusive)."
|
||||
msgstr ""
|
||||
"Filtra i piani alimentari in base alla data (inclusa) nel formato AAAA-MM-GG."
|
||||
msgstr "Filtra i piani alimentari dalla data (inclusa)."
|
||||
|
||||
#: .\cookbook\views\api.py:1241
|
||||
#, fuzzy
|
||||
#| msgid "Filter meal plans to date (inclusive) in the format of YYYY-MM-DD."
|
||||
msgid "Filter meal plans to date (inclusive)."
|
||||
msgstr ""
|
||||
"Filtra i piani alimentari fino alla data (inclusa) nel formato AAAA-MM-GG."
|
||||
msgstr "Filtra i piani alimentari fino alla data (inclusa)."
|
||||
|
||||
#: .\cookbook\views\api.py:1244
|
||||
msgid "Filter meal plans with MealType ID. For multiple repeat parameter."
|
||||
@@ -1940,115 +1917,70 @@ msgstr "ID dell'unità che una ricetta dovrebbe avere."
|
||||
|
||||
#: .\cookbook\views\api.py:1464
|
||||
msgid "Exact rating of recipe"
|
||||
msgstr ""
|
||||
msgstr "Valutazione precisa della ricetta"
|
||||
|
||||
#: .\cookbook\views\api.py:1465
|
||||
#, fuzzy
|
||||
#| msgid "ID of unit a recipe should have."
|
||||
msgid "Rating a recipe should have or greater."
|
||||
msgstr "ID dell'unità che una ricetta dovrebbe avere."
|
||||
msgstr "La valutazione che una ricetta dovrebbe avere o superiore."
|
||||
|
||||
#: .\cookbook\views\api.py:1466
|
||||
#, fuzzy
|
||||
#| msgid "ID of unit a recipe should have."
|
||||
msgid "Rating a recipe should have or smaller."
|
||||
msgstr "ID dell'unità che una ricetta dovrebbe avere."
|
||||
msgstr "La valutazione che una ricetta dovrebbe avere o inferiore."
|
||||
|
||||
#: .\cookbook\views\api.py:1468
|
||||
msgid "Filter recipes cooked X times."
|
||||
msgstr ""
|
||||
msgstr "Filtra le ricette cucinate N volte."
|
||||
|
||||
#: .\cookbook\views\api.py:1469
|
||||
#, fuzzy
|
||||
#| msgid ""
|
||||
#| "Filter recipes cooked X times or more. Negative values returns cooked "
|
||||
#| "less than X times"
|
||||
msgid "Filter recipes cooked X times or more."
|
||||
msgstr ""
|
||||
"Filtra le ricette cucinate X volte o più. I valori negativi restituiscono "
|
||||
"ricette cucinate meno di X volte"
|
||||
msgstr "Filtra le ricette cucinate N volte o più."
|
||||
|
||||
#: .\cookbook\views\api.py:1470
|
||||
msgid "Filter recipes cooked X times or less."
|
||||
msgstr ""
|
||||
msgstr "Filtra le ricette cucinate N volte o meno."
|
||||
|
||||
#: .\cookbook\views\api.py:1472
|
||||
#, fuzzy
|
||||
#| msgid "Filter for entries with the given recipe"
|
||||
msgid "Filter recipes created on the given date."
|
||||
msgstr "Filtra le voci con la ricetta specificata"
|
||||
msgstr "Filtra create alla data specificata."
|
||||
|
||||
#: .\cookbook\views\api.py:1473
|
||||
#, fuzzy
|
||||
#| msgid ""
|
||||
#| "Filter recipes created on or after YYYY-MM-DD. Prepending - filters on or "
|
||||
#| "before date."
|
||||
msgid "Filter recipes created on the given date or after."
|
||||
msgstr ""
|
||||
"Filtra le ricette create il o dopo AAAA-MM-GG. Anteponendo: filtra alla data "
|
||||
"o prima della data."
|
||||
msgstr "Filtra le ricette create alla data specificata o dopo."
|
||||
|
||||
#: .\cookbook\views\api.py:1474
|
||||
#, fuzzy
|
||||
#| msgid ""
|
||||
#| "Filter recipes created on or after YYYY-MM-DD. Prepending - filters on or "
|
||||
#| "before date."
|
||||
msgid "Filter recipes created on the given date or before."
|
||||
msgstr ""
|
||||
"Filtra le ricette create il o dopo AAAA-MM-GG. Anteponendo: filtra alla data "
|
||||
"o prima della data."
|
||||
msgstr "Filtra le ricette create alla data specificata o prima."
|
||||
|
||||
#: .\cookbook\views\api.py:1476 .\cookbook\views\api.py:1477
|
||||
#: .\cookbook\views\api.py:1478
|
||||
#, fuzzy
|
||||
#| msgid "Filter for entries with the given recipe"
|
||||
msgid "Filter recipes updated on the given date."
|
||||
msgstr "Filtra le voci con la ricetta specificata"
|
||||
msgstr "Filtra le ricette aggiornate alla data specificata."
|
||||
|
||||
#: .\cookbook\views\api.py:1480
|
||||
#, fuzzy
|
||||
#| msgid ""
|
||||
#| "Filter recipes last cooked on or after YYYY-MM-DD. Prepending - filters "
|
||||
#| "on or before date."
|
||||
msgid "Filter recipes last cooked on the given date or after."
|
||||
msgstr ""
|
||||
"Filtra le ricette cucinate l'ultima volta il o dopo AAAA-MM-GG. Anteponendo "
|
||||
"- filtra alla data o prima della data."
|
||||
msgstr "Filtra le ricette cucinate l'ultima volta alla data specificata o dopo."
|
||||
|
||||
#: .\cookbook\views\api.py:1481
|
||||
#, fuzzy
|
||||
#| msgid ""
|
||||
#| "Filter recipes last cooked on or after YYYY-MM-DD. Prepending - filters "
|
||||
#| "on or before date."
|
||||
msgid "Filter recipes last cooked on the given date or before."
|
||||
msgstr ""
|
||||
"Filtra le ricette cucinate l'ultima volta il o dopo AAAA-MM-GG. Anteponendo "
|
||||
"- filtra alla data o prima della data."
|
||||
"Filtra le ricette cucinate l'ultima volta alla data specificata o prima."
|
||||
|
||||
#: .\cookbook\views\api.py:1483 .\cookbook\views\api.py:1484
|
||||
#, fuzzy
|
||||
#| msgid ""
|
||||
#| "Filter recipes lasts viewed on or after YYYY-MM-DD. Prepending - filters "
|
||||
#| "on or before date."
|
||||
msgid "Filter recipes lasts viewed on the given date."
|
||||
msgstr ""
|
||||
"Filtra le ricette visualizzate per ultime il o dopo AAAA-MM-GG. Anteponendo "
|
||||
"- filtra alla data o prima della data."
|
||||
msgstr "Filtra le ricette visualizzate per ultime alla data specificata."
|
||||
|
||||
#: .\cookbook\views\api.py:1486
|
||||
#, fuzzy
|
||||
#| msgid "Filter for entries with the given recipe"
|
||||
msgid "Filter recipes for ones created by the given user ID"
|
||||
msgstr "Filtra le voci con la ricetta specificata"
|
||||
msgstr "Filtra le ricette create dall'ID utente specificato"
|
||||
|
||||
#: .\cookbook\views\api.py:1487
|
||||
msgid "If only internal recipes should be returned. [true/<b>false</b>]"
|
||||
msgstr ""
|
||||
"Se devono essere restituite solo le ricette interne. [vero/<b>falso</b>]"
|
||||
"Se devono essere restituite solo le ricette interne. [true/<b>false</b>]"
|
||||
|
||||
#: .\cookbook\views\api.py:1488
|
||||
msgid "Returns the results in randomized order. [true/<b>false</b>]"
|
||||
msgstr "Restituisce i risultati in ordine casuale. [vero/<b>falso</b>]"
|
||||
msgstr "Restituisce i risultati in ordine casuale. [true/<b>false</b>]"
|
||||
|
||||
#: .\cookbook\views\api.py:1490
|
||||
msgid ""
|
||||
@@ -2056,6 +1988,9 @@ msgid ""
|
||||
"lastcooked,-lastcooked,rating,-rating,times_cooked,-times_cooked,created_at,-"
|
||||
"created_at,lastviewed,-lastviewed"
|
||||
msgstr ""
|
||||
"Determina l'ordine dei risultati. Le opzioni sono: "
|
||||
"score,-score,name,-name,lastcooked,-lastcooked,rating,-rating,times_cooked,-times_cooked,created_at,-created_at,lastviewed,-lastviewed"
|
||||
""
|
||||
|
||||
#: .\cookbook\views\api.py:1492
|
||||
msgid "Returns new results first in search results. [true/<b>false</b>]"
|
||||
@@ -2068,10 +2003,14 @@ msgid ""
|
||||
"Returns the given number of recently viewed recipes before search results "
|
||||
"(if given)"
|
||||
msgstr ""
|
||||
"Restituisce il numero specificato di ricette visualizzate di recente prima "
|
||||
"dei risultati di ricerca (se indicati)"
|
||||
|
||||
#: .\cookbook\views\api.py:1494
|
||||
msgid "ID of a custom filter. Returns all recipes matched by that filter."
|
||||
msgstr ""
|
||||
"ID di un filtro personalizzato. Restituisce tutte le ricette verificate da "
|
||||
"quel filtro."
|
||||
|
||||
#: .\cookbook\views\api.py:1495
|
||||
msgid "Filter recipes that can be made with OnHand food. [true/<b>false</b>]"
|
||||
@@ -2084,48 +2023,47 @@ msgid ""
|
||||
"Return the PropertyTypes matching the property category. Repeat for "
|
||||
"multiple."
|
||||
msgstr ""
|
||||
"Restituisci i PropertyTypes corrispondenti alla categoria di proprietà. "
|
||||
"Ripeti per più di uno."
|
||||
|
||||
#: .\cookbook\views\api.py:1804 .\cookbook\views\api.py:1860
|
||||
#, fuzzy
|
||||
#| msgid "Filter for entries with the given recipe"
|
||||
msgid "Returns only entries associated with the given mealplan id"
|
||||
msgstr "Filtra le voci con la ricetta specificata"
|
||||
msgstr ""
|
||||
"Restituisce solo le voci associate all'ID del piano alimentare specificato"
|
||||
|
||||
#: .\cookbook\views\api.py:1858
|
||||
msgid ""
|
||||
"Returns only elements updated after the given timestamp in ISO 8601 format."
|
||||
msgstr ""
|
||||
"Restituisce solo gli elementi aggiornati dopo la marca temporale specificata "
|
||||
"nel formato ISO 8601."
|
||||
|
||||
#: .\cookbook\views\api.py:2031
|
||||
#, fuzzy
|
||||
#| msgid ""
|
||||
#| "Return the Automations matching the automation type. Multiple values "
|
||||
#| "allowed."
|
||||
msgid ""
|
||||
"Return the Automations matching the automation type. Repeat for multiple."
|
||||
msgstr ""
|
||||
"Restituisce le automazioni corrispondenti al tipo di automazione. Sono "
|
||||
"consentiti più valori."
|
||||
"Restituisci le automazioni corrispondenti al tipo di automazione. Ripeti per "
|
||||
"più automazioni."
|
||||
|
||||
#: .\cookbook\views\api.py:2048
|
||||
msgid ""
|
||||
"Text field to store data that gets carried over to the UserSpace created "
|
||||
"from the InviteLink"
|
||||
msgstr ""
|
||||
"Campo di testo per memorizzare i dati che vengono trasferiti allo spazio "
|
||||
"utente creato dal collegamento di invito"
|
||||
|
||||
#: .\cookbook\views\api.py:2049
|
||||
msgid "Only return InviteLinks that have not been used yet."
|
||||
msgstr ""
|
||||
"Restituisci solo i collegamenti di invito che non sono ancora stati "
|
||||
"utilizzati."
|
||||
|
||||
#: .\cookbook\views\api.py:2076
|
||||
#, fuzzy
|
||||
#| msgid ""
|
||||
#| "Return the Automations matching the automation type. Multiple values "
|
||||
#| "allowed."
|
||||
msgid "Return the CustomFilters matching the model type. Repeat for multiple."
|
||||
msgstr ""
|
||||
"Restituisce le automazioni corrispondenti al tipo di automazione. Sono "
|
||||
"consentiti più valori."
|
||||
"Restituisci i CustomFilters corrispondenti al tipo di modello. Ripeti per "
|
||||
"più di un modello."
|
||||
|
||||
#: .\cookbook\views\api.py:2176
|
||||
msgid "Nothing to do."
|
||||
@@ -2149,13 +2087,15 @@ msgstr "Nessuna informazione utilizzabile è stata trovata."
|
||||
|
||||
#: .\cookbook\views\api.py:2286 .\cookbook\views\api.py:2434
|
||||
msgid "You must select an AI provider to perform your request."
|
||||
msgstr ""
|
||||
msgstr "Devi selezionare un fornitore AI per eseguire la tua richiesta."
|
||||
|
||||
#: .\cookbook\views\api.py:2293 .\cookbook\views\api.py:2441
|
||||
msgid ""
|
||||
"You don't have any credits remaining to use AI or AI features are not "
|
||||
"enabled for your space."
|
||||
msgstr ""
|
||||
"Non hai credito rimanente per utilizzare l'AI o le funzionalità di AI "
|
||||
"abilitate per il tuo spazio."
|
||||
|
||||
#: .\cookbook\views\api.py:2499 .\cookbook\views\api.py:2667
|
||||
msgid "File is above space limit"
|
||||
|
||||
@@ -8,7 +8,7 @@ msgstr ""
|
||||
"Project-Id-Version: PACKAGE VERSION\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2025-09-22 20:15+0200\n"
|
||||
"PO-Revision-Date: 2024-11-19 06:58+0000\n"
|
||||
"PO-Revision-Date: 2025-11-15 12:08+0000\n"
|
||||
"Last-Translator: \"Matjaž T.\" <matjaz@moj-svet.si>\n"
|
||||
"Language-Team: Slovenian <http://translate.tandoor.dev/projects/tandoor/"
|
||||
"recipes-backend/sl/>\n"
|
||||
@@ -16,9 +16,9 @@ msgstr ""
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"Plural-Forms: nplurals=4; plural=n%100==1 ? 0 : n%100==2 ? 1 : n%100==3 || n"
|
||||
"%100==4 ? 2 : 3;\n"
|
||||
"X-Generator: Weblate 5.6.2\n"
|
||||
"Plural-Forms: nplurals=4; plural=n%100==1 ? 0 : n%100==2 ? 1 : n%100==3 || "
|
||||
"n%100==4 ? 2 : 3;\n"
|
||||
"X-Generator: Weblate 5.13.3\n"
|
||||
|
||||
#: .\cookbook\forms.py:50
|
||||
msgid "Default"
|
||||
@@ -123,51 +123,51 @@ msgstr "fermentiramo"
|
||||
|
||||
#: .\cookbook\helper\recipe_url_import.py:325
|
||||
msgid "slow cook"
|
||||
msgstr ""
|
||||
msgstr "počasno kuhanje"
|
||||
|
||||
#: .\cookbook\helper\recipe_url_import.py:326
|
||||
msgid "egg boiler"
|
||||
msgstr ""
|
||||
msgstr "kuhalnik jajc"
|
||||
|
||||
#: .\cookbook\helper\recipe_url_import.py:327
|
||||
msgid "kettle"
|
||||
msgstr ""
|
||||
msgstr "kotel"
|
||||
|
||||
#: .\cookbook\helper\recipe_url_import.py:328
|
||||
msgid "blend"
|
||||
msgstr ""
|
||||
msgstr "mešanica"
|
||||
|
||||
#: .\cookbook\helper\recipe_url_import.py:329
|
||||
msgid "pre-clean"
|
||||
msgstr ""
|
||||
msgstr "predhodno čiščenje"
|
||||
|
||||
#: .\cookbook\helper\recipe_url_import.py:330
|
||||
msgid "high temperature"
|
||||
msgstr ""
|
||||
msgstr "visoka temperatura"
|
||||
|
||||
#: .\cookbook\helper\recipe_url_import.py:331
|
||||
msgid "rice cooker"
|
||||
msgstr ""
|
||||
msgstr "kuhalnik riža"
|
||||
|
||||
#: .\cookbook\helper\recipe_url_import.py:332
|
||||
msgid "caramelize"
|
||||
msgstr ""
|
||||
msgstr "karamelizirati"
|
||||
|
||||
#: .\cookbook\helper\recipe_url_import.py:333
|
||||
msgid "peeler"
|
||||
msgstr ""
|
||||
msgstr "lupilec"
|
||||
|
||||
#: .\cookbook\helper\recipe_url_import.py:334
|
||||
msgid "slicer"
|
||||
msgstr ""
|
||||
msgstr "rezalnik"
|
||||
|
||||
#: .\cookbook\helper\recipe_url_import.py:335
|
||||
msgid "grater"
|
||||
msgstr ""
|
||||
msgstr "strgalo"
|
||||
|
||||
#: .\cookbook\helper\recipe_url_import.py:336
|
||||
msgid "spiralizer"
|
||||
msgstr ""
|
||||
msgstr "spiralizator"
|
||||
|
||||
#: .\cookbook\helper\recipe_url_import.py:337
|
||||
msgid "sous-vide"
|
||||
@@ -229,7 +229,7 @@ msgstr "Ogljikovi hidrati"
|
||||
|
||||
#: .\cookbook\integration\mealie1.py:212
|
||||
msgid "Cholesterol"
|
||||
msgstr ""
|
||||
msgstr "Holesterol"
|
||||
|
||||
#: .\cookbook\integration\mealie1.py:213
|
||||
#: .\cookbook\migrations\0190_auto_20230525_1506.py:17
|
||||
@@ -238,33 +238,31 @@ msgstr "Maščoba"
|
||||
|
||||
#: .\cookbook\integration\mealie1.py:214
|
||||
msgid "Fiber"
|
||||
msgstr ""
|
||||
msgstr "Vlaknine"
|
||||
|
||||
#: .\cookbook\integration\mealie1.py:215
|
||||
#, fuzzy
|
||||
#| msgid "Proteins"
|
||||
msgid "Protein"
|
||||
msgstr "Beljakovine"
|
||||
|
||||
#: .\cookbook\integration\mealie1.py:216
|
||||
msgid "Saturated Fat"
|
||||
msgstr ""
|
||||
msgstr "Nasičene maščobe"
|
||||
|
||||
#: .\cookbook\integration\mealie1.py:217
|
||||
msgid "Sodium"
|
||||
msgstr ""
|
||||
msgstr "Natrij"
|
||||
|
||||
#: .\cookbook\integration\mealie1.py:218
|
||||
msgid "Sugar"
|
||||
msgstr ""
|
||||
msgstr "Sladkor"
|
||||
|
||||
#: .\cookbook\integration\mealie1.py:219
|
||||
msgid "Trans Fat"
|
||||
msgstr ""
|
||||
msgstr "Trans maščobe"
|
||||
|
||||
#: .\cookbook\integration\mealie1.py:220
|
||||
msgid "Unsaturated Fat"
|
||||
msgstr ""
|
||||
msgstr "Nenasičene maščobe"
|
||||
|
||||
#: .\cookbook\integration\openeats.py:28
|
||||
msgid "Recipe source:"
|
||||
@@ -478,7 +476,7 @@ msgstr "Dosegli ste omejitev nalaganja datotek."
|
||||
|
||||
#: .\cookbook\serializer.py:281
|
||||
msgid "The given file type is not allowed."
|
||||
msgstr ""
|
||||
msgstr "Navedena vrsta datoteke ni dovoljena."
|
||||
|
||||
#: .\cookbook\serializer.py:421 .\cookbook\views\views.py:94
|
||||
msgid ""
|
||||
@@ -487,7 +485,7 @@ msgstr "Dosegli ste največje število prostorov, ki so lahko v vaši lasti."
|
||||
|
||||
#: .\cookbook\serializer.py:434
|
||||
msgid "Space Name must be unique."
|
||||
msgstr ""
|
||||
msgstr "Ime prostora mora biti edinstveno."
|
||||
|
||||
#: .\cookbook\serializer.py:469
|
||||
msgid "Cannot modify Space owner permission."
|
||||
@@ -838,10 +836,8 @@ msgid "We are sorry, but the sign up is currently closed."
|
||||
msgstr "Žal nam je, vendar so registracije trenutno zaprte."
|
||||
|
||||
#: .\cookbook\templates\frontend\tandoor.html:15
|
||||
#, fuzzy
|
||||
#| msgid "Tandoor Recipes Invite"
|
||||
msgid "Tandoor Recipe Manager"
|
||||
msgstr "Tandoor Recepti vabilo"
|
||||
msgstr "Urejevalnik Tandoor Recepti"
|
||||
|
||||
#: .\cookbook\templates\index.html:28
|
||||
msgid "Search recipe ..."
|
||||
@@ -1428,8 +1424,6 @@ msgstr ""
|
||||
" %(site_name)s. Kot zadnji korak izpolnite naslednji obrazec:"
|
||||
|
||||
#: .\cookbook\templates\socialaccount\signup.html:32
|
||||
#, fuzzy
|
||||
#| msgid "I accept the follwoing"
|
||||
msgid "I accept the following"
|
||||
msgstr "Sprejemam naslednje"
|
||||
|
||||
@@ -1514,15 +1508,6 @@ msgid "System"
|
||||
msgstr "Sistem"
|
||||
|
||||
#: .\cookbook\templates\system.html:24
|
||||
#, fuzzy
|
||||
#| msgid ""
|
||||
#| "\n"
|
||||
#| " Django Recipes is an open source free software application. It "
|
||||
#| "can be found on\n"
|
||||
#| " <a href=\"https://github.com/vabene1111/recipes\">GitHub</a>.\n"
|
||||
#| " Changelogs can be found <a href=\"https://github.com/vabene1111/"
|
||||
#| "recipes/releases\">here</a>.\n"
|
||||
#| " "
|
||||
msgid ""
|
||||
"\n"
|
||||
" Tandoor Recipes is an open source free software application. It can "
|
||||
@@ -1533,11 +1518,11 @@ msgid ""
|
||||
" "
|
||||
msgstr ""
|
||||
"\n"
|
||||
" Django Recipes je odprtokodna brezplačna programska aplikacija. "
|
||||
"Najdete ga na GitHub\n"
|
||||
" <a href=\"https://github.com/vabene1111/recipes\"></a>.\n"
|
||||
" Dnevnike sprememb lahko najdete tukaj <a href=\"https://github.com/"
|
||||
"vabene1111/recipes/releases\"></a>.\n"
|
||||
" Tandoor Recipes je odprtokodna brezplačna programska oprema. Najdete "
|
||||
"jo na\n"
|
||||
" <a href=\"https://github.com/TandoorRecipes/recipes\">GitHub</a>.\n"
|
||||
" Dnevnike sprememb najdete <a href="
|
||||
"\"https://github.com/TandoorRecipes/recipes/releases\">tukaj</a>.\n"
|
||||
" "
|
||||
|
||||
#: .\cookbook\templates\system.html:30
|
||||
@@ -1559,7 +1544,7 @@ msgstr ""
|
||||
|
||||
#: .\cookbook\templates\system.html:56
|
||||
msgid "Plugins"
|
||||
msgstr ""
|
||||
msgstr "Vtičniki"
|
||||
|
||||
#: .\cookbook\templates\system.html:67
|
||||
msgid "Media Serving"
|
||||
@@ -1789,16 +1774,12 @@ msgid "{obj.name} was added to the shopping list."
|
||||
msgstr "{obj.name} je bil dodan na nakupovalni seznam."
|
||||
|
||||
#: .\cookbook\views\api.py:1239
|
||||
#, fuzzy
|
||||
#| msgid "Filter meal plans from date (inclusive) in the format of YYYY-MM-DD."
|
||||
msgid "Filter meal plans from date (inclusive)."
|
||||
msgstr "Filtrirajte načrte obrokov od datuma (vključno) v obliki LLLL-MM-DD."
|
||||
msgstr "Filtriraj načrte obrokov od datuma (vključno)."
|
||||
|
||||
#: .\cookbook\views\api.py:1241
|
||||
#, fuzzy
|
||||
#| msgid "Filter meal plans to date (inclusive) in the format of YYYY-MM-DD."
|
||||
msgid "Filter meal plans to date (inclusive)."
|
||||
msgstr "Filtrirajte dosedanje načrte obrokov (vključno) v obliki LLLL-MM-DD."
|
||||
msgstr "Filtriraj načrte obrokov do danes (vključno)."
|
||||
|
||||
#: .\cookbook\views\api.py:1244
|
||||
msgid "Filter meal plans with MealType ID. For multiple repeat parameter."
|
||||
@@ -1905,106 +1886,62 @@ msgstr "ID enote, ki bi jo moral imeti recept."
|
||||
|
||||
#: .\cookbook\views\api.py:1464
|
||||
msgid "Exact rating of recipe"
|
||||
msgstr ""
|
||||
msgstr "Natančna ocena recepta"
|
||||
|
||||
#: .\cookbook\views\api.py:1465
|
||||
#, fuzzy
|
||||
#| msgid "ID of unit a recipe should have."
|
||||
msgid "Rating a recipe should have or greater."
|
||||
msgstr "ID enote, ki bi jo moral imeti recept."
|
||||
msgstr "Ocena, ki bi jo moral imeti recept, ali višja."
|
||||
|
||||
#: .\cookbook\views\api.py:1466
|
||||
#, fuzzy
|
||||
#| msgid "ID of unit a recipe should have."
|
||||
msgid "Rating a recipe should have or smaller."
|
||||
msgstr "ID enote, ki bi jo moral imeti recept."
|
||||
msgstr "Ocena, ki jo mora imeti recept, je 1 ali manjša."
|
||||
|
||||
#: .\cookbook\views\api.py:1468
|
||||
msgid "Filter recipes cooked X times."
|
||||
msgstr ""
|
||||
msgstr "Filtriraj recepte, kuhane X-krat."
|
||||
|
||||
#: .\cookbook\views\api.py:1469
|
||||
#, fuzzy
|
||||
#| msgid ""
|
||||
#| "Filter recipes cooked X times or more. Negative values returns cooked "
|
||||
#| "less than X times"
|
||||
msgid "Filter recipes cooked X times or more."
|
||||
msgstr ""
|
||||
"Filtrirajte recepte, kuhane X-krat ali večkrat. Negativne vrednosti se "
|
||||
"vrnejo kuhane manj kot X-krat"
|
||||
msgstr "Filtriraj recepte, kuhane X-krat ali večkrat."
|
||||
|
||||
#: .\cookbook\views\api.py:1470
|
||||
msgid "Filter recipes cooked X times or less."
|
||||
msgstr ""
|
||||
msgstr "Filtriraj recepte, kuhane X-krat ali manj."
|
||||
|
||||
#: .\cookbook\views\api.py:1472
|
||||
#, fuzzy
|
||||
#| msgid "Filter for entries with the given recipe"
|
||||
msgid "Filter recipes created on the given date."
|
||||
msgstr "Filter za vnose z danim receptom"
|
||||
msgstr "Filtriraj recepte, ustvarjene na določen datum."
|
||||
|
||||
#: .\cookbook\views\api.py:1473
|
||||
#, fuzzy
|
||||
#| msgid ""
|
||||
#| "Filter recipes created on or after YYYY-MM-DD. Prepending - filters on or "
|
||||
#| "before date."
|
||||
msgid "Filter recipes created on the given date or after."
|
||||
msgstr ""
|
||||
"Filtrirajte recepte, ustvarjene LLLL-MM-DD ali pozneje. Pred - filtrira na "
|
||||
"ali pred datumom."
|
||||
msgstr "Filtriraj recepte, ustvarjene na določen datum ali pozneje."
|
||||
|
||||
#: .\cookbook\views\api.py:1474
|
||||
#, fuzzy
|
||||
#| msgid ""
|
||||
#| "Filter recipes created on or after YYYY-MM-DD. Prepending - filters on or "
|
||||
#| "before date."
|
||||
msgid "Filter recipes created on the given date or before."
|
||||
msgstr ""
|
||||
"Filtrirajte recepte, ustvarjene LLLL-MM-DD ali pozneje. Pred - filtrira na "
|
||||
"ali pred datumom."
|
||||
msgstr "Filtriraj recepte, ustvarjene na določen datum ali prej."
|
||||
|
||||
#: .\cookbook\views\api.py:1476 .\cookbook\views\api.py:1477
|
||||
#: .\cookbook\views\api.py:1478
|
||||
#, fuzzy
|
||||
#| msgid "Filter for entries with the given recipe"
|
||||
msgid "Filter recipes updated on the given date."
|
||||
msgstr "Filter za vnose z danim receptom"
|
||||
msgstr "Filtriraj recepte, posodobljene na navedeni datum."
|
||||
|
||||
#: .\cookbook\views\api.py:1480
|
||||
#, fuzzy
|
||||
#| msgid ""
|
||||
#| "Filter recipes last cooked on or after YYYY-MM-DD. Prepending - filters "
|
||||
#| "on or before date."
|
||||
msgid "Filter recipes last cooked on the given date or after."
|
||||
msgstr ""
|
||||
"Filtriraj recepte, nazadnje kuhane LLLL-MM-DD ali pozneje. Pred - filtrira "
|
||||
"na ali pred datumom."
|
||||
"Filtriraj recepte, ki so bili nazadnje kuhani na določen datum ali pozneje."
|
||||
|
||||
#: .\cookbook\views\api.py:1481
|
||||
#, fuzzy
|
||||
#| msgid ""
|
||||
#| "Filter recipes last cooked on or after YYYY-MM-DD. Prepending - filters "
|
||||
#| "on or before date."
|
||||
msgid "Filter recipes last cooked on the given date or before."
|
||||
msgstr ""
|
||||
"Filtriraj recepte, nazadnje kuhane LLLL-MM-DD ali pozneje. Pred - filtrira "
|
||||
"na ali pred datumom."
|
||||
"Filtriraj recepte, ki so bili nazadnje kuhani na določen datum ali prej."
|
||||
|
||||
#: .\cookbook\views\api.py:1483 .\cookbook\views\api.py:1484
|
||||
#, fuzzy
|
||||
#| msgid ""
|
||||
#| "Filter recipes lasts viewed on or after YYYY-MM-DD. Prepending - filters "
|
||||
#| "on or before date."
|
||||
msgid "Filter recipes lasts viewed on the given date."
|
||||
msgstr ""
|
||||
"Recepti filtrov so zadnjič prikazani LLLL-MM-DD ali pozneje. Pred - filtrira "
|
||||
"na ali pred datumom."
|
||||
msgstr "Filtriraj recepte, ki so bili nazadnje ogledani na določen datum."
|
||||
|
||||
#: .\cookbook\views\api.py:1486
|
||||
#, fuzzy
|
||||
#| msgid "Filter for entries with the given recipe"
|
||||
msgid "Filter recipes for ones created by the given user ID"
|
||||
msgstr "Filter za vnose z danim receptom"
|
||||
msgstr "Filtriraj recepte po tistih, ki jih je ustvaril dani uporabniški ID"
|
||||
|
||||
#: .\cookbook\views\api.py:1487
|
||||
msgid "If only internal recipes should be returned. [true/<b>false</b>]"
|
||||
@@ -2021,6 +1958,9 @@ msgid ""
|
||||
"lastcooked,-lastcooked,rating,-rating,times_cooked,-times_cooked,created_at,-"
|
||||
"created_at,lastviewed,-lastviewed"
|
||||
msgstr ""
|
||||
"Določa vrstni red rezultatov. Možnosti so: "
|
||||
"score,-score,name,-name,lastcooked,-lastcooked,rating,-rating,times_cooked,-times_cooked,created_at,-created_at,lastviewed,-lastviewed"
|
||||
""
|
||||
|
||||
#: .\cookbook\views\api.py:1492
|
||||
msgid "Returns new results first in search results. [true/<b>false</b>]"
|
||||
@@ -2031,10 +1971,12 @@ msgid ""
|
||||
"Returns the given number of recently viewed recipes before search results "
|
||||
"(if given)"
|
||||
msgstr ""
|
||||
"Vrne podano število nedavno ogledanih receptov pred rezultati iskanja "
|
||||
"(če je podano)"
|
||||
|
||||
#: .\cookbook\views\api.py:1494
|
||||
msgid "ID of a custom filter. Returns all recipes matched by that filter."
|
||||
msgstr ""
|
||||
msgstr "ID filtra po meri. Vrne vse recepte, ki se ujemajo s tem filtrom."
|
||||
|
||||
#: .\cookbook\views\api.py:1495
|
||||
msgid "Filter recipes that can be made with OnHand food. [true/<b>false</b>]"
|
||||
@@ -2047,48 +1989,43 @@ msgid ""
|
||||
"Return the PropertyTypes matching the property category. Repeat for "
|
||||
"multiple."
|
||||
msgstr ""
|
||||
"Vrne vrste lastnosti (PropertyTypes), ki ustrezajo kategoriji lastnosti. "
|
||||
"Ponovite za več vrst lastnosti."
|
||||
|
||||
#: .\cookbook\views\api.py:1804 .\cookbook\views\api.py:1860
|
||||
#, fuzzy
|
||||
#| msgid "Filter for entries with the given recipe"
|
||||
msgid "Returns only entries associated with the given mealplan id"
|
||||
msgstr "Filter za vnose z danim receptom"
|
||||
msgstr "Vrne samo vnose, povezane z danim ID-jem načrta obrokov"
|
||||
|
||||
#: .\cookbook\views\api.py:1858
|
||||
msgid ""
|
||||
"Returns only elements updated after the given timestamp in ISO 8601 format."
|
||||
msgstr ""
|
||||
"Vrne samo elemente, posodobljene po danem časovnem žigu v formatu ISO 8601."
|
||||
|
||||
#: .\cookbook\views\api.py:2031
|
||||
#, fuzzy
|
||||
#| msgid ""
|
||||
#| "Return the Automations matching the automation type. Multiple values "
|
||||
#| "allowed."
|
||||
msgid ""
|
||||
"Return the Automations matching the automation type. Repeat for multiple."
|
||||
msgstr ""
|
||||
"Vrnite avtomatizacije, ki ustrezajo vrsti avtomatizacije. Dovoljenih je "
|
||||
"več vrednosti."
|
||||
"Vrne avtomatizacije, ki ustrezajo vrsti avtomatizacije. Ponovite za več "
|
||||
"avtomatizacij."
|
||||
|
||||
#: .\cookbook\views\api.py:2048
|
||||
msgid ""
|
||||
"Text field to store data that gets carried over to the UserSpace created "
|
||||
"from the InviteLink"
|
||||
msgstr ""
|
||||
"Besedilno polje za shranjevanje podatkov, ki se prenesejo v uporabniški "
|
||||
"prostor, ustvarjen iz InviteLink"
|
||||
|
||||
#: .\cookbook\views\api.py:2049
|
||||
msgid "Only return InviteLinks that have not been used yet."
|
||||
msgstr ""
|
||||
msgstr "Vrni samo povezave InviteLink, ki še niso bile uporabljene."
|
||||
|
||||
#: .\cookbook\views\api.py:2076
|
||||
#, fuzzy
|
||||
#| msgid ""
|
||||
#| "Return the Automations matching the automation type. Multiple values "
|
||||
#| "allowed."
|
||||
msgid "Return the CustomFilters matching the model type. Repeat for multiple."
|
||||
msgstr ""
|
||||
"Vrnite avtomatizacije, ki ustrezajo vrsti avtomatizacije. Dovoljenih je "
|
||||
"več vrednosti."
|
||||
"Vrne filtre CustomFilters, ki ustrezajo vrsti modela. Ponovite za več "
|
||||
"filtrov."
|
||||
|
||||
#: .\cookbook\views\api.py:2176
|
||||
msgid "Nothing to do."
|
||||
@@ -2112,13 +2049,15 @@ msgstr "Uporabnih podatkov ni bilo mogoče najti."
|
||||
|
||||
#: .\cookbook\views\api.py:2286 .\cookbook\views\api.py:2434
|
||||
msgid "You must select an AI provider to perform your request."
|
||||
msgstr ""
|
||||
msgstr "Za izvedbo vaše zahteve morate izbrati ponudnika umetne inteligence."
|
||||
|
||||
#: .\cookbook\views\api.py:2293 .\cookbook\views\api.py:2441
|
||||
msgid ""
|
||||
"You don't have any credits remaining to use AI or AI features are not "
|
||||
"enabled for your space."
|
||||
msgstr ""
|
||||
"Nimate več kreditov za uporabo umetne inteligence ali pa funkcije umetne "
|
||||
"inteligence niso omogočene za vaš prostor."
|
||||
|
||||
#: .\cookbook\views\api.py:2499 .\cookbook\views\api.py:2667
|
||||
msgid "File is above space limit"
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
29
cookbook/migrations/0232_shoppinglist.py
Normal file
29
cookbook/migrations/0232_shoppinglist.py
Normal file
@@ -0,0 +1,29 @@
|
||||
# Generated by Django 5.2.7 on 2025-11-30 10:19
|
||||
|
||||
import cookbook.models
|
||||
import django.db.models.deletion
|
||||
import django_prometheus.models
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('cookbook', '0231_alter_aiprovider_options_alter_automation_options_and_more'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.CreateModel(
|
||||
name='ShoppingList',
|
||||
fields=[
|
||||
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||
('name', models.CharField(blank=True, default='', max_length=32)),
|
||||
('description', models.TextField(blank=True)),
|
||||
('color', models.CharField(blank=True, max_length=7, null=True)),
|
||||
('created_at', models.DateTimeField(auto_now_add=True)),
|
||||
('updated_at', models.DateTimeField(auto_now=True)),
|
||||
('space', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='cookbook.space')),
|
||||
],
|
||||
bases=(django_prometheus.models.ExportModelOperationsMixin('shopping_list'), models.Model, cookbook.models.PermissionModelMixin),
|
||||
),
|
||||
]
|
||||
@@ -1157,7 +1157,7 @@ class Comment(ExportModelOperationsMixin('comment'), models.Model, PermissionMod
|
||||
|
||||
def __str__(self):
|
||||
return self.text
|
||||
|
||||
|
||||
class Meta:
|
||||
ordering = ('pk',)
|
||||
|
||||
@@ -1304,6 +1304,18 @@ class ShoppingListRecipe(ExportModelOperationsMixin('shopping_list_recipe'), mod
|
||||
ordering = ('pk',)
|
||||
|
||||
|
||||
class ShoppingList(ExportModelOperationsMixin('shopping_list'), models.Model, PermissionModelMixin):
|
||||
name = models.CharField(max_length=32, blank=True, default='')
|
||||
description = models.TextField(blank=True)
|
||||
color = models.CharField(max_length=7, blank=True, null=True)
|
||||
|
||||
created_at = models.DateTimeField(auto_now_add=True)
|
||||
updated_at = models.DateTimeField(auto_now=True)
|
||||
|
||||
space = models.ForeignKey(Space, on_delete=models.CASCADE)
|
||||
objects = ScopedManager(space='space')
|
||||
|
||||
|
||||
class ShoppingListEntry(ExportModelOperationsMixin('shopping_list_entry'), models.Model, PermissionModelMixin):
|
||||
list_recipe = models.ForeignKey(ShoppingListRecipe, on_delete=models.CASCADE, null=True, blank=True, related_name='entries')
|
||||
food = models.ForeignKey(Food, on_delete=models.CASCADE, related_name='shopping_entries')
|
||||
|
||||
@@ -37,7 +37,7 @@ from cookbook.models import (Automation, BookmarkletImport, Comment, CookLog, Cu
|
||||
ShareLink, ShoppingListEntry, ShoppingListRecipe, Space,
|
||||
Step, Storage, Supermarket, SupermarketCategory,
|
||||
SupermarketCategoryRelation, Sync, SyncLog, Unit, UnitConversion,
|
||||
UserFile, UserPreference, UserSpace, ViewLog, ConnectorConfig, SearchPreference, SearchFields, AiLog, AiProvider)
|
||||
UserFile, UserPreference, UserSpace, ViewLog, ConnectorConfig, SearchPreference, SearchFields, AiLog, AiProvider, ShoppingList)
|
||||
from cookbook.templatetags.custom_tags import markdown
|
||||
from recipes.settings import AWS_ENABLED, MEDIA_URL, EMAIL_HOST
|
||||
|
||||
@@ -1397,6 +1397,20 @@ class ShoppingListRecipeSerializer(serializers.ModelSerializer):
|
||||
read_only_fields = ('id', 'created_by',)
|
||||
|
||||
|
||||
class ShoppingListSerializer(SpacedModelSerializer, WritableNestedModelSerializer):
|
||||
|
||||
def create(self, validated_data):
|
||||
validated_data['name'] = validated_data['name'].strip()
|
||||
space = validated_data.pop('space', self.context['request'].space)
|
||||
obj, created = ShoppingList.objects.get_or_create(name__iexact=validated_data['name'], space=space, defaults=validated_data)
|
||||
return obj
|
||||
|
||||
class Meta:
|
||||
model = ShoppingList
|
||||
fields = ('id', 'name', 'description', 'color', 'created_at', 'updated_at',)
|
||||
read_only_fields = ('id', 'created_at', 'updated_at',)
|
||||
|
||||
|
||||
class ShoppingListEntrySerializer(WritableNestedModelSerializer):
|
||||
food = FoodSerializer(allow_null=True)
|
||||
unit = UnitSerializer(allow_null=True, required=False)
|
||||
@@ -1729,6 +1743,7 @@ class GenericModelReferenceSerializer(serializers.Serializer):
|
||||
model = serializers.CharField()
|
||||
name = serializers.CharField()
|
||||
|
||||
|
||||
# Export/Import Serializers
|
||||
|
||||
class KeywordExportSerializer(KeywordSerializer):
|
||||
|
||||
@@ -40,6 +40,7 @@ router.register(r'recipe-book-entry', api.RecipeBookEntryViewSet)
|
||||
router.register(r'unit-conversion', api.UnitConversionViewSet)
|
||||
router.register(r'property-type', api.PropertyTypeViewSet) # NOTE: if regenerating the legacy API these need renamed to food-property
|
||||
router.register(r'property', api.PropertyViewSet)
|
||||
router.register(r'shopping-list', api.ShoppingListViewSet)
|
||||
router.register(r'shopping-list-entry', api.ShoppingListEntryViewSet)
|
||||
router.register(r'shopping-list-recipe', api.ShoppingListRecipeViewSet)
|
||||
router.register(r'space', api.SpaceViewSet)
|
||||
|
||||
@@ -88,7 +88,7 @@ from cookbook.models import (Automation, BookmarkletImport, ConnectorConfig, Coo
|
||||
RecipeBookEntry, ShareLink, ShoppingListEntry,
|
||||
ShoppingListRecipe, Space, Step, Storage, Supermarket, SupermarketCategory,
|
||||
SupermarketCategoryRelation, Sync, SyncLog, Unit, UnitConversion,
|
||||
UserFile, UserPreference, UserSpace, ViewLog, RecipeImport, SearchPreference, SearchFields, AiLog, AiProvider
|
||||
UserFile, UserPreference, UserSpace, ViewLog, RecipeImport, SearchPreference, SearchFields, AiLog, AiProvider, ShoppingList
|
||||
)
|
||||
from cookbook.provider.dropbox import Dropbox
|
||||
from cookbook.provider.local import Local
|
||||
@@ -114,7 +114,7 @@ from cookbook.serializer import (AccessTokenSerializer, AutomationSerializer, Au
|
||||
LocalizationSerializer, ServerSettingsSerializer, RecipeFromSourceResponseSerializer, ShoppingListEntryBulkCreateSerializer, FdcQuerySerializer,
|
||||
AiImportSerializer, ImportOpenDataSerializer, ImportOpenDataMetaDataSerializer, ImportOpenDataResponseSerializer, ExportRequestSerializer,
|
||||
RecipeImportSerializer, ConnectorConfigSerializer, SearchPreferenceSerializer, SearchFieldsSerializer, RecipeBatchUpdateSerializer,
|
||||
AiProviderSerializer, AiLogSerializer, FoodBatchUpdateSerializer, GenericModelReferenceSerializer
|
||||
AiProviderSerializer, AiLogSerializer, FoodBatchUpdateSerializer, GenericModelReferenceSerializer, ShoppingListSerializer
|
||||
)
|
||||
from cookbook.version_info import TANDOOR_VERSION
|
||||
from cookbook.views.import_export import get_integration
|
||||
@@ -307,7 +307,8 @@ class FuzzyFilterMixin(viewsets.ModelViewSet, ExtendedRecipeMixin):
|
||||
filter = Q(name__icontains=query)
|
||||
if self.request.user.is_authenticated:
|
||||
if any([self.model.__name__.lower() in x for x in
|
||||
self.request.user.searchpreference.unaccent.values_list('field', flat=True)]):
|
||||
self.request.user.searchpreference.unaccent.values_list('field', flat=True)]) and (
|
||||
settings.DATABASES['default']['ENGINE'] == 'django.db.backends.postgresql'):
|
||||
filter |= Q(name__unaccent__icontains=query)
|
||||
|
||||
self.queryset = (
|
||||
@@ -2010,6 +2011,17 @@ class ShoppingListRecipeViewSet(LoggingMixin, viewsets.ModelViewSet):
|
||||
return Response(serializer.errors, 400)
|
||||
|
||||
|
||||
class ShoppingListViewSet(LoggingMixin, viewsets.ModelViewSet, DeleteRelationMixing):
|
||||
queryset = ShoppingList.objects
|
||||
serializer_class = ShoppingListSerializer
|
||||
permission_classes = [CustomIsUser & CustomTokenHasReadWriteScope]
|
||||
pagination_class = DefaultPagination
|
||||
|
||||
def get_queryset(self):
|
||||
queryset = self.queryset.filter(space=self.request.space).all()
|
||||
return queryset
|
||||
|
||||
|
||||
@extend_schema_view(list=extend_schema(parameters=[
|
||||
OpenApiParameter(name='updated_after',
|
||||
description=_('Returns only elements updated after the given timestamp in ISO 8601 format.'),
|
||||
@@ -3056,11 +3068,20 @@ def meal_plans_to_ical(queryset, filename):
|
||||
for p in queryset:
|
||||
event = Event()
|
||||
event['uid'] = p.id
|
||||
event.add('dtstart', p.from_date)
|
||||
|
||||
start_date_time = p.from_date
|
||||
end_date_time = p.from_date
|
||||
|
||||
if p.to_date:
|
||||
event.add('dtend', p.to_date)
|
||||
else:
|
||||
event.add('dtend', p.from_date)
|
||||
end_date_time = p.to_date
|
||||
|
||||
if p.meal_type.time:
|
||||
start_date_time = datetime.datetime.combine(p.from_date, p.meal_type.time)
|
||||
end_date_time = datetime.datetime.combine(p.to_date, p.meal_type.time) + datetime.timedelta(minutes=60)
|
||||
|
||||
event.add('dtstart', start_date_time)
|
||||
event.add('dtend', end_date_time)
|
||||
|
||||
event['summary'] = f'{p.meal_type.name}: {p.get_label()}'
|
||||
event['description'] = p.note
|
||||
cal.add_component(event)
|
||||
|
||||
@@ -43,7 +43,10 @@ def index(request, path=None, resource=None):
|
||||
return HttpResponseRedirect(reverse_lazy('view_setup'))
|
||||
|
||||
if 'signup_token' in request.session:
|
||||
return HttpResponseRedirect(reverse('view_invite', args=[request.session.pop('signup_token', '')]))
|
||||
value = request.session['signup_token']
|
||||
del request.session['signup_token']
|
||||
request.session.modified = True
|
||||
return HttpResponseRedirect(reverse('view_invite', args=[value]))
|
||||
|
||||
if request.user.is_authenticated or re.search(r'/recipe/\d+/', request.path[:512]) and request.GET.get('share'):
|
||||
return render(request, 'frontend/tandoor.html', {})
|
||||
|
||||
@@ -675,4 +675,4 @@ DISABLE_EXTERNAL_CONNECTORS = extract_bool('DISABLE_EXTERNAL_CONNECTORS', False)
|
||||
EXTERNAL_CONNECTORS_QUEUE_SIZE = int(os.getenv('EXTERNAL_CONNECTORS_QUEUE_SIZE', 100))
|
||||
|
||||
mimetypes.add_type("text/javascript", ".js", True)
|
||||
mimetypes.add_type("text/javascript", ".mjs", True)
|
||||
mimetypes.add_type("text/javascript", ".mjs", True)
|
||||
@@ -29,7 +29,7 @@ microdata==0.8.0
|
||||
mock==5.2.0
|
||||
Jinja2==3.1.6
|
||||
django-allauth[mfa,socialaccount]==65.9.0
|
||||
recipe-scrapers==15.9.0
|
||||
recipe-scrapers==15.10.0
|
||||
django-scopes==2.0.0
|
||||
django-treebeard==4.7.1
|
||||
django-cors-headers==4.6.0
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
<template>
|
||||
<v-app>
|
||||
<v-app-bar color="tandoor" flat density="comfortable" v-if="!useUserPreferenceStore().isAuthenticated">
|
||||
<v-app-bar color="tandoor" flat density="comfortable" v-if="!useUserPreferenceStore().isAuthenticated && !useUserPreferenceStore().isPrintMode">
|
||||
|
||||
</v-app-bar>
|
||||
<v-app-bar :color="useUserPreferenceStore().activeSpace.navBgColor ? useUserPreferenceStore().activeSpace.navBgColor : useUserPreferenceStore().userSettings.navBgColor"
|
||||
flat density="comfortable" v-if="useUserPreferenceStore().isAuthenticated" :scroll-behavior="useUserPreferenceStore().userSettings.navSticky ? '' : 'hide'">
|
||||
flat density="comfortable" v-if="useUserPreferenceStore().isAuthenticated && !useUserPreferenceStore().isPrintMode" :scroll-behavior="useUserPreferenceStore().userSettings.navSticky ? '' : 'hide'">
|
||||
<router-link :to="{ name: 'StartPage', params: {} }">
|
||||
<v-img src="../../assets/brand_logo.svg" width="140px" class="ms-2"
|
||||
v-if="useUserPreferenceStore().userSettings.navShowLogo && !useUserPreferenceStore().activeSpace.navLogo"></v-img>
|
||||
@@ -58,7 +58,7 @@
|
||||
</p>
|
||||
</v-app-bar>
|
||||
|
||||
<v-app-bar color="info" density="compact" v-if="useUserPreferenceStore().isAuthenticated && useUserPreferenceStore().activeSpace.message != ''">
|
||||
<v-app-bar color="info" density="compact" v-if="useUserPreferenceStore().isAuthenticated && useUserPreferenceStore().activeSpace.message != '' && !useUserPreferenceStore().isPrintMode">
|
||||
<p class="text-center w-100">
|
||||
{{ useUserPreferenceStore().activeSpace.message }}
|
||||
</p>
|
||||
@@ -69,7 +69,7 @@
|
||||
</v-main>
|
||||
|
||||
<!-- completely hide in print mode because setting d-print-node keeps layout -->
|
||||
<v-navigation-drawer v-if="lgAndUp && useUserPreferenceStore().isAuthenticated && !isPrintMode">
|
||||
<v-navigation-drawer v-if="lgAndUp && useUserPreferenceStore().isAuthenticated && !useUserPreferenceStore().isPrintMode">
|
||||
<v-list nav>
|
||||
<v-list-item :to="{ name: 'SettingsPage', params: {} }">
|
||||
<template #prepend>
|
||||
@@ -96,7 +96,7 @@
|
||||
|
||||
</v-navigation-drawer>
|
||||
|
||||
<v-bottom-navigation grow v-if="useUserPreferenceStore().isAuthenticated && !lgAndUp">
|
||||
<v-bottom-navigation grow v-if="useUserPreferenceStore().isAuthenticated && !lgAndUp && !useUserPreferenceStore().isPrintMode">
|
||||
<v-btn value="recent" :to="{ name: 'StartPage', params: {} }">
|
||||
<v-icon icon="fa-fw fas fa-book "/>
|
||||
</v-btn>
|
||||
@@ -133,28 +133,21 @@ import GlobalSearchDialog from "@/components/inputs/GlobalSearchDialog.vue"
|
||||
|
||||
import {useDisplay, useLocale} from "vuetify"
|
||||
import VSnackbarQueued from "@/components/display/VSnackbarQueued.vue";
|
||||
import MessageListDialog from "@/components/dialogs/MessageListDialog.vue";
|
||||
import {useUserPreferenceStore} from "@/stores/UserPreferenceStore";
|
||||
import NavigationDrawerContextMenu from "@/components/display/NavigationDrawerContextMenu.vue";
|
||||
import {useDjangoUrls} from "@/composables/useDjangoUrls";
|
||||
import {nextTick, onMounted} from "vue";
|
||||
import {nextTick, onMounted, ref} from "vue";
|
||||
import {isSpaceAboveLimit} from "@/utils/logic_utils";
|
||||
import {useMediaQuery, useTitle} from "@vueuse/core";
|
||||
import {useTitle} from "@vueuse/core";
|
||||
import HelpDialog from "@/components/dialogs/HelpDialog.vue";
|
||||
import {NAVIGATION_DRAWER} from "@/utils/navigation.ts";
|
||||
import {useNavigation} from "@/composables/useNavigation.ts";
|
||||
import {useRouter} from "vue-router";
|
||||
import {useI18n} from "vue-i18n";
|
||||
|
||||
const {lgAndUp} = useDisplay()
|
||||
const {getDjangoUrl} = useDjangoUrls()
|
||||
const {t} = useI18n()
|
||||
|
||||
const title = useTitle()
|
||||
const router = useRouter()
|
||||
const i18n = useI18n()
|
||||
|
||||
const isPrintMode = useMediaQuery('print')
|
||||
|
||||
onMounted(() => {
|
||||
useUserPreferenceStore().init().then(() => {
|
||||
|
||||
@@ -10,15 +10,15 @@
|
||||
<!-- <v-text-field density="compact" variant="outlined" class="pt-2 pb-2" :label="$t('Search')" hide-details clearable></v-text-field>-->
|
||||
<!-- </v-list-item>-->
|
||||
<!-- <v-divider></v-divider>-->
|
||||
<v-list-item link title="Start" @click="window = 'start'" prepend-icon="fa-solid fa-house"></v-list-item>
|
||||
<v-list-item link title="Space" @click="window = 'space'" prepend-icon="fa-solid fa-database"></v-list-item>
|
||||
<v-list-item link :title="$t('Start')" @click="window = 'start'" prepend-icon="fa-solid fa-house"></v-list-item>
|
||||
<v-list-item link :title="$t('Space')" @click="window = 'space'" prepend-icon="fa-solid fa-database"></v-list-item>
|
||||
<v-list-item link :title="$t('Recipes')" @click="window = 'recipes'" prepend-icon="$recipes"></v-list-item>
|
||||
<v-list-item link :title="$t('Import')" @click="window = 'import'" prepend-icon="$import"></v-list-item>
|
||||
<v-list-item link :title="$t('AI')" @click="window = 'ai'" prepend-icon="$ai"></v-list-item>
|
||||
<v-list-item link :title="$t('Unit')" @click="window = 'unit'" prepend-icon="fa-solid fa-scale-balanced"></v-list-item>
|
||||
<v-list-item link :title="$t('Food')" @click="window = 'food'" prepend-icon="fa-solid fa-carrot"></v-list-item>
|
||||
<v-list-item link :title="$t('Keyword')" @click="window = 'keyword'" prepend-icon="fa-solid fa-tags"></v-list-item>
|
||||
<v-list-item link title="Recipe Structure" @click="window = 'recipe_structure'" prepend-icon="fa-solid fa-diagram-project"></v-list-item>
|
||||
<v-list-item link :title="$t('Recipe Structure')" @click="window = 'recipe_structure'" prepend-icon="fa-solid fa-diagram-project"></v-list-item>
|
||||
<v-list-item link :title="$t('Properties')" @click="window = 'properties'" prepend-icon="fa-solid fa-database"></v-list-item>
|
||||
<v-list-item link :title="$t('Search')" @click="window = 'recipe_search'" prepend-icon="$search"></v-list-item>
|
||||
<v-list-item link :title="$t('SavedSearch')" @click="window = 'search_filter'" prepend-icon="fa-solid fa-sd-card"></v-list-item>
|
||||
@@ -31,6 +31,8 @@
|
||||
|
||||
<v-main>
|
||||
<v-container>
|
||||
<v-select v-model="window" :items="mobileMenuItems" class="d-block d-lg-none"> </v-select>
|
||||
|
||||
<v-window v-model="window">
|
||||
<v-window-item value="start">
|
||||
<h2>Welcome to Tandoor 2</h2>
|
||||
@@ -46,7 +48,8 @@
|
||||
<v-btn class="mt-2 ms-2" color="info" href="https://github.com/TandoorRecipes/recipes" target="_blank" prepend-icon="fa-solid fa-code-branch">GitHub
|
||||
</v-btn>
|
||||
|
||||
<v-alert class="mt-3" border="start" variant="tonal" color="success" v-if="(!useUserPreferenceStore().serverSettings.hosted && !useUserPreferenceStore().activeSpace.demo)">
|
||||
<v-alert class="mt-3" border="start" variant="tonal" color="success"
|
||||
v-if="(!useUserPreferenceStore().serverSettings.hosted && !useUserPreferenceStore().activeSpace.demo)">
|
||||
<v-alert-title>Did you know?</v-alert-title>
|
||||
Tandoor is Open Source and available to anyone for free to host on their own server. Thousands of hours have been spend
|
||||
making Tandoor what it is today. You can help make Tandoor even better by contributing or helping financing the effort.
|
||||
@@ -60,10 +63,12 @@
|
||||
|
||||
</v-window-item>
|
||||
<v-window-item value="space">
|
||||
<p class="mt-3">All your data is stored in a Space where you can invite other people to collaborate on your recipe database. Typcially the members of a space
|
||||
<p class="mt-3">All your data is stored in a Space where you can invite other people to collaborate on your recipe database. Typcially the members of a
|
||||
space
|
||||
belong to one family/household/organization.</p>
|
||||
|
||||
<p class="mt-3">While everyone can access all recipes by default, Books, Shopping Lists and Mealplans are not shared by default. You can share them with other
|
||||
<p class="mt-3">While everyone can access all recipes by default, Books, Shopping Lists and Mealplans are not shared by default. You can share them with
|
||||
other
|
||||
members of your space
|
||||
using the settings.
|
||||
</p>
|
||||
@@ -77,19 +82,24 @@
|
||||
|
||||
</v-window-item>
|
||||
<v-window-item value="recipes">
|
||||
<p class="mt-3">Recipes are the foundation of your Tandoor space. A Recipe has one or more steps that contain ingredients, instructions and other information.
|
||||
<p class="mt-3">Recipes are the foundation of your Tandoor space. A Recipe has one or more steps that contain ingredients, instructions and other
|
||||
information.
|
||||
Ingredients in turn consist of an amount, a unit and a food, allowing recipes to be scaled, nutrition's to be calculated and shopping to be organized.
|
||||
</p>
|
||||
|
||||
<p class="mt-3">Besides manually creating them you can also import them from various different places.
|
||||
</p>
|
||||
<p class="mt-3">Recipes, by default, are visible to all members of your space. Setting them to private means only you can see it. After setting it to private you
|
||||
<p class="mt-3">Recipes, by default, are visible to all members of your space. Setting them to private means only you can see it. After setting it to
|
||||
private you
|
||||
can manually specify the people who should be able to view the recipe.
|
||||
You can also create a share link for the recipe to share it with everyone that has access to the link.
|
||||
</p>
|
||||
<p class="mt-3"></p>
|
||||
|
||||
<v-btn color="primary" variant="tonal" prepend-icon="$create" class="me-2" :to="{name: 'ModelEditPage', params: {model: 'Recipe'}}">{{ $t('Create') }}</v-btn>
|
||||
<v-btn color="primary" variant="tonal" prepend-icon="$create" class="me-2" :to="{name: 'ModelEditPage', params: {model: 'Recipe'}}">{{
|
||||
$t('Create')
|
||||
}}
|
||||
</v-btn>
|
||||
<v-btn color="primary" variant="tonal" prepend-icon="$search" class="me-2" :to="{name: 'SearchPage'}">{{ $t('Search') }}</v-btn>
|
||||
|
||||
</v-window-item>
|
||||
@@ -119,7 +129,8 @@
|
||||
</p>
|
||||
|
||||
<p class="mt-3" v-if="useUserPreferenceStore().serverSettings.hosted">
|
||||
To prevent accidental AI cost you can review your AI usage using the AI Log. The Server Administrator can also set AI usage limits for your space (either monthly or using a balance).
|
||||
To prevent accidental AI cost you can review your AI usage using the AI Log. The Server Administrator can also set AI usage limits for your space
|
||||
(either monthly or using a balance).
|
||||
</p>
|
||||
<p class="mt-3" v-if="!useUserPreferenceStore().serverSettings.hosted">
|
||||
Depending on your subscription you will have different AI Credits available for your space every month. Additionally you might have a Credit balance
|
||||
@@ -153,7 +164,8 @@
|
||||
<v-btn color="primary" variant="tonal" prepend-icon="fa-solid fa-scale-balanced" class="me-2" :to="{name: 'ModelListPage', params: {model: 'Unit'}}">
|
||||
{{ $t('Unit') }}
|
||||
</v-btn>
|
||||
<v-btn color="primary" variant="tonal" prepend-icon="fa-solid fa-exchange-alt" class="me-2" :to="{name: 'ModelListPage', params: {model: 'UnitConversion'}}">
|
||||
<v-btn color="primary" variant="tonal" prepend-icon="fa-solid fa-exchange-alt" class="me-2"
|
||||
:to="{name: 'ModelListPage', params: {model: 'UnitConversion'}}">
|
||||
{{ $t('Conversion') }}
|
||||
</v-btn>
|
||||
|
||||
@@ -223,7 +235,8 @@
|
||||
calculate the property amount if a Food is given in a different unit (e.g. 1kg or 1 cup).
|
||||
</p>
|
||||
|
||||
<v-btn color="primary" variant="tonal" prepend-icon="fa-solid fa-database" class="me-2 mt-2 mb-2" :to="{name: 'ModelListPage', params: {model: 'PropertyType'}}">
|
||||
<v-btn color="primary" variant="tonal" prepend-icon="fa-solid fa-database" class="me-2 mt-2 mb-2"
|
||||
:to="{name: 'ModelListPage', params: {model: 'PropertyType'}}">
|
||||
{{ $t('Property') }}
|
||||
</v-btn>
|
||||
<h3>Editor</h3>
|
||||
@@ -294,7 +307,8 @@
|
||||
</p>
|
||||
|
||||
<p class="mt-3">
|
||||
You can assign Supermarket Categories to your Foods, either trough the Food Editor or directly by clicking on a Shopping List Entry, to automatically sort the list
|
||||
You can assign Supermarket Categories to your Foods, either trough the Food Editor or directly by clicking on a Shopping List Entry, to automatically
|
||||
sort the list
|
||||
according to the Category Order defined in the Supermarket.
|
||||
</p>
|
||||
|
||||
@@ -333,7 +347,8 @@
|
||||
|
||||
<p class="mt-3">
|
||||
When selecting a Recipe in a Meal Plan you can automatically add its ingredients to the shopping list. You can also manually add more entries trough the
|
||||
shopping tab in the Meal Plan editor. When deleting a Meal Plan all Shopping List Entries associated with that Meal Plan are deleted as well. When changing the
|
||||
shopping tab in the Meal Plan editor. When deleting a Meal Plan all Shopping List Entries associated with that Meal Plan are deleted as well. When
|
||||
changing the
|
||||
number of servings in a Meal Plan the Servings of the connected Recipe in the Shopping list are automatically changed as well.
|
||||
|
||||
</p>
|
||||
@@ -368,10 +383,30 @@
|
||||
|
||||
import {ref} from "vue";
|
||||
import {useUserPreferenceStore} from "@/stores/UserPreferenceStore.ts";
|
||||
import {useI18n} from "vue-i18n";
|
||||
|
||||
const {t} = useI18n()
|
||||
const drawer = defineModel()
|
||||
const window = ref('start')
|
||||
|
||||
const mobileMenuItems = ref([
|
||||
{title: t('Start'), props: {prependIcon: 'fa-solid fa-house'}, value: 'start'},
|
||||
{title: t('Space'), props: {prependIcon: 'fa-solid fa-database'}, value: 'space'},
|
||||
{title: t('Recipes'), props: {prependIcon: '$recipes'}, value: 'recipes'},
|
||||
{title: t('Import'), props: {prependIcon: '$import'}, value: 'import'},
|
||||
{title: t('AI'), props: {prependIcon: '$ai'}, value: 'ai'},
|
||||
{title: t('Unit'), props: {prependIcon: 'fa-solid fa-scale-balanced'}, value: 'unit'},
|
||||
{title: t('Food'), props: {prependIcon: 'fa-solid fa-carrot'}, value: 'food'},
|
||||
{title: t('Keyword'), props: {prependIcon: 'fa-solid fa-tags'}, value: 'keyword'},
|
||||
{title: t('RecipeStructure'), props: {prependIcon: 'fa-solid fa-diagram-project'}, value: 'recipe_structure'},
|
||||
{title: t('Properties'), props: {prependIcon: 'fa-solid fa-database'}, value: 'properties'},
|
||||
{title: t('Search'), props: {prependIcon: '$search'}, value: 'recipe_search'},
|
||||
{title: t('SavedSearch'), props: {prependIcon: 'fa-solid fa-sd-card'}, value: 'search_filter'},
|
||||
{title: t('Books'), props: {prependIcon: '$books'}, value: 'books'},
|
||||
{title: t('Shopping'), props: {prependIcon: '$shopping'}, value: 'shopping'},
|
||||
{title: t('Meal_Plan'), props: {prependIcon: '$mealplan'}, value: 'meal_plan'}
|
||||
])
|
||||
|
||||
</script>
|
||||
|
||||
|
||||
|
||||
@@ -146,7 +146,11 @@ onMounted(() => {
|
||||
|
||||
function clickMealPlan(plan: MealPlan) {
|
||||
if (plan.recipe) {
|
||||
router.push({name: 'RecipeViewPage', params: {id: plan.recipe.id}})
|
||||
router.push({
|
||||
name: 'RecipeViewPage',
|
||||
params: { id: String(plan.recipe.id) }, // keep id in params
|
||||
query: { servings: String(plan.servings ?? '') } // pass servings as query
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -54,15 +54,13 @@
|
||||
</router-link>
|
||||
<a v-else-if="i.food.url" :href="i.food.url" target="_blank">{{ ingredientToFoodString(i, ingredientFactor) }}</a>
|
||||
<span v-else>{{ ingredientToFoodString(i, ingredientFactor) }}</span>
|
||||
<template v-if="i.note != '' && i.note != undefined">
|
||||
<span class="d-none d-print-block text-disabled font-italic"> {{ i.note }}</span>
|
||||
</template>
|
||||
|
||||
</template>
|
||||
|
||||
|
||||
</td>
|
||||
|
||||
<td style="width: 1%; text-wrap: nowrap" class="d-print-none">
|
||||
<td v-if="useUserPreferenceStore().isPrintMode">
|
||||
<span class="text-disabled font-italic"> {{ i.note }}</span>
|
||||
</td>
|
||||
<td style="width: 1%; text-wrap: nowrap" v-if="!useUserPreferenceStore().isPrintMode">
|
||||
<v-icon class="far fa-comment float-right" v-if="i.note != '' && i.note != undefined">
|
||||
<v-tooltip activator="parent" open-on-click location="start">{{ i.note }}</v-tooltip>
|
||||
</v-icon>
|
||||
|
||||
@@ -37,15 +37,15 @@
|
||||
|
||||
<v-dialog max-width="900px" v-model="dialog">
|
||||
<v-card v-if="dialogProperty" :loading="loading">
|
||||
<v-closable-card-title :title="`${dialogProperty.propertyAmountTotal} ${dialogProperty.unit} ${dialogProperty.name}`" :sub-title="$t('total')" icon="$properties"
|
||||
<v-closable-card-title :title="`${dialogProperty.propertyAmountTotal} ${(dialogProperty.unit != null) ? dialogProperty.unit : ''} ${dialogProperty.name}`" :sub-title="$t('total')" icon="$properties"
|
||||
v-model="dialog"></v-closable-card-title>
|
||||
<v-card-text>
|
||||
<v-list>
|
||||
<v-list-item border v-for="fv in dialogProperty.foodValues" :key="`${dialogProperty.id}_${fv.id}`">
|
||||
<template #prepend>
|
||||
<v-progress-circular size="55" width="5" :model-value="(fv.value/dialogProperty.propertyAmountTotal)*100"
|
||||
:color="colorScale((fv.value/dialogProperty.propertyAmountTotal)*100)" v-if="fv.value != null && dialogProperty.propertyAmountTotal > 0">
|
||||
{{ Math.round((fv.value / dialogProperty.propertyAmountTotal) * 100) }}%
|
||||
<v-progress-circular size="55" width="5" :model-value="(fv.value* props.ingredientFactor/dialogProperty.propertyAmountTotal)*100"
|
||||
:color="colorScale((fv.value* props.ingredientFactor/dialogProperty.propertyAmountTotal)*100)" v-if="fv.value != null && dialogProperty.propertyAmountTotal > 0">
|
||||
{{ Math.round((fv.value* props.ingredientFactor / dialogProperty.propertyAmountTotal) * 100) }}%
|
||||
</v-progress-circular>
|
||||
<v-progress-circular size="55" width="5" v-if="fv.value == null">?</v-progress-circular>
|
||||
</template>
|
||||
@@ -59,7 +59,7 @@
|
||||
<model-edit-dialog model="UnitConversion" @create="refreshRecipe()"
|
||||
:item-defaults="{baseAmount: 1, baseUnit: fv.missing_conversion.base_unit, convertedUnit: fv.missing_conversion.converted_unit, food: fv.food}"></model-edit-dialog>
|
||||
</v-chip>
|
||||
<v-chip v-else-if="fv.value != undefined">{{ $n(fv.value) }} {{ dialogProperty.unit }}</v-chip>
|
||||
<v-chip v-else-if="fv.value != undefined">{{ $n(fv.value * props.ingredientFactor) }} {{ dialogProperty.unit }}</v-chip>
|
||||
<v-chip color="warning" prepend-icon="$edit" class="cursor-pointer" :to="{name: 'ModelEditPage', params: {model: 'Recipe', id: recipe.id}}" v-else-if="fv.missing_unit">
|
||||
{{ $t('NoUnit') }}
|
||||
</v-chip>
|
||||
@@ -101,7 +101,10 @@ type PropertyWrapper = {
|
||||
}
|
||||
|
||||
const props = defineProps({
|
||||
servings: {type: Number, required: true,},
|
||||
ingredientFactor: {
|
||||
type: Number,
|
||||
required: true,
|
||||
},
|
||||
})
|
||||
|
||||
const recipe = defineModel<Recipe>({required: true})
|
||||
@@ -143,7 +146,7 @@ const propertyList = computed(() => {
|
||||
description: rp.propertyType.description,
|
||||
foodValues: [],
|
||||
propertyAmountPerServing: rp.propertyAmount,
|
||||
propertyAmountTotal: rp.propertyAmount * recipe.value.servings * (props.servings / recipe.value.servings),
|
||||
propertyAmountTotal: rp.propertyAmount * recipe.value.servings * props.ingredientFactor,
|
||||
missingValue: false,
|
||||
unit: rp.propertyType.unit,
|
||||
type: rp.propertyType,
|
||||
@@ -161,7 +164,7 @@ const propertyList = computed(() => {
|
||||
icon: fp.icon,
|
||||
foodValues: fp.food_values,
|
||||
propertyAmountPerServing: fp.total_value / recipe.value.servings,
|
||||
propertyAmountTotal: fp.total_value * (props.servings / recipe.value.servings),
|
||||
propertyAmountTotal: fp.total_value * props.ingredientFactor,
|
||||
missingValue: fp.missing_value,
|
||||
unit: fp.unit,
|
||||
type: fp,
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<template>
|
||||
<v-card class="mt-1 d-print-none" v-if="useUserPreferenceStore().isAuthenticated" :loading="loading">
|
||||
<v-card class="mt-1" v-if="useUserPreferenceStore().isAuthenticated && !useUserPreferenceStore().isPrintMode" :loading="loading">
|
||||
<v-card-text>
|
||||
<v-textarea :label="$t('Comment')" rows="2" v-model="newCookLog.comment" auto-grow></v-textarea>
|
||||
<v-row dense>
|
||||
@@ -69,7 +69,7 @@
|
||||
|
||||
<script setup lang="ts">
|
||||
|
||||
import {onMounted, PropType, ref} from "vue";
|
||||
import {onMounted, PropType, ref, watch} from "vue";
|
||||
import {ApiApi, CookLog, Recipe} from "@/openapi";
|
||||
import {DateTime} from "luxon";
|
||||
import {ErrorMessageType, useMessageStore} from "@/stores/MessageStore";
|
||||
@@ -82,6 +82,10 @@ const props = defineProps({
|
||||
type: Object as PropType<Recipe>,
|
||||
required: true
|
||||
},
|
||||
servings: {
|
||||
type: Number,
|
||||
required: true
|
||||
}
|
||||
})
|
||||
|
||||
const newCookLog = ref({} as CookLog);
|
||||
@@ -121,7 +125,7 @@ function recLoadCookLog(recipeId: number, page: number = 1) {
|
||||
*/
|
||||
function resetForm() {
|
||||
newCookLog.value = {} as CookLog
|
||||
newCookLog.value.servings = props.recipe.servings
|
||||
newCookLog.value.servings = props.servings
|
||||
newCookLog.value.createdAt = new Date()
|
||||
newCookLog.value.recipe = props.recipe.id!
|
||||
}
|
||||
@@ -140,6 +144,13 @@ function saveCookLog() {
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* watch for changes in servings prop and update the servings input field
|
||||
*/
|
||||
watch(() => props.servings, (newVal) => {
|
||||
newCookLog.value.servings = newVal
|
||||
})
|
||||
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
<template>
|
||||
<template v-if="!props.loading">
|
||||
|
||||
<router-link :to="{name: 'RecipeViewPage', params: {id: props.recipe.id}}" :target="linkTarget">
|
||||
<router-link :to="dest" :target="linkTarget">
|
||||
<recipe-image :style="{height: props.height}" :recipe="props.recipe" rounded="lg" class="mr-3 ml-3">
|
||||
|
||||
</recipe-image>
|
||||
@@ -36,7 +36,7 @@
|
||||
</div>
|
||||
|
||||
|
||||
<v-card :to="{name: 'RecipeViewPage', params: {id: props.recipe.id}}" :style="{'height': props.height}" v-if="false">
|
||||
<v-card :to="dest" :style="{'height': props.height}" v-if="false">
|
||||
<v-tooltip
|
||||
class="align-center justify-center"
|
||||
location="top center" origin="overlap"
|
||||
@@ -97,7 +97,7 @@
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import {PropType} from 'vue'
|
||||
import {computed, PropType} from 'vue'
|
||||
import KeywordsComponent from "@/components/display/KeywordsBar.vue";
|
||||
import {Recipe, RecipeOverview} from "@/openapi";
|
||||
|
||||
@@ -113,20 +113,29 @@ const props = defineProps({
|
||||
show_description: {type: Boolean, required: false},
|
||||
height: {type: String, required: false, default: '15vh'},
|
||||
linkTarget: {type: String, required: false, default: ''},
|
||||
showMenu: {type: Boolean, default: true, required: false}
|
||||
showMenu: {type: Boolean, default: true, required: false},
|
||||
servings: {type: Number, required: false},
|
||||
})
|
||||
|
||||
const router = useRouter()
|
||||
|
||||
const dest = computed(() => {
|
||||
const route: any = { name: 'RecipeViewPage', params: { id: props.recipe.id } };
|
||||
if (props.servings !== undefined) {
|
||||
route.query = { servings: String(props.servings) };
|
||||
}
|
||||
return route;
|
||||
})
|
||||
|
||||
/**
|
||||
* open the recipe either in the same tab or in a new tab depending on the link target prop
|
||||
*/
|
||||
function openRecipe() {
|
||||
if (props.linkTarget != '') {
|
||||
const routeData = router.resolve({name: 'RecipeViewPage', params: {id: props.recipe.id}});
|
||||
const routeData = router.resolve(dest.value);
|
||||
window.open(routeData.href, props.linkTarget);
|
||||
} else {
|
||||
router.push({name: 'RecipeViewPage', params: {id: props.recipe.id}})
|
||||
router.push(dest.value);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
|
||||
<template v-if="recipe.name != undefined">
|
||||
|
||||
<template class="d-block d-lg-none">
|
||||
<template class="d-block d-lg-none d-print-none">
|
||||
|
||||
<!-- mobile layout -->
|
||||
<v-card class="rounded-0">
|
||||
@@ -25,7 +25,7 @@
|
||||
<span class="ps-2 text-h5 flex-grow-1 pa-1" :class="{'text-truncate': !showFullRecipeName}" @click="showFullRecipeName = !showFullRecipeName">
|
||||
{{ recipe.name }}
|
||||
</span>
|
||||
<recipe-context-menu :recipe="recipe" v-if="useUserPreferenceStore().isAuthenticated"></recipe-context-menu>
|
||||
<recipe-context-menu :recipe="recipe" :servings="servings" v-if="useUserPreferenceStore().isAuthenticated"></recipe-context-menu>
|
||||
</v-sheet>
|
||||
<keywords-component variant="flat" class="ms-1" :keywords="recipe.keywords"></keywords-component>
|
||||
<private-recipe-badge :users="recipe.shared" v-if="recipe._private"></private-recipe-badge>
|
||||
@@ -61,7 +61,7 @@
|
||||
</v-card>
|
||||
</template>
|
||||
<!-- Desktop horizontal layout -->
|
||||
<template class="d-none d-lg-block">
|
||||
<template class="d-none d-lg-block d-print-block">
|
||||
<v-row dense>
|
||||
<v-col cols="8">
|
||||
<recipe-image
|
||||
@@ -75,7 +75,7 @@
|
||||
<v-card-text class="flex-grow-1">
|
||||
<div class="d-flex">
|
||||
<h1 class="flex-column flex-grow-1">{{ recipe.name }}</h1>
|
||||
<recipe-context-menu :recipe="recipe" v-if="useUserPreferenceStore().isAuthenticated"
|
||||
<recipe-context-menu :recipe="recipe" :servings="servings" v-if="useUserPreferenceStore().isAuthenticated"
|
||||
class="flex-column mb-auto mt-2 float-right"></recipe-context-menu>
|
||||
</div>
|
||||
<p>
|
||||
@@ -118,13 +118,13 @@
|
||||
</v-row>
|
||||
</template>
|
||||
|
||||
<template v-if="recipe.filePath">
|
||||
<template v-if="recipe.filePath && !useUserPreferenceStore().isPrintMode">
|
||||
<external-recipe-viewer class="mt-2" :recipe="recipe"></external-recipe-viewer>
|
||||
|
||||
<v-card :title="$t('AI')" prepend-icon="$ai" :loading="fileApiLoading || loading" :disabled="fileApiLoading || loading || !useUserPreferenceStore().activeSpace.aiEnabled"
|
||||
<v-card :title="$t('AI')" prepend-icon="$ai" :loading="fileApiLoading || loading" :disabled="fileApiLoading || loading || !useUserPreferenceStore().activeSpace.aiEnabled"
|
||||
v-if="!recipe.internal">
|
||||
<v-card-text>
|
||||
{{$t('ConvertUsingAI')}}
|
||||
{{ $t('ConvertUsingAI') }}
|
||||
|
||||
<model-select model="AiProvider" v-model="selectedAiProvider">
|
||||
<template #append>
|
||||
@@ -135,7 +135,8 @@
|
||||
</v-card>
|
||||
</template>
|
||||
|
||||
<v-card class="mt-1" v-if="(recipe.steps.length > 1 || (recipe.steps.length == 1 && !recipe.steps[0].showIngredientsTable)) && recipe.showIngredientOverview">
|
||||
<v-card class="mt-1"
|
||||
v-if="(recipe.steps.length > 1 || (recipe.steps.length == 1 && !recipe.steps[0].showIngredientsTable)) && recipe.showIngredientOverview && !useUserPreferenceStore().isPrintMode">
|
||||
<steps-overview :steps="recipe.steps" :ingredient-factor="ingredientFactor"></steps-overview>
|
||||
</v-card>
|
||||
|
||||
@@ -143,12 +144,12 @@
|
||||
<step-view v-model="recipe.steps[index]" :step-number="index+1" :ingredientFactor="ingredientFactor"></step-view>
|
||||
</v-card>
|
||||
|
||||
<property-view v-model="recipe" :servings="servings"></property-view>
|
||||
<property-view v-model="recipe" :ingredientFactor="ingredientFactor"></property-view>
|
||||
|
||||
<v-card class="mt-2">
|
||||
<v-card-text>
|
||||
<v-row>
|
||||
<v-col cols="12" md="3">
|
||||
<v-row dense>
|
||||
<v-col cols="12" :sm="(recipe.sourceUrl) ? 3 : 4">
|
||||
<v-card
|
||||
variant="outlined"
|
||||
:title="$t('CreatedBy')"
|
||||
@@ -157,7 +158,7 @@
|
||||
:to="(useUserPreferenceStore().isAuthenticated) ? {name: 'SearchPage', query: {createdby: recipe.createdBy.id!}}: undefined">
|
||||
</v-card>
|
||||
</v-col>
|
||||
<v-col cols="12" md="3">
|
||||
<v-col cols="12" :sm="(recipe.sourceUrl) ? 3 : 4">
|
||||
<v-card
|
||||
variant="outlined"
|
||||
:title="$t('Created')"
|
||||
@@ -166,7 +167,7 @@
|
||||
:to="(useUserPreferenceStore().isAuthenticated) ? {name: 'SearchPage', query: {createdon: DateTime.fromJSDate(recipe.createdAt).toISODate()}} : undefined">
|
||||
</v-card>
|
||||
</v-col>
|
||||
<v-col cols="12" md="3">
|
||||
<v-col cols="12" :sm="(recipe.sourceUrl) ? 3 : 4">
|
||||
<v-card
|
||||
variant="outlined"
|
||||
:title="$t('Updated')"
|
||||
@@ -175,7 +176,7 @@
|
||||
:to="(useUserPreferenceStore().isAuthenticated) ? {name: 'SearchPage', query: {updatedon: DateTime.fromJSDate(recipe.updatedAt).toISODate()}}: undefined">
|
||||
</v-card>
|
||||
</v-col>
|
||||
<v-col cols="12" md="3" v-if="recipe.sourceUrl">
|
||||
<v-col cols="12" :sm="(recipe.sourceUrl) ? 3 : 4" v-if="recipe.sourceUrl">
|
||||
<v-card
|
||||
variant="outlined"
|
||||
:title="$t('Imported_From')"
|
||||
@@ -189,7 +190,7 @@
|
||||
</v-card-text>
|
||||
</v-card>
|
||||
|
||||
<recipe-activity :recipe="recipe" v-if="useUserPreferenceStore().userSettings.comments"></recipe-activity>
|
||||
<recipe-activity :recipe="recipe" :servings="servings" v-if="useUserPreferenceStore().userSettings.comments"></recipe-activity>
|
||||
</template>
|
||||
</template>
|
||||
|
||||
@@ -219,8 +220,11 @@ const {doAiImport, fileApiLoading} = useFileApi()
|
||||
|
||||
const loading = ref(false)
|
||||
const recipe = defineModel<Recipe>({required: true})
|
||||
const props = defineProps<{
|
||||
servings: {type: Number, required: false},
|
||||
}>()
|
||||
|
||||
const servings = ref(1)
|
||||
const servings = ref(props.servings ?? recipe.value.servings ?? 1)
|
||||
const showFullRecipeName = ref(false)
|
||||
|
||||
const selectedAiProvider = ref<undefined | AiProvider>(useUserPreferenceStore().activeSpace.aiDefaultProvider)
|
||||
@@ -235,11 +239,13 @@ const ingredientFactor = computed(() => {
|
||||
/**
|
||||
* change servings when recipe servings are changed
|
||||
*/
|
||||
watch(() => recipe.value.servings, () => {
|
||||
if (recipe.value.servings) {
|
||||
servings.value = recipe.value.servings
|
||||
}
|
||||
})
|
||||
if (props.servings === undefined) {
|
||||
watch(() => recipe.value.servings, () => {
|
||||
if (recipe.value.servings) {
|
||||
servings.value = recipe.value.servings
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
//keep screen on while viewing a recipe
|
||||
@@ -257,7 +263,7 @@ onBeforeUnmount(() => {
|
||||
function aiConvertRecipe() {
|
||||
let api = new ApiApi()
|
||||
|
||||
doAiImport(selectedAiProvider.value.id!,null, '', recipe.value.id!).then(r => {
|
||||
doAiImport(selectedAiProvider.value.id!, null, '', recipe.value.id!).then(r => {
|
||||
if (r.recipe) {
|
||||
recipe.value.internal = true
|
||||
recipe.value.steps = r.recipe.steps
|
||||
|
||||
@@ -22,10 +22,10 @@
|
||||
<timer :seconds="step.time != undefined ? step.time*60 : 0" @stop="timerRunning = false" v-if="timerRunning"></timer>
|
||||
<v-card-text v-if="step.ingredients.length > 0 || step.instruction != ''">
|
||||
<v-row>
|
||||
<v-col cols="12" md="6" v-if="step.ingredients.length > 0 && (step.showIngredientsTable || step.show_ingredients_table)">
|
||||
<v-col :cols="(useUserPreferenceStore().isPrintMode) ? 6 : 12" md="6" v-if="step.ingredients.length > 0 && (step.showIngredientsTable || step.show_ingredients_table)">
|
||||
<ingredients-table v-model="step.ingredients" :ingredient-factor="ingredientFactor"></ingredients-table>
|
||||
</v-col>
|
||||
<v-col cols="12" md="6" class="markdown-body">
|
||||
<v-col :cols="(useUserPreferenceStore().isPrintMode) ? 6 : 12" md="6" class="markdown-body">
|
||||
<instructions :instructions_html="step.instructionsMarkdown" :ingredient_factor="ingredientFactor"
|
||||
v-if="step.instructionsMarkdown != undefined"></instructions>
|
||||
<!-- sub recipes dont have a correct schema, thus they use different variable naming -->
|
||||
@@ -62,6 +62,7 @@ import {Step} from "@/openapi";
|
||||
|
||||
import Instructions from "@/components/display/Instructions.vue";
|
||||
import Timer from "@/components/display/Timer.vue";
|
||||
import {useUserPreferenceStore} from "@/stores/UserPreferenceStore.ts";
|
||||
|
||||
const step = defineModel<Step>({required: true})
|
||||
|
||||
|
||||
@@ -26,11 +26,9 @@
|
||||
<v-progress-circular v-if="duplicateLoading" indeterminate size="small"></v-progress-circular>
|
||||
</template>
|
||||
</v-list-item>
|
||||
|
||||
<!-- TODO when calling print() some timing or whatever issue makes it so the useMediaQuery does not work and the sidebar is still shown -->
|
||||
<!-- <v-list-item prepend-icon="fa-solid fa-print" @click="openPrintView()">-->
|
||||
<!-- {{ $t('Print') }}-->
|
||||
<!-- </v-list-item>-->
|
||||
<v-list-item :to="{ name: 'RecipeViewPage', params: { id: recipe.id}, query: {print: 'true', servings: props.servings} }" :active="false" target="_blank" prepend-icon="fa-solid fa-print">
|
||||
{{ $t('Print') }}
|
||||
</v-list-item>
|
||||
</v-list>
|
||||
</v-menu>
|
||||
</v-btn>
|
||||
@@ -49,22 +47,21 @@ import AddToShoppingDialog from "@/components/dialogs/AddToShoppingDialog.vue";
|
||||
import {ErrorMessageType, useMessageStore} from "@/stores/MessageStore.ts";
|
||||
import {useRouter} from "vue-router";
|
||||
import {useFileApi} from "@/composables/useFileApi.ts";
|
||||
import {useI18n} from "vue-i18n";
|
||||
|
||||
const router = useRouter()
|
||||
const {t} = useI18n()
|
||||
const {updateRecipeImage} = useFileApi()
|
||||
|
||||
const props = defineProps({
|
||||
recipe: {type: Object as PropType<Recipe | RecipeOverview>, required: true},
|
||||
servings: {type: Number, default: undefined},
|
||||
size: {type: String, default: 'medium'},
|
||||
})
|
||||
|
||||
const mealPlanDialog = ref(false)
|
||||
const duplicateLoading = ref(false)
|
||||
|
||||
function openPrintView() {
|
||||
print()
|
||||
}
|
||||
|
||||
/**
|
||||
* create a duplicate of the recipe by pulling its current data and creating a new recipe with the same data
|
||||
*/
|
||||
@@ -72,7 +69,27 @@ function duplicateRecipe() {
|
||||
let api = new ApiApi()
|
||||
duplicateLoading.value = true
|
||||
api.apiRecipeRetrieve({id: props.recipe.id!}).then(originalRecipe => {
|
||||
api.apiRecipeCreate({recipe: originalRecipe}).then(newRecipe => {
|
||||
|
||||
let recipe = {...originalRecipe, ...{id: undefined, name: originalRecipe.name + `(${t('Copy')})`}}
|
||||
recipe.steps = recipe.steps.map((step) => {
|
||||
return {
|
||||
...step,
|
||||
...{
|
||||
id: undefined,
|
||||
ingredients: step.ingredients.map((ingredient) => {
|
||||
return {...ingredient, ...{id: undefined}}
|
||||
}),
|
||||
},
|
||||
}
|
||||
})
|
||||
|
||||
if (recipe.properties != null) {
|
||||
recipe.properties = recipe.properties.map((p) => {
|
||||
return {...p, ...{id: undefined}}
|
||||
})
|
||||
}
|
||||
|
||||
api.apiRecipeCreate({recipe: recipe}).then(newRecipe => {
|
||||
|
||||
if (originalRecipe.image) {
|
||||
updateRecipeImage(newRecipe.id!, null, originalRecipe.image).then(r => {
|
||||
|
||||
@@ -283,8 +283,9 @@ function parseAndInsertIngredients() {
|
||||
}
|
||||
})
|
||||
Promise.allSettled(promises).then(r => {
|
||||
step.value.ingredients = step.value.ingredients.filter(i => i.food != null || i.note != null || i.amount != 0)
|
||||
|
||||
r.forEach(i => {
|
||||
console.log(i)
|
||||
step.value.ingredients.push({
|
||||
originalText: i.value.originalText,
|
||||
amount: i.value.amount,
|
||||
|
||||
@@ -29,7 +29,7 @@
|
||||
@update:modelValue="editingObj.servings = editingObj.recipe ? editingObj.recipe.servings : 1"></ModelSelect>
|
||||
<!-- <v-number-input label="Days" control-variant="split" :min="1"></v-number-input>-->
|
||||
<!--TODO create days input with +/- synced to date -->
|
||||
<recipe-card :recipe="editingObj.recipe" v-if="editingObj && editingObj.recipe" link-target="_blank"></recipe-card>
|
||||
<recipe-card :recipe="editingObj.recipe" :servings="editingObj.servings" v-if="editingObj && editingObj.recipe" link-target="_blank"></recipe-card>
|
||||
<v-btn prepend-icon="$shopping" color="create" class="mt-1" v-if="!editingObj.shopping && editingObj.recipe && isUpdate()">
|
||||
{{$t('Add')}}
|
||||
<add-to-shopping-dialog :recipe="editingObj.recipe" :meal-plan="editingObj" @created="loadShoppingListEntries(); editingObj.shopping = true;"></add-to-shopping-dialog>
|
||||
|
||||
68
vue3/src/components/model_editors/ShoppingListEditor.vue
Normal file
68
vue3/src/components/model_editors/ShoppingListEditor.vue
Normal file
@@ -0,0 +1,68 @@
|
||||
<template>
|
||||
<model-editor-base
|
||||
:loading="loading"
|
||||
:dialog="dialog"
|
||||
@save="saveObject"
|
||||
@delete="deleteObject"
|
||||
@close="emit('close'); editingObjChanged = false"
|
||||
:is-update="isUpdate()"
|
||||
:is-changed="editingObjChanged"
|
||||
:model-class="modelClass"
|
||||
:object-name="editingObjName()"
|
||||
:editing-object="editingObj">
|
||||
<v-card-text>
|
||||
<v-form :disabled="loading">
|
||||
<v-text-field :label="$t('Name')" v-model="editingObj.name"></v-text-field>
|
||||
<v-textarea :label="$t('Description')" v-model="editingObj.description" :rows="2" auto-grow></v-textarea>
|
||||
<v-color-picker :label="$t('Color')" v-model="editingObj.color" mode="hex" :modes="['hex']" show-swatches
|
||||
:swatches="[['#ddbf86'],['#b98766'],['#b55e4f'],['#82aa8b'],['#385f84']]"></v-color-picker>
|
||||
</v-form>
|
||||
</v-card-text>
|
||||
</model-editor-base>
|
||||
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
|
||||
import {onMounted, PropType, watch} from "vue";
|
||||
import {ShoppingList, ShoppingListEntry} from "@/openapi";
|
||||
import ModelEditorBase from "@/components/model_editors/ModelEditorBase.vue";
|
||||
import {useModelEditorFunctions} from "@/composables/useModelEditorFunctions";
|
||||
import ModelSelect from "@/components/inputs/ModelSelect.vue";
|
||||
|
||||
const props = defineProps({
|
||||
item: {type: {} as PropType<ShoppingList>, required: false, default: null},
|
||||
itemId: {type: [Number, String], required: false, default: undefined},
|
||||
itemDefaults: {type: {} as PropType<ShoppingList>, required: false, default: {} as ShoppingList},
|
||||
dialog: {type: Boolean, default: false}
|
||||
})
|
||||
|
||||
const emit = defineEmits(['create', 'save', 'delete', 'close', 'changedState'])
|
||||
const {setupState, deleteObject, saveObject, isUpdate, editingObjName, loading, editingObj, editingObjChanged, modelClass} = useModelEditorFunctions<ShoppingList>('ShoppingList', emit)
|
||||
|
||||
/**
|
||||
* watch prop changes and re-initialize editor
|
||||
* required to embed editor directly into pages and be able to change item from the outside
|
||||
*/
|
||||
watch([() => props.item, () => props.itemId], () => {
|
||||
initializeEditor()
|
||||
})
|
||||
|
||||
// object specific data (for selects/display)
|
||||
|
||||
onMounted(() => {
|
||||
initializeEditor()
|
||||
})
|
||||
|
||||
/**
|
||||
* component specific state setup logic
|
||||
*/
|
||||
function initializeEditor(){
|
||||
setupState(props.item, props.itemId, {itemDefaults: props.itemDefaults})
|
||||
}
|
||||
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
|
||||
</style>
|
||||
@@ -16,7 +16,7 @@ export function useNavigation() {
|
||||
{component: VListItem, prependIcon: '$recipes', title: 'Home', to: {name: 'StartPage', params: {}}},
|
||||
{component: VListItem, prependIcon: '$search', title: t('Search'), to: {name: 'SearchPage', params: {}}},
|
||||
{component: VListItem, prependIcon: '$mealplan', title: t('Meal_Plan'), to: {name: 'MealPlanPage', params: {}}},
|
||||
{component: VListItem, prependIcon: '$shopping', title: t('Shopping_list'), to: {name: 'ShoppingListPage', params: {}}},
|
||||
{component: VListItem, prependIcon: '$shopping', title: t('Shopping'), to: {name: 'ShoppingListPage', params: {}}},
|
||||
{component: VListItem, prependIcon: 'fas fa-globe', title: t('Import'), to: {name: 'RecipeImportPage', params: {}}},
|
||||
{component: VListItem, prependIcon: '$books', title: t('Books'), to: {name: 'BooksPage', params: {}}},
|
||||
{component: VListItem, prependIcon: 'fa-solid fa-folder-tree', title: t('Database'), to: {name: 'DatabasePage', params: {}}},
|
||||
|
||||
@@ -267,6 +267,7 @@
|
||||
"Ratings": "",
|
||||
"Recently_Viewed": "",
|
||||
"Recipe": "",
|
||||
"RecipeStructure": "",
|
||||
"Recipe_Book": "",
|
||||
"Recipe_Image": "",
|
||||
"Recipes": "",
|
||||
@@ -314,6 +315,7 @@
|
||||
"SpaceMembersHelp": "",
|
||||
"SpaceName": "",
|
||||
"SpacePrivateObjectsHelp": "",
|
||||
"Start": "",
|
||||
"Starting_Day": "",
|
||||
"StartsWith": "",
|
||||
"StartsWithHelp": "",
|
||||
|
||||
@@ -260,6 +260,7 @@
|
||||
"Ratings": "Рейтинги",
|
||||
"Recently_Viewed": "Наскоро разгледани",
|
||||
"Recipe": "Рецепта",
|
||||
"RecipeStructure": "",
|
||||
"Recipe_Book": "Книга с рецепти",
|
||||
"Recipe_Image": "Изображение на рецептата",
|
||||
"Recipes": "Рецепти",
|
||||
@@ -307,6 +308,7 @@
|
||||
"SpaceMembersHelp": "",
|
||||
"SpaceName": "",
|
||||
"SpacePrivateObjectsHelp": "",
|
||||
"Start": "",
|
||||
"Starting_Day": "Начален ден от седмицата",
|
||||
"StartsWith": "",
|
||||
"StartsWithHelp": "",
|
||||
|
||||
@@ -337,6 +337,7 @@
|
||||
"Ratings": "Avaluació",
|
||||
"Recently_Viewed": "Vistos recentment",
|
||||
"Recipe": "Recepta",
|
||||
"RecipeStructure": "",
|
||||
"Recipe_Book": "Llibre de receptes",
|
||||
"Recipe_Image": "Imatge de la recepta",
|
||||
"Recipes": "Receptes",
|
||||
@@ -394,6 +395,7 @@
|
||||
"SpacePrivateObjectsHelp": "",
|
||||
"Space_Cosmetic_Settings": "Un administrador de l'espai podria canviar algunes configuracions estètiques i tindrien prioritat sobre la configuració dels usuaris per a aquest espai.",
|
||||
"Split_All_Steps": "Dividir totes les files en passos separats.",
|
||||
"Start": "",
|
||||
"StartDate": "Data d'inici",
|
||||
"Starting_Day": "Dia d'inici de la setmana",
|
||||
"StartsWith": "",
|
||||
|
||||
@@ -334,6 +334,7 @@
|
||||
"Ratings": "Hodnocení",
|
||||
"Recently_Viewed": "Naposledy prohlížené",
|
||||
"Recipe": "Recept",
|
||||
"RecipeStructure": "",
|
||||
"Recipe_Book": "Kuchařka",
|
||||
"Recipe_Image": "Obrázek k receptu",
|
||||
"Recipes": "Recepty",
|
||||
@@ -389,6 +390,7 @@
|
||||
"SpacePrivateObjectsHelp": "",
|
||||
"Space_Cosmetic_Settings": "Některá kosmetická nastavení mohou měnit správci prostoru a budou mít přednost před nastavením klienta pro daný prostor.",
|
||||
"Split_All_Steps": "Rozdělit každý řádek do samostatného kroku.",
|
||||
"Start": "",
|
||||
"StartDate": "Počáteční datum",
|
||||
"Starting_Day": "První den v týdnu",
|
||||
"StartsWith": "",
|
||||
|
||||
@@ -337,6 +337,7 @@
|
||||
"Ratings": "Bedømmelser",
|
||||
"Recently_Viewed": "Vist for nylig",
|
||||
"Recipe": "Opskrift",
|
||||
"RecipeStructure": "",
|
||||
"Recipe_Book": "Opskriftsbog",
|
||||
"Recipe_Image": "Opskriftsbillede",
|
||||
"Recipes": "Opskrifter",
|
||||
@@ -394,6 +395,7 @@
|
||||
"SpacePrivateObjectsHelp": "",
|
||||
"Space_Cosmetic_Settings": "Visse kosmetiske indstillinger kan ændres af område-administratorer og vil overskrive klient-indstillinger for pågældende område.",
|
||||
"Split_All_Steps": "Opdel rækker i separate trin.",
|
||||
"Start": "",
|
||||
"StartDate": "Startdato",
|
||||
"Starting_Day": "Første dag på ugen",
|
||||
"StartsWith": "",
|
||||
|
||||
@@ -454,6 +454,7 @@
|
||||
"RecipeBookHelp": "Rezeptbücher enthalten Rezeptbucheinträge oder können über hinterlegte gespeicherte Suchen automatisch gefüllt werden. ",
|
||||
"RecipeHelp": "Rezepte sind die Grundlage von Tandoor und bestehen aus allgemeinen Informationen und Schritten, die wiederrum aus Zutaten, Texten und mehr bestehen. ",
|
||||
"RecipeStepsHelp": "Zutaten, Anleitungen und mehr können unter dem Tab Schritte hinzugefügt werden.",
|
||||
"RecipeStructure": "Rezept Struktur",
|
||||
"Recipe_Book": "Kochbuch",
|
||||
"Recipe_Image": "Rezeptbild",
|
||||
"Recipes": "Rezepte",
|
||||
@@ -502,9 +503,12 @@
|
||||
"Share": "Teilen",
|
||||
"ShopLater": "Später kaufen",
|
||||
"ShopNow": "Jetzt kaufen",
|
||||
"Shopping": "Einkaufen",
|
||||
"ShoppingBackgroundSyncWarning": "Schlechte Netzwerkverbindung, Warten auf Synchronisation ...",
|
||||
"ShoppingList": "Einkaufsliste",
|
||||
"ShoppingListEntry": "Einkaufslisten Eintrag",
|
||||
"ShoppingListEntryHelp": "Einträge auf der Einkaufsliste können manuell oder automatisch durch Rezepte und Essenspläne erstellt werden.",
|
||||
"ShoppingListHelp": "Erlaubt es Einträge auf verschiedene Listen zu setzten. Beispielsweise für verschiedene Supermärkte, Angebote oder Ereignisse. ",
|
||||
"ShoppingListRecipe": "Einkaufslisten Rezepte",
|
||||
"Shopping_Categories": "Einkaufskategorien",
|
||||
"Shopping_Category": "Einkaufskategorie",
|
||||
@@ -541,6 +545,7 @@
|
||||
"Space_Cosmetic_Settings": "Kosmetische Einstellungen auf Space Ebene überschreiben die Einstellungen der einzelnen Nutzer.",
|
||||
"Split": "Aufteilen",
|
||||
"Split_All_Steps": "Teile alle Zeilen in separate Schritte auf.",
|
||||
"Start": "Start",
|
||||
"StartDate": "Startdatum",
|
||||
"Starting_Day": "Wochenbeginn am",
|
||||
"StartsWith": "Beginnt mit",
|
||||
@@ -594,7 +599,7 @@
|
||||
"TrigramThresholdHelp": "Steuert bei der Verwendung unscharfer Suche wie viele Unterschiede zugelasen werden. Niedrigere Werte führen zu mehr Ergebnissen/größerer Unschärfe.",
|
||||
"Tuesday": "Dienstag",
|
||||
"Type": "Typ",
|
||||
"UPDATE_ERROR": "Fehler beim aktualisieren",
|
||||
"UPDATE_ERROR": "Fehler beim Aktualisieren",
|
||||
"Unchanged": "Unverändert",
|
||||
"Undefined": "undefiniert",
|
||||
"Undo": "Rückgängig",
|
||||
|
||||
@@ -337,6 +337,7 @@
|
||||
"Ratings": "Βαθμολογίες",
|
||||
"Recently_Viewed": "Προβλήθηκαν πρόσφατα",
|
||||
"Recipe": "Συνταγή",
|
||||
"RecipeStructure": "",
|
||||
"Recipe_Book": "Βιβλίο συνταγών",
|
||||
"Recipe_Image": "Εικόνα συνταγής",
|
||||
"Recipes": "Συνταγές",
|
||||
@@ -394,6 +395,7 @@
|
||||
"SpacePrivateObjectsHelp": "",
|
||||
"Space_Cosmetic_Settings": "Ορισμένες ρυθμίσεις εμφάνισης μπορούν να αλλάξουν από τους διαχειριστές του χώρου και θα παρακάμψουν τις ρυθμίσεις πελάτη για αυτόν τον χώρο.",
|
||||
"Split_All_Steps": "Διαχωρισμός όλων των γραμμών σε χωριστά βήματα.",
|
||||
"Start": "",
|
||||
"StartDate": "Ημερομηνία Έναρξης",
|
||||
"Starting_Day": "Πρώτη μέρα της εβδομάδας",
|
||||
"StartsWith": "",
|
||||
|
||||
@@ -452,6 +452,7 @@
|
||||
"RecipeBookHelp": "Recipebooks contain recipe book entries or can be automatically populated by using saved search filters. ",
|
||||
"RecipeHelp": "Recipes are the foundation of Tandoor and consist of general information and steps, made up of ingredients, instructions and more. ",
|
||||
"RecipeStepsHelp": "Ingredients, Instructions and more can be edited in the tab Steps.",
|
||||
"RecipeStructure": "Recipe Structure",
|
||||
"Recipe_Book": "Recipe Book",
|
||||
"Recipe_Image": "Recipe Image",
|
||||
"Recipes": "Recipes",
|
||||
@@ -500,9 +501,12 @@
|
||||
"Share": "Share",
|
||||
"ShopLater": "Shop later",
|
||||
"ShopNow": "Shop now",
|
||||
"Shopping": "Shopping",
|
||||
"ShoppingBackgroundSyncWarning": "Bad network, waiting to sync ...",
|
||||
"ShoppingList": "Shoppinglist",
|
||||
"ShoppingListEntry": "Shoppinglist Entry",
|
||||
"ShoppingListEntryHelp": "Shopping list entries can be created manually or trough recipes and meal plans.",
|
||||
"ShoppingListHelp": "Allows you to put entries on different lists. Can be used for different supermarkets, special offers or events. ",
|
||||
"ShoppingListRecipe": "Shoppinglist Recipe",
|
||||
"Shopping_Categories": "Shopping Categories",
|
||||
"Shopping_Category": "Shopping Category",
|
||||
@@ -539,6 +543,7 @@
|
||||
"Space_Cosmetic_Settings": "Some cosmetic settings can be changed by space administrators and will override client settings for that space.",
|
||||
"Split": "Split",
|
||||
"Split_All_Steps": "Split all rows into separate steps.",
|
||||
"Start": "Start",
|
||||
"StartDate": "Start Date",
|
||||
"Starting_Day": "Starting day of the week",
|
||||
"StartsWith": "Starts with",
|
||||
|
||||
@@ -439,6 +439,7 @@
|
||||
"RecipeBookHelp": "Los recetarios contienen entradas de recetas o pueden ser rellenados automáticamente usando filtros de búsqueda guardados. ",
|
||||
"RecipeHelp": "Las recetas son la base de Tandoor y consisten de información general y pasos, que incluyen ingredientes, instrucciones y más. ",
|
||||
"RecipeStepsHelp": "Los ingredientes, las instrucciones y más se pueden editar en la pestaña «Pasos».",
|
||||
"RecipeStructure": "",
|
||||
"Recipe_Book": "Libro de recetas",
|
||||
"Recipe_Image": "Imagen de la receta",
|
||||
"Recipes": "Recetas",
|
||||
@@ -521,6 +522,7 @@
|
||||
"Space_Cosmetic_Settings": "Algunos ajustes de apariencia pueden ser cambiados por los administradores del espacio y anularán los ajustes del cliente para ese espacio.",
|
||||
"Split": "Dividir",
|
||||
"Split_All_Steps": "Dividir todas las filas en pasos separados.",
|
||||
"Start": "",
|
||||
"StartDate": "Fecha de Inicio",
|
||||
"Starting_Day": "Día de comienzo de la semana",
|
||||
"Step": "Paso",
|
||||
|
||||
@@ -326,6 +326,7 @@
|
||||
"Ratings": "Luokitukset",
|
||||
"Recently_Viewed": "Äskettäin katsotut",
|
||||
"Recipe": "Resepti",
|
||||
"RecipeStructure": "",
|
||||
"Recipe_Book": "Keittokirja",
|
||||
"Recipe_Image": "Reseptin Kuva",
|
||||
"Recipes": "Reseptit",
|
||||
@@ -381,6 +382,7 @@
|
||||
"SpaceName": "",
|
||||
"SpacePrivateObjectsHelp": "",
|
||||
"Split_All_Steps": "Jaa kaikki rivit erillisiin vaiheisiin.",
|
||||
"Start": "",
|
||||
"StartDate": "Aloituspäivä",
|
||||
"Starting_Day": "Viikon aloituspäivä",
|
||||
"StartsWith": "",
|
||||
|
||||
@@ -449,6 +449,7 @@
|
||||
"RecipeBookHelp": "Les livres de recettes contiennent des entrées de livre de recettes ou peuvent être automatiquement remplis à l'aide de filtres de recherche enregistrés. ",
|
||||
"RecipeHelp": "Les recettes sont la base de Tandoor et se composent d'informations générales et d'étapes, elles-mêmes composées d'ingrédients, d'instructions et plus encore. ",
|
||||
"RecipeStepsHelp": "Les ingrédients, les instructions et plus encore, peuvent être modifiés dans l'onglet Étapes.",
|
||||
"RecipeStructure": "",
|
||||
"Recipe_Book": "Livre de recettes",
|
||||
"Recipe_Image": "Image de la recette",
|
||||
"Recipes": "Recettes",
|
||||
@@ -536,6 +537,7 @@
|
||||
"Space_Cosmetic_Settings": "Certains paramètres cosmétiques peuvent être modifiés par un administrateur de l'espace et seront prioritaires sur les paramètres des utilisateurs pour cet espace.",
|
||||
"Split": "Diviser",
|
||||
"Split_All_Steps": "Diviser toutes les lignes en étapes séparées.",
|
||||
"Start": "",
|
||||
"StartDate": "Date de début",
|
||||
"Starting_Day": "Jour de début de la semaine",
|
||||
"StartsWith": "Commence par",
|
||||
|
||||
@@ -337,6 +337,7 @@
|
||||
"Ratings": "דירוג",
|
||||
"Recently_Viewed": "נצפו לאחרונה",
|
||||
"Recipe": "מתכון",
|
||||
"RecipeStructure": "",
|
||||
"Recipe_Book": "ספר מתכון",
|
||||
"Recipe_Image": "תמונת מתכון",
|
||||
"Recipes": "מתכונים",
|
||||
@@ -394,6 +395,7 @@
|
||||
"SpacePrivateObjectsHelp": "",
|
||||
"Space_Cosmetic_Settings": "חלק מהגדרות הקוסמטיות יכולות להיות מעודכנות על ידי מנהל המרחב וידרסו את הגדרות הקליינט עבור מרחב זה.",
|
||||
"Split_All_Steps": "פצל את כל השורות לצעדים נפרדים.",
|
||||
"Start": "",
|
||||
"StartDate": "תאריך התחלה",
|
||||
"Starting_Day": "יום תחילת השבוע",
|
||||
"StartsWith": "",
|
||||
|
||||
@@ -337,6 +337,7 @@
|
||||
"Ratings": "Ocjene",
|
||||
"Recently_Viewed": "Nedavno pogledano",
|
||||
"Recipe": "Recept",
|
||||
"RecipeStructure": "",
|
||||
"Recipe_Book": "Knjiga recepata",
|
||||
"Recipe_Image": "Slika recepta",
|
||||
"Recipes": "Recepti",
|
||||
@@ -394,6 +395,7 @@
|
||||
"SpacePrivateObjectsHelp": "",
|
||||
"Space_Cosmetic_Settings": "Neke kozmetičke postavke mogu promijeniti administratori prostora i one će poništiti postavke klijenta za taj prostor.",
|
||||
"Split_All_Steps": "Podijeli sve retke u zasebne korake.",
|
||||
"Start": "",
|
||||
"StartDate": "Početni datum",
|
||||
"Starting_Day": "Početni dan u tjednu",
|
||||
"StartsWith": "",
|
||||
|
||||
@@ -310,6 +310,7 @@
|
||||
"Ratings": "Értékelések",
|
||||
"Recently_Viewed": "Nemrég megtekintett",
|
||||
"Recipe": "Recept",
|
||||
"RecipeStructure": "",
|
||||
"Recipe_Book": "Szakácskönyv",
|
||||
"Recipe_Image": "Receptkép",
|
||||
"Recipes": "Receptek",
|
||||
@@ -360,6 +361,7 @@
|
||||
"SpaceName": "",
|
||||
"SpacePrivateObjectsHelp": "",
|
||||
"Split_All_Steps": "Ossza fel az összes sort különálló lépésekbe.",
|
||||
"Start": "",
|
||||
"StartDate": "Kezdés dátuma",
|
||||
"Starting_Day": "A hét kezdőnapja",
|
||||
"StartsWith": "",
|
||||
|
||||
@@ -143,6 +143,7 @@
|
||||
"Rating": "",
|
||||
"Recently_Viewed": "Վերջերս դիտած",
|
||||
"Recipe": "Բաղադրատոմս",
|
||||
"RecipeStructure": "",
|
||||
"Recipe_Book": "Բաղադրատոմսերի գիրք",
|
||||
"Recipe_Image": "Բաղադրատոմսի նկար",
|
||||
"Recipes": "Բաղադրատոմսեր",
|
||||
@@ -177,6 +178,7 @@
|
||||
"SpaceMembersHelp": "",
|
||||
"SpaceName": "",
|
||||
"SpacePrivateObjectsHelp": "",
|
||||
"Start": "",
|
||||
"StartsWith": "",
|
||||
"StartsWithHelp": "",
|
||||
"Step": "",
|
||||
|
||||
@@ -286,6 +286,7 @@
|
||||
"Ratings": "",
|
||||
"Recently_Viewed": "baru saja dilihat",
|
||||
"Recipe": "",
|
||||
"RecipeStructure": "",
|
||||
"Recipe_Book": "",
|
||||
"Recipe_Image": "Gambar Resep",
|
||||
"Recipes": "Resep",
|
||||
@@ -336,6 +337,7 @@
|
||||
"SpaceMembersHelp": "",
|
||||
"SpaceName": "",
|
||||
"SpacePrivateObjectsHelp": "",
|
||||
"Start": "",
|
||||
"Starting_Day": "",
|
||||
"StartsWith": "",
|
||||
"StartsWithHelp": "",
|
||||
|
||||
@@ -336,6 +336,7 @@
|
||||
"Ratings": "",
|
||||
"Recently_Viewed": "",
|
||||
"Recipe": "",
|
||||
"RecipeStructure": "",
|
||||
"Recipe_Book": "",
|
||||
"Recipe_Image": "",
|
||||
"Recipes": "",
|
||||
@@ -392,6 +393,7 @@
|
||||
"SpacePrivateObjectsHelp": "",
|
||||
"Space_Cosmetic_Settings": "",
|
||||
"Split_All_Steps": "",
|
||||
"Start": "",
|
||||
"StartDate": "",
|
||||
"Starting_Day": "",
|
||||
"StartsWith": "",
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -314,6 +314,7 @@
|
||||
"Ratings": "",
|
||||
"Recently_Viewed": "Neseniai Žiūrėta",
|
||||
"Recipe": "",
|
||||
"RecipeStructure": "",
|
||||
"Recipe_Book": "",
|
||||
"Recipe_Image": "Recepto nuotrauka",
|
||||
"Recipes": "",
|
||||
@@ -365,6 +366,7 @@
|
||||
"SpaceName": "",
|
||||
"SpacePrivateObjectsHelp": "",
|
||||
"Split_All_Steps": "",
|
||||
"Start": "",
|
||||
"StartDate": "",
|
||||
"Starting_Day": "",
|
||||
"StartsWith": "",
|
||||
|
||||
@@ -337,6 +337,7 @@
|
||||
"Ratings": "",
|
||||
"Recently_Viewed": "",
|
||||
"Recipe": "",
|
||||
"RecipeStructure": "",
|
||||
"Recipe_Book": "",
|
||||
"Recipe_Image": "",
|
||||
"Recipes": "",
|
||||
@@ -394,6 +395,7 @@
|
||||
"SpacePrivateObjectsHelp": "",
|
||||
"Space_Cosmetic_Settings": "",
|
||||
"Split_All_Steps": "",
|
||||
"Start": "",
|
||||
"StartDate": "",
|
||||
"Starting_Day": "",
|
||||
"StartsWith": "",
|
||||
|
||||
@@ -321,6 +321,7 @@
|
||||
"Ratings": "",
|
||||
"Recently_Viewed": "Nylig vist",
|
||||
"Recipe": "Oppskrift",
|
||||
"RecipeStructure": "",
|
||||
"Recipe_Book": "Oppskriftsbok",
|
||||
"Recipe_Image": "Oppskriftsbilde",
|
||||
"Recipes": "Oppskrift",
|
||||
@@ -375,6 +376,7 @@
|
||||
"SpaceName": "",
|
||||
"SpacePrivateObjectsHelp": "",
|
||||
"Split_All_Steps": "",
|
||||
"Start": "",
|
||||
"StartDate": "Startdato",
|
||||
"Starting_Day": "Dag uken skal state på",
|
||||
"StartsWith": "",
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -363,6 +363,7 @@
|
||||
"Ratings": "Oceny",
|
||||
"Recently_Viewed": "Ostatnio oglądane",
|
||||
"Recipe": "Przepis",
|
||||
"RecipeStructure": "",
|
||||
"Recipe_Book": "Książka z przepisami",
|
||||
"Recipe_Image": "Obrazek dla przepisu",
|
||||
"Recipes": "Przepisy",
|
||||
@@ -420,6 +421,7 @@
|
||||
"SpacePrivateObjectsHelp": "",
|
||||
"Space_Cosmetic_Settings": "Administratorzy przestrzeni mogą zmienić niektóre ustawienia kosmetyczne, które zastąpią ustawienia klienta dla tej przestrzeni.",
|
||||
"Split_All_Steps": "Traktuj każdy wiersz jako osobne kroki.",
|
||||
"Start": "",
|
||||
"StartDate": "Data początkowa",
|
||||
"Starting_Day": "Dzień rozpoczęcia tygodnia",
|
||||
"StartsWith": "",
|
||||
|
||||
@@ -236,6 +236,7 @@
|
||||
"Ratings": "Avaliações",
|
||||
"Recently_Viewed": "Vistos Recentemente",
|
||||
"Recipe": "Receita",
|
||||
"RecipeStructure": "",
|
||||
"Recipe_Book": "Livro de Receitas",
|
||||
"Recipe_Image": "Imagem da Receita",
|
||||
"Recipes": "Receitas",
|
||||
@@ -271,6 +272,7 @@
|
||||
"Show_as_header": "Mostrar como cabeçalho",
|
||||
"Size": "Tamanho",
|
||||
"Sort_by_new": "Ordenar por mais recente",
|
||||
"Start": "",
|
||||
"StartDate": "Data de início",
|
||||
"Starting_Day": "Dia de início da semana",
|
||||
"StartsWith": "",
|
||||
|
||||
@@ -414,6 +414,7 @@
|
||||
"Ratings": "Classificações",
|
||||
"Recently_Viewed": "Visto recentemente",
|
||||
"Recipe": "Receita",
|
||||
"RecipeStructure": "",
|
||||
"Recipe_Book": "Livro de Receitas",
|
||||
"Recipe_Image": "Imagem da receita",
|
||||
"Recipes": "Receitas",
|
||||
@@ -468,6 +469,7 @@
|
||||
"SpacePrivateObjectsHelp": "",
|
||||
"Space_Cosmetic_Settings": "Algumas configurações cosméticas podem ser alteradas pelos administradores do espaço e substituirão as configurações do cliente para esse espaço.",
|
||||
"Split_All_Steps": "Divida todas as linhas em etapas separadas.",
|
||||
"Start": "",
|
||||
"StartDate": "Data Início",
|
||||
"Starting_Day": "Dia de início da semana",
|
||||
"Step": "Etapa",
|
||||
|
||||
@@ -298,6 +298,7 @@
|
||||
"Ratings": "Evaluări",
|
||||
"Recently_Viewed": "Vizualizate recent",
|
||||
"Recipe": "Rețetă",
|
||||
"RecipeStructure": "",
|
||||
"Recipe_Book": "Carte de rețete",
|
||||
"Recipe_Image": "Imagine a rețetei",
|
||||
"Recipes": "Rețete",
|
||||
@@ -349,6 +350,7 @@
|
||||
"SpaceName": "",
|
||||
"SpacePrivateObjectsHelp": "",
|
||||
"Split_All_Steps": "Împărțiți toate rândurile în pași separați.",
|
||||
"Start": "",
|
||||
"Starting_Day": "Ziua de început a săptămânii",
|
||||
"StartsWith": "",
|
||||
"StartsWithHelp": "",
|
||||
|
||||
@@ -449,6 +449,7 @@
|
||||
"RecipeBookHelp": "Кулинарные книги содержат записи рецептов или могут автоматически заполняться с помощью сохранённых фильтров поиска. ",
|
||||
"RecipeHelp": "Рецепты — основа Tandoor и состоят из общей информации и шагов, включающих ингредиенты, инструкции и многое другое. ",
|
||||
"RecipeStepsHelp": "Ингредиенты, инструкции и другое можно редактировать на вкладке «Шаги».",
|
||||
"RecipeStructure": "",
|
||||
"Recipe_Book": "Книга рецептов",
|
||||
"Recipe_Image": "Изображение рецепта",
|
||||
"Recipes": "Рецепты",
|
||||
@@ -536,6 +537,7 @@
|
||||
"Space_Cosmetic_Settings": "Администраторы пространства могут менять некоторые визуальные настройки, которые будут переопределять настройки клиента для данного пространства.",
|
||||
"Split": "Разделить",
|
||||
"Split_All_Steps": "Разделить все строки на отдельные шаги.",
|
||||
"Start": "",
|
||||
"StartDate": "Дата начала",
|
||||
"Starting_Day": "Начальный день недели",
|
||||
"StartsWith": "Начинается с",
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -337,6 +337,7 @@
|
||||
"Ratings": "Derecelendirmeler",
|
||||
"Recently_Viewed": "Son Görüntülenen",
|
||||
"Recipe": "Tarif",
|
||||
"RecipeStructure": "",
|
||||
"Recipe_Book": "Yemek Tarifi Kitabı",
|
||||
"Recipe_Image": "Tarif Resmi",
|
||||
"Recipes": "Tarifler",
|
||||
@@ -394,6 +395,7 @@
|
||||
"SpacePrivateObjectsHelp": "",
|
||||
"Space_Cosmetic_Settings": "Bazı kozmetik ayarlar alan yöneticileri tarafından değiştirilebilir ve o alanın istemci ayarlarını geçersiz kılar.",
|
||||
"Split_All_Steps": "Tüm satırları ayrı adımlara bölün.",
|
||||
"Start": "",
|
||||
"StartDate": "Başlangıç Tarihi",
|
||||
"Starting_Day": "Haftanın başlangıç günü",
|
||||
"StartsWith": "",
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -337,6 +337,7 @@
|
||||
"Ratings": "等级",
|
||||
"Recently_Viewed": "最近浏览",
|
||||
"Recipe": "食谱",
|
||||
"RecipeStructure": "",
|
||||
"Recipe_Book": "食谱书",
|
||||
"Recipe_Image": "食谱图像",
|
||||
"Recipes": "食谱",
|
||||
@@ -394,6 +395,7 @@
|
||||
"SpacePrivateObjectsHelp": "",
|
||||
"Space_Cosmetic_Settings": "空间管理员可以更改某些装饰设置,并将覆盖该空间的客户端设置。",
|
||||
"Split_All_Steps": "将所有行拆分为单独的步骤。",
|
||||
"Start": "",
|
||||
"StartDate": "开始日期",
|
||||
"Starting_Day": "一周中的第一天",
|
||||
"StartsWith": "",
|
||||
|
||||
@@ -450,6 +450,7 @@
|
||||
"RecipeBookHelp": "食譜書包含食譜書條目,或可以透過使用已儲存的搜尋篩選器自動填充。 ",
|
||||
"RecipeHelp": "食譜是 Tandoor 的基礎,由一般資訊和步驟組成,步驟由食材、指示等組成。 ",
|
||||
"RecipeStepsHelp": "食材、指示等可以在步驟標籤中編輯。",
|
||||
"RecipeStructure": "",
|
||||
"Recipe_Book": "食譜書",
|
||||
"Recipe_Image": "食譜圖片",
|
||||
"Recipes": "食譜",
|
||||
@@ -537,6 +538,7 @@
|
||||
"Space_Cosmetic_Settings": "空間管理員可以更改某些裝飾設置,並將覆蓋該空間的客戶端設置。",
|
||||
"Split": "分割",
|
||||
"Split_All_Steps": "將所有行拆分為單獨的步驟。",
|
||||
"Start": "",
|
||||
"StartDate": "開始日期",
|
||||
"Starting_Day": "開始日",
|
||||
"StartsWith": "開頭為",
|
||||
|
||||
@@ -94,6 +94,7 @@ models/PaginatedRecipeBookList.ts
|
||||
models/PaginatedRecipeImportList.ts
|
||||
models/PaginatedRecipeOverviewList.ts
|
||||
models/PaginatedShoppingListEntryList.ts
|
||||
models/PaginatedShoppingListList.ts
|
||||
models/PaginatedShoppingListRecipeList.ts
|
||||
models/PaginatedSpaceList.ts
|
||||
models/PaginatedStepList.ts
|
||||
@@ -140,6 +141,7 @@ models/PatchedRecipeBook.ts
|
||||
models/PatchedRecipeBookEntry.ts
|
||||
models/PatchedRecipeImport.ts
|
||||
models/PatchedSearchPreference.ts
|
||||
models/PatchedShoppingList.ts
|
||||
models/PatchedShoppingListEntry.ts
|
||||
models/PatchedShoppingListRecipe.ts
|
||||
models/PatchedSpace.ts
|
||||
@@ -174,6 +176,7 @@ models/SearchFields.ts
|
||||
models/SearchPreference.ts
|
||||
models/ServerSettings.ts
|
||||
models/ShareLink.ts
|
||||
models/ShoppingList.ts
|
||||
models/ShoppingListEntry.ts
|
||||
models/ShoppingListEntryBulk.ts
|
||||
models/ShoppingListEntryBulkCreate.ts
|
||||
|
||||
@@ -85,6 +85,7 @@ import type {
|
||||
PaginatedRecipeImportList,
|
||||
PaginatedRecipeOverviewList,
|
||||
PaginatedShoppingListEntryList,
|
||||
PaginatedShoppingListList,
|
||||
PaginatedShoppingListRecipeList,
|
||||
PaginatedSpaceList,
|
||||
PaginatedStepList,
|
||||
@@ -131,6 +132,7 @@ import type {
|
||||
PatchedRecipeBookEntry,
|
||||
PatchedRecipeImport,
|
||||
PatchedSearchPreference,
|
||||
PatchedShoppingList,
|
||||
PatchedShoppingListEntry,
|
||||
PatchedShoppingListRecipe,
|
||||
PatchedSpace,
|
||||
@@ -163,6 +165,7 @@ import type {
|
||||
SearchPreference,
|
||||
ServerSettings,
|
||||
ShareLink,
|
||||
ShoppingList,
|
||||
ShoppingListEntry,
|
||||
ShoppingListEntryBulk,
|
||||
ShoppingListEntryBulkCreate,
|
||||
@@ -324,6 +327,8 @@ import {
|
||||
PaginatedRecipeOverviewListToJSON,
|
||||
PaginatedShoppingListEntryListFromJSON,
|
||||
PaginatedShoppingListEntryListToJSON,
|
||||
PaginatedShoppingListListFromJSON,
|
||||
PaginatedShoppingListListToJSON,
|
||||
PaginatedShoppingListRecipeListFromJSON,
|
||||
PaginatedShoppingListRecipeListToJSON,
|
||||
PaginatedSpaceListFromJSON,
|
||||
@@ -416,6 +421,8 @@ import {
|
||||
PatchedRecipeImportToJSON,
|
||||
PatchedSearchPreferenceFromJSON,
|
||||
PatchedSearchPreferenceToJSON,
|
||||
PatchedShoppingListFromJSON,
|
||||
PatchedShoppingListToJSON,
|
||||
PatchedShoppingListEntryFromJSON,
|
||||
PatchedShoppingListEntryToJSON,
|
||||
PatchedShoppingListRecipeFromJSON,
|
||||
@@ -480,6 +487,8 @@ import {
|
||||
ServerSettingsToJSON,
|
||||
ShareLinkFromJSON,
|
||||
ShareLinkToJSON,
|
||||
ShoppingListFromJSON,
|
||||
ShoppingListToJSON,
|
||||
ShoppingListEntryFromJSON,
|
||||
ShoppingListEntryToJSON,
|
||||
ShoppingListEntryBulkFromJSON,
|
||||
@@ -1955,6 +1964,21 @@ export interface ApiShareLinkRetrieveRequest {
|
||||
id: number;
|
||||
}
|
||||
|
||||
export interface ApiShoppingListCascadingListRequest {
|
||||
id: number;
|
||||
cache?: boolean;
|
||||
page?: number;
|
||||
pageSize?: number;
|
||||
}
|
||||
|
||||
export interface ApiShoppingListCreateRequest {
|
||||
shoppingList?: Omit<ShoppingList, 'createdAt'|'updatedAt'>;
|
||||
}
|
||||
|
||||
export interface ApiShoppingListDestroyRequest {
|
||||
id: number;
|
||||
}
|
||||
|
||||
export interface ApiShoppingListEntryBulkCreateRequest {
|
||||
shoppingListEntryBulk: Omit<ShoppingListEntryBulk, 'timestamp'>;
|
||||
}
|
||||
@@ -1988,6 +2012,30 @@ export interface ApiShoppingListEntryUpdateRequest {
|
||||
shoppingListEntry: Omit<ShoppingListEntry, 'listRecipeData'|'createdBy'|'createdAt'|'updatedAt'>;
|
||||
}
|
||||
|
||||
export interface ApiShoppingListListRequest {
|
||||
page?: number;
|
||||
pageSize?: number;
|
||||
}
|
||||
|
||||
export interface ApiShoppingListNullingListRequest {
|
||||
id: number;
|
||||
cache?: boolean;
|
||||
page?: number;
|
||||
pageSize?: number;
|
||||
}
|
||||
|
||||
export interface ApiShoppingListPartialUpdateRequest {
|
||||
id: number;
|
||||
patchedShoppingList?: Omit<PatchedShoppingList, 'createdAt'|'updatedAt'>;
|
||||
}
|
||||
|
||||
export interface ApiShoppingListProtectingListRequest {
|
||||
id: number;
|
||||
cache?: boolean;
|
||||
page?: number;
|
||||
pageSize?: number;
|
||||
}
|
||||
|
||||
export interface ApiShoppingListRecipeBulkCreateEntriesCreateRequest {
|
||||
id: number;
|
||||
shoppingListEntryBulkCreate: ShoppingListEntryBulkCreate;
|
||||
@@ -2021,6 +2069,15 @@ export interface ApiShoppingListRecipeUpdateRequest {
|
||||
shoppingListRecipe: Omit<ShoppingListRecipe, 'recipeData'|'mealPlanData'|'createdBy'>;
|
||||
}
|
||||
|
||||
export interface ApiShoppingListRetrieveRequest {
|
||||
id: number;
|
||||
}
|
||||
|
||||
export interface ApiShoppingListUpdateRequest {
|
||||
id: number;
|
||||
shoppingList?: Omit<ShoppingList, 'createdAt'|'updatedAt'>;
|
||||
}
|
||||
|
||||
export interface ApiSpaceCreateRequest {
|
||||
space?: Omit<Space, 'createdBy'|'createdAt'|'maxRecipes'|'maxFileStorageMb'|'maxUsers'|'allowSharing'|'demo'|'userCount'|'recipeCount'|'fileSizeMb'|'aiMonthlyCreditsUsed'>;
|
||||
}
|
||||
@@ -14551,6 +14608,124 @@ export class ApiApi extends runtime.BaseAPI {
|
||||
return await response.value();
|
||||
}
|
||||
|
||||
/**
|
||||
* get a paginated list of objects that will be cascaded (deleted) when deleting the selected object
|
||||
*/
|
||||
async apiShoppingListCascadingListRaw(requestParameters: ApiShoppingListCascadingListRequest, initOverrides?: RequestInit | runtime.InitOverrideFunction): Promise<runtime.ApiResponse<PaginatedGenericModelReferenceList>> {
|
||||
if (requestParameters['id'] == null) {
|
||||
throw new runtime.RequiredError(
|
||||
'id',
|
||||
'Required parameter "id" was null or undefined when calling apiShoppingListCascadingList().'
|
||||
);
|
||||
}
|
||||
|
||||
const queryParameters: any = {};
|
||||
|
||||
if (requestParameters['cache'] != null) {
|
||||
queryParameters['cache'] = requestParameters['cache'];
|
||||
}
|
||||
|
||||
if (requestParameters['page'] != null) {
|
||||
queryParameters['page'] = requestParameters['page'];
|
||||
}
|
||||
|
||||
if (requestParameters['pageSize'] != null) {
|
||||
queryParameters['page_size'] = requestParameters['pageSize'];
|
||||
}
|
||||
|
||||
const headerParameters: runtime.HTTPHeaders = {};
|
||||
|
||||
if (this.configuration && this.configuration.apiKey) {
|
||||
headerParameters["Authorization"] = await this.configuration.apiKey("Authorization"); // ApiKeyAuth authentication
|
||||
}
|
||||
|
||||
const response = await this.request({
|
||||
path: `/api/shopping-list/{id}/cascading/`.replace(`{${"id"}}`, encodeURIComponent(String(requestParameters['id']))),
|
||||
method: 'GET',
|
||||
headers: headerParameters,
|
||||
query: queryParameters,
|
||||
}, initOverrides);
|
||||
|
||||
return new runtime.JSONApiResponse(response, (jsonValue) => PaginatedGenericModelReferenceListFromJSON(jsonValue));
|
||||
}
|
||||
|
||||
/**
|
||||
* get a paginated list of objects that will be cascaded (deleted) when deleting the selected object
|
||||
*/
|
||||
async apiShoppingListCascadingList(requestParameters: ApiShoppingListCascadingListRequest, initOverrides?: RequestInit | runtime.InitOverrideFunction): Promise<PaginatedGenericModelReferenceList> {
|
||||
const response = await this.apiShoppingListCascadingListRaw(requestParameters, initOverrides);
|
||||
return await response.value();
|
||||
}
|
||||
|
||||
/**
|
||||
* logs request counts to redis cache total/per user/
|
||||
*/
|
||||
async apiShoppingListCreateRaw(requestParameters: ApiShoppingListCreateRequest, initOverrides?: RequestInit | runtime.InitOverrideFunction): Promise<runtime.ApiResponse<ShoppingList>> {
|
||||
const queryParameters: any = {};
|
||||
|
||||
const headerParameters: runtime.HTTPHeaders = {};
|
||||
|
||||
headerParameters['Content-Type'] = 'application/json';
|
||||
|
||||
if (this.configuration && this.configuration.apiKey) {
|
||||
headerParameters["Authorization"] = await this.configuration.apiKey("Authorization"); // ApiKeyAuth authentication
|
||||
}
|
||||
|
||||
const response = await this.request({
|
||||
path: `/api/shopping-list/`,
|
||||
method: 'POST',
|
||||
headers: headerParameters,
|
||||
query: queryParameters,
|
||||
body: ShoppingListToJSON(requestParameters['shoppingList']),
|
||||
}, initOverrides);
|
||||
|
||||
return new runtime.JSONApiResponse(response, (jsonValue) => ShoppingListFromJSON(jsonValue));
|
||||
}
|
||||
|
||||
/**
|
||||
* logs request counts to redis cache total/per user/
|
||||
*/
|
||||
async apiShoppingListCreate(requestParameters: ApiShoppingListCreateRequest = {}, initOverrides?: RequestInit | runtime.InitOverrideFunction): Promise<ShoppingList> {
|
||||
const response = await this.apiShoppingListCreateRaw(requestParameters, initOverrides);
|
||||
return await response.value();
|
||||
}
|
||||
|
||||
/**
|
||||
* logs request counts to redis cache total/per user/
|
||||
*/
|
||||
async apiShoppingListDestroyRaw(requestParameters: ApiShoppingListDestroyRequest, initOverrides?: RequestInit | runtime.InitOverrideFunction): Promise<runtime.ApiResponse<void>> {
|
||||
if (requestParameters['id'] == null) {
|
||||
throw new runtime.RequiredError(
|
||||
'id',
|
||||
'Required parameter "id" was null or undefined when calling apiShoppingListDestroy().'
|
||||
);
|
||||
}
|
||||
|
||||
const queryParameters: any = {};
|
||||
|
||||
const headerParameters: runtime.HTTPHeaders = {};
|
||||
|
||||
if (this.configuration && this.configuration.apiKey) {
|
||||
headerParameters["Authorization"] = await this.configuration.apiKey("Authorization"); // ApiKeyAuth authentication
|
||||
}
|
||||
|
||||
const response = await this.request({
|
||||
path: `/api/shopping-list/{id}/`.replace(`{${"id"}}`, encodeURIComponent(String(requestParameters['id']))),
|
||||
method: 'DELETE',
|
||||
headers: headerParameters,
|
||||
query: queryParameters,
|
||||
}, initOverrides);
|
||||
|
||||
return new runtime.VoidApiResponse(response);
|
||||
}
|
||||
|
||||
/**
|
||||
* logs request counts to redis cache total/per user/
|
||||
*/
|
||||
async apiShoppingListDestroy(requestParameters: ApiShoppingListDestroyRequest, initOverrides?: RequestInit | runtime.InitOverrideFunction): Promise<void> {
|
||||
await this.apiShoppingListDestroyRaw(requestParameters, initOverrides);
|
||||
}
|
||||
|
||||
/**
|
||||
* individual entries of a shopping list automatically filtered to only contain unchecked items that are not older than the shopping recent days setting to not bloat endpoint
|
||||
*/
|
||||
@@ -14837,6 +15012,182 @@ export class ApiApi extends runtime.BaseAPI {
|
||||
return await response.value();
|
||||
}
|
||||
|
||||
/**
|
||||
* logs request counts to redis cache total/per user/
|
||||
*/
|
||||
async apiShoppingListListRaw(requestParameters: ApiShoppingListListRequest, initOverrides?: RequestInit | runtime.InitOverrideFunction): Promise<runtime.ApiResponse<PaginatedShoppingListList>> {
|
||||
const queryParameters: any = {};
|
||||
|
||||
if (requestParameters['page'] != null) {
|
||||
queryParameters['page'] = requestParameters['page'];
|
||||
}
|
||||
|
||||
if (requestParameters['pageSize'] != null) {
|
||||
queryParameters['page_size'] = requestParameters['pageSize'];
|
||||
}
|
||||
|
||||
const headerParameters: runtime.HTTPHeaders = {};
|
||||
|
||||
if (this.configuration && this.configuration.apiKey) {
|
||||
headerParameters["Authorization"] = await this.configuration.apiKey("Authorization"); // ApiKeyAuth authentication
|
||||
}
|
||||
|
||||
const response = await this.request({
|
||||
path: `/api/shopping-list/`,
|
||||
method: 'GET',
|
||||
headers: headerParameters,
|
||||
query: queryParameters,
|
||||
}, initOverrides);
|
||||
|
||||
return new runtime.JSONApiResponse(response, (jsonValue) => PaginatedShoppingListListFromJSON(jsonValue));
|
||||
}
|
||||
|
||||
/**
|
||||
* logs request counts to redis cache total/per user/
|
||||
*/
|
||||
async apiShoppingListList(requestParameters: ApiShoppingListListRequest = {}, initOverrides?: RequestInit | runtime.InitOverrideFunction): Promise<PaginatedShoppingListList> {
|
||||
const response = await this.apiShoppingListListRaw(requestParameters, initOverrides);
|
||||
return await response.value();
|
||||
}
|
||||
|
||||
/**
|
||||
* get a paginated list of objects where the selected object will be removed whe its deleted
|
||||
*/
|
||||
async apiShoppingListNullingListRaw(requestParameters: ApiShoppingListNullingListRequest, initOverrides?: RequestInit | runtime.InitOverrideFunction): Promise<runtime.ApiResponse<PaginatedGenericModelReferenceList>> {
|
||||
if (requestParameters['id'] == null) {
|
||||
throw new runtime.RequiredError(
|
||||
'id',
|
||||
'Required parameter "id" was null or undefined when calling apiShoppingListNullingList().'
|
||||
);
|
||||
}
|
||||
|
||||
const queryParameters: any = {};
|
||||
|
||||
if (requestParameters['cache'] != null) {
|
||||
queryParameters['cache'] = requestParameters['cache'];
|
||||
}
|
||||
|
||||
if (requestParameters['page'] != null) {
|
||||
queryParameters['page'] = requestParameters['page'];
|
||||
}
|
||||
|
||||
if (requestParameters['pageSize'] != null) {
|
||||
queryParameters['page_size'] = requestParameters['pageSize'];
|
||||
}
|
||||
|
||||
const headerParameters: runtime.HTTPHeaders = {};
|
||||
|
||||
if (this.configuration && this.configuration.apiKey) {
|
||||
headerParameters["Authorization"] = await this.configuration.apiKey("Authorization"); // ApiKeyAuth authentication
|
||||
}
|
||||
|
||||
const response = await this.request({
|
||||
path: `/api/shopping-list/{id}/nulling/`.replace(`{${"id"}}`, encodeURIComponent(String(requestParameters['id']))),
|
||||
method: 'GET',
|
||||
headers: headerParameters,
|
||||
query: queryParameters,
|
||||
}, initOverrides);
|
||||
|
||||
return new runtime.JSONApiResponse(response, (jsonValue) => PaginatedGenericModelReferenceListFromJSON(jsonValue));
|
||||
}
|
||||
|
||||
/**
|
||||
* get a paginated list of objects where the selected object will be removed whe its deleted
|
||||
*/
|
||||
async apiShoppingListNullingList(requestParameters: ApiShoppingListNullingListRequest, initOverrides?: RequestInit | runtime.InitOverrideFunction): Promise<PaginatedGenericModelReferenceList> {
|
||||
const response = await this.apiShoppingListNullingListRaw(requestParameters, initOverrides);
|
||||
return await response.value();
|
||||
}
|
||||
|
||||
/**
|
||||
* logs request counts to redis cache total/per user/
|
||||
*/
|
||||
async apiShoppingListPartialUpdateRaw(requestParameters: ApiShoppingListPartialUpdateRequest, initOverrides?: RequestInit | runtime.InitOverrideFunction): Promise<runtime.ApiResponse<ShoppingList>> {
|
||||
if (requestParameters['id'] == null) {
|
||||
throw new runtime.RequiredError(
|
||||
'id',
|
||||
'Required parameter "id" was null or undefined when calling apiShoppingListPartialUpdate().'
|
||||
);
|
||||
}
|
||||
|
||||
const queryParameters: any = {};
|
||||
|
||||
const headerParameters: runtime.HTTPHeaders = {};
|
||||
|
||||
headerParameters['Content-Type'] = 'application/json';
|
||||
|
||||
if (this.configuration && this.configuration.apiKey) {
|
||||
headerParameters["Authorization"] = await this.configuration.apiKey("Authorization"); // ApiKeyAuth authentication
|
||||
}
|
||||
|
||||
const response = await this.request({
|
||||
path: `/api/shopping-list/{id}/`.replace(`{${"id"}}`, encodeURIComponent(String(requestParameters['id']))),
|
||||
method: 'PATCH',
|
||||
headers: headerParameters,
|
||||
query: queryParameters,
|
||||
body: PatchedShoppingListToJSON(requestParameters['patchedShoppingList']),
|
||||
}, initOverrides);
|
||||
|
||||
return new runtime.JSONApiResponse(response, (jsonValue) => ShoppingListFromJSON(jsonValue));
|
||||
}
|
||||
|
||||
/**
|
||||
* logs request counts to redis cache total/per user/
|
||||
*/
|
||||
async apiShoppingListPartialUpdate(requestParameters: ApiShoppingListPartialUpdateRequest, initOverrides?: RequestInit | runtime.InitOverrideFunction): Promise<ShoppingList> {
|
||||
const response = await this.apiShoppingListPartialUpdateRaw(requestParameters, initOverrides);
|
||||
return await response.value();
|
||||
}
|
||||
|
||||
/**
|
||||
* get a paginated list of objects that are protecting the selected object form being deleted
|
||||
*/
|
||||
async apiShoppingListProtectingListRaw(requestParameters: ApiShoppingListProtectingListRequest, initOverrides?: RequestInit | runtime.InitOverrideFunction): Promise<runtime.ApiResponse<PaginatedGenericModelReferenceList>> {
|
||||
if (requestParameters['id'] == null) {
|
||||
throw new runtime.RequiredError(
|
||||
'id',
|
||||
'Required parameter "id" was null or undefined when calling apiShoppingListProtectingList().'
|
||||
);
|
||||
}
|
||||
|
||||
const queryParameters: any = {};
|
||||
|
||||
if (requestParameters['cache'] != null) {
|
||||
queryParameters['cache'] = requestParameters['cache'];
|
||||
}
|
||||
|
||||
if (requestParameters['page'] != null) {
|
||||
queryParameters['page'] = requestParameters['page'];
|
||||
}
|
||||
|
||||
if (requestParameters['pageSize'] != null) {
|
||||
queryParameters['page_size'] = requestParameters['pageSize'];
|
||||
}
|
||||
|
||||
const headerParameters: runtime.HTTPHeaders = {};
|
||||
|
||||
if (this.configuration && this.configuration.apiKey) {
|
||||
headerParameters["Authorization"] = await this.configuration.apiKey("Authorization"); // ApiKeyAuth authentication
|
||||
}
|
||||
|
||||
const response = await this.request({
|
||||
path: `/api/shopping-list/{id}/protecting/`.replace(`{${"id"}}`, encodeURIComponent(String(requestParameters['id']))),
|
||||
method: 'GET',
|
||||
headers: headerParameters,
|
||||
query: queryParameters,
|
||||
}, initOverrides);
|
||||
|
||||
return new runtime.JSONApiResponse(response, (jsonValue) => PaginatedGenericModelReferenceListFromJSON(jsonValue));
|
||||
}
|
||||
|
||||
/**
|
||||
* get a paginated list of objects that are protecting the selected object form being deleted
|
||||
*/
|
||||
async apiShoppingListProtectingList(requestParameters: ApiShoppingListProtectingListRequest, initOverrides?: RequestInit | runtime.InitOverrideFunction): Promise<PaginatedGenericModelReferenceList> {
|
||||
const response = await this.apiShoppingListProtectingListRaw(requestParameters, initOverrides);
|
||||
return await response.value();
|
||||
}
|
||||
|
||||
/**
|
||||
* logs request counts to redis cache total/per user/
|
||||
*/
|
||||
@@ -15126,6 +15477,83 @@ export class ApiApi extends runtime.BaseAPI {
|
||||
return await response.value();
|
||||
}
|
||||
|
||||
/**
|
||||
* logs request counts to redis cache total/per user/
|
||||
*/
|
||||
async apiShoppingListRetrieveRaw(requestParameters: ApiShoppingListRetrieveRequest, initOverrides?: RequestInit | runtime.InitOverrideFunction): Promise<runtime.ApiResponse<ShoppingList>> {
|
||||
if (requestParameters['id'] == null) {
|
||||
throw new runtime.RequiredError(
|
||||
'id',
|
||||
'Required parameter "id" was null or undefined when calling apiShoppingListRetrieve().'
|
||||
);
|
||||
}
|
||||
|
||||
const queryParameters: any = {};
|
||||
|
||||
const headerParameters: runtime.HTTPHeaders = {};
|
||||
|
||||
if (this.configuration && this.configuration.apiKey) {
|
||||
headerParameters["Authorization"] = await this.configuration.apiKey("Authorization"); // ApiKeyAuth authentication
|
||||
}
|
||||
|
||||
const response = await this.request({
|
||||
path: `/api/shopping-list/{id}/`.replace(`{${"id"}}`, encodeURIComponent(String(requestParameters['id']))),
|
||||
method: 'GET',
|
||||
headers: headerParameters,
|
||||
query: queryParameters,
|
||||
}, initOverrides);
|
||||
|
||||
return new runtime.JSONApiResponse(response, (jsonValue) => ShoppingListFromJSON(jsonValue));
|
||||
}
|
||||
|
||||
/**
|
||||
* logs request counts to redis cache total/per user/
|
||||
*/
|
||||
async apiShoppingListRetrieve(requestParameters: ApiShoppingListRetrieveRequest, initOverrides?: RequestInit | runtime.InitOverrideFunction): Promise<ShoppingList> {
|
||||
const response = await this.apiShoppingListRetrieveRaw(requestParameters, initOverrides);
|
||||
return await response.value();
|
||||
}
|
||||
|
||||
/**
|
||||
* logs request counts to redis cache total/per user/
|
||||
*/
|
||||
async apiShoppingListUpdateRaw(requestParameters: ApiShoppingListUpdateRequest, initOverrides?: RequestInit | runtime.InitOverrideFunction): Promise<runtime.ApiResponse<ShoppingList>> {
|
||||
if (requestParameters['id'] == null) {
|
||||
throw new runtime.RequiredError(
|
||||
'id',
|
||||
'Required parameter "id" was null or undefined when calling apiShoppingListUpdate().'
|
||||
);
|
||||
}
|
||||
|
||||
const queryParameters: any = {};
|
||||
|
||||
const headerParameters: runtime.HTTPHeaders = {};
|
||||
|
||||
headerParameters['Content-Type'] = 'application/json';
|
||||
|
||||
if (this.configuration && this.configuration.apiKey) {
|
||||
headerParameters["Authorization"] = await this.configuration.apiKey("Authorization"); // ApiKeyAuth authentication
|
||||
}
|
||||
|
||||
const response = await this.request({
|
||||
path: `/api/shopping-list/{id}/`.replace(`{${"id"}}`, encodeURIComponent(String(requestParameters['id']))),
|
||||
method: 'PUT',
|
||||
headers: headerParameters,
|
||||
query: queryParameters,
|
||||
body: ShoppingListToJSON(requestParameters['shoppingList']),
|
||||
}, initOverrides);
|
||||
|
||||
return new runtime.JSONApiResponse(response, (jsonValue) => ShoppingListFromJSON(jsonValue));
|
||||
}
|
||||
|
||||
/**
|
||||
* logs request counts to redis cache total/per user/
|
||||
*/
|
||||
async apiShoppingListUpdate(requestParameters: ApiShoppingListUpdateRequest, initOverrides?: RequestInit | runtime.InitOverrideFunction): Promise<ShoppingList> {
|
||||
const response = await this.apiShoppingListUpdateRaw(requestParameters, initOverrides);
|
||||
return await response.value();
|
||||
}
|
||||
|
||||
/**
|
||||
* logs request counts to redis cache total/per user/
|
||||
*/
|
||||
|
||||
101
vue3/src/openapi/models/PaginatedShoppingListList.ts
Normal file
101
vue3/src/openapi/models/PaginatedShoppingListList.ts
Normal file
@@ -0,0 +1,101 @@
|
||||
/* tslint:disable */
|
||||
/* eslint-disable */
|
||||
/**
|
||||
* Tandoor
|
||||
* Tandoor API Docs
|
||||
*
|
||||
* The version of the OpenAPI document: 0.0.0
|
||||
*
|
||||
*
|
||||
* NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech).
|
||||
* https://openapi-generator.tech
|
||||
* Do not edit the class manually.
|
||||
*/
|
||||
|
||||
import { mapValues } from '../runtime';
|
||||
import type { ShoppingList } from './ShoppingList';
|
||||
import {
|
||||
ShoppingListFromJSON,
|
||||
ShoppingListFromJSONTyped,
|
||||
ShoppingListToJSON,
|
||||
} from './ShoppingList';
|
||||
|
||||
/**
|
||||
*
|
||||
* @export
|
||||
* @interface PaginatedShoppingListList
|
||||
*/
|
||||
export interface PaginatedShoppingListList {
|
||||
/**
|
||||
*
|
||||
* @type {number}
|
||||
* @memberof PaginatedShoppingListList
|
||||
*/
|
||||
count: number;
|
||||
/**
|
||||
*
|
||||
* @type {string}
|
||||
* @memberof PaginatedShoppingListList
|
||||
*/
|
||||
next?: string;
|
||||
/**
|
||||
*
|
||||
* @type {string}
|
||||
* @memberof PaginatedShoppingListList
|
||||
*/
|
||||
previous?: string;
|
||||
/**
|
||||
*
|
||||
* @type {Array<ShoppingList>}
|
||||
* @memberof PaginatedShoppingListList
|
||||
*/
|
||||
results: Array<ShoppingList>;
|
||||
/**
|
||||
*
|
||||
* @type {Date}
|
||||
* @memberof PaginatedShoppingListList
|
||||
*/
|
||||
timestamp?: Date;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if a given object implements the PaginatedShoppingListList interface.
|
||||
*/
|
||||
export function instanceOfPaginatedShoppingListList(value: object): value is PaginatedShoppingListList {
|
||||
if (!('count' in value) || value['count'] === undefined) return false;
|
||||
if (!('results' in value) || value['results'] === undefined) return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
export function PaginatedShoppingListListFromJSON(json: any): PaginatedShoppingListList {
|
||||
return PaginatedShoppingListListFromJSONTyped(json, false);
|
||||
}
|
||||
|
||||
export function PaginatedShoppingListListFromJSONTyped(json: any, ignoreDiscriminator: boolean): PaginatedShoppingListList {
|
||||
if (json == null) {
|
||||
return json;
|
||||
}
|
||||
return {
|
||||
|
||||
'count': json['count'],
|
||||
'next': json['next'] == null ? undefined : json['next'],
|
||||
'previous': json['previous'] == null ? undefined : json['previous'],
|
||||
'results': ((json['results'] as Array<any>).map(ShoppingListFromJSON)),
|
||||
'timestamp': json['timestamp'] == null ? undefined : (new Date(json['timestamp'])),
|
||||
};
|
||||
}
|
||||
|
||||
export function PaginatedShoppingListListToJSON(value?: PaginatedShoppingListList | null): any {
|
||||
if (value == null) {
|
||||
return value;
|
||||
}
|
||||
return {
|
||||
|
||||
'count': value['count'],
|
||||
'next': value['next'],
|
||||
'previous': value['previous'],
|
||||
'results': ((value['results'] as Array<any>).map(ShoppingListToJSON)),
|
||||
'timestamp': value['timestamp'] == null ? undefined : ((value['timestamp']).toISOString()),
|
||||
};
|
||||
}
|
||||
|
||||
98
vue3/src/openapi/models/PatchedShoppingList.ts
Normal file
98
vue3/src/openapi/models/PatchedShoppingList.ts
Normal file
@@ -0,0 +1,98 @@
|
||||
/* tslint:disable */
|
||||
/* eslint-disable */
|
||||
/**
|
||||
* Tandoor
|
||||
* Tandoor API Docs
|
||||
*
|
||||
* The version of the OpenAPI document: 0.0.0
|
||||
*
|
||||
*
|
||||
* NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech).
|
||||
* https://openapi-generator.tech
|
||||
* Do not edit the class manually.
|
||||
*/
|
||||
|
||||
import { mapValues } from '../runtime';
|
||||
/**
|
||||
* Adds nested create feature
|
||||
* @export
|
||||
* @interface PatchedShoppingList
|
||||
*/
|
||||
export interface PatchedShoppingList {
|
||||
/**
|
||||
*
|
||||
* @type {number}
|
||||
* @memberof PatchedShoppingList
|
||||
*/
|
||||
id?: number;
|
||||
/**
|
||||
*
|
||||
* @type {string}
|
||||
* @memberof PatchedShoppingList
|
||||
*/
|
||||
name?: string;
|
||||
/**
|
||||
*
|
||||
* @type {string}
|
||||
* @memberof PatchedShoppingList
|
||||
*/
|
||||
description?: string;
|
||||
/**
|
||||
*
|
||||
* @type {string}
|
||||
* @memberof PatchedShoppingList
|
||||
*/
|
||||
color?: string;
|
||||
/**
|
||||
*
|
||||
* @type {Date}
|
||||
* @memberof PatchedShoppingList
|
||||
*/
|
||||
readonly createdAt?: Date;
|
||||
/**
|
||||
*
|
||||
* @type {Date}
|
||||
* @memberof PatchedShoppingList
|
||||
*/
|
||||
readonly updatedAt?: Date;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if a given object implements the PatchedShoppingList interface.
|
||||
*/
|
||||
export function instanceOfPatchedShoppingList(value: object): value is PatchedShoppingList {
|
||||
return true;
|
||||
}
|
||||
|
||||
export function PatchedShoppingListFromJSON(json: any): PatchedShoppingList {
|
||||
return PatchedShoppingListFromJSONTyped(json, false);
|
||||
}
|
||||
|
||||
export function PatchedShoppingListFromJSONTyped(json: any, ignoreDiscriminator: boolean): PatchedShoppingList {
|
||||
if (json == null) {
|
||||
return json;
|
||||
}
|
||||
return {
|
||||
|
||||
'id': json['id'] == null ? undefined : json['id'],
|
||||
'name': json['name'] == null ? undefined : json['name'],
|
||||
'description': json['description'] == null ? undefined : json['description'],
|
||||
'color': json['color'] == null ? undefined : json['color'],
|
||||
'createdAt': json['created_at'] == null ? undefined : (new Date(json['created_at'])),
|
||||
'updatedAt': json['updated_at'] == null ? undefined : (new Date(json['updated_at'])),
|
||||
};
|
||||
}
|
||||
|
||||
export function PatchedShoppingListToJSON(value?: Omit<PatchedShoppingList, 'createdAt'|'updatedAt'> | null): any {
|
||||
if (value == null) {
|
||||
return value;
|
||||
}
|
||||
return {
|
||||
|
||||
'id': value['id'],
|
||||
'name': value['name'],
|
||||
'description': value['description'],
|
||||
'color': value['color'],
|
||||
};
|
||||
}
|
||||
|
||||
100
vue3/src/openapi/models/ShoppingList.ts
Normal file
100
vue3/src/openapi/models/ShoppingList.ts
Normal file
@@ -0,0 +1,100 @@
|
||||
/* tslint:disable */
|
||||
/* eslint-disable */
|
||||
/**
|
||||
* Tandoor
|
||||
* Tandoor API Docs
|
||||
*
|
||||
* The version of the OpenAPI document: 0.0.0
|
||||
*
|
||||
*
|
||||
* NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech).
|
||||
* https://openapi-generator.tech
|
||||
* Do not edit the class manually.
|
||||
*/
|
||||
|
||||
import { mapValues } from '../runtime';
|
||||
/**
|
||||
* Adds nested create feature
|
||||
* @export
|
||||
* @interface ShoppingList
|
||||
*/
|
||||
export interface ShoppingList {
|
||||
/**
|
||||
*
|
||||
* @type {number}
|
||||
* @memberof ShoppingList
|
||||
*/
|
||||
id?: number;
|
||||
/**
|
||||
*
|
||||
* @type {string}
|
||||
* @memberof ShoppingList
|
||||
*/
|
||||
name?: string;
|
||||
/**
|
||||
*
|
||||
* @type {string}
|
||||
* @memberof ShoppingList
|
||||
*/
|
||||
description?: string;
|
||||
/**
|
||||
*
|
||||
* @type {string}
|
||||
* @memberof ShoppingList
|
||||
*/
|
||||
color?: string;
|
||||
/**
|
||||
*
|
||||
* @type {Date}
|
||||
* @memberof ShoppingList
|
||||
*/
|
||||
readonly createdAt: Date;
|
||||
/**
|
||||
*
|
||||
* @type {Date}
|
||||
* @memberof ShoppingList
|
||||
*/
|
||||
readonly updatedAt: Date;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if a given object implements the ShoppingList interface.
|
||||
*/
|
||||
export function instanceOfShoppingList(value: object): value is ShoppingList {
|
||||
if (!('createdAt' in value) || value['createdAt'] === undefined) return false;
|
||||
if (!('updatedAt' in value) || value['updatedAt'] === undefined) return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
export function ShoppingListFromJSON(json: any): ShoppingList {
|
||||
return ShoppingListFromJSONTyped(json, false);
|
||||
}
|
||||
|
||||
export function ShoppingListFromJSONTyped(json: any, ignoreDiscriminator: boolean): ShoppingList {
|
||||
if (json == null) {
|
||||
return json;
|
||||
}
|
||||
return {
|
||||
|
||||
'id': json['id'] == null ? undefined : json['id'],
|
||||
'name': json['name'] == null ? undefined : json['name'],
|
||||
'description': json['description'] == null ? undefined : json['description'],
|
||||
'color': json['color'] == null ? undefined : json['color'],
|
||||
'createdAt': (new Date(json['created_at'])),
|
||||
'updatedAt': (new Date(json['updated_at'])),
|
||||
};
|
||||
}
|
||||
|
||||
export function ShoppingListToJSON(value?: Omit<ShoppingList, 'createdAt'|'updatedAt'> | null): any {
|
||||
if (value == null) {
|
||||
return value;
|
||||
}
|
||||
return {
|
||||
|
||||
'id': value['id'],
|
||||
'name': value['name'],
|
||||
'description': value['description'],
|
||||
'color': value['color'],
|
||||
};
|
||||
}
|
||||
|
||||
@@ -92,6 +92,7 @@ export * from './PaginatedRecipeBookList';
|
||||
export * from './PaginatedRecipeImportList';
|
||||
export * from './PaginatedRecipeOverviewList';
|
||||
export * from './PaginatedShoppingListEntryList';
|
||||
export * from './PaginatedShoppingListList';
|
||||
export * from './PaginatedShoppingListRecipeList';
|
||||
export * from './PaginatedSpaceList';
|
||||
export * from './PaginatedStepList';
|
||||
@@ -138,6 +139,7 @@ export * from './PatchedRecipeBook';
|
||||
export * from './PatchedRecipeBookEntry';
|
||||
export * from './PatchedRecipeImport';
|
||||
export * from './PatchedSearchPreference';
|
||||
export * from './PatchedShoppingList';
|
||||
export * from './PatchedShoppingListEntry';
|
||||
export * from './PatchedShoppingListRecipe';
|
||||
export * from './PatchedSpace';
|
||||
@@ -172,6 +174,7 @@ export * from './SearchFields';
|
||||
export * from './SearchPreference';
|
||||
export * from './ServerSettings';
|
||||
export * from './ShareLink';
|
||||
export * from './ShoppingList';
|
||||
export * from './ShoppingListEntry';
|
||||
export * from './ShoppingListEntryBulk';
|
||||
export * from './ShoppingListEntryBulkCreate';
|
||||
|
||||
@@ -31,6 +31,7 @@
|
||||
</v-row>
|
||||
<v-row dense>
|
||||
<database-model-col model="Supermarket"></database-model-col>
|
||||
<database-model-col model="ShoppingList"></database-model-col>
|
||||
<database-model-col model="SupermarketCategory"></database-model-col>
|
||||
<database-model-col model="MealType"></database-model-col>
|
||||
</v-row>
|
||||
|
||||
@@ -95,6 +95,9 @@
|
||||
<v-chip label v-if="item.id == useUserPreferenceStore().activeSpace.id!" color="success">{{ $t('Active') }}</v-chip>
|
||||
<v-chip label v-else color="info" @click="useUserPreferenceStore().switchSpace(item)">{{ $t('Select') }}</v-chip>
|
||||
</template>
|
||||
<template v-slot:item.color="{ item }">
|
||||
<v-chip label :color="item.color">{{ item.color }}</v-chip>
|
||||
</template>
|
||||
<template v-slot:item.action="{ item }">
|
||||
<v-btn class="float-right" icon="$menu" variant="plain">
|
||||
<v-icon icon="$menu"></v-icon>
|
||||
|
||||
@@ -606,7 +606,7 @@ function importFromUrlList() {
|
||||
setTimeout(importFromUrlList, 500)
|
||||
})
|
||||
}).catch(err => {
|
||||
|
||||
setTimeout(importFromUrlList, 500)
|
||||
}).finally(() => {
|
||||
loading.value = false
|
||||
})
|
||||
|
||||
@@ -1,11 +1,14 @@
|
||||
<template>
|
||||
<v-container :class="{'ps-0 pe-0 pt-0': mobile}">
|
||||
<recipe-view v-model="recipe"></recipe-view>
|
||||
<v-defaults-provider :defaults="(useUserPreferenceStore().isPrintMode ? {VCard: {variant: 'flat'}} : {})">
|
||||
|
||||
<div class="mt-2" v-if="isShared && Object.keys(recipe).length > 0">
|
||||
<import-tandoor-dialog></import-tandoor-dialog>
|
||||
</div>
|
||||
|
||||
<recipe-view v-model="recipe" :servings="servings"></recipe-view>
|
||||
|
||||
<div class="mt-2" v-if="isShared && Object.keys(recipe).length > 0">
|
||||
<import-tandoor-dialog></import-tandoor-dialog>
|
||||
</div>
|
||||
</v-defaults-provider>
|
||||
|
||||
</v-container>
|
||||
|
||||
@@ -33,6 +36,13 @@ const isShared = computed(() => {
|
||||
return params.share && typeof params.share == "string"
|
||||
})
|
||||
|
||||
const servings = computed(() => {
|
||||
const value = params.servings
|
||||
if (!value) return undefined
|
||||
const parsed = parseInt(value as string, 10)
|
||||
return parsed > 0 ? parsed : undefined
|
||||
})
|
||||
|
||||
const recipe = ref({} as Recipe)
|
||||
|
||||
watch(() => props.id, () => {
|
||||
@@ -56,6 +66,12 @@ function refreshData(recipeId: string) {
|
||||
recipe.value = r
|
||||
title.value = recipe.value.name
|
||||
|
||||
setTimeout(() => {
|
||||
if (useUserPreferenceStore().isPrintMode) {
|
||||
window.print()
|
||||
}
|
||||
}, 500)
|
||||
|
||||
if (useUserPreferenceStore().isAuthenticated) {
|
||||
api.apiViewLogCreate({viewLog: {recipe: Number(recipeId)} as ViewLog})
|
||||
}
|
||||
|
||||
@@ -184,7 +184,7 @@ import RecipeCard from "@/components/display/RecipeCard.vue";
|
||||
import {useDisplay} from "vuetify";
|
||||
import {useUserPreferenceStore} from "@/stores/UserPreferenceStore";
|
||||
import {useRouteQuery} from "@vueuse/router";
|
||||
import {routeQueryDateTransformer, stringToBool, toNumberArray} from "@/utils/utils";
|
||||
import {numberOrUndefinedTransformer, routeQueryDateTransformer, stringToBool, toNumberArray} from "@/utils/utils";
|
||||
import RandomIcon from "@/components/display/RandomIcon.vue";
|
||||
import {VSelect, VTextField, VNumberInput} from "vuetify/components";
|
||||
import RatingField from "@/components/inputs/RatingField.vue";
|
||||
@@ -759,27 +759,30 @@ const filters = ref({
|
||||
label: `${t('Rating')} (${t('exact')})`,
|
||||
hint: '',
|
||||
enabled: false,
|
||||
clearable: true,
|
||||
default: undefined,
|
||||
is: RatingField,
|
||||
modelValue: useRouteQuery('rating', undefined, {transform: Number}),
|
||||
modelValue: useRouteQuery('rating', undefined, {transform: numberOrUndefinedTransformer}),
|
||||
},
|
||||
ratingGte: {
|
||||
id: 'ratingGte',
|
||||
label: `${t('Rating')} (>=)`,
|
||||
hint: '',
|
||||
enabled: false,
|
||||
clearable: true,
|
||||
default: undefined,
|
||||
is: RatingField,
|
||||
modelValue: useRouteQuery('ratingGte', undefined, {transform: Number}),
|
||||
modelValue: useRouteQuery('ratingGte', undefined, {transform: numberOrUndefinedTransformer}),
|
||||
},
|
||||
ratingLte: {
|
||||
id: 'ratingLte',
|
||||
label: `${t('Rating')} (<=)`,
|
||||
hint: '',
|
||||
enabled: false,
|
||||
clearable: true,
|
||||
default: undefined,
|
||||
is: RatingField,
|
||||
modelValue: useRouteQuery('ratingLte', undefined, {transform: Number}),
|
||||
modelValue: useRouteQuery('ratingLte', undefined, {transform: numberOrUndefinedTransformer}),
|
||||
},
|
||||
timescooked: {
|
||||
id: 'timescooked',
|
||||
@@ -787,26 +790,29 @@ const filters = ref({
|
||||
hint: 'Recipes that were cooked at least X times',
|
||||
enabled: false,
|
||||
default: undefined,
|
||||
clearable: true,
|
||||
is: VNumberInput,
|
||||
modelValue: useRouteQuery('timescooked', undefined, {transform: Number}),
|
||||
modelValue: useRouteQuery('timescooked', undefined, {transform: numberOrUndefinedTransformer}),
|
||||
},
|
||||
timescookedGte: {
|
||||
id: 'timescookedGte',
|
||||
label: `${t('times_cooked')} (>=)`,
|
||||
hint: '',
|
||||
enabled: false,
|
||||
clearable: true,
|
||||
default: undefined,
|
||||
is: VNumberInput,
|
||||
modelValue: useRouteQuery('timescookedGte', undefined, {transform: Number}),
|
||||
modelValue: useRouteQuery('timescookedGte', undefined, {transform: numberOrUndefinedTransformer}),
|
||||
},
|
||||
timescookedLte: {
|
||||
id: 'timescookedLte',
|
||||
label: `${t('times_cooked')} (<=)`,
|
||||
hint: '',
|
||||
enabled: false,
|
||||
clearable: true,
|
||||
default: undefined,
|
||||
is: VNumberInput,
|
||||
modelValue: useRouteQuery('timescookedLte', undefined, {transform: Number}),
|
||||
modelValue: useRouteQuery('timescookedLte', undefined, {transform: numberOrUndefinedTransformer}),
|
||||
},
|
||||
makenow: {
|
||||
id: 'makenow',
|
||||
|
||||
@@ -7,6 +7,7 @@ import {computed, ComputedRef, ref} from "vue";
|
||||
import {DeviceSettings} from "@/types/settings";
|
||||
import {useTheme} from "vuetify";
|
||||
import {useRouter} from "vue-router";
|
||||
import {useRouteQuery} from "@vueuse/router";
|
||||
|
||||
const DEVICE_SETTINGS_KEY = 'TANDOOR_DEVICE_SETTINGS'
|
||||
const USER_PREFERENCE_KEY = 'TANDOOR_USER_PREFERENCE'
|
||||
@@ -55,6 +56,11 @@ export const useUserPreferenceStore = defineStore('user_preference_store', () =>
|
||||
*/
|
||||
const defaultUnitObj = ref<Unit | null>(null)
|
||||
|
||||
/**
|
||||
* detect if print mode is activated by checking for "print" query parameter
|
||||
*/
|
||||
const isPrintMode = useRouteQuery('print', false, {transform: Boolean})
|
||||
|
||||
const theme = useTheme()
|
||||
const router = useRouter()
|
||||
|
||||
@@ -250,10 +256,10 @@ export const useUserPreferenceStore = defineStore('user_preference_store', () =>
|
||||
* applies user settings regarding themes/styling
|
||||
*/
|
||||
function updateTheme() {
|
||||
if (userSettings.value.theme == 'TANDOOR') {
|
||||
theme.change('light')
|
||||
} else if (userSettings.value.theme == 'TANDOOR_DARK') {
|
||||
if (userSettings.value.theme == 'TANDOOR_DARK' && !isPrintMode.value) {
|
||||
theme.change('dark')
|
||||
} else {
|
||||
theme.change('light')
|
||||
}
|
||||
}
|
||||
|
||||
@@ -281,6 +287,7 @@ export const useUserPreferenceStore = defineStore('user_preference_store', () =>
|
||||
spaces,
|
||||
activeUserSpace,
|
||||
isAuthenticated,
|
||||
isPrintMode,
|
||||
initCompleted,
|
||||
defaultUnitObj,
|
||||
loadUserSettings,
|
||||
|
||||
@@ -7,7 +7,7 @@ import {
|
||||
MealPlan,
|
||||
MealType,
|
||||
Property, PropertyType,
|
||||
Recipe, RecipeBook, RecipeBookEntry, RecipeImport, SearchFields, ShoppingListEntry, Space,
|
||||
Recipe, RecipeBook, RecipeBookEntry, RecipeImport, SearchFields, ShoppingList, ShoppingListEntry, Space,
|
||||
Step,
|
||||
Supermarket,
|
||||
SupermarketCategory, Sync, SyncLog,
|
||||
@@ -144,6 +144,7 @@ export type EditorSupportedModels =
|
||||
| 'Automation'
|
||||
| 'Keyword'
|
||||
| 'UserFile'
|
||||
| 'ShoppingList'
|
||||
| 'ShoppingListEntry'
|
||||
| 'User'
|
||||
| 'RecipeBook'
|
||||
@@ -182,6 +183,7 @@ export type EditorSupportedTypes =
|
||||
| Automation
|
||||
| Keyword
|
||||
| UserFile
|
||||
| ShoppingList
|
||||
| ShoppingListEntry
|
||||
| User
|
||||
| RecipeBook
|
||||
@@ -484,6 +486,28 @@ export const TSupermarketCategory = {
|
||||
} as Model
|
||||
registerModel(TSupermarketCategory)
|
||||
|
||||
|
||||
export const TShoppingList = {
|
||||
name: 'ShoppingList',
|
||||
localizationKey: 'ShoppingList',
|
||||
localizationKeyDescription: 'ShoppingListHelp',
|
||||
icon: 'fa-solid fa-list-check',
|
||||
|
||||
editorComponent: defineAsyncComponent(() => import(`@/components/model_editors/ShoppingListEditor.vue`)),
|
||||
|
||||
disableListView: true,
|
||||
isPaginated: true,
|
||||
toStringKeys: ['name'],
|
||||
|
||||
tableHeaders: [
|
||||
{title: 'Name', key: 'name'},
|
||||
{title: 'Color', key: 'color'},
|
||||
{title: 'Description', key: 'description'},
|
||||
{title: 'Actions', key: 'action', align: 'end'},
|
||||
]
|
||||
} as Model
|
||||
registerModel(TShoppingList)
|
||||
|
||||
export const TShoppingListEntry = {
|
||||
name: 'ShoppingListEntry',
|
||||
localizationKey: 'ShoppingListEntry',
|
||||
|
||||
@@ -79,4 +79,20 @@ export function stringToBool(param: string): boolean | undefined {
|
||||
export const routeQueryDateTransformer = {
|
||||
get: (value: string | null | Date) => ((value == null) ? null : (new Date(value))),
|
||||
set: (value: string | null | Date) => ((value == null) ? null : (DateTime.fromJSDate(new Date(value)).toISODate()))
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* routeQueryParam transformer for boolean fields converting string bools to real bools
|
||||
*/
|
||||
export const boolOrUndefinedTransformer = {
|
||||
get: (value: string | null | undefined) => ((value == null) ? undefined : value == 'true'),
|
||||
set: (value: boolean | null | undefined) => ((value == null) ? undefined : value.toString())
|
||||
}
|
||||
|
||||
/**
|
||||
* routeQueryParam transformer for number fields converting string numbers to real numbers and allowing undefined for resettable parameters
|
||||
*/
|
||||
export const numberOrUndefinedTransformer = {
|
||||
get: (value: string | null | undefined) => ((value == null) ? undefined : Number(value)),
|
||||
set: (value: string | null | undefined) => ((value == null) ? undefined : value.toString())
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user