mirror of
https://github.com/hay-kot/mealie.git
synced 2025-08-22 14:33:33 -07:00
ui improvements + mealplanner search
This commit is contained in:
parent
12a53ed6fe
commit
4493894162
11 changed files with 93 additions and 42 deletions
|
@ -150,4 +150,5 @@ export default {
|
||||||
*::-webkit-scrollbar-thumb {
|
*::-webkit-scrollbar-thumb {
|
||||||
background: grey;
|
background: grey;
|
||||||
}
|
}
|
||||||
|
|
||||||
</style>
|
</style>
|
||||||
|
|
|
@ -8,13 +8,14 @@ const prefix = baseURL + "recipes/";
|
||||||
|
|
||||||
const recipeURLs = {
|
const recipeURLs = {
|
||||||
allRecipes: baseURL + "recipes",
|
allRecipes: baseURL + "recipes",
|
||||||
|
allRecipesByCategory: prefix + "category",
|
||||||
create: prefix + "create",
|
create: prefix + "create",
|
||||||
createByURL: prefix + "create-url",
|
createByURL: prefix + "create-url",
|
||||||
recipe: (slug) => prefix + slug,
|
recipe: slug => prefix + slug,
|
||||||
update: (slug) => prefix + slug,
|
update: slug => prefix + slug,
|
||||||
delete: (slug) => prefix + slug,
|
delete: slug => prefix + slug,
|
||||||
recipeImage: (slug) => `${prefix}${slug}/image`,
|
recipeImage: slug => `${prefix}${slug}/image`,
|
||||||
updateImage: (slug) => `${prefix}${slug}/image`,
|
updateImage: slug => `${prefix}${slug}/image`,
|
||||||
};
|
};
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
|
@ -27,6 +28,14 @@ export default {
|
||||||
return response;
|
return response;
|
||||||
},
|
},
|
||||||
|
|
||||||
|
async getAllByCategory(categories) {
|
||||||
|
let response = await apiReq.post(
|
||||||
|
recipeURLs.allRecipesByCategory,
|
||||||
|
categories
|
||||||
|
);
|
||||||
|
return response.data;
|
||||||
|
},
|
||||||
|
|
||||||
async create(recipeData) {
|
async create(recipeData) {
|
||||||
let response = await apiReq.post(recipeURLs.create, recipeData);
|
let response = await apiReq.post(recipeURLs.create, recipeData);
|
||||||
return response.data;
|
return response.data;
|
||||||
|
@ -67,7 +76,7 @@ export default {
|
||||||
keys: recipeKeys,
|
keys: recipeKeys,
|
||||||
num: num,
|
num: num,
|
||||||
},
|
},
|
||||||
paramsSerializer: (params) => {
|
paramsSerializer: params => {
|
||||||
return qs.stringify(params, { arrayFormat: "repeat" });
|
return qs.stringify(params, { arrayFormat: "repeat" });
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
|
@ -96,6 +96,7 @@ export default {
|
||||||
return {
|
return {
|
||||||
isLoading: false,
|
isLoading: false,
|
||||||
meals: [],
|
meals: [],
|
||||||
|
items: [],
|
||||||
|
|
||||||
// Dates
|
// Dates
|
||||||
startDate: null,
|
startDate: null,
|
||||||
|
@ -117,11 +118,13 @@ export default {
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
async mounted() {
|
||||||
|
let settings = await api.settings.requestAll();
|
||||||
|
console.log(settings);
|
||||||
|
this.items = await api.recipes.getAllByCategory(settings.planCategories);
|
||||||
|
},
|
||||||
|
|
||||||
computed: {
|
computed: {
|
||||||
items() {
|
|
||||||
return this.$store.getters.getRecentRecipes;
|
|
||||||
},
|
|
||||||
actualStartDate() {
|
actualStartDate() {
|
||||||
return Date.parse(this.startDate);
|
return Date.parse(this.startDate);
|
||||||
},
|
},
|
||||||
|
|
|
@ -6,7 +6,19 @@
|
||||||
:to="route ? `/recipe/${slug}` : ''"
|
:to="route ? `/recipe/${slug}` : ''"
|
||||||
@click="$emit('click')"
|
@click="$emit('click')"
|
||||||
>
|
>
|
||||||
<v-img height="200" :src="getImage(image)"></v-img>
|
<v-img height="200" :src="getImage(image)">
|
||||||
|
<v-expand-transition v-if="description">
|
||||||
|
<div
|
||||||
|
v-if="hover"
|
||||||
|
class="d-flex transition-fast-in-fast-out secondary v-card--reveal "
|
||||||
|
style="height: 100%;"
|
||||||
|
>
|
||||||
|
<v-card-text class="v-card--text-show white--text">
|
||||||
|
{{ description }}
|
||||||
|
</v-card-text>
|
||||||
|
</div>
|
||||||
|
</v-expand-transition>
|
||||||
|
</v-img>
|
||||||
<v-card-title class="my-n3 mb-n6">{{ name | truncate(30) }}</v-card-title>
|
<v-card-title class="my-n3 mb-n6">{{ name | truncate(30) }}</v-card-title>
|
||||||
|
|
||||||
<v-card-actions class="">
|
<v-card-actions class="">
|
||||||
|
@ -23,16 +35,7 @@
|
||||||
></v-rating>
|
></v-rating>
|
||||||
</v-col>
|
</v-col>
|
||||||
<v-col></v-col>
|
<v-col></v-col>
|
||||||
<v-col align="end">
|
<v-col align="end"> </v-col>
|
||||||
<v-tooltip top color="secondary" max-width="400" open-delay="50">
|
|
||||||
<template v-slot:activator="{ on, attrs }">
|
|
||||||
<v-btn color="secondary" v-on="on" v-bind="attrs" text>{{
|
|
||||||
$t("recipe.description")
|
|
||||||
}}</v-btn>
|
|
||||||
</template>
|
|
||||||
<span>{{ description }}</span>
|
|
||||||
</v-tooltip>
|
|
||||||
</v-col>
|
|
||||||
</v-row>
|
</v-row>
|
||||||
</v-card-actions>
|
</v-card-actions>
|
||||||
</v-card>
|
</v-card>
|
||||||
|
@ -61,4 +64,15 @@ export default {
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style>
|
<style>
|
||||||
|
.v-card--reveal {
|
||||||
|
align-items: center;
|
||||||
|
bottom: 0;
|
||||||
|
justify-content: center;
|
||||||
|
opacity: 0.8;
|
||||||
|
position: absolute;
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
.v-card--text-show {
|
||||||
|
opacity: 1 !important;
|
||||||
|
}
|
||||||
</style>
|
</style>
|
|
@ -57,6 +57,7 @@ export default {
|
||||||
return {
|
return {
|
||||||
searchSlug: "",
|
searchSlug: "",
|
||||||
search: " ",
|
search: " ",
|
||||||
|
data: [],
|
||||||
result: [],
|
result: [],
|
||||||
autoResults: [],
|
autoResults: [],
|
||||||
isDark: false,
|
isDark: false,
|
||||||
|
@ -67,27 +68,31 @@ export default {
|
||||||
distance: 100,
|
distance: 100,
|
||||||
maxPatternLength: 32,
|
maxPatternLength: 32,
|
||||||
minMatchCharLength: 1,
|
minMatchCharLength: 1,
|
||||||
keys: ["name", "slug"],
|
keys: ["name", "slug", "description"],
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
mounted() {
|
mounted() {
|
||||||
this.isDark = this.$store.getters.getIsDark;
|
this.isDark = this.$store.getters.getIsDark;
|
||||||
|
this.data = this.$store.getters.getRecentRecipes;
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
data() {
|
|
||||||
return this.$store.getters.getRecentRecipes;
|
|
||||||
},
|
|
||||||
fuse() {
|
fuse() {
|
||||||
return new Fuse(this.data, this.options);
|
return new Fuse(this.data, this.options);
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
watch: {
|
watch: {
|
||||||
search() {
|
search() {
|
||||||
if (this.search.trim() === "") this.result = this.list;
|
try {
|
||||||
else this.result = this.fuse.search(this.search.trim());
|
this.result = this.fuse.search(this.search.trim());
|
||||||
|
} catch {
|
||||||
|
this.result = this.data
|
||||||
|
.map(x => ({ item: x }))
|
||||||
|
.sort((a, b) => (a.name > b.name ? 1 : -1));
|
||||||
|
}
|
||||||
|
console.log(this.result);
|
||||||
this.$emit("results", this.result);
|
this.$emit("results", this.result);
|
||||||
|
|
||||||
if (this.showResults === true) {
|
if (this.showResults === true) {
|
||||||
this.autoResults = this.result;
|
this.autoResults = this.result;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
<template>
|
<template>
|
||||||
<div class="text-center">
|
<div class="text-center">
|
||||||
<v-dialog v-model="dialog" min-height="700" max-width="1000">
|
<v-dialog v-model="dialog" height="100%" max-width="1200">
|
||||||
<v-card min-height="725" height="100%">
|
<v-card min-height="725" height="100%">
|
||||||
<v-card-text>
|
<v-card-text>
|
||||||
<v-card-title></v-card-title>
|
<v-card-title></v-card-title>
|
||||||
|
@ -22,7 +22,7 @@
|
||||||
:md="6"
|
:md="6"
|
||||||
:lg="4"
|
:lg="4"
|
||||||
:xl="3"
|
:xl="3"
|
||||||
v-for="item in searchResults.slice(0, 10)"
|
v-for="item in searchResults.slice(0, 24)"
|
||||||
:key="item.item.name"
|
:key="item.item.name"
|
||||||
>
|
>
|
||||||
<RecipeCard
|
<RecipeCard
|
||||||
|
@ -58,6 +58,7 @@ export default {
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
updateResults(results) {
|
updateResults(results) {
|
||||||
|
console.log(results);
|
||||||
this.searchResults = results;
|
this.searchResults = results;
|
||||||
},
|
},
|
||||||
emitSelect(name, slug) {
|
emitSelect(name, slug) {
|
||||||
|
|
|
@ -15,7 +15,7 @@ const mutations = {
|
||||||
state.showLimit = payload;
|
state.showLimit = payload;
|
||||||
},
|
},
|
||||||
setCategories(state, payload) {
|
setCategories(state, payload) {
|
||||||
state.categories = payload;
|
state.categories = payload.sort((a, b) => (a.name > b.name ? 1 : -1));
|
||||||
},
|
},
|
||||||
setHomeCategories(state, payload) {
|
setHomeCategories(state, payload) {
|
||||||
state.homeCategories = payload;
|
state.homeCategories = payload;
|
||||||
|
|
|
@ -47,7 +47,6 @@ const store = new Vuex.Store({
|
||||||
},
|
},
|
||||||
|
|
||||||
setMealPlanCategories(state, payload) {
|
setMealPlanCategories(state, payload) {
|
||||||
console.log(payload);
|
|
||||||
state.mealPlanCategories = payload;
|
state.mealPlanCategories = payload;
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
|
@ -100,11 +100,17 @@ class BaseDocument:
|
||||||
match_key = self.primary_key
|
match_key = self.primary_key
|
||||||
|
|
||||||
result = (
|
result = (
|
||||||
session.query(self.sql_model).filter_by(**{match_key: match_value}).one()
|
session.query(self.sql_model)
|
||||||
|
.filter_by(**{match_key: match_value})
|
||||||
|
.limit(limit)
|
||||||
|
.all()
|
||||||
)
|
)
|
||||||
db_entry = result.dict()
|
db_entries = [x.dict() for x in result]
|
||||||
|
|
||||||
return db_entry
|
if limit == 1:
|
||||||
|
return db_entries[0]
|
||||||
|
|
||||||
|
return db_entries
|
||||||
|
|
||||||
def create(self, session: Session, document: dict) -> dict:
|
def create(self, session: Session, document: dict) -> dict:
|
||||||
"""Creates a new database entry for the given SQL Alchemy Model.
|
"""Creates a new database entry for the given SQL Alchemy Model.
|
||||||
|
|
|
@ -4,6 +4,7 @@ from db.database import db
|
||||||
from db.db_setup import generate_session
|
from db.db_setup import generate_session
|
||||||
from fastapi import APIRouter, Depends, Query
|
from fastapi import APIRouter, Depends, Query
|
||||||
from models.recipe_models import AllRecipeRequest
|
from models.recipe_models import AllRecipeRequest
|
||||||
|
from slugify import slugify
|
||||||
from sqlalchemy.orm.session import Session
|
from sqlalchemy.orm.session import Session
|
||||||
|
|
||||||
router = APIRouter(tags=["Query All Recipes"])
|
router = APIRouter(tags=["Query All Recipes"])
|
||||||
|
@ -71,13 +72,23 @@ def get_all_recipes_post(
|
||||||
return db.recipes.get_all_limit_columns(session, body.properties, body.limit)
|
return db.recipes.get_all_limit_columns(session, body.properties, body.limit)
|
||||||
|
|
||||||
|
|
||||||
@router.post("/api/category")
|
@router.post("/api/recipes/category")
|
||||||
async def filter_by_category(
|
def filter_by_category(categories: list, session: Session = Depends(generate_session)):
|
||||||
categories: list, session: Session = Depends(generate_session)
|
|
||||||
):
|
|
||||||
""" pass a list of categories and get a list of recipes associated with those categories """
|
""" pass a list of categories and get a list of recipes associated with those categories """
|
||||||
#! This should be refactored into a single database call, but I couldn't figure it out
|
#! This should be refactored into a single database call, but I couldn't figure it out
|
||||||
in_category = [db.categories.get(session, cat) for cat in categories]
|
in_category = [
|
||||||
|
db.categories.get(session, slugify(cat), limit=1) for cat in categories
|
||||||
|
]
|
||||||
in_category = [cat.get("recipes") for cat in in_category]
|
in_category = [cat.get("recipes") for cat in in_category]
|
||||||
in_category = [item for sublist in in_category for item in sublist]
|
in_category = [item for sublist in in_category for item in sublist]
|
||||||
return in_category
|
return in_category
|
||||||
|
|
||||||
|
|
||||||
|
@router.post("/api/recipes/tag")
|
||||||
|
async def filter_by_tags(tags: list, session: Session = Depends(generate_session)):
|
||||||
|
""" pass a list of tags and get a list of recipes associated with those tags"""
|
||||||
|
#! This should be refactored into a single database call, but I couldn't figure it out
|
||||||
|
in_tags = [db.tags.get(session, slugify(tag), limit=1) for tag in tags]
|
||||||
|
in_tags = [tag.get("recipes") for tag in in_tags]
|
||||||
|
in_tags = [item for sublist in in_tags for item in sublist]
|
||||||
|
return in_tags
|
||||||
|
|
|
@ -69,8 +69,10 @@ def delete_recipe(recipe_slug: str, db: Session = Depends(generate_session)):
|
||||||
async def get_recipe_img(recipe_slug: str):
|
async def get_recipe_img(recipe_slug: str):
|
||||||
""" Takes in a recipe slug, returns the static image """
|
""" Takes in a recipe slug, returns the static image """
|
||||||
recipe_image = read_image(recipe_slug)
|
recipe_image = read_image(recipe_slug)
|
||||||
|
if recipe_image:
|
||||||
return FileResponse(recipe_image)
|
return FileResponse(recipe_image)
|
||||||
|
else:
|
||||||
|
return
|
||||||
|
|
||||||
|
|
||||||
@router.put("/{recipe_slug}/image")
|
@router.put("/{recipe_slug}/image")
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue