diff --git a/.gitignore b/.gitignore index 4fcab0443..e91e0d2f4 100644 --- a/.gitignore +++ b/.gitignore @@ -1,6 +1,6 @@ # Byte-compiled / optimized / DLL files .env -__pycache__/ +*__pycache__/ *.py[cod] *$py.class # frontend/.env.development diff --git a/frontend/src/App.vue b/frontend/src/App.vue index 4698f055a..5dc73ef67 100644 --- a/frontend/src/App.vue +++ b/frontend/src/App.vue @@ -76,8 +76,6 @@ export default { mounted() { this.$store.dispatch("initTheme"); this.$store.dispatch("requestRecentRecipes"); - this.$store.dispatch("requestHomePageSettings"); - this.$store.dispatch("requestSiteSettings"); this.$store.dispatch("refreshToken"); this.$store.dispatch("requestCurrentGroup"); this.darkModeSystemCheck(); diff --git a/frontend/src/components/Admin/General/CustomPageCreator.vue b/frontend/src/components/Admin/General/CustomPageCreator.vue new file mode 100644 index 000000000..951984362 --- /dev/null +++ b/frontend/src/components/Admin/General/CustomPageCreator.vue @@ -0,0 +1,138 @@ + + + + + \ No newline at end of file diff --git a/frontend/src/components/UI/CardSection.vue b/frontend/src/components/UI/CardSection.vue index 078e2b83b..3d53ee6a5 100644 --- a/frontend/src/components/UI/CardSection.vue +++ b/frontend/src/components/UI/CardSection.vue @@ -1,9 +1,9 @@ @@ -84,12 +91,19 @@ export default { sortable: { default: false, }, - title: String, + title: { + default: null + }, recipes: Array, cardLimit: { - default: 6, + default: 999, }, }, + watch: { + recipes(val) { + console.log(val) + } + }, computed: { viewScale() { switch (this.$vuetify.breakpoint.name) { diff --git a/frontend/src/pages/Admin/Settings/index.vue b/frontend/src/pages/Admin/Settings/index.vue index 13d2bc18f..2a7920d44 100644 --- a/frontend/src/pages/Admin/Settings/index.vue +++ b/frontend/src/pages/Admin/Settings/index.vue @@ -14,15 +14,19 @@ + + + + \ No newline at end of file diff --git a/frontend/src/routes/index.js b/frontend/src/routes/index.js index df94b1512..01eb86780 100644 --- a/frontend/src/routes/index.js +++ b/frontend/src/routes/index.js @@ -3,6 +3,7 @@ import Page404 from "@/pages/404Page"; import SearchPage from "@/pages/SearchPage"; import ViewRecipe from "@/pages/Recipe/ViewRecipe"; import NewRecipe from "@/pages/Recipe/NewRecipe"; +import CustomPage from "@/pages/Recipes/CustomPage"; import AllRecipes from "@/pages/Recipes/AllRecipes"; import CategoryPage from "@/pages/Recipes/CategoryPage"; import Planner from "@/pages/MealPlan/Planner"; @@ -31,6 +32,7 @@ export const routes = [ { path: "/debug", component: Debug }, { path: "/search", component: SearchPage }, { path: "/recipes/all", component: AllRecipes }, + { path: "/recipes/test-page", component: CustomPage }, { path: "/recipes/:category", component: CategoryPage }, { path: "/recipe/:recipe", component: ViewRecipe }, { path: "/new/", component: NewRecipe }, diff --git a/frontend/src/store/index.js b/frontend/src/store/index.js index b6bbc277c..a538bed6e 100644 --- a/frontend/src/store/index.js +++ b/frontend/src/store/index.js @@ -4,7 +4,6 @@ import api from "@/api"; import createPersistedState from "vuex-persistedstate"; import userSettings from "./modules/userSettings"; import language from "./modules/language"; -import homePage from "./modules/homePage"; import siteSettings from "./modules/siteSettings"; import groups from "./modules/groups"; @@ -13,13 +12,12 @@ Vue.use(Vuex); const store = new Vuex.Store({ plugins: [ createPersistedState({ - paths: ["userSettings", "language", "homePage", "SideSettings"], + paths: ["userSettings", "language", "SideSettings"], }), ], modules: { userSettings, language, - homePage, siteSettings, groups, }, diff --git a/frontend/src/store/modules/siteSettings.js b/frontend/src/store/modules/siteSettings.js index d8357fa9b..1495b4c72 100644 --- a/frontend/src/store/modules/siteSettings.js +++ b/frontend/src/store/modules/siteSettings.js @@ -11,7 +11,7 @@ const state = { const mutations = { setSettings(state, payload) { - state.settings = payload; + state.siteSettings = payload; }, }; diff --git a/mealie/app.py b/mealie/app.py index f4faeafd1..89ee63842 100644 --- a/mealie/app.py +++ b/mealie/app.py @@ -4,7 +4,8 @@ from fastapi.logger import logger # import utils.startup as startup from mealie.core.config import APP_VERSION, PORT, docs_url, redoc_url -from mealie.routes import backup_routes, debug_routes, migration_routes, setting_routes, theme_routes +from mealie.routes import backup_routes, debug_routes, migration_routes, theme_routes +from mealie.routes.site_settings import all_settings from mealie.routes.groups import groups from mealie.routes.mealplans import mealplans from mealie.routes.recipe import all_recipe_routes, category_routes, recipe_crud_routes, tag_routes @@ -36,7 +37,7 @@ def api_routers(): # Meal Routes app.include_router(mealplans.router) # Settings Routes - app.include_router(setting_routes.router) + app.include_router(all_settings.router) app.include_router(theme_routes.router) # Backups/Imports Routes app.include_router(backup_routes.router) diff --git a/mealie/db/database.py b/mealie/db/database.py index a519ed309..5a1a66343 100644 --- a/mealie/db/database.py +++ b/mealie/db/database.py @@ -2,14 +2,14 @@ from mealie.db.db_base import BaseDocument from mealie.db.models.group import Group from mealie.db.models.mealplan import MealPlanModel from mealie.db.models.recipe.recipe import Category, RecipeModel, Tag -from mealie.db.models.settings import SiteSettings +from mealie.db.models.settings import CustomPage, SiteSettings from mealie.db.models.sign_up import SignUp from mealie.db.models.theme import SiteThemeModel from mealie.db.models.users import User from mealie.schema.category import RecipeCategoryResponse, RecipeTagResponse from mealie.schema.meal import MealPlanInDB from mealie.schema.recipe import Recipe -from mealie.schema.settings import SiteSettings as SiteSettingsSchema +from mealie.schema.settings import CustomPageOut, SiteSettings as SiteSettingsSchema from mealie.schema.sign_up import SignUpOut from mealie.schema.theme import SiteTheme from mealie.schema.user import GroupInDB, UserInDB @@ -118,6 +118,13 @@ class _SignUps(BaseDocument): self.orm_mode = True self.schema = SignUpOut +class _CustomPages(BaseDocument): + def __init__(self) -> None: + self.primary_key = "id" + self.sql_model = CustomPage + self.orm_mode = True + self.schema = CustomPageOut + class Database: def __init__(self) -> None: @@ -130,6 +137,7 @@ class Database: self.users = _Users() self.sign_ups = _SignUps() self.groups = _Groups() + self.custom_pages = _CustomPages() db = Database() diff --git a/mealie/db/models/recipe/category.py b/mealie/db/models/recipe/category.py index e954bc02b..a4ee7b0b3 100644 --- a/mealie/db/models/recipe/category.py +++ b/mealie/db/models/recipe/category.py @@ -26,6 +26,13 @@ recipes2categories = sa.Table( sa.Column("category_slug", sa.String, sa.ForeignKey("categories.slug")), ) +custom_pages2categories = sa.Table( + "custom_pages2categories", + SqlAlchemyBase.metadata, + sa.Column("custom_page_id", sa.Integer, sa.ForeignKey("custom_pages.id")), + sa.Column("category_slug", sa.String, sa.ForeignKey("categories.slug")), +) + class Category(SqlAlchemyBase): __tablename__ = "categories" @@ -36,7 +43,7 @@ class Category(SqlAlchemyBase): @validates("name") def validate_name(self, key, name): - assert not name == "" + assert name != "" return name def __init__(self, name) -> None: diff --git a/mealie/db/models/settings.py b/mealie/db/models/settings.py index b26e80ed1..1f64745ca 100644 --- a/mealie/db/models/settings.py +++ b/mealie/db/models/settings.py @@ -1,7 +1,7 @@ import sqlalchemy as sa import sqlalchemy.orm as orm from mealie.db.models.model_base import BaseMixins, SqlAlchemyBase -from mealie.db.models.recipe.category import Category, site_settings2categories +from mealie.db.models.recipe.category import Category, custom_pages2categories, site_settings2categories from sqlalchemy.orm import Session @@ -29,7 +29,29 @@ class SiteSettings(SqlAlchemyBase, BaseMixins): self.language = language self.cards_per_section = cards_per_section self.show_recent = show_recent - self.categories = [Category.get_ref(session=session, name=cat.get("slug")) for cat in categories] + self.categories = [Category.get_ref(session=session, slug=cat.get("slug")) for cat in categories] + + def update(self, *args, **kwarg): + self.__init__(*args, **kwarg) + + +class CustomPage(SqlAlchemyBase, BaseMixins): + __tablename__ = "custom_pages" + id = sa.Column(sa.Integer, primary_key=True) + position = sa.Column(sa.Integer, nullable=False) + name = sa.Column(sa.String, nullable=False) + slug = sa.Column(sa.String, nullable=False) + categories = orm.relationship( + "Category", + secondary=custom_pages2categories, + single_parent=True, + ) + + def __init__(self, session=None, name=None, slug=None, position=0, categories=[]) -> None: + self.name = name + self.slug = slug + self.position = position + self.categories = [Category.get_ref(session=session, slug=cat.get("slug")) for cat in categories] def update(self, *args, **kwarg): self.__init__(*args, **kwarg) diff --git a/mealie/routes/users/__init__ copy.py b/mealie/routes/site_settings/__init__.py similarity index 100% rename from mealie/routes/users/__init__ copy.py rename to mealie/routes/site_settings/__init__.py diff --git a/mealie/routes/site_settings/all_settings.py b/mealie/routes/site_settings/all_settings.py new file mode 100644 index 000000000..09da2eda2 --- /dev/null +++ b/mealie/routes/site_settings/all_settings.py @@ -0,0 +1,7 @@ +from fastapi import APIRouter +from mealie.routes.site_settings import custom_pages, site_settings + +router = APIRouter() + +router.include_router(custom_pages.router) +router.include_router(site_settings.router) diff --git a/mealie/routes/site_settings/custom_pages.py b/mealie/routes/site_settings/custom_pages.py new file mode 100644 index 000000000..4c91779c1 --- /dev/null +++ b/mealie/routes/site_settings/custom_pages.py @@ -0,0 +1,52 @@ +from fastapi import APIRouter, Depends +from mealie.db.database import db +from mealie.db.db_setup import generate_session +from mealie.routes.deps import get_current_user +from mealie.schema.settings import CustomPageBase +from mealie.schema.snackbar import SnackResponse +from mealie.schema.user import UserInDB +from sqlalchemy.orm.session import Session + +router = APIRouter(prefix="/api/site-settings/custom-pages", tags=["Settings"]) + + +@router.get("") +def get_custom_pages(session: Session = Depends(generate_session)): + """ Returns the sites custom pages """ + + return db.custom_pages.get_all(session) + + +@router.post("") +async def create_new_page( + new_page: CustomPageBase, + session: Session = Depends(generate_session), + current_user: UserInDB = Depends(get_current_user), +): + """ Creates a new Custom Page """ + + db.custom_pages.create(session, new_page.dict()) + + return SnackResponse.success("New Page Created") + + +@router.get("/{id}") +async def delete_custom_page( + id: int, + session: Session = Depends(generate_session), +): + """ Removes a custom page from the database """ + + return db.custom_pages.get(session, id) + + +@router.delete("/{id}") +async def delete_custom_page( + id: int, + session: Session = Depends(generate_session), + current_user: UserInDB = Depends(get_current_user), +): + """ Removes a custom page from the database """ + + db.custom_pages.delete(session, id) + return diff --git a/mealie/routes/setting_routes.py b/mealie/routes/site_settings/site_settings.py similarity index 95% rename from mealie/routes/setting_routes.py rename to mealie/routes/site_settings/site_settings.py index 81cd18d46..4f7bf9772 100644 --- a/mealie/routes/setting_routes.py +++ b/mealie/routes/site_settings/site_settings.py @@ -16,9 +16,7 @@ router = APIRouter(prefix="/api/site-settings", tags=["Settings"]) def get_main_settings(session: Session = Depends(generate_session)): """ Returns basic site settings """ - data = db.settings.get(session, 1) - - return data + return db.settings.get(session, 1) @router.put("") diff --git a/mealie/schema/recipe.py b/mealie/schema/recipe.py index ad343be18..54a83d534 100644 --- a/mealie/schema/recipe.py +++ b/mealie/schema/recipe.py @@ -101,11 +101,10 @@ class Recipe(BaseModel): name: str = values["name"] calc_slug: str = slugify(name) - if slug == calc_slug: - return slug - else: + if slug != calc_slug: slug = calc_slug - return slug + + return slug class AllRecipeRequest(BaseModel): diff --git a/mealie/schema/settings.py b/mealie/schema/settings.py index 0e97aa8c9..9f147d16a 100644 --- a/mealie/schema/settings.py +++ b/mealie/schema/settings.py @@ -1,8 +1,9 @@ from typing import Optional from fastapi_camelcase import CamelModel - from mealie.schema.category import CategoryBase +from pydantic import validator +from slugify import slugify class SiteSettings(CamelModel): @@ -25,3 +26,30 @@ class SiteSettings(CamelModel): ], } } + + +class CustomPageBase(CamelModel): + name: str + slug: Optional[str] + position: int + categories: list[CategoryBase] = [] + + class Config: + orm_mode = True + + @validator("slug", always=True, pre=True) + def validate_slug(slug: str, values): + name: str = values["name"] + calc_slug: str = slugify(name) + + if slug != calc_slug: + slug = calc_slug + + return slug + + +class CustomPageOut(CustomPageBase): + id: int + + class Config: + orm_mode = True