TandoorRecipes/cookbook/templates/url_import.html
2021-05-01 16:30:21 -05:00

928 lines
52 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-app" data-toggle="tab" role="tab" aria-controls="nav-app">App</a>
<a class="nav-link" href="#nav-source" data-toggle="tab" role="tab" aria-controls="nav-source">Source</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-file" data-toggle="tab" role="tab" aria-controls="nav-file">File</a>
</nav>
<div class="tab-content" id="nav-tabContent">
<!-- Import URL -->
<div class="tab-pane fade show active" id="nav-url" role="tabpanel">
<div class="btn-group btn-group-toggle" data-toggle="buttons">
<label class="btn btn-outline-info btn-sm active" @click="automatic=true">
<input type="radio" autocomplete="off" checked> Automatic
</label>
<label class="btn btn-outline-info btn-sm" @click="automatic=false">
<input type="radio" autocomplete="off"> Manual
</label>
</div>
<div class="input-group my-2">
<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>
<!-- Import from Recipe Application -->
<div class=" tab-pane fade show" id="nav-app" role="tabpanel">
<div class="btn-group btn-group-toggle" data-toggle="buttons">
<label class="btn btn-outline-info btn-sm active" @click="recipe_app='DEFAULT'">
<input type="radio" autocomplete="off" checked> Tandoor
</label>
<label class="btn btn-outline-info btn-sm" @click="recipe_app='PAPRIKA'">
<input type="radio" autocomplete="off"> Paprika
</label>
<label class="btn btn-outline-info btn-sm" @click="recipe_app='NEXTCLOUD'">
<input type="radio" autocomplete="off"> Nextcloud Cookbook
</label>
<label class="btn btn-outline-info btn-sm" @click="recipe_app='MEALIE'">
<input type="radio" autocomplete="off"> Mealie
</label>
<label class="btn btn-outline-info btn-sm" @click="recipe_app='CHOWDOWN'">
<input type="radio" autocomplete="off"> Chowdown
</label>
<label class="btn btn-outline-info btn-sm" @click="recipe_app='SAFRON'">
<input type="radio" autocomplete="off"> Safron
</label>
</div>
<b-form-file
class="my-2"
accept=".zip"
multiple
v-model="recipe_files"
placeholder="{% trans 'Select recipe files to import or drop them here...' %}"
drop-placeholder="Drop recipe files here...">
</b-form-file>
<button @click="importAppRecipe()" class="btn btn-primary shadow-none" type="button"
id="id_btn_app"><i class="fas fa-file-archive"></i> {% trans 'Import' %}
</button>
</div>
<!-- Import JSON or HTML -->
<div class=" tab-pane fade show" id="nav-source" role="tabpanel">
<div class="btn-group btn-group-toggle" data-toggle="buttons">
<label class="btn btn-outline-info btn-sm active" @click="automatic=true">
<input type="radio" autocomplete="off" checked> Automatic
</label>
<label class="btn btn-outline-info btn-sm" @click="automatic=false">
<input type="radio" autocomplete="off"> Manual
</label>
</div>
<div class="input-group my-2">
<textarea class="form-control input-group-append" v-model="source_data" rows=10 placeholder="{% trans 'Paste json or html source here to load recipe.' %}" style="font-size: 12px">
</textarea>
</div>
<button @click="loadSource()" class="btn btn-primary shadow-none" type="button"
id="id_btn_app"><i class="fas fa-code"></i> {% trans 'Import' %}
</button>
</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="preview" id="manage_tree">
<div class="row">
<div class="col" style="max-width:50%">
<!-- start of preview card -->
<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">
<div class="row px-3" style="justify-content:space-between;">
{% trans 'Name' %}
<i class="fas fa-eraser" style="cursor:pointer;" @click="recipe_json.name=''" title="{% trans 'Clear Contents'%}"></i>
</div>
<div class="small text-muted">{% trans 'Text dragged here will be appended to the name.'%}</div>
</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">
<div class="row px-3" style="justify-content:space-between;">
{% trans 'Description' %}
<i class="fas fa-eraser" style="cursor:pointer;" @click="recipe_json.description=''" title="{% trans 'Clear Contents'%}"></i>
</div>
<div class="small text-muted">{% trans 'Text dragged here will be appended to the description.'%}</div>
</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 px-3" style="justify-content:space-between;">
{% trans 'Keywords' %}
<i class="fas fa-eraser" style="cursor:pointer;" @click="recipe_json.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="recipe_json.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="recipe_json.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="recipe_json.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="recipe_json.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 px-3" style="display:flex; justify-content:space-between;">
{% trans 'Ingredients' %}
<i class="fas fa-eraser" style="cursor:pointer;" @click="recipe_json.recipeIngredient=[]" 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-light" >
<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">
<div class="row px-3" style="justify-content:space-between;">
{% trans 'Instructions' %}
<i class="fas fa-eraser" style="cursor:pointer;" @click="recipe_json.recipeInstructions=''" title="{% trans 'Clear Contents'%}"></i>
</div>
<div class="small text-muted">{% trans 'Recipe instructions dragged here will be appended to current instructions.'%}</div>
</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/>
<!-- end of preview card -->
<button @click="loadRecipeManual()" class="btn btn-primary shadow-none" type="button"
id="id_btn_json"><i class="fas fa-code"></i> {% trans 'Import' %}
</button>
</div>
<!-- start of source data -->
<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="btn-group btn-group-toggle" data-toggle="buttons">
<label class="btn btn-outline-info btn-sm active" @click="preview_type='json'">
<input type="radio" autocomplete="off" checked> json
</label>
<label class="btn btn-outline-info btn-sm" @click="preview_type='html'">
<input type="radio" autocomplete="off"> html
</label>
<label class="btn btn-outline-info btn-sm" @click="preview_type='image'">
<input type="radio" autocomplete="off"> images
</label>
</div>
<div class="card-body p-1">
<div class="card card-border-primary" v-if="show_blank">
<div class="card-header">
<div class="row px-3" style="justify-content:space-between;">
{% trans 'Blank Field' %}
<i class="fas fa-eraser" style="cursor:pointer;" @click="blank_field=''" title="{% trans 'Clear Contents'%}"></i>
</div>
<div class="small text-muted">{% trans 'Items dragged to Blank Field will be appended.'%}</div>
</div>
<div class="card-body drop-zone"
v-model: blank_field
@drop="replacePreview()"
@dragover.prevent
@dragenter.prevent>
</div>
</div>
<!-- start of json data -->
<v-jstree v-if="preview_type=='json'" :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>
<!-- start of html data -->
<div v-if="preview_type=='html'">
<ul class="list-group list-group-flush" v-for="(txt, key) in recipe_html">
<div class="list-group-item bg-light m-0 small"
draggable
@dragstart="htmlDragStart($event)"
style="display:flex; justify-content:space-between;">
[[txt]]
<i class="fas fa-minus-square" style="cursor:pointer; color:red" @click="$delete(recipe_html, key)" title="{% trans 'Delete Text'%}"></i>
</div>
</ul>
</div>
<!-- start of images -->
<div v-if="preview_type=='image'">
<ul class="list-group list-group-flush" v-for="(img, key) in images">
<div class="list-group-item bg-light m-0 small"
draggable
@dragstart="imageDragStart($event)"
style="display:flex; justify-content:space-between;">
<img class="card-img" v-bind:src=[[img]] alt="Image">
<i class="fas fa-minus-square" style="cursor:pointer; color:red" @click="$delete(images, key)" title="{% trans 'Delete image'%}"></i>
</div>
</ul>
</div>
</div>
</div>
</div>
<!-- end of json tree -->
</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: '',
source_data: undefined,
keywords: [],
keywords_loading: false,
units: [],
units_loading: false,
ingredients: [],
ingredients_loading: false,
recipe_data: undefined,
error: undefined,
loading: false,
preview: false,
preview_type: 'json',
all_keywords: false,
importing_recipe: false,
recipe_json: undefined,
recipe_tree: undefined,
recipe_html: undefined,
automatic: true,
show_blank: false,
blank_field: '',
recipe_app: 'DEFAULT',
recipe_files: [],
images: [],
},
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.recipe_json = undefined
this.recipe_tree = undefined
this.images = []
this.error = undefined
this.loading = true
this.preview = false
if (this.automatic) {
console.log('true')
}
this.$http.post("{% url 'api_recipe_from_url' %}", {'url': this.remote_url, 'auto':this.automatic}, {emulateJSON: true}).then((response) => {
console.log(response.data)
if (this.automatic) {
this.recipe_data = response.data;
} else {
this.recipe_json = response.data['recipe_json'];
this.recipe_tree = response.data['recipe_tree'];
this.recipe_html = response.data['recipe_html'];
this.images = response.data['images'];
this.preview = true
}
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')
})
},
loadSource: function() {
this.recipe_data = undefined
this.recipe_json = undefined
this.recipe_tree = undefined
this.images = []
this.error = undefined
this.loading = true
this.preview = false
this.$http.post("{% url 'api_recipe_from_source' %}", {'data': this.source_data, 'auto':this.automatic}, {emulateJSON: true}).then((response) => {
console.log(response.data)
if (this.automatic) {
this.recipe_data = response.data['recipe_json'];
this.preview = false
} else {
this.recipe_json = response.data['recipe_json'];
this.recipe_tree = response.data['recipe_tree'];
this.recipe_html = response.data['recipe_html'];
this.preview = true
}
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')
})
},
loadRecipeManual: function () {
this.error = undefined
this.preview = false
this.loading = true
this.recipe_json['@type'] = "Recipe"
this.$http.post("{% url 'api_recipe_from_source' %}", {'data': JSON.stringify(this.recipe_json), 'auto':'true'}, {emulateJSON: true}).then((response) => {
console.log(response.data)
this.recipe_data = response.data['recipe_json'];
this.loading = false
this.preview = 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')
})
},
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) => {
this.importing_recipe = false
console.log(err);
this.makeToast(gettext('Error'), gettext('An error occurred while trying to import this recipe!') + err.bodyText, 'danger')
})
},
importAppRecipe: function() {
this.error = undefined
this.preview = false
this.loading = true
let formData = new FormData();
let files = []
formData.append('type', this.recipe_app);
for( var i = 0; i < this.recipe_files.length; i++ ){
formData.append('files', this.recipe_files[i]);
}
this.$http.post("{% url 'view_import' %}", formData, {headers: {'Content-Type': 'multipart/form-data'}}).then((response) => {
console.log(response.data)
window.location.href = "{% url 'view_import_response' 1237654 %}".replace('1237654', response.data['import_id'])
}).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')
})
},
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)
},
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)
},
htmlDragStart: function (e) {
console.log(e.target.innerText)
e.dataTransfer.setData('value', e.target.innerText)
},
imageDragStart: function (e) {
console.log(e.target.src)
e.dataTransfer.setData('value', e.target.src)
},
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 = [this.recipe_json.name, v].filter(Boolean).join(" ");
break;
case 'description':
this.recipe_json.description = [this.recipe_json.description, v].filter(Boolean).join(" ");
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, v].filter(Boolean).join("\n\n");
break;
case 'blank':
this.blank_field = [this.blank_field, v].filter(Boolean).join(" ");
break;
}
},
}
});
</script>
{% endblock %}