mirror of
https://github.com/TandoorRecipes/recipes.git
synced 2026-01-01 12:18:45 -05:00
add to shopping from card context menu
This commit is contained in:
@@ -1,40 +1,45 @@
|
||||
<template>
|
||||
<span>
|
||||
<b-button 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})]"
|
||||
:class="[onhand ? 'text-success fa-clipboard-check' : 'text-muted fa-clipboard' ]"
|
||||
@click="toggleOnHand"
|
||||
<span>
|
||||
<b-button
|
||||
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 })]"
|
||||
:class="[onhand ? 'text-success fa-clipboard-check' : 'text-muted fa-clipboard']"
|
||||
@click="toggleOnHand"
|
||||
/>
|
||||
</span>
|
||||
</span>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
|
||||
import {ApiMixin} from "@/utils/utils";
|
||||
import { ApiMixin } from "@/utils/utils"
|
||||
|
||||
export default {
|
||||
name: 'OnHandBadge',
|
||||
props: {
|
||||
item: {type: Object}
|
||||
},
|
||||
mixins: [ ApiMixin ],
|
||||
data() {
|
||||
return {
|
||||
onhand: false
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
this.onhand = this.item.on_hand
|
||||
},
|
||||
watch: {
|
||||
},
|
||||
methods: {
|
||||
toggleOnHand() {
|
||||
let params = {'id': this.item.id, 'on_hand': !this.onhand}
|
||||
this.genericAPI(this.Models.FOOD, this.Actions.UPDATE, params).then(() => {
|
||||
this.onhand = !this.onhand
|
||||
})
|
||||
}
|
||||
}
|
||||
name: "OnHandBadge",
|
||||
props: {
|
||||
item: { type: Object },
|
||||
},
|
||||
mixins: [ApiMixin],
|
||||
data() {
|
||||
return {
|
||||
onhand: false,
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
this.onhand = this.item.on_hand
|
||||
},
|
||||
watch: {
|
||||
"item.on_hand": function(newVal, oldVal) {
|
||||
this.onhand = newVal
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
toggleOnHand() {
|
||||
let params = { id: this.item.id, on_hand: !this.onhand }
|
||||
this.genericAPI(this.Models.FOOD, this.Actions.UPDATE, params).then(() => {
|
||||
this.onhand = !this.onhand
|
||||
})
|
||||
},
|
||||
},
|
||||
}
|
||||
</script>
|
||||
</script>
|
||||
|
||||
@@ -1,94 +1,96 @@
|
||||
<template>
|
||||
<span>
|
||||
<b-button class="btn text-decoration-none px-1 border-0" variant="link"
|
||||
v-if="ShowBadge"
|
||||
:id="`shopping${item.id}`"
|
||||
@click="addShopping()">
|
||||
<i class="fas"
|
||||
v-b-popover.hover.html
|
||||
:title="[shopping ? $t('RemoveFoodFromShopping', {'food': item.name}) : $t('AddFoodToShopping', {'food': item.name})]"
|
||||
: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" >
|
||||
<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-row >
|
||||
</b-popover>
|
||||
</span>
|
||||
<span>
|
||||
<b-button class="btn text-decoration-none px-1 border-0" variant="link" v-if="ShowBadge" :id="`shopping${item.id}`" @click="addShopping()">
|
||||
<i
|
||||
class="fas"
|
||||
v-b-popover.hover.html
|
||||
:title="[shopping ? $t('RemoveFoodFromShopping', { food: item.name }) : $t('AddFoodToShopping', { food: item.name })]"
|
||||
: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">
|
||||
<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-row>
|
||||
</b-popover>
|
||||
</span>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import {ApiMixin, StandardToasts} from "@/utils/utils";
|
||||
import { ApiMixin, StandardToasts } from "@/utils/utils"
|
||||
|
||||
export default {
|
||||
name: 'ShoppingBadge',
|
||||
props: {
|
||||
item: {type: Object},
|
||||
override_ignore: {type: Boolean, default: false}
|
||||
},
|
||||
mixins: [ ApiMixin ],
|
||||
data() {
|
||||
return {
|
||||
shopping: false,
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
// let random = [true, false,]
|
||||
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
|
||||
}
|
||||
name: "ShoppingBadge",
|
||||
props: {
|
||||
item: { type: Object },
|
||||
override_ignore: { type: Boolean, default: false },
|
||||
},
|
||||
DeleteConfirmation() {
|
||||
return this.$t('DeleteShoppingConfirm',{'food':this.item.name})
|
||||
mixins: [ApiMixin],
|
||||
data() {
|
||||
return {
|
||||
shopping: false,
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
// let random = [true, false,]
|
||||
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 })
|
||||
},
|
||||
ShowConfirmation() {
|
||||
if (this.shopping) {
|
||||
return "shopping" + this.item.id
|
||||
} else {
|
||||
return "NoDialog"
|
||||
}
|
||||
},
|
||||
},
|
||||
watch: {
|
||||
"item.shopping": function(newVal, oldVal) {
|
||||
this.shopping = newVal
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
addShopping() {
|
||||
if (this.shopping) {
|
||||
return
|
||||
} // if item already in shopping list, excution handled after confirmation
|
||||
let params = {
|
||||
id: this.item.id,
|
||||
amount: 1,
|
||||
}
|
||||
this.genericAPI(this.Models.FOOD, this.Actions.SHOPPING, params).then((result) => {
|
||||
this.shopping = true
|
||||
StandardToasts.makeStandardToast(StandardToasts.SUCCESS_CREATE)
|
||||
})
|
||||
},
|
||||
cancelDelete() {
|
||||
this.$refs["shopping" + this.item.id].$emit("close")
|
||||
},
|
||||
confirmDelete() {
|
||||
let params = {
|
||||
id: this.item.id,
|
||||
_delete: "true",
|
||||
}
|
||||
this.genericAPI(this.Models.FOOD, this.Actions.SHOPPING, params).then(() => {
|
||||
this.shopping = false
|
||||
this.$refs["shopping" + this.item.id].$emit("close")
|
||||
StandardToasts.makeStandardToast(StandardToasts.SUCCESS_DELETE)
|
||||
})
|
||||
},
|
||||
},
|
||||
ShowConfirmation() {
|
||||
if (this.shopping) {
|
||||
return 'shopping' + this.item.id
|
||||
} else {
|
||||
return 'NoDialog'
|
||||
}
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
},
|
||||
methods: {
|
||||
addShopping() {
|
||||
if (this.shopping) {return} // if item already in shopping list, excution handled after confirmation
|
||||
let params = {
|
||||
'id': this.item.id,
|
||||
'amount': 1
|
||||
}
|
||||
this.genericAPI(this.Models.FOOD, this.Actions.SHOPPING, params).then((result) => {
|
||||
this.shopping = true
|
||||
StandardToasts.makeStandardToast(StandardToasts.SUCCESS_CREATE)
|
||||
})
|
||||
},
|
||||
cancelDelete() {
|
||||
this.$refs['shopping' + this.item.id].$emit('close')
|
||||
},
|
||||
confirmDelete() {
|
||||
let params = {
|
||||
'id': this.item.id,
|
||||
'_delete': 'true'
|
||||
}
|
||||
this.genericAPI(this.Models.FOOD, this.Actions.SHOPPING, params).then(() => {
|
||||
this.shopping = false
|
||||
this.$refs['shopping' + this.item.id].$emit('close')
|
||||
StandardToasts.makeStandardToast(StandardToasts.SUCCESS_DELETE)
|
||||
})
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
</script>
|
||||
</script>
|
||||
|
||||
@@ -1,42 +1,38 @@
|
||||
<template>
|
||||
<span>
|
||||
<b-dropdown variant="link" toggle-class="text-decoration-none" right no-caret style="boundary:window">
|
||||
<template #button-content>
|
||||
<i class="fas fa-ellipsis-v" ></i>
|
||||
</template>
|
||||
<b-dropdown-item v-on:click="$emit('item-action', 'edit')" v-if="show_edit">
|
||||
<i class="fas fa-pencil-alt fa-fw"></i> {{ $t('Edit') }}
|
||||
</b-dropdown-item>
|
||||
|
||||
<b-dropdown-item v-on:click="$emit('item-action', 'delete')" v-if="show_delete">
|
||||
<i class="fas fa-trash-alt fa-fw"></i> {{ $t('Delete') }}
|
||||
</b-dropdown-item>
|
||||
<span>
|
||||
<b-dropdown variant="link" toggle-class="text-decoration-none" right no-caret style="boundary:window">
|
||||
<template #button-content>
|
||||
<i class="fas fa-ellipsis-v"></i>
|
||||
</template>
|
||||
<b-dropdown-item v-on:click="$emit('item-action', 'edit')" v-if="show_edit"> <i class="fas fa-pencil-alt fa-fw"></i> {{ $t("Edit") }} </b-dropdown-item>
|
||||
|
||||
<b-dropdown-item v-on:click="$emit('item-action', 'move')" v-if="show_move">
|
||||
<i class="fas fa-expand-arrows-alt fa-fw"></i> {{ $t('Move') }}
|
||||
</b-dropdown-item>
|
||||
|
||||
<b-dropdown-item v-if="show_merge" v-on:click="$emit('item-action', 'merge')">
|
||||
<i class="fas fa-compress-arrows-alt fa-fw"></i> {{ $t('Merge') }}
|
||||
</b-dropdown-item>
|
||||
<b-dropdown-item v-on:click="$emit('item-action', 'delete')" v-if="show_delete"> <i class="fas fa-trash-alt fa-fw"></i> {{ $t("Delete") }} </b-dropdown-item>
|
||||
<b-dropdown-item v-on:click="$emit('item-action', 'add-shopping')" v-if="show_shopping">
|
||||
<i class="fas fa-cart-plus fa-fw"></i> {{ $t("Add_to_Shopping") }}
|
||||
</b-dropdown-item>
|
||||
<b-dropdown-item v-on:click="$emit('item-action', 'add-onhand')" v-if="show_onhand"> <i class="fas fa-clipboard-check fa-fw"></i> {{ $t("OnHand") }} </b-dropdown-item>
|
||||
|
||||
<b-dropdown-item v-if="show_merge" v-on:click="$emit('item-action', 'merge-automate')">
|
||||
<i class="fas fa-robot fa-fw"></i> {{$t('Merge')}} & {{$t('Automate')}} <b-badge v-b-tooltip.hover :title="$t('warning_feature_beta')">BETA</b-badge>
|
||||
</b-dropdown-item>
|
||||
<b-dropdown-item v-on:click="$emit('item-action', 'move')" v-if="show_move"> <i class="fas fa-expand-arrows-alt fa-fw"></i> {{ $t("Move") }} </b-dropdown-item>
|
||||
|
||||
</b-dropdown>
|
||||
</span>
|
||||
<b-dropdown-item v-if="show_merge" v-on:click="$emit('item-action', 'merge')"> <i class="fas fa-compress-arrows-alt fa-fw"></i> {{ $t("Merge") }} </b-dropdown-item>
|
||||
|
||||
<b-dropdown-item v-if="show_merge" v-on:click="$emit('item-action', 'merge-automate')">
|
||||
<i class="fas fa-robot fa-fw"></i> {{ $t("Merge") }} & {{ $t("Automate") }} <b-badge v-b-tooltip.hover :title="$t('warning_feature_beta')">BETA</b-badge>
|
||||
</b-dropdown-item>
|
||||
</b-dropdown>
|
||||
</span>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
|
||||
export default {
|
||||
name: 'GenericContextMenu',
|
||||
props: {
|
||||
show_edit: {type: Boolean, default: true},
|
||||
show_delete: {type: Boolean, default: true},
|
||||
show_move: {type: Boolean, default: false},
|
||||
show_merge: {type: Boolean, default: false},
|
||||
}
|
||||
name: "GenericContextMenu",
|
||||
props: {
|
||||
show_edit: { type: Boolean, default: true },
|
||||
show_delete: { type: Boolean, default: true },
|
||||
show_move: { type: Boolean, default: false },
|
||||
show_merge: { type: Boolean, default: false },
|
||||
show_shopping: { type: Boolean, default: false },
|
||||
show_onhand: { type: Boolean, default: false },
|
||||
},
|
||||
}
|
||||
</script>
|
||||
</script>
|
||||
|
||||
@@ -24,7 +24,7 @@
|
||||
<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>
|
||||
<!-- <span>{{this_item[itemTags.field]}}</span> -->
|
||||
|
||||
<generic-pill v-for="x in itemTags" :key="x.field" :item_list="item[x.field]" :label="x.label" :color="x.color" />
|
||||
<generic-ordered-pill
|
||||
v-for="x in itemOrderedTags"
|
||||
@@ -66,6 +66,8 @@
|
||||
class="p-0"
|
||||
:show_merge="useMerge"
|
||||
:show_move="useMove"
|
||||
:show_shopping="useShopping"
|
||||
:show_onhand="useOnhand"
|
||||
@item-action="$emit('item-action', { action: $event, source: item })"
|
||||
>
|
||||
</generic-context-menu>
|
||||
@@ -126,8 +128,6 @@
|
||||
<b-list-group-item action v-on:click="closeMenu()">
|
||||
<i class="fas fa-times fa-fw"></i> <b>{{ $t("Cancel") }}</b>
|
||||
</b-list-group-item>
|
||||
<!-- TODO add to shopping list -->
|
||||
<!-- TODO toggle onhand -->
|
||||
</b-list-group>
|
||||
</div>
|
||||
</template>
|
||||
@@ -185,6 +185,12 @@ export default {
|
||||
useMerge: function() {
|
||||
return this.model?.["merge"] ?? false ? true : false
|
||||
},
|
||||
useShopping: function() {
|
||||
return this.model?.["shop"] ?? false ? true : false
|
||||
},
|
||||
useOnhand: function() {
|
||||
return this.model?.["onhand"] ?? false ? true : false
|
||||
},
|
||||
useDrag: function() {
|
||||
return this.useMove || this.useMerge
|
||||
},
|
||||
|
||||
@@ -1,72 +1,75 @@
|
||||
|
||||
<template>
|
||||
<draggable v-if="itemList" v-model="this_list" tag="span" group="ordered_items" z-index="500"
|
||||
@change="orderChanged">
|
||||
<span :key="k.id" v-for="k in itemList" class="pl-1">
|
||||
<b-badge squared :variant="color"><i class="fas fa-grip-lines-vertical text-muted"></i><span class="ml-1">{{thisLabel(k)}}</span></b-badge>
|
||||
</span>
|
||||
<draggable v-if="itemList" v-model="this_list" tag="span" group="ordered_items" z-index="500" @change="orderChanged">
|
||||
<span :key="k.id" v-for="k in itemList" class="pl-1">
|
||||
<b-badge squared :variant="color"
|
||||
><i class="fas fa-grip-lines-vertical text-muted"></i><span class="ml-1">{{ thisLabel(k) }}</span></b-badge
|
||||
>
|
||||
</span>
|
||||
</draggable>
|
||||
</template>
|
||||
|
||||
|
||||
<script>
|
||||
// you can't use this component with a horizontal card that is also draggable
|
||||
import draggable from 'vuedraggable'
|
||||
import draggable from "vuedraggable"
|
||||
|
||||
export default {
|
||||
name: 'GenericOrderedPill',
|
||||
components: {draggable},
|
||||
props: {
|
||||
item_list: {required: true, type: Array},
|
||||
label: {type: String, default: 'name'},
|
||||
color: {type: String, default: 'light'},
|
||||
field: {type: String, required: true},
|
||||
item: {type: Object},
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
this_list: [],
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
itemList: function() {
|
||||
if(Array.isArray(this.this_list)) {
|
||||
return this.this_list
|
||||
} else if (!this.this_list?.name) {
|
||||
return false
|
||||
} else {
|
||||
return [this.this_list]
|
||||
}
|
||||
name: "GenericOrderedPill",
|
||||
components: { draggable },
|
||||
props: {
|
||||
item_list: {
|
||||
type: Array,
|
||||
default() {
|
||||
return []
|
||||
},
|
||||
},
|
||||
label: { type: String, default: "name" },
|
||||
color: { type: String, default: "light" },
|
||||
field: { type: String, required: true },
|
||||
item: { type: Object },
|
||||
},
|
||||
|
||||
},
|
||||
mounted() {
|
||||
this.this_list = this.item_list
|
||||
},
|
||||
watch: {
|
||||
'item_list': function (newVal) {
|
||||
this.this_list = newVal
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
thisLabel: function (item) {
|
||||
let fields = this.label.split('::')
|
||||
let value = item
|
||||
fields.forEach(x => {
|
||||
value = value[x]
|
||||
});
|
||||
return value
|
||||
data() {
|
||||
return {
|
||||
this_list: [],
|
||||
}
|
||||
},
|
||||
orderChanged: function(e){
|
||||
let order = 0
|
||||
this.this_list.forEach(x => {
|
||||
x['order'] = order
|
||||
order++
|
||||
})
|
||||
let new_order = {...this.item}
|
||||
new_order[this.field] = this.this_list
|
||||
this.$emit('finish-action', {'action':'save','form_data': new_order })
|
||||
computed: {
|
||||
itemList: function() {
|
||||
if (Array.isArray(this.this_list)) {
|
||||
return this.this_list
|
||||
} else if (!this.this_list?.name) {
|
||||
return false
|
||||
} else {
|
||||
return [this.this_list]
|
||||
}
|
||||
},
|
||||
},
|
||||
mounted() {
|
||||
this.this_list = this.item_list
|
||||
},
|
||||
watch: {
|
||||
item_list: function(newVal) {
|
||||
this.this_list = newVal
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
thisLabel: function(item) {
|
||||
let fields = this.label.split("::")
|
||||
let value = item
|
||||
fields.forEach((x) => {
|
||||
value = value[x]
|
||||
})
|
||||
return value
|
||||
},
|
||||
orderChanged: function(e) {
|
||||
let order = 0
|
||||
this.this_list.forEach((x) => {
|
||||
x["order"] = order
|
||||
order++
|
||||
})
|
||||
let new_order = { ...this.item }
|
||||
new_order[this.field] = this.this_list
|
||||
this.$emit("finish-action", { action: "save", form_data: new_order })
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
@@ -83,7 +83,6 @@ export default {
|
||||
show: function () {
|
||||
if (this.show) {
|
||||
this.form = getForm(this.model, this.action, this.item1, this.item2)
|
||||
// TODO: I don't know how to generalize this, but Food needs default values to drive inheritance
|
||||
if (this.form?.form_function) {
|
||||
this.form = formFunctions[this.form.form_function](this.form)
|
||||
}
|
||||
|
||||
@@ -82,6 +82,9 @@ export default {
|
||||
footer_text: String,
|
||||
footer_icon: String,
|
||||
},
|
||||
mounted() {
|
||||
console.log(this.recipe)
|
||||
},
|
||||
computed: {
|
||||
detailed: function() {
|
||||
return this.recipe?.steps !== undefined
|
||||
|
||||
Reference in New Issue
Block a user