basic version of the webApp

This commit is contained in:
Davide Alberani 2017-01-14 11:47:54 +01:00
parent cc7df7914b
commit ea03547a10
4 changed files with 351 additions and 73 deletions

View file

@ -1,56 +1,96 @@
<template>
<div id="app">
<div id="datepicker">
<datepicker :value="state.date" :inline="true" v-on:selected="getDay"></datepicker>
<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>
<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>
</template>
<script>
import Datepicker from 'vuejs-datepicker';
import Group from './Group';
export default {
data () {
return {
state: {
date: new Date(),
date: null, // a Date object representing the selected date
day: {},
},
newAttendee: null,
newGroup: null
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
View 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
View 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>&nbsp;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>&nbsp;&nbsp;<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>

View file

@ -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({
var routes = [
{path: '/day/:day', component: App}
];
const router = new VueRouter({routes});
var vue = new Vue({
el: '#app',
template: '<App/>',
router: router,
components: { App }
})
});