github.com/muhammedhassanm/blockchain@v0.0.0-20200120143007-697261defd4d/sawtooth-supply-chain-master/fish_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 if (property.name === 'tilt') { 48 return m(LineGraphWidget, { 49 updates: property.updates.map(update => { 50 const amplitude = Math.sqrt(update.value.x ** 2 + update.value.y ** 2) 51 return _.assign({}, update, {value: amplitude.toFixed(3)}) 52 }) 53 }) 54 } 55 56 if (property.name === 'shock') { 57 return m(LineGraphWidget, { 58 updates: property.updates.map(update => { 59 const degree = update.value.duration === 0 60 ? 0 61 : update.value.accel / update.value.duration 62 return _.assign({}, update, {value: degree.toFixed(3)}) 63 }) 64 }) 65 } 66 67 return null 68 } 69 70 const updateSubmitter = state => e => { 71 e.preventDefault() 72 const { name, dataType, recordId } = state.property 73 74 let value = null 75 if (state.update) { 76 value = state.update 77 } else if (name === 'tilt' || name === 'shock') { 78 value = JSON.stringify(state.tmp) 79 } else { 80 value = state.tmp 81 } 82 83 const update = { name } 84 update.dataType = payloads.updateProperties.enum[dataType] 85 update[`${dataType.toLowerCase()}Value`] = value 86 87 const payload = payloads.updateProperties({ 88 recordId, 89 properties: [update] 90 }) 91 92 transactions.submit(payload, true) 93 .then(() => api.get(`records/${recordId}/${name}`)) 94 .then(property => { 95 _.each(e.target.elements, el => { el.value = null }) 96 state.update = null 97 state.tmp = {} 98 property.updates.forEach(update => { 99 update.value = parsing.floatifyValue(update.value) 100 }) 101 state.property = property 102 }) 103 } 104 105 // Produces custom input fields for location, tilt, and shock 106 const typedInput = state => { 107 const { dataType, name } = state.property 108 109 if (dataType === 'LOCATION') { 110 return [ 111 m('.col.md-4.mr-1', 112 m('input.form-control', { 113 placeholder: 'Enter Latitude...', 114 oninput: withIntVal(value => { state.tmp.latitude = value }) 115 })), 116 m('.col.md-4', 117 m('input.form-control', { 118 placeholder: 'Enter Longitude...', 119 oninput: withIntVal(value => { state.tmp.longitude = value }) 120 })) 121 ] 122 } 123 124 if (name === 'tilt') { 125 return [ 126 m('.col.md-4.mr-1', 127 m('input.form-control', { 128 placeholder: 'Enter X...', 129 oninput: withIntVal(value => { state.tmp.x = value }) 130 })), 131 m('.col.md-4', 132 m('input.form-control', { 133 placeholder: 'Enter Y...', 134 oninput: withIntVal(value => { state.tmp.y = value }) 135 })) 136 ] 137 } 138 139 if (name === 'shock') { 140 return [ 141 m('.col.md-4.mr-1', 142 m('input.form-control', { 143 placeholder: 'Enter Acceleration...', 144 oninput: withIntVal(value => { state.tmp.accel = value }) 145 })), 146 m('.col.md-4', 147 m('input.form-control', { 148 placeholder: 'Enter Duration...', 149 oninput: withIntVal(value => { state.tmp.duration = value }) 150 })) 151 ] 152 } 153 154 if (name === 'temperature') { 155 return m('.col-md-8', [ 156 m('input.form-control', { 157 placeholder: 'Enter Temperature...', 158 oninput: withIntVal(value => { state.update = value }) 159 }) 160 ]) 161 } 162 163 return null 164 } 165 166 const updateForm = state => { 167 const inputField = typedInput(state) 168 if (!inputField) return null 169 170 return m('form.my-5', { 171 onsubmit: updateSubmitter(state) 172 }, [ 173 m('.container', 174 m('.row.justify-content-center', 175 inputField, 176 m('.col-md-2', 177 m('button.btn.btn-primary', { type: 'submit' }, 'Update')))) 178 ]) 179 } 180 181 /** 182 * Displays updates to a property, and form for submitting new updates. 183 */ 184 const PropertyDetailPage = { 185 oninit (vnode) { 186 vnode.state.currentPage = 0 187 vnode.state.tmp = {} 188 189 const refresh = () => { 190 api.get(`records/${vnode.attrs.recordId}/${vnode.attrs.name}`) 191 .then(property => { 192 property.updates.forEach(update => { 193 update.value = parsing.floatifyValue(update.value) 194 }) 195 vnode.state.property = property 196 }) 197 .then(() => { vnode.state.refreshId = setTimeout(refresh, 2000) }) 198 } 199 200 refresh() 201 }, 202 203 onbeforeremove (vnode) { 204 clearTimeout(vnode.state.refreshId) 205 }, 206 207 view (vnode) { 208 const name = _.capitalize(vnode.attrs.name) 209 const record = vnode.attrs.recordId 210 211 const reporters = _.get(vnode.state, 'property.reporters', []) 212 const isReporter = reporters.includes(api.getPublicKey()) 213 214 const updates = _.get(vnode.state, 'property.updates', []) 215 const page = updates.slice(vnode.state.currentPage * PAGE_SIZE, 216 (vnode.state.currentPage + 1) * PAGE_SIZE) 217 218 return [ 219 layout.title(`${name} of ${record}`), 220 typedWidget(vnode.state), 221 isReporter ? updateForm(vnode.state) : null, 222 m('.container', 223 layout.row([ 224 m('h5.mr-auto', 'Update History'), 225 m(PagingButtons, { 226 setPage: page => { vnode.state.currentPage = page }, 227 currentPage: vnode.state.currentPage, 228 maxPage: updates.length / PAGE_SIZE 229 }) 230 ]), 231 m(Table, { 232 headers: ['Value', 'Reporter', 'Time'], 233 rows: page.map(update => { 234 return [ 235 parsing.stringifyValue(update.value, 236 vnode.state.property.dataType, 237 vnode.state.property.name), 238 update.reporter.name, 239 parsing.formatTimestamp(update.timestamp) 240 ] 241 }), 242 noRowsText: 'This property has never been updated' 243 })) 244 ] 245 } 246 } 247 248 module.exports = PropertyDetailPage