Compare commits

...

No commits in common. "addNodeServer" and "master" have entirely different histories.

19 changed files with 191 additions and 47 deletions

1
.github/FUNDING.yml vendored Normal file
View File

@ -0,0 +1 @@
github: [joshuashoemaker]

View File

@ -0,0 +1,15 @@
# <img alt="data lovelace logo" src="./public/favicon.png" width="30px"/> [dataLovelace.app](https://datalovelace.app)
[💛 Support this Project 💛](https://github.com/sponsors/joshuashoemaker/)
Lovelace is a simple to use web application that puts the power of data reporting into the hands of those who need it for free. It is a Free and Open Source Software (FOSS) project.
With Lovelace you can take all of your varying data sources, aggregate them to one place, and perform what ever kind of reports on them you need to either create beautiful chart representations, or robust tabular exports of your data.
![Data Lovelace Example](./docs/graphExample.png)
### [Read the Documentation](https://docs.datalovelace.app)
or
### [Jump Right In](https://datalovelace.app) and play with the tool yourself

BIN
docs/graphExample.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 192 KiB

View File

@ -1,5 +1,5 @@
{
"name": "lovelace.technology",
"name": "datalovelace",
"version": "0.1.0",
"private": true,
"type": "module",

View File

@ -7,7 +7,7 @@
<meta name="theme-color" content="#000000" />
<meta
name="description"
content="Web site created using create-react-app"
content="With Lovelace you can take all of your varying data sources, aggregate them to one place, and perform what ever kind of reports on them you need to either create beautiful chart representations, or a robust tabular export of your data."
/>
<link rel="apple-touch-icon" href="%PUBLIC_URL%/logo192.png" />
<!--

View File

@ -1,19 +1,20 @@
{
"short_name": "React App",
"name": "Create React App Sample",
"short_name": "Lovelace",
"name": "datalovelace.app",
"description": "With Lovelace you can take all of your varying data sources, aggregate them to one place, and perform what ever kind of reports on them you need to either create beautiful chart representations, or a robust tabular export of your data.",
"icons": [
{
"src": "favicon.ico",
"src": "favicon.png",
"sizes": "64x64 32x32 24x24 16x16",
"type": "image/x-icon"
},
{
"src": "logo192.png",
"src": "favicon.png",
"type": "image/png",
"sizes": "192x192"
},
{
"src": "logo512.png",
"src": "favicon.png",
"type": "image/png",
"sizes": "512x512"
}

1
src/img/faq.svg Normal file
View File

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 80 80" width="16px" height="16px"><path fill="#f78f8f" d="M76.993,77.479c-4.54-0.181-9.738-1.562-14.762-3.935l-0.193-0.091L61.84,73.53 C58.411,74.837,54.764,75.5,51,75.5c-14.612,0-26.5-10.094-26.5-22.5S36.388,30.5,51,30.5S77.5,40.594,77.5,53 c0,5.191-2.134,10.258-6.009,14.266l-0.242,0.251L71.4,67.83C73.215,71.602,75.094,74.842,76.993,77.479z"/><path fill="#c74343" d="M51,31c14.336,0,26,9.869,26,22c0,5.061-2.084,10.004-5.869,13.918l-0.484,0.501l0.302,0.628 c1.637,3.402,3.327,6.377,5.04,8.873c-4.223-0.329-8.954-1.659-13.545-3.827l-0.385-0.182l-0.398,0.152 C58.29,74.348,54.703,75,51,75c-14.336,0-26-9.869-26-22S36.664,31,51,31 M51,30c-14.912,0-27,10.297-27,23 c0,12.703,12.088,23,27,23c3.926,0,7.652-0.72,11.017-2.003C67.105,76.4,72.814,78,78,78c-2.352-3.098-4.402-6.754-6.15-10.387 C75.692,63.64,78,58.55,78,53C78,40.297,65.912,30,51,30L51,30z"/><path fill="#fff" d="M61.534,62.538h-5.33l-1.545-4.83h-7.723l-1.529,4.83h-5.3l7.904-21.714h5.8L61.534,62.538z M53.539,53.952c0,0-2.625-8.495-2.695-9.252h-0.121c-0.05,0.636-2.741,9.252-2.741,9.252H53.539z"/><path fill="#8bb7f0" d="M3.007,49.479c1.899-2.638,3.778-5.878,5.593-9.649l0.151-0.313l-0.242-0.251 C4.634,35.258,2.5,30.191,2.5,25C2.5,12.594,14.388,2.5,29,2.5S55.5,12.594,55.5,25S43.612,47.5,29,47.5 c-3.764,0-7.411-0.663-10.84-1.97l-0.198-0.076l-0.193,0.091C12.745,47.918,7.547,49.299,3.007,49.479z"/><path fill="#4e7ab5" d="M29,3c14.336,0,26,9.869,26,22S43.336,47,29,47c-3.703,0-7.29-0.652-10.661-1.937l-0.398-0.152 l-0.385,0.182c-4.591,2.168-9.322,3.498-13.545,3.827c1.712-2.496,3.403-5.471,5.04-8.873l0.302-0.628l-0.484-0.501 C5.084,35.004,3,30.061,3,25C3,12.869,14.664,3,29,3 M29,2C14.088,2,2,12.297,2,25c0,5.55,2.308,10.64,6.15,14.613 C6.402,43.246,4.352,46.902,2,50c5.186,0,10.895-1.6,15.983-4.003C21.348,47.28,25.074,48,29,48c14.912,0,27-10.297,27-23 C56,12.297,43.912,2,29,2L29,2z"/><g><path fill="#fff" d="M28.534,35.965c-2.986,0-5.42-1.077-7.301-3.232c-1.881-2.176-2.822-4.993-2.822-8.452 c0-3.652,0.955-6.615,2.865-8.888c1.9-2.262,4.431-3.394,7.592-3.394c2.977,0,5.381,1.083,7.214,3.248 c1.842,2.155,2.763,5.01,2.763,8.565c0,3.631-0.955,6.566-2.865,8.807c-0.068,0.075-0.131,0.148-0.189,0.218 c-0.058,0.07-0.121,0.137-0.189,0.202l5.279,5.64h-6.574l-2.763-3.119C30.634,35.831,29.63,35.965,28.534,35.965z M28.738,16.493 c-1.639,0-2.948,0.689-3.927,2.068c-0.97,1.368-1.454,3.184-1.454,5.446c0,2.295,0.485,4.11,1.454,5.446 c0.97,1.336,2.24,2.004,3.811,2.004c1.619,0,2.904-0.646,3.854-1.939c0.95-1.314,1.425-3.119,1.425-5.414 c0-2.392-0.461-4.261-1.382-5.608C31.608,17.16,30.348,16.493,28.738,16.493z"/></g></svg>

After

Width:  |  Height:  |  Size: 2.6 KiB

1
src/img/information.svg Normal file
View File

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 100 100" width="50px" height="50px"><circle cx="77" cy="13" r="1" fill="#f1bc19"/><circle cx="50" cy="50" r="37" fill="#e6edb7"/><circle cx="83" cy="15" r="4" fill="#f1bc19"/><circle cx="87" cy="24" r="2" fill="#88ae45"/><circle cx="81" cy="76" r="2" fill="#fbcd59"/><circle cx="15" cy="63" r="4" fill="#fbcd59"/><circle cx="25" cy="87" r="2" fill="#88ae45"/><circle cx="18.5" cy="51.5" r="2.5" fill="#fff"/><circle cx="79.5" cy="33.5" r="1.5" fill="#fff"/><g><path fill="#f4989e" d="M37.084,74.3c-0.212,0-0.384-0.172-0.384-0.384v-4.832c0-0.212,0.172-0.384,0.384-0.384h5.724 c1.044,0,1.893-0.849,1.893-1.893V46.192c0-1.044-0.849-1.893-1.893-1.893h-3.724c-0.212,0-0.384-0.172-0.384-0.384v-4.832 c0-0.212,0.172-0.384,0.384-0.384h16.832c0.212,0,0.384,0.172,0.384,0.384v27.724c0,1.044,0.849,1.893,1.893,1.893h6.724 c0.212,0,0.384,0.172,0.384,0.384v4.832c0,0.212-0.172,0.384-0.384,0.384H37.084z"/><path fill="#472b29" d="M55.6,39.4V41v4v21.808c0,1.429,1.163,2.592,2.593,2.592H64.6v4.2H37.4v-4.2h5.407 c1.43,0,2.593-1.163,2.593-2.592V46.192c0-1.429-1.163-2.592-2.593-2.592H39.4v-4.2H55.6 M55.916,38H39.084 C38.485,38,38,38.485,38,39.084v4.832C38,44.515,38.485,45,39.084,45h3.724C43.466,45,44,45.534,44,46.192v20.615 C44,67.466,43.466,68,42.807,68h-5.724C36.485,68,36,68.485,36,69.084v4.832C36,74.515,36.485,75,37.084,75h27.832 C65.515,75,66,74.515,66,73.916v-4.832C66,68.485,65.515,68,64.916,68h-6.724C57.534,68,57,67.466,57,66.808V45v-4v-1.916 C57,38.485,56.515,38,55.916,38L55.916,38z"/><g><ellipse cx="49.5" cy="27.5" fill="#f4989e" rx="6.8" ry="5.8"/><path fill="#472b29" d="M49.5,22.4c3.364,0,6.1,2.288,6.1,5.1s-2.736,5.1-6.1,5.1s-6.1-2.288-6.1-5.1 S46.136,22.4,49.5,22.4 M49.5,21c-4.142,0-7.5,2.91-7.5,6.5s3.358,6.5,7.5,6.5s7.5-2.91,7.5-6.5S53.642,21,49.5,21L49.5,21z"/></g></g><g><path fill="#472b29" d="M62.5,71.25h-5.292c-2.183,0-3.958-1.775-3.958-3.958V63c0-0.138,0.112-0.25,0.25-0.25 s0.25,0.112,0.25,0.25v4.292c0,1.907,1.551,3.458,3.458,3.458H62.5c0.138,0,0.25,0.112,0.25,0.25S62.638,71.25,62.5,71.25z"/></g><g><path fill="#472b29" d="M53.5,60.667c-0.138,0-0.25-0.112-0.25-0.25V41.75h-2.167c-0.138,0-0.25-0.112-0.25-0.25 s0.112-0.25,0.25-0.25h2.667v19.167C53.75,60.555,53.638,60.667,53.5,60.667z"/></g><g><path fill="#472b29" d="M49.167,41.75H46.25c-0.138,0-0.25-0.112-0.25-0.25s0.112-0.25,0.25-0.25h2.917 c0.138,0,0.25,0.112,0.25,0.25S49.305,41.75,49.167,41.75z"/></g><g><path fill="#472b29" d="M44.25,41.75H41.5c-0.138,0-0.25-0.112-0.25-0.25s0.112-0.25,0.25-0.25h2.75 c0.138,0,0.25,0.112,0.25,0.25S44.388,41.75,44.25,41.75z"/></g></svg>

After

Width:  |  Height:  |  Size: 2.5 KiB

1
src/img/sponsor.svg Normal file
View File

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 80 80" width="16px" height="16px"><path fill="#efd476" d="M39.1,69.3l23.4-11.7c2.6-1.3,4.2-4.6,3.2-7.4c-0.9-2.3-3.1-3.7-5.4-3.7c-0.7,0-1.5,0.1-2.2,0.4 L39,54.8"/><path fill="#c59b3f" d="M39.4,69.8L39,68.9l23.4-11.7c2.5-1.2,3.8-4.3,2.9-6.7c-0.8-2.1-2.7-3.4-4.9-3.4 c-0.7,0-1.4,0.1-2,0.4l-19.1,7.9l-0.4-0.9L58,46.6c0.8-0.3,1.6-0.5,2.4-0.5c2.6,0,4.9,1.6,5.8,4c1.1,2.9-0.5,6.6-3.4,8L39.4,69.8z"/><path fill="#efd476" d="M45.1,71.3l23.4-11.7c2.6-1.3,4.2-4.6,3.2-7.4c-0.9-2.3-3.1-3.7-5.4-3.7c-0.7,0-1.5,0.1-2.2,0.4 L45,56.8"/><path fill="#c59b3f" d="M45.4,71.8L45,70.9l23.4-11.7c2.5-1.2,3.8-4.3,2.9-6.7c-0.8-2.1-2.7-3.4-4.9-3.4 c-0.7,0-1.4,0.1-2,0.4l-19.1,7.9l-0.4-0.9L64,48.6c0.8-0.3,1.6-0.5,2.4-0.5c2.6,0,4.9,1.6,5.8,4c1.1,2.9-0.5,6.6-3.4,8L45.4,71.8z"/><path fill="#ffee9f" d="M48.2,73.5c-0.9,0-1.7-0.2-2.5-0.6L15.6,59.1L2.5,64.4V43.7l11.1-4.1c2-0.7,4-1.1,6.1-1.1 c2.9,0,5.7,0.7,8.2,2l19.1,10c2.6,1.4,3.6,4.6,2.3,7.2l-0.2,0.4l1.7,1.2l19.4-8c0.6-0.3,1.3-0.4,2-0.4c2.2,0,4.1,1.3,4.9,3.4 c0.9,2.4-0.4,5.5-2.9,6.7L50.9,72.9C50.1,73.3,49.1,73.5,48.2,73.5z"/><path fill="#c59b3f" d="M19.8,39c2.8,0,5.5,0.7,8,2l19.1,10c2.4,1.2,3.3,4.2,2.1,6.6l-0.4,0.8l0.7,0.5l1.2,0.8l0.4,0.3 l0.5-0.2l19.2-7.9c0.6-0.2,1.2-0.4,1.8-0.4c2,0,3.7,1.2,4.4,3.1c0.8,2.2-0.4,5-2.7,6.1L50.7,72.4c-0.8,0.4-1.6,0.6-2.5,0.6 c-0.8,0-1.6-0.2-2.3-0.5L16,58.8l-0.4-0.2l-0.4,0.2L3,63.6V44.1l10.8-4C15.7,39.4,17.7,39,19.8,39 M19.8,38c-2.1,0-4.3,0.4-6.3,1.1 L2,43.4v21.7l13.6-5.4l29.9,13.7c0.9,0.4,1.8,0.6,2.7,0.6c1,0,2-0.2,2.9-0.7l23.4-11.7c2.6-1.3,4.2-4.6,3.2-7.4 c-0.9-2.3-3.1-3.7-5.4-3.7c-0.7,0-1.5,0.1-2.2,0.4L51,58.8L49.8,58c1.5-2.9,0.3-6.4-2.5-7.9l-19.1-10C25.6,38.7,22.7,38,19.8,38 L19.8,38z"/><path fill="#c59b3f" d="M45.7,60.6c-0.7,0-1.4-0.2-2-0.5l-12.3-6.2l0.5-0.9l12.3,6.2c1.6,0.8,3.7,0.2,4.6-1.4l0.9,0.5 C48.8,59.8,47.3,60.6,45.7,60.6z"/><g><path fill="#ffee9f" d="M3.7 43.8L2 44.5 2 64 3.7 63.4z"/></g><g><path fill="#ff8f8f" d="M48,37.394C45.74,35.831,30.5,24.95,30.5,16c0-5.505,3.866-9.5,9.192-9.5 c3.238,0,5.678,1.326,7.907,4.3l0.4,0.534l0.4-0.534c2.229-2.974,4.669-4.3,7.907-4.3c5.326,0,9.192,3.995,9.192,9.5 C65.5,24.953,50.261,35.831,48,37.394z"/><path fill="#e54141" d="M56.308,7C61.344,7,65,10.785,65,16c0,8.395-14.013,18.687-17,20.785C45.011,34.687,31,24.401,31,16 c0-5.215,3.656-9,8.692-9c3.107,0,5.353,1.226,7.508,4.1l0.8,1.067l0.8-1.067C50.955,8.226,53.201,7,56.308,7 M56.308,6 C52.776,6,50.25,7.5,48,10.5c-2.25-3-4.776-4.5-8.308-4.5C34.339,6,30,10,30,16c0,10,18,22,18,22s18-12,18-22 C66,10,61.661,6,56.308,6L56.308,6z"/></g></svg>

After

Width:  |  Height:  |  Size: 2.5 KiB

View File

@ -0,0 +1,7 @@
.chartLabel {
text-align: center;
margin: 10px auto;
}
.saveChartAsImageButton {
}

View File

@ -3,6 +3,7 @@ import FocusChart from '../../Models/Chart/FocusChart'
import { Doughnut, Bar, Line, Pie, Polar, Radar, Scatter, Bubble } from 'react-chartjs-2'
import { Button } from 'semantic-ui-react'
import download from 'downloadjs'
import './ChartViewer.css'
class ChartViewer extends Component {
constructor () {
@ -17,6 +18,11 @@ class ChartViewer extends Component {
document.addEventListener('setSelectedChart', this.setFocusChart)
}
closeChart = () => {
this.focusChart.chart = null
this.setFocusChart()
}
handleGroupByChange = (e, value) => {
this.setState({ reportValue: value.value })
}
@ -28,12 +34,10 @@ class ChartViewer extends Component {
setFocusChart = () => {
const focusChart = this.focusChart.chart
if (focusChart) {
this.setState({
chart: focusChart
})
}
}
renderChart = () => {
const { chart } = this.state
@ -50,11 +54,14 @@ class ChartViewer extends Component {
}
render = () => {
const chart = this.state.chart || {}
return (
<div className='ChartViewer'>
<h3 className='chartLabel'>{ chart.label }</h3>
{this.renderChart()}
<br />
{ this.state.chart ? <Button fluid onClick={this.saveChart}>Save As Image</Button> : '' }
{ this.state.chart ? <Button className='saveChartAsImageButton' fluid primary onClick={this.saveChart}>Save As Image</Button> : '' }
{ this.state.chart ? <Button className='closeChartButton' fluid onClick={this.closeChart}>Close Chart</Button> : '' }
</div>
)
}

View File

@ -1,15 +1,19 @@
import React, { Component } from 'react'
import { Input, Dropdown, Grid, Button, Icon, List } from 'semantic-ui-react'
import Tables from '../../Models/Tables'
import './CreateNodule.css'
class CreateFilterNoduleForm extends Component {
constructor () {
super()
constructor (props) {
super(props)
this.state = {
filterType: '',
filterParams: {}
filterParams: {},
tables: props.tables
}
this.tables = new Tables()
this.keyInput = React.createRef()
this.valueInput = React.createRef()
document.addEventListener('updateTables', this.updateTableList)
@ -18,21 +22,44 @@ class CreateFilterNoduleForm extends Component {
addKeyValueInput = () => {
let filterParams = this.state.filterParams || {}
if (this.keyInput.current.inputRef.current.value)
filterParams[this.keyInput.current.inputRef.current.value] = this.valueInput.current.inputRef.current.value
if (this.state.keyValue)
filterParams[this.state.keyValue] = this.valueInput.current.inputRef.current.value
this.setState({ filterParams: filterParams })
}
handleChange = (e, value) => {
componentWillReceiveProps = nextProps => {
this.setState({tables: nextProps.tables})
}
handleComparisonChange = (e, value) => {
this.setState({ filterType: value.value })
}
handleKeyChange = (e, value) => {
this.setState({ keyValue: value.value })
}
getFilterProperties = () => {
const { filterType, filterParams } = this.state
return { filterType, filterParams }
}
getHeadersDropDownOptions = () => {
const { tables } = this.state
if (!tables || tables.length === 0) return []
const headers = tables.map(t => {
const table = this.tables.getTableByLabel(t)
return table.headers
}).flat()
const dropdownOptions = headers.map(h => {
return { key: h, text: h, value: h }
})
return dropdownOptions
}
renderFilterParams = () => {
const { filterParams } = this.state
@ -61,7 +88,7 @@ class CreateFilterNoduleForm extends Component {
{key: 'LESSER', text: 'LESSER THAN', value: 'LESSER'},
{key: 'LESSEREQUAL', text: 'LESSER THEN EQUAL TO', value: 'LESSEREQUAL'}
]}
onChange={this.handleChange}
onChange={this.handleComparisonChange}
/>
<Grid columns={2} stackable>
@ -69,7 +96,18 @@ class CreateFilterNoduleForm extends Component {
<List celled>
{ filterParamElements.filterKeyElements }
</List>
<Input placeholder='Key' ref={this.keyInput} fluid />
<Dropdown
clearable
search
header='Headers'
placeholder='Header Key'
fluid
selection
options={this.getHeadersDropDownOptions()}
onChange={this.handleKeyChange}
/>
</Grid.Column>
<Grid.Column>
<List celled>

View File

@ -156,9 +156,8 @@ class CreateJoinNoduleForm extends Component {
options={this.getForeignHeadersDropDownOptions()}
onChange={this.handleForeignKeyChange}
/>
<br />
<Button animated='vertical' onClick={this.addJoinParam}>
<Button fluid animated='vertical' onClick={this.addJoinParam}>
<Button.Content hidden><Icon name='add' /></Button.Content>
<Button.Content visible>Add</Button.Content>
</Button>

View File

@ -15,7 +15,8 @@ class CreateNodule extends Component {
this.state = {
noduleType: '',
tablesToImportByLabel: []
tablesToImportByLabel: [],
selectedTablesLabels: []
}
this.tables = new Tables()
@ -32,13 +33,18 @@ class CreateNodule extends Component {
clearInput = () => {
this.setState({ noduleType: '' })
this.noduleLabelInput.current.inputRef.current.value = ''
this.tableSelect.current.clearTablesSelected()
}
handleChange = (e, value) => {
handleNoduleTypeChange = (e, value) => {
this.setState({ noduleType: value.value })
}
handleSelectedTablesChange = labels => {
this.setState({ selectedTablesLabels: labels })
}
handleSubmit = () => {
const { noduleType } = this.state
@ -79,11 +85,12 @@ class CreateNodule extends Component {
}
renderNoduleForm = () => {
const { noduleType, tablesToImportByLabel } = this.state
const { noduleType, selectedTablesLabels } = this.state
console.log(selectedTablesLabels)
if (noduleType === 'filter') return <CreateFilterNoduleForm ref={this.filterNoduleForm} />
else if (noduleType === 'join') return <CreateJoinNoduleForm ref={this.joinNoduleForm} tables={tablesToImportByLabel || []}/>
else if (noduleType === 'transform') return <CreateTransformNoduleForm ref={this.transformNoduleForm} tables={tablesToImportByLabel || []}/>
if (noduleType === 'filter') return <CreateFilterNoduleForm ref={this.filterNoduleForm} tables={selectedTablesLabels} />
else if (noduleType === 'join') return <CreateJoinNoduleForm ref={this.joinNoduleForm} tables={selectedTablesLabels} />
else if (noduleType === 'transform') return <CreateTransformNoduleForm ref={this.transformNoduleForm} tables={selectedTablesLabels} />
else return ''
}
@ -99,7 +106,7 @@ class CreateNodule extends Component {
fluid
/>
<TableSelect ref={this.tableSelect} />
<TableSelect ref={this.tableSelect} onChange={this.handleSelectedTablesChange} />
<Dropdown
value ={this.state.noduleType}
@ -111,13 +118,13 @@ class CreateNodule extends Component {
]}
fluid
selection
onChange={this.handleChange}
onChange={this.handleNoduleTypeChange}
/>
{ this.renderNoduleForm() }
<div className='creatTableFormSubmitButtons'>
<Button content='Cancel' secondary />
<Button content='Cancel' secondary onClick={this.clearInput} />
<Button content='Confirm' primary onClick={this.handleSubmit} />
</div>
</div>

View File

@ -1,34 +1,61 @@
import React, { Component } from 'react'
import { Input, Grid, Button, Icon, List } from 'semantic-ui-react'
import { Input, Grid, Button, Icon, List, Dropdown } from 'semantic-ui-react'
import Tables from '../../Models/Tables'
import './CreateNodule.css'
class CreateTransformNoduleForm extends Component {
constructor () {
super()
constructor (props) {
super(props)
this.state = {
structure: {}
structure: {},
tables: props.tables
}
this.tables = new Tables()
this.initialKeyInput = React.createRef()
this.newKeyInput = React.createRef()
document.addEventListener('updateTables', this.updateTableList)
}
addStructureInput = () => {
let structure = this.state.structure || {}
let { structure, keyValue } = this.state
const initialKey = this.initialKeyInput.current.inputRef.current.value
// const initialKey = this.initialKeyInput.current.inputRef.current.value
const newKey = this.newKeyInput.current.inputRef.current.value
if (initialKey && newKey) structure[initialKey] = newKey
if (keyValue && newKey) structure[keyValue] = newKey
this.setState({ structure: structure })
}
componentWillReceiveProps = nextProps => {
this.setState({tables: nextProps.tables})
}
getHeadersDropDownOptions = () => {
const { tables } = this.state
if (!tables || tables.length === 0) return []
const headers = tables.map(t => {
const table = this.tables.getTableByLabel(t)
return table.headers
}).flat()
const dropdownOptions = headers.map(h => {
return { key: h, text: h, value: h }
})
return dropdownOptions
}
getStructureProperties = () => {
return this.state.structure
}
handleKeyChange = (e, value) => {
this.setState({ keyValue: value.value })
}
renderStructure = () => {
const { structure } = this.state
@ -51,7 +78,20 @@ class CreateTransformNoduleForm extends Component {
<List celled>
{ structureElements.initialKeyElements }
</List>
<Input placeholder='Initial Key' ref={this.initialKeyInput} fluid />
{/* <Input placeholder='Initial Key' ref={this.initialKeyInput} fluid /> */}
<Dropdown
clearable
search
header='Headers'
placeholder='Header Key'
fluid
selection
options={this.getHeadersDropDownOptions()}
onChange={this.handleKeyChange}
/>
</Grid.Column>
<Grid.Column>
<List celled>

View File

@ -23,6 +23,7 @@ class TableSelect extends Component {
toggleSelect = (event, element) => {
this.setState({ selectedTablesLabels: element.value })
this.props.onChange(element.value)
}
updateTableList = () => {

View File

@ -1,5 +1,6 @@
import React, { Component } from 'react'
import { Table } from 'semantic-ui-react'
import { Table, Button } from 'semantic-ui-react'
import {json2excel} from 'js2excel'
import './DataTable.css'
import Tables from '../../Models/Tables'
@ -23,7 +24,8 @@ class DataTable extends Component {
if (focusTable) {
this.setState({
headers: focusTable.headers,
tableData: focusTable.rows
tableData: focusTable.rows,
label: focusTable.label
})
}
}
@ -47,9 +49,25 @@ class DataTable extends Component {
return tableRowElements
}
saveTable = () => {
const { tableData, label } = this.state
try {
json2excel({
data: tableData,
name: label
})
// download(tableFile, this.focusTable.label, 'application/vnd.ms-excel')
} catch (err) {
console.log(err)
window.alert('Issue downloading Table.')
}
}
render = () => {
const { tableData, label } = this.state
return (
<div className='DataTable'>
{ tableData.length ? <Button className='saveTableAsExcel' fluid onClick={this.saveTable}>Download { label } Table</Button> : '' }
<Table celled>
<Table.Header>
<Table.Row>

View File

@ -7,7 +7,9 @@
.documentationLink{
color: white;
display: block;
display: flex;
align-items: center;
justify-content: space-around;
font-size: 18px;
padding: 10px 16px;
width: 140px;
@ -18,6 +20,11 @@
background-color: white;
}
.documentationLink img {
width: 36px;
}
.Nav{
height: 40px;
background-color: #2185d0;
@ -55,7 +62,6 @@
background-color: rgb(255, 171, 75);
}
.sponsorLink img {
width: 36px;
}

View File

@ -1,18 +1,19 @@
import React, { Component } from 'react'
import './Nav.css'
import octocat from '../../img/octocat.svg'
import sponsorIcon from '../../img/sponsor.svg'
import guideIcon from '../../img/faq.svg'
import adaLovelaceLogo from '../../img/adaLovelace.jpg'
class Nav extends Component {
render = () => {
return (
<div className='Nav'>
<a className='documentationLink' target='_blank' rel='noopener noreferrer' href='https://docs.lovelace.technology'>Guide</a>
<a className='documentationLink' target='_blank' rel='noopener noreferrer' href='https://docs.datalovelace.app'><img src={guideIcon} alt='guide button' />Guide</a>
<div className='brandHeader'>
<img src={adaLovelaceLogo} alt='logo' className='logo' />
<span className='appName'>Lovelace</span>
</div>
<a className='sponsorLink' target='_blank' rel='noopener noreferrer' href='https://github.com/sponsors/joshuashoemaker'><img src={octocat} alt='sponsor button' /> Sponsor</a>
<a className='sponsorLink' target='_blank' rel='noopener noreferrer' href='https://github.com/sponsors/joshuashoemaker'><img src={sponsorIcon} alt='sponsor button' /> Sponsor</a>
</div>
)
}