some fixes and changes

This commit is contained in:
vabene1111
2025-05-13 16:24:23 +02:00
parent 36352ae6fb
commit 9a7a05e8a9
45 changed files with 398 additions and 135 deletions

View File

@@ -188,6 +188,7 @@ import NavigationDrawerContextMenu from "@/components/display/NavigationDrawerCo
import {useDjangoUrls} from "@/composables/useDjangoUrls";
import {onMounted, ref} from "vue";
import {isSpaceAboveLimit} from "@/utils/logic_utils";
import '@/assets/tandoor_light.css'
const {lgAndUp} = useDisplay()
const {getDjangoUrl} = useDjangoUrls()
@@ -200,5 +201,4 @@ onMounted(() => {
<style scoped>
</style>

View File

@@ -28,6 +28,7 @@ const routes = [
{path: 'space', component: () => import("@/components/settings/SpaceSettings.vue"), name: 'SpaceSettings'},
{path: 'space-members', component: () => import("@/components/settings/SpaceMemberSettings.vue"), name: 'SpaceMemberSettings'},
{path: 'user-space', component: () => import("@/components/settings/UserSpaceSettings.vue"), name: 'UserSpaceSettings'},
{path: 'open-data-import', component: () => import("@/components/settings/OpenDataImportSettings.vue"), name: 'OpenDataImportSettings'},
{path: 'api', component: () => import("@/components/settings/ApiSettings.vue"), name: 'ApiSettings'},
]
},

View File

@@ -1,3 +1,23 @@
a {
color: #b98766;
text-decoration: none;
background-color: transparent
}
a:hover {
color: #fff;
text-decoration: none
}
a:not([href]):not([tabindex]), a:not([href]):not([tabindex]):focus, a:not([href]):not([tabindex]):hover {
color: inherit;
text-decoration: none
}
a:not([href]):not([tabindex]):focus {
outline: 0
}
/* Meal-Plan */
.cv-header {
@@ -37,4 +57,4 @@
.multiselect-option.is-pointed {
background: #b98766!important;
}
}

View File

@@ -5,7 +5,7 @@ a {
}
a:hover {
color: #fff;
color: #000;
text-decoration: none
}
@@ -18,43 +18,3 @@ a:not([href]):not([tabindex]):focus {
outline: 0
}
/* Meal-Plan */
.cv-header {
background-color: #303030 !important;
}
.cv-weeknumber, .cv-header-day {
background-color: #303030 !important;
color: #fff !important;
}
.cv-day.past {
background-color: #333333 !important;
}
.cv-day.today {
background-color: var(--primary) !important;
}
.cv-day.outsideOfMonth {
background-color: #0d0d0d !important;
}
.cv-item {
background-color: #4E4E4E !important;
}
.d01 .cv-day-number {
background-color: #b98766!important;
}
/* vueform/multiselect */
.multiselect-dropdown {
background: #212121!important;
}
.multiselect-option.is-pointed {
background: #b98766!important;
}

View File

@@ -0,0 +1,82 @@
<template>
<v-dialog
v-model="dialog"
:max-width="(mobile) ? '100vw': '25vw'"
:fullscreen="mobile">
<v-card>
<v-closable-card-title :title="$t('Move')" v-model="dialog"
:sub-title="ingredientToString(step.ingredients[editingIngredientIndex])"></v-closable-card-title>
<v-card-text>
<v-btn block :disabled="editingIngredientIndex== 0" @click="moveIngredient(editingIngredientIndex, props.stepIndex, 0)">{{ $t('First') }}</v-btn>
<v-btn block :disabled="editingIngredientIndex == 0" class="mt-1" @click="moveIngredient(editingIngredientIndex, props.stepIndex, editingIngredientIndex - 1)">{{
$t('Up')
}}
</v-btn>
<v-btn block :disabled="editingIngredientIndex + 1 == step.ingredients.length" class="mt-1"
@click="moveIngredient(editingIngredientIndex, props.stepIndex, editingIngredientIndex + 1)"> {{ $t('Down') }}
</v-btn>
<v-btn block :disabled="editingIngredientIndex + 1 == step.ingredients.length" class="mt-1"
@click="moveIngredient(editingIngredientIndex, props.stepIndex, step.ingredients.length - 1)">{{ $t('Last') }}
</v-btn>
{{ $t('Step') }}
<v-btn block v-for="(s,i) in recipe.steps" :disabled="i == props.stepIndex" class="mt-1"
@click="moveIngredient(editingIngredientIndex, i, recipe.steps[i].ingredients.length)">{{ i + 1 }} <span v-if="'name' in s">{{ s.name }}</span>
</v-btn>
</v-card-text>
<v-card-actions>
<v-btn @click="dialog = false">{{$t('Close')}}</v-btn>
</v-card-actions>
</v-card>
</v-dialog>
</template>
<script setup lang="ts">
import {Recipe, SourceImportRecipe, SourceImportStep, Step} from "@/openapi";
import {ingredientToString} from "@/utils/model_utils.ts";
import VClosableCardTitle from "@/components/dialogs/VClosableCardTitle.vue";
import {ref, watch} from "vue";
import {useDisplay} from "vuetify/framework";
const dialog = defineModel<Boolean>({required: true, default: false})
const step = defineModel<Step|SourceImportStep>('step', {required: true})
const recipe = defineModel<Recipe|SourceImportRecipe>('recipe', {required: true})
const props = defineProps({
stepIndex: {type: Number, required: true},
ingredientIndex: {type: Number, required: true},
})
const {mobile} = useDisplay()
watch(() => props.ingredientIndex, () => {
console.log('updated ingredient inex')
editingIngredientIndex.value = props.ingredientIndex
})
const editingIngredientIndex = ref(0)
/**
* move the ingredient at the given index of this step to the step and index at that step given in the target
* @param sourceIngredientIndex index of the ingredient to move
* @param targetStepIndex index of the step to place ingredient into
* @param targetIngredientIndex place in the target steps ingredient list to insert into
*/
function moveIngredient(sourceIngredientIndex: number, targetStepIndex: number, targetIngredientIndex: number,) {
let ingredient = step.value.ingredients[sourceIngredientIndex]
step.value.ingredients.splice(sourceIngredientIndex, 1)
recipe.value.steps[targetStepIndex].ingredients.splice(targetIngredientIndex, 0, ingredient)
// close dialog if moved to a new step, update index if its in the same step
if (targetStepIndex != props.stepIndex) {
dialog.value = false
} else {
editingIngredientIndex.value = targetIngredientIndex
}
}
</script>
<style scoped>
</style>

View File

@@ -42,7 +42,13 @@
<template v-if="i.unit"> {{ ingredientToUnitString(i, ingredientFactor) }}</template>
</td>
<td>
<template v-if="i.food"> {{ ingredientToFoodString(i, ingredientFactor) }}</template>
<template v-if="i.food">
<router-link v-if="i.food.recipe" :to="{name: 'RecipeViewPage', params: {id: i.food.recipe.id}}">
{{ ingredientToFoodString(i, ingredientFactor) }}
</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>
</td>
<td style="width: 1%; text-wrap: nowrap">

View File

@@ -171,35 +171,7 @@
</v-card>
</v-dialog>
<v-dialog
v-model="dialogIngredientSorter"
:max-width="(mobile) ? '100vw': '25vw'"
:fullscreen="mobile">
<v-card>
<v-closable-card-title :title="$t('Move')" v-model="dialogIngredientSorter"
:sub-title="ingredientToString(step.ingredients[editingIngredientIndex])"></v-closable-card-title>
<v-card-text>
<v-btn block :disabled="editingIngredientIndex== 0" @click="moveIngredient(editingIngredientIndex, props.stepIndex, 0)">{{ $t('First') }}</v-btn>
<v-btn block :disabled="editingIngredientIndex == 0" class="mt-1" @click="moveIngredient(editingIngredientIndex, props.stepIndex, editingIngredientIndex - 1)">{{
$t('Up')
}}
</v-btn>
<v-btn block :disabled="editingIngredientIndex + 1 == step.ingredients.length" class="mt-1"
@click="moveIngredient(editingIngredientIndex, props.stepIndex, editingIngredientIndex + 1)"> {{ $t('Down') }}
</v-btn>
<v-btn block :disabled="editingIngredientIndex + 1 == step.ingredients.length" class="mt-1"
@click="moveIngredient(editingIngredientIndex, props.stepIndex, step.ingredients.length - 1)">{{ $t('Last') }}
</v-btn>
{{ $t('Step') }}
<v-btn block v-for="(s,i) in recipe.steps" :disabled="i == props.stepIndex" class="mt-1"
@click="moveIngredient(editingIngredientIndex, i, recipe.steps[i].ingredients.length)">{{ i + 1 }} {{ s.name }}
</v-btn>
</v-card-text>
<v-card-actions>
</v-card-actions>
</v-card>
</v-dialog>
<step-ingredient-sorter-dialog :step-index="props.stepIndex" :step="step" :recipe="recipe" v-model="dialogIngredientSorter" :ingredient-index="editingIngredientIndex"></step-ingredient-sorter-dialog>
<v-bottom-sheet v-model="dialogIngredientEditor">
<v-card v-if="editingIngredientIndex >= 0">
@@ -247,6 +219,7 @@ import IngredientString from "@/components/display/IngredientString.vue";
import {useUserPreferenceStore} from "@/stores/UserPreferenceStore";
import {ErrorMessageType, useMessageStore} from "@/stores/MessageStore";
import {ingredientToString} from "@/utils/model_utils";
import StepIngredientSorterDialog from "@/components/dialogs/StepIngredientSorterDialog.vue";
const emit = defineEmits(['delete'])
@@ -289,25 +262,6 @@ onMounted(() => {
}
})
/**
* move the ingredient at the given index of this step to the step and index at that step given in the target
* @param sourceIngredientIndex index of the ingredient to move
* @param targetStepIndex index of the step to place ingredient into
* @param targetIngredientIndex place in the target steps ingredient list to insert into
*/
function moveIngredient(sourceIngredientIndex: number, targetStepIndex: number, targetIngredientIndex: number,) {
let ingredient = step.value.ingredients[sourceIngredientIndex]
step.value.ingredients.splice(sourceIngredientIndex, 1)
recipe.value.steps[targetStepIndex].ingredients.splice(targetIngredientIndex, 0, ingredient)
// close dialog if moved to a new step, update index if its in the same step
if (targetStepIndex != props.stepIndex) {
dialogIngredientSorter.value = false
} else {
editingIngredientIndex.value = targetIngredientIndex
}
}
/**
* sort function called by draggable when ingredient table is sorted
*/

View File

@@ -65,7 +65,7 @@ import {VDataTableUpdateOptions} from "@/vuetify";
import {useModelEditorFunctions} from "@/composables/useModelEditorFunctions";
import ModelEditorBase from "@/components/model_editors/ModelEditorBase.vue";
import ModelSelect from "@/components/inputs/ModelSelect.vue";
import {ErrorMessageType, PreparedMessage, useMessageStore} from "@/stores/MessageStore";
import {ErrorMessageType, MessageType, PreparedMessage, useMessageStore} from "@/stores/MessageStore";
import {useUserPreferenceStore} from "@/stores/UserPreferenceStore";
import {useI18n} from "vue-i18n";
@@ -113,12 +113,25 @@ function addRecipeToBook() {
let api = new ApiApi()
if (Object.keys(selectedRecipe.value).length > 0) {
api.apiRecipeBookEntryCreate({recipeBookEntry: {book: editingObj.value.id!, recipe: selectedRecipe.value.id!}}).then(r => {
recipeBookEntries.value.push(r)
selectedRecipe.value = {} as Recipe
}).catch(err => {
useMessageStore().addError(ErrorMessageType.CREATE_ERROR, err)
let duplicateFound = false
recipeBookEntries.value.forEach(rBE => {
if (rBE.recipe == selectedRecipe.value.id){
duplicateFound = true
}
})
if (!duplicateFound){
api.apiRecipeBookEntryCreate({recipeBookEntry: {book: editingObj.value.id!, recipe: selectedRecipe.value.id!}}).then(r => {
recipeBookEntries.value.push(r)
selectedRecipe.value = {} as Recipe
}).catch(err => {
useMessageStore().addError(ErrorMessageType.CREATE_ERROR, err)
})
} else {
selectedRecipe.value = {} as Recipe
useMessageStore().addMessage(MessageType.WARNING, $t('WarningRecipeBookEntryDuplicate'), 5000)
}
}
}

View File

@@ -241,6 +241,7 @@
"OnHand": "",
"OnHand_help": "",
"Open": "",
"Open_Data_Import": "",
"Options": "",
"Order": "",
"Owner": "",
@@ -397,6 +398,7 @@
"WaitingTime": "",
"WarnPageLeave": "",
"Warning": "",
"WarningRecipeBookEntryDuplicate": "",
"Warning_Delete_Supermarket_Category": "",
"Website": "",
"Wednesday": "",

View File

@@ -234,6 +234,7 @@
"OnHand": "В момента под ръка",
"OnHand_help": "Храната е в инвентара и няма да бъде добавена автоматично към списък за пазаруване. Състоянието на ръка се споделя с пазаруващите потребители.",
"Open": "Отвори",
"Open_Data_Import": "",
"Options": "Настроики",
"Order": "",
"Owner": "",
@@ -388,6 +389,7 @@
"WaitingTime": "",
"WarnPageLeave": "",
"Warning": "Внимание",
"WarningRecipeBookEntryDuplicate": "",
"Warning_Delete_Supermarket_Category": "Изтриването на категория супермаркет ще изтрие и всички връзки с храни. Сигурен ли си?",
"Website": "уебсайт",
"Wednesday": "",

View File

@@ -491,6 +491,7 @@
"WaitingTime": "",
"WarnPageLeave": "",
"Warning": "",
"WarningRecipeBookEntryDuplicate": "",
"Warning_Delete_Supermarket_Category": "",
"Website": "",
"Wednesday": "",

View File

@@ -484,6 +484,7 @@
"WaitingTime": "",
"WarnPageLeave": "",
"Warning": "Varování",
"WarningRecipeBookEntryDuplicate": "",
"Warning_Delete_Supermarket_Category": "Vymazáním kategorie obchodu dojde k odstranění všech vazeb na potraviny. Jste si jistí?",
"Website": "Web",
"Wednesday": "",

View File

@@ -464,6 +464,7 @@
"WaitingTime": "",
"WarnPageLeave": "",
"Warning": "Advarsel",
"WarningRecipeBookEntryDuplicate": "",
"Warning_Delete_Supermarket_Category": "At slette en supermarkedskategori vil også slette alle relationer til mad. Er du sikker?",
"Website": "Hjemmeside",
"Wednesday": "",

View File

@@ -302,7 +302,7 @@
"OnHand": "Aktuell vorrätig",
"OnHand_help": "Lebensmittel ist \"Vorrätig\" und wird nicht automatisch zur Einkaufsliste hinzugefügt. Der Status \"Vorrätig\" wird mit den Benutzern der Einkaufsliste geteilt.",
"Open": "Öffnen",
"Open_Data_Import": "Datenimport öffnen",
"Open_Data_Import": "Datenimport",
"Open_Data_Slug": "Open Data Schlagwort",
"Options": "Optionen",
"Order": "Reihenfolge",
@@ -495,6 +495,7 @@
"WaitingTime": "Wartezeit",
"WarnPageLeave": "Deine Änderungen wurden noch nicht gespeichert und gehen verloren. Seite wirklich verlassen?",
"Warning": "Warnung",
"WarningRecipeBookEntryDuplicate": "Jedes Rezept kann nur einmal in einem Buch vorkommen.",
"Warning_Delete_Supermarket_Category": "Die Löschung einer Supermarktkategorie werden auch alle Beziehungen zu Lebensmitteln gelöscht. Bist du dir sicher?",
"Website": "Webseite",
"Wednesday": "Mittwoch",

View File

@@ -453,6 +453,7 @@
"WaitingTime": "",
"WarnPageLeave": "",
"Warning": "Προειδοποίηση",
"WarningRecipeBookEntryDuplicate": "",
"Warning_Delete_Supermarket_Category": "Η διαγραφή μιας κατηγορίας supermarket θα διαγράψει και όλες τις σχέσεις της με φαγητά. Είστε σίγουροι;",
"Website": "Ιστοσελίδα",
"Wednesday": "",

View File

@@ -493,6 +493,7 @@
"WaitingTime": "Waiting Time",
"WarnPageLeave": "There are unsaved changes that will get lost. Leave page anyway?",
"Warning": "Warning",
"WarningRecipeBookEntryDuplicate": "A recipe can only be added once to a book.",
"Warning_Delete_Supermarket_Category": "Deleting a supermarket category will also delete all relations to foods. Are you sure?",
"Website": "Website",
"Wednesday": "Wednesday",

View File

@@ -491,6 +491,7 @@
"WaitingTime": "",
"WarnPageLeave": "",
"Warning": "Advertencia",
"WarningRecipeBookEntryDuplicate": "",
"Warning_Delete_Supermarket_Category": "Borrar una categoría de supermercado borrará también todas las relaciones con alimentos. ¿Está seguro?",
"Website": "Sitio Web",
"Wednesday": "",

View File

@@ -181,6 +181,7 @@
"Nutrition": "Ravitsemus",
"Ok": "Avaa",
"Open": "Avaa",
"Open_Data_Import": "",
"Order": "",
"Owner": "",
"Parameter": "Parametri",
@@ -307,6 +308,7 @@
"Waiting": "Odottaa",
"WaitingTime": "",
"WarnPageLeave": "",
"WarningRecipeBookEntryDuplicate": "",
"Wednesday": "",
"Week": "Viikko",
"Week_Numbers": "Viikkonumerot",

View File

@@ -490,6 +490,7 @@
"WaitingTime": "",
"WarnPageLeave": "",
"Warning": "Avertissement",
"WarningRecipeBookEntryDuplicate": "",
"Warning_Delete_Supermarket_Category": "Supprimer une catégorie de supermarché supprimera également toutes les relations avec les aliments. Êtes-vous sûr ?",
"Website": "Site",
"Wednesday": "",

View File

@@ -492,6 +492,7 @@
"WaitingTime": "",
"WarnPageLeave": "",
"Warning": "אזהרה",
"WarningRecipeBookEntryDuplicate": "",
"Warning_Delete_Supermarket_Category": "מחיקת קטגורית סופרמרקט תמחוק גם את המאכלים הקשורים. האם אתה בטוח ?",
"Website": "אתר",
"Wednesday": "",

View File

@@ -455,6 +455,7 @@
"WaitingTime": "",
"WarnPageLeave": "",
"Warning": "Figyelmeztetés",
"WarningRecipeBookEntryDuplicate": "",
"Warning_Delete_Supermarket_Category": "Egy szupermarket-kategória törlése az alapanyagokkal való összes kapcsolatot is törli. Biztos vagy benne?",
"Website": "Weboldal",
"Wednesday": "",

View File

@@ -136,6 +136,7 @@
"Nutrition": "",
"Ok": "",
"Open": "",
"Open_Data_Import": "",
"Order": "",
"Owner": "",
"Parent": "Ծնող",
@@ -237,6 +238,7 @@
"Waiting": "",
"WaitingTime": "",
"WarnPageLeave": "",
"WarningRecipeBookEntryDuplicate": "",
"Wednesday": "",
"WorkingTime": "",
"YourSpaces": "",

View File

@@ -260,6 +260,7 @@
"OnHand": "",
"OnHand_help": "",
"Open": "Membuka",
"Open_Data_Import": "",
"Options": "",
"Order": "",
"Owner": "",
@@ -422,6 +423,7 @@
"WaitingTime": "",
"WarnPageLeave": "",
"Warning": "",
"WarningRecipeBookEntryDuplicate": "",
"Warning_Delete_Supermarket_Category": "",
"Website": "",
"Wednesday": "",

View File

@@ -490,6 +490,7 @@
"WaitingTime": "",
"WarnPageLeave": "",
"Warning": "",
"WarningRecipeBookEntryDuplicate": "",
"Warning_Delete_Supermarket_Category": "",
"Website": "",
"Wednesday": "",

View File

@@ -265,6 +265,7 @@
"OnHand": "Attualmente disponibili",
"OnHand_help": "Gli alimenti sono nell'inventario e non verranno automaticamente aggiunti alla lista della spesa. Lo stato di disponibilità è condiviso con gli utenti di spesa.",
"Open": "Apri",
"Open_Data_Import": "",
"Options": "Opzioni",
"Order": "",
"Original_Text": "Testo originale",
@@ -437,6 +438,7 @@
"WaitingTime": "",
"WarnPageLeave": "",
"Warning": "Attenzione",
"WarningRecipeBookEntryDuplicate": "",
"Warning_Delete_Supermarket_Category": "L'eliminazione di una categoria di supermercato comporta anche l'eliminazione di tutte le relazioni con gli alimenti. Sei sicuro?",
"Website": "Sito web",
"Wednesday": "",

View File

@@ -462,6 +462,7 @@
"WaitingTime": "",
"WarnPageLeave": "",
"Warning": "",
"WarningRecipeBookEntryDuplicate": "",
"Warning_Delete_Supermarket_Category": "",
"Website": "",
"Wednesday": "",

View File

@@ -451,6 +451,7 @@
"WaitingTime": "",
"WarnPageLeave": "",
"Warning": "Advarsel",
"WarningRecipeBookEntryDuplicate": "",
"Warning_Delete_Supermarket_Category": "",
"Website": "Nettside",
"Wednesday": "",

View File

@@ -455,6 +455,7 @@
"WaitingTime": "",
"WarnPageLeave": "",
"Warning": "Waarschuwing",
"WarningRecipeBookEntryDuplicate": "",
"Warning_Delete_Supermarket_Category": "Een supermarktcategorie verwijderen verwijdert ook alle relaties naar ingrediënten. Weet je het zeker?",
"Website": "Website",
"Wednesday": "",

View File

@@ -493,6 +493,7 @@
"WaitingTime": "",
"WarnPageLeave": "",
"Warning": "Ostrzeżenie",
"WarningRecipeBookEntryDuplicate": "",
"Warning_Delete_Supermarket_Category": "Usunięcie kategorii supermarketu spowoduje również usunięcie wszystkich relacji z żywnością. Jesteś pewny?",
"Website": "Strona internetowa",
"Wednesday": "",

View File

@@ -229,6 +229,7 @@
"OnHand": "Atualmente disponível",
"OnHand_help": "",
"Open": "Abrir",
"Open_Data_Import": "",
"Order": "",
"Original_Text": "Texto original",
"Owner": "",
@@ -384,6 +385,7 @@
"WaitingTime": "",
"WarnPageLeave": "",
"Warning": "Aviso",
"WarningRecipeBookEntryDuplicate": "",
"Wednesday": "",
"Week": "Semana",
"Week_Numbers": "Números das semanas",

View File

@@ -469,6 +469,7 @@
"WaitingTime": "",
"WarnPageLeave": "",
"Warning": "Alerta",
"WarningRecipeBookEntryDuplicate": "",
"Website": "Website",
"Wednesday": "",
"Week": "Semana",

View File

@@ -269,6 +269,7 @@
"OnHand": "În prezent, la îndemână",
"OnHand_help": "Alimentele sunt în inventar și nu vor fi adăugate automat la o listă de cumpărături. Starea la îndemână este partajată cu utilizatorii de cumpărături.",
"Open": "Deschide",
"Open_Data_Import": "",
"Options": "Opțiuni",
"Order": "",
"Original_Text": "Text original",
@@ -441,6 +442,7 @@
"WaitingTime": "",
"WarnPageLeave": "",
"Warning": "Atenționare",
"WarningRecipeBookEntryDuplicate": "",
"Warning_Delete_Supermarket_Category": "Ștergerea unei categorii de supermarketuri va șterge, de asemenea, toate relațiile cu alimentele. Sunteți sigur?",
"Website": "Site web",
"Wednesday": "",

View File

@@ -219,6 +219,7 @@
"Ok": "Открыть",
"OnHand": "В Наличии",
"Open": "Открыть",
"Open_Data_Import": "",
"Options": "Опции",
"Order": "",
"Owner": "",
@@ -359,6 +360,7 @@
"WaitingTime": "",
"WarnPageLeave": "",
"Warning": "Предупреждение",
"WarningRecipeBookEntryDuplicate": "",
"Warning_Delete_Supermarket_Category": "Удаление категории супермаркета также приведет к удалению всех связей с продуктами. Вы уверены?",
"Wednesday": "",
"Week": "Неделя",

View File

@@ -354,6 +354,7 @@
"WaitingTime": "",
"WarnPageLeave": "",
"Warning": "Opozorilo",
"WarningRecipeBookEntryDuplicate": "",
"Wednesday": "",
"Week": "Teden",
"Week_Numbers": "Števila tednov",

View File

@@ -493,6 +493,7 @@
"WaitingTime": "",
"WarnPageLeave": "",
"Warning": "Varning",
"WarningRecipeBookEntryDuplicate": "",
"Warning_Delete_Supermarket_Category": "Om du tar bort en mataffärskategori raderas också alla relationer till livsmedel. Är du säker?",
"Website": "Hemsida",
"Wednesday": "",

View File

@@ -492,6 +492,7 @@
"WaitingTime": "",
"WarnPageLeave": "",
"Warning": "Uyarı",
"WarningRecipeBookEntryDuplicate": "",
"Warning_Delete_Supermarket_Category": "Bir market kategorisinin silinmesi, gıdalarla olan tüm ilişkileri de silecektir. Emin misiniz?",
"Website": "Website",
"Wednesday": "",

View File

@@ -246,6 +246,7 @@
"OnHand": "Зараз На Руках",
"OnHand_help": "",
"Open": "Відкрити",
"Open_Data_Import": "",
"Options": "",
"Order": "",
"Original_Text": "Оригінальний текст",
@@ -406,6 +407,7 @@
"WaitingTime": "",
"WarnPageLeave": "",
"Warning": "Увага",
"WarningRecipeBookEntryDuplicate": "",
"Warning_Delete_Supermarket_Category": "",
"Website": "",
"Wednesday": "",

View File

@@ -483,6 +483,7 @@
"WaitingTime": "",
"WarnPageLeave": "",
"Warning": "警告",
"WarningRecipeBookEntryDuplicate": "",
"Warning_Delete_Supermarket_Category": "删除超市类别也会删除与食品的所有关系。 你确定吗?",
"Website": "网站",
"Wednesday": "",

View File

@@ -112,6 +112,7 @@
"Nutrition": "",
"Ok": "",
"Open": "",
"Open_Data_Import": "",
"Order": "",
"Owner": "",
"PerPage": "",
@@ -206,6 +207,7 @@
"Waiting": "",
"WaitingTime": "",
"WarnPageLeave": "",
"WarningRecipeBookEntryDuplicate": "",
"Wednesday": "",
"WorkingTime": "",
"YourSpaces": "",

View File

@@ -33,8 +33,12 @@
<v-row>
<v-col class="text-center">
<v-pagination :model-value="page + 1" @update:model-value="value => page = value - 1" :length="totalItems" @next="page = page + (mdAndUp ? 2 : 1)"
@prev="page = page - (mdAndUp ? 2 : 1)"></v-pagination>
<v-pagination :model-value="page + 1"
@update:model-value="value => page = Math.max(value - 1, 0)"
:length="totalItems"
@next="page = Math.min(page + (mdAndUp ? 1 : 0), totalItems - 1)"
@prev="page = Math.max(page - (mdAndUp ? 1 : 0), 0)"
></v-pagination>
</v-col>
</v-row>

View File

@@ -261,9 +261,9 @@
</v-btn-group>
</v-col>
</v-row>
<v-row v-for="(s,i) in importResponse.recipe.steps" :key="i">
<v-row v-for="(s, stepIndex) in importResponse.recipe.steps" :key="i">
<v-col cols="12">
<v-chip color="primary">#{{ i + 1 }}</v-chip>
<v-chip color="primary">#{{ stepIndex + 1 }}</v-chip>
<v-btn variant="plain" size="small" icon class="float-right">
<v-icon icon="$menu"></v-icon>
<v-menu activator="parent">
@@ -277,7 +277,7 @@
<v-col cols="12" md="6">
<v-list>
<vue-draggable v-model="s.ingredients" group="ingredients" handle=".drag-handle" empty-insert-threshold="25">
<v-list-item v-for="i in s.ingredients" border>
<v-list-item v-for="(i, ingredientIndex) in s.ingredients" border>
<v-icon size="small" class="drag-handle cursor-grab mr-2" icon="$dragHandle"></v-icon>
<v-chip density="compact" label class="mr-1">{{ i.amount }}</v-chip>
<v-chip density="compact" label class="mr-1" v-if="i.unit">{{ i.unit.name }}</v-chip>
@@ -289,6 +289,7 @@
<v-list>
<v-list-item prepend-icon="$edit" @click="editingIngredient = i; dialog=true">{{ $t('Edit') }}</v-list-item>
<v-list-item prepend-icon="$delete" @click="deleteIngredient(s,i)">{{ $t('Delete') }}</v-list-item>
<v-list-item prepend-icon="fa-solid fa-sort" @click="editingIngredientIndex = ingredientIndex; editingStepIndex = stepIndex; editingStep = s; dialogIngredientSorter = true">{{ $t('Move') }}</v-list-item>
</v-list>
</v-menu>
</v-btn>
@@ -349,7 +350,10 @@
<v-number-input :label="$t('Servings')" v-model="importResponse.recipe.servings" :precision="2"></v-number-input>
<v-text-field :label="$t('ServingsText')" v-model="importResponse.recipe.servingsText"></v-text-field>
<v-textarea :label="$t('Description')" v-model="importResponse.recipe.description" clearable></v-textarea>
<v-checkbox v-model="editAfterImport" :label="$t('Edit_Recipe')" hide-details></v-checkbox>
</v-col>
</v-row>
</v-card>
@@ -455,12 +459,15 @@
</v-col>
</v-row>
</v-container>
<step-ingredient-sorter-dialog :step-index="editingStepIndex" :step="editingStep" :recipe="importResponse.recipe" v-model="dialogIngredientSorter" :ingredient-index="editingIngredientIndex"></step-ingredient-sorter-dialog>
</template>
<script lang="ts" setup>
import {computed, onMounted, ref} from "vue";
import {AccessToken, ApiApi, ImportLog, type RecipeFromSource, RecipeFromSourceResponse, type SourceImportIngredient, SourceImportKeyword, SourceImportStep} from "@/openapi";
import {AccessToken, ApiApi, ImportLog, type RecipeFromSource, RecipeFromSourceResponse, type SourceImportIngredient, SourceImportKeyword, SourceImportStep, Step} from "@/openapi";
import {ErrorMessageType, MessageType, useMessageStore} from "@/stores/MessageStore";
import {useRouter} from "vue-router";
import {useUserPreferenceStore} from "@/stores/UserPreferenceStore";
@@ -476,7 +483,7 @@ import ImportLogViewer from "@/components/display/ImportLogViewer.vue";
import {DateTime} from "luxon";
import {useDjangoUrls} from "@/composables/useDjangoUrls";
import bookmarkletJs from '@/assets/bookmarklet_v3?url'
import BtnCopy from "@/components/buttons/BtnCopy.vue";
import StepIngredientSorterDialog from "@/components/dialogs/StepIngredientSorterDialog.vue";
const params = useUrlSearchParams('history', {})
const {mobile} = useDisplay()
@@ -500,6 +507,7 @@ const importType = ref<'url' | 'ai' | 'app' | 'bookmarklet' | 'source'>("url")
const importApp = ref('DEFAULT')
const stepper = ref("type")
const dialog = ref(false)
const loading = ref(false)
const importUrl = ref("")
@@ -510,6 +518,7 @@ const appImportDuplicates = ref(false)
const appImportLog = ref<null | ImportLog>(null)
const image = ref<null | File>(null)
const aiMode = ref<'file' | 'text'>('file')
const editAfterImport = ref(false)
const bookmarkletToken = ref("")
@@ -517,6 +526,12 @@ const importResponse = ref({} as RecipeFromSourceResponse)
const keywordSelect = ref<null | SourceImportKeyword>(null)
const editingIngredient = ref({} as SourceImportIngredient)
// stuff for ingredient mover, find some better solution at some point (finally merge importer/editor?)
const editingIngredientIndex = ref(0)
const dialogIngredientSorter = ref(false)
const editingStep = ref<Step| SourceImportStep>({} as Step)
const editingStepIndex = ref(0)
onMounted(() => {
loadOrCreateBookmarkletToken()
@@ -641,7 +656,11 @@ function createRecipeFromImport() {
api.apiRecipeCreate({recipe: importResponse.value.recipe}).then(recipe => {
updateRecipeImage(recipe.id!, null, importResponse.value.recipe?.imageUrl).then(r => {
router.push({name: 'RecipeViewPage', params: {id: recipe.id}})
if(editAfterImport.value){
router.push({name: 'ModelEditPage', params: {id: recipe.id, model: 'recipe'}})
} else {
router.push({name: 'RecipeViewPage', params: {id: recipe.id}})
}
})
}).catch(err => {
useMessageStore().addError(ErrorMessageType.CREATE_ERROR, err)

View File

@@ -16,6 +16,7 @@
<v-list-item :to="{name: 'UserSpaceSettings'}" prepend-icon="$spaces">{{ $t('YourSpaces') }}</v-list-item>
<v-list-item :to="{name: 'SpaceSettings'}" prepend-icon="$settings">{{ $t('SpaceSettings') }}</v-list-item>
<v-list-item :to="{name: 'SpaceMemberSettings'}" prepend-icon="fa-solid fa-users">{{ $t('SpaceMembers') }}</v-list-item>
<v-list-item :to="{name: 'OpenDataImportSettings'}" prepend-icon="fa-solid fa-cloud-arrow-down">{{ $t('Open_Data_Import') }}</v-list-item>
<v-divider></v-divider>
<v-list-subheader>Admin</v-list-subheader>
<v-list-item :to="{name: 'ApiSettings'}" prepend-icon="fa-solid fa-code">{{ $t('API') }}</v-list-item>