mirror of
https://github.com/hay-kot/mealie.git
synced 2025-08-22 22:43:34 -07:00
custom pages starter
This commit is contained in:
parent
318cbceee6
commit
341faace21
20 changed files with 447 additions and 81 deletions
2
.gitignore
vendored
2
.gitignore
vendored
|
@ -1,6 +1,6 @@
|
||||||
# Byte-compiled / optimized / DLL files
|
# Byte-compiled / optimized / DLL files
|
||||||
.env
|
.env
|
||||||
__pycache__/
|
*__pycache__/
|
||||||
*.py[cod]
|
*.py[cod]
|
||||||
*$py.class
|
*$py.class
|
||||||
# frontend/.env.development
|
# frontend/.env.development
|
||||||
|
|
|
@ -76,8 +76,6 @@ export default {
|
||||||
mounted() {
|
mounted() {
|
||||||
this.$store.dispatch("initTheme");
|
this.$store.dispatch("initTheme");
|
||||||
this.$store.dispatch("requestRecentRecipes");
|
this.$store.dispatch("requestRecentRecipes");
|
||||||
this.$store.dispatch("requestHomePageSettings");
|
|
||||||
this.$store.dispatch("requestSiteSettings");
|
|
||||||
this.$store.dispatch("refreshToken");
|
this.$store.dispatch("refreshToken");
|
||||||
this.$store.dispatch("requestCurrentGroup");
|
this.$store.dispatch("requestCurrentGroup");
|
||||||
this.darkModeSystemCheck();
|
this.darkModeSystemCheck();
|
||||||
|
|
138
frontend/src/components/Admin/General/CustomPageCreator.vue
Normal file
138
frontend/src/components/Admin/General/CustomPageCreator.vue
Normal file
|
@ -0,0 +1,138 @@
|
||||||
|
<template>
|
||||||
|
<v-card flat>
|
||||||
|
<v-card-text>
|
||||||
|
<h2 class="mt-1 mb-1 ">
|
||||||
|
Custom Pages
|
||||||
|
<span>
|
||||||
|
<v-btn color="success" small class="ml-3">
|
||||||
|
New
|
||||||
|
</v-btn>
|
||||||
|
</span>
|
||||||
|
</h2>
|
||||||
|
<v-row class="mt-1">
|
||||||
|
<v-col
|
||||||
|
:sm="6"
|
||||||
|
:md="6"
|
||||||
|
:lg="4"
|
||||||
|
:xl="3"
|
||||||
|
v-for="item in customPages"
|
||||||
|
:key="item + item.id"
|
||||||
|
>
|
||||||
|
<v-card>
|
||||||
|
<v-card-title class="headline">{{ item.name }}</v-card-title>
|
||||||
|
<v-divider></v-divider>
|
||||||
|
|
||||||
|
<v-card-text>
|
||||||
|
Card Position: {{ item.position }}
|
||||||
|
<div>
|
||||||
|
<v-chip
|
||||||
|
v-for="cat in item.categories"
|
||||||
|
:key="cat.slug + cat.id"
|
||||||
|
class="my-2 mr-2"
|
||||||
|
label
|
||||||
|
small
|
||||||
|
color="accent lighten-1"
|
||||||
|
>
|
||||||
|
{{ cat.name }}
|
||||||
|
</v-chip>
|
||||||
|
</div>
|
||||||
|
</v-card-text>
|
||||||
|
|
||||||
|
<v-card-actions>
|
||||||
|
<v-btn text small color="error">
|
||||||
|
Delete
|
||||||
|
</v-btn>
|
||||||
|
<v-spacer> </v-spacer>
|
||||||
|
<v-btn small text color="success">
|
||||||
|
Edit
|
||||||
|
</v-btn>
|
||||||
|
</v-card-actions>
|
||||||
|
</v-card>
|
||||||
|
</v-col>
|
||||||
|
</v-row>
|
||||||
|
</v-card-text>
|
||||||
|
</v-card>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
export default {
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
customPages: [
|
||||||
|
{
|
||||||
|
id: 0,
|
||||||
|
name: "My Page Name",
|
||||||
|
slug: "my-page-name",
|
||||||
|
position: 0,
|
||||||
|
categories: [
|
||||||
|
{
|
||||||
|
id: 2,
|
||||||
|
slug: "brownie",
|
||||||
|
name: "brownie",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 3,
|
||||||
|
slug: "dessert",
|
||||||
|
name: "dessert",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 4,
|
||||||
|
slug: "drink",
|
||||||
|
name: "Drink",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 3,
|
||||||
|
name: "My Page Name 1",
|
||||||
|
slug: "my-page-name",
|
||||||
|
position: 1,
|
||||||
|
categories: [
|
||||||
|
{
|
||||||
|
id: 2,
|
||||||
|
slug: "brownie",
|
||||||
|
name: "brownie",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 3,
|
||||||
|
slug: "dessert",
|
||||||
|
name: "dessert",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 4,
|
||||||
|
slug: "drink",
|
||||||
|
name: "Drink",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 2,
|
||||||
|
name: "My Page Name 2",
|
||||||
|
slug: "my-page-name",
|
||||||
|
position: 2,
|
||||||
|
categories: [
|
||||||
|
{
|
||||||
|
id: 2,
|
||||||
|
slug: "brownie",
|
||||||
|
name: "brownie",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 3,
|
||||||
|
slug: "dessert",
|
||||||
|
name: "dessert",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 4,
|
||||||
|
slug: "drink",
|
||||||
|
name: "Drink",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
};
|
||||||
|
},
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
</style>
|
|
@ -1,8 +1,8 @@
|
||||||
<template>
|
<template>
|
||||||
<div class="mt-n5">
|
<div class="mt-n5" v-if="recipes">
|
||||||
<v-card flat class="transparent" height="60px">
|
<v-card flat class="transparent" height="60px">
|
||||||
<v-card-text>
|
<v-card-text>
|
||||||
<v-row>
|
<v-row v-if="title != null">
|
||||||
<v-col >
|
<v-col >
|
||||||
<v-btn-toggle group>
|
<v-btn-toggle group>
|
||||||
<v-btn text :to="`/recipes/${title.toLowerCase()}`">
|
<v-btn text :to="`/recipes/${title.toLowerCase()}`">
|
||||||
|
@ -15,15 +15,21 @@
|
||||||
<v-menu offset-y v-if="sortable">
|
<v-menu offset-y v-if="sortable">
|
||||||
<template v-slot:activator="{ on, attrs }">
|
<template v-slot:activator="{ on, attrs }">
|
||||||
<v-btn-toggle group>
|
<v-btn-toggle group>
|
||||||
<v-btn text v-bind="attrs" v-on="on">{{$t('general.sort')}}</v-btn>
|
<v-btn text v-bind="attrs" v-on="on">{{
|
||||||
|
$t("general.sort")
|
||||||
|
}}</v-btn>
|
||||||
</v-btn-toggle>
|
</v-btn-toggle>
|
||||||
</template>
|
</template>
|
||||||
<v-list>
|
<v-list>
|
||||||
<v-list-item @click="$emit('sort-recent')">
|
<v-list-item @click="$emit('sort-recent')">
|
||||||
<v-list-item-title>{{$t('general.recent')}}</v-list-item-title>
|
<v-list-item-title>{{
|
||||||
|
$t("general.recent")
|
||||||
|
}}</v-list-item-title>
|
||||||
</v-list-item>
|
</v-list-item>
|
||||||
<v-list-item @click="$emit('sort')">
|
<v-list-item @click="$emit('sort')">
|
||||||
<v-list-item-title>{{$t('general.sort-alphabetically')}}</v-list-item-title>
|
<v-list-item-title>{{
|
||||||
|
$t("general.sort-alphabetically")
|
||||||
|
}}</v-list-item-title>
|
||||||
</v-list-item>
|
</v-list-item>
|
||||||
</v-list>
|
</v-list>
|
||||||
</v-menu>
|
</v-menu>
|
||||||
|
@ -31,6 +37,7 @@
|
||||||
</v-row>
|
</v-row>
|
||||||
</v-card-text>
|
</v-card-text>
|
||||||
</v-card>
|
</v-card>
|
||||||
|
<div v-if="recipes">
|
||||||
<v-row v-if="!viewScale">
|
<v-row v-if="!viewScale">
|
||||||
<v-col
|
<v-col
|
||||||
:sm="6"
|
:sm="6"
|
||||||
|
@ -58,7 +65,6 @@
|
||||||
xl="3"
|
xl="3"
|
||||||
v-for="recipe in recipes.slice(0, cardLimit)"
|
v-for="recipe in recipes.slice(0, cardLimit)"
|
||||||
:key="recipe.name"
|
:key="recipe.name"
|
||||||
|
|
||||||
>
|
>
|
||||||
<MobileRecipeCard
|
<MobileRecipeCard
|
||||||
:name="recipe.name"
|
:name="recipe.name"
|
||||||
|
@ -70,6 +76,7 @@
|
||||||
</v-col>
|
</v-col>
|
||||||
</v-row>
|
</v-row>
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
|
@ -84,12 +91,19 @@ export default {
|
||||||
sortable: {
|
sortable: {
|
||||||
default: false,
|
default: false,
|
||||||
},
|
},
|
||||||
title: String,
|
title: {
|
||||||
|
default: null
|
||||||
|
},
|
||||||
recipes: Array,
|
recipes: Array,
|
||||||
cardLimit: {
|
cardLimit: {
|
||||||
default: 6,
|
default: 999,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
watch: {
|
||||||
|
recipes(val) {
|
||||||
|
console.log(val)
|
||||||
|
}
|
||||||
|
},
|
||||||
computed: {
|
computed: {
|
||||||
viewScale() {
|
viewScale() {
|
||||||
switch (this.$vuetify.breakpoint.name) {
|
switch (this.$vuetify.breakpoint.name) {
|
||||||
|
|
|
@ -14,15 +14,19 @@
|
||||||
<v-divider></v-divider>
|
<v-divider></v-divider>
|
||||||
<HomePageSettings />
|
<HomePageSettings />
|
||||||
<v-divider></v-divider>
|
<v-divider></v-divider>
|
||||||
|
<CustomPageCreator />
|
||||||
|
<v-divider></v-divider>
|
||||||
</v-card>
|
</v-card>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import HomePageSettings from "@/components/Admin/General/HomePageSettings";
|
import HomePageSettings from "@/components/Admin/General/HomePageSettings";
|
||||||
|
import CustomPageCreator from "@/components/Admin/General/CustomPageCreator";
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
components: {
|
components: {
|
||||||
HomePageSettings,
|
HomePageSettings,
|
||||||
|
CustomPageCreator,
|
||||||
},
|
},
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
|
|
|
@ -2,10 +2,10 @@
|
||||||
<v-container>
|
<v-container>
|
||||||
<CategorySidebar />
|
<CategorySidebar />
|
||||||
<CardSection
|
<CardSection
|
||||||
v-if="showRecent"
|
v-if="siteSettings.showRecent"
|
||||||
:title="$t('page.recent')"
|
:title="$t('page.recent')"
|
||||||
:recipes="recentRecipes"
|
:recipes="recentRecipes"
|
||||||
:card-limit="showLimit"
|
:card-limit="siteSettings.cardsPerSection"
|
||||||
/>
|
/>
|
||||||
<CardSection
|
<CardSection
|
||||||
:sortable="true"
|
:sortable="true"
|
||||||
|
@ -13,7 +13,7 @@
|
||||||
:key="section.name + section.position"
|
:key="section.name + section.position"
|
||||||
:title="section.name"
|
:title="section.name"
|
||||||
:recipes="section.recipes"
|
:recipes="section.recipes"
|
||||||
:card-limit="showLimit"
|
:card-limit="siteSettings.cardsPerSection"
|
||||||
@sort="sortAZ(index)"
|
@sort="sortAZ(index)"
|
||||||
@sort-recent="sortRecent(index)"
|
@sort-recent="sortRecent(index)"
|
||||||
/>
|
/>
|
||||||
|
@ -35,14 +35,9 @@ export default {
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
showRecent() {
|
siteSettings() {
|
||||||
return this.$store.getters.getShowRecent;
|
console.log(this.$store.getters.getSiteSettings);
|
||||||
},
|
return this.$store.getters.getSiteSettings;
|
||||||
showLimit() {
|
|
||||||
return this.$store.getters.getShowLimit;
|
|
||||||
},
|
|
||||||
homeCategories() {
|
|
||||||
return this.$store.getters.getHomeCategories;
|
|
||||||
},
|
},
|
||||||
recentRecipes() {
|
recentRecipes() {
|
||||||
let recipes = this.$store.getters.getRecentRecipes;
|
let recipes = this.$store.getters.getRecentRecipes;
|
||||||
|
@ -55,9 +50,11 @@ export default {
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
async buildPage() {
|
async buildPage() {
|
||||||
this.homeCategories.forEach(async element => {
|
await this.$store.dispatch("requestSiteSettings");
|
||||||
|
this.siteSettings.categories.forEach(async element => {
|
||||||
let recipes = await this.getRecipeByCategory(element.slug);
|
let recipes = await this.getRecipeByCategory(element.slug);
|
||||||
recipes.position = element.position;
|
if (recipes.recipes.length < 0 ) recipes.recipes = []
|
||||||
|
console.log(recipes)
|
||||||
this.recipeByCategory.push(recipes);
|
this.recipeByCategory.push(recipes);
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
93
frontend/src/pages/Recipes/CustomPage.vue
Normal file
93
frontend/src/pages/Recipes/CustomPage.vue
Normal file
|
@ -0,0 +1,93 @@
|
||||||
|
<template>
|
||||||
|
<v-container>
|
||||||
|
<CategorySidebar />
|
||||||
|
<v-card flat height="100%">
|
||||||
|
<v-card-title class="text-center justify-center py-6 headline">
|
||||||
|
Category Section
|
||||||
|
</v-card-title>
|
||||||
|
<div v-if="render">
|
||||||
|
<v-tabs v-model="tab" background-color="transparent" grow>
|
||||||
|
<v-tab v-for="item in categories" :key="item.slug">
|
||||||
|
{{ item.name }}
|
||||||
|
</v-tab>
|
||||||
|
</v-tabs>
|
||||||
|
|
||||||
|
<v-tabs-items v-model="tab">
|
||||||
|
<v-tab-item
|
||||||
|
v-for="(item, index) in categories"
|
||||||
|
:key="item.slug + index"
|
||||||
|
>
|
||||||
|
<CardSection class="mb-5 mx-1" :recipes="filterRecipe(item.slug)" />
|
||||||
|
</v-tab-item>
|
||||||
|
</v-tabs-items>
|
||||||
|
</div>
|
||||||
|
</v-card>
|
||||||
|
</v-container>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import CardSection from "@/components/UI/CardSection";
|
||||||
|
import CategorySidebar from "@/components/UI/CategorySidebar";
|
||||||
|
import api from "@/api";
|
||||||
|
|
||||||
|
export default {
|
||||||
|
components: {
|
||||||
|
CardSection,
|
||||||
|
CategorySidebar,
|
||||||
|
},
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
tab: null,
|
||||||
|
render: false,
|
||||||
|
recipeStore: [],
|
||||||
|
categories: [
|
||||||
|
{
|
||||||
|
id: 2,
|
||||||
|
slug: "brownie",
|
||||||
|
name: "brownie",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 3,
|
||||||
|
slug: "dessert",
|
||||||
|
name: "dessert",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 4,
|
||||||
|
slug: "drink",
|
||||||
|
name: "Drink",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
};
|
||||||
|
},
|
||||||
|
|
||||||
|
watch: {
|
||||||
|
tab(val) {
|
||||||
|
console.log(val);
|
||||||
|
},
|
||||||
|
},
|
||||||
|
async mounted() {
|
||||||
|
await this.buildPage();
|
||||||
|
this.render = true;
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
async buildPage() {
|
||||||
|
this.categories.forEach(async element => {
|
||||||
|
let categoryRecipes = await this.getRecipeByCategory(element.slug);
|
||||||
|
this.recipeStore.push(categoryRecipes);
|
||||||
|
});
|
||||||
|
},
|
||||||
|
async getRecipeByCategory(category) {
|
||||||
|
return await api.categories.get_recipes_in_category(category);
|
||||||
|
},
|
||||||
|
filterRecipe(slug) {
|
||||||
|
const storeCategory = this.recipeStore.find(
|
||||||
|
element => element.slug === slug
|
||||||
|
);
|
||||||
|
return storeCategory ? storeCategory.recipes : [];
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
</style>
|
|
@ -3,6 +3,7 @@ import Page404 from "@/pages/404Page";
|
||||||
import SearchPage from "@/pages/SearchPage";
|
import SearchPage from "@/pages/SearchPage";
|
||||||
import ViewRecipe from "@/pages/Recipe/ViewRecipe";
|
import ViewRecipe from "@/pages/Recipe/ViewRecipe";
|
||||||
import NewRecipe from "@/pages/Recipe/NewRecipe";
|
import NewRecipe from "@/pages/Recipe/NewRecipe";
|
||||||
|
import CustomPage from "@/pages/Recipes/CustomPage";
|
||||||
import AllRecipes from "@/pages/Recipes/AllRecipes";
|
import AllRecipes from "@/pages/Recipes/AllRecipes";
|
||||||
import CategoryPage from "@/pages/Recipes/CategoryPage";
|
import CategoryPage from "@/pages/Recipes/CategoryPage";
|
||||||
import Planner from "@/pages/MealPlan/Planner";
|
import Planner from "@/pages/MealPlan/Planner";
|
||||||
|
@ -31,6 +32,7 @@ export const routes = [
|
||||||
{ path: "/debug", component: Debug },
|
{ path: "/debug", component: Debug },
|
||||||
{ path: "/search", component: SearchPage },
|
{ path: "/search", component: SearchPage },
|
||||||
{ path: "/recipes/all", component: AllRecipes },
|
{ path: "/recipes/all", component: AllRecipes },
|
||||||
|
{ path: "/recipes/test-page", component: CustomPage },
|
||||||
{ path: "/recipes/:category", component: CategoryPage },
|
{ path: "/recipes/:category", component: CategoryPage },
|
||||||
{ path: "/recipe/:recipe", component: ViewRecipe },
|
{ path: "/recipe/:recipe", component: ViewRecipe },
|
||||||
{ path: "/new/", component: NewRecipe },
|
{ path: "/new/", component: NewRecipe },
|
||||||
|
|
|
@ -4,7 +4,6 @@ import api from "@/api";
|
||||||
import createPersistedState from "vuex-persistedstate";
|
import createPersistedState from "vuex-persistedstate";
|
||||||
import userSettings from "./modules/userSettings";
|
import userSettings from "./modules/userSettings";
|
||||||
import language from "./modules/language";
|
import language from "./modules/language";
|
||||||
import homePage from "./modules/homePage";
|
|
||||||
import siteSettings from "./modules/siteSettings";
|
import siteSettings from "./modules/siteSettings";
|
||||||
import groups from "./modules/groups";
|
import groups from "./modules/groups";
|
||||||
|
|
||||||
|
@ -13,13 +12,12 @@ Vue.use(Vuex);
|
||||||
const store = new Vuex.Store({
|
const store = new Vuex.Store({
|
||||||
plugins: [
|
plugins: [
|
||||||
createPersistedState({
|
createPersistedState({
|
||||||
paths: ["userSettings", "language", "homePage", "SideSettings"],
|
paths: ["userSettings", "language", "SideSettings"],
|
||||||
}),
|
}),
|
||||||
],
|
],
|
||||||
modules: {
|
modules: {
|
||||||
userSettings,
|
userSettings,
|
||||||
language,
|
language,
|
||||||
homePage,
|
|
||||||
siteSettings,
|
siteSettings,
|
||||||
groups,
|
groups,
|
||||||
},
|
},
|
||||||
|
|
|
@ -11,7 +11,7 @@ const state = {
|
||||||
|
|
||||||
const mutations = {
|
const mutations = {
|
||||||
setSettings(state, payload) {
|
setSettings(state, payload) {
|
||||||
state.settings = payload;
|
state.siteSettings = payload;
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -4,7 +4,8 @@ from fastapi.logger import logger
|
||||||
|
|
||||||
# import utils.startup as startup
|
# import utils.startup as startup
|
||||||
from mealie.core.config import APP_VERSION, PORT, docs_url, redoc_url
|
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.groups import groups
|
||||||
from mealie.routes.mealplans import mealplans
|
from mealie.routes.mealplans import mealplans
|
||||||
from mealie.routes.recipe import all_recipe_routes, category_routes, recipe_crud_routes, tag_routes
|
from mealie.routes.recipe import all_recipe_routes, category_routes, recipe_crud_routes, tag_routes
|
||||||
|
@ -36,7 +37,7 @@ def api_routers():
|
||||||
# Meal Routes
|
# Meal Routes
|
||||||
app.include_router(mealplans.router)
|
app.include_router(mealplans.router)
|
||||||
# Settings Routes
|
# Settings Routes
|
||||||
app.include_router(setting_routes.router)
|
app.include_router(all_settings.router)
|
||||||
app.include_router(theme_routes.router)
|
app.include_router(theme_routes.router)
|
||||||
# Backups/Imports Routes
|
# Backups/Imports Routes
|
||||||
app.include_router(backup_routes.router)
|
app.include_router(backup_routes.router)
|
||||||
|
|
|
@ -2,14 +2,14 @@ from mealie.db.db_base import BaseDocument
|
||||||
from mealie.db.models.group import Group
|
from mealie.db.models.group import Group
|
||||||
from mealie.db.models.mealplan import MealPlanModel
|
from mealie.db.models.mealplan import MealPlanModel
|
||||||
from mealie.db.models.recipe.recipe import Category, RecipeModel, Tag
|
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.sign_up import SignUp
|
||||||
from mealie.db.models.theme import SiteThemeModel
|
from mealie.db.models.theme import SiteThemeModel
|
||||||
from mealie.db.models.users import User
|
from mealie.db.models.users import User
|
||||||
from mealie.schema.category import RecipeCategoryResponse, RecipeTagResponse
|
from mealie.schema.category import RecipeCategoryResponse, RecipeTagResponse
|
||||||
from mealie.schema.meal import MealPlanInDB
|
from mealie.schema.meal import MealPlanInDB
|
||||||
from mealie.schema.recipe import Recipe
|
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.sign_up import SignUpOut
|
||||||
from mealie.schema.theme import SiteTheme
|
from mealie.schema.theme import SiteTheme
|
||||||
from mealie.schema.user import GroupInDB, UserInDB
|
from mealie.schema.user import GroupInDB, UserInDB
|
||||||
|
@ -118,6 +118,13 @@ class _SignUps(BaseDocument):
|
||||||
self.orm_mode = True
|
self.orm_mode = True
|
||||||
self.schema = SignUpOut
|
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:
|
class Database:
|
||||||
def __init__(self) -> None:
|
def __init__(self) -> None:
|
||||||
|
@ -130,6 +137,7 @@ class Database:
|
||||||
self.users = _Users()
|
self.users = _Users()
|
||||||
self.sign_ups = _SignUps()
|
self.sign_ups = _SignUps()
|
||||||
self.groups = _Groups()
|
self.groups = _Groups()
|
||||||
|
self.custom_pages = _CustomPages()
|
||||||
|
|
||||||
|
|
||||||
db = Database()
|
db = Database()
|
||||||
|
|
|
@ -26,6 +26,13 @@ recipes2categories = sa.Table(
|
||||||
sa.Column("category_slug", sa.String, sa.ForeignKey("categories.slug")),
|
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):
|
class Category(SqlAlchemyBase):
|
||||||
__tablename__ = "categories"
|
__tablename__ = "categories"
|
||||||
|
@ -36,7 +43,7 @@ class Category(SqlAlchemyBase):
|
||||||
|
|
||||||
@validates("name")
|
@validates("name")
|
||||||
def validate_name(self, key, name):
|
def validate_name(self, key, name):
|
||||||
assert not name == ""
|
assert name != ""
|
||||||
return name
|
return name
|
||||||
|
|
||||||
def __init__(self, name) -> None:
|
def __init__(self, name) -> None:
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
import sqlalchemy as sa
|
import sqlalchemy as sa
|
||||||
import sqlalchemy.orm as orm
|
import sqlalchemy.orm as orm
|
||||||
from mealie.db.models.model_base import BaseMixins, SqlAlchemyBase
|
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
|
from sqlalchemy.orm import Session
|
||||||
|
|
||||||
|
|
||||||
|
@ -29,7 +29,29 @@ class SiteSettings(SqlAlchemyBase, BaseMixins):
|
||||||
self.language = language
|
self.language = language
|
||||||
self.cards_per_section = cards_per_section
|
self.cards_per_section = cards_per_section
|
||||||
self.show_recent = show_recent
|
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):
|
def update(self, *args, **kwarg):
|
||||||
self.__init__(*args, **kwarg)
|
self.__init__(*args, **kwarg)
|
||||||
|
|
7
mealie/routes/site_settings/all_settings.py
Normal file
7
mealie/routes/site_settings/all_settings.py
Normal file
|
@ -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)
|
52
mealie/routes/site_settings/custom_pages.py
Normal file
52
mealie/routes/site_settings/custom_pages.py
Normal file
|
@ -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
|
|
@ -16,9 +16,7 @@ router = APIRouter(prefix="/api/site-settings", tags=["Settings"])
|
||||||
def get_main_settings(session: Session = Depends(generate_session)):
|
def get_main_settings(session: Session = Depends(generate_session)):
|
||||||
""" Returns basic site settings """
|
""" Returns basic site settings """
|
||||||
|
|
||||||
data = db.settings.get(session, 1)
|
return db.settings.get(session, 1)
|
||||||
|
|
||||||
return data
|
|
||||||
|
|
||||||
|
|
||||||
@router.put("")
|
@router.put("")
|
|
@ -101,10 +101,9 @@ class Recipe(BaseModel):
|
||||||
name: str = values["name"]
|
name: str = values["name"]
|
||||||
calc_slug: str = slugify(name)
|
calc_slug: str = slugify(name)
|
||||||
|
|
||||||
if slug == calc_slug:
|
if slug != calc_slug:
|
||||||
return slug
|
|
||||||
else:
|
|
||||||
slug = calc_slug
|
slug = calc_slug
|
||||||
|
|
||||||
return slug
|
return slug
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -1,8 +1,9 @@
|
||||||
from typing import Optional
|
from typing import Optional
|
||||||
|
|
||||||
from fastapi_camelcase import CamelModel
|
from fastapi_camelcase import CamelModel
|
||||||
|
|
||||||
from mealie.schema.category import CategoryBase
|
from mealie.schema.category import CategoryBase
|
||||||
|
from pydantic import validator
|
||||||
|
from slugify import slugify
|
||||||
|
|
||||||
|
|
||||||
class SiteSettings(CamelModel):
|
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
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue