mirror of
https://github.com/hay-kot/mealie.git
synced 2025-08-22 14:33:33 -07:00
recipe partial updates - closes #25
This commit is contained in:
parent
701f05e758
commit
6fdeb95ca3
8 changed files with 110 additions and 38 deletions
75
frontend/src/components/Recipe/ContextMenu.vue
Normal file
75
frontend/src/components/Recipe/ContextMenu.vue
Normal file
|
@ -0,0 +1,75 @@
|
|||
<template>
|
||||
<div class="text-center" v-if="loggedIn">
|
||||
<v-menu offset-y top left>
|
||||
<template v-slot:activator="{ on, attrs }">
|
||||
<v-btn
|
||||
:loading="loading"
|
||||
color="primary"
|
||||
icon
|
||||
dark
|
||||
v-bind="attrs"
|
||||
v-on="on"
|
||||
@click.prevent
|
||||
>
|
||||
<v-icon>mdi-dots-vertical</v-icon>
|
||||
</v-btn>
|
||||
</template>
|
||||
<v-list dense>
|
||||
<v-list-item
|
||||
v-for="(item, index) in items"
|
||||
:key="index"
|
||||
@click="menuAction(item.action)"
|
||||
>
|
||||
<v-list-item-icon>
|
||||
<v-icon v-text="item.icon" :color="item.color"></v-icon>
|
||||
</v-list-item-icon>
|
||||
<v-list-item-title>{{ item.title }}</v-list-item-title>
|
||||
</v-list-item>
|
||||
</v-list>
|
||||
</v-menu>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { api } from "@/api";
|
||||
export default {
|
||||
props: {
|
||||
slug: {
|
||||
type: String,
|
||||
},
|
||||
},
|
||||
computed: {
|
||||
loggedIn() {
|
||||
return this.$store.getters.getIsLoggedIn;
|
||||
},
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
items: [
|
||||
{
|
||||
title: "Delete",
|
||||
icon: "mdi-delete",
|
||||
color: "error",
|
||||
action: "delete",
|
||||
},
|
||||
],
|
||||
loading: false,
|
||||
};
|
||||
},
|
||||
methods: {
|
||||
async menuAction(action) {
|
||||
this.loading = true;
|
||||
|
||||
switch (action) {
|
||||
case "delete":
|
||||
await api.recipes.delete(this.slug);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
this.loading = false;
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
|
@ -27,15 +27,7 @@
|
|||
</v-card-title>
|
||||
|
||||
<v-card-actions>
|
||||
<v-rating
|
||||
class="mr-2 my-auto"
|
||||
color="secondary"
|
||||
background-color="secondary lighten-3"
|
||||
dense
|
||||
length="5"
|
||||
size="15"
|
||||
:value="rating"
|
||||
></v-rating>
|
||||
<Rating :value="rating" :name="name" :slug="slug" :small="true" />
|
||||
<v-spacer></v-spacer>
|
||||
<RecipeChips
|
||||
:items="tags"
|
||||
|
@ -44,6 +36,7 @@
|
|||
:small="true"
|
||||
:isCategory="false"
|
||||
/>
|
||||
<ContextMenu :slug="slug" />
|
||||
</v-card-actions>
|
||||
</v-card>
|
||||
</v-hover>
|
||||
|
@ -51,10 +44,14 @@
|
|||
|
||||
<script>
|
||||
import RecipeChips from "@/components/Recipe/RecipeViewer/RecipeChips";
|
||||
import ContextMenu from "@/components/Recipe/ContextMenu";
|
||||
import Rating from "@/components/Recipe/Parts/Rating";
|
||||
import { api } from "@/api";
|
||||
export default {
|
||||
components: {
|
||||
RecipeChips,
|
||||
ContextMenu,
|
||||
Rating,
|
||||
},
|
||||
props: {
|
||||
name: String,
|
||||
|
|
|
@ -59,13 +59,7 @@
|
|||
</v-text-field>
|
||||
</v-col>
|
||||
<v-spacer></v-spacer>
|
||||
<v-rating
|
||||
class="mr-2 align-end"
|
||||
color="secondary darken-1"
|
||||
background-color="secondary lighten-3"
|
||||
length="5"
|
||||
v-model="value.rating"
|
||||
></v-rating>
|
||||
<Rating v-model="value.rating" :emit-only="true" />
|
||||
</v-row>
|
||||
<v-row>
|
||||
<v-col cols="12" sm="12" md="4" lg="4">
|
||||
|
@ -128,6 +122,7 @@ import Ingredients from "@/components/Recipe/Parts/Ingredients";
|
|||
import Assets from "@/components/Recipe/Parts/Assets.vue";
|
||||
import Notes from "@/components/Recipe/Parts/Notes.vue";
|
||||
import SettingsMenu from "@/components/Recipe/Parts/Helpers/SettingsMenu.vue";
|
||||
import Rating from "@/components/Recipe/Parts/Rating";
|
||||
export default {
|
||||
components: {
|
||||
BulkAdd,
|
||||
|
@ -140,6 +135,7 @@ export default {
|
|||
Assets,
|
||||
Notes,
|
||||
SettingsMenu,
|
||||
Rating,
|
||||
},
|
||||
props: {
|
||||
value: Object,
|
||||
|
|
|
@ -21,13 +21,7 @@
|
|||
{{ yields }}
|
||||
</v-btn>
|
||||
</v-col>
|
||||
<v-rating
|
||||
class="mr-2 align-end static"
|
||||
color="secondary darken-1"
|
||||
background-color="secondary lighten-3"
|
||||
length="5"
|
||||
:value="rating"
|
||||
></v-rating>
|
||||
<Rating :value="rating" :name="name" :slug="slug"/>
|
||||
</v-row>
|
||||
<v-row>
|
||||
<v-col cols="12" sm="12" md="4" lg="4">
|
||||
|
@ -100,6 +94,7 @@ import Nutrition from "@/components/Recipe/Parts/Nutrition";
|
|||
import VueMarkdown from "@adapttive/vue-markdown";
|
||||
import utils from "@/utils";
|
||||
import RecipeChips from "./RecipeChips";
|
||||
import Rating from "@/components/Recipe/Parts/Rating";
|
||||
import Notes from "@/components/Recipe/Parts/Notes";
|
||||
import Ingredients from "@/components/Recipe/Parts/Ingredients";
|
||||
import Instructions from "@/components/Recipe/Parts/Instructions.vue";
|
||||
|
@ -113,6 +108,7 @@ export default {
|
|||
Nutrition,
|
||||
Instructions,
|
||||
Assets,
|
||||
Rating,
|
||||
},
|
||||
props: {
|
||||
name: String,
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
<template>
|
||||
<v-container>
|
||||
|
||||
<CardSection
|
||||
v-if="siteSettings.showRecent"
|
||||
:title="$t('page.recent')"
|
||||
|
@ -38,8 +37,8 @@ export default {
|
|||
return this.$store.getters.getSiteSettings;
|
||||
},
|
||||
recentRecipes() {
|
||||
console.log("Recent Recipes");
|
||||
return this.$store.getters.getRecentRecipes;
|
||||
|
||||
},
|
||||
},
|
||||
async mounted() {
|
||||
|
|
|
@ -80,7 +80,6 @@ import RecipeEditor from "@/components/Recipe/RecipeEditor";
|
|||
import RecipeTimeCard from "@/components/Recipe/RecipeTimeCard.vue";
|
||||
import EditorButtonRow from "@/components/Recipe/EditorButtonRow";
|
||||
import { user } from "@/mixins/user";
|
||||
import store from "@/store";
|
||||
import { router } from "@/routes";
|
||||
|
||||
export default {
|
||||
|
@ -171,7 +170,6 @@ export default {
|
|||
async deleteRecipe() {
|
||||
let response = await api.recipes.delete(this.recipeDetails.slug);
|
||||
if (response) {
|
||||
store.dispatch("requestRecentRecipes");
|
||||
router.push(`/`);
|
||||
}
|
||||
},
|
||||
|
|
|
@ -115,7 +115,7 @@ class BaseDocument:
|
|||
|
||||
return self.schema.from_orm(new_document)
|
||||
|
||||
def update(self, session: Session, match_value: str, new_data: str) -> BaseModel:
|
||||
def update(self, session: Session, match_value: str, new_data: dict) -> BaseModel:
|
||||
"""Update a database entry.
|
||||
Args: \n
|
||||
session (Session): Database Session
|
||||
|
@ -132,8 +132,24 @@ class BaseDocument:
|
|||
session.commit()
|
||||
return self.schema.from_orm(entry)
|
||||
|
||||
def patch(self, session: Session, match_value: str, new_data: dict) -> BaseModel:
|
||||
entry = self._query_one(session=session, match_value=match_value)
|
||||
|
||||
if not entry:
|
||||
return
|
||||
|
||||
entry_as_dict = self.schema.from_orm(entry).dict()
|
||||
entry_as_dict.update(new_data)
|
||||
|
||||
return self.update(session, match_value, entry_as_dict)
|
||||
|
||||
|
||||
|
||||
|
||||
def delete(self, session: Session, primary_key_value) -> dict:
|
||||
result = session.query(self.sql_model).filter_by(**{self.primary_key: primary_key_value}).one()
|
||||
|
||||
session.delete(result)
|
||||
session.commit()
|
||||
|
||||
return self.schema.from_orm(result)
|
||||
|
|
|
@ -66,20 +66,13 @@ def update_recipe(
|
|||
@router.patch("/{recipe_slug}")
|
||||
def patch_recipe(
|
||||
recipe_slug: str,
|
||||
data: dict,
|
||||
data: Recipe,
|
||||
session: Session = Depends(generate_session),
|
||||
current_user=Depends(get_current_user),
|
||||
):
|
||||
""" Updates a recipe by existing slug and data. """
|
||||
|
||||
existing_entry: Recipe = db.recipes.get(session, recipe_slug)
|
||||
|
||||
entry_dict = existing_entry.dict()
|
||||
entry_dict.update(data)
|
||||
updated_entry = Recipe(**entry_dict) # ! Surely there's a better way?
|
||||
|
||||
recipe: Recipe = db.recipes.update(session, recipe_slug, updated_entry.dict())
|
||||
|
||||
recipe: Recipe = db.recipes.patch(session, recipe_slug, new_data=data.dict(exclude_unset=True, exclude_defaults=True))
|
||||
if recipe_slug != recipe.slug:
|
||||
rename_image(original_slug=recipe_slug, new_slug=recipe.slug)
|
||||
|
||||
|
@ -95,8 +88,10 @@ def delete_recipe(
|
|||
""" Deletes a recipe by slug """
|
||||
|
||||
try:
|
||||
db.recipes.delete(session, recipe_slug)
|
||||
delete_data = db.recipes.delete(session, recipe_slug)
|
||||
delete_image(recipe_slug)
|
||||
|
||||
return delete_data
|
||||
except Exception:
|
||||
raise HTTPException(status.HTTP_400_BAD_REQUEST)
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue