| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149 |
- import React from 'react'
- import { reverse } from 'lodash'
- import { makeStyles } from '@material-ui/styles'
- import { BarChart, Bar, XAxis, YAxis, CartesianGrid, Tooltip, ResponsiveContainer } from 'recharts'
- import { primary, greyscale, browns } from '../../constants/colors';
- import { toCurrency } from '../../util';
- const useStyles = makeStyles({
- tooltip: {
- borderRadius: 2,
- background: 'rgba(255, 255, 255, 0.95)',
- padding: 12,
- },
- tooltipLineItem: {
- fontSize: '1rem',
- margin: 0,
- marginBottom: 4,
- padding: 0,
- },
- })
- function toBarLabels(allocationRange) {
- let keyToFill = {}
- let p = 0
- let g = 0
- let b = 0
- for (const { idle } of allocationRange) {
- for (const allocation of idle) {
- const key = allocation.name
- if (keyToFill[key] === undefined) {
- // idle allocations are assigned grey
- keyToFill[key] = greyscale[g]
- g = (g+1) % greyscale.length
- }
- }
- }
- for (const { top } of allocationRange) {
- for (const allocation of top) {
- const key = allocation.name
- if (keyToFill[key] === undefined) {
- if (key === "__unallocated__") {
- // unallocated gets black (clean up)
- keyToFill[key] = "#212121"
- } else {
- // non-idle allocations get the next available color
- keyToFill[key] = primary[p]
- p = (p+1) % primary.length
- }
- }
- }
- }
- for (const { other } of allocationRange) {
- for (const allocation of other) {
- const key = allocation.name
- if (keyToFill[key] === undefined) {
- // idle allocations are assigned grey
- keyToFill[key] = browns[b]
- b = (b+1) % browns.length
- }
- }
- }
- let labels = []
- for (const key in keyToFill) {
- labels.push({
- dataKey: key,
- fill: keyToFill[key],
- })
- }
- return reverse(labels)
- }
- function toBar(datum) {
- const { top, other, idle } = datum
- const bar = {}
- for (const key in top) {
- const allocation = top[key]
- const start = new Date(allocation.start)
- bar.start = `${start.getUTCFullYear()}-${start.getUTCMonth()+1}-${start.getUTCDate()}`
- bar[allocation.name] = allocation.totalCost
- }
- for (const key in other) {
- const allocation = other[key]
- const start = new Date(allocation.start)
- bar.start = `${start.getUTCFullYear()}-${start.getUTCMonth()+1}-${start.getUTCDate()}`
- bar[allocation.name] = allocation.totalCost
- }
- for (const key in idle) {
- const allocation = idle[key]
- const start = new Date(allocation.start)
- bar.start = `${start.getUTCFullYear()}-${start.getUTCMonth()+1}-${start.getUTCDate()}`
- bar[allocation.name] = allocation.totalCost
- }
- return bar
- }
- const RangeChart = ({ data, currency, height }) => {
- const classes = useStyles()
- const barData = data.map(toBar)
- const barLabels = toBarLabels(data)
- const CustomTooltip = (params) => {
- const { active, payload } = params
- if (!payload || payload.length == 0) {
- return null
- }
- const total = payload.reduce((sum, item) => sum + item.value, 0.0)
- if (active) {
- return (
- <div className={classes.tooltip}>
- <p className={classes.tooltipLineItem} style={{ color: '#000000' }}>{`Total: ${toCurrency(total, currency)}`}</p>
- {reverse(payload).map((item, i) => (
- <p key={i} className={classes.tooltipLineItem} style={{ color: item.fill }}>{`${item.name}: ${toCurrency(item.value, currency)}`}</p>
- ))}
- </div>
- )
- }
- return null
- }
- return (
- <ResponsiveContainer width="100%" height={height}>
- <BarChart
- data={barData}
- margin={{ top: 30, right: 30, left: 30, bottom: 12 }}
- >
- <CartesianGrid strokeDasharray="3 3" />
- <XAxis dataKey="start" />
- <YAxis />
- <Tooltip content={<CustomTooltip />} />
- {barLabels.map((barLabel, i) => <Bar key={i} dataKey={barLabel.dataKey} stackId="a" fill={barLabel.fill} />)}
- </BarChart>
- </ResponsiveContainer>
- )
- }
- export default RangeChart
|