From 12f480eb75bcc6a45f02dc917856bec472fd95e7 Mon Sep 17 00:00:00 2001 From: Hayden <64056131+hay-kot@users.noreply.github.com> Date: Fri, 3 Jun 2022 20:12:32 -0800 Subject: [PATCH] refactor: unify recipe-organizer components (#1340) * use generic context menu * implement organizer stores * add basic organizer types * refactor selectors to apply for all organizers * remove legacy organizer composables --- .../Domain/Group/GroupMealPlanRuleForm.vue | 13 +- .../Domain/Recipe/RecipeCategoryTagDialog.vue | 111 ---------- .../Recipe/RecipeCategoryTagSelector.vue | 164 -------------- .../RecipeCategoryTagToolContextMenu.vue | 207 ------------------ .../Recipe/RecipeCategoryTagToolPage.vue | 123 ----------- .../Domain/Recipe/RecipeOrganizerDialog.vue | 152 +++++++++++++ .../Domain/Recipe/RecipeOrganizerPage.vue | 139 ++++++++++++ .../Domain/Recipe/RecipeOrganizerSelector.vue | 88 ++++++-- frontend/components/global/ContextMenu.vue | 56 +++++ .../partials/use-actions-factory.ts | 13 +- frontend/composables/recipes/index.ts | 1 - .../recipes/use-tags-categories.ts | 65 ------ frontend/composables/store/index.ts | 3 + .../composables/store/use-category-store.ts | 47 ++++ frontend/composables/store/use-tag-store.ts | 47 ++++ frontend/composables/store/use-tool-store.ts | 49 +++++ frontend/composables/use-context-presents.ts | 30 +++ frontend/pages/group/cookbooks.vue | 22 +- frontend/pages/group/data/recipes.vue | 8 +- frontend/pages/recipe/_slug/index.vue | 45 ++-- frontend/pages/recipe/create/bulk.vue | 17 +- frontend/pages/recipes/categories/index.vue | 46 ++-- frontend/pages/recipes/tags/index.vue | 46 ++-- frontend/pages/recipes/tools/index.vue | 48 ++-- frontend/pages/search.vue | 29 ++- frontend/types/recipe/organizers.ts | 7 + 26 files changed, 719 insertions(+), 857 deletions(-) delete mode 100644 frontend/components/Domain/Recipe/RecipeCategoryTagDialog.vue delete mode 100644 frontend/components/Domain/Recipe/RecipeCategoryTagSelector.vue delete mode 100644 frontend/components/Domain/Recipe/RecipeCategoryTagToolContextMenu.vue delete mode 100644 frontend/components/Domain/Recipe/RecipeCategoryTagToolPage.vue create mode 100644 frontend/components/Domain/Recipe/RecipeOrganizerDialog.vue create mode 100644 frontend/components/Domain/Recipe/RecipeOrganizerPage.vue create mode 100644 frontend/components/global/ContextMenu.vue delete mode 100644 frontend/composables/recipes/use-tags-categories.ts create mode 100644 frontend/composables/store/use-category-store.ts create mode 100644 frontend/composables/store/use-tag-store.ts create mode 100644 frontend/composables/store/use-tool-store.ts create mode 100644 frontend/composables/use-context-presents.ts create mode 100644 frontend/types/recipe/organizers.ts diff --git a/frontend/components/Domain/Group/GroupMealPlanRuleForm.vue b/frontend/components/Domain/Group/GroupMealPlanRuleForm.vue index 04f3efba034d..81d4b570b12c 100644 --- a/frontend/components/Domain/Group/GroupMealPlanRuleForm.vue +++ b/frontend/components/Domain/Group/GroupMealPlanRuleForm.vue @@ -5,8 +5,8 @@ - - + + {{ inputDay === "unset" ? "This rule will apply to all days" : `This rule applies on ${inputDay}s` }} {{ inputEntryType === "unset" ? "for all meal types" : ` and for ${inputEntryType} meal types` }} @@ -15,7 +15,8 @@ - - diff --git a/frontend/components/Domain/Recipe/RecipeCategoryTagSelector.vue b/frontend/components/Domain/Recipe/RecipeCategoryTagSelector.vue deleted file mode 100644 index 43160f93a6e5..000000000000 --- a/frontend/components/Domain/Recipe/RecipeCategoryTagSelector.vue +++ /dev/null @@ -1,164 +0,0 @@ -//TODO: Prevent fetching Categories/Tags multiple time when selector is on page multiple times - - - - diff --git a/frontend/components/Domain/Recipe/RecipeCategoryTagToolContextMenu.vue b/frontend/components/Domain/Recipe/RecipeCategoryTagToolContextMenu.vue deleted file mode 100644 index 6016e3fd5187..000000000000 --- a/frontend/components/Domain/Recipe/RecipeCategoryTagToolContextMenu.vue +++ /dev/null @@ -1,207 +0,0 @@ - - - diff --git a/frontend/components/Domain/Recipe/RecipeCategoryTagToolPage.vue b/frontend/components/Domain/Recipe/RecipeCategoryTagToolPage.vue deleted file mode 100644 index 96dfa1505f37..000000000000 --- a/frontend/components/Domain/Recipe/RecipeCategoryTagToolPage.vue +++ /dev/null @@ -1,123 +0,0 @@ - - - diff --git a/frontend/components/Domain/Recipe/RecipeOrganizerDialog.vue b/frontend/components/Domain/Recipe/RecipeOrganizerDialog.vue new file mode 100644 index 000000000000..e0db9549798c --- /dev/null +++ b/frontend/components/Domain/Recipe/RecipeOrganizerDialog.vue @@ -0,0 +1,152 @@ + + + + + diff --git a/frontend/components/Domain/Recipe/RecipeOrganizerPage.vue b/frontend/components/Domain/Recipe/RecipeOrganizerPage.vue new file mode 100644 index 000000000000..a581d6701714 --- /dev/null +++ b/frontend/components/Domain/Recipe/RecipeOrganizerPage.vue @@ -0,0 +1,139 @@ + + + diff --git a/frontend/components/Domain/Recipe/RecipeOrganizerSelector.vue b/frontend/components/Domain/Recipe/RecipeOrganizerSelector.vue index 26a55c5a881e..9ac2cddcf93d 100644 --- a/frontend/components/Domain/Recipe/RecipeOrganizerSelector.vue +++ b/frontend/components/Domain/Recipe/RecipeOrganizerSelector.vue @@ -1,14 +1,14 @@ + diff --git a/frontend/composables/partials/use-actions-factory.ts b/frontend/composables/partials/use-actions-factory.ts index fbb8e2d6feb2..884f6217b26f 100644 --- a/frontend/composables/partials/use-actions-factory.ts +++ b/frontend/composables/partials/use-actions-factory.ts @@ -3,7 +3,7 @@ import { useAsyncKey } from "../use-utils"; import { BaseCRUDAPI } from "~/api/_base"; type BoundT = { - id: string | number; + id?: string | number; }; interface StoreActions { @@ -29,7 +29,12 @@ export function useStoreActions( loading.value = true; const allItems = useAsync(async () => { const { data } = await api.getAll(); - return data; + + if (allRef) { + allRef.value = data; + } + + return data ?? []; }, useAsyncKey()); loading.value = false; @@ -73,8 +78,8 @@ export function useStoreActions( async function deleteOne(id: string | number) { loading.value = true; - const { data } = await api.deleteOne(id); - if (data && allRef?.value) { + const { response } = await api.deleteOne(id); + if (response && allRef?.value) { refresh(); } loading.value = false; diff --git a/frontend/composables/recipes/index.ts b/frontend/composables/recipes/index.ts index 2f46abef0038..364ffe915575 100644 --- a/frontend/composables/recipes/index.ts +++ b/frontend/composables/recipes/index.ts @@ -1,7 +1,6 @@ export { useFraction } from "./use-fraction"; export { useRecipe } from "./use-recipe"; export { useRecipes, recentRecipes, allRecipes, useLazyRecipes, useSorter } from "./use-recipes"; -export { useTags, useCategories, allCategories, allTags } from "./use-tags-categories"; export { parseIngredientText } from "./use-recipe-ingredients"; export { useRecipeSearch } from "./use-recipe-search"; export { useTools } from "./use-recipe-tools"; diff --git a/frontend/composables/recipes/use-tags-categories.ts b/frontend/composables/recipes/use-tags-categories.ts deleted file mode 100644 index f022f8b6ba42..000000000000 --- a/frontend/composables/recipes/use-tags-categories.ts +++ /dev/null @@ -1,65 +0,0 @@ -import { Ref, ref, useAsync } from "@nuxtjs/composition-api"; -import { useUserApi } from "../api"; -import { useAsyncKey } from "../use-utils"; -import { CategoriesAPI } from "~/api/class-interfaces/organizer-categories"; -import { TagsAPI } from "~/api/class-interfaces/organizer-tags"; -import { RecipeTag, RecipeCategory } from "~/types/api-types/recipe"; - -export const allCategories = ref([]); -export const allTags = ref([]); - -function baseTagsCategories( - reference: Ref | Ref, - api: TagsAPI | CategoriesAPI -) { - function useAsyncGetAll() { - useAsync(async () => { - await refreshItems(); - }, useAsyncKey()); - } - - async function refreshItems() { - const { data } = await api.getAll(); - // @ts-ignore hotfix - reference.value = data; - } - - async function createOne(payload: { name: string }) { - const { data } = await api.createOne(payload); - if (data) { - refreshItems(); - } - } - - async function deleteOne(slug: string) { - const { data } = await api.deleteOne(slug); - if (data) { - refreshItems(); - } - } - - async function updateOne(slug: string, payload: { name: string }) { - // @ts-ignore // TODO: Fix Typescript Issue - Unsure how to fix this while also keeping mixins - const { data } = await api.updateOne(slug, payload); - if (data) { - refreshItems(); - } - } - - return { useAsyncGetAll, refreshItems, createOne, deleteOne, updateOne }; -} - -export const useTags = function () { - const api = useUserApi(); - return { - allTags, - ...baseTagsCategories(allTags, api.tags), - }; -}; -export const useCategories = function () { - const api = useUserApi(); - return { - allCategories, - ...baseTagsCategories(allCategories, api.categories), - }; -}; diff --git a/frontend/composables/store/index.ts b/frontend/composables/store/index.ts index 6a01f40213fc..e00aba57e1a1 100644 --- a/frontend/composables/store/index.ts +++ b/frontend/composables/store/index.ts @@ -1,3 +1,6 @@ export { useFoodStore, useFoodData } from "./use-food-store"; export { useUnitStore, useUnitData } from "./use-unit-store"; export { useLabelStore, useLabelData } from "./use-label-store"; +export { useToolStore, useToolData } from "./use-tool-store"; +export { useCategoryStore, useCategoryData } from "./use-category-store"; +export { useTagStore, useTagData } from "./use-tag-store"; diff --git a/frontend/composables/store/use-category-store.ts b/frontend/composables/store/use-category-store.ts new file mode 100644 index 000000000000..10206d0d7bfc --- /dev/null +++ b/frontend/composables/store/use-category-store.ts @@ -0,0 +1,47 @@ +import { reactive, ref, Ref } from "@nuxtjs/composition-api"; +import { useStoreActions } from "../partials/use-actions-factory"; +import { useUserApi } from "~/composables/api"; +import { RecipeCategory } from "~/types/api-types/admin"; + +const categoryStore: Ref = ref([]); + +export function useCategoryData() { + const data = reactive({ + id: "", + name: "", + slug: undefined, + }); + + function reset() { + data.id = ""; + data.name = ""; + data.slug = undefined; + } + + return { + data, + reset, + }; +} + +export function useCategoryStore() { + const api = useUserApi(); + const loading = ref(false); + + const actions = { + ...useStoreActions(api.categories, categoryStore, loading), + flushStore() { + categoryStore.value = []; + }, + }; + + if (!categoryStore.value || categoryStore.value?.length === 0) { + actions.getAll(); + } + + return { + items: categoryStore, + actions, + loading, + }; +} diff --git a/frontend/composables/store/use-tag-store.ts b/frontend/composables/store/use-tag-store.ts new file mode 100644 index 000000000000..401ef612ee6d --- /dev/null +++ b/frontend/composables/store/use-tag-store.ts @@ -0,0 +1,47 @@ +import { reactive, ref, Ref } from "@nuxtjs/composition-api"; +import { useStoreActions } from "../partials/use-actions-factory"; +import { useUserApi } from "~/composables/api"; +import { RecipeTag } from "~/types/api-types/admin"; + +const items: Ref = ref([]); + +export function useTagData() { + const data = reactive({ + id: "", + name: "", + slug: undefined, + }); + + function reset() { + data.id = ""; + data.name = ""; + data.slug = undefined; + } + + return { + data, + reset, + }; +} + +export function useTagStore() { + const api = useUserApi(); + const loading = ref(false); + + const actions = { + ...useStoreActions(api.tags, items, loading), + flushStore() { + items.value = []; + }, + }; + + if (!items.value || items.value?.length === 0) { + actions.getAll(); + } + + return { + items, + actions, + loading, + }; +} diff --git a/frontend/composables/store/use-tool-store.ts b/frontend/composables/store/use-tool-store.ts new file mode 100644 index 000000000000..1a5fcb5c47f4 --- /dev/null +++ b/frontend/composables/store/use-tool-store.ts @@ -0,0 +1,49 @@ +import { reactive, ref, Ref } from "@nuxtjs/composition-api"; +import { useStoreActions } from "../partials/use-actions-factory"; +import { useUserApi } from "~/composables/api"; +import { RecipeTool } from "~/types/api-types/recipe"; + +const toolStore: Ref = ref([]); + +export function useToolData() { + const data = reactive({ + id: "", + name: "", + slug: undefined, + onHand: false, + }); + + function reset() { + data.id = ""; + data.name = ""; + data.slug = undefined; + data.onHand = false; + } + + return { + data, + reset, + }; +} + +export function useToolStore() { + const api = useUserApi(); + const loading = ref(false); + + const actions = { + ...useStoreActions(api.tools, toolStore, loading), + flushStore() { + toolStore.value = []; + }, + }; + + if (!toolStore.value || toolStore.value?.length === 0) { + actions.getAll(); + } + + return { + items: toolStore, + actions, + loading, + }; +} diff --git a/frontend/composables/use-context-presents.ts b/frontend/composables/use-context-presents.ts new file mode 100644 index 000000000000..42b3b7390903 --- /dev/null +++ b/frontend/composables/use-context-presents.ts @@ -0,0 +1,30 @@ +import { useContext } from "@nuxtjs/composition-api"; + +export interface ContextMenuItem { + title: string; + icon: string; + event: string; + color?: string; +} + +export function useContextPresets(): { [key: string]: ContextMenuItem } { + const { $globals, i18n } = useContext(); + + return { + delete: { + title: i18n.tc("general.delete"), + icon: $globals.icons.delete, + event: "delete", + }, + edit: { + title: i18n.tc("general.edit"), + icon: $globals.icons.edit, + event: "edit", + }, + save: { + title: i18n.tc("general.save"), + icon: $globals.icons.save, + event: "save", + }, + }; +} diff --git a/frontend/pages/group/cookbooks.vue b/frontend/pages/group/cookbooks.vue index 64791860ddcc..f5ad3847a89a 100644 --- a/frontend/pages/group/cookbooks.vue +++ b/frontend/pages/group/cookbooks.vue @@ -36,14 +36,9 @@ - - - - + + +