mirror of
https://github.com/TandoorRecipes/recipes.git
synced 2025-12-25 03:13:13 -05:00
Compare commits
12 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
1cc0806729 | ||
|
|
09487a0e94 | ||
|
|
89e58edcad | ||
|
|
887d7fe9f0 | ||
|
|
14696e3ce8 | ||
|
|
dd56bb4b35 | ||
|
|
8ec0ba9541 | ||
|
|
c105c9190e | ||
|
|
9c1700adb9 | ||
|
|
b43a87a7e3 | ||
|
|
01e78baecf | ||
|
|
0ee241524d |
@@ -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,13 +96,19 @@ 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=servings,
|
||||
servings_text=r['recipe_yield'].strip() if r['recipe_yield'] else "",
|
||||
internal=True,
|
||||
created_at=r['created_at'],
|
||||
@@ -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)
|
||||
@@ -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])) / (
|
||||
|
||||
@@ -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 = (
|
||||
@@ -3056,11 +3057,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)
|
||||
|
||||
@@ -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)
|
||||
@@ -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>
|
||||
|
||||
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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">
|
||||
@@ -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
|
||||
@@ -118,7 +118,7 @@
|
||||
</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"
|
||||
@@ -144,7 +144,7 @@
|
||||
<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>
|
||||
|
||||
@@ -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})
|
||||
|
||||
|
||||
@@ -47,8 +47,10 @@ 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({
|
||||
@@ -70,7 +72,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,
|
||||
|
||||
@@ -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": "",
|
||||
|
||||
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": "",
|
||||
|
||||
@@ -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",
|
||||
@@ -539,6 +540,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",
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -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": "",
|
||||
|
||||
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": "",
|
||||
|
||||
@@ -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": "",
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -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": "",
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -374,6 +374,7 @@
|
||||
"Ratings": "Betyg",
|
||||
"Recently_Viewed": "Nyligen visade",
|
||||
"Recipe": "Recept",
|
||||
"RecipeStructure": "",
|
||||
"Recipe_Book": "Receptbok",
|
||||
"Recipe_Image": "Receptbild",
|
||||
"Recipes": "Recept",
|
||||
@@ -431,6 +432,7 @@
|
||||
"SpacePrivateObjectsHelp": "",
|
||||
"Space_Cosmetic_Settings": "Vissa kosmetiska inställningar kan ändras av hushålls-administratörer och skriver över klientinställningar för det hushållet.",
|
||||
"Split_All_Steps": "Dela upp alla rader i separata steg.",
|
||||
"Start": "",
|
||||
"StartDate": "Startdatum",
|
||||
"Starting_Day": "Startdag i veckan",
|
||||
"StartsWith": "",
|
||||
|
||||
@@ -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": "",
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -606,7 +606,7 @@ function importFromUrlList() {
|
||||
setTimeout(importFromUrlList, 500)
|
||||
})
|
||||
}).catch(err => {
|
||||
|
||||
setTimeout(importFromUrlList, 500)
|
||||
}).finally(() => {
|
||||
loading.value = false
|
||||
})
|
||||
|
||||
@@ -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',
|
||||
|
||||
@@ -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