Compare commits

...

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

33 changed files with 1394 additions and 2249 deletions

1
.github/FUNDING.yml vendored
View File

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

View File

@ -1,15 +0,0 @@
# <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

Binary file not shown.

Before

Width:  |  Height:  |  Size: 192 KiB

73
package-lock.json generated
View File

@ -3269,23 +3269,6 @@
"ms": "2.0.0" "ms": "2.0.0"
} }
}, },
"http-errors": {
"version": "1.7.2",
"resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.7.2.tgz",
"integrity": "sha512-uUQBt3H/cSIVfch6i1EuPNy/YsRSOUBXTVfZ+yR7Zjez3qjBz6i9+i4zjNaoqcoFVI4lQJ5plg63TvGfRSDCRg==",
"requires": {
"depd": "~1.1.2",
"inherits": "2.0.3",
"setprototypeof": "1.1.1",
"statuses": ">= 1.5.0 < 2",
"toidentifier": "1.0.0"
}
},
"inherits": {
"version": "2.0.3",
"resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz",
"integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4="
},
"ms": { "ms": {
"version": "2.0.0", "version": "2.0.0",
"resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
@ -4097,15 +4080,6 @@
"resolved": "https://registry.npmjs.org/cookie/-/cookie-0.4.0.tgz", "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.4.0.tgz",
"integrity": "sha512-+Hp8fLp57wnUSt0tY0tHEXh4voZRDnoIrZPqlo3DPiI4y9lwg/jqx+1Om94/W6ZaPDOUbnjOt/99w66zk+l1Xg==" "integrity": "sha512-+Hp8fLp57wnUSt0tY0tHEXh4voZRDnoIrZPqlo3DPiI4y9lwg/jqx+1Om94/W6ZaPDOUbnjOt/99w66zk+l1Xg=="
}, },
"cookie-parser": {
"version": "1.4.5",
"resolved": "https://registry.npmjs.org/cookie-parser/-/cookie-parser-1.4.5.tgz",
"integrity": "sha512-f13bPUj/gG/5mDr+xLmSxxDsB9DQiTIfhJS/sqjrmfAWiAN+x2O4i/XguTL9yDZ+/IFDanJ+5x7hC4CXT9Tdzw==",
"requires": {
"cookie": "0.4.0",
"cookie-signature": "1.0.6"
}
},
"cookie-signature": { "cookie-signature": {
"version": "1.0.6", "version": "1.0.6",
"resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz", "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz",
@ -6801,21 +6775,21 @@
"integrity": "sha1-+nFolEq5pRnTN8sL7HKE3D5yPYc=" "integrity": "sha1-+nFolEq5pRnTN8sL7HKE3D5yPYc="
}, },
"http-errors": { "http-errors": {
"version": "1.8.0", "version": "1.7.2",
"resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.8.0.tgz", "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.7.2.tgz",
"integrity": "sha512-4I8r0C5JDhT5VkvI47QktDW75rNlGVsUf/8hzjCC/wkWI/jdTRmBb9aI7erSG82r1bjKY3F6k28WnsVxB1C73A==", "integrity": "sha512-uUQBt3H/cSIVfch6i1EuPNy/YsRSOUBXTVfZ+yR7Zjez3qjBz6i9+i4zjNaoqcoFVI4lQJ5plg63TvGfRSDCRg==",
"requires": { "requires": {
"depd": "~1.1.2", "depd": "~1.1.2",
"inherits": "2.0.4", "inherits": "2.0.3",
"setprototypeof": "1.2.0", "setprototypeof": "1.1.1",
"statuses": ">= 1.5.0 < 2", "statuses": ">= 1.5.0 < 2",
"toidentifier": "1.0.0" "toidentifier": "1.0.0"
}, },
"dependencies": { "dependencies": {
"setprototypeof": { "inherits": {
"version": "1.2.0", "version": "2.0.3",
"resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz", "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz",
"integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==" "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4="
} }
} }
}, },
@ -10812,23 +10786,6 @@
"version": "3.1.0", "version": "3.1.0",
"resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.0.tgz", "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.0.tgz",
"integrity": "sha512-zauLjrfCG+xvoyaqLoV8bLVXXNGC4JqlxFCutSDWA6fJrTo2ZuvLYTqZ7aHBLZSMOopbzwv8f+wZcVzfVTI2Dg==" "integrity": "sha512-zauLjrfCG+xvoyaqLoV8bLVXXNGC4JqlxFCutSDWA6fJrTo2ZuvLYTqZ7aHBLZSMOopbzwv8f+wZcVzfVTI2Dg=="
},
"http-errors": {
"version": "1.7.2",
"resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.7.2.tgz",
"integrity": "sha512-uUQBt3H/cSIVfch6i1EuPNy/YsRSOUBXTVfZ+yR7Zjez3qjBz6i9+i4zjNaoqcoFVI4lQJ5plg63TvGfRSDCRg==",
"requires": {
"depd": "~1.1.2",
"inherits": "2.0.3",
"setprototypeof": "1.1.1",
"statuses": ">= 1.5.0 < 2",
"toidentifier": "1.0.0"
}
},
"inherits": {
"version": "2.0.3",
"resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz",
"integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4="
} }
} }
}, },
@ -11819,18 +11776,6 @@
} }
} }
}, },
"http-errors": {
"version": "1.7.3",
"resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.7.3.tgz",
"integrity": "sha512-ZTTX0MWrsQ2ZAhA1cejAwDLycFsd7I7nVtnkT3Ol0aqodaKW+0CTZDQ1uBv5whptCnc8e8HeRRJxRs0kmm/Qfw==",
"requires": {
"depd": "~1.1.2",
"inherits": "2.0.4",
"setprototypeof": "1.1.1",
"statuses": ">= 1.5.0 < 2",
"toidentifier": "1.0.0"
}
},
"mime": { "mime": {
"version": "1.6.0", "version": "1.6.0",
"resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz", "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz",

View File

@ -1,18 +1,13 @@
{ {
"name": "datalovelace", "name": "lovelace.technology",
"version": "0.1.0", "version": "0.1.0",
"private": true, "private": true,
"type": "module",
"dependencies": { "dependencies": {
"@testing-library/jest-dom": "^4.2.4", "@testing-library/jest-dom": "^4.2.4",
"@testing-library/react": "^9.5.0", "@testing-library/react": "^9.5.0",
"@testing-library/user-event": "^7.2.1", "@testing-library/user-event": "^7.2.1",
"body-parser": "^1.19.0",
"chart.js": "^2.9.3", "chart.js": "^2.9.3",
"cookie-parser": "^1.4.5",
"downloadjs": "^1.4.7", "downloadjs": "^1.4.7",
"express": "^4.17.1",
"http-errors": "^1.8.0",
"js2excel": "^1.0.1", "js2excel": "^1.0.1",
"lovelacejs": "^0.2.1", "lovelacejs": "^0.2.1",
"react": "^16.13.1", "react": "^16.13.1",
@ -26,7 +21,6 @@
"scripts": { "scripts": {
"start": "react-scripts start", "start": "react-scripts start",
"build": "react-scripts build", "build": "react-scripts build",
"server": "node ./server/index",
"test": "react-scripts test", "test": "react-scripts test",
"eject": "react-scripts eject" "eject": "react-scripts eject"
}, },

BIN
public/favicon.ico Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 167 KiB

View File

@ -2,12 +2,12 @@
<html lang="en"> <html lang="en">
<head> <head>
<meta charset="utf-8" /> <meta charset="utf-8" />
<link rel="icon" href="%PUBLIC_URL%/favicon.png" /> <link rel="icon" href="%PUBLIC_URL%/favicon.ico" />
<meta name="viewport" content="width=device-width, initial-scale=1" /> <meta name="viewport" content="width=device-width, initial-scale=1" />
<meta name="theme-color" content="#000000" /> <meta name="theme-color" content="#000000" />
<meta <meta
name="description" name="description"
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." content="Web site created using create-react-app"
/> />
<link rel="apple-touch-icon" href="%PUBLIC_URL%/logo192.png" /> <link rel="apple-touch-icon" href="%PUBLIC_URL%/logo192.png" />
<!-- <!--

View File

@ -1,20 +1,19 @@
{ {
"short_name": "Lovelace", "short_name": "React App",
"name": "datalovelace.app", "name": "Create React App Sample",
"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": [ "icons": [
{ {
"src": "favicon.png", "src": "favicon.ico",
"sizes": "64x64 32x32 24x24 16x16", "sizes": "64x64 32x32 24x24 16x16",
"type": "image/x-icon" "type": "image/x-icon"
}, },
{ {
"src": "favicon.png", "src": "logo192.png",
"type": "image/png", "type": "image/png",
"sizes": "192x192" "sizes": "192x192"
}, },
{ {
"src": "favicon.png", "src": "logo512.png",
"type": "image/png", "type": "image/png",
"sizes": "512x512" "sizes": "512x512"
} }

View File

@ -1,61 +0,0 @@
import express from 'express'
import createError from 'http-errors'
import path from 'path'
import bodyParser from 'body-parser'
import cookieParser from 'cookie-parser'
let instance = null
class Server {
constructor () {
if (!instance) { instance = this }
this.createApp()
this.setupAppOptions()
this.setupAppRoutes()
this.setupAppErrors()
return instance
}
createApp = () => {
this.app = express()
this.app.use(express.json())
this.app.use(express.urlencoded({ extended: false }))
this.app.use(cookieParser())
this.app.use(express.static('./build'))
this.app.use(bodyParser.json())
}
setupAppErrors = () => {
this.app.use((request, response, next) => {
next(createError(404))
})
this.app.use((error, request, response, next) => {
response.locals.message = error.message
response.locals.error = request.app.get('env') === 'development' ? error : {}
response.status(error.status || 500)
response.send('error')
})
}
setupAppRoutes = () => {
const __dirname = path.resolve()
this.app.get('*', (request, response, next) => {
response.sendFile(path.join(__dirname, '/build/index.html'))
})
}
setupAppOptions = () => {
this.app.use((request, response, next) => {
response.header('Access-Control-Allow-Origin', request.headers.origin || '*')
response.header('Access-Control-Allow-Methods', 'GET,POST,PUT,PATCH,HEAD,DELETE,OPTIONS')
response.header('Access-Control-Allow-Headers', 'Content-Type,x-requested-with')
next()
})
}
}
export default Server

View File

@ -1,29 +0,0 @@
import http from 'http'
import Server from './Server.js'
const main = () => {
const port = normalizePort(process.env.PORT || '5003')
const webService = createServer()
webService.listen(port, () => {
console.log(`Server is listening on ${port}`)
})
}
const createServer = () => {
const server = new Server()
return http.createServer(server.app)
}
const normalizePort = portString => {
const port = parseInt(portString, 10)
if (isNaN(port)) return portString
else if (port >= 0 ) return port
else return 0
}
main()
export { main, createServer, normalizePort }

View File

@ -1,5 +1,4 @@
export default { export default {
oneAxisCharts: ['line', 'bar', 'radar', 'doughnut', 'pie', 'polar'], oneAxisCharts: ['line', 'bar', 'radar', 'doughnut', 'pie', 'polar'],
twoAxisCharts: ['scatter'], twoAxisCharts: ['scatter'] /*'bubble', 'scatter', 'area', 'mixed' */
threeAxisCharts: ['bubble'] /* 'mixed' */
} }

View File

@ -7,8 +7,6 @@ class Chart {
this.table = props.table this.table = props.table
this.type = props.type this.type = props.type
this.reportValue = props.reportValue this.reportValue = props.reportValue
this.xAxis = props.xAxis
this.yAxis = props.yAxis
} }
get id () { get id () {
@ -40,14 +38,6 @@ class Chart {
return this._reportValue return this._reportValue
} }
get xAxis () {
return this._xAxis
}
get yAxis () {
return this._yAxis
}
set reportValue (value) { set reportValue (value) {
this._reportValue = value || this._reportValue this._reportValue = value || this._reportValue
return this._reportValue return this._reportValue
@ -66,16 +56,6 @@ class Chart {
else this._chartType = null else this._chartType = null
} }
set xAxis (value) {
this._xAxis = value
return this._xAxis
}
set yAxis (value) {
this._yAxis = value
return this._yAxis
}
_validateChartType = type => { _validateChartType = type => {
const allChartTypes = Object.values(chartTypes).flat() const allChartTypes = Object.values(chartTypes).flat()
if (allChartTypes.includes(type)) return true if (allChartTypes.includes(type)) return true

View File

@ -1,7 +1,6 @@
import { uuid } from 'uuidv4' import { uuid } from 'uuidv4'
import OneAxisChart from '../../Models/Chart/OneAxisChart' import OneAxisChart from '../../Models/Chart/OneAxisChart'
import TwoAxisChart from '../../Models/Chart/TwoAxisChart' import TwoAxisChart from '../../Models/Chart/TwoAxisChart'
import ThreeAxisChart from '../../Models/Chart/ThreeAxisChart'
import chartTypes from '../../Constants/chartTypes' import chartTypes from '../../Constants/chartTypes'
let instance = null let instance = null
@ -17,7 +16,6 @@ class Charts {
let newChart = null let newChart = null
if (chartTypes.oneAxisCharts.includes(chart.type)) newChart = this._generateOneAxisChart(chart) if (chartTypes.oneAxisCharts.includes(chart.type)) newChart = this._generateOneAxisChart(chart)
if (chartTypes.twoAxisCharts.includes(chart.type)) newChart = this._generateTwoAxisChart(chart) if (chartTypes.twoAxisCharts.includes(chart.type)) newChart = this._generateTwoAxisChart(chart)
if (chartTypes.threeAxisCharts.includes(chart.type)) newChart = this._generateThreeAxisChart(chart)
if (newChart) this.collection.push(newChart) if (newChart) this.collection.push(newChart)
} }
@ -68,19 +66,6 @@ class Charts {
}) })
return newChart return newChart
} }
_generateThreeAxisChart = chart => {
const newChart = new ThreeAxisChart({
id: chart.id || uuid(),
label: chart.label,
type: chart.type,
table: chart.table,
xAxis: chart.xAxis,
yAxis: chart.yAxis,
reportValue: chart.reportValue
})
return newChart
}
} }
export default Charts export default Charts

View File

@ -1,51 +0,0 @@
import Chart from './Chart.js'
class ThreeAxisChart extends Chart {
constructor (props) {
super(props)
this.chartSize = 600
this.rows = props.table.export()
this.scale = props.scale || 3.5
const reportValues = this.rows.map(r => {
return r[this.reportValue]
})
this.largestValue = Math.max(...reportValues)
}
get bubble () {
const data = this.rows.map(r => {
return { x: r[this.xAxis], y: r[this.yAxis], r: this.calculateReportValueScale(r[this.reportValue]) }
})
const pointColor = { r: this._generateRandomRGBNumber(), g: this._generateRandomRGBNumber(), b: this._generateRandomRGBNumber(), a: 0.2 }
return {
labels: [this.xAxis, this.yAxis],
datasets: [
{
label: this.reportValue,
data: data,
borderColor: `rgba(255, 255, 255, 0.3)`,
backgroundColor: `rgba(${pointColor.r}, ${pointColor.g}, ${pointColor.b}, 0.6)`
}
]
}
}
calculateReportValueScale = (reportValue) => {
const scale = 3.5
const size = (reportValue / this.largestValue) * 100
return Math.round(size / scale)
}
_generateRandomRGBNumber () {
const max = 255
const min = 0
return Math.round(Math.random() * (max - min) - min)
}
}
export default ThreeAxisChart

View File

@ -3,11 +3,14 @@ import Chart from './Chart'
class TwoAxisChart extends Chart { class TwoAxisChart extends Chart {
constructor (props) { constructor (props) {
super(props) super(props)
this.xAxis = props.xAxis
this.yAxis = props.yAxis
this.rows = props.table.export() this.rows = props.table.export()
} }
get scatter () { get scatter () {
const data = this.rows.map(r => { const rows = this.table.export()
const data = rows.map(r => {
return { x: r[this.xAxis], y: r[this.yAxis] } return { x: r[this.xAxis], y: r[this.yAxis] }
}) })

View File

@ -1 +0,0 @@
<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>

Before

Width:  |  Height:  |  Size: 2.6 KiB

View File

@ -1 +0,0 @@
<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>

Before

Width:  |  Height:  |  Size: 2.5 KiB

View File

@ -1 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 48 48" width="48px" height="48px"><path fill="#455A64" d="M40.3,15.7c0.6-1.7,1.2-5-0.4-8.7c-4.5,0-8.3,3.2-8.9,3.8c-2.2-0.5-4.6-0.7-7-0.7c-2.5,0-4.9,0.3-7.2,0.8C13.7,7.7,9.6,7,8,7c0,0-0.9,1.8-0.9,5c0,2,0.5,3.2,0.8,3.8C5.5,18.3,4,21.7,4,26.1c0,11.2,7.1,15,20,15s20-3.8,20-15C44,21.5,42.6,18.1,40.3,15.7z"/><path fill="#FFCCBC" d="M24,39c-8.2,0-15-1.4-15-9c0-2.9,1.6-4.5,2.7-5.5c2.5-2.2,6.7-1.2,12.3-1.2c4.1,0,7.6-0.7,10.4,0.2c2.8,0.9,4.6,3.5,4.6,6.3C39,37.7,35,39,24,39z"/><path fill="#D84315" d="M25,34c0,0.6-0.4,1-1,1s-1-0.4-1-1s0.4-1,1-1S25,33.4,25,34z M26.5,36.5c0.2-0.2,0.2-0.5,0-0.7s-0.5-0.2-0.7,0c-0.9,0.9-2.6,0.9-3.5,0c-0.2-0.2-0.5-0.2-0.7,0s-0.2,0.5,0,0.7c0.7,0.7,1.5,1,2.5,1S25.8,37.1,26.5,36.5z"/><path fill="#FFF" d="M19,29.5c0,2.5-1.3,4.5-3,4.5s-3-2-3-4.5s1.3-4.5,3-4.5S19,27,19,29.5z M32,25c-1.7,0-3,2-3,4.5s1.3,4.5,3,4.5c1.7,0,3-2,3-4.5S33.7,25,32,25z"/><path fill="#6D4C41" d="M34,30c0,1.7-0.9,3-2,3s-2-1.3-2-3c0-0.2,0-0.5,0.1-0.7c0.1,0.4,0.5,0.7,0.9,0.7c0.6,0,1-0.4,1-1c0-0.6-0.4-1-1-1c-0.2,0-0.4,0.1-0.6,0.2c0.4-0.7,0.9-1.2,1.6-1.2C33.1,27,34,28.3,34,30z M16,27c-0.7,0-1.2,0.5-1.6,1.2c0.2-0.1,0.4-0.2,0.6-0.2c0.6,0,1,0.4,1,1c0,0.6-0.4,1-1,1c-0.4,0-0.8-0.3-0.9-0.7c0,0.2-0.1,0.5-0.1,0.7c0,1.7,0.9,3,2,3s2-1.3,2-3S17.1,27,16,27z"/></svg>

Before

Width:  |  Height:  |  Size: 1.3 KiB

View File

@ -1 +0,0 @@
<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>

Before

Width:  |  Height:  |  Size: 2.5 KiB

View File

@ -1,7 +1,6 @@
import React, { Component } from 'react' import React, { Component } from 'react'
import './App.css' import './App.css'
import 'semantic-ui-css/semantic.min.css' import 'semantic-ui-css/semantic.min.css'
import DataTable from './DataTable/DataTable' import DataTable from './DataTable/DataTable'
import ListViewer from './ListViewer/ListViewer' import ListViewer from './ListViewer/ListViewer'
import Nav from './Nav/Nav' import Nav from './Nav/Nav'

View File

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

View File

@ -1,9 +1,8 @@
import React, { Component } from 'react' import React, { Component } from 'react'
import FocusChart from '../../Models/Chart/FocusChart' import FocusChart from '../../Models/Chart/FocusChart'
import { Doughnut, Bar, Line, Pie, Polar, Radar, Scatter, Bubble } from 'react-chartjs-2' import { Doughnut, Bar, Line, Pie, Polar, Radar, Scatter } from 'react-chartjs-2'
import { Button } from 'semantic-ui-react' import { Button } from 'semantic-ui-react'
import download from 'downloadjs' import download from 'downloadjs'
import './ChartViewer.css'
class ChartViewer extends Component { class ChartViewer extends Component {
constructor () { constructor () {
@ -18,31 +17,31 @@ class ChartViewer extends Component {
document.addEventListener('setSelectedChart', this.setFocusChart) document.addEventListener('setSelectedChart', this.setFocusChart)
} }
closeChart = () => {
this.focusChart.chart = null
this.setFocusChart()
}
handleGroupByChange = (e, value) => { handleGroupByChange = (e, value) => {
this.setState({ reportValue: value.value }) this.setState({ reportValue: value.value })
} }
saveChart = () => { saveChart = () => {
console.log(this.chart.current)
const base64OfChart = this.chart.current.chartInstance.toBase64Image() const base64OfChart = this.chart.current.chartInstance.toBase64Image()
download(base64OfChart, this.focusChart.chart.label, 'image/png') download(base64OfChart, this.focusChart.chart.label, 'image/png')
} }
setFocusChart = () => { setFocusChart = () => {
const focusChart = this.focusChart.chart const focusChart = this.focusChart.chart
this.setState({ if (focusChart) {
chart: focusChart this.setState({
}) chart: focusChart
})
}
} }
renderChart = () => { renderChart = () => {
const { chart } = this.state const { chart } = this.state
if (!chart) return if (!chart) return
console.log(chart)
if (chart.type === 'bar') return <Bar data={chart[chart.type]} width={600} height={600} ref={this.chart} /> if (chart.type === 'bar') return <Bar data={chart[chart.type]} width={600} height={600} ref={this.chart} />
if (chart.type === 'doughnut') return <Doughnut data={chart[chart.type]} width={600} height={600} ref={this.chart} /> if (chart.type === 'doughnut') return <Doughnut data={chart[chart.type]} width={600} height={600} ref={this.chart} />
if (chart.type === 'line') return <Line data={chart[chart.type]} width={600} height={600} ref={this.chart} /> if (chart.type === 'line') return <Line data={chart[chart.type]} width={600} height={600} ref={this.chart} />
@ -50,18 +49,14 @@ class ChartViewer extends Component {
if (chart.type === 'polar') return <Polar data={chart[chart.type]} width={600} height={600} ref={this.chart} /> if (chart.type === 'polar') return <Polar data={chart[chart.type]} width={600} height={600} ref={this.chart} />
if (chart.type === 'radar') return <Radar data={chart[chart.type]} width={600} height={600} ref={this.chart} /> if (chart.type === 'radar') return <Radar data={chart[chart.type]} width={600} height={600} ref={this.chart} />
if (chart.type === 'scatter') return <Scatter data={chart[chart.type]} width={600} height={600} ref={this.chart} /> if (chart.type === 'scatter') return <Scatter data={chart[chart.type]} width={600} height={600} ref={this.chart} />
if (chart.type === 'bubble') return <Bubble data={chart[chart.type]} width={600} height={600} ref={this.chart} />
} }
render = () => { render = () => {
const chart = this.state.chart || {}
return ( return (
<div className='ChartViewer'> <div className='ChartViewer'>
<h3 className='chartLabel'>{ chart.label }</h3>
{this.renderChart()} {this.renderChart()}
<br /> <br />
{ this.state.chart ? <Button className='saveChartAsImageButton' fluid primary onClick={this.saveChart}>Save As Image</Button> : '' } { this.state.chart ? <Button fluid onClick={this.saveChart}>Save As Image</Button> : '' }
{ this.state.chart ? <Button className='closeChartButton' fluid onClick={this.closeChart}>Close Chart</Button> : '' }
</div> </div>
) )
} }

View File

@ -109,7 +109,7 @@ class CreateChartForm extends Component {
} }
renderConfigOptions = () => { renderConfigOptions = () => {
const { oneAxisCharts, twoAxisCharts, threeAxisCharts } = chartTypes const { oneAxisCharts, twoAxisCharts } = chartTypes
const { chartType } = this.state const { chartType } = this.state
let configElements = [] let configElements = []
@ -148,41 +148,6 @@ class CreateChartForm extends Component {
/> />
) )
} }
else if (threeAxisCharts.includes(chartType)) {
configElements.push(
<Dropdown
value ={this.state.xAxisValue}
placeholder='X-Axis'
options={this.getHeaderDropDownOptions()}
fluid
selection
style={{ width: '300px' }}
onChange={this.handleXAxisChange}
/>
)
configElements.push(
<Dropdown
value ={this.state.yAxisValue}
placeholder='Y-Axis'
options={this.getHeaderDropDownOptions()}
fluid
selection
style={{ width: '300px' }}
onChange={this.handleYAxisChange}
/>
)
configElements.push(
<Dropdown
value ={this.state.reportValue}
placeholder='Report By'
options={this.getHeaderDropDownOptions()}
fluid
selection
style={{ width: '300px' }}
onChange={this.handleReportValueChange}
/>
)
}
return configElements return configElements
} }

View File

@ -1,19 +1,15 @@
import React, { Component } from 'react' import React, { Component } from 'react'
import { Input, Dropdown, Grid, Button, Icon, List } from 'semantic-ui-react' import { Input, Dropdown, Grid, Button, Icon, List } from 'semantic-ui-react'
import Tables from '../../Models/Tables'
import './CreateNodule.css' import './CreateNodule.css'
class CreateFilterNoduleForm extends Component { class CreateFilterNoduleForm extends Component {
constructor (props) { constructor () {
super(props) super()
this.state = { this.state = {
filterType: '', filterType: '',
filterParams: {}, filterParams: {}
tables: props.tables
} }
this.tables = new Tables()
this.keyInput = React.createRef() this.keyInput = React.createRef()
this.valueInput = React.createRef() this.valueInput = React.createRef()
document.addEventListener('updateTables', this.updateTableList) document.addEventListener('updateTables', this.updateTableList)
@ -22,44 +18,21 @@ class CreateFilterNoduleForm extends Component {
addKeyValueInput = () => { addKeyValueInput = () => {
let filterParams = this.state.filterParams || {} let filterParams = this.state.filterParams || {}
if (this.state.keyValue) if (this.keyInput.current.inputRef.current.value)
filterParams[this.state.keyValue] = this.valueInput.current.inputRef.current.value filterParams[this.keyInput.current.inputRef.current.value] = this.valueInput.current.inputRef.current.value
this.setState({ filterParams: filterParams }) this.setState({ filterParams: filterParams })
} }
componentWillReceiveProps = nextProps => { handleChange = (e, value) => {
this.setState({tables: nextProps.tables})
}
handleComparisonChange = (e, value) => {
this.setState({ filterType: value.value }) this.setState({ filterType: value.value })
} }
handleKeyChange = (e, value) => {
this.setState({ keyValue: value.value })
}
getFilterProperties = () => { getFilterProperties = () => {
const { filterType, filterParams } = this.state const { filterType, filterParams } = this.state
return { filterType, filterParams } 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 = () => { renderFilterParams = () => {
const { filterParams } = this.state const { filterParams } = this.state
@ -88,7 +61,7 @@ class CreateFilterNoduleForm extends Component {
{key: 'LESSER', text: 'LESSER THAN', value: 'LESSER'}, {key: 'LESSER', text: 'LESSER THAN', value: 'LESSER'},
{key: 'LESSEREQUAL', text: 'LESSER THEN EQUAL TO', value: 'LESSEREQUAL'} {key: 'LESSEREQUAL', text: 'LESSER THEN EQUAL TO', value: 'LESSEREQUAL'}
]} ]}
onChange={this.handleComparisonChange} onChange={this.handleChange}
/> />
<Grid columns={2} stackable> <Grid columns={2} stackable>
@ -96,18 +69,7 @@ class CreateFilterNoduleForm extends Component {
<List celled> <List celled>
{ filterParamElements.filterKeyElements } { filterParamElements.filterKeyElements }
</List> </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>
<Grid.Column> <Grid.Column>
<List celled> <List celled>

View File

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

View File

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

View File

@ -1,61 +1,34 @@
import React, { Component } from 'react' import React, { Component } from 'react'
import { Input, Grid, Button, Icon, List, Dropdown } from 'semantic-ui-react' import { Input, Grid, Button, Icon, List } from 'semantic-ui-react'
import Tables from '../../Models/Tables'
import './CreateNodule.css' import './CreateNodule.css'
class CreateTransformNoduleForm extends Component { class CreateTransformNoduleForm extends Component {
constructor (props) { constructor () {
super(props) super()
this.state = { this.state = {
structure: {}, structure: {}
tables: props.tables
} }
this.tables = new Tables()
this.initialKeyInput = React.createRef() this.initialKeyInput = React.createRef()
this.newKeyInput = React.createRef() this.newKeyInput = React.createRef()
document.addEventListener('updateTables', this.updateTableList) document.addEventListener('updateTables', this.updateTableList)
} }
addStructureInput = () => { addStructureInput = () => {
let { structure, keyValue } = this.state let structure = this.state.structure || {}
// const initialKey = this.initialKeyInput.current.inputRef.current.value const initialKey = this.initialKeyInput.current.inputRef.current.value
const newKey = this.newKeyInput.current.inputRef.current.value const newKey = this.newKeyInput.current.inputRef.current.value
if (keyValue && newKey) structure[keyValue] = newKey if (initialKey && newKey) structure[initialKey] = newKey
this.setState({ structure: structure }) 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 = () => { getStructureProperties = () => {
return this.state.structure return this.state.structure
} }
handleKeyChange = (e, value) => {
this.setState({ keyValue: value.value })
}
renderStructure = () => { renderStructure = () => {
const { structure } = this.state const { structure } = this.state
@ -78,20 +51,7 @@ class CreateTransformNoduleForm extends Component {
<List celled> <List celled>
{ structureElements.initialKeyElements } { structureElements.initialKeyElements }
</List> </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>
<Grid.Column> <Grid.Column>
<List celled> <List celled>

View File

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

View File

@ -1,6 +1,5 @@
import React, { Component } from 'react' import React, { Component } from 'react'
import { Table, Button } from 'semantic-ui-react' import { Table } from 'semantic-ui-react'
import {json2excel} from 'js2excel'
import './DataTable.css' import './DataTable.css'
import Tables from '../../Models/Tables' import Tables from '../../Models/Tables'
@ -24,8 +23,7 @@ class DataTable extends Component {
if (focusTable) { if (focusTable) {
this.setState({ this.setState({
headers: focusTable.headers, headers: focusTable.headers,
tableData: focusTable.rows, tableData: focusTable.rows
label: focusTable.label
}) })
} }
} }
@ -49,25 +47,9 @@ class DataTable extends Component {
return tableRowElements 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 = () => { render = () => {
const { tableData, label } = this.state
return ( return (
<div className='DataTable'> <div className='DataTable'>
{ tableData.length ? <Button className='saveTableAsExcel' fluid onClick={this.saveTable}>Download { label } Table</Button> : '' }
<Table celled> <Table celled>
<Table.Header> <Table.Header>
<Table.Row> <Table.Row>

View File

@ -1,38 +1,15 @@
.brandHeader { .brandHeader {
display: flex; display: flex;
justify-content: space-around; justify-content: space-around;
width: 140px; width: 280px;
margin: 0px auto; margin: 0px auto;
} }
.documentationLink{
color: white;
display: flex;
align-items: center;
justify-content: space-around;
font-size: 18px;
padding: 10px 16px;
width: 140px;
}
.documentationLink:hover{
color: black;
background-color: white;
}
.documentationLink img {
width: 36px;
}
.Nav{ .Nav{
height: 40px; height: 40px;
background-color: #2185d0; background-color: #2185d0;
color: white; color: white;
text-align: center; text-align: center;
display: flex;
justify-content: space-between;
align-items: center;
} }
.appName { .appName {
@ -44,24 +21,4 @@
.logo { .logo {
width: 36px; width: 36px;
border-radius: 100%; border-radius: 100%;
}
.sponsorLink {
color: white;
display: flex;
font-size: 18px;
padding: 1.5px 16px;
align-items: center;
justify-content: space-between;
width: 140px;
}
.sponsorLink:hover{
color: black;
background-color: rgb(255, 171, 75);
}
.sponsorLink img {
width: 36px;
} }

View File

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

File diff suppressed because it is too large Load Diff