This commit is contained in:
Dan Elbert 2018-08-28 16:52:56 -05:00
parent 9f0422acf8
commit 6aa2c8ee4a
8 changed files with 238 additions and 9 deletions

View File

@ -0,0 +1,62 @@
<template>
<div class="dropdown" :class="{'is-active': open}">
<div class="dropdown-trigger">
<slot name="button">
<button type="button" class="button" :class="buttonClass" @click="toggle">
<span>{{ label }}</span>
<app-icon icon="caret-bottom"></app-icon>
</button>
</slot>
</div>
<div class="dropdown-menu">
<div class="dropdown-content">
<slot>
Default Content
</slot>
</div>
</div>
</div>
</template>
<script>
export default {
props: {
open: {
required: true,
type: Boolean
},
label: {
required: false,
type: String,
default: 'Select'
},
buttonClass: {
required: false,
default: ""
}
},
methods: {
toggle() {
if (this.open) {
this.triggerClose();
} else {
this.triggerOpen();
}
},
triggerOpen() {
this.$emit("open");
},
triggerClose() {
this.$emit("close");
}
}
}
</script>

View File

@ -0,0 +1,48 @@
<template>
<div>
<app-validation-errors :errors="validationErrors"></app-validation-errors>
<label class="label is-small">Add New List</label>
<div class="field has-addons">
<div class="control">
<input class="input is-small" type="text" v-model="taskList.name" @keydown="nameKeydownHandler">
</div>
<div class="control">
<button type="button" class="button is-primary is-small" @click="save">Add</button>
</div>
</div>
</div>
</template>
<script>
export default {
props: {
taskList: {
required: true,
type: Object
},
validationErrors: {
required: false,
type: Object,
default: function() { return {}; }
}
},
methods: {
save() {
this.$emit("save");
},
nameKeydownHandler(evt) {
switch (evt.key) {
case "Enter":
evt.preventDefault();
this.save();
}
}
}
}
</script>

View File

@ -2,19 +2,75 @@
<div>
<h1>Tasks</h1>
<select>
<option v-for="l in taskLists" :value="l.id">{{l.name}}</option>
</select>
<app-dropdown button-class="is-primary" :open="showListDropdown" :label="listSelectLabel" @open="showListDropdown = true" @close="showListDropdown = false">
<a class="dropdown-item" href="#" v-for="l in taskLists" @click="selectList(l)">{{l.name}}</a>
<hr class="dropdown-divider" v-if="taskLists.length > 0">
<div class="dropdown-item">
<task-list-mini-form :task-list="newList" :validation-errors="newListValidationErrors" @save="saveNewList"></task-list-mini-form>
</div>
</app-dropdown>
</div>
</template>
<script>
import api from "../lib/Api";
import * as Errors from '../lib/Errors';
import TaskListMiniForm from "./TaskListMiniForm";
const newListTemplate = function() {
return {
name: ''
};
};
export default {
data() {
return {
taskLists: []
taskLists: [],
showListDropdown: false,
currentList: null,
newList: newListTemplate(),
newListValidationErrors: {}
}
},
computed: {
listSelectLabel() {
if (this.currentList === null) {
return "Select or Create a List";
} else {
return this.currentList.name;
}
}
},
methods: {
selectList(l) {
this.currentList = l;
},
saveNewList() {
this.loadResource(
api.postTaskList(this.newList)
.then(() => this.updateData())
.then(() => { this.newList = newListTemplate(); this.newListValidationErrors = {}; } )
.catch(Errors.onlyFor(Errors.ApiValidationError, err => this.newListValidationErrors = err.validationErrors()))
);
},
updateData() {
return this.loadResource(api.getTaskLists(data => this.taskLists = data));
}
},
created() {
this.updateData()
},
components: {
TaskListMiniForm
}
}

View File

@ -357,6 +357,54 @@ class Api {
return this.patch("/logs/" + log.id, this.buildLogParams(log));
}
getTaskLists(dataHandler) {
return this.cacheFirstGet("/task_lists/", {}, dataHandler);
}
buildTaskListParams(taskList) {
return {
task_list: {
name: taskList.name
}
};
}
postTaskList(taskList) {
const params = this.buildTaskListParams(taskList);
return this.post("/task_lists/", params);
}
patchTaskList(taskList) {
const params = this.buildTaskListParams(taskList);
return this.patch(`/task_lists/${taskList.id}`, params);
}
deleteTaskList(taskList) {
return this.del(`/task_lists/${taskList.id}`);
}
buildTaskItemParams(taskItem) {
return {
task_item: {
name: taskItem.name,
quantity: taskItem.quantity
}
}
}
postTaskItem(listId, taskItem) {
return this.post(`/task_lists/${listId}/task_items`)
}
patchTaskItem(listId, taskItem) {
const params = this.buildTaskItemParams(taskItem);
return this.patch(`/task_lists/${listId}/task_items/${taskItem.id}`, params);
}
deleteTaskItem(listId, taskItem) {
return this.del(`/task_lists/${listId}/task_items/${taskItem.id}`);
}
getAdminUserList() {
return this.get("/admin/users");
}

View File

@ -4,6 +4,11 @@ import { mapGetters, mapMutations, mapState } from 'vuex';
import api from "../lib/Api";
Vue.mixin({
data() {
return {
localLoadingCount: 0
};
},
computed: {
...mapGetters([
"isLoading",
@ -12,7 +17,10 @@ Vue.mixin({
]),
...mapState([
"user"
])
]),
localLoading() {
return this.localLoadingCount > 0;
}
},
methods: {
...mapMutations([
@ -23,10 +31,15 @@ Vue.mixin({
loadResource(promise) {
this.setLoading(true);
this.localLoadingCount = this.localLoadingCount + 1;
return promise
.catch(err => this.setError(err))
.then(() => this.setLoading(false));
.then(res => {
this.setLoading(false);
this.localLoadingCount = this.localLoadingCount - 1;
return res;
});
},
checkAuthentication() {

View File

@ -14,6 +14,7 @@ import AppAutocomplete from "../components/AppAutocomplete";
import AppConfirm from "../components/AppConfirm";
import AppDateTime from "../components/AppDateTime";
import AppDatePicker from "../components/AppDatePicker";
import AppDropdown from "../components/AppDropdown";
import AppIcon from "../components/AppIcon";
import AppModal from "../components/AppModal";
import AppNavbar from "../components/AppNavbar";
@ -27,6 +28,7 @@ Vue.component("AppAutocomplete", AppAutocomplete);
Vue.component("AppConfirm", AppConfirm);
Vue.component("AppDateTime", AppDateTime);
Vue.component("AppDatePicker", AppDatePicker);
Vue.component("AppDropdown", AppDropdown);
Vue.component("AppIcon", AppIcon);
Vue.component("AppModal", AppModal);
Vue.component("AppNavbar", AppNavbar);

View File

@ -10,7 +10,6 @@ export default new Vuex.Store({
state: {
updateAvailable: false,
loadingCount: 0,
loading: false,
error: null,
authChecked: false,
user: null,
@ -30,7 +29,7 @@ export default new Vuex.Store({
},
getters: {
isLoading(state) {
return state.loading === true;
return state.loadingCount > 0;
},
isLoggedIn(state) {
return state.user !== null;

View File

@ -2,6 +2,7 @@
@import "~bulma/sass/utilities/_all";
@import "~bulma/sass/base/_all";
@import "~bulma/sass/components/dropdown";
@import "~bulma/sass/components/navbar";
@import "~bulma/sass/components/level";
@import "~bulma/sass/components/message";