parsley/app/javascript/components/RecipeEditIngredientEditor.vue
Dan Elbert ffed63e0b3
Some checks failed
parsley/pipeline/head There was a failure building this commit
fixed
2018-09-12 14:17:18 -05:00

222 lines
5.9 KiB
Vue

<template>
<div>
<h3 class="title is-4">
Ingredients
<button type="button" class="button is-primary" @click="bulkEditIngredients">Bulk Edit</button>
</h3>
<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() {
const sort_orders = this.ingredients.map(i => i.sort_order);
sort_orders.push(0);
const next_sort_order = Math.max(...sort_orders) + 5;
return {
id: null,
quantity: null,
units: null,
name: null,
preparation: null,
ingredient_id: null,
sort_order: next_sort_order
};
},
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>