UI updates; added delete
This commit is contained in:
parent
c3dbefbb4f
commit
3c87ee8083
@ -74,12 +74,9 @@ class RecipesController < ApplicationController
|
||||
def destroy
|
||||
ensure_owner(@recipe) do
|
||||
@recipe.deleted = true
|
||||
@recipe.save!(validate: false)
|
||||
|
||||
if @recipe.save(validate: false)
|
||||
redirect_to recipes_url, notice: 'Recipe was successfully destroyed.'
|
||||
else
|
||||
redirect_to recipes_url, error: 'Recipe could not be destroyed.'
|
||||
end
|
||||
render json: { success: true }
|
||||
end
|
||||
end
|
||||
|
||||
|
47
app/javascript/components/AppConfirm.vue
Normal file
47
app/javascript/components/AppConfirm.vue
Normal file
@ -0,0 +1,47 @@
|
||||
<template>
|
||||
<app-modal :open="open" :title="message" @dismiss="runCancel">
|
||||
<div class="buttons">
|
||||
<button type="button" class="button is-primary" @click="runConfirm">OK</button>
|
||||
<button type="button" class="button" @click="runCancel">Cancel</button>
|
||||
</div>
|
||||
</app-modal>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
|
||||
export default {
|
||||
props: {
|
||||
cancel: {
|
||||
type: Function,
|
||||
required: true
|
||||
},
|
||||
|
||||
confirm: {
|
||||
type: Function,
|
||||
required: true
|
||||
},
|
||||
|
||||
message: {
|
||||
type: String,
|
||||
required: false,
|
||||
default: 'Are you sure?'
|
||||
},
|
||||
|
||||
open: {
|
||||
type: Boolean,
|
||||
required: true
|
||||
}
|
||||
},
|
||||
|
||||
methods: {
|
||||
runConfirm() {
|
||||
this.confirm();
|
||||
},
|
||||
|
||||
runCancel() {
|
||||
this.cancel();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
</script>
|
@ -1,6 +1,6 @@
|
||||
<template>
|
||||
<div ref="container">
|
||||
<div ref="modal" :class="['popup', 'modal', { 'is-active': open && error === null }]">
|
||||
<div ref="modal" :class="['popup', 'modal', { 'is-wide': wide, 'is-active': open && error === null }]">
|
||||
<div class="modal-background" @click="close"></div>
|
||||
<div class="modal-card">
|
||||
<header class="modal-card-head">
|
||||
@ -28,7 +28,11 @@
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
title: String
|
||||
title: String,
|
||||
wide: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
}
|
||||
},
|
||||
|
||||
mounted() {
|
||||
|
@ -120,9 +120,6 @@
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.bulk-input {
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.directions-input {
|
||||
height: 100%;
|
||||
|
@ -1,10 +1,10 @@
|
||||
<template>
|
||||
<div>
|
||||
<button type="button" class="button is-primary" @click="bulkEditIngredients">Bulk Edit</button>
|
||||
<app-modal :open="isBulkEditing" title="Edit Ingredients" @dismiss="cancelBulkEditing">
|
||||
<app-modal wide :open="isBulkEditing" title="Edit Ingredients" @dismiss="cancelBulkEditing">
|
||||
<div class="columns">
|
||||
<div class="column is-half">
|
||||
<textarea ref="bulkEditTextarea" class="textarea is-size-7-mobile bulk-input" v-model="bulkEditText"></textarea>
|
||||
<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">
|
||||
@ -27,7 +27,9 @@
|
||||
<button class="button is-secondary" type="button" @click="cancelBulkEditing">Cancel</button>
|
||||
</app-modal>
|
||||
|
||||
<recipe-edit-ingredient-item v-for="(i, idx) in visibleIngredients" :key="i.id" :ingredient="i" :show-labels="idx === 0 || isMobile" @deleteIngredient="deleteIngredient"></recipe-edit-ingredient-item>
|
||||
<div>
|
||||
<recipe-edit-ingredient-item v-for="(i, idx) in visibleIngredients" :key="i.id" :ingredient="i" :show-labels="idx === 0 || isMobile" @deleteIngredient="deleteIngredient"></recipe-edit-ingredient-item>
|
||||
</div>
|
||||
|
||||
<button type="button" class="button is-primary" @click="addIngredient">Add Ingredient</button>
|
||||
</div>
|
||||
@ -63,7 +65,7 @@
|
||||
return [];
|
||||
}
|
||||
|
||||
const regex = /^(?:([\d\/.]+(?:\s+[\d\/]+)?)\s+)?(?:([\w-]+)(?:\s+of)?\s+)?([^,|]+?|.+\|)(?:,\s*([^|]*?))?(?:\s*\[(\d+)\]\s*)?$/i;
|
||||
const regex = /^\s*(?:([\d\/.]+(?:\s+[\d\/]+)?)\s+)?(?:([\w-]+)(?:\s+of)?\s+)?([^,|]+?|.+\|)(?:,\s*([^|]*?))?(?:\s*\[(\d+)\]\s*)?$/i;
|
||||
|
||||
const magicFunc = function(str) {
|
||||
if (str === "-") {
|
||||
@ -201,6 +203,12 @@
|
||||
|
||||
<style lang="scss" scoped>
|
||||
|
||||
.bulk-input {
|
||||
|
||||
textarea {
|
||||
height: 100%;
|
||||
min-height: 15rem;
|
||||
}
|
||||
}
|
||||
|
||||
</style>
|
@ -1,5 +1,5 @@
|
||||
<template>
|
||||
<div class="columns is-mobile">
|
||||
<div class="columns is-mobile edit-ingredient-item">
|
||||
<div class="column">
|
||||
|
||||
<div class="columns is-multiline is-mobile">
|
||||
@ -37,8 +37,8 @@
|
||||
</div>
|
||||
<div class="column is-narrow">
|
||||
<span class="label is-small-mobile" v-if="showLabels"> </span>
|
||||
<button type="button" class="button is-danger is-small-mobile" @click="deleteIngredient(ingredient)">
|
||||
<app-icon icon="x"></app-icon>
|
||||
<button type="button" class="button is-danger is-small" @click="deleteIngredient(ingredient)">
|
||||
<app-icon icon="x" size="md"></app-icon>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
@ -99,4 +99,16 @@
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
|
||||
@import "../styles/variables";
|
||||
|
||||
.edit-ingredient-item {
|
||||
border-bottom: solid 1px $grey-light;
|
||||
margin-bottom: 1.25rem;
|
||||
|
||||
&:last-child {
|
||||
border-bottom: none;
|
||||
}
|
||||
}
|
||||
|
||||
</style>
|
@ -38,7 +38,7 @@
|
||||
<router-link v-if="isLoggedIn" class="button" :to="{name: 'edit_ingredient', params: { id: i.id } }">
|
||||
<app-icon icon="pencil"></app-icon>
|
||||
</router-link>
|
||||
<button v-if="isLoggedIn" type="button" class="button is-danger">
|
||||
<button v-if="isLoggedIn" type="button" class="button is-danger" @click="deleteIngredient(i)">
|
||||
<app-icon icon="x"></app-icon>
|
||||
</button>
|
||||
</td>
|
||||
@ -52,6 +52,8 @@
|
||||
<router-link v-if="isLoggedIn" :to="{name: 'new_ingredient'}" class="button is-primary">Create Ingredient</router-link>
|
||||
</div>
|
||||
|
||||
<app-confirm :open="showConfirmIngredientDelete" :message="confirmIngredientDeleteMessage" :cancel="ingredientDeleteCancel" :confirm="ingredientDeleteConfirm"></app-confirm>
|
||||
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@ -64,6 +66,7 @@
|
||||
data() {
|
||||
return {
|
||||
ingredientData: null,
|
||||
ingredientForDeletion: null,
|
||||
search: {
|
||||
page: 1,
|
||||
per: 25,
|
||||
@ -93,6 +96,18 @@
|
||||
return this.ingredientData.current_page
|
||||
}
|
||||
return 0;
|
||||
},
|
||||
|
||||
showConfirmIngredientDelete() {
|
||||
return this.ingredientForDeletion !== null;
|
||||
},
|
||||
|
||||
confirmIngredientDeleteMessage() {
|
||||
if (this.ingredientForDeletion !== null) {
|
||||
return `Are you sure you want to delete ${this.ingredientForDeletion.name}?`;
|
||||
} else {
|
||||
return "??";
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
@ -102,11 +117,33 @@
|
||||
},
|
||||
|
||||
getList: debounce(function() {
|
||||
this.loadResource(
|
||||
return this.loadResource(
|
||||
api.getIngredientList(this.search.page, this.search.per, this.search.name)
|
||||
.then(data => this.ingredientData = data)
|
||||
);
|
||||
}, 500, {leading: true, trailing: true})
|
||||
}, 500, {leading: true, trailing: true}),
|
||||
|
||||
deleteIngredient(ingredient) {
|
||||
this.ingredientForDeletion = ingredient;
|
||||
},
|
||||
|
||||
ingredientDeleteCancel() {
|
||||
this.ingredientForDeletion = null;
|
||||
},
|
||||
|
||||
ingredientDeleteConfirm() {
|
||||
if (this.ingredientForDeletion !== null) {
|
||||
this.loadResource(
|
||||
api.deleteIngredient(this.ingredientForDeletion.id).then(res => {
|
||||
this.ingredientForDeletion = null;
|
||||
return this.getList();
|
||||
})
|
||||
);
|
||||
|
||||
console.log("This is where the thing happens!!");
|
||||
this.ingredientForDeletion = null;
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
created() {
|
||||
|
@ -48,7 +48,7 @@
|
||||
|
||||
created() {
|
||||
this.loadResource(
|
||||
api.getRecipe(this.recipeId, data => { this.recipe = data; return data; })
|
||||
api.getRecipe(this.recipeId, null, null, null, data => { this.recipe = data; return data; })
|
||||
);
|
||||
},
|
||||
|
||||
|
@ -58,7 +58,7 @@
|
||||
<router-link v-if="isLoggedIn" :to="{name: 'edit_recipe', params: { id: r.id } }" class="button is-primary">
|
||||
<app-icon icon="pencil" size="md"></app-icon>
|
||||
</router-link>
|
||||
<button v-if="isLoggedIn" type="button" class="button is-danger">
|
||||
<button v-if="isLoggedIn" type="button" class="button is-danger" @click="deleteRecipe(r)">
|
||||
<app-icon icon="x" size="md"></app-icon>
|
||||
</button>
|
||||
</td>
|
||||
@ -68,6 +68,8 @@
|
||||
|
||||
<app-pager :current-page="currentPage" :total-pages="totalPages" paged-item-name="recipe" @changePage="changePage"></app-pager>
|
||||
|
||||
<app-confirm :open="showConfirmRecipeDelete" :message="confirmRecipeDeleteMessage" :cancel="recipeDeleteCancel" :confirm="recipeDeleteConfirm"></app-confirm>
|
||||
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@ -80,6 +82,7 @@
|
||||
data() {
|
||||
return {
|
||||
recipeData: null,
|
||||
recipeForDeletion: null,
|
||||
search: {
|
||||
sortColumn: 'created_at',
|
||||
sortDirection: 'desc',
|
||||
@ -123,6 +126,18 @@
|
||||
return this.recipeData.current_page;
|
||||
}
|
||||
return 0;
|
||||
},
|
||||
|
||||
showConfirmRecipeDelete() {
|
||||
return this.recipeForDeletion !== null;
|
||||
},
|
||||
|
||||
confirmRecipeDeleteMessage() {
|
||||
if (this.showConfirmRecipeDelete) {
|
||||
return `Are you sure you want to delete ${this.recipeForDeletion.name}?`;
|
||||
} else {
|
||||
return "??";
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
@ -140,8 +155,27 @@
|
||||
}
|
||||
},
|
||||
|
||||
deleteRecipe(recipe) {
|
||||
this.recipeForDeletion = recipe;
|
||||
},
|
||||
|
||||
recipeDeleteConfirm() {
|
||||
if (this.recipeForDeletion !== null) {
|
||||
this.loadResource(
|
||||
api.deleteRecipe(this.recipeForDeletion.id).then(() => {
|
||||
this.recipeForDeletion = null;
|
||||
return this.getList();
|
||||
})
|
||||
);
|
||||
}
|
||||
},
|
||||
|
||||
recipeDeleteCancel() {
|
||||
this.recipeForDeletion = null;
|
||||
},
|
||||
|
||||
getList: debounce(function() {
|
||||
this.loadResource(
|
||||
return this.loadResource(
|
||||
api.getRecipeList(this.search.page, this.search.per, this.search.sortColumn, this.search.sortDirection, this.search.name, this.search.tags, data => this.recipeData = data)
|
||||
);
|
||||
}, 500, {leading: true, trailing: true}),
|
||||
|
@ -1,7 +1,7 @@
|
||||
<template>
|
||||
<div>
|
||||
|
||||
<button class="button" type="button" @click="showLogin = true">
|
||||
<button class="button" type="button" @click="openDialog">
|
||||
Login
|
||||
</button>
|
||||
|
||||
@ -16,7 +16,7 @@
|
||||
<div class="field">
|
||||
<label class="label">Username</label>
|
||||
<div class="control has-icons-left">
|
||||
<input class="input" type="text" placeholder="username" v-model="username">
|
||||
<input class="input" type="text" placeholder="username" ref="usernameInput" v-model="username">
|
||||
<app-icon icon="person" size="sm" class="is-left"></app-icon>
|
||||
</div>
|
||||
</div>
|
||||
@ -71,8 +71,13 @@
|
||||
'setUser'
|
||||
]),
|
||||
|
||||
openDialog() {
|
||||
this.showLogin = true;
|
||||
this.$nextTick(() => this.$refs.usernameInput.focus());
|
||||
},
|
||||
|
||||
login() {
|
||||
if (this.username !== '' && this.password != '') {
|
||||
if (this.username !== '' && this.password !== '') {
|
||||
this.loadResource(api.postLogin(this.username, this.password).then(data => {
|
||||
if (data.success) {
|
||||
this.setUser(data.user);
|
||||
|
@ -187,6 +187,10 @@ class Api {
|
||||
return this.post("/recipes/", this.buildRecipeParams(recipe));
|
||||
}
|
||||
|
||||
deleteRecipe(id) {
|
||||
return this.del("/recipes/" + id);
|
||||
}
|
||||
|
||||
postPreviewSteps(step_text) {
|
||||
const params = {
|
||||
step_text: step_text
|
||||
@ -284,6 +288,10 @@ class Api {
|
||||
return this.patch("/ingredients/" + ingredient.id, this.buildIngredientParams(ingredient));
|
||||
}
|
||||
|
||||
deleteIngredient(id) {
|
||||
return this.del("/ingredients/" + id);
|
||||
}
|
||||
|
||||
postIngredientSelectNdbn(ingredient) {
|
||||
const url = ingredient.id ? "/ingredients/" + ingredient.id + "/select_ndbn" : "/ingredients/select_ndbn";
|
||||
return this.post(url, this.buildIngredientParams(ingredient));
|
||||
|
@ -11,6 +11,7 @@ import '../lib/GlobalMixins';
|
||||
import App from '../components/App';
|
||||
|
||||
import AppAutocomplete from "../components/AppAutocomplete";
|
||||
import AppConfirm from "../components/AppConfirm";
|
||||
import AppDateTime from "../components/AppDateTime";
|
||||
import AppDatePicker from "../components/AppDatePicker";
|
||||
import AppIcon from "../components/AppIcon";
|
||||
@ -22,6 +23,7 @@ import AppTagEditor from "../components/AppTagEditor";
|
||||
import AppTextField from "../components/AppTextField";
|
||||
|
||||
Vue.component("AppAutocomplete", AppAutocomplete);
|
||||
Vue.component("AppConfirm", AppConfirm);
|
||||
Vue.component("AppDateTime", AppDateTime);
|
||||
Vue.component("AppDatePicker", AppDatePicker);
|
||||
Vue.component("AppIcon", AppIcon);
|
||||
|
18
app/javascript/styles/_wide_modal.scss
Normal file
18
app/javascript/styles/_wide_modal.scss
Normal file
@ -0,0 +1,18 @@
|
||||
|
||||
@include until($desktop) {
|
||||
.modal.is-wide {
|
||||
.modal-content, .modal-card {
|
||||
margin: 0 20px;
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@include from($desktop) {
|
||||
.modal.is-wide {
|
||||
.modal-content, .modal-card {
|
||||
margin: 0 auto;
|
||||
width: 1000px;
|
||||
}
|
||||
}
|
||||
}
|
@ -12,6 +12,7 @@
|
||||
@import "~bulma/sass/layout/section";
|
||||
|
||||
@import "./responsive_controls";
|
||||
@import "./wide_modal";
|
||||
|
||||
html {
|
||||
height: 100%;
|
||||
|
Loading…
Reference in New Issue
Block a user