mirror of
https://github.com/hay-kot/mealie.git
synced 2025-08-22 22:43:34 -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-title>
|
||||||
|
|
||||||
<v-card-actions>
|
<v-card-actions>
|
||||||
<v-rating
|
<Rating :value="rating" :name="name" :slug="slug" :small="true" />
|
||||||
class="mr-2 my-auto"
|
|
||||||
color="secondary"
|
|
||||||
background-color="secondary lighten-3"
|
|
||||||
dense
|
|
||||||
length="5"
|
|
||||||
size="15"
|
|
||||||
:value="rating"
|
|
||||||
></v-rating>
|
|
||||||
<v-spacer></v-spacer>
|
<v-spacer></v-spacer>
|
||||||
<RecipeChips
|
<RecipeChips
|
||||||
:items="tags"
|
:items="tags"
|
||||||
|
@ -44,6 +36,7 @@
|
||||||
:small="true"
|
:small="true"
|
||||||
:isCategory="false"
|
:isCategory="false"
|
||||||
/>
|
/>
|
||||||
|
<ContextMenu :slug="slug" />
|
||||||
</v-card-actions>
|
</v-card-actions>
|
||||||
</v-card>
|
</v-card>
|
||||||
</v-hover>
|
</v-hover>
|
||||||
|
@ -51,10 +44,14 @@
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import RecipeChips from "@/components/Recipe/RecipeViewer/RecipeChips";
|
import RecipeChips from "@/components/Recipe/RecipeViewer/RecipeChips";
|
||||||
|
import ContextMenu from "@/components/Recipe/ContextMenu";
|
||||||
|
import Rating from "@/components/Recipe/Parts/Rating";
|
||||||
import { api } from "@/api";
|
import { api } from "@/api";
|
||||||
export default {
|
export default {
|
||||||
components: {
|
components: {
|
||||||
RecipeChips,
|
RecipeChips,
|
||||||
|
ContextMenu,
|
||||||
|
Rating,
|
||||||
},
|
},
|
||||||
props: {
|
props: {
|
||||||
name: String,
|
name: String,
|
||||||
|
|
|
@ -59,13 +59,7 @@
|
||||||
</v-text-field>
|
</v-text-field>
|
||||||
</v-col>
|
</v-col>
|
||||||
<v-spacer></v-spacer>
|
<v-spacer></v-spacer>
|
||||||
<v-rating
|
<Rating v-model="value.rating" :emit-only="true" />
|
||||||
class="mr-2 align-end"
|
|
||||||
color="secondary darken-1"
|
|
||||||
background-color="secondary lighten-3"
|
|
||||||
length="5"
|
|
||||||
v-model="value.rating"
|
|
||||||
></v-rating>
|
|
||||||
</v-row>
|
</v-row>
|
||||||
<v-row>
|
<v-row>
|
||||||
<v-col cols="12" sm="12" md="4" lg="4">
|
<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 Assets from "@/components/Recipe/Parts/Assets.vue";
|
||||||
import Notes from "@/components/Recipe/Parts/Notes.vue";
|
import Notes from "@/components/Recipe/Parts/Notes.vue";
|
||||||
import SettingsMenu from "@/components/Recipe/Parts/Helpers/SettingsMenu.vue";
|
import SettingsMenu from "@/components/Recipe/Parts/Helpers/SettingsMenu.vue";
|
||||||
|
import Rating from "@/components/Recipe/Parts/Rating";
|
||||||
export default {
|
export default {
|
||||||
components: {
|
components: {
|
||||||
BulkAdd,
|
BulkAdd,
|
||||||
|
@ -140,6 +135,7 @@ export default {
|
||||||
Assets,
|
Assets,
|
||||||
Notes,
|
Notes,
|
||||||
SettingsMenu,
|
SettingsMenu,
|
||||||
|
Rating,
|
||||||
},
|
},
|
||||||
props: {
|
props: {
|
||||||
value: Object,
|
value: Object,
|
||||||
|
|
|
@ -21,13 +21,7 @@
|
||||||
{{ yields }}
|
{{ yields }}
|
||||||
</v-btn>
|
</v-btn>
|
||||||
</v-col>
|
</v-col>
|
||||||
<v-rating
|
<Rating :value="rating" :name="name" :slug="slug"/>
|
||||||
class="mr-2 align-end static"
|
|
||||||
color="secondary darken-1"
|
|
||||||
background-color="secondary lighten-3"
|
|
||||||
length="5"
|
|
||||||
:value="rating"
|
|
||||||
></v-rating>
|
|
||||||
</v-row>
|
</v-row>
|
||||||
<v-row>
|
<v-row>
|
||||||
<v-col cols="12" sm="12" md="4" lg="4">
|
<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 VueMarkdown from "@adapttive/vue-markdown";
|
||||||
import utils from "@/utils";
|
import utils from "@/utils";
|
||||||
import RecipeChips from "./RecipeChips";
|
import RecipeChips from "./RecipeChips";
|
||||||
|
import Rating from "@/components/Recipe/Parts/Rating";
|
||||||
import Notes from "@/components/Recipe/Parts/Notes";
|
import Notes from "@/components/Recipe/Parts/Notes";
|
||||||
import Ingredients from "@/components/Recipe/Parts/Ingredients";
|
import Ingredients from "@/components/Recipe/Parts/Ingredients";
|
||||||
import Instructions from "@/components/Recipe/Parts/Instructions.vue";
|
import Instructions from "@/components/Recipe/Parts/Instructions.vue";
|
||||||
|
@ -113,6 +108,7 @@ export default {
|
||||||
Nutrition,
|
Nutrition,
|
||||||
Instructions,
|
Instructions,
|
||||||
Assets,
|
Assets,
|
||||||
|
Rating,
|
||||||
},
|
},
|
||||||
props: {
|
props: {
|
||||||
name: String,
|
name: String,
|
||||||
|
|
|
@ -1,6 +1,5 @@
|
||||||
<template>
|
<template>
|
||||||
<v-container>
|
<v-container>
|
||||||
|
|
||||||
<CardSection
|
<CardSection
|
||||||
v-if="siteSettings.showRecent"
|
v-if="siteSettings.showRecent"
|
||||||
:title="$t('page.recent')"
|
:title="$t('page.recent')"
|
||||||
|
@ -38,8 +37,8 @@ export default {
|
||||||
return this.$store.getters.getSiteSettings;
|
return this.$store.getters.getSiteSettings;
|
||||||
},
|
},
|
||||||
recentRecipes() {
|
recentRecipes() {
|
||||||
|
console.log("Recent Recipes");
|
||||||
return this.$store.getters.getRecentRecipes;
|
return this.$store.getters.getRecentRecipes;
|
||||||
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
async mounted() {
|
async mounted() {
|
||||||
|
|
|
@ -80,7 +80,6 @@ import RecipeEditor from "@/components/Recipe/RecipeEditor";
|
||||||
import RecipeTimeCard from "@/components/Recipe/RecipeTimeCard.vue";
|
import RecipeTimeCard from "@/components/Recipe/RecipeTimeCard.vue";
|
||||||
import EditorButtonRow from "@/components/Recipe/EditorButtonRow";
|
import EditorButtonRow from "@/components/Recipe/EditorButtonRow";
|
||||||
import { user } from "@/mixins/user";
|
import { user } from "@/mixins/user";
|
||||||
import store from "@/store";
|
|
||||||
import { router } from "@/routes";
|
import { router } from "@/routes";
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
|
@ -171,7 +170,6 @@ export default {
|
||||||
async deleteRecipe() {
|
async deleteRecipe() {
|
||||||
let response = await api.recipes.delete(this.recipeDetails.slug);
|
let response = await api.recipes.delete(this.recipeDetails.slug);
|
||||||
if (response) {
|
if (response) {
|
||||||
store.dispatch("requestRecentRecipes");
|
|
||||||
router.push(`/`);
|
router.push(`/`);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
|
@ -115,7 +115,7 @@ class BaseDocument:
|
||||||
|
|
||||||
return self.schema.from_orm(new_document)
|
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.
|
"""Update a database entry.
|
||||||
Args: \n
|
Args: \n
|
||||||
session (Session): Database Session
|
session (Session): Database Session
|
||||||
|
@ -132,8 +132,24 @@ class BaseDocument:
|
||||||
session.commit()
|
session.commit()
|
||||||
return self.schema.from_orm(entry)
|
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:
|
def delete(self, session: Session, primary_key_value) -> dict:
|
||||||
result = session.query(self.sql_model).filter_by(**{self.primary_key: primary_key_value}).one()
|
result = session.query(self.sql_model).filter_by(**{self.primary_key: primary_key_value}).one()
|
||||||
|
|
||||||
session.delete(result)
|
session.delete(result)
|
||||||
session.commit()
|
session.commit()
|
||||||
|
|
||||||
|
return self.schema.from_orm(result)
|
||||||
|
|
|
@ -66,20 +66,13 @@ def update_recipe(
|
||||||
@router.patch("/{recipe_slug}")
|
@router.patch("/{recipe_slug}")
|
||||||
def patch_recipe(
|
def patch_recipe(
|
||||||
recipe_slug: str,
|
recipe_slug: str,
|
||||||
data: dict,
|
data: Recipe,
|
||||||
session: Session = Depends(generate_session),
|
session: Session = Depends(generate_session),
|
||||||
current_user=Depends(get_current_user),
|
current_user=Depends(get_current_user),
|
||||||
):
|
):
|
||||||
""" Updates a recipe by existing slug and data. """
|
""" Updates a recipe by existing slug and data. """
|
||||||
|
|
||||||
existing_entry: Recipe = db.recipes.get(session, recipe_slug)
|
recipe: Recipe = db.recipes.patch(session, recipe_slug, new_data=data.dict(exclude_unset=True, exclude_defaults=True))
|
||||||
|
|
||||||
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())
|
|
||||||
|
|
||||||
if recipe_slug != recipe.slug:
|
if recipe_slug != recipe.slug:
|
||||||
rename_image(original_slug=recipe_slug, new_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 """
|
""" Deletes a recipe by slug """
|
||||||
|
|
||||||
try:
|
try:
|
||||||
db.recipes.delete(session, recipe_slug)
|
delete_data = db.recipes.delete(session, recipe_slug)
|
||||||
delete_image(recipe_slug)
|
delete_image(recipe_slug)
|
||||||
|
|
||||||
|
return delete_data
|
||||||
except Exception:
|
except Exception:
|
||||||
raise HTTPException(status.HTTP_400_BAD_REQUEST)
|
raise HTTPException(status.HTTP_400_BAD_REQUEST)
|
||||||
|
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue