Merge branch 'develop' into feature/keywords-rework

# Conflicts:
#	cookbook/static/vue/js/chunk-vendors.js
#	cookbook/static/vue/js/import_response_view.js
#	cookbook/static/vue/js/offline_view.js
#	cookbook/static/vue/js/recipe_search_view.js
#	cookbook/static/vue/js/recipe_view.js
#	cookbook/static/vue/js/supermarket_view.js
#	cookbook/static/vue/js/user_file_view.js
#	cookbook/templates/sw.js
#	cookbook/views/views.py
#	vue/src/components/RecipeCard.vue
#	vue/src/locales/en.json
This commit is contained in:
vabene1111
2021-06-30 14:57:33 +02:00
115 changed files with 14137 additions and 5122 deletions

View File

@ -1,54 +1,42 @@
<template>
<div id="app">
<div class="row">
<div class="col col-md-12">
<h2>{{ $t('Import') }}</h2>
</div>
</div>
<br/>
<br/>
<template v-if="import_info !== undefined">
<template v-if="import_info.running" style="text-align: center;">
<div class="row">
<div class="col col-md-12">
</div>
</div>
<loading-spinner></loading-spinner>
<br/>
<br/>
<template v-if="import_info.running">
<h5 style="text-align: center">{{ $t('Importing') }}...</h5>
<b-progress :max="import_info.total_recipes">
<b-progress-bar :value="import_info.imported_recipes" :label="`${import_info.imported_recipes}/${import_info.total_recipes}`"></b-progress-bar>
</b-progress>
<loading-spinner :size="25"></loading-spinner>
</template>
<template v-else>
<div class="row">
<div class="col col-md-12">
<span>{{ $t('Import_finished') }}! </span>
<a :href="`${resolveDjangoUrl('view_search') }?keyword=${import_info.keyword.id}`"
v-if="import_info.keyword !== null">{{ $t('View_Recipes') }}</a>
</div>
<div class="row">
<div class="col col-md-12" v-if="!import_info.running">
<span>{{ $t('Import_finished') }}! </span>
<a :href="`${resolveDjangoUrl('view_search') }?keyword=${import_info.keyword.id}`"
v-if="import_info.keyword !== null">{{ $t('View_Recipes') }}</a>
</div>
</div>
<br/>
<br/>
<div class="row">
<div class="col col-md-12">
<label for="id_textarea">{{ $t('Information') }}</label>
<textarea id="id_textarea" class="form-control" style="height: 50vh" v-html="import_info.msg"
disabled></textarea>
<div class="row">
<div class="col col-md-12">
<label for="id_textarea">{{ $t('Information') }}</label>
<textarea id="id_textarea" ref="output_text" class="form-control" style="height: 50vh"
v-html="import_info.msg"
disabled></textarea>
</div>
</div>
</template>
</div>
<br/>
<br/>
</template>
</div>
@ -90,6 +78,8 @@ export default {
setInterval(() => {
if ((this.import_id !== null) && window.navigator.onLine && this.import_info.running) {
this.refreshData()
let el = this.$refs.output_text
el.scrollTop = el.scrollHeight;
}
}, 5000)
@ -100,6 +90,7 @@ export default {
apiClient.retrieveImportLog(this.import_id).then(result => {
this.import_info = result.data
})
}
}

View File

@ -15,7 +15,9 @@
<b-input-group class="mt-3">
<b-input class="form-control" v-model="settings.search_input" v-bind:placeholder="$t('Search')"></b-input>
<b-input-group-append>
<b-button v-b-toggle.collapse_advanced_search variant="primary" class="shadow-none"><i
<b-button v-b-toggle.collapse_advanced_search
v-bind:class="{'btn-primary': !isAdvancedSettingsSet(), 'btn-danger': isAdvancedSettingsSet()}"
class="shadow-none btn"><i
class="fas fa-caret-down" v-if="!settings.advanced_search_visible"></i><i class="fas fa-caret-up"
v-if="settings.advanced_search_visible"></i>
</b-button>
@ -37,21 +39,16 @@
:href="resolveDjangoUrl('data_import_url')">{{ $t('Import') }}</a>
</div>
<div class="col-md-3" style="margin-top: 1vh">
<button class="btn btn-primary btn-block text-uppercase" @click="resetSearch">
{{ $t('Reset_Search') }}
<button class="btn btn-block text-uppercase" v-b-tooltip.hover :title="$t('show_only_internal')"
v-bind:class="{'btn-success':settings.search_internal, 'btn-primary':!settings.search_internal}"
@click="settings.search_internal = !settings.search_internal;refreshData()">
{{ $t('Internal') }}
</button>
</div>
<div class="col-md-2" style="position: relative; margin-top: 1vh">
<b-form-checkbox v-model="settings.search_internal" name="check-button"
@change="refreshData(false)"
class="shadow-none"
style="position:relative;top: 50%; transform: translateY(-50%);" switch>
{{ $t('show_only_internal') }}
</b-form-checkbox>
</div>
<div class="col-md-1" style="position: relative; margin-top: 1vh">
<button id="id_settings_button" class="btn btn-primary btn-block"><i class="fas fa-cog"></i>
<div class="col-md-3" style="position: relative; margin-top: 1vh">
<button id="id_settings_button" class="btn btn-primary btn-block text-uppercase"><i
class="fas fa-cog"></i>
</button>
</div>
@ -181,7 +178,16 @@
</div>
</div>
<div class="row" style="margin-top: 2vh">
<div class="row">
<div class="col col-md-12 text-right" style="margin-top: 2vh">
<span class="text-muted">
{{ $t('Page') }} {{ settings.pagination_page }}/{{ pagination_count }} <a href="#" @click="resetSearch"><i
class="fas fa-times-circle"></i> {{ $t('Reset') }}</a>
</span>
</div>
</div>
<div class="row">
<div class="col col-md-12">
<div style="display: grid; grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));grid-gap: 1rem;">
@ -200,10 +206,16 @@
</div>
</div>
<div class="row" style="margin-top: 2vh; text-align: center">
<div class="row" style="margin-top: 2vh">
<div class="col col-md-12">
<b-button @click="loadMore()" class="btn-block btn-success" v-if="pagination_more">{{ $t('Load_More') }}
</b-button>
<b-pagination pills
v-model="settings.pagination_page"
:total-rows="pagination_count"
per-page="25"
@change="pageChange"
align="center">
</b-pagination>
</div>
</div>
@ -239,6 +251,8 @@ import GenericMultiselect from "@/components/GenericMultiselect";
Vue.use(BootstrapVue)
let SETTINGS_COOKIE_NAME = 'search_settings'
export default {
name: 'RecipeSearchView',
mixins: [ResolveUrlMixin],
@ -249,6 +263,7 @@ export default {
meal_plans: [],
last_viewed_recipes: [],
settings_loaded: false,
settings: {
search_input: '',
search_internal: false,
@ -262,19 +277,33 @@ export default {
advanced_search_visible: false,
show_meal_plan: true,
recently_viewed: 5,
pagination_page: 1,
},
pagination_more: true,
pagination_page: 1,
pagination_count: 0,
}
},
mounted() {
this.$nextTick(function () {
if (this.$cookies.isKey('search_settings_v2')) {
this.settings = this.$cookies.get("search_settings_v2")
if (this.$cookies.isKey(SETTINGS_COOKIE_NAME)) {
let cookie_val = this.$cookies.get(SETTINGS_COOKIE_NAME)
for (let i of Object.keys(cookie_val)) {
this.$set(this.settings, i, cookie_val[i])
}
//TODO i have no idea why the above code does not suffice to update the
//TODO pagination UI element as $set should update all values reactively but it does not
setTimeout(function () {
this.$set(this.settings, 'pagination_page', 0)
}.bind(this), 50)
setTimeout(function () {
this.$set(this.settings, 'pagination_page', cookie_val['pagination_page'])
}.bind(this), 51)
}
let urlParams = new URLSearchParams(window.location.search);
let apiClient = new ApiApiFactory()
@ -284,7 +313,7 @@ export default {
let keyword = {id: x, name: 'loading'}
this.settings.search_keywords.push(keyword)
apiClient.retrieveKeyword(x).then(result => {
this.$set(this.settings.search_keywords,this.settings.search_keywords.indexOf(keyword), result.data)
this.$set(this.settings.search_keywords, this.settings.search_keywords.indexOf(keyword), result.data)
})
}
}
@ -299,7 +328,7 @@ export default {
watch: {
settings: {
handler() {
this.$cookies.set("search_settings_v2", this.settings, -1)
this.$cookies.set(SETTINGS_COOKIE_NAME, this.settings, -1)
},
deep: true
},
@ -333,16 +362,11 @@ export default {
this.settings.search_internal,
undefined,
this.pagination_page,
this.settings.pagination_page,
).then(result => {
this.pagination_more = (result.data.next !== null)
if (page_load) {
for (let x of result.data.results) {
this.recipes.push(x)
}
} else {
this.recipes = result.data.results
}
window.scrollTo(0, 0);
this.pagination_count = result.data.count
this.recipes = result.data.results
})
},
loadMealPlan: function () {
@ -384,11 +408,15 @@ export default {
this.settings.search_keywords = []
this.settings.search_foods = []
this.settings.search_books = []
this.settings.pagination_page = 1
this.refreshData(false)
},
loadMore: function (page) {
this.pagination_page++
this.refreshData(true)
pageChange: function (page) {
this.settings.pagination_page = page
this.refreshData()
},
isAdvancedSettingsSet() {
return ((this.settings.search_keywords.length + this.settings.search_foods.length + this.settings.search_books.length) > 0)
}
}
}

View File

@ -11,6 +11,13 @@
</div>
</div>
<div class="row text-center">
<div class="col col-md-12">
<recipe-rating :recipe="recipe"></recipe-rating> <br/>
<last-cooked :recipe="recipe"></last-cooked>
</div>
</div>
<div class="my-auto">
<div class="col-12" style="text-align: center">
<i>{{ recipe.description }}</i>
@ -47,7 +54,7 @@
</div>
</div>
<div class="col col-md-4 col-10">
<div class="col col-md-4 col-10 mt-2 mt-md-0 mt-lg-0 mt-xl-0">
<div class="row d-flex" style="padding-left: 16px">
<div class="my-auto" style="padding-right: 4px">
<i class="fas fa-pizza-slice fa-2x text-primary"></i>
@ -134,6 +141,14 @@
</div>
<add-recipe-to-book :recipe="recipe"></add-recipe-to-book>
<div class="row text-center d-print-none" style="margin-top: 3vh; margin-bottom: 3vh" v-if="share_uid !== 'None'">
<div class="col col-md-12">
<a :href="resolveDjangoUrl('view_report_share_abuse', share_uid)" >{{$t('Report Abuse')}}</a>
</div>
</div>
</div>
</template>
@ -158,6 +173,8 @@ import moment from 'moment'
import Keywords from "@/components/Keywords";
import LoadingSpinner from "@/components/LoadingSpinner";
import AddRecipeToBook from "@/components/AddRecipeToBook";
import RecipeRating from "@/components/RecipeRating";
import LastCooked from "@/components/LastCooked";
Vue.prototype.moment = moment
@ -170,6 +187,8 @@ export default {
ToastMixin,
],
components: {
LastCooked,
RecipeRating,
PdfViewer,
ImageViewer,
Ingredient,
@ -191,7 +210,8 @@ export default {
recipe: undefined,
ingredient_count: 0,
servings: 1,
start_time: ""
start_time: "",
share_uid: window.SHARE_UID
}
},
mounted() {

View File

@ -1,7 +1,7 @@
<template>
<div>
<b-modal class="modal" id="id_modal_add_book" :title="$t('Add_to_Book')" :ok-title="$t('Add')"
<b-modal class="modal" :id="`id_modal_add_book_${modal_id}`" :title="$t('Add_to_Book')" :ok-title="$t('Add')"
:cancel-title="$t('Close')" @ok="addToBook()">
<multiselect
@ -42,6 +42,7 @@ export default {
},
props: {
recipe: Object,
modal_id: Number
},
data() {
return {

View File

@ -1,7 +1,7 @@
<template>
<div>
<b-modal class="modal" id="id_modal_cook_log" :title="$t('Log_Recipe_Cooking')" :ok-title="$t('Save')"
<b-modal class="modal" :id="`id_modal_cook_log_${modal_id}`" :title="$t('Log_Recipe_Cooking')" :ok-title="$t('Save')"
:cancel-title="$t('Close')" @ok="logCook()">
<p>{{ $t('all_fields_optional') }}</p>
@ -38,6 +38,7 @@ export default {
name: 'CookLog',
props: {
recipe: Object,
modal_id: Number
},
data() {
return {

View File

@ -7,7 +7,7 @@
</td>
</template>
<template v-else>
<td>
<td class="d-print-none">
<i class="far fa-check-circle text-success" v-if="ingredient.checked"></i>
<i class="far fa-check-circle text-primary" v-if="!ingredient.checked"></i>
</td>
@ -31,7 +31,7 @@
</span>
<div class="d-none d-print-block">
<i class="far fa-comment-alt"></i> {{ ingredient.note }}
<i class="far fa-comment-alt d-print-none"></i> {{ ingredient.note }}
</div>
</div>
</td>

View File

@ -0,0 +1,28 @@
<template>
<span>
<b-badge pill variant="primary" v-if="recipe.last_cooked !== null"><i class="fas fa-utensils"></i> {{
formatDate(recipe.last_cooked)
}}</b-badge>
</span>
</template>
<script>
import moment from "moment/moment";
export default {
name: "LastCooked",
props: {
recipe: Object
},
methods: {
formatDate: function (datetime) {
moment.locale(window.navigator.language);
return moment(datetime).format('L')
}
}
}
</script>
<style scoped>
</style>

View File

@ -1,9 +1,9 @@
<template>
<div class="row">
<div class="col" style="text-align: center">
<img class="spinner-tandoor" alt="loading spinner" src="" style="height: 30vh"/>
</div>
<div class="row">
<div class="col" style="text-align: center">
<img class="spinner-tandoor" alt="loading spinner" src="" v-bind:style="{ height: size + 'vh' }"/>
</div>
</div>
</template>
<script>
@ -12,6 +12,10 @@ export default {
name: 'LoadingSpinner',
props: {
recipe: Object,
size: {
type: Number,
default: 30
},
},
}
</script>

View File

@ -5,25 +5,44 @@
<a :href="clickUrl()">
<b-card-img-lazy style="height: 15vh; object-fit: cover" :src=recipe_image v-bind:alt="$t('Recipe_Image')"
top></b-card-img-lazy>
<div class="h-100 d-flex flex-column justify-content-right"
<div class="card-img-overlay h-100 d-flex flex-column justify-content-right"
style="float:right; text-align: right; padding-top: 10px; padding-right: 5px">
<recipe-context-menu :recipe="recipe" style="float:right" v-if="recipe !== null"></recipe-context-menu>
<a>
<recipe-context-menu :recipe="recipe" style="float:right" v-if="recipe !== null"></recipe-context-menu>
</a>
</div>
</a>
<b-card-body>
<h5><a :href="clickUrl()">
<b-card-body style="padding: 16px">
<h6><a :href="clickUrl()">
<template v-if="recipe !== null">{{ recipe.name }}</template>
<template v-else>{{ meal_plan.title }}</template>
</a></h5>
</a></h6>
<b-card-text style="text-overflow: ellipsis">
<b-card-text style="text-overflow: ellipsis;">
<template v-if="recipe !== null">
<recipe-rating :recipe="recipe"></recipe-rating>
<template v-if="recipe.description !== null">
<span v-if="recipe.description.length > 120">
{{ recipe.description.substr(0, 120) + "\u2026" }}
</span>
<span v-if="recipe.description.length <= 120">
{{ recipe.description }}
</span>
</template>
<br/> <!-- TODO UGLY! -->
<last-cooked :recipe="recipe"></last-cooked>
<keywords :recipe="recipe" style="margin-top: 4px"></keywords>
<b-badge pill variant="info" v-if="!recipe.internal">{{ $t('External') }}</b-badge>
<b-badge pill variant="success" v-if="Date.parse(recipe.created_at) > new Date(Date.now() - (7 * (1000 * 60 * 60 * 24)))">{{ $t('New') }}</b-badge>
<b-badge pill variant="success"
v-if="Date.parse(recipe.created_at) > new Date(Date.now() - (7 * (1000 * 60 * 60 * 24)))">
{{ $t('New') }}
</b-badge>
</template>
<template v-else>{{ meal_plan.note }}</template>
</b-card-text>
@ -41,13 +60,19 @@
import RecipeContextMenu from "@/components/RecipeContextMenu";
import Keywords from "@/components/Keywords";
import {resolveDjangoUrl, ResolveUrlMixin} from "@/utils/utils";
import RecipeRating from "@/components/RecipeRating";
import moment from "moment/moment";
import Vue from "vue";
import LastCooked from "@/components/LastCooked";
Vue.prototype.moment = moment
export default {
name: "RecipeCard",
mixins: [
ResolveUrlMixin,
],
components: {Keywords, RecipeContextMenu},
components: {LastCooked, RecipeRating, Keywords, RecipeContextMenu},
props: {
recipe: Object,
meal_plan: Object,
@ -81,4 +106,4 @@ export default {
<style scoped>
</style>
</style>

View File

@ -1,10 +1,10 @@
<template>
<div>
<div class="dropdown">
<div class="dropdown d-print-none">
<a class="btn shadow-none" href="#" role="button" id="dropdownMenuLink"
data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
<i class="fas fa-ellipsis-v"></i>
<i class="fas fa-ellipsis-v fa-lg"></i>
</a>
<div class="dropdown-menu dropdown-menu-right" aria-labelledby="dropdownMenuLink">
@ -15,9 +15,11 @@
<a class="dropdown-item" :href="resolveDjangoUrl('edit_convert_recipe', recipe.id)" v-if="!recipe.internal"><i
class="fas fa-exchange-alt fa-fw"></i> {{ $t('convert_internal') }}</a>
<button class="dropdown-item" @click="$bvModal.show('id_modal_add_book')">
<i class="fas fa-bookmark fa-fw"></i> {{ $t('Add_to_Book') }}
</button>
<a href="#">
<button class="dropdown-item" @click="$bvModal.show(`id_modal_add_book_${modal_id}`)">
<i class="fas fa-bookmark fa-fw"></i> {{ $t('Add_to_Book') }}
</button>
</a>
<a class="dropdown-item" :href="`${resolveDjangoUrl('view_shopping') }?r=[${recipe.id},${servings_value}]`"
v-if="recipe.internal" target="_blank" rel="noopener noreferrer">
@ -29,33 +31,60 @@
class="fas fa-calendar fa-fw"></i> {{ $t('Add_to_Plan') }}
</a>
<a href="#">
<button class="dropdown-item" @click="$bvModal.show(`id_modal_cook_log_${modal_id}`)"><i
class="fas fa-clipboard-list fa-fw"></i> {{ $t('Log_Cooking') }}
</button>
</a>
<button class="dropdown-item" @click="$bvModal.show('id_modal_cook_log')"><i
class="fas fa-clipboard-list fa-fw"></i> {{ $t('Log_Cooking') }}
</button>
<button class="dropdown-item" onclick="window.print()"><i
class="fas fa-print fa-fw"></i> {{ $t('Print') }}
</button>
<a href="#">
<button class="dropdown-item" onclick="window.print()"><i
class="fas fa-print fa-fw"></i> {{ $t('Print') }}
</button>
</a>
<a class="dropdown-item" :href="resolveDjangoUrl('view_export') + '?r=' + recipe.id" target="_blank"
rel="noopener noreferrer"><i class="fas fa-file-export fa-fw"></i> {{ $t('Export') }}</a>
<a class="dropdown-item" :href="resolveDjangoUrl('new_share_link', recipe.id)" target="_blank"
rel="noopener noreferrer" v-if="recipe.internal"><i class="fas fa-share-alt fa-fw"></i> {{ $t('Share') }}</a>
<a href="#">
<button class="dropdown-item" @click="createShareLink()" v-if="recipe.internal"><i
class="fas fa-share-alt fa-fw"></i> {{ $t('Share') }}
</button>
</a>
</div>
</div>
<cook-log :recipe="recipe"></cook-log>
<cook-log :recipe="recipe" :modal_id="modal_id"></cook-log>
<add-recipe-to-book :recipe="recipe" :modal_id="modal_id"></add-recipe-to-book>
<b-modal :id="`modal-share-link_${modal_id}`" v-bind:title="$t('Share')" hide-footer>
<div class="row">
<div class="col col-md-12">
<label v-if="recipe_share_link !== undefined">{{ $t('Public share link') }}</label>
<input ref="share_link_ref" class="form-control" v-model="recipe_share_link"/>
<b-button class="mt-2 mb-3 d-none d-md-inline" variant="secondary"
@click="$bvModal.hide(`modal-share-link_${modal_id}`)">{{ $t('Close') }}
</b-button>
<b-button class="mt-2 mb-3 ml-md-2" variant="primary" @click="copyShareLink()">{{ $t('Copy') }}</b-button>
<b-button class="mt-2 mb-3 ml-2 float-right" variant="success" @click="shareIntend()">{{ $t('Share') }} <i
class="fa fa-share-alt"></i></b-button>
</div>
</div>
</b-modal>
</div>
</template>
<script>
import {ResolveUrlMixin} from "@/utils/utils";
import {makeToast, resolveDjangoUrl, ResolveUrlMixin} from "@/utils/utils";
import CookLog from "@/components/CookLog";
import axios from "axios";
import AddRecipeToBook from "./AddRecipeToBook";
export default {
name: 'RecipeContextMenu',
@ -63,11 +92,14 @@ export default {
ResolveUrlMixin
],
components: {
AddRecipeToBook,
CookLog
},
data() {
return {
servings_value: 0
servings_value: 0,
recipe_share_link: undefined,
modal_id: this.recipe.id + Math.round(Math.random() * 100000)
}
},
props: {
@ -79,6 +111,33 @@ export default {
},
mounted() {
this.servings_value = ((this.servings === -1) ? this.recipe.servings : this.servings)
},
methods: {
createShareLink: function () {
axios.get(resolveDjangoUrl('api_share_link', this.recipe.id)).then(result => {
this.$bvModal.show(`modal-share-link_${this.modal_id}`)
this.recipe_share_link = result.data.link
}).catch(err => {
if (err.response.status === 403) {
makeToast(this.$t('Share'), this.$t('Sharing is not enabled for this space.'), 'danger')
}
})
},
copyShareLink: function () {
let share_input = this.$refs.share_link_ref;
share_input.select();
document.execCommand("copy");
},
shareIntend: function () {
let shareData = {
title: this.recipe.name,
text: `${this.$t('Check out this recipe: ')} ${this.recipe.name}`,
url: this.recipe_share_link
}
navigator.share(shareData)
}
}
}
</script>

View File

@ -0,0 +1,25 @@
<template>
<div>
<span class="d-inline" v-if="recipe.rating > 0">
<i class="fas fa-star fa-xs text-primary" v-for="i in Math.floor(recipe.rating)" v-bind:key="i"></i>
<i class="fas fa-star-half-alt fa-xs text-primary" v-if="recipe.rating % 1 > 0"></i>
<i class="far fa-star fa-xs text-secondary" v-for="i in (5 - Math.ceil(recipe.rating))" v-bind:key="i + 10"></i>
</span>
</div>
</template>
<script>
export default {
name: "RecipeRating",
props: {
recipe: Object
}
}
</script>
<style scoped>
</style>

View File

@ -22,7 +22,7 @@
</div>
<div class="col col-md-4" style="text-align: right">
<b-button @click="details_visible = !details_visible" style="border: none; background: none"
class="shadow-none" :class="{ 'text-primary': details_visible, 'text-success': !details_visible}">
class="shadow-none d-print-none" :class="{ 'text-primary': details_visible, 'text-success': !details_visible}">
<i class="far fa-check-circle"></i>
</b-button>
</div>
@ -65,7 +65,7 @@
<div class="col-md-2" style="text-align: right">
<b-button @click="details_visible = !details_visible" style="border: none; background: none"
class="shadow-none" :class="{ 'text-primary': details_visible, 'text-success': !details_visible}">
class="shadow-none d-print-none" :class="{ 'text-primary': details_visible, 'text-success': !details_visible}">
<i class="far fa-check-circle"></i>
</b-button>
</div>

View File

@ -1,5 +1,5 @@
{
"Import": "Import",
"Import": "Importieren",
"import_running": "Import läuft, bitte warten!",
"Import_finished": "Import fertig",
"View_Recipes": "Rezepte Ansehen",
@ -27,7 +27,7 @@
"min": "Min",
"Servings": "Portionen",
"Waiting": "Wartezeit",
"Preparation": "Zubereitung",
"Preparation": "Vorbereitung",
"Edit": "Bearbeiten",
"Open": "Öffnen",
"Save": "Speichern",
@ -48,5 +48,28 @@
"Export": "Exportieren",
"Rating": "Bewertung",
"Close": "Schließen",
"Add": "Hinzufügen"
"Add": "Hinzufügen",
"Copy": "Kopieren",
"New": "Neu",
"Categories": "Kategorien",
"Category": "Kategorie",
"Selected": "Ausgewählt",
"Supermarket": "Supermarkt",
"Files": "Dateien",
"Size": "Größe",
"success_fetching_resource": "Ressource erfolgreich abgerufen!",
"Download": "Herunterladen",
"Success": "Erfolgreich",
"err_fetching_resource": "Ein Fehler trat während dem Abrufen einer Ressource auf!",
"err_creating_resource": "Ein Fehler trat während dem Erstellen einer Ressource auf!",
"err_updating_resource": "Ein Fehler trat während dem Aktualisieren einer Ressource auf!",
"success_creating_resource": "Ressource erfolgreich erstellt!",
"success_updating_resource": "Ressource erfolgreich aktualisiert!",
"File": "Datei",
"Delete": "Löschen",
"err_deleting_resource": "Ein Fehler trat während dem Löschen einer Ressource auf!",
"Cancel": "Abbrechen",
"success_deleting_resource": "Ressource erfolgreich gelöscht!",
"Load_More": "Mehr laden",
"Ok": "Öffnen"
}

View File

@ -50,9 +50,11 @@
"Date": "Date",
"Share": "Share",
"Export": "Export",
"Copy": "Copy",
"Rating": "Rating",
"Close": "Close",
"Cancel": "Cancel",
"Link": "Link",
"Add": "Add",
"New": "New",
"Success": "Success",
@ -95,4 +97,4 @@
"Advanced Search Settings": "Advanced Search Settings",
"Download": "Download",
"Root": "Root"
}
}

76
vue/src/locales/hy.json Normal file
View File

@ -0,0 +1,76 @@
{
"err_fetching_resource": "",
"err_creating_resource": "",
"err_updating_resource": "",
"err_deleting_resource": "",
"success_fetching_resource": "",
"success_creating_resource": "",
"success_updating_resource": "",
"success_deleting_resource": "",
"import_running": "",
"all_fields_optional": "",
"convert_internal": "",
"show_only_internal": "",
"Log_Recipe_Cooking": "",
"External_Recipe_Image": "",
"Add_to_Book": "",
"Add_to_Shopping": "",
"Add_to_Plan": "",
"Step_start_time": "",
"Meal_Plan": "",
"Select_Book": "",
"Recipe_Image": "",
"Import_finished": "",
"View_Recipes": "",
"Log_Cooking": "",
"New_Recipe": "",
"Url_Import": "",
"Reset_Search": "",
"Recently_Viewed": "",
"Load_More": "",
"Keywords": "",
"Books": "",
"Proteins": "",
"Fats": "",
"Carbohydrates": "",
"Calories": "",
"Nutrition": "",
"Date": "",
"Share": "",
"Export": "",
"Copy": "",
"Rating": "",
"Close": "",
"Link": "",
"Add": "",
"New": "",
"Success": "",
"Ingredients": "",
"Supermarket": "",
"Categories": "",
"Category": "",
"Selected": "",
"min": "",
"Servings": "",
"Waiting": "",
"Preparation": "",
"External": "",
"Size": "",
"Files": "",
"File": "",
"Edit": "",
"Cancel": "",
"Delete": "",
"Open": "",
"Ok": "",
"Save": "",
"Step": "",
"Search": "",
"Import": "",
"Print": "",
"Settings": "",
"or": "",
"and": "",
"Information": "",
"Download": ""
}

View File

@ -193,6 +193,18 @@ export interface ImportLog {
* @memberof ImportLog
*/
keyword?: ImportLogKeyword;
/**
*
* @type {number}
* @memberof ImportLog
*/
total_recipes?: number;
/**
*
* @type {number}
* @memberof ImportLog
*/
imported_recipes?: number;
/**
*
* @type {string}
@ -631,7 +643,19 @@ export interface MealPlanRecipe {
* @type {string}
* @memberof MealPlanRecipe
*/
file_path?: string;
servings_text?: string;
/**
*
* @type {string}
* @memberof MealPlanRecipe
*/
rating?: string;
/**
*
* @type {string}
* @memberof MealPlanRecipe
*/
last_cooked?: string;
}
/**
*
@ -766,6 +790,18 @@ export interface Recipe {
* @memberof Recipe
*/
servings_text?: string;
/**
*
* @type {string}
* @memberof Recipe
*/
rating?: string;
/**
*
* @type {string}
* @memberof Recipe
*/
last_cooked?: string;
}
/**
*
@ -1047,7 +1083,19 @@ export interface RecipeOverview {
* @type {string}
* @memberof RecipeOverview
*/
file_path?: string;
servings_text?: string;
/**
*
* @type {string}
* @memberof RecipeOverview
*/
rating?: string;
/**
*
* @type {string}
* @memberof RecipeOverview
*/
last_cooked?: string;
}
/**
*
@ -1134,6 +1182,12 @@ export interface RecipeSteps {
* @memberof RecipeSteps
*/
show_as_header?: boolean;
/**
*
* @type {StepFile}
* @memberof RecipeSteps
*/
file?: StepFile | null;
}
/**
@ -1142,7 +1196,8 @@ export interface RecipeSteps {
*/
export enum RecipeStepsTypeEnum {
Text = 'TEXT',
Time = 'TIME'
Time = 'TIME',
File = 'FILE'
}
/**
@ -1532,6 +1587,12 @@ export interface Step {
* @memberof Step
*/
show_as_header?: boolean;
/**
*
* @type {StepFile}
* @memberof Step
*/
file?: StepFile | null;
}
/**
@ -1540,9 +1601,35 @@ export interface Step {
*/
export enum StepTypeEnum {
Text = 'TEXT',
Time = 'TIME'
Time = 'TIME',
File = 'FILE'
}
/**
*
* @export
* @interface StepFile
*/
export interface StepFile {
/**
*
* @type {string}
* @memberof StepFile
*/
name: string;
/**
*
* @type {any}
* @memberof StepFile
*/
file?: any;
/**
*
* @type {number}
* @memberof StepFile
*/
id?: number;
}
/**
*
* @export