ui
This commit is contained in:
parent
9f0422acf8
commit
6aa2c8ee4a
62
app/javascript/components/AppDropdown.vue
Normal file
62
app/javascript/components/AppDropdown.vue
Normal 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>
|
48
app/javascript/components/TaskListMiniForm.vue
Normal file
48
app/javascript/components/TaskListMiniForm.vue
Normal 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>
|
@ -2,19 +2,75 @@
|
|||||||
<div>
|
<div>
|
||||||
<h1>Tasks</h1>
|
<h1>Tasks</h1>
|
||||||
|
|
||||||
<select>
|
<app-dropdown button-class="is-primary" :open="showListDropdown" :label="listSelectLabel" @open="showListDropdown = true" @close="showListDropdown = false">
|
||||||
<option v-for="l in taskLists" :value="l.id">{{l.name}}</option>
|
<a class="dropdown-item" href="#" v-for="l in taskLists" @click="selectList(l)">{{l.name}}</a>
|
||||||
</select>
|
<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>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
|
|
||||||
|
import api from "../lib/Api";
|
||||||
|
import * as Errors from '../lib/Errors';
|
||||||
|
import TaskListMiniForm from "./TaskListMiniForm";
|
||||||
|
|
||||||
|
const newListTemplate = function() {
|
||||||
|
return {
|
||||||
|
name: ''
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
data() {
|
data() {
|
||||||
return {
|
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
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -357,6 +357,54 @@ class Api {
|
|||||||
return this.patch("/logs/" + log.id, this.buildLogParams(log));
|
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() {
|
getAdminUserList() {
|
||||||
return this.get("/admin/users");
|
return this.get("/admin/users");
|
||||||
}
|
}
|
||||||
|
@ -4,6 +4,11 @@ import { mapGetters, mapMutations, mapState } from 'vuex';
|
|||||||
import api from "../lib/Api";
|
import api from "../lib/Api";
|
||||||
|
|
||||||
Vue.mixin({
|
Vue.mixin({
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
localLoadingCount: 0
|
||||||
|
};
|
||||||
|
},
|
||||||
computed: {
|
computed: {
|
||||||
...mapGetters([
|
...mapGetters([
|
||||||
"isLoading",
|
"isLoading",
|
||||||
@ -12,7 +17,10 @@ Vue.mixin({
|
|||||||
]),
|
]),
|
||||||
...mapState([
|
...mapState([
|
||||||
"user"
|
"user"
|
||||||
])
|
]),
|
||||||
|
localLoading() {
|
||||||
|
return this.localLoadingCount > 0;
|
||||||
|
}
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
...mapMutations([
|
...mapMutations([
|
||||||
@ -23,10 +31,15 @@ Vue.mixin({
|
|||||||
|
|
||||||
loadResource(promise) {
|
loadResource(promise) {
|
||||||
this.setLoading(true);
|
this.setLoading(true);
|
||||||
|
this.localLoadingCount = this.localLoadingCount + 1;
|
||||||
|
|
||||||
return promise
|
return promise
|
||||||
.catch(err => this.setError(err))
|
.catch(err => this.setError(err))
|
||||||
.then(() => this.setLoading(false));
|
.then(res => {
|
||||||
|
this.setLoading(false);
|
||||||
|
this.localLoadingCount = this.localLoadingCount - 1;
|
||||||
|
return res;
|
||||||
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
checkAuthentication() {
|
checkAuthentication() {
|
||||||
|
@ -14,6 +14,7 @@ import AppAutocomplete from "../components/AppAutocomplete";
|
|||||||
import AppConfirm from "../components/AppConfirm";
|
import AppConfirm from "../components/AppConfirm";
|
||||||
import AppDateTime from "../components/AppDateTime";
|
import AppDateTime from "../components/AppDateTime";
|
||||||
import AppDatePicker from "../components/AppDatePicker";
|
import AppDatePicker from "../components/AppDatePicker";
|
||||||
|
import AppDropdown from "../components/AppDropdown";
|
||||||
import AppIcon from "../components/AppIcon";
|
import AppIcon from "../components/AppIcon";
|
||||||
import AppModal from "../components/AppModal";
|
import AppModal from "../components/AppModal";
|
||||||
import AppNavbar from "../components/AppNavbar";
|
import AppNavbar from "../components/AppNavbar";
|
||||||
@ -27,6 +28,7 @@ Vue.component("AppAutocomplete", AppAutocomplete);
|
|||||||
Vue.component("AppConfirm", AppConfirm);
|
Vue.component("AppConfirm", AppConfirm);
|
||||||
Vue.component("AppDateTime", AppDateTime);
|
Vue.component("AppDateTime", AppDateTime);
|
||||||
Vue.component("AppDatePicker", AppDatePicker);
|
Vue.component("AppDatePicker", AppDatePicker);
|
||||||
|
Vue.component("AppDropdown", AppDropdown);
|
||||||
Vue.component("AppIcon", AppIcon);
|
Vue.component("AppIcon", AppIcon);
|
||||||
Vue.component("AppModal", AppModal);
|
Vue.component("AppModal", AppModal);
|
||||||
Vue.component("AppNavbar", AppNavbar);
|
Vue.component("AppNavbar", AppNavbar);
|
||||||
|
@ -10,7 +10,6 @@ export default new Vuex.Store({
|
|||||||
state: {
|
state: {
|
||||||
updateAvailable: false,
|
updateAvailable: false,
|
||||||
loadingCount: 0,
|
loadingCount: 0,
|
||||||
loading: false,
|
|
||||||
error: null,
|
error: null,
|
||||||
authChecked: false,
|
authChecked: false,
|
||||||
user: null,
|
user: null,
|
||||||
@ -30,7 +29,7 @@ export default new Vuex.Store({
|
|||||||
},
|
},
|
||||||
getters: {
|
getters: {
|
||||||
isLoading(state) {
|
isLoading(state) {
|
||||||
return state.loading === true;
|
return state.loadingCount > 0;
|
||||||
},
|
},
|
||||||
isLoggedIn(state) {
|
isLoggedIn(state) {
|
||||||
return state.user !== null;
|
return state.user !== null;
|
||||||
|
@ -2,6 +2,7 @@
|
|||||||
|
|
||||||
@import "~bulma/sass/utilities/_all";
|
@import "~bulma/sass/utilities/_all";
|
||||||
@import "~bulma/sass/base/_all";
|
@import "~bulma/sass/base/_all";
|
||||||
|
@import "~bulma/sass/components/dropdown";
|
||||||
@import "~bulma/sass/components/navbar";
|
@import "~bulma/sass/components/navbar";
|
||||||
@import "~bulma/sass/components/level";
|
@import "~bulma/sass/components/level";
|
||||||
@import "~bulma/sass/components/message";
|
@import "~bulma/sass/components/message";
|
||||||
|
Loading…
Reference in New Issue
Block a user