Merge branch 'develop' into feature/importer_to_vue

# Conflicts:
#	cookbook/helper/recipe_url_import.py
This commit is contained in:
vabene1111
2022-03-04 14:33:59 +01:00
60 changed files with 1172 additions and 1172 deletions

View File

@@ -1,145 +1,126 @@
<template>
<div id="app">
<br/>
<div id="app">
<br />
<template v-if="export_info !== undefined">
<template v-if="export_info !== undefined">
<template v-if="export_info.running">
<h5 style="text-align: center">{{ $t("Exporting") }}...</h5>
<template v-if="export_info.running">
<h5 style="text-align: center">{{ $t('Exporting') }}...</h5>
<b-progress :max="export_info.total_recipes">
<b-progress-bar :value="export_info.exported_recipes" :label="`${export_info.exported_recipes}/${export_info.total_recipes}`"></b-progress-bar>
</b-progress>
<b-progress :max="export_info.total_recipes">
<b-progress-bar :value="export_info.exported_recipes" :label="`${export_info.exported_recipes}/${export_info.total_recipes}`"></b-progress-bar>
</b-progress>
<loading-spinner :size="25"></loading-spinner>
</template>
<loading-spinner :size="25"></loading-spinner>
</template>
<div class="row">
<div class="col col-md-12" v-if="!export_info.running">
<span>{{ $t("Export_finished") }}! </span> <a :href="`${resolveDjangoUrl('viewExport')}`">{{ $t("Return to export") }} </a><br /><br />
<div class="row">
<div class="col col-md-12" v-if="!export_info.running">
<span>{{ $t('Export_finished') }}! </span> <a :href="`${resolveDjangoUrl('viewExport') }`">{{ $t('Return to export') }} </a><br><br>
{{ $t("If download did not start automatically: ") }}
{{ $t('If download did not start automatically: ') }}
<template v-if="export_info.expired">
<a disabled><del>{{ $t('Download') }}</del></a> ({{ $t('Expired') }})
</template>
<a v-else :href="`/export-file/${export_id}/`" ref="downloadAnchor" >{{ $t('Download') }}</a>
<template v-if="export_info.expired">
<a disabled
><del>{{ $t("Download") }}</del></a
>
({{ $t("Expired") }})
</template>
<a v-else :href="`${resolveDjangoUrl('view_export_file', export_id)}`" ref="downloadAnchor">{{ $t("Download") }}</a>
<br>
{{ $t('The link will remain active for') }}
<template v-if="export_info.cache_duration > 3600">
{{ export_info.cache_duration/3600 }}{{ $t('hr') }}
</template>
<template v-else-if="export_info.cache_duration > 60">
{{ export_info.cache_duration/60 }}{{ $t('min') }}
</template>
<template v-else>
{{ export_info.cache_duration }}{{ $t('sec') }}
</template>
<br />
{{ $t("The link will remain active for") }}
<template v-if="export_info.cache_duration > 3600"> {{ export_info.cache_duration / 3600 }}{{ $t("hr") }} </template>
<template v-else-if="export_info.cache_duration > 60"> {{ export_info.cache_duration / 60 }}{{ $t("min") }} </template>
<template v-else> {{ export_info.cache_duration }}{{ $t("sec") }} </template>
<br>
</div>
</div>
<br/>
<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="export_info.msg"
disabled></textarea>
</div>
</div>
<br/>
<br/>
</template>
</div>
<br />
</div>
</div>
<br />
<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="export_info.msg" disabled></textarea>
</div>
</div>
<br />
<br />
</template>
</div>
</template>
<script>
import Vue from 'vue'
import {BootstrapVue} from 'bootstrap-vue'
import Vue from "vue"
import { BootstrapVue } from "bootstrap-vue"
import 'bootstrap-vue/dist/bootstrap-vue.css'
import "bootstrap-vue/dist/bootstrap-vue.css"
import {ResolveUrlMixin, makeToast, ToastMixin} from "@/utils/utils";
import { ResolveUrlMixin, makeToast, ToastMixin } from "@/utils/utils"
import LoadingSpinner from "@/components/LoadingSpinner";
import LoadingSpinner from "@/components/LoadingSpinner"
import {ApiApiFactory} from "@/utils/openapi/api.ts";
import { ApiApiFactory } from "@/utils/openapi/api.ts"
Vue.use(BootstrapVue)
export default {
name: 'ExportResponseView',
mixins: [
ResolveUrlMixin,
ToastMixin,
],
components: {
LoadingSpinner
},
data() {
return {
export_id: window.EXPORT_ID,
export_info: undefined,
}
},
mounted() {
this.refreshData()
this.$i18n.locale = window.CUSTOM_LOCALE
this.dynamicIntervalTimeout = 250 //initial refresh rate
this.run = setTimeout(this.dynamicInterval.bind(this), this.dynamicIntervalTimeout)
},
methods: {
dynamicInterval: function(){
//update frequently at start but slowdown as it takes longer
this.dynamicIntervalTimeout = Math.round(this.dynamicIntervalTimeout*((1+Math.sqrt(5))/2))
if(this.dynamicIntervalTimeout > 5000) this.dynamicIntervalTimeout = 5000
clearInterval(this.run);
this.run = setInterval(this.dynamicInterval.bind(this), this.dynamicIntervalTimeout);
if ((this.export_id !== null) && window.navigator.onLine && this.export_info.running) {
name: "ExportResponseView",
mixins: [ResolveUrlMixin, ToastMixin],
components: {
LoadingSpinner,
},
data() {
return {
export_id: window.EXPORT_ID,
export_info: undefined,
}
},
mounted() {
this.refreshData()
let el = this.$refs.output_text
el.scrollTop = el.scrollHeight;
this.$i18n.locale = window.CUSTOM_LOCALE
if(this.export_info.expired)
makeToast(this.$t("Error"), this.$t("The download link is expired!"), "danger")
}
this.dynamicIntervalTimeout = 250 //initial refresh rate
this.run = setTimeout(this.dynamicInterval.bind(this), this.dynamicIntervalTimeout)
},
methods: {
dynamicInterval: function () {
//update frequently at start but slowdown as it takes longer
this.dynamicIntervalTimeout = Math.round(this.dynamicIntervalTimeout * ((1 + Math.sqrt(5)) / 2))
if (this.dynamicIntervalTimeout > 5000) this.dynamicIntervalTimeout = 5000
clearInterval(this.run)
this.run = setInterval(this.dynamicInterval.bind(this), this.dynamicIntervalTimeout)
startDownload: function(){
this.$refs['downloadAnchor'].click()
if (this.export_id !== null && window.navigator.onLine && this.export_info.running) {
this.refreshData()
let el = this.$refs.output_text
el.scrollTop = el.scrollHeight
if (this.export_info.expired) makeToast(this.$t("Error"), this.$t("The download link is expired!"), "danger")
}
},
startDownload: function () {
this.$refs["downloadAnchor"].click()
},
refreshData: function () {
let apiClient = new ApiApiFactory()
apiClient.retrieveExportLog(this.export_id).then((result) => {
this.export_info = result.data
this.export_info.expired = !this.export_info.possibly_not_expired
if (!this.export_info.running)
this.$nextTick(() => {
this.startDownload()
})
})
},
},
refreshData: function () {
let apiClient = new ApiApiFactory()
apiClient.retrieveExportLog(this.export_id).then(result => {
this.export_info = result.data
this.export_info.expired = !this.export_info.possibly_not_expired
if(!this.export_info.running)
this.$nextTick(()=>{ this.startDownload(); } )
})
}
}
}
</script>
<style>
</style>
<style></style>

View File

@@ -1,174 +1,180 @@
<template>
<div id="app">
<div id="app">
<h2>{{ $t("Export") }}</h2>
<div class="row">
<div class="col col-md-12">
<br />
<!-- TODO get option dynamicaly -->
<select class="form-control" v-model="recipe_app">
<option value="DEFAULT">Default</option>
<option value="SAFFRON">Saffron</option>
<option value="RECIPESAGE">Recipe Sage</option>
<option value="PDF">PDF (experimental)</option>
</select>
<h2>{{ $t('Export') }}</h2>
<div class="row">
<div class="col col-md-12">
<br />
<b-form-checkbox v-model="export_all" @change="disabled_multiselect = $event" name="check-button" switch style="margin-top: 1vh">
{{ $t("All recipes") }}
</b-form-checkbox>
<br/>
<!-- TODO get option dynamicaly -->
<select class="form-control" v-model="recipe_app">
<option value="DEFAULT">Default</option>
<option value="SAFFRON">Saffron</option>
<option value="RECIPESAGE">Recipe Sage</option>
<option value="PDF">PDF (experimental)</option>
</select>
<!-- <multiselect
:searchable="true"
:disabled="disabled_multiselect"
v-model="recipe_list"
:options="recipes"
:close-on-select="false"
:clear-on-select="true"
:hide-selected="true"
:preserve-search="true"
placeholder="Select Recipes"
:taggable="false"
label="name"
track-by="id"
id="id_recipes"
:multiple="true"
:loading="recipes_loading"
@search-change="searchRecipes"
>
</multiselect> -->
<generic-multiselect
class="input-group-text m-0 p-0"
@change="recipe_list = $event.val"
label="name"
:model="Models.RECIPE"
style="flex-grow: 1; flex-shrink: 1; flex-basis: 0"
v-bind:placeholder="$t('Recipe')"
:limit="20"
:multiple="true"
/>
<generic-multiselect
@change="filter = $event.val"
:model="Models.CUSTOM_FILTER"
style="flex-grow: 1; flex-shrink: 1; flex-basis: 0"
:placeholder="$t('Custom Filter')"
:multiple="false"
:limit="50"
/>
<br/>
<b-form-checkbox v-model="export_all" @change="disabled_multiselect=$event" name="check-button" switch style="margin-top: 1vh">
{{ $t('All recipes') }}
</b-form-checkbox>
<multiselect
:searchable="true"
:disabled="disabled_multiselect"
v-model="recipe_list"
:options="recipes"
:close-on-select="false"
:clear-on-select="true"
:hide-selected="true"
:preserve-search="true"
placeholder="Select Recipes"
:taggable="false"
label="name"
track-by="id"
id="id_recipes"
:multiple="true"
:loading="recipes_loading"
@search-change="searchRecipes">
</multiselect>
<br/>
<button @click="exportRecipe()" class="btn btn-primary shadow-none"><i class="fas fa-file-export"></i> {{ $t('Export') }}
</button>
<br />
<button @click="exportRecipe()" class="btn btn-primary shadow-none"><i class="fas fa-file-export"></i> {{ $t("Export") }}</button>
</div>
</div>
</div>
</div>
</template>
<script>
import Vue from 'vue'
import {BootstrapVue} from 'bootstrap-vue'
import Vue from "vue"
import { BootstrapVue } from "bootstrap-vue"
import 'bootstrap-vue/dist/bootstrap-vue.css'
import "bootstrap-vue/dist/bootstrap-vue.css"
import LoadingSpinner from "@/components/LoadingSpinner"
import LoadingSpinner from "@/components/LoadingSpinner";
import {StandardToasts, makeToast, resolveDjangoUrl} from "@/utils/utils";
import Multiselect from "vue-multiselect";
import {ApiApiFactory} from "@/utils/openapi/api.ts";
import axios from "axios";
import { StandardToasts, makeToast, resolveDjangoUrl, ApiMixin } from "@/utils/utils"
// import Multiselect from "vue-multiselect"
import GenericMultiselect from "@/components/GenericMultiselect"
import { ApiApiFactory } from "@/utils/openapi/api.ts"
import axios from "axios"
Vue.use(BootstrapVue)
export default {
name: 'ExportView',
/*mixins: [
name: "ExportView",
/*mixins: [
ResolveUrlMixin,
ToastMixin,
],*/
components: {Multiselect},
data() {
return {
export_id: window.EXPORT_ID,
loading: false,
disabled_multiselect: false,
components: { GenericMultiselect },
mixins: [ApiMixin],
data() {
return {
export_id: window.EXPORT_ID,
loading: false,
disabled_multiselect: false,
recipe_app: 'DEFAULT',
recipe_list: [],
recipes_loading: false,
recipes: [],
export_all: false,
}
},
mounted() {
if(this.export_id)
this.insertRequested()
else
this.searchRecipes('')
},
methods: {
insertRequested: function(){
let apiFactory = new ApiApiFactory()
this.recipes_loading = true
apiFactory.retrieveRecipe(this.export_id).then((response) => {
this.recipes_loading = false
this.recipe_list.push(response.data)
}).catch((err) => {
console.log(err)
StandardToasts.makeStandardToast(StandardToasts.FAIL_FETCH)
}).then(e => this.searchRecipes(''))
recipe_app: "DEFAULT",
recipe_list: [],
recipes_loading: false,
recipes: [],
export_all: false,
filter: undefined,
}
},
searchRecipes: function (query) {
let apiFactory = new ApiApiFactory()
this.recipes_loading = true
let maxResultLenght = 1000
apiFactory.listRecipes(query, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, 1, maxResultLenght).then((response) => {
this.recipes = response.data.results;
this.recipes_loading = false
}).catch((err) => {
console.log(err)
StandardToasts.makeStandardToast(StandardToasts.FAIL_FETCH)
})
mounted() {
if (this.export_id) this.insertRequested()
// else this.searchRecipes("")
},
methods: {
insertRequested: function () {
let apiFactory = new ApiApiFactory()
exportRecipe: function () {
this.recipes_loading = true
if (this.recipe_list.length < 1 && this.export_all == false) {
makeToast(this.$t("Error"), this.$t("Select at least one recipe"), "danger")
return;
}
apiFactory
.retrieveRecipe(this.export_id)
.then((response) => {
this.recipes_loading = false
this.recipe_list.push(response.data)
})
.catch((err) => {
console.log(err)
StandardToasts.makeStandardToast(StandardToasts.FAIL_FETCH)
})
// .then((e) => this.searchRecipes(""))
},
this.error = undefined
this.loading = true
let formData = new FormData();
formData.append('type', this.recipe_app);
formData.append('all', this.export_all)
// searchRecipes: function (query) {
// this.recipes_loading = true
for (var i = 0; i < this.recipe_list.length; i++) {
formData.append('recipes', this.recipe_list[i].id);
}
// this.genericAPI(this.Models.RECIPE, this.Actions.LIST, { query: query })
// .then((response) => {
// this.recipes = response.data.results
// this.recipes_loading = false
// })
// .catch((err) => {
// console.log(err)
// StandardToasts.makeStandardToast(StandardToasts.FAIL_FETCH)
// })
// },
axios.defaults.headers.post['Content-Type'] = 'application/x-www-form-urlencoded';
axios.post(resolveDjangoUrl('view_export',), formData).then((response) => {
if (response.data['error'] !== undefined){
makeToast(this.$t("Error"), response.data['error'],"warning")
}else{
window.location.href = resolveDjangoUrl('view_export_response', response.data['export_id'])
}
exportRecipe: function () {
if (this.recipe_list.length < 1 && this.export_all == false && this.filter === undefined) {
makeToast(this.$t("Error"), this.$t("Select at least one recipe"), "danger")
return
}
}).catch((err) => {
this.error = err.data
this.loading = false
console.log(err)
makeToast(this.$t("Error"), this.$t("There was an error loading a resource!"), "warning")
})
this.error = undefined
this.loading = true
let formData = new FormData()
formData.append("type", this.recipe_app)
formData.append("all", this.export_all)
formData.append("filter", this.filter?.id ?? null)
for (var i = 0; i < this.recipe_list.length; i++) {
formData.append("recipes", this.recipe_list[i].id)
}
axios.defaults.headers.post["Content-Type"] = "application/x-www-form-urlencoded"
axios
.post(resolveDjangoUrl("view_export"), formData)
.then((response) => {
if (response.data["error"] !== undefined) {
makeToast(this.$t("Error"), response.data["error"], "warning")
} else {
window.location.href = resolveDjangoUrl("view_export_response", response.data["export_id"])
}
})
.catch((err) => {
this.error = err.data
this.loading = false
console.log(err)
makeToast(this.$t("Error"), this.$t("There was an error loading a resource!"), "warning")
})
},
},
}
}
</script>
<style src="vue-multiselect/dist/vue-multiselect.min.css"></style>
<style>
</style>
<style></style>

View File

@@ -65,8 +65,11 @@
:preserve-search="true"
:internal-search="false"
:limit="options_limit"
placeholder="Select Keyword"
tag-placeholder="Add Keyword"
:placeholder="$t('select_keyword')"
:tag-placeholder="$t('add_keyword')"
:select-label="$t('Select')"
:selected-label="$t('Selected')"
:deselect-label="$t('remove_selection')"
:taggable="true"
@tag="addKeyword"
label="label"
@@ -76,6 +79,7 @@
:loading="keywords_loading"
@search-change="searchKeywords"
>
<template v-slot:noOptions>{{ $t("empty_list") }}</template>
</multiselect>
</div>
</div>
@@ -244,8 +248,10 @@
:clear-on-select="true"
:allow-empty="true"
:preserve-search="true"
placeholder="Select File"
select-label="Select"
:placeholder="$t('select_file')"
:select-label="$t('Select')"
:selected-label="$t('Selected')"
:deselect-label="$t('remove_selection')"
:id="'id_step_' + step.id + '_file'"
label="name"
track-by="name"
@@ -254,6 +260,7 @@
style="flex-grow: 1; flex-shrink: 1; flex-basis: 0"
@search-change="searchFiles"
>
<template v-slot:noOptions>{{ $t("empty_list") }}</template>
</multiselect>
<b-input-group-append>
<b-button
@@ -283,14 +290,17 @@
:preserve-search="true"
:internal-search="false"
:limit="options_limit"
placeholder="Select Recipe"
select-label="Select"
:placeholder="$t('select_recipe')"
:select-label="$t('Select')"
:selected-label="$t('Selected')"
:deselect-label="$t('remove_selection')"
:id="'id_step_' + step.id + '_recipe'"
:custom-label="(opt) => recipes.find((x) => x.id === opt).name"
:multiple="false"
:loading="recipes_loading"
@search-change="searchRecipes"
>
<template v-slot:noOptions>{{ $t("empty_list") }}</template>
</multiselect>
</div>
</div>
@@ -340,9 +350,11 @@
:preserve-search="true"
:internal-search="false"
:limit="options_limit"
placeholder="Select Unit"
tag-placeholder="Create"
select-label="Select"
:placeholder="$t('select_unit')"
:tag-placeholder="$t('Create')"
:select-label="$t('Select')"
:selected-label="$t('Selected')"
:deselect-label="$t('remove_selection')"
:taggable="true"
@tag="addUnitType"
:id="`unit_${step_index}_${index}`"
@@ -352,6 +364,7 @@
:loading="units_loading"
@search-change="searchUnits"
>
<template v-slot:noOptions>{{ $t("empty_list") }}</template>
</multiselect>
</div>
<div class="col-lg-4 col-md-6 small-padding" v-if="!ingredient.is_header">
@@ -367,9 +380,11 @@
:preserve-search="true"
:internal-search="false"
:limit="options_limit"
placeholder="Select Food"
tag-placeholder="Create"
select-label="Select"
:placeholder="$t('select_food')"
:tag-placeholder="$t('Create')"
:select-label="$t('Select')"
:selected-label="$t('Selected')"
:deselect-label="$t('remove_selection')"
:taggable="true"
@tag="addFoodType"
:id="`ingredient_${step_index}_${index}`"
@@ -379,6 +394,7 @@
:loading="foods_loading"
@search-change="searchFoods"
>
<template v-slot:noOptions>{{ $t("empty_list") }}</template>
</multiselect>
</div>
<div class="small-padding" v-bind:class="{ 'col-lg-4 col-md-6': !ingredient.is_header, 'col-lg-12 col-md-12': ingredient.is_header }">
@@ -804,7 +820,7 @@ export default {
no_amount: false,
})
this.sortIngredients(step)
this.$nextTick(() => document.getElementById(`amount_${this.recipe.steps.indexOf(step)}_${step.ingredients.length - 1}`).focus())
this.$nextTick(() => document.getElementById(`amount_${this.recipe.steps.indexOf(step)}_${step.ingredients.length - 1}`).select())
},
removeIngredient: function (step, ingredient) {
if (confirm(this.$t("confirm_delete", { object: this.$t("Ingredient") }))) {
@@ -985,6 +1001,7 @@ export default {
unit: unit,
food: { name: result.data.food },
note: result.data.note,
original_text: ing,
})
})
order++

File diff suppressed because it is too large Load Diff

View File

@@ -934,6 +934,8 @@ export default {
this.ui = Object.assign({}, this.ui, this.$cookies.get(SETTINGS_COOKIE_NAME))
}
})
this.$i18n.locale = window.CUSTOM_LOCALE
console.log(window.CUSTOM_LOCALE)
},
methods: {
// this.genericAPI inherited from ApiMixin
@@ -1491,7 +1493,7 @@ export default {
flex-grow: 1;
overflow-y: scroll;
overflow-x: hidden;
height: 6vh;
height: 60vh; /* TODO use proper fill height here to not render list underneath bottom buttons */
padding-right: 8px !important;
}
}

View File

@@ -6,7 +6,7 @@
<b-sidebar id="related-recipes" backdrop right bottom no-header shadow="sm" style="z-index: 10000" @shown="updatePinnedRecipes()">
<template #default="{ hide }">
<div class="d-flex flex-column justify-content-end h-100 p-3 align-items-end">
<h5>Planned <i class="fas fa-calendar fa-fw"></i></h5>
<h5>{{$t("Planned")}} <i class="fas fa-calendar fa-fw"></i></h5>
<div class="text-right">
<template v-if="planned_recipes.length > 0">
@@ -24,11 +24,11 @@
</div>
</template>
<template v-else>
<span class="text-muted">You have nothing planned for today!</span>
<span class="text-muted">{{$t("nothing_planned_today")}}</span>
</template>
</div>
<h5>Pinned <i class="fas fa-thumbtack fa-fw"></i></h5>
<h5>{{$t("Pinned")}} <i class="fas fa-thumbtack fa-fw"></i></h5>
<template v-if="pinned_recipes.length > 0">
<div class="text-right">
@@ -53,7 +53,7 @@
</div>
</template>
<template v-else>
<span class="text-muted">You have no pinned recipes!</span>
<span class="text-muted">{{$t("no_pinned_recipes")}}</span>
</template>
<template v-if="related_recipes.length > 0">
@@ -77,8 +77,8 @@
</template>
<template #footer="{ hide }">
<div class="d-flex bg-dark text-light align-items-center px-3 py-2">
<strong class="mr-auto">Quick actions</strong>
<b-button size="sm" @click="hide">Close</b-button>
<strong class="mr-auto">{{$t("Quick actions")}}</strong>
<b-button size="sm" @click="hide">{{$t("Close")}}</b-button>
</div>
</template>
</b-sidebar>

View File

@@ -52,10 +52,12 @@ export default {
page_count: function () {
return Math.ceil(this.page_count_pagination / this.per_page_count)
},
display_recipes: function() {
return this.recipes.slice((this.current_page - 1 - 1) * 2, (this.current_page - 1) * 2)
}
},
data() {
return {
display_recipes: [],
current_page: 1,
per_page_count: 2,
bounce_left: false,
@@ -66,18 +68,23 @@ export default {
methods: {
pageChange: function (page) {
this.current_page = page
this.display_recipes = this.recipes.slice((this.current_page - 1 - 1) * 2, (this.current_page - 1) * 2)
this.loadRecipeDetails(page)
},
loadRecipeDetails: function (page) {
this.display_recipes.forEach((recipe, index) => {
if (recipe.recipe_content.steps === undefined) {
let apiClient = new ApiApiFactory()
apiClient.retrieveRecipe(recipe.recipe).then((result) => {
let new_entry = Object.assign({}, recipe)
new_entry.recipe_content = result.data
this.$set(this.display_recipes, index, new_entry)
let new_entry = Object.assign({}, recipe)
new_entry.recipe_content = result.data
this.recipes.forEach((rec, i) => {
if (rec.recipe === new_entry.recipe) {
this.$set(this.recipes, i, new_entry)
}
})
})
}
})
},
swipeLeft: function () {

View File

@@ -21,11 +21,11 @@
</div>
<div class="actionArea pt-1 pb-1 d-none d-lg-flex">
<span class="period-span-1 pt-1 pb-1 pl-1 pr-1 d-none d-xl-inline-flex text-body align-items-center">
<small>Period:</small>
<small>{{ $t('Period') }}:</small>
<b-form-select class="ml-1" id="UomInput" v-model="settings.displayPeriodUom" :options="options.displayPeriodUom"></b-form-select>
</span>
<span class="period-span-2 pt-1 pb-1 pl-1 pr-1 mr-1 ml-1 d-none d-xl-inline-flex text-body align-items-center">
<small>Periods:</small>
<small>{{ $t('Periods') }}:</small>
<b-form-select class="ml-1" id="UomInput" v-model="settings.displayPeriodCount" :options="options.displayPeriodCount"></b-form-select>
</span>
<span

View File

@@ -206,7 +206,7 @@ export default {
}
if (!cancel) {
this.$bvModal.hide(`edit-modal`)
this.$emit("save-entry", { ...this.mealplan_settings, ...this.entryEditing, ...{ addshopping: this.entryEditing.addshopping && !this.autoMealPlan } })
this.$emit("save-entry", { ...this.mealplan_settings, ...this.entryEditing, ...{ addshopping: this.mealplan_settings.addshopping && !this.autoMealPlan } })
}
},
deleteEntry() {

View File

@@ -1,39 +1,42 @@
<template>
<div>
<b-form-group
v-bind:label="label"
class="mb-3">
<b-form-select v-model="new_value" :placeholder="placeholder" :options="options"></b-form-select>
<b-form-group v-bind:label="label" class="mb-3">
<b-form-select v-model="new_value" :placeholder="placeholder" :options="translatedOptions"></b-form-select>
</b-form-group>
</div>
</template>
<script>
export default {
name: 'ChoiceInput',
props: {
field: {type: String, default: 'You Forgot To Set Field Name'},
label: {type: String, default: 'Text Field'},
value: {type: String, default: ''},
options: [],
placeholder: {type: String, default: 'You Should Add Placeholder Text'},
show_merge: {type: Boolean, default: false},
},
data() {
return {
new_value: undefined,
}
},
mounted() {
this.new_value = this.value
},
watch: {
'new_value': function () {
this.$root.$emit('change', this.field, this.new_value)
name: "ChoiceInput",
props: {
field: { type: String, default: "You Forgot To Set Field Name" },
label: { type: String, default: "Text Field" },
value: { type: String, default: "" },
options: [],
placeholder: { type: String, default: "You Should Add Placeholder Text" },
show_merge: { type: Boolean, default: false },
},
},
methods: {
}
data() {
return {
new_value: undefined,
}
},
mounted() {
this.new_value = this.value
},
watch: {
new_value: function () {
this.$root.$emit("change", this.field, this.new_value)
},
},
computed: {
translatedOptions() {
return this.options.map((x) => {
return { ...x, text: this.$t(x.text) }
})
},
},
methods: {},
}
</script>
</script>

View File

@@ -8,7 +8,7 @@
:initialContent="value"
:emojiData="emojiDataAll"
:emojiGroups="emojiGroups"
triggerType="hover"
triggerType="click"
:recentEmojisFeat="true"
recentEmojisStorage="local"
@contentChanged="setIcon"

View File

@@ -67,6 +67,9 @@ export default {
this.field = this.form?.field ?? "You Forgot To Set Field Name"
this.label = this.form?.label ?? ""
this.sticky_options = this.form?.sticky_options ?? []
this.sticky_options = this.sticky_options.map((x) => {
return { ...x, name: this.$t(x.name) }
})
this.list_label = this.form?.list_label ?? undefined
if (this.list_label?.includes("::")) {
this.list_label = this.list_label.split("::")[1]
@@ -74,7 +77,7 @@ export default {
},
computed: {
modelName() {
return this?.model?.name ?? this.$t("Search")
return this.$t(this?.model?.name) ?? this.$t("Search")
},
useMultiple() {
return this.form?.multiple || this.form?.ordered || false

View File

@@ -7,7 +7,7 @@
<div class="text-nowrap"><i class="fa fa-chevron-right rotate" :class="showDetails ? 'rotated' : ''"></i></div>
</b-button>
</b-col>
<b-col cols="1" class="align-items-center d-flex">
<b-col cols="2" md="1" class="align-items-center d-flex">
<div class="dropdown b-dropdown position-static inline-block" data-html2canvas-ignore="true" @click.stop="$emit('open-context-menu', $event, entries)">
<button
aria-haspopup="true"
@@ -23,7 +23,7 @@
<b-col cols="1" class="px-1 justify-content-center align-items-center d-none d-md-flex">
<input type="checkbox" class="form-control form-control-sm checkbox-control" :checked="formatChecked" @change="updateChecked" :key="entries[0].id" />
</b-col>
<b-col cols="8" md="9">
<b-col cols="7" md="9">
<b-row class="d-flex h-100">
<b-col cols="5" md="3" class="d-flex align-items-center" v-if="Object.entries(formatAmount).length == 1">
<strong class="mr-1">{{ Object.entries(formatAmount)[0][1] }}</strong> {{ Object.entries(formatAmount)[0][0] }}
@@ -86,7 +86,7 @@
<b-col cols="3" md="2" class="justify-content-start align-items-center d-flex d-md-none pr-0" v-if="settings.left_handed">
<input type="checkbox" class="form-control form-control-sm checkbox-control-mobile" :checked="formatChecked" @change="updateChecked" :key="entries[0].id" />
</b-col>
<b-col cols="1" class="align-items-center d-flex">
<b-col cols="2" md="1" class="align-items-center d-flex">
<div class="dropdown b-dropdown position-static inline-block" data-html2canvas-ignore="true" @click.stop="$emit('open-context-menu', $event, e)">
<button
aria-haspopup="true"
@@ -102,7 +102,7 @@
<b-col cols="1" class="justify-content-center align-items-center d-none d-md-flex">
<input type="checkbox" class="form-control form-control-sm checkbox-control" :checked="formatChecked" @change="updateChecked" :key="entries[0].id" />
</b-col>
<b-col cols="8" md="9">
<b-col cols="7" md="9">
<b-row class="d-flex align-items-center h-100">
<b-col cols="5" md="3" class="d-flex align-items-center">
<strong class="mr-1">{{ formatOneAmount(e) }}</strong> {{ formatOneUnit(e) }}

View File

@@ -137,7 +137,7 @@
"Move_Down": "Runter",
"Step_Name": "Schritt Name",
"Create": "Erstellen",
"Advanced Search Settings": "Erweiterte Sucheinstellungen",
"advanced_search_settings": "Erweiterte Sucheinstellungen",
"View": "Ansicht",
"Recipes": "Rezepte",
"Move": "Verschieben",
@@ -249,7 +249,7 @@
"shopping_auto_sync_desc": "Bei 0 wird Auto-Sync deaktiviert. Beim Betrachten einer Einkaufsliste wird die Liste alle gesetzten Sekunden aktualisiert, um mögliche Änderungen anderer zu zeigen. Nützlich, wenn mehrere Personen einkaufen und mobile Daten nutzen.",
"MoveCategory": "Verschieben nach: ",
"mealplan_autoadd_shopping_desc": "Essensplan-Zutaten automatisch zur Einkaufsliste hinzufügen.",
"Pin": "Pin",
"Pin": "Anheften",
"mark_complete": "Vollständig markieren",
"shopping_add_onhand_desc": "Markiere Lebensmittel als \"Vorrätig\", wenn von der Einkaufsliste abgehakt wurden.",
"left_handed": "Linkshänder-Modus",
@@ -298,5 +298,61 @@
"Foods": "Lebensmittel",
"food_recipe_help": "Wird ein Rezept hier verknüpft, wird diese Verknüpfung in allen anderen Rezepten übernommen, die dieses Lebensmittel beinhaltet",
"review_shopping": "Überprüfe die Einkaufsliste vor dem Speichern",
"view_recipe": "Rezept anschauen"
"view_recipe": "Rezept anschauen",
"Planned": "Geplant",
"Pinned": "Angeheftet",
"nothing_planned_today": "Sie haben für heute nichts geplant!",
"no_pinned_recipes": "Sie haben nichts angeheftet!",
"Quick actions": "Schnellbefehle",
"search_no_recipes": "Keine Rezepte gefunden!",
"search_import_help_text": "Importiere ein Rezept von einer externen Webseite oder Anwendung.",
"search_create_help_text": "Erstelle ein neues Rezept direkt in Tandoor.",
"Ratings": "Bewertungen",
"Custom Filter": "Benutzerdefinierter Filter",
"expert_mode": "Experten-Modus",
"simple_mode": "Einfacher Modus",
"explain": "Erklären",
"save_filter": "Filter speichern",
"Internal": "Intern",
"advanced": "Erweitert",
"fields": "Felder",
"show_keywords": "Schlüsselwörter anzeigen",
"show_foods": "Zutaten anzeigen",
"show_books": "Bücher anzeigen",
"show_rating": "Bewertungen anzeigen",
"show_units": "Einheiten anzeigen",
"show_filters": "Filter anzeigen",
"times_cooked": "Wie oft gekocht",
"show_sortby": "Zeige 'Sortiere nach'",
"make_now": "Jetzt machen",
"date_viewed": "Letztens besucht",
"last_cooked": "Letztens gekocht",
"created_on": "Erstellt am",
"updatedon": "Geändert am",
"date_created": "Erstellungsdatum",
"Units": "Einheiten",
"last_viewed": "Letztens besucht",
"sort_by": "Sortiere nach",
"Random Recipes": "Zufällige Rezepte",
"recipe_filter": "Rezept-Filter",
"parameter_count": "Parameter {count}",
"select_keyword": "Stichwort auswählen",
"add_keyword": "Stichwort hinzufügen",
"select_file": "Datei auswählen",
"select_recipe": "Rezept auswählen",
"select_unit": "Einheit wählen",
"select_food": "Zutat auswählen",
"remove_selection": "Abwählen",
"empty_list": "Liste ist leer.",
"Select": "Auswählen",
"Supermarkets": "Supermärkte",
"User": "Benutzer",
"Keyword": "Schlüsselwort",
"Advanced": "Erweitert",
"Substitutes": "Zusätze",
"copy_to_new": "Kopiere zu neuem Rezept",
"Page": "Seite",
"Reset": "Zurücksetzen",
"search_rank": "Such-Rang",
"paste_ingredients": "Zutaten einfügen"
}

View File

@@ -355,5 +355,31 @@
"InheritFields_help": "The values of these fields will be inheritted from parent (Exception: blank shopping categories are not inheritted)",
"last_viewed": "Last Viewed",
"created_on": "Created On",
"updatedon": "Updated On"
"updatedon": "Updated On",
"advanced_search_settings": "Advanced Search Settings",
"nothing_planned_today": "You have nothing planned for today!",
"no_pinned_recipes": "You have no pinned recipes!",
"Planned": "Planned",
"Pinned": "Pinned",
"Quick actions": "Quick actions",
"Ratings": "Ratings",
"Internal": "Internal",
"Units": "Units",
"Random Recipes": "Random Recipes",
"parameter_count": "Parameter {count}",
"select_keyword": "Select Keyword",
"add_keyword": "Add Keyword",
"select_file": "Select File",
"select_recipe": "Select Recipe",
"select_unit": "Select Unit",
"select_food": "Select Food",
"remove_selection": "Deselect",
"empty_list": "List is empty.",
"Select": "Select",
"Supermarkets": "Supermarkets",
"User": "User",
"Keyword": "Keyword",
"Advanced": "Advanced",
"Page": "Page",
"Reset": "Reset"
}

View File

@@ -1,7 +1,6 @@
/*
* Utility CLASS to define model configurations
* */
import i18n from "@/i18n"
// TODO this needs rethought and simplified
// maybe a function that returns a single dictionary based on action?
@@ -51,7 +50,7 @@ export class Models {
type: "lookup",
field: "target",
list: "self",
sticky_options: [{ id: 0, name: i18n.t("tree_root") }],
sticky_options: [{ id: 0, name: "tree_root" }],
},
},
},
@@ -59,7 +58,7 @@ export class Models {
// MODELS - inherits and takes precedence over MODEL_TYPES and ACTIONS
static FOOD = {
name: i18n.t("Food"), // *OPTIONAL* : parameters will be built model -> model_type -> default
name: "Food", // *OPTIONAL* : parameters will be built model -> model_type -> default
apiName: "Food", // *REQUIRED* : the name that is used in api.ts for this model
model_type: this.TREE, // *OPTIONAL* : model specific params for api, if not present will attempt modeltype_create then default_create
paginated: true,
@@ -100,15 +99,15 @@ export class Models {
form_field: true,
type: "text",
field: "name",
label: i18n.t("Name"),
placeholder: "",
label: "Name", // form.label always translated in utils.getForm()
placeholder: "", // form.placeholder always translated
subtitle_field: "full_name",
},
description: {
form_field: true,
type: "text",
field: "description",
label: i18n.t("Description"),
label: "Description", // form.label always translated in utils.getForm()
placeholder: "",
},
recipe: {
@@ -116,31 +115,31 @@ export class Models {
type: "lookup",
field: "recipe",
list: "RECIPE",
label: i18n.t("Recipe"),
help_text: i18n.t("food_recipe_help"),
label: "Recipe", // form.label always translated in utils.getForm()
help_text: "food_recipe_help", // form.help_text always translated
},
onhand: {
form_field: true,
type: "checkbox",
field: "food_onhand",
label: i18n.t("OnHand"),
help_text: i18n.t("OnHand_help"),
label: "OnHand",
help_text: "OnHand_help",
},
ignore_shopping: {
form_field: true,
type: "checkbox",
field: "ignore_shopping",
label: i18n.t("Ignore_Shopping"),
help_text: i18n.t("ignore_shopping_help"),
label: "Ignore_Shopping",
help_text: "ignore_shopping_help",
},
shopping_category: {
form_field: true,
type: "lookup",
field: "supermarket_category",
list: "SHOPPING_CATEGORY",
label: i18n.t("Shopping_Category"),
label: "Shopping_Category",
allow_create: true,
help_text: i18n.t("shopping_category_help"),
help_text: "shopping_category_help", // form.help_text always translated
},
substitute: {
form_field: true,
@@ -149,17 +148,17 @@ export class Models {
multiple: true,
field: "substitute",
list: "FOOD",
label: i18n.t("Substitutes"),
label: "Substitutes",
allow_create: false,
help_text: i18n.t("substitute_help"),
help_text: "substitute_help",
},
substitute_siblings: {
form_field: true,
advanced: true,
type: "checkbox",
field: "substitute_siblings",
label: i18n.t("substitute_siblings"),
help_text: i18n.t("substitute_siblings_help"),
label: "substitute_siblings", // form.label always translated in utils.getForm()
help_text: "substitute_siblings_help", // form.help_text always translated
condition: { field: "parent", value: true, condition: "field_exists" },
},
substitute_children: {
@@ -167,8 +166,8 @@ export class Models {
advanced: true,
type: "checkbox",
field: "substitute_children",
label: i18n.t("substitute_children"),
help_text: i18n.t("substitute_children_help"),
label: "substitute_children",
help_text: "substitute_children_help",
condition: { field: "numchild", value: 0, condition: "gt" },
},
inherit_fields: {
@@ -178,9 +177,9 @@ export class Models {
multiple: true,
field: "inherit_fields",
list: "FOOD_INHERIT_FIELDS",
label: i18n.t("InheritFields"),
label: "InheritFields",
condition: { field: "food_children_exist", value: true, condition: "preference_equals" },
help_text: i18n.t("InheritFields_help"),
help_text: "InheritFields_help",
},
child_inherit_fields: {
form_field: true,
@@ -189,17 +188,17 @@ export class Models {
multiple: true,
field: "child_inherit_fields",
list: "FOOD_INHERIT_FIELDS",
label: i18n.t("ChildInheritFields"),
label: "ChildInheritFields", // form.label always translated in utils.getForm()
condition: { field: "numchild", value: 0, condition: "gt" },
help_text: i18n.t("ChildInheritFields_help"),
help_text: "ChildInheritFields_help", // form.help_text always translated
},
reset_inherit: {
form_field: true,
advanced: true,
type: "checkbox",
field: "reset_inherit",
label: i18n.t("reset_children"),
help_text: i18n.t("reset_children_help"),
label: "reset_children",
help_text: "reset_children_help",
condition: { field: "numchild", value: 0, condition: "gt" },
},
form_function: "FoodCreateDefault",
@@ -215,7 +214,7 @@ export class Models {
}
static KEYWORD = {
name: i18n.t("Keyword"), // *OPTIONAL: parameters will be built model -> model_type -> default
name: "Keyword", // *OPTIONAL: parameters will be built model -> model_type -> default
apiName: "Keyword",
model_type: this.TREE,
paginated: true,
@@ -232,21 +231,21 @@ export class Models {
form_field: true,
type: "text",
field: "name",
label: i18n.t("Name"),
label: "Name",
placeholder: "",
},
description: {
form_field: true,
type: "text",
field: "description",
label: i18n.t("Description"),
label: "Description",
placeholder: "",
},
icon: {
form_field: true,
type: "emoji",
field: "icon",
label: i18n.t("Icon"),
label: "Icon",
},
full_name: {
form_field: true,
@@ -258,7 +257,7 @@ export class Models {
}
static UNIT = {
name: i18n.t("Unit"),
name: "Unit",
apiName: "Unit",
paginated: true,
create: {
@@ -268,14 +267,14 @@ export class Models {
form_field: true,
type: "text",
field: "name",
label: i18n.t("Name"),
label: "Name",
placeholder: "",
},
description: {
form_field: true,
type: "text",
field: "description",
label: i18n.t("Description"),
label: "Description",
placeholder: "",
},
},
@@ -284,7 +283,7 @@ export class Models {
}
static SHOPPING_LIST = {
name: i18n.t("Shopping_list"),
name: "Shopping_list",
apiName: "ShoppingListEntry",
list: {
params: ["id", "checked", "supermarket", "options"],
@@ -297,7 +296,7 @@ export class Models {
type: "lookup",
field: "unit",
list: "UNIT",
label: i18n.t("Unit"),
label: "Unit",
allow_create: true,
},
food: {
@@ -305,7 +304,7 @@ export class Models {
type: "lookup",
field: "food",
list: "FOOD",
label: i18n.t("Food"),
label: "Food", // form.label always translated in utils.getForm()
allow_create: true,
},
},
@@ -313,7 +312,7 @@ export class Models {
}
static RECIPE_BOOK = {
name: i18n.t("Recipe_Book"),
name: "Recipe_Book",
apiName: "RecipeBook",
create: {
params: [["name", "description", "icon", "filter"]],
@@ -322,27 +321,27 @@ export class Models {
form_field: true,
type: "text",
field: "name",
label: i18n.t("Name"),
label: "Name",
placeholder: "",
},
description: {
form_field: true,
type: "text",
field: "description",
label: i18n.t("Description"),
label: "Description",
placeholder: "",
},
icon: {
form_field: true,
type: "emoji",
field: "icon",
label: i18n.t("Icon"),
label: "Icon",
},
filter: {
form_field: true,
type: "lookup",
field: "filter",
label: i18n.t("Custom Filter"),
label: "Custom Filter",
list: "CUSTOM_FILTER",
},
},
@@ -350,7 +349,7 @@ export class Models {
}
static SHOPPING_CATEGORY = {
name: i18n.t("Shopping_Category"),
name: "Shopping_Category",
apiName: "SupermarketCategory",
create: {
params: [["name", "description"]],
@@ -359,14 +358,14 @@ export class Models {
form_field: true,
type: "text",
field: "name",
label: i18n.t("Name"),
label: "Name", // form.label always translated in utils.getForm()
placeholder: "",
},
description: {
form_field: true,
type: "text",
field: "description",
label: i18n.t("Description"),
label: "Description",
placeholder: "",
},
},
@@ -374,7 +373,7 @@ export class Models {
}
static SHOPPING_CATEGORY_RELATION = {
name: i18n.t("Shopping_Category_Relation"),
name: "Shopping_Category_Relation",
apiName: "SupermarketCategoryRelation",
create: {
params: [["category", "supermarket", "order"]],
@@ -383,14 +382,14 @@ export class Models {
form_field: true,
type: "text",
field: "name",
label: i18n.t("Name"),
label: "Name",
placeholder: "",
},
description: {
form_field: true,
type: "text",
field: "description",
label: i18n.t("Description"),
label: "Description",
placeholder: "",
},
},
@@ -398,7 +397,7 @@ export class Models {
}
static SUPERMARKET = {
name: i18n.t("Supermarket"),
name: "Supermarket",
apiName: "Supermarket",
ordered_tags: [{ field: "category_to_supermarket", label: "category::name", color: "info" }],
create: {
@@ -408,14 +407,14 @@ export class Models {
form_field: true,
type: "text",
field: "name",
label: i18n.t("Name"),
label: "Name",
placeholder: "",
},
description: {
form_field: true,
type: "text",
field: "description",
label: i18n.t("Description"),
label: "Description",
placeholder: "",
},
categories: {
@@ -425,7 +424,7 @@ export class Models {
list_label: "category::name",
ordered: true, // ordered lookups assume working with relation field
field: "category_to_supermarket",
label: i18n.t("Categories"),
label: "Categories", // form.label always translated in utils.getForm()
placeholder: "",
},
},
@@ -441,7 +440,7 @@ export class Models {
}
static AUTOMATION = {
name: i18n.t("Automation"),
name: "Automation",
apiName: "Automation",
paginated: true,
list: {
@@ -456,47 +455,74 @@ export class Models {
form_field: true,
type: "text",
field: "name",
label: i18n.t("Name"),
label: "Name",
placeholder: "",
},
description: {
form_field: true,
type: "text",
field: "description",
label: i18n.t("Description"),
label: "Description",
placeholder: "",
},
type: {
form_field: true,
type: "choice",
options: [
{ value: "FOOD_ALIAS", text: i18n.t("Food_Alias") },
{ value: "UNIT_ALIAS", text: i18n.t("Unit_Alias") },
{ value: "KEYWORD_ALIAS", text: i18n.t("Keyword_Alias") },
{ value: "FOOD_ALIAS", text: "Food_Alias" },
{ value: "UNIT_ALIAS", text: "Unit_Alias" },
{ value: "KEYWORD_ALIAS", text: "Keyword_Alias" },
],
field: "type",
label: i18n.t("Type"),
label: "Type",
placeholder: "",
},
param_1: {
form_field: true,
type: "text",
field: "param_1",
label: i18n.t("Parameter") + " 1",
label: {
function: "translate",
phrase: "parameter_count",
params: [
{
token: "count",
attribute: "1",
},
],
},
placeholder: "",
},
param_2: {
form_field: true,
type: "text",
field: "param_2",
label: i18n.t("Parameter") + " 2",
label: {
function: "translate",
phrase: "parameter_count",
params: [
{
token: "count",
attribute: "2",
},
],
},
placeholder: "",
},
param_3: {
form_field: true,
type: "text",
field: "param_3",
label: i18n.t("Parameter") + " 3",
label: {
function: "translate",
phrase: "parameter_count",
params: [
{
token: "count",
attribute: "3",
},
],
},
placeholder: "",
},
},
@@ -504,7 +530,7 @@ export class Models {
}
static RECIPE = {
name: i18n.t("Recipe"),
name: "Recipe",
apiName: "Recipe",
list: {
params: [
@@ -546,7 +572,7 @@ export class Models {
}
static CUSTOM_FILTER = {
name: i18n.t("Custom Filter"),
name: "Custom Filter",
apiName: "CustomFilter",
create: {
@@ -556,7 +582,7 @@ export class Models {
form_field: true,
type: "text",
field: "name",
label: i18n.t("Name"),
label: "Name", // form.label always translated in utils.getForm()
placeholder: "",
},
@@ -566,14 +592,14 @@ export class Models {
field: "shared",
list: "USER",
list_label: "username",
label: i18n.t("shared_with"),
label: "shared_with",
multiple: true,
},
},
},
}
static USER_NAME = {
name: i18n.t("User"),
name: "User",
apiName: "User",
list: {
params: ["filter_list"],
@@ -581,7 +607,7 @@ export class Models {
}
static MEAL_TYPE = {
name: i18n.t("Meal_Type"),
name: "Meal_Type",
apiName: "MealType",
list: {
params: ["filter_list"],
@@ -589,7 +615,7 @@ export class Models {
}
static MEAL_PLAN = {
name: i18n.t("Meal_Plan"),
name: "Meal_Plan",
apiName: "MealPlan",
list: {
params: ["options"],
@@ -597,7 +623,7 @@ export class Models {
}
static USERFILE = {
name: i18n.t("File"),
name: "File",
apiName: "UserFile",
paginated: false,
list: {
@@ -612,27 +638,27 @@ export class Models {
form_field: true,
type: "text",
field: "name",
label: i18n.t("Name"),
label: "Name",
placeholder: "",
},
file: {
form_field: true,
type: "file",
field: "file",
label: i18n.t("File"),
label: "File", // form.label always translated in utils.getForm()
placeholder: "",
},
},
},
}
static USER = {
name: i18n.t("User"),
name: "User",
apiName: "User",
paginated: false,
}
static STEP = {
name: i18n.t("Step"),
name: "Step",
apiName: "Step",
list: {
params: ["recipe", "query", "page", "pageSize", "options"],
@@ -652,10 +678,11 @@ export class Actions {
token: "type",
from: "model",
attribute: "name",
translate: true,
},
],
},
ok_label: i18n.t("Save"),
ok_label: { function: "translate", phrase: "Save" },
},
}
static UPDATE = {
@@ -669,6 +696,7 @@ export class Actions {
token: "type",
from: "model",
attribute: "name",
translate: true,
},
],
},
@@ -685,10 +713,11 @@ export class Actions {
token: "type",
from: "model",
attribute: "name",
translate: true,
},
],
},
ok_label: i18n.t("Delete"),
ok_label: { function: "translate", phrase: "Delete" },
instruction: {
form_field: true,
type: "instruction",
@@ -736,10 +765,11 @@ export class Actions {
token: "type",
from: "model",
attribute: "name",
translate: true,
},
],
},
ok_label: i18n.t("Merge"),
ok_label: { function: "translate", phrase: "Merge" },
instruction: {
form_field: true,
type: "instruction",
@@ -756,6 +786,7 @@ export class Actions {
token: "type",
from: "model",
attribute: "name",
translate: true,
},
],
},
@@ -784,10 +815,11 @@ export class Actions {
token: "type",
from: "model",
attribute: "name",
translate: true,
},
],
},
ok_label: i18n.t("Move"),
ok_label: { function: "translate", phrase: "Move" },
instruction: {
form_field: true,
type: "instruction",
@@ -804,6 +836,7 @@ export class Actions {
token: "type",
from: "model",
attribute: "name",
translate: true,
},
],
},

View File

@@ -349,7 +349,7 @@ export function getConfig(model, action) {
}
let config = {
name: model.name,
name: i18n.t(model.name),
apiName: model.apiName,
}
// spread operator merges dictionaries - last item in list takes precedence
@@ -391,8 +391,11 @@ export function getForm(model, action, item1, item2) {
value = v
}
if (value?.form_field) {
for (const [i, h] of Object.entries(value)) {
// console.log("formfield", i)
}
value["value"] = item1?.[value?.field] ?? undefined
value["help"] = item1?.[value?.help_text_field] ?? value?.help_text ?? undefined
value["help"] = item1?.[value?.help_text_field] ?? formTranslate(value?.help_text) ?? undefined
value["subtitle"] = item1?.[value?.subtitle_field] ?? value?.subtitle ?? undefined
form.fields.push({
...value,
@@ -410,23 +413,31 @@ export function getForm(model, action, item1, item2) {
function formTranslate(translate, model, item1, item2) {
if (typeof translate !== "object") {
return translate
return i18n.t(translate)
}
let phrase = translate.phrase
let options = {}
let obj = undefined
translate?.params.forEach(function (x, index) {
switch (x.from) {
let value = undefined
translate?.params?.forEach(function (x, index) {
switch (x?.from) {
case "item1":
obj = item1
value = item1[x.attribute]
break
case "item2":
obj = item2
value = item2[x.attribute]
break
case "model":
obj = model
value = model[x.attribute]
break
default:
value = x.attribute
}
if (x.translate) {
options[x.token] = i18n.t(value)
} else {
options[x.token] = value
}
options[x.token] = obj[x.attribute]
})
return i18n.t(phrase, options)
}