Merge branch 'develop'

This commit is contained in:
vabene1111
2025-11-24 20:40:07 +01:00
12 changed files with 778 additions and 733 deletions

View File

@@ -109,7 +109,7 @@ class Mealie1(Integration):
name=r['name'],
source_url=r['org_url'],
servings=servings,
servings_text=r['recipe_yield'].strip() if r['recipe_yield'] else "",
servings_text=r['recipe_yield'].strip()[:32] if r['recipe_yield'] else "",
internal=True,
created_at=r['created_at'],
space=self.request.space,
@@ -159,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))

View File

@@ -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)

View File

@@ -8,7 +8,7 @@ msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2024-08-01 15:04+0200\n"
"PO-Revision-Date: 2025-11-18 07:01+0000\n"
"PO-Revision-Date: 2025-11-22 20:03+0000\n"
"Last-Translator: SerhiiOS <serhios@users.noreply.translate.tandoor.dev>\n"
"Language-Team: Ukrainian <http://translate.tandoor.dev/projects/tandoor/"
"recipes-backend/uk/>\n"
@@ -1085,7 +1085,7 @@ msgstr "Ви використовуєте безкоштовну версію Ta
#: .\cookbook\templates\base.html:407
msgid "Upgrade Now"
msgstr "Оновити зараз"
msgstr "Оновити Зараз"
#: .\cookbook\templates\batch\edit.html:6
msgid "Batch edit Category"
@@ -1447,7 +1447,7 @@ msgstr "Таблиця"
#: .\cookbook\templates\markdown_info.html:155
#: .\cookbook\templates\markdown_info.html:172
msgid "Header"
msgstr "Заголовок"
msgstr "Шапка"
#: .\cookbook\templates\markdown_info.html:157
#: .\cookbook\templates\markdown_info.html:178
@@ -2639,7 +2639,7 @@ msgstr "Конфігурація коннектора для бекенду"
#: .\cookbook\views\lists.py:91
msgid "Invite Links"
msgstr "Посилання для запрошення"
msgstr "Посилання для запрошеннь"
#: .\cookbook\views\lists.py:154
msgid "Supermarkets"

View File

@@ -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', {})

View File

@@ -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
})
}
}

View File

@@ -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>

View File

@@ -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);
}
}

View File

@@ -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>
@@ -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>
@@ -190,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>
@@ -220,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)
@@ -236,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

View File

@@ -26,7 +26,7 @@
<v-progress-circular v-if="duplicateLoading" indeterminate size="small"></v-progress-circular>
</template>
</v-list-item>
<v-list-item :to="{ name: 'RecipeViewPage', params: { id: recipe.id}, query: {print: 'true'} }" :active="false" target="_blank" prepend-icon="fa-solid fa-print">
<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>
@@ -55,16 +55,13 @@ 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
*/

View File

@@ -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>

File diff suppressed because it is too large Load Diff

View File

@@ -2,7 +2,8 @@
<v-container :class="{'ps-0 pe-0 pt-0': mobile}">
<v-defaults-provider :defaults="(useUserPreferenceStore().isPrintMode ? {VCard: {variant: 'flat'}} : {})">
<recipe-view v-model="recipe"></recipe-view>
<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>
@@ -35,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, () => {