visual indicator meal plan in shopping

This commit is contained in:
smilerz 2021-12-02 09:50:01 -06:00
parent 24b0643765
commit 2af7b64d4f
4 changed files with 122 additions and 113 deletions

View File

@ -610,10 +610,14 @@ class MealPlanSerializer(SpacedModelSerializer, WritableNestedModelSerializer):
note_markdown = serializers.SerializerMethodField('get_note_markdown') note_markdown = serializers.SerializerMethodField('get_note_markdown')
servings = CustomDecimalField() servings = CustomDecimalField()
shared = UserNameSerializer(many=True, required=False, allow_null=True) shared = UserNameSerializer(many=True, required=False, allow_null=True)
shopping = serializers.SerializerMethodField('in_shopping')
def get_note_markdown(self, obj): def get_note_markdown(self, obj):
return markdown(obj.note) return markdown(obj.note)
def in_shopping(self, obj):
return ShoppingListRecipe.objects.filter(mealplan=obj.id).exists()
def create(self, validated_data): def create(self, validated_data):
validated_data['created_by'] = self.context['request'].user validated_data['created_by'] = self.context['request'].user
mealplan = super().create(validated_data) mealplan = super().create(validated_data)
@ -626,7 +630,7 @@ class MealPlanSerializer(SpacedModelSerializer, WritableNestedModelSerializer):
fields = ( fields = (
'id', 'title', 'recipe', 'servings', 'note', 'note_markdown', 'id', 'title', 'recipe', 'servings', 'note', 'note_markdown',
'date', 'meal_type', 'created_by', 'shared', 'recipe_name', 'date', 'meal_type', 'created_by', 'shared', 'recipe_name',
'meal_type_name' 'meal_type_name', 'shopping'
) )
read_only_fields = ('created_by',) read_only_fields = ('created_by',)

View File

@ -1,128 +1,132 @@
<template> <template>
<div v-hover class="card cv-item meal-plan-card p-0" :key="value.id" :draggable="true" <div
:style="`top:${top};max-height:${item_height}`" v-hover
@dragstart="onDragItemStart(value, $event)" class="card cv-item meal-plan-card p-0"
@click="onClickItem(value, $event)" :key="value.id"
:aria-grabbed="value == currentDragItem" :draggable="true"
:class="value.classes" :style="`top:${top};max-height:${item_height}`"
@contextmenu.prevent="$emit('open-context-menu', $event, value)"> @dragstart="onDragItemStart(value, $event)"
<div class="card-header p-1 text-center text-primary border-bottom-0" v-if="detailed" @click="onClickItem(value, $event)"
:style="`background-color: ${background_color}`"> :aria-grabbed="value == currentDragItem"
<span class="font-light text-center" v-if="entry.entry.meal_type.icon != null">{{ :class="value.classes"
entry.entry.meal_type.icon @contextmenu.prevent="$emit('open-context-menu', $event, value)"
}}</span> >
<span class="font-light d-none d-md-inline">{{ entry.entry.meal_type.name }}</span> <div class="card-header p-1 text-center text-primary border-bottom-0" v-if="detailed" :style="`background-color: ${background_color}`">
</div> <span class="font-light text-center" v-if="entry.entry.meal_type.icon != null">{{ entry.entry.meal_type.icon }}</span>
<div class="card-img-overlay h-100 d-flex flex-column justify-content-right float-right text-right p-0" <span class="font-light d-none d-md-inline">{{ entry.entry.meal_type.name }}</span>
v-if="detailed"> <span v-if="entry.entry.shopping" class="font-light"><i class="fas fa-shopping-cart fa-xs float-left" v-b-tooltip.hover.top :title="$t('in_shopping')" /></span>
<a> </div>
<div style="position: static;"> <div class="card-img-overlay h-100 d-flex flex-column justify-content-right float-right text-right p-0" v-if="detailed">
<div class="dropdown b-dropdown position-static btn-group"> <a>
<button aria-haspopup="true" aria-expanded="false" type="button" <div style="position: static">
class="btn btn-link text-decoration-none text-body pr-2 dropdown-toggle-no-caret" <div class="dropdown b-dropdown position-static btn-group">
@click.stop="$emit('open-context-menu', $event, value)"><i class="fas fa-ellipsis-v fa-lg"></i> <button
</button> aria-haspopup="true"
</div> aria-expanded="false"
type="button"
class="btn btn-link text-decoration-none text-body pr-2 dropdown-toggle-no-caret"
@click.stop="$emit('open-context-menu', $event, value)"
>
<i class="fas fa-ellipsis-v fa-lg"></i>
</button>
</div>
</div>
</a>
</div>
<div class="card-header p-1 text-center" v-if="detailed" :style="`background-color: ${background_color}`">
<span class="font-light">{{ title }}</span>
</div>
<b-img fluid class="card-img-bottom" :src="entry.entry.recipe.image" v-if="hasRecipe && detailed"></b-img>
<b-img fluid class="card-img-bottom" :src="image_placeholder" v-if="detailed && ((!hasRecipe && entry.entry.note === '') || (hasRecipe && entry.entry.recipe.image === null))"></b-img>
<div class="card-body p-1" v-if="detailed && entry.entry.recipe == null" :style="`background-color: ${background_color}`">
<p>{{ entry.entry.note }}</p>
</div>
<div class="row p-1 flex-nowrap" v-if="!detailed" :style="`background-color: ${background_color}`">
<div class="col-2">
<span class="font-light text-center" v-if="entry.entry.meal_type.icon != null" v-b-tooltip.hover.left :title="entry.entry.meal_type.name">{{ entry.entry.meal_type.icon }}</span>
<span class="font-light text-center" v-if="entry.entry.meal_type.icon == null" v-b-tooltip.hover.left :title="entry.entry.meal_type.name"></span>
</div>
<div class="col-10 d-inline-block text-truncate" :style="`max-height:${item_height}`">
<span class="font-light">{{ title }}</span>
</div>
</div> </div>
</a>
</div> </div>
<div class="card-header p-1 text-center" v-if="detailed" :style="`background-color: ${background_color}`">
<span class="font-light">{{ title }}</span>
</div>
<b-img fluid class="card-img-bottom" :src="entry.entry.recipe.image" v-if="hasRecipe && detailed" ></b-img>
<b-img fluid class="card-img-bottom" :src="image_placeholder"
v-if="detailed && ((!hasRecipe && entry.entry.note === '') || (hasRecipe && entry.entry.recipe.image === null))"></b-img>
<div class="card-body p-1" v-if="detailed && entry.entry.recipe == null"
:style="`background-color: ${background_color}`">
<p>{{ entry.entry.note }}</p>
</div>
<div class="row p-1 flex-nowrap" v-if="!detailed" :style="`background-color: ${background_color}`">
<div class="col-2">
<span class="font-light text-center" v-if="entry.entry.meal_type.icon != null" v-b-tooltip.hover.left
:title=" entry.entry.meal_type.name">{{
entry.entry.meal_type.icon
}}</span>
<span class="font-light text-center" v-if="entry.entry.meal_type.icon == null" v-b-tooltip.hover.left
:title=" entry.entry.meal_type.name"></span>
</div>
<div class="col-10 d-inline-block text-truncate" :style="`max-height:${item_height}`">
<span class="font-light">{{ title }}</span>
</div>
</div>
</div>
</template> </template>
<script> <script>
export default { export default {
name: "MealPlanCard.vue", name: "MealPlanCard.vue",
components: {}, components: {},
props: { props: {
value: Object, value: Object,
weekStartDate: Date, weekStartDate: Date,
top: String, top: String,
detailed: Boolean, detailed: Boolean,
item_height: String item_height: String,
},
data: function () {
return {
dateSelectionOrigin: null,
currentDragItem: null,
image_placeholder: window.IMAGE_PLACEHOLDER
}
},
computed: {
entry: function () {
return this.value.originalItem
}, },
title: function () { data: function () {
if (this.entry.entry.title != null && this.entry.entry.title !== '') { return {
return this.entry.entry.title dateSelectionOrigin: null,
} else { currentDragItem: null,
return this.entry.entry.recipe_name image_placeholder: window.IMAGE_PLACEHOLDER,
} }
}, },
hasRecipe: function () { mounted() {
return this.entry.entry.recipe != null; console.log(this.value)
}, },
background_color: function () { computed: {
if (this.entry.entry.meal_type.color != null && this.entry.entry.meal_type.color !== '') { entry: function () {
return this.entry.entry.meal_type.color return this.value.originalItem
} else { },
return "#fff" title: function () {
} if (this.entry.entry.title != null && this.entry.entry.title !== "") {
return this.entry.entry.title
} else {
return this.entry.entry.recipe_name
}
},
hasRecipe: function () {
return this.entry.entry.recipe != null
},
background_color: function () {
if (this.entry.entry.meal_type.color != null && this.entry.entry.meal_type.color !== "") {
return this.entry.entry.meal_type.color
} else {
return "#fff"
}
},
}, },
}, methods: {
methods: { onDragItemStart(calendarItem, windowEvent) {
onDragItemStart(calendarItem, windowEvent) { this.$emit("dragstart", calendarItem, windowEvent)
this.$emit("dragstart", calendarItem, windowEvent) return true
return true },
onContextMenuOpen(calendarItem, windowEvent) {
this.$emit("dragstart", calendarItem, windowEvent)
return true
},
onClickItem(calendarItem, windowEvent) {
this.$emit("click-item", calendarItem)
return true
},
}, },
onContextMenuOpen(calendarItem, windowEvent) { directives: {
this.$emit("dragstart", calendarItem, windowEvent) hover: {
return true inserted: (el) => {
el.addEventListener("mouseenter", () => {
el.classList.add("shadow")
})
el.addEventListener("mouseleave", () => {
el.classList.remove("shadow")
})
},
},
}, },
onClickItem(calendarItem, windowEvent) {
this.$emit("click-item", calendarItem)
return true
},
},
directives: {
hover: {
inserted: (el) => {
el.addEventListener('mouseenter', () => {
el.classList.add("shadow")
});
el.addEventListener('mouseleave', () => {
el.classList.remove("shadow")
});
}
}
}
} }
</script> </script>
<style scoped> <style scoped>
.meal-plan-card { .meal-plan-card {
background-color: #fff; background-color: #fff;
} }
</style> </style>

View File

@ -17,7 +17,7 @@
<a class="dropdown-item" :href="`${resolveDjangoUrl('view_shopping')}?r=[${recipe.id},${servings_value}]`" v-if="recipe.internal" target="_blank" rel="noopener noreferrer"> <a class="dropdown-item" :href="`${resolveDjangoUrl('view_shopping')}?r=[${recipe.id},${servings_value}]`" v-if="recipe.internal" target="_blank" rel="noopener noreferrer">
<i class="fas fa-shopping-cart fa-fw"></i> {{ $t("Add_to_Shopping") }} <i class="fas fa-shopping-cart fa-fw"></i> {{ $t("Add_to_Shopping") }}
</a> </a>
<a class="dropdown-item" v-if="recipe.internal" @click="addToShopping" href="#"> <i class="fas fa-shopping-cart fa-fw"></i> New {{ $t("create_shopping_new") }} </a> <a class="dropdown-item" v-if="recipe.internal" @click="addToShopping" href="#"> <i class="fas fa-shopping-cart fa-fw"></i> {{ $t("create_shopping_new") }} </a>
<a class="dropdown-item" @click="createMealPlan" href="javascript:void(0);"><i class="fas fa-calendar fa-fw"></i> {{ $t("Add_to_Plan") }} </a> <a class="dropdown-item" @click="createMealPlan" href="javascript:void(0);"><i class="fas fa-calendar fa-fw"></i> {{ $t("Add_to_Plan") }} </a>

View File

@ -265,7 +265,7 @@
"CategoryInstruction": "Drag categories to change the order categories appear in shopping list.", "CategoryInstruction": "Drag categories to change the order categories appear in shopping list.",
"shopping_recent_days_desc": "Days of recent shopping list entries to display.", "shopping_recent_days_desc": "Days of recent shopping list entries to display.",
"shopping_recent_days": "Recent Days", "shopping_recent_days": "Recent Days",
"create_shopping_new": "NEW: Add to Shopping List", "create_shopping_new": "Add to NEW Shopping List",
"download_pdf": "Download PDF", "download_pdf": "Download PDF",
"download_csv": "Download CSV", "download_csv": "Download CSV",
"csv_delim_help": "Delimiter to use for CSV exports.", "csv_delim_help": "Delimiter to use for CSV exports.",
@ -274,5 +274,6 @@
"copy_to_clipboard": "Copy to Clipboard", "copy_to_clipboard": "Copy to Clipboard",
"csv_prefix_help": "Prefix to add when copying list to the clipboard.", "csv_prefix_help": "Prefix to add when copying list to the clipboard.",
"csv_prefix_label": "List Prefix", "csv_prefix_label": "List Prefix",
"copy_markdown_table": "Copy as Markdown Table" "copy_markdown_table": "Copy as Markdown Table",
"in_shopping": "In Shopping List"
} }