Permalink
Browse files

Admin interface typescript (#185)

Build a new administrator interface in typescript, add feedback for administrator operations.
  • Loading branch information...
jlyon1 committed Feb 4, 2019
1 parent bfa68d4 commit cd88353edfc3fd37bc506fb7146ece7a1e2d1be2
@@ -112,7 +112,7 @@ func New(cfg Config, ms shuttletracker.ModelService, msg shuttletracker.MessageS
// Admin
r.Route("/admin", func(r chi.Router) {
r.Use(cli.casauth)
r.Get("/", api.AdminHandler)
r.Get("/*", api.AdminHandler)
r.Get("/login", api.AdminHandler)
r.Get("/logout", cli.logout)
})
@@ -166,7 +166,7 @@ func (api *API) AdminHandler(w http.ResponseWriter, r *http.Request) {
http.Redirect(w, r, "/admin", 301)
}
w.Header().Set("Cache-Control", "no-cache")
http.ServeFile(w, r, "admin.html")
http.ServeFile(w, r, "static/admin.html")

}

@@ -22,11 +22,11 @@ func TestStatic(t *testing.T) {
},
{
method: "GET",
path: "/static/css/application.css",
path: "/admin/routes",
},
{
method: "GET",
path: "/static/js/admin.js",
path: "/admin/stops",
},
}

Some generated files are not rendered by default. Learn more.

Oops, something went wrong.
@@ -11,10 +11,13 @@
},
"dependencies": {
"@types/leaflet": "^1.2.10",
"@types/leaflet-routing-machine": "^3.2.1",
"bulma": "^0.7.2",
"@types/leaflet-rotatedmarker": "^0.2.1",
"ajv": "^6.5.4",
"leaflet": "^1.3.4",
"leaflet-rotatedmarker": "^0.2.0",
"leaflet-routing-machine": "^3.2.12",
"vue": "^2.5.17",
"vue-router": "^3.0.1",
"vuex": "^3.0.1"
@@ -0,0 +1,16 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width,initial-scale=1.0">
<title>Shuttle Tracker - Fleet Management</title>
</head>
<body>
<noscript>
<strong>The Shuttle Tracker doesn't work properly without JavaScript enabled. Please enable it to continue.</strong>
</noscript>
<div id="app"></div>
<!-- built files will be auto injected -->
</body>
</html>
@@ -0,0 +1,80 @@
<template>
<div id="admin">
<nav class="navbar" role="navigation" aria-label="main navigation">
<div class="navbar-brand">
<router-link class="navbar-item" :to="'/admin/routes'">
<p>Shuttle Tracker - <span class="has-text-danger">Fleet Management</span></p>
</router-link>
<a @click="menuVisible = !menuVisible" role="button" class="navbar-burger burger" aria-label="menu" aria-expanded="false">
<span aria-hidden="true"></span>
<span aria-hidden="true"></span>
<span aria-hidden="true"></span>
</a>
</div>

<div id="navbarBasicExample" class="navbar-menu" :class="{'is-active': menuVisible}">

<div class="navbar-start">
<router-link to="/admin/routes" class="navbar-item">
Routes
</router-link>
<router-link to="/admin/stops" class="navbar-item">
Stops
</router-link>
<router-link to="/admin/vehicles" class="navbar-item">
Vehicles
</router-link >
<router-link to="/admin/messages" class="navbar-item">
Messages
</router-link>
</div>

<div class="navbar-end">
<div class="navbar-item">
<div class="buttons">
<a href="/admin/logout" class="button is-light">
Log out
</a>
</div>
</div>
</div>
</div>
</nav>
<router-view>
</router-view>
</div>
</template>


<script lang="ts">
import Vue from 'vue';
import routesAdmin from '@/components/admin/routesAdmin.vue';
export default Vue.extend({
name: 'admin',
components: {
routesAdmin,
},
data() {
return {
menuVisible: false,
}as {
menuVisible: boolean;
};
},
mounted() {
this.$store.dispatch('grabRoutes');
this.$store.dispatch('grabVehicles');
this.$store.dispatch('grabStops');
},
});
</script>

<style lang="scss">
#app{
width: 100%;
height: 100%;
}
</style>
@@ -0,0 +1,21 @@
import Vue from 'vue';
import 'bulma';
import '@/assets/styles.scss';
import Admin from '@/Admin.vue';
import router from '@/adminrouter';
import store from '@/store';
import '../node_modules/leaflet/dist/leaflet.css';
import '../node_modules/leaflet-routing-machine/dist/leaflet-routing-machine.css';

Vue.config.productionTip = false;


/**
* Declare the main Vue instance with components and vuex store.
*/
new Vue({
render: (h) => h(Admin),
router,
store,
}).$mount('#app');

@@ -0,0 +1,74 @@
import Vue from 'vue';
import Router from 'vue-router';
import routesAdmin from '@/components/admin/routesAdmin.vue';
import stopsAdmin from '@/components/admin/stopsAdmin.vue';
import vehiclesAdmin from '@/components/admin/vehiclesAdmin.vue';
import routeOverview from '@/components/admin/routeOverview.vue';
import routeEditing from '@/components/admin/routeEditing.vue';
import vehicleEditing from '@/components/admin/vehicleEditing.vue';
import vehicleOverview from '@/components/admin/vehicleOverview.vue';
import messagesAdmin from '@/components/admin/MessagesAdmin.vue';

Vue.use(Router);

export default new Router({
mode: 'history',
routes: [
{
path: '/admin',
redirect: '/admin/routes/',
},
{
path: '/admin/routes/',
name: 'routes',
component: routesAdmin,
},
{
path: '/admin/routes/:id',
name: 'routes view',
component: routeOverview,
},
{
path: '/admin/routes/:id/edit',
name: 'routes edit',
component: routeEditing,
},
{
path: '/admin/routes/:id/new',
name: 'new route',
component: routeEditing,
props: {creation: true},
},
{
path: '/admin/stops',
name: 'stops',
component: stopsAdmin,
},
{
path: '/admin/messages',
name: 'messages',
component: messagesAdmin,
},
{
path: '/admin/vehicles',
name: 'vehicles overview',
component: vehiclesAdmin,
},
{
path: '/admin/vehicles/:id',
name: 'vehicle overview',
component: vehicleOverview,
},
{
path: '/admin/vehicles/:id/edit',
name: 'vehicles',
component: vehicleEditing,
},
{
path: '/admin/vehicles/:id/new',
name: 'new vehicle',
component: vehicleEditing,
props: {creation: true},
},
],
});
@@ -0,0 +1,38 @@
<template>
<div class="modal" :class="{'is-active': active}">
<div class="modal-background"></div>
<div class="modal-content">
<div class="box">
<p class="title">Are you sure?</p>
<p class="subtitle">This action is permanent</p>
<div class="level">
<div class="level-left"></div>
<div class="level-right">
<div class="control">
<button @click="$emit('yes');" style="margin-right: 10px;" class="button is-success">Yes</button>
</div>
<div class="control">
<button @click="$emit('no');" class="button">No</button>
</div>
</div>
</div>

</div>
</div>
<button @click="$emit('no');" class="modal-close is-large" aria-label="close"></button>
</div>
</template>

<script lang="ts">
// This is an 'are you sure' modal, it is active on the active property, and emits no when close or no is clicked, and emits yes when yes is clicked
import Vue from 'vue';
export default Vue.extend({
props: {
active: {
type: Boolean,
default: () => false,
},
},
});
</script>
Oops, something went wrong.

0 comments on commit cd88353

Please sign in to comment.