WIP on web UI: added nav, routing, basic page skeletons.

This commit is contained in:
Jonathan Bernard 2019-02-25 00:24:51 -06:00
parent 7f93ca90da
commit 0d0b4a945a
24 changed files with 279 additions and 73 deletions

View File

@ -1,7 +1,7 @@
{
"authSecret":"change me",
"authSecret":"bifekHuffIs3",
"dbConnString":"host=localhost port=5500 dbname=personal_measure user=postgres password=password",
"debug":true,
"port":8080,
"port":8081,
"pwdCost":11
}

View File

@ -293,7 +293,7 @@ proc start*(ctx: PMApiContext): void =
try:
if not ctx.db.deleteUser(user): raiseEx "unable to delete user"
makeJsonResp(Http200, "user " & user.email & " deleted")
jsonResp(Http200, "user " & user.email & " deleted")
except: jsonResp(Http500, getCurrentExceptionMsg())
@ -518,4 +518,3 @@ proc start*(ctx: PMApiContext): void =
resp($(%"shutting down"), JSON)
waitFor(stopFuture)

View File

@ -83,7 +83,7 @@ macro generateProcsForModels*(modelTypes: openarray[type]): untyped =
for t in modelTypes:
let modelName = $(t.getType[1])
let getName = ident("get" & modelName)
let getAllName = ident("getAll" & modelName)
let getAllName = ident("getAll" & modelName & "s")
let findWhereName = ident("find" & modelName & "sWhere")
let createName = ident("create" & modelName)
let updateName = ident("update" & modelName)

5
web/.sass-lint.yml Normal file
View File

@ -0,0 +1,5 @@
rules:
no-ids: 0
no-url-protocols: 0
no-url-domains: 0
no-css-comments: 0

61
web/package-lock.json generated
View File

@ -816,6 +816,32 @@
"to-fast-properties": "^2.0.0"
}
},
"@fortawesome/fontawesome-common-types": {
"version": "0.2.15",
"resolved": "https://registry.npmjs.org/@fortawesome/fontawesome-common-types/-/fontawesome-common-types-0.2.15.tgz",
"integrity": "sha512-ATBRyKJw1d2ko+0DWN9+BXau0EK3I/Q6pPzPv3LhJD7r052YFAkAdfb1Bd7ZqhBsJrdse/S7jKxWUOZ61qBD4g=="
},
"@fortawesome/fontawesome-svg-core": {
"version": "1.2.15",
"resolved": "https://registry.npmjs.org/@fortawesome/fontawesome-svg-core/-/fontawesome-svg-core-1.2.15.tgz",
"integrity": "sha512-M/sHyl4g2VBtKYkay1Z+XImMyTVcaBPmehYtPw4HKD9zg2E7eovB7Yx98aUfZjPbroGqa+IL4/+KhWBMOGlHIQ==",
"requires": {
"@fortawesome/fontawesome-common-types": "^0.2.15"
}
},
"@fortawesome/free-solid-svg-icons": {
"version": "5.7.2",
"resolved": "https://registry.npmjs.org/@fortawesome/free-solid-svg-icons/-/free-solid-svg-icons-5.7.2.tgz",
"integrity": "sha512-iujcXMyAvIbWM8W3jkOLpvJbR+rPpdN1QyqhZeJaLRdHPH4JmuovIAYP4vx5Sa1csZVXfRD1eDWqVZ/jGM620A==",
"requires": {
"@fortawesome/fontawesome-common-types": "^0.2.15"
}
},
"@fortawesome/vue-fontawesome": {
"version": "0.1.5",
"resolved": "https://registry.npmjs.org/@fortawesome/vue-fontawesome/-/vue-fontawesome-0.1.5.tgz",
"integrity": "sha512-tiNZCgh+ZkUsyFfm2MQMMdHKRrKj82M7g0XFPSNNY+s5nRB82soy0US+xj0jGRy433b0c4WpylCOhgle3294Uw=="
},
"@iamstarkov/listr-update-renderer": {
"version": "0.4.1",
"resolved": "https://registry.npmjs.org/@iamstarkov/listr-update-renderer/-/listr-update-renderer-0.4.1.tgz",
@ -6834,8 +6860,7 @@
"ansi-regex": {
"version": "2.1.1",
"bundled": true,
"dev": true,
"optional": true
"dev": true
},
"aproba": {
"version": "1.2.0",
@ -6863,7 +6888,6 @@
"version": "1.1.11",
"bundled": true,
"dev": true,
"optional": true,
"requires": {
"balanced-match": "^1.0.0",
"concat-map": "0.0.1"
@ -6878,8 +6902,7 @@
"code-point-at": {
"version": "1.1.0",
"bundled": true,
"dev": true,
"optional": true
"dev": true
},
"concat-map": {
"version": "0.0.1",
@ -6890,8 +6913,7 @@
"console-control-strings": {
"version": "1.1.0",
"bundled": true,
"dev": true,
"optional": true
"dev": true
},
"core-util-is": {
"version": "1.0.2",
@ -7008,8 +7030,7 @@
"inherits": {
"version": "2.0.3",
"bundled": true,
"dev": true,
"optional": true
"dev": true
},
"ini": {
"version": "1.3.5",
@ -7021,7 +7042,6 @@
"version": "1.0.0",
"bundled": true,
"dev": true,
"optional": true,
"requires": {
"number-is-nan": "^1.0.0"
}
@ -7036,7 +7056,6 @@
"version": "3.0.4",
"bundled": true,
"dev": true,
"optional": true,
"requires": {
"brace-expansion": "^1.1.7"
}
@ -7044,14 +7063,12 @@
"minimist": {
"version": "0.0.8",
"bundled": true,
"dev": true,
"optional": true
"dev": true
},
"minipass": {
"version": "2.3.5",
"bundled": true,
"dev": true,
"optional": true,
"requires": {
"safe-buffer": "^5.1.2",
"yallist": "^3.0.0"
@ -7070,7 +7087,6 @@
"version": "0.5.1",
"bundled": true,
"dev": true,
"optional": true,
"requires": {
"minimist": "0.0.8"
}
@ -7151,8 +7167,7 @@
"number-is-nan": {
"version": "1.0.1",
"bundled": true,
"dev": true,
"optional": true
"dev": true
},
"object-assign": {
"version": "4.1.1",
@ -7164,7 +7179,6 @@
"version": "1.4.0",
"bundled": true,
"dev": true,
"optional": true,
"requires": {
"wrappy": "1"
}
@ -7250,8 +7264,7 @@
"safe-buffer": {
"version": "5.1.2",
"bundled": true,
"dev": true,
"optional": true
"dev": true
},
"safer-buffer": {
"version": "2.1.2",
@ -7287,7 +7300,6 @@
"version": "1.0.2",
"bundled": true,
"dev": true,
"optional": true,
"requires": {
"code-point-at": "^1.0.0",
"is-fullwidth-code-point": "^1.0.0",
@ -7307,7 +7319,6 @@
"version": "3.0.1",
"bundled": true,
"dev": true,
"optional": true,
"requires": {
"ansi-regex": "^2.0.0"
}
@ -7351,14 +7362,12 @@
"wrappy": {
"version": "1.0.2",
"bundled": true,
"dev": true,
"optional": true
"dev": true
},
"yallist": {
"version": "3.0.3",
"bundled": true,
"dev": true,
"optional": true
"dev": true
}
}
},

View File

@ -9,6 +9,9 @@
"test:unit": "vue-cli-service test:unit"
},
"dependencies": {
"@fortawesome/fontawesome-svg-core": "^1.2.15",
"@fortawesome/free-solid-svg-icons": "^5.7.2",
"@fortawesome/vue-fontawesome": "^0.1.5",
"@types/lodash.merge": "^4.6.5",
"axios": "^0.18.0",
"keen-ui": "^1.1.2",

View File

@ -1,9 +1,6 @@
<template>
<div id="app">
<div id="nav">
<router-link to="/">Home</router-link> |
<router-link to="/about">About</router-link>
</div>
<NavBar></NavBar>
<router-view/>
</div>
</template>

View File

@ -1,18 +1,29 @@
#app {
font-family: 'Avenir', Helvetica, Arial, sans-serif;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
text-align: center;
color: #2c3e50;
}
#nav {
padding: 30px;
a {
font-weight: bold;
color: #2c3e50;
&.router-link-exact-active {
color: #42b983;
}
}
@import '~@/styles/vars';
@import '~@/styles/reset';
@import url('https://fonts.googleapis.com/css?family=Dosis|Exo+2:600|Exo:700|Josefin+Sans:300|Montserrat:300|Raleway|Teko:500|Titillium+Web:200');
body {
background-color: $bg-primary;
color: $fg-primary;
}
h1, h2, h3 { font-family: 'Exo 2'; }
h1 { font-size: 2rem; }
h2 { font-size: 1.6rem; }
h3 { font-size: 1.3rem; }
#app {
border: 0;
display: flex;
align-items: stretch;
font-family: 'Lato', 'Avenir', Helvetica, Arial, sans-serif;
margin: 0;
min-height: 100vh;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
& > * { padding: 1rem 2rem; }
}

View File

@ -3,6 +3,9 @@ import App from './App.vue';
import router from './router';
import store from './store';
import './registerServiceWorker';
import { FontAwesomeIcon } from '@fortawesome/vue-fontawesome';
Vue.component('fa-icon', FontAwesomeIcon);
Vue.config.productionTip = false;

View File

@ -1,6 +1,8 @@
import Vue from 'vue';
import Router from 'vue-router';
import Home from './views/Home.vue';
import Dashboard from './views/Dashboard.vue';
import Measures from './views/Measures.vue';
import UserAccount from './views/UserAccount.vue';
Vue.use(Router);
@ -10,16 +12,22 @@ export default new Router({
routes: [
{
path: '/',
name: 'home',
component: Home
redirect: '/dashboard'
},
{
path: '/about',
name: 'about',
// route level code-splitting
// this generates a separate chunk (about.[hash].js) for this route
// which is lazy-loaded when the route is visited.
component: () => import(/* webpackChunkName: "about" */ './views/About.vue')
path: '/dashboard',
name: 'dashboard',
component: Dashboard
},
{
path: '/measures',
name: 'measures',
component: Measures
},
{
path: '/user-account',
name: 'user-account',
component: UserAccount
}
]
});

View File

@ -0,0 +1,5 @@
* {
border: 0;
box-sizing: border-box;
margin: 0;
}

53
web/src/styles/vars.scss Normal file
View File

@ -0,0 +1,53 @@
/* Coolors Exported Palette - coolors.co/4f4e4d-1b998b-fffd82-ff9b71-e84855 */
/* HSL */
// $color1: hsla(30%, 1%, 31%, 1);
// $color2: hsla(173%, 70%, 35%, 1);
// $color3: hsla(59%, 100%, 75%, 1);
// $color4: hsla(18%, 100%, 72%, 1);
// $color5: hsla(355%, 78%, 60%, 1);
/* RGB */
$color1: rgba(79, 78, 77, 1);
$color2: rgba(27, 153, 139, 1);
$color3: rgba(255, 253, 130, 1);
$color4: rgba(255, 155, 113, 1);
$color5: rgba(232, 72, 85, 1);
$bg-primary: #333;
$fg-primary: #d8d8e0;
$screen-x-small: 320px;
$screen-small: 640px;
$screen-wide: 1200px;
$screen-ultrawide: 1600px;
/** ### forSize
* This mixin allows us to apply some rules selectively based on the screen
* size. There are three primary sizes: `small`, `medium`, and `large`, which
* are mutually exclusive. Additionally there are two additional sizes:
* `notSmall` and `ultraLarge`. `notSmall`, as the name implies matches any
* value which is not the small screen size, so it overlaps with medium,
* large, and ultraLarge. `ultraLarge` defines a wider minimum screen size
* than large, but neither large nor ultraLarge specify maximum widths,
* so ultraLarge is a strict subset of large. A screen large enough to match
* ultraLarge will also match large (compare with medium and large: matching
* medium means it will not match large, and vice versa). */
@mixin for-size($size) {
@if $size == xsmall {
@media screen and (max-width: $screen-x-small) { @content; }
} @else if $size == small {
@media screen and (max-width: $screen-small) { @content; }
} @else if $size == not-small {
@media screen and (min-width: $screen-small + 1) { @content; }
} @else if $size == medium {
@media screen and (min-width: $screen-small + 1) and (max-width: $screen-wide - 1) { @content; }
} @else if $size == large {
@media screen and (min-width: $screen-wide) { @content; }
} @else if $size == ultra-large {
@media screen and (min-width: $screen-ultrawide) { @content; }
}
}

View File

@ -1,5 +0,0 @@
<template>
<div class="about">
<h1>This is an about page</h1>
</div>
</template>

View File

@ -0,0 +1,6 @@
<template>
<div class="dashboard">
<h1>Dashboard</h1>
</div>
</template>
<script lang="ts" src="./dashboard.ts"></script>

View File

@ -1,6 +0,0 @@
<template>
<div class="home">
</div>
</template>
<script lang="ts" src="./home.ts"></script>

View File

@ -0,0 +1,6 @@
<template>
<div class="measures">
<h1>Things You Are Measuring</h1>
</div>
</template>
<script lang="ts" src="./measures.ts"></script>

View File

@ -1,5 +1,28 @@
<template>
<nav v-bind:class='collapsed ? "collapsed" : "expanded"'>
<h1 class=logo>
<span class=expanded>Personal Measure</span>
<span class=collapsed>PM</span>
</h1>
<router-link to="/dashboard">
<fa-icon icon=home></fa-icon>
<span class=expanded>Dashboard</span>
</router-link>
<router-link to="/measures">
<fa-icon icon=pencil-ruler></fa-icon>
<span class=expanded>Measures</span>
</router-link>
<router-link to="/user-account">
<fa-icon icon=user></fa-icon>
<span class=expanded>Your Account</span>
</router-link>
<div class=collapse-handle tabindex="0"
v-on:click='toggleCollapsed()'
v-on:keypress='toggleCollapsed()'>
<span class=collapsed><fa-icon icon=angle-double-right></fa-icon></span>
<span class=expanded><fa-icon icon=angle-double-left></fa-icon></span>
</div>
</nav>
</template>
<script lang="ts" src="./nav-bar.ts"></script>
<style lang="scss" src="./nav-bar.scss"></style>

View File

@ -0,0 +1,6 @@
<template>
<div class=user-account>
<h1>Your Account</h1>
</div>
</template>
<script lang="ts" src="./user-account.ts"></script>

View File

@ -0,0 +1,4 @@
import { Component, Vue } from 'vue-property-decorator';
@Component({})
export default class Dashboard extends Vue {}

View File

@ -0,0 +1,6 @@
import { Component, Vue } from 'vue-property-decorator';
@Component({
components: { }
})
export default class Measures extends Vue {}

View File

@ -0,0 +1,49 @@
@import '~@/styles/vars';
nav {
background-color: $color1;
color: $fg-primary;
display: flex;
flex-direction: column;
font-family: 'Exo 2';
&.expanded {
.collapsed { display: none; }
.collapse-handle { align-self: flex-end; }
}
&.collapsed {
.expanded { display: none; }
svg { font-size: 2.2rem; }
.collapse-handle { align-self: center; }
}
a {
color: inherit;
display: block;
font-size: 1.5rem;
padding: .5rem 0;
text-decoration: none;
width: 100%;
&.router-link-active { color: $color3; }
&:hover { color: $color3; }
svg {
display: inline-block;
min-width: 1.5em;
}
}
.logo {
color: $color4;
padding: 0 0 1rem;
}
.collapse-handle {
cursor: pointer;
font-size: 1.5rem;
margin-top: auto;
}
}

View File

@ -1,6 +1,26 @@
import { Component, Vue } from 'vue-property-decorator';
import { library } from '@fortawesome/fontawesome-svg-core';
import { faAngleDoubleLeft, faAngleDoubleRight,
faPencilRuler, faHome, faUser } from '@fortawesome/free-solid-svg-icons';
// import UiIconButton from 'keen-ui/src/UiIconButton.vue';
library.add(faAngleDoubleLeft, faAngleDoubleRight, faPencilRuler, faHome, faUser);
@Component({
components: {
// UiIconButton
}
})
export default class NavBar extends Vue {}
export default class NavBar extends Vue {
private collapsed: boolean;
constructor() {
super();
this.collapsed = false;
}
public toggleCollapsed(): boolean {
this.collapsed = !this.collapsed;
return this.collapsed;
}
}

View File

@ -0,0 +1,4 @@
import { Component, Vue } from 'vue-property-decorator';
@Component({})
export default class UserAccount extends Vue {}