web: WIP work on adding create measure functionality in the UI.
This commit is contained in:
parent
0fe3ccfdd2
commit
9d9f8c4f9b
@ -2,12 +2,18 @@
|
|||||||
<fieldset>
|
<fieldset>
|
||||||
<div>
|
<div>
|
||||||
<label for=measureType>Type</label>
|
<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=simple>Simple</option>
|
||||||
<option value=list>List</option>
|
<option value=list>List</option>
|
||||||
</select>
|
</select>
|
||||||
</div>
|
</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'"/>-->
|
<!--<ListMeasureConfigForm :config=config v-show="config.type === 'list'"/>-->
|
||||||
</fieldset>
|
</fieldset>
|
||||||
</template>
|
</template>
|
||||||
|
@ -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 { logService } from '@/services/logging';
|
||||||
import { Measure, MeasureConfig, MeasureType } from '@/models';
|
import { Measure, MeasureConfig, MeasureType } from '@/models';
|
||||||
|
|
||||||
@Component({})
|
@Component({})
|
||||||
export class MeasureConfigForm extends Vue {
|
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;
|
export default MeasureConfigForm;
|
||||||
|
@ -5,6 +5,7 @@ import App from './App.vue';
|
|||||||
import { store } from './store';
|
import { store } from './store';
|
||||||
import router from './router';
|
import router from './router';
|
||||||
import { FontAwesomeIcon } from '@fortawesome/vue-fontawesome';
|
import { FontAwesomeIcon } from '@fortawesome/vue-fontawesome';
|
||||||
|
import { dom as FA_DOM} from '@fortawesome/fontawesome-svg-core';
|
||||||
|
|
||||||
import SmartTable from 'vuejs-smart-table';
|
import SmartTable from 'vuejs-smart-table';
|
||||||
import ApexChart from 'vue-apexcharts';
|
import ApexChart from 'vue-apexcharts';
|
||||||
@ -13,6 +14,8 @@ import './registerServiceWorker';
|
|||||||
import { logService, LogLevel, ApiLogAppender, ConsoleLogAppender } from '@/services/logging';
|
import { logService, LogLevel, ApiLogAppender, ConsoleLogAppender } from '@/services/logging';
|
||||||
|
|
||||||
Vue.component('fa-icon', FontAwesomeIcon);
|
Vue.component('fa-icon', FontAwesomeIcon);
|
||||||
|
FA_DOM.watch();
|
||||||
|
|
||||||
Vue.component('apex-chart', ApexChart);
|
Vue.component('apex-chart', ApexChart);
|
||||||
Vue.use(SmartTable);
|
Vue.use(SmartTable);
|
||||||
|
|
||||||
|
@ -114,7 +114,7 @@ export class PmApiClient {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public async createMeasure<T extends MeasureConfig>(measure: Measure<T>): Promise<Measure<T>> {
|
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;
|
return resp.data;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -24,6 +24,14 @@ export class MeasureStoreModule extends VuexModule {
|
|||||||
public async fetchMeasure(slug: string) {
|
public async fetchMeasure(slug: string) {
|
||||||
const measure = api.getMeasure(slug);
|
const measure = api.getMeasure(slug);
|
||||||
this.context.commit('SET_MEASURE', measure);
|
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>) {
|
@Mutation private SET_MEASURE<T extends MeasureConfig>(measure: Measure<T>) {
|
||||||
|
@ -6,6 +6,7 @@
|
|||||||
border-radius: .25em;
|
border-radius: .25em;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
font-family: $body-font;
|
font-family: $body-font;
|
||||||
|
font-size: inherit;
|
||||||
font-weight: bold;
|
font-weight: bold;
|
||||||
padding: .5em 1em;
|
padding: .5em 1em;
|
||||||
|
|
||||||
@ -15,12 +16,21 @@
|
|||||||
.btn-action {
|
.btn-action {
|
||||||
background-color: $color2;
|
background-color: $color2;
|
||||||
color: $color3;
|
color: $color3;
|
||||||
|
cursor: pointer;
|
||||||
position: relative;
|
position: relative;
|
||||||
|
|
||||||
&:hover {
|
&:hover {
|
||||||
background-color: darken($color2, 5%);
|
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,
|
a.btn,
|
||||||
@ -43,8 +53,34 @@ a.btn-action { text-decoration: none; }
|
|||||||
|
|
||||||
.main { flex-grow: 1; }
|
.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,
|
input,
|
||||||
select {
|
select,
|
||||||
|
textarea {
|
||||||
font-size: inherit;
|
font-size: inherit;
|
||||||
padding: .35em .5em;
|
padding: .35em .5em;
|
||||||
|
|
||||||
|
&:disabled { cursor: wait; }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
textarea { font-family: $body-font; }
|
||||||
|
|
||||||
|
@ -4,34 +4,45 @@
|
|||||||
<h1>New Measure</h1>
|
<h1>New Measure</h1>
|
||||||
<h2>What do you want to measure?</h2>
|
<h2>What do you want to measure?</h2>
|
||||||
</div>
|
</div>
|
||||||
<form @submit.prevent=login() class=new-measure-form>
|
<form @submit.prevent=createMeasure() class=new-measure-form>
|
||||||
<fieldset>
|
<fieldset>
|
||||||
<div>
|
<div>
|
||||||
<label for=measureName>Display Name</label>
|
<label for=measureName>Display Name</label>
|
||||||
<input
|
<input
|
||||||
|
:disabled=waiting
|
||||||
type=text
|
type=text
|
||||||
name=measureName
|
name=measureName
|
||||||
placeholder="what you are measuring"
|
placeholder="what you are measuring"
|
||||||
|
required
|
||||||
v-model="measure.name" />
|
v-model="measure.name" />
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<label for=measureDescription>Description</label>
|
<label for=measureDescription>Description</label>
|
||||||
<input
|
<textarea
|
||||||
type=text
|
:disabled=waiting
|
||||||
name=measureDescription
|
name=measureDescription
|
||||||
placeholder="optional description"
|
placeholder="optional description"
|
||||||
v-model="measure.description" />
|
v-model="measure.description" ></textarea>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<label for=measureSlug>Slug (short-name)</label>
|
<label for=measureSlug>Short name (slug)</label>
|
||||||
<input
|
<input
|
||||||
|
:disabled=waiting
|
||||||
type=text
|
type=text
|
||||||
name=measureDescription
|
name=measureDescription
|
||||||
:placeholder='slugFromName'
|
:placeholder='slugFromName + " (default)"'
|
||||||
v-model="measure.slug" />
|
:value="measure.slug"
|
||||||
|
@input="measure.slug = slugify($event.target.value)"/>
|
||||||
</div>
|
</div>
|
||||||
</fieldset>
|
</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>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
@ -1,16 +1,19 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class=user-account>
|
<form class=user-account>
|
||||||
<h1>Your Account</h1>
|
<h1>Your Account</h1>
|
||||||
<fieldset>
|
<fieldset>
|
||||||
<legend>About You</legend>
|
<div>
|
||||||
<label for=name>Name: </label>
|
<label for=name>Name:</label>
|
||||||
<input name=name type=text :value="user.displayName"></input>
|
<input name=name type=text v-model="user.displayName" />
|
||||||
<label for=name>Email Address: </label>
|
</div>
|
||||||
<input name=name type=text :value="user.email"></input>
|
<div>
|
||||||
|
<label for=name>Email Address: </label>
|
||||||
|
<input name=name type=text v-model="user.email"></input>
|
||||||
|
</div>
|
||||||
</fieldset>
|
</fieldset>
|
||||||
<section class=api-tokens>
|
<section class=api-tokens>
|
||||||
<h2>API Tokens</h2>
|
<h2>API Tokens</h2>
|
||||||
<v-table :data=apiTokens>
|
<v-table v-show='apiTokens && apiTokens.length > 0' :data=apiTokens>
|
||||||
<thead slot=head>
|
<thead slot=head>
|
||||||
<v-th sortKey="name">Name</v-th>
|
<v-th sortKey="name">Name</v-th>
|
||||||
<v-th sortKey="created">Created</v-th>
|
<v-th sortKey="created">Created</v-th>
|
||||||
@ -24,11 +27,14 @@
|
|||||||
</tr>
|
</tr>
|
||||||
</tbody>
|
</tbody>
|
||||||
</v-table>
|
</v-table>
|
||||||
|
<div class=no-data v-show='!apiTokens || apiTokens.length === 0'>
|
||||||
|
You have not created any API tokens.
|
||||||
|
</div>
|
||||||
</section>
|
</section>
|
||||||
<section class=device-data>
|
<section class=device-data>
|
||||||
<h2>Data on this Device</h2>
|
<h2>Data on this Device</h2>
|
||||||
</section>
|
</section>
|
||||||
</div>
|
</form>
|
||||||
</template>
|
</template>
|
||||||
<script lang="ts" src="./user-account.ts"></script>
|
<script lang="ts" src="./user-account.ts"></script>
|
||||||
<style scoped lang="scss" src="./user-account.scss"></style>
|
<style scoped lang="scss" src="./user-account.scss"></style>
|
||||||
|
@ -8,3 +8,16 @@
|
|||||||
margin-right: 2rem;
|
margin-right: 2rem;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.measure-list {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
justify-content: space-evenly;
|
||||||
|
|
||||||
|
.measure-summary {
|
||||||
|
height: 12rem;
|
||||||
|
width: 12rem;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@ -1,7 +1,8 @@
|
|||||||
@import '~@/styles/vars';
|
@import '~@/styles/vars';
|
||||||
|
|
||||||
input,
|
input,
|
||||||
select {
|
select,
|
||||||
|
textarea {
|
||||||
// background-color: $bg-primary;
|
// background-color: $bg-primary;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -13,17 +14,3 @@ select {
|
|||||||
border: solid thin $color1;
|
border: solid thin $color1;
|
||||||
border-radius: 4px;
|
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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
@ -1,16 +1,20 @@
|
|||||||
import { Component, Prop, Vue, Watch } from 'vue-property-decorator';
|
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 { logService } from '@/services/logging';
|
||||||
import { userStore } from '@/store';
|
import { measureStore, userStore } from '@/store';
|
||||||
import { Measure, MeasureConfig, MeasureType } from '@/models';
|
import { Measure, MeasureConfig, MeasureType } from '@/models';
|
||||||
import MeasureConfigForm from '@/components/measure-config/MeasureConfigForm.vue';
|
import MeasureConfigForm from '@/components/measure-config/MeasureConfigForm.vue';
|
||||||
|
|
||||||
|
library.add(faSync);
|
||||||
|
|
||||||
const logger = logService.getLogger('/views/new-measure');
|
const logger = logService.getLogger('/views/new-measure');
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
components: { MeasureConfigForm }
|
components: { MeasureConfigForm }
|
||||||
})
|
})
|
||||||
export class NewMeasure extends Vue {
|
export class NewMeasure extends Vue {
|
||||||
private test: string = 'test';
|
private waiting = false;
|
||||||
private measure: Measure<MeasureConfig> = {
|
private measure: Measure<MeasureConfig> = {
|
||||||
id: '',
|
id: '',
|
||||||
config: {
|
config: {
|
||||||
@ -24,11 +28,33 @@ export class NewMeasure extends Vue {
|
|||||||
};
|
};
|
||||||
|
|
||||||
private get slugFromName() {
|
private get slugFromName() {
|
||||||
return this.measure.name
|
return this.slugify(this.measure.name);
|
||||||
|
}
|
||||||
|
|
||||||
|
private slugify(s: string): string {
|
||||||
|
return s
|
||||||
.toLowerCase()
|
.toLowerCase()
|
||||||
.replace(/[^\w\s]/g, '')
|
.replace(/[^\w\s\-]/g, '')
|
||||||
.replace(/\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;
|
export default NewMeasure;
|
||||||
|
Loading…
x
Reference in New Issue
Block a user