Squashed commit of shoppinglist_v2

This commit is contained in:
smilerz
2021-12-30 15:33:34 -06:00
parent 67e4c88be7
commit 957c659a62
49 changed files with 701 additions and 1472 deletions

View File

@ -19,8 +19,10 @@
<!-- <span><b-button variant="link" size="sm" class="text-dark shadow-none"><i class="fas fa-chevron-down"></i></b-button></span> -->
<model-menu />
<span>{{ this.this_model.name }}</span>
<span v-if="this_model.name !== 'Step'"
><b-button variant="link" @click="startAction({ action: 'new' })"><i class="fas fa-plus-circle fa-2x"></i></b-button></span
<span v-if="apiName !== 'Step'">
<b-button variant="link" @click="startAction({ action: 'new' })">
<i class="fas fa-plus-circle fa-2x"></i>
</b-button> </span
><!-- TODO add proper field to model config to determine if create should be available or not -->
</h3>
</div>
@ -112,6 +114,9 @@ export default {
// TODO this is not necessarily bad but maybe there are better options to do this
return () => import(/* webpackChunkName: "header-component" */ `@/components/${this.header_component_name}`)
},
apiName() {
return this.this_model?.apiName
},
},
mounted() {
// value is passed from lists.py
@ -291,11 +296,6 @@ export default {
this.refreshCard({ ...food }, this.items_right)
})
},
addOnhand: function (item) {
item.on_hand = true
this.saveThis(item)
},
updateThis: function (item) {
this.refreshThis(item.id)
},

View File

@ -49,13 +49,13 @@
<div class="col-md-6 mt-1">
<label for="id_name"> {{ $t('Preparation') }} {{ $t('Time') }} ({{ $t('min') }})</label>
<input class="form-control" id="id_prep_time" v-model="recipe.working_time">
<input class="form-control" id="id_prep_time" v-model="recipe.working_time" type="number">
<br/>
<label for="id_name"> {{ $t('Waiting') }} {{ $t('Time') }} ({{ $t('min') }})</label>
<input class="form-control" id="id_wait_time" v-model="recipe.waiting_time">
<input class="form-control" id="id_wait_time" v-model="recipe.waiting_time" type="number">
<br/>
<label for="id_name"> {{ $t('Servings') }}</label>
<input class="form-control" id="id_servings" v-model="recipe.servings">
<input class="form-control" id="id_servings" v-model="recipe.servings" type="number">
<br/>
<label for="id_name"> {{ $t('Servings') }} {{ $t('Text') }}</label>
<input class="form-control" id="id_servings_text" v-model="recipe.servings_text" maxlength="32">
@ -343,7 +343,7 @@
</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 }">
<input class="form-control"
<input class="form-control" maxlength="256"
v-model="ingredient.note"
v-bind:placeholder="$t('Note')"
v-on:keydown.tab="event => {if(step.ingredients.indexOf(ingredient) === (step.ingredients.length -1)){event.preventDefault();addIngredient(step)}}">
@ -623,9 +623,10 @@ export default {
this.sortIngredients(s)
}
if (this.recipe.waiting_time === ''){ this.recipe.waiting_time = 0}
if (this.recipe.working_time === ''){ this.recipe.working_time = 0}
if (this.recipe.servings === ''){ this.recipe.servings = 0}
if (this.recipe.waiting_time === '' || isNaN(this.recipe.waiting_time)){ this.recipe.waiting_time = 0}
if (this.recipe.working_time === ''|| isNaN(this.recipe.working_time)){ this.recipe.working_time = 0}
if (this.recipe.servings === ''|| isNaN(this.recipe.servings)){ this.recipe.servings = 0}
apiFactory.updateRecipe(this.recipe_id, this.recipe,
{}).then((response) => {

View File

@ -306,7 +306,7 @@ export default {
this.settings?.search_keywords?.length === 0 &&
this.settings?.search_foods?.length === 0 &&
this.settings?.search_books?.length === 0 &&
this.settings?.pagination_page === 1 &&
// this.settings?.pagination_page === 1 &&
!this.random_search &&
this.settings?.search_ratings === undefined
) {

View File

@ -200,6 +200,9 @@ export default {
ingredient_factor: function () {
return this.servings / this.recipe.servings
},
title() {
return this.recipe?.steps?.map((x) => x?.ingredients).flat()
},
},
data() {
return {
@ -212,9 +215,11 @@ export default {
share_uid: window.SHARE_UID,
}
},
mounted() {
this.loadRecipe(window.RECIPE_ID)
this.$i18n.locale = window.CUSTOM_LOCALE
console.log(this.recipe)
},
methods: {
loadRecipe: function (recipe_id) {

View File

@ -30,22 +30,24 @@
<div class="row">
<div class="col col-md-12">
<div role="tablist">
<div class="row justify-content-md-center w-75" v-if="entrymode">
<div class="col col-md-2">
<!-- add to shopping form -->
<b-row class="row justify-content-md-center" v-if="entrymode">
<b-col cols="12" sm="4" md="2">
<b-form-input min="1" type="number" :description="$t('Amount')" v-model="new_item.amount"></b-form-input>
</div>
<div class="col col-md-3">
</b-col>
<b-col cols="12" sm="8" md="3">
<lookup-input :form="formUnit" :model="Models.UNIT" @change="new_item.unit = $event" :show_label="false" />
</div>
<div class="col col-md-4">
</b-col>
<b-col cols="12" sm="8" md="4">
<lookup-input :form="formFood" :model="Models.FOOD" @change="new_item.food = $event" :show_label="false" />
</div>
<div class="col col-md-1">
</b-col>
<b-col cols="12" sm="4" md="1">
<b-button variant="link" class="px-0">
<i class="btn fas fa-cart-plus fa-lg px-0 text-success" @click="addItem" />
</b-button>
</div>
</div>
</b-col>
</b-row>
<!-- shopping list table -->
<div v-if="items && items.length > 0">
<div v-for="(done, x) in Sections" :key="x">
<div v-if="x == 'true'">
@ -491,15 +493,6 @@
</b-form-group>
</ContextMenuItem>
<ContextMenuItem
@click="
$refs.menu.close()
ignoreThis(contextData)
"
>
<a class="dropdown-item p-2" href="#"><i class="fas fa-ban"></i> {{ $t("IgnoreThis", { food: foodName(contextData) }) }}</a>
</ContextMenuItem>
<ContextMenuItem
@click="
$refs.menu.close()
@ -746,7 +739,7 @@ export default {
} else {
console.log("no data returned")
}
this.new_item = { amount: 1 }
this.new_item = { amount: 1, unit: undefined, food: undefined }
})
.catch((err) => {
console.log(err)
@ -906,13 +899,6 @@ export default {
getThis: function (id) {
return this.genericAPI(this.Models.SHOPPING_CATEGORY, this.Actions.FETCH, { id: id })
},
ignoreThis: function (item) {
let food = {
id: item?.[0]?.food.id ?? item.food.id,
ignore_shopping: true,
}
this.updateFood(food, "ignore_shopping")
},
mergeShoppingList: function (data) {
this.items.map((x) =>
data.map((y) => {
@ -939,10 +925,10 @@ export default {
let api = new ApiApiFactory()
let food = {
id: item?.[0]?.food.id ?? item?.food?.id,
on_hand: true,
food_onhand: true,
}
this.updateFood(food)
this.updateFood(food, "food_onhand")
.then((result) => {
let entries = this.items.filter((x) => x.food.id == food.id).map((x) => x.id)
this.items = this.items.filter((x) => x.food.id !== food.id)
@ -1005,16 +991,18 @@ export default {
// when checking a sub item don't refresh the screen until all entries complete but change class to cross out
let promises = []
update.entries.forEach((x) => {
promises.push(this.saveThis({ id: x, checked: update.checked }, false))
let item = this.items.filter((entry) => entry.id == x)[0]
Vue.set(item, "checked", update.checked)
const id = x?.id ?? x
let completed_at = undefined
if (update.checked) {
Vue.set(item, "completed_at", new Date().toISOString())
} else {
Vue.set(item, "completed_at", undefined)
completed_at = new Date().toISOString()
}
promises.push(this.saveThis({ id: id, checked: update.checked }, false))
let item = this.items.filter((entry) => entry.id == id)[0]
Vue.set(item, "checked", update.checked)
Vue.set(item, "completed_at", completed_at)
})
Promise.all(promises).catch((err) => {
console.log(err, err.response)
StandardToasts.makeStandardToast(StandardToasts.FAIL_UPDATE)
@ -1024,8 +1012,8 @@ export default {
let api = new ApiApiFactory()
let ignore_category
if (field) {
ignore_category = food.ignore_inherit
.map((x) => food.ignore_inherit.fields)
ignore_category = food.inherit_fields
.map((x) => food.inherit_fields.fields)
.flat()
.includes(field)
} else {
@ -1035,7 +1023,7 @@ export default {
return api
.partialUpdateFood(food.id, food)
.then((result) => {
if (food.inherit && food.supermarket_category && !ignore_category && food.parent) {
if (food.supermarket_category && !ignore_category && food.parent) {
makeToast(this.$t("Warning"), this.$t("InheritWarning", { food: food.name }), "warning")
} else {
StandardToasts.makeStandardToast(StandardToasts.SUCCESS_UPDATE)

View File

@ -1,52 +1,44 @@
<template>
<span>
<linked-recipe v-if="linkedRecipe"
:item="item"/>
<icon-badge v-if="Icon"
:item="item"/>
<on-hand-badge v-if="OnHand"
:item="item"/>
<shopping-badge v-if="Shopping"
:item="item"/>
<linked-recipe v-if="linkedRecipe" :item="item" />
<icon-badge v-if="Icon" :item="item" />
<on-hand-badge v-if="OnHand" :item="item" />
<shopping-badge v-if="Shopping" :item="item" />
</span>
</template>
<script>
import LinkedRecipe from "@/components/Badges/LinkedRecipe";
import IconBadge from "@/components/Badges/Icon";
import OnHandBadge from "@/components/Badges/OnHand";
import ShoppingBadge from "@/components/Badges/Shopping";
import LinkedRecipe from "@/components/Badges/LinkedRecipe"
import IconBadge from "@/components/Badges/Icon"
import OnHandBadge from "@/components/Badges/OnHand"
import ShoppingBadge from "@/components/Badges/Shopping"
export default {
name: 'CardBadges',
components: {LinkedRecipe, IconBadge, OnHandBadge, ShoppingBadge},
props: {
item: {type: Object},
model: {type: Object}
},
data() {
return {
}
},
mounted() {
},
computed: {
linkedRecipe: function () {
return this.model?.badges?.linked_recipe ?? false
name: "CardBadges",
components: { LinkedRecipe, IconBadge, OnHandBadge, ShoppingBadge },
props: {
item: { type: Object },
model: { type: Object },
},
Icon: function () {
return this.model?.badges?.icon ?? false
data() {
return {}
},
OnHand: function () {
return this.model?.badges?.on_hand ?? false
mounted() {},
computed: {
linkedRecipe: function () {
return this.model?.badges?.linked_recipe ?? false
},
Icon: function () {
return this.model?.badges?.icon ?? false
},
OnHand: function () {
return this.model?.badges?.food_onhand ?? false
},
Shopping: function () {
return this.model?.badges?.shopping ?? false
},
},
Shopping: function () {
return this.model?.badges?.shopping ?? false
}
},
watch: {
},
methods: {
}
watch: {},
methods: {},
}
</script>
</script>

View File

@ -1,7 +1,7 @@
<template>
<span>
<b-button
class="btn text-decoration-none fas px-1 py-0 border-0"
class="btn text-decoration-none fas px-1 py-0 border-0"
variant="link"
v-b-popover.hover.html
:title="[onhand ? $t('FoodOnHand', { food: item.name }) : $t('FoodNotOnHand', { food: item.name })]"
@ -26,16 +26,16 @@ export default {
}
},
mounted() {
this.onhand = this.item.on_hand
this.onhand = this.item.food_onhand
},
watch: {
"item.on_hand": function(newVal, oldVal) {
"item.food_onhand": function (newVal, oldVal) {
this.onhand = newVal
},
},
methods: {
toggleOnHand() {
let params = { id: this.item.id, on_hand: !this.onhand }
let params = { id: this.item.id, food_onhand: !this.onhand }
this.genericAPI(this.Models.FOOD, this.Actions.UPDATE, params).then(() => {
this.onhand = !this.onhand
})

View File

@ -1,6 +1,6 @@
<template>
<span>
<b-button class="btn text-decoration-none px-1 border-0" variant="link" v-if="ShowBadge" :id="`shopping${item.id}`" @click="addShopping()">
<b-button class="btn text-decoration-none px-1 border-0" variant="link" :id="`shopping${item.id}`" @click="addShopping()">
<i
class="fas"
v-b-popover.hover.html
@ -8,13 +8,13 @@
:class="[shopping ? 'text-success fa-shopping-cart' : 'text-muted fa-cart-plus']"
/>
</b-button>
<b-popover :target="`${ShowConfirmation}`" :ref="'shopping' + item.id" triggers="focus" placement="top">
<b-popover v-if="shopping" :target="`${ShowConfirmation}`" :ref="'shopping' + item.id" triggers="focus" placement="top">
<template #title>{{ DeleteConfirmation }}</template>
<b-row align-h="end">
<b-col cols="auto"
><b-button class="btn btn-sm btn-info shadow-none px-1 border-0" @click="cancelDelete()">{{ $t("Cancel") }}</b-button>
<b-button class="btn btn-sm btn-danger shadow-none px-1" @click="confirmDelete()">{{ $t("Confirm") }}</b-button></b-col
>
<b-col cols="auto">
<b-button class="btn btn-sm btn-info shadow-none px-1 border-0" @click="cancelDelete()">{{ $t("Cancel") }}</b-button>
<b-button class="btn btn-sm btn-danger shadow-none px-1" @click="confirmDelete()">{{ $t("Confirm") }}</b-button>
</b-col>
</b-row>
</b-popover>
</span>
@ -27,7 +27,6 @@ export default {
name: "ShoppingBadge",
props: {
item: { type: Object },
override_ignore: { type: Boolean, default: false },
},
mixins: [ApiMixin],
data() {
@ -40,13 +39,6 @@ export default {
this.shopping = this.item?.shopping //?? random[Math.floor(Math.random() * random.length)]
},
computed: {
ShowBadge() {
if (this.override_ignore) {
return true
} else {
return !this.item.ignore_shopping
}
},
DeleteConfirmation() {
return this.$t("DeleteShoppingConfirm", { food: this.item.name })
},
@ -54,12 +46,12 @@ export default {
if (this.shopping) {
return "shopping" + this.item.id
} else {
return "NoDialog"
return ""
}
},
},
watch: {
"item.shopping": function(newVal, oldVal) {
"item.shopping": function (newVal, oldVal) {
this.shopping = newVal
},
},

View File

@ -1,74 +1,52 @@
<template>
<!-- <b-button variant="link" size="sm" class="text-dark shadow-none"><i class="fas fa-chevron-down"></i></b-button> -->
<span>
<b-dropdown variant="link" toggle-class="text-decoration-none text-dark shadow-none" no-caret
style="boundary:window">
<template #button-content>
<i class="fas fa-chevron-down"></i>
</template>
<b-dropdown-item :href="resolveDjangoUrl('list_food')">
<i class="fas fa-leaf fa-fw"></i> {{ Models['FOOD'].name }}
</b-dropdown-item>
<b-dropdown-item :href="resolveDjangoUrl('list_keyword')">
<i class="fas fa-tags fa-fw"></i> {{ Models['KEYWORD'].name }}
</b-dropdown-item>
<!-- <b-button variant="link" size="sm" class="text-dark shadow-none"><i class="fas fa-chevron-down"></i></b-button> -->
<span>
<b-dropdown variant="link" toggle-class="text-decoration-none text-dark shadow-none" no-caret style="boundary: window">
<template #button-content>
<i class="fas fa-chevron-down"></i>
</template>
<b-dropdown-item :href="resolveDjangoUrl('list_food')"> <i class="fas fa-leaf fa-fw"></i> {{ Models["FOOD"].name }} </b-dropdown-item>
<b-dropdown-item :href="resolveDjangoUrl('list_unit')">
<i class="fas fa-balance-scale fa-fw"></i> {{ Models['UNIT'].name }}
</b-dropdown-item>
<b-dropdown-item :href="resolveDjangoUrl('list_supermarket')">
<i class="fas fa-store-alt fa-fw"></i> {{ Models['SUPERMARKET'].name }}
</b-dropdown-item>
<b-dropdown-item :href="resolveDjangoUrl('list_keyword')"> <i class="fas fa-tags fa-fw"></i> {{ Models["KEYWORD"].name }} </b-dropdown-item>
<b-dropdown-item :href="resolveDjangoUrl('list_supermarket_category')">
<i class="fas fa-cubes fa-fw"></i> {{ Models['SHOPPING_CATEGORY'].name }}
</b-dropdown-item>
<b-dropdown-item :href="resolveDjangoUrl('list_unit')"> <i class="fas fa-balance-scale fa-fw"></i> {{ Models["UNIT"].name }} </b-dropdown-item>
<b-dropdown-item :href="resolveDjangoUrl('list_automation')">
<i class="fas fa-robot fa-fw"></i> {{ Models['AUTOMATION'].name }}
</b-dropdown-item>
<b-dropdown-item :href="resolveDjangoUrl('list_supermarket')"> <i class="fas fa-store-alt fa-fw"></i> {{ Models["SUPERMARKET"].name }} </b-dropdown-item>
<b-dropdown-item :href="resolveDjangoUrl('list_user_file')">
<i class="fas fa-file fa-fw"></i> {{ Models['USERFILE'].name }}
</b-dropdown-item>
<b-dropdown-item :href="resolveDjangoUrl('list_supermarket_category')"> <i class="fas fa-cubes fa-fw"></i> {{ Models["SHOPPING_CATEGORY"].name }} </b-dropdown-item>
<b-dropdown-item :href="resolveDjangoUrl('list_step')">
<i class="fas fa-puzzle-piece fa-fw"></i>{{ Models['STEP'].name }}
</b-dropdown-item>
<b-dropdown-item :href="resolveDjangoUrl('list_automation')"> <i class="fas fa-robot fa-fw"></i> {{ Models["AUTOMATION"].name }} </b-dropdown-item>
<b-dropdown-item :href="resolveDjangoUrl('list_user_file')"> <i class="fas fa-file fa-fw"></i> {{ Models["USERFILE"].name }} </b-dropdown-item>
<b-dropdown-item :href="resolveDjangoUrl('list_step')"> <i class="fas fa-puzzle-piece fa-fw"></i>{{ Models["STEP"].name }} </b-dropdown-item>
</b-dropdown>
</span>
</template>
<script>
import Vue from "vue"
import { BootstrapVue } from "bootstrap-vue"
import "bootstrap-vue/dist/bootstrap-vue.css"
import Vue from 'vue'
import {BootstrapVue} from 'bootstrap-vue'
import 'bootstrap-vue/dist/bootstrap-vue.css'
import {Models} from "@/utils/models";
import {ResolveUrlMixin} from "@/utils/utils";
import { Models } from "@/utils/models"
import { ResolveUrlMixin } from "@/utils/utils"
Vue.use(BootstrapVue)
export default {
name: 'ModelMenu',
mixins: [ResolveUrlMixin],
data() {
return {
Models: Models
}
},
mounted() {
},
methods: {
gotoURL: function (model) {
return
}
}
name: "ModelMenu",
mixins: [ResolveUrlMixin],
data() {
return {
Models: Models,
}
},
mounted() {},
methods: {
gotoURL: function (model) {
return
},
},
}
</script>
</script>

View File

@ -17,13 +17,14 @@
>
<b-row no-gutters>
<b-col no-gutters class="col-sm-3">
<b-card-img-lazy style="object-fit: cover; height: 6em;" :src="item_image" v-bind:alt="$t('Recipe_Image')"></b-card-img-lazy>
<b-card-img-lazy style="object-fit: cover; height: 6em" :src="item_image" v-bind:alt="$t('Recipe_Image')"></b-card-img-lazy>
</b-col>
<b-col no-gutters class="col-sm-9">
<b-card-body class="m-0 py-0">
<b-card-text class=" h-100 my-0 d-flex flex-column" style="text-overflow: ellipsis">
<b-card-text class="h-100 my-0 d-flex flex-column" style="text-overflow: ellipsis">
<h5 class="m-0 mt-1 text-truncate">{{ item[title] }}</h5>
<div class="m-0 text-truncate">{{ item[subtitle] }}</div>
<div class="m-0 text-truncate small text-muted" v-if="getFullname">{{ getFullname }}</div>
<generic-pill v-for="x in itemTags" :key="x.field" :item_list="item[x.field]" :label="x.label" :color="x.color" />
<generic-ordered-pill
@ -37,21 +38,11 @@
@finish-action="finishAction"
/>
<div class="mt-auto mb-1" align="right">
<span
v-if="item[child_count]"
class="mx-2 btn btn-link btn-sm"
style="z-index: 800;"
v-on:click="$emit('item-action', { action: 'get-children', source: item })"
>
<span v-if="item[child_count]" class="mx-2 btn btn-link btn-sm" style="z-index: 800" v-on:click="$emit('item-action', { action: 'get-children', source: item })">
<div v-if="!item.show_children">{{ item[child_count] }} {{ itemName }}</div>
<div v-else>{{ text.hide_children }}</div>
</span>
<span
v-if="item[recipe_count]"
class="mx-2 btn btn-link btn-sm"
style="z-index: 800;"
v-on:click="$emit('item-action', { action: 'get-recipes', source: item })"
>
<span v-if="item[recipe_count]" class="mx-2 btn btn-link btn-sm" style="z-index: 800" v-on:click="$emit('item-action', { action: 'get-recipes', source: item })">
<div v-if="!item.show_recipes">{{ item[recipe_count] }} {{ $t("Recipes") }}</div>
<div v-else>{{ $t("Hide_Recipes") }}</div>
</span>
@ -77,20 +68,19 @@
<!-- recursively add child cards -->
<div class="row" v-if="item.show_children">
<div class="col-md-10 offset-md-2">
<generic-horizontal-card v-for="child in item[children]" v-bind:key="child.id" :item="child" :model="model" @item-action="$emit('item-action', $event)">
</generic-horizontal-card>
<generic-horizontal-card v-for="child in item[children]" v-bind:key="child.id" :item="child" :model="model" @item-action="$emit('item-action', $event)"> </generic-horizontal-card>
</div>
</div>
<!-- conditionally view recipes -->
<div class="row" v-if="item.show_recipes">
<div class="col-md-10 offset-md-2">
<div style="display: grid; grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));grid-gap: 1rem;">
<div style="display: grid; grid-template-columns: repeat(auto-fit, minmax(200px, 1fr)); grid-gap: 1rem">
<recipe-card v-for="r in item[recipes]" v-bind:key="r.id" :recipe="r"> </recipe-card>
</div>
</div>
</div>
<!-- this should be made a generic component, would also require mixin for functions that generate the popup and put in parent container-->
<b-list-group ref="tooltip" variant="light" v-show="show_menu" v-on-clickaway="closeMenu" style="z-index:9999; cursor:pointer">
<b-list-group ref="tooltip" variant="light" v-show="show_menu" v-on-clickaway="closeMenu" style="z-index: 9999; cursor: pointer">
<b-list-group-item
v-if="useMove"
action
@ -176,47 +166,53 @@ export default {
this.text.hide_children = this.$t("Hide_" + this.itemName)
},
computed: {
itemName: function() {
itemName: function () {
return this.model?.name ?? "You Forgot To Set Model Name in model.js"
},
useMove: function() {
useMove: function () {
return this.model?.["move"] ?? false ? true : false
},
useMerge: function() {
useMerge: function () {
return this.model?.["merge"] ?? false ? true : false
},
useShopping: function() {
useShopping: function () {
return this.model?.["shop"] ?? false ? true : false
},
useOnhand: function() {
useOnhand: function () {
return this.model?.["onhand"] ?? false ? true : false
},
useDrag: function() {
useDrag: function () {
return this.useMove || this.useMerge
},
itemTags: function() {
itemTags: function () {
return this.model?.tags ?? []
},
itemOrderedTags: function() {
itemOrderedTags: function () {
return this.model?.ordered_tags ?? []
},
getFullname: function () {
if (!this.item?.full_name?.includes(">")) {
return undefined
}
return this.item?.full_name
},
},
methods: {
handleDragStart: function(e) {
handleDragStart: function (e) {
this.isError = false
e.dataTransfer.setData("source", JSON.stringify(this.item))
},
handleDragEnter: function(e) {
handleDragEnter: function (e) {
if (!e.currentTarget.contains(e.relatedTarget) && e.relatedTarget != null) {
this.over = true
}
},
handleDragLeave: function(e) {
handleDragLeave: function (e) {
if (!e.currentTarget.contains(e.relatedTarget)) {
this.over = false
}
},
handleDragDrop: function(e) {
handleDragDrop: function (e) {
let source = JSON.parse(e.dataTransfer.getData("source"))
if (source.id != this.item.id) {
this.source = source
@ -247,7 +243,7 @@ export default {
this.isError = true
}
},
generateLocation: function(x = 0, y = 0) {
generateLocation: function (x = 0, y = 0) {
return () => ({
width: 0,
height: 0,
@ -257,10 +253,10 @@ export default {
left: x,
})
},
closeMenu: function() {
closeMenu: function () {
this.show_menu = false
},
finishAction: function(e) {
finishAction: function (e) {
this.$emit("finish-action", e)
},
},

View File

@ -10,12 +10,7 @@
export default {
name: "GenericPill",
props: {
item_list: {
type: Array,
default() {
return []
},
},
item_list: { type: Object },
label: { type: String, default: "name" },
color: { type: String, default: "light" },
},

View File

@ -33,31 +33,20 @@
</div>
</td>
<td v-else-if="show_shopping" class="text-right text-nowrap">
<!-- in shopping mode and ingredient is not ignored -->
<div v-if="!ingredient.food.ignore_shopping">
<b-button
class="btn text-decoration-none fas fa-shopping-cart px-2 user-select-none"
variant="link"
v-b-popover.hover.click.blur.html.top="{ title: ShoppingPopover, variant: 'outline-dark' }"
:class="{
'text-success': shopping_status === true,
'text-muted': shopping_status === false,
'text-warning': shopping_status === null,
}"
/>
<span class="px-2">
<input type="checkbox" class="align-middle" v-model="shop" @change="changeShopping" />
</span>
<on-hand-badge :item="ingredient.food" />
</div>
<div v-else>
<!-- or in shopping mode and food is ignored: Shopping Badge bypasses linking ingredient to Recipe which would get ignored -->
<shopping-badge :item="ingredient.food" :override_ignore="true" class="px-1" />
<span class="px-2">
<input type="checkbox" class="align-middle" disabled v-b-popover.hover.click.blur :title="$t('IgnoredFood', { food: ingredient.food.name })" />
</span>
<on-hand-badge :item="ingredient.food" />
</div>
<b-button
class="btn text-decoration-none fas fa-shopping-cart px-2 user-select-none"
variant="link"
v-b-popover.hover.click.blur.html.top="{ title: ShoppingPopover, variant: 'outline-dark' }"
:class="{
'text-success': shopping_status === true,
'text-muted': shopping_status === false,
'text-warning': shopping_status === null,
}"
/>
<span class="px-2">
<input type="checkbox" class="align-middle" v-model="shop" @change="changeShopping" />
</span>
<on-hand-badge :item="ingredient.food" />
</td>
</template>
</tr>
@ -66,11 +55,10 @@
<script>
import { calculateAmount, ResolveUrlMixin, ApiMixin } from "@/utils/utils"
import OnHandBadge from "@/components/Badges/OnHand"
import ShoppingBadge from "@/components/Badges/Shopping"
export default {
name: "IngredientComponent",
components: { OnHandBadge, ShoppingBadge },
components: { OnHandBadge },
props: {
ingredient: Object,
ingredient_factor: { type: Number, default: 1 },
@ -89,9 +77,9 @@ export default {
data() {
return {
checked: false,
shopping_status: null,
shopping_status: null, // in any shopping list: boolean + null=in shopping list, but not for this recipe
shopping_items: [],
shop: false,
shop: false, // in shopping list for this recipe: boolean
dirty: undefined,
}
},
@ -99,6 +87,13 @@ export default {
ShoppingListAndFilter: {
immediate: true,
handler(newVal, oldVal) {
// this whole sections is overly complicated
// trying to infer status of shopping for THIS recipe and THIS ingredient
// without know which recipe it is.
// If refactored:
// ## Needs to handle same recipe (multiple mealplans) being in shopping list multiple times
// ## Needs to handle same recipe being added as ShoppingListRecipe AND ingredients added from recipe as one-off
let filtered_list = this.shopping_list
// if a recipe list is provided, filter the shopping list
if (this.recipe_list) {
@ -108,34 +103,39 @@ export default {
let count_shopping_recipes = [...new Set(filtered_list.map((x) => x.list_recipe))].length
let count_shopping_ingredient = filtered_list.filter((x) => x.ingredient == this.ingredient.id).length
if (count_shopping_recipes > 1) {
if (count_shopping_recipes >= 1) {
// This recipe is in the shopping list
this.shop = false // don't check any boxes until user selects a shopping list to edit
if (count_shopping_ingredient >= 1) {
this.shopping_status = true
this.shopping_status = true // ingredient is in the shopping list - probably (but not definitely, this ingredient)
} else if (this.ingredient.food.shopping) {
this.shopping_status = null // food is in the shopping list, just not for this ingredient/recipe
} else {
this.shopping_status = false // food is not in any shopping list
// food is not in any shopping list
this.shopping_status = false
}
} else {
// there are not recipes in the shopping list
// set default value
this.shop = !this.ingredient?.food?.food_onhand && !this.ingredient?.food?.recipe
this.$emit("add-to-shopping", { item: this.ingredient, add: this.shop })
// mark checked if the food is in the shopping list for this ingredient/recipe
if (count_shopping_ingredient >= 1) {
// ingredient is in this shopping list
this.shop = true
// ingredient is in this shopping list (not entirely sure how this could happen?)
this.shopping_status = true
} else if (count_shopping_ingredient == 0 && this.ingredient.food.shopping) {
// food is in the shopping list, just not for this ingredient/recipe
this.shop = false
this.shopping_status = null
} else {
// the food is not in any shopping list
this.shop = false
this.shopping_status = false
}
}
// if we are in add shopping mode start with all checks marked
if (this.add_shopping_mode) {
this.shop = !this.ingredient.food.on_hand && !this.ingredient.food.ignore_shopping && !this.ingredient.food.recipe
// if we are in add shopping mode (e.g. recipe_shopping_modal) start with all checks marked
// except if on_hand (could be if recipe too?)
this.shop = !this.ingredient?.food?.food_onhand && !this.ingredient?.food?.recipe
}
},
},

View File

@ -1,19 +1,18 @@
<template>
<div>
<b-modal :id="'modal_' + id" @hidden="cancelAction">
<template v-slot:modal-title
><h4>{{ form.title }}</h4></template
>
<template v-slot:modal-title>
<h4>{{ form.title }}</h4>
</template>
<div v-for="(f, i) in form.fields" v-bind:key="i">
<p v-if="f.type == 'instruction'">{{ f.label }}</p>
<!-- this lookup is single selection -->
<lookup-input v-if="f.type == 'lookup'" :form="f" :model="listModel(f.list)" @change="storeValue" />
<!-- TODO: add multi-selection input list -->
<checkbox-input v-if="f.type == 'checkbox'" :label="f.label" :value="f.value" :field="f.field" />
<text-input v-if="f.type == 'text'" :label="f.label" :value="f.value" :field="f.field" :placeholder="f.placeholder" />
<choice-input v-if="f.type == 'choice'" :label="f.label" :value="f.value" :field="f.field" :options="f.options" :placeholder="f.placeholder" />
<emoji-input v-if="f.type == 'emoji'" :label="f.label" :value="f.value" :field="f.field" @change="storeValue" />
<file-input v-if="f.type == 'file'" :label="f.label" :value="f.value" :field="f.field" @change="storeValue" />
<p v-if="visibleCondition(f, 'instruction')">{{ f.label }}</p>
<lookup-input v-if="visibleCondition(f, 'lookup')" :form="f" :model="listModel(f.list)" @change="storeValue" />
<checkbox-input class="mb-3" v-if="visibleCondition(f, 'checkbox')" :label="f.label" :value="f.value" :field="f.field" />
<text-input v-if="visibleCondition(f, 'text')" :label="f.label" :value="f.value" :field="f.field" :placeholder="f.placeholder" />
<choice-input v-if="visibleCondition(f, 'choice')" :label="f.label" :value="f.value" :field="f.field" :options="f.options" :placeholder="f.placeholder" />
<emoji-input v-if="visibleCondition(f, 'emoji')" :label="f.label" :value="f.value" :field="f.field" @change="storeValue" />
<file-input v-if="visibleCondition(f, 'file')" :label="f.label" :value="f.value" :field="f.field" @change="storeValue" />
<small-text v-if="visibleCondition(f, 'smalltext')" :value="f.value" />
</div>
<template v-slot:modal-footer>
@ -39,14 +38,20 @@ import TextInput from "@/components/Modals/TextInput"
import EmojiInput from "@/components/Modals/EmojiInput"
import ChoiceInput from "@/components/Modals/ChoiceInput"
import FileInput from "@/components/Modals/FileInput"
import SmallText from "@/components/Modals/SmallText"
export default {
name: "GenericModalForm",
components: { FileInput, CheckboxInput, LookupInput, TextInput, EmojiInput, ChoiceInput },
components: { FileInput, CheckboxInput, LookupInput, TextInput, EmojiInput, ChoiceInput, SmallText },
mixins: [ApiMixin, ToastMixin],
props: {
model: { required: true, type: Object },
action: { type: Object },
action: {
type: Object,
default() {
return {}
},
},
item1: {
type: Object,
default() {
@ -247,6 +252,21 @@ export default {
apiClient.createAutomation(automation)
}
},
visibleCondition(field, field_type) {
let type_match = field?.type == field_type
let checks = true
if (type_match && field?.condition) {
if (field.condition?.condition === "exists") {
if ((this.item1[field.condition.field] != undefined) === field.condition.value) {
checks = true
} else {
checks = false
}
}
}
return type_match && checks
},
},
}
</script>

View File

@ -106,7 +106,7 @@ export default {
...this.steps
.map((x) => x.ingredients)
.flat()
.filter((x) => !x?.food?.on_hand && !x?.food?.ignore_shopping)
.filter((x) => !x?.food?.food_onhand)
.map((x) => x.id),
]
this.recipe_servings = result.data?.servings
@ -141,7 +141,7 @@ export default {
.flat()
.map((x) => x.ingredients)
.flat()
.filter((x) => !x.food.on_hand && !x.food.ignore_shopping)
.filter((x) => !x.food.override_ignore)
.map((x) => x.id),
]
})

View File

@ -0,0 +1,20 @@
<template>
<div class="small text-muted">
{{ value }}
</div>
</template>
<script>
export default {
name: "TextInput",
props: {
value: { type: String, default: "" },
},
data() {
return {}
},
mounted() {},
watch: {},
methods: {},
}
</script>

View File

@ -1,42 +1,34 @@
<template>
<div>
<b-form-group
v-bind:label="label"
class="mb-3">
<b-form-input
v-model="new_value"
type="string"
:placeholder="placeholder"
></b-form-input>
<b-form-group v-bind:label="label" class="mb-3">
<b-form-input v-model="new_value" type="text" :placeholder="placeholder"></b-form-input>
</b-form-group>
</div>
</template>
<script>
export default {
name: 'TextInput',
props: {
field: {type: String, default: 'You Forgot To Set Field Name'},
label: {type: String, default: 'Text Field'},
value: {type: String, default: ''},
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: "TextInput",
props: {
field: { type: String, default: "You Forgot To Set Field Name" },
label: { type: String, default: "Text Field" },
value: { type: String, default: "" },
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)
},
},
methods: {},
}
</script>
</script>

View File

@ -47,7 +47,7 @@
<!-- detail rows -->
<div class="card no-body" v-if="showDetails">
<b-container fluid>
<div v-for="(e, z) in entries" :key="z">
<div v-for="e in entries" :key="e.id">
<b-row class="ml-2 small">
<b-col cols="6" md="4" class="overflow-hidden text-nowrap">
<button
@ -63,7 +63,10 @@
</button>
</b-col>
<b-col cols="6" md="4" class="col-md-4 text-muted">{{ formatOneMealPlan(e) }}</b-col>
<b-col cols="12" md="4" class="col-md-4 text-muted text-right overflow-hidden text-nowrap">{{ formatOneCreatedBy(e) }}</b-col>
<b-col cols="12" md="4" class="col-md-4 text-muted text-right overflow-hidden text-nowrap">
{{ formatOneCreatedBy(e) }}
<div v-if="formatOneCompletedAt(e)">{{ formatOneCompletedAt(e) }}</div>
</b-col>
</b-row>
<b-row class="ml-2 light">
@ -240,9 +243,6 @@ export default {
formatOneFood: function (item) {
return item.food.name
},
formatOneChecked: function (item) {
return item.checked
},
formatOneDelayUntil: function (item) {
if (!item.delay_until || (item.delay_until && item.checked)) {
return false
@ -273,12 +273,13 @@ export default {
})
},
updateChecked: function (e, item) {
let update = undefined
if (!item) {
let update = { entries: this.entries.map((x) => x.id), checked: !this.formatChecked }
this.$emit("update-checkbox", update)
update = { entries: this.entries.map((x) => x.id), checked: !this.formatChecked }
} else {
this.$emit("update-checkbox", { id: item.id, checked: !item.checked })
update = { entries: [item], checked: !item.checked }
}
this.$emit("update-checkbox", update)
},
},
}

View File

@ -11,11 +11,7 @@
<small style="margin-left: 4px" class="text-muted" v-if="step.time !== 0"><i class="fas fa-user-clock"></i> {{ step.time }} {{ $t("min") }} </small>
<small v-if="start_time !== ''" class="d-print-none">
<b-link :id="`id_reactive_popover_${step.id}`" @click="openPopover" href="#">
{{
moment(start_time)
.add(step.time_offset, "minutes")
.format("HH:mm")
}}
{{ moment(start_time).add(step.time_offset, "minutes").format("HH:mm") }}
</b-link>
</small>
</h5>
@ -57,11 +53,7 @@
</h4>
<span style="margin-left: 4px" class="text-muted" v-if="step.time !== 0"><i class="fa fa-stopwatch"></i> {{ step.time }} {{ $t("min") }}</span>
<b-link class="d-print-none" :id="`id_reactive_popover_${step.id}`" @click="openPopover" href="#" v-if="start_time !== ''">
{{
moment(start_time)
.add(step.time_offset, "minutes")
.format("HH:mm")
}}
{{ moment(start_time).add(step.time_offset, "minutes").format("HH:mm") }}
</b-link>
</div>
@ -106,14 +98,14 @@
<a :href="resolveDjangoUrl('view_recipe', step.step_recipe_data.id)">{{ step.step_recipe_data.name }}</a>
</h2>
<div v-for="(sub_step, index) in step.step_recipe_data.steps" v-bind:key="`substep_${sub_step.id}`">
<Step
<step-component
:recipe="step.step_recipe_data"
:step="sub_step"
:ingredient_factor="ingredient_factor"
:index="index"
:start_time="start_time"
:force_ingredients="true"
></Step>
></step-component>
</div>
</div>
</b-collapse>
@ -128,8 +120,8 @@
</div>
<div class="row" style="margin-top: 1vh">
<div class="col-12" style="text-align: right">
<b-button @click="closePopover" size="sm" variant="secondary" style="margin-right:8px">Cancel</b-button>
<b-button @click="updateTime" size="sm" variant="primary">Ok</b-button>
<b-button @click="closePopover" size="sm" variant="secondary" style="margin-right: 8px">{{ $t("Cancel") }}</b-button>
<b-button @click="updateTime" size="sm" variant="primary">{{ $t("Ok") }}</b-button>
</div>
</div>
</b-popover>
@ -172,16 +164,14 @@ export default {
}
},
mounted() {
this.set_time_input = moment(this.start_time)
.add(this.step.time_offset, "minutes")
.format("yyyy-MM-DDTHH:mm")
this.set_time_input = moment(this.start_time).add(this.step.time_offset, "minutes").format("yyyy-MM-DDTHH:mm")
},
methods: {
calculateAmount: function(x) {
calculateAmount: function (x) {
// used by the jinja2 template
return calculateAmount(x, this.ingredient_factor)
},
updateTime: function() {
updateTime: function () {
let new_start_time = moment(this.set_time_input)
.add(this.step.time_offset * -1, "minutes")
.format("yyyy-MM-DDTHH:mm")
@ -189,10 +179,10 @@ export default {
this.$emit("update-start-time", new_start_time)
this.closePopover()
},
closePopover: function() {
closePopover: function () {
this.$refs[`id_reactive_popover_${this.step.id}`].$emit("close")
},
openPopover: function() {
openPopover: function () {
this.$refs[`id_reactive_popover_${this.step.id}`].$emit("open")
},
},

View File

@ -179,7 +179,7 @@
"AddToShopping": "Add to shopping list",
"IngredientInShopping": "This ingredient is in your shopping list.",
"NotInShopping": "{food} is not in your shopping list.",
"OnHand": "Have On Hand",
"OnHand": "Currently On Hand",
"FoodOnHand": "You have {food} on hand.",
"FoodNotOnHand": "You do not have {food} on hand.",
"Undefined": "Undefined",
@ -222,7 +222,7 @@
"Next_Day": "Next Day",
"Previous_Day": "Previous Day",
"Inherit": "Inherit",
"IgnoreInherit": "Do Not Inherit Fields",
"InheritFields": "Inherit Fields Values",
"FoodInherit": "Food Inheritable Fields",
"ShowUncategorizedFood": "Show Undefined",
"GroupBy": "Group By",
@ -240,13 +240,13 @@
"shopping_share": "Share Shopping List",
"shopping_auto_sync": "Autosync",
"mealplan_autoadd_shopping": "Auto Add Meal Plan",
"mealplan_autoexclude_onhand": "Exclude On Hand",
"mealplan_autoexclude_onhand": "Exclude Food On Hand",
"mealplan_autoinclude_related": "Add Related Recipes",
"default_delay": "Default Delay Hours",
"shopping_share_desc": "Users will see all items you add to your shopping list. They must add you to see items on their list.",
"shopping_auto_sync_desc": "Setting to 0 will disable auto sync. When viewing a shopping list the list is updated every set seconds to sync changes someone else might have made. Useful when shopping with multiple people but will use mobile data.",
"mealplan_autoadd_shopping_desc": "Automatically add meal plan ingredients to shopping list.",
"mealplan_autoexclude_onhand_desc": "When adding a meal plan to the shopping list (manually or automatically), exclude ingredients that are on hand.",
"mealplan_autoexclude_onhand_desc": "When adding a meal plan to the shopping list (manually or automatically), exclude ingredients that are currently on hand.",
"mealplan_autoinclude_related_desc": "When adding a meal plan to the shopping list (manually or automatically), include all related recipes.",
"default_delay_desc": "Default number of hours to delay a shopping list entry.",
"filter_to_supermarket": "Filter to Supermarket",

View File

@ -69,14 +69,14 @@ export class Models {
onhand: true,
badges: {
linked_recipe: true,
on_hand: true,
food_onhand: true,
shopping: true,
},
tags: [{ field: "supermarket_category", label: "name", color: "info" }],
// REQUIRED: unordered array of fields that can be set during create
create: {
// if not defined partialUpdate will use the same parameters, prepending 'id'
params: [["name", "description", "recipe", "ignore_shopping", "supermarket_category", "on_hand", "inherit", "ignore_inherit"]],
params: [["name", "description", "recipe", "food_onhand", "supermarket_category", "inherit", "inherit_fields"]],
form: {
name: {
@ -103,13 +103,7 @@ export class Models {
shopping: {
form_field: true,
type: "checkbox",
field: "ignore_shopping",
label: i18n.t("Ignore_Shopping"),
},
onhand: {
form_field: true,
type: "checkbox",
field: "on_hand",
field: "food_onhand",
label: i18n.t("OnHand"),
},
shopping_category: {
@ -120,19 +114,19 @@ export class Models {
label: i18n.t("Shopping_Category"),
allow_create: true,
},
inherit: {
form_field: true,
type: "checkbox",
field: "inherit",
label: i18n.t("Inherit"),
},
ignore_inherit: {
inherit_fields: {
form_field: true,
type: "lookup",
multiple: true,
field: "ignore_inherit",
field: "inherit_fields",
list: "FOOD_INHERIT_FIELDS",
label: i18n.t("IgnoreInherit"),
label: i18n.t("InheritFields"),
condition: { field: "parent", value: true, condition: "exists" },
},
full_name: {
form_field: true,
type: "smalltext",
field: "full_name",
},
form_function: "FoodCreateDefault",
},
@ -180,6 +174,11 @@ export class Models {
field: "icon",
label: i18n.t("Icon"),
},
full_name: {
form_field: true,
type: "smalltext",
field: "full_name",
},
},
},
}
@ -497,6 +496,14 @@ export class Models {
apiName: "User",
paginated: false,
}
static STEP = {
name: i18n.t("Step"),
apiName: "Step",
list: {
params: ["recipe", "query", "page", "pageSize", "options"],
},
}
}
export class Actions {

View File

@ -214,7 +214,7 @@ export interface Food {
* @type {boolean}
* @memberof Food
*/
ignore_shopping?: boolean;
food_onhand?: boolean;
/**
*
* @type {FoodSupermarketCategory}
@ -235,47 +235,16 @@ export interface Food {
numchild?: number;
/**
*
* @type {boolean}
* @type {Array<FoodInheritFields>}
* @memberof Food
*/
on_hand?: boolean;
/**
*
* @type {boolean}
* @memberof Food
*/
inherit?: boolean;
/**
*
* @type {Array<FoodIgnoreInherit>}
* @memberof Food
*/
ignore_inherit?: Array<FoodIgnoreInherit> | null;
}
/**
*
* @export
* @interface FoodIgnoreInherit
*/
export interface FoodIgnoreInherit {
/**
*
* @type {number}
* @memberof FoodIgnoreInherit
*/
id?: number;
inherit_fields?: Array<FoodInheritFields> | null;
/**
*
* @type {string}
* @memberof FoodIgnoreInherit
* @memberof Food
*/
name?: string;
/**
*
* @type {string}
* @memberof FoodIgnoreInherit
*/
field?: string;
full_name?: string;
}
/**
*
@ -294,13 +263,38 @@ export interface FoodInheritField {
* @type {string}
* @memberof FoodInheritField
*/
name?: string;
name?: string | null;
/**
*
* @type {string}
* @memberof FoodInheritField
*/
field?: string;
field?: string | null;
}
/**
*
* @export
* @interface FoodInheritFields
*/
export interface FoodInheritFields {
/**
*
* @type {number}
* @memberof FoodInheritFields
*/
id?: number;
/**
*
* @type {string}
* @memberof FoodInheritFields
*/
name?: string | null;
/**
*
* @type {string}
* @memberof FoodInheritFields
*/
field?: string | null;
}
/**
*
@ -513,6 +507,12 @@ export interface ImportLogKeyword {
* @memberof ImportLogKeyword
*/
updated_at?: string;
/**
*
* @type {string}
* @memberof ImportLogKeyword
*/
full_name?: string;
}
/**
*
@ -610,7 +610,7 @@ export interface IngredientFood {
* @type {boolean}
* @memberof IngredientFood
*/
ignore_shopping?: boolean;
food_onhand?: boolean;
/**
*
* @type {FoodSupermarketCategory}
@ -631,22 +631,16 @@ export interface IngredientFood {
numchild?: number;
/**
*
* @type {boolean}
* @type {Array<FoodInheritFields>}
* @memberof IngredientFood
*/
on_hand?: boolean;
inherit_fields?: Array<FoodInheritFields> | null;
/**
*
* @type {boolean}
* @type {string}
* @memberof IngredientFood
*/
inherit?: boolean;
/**
*
* @type {Array<FoodIgnoreInherit>}
* @memberof IngredientFood
*/
ignore_inherit?: Array<FoodIgnoreInherit> | null;
full_name?: string;
}
/**
*
@ -1018,6 +1012,12 @@ export interface Keyword {
* @memberof Keyword
*/
updated_at?: string;
/**
*
* @type {string}
* @memberof Keyword
*/
full_name?: string;
}
/**
*
@ -1691,6 +1691,12 @@ export interface RecipeKeywords {
* @memberof RecipeKeywords
*/
updated_at?: string;
/**
*
* @type {string}
* @memberof RecipeKeywords
*/
full_name?: string;
}
/**
*
@ -2996,10 +3002,10 @@ export interface UserPreference {
mealplan_autoadd_shopping?: boolean;
/**
*
* @type {string}
* @type {Array<FoodInheritFields>}
* @memberof UserPreference
*/
food_ignore_default?: string;
food_inherit_default?: Array<FoodInheritFields> | null;
/**
*
* @type {string}
@ -3042,6 +3048,12 @@ export interface UserPreference {
* @memberof UserPreference
*/
csv_prefix?: string;
/**
*
* @type {boolean}
* @memberof UserPreference
*/
filter_to_supermarket?: boolean;
}
/**

View File

@ -220,11 +220,6 @@ export const ApiMixin = {
return {
Models: Models,
Actions: Actions,
FoodCreateDefault: function (form) {
form.inherit_ignore = getUserPreference("food_ignore_default")
form.inherit = form.supermarket_category.length > 0
return form
},
}
},
methods: {
@ -369,6 +364,7 @@ export function getForm(model, action, item1, item2) {
if (f === "partialUpdate" && Object.keys(config).length == 0) {
config = { ...Actions.CREATE?.form, ...model.model_type?.["create"]?.form, ...model?.["create"]?.form }
config["title"] = { ...action?.form_title, ...model.model_type?.[f]?.form_title, ...model?.[f]?.form_title }
// form functions should not be inherited
if (config?.["form_function"]?.includes("Create")) {
delete config["form_function"]
}
@ -542,8 +538,7 @@ const specialCases = {
export const formFunctions = {
FoodCreateDefault: function (form) {
form.fields.filter((x) => x.field === "ignore_inherit")[0].value = getUserPreference("food_ignore_default")
form.fields.filter((x) => x.field === "inherit")[0].value = getUserPreference("food_ignore_default").length > 0
form.fields.filter((x) => x.field === "inherit_fields")[0].value = getUserPreference("food_inherit_default")
return form
},
}