214 lines
5.8 KiB
Vue
214 lines
5.8 KiB
Vue
<template>
|
|
<div>
|
|
<button type="button" class="button is-primary" @click="bulkEditIngredients">Bulk Edit</button>
|
|
<app-modal wide :open="isBulkEditing" title="Edit Ingredients" @dismiss="cancelBulkEditing">
|
|
<div class="columns">
|
|
<div class="column is-half bulk-input">
|
|
<textarea ref="bulkEditTextarea" class="textarea is-size-7-mobile" v-model="bulkEditText"></textarea>
|
|
</div>
|
|
<div class="column is-half">
|
|
<table class="table is-bordered is-narrow is-size-7">
|
|
<tr>
|
|
<th>#</th>
|
|
<th>Unit</th>
|
|
<th>Name</th>
|
|
<th>Prep</th>
|
|
</tr>
|
|
<tr v-for="i in bulkIngredientPreview">
|
|
<td>{{i.quantity}}</td>
|
|
<td>{{i.units}}</td>
|
|
<td>{{i.name}}</td>
|
|
<td>{{i.preparation}}</td>
|
|
</tr>
|
|
</table>
|
|
</div>
|
|
</div>
|
|
<button class="button is-primary" type="button" @click="saveBulkEditing">Save</button>
|
|
<button class="button is-secondary" type="button" @click="cancelBulkEditing">Cancel</button>
|
|
</app-modal>
|
|
|
|
<div>
|
|
<recipe-edit-ingredient-item v-for="(i, idx) in visibleIngredients" :key="i.id" :ingredient="i" :show-labels="idx === 0 || isMobile" @deleteFood="deleteFood"></recipe-edit-ingredient-item>
|
|
</div>
|
|
|
|
<button type="button" class="button is-primary" @click="addIngredient">Add Ingredient</button>
|
|
</div>
|
|
</template>
|
|
|
|
<script>
|
|
|
|
import RecipeEditIngredientItem from "./RecipeEditIngredientItem";
|
|
|
|
import { mapState } from "vuex";
|
|
|
|
export default {
|
|
props: {
|
|
ingredients: {
|
|
required: true,
|
|
type: Array
|
|
}
|
|
},
|
|
|
|
data() {
|
|
return {
|
|
isBulkEditing: false,
|
|
bulkEditText: null
|
|
};
|
|
},
|
|
|
|
computed: {
|
|
...mapState({
|
|
isMobile: state => state.mediaQueries.mobile
|
|
}),
|
|
bulkIngredientPreview() {
|
|
if (this.bulkEditText === null) {
|
|
return [];
|
|
}
|
|
|
|
const regex = /^\s*(?:([\d\/.]+(?:\s+[\d\/]+)?)\s+)?(?:([\w-]+)(?:\s+of)?\s+)?([^,|]+?|.+\|)(?:,\s*([^|]*?))?(?:\s*\[(\d+)\]\s*)?$/i;
|
|
|
|
const magicFunc = function(str) {
|
|
if (str === "-") {
|
|
return "";
|
|
} else {
|
|
return str;
|
|
}
|
|
};
|
|
|
|
const parsed = [];
|
|
const lines = this.bulkEditText.replace("\r", "").split("\n");
|
|
|
|
for (let line of lines) {
|
|
if (line.length === 0) { continue; }
|
|
|
|
const match = line.match(regex);
|
|
|
|
if (match) {
|
|
const matchedName = match[3].replace(/\|\s*$/, "");
|
|
let item = {quantity: magicFunc(match[1]), units: magicFunc(match[2]), name: magicFunc(matchedName), preparation: magicFunc(match[4]), id: match[5] || null};
|
|
parsed.push(item);
|
|
} else {
|
|
parsed.push(null);
|
|
}
|
|
}
|
|
|
|
return parsed;
|
|
},
|
|
|
|
visibleIngredients() {
|
|
return this.ingredients.filter(i => i._destroy !== true);
|
|
}
|
|
},
|
|
|
|
methods: {
|
|
createIngredient() {
|
|
return {
|
|
id: null,
|
|
quantity: null,
|
|
units: null,
|
|
name: null,
|
|
preparation: null,
|
|
ingredient_id: null,
|
|
sort_order: Math.max([0].concat(this.ingredients.map(i => i.sort_order))) + 5
|
|
};
|
|
},
|
|
|
|
addIngredient() {
|
|
this.ingredients.push(this.createIngredient());
|
|
},
|
|
|
|
deleteFood(food) {
|
|
if (food.id) {
|
|
food._destroy = true;
|
|
} else {
|
|
const idx = this.ingredients.findIndex(i => i === food);
|
|
this.ingredients.splice(idx, 1);
|
|
}
|
|
},
|
|
|
|
bulkEditIngredients() {
|
|
this.isBulkEditing = true;
|
|
|
|
let text = [];
|
|
|
|
for (let item of this.visibleIngredients) {
|
|
text.push(
|
|
item.quantity + " " +
|
|
(item.units || "-") + " " +
|
|
(item.name.indexOf(",") >= 0 ? item.name + "|" : item.name) +
|
|
(item.preparation ? (", " + item.preparation) : "") +
|
|
(item.id ? (" [" + item.id + "]") : "")
|
|
);
|
|
}
|
|
|
|
this.bulkEditText = text.join("\n");
|
|
},
|
|
|
|
cancelBulkEditing() {
|
|
this.isBulkEditing = false;
|
|
},
|
|
|
|
saveBulkEditing() {
|
|
const parsed = this.bulkIngredientPreview.filter(i => i !== null);
|
|
const existing = [...this.ingredients];
|
|
const newList = [];
|
|
|
|
for (let parsedIngredient of parsed) {
|
|
let newIngredient = null;
|
|
|
|
if (parsedIngredient.id !== null) {
|
|
let intId = parseInt(parsedIngredient.id);
|
|
let exIdx = existing.findIndex(i => i.id === intId);
|
|
if (exIdx >= 0) {
|
|
let ex = existing[exIdx];
|
|
if (ex.name === parsedIngredient.name) {
|
|
newIngredient = ex;
|
|
existing.splice(exIdx, 1);
|
|
}
|
|
}
|
|
}
|
|
|
|
if (newIngredient === null) {
|
|
newIngredient = this.createIngredient();
|
|
}
|
|
|
|
newIngredient.quantity = parsedIngredient.quantity;
|
|
newIngredient.units = parsedIngredient.units;
|
|
newIngredient.name = parsedIngredient.name;
|
|
newIngredient.preparation = parsedIngredient.preparation;
|
|
newList.push(newIngredient);
|
|
}
|
|
|
|
for (let oldExisting of existing.filter(i => i.id !== null)) {
|
|
newList.push({id: oldExisting.id, _destroy: true});
|
|
}
|
|
|
|
this.ingredients.splice(0);
|
|
let sortIdx = 0;
|
|
for (let n of newList) {
|
|
n.sort_order = sortIdx++;
|
|
this.ingredients.push(n);
|
|
}
|
|
|
|
this.isBulkEditing = false;
|
|
}
|
|
},
|
|
|
|
components: {
|
|
RecipeEditIngredientItem
|
|
}
|
|
}
|
|
|
|
</script>
|
|
|
|
<style lang="scss" scoped>
|
|
|
|
.bulk-input {
|
|
|
|
textarea {
|
|
height: 100%;
|
|
min-height: 15rem;
|
|
}
|
|
}
|
|
|
|
</style> |