web: WIP work on adding create measure functionality in the UI.

This commit is contained in:
Jonathan Bernard 2019-04-11 09:24:47 -05:00
parent 0fe3ccfdd2
commit 9d9f8c4f9b
11 changed files with 144 additions and 41 deletions

View File

@ -2,12 +2,18 @@
<fieldset>
<div>
<label for=measureType>Type</label>
<select name=measureType v-model=config.type>
<select
:disabled=disabled
name=measureType
v-model=value.type>
<option value=simple>Simple</option>
<option value=list>List</option>
</select>
</div>
<div><input type=checkbox v-model=config.isVisible>Show by default.</input></div>
<div>
<label for=measureIsVisible>Show by default.</label>
<input type=checkbox v-model=value.isVisible :disabled=disabled />
</div>
<!--<ListMeasureConfigForm :config=config v-show="config.type === 'list'"/>-->
</fieldset>
</template>

View File

@ -1,10 +1,17 @@
import { Component, Prop, Vue, Watch } from 'vue-property-decorator';
import { Component, Emit, Prop, Vue, Watch } from 'vue-property-decorator';
import { logService } from '@/services/logging';
import { Measure, MeasureConfig, MeasureType } from '@/models';
@Component({})
export class MeasureConfigForm extends Vue {
@Prop({}) public config!: MeasureConfig;
@Prop({}) public value!: MeasureConfig;
@Prop({}) public disabled: boolean = false;
@Watch('value', { immediate: true, deep: true })
@Emit('input')
private onConfigChanged(newVal: MeasureConfig, oldVal: MeasureConfig) {
return newVal;
}
}
export default MeasureConfigForm;

View File

@ -5,6 +5,7 @@ import App from './App.vue';
import { store } from './store';
import router from './router';
import { FontAwesomeIcon } from '@fortawesome/vue-fontawesome';
import { dom as FA_DOM} from '@fortawesome/fontawesome-svg-core';
import SmartTable from 'vuejs-smart-table';
import ApexChart from 'vue-apexcharts';
@ -13,6 +14,8 @@ import './registerServiceWorker';
import { logService, LogLevel, ApiLogAppender, ConsoleLogAppender } from '@/services/logging';
Vue.component('fa-icon', FontAwesomeIcon);
FA_DOM.watch();
Vue.component('apex-chart', ApexChart);
Vue.use(SmartTable);

View File

@ -114,7 +114,7 @@ export class PmApiClient {
}
public async createMeasure<T extends MeasureConfig>(measure: Measure<T>): Promise<Measure<T>> {
const resp = await this.http.post(`/measures`);
const resp = await this.http.post(`/measures`, measure);
return resp.data;
}

View File

@ -24,6 +24,14 @@ export class MeasureStoreModule extends VuexModule {
public async fetchMeasure(slug: string) {
const measure = api.getMeasure(slug);
this.context.commit('SET_MEASURE', measure);
return measure;
}
@Action({ rawError: true })
public async createMeasure(m: Measure<MeasureConfig>) {
const newMeasure = await api.createMeasure(m);
this.context.commit('SET_MEASURE', newMeasure);
return newMeasure;
}
@Mutation private SET_MEASURE<T extends MeasureConfig>(measure: Measure<T>) {

View File

@ -6,6 +6,7 @@
border-radius: .25em;
cursor: pointer;
font-family: $body-font;
font-size: inherit;
font-weight: bold;
padding: .5em 1em;
@ -15,12 +16,21 @@
.btn-action {
background-color: $color2;
color: $color3;
cursor: pointer;
position: relative;
&:hover {
background-color: darken($color2, 5%);
}
}
.form-waiting .wait-spinner {
border: solid thin $fg-primary;
border-radius: .25em;
cursor: wait;
font-family: $body-font;
font-size: inherit;
padding: .5em 1em;
}
a.btn,
@ -43,8 +53,34 @@ a.btn-action { text-decoration: none; }
.main { flex-grow: 1; }
form {
display: flex;
flex-direction: column;
justify-content: space-between;
label {
display: inline-block;
margin: .5rem 0;
min-width: 10em;
}
select { background-color: white; }
.form-actions,
.form-waiting {
display: flex;
flex-direction: row-reverse;
}
}
input,
select {
select,
textarea {
font-size: inherit;
padding: .35em .5em;
&:disabled { cursor: wait; }
}
textarea { font-family: $body-font; }

View File

@ -4,34 +4,45 @@
<h1>New Measure</h1>
<h2>What do you want to measure?</h2>
</div>
<form @submit.prevent=login() class=new-measure-form>
<form @submit.prevent=createMeasure() class=new-measure-form>
<fieldset>
<div>
<label for=measureName>Display Name</label>
<input
:disabled=waiting
type=text
name=measureName
placeholder="what you are measuring"
required
v-model="measure.name" />
</div>
<div>
<label for=measureDescription>Description</label>
<input
type=text
<textarea
:disabled=waiting
name=measureDescription
placeholder="optional description"
v-model="measure.description" />
v-model="measure.description" ></textarea>
</div>
<div>
<label for=measureSlug>Slug (short-name)</label>
<label for=measureSlug>Short name (slug)</label>
<input
:disabled=waiting
type=text
name=measureDescription
:placeholder='slugFromName'
v-model="measure.slug" />
:placeholder='slugFromName + " (default)"'
:value="measure.slug"
@input="measure.slug = slugify($event.target.value)"/>
</div>
</fieldset>
<MeasureConfigForm :config=measure.config />
<MeasureConfigForm v-model=measure.config :disabled=waiting />
<div v-if='!waiting' class=form-actions>
<button class=btn-action>Create </button>
<a class=btn @click="$router.go(-1)">Cancel</a>
</div>
<div v-if='waiting' class=form-waiting>
<div class=wait-spinner>working <fa-icon icon=sync spin /></div>
</div>
</form>
</div>
</template>

View File

@ -1,16 +1,19 @@
<template>
<div class=user-account>
<form class=user-account>
<h1>Your Account</h1>
<fieldset>
<legend>About You</legend>
<label for=name>Name: </label>
<input name=name type=text :value="user.displayName"></input>
<label for=name>Email Address: </label>
<input name=name type=text :value="user.email"></input>
<div>
<label for=name>Name:</label>
<input name=name type=text v-model="user.displayName" />
</div>
<div>
<label for=name>Email Address: </label>
<input name=name type=text v-model="user.email"></input>
</div>
</fieldset>
<section class=api-tokens>
<h2>API Tokens</h2>
<v-table :data=apiTokens>
<v-table v-show='apiTokens && apiTokens.length > 0' :data=apiTokens>
<thead slot=head>
<v-th sortKey="name">Name</v-th>
<v-th sortKey="created">Created</v-th>
@ -24,11 +27,14 @@
</tr>
</tbody>
</v-table>
<div class=no-data v-show='!apiTokens || apiTokens.length === 0'>
You have not created any API tokens.
</div>
</section>
<section class=device-data>
<h2>Data on this Device</h2>
</section>
</div>
</form>
</template>
<script lang="ts" src="./user-account.ts"></script>
<style scoped lang="scss" src="./user-account.scss"></style>

View File

@ -8,3 +8,16 @@
margin-right: 2rem;
}
}
.measure-list {
display: flex;
flex-direction: row;
flex-wrap: wrap;
justify-content: space-evenly;
.measure-summary {
height: 12rem;
width: 12rem;
}
}

View File

@ -1,7 +1,8 @@
@import '~@/styles/vars';
input,
select {
select,
textarea {
// background-color: $bg-primary;
/*
@ -13,17 +14,3 @@ select {
border: solid thin $color1;
border-radius: 4px;
}
select { background-color: white; }
form {
display: flex;
flex-direction: column;
justify-content: space-between;
label {
display: inline-block;
margin: .5rem 0;
min-width: 10em;
}
}

View File

@ -1,16 +1,20 @@
import { Component, Prop, Vue, Watch } from 'vue-property-decorator';
import { library } from '@fortawesome/fontawesome-svg-core';
import { faSync } from '@fortawesome/free-solid-svg-icons';
import { logService } from '@/services/logging';
import { userStore } from '@/store';
import { measureStore, userStore } from '@/store';
import { Measure, MeasureConfig, MeasureType } from '@/models';
import MeasureConfigForm from '@/components/measure-config/MeasureConfigForm.vue';
library.add(faSync);
const logger = logService.getLogger('/views/new-measure');
@Component({
components: { MeasureConfigForm }
})
export class NewMeasure extends Vue {
private test: string = 'test';
private waiting = false;
private measure: Measure<MeasureConfig> = {
id: '',
config: {
@ -24,11 +28,33 @@ export class NewMeasure extends Vue {
};
private get slugFromName() {
return this.measure.name
return this.slugify(this.measure.name);
}
private slugify(s: string): string {
return s
.toLowerCase()
.replace(/[^\w\s]/g, '')
.replace(/[^\w\s\-]/g, '')
.replace(/\s+/g, '-');
}
private async createMeasure() {
if (!this.measure.slug) {
this.measure.slug = this.slugify(this.measure.name);
}
this.waiting = true;
try {
await measureStore.createMeasure(this.measure);
this.$router.push({ name: 'measures' });
} catch (e) {
// TODO: show errors
logger.error('Unable to create measure. \n\t ' + JSON.stringify(this.measure), e.stack);
} finally {
this.waiting = false;
}
}
}
export default NewMeasure;