Merge remote-tracking branch 'upstream/develop' into recipe-serving-count

This commit is contained in:
tourn
2020-12-24 11:15:06 +01:00
52 changed files with 7347 additions and 2762 deletions

View File

@@ -56,7 +56,7 @@
<input type="file" @change="imageChanged">
</div>
<div class="col-md-6">
<label for="id_name"> {% trans 'Preperation Time' %}</label>
<label for="id_name"> {% trans 'Preparation Time' %}</label>
<input class="form-control" id="id_prep_time" v-model="recipe.working_time">
<br/>
<label for="id_name"> {% trans 'Waiting Time' %}</label>
@@ -83,6 +83,35 @@
</multiselect>
</div>
</div>
<template v-if="recipe !== undefined">
<div class="row" v-if="recipe.nutrition" style="margin-top: 1vh">
<div class="col-md-12">
<div class="card border-grey">
<div class="card-body">
<h4 class="card-title">{% trans 'Nutrition' %}</h4>
<div class="dropdown-menu dropdown-menu-right"
aria-labelledby="dropdownMenuLink">
<button class="dropdown-item" @click="removeStep(step)"><i
class="fa fa-trash fa-fw"></i> {% trans 'Delete Step' %}</button>
</div>
<label for="id_name"> {% trans 'Calories' %}</label>
<input class="form-control" id="id_calories" v-model="recipe.nutrition.calories">
<label for="id_name"> {% trans 'Carbohydrates' %}</label>
<input class="form-control" id="id_carbohydrates" v-model="recipe.nutrition.carbohydrates">
<label for="id_name"> {% trans 'Fats' %}</label>
<input class="form-control" id="id_fats" v-model="recipe.nutrition.fats">
<label for="id_name"> {% trans 'Proteins' %}</label>
<input class="form-control" id="id_proteins" v-model="recipe.nutrition.proteins">
<br/>
</div>
</div>
</div>
</div>
</template>
<draggable :list="recipe.steps" group="steps"
@@ -322,7 +351,7 @@
<div class="col-md-12">
<label :for="'id_instruction_' + step.id">{% trans 'Instructions' %}</label>
<b-form-textarea class="form-control" rows="2" max-rows="20" v-model="step.instruction"
:id="'id_instruction_' + step.id"></b-form-textarea>
:id="'id_instruction_' + step.id"></b-form-textarea>
<small class="text-muted">{% trans 'You can use markdown to format this field. See the <a href="/docs/markdown/">docs here</a>' %}</small>
</div>
</div>
@@ -330,7 +359,7 @@
</div>
</draggable>
<div class="row" style="margin-top: 1vh; margin-bottom: 8vh">
<div class="row" style="margin-top: 1vh; margin-bottom: 8vh" v-if="recipe !== undefined">
<div class="col-12">
<button type="button" @click="updateRecipe(true)"
class="btn btn-success shadow-none">{% trans 'Save & View' %}</button>
@@ -338,6 +367,11 @@
class="btn btn-info shadow-none">{% trans 'Save' %}</button>
<button type="button" @click="addStep()"
class="btn btn-primary shadow-none">{% trans 'Add Step' %}</button>
<button type="button" @click="addNutrition()"
class="btn btn-primary shadow-none"
v-if="recipe.nutrition === null">{% trans 'Add Nutrition' %}</button>
<button type="button" @click="removeNutrition()" v-if="recipe.nutrition !== null"
class="btn btn-warning shadow-none">{% trans 'Remove Nutrition' %}</button>
<a href="{% url 'view_recipe' recipe.pk %}" @click="addStep()"
class="btn btn-secondary shadow-none">{% trans 'View Recipe' %}</a>
<a href="{% url 'delete_recipe' recipe.pk %}"
@@ -352,7 +386,7 @@
{% endblock %}
{% block content_xl_right %}
<div class="sticky-top" style="top: 2vh; z-index: 100;">
<div class="sticky-top" style="top: 2vh; z-index: 100;" v-if="recipe !== undefined">
<div class="row">
<div class="col-md-11">
<button type="button" @click="updateRecipe(true)"
@@ -364,6 +398,12 @@
<button type="button" @click="addStep()"
class="btn btn-primary btn-block shadow-none">{% trans 'Add Step' %}</button>
<button type="button" @click="addNutrition()"
class="btn btn-primary btn-block shadow-none"
v-if="recipe.nutrition === null">{% trans 'Add Nutrition' %}</button>
<button type="button" @click="removeNutrition()" v-if="recipe.nutrition !== null"
class="btn btn-warning btn-block shadow-none">{% trans 'Remove Nutrition' %}</button>
<a href="{% url 'view_recipe' recipe.pk %}"
class="btn btn-secondary btn-block shadow-none">{% trans 'View Recipe' %}</a>
<a href="{% url 'delete_recipe' recipe.pk %}"
@@ -458,8 +498,8 @@
e.preventDefault(); // present "Save Page" from getting triggered.
for (el of e.path) {
if(el.id !== undefined && el.id.includes('id_card_step_')) {
let step = this.recipe.steps[el.id.replace('id_card_step_','')]
if (el.id !== undefined && el.id.includes('id_card_step_')) {
let step = this.recipe.steps[el.id.replace('id_card_step_', '')]
this.addIngredient(step)
}
}
@@ -652,6 +692,12 @@
scrollToStep: function (step_index) {
document.getElementById('id_step_' + step_index).scrollIntoView({behavior: 'smooth'});
},
addNutrition: function () {
this.recipe.nutrition = {}
},
removeNutrition: function () {
this.recipe.nutrition = null
}
}
});
</script>

View File

@@ -12,6 +12,7 @@
<script src="{% static 'js/Sortable.min.js' %}"></script>
<script src="{% static 'js/vuedraggable.umd.min.js' %}"></script>
<script src="{% static 'js/vue-cookies.js' %}"></script>
<script src="{% static 'js/js.cookie.min.js' %}"></script>
@@ -24,14 +25,15 @@
<div class="col-md-4 offset-md-4">
<div class="input-group" style="margin-top: 8px; margin-bottom: 8px">
<div class="input-group-prepend">
<button class="btn btn-outline-secondary shadow-none" @click="changeWeek(-1)">
<button class="btn btn-outline-secondary shadow-none"
@click="changeStartDate(number_of_days * -1)">
<i class="fas fa-arrow-left"></i>
</button>
</div>
<input name="week" id="id_week" class="form-control" type="week" v-model="week"
<input name="date" id="id_date" class="form-control" type="date" v-model="start_date"
@change="updatePlan()">
<div class="input-group-append">
<button class="btn btn-outline-secondary shadow-none" @click="changeWeek(1)">
<button class="btn btn-outline-secondary shadow-none" @click="changeStartDate(number_of_days)">
<i class="fas fa-arrow-right"></i>
</button>
</div>
@@ -41,10 +43,10 @@
<div class="row">
<div class="col-md-12">
<table class="table table-sm table-striped table-responsive-sm">
<table class="table table-sm table-striped table-responsive-sm" style=" table-layout:fixed;">
<thead class="thead-dark">
<tr>
<th v-for="d in days" style="width: 14.2%; text-align: center">[[d]]<br/>[[formatDateDay(d)]].
<th v-for="d in dates" style="width: 14.2%; text-align: center">[[formatDateDayname(d)]]<br/>[[formatDateDay(d)]].
<button class="btn btn-sm btn-outline-secondary shadow-none" @click="addDayToShopping(d)"><i
class="fas fa-cart-plus fa-sm"></i></button>
</th>
@@ -52,7 +54,7 @@
</thead>
<tbody v-for="t in meal_types">
<tr v-if="meal_plan[t.name] !== undefined">
<td colspan="7" style="text-align: center">
<td :colspan="number_of_days" style="text-align: center">
[[ meal_plan[t.name].name]]
<template
v-if="t.created_by !== {{ request.user.pk }} && user_names[t.created_by] !== undefined">
@@ -66,18 +68,21 @@
@change="dragChanged(d.date, t, $event)"
:empty-insert-threshold="10" handle=".handle">
<div class="" v-for="(element, index) in d.items" :key="element.id">
<!-- small layout with handle -->
<div class="d-block d-md-none">
<div class="col-">
<i class="fas fa-arrows-alt handle input-group-text"
style="width: 100%"></i>
</div>
<div class="list-group-item">
<div class="list-group-item" style="word-wrap: break-word;">
<a href="#" @click="plan_detail = element" data-toggle="modal"
data-target="#id_plan_detail_modal">[[ planElementName(element)]]</a>
</div>
</div>
<div class="list-group-item handle d-md-block d-none">
<div class="col-md-12">
<!-- big layout -->
<div class="list-group-item handle d-md-block d-none"
style="word-wrap: break-word; padding: 2;margin-bottom: 4">
<div class="col-md-12" style="padding: 0">
<a href="#" @click="plan_detail = element" data-toggle="modal"
data-target="#id_plan_detail_modal">[[ planElementName(element)]]</a>
</div>
@@ -107,7 +112,8 @@
<input type="text" class="form-control" v-model="recipe_query" @keyup="getRecipes"
placeholder="{% trans 'Search Recipe' %}">
<div class="input-group-append">
<button class="btn btn-outline-secondary" type="button" @click="getRandomRecipes">
<button class="btn btn-outline-secondary" type="button"
@click="getRandomRecipes">
<i class="fas fa-dice"></i>
</button>
</div>
@@ -180,6 +186,29 @@
</div>
<div class="card-body">
<div class="row">
<div class="col">
<label>
{% trans 'Number of Days' %}
<input class="form-control" type="number" v-model="number_of_days"
@change="updatePlan(); $cookies.set('number_of_days',number_of_days)">
</label>
</div>
</div>
<div class="row">
<div class="col">
<label>
{% trans 'Weekday offset' %}
<input class="form-control" type="number" v-model="start_offset"
@change="updatePlan(); $cookies.set('start_offset',start_offset)">
<small class="text-muted">{% trans 'Number of days starting from the first day of the week to offset the default view.' %}</small>
</label>
</div>
</div>
<a href="#" data-toggle="modal"
data-target="#id_plan_types_modal">{% trans 'Edit plan types' %}</a> <br/>
<a href="#" data-toggle="modal"
@@ -345,8 +374,10 @@
delimiters: ['[[', ']]'],
el: '#app',
data: {
week: moment().format('YYYY-[W]WW'),
days: moment.weekdays(true),
start_date: undefined,
start_offset: 0,
dates: [],
number_of_days: $cookies.isKey('number_of_days') ? $cookies.get('number_of_days') : 7,
plan_entries: [],
meal_types: [],
meal_types_edit: [],
@@ -374,6 +405,9 @@
this.$set(this.user_names, {{ request.user.pk }}, '{{ request.user.get_user_name }}')
this.user_id_update = Array.from(this.default_shared_users)
this.start_offset = $cookies.isKey('start_offset') ? $cookies.get('start_offset') : 0;
this.start_date = moment().weekday(0).add(this.start_offset, 'days').format('YYYY-MM-DD')
this.updatePlan();
this.getRecipes();
},
@@ -388,6 +422,11 @@
})
},
updatePlan: function () {
this.dates = [];
for (var i = 0; i <= (this.number_of_days - 1); i++) {
this.dates.push(moment(this.start_date).add(i, 'days'));
}
let planEntryPromise = this.getPlanEntries();
let planTypePromise = this.getPlanTypes();
@@ -396,7 +435,7 @@
})
},
getPlanEntries: function () {
return this.$http.get("{% url 'api:mealplan-list' %}?html_week=" + this.week).then((response) => {
return this.$http.get("{% url 'api:mealplan-list' %}?from_date=" + this.dates[0].format('YYYY-MM-DD') + "&to_date=" + this.dates[this.dates.length - 1].format('YYYY-MM-DD')).then((response) => {
this.plan_entries = response.data;
}).catch((err) => {
console.log("getPlanEntries error: ", err);
@@ -431,11 +470,10 @@
meal_type: t.id,
days: {}
})
for (let d of this.days) {
let date = moment(this.week).weekday(this.days.indexOf(d)).format('YYYY-MM-DD')
this.$set(this.meal_plan[t.name].days, date, {
name: d,
date: date,
for (let d of this.dates) {
this.$set(this.meal_plan[t.name].days, d.format('YYYY-MM-DD'), {
name: this.formatDateDayname(d),
date: d.format('YYYY-MM-DD'),
items: []
})
}
@@ -632,11 +670,14 @@
formatLocalDate: function (date) {
return moment(date).format('LL')
},
formatDateDay: function (day) {
return moment(this.week).weekday(this.days.indexOf(day)).format('D')
formatDateDay: function (date) {
return moment(date).format('D')
},
changeWeek: function (change) {
this.week = moment(this.week).add(change, 'w').format('YYYY-[W]WW')
formatDateDayname: function (date) {
return moment(date).format('dddd')
},
changeStartDate: function (change) {
this.start_date = moment(this.start_date).add(change, 'days').format('YYYY-MM-DD')
this.updatePlan();
},
getShoppingUrl: function () {
@@ -653,13 +694,14 @@
return url
},
getIcalUrl: function () {
return "{% url 'api_get_plan_ical' 12345 %}".replace(/12345/, this.week);
if (this.dates.length === 0) {
return ""
}
return "{% url 'api_get_plan_ical' 12345 6789 %}".replace(/12345/, this.dates[0].format('YYYY-MM-DD')).replace(/6789/, this.dates[this.dates.length - 1].format('YYYY-MM-DD'));
},
addDayToShopping: function (day) {
let date = moment(this.week).weekday(this.days.indexOf(day)).format('YYYY-MM-DD')
addDayToShopping: function (date) {
for (let t of this.meal_types) {
for (let i of this.meal_plan[t.name].days[date].items) {
for (let i of this.meal_plan[t.name].days[date.format('YYYY-MM-DD')].items) {
if (!this.shopping_list.includes(i)) {
this.shopping_list.push(i)
}

View File

@@ -12,6 +12,8 @@
{% include 'include/vue_base.html' %}
<script src="{% static 'js/moment-with-locales.min.js' %}"></script>
<script src="{% static 'js/frac.js' %}"></script>
<link rel="stylesheet" href="{% static 'css/pretty-checkbox.min.css' %}">
<link rel="stylesheet" href="{% static 'custom/css/markdown_blockquote.css' %}">
@@ -77,13 +79,13 @@
{% if recipe.working_time and recipe.working_time != 0 %}
<span class="badge badge-secondary"><i
class="fas fa-user-clock"></i> {% trans 'Preparation time ca.' %} {{ recipe.working_time }} min </span>
class="fas fa-user-clock"></i> {% trans 'Preparation time ~' %} {{ recipe.working_time }} min </span>
{% endif %}
{% if recipe.waiting_time and recipe.waiting_time != 0 %}
<span
class="badge badge-secondary"><i
class="far fa-clock"></i> {% trans 'Waiting time ca.' %} {{ recipe.waiting_time }} min </span>
class="far fa-clock"></i> {% trans 'Waiting time ~' %} {{ recipe.waiting_time }} min </span>
{% endif %}
{% recipe_last recipe request.user as last_cooked %}
{% if last_cooked %}
@@ -150,7 +152,7 @@
<span>&#x2063;</span>
</template>
<template v-if="!i.no_amount">
<span>[[roundDecimals(i.amount * ingredient_factor)]]</span>
<span v-html="calculateAmount(i.amount)"></span>
{# Allow for amounts without units, such as "2 eggs" #}
<template v-if="i.unit">
[[i.unit.name]]
@@ -208,6 +210,60 @@
{% endif %}
</div>
{% if recipe.nutrition %}
<div class="row mt-5">
<div class="col-md-6 order-md-1 col-sm-12 order-sm-2 col-12 order-2">
<div class="card border-primary">
<div class="card-body">
<h4 class="card-title">{% trans 'Nutrition' %}</h4>
<table class="table table-sm">
<tr>
<td style="padding-top: 8px!important; ">
<b>{% trans 'Calories' %}</b>
</td>
<td style="text-align: right">{{ recipe.nutrition.calories|floatformat:2 }}</td>
<td>kcal</td>
</tr>
<tr>
<tr>
<td style="padding-top: 8px!important; ">
<b>{% trans 'Carbohydrates' %}</b>
</td>
<td style="text-align: right">{{ recipe.nutrition.carbohydrates|floatformat:2 }}</td>
<td>g</td>
</tr>
<tr>
<tr>
<td style="padding-top: 8px!important; ">
<b>{% trans 'Fats' %}</b>
</td>
<td style="text-align: right">{{ recipe.nutrition.fats|floatformat:2 }}</td>
<td>g</td>
</tr>
<tr>
<tr>
<td style="padding-top: 8px!important; ">
<b>{% trans 'Proteins' %}</b>
</td>
<td style="text-align: right">{{ recipe.nutrition.proteins|floatformat:2 }}</td>
<td>g</td>
</tr>
<tr>
<td></td>
<td></td>
<td></td>
</tr>
</table>
{% if recipe.nutrition.source %}
Source: {{ recipe.nutrition.source }}
{% endif %}
</div>
</div>
</div>
</div>
{% endif %}
<div v-if="recipe !== undefined && recipe.steps.length > 0">
<hr>
<h3>{% trans 'Instructions' %}</h3>
@@ -271,7 +327,7 @@
<span>&#x2063;</span>
</template>
<template v-if="!i.no_amount">
<span>[[roundDecimals(i.amount * ingredient_factor)]]</span>
<span v-html="calculateAmount(i.amount)"></span>
{# Allow for amounts without units, such as "2 eggs" #}
<template v-if="i.unit">
[[i.unit.name]]
@@ -484,7 +540,7 @@
this.$http.get("{% url 'api:recipe-detail' recipe.pk %}" {% if share %}
+ "?share={{ share }}"{% endif %}).then((response) => {
this.recipe = response.data;
this.loading = false
this.loading = false;
for (let step of this.recipe.steps) {
if (step.ingredients.length > 0) {
@@ -493,25 +549,25 @@
if (step.time !== 0) {
this.has_times = true
}
this.$set(step, 'time_finished', undefined)
this.$set(step, 'time_finished', undefined);
for (let i of step.ingredients) {
this.$set(i, 'checked', false)
}
}
}).catch((err) => {
this.error = err.data
this.loading = false
this.error = err.data;
this.loading = false;
console.log(err)
})
},
roundDecimals: function (num) {
let decimals = {% if request.user.userpreference.ingredient_decimals %}
{{ request.user.userpreference.ingredient_decimals }} {% else %} 2 {% endif %}
{{ request.user.userpreference.ingredient_decimals }} {% else %} 2; {% endif %}
return +(Math.round(num + `e+${decimals}`) + `e-${decimals}`);
},
updateTimes: function (step) {
let time_diff_first = 0
let time_diff_first = 0;
for (let s of this.recipe.steps) {
if (this.recipe.steps.indexOf(s) < this.recipe.steps.indexOf(step)) {
time_diff_first += s.time
@@ -520,7 +576,7 @@
this.recipe.steps[0].time_finished = moment(step.time_finished).subtract(time_diff_first, 'minutes').format(moment.HTML5_FMT.DATETIME_LOCAL);
let time_diff = 0
let time_diff = 0;
for (let s of this.recipe.steps) {
s.time_finished = moment(this.recipe.steps[0].time_finished).add(time_diff, 'minutes').format(moment.HTML5_FMT.DATETIME_LOCAL);
time_diff += s.time
@@ -529,8 +585,28 @@
},
getShoppingUrl: function () {
return `{% url 'view_shopping' %}?r=[${this.recipe.id},${this.ingredient_factor}]`
}
},
calculateAmount: function (amount) {
{% if request.user.userpreference.use_fractions %}
let return_string = ''
let fraction = frac.cont((amount * this.ingredient_factor), 9, true)
if (fraction[0] > 0) {
return_string += fraction[0]
}
if (fraction[1] > 0) {
return_string += ` <sup>${(fraction[1])}</sup>&frasl;<sub>${(fraction[2])}</sub>`
}
return return_string
{% else %}
return this.roundDecimals(amount * this.ingredient_factor)
{% endif %}
},
}
});
</script>
{% endblock %}