add to shopping from card context menu
This commit is contained in:
parent
a217db5822
commit
f245aa8b4f
@ -673,7 +673,7 @@ class ShoppingListEntrySerializer(WritableNestedModelSerializer):
|
||||
recipe_mealplan = ShoppingListRecipeSerializer(source='list_recipe', read_only=True)
|
||||
amount = CustomDecimalField()
|
||||
created_by = UserNameSerializer(read_only=True)
|
||||
completed_at = serializers.DateTimeField(allow_null=True)
|
||||
completed_at = serializers.DateTimeField(allow_null=True, required=False)
|
||||
|
||||
def get_fields(self, *args, **kwargs):
|
||||
fields = super().get_fields(*args, **kwargs)
|
||||
|
@ -1,4 +1,5 @@
|
||||
{% load i18n %}
|
||||
{% comment %} TODO: Deprecate {% endcomment %}
|
||||
|
||||
<div class="modal" tabindex="-1" role="dialog" id="id_modal_cook_log">
|
||||
<div class="modal-dialog" role="document">
|
||||
@ -77,4 +78,4 @@
|
||||
$('#id_rating_show').html(rating.val() + '/5')
|
||||
});
|
||||
|
||||
</script>
|
||||
</script>
|
||||
|
@ -1,5 +1,12 @@
|
||||
{% extends "base.html" %} {% comment %} TODO: Deprecate {% endcomment %} {% load django_tables2 %} {% load crispy_forms_tags %} {% load static %} {% load i18n %} {% block title
|
||||
%}{% trans "Shopping List" %}{% endblock %} {% block extra_head %} {% include 'include/vue_base.html' %}
|
||||
{% extends "base.html" %}
|
||||
{% comment %} TODO: Deprecate {% endcomment %}
|
||||
{% load django_tables2 %}
|
||||
{% load crispy_forms_tags %}
|
||||
{% load static %}
|
||||
{% load i18n %}
|
||||
{% block title %}{% trans "Shopping List" %}{% endblock %}
|
||||
{% block extra_head %}
|
||||
{% include 'include/vue_base.html' %}
|
||||
|
||||
<link rel="stylesheet" href="{% static 'css/vue-multiselect-bs4.min.css' %}" />
|
||||
<script src="{% static 'js/vue-multiselect.min.js' %}"></script>
|
||||
@ -907,7 +914,7 @@
|
||||
this.makeToast(gettext('Error'), gettext("There was an error loading a resource!") + err.bodyText, 'danger')
|
||||
})
|
||||
},
|
||||
searchSupermarket: function (query) { //TODO move to central component
|
||||
searchSupermarket: function (query) {
|
||||
this.supermarkets_loading = true
|
||||
this.$http.get("{% url 'api:supermarket-list' %}" + '?query=' + query + '&limit=10').then((response) => {
|
||||
this.supermarkets = response.data
|
||||
|
@ -715,7 +715,6 @@
|
||||
},
|
||||
methods: {
|
||||
makeToast: function (title, message, variant = null) {
|
||||
//TODO remove duplicate function in favor of central one
|
||||
this.$bvToast.toast(message, {
|
||||
title: title,
|
||||
variant: variant,
|
||||
|
@ -657,7 +657,6 @@ class RecipeViewSet(viewsets.ModelViewSet):
|
||||
servings = request.data.get('servings', obj.servings)
|
||||
list_recipe = request.data.get('list_recipe', None)
|
||||
content = {'msg': _(f'{obj.name} was added to the shopping list.')}
|
||||
# TODO: Consider if this should be a Recipe method
|
||||
list_from_recipe(list_recipe=list_recipe, recipe=obj, ingredients=ingredients, servings=servings, space=request.space, created_by=request.user)
|
||||
|
||||
return Response(content, status=status.HTTP_204_NO_CONTENT)
|
||||
|
@ -90,7 +90,7 @@ export default {
|
||||
left_counts: { max: 9999, current: 0 },
|
||||
this_model: undefined,
|
||||
model_menu: undefined,
|
||||
this_action: undefined,
|
||||
this_action: {},
|
||||
this_recipe_param: undefined,
|
||||
this_item: {},
|
||||
this_target: {},
|
||||
@ -193,6 +193,13 @@ export default {
|
||||
this.getRecipes(param, source)
|
||||
}
|
||||
break
|
||||
case "add-shopping":
|
||||
//TODO: add modal to edit units and amount
|
||||
this.addShopping(e.source)
|
||||
break
|
||||
case "add-onhand":
|
||||
this.addOnhand(e.source)
|
||||
break
|
||||
}
|
||||
},
|
||||
finishAction: function (e) {
|
||||
@ -232,26 +239,6 @@ export default {
|
||||
let results = result.data?.results ?? result.data
|
||||
|
||||
if (results?.length) {
|
||||
// let secondaryRequest = undefined;
|
||||
// if (this['items_' + column]?.length < getConfig(this.this_model, this.Actions.LIST).config.pageSize.default * (params.page - 1)) {
|
||||
// // the item list is smaller than it should be based on the site the user is own
|
||||
// // this happens when an item is deleted (or merged)
|
||||
// // to prevent issues insert the last item of the previous search page before loading the new results
|
||||
// params.page = params.page - 1
|
||||
// secondaryRequest = this.genericAPI(this.this_model, this.Actions.LIST, params).then((result) => {
|
||||
// let prev_page_results = result.data?.results ?? result.data
|
||||
// if (prev_page_results?.length) {
|
||||
// results = [prev_page_results[prev_page_results.length]].concat(results)
|
||||
//
|
||||
// this['items_' + column] = this['items_' + column].concat(results) //TODO duplicate code, find some elegant workaround
|
||||
// this[column + '_counts']['current'] = getConfig(this.this_model, this.Actions.LIST).config.pageSize.default * (params.page - 1) + results.length
|
||||
// this[column + '_counts']['max'] = result.data?.count ?? 0
|
||||
// }
|
||||
// })
|
||||
// } else {
|
||||
//
|
||||
// }
|
||||
|
||||
this["items_" + column] = this["items_" + column].concat(results)
|
||||
this[column + "_counts"]["current"] = getConfig(this.this_model, this.Actions.LIST).config.pageSize.default * (params.page - 1) + results.length
|
||||
this[column + "_counts"]["max"] = result.data?.count ?? 0
|
||||
@ -276,6 +263,21 @@ export default {
|
||||
// this creates a deep copy to make sure that columns stay independent
|
||||
this.items_right = [{ ...item }].concat(this.destroyCard(item?.id, this.items_right))
|
||||
},
|
||||
// this currently assumes shopping is only applicable on FOOD model
|
||||
addShopping: function (food) {
|
||||
let api = new ApiApiFactory()
|
||||
food.shopping = true
|
||||
api.createShoppingListEntry({ food: food, amount: 1 }).then(() => {
|
||||
StandardToasts.makeStandardToast(StandardToasts.SUCCESS_CREATE)
|
||||
this.refreshCard(food, this.items_left)
|
||||
this.refreshCard({ ...food }, this.items_right)
|
||||
})
|
||||
},
|
||||
addOnhand: function (item) {
|
||||
item.on_hand = true
|
||||
this.saveThis(item)
|
||||
},
|
||||
|
||||
updateThis: function (item) {
|
||||
this.refreshThis(item.id)
|
||||
},
|
||||
@ -300,7 +302,7 @@ export default {
|
||||
})
|
||||
.catch((err) => {
|
||||
console.log(err)
|
||||
makeToast(this.$t("Error"), err.bodyText, "danger")
|
||||
StandardToasts.makeStandardToast(StandardToasts.FAIL_MOVE, err?.bodyText)
|
||||
})
|
||||
},
|
||||
moveUpdateItem: function (source_id, target_id) {
|
||||
@ -336,12 +338,12 @@ export default {
|
||||
.then((result) => {
|
||||
this.mergeUpdateItem(source_id, target_id)
|
||||
// TODO make standard toast
|
||||
makeToast(this.$t("Success"), "Succesfully merged resource", "success")
|
||||
StandardToasts.makeStandardToast(StandardToasts.SUCCESS_MERGE)
|
||||
})
|
||||
.catch((err) => {
|
||||
//TODO error checking not working with OpenAPI methods
|
||||
console.log("Error", err)
|
||||
makeToast(this.$t("Error"), err.bodyText, "danger")
|
||||
StandardToasts.makeStandardToast(StandardToasts.FAIL_MOVE, err?.bodyText)
|
||||
})
|
||||
|
||||
if (automate) {
|
||||
@ -425,7 +427,7 @@ export default {
|
||||
},
|
||||
clearState: function () {
|
||||
this.show_modal = false
|
||||
this.this_action = undefined
|
||||
this.this_action = {}
|
||||
this.this_item = undefined
|
||||
this.this_target = undefined
|
||||
},
|
||||
|
@ -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
|
||||
|
@ -65,6 +65,8 @@ export class Models {
|
||||
paginated: true,
|
||||
move: true,
|
||||
merge: true,
|
||||
shop: true,
|
||||
onhand: true,
|
||||
badges: {
|
||||
linked_recipe: true,
|
||||
on_hand: true,
|
||||
|
@ -2160,7 +2160,7 @@ export interface ShoppingListEntries {
|
||||
* @type {string}
|
||||
* @memberof ShoppingListEntries
|
||||
*/
|
||||
completed_at: string | null;
|
||||
completed_at?: string | null;
|
||||
/**
|
||||
*
|
||||
* @type {string}
|
||||
@ -2251,7 +2251,7 @@ export interface ShoppingListEntry {
|
||||
* @type {string}
|
||||
* @memberof ShoppingListEntry
|
||||
*/
|
||||
completed_at: string | null;
|
||||
completed_at?: string | null;
|
||||
/**
|
||||
*
|
||||
* @type {string}
|
||||
@ -3014,10 +3014,10 @@ export interface UserPreference {
|
||||
food_ignore_default?: string;
|
||||
/**
|
||||
*
|
||||
* @type {number}
|
||||
* @type {string}
|
||||
* @memberof UserPreference
|
||||
*/
|
||||
default_delay?: number;
|
||||
default_delay?: string;
|
||||
/**
|
||||
*
|
||||
* @type {boolean}
|
||||
@ -3036,6 +3036,12 @@ export interface UserPreference {
|
||||
* @memberof UserPreference
|
||||
*/
|
||||
shopping_share?: Array<number>;
|
||||
/**
|
||||
*
|
||||
* @type {number}
|
||||
* @memberof UserPreference
|
||||
*/
|
||||
shopping_recent_days?: number;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -89,7 +89,6 @@ module.exports = {
|
||||
},
|
||||
},
|
||||
},
|
||||
// TODO make this conditional on .env DEBUG = FALSE
|
||||
config.optimization.minimize(false)
|
||||
)
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user