Skip to content

Commit

Permalink
Merge branch 'master' of github.com:zeroshade/knkpwa
Browse files Browse the repository at this point in the history
  • Loading branch information
Matthew Topol committed Feb 11, 2019
2 parents 7904363 + 9466b72 commit 9cd6f8f
Show file tree
Hide file tree
Showing 13 changed files with 265 additions and 77 deletions.
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@
"stylus-loader": "^3.0.2",
"typescript": "^3.0.0",
"vue-cli-plugin-vuetify": "^0.4.6",
"vue-style-loader": "^4.1.2",
"vue-template-compiler": "^2.5.21",
"vuetify-loader": "^1.0.5",
"webpack-pwa-manifest": "^4.0.0"
Expand Down
7 changes: 1 addition & 6 deletions src/App.vue
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
</template>

<script lang='ts'>
import { Component, Vue } from 'vue-property-decorator';
import { Component, Prop, Vue } from 'vue-property-decorator';
import { Getter, Action } from 'vuex-class';
import Toolbar from '@/components/Toolbar.vue';
import NavBar from '@/components/NavBar.vue';
Expand All @@ -27,13 +27,8 @@ import EventDialog from '@/components/EventDialog.vue';
},
})
export default class Layout extends Vue {
@Action('fetchScheds') public fetchScheds!: () => Promise<void>;
@Getter('auth/admin') public isAdmin!: boolean;
public drawer = null;
public mounted() {
this.fetchScheds();
}
}
</script>
21 changes: 7 additions & 14 deletions src/components/NavBar.vue
Original file line number Diff line number Diff line change
Expand Up @@ -13,15 +13,9 @@
</v-list>
<v-list class='pt-0' dense>
<v-divider light/>
<v-list-tile v-for='(s, idx) in items' :value='select === idx'
:key='s.id' @click='schedule = idx'>
<v-list-tile-action v-if='idx === select'>
<v-icon>chevron_right</v-icon>
</v-list-tile-action>

<v-list-tile-content>
<v-list-tile-title>{{ s.title }}</v-list-tile-title>
</v-list-tile-content>
<v-list-tile v-for='s in items' :key='s.id'
:to='{ name: $route.name, params: { id: s.id }}'>
<v-list-tile-title>{{ s.title }}</v-list-tile-title>
</v-list-tile>
</v-list>
<template v-if='authenticated'>
Expand Down Expand Up @@ -60,18 +54,17 @@ export default class NavBar extends Vue {
@State('schedules') public items!: Schedule[];
@Getter('auth/userfavs') public userfavs!: number[];
@Mutation('showModal') public showModal!: (payload: {ev: Event, color: string}) => void;
public select: number = 0;
@Getter('curSchedule') public curSchedule!: Schedule;
public get favs(): Event[] {
if (this.items.length <= this.select) { return []; }
if (!this.items.length) { return []; }
return this.items[this.select].events.filter((e) => this.userfavs.includes(e.id))
return this.curSchedule.events.filter((e) => this.userfavs.includes(e.id))
.sort((a, b) => a.start.diff(b.start));
}
public eventColor(ev: Event): string {
return this.items[this.select].colorMap[ev.room];
return this.curSchedule.colorMap[ev.room];
}
}
</script>
6 changes: 3 additions & 3 deletions src/components/Toolbar.vue
Original file line number Diff line number Diff line change
Expand Up @@ -51,9 +51,9 @@
</v-menu>
</template>
<v-tabs slot='extension' centered grow color='orange' slider-color='yellow'>
<v-tab to='/agenda'>Agenda</v-tab>
<v-tab to='/rooms'>Room View</v-tab>
<v-tab to='/events'>Event View</v-tab>
<v-tab :to='{ name: "agenda", params: $route.params }'>Agenda</v-tab>
<v-tab :to='{ name: "rooms", params: $route.params }'>Room View</v-tab>
<v-tab :to='{ name: "events", params: $route.params }'>Event View</v-tab>
</v-tabs>
</v-toolbar>
</template>
Expand Down
57 changes: 57 additions & 0 deletions src/components/admin/InlineComboBox.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
<template>
<v-edit-dialog
:return-value.sync='saved'
lazy
@open='open'
@save='save'
@cancel='$emit("cancel")'
@close='$emit("close")'>
{{ value }}
<v-combobox slot='input'
:rules='[(v) => !!v || "Cannot be empty"]'
:multiple='multiple'
:chips='chips'
v-model='saved'
:label='label'
:items='items'></v-combobox>
</v-edit-dialog>
</template>

<script lang='ts'>
import { Prop, Component, Vue, Emit } from 'vue-property-decorator';
@Component
export default class InlineCombobox extends Vue {
@Prop(String) public value!: string;
@Prop(String) public label!: string;
@Prop(Array) public items!: string[];
@Prop({default: false}) public multiple!: boolean;
@Prop({default: false}) public chips!: boolean;
public saved: string | string[] = '';
public save() {
if (this.saved) {
if (this.multiple) {
this.$emit('input', (this.saved as string[]).join(', '));
} else {
this.$emit('input', this.saved);
}
this.$emit('save');
} else {
this.$emit('cancel');
}
}
@Emit()
public open() {
if (this.multiple) {
console.log(this.value, this.value.split(','));
this.saved = this.value.split(',').map((o) => o.trim());
} else {
this.saved = this.value;
}
}
}
</script>
18 changes: 16 additions & 2 deletions src/mixins/grid-mixin.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,26 @@
import { Component, Vue } from 'vue-property-decorator';
import { Component, Prop, Watch, Vue } from 'vue-property-decorator';
import { Schedule } from '@/api/schedule';
import { Mutation, Action } from 'vuex-class';
import moment from 'moment';

@Component
export default class GridMixin extends Vue {
public id = 3;
@Prop(Number) public id!: number;
@Action('fetchScheds') public fetchScheds!: () => Promise<void>;
@Mutation('setCurSchedule') public setSched!: (id: number) => void;

public pixelHeight = 50;

public async mounted() {
await this.fetchScheds();
this.setSched(this.id);
}

@Watch('id')
public load(val: number) {
this.setSched(this.id);
}

public get times(): string[] {
if (!this.sched) { return []; }
return this.sched.times();
Expand Down
58 changes: 32 additions & 26 deletions src/router.ts
Original file line number Diff line number Diff line change
@@ -1,26 +1,32 @@
import Vue from 'vue';
import VueRouter, { Route } from 'vue-router';
Vue.use(VueRouter);

export default new VueRouter({
mode: 'history',
base: process.env.BASE_URL,
routes: [
{ path: '/', redirect: '/rooms' },
{
path: '/rooms', name: 'home',
component: () => import(/* webpackChunkName: "group-app" */ '@/views/RoomGrid.vue'),
},
{
path: '/agenda', name: 'agenda',
component: () => import(/* webpackChunkName: "group-app" */ '@/views/Agenda.vue'),
},
{
path: '/events', name: 'events',
component: () => import(/* webpackChunkName: "group-app" */ '@/views/Events.vue'),
},
{
path: '/callback', component: () => import(/* webpackChunkName: "group-auth" */ '@/views/Auth.vue'), name: 'auth',
},
],
});
import Vue from 'vue';
import VueRouter, { Route } from 'vue-router';
Vue.use(VueRouter);

const props = (route: Route) => ({ id: +route.params.id || 3 });

export default new VueRouter({
mode: 'history',
base: process.env.BASE_URL,
routes: [
{ path: '/', redirect: '/rooms' },
{
path: '/rooms/:id?', name: 'rooms',
props,
component: () => import(/* webpackChunkName: "group-app" */ '@/views/RoomGrid.vue'),
},
{
path: '/agenda/:id?', name: 'agenda',
props,
component: () => import(/* webpackChunkName: "group-app" */ '@/views/Agenda.vue'),
},
{
path: '/events/:id?', name: 'events',
props,
component: () => import(/* webpackChunkName: "group-app" */ '@/views/Events.vue'),
},
{
path: '/callback', component: () => import(/* webpackChunkName: "group-auth" */ '@/views/Auth.vue'),
name: 'auth',
},
],
});
108 changes: 108 additions & 0 deletions src/store/adminmod.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
import { AdminState, RootState } from './states';
import { Module } from 'vuex';
import sched, { Schedule, ISchedule } from '@/api/schedule';
import event, { Event } from '@/api/event';
import colors from 'vuetify/es5/util/colors';

function camelCaseToDash(str: string): string {
return str
.replace(/[^a-zA-Z0-9]+/g, '-')
.replace(/([A-Z]+)([A-Z][a-z])/g, '$1-$2')
.replace(/([a-z])([A-Z])/g, '$1-$2')
.replace(/([0-9])([^0-9])/g, '$1-$2')
.replace(/([^0-9])([0-9])/g, '$1-$2')
.replace(/-+/g, '-')
.toLowerCase();
}

const adminModule: Module<AdminState, RootState> = {
namespaced: true,
state: {
schedule: null,
events: [],
colorNames: Object.keys(colors).map(camelCaseToDash),
modifierNames: Object.keys(colors.red).map(camelCaseToDash),
},
getters: {
sched(state) {
return state.schedule;
},
events(state) {
return state.events;
},
},
mutations: {
setSched(state: AdminState, payload: Schedule) {
state.schedule = payload;
},
setEvents(state: AdminState, payload: Event[]) {
state.events = payload;
},
updateEvent(state: AdminState, payload: Event) {
const idx = state.events.findIndex((e) => e.id === payload.id);
if (idx !== -1) {
state.events.splice(idx, 1, payload);
} else {
state.events.push(payload);
}
},
removeEvent(state: AdminState, id: number) {
const idx = state.events.findIndex((e) => e.id === id);
state.events.splice(idx, 1);
},
},
actions: {
async loadSchedById({ commit }, id: number) {
const s = await sched.getSchedule(id);
commit('setSched', s);
const e = await event.getEvents(id);
commit('setEvents', e);
},
async deleteSchedule({ dispatch }, id: number) {
await dispatch('auth/makeAuthedRequest', {
path: `/scheds/${id}`,
method: 'DELETE',
}, { root: true });
await dispatch('fetchScheds', undefined, { root: true });
},
async newSchedule({ dispatch }, payload: ISchedule) {
await dispatch('auth/makeAuthedRequest', {
path: '/scheds',
method: 'POST',
body: payload,
}, { root: true });
await dispatch('fetchScheds', undefined, { root: true });
},
async updateSchedule({ commit, dispatch }, payload: Schedule) {
commit('setSched', payload);
await dispatch('auth/makeAuthedRequest', {
path: `/scheds/${payload.id}`,
method: 'PUT',
body: payload.getISched(),
}, { root: true });
},
async updateEvent({ commit, dispatch }, payload: Event) {
commit('updateEvent', payload);
const body = payload.getIEvent();
let path = '/events';
let method = 'POST';
if (payload.id !== 0) {
path += `/${payload.id}`;
method = 'PUT';
}

await dispatch('auth/makeAuthedRequest', {
path, method, body,
}, { root: true });
},
async delEvent({ commit, dispatch }, id: number) {
commit('removeEvent', id);
await dispatch('auth/makeAuthedRequest', {
path: `/events/${id}`,
method: 'DELETE',
}, { root: true });
},
},
};

export default adminModule;
10 changes: 9 additions & 1 deletion src/store/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import Vue from 'vue';
import Vuex, { StoreOptions } from 'vuex';
import { RootState } from './states';
import authModule from './authmod';
import adminModule from './admin';
import adminModule from './adminmod';
import sched, { Schedule } from '@/api/schedule';
import { Event } from '@/api/event';

Expand All @@ -15,6 +15,7 @@ export default new Vuex.Store<RootState>({
},
state: {
schedules: [],
curSchedule: 0,
showModal: false,
modalEvent: null,
modalColor: '',
Expand All @@ -23,8 +24,15 @@ export default new Vuex.Store<RootState>({
getScheduleById: (state) => (id: number) => {
return state.schedules.find((s) => s.id === id);
},
curSchedule(state: RootState): Schedule {
return state.schedules[state.curSchedule];
},
},
mutations: {
setCurSchedule(state: RootState, id: number) {
state.curSchedule = state.schedules.findIndex((s) => s.id === id);
state.schedules[state.curSchedule].loadEvents();
},
setScheds(state: RootState, scheds: Schedule[]) {
state.schedules = scheds;
},
Expand Down
1 change: 1 addition & 0 deletions src/store/states.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,4 +22,5 @@ export interface RootState {
showModal: boolean;
modalEvent: Event | null;
modalColor: string;
curSchedule: number;
}
Loading

0 comments on commit 9cd6f8f

Please sign in to comment.