basic version of the webApp
This commit is contained in:
parent
cc7df7914b
commit
ea03547a10
4 changed files with 351 additions and 73 deletions
189
src/App.vue
189
src/App.vue
|
@ -1,56 +1,96 @@
|
|||
<template>
|
||||
<div id="app">
|
||||
<div id="datepicker">
|
||||
<datepicker :value="state.date" :inline="true" v-on:selected="getDay"></datepicker>
|
||||
</div>
|
||||
<div id="panel">
|
||||
<ul class="groups">
|
||||
<li v-for="group in state.day.groups || []">
|
||||
<div>{{ group.group }}</div>
|
||||
<ul class="attendees">
|
||||
<li v-for="attendee in group.attendees || []">
|
||||
{{attendee.name}}
|
||||
</li>
|
||||
<li class="add-attendee">add: <input v-on:keyup.enter="addAttendee(group.group, newAttendee)" v-model="newAttendee" /></li>
|
||||
</ul>
|
||||
</li>
|
||||
<li class="add-group"><input v-model="newGroup" /></a>
|
||||
<ul v-if="newGroup">
|
||||
<li class="add-attendee">add: <input v-on:keyup.enter="addAttendee(newGroup, newAttendee)" v-model="newAttendee" /></li>
|
||||
</ul>
|
||||
</ul>
|
||||
</div>
|
||||
<div>
|
||||
<md-toolbar class="md-dense">
|
||||
<h2 id="toolbar-title" class="md-title">ibt2</h2>
|
||||
<span v-if="loggedInUser.username">
|
||||
Logged in: {{ loggedInUser.username }}
|
||||
<md-button class="md-icon-button" @click="logout()">
|
||||
<md-icon>exit_to_app</md-icon>
|
||||
</md-button>
|
||||
</span>
|
||||
<span v-else>
|
||||
<md-button v-show="!showLoginForm" class="md-icon-button" @click="focusToLoginForm()">
|
||||
<md-icon>power_settings_new</md-icon>
|
||||
</md-button>
|
||||
<span v-show="showLoginForm">
|
||||
<md-input-container id="login-form" md-inline>
|
||||
<md-input ref="usernameInput" @keyup.enter.native="focusToPassword()" v-model="username" placeholder="username" md-inline />
|
||||
<md-input ref="passwordInput" @keyup.enter.native="login()" v-model="password" placeholder="password" md-line />
|
||||
</md-input-container>
|
||||
</span>
|
||||
</span>
|
||||
</md-toolbar>
|
||||
<md-layout md-gutter md-row>
|
||||
<md-layout md-column md-flex="20" md-gutter>
|
||||
<datepicker id="datepicker" :value="date" :inline="true" @selected="getDay"></datepicker>
|
||||
</md-layout>
|
||||
<md-layout id="panel" md-column>
|
||||
<md-layout md-row>
|
||||
<group v-for="group in day.groups || []" :group="group" :day="day.day" new-attendee="" @updated="reload" />
|
||||
<group :add-new-group="true" :day="day.day" new-attendee="" new-group="" @updated="reload" />
|
||||
</md-layout>
|
||||
</md-layout>
|
||||
</md-layout>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
|
||||
import Datepicker from 'vuejs-datepicker';
|
||||
import Group from './Group';
|
||||
|
||||
export default {
|
||||
data () {
|
||||
return {
|
||||
state: {
|
||||
date: new Date(),
|
||||
day: {},
|
||||
},
|
||||
newAttendee: null,
|
||||
newGroup: null
|
||||
date: null, // a Date object representing the selected date
|
||||
day: {},
|
||||
daysSummary: {},
|
||||
username: '',
|
||||
password: '',
|
||||
showLoginForm: false,
|
||||
loggedInUser: {username: ''}
|
||||
}
|
||||
},
|
||||
|
||||
beforeCreate: function() {
|
||||
this.daysUrl = this.$resource('days{/day}');
|
||||
this.attendeesUrl = this.$resource('attendees{/id}');
|
||||
this.currentUserUrl = this.$resource('users/current');
|
||||
this.loginUrl = this.$resource('login');
|
||||
this.logoutUrl = this.$resource('logout');
|
||||
},
|
||||
|
||||
mounted: function() {
|
||||
var ym = this.dateToString(this.state.date, true);
|
||||
this.getSummary({start: ym, end: ym});
|
||||
this.getDay(this.state.date);
|
||||
this.getUserInfo();
|
||||
var [year, month, day] = (this.$route.params.day || '').split('-');
|
||||
year = parseInt(year);
|
||||
month = parseInt(month) - 1;
|
||||
day = parseInt(day);
|
||||
if (!isNaN(year) && !isNaN(month) && !isNaN(day)) {
|
||||
this.date = new Date(year, month, day);
|
||||
}
|
||||
if (!(this.date && !isNaN(this.date.getTime()))) {
|
||||
this.date = new Date();
|
||||
}
|
||||
this.reload();
|
||||
},
|
||||
|
||||
methods: {
|
||||
focusToLoginForm() {
|
||||
this.showLoginForm = true;
|
||||
var that = this;
|
||||
setTimeout(function() { that.$refs.usernameInput.$el.focus(); }, 400);
|
||||
},
|
||||
|
||||
focusToPassword() {
|
||||
this.$refs.passwordInput.$el.focus();
|
||||
},
|
||||
|
||||
reload() {
|
||||
var ym = this.dateToString(this.date, true);
|
||||
this.getSummary({start: ym, end: ym});
|
||||
this.getDay();
|
||||
},
|
||||
|
||||
dateToString(date, excludeDay) {
|
||||
var year = '' + date.getFullYear();
|
||||
var month = '' + (date.getMonth() + 1);
|
||||
|
@ -65,8 +105,6 @@ export default {
|
|||
},
|
||||
|
||||
getSummary(params) {
|
||||
console.log('getSummary');
|
||||
console.log(params);
|
||||
if (!params) {
|
||||
params = {};
|
||||
}
|
||||
|
@ -74,50 +112,73 @@ export default {
|
|||
this.daysUrl.query(params).then((response) => {
|
||||
return response.json();
|
||||
}, (response) => {
|
||||
alert('failed get resource');
|
||||
alert('getSummary: failed to get resource');
|
||||
}).then((json) => {
|
||||
console.log('summary data');
|
||||
console.log(json);
|
||||
this.daysSummary = json;
|
||||
});
|
||||
},
|
||||
|
||||
getDay(day) {
|
||||
console.log("getDay");
|
||||
console.log(day);
|
||||
if (day) {
|
||||
if (day instanceof Date) {
|
||||
this.date = day;
|
||||
day = this.dateToString(day);
|
||||
} else if (this.date && this.date instanceof Date) {
|
||||
day = this.dateToString(this.date);
|
||||
} else {
|
||||
day = this.state.day.day;
|
||||
var today = new Date();
|
||||
day = this.dateToString(today);
|
||||
this.date = today;
|
||||
}
|
||||
this.$router.push('/day/' + day);
|
||||
this.daysUrl.get({day: day}).then((response) => {
|
||||
return response.json();
|
||||
}, (response) => {
|
||||
alert('failed get resource');
|
||||
}).then((json) => {
|
||||
console.log('day data');
|
||||
console.log(json);
|
||||
this.state.day = json;
|
||||
alert('getDay: failed to get resource');
|
||||
}).then((dayData) => {
|
||||
if (!dayData.day) {
|
||||
dayData.day = day;
|
||||
}
|
||||
this.day = dayData;
|
||||
});
|
||||
},
|
||||
|
||||
addAttendee(group, newAttendee) {
|
||||
console.log(group);
|
||||
console.log(newAttendee);
|
||||
this.newAttendee = '';
|
||||
this.attendeesUrl.save({day: this.state.day.day, group: group, name: newAttendee}).then((response) => {
|
||||
login() {
|
||||
this.loginUrl.save({username: this.username, password: this.password}).then((response) => {
|
||||
return response.json();
|
||||
}, (response) => {
|
||||
alert('failed get resource');
|
||||
alert('login: failed to get resource');
|
||||
}).then((data) => {
|
||||
this.showLoginForm = false;
|
||||
this.getUserInfo();
|
||||
});
|
||||
},
|
||||
|
||||
logout() {
|
||||
this.logoutUrl.get().then((response) => {
|
||||
return response.json();
|
||||
}, (response) => {
|
||||
alert('logout: failed to get resource');
|
||||
}).then((json) => {
|
||||
console.log('attendee data');
|
||||
console.log(json);
|
||||
this.getDay();
|
||||
this.loggedInUser = {};
|
||||
});
|
||||
},
|
||||
|
||||
getUserInfo(callback) {
|
||||
this.currentUserUrl.get().then((response) => {
|
||||
return response.json();
|
||||
}, (response) => {
|
||||
alert('getUserInfo: failed to get resource');
|
||||
}).then((data) => {
|
||||
this.loggedInUser = data || {};
|
||||
if (callback) {
|
||||
callback(this.loggedInUser);
|
||||
}
|
||||
});
|
||||
}
|
||||
},
|
||||
|
||||
components: {
|
||||
Datepicker
|
||||
Datepicker, Group
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
@ -128,6 +189,22 @@ export default {
|
|||
-webkit-font-smoothing: antialiased;
|
||||
-moz-osx-font-smoothing: grayscale;
|
||||
color: #2c3e50;
|
||||
margin-top: 60px;
|
||||
margin-top: 0px;
|
||||
}
|
||||
|
||||
#datepicker {
|
||||
padding: 10px;
|
||||
}
|
||||
|
||||
#panel .md-layout {
|
||||
flex: initial;
|
||||
}
|
||||
|
||||
#toolbar-title {
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
#login-form {
|
||||
width: 200px;
|
||||
}
|
||||
</style>
|
||||
|
|
71
src/Attendee.vue
Normal file
71
src/Attendee.vue
Normal file
|
@ -0,0 +1,71 @@
|
|||
<template>
|
||||
<md-list-item :key="attendee._id">
|
||||
<md-icon>person</md-icon>
|
||||
<span v-if="!edit">{{attendee.name}}</span>
|
||||
<md-input-container md-inline v-if="edit">
|
||||
<md-input @keyup.enter.native="updateAttendee()" v-model="attendee.name" ref="updateAttendeeName" />
|
||||
</md-input-container>
|
||||
<md-button class="md-icon-button md-list-action" @click="editAttendee()">
|
||||
<md-icon>edit</md-icon>
|
||||
</md-button>
|
||||
<md-button class="md-icon-button md-list-action" @click="deleteAttendee()">
|
||||
<md-icon>cancel</md-icon>
|
||||
</md-button>
|
||||
</md-list-item>
|
||||
</template>
|
||||
<script>
|
||||
|
||||
export default {
|
||||
props: {attendee: {default: {}}},
|
||||
|
||||
data: function () {
|
||||
return {
|
||||
edit: false
|
||||
}
|
||||
},
|
||||
|
||||
beforeCreate: function() {
|
||||
this.attendeesUrl = this.$resource('attendees{/id}');
|
||||
},
|
||||
|
||||
methods: {
|
||||
editAttendee() {
|
||||
this.edit = true;
|
||||
// FIXME: it's so wrong it hurts, but any other attempt to set the focus
|
||||
// failed, being called too early. Also, I don't know how I can access
|
||||
// Vue.nextTick from here.
|
||||
var that = this;
|
||||
setTimeout(function() { that.$refs.updateAttendeeName.$el.focus(); }, 400);
|
||||
},
|
||||
|
||||
updateAttendee() {
|
||||
this.attendeesUrl.update({id: this.attendee._id}, this.attendee).then((response) => {
|
||||
return response.json();
|
||||
}, (response) => {
|
||||
alert('updateAttendee: failed to update resource');
|
||||
}).then((json) => {
|
||||
this.edit = false;
|
||||
this.$emit('updated');
|
||||
});
|
||||
},
|
||||
|
||||
deleteAttendee() {
|
||||
this.attendeesUrl.delete({id: this.attendee._id}).then((response) => {
|
||||
return response.json();
|
||||
}, (response) => {
|
||||
alert('deleteAttendee: failed to delete resource');
|
||||
}).then((json) => {
|
||||
this.$emit('updated');
|
||||
});
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
</script>
|
||||
<style>
|
||||
|
||||
.md-list-item .md-list-item-holder>.md-icon:first-child {
|
||||
margin-right: 16px;
|
||||
}
|
||||
|
||||
</style>
|
124
src/Group.vue
Normal file
124
src/Group.vue
Normal file
|
@ -0,0 +1,124 @@
|
|||
<template>
|
||||
<md-layout class="group-layout" md-col gutter="120" md-align="start">
|
||||
<md-card v-if="!addNewGroup" md-with-hover @mouseenter.native="focusToNewAttendee()">
|
||||
<md-card-header class="group-header">
|
||||
<md-layout md-row>
|
||||
<div class="md-title">
|
||||
<md-icon class="group-icon">folder_open</md-icon> Group: {{ group.group }}
|
||||
</div>
|
||||
</md-layout>
|
||||
</md-card-header>
|
||||
<md-card-content>
|
||||
<md-list md-dense>
|
||||
<attendee v-for="attendee in group.attendees || []" :attendee="attendee" @updated="reload" />
|
||||
<md-list-item class="attendee-add">
|
||||
<md-icon>person_add</md-icon>
|
||||
<md-input-container class="new-attendee">
|
||||
<md-input ref="newAttendeeInput" @keyup.enter.native="addAttendee(group.group, newAttendee)" v-model="newAttendee" class="attendee-add-name" />
|
||||
</md-input-container>
|
||||
</md-list-item>
|
||||
</md-list>
|
||||
</md-card-content>
|
||||
</md-card>
|
||||
<md-card v-if="addNewGroup" md-with-hover @mouseenter.native="focusToNewGroup()" md-align="start">
|
||||
<md-card-header class="new-group-header">
|
||||
<div class="md-title">
|
||||
<md-input-container class="new-group">
|
||||
<md-icon>create_new_folder</md-icon> <md-input ref="newGroup" v-model="newGroup" @keyup.enter.native="focusToNewAttendee()" class="group-add-name" placeholder="new group" />
|
||||
</md-input-container>
|
||||
</div>
|
||||
</md-card-header>
|
||||
<md-card-content>
|
||||
<md-list v-show="newGroup">
|
||||
<md-list-item class="attendee-add">
|
||||
<md-icon>person_add</md-icon>
|
||||
<md-input-container md-inline>
|
||||
<md-input ref="newAttendeeInput" @keyup.enter.native="addAttendee(newGroup, newAttendee)" v-model="newAttendee" class="attendee-add-name" />
|
||||
</md-input-container>
|
||||
</md-list-item>
|
||||
</md-list>
|
||||
</md-card-content>
|
||||
</md-card>
|
||||
</md-layout>
|
||||
</template>
|
||||
<script>
|
||||
|
||||
import Attendee from './Attendee';
|
||||
|
||||
export default {
|
||||
props: {group: {}, day: {}, addNewGroup: {default: false}},
|
||||
|
||||
data: function () {
|
||||
return { newAttendee: '', newGroup: '' }
|
||||
},
|
||||
|
||||
beforeCreate: function() {
|
||||
this.attendeesUrl = this.$resource('attendees{/id}');
|
||||
},
|
||||
|
||||
methods: {
|
||||
reset() {
|
||||
this.newAttendee = '';
|
||||
this.newGroup = '';
|
||||
},
|
||||
|
||||
reload() {
|
||||
this.$emit('updated');
|
||||
this.focusToNewAttendee();
|
||||
},
|
||||
|
||||
focusToNewGroup() {
|
||||
this.$refs.newGroup.$el.focus();
|
||||
},
|
||||
|
||||
focusToNewAttendee() {
|
||||
this.$refs.newAttendeeInput.$el.focus();
|
||||
},
|
||||
|
||||
addAttendee(group, newAttendee) {
|
||||
this.attendeesUrl.save({day: this.day, group: group, name: newAttendee}).then((response) => {
|
||||
return response.json();
|
||||
}, (response) => {
|
||||
alert('addAttendee: failed to get resource');
|
||||
}).then((json) => {
|
||||
this.reset();
|
||||
this.$emit('updated');
|
||||
});
|
||||
}
|
||||
},
|
||||
|
||||
components: { Attendee }
|
||||
};
|
||||
|
||||
</script>
|
||||
<style>
|
||||
.group-layout {
|
||||
padding: 10px;
|
||||
}
|
||||
|
||||
.new-group-header {
|
||||
background-color: lightsteelblue;
|
||||
padding-top: 0px !important;
|
||||
padding-bottom: 0px !important;
|
||||
}
|
||||
|
||||
.new-group-header .md-title {
|
||||
margin-top: 0px !important;
|
||||
}
|
||||
|
||||
.group-header {
|
||||
background-color: lightblue;
|
||||
}
|
||||
|
||||
.group-icon {
|
||||
vertical-align: text-top;
|
||||
}
|
||||
|
||||
.new-attendee {
|
||||
width: 50px;
|
||||
}
|
||||
|
||||
.new-group {
|
||||
width: 260px;
|
||||
}
|
||||
</style>
|
40
src/main.js
40
src/main.js
|
@ -1,22 +1,28 @@
|
|||
// The Vue build version to load with the `import` command
|
||||
// (runtime-only or standalone) has been set in webpack.base.conf with an alias.
|
||||
import Vue from 'vue'
|
||||
import App from './App'
|
||||
require("vue-resource")
|
||||
/*
|
||||
import 'jquery/dist/jquery.min.js'
|
||||
import 'materialize-css/bin/materialize.css'
|
||||
import 'materialize-css/bin/materialize.js'
|
||||
require("material-ui-vue")
|
||||
*/
|
||||
var VueResource = require("vue-resource");
|
||||
require("jquery");
|
||||
import Vue from 'vue';
|
||||
import VueRouter from 'vue-router';
|
||||
import VueResource from 'vue-resource';
|
||||
import VueMaterial from 'vue-material';
|
||||
import 'vue-material/dist/vue-material.css';
|
||||
import 'roboto-fontface/css/roboto/roboto-fontface.css';
|
||||
import 'material-design-icons/iconfont/material-icons.css';
|
||||
import jQuery from 'jquery';
|
||||
import App from './App';
|
||||
|
||||
Vue.use(VueRouter);
|
||||
Vue.use(VueResource);
|
||||
Vue.use(VueMaterial);
|
||||
|
||||
/* eslint-disable no-new */
|
||||
new Vue({
|
||||
el: '#app',
|
||||
template: '<App/>',
|
||||
components: { App }
|
||||
})
|
||||
var routes = [
|
||||
{path: '/day/:day', component: App}
|
||||
];
|
||||
|
||||
const router = new VueRouter({routes});
|
||||
|
||||
var vue = new Vue({
|
||||
el: '#app',
|
||||
template: '<App/>',
|
||||
router: router,
|
||||
components: { App }
|
||||
});
|
||||
|
|
Loading…
Reference in a new issue