From fcfef255c1a468f4abe6b5371c3ea47e1950e170 Mon Sep 17 00:00:00 2001 From: Kaibu Date: Sat, 23 Apr 2022 20:44:46 +0200 Subject: [PATCH] shopping list supermarket rework --- vue/src/apps/MealPlanView/MealPlanView.vue | 2 +- .../ShoppingListView/ShoppingListView.vue | 638 ++++++++++-------- vue/src/locales/en.json | 6 +- 3 files changed, 368 insertions(+), 278 deletions(-) diff --git a/vue/src/apps/MealPlanView/MealPlanView.vue b/vue/src/apps/MealPlanView/MealPlanView.vue index a7131729f..a2113a537 100644 --- a/vue/src/apps/MealPlanView/MealPlanView.vue +++ b/vue/src/apps/MealPlanView/MealPlanView.vue @@ -93,7 +93,7 @@
- +
diff --git a/vue/src/apps/ShoppingListView/ShoppingListView.vue b/vue/src/apps/ShoppingListView/ShoppingListView.vue index bdb6951d1..d418a3ed8 100644 --- a/vue/src/apps/ShoppingListView/ShoppingListView.vue +++ b/vue/src/apps/ShoppingListView/ShoppingListView.vue @@ -33,7 +33,7 @@
@@ -260,236 +260,179 @@
-
+
-
- -
- - - - - - - - {{ $t("Create") }} - - - + + +
{{ $t("Supermarkets") }}
+ + + + + +
+ {{ supermarket.name }} + +
+
+
+
+ +
+ + +
+
+ + +
+ + +
- - - - -
-
+ + + + +
{{ $t("Shopping_Categories") }} - + {{ editingSupermarket[0].name }}
+
{{ $t("Shopping_Categories") }}
+
+ + + + +
- {{ s.name }} - - - - - - + {{ category.name }} + +
+
+
+
+ +
+ + +
+
+ + +
+ + +
+
+
+ +
+
+ + + +
+
+ +
+
+
+ + #{{ index + 1 }} + + {{ category.name }} +
- - -
- - -
-
- - - - - - - {{ $t("Create") }} - - - - - - - {{ $t("CategoryInstruction") }} - - - - - +
{{ $t("Available") }}
+ + - -
-
- -
-
-
- {{ categoryName(c) }} - - - -
-
+ :key="category.id"> + +
+
+
- - - - -
- - - - - -
-
- -
-
-
- {{ categoryName(c) }} - - - -
-
+
+
+ {{ category.name }} +
- - - +
+
+
-
-
-
+
+
+
-
+
@@ -811,6 +754,7 @@ import Vue from "vue" import {BootstrapVue} from "bootstrap-vue" import "bootstrap-vue/dist/bootstrap-vue.css" import VueCookies from "vue-cookies" +import draggable from "vuedraggable" import ContextMenu from "@/components/ContextMenu/ContextMenu" import ContextMenuItem from "@/components/ContextMenu/ContextMenuItem" @@ -819,10 +763,8 @@ import DownloadPDF from "@/components/Buttons/DownloadPDF" import DownloadCSV from "@/components/Buttons/DownloadCSV" import CopyToClipboard from "@/components/Buttons/CopyToClipboard" import GenericMultiselect from "@/components/GenericMultiselect" -import GenericPill from "@/components/GenericPill" import LookupInput from "@/components/Modals/LookupInput" import ShoppingModal from "@/components/Modals/ShoppingModal" -import draggable from "vuedraggable" import {ApiMixin, getUserPreference, StandardToasts, makeToast} from "@/utils/utils" import {ApiApiFactory} from "@/utils/openapi/api" @@ -839,13 +781,12 @@ export default { ContextMenuItem, ShoppingLineItem, GenericMultiselect, - GenericPill, - draggable, LookupInput, DownloadPDF, DownloadCSV, CopyToClipboard, ShoppingModal, + draggable }, data() { @@ -862,6 +803,8 @@ export default { shopcat: null, delay: 0, clear: Math.random(), + generic_action: null, + generic_model: null, ui: { entry_mode_simple: true, selected_supermarket: undefined, @@ -879,6 +822,8 @@ export default { shopping_add_onhand: true, left_handed: false, }, + editing_supermarket_categories: [], + editing_supermarket: null, new_supermarket: {entrymode: false, value: undefined, editmode: undefined}, new_category: {entrymode: false, value: undefined}, autosync_id: undefined, @@ -979,6 +924,18 @@ export default { defaultDelay() { return Number(getUserPreference("default_delay")) || 2 }, + editingSupermarket() { + return this.supermarkets.filter((el) => { + return el.editing + }) + }, + unusedSupermarketCategories() { + if (this.editingSupermarket.length > 0) { + return this.supermarket_categories.filter(a => !this.editing_supermarket_categories.map(b => b.id).includes(a.id)) + } else { + return [] + } + }, formUnit() { let unit = this.Models.SHOPPING_LIST.create.form.unit unit.value = this.new_item.unit @@ -999,10 +956,10 @@ export default { // hiding recipes associated with shopping list items that are complete return [...new Map(this.items.filter((x) => x.list_recipe && !x.checked).map((item) => [item["list_recipe"], item])).values()] }, - supermarketCategory() { + supermarket_categories() { return this.new_supermarket.editmode ? this.new_supermarket.value.category_to_supermarket : this.shopping_categories }, - notSupermarketCategory() { + notsupermarket_categories() { let supercats = this.new_supermarket.value.category_to_supermarket .map((x) => x.category) .flat() @@ -1146,32 +1103,6 @@ export default { StandardToasts.makeStandardToast(this, StandardToasts.FAIL_CREATE, err) }) }, - deleteSupermarket: function (s) { - let api = new ApiApiFactory() - api.destroySupermarket(s.id) - .then(() => { - this.getSupermarkets() - StandardToasts.makeStandardToast(this, StandardToasts.SUCCESS_DELETE) - }) - .catch((err) => { - StandardToasts.makeStandardToast(this, StandardToasts.FAIL_DELETE, err) - }) - }, - deleteCategory: function (c) { - // could be category relation or a catory - let c_id = c?.category?.id ?? c.id - let api = new ApiApiFactory() - api.destroySupermarketCategory(c_id) - .then(() => { - this.getSupermarkets() - this.getShoppingCategories() - this.new_supermarket.value.category_to_supermarket = this.new_supermarket.value.category_to_supermarket.filter((x) => x.category.id != c_id) - StandardToasts.makeStandardToast(this, StandardToasts.SUCCESS_DELETE) - }) - .catch((err) => { - StandardToasts.makeStandardToast(this, StandardToasts.FAIL_DELETE, err) - }) - }, resetFilters: function () { this.ui.selected_supermarket = undefined this.supermarket_categories_only = this.settings.filter_to_supermarket @@ -1237,22 +1168,15 @@ export default { StandardToasts.makeStandardToast(this, StandardToasts.SUCCESS_DELETE) }) }, - editSupermarket(s) { - if (!s.editmode) { - this.new_supermarket = {entrymode: false, value: undefined, editmode: undefined} - this.supermarkets.map((x) => (x.editmode = false)) - } else { - this.new_supermarket.value = s - this.new_supermarket.editmode = true - this.supermarkets.filter((x) => x.id !== s.id).map((x) => (x.editmode = false)) - } - }, foodName: function (value) { return value?.food?.name ?? value?.[0]?.food?.name ?? "" }, getShoppingCategories: function () { let api = new ApiApiFactory() api.listSupermarketCategorys().then((result) => { + result.data.forEach((category) => { + category.editing = false + }) this.shopping_categories = result.data }) }, @@ -1290,6 +1214,9 @@ export default { getSupermarkets: function () { let api = new ApiApiFactory() api.listSupermarkets().then((result) => { + result.data.forEach((supermarket) => { + supermarket.editing = false + }) this.supermarkets = result.data }) }, @@ -1457,22 +1384,36 @@ export default { this.getShoppingList() }) }, - addCategory: function () { - let api = new ApiApiFactory() - api.createSupermarketCategory({name: this.new_category.value}) - .then((result) => { - StandardToasts.makeStandardToast(this, StandardToasts.SUCCESS_CREATE) - this.shopping_categories.push(result.data) - this.new_category.value = undefined - }) - .catch((err) => { - console.log(err, Object.keys(err)) - StandardToasts.makeStandardToast(this, StandardToasts.FAIL_CREATE) - }) + deleteSupermarket(index) { + this.$bvModal.msgBoxConfirm(this.$t('Are_You_Sure'), { + title: this.$t('Confirm'), + size: 'md', + buttonSize: 'sm', + okVariant: 'success', + headerClass: 'p-3 border-bottom-0', + footerClass: 'p-3 border-top-0 justify-content-center', + centered: true, + cancelTitle: this.$t('Cancel'), + okTitle: this.$t('Delete') + }).then(value => { + if (value) { + let apiClient = new ApiApiFactory() + apiClient.destroySupermarket(this.supermarkets[index].id) + .then((e) => { + this.getShoppingList() + this.getSupermarkets() + this.getShoppingCategories() + StandardToasts.makeStandardToast(this, StandardToasts.SUCCESS_DELETE) + }) + .catch((err) => { + StandardToasts.makeStandardToast(this, StandardToasts.FAIL_DELETE, err) + }) + } + }) }, addSupermarket: function () { let api = new ApiApiFactory() - api.createSupermarket({name: this.new_supermarket.value}) + api.createSupermarket({name: this.$t('Supermarket') + Math.floor(1000 + Math.random() * 9000)}) .then((result) => { StandardToasts.makeStandardToast(this, StandardToasts.SUCCESS_CREATE) this.supermarkets.push(result.data) @@ -1483,7 +1424,152 @@ export default { StandardToasts.makeStandardToast(this, StandardToasts.FAIL_CREATE) }) }, - saveSupermarketCategoryOrder(e) { + editOrSaveSupermarket(index) { + let supermarket = this.supermarkets[index] + + if (supermarket.editing) { + this.$set(this.supermarkets[index], "editing", false) + this.editing_supermarket_categories = [] + + let apiClient = new ApiApiFactory() + + apiClient + .updateSupermarket(this.supermarkets[index].id, this.supermarkets[index]) + .then((e) => { + this.periodChangedCallback(this.current_period) + StandardToasts.makeStandardToast(this, StandardToasts.SUCCESS_UPDATE) + }) + .catch((err) => { + StandardToasts.makeStandardToast(this, StandardToasts.FAIL_UPDATE, err) + }) + } else { + this.supermarkets.forEach((market, i) => { + if (i !== index) { + this.$set(this.supermarkets[i], "editing", false) + } + }) + + this.$set(this.supermarkets[index], "editing", true) + + this.editing_supermarket_categories = [] + this.supermarkets[index].category_to_supermarket.forEach((cur, i) => { + this.editing_supermarket_categories.push({ + name: cur.category.name, + description: cur.category.description, + id: cur.category.id, + relation_id: cur.id, + order: cur.order, + supermarket: cur.supermarket, + category: cur.category + }) + }) + } + }, + deleteSupermarketCategory(index) { + this.$bvModal.msgBoxConfirm(this.$t('Warning_Delete_Supermarket_Category'), { + title: this.$t('Confirm'), + size: 'md', + buttonSize: 'sm', + okVariant: 'success', + headerClass: 'p-3 border-bottom-0', + footerClass: 'p-3 border-top-0 justify-content-center', + centered: true, + cancelTitle: this.$t('Cancel'), + okTitle: this.$t('Delete') + }).then(value => { + if (value) { + let apiClient = new ApiApiFactory() + apiClient.destroySupermarketCategory(this.supermarket_categories[index].id) + .then((e) => { + this.getShoppingList() + this.getSupermarkets() + this.getShoppingCategories() + StandardToasts.makeStandardToast(this, StandardToasts.SUCCESS_DELETE) + }) + .catch((err) => { + StandardToasts.makeStandardToast(this, StandardToasts.FAIL_DELETE, err) + }) + } + }) + }, + addSupermarketCategory() { + let apiClient = new ApiApiFactory() + + apiClient.createSupermarketCategory({name: this.$t("Shopping_Category") + Math.floor(1000 + Math.random() * 9000)}) + .then((result) => { + StandardToasts.makeStandardToast(this, StandardToasts.SUCCESS_CREATE) + this.shopping_categories.push(result.data) + }) + .catch((err) => { + StandardToasts.makeStandardToast(this, StandardToasts.FAIL_CREATE, err) + }) + }, + editOrSaveSupermarketCategory(index) { + let category = this.supermarket_categories[index] + + this.supermarkets.forEach((supermarket) => { + supermarket.category_to_supermarket.forEach((cat) => { + if (cat.category.id === this.supermarket_categories[index].id) { + cat.category = this.supermarket_categories[index] + } + }) + }) + + let apiClient = new ApiApiFactory() + + apiClient + .updateSupermarketCategory(this.supermarket_categories[index].id, this.supermarket_categories[index]) + .then((e) => { + this.periodChangedCallback(this.current_period) + StandardToasts.makeStandardToast(this, StandardToasts.SUCCESS_UPDATE) + }) + .catch((err) => { + StandardToasts.makeStandardToast(this, StandardToasts.FAIL_UPDATE, err) + }) + + if (category.editing) { + this.$set(this.supermarket_categories[index], "editing", false) + } else { + this.supermarket_categories.forEach((market, i) => { + if (i !== index) { + this.$set(this.supermarket_categories[i], "editing", false) + } + }) + + this.$set(this.supermarket_categories[index], "editing", true) + } + }, + removeSupermarketCategoryRelation(index) { + this.editing_supermarket_categories[index].relation_id + + let apiClient = new ApiApiFactory() + + apiClient.destroySupermarketCategoryRelation(this.editing_supermarket_categories[index].relation_id) + .then((e) => { + this.editing_supermarket_categories.splice(index, 1); + }) + .catch((err) => { + StandardToasts.makeStandardToast(this, StandardToasts.FAIL_DELETE, err) + }) + }, + sortSupermarketCategories() { + this.editing_supermarket_categories.forEach((element, index) => { + element.order = index + }) + + this.editing_supermarket_categories.forEach((element, index) => { + let apiClient = new ApiApiFactory() + + apiClient.updateSupermarketCategoryRelation(element.relation_id, element) + .then((e) => { + + }) + .catch((err) => { + StandardToasts.makeStandardToast(this, StandardToasts.FAIL_UPDATE, err) + }) + }) + }, + sortSupermarketCategoriesR(e) { // TODO: all of this complexity should be moved to the backend let apiClient = new ApiApiFactory() let supermarket = this.new_supermarket.value @@ -1492,7 +1578,7 @@ export default { var promises = [] supermarket.category_to_supermarket.forEach((x, i) => { x.order = i - promises.push(apiClient.partialUpdateSupermarketCategoryRelation(x.id, {order: i})) + promises.push(apiClient.partialUpdatesupermarket_categoriesRelation(x.id, {order: i})) }) return Promise.all(promises).then(() => { return supermarket @@ -1504,7 +1590,7 @@ export default { let idx = this.supermarkets.indexOf((x) => x.id === supermarket.id) Vue.set(this.supermarkets, idx, supermarket) apiClient - .destroySupermarketCategoryRelation(e.removed.element.id) + .destroysupermarket_categoriesRelation(e.removed.element.id) .then((result) => { StandardToasts.makeStandardToast(this, StandardToasts.SUCCESS_UPDATE) }) @@ -1520,7 +1606,7 @@ export default { let category = e.added.element.category apiClient - .createSupermarketCategoryRelation({ + .createsupermarket_categoriesRelation({ supermarket: supermarket.id, category: category, order: e.added.element.newIndex, diff --git a/vue/src/locales/en.json b/vue/src/locales/en.json index 98eaf3a9e..064be63ad 100644 --- a/vue/src/locales/en.json +++ b/vue/src/locales/en.json @@ -403,5 +403,9 @@ "Import_Result_Info": "{imported} of {total} recipes were imported", "Recipes_In_Import": "Recipes in your import file", "Toggle": "Toggle", - "Import_Error": "An Error occurred during your import. Please expand the Details at the bottom of the page to view it." + "Import_Error": "An Error occurred during your import. Please expand the Details at the bottom of the page to view it.", + "Warning_Delete_Supermarket_Category": "Deleting a supermarket category will also delete all relations to foods. Are you sure?", + "New_Supermarket": "Create new supermarket", + "New_Supermarket_Category": "Create new supermarket category", + "Are_You_Sure": "Are you sure?" }