diff --git a/.gitignore b/.gitignore
index 0f4444b6..da82f89e 100644
--- a/.gitignore
+++ b/.gitignore
@@ -145,4 +145,26 @@ node_modules/
scratch.py
dev/data/backups/dev_sample_data*.zip
!dev/data/backups/test*.zip
-dev/data/recipes/*
\ No newline at end of file
+dev/data/recipes/*
+dev/scripts/output/app_routes.py
+dev/scripts/output/javascriptAPI/apiRoutes.js
+dev/scripts/output/javascriptAPI/appEvents.js
+dev/scripts/output/javascriptAPI/authentication.js
+dev/scripts/output/javascriptAPI/backups.js
+dev/scripts/output/javascriptAPI/debug.js
+dev/scripts/output/javascriptAPI/groups.js
+dev/scripts/output/javascriptAPI/index.js
+dev/scripts/output/javascriptAPI/mealPlan.js
+dev/scripts/output/javascriptAPI/migration.js
+dev/scripts/output/javascriptAPI/queryAllRecipes.js
+dev/scripts/output/javascriptAPI/recipeCategories.js
+dev/scripts/output/javascriptAPI/recipeCRUD.js
+dev/scripts/output/javascriptAPI/recipeTags.js
+dev/scripts/output/javascriptAPI/settings.js
+dev/scripts/output/javascriptAPI/shoppingLists.js
+dev/scripts/output/javascriptAPI/siteMedia.js
+dev/scripts/output/javascriptAPI/themes.js
+dev/scripts/output/javascriptAPI/userAPITokens.js
+dev/scripts/output/javascriptAPI/users.js
+dev/scripts/output/javascriptAPI/userSignup.js
+dev/scripts/output/javascriptAPI/utils.js
diff --git a/dev/scripts/app_routes_gen copy.py b/dev/scripts/app_routes_gen copy.py
new file mode 100644
index 00000000..3e42f057
--- /dev/null
+++ b/dev/scripts/app_routes_gen copy.py
@@ -0,0 +1,144 @@
+import json
+import re
+from enum import Enum
+from itertools import groupby
+from pathlib import Path
+
+import slugify
+from fastapi import FastAPI
+from humps import camelize
+from jinja2 import Template
+from mealie.app import app
+from pydantic import BaseModel
+
+CWD = Path(__file__).parent
+OUT_DIR = CWD / "output"
+OUT_FILE = OUT_DIR / "app_routes.py"
+
+JS_DIR = OUT_DIR / "javascriptAPI"
+JS_OUT_FILE = JS_DIR / "apiRoutes.js"
+TEMPLATES_DIR = CWD / "templates"
+
+PYTEST_TEMPLATE = TEMPLATES_DIR / "pytest_routes.j2"
+JS_REQUESTS = TEMPLATES_DIR / "js_requests.j2"
+JS_ROUTES = TEMPLATES_DIR / "js_routes.j2"
+JS_INDEX = TEMPLATES_DIR / "js_index.j2"
+
+JS_DIR.mkdir(exist_ok=True, parents=True)
+
+
+class RouteObject:
+ def __init__(self, route_string) -> None:
+ self.prefix = "/" + route_string.split("/")[1]
+ self.route = route_string.replace(self.prefix, "")
+ self.js_route = self.route.replace("{", "${")
+ self.parts = route_string.split("/")[1:]
+ self.var = re.findall(r"\{(.*?)\}", route_string)
+ self.is_function = "{" in self.route
+ self.router_slug = slugify.slugify("_".join(self.parts[1:]), separator="_")
+ self.router_camel = camelize(self.router_slug)
+
+ def __repr__(self) -> str:
+ return f"""Route: {self.route}
+Parts: {self.parts}
+Function: {self.is_function}
+Var: {self.var}
+Slug: {self.router_slug}
+"""
+
+
+class RequestType(str, Enum):
+ get = "get"
+ put = "put"
+ post = "post"
+ patch = "patch"
+ delete = "delete"
+
+
+class HTTPRequest(BaseModel):
+ request_type: RequestType
+ description: str = ""
+ summary: str
+ tags: list[str]
+
+ @property
+ def summary_camel(self):
+ return camelize(self.summary)
+
+ @property
+ def js_docs(self):
+ return self.description.replace("\n", " \n * ")
+
+
+class PathObject(BaseModel):
+ route_object: RouteObject
+ http_verbs: list[HTTPRequest]
+
+ class Config:
+ arbitrary_types_allowed = True
+
+
+def get_path_objects(app: FastAPI):
+ paths = []
+
+ with open("scratch.json", "w") as f:
+ f.write(json.dumps(app.openapi()))
+ for key, value in app.openapi().items():
+ if key == "paths":
+ for key, value in value.items():
+
+ paths.append(
+ PathObject(
+ route_object=RouteObject(key),
+ http_verbs=[HTTPRequest(request_type=k, **v) for k, v in value.items()],
+ )
+ )
+
+ return paths
+
+
+def read_template(file: Path):
+ with open(file, "r") as f:
+ return f.read()
+
+
+def generate_template(app):
+ paths = get_path_objects(app)
+
+ static_paths = [x.route_object for x in paths if not x.route_object.is_function]
+ function_paths = [x.route_object for x in paths if x.route_object.is_function]
+
+ static_paths.sort(key=lambda x: x.router_slug)
+ function_paths.sort(key=lambda x: x.router_slug)
+
+ template = Template(read_template(PYTEST_TEMPLATE))
+ content = template.render(paths={"prefix": "/api", "static_paths": static_paths, "function_paths": function_paths})
+ with open(OUT_FILE, "w") as f:
+ f.write(content)
+
+ template = Template(read_template(JS_ROUTES))
+ content = template.render(
+ paths={"prefix": "/api", "static_paths": static_paths, "function_paths": function_paths, "all_paths": paths}
+ )
+ with open(JS_OUT_FILE, "w") as f:
+ f.write(content)
+
+ all_tags = []
+ for k, g in groupby(paths, lambda x: x.http_verbs[0].tags[0]):
+ template = Template(read_template(JS_REQUESTS))
+ content = template.render(paths={"all_paths": list(g), "export_name": camelize(k)})
+
+ all_tags.append(camelize(k))
+
+ with open(JS_DIR.joinpath(camelize(k) + ".js"), "w") as f:
+ f.write(content)
+
+ template = Template(read_template(JS_INDEX))
+ content = template.render(files={"files": all_tags})
+
+ with open(JS_DIR.joinpath("index.js"), "w") as f:
+ f.write(content)
+
+
+if __name__ == "__main__":
+ generate_template(app)
diff --git a/dev/scripts/app_routes_gen.py b/dev/scripts/app_routes_gen.py
deleted file mode 100644
index aa52c1da..00000000
--- a/dev/scripts/app_routes_gen.py
+++ /dev/null
@@ -1,81 +0,0 @@
-import json
-import re
-from pathlib import Path
-
-import slugify
-from jinja2 import Template
-from mealie.app import app
-
-CWD = Path(__file__).parent
-OUT_FILE = CWD.joinpath("output", "app_routes.py")
-
-code_template = """
-class AppRoutes:
- def __init__(self) -> None:
- self.prefix = '{{paths.prefix}}'
-{% for path in paths.static_paths %}
- self.{{ path.router_slug }} = "{{path.prefix}}{{ path.route }}"{% endfor %}
-{% for path in paths.function_paths %}
- def {{path.router_slug}}(self, {{path.var|join(", ")}}):
- return f"{self.prefix}{{ path.route }}"
-{% endfor %}
-"""
-
-
-def get_variables(path):
- path = path.replace("/", " ")
- print(path)
- var = re.findall(r" \{.*\}", path)
- print(var)
- if var:
- return [v.replace("{", "").replace("}", "") for v in var]
- else:
- return None
-
-
-class RouteObject:
- def __init__(self, route_string) -> None:
- self.prefix = "/" + route_string.split("/")[1]
- self.route = route_string.replace(self.prefix, "")
- self.parts = route_string.split("/")[1:]
- self.var = re.findall(r"\{(.*?)\}", route_string)
- self.is_function = "{" in self.route
- self.router_slug = slugify.slugify("_".join(self.parts[1:]), separator="_")
-
- def __repr__(self) -> str:
- return f"""Route: {self.route}
-Parts: {self.parts}
-Function: {self.is_function}
-Var: {self.var}
-Slug: {self.router_slug}
-"""
-
-
-def get_paths(app):
- paths = []
- print(json.dumps(app.openapi()))
- for key, value in app.openapi().items():
- if key == "paths":
- for key, value in value.items():
- paths.append(key)
-
- return paths
-
-
-def generate_template(app):
- paths = get_paths(app)
- new_paths = [RouteObject(path) for path in paths]
-
- static_paths = [p for p in new_paths if not p.is_function]
- function_paths = [p for p in new_paths if p.is_function]
-
- template = Template(code_template)
-
- content = template.render(paths={"prefix": "/api", "static_paths": static_paths, "function_paths": function_paths})
-
- with open(OUT_FILE, "w") as f:
- f.write(content)
-
-
-if __name__ == "__main__":
- generate_template(app)
diff --git a/dev/scripts/output/app_routes.py b/dev/scripts/output/app_routes.py
index 40e4b2c1..f46ec710 100644
--- a/dev/scripts/output/app_routes.py
+++ b/dev/scripts/output/app_routes.py
@@ -1,105 +1,63 @@
+# This Content is Auto Generated for Pytest
+
+
class AppRoutes:
def __init__(self) -> None:
- self.prefix = "/api"
+ self.prefix = '/api'
+ self.about_events = "/api/about/events"
+ self.about_events_notifications = "/api/about/events/notifications"
+ self.about_events_notifications_test = "/api/about/events/notifications/test"
+ self.auth_refresh = "/api/auth/refresh"
self.auth_token = "/api/auth/token"
self.auth_token_long = "/api/auth/token/long"
- self.auth_refresh = "/api/auth/refresh"
- self.users_sign_ups = "/api/users/sign-ups"
- self.users = "/api/users"
- self.users_self = "/api/users/self"
- self.users_api_tokens = "/api/users-tokens"
- self.groups = "/api/groups"
- self.groups_self = "/api/groups/self"
- self.recipes_summary = "/api/recipes/summary"
- self.recipes_summary_untagged = "/api/recipes/summary/untagged"
- self.recipes_summary_uncategorized = "/api/recipes/summary/uncategorized"
- self.recipes_category = "/api/recipes/category"
- self.recipes_tag = "/api/recipes/tag"
- self.recipes_create = "/api/recipes/create"
- self.recipes_create_url = "/api/recipes/create-url"
+ self.backups_available = "/api/backups/available"
+ self.backups_export_database = "/api/backups/export/database"
+ self.backups_upload = "/api/backups/upload"
self.categories = "/api/categories"
self.categories_empty = "/api/categories/empty"
- self.tags = "/api/tags"
- self.tags_empty = "/api/tags/empty"
- self.about_events = "/api/about/events"
+ self.debug = "/api/debug"
+ self.debug_last_recipe_json = "/api/debug/last-recipe-json"
+ self.debug_log = "/api/debug/log"
+ self.debug_statistics = "/api/debug/statistics"
+ self.debug_version = "/api/debug/version"
+ self.groups = "/api/groups"
+ self.groups_self = "/api/groups/self"
self.meal_plans_all = "/api/meal-plans/all"
self.meal_plans_create = "/api/meal-plans/create"
self.meal_plans_this_week = "/api/meal-plans/this-week"
self.meal_plans_today = "/api/meal-plans/today"
self.meal_plans_today_image = "/api/meal-plans/today/image"
- self.site_settings_custom_pages = "/api/site-settings/custom-pages"
+ self.migrations = "/api/migrations"
+ self.recipes_category = "/api/recipes/category"
+ self.recipes_create = "/api/recipes/create"
+ self.recipes_create_url = "/api/recipes/create-url"
+ self.recipes_summary = "/api/recipes/summary"
+ self.recipes_summary_uncategorized = "/api/recipes/summary/uncategorized"
+ self.recipes_summary_untagged = "/api/recipes/summary/untagged"
+ self.recipes_tag = "/api/recipes/tag"
+ self.shopping_lists = "/api/shopping-lists"
self.site_settings = "/api/site-settings"
+ self.site_settings_custom_pages = "/api/site-settings/custom-pages"
self.site_settings_webhooks_test = "/api/site-settings/webhooks/test"
+ self.tags = "/api/tags"
+ self.tags_empty = "/api/tags/empty"
self.themes = "/api/themes"
self.themes_create = "/api/themes/create"
- self.backups_available = "/api/backups/available"
- self.backups_export_database = "/api/backups/export/database"
- self.backups_upload = "/api/backups/upload"
- self.migrations = "/api/migrations"
- self.debug = "/api/debug"
- self.debug_statistics = "/api/debug/statistics"
- self.debug_version = "/api/debug/version"
- self.debug_last_recipe_json = "/api/debug/last-recipe-json"
- self.debug_log = "/api/debug/log"
+ self.users = "/api/users"
+ self.users_api_tokens = "/api/users-tokens"
+ self.users_self = "/api/users/self"
+ self.users_sign_ups = "/api/users/sign-ups"
self.utils_download = "/api/utils/download"
- def users_sign_ups_token(self, token):
- return f"{self.prefix}/users/sign-ups/{token}"
-
- def users_id(self, id):
- return f"{self.prefix}/users/{id}"
-
- def users_id_reset_password(self, id):
- return f"{self.prefix}/users/{id}/reset-password"
-
- def users_id_image(self, id):
- return f"{self.prefix}/users/{id}/image"
-
- def users_id_password(self, id):
- return f"{self.prefix}/users/{id}/password"
-
- def users_api_tokens_token_id(self, token_id):
- return f"{self.prefix}/users-tokens/{token_id}"
-
- def groups_id(self, id):
- return f"{self.prefix}/groups/{id}"
-
- def recipes_recipe_slug(self, recipe_slug):
- return f"{self.prefix}/recipes/{recipe_slug}"
-
- def recipes_recipe_slug_image(self, recipe_slug):
- return f"{self.prefix}/recipes/{recipe_slug}/image"
-
- def recipes_recipe_slug_assets(self, recipe_slug):
- return f"{self.prefix}/recipes/{recipe_slug}/assets"
-
- def categories_category(self, category):
- return f"{self.prefix}/categories/{category}"
-
- def tags_tag(self, tag):
- return f"{self.prefix}/tags/{tag}"
-
- def media_recipes_recipe_slug_images_file_name(self, recipe_slug, file_name):
- return f"{self.prefix}/media/recipes/{recipe_slug}/images/{file_name}"
-
- def media_recipes_recipe_slug_assets_file_name(self, recipe_slug, file_name):
- return f"{self.prefix}/media/recipes/{recipe_slug}/assets/{file_name}"
-
def about_events_id(self, id):
return f"{self.prefix}/about/events/{id}"
- def meal_plans_plan_id(self, plan_id):
- return f"{self.prefix}/meal-plans/{plan_id}"
+ def about_events_notifications_id(self, id):
+ return f"{self.prefix}/about/events/notifications/{id}"
- def meal_plans_id_shopping_list(self, id):
- return f"{self.prefix}/meal-plans/{id}/shopping-list"
-
- def site_settings_custom_pages_id(self, id):
- return f"{self.prefix}/site-settings/custom-pages/{id}"
-
- def themes_id(self, id):
- return f"{self.prefix}/themes/{id}"
+ def backups_file_name_delete(self, file_name):
+ return f"{self.prefix}/backups/{file_name}/delete"
def backups_file_name_download(self, file_name):
return f"{self.prefix}/backups/{file_name}/download"
@@ -107,17 +65,71 @@ class AppRoutes:
def backups_file_name_import(self, file_name):
return f"{self.prefix}/backups/{file_name}/import"
- def backups_file_name_delete(self, file_name):
- return f"{self.prefix}/backups/{file_name}/delete"
+ def categories_category(self, category):
+ return f"{self.prefix}/categories/{category}"
- def migrations_import_type_file_name_import(self, import_type, file_name):
- return f"{self.prefix}/migrations/{import_type}/{file_name}/import"
+ def debug_log_num(self, num):
+ return f"{self.prefix}/debug/log/{num}"
+
+ def groups_id(self, id):
+ return f"{self.prefix}/groups/{id}"
+
+ def meal_plans_id_shopping_list(self, id):
+ return f"{self.prefix}/meal-plans/{id}/shopping-list"
+
+ def meal_plans_plan_id(self, plan_id):
+ return f"{self.prefix}/meal-plans/{plan_id}"
+
+ def media_recipes_recipe_slug_assets_file_name(self, recipe_slug, file_name):
+ return f"{self.prefix}/media/recipes/{recipe_slug}/assets/{file_name}"
+
+ def media_recipes_recipe_slug_images_file_name(self, recipe_slug, file_name):
+ return f"{self.prefix}/media/recipes/{recipe_slug}/images/{file_name}"
def migrations_import_type_file_name_delete(self, import_type, file_name):
return f"{self.prefix}/migrations/{import_type}/{file_name}/delete"
+ def migrations_import_type_file_name_import(self, import_type, file_name):
+ return f"{self.prefix}/migrations/{import_type}/{file_name}/import"
+
def migrations_import_type_upload(self, import_type):
return f"{self.prefix}/migrations/{import_type}/upload"
- def debug_log_num(self, num):
- return f"{self.prefix}/debug/log/{num}"
+ def recipes_recipe_slug(self, recipe_slug):
+ return f"{self.prefix}/recipes/{recipe_slug}"
+
+ def recipes_recipe_slug_assets(self, recipe_slug):
+ return f"{self.prefix}/recipes/{recipe_slug}/assets"
+
+ def recipes_recipe_slug_image(self, recipe_slug):
+ return f"{self.prefix}/recipes/{recipe_slug}/image"
+
+ def shopping_lists_id(self, id):
+ return f"{self.prefix}/shopping-lists/{id}"
+
+ def site_settings_custom_pages_id(self, id):
+ return f"{self.prefix}/site-settings/custom-pages/{id}"
+
+ def tags_tag(self, tag):
+ return f"{self.prefix}/tags/{tag}"
+
+ def themes_id(self, id):
+ return f"{self.prefix}/themes/{id}"
+
+ def users_api_tokens_token_id(self, token_id):
+ return f"{self.prefix}/users-tokens/{token_id}"
+
+ def users_id(self, id):
+ return f"{self.prefix}/users/{id}"
+
+ def users_id_image(self, id):
+ return f"{self.prefix}/users/{id}/image"
+
+ def users_id_password(self, id):
+ return f"{self.prefix}/users/{id}/password"
+
+ def users_id_reset_password(self, id):
+ return f"{self.prefix}/users/{id}/reset-password"
+
+ def users_sign_ups_token(self, token):
+ return f"{self.prefix}/users/sign-ups/{token}"
diff --git a/frontend/src/api/apiRoutes.js b/frontend/src/api/apiRoutes.js
new file mode 100644
index 00000000..a978ccd3
--- /dev/null
+++ b/frontend/src/api/apiRoutes.js
@@ -0,0 +1,77 @@
+// This Content is Auto Generated
+const prefix = '/api'
+export const API_ROUTES = {
+ aboutEvents: "/api/about/events",
+ aboutEventsNotifications: "/api/about/events/notifications",
+ aboutEventsNotificationsTest: "/api/about/events/notifications/test",
+ authRefresh: "/api/auth/refresh",
+ authToken: "/api/auth/token",
+ authTokenLong: "/api/auth/token/long",
+ backupsAvailable: "/api/backups/available",
+ backupsExportDatabase: "/api/backups/export/database",
+ backupsUpload: "/api/backups/upload",
+ categories: "/api/categories",
+ categoriesEmpty: "/api/categories/empty",
+ debug: "/api/debug",
+ debugLastRecipeJson: "/api/debug/last-recipe-json",
+ debugLog: "/api/debug/log",
+ debugStatistics: "/api/debug/statistics",
+ debugVersion: "/api/debug/version",
+ groups: "/api/groups",
+ groupsSelf: "/api/groups/self",
+ mealPlansAll: "/api/meal-plans/all",
+ mealPlansCreate: "/api/meal-plans/create",
+ mealPlansThisWeek: "/api/meal-plans/this-week",
+ mealPlansToday: "/api/meal-plans/today",
+ mealPlansTodayImage: "/api/meal-plans/today/image",
+ migrations: "/api/migrations",
+ recipesCategory: "/api/recipes/category",
+ recipesCreate: "/api/recipes/create",
+ recipesCreateUrl: "/api/recipes/create-url",
+ recipesSummary: "/api/recipes/summary",
+ recipesSummaryUncategorized: "/api/recipes/summary/uncategorized",
+ recipesSummaryUntagged: "/api/recipes/summary/untagged",
+ recipesTag: "/api/recipes/tag",
+ shoppingLists: "/api/shopping-lists",
+ siteSettings: "/api/site-settings",
+ siteSettingsCustomPages: "/api/site-settings/custom-pages",
+ siteSettingsWebhooksTest: "/api/site-settings/webhooks/test",
+ tags: "/api/tags",
+ tagsEmpty: "/api/tags/empty",
+ themes: "/api/themes",
+ themesCreate: "/api/themes/create",
+ users: "/api/users",
+ usersApiTokens: "/api/users-tokens",
+ usersSelf: "/api/users/self",
+ usersSignUps: "/api/users/sign-ups",
+ utilsDownload: "/api/utils/download",
+
+ aboutEventsId: (id) => `${prefix}/about/events/${id}`,
+ aboutEventsNotificationsId: (id) => `${prefix}/about/events/notifications/${id}`,
+ backupsFileNameDelete: (file_name) => `${prefix}/backups/${file_name}/delete`,
+ backupsFileNameDownload: (file_name) => `${prefix}/backups/${file_name}/download`,
+ backupsFileNameImport: (file_name) => `${prefix}/backups/${file_name}/import`,
+ categoriesCategory: (category) => `${prefix}/categories/${category}`,
+ debugLogNum: (num) => `${prefix}/debug/log/${num}`,
+ groupsId: (id) => `${prefix}/groups/${id}`,
+ mealPlansIdShoppingList: (id) => `${prefix}/meal-plans/${id}/shopping-list`,
+ mealPlansPlanId: (plan_id) => `${prefix}/meal-plans/${plan_id}`,
+ mediaRecipesRecipeSlugAssetsFileName: (recipe_slug, file_name) => `${prefix}/media/recipes/${recipe_slug}/assets/${file_name}`,
+ mediaRecipesRecipeSlugImagesFileName: (recipe_slug, file_name) => `${prefix}/media/recipes/${recipe_slug}/images/${file_name}`,
+ migrationsImportTypeFileNameDelete: (import_type, file_name) => `${prefix}/migrations/${import_type}/${file_name}/delete`,
+ migrationsImportTypeFileNameImport: (import_type, file_name) => `${prefix}/migrations/${import_type}/${file_name}/import`,
+ migrationsImportTypeUpload: (import_type) => `${prefix}/migrations/${import_type}/upload`,
+ recipesRecipeSlug: (recipe_slug) => `${prefix}/recipes/${recipe_slug}`,
+ recipesRecipeSlugAssets: (recipe_slug) => `${prefix}/recipes/${recipe_slug}/assets`,
+ recipesRecipeSlugImage: (recipe_slug) => `${prefix}/recipes/${recipe_slug}/image`,
+ shoppingListsId: (id) => `${prefix}/shopping-lists/${id}`,
+ siteSettingsCustomPagesId: (id) => `${prefix}/site-settings/custom-pages/${id}`,
+ tagsTag: (tag) => `${prefix}/tags/${tag}`,
+ themesId: (id) => `${prefix}/themes/${id}`,
+ usersApiTokensTokenId: (token_id) => `${prefix}/users-tokens/${token_id}`,
+ usersId: (id) => `${prefix}/users/${id}`,
+ usersIdImage: (id) => `${prefix}/users/${id}/image`,
+ usersIdPassword: (id) => `${prefix}/users/${id}/password`,
+ usersIdResetPassword: (id) => `${prefix}/users/${id}/reset-password`,
+ usersSignUpsToken: (token) => `${prefix}/users/sign-ups/${token}`,
+}
\ No newline at end of file
diff --git a/frontend/src/api/index.js b/frontend/src/api/index.js
index 53d22e8a..dbc63321 100644
--- a/frontend/src/api/index.js
+++ b/frontend/src/api/index.js
@@ -12,6 +12,7 @@ import { signupAPI } from "./signUps";
import { groupAPI } from "./groups";
import { siteSettingsAPI } from "./siteSettings";
import { aboutAPI } from "./about";
+import { shoppingListsAPI } from "./shoppingLists";
/**
* The main object namespace for interacting with the backend database
@@ -32,4 +33,5 @@ export const api = {
signUps: signupAPI,
groups: groupAPI,
about: aboutAPI,
+ shoppingLists: shoppingListsAPI,
};
diff --git a/frontend/src/api/shoppingLists.js b/frontend/src/api/shoppingLists.js
new file mode 100644
index 00000000..74a7c9e2
--- /dev/null
+++ b/frontend/src/api/shoppingLists.js
@@ -0,0 +1,33 @@
+// This Content is Auto Generated
+import { API_ROUTES } from "./apiRoutes";
+import { apiReq } from "./api-utils";
+
+export const shoppingListsAPI = {
+ /** Create Shopping List in the Database
+ */
+ async createShoppingList(data) {
+ const response = await apiReq.post(API_ROUTES.shoppingLists, data);
+ return response.data;
+ },
+ /** Get Shopping List from the Database
+ * @param id
+ */
+ async getShoppingList(id) {
+ const response = await apiReq.get(API_ROUTES.shoppingListsId(id));
+ return response.data;
+ },
+ /** Update Shopping List in the Database
+ * @param id
+ */
+ async updateShoppingList(id, data) {
+ const response = await apiReq.put(API_ROUTES.shoppingListsId(id), data);
+ return response.data;
+ },
+ /** Delete Shopping List from the Database
+ * @param id
+ */
+ async deleteShoppingList(id) {
+ const response = await apiReq.delete(API_ROUTES.shoppingListsId(id));
+ return response.data;
+ },
+};
diff --git a/frontend/src/components/Fallbacks/NoRecipe.vue b/frontend/src/components/Fallbacks/NoRecipe.vue
new file mode 100644
index 00000000..d93b9240
--- /dev/null
+++ b/frontend/src/components/Fallbacks/NoRecipe.vue
@@ -0,0 +1,17 @@
+
+
+
+ No Recipe Found
+
+
+
+
+
+
+
+
diff --git a/frontend/src/components/Fallbacks/The404.vue b/frontend/src/components/Fallbacks/The404.vue
new file mode 100644
index 00000000..01e57e37
--- /dev/null
+++ b/frontend/src/components/Fallbacks/The404.vue
@@ -0,0 +1,51 @@
+
+
+
+
+ {{ $t("404.page-not-found") }}
+
+
+
+
+
4
+
+ mdi-silverware-variant
+
+
4
+
+
+
+
+
+
+ {{ button.icon }}
+ {{ button.text }}
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/frontend/src/components/MealPlan/MealPlanCard.vue b/frontend/src/components/MealPlan/MealPlanCard.vue
index 69330e89..da95e286 100644
--- a/frontend/src/components/MealPlan/MealPlanCard.vue
+++ b/frontend/src/components/MealPlan/MealPlanCard.vue
@@ -1,14 +1,76 @@
-
+
+
+
+
+
+
+
-
+
+
+
+
+ mdi-square-edit-outline
+
+ No Recipe
+
+
+
+
- {{ $d(new Date(meal.date.split("-")), "short") }}
+ {{ $d(new Date(planDay.date.split("-")), "short") }}
- {{ meal.name }}
+ {{ planDay.meals[0].name }}
+
+
+
+
+
+
+ mdi-square-edit-outline
+
+ No Recipe
+
+
+
+
+ mdi-plus
+
+ Side
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ mdi-delete
+
+
+
+
+
@@ -17,38 +79,101 @@
-
+
diff --git a/frontend/src/components/MealPlan/MealPlanEditor.vue b/frontend/src/components/MealPlan/MealPlanEditor.vue
index f38cdedd..682842a9 100644
--- a/frontend/src/components/MealPlan/MealPlanEditor.vue
+++ b/frontend/src/components/MealPlan/MealPlanEditor.vue
@@ -6,7 +6,7 @@
-
+
@@ -30,6 +30,9 @@ export default {
props: {
mealPlan: Object,
},
+ mounted() {
+ console.log(this.mealPlan);
+ },
methods: {
formatDate(timestamp) {
let dateObject = new Date(timestamp);
diff --git a/frontend/src/components/MealPlan/MealPlanNew.vue b/frontend/src/components/MealPlan/MealPlanNew.vue
index d0f264e6..00d41f63 100644
--- a/frontend/src/components/MealPlan/MealPlanNew.vue
+++ b/frontend/src/components/MealPlan/MealPlanNew.vue
@@ -63,14 +63,14 @@
-
+
-
+
{{ $t("general.random") }}
-
+
{{ $t("general.save") }}
@@ -92,7 +92,7 @@ export default {
data() {
return {
isLoading: false,
- meals: [],
+ planDays: [],
items: [],
// Dates
@@ -106,11 +106,17 @@ export default {
watch: {
dateDif() {
- this.meals = [];
+ this.planDays = [];
for (let i = 0; i < this.dateDif; i++) {
- this.meals.push({
- slug: "empty",
+ this.planDays.push({
date: this.getDate(i),
+ meals: [
+ {
+ name: "",
+ slug: "empty",
+ description: "empty",
+ },
+ ],
});
}
},
@@ -172,10 +178,10 @@ export default {
},
random() {
this.usedRecipes = [1];
- this.meals.forEach((element, index) => {
+ this.planDays.forEach((element, index) => {
let recipe = this.getRandom(this.filteredRecipes);
- this.meals[index]["slug"] = recipe.slug;
- this.meals[index]["name"] = recipe.name;
+ this.planDays[index]["meals"][0]["slug"] = recipe.slug;
+ this.planDays[index]["meals"][0]["name"] = recipe.name;
this.usedRecipes.push(recipe);
});
},
@@ -193,11 +199,11 @@ export default {
group: this.groupSettings.name,
startDate: this.startDate,
endDate: this.endDate,
- meals: this.meals,
+ planDays: this.planDays,
};
if (await api.mealPlans.create(mealBody)) {
this.$emit(CREATE_EVENT);
- this.meals = [];
+ this.planDays = [];
this.startDate = null;
this.endDate = null;
}
diff --git a/frontend/src/components/Recipe/CardImage.vue b/frontend/src/components/Recipe/CardImage.vue
new file mode 100644
index 00000000..d37f1661
--- /dev/null
+++ b/frontend/src/components/Recipe/CardImage.vue
@@ -0,0 +1,99 @@
+
+
+
+
+
+
+
+
+
+
+ mdi-silverware-variant
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/frontend/src/components/Recipe/RecipeCard.vue b/frontend/src/components/Recipe/RecipeCard.vue
index d84aa956..8a22176d 100644
--- a/frontend/src/components/Recipe/RecipeCard.vue
+++ b/frontend/src/components/Recipe/RecipeCard.vue
@@ -7,10 +7,7 @@
@click="$emit('click')"
min-height="275"
>
-
-
- mdi-silverware-variant
-
+
@@ -18,7 +15,7 @@
-
+