Allow to edit a recipe
This commit is contained in:
parent
da844e0a08
commit
d30e5f9053
29
cuizin/db.py
29
cuizin/db.py
@ -40,7 +40,7 @@ class Recipe(Model):
|
|||||||
# Author
|
# Author
|
||||||
author = CharField(null=True)
|
author = CharField(null=True)
|
||||||
# Picture as a binary blob
|
# Picture as a binary blob
|
||||||
picture = BlobField(null=True)
|
picture = TextField(null=True)
|
||||||
# Short description
|
# Short description
|
||||||
short_description = TextField(null=True)
|
short_description = TextField(null=True)
|
||||||
# Number of persons as text, as it can be either "N persons" or "N parts"
|
# Number of persons as text, as it can be either "N persons" or "N parts"
|
||||||
@ -69,7 +69,18 @@ class Recipe(Model):
|
|||||||
setattr(self, field, value)
|
setattr(self, field, value)
|
||||||
# Download picture and save it as a blob
|
# Download picture and save it as a blob
|
||||||
if d.get('picture_url', None):
|
if d.get('picture_url', None):
|
||||||
self.picture = requests.get(d['picture_url']).content
|
try:
|
||||||
|
picture = requests.get(d['picture_url']).content
|
||||||
|
picture_mime = (
|
||||||
|
'data:%s;base64' % magic.from_buffer(picture,
|
||||||
|
mime=True)
|
||||||
|
)
|
||||||
|
self.picture = '%s,%s' % (
|
||||||
|
picture_mime,
|
||||||
|
base64.b64encode(picture).decode('utf-8')
|
||||||
|
)
|
||||||
|
except requests.exceptions.InvalidSchema:
|
||||||
|
self.picture = d['picture_url']
|
||||||
|
|
||||||
def update_from_weboob(self, weboob_obj):
|
def update_from_weboob(self, weboob_obj):
|
||||||
"""
|
"""
|
||||||
@ -86,16 +97,4 @@ class Recipe(Model):
|
|||||||
"""
|
"""
|
||||||
Dict conversion function, for serialization in the API.
|
Dict conversion function, for serialization in the API.
|
||||||
"""
|
"""
|
||||||
serialized = model_to_dict(self)
|
return model_to_dict(self)
|
||||||
# Dump picture as a base64 string, compatible with HTML `src` attribute
|
|
||||||
# for images.
|
|
||||||
if serialized['picture']:
|
|
||||||
picture_mime = (
|
|
||||||
'data:%s;base64' % magic.from_buffer(serialized['picture'],
|
|
||||||
mime=True)
|
|
||||||
)
|
|
||||||
serialized['picture'] = '%s,%s' % (
|
|
||||||
picture_mime,
|
|
||||||
base64.b64encode(serialized['picture']).decode('utf-8')
|
|
||||||
)
|
|
||||||
return serialized
|
|
||||||
|
@ -66,6 +66,15 @@ export function postRecipeManually(recipe) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
export function editRecipe(id, recipe) {
|
||||||
|
return fetch(`${constants.API_URL}api/v1/recipe/${id}`, {
|
||||||
|
method: 'POST',
|
||||||
|
body: JSON.stringify(recipe),
|
||||||
|
})
|
||||||
|
.then(_postProcessRecipes);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
export function deleteRecipe(id) {
|
export function deleteRecipe(id) {
|
||||||
return fetch(`${constants.API_URL}api/v1/recipe/${id}`, {
|
return fetch(`${constants.API_URL}api/v1/recipe/${id}`, {
|
||||||
method: 'DELETE',
|
method: 'DELETE',
|
||||||
|
@ -1,7 +1,11 @@
|
|||||||
<template>
|
<template>
|
||||||
<v-flex xs12>
|
<v-flex xs12>
|
||||||
<h2>{{ $t('new.add_manually') }}</h2>
|
<ErrorDialog :v-model="error" :description="$t('error.title')" />
|
||||||
<v-form v-model="isValidAdd">
|
|
||||||
|
<h2 v-if="recipe">{{ $t('new.edit_recipe') }}</h2>
|
||||||
|
<h2 v-else>{{ $t('new.add_manually') }}</h2>
|
||||||
|
|
||||||
|
<v-form v-model="isValidForm">
|
||||||
<v-text-field
|
<v-text-field
|
||||||
:label="$t('new.title')"
|
:label="$t('new.title')"
|
||||||
v-model="title"
|
v-model="title"
|
||||||
@ -13,6 +17,9 @@
|
|||||||
v-model="picture_url"
|
v-model="picture_url"
|
||||||
:rules="urlRules"
|
:rules="urlRules"
|
||||||
></v-text-field>
|
></v-text-field>
|
||||||
|
<p>
|
||||||
|
<img :src="picture_url" />
|
||||||
|
</p>
|
||||||
<v-text-field
|
<v-text-field
|
||||||
:label="$t('new.short_description')"
|
:label="$t('new.short_description')"
|
||||||
v-model="short_description"
|
v-model="short_description"
|
||||||
@ -71,9 +78,17 @@
|
|||||||
required
|
required
|
||||||
></v-text-field>
|
></v-text-field>
|
||||||
|
|
||||||
|
<v-btn
|
||||||
|
@click="submitEdit"
|
||||||
|
:disabled="!isValidForm || isImporting"
|
||||||
|
v-if="recipe"
|
||||||
|
>
|
||||||
|
{{ $t('new.edit') }}
|
||||||
|
</v-btn>
|
||||||
<v-btn
|
<v-btn
|
||||||
@click="submitAdd"
|
@click="submitAdd"
|
||||||
:disabled="!isValidAdd || isImporting"
|
:disabled="!isValidForm || isImporting"
|
||||||
|
v-else
|
||||||
>
|
>
|
||||||
{{ $t('new.add') }}
|
{{ $t('new.add') }}
|
||||||
</v-btn>
|
</v-btn>
|
||||||
@ -85,8 +100,17 @@
|
|||||||
import * as api from '@/api';
|
import * as api from '@/api';
|
||||||
import * as rules from '@/rules';
|
import * as rules from '@/rules';
|
||||||
|
|
||||||
|
import ErrorDialog from '@/components/ErrorDialog';
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
|
components: {
|
||||||
|
ErrorDialog,
|
||||||
|
},
|
||||||
props: {
|
props: {
|
||||||
|
recipe: {
|
||||||
|
default: null,
|
||||||
|
type: Object,
|
||||||
|
},
|
||||||
value: Boolean,
|
value: Boolean,
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
@ -100,18 +124,31 @@ export default {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
data() {
|
data() {
|
||||||
|
let defaultPreparationTime = null;
|
||||||
|
if (this.recipe &&
|
||||||
|
this.recipe.preparation_time !== null && this.recipe.preparation_time !== undefined
|
||||||
|
) {
|
||||||
|
defaultPreparationTime = this.recipe.preparation_time;
|
||||||
|
}
|
||||||
|
let defaultCookingTime = null;
|
||||||
|
if (this.recipe &&
|
||||||
|
this.recipe.cooking_time !== null && this.recipe.cooking_time !== undefined
|
||||||
|
) {
|
||||||
|
defaultCookingTime = this.recipe.cooking_time;
|
||||||
|
}
|
||||||
return {
|
return {
|
||||||
|
error: null,
|
||||||
url: null,
|
url: null,
|
||||||
isValidAdd: false,
|
isValidForm: false,
|
||||||
title: null,
|
title: (this.recipe && this.recipe.title) || null,
|
||||||
picture_url: null,
|
picture_url: (this.recipe && this.recipe.picture) || null,
|
||||||
short_description: null,
|
short_description: (this.recipe && this.recipe.short_description) || null,
|
||||||
nb_person: null,
|
nb_person: (this.recipe && this.recipe.nb_person) || null,
|
||||||
preparation_time: null,
|
preparation_time: defaultPreparationTime,
|
||||||
cooking_time: null,
|
cooking_time: defaultCookingTime,
|
||||||
new_ingredient: null,
|
new_ingredient: null,
|
||||||
ingredients: [],
|
ingredients: (this.recipe && this.recipe.ingredients) || [],
|
||||||
instructions: null,
|
instructions: (this.recipe && this.recipe.instructions.join('\n\n').replace(/\n{2,}/, '\n\n')) || null,
|
||||||
urlRules: rules.url,
|
urlRules: rules.url,
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
@ -149,6 +186,29 @@ export default {
|
|||||||
this.error = error;
|
this.error = error;
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
submitEdit() {
|
||||||
|
this.isImporting = true;
|
||||||
|
api.editRecipe(this.recipe.id, {
|
||||||
|
title: this.title,
|
||||||
|
picture_url: this.picture_url,
|
||||||
|
short_description: this.short_description,
|
||||||
|
preparation_time: this.preparation_time,
|
||||||
|
cooking_time: this.cooking_time,
|
||||||
|
nb_person: this.nb_person,
|
||||||
|
ingredients: this.ingredients,
|
||||||
|
instructions: this.instructions,
|
||||||
|
})
|
||||||
|
.then(response => this.$router.push({
|
||||||
|
name: 'Recipe',
|
||||||
|
params: {
|
||||||
|
recipeId: response.recipes[0].id,
|
||||||
|
},
|
||||||
|
}))
|
||||||
|
.catch((error) => {
|
||||||
|
this.isImporting = false;
|
||||||
|
this.error = error;
|
||||||
|
});
|
||||||
|
},
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
@ -157,4 +217,8 @@ export default {
|
|||||||
.transparent {
|
.transparent {
|
||||||
background: transparent;
|
background: transparent;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
img {
|
||||||
|
max-height: 150px;
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
|
@ -1,8 +1,11 @@
|
|||||||
export default {
|
export default {
|
||||||
error: {
|
error: {
|
||||||
title: 'Error',
|
title: 'Error',
|
||||||
|
unable_delete_recipe: 'Unable to delete recipe: ',
|
||||||
|
unable_fetch_recipe: 'Unable to fetch recipe: ',
|
||||||
unable_import_recipe: 'Unable to import recipe: ',
|
unable_import_recipe: 'Unable to import recipe: ',
|
||||||
unable_load_recipes: 'Unable to load recipes: ',
|
unable_load_recipes: 'Unable to load recipes: ',
|
||||||
|
unable_refetch_recipe: 'Unable to refetch recipe: ',
|
||||||
},
|
},
|
||||||
home: {
|
home: {
|
||||||
onboarding: 'Start by adding a recipe with the "+" button on the top right corner!',
|
onboarding: 'Start by adding a recipe with the "+" button on the top right corner!',
|
||||||
@ -16,6 +19,8 @@ export default {
|
|||||||
add_ingredient: 'Add ingredient',
|
add_ingredient: 'Add ingredient',
|
||||||
add_manually: 'Add manually',
|
add_manually: 'Add manually',
|
||||||
cooking_time: 'Cooking time',
|
cooking_time: 'Cooking time',
|
||||||
|
edit: 'Edit',
|
||||||
|
edit_recipe: 'Edit recipe',
|
||||||
import: 'Import',
|
import: 'Import',
|
||||||
import_from_url: 'Import from URL',
|
import_from_url: 'Import from URL',
|
||||||
importing: 'Importing…',
|
importing: 'Importing…',
|
||||||
@ -30,6 +35,7 @@ export default {
|
|||||||
short_description: 'Short description',
|
short_description: 'Short description',
|
||||||
title: 'Title',
|
title: 'Title',
|
||||||
title_is_required: 'Title is required',
|
title_is_required: 'Title is required',
|
||||||
|
updating: 'Updating…',
|
||||||
url_is_required: 'URL is required',
|
url_is_required: 'URL is required',
|
||||||
url_must_be_valid: 'URL must be a valid one',
|
url_must_be_valid: 'URL must be a valid one',
|
||||||
},
|
},
|
||||||
@ -38,6 +44,7 @@ export default {
|
|||||||
delete: 'Delete',
|
delete: 'Delete',
|
||||||
delete_recipe: 'Delete recipe',
|
delete_recipe: 'Delete recipe',
|
||||||
delete_recipe_description: 'This will delete this recipe. Are you sure?',
|
delete_recipe_description: 'This will delete this recipe. Are you sure?',
|
||||||
|
edit: 'Edit',
|
||||||
ingredients: 'Ingredients',
|
ingredients: 'Ingredients',
|
||||||
instructions: 'Instructions',
|
instructions: 'Instructions',
|
||||||
none: 'Aucun',
|
none: 'Aucun',
|
||||||
|
@ -1,8 +1,11 @@
|
|||||||
export default {
|
export default {
|
||||||
error: {
|
error: {
|
||||||
title: 'Erreur',
|
title: 'Erreur',
|
||||||
|
unable_delete_recipe: 'Impossible de supprimer la recette : ',
|
||||||
|
unable_fetch_recipe: 'Impossible de récupérer la recette : ',
|
||||||
unable_import_recipe: 'Impossible d\'importer la recette : ',
|
unable_import_recipe: 'Impossible d\'importer la recette : ',
|
||||||
unable_load_recipes: 'Impossible de charger les recettes : ',
|
unable_load_recipes: 'Impossible de charger les recettes : ',
|
||||||
|
unable_refetch_recipe: 'Impossible de réactualiser la recette : ',
|
||||||
},
|
},
|
||||||
home: {
|
home: {
|
||||||
onboarding: 'Commencez par ajouter une recette avec le bouton "+" dans le coin supérieur droit !',
|
onboarding: 'Commencez par ajouter une recette avec le bouton "+" dans le coin supérieur droit !',
|
||||||
@ -16,6 +19,8 @@ export default {
|
|||||||
add_ingredient: 'Ajouter un ingrédient',
|
add_ingredient: 'Ajouter un ingrédient',
|
||||||
add_manually: 'Ajouter manuellement',
|
add_manually: 'Ajouter manuellement',
|
||||||
cooking_time: 'Temps de cuisson',
|
cooking_time: 'Temps de cuisson',
|
||||||
|
edit: 'Modifier',
|
||||||
|
edit_recipe: 'Modifier une recette',
|
||||||
import: 'Importer',
|
import: 'Importer',
|
||||||
import_from_url: 'Importer depuis l\'URL',
|
import_from_url: 'Importer depuis l\'URL',
|
||||||
importing: 'En cours d\'import…',
|
importing: 'En cours d\'import…',
|
||||||
@ -30,6 +35,7 @@ export default {
|
|||||||
short_description: 'Description brève',
|
short_description: 'Description brève',
|
||||||
title: 'Titre',
|
title: 'Titre',
|
||||||
title_is_required: 'Un titre est requis',
|
title_is_required: 'Un titre est requis',
|
||||||
|
updating: 'En cours de mise à jour…',
|
||||||
url_is_required: 'Une URL est requise',
|
url_is_required: 'Une URL est requise',
|
||||||
url_must_be_valid: 'L\'URL est invalide',
|
url_must_be_valid: 'L\'URL est invalide',
|
||||||
},
|
},
|
||||||
@ -38,6 +44,7 @@ export default {
|
|||||||
delete: 'Supprimer',
|
delete: 'Supprimer',
|
||||||
delete_recipe: 'Suppression d\'une recette',
|
delete_recipe: 'Suppression d\'une recette',
|
||||||
delete_recipe_description: 'Vous allez supprimer cette recette. En êtes-vous sûr ?',
|
delete_recipe_description: 'Vous allez supprimer cette recette. En êtes-vous sûr ?',
|
||||||
|
edit: 'Modifier',
|
||||||
ingredients: 'Ingrédients',
|
ingredients: 'Ingrédients',
|
||||||
instructions: 'Instructions',
|
instructions: 'Instructions',
|
||||||
none: 'Aucun',
|
none: 'Aucun',
|
||||||
|
@ -1,6 +1,8 @@
|
|||||||
import Vue from 'vue';
|
import Vue from 'vue';
|
||||||
import Router from 'vue-router';
|
import Router from 'vue-router';
|
||||||
|
|
||||||
import Home from '@/views/Home';
|
import Home from '@/views/Home';
|
||||||
|
import Edit from '@/views/Edit';
|
||||||
import New from '@/views/New';
|
import New from '@/views/New';
|
||||||
import Recipe from '@/views/Recipe';
|
import Recipe from '@/views/Recipe';
|
||||||
|
|
||||||
@ -23,5 +25,10 @@ export default new Router({
|
|||||||
name: 'Recipe',
|
name: 'Recipe',
|
||||||
component: Recipe,
|
component: Recipe,
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
path: '/edit/:recipeId',
|
||||||
|
name: 'Edit',
|
||||||
|
component: Edit,
|
||||||
|
},
|
||||||
],
|
],
|
||||||
});
|
});
|
||||||
|
72
cuizin/js_src/views/Edit.vue
Normal file
72
cuizin/js_src/views/Edit.vue
Normal file
@ -0,0 +1,72 @@
|
|||||||
|
<template>
|
||||||
|
<v-container text-xs-center v-if="isLoading">
|
||||||
|
<v-layout row>
|
||||||
|
<Loader></Loader>
|
||||||
|
</v-layout>
|
||||||
|
</v-container>
|
||||||
|
<v-container text-xs-center v-else-if="isImporting">
|
||||||
|
<v-layout row wrap>
|
||||||
|
<Loader></Loader>
|
||||||
|
|
||||||
|
<v-flex xs12>
|
||||||
|
<p>{{ $t('new.updating') }}</p>
|
||||||
|
</v-flex>
|
||||||
|
</v-layout>
|
||||||
|
</v-container>
|
||||||
|
<v-container text-xs-center v-else class="panel">
|
||||||
|
<v-layout row wrap>
|
||||||
|
<EditForm v-model="isImporting" :recipe="recipe"></EditForm>
|
||||||
|
</v-layout>
|
||||||
|
</v-container>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import * as api from '@/api';
|
||||||
|
|
||||||
|
import EditForm from '@/components/EditForm';
|
||||||
|
import Loader from '@/components/Loader';
|
||||||
|
|
||||||
|
|
||||||
|
export default {
|
||||||
|
components: {
|
||||||
|
EditForm,
|
||||||
|
Loader,
|
||||||
|
},
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
isImporting: false,
|
||||||
|
isLoading: false,
|
||||||
|
error: null,
|
||||||
|
recipe: null,
|
||||||
|
};
|
||||||
|
},
|
||||||
|
created() {
|
||||||
|
this.loadRecipe();
|
||||||
|
},
|
||||||
|
watch: {
|
||||||
|
// call again the method if the route changes
|
||||||
|
$route: 'loadRecipe',
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
handleRecipesResponse(response) {
|
||||||
|
if (response.recipes.length < 1) {
|
||||||
|
this.$router.replace({
|
||||||
|
name: 'Home',
|
||||||
|
});
|
||||||
|
}
|
||||||
|
this.recipe = response.recipes[0];
|
||||||
|
this.isLoading = false;
|
||||||
|
},
|
||||||
|
loadRecipe() {
|
||||||
|
this.isLoading = true;
|
||||||
|
|
||||||
|
api.loadRecipe(this.$route.params.recipeId)
|
||||||
|
.then(this.handleRecipesResponse)
|
||||||
|
.catch((error) => {
|
||||||
|
this.isLoading = false;
|
||||||
|
this.error = error;
|
||||||
|
});
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
</script>
|
@ -18,13 +18,15 @@
|
|||||||
<h3 class="headline mb-0">{{ recipe.title }}</h3>
|
<h3 class="headline mb-0">{{ recipe.title }}</h3>
|
||||||
</div>
|
</div>
|
||||||
</v-card-title>
|
</v-card-title>
|
||||||
|
<v-layout row text-xs-center wrap>
|
||||||
|
<v-flex xs12 v-if="recipe.short_description">
|
||||||
<p>{{ recipe.short_description }}</p>
|
<p>{{ recipe.short_description }}</p>
|
||||||
<v-layout row text-xs-center>
|
|
||||||
<v-flex xs6>
|
|
||||||
<p><v-icon>timelapse</v-icon> {{ $tc('misc.Nmins', recipe.preparation_time, { count: recipe.preparation_time ? recipe.preparation_time : '?' }) }}</p>
|
|
||||||
</v-flex>
|
</v-flex>
|
||||||
<v-flex xs6>
|
<v-flex xs6>
|
||||||
<p><v-icon>whatshot</v-icon> {{ $tc('misc.Nmins', recipe.cooking_time, { count: recipe.cooking_time ? recipe.cooking_time : '?' }) }}</p>
|
<p><v-icon>timelapse</v-icon> {{ $tc('misc.Nmins', recipe.preparation_time, { count: timeOrUnknown(recipe.preparation_time) }) }}</p>
|
||||||
|
</v-flex>
|
||||||
|
<v-flex xs6>
|
||||||
|
<p><v-icon>whatshot</v-icon> {{ $tc('misc.Nmins', recipe.cooking_time, { count: timeOrUnknown(recipe.cooking_time) }) }}</p>
|
||||||
</v-flex>
|
</v-flex>
|
||||||
</v-layout>
|
</v-layout>
|
||||||
</v-card>
|
</v-card>
|
||||||
@ -72,6 +74,12 @@ export default {
|
|||||||
this.isLoading = false;
|
this.isLoading = false;
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
timeOrUnknown(time) {
|
||||||
|
if (time !== null && time !== undefined) {
|
||||||
|
return time;
|
||||||
|
}
|
||||||
|
return '?';
|
||||||
|
},
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
|
@ -2,9 +2,9 @@
|
|||||||
<v-container grid-list-md class="panel">
|
<v-container grid-list-md class="panel">
|
||||||
<Loader v-if="isLoading"></Loader>
|
<Loader v-if="isLoading"></Loader>
|
||||||
<v-layout row v-else>
|
<v-layout row v-else>
|
||||||
<ErrorDialog :v-model="errorDelete" description="Unable to delete recipe: " />
|
<ErrorDialog :v-model="errorDelete" :description="$t('error.unable_delete_recipe')" />
|
||||||
<ErrorDialog :v-model="errorFetch" description="Unable to fetch recipe: " />
|
<ErrorDialog :v-model="errorFetch" :description="$t('error.unable_fetch_recipe')" />
|
||||||
<ErrorDialog :v-model="errorRefetch" description="Unable to refetch recipe: " />
|
<ErrorDialog :v-model="errorRefetch" :description="$t('error.unable_refetch_recipe')" />
|
||||||
|
|
||||||
<v-dialog v-model="refetchConfirm" max-width="500px">
|
<v-dialog v-model="refetchConfirm" max-width="500px">
|
||||||
<v-card>
|
<v-card>
|
||||||
@ -46,8 +46,8 @@
|
|||||||
<p>{{ recipe.nb_person }}</p>
|
<p>{{ recipe.nb_person }}</p>
|
||||||
</v-flex>
|
</v-flex>
|
||||||
<v-flex xs6>
|
<v-flex xs6>
|
||||||
<p><v-icon>timelapse</v-icon> {{ $t('recipe.preparation') }} {{ $tc('misc.Nmins', recipe.preparation_time, { count: recipe.preparation_time ? recipe.preparation_time : '?' }) }}</p>
|
<p><v-icon>timelapse</v-icon> {{ $t('recipe.preparation') }} {{ $tc('misc.Nmins', recipe.preparation_time, { count: timeOrUnknown(recipe.preparation_time) }) }}</p>
|
||||||
<p><v-icon>whatshot</v-icon> {{ $t('recipe.cooking') }} {{ $tc('misc.Nmins', recipe.cooking_time, { count: recipe.cooking_time ? recipe.cooking_time : '?' }) }}</p>
|
<p><v-icon>whatshot</v-icon> {{ $t('recipe.cooking') }} {{ $tc('misc.Nmins', recipe.cooking_time, { count: timeOrUnknown(recipe.cooking_time) }) }}</p>
|
||||||
</v-flex>
|
</v-flex>
|
||||||
</v-layout>
|
</v-layout>
|
||||||
<p>{{ recipe.short_description }}</p>
|
<p>{{ recipe.short_description }}</p>
|
||||||
@ -68,6 +68,9 @@
|
|||||||
<v-btn :href="recipe.url" :title="$t('recipe.website')" v-if="recipe.url">
|
<v-btn :href="recipe.url" :title="$t('recipe.website')" v-if="recipe.url">
|
||||||
<v-icon class="fa-icon">fa-external-link</v-icon>
|
<v-icon class="fa-icon">fa-external-link</v-icon>
|
||||||
</v-btn>
|
</v-btn>
|
||||||
|
<v-btn :to="{name: 'Edit', params: { recipeId: recipe.id }}" :title="$t('recipe.edit')">
|
||||||
|
<v-icon>edit</v-icon>
|
||||||
|
</v-btn>
|
||||||
<v-btn @click.stop="deleteConfirm = true" :title="$t('recipe.delete')">
|
<v-btn @click.stop="deleteConfirm = true" :title="$t('recipe.delete')">
|
||||||
<v-icon>delete</v-icon>
|
<v-icon>delete</v-icon>
|
||||||
</v-btn>
|
</v-btn>
|
||||||
@ -110,6 +113,12 @@ export default {
|
|||||||
$route: 'loadRecipe',
|
$route: 'loadRecipe',
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
|
timeOrUnknown(time) {
|
||||||
|
if (time !== null && time !== undefined) {
|
||||||
|
return time;
|
||||||
|
}
|
||||||
|
return '?';
|
||||||
|
},
|
||||||
handleRecipesResponse(response) {
|
handleRecipesResponse(response) {
|
||||||
if (response.recipes.length < 1) {
|
if (response.recipes.length < 1) {
|
||||||
this.$router.replace({
|
this.$router.replace({
|
||||||
|
@ -119,6 +119,32 @@ def api_v1_recipe(id):
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@app.route('/api/v1/recipe/:id', ['POST', 'OPTIONS'])
|
||||||
|
def api_v1_recipe_edit(id):
|
||||||
|
"""
|
||||||
|
Edit a given recipe from db
|
||||||
|
"""
|
||||||
|
# CORS
|
||||||
|
if bottle.request.method == 'OPTIONS':
|
||||||
|
return ''
|
||||||
|
|
||||||
|
data = json.loads(bottle.request.body.read().decode('utf-8'))
|
||||||
|
|
||||||
|
recipe = db.Recipe.select().where(
|
||||||
|
db.Recipe.id == id
|
||||||
|
).first()
|
||||||
|
if not recipe:
|
||||||
|
return bottle.abort(400, 'No recipe with id %s.' % id)
|
||||||
|
recipe.update_from_dict(data)
|
||||||
|
recipe.save()
|
||||||
|
|
||||||
|
return {
|
||||||
|
'recipes': [
|
||||||
|
recipe.to_dict()
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
@app.route('/api/v1/recipe/:id/refetch', ['GET', 'OPTIONS'])
|
@app.route('/api/v1/recipe/:id/refetch', ['GET', 'OPTIONS'])
|
||||||
def api_v1_recipe_refetch(id):
|
def api_v1_recipe_refetch(id):
|
||||||
"""
|
"""
|
||||||
|
Loading…
Reference in New Issue
Block a user