mirror of
https://github.com/TandoorRecipes/recipes.git
synced 2026-01-06 14:48:02 -05:00
editor improvements
This commit is contained in:
@@ -1,11 +1,11 @@
|
|||||||
{% load django_vite %}
|
{% load django_vite %}
|
||||||
|
|
||||||
|
<!DOCTYPE html>
|
||||||
<html>
|
<html>
|
||||||
<head>
|
<head>
|
||||||
<title></title>
|
<title>Tandoor</title>
|
||||||
<meta charset="utf-8">
|
<meta charset="utf-8">
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no, viewport-fit=cover">
|
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=5, minimal-ui, shrink-to-fit=no">
|
||||||
<meta name="robots" content="noindex,nofollow"/>
|
<meta name="robots" content="noindex,nofollow"/>
|
||||||
<meta name="apple-mobile-web-app-capable" content="yes"/>
|
<meta name="apple-mobile-web-app-capable" content="yes"/>
|
||||||
|
|
||||||
@@ -15,7 +15,7 @@
|
|||||||
|
|
||||||
{% vite_hmr_client %}
|
{% vite_hmr_client %}
|
||||||
{% vite_asset 'src/apps/tandoor/main.ts' %}
|
{% vite_asset 'src/apps/tandoor/main.ts' %}
|
||||||
<script src="{% url 'js_reverse' %}"></script>
|
|
||||||
|
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|||||||
@@ -17,7 +17,7 @@
|
|||||||
"vue": "^3.4.15",
|
"vue": "^3.4.15",
|
||||||
"vue-router": "4",
|
"vue-router": "4",
|
||||||
"vuedraggable": "^4.1.0",
|
"vuedraggable": "^4.1.0",
|
||||||
"vuetify": "^3.3.15"
|
"vuetify": "^3.5.8"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@fortawesome/fontawesome-free": "^6.5.1",
|
"@fortawesome/fontawesome-free": "^6.5.1",
|
||||||
|
|||||||
@@ -1,21 +1,18 @@
|
|||||||
<template>
|
<template>
|
||||||
<v-app>
|
<v-app >
|
||||||
|
|
||||||
|
|
||||||
<v-app-bar color="tandoor" flat density="comfortable">
|
<v-app-bar color="tandoor" flat density="comfortable">
|
||||||
<router-link :to="{name: 'view_search', params: {}}">
|
<router-link :to="{name: 'view_search', params: {}}">
|
||||||
<v-img src="../../assets/brand_logo.svg" width="140px" class="ms-2"></v-img>
|
<v-img src="../../assets/brand_logo.svg" width="140px" class="ms-2"></v-img>
|
||||||
</router-link>
|
</router-link>
|
||||||
<global-search-dialog></global-search-dialog>
|
<global-search-dialog></global-search-dialog>
|
||||||
<v-spacer></v-spacer>
|
<v-spacer></v-spacer>
|
||||||
<!-- <v-btn density="compact" icon="fas fa-ellipsis-v"></v-btn>-->
|
<!-- <v-btn density="compact" icon="fas fa-ellipsis-v"></v-btn>-->
|
||||||
</v-app-bar>
|
</v-app-bar>
|
||||||
|
|
||||||
|
|
||||||
<v-main>
|
<v-main>
|
||||||
|
|
||||||
<router-view></router-view>
|
<router-view></router-view>
|
||||||
|
|
||||||
</v-main>
|
</v-main>
|
||||||
|
|
||||||
<v-bottom-navigation grow>
|
<v-bottom-navigation grow>
|
||||||
@@ -58,6 +55,7 @@ export default defineComponent({
|
|||||||
return {
|
return {
|
||||||
drawer: true,
|
drawer: true,
|
||||||
rail: true,
|
rail: true,
|
||||||
|
overlay: false
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
mounted() {
|
mounted() {
|
||||||
|
|||||||
@@ -1,20 +1,9 @@
|
|||||||
<template>
|
<template>
|
||||||
<v-table density="compact" v-if="ingredients.length > 0">
|
<v-table density="compact" v-if="ingredients.length > 0">
|
||||||
|
|
||||||
<template v-if="draggable">
|
<tbody>
|
||||||
<draggable tag="tbody" v-model="mutable_ingredients" handle=".drag-handle" item-key="id">
|
<IngredientsTableRow v-for="i in ingredients" :ingredient="i" :key="i.id" :show-notes="showNotes" :draggable="draggable"></IngredientsTableRow>
|
||||||
<template #item="{element}">
|
</tbody>
|
||||||
<IngredientsTableRow :ingredient="element" :key="element.id" :show-notes="showNotes" :draggable="draggable"></IngredientsTableRow>
|
|
||||||
</template>
|
|
||||||
</draggable>
|
|
||||||
</template>
|
|
||||||
<template v-else>
|
|
||||||
<tbody>
|
|
||||||
<!-- TODO make into one condition so there is no duplicate code possibly by disabling dragging when not enabled?! -->
|
|
||||||
<IngredientsTableRow v-for="i in ingredients" :ingredient="i" :key="i.id" :show-notes="showNotes" :draggable="draggable"></IngredientsTableRow>
|
|
||||||
</tbody>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
|
|
||||||
</v-table>
|
</v-table>
|
||||||
</template>
|
</template>
|
||||||
|
|||||||
@@ -19,29 +19,63 @@
|
|||||||
<v-chip><i class="fas fa-plus-circle"></i> Time</v-chip>
|
<v-chip><i class="fas fa-plus-circle"></i> Time</v-chip>
|
||||||
</v-chip-group>
|
</v-chip-group>
|
||||||
|
|
||||||
<ingredients-table :ingredients="step.ingredients" :show-notes="false" draggable>
|
<v-table density="compact">
|
||||||
|
|
||||||
</ingredients-table>
|
<draggable tag="tbody" v-model="step.ingredients" handle=".drag-handle" item-key="id" @sort="sortIngredients">
|
||||||
|
<template #item="{element}">
|
||||||
|
<v-dialog>
|
||||||
|
<template v-slot:activator="{ props: activatorProps }">
|
||||||
|
<IngredientsTableRow v-bind="activatorProps" :ingredient="element" :key="element.id" :show-notes="false" :draggable="true"></IngredientsTableRow>
|
||||||
|
</template>
|
||||||
|
<template v-slot:default="{ isActive }">
|
||||||
|
<v-card >
|
||||||
|
<v-card-title>Ingredient</v-card-title>
|
||||||
|
<v-card-text>
|
||||||
|
<v-form>
|
||||||
|
<v-text-field
|
||||||
|
label="Amount"
|
||||||
|
v-model="element.amount"
|
||||||
|
></v-text-field>
|
||||||
|
</v-form>
|
||||||
|
|
||||||
<v-alert @click="dialog_markdown_edit = true">
|
</v-card-text>
|
||||||
|
</v-card>
|
||||||
|
</template>
|
||||||
|
</v-dialog>
|
||||||
|
|
||||||
|
</template>
|
||||||
|
</draggable>
|
||||||
|
|
||||||
|
</v-table>
|
||||||
|
|
||||||
|
<v-alert @click="dialog_markdown_edit = true" class="mt-2">
|
||||||
{{ step.instruction }}
|
{{ step.instruction }}
|
||||||
</v-alert>
|
</v-alert>
|
||||||
|
|
||||||
</v-card-text>
|
</v-card-text>
|
||||||
</v-card>
|
</v-card>
|
||||||
|
|
||||||
|
<v-dialog
|
||||||
|
v-model="dialog_markdown_edit"
|
||||||
|
transition="dialog-bottom-transition">
|
||||||
|
<v-card>
|
||||||
|
<v-card-title>Ingredient</v-card-title>
|
||||||
|
<v-form>
|
||||||
|
<v-text-field></v-text-field>
|
||||||
|
</v-form>
|
||||||
|
</v-card>
|
||||||
|
</v-dialog>
|
||||||
|
|
||||||
<v-dialog
|
<v-dialog
|
||||||
v-model="dialog_markdown_edit"
|
v-model="dialog_markdown_edit"
|
||||||
transition="dialog-bottom-transition"
|
transition="dialog-bottom-transition"
|
||||||
fullscreen
|
fullscreen>
|
||||||
@close="$emit('change', {step: step})">
|
|
||||||
<v-card>
|
<v-card>
|
||||||
<v-toolbar>
|
<v-toolbar>
|
||||||
<v-toolbar-title>Edit Instructions</v-toolbar-title>
|
<v-toolbar-title>Edit Instructions</v-toolbar-title>
|
||||||
<v-btn icon="fas fa-close" @click="dialog_markdown_edit = false"></v-btn>
|
<v-btn icon="fas fa-close" @click="dialog_markdown_edit = false"></v-btn>
|
||||||
</v-toolbar>
|
</v-toolbar>
|
||||||
<step-markdown-editor class="h-100" :step="step" @change="step = $event.step; $emit('change', {step: step})"></step-markdown-editor>
|
<step-markdown-editor class="h-100" :step="step" @change="step = $event.step;"></step-markdown-editor>
|
||||||
</v-card>
|
</v-card>
|
||||||
</v-dialog>
|
</v-dialog>
|
||||||
</template>
|
</template>
|
||||||
@@ -51,17 +85,15 @@ import {defineComponent, PropType} from 'vue'
|
|||||||
import {Step} from "@/openapi";
|
import {Step} from "@/openapi";
|
||||||
import StepMarkdownEditor from "@/components/inputs/StepMarkdownEditor.vue";
|
import StepMarkdownEditor from "@/components/inputs/StepMarkdownEditor.vue";
|
||||||
import IngredientsTable from "@/components/display/IngredientsTable.vue";
|
import IngredientsTable from "@/components/display/IngredientsTable.vue";
|
||||||
|
import IngredientsTableRow from "@/components/display/IngredientsTableRow.vue";
|
||||||
|
import draggable from "vuedraggable";
|
||||||
|
|
||||||
export default defineComponent({
|
export default defineComponent({
|
||||||
name: "StepEditor",
|
name: "StepEditor",
|
||||||
components: {IngredientsTable, StepMarkdownEditor},
|
components: {draggable, IngredientsTableRow, IngredientsTable, StepMarkdownEditor},
|
||||||
emits: {
|
emits: ['update:modelValue'],
|
||||||
change(payload: { step: Step }) {
|
|
||||||
return payload
|
|
||||||
}
|
|
||||||
},
|
|
||||||
props: {
|
props: {
|
||||||
step: {
|
modelValue: {
|
||||||
type: Object as PropType<Step>,
|
type: Object as PropType<Step>,
|
||||||
required: true,
|
required: true,
|
||||||
},
|
},
|
||||||
@@ -70,10 +102,27 @@ export default defineComponent({
|
|||||||
required: false,
|
required: false,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
computed: {
|
||||||
|
step: {
|
||||||
|
get() {
|
||||||
|
return this.modelValue
|
||||||
|
},
|
||||||
|
set(value: Step) {
|
||||||
|
this.$emit('update:modelValue', value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
dialog_markdown_edit: false,
|
dialog_markdown_edit: false,
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
sortIngredients() {
|
||||||
|
this.step.ingredients.forEach((value, index) => {
|
||||||
|
value.order = index
|
||||||
|
})
|
||||||
|
}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
@@ -1,70 +1,83 @@
|
|||||||
<template>
|
<template>
|
||||||
<v-container>
|
<v-container>
|
||||||
|
<v-row>
|
||||||
|
<v-col>
|
||||||
|
<v-card>
|
||||||
|
<v-card-title>{{ recipe.name }}</v-card-title>
|
||||||
|
|
||||||
<v-form>
|
<v-card-text>
|
||||||
<v-text-field
|
|
||||||
label="Name"
|
|
||||||
v-model="recipe.name"
|
|
||||||
></v-text-field>
|
|
||||||
|
|
||||||
<v-textarea
|
|
||||||
label="Description"
|
|
||||||
v-model="recipe.description"
|
|
||||||
clearable
|
|
||||||
></v-textarea>
|
|
||||||
|
|
||||||
<v-combobox
|
<v-form>
|
||||||
label="Keywords"
|
<v-text-field
|
||||||
v-model="recipe.keywords"
|
label="Name"
|
||||||
:items="keywords"
|
v-model="recipe.name"
|
||||||
item-title="name"
|
></v-text-field>
|
||||||
multiple
|
|
||||||
clearable
|
|
||||||
chips
|
|
||||||
></v-combobox>
|
|
||||||
|
|
||||||
<v-row>
|
<v-textarea
|
||||||
<v-col>
|
label="Description"
|
||||||
<v-text-field
|
v-model="recipe.description"
|
||||||
v-model.number="recipe.waitingTime"
|
clearable
|
||||||
label="Waiting Time (min)"
|
></v-textarea>
|
||||||
></v-text-field>
|
|
||||||
</v-col>
|
|
||||||
<v-col>
|
|
||||||
<v-text-field
|
|
||||||
v-model.number="recipe.workingTime"
|
|
||||||
label="Working Time (min)"
|
|
||||||
></v-text-field>
|
|
||||||
</v-col>
|
|
||||||
</v-row>
|
|
||||||
|
|
||||||
<v-row>
|
<v-combobox
|
||||||
<v-col>
|
label="Keywords"
|
||||||
<v-text-field
|
v-model="recipe.keywords"
|
||||||
v-model.number="recipe.servings"
|
:items="keywords"
|
||||||
label="Servings"
|
item-title="name"
|
||||||
></v-text-field>
|
multiple
|
||||||
</v-col>
|
clearable
|
||||||
<v-col>
|
chips
|
||||||
<v-text-field
|
></v-combobox>
|
||||||
v-model="recipe.servingsText"
|
|
||||||
label="Servings Text"
|
|
||||||
></v-text-field>
|
|
||||||
</v-col>
|
|
||||||
</v-row>
|
|
||||||
|
|
||||||
<v-row v-for="(step, index) in recipe.steps">
|
<v-row>
|
||||||
<v-col>
|
<v-col>
|
||||||
<step-editor :step="step" :step-index="index"></step-editor>
|
<v-text-field
|
||||||
</v-col>
|
v-model.number="recipe.waitingTime"
|
||||||
</v-row>
|
label="Waiting Time (min)"
|
||||||
|
></v-text-field>
|
||||||
|
</v-col>
|
||||||
|
<v-col>
|
||||||
|
<v-text-field
|
||||||
|
v-model.number="recipe.workingTime"
|
||||||
|
label="Working Time (min)"
|
||||||
|
></v-text-field>
|
||||||
|
</v-col>
|
||||||
|
</v-row>
|
||||||
|
|
||||||
</v-form>
|
<v-row>
|
||||||
|
<v-col>
|
||||||
|
<v-text-field
|
||||||
|
v-model.number="recipe.servings"
|
||||||
|
label="Servings"
|
||||||
|
></v-text-field>
|
||||||
|
</v-col>
|
||||||
|
<v-col>
|
||||||
|
<v-text-field
|
||||||
|
v-model="recipe.servingsText"
|
||||||
|
label="Servings Text"
|
||||||
|
></v-text-field>
|
||||||
|
</v-col>
|
||||||
|
</v-row>
|
||||||
|
|
||||||
<v-btn @click="updateRecipe()">Save</v-btn>
|
|
||||||
<v-btn :to="{name: 'view_recipe', params: {id: recipe_id}}">View</v-btn>
|
</v-form>
|
||||||
|
</v-card-text>
|
||||||
|
</v-card>
|
||||||
|
</v-col>
|
||||||
|
</v-row>
|
||||||
|
|
||||||
|
|
||||||
|
<v-row v-for="(step, index) in recipe.steps" class="mt-1">
|
||||||
|
<v-col>
|
||||||
|
<step-editor v-model="recipe.steps[index]" :step-index="index"></step-editor>
|
||||||
|
</v-col>
|
||||||
|
</v-row>
|
||||||
</v-container>
|
</v-container>
|
||||||
|
|
||||||
|
<v-btn @click="updateRecipe()">Save</v-btn>
|
||||||
|
<v-btn :to="{name: 'view_recipe', params: {id: recipe_id}}">View</v-btn>
|
||||||
|
|
||||||
|
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
@@ -96,13 +109,13 @@ export default defineComponent({
|
|||||||
this.refreshRecipe()
|
this.refreshRecipe()
|
||||||
|
|
||||||
const api = new ApiApi()
|
const api = new ApiApi()
|
||||||
api.apiKeywordList().then(r => {
|
api.apiKeywordList({page: 1, pageSize: 100}).then(r => {
|
||||||
this.keywords = r.results
|
this.keywords = r.results
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
refreshRecipe() {
|
refreshRecipe() {
|
||||||
if (this.recipe.id != this.recipe_id) {
|
if (this.recipe.id != Number(this.recipe_id)) {
|
||||||
const api = new ApiApi()
|
const api = new ApiApi()
|
||||||
api.apiRecipeRetrieve({id: Number(this.recipe_id)}).then(r => {
|
api.apiRecipeRetrieve({id: Number(this.recipe_id)}).then(r => {
|
||||||
this.recipe = r
|
this.recipe = r
|
||||||
@@ -111,7 +124,7 @@ export default defineComponent({
|
|||||||
},
|
},
|
||||||
updateRecipe() {
|
updateRecipe() {
|
||||||
const api = new ApiApi()
|
const api = new ApiApi()
|
||||||
api.apiRecipeUpdate({id: this.recipe_id, recipe: this.recipe}).then(r => {
|
api.apiRecipeUpdate({id: Number(this.recipe_id), recipe: this.recipe}).then(r => {
|
||||||
this.recipe = r
|
this.recipe = r
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,7 +3,7 @@
|
|||||||
|
|
||||||
<!--TODO ideas for "start page": new recipes, meal plan, "last year/month/cooked long ago", high rated, random keyword -->
|
<!--TODO ideas for "start page": new recipes, meal plan, "last year/month/cooked long ago", high rated, random keyword -->
|
||||||
<horizontal-recipe-scroller title="New Recipes" :recipes="new_recipes"></horizontal-recipe-scroller>
|
<horizontal-recipe-scroller title="New Recipes" :recipes="new_recipes"></horizontal-recipe-scroller>
|
||||||
<horizontal-recipe-scroller title="Top Rated" :recipes="high_rated_recipes"></horizontal-recipe-scroller>
|
<horizontal-recipe-scroller title="Top Rated" :recipes="high_rated_recipes" ></horizontal-recipe-scroller>
|
||||||
|
|
||||||
|
|
||||||
</v-container>
|
</v-container>
|
||||||
|
|||||||
@@ -904,10 +904,10 @@ vuedraggable@^4.1.0:
|
|||||||
dependencies:
|
dependencies:
|
||||||
sortablejs "1.14.0"
|
sortablejs "1.14.0"
|
||||||
|
|
||||||
vuetify@^3.3.15:
|
vuetify@^3.5.8:
|
||||||
version "3.5.4"
|
version "3.5.8"
|
||||||
resolved "https://registry.yarnpkg.com/vuetify/-/vuetify-3.5.4.tgz#f919c5194995a123815c277a95812bc230e33464"
|
resolved "https://registry.yarnpkg.com/vuetify/-/vuetify-3.5.8.tgz#bc8f08dfd3314640e7b5d43b50138a26d650cbbf"
|
||||||
integrity sha512-fHgfWMI7+z/UtbVPOezX+O1MNBOOMBW9HnKejcBIyQQ7jFRnTHbDQmbINf25FK0wrg/zkjfzyOmWWREKW39eXg==
|
integrity sha512-8nGS+lKejZkev55HFwIfsRt+9fOqbeDQNmXxfmLKAlnUT8FtynVwbjAwHMtX/OQAQ3ZwRaR1ptqQQmx3OgxzbQ==
|
||||||
|
|
||||||
w3c-xmlserializer@^4.0.0:
|
w3c-xmlserializer@^4.0.0:
|
||||||
version "4.0.0"
|
version "4.0.0"
|
||||||
|
|||||||
Reference in New Issue
Block a user