Merge branch 'develop' into facet-fix

This commit is contained in:
vabene1111
2022-01-18 07:59:32 +01:00
committed by GitHub
7 changed files with 1641 additions and 1460 deletions

View File

@@ -15,7 +15,7 @@ from .models import (BookmarkletImport, Comment, CookLog, Food, FoodInheritField
Recipe, RecipeBook, RecipeBookEntry, RecipeImport, SearchPreference, ShareLink, Recipe, RecipeBook, RecipeBookEntry, RecipeImport, SearchPreference, ShareLink,
ShoppingList, ShoppingListEntry, ShoppingListRecipe, Space, Step, Storage, ShoppingList, ShoppingListEntry, ShoppingListRecipe, Space, Step, Storage,
Supermarket, SupermarketCategory, SupermarketCategoryRelation, Sync, SyncLog, Supermarket, SupermarketCategory, SupermarketCategoryRelation, Sync, SyncLog,
TelegramBot, Unit, UserFile, UserPreference, ViewLog) TelegramBot, Unit, UserFile, UserPreference, ViewLog, Automation)
class CustomUserAdmin(UserAdmin): class CustomUserAdmin(UserAdmin):
@@ -29,11 +29,52 @@ admin.site.register(User, CustomUserAdmin)
admin.site.unregister(Group) admin.site.unregister(Group)
@admin.action(description='Delete all data from a space')
def delete_space_action(modeladmin, request, queryset):
for space in queryset:
CookLog.objects.filter(space=space).delete()
ViewLog.objects.filter(space=space).delete()
ImportLog.objects.filter(space=space).delete()
BookmarkletImport.objects.filter(space=space).delete()
Comment.objects.filter(recipe__space=space).delete()
Keyword.objects.filter(space=space).delete()
Ingredient.objects.filter(space=space).delete()
Food.objects.filter(space=space).delete()
Unit.objects.filter(space=space).delete()
Step.objects.filter(space=space).delete()
NutritionInformation.objects.filter(space=space).delete()
RecipeBookEntry.objects.filter(book__space=space).delete()
RecipeBook.objects.filter(space=space).delete()
MealType.objects.filter(space=space).delete()
MealPlan.objects.filter(space=space).delete()
ShareLink.objects.filter(space=space).delete()
Recipe.objects.filter(space=space).delete()
RecipeImport.objects.filter(space=space).delete()
SyncLog.objects.filter(sync__space=space).delete()
Sync.objects.filter(space=space).delete()
Storage.objects.filter(space=space).delete()
ShoppingListEntry.objects.filter(shoppinglist__space=space).delete()
ShoppingListRecipe.objects.filter(shoppinglist__space=space).delete()
ShoppingList.objects.filter(space=space).delete()
SupermarketCategoryRelation.objects.filter(supermarket__space=space).delete()
SupermarketCategory.objects.filter(space=space).delete()
Supermarket.objects.filter(space=space).delete()
InviteLink.objects.filter(space=space).delete()
UserFile.objects.filter(space=space).delete()
Automation.objects.filter(space=space).delete()
class SpaceAdmin(admin.ModelAdmin): class SpaceAdmin(admin.ModelAdmin):
list_display = ('name', 'created_by', 'max_recipes', 'max_users', 'max_file_storage_mb', 'allow_sharing') list_display = ('name', 'created_by', 'max_recipes', 'max_users', 'max_file_storage_mb', 'allow_sharing')
search_fields = ('name', 'created_by__username') search_fields = ('name', 'created_by__username')
list_filter = ('max_recipes', 'max_users', 'max_file_storage_mb', 'allow_sharing') list_filter = ('max_recipes', 'max_users', 'max_file_storage_mb', 'allow_sharing')
date_hierarchy = 'created_at' date_hierarchy = 'created_at'
actions = [delete_space_action]
admin.site.register(Space, SpaceAdmin) admin.site.register(Space, SpaceAdmin)
@@ -171,6 +212,8 @@ class RecipeAdmin(admin.ModelAdmin):
admin.site.register(Recipe, RecipeAdmin) admin.site.register(Recipe, RecipeAdmin)
admin.site.register(Unit) admin.site.register(Unit)
# admin.site.register(FoodInheritField) # admin.site.register(FoodInheritField)

View File

@@ -165,9 +165,10 @@ class FoodInheritFieldSerializer(WritableNestedModelSerializer):
read_only_fields = ['id'] read_only_fields = ['id']
class UserPreferenceSerializer(serializers.ModelSerializer): class UserPreferenceSerializer(WritableNestedModelSerializer):
food_inherit_default = FoodInheritFieldSerializer(source='space.food_inherit', many=True, allow_null=True, required=False, read_only=True) food_inherit_default = FoodInheritFieldSerializer(source='space.food_inherit', many=True, allow_null=True, required=False, read_only=True)
plan_share = UserNameSerializer(many=True, allow_null=True, required=False, read_only=True) plan_share = UserNameSerializer(many=True, allow_null=True, required=False, read_only=True)
shopping_share = UserNameSerializer(many=True, allow_null=True, required=False)
def create(self, validated_data): def create(self, validated_data):
if not validated_data.get('user', None): if not validated_data.get('user', None):

View File

@@ -45,21 +45,15 @@ def hook(request, token):
tb.save() tb.save()
if tb.chat_id == str(data['message']['chat']['id']): if tb.chat_id == str(data['message']['chat']['id']):
sl = ShoppingList.objects.filter(Q(created_by=tb.created_by)).filter(finished=False, space=tb.space).order_by('-created_at').first()
if not sl:
sl = ShoppingList.objects.create(created_by=tb.created_by, space=tb.space)
request.space = tb.space # TODO this is likely a bad idea. Verify and test request.space = tb.space # TODO this is likely a bad idea. Verify and test
request.user = tb.created_by request.user = tb.created_by
ingredient_parser = IngredientParser(request, False) ingredient_parser = IngredientParser(request, False)
amount, unit, ingredient, note = ingredient_parser.parse(data['message']['text']) amount, unit, ingredient, note = ingredient_parser.parse(data['message']['text'])
f = ingredient_parser.get_food(ingredient) f = ingredient_parser.get_food(ingredient)
u = ingredient_parser.get_unit(unit) u = ingredient_parser.get_unit(unit)
sl.entries.add(
ShoppingListEntry.objects.create( ShoppingListEntry.objects.create(food=f, unit=u, amount=amount, created_by=request.user, space=request.space)
food=f, unit=u, amount=amount
)
)
return JsonResponse({'data': data['message']['text']}) return JsonResponse({'data': data['message']['text']})
except Exception: except Exception:
pass pass

View File

@@ -1,81 +1,111 @@
<template> <template>
<div id="app" style="margin-bottom: 4vh"> <div id="app" style="margin-bottom: 4vh">
<b-alert :show="!online" dismissible class="small float-up" variant="warning">{{ $t("OfflineAlert") }}</b-alert> <b-alert :show="!online" dismissible class="small float-up" variant="warning">{{ $t("OfflineAlert") }}</b-alert>
<div class="row float-top"> <div class="row float-top pl-0 pr-0">
<div class="col-auto no-gutter ml-auto"> <div class="col-auto no-gutter ml-auto">
<b-button variant="link" class="px-0"> <b-button variant="link" class="px-1 pt-0 pb-1 d-none d-md-inline-block">
<i class="btn fas fa-plus-circle fa-lg px-0" @click="entrymode = !entrymode" :class="entrymode ? 'text-success' : 'text-muted'" /> <i class="btn fas fa-plus-circle fa-lg px-0" @click="entrymode = !entrymode"
:class="entrymode ? 'text-success' : 'text-muted'"/>
</b-button> </b-button>
<b-button variant="link" class="px-1"> <b-button variant="link" class="px-1 pt-0 pb-1 d-none d-md-inline-block">
<i class="fas fa-download fa-lg nav-link dropdown-toggle text-muted px-1" id="downloadShoppingLink" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false"></i> <i class="fas fa-download fa-lg nav-link dropdown-toggle text-muted px-1" id="downloadShoppingLink"
data-toggle="dropdown" aria-haspopup="true" aria-expanded="false"></i>
<div class="dropdown-menu dropdown-menu-center" aria-labelledby="downloadShoppingLink"> <div class="dropdown-menu dropdown-menu-center" aria-labelledby="downloadShoppingLink">
<DownloadPDF dom="#shoppinglist" name="shopping.pdf" :label="$t('download_pdf')" icon="far fa-file-pdf"/> <DownloadPDF dom="#shoppinglist" name="shopping.pdf" :label="$t('download_pdf')" icon="far fa-file-pdf"/>
<DownloadCSV :items="csvData" :delim="settings.csv_delim" name="shopping.csv" :label="$t('download_csv')" icon="fas fa-file-csv" /> <DownloadCSV :items="csvData" :delim="settings.csv_delim" name="shopping.csv" :label="$t('download_csv')"
<CopyToClipboard :items="csvData" :settings="settings" :label="$t('copy_to_clipboard')" icon="fas fa-clipboard-list" /> icon="fas fa-file-csv"/>
<CopyToClipboard :items="csvData" :settings="settings" format="table" :label="$t('copy_markdown_table')" icon="fab fa-markdown" /> <CopyToClipboard :items="csvData" :settings="settings" :label="$t('copy_to_clipboard')"
icon="fas fa-clipboard-list"/>
<CopyToClipboard :items="csvData" :settings="settings" format="table" :label="$t('copy_markdown_table')"
icon="fab fa-markdown"/>
</div> </div>
</b-button> </b-button>
<b-button variant="link" id="id_filters_button" class="px-1"> <b-button variant="link" id="id_filters_button" class="px-1 pt-0 pb-1">
<i class="btn fas fa-filter text-decoration-none fa-lg px-1" :class="filterApplied ? 'text-danger' : 'text-muted'" /> <i class="btn fas fa-filter text-decoration-none fa-lg px-1"
:class="filterApplied ? 'text-danger' : 'text-muted'"/>
</b-button> </b-button>
</div> </div>
</div> </div>
<b-tabs content-class="mt-3"> <b-tabs content-class="mt-3" v-model="current_tab">
<!-- shopping list tab --> <!-- shopping list tab -->
<b-tab active> <b-tab active>
<template #title> <b-spinner v-if="loading" type="border" small></b-spinner> {{ $t("Shopping_list") }} </template> <template #title>
<div class="container" id="shoppinglist"> <b-spinner v-if="loading" type="border" small></b-spinner>
{{ $t("Shopping_list") }}
</template>
<div class="container p-0 pr-lg-5 pl-lg-5" id="shoppinglist">
<div class="row"> <div class="row">
<div class="col col-md-12"> <div class="col col-md-12 p-0 p-lg-3">
<div role="tablist"> <div role="tablist">
<!-- add to shopping form --> <!-- add to shopping form -->
<b-row class="row justify-content-md-center" v-if="entrymode"> <b-row class="justify-content-md-center align-items-center pl-1 pr-1" v-if="entrymode">
<b-col cols="12" sm="4" md="2" v-if="!entry_mode_simple"> <b-col cols="12" md="3" v-if="!entry_mode_simple" class="d-none d-md-block mt-1">
<b-form-input min="1" type="number" :description="$t('Amount')" v-model="new_item.amount"></b-form-input> <b-form-input size="lg" min="1" type="number" :description="$t('Amount')"
v-model="new_item.amount"
style="font-size: 16px;border-radius: 5px !important;border: 1px solid #e8e8e8 !important;"></b-form-input>
</b-col> </b-col>
<b-col cols="12" sm="8" md="3" v-if="!entry_mode_simple"> <b-col cols="12" md="4" v-if="!entry_mode_simple" class="mt-1">
<lookup-input :form="formUnit" :model="Models.UNIT" @change="new_item.unit = $event" :show_label="false" :clear="clear" /> <lookup-input :class_list="'mb-0'" :form="formUnit" :model="Models.UNIT"
@change="new_item.unit = $event"
:show_label="false" :clear="clear"/>
</b-col> </b-col>
<b-col cols="12" sm="8" md="4" v-if="!entry_mode_simple"> <b-col cols="12" md="4" v-if="!entry_mode_simple" class="mt-1">
<lookup-input :form="formFood" :model="Models.FOOD" @change="new_item.food = $event" :show_label="false" :clear="clear" /> <lookup-input :class_list="'mb-0'" :form="formFood" :model="Models.FOOD"
@change="new_item.food = $event"
:show_label="false" :clear="clear"/>
</b-col> </b-col>
<b-col cols="12" sm="8" v-if="entry_mode_simple"> <b-col cols="12" md="11" v-if="entry_mode_simple" class="mt-1">
<b-form-input type="text" :placeholder="$t('QuickEntry')" v-model="new_item.ingredient" @keyup.enter="addItem"></b-form-input> <b-form-input size="lg" type="text" :placeholder="$t('QuickEntry')" v-model="new_item.ingredient"
@keyup.enter="addItem"></b-form-input>
</b-col> </b-col>
<b-col cols="12" sm="4" md="1"> <b-col cols="12" md="1" class="d-none d-md-block mt-1">
<b-button variant="link" class="px-0"> <b-button variant="link" class="px-0">
<i class="btn fas fa-cart-plus fa-lg px-0 text-success" @click="addItem"/> <i class="btn fas fa-cart-plus fa-lg px-0 text-success" @click="addItem"/>
</b-button> </b-button>
</b-col> </b-col>
<b-col cols="12" md="3" v-if="!entry_mode_simple" class="d-block d-md-none mt-1">
<b-row>
<b-col cols="9">
<b-form-input size="lg" min="1" type="number" :description="$t('Amount')"
v-model="new_item.amount"
style="font-size: 16px;border-radius: 5px !important;border: 1px solid #e8e8e8 !important;"></b-form-input>
</b-col>
<b-col cols="3" class="flex-grow-1">
<b-button variant="success" class="p-0 pt-1 w-100 h-100">
<i class="btn fas fa-cart-plus fa-lg" @click="addItem"/>
</b-button>
</b-col>
</b-row>
</b-col>
</b-row> </b-row>
<b-row class="row justify-content-md-end" v-if="entrymode"> <b-row class="row justify-content-around mt-2" v-if="entrymode">
<b-form-checkbox switch v-model="entry_mode_simple"> <b-form-checkbox switch v-model="entry_mode_simple">
{{ $t("QuickEntry") }} {{ $t("QuickEntry") }}
</b-form-checkbox> </b-form-checkbox>
<b-button variant="success" size="sm" class="d-flex d-md-none p-0" v-if="entry_mode_simple">
<i class="btn fas fa-cart-plus" @click="addItem"/>
</b-button>
</b-row> </b-row>
<!-- shopping list table --> <!-- shopping list table -->
<div v-if="items && items.length > 0"> <div v-if="items && items.length > 0">
<div v-for="(done, x) in Sections" :key="x"> <div v-for="(done, x) in Sections" :key="x">
<div v-if="x == 'true'"> <div v-if="x == 'true'">
<hr /> <h4 class="pl-2 pl-md-0">{{ $t("Completed") }}</h4>
<hr />
<h4>{{ $t("Completed") }}</h4>
</div> </div>
<div v-for="(s, i) in done" :key="i"> <div v-for="(s, i) in done" :key="i">
<h5 v-if="Object.entries(s).length > 0"> <div class="dropdown b-dropdown position-static inline-block" data-html2canvas-ignore="true"
<div class="dropdown b-dropdown position-static inline-block" data-html2canvas-ignore="true"> v-if="Object.entries(s).length > 0">
<button <button
aria-haspopup="true" aria-haspopup="true"
aria-expanded="false" aria-expanded="false"
type="button" type="button"
class="btn dropdown-toggle btn-link text-decoration-none text-dark pr-2 dropdown-toggle-no-caret" class="btn dropdown-toggle btn-link text-decoration-none text-dark pr-2 dropdown-toggle-no-caret"
@click.stop="openContextMenu($event, s, true)" @click.stop="openContextMenu($event, s, true)">
>
<i class="fas fa-ellipsis-v fa-lg"></i> <i class="fas fa-ellipsis-v fa-lg"></i>
</button> </button>
@@ -87,16 +117,21 @@
:aria-expanded="'true' ? x == 'false' : 'true'" :aria-expanded="'true' ? x == 'false' : 'true'"
> >
<i class="fa fa-chevron-right rotate"/> <i class="fa fa-chevron-right rotate"/>
{{ i }} <span class="h5 ml-2 text-secondary">{{ i }}</span>
</b-button> </b-button>
</div> </div>
</h5>
<div class="collapse" :id="'section-' + sectionID(x, i)" visible role="tabpanel" :class="{ show: x == 'false' }"> <div class="collapse" :id="'section-' + sectionID(x, i)" visible role="tabpanel"
:class="{ show: x == 'false' }">
<!-- passing an array of values to the table grouped by Food --> <!-- passing an array of values to the table grouped by Food -->
<transition-group name="slider-fade" mode="out-in">
<div v-for="(entries, x) in Object.entries(s)" :key="x"> <div v-for="(entries, x) in Object.entries(s)" :key="x">
<ShoppingLineItem :entries="entries[1]" :groupby="group_by" @open-context-menu="openContextMenu" @update-checkbox="updateChecked" />
<ShoppingLineItem :entries="entries[1]" :groupby="group_by"
@open-context-menu="openContextMenu" @update-checkbox="updateChecked"/>
</div> </div>
</transition-group>
</div> </div>
</div> </div>
</div> </div>
@@ -108,7 +143,7 @@
</b-tab> </b-tab>
<!-- recipe tab --> <!-- recipe tab -->
<b-tab :title="$t('Recipes')"> <b-tab :title="$t('Recipes')">
<table class="table w-75"> <table class="table w-100">
<thead> <thead>
<tr> <tr>
<th scope="col">{{ $t("Meal_Plan") }}</th> <th scope="col">{{ $t("Meal_Plan") }}</th>
@@ -121,10 +156,12 @@
<td>{{ r.recipe_mealplan.name }}</td> <td>{{ r.recipe_mealplan.name }}</td>
<td>{{ r.recipe_mealplan.recipe_name }}</td> <td>{{ r.recipe_mealplan.recipe_name }}</td>
<td class="block-inline"> <td class="block-inline">
<b-form-input min="1" type="number" :debounce="300" :value="r.recipe_mealplan.servings" @input="updateServings($event, r.list_recipe)"></b-form-input> <b-form-input min="1" type="number" :debounce="300" :value="r.recipe_mealplan.servings"
@input="updateServings($event, r.list_recipe)"></b-form-input>
</td> </td>
<td> <td>
<i class="btn text-danger fas fa-trash fa-lg px-2 border-0" variant="link" :title="$t('Delete')" @click="deleteRecipe($event, r.list_recipe)" /> <i class="btn text-danger fas fa-trash fa-lg px-2 border-0" variant="link" :title="$t('Delete')"
@click="deleteRecipe($event, r.list_recipe)"/>
</td> </td>
</tr> </tr>
</table> </table>
@@ -151,7 +188,8 @@
}) })
" "
> >
<i class="btn fas fa-plus-circle fa-lg px-0" :class="new_supermarket.entrymode ? 'text-success' : 'text-muted'" /> <i class="btn fas fa-plus-circle fa-lg px-0"
:class="new_supermarket.entrymode ? 'text-success' : 'text-muted'"/>
</b-button> </b-button>
</h4> </h4>
</template> </template>
@@ -167,7 +205,9 @@
> >
<div class="input-group"> <div class="input-group">
<b-form-input type="text" :placeholder="$t('SupermarketName')" v-model="new_supermarket.value"/> <b-form-input type="text" :placeholder="$t('SupermarketName')" v-model="new_supermarket.value"/>
<b-button class="input-group-append" variant="success" @click="addSupermarket"><i class="pr-2 pt-1 fas fa-save"></i> {{ $t("Save") }}</b-button> <b-button class="input-group-append" variant="success" @click="addSupermarket"><i
class="pr-2 pt-1 fas fa-save"></i> {{ $t("Save") }}
</b-button>
</div> </div>
</b-card> </b-card>
@@ -176,18 +216,10 @@
<b-card-title> <b-card-title>
<div class="row"> <div class="row">
<div class="col">{{ s.name }}</div> <div class="col">{{ s.name }}</div>
<div class="col-auto text-right ml-auto"> <div class="col-auto text-right ml-auto">
<b-button <b-button variant="link"
variant="link"
class="p-0 m-0" class="p-0 m-0"
@click=" @click="s.editmode = !s.editmode;new_category.entrymode = false;new_supermarket.entrymode = false;editSupermarket(s)">
s.editmode = !s.editmode
new_category.entrymode = false
new_supermarket.entrymode = false
editSupermarket(s)
"
>
<i class="btn fas fa-edit fa-lg px-0" :class="s.editmode ? 'text-success' : 'text-muted'"/> <i class="btn fas fa-edit fa-lg px-0" :class="s.editmode ? 'text-success' : 'text-muted'"/>
</b-button> </b-button>
<b-button variant="link" class="p-0 m-0" @click="deleteSupermarket(s)"> <b-button variant="link" class="p-0 m-0" @click="deleteSupermarket(s)">
@@ -196,9 +228,9 @@
</div> </div>
</div> </div>
</b-card-title> </b-card-title>
<b-card-body class="py-0"> <b-card-body class="py-0">
<generic-pill :item_list="s.category_to_supermarket" label="category::name" color="info"></generic-pill> <generic-pill :item_list="s.category_to_supermarket" label="category::name"
color="info"></generic-pill>
</b-card-body> </b-card-body>
</b-card> </b-card>
</b-card-body> </b-card-body>
@@ -222,7 +254,8 @@
new_supermarket.entrymode = false new_supermarket.entrymode = false
" "
> >
<i class="btn fas fa-plus-circle fa-lg px-0" :class="new_category.entrymode ? 'text-success' : 'text-muted'" /> <i class="btn fas fa-plus-circle fa-lg px-0"
:class="new_category.entrymode ? 'text-success' : 'text-muted'"/>
</b-button> </b-button>
</div> </div>
</div> </div>
@@ -238,12 +271,18 @@
> >
<div class="input-group"> <div class="input-group">
<b-form-input type="text" :placeholder="$t('CategoryName')" v-model="new_category.value"/> <b-form-input type="text" :placeholder="$t('CategoryName')" v-model="new_category.value"/>
<b-button class="input-group-append" variant="success" @click="addCategory"><i class="pr-2 pt-1 fas fa-save"></i> {{ $t("Save") }}</b-button> <b-button class="input-group-append" variant="success" @click="addCategory"><i
class="pr-2 pt-1 fas fa-save"></i> {{ $t("Save") }}
</b-button>
</div> </div>
</b-card> </b-card>
<b-card-sub-title v-if="new_supermarket.editmode" class="pt-0 pb-3">{{ $t("CategoryInstruction") }}</b-card-sub-title> <b-card-sub-title v-if="new_supermarket.editmode" class="pt-0 pb-3">{{
<b-card v-if="new_supermarket.editmode && supermarketCategory.length === 0" class="m-0 p-0 font-weight-bold no-body" border-variant="success" v-bind:key="-1" /> $t("CategoryInstruction")
}}
</b-card-sub-title>
<b-card v-if="new_supermarket.editmode && supermarketCategory.length === 0"
class="m-0 p-0 font-weight-bold no-body" border-variant="success" v-bind:key="-1"/>
<draggable <draggable
class="list-group" class="list-group"
:list="supermarketCategory" :list="supermarketCategory"
@@ -270,7 +309,8 @@
</transition-group> </transition-group>
</draggable> </draggable>
<hr style="height: 2px; background-color: black" v-if="new_supermarket.editmode"/> <hr style="height: 2px; background-color: black" v-if="new_supermarket.editmode"/>
<b-card v-if="new_supermarket.editmode && notSupermarketCategory.length === 0" v-bind:key="-2" class="m-0 p-0 font-weight-bold no-body" border-variant="danger" /> <b-card v-if="new_supermarket.editmode && notSupermarketCategory.length === 0" v-bind:key="-2"
class="m-0 p-0 font-weight-bold no-body" border-variant="danger"/>
<draggable <draggable
class="list-group" class="list-group"
:list="notSupermarketCategory" :list="notSupermarketCategory"
@@ -282,7 +322,8 @@
v-bind="{ animation: 200 }" v-bind="{ animation: 200 }"
> >
<transition-group type="transition" :name="!drag ? 'flip-list' : null"> <transition-group type="transition" :name="!drag ? 'flip-list' : null">
<b-card class="m-0 p-0 font-weight-bold no-body list-group-item" style="cursor: move" v-for="c in notSupermarketCategory" v-bind:key="c.id" :border-variant="'danger'"> <b-card class="m-0 p-0 font-weight-bold no-body list-group-item" style="cursor: move"
v-for="c in notSupermarketCategory" v-bind:key="c.id" :border-variant="'danger'">
{{ categoryName(c) }} {{ categoryName(c) }}
<b-button variant="link" class="p-0 m-0 float-right" @click="deleteCategory(c)"> <b-button variant="link" class="p-0 m-0 float-right" @click="deleteCategory(c)">
<i class="btn fas fa-trash fa-lg px-2 text-muted"/> <i class="btn fas fa-trash fa-lg px-2 text-muted"/>
@@ -297,12 +338,13 @@
<!-- settings tab --> <!-- settings tab -->
<b-tab :title="$t('Settings')"> <b-tab :title="$t('Settings')">
<div class="row justify-content-center"> <div class="row justify-content-center">
<div class="col col-md-4 col-sm-8"> <div class="col-12 col-md-8">
<b-card class="no-body"> <b-card class="no-body">
<div class="row"> <div class="row">
<div class="col col-md-6">{{ $t("mealplan_autoadd_shopping") }}</div> <div class="col col-md-6">{{ $t("mealplan_autoadd_shopping") }}</div>
<div class="col col-md-6 text-right"> <div class="col col-md-6 text-right">
<input type="checkbox" size="sm" v-model="settings.mealplan_autoadd_shopping" @change="saveSettings" /> <input type="checkbox" class="form-control-sm" v-model="settings.mealplan_autoadd_shopping"
@change="saveSettings"/>
</div> </div>
</div> </div>
<div class="row sm mb-3"> <div class="row sm mb-3">
@@ -314,7 +356,8 @@
<div class="row"> <div class="row">
<div class="col col-md-6">{{ $t("mealplan_autoadd_shopping") }}</div> <div class="col col-md-6">{{ $t("mealplan_autoadd_shopping") }}</div>
<div class="col col-md-6 text-right"> <div class="col col-md-6 text-right">
<input type="checkbox" size="sm" v-model="settings.mealplan_autoexclude_onhand" @change="saveSettings" /> <input type="checkbox" class="form-control-sm" v-model="settings.mealplan_autoexclude_onhand"
@change="saveSettings"/>
</div> </div>
</div> </div>
<div class="row sm mb-3"> <div class="row sm mb-3">
@@ -327,7 +370,8 @@
<div class="row"> <div class="row">
<div class="col col-md-6">{{ $t("mealplan_autoinclude_related") }}</div> <div class="col col-md-6">{{ $t("mealplan_autoinclude_related") }}</div>
<div class="col col-md-6 text-right"> <div class="col col-md-6 text-right">
<input type="checkbox" size="sm" v-model="settings.mealplan_autoinclude_related" @change="saveSettings" /> <input type="checkbox" class="form-control-sm" v-model="settings.mealplan_autoinclude_related"
@change="saveSettings"/>
</div> </div>
</div> </div>
<div class="row sm mb-3"> <div class="row sm mb-3">
@@ -343,10 +387,7 @@
<div class="col col-md-6 text-right"> <div class="col col-md-6 text-right">
<generic-multiselect <generic-multiselect
size="sm" size="sm"
@change=" @change="settings.shopping_share = $event.valsaveSettings()"
settings.shopping_share = $event.val
saveSettings()
"
:model="Models.USER" :model="Models.USER"
:initial_selection="settings.shopping_share" :initial_selection="settings.shopping_share"
label="username" label="username"
@@ -365,7 +406,8 @@
<div class="row"> <div class="row">
<div class="col col-md-6">{{ $t("shopping_auto_sync") }}</div> <div class="col col-md-6">{{ $t("shopping_auto_sync") }}</div>
<div class="col col-md-6 text-right"> <div class="col col-md-6 text-right">
<input type="number" size="sm" v-model="settings.shopping_auto_sync" @change="saveSettings" /> <input type="number" class="form-control-sm" v-model="settings.shopping_auto_sync"
@change="saveSettings"/>
</div> </div>
</div> </div>
<div class="row sm mb-3"> <div class="row sm mb-3">
@@ -378,7 +420,8 @@
<div class="row"> <div class="row">
<div class="col col-md-6">{{ $t("shopping_add_onhand") }}</div> <div class="col col-md-6">{{ $t("shopping_add_onhand") }}</div>
<div class="col col-md-6 text-right"> <div class="col col-md-6 text-right">
<input type="checkbox" size="sm" v-model="settings.shopping_add_onhand" @change="saveSettings" /> <input type="checkbox" class="form-control-sm" v-model="settings.shopping_add_onhand"
@change="saveSettings"/>
</div> </div>
</div> </div>
<div class="row sm mb-3"> <div class="row sm mb-3">
@@ -391,7 +434,8 @@
<div class="row"> <div class="row">
<div class="col col-md-6">{{ $t("shopping_recent_days") }}</div> <div class="col col-md-6">{{ $t("shopping_recent_days") }}</div>
<div class="col col-md-6 text-right"> <div class="col col-md-6 text-right">
<input type="number" size="sm" v-model="settings.shopping_recent_days" @change="saveSettings" /> <input type="number" class="form-control-sm" v-model="settings.shopping_recent_days"
@change="saveSettings"/>
</div> </div>
</div> </div>
@@ -405,7 +449,8 @@
<div class="row"> <div class="row">
<div class="col col-md-6">{{ $t("filter_to_supermarket") }}</div> <div class="col col-md-6">{{ $t("filter_to_supermarket") }}</div>
<div class="col col-md-6 text-right"> <div class="col col-md-6 text-right">
<input type="checkbox" size="sm" v-model="settings.filter_to_supermarket" @change="saveSettings" /> <input type="checkbox" class="form-control-sm" v-model="settings.filter_to_supermarket"
@change="saveSettings"/>
</div> </div>
</div> </div>
<div class="row sm mb-3"> <div class="row sm mb-3">
@@ -418,7 +463,8 @@
<div class="row"> <div class="row">
<div class="col col-md-6">{{ $t("default_delay") }}</div> <div class="col col-md-6">{{ $t("default_delay") }}</div>
<div class="col col-md-6 text-right"> <div class="col col-md-6 text-right">
<input type="number" size="sm" min="1" v-model="settings.default_delay" @change="saveSettings" /> <input type="number" class="form-control-sm" min="1" v-model="settings.default_delay"
@change="saveSettings"/>
</div> </div>
</div> </div>
<div class="row sm mb-3"> <div class="row sm mb-3">
@@ -431,7 +477,7 @@
<div class="row"> <div class="row">
<div class="col col-md-6">{{ $t("csv_delim_label") }}</div> <div class="col col-md-6">{{ $t("csv_delim_label") }}</div>
<div class="col col-md-6 text-right"> <div class="col col-md-6 text-right">
<input type="string" size="sm" v-model="settings.csv_delim" @change="saveSettings" /> <input class="form-control-sm" v-model="settings.csv_delim" @change="saveSettings"/>
</div> </div>
</div> </div>
<div class="row sm mb-3"> <div class="row sm mb-3">
@@ -444,7 +490,7 @@
<div class="row"> <div class="row">
<div class="col col-md-6">{{ $t("csv_prefix_label") }}</div> <div class="col col-md-6">{{ $t("csv_prefix_label") }}</div>
<div class="col col-md-6 text-right"> <div class="col col-md-6 text-right">
<input type="string" size="sm" v-model="settings.csv_prefix" @change="saveSettings" /> <input class="form-control-sm" v-model="settings.csv_prefix" @change="saveSettings"/>
</div> </div>
</div> </div>
<div class="row sm mb-3"> <div class="row sm mb-3">
@@ -465,87 +511,81 @@
<b-form-select v-model="group_by" :options="group_by_choices" size="sm"></b-form-select> <b-form-select v-model="group_by" :options="group_by_choices" size="sm"></b-form-select>
</b-form-group> </b-form-group>
<b-form-group v-bind:label="$t('Supermarket')" label-for="popover-input-2" label-cols="6" class="mb-1"> <b-form-group v-bind:label="$t('Supermarket')" label-for="popover-input-2" label-cols="6" class="mb-1">
<b-form-select v-model="selected_supermarket" :options="supermarkets" text-field="name" value-field="id" size="sm"></b-form-select> <b-form-select v-model="selected_supermarket" :options="supermarkets" text-field="name" value-field="id"
size="sm"></b-form-select>
</b-form-group> </b-form-group>
<!-- TODO: shade filters red when they are actually filtering content --> <!-- TODO: shade filters red when they are actually filtering content -->
<b-form-group v-bind:label="$t('ShowDelayed')" label-for="popover-input-3" content-cols="1" class="mb-1"> <b-form-group v-bind:label="$t('ShowDelayed')" label-for="popover-input-3" content-cols="1" class="mb-1">
<b-form-checkbox v-model="show_delay"></b-form-checkbox> <b-form-checkbox v-model="show_delay"></b-form-checkbox>
</b-form-group> </b-form-group>
<b-form-group v-bind:label="$t('ShowUncategorizedFood')" label-for="popover-input-4" content-cols="1" class="mb-1" v-if="!selected_supermarket"> <b-form-group v-bind:label="$t('ShowUncategorizedFood')" label-for="popover-input-4" content-cols="1"
class="mb-1" v-if="!selected_supermarket">
<b-form-checkbox v-model="show_undefined_categories"></b-form-checkbox> <b-form-checkbox v-model="show_undefined_categories"></b-form-checkbox>
</b-form-group> </b-form-group>
<b-form-group v-bind:label="$t('SupermarketCategoriesOnly')" label-for="popover-input-5" content-cols="1" class="mb-1" v-if="selected_supermarket"> <b-form-group v-bind:label="$t('SupermarketCategoriesOnly')" label-for="popover-input-5" content-cols="1"
class="mb-1" v-if="selected_supermarket">
<b-form-checkbox v-model="supermarket_categories_only"></b-form-checkbox> <b-form-checkbox v-model="supermarket_categories_only"></b-form-checkbox>
</b-form-group> </b-form-group>
</div> </div>
<div class="row" style="margin-top: 1vh; min-width: 300px"> <div class="row" style="margin-top: 1vh; min-width: 300px">
<div class="col-12" style="text-align: right"> <div class="col-12" style="text-align: right">
<b-button size="sm" variant="primary" class="mx-1" @click="resetFilters">{{ $t("Reset") }}</b-button> <b-button size="sm" variant="primary" class="mx-1" @click="resetFilters">{{ $t("Reset") }}</b-button>
<b-button size="sm" variant="secondary" class="mr-3" @click="$root.$emit('bv::hide::popover')">{{ $t("Close") }} </b-button> <b-button size="sm" variant="secondary" class="mr-3" @click="$root.$emit('bv::hide::popover')">{{
$t("Close")
}}
</b-button>
</div> </div>
</div> </div>
</b-popover> </b-popover>
<ContextMenu ref="menu"> <ContextMenu ref="menu">
<template #menu="{ contextData }"> <template #menu="{ contextData }">
<ContextMenuItem <ContextMenuItem>
@click=" <b-row class="d-flex align-items-center mr-0">
moveEntry($event, contextData) <b-col cols="6">
$refs.menu.close()
"
>
<b-form-group label-cols="6" content-cols="6" class="text-nowrap m-0 mr-2">
<template #label>
<a class="dropdown-item p-2" href="#"><i class="fas fa-cubes"></i> {{ $t("MoveCategory") }}</a> <a class="dropdown-item p-2" href="#"><i class="fas fa-cubes"></i> {{ $t("MoveCategory") }}</a>
</template> </b-col>
<span @click.prevent.stop @mouseup.prevent.stop> <b-col cols="6 pl-1">
<!-- would like to hide the dropdown value and only display value in button - not sure how to do that --> <b-form-select class="form-control form-control-sm" :options="shopping_categories" text-field="name"
<b-form-select class="mt-2 border-0" :options="shopping_categories" text-field="name" value-field="id" v-model="shopcat"></b-form-select> value-field="id" v-model="shopcat"
</span> @change="moveEntry($event, contextData);$refs.menu.close()"></b-form-select>
</b-form-group> </b-col>
</b-row>
</ContextMenuItem> </ContextMenuItem>
<ContextMenuItem <ContextMenuItem @click="$refs.menu.close();onHand(contextData)">
@click="
$refs.menu.close()
onHand(contextData)
"
>
<a class="dropdown-item p-2" href="#"><i class="fas fa-clipboard-check"></i> {{ $t("OnHand") }}</a> <a class="dropdown-item p-2" href="#"><i class="fas fa-clipboard-check"></i> {{ $t("OnHand") }}</a>
</ContextMenuItem> </ContextMenuItem>
<ContextMenuItem <ContextMenuItem @click="$refs.menu.close();delayThis(contextData)">
@click=" <a class="dropdown-item p-2" href="#"><i class="fas fa-hourglass"></i> {{
$refs.menu.close() $t("DelayFor", {hours: delay})
delayThis(contextData) }}</a>
"
>
<b-form-group label-cols="9" content-cols="3" class="text-nowrap m-0 mr-2">
<template #label>
<a class="dropdown-item p-2" href="#"><i class="far fa-hourglass"></i> {{ $t("DelayFor", { hours: delay }) }}</a>
</template>
<div @click.prevent.stop>
<b-form-input class="mt-2" min="0" type="number" v-model="delay"></b-form-input>
</div>
</b-form-group>
</ContextMenuItem> </ContextMenuItem>
<ContextMenuItem <ContextMenuItem @click="$refs.menu.close();updateChecked({ entries: contextData, checked: true })">
@click="
$refs.menu.close()
updateChecked({ entries: contextData, checked: true })
"
>
<a class="dropdown-item p-2" href="#"><i class="fas fa-check-square"></i> {{ $t("mark_complete") }}</a> <a class="dropdown-item p-2" href="#"><i class="fas fa-check-square"></i> {{ $t("mark_complete") }}</a>
</ContextMenuItem> </ContextMenuItem>
<ContextMenuItem @click="$refs.menu.close();deleteThis(contextData)">
<ContextMenuItem
@click="
$refs.menu.close()
deleteThis(contextData)
"
>
<a class="dropdown-item p-2 text-danger" href="#"><i class="fas fa-trash"></i> {{ $t("Delete") }}</a> <a class="dropdown-item p-2 text-danger" href="#"><i class="fas fa-trash"></i> {{ $t("Delete") }}</a>
</ContextMenuItem> </ContextMenuItem>
</template> </template>
</ContextMenu> </ContextMenu>
<transition name="slided-fade">
<div class="row fixed-bottom p-2 b-1 border-top text-center d-flex d-md-none"
style="background: rgba(255, 255, 255, 0.6)"
v-if="current_tab === 0">
<div class="col-md-3 col-6">
<a class="btn btn-block btn-success shadow-none" @click="entrymode = !entrymode"
><i class="fas fa-cart-plus"></i>
{{ $t("New Entry") }}
</a>
</div>
<div class="col-md-3 col-6">
<a class="btn btn-block btn-secondary shadow-none"
><i class="fas fa-download"></i>
{{ $t("Export") }}
</a>
</div>
</div>
</transition>
</div> </div>
</template> </template>
@@ -576,12 +616,24 @@ let SETTINGS_COOKIE_NAME = "shopping_settings"
export default { export default {
name: "ShoppingListView", name: "ShoppingListView",
mixins: [ApiMixin], mixins: [ApiMixin],
components: { ContextMenu, ContextMenuItem, ShoppingLineItem, GenericMultiselect, GenericPill, draggable, LookupInput, DownloadPDF, DownloadCSV, CopyToClipboard }, components: {
ContextMenu,
ContextMenuItem,
ShoppingLineItem,
GenericMultiselect,
GenericPill,
draggable,
LookupInput,
DownloadPDF,
DownloadCSV,
CopyToClipboard
},
data() { data() {
return { return {
// this.Models and this.Actions inherited from ApiMixin // this.Models and this.Actions inherited from ApiMixin
items: [], items: [],
current_tab: 0,
group_by: "category", group_by: "category",
group_by_choices: ["created_by", "category", "recipe"], group_by_choices: ["created_by", "category", "recipe"],
supermarkets: [], supermarkets: [],
@@ -1019,7 +1071,8 @@ export default {
}) })
.then((entries) => { .then((entries) => {
entries.forEach((x) => { entries.forEach((x) => {
api.destroyShoppingListEntry(x).then((result) => {}) api.destroyShoppingListEntry(x).then((result) => {
})
}) })
}) })
}, },
@@ -1245,15 +1298,18 @@ export default {
-webkit-transition: all 0.25s linear; -webkit-transition: all 0.25s linear;
transition: all 0.25s linear; transition: all 0.25s linear;
} }
.btn[aria-expanded="true"] > .rotate { .btn[aria-expanded="true"] > .rotate {
-moz-transform: rotate(90deg); -moz-transform: rotate(90deg);
-webkit-transform: rotate(90deg); -webkit-transform: rotate(90deg);
transform: rotate(90deg); transform: rotate(90deg);
} }
.float-top { .float-top {
padding-bottom: -3em; padding-bottom: -3em;
margin-bottom: -3em; margin-bottom: -3em;
} }
.float-up { .float-up {
padding-top: -3em; padding-top: -3em;
margin-top: -3em; margin-top: -3em;
@@ -1263,4 +1319,29 @@ export default {
opacity: 0.5; opacity: 0.5;
background: #c8ebfb; background: #c8ebfb;
} }
.slider-fade-enter-active, .slider-fade-leave-active {
transition: all 0.3s ease;
}
.slider-fade-enter, .slider-fade-leave-to
/* .slider-fade-leave-active below version 2.1.8 */
{
transform: translateX(10px);
opacity: 0;
}
.slided-fade-enter-active {
transition: all 0.3s ease;
}
.slided-fade-leave-active {
transition: all 0.1s cubic-bezier(1, 0.5, 0.8, 1);
}
.slided-fade-enter,
.slided-fade-leave-to {
transform: translateY(10px);
opacity: 0;
}
</style> </style>

View File

@@ -8,12 +8,12 @@
:class="{ 'border border-primary': over, shake: isError }" :class="{ 'border border-primary': over, shake: isError }"
:style="{ 'cursor:grab': useDrag }" :style="{ 'cursor:grab': useDrag }"
:draggable="useDrag" :draggable="useDrag"
@[useDrag&&`dragover`].prevent @[useDrag&&`dragover`||``].prevent
@[useDrag&&`dragenter`].prevent @[useDrag&&`dragenter`||``].prevent
@[useDrag&&`dragstart`]="handleDragStart($event)" @[useDrag&&`dragstart`||``]="handleDragStart($event)"
@[useDrag&&`dragenter`]="handleDragEnter($event)" @[useDrag&&`dragenter`||``]="handleDragEnter($event)"
@[useDrag&&`dragleave`]="handleDragLeave($event)" @[useDrag&&`dragleave`||``]="handleDragLeave($event)"
@[useDrag&&`drop`]="handleDragDrop($event)" @[useDrag&&`drop`||``]="handleDragDrop($event)"
> >
<b-row no-gutters> <b-row no-gutters>
<b-col no-gutters class="col-sm-3"> <b-col no-gutters class="col-sm-3">
@@ -27,6 +27,7 @@
<div class="m-0 text-truncate small text-muted" v-if="getFullname">{{ getFullname }}</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="itemList(x)" :label="x.label" :color="x.color" /> <generic-pill v-for="x in itemTags" :key="x.field" :item_list="itemList(x)" :label="x.label" :color="x.color" />
<generic-ordered-pill <generic-ordered-pill
v-for="x in itemOrderedTags" v-for="x in itemOrderedTags"
:key="x.field" :key="x.field"
@@ -37,6 +38,7 @@
:item="item" :item="item"
@finish-action="finishAction" @finish-action="finishAction"
/> />
<div class="mt-auto mb-1" align="right"> <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-if="!item.show_children">{{ item[child_count] }} {{ itemName }}</div>

View File

@@ -1,6 +1,6 @@
<template> <template>
<div> <div>
<b-form-group class="mb-3"> <b-form-group :class="class_list">
<template #label v-if="show_label"> <template #label v-if="show_label">
{{ form.label }} {{ form.label }}
</template> </template>
@@ -44,6 +44,7 @@ export default {
return undefined return undefined
}, },
}, },
class_list: {type: String, default: "mb-3"},
show_label: { type: Boolean, default: true }, show_label: { type: Boolean, default: true },
clear: { type: Number }, clear: { type: Number },
}, },
@@ -82,7 +83,7 @@ export default {
} else { } else {
arrayValues = [{ id: -1, name: this_value }] arrayValues = [{ id: -1, name: this_value }]
} }
if (this.form?.ordered && this.first_run && arrayValues.length > 0) { if (this.form?.ordered && this.first_run) {
return this.flattenItems(arrayValues) return this.flattenItems(arrayValues)
} else { } else {
return arrayValues return arrayValues

View File

@@ -1,55 +1,73 @@
<template> <template>
<div id="shopping_line_item"> <div id="shopping_line_item">
<div class="col-12"> <b-container fluid class="pr-0 pl-1 pl-md-3">
<b-container fluid>
<!-- summary rows --> <!-- summary rows -->
<b-row align-h="start"> <b-row align-h="start">
<b-col cols="12" sm="2"> <b-col cols="1" class="align-items-center d-flex">
<div style="position: static" class="btn-group">
<div class="dropdown b-dropdown position-static inline-block" data-html2canvas-ignore="true"> <div class="dropdown b-dropdown position-static inline-block" data-html2canvas-ignore="true">
<button <button
aria-haspopup="true" aria-haspopup="true"
aria-expanded="false" aria-expanded="false"
type="button" type="button"
class="btn dropdown-toggle btn-link text-decoration-none text-body pr-1 dropdown-toggle-no-caret" class="btn dropdown-toggle btn-link text-decoration-none text-body pr-1 dropdown-toggle-no-caret"
@click.stop="$emit('open-context-menu', $event, entries)" @click.stop="$emit('open-context-menu', $event, entries)">
>
<i class="fas fa-ellipsis-v fa-lg"></i> <i class="fas fa-ellipsis-v fa-lg"></i>
</button> </button>
</div> </div>
<input type="checkbox" class="text-right mx-3 mt-2" :checked="formatChecked" @change="updateChecked" :key="entries[0].id" /> </b-col>
<b-col cols="1" class="justify-content-center align-items-center d-none d-md-flex">
<input type="checkbox" class="form-control form-control-sm checkbox-control" :checked="formatChecked"
@change="updateChecked"
:key="entries[0].id"/>
</b-col>
<b-col cols="8" md="9">
<b-row class="d-flex h-100" @click.stop="$emit('open-context-menu', $event, entries)">
<b-col cols="6" md="6" class="d-flex align-items-center" v-if="Object.entries(formatAmount).length == 1">
<div><strong>{{ Object.entries(formatAmount)[0][1] }}</strong> &ensp;
{{ Object.entries(formatAmount)[0][0] }}
</div> </div>
</b-col> </b-col>
<b-col cols="12" sm="10"> <b-col cols="6" md="6" class="d-flex flex-column" v-if="Object.entries(formatAmount).length != 1">
<b-row> <div class="small" v-for="(x, i) in Object.entries(formatAmount)" :key="i">{{ x[1] }} &ensp;
<b-col cols="6" sm="3"> {{ x[0] }}
<div v-if="Object.entries(formatAmount).length == 1">{{ Object.entries(formatAmount)[0][1] }} &ensp; {{ Object.entries(formatAmount)[0][0] }}</div> </div>
<div class="small" v-else v-for="(x, i) in Object.entries(formatAmount)" :key="i">{{ x[1] }} &ensp; {{ x[0] }}</div>
</b-col> </b-col>
<b-col cols="6" sm="7"> <b-col cols="6" md="3" class="align-items-center d-flex pl-0 pr-0 pl-md-2 pr-md-2">
{{ formatFood }} {{ formatFood }}
</b-col> </b-col>
<b-col cols="6" sm="2" data-html2canvas-ignore="true"> <b-col cols="3" data-html2canvas-ignore="true" class="align-items-center d-none d-md-flex">
<b-button size="sm" @click="showDetails = !showDetails" class="mr-2" variant="link"> <b-button size="sm" @click="showDetails = !showDetails" class="p-0 mr-0 mr-md-2 p-md-2" variant="link">
<div class="text-nowrap">{{ showDetails ? "Hide" : "Show" }} Details</div> <div class="text-nowrap"><i class="fa fa-chevron-right rotate"
:class="showDetails ? 'rotated' : ''"></i> <span
class="d-none d-md-inline-block">{{ $t('Details') }}</span>
</div>
</b-button> </b-button>
</b-col> </b-col>
</b-row> </b-row>
</b-col> </b-col>
<b-col cols="3" md="2" class="justify-content-start align-items-center d-flex d-md-none">
<b-button size="sm" @click="showDetails = !showDetails" class="d-inline-block d-md-none p-0" variant="link">
<div class="text-nowrap"><i class="fa fa-chevron-right rotate" :class="showDetails ? 'rotated' : ''"></i>
</div>
</b-button>
<input type="checkbox" class="form-control form-control-sm checkbox-control-mobile" :checked="formatChecked"
@change="updateChecked"
:key="entries[0].id"/>
</b-col>
</b-row> </b-row>
<b-row align-h="center"> <b-row align-h="center" class="d-none d-md-flex">
<b-col cols="12"> <b-col cols="12">
<div class="small text-muted text-truncate">{{ formatHint }}</div> <div class="small text-muted text-truncate">{{ formatHint }}</div>
</b-col> </b-col>
</b-row> </b-row>
</b-container> </b-container>
<!-- detail rows --> <!-- detail rows -->
<div class="card no-body" v-if="showDetails"> <div class="card no-body mb-1 pt-2 align-content-center ml-2" v-if="showDetails">
<b-container fluid> <b-container fluid>
<div v-for="e in entries" :key="e.id"> <div v-for="(e, x) in entries" :key="e.id">
<b-row class="ml-2 small"> <b-row class="small justify-content-around">
<b-col cols="6" md="4" class="overflow-hidden text-nowrap"> <b-col cols="auto" md="4" class="overflow-hidden text-nowrap">
<button <button
aria-haspopup="true" aria-haspopup="true"
aria-expanded="false" aria-expanded="false"
@@ -57,58 +75,72 @@
class="btn btn-link btn-sm m-0 p-0" class="btn btn-link btn-sm m-0 p-0"
style="text-overflow: ellipsis" style="text-overflow: ellipsis"
@click.stop="openRecipeCard($event, e)" @click.stop="openRecipeCard($event, e)"
@mouseover="openRecipeCard($event, e)" @mouseover="openRecipeCard($event, e)">
>
{{ formatOneRecipe(e) }} {{ formatOneRecipe(e) }}
</button> </button>
</b-col> </b-col>
<b-col cols="6" md="4" class="col-md-4 text-muted">{{ formatOneMealPlan(e) }}</b-col> <b-col cols="auto" md="4" class="text-muted">{{ formatOneMealPlan(e) }}</b-col>
<b-col cols="12" md="4" class="col-md-4 text-muted text-right overflow-hidden text-nowrap"> <b-col cols="auto" md="4" class="text-muted text-right overflow-hidden text-nowrap">
{{ formatOneCreatedBy(e) }} {{ formatOneCreatedBy(e) }}
<div v-if="formatOneCompletedAt(e)">{{ formatOneCompletedAt(e) }}</div> <div v-if="formatOneCompletedAt(e)">{{ formatOneCompletedAt(e) }}</div>
</b-col> </b-col>
</b-row> </b-row>
<b-row class="ml-2 light">
<b-col cols="12" sm="2">
<div style="position: static" class="btn-group">
<b-row align-h="start">
<b-col cols="1" class="align-items-center d-flex">
<div class="dropdown b-dropdown position-static inline-block" data-html2canvas-ignore="true"> <div class="dropdown b-dropdown position-static inline-block" data-html2canvas-ignore="true">
<button <button
aria-haspopup="true" aria-haspopup="true"
aria-expanded="false" aria-expanded="false"
type="button" type="button"
class="btn dropdown-toggle btn-link text-decoration-none text-body pr-1 dropdown-toggle-no-caret" class="btn dropdown-toggle btn-link text-decoration-none text-body pr-1 dropdown-toggle-no-caret"
@click.stop="$emit('open-context-menu', $event, e)" @click.stop="$emit('open-context-menu', $event, e)">
>
<i class="fas fa-ellipsis-v fa-lg"></i> <i class="fas fa-ellipsis-v fa-lg"></i>
</button> </button>
</div> </div>
<input type="checkbox" class="text-right mx-3 mt-2" :checked="e.checked" @change="updateChecked($event, e)" /> </b-col>
<b-col cols="1" class="justify-content-center align-items-center d-none d-md-flex">
<input type="checkbox" class="form-control form-control-sm checkbox-control" :checked="formatChecked"
@change="updateChecked"
:key="entries[0].id"/>
</b-col>
<b-col cols="8" md="9">
<b-row class="d-flex justify-content-around">
<b-col cols="6" md="6" class="d-flex align-items-center">
<div>{{ formatOneAmount(e) }} &ensp;
{{ formatOneUnit(e) }}
</div> </div>
</b-col> </b-col>
<b-col cols="12" sm="10">
<b-row>
<b-col cols="2" sm="2" md="1" class="text-nowrap">{{ formatOneAmount(e) }}</b-col>
<b-col cols="10" sm="4" md="2" class="text-nowrap">{{ formatOneUnit(e) }}</b-col>
<b-col cols="12" sm="6" md="4" class="text-nowrap">{{ formatOneFood(e) }}</b-col> <b-col cols="6" md="3" class="align-items-center d-flex pl-0 pr-0 pl-md-2 pr-md-2">
{{ formatOneFood(e) }}
<b-col cols="12" sm="6" md="5"> </b-col>
<div class="small" v-for="(n, i) in formatOneNote(e)" :key="i">{{ n }}</div> <b-col cols="12" class="d-flex d-md-none">
<div class="small text-muted text-truncate" v-for="(n, i) in formatOneNote(e)" :key="i">{{ n }}</div>
</b-col> </b-col>
</b-row> </b-row>
</b-col> </b-col>
<b-col cols="3" md="2" class="justify-content-start align-items-center d-flex d-md-none">
<input type="checkbox" class="form-control form-control-sm checkbox-control-mobile"
:checked="formatChecked"
@change="updateChecked"
:key="entries[0].id"/>
</b-col>
</b-row> </b-row>
<hr class="w-75" v-if="x !== entries.length -1"/>
<hr class="w-75" /> <div class="pb-4" v-if="x === entries.length -1"></div>
</div> </div>
</b-container> </b-container>
</div> </div>
<hr class="m-1" /> <hr class="m-1" v-if="!showDetails"/>
</div>
<ContextMenu ref="recipe_card" triggers="click, hover" :title="$t('Filters')" style="max-width: 300"> <ContextMenu ref="recipe_card" triggers="click, hover" :title="$t('Filters')" style="max-width: 300">
<template #menu="{ contextData }" v-if="recipe"> <template #menu="{ contextData }" v-if="recipe">
<ContextMenuItem><RecipeCard :recipe="contextData" :detail="false"></RecipeCard></ContextMenuItem> <ContextMenuItem>
<RecipeCard :recipe="contextData" :detail="false"></RecipeCard>
</ContextMenuItem>
<ContextMenuItem @click="$refs.menu.close()"> <ContextMenuItem @click="$refs.menu.close()">
<b-form-group label-cols="9" content-cols="3" class="text-nowrap m-0 mr-2"> <b-form-group label-cols="9" content-cols="3" class="text-nowrap m-0 mr-2">
<template #label> <template #label>
@@ -223,7 +255,10 @@ export default {
if (!datetime) { if (!datetime) {
return return
} }
return Intl.DateTimeFormat(window.navigator.language, { dateStyle: "short", timeStyle: "short" }).format(Date.parse(datetime)) return Intl.DateTimeFormat(window.navigator.language, {
dateStyle: "short",
timeStyle: "short"
}).format(Date.parse(datetime))
}, },
formatOneAmount: function (item) { formatOneAmount: function (item) {
return item?.amount ?? 1 return item?.amount ?? 1
@@ -296,4 +331,28 @@ export default {
/* left: 0; top: 50%; width: 100%; /* …with the top across the middle */ /* left: 0; top: 50%; width: 100%; /* …with the top across the middle */
/* border-bottom: 1px solid #000; /* …and with a border on the top */ /* border-bottom: 1px solid #000; /* …and with a border on the top */
/* } */ /* } */
.checkbox-control {
font-size: 0.6rem
}
.checkbox-control-mobile {
font-size: 1rem
}
.rotate {
-moz-transition: all 0.25s linear;
-webkit-transition: all 0.25s linear;
transition: all 0.25s linear;
}
.rotated {
-moz-transform: rotate(90deg);
-webkit-transform: rotate(90deg);
transform: rotate(90deg);
}
.unit-badge-lg {
font-size: 1rem !important;
font-weight: 500 !important;
}
</style> </style>