github.com/muhammedhassanm/blockchain@v0.0.0-20200120143007-697261defd4d/sawtooth-supply-chain-master/asset_client/src/views/property_detail.js (about) 1 /** 2 * Copyright 2017 Intel Corporation 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 * ---------------------------------------------------------------------------- 16 */ 17 'use strict' 18 19 const m = require('mithril') 20 const _ = require('lodash') 21 22 const api = require('../services/api') 23 const payloads = require('../services/payloads') 24 const parsing = require('../services/parsing') 25 const transactions = require('../services/transactions') 26 const layout = require('../components/layout') 27 const { LineGraphWidget, MapWidget } = require('../components/data') 28 const { Table, PagingButtons } = require('../components/tables') 29 30 const PAGE_SIZE = 50 31 32 const withIntVal = fn => m.withAttr('value', v => fn(parsing.toInt(v))) 33 34 const typedWidget = state => { 35 const property = _.get(state, 'property', {}) 36 37 if (property.dataType === 'LOCATION') { 38 return m(MapWidget, { 39 coordinates: property.updates.map(update => update.value) 40 }) 41 } 42 43 if (property.dataType === 'NUMBER') { 44 return m(LineGraphWidget, { updates: property.updates }) 45 } 46 47 return null 48 } 49 50 const updateSubmitter = state => e => { 51 e.preventDefault() 52 const { name, dataType, recordId } = state.property 53 54 let value = null 55 if (state.update) { 56 value = state.update 57 } else { 58 value = state.tmp 59 } 60 61 const update = { name } 62 update.dataType = payloads.updateProperties.enum[dataType] 63 update[`${dataType.toLowerCase()}Value`] = value 64 65 const payload = payloads.updateProperties({ 66 recordId, 67 properties: [update] 68 }) 69 70 transactions.submit(payload, true) 71 .then(() => api.get(`records/${recordId}/${name}`)) 72 .then(property => { 73 _.each(e.target.elements, el => { el.value = null }) 74 state.update = null 75 state.tmp = {} 76 property.updates.forEach(update => { 77 update.value = parsing.floatifyValue(update.value) 78 }) 79 state.property = property 80 }) 81 } 82 83 // Produces an input field particular to the type of data 84 const typedInput = state => { 85 if (state.property.dataType === 'NUMBER') { 86 return m('.col-md-8', [ 87 m('input.form-control', { 88 placeholder: 'Enter New Value...', 89 oninput: withIntVal(value => { state.update = value }) 90 }) 91 ]) 92 } 93 94 if (state.property.dataType === 'LOCATION') { 95 return [ 96 m('.col.md-4.mr-1', 97 m('input.form-control', { 98 placeholder: 'Enter New Latitude...', 99 oninput: withIntVal(value => { state.tmp.latitude = value }) 100 })), 101 m('.col.md-4', 102 m('input.form-control', { 103 placeholder: 'Enter New Longitude...', 104 oninput: withIntVal(value => { state.tmp.longitude = value }) 105 })) 106 ] 107 } 108 109 return m('.col-md-8', [ 110 m('input.form-control', { 111 placeholder: 'Enter New Value...', 112 oninput: m.withAttr('value', value => { state.update = value }) 113 }) 114 ]) 115 } 116 117 const updateForm = state => { 118 const inputField = typedInput(state) 119 if (!inputField) return null 120 121 return m('form.my-5', { 122 onsubmit: updateSubmitter(state) 123 }, [ 124 m('.container', 125 m('.row.justify-content-center', 126 inputField, 127 m('.col-md-2', 128 m('button.btn.btn-primary', { type: 'submit' }, 'Update')))) 129 ]) 130 } 131 132 /** 133 * Displays updates to a property, and form for submitting new updates. 134 */ 135 const PropertyDetailPage = { 136 oninit (vnode) { 137 vnode.state.currentPage = 0 138 vnode.state.tmp = {} 139 140 const refresh = () => { 141 api.get(`records/${vnode.attrs.recordId}/${vnode.attrs.name}`) 142 .then(property => { 143 property.updates.forEach(update => { 144 update.value = parsing.floatifyValue(update.value) 145 }) 146 vnode.state.property = property 147 }) 148 .then(() => { vnode.state.refreshId = setTimeout(refresh, 2000) }) 149 } 150 151 refresh() 152 }, 153 154 onbeforeremove (vnode) { 155 clearTimeout(vnode.state.refreshId) 156 }, 157 158 view (vnode) { 159 const name = _.capitalize(vnode.attrs.name) 160 const record = vnode.attrs.recordId 161 162 const reporters = _.get(vnode.state, 'property.reporters', []) 163 const isReporter = reporters.includes(api.getPublicKey()) 164 165 const updates = _.get(vnode.state, 'property.updates', []) 166 const page = updates.slice(vnode.state.currentPage * PAGE_SIZE, 167 (vnode.state.currentPage + 1) * PAGE_SIZE) 168 169 return [ 170 layout.title(`${name} of ${record}`), 171 typedWidget(vnode.state), 172 isReporter ? updateForm(vnode.state) : null, 173 m('.container', 174 layout.row([ 175 m('h5.mr-auto', 'Update History'), 176 m(PagingButtons, { 177 setPage: page => { vnode.state.currentPage = page }, 178 currentPage: vnode.state.currentPage, 179 maxPage: updates.length / PAGE_SIZE 180 }) 181 ]), 182 m(Table, { 183 headers: ['Value', 'Reporter', 'Time'], 184 rows: page.map(update => { 185 return [ 186 parsing.stringifyValue(update.value, 187 vnode.state.property.dataType, 188 vnode.state.property.name), 189 update.reporter.name, 190 parsing.formatTimestamp(update.timestamp) 191 ] 192 }), 193 noRowsText: 'This property has never been updated' 194 })) 195 ] 196 } 197 } 198 199 module.exports = PropertyDetailPage