Squashed commit of shoppinglist_v2
This commit is contained in:
@ -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)
|
||||
},
|
||||
|
@ -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) => {
|
||||
|
@ -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
|
||||
) {
|
||||
|
@ -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) {
|
||||
|
@ -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)
|
||||
|
@ -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>
|
||||
|
@ -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
|
||||
})
|
||||
|
@ -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
|
||||
},
|
||||
},
|
||||
|
@ -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>
|
||||
|
@ -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)
|
||||
},
|
||||
},
|
||||
|
@ -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" },
|
||||
},
|
||||
|
@ -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
|
||||
}
|
||||
},
|
||||
},
|
||||
|
@ -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>
|
||||
|
@ -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),
|
||||
]
|
||||
})
|
||||
|
20
vue/src/components/Modals/SmallText.vue
Normal file
20
vue/src/components/Modals/SmallText.vue
Normal 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>
|
@ -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>
|
||||
|
@ -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)
|
||||
},
|
||||
},
|
||||
}
|
||||
|
@ -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")
|
||||
},
|
||||
},
|
||||
|
@ -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",
|
||||
|
@ -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 {
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -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
|
||||
},
|
||||
}
|
||||
|
Reference in New Issue
Block a user