feat: create chart instances
This commit is contained in:
parent
4a093fc301
commit
c3aa1a4c40
24
src/Controllers/ChartListController.js
Normal file
24
src/Controllers/ChartListController.js
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
import Charts from '../Models/Chart/Charts'
|
||||||
|
import FocusChart from '../Models/Chart/FocusChart'
|
||||||
|
|
||||||
|
class ChartListController {
|
||||||
|
constructor() {
|
||||||
|
this.charts = new Charts()
|
||||||
|
this.focusChart = new FocusChart()
|
||||||
|
this.updatedChartsEvent = new Event('updateCharts')
|
||||||
|
this.setSelectedChartEvent = new Event('setSelectedChart')
|
||||||
|
}
|
||||||
|
|
||||||
|
deleteChart = id => {
|
||||||
|
this.charts.removeById(id)
|
||||||
|
document.dispatchEvent(this.updatedChartsEvent)
|
||||||
|
}
|
||||||
|
|
||||||
|
selectChartToView = id => {
|
||||||
|
const chart = this.charts.getById(id)
|
||||||
|
this.focusChart.chart = chart
|
||||||
|
document.dispatchEvent(this.setSelectedChartEvent)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default ChartListController
|
||||||
20
src/Controllers/CreateChartController.js
Normal file
20
src/Controllers/CreateChartController.js
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
import Charts from '../Models/Chart/Charts'
|
||||||
|
|
||||||
|
class CreateTableController {
|
||||||
|
constructor() {
|
||||||
|
this.charts = new Charts()
|
||||||
|
this.updatedChartsEvent = new Event('updateCharts')
|
||||||
|
}
|
||||||
|
|
||||||
|
addNewChart = chart => {
|
||||||
|
this.charts.addNewChart({
|
||||||
|
label: chart.label,
|
||||||
|
type: chart.type,
|
||||||
|
table: chart.table,
|
||||||
|
groupByValue: chart.groupByValue,
|
||||||
|
})
|
||||||
|
document.dispatchEvent(this.updatedChartsEvent)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default CreateTableController
|
||||||
10
src/Models/Chart/Chart.js
vendored
10
src/Models/Chart/Chart.js
vendored
@ -2,12 +2,22 @@ import chartTypes from '../../Constants/chartTypes'
|
|||||||
|
|
||||||
class Chart {
|
class Chart {
|
||||||
constructor (props) {
|
constructor (props) {
|
||||||
|
this.id = props.id
|
||||||
this.label = props.label
|
this.label = props.label
|
||||||
this.table = props.table
|
this.table = props.table
|
||||||
this.type = props.type
|
this.type = props.type
|
||||||
this.groupByValue = props.groupByValue
|
this.groupByValue = props.groupByValue
|
||||||
}
|
}
|
||||||
|
|
||||||
|
get id () {
|
||||||
|
return this._id
|
||||||
|
}
|
||||||
|
|
||||||
|
set id (newId) {
|
||||||
|
this._id = newId
|
||||||
|
return this._id
|
||||||
|
}
|
||||||
|
|
||||||
get data () {
|
get data () {
|
||||||
return this.table.rows
|
return this.table.rows
|
||||||
}
|
}
|
||||||
|
|||||||
@ -2,11 +2,10 @@ import Chart from './Chart.js'
|
|||||||
import { GroupByNodule } from 'lovelacejs'
|
import { GroupByNodule } from 'lovelacejs'
|
||||||
|
|
||||||
class ChartJsDataset extends Chart {
|
class ChartJsDataset extends Chart {
|
||||||
get props () {
|
get doughnut () {
|
||||||
|
|
||||||
const groupByNodule = new GroupByNodule({
|
const groupByNodule = new GroupByNodule({
|
||||||
id: this.table.id,
|
id: this.id,
|
||||||
label: `${this.table.label} groupedBy ${this.groupByValue}`,
|
label: `${this.label} groupedBy ${this.groupByValue}`,
|
||||||
tables: [this.table],
|
tables: [this.table],
|
||||||
groupByValue: this.groupByValue
|
groupByValue: this.groupByValue
|
||||||
}).export()
|
}).export()
|
||||||
@ -21,7 +20,7 @@ class ChartJsDataset extends Chart {
|
|||||||
return {
|
return {
|
||||||
labels: labels,
|
labels: labels,
|
||||||
datasets: [{
|
datasets: [{
|
||||||
label: this.label,
|
label: `${this.label} groupedBy ${this.groupByValue}`,
|
||||||
data: groupByCounts,
|
data: groupByCounts,
|
||||||
backgroundColor: this._getbackgroundColors()
|
backgroundColor: this._getbackgroundColors()
|
||||||
}],
|
}],
|
||||||
|
|||||||
56
src/Models/Chart/Charts.js
Normal file
56
src/Models/Chart/Charts.js
Normal file
@ -0,0 +1,56 @@
|
|||||||
|
import { uuid } from 'uuidv4'
|
||||||
|
import ChartJsDataset from '../../Models/Chart/ChartjsDataset'
|
||||||
|
|
||||||
|
let instance = null
|
||||||
|
|
||||||
|
class Charts {
|
||||||
|
constructor () {
|
||||||
|
if (!instance) instance = this
|
||||||
|
this.collection = []
|
||||||
|
return instance
|
||||||
|
}
|
||||||
|
|
||||||
|
addNewChart = chart => {
|
||||||
|
let newChart = null
|
||||||
|
if (chart.type === 'bar') newChart = this._generateChartJsDataset(chart)
|
||||||
|
if (chart.type === 'doughnut') newChart = this._generateChartJsDataset(chart)
|
||||||
|
|
||||||
|
if (newChart) this.collection.push(newChart)
|
||||||
|
}
|
||||||
|
|
||||||
|
getById = id => this.collection.find(t => id === t.id)
|
||||||
|
|
||||||
|
getByLabel = label => this.collection.find(t => label === t.label)
|
||||||
|
|
||||||
|
getCollectionProps = () => {
|
||||||
|
const charts = this.collection
|
||||||
|
const chartProps = charts.map(c => {
|
||||||
|
return {
|
||||||
|
id: c.id,
|
||||||
|
label: c.label,
|
||||||
|
table: c.table.label,
|
||||||
|
type: c.type,
|
||||||
|
groupByValue: c.groupByValue
|
||||||
|
}
|
||||||
|
})
|
||||||
|
return chartProps
|
||||||
|
}
|
||||||
|
|
||||||
|
removeById = id => {
|
||||||
|
const indexToRemove = this.collection.findIndex(t => t.id === id)
|
||||||
|
if (indexToRemove > -1) this.collection.splice(indexToRemove, 1)
|
||||||
|
}
|
||||||
|
|
||||||
|
_generateChartJsDataset = chart => {
|
||||||
|
const newChart = new ChartJsDataset({
|
||||||
|
id: chart.id || uuid(),
|
||||||
|
label: chart.label,
|
||||||
|
type: chart.type,
|
||||||
|
table: chart.table,
|
||||||
|
groupByValue: chart.groupByValue
|
||||||
|
})
|
||||||
|
return newChart
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default Charts
|
||||||
20
src/Models/Chart/FocusChart.js
Normal file
20
src/Models/Chart/FocusChart.js
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
let instance = null
|
||||||
|
|
||||||
|
class FocusChart {
|
||||||
|
constructor (chart) {
|
||||||
|
if (instance === null) instance = this
|
||||||
|
this._value = chart
|
||||||
|
return instance
|
||||||
|
}
|
||||||
|
|
||||||
|
set chart (chart) {
|
||||||
|
this._value = chart
|
||||||
|
return this._value
|
||||||
|
}
|
||||||
|
|
||||||
|
get chart () {
|
||||||
|
return this._value
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default FocusChart
|
||||||
@ -1,70 +1,64 @@
|
|||||||
import React, { Component } from 'react'
|
import React, { Component } from 'react'
|
||||||
import ChartJsDataset from '../../Models/Chart/ChartjsDataset'
|
import ChartJsDataset from '../../Models/Chart/ChartjsDataset'
|
||||||
import FocusTable from '../../Models/FocusTable'
|
import FocusChart from '../../Models/Chart/FocusChart'
|
||||||
import { Doughnut } from 'react-chartjs-2'
|
import { Doughnut } from 'react-chartjs-2'
|
||||||
import { Dropdown } from 'semantic-ui-react'
|
import { Dropdown } from 'semantic-ui-react'
|
||||||
|
|
||||||
class ChartViewer extends Component {
|
class ChartViewer extends Component {
|
||||||
constructor () {
|
constructor () {
|
||||||
super ()
|
super ()
|
||||||
this.focusTable = new FocusTable()
|
this.focusChart = new FocusChart()
|
||||||
this.state = {
|
this.state = {
|
||||||
table: null,
|
chart: null,
|
||||||
groupByValue: ''
|
groupByValue: ''
|
||||||
}
|
}
|
||||||
|
|
||||||
document.addEventListener('setSelectedTable', this.setFocusTable)
|
document.addEventListener('setSelectedChart', this.setFocusChart)
|
||||||
}
|
}
|
||||||
|
|
||||||
handleGroupByChange = (e, value) => {
|
handleGroupByChange = (e, value) => {
|
||||||
this.setState({ groupByValue: value.value })
|
this.setState({ groupByValue: value.value })
|
||||||
}
|
}
|
||||||
|
|
||||||
setFocusTable = () => {
|
setFocusChart = () => {
|
||||||
const focusTable = this.focusTable.table
|
const focusChart = this.focusChart.chart
|
||||||
if (focusTable) {
|
if (focusChart) {
|
||||||
this.setState({
|
this.setState({
|
||||||
table: focusTable,
|
chart: focusChart,
|
||||||
headers: this.renderGroupByOptions()
|
// headers: this.renderGroupByOptions()
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
renderChart = () => {
|
renderChart = () => {
|
||||||
if (!this.state.table) return
|
const { chart } = this.state
|
||||||
const chart = new ChartJsDataset({
|
if (!chart) return
|
||||||
label: this.state.label,
|
// console.log(chart)
|
||||||
type: 'bar',
|
|
||||||
table: this.state.table,
|
|
||||||
groupByValue: this.state.groupByValue
|
|
||||||
})
|
|
||||||
|
|
||||||
console.log(chart)
|
return <Doughnut data={chart[chart.type]} width={600} height={600} />
|
||||||
|
|
||||||
return <Doughnut data={chart.props} width={600} height={600} />
|
|
||||||
}
|
}
|
||||||
|
|
||||||
renderGroupByOptions = () => {
|
// renderGroupByOptions = () => {
|
||||||
const focusTable = this.focusTable.table
|
// const focusTable = this.focusTable.table
|
||||||
if (!focusTable) return []
|
// if (!focusTable) return []
|
||||||
|
|
||||||
const headers = focusTable.headers
|
// const headers = focusTable.headers
|
||||||
const options = headers.map(h => {
|
// const options = headers.map(h => {
|
||||||
return {key: h, text: h, value: h}
|
// return {key: h, text: h, value: h}
|
||||||
})
|
// })
|
||||||
return options
|
// return options
|
||||||
}
|
// }
|
||||||
|
|
||||||
render = () => {
|
render = () => {
|
||||||
return (
|
return (
|
||||||
<div className='ChartViewer'>
|
<div className='ChartViewer'>
|
||||||
<Dropdown
|
{/* <Dropdown
|
||||||
placeholder='Select Value to Report'
|
placeholder='Select Value to Report'
|
||||||
fluid
|
fluid
|
||||||
selection
|
selection
|
||||||
options={this.state.headers}
|
options={this.state.headers}
|
||||||
onChange={this.handleGroupByChange}
|
onChange={this.handleGroupByChange}
|
||||||
/>
|
/> */}
|
||||||
{this.renderChart()}
|
{this.renderChart()}
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
|
|||||||
52
src/views/ChartList/ChartList.js
Normal file
52
src/views/ChartList/ChartList.js
Normal file
@ -0,0 +1,52 @@
|
|||||||
|
import React, { Component } from 'react'
|
||||||
|
import { Card, Button, Icon } from 'semantic-ui-react'
|
||||||
|
import CreateChartForm from '../CreateChart/CreateChartForm'
|
||||||
|
import ChartListItem from './ChartListItem'
|
||||||
|
// import './TableList.css'
|
||||||
|
|
||||||
|
import Charts from '../../Models/Chart/Charts'
|
||||||
|
import ChartListController from '../../Controllers/ChartListController'
|
||||||
|
|
||||||
|
class ChartList extends Component {
|
||||||
|
constructor () {
|
||||||
|
super()
|
||||||
|
|
||||||
|
this.charts = new Charts()
|
||||||
|
this.controller = new ChartListController()
|
||||||
|
this.state = {
|
||||||
|
adding: false,
|
||||||
|
charts: this.charts.getCollectionProps()
|
||||||
|
}
|
||||||
|
|
||||||
|
document.addEventListener('updateCharts', this.updateChartList)
|
||||||
|
}
|
||||||
|
|
||||||
|
toggleAddingTable = () => { this.setState({ adding: !this.state.adding }) }
|
||||||
|
|
||||||
|
updateChartList = () => {
|
||||||
|
this.setState({charts: this.charts.getCollectionProps()})
|
||||||
|
}
|
||||||
|
|
||||||
|
renderListItemElements = () => {
|
||||||
|
const { charts } = this.state
|
||||||
|
const chartListElements = charts.map(c => <ChartListItem key={c.id} chart={c} /> )
|
||||||
|
return chartListElements
|
||||||
|
}
|
||||||
|
|
||||||
|
render = () => {
|
||||||
|
return (
|
||||||
|
<div className='TableList'>
|
||||||
|
<Card.Group>
|
||||||
|
<Button animated primary style={{ width: '100%', display: 'block' }} onClick={this.toggleAddingTable}>
|
||||||
|
<Button.Content visible>Add Chart</Button.Content>
|
||||||
|
<Button.Content hidden><Icon name='add' /></Button.Content>
|
||||||
|
</Button>
|
||||||
|
{this.state.adding ? <CreateChartForm /> : ''}
|
||||||
|
{ this.renderListItemElements() }
|
||||||
|
</Card.Group>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default ChartList
|
||||||
38
src/views/ChartList/ChartListItem.js
Normal file
38
src/views/ChartList/ChartListItem.js
Normal file
@ -0,0 +1,38 @@
|
|||||||
|
import React, { Component } from 'react'
|
||||||
|
import { Card, Icon } from 'semantic-ui-react'
|
||||||
|
// import './TableList.css'
|
||||||
|
|
||||||
|
import ChartListController from '../../Controllers/ChartListController'
|
||||||
|
|
||||||
|
class ChartListItem extends Component {
|
||||||
|
constructor () {
|
||||||
|
super()
|
||||||
|
this.controller = new ChartListController()
|
||||||
|
}
|
||||||
|
|
||||||
|
render = () => {
|
||||||
|
const { chart } = this.props
|
||||||
|
return (
|
||||||
|
<Card key={chart.id} id={chart.id} style={{ width: '380px' }}>
|
||||||
|
<Card.Content>
|
||||||
|
<Card.Header>{ chart.label }</Card.Header>
|
||||||
|
<Card.Meta>{`${chart.table} grouped by ${chart.groupByValue}`}</Card.Meta>
|
||||||
|
</Card.Content>
|
||||||
|
<Card.Content extra>
|
||||||
|
<span
|
||||||
|
onClick={() => { this.controller.deleteChart(chart.id) }}
|
||||||
|
style={{ cursor: 'pointer' }}>
|
||||||
|
Delete <Icon name='trash' />
|
||||||
|
</span>
|
||||||
|
<span
|
||||||
|
onClick={() => { this.controller.selectChartToView(chart.id) }}
|
||||||
|
style={{ cursor: 'pointer' }}>
|
||||||
|
View <Icon name='table' />
|
||||||
|
</span>
|
||||||
|
</Card.Content>
|
||||||
|
</Card>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default ChartListItem
|
||||||
156
src/views/CreateChart/CreateChartForm.js
Normal file
156
src/views/CreateChart/CreateChartForm.js
Normal file
@ -0,0 +1,156 @@
|
|||||||
|
import React, { Component } from 'react'
|
||||||
|
import { Button, Input, Header, Dropdown } from 'semantic-ui-react'
|
||||||
|
// import './CreateNodule.css'
|
||||||
|
|
||||||
|
import Tables from '../../Models/Tables'
|
||||||
|
import Nodules from '../../Models/Nodules'
|
||||||
|
import CreateChartController from '../../Controllers/CreateChartController'
|
||||||
|
import chartTypes from '../../Constants/chartTypes'
|
||||||
|
|
||||||
|
|
||||||
|
class CreateChartForm extends Component {
|
||||||
|
constructor () {
|
||||||
|
super()
|
||||||
|
this.tables = new Tables()
|
||||||
|
|
||||||
|
this.state = {
|
||||||
|
chartType: '',
|
||||||
|
selectedTableId: '',
|
||||||
|
tables: this.tables.getCollectionProps(),
|
||||||
|
groupByValue: '',
|
||||||
|
headers : []
|
||||||
|
}
|
||||||
|
|
||||||
|
this.tables = new Tables()
|
||||||
|
this.nodules = new Nodules()
|
||||||
|
this.controller = new CreateChartController()
|
||||||
|
|
||||||
|
this.chartLabelInput = React.createRef()
|
||||||
|
this.groupByValueInput = React.createRef()
|
||||||
|
|
||||||
|
document.addEventListener('updateTables', this.updateTableList)
|
||||||
|
}
|
||||||
|
|
||||||
|
clearInput = () => {
|
||||||
|
this.setState({ noduleType: '' })
|
||||||
|
}
|
||||||
|
|
||||||
|
getChartTypeDropdownOptions = () => {
|
||||||
|
return chartTypes.map(t => {
|
||||||
|
return {key: t, text: t, value: t}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
handleChartTypeChange = (e, value) => {
|
||||||
|
this.setState({ chartType: value.value })
|
||||||
|
}
|
||||||
|
|
||||||
|
handleGroupByChange = (e, value) => {
|
||||||
|
this.setState({ groupByValue: value.value })
|
||||||
|
}
|
||||||
|
|
||||||
|
handleSelectedTableChange = (e, value) => {
|
||||||
|
const selectedTable = this.tables.getById(value.value)
|
||||||
|
this.setState({
|
||||||
|
selectedTableId: value.value,
|
||||||
|
headers: selectedTable.headers
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
getTableDropDownOptions = () => {
|
||||||
|
const { tables } = this.state
|
||||||
|
const tableDropdownOptions = tables.map(t => {
|
||||||
|
return {
|
||||||
|
key: t.id,
|
||||||
|
value: t.id,
|
||||||
|
text: t.label
|
||||||
|
}
|
||||||
|
})
|
||||||
|
return tableDropdownOptions
|
||||||
|
}
|
||||||
|
|
||||||
|
getGroupByDropDownOptions = () => {
|
||||||
|
const { headers } = this.state
|
||||||
|
const tableDropdownOptions = headers.map(h => {
|
||||||
|
return {
|
||||||
|
key: h,
|
||||||
|
value: h,
|
||||||
|
text: h
|
||||||
|
}
|
||||||
|
})
|
||||||
|
return tableDropdownOptions
|
||||||
|
}
|
||||||
|
|
||||||
|
handleSubmit = () => {
|
||||||
|
const { chartType, selectedTableId, groupByValue } = this.state
|
||||||
|
|
||||||
|
const chartLabel = this.chartLabelInput.current.inputRef.current.value
|
||||||
|
// const groupByValue = this.groupByValueInput.current.inputRef.current.value
|
||||||
|
const table = this.tables.getById(selectedTableId)
|
||||||
|
this.controller.addNewChart({
|
||||||
|
label: chartLabel,
|
||||||
|
type: chartType,
|
||||||
|
table: table,
|
||||||
|
groupByValue: groupByValue
|
||||||
|
})
|
||||||
|
|
||||||
|
this.clearInput()
|
||||||
|
}
|
||||||
|
|
||||||
|
updateTableList = () => {
|
||||||
|
this.setState({tables: this.tables.getCollectionProps()})
|
||||||
|
}
|
||||||
|
|
||||||
|
render = () => {
|
||||||
|
return (
|
||||||
|
<div className='CreateNodule'>
|
||||||
|
<Header as='h3'>Create Graph</Header>
|
||||||
|
|
||||||
|
<Input
|
||||||
|
placeholder='Chart Label'
|
||||||
|
ref={this.chartLabelInput}
|
||||||
|
icon='tags'
|
||||||
|
style={{ width: '300px' }}
|
||||||
|
/>
|
||||||
|
<br />
|
||||||
|
|
||||||
|
<Dropdown
|
||||||
|
value ={this.state.chartType}
|
||||||
|
placeholder='Select a Chart Type'
|
||||||
|
options={this.getChartTypeDropdownOptions()}
|
||||||
|
fluid
|
||||||
|
selection
|
||||||
|
style={{ width: '300px' }}
|
||||||
|
onChange={this.handleChartTypeChange}
|
||||||
|
/>
|
||||||
|
|
||||||
|
<Dropdown
|
||||||
|
value ={this.state.selectedTable}
|
||||||
|
placeholder='Select a Table'
|
||||||
|
options={this.getTableDropDownOptions()}
|
||||||
|
fluid
|
||||||
|
selection
|
||||||
|
style={{ width: '300px' }}
|
||||||
|
onChange={this.handleSelectedTableChange}
|
||||||
|
/>
|
||||||
|
|
||||||
|
<Dropdown
|
||||||
|
value ={this.state.selectedTable}
|
||||||
|
placeholder='Group By'
|
||||||
|
options={this.getGroupByDropDownOptions()}
|
||||||
|
fluid
|
||||||
|
selection
|
||||||
|
style={{ width: '300px' }}
|
||||||
|
onChange={this.handleGroupByChange}
|
||||||
|
/>
|
||||||
|
|
||||||
|
<div className='creatTableFormSubmitButtons'>
|
||||||
|
<Button content='Cancel' secondary />
|
||||||
|
<Button content='Confirm' primary onClick={this.handleSubmit} />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default CreateChartForm
|
||||||
@ -3,13 +3,15 @@ import TableList from '../TableList/TableList'
|
|||||||
import NoduleList from '../NoduleList/NoduleList'
|
import NoduleList from '../NoduleList/NoduleList'
|
||||||
import './ListViewer.css'
|
import './ListViewer.css'
|
||||||
import { Tab } from 'semantic-ui-react'
|
import { Tab } from 'semantic-ui-react'
|
||||||
|
import ChartList from '../ChartList/ChartList'
|
||||||
|
|
||||||
class ListViewer extends Component {
|
class ListViewer extends Component {
|
||||||
constructor () {
|
constructor () {
|
||||||
super()
|
super()
|
||||||
this.panes = [
|
this.panes = [
|
||||||
{ menuItem: 'Tables', render: () => <Tab.Pane><TableList /></Tab.Pane> },
|
{ menuItem: 'Tables', render: () => <Tab.Pane><TableList /></Tab.Pane> },
|
||||||
{ menuItem: 'Nodules', render: () => <Tab.Pane><NoduleList /></Tab.Pane> }
|
{ menuItem: 'Nodules', render: () => <Tab.Pane><NoduleList /></Tab.Pane> },
|
||||||
|
{ menuItem: 'Charts', render: () => <Tab.Pane><ChartList /></Tab.Pane> }
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
render = () => {
|
render = () => {
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user