TandoorRecipes/cookbook/templates/url_import.html
2021-04-16 09:02:01 -05:00

867 lines
47 KiB
HTML

{% extends "base.html" %}
{% load i18n %}
{% load static %}
{% block title %}{% trans 'URL Import' %}{% endblock %}
{% block extra_head %}
{% include 'include/vue_base.html' %}
<script src="{% static 'js/jquery-3.5.1.min.js' %}"></script>
<script src="{% static 'js/vue-jstree.js' %}"></script>
<script src="{% static 'js/vue-multiselect.min.js' %}"></script>
<link rel="stylesheet" href="{% static 'css/vue-multiselect.min.css' %}">
<style>
.tree-anchor {
width:90%;
}
</style>
{% endblock %}
{% block content %}
<div id="app">
<h2> {% trans 'Import' %} </h2>
<nav class="nav nav-pills flex-sm-row" style="margin-bottom:10px">
<a class="nav-link active" href="#nav-url" data-toggle="tab" role="tab" aria-controls="nav-url" aria-selected="true">URL</a>
<a class="nav-link" href="#nav-ldjson" data-toggle="tab" role="tab" aria-controls="nav-ldjson">ld+json</a>
<a class="nav-link" href="#nav-json" data-toggle="tab" role="tab" aria-controls="nav-json">json</a>
<a class="nav-link disabled" href="#nav-html" data-toggle="tab" role="tab" aria-controls="nav-html">HTML</a>
<a class="nav-link disabled" href="#nav-text" data-toggle="tab" role="tab" aria-controls="nav-text">text</a>
<a class="nav-link disabled" href="#nav-pdf" data-toggle="tab" role="tab" aria-controls="nav-pdf">PDF</a>
</nav>
<div class="tab-content" id="nav-tabContent">
<!-- Import URL -->
<div class="row tab-pane fade show active" id="nav-url" role="tabpanel">
<div class="col-md-12">
<div class="input-group mb-3">
<input class="form-control" v-model="remote_url" placeholder="{% trans 'Enter website URL' %}">
<div class="input-group-append">
<button @click="loadRecipe()" class="btn btn-primary shadow-none" type="button"
id="id_btn_search"><i class="fas fa-search"></i>
</button>
</div>
</div>
</div>
</div>
<!-- Automatically import LD+JSON -->
<div class="row tab-pane fade show" id="nav-ldjson" role="tabpanel">
<div class="col-md-12">
<div class="input-group input-group-lg">
<textarea class="form-control input-group-append" v-model="raw_data" rows=10 placeholder="{% trans 'Paste ld+json here to parse recipe automatically.' %}" style="font-size: 12px">
</textarea>
</div>
<br>
<button @click="loadRecipeJson()" class="btn btn-primary shadow-none" type="button"
id="id_btn_json"><i class="fas fa-code"></i> {% trans 'Import' %}
</button>
</div>
</div>
<!-- Manually import from JSON -->
<div class="row tab-pane fade show" id="nav-json" role="tabpanel">
<div class="col-md-12">
<div class="input-group input-group-lg">
<textarea class="form-control input-group-append" v-model="raw_data" rows=10
placeholder="{% trans 'To parse recipe manually: Paste JSON document here or a web page source that contains one or more JSON elements here.' %}" style="font-size: 12px">
</textarea>
</div>
<br>
<button @click="loadPreviewRaw()" class="btn btn-primary shadow-none" type="button"
id="id_btn_raw"><i class="fas fa-code"></i> {% trans 'Preview Import' %}
</button>
</div>
</div>
<!-- Manually import from HTML -->
<div class="row tab-pane fade show" id="nav-html" role="tabpanel">
<div class="col-md-12">
<div class="input-group input-group-lg">
<textarea class="form-control input-group-append" v-model="raw_data" rows=10 placeholder="{% trans 'Paste html source here to parse recipe manually.' %}" style="font-size: 12px">
</textarea>
</div>
<br>
<button @click="loadPreviewHTML()" class="btn btn-primary shadow-none" type="button"
id="id_btn_HTML"><i class="fas fa-code"></i> {% trans 'Preview Import' %}
</button>
</div>
</div>
</div>
<br/>
<div v-if="loading" class="text-center">
<br/>
<i class="fas fa-spinner fa-spin fa-8x"></i>
</div>
<!-- recipe preview before Import -->
<div class="container-fluid" v-if="parsed" id="manage_tree">
<h2></h2>
<div class="row">
<div class="col" style="max-width:50%">
<div class="card card-border-primary" >
<div class="card-header">
<h3>{% trans 'Preview Recipe Data' %}</h3>
<div class='small text-muted'>{% trans 'Drag recipe attributes from the right into the appropriate box below.' %} </div>
</div>
<div class="card-body p-2">
<div class="card mb-2">
<div class="card-header" style="display:flex; justify-content:space-between;">
{% trans 'Name' %}
<i class="fas fa-eraser" style="cursor:pointer;" @click="deletePreview('name')" title="{% trans 'Clear Contents'%}"></i>
</div>
<div class="card-body drop-zone" @drop="replacePreview('name', $event)" @dragover.prevent @dragenter.prevent>
<div class="card-text">[[recipe_json.name]]</div>
</div>
</div>
<div class="card mb-2">
<div class="card-header" style="display:flex; justify-content:space-between;">
{% trans 'Description' %}
<i class="fas fa-eraser" style="cursor:pointer;" @click="deletePreview('description')" title="{% trans 'Clear Contents'%}"></i>
</div>
<div class="card-body drop-zone" @drop="replacePreview('description', $event)" @dragover.prevent @dragenter.prevent>
<div class="card-text">[[recipe_json.description]]</div>
</div>
</div>
<div class="card mb-2">
<div class="card-header">
<div class="row" style="display:flex; justify-content:space-between;">
{% trans 'Keywords' %}
<i class="fas fa-eraser" style="cursor:pointer;" @click="deletePreview('keywords')" title="{% trans 'Clear Contents'%}"></i>
</div>
<div class="small text-muted">{% trans 'Keywords dragged here will be appended to current list'%}</div>
</div>
<div class="card-body drop-zone" @drop="replacePreview('keywords', $event)" @dragover.prevent @dragenter.prevent>
<div v-for="kw in recipe_json.keywords">
<div class="card-text">[[kw]] </div>
</div>
</div>
</div>
<div class="card mb-2">
<div class="card-header" style="display:flex; justify-content:space-between;">
{% trans 'Image' %}
<i class="fas fa-eraser" style="cursor:pointer;" @click="deletePreview('image')" title="{% trans 'Clear Contents'%}"></i>
</div>
<div class="card-body m-0 p-0 drop-zone" @drop="replacePreview('image', $event)" @dragover.prevent @dragenter.prevent>
<img class="card-img" v-bind:src="[[recipe_json.image]]" alt="Recipe Image">
</div>
</div>
<div class = "row mb-2">
<div class="col">
<div class="card" >
<div class="card-header p-1" style="display:flex; justify-content:space-between;">
{% trans 'Servings' %}
<i class="fas fa-eraser" style="cursor:pointer;" @click="deletePreview('servings')" title="{% trans 'Clear Contents'%}"></i>
</div>
<div class="card-body p-2 drop-zone" @drop="replacePreview('servings', $event)" @dragover.prevent @dragenter.prevent>
<div class="card-text">[[recipe_json.servings]]</div>
</div>
</div>
</div>
<div class="col">
<div class="card">
<div class="card-header p-1" style="display:flex; justify-content:space-between;">
{% trans 'Prep Time' %}
<i class="fas fa-eraser" style="cursor:pointer;" @click="deletePreview('prepTime')" title="{% trans 'Clear Contents'%}"></i>
</div>
<div class="card-body p-2 drop-zone" @drop="replacePreview('prepTime', $event)" @dragover.prevent @dragenter.prevent>
<div class="card-text">[[recipe_json.prepTime]]</div>
</div>
</div>
</div>
<div class="col">
<div class="card">
<div class="card-header p-1" style="display:flex; justify-content:space-between;">
{% trans 'Cook Time' %}
<i class="fas fa-eraser" style="cursor:pointer;" @click="deletePreview('cookTime')" title="{% trans 'Clear Contents'%}"></i>
</div>
<div class="card-body p-2 drop-zone" @drop="replacePreview('cookTime', $event)" @dragover.prevent @dragenter.prevent>
<div class="card-text">[[recipe_json.cookTime]]</div>
</div>
</div>
</div>
</div>
<div class="card mb-2">
<div class="card-header">
<div class="row" style="display:flex; justify-content:space-between;">
{% trans 'Ingredients' %}
<i class="fas fa-eraser" style="cursor:pointer;" @click="deletePreview('ingredients')" title="{% trans 'Clear Contents'%}"></i>
</div>
<div class="small text-muted">{% trans 'Ingredients dragged here will be appended to current list.'%}</div>
</div>
<div class="card-body drop-zone" @drop="replacePreview('ingredients', $event)" @dragover.prevent @dragenter.prevent>
<ul class="list-group list-group">
<div v-for="i in recipe_json.recipeIngredient">
<li class="row border" style="border:1px">
<div class="col-sm-1 border">[[i.amount]]</div>
<div class="col-sm border">[[i.unit.text]]</div>
<div class="col-sm border">[[i.ingredient.text]]</div>
<div class="col-sm border">[[i.note]]</div>
</li>
</div>
</ul>
</div>
</div>
<div class="card mb-2">
<div class="card-header" style="display:flex; justify-content:space-between;">
{% trans 'Instructions' %}
<i class="fas fa-eraser" style="cursor:pointer;" @click="deletePreview('instructions')" title="{% trans 'Clear Contents'%}"></i>
</div>
<div class="card-body drop-zone" @drop="replacePreview('instructions', $event)" @dragover.prevent @dragenter.prevent>
<div class="card-text">[[recipe_json.recipeInstructions]]</div>
</div>
</div>
</div>
</div>
<br/>
<button @click="loadRecipeHTML()" class="btn btn-primary shadow-none" type="button"
id="id_btn_json"><i class="fas fa-code"></i> {% trans 'Import' %}
</button>
</div>
<div class="col" style="max-width:50%">
<div class="card card-border-primary">
<div class="card-header">
<h3>{% trans 'Discovered Attributes' %}</h3>
<div class='small text-muted'>
{% trans 'Drag recipe attributes from below into the appropriate box on the left. Click any node to display its full properties.' %}
</div>
</div>
<div class="card-body">
<v-jstree :data="recipe_tree"
text-field-name="name"
collapse:true
draggable
@item-drag-start="itemDragStart"
@item-click="itemClick">
<template scope="_">
<div class="col" @click.ctrl="customItemClickWithCtrl">
<div class="row clearfix" style="width:100%" >
<div class="col-es" style="align-right">
<button style="border: 0px; background-color: transparent; cursor: pointer;"
@click="deleteNode(_.vm, _.model, $event)"><i class="fas fa-minus-square" style="color:red"></i></button>
</div>
<div class="col overflow-hidden">
<i :class="_.vm.themeIconClasses" role="presentation" v-if="!_.model.loading"></i>
{% verbatim %}
[[_.model.name]]
{% endverbatim %}
</div>
</div>
</div>
</template>
</v-jstree>
</div>
</div>
</div>
</div>
</div>
<!-- end of recipe preview before Import -->
<template v-if="recipe_data !== undefined">
<form>
<div class="form-group">
<label for="id_name">{% trans 'Recipe Name' %}</label>
<input id="id_name" class="form-control" v-model="recipe_data.name">
</div>
<div class="form-group">
<label for="id_description">{% trans 'Recipe Description' %}</label>
<textarea id="id_description" class="form-control" rows="3" v-model="recipe_data.description"></textarea>
</div>
<div class="row">
<div class="col col-md-6" v-if="recipe_data.image !== ''">
<img v-bind:src="recipe_data.image" alt="{% trans 'Recipe Image' %}"
class="img-fluid img-responsive img-rounded">
</div>
<div class="col col-md-6">
<div class="form-group">
<label for="id_prep_time">{% trans 'Preparation time ca.' %}</label>
<input id="id_prep_time" class="form-control" v-model="recipe_data.prepTime">
</div>
<div class="form-group">
<label for="id_waiting_time">{% trans 'Waiting time ca.' %}</label>
<input id="id_waiting_time" class="form-control" v-model="recipe_data.cookTime">
</div>
<div class="form-group">
<label for="id_servings">{% trans 'Servings' %}</label>
<input id="id_servings" class="form-control" v-model="recipe_data.servings">
</div>
</div>
</div>
<br/>
<div class="row">
<div class="col-md-12">
<template v-for="(i, index) in recipe_data.recipeIngredient">
<div class="card" style="margin-top: 4px">
<div class="card-body">
<div class="row" v-if="i.original">
<div class="col-md-12" style="margin-bottom: 4px">
<span class="text-muted"><i class="fas fa-globe"></i> [[i.original]]</span>
</div>
</div>
<div class="row">
<div class="col-md-1">
<input class="form-control" v-model="i.amount">
</div>
<div class="col-md-4">
<table class="table-layout:fixed">
<col width="95%"/>
<col width="5%"/>
<tr>
<td>
<multiselect v-tabindex
ref="unit"
style="width: 100%!important;"
v-model="i.unit"
:options="units"
:close-on-select="true"
:clear-on-select="true"
:allow-empty="true"
:preserve-search="true"
placeholder="{% trans 'Select one' %}"
tag-placeholder="{% trans 'Select' %}"
label="text"
:taggable="true"
@tag="addUnitType"
:id="'unit_' + index"
@open="openUnitSelect"
track-by="id"
:multiple="false"
:loading="units_loading"
@search-change="searchUnits">
</multiselect>
</td>
<td>
<button class="btn btn-outline-success btn-lg" type="button"
@click="i.unit = ''" tabindex="-1">
<i class="fas fa-eraser"></i></button>
</td>
</tr>
</table>
</div>
<div class="col-md-4">
<multiselect v-tabindex
ref="ingredient"
v-model="i.ingredient"
:options="ingredients"
:taggable="true"
@tag="addIngredientType"
:id="'ingredient_' + index"
placeholder="{% trans 'Select one' %}"
tag-placeholder="{% trans 'Select' %}"
:close-on-select="true"
:clear-on-select="true"
:allow-empty="false"
:preserve-search="true"
label="text"
track-by="id"
:multiple="false"
:loading="ingredients_loading"
@search-change="searchIngredients"
@open="openIngredientSelect">
</multiselect>
</div>
<div class="col-md-2">
<input type="text" placeholder="{% trans 'Note' %}" class="form-control"
v-model="i.note">
</div>
<div class="col-md-1">
<button class="btn btn-outline-danger btn-lg" type="button"
@click="deleteIngredient(i)" tabindex="-1"><i
class="fas fa-trash-alt"></i></button>
</div>
</div>
</div>
</div>
</template>
<div style="text-align: center; margin-top: 16px">
<button class="btn btn-success" type="button" @click="addIngredient()"><i
class="fas fa-plus"></i></button>
<br/><br/>
</div>
</div>
</div>
<div class="form-group">
<label for="id_instructions">{% trans 'Instructions' %}</label>
<textarea id="id_instructions" class="form-control" v-model="recipe_data.recipeInstructions"
rows="8"></textarea>
</div>
<div class="form-group">
<label for="id_keywords">{% trans 'Keywords' %}</label>
<multiselect
v-model="recipe_data.keywords"
:options="keywords"
:close-on-select="false"
:clear-on-select="true"
:hide-selected="true"
:preserve-search="true"
placeholder="{% trans 'Select one' %}"
tag-placeholder="{% trans 'Add Keyword' %}"
:taggable="true"
@tag="addKeyword"
label="text"
track-by="id"
id="id_keywords"
:multiple="true"
:loading="keywords_loading"
@search-change="searchKeywords">
</multiselect>
</div>
<div class="form-group">
{% trans 'All Keywords' %}<br/>
<input id="id_all_keywords" type="checkbox"
v-model="all_keywords"> <label
for="id_all_keywords">{% trans 'Import all keywords, not only the ones already existing.' %}</label>
</div>
<div class="form-group">
<button type="button" class="btn btn-success" @click="importRecipe()"
:disabled="importing_recipe">{% trans 'Import' %}</button>
</div>
<br/>
<br/>
<br/>
</form>
</template>
<template v-if="error !== undefined">
<div>
<div style="text-align: center">
<i class="fas fa-robot fa-8x"></i><br/><br/>
[[error.msg]]
</div>
</div>
<br/>
<div class="row">
<div class="col-md-8 offset-md-2">
<div class="card border-info mb-6">
<div class="card-body text-info">
<h5 class="card-title">{% trans 'Information' %}</h5>
<p class="card-text">
{% blocktrans %} Only websites containing ld+json or microdata information can currently
be imported. Most big recipe pages support this. If you site cannot be imported but
you think
it probably has some kind of structured data feel free to post an example in the
github issues.{% endblocktrans %}
</p>
<a href="https://developers.google.com/search/docs/data-types/recipe" target="_blank"
rel="noreferrer nofollow"
class="card-link">{% trans 'Google ld+json Info' %}</a>
<a href="https://github.com/vabene1111/recipes/issues" target="_blank"
rel="noreferrer nofollow"
class="card-link">{% trans 'GitHub Issues' %}</a>
<a href="https://schema.org/Recipe" target="_blank" rel="noreferrer nofollow"
class="card-link">{% trans 'Recipe Markup Specification' %}</a>
</div>
</div>
</div>
</div>
</template>
</div>
<script src="{% url 'javascript-catalog' %}"></script>
<script type="application/javascript">
let csrftoken = Cookies.get('csrftoken');
Vue.http.headers.common['X-CSRFToken'] = csrftoken;
Vue.component('vue-multiselect', window.VueMultiselect.default)
let app = new Vue({
components: {
Multiselect: window.VueMultiselect.default
},
delimiters: ['[[', ']]'],
el: '#app',
data: {
remote_url: '',
raw_data: undefined,
keywords: [],
keywords_loading: false,
units: [],
units_loading: false,
ingredients: [],
ingredients_loading: false,
recipe_data: undefined,
error: undefined,
loading: false,
parsed: false,
all_keywords: false,
importing_recipe: false,
recipe_json: undefined,
recipe_tree: undefined,
},
directives: {
tabindex: {
inserted(el) {
el.setAttribute('tabindex', 0);
}
}
},
mounted: function () {
this.searchKeywords('')
this.searchUnits('')
this.searchIngredients('')
},
methods: {
makeToast: function (title, message, variant = null) {
//TODO remove duplicate function in favor of central one
this.$bvToast.toast(message, {
title: title,
variant: variant,
toaster: 'b-toaster-top-center',
solid: true
})
},
loadRecipe: function () {
this.recipe_data = undefined
this.error = undefined
this.loading = true
this.$http.post("{% url 'api_recipe_from_url' %}", {'url': this.remote_url}, {emulateJSON: true}).then((response) => {
console.log(response.data)
this.recipe_data = response.data;
this.loading = false
}).catch((err) => {
this.error = err.data
this.loading = false
console.log(err)
let msg = gettext('There was an error loading a resource!')
if (err.bodyText.length < 300) {
msg += err.bodyText
} else {
msg += ' ' + err.status + ' ' + err.statusText
}
this.makeToast(gettext('Error'), msg, 'danger')
})
},
loadRecipeJson: function () {
this.recipe_data = undefined
this.error = undefined
this.loading = true
this.$http.post("{% url 'api_recipe_from_json' %}", {'json': this.raw_data}, {emulateJSON: true}).then((response) => {
console.log(response.data)
this.recipe_data = response.data;
this.loading = false
}).catch((err) => {
this.error = err.data
this.loading = false
console.log(err)
let msg = gettext('There was an error loading a resource!')
if (err.bodyText.length < 300) {
msg += err.bodyText
} else {
msg += ' ' + err.status + ' ' + err.statusText
}
this.makeToast(gettext('Error'), msg, 'danger')
})
},
loadRecipeHTML: function () {
// TODO this needs refactored to get HTML elements
this.error = undefined
this.loading = true
this.parsed = false
this.recipe_json['@type'] = "Recipe"
this.$http.post("{% url 'api_recipe_from_json' %}", {'html': JSON.stringify(this.raw_data)}, {emulateJSON: true}).then((response) => {
console.log(response.data)
this.recipe_data = response.data;
this.loading = false
}).catch((err) => {
this.error = err.data
this.loading = false
console.log(err)
this.makeToast(gettext('Error'), gettext('There was an error loading a resource!') + err.bodyText, 'danger')
})
},
loadRecipeJson: function () {
this.recipe_data = undefined
this.error = undefined
this.loading = true
this.$http.post("{% url 'api_recipe_from_json' %}", {'json': this.json_data}, {emulateJSON: true}).then((response) => {
console.log(response.data)
this.recipe_data = response.data;
this.loading = false
}).catch((err) => {
this.error = err.data
this.loading = false
console.log(err)
this.makeToast(gettext('Error'), gettext('There was an error loading a resource!') + err.bodyText, 'danger')
})
},
loadPreviewRaw: function () {
this.recipe_json = undefined
this.recipe_tree = undefined
this.error = undefined
this.loading = true
this.$http.post("{% url 'api_manual_recipe_from_json' %}", {'data': this.raw_data}, {emulateJSON: true}).then((response) => {
console.log(response.data)
this.recipe_json = response.data['recipe_json'];
this.recipe_tree = response.data['recipe_tree'];
this.loading = false
this.parsed = true
}).catch((err) => {
this.error = err.data
this.loading = false
this.parsed = false
console.log(err)
this.makeToast(gettext('Error'), gettext('There was an error loading a resource!') + err.bodyText, 'danger')
})
},
importRecipe: function () {
if (this.recipe_data.name.length > 128) {
this.makeToast(gettext('Error'), gettext('Recipe name is longer than 128 characters'), 'danger')
return;
}
if (this.recipe_data.description.length > 512) {
this.makeToast(gettext('Error'), gettext('Recipe description is longer than 512 characters'), 'danger')
return;
}
if (this.importing_recipe) {
this.makeToast(gettext('Error'), gettext('Already importing the selected recipe, please wait!'), 'danger')
return;
}
this.importing_recipe = true
this.$set(this.recipe_data, 'all_keywords', this.all_keywords)
this.$http.post(`{% url 'data_import_url' %}`, this.recipe_data).then((response) => {
window.location.href = response.data
}).catch((err) => {
console.log(err);
this.makeToast(gettext('Error'), gettext('An error occurred while trying to import this recipe!') + err.bodyText, 'danger')
})
},
deleteIngredient: function (i) {
this.recipe_data.recipeIngredient = this.recipe_data.recipeIngredient.filter(item => item !== i)
},
addIngredient: function (i) {
this.recipe_data.recipeIngredient.push({
unit: {id: Math.random() * 1000, text: '{{ request.user.userpreference.default_unit }}'},
amount: 0,
ingredient: {id: Math.random() * 1000, text: ''}
})
},
addIngredientType: function (tag, index) {
index = index.replace('ingredient_', '')
let new_ingredient = this.recipe_data.recipeIngredient[index]
new_ingredient.ingredient = {'id': Math.random() * 1000, 'text': tag}
this.ingredients.push(new_ingredient.ingredient)
this.recipe_data.recipeIngredient[index] = new_ingredient
},
addUnitType: function (tag, index) {
index = index.replace('unit_', '')
let new_unit = this.recipe_data.recipeIngredient[index]
new_unit.unit = {'id': Math.random() * 1000, 'text': tag}
this.units.push(new_unit.unit)
this.recipe_data.recipeIngredient[index] = new_unit
},
addKeyword: function (tag) {
let new_keyword = {'text': tag, 'id': null}
this.recipe_data.keywords.push(new_keyword)
},
openUnitSelect: function (id) {
let index = id.replace('unit_', '')
if (this.recipe_data.recipeIngredient[index].unit !== null) {
this.$set(app.$refs.unit[index].$data, 'search', this.recipe_data.recipeIngredient[index].unit.text)
}
},
openIngredientSelect: function (id) {
let index = id.replace('ingredient_', '')
this.$set(this.$refs.ingredient[index].$data, 'search', this.recipe_data.recipeIngredient[index].ingredient.text)
},
searchKeywords: function (query) {
this.keywords_loading = true
this.$http.get("{% url 'dal_keyword' %}" + '?q=' + query).then((response) => {
this.keywords = response.data.results;
this.keywords_loading = false
}).catch((err) => {
console.log(err)
this.makeToast(gettext('Error'), gettext('There was an error loading a resource!') + err.bodyText, 'danger')
})
},
searchUnits: function (query) {
this.units_loading = true
this.$http.get("{% url 'dal_unit' %}" + '?q=' + query).then((response) => {
this.units = response.data.results;
if (this.recipe_data !== undefined) {
for (let x of Array.from(this.recipe_data.recipeIngredient)) {
if (x.unit !== null && x.unit.text !== '') {
this.units = this.units.filter(item => item.text !== x.unit.text)
this.units.push(x.unit)
}
}
}
this.units_loading = false
}).catch((err) => {
console.log(err)
this.makeToast(gettext('Error'), gettext('There was an error loading a resource!') + err.bodyText, 'danger')
})
},
searchIngredients: function (query) {
this.ingredients_loading = true
this.$http.get("{% url 'dal_food' %}" + '?q=' + query).then((response) => {
this.ingredients = response.data.results
if (this.recipe_data !== undefined) {
for (let x of Array.from(this.recipe_data.recipeIngredient)) {
if (x.ingredient.text !== '') {
this.ingredients = this.ingredients.filter(item => item.text !== x.ingredient.text)
this.ingredients.push(x.ingredient)
}
}
}
this.ingredients_loading = false
}).catch((err) => {
console.log(err)
this.makeToast(gettext('Error'), gettext('There was an error loading a resource!') + err.bodyText, 'danger')
})
},
deleteNode: function (node ,item, e) {
e.stopPropagation()
var index = node.parentItem.indexOf(item)
node.parentItem.splice(index, 1)
},
deletePreview: function(field) {
switch (field) {
case 'name':
this.recipe_json.name=""
break;
case 'description':
this.recipe_json.description=""
break;
case 'image':
this.recipe_json.image=""
break;
case 'keywords':
this.recipe_json.keywords=[]
break;
case 'servings':
this.recipe_json.servings=""
break;
case 'prepTime':
this.recipe_json.prepTime=""
break;
case 'cookTime':
this.recipe_json.cookTime=""
break;
case 'ingredients':
this.recipe_json.recipeIngredient=[]
break;
case 'instructions':
this.recipe_json.recipeInstructions=""
break;
}
},
itemClick: function (node, item, e) {
this.makeToast(gettext('Details'), node.model.value, 'info')
},
itemDragStart (node,item,e) {
if (node.model.children.length > 0) {
e.dataTransfer.setData('hasChildren', true)
}
e.dataTransfer.setData('value', node.model.value)
},
replacePreview: function(field, e) {
v = e.dataTransfer.getData('value')
if (e.dataTransfer.getData('hasChildren')) {
this.makeToast(gettext('Error'), gettext('Items with children cannot be dropped here!') , 'danger')
return
}
switch (field) {
case 'name':
this.recipe_json.name=v
break;
case 'description':
this.recipe_json.description=v
break;
case 'image':
this.recipe_json.image=v
break;
case 'keywords':
this.recipe_json.keywords.push(v)
break;
case 'servings':
this.recipe_json.servings=v
break;
case 'prepTime':
this.recipe_json.prepTime=v
break;
case 'cookTime':
this.recipe_json.cookTime=v
break;
case 'ingredients':
this.$http.post('{% url 'api_ingredient_from_string' %}', {text: v}, {emulateJSON: true}).then((response) => {
console.log(response)
let new_ingredient={
unit: {id: Math.random() * 1000, text: response.body.unit},
amount: response.body.amount,
ingredient: {id: Math.random() * 1000, text: response.body.food},
note: response.body.note
}
this.recipe_json.recipeIngredient.push(new_ingredient)
}).catch((err) => {
console.log(err)
this.makeToast(gettext('Error'), gettext('Something went wrong.'), 'danger')
})
break;
case 'instructions':
this.recipe_json.recipeInstructions=this.recipe_json.recipeInstructions.concat(v)
break;
}
},
parseIngredient: function(txt) {
this.$http.post('{% url 'api_ingredient_from_string' %}', {text: txt}, {emulateJSON: true}).then((response) => {
console.log(response)
let ing = [response.body.amount, response.body.unit, response.body.food, response.body.note]
return ing
}).catch((err) => {
console.log(err)
this.makeToast(gettext('Error'), gettext('Something went wrong.'), 'danger')
})
}
}
});
</script>
{% endblock %}