Merge branch 'develop' of github.com:ndbeals/tandoor_recipes into develop

This commit is contained in:
Nathan Beals
2022-04-23 00:11:08 -04:00
31 changed files with 1312 additions and 698 deletions

View File

@@ -124,7 +124,7 @@ class IngredientParser:
def parse_amount(self, x): def parse_amount(self, x):
amount = 0 amount = 0
unit = '' unit = None
note = '' note = ''
did_check_frac = False did_check_frac = False
@@ -155,7 +155,7 @@ class IngredientParser:
except ValueError: except ValueError:
unit = x[end:] unit = x[end:]
if unit.startswith('(') or unit.startswith('-'): # i dont know any unit that starts with ( or - so its likely an alternative like 1L (500ml) Water or 2-3 if unit is not None and (unit.startswith('(') or unit.startswith('-')): # i dont know any unit that starts with ( or - so its likely an alternative like 1L (500ml) Water or 2-3
unit = '' unit = ''
note = x note = x
return amount, unit, note return amount, unit, note
@@ -230,7 +230,7 @@ class IngredientParser:
# a fraction for the amount # a fraction for the amount
if len(tokens) > 2: if len(tokens) > 2:
try: try:
if not unit == '': if unit is not None:
# a unit is already found, no need to try the second argument for a fraction # a unit is already found, no need to try the second argument for a fraction
# probably not the best method to do it, but I didn't want to make an if check and paste the exact same thing in the else as already is in the except # noqa: E501 # probably not the best method to do it, but I didn't want to make an if check and paste the exact same thing in the else as already is in the except # noqa: E501
raise ValueError raise ValueError
@@ -252,7 +252,7 @@ class IngredientParser:
# try to use second argument as unit and everything else as ingredient, use everything as ingredient if it fails # noqa: E501 # try to use second argument as unit and everything else as ingredient, use everything as ingredient if it fails # noqa: E501
try: try:
ingredient, note = self.parse_ingredient(tokens[2:]) ingredient, note = self.parse_ingredient(tokens[2:])
if unit == '': if unit is None:
unit = tokens[1] unit = tokens[1]
else: else:
note = tokens[1] note = tokens[1]

View File

@@ -10,33 +10,33 @@ def test_ingredient_parser():
"4 l Wasser": (4, "l", "Wasser", ""), "4 l Wasser": (4, "l", "Wasser", ""),
"½l Wasser": (0.5, "l", "Wasser", ""), "½l Wasser": (0.5, "l", "Wasser", ""),
"⅛ Liter Sauerrahm": (0.125, "Liter", "Sauerrahm", ""), "⅛ Liter Sauerrahm": (0.125, "Liter", "Sauerrahm", ""),
"5 Zwiebeln": (5, "", "Zwiebeln", ""), "5 Zwiebeln": (5, None, "Zwiebeln", ""),
"3 Zwiebeln, gehackt": (3, "", "Zwiebeln", "gehackt"), "3 Zwiebeln, gehackt": (3, None, "Zwiebeln", "gehackt"),
"5 Zwiebeln (gehackt)": (5, "", "Zwiebeln", "gehackt"), "5 Zwiebeln (gehackt)": (5, None, "Zwiebeln", "gehackt"),
"1 Zwiebel(n)": (1, "", "Zwiebel(n)", ""), "1 Zwiebel(n)": (1, None, "Zwiebel(n)", ""),
"4 1/2 Zwiebeln": (4.5, "", "Zwiebeln", ""), "4 1/2 Zwiebeln": (4.5, None, "Zwiebeln", ""),
"4 ½ Zwiebeln": (4.5, "", "Zwiebeln", ""), "4 ½ Zwiebeln": (4.5, None, "Zwiebeln", ""),
"1/2 EL Mehl": (0.5, "EL", "Mehl", ""), "1/2 EL Mehl": (0.5, "EL", "Mehl", ""),
"1/2 Zwiebel": (0.5, "", "Zwiebel", ""), "1/2 Zwiebel": (0.5, None, "Zwiebel", ""),
"1/5g Mehl, gesiebt": (0.2, "g", "Mehl", "gesiebt"), "1/5g Mehl, gesiebt": (0.2, "g", "Mehl", "gesiebt"),
"1/2 Zitrone, ausgepresst": (0.5, "", "Zitrone", "ausgepresst"), "1/2 Zitrone, ausgepresst": (0.5, None, "Zitrone", "ausgepresst"),
"etwas Mehl": (0, "", "etwas Mehl", ""), "etwas Mehl": (0, None, "etwas Mehl", ""),
"Öl zum Anbraten": (0, "", "Öl zum Anbraten", ""), "Öl zum Anbraten": (0, None, "Öl zum Anbraten", ""),
"n. B. Knoblauch, zerdrückt": (0, "", "n. B. Knoblauch", "zerdrückt"), "n. B. Knoblauch, zerdrückt": (0, None, "n. B. Knoblauch", "zerdrückt"),
"Kräuter, mediterrane (Oregano, Rosmarin, Basilikum)": ( "Kräuter, mediterrane (Oregano, Rosmarin, Basilikum)": (
0, "", "Kräuter, mediterrane", "Oregano, Rosmarin, Basilikum"), 0, None, "Kräuter, mediterrane", "Oregano, Rosmarin, Basilikum"),
"600 g Kürbisfleisch (Hokkaido), geschält, entkernt und geraspelt": ( "600 g Kürbisfleisch (Hokkaido), geschält, entkernt und geraspelt": (
600, "g", "Kürbisfleisch (Hokkaido)", "geschält, entkernt und geraspelt"), 600, "g", "Kürbisfleisch (Hokkaido)", "geschält, entkernt und geraspelt"),
"Muskat": (0, "", "Muskat", ""), "Muskat": (0, None, "Muskat", ""),
"200 g Mehl, glattes": (200, "g", "Mehl", "glattes"), "200 g Mehl, glattes": (200, "g", "Mehl", "glattes"),
"1 Ei(er)": (1, "", "Ei(er)", ""), "1 Ei(er)": (1, None, "Ei(er)", ""),
"1 Prise(n) Salz": (1, "Prise(n)", "Salz", ""), "1 Prise(n) Salz": (1, "Prise(n)", "Salz", ""),
"etwas Wasser, lauwarmes": (0, "", "etwas Wasser", "lauwarmes"), "etwas Wasser, lauwarmes": (0, None, "etwas Wasser", "lauwarmes"),
"Strudelblätter, fertige, für zwei Strudel": (0, "", "Strudelblätter", "fertige, für zwei Strudel"), "Strudelblätter, fertige, für zwei Strudel": (0,None, "Strudelblätter", "fertige, für zwei Strudel"),
"barrel-aged Bourbon": (0, "", "barrel-aged Bourbon", ""), "barrel-aged Bourbon": (0, None, "barrel-aged Bourbon", ""),
"golden syrup": (0, "", "golden syrup", ""), "golden syrup": (0, None, "golden syrup", ""),
"unsalted butter, for greasing": (0, "", "unsalted butter", "for greasing"), "unsalted butter, for greasing": (0, None, "unsalted butter", "for greasing"),
"unsalted butter , for greasing": (0, "", "unsalted butter", "for greasing"), # trim "unsalted butter , for greasing": (0, None, "unsalted butter", "for greasing"), # trim
"1 small sprig of fresh rosemary": (1, "small", "sprig of fresh rosemary", ""), "1 small sprig of fresh rosemary": (1, "small", "sprig of fresh rosemary", ""),
# does not always work perfectly! # does not always work perfectly!
"75 g fresh breadcrumbs": (75, "g", "fresh breadcrumbs", ""), "75 g fresh breadcrumbs": (75, "g", "fresh breadcrumbs", ""),
@@ -49,7 +49,7 @@ def test_ingredient_parser():
"1 Zwiebel gehackt": (1, "Zwiebel", "gehackt", ""), "1 Zwiebel gehackt": (1, "Zwiebel", "gehackt", ""),
"1 EL Kokosöl": (1, "EL", "Kokosöl", ""), "1 EL Kokosöl": (1, "EL", "Kokosöl", ""),
"0.5 paket jäst (à 50 g)": (0.5, "paket", "jäst", "à 50 g"), "0.5 paket jäst (à 50 g)": (0.5, "paket", "jäst", "à 50 g"),
"ägg": (0, "", "ägg", ""), "ägg": (0, None, "ägg", ""),
"50 g smör eller margarin": (50, "g", "smör eller margarin", ""), "50 g smör eller margarin": (50, "g", "smör eller margarin", ""),
"3,5 l Wasser": (3.5, "l", "Wasser", ""), "3,5 l Wasser": (3.5, "l", "Wasser", ""),
"3.5 l Wasser": (3.5, "l", "Wasser", ""), "3.5 l Wasser": (3.5, "l", "Wasser", ""),
@@ -70,4 +70,4 @@ def test_ingredient_parser():
for key, val in expectations.items(): for key, val in expectations.items():
count += 1 count += 1
parsed = ingredient_parser.parse(key) parsed = ingredient_parser.parse(key)
assert val == parsed assert parsed == val

View File

@@ -594,6 +594,7 @@ class IngredientViewSet(viewsets.ModelViewSet):
queryset = Ingredient.objects queryset = Ingredient.objects
serializer_class = IngredientSerializer serializer_class = IngredientSerializer
permission_classes = [CustomIsUser] permission_classes = [CustomIsUser]
pagination_class = DefaultPagination
def get_serializer_class(self): def get_serializer_class(self):
if self.request and self.request.query_params.get('simple', False): if self.request and self.request.query_params.get('simple', False):
@@ -1193,6 +1194,11 @@ def recipe_from_source(request):
'error': True, 'error': True,
'msg': _('Connection Refused.') 'msg': _('Connection Refused.')
}, status=400) }, status=400)
except requests.exceptions.MissingSchema:
return JsonResponse({
'error': True,
'msg': _('Bad URL Schema.')
}, status=400)
recipe_json, recipe_tree, recipe_html, recipe_images = get_recipe_from_source(data, url, request) recipe_json, recipe_tree, recipe_html, recipe_images = get_recipe_from_source(data, url, request)
if len(recipe_tree) == 0 and len(recipe_json) == 0: if len(recipe_tree) == 0 and len(recipe_json) == 0:
return JsonResponse({ return JsonResponse({

View File

@@ -58,3 +58,7 @@ apache:
I used two paths `<sub path>` and `<www path>` for simplicity. In my case I have `<sub path> = recipes` and `<www path> = serve/recipes`. One could also change the matching rules of traefik to have everything under one path. I used two paths `<sub path>` and `<www path>` for simplicity. In my case I have `<sub path> = recipes` and `<www path> = serve/recipes`. One could also change the matching rules of traefik to have everything under one path.
I left out the TLS config in this example for simplicity. I left out the TLS config in this example for simplicity.
## WSL
If you want to install Tandoor on the Windows Subsystem for Linux you can find a detailed post herre https://github.com/TandoorRecipes/recipes/issues/1733

View File

@@ -1,4 +1,4 @@
Django==3.2.12 Django==3.2.13
cryptography==36.0.2 cryptography==36.0.2
django-annoying==0.10.6 django-annoying==0.10.6
django-autocomplete-light==3.9.4 django-autocomplete-light==3.9.4

View File

@@ -130,10 +130,10 @@ export default {
.then((result) => { .then((result) => {
let new_book = result.data let new_book = result.data
this.refreshData() this.refreshData()
StandardToasts.makeStandardToast(StandardToasts.SUCCESS_CREATE) StandardToasts.makeStandardToast(this,StandardToasts.SUCCESS_CREATE)
}) })
.catch((error) => { .catch((err) => {
StandardToasts.makeStandardToast(StandardToasts.FAIL_CREATE) StandardToasts.makeStandardToast(this,StandardToasts.FAIL_CREATE, err)
}) })
}, },
appendRecipeFilter: function (page, book) { appendRecipeFilter: function (page, book) {

View File

@@ -117,8 +117,7 @@ export default {
this.recipe_list.push(response.data) this.recipe_list.push(response.data)
}) })
.catch((err) => { .catch((err) => {
console.log(err) StandardToasts.makeStandardToast(this,StandardToasts.FAIL_FETCH, err)
StandardToasts.makeStandardToast(StandardToasts.FAIL_FETCH)
}) })
// .then((e) => this.searchRecipes("")) // .then((e) => this.searchRecipes(""))
}, },
@@ -133,7 +132,7 @@ export default {
// }) // })
// .catch((err) => { // .catch((err) => {
// console.log(err) // console.log(err)
// StandardToasts.makeStandardToast(StandardToasts.FAIL_FETCH) // StandardToasts.makeStandardToast(this,StandardToasts.FAIL_FETCH)
// }) // })
// }, // },

View File

@@ -17,8 +17,10 @@
<b-card no-body> <b-card no-body>
<b-card-header header-tag="header" class="p-1" role="tab"> <b-card-header header-tag="header" class="p-1" role="tab">
<b-col cols="12" md="6" offset="0" offset-md="3"> <b-col cols="12" md="6" offset="0" offset-md="3">
<b-button block v-b-toggle.id_accordion_url variant="info">Website</b-button> <b-button block v-b-toggle.id_accordion_url variant="info">{{
<!-- TODO localize --> $t('Website')
}}
</b-button>
</b-col> </b-col>
</b-card-header> </b-card-header>
<b-collapse id="id_accordion_url" visible accordion="url_import_accordion" <b-collapse id="id_accordion_url" visible accordion="url_import_accordion"
@@ -30,7 +32,7 @@
<b-checkbox v-model="import_multiple" switch><span <b-checkbox v-model="import_multiple" switch><span
v-if="import_multiple">Multiple Recipes</span><span v-if="import_multiple">Multiple Recipes</span><span
v-if="!import_multiple">Single Recipe</span></b-checkbox> v-if="!import_multiple">Single Recipe</span></b-checkbox>
<!-- TODO localize --> <!-- TODO localize or maybe icons ? -->
</div> </div>
</div> </div>
<b-input-group class="mt-2" :class="{ bounce: empty_input }" <b-input-group class="mt-2" :class="{ bounce: empty_input }"
@@ -38,8 +40,7 @@
<b-input <b-input
class="form-control form-control-lg form-control-borderless form-control-search" class="form-control form-control-lg form-control-borderless form-control-search"
v-model="website_url" v-model="website_url"
placeholder="Website URL" @paste="onURLPaste"></b-input> placeholder="https://..." @paste="onURLPaste"></b-input>
<!-- TODO localize -->
<b-input-group-append> <b-input-group-append>
<b-button variant="primary" <b-button variant="primary"
@click="loadRecipe(website_url,false,undefined)"><i @click="loadRecipe(website_url,false,undefined)"><i
@@ -47,14 +48,14 @@
</b-button> </b-button>
</b-input-group-append> </b-input-group-append>
</b-input-group> </b-input-group>
<b-textarea rows="10" placeholder="Enter one URL per line" <b-textarea rows="10" :placeholder="$t('one_url_per_line')"
v-model="website_url_list" v-model="website_url_list"
v-if="import_multiple"> <!-- TODO localize --> v-if="import_multiple">
</b-textarea> </b-textarea>
<b-button class="float-right" v-if="import_multiple" <b-button class="float-right" v-if="import_multiple"
:disabled="website_url_list.length < 1" :disabled="website_url_list.length < 1"
@click="autoImport()">Import @click="autoImport()">{{ $t('Import') }}
</b-button> </b-button>
<div class="row mt-2"> <!-- TODO remove --> <div class="row mt-2"> <!-- TODO remove -->
@@ -89,7 +90,7 @@
<b-card-header header-tag="header" class="p-1" role="tab"> <b-card-header header-tag="header" class="p-1" role="tab">
<b-col cols="12" md="6" offset="0" offset-md="3"> <b-col cols="12" md="6" offset="0" offset-md="3">
<b-button block v-b-toggle.id_accordion_add_options variant="info" <b-button block v-b-toggle.id_accordion_add_options variant="info"
:disabled="recipe_json === undefined">Options :disabled="recipe_json === undefined">{{ $t('Options') }}
</b-button> </b-button>
</b-col> </b-col>
</b-card-header> </b-card-header>
@@ -127,9 +128,11 @@
<div class="row mt-1"> <div class="row mt-1">
<div class="col col-md-12 text-center"> <div class="col col-md-12 text-center">
<small class="text-muted">Click the image you want to import for this <small class="text-muted">{{ $t('click_image_import') }}</small><br/>
recipe</small> <!-- TODO localize --> <span
<span v-if="recipe_images.length === 0">No additional images found in source.</span> v-if="recipe_images.length === 0">{{
$t('no_more_images_found')
}}</span>
<div class="scrolling-wrapper-flexbox"> <div class="scrolling-wrapper-flexbox">
<div class="wrapper-card" v-for="i in recipe_images" <div class="wrapper-card" v-for="i in recipe_images"
v-bind:key="i" v-bind:key="i"
@@ -147,7 +150,7 @@
<b-card no-body> <b-card no-body>
<b-card-title> <b-card-title>
<div class="clearfix"> <div class="clearfix">
<span class="float-left h5">Keywords</span> <span class="float-left h5">{{ $t('Keywords') }}</span>
<b-button-group class="float-right"> <b-button-group class="float-right">
<b-button class="float-right" variant="primary" <b-button class="float-right" variant="primary"
@click="$set(recipe_json, 'keywords', recipe_json.keywords.map(x => {x.show = true; return x}))"> @click="$set(recipe_json, 'keywords', recipe_json.keywords.map(x => {x.show = true; return x}))">
@@ -194,7 +197,7 @@
<b-card-header header-tag="header" class="p-1" role="tab"> <b-card-header header-tag="header" class="p-1" role="tab">
<b-col cols="12" md="6" offset="0" offset-md="3"> <b-col cols="12" md="6" offset="0" offset-md="3">
<b-button block v-b-toggle.id_accordion_import variant="info" <b-button block v-b-toggle.id_accordion_import variant="info"
:disabled="recipe_json === undefined">Import :disabled="recipe_json === undefined">{{ $t('Import') }}
</b-button> </b-button>
</b-col> </b-col>
</b-card-header> </b-card-header>
@@ -261,31 +264,94 @@
</b-tab> </b-tab>
<!-- App Tab --> <!-- App Tab -->
<b-tab v-bind:title="$t('App')"> <b-tab v-bind:title="$t('App')">
<b-container>
<select class="form-control" v-model="recipe_app"> <h4>{{ $t('Select_App_To_Import') }}:</h4>
<option v-for="i in INTEGRATIONS" :value="i.id" v-bind:key="i.id">{{ i.name }}</option> <b-row class="mt-4">
</select> <b-col cols="4" offset="0" offset-md="4" v-for="i in INTEGRATIONS_TD" :value="i.id"
v-bind:key="i.id">
<b-list-group style="max-width: 300px;">
<b-list-group-item class="d-flex align-items-center" v-hover
style="cursor: pointer"
v-bind:class="{ 'bg-success': recipe_app === i.id }"
@click="recipe_app = i.id">
<b-avatar class="mr-3"><i :class="i.icon"
v-if="i.img_src === undefined"></i><img
:src="i.img_src" v-if="i.img_src !== undefined" alt="Icon">
</b-avatar>
<span class="mr-auto">{{ i.name }}</span>
<b-badge variant="success" class="ml-1" v-b-tooltip:top
:title="$t('Import_Supported')"><i
class="fas fa-file-import fa-fw" v-if="i.import"></i></b-badge>
<b-badge variant="warning" class="ml-1" v-b-tooltip:top
:title="$t('Import_Not_Yet_Supported')"><i
class="fas fa-file-import fa-fw" v-if="!i.import"></i></b-badge>
<b-badge variant="success" class="ml-1" v-b-tooltip:top
:title="$t('Export_Supported')"><i
class="fas fa-file-export fa-fw" v-if="i.export"></i></b-badge>
<b-badge variant="warning" class="ml-1" v-b-tooltip:top
:title="$t('Export_Not_Yet_Supported')"><i
class="fas fa-file-export fa-fw" v-if="!i.export"></i></b-badge>
</b-list-group-item>
</b-list-group>
</b-col>
</b-row>
<b-row class="mt-4">
<b-col cols="3" v-for="i in INTEGRATIONS_WO" :value="i.id" v-bind:key="i.id"
class="mt-1">
<b-list-group style="max-width: 300px;">
<b-list-group-item class="d-flex align-items-center" v-hover
style="cursor: pointer"
v-bind:class="{ 'bg-success': recipe_app === i.id }"
@click="recipe_app = i.id">
<b-avatar class="mr-3"><i :class="i.icon"
v-if="i.img_src === undefined"></i><img
:src="i.img_src" v-if="i.img_src !== undefined" alt="Icon">
</b-avatar>
<span class="mr-auto">{{ i.name }}</span>
<b-badge variant="success" class="ml-1" v-b-tooltip:top
:title="$t('Import_Supported')"><i
class="fas fa-file-import fa-fw" v-if="i.import"></i></b-badge>
<b-badge variant="warning" class="ml-1" v-b-tooltip:top
:title="$t('Import_Not_Yet_Supported')"><i
class="fas fa-file-import fa-fw" v-if="!i.import"></i></b-badge>
<b-badge variant="success" class="ml-1" v-b-tooltip:top
:title="$t('Export_Supported')"><i
class="fas fa-file-export fa-fw" v-if="i.export"></i></b-badge>
<b-badge variant="warning" class="ml-1" v-b-tooltip:top
:title="$t('Export_Not_Yet_Supported')"><i
class="fas fa-file-export fa-fw" v-if="!i.export"></i></b-badge>
</b-list-group-item>
</b-list-group>
</b-col>
</b-row>
<b-row v-if="recipe_app_info !== undefined && recipe_app_info.help_url !== ''"
class="mt-3">
<b-col cols="12" class="text-center">
{{ $t('Importer_Help') }} <a
:href="recipe_app_info.help_url"> {{ $t('Documentation') }}</a>
</b-col>
</b-row>
<b-form-checkbox v-model="import_duplicates" name="check-button" switch <b-form-checkbox v-model="import_duplicates" name="check-button" switch
style="margin-top: 1vh"> style="margin-top: 1vh">
{{ $t('import_duplicates') }} {{ $t('import_duplicates') }}
</b-form-checkbox> </b-form-checkbox>
<a href="recipe_app_info.help_url"
v-if="recipe_app_info !== undefined && recipe_app_info.help_url !== ''">{{
$t('Help')
}}</a>
<b-form-file <b-form-file
class="my-2" class="my-2"
multiple multiple
size="lg"
v-model="recipe_files" v-model="recipe_files"
:placeholder="$t('Select_File')" :placeholder="$t('Select_File')"
drop-placeholder="Drop recipe files here..."> drop-placeholder="Drop recipe files here...">
</b-form-file> </b-form-file>
<button @click="importAppRecipe()" class="btn btn-primary shadow-none" type="button" <div class="text-center">
<b-button @click="importAppRecipe()" size="lg"
id="id_btn_app"><i class="fas fa-file-archive"></i> {{ $t('Import') }} id="id_btn_app"><i class="fas fa-file-archive"></i> {{ $t('Import') }}
</button> </b-button>
</div>
</b-container>
</b-tab> </b-tab>
<!-- Source Tab --> <!-- Source Tab -->
<b-tab v-bind:title="$t('Source')"> <b-tab v-bind:title="$t('Source')">
@@ -332,7 +398,15 @@ import {BootstrapVue} from 'bootstrap-vue'
import 'bootstrap-vue/dist/bootstrap-vue.css' import 'bootstrap-vue/dist/bootstrap-vue.css'
import {resolveDjangoStatic, resolveDjangoUrl, ResolveUrlMixin, StandardToasts, ToastMixin} from "@/utils/utils"; import {
RandomIconMixin,
resolveDjangoStatic,
resolveDjangoUrl,
ResolveUrlMixin,
StandardToasts,
ToastMixin
} from "@/utils/utils";
import axios from "axios"; import axios from "axios";
import {ApiApiFactory} from "@/utils/openapi/api"; import {ApiApiFactory} from "@/utils/openapi/api";
import {INTEGRATIONS} from "@/utils/integration"; import {INTEGRATIONS} from "@/utils/integration";
@@ -347,6 +421,7 @@ export default {
mixins: [ mixins: [
ResolveUrlMixin, ResolveUrlMixin,
ToastMixin, ToastMixin,
RandomIconMixin
], ],
components: { components: {
LoadingSpinner, LoadingSpinner,
@@ -357,10 +432,21 @@ export default {
recipe_app_info: function () { recipe_app_info: function () {
return this.INTEGRATIONS.filter(x => x.id === this.recipe_app)[0] return this.INTEGRATIONS.filter(x => x.id === this.recipe_app)[0]
}, },
INTEGRATIONS_WO: function () {
return this.INTEGRATIONS.filter((int) => {
return int.id !== "DEFAULT"
})
},
INTEGRATIONS_TD: function () {
return this.INTEGRATIONS.filter((int) => {
return int.id === "DEFAULT"
})
}
}, },
data() { data() {
return { return {
tab_index: 0, tab_index: 1,
collapse_visible: { collapse_visible: {
url: true, url: true,
options: false, options: false,
@@ -391,7 +477,8 @@ export default {
empty_input: false, empty_input: false,
edit_name: false, edit_name: false,
// Bookmarklet // Bookmarklet
BOOKMARKLET_CODE: window.BOOKMARKLET_CODE BOOKMARKLET_CODE: window.BOOKMARKLET_CODE,
error: undefined
} }
}, },
mounted() { mounted() {
@@ -402,6 +489,9 @@ export default {
if (window.BOOKMARKLET_IMPORT_ID !== -1) { if (window.BOOKMARKLET_IMPORT_ID !== -1) {
this.loadRecipe('', false, window.BOOKMARKLET_IMPORT_ID) this.loadRecipe('', false, window.BOOKMARKLET_IMPORT_ID)
} }
this.INTEGRATIONS.forEach((int) => {
int.icon = this.getRandomFoodIcon()
})
}, },
methods: { methods: {
/** /**
@@ -422,12 +512,12 @@ export default {
let recipe = response.data let recipe = response.data
apiFactory.imageRecipe(response.data.id, undefined, recipe_json.image).then(response => { // save recipe image apiFactory.imageRecipe(response.data.id, undefined, recipe_json.image).then(response => { // save recipe image
if (!silent) { if (!silent) {
StandardToasts.makeStandardToast(StandardToasts.SUCCESS_CREATE) StandardToasts.makeStandardToast(this, StandardToasts.SUCCESS_CREATE)
} }
this.afterImportAction(action, recipe) this.afterImportAction(action, recipe)
}).catch(e => { }).catch(e => {
if (!silent) { if (!silent) {
StandardToasts.makeStandardToast(StandardToasts.FAIL_UPDATE) StandardToasts.makeStandardToast(this, StandardToasts.FAIL_UPDATE)
} }
this.afterImportAction(action, recipe) this.afterImportAction(action, recipe)
}) })
@@ -436,13 +526,13 @@ export default {
this.failed_imports.push(recipe_json.source_url) this.failed_imports.push(recipe_json.source_url)
} }
if (!silent) { if (!silent) {
StandardToasts.makeStandardToast(StandardToasts.FAIL_CREATE) StandardToasts.makeStandardToast(this, StandardToasts.FAIL_CREATE)
} }
}) })
} else { } else {
console.log('cant import recipe without data') console.log('cant import recipe without data')
if (!silent) { if (!silent) {
StandardToasts.makeStandardToast(StandardToasts.FAIL_CREATE) StandardToasts.makeStandardToast(this, StandardToasts.FAIL_CREATE)
} }
} }
}, },
@@ -543,10 +633,11 @@ export default {
} }
return this.recipe_json return this.recipe_json
}).catch((err) => { }).catch((err) => {
this.loading = false
if (url !== '') { if (url !== '') {
this.failed_imports.push(url) this.failed_imports.push(url)
} }
StandardToasts.makeStandardToast(StandardToasts.FAIL_FETCH, err.response.data.msg) StandardToasts.makeStandardToast(this, StandardToasts.FAIL_FETCH, err)
throw "Load Recipe Error" throw "Load Recipe Error"
}) })
}, },
@@ -577,15 +668,14 @@ export default {
window.location.href = resolveDjangoUrl('view_import_response', response.data['import_id']) window.location.href = resolveDjangoUrl('view_import_response', response.data['import_id'])
}).catch((err) => { }).catch((err) => {
console.log(err) console.log(err)
StandardToasts.makeStandardToast(StandardToasts.FAIL_CREATE) StandardToasts.makeStandardToast(this, StandardToasts.FAIL_CREATE)
}) })
}, },
/** /**
* Handles pasting URLs * Handles pasting URLs
*/ */
onURLPaste: function (evt) { onURLPaste: function (evt) {
this.website_url = evt.clipboardData.getData('text') this.loadRecipe(evt.clipboardData.getData('text'), false, undefined)
this.loadRecipe(false, undefined)
return true; return true;
}, },
/**loadRecipe(false,undefined) /**loadRecipe(false,undefined)

View File

@@ -4,24 +4,31 @@
<div class="row mt-2"> <div class="row mt-2">
<div class="col col-md-6"> <div class="col col-md-6">
<generic-multiselect @change="food = $event.val; refreshList()" <generic-multiselect @change="food = $event.val; refreshList()" ref="food_multiselect"
:model="Models.FOOD" :model="Models.FOOD"
:initial_single_selection="food" :initial_single_selection="food"
:multiple="false"></generic-multiselect> :multiple="false"></generic-multiselect>
<b-button @click="show_food_delete=true" :disabled="food === null"><i class="fas fa-trash-alt"></i> <b-button @click="generic_model = Models.FOOD; generic_action=Actions.DELETE" :disabled="food === null">
<i class="fas fa-trash-alt"></i>
</b-button> </b-button>
<b-button @click="generic_model = Models.FOOD; generic_action=Actions.MERGE" :disabled="food === null"> <b-button @click="generic_model = Models.FOOD; generic_action=Actions.MERGE" :disabled="food === null">
<i class="fas fa-compress-arrows-alt"></i> <i class="fas fa-compress-arrows-alt"></i>
</b-button> </b-button>
<b-button @click="generic_model = Models.FOOD; generic_action=Actions.UPDATE" :disabled="food === null">
<i class="fas fa-edit"></i>
</b-button>
<generic-modal-form :model="Models.FOOD" :action="generic_action" :show="generic_model === Models.FOOD" <generic-modal-form :model="Models.FOOD" :action="generic_action" :show="generic_model === Models.FOOD"
:item1="food" :item1="food"
@finish-action="food = null; generic_action=null; generic_model=null"/> @finish-action="finishGenericAction"/>
</div> </div>
<div class="col col-md-6"> <div class="col col-md-6">
<generic-multiselect <generic-multiselect
@change="unit = $event.val; refreshList()" @change="unit = $event.val; refreshList()"
:model="Models.UNIT" :model="Models.UNIT"
ref="unit_multiselect"
:initial_single_selection="unit" :initial_single_selection="unit"
:multiple="false"></generic-multiselect> :multiple="false"></generic-multiselect>
@@ -31,14 +38,20 @@
<b-button @click="generic_model = Models.UNIT; generic_action=Actions.MERGE" :disabled="unit === null"> <b-button @click="generic_model = Models.UNIT; generic_action=Actions.MERGE" :disabled="unit === null">
<i class="fas fa-compress-arrows-alt"></i> <i class="fas fa-compress-arrows-alt"></i>
</b-button> </b-button>
<b-button @click="generic_model = Models.UNIT; generic_action=Actions.UPDATE" :disabled="unit === null">
<i class="fas fa-edit"></i>
</b-button>
<generic-modal-form :model="Models.UNIT" :action="generic_action" :show="generic_model === Models.UNIT" <generic-modal-form :model="Models.UNIT" :action="generic_action" :show="generic_model === Models.UNIT"
:item1="unit" :item1="unit"
@finish-action="unit = null; generic_action=null; generic_model=null"/> @finish-action="finishGenericAction()"/>
</div> </div>
</div> </div>
<b-pagination pills v-model="current_page" :total-rows="total_object_count" :per-page="page_size"
@change="pageChange"></b-pagination>
<div class="row mt-2"> <div class="row mt-2">
<div class="col col-md-12"> <div class="col col-md-12">
<table class="table table-bordered"> <table class="table table-bordered">
@@ -113,6 +126,9 @@
</template> </template>
</table> </table>
<b-pagination pills v-model="current_page" :total-rows="total_object_count" :per-page="page_size"
@change="pageChange"></b-pagination>
</div> </div>
</div> </div>
@@ -148,6 +164,9 @@ export default {
generic_model: null, generic_model: null,
show_food_delete: false, show_food_delete: false,
show_unit_delete: false, show_unit_delete: false,
current_page: 1,
total_object_count: 0,
page_size: 50,
} }
}, },
computed: {}, computed: {},
@@ -176,22 +195,27 @@ export default {
} else { } else {
this.loading = true this.loading = true
let apiClient = new ApiApiFactory() let apiClient = new ApiApiFactory()
let params = {'query': {'simple': 1}} let params = {'query': {'simple': 1,}}
if (this.food !== null) { if (this.food !== null) {
params.query.food = this.food.id params.query.food = this.food.id
} }
if (this.unit !== null) { if (this.unit !== null) {
params.query.unit = this.unit.id params.query.unit = this.unit.id
} }
apiClient.listIngredients(params).then(result => { apiClient.listIngredients(this.current_page, this.page_size,params).then(result => {
this.ingredients = result.data this.ingredients = result.data.results
this.total_object_count = result.data.count
this.loading = false this.loading = false
}).catch((err) => { }).catch((err) => {
StandardToasts.makeStandardToast(StandardToasts.FAIL_FETCH) StandardToasts.makeStandardToast(this, StandardToasts.FAIL_FETCH, err)
this.loading = false this.loading = false
}) })
} }
}, },
pageChange: function (page) {
this.current_page = page
this.refreshList()
},
updateIngredient: function (i) { updateIngredient: function (i) {
let update_list = [] let update_list = []
if (i === undefined) { if (i === undefined) {
@@ -208,8 +232,8 @@ export default {
let apiClient = new ApiApiFactory() let apiClient = new ApiApiFactory()
apiClient.updateIngredient(i.id, i).then(r => { apiClient.updateIngredient(i.id, i).then(r => {
this.$set(i, 'changed', false) this.$set(i, 'changed', false)
}).catch((r, e) => { }).catch(err => {
StandardToasts.makeStandardToast(StandardToasts.FAIL_UPDATE) StandardToasts.makeStandardToast(this, StandardToasts.FAIL_UPDATE, err)
}) })
}) })
}, },
@@ -217,12 +241,70 @@ export default {
if (confirm(this.$t('delete_confirmation', this.$t('Ingredient')))) { if (confirm(this.$t('delete_confirmation', this.$t('Ingredient')))) {
let apiClient = new ApiApiFactory() let apiClient = new ApiApiFactory()
apiClient.destroyIngredient(i.id).then(r => { apiClient.destroyIngredient(i.id).then(r => {
StandardToasts.makeStandardToast(StandardToasts.SUCCESS_DELETE) StandardToasts.makeStandardToast(this, StandardToasts.SUCCESS_DELETE)
this.ingredients = this.ingredients.filter(li => li.id !== i.id) this.ingredients = this.ingredients.filter(li => li.id !== i.id)
}).catch(e => { }).catch(err => {
StandardToasts.makeStandardToast(StandardToasts.FAIL_DELETE) StandardToasts.makeStandardToast(this, StandardToasts.FAIL_DELETE, err)
}) })
} }
},
finishGenericAction: function (e) {
if (e !== 'cancel') {
if (this.generic_action === this.Actions.DELETE) {
this.ingredients = []
if (this.generic_model === this.Models.FOOD) {
this.food = null;
} else {
this.unit = null;
}
}
if (this.generic_action === this.Actions.UPDATE) {
if (this.generic_model === this.Models.FOOD) {
this.food = e.item
this.ingredients.forEach((element, i) => {
if (element.food.id === this.food.id) {
this.ingredients[i].food = this.food
}
})
} else {
this.unit = e.item
this.ingredients.forEach((element, i) => {
if (element.unit?.id === this.unit.id) {
this.ingredients[i].unit = this.unit
}
})
}
}
if (this.generic_action === this.Actions.MERGE) {
if (this.generic_model === this.Models.FOOD) {
this.ingredients.forEach((element, i) => {
if (element.food.id === this.food.id) {
this.ingredients[i].food = e.target_object
}
})
this.food = e.target_object
} else {
this.ingredients.forEach((element, i) => {
if (element.unit?.id === this.unit.id) {
this.ingredients[i].unit = e.target_object
}
})
this.unit = e.target_object
}
this.refreshList()
}
}
if (this.generic_model === this.Models.FOOD) {
this.$refs.food_multiselect.search('');
} else {
this.$refs.unit_multiselect.search('');
}
this.generic_action = null;
this.generic_model = null;
} }
}, },

View File

@@ -2,7 +2,7 @@
<div> <div>
<b-tabs content-class="mt-3" v-model="current_tab"> <b-tabs content-class="mt-3" v-model="current_tab">
<b-tab :title="$t('Planner')" active> <b-tab :title="$t('Planner')" active>
<div class="row"> <div class="row calender-row">
<div class="col-12 calender-parent"> <div class="col-12 calender-parent">
<calendar-view <calendar-view
:show-date="showDate" :show-date="showDate"
@@ -450,8 +450,8 @@ export default {
.then((e) => { .then((e) => {
this.periodChangedCallback(this.current_period) this.periodChangedCallback(this.current_period)
}) })
.catch((error) => { .catch((err) => {
StandardToasts.makeStandardToast(StandardToasts.FAIL_UPDATE) StandardToasts.makeStandardToast(this,StandardToasts.FAIL_UPDATE, err)
}) })
this.refreshMealTypes() this.refreshMealTypes()
@@ -473,8 +473,8 @@ export default {
updated++ updated++
} }
}) })
.catch((error) => { .catch((err) => {
StandardToasts.makeStandardToast(StandardToasts.FAIL_UPDATE) StandardToasts.makeStandardToast(this,StandardToasts.FAIL_UPDATE, err)
}) })
}) })
}, },
@@ -488,10 +488,10 @@ export default {
.updateMealType(this.meal_types[index].id, this.meal_types[index]) .updateMealType(this.meal_types[index].id, this.meal_types[index])
.then((e) => { .then((e) => {
this.periodChangedCallback(this.current_period) this.periodChangedCallback(this.current_period)
StandardToasts.makeStandardToast(StandardToasts.SUCCESS_UPDATE) StandardToasts.makeStandardToast(this,StandardToasts.SUCCESS_UPDATE)
}) })
.catch((error) => { .catch((err) => {
StandardToasts.makeStandardToast(StandardToasts.FAIL_UPDATE) StandardToasts.makeStandardToast(this,StandardToasts.FAIL_UPDATE, err)
}) })
} else { } else {
this.$set(this.meal_types[index], "editing", true) this.$set(this.meal_types[index], "editing", true)
@@ -504,10 +504,10 @@ export default {
.destroyMealType(this.meal_types[index].id) .destroyMealType(this.meal_types[index].id)
.then((e) => { .then((e) => {
this.periodChangedCallback(this.current_period) this.periodChangedCallback(this.current_period)
StandardToasts.makeStandardToast(StandardToasts.SUCCESS_DELETE) StandardToasts.makeStandardToast(this,StandardToasts.SUCCESS_DELETE)
}) })
.catch((error) => { .catch((err) => {
StandardToasts.makeStandardToast(StandardToasts.FAIL_DELETE) StandardToasts.makeStandardToast(this,StandardToasts.FAIL_DELETE, err)
}) })
}, },
updateEmoji: function (field, value) { updateEmoji: function (field, value) {
@@ -582,8 +582,8 @@ export default {
.then((e) => { .then((e) => {
list.splice(index, 1) list.splice(index, 1)
}) })
.catch((error) => { .catch((err) => {
StandardToasts.makeStandardToast(StandardToasts.FAIL_UPDATE) StandardToasts.makeStandardToast(this,StandardToasts.FAIL_UPDATE, err)
}) })
} }
}) })
@@ -634,8 +634,8 @@ export default {
let apiClient = new ApiApiFactory() let apiClient = new ApiApiFactory()
apiClient.updateMealPlan(entry.id, entry).catch((error) => { apiClient.updateMealPlan(entry.id, entry).catch((err) => {
StandardToasts.makeStandardToast(StandardToasts.FAIL_UPDATE) StandardToasts.makeStandardToast(this,StandardToasts.FAIL_UPDATE, err)
}) })
}, },
createEntry(entry) { createEntry(entry) {
@@ -645,8 +645,8 @@ export default {
apiClient apiClient
.createMealPlan(entry) .createMealPlan(entry)
.catch((error) => { .catch((err) => {
StandardToasts.makeStandardToast(StandardToasts.FAIL_UPDATE) StandardToasts.makeStandardToast(this,StandardToasts.FAIL_UPDATE, err)
}) })
.then((entry_result) => { .then((entry_result) => {
this.plan_entries.push(entry_result.data) this.plan_entries.push(entry_result.data)
@@ -693,19 +693,24 @@ export default {
opacity: 0; opacity: 0;
} }
.calender-row {
height: calc(100% - 240px);
}
.calender-parent { .calender-parent {
display: flex; display: flex;
flex-direction: column; flex-direction: column;
flex-grow: 1; flex-grow: 1;
overflow-x: hidden; overflow-x: hidden;
overflow-y: hidden; overflow-y: hidden;
height: 70vh; height: 100%
} }
.cv-item { .cv-item {
white-space: inherit !important; white-space: inherit !important;
} }
.isHovered { .isHovered {
box-shadow: 0 0.5rem 1rem rgba(0, 0, 0, 0.15) !important; box-shadow: 0 0.5rem 1rem rgba(0, 0, 0, 0.15) !important;
} }
@@ -837,4 +842,10 @@ having to override as much.
opacity: 0.5; opacity: 0.5;
background: #c8ebfb; background: #c8ebfb;
} }
@media (max-width: 767.9px) {
.periodLabel {
font-size: 18px !important;
}
}
</style> </style>

View File

@@ -275,8 +275,7 @@ export default {
} }
}) })
.catch((err) => { .catch((err) => {
console.log(err, Object.keys(err)) StandardToasts.makeStandardToast(this,StandardToasts.FAIL_FETCH, err)
StandardToasts.makeStandardToast(StandardToasts.FAIL_FETCH)
}) })
}, },
getThis: function (id, callback) { getThis: function (id, callback) {
@@ -292,21 +291,19 @@ export default {
this.items_left = [result.data].concat(this.destroyCard(result?.data?.id, this.items_left)) this.items_left = [result.data].concat(this.destroyCard(result?.data?.id, this.items_left))
// this creates a deep copy to make sure that columns stay independent // this creates a deep copy to make sure that columns stay independent
this.items_right = [{...result.data}].concat(this.destroyCard(result?.data?.id, this.items_right)) this.items_right = [{...result.data}].concat(this.destroyCard(result?.data?.id, this.items_right))
StandardToasts.makeStandardToast(StandardToasts.SUCCESS_CREATE) StandardToasts.makeStandardToast(this,StandardToasts.SUCCESS_CREATE)
}) })
.catch((err) => { .catch((err) => {
console.log(err) StandardToasts.makeStandardToast(this,StandardToasts.FAIL_CREATE, err)
StandardToasts.makeStandardToast(StandardToasts.FAIL_CREATE)
}) })
} else { } else {
this.genericAPI(this.this_model, this.Actions.UPDATE, item) this.genericAPI(this.this_model, this.Actions.UPDATE, item)
.then((result) => { .then((result) => {
this.refreshThis(item.id) this.refreshThis(item.id)
StandardToasts.makeStandardToast(StandardToasts.SUCCESS_UPDATE) StandardToasts.makeStandardToast(this,StandardToasts.SUCCESS_UPDATE)
}) })
.catch((err) => { .catch((err) => {
console.log(err, err.response) StandardToasts.makeStandardToast(this,StandardToasts.FAIL_UPDATE, err)
StandardToasts.makeStandardToast(StandardToasts.FAIL_UPDATE)
}) })
} }
}, },
@@ -315,7 +312,7 @@ export default {
let api = new ApiApiFactory() let api = new ApiApiFactory()
food.shopping = true food.shopping = true
api.createShoppingListEntry({food: food, amount: 1}).then(() => { api.createShoppingListEntry({food: food, amount: 1}).then(() => {
StandardToasts.makeStandardToast(StandardToasts.SUCCESS_CREATE) StandardToasts.makeStandardToast(this,StandardToasts.SUCCESS_CREATE)
this.refreshCard(food, this.items_left) this.refreshCard(food, this.items_left)
this.refreshCard({...food}, this.items_right) this.refreshCard({...food}, this.items_right)
}) })
@@ -339,7 +336,7 @@ export default {
this.genericAPI(this.this_model, this.Actions.MOVE, {source: source_id, target: target_id}) this.genericAPI(this.this_model, this.Actions.MOVE, {source: source_id, target: target_id})
.then((result) => { .then((result) => {
this.moveUpdateItem(source_id, target_id) this.moveUpdateItem(source_id, target_id)
StandardToasts.makeStandardToast(StandardToasts.SUCCESS_MOVE) StandardToasts.makeStandardToast(this,StandardToasts.SUCCESS_MOVE)
}) })
.catch((err) => { .catch((err) => {
console.log(err) console.log(err)
@@ -378,7 +375,7 @@ export default {
}) })
.then((result) => { .then((result) => {
this.mergeUpdateItem(source_id, target_id) this.mergeUpdateItem(source_id, target_id)
StandardToasts.makeStandardToast(StandardToasts.SUCCESS_MERGE) StandardToasts.makeStandardToast(this,StandardToasts.SUCCESS_MERGE)
}) })
.catch((err) => { .catch((err) => {
//TODO error checking not working with OpenAPI methods //TODO error checking not working with OpenAPI methods
@@ -432,7 +429,7 @@ export default {
}) })
.catch((err) => { .catch((err) => {
console.log(err) console.log(err)
StandardToasts.makeStandardToast(StandardToasts.FAIL_FETCH) StandardToasts.makeStandardToast(this,StandardToasts.FAIL_FETCH, err)
}) })
}, },
getRecipes: function (col, item) { getRecipes: function (col, item) {

View File

@@ -821,8 +821,7 @@ export default {
}) })
.catch((err) => { .catch((err) => {
this.loading = false this.loading = false
console.log(err) StandardToasts.makeStandardToast(this,StandardToasts.FAIL_FETCH, err)
StandardToasts.makeStandardToast(StandardToasts.FAIL_FETCH)
}) })
}, },
updateRecipe: function (view_after) { updateRecipe: function (view_after) {
@@ -850,7 +849,7 @@ export default {
apiFactory apiFactory
.updateRecipe(this.recipe_id, this.recipe, {}) .updateRecipe(this.recipe_id, this.recipe, {})
.then((response) => { .then((response) => {
StandardToasts.makeStandardToast(StandardToasts.SUCCESS_UPDATE) StandardToasts.makeStandardToast(this,StandardToasts.SUCCESS_UPDATE)
this.recipe_changed = false this.recipe_changed = false
if (this.create_food) { if (this.create_food) {
apiFactory.createFood({ apiFactory.createFood({
@@ -863,8 +862,7 @@ export default {
} }
}) })
.catch((err) => { .catch((err) => {
console.log(err) StandardToasts.makeStandardToast(this,StandardToasts.FAIL_UPDATE, err)
StandardToasts.makeStandardToast(StandardToasts.FAIL_UPDATE)
}) })
}, },
uploadImage: function (file) { uploadImage: function (file) {
@@ -875,11 +873,10 @@ export default {
.then((request) => { .then((request) => {
this.recipe.image = request.data.image this.recipe.image = request.data.image
this.recipe_changed = false this.recipe_changed = false
StandardToasts.makeStandardToast(StandardToasts.SUCCESS_UPDATE) StandardToasts.makeStandardToast(this,StandardToasts.SUCCESS_UPDATE)
}) })
.catch((err) => { .catch((err) => {
StandardToasts.makeStandardToast(StandardToasts.FAIL_UPDATE) StandardToasts.makeStandardToast(this,StandardToasts.FAIL_UPDATE, err)
console.log(err.request, err.response)
}) })
} }
}, },
@@ -891,11 +888,10 @@ export default {
.then((request) => { .then((request) => {
this.recipe.image = null this.recipe.image = null
this.recipe_changed = false this.recipe_changed = false
StandardToasts.makeStandardToast(StandardToasts.SUCCESS_DELETE) StandardToasts.makeStandardToast(this,StandardToasts.SUCCESS_DELETE)
}) })
.catch((err) => { .catch((err) => {
StandardToasts.makeStandardToast(StandardToasts.FAIL_DELETE) StandardToasts.makeStandardToast(this,StandardToasts.FAIL_DELETE, err)
console.log(err.request, err.response)
}) })
} }
}, },
@@ -990,8 +986,7 @@ export default {
this.keywords_loading = false this.keywords_loading = false
}) })
.catch((err) => { .catch((err) => {
console.log(err) StandardToasts.makeStandardToast(this,StandardToasts.FAIL_FETCH, err)
StandardToasts.makeStandardToast(StandardToasts.FAIL_FETCH)
}) })
}, },
searchFiles: function (query) { searchFiles: function (query) {
@@ -1005,8 +1000,7 @@ export default {
this.files_loading = false this.files_loading = false
}) })
.catch((err) => { .catch((err) => {
console.log(err) StandardToasts.makeStandardToast(this,StandardToasts.FAIL_FETCH, err)
StandardToasts.makeStandardToast(StandardToasts.FAIL_FETCH)
}) })
}, },
searchRecipes: function (query) { searchRecipes: function (query) {
@@ -1017,8 +1011,7 @@ export default {
this.recipes_loading = false this.recipes_loading = false
}) })
.catch((err) => { .catch((err) => {
console.log(err) StandardToasts.makeStandardToast(this,StandardToasts.FAIL_FETCH, err)
StandardToasts.makeStandardToast(StandardToasts.FAIL_FETCH)
}) })
}, },
searchUnits: function (query) { searchUnits: function (query) {
@@ -1042,7 +1035,7 @@ export default {
this.units_loading = false this.units_loading = false
}) })
.catch((err) => { .catch((err) => {
StandardToasts.makeStandardToast(StandardToasts.FAIL_FETCH) StandardToasts.makeStandardToast(this,StandardToasts.FAIL_FETCH, err)
}) })
}, },
searchFoods: function (query) { searchFoods: function (query) {
@@ -1067,7 +1060,7 @@ export default {
this.foods_loading = false this.foods_loading = false
}) })
.catch((err) => { .catch((err) => {
StandardToasts.makeStandardToast(StandardToasts.FAIL_FETCH) StandardToasts.makeStandardToast(this,StandardToasts.FAIL_FETCH, err)
}) })
}, },
fileCreated: function (data) { fileCreated: function (data) {

View File

@@ -1496,11 +1496,10 @@ export default {
this.genericAPI(this.Models.CUSTOM_FILTER, this.Actions.CREATE, params) this.genericAPI(this.Models.CUSTOM_FILTER, this.Actions.CREATE, params)
.then((result) => { .then((result) => {
this.search.search_filter = result.data this.search.search_filter = result.data
StandardToasts.makeStandardToast(StandardToasts.SUCCESS_CREATE) StandardToasts.makeStandardToast(this,StandardToasts.SUCCESS_CREATE)
}) })
.catch((err) => { .catch((err) => {
console.log(err, Object.keys(err)) StandardToasts.makeStandardToast(this,StandardToasts.FAIL_CREATE, err)
StandardToasts.makeStandardToast(StandardToasts.FAIL_CREATE)
}) })
}, },
addField: function (field, count) { addField: function (field, count) {

View File

@@ -233,7 +233,7 @@ export default {
this.loadRecipe(window.RECIPE_ID) this.loadRecipe(window.RECIPE_ID)
this.$i18n.locale = window.CUSTOM_LOCALE this.$i18n.locale = window.CUSTOM_LOCALE
this.requestWakeLock() this.requestWakeLock()
window.addEventListener('resize', this.handleRezise); window.addEventListener('resize', this.handleResize);
}, },
beforeUnmount() { beforeUnmount() {
this.destroyWakeLock() this.destroyWakeLock()
@@ -249,7 +249,7 @@ export default {
} }
} }
}, },
handleRezise: function () { handleResize: function () {
if (document.getElementById('nutrition_container') !== null) { if (document.getElementById('nutrition_container') !== null) {
this.ingredient_height = document.getElementById('ingredient_container').clientHeight - document.getElementById('nutrition_container').clientHeight this.ingredient_height = document.getElementById('ingredient_container').clientHeight - document.getElementById('nutrition_container').clientHeight
} else { } else {
@@ -300,7 +300,7 @@ export default {
this.loading = false this.loading = false
setTimeout(() => { setTimeout(() => {
this.handleRezise() this.handleResize()
}, 100) }, 100)
}) })
}, },

View File

@@ -1,42 +1,50 @@
<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 pl-0 pr-0"> <div class="row float-top w-100">
<div class="col-auto no-gutter ml-auto"> <div class="col-auto no-gutter ml-auto">
<b-button variant="link" class="px-1 pt-0 pb-1 d-none d-md-inline-block"> <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-primary'" /> <i class="btn fas fa-plus-circle fa-lg px-0" @click="entrymode = !entrymode"
:class="entrymode ? 'text-success' : 'text-primary'"/>
</b-button> </b-button>
<b-button variant="link" class="px-1 pt-0 pb-1 d-none d-md-inline-block"> <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-primary 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-primary px-1"
id="downloadShoppingLink" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false"></i>
<div class="dropdown-menu dropdown-menu-right" aria-labelledby="downloadShoppingLink"> <div class="dropdown-menu dropdown-menu-right" 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')"
<DownloadCSV :items="csvData" :delim="settings.csv_delim" name="shopping.csv" :label="$t('download_csv')" icon="fas fa-file-csv" /> icon="far fa-file-pdf"/>
<CopyToClipboard :items="csvData" :settings="settings" :label="$t('copy_to_clipboard')" icon="fas fa-clipboard-list" /> <DownloadCSV :items="csvData" :delim="settings.csv_delim" name="shopping.csv"
<CopyToClipboard :items="csvData" :settings="settings" format="table" :label="$t('copy_markdown_table')" icon="fab fa-markdown" /> :label="$t('download_csv')" icon="fas fa-file-csv"/>
<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 pt-0 pb-1"> <i id="id_filters_button" class="fas fa-filter fa-fw mt-1" style="font-size: 16px"
<i class="btn fas fa-filter text-decoration-none fa-lg px-1" :class="filterApplied ? 'text-danger' : 'text-primary'" /> :class="filterApplied ? 'text-danger' : 'text-primary'"/>
</b-button>
</div> </div>
</div> </div>
<b-tabs content-class="mt-3" v-model="current_tab"> <b-tabs content-class="mt-2" v-model="current_tab" class="mt-md-1" style="margin-top: 22px">
<!-- shopping list tab --> <!-- shopping list tab -->
<b-tab active> <b-tab active>
<template #title> <template #title>
<b-spinner v-if="loading" type="border" small></b-spinner> <b-spinner v-if="loading" type="border" small class="d-inline-block"></b-spinner>
{{ $t("Shopping_list") }} <i v-if="!loading" class="fas fa-shopping-cart fa-fw d-inline-block d-md-none"></i>
<span class="d-none d-md-block">{{ $t('Shopping_list') }}</span>
</template> </template>
<div class="container p-0" id="shoppinglist"> <div class="container p-0" id="shoppinglist">
<div class="row"> <div class="row">
<div class="col col-md-12 p-0 p-lg-3"> <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 -->
<div class="container">
<b-row class="justify-content-md-center align-items-center pl-1 pr-1" v-if="entrymode"> <b-row class="justify-content-md-center align-items-center pl-1 pr-1"
<b-col cols="12" md="3" v-if="!ui.entry_mode_simple" class="d-none d-md-block mt-1"> v-if="entrymode">
<b-col cols="12" md="3" v-if="!ui.entry_mode_simple"
class="d-none d-md-block mt-1">
<b-form-input <b-form-input
size="lg" size="lg"
min="1" min="1"
@@ -47,20 +55,28 @@
></b-form-input> ></b-form-input>
</b-col> </b-col>
<b-col cols="12" md="4" v-if="!ui.entry_mode_simple" class="mt-1"> <b-col cols="12" md="4" v-if="!ui.entry_mode_simple" class="mt-1">
<lookup-input :class_list="'mb-0'" :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" md="4" v-if="!ui.entry_mode_simple" class="mt-1"> <b-col cols="12" md="4" v-if="!ui.entry_mode_simple" class="mt-1">
<lookup-input :class_list="'mb-0'" :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" md="11" v-if="ui.entry_mode_simple" class="mt-1"> <b-col cols="12" md="11" v-if="ui.entry_mode_simple" class="mt-1">
<b-form-input size="lg" 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" md="1" class="d-none d-md-block mt-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="!ui.entry_mode_simple" class="d-block d-md-none mt-1"> <b-col cols="12" md="3" v-if="!ui.entry_mode_simple"
class="d-block d-md-none mt-1">
<b-row> <b-row>
<b-col cols="9"> <b-col cols="9">
<b-form-input <b-form-input
@@ -85,19 +101,24 @@
<b-form-checkbox switch v-model="ui.entry_mode_simple"> <b-form-checkbox switch v-model="ui.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="ui.entry_mode_simple"> <b-button variant="success" size="sm" class="d-flex d-md-none p-0"
v-if="ui.entry_mode_simple">
<i class="btn fas fa-cart-plus" @click="addItem"/> <i class="btn fas fa-cart-plus" @click="addItem"/>
</b-button> </b-button>
</b-row> </b-row>
</div>
<!-- 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'" class="bg-header w-100 text-center d-flex justify-content-center align-items-center"> <div v-if="x == 'true'"
class="bg-header w-100 text-center d-flex justify-content-center align-items-center">
<span class="h4 d-flex mt-1 mb-1">{{ $t("Completed") }}</span> <span class="h4 d-flex mt-1 mb-1">{{ $t("Completed") }}</span>
</div> </div>
<div v-for="(s, i) in done" :key="i"> <div v-for="(s, i) in done" :key="i">
<div class="dropdown b-dropdown position-static inline-block" data-html2canvas-ignore="true" v-if="Object.entries(s).length > 0"> <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"
@@ -120,10 +141,12 @@
</b-button> </b-button>
</div> </div>
<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="slide-fade"> <transition-group name="slide-fade">
<div class="ml-4 mr-0" v-for="(entries, x) in Object.entries(s)" :key="x"> <div class="pl-4 pr-0" v-for="(entries, x) in Object.entries(s)"
:key="x">
<transition name="slide-fade" mode="out-in"> <transition name="slide-fade" mode="out-in">
<shopping-line-item <shopping-line-item
:entries="entries[1]" :entries="entries[1]"
@@ -131,6 +154,7 @@
:settings="settings" :settings="settings"
@open-context-menu="openContextMenu" @open-context-menu="openContextMenu"
@update-checkbox="updateChecked" @update-checkbox="updateChecked"
@update-delaythis="delayThis"
/> />
</transition> </transition>
</div> </div>
@@ -146,16 +170,35 @@
</b-tab> </b-tab>
<!-- recipe tab --> <!-- recipe tab -->
<b-tab :title="$t('Recipes')"> <b-tab :title="$t('Recipes')">
<template #title>
<i class="fas fa-book fa-fw d-block d-md-none"></i>
<span class="d-none d-md-block">{{ $t('Recipes') }}</span>
</template>
<div class="container p-0"> <div class="container p-0">
<b-row class="justify-content-md-center align-items-center p-1"> <div class="row">
<b-col cols="10"> <div class="col col-md-12 p-0 p-lg-3">
<b-input-group> <div role="tablist">
<b-input-group-prepend is-text> <!-- add to shopping form -->
{{ $t("Servings") }} <div class="container">
</b-input-group-prepend> <b-row class="justify-content-md-center align-items-center pl-1 pr-1">
<b-input-group-prepend is-text> <b-col cols="10" md="3" class="mt-1">
<input type="number" :min="1" v-model="add_recipe_servings" style="width: 3em" /> <b-form-input
</b-input-group-prepend> size="lg"
min="1"
type="number"
:description="$t('Servings')"
v-model="add_recipe_servings"
style="font-size: 16px; border-radius: 5px !important; border: 1px solid #e8e8e8 !important"
></b-form-input>
</b-col>
<b-col cols="2" md="1" class="d-block d-md-none mt-1">
<b-button variant="link" class="px-0">
<i class="btn fas fa-cart-plus fa-lg px-0 text-success"
@click="addRecipeToShopping"
:disabled="!new_recipe.id"/>
</b-button>
</b-col>
<b-col cols="12" md="8" class="mt-1">
<generic-multiselect <generic-multiselect
class="input-group-text m-0 p-0" class="input-group-text m-0 p-0"
@change="new_recipe = $event.val" @change="new_recipe = $event.val"
@@ -166,13 +209,16 @@
:limit="20" :limit="20"
:multiple="false" :multiple="false"
/> />
<b-input-group-append> </b-col>
<b-button variant="success" @click="addRecipeToShopping" :disabled="!new_recipe.id">{{ $t("Add_to_Shopping") }}</b-button> <b-col cols="12" md="1" class="d-none d-md-block mt-1">
</b-input-group-append> <b-button variant="link" class="px-0">
</b-input-group> <i class="btn fas fa-cart-plus fa-lg px-0 text-success"
@click="addRecipeToShopping"
:disabled="!new_recipe.id"/>
</b-button>
</b-col> </b-col>
</b-row> </b-row>
<table class="table w-100"> <table class="table w-100 mt-3 recipe-table">
<thead> <thead>
<tr> <tr>
<th scope="col">{{ $t("Meal_Plan") }}</th> <th scope="col">{{ $t("Meal_Plan") }}</th>
@@ -185,20 +231,36 @@
<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-primary far fa-eye fa-lg px-2 border-0" variant="link" :title="$t('view_recipe')" @click="editRecipeList($event, r)" /> <i class="text-primary far fa-eye fa-fw fa-lg"
</td> :title="$t('view_recipe')" @click="editRecipeList($event, r)"/>
<td> <i class="text-danger fas fa-trash fa-fw fa-lg mt-3"
<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)" /> :title="$t('Delete')" @click="deleteRecipe($event, r.list_recipe)"/>
</td> </td>
</tr> </tr>
</table> </table>
</div> </div>
</div>
</div>
</div>
</div>
</b-tab> </b-tab>
<!-- supermarkets tab --> <!-- supermarkets tab -->
<b-tab :title="$t('Supermarkets')"> <b-tab>
<template #title>
<i class="fas fa-store-alt fa-fw d-block d-md-none"></i>
<span class="d-none d-md-block">{{ $t('Supermarkets') }}</span>
</template>
<div class="container p-0">
<div class="row">
<div class="col col-md-12 p-0 p-lg-3">
<div role="tablist">
<!-- add to shopping form -->
<div class="container">
<div class="row justify-content-center"> <div class="row justify-content-center">
<!-- supermarkets column --> <!-- supermarkets column -->
<div class="col col-md-5"> <div class="col col-md-5">
@@ -219,7 +281,8 @@
}) })
" "
> >
<i class="btn fas fa-plus-circle fa-lg px-0" :class="new_supermarket.entrymode ? 'text-success' : 'text-primary'" /> <i class="btn fas fa-plus-circle fa-lg px-0"
:class="new_supermarket.entrymode ? 'text-success' : 'text-primary'"/>
</b-button> </b-button>
</h4> </h4>
</template> </template>
@@ -233,15 +296,21 @@
:header="new_supermarket.value ? new_supermarket.value : $t('SupermarketName')" :header="new_supermarket.value ? new_supermarket.value : $t('SupermarketName')"
> >
<b-input-group> <b-input-group>
<b-form-input type="text" class="form-control-append" :placeholder="$t('SupermarketName')" v-model="new_supermarket.value" /> <b-form-input type="text" class="form-control-append"
:placeholder="$t('SupermarketName')"
v-model="new_supermarket.value"/>
<b-input-group-append> <b-input-group-append>
<b-button class="input-group-append" variant="success" @click="addSupermarket"><i class="pr-2 pt-1 fas fa-save"></i> {{ $t("Create") }} </b-button> <b-button class="input-group-append" variant="success"
@click="addSupermarket">
<i class="pr-2 pt-1 fas fa-save"></i> {{ $t("Create") }}
</b-button>
</b-input-group-append> </b-input-group-append>
</b-input-group> </b-input-group>
</b-card> </b-card>
<b-card-body class="m-0 p-0"> <b-card-body class="m-0 p-0">
<b-card class="mt-1 p-0" v-for="s in supermarkets" v-bind:key="s.id"> <b-card class="mt-1 p-0" v-for="s in supermarkets"
v-bind:key="s.id">
<b-card-header class="p-2 border-0 pt-3"> <b-card-header class="p-2 border-0 pt-3">
<div class="row"> <div class="row">
<div class="col-12"> <div class="col-12">
@@ -257,9 +326,12 @@
editSupermarket(s) editSupermarket(s)
" "
> >
<i class="btn fas fa-edit fa-lg px-0" :class="s.editmode ? 'text-success' : 'text-primary'" /> <i class="btn fas fa-edit fa-lg px-0"
:class="s.editmode ? 'text-success' : 'text-primary'"/>
</b-button> </b-button>
<b-button variant="link" class="p-0 m-0 float-right" @click="deleteSupermarket(s)"> <b-button variant="link"
class="p-0 m-0 float-right"
@click="deleteSupermarket(s)">
<i class="btn fas fa-trash fa-lg px-2 text-danger"/> <i class="btn fas fa-trash fa-lg px-2 text-danger"/>
</b-button> </b-button>
</h5> </h5>
@@ -267,7 +339,9 @@
</div> </div>
</b-card-header> </b-card-header>
<b-card-body class="m-0 p-0"> <b-card-body class="m-0 p-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>
@@ -286,7 +360,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-primary'" /> <i class="btn fas fa-plus-circle fa-lg px-0"
:class="new_category.entrymode ? 'text-success' : 'text-primary'"/>
</b-button> </b-button>
</h4> </h4>
</template> </template>
@@ -300,15 +375,25 @@
:header="new_category.value ? new_category.value : $t('CategoryName')" :header="new_category.value ? new_category.value : $t('CategoryName')"
> >
<b-input-group> <b-input-group>
<b-form-input type="text" class="form-control-append" :placeholder="$t('CategoryName')" v-model="new_category.value" /> <b-form-input type="text" class="form-control-append"
:placeholder="$t('CategoryName')"
v-model="new_category.value"/>
<b-input-group-append> <b-input-group-append>
<b-button class="input-group-append" variant="success" @click="addCategory"><i class="pr-2 pt-1 fas fa-save"></i> {{ $t("Create") }} </b-button> <b-button class="input-group-append" variant="success"
@click="addCategory"><i
class="pr-2 pt-1 fas fa-save"></i> {{ $t("Create") }}
</b-button>
</b-input-group-append> </b-input-group-append>
</b-input-group> </b-input-group>
</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"
@@ -319,7 +404,8 @@
@change="saveSupermarketCategoryOrder" @change="saveSupermarketCategoryOrder"
v-bind="{ animation: 200, disabled: !new_supermarket.editmode }" v-bind="{ animation: 200, disabled: !new_supermarket.editmode }"
> >
<transition-group type="transition" :name="!drag ? 'flip-list' : null"> <transition-group type="transition"
:name="!drag ? 'flip-list' : null">
<b-card <b-card
no-body no-body
v-hover v-hover
@@ -332,12 +418,17 @@
<b-card-header class="p-2 border-0"> <b-card-header class="p-2 border-0">
<div class="row"> <div class="row">
<div class="col-2" v-if="new_supermarket.editmode"> <div class="col-2" v-if="new_supermarket.editmode">
<button type="button" class="btn btn-lg shadow-none"><i class="fas fa-arrows-alt-v"></i></button> <button type="button"
class="btn btn-lg shadow-none"><i
class="fas fa-arrows-alt-v"></i></button>
</div> </div>
<div :class="new_supermarket.editmode ? 'col-10' : 'col-12'"> <div
:class="new_supermarket.editmode ? 'col-10' : 'col-12'">
<h5 class="mt-1 mb-1"> <h5 class="mt-1 mb-1">
{{ 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-danger"/> <i class="btn fas fa-trash fa-lg px-2 text-danger"/>
</b-button> </b-button>
</h5> </h5>
@@ -347,8 +438,12 @@
</b-card> </b-card>
</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"
<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" /> 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"/>
<draggable <draggable
class="list-group" class="list-group"
:list="notSupermarketCategory" :list="notSupermarketCategory"
@@ -359,17 +454,26 @@
v-if="new_supermarket.editmode" v-if="new_supermarket.editmode"
v-bind="{ animation: 200 }" v-bind="{ animation: 200 }"
> >
<transition-group type="transition" :name="!drag ? 'flip-list' : null"> <transition-group type="transition"
<b-card no-body v-hover class="mt-1 list-group-item p-2" style="cursor: move" v-for="c in notSupermarketCategory" v-bind:key="c.id" :border-variant="'danger'"> :name="!drag ? 'flip-list' : null">
<b-card no-body v-hover class="mt-1 list-group-item p-2"
style="cursor: move"
v-for="c in notSupermarketCategory" v-bind:key="c.id"
:border-variant="'danger'">
<b-card-header class="p-2 border-0"> <b-card-header class="p-2 border-0">
<div class="row"> <div class="row">
<div class="col-2" v-if="new_supermarket.editmode"> <div class="col-2" v-if="new_supermarket.editmode">
<button type="button" class="btn btn-lg shadow-none"><i class="fas fa-arrows-alt-v"></i></button> <button type="button"
class="btn btn-lg shadow-none"><i
class="fas fa-arrows-alt-v"></i></button>
</div> </div>
<div :class="new_supermarket.editmode ? 'col-10' : 'col-12'"> <div
:class="new_supermarket.editmode ? 'col-10' : 'col-12'">
<h5 class="mt-1 mb-1"> <h5 class="mt-1 mb-1">
{{ 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-primary"/> <i class="btn fas fa-trash fa-lg px-2 text-primary"/>
</b-button> </b-button>
</h5> </h5>
@@ -382,16 +486,26 @@
</b-card> </b-card>
</div> </div>
</div> </div>
</div>
</div>
</div>
</div>
</div>
</b-tab> </b-tab>
<!-- settings tab --> <!-- settings tab -->
<b-tab :title="$t('Settings')"> <b-tab>
<template #title>
<i class="fas fa-user-cog fa-fw d-block d-md-none"></i>
<span class="d-none d-md-block">{{ $t('Settings') }}</span>
</template>
<div class="row justify-content-center"> <div class="row justify-content-center">
<div class="col-12 col-md-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" class="form-control settings-checkbox" v-model="settings.mealplan_autoadd_shopping" @change="saveSettings" /> <input type="checkbox" class="form-control settings-checkbox"
v-model="settings.mealplan_autoadd_shopping" @change="saveSettings"/>
</div> </div>
</div> </div>
<div class="row sm mb-3"> <div class="row sm mb-3">
@@ -403,7 +517,8 @@
<div class="row"> <div class="row">
<div class="col col-md-6">{{ $t("mealplan_autoexclude_onhand") }}</div> <div class="col col-md-6">{{ $t("mealplan_autoexclude_onhand") }}</div>
<div class="col col-md-6 text-right"> <div class="col col-md-6 text-right">
<input type="checkbox" class="form-control settings-checkbox" v-model="settings.mealplan_autoexclude_onhand" @change="saveSettings" /> <input type="checkbox" class="form-control settings-checkbox"
v-model="settings.mealplan_autoexclude_onhand" @change="saveSettings"/>
</div> </div>
</div> </div>
<div class="row sm mb-3"> <div class="row sm mb-3">
@@ -416,7 +531,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" class="form-control settings-checkbox" v-model="settings.mealplan_autoinclude_related" @change="saveSettings" /> <input type="checkbox" class="form-control settings-checkbox"
v-model="settings.mealplan_autoinclude_related" @change="saveSettings"/>
</div> </div>
</div> </div>
<div class="row sm mb-3"> <div class="row sm mb-3">
@@ -453,7 +569,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" class="form-control" v-model="settings.shopping_auto_sync" @change="saveSettings" /> <input type="number" class="form-control" v-model="settings.shopping_auto_sync"
@change="saveSettings"/>
</div> </div>
</div> </div>
<div class="row sm mb-3"> <div class="row sm mb-3">
@@ -466,7 +583,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" class="form-control settings-checkbox" v-model="settings.shopping_add_onhand" @change="saveSettings" /> <input type="checkbox" class="form-control settings-checkbox"
v-model="settings.shopping_add_onhand" @change="saveSettings"/>
</div> </div>
</div> </div>
<div class="row sm mb-3"> <div class="row sm mb-3">
@@ -479,7 +597,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" class="form-control" v-model="settings.shopping_recent_days" @change="saveSettings" /> <input type="number" class="form-control" v-model="settings.shopping_recent_days"
@change="saveSettings"/>
</div> </div>
</div> </div>
<div class="row sm mb-3"> <div class="row sm mb-3">
@@ -492,7 +611,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" class="form-control settings-checkbox" v-model="settings.filter_to_supermarket" @change="saveSettings" /> <input type="checkbox" class="form-control settings-checkbox"
v-model="settings.filter_to_supermarket" @change="saveSettings"/>
</div> </div>
</div> </div>
<div class="row sm mb-3"> <div class="row sm mb-3">
@@ -505,7 +625,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" class="form-control" min="1" v-model="settings.default_delay" @change="saveSettings" /> <input type="number" class="form-control" 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">
@@ -544,7 +665,8 @@
<div class="row"> <div class="row">
<div class="col col-md-6">{{ $t("left_handed") }}</div> <div class="col col-md-6">{{ $t("left_handed") }}</div>
<div class="col col-md-6"> <div class="col col-md-6">
<input type="checkbox" class="form-control settings-checkbox" v-model="settings.left_handed" @change="saveSettings" /> <input type="checkbox" class="form-control settings-checkbox"
v-model="settings.left_handed" @change="saveSettings"/>
</div> </div>
</div> </div>
<div class="row sm mb-3"> <div class="row sm mb-3">
@@ -561,9 +683,10 @@
</b-tabs> </b-tabs>
<transition name="slided-fade"> <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="row fixed-bottom p-2 b-1 border-top text-center d-flex d-md-none"
style="background: rgba(255, 255, 255, 0.6);width: 105%;" v-if="current_tab === 0">
<div class="col-6"> <div class="col-6">
<a class="btn btn-block btn-success shadow-none" @click="entrymode = !entrymode" <a class="btn btn-block btn-success shadow-none" @click="entrymode = !entrymode; "
><i class="fas fa-cart-plus"></i> ><i class="fas fa-cart-plus"></i>
{{ $t("New Entry") }} {{ $t("New Entry") }}
</a> </a>
@@ -571,10 +694,14 @@
<div class="col-6"> <div class="col-6">
<b-dropdown id="dropdown-dropup" block dropup variant="primary" class="shadow-none"> <b-dropdown id="dropdown-dropup" block dropup variant="primary" class="shadow-none">
<template #button-content><i class="fas fa-download"></i> {{ $t("Export") }}</template> <template #button-content><i class="fas fa-download"></i> {{ $t("Export") }}</template>
<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')"
<DownloadCSV :items="csvData" :delim="settings.csv_delim" name="shopping.csv" :label="$t('download_csv')" icon="fas fa-file-csv" /> icon="far fa-file-pdf"/>
<CopyToClipboard :items="csvData" :settings="settings" :label="$t('copy_to_clipboard')" icon="fas fa-clipboard-list" /> <DownloadCSV :items="csvData" :delim="settings.csv_delim" name="shopping.csv"
<CopyToClipboard :items="csvData" :settings="settings" format="table" :label="$t('copy_markdown_table')" icon="fab fa-markdown" /> :label="$t('download_csv')" icon="fas fa-file-csv"/>
<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"/>
</b-dropdown> </b-dropdown>
</div> </div>
</div> </div>
@@ -585,23 +712,32 @@
<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="ui.selected_supermarket" :options="supermarkets" text-field="name" value-field="id" size="sm"></b-form-select> <b-form-select v-model="ui.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="!ui.selected_supermarket"> <b-form-group v-bind:label="$t('ShowUncategorizedFood')" label-for="popover-input-4" content-cols="1"
class="mb-1" v-if="!ui.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="ui.selected_supermarket"> <b-form-group v-bind:label="$t('SupermarketCategoriesOnly')" label-for="popover-input-5"
content-cols="1" class="mb-1" v-if="ui.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">{{
<b-button size="sm" variant="secondary" class="mr-3" @click="$root.$emit('bv::hide::popover')">{{ $t("Close") }} </b-button> $t("Reset")
}}
</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>
@@ -610,7 +746,8 @@
<ContextMenuItem> <ContextMenuItem>
<b-input-group> <b-input-group>
<template #prepend> <template #prepend>
<span class="dropdown-item p-2 text-decoration-none" style="user-select: none !important"><i class="fas fa-cubes"></i> {{ $t("MoveCategory") }}</span> <span class="dropdown-item p-2 text-decoration-none" style="user-select: none !important"><i
class="fas fa-cubes"></i> {{ $t("MoveCategory") }}</span>
</template> </template>
<b-form-select <b-form-select
class="form-control mt-1 mr-1" class="form-control mt-1 mr-1"
@@ -639,7 +776,8 @@
delayThis(contextData) delayThis(contextData)
" "
> >
<a class="dropdown-item p-2" href="#"><i class="fas fa-hourglass"></i> {{ $t("DelayFor", { hours: delay }) }}</a> <a class="dropdown-item p-2" href="#"><i class="fas fa-hourglass"></i>
{{ $t("DelayFor", {hours: delay}) }}</a>
</ContextMenuItem> </ContextMenuItem>
<ContextMenuItem <ContextMenuItem
@click=" @click="
@@ -655,11 +793,14 @@
deleteThis(contextData) 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>
<shopping-modal v-if="new_recipe.id" :recipe="new_recipe" :servings="parseInt(add_recipe_servings)" :modal_id="new_recipe.id" @finish="finishShopping" :list_recipe="new_recipe.list_recipe" /> <shopping-modal v-if="new_recipe.id" :recipe="new_recipe" :servings="parseInt(add_recipe_servings)"
:modal_id="new_recipe.id" @finish="finishShopping" :list_recipe="new_recipe.list_recipe"/>
</div> </div>
</template> </template>
@@ -753,6 +894,7 @@ export default {
id: undefined, id: undefined,
}, },
add_recipe_servings: 1, add_recipe_servings: 1,
shopping_list_height: '60vh'
} }
}, },
computed: { computed: {
@@ -881,7 +1023,13 @@ export default {
}, },
deep: true, deep: true,
}, },
entrymode: {
handler() {
if (this.entrymode) {
document.getElementById('shoppinglist').scrollTop = 0
}
}
},
new_recipe: { new_recipe: {
handler() { handler() {
this.add_recipe_servings = this.new_recipe.servings this.add_recipe_servings = this.new_recipe.servings
@@ -965,7 +1113,7 @@ export default {
.then((results) => { .then((results) => {
if (results?.data) { if (results?.data) {
this.items.push(results.data) this.items.push(results.data)
StandardToasts.makeStandardToast(StandardToasts.SUCCESS_CREATE) StandardToasts.makeStandardToast(this,StandardToasts.SUCCESS_CREATE)
} else { } else {
console.log("no data returned") console.log("no data returned")
} }
@@ -973,8 +1121,7 @@ export default {
this.clear += 1 this.clear += 1
}) })
.catch((err) => { .catch((err) => {
console.log(err) StandardToasts.makeStandardToast(this,StandardToasts.FAIL_CREATE, err)
StandardToasts.makeStandardToast(StandardToasts.FAIL_CREATE)
}) })
}, },
deleteSupermarket: function (s) { deleteSupermarket: function (s) {
@@ -982,11 +1129,10 @@ export default {
api.destroySupermarket(s.id) api.destroySupermarket(s.id)
.then(() => { .then(() => {
this.getSupermarkets() this.getSupermarkets()
StandardToasts.makeStandardToast(StandardToasts.SUCCESS_DELETE) StandardToasts.makeStandardToast(this,StandardToasts.SUCCESS_DELETE)
}) })
.catch((err) => { .catch((err) => {
console.log(err) StandardToasts.makeStandardToast(this,StandardToasts.FAIL_DELETE, err)
StandardToasts.makeStandardToast(StandardToasts.FAIL_DELETE)
}) })
}, },
deleteCategory: function (c) { deleteCategory: function (c) {
@@ -998,11 +1144,10 @@ export default {
this.getSupermarkets() this.getSupermarkets()
this.getShoppingCategories() this.getShoppingCategories()
this.new_supermarket.value.category_to_supermarket = this.new_supermarket.value.category_to_supermarket.filter((x) => x.category.id != c_id) this.new_supermarket.value.category_to_supermarket = this.new_supermarket.value.category_to_supermarket.filter((x) => x.category.id != c_id)
StandardToasts.makeStandardToast(StandardToasts.SUCCESS_DELETE) StandardToasts.makeStandardToast(this,StandardToasts.SUCCESS_DELETE)
}) })
.catch((err) => { .catch((err) => {
console.log(err) StandardToasts.makeStandardToast(this,StandardToasts.FAIL_DELETE, err)
StandardToasts.makeStandardToast(StandardToasts.FAIL_DELETE)
}) })
}, },
resetFilters: function () { resetFilters: function () {
@@ -1031,7 +1176,7 @@ export default {
promises.push(this.saveThis({id: entry, delay_until: delay_date}, false)) promises.push(this.saveThis({id: entry, delay_until: delay_date}, false))
}) })
Promise.all(promises).then(() => { Promise.all(promises).then(() => {
StandardToasts.makeStandardToast(StandardToasts.SUCCESS_UPDATE) StandardToasts.makeStandardToast(this,StandardToasts.SUCCESS_UPDATE)
this.items = this.items.filter((x) => !entries.includes(x.id)) this.items = this.items.filter((x) => !entries.includes(x.id))
this.delay = this.defaultDelay this.delay = this.defaultDelay
}) })
@@ -1041,11 +1186,10 @@ export default {
api.destroyShoppingListRecipe(recipe) api.destroyShoppingListRecipe(recipe)
.then((x) => { .then((x) => {
this.items = this.items.filter((x) => x.list_recipe !== recipe) this.items = this.items.filter((x) => x.list_recipe !== recipe)
StandardToasts.makeStandardToast(StandardToasts.SUCCESS_DELETE) StandardToasts.makeStandardToast(this,StandardToasts.SUCCESS_DELETE)
}) })
.catch((err) => { .catch((err) => {
console.log(err) StandardToasts.makeStandardToast(this,StandardToasts.FAIL_DELETE, err)
StandardToasts.makeStandardToast(StandardToasts.FAIL_DELETE)
}) })
}, },
deleteThis: function (item) { deleteThis: function (item) {
@@ -1061,15 +1205,14 @@ export default {
entries.forEach((x) => { entries.forEach((x) => {
promises.push( promises.push(
api.destroyShoppingListEntry(x).catch((err) => { api.destroyShoppingListEntry(x).catch((err) => {
console.log(err) StandardToasts.makeStandardToast(this,StandardToasts.FAIL_DELETE, err)
StandardToasts.makeStandardToast(StandardToasts.FAIL_DELETE)
}) })
) )
}) })
Promise.all(promises).then((result) => { Promise.all(promises).then((result) => {
this.items = this.items.filter((x) => !entries.includes(x.id)) this.items = this.items.filter((x) => !entries.includes(x.id))
StandardToasts.makeStandardToast(StandardToasts.SUCCESS_DELETE) StandardToasts.makeStandardToast(this,StandardToasts.SUCCESS_DELETE)
}) })
}, },
editSupermarket(s) { editSupermarket(s) {
@@ -1117,9 +1260,8 @@ export default {
} }
}) })
.catch((err) => { .catch((err) => {
console.log(err)
if (!autosync) { if (!autosync) {
StandardToasts.makeStandardToast(StandardToasts.FAIL_FETCH) StandardToasts.makeStandardToast(this,StandardToasts.FAIL_FETCH, err)
} }
}) })
}, },
@@ -1187,7 +1329,8 @@ export default {
}) })
.then((entries) => { .then((entries) => {
entries.forEach((x) => { entries.forEach((x) => {
api.destroyShoppingListEntry(x).then((result) => {}) api.destroyShoppingListEntry(x).then((result) => {
})
}) })
}) })
}, },
@@ -1204,11 +1347,10 @@ export default {
let api = ApiApiFactory() let api = ApiApiFactory()
api.partialUpdateUserPreference(this.settings.user, this.settings) api.partialUpdateUserPreference(this.settings.user, this.settings)
.then((result) => { .then((result) => {
StandardToasts.makeStandardToast(StandardToasts.SUCCESS_UPDATE) StandardToasts.makeStandardToast(this,StandardToasts.SUCCESS_UPDATE)
}) })
.catch((err) => { .catch((err) => {
console.log(err) StandardToasts.makeStandardToast(this,StandardToasts.FAIL_UPDATE, err)
StandardToasts.makeStandardToast(StandardToasts.FAIL_UPDATE)
}) })
}, },
saveThis: function (thisItem, toast = true) { saveThis: function (thisItem, toast = true) {
@@ -1219,24 +1361,22 @@ export default {
.createShoppingListEntry(thisItem) .createShoppingListEntry(thisItem)
.then((result) => { .then((result) => {
if (toast) { if (toast) {
StandardToasts.makeStandardToast(StandardToasts.SUCCESS_CREATE) StandardToasts.makeStandardToast(this,StandardToasts.SUCCESS_CREATE)
} }
}) })
.catch((err) => { .catch((err) => {
console.log(err) StandardToasts.makeStandardToast(this,StandardToasts.FAIL_CREATE, err)
StandardToasts.makeStandardToast(StandardToasts.FAIL_CREATE)
}) })
} else { } else {
return api return api
.partialUpdateShoppingListEntry(thisItem.id, thisItem) .partialUpdateShoppingListEntry(thisItem.id, thisItem)
.then((result) => { .then((result) => {
if (toast) { if (toast) {
StandardToasts.makeStandardToast(StandardToasts.SUCCESS_UPDATE) StandardToasts.makeStandardToast(this,StandardToasts.SUCCESS_UPDATE)
} }
}) })
.catch((err) => { .catch((err) => {
console.log(err, err.response) StandardToasts.makeStandardToast(this,StandardToasts.FAIL_UPDATE, err)
StandardToasts.makeStandardToast(StandardToasts.FAIL_UPDATE)
}) })
} }
}, },
@@ -1266,8 +1406,7 @@ export default {
}) })
.catch((err) => { .catch((err) => {
this.auto_sync_blocked = false this.auto_sync_blocked = false
console.log(err, err.response) StandardToasts.makeStandardToast(this,StandardToasts.FAIL_UPDATE, err)
StandardToasts.makeStandardToast(StandardToasts.FAIL_UPDATE)
}) })
}, },
updateFood: function (food, field) { updateFood: function (food, field) {
@@ -1280,14 +1419,13 @@ export default {
return api return api
.partialUpdateFood(food.id, food) .partialUpdateFood(food.id, food)
.then((result) => { .then((result) => {
StandardToasts.makeStandardToast(StandardToasts.SUCCESS_UPDATE) StandardToasts.makeStandardToast(this,StandardToasts.SUCCESS_UPDATE)
if (food?.numchild > 0) { if (food?.numchild > 0) {
this.getShoppingList() // if food has children, just get the whole list. probably could be more efficient this.getShoppingList() // if food has children, just get the whole list. probably could be more efficient
} }
}) })
.catch((err) => { .catch((err) => {
console.log(err, Object.keys(err)) StandardToasts.makeStandardToast(this,StandardToasts.FAIL_UPDATE, err)
StandardToasts.makeStandardToast(StandardToasts.FAIL_UPDATE)
}) })
}, },
updateServings(e, plan) { updateServings(e, plan) {
@@ -1301,26 +1439,26 @@ export default {
let api = new ApiApiFactory() let api = new ApiApiFactory()
api.createSupermarketCategory({name: this.new_category.value}) api.createSupermarketCategory({name: this.new_category.value})
.then((result) => { .then((result) => {
StandardToasts.makeStandardToast(StandardToasts.SUCCESS_CREATE) StandardToasts.makeStandardToast(this,StandardToasts.SUCCESS_CREATE)
this.shopping_categories.push(result.data) this.shopping_categories.push(result.data)
this.new_category.value = undefined this.new_category.value = undefined
}) })
.catch((err) => { .catch((err) => {
console.log(err, Object.keys(err)) console.log(err, Object.keys(err))
StandardToasts.makeStandardToast(StandardToasts.FAIL_CREATE) StandardToasts.makeStandardToast(this,StandardToasts.FAIL_CREATE)
}) })
}, },
addSupermarket: function () { addSupermarket: function () {
let api = new ApiApiFactory() let api = new ApiApiFactory()
api.createSupermarket({name: this.new_supermarket.value}) api.createSupermarket({name: this.new_supermarket.value})
.then((result) => { .then((result) => {
StandardToasts.makeStandardToast(StandardToasts.SUCCESS_CREATE) StandardToasts.makeStandardToast(this,StandardToasts.SUCCESS_CREATE)
this.supermarkets.push(result.data) this.supermarkets.push(result.data)
this.new_supermarket.value = undefined this.new_supermarket.value = undefined
}) })
.catch((err) => { .catch((err) => {
console.log(err, Object.keys(err)) console.log(err, Object.keys(err))
StandardToasts.makeStandardToast(StandardToasts.FAIL_CREATE) StandardToasts.makeStandardToast(this,StandardToasts.FAIL_CREATE)
}) })
}, },
saveSupermarketCategoryOrder(e) { saveSupermarketCategoryOrder(e) {
@@ -1346,11 +1484,11 @@ export default {
apiClient apiClient
.destroySupermarketCategoryRelation(e.removed.element.id) .destroySupermarketCategoryRelation(e.removed.element.id)
.then((result) => { .then((result) => {
StandardToasts.makeStandardToast(StandardToasts.SUCCESS_UPDATE) StandardToasts.makeStandardToast(this,StandardToasts.SUCCESS_UPDATE)
}) })
.catch((err) => { .catch((err) => {
console.log(err, Object.keys(err)) console.log(err, Object.keys(err))
StandardToasts.makeStandardToast(StandardToasts.FAIL_UPDATE) StandardToasts.makeStandardToast(this,StandardToasts.FAIL_UPDATE)
this.supermarkets = temp_supermarkets this.supermarkets = temp_supermarkets
}) })
} }
@@ -1373,11 +1511,11 @@ export default {
.then((updated_supermarket) => { .then((updated_supermarket) => {
let idx = this.supermarkets.indexOf((x) => x.id === updated_supermarket.id) let idx = this.supermarkets.indexOf((x) => x.id === updated_supermarket.id)
Vue.set(this.supermarkets, idx, updated_supermarket) Vue.set(this.supermarkets, idx, updated_supermarket)
StandardToasts.makeStandardToast(StandardToasts.SUCCESS_UPDATE) StandardToasts.makeStandardToast(this,StandardToasts.SUCCESS_UPDATE)
}) })
.catch((err) => { .catch((err) => {
console.log(err, Object.keys(err)) console.log(err, Object.keys(err))
StandardToasts.makeStandardToast(StandardToasts.FAIL_UPDATE) StandardToasts.makeStandardToast(this,StandardToasts.FAIL_UPDATE)
this.supermarkets = temp_supermarkets this.supermarkets = temp_supermarkets
}) })
} }
@@ -1387,11 +1525,11 @@ export default {
.then((updated_supermarket) => { .then((updated_supermarket) => {
let idx = this.supermarkets.indexOf((x) => x.id === updated_supermarket.id) let idx = this.supermarkets.indexOf((x) => x.id === updated_supermarket.id)
Vue.set(this.supermarkets, idx, updated_supermarket) Vue.set(this.supermarkets, idx, updated_supermarket)
StandardToasts.makeStandardToast(StandardToasts.SUCCESS_UPDATE) StandardToasts.makeStandardToast(this,StandardToasts.SUCCESS_UPDATE)
}) })
.catch((err) => { .catch((err) => {
console.log(err, Object.keys(err)) console.log(err, Object.keys(err))
StandardToasts.makeStandardToast(StandardToasts.FAIL_UPDATE) StandardToasts.makeStandardToast(this,StandardToasts.FAIL_UPDATE)
this.supermarkets = temp_supermarkets this.supermarkets = temp_supermarkets
}) })
} }
@@ -1417,13 +1555,21 @@ export default {
this.getShoppingList() this.getShoppingList()
}, },
editRecipeList(e, r) { editRecipeList(e, r) {
this.new_recipe = { id: r.recipe_mealplan.recipe, name: r.recipe_mealplan.recipe_name, servings: r.recipe_mealplan.servings, list_recipe: r.list_recipe } this.new_recipe = {
id: r.recipe_mealplan.recipe,
name: r.recipe_mealplan.recipe_name,
servings: r.recipe_mealplan.servings,
list_recipe: r.list_recipe
}
this.$nextTick(function () { this.$nextTick(function () {
this.$bvModal.show(`shopping_${this.new_recipe.id}`) this.$bvModal.show(`shopping_${this.new_recipe.id}`)
}) })
// this.$bvModal.show(`shopping_${this.new_recipe.id}`) // this.$bvModal.show(`shopping_${this.new_recipe.id}`)
}, },
handleResize: function () {
this.shoppinglist_height = document.body.clientHeight - 115
},
}, },
directives: { directives: {
hover: { hover: {
@@ -1476,7 +1622,8 @@ export default {
} }
.slide-fade-enter, .slide-fade-leave-to .slide-fade-enter, .slide-fade-leave-to
/* .slider-fade-leave-active below version 2.1.8 */ { /* .slider-fade-leave-active below version 2.1.8 */
{
transform: translateX(10px); transform: translateX(10px);
opacity: 0; opacity: 0;
} }
@@ -1485,30 +1632,43 @@ export default {
font-size: 20px; font-size: 20px;
} }
@media screen and (max-width: 768px) {
#shoppinglist { #shoppinglist {
display: flex; display: flex;
flex-direction: column; flex-direction: column;
flex-grow: 1; flex-grow: 1;
overflow-y: scroll; overflow-y: scroll;
overflow-x: hidden; overflow-x: hidden;
height: 60vh; /* TODO use proper fill height here to not render list underneath bottom buttons */ height: calc(100% - 170px);
padding-right: 8px !important;
} }
#id_base_container {
padding-right: 5px;
padding-left: 5px;
} }
@media screen and (min-height: 700px) and (max-width: 768px) {
@media (min-width: 768px) {
#shoppinglist { #shoppinglist {
display: flex; overflow-y: auto;
flex-direction: column; overflow-x: auto;
flex-grow: 1; height: auto;
overflow-y: scroll; margin: auto;
overflow-x: hidden; }
height: 72vh;
padding-right: 8px !important; #id_base_container {
padding-right: 15px;
padding-left: 15px;
} }
} }
.settings-checkbox { .settings-checkbox {
font-size: 0.3rem; font-size: 0.3rem;
} }
@media (max-width: 767.9px) {
.recipe-table {
font-size: 14px;
}
}
</style> </style>

View File

@@ -87,21 +87,21 @@ export default {
apiFactory.createRecipeBook({ name: name }).then((r) => { apiFactory.createRecipeBook({ name: name }).then((r) => {
this.books.push(r.data) this.books.push(r.data)
this.selected_book = r.data this.selected_book = r.data
StandardToasts.makeStandardToast(StandardToasts.SUCCESS_CREATE) StandardToasts.makeStandardToast(this,StandardToasts.SUCCESS_CREATE)
}) })
}, },
addToBook: function () { addToBook: function () {
let apiFactory = new ApiApiFactory() let apiFactory = new ApiApiFactory()
apiFactory.createRecipeBookEntry({ book: this.selected_book.id, recipe: this.recipe.id }).then((r) => { apiFactory.createRecipeBookEntry({ book: this.selected_book.id, recipe: this.recipe.id }).then((r) => {
this.recipe_book_list.push(r.data) this.recipe_book_list.push(r.data)
StandardToasts.makeStandardToast(StandardToasts.SUCCESS_CREATE) StandardToasts.makeStandardToast(this,StandardToasts.SUCCESS_CREATE)
}) })
}, },
removeFromBook: function (book_entry) { removeFromBook: function (book_entry) {
let apiFactory = new ApiApiFactory() let apiFactory = new ApiApiFactory()
apiFactory.destroyRecipeBookEntry(book_entry.id).then((r) => { apiFactory.destroyRecipeBookEntry(book_entry.id).then((r) => {
this.recipe_book_list = this.recipe_book_list.filter((e) => e.id !== book_entry.id) this.recipe_book_list = this.recipe_book_list.filter((e) => e.id !== book_entry.id)
StandardToasts.makeStandardToast(StandardToasts.SUCCESS_DELETE) StandardToasts.makeStandardToast(this,StandardToasts.SUCCESS_DELETE)
}) })
}, },
loadBookEntries: function () { loadBookEntries: function () {

View File

@@ -66,7 +66,7 @@ export default {
} }
this.genericAPI(this.Models.FOOD, this.Actions.SHOPPING, params).then((result) => { this.genericAPI(this.Models.FOOD, this.Actions.SHOPPING, params).then((result) => {
this.shopping = true this.shopping = true
StandardToasts.makeStandardToast(StandardToasts.SUCCESS_CREATE) StandardToasts.makeStandardToast(this,StandardToasts.SUCCESS_CREATE)
}) })
}, },
cancelDelete() { cancelDelete() {
@@ -80,7 +80,7 @@ export default {
this.genericAPI(this.Models.FOOD, this.Actions.SHOPPING, params).then(() => { this.genericAPI(this.Models.FOOD, this.Actions.SHOPPING, params).then(() => {
this.shopping = false this.shopping = false
this.$refs["shopping" + this.item.id].$emit("close") this.$refs["shopping" + this.item.id].$emit("close")
StandardToasts.makeStandardToast(StandardToasts.SUCCESS_DELETE) StandardToasts.makeStandardToast(this,StandardToasts.SUCCESS_DELETE)
}) })
}, },
}, },

View File

@@ -114,10 +114,10 @@ export default {
apiClient apiClient
.updateRecipeBook(this.book_copy.id, this.book_copy) .updateRecipeBook(this.book_copy.id, this.book_copy)
.then((result) => { .then((result) => {
StandardToasts.makeStandardToast(StandardToasts.SUCCESS_UPDATE) StandardToasts.makeStandardToast(this,StandardToasts.SUCCESS_UPDATE)
}) })
.catch((error) => { .catch((err) => {
StandardToasts.makeStandardToast(StandardToasts.FAIL_UPDATE) StandardToasts.makeStandardToast(this,StandardToasts.FAIL_UPDATE, err)
}) })
}, },
refreshData: function () { refreshData: function () {
@@ -135,10 +135,10 @@ export default {
.destroyRecipeBook(this.book.id) .destroyRecipeBook(this.book.id)
.then((result) => { .then((result) => {
this.$emit("refresh") this.$emit("refresh")
StandardToasts.makeStandardToast(StandardToasts.SUCCESS_DELETE) StandardToasts.makeStandardToast(this,StandardToasts.SUCCESS_DELETE)
}) })
.catch((error) => { .catch((err) => {
StandardToasts.makeStandardToast(StandardToasts.FAIL_DELETE) StandardToasts.makeStandardToast(this,StandardToasts.FAIL_DELETE, err)
}) })
} }
}, },

View File

@@ -140,7 +140,7 @@ export default {
}, },
nothingSelected() { nothingSelected() {
if (this.multiple || !this.initial_single_selection) { if (this.multiple || !this.initial_single_selection) {
return this.selected_objects.length === 0 && this.initial_selection.length === 0 return this.selected_objects?.length === 0 && this.initial_selection?.length === 0
} else { } else {
return !this.selected_objects && !this.initial_single_selection return !this.selected_objects && !this.initial_single_selection
} }
@@ -187,7 +187,7 @@ export default {
console.log('CREATEING NEW with -> ' , e) console.log('CREATEING NEW with -> ' , e)
this.genericAPI(this.model, this.Actions.CREATE, {name: e}).then(result => { this.genericAPI(this.model, this.Actions.CREATE, {name: e}).then(result => {
let createdObj = result.data?.results ?? result.data let createdObj = result.data?.results ?? result.data
StandardToasts.makeStandardToast(StandardToasts.SUCCESS_CREATE) StandardToasts.makeStandardToast(this,StandardToasts.SUCCESS_CREATE)
if (this.multiple) { if (this.multiple) {
this.selected_objects.push(createdObj) this.selected_objects.push(createdObj)
} else { } else {
@@ -196,7 +196,7 @@ export default {
this.objects.push(createdObj) this.objects.push(createdObj)
this.selectionChanged() this.selectionChanged()
}).catch((r, err) => { }).catch((r, err) => {
StandardToasts.makeStandardToast(StandardToasts.FAIL_CREATE) StandardToasts.makeStandardToast(this,StandardToasts.FAIL_CREATE)
}) })
}, },
}, },

View File

@@ -175,20 +175,20 @@ export default {
this.genericAPI(this.Models.RECIPE, this.Actions.SHOPPING, params) this.genericAPI(this.Models.RECIPE, this.Actions.SHOPPING, params)
.then((result) => { .then((result) => {
if (del_shopping) { if (del_shopping) {
StandardToasts.makeStandardToast(StandardToasts.SUCCESS_DELETE) StandardToasts.makeStandardToast(this,StandardToasts.SUCCESS_DELETE)
} else if (this.selected_shoppingrecipe) { } else if (this.selected_shoppingrecipe) {
StandardToasts.makeStandardToast(StandardToasts.SUCCESS_UPDATE) StandardToasts.makeStandardToast(this,StandardToasts.SUCCESS_UPDATE)
} else { } else {
StandardToasts.makeStandardToast(StandardToasts.SUCCESS_CREATE) StandardToasts.makeStandardToast(this,StandardToasts.SUCCESS_CREATE)
} }
}) })
.catch((err) => { .catch((err) => {
if (del_shopping) { if (del_shopping) {
StandardToasts.makeStandardToast(StandardToasts.FAIL_DELETE) StandardToasts.makeStandardToast(this,StandardToasts.FAIL_DELETE, err)
} else if (this.selected_shoppingrecipe) { } else if (this.selected_shoppingrecipe) {
StandardToasts.makeStandardToast(StandardToasts.FAIL_UPDATE) StandardToasts.makeStandardToast(this,StandardToasts.FAIL_UPDATE, err)
} else { } else {
StandardToasts.makeStandardToast(StandardToasts.FAIL_CREATE) StandardToasts.makeStandardToast(this,StandardToasts.FAIL_CREATE, err)
} }
this.$emit("shopping-failed") this.$emit("shopping-failed")
}) })

View File

@@ -127,4 +127,10 @@ export default {
.meal-plan-card { .meal-plan-card {
background-color: #fff; background-color: #fff;
} }
@media (max-width: 767.9px) {
.meal-plan-card {
font-size: 13px;
}
}
</style> </style>

View File

@@ -90,23 +90,23 @@ export default {
apiFactory.createRecipeBook({name: name}).then(r => { apiFactory.createRecipeBook({name: name}).then(r => {
this.books.push(r.data) this.books.push(r.data)
this.selected_book = r.data this.selected_book = r.data
StandardToasts.makeStandardToast(StandardToasts.SUCCESS_CREATE) StandardToasts.makeStandardToast(this,StandardToasts.SUCCESS_CREATE)
}) })
}, },
addToBook: function () { addToBook: function () {
let apiFactory = new ApiApiFactory() let apiFactory = new ApiApiFactory()
apiFactory.createRecipeBookEntry({book: this.selected_book.id, recipe: this.recipe.id}).then(r => { apiFactory.createRecipeBookEntry({book: this.selected_book.id, recipe: this.recipe.id}).then(r => {
this.recipe_book_list.push(r.data) this.recipe_book_list.push(r.data)
StandardToasts.makeStandardToast(StandardToasts.SUCCESS_CREATE) StandardToasts.makeStandardToast(this,StandardToasts.SUCCESS_CREATE)
}).catch(e => { }).catch(err => {
StandardToasts.makeStandardToast(StandardToasts.FAIL_UPDATE) StandardToasts.makeStandardToast(this,StandardToasts.FAIL_UPDATE, err)
}) })
}, },
removeFromBook: function (book_entry) { removeFromBook: function (book_entry) {
let apiFactory = new ApiApiFactory() let apiFactory = new ApiApiFactory()
apiFactory.destroyRecipeBookEntry(book_entry.id).then(r => { apiFactory.destroyRecipeBookEntry(book_entry.id).then(r => {
this.recipe_book_list = this.recipe_book_list.filter(e => e.id !== book_entry.id) this.recipe_book_list = this.recipe_book_list.filter(e => e.id !== book_entry.id)
StandardToasts.makeStandardToast(StandardToasts.SUCCESS_DELETE) StandardToasts.makeStandardToast(this,StandardToasts.SUCCESS_DELETE)
}) })
}, },
loadBookEntries: function () { loadBookEntries: function () {

View File

@@ -173,13 +173,13 @@ export default {
this.genericAPI(this.model, this.Actions.DELETE, { id: this.item1.id }) this.genericAPI(this.model, this.Actions.DELETE, { id: this.item1.id })
.then((result) => { .then((result) => {
this.$emit("finish-action") this.$emit("finish-action")
StandardToasts.makeStandardToast(StandardToasts.SUCCESS_DELETE) StandardToasts.makeStandardToast(this,StandardToasts.SUCCESS_DELETE)
}) })
.catch((err) => { .catch((err) => {
if (err.response.status === 403){ if (err.response.status === 403){
StandardToasts.makeStandardToast(StandardToasts.FAIL_DELETE_PROTECTED) StandardToasts.makeStandardToast(this,StandardToasts.FAIL_DELETE_PROTECTED, err)
}else { }else {
StandardToasts.makeStandardToast(StandardToasts.FAIL_DELETE) StandardToasts.makeStandardToast(this,StandardToasts.FAIL_DELETE, err)
} }
this.$emit("finish-action", "cancel") this.$emit("finish-action", "cancel")
}) })
@@ -190,22 +190,21 @@ export default {
this.genericAPI(this.model, this.Actions.CREATE, this.form_data) this.genericAPI(this.model, this.Actions.CREATE, this.form_data)
.then((result) => { .then((result) => {
this.$emit("finish-action", { item: result.data }) this.$emit("finish-action", { item: result.data })
StandardToasts.makeStandardToast(StandardToasts.SUCCESS_CREATE) StandardToasts.makeStandardToast(this,StandardToasts.SUCCESS_CREATE)
}) })
.catch((err) => { .catch((err) => {
console.log(err) console.log(err)
StandardToasts.makeStandardToast(StandardToasts.FAIL_CREATE) StandardToasts.makeStandardToast(this,StandardToasts.FAIL_CREATE)
this.$emit("finish-action", "cancel") this.$emit("finish-action", "cancel")
}) })
} else { } else {
this.genericAPI(this.model, this.Actions.UPDATE, this.form_data) this.genericAPI(this.model, this.Actions.UPDATE, this.form_data)
.then((result) => { .then((result) => {
this.$emit("finish-action") this.$emit("finish-action", { item: result.data })
StandardToasts.makeStandardToast(StandardToasts.SUCCESS_UPDATE) StandardToasts.makeStandardToast(this,StandardToasts.SUCCESS_UPDATE)
}) })
.catch((err) => { .catch((err) => {
console.log(err, err.response) StandardToasts.makeStandardToast(this,StandardToasts.FAIL_UPDATE, err)
StandardToasts.makeStandardToast(StandardToasts.FAIL_UPDATE)
this.$emit("finish-action", "cancel") this.$emit("finish-action", "cancel")
}) })
} }
@@ -224,11 +223,10 @@ export default {
this.genericAPI(this.model, this.Actions.MOVE, { source: this.item1.id, target: this.form_data.target.id }) this.genericAPI(this.model, this.Actions.MOVE, { source: this.item1.id, target: this.form_data.target.id })
.then((result) => { .then((result) => {
this.$emit("finish-action", { target: this.form_data.target.id }) this.$emit("finish-action", { target: this.form_data.target.id })
StandardToasts.makeStandardToast(StandardToasts.SUCCESS_MOVE) StandardToasts.makeStandardToast(this,StandardToasts.SUCCESS_MOVE)
}) })
.catch((err) => { .catch((err) => {
console.log(err) StandardToasts.makeStandardToast(this,StandardToasts.FAIL_MOVE, err)
StandardToasts.makeStandardToast(StandardToasts.FAIL_MOVE)
this.$emit("finish-action", "cancel") this.$emit("finish-action", "cancel")
}) })
}, },
@@ -248,13 +246,11 @@ export default {
target: this.form_data.target.id, target: this.form_data.target.id,
}) })
.then((result) => { .then((result) => {
this.$emit("finish-action", { target: this.form_data.target.id }) this.$emit("finish-action", { target: this.form_data.target.id, target_object: this.form_data.target }) //TODO temporary workaround to not change other apis
StandardToasts.makeStandardToast(StandardToasts.SUCCESS_MERGE) StandardToasts.makeStandardToast(this,StandardToasts.SUCCESS_MERGE)
}) })
.catch((err) => { .catch((err) => {
//TODO error checking not working with OpenAPI methods StandardToasts.makeStandardToast(this,StandardToasts.FAIL_MERGE, err)
console.log("Error", err)
StandardToasts.makeStandardToast(StandardToasts.FAIL_MERGE)
this.$emit("finish-action", "cancel") this.$emit("finish-action", "cancel")
}) })

View File

@@ -184,11 +184,11 @@ export default {
apiClient apiClient
.shoppingRecipe(this.recipe.id, shopping_recipe) .shoppingRecipe(this.recipe.id, shopping_recipe)
.then((result) => { .then((result) => {
StandardToasts.makeStandardToast(StandardToasts.SUCCESS_CREATE) StandardToasts.makeStandardToast(this,StandardToasts.SUCCESS_CREATE)
this.$emit("finish") this.$emit("finish")
}) })
.catch((err) => { .catch((err) => {
StandardToasts.makeStandardToast(StandardToasts.FAIL_CREATE) StandardToasts.makeStandardToast(this,StandardToasts.FAIL_CREATE, err)
}) })
this.$bvModal.hide(`shopping_${this.modal_id}`) this.$bvModal.hide(`shopping_${this.modal_id}`)

View File

@@ -162,10 +162,10 @@ export default {
this.servings_value = result.data.servings this.servings_value = result.data.servings
this.addToShopping() this.addToShopping()
} }
StandardToasts.makeStandardToast(StandardToasts.SUCCESS_CREATE) StandardToasts.makeStandardToast(this,StandardToasts.SUCCESS_CREATE)
}) })
.catch((error) => { .catch((err) => {
StandardToasts.makeStandardToast(StandardToasts.FAIL_CREATE) StandardToasts.makeStandardToast(this,StandardToasts.FAIL_CREATE, err)
}) })
}, },
createMealPlan(data) { createMealPlan(data) {
@@ -228,11 +228,11 @@ export default {
apiClient apiClient
.createRecipe(recipe) .createRecipe(recipe)
.then((new_recipe) => { .then((new_recipe) => {
StandardToasts.makeStandardToast(StandardToasts.SUCCESS_CREATE) StandardToasts.makeStandardToast(this,StandardToasts.SUCCESS_CREATE)
window.open(this.resolveDjangoUrl("view_recipe", new_recipe.data.id)) window.open(this.resolveDjangoUrl("view_recipe", new_recipe.data.id))
}) })
.catch((error) => { .catch((err) => {
StandardToasts.makeStandardToast(StandardToasts.FAIL_CREATE) StandardToasts.makeStandardToast(this,StandardToasts.FAIL_CREATE, err)
}) })
}) })
}, },

View File

@@ -1,34 +1,43 @@
<template> <template>
<div id="shopping_line_item"> <div id="shopping_line_item" class="pt-1">
<b-row align-h="start"> <b-row align-h="start" v-touch:start="startHandler" v-touch:moving="moveHandler" v-touch:end="endHandler"
<b-col cols="2" md="2" class="justify-content-start align-items-center d-flex d-md-none pr-0" v-if="settings.left_handed"> ref="shopping_line_item" class="invis-border">
<input type="checkbox" class="form-control form-control-sm checkbox-control-mobile" :checked="formatChecked" @change="updateChecked" :key="entries[0].id" /> <b-col cols="2" md="2" class="justify-content-start align-items-center d-flex d-md-none pr-0"
<b-button size="sm" @click="showDetails = !showDetails" class="d-inline-block d-md-none p-0" variant="link"> v-if="settings.left_handed">
<div class="text-nowrap"><i class="fa fa-chevron-right rotate" :class="showDetails ? 'rotated' : ''"></i></div> <input type="checkbox" class="form-control form-control-sm checkbox-control-mobile"
:checked="formatChecked" @change="updateChecked" :key="entries[0].id"/>
<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> </b-button>
</b-col> </b-col>
<b-col cols="1" class="align-items-center d-flex"> <b-col cols="1" class="align-items-center d-flex">
<div class="dropdown b-dropdown position-static inline-block" data-html2canvas-ignore="true" @click.stop="$emit('open-context-menu', $event, entries)"> <div class="dropdown b-dropdown position-static inline-block" data-html2canvas-ignore="true"
@click.stop="$emit('open-context-menu', $event, entries)">
<button <button
aria-haspopup="true" aria-haspopup="true"
aria-expanded="false" aria-expanded="false"
type="button" type="button"
:class="settings.left_handed ? 'dropdown-spacing' : ''" :class="settings.left_handed ? 'dropdown-spacing' : ''"
class="btn dropdown-toggle btn-link text-decoration-none text-body pr-0 pl-1 dropdown-toggle-no-caret" class="btn dropdown-toggle btn-link text-decoration-none text-body pr-0 pl-1 dropdown-toggle-no-caret">
>
<i class="fas fa-ellipsis-v"></i> <i class="fas fa-ellipsis-v"></i>
</button> </button>
</div> </div>
</b-col> </b-col>
<b-col cols="1" class="px-1 justify-content-center align-items-center d-none d-md-flex"> <b-col cols="1" class="px-1 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" /> <input type="checkbox" class="form-control form-control-sm checkbox-control"
:checked="formatChecked" @change="updateChecked" :key="entries[0].id"/>
</b-col> </b-col>
<b-col cols="8"> <b-col cols="8">
<b-row class="d-flex h-100"> <b-row class="d-flex h-100">
<b-col cols="6" md="3" class="d-flex align-items-center" v-if="Object.entries(formatAmount).length == 1"> <b-col cols="6" md="3" class="d-flex align-items-center"
<strong class="mr-1">{{ Object.entries(formatAmount)[0][1] }}</strong> {{ Object.entries(formatAmount)[0][0] }} v-if="Object.entries(formatAmount).length == 1">
<strong class="mr-1">{{ Object.entries(formatAmount)[0][1] }}</strong>
{{ Object.entries(formatAmount)[0][0] }}
</b-col> </b-col>
<b-col cols="6" md="3" class="d-flex flex-column" v-if="Object.entries(formatAmount).length != 1"> <b-col cols="6" md="3" class="d-flex flex-column"
v-if="Object.entries(formatAmount).length != 1">
<div class="small" v-for="(x, i) in Object.entries(formatAmount)" :key="i"> <div class="small" v-for="(x, i) in Object.entries(formatAmount)" :key="i">
{{ x[1] }} &ensp; {{ x[1] }} &ensp;
{{ x[0] }} {{ x[0] }}
@@ -38,20 +47,27 @@
<b-col cols="6" md="6" class="align-items-center d-flex pl-0 pr-0 pl-md-2 pr-md-2"> <b-col cols="6" md="6" 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="3" data-html2canvas-ignore="true" class="align-items-center d-none d-md-flex justify-content-end"> <b-col cols="3" data-html2canvas-ignore="true"
<b-button size="sm" @click="showDetails = !showDetails" class="p-0 mr-0 mr-md-2 p-md-2 text-decoration-none" variant="link"> class="align-items-center d-none d-md-flex justify-content-end">
<b-button size="sm" @click="showDetails = !showDetails"
class="p-0 mr-0 mr-md-2 p-md-2 text-decoration-none" variant="link">
<div class="text-nowrap"> <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> <i class="fa fa-chevron-right rotate" :class="showDetails ? 'rotated' : ''"></i>
<span class="d-none d-md-inline-block">{{ $t("Details") }}</span>
</div> </div>
</b-button> </b-button>
</b-col> </b-col>
</b-row> </b-row>
</b-col> </b-col>
<b-col cols="2" class="justify-content-start align-items-center d-flex d-md-none pl-0 pr-0" v-if="!settings.left_handed"> <b-col cols="2" class="justify-content-start align-items-center d-flex d-md-none pl-0 pr-0"
<b-button size="sm" @click="showDetails = !showDetails" class="d-inline-block d-md-none p-0" variant="link"> v-if="!settings.left_handed">
<div class="text-nowrap"><i class="fa fa-chevron-right rotate" :class="showDetails ? 'rotated' : ''"></i></div> <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> </b-button>
<input type="checkbox" class="form-control form-control-sm checkbox-control-mobile" :checked="formatChecked" @change="updateChecked" :key="entries[0].id" /> <input type="checkbox" class="form-control form-control-sm checkbox-control-mobile"
:checked="formatChecked" @change="updateChecked" :key="entries[0].id"/>
</b-col> </b-col>
</b-row> </b-row>
<b-row align-h="center" class="d-none d-md-flex"> <b-row align-h="center" class="d-none d-md-flex">
@@ -83,11 +99,14 @@
</b-col> </b-col>
</b-row> </b-row>
<b-row align-h="start"> <b-row align-h="start">
<b-col cols="3" md="2" class="justify-content-start align-items-center d-flex d-md-none pr-0" v-if="settings.left_handed"> <b-col cols="3" md="2" class="justify-content-start align-items-center d-flex d-md-none pr-0"
<input type="checkbox" class="form-control form-control-sm checkbox-control-mobile" :checked="formatChecked" @change="updateChecked" :key="entries[0].id" /> v-if="settings.left_handed">
<input type="checkbox" class="form-control form-control-sm checkbox-control-mobile"
:checked="formatChecked" @change="updateChecked" :key="entries[0].id"/>
</b-col> </b-col>
<b-col cols="2" md="1" class="align-items-center d-flex"> <b-col cols="2" md="1" class="align-items-center d-flex">
<div class="dropdown b-dropdown position-static inline-block" data-html2canvas-ignore="true" @click.stop="$emit('open-context-menu', $event, e)"> <div class="dropdown b-dropdown position-static inline-block" data-html2canvas-ignore="true"
@click.stop="$emit('open-context-menu', $event, e)">
<button <button
aria-haspopup="true" aria-haspopup="true"
aria-expanded="false" aria-expanded="false"
@@ -100,7 +119,8 @@
</div> </div>
</b-col> </b-col>
<b-col cols="1" class="justify-content-center align-items-center d-none d-md-flex"> <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" /> <input type="checkbox" class="form-control form-control-sm checkbox-control"
:checked="formatChecked" @change="updateChecked" :key="entries[0].id"/>
</b-col> </b-col>
<b-col cols="7" md="9"> <b-col cols="7" md="9">
<b-row class="d-flex align-items-center h-100"> <b-row class="d-flex align-items-center h-100">
@@ -111,16 +131,20 @@
{{ formatOneFood(e) }} {{ formatOneFood(e) }}
</b-col> </b-col>
<b-col cols="12" class="d-flex d-md-none"> <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> <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" v-if="!settings.left_handed"> <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" /> v-if="!settings.left_handed">
<input type="checkbox" class="form-control form-control-sm checkbox-control-mobile"
:checked="formatChecked" @change="updateChecked" :key="entries[0].id"/>
</b-col> </b-col>
</b-row> </b-row>
<hr class="w-75" v-if="x !== entries.length - 1" /> <hr class="w-75 mt-1 mb-1 mt-md-3 mb-md-3" v-if="x !== entries.length - 1"/>
<div class="pb-4" v-if="x === entries.length - 1"></div> <div class="pb-1 pb-md-4" v-if="x === entries.length - 1"></div>
</div> </div>
</div> </div>
<hr class="m-1" v-if="!showDetails"/> <hr class="m-1" v-if="!showDetails"/>
@@ -132,7 +156,8 @@
<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>
<a class="dropdown-item p-2" href="#"><i class="fas fa-pizza-slice"></i> {{ $t("Servings") }}</a> <a class="dropdown-item p-2" href="#"><i class="fas fa-pizza-slice"></i>
{{ $t("Servings") }}</a>
</template> </template>
<div @click.prevent.stop> <div @click.prevent.stop>
<b-form-input class="mt-2" min="0" type="number" v-model="servings"></b-form-input> <b-form-input class="mt-2" min="0" type="number" v-model="servings"></b-form-input>
@@ -141,6 +166,9 @@
</ContextMenuItem> </ContextMenuItem>
</template> </template>
</ContextMenu> </ContextMenu>
<i class="fa fa-hourglass fa-lg" style="display: none; position: absolute" aria-hidden="true"
ref="delay_icon"></i>
<i class="fa fa-check fa-lg" style="display: none; position: absolute" aria-hidden="true" ref="check_icon"></i>
</div> </div>
</template> </template>
@@ -152,8 +180,10 @@ import ContextMenu from "@/components/ContextMenu/ContextMenu"
import ContextMenuItem from "@/components/ContextMenu/ContextMenuItem" import ContextMenuItem from "@/components/ContextMenu/ContextMenuItem"
import {ApiMixin} from "@/utils/utils" import {ApiMixin} from "@/utils/utils"
import RecipeCard from "./RecipeCard.vue" import RecipeCard from "./RecipeCard.vue"
import Vue2TouchEvents from "vue2-touch-events"
Vue.use(BootstrapVue) Vue.use(BootstrapVue)
Vue.use(Vue2TouchEvents)
export default { export default {
// TODO ApiGenerator doesn't capture and share error information - would be nice to share error details when available // TODO ApiGenerator doesn't capture and share error information - would be nice to share error details when available
@@ -173,6 +203,8 @@ export default {
showDetails: false, showDetails: false,
recipe: undefined, recipe: undefined,
servings: 1, servings: 1,
dragStartX: 0,
distance_left: 0
} }
}, },
computed: { computed: {
@@ -249,6 +281,71 @@ export default {
timeStyle: "short", timeStyle: "short",
}).format(Date.parse(datetime)) }).format(Date.parse(datetime))
}, },
startHandler: function (event) {
if (event.changedTouches.length > 0) {
this.dragStartX = event.changedTouches[0].clientX
}
},
getOffset(el) {
let rect = el.getBoundingClientRect();
return {
left: rect.left + window.scrollX,
top: rect.top + window.scrollY,
right: rect.right - window.scrollX,
};
},
moveHandler: function (event) {
let item = this.$refs['shopping_line_item'];
this.distance_left = event.changedTouches[0].clientX - this.dragStartX;
item.style.marginLeft = this.distance_left
item.style.marginRight = -this.distance_left
item.style.backgroundColor = '#ddbf86'
item.style.border = "1px solid #000"
let delay_icon = this.$refs['delay_icon']
let check_icon = this.$refs['check_icon']
let color_factor = Math.abs(this.distance_left) / 100
if (this.distance_left > 0) {
item.parentElement.parentElement.style.backgroundColor = 'rgba(130,170,139,0)'.replace(/[^,]+(?=\))/, color_factor)
check_icon.style.display = "block"
check_icon.style.left = this.getOffset(item.parentElement.parentElement).left + 40
check_icon.style.top = this.getOffset(item.parentElement.parentElement).top - 92
check_icon.style.opacity = color_factor - 0.3
} else {
item.parentElement.parentElement.style.backgroundColor = 'rgba(185,135,102,0)'.replace(/[^,]+(?=\))/, color_factor)
delay_icon.style.display = "block"
console.log(item.parentElement.parentElement.clientWidth)
delay_icon.style.left = this.getOffset(item.parentElement.parentElement).right - 40
delay_icon.style.top = this.getOffset(item.parentElement.parentElement).top - 92
delay_icon.style.opacity = color_factor - 0.3
}
},
endHandler: function (event) {
let item = this.$refs['shopping_line_item'];
item.removeAttribute('style');
item.parentElement.parentElement.removeAttribute('style');
let delay_icon = this.$refs['delay_icon']
let check_icon = this.$refs['check_icon']
delay_icon.style.display = "none"
check_icon.style.display = "none"
if (Math.abs(this.distance_left) > window.screen.width / 6) {
if (this.distance_left > 0) {
let checked = false;
this.entries.forEach((cur) => {
checked = cur.checked
})
let update = {entries: this.entries.map((x) => x.id), checked: !checked}
this.$emit("update-checkbox", update)
} else {
this.$emit("update-delaythis", this.entries)
}
}
},
formatOneAmount: function (item) { formatOneAmount: function (item) {
return item?.amount ?? 1 return item?.amount ?? 1
}, },
@@ -303,6 +400,7 @@ export default {
} else { } else {
update = {entries: [item], checked: !item.checked} update = {entries: [item], checked: !item.checked}
} }
console.log(update)
this.$emit("update-checkbox", update) this.$emit("update-checkbox", update)
}, },
}, },
@@ -351,4 +449,8 @@ export default {
padding-right: 0 !important; padding-right: 0 !important;
} }
} }
.invis-border {
border: 1px solid transparent;
}
</style> </style>

View File

@@ -114,7 +114,7 @@
"Create_New_Shopping Category": "Neue Einkaufskategorie erstellen", "Create_New_Shopping Category": "Neue Einkaufskategorie erstellen",
"Automate": "Automatisieren", "Automate": "Automatisieren",
"Type": "Typ", "Type": "Typ",
"and_up": "& Hoch", "and_up": "& Höher",
"Unrated": "Unbewertet", "Unrated": "Unbewertet",
"Shopping_list": "Einkaufsliste", "Shopping_list": "Einkaufsliste",
"step_time_minutes": "Schritt Dauer in Minuten", "step_time_minutes": "Schritt Dauer in Minuten",
@@ -217,7 +217,7 @@
"Inherit": "Vererben", "Inherit": "Vererben",
"InheritFields": "Feldwerte vererben", "InheritFields": "Feldwerte vererben",
"ShowUncategorizedFood": "Zeige nicht zugeordnete", "ShowUncategorizedFood": "Zeige nicht zugeordnete",
"DelayFor": "Um {hours } verschieben", "DelayFor": "Um {hours} Stunden verschieben",
"Warning": "Warnung", "Warning": "Warnung",
"NoCategory": "Keine Kategorie ausgewählt.", "NoCategory": "Keine Kategorie ausgewählt.",
"ShowDelayed": "Zeige verschobene Elemente", "ShowDelayed": "Zeige verschobene Elemente",
@@ -283,7 +283,7 @@
"copy_markdown_table": "Als Markdown-Tabelle kopieren", "copy_markdown_table": "Als Markdown-Tabelle kopieren",
"in_shopping": "In Einkaufsliste", "in_shopping": "In Einkaufsliste",
"DelayUntil": "Verzögerung bis", "DelayUntil": "Verzögerung bis",
"QuickEntry": "Schnelleinstieg", "QuickEntry": "Einfach",
"shopping_add_onhand": "Automatisch vorrätig", "shopping_add_onhand": "Automatisch vorrätig",
"related_recipes": "Ähnliche Rezepte", "related_recipes": "Ähnliche Rezepte",
"today_recipes": "Rezepte des Tages", "today_recipes": "Rezepte des Tages",
@@ -354,5 +354,24 @@
"Page": "Seite", "Page": "Seite",
"Reset": "Zurücksetzen", "Reset": "Zurücksetzen",
"search_rank": "Such-Rang", "search_rank": "Such-Rang",
"paste_ingredients": "Zutaten einfügen" "paste_ingredients": "Zutaten einfügen",
"Ingredient Editor": "Zutateneditor",
"Protected": "Geschützt",
"not": "nicht",
"warning_duplicate_filter": "Warnung: Wegen technischen Limitierungen können mehrere Filter der selben Kombination (und/oder/nicht) zu unerwarteten Ergebnissen führen.",
"and_down": "& Niedriger",
"enable_expert": "Expertenmodus aktivieren",
"filter_name": "Name des Filters",
"shared_with": "Geteilt mit",
"asc": "Aufsteigend",
"desc": "Absteigend",
"book_filter_help": "Schließt zusätzlich zu den manuell hinzugefügten Rezepte, alle Rezepte die dem Filter entsprechen ein.",
"recipe_name": "Rezeptname",
"paste_ingredients_placeholder": "Zutatenliste hier einfügen",
"ingredient_list": "Zutatenliste",
"filter": "Filter",
"err_deleting_protected_resource": "Das Objekt, das du versuchst zu löschen, wird noch benutzt und kann nicht gelöscht werden.",
"Create Food": "Zutat erstellen",
"additional_options": "Weitere Möglichkeiten",
"create_food_desc": "Zutat erstellen und mit diesem Rezept verknüpfen"
} }

View File

@@ -243,6 +243,7 @@
"OfflineAlert": "You are offline, shopping list may not syncronize.", "OfflineAlert": "You are offline, shopping list may not syncronize.",
"shopping_share": "Share Shopping List", "shopping_share": "Share Shopping List",
"shopping_auto_sync": "Autosync", "shopping_auto_sync": "Autosync",
"one_url_per_line": "One URL per line",
"mealplan_autoadd_shopping": "Auto Add Meal Plan", "mealplan_autoadd_shopping": "Auto Add Meal Plan",
"mealplan_autoexclude_onhand": "Exclude Food On Hand", "mealplan_autoexclude_onhand": "Exclude Food On Hand",
"mealplan_autoinclude_related": "Add Related Recipes", "mealplan_autoinclude_related": "Add Related Recipes",
@@ -339,8 +340,11 @@
"Website": "Website", "Website": "Website",
"App": "App", "App": "App",
"Bookmarklet": "Bookmarklet", "Bookmarklet": "Bookmarklet",
"click_image_import": "Click the image you want to import for this recipe",
"no_more_images_found": "No additional images found on Website.",
"import_duplicates": "To prevent duplicates recipes with the same name as existing ones are ignored. Check this box to import everything.", "import_duplicates": "To prevent duplicates recipes with the same name as existing ones are ignored. Check this box to import everything.",
"paste_json": "Paste json or html source here to load recipe.", "paste_json": "Paste json or html source here to load recipe.",
"Click_To_Edit": "Click to edit",
"search_no_recipes": "Could not find any recipes!", "search_no_recipes": "Could not find any recipes!",
"search_import_help_text": "Import a recipe from an external website or application.", "search_import_help_text": "Import a recipe from an external website or application.",
"search_create_help_text": "Create a new recipe directly in Tandoor.", "search_create_help_text": "Create a new recipe directly in Tandoor.",
@@ -386,7 +390,15 @@
"Advanced": "Advanced", "Advanced": "Advanced",
"Page": "Page", "Page": "Page",
"Reset": "Reset", "Reset": "Reset",
"Options": "Options",
"Create Food": "Create Food", "Create Food": "Create Food",
"create_food_desc": "Create a food and link it to this recipe.", "create_food_desc": "Create a food and link it to this recipe.",
"additional_options": "Additional Options" "additional_options": "Additional Options",
"Importer_Help": "More information and help on this importer:",
"Documentation": "Documentation",
"Select_App_To_Import": "Please select an App to Import from",
"Import_Supported": "Import supported",
"Export_Supported": "Export supported",
"Import_Not_Yet_Supported": "Import not yet supported",
"Export_Not_Yet_Supported": "Export not yet supported"
} }

View File

@@ -1,7 +1,7 @@
// containing all data and functions regarding the different integrations // containing all data and functions regarding the different integrations
export const INTEGRATIONS = [ export const INTEGRATIONS = [
{id: 'DEFAULT', name: "Tandoor", import: true, export: true, help_url: 'https://docs.tandoor.dev/features/import_export/#default'}, {id: 'DEFAULT', name: "Tandoor", import: true, export: true, help_url: 'https://docs.tandoor.dev/features/import_export/#default', img_src: 'https://raw.githubusercontent.com/TandoorRecipes/recipes/develop/docs/logo_color.svg'},
{id: 'CHEFTAP', name: "Cheftap", import: true, export: false, help_url: 'https://docs.tandoor.dev/features/import_export/#cheftap'}, {id: 'CHEFTAP', name: "Cheftap", import: true, export: false, help_url: 'https://docs.tandoor.dev/features/import_export/#cheftap'},
{id: 'CHOWDOWN', name: "Chowdown", import: true, export: false, help_url: 'https://docs.tandoor.dev/features/import_export/#chowdown'}, {id: 'CHOWDOWN', name: "Chowdown", import: true, export: false, help_url: 'https://docs.tandoor.dev/features/import_export/#chowdown'},
{id: 'COOKBOOKAPP', name: "CookBookApp", import: true, export: false, help_url: 'https://docs.tandoor.dev/features/import_export/#cookbookapp'}, {id: 'COOKBOOKAPP', name: "CookBookApp", import: true, export: false, help_url: 'https://docs.tandoor.dev/features/import_export/#cookbookapp'},

View File

@@ -987,9 +987,40 @@ export interface InlineResponse20010 {
previous?: string | null; previous?: string | null;
/** /**
* *
* @type {Array<ViewLog>} * @type {Array<Unit>}
* @memberof InlineResponse20010 * @memberof InlineResponse20010
*/ */
results?: Array<Unit>;
}
/**
*
* @export
* @interface InlineResponse20011
*/
export interface InlineResponse20011 {
/**
*
* @type {number}
* @memberof InlineResponse20011
*/
count?: number;
/**
*
* @type {string}
* @memberof InlineResponse20011
*/
next?: string | null;
/**
*
* @type {string}
* @memberof InlineResponse20011
*/
previous?: string | null;
/**
*
* @type {Array<ViewLog>}
* @memberof InlineResponse20011
*/
results?: Array<ViewLog>; results?: Array<ViewLog>;
} }
/** /**
@@ -1080,10 +1111,10 @@ export interface InlineResponse2004 {
previous?: string | null; previous?: string | null;
/** /**
* *
* @type {Array<Keyword>} * @type {Array<Ingredient>}
* @memberof InlineResponse2004 * @memberof InlineResponse2004
*/ */
results?: Array<Keyword>; results?: Array<Ingredient>;
} }
/** /**
* *
@@ -1111,10 +1142,10 @@ export interface InlineResponse2005 {
previous?: string | null; previous?: string | null;
/** /**
* *
* @type {Array<RecipeOverview>} * @type {Array<Keyword>}
* @memberof InlineResponse2005 * @memberof InlineResponse2005
*/ */
results?: Array<RecipeOverview>; results?: Array<Keyword>;
} }
/** /**
* *
@@ -1142,10 +1173,10 @@ export interface InlineResponse2006 {
previous?: string | null; previous?: string | null;
/** /**
* *
* @type {Array<Step>} * @type {Array<RecipeOverview>}
* @memberof InlineResponse2006 * @memberof InlineResponse2006
*/ */
results?: Array<Step>; results?: Array<RecipeOverview>;
} }
/** /**
* *
@@ -1173,10 +1204,10 @@ export interface InlineResponse2007 {
previous?: string | null; previous?: string | null;
/** /**
* *
* @type {Array<SupermarketCategoryRelation>} * @type {Array<Step>}
* @memberof InlineResponse2007 * @memberof InlineResponse2007
*/ */
results?: Array<SupermarketCategoryRelation>; results?: Array<Step>;
} }
/** /**
* *
@@ -1204,10 +1235,10 @@ export interface InlineResponse2008 {
previous?: string | null; previous?: string | null;
/** /**
* *
* @type {Array<SyncLog>} * @type {Array<SupermarketCategoryRelation>}
* @memberof InlineResponse2008 * @memberof InlineResponse2008
*/ */
results?: Array<SyncLog>; results?: Array<SupermarketCategoryRelation>;
} }
/** /**
* *
@@ -1235,10 +1266,10 @@ export interface InlineResponse2009 {
previous?: string | null; previous?: string | null;
/** /**
* *
* @type {Array<Unit>} * @type {Array<SyncLog>}
* @memberof InlineResponse2009 * @memberof InlineResponse2009
*/ */
results?: Array<Unit>; results?: Array<SyncLog>;
} }
/** /**
* *
@@ -5614,10 +5645,12 @@ export const ApiApiAxiosParamCreator = function (configuration?: Configuration)
}, },
/** /**
* *
* @param {number} [page] A page number within the paginated result set.
* @param {number} [pageSize] Number of results to return per page.
* @param {*} [options] Override http request option. * @param {*} [options] Override http request option.
* @throws {RequiredError} * @throws {RequiredError}
*/ */
listIngredients: async (options: any = {}): Promise<RequestArgs> => { listIngredients: async (page?: number, pageSize?: number, options: any = {}): Promise<RequestArgs> => {
const localVarPath = `/api/ingredient/`; const localVarPath = `/api/ingredient/`;
// use dummy base URL string because the URL constructor only accepts absolute URLs. // use dummy base URL string because the URL constructor only accepts absolute URLs.
const localVarUrlObj = new URL(localVarPath, DUMMY_BASE_URL); const localVarUrlObj = new URL(localVarPath, DUMMY_BASE_URL);
@@ -5630,6 +5663,14 @@ export const ApiApiAxiosParamCreator = function (configuration?: Configuration)
const localVarHeaderParameter = {} as any; const localVarHeaderParameter = {} as any;
const localVarQueryParameter = {} as any; const localVarQueryParameter = {} as any;
if (page !== undefined) {
localVarQueryParameter['page'] = page;
}
if (pageSize !== undefined) {
localVarQueryParameter['page_size'] = pageSize;
}
setSearchParams(localVarUrlObj, localVarQueryParameter, options.query); setSearchParams(localVarUrlObj, localVarQueryParameter, options.query);
@@ -10494,11 +10535,13 @@ export const ApiApiFp = function(configuration?: Configuration) {
}, },
/** /**
* *
* @param {number} [page] A page number within the paginated result set.
* @param {number} [pageSize] Number of results to return per page.
* @param {*} [options] Override http request option. * @param {*} [options] Override http request option.
* @throws {RequiredError} * @throws {RequiredError}
*/ */
async listIngredients(options?: any): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise<Array<Ingredient>>> { async listIngredients(page?: number, pageSize?: number, options?: any): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise<InlineResponse2004>> {
const localVarAxiosArgs = await localVarAxiosParamCreator.listIngredients(options); const localVarAxiosArgs = await localVarAxiosParamCreator.listIngredients(page, pageSize, options);
return createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration); return createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration);
}, },
/** /**
@@ -10511,7 +10554,7 @@ export const ApiApiFp = function(configuration?: Configuration) {
* @param {*} [options] Override http request option. * @param {*} [options] Override http request option.
* @throws {RequiredError} * @throws {RequiredError}
*/ */
async listKeywords(query?: string, root?: number, tree?: number, page?: number, pageSize?: number, options?: any): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise<InlineResponse2004>> { async listKeywords(query?: string, root?: number, tree?: number, page?: number, pageSize?: number, options?: any): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise<InlineResponse2005>> {
const localVarAxiosArgs = await localVarAxiosParamCreator.listKeywords(query, root, tree, page, pageSize, options); const localVarAxiosArgs = await localVarAxiosParamCreator.listKeywords(query, root, tree, page, pageSize, options);
return createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration); return createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration);
}, },
@@ -10585,7 +10628,7 @@ export const ApiApiFp = function(configuration?: Configuration) {
* @param {*} [options] Override http request option. * @param {*} [options] Override http request option.
* @throws {RequiredError} * @throws {RequiredError}
*/ */
async listRecipes(query?: string, keywords?: number, keywordsOr?: number, keywordsAnd?: number, keywordsOrNot?: number, keywordsAndNot?: number, foods?: number, foodsOr?: number, foodsAnd?: number, foodsOrNot?: number, foodsAndNot?: number, units?: number, rating?: number, books?: string, booksOr?: number, booksAnd?: number, booksOrNot?: number, booksAndNot?: number, internal?: string, random?: string, _new?: string, timescooked?: number, cookedon?: string, createdon?: string, updatedon?: string, viewedon?: string, makenow?: string, page?: number, pageSize?: number, options?: any): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise<InlineResponse2005>> { async listRecipes(query?: string, keywords?: number, keywordsOr?: number, keywordsAnd?: number, keywordsOrNot?: number, keywordsAndNot?: number, foods?: number, foodsOr?: number, foodsAnd?: number, foodsOrNot?: number, foodsAndNot?: number, units?: number, rating?: number, books?: string, booksOr?: number, booksAnd?: number, booksOrNot?: number, booksAndNot?: number, internal?: string, random?: string, _new?: string, timescooked?: number, cookedon?: string, createdon?: string, updatedon?: string, viewedon?: string, makenow?: string, page?: number, pageSize?: number, options?: any): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise<InlineResponse2006>> {
const localVarAxiosArgs = await localVarAxiosParamCreator.listRecipes(query, keywords, keywordsOr, keywordsAnd, keywordsOrNot, keywordsAndNot, foods, foodsOr, foodsAnd, foodsOrNot, foodsAndNot, units, rating, books, booksOr, booksAnd, booksOrNot, booksAndNot, internal, random, _new, timescooked, cookedon, createdon, updatedon, viewedon, makenow, page, pageSize, options); const localVarAxiosArgs = await localVarAxiosParamCreator.listRecipes(query, keywords, keywordsOr, keywordsAnd, keywordsOrNot, keywordsAndNot, foods, foodsOr, foodsAnd, foodsOrNot, foodsAndNot, units, rating, books, booksOr, booksAnd, booksOrNot, booksAndNot, internal, random, _new, timescooked, cookedon, createdon, updatedon, viewedon, makenow, page, pageSize, options);
return createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration); return createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration);
}, },
@@ -10628,7 +10671,7 @@ export const ApiApiFp = function(configuration?: Configuration) {
* @param {*} [options] Override http request option. * @param {*} [options] Override http request option.
* @throws {RequiredError} * @throws {RequiredError}
*/ */
async listSteps(recipe?: number, query?: string, page?: number, pageSize?: number, options?: any): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise<InlineResponse2006>> { async listSteps(recipe?: number, query?: string, page?: number, pageSize?: number, options?: any): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise<InlineResponse2007>> {
const localVarAxiosArgs = await localVarAxiosParamCreator.listSteps(recipe, query, page, pageSize, options); const localVarAxiosArgs = await localVarAxiosParamCreator.listSteps(recipe, query, page, pageSize, options);
return createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration); return createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration);
}, },
@@ -10648,7 +10691,7 @@ export const ApiApiFp = function(configuration?: Configuration) {
* @param {*} [options] Override http request option. * @param {*} [options] Override http request option.
* @throws {RequiredError} * @throws {RequiredError}
*/ */
async listSupermarketCategoryRelations(page?: number, pageSize?: number, options?: any): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise<InlineResponse2007>> { async listSupermarketCategoryRelations(page?: number, pageSize?: number, options?: any): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise<InlineResponse2008>> {
const localVarAxiosArgs = await localVarAxiosParamCreator.listSupermarketCategoryRelations(page, pageSize, options); const localVarAxiosArgs = await localVarAxiosParamCreator.listSupermarketCategoryRelations(page, pageSize, options);
return createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration); return createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration);
}, },
@@ -10677,7 +10720,7 @@ export const ApiApiFp = function(configuration?: Configuration) {
* @param {*} [options] Override http request option. * @param {*} [options] Override http request option.
* @throws {RequiredError} * @throws {RequiredError}
*/ */
async listSyncLogs(page?: number, pageSize?: number, options?: any): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise<InlineResponse2008>> { async listSyncLogs(page?: number, pageSize?: number, options?: any): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise<InlineResponse2009>> {
const localVarAxiosArgs = await localVarAxiosParamCreator.listSyncLogs(page, pageSize, options); const localVarAxiosArgs = await localVarAxiosParamCreator.listSyncLogs(page, pageSize, options);
return createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration); return createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration);
}, },
@@ -10698,7 +10741,7 @@ export const ApiApiFp = function(configuration?: Configuration) {
* @param {*} [options] Override http request option. * @param {*} [options] Override http request option.
* @throws {RequiredError} * @throws {RequiredError}
*/ */
async listUnits(query?: string, page?: number, pageSize?: number, options?: any): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise<InlineResponse2009>> { async listUnits(query?: string, page?: number, pageSize?: number, options?: any): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise<InlineResponse20010>> {
const localVarAxiosArgs = await localVarAxiosParamCreator.listUnits(query, page, pageSize, options); const localVarAxiosArgs = await localVarAxiosParamCreator.listUnits(query, page, pageSize, options);
return createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration); return createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration);
}, },
@@ -10736,7 +10779,7 @@ export const ApiApiFp = function(configuration?: Configuration) {
* @param {*} [options] Override http request option. * @param {*} [options] Override http request option.
* @throws {RequiredError} * @throws {RequiredError}
*/ */
async listViewLogs(page?: number, pageSize?: number, options?: any): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise<InlineResponse20010>> { async listViewLogs(page?: number, pageSize?: number, options?: any): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise<InlineResponse20011>> {
const localVarAxiosArgs = await localVarAxiosParamCreator.listViewLogs(page, pageSize, options); const localVarAxiosArgs = await localVarAxiosParamCreator.listViewLogs(page, pageSize, options);
return createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration); return createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration);
}, },
@@ -12319,11 +12362,13 @@ export const ApiApiFactory = function (configuration?: Configuration, basePath?:
}, },
/** /**
* *
* @param {number} [page] A page number within the paginated result set.
* @param {number} [pageSize] Number of results to return per page.
* @param {*} [options] Override http request option. * @param {*} [options] Override http request option.
* @throws {RequiredError} * @throws {RequiredError}
*/ */
listIngredients(options?: any): AxiosPromise<Array<Ingredient>> { listIngredients(page?: number, pageSize?: number, options?: any): AxiosPromise<InlineResponse2004> {
return localVarFp.listIngredients(options).then((request) => request(axios, basePath)); return localVarFp.listIngredients(page, pageSize, options).then((request) => request(axios, basePath));
}, },
/** /**
* *
@@ -12335,7 +12380,7 @@ export const ApiApiFactory = function (configuration?: Configuration, basePath?:
* @param {*} [options] Override http request option. * @param {*} [options] Override http request option.
* @throws {RequiredError} * @throws {RequiredError}
*/ */
listKeywords(query?: string, root?: number, tree?: number, page?: number, pageSize?: number, options?: any): AxiosPromise<InlineResponse2004> { listKeywords(query?: string, root?: number, tree?: number, page?: number, pageSize?: number, options?: any): AxiosPromise<InlineResponse2005> {
return localVarFp.listKeywords(query, root, tree, page, pageSize, options).then((request) => request(axios, basePath)); return localVarFp.listKeywords(query, root, tree, page, pageSize, options).then((request) => request(axios, basePath));
}, },
/** /**
@@ -12404,7 +12449,7 @@ export const ApiApiFactory = function (configuration?: Configuration, basePath?:
* @param {*} [options] Override http request option. * @param {*} [options] Override http request option.
* @throws {RequiredError} * @throws {RequiredError}
*/ */
listRecipes(query?: string, keywords?: number, keywordsOr?: number, keywordsAnd?: number, keywordsOrNot?: number, keywordsAndNot?: number, foods?: number, foodsOr?: number, foodsAnd?: number, foodsOrNot?: number, foodsAndNot?: number, units?: number, rating?: number, books?: string, booksOr?: number, booksAnd?: number, booksOrNot?: number, booksAndNot?: number, internal?: string, random?: string, _new?: string, timescooked?: number, cookedon?: string, createdon?: string, updatedon?: string, viewedon?: string, makenow?: string, page?: number, pageSize?: number, options?: any): AxiosPromise<InlineResponse2005> { listRecipes(query?: string, keywords?: number, keywordsOr?: number, keywordsAnd?: number, keywordsOrNot?: number, keywordsAndNot?: number, foods?: number, foodsOr?: number, foodsAnd?: number, foodsOrNot?: number, foodsAndNot?: number, units?: number, rating?: number, books?: string, booksOr?: number, booksAnd?: number, booksOrNot?: number, booksAndNot?: number, internal?: string, random?: string, _new?: string, timescooked?: number, cookedon?: string, createdon?: string, updatedon?: string, viewedon?: string, makenow?: string, page?: number, pageSize?: number, options?: any): AxiosPromise<InlineResponse2006> {
return localVarFp.listRecipes(query, keywords, keywordsOr, keywordsAnd, keywordsOrNot, keywordsAndNot, foods, foodsOr, foodsAnd, foodsOrNot, foodsAndNot, units, rating, books, booksOr, booksAnd, booksOrNot, booksAndNot, internal, random, _new, timescooked, cookedon, createdon, updatedon, viewedon, makenow, page, pageSize, options).then((request) => request(axios, basePath)); return localVarFp.listRecipes(query, keywords, keywordsOr, keywordsAnd, keywordsOrNot, keywordsAndNot, foods, foodsOr, foodsAnd, foodsOrNot, foodsAndNot, units, rating, books, booksOr, booksAnd, booksOrNot, booksAndNot, internal, random, _new, timescooked, cookedon, createdon, updatedon, viewedon, makenow, page, pageSize, options).then((request) => request(axios, basePath));
}, },
/** /**
@@ -12443,7 +12488,7 @@ export const ApiApiFactory = function (configuration?: Configuration, basePath?:
* @param {*} [options] Override http request option. * @param {*} [options] Override http request option.
* @throws {RequiredError} * @throws {RequiredError}
*/ */
listSteps(recipe?: number, query?: string, page?: number, pageSize?: number, options?: any): AxiosPromise<InlineResponse2006> { listSteps(recipe?: number, query?: string, page?: number, pageSize?: number, options?: any): AxiosPromise<InlineResponse2007> {
return localVarFp.listSteps(recipe, query, page, pageSize, options).then((request) => request(axios, basePath)); return localVarFp.listSteps(recipe, query, page, pageSize, options).then((request) => request(axios, basePath));
}, },
/** /**
@@ -12461,7 +12506,7 @@ export const ApiApiFactory = function (configuration?: Configuration, basePath?:
* @param {*} [options] Override http request option. * @param {*} [options] Override http request option.
* @throws {RequiredError} * @throws {RequiredError}
*/ */
listSupermarketCategoryRelations(page?: number, pageSize?: number, options?: any): AxiosPromise<InlineResponse2007> { listSupermarketCategoryRelations(page?: number, pageSize?: number, options?: any): AxiosPromise<InlineResponse2008> {
return localVarFp.listSupermarketCategoryRelations(page, pageSize, options).then((request) => request(axios, basePath)); return localVarFp.listSupermarketCategoryRelations(page, pageSize, options).then((request) => request(axios, basePath));
}, },
/** /**
@@ -12487,7 +12532,7 @@ export const ApiApiFactory = function (configuration?: Configuration, basePath?:
* @param {*} [options] Override http request option. * @param {*} [options] Override http request option.
* @throws {RequiredError} * @throws {RequiredError}
*/ */
listSyncLogs(page?: number, pageSize?: number, options?: any): AxiosPromise<InlineResponse2008> { listSyncLogs(page?: number, pageSize?: number, options?: any): AxiosPromise<InlineResponse2009> {
return localVarFp.listSyncLogs(page, pageSize, options).then((request) => request(axios, basePath)); return localVarFp.listSyncLogs(page, pageSize, options).then((request) => request(axios, basePath));
}, },
/** /**
@@ -12506,7 +12551,7 @@ export const ApiApiFactory = function (configuration?: Configuration, basePath?:
* @param {*} [options] Override http request option. * @param {*} [options] Override http request option.
* @throws {RequiredError} * @throws {RequiredError}
*/ */
listUnits(query?: string, page?: number, pageSize?: number, options?: any): AxiosPromise<InlineResponse2009> { listUnits(query?: string, page?: number, pageSize?: number, options?: any): AxiosPromise<InlineResponse20010> {
return localVarFp.listUnits(query, page, pageSize, options).then((request) => request(axios, basePath)); return localVarFp.listUnits(query, page, pageSize, options).then((request) => request(axios, basePath));
}, },
/** /**
@@ -12540,7 +12585,7 @@ export const ApiApiFactory = function (configuration?: Configuration, basePath?:
* @param {*} [options] Override http request option. * @param {*} [options] Override http request option.
* @throws {RequiredError} * @throws {RequiredError}
*/ */
listViewLogs(page?: number, pageSize?: number, options?: any): AxiosPromise<InlineResponse20010> { listViewLogs(page?: number, pageSize?: number, options?: any): AxiosPromise<InlineResponse20011> {
return localVarFp.listViewLogs(page, pageSize, options).then((request) => request(axios, basePath)); return localVarFp.listViewLogs(page, pageSize, options).then((request) => request(axios, basePath));
}, },
/** /**
@@ -14156,12 +14201,14 @@ export class ApiApi extends BaseAPI {
/** /**
* *
* @param {number} [page] A page number within the paginated result set.
* @param {number} [pageSize] Number of results to return per page.
* @param {*} [options] Override http request option. * @param {*} [options] Override http request option.
* @throws {RequiredError} * @throws {RequiredError}
* @memberof ApiApi * @memberof ApiApi
*/ */
public listIngredients(options?: any) { public listIngredients(page?: number, pageSize?: number, options?: any) {
return ApiApiFp(this.configuration).listIngredients(options).then((request) => request(this.axios, this.basePath)); return ApiApiFp(this.configuration).listIngredients(page, pageSize, options).then((request) => request(this.axios, this.basePath));
} }
/** /**

View File

@@ -50,51 +50,142 @@ export class StandardToasts {
static FAIL_MOVE = "FAIL_MOVE" static FAIL_MOVE = "FAIL_MOVE"
static FAIL_MERGE = "FAIL_MERGE" static FAIL_MERGE = "FAIL_MERGE"
static makeStandardToast(toast, err_details = undefined) { //TODO err_details render very ugly, improve this maybe by using a custom toast component (in conjunction with error logging maybe) static makeStandardToast(context, toast, err) {
let title = ''
let msg = ''
let variant = ''
switch (toast) { switch (toast) {
case StandardToasts.SUCCESS_CREATE: case StandardToasts.SUCCESS_CREATE:
makeToast(i18n.tc("Success"), i18n.tc("success_creating_resource") + (err_details ? "\n" + err_details : ""), "success") variant = 'success'
title = i18n.tc("Success")
msg = i18n.tc("success_creating_resource")
break break
case StandardToasts.SUCCESS_FETCH: case StandardToasts.SUCCESS_FETCH:
makeToast(i18n.tc("Success"), i18n.tc("success_fetching_resource") + (err_details ? "\n" + err_details : ""), "success") variant = 'success'
title = i18n.tc("Success")
msg = i18n.tc("success_fetching_resource")
break break
case StandardToasts.SUCCESS_UPDATE: case StandardToasts.SUCCESS_UPDATE:
makeToast(i18n.tc("Success"), i18n.tc("success_updating_resource") + (err_details ? "\n" + err_details : ""), "success") variant = 'success'
title = i18n.tc("Success")
msg = i18n.tc("success_updating_resource")
break break
case StandardToasts.SUCCESS_DELETE: case StandardToasts.SUCCESS_DELETE:
makeToast(i18n.tc("Success"), i18n.tc("success_deleting_resource") + (err_details ? "\n" + err_details : ""), "success") variant = 'success'
title = i18n.tc("Success")
msg = i18n.tc("success_deleting_resource")
break break
case StandardToasts.SUCCESS_MOVE: case StandardToasts.SUCCESS_MOVE:
makeToast(i18n.tc("Success"), i18n.tc("success_moving_resource") + (err_details ? "\n" + err_details : ""), "success") variant = 'success'
title = i18n.tc("Success")
msg = i18n.tc("success_moving_resource")
break break
case StandardToasts.SUCCESS_MERGE: case StandardToasts.SUCCESS_MERGE:
makeToast(i18n.tc("Success"), i18n.tc("success_merging_resource") + (err_details ? "\n" + err_details : ""), "success") variant = 'success'
title = i18n.tc("Success")
msg = i18n.tc("success_merging_resource")
break break
case StandardToasts.FAIL_CREATE: case StandardToasts.FAIL_CREATE:
makeToast(i18n.tc("Failure"), i18n.tc("err_creating_resource") + (err_details ? "\n" + err_details : ""), "danger") variant = 'danger'
title = i18n.tc("Failure")
msg = i18n.tc("err_creating_resource")
break break
case StandardToasts.FAIL_FETCH: case StandardToasts.FAIL_FETCH:
makeToast(i18n.tc("Failure"), i18n.tc("err_fetching_resource") + (err_details ? "\n" + err_details : ""), "danger") variant = 'danger'
title = i18n.tc("Failure")
msg = i18n.tc("err_fetching_resource")
break break
case StandardToasts.FAIL_UPDATE: case StandardToasts.FAIL_UPDATE:
makeToast(i18n.tc("Failure"), i18n.tc("err_updating_resource") + (err_details ? "\n" + err_details : ""), "danger") variant = 'danger'
title = i18n.tc("Failure")
msg = i18n.tc("err_updating_resource")
break break
case StandardToasts.FAIL_DELETE: case StandardToasts.FAIL_DELETE:
makeToast(i18n.tc("Failure"), i18n.tc("err_deleting_resource") + (err_details ? "\n" + err_details : ""), "danger") variant = 'danger'
title = i18n.tc("Failure")
msg = i18n.tc("err_deleting_resource")
break break
case StandardToasts.FAIL_DELETE_PROTECTED: case StandardToasts.FAIL_DELETE_PROTECTED:
makeToast(i18n.tc("Protected"), i18n.tc("err_deleting_protected_resource"), "danger") variant = 'danger'
title = i18n.tc("Failure")
msg = i18n.tc("err_deleting_protected_resource")
break break
case StandardToasts.FAIL_MOVE: case StandardToasts.FAIL_MOVE:
makeToast(i18n.tc("Failure"), i18n.tc("err_moving_resource") + (err_details ? "\n" + err_details : ""), "danger") variant = 'danger'
title = i18n.tc("Failure")
msg = i18n.tc("err_moving_resource")
break break
case StandardToasts.FAIL_MERGE: case StandardToasts.FAIL_MERGE:
makeToast(i18n.tc("Failure"), i18n.tc("err_merging_resource") + (err_details ? "\n" + err_details : ""), "danger") variant = 'danger'
title = i18n.tc("Failure")
msg = i18n.tc("err_merging_resource")
break break
} }
let DEBUG = localStorage.getItem("DEBUG") === "True" || false
if (err !== undefined) {
if (DEBUG && err.response.headers['content-type'] === 'application/json' && err.response.status < 500) {
console.log('ERROR ', JSON.stringify(err.response.data))
msg = context.$createElement('div', {}, [
context.$createElement('span', {}, [msg]),
context.$createElement('br', {}, []),
context.$createElement('code', {'class': 'mt-2'}, [JSON.stringify(err.response.data)])
])
} }
} }
let toaster = new BToast()
toaster.$bvToast.toast(msg, {
title: title,
variant: variant,
toaster: "b-toaster-bottom-right",
solid: true,
})
}
}
/*
* Utility function to get random food icon from fontawesome
* */
export const RandomIconMixin = {
name: "RandomIconMixin",
methods: {
getRandomFoodIcon: function () {
return getRandomFoodIcon()
},
},
}
export function getRandomFoodIcon() {
let icons = [
'fas fa-hamburger',
'fas fa-utensils',
'fas fa-apple-alt',
'fas fa-bacon',
'fas fa-bread-slice',
'fas fa-candy-cane',
'fas fa-carrot',
'fas fa-cheese',
'fas fa-cookie',
'fas fa-drumstick-bite',
'fas fa-egg',
'fas fa-fish',
'fas fa-hotdog',
'fas fa-ice-cream',
'fas fa-lemon',
'fas fa-pepper-hot',
'fas fa-pizza-slice',
'fas fa-cookie-bite'
]
return icons[Math.floor(Math.random() * icons.length)];
}
/* /*
* Utility functions to use djangos gettext * Utility functions to use djangos gettext
* */ * */